summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml132
-rw-r--r--.cirrus.yml133
-rw-r--r--.document3
-rw-r--r--.gdbinit7
-rw-r--r--.git-blame-ignore-revs8
-rw-r--r--.github/actions/launchable/setup/action.yml144
-rw-r--r--.github/actions/setup/directories/action.yml174
-rw-r--r--.github/actions/setup/macos/action.yml24
-rw-r--r--.github/actions/setup/ubuntu/action.yml53
-rw-r--r--.github/actions/slack/action.yml39
-rw-r--r--.github/auto_request_review.yml1
-rw-r--r--.github/dependabot.yml12
-rw-r--r--.github/workflows/annocheck.yml177
-rw-r--r--.github/workflows/auto_request_review.yml4
-rw-r--r--.github/workflows/baseruby.yml63
-rw-r--r--.github/workflows/bundled_gems.yml117
-rw-r--r--.github/workflows/check_dependencies.yml69
-rw-r--r--.github/workflows/check_misc.yml69
-rw-r--r--.github/workflows/cirrus-notify.yml46
-rw-r--r--.github/workflows/codeql-analysis.yml154
-rw-r--r--.github/workflows/compilers.yml172
-rw-r--r--.github/workflows/dependabot_automerge.yml12
-rw-r--r--.github/workflows/macos.yml161
-rw-r--r--.github/workflows/mingw.yml135
-rw-r--r--.github/workflows/pr-playground.yml127
-rw-r--r--.github/workflows/prism.yml114
-rw-r--r--.github/workflows/rjit-bindgen.yml86
-rw-r--r--.github/workflows/rjit.yml102
-rw-r--r--.github/workflows/scorecards.yml12
-rw-r--r--.github/workflows/spec_guards.yml41
-rw-r--r--.github/workflows/ubuntu.yml177
-rw-r--r--.github/workflows/wasm.yml121
-rw-r--r--.github/workflows/windows.yml154
-rw-r--r--.github/workflows/yjit-macos.yml169
-rw-r--r--.github/workflows/yjit-ubuntu.yml214
-rw-r--r--.gitignore32
-rw-r--r--.mailmap431
-rw-r--r--.rdoc_options3
-rw-r--r--.travis.yml202
-rw-r--r--LEGAL30
-rw-r--r--NEWS.md199
-rw-r--r--README.ja.md4
-rw-r--r--README.md6
-rw-r--r--addr2line.c812
-rw-r--r--addr2line.h4
-rw-r--r--array.c913
-rw-r--r--array.rb73
-rw-r--r--ast.c453
-rw-r--r--ast.rb18
-rwxr-xr-xbasictest/test.rb18
-rw-r--r--benchmark/array_large_literal.yml19
-rw-r--r--benchmark/hash_key.yml5
-rw-r--r--benchmark/loop_each.yml4
-rw-r--r--benchmark/loop_times_megamorphic.yml7
-rw-r--r--benchmark/object_allocate.yml27
-rw-r--r--benchmark/range_bsearch_bignum.yml10
-rw-r--r--benchmark/range_bsearch_endpointless.yml21
-rw-r--r--benchmark/range_bsearch_fixnum.yml10
-rw-r--r--benchmark/range_count.yml11
-rw-r--r--benchmark/range_overlap.yml19
-rw-r--r--benchmark/range_reverse_each.yml16
-rw-r--r--benchmark/realpath.yml3
-rw-r--r--benchmark/regexp_dup.yml6
-rw-r--r--benchmark/regexp_new.yml7
-rw-r--r--benchmark/so_count_words.yml33
-rw-r--r--benchmark/string_dup.yml7
-rw-r--r--benchmark/string_rpartition.yml18
-rw-r--r--benchmark/struct_accessor.yml25
-rw-r--r--benchmark/vm_call_kw_and_kw_splat.yml25
-rw-r--r--benchmark/vm_ivar_ic_miss.yml20
-rw-r--r--benchmark/vm_ivar_memoize.yml85
-rw-r--r--benchmark/vm_method_splat_calls.yml13
-rw-r--r--benchmark/vm_method_splat_calls2.yml27
-rw-r--r--benchmark/vm_super_splat_calls.yml25
-rw-r--r--benchmark/vm_zsuper_splat_calls.yml28
-rw-r--r--bignum.c252
-rwxr-xr-xbootstraptest/runner.rb61
-rw-r--r--bootstraptest/test_eval.rb41
-rw-r--r--bootstraptest/test_exception.rb2
-rw-r--r--bootstraptest/test_finalizer.rb8
-rw-r--r--bootstraptest/test_flow.rb2
-rw-r--r--bootstraptest/test_gc.rb2
-rw-r--r--bootstraptest/test_insns.rb6
-rw-r--r--bootstraptest/test_jump.rb2
-rw-r--r--bootstraptest/test_literal.rb1
-rw-r--r--bootstraptest/test_literal_suffix.rb12
-rw-r--r--bootstraptest/test_method.rb32
-rw-r--r--bootstraptest/test_ractor.rb122
-rw-r--r--bootstraptest/test_rjit.rb14
-rw-r--r--bootstraptest/test_syntax.rb50
-rw-r--r--bootstraptest/test_thread.rb18
-rw-r--r--bootstraptest/test_yjit.rb1023
-rw-r--r--builtin.c13
-rw-r--r--builtin.h2
-rw-r--r--ccan/list/list.h6
-rw-r--r--class.c203
-rw-r--r--common.mk3188
-rw-r--r--compar.c21
-rw-r--r--compile.c3108
-rw-r--r--complex.c941
-rw-r--r--configure.ac276
-rw-r--r--cont.c135
-rw-r--r--coroutine/amd64/Context.S42
-rw-r--r--coroutine/arm64/Context.S67
-rw-r--r--coroutine/arm64/Context.h21
-rw-r--r--coroutine/asyncify/Context.h8
-rw-r--r--coroutine/ppc/Context.S2
-rw-r--r--coroutine/ppc/Context.h2
-rw-r--r--coroutine/ppc64/Context.h2
-rw-r--r--coroutine/win64/Context.h2
-rw-r--r--cygwin/GNUmakefile.in12
-rw-r--r--darray.h124
-rw-r--r--debug.c94
-rw-r--r--debug_counter.h17
-rw-r--r--defs/gmake.mk116
-rw-r--r--defs/known_errors.def164
-rw-r--r--dir.c905
-rw-r--r--dir.rb378
-rw-r--r--dln.c173
-rw-r--r--dln.h3
-rw-r--r--dln_find.c13
-rw-r--r--dmydln.c27
-rw-r--r--dmyenc.c14
-rw-r--r--dmyext.c14
-rw-r--r--doc/.document6
-rw-r--r--doc/ChangeLog/ChangeLog-2.1.02
-rw-r--r--doc/ChangeLog/ChangeLog-2.2.02
-rw-r--r--doc/ChangeLog/ChangeLog-2.4.02
-rw-r--r--doc/NEWS/NEWS-3.3.0.md529
-rw-r--r--doc/_regexp.rdoc1276
-rw-r--r--doc/_timezones.rdoc156
-rw-r--r--doc/bsearch.rdoc2
-rw-r--r--doc/case_mapping.rdoc6
-rw-r--r--doc/character_selectors.rdoc6
-rw-r--r--doc/command_injection.rdoc14
-rw-r--r--doc/command_line/environment.md173
-rw-r--r--doc/contributing/building_ruby.md79
-rw-r--r--doc/contributing/documentation_guide.md133
-rw-r--r--doc/contributing/glossary.md2
-rw-r--r--doc/contributing/testing_ruby.md30
-rw-r--r--doc/csv/options/parsing/liberal_parsing.rdoc23
-rw-r--r--doc/csv/recipes/generating.rdoc2
-rw-r--r--doc/csv/recipes/parsing.rdoc2
-rw-r--r--doc/encodings.rdoc48
-rw-r--r--doc/extension.ja.rdoc48
-rw-r--r--doc/extension.rdoc149
-rw-r--r--doc/format_specifications.rdoc58
-rw-r--r--doc/globals.rdoc491
-rw-r--r--doc/implicit_conversion.rdoc10
-rw-r--r--doc/irb/indexes.md189
-rw-r--r--doc/irb/irb.rd.ja4
-rw-r--r--doc/keywords.rdoc2
-rw-r--r--doc/maintainers.md138
-rw-r--r--doc/optparse/argument_converters.rdoc70
-rw-r--r--doc/optparse/option_params.rdoc6
-rw-r--r--doc/optparse/ruby/argument_abbreviation.rb9
-rw-r--r--doc/optparse/tutorial.rdoc93
-rw-r--r--doc/packed_data.rdoc95
-rw-r--r--doc/pty/README.expect.ja32
-rw-r--r--doc/pty/README.ja50
-rw-r--r--doc/ractor.md4
-rw-r--r--doc/rdoc/markup_reference.rb138
-rw-r--r--doc/regexp.rdoc827
-rw-r--r--doc/regexp/methods.rdoc41
-rw-r--r--doc/regexp/unicode_properties.rdoc678
-rw-r--r--doc/reline/face.md111
-rw-r--r--doc/rjit/README.md53
-rw-r--r--doc/rjit/rjit.md45
-rw-r--r--doc/ruby/option_dump.md297
-rw-r--r--doc/ruby/options.md723
-rw-r--r--doc/security.rdoc2
-rw-r--r--doc/standard_library.rdoc27
-rw-r--r--doc/strftime_formatting.rdoc54
-rw-r--r--doc/string/split.rdoc2
-rw-r--r--doc/syntax.rdoc3
-rw-r--r--doc/syntax/assignment.rdoc4
-rw-r--r--doc/syntax/calling_methods.rdoc14
-rw-r--r--doc/syntax/comments.rdoc2
-rw-r--r--doc/syntax/control_expressions.rdoc2
-rw-r--r--doc/syntax/exceptions.rdoc10
-rw-r--r--doc/syntax/literals.rdoc12
-rw-r--r--doc/syntax/modules_and_classes.rdoc28
-rw-r--r--doc/syntax/operators.rdoc75
-rw-r--r--doc/syntax/refinements.rdoc2
-rw-r--r--doc/timezones.rdoc108
-rw-r--r--doc/windows.md21
-rw-r--r--doc/yjit/yjit.md269
-rw-r--r--enc/Makefile.in6
-rw-r--r--enc/depend78
-rw-r--r--enc/ebcdic.h2
-rw-r--r--enc/encinit.c.erb3
-rwxr-xr-xenc/make_encmake.rb29
-rw-r--r--enc/unicode/15.0.0/name2ctype.h2
-rw-r--r--encoding.c45
-rw-r--r--enum.c54
-rw-r--r--enumerator.c196
-rw-r--r--error.c557
-rw-r--r--eval.c146
-rw-r--r--eval_error.c17
-rw-r--r--eval_intern.h33
-rw-r--r--ext/-test-/RUBY_ALIGNOF/depend1
-rw-r--r--ext/-test-/arith_seq/beg_len_step/depend1
-rw-r--r--ext/-test-/arith_seq/extract/depend1
-rw-r--r--ext/-test-/array/concat/depend1
-rw-r--r--ext/-test-/array/resize/depend1
-rw-r--r--ext/-test-/asan/asan.c24
-rw-r--r--ext/-test-/asan/extconf.rb2
-rw-r--r--ext/-test-/bignum/depend7
-rw-r--r--ext/-test-/bug-14834/depend1
-rw-r--r--ext/-test-/bug-3571/depend1
-rw-r--r--ext/-test-/bug-5832/depend1
-rw-r--r--ext/-test-/bug_reporter/depend1
-rw-r--r--ext/-test-/class/depend2
-rw-r--r--ext/-test-/debug/depend3
-rw-r--r--ext/-test-/debug/profile_frames.c21
-rw-r--r--ext/-test-/dln/empty/depend1
-rw-r--r--ext/-test-/enumerator_kw/depend1
-rw-r--r--ext/-test-/exception/depend4
-rw-r--r--ext/-test-/fatal/depend322
-rw-r--r--ext/-test-/fatal/extconf.rb3
-rw-r--r--ext/-test-/fatal/init.c10
-rw-r--r--ext/-test-/fatal/invalid.c28
-rw-r--r--ext/-test-/fatal/rb_fatal.c3
-rw-r--r--ext/-test-/file/depend175
-rw-r--r--ext/-test-/file/newline_conv.c73
-rw-r--r--ext/-test-/float/depend2
-rw-r--r--ext/-test-/funcall/depend1
-rw-r--r--ext/-test-/gvl/call_without_gvl/depend1
-rw-r--r--ext/-test-/hash/depend2
-rw-r--r--ext/-test-/integer/depend3
-rw-r--r--ext/-test-/iseq_load/depend1
-rw-r--r--ext/-test-/iter/depend3
-rw-r--r--ext/-test-/load/dot.dot/depend1
-rw-r--r--ext/-test-/load/protect/depend1
-rw-r--r--ext/-test-/load/resolve_symbol_resolver/extconf.rb1
-rw-r--r--ext/-test-/load/resolve_symbol_resolver/resolve_symbol_resolver.c55
-rw-r--r--ext/-test-/load/resolve_symbol_target/extconf.rb1
-rw-r--r--ext/-test-/load/resolve_symbol_target/resolve_symbol_target.c15
-rw-r--r--ext/-test-/load/resolve_symbol_target/resolve_symbol_target.def4
-rw-r--r--ext/-test-/load/resolve_symbol_target/resolve_symbol_target.h4
-rw-r--r--ext/-test-/load/stringify_symbols/extconf.rb1
-rw-r--r--ext/-test-/load/stringify_symbols/stringify_symbols.c29
-rw-r--r--ext/-test-/load/stringify_target/extconf.rb1
-rw-r--r--ext/-test-/load/stringify_target/stringify_target.c15
-rw-r--r--ext/-test-/load/stringify_target/stringify_target.def4
-rw-r--r--ext/-test-/load/stringify_target/stringify_target.h4
-rw-r--r--ext/-test-/marshal/compat/depend1
-rw-r--r--ext/-test-/marshal/internal_ivar/depend1
-rw-r--r--ext/-test-/marshal/usr/depend1
-rw-r--r--ext/-test-/memory_view/depend1
-rw-r--r--ext/-test-/memory_view/memory_view.c4
-rw-r--r--ext/-test-/method/depend2
-rw-r--r--ext/-test-/notimplement/depend1
-rw-r--r--ext/-test-/num2int/depend1
-rw-r--r--ext/-test-/path_to_class/depend1
-rw-r--r--ext/-test-/popen_deadlock/depend1
-rw-r--r--ext/-test-/postponed_job/depend1
-rw-r--r--ext/-test-/postponed_job/postponed_job.c131
-rw-r--r--ext/-test-/printf/depend1
-rw-r--r--ext/-test-/proc/depend3
-rw-r--r--ext/-test-/random/depend3
-rw-r--r--ext/-test-/rational/depend1
-rw-r--r--ext/-test-/rb_call_super_kw/depend1
-rw-r--r--ext/-test-/recursion/depend1
-rw-r--r--ext/-test-/regexp/depend2
-rw-r--r--ext/-test-/scan_args/depend1
-rw-r--r--ext/-test-/st/foreach/depend1
-rw-r--r--ext/-test-/st/numhash/depend1
-rw-r--r--ext/-test-/st/update/depend1
-rw-r--r--ext/-test-/string/chilled.c13
-rw-r--r--ext/-test-/string/cstr.c5
-rw-r--r--ext/-test-/string/depend180
-rw-r--r--ext/-test-/string/fstring.c18
-rw-r--r--ext/-test-/string/set_len.c10
-rw-r--r--ext/-test-/struct/data.c13
-rw-r--r--ext/-test-/struct/depend164
-rw-r--r--ext/-test-/struct/member.c2
-rw-r--r--ext/-test-/symbol/depend2
-rw-r--r--ext/-test-/thread/instrumentation/depend1
-rw-r--r--ext/-test-/thread/instrumentation/instrumentation.c206
-rw-r--r--ext/-test-/thread/lock_native_thread/extconf.rb2
-rw-r--r--ext/-test-/thread/lock_native_thread/lock_native_thread.c50
-rw-r--r--ext/-test-/thread_fd/depend1
-rw-r--r--ext/-test-/time/depend3
-rw-r--r--ext/-test-/tracepoint/depend2
-rw-r--r--ext/-test-/tracepoint/gc_hook.c4
-rw-r--r--ext/-test-/typeddata/depend1
-rw-r--r--ext/-test-/vm/depend1
-rw-r--r--ext/-test-/wait/depend1
-rw-r--r--ext/.document3
-rw-r--r--ext/Setup.atheos2
-rw-r--r--ext/Setup.nt2
-rw-r--r--ext/bigdecimal/bigdecimal.c7730
-rw-r--r--ext/bigdecimal/bigdecimal.gemspec54
-rw-r--r--ext/bigdecimal/bigdecimal.h313
-rw-r--r--ext/bigdecimal/bits.h141
-rw-r--r--ext/bigdecimal/depend327
-rw-r--r--ext/bigdecimal/extconf.rb62
-rw-r--r--ext/bigdecimal/feature.h68
-rw-r--r--ext/bigdecimal/lib/bigdecimal.rb5
-rw-r--r--ext/bigdecimal/lib/bigdecimal/jacobian.rb90
-rw-r--r--ext/bigdecimal/lib/bigdecimal/ludcmp.rb89
-rw-r--r--ext/bigdecimal/lib/bigdecimal/math.rb232
-rw-r--r--ext/bigdecimal/lib/bigdecimal/newton.rb80
-rw-r--r--ext/bigdecimal/lib/bigdecimal/util.rb185
-rw-r--r--ext/bigdecimal/missing.c27
-rw-r--r--ext/bigdecimal/missing.h196
-rw-r--r--ext/bigdecimal/missing/dtoa.c3462
-rw-r--r--ext/bigdecimal/sample/linear.rb74
-rw-r--r--ext/bigdecimal/sample/nlsolve.rb40
-rw-r--r--ext/bigdecimal/sample/pi.rb21
-rw-r--r--ext/bigdecimal/static_assert.h54
-rw-r--r--ext/cgi/escape/depend1
-rw-r--r--ext/cgi/escape/escape.c35
-rw-r--r--ext/continuation/depend1
-rw-r--r--ext/coverage/coverage.c3
-rw-r--r--ext/coverage/depend14
-rw-r--r--ext/date/date.gemspec2
-rw-r--r--ext/date/date_core.c10
-rw-r--r--ext/date/depend4
-rw-r--r--ext/date/lib/date.rb2
-rw-r--r--ext/digest/.document3
-rw-r--r--ext/digest/bubblebabble/bubblebabble.c7
-rw-r--r--ext/digest/bubblebabble/depend1
-rw-r--r--ext/digest/depend1
-rw-r--r--ext/digest/digest.h3
-rw-r--r--ext/digest/md5/depend2
-rw-r--r--ext/digest/md5/md5init.c3
-rw-r--r--ext/digest/rmd160/depend2
-rw-r--r--ext/digest/rmd160/rmd160init.c3
-rw-r--r--ext/digest/sha1/depend2
-rw-r--r--ext/digest/sha1/sha1init.c3
-rw-r--r--ext/digest/sha2/depend2
-rw-r--r--ext/digest/sha2/sha2init.c47
-rw-r--r--ext/erb/escape/extconf.rb3
-rw-r--r--ext/etc/.document2
-rw-r--r--ext/etc/depend1
-rw-r--r--ext/etc/etc.c113
-rw-r--r--ext/etc/extconf.rb12
-rw-r--r--ext/etc/mkconstants.rb32
-rwxr-xr-xext/extmk.rb64
-rw-r--r--ext/fcntl/depend1
-rw-r--r--ext/fcntl/fcntl.c100
-rw-r--r--ext/fcntl/fcntl.gemspec3
-rw-r--r--ext/fiddle/closure.c33
-rw-r--r--ext/fiddle/conversions.c55
-rw-r--r--ext/fiddle/conversions.h2
-rw-r--r--ext/fiddle/depend8
-rw-r--r--ext/fiddle/extconf.rb5
-rw-r--r--ext/fiddle/fiddle.c20
-rw-r--r--ext/fiddle/fiddle.gemspec1
-rw-r--r--ext/fiddle/fiddle.h8
-rw-r--r--ext/fiddle/function.c9
-rw-r--r--ext/fiddle/handle.c9
-rw-r--r--ext/fiddle/lib/fiddle/cparser.rb28
-rw-r--r--ext/fiddle/lib/fiddle/import.rb2
-rw-r--r--ext/fiddle/lib/fiddle/pack.rb12
-rw-r--r--ext/fiddle/lib/fiddle/version.rb2
-rw-r--r--ext/fiddle/pointer.c50
-rw-r--r--ext/io/console/.document2
-rw-r--r--ext/io/console/console.c465
-rw-r--r--ext/io/console/depend1
-rw-r--r--ext/io/console/extconf.rb1
-rw-r--r--ext/io/console/io-console.gemspec29
-rw-r--r--ext/io/nonblock/depend1
-rw-r--r--ext/io/nonblock/io-nonblock.gemspec2
-rw-r--r--ext/io/nonblock/nonblock.c7
-rw-r--r--ext/io/wait/depend1
-rw-r--r--ext/io/wait/io-wait.gemspec2
-rw-r--r--ext/json/VERSION1
-rw-r--r--ext/json/generator/depend1
-rw-r--r--ext/json/generator/generator.c144
-rw-r--r--ext/json/generator/generator.h13
-rw-r--r--ext/json/json.gemspec7
-rw-r--r--ext/json/lib/json.rb9
-rw-r--r--ext/json/lib/json/add/bigdecimal.rb45
-rw-r--r--ext/json/lib/json/add/complex.rb33
-rw-r--r--ext/json/lib/json/add/date.rb32
-rw-r--r--ext/json/lib/json/add/date_time.rb33
-rw-r--r--ext/json/lib/json/add/exception.rb30
-rw-r--r--ext/json/lib/json/add/ostruct.rb39
-rw-r--r--ext/json/lib/json/add/range.rb39
-rw-r--r--ext/json/lib/json/add/rational.rb32
-rw-r--r--ext/json/lib/json/add/regexp.rb32
-rw-r--r--ext/json/lib/json/add/set.rb31
-rw-r--r--ext/json/lib/json/add/struct.rb34
-rw-r--r--ext/json/lib/json/add/symbol.rb31
-rw-r--r--ext/json/lib/json/add/time.rb31
-rw-r--r--ext/json/lib/json/common.rb63
-rw-r--r--ext/json/lib/json/generic_object.rb8
-rw-r--r--ext/json/lib/json/version.rb2
-rw-r--r--ext/json/parser/depend1
-rw-r--r--ext/json/parser/parser.c4792
-rw-r--r--ext/json/parser/parser.rl173
-rw-r--r--ext/monitor/depend1
-rw-r--r--ext/nkf/depend181
-rw-r--r--ext/nkf/extconf.rb3
-rw-r--r--ext/nkf/lib/kconv.rb283
-rw-r--r--ext/nkf/nkf-utf8/config.h51
-rw-r--r--ext/nkf/nkf-utf8/nkf.c7205
-rw-r--r--ext/nkf/nkf-utf8/nkf.h189
-rw-r--r--ext/nkf/nkf-utf8/utf8tbl.c14638
-rw-r--r--ext/nkf/nkf-utf8/utf8tbl.h72
-rw-r--r--ext/nkf/nkf.c506
-rw-r--r--ext/nkf/nkf.gemspec35
-rw-r--r--ext/objspace/depend32
-rw-r--r--ext/objspace/objspace.c147
-rw-r--r--ext/objspace/objspace_dump.c61
-rw-r--r--ext/openssl/History.md42
-rw-r--r--ext/openssl/depend259
-rw-r--r--ext/openssl/extconf.rb36
-rw-r--r--ext/openssl/lib/openssl/buffering.rb15
-rw-r--r--ext/openssl/lib/openssl/ssl.rb86
-rw-r--r--ext/openssl/lib/openssl/version.rb2
-rw-r--r--ext/openssl/openssl.gemspec8
-rw-r--r--ext/openssl/ossl.c239
-rw-r--r--ext/openssl/ossl.h11
-rw-r--r--ext/openssl/ossl_bn.c2
-rw-r--r--ext/openssl/ossl_cipher.c6
-rw-r--r--ext/openssl/ossl_config.c2
-rw-r--r--ext/openssl/ossl_digest.c35
-rw-r--r--ext/openssl/ossl_engine.c2
-rw-r--r--ext/openssl/ossl_hmac.c2
-rw-r--r--ext/openssl/ossl_kdf.c12
-rw-r--r--ext/openssl/ossl_ns_spki.c6
-rw-r--r--ext/openssl/ossl_ocsp.c12
-rw-r--r--ext/openssl/ossl_pkcs12.c2
-rw-r--r--ext/openssl/ossl_pkcs7.c6
-rw-r--r--ext/openssl/ossl_pkey.c261
-rw-r--r--ext/openssl/ossl_pkey_dh.c29
-rw-r--r--ext/openssl/ossl_pkey_dsa.c65
-rw-r--r--ext/openssl/ossl_pkey_ec.c74
-rw-r--r--ext/openssl/ossl_pkey_rsa.c81
-rw-r--r--ext/openssl/ossl_provider.c211
-rw-r--r--ext/openssl/ossl_provider.h5
-rw-r--r--ext/openssl/ossl_ssl.c111
-rw-r--r--ext/openssl/ossl_ssl_session.c2
-rw-r--r--ext/openssl/ossl_ts.c6
-rw-r--r--ext/openssl/ossl_x509attr.c2
-rw-r--r--ext/openssl/ossl_x509cert.c2
-rw-r--r--ext/openssl/ossl_x509crl.c2
-rw-r--r--ext/openssl/ossl_x509ext.c20
-rw-r--r--ext/openssl/ossl_x509name.c2
-rw-r--r--ext/openssl/ossl_x509req.c2
-rw-r--r--ext/openssl/ossl_x509revoked.c2
-rw-r--r--ext/openssl/ossl_x509store.c17
-rw-r--r--ext/pathname/depend1
-rw-r--r--ext/pathname/extconf.rb1
-rw-r--r--ext/pathname/lib/pathname.rb5
-rw-r--r--ext/pathname/pathname.c5
-rw-r--r--ext/psych/depend5
-rw-r--r--ext/psych/lib/psych.rb56
-rw-r--r--ext/psych/lib/psych/versions.rb4
-rw-r--r--ext/psych/lib/psych/visitors/to_ruby.rb2
-rw-r--r--ext/psych/lib/psych/visitors/yaml_tree.rb24
-rw-r--r--ext/psych/psych.gemspec47
-rw-r--r--ext/pty/depend1
-rw-r--r--ext/pty/extconf.rb7
-rw-r--r--ext/pty/pty.c115
-rw-r--r--ext/racc/cparse/README11
-rw-r--r--ext/racc/cparse/cparse.c861
-rw-r--r--ext/racc/cparse/depend161
-rw-r--r--ext/racc/cparse/extconf.rb9
-rw-r--r--ext/rbconfig/sizeof/depend2
-rw-r--r--ext/ripper/depend555
-rw-r--r--ext/ripper/eventids2.c14
-rw-r--r--ext/ripper/eventids2.h8
-rw-r--r--ext/ripper/extconf.rb4
-rw-r--r--ext/ripper/ripper_init.c.tmpl669
-rw-r--r--ext/ripper/ripper_init.h7
-rw-r--r--ext/ripper/tools/dsl.rb48
-rw-r--r--ext/ripper/tools/generate.rb45
-rw-r--r--ext/ripper/tools/preproc.rb85
-rw-r--r--ext/socket/ancdata.c4
-rw-r--r--ext/socket/depend60
-rw-r--r--ext/socket/extconf.rb5
-rw-r--r--ext/socket/getaddrinfo.c3
-rw-r--r--ext/socket/init.c82
-rw-r--r--ext/socket/lib/socket.rb533
-rw-r--r--ext/socket/mkconstants.rb46
-rw-r--r--ext/socket/raddrinfo.c525
-rw-r--r--ext/socket/rubysocket.h6
-rw-r--r--ext/socket/socket.c5
-rw-r--r--ext/stringio/.document1
-rw-r--r--ext/stringio/depend1
-rw-r--r--ext/stringio/extconf.rb6
-rw-r--r--ext/stringio/stringio.c250
-rw-r--r--ext/stringio/stringio.gemspec11
-rw-r--r--ext/strscan/depend1
-rw-r--r--ext/strscan/extconf.rb1
-rw-r--r--ext/strscan/strscan.c186
-rw-r--r--ext/syslog/depend161
-rw-r--r--ext/syslog/extconf.rb13
-rw-r--r--ext/syslog/lib/syslog/logger.rb209
-rw-r--r--ext/syslog/syslog.c592
-rw-r--r--ext/syslog/syslog.gemspec28
-rw-r--r--ext/syslog/syslog.txt124
-rw-r--r--ext/win32/lib/win32/registry.rb9
-rw-r--r--ext/win32ole/.document1
-rw-r--r--ext/win32ole/lib/win32ole.rb3
-rw-r--r--ext/win32ole/lib/win32ole/property.rb18
-rw-r--r--ext/win32ole/sample/olegen.rb348
-rw-r--r--ext/win32ole/win32ole.c98
-rw-r--r--ext/win32ole/win32ole.gemspec8
-rw-r--r--ext/win32ole/win32ole_error.c17
-rw-r--r--ext/win32ole/win32ole_event.c51
-rw-r--r--ext/win32ole/win32ole_method.c127
-rw-r--r--ext/win32ole/win32ole_param.c113
-rw-r--r--ext/win32ole/win32ole_record.c51
-rw-r--r--ext/win32ole/win32ole_type.c142
-rw-r--r--ext/win32ole/win32ole_typelib.c75
-rw-r--r--ext/win32ole/win32ole_variable.c37
-rw-r--r--ext/win32ole/win32ole_variant.c67
-rw-r--r--ext/win32ole/win32ole_variant_m.c12
-rw-r--r--ext/zlib/depend1
-rw-r--r--ext/zlib/extconf.rb10
-rw-r--r--ext/zlib/zlib.c11
-rw-r--r--ext/zlib/zlib.gemspec2
-rw-r--r--file.c884
-rw-r--r--gc.c3357
-rw-r--r--gc.rb90
-rw-r--r--gem_prelude.rb2
-rw-r--r--gems/bundled_gems38
-rw-r--r--gems/lib/core_assertions.rb1
-rw-r--r--gems/lib/envutil.rb1
-rw-r--r--gems/lib/rake/extensiontask.rb12
-rw-r--r--hash.c894
-rw-r--r--hrtime.h14
-rw-r--r--id_table.c4
-rw-r--r--imemo.c588
-rw-r--r--include/ruby/assert.h100
-rw-r--r--include/ruby/atomic.h67
-rw-r--r--include/ruby/debug.h171
-rw-r--r--include/ruby/fiber/scheduler.h2
-rw-r--r--include/ruby/internal/abi.h2
-rw-r--r--include/ruby/internal/attr/noexcept.h2
-rw-r--r--include/ruby/internal/core/rarray.h181
-rw-r--r--include/ruby/internal/core/rbasic.h18
-rw-r--r--include/ruby/internal/core/rmatch.h14
-rw-r--r--include/ruby/internal/core/robject.h7
-rw-r--r--include/ruby/internal/core/rstring.h66
-rw-r--r--include/ruby/internal/core/rtypeddata.h44
-rw-r--r--include/ruby/internal/encoding/encoding.h1
-rw-r--r--include/ruby/internal/encoding/string.h2
-rw-r--r--include/ruby/internal/error.h22
-rw-r--r--include/ruby/internal/event.h5
-rw-r--r--include/ruby/internal/fl_type.h96
-rw-r--r--include/ruby/internal/gc.h38
-rw-r--r--include/ruby/internal/globals.h2
-rw-r--r--include/ruby/internal/has/c_attribute.h12
-rw-r--r--include/ruby/internal/intern/array.h2
-rw-r--r--include/ruby/internal/intern/bignum.h4
-rw-r--r--include/ruby/internal/intern/class.h8
-rw-r--r--include/ruby/internal/intern/error.h1
-rw-r--r--include/ruby/internal/intern/load.h37
-rw-r--r--include/ruby/internal/intern/object.h3
-rw-r--r--include/ruby/internal/intern/process.h11
-rw-r--r--include/ruby/internal/intern/re.h5
-rw-r--r--include/ruby/internal/intern/select.h2
-rw-r--r--include/ruby/internal/intern/signal.h8
-rw-r--r--include/ruby/internal/intern/string.h19
-rw-r--r--include/ruby/internal/intern/struct.h38
-rw-r--r--include/ruby/internal/intern/vm.h3
-rw-r--r--include/ruby/internal/interpreter.h2
-rw-r--r--include/ruby/internal/memory.h10
-rw-r--r--include/ruby/internal/module.h16
-rw-r--r--include/ruby/internal/newobj.h49
-rw-r--r--include/ruby/internal/static_assert.h2
-rw-r--r--include/ruby/internal/stdckdint.h68
-rw-r--r--include/ruby/io.h40
-rw-r--r--include/ruby/io/buffer.h28
-rw-r--r--include/ruby/memory_view.h4
-rw-r--r--include/ruby/onigmo.h2
-rw-r--r--include/ruby/random.h2
-rw-r--r--include/ruby/re.h25
-rw-r--r--include/ruby/ruby.h124
-rw-r--r--include/ruby/st.h2
-rw-r--r--include/ruby/thread.h100
-rw-r--r--include/ruby/util.h4
-rw-r--r--include/ruby/version.h2
-rw-r--r--include/ruby/vm.h7
-rw-r--r--include/ruby/win32.h1
-rw-r--r--inits.c9
-rw-r--r--insns.def156
-rw-r--r--internal.h4
-rw-r--r--internal/array.h29
-rw-r--r--internal/bits.h20
-rw-r--r--internal/class.h72
-rw-r--r--internal/cmdlineopt.h6
-rw-r--r--internal/compile.h1
-rw-r--r--internal/cont.h3
-rw-r--r--internal/encoding.h4
-rw-r--r--internal/error.h3
-rw-r--r--internal/eval.h1
-rw-r--r--internal/gc.h87
-rw-r--r--internal/hash.h7
-rw-r--r--internal/imemo.h35
-rw-r--r--internal/inits.h3
-rw-r--r--internal/io.h102
-rw-r--r--internal/missing.h1
-rw-r--r--internal/numeric.h3
-rw-r--r--internal/object.h4
-rw-r--r--internal/parse.h106
-rw-r--r--internal/random.h1
-rw-r--r--internal/re.h2
-rw-r--r--internal/ruby_parser.h102
-rw-r--r--internal/sanitizers.h159
-rw-r--r--internal/signal.h5
-rw-r--r--internal/st.h11
-rw-r--r--internal/string.h41
-rw-r--r--internal/struct.h30
-rw-r--r--internal/symbol.h3
-rw-r--r--internal/thread.h5
-rw-r--r--internal/transcode.h3
-rw-r--r--internal/variable.h58
-rw-r--r--internal/vm.h19
-rw-r--r--io.c998
-rw-r--r--io.rb13
-rw-r--r--io_buffer.c951
-rw-r--r--iseq.c604
-rw-r--r--iseq.h26
-rw-r--r--kernel.rb131
-rw-r--r--lib/English.gemspec2
-rw-r--r--lib/English.rb22
-rw-r--r--lib/abbrev.gemspec29
-rw-r--r--lib/abbrev.rb133
-rw-r--r--lib/base64.gemspec27
-rw-r--r--lib/base64.rb113
-rw-r--r--lib/benchmark.rb2
-rw-r--r--lib/bundled_gems.rb189
-rw-r--r--lib/bundler.rb124
-rw-r--r--lib/bundler/build_metadata.rb2
-rw-r--r--lib/bundler/bundler.gemspec8
-rw-r--r--lib/bundler/capistrano.rb2
-rw-r--r--lib/bundler/checksum.rb254
-rw-r--r--lib/bundler/ci_detector.rb75
-rw-r--r--lib/bundler/cli.rb501
-rw-r--r--lib/bundler/cli/add.rb6
-rw-r--r--lib/bundler/cli/binstubs.rb8
-rw-r--r--lib/bundler/cli/cache.rb2
-rw-r--r--lib/bundler/cli/check.rb4
-rw-r--r--lib/bundler/cli/common.rb10
-rw-r--r--lib/bundler/cli/config.rb15
-rw-r--r--lib/bundler/cli/console.rb5
-rw-r--r--lib/bundler/cli/doctor.rb4
-rw-r--r--lib/bundler/cli/exec.rb2
-rw-r--r--lib/bundler/cli/gem.rb72
-rw-r--r--lib/bundler/cli/info.rb15
-rw-r--r--lib/bundler/cli/install.rb13
-rw-r--r--lib/bundler/cli/lock.rb56
-rw-r--r--lib/bundler/cli/open.rb12
-rw-r--r--lib/bundler/cli/outdated.rb12
-rw-r--r--lib/bundler/cli/plugin.rb24
-rw-r--r--lib/bundler/cli/pristine.rb68
-rw-r--r--lib/bundler/cli/update.rb11
-rw-r--r--lib/bundler/compact_index_client.rb21
-rw-r--r--lib/bundler/compact_index_client/cache.rb38
-rw-r--r--lib/bundler/compact_index_client/cache_file.rb153
-rw-r--r--lib/bundler/compact_index_client/gem_parser.rb10
-rw-r--r--lib/bundler/compact_index_client/updater.rb160
-rw-r--r--lib/bundler/constants.rb2
-rw-r--r--lib/bundler/current_ruby.rb2
-rw-r--r--lib/bundler/definition.rb405
-rw-r--r--lib/bundler/dependency.rb34
-rw-r--r--lib/bundler/digest.rb2
-rw-r--r--lib/bundler/dsl.rb98
-rw-r--r--lib/bundler/endpoint_specification.rb8
-rw-r--r--lib/bundler/env.rb4
-rw-r--r--lib/bundler/environment_preserver.rb28
-rw-r--r--lib/bundler/errors.rb58
-rw-r--r--lib/bundler/feature_flag.rb1
-rw-r--r--lib/bundler/fetcher.rb126
-rw-r--r--lib/bundler/fetcher/base.rb8
-rw-r--r--lib/bundler/fetcher/compact_index.rb16
-rw-r--r--lib/bundler/fetcher/dependency.rb2
-rw-r--r--lib/bundler/fetcher/downloader.rb26
-rw-r--r--lib/bundler/fetcher/gem_remote_fetcher.rb16
-rw-r--r--lib/bundler/fetcher/index.rb5
-rw-r--r--lib/bundler/friendly_errors.rb4
-rw-r--r--lib/bundler/gem_helper.rb2
-rw-r--r--lib/bundler/gem_helpers.rb14
-rw-r--r--lib/bundler/gem_version_promoter.rb82
-rw-r--r--lib/bundler/graph.rb18
-rw-r--r--lib/bundler/index.rb96
-rw-r--r--lib/bundler/injector.rb9
-rw-r--r--lib/bundler/inline.rb6
-rw-r--r--lib/bundler/installer.rb32
-rw-r--r--lib/bundler/installer/gem_installer.rb14
-rw-r--r--lib/bundler/installer/parallel_installer.rb50
-rw-r--r--lib/bundler/installer/standalone.rb19
-rw-r--r--lib/bundler/lazy_specification.rb48
-rw-r--r--lib/bundler/lockfile_generator.rb11
-rw-r--r--lib/bundler/lockfile_parser.rb152
-rw-r--r--lib/bundler/man/bundle-add.129
-rw-r--r--lib/bundler/man/bundle-binstubs.120
-rw-r--r--lib/bundler/man/bundle-cache.127
-rw-r--r--lib/bundler/man/bundle-check.117
-rw-r--r--lib/bundler/man/bundle-check.1.ronn3
-rw-r--r--lib/bundler/man/bundle-clean.113
-rw-r--r--lib/bundler/man/bundle-config.1238
-rw-r--r--lib/bundler/man/bundle-config.1.ronn16
-rw-r--r--lib/bundler/man/bundle-console.126
-rw-r--r--lib/bundler/man/bundle-doctor.122
-rw-r--r--lib/bundler/man/bundle-exec.187
-rw-r--r--lib/bundler/man/bundle-exec.1.ronn5
-rw-r--r--lib/bundler/man/bundle-gem.162
-rw-r--r--lib/bundler/man/bundle-help.110
-rw-r--r--lib/bundler/man/bundle-info.116
-rw-r--r--lib/bundler/man/bundle-info.1.ronn6
-rw-r--r--lib/bundler/man/bundle-init.115
-rw-r--r--lib/bundler/man/bundle-inject.125
-rw-r--r--lib/bundler/man/bundle-install.1156
-rw-r--r--lib/bundler/man/bundle-install.1.ronn5
-rw-r--r--lib/bundler/man/bundle-list.123
-rw-r--r--lib/bundler/man/bundle-lock.134
-rw-r--r--lib/bundler/man/bundle-open.134
-rw-r--r--lib/bundler/man/bundle-outdated.178
-rw-r--r--lib/bundler/man/bundle-outdated.1.ronn20
-rw-r--r--lib/bundler/man/bundle-platform.132
-rw-r--r--lib/bundler/man/bundle-plugin.171
-rw-r--r--lib/bundler/man/bundle-plugin.1.ronn20
-rw-r--r--lib/bundler/man/bundle-pristine.121
-rw-r--r--lib/bundler/man/bundle-remove.118
-rw-r--r--lib/bundler/man/bundle-show.113
-rw-r--r--lib/bundler/man/bundle-update.1155
-rw-r--r--lib/bundler/man/bundle-version.119
-rw-r--r--lib/bundler/man/bundle-viz.120
-rw-r--r--lib/bundler/man/bundle.149
-rw-r--r--lib/bundler/man/gemfile.5333
-rw-r--r--lib/bundler/man/gemfile.5.ronn16
-rw-r--r--lib/bundler/match_metadata.rb4
-rw-r--r--lib/bundler/match_platform.rb2
-rw-r--r--lib/bundler/mirror.rb6
-rw-r--r--lib/bundler/plugin.rb19
-rw-r--r--lib/bundler/plugin/api/source.rb9
-rw-r--r--lib/bundler/plugin/events.rb24
-rw-r--r--lib/bundler/plugin/index.rb8
-rw-r--r--lib/bundler/plugin/installer.rb54
-rw-r--r--lib/bundler/plugin/installer/path.rb18
-rw-r--r--lib/bundler/plugin/source_list.rb8
-rw-r--r--lib/bundler/remote_specification.rb4
-rw-r--r--lib/bundler/resolver.rb183
-rw-r--r--lib/bundler/resolver/base.rb2
-rw-r--r--lib/bundler/resolver/candidate.rb2
-rw-r--r--lib/bundler/resolver/incompatibility.rb2
-rw-r--r--lib/bundler/resolver/package.rb5
-rw-r--r--lib/bundler/resolver/spec_group.rb5
-rw-r--r--lib/bundler/retry.rb2
-rw-r--r--lib/bundler/ruby_dsl.rb49
-rw-r--r--lib/bundler/ruby_version.rb11
-rw-r--r--lib/bundler/rubygems_ext.rb111
-rw-r--r--lib/bundler/rubygems_gem_installer.rb81
-rw-r--r--lib/bundler/rubygems_integration.rb115
-rw-r--r--lib/bundler/runtime.rb27
-rw-r--r--lib/bundler/self_manager.rb36
-rw-r--r--lib/bundler/settings.rb145
-rw-r--r--lib/bundler/setup.rb8
-rw-r--r--lib/bundler/shared_helpers.rb63
-rw-r--r--lib/bundler/source.rb4
-rw-r--r--lib/bundler/source/git.rb63
-rw-r--r--lib/bundler/source/git/git_proxy.rb107
-rw-r--r--lib/bundler/source/metadata.rb32
-rw-r--r--lib/bundler/source/path.rb13
-rw-r--r--lib/bundler/source/rubygems.rb109
-rw-r--r--lib/bundler/source/rubygems/remote.rb2
-rw-r--r--lib/bundler/source_list.rb25
-rw-r--r--lib/bundler/spec_set.rb132
-rw-r--r--lib/bundler/stub_specification.rb7
-rw-r--r--lib/bundler/templates/Executable.bundler2
-rw-r--r--lib/bundler/templates/newgem/Rakefile.tt16
-rw-r--r--lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt2
-rw-r--r--lib/bundler/templates/newgem/ext/newgem/src/lib.rs.tt8
-rw-r--r--lib/bundler/templates/newgem/github/workflows/main.yml.tt2
-rw-r--r--lib/bundler/templates/newgem/newgem.gemspec.tt8
-rw-r--r--lib/bundler/templates/newgem/rubocop.yml.tt5
-rw-r--r--lib/bundler/templates/newgem/standard.yml.tt2
-rw-r--r--lib/bundler/ui/shell.rb4
-rw-r--r--lib/bundler/uri_credentials_filter.rb4
-rw-r--r--lib/bundler/vendor/connection_pool/.document (renamed from lib/rubygems/optparse/.document)0
-rw-r--r--lib/bundler/vendor/connection_pool/lib/connection_pool.rb59
-rw-r--r--lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb2
-rw-r--r--lib/bundler/vendor/fileutils/.document (renamed from lib/rubygems/tsort/.document)0
-rw-r--r--lib/bundler/vendor/fileutils/lib/fileutils.rb28
-rw-r--r--lib/bundler/vendor/net-http-persistent/.document1
-rw-r--r--lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb114
-rw-r--r--lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/connection.rb7
-rw-r--r--lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/pool.rb34
-rw-r--r--lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb2
-rw-r--r--lib/bundler/vendor/pub_grub/.document1
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb1
-rw-r--r--lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb13
-rw-r--r--lib/bundler/vendor/thor/.document1
-rw-r--r--lib/bundler/vendor/thor/lib/thor.rb161
-rw-r--r--lib/bundler/vendor/thor/lib/thor/actions.rb29
-rw-r--r--lib/bundler/vendor/thor/lib/thor/actions/create_file.rb3
-rw-r--r--lib/bundler/vendor/thor/lib/thor/actions/directory.rb2
-rw-r--r--lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb2
-rw-r--r--lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb14
-rw-r--r--lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb19
-rw-r--r--lib/bundler/vendor/thor/lib/thor/base.rb145
-rw-r--r--lib/bundler/vendor/thor/lib/thor/command.rb17
-rw-r--r--lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb4
-rw-r--r--lib/bundler/vendor/thor/lib/thor/error.rb41
-rw-r--r--lib/bundler/vendor/thor/lib/thor/invocation.rb2
-rw-r--r--lib/bundler/vendor/thor/lib/thor/nested_context.rb4
-rw-r--r--lib/bundler/vendor/thor/lib/thor/parser/argument.rb21
-rw-r--r--lib/bundler/vendor/thor/lib/thor/parser/arguments.rb48
-rw-r--r--lib/bundler/vendor/thor/lib/thor/parser/option.rb25
-rw-r--r--lib/bundler/vendor/thor/lib/thor/parser/options.rb46
-rw-r--r--lib/bundler/vendor/thor/lib/thor/runner.rb22
-rw-r--r--lib/bundler/vendor/thor/lib/thor/shell.rb2
-rw-r--r--lib/bundler/vendor/thor/lib/thor/shell/basic.rb174
-rw-r--r--lib/bundler/vendor/thor/lib/thor/shell/color.rb47
-rw-r--r--lib/bundler/vendor/thor/lib/thor/shell/column_printer.rb29
-rw-r--r--lib/bundler/vendor/thor/lib/thor/shell/html.rb45
-rw-r--r--lib/bundler/vendor/thor/lib/thor/shell/table_printer.rb134
-rw-r--r--lib/bundler/vendor/thor/lib/thor/shell/terminal.rb42
-rw-r--r--lib/bundler/vendor/thor/lib/thor/shell/wrapped_printer.rb38
-rw-r--r--lib/bundler/vendor/thor/lib/thor/util.rb7
-rw-r--r--lib/bundler/vendor/thor/lib/thor/version.rb2
-rw-r--r--lib/bundler/vendor/tsort/.document1
-rw-r--r--lib/bundler/vendor/tsort/lib/tsort.rb3
-rw-r--r--lib/bundler/vendor/uri/.document1
-rw-r--r--lib/bundler/vendor/uri/lib/uri/common.rb388
-rw-r--r--lib/bundler/vendor/uri/lib/uri/generic.rb1
-rw-r--r--lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb4
-rw-r--r--lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb128
-rw-r--r--lib/bundler/vendor/uri/lib/uri/version.rb2
-rw-r--r--lib/bundler/vendored_net_http.rb12
-rw-r--r--lib/bundler/vendored_persistent.rb4
-rw-r--r--lib/bundler/vendored_timeout.rb12
-rw-r--r--lib/bundler/vendored_uri.rb19
-rw-r--r--lib/bundler/version.rb2
-rw-r--r--lib/bundler/vlad.rb2
-rw-r--r--lib/bundler/yaml_serializer.rb38
-rw-r--r--lib/cgi.rb2
-rw-r--r--lib/cgi/util.rb7
-rw-r--r--lib/csv.rb2881
-rw-r--r--lib/csv/core_ext/array.rb9
-rw-r--r--lib/csv/core_ext/string.rb9
-rw-r--r--lib/csv/csv.gemspec64
-rw-r--r--lib/csv/fields_converter.rb89
-rw-r--r--lib/csv/input_record_separator.rb18
-rw-r--r--lib/csv/parser.rb1288
-rw-r--r--lib/csv/row.rb757
-rw-r--r--lib/csv/table.rb1055
-rw-r--r--lib/csv/version.rb6
-rw-r--r--lib/csv/writer.rb210
-rw-r--r--lib/delegate.rb6
-rw-r--r--lib/did_you_mean/jaro_winkler.rb7
-rw-r--r--lib/did_you_mean/spell_checkers/key_error_checker.rb10
-rw-r--r--lib/did_you_mean/spell_checkers/pattern_key_name_checker.rb10
-rw-r--r--lib/drb.rb3
-rw-r--r--lib/drb/acl.rb239
-rw-r--r--lib/drb/drb.gemspec43
-rw-r--r--lib/drb/drb.rb1942
-rw-r--r--lib/drb/eq.rb15
-rw-r--r--lib/drb/extserv.rb44
-rw-r--r--lib/drb/extservm.rb94
-rw-r--r--lib/drb/gw.rb161
-rw-r--r--lib/drb/invokemethod.rb35
-rw-r--r--lib/drb/observer.rb26
-rw-r--r--lib/drb/ssl.rb344
-rw-r--r--lib/drb/timeridconv.rb97
-rw-r--r--lib/drb/unix.rb118
-rw-r--r--lib/drb/version.rb3
-rw-r--r--lib/drb/weakidconv.rb59
-rw-r--r--lib/erb/compiler.rb1
-rw-r--r--lib/erb/def_method.rb1
-rw-r--r--lib/erb/util.rb1
-rw-r--r--lib/erb/version.rb2
-rw-r--r--lib/error_highlight/base.rb11
-rw-r--r--lib/error_highlight/version.rb2
-rw-r--r--lib/fileutils.rb28
-rw-r--r--lib/find.rb2
-rw-r--r--lib/getoptlong.gemspec30
-rw-r--r--lib/getoptlong.rb867
-rw-r--r--lib/ipaddr.rb110
-rw-r--r--lib/irb.rb1844
-rw-r--r--lib/irb/cmd/backtrace.rb21
-rw-r--r--lib/irb/cmd/break.rb21
-rw-r--r--lib/irb/cmd/catch.rb21
-rw-r--r--lib/irb/cmd/chws.rb36
-rw-r--r--lib/irb/cmd/continue.rb17
-rw-r--r--lib/irb/cmd/debug.rb136
-rw-r--r--lib/irb/cmd/delete.rb17
-rw-r--r--lib/irb/cmd/edit.rb61
-rw-r--r--lib/irb/cmd/finish.rb17
-rw-r--r--lib/irb/cmd/fork.rb34
-rw-r--r--lib/irb/cmd/help.rb23
-rw-r--r--lib/irb/cmd/info.rb21
-rw-r--r--lib/irb/cmd/irb_info.rb33
-rw-r--r--lib/irb/cmd/load.rb76
-rw-r--r--lib/irb/cmd/ls.rb132
-rw-r--r--lib/irb/cmd/measure.rb48
-rw-r--r--lib/irb/cmd/next.rb17
-rw-r--r--lib/irb/cmd/nop.rb55
-rw-r--r--lib/irb/cmd/pushws.rb45
-rw-r--r--lib/irb/cmd/show_cmds.rb39
-rw-r--r--lib/irb/cmd/show_doc.rb48
-rw-r--r--lib/irb/cmd/show_source.rb113
-rw-r--r--lib/irb/cmd/step.rb17
-rw-r--r--lib/irb/cmd/subirb.rb66
-rw-r--r--lib/irb/cmd/whereami.rb25
-rw-r--r--lib/irb/color.rb2
-rw-r--r--lib/irb/command.rb23
-rw-r--r--lib/irb/command/backtrace.rb17
-rw-r--r--lib/irb/command/base.rb62
-rw-r--r--lib/irb/command/break.rb17
-rw-r--r--lib/irb/command/catch.rb17
-rw-r--r--lib/irb/command/chws.rb40
-rw-r--r--lib/irb/command/context.rb16
-rw-r--r--lib/irb/command/continue.rb17
-rw-r--r--lib/irb/command/debug.rb86
-rw-r--r--lib/irb/command/delete.rb17
-rw-r--r--lib/irb/command/disable_irb.rb19
-rw-r--r--lib/irb/command/edit.rb63
-rw-r--r--lib/irb/command/exit.rb18
-rw-r--r--lib/irb/command/finish.rb17
-rw-r--r--lib/irb/command/force_exit.rb18
-rw-r--r--lib/irb/command/help.rb75
-rw-r--r--lib/irb/command/history.rb45
-rw-r--r--lib/irb/command/info.rb17
-rw-r--r--lib/irb/command/internal_helpers.rb27
-rw-r--r--lib/irb/command/irb_info.rb33
-rw-r--r--lib/irb/command/load.rb91
-rw-r--r--lib/irb/command/ls.rb155
-rw-r--r--lib/irb/command/measure.rb49
-rw-r--r--lib/irb/command/next.rb17
-rw-r--r--lib/irb/command/pushws.rb65
-rw-r--r--lib/irb/command/show_doc.rb51
-rw-r--r--lib/irb/command/show_source.rb74
-rw-r--r--lib/irb/command/step.rb17
-rw-r--r--lib/irb/command/subirb.rb123
-rw-r--r--lib/irb/command/whereami.rb23
-rw-r--r--lib/irb/completion.rb237
-rw-r--r--lib/irb/context.rb247
-rw-r--r--lib/irb/debug.rb130
-rw-r--r--lib/irb/debug/ui.rb103
-rw-r--r--lib/irb/default_commands.rb260
-rw-r--r--lib/irb/easter-egg.rb22
-rw-r--r--lib/irb/ext/change-ws.rb14
-rw-r--r--lib/irb/ext/eval_history.rb149
-rw-r--r--lib/irb/ext/history.rb149
-rw-r--r--lib/irb/ext/loader.rb8
-rw-r--r--lib/irb/ext/multi-irb.rb10
-rw-r--r--lib/irb/ext/save-history.rb125
-rw-r--r--lib/irb/ext/tracer.rb63
-rw-r--r--lib/irb/ext/use-loader.rb14
-rw-r--r--lib/irb/ext/workspaces.rb44
-rw-r--r--lib/irb/extend-command.rb356
-rw-r--r--lib/irb/frame.rb2
-rw-r--r--lib/irb/help.rb6
-rw-r--r--lib/irb/helper_method.rb29
-rw-r--r--lib/irb/helper_method/base.rb16
-rw-r--r--lib/irb/helper_method/conf.rb11
-rw-r--r--lib/irb/history.rb87
-rw-r--r--lib/irb/init.rb185
-rw-r--r--lib/irb/input-method.rb439
-rw-r--r--lib/irb/inspector.rb6
-rw-r--r--lib/irb/irb.gemspec3
-rw-r--r--lib/irb/lc/error.rb12
-rw-r--r--lib/irb/lc/help-message4
-rw-r--r--lib/irb/lc/ja/error.rb12
-rw-r--r--lib/irb/lc/ja/help-message13
-rw-r--r--lib/irb/locale.rb4
-rw-r--r--lib/irb/nesting_parser.rb237
-rw-r--r--lib/irb/notifier.rb2
-rw-r--r--lib/irb/output-method.rb10
-rw-r--r--lib/irb/pager.rb91
-rw-r--r--lib/irb/ruby-lex.rb1125
-rw-r--r--lib/irb/ruby_logo.aa43
-rw-r--r--lib/irb/source_finder.rb139
-rw-r--r--lib/irb/src_encoding.rb7
-rw-r--r--lib/irb/statement.rb80
-rw-r--r--lib/irb/version.rb6
-rw-r--r--lib/irb/workspace.rb28
-rw-r--r--lib/irb/ws-for-case-2.rb2
-rw-r--r--lib/irb/xmp.rb6
-rw-r--r--lib/logger.rb3
-rw-r--r--lib/logger/logger.gemspec6
-rw-r--r--lib/logger/version.rb2
-rw-r--r--lib/mkmf.rb246
-rw-r--r--lib/mutex_m.gemspec28
-rw-r--r--lib/mutex_m.rb116
-rw-r--r--lib/net/http.rb22
-rw-r--r--lib/net/http/backward.rb2
-rw-r--r--lib/net/http/exceptions.rb2
-rw-r--r--lib/net/http/generic_request.rb14
-rw-r--r--lib/net/http/header.rb14
-rw-r--r--lib/net/http/proxy_delta.rb2
-rw-r--r--lib/net/http/request.rb2
-rw-r--r--lib/net/http/requests.rb2
-rw-r--r--lib/net/http/response.rb9
-rw-r--r--lib/net/https.rb2
-rw-r--r--lib/net/net-protocol.gemspec1
-rw-r--r--lib/net/protocol.rb2
-rw-r--r--lib/observer.gemspec32
-rw-r--r--lib/observer.rb229
-rw-r--r--lib/open-uri.rb9
-rw-r--r--lib/open3.rb1268
-rw-r--r--lib/open3/version.rb2
-rw-r--r--lib/optparse.rb202
-rw-r--r--lib/optparse/ac.rb16
-rw-r--r--lib/optparse/kwargs.rb11
-rw-r--r--lib/optparse/optparse.gemspec3
-rw-r--r--lib/optparse/version.rb9
-rw-r--r--lib/ostruct.gemspec3
-rw-r--r--lib/ostruct.rb16
-rw-r--r--lib/pp.rb36
-rw-r--r--lib/prettyprint.rb6
-rw-r--r--lib/prism.rb90
-rw-r--r--lib/prism/debug.rb249
-rw-r--r--lib/prism/desugar_compiler.rb354
-rw-r--r--lib/prism/ffi.rb437
-rw-r--r--lib/prism/lex_compat.rb927
-rw-r--r--lib/prism/node_ext.rb260
-rw-r--r--lib/prism/pack.rb228
-rw-r--r--lib/prism/parse_result.rb618
-rw-r--r--lib/prism/parse_result/comments.rb194
-rw-r--r--lib/prism/parse_result/newlines.rb64
-rw-r--r--lib/prism/pattern.rb262
-rw-r--r--lib/prism/polyfill/string.rb14
-rw-r--r--lib/prism/prism.gemspec165
-rw-r--r--lib/prism/translation.rb13
-rw-r--r--lib/prism/translation/parser.rb302
-rw-r--r--lib/prism/translation/parser/compiler.rb1996
-rw-r--r--lib/prism/translation/parser/lexer.rb416
-rw-r--r--lib/prism/translation/parser/rubocop.rb73
-rw-r--r--lib/prism/translation/parser33.rb12
-rw-r--r--lib/prism/translation/parser34.rb12
-rw-r--r--lib/prism/translation/ripper.rb3446
-rw-r--r--lib/prism/translation/ripper/sexp.rb125
-rw-r--r--lib/prism/translation/ripper/shim.rb5
-rw-r--r--lib/prism/translation/ruby_parser.rb1576
-rw-r--r--lib/pstore.rb12
-rw-r--r--lib/racc.rb6
-rw-r--r--lib/racc/compat.rb33
-rw-r--r--lib/racc/debugflags.rb60
-rw-r--r--lib/racc/exception.rb16
-rw-r--r--lib/racc/grammar.rb1114
-rw-r--r--lib/racc/grammarfileparser.rb561
-rw-r--r--lib/racc/info.rb17
-rw-r--r--lib/racc/iset.rb92
-rw-r--r--lib/racc/logfilegenerator.rb212
-rw-r--r--lib/racc/parser-text.rb637
-rw-r--r--lib/racc/parser.rb632
-rw-r--r--lib/racc/parserfilegenerator.rb470
-rw-r--r--lib/racc/racc.gemspec58
-rw-r--r--lib/racc/sourcetext.rb35
-rw-r--r--lib/racc/state.rb972
-rw-r--r--lib/racc/statetransitiontable.rb311
-rw-r--r--lib/racc/static.rb5
-rw-r--r--lib/random/formatter.rb144
-rw-r--r--lib/rdoc.rb12
-rw-r--r--lib/rdoc/any_method.rb15
-rw-r--r--lib/rdoc/comment.rb11
-rw-r--r--lib/rdoc/context.rb2
-rw-r--r--lib/rdoc/cross_reference.rb3
-rw-r--r--lib/rdoc/encoding.rb18
-rw-r--r--lib/rdoc/erbio.rb8
-rw-r--r--lib/rdoc/generator/darkfish.rb8
-rw-r--r--lib/rdoc/generator/json_index.rb4
-rw-r--r--lib/rdoc/generator/pot.rb1
-rw-r--r--lib/rdoc/generator/template/darkfish/_sidebar_search.rhtml2
-rw-r--r--lib/rdoc/generator/template/darkfish/class.rhtml6
-rw-r--r--lib/rdoc/generator/template/darkfish/css/rdoc.css13
-rw-r--r--lib/rdoc/generator/template/darkfish/js/darkfish.js13
-rw-r--r--lib/rdoc/generator/template/darkfish/js/search.js6
-rw-r--r--lib/rdoc/generator/template/darkfish/table_of_contents.rhtml4
-rw-r--r--lib/rdoc/generator/template/json_index/js/navigation.js16
-rw-r--r--lib/rdoc/markdown.rb8
-rw-r--r--lib/rdoc/markdown/literals.rb1
-rw-r--r--lib/rdoc/markup/attribute_manager.rb3
-rw-r--r--lib/rdoc/markup/formatter.rb2
-rw-r--r--lib/rdoc/markup/parser.rb6
-rw-r--r--lib/rdoc/markup/table.rb13
-rw-r--r--lib/rdoc/markup/to_bs.rb25
-rw-r--r--lib/rdoc/markup/to_html.rb9
-rw-r--r--lib/rdoc/markup/to_html_crossref.rb26
-rw-r--r--lib/rdoc/markup/to_html_snippet.rb3
-rw-r--r--lib/rdoc/markup/to_joined_paragraph.rb4
-rw-r--r--lib/rdoc/markup/to_markdown.rb8
-rw-r--r--lib/rdoc/markup/to_rdoc.rb14
-rw-r--r--lib/rdoc/options.rb1
-rw-r--r--lib/rdoc/parser.rb6
-rw-r--r--lib/rdoc/parser/c.rb55
-rw-r--r--lib/rdoc/parser/changelog.rb15
-rw-r--r--lib/rdoc/parser/ripper_state_lex.rb12
-rw-r--r--lib/rdoc/parser/ruby.rb16
-rw-r--r--lib/rdoc/rd/block_parser.rb654
-rw-r--r--lib/rdoc/rd/inline_parser.rb654
-rw-r--r--lib/rdoc/rdoc.gemspec4
-rw-r--r--lib/rdoc/ri/driver.rb19
-rw-r--r--lib/rdoc/store.rb48
-rw-r--r--lib/rdoc/text.rb10
-rw-r--r--lib/rdoc/token_stream.rb2
-rw-r--r--lib/rdoc/top_level.rb3
-rw-r--r--lib/rdoc/version.rb2
-rw-r--r--lib/readline.gemspec2
-rw-r--r--lib/readline.rb2
-rw-r--r--lib/reline.rb257
-rw-r--r--lib/reline/ansi.rb136
-rw-r--r--lib/reline/config.rb40
-rw-r--r--lib/reline/face.rb199
-rw-r--r--lib/reline/general_io.rb22
-rw-r--r--lib/reline/history.rb2
-rw-r--r--lib/reline/key_actor/emacs.rb22
-rw-r--r--lib/reline/key_actor/vi_command.rb46
-rw-r--r--lib/reline/key_actor/vi_insert.rb4
-rw-r--r--lib/reline/key_stroke.rb57
-rw-r--r--lib/reline/kill_ring.rb4
-rw-r--r--lib/reline/line_editor.rb2740
-rw-r--r--lib/reline/reline.gemspec5
-rw-r--r--lib/reline/terminfo.rb21
-rw-r--r--lib/reline/unicode.rb88
-rw-r--r--lib/reline/unicode/east_asian_width.rb6
-rw-r--r--lib/reline/version.rb2
-rw-r--r--lib/reline/windows.rb8
-rw-r--r--lib/resolv-replace.gemspec22
-rw-r--r--lib/resolv-replace.rb76
-rw-r--r--lib/resolv.rb574
-rw-r--r--lib/rinda/rinda.gemspec35
-rw-r--r--lib/rinda/rinda.rb329
-rw-r--r--lib/rinda/ring.rb484
-rw-r--r--lib/rinda/tuplespace.rb641
-rw-r--r--lib/ruby_vm/rjit/.document1
-rw-r--r--lib/ruby_vm/rjit/assembler.rb11
-rw-r--r--lib/ruby_vm/rjit/c_pointer.rb36
-rw-r--r--lib/ruby_vm/rjit/c_type.rb8
-rw-r--r--lib/ruby_vm/rjit/code_block.rb10
-rw-r--r--lib/ruby_vm/rjit/compiler.rb21
-rw-r--r--lib/ruby_vm/rjit/context.rb6
-rw-r--r--lib/ruby_vm/rjit/insn_compiler.rb267
-rw-r--r--lib/ruby_vm/rjit/invariants.rb4
-rw-r--r--lib/ruby_vm/rjit/stats.rb3
-rw-r--r--lib/ruby_vm/rjit/type.rb16
-rw-r--r--lib/rubygems.rb55
-rw-r--r--lib/rubygems/basic_specification.rb30
-rw-r--r--lib/rubygems/bundler_version_finder.rb4
-rw-r--r--lib/rubygems/ci_detector.rb75
-rw-r--r--lib/rubygems/command.rb12
-rw-r--r--lib/rubygems/command_manager.rb6
-rw-r--r--lib/rubygems/commands/build_command.rb13
-rw-r--r--lib/rubygems/commands/cert_command.rb2
-rw-r--r--lib/rubygems/commands/check_command.rb2
-rw-r--r--lib/rubygems/commands/cleanup_command.rb8
-rw-r--r--lib/rubygems/commands/contents_command.rb4
-rw-r--r--lib/rubygems/commands/dependency_command.rb2
-rw-r--r--lib/rubygems/commands/fetch_command.rb4
-rw-r--r--lib/rubygems/commands/generate_index_command.rb113
-rw-r--r--lib/rubygems/commands/help_command.rb6
-rw-r--r--lib/rubygems/commands/info_command.rb4
-rw-r--r--lib/rubygems/commands/install_command.rb20
-rw-r--r--lib/rubygems/commands/list_command.rb4
-rw-r--r--lib/rubygems/commands/lock_command.rb2
-rw-r--r--lib/rubygems/commands/open_command.rb4
-rw-r--r--lib/rubygems/commands/owner_command.rb2
-rw-r--r--lib/rubygems/commands/pristine_command.rb22
-rw-r--r--lib/rubygems/commands/push_command.rb2
-rw-r--r--lib/rubygems/commands/query_command.rb4
-rw-r--r--lib/rubygems/commands/rdoc_command.rb13
-rw-r--r--lib/rubygems/commands/rebuild_command.rb262
-rw-r--r--lib/rubygems/commands/search_command.rb4
-rw-r--r--lib/rubygems/commands/setup_command.rb59
-rw-r--r--lib/rubygems/commands/sources_command.rb4
-rw-r--r--lib/rubygems/commands/specification_command.rb4
-rw-r--r--lib/rubygems/commands/uninstall_command.rb12
-rw-r--r--lib/rubygems/commands/unpack_command.rb10
-rw-r--r--lib/rubygems/commands/update_command.rb33
-rw-r--r--lib/rubygems/commands/which_command.rb2
-rw-r--r--lib/rubygems/config_file.rb42
-rw-r--r--lib/rubygems/core_ext/kernel_require.rb68
-rw-r--r--lib/rubygems/defaults.rb30
-rw-r--r--lib/rubygems/dependency.rb4
-rw-r--r--lib/rubygems/dependency_installer.rb56
-rw-r--r--lib/rubygems/dependency_list.rb2
-rw-r--r--lib/rubygems/deprecate.rb156
-rw-r--r--lib/rubygems/exceptions.rb6
-rw-r--r--lib/rubygems/ext/builder.rb22
-rw-r--r--lib/rubygems/ext/cargo_builder.rb11
-rw-r--r--lib/rubygems/ext/ext_conf_builder.rb3
-rw-r--r--lib/rubygems/ext/rake_builder.rb5
-rw-r--r--lib/rubygems/gemcutter_utilities.rb127
-rw-r--r--lib/rubygems/gemcutter_utilities/webauthn_listener.rb105
-rw-r--r--lib/rubygems/gemcutter_utilities/webauthn_listener/response.rb163
-rw-r--r--lib/rubygems/gemcutter_utilities/webauthn_poller.rb78
-rw-r--r--lib/rubygems/gemspec_helpers.rb19
-rw-r--r--lib/rubygems/indexer.rb428
-rw-r--r--lib/rubygems/install_update_options.rb2
-rw-r--r--lib/rubygems/installer.rb80
-rw-r--r--lib/rubygems/local_remote_options.rb12
-rw-r--r--lib/rubygems/name_tuple.rb12
-rw-r--r--lib/rubygems/optparse.rb3
-rw-r--r--lib/rubygems/optparse/lib/optparse.rb2308
-rw-r--r--lib/rubygems/optparse/lib/optparse/uri.rb7
-rw-r--r--lib/rubygems/package.rb71
-rw-r--r--lib/rubygems/package/old.rb4
-rw-r--r--lib/rubygems/package/tar_header.rb76
-rw-r--r--lib/rubygems/package/tar_reader.rb14
-rw-r--r--lib/rubygems/package/tar_reader/entry.rb38
-rw-r--r--lib/rubygems/package/tar_writer.rb30
-rw-r--r--lib/rubygems/package_task.rb4
-rw-r--r--lib/rubygems/path_support.rb19
-rw-r--r--lib/rubygems/platform.rb35
-rw-r--r--lib/rubygems/psych_tree.rb4
-rw-r--r--lib/rubygems/query_utils.rb4
-rw-r--r--lib/rubygems/remote_fetcher.rb20
-rw-r--r--lib/rubygems/request.rb28
-rw-r--r--lib/rubygems/request/connection_pools.rb2
-rw-r--r--lib/rubygems/request_set.rb4
-rw-r--r--lib/rubygems/request_set/gem_dependency_api.rb224
-rw-r--r--lib/rubygems/request_set/lockfile.rb7
-rw-r--r--lib/rubygems/requirement.rb9
-rw-r--r--lib/rubygems/resolver.rb19
-rw-r--r--lib/rubygems/resolver/api_set.rb2
-rw-r--r--lib/rubygems/resolver/api_set/gem_parser.rb10
-rw-r--r--lib/rubygems/resolver/api_specification.rb2
-rw-r--r--lib/rubygems/resolver/best_set.rb2
-rw-r--r--lib/rubygems/resolver/index_specification.rb2
-rw-r--r--lib/rubygems/resolver/installer_set.rb4
-rw-r--r--lib/rubygems/resolver/molinillo.rb3
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo.rb11
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/delegates/resolution_state.rb57
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider.rb88
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb255
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action.rb36
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb66
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_vertex.rb62
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/delete_edge.rb63
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb61
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/log.rb126
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/set_payload.rb46
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/tag.rb36
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb164
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb149
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb6
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider.rb112
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb67
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb839
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/resolver.rb46
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/state.rb58
-rw-r--r--lib/rubygems/resolver/spec_specification.rb7
-rw-r--r--lib/rubygems/s3_uri_signer.rb10
-rw-r--r--lib/rubygems/safe_marshal.rb74
-rw-r--r--lib/rubygems/safe_marshal/elements.rb146
-rw-r--r--lib/rubygems/safe_marshal/reader.rb308
-rw-r--r--lib/rubygems/safe_marshal/visitors/stream_printer.rb31
-rw-r--r--lib/rubygems/safe_marshal/visitors/to_ruby.rb415
-rw-r--r--lib/rubygems/safe_marshal/visitors/visitor.rb74
-rw-r--r--lib/rubygems/safe_yaml.rb11
-rw-r--r--lib/rubygems/security.rb2
-rw-r--r--lib/rubygems/security/policies.rb72
-rw-r--r--lib/rubygems/security/trust_dir.rb6
-rw-r--r--lib/rubygems/shellwords.rb3
-rw-r--r--lib/rubygems/source.rb16
-rw-r--r--lib/rubygems/source/git.rb6
-rw-r--r--lib/rubygems/source/local.rb3
-rw-r--r--lib/rubygems/source_list.rb2
-rw-r--r--lib/rubygems/spec_fetcher.rb6
-rw-r--r--lib/rubygems/specification.rb163
-rw-r--r--lib/rubygems/specification_policy.rb84
-rw-r--r--lib/rubygems/stub_specification.rb20
-rw-r--r--lib/rubygems/tsort.rb3
-rw-r--r--lib/rubygems/tsort/lib/tsort.rb452
-rw-r--r--lib/rubygems/uninstaller.rb2
-rw-r--r--lib/rubygems/update_suggestion.rb23
-rw-r--r--lib/rubygems/uri.rb12
-rw-r--r--lib/rubygems/user_interaction.rb9
-rw-r--r--lib/rubygems/util.rb6
-rw-r--r--lib/rubygems/util/licenses.rb219
-rw-r--r--lib/rubygems/vendor/molinillo/.document1
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo.rb11
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb57
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb88
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph.rb255
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/action.rb36
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb66
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb62
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb63
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb61
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/log.rb126
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb46
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb36
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb164
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/errors.rb149
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/gem_metadata.rb6
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/modules/specification_provider.rb112
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/modules/ui.rb67
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb839
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/resolver.rb46
-rw-r--r--lib/rubygems/vendor/molinillo/lib/molinillo/state.rb58
-rw-r--r--lib/rubygems/vendor/net-http/.document1
-rw-r--r--lib/rubygems/vendor/net-http/lib/net/http.rb2496
-rw-r--r--lib/rubygems/vendor/net-http/lib/net/http/backward.rb40
-rw-r--r--lib/rubygems/vendor/net-http/lib/net/http/exceptions.rb34
-rw-r--r--lib/rubygems/vendor/net-http/lib/net/http/generic_request.rb414
-rw-r--r--lib/rubygems/vendor/net-http/lib/net/http/header.rb981
-rw-r--r--lib/rubygems/vendor/net-http/lib/net/http/proxy_delta.rb17
-rw-r--r--lib/rubygems/vendor/net-http/lib/net/http/request.rb88
-rw-r--r--lib/rubygems/vendor/net-http/lib/net/http/requests.rb425
-rw-r--r--lib/rubygems/vendor/net-http/lib/net/http/response.rb738
-rw-r--r--lib/rubygems/vendor/net-http/lib/net/http/responses.rb1174
-rw-r--r--lib/rubygems/vendor/net-http/lib/net/http/status.rb84
-rw-r--r--lib/rubygems/vendor/net-http/lib/net/https.rb23
-rw-r--r--lib/rubygems/vendor/net-protocol/.document1
-rw-r--r--lib/rubygems/vendor/net-protocol/lib/net/protocol.rb544
-rw-r--r--lib/rubygems/vendor/optparse/.document1
-rw-r--r--lib/rubygems/vendor/optparse/lib/optionparser.rb (renamed from lib/rubygems/optparse/lib/optionparser.rb)0
-rw-r--r--lib/rubygems/vendor/optparse/lib/optparse.rb2330
-rw-r--r--lib/rubygems/vendor/optparse/lib/optparse/ac.rb (renamed from lib/rubygems/optparse/lib/optparse/ac.rb)0
-rw-r--r--lib/rubygems/vendor/optparse/lib/optparse/date.rb (renamed from lib/rubygems/optparse/lib/optparse/date.rb)0
-rw-r--r--lib/rubygems/vendor/optparse/lib/optparse/kwargs.rb (renamed from lib/rubygems/optparse/lib/optparse/kwargs.rb)0
-rw-r--r--lib/rubygems/vendor/optparse/lib/optparse/shellwords.rb (renamed from lib/rubygems/optparse/lib/optparse/shellwords.rb)0
-rw-r--r--lib/rubygems/vendor/optparse/lib/optparse/time.rb (renamed from lib/rubygems/optparse/lib/optparse/time.rb)0
-rw-r--r--lib/rubygems/vendor/optparse/lib/optparse/uri.rb7
-rw-r--r--lib/rubygems/vendor/optparse/lib/optparse/version.rb (renamed from lib/rubygems/optparse/lib/optparse/version.rb)0
-rw-r--r--lib/rubygems/vendor/resolv/.document1
-rw-r--r--lib/rubygems/vendor/resolv/lib/resolv.rb3442
-rw-r--r--lib/rubygems/vendor/timeout/.document1
-rw-r--r--lib/rubygems/vendor/timeout/lib/timeout.rb199
-rw-r--r--lib/rubygems/vendor/tsort/.document1
-rw-r--r--lib/rubygems/vendor/tsort/lib/tsort.rb455
-rw-r--r--lib/rubygems/vendor/uri/.document1
-rw-r--r--lib/rubygems/vendor/uri/lib/uri.rb104
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/common.rb853
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/file.rb100
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/ftp.rb267
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/generic.rb1588
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/http.rb125
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/https.rb23
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/ldap.rb261
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/ldaps.rb22
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/mailto.rb293
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/rfc2396_parser.rb539
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/rfc3986_parser.rb183
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/version.rb6
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/ws.rb83
-rw-r--r--lib/rubygems/vendor/uri/lib/uri/wss.rb23
-rw-r--r--lib/rubygems/vendored_molinillo.rb3
-rw-r--r--lib/rubygems/vendored_net_http.rb5
-rw-r--r--lib/rubygems/vendored_optparse.rb3
-rw-r--r--lib/rubygems/vendored_timeout.rb5
-rw-r--r--lib/rubygems/vendored_tsort.rb3
-rw-r--r--lib/rubygems/version.rb62
-rw-r--r--lib/rubygems/webauthn_listener.rb92
-rw-r--r--lib/rubygems/webauthn_listener/response.rb161
-rw-r--r--lib/rubygems/yaml_serializer.rb38
-rw-r--r--lib/securerandom.gemspec9
-rw-r--r--lib/securerandom.rb13
-rw-r--r--lib/set.rb36
-rw-r--r--lib/set/set.gemspec2
-rw-r--r--lib/shellwords.rb3
-rw-r--r--lib/singleton.rb2
-rw-r--r--lib/syntax_suggest/api.rb47
-rw-r--r--lib/syntax_suggest/around_block_scan.rb4
-rw-r--r--lib/syntax_suggest/block_expand.rb2
-rw-r--r--lib/syntax_suggest/capture_code_context.rb2
-rw-r--r--lib/syntax_suggest/clean_document.rb14
-rw-r--r--lib/syntax_suggest/code_block.rb2
-rw-r--r--lib/syntax_suggest/code_frontier.rb2
-rw-r--r--lib/syntax_suggest/code_line.rb17
-rw-r--r--lib/syntax_suggest/code_search.rb4
-rw-r--r--lib/syntax_suggest/core_ext.rb2
-rw-r--r--lib/syntax_suggest/display_invalid_blocks.rb2
-rw-r--r--lib/syntax_suggest/explain_syntax.rb22
-rw-r--r--lib/syntax_suggest/lex_all.rb39
-rw-r--r--lib/syntax_suggest/parse_blocks_from_indent_line.rb2
-rw-r--r--lib/syntax_suggest/pathname_from_message.rb2
-rw-r--r--lib/syntax_suggest/ripper_errors.rb5
-rw-r--r--lib/syntax_suggest/scan_history.rb2
-rw-r--r--lib/syntax_suggest/syntax_suggest.gemspec2
-rw-r--r--lib/syntax_suggest/version.rb2
-rw-r--r--lib/tempfile.gemspec8
-rw-r--r--lib/tempfile.rb77
-rw-r--r--lib/time.gemspec8
-rw-r--r--lib/time.rb6
-rw-r--r--lib/timeout.gemspec3
-rw-r--r--lib/timeout.rb37
-rw-r--r--lib/tmpdir.gemspec10
-rw-r--r--lib/tmpdir.rb31
-rw-r--r--lib/tsort.rb2
-rw-r--r--lib/un.rb3
-rw-r--r--lib/uri.rb18
-rw-r--r--lib/uri/ftp.rb2
-rw-r--r--lib/uri/generic.rb19
-rw-r--r--lib/uri/http.rb4
-rw-r--r--lib/uri/rfc2396_parser.rb4
-rw-r--r--lib/uri/rfc3986_parser.rb128
-rw-r--r--lib/uri/version.rb2
-rw-r--r--lib/weakref.rb2
-rw-r--r--lib/yaml.rb2
-rw-r--r--lib/yaml/store.rb10
-rwxr-xr-xlibexec/bundle11
-rwxr-xr-xlibexec/racc320
-rw-r--r--load.c201
-rw-r--r--main.c12
-rw-r--r--man/irb.17
-rw-r--r--man/ruby.183
-rw-r--r--marshal.c94
-rw-r--r--math.c64
-rw-r--r--memory_view.c6
-rw-r--r--method.h19
-rw-r--r--mini_builtin.c35
-rw-r--r--miniinit.c13
-rw-r--r--misc/call_fuzzer.rb372
-rwxr-xr-xmisc/call_fuzzer.sh13
-rw-r--r--misc/gdb.py115
-rwxr-xr-xmisc/lldb_cruby.py57
-rw-r--r--misc/lldb_rb/commands/heap_page_command.py4
-rw-r--r--misc/lldb_rb/commands/rp_command.py1
-rw-r--r--misc/lldb_rb/lldb_interface.py1
-rw-r--r--misc/lldb_rb/rb_base_command.py2
-rw-r--r--misc/lldb_rb/rb_heap_structs.py19
-rw-r--r--misc/lldb_rb/utils.py284
-rw-r--r--misc/lldb_yjit.py47
-rwxr-xr-xmisc/yjit_perf.py116
-rw-r--r--missing/dtoa.c11
-rw-r--r--missing/explicit_bzero.c5
-rw-r--r--missing/procstat_vm.c34
-rw-r--r--missing/setproctitle.c41
-rw-r--r--node.c1573
-rw-r--r--node.h537
-rw-r--r--node_dump.c1206
-rw-r--r--numeric.c346
-rw-r--r--numeric.rb295
-rw-r--r--object.c331
-rw-r--r--pack.c3
-rw-r--r--pack.rb1
-rw-r--r--parse.y10563
-rw-r--r--parser_bits.h564
-rw-r--r--parser_node.h32
-rw-r--r--parser_st.c164
-rw-r--r--parser_st.h162
-rw-r--r--parser_value.h106
-rw-r--r--prism/api_pack.c276
-rw-r--r--prism/config.yml3689
-rw-r--r--prism/defines.h206
-rw-r--r--prism/encoding.c5235
-rw-r--r--prism/encoding.h283
-rw-r--r--prism/extension.c1433
-rw-r--r--prism/extension.h19
-rw-r--r--prism/node.h150
-rw-r--r--prism/options.c248
-rw-r--r--prism/options.h305
-rw-r--r--prism/pack.c509
-rw-r--r--prism/pack.h163
-rw-r--r--prism/parser.h891
-rw-r--r--prism/prettyprint.h34
-rw-r--r--prism/prism.c21437
-rw-r--r--prism/prism.h347
-rw-r--r--prism/regexp.c653
-rw-r--r--prism/regexp.h33
-rw-r--r--prism/static_literals.c638
-rw-r--r--prism/static_literals.h120
-rw-r--r--prism/templates/ext/prism/api_node.c.erb257
-rw-r--r--prism/templates/include/prism/ast.h.erb217
-rw-r--r--prism/templates/include/prism/diagnostic.h.erb130
-rw-r--r--prism/templates/lib/prism/compiler.rb.erb41
-rw-r--r--prism/templates/lib/prism/dispatcher.rb.erb89
-rw-r--r--prism/templates/lib/prism/dot_visitor.rb.erb186
-rw-r--r--prism/templates/lib/prism/dsl.rb.erb49
-rw-r--r--prism/templates/lib/prism/inspect_visitor.rb.erb137
-rw-r--r--prism/templates/lib/prism/mutation_compiler.rb.erb19
-rw-r--r--prism/templates/lib/prism/node.rb.erb351
-rw-r--r--prism/templates/lib/prism/reflection.rb.erb137
-rw-r--r--prism/templates/lib/prism/serialize.rb.erb404
-rw-r--r--prism/templates/lib/prism/visitor.rb.erb53
-rw-r--r--prism/templates/src/diagnostic.c.erb487
-rw-r--r--prism/templates/src/node.c.erb398
-rw-r--r--prism/templates/src/prettyprint.c.erb167
-rw-r--r--prism/templates/src/serialize.c.erb402
-rw-r--r--prism/templates/src/token_type.c.erb369
-rwxr-xr-xprism/templates/template.rb662
-rw-r--r--prism/util/pm_buffer.c317
-rw-r--r--prism/util/pm_buffer.h218
-rw-r--r--prism/util/pm_char.c318
-rw-r--r--prism/util/pm_char.h205
-rw-r--r--prism/util/pm_constant_pool.c346
-rw-r--r--prism/util/pm_constant_pool.h226
-rw-r--r--prism/util/pm_integer.c642
-rw-r--r--prism/util/pm_integer.h119
-rw-r--r--prism/util/pm_list.c49
-rw-r--r--prism/util/pm_list.h97
-rw-r--r--prism/util/pm_memchr.c35
-rw-r--r--prism/util/pm_memchr.h29
-rw-r--r--prism/util/pm_newline_list.c96
-rw-r--r--prism/util/pm_newline_list.h102
-rw-r--r--prism/util/pm_string.c332
-rw-r--r--prism/util/pm_string.h174
-rw-r--r--prism/util/pm_string_list.c28
-rw-r--r--prism/util/pm_string_list.h44
-rw-r--r--prism/util/pm_strncasecmp.c24
-rw-r--r--prism/util/pm_strncasecmp.h32
-rw-r--r--prism/util/pm_strpbrk.c180
-rw-r--r--prism/util/pm_strpbrk.h46
-rw-r--r--prism/version.h29
-rw-r--r--prism_compile.c9137
-rw-r--r--prism_compile.h88
-rw-r--r--prism_init.c9
-rw-r--r--proc.c205
-rw-r--r--process.c2824
-rw-r--r--ractor.c455
-rw-r--r--ractor.rb126
-rw-r--r--ractor_core.h14
-rw-r--r--random.c10
-rw-r--r--range.c446
-rw-r--r--rational.c36
-rw-r--r--re.c601
-rw-r--r--regcomp.c124
-rw-r--r--regenc.c2
-rw-r--r--regexec.c628
-rw-r--r--regint.h28
-rw-r--r--regparse.c187
-rw-r--r--regparse.h3
-rw-r--r--rjit.c88
-rw-r--r--rjit.h14
-rw-r--r--rjit.rb6
-rw-r--r--rjit_c.c3
-rw-r--r--rjit_c.h2
-rw-r--r--rjit_c.rb154
-rw-r--r--ruby-runner.c16
-rw-r--r--ruby.c1497
-rw-r--r--ruby_parser.c1158
-rw-r--r--rubyparser.h1430
-rw-r--r--sample/getoptlong/abbrev.rb9
-rw-r--r--sample/getoptlong/aliases.rb8
-rw-r--r--sample/getoptlong/argv.rb12
-rw-r--r--sample/getoptlong/each.rb12
-rw-r--r--sample/getoptlong/fibonacci.rb62
-rw-r--r--sample/getoptlong/permute.rb12
-rw-r--r--sample/getoptlong/require_order.rb13
-rw-r--r--sample/getoptlong/return_in_order.rb13
-rw-r--r--sample/getoptlong/simple.rb7
-rw-r--r--sample/getoptlong/types.rb10
-rw-r--r--sample/net-imap.rb167
-rw-r--r--sample/win32ole/excel1.rb (renamed from ext/win32ole/sample/excel1.rb)0
-rw-r--r--sample/win32ole/excel2.rb (renamed from ext/win32ole/sample/excel2.rb)0
-rw-r--r--sample/win32ole/excel3.rb (renamed from ext/win32ole/sample/excel3.rb)0
-rw-r--r--sample/win32ole/ie.rb (renamed from ext/win32ole/sample/ie.rb)0
-rw-r--r--sample/win32ole/ieconst.rb (renamed from ext/win32ole/sample/ieconst.rb)0
-rw-r--r--sample/win32ole/ienavi.rb (renamed from ext/win32ole/sample/ienavi.rb)0
-rw-r--r--sample/win32ole/ienavi2.rb (renamed from ext/win32ole/sample/ienavi2.rb)0
-rw-r--r--sample/win32ole/oledirs.rb (renamed from ext/win32ole/sample/oledirs.rb)0
-rw-r--r--sample/win32ole/olegen.rb348
-rw-r--r--sample/win32ole/xml.rb (renamed from ext/win32ole/sample/xml.rb)0
-rw-r--r--scheduler.c22
-rw-r--r--shape.c796
-rw-r--r--shape.h73
-rw-r--r--signal.c79
-rw-r--r--spec/README.md8
-rw-r--r--spec/bundled_gems.mspec6
-rw-r--r--spec/bundler/bundler/bundler_spec.rb30
-rw-r--r--spec/bundler/bundler/ci_detector_spec.rb21
-rw-r--r--spec/bundler/bundler/cli_spec.rb32
-rw-r--r--spec/bundler/bundler/compact_index_client/updater_spec.rb205
-rw-r--r--spec/bundler/bundler/definition_spec.rb79
-rw-r--r--spec/bundler/bundler/dependency_spec.rb221
-rw-r--r--spec/bundler/bundler/digest_spec.rb2
-rw-r--r--spec/bundler/bundler/dsl_spec.rb108
-rw-r--r--spec/bundler/bundler/endpoint_specification_spec.rb6
-rw-r--r--spec/bundler/bundler/env_spec.rb18
-rw-r--r--spec/bundler/bundler/environment_preserver_spec.rb16
-rw-r--r--spec/bundler/bundler/fetcher/base_spec.rb11
-rw-r--r--spec/bundler/bundler/fetcher/compact_index_spec.rb9
-rw-r--r--spec/bundler/bundler/fetcher/dependency_spec.rb25
-rw-r--r--spec/bundler/bundler/fetcher/downloader_spec.rb70
-rw-r--r--spec/bundler/bundler/fetcher/index_spec.rb30
-rw-r--r--spec/bundler/bundler/fetcher_spec.rb98
-rw-r--r--spec/bundler/bundler/friendly_errors_spec.rb6
-rw-r--r--spec/bundler/bundler/gem_helper_spec.rb40
-rw-r--r--spec/bundler/bundler/gem_version_promoter_spec.rb56
-rw-r--r--spec/bundler/bundler/installer/gem_installer_spec.rb12
-rw-r--r--spec/bundler/bundler/installer/parallel_installer_spec.rb46
-rw-r--r--spec/bundler/bundler/installer/spec_installation_spec.rb18
-rw-r--r--spec/bundler/bundler/lockfile_parser_spec.rb78
-rw-r--r--spec/bundler/bundler/mirror_spec.rb16
-rw-r--r--spec/bundler/bundler/plugin/api/source_spec.rb4
-rw-r--r--spec/bundler/bundler/plugin/dsl_spec.rb6
-rw-r--r--spec/bundler/bundler/plugin/installer_spec.rb21
-rw-r--r--spec/bundler/bundler/plugin_spec.rb10
-rw-r--r--spec/bundler/bundler/remote_specification_spec.rb12
-rw-r--r--spec/bundler/bundler/resolver/candidate_spec.rb6
-rw-r--r--spec/bundler/bundler/ruby_dsl_spec.rb115
-rw-r--r--spec/bundler/bundler/rubygems_integration_spec.rb29
-rw-r--r--spec/bundler/bundler/settings/validator_spec.rb6
-rw-r--r--spec/bundler/bundler/settings_spec.rb25
-rw-r--r--spec/bundler/bundler/shared_helpers_spec.rb77
-rw-r--r--spec/bundler/bundler/source/git/git_proxy_spec.rb108
-rw-r--r--spec/bundler/bundler/source/git_spec.rb4
-rw-r--r--spec/bundler/bundler/source/rubygems/remote_spec.rb20
-rw-r--r--spec/bundler/bundler/source_list_spec.rb6
-rw-r--r--spec/bundler/bundler/source_spec.rb20
-rw-r--r--spec/bundler/bundler/spec_set_spec.rb18
-rw-r--r--spec/bundler/bundler/specifications/foo.gemspec13
-rw-r--r--spec/bundler/bundler/stub_specification_spec.rb11
-rw-r--r--spec/bundler/bundler/uri_credentials_filter_spec.rb10
-rw-r--r--spec/bundler/bundler/yaml_serializer_spec.rb57
-rw-r--r--spec/bundler/cache/gems_spec.rb77
-rw-r--r--spec/bundler/cache/git_spec.rb12
-rw-r--r--spec/bundler/cache/path_spec.rb10
-rw-r--r--spec/bundler/cache/platform_spec.rb2
-rw-r--r--spec/bundler/commands/add_spec.rb95
-rw-r--r--spec/bundler/commands/binstubs_spec.rb50
-rw-r--r--spec/bundler/commands/cache_spec.rb14
-rw-r--r--spec/bundler/commands/check_spec.rb62
-rw-r--r--spec/bundler/commands/clean_spec.rb46
-rw-r--r--spec/bundler/commands/config_spec.rb34
-rw-r--r--spec/bundler/commands/console_spec.rb2
-rw-r--r--spec/bundler/commands/doctor_spec.rb7
-rw-r--r--spec/bundler/commands/exec_spec.rb98
-rw-r--r--spec/bundler/commands/help_spec.rb9
-rw-r--r--spec/bundler/commands/info_spec.rb22
-rw-r--r--spec/bundler/commands/init_spec.rb26
-rw-r--r--spec/bundler/commands/inject_spec.rb10
-rw-r--r--spec/bundler/commands/install_spec.rb416
-rw-r--r--spec/bundler/commands/list_spec.rb12
-rw-r--r--spec/bundler/commands/lock_spec.rb593
-rw-r--r--spec/bundler/commands/newgem_spec.rb134
-rw-r--r--spec/bundler/commands/open_spec.rb54
-rw-r--r--spec/bundler/commands/outdated_spec.rb176
-rw-r--r--spec/bundler/commands/platform_spec.rb132
-rw-r--r--spec/bundler/commands/post_bundle_message_spec.rb16
-rw-r--r--spec/bundler/commands/pristine_spec.rb20
-rw-r--r--spec/bundler/commands/remove_spec.rb22
-rw-r--r--spec/bundler/commands/show_spec.rb20
-rw-r--r--spec/bundler/commands/update_spec.rb497
-rw-r--r--spec/bundler/commands/version_spec.rb16
-rw-r--r--spec/bundler/commands/viz_spec.rb16
-rw-r--r--spec/bundler/install/allow_offline_install_spec.rb18
-rw-r--r--spec/bundler/install/bundler_spec.rb10
-rw-r--r--spec/bundler/install/deploy_spec.rb194
-rw-r--r--spec/bundler/install/failure_spec.rb4
-rw-r--r--spec/bundler/install/gemfile/eval_gemfile_spec.rb8
-rw-r--r--spec/bundler/install/gemfile/force_ruby_platform_spec.rb32
-rw-r--r--spec/bundler/install/gemfile/gemspec_spec.rb167
-rw-r--r--spec/bundler/install/gemfile/git_spec.rb192
-rw-r--r--spec/bundler/install/gemfile/groups_spec.rb42
-rw-r--r--spec/bundler/install/gemfile/install_if_spec.rb9
-rw-r--r--spec/bundler/install/gemfile/lockfile_spec.rb5
-rw-r--r--spec/bundler/install/gemfile/path_spec.rb242
-rw-r--r--spec/bundler/install/gemfile/platform_spec.rb39
-rw-r--r--spec/bundler/install/gemfile/ruby_spec.rb36
-rw-r--r--spec/bundler/install/gemfile/sources_spec.rb526
-rw-r--r--spec/bundler/install/gemfile/specific_platform_spec.rb563
-rw-r--r--spec/bundler/install/gemfile_spec.rb10
-rw-r--r--spec/bundler/install/gems/compact_index_spec.rb379
-rw-r--r--spec/bundler/install/gems/dependency_api_spec.rb136
-rw-r--r--spec/bundler/install/gems/flex_spec.rb25
-rw-r--r--spec/bundler/install/gems/native_extensions_spec.rb8
-rw-r--r--spec/bundler/install/gems/resolving_spec.rb130
-rw-r--r--spec/bundler/install/gems/standalone_spec.rb77
-rw-r--r--spec/bundler/install/gemspecs_spec.rb30
-rw-r--r--spec/bundler/install/git_spec.rb139
-rw-r--r--spec/bundler/install/global_cache_spec.rb40
-rw-r--r--spec/bundler/install/path_spec.rb36
-rw-r--r--spec/bundler/install/redownload_spec.rb2
-rw-r--r--spec/bundler/install/security_policy_spec.rb12
-rw-r--r--spec/bundler/install/yanked_spec.rb20
-rw-r--r--spec/bundler/lock/git_spec.rb6
-rw-r--r--spec/bundler/lock/lockfile_spec.rb404
-rw-r--r--spec/bundler/other/cli_dispatch_spec.rb6
-rw-r--r--spec/bundler/other/ext_spec.rb24
-rw-r--r--spec/bundler/other/major_deprecation_spec.rb193
-rw-r--r--spec/bundler/plugins/command_spec.rb2
-rw-r--r--spec/bundler/plugins/hook_spec.rb99
-rw-r--r--spec/bundler/plugins/install_spec.rb91
-rw-r--r--spec/bundler/plugins/source/example_spec.rb14
-rw-r--r--spec/bundler/plugins/uninstall_spec.rb25
-rw-r--r--spec/bundler/quality_spec.rb17
-rw-r--r--spec/bundler/realworld/dependency_api_spec.rb16
-rw-r--r--spec/bundler/realworld/double_check_spec.rb6
-rw-r--r--spec/bundler/realworld/edgecases_spec.rb155
-rw-r--r--spec/bundler/realworld/ffi_spec.rb2
-rw-r--r--spec/bundler/realworld/fixtures/warbler/Gemfile2
-rw-r--r--spec/bundler/realworld/gemfile_source_header_spec.rb18
-rw-r--r--spec/bundler/realworld/git_spec.rb2
-rw-r--r--spec/bundler/realworld/mirror_probe_spec.rb22
-rw-r--r--spec/bundler/realworld/parallel_spec.rb8
-rw-r--r--spec/bundler/realworld/slow_perf_spec.rb117
-rw-r--r--spec/bundler/resolver/basic_spec.rb34
-rw-r--r--spec/bundler/resolver/platform_spec.rb2
-rw-r--r--spec/bundler/runtime/executable_spec.rb22
-rw-r--r--spec/bundler/runtime/gem_tasks_spec.rb10
-rw-r--r--spec/bundler/runtime/inline_spec.rb99
-rw-r--r--spec/bundler/runtime/load_spec.rb2
-rw-r--r--spec/bundler/runtime/platform_spec.rb31
-rw-r--r--spec/bundler/runtime/require_spec.rb12
-rw-r--r--spec/bundler/runtime/requiring_spec.rb15
-rw-r--r--spec/bundler/runtime/self_management_spec.rb49
-rw-r--r--spec/bundler/runtime/setup_spec.rb214
-rw-r--r--spec/bundler/runtime/with_unbundled_env_spec.rb22
-rw-r--r--spec/bundler/spec_helper.rb10
-rw-r--r--spec/bundler/support/activate.rb9
-rw-r--r--spec/bundler/support/artifice/compact_index_checksum_mismatch.rb4
-rw-r--r--spec/bundler/support/artifice/compact_index_concurrent_download.rb7
-rw-r--r--spec/bundler/support/artifice/compact_index_etag_match.rb16
-rw-r--r--spec/bundler/support/artifice/compact_index_host_redirect.rb2
-rw-r--r--spec/bundler/support/artifice/compact_index_partial_update.rb2
-rw-r--r--spec/bundler/support/artifice/compact_index_partial_update_bad_digest.rb40
-rw-r--r--spec/bundler/support/artifice/compact_index_partial_update_no_digest_not_incremental.rb42
-rw-r--r--spec/bundler/support/artifice/compact_index_partial_update_no_etag_not_incremental.rb40
-rw-r--r--spec/bundler/support/artifice/compact_index_range_ignored.rb40
-rw-r--r--spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb2
-rw-r--r--spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb3
-rw-r--r--spec/bundler/support/artifice/endpoint_500.rb2
-rw-r--r--spec/bundler/support/artifice/endpoint_host_redirect.rb2
-rw-r--r--spec/bundler/support/artifice/endpoint_mirror_source.rb2
-rw-r--r--spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb2
-rw-r--r--spec/bundler/support/artifice/fail.rb14
-rw-r--r--spec/bundler/support/artifice/helpers/artifice.rb6
-rw-r--r--spec/bundler/support/artifice/helpers/compact_index.rb27
-rw-r--r--spec/bundler/support/artifice/helpers/endpoint.rb10
-rw-r--r--spec/bundler/support/artifice/helpers/rack_request.rb24
-rw-r--r--spec/bundler/support/artifice/vcr.rb14
-rw-r--r--spec/bundler/support/artifice/windows.rb2
-rw-r--r--spec/bundler/support/build_metadata.rb14
-rw-r--r--spec/bundler/support/builders.rb102
-rw-r--r--spec/bundler/support/bundle.rb7
-rw-r--r--spec/bundler/support/checksums.rb114
-rw-r--r--spec/bundler/support/command_execution.rb15
-rw-r--r--spec/bundler/support/filters.rb22
-rw-r--r--spec/bundler/support/helpers.rb114
-rw-r--r--spec/bundler/support/indexes.rb16
-rw-r--r--spec/bundler/support/matchers.rb12
-rw-r--r--spec/bundler/support/path.rb29
-rw-r--r--spec/bundler/support/platforms.rb11
-rw-r--r--spec/bundler/support/rubygems_ext.rb11
-rw-r--r--spec/bundler/support/rubygems_version_manager.rb9
-rw-r--r--spec/bundler/update/gemfile_spec.rb10
-rw-r--r--spec/bundler/update/gems/fund_spec.rb2
-rw-r--r--spec/bundler/update/gems/post_install_spec.rb4
-rw-r--r--spec/bundler/update/git_spec.rb71
-rw-r--r--spec/bundler/update/path_spec.rb4
-rw-r--r--spec/bundler/update/redownload_spec.rb14
-rw-r--r--spec/default.mspec85
-rw-r--r--spec/mspec/lib/mspec/helpers/tmp.rb3
-rw-r--r--spec/mspec/lib/mspec/mocks/mock.rb25
-rw-r--r--spec/mspec/lib/mspec/runner/actions/leakchecker.rb58
-rw-r--r--spec/mspec/lib/mspec/runner/actions/timeout.rb30
-rw-r--r--spec/mspec/lib/mspec/runner/formatters/base.rb8
-rw-r--r--spec/mspec/lib/mspec/utils/options.rb2
-rw-r--r--spec/mspec/spec/integration/run_spec.rb9
-rw-r--r--spec/mspec/spec/integration/tag_spec.rb9
-rw-r--r--spec/mspec/spec/mocks/mock_spec.rb21
-rw-r--r--spec/mspec/spec/runner/context_spec.rb2
-rw-r--r--spec/mspec/spec/spec_helper.rb2
-rw-r--r--spec/mspec/tool/remove_old_guards.rb51
-rwxr-xr-xspec/mspec/tool/tag_from_output.rb10
-rw-r--r--spec/prism.mspec26
-rw-r--r--spec/ruby/.rubocop.yml12
-rw-r--r--spec/ruby/.rubocop_todo.yml1
-rw-r--r--spec/ruby/CONTRIBUTING.md15
-rw-r--r--spec/ruby/README.md11
-rw-r--r--spec/ruby/command_line/backtrace_limit_spec.rb49
-rw-r--r--spec/ruby/command_line/dash_a_spec.rb4
-rw-r--r--spec/ruby/command_line/dash_l_spec.rb8
-rw-r--r--spec/ruby/command_line/dash_n_spec.rb8
-rw-r--r--spec/ruby/command_line/dash_p_spec.rb4
-rw-r--r--spec/ruby/command_line/dash_r_spec.rb5
-rw-r--r--spec/ruby/command_line/dash_upper_f_spec.rb2
-rw-r--r--spec/ruby/command_line/dash_upper_u_spec.rb7
-rw-r--r--spec/ruby/command_line/dash_v_spec.rb5
-rw-r--r--spec/ruby/command_line/fixtures/string_literal_frozen_comment.rb4
-rw-r--r--spec/ruby/command_line/fixtures/string_literal_mutable_comment.rb4
-rw-r--r--spec/ruby/command_line/fixtures/string_literal_raw.rb3
-rw-r--r--spec/ruby/command_line/frozen_strings_spec.rb44
-rw-r--r--spec/ruby/command_line/rubyopt_spec.rb18
-rw-r--r--spec/ruby/command_line/syntax_error_spec.rb10
-rw-r--r--spec/ruby/core/argf/bytes_spec.rb16
-rw-r--r--spec/ruby/core/argf/chars_spec.rb16
-rw-r--r--spec/ruby/core/argf/codepoints_spec.rb16
-rw-r--r--spec/ruby/core/argf/lines_spec.rb16
-rw-r--r--spec/ruby/core/argf/readpartial_spec.rb4
-rw-r--r--spec/ruby/core/argf/shared/getc.rb2
-rw-r--r--spec/ruby/core/argf/shared/read.rb4
-rw-r--r--spec/ruby/core/array/assoc_spec.rb12
-rw-r--r--spec/ruby/core/array/bsearch_index_spec.rb4
-rw-r--r--spec/ruby/core/array/drop_spec.rb12
-rw-r--r--spec/ruby/core/array/drop_while_spec.rb12
-rw-r--r--spec/ruby/core/array/fill_spec.rb2
-rw-r--r--spec/ruby/core/array/fixtures/encoded_strings.rb18
-rw-r--r--spec/ruby/core/array/flatten_spec.rb24
-rw-r--r--spec/ruby/core/array/multiply_spec.rb18
-rw-r--r--spec/ruby/core/array/pack/buffer_spec.rb12
-rw-r--r--spec/ruby/core/array/pack/shared/string.rb2
-rw-r--r--spec/ruby/core/array/rassoc_spec.rb14
-rw-r--r--spec/ruby/core/array/shared/inspect.rb6
-rw-r--r--spec/ruby/core/array/shared/slice.rb400
-rw-r--r--spec/ruby/core/array/slice_spec.rb60
-rw-r--r--spec/ruby/core/array/take_spec.rb12
-rw-r--r--spec/ruby/core/array/take_while_spec.rb12
-rw-r--r--spec/ruby/core/array/uniq_spec.rb12
-rw-r--r--spec/ruby/core/basicobject/instance_eval_spec.rb7
-rw-r--r--spec/ruby/core/basicobject/singleton_method_added_spec.rb8
-rw-r--r--spec/ruby/core/binding/clone_spec.rb6
-rw-r--r--spec/ruby/core/binding/dup_spec.rb6
-rw-r--r--spec/ruby/core/binding/eval_spec.rb99
-rw-r--r--spec/ruby/core/binding/fixtures/irbrc1
-rw-r--r--spec/ruby/core/binding/irb_spec.rb15
-rw-r--r--spec/ruby/core/binding/shared/clone.rb22
-rw-r--r--spec/ruby/core/class/attached_object_spec.rb8
-rw-r--r--spec/ruby/core/class/dup_spec.rb3
-rw-r--r--spec/ruby/core/class/subclasses_spec.rb29
-rw-r--r--spec/ruby/core/complex/inspect_spec.rb6
-rw-r--r--spec/ruby/core/complex/to_r_spec.rb12
-rw-r--r--spec/ruby/core/complex/to_s_spec.rb3
-rw-r--r--spec/ruby/core/conditionvariable/broadcast_spec.rb1
-rw-r--r--spec/ruby/core/conditionvariable/marshal_dump_spec.rb1
-rw-r--r--spec/ruby/core/conditionvariable/signal_spec.rb1
-rw-r--r--spec/ruby/core/conditionvariable/wait_spec.rb1
-rw-r--r--spec/ruby/core/data/constants_spec.rb16
-rw-r--r--spec/ruby/core/data/fixtures/classes.rb2
-rw-r--r--spec/ruby/core/data/initialize_spec.rb7
-rw-r--r--spec/ruby/core/data/with_spec.rb35
-rw-r--r--spec/ruby/core/dir/children_spec.rb17
-rw-r--r--spec/ruby/core/dir/each_child_spec.rb13
-rw-r--r--spec/ruby/core/dir/each_spec.rb11
-rw-r--r--spec/ruby/core/dir/entries_spec.rb2
-rw-r--r--spec/ruby/core/dir/exist_spec.rb8
-rw-r--r--spec/ruby/core/dir/glob_spec.rb12
-rw-r--r--spec/ruby/core/dir/home_spec.rb27
-rw-r--r--spec/ruby/core/dir/shared/chroot.rb4
-rw-r--r--spec/ruby/core/dir/shared/exist.rb8
-rw-r--r--spec/ruby/core/dir/shared/glob.rb28
-rw-r--r--spec/ruby/core/encoding/compatible_spec.rb74
-rw-r--r--spec/ruby/core/encoding/converter/convert_spec.rb15
-rw-r--r--spec/ruby/core/encoding/converter/finish_spec.rb4
-rw-r--r--spec/ruby/core/encoding/converter/last_error_spec.rb16
-rw-r--r--spec/ruby/core/encoding/converter/new_spec.rb2
-rw-r--r--spec/ruby/core/encoding/converter/primitive_convert_spec.rb5
-rw-r--r--spec/ruby/core/encoding/converter/primitive_errinfo_spec.rb1
-rw-r--r--spec/ruby/core/encoding/converter/putback_spec.rb14
-rw-r--r--spec/ruby/core/encoding/converter/replacement_spec.rb20
-rw-r--r--spec/ruby/core/encoding/default_external_spec.rb8
-rw-r--r--spec/ruby/core/encoding/inspect_spec.rb20
-rw-r--r--spec/ruby/core/encoding/invalid_byte_sequence_error/incomplete_input_spec.rb4
-rw-r--r--spec/ruby/core/encoding/invalid_byte_sequence_error/readagain_bytes_spec.rb4
-rw-r--r--spec/ruby/core/encoding/replicate_spec.rb23
-rw-r--r--spec/ruby/core/enumerable/fixtures/classes.rb6
-rw-r--r--spec/ruby/core/enumerable/grep_spec.rb49
-rw-r--r--spec/ruby/core/enumerable/grep_v_spec.rb49
-rw-r--r--spec/ruby/core/enumerable/to_set_spec.rb29
-rw-r--r--spec/ruby/core/enumerator/chain/initialize_spec.rb4
-rw-r--r--spec/ruby/core/enumerator/each_spec.rb18
-rw-r--r--spec/ruby/core/enumerator/generator/initialize_spec.rb4
-rw-r--r--spec/ruby/core/enumerator/initialize_spec.rb12
-rw-r--r--spec/ruby/core/enumerator/lazy/initialize_spec.rb4
-rw-r--r--spec/ruby/core/enumerator/new_spec.rb47
-rw-r--r--spec/ruby/core/enumerator/product/each_spec.rb73
-rw-r--r--spec/ruby/core/enumerator/product/initialize_copy_spec.rb54
-rw-r--r--spec/ruby/core/enumerator/product/initialize_spec.rb33
-rw-r--r--spec/ruby/core/enumerator/product/inspect_spec.rb22
-rw-r--r--spec/ruby/core/enumerator/product/rewind_spec.rb64
-rw-r--r--spec/ruby/core/enumerator/product/size_spec.rb56
-rw-r--r--spec/ruby/core/enumerator/product_spec.rb7
-rw-r--r--spec/ruby/core/enumerator/rewind_spec.rb4
-rw-r--r--spec/ruby/core/env/delete_spec.rb16
-rw-r--r--spec/ruby/core/env/except_spec.rb44
-rw-r--r--spec/ruby/core/env/index_spec.rb14
-rw-r--r--spec/ruby/core/env/indexes_spec.rb1
-rw-r--r--spec/ruby/core/env/indices_spec.rb1
-rw-r--r--spec/ruby/core/env/key_spec.rb32
-rw-r--r--spec/ruby/core/env/shared/include.rb7
-rw-r--r--spec/ruby/core/env/shared/key.rb31
-rw-r--r--spec/ruby/core/env/shared/value.rb7
-rw-r--r--spec/ruby/core/env/slice_spec.rb10
-rw-r--r--spec/ruby/core/env/to_a_spec.rb5
-rw-r--r--spec/ruby/core/exception/backtrace_spec.rb27
-rw-r--r--spec/ruby/core/exception/case_compare_spec.rb2
-rw-r--r--spec/ruby/core/exception/detailed_message_spec.rb12
-rw-r--r--spec/ruby/core/exception/equal_value_spec.rb14
-rw-r--r--spec/ruby/core/exception/fixtures/syntax_error.rb3
-rw-r--r--spec/ruby/core/exception/frozen_error_spec.rb16
-rw-r--r--spec/ruby/core/exception/full_message_spec.rb89
-rw-r--r--spec/ruby/core/exception/interrupt_spec.rb2
-rw-r--r--spec/ruby/core/exception/no_method_error_spec.rb28
-rw-r--r--spec/ruby/core/exception/set_backtrace_spec.rb28
-rw-r--r--spec/ruby/core/exception/syntax_error_spec.rb27
-rw-r--r--spec/ruby/core/exception/to_s_spec.rb2
-rw-r--r--spec/ruby/core/exception/top_level_spec.rb30
-rw-r--r--spec/ruby/core/false/singleton_method_spec.rb15
-rw-r--r--spec/ruby/core/fiber/blocking_spec.rb68
-rw-r--r--spec/ruby/core/fiber/inspect_spec.rb8
-rw-r--r--spec/ruby/core/fiber/raise_spec.rb56
-rw-r--r--spec/ruby/core/fiber/resume_spec.rb15
-rw-r--r--spec/ruby/core/fiber/storage_spec.rb88
-rw-r--r--spec/ruby/core/file/absolute_path_spec.rb2
-rw-r--r--spec/ruby/core/file/atime_spec.rb3
-rw-r--r--spec/ruby/core/file/ctime_spec.rb3
-rw-r--r--spec/ruby/core/file/exist_spec.rb8
-rw-r--r--spec/ruby/core/file/expand_path_spec.rb2
-rw-r--r--spec/ruby/core/file/flock_spec.rb4
-rw-r--r--spec/ruby/core/file/lutime_spec.rb9
-rw-r--r--spec/ruby/core/file/mtime_spec.rb3
-rw-r--r--spec/ruby/core/file/new_spec.rb24
-rw-r--r--spec/ruby/core/file/open_spec.rb22
-rw-r--r--spec/ruby/core/file/realpath_spec.rb4
-rw-r--r--spec/ruby/core/file/shared/fnmatch.rb47
-rw-r--r--spec/ruby/core/file/shared/path.rb4
-rw-r--r--spec/ruby/core/file/shared/update_time.rb105
-rw-r--r--spec/ruby/core/file/utime_spec.rb100
-rw-r--r--spec/ruby/core/filetest/exist_spec.rb8
-rw-r--r--spec/ruby/core/gc/auto_compact_spec.rb36
-rw-r--r--spec/ruby/core/hash/assoc_spec.rb6
-rw-r--r--spec/ruby/core/hash/compare_by_identity_spec.rb19
-rw-r--r--spec/ruby/core/hash/delete_spec.rb18
-rw-r--r--spec/ruby/core/hash/element_reference_spec.rb2
-rw-r--r--spec/ruby/core/hash/except_spec.rb50
-rw-r--r--spec/ruby/core/hash/hash_spec.rb2
-rw-r--r--spec/ruby/core/hash/index_spec.rb9
-rw-r--r--spec/ruby/core/hash/rehash_spec.rb30
-rw-r--r--spec/ruby/core/hash/shared/each.rb37
-rw-r--r--spec/ruby/core/hash/shared/equal.rb90
-rw-r--r--spec/ruby/core/hash/shared/store.rb8
-rw-r--r--spec/ruby/core/hash/shared/to_s.rb4
-rw-r--r--spec/ruby/core/hash/to_a_spec.rb10
-rw-r--r--spec/ruby/core/hash/to_proc_spec.rb16
-rw-r--r--spec/ruby/core/hash/transform_keys_spec.rb32
-rw-r--r--spec/ruby/core/integer/coerce_spec.rb13
-rw-r--r--spec/ruby/core/integer/div_spec.rb8
-rw-r--r--spec/ruby/core/integer/shared/arithmetic_coerce.rb20
-rw-r--r--spec/ruby/core/integer/zero_spec.rb12
-rw-r--r--spec/ruby/core/io/binread_spec.rb10
-rw-r--r--spec/ruby/core/io/bytes_spec.rb47
-rw-r--r--spec/ruby/core/io/chars_spec.rb30
-rw-r--r--spec/ruby/core/io/close_read_spec.rb3
-rw-r--r--spec/ruby/core/io/close_write_spec.rb14
-rw-r--r--spec/ruby/core/io/codepoints_spec.rb38
-rw-r--r--spec/ruby/core/io/copy_stream_spec.rb33
-rw-r--r--spec/ruby/core/io/eof_spec.rb2
-rw-r--r--spec/ruby/core/io/foreach_spec.rb19
-rw-r--r--spec/ruby/core/io/getbyte_spec.rb16
-rw-r--r--spec/ruby/core/io/gets_spec.rb18
-rw-r--r--spec/ruby/core/io/initialize_spec.rb16
-rw-r--r--spec/ruby/core/io/ioctl_spec.rb2
-rw-r--r--spec/ruby/core/io/lineno_spec.rb6
-rw-r--r--spec/ruby/core/io/lines_spec.rb46
-rw-r--r--spec/ruby/core/io/new_spec.rb8
-rw-r--r--spec/ruby/core/io/nonblock_spec.rb46
-rw-r--r--spec/ruby/core/io/open_spec.rb13
-rw-r--r--spec/ruby/core/io/pread_spec.rb79
-rw-r--r--spec/ruby/core/io/puts_spec.rb2
-rw-r--r--spec/ruby/core/io/pwrite_spec.rb30
-rw-r--r--spec/ruby/core/io/read_nonblock_spec.rb8
-rw-r--r--spec/ruby/core/io/read_spec.rb120
-rw-r--r--spec/ruby/core/io/readline_spec.rb12
-rw-r--r--spec/ruby/core/io/readlines_spec.rb33
-rw-r--r--spec/ruby/core/io/readpartial_spec.rb6
-rw-r--r--spec/ruby/core/io/select_spec.rb48
-rw-r--r--spec/ruby/core/io/shared/binwrite.rb12
-rw-r--r--spec/ruby/core/io/shared/each.rb20
-rw-r--r--spec/ruby/core/io/shared/new.rb55
-rw-r--r--spec/ruby/core/io/shared/readlines.rb22
-rw-r--r--spec/ruby/core/io/stat_spec.rb3
-rw-r--r--spec/ruby/core/io/sysread_spec.rb14
-rw-r--r--spec/ruby/core/io/ungetc_spec.rb16
-rw-r--r--spec/ruby/core/io/write_spec.rb67
-rw-r--r--spec/ruby/core/kernel/Float_spec.rb2
-rw-r--r--spec/ruby/core/kernel/Integer_spec.rb27
-rw-r--r--spec/ruby/core/kernel/String_spec.rb2
-rw-r--r--spec/ruby/core/kernel/__dir___spec.rb16
-rw-r--r--spec/ruby/core/kernel/caller_locations_spec.rb30
-rw-r--r--spec/ruby/core/kernel/caller_spec.rb10
-rw-r--r--spec/ruby/core/kernel/catch_spec.rb2
-rw-r--r--spec/ruby/core/kernel/class_spec.rb2
-rw-r--r--spec/ruby/core/kernel/clone_spec.rb86
-rw-r--r--spec/ruby/core/kernel/eval_spec.rb86
-rw-r--r--spec/ruby/core/kernel/exec_spec.rb4
-rw-r--r--spec/ruby/core/kernel/initialize_clone_spec.rb10
-rw-r--r--spec/ruby/core/kernel/initialize_copy_spec.rb9
-rw-r--r--spec/ruby/core/kernel/iterator_spec.rb14
-rw-r--r--spec/ruby/core/kernel/lambda_spec.rb74
-rw-r--r--spec/ruby/core/kernel/not_match_spec.rb14
-rw-r--r--spec/ruby/core/kernel/open_spec.rb78
-rw-r--r--spec/ruby/core/kernel/printf_spec.rb7
-rw-r--r--spec/ruby/core/kernel/proc_spec.rb18
-rw-r--r--spec/ruby/core/kernel/public_send_spec.rb4
-rw-r--r--spec/ruby/core/kernel/require_relative_spec.rb8
-rw-r--r--spec/ruby/core/kernel/require_spec.rb2
-rw-r--r--spec/ruby/core/kernel/shared/load.rb38
-rw-r--r--spec/ruby/core/kernel/shared/require.rb68
-rw-r--r--spec/ruby/core/kernel/shared/sprintf.rb4
-rw-r--r--spec/ruby/core/kernel/shared/sprintf_encoding.rb8
-rw-r--r--spec/ruby/core/kernel/singleton_class_spec.rb29
-rw-r--r--spec/ruby/core/kernel/sleep_spec.rb1
-rw-r--r--spec/ruby/core/kernel/sprintf_spec.rb16
-rw-r--r--spec/ruby/core/kernel/system_spec.rb17
-rw-r--r--spec/ruby/core/kernel/taint_spec.rb8
-rw-r--r--spec/ruby/core/kernel/tainted_spec.rb12
-rw-r--r--spec/ruby/core/kernel/test_spec.rb4
-rw-r--r--spec/ruby/core/kernel/trust_spec.rb10
-rw-r--r--spec/ruby/core/kernel/untaint_spec.rb10
-rw-r--r--spec/ruby/core/kernel/untrust_spec.rb8
-rw-r--r--spec/ruby/core/kernel/untrusted_spec.rb10
-rw-r--r--spec/ruby/core/kernel/warn_spec.rb141
-rw-r--r--spec/ruby/core/main/private_spec.rb12
-rw-r--r--spec/ruby/core/main/public_spec.rb12
-rw-r--r--spec/ruby/core/marshal/dump_spec.rb68
-rw-r--r--spec/ruby/core/marshal/fixtures/marshal_data.rb8
-rw-r--r--spec/ruby/core/marshal/shared/load.rb96
-rw-r--r--spec/ruby/core/matchdata/begin_spec.rb28
-rw-r--r--spec/ruby/core/matchdata/byteoffset_spec.rb4
-rw-r--r--spec/ruby/core/matchdata/element_reference_spec.rb10
-rw-r--r--spec/ruby/core/matchdata/post_match_spec.rb12
-rw-r--r--spec/ruby/core/matchdata/pre_match_spec.rb12
-rw-r--r--spec/ruby/core/matchdata/shared/captures.rb8
-rw-r--r--spec/ruby/core/matchdata/string_spec.rb5
-rw-r--r--spec/ruby/core/matchdata/to_a_spec.rb8
-rw-r--r--spec/ruby/core/matchdata/to_s_spec.rb8
-rw-r--r--spec/ruby/core/matchdata/values_at_spec.rb4
-rw-r--r--spec/ruby/core/method/clone_spec.rb15
-rw-r--r--spec/ruby/core/method/compose_spec.rb3
-rw-r--r--spec/ruby/core/method/dup_spec.rb15
-rw-r--r--spec/ruby/core/method/parameters_spec.rb47
-rw-r--r--spec/ruby/core/method/shared/dup.rb32
-rw-r--r--spec/ruby/core/method/shared/to_s.rb30
-rw-r--r--spec/ruby/core/method/source_location_spec.rb2
-rw-r--r--spec/ruby/core/method/super_method_spec.rb10
-rw-r--r--spec/ruby/core/method/to_proc_spec.rb2
-rw-r--r--spec/ruby/core/module/alias_method_spec.rb14
-rw-r--r--spec/ruby/core/module/attr_accessor_spec.rb19
-rw-r--r--spec/ruby/core/module/attr_reader_spec.rb17
-rw-r--r--spec/ruby/core/module/attr_spec.rb23
-rw-r--r--spec/ruby/core/module/attr_writer_spec.rb17
-rw-r--r--spec/ruby/core/module/autoload_spec.rb103
-rw-r--r--spec/ruby/core/module/const_added_spec.rb35
-rw-r--r--spec/ruby/core/module/const_get_spec.rb2
-rw-r--r--spec/ruby/core/module/const_set_spec.rb18
-rw-r--r--spec/ruby/core/module/const_source_location_spec.rb12
-rw-r--r--spec/ruby/core/module/define_method_spec.rb46
-rw-r--r--spec/ruby/core/module/fixtures/autoload_const_source_location.rb6
-rw-r--r--spec/ruby/core/module/fixtures/autoload_during_autoload_after_define.rb6
-rw-r--r--spec/ruby/core/module/fixtures/module.rb4
-rw-r--r--spec/ruby/core/module/module_function_spec.rb136
-rw-r--r--spec/ruby/core/module/name_spec.rb54
-rw-r--r--spec/ruby/core/module/prepend_spec.rb83
-rw-r--r--spec/ruby/core/module/private_class_method_spec.rb14
-rw-r--r--spec/ruby/core/module/public_class_method_spec.rb20
-rw-r--r--spec/ruby/core/module/ruby2_keywords_spec.rb24
-rw-r--r--spec/ruby/core/module/set_temporary_name_spec.rb68
-rw-r--r--spec/ruby/core/module/shared/attr_added.rb34
-rw-r--r--spec/ruby/core/module/shared/class_eval.rb6
-rw-r--r--spec/ruby/core/module/shared/set_visibility.rb28
-rw-r--r--spec/ruby/core/module/undef_method_spec.rb10
-rw-r--r--spec/ruby/core/module/using_spec.rb2
-rw-r--r--spec/ruby/core/mutex/lock_spec.rb4
-rw-r--r--spec/ruby/core/mutex/owned_spec.rb18
-rw-r--r--spec/ruby/core/nil/singleton_method_spec.rb15
-rw-r--r--spec/ruby/core/numeric/clone_spec.rb10
-rw-r--r--spec/ruby/core/numeric/fdiv_spec.rb1
-rw-r--r--spec/ruby/core/numeric/quo_spec.rb1
-rw-r--r--spec/ruby/core/numeric/shared/quo.rb7
-rw-r--r--spec/ruby/core/numeric/shared/step.rb8
-rw-r--r--spec/ruby/core/numeric/step_spec.rb85
-rw-r--r--spec/ruby/core/objectspace/define_finalizer_spec.rb86
-rw-r--r--spec/ruby/core/objectspace/weakkeymap/element_set_spec.rb2
-rw-r--r--spec/ruby/core/proc/arity_spec.rb16
-rw-r--r--spec/ruby/core/proc/clone_spec.rb9
-rw-r--r--spec/ruby/core/proc/compose_spec.rb46
-rw-r--r--spec/ruby/core/proc/dup_spec.rb7
-rw-r--r--spec/ruby/core/proc/eql_spec.rb8
-rw-r--r--spec/ruby/core/proc/equal_value_spec.rb8
-rw-r--r--spec/ruby/core/proc/fixtures/proc_aref.rb1
-rw-r--r--spec/ruby/core/proc/lambda_spec.rb8
-rw-r--r--spec/ruby/core/proc/new_spec.rb35
-rw-r--r--spec/ruby/core/proc/parameters_spec.rb43
-rw-r--r--spec/ruby/core/proc/ruby2_keywords_spec.rb22
-rw-r--r--spec/ruby/core/proc/shared/dup.rb23
-rw-r--r--spec/ruby/core/proc/shared/equal.rb17
-rw-r--r--spec/ruby/core/proc/shared/to_s.rb14
-rw-r--r--spec/ruby/core/proc/source_location_spec.rb8
-rw-r--r--spec/ruby/core/process/argv0_spec.rb2
-rw-r--r--spec/ruby/core/process/constants_spec.rb8
-rw-r--r--spec/ruby/core/process/detach_spec.rb14
-rw-r--r--spec/ruby/core/process/exec_spec.rb44
-rw-r--r--spec/ruby/core/process/fixtures/kill.rb2
-rw-r--r--spec/ruby/core/process/spawn_spec.rb2
-rw-r--r--spec/ruby/core/process/status/termsig_spec.rb2
-rw-r--r--spec/ruby/core/process/status/wait_spec.rb158
-rw-r--r--spec/ruby/core/process/times_spec.rb27
-rw-r--r--spec/ruby/core/process/waitpid_spec.rb3
-rw-r--r--spec/ruby/core/process/warmup_spec.rb11
-rw-r--r--spec/ruby/core/queue/deq_spec.rb7
-rw-r--r--spec/ruby/core/queue/pop_spec.rb7
-rw-r--r--spec/ruby/core/queue/shift_spec.rb7
-rw-r--r--spec/ruby/core/random/bytes_spec.rb1
-rw-r--r--spec/ruby/core/random/default_spec.rb24
-rw-r--r--spec/ruby/core/range/bsearch_spec.rb44
-rw-r--r--spec/ruby/core/range/cover_spec.rb4
-rw-r--r--spec/ruby/core/range/frozen_spec.rb34
-rw-r--r--spec/ruby/core/range/include_spec.rb4
-rw-r--r--spec/ruby/core/range/initialize_spec.rb15
-rw-r--r--spec/ruby/core/range/max_spec.rb18
-rw-r--r--spec/ruby/core/range/minmax_spec.rb24
-rw-r--r--spec/ruby/core/range/new_spec.rb14
-rw-r--r--spec/ruby/core/range/shared/cover_and_include.rb1
-rw-r--r--spec/ruby/core/range/size_spec.rb62
-rw-r--r--spec/ruby/core/range/step_spec.rb45
-rw-r--r--spec/ruby/core/rational/coerce_spec.rb6
-rw-r--r--spec/ruby/core/refinement/extend_object_spec.rb6
-rw-r--r--spec/ruby/core/refinement/import_methods_spec.rb4
-rw-r--r--spec/ruby/core/refinement/refined_class_spec.rb2
-rw-r--r--spec/ruby/core/regexp/initialize_spec.rb12
-rw-r--r--spec/ruby/core/regexp/shared/new.rb44
-rw-r--r--spec/ruby/core/regexp/shared/quote.rb10
-rw-r--r--spec/ruby/core/regexp/union_spec.rb51
-rw-r--r--spec/ruby/core/signal/signame_spec.rb12
-rw-r--r--spec/ruby/core/signal/trap_spec.rb31
-rw-r--r--spec/ruby/core/sizedqueue/append_spec.rb7
-rw-r--r--spec/ruby/core/sizedqueue/deq_spec.rb7
-rw-r--r--spec/ruby/core/sizedqueue/enq_spec.rb7
-rw-r--r--spec/ruby/core/sizedqueue/pop_spec.rb7
-rw-r--r--spec/ruby/core/sizedqueue/push_spec.rb7
-rw-r--r--spec/ruby/core/sizedqueue/shift_spec.rb7
-rw-r--r--spec/ruby/core/string/ascii_only_spec.rb23
-rw-r--r--spec/ruby/core/string/b_spec.rb1
-rw-r--r--spec/ruby/core/string/byteindex_spec.rb304
-rw-r--r--spec/ruby/core/string/byterindex_spec.rb359
-rw-r--r--spec/ruby/core/string/bytes_spec.rb2
-rw-r--r--spec/ruby/core/string/bytesize_spec.rb10
-rw-r--r--spec/ruby/core/string/byteslice_spec.rb8
-rw-r--r--spec/ruby/core/string/bytesplice_spec.rb1
-rw-r--r--spec/ruby/core/string/capitalize_spec.rb39
-rw-r--r--spec/ruby/core/string/center_spec.rb29
-rw-r--r--spec/ruby/core/string/chilled_string_spec.rb69
-rw-r--r--spec/ruby/core/string/chomp_spec.rb16
-rw-r--r--spec/ruby/core/string/chop_spec.rb13
-rw-r--r--spec/ruby/core/string/clear_spec.rb1
-rw-r--r--spec/ruby/core/string/codepoints_spec.rb2
-rw-r--r--spec/ruby/core/string/comparison_spec.rb8
-rw-r--r--spec/ruby/core/string/concat_spec.rb6
-rw-r--r--spec/ruby/core/string/delete_prefix_spec.rb16
-rw-r--r--spec/ruby/core/string/delete_spec.rb13
-rw-r--r--spec/ruby/core/string/delete_suffix_spec.rb16
-rw-r--r--spec/ruby/core/string/downcase_spec.rb13
-rw-r--r--spec/ruby/core/string/dump_spec.rb12
-rw-r--r--spec/ruby/core/string/dup_spec.rb2
-rw-r--r--spec/ruby/core/string/each_byte_spec.rb16
-rw-r--r--spec/ruby/core/string/each_grapheme_cluster_spec.rb10
-rw-r--r--spec/ruby/core/string/element_set_spec.rb1
-rw-r--r--spec/ruby/core/string/encode_spec.rb26
-rw-r--r--spec/ruby/core/string/encoding_spec.rb24
-rw-r--r--spec/ruby/core/string/fixtures/utf-8-encoding.rb7
-rw-r--r--spec/ruby/core/string/force_encoding_spec.rb1
-rw-r--r--spec/ruby/core/string/freeze_spec.rb1
-rw-r--r--spec/ruby/core/string/gsub_spec.rb22
-rw-r--r--spec/ruby/core/string/include_spec.rb12
-rw-r--r--spec/ruby/core/string/index_spec.rb22
-rw-r--r--spec/ruby/core/string/insert_spec.rb2
-rw-r--r--spec/ruby/core/string/inspect_spec.rb2
-rw-r--r--spec/ruby/core/string/ljust_spec.rb29
-rw-r--r--spec/ruby/core/string/lstrip_spec.rb19
-rw-r--r--spec/ruby/core/string/ord_spec.rb2
-rw-r--r--spec/ruby/core/string/partition_spec.rb4
-rw-r--r--spec/ruby/core/string/prepend_spec.rb1
-rw-r--r--spec/ruby/core/string/reverse_spec.rb19
-rw-r--r--spec/ruby/core/string/rindex_spec.rb14
-rw-r--r--spec/ruby/core/string/rjust_spec.rb29
-rw-r--r--spec/ruby/core/string/rpartition_spec.rb4
-rw-r--r--spec/ruby/core/string/rstrip_spec.rb11
-rw-r--r--spec/ruby/core/string/scan_spec.rb10
-rw-r--r--spec/ruby/core/string/scrub_spec.rb37
-rw-r--r--spec/ruby/core/string/setbyte_spec.rb1
-rw-r--r--spec/ruby/core/string/shared/byte_index_common.rb63
-rw-r--r--spec/ruby/core/string/shared/chars.rb12
-rw-r--r--spec/ruby/core/string/shared/codepoints.rb6
-rw-r--r--spec/ruby/core/string/shared/concat.rb1
-rw-r--r--spec/ruby/core/string/shared/dedup.rb9
-rw-r--r--spec/ruby/core/string/shared/each_codepoint_without_block.rb4
-rw-r--r--spec/ruby/core/string/shared/each_line.rb20
-rw-r--r--spec/ruby/core/string/shared/encode.rb1
-rw-r--r--spec/ruby/core/string/shared/eql.rb8
-rw-r--r--spec/ruby/core/string/shared/length.rb10
-rw-r--r--spec/ruby/core/string/shared/partition.rb34
-rw-r--r--spec/ruby/core/string/shared/replace.rb1
-rw-r--r--spec/ruby/core/string/shared/slice.rb118
-rw-r--r--spec/ruby/core/string/shared/strip.rb18
-rw-r--r--spec/ruby/core/string/shared/succ.rb19
-rw-r--r--spec/ruby/core/string/shared/to_a.rb9
-rw-r--r--spec/ruby/core/string/shared/to_sym.rb4
-rw-r--r--spec/ruby/core/string/slice_spec.rb95
-rw-r--r--spec/ruby/core/string/split_spec.rb120
-rw-r--r--spec/ruby/core/string/squeeze_spec.rb13
-rw-r--r--spec/ruby/core/string/start_with_spec.rb17
-rw-r--r--spec/ruby/core/string/strip_spec.rb17
-rw-r--r--spec/ruby/core/string/sub_spec.rb22
-rw-r--r--spec/ruby/core/string/swapcase_spec.rb16
-rw-r--r--spec/ruby/core/string/to_i_spec.rb12
-rw-r--r--spec/ruby/core/string/tr_s_spec.rb22
-rw-r--r--spec/ruby/core/string/tr_spec.rb22
-rw-r--r--spec/ruby/core/string/unicode_normalize_spec.rb1
-rw-r--r--spec/ruby/core/string/unicode_normalized_spec.rb1
-rw-r--r--spec/ruby/core/string/unpack/a_spec.rb2
-rw-r--r--spec/ruby/core/string/unpack/b_spec.rb4
-rw-r--r--spec/ruby/core/string/unpack/u_spec.rb2
-rw-r--r--spec/ruby/core/string/upcase_spec.rb13
-rw-r--r--spec/ruby/core/string/uplus_spec.rb4
-rw-r--r--spec/ruby/core/string/upto_spec.rb6
-rw-r--r--spec/ruby/core/string/valid_encoding_spec.rb30
-rw-r--r--spec/ruby/core/struct/constants_spec.rb15
-rw-r--r--spec/ruby/core/struct/new_spec.rb2
-rw-r--r--spec/ruby/core/symbol/name_spec.rb24
-rw-r--r--spec/ruby/core/symbol/to_proc_spec.rb47
-rw-r--r--spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb2
-rw-r--r--spec/ruby/core/thread/backtrace/location/label_spec.rb10
-rw-r--r--spec/ruby/core/thread/backtrace/location/lineno_spec.rb2
-rw-r--r--spec/ruby/core/thread/backtrace/location/path_spec.rb2
-rw-r--r--spec/ruby/core/thread/backtrace_locations_spec.rb2
-rw-r--r--spec/ruby/core/thread/backtrace_spec.rb2
-rw-r--r--spec/ruby/core/thread/each_caller_location_spec.rb49
-rw-r--r--spec/ruby/core/thread/exclusive_spec.rb49
-rw-r--r--spec/ruby/core/thread/fetch_spec.rb30
-rw-r--r--spec/ruby/core/thread/ignore_deadlock_spec.rb26
-rw-r--r--spec/ruby/core/thread/native_thread_id_spec.rb9
-rw-r--r--spec/ruby/core/thread/report_on_exception_spec.rb50
-rw-r--r--spec/ruby/core/thread/thread_variable_get_spec.rb2
-rw-r--r--spec/ruby/core/time/_load_spec.rb3
-rw-r--r--spec/ruby/core/time/at_spec.rb9
-rw-r--r--spec/ruby/core/time/deconstruct_keys_spec.rb5
-rw-r--r--spec/ruby/core/time/fixtures/classes.rb1
-rw-r--r--spec/ruby/core/time/new_spec.rb24
-rw-r--r--spec/ruby/core/time/succ_spec.rb40
-rw-r--r--spec/ruby/core/tracepoint/enable_spec.rb8
-rw-r--r--spec/ruby/core/tracepoint/inspect_spec.rb14
-rw-r--r--spec/ruby/core/tracepoint/path_spec.rb31
-rw-r--r--spec/ruby/core/true/singleton_method_spec.rb15
-rw-r--r--spec/ruby/core/unboundmethod/clone_spec.rb13
-rw-r--r--spec/ruby/core/unboundmethod/dup_spec.rb15
-rw-r--r--spec/ruby/core/unboundmethod/shared/dup.rb32
-rw-r--r--spec/ruby/core/unboundmethod/source_location_spec.rb2
-rw-r--r--spec/ruby/core/unboundmethod/super_method_spec.rb10
-rw-r--r--spec/ruby/core/warning/element_reference_spec.rb8
-rw-r--r--spec/ruby/core/warning/element_set_spec.rb20
-rw-r--r--spec/ruby/core/warning/warn_spec.rb145
-rw-r--r--spec/ruby/default.mspec1
-rw-r--r--spec/ruby/fixtures/code/d/load_fixture.rb.rb1
-rw-r--r--spec/ruby/language/alias_spec.rb20
-rw-r--r--spec/ruby/language/assignments_spec.rb529
-rw-r--r--spec/ruby/language/block_spec.rb245
-rw-r--r--spec/ruby/language/break_spec.rb2
-rw-r--r--spec/ruby/language/case_spec.rb133
-rw-r--r--spec/ruby/language/class_spec.rb18
-rw-r--r--spec/ruby/language/class_variable_spec.rb56
-rw-r--r--spec/ruby/language/constants_spec.rb38
-rw-r--r--spec/ruby/language/def_spec.rb2
-rw-r--r--spec/ruby/language/defined_spec.rb147
-rw-r--r--spec/ruby/language/delegation_spec.rb62
-rw-r--r--spec/ruby/language/encoding_spec.rb8
-rw-r--r--spec/ruby/language/ensure_spec.rb17
-rw-r--r--spec/ruby/language/execution_spec.rb78
-rw-r--r--spec/ruby/language/file_spec.rb12
-rw-r--r--spec/ruby/language/fixtures/rescue/top_level.rb7
-rw-r--r--spec/ruby/language/fixtures/super.rb48
-rw-r--r--spec/ruby/language/hash_spec.rb47
-rw-r--r--spec/ruby/language/if_spec.rb10
-rw-r--r--spec/ruby/language/keyword_arguments_spec.rb555
-rw-r--r--spec/ruby/language/lambda_spec.rb68
-rw-r--r--spec/ruby/language/method_spec.rb734
-rw-r--r--spec/ruby/language/module_spec.rb18
-rw-r--r--spec/ruby/language/numbered_parameters_spec.rb68
-rw-r--r--spec/ruby/language/optional_assignments_spec.rb294
-rw-r--r--spec/ruby/language/pattern_matching_spec.rb482
-rw-r--r--spec/ruby/language/predefined_spec.rb59
-rw-r--r--spec/ruby/language/proc_spec.rb14
-rw-r--r--spec/ruby/language/regexp/character_classes_spec.rb6
-rw-r--r--spec/ruby/language/regexp/encoding_spec.rb42
-rw-r--r--spec/ruby/language/regexp/repetition_spec.rb8
-rw-r--r--spec/ruby/language/regexp_spec.rb8
-rw-r--r--spec/ruby/language/rescue_spec.rb91
-rw-r--r--spec/ruby/language/return_spec.rb15
-rw-r--r--spec/ruby/language/safe_navigator_spec.rb80
-rw-r--r--spec/ruby/language/safe_spec.rb28
-rw-r--r--spec/ruby/language/send_spec.rb38
-rw-r--r--spec/ruby/language/singleton_class_spec.rb9
-rw-r--r--spec/ruby/language/string_spec.rb42
-rw-r--r--spec/ruby/language/super_spec.rb26
-rw-r--r--spec/ruby/language/symbol_spec.rb14
-rw-r--r--spec/ruby/language/undef_spec.rb9
-rw-r--r--spec/ruby/language/variables_spec.rb30
-rw-r--r--spec/ruby/language/yield_spec.rb31
-rw-r--r--spec/ruby/library/English/English_spec.rb16
-rw-r--r--spec/ruby/library/bigdecimal/add_spec.rb2
-rw-r--r--spec/ruby/library/bigdecimal/core_spec.rb59
-rw-r--r--spec/ruby/library/bigdecimal/remainder_spec.rb2
-rw-r--r--spec/ruby/library/bigdecimal/round_spec.rb4
-rw-r--r--spec/ruby/library/bigdecimal/shared/to_int.rb2
-rw-r--r--spec/ruby/library/bigdecimal/sqrt_spec.rb6
-rw-r--r--spec/ruby/library/bigdecimal/to_s_spec.rb27
-rw-r--r--spec/ruby/library/bigmath/log_spec.rb10
-rw-r--r--spec/ruby/library/cgi/escapeURIComponent_spec.rb57
-rw-r--r--spec/ruby/library/cgi/initialize_spec.rb2
-rw-r--r--spec/ruby/library/coverage/result_spec.rb266
-rw-r--r--spec/ruby/library/coverage/start_spec.rb81
-rw-r--r--spec/ruby/library/csv/generate_spec.rb2
-rw-r--r--spec/ruby/library/date/deconstruct_keys_spec.rb3
-rw-r--r--spec/ruby/library/date/iso8601_spec.rb21
-rw-r--r--spec/ruby/library/date/new_spec.rb1
-rw-r--r--spec/ruby/library/date/parse_spec.rb10
-rw-r--r--spec/ruby/library/date/shared/new_bang.rb14
-rw-r--r--spec/ruby/library/date/shared/parse.rb4
-rw-r--r--spec/ruby/library/date/shared/parse_eu.rb8
-rw-r--r--spec/ruby/library/date/shared/parse_us.rb8
-rw-r--r--spec/ruby/library/date/strftime_spec.rb5
-rw-r--r--spec/ruby/library/datetime/deconstruct_keys_spec.rb3
-rw-r--r--spec/ruby/library/datetime/rfc2822_spec.rb4
-rw-r--r--spec/ruby/library/datetime/strftime_spec.rb5
-rw-r--r--spec/ruby/library/datetime/to_time_spec.rb4
-rw-r--r--spec/ruby/library/digest/instance/shared/update.rb2
-rw-r--r--spec/ruby/library/digest/md5/shared/sample.rb17
-rw-r--r--spec/ruby/library/drb/start_service_spec.rb47
-rw-r--r--spec/ruby/library/erb/new_spec.rb2
-rw-r--r--spec/ruby/library/erb/run_spec.rb2
-rw-r--r--spec/ruby/library/etc/uname_spec.rb14
-rw-r--r--spec/ruby/library/fiber/current_spec.rb15
-rw-r--r--spec/ruby/library/fiber/resume_spec.rb35
-rw-r--r--spec/ruby/library/fiber/transfer_spec.rb56
-rw-r--r--spec/ruby/library/io-wait/wait_spec.rb113
-rw-r--r--spec/ruby/library/ipaddr/new_spec.rb10
-rw-r--r--spec/ruby/library/logger/device/close_spec.rb15
-rw-r--r--spec/ruby/library/logger/device/write_spec.rb15
-rw-r--r--spec/ruby/library/matrix/I_spec.rb9
-rw-r--r--spec/ruby/library/matrix/antisymmetric_spec.rb54
-rw-r--r--spec/ruby/library/matrix/build_spec.rb117
-rw-r--r--spec/ruby/library/matrix/clone_spec.rb37
-rw-r--r--spec/ruby/library/matrix/coerce_spec.rb11
-rw-r--r--spec/ruby/library/matrix/collect_spec.rb9
-rw-r--r--spec/ruby/library/matrix/column_size_spec.rb19
-rw-r--r--spec/ruby/library/matrix/column_spec.rb53
-rw-r--r--spec/ruby/library/matrix/column_vector_spec.rb37
-rw-r--r--spec/ruby/library/matrix/column_vectors_spec.rb37
-rw-r--r--spec/ruby/library/matrix/columns_spec.rb67
-rw-r--r--spec/ruby/library/matrix/conj_spec.rb9
-rw-r--r--spec/ruby/library/matrix/conjugate_spec.rb9
-rw-r--r--spec/ruby/library/matrix/constructor_spec.rb103
-rw-r--r--spec/ruby/library/matrix/det_spec.rb11
-rw-r--r--spec/ruby/library/matrix/determinant_spec.rb11
-rw-r--r--spec/ruby/library/matrix/diagonal_spec.rb105
-rw-r--r--spec/ruby/library/matrix/divide_spec.rb83
-rw-r--r--spec/ruby/library/matrix/each_spec.rb119
-rw-r--r--spec/ruby/library/matrix/each_with_index_spec.rb133
-rw-r--r--spec/ruby/library/matrix/eigenvalue_decomposition/eigenvalue_matrix_spec.rb13
-rw-r--r--spec/ruby/library/matrix/eigenvalue_decomposition/eigenvalues_spec.rb35
-rw-r--r--spec/ruby/library/matrix/eigenvalue_decomposition/eigenvector_matrix_spec.rb33
-rw-r--r--spec/ruby/library/matrix/eigenvalue_decomposition/eigenvectors_spec.rb37
-rw-r--r--spec/ruby/library/matrix/eigenvalue_decomposition/initialize_spec.rb39
-rw-r--r--spec/ruby/library/matrix/eigenvalue_decomposition/to_a_spec.rb27
-rw-r--r--spec/ruby/library/matrix/element_reference_spec.rb31
-rw-r--r--spec/ruby/library/matrix/empty_spec.rb107
-rw-r--r--spec/ruby/library/matrix/eql_spec.rb15
-rw-r--r--spec/ruby/library/matrix/equal_value_spec.rb15
-rw-r--r--spec/ruby/library/matrix/exponent_spec.rb93
-rw-r--r--spec/ruby/library/matrix/find_index_spec.rb221
-rw-r--r--spec/ruby/library/matrix/hash_spec.rb21
-rw-r--r--spec/ruby/library/matrix/hermitian_spec.rb53
-rw-r--r--spec/ruby/library/matrix/identity_spec.rb9
-rw-r--r--spec/ruby/library/matrix/imag_spec.rb9
-rw-r--r--spec/ruby/library/matrix/imaginary_spec.rb9
-rw-r--r--spec/ruby/library/matrix/inspect_spec.rb39
-rw-r--r--spec/ruby/library/matrix/inv_spec.rb11
-rw-r--r--spec/ruby/library/matrix/inverse_from_spec.rb9
-rw-r--r--spec/ruby/library/matrix/inverse_spec.rb11
-rw-r--r--spec/ruby/library/matrix/lower_triangular_spec.rb39
-rw-r--r--spec/ruby/library/matrix/lup_decomposition/determinant_spec.rb33
-rw-r--r--spec/ruby/library/matrix/lup_decomposition/initialize_spec.rb21
-rw-r--r--spec/ruby/library/matrix/lup_decomposition/l_spec.rb27
-rw-r--r--spec/ruby/library/matrix/lup_decomposition/p_spec.rb27
-rw-r--r--spec/ruby/library/matrix/lup_decomposition/solve_spec.rb85
-rw-r--r--spec/ruby/library/matrix/lup_decomposition/to_a_spec.rb53
-rw-r--r--spec/ruby/library/matrix/lup_decomposition/u_spec.rb27
-rw-r--r--spec/ruby/library/matrix/map_spec.rb9
-rw-r--r--spec/ruby/library/matrix/minor_spec.rb135
-rw-r--r--spec/ruby/library/matrix/minus_spec.rb65
-rw-r--r--spec/ruby/library/matrix/multiply_spec.rb104
-rw-r--r--spec/ruby/library/matrix/new_spec.rb11
-rw-r--r--spec/ruby/library/matrix/normal_spec.rb41
-rw-r--r--spec/ruby/library/matrix/orthogonal_spec.rb41
-rw-r--r--spec/ruby/library/matrix/permutation_spec.rb51
-rw-r--r--spec/ruby/library/matrix/plus_spec.rb65
-rw-r--r--spec/ruby/library/matrix/rank_spec.rb29
-rw-r--r--spec/ruby/library/matrix/real_spec.rb63
-rw-r--r--spec/ruby/library/matrix/rect_spec.rb9
-rw-r--r--spec/ruby/library/matrix/rectangular_spec.rb9
-rw-r--r--spec/ruby/library/matrix/regular_spec.rb45
-rw-r--r--spec/ruby/library/matrix/round_spec.rb31
-rw-r--r--spec/ruby/library/matrix/row_size_spec.rb19
-rw-r--r--spec/ruby/library/matrix/row_spec.rb55
-rw-r--r--spec/ruby/library/matrix/row_vector_spec.rb33
-rw-r--r--spec/ruby/library/matrix/row_vectors_spec.rb37
-rw-r--r--spec/ruby/library/matrix/rows_spec.rb65
-rw-r--r--spec/ruby/library/matrix/scalar/Fail_spec.rb9
-rw-r--r--spec/ruby/library/matrix/scalar/Raise_spec.rb9
-rw-r--r--spec/ruby/library/matrix/scalar/divide_spec.rb9
-rw-r--r--spec/ruby/library/matrix/scalar/exponent_spec.rb9
-rw-r--r--spec/ruby/library/matrix/scalar/included_spec.rb9
-rw-r--r--spec/ruby/library/matrix/scalar/initialize_spec.rb9
-rw-r--r--spec/ruby/library/matrix/scalar/minus_spec.rb9
-rw-r--r--spec/ruby/library/matrix/scalar/multiply_spec.rb9
-rw-r--r--spec/ruby/library/matrix/scalar/plus_spec.rb9
-rw-r--r--spec/ruby/library/matrix/scalar_spec.rb93
-rw-r--r--spec/ruby/library/matrix/singular_spec.rb47
-rw-r--r--spec/ruby/library/matrix/square_spec.rb41
-rw-r--r--spec/ruby/library/matrix/symmetric_spec.rb45
-rw-r--r--spec/ruby/library/matrix/t_spec.rb9
-rw-r--r--spec/ruby/library/matrix/to_a_spec.rb17
-rw-r--r--spec/ruby/library/matrix/to_s_spec.rb9
-rw-r--r--spec/ruby/library/matrix/tr_spec.rb11
-rw-r--r--spec/ruby/library/matrix/trace_spec.rb11
-rw-r--r--spec/ruby/library/matrix/transpose_spec.rb9
-rw-r--r--spec/ruby/library/matrix/unit_spec.rb9
-rw-r--r--spec/ruby/library/matrix/unitary_spec.rb50
-rw-r--r--spec/ruby/library/matrix/upper_triangular_spec.rb39
-rw-r--r--spec/ruby/library/matrix/vector/cross_product_spec.rb21
-rw-r--r--spec/ruby/library/matrix/vector/each2_spec.rb81
-rw-r--r--spec/ruby/library/matrix/vector/eql_spec.rb23
-rw-r--r--spec/ruby/library/matrix/vector/inner_product_spec.rb33
-rw-r--r--spec/ruby/library/matrix/vector/normalize_spec.rb29
-rw-r--r--spec/ruby/library/matrix/zero_spec.rb75
-rw-r--r--spec/ruby/library/net-ftp/FTPError_spec.rb8
-rw-r--r--spec/ruby/library/net-ftp/FTPPermError_spec.rb12
-rw-r--r--spec/ruby/library/net-ftp/FTPProtoError_spec.rb12
-rw-r--r--spec/ruby/library/net-ftp/FTPReplyError_spec.rb12
-rw-r--r--spec/ruby/library/net-ftp/FTPTempError_spec.rb12
-rw-r--r--spec/ruby/library/net-ftp/abort_spec.rb62
-rw-r--r--spec/ruby/library/net-ftp/acct_spec.rb58
-rw-r--r--spec/ruby/library/net-ftp/binary_spec.rb24
-rw-r--r--spec/ruby/library/net-ftp/chdir_spec.rb99
-rw-r--r--spec/ruby/library/net-ftp/close_spec.rb30
-rw-r--r--spec/ruby/library/net-ftp/closed_spec.rb21
-rw-r--r--spec/ruby/library/net-ftp/connect_spec.rb51
-rw-r--r--spec/ruby/library/net-ftp/debug_mode_spec.rb23
-rw-r--r--spec/ruby/library/net-ftp/default_passive_spec.rb8
-rw-r--r--spec/ruby/library/net-ftp/delete_spec.rb59
-rw-r--r--spec/ruby/library/net-ftp/dir_spec.rb8
-rw-r--r--spec/ruby/library/net-ftp/fixtures/default_passive.rb (renamed from spec/ruby/library/net/ftp/fixtures/default_passive.rb)0
-rw-r--r--spec/ruby/library/net-ftp/fixtures/passive.rb (renamed from spec/ruby/library/net/ftp/fixtures/passive.rb)0
-rw-r--r--spec/ruby/library/net-ftp/fixtures/putbinaryfile (renamed from spec/ruby/library/net/ftp/fixtures/putbinaryfile)0
-rw-r--r--spec/ruby/library/net-ftp/fixtures/puttextfile (renamed from spec/ruby/library/net/ftp/fixtures/puttextfile)0
-rw-r--r--spec/ruby/library/net-ftp/fixtures/server.rb (renamed from spec/ruby/library/net/ftp/fixtures/server.rb)0
-rw-r--r--spec/ruby/library/net-ftp/get_spec.rb21
-rw-r--r--spec/ruby/library/net-ftp/getbinaryfile_spec.rb8
-rw-r--r--spec/ruby/library/net-ftp/getdir_spec.rb7
-rw-r--r--spec/ruby/library/net-ftp/gettextfile_spec.rb8
-rw-r--r--spec/ruby/library/net-ftp/help_spec.rb66
-rw-r--r--spec/ruby/library/net-ftp/initialize_spec.rb405
-rw-r--r--spec/ruby/library/net-ftp/last_response_code_spec.rb8
-rw-r--r--spec/ruby/library/net-ftp/last_response_spec.rb25
-rw-r--r--spec/ruby/library/net-ftp/lastresp_spec.rb8
-rw-r--r--spec/ruby/library/net-ftp/list_spec.rb8
-rw-r--r--spec/ruby/library/net-ftp/login_spec.rb195
-rw-r--r--spec/ruby/library/net-ftp/ls_spec.rb8
-rw-r--r--spec/ruby/library/net-ftp/mdtm_spec.rb38
-rw-r--r--spec/ruby/library/net-ftp/mkdir_spec.rb61
-rw-r--r--spec/ruby/library/net-ftp/mtime_spec.rb50
-rw-r--r--spec/ruby/library/net-ftp/nlst_spec.rb92
-rw-r--r--spec/ruby/library/net-ftp/noop_spec.rb38
-rw-r--r--spec/ruby/library/net-ftp/open_spec.rb55
-rw-r--r--spec/ruby/library/net-ftp/passive_spec.rb28
-rw-r--r--spec/ruby/library/net-ftp/put_spec.rb21
-rw-r--r--spec/ruby/library/net-ftp/putbinaryfile_spec.rb8
-rw-r--r--spec/ruby/library/net-ftp/puttextfile_spec.rb8
-rw-r--r--spec/ruby/library/net-ftp/pwd_spec.rb53
-rw-r--r--spec/ruby/library/net-ftp/quit_spec.rb33
-rw-r--r--spec/ruby/library/net-ftp/rename_spec.rb94
-rw-r--r--spec/ruby/library/net-ftp/resume_spec.rb23
-rw-r--r--spec/ruby/library/net-ftp/retrbinary_spec.rb30
-rw-r--r--spec/ruby/library/net-ftp/retrlines_spec.rb34
-rw-r--r--spec/ruby/library/net-ftp/return_code_spec.rb24
-rw-r--r--spec/ruby/library/net-ftp/rmdir_spec.rb58
-rw-r--r--spec/ruby/library/net-ftp/sendcmd_spec.rb54
-rw-r--r--spec/ruby/library/net-ftp/set_socket_spec.rb8
-rw-r--r--spec/ruby/library/net-ftp/shared/getbinaryfile.rb150
-rw-r--r--spec/ruby/library/net-ftp/shared/gettextfile.rb100
-rw-r--r--spec/ruby/library/net-ftp/shared/last_response_code.rb (renamed from spec/ruby/library/net/ftp/shared/last_response_code.rb)0
-rw-r--r--spec/ruby/library/net-ftp/shared/list.rb (renamed from spec/ruby/library/net/ftp/shared/list.rb)0
-rw-r--r--spec/ruby/library/net-ftp/shared/putbinaryfile.rb167
-rw-r--r--spec/ruby/library/net-ftp/shared/puttextfile.rb120
-rw-r--r--spec/ruby/library/net-ftp/shared/pwd.rb (renamed from spec/ruby/library/net/ftp/shared/pwd.rb)0
-rw-r--r--spec/ruby/library/net-ftp/site_spec.rb53
-rw-r--r--spec/ruby/library/net-ftp/size_spec.rb48
-rw-r--r--spec/ruby/library/net-ftp/spec_helper.rb (renamed from spec/ruby/library/net/ftp/spec_helper.rb)0
-rw-r--r--spec/ruby/library/net-ftp/status_spec.rb67
-rw-r--r--spec/ruby/library/net-ftp/storbinary_spec.rb49
-rw-r--r--spec/ruby/library/net-ftp/storlines_spec.rb44
-rw-r--r--spec/ruby/library/net-ftp/system_spec.rb48
-rw-r--r--spec/ruby/library/net-ftp/voidcmd_spec.rb54
-rw-r--r--spec/ruby/library/net-ftp/welcome_spec.rb25
-rw-r--r--spec/ruby/library/net-http/HTTPBadResponse_spec.rb8
-rw-r--r--spec/ruby/library/net-http/HTTPClientExcepton_spec.rb12
-rw-r--r--spec/ruby/library/net-http/HTTPError_spec.rb12
-rw-r--r--spec/ruby/library/net-http/HTTPFatalError_spec.rb12
-rw-r--r--spec/ruby/library/net-http/HTTPHeaderSyntaxError_spec.rb8
-rw-r--r--spec/ruby/library/net-http/HTTPRetriableError_spec.rb12
-rw-r--r--spec/ruby/library/net-http/HTTPServerException_spec.rb12
-rw-r--r--spec/ruby/library/net-http/http/Proxy_spec.rb35
-rw-r--r--spec/ruby/library/net-http/http/active_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/address_spec.rb9
-rw-r--r--spec/ruby/library/net-http/http/close_on_empty_response_spec.rb10
-rw-r--r--spec/ruby/library/net-http/http/copy_spec.rb21
-rw-r--r--spec/ruby/library/net-http/http/default_port_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/delete_spec.rb21
-rw-r--r--spec/ruby/library/net-http/http/finish_spec.rb29
-rw-r--r--spec/ruby/library/net-http/http/fixtures/http_server.rb (renamed from spec/ruby/library/net/http/http/fixtures/http_server.rb)0
-rw-r--r--spec/ruby/library/net-http/http/get2_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/get_print_spec.rb30
-rw-r--r--spec/ruby/library/net-http/http/get_response_spec.rb30
-rw-r--r--spec/ruby/library/net-http/http/get_spec.rb94
-rw-r--r--spec/ruby/library/net-http/http/head2_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/head_spec.rb25
-rw-r--r--spec/ruby/library/net-http/http/http_default_port_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/https_default_port_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/initialize_spec.rb46
-rw-r--r--spec/ruby/library/net-http/http/inspect_spec.rb24
-rw-r--r--spec/ruby/library/net-http/http/is_version_1_1_spec.rb7
-rw-r--r--spec/ruby/library/net-http/http/is_version_1_2_spec.rb7
-rw-r--r--spec/ruby/library/net-http/http/lock_spec.rb21
-rw-r--r--spec/ruby/library/net-http/http/mkcol_spec.rb21
-rw-r--r--spec/ruby/library/net-http/http/move_spec.rb25
-rw-r--r--spec/ruby/library/net-http/http/new_spec.rb86
-rw-r--r--spec/ruby/library/net-http/http/newobj_spec.rb48
-rw-r--r--spec/ruby/library/net-http/http/open_timeout_spec.rb24
-rw-r--r--spec/ruby/library/net-http/http/options_spec.rb25
-rw-r--r--spec/ruby/library/net-http/http/port_spec.rb9
-rw-r--r--spec/ruby/library/net-http/http/post2_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/post_form_spec.rb22
-rw-r--r--spec/ruby/library/net-http/http/post_spec.rb74
-rw-r--r--spec/ruby/library/net-http/http/propfind_spec.rb24
-rw-r--r--spec/ruby/library/net-http/http/proppatch_spec.rb24
-rw-r--r--spec/ruby/library/net-http/http/proxy_address_spec.rb31
-rw-r--r--spec/ruby/library/net-http/http/proxy_class_spec.rb9
-rw-r--r--spec/ruby/library/net-http/http/proxy_pass_spec.rb39
-rw-r--r--spec/ruby/library/net-http/http/proxy_port_spec.rb39
-rw-r--r--spec/ruby/library/net-http/http/proxy_user_spec.rb39
-rw-r--r--spec/ruby/library/net-http/http/put2_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/put_spec.rb24
-rw-r--r--spec/ruby/library/net-http/http/read_timeout_spec.rb24
-rw-r--r--spec/ruby/library/net-http/http/request_get_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/request_head_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/request_post_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/request_put_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/request_spec.rb109
-rw-r--r--spec/ruby/library/net-http/http/request_types_spec.rb254
-rw-r--r--spec/ruby/library/net-http/http/send_request_spec.rb61
-rw-r--r--spec/ruby/library/net-http/http/set_debug_output_spec.rb33
-rw-r--r--spec/ruby/library/net-http/http/shared/request_get.rb (renamed from spec/ruby/library/net/http/http/shared/request_get.rb)0
-rw-r--r--spec/ruby/library/net-http/http/shared/request_head.rb (renamed from spec/ruby/library/net/http/http/shared/request_head.rb)0
-rw-r--r--spec/ruby/library/net-http/http/shared/request_post.rb (renamed from spec/ruby/library/net/http/http/shared/request_post.rb)0
-rw-r--r--spec/ruby/library/net-http/http/shared/request_put.rb (renamed from spec/ruby/library/net/http/http/shared/request_put.rb)0
-rw-r--r--spec/ruby/library/net-http/http/shared/started.rb (renamed from spec/ruby/library/net/http/http/shared/started.rb)0
-rw-r--r--spec/ruby/library/net-http/http/shared/version_1_1.rb (renamed from spec/ruby/library/net/http/http/shared/version_1_1.rb)0
-rw-r--r--spec/ruby/library/net-http/http/shared/version_1_2.rb (renamed from spec/ruby/library/net/http/http/shared/version_1_2.rb)0
-rw-r--r--spec/ruby/library/net-http/http/socket_type_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/start_spec.rb111
-rw-r--r--spec/ruby/library/net-http/http/started_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/trace_spec.rb24
-rw-r--r--spec/ruby/library/net-http/http/unlock_spec.rb24
-rw-r--r--spec/ruby/library/net-http/http/use_ssl_spec.rb9
-rw-r--r--spec/ruby/library/net-http/http/version_1_1_spec.rb7
-rw-r--r--spec/ruby/library/net-http/http/version_1_2_spec.rb20
-rw-r--r--spec/ruby/library/net-http/httpexceptions/fixtures/classes.rb (renamed from spec/ruby/library/net/http/httpexceptions/fixtures/classes.rb)0
-rw-r--r--spec/ruby/library/net-http/httpexceptions/initialize_spec.rb17
-rw-r--r--spec/ruby/library/net-http/httpexceptions/response_spec.rb10
-rw-r--r--spec/ruby/library/net-http/httpgenericrequest/body_exist_spec.rb21
-rw-r--r--spec/ruby/library/net-http/httpgenericrequest/body_spec.rb30
-rw-r--r--spec/ruby/library/net-http/httpgenericrequest/body_stream_spec.rb32
-rw-r--r--spec/ruby/library/net-http/httpgenericrequest/exec_spec.rb131
-rw-r--r--spec/ruby/library/net-http/httpgenericrequest/inspect_spec.rb25
-rw-r--r--spec/ruby/library/net-http/httpgenericrequest/method_spec.rb15
-rw-r--r--spec/ruby/library/net-http/httpgenericrequest/path_spec.rb12
-rw-r--r--spec/ruby/library/net-http/httpgenericrequest/request_body_permitted_spec.rb12
-rw-r--r--spec/ruby/library/net-http/httpgenericrequest/response_body_permitted_spec.rb12
-rw-r--r--spec/ruby/library/net-http/httpgenericrequest/set_body_internal_spec.rb21
-rw-r--r--spec/ruby/library/net-http/httpheader/add_field_spec.rb31
-rw-r--r--spec/ruby/library/net-http/httpheader/basic_auth_spec.rb14
-rw-r--r--spec/ruby/library/net-http/httpheader/canonical_each_spec.rb8
-rw-r--r--spec/ruby/library/net-http/httpheader/chunked_spec.rb22
-rw-r--r--spec/ruby/library/net-http/httpheader/content_length_spec.rb54
-rw-r--r--spec/ruby/library/net-http/httpheader/content_range_spec.rb32
-rw-r--r--spec/ruby/library/net-http/httpheader/content_type_spec.rb26
-rw-r--r--spec/ruby/library/net-http/httpheader/delete_spec.rb30
-rw-r--r--spec/ruby/library/net-http/httpheader/each_capitalized_name_spec.rb35
-rw-r--r--spec/ruby/library/net-http/httpheader/each_capitalized_spec.rb8
-rw-r--r--spec/ruby/library/net-http/httpheader/each_header_spec.rb8
-rw-r--r--spec/ruby/library/net-http/httpheader/each_key_spec.rb8
-rw-r--r--spec/ruby/library/net-http/httpheader/each_name_spec.rb8
-rw-r--r--spec/ruby/library/net-http/httpheader/each_spec.rb8
-rw-r--r--spec/ruby/library/net-http/httpheader/each_value_spec.rb35
-rw-r--r--spec/ruby/library/net-http/httpheader/element_reference_spec.rb39
-rw-r--r--spec/ruby/library/net-http/httpheader/element_set_spec.rb41
-rw-r--r--spec/ruby/library/net-http/httpheader/fetch_spec.rb68
-rw-r--r--spec/ruby/library/net-http/httpheader/fixtures/classes.rb (renamed from spec/ruby/library/net/http/httpheader/fixtures/classes.rb)0
-rw-r--r--spec/ruby/library/net-http/httpheader/form_data_spec.rb8
-rw-r--r--spec/ruby/library/net-http/httpheader/get_fields_spec.rb39
-rw-r--r--spec/ruby/library/net-http/httpheader/initialize_http_header_spec.rb21
-rw-r--r--spec/ruby/library/net-http/httpheader/key_spec.rb21
-rw-r--r--spec/ruby/library/net-http/httpheader/length_spec.rb8
-rw-r--r--spec/ruby/library/net-http/httpheader/main_type_spec.rb24
-rw-r--r--spec/ruby/library/net-http/httpheader/proxy_basic_auth_spec.rb14
-rw-r--r--spec/ruby/library/net-http/httpheader/range_length_spec.rb32
-rw-r--r--spec/ruby/library/net-http/httpheader/range_spec.rb48
-rw-r--r--spec/ruby/library/net-http/httpheader/set_content_type_spec.rb8
-rw-r--r--spec/ruby/library/net-http/httpheader/set_form_data_spec.rb8
-rw-r--r--spec/ruby/library/net-http/httpheader/set_range_spec.rb8
-rw-r--r--spec/ruby/library/net-http/httpheader/shared/each_capitalized.rb (renamed from spec/ruby/library/net/http/httpheader/shared/each_capitalized.rb)0
-rw-r--r--spec/ruby/library/net-http/httpheader/shared/each_header.rb (renamed from spec/ruby/library/net/http/httpheader/shared/each_header.rb)0
-rw-r--r--spec/ruby/library/net-http/httpheader/shared/each_name.rb (renamed from spec/ruby/library/net/http/httpheader/shared/each_name.rb)0
-rw-r--r--spec/ruby/library/net-http/httpheader/shared/set_content_type.rb (renamed from spec/ruby/library/net/http/httpheader/shared/set_content_type.rb)0
-rw-r--r--spec/ruby/library/net-http/httpheader/shared/set_form_data.rb (renamed from spec/ruby/library/net/http/httpheader/shared/set_form_data.rb)0
-rw-r--r--spec/ruby/library/net-http/httpheader/shared/set_range.rb (renamed from spec/ruby/library/net/http/httpheader/shared/set_range.rb)0
-rw-r--r--spec/ruby/library/net-http/httpheader/shared/size.rb (renamed from spec/ruby/library/net/http/httpheader/shared/size.rb)0
-rw-r--r--spec/ruby/library/net-http/httpheader/size_spec.rb8
-rw-r--r--spec/ruby/library/net-http/httpheader/sub_type_spec.rb32
-rw-r--r--spec/ruby/library/net-http/httpheader/to_hash_spec.rb25
-rw-r--r--spec/ruby/library/net-http/httpheader/type_params_spec.rb24
-rw-r--r--spec/ruby/library/net-http/httprequest/initialize_spec.rb45
-rw-r--r--spec/ruby/library/net-http/httpresponse/body_permitted_spec.rb13
-rw-r--r--spec/ruby/library/net-http/httpresponse/body_spec.rb7
-rw-r--r--spec/ruby/library/net-http/httpresponse/code_spec.rb24
-rw-r--r--spec/ruby/library/net-http/httpresponse/code_type_spec.rb24
-rw-r--r--spec/ruby/library/net-http/httpresponse/entity_spec.rb7
-rw-r--r--spec/ruby/library/net-http/httpresponse/error_spec.rb24
-rw-r--r--spec/ruby/library/net-http/httpresponse/error_type_spec.rb24
-rw-r--r--spec/ruby/library/net-http/httpresponse/exception_type_spec.rb13
-rw-r--r--spec/ruby/library/net-http/httpresponse/header_spec.rb9
-rw-r--r--spec/ruby/library/net-http/httpresponse/http_version_spec.rb12
-rw-r--r--spec/ruby/library/net-http/httpresponse/initialize_spec.rb11
-rw-r--r--spec/ruby/library/net-http/httpresponse/inspect_spec.rb15
-rw-r--r--spec/ruby/library/net-http/httpresponse/message_spec.rb9
-rw-r--r--spec/ruby/library/net-http/httpresponse/msg_spec.rb9
-rw-r--r--spec/ruby/library/net-http/httpresponse/read_body_spec.rb86
-rw-r--r--spec/ruby/library/net-http/httpresponse/read_header_spec.rb9
-rw-r--r--spec/ruby/library/net-http/httpresponse/read_new_spec.rb23
-rw-r--r--spec/ruby/library/net-http/httpresponse/reading_body_spec.rb58
-rw-r--r--spec/ruby/library/net-http/httpresponse/response_spec.rb9
-rw-r--r--spec/ruby/library/net-http/httpresponse/shared/body.rb (renamed from spec/ruby/library/net/http/httpresponse/shared/body.rb)0
-rw-r--r--spec/ruby/library/net-http/httpresponse/value_spec.rb24
-rw-r--r--spec/ruby/library/net/FTPError_spec.rb11
-rw-r--r--spec/ruby/library/net/FTPPermError_spec.rb15
-rw-r--r--spec/ruby/library/net/FTPProtoError_spec.rb15
-rw-r--r--spec/ruby/library/net/FTPReplyError_spec.rb15
-rw-r--r--spec/ruby/library/net/FTPTempError_spec.rb15
-rw-r--r--spec/ruby/library/net/ftp/abort_spec.rb65
-rw-r--r--spec/ruby/library/net/ftp/acct_spec.rb61
-rw-r--r--spec/ruby/library/net/ftp/binary_spec.rb27
-rw-r--r--spec/ruby/library/net/ftp/chdir_spec.rb102
-rw-r--r--spec/ruby/library/net/ftp/close_spec.rb33
-rw-r--r--spec/ruby/library/net/ftp/closed_spec.rb24
-rw-r--r--spec/ruby/library/net/ftp/connect_spec.rb52
-rw-r--r--spec/ruby/library/net/ftp/debug_mode_spec.rb26
-rw-r--r--spec/ruby/library/net/ftp/default_passive_spec.rb11
-rw-r--r--spec/ruby/library/net/ftp/delete_spec.rb62
-rw-r--r--spec/ruby/library/net/ftp/dir_spec.rb11
-rw-r--r--spec/ruby/library/net/ftp/get_spec.rb24
-rw-r--r--spec/ruby/library/net/ftp/getbinaryfile_spec.rb11
-rw-r--r--spec/ruby/library/net/ftp/getdir_spec.rb10
-rw-r--r--spec/ruby/library/net/ftp/gettextfile_spec.rb11
-rw-r--r--spec/ruby/library/net/ftp/help_spec.rb69
-rw-r--r--spec/ruby/library/net/ftp/initialize_spec.rb408
-rw-r--r--spec/ruby/library/net/ftp/last_response_code_spec.rb11
-rw-r--r--spec/ruby/library/net/ftp/last_response_spec.rb28
-rw-r--r--spec/ruby/library/net/ftp/lastresp_spec.rb11
-rw-r--r--spec/ruby/library/net/ftp/list_spec.rb11
-rw-r--r--spec/ruby/library/net/ftp/login_spec.rb198
-rw-r--r--spec/ruby/library/net/ftp/ls_spec.rb11
-rw-r--r--spec/ruby/library/net/ftp/mdtm_spec.rb41
-rw-r--r--spec/ruby/library/net/ftp/mkdir_spec.rb64
-rw-r--r--spec/ruby/library/net/ftp/mtime_spec.rb53
-rw-r--r--spec/ruby/library/net/ftp/nlst_spec.rb95
-rw-r--r--spec/ruby/library/net/ftp/noop_spec.rb41
-rw-r--r--spec/ruby/library/net/ftp/open_spec.rb58
-rw-r--r--spec/ruby/library/net/ftp/passive_spec.rb31
-rw-r--r--spec/ruby/library/net/ftp/put_spec.rb24
-rw-r--r--spec/ruby/library/net/ftp/putbinaryfile_spec.rb11
-rw-r--r--spec/ruby/library/net/ftp/puttextfile_spec.rb11
-rw-r--r--spec/ruby/library/net/ftp/pwd_spec.rb56
-rw-r--r--spec/ruby/library/net/ftp/quit_spec.rb36
-rw-r--r--spec/ruby/library/net/ftp/rename_spec.rb97
-rw-r--r--spec/ruby/library/net/ftp/resume_spec.rb26
-rw-r--r--spec/ruby/library/net/ftp/retrbinary_spec.rb33
-rw-r--r--spec/ruby/library/net/ftp/retrlines_spec.rb37
-rw-r--r--spec/ruby/library/net/ftp/return_code_spec.rb27
-rw-r--r--spec/ruby/library/net/ftp/rmdir_spec.rb61
-rw-r--r--spec/ruby/library/net/ftp/sendcmd_spec.rb57
-rw-r--r--spec/ruby/library/net/ftp/set_socket_spec.rb11
-rw-r--r--spec/ruby/library/net/ftp/shared/getbinaryfile.rb150
-rw-r--r--spec/ruby/library/net/ftp/shared/gettextfile.rb100
-rw-r--r--spec/ruby/library/net/ftp/shared/putbinaryfile.rb167
-rw-r--r--spec/ruby/library/net/ftp/shared/puttextfile.rb120
-rw-r--r--spec/ruby/library/net/ftp/site_spec.rb56
-rw-r--r--spec/ruby/library/net/ftp/size_spec.rb51
-rw-r--r--spec/ruby/library/net/ftp/status_spec.rb70
-rw-r--r--spec/ruby/library/net/ftp/storbinary_spec.rb51
-rw-r--r--spec/ruby/library/net/ftp/storlines_spec.rb46
-rw-r--r--spec/ruby/library/net/ftp/system_spec.rb51
-rw-r--r--spec/ruby/library/net/ftp/voidcmd_spec.rb57
-rw-r--r--spec/ruby/library/net/ftp/welcome_spec.rb28
-rw-r--r--spec/ruby/library/net/http/HTTPBadResponse_spec.rb8
-rw-r--r--spec/ruby/library/net/http/HTTPClientExcepton_spec.rb12
-rw-r--r--spec/ruby/library/net/http/HTTPError_spec.rb12
-rw-r--r--spec/ruby/library/net/http/HTTPFatalError_spec.rb12
-rw-r--r--spec/ruby/library/net/http/HTTPHeaderSyntaxError_spec.rb8
-rw-r--r--spec/ruby/library/net/http/HTTPRetriableError_spec.rb12
-rw-r--r--spec/ruby/library/net/http/HTTPServerException_spec.rb12
-rw-r--r--spec/ruby/library/net/http/http/Proxy_spec.rb35
-rw-r--r--spec/ruby/library/net/http/http/active_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/address_spec.rb9
-rw-r--r--spec/ruby/library/net/http/http/close_on_empty_response_spec.rb10
-rw-r--r--spec/ruby/library/net/http/http/copy_spec.rb21
-rw-r--r--spec/ruby/library/net/http/http/default_port_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/delete_spec.rb21
-rw-r--r--spec/ruby/library/net/http/http/finish_spec.rb29
-rw-r--r--spec/ruby/library/net/http/http/get2_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/get_print_spec.rb30
-rw-r--r--spec/ruby/library/net/http/http/get_response_spec.rb30
-rw-r--r--spec/ruby/library/net/http/http/get_spec.rb96
-rw-r--r--spec/ruby/library/net/http/http/head2_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/head_spec.rb25
-rw-r--r--spec/ruby/library/net/http/http/http_default_port_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/https_default_port_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/initialize_spec.rb46
-rw-r--r--spec/ruby/library/net/http/http/inspect_spec.rb24
-rw-r--r--spec/ruby/library/net/http/http/is_version_1_1_spec.rb7
-rw-r--r--spec/ruby/library/net/http/http/is_version_1_2_spec.rb7
-rw-r--r--spec/ruby/library/net/http/http/lock_spec.rb21
-rw-r--r--spec/ruby/library/net/http/http/mkcol_spec.rb21
-rw-r--r--spec/ruby/library/net/http/http/move_spec.rb25
-rw-r--r--spec/ruby/library/net/http/http/new_spec.rb86
-rw-r--r--spec/ruby/library/net/http/http/newobj_spec.rb48
-rw-r--r--spec/ruby/library/net/http/http/open_timeout_spec.rb24
-rw-r--r--spec/ruby/library/net/http/http/options_spec.rb25
-rw-r--r--spec/ruby/library/net/http/http/port_spec.rb9
-rw-r--r--spec/ruby/library/net/http/http/post2_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/post_form_spec.rb22
-rw-r--r--spec/ruby/library/net/http/http/post_spec.rb74
-rw-r--r--spec/ruby/library/net/http/http/propfind_spec.rb24
-rw-r--r--spec/ruby/library/net/http/http/proppatch_spec.rb24
-rw-r--r--spec/ruby/library/net/http/http/proxy_address_spec.rb31
-rw-r--r--spec/ruby/library/net/http/http/proxy_class_spec.rb9
-rw-r--r--spec/ruby/library/net/http/http/proxy_pass_spec.rb39
-rw-r--r--spec/ruby/library/net/http/http/proxy_port_spec.rb39
-rw-r--r--spec/ruby/library/net/http/http/proxy_user_spec.rb39
-rw-r--r--spec/ruby/library/net/http/http/put2_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/put_spec.rb24
-rw-r--r--spec/ruby/library/net/http/http/read_timeout_spec.rb24
-rw-r--r--spec/ruby/library/net/http/http/request_get_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/request_head_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/request_post_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/request_put_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/request_spec.rb109
-rw-r--r--spec/ruby/library/net/http/http/request_types_spec.rb254
-rw-r--r--spec/ruby/library/net/http/http/send_request_spec.rb61
-rw-r--r--spec/ruby/library/net/http/http/set_debug_output_spec.rb33
-rw-r--r--spec/ruby/library/net/http/http/socket_type_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/start_spec.rb111
-rw-r--r--spec/ruby/library/net/http/http/started_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/trace_spec.rb24
-rw-r--r--spec/ruby/library/net/http/http/unlock_spec.rb24
-rw-r--r--spec/ruby/library/net/http/http/use_ssl_spec.rb9
-rw-r--r--spec/ruby/library/net/http/http/version_1_1_spec.rb7
-rw-r--r--spec/ruby/library/net/http/http/version_1_2_spec.rb20
-rw-r--r--spec/ruby/library/net/http/httpexceptions/initialize_spec.rb17
-rw-r--r--spec/ruby/library/net/http/httpexceptions/response_spec.rb10
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/body_exist_spec.rb21
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/body_spec.rb30
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/body_stream_spec.rb32
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/exec_spec.rb131
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/inspect_spec.rb25
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/method_spec.rb15
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/path_spec.rb12
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/request_body_permitted_spec.rb12
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/response_body_permitted_spec.rb12
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/set_body_internal_spec.rb21
-rw-r--r--spec/ruby/library/net/http/httpheader/add_field_spec.rb31
-rw-r--r--spec/ruby/library/net/http/httpheader/basic_auth_spec.rb14
-rw-r--r--spec/ruby/library/net/http/httpheader/canonical_each_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/chunked_spec.rb22
-rw-r--r--spec/ruby/library/net/http/httpheader/content_length_spec.rb54
-rw-r--r--spec/ruby/library/net/http/httpheader/content_range_spec.rb32
-rw-r--r--spec/ruby/library/net/http/httpheader/content_type_spec.rb26
-rw-r--r--spec/ruby/library/net/http/httpheader/delete_spec.rb30
-rw-r--r--spec/ruby/library/net/http/httpheader/each_capitalized_name_spec.rb35
-rw-r--r--spec/ruby/library/net/http/httpheader/each_capitalized_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/each_header_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/each_key_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/each_name_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/each_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/each_value_spec.rb35
-rw-r--r--spec/ruby/library/net/http/httpheader/element_reference_spec.rb39
-rw-r--r--spec/ruby/library/net/http/httpheader/element_set_spec.rb41
-rw-r--r--spec/ruby/library/net/http/httpheader/fetch_spec.rb68
-rw-r--r--spec/ruby/library/net/http/httpheader/form_data_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/get_fields_spec.rb39
-rw-r--r--spec/ruby/library/net/http/httpheader/initialize_http_header_spec.rb21
-rw-r--r--spec/ruby/library/net/http/httpheader/key_spec.rb21
-rw-r--r--spec/ruby/library/net/http/httpheader/length_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/main_type_spec.rb24
-rw-r--r--spec/ruby/library/net/http/httpheader/proxy_basic_auth_spec.rb14
-rw-r--r--spec/ruby/library/net/http/httpheader/range_length_spec.rb32
-rw-r--r--spec/ruby/library/net/http/httpheader/range_spec.rb48
-rw-r--r--spec/ruby/library/net/http/httpheader/set_content_type_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/set_form_data_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/set_range_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/size_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/sub_type_spec.rb32
-rw-r--r--spec/ruby/library/net/http/httpheader/to_hash_spec.rb25
-rw-r--r--spec/ruby/library/net/http/httpheader/type_params_spec.rb24
-rw-r--r--spec/ruby/library/net/http/httprequest/initialize_spec.rb45
-rw-r--r--spec/ruby/library/net/http/httpresponse/body_permitted_spec.rb13
-rw-r--r--spec/ruby/library/net/http/httpresponse/body_spec.rb7
-rw-r--r--spec/ruby/library/net/http/httpresponse/code_spec.rb24
-rw-r--r--spec/ruby/library/net/http/httpresponse/code_type_spec.rb24
-rw-r--r--spec/ruby/library/net/http/httpresponse/entity_spec.rb7
-rw-r--r--spec/ruby/library/net/http/httpresponse/error_spec.rb24
-rw-r--r--spec/ruby/library/net/http/httpresponse/error_type_spec.rb24
-rw-r--r--spec/ruby/library/net/http/httpresponse/exception_type_spec.rb13
-rw-r--r--spec/ruby/library/net/http/httpresponse/header_spec.rb9
-rw-r--r--spec/ruby/library/net/http/httpresponse/http_version_spec.rb12
-rw-r--r--spec/ruby/library/net/http/httpresponse/initialize_spec.rb11
-rw-r--r--spec/ruby/library/net/http/httpresponse/inspect_spec.rb15
-rw-r--r--spec/ruby/library/net/http/httpresponse/message_spec.rb9
-rw-r--r--spec/ruby/library/net/http/httpresponse/msg_spec.rb9
-rw-r--r--spec/ruby/library/net/http/httpresponse/read_body_spec.rb86
-rw-r--r--spec/ruby/library/net/http/httpresponse/read_header_spec.rb9
-rw-r--r--spec/ruby/library/net/http/httpresponse/read_new_spec.rb23
-rw-r--r--spec/ruby/library/net/http/httpresponse/reading_body_spec.rb58
-rw-r--r--spec/ruby/library/net/http/httpresponse/response_spec.rb9
-rw-r--r--spec/ruby/library/net/http/httpresponse/value_spec.rb24
-rw-r--r--spec/ruby/library/objectspace/dump_all_spec.rb38
-rw-r--r--spec/ruby/library/objectspace/dump_spec.rb60
-rw-r--r--spec/ruby/library/objectspace/fixtures/trace.rb1
-rw-r--r--spec/ruby/library/objectspace/reachable_objects_from_spec.rb2
-rw-r--r--spec/ruby/library/objectspace/trace_spec.rb4
-rw-r--r--spec/ruby/library/openssl/config/freeze_spec.rb22
-rw-r--r--spec/ruby/library/openssl/digest/append_spec.rb6
-rw-r--r--spec/ruby/library/openssl/digest/block_length_spec.rb44
-rw-r--r--spec/ruby/library/openssl/digest/digest_length_spec.rb44
-rw-r--r--spec/ruby/library/openssl/digest/digest_spec.rb62
-rw-r--r--spec/ruby/library/openssl/digest/initialize_spec.rb141
-rw-r--r--spec/ruby/library/openssl/digest/name_spec.rb16
-rw-r--r--spec/ruby/library/openssl/digest/reset_spec.rb36
-rw-r--r--spec/ruby/library/openssl/digest/shared/update.rb123
-rw-r--r--spec/ruby/library/openssl/digest/update_spec.rb6
-rw-r--r--spec/ruby/library/openssl/digest_spec.rb63
-rw-r--r--spec/ruby/library/openssl/fixed_length_secure_compare_spec.rb42
-rw-r--r--spec/ruby/library/openssl/kdf/pbkdf2_hmac_spec.rb168
-rw-r--r--spec/ruby/library/openssl/kdf/scrypt_spec.rb209
-rw-r--r--spec/ruby/library/openssl/random/shared/random_bytes.rb2
-rw-r--r--spec/ruby/library/openssl/secure_compare_spec.rb38
-rw-r--r--spec/ruby/library/openssl/x509/name/verify_spec.rb78
-rw-r--r--spec/ruby/library/openssl/x509/store/verify_spec.rb78
-rw-r--r--spec/ruby/library/prime/each_spec.rb247
-rw-r--r--spec/ruby/library/prime/instance_spec.rb31
-rw-r--r--spec/ruby/library/prime/int_from_prime_division_spec.rb19
-rw-r--r--spec/ruby/library/prime/integer/each_prime_spec.rb19
-rw-r--r--spec/ruby/library/prime/integer/from_prime_division_spec.rb19
-rw-r--r--spec/ruby/library/prime/integer/prime_division_spec.rb31
-rw-r--r--spec/ruby/library/prime/integer/prime_spec.rb27
-rw-r--r--spec/ruby/library/prime/next_spec.rb11
-rw-r--r--spec/ruby/library/prime/prime_division_spec.rb37
-rw-r--r--spec/ruby/library/prime/prime_spec.rb27
-rw-r--r--spec/ruby/library/prime/succ_spec.rb11
-rw-r--r--spec/ruby/library/rexml/attribute/clone_spec.rb14
-rw-r--r--spec/ruby/library/rexml/attribute/element_spec.rb26
-rw-r--r--spec/ruby/library/rexml/attribute/equal_value_spec.rb21
-rw-r--r--spec/ruby/library/rexml/attribute/hash_spec.rb16
-rw-r--r--spec/ruby/library/rexml/attribute/initialize_spec.rb32
-rw-r--r--spec/ruby/library/rexml/attribute/inspect_spec.rb22
-rw-r--r--spec/ruby/library/rexml/attribute/namespace_spec.rb27
-rw-r--r--spec/ruby/library/rexml/attribute/node_type_spec.rb13
-rw-r--r--spec/ruby/library/rexml/attribute/prefix_spec.rb21
-rw-r--r--spec/ruby/library/rexml/attribute/remove_spec.rb23
-rw-r--r--spec/ruby/library/rexml/attribute/to_s_spec.rb17
-rw-r--r--spec/ruby/library/rexml/attribute/to_string_spec.rb17
-rw-r--r--spec/ruby/library/rexml/attribute/value_spec.rb17
-rw-r--r--spec/ruby/library/rexml/attribute/write_spec.rb26
-rw-r--r--spec/ruby/library/rexml/attribute/xpath_spec.rb22
-rw-r--r--spec/ruby/library/rexml/attributes/add_spec.rb10
-rw-r--r--spec/ruby/library/rexml/attributes/append_spec.rb10
-rw-r--r--spec/ruby/library/rexml/attributes/delete_all_spec.rb34
-rw-r--r--spec/ruby/library/rexml/attributes/delete_spec.rb30
-rw-r--r--spec/ruby/library/rexml/attributes/each_attribute_spec.rb25
-rw-r--r--spec/ruby/library/rexml/attributes/each_spec.rb26
-rw-r--r--spec/ruby/library/rexml/attributes/element_reference_spec.rb21
-rw-r--r--spec/ruby/library/rexml/attributes/element_set_spec.rb28
-rw-r--r--spec/ruby/library/rexml/attributes/get_attribute_ns_spec.rb17
-rw-r--r--spec/ruby/library/rexml/attributes/get_attribute_spec.rb32
-rw-r--r--spec/ruby/library/rexml/attributes/initialize_spec.rb21
-rw-r--r--spec/ruby/library/rexml/attributes/length_spec.rb10
-rw-r--r--spec/ruby/library/rexml/attributes/namespaces_spec.rb9
-rw-r--r--spec/ruby/library/rexml/attributes/prefixes_spec.rb27
-rw-r--r--spec/ruby/library/rexml/attributes/shared/add.rb17
-rw-r--r--spec/ruby/library/rexml/attributes/shared/length.rb13
-rw-r--r--spec/ruby/library/rexml/attributes/size_spec.rb10
-rw-r--r--spec/ruby/library/rexml/attributes/to_a_spec.rb22
-rw-r--r--spec/ruby/library/rexml/cdata/clone_spec.rb13
-rw-r--r--spec/ruby/library/rexml/cdata/initialize_spec.rb27
-rw-r--r--spec/ruby/library/rexml/cdata/shared/to_s.rb11
-rw-r--r--spec/ruby/library/rexml/cdata/to_s_spec.rb10
-rw-r--r--spec/ruby/library/rexml/cdata/value_spec.rb10
-rw-r--r--spec/ruby/library/rexml/document/add_element_spec.rb34
-rw-r--r--spec/ruby/library/rexml/document/add_spec.rb60
-rw-r--r--spec/ruby/library/rexml/document/clone_spec.rb23
-rw-r--r--spec/ruby/library/rexml/document/doctype_spec.rb18
-rw-r--r--spec/ruby/library/rexml/document/encoding_spec.rb25
-rw-r--r--spec/ruby/library/rexml/document/expanded_name_spec.rb19
-rw-r--r--spec/ruby/library/rexml/document/new_spec.rb39
-rw-r--r--spec/ruby/library/rexml/document/node_type_spec.rb11
-rw-r--r--spec/ruby/library/rexml/document/root_spec.rb15
-rw-r--r--spec/ruby/library/rexml/document/stand_alone_spec.rb22
-rw-r--r--spec/ruby/library/rexml/document/version_spec.rb17
-rw-r--r--spec/ruby/library/rexml/document/write_spec.rb38
-rw-r--r--spec/ruby/library/rexml/document/xml_decl_spec.rb18
-rw-r--r--spec/ruby/library/rexml/element/add_attribute_spec.rb44
-rw-r--r--spec/ruby/library/rexml/element/add_attributes_spec.rb25
-rw-r--r--spec/ruby/library/rexml/element/add_element_spec.rb41
-rw-r--r--spec/ruby/library/rexml/element/add_namespace_spec.rb26
-rw-r--r--spec/ruby/library/rexml/element/add_text_spec.rb27
-rw-r--r--spec/ruby/library/rexml/element/attribute_spec.rb20
-rw-r--r--spec/ruby/library/rexml/element/attributes_spec.rb22
-rw-r--r--spec/ruby/library/rexml/element/cdatas_spec.rb27
-rw-r--r--spec/ruby/library/rexml/element/clone_spec.rb32
-rw-r--r--spec/ruby/library/rexml/element/comments_spec.rb23
-rw-r--r--spec/ruby/library/rexml/element/delete_attribute_spec.rb42
-rw-r--r--spec/ruby/library/rexml/element/delete_element_spec.rb52
-rw-r--r--spec/ruby/library/rexml/element/delete_namespace_spec.rb28
-rw-r--r--spec/ruby/library/rexml/element/document_spec.rb19
-rw-r--r--spec/ruby/library/rexml/element/each_element_with_attribute_spec.rb38
-rw-r--r--spec/ruby/library/rexml/element/each_element_with_text_spec.rb34
-rw-r--r--spec/ruby/library/rexml/element/element_reference_spec.rb23
-rw-r--r--spec/ruby/library/rexml/element/get_text_spec.rb21
-rw-r--r--spec/ruby/library/rexml/element/has_attributes_spec.rb20
-rw-r--r--spec/ruby/library/rexml/element/has_elements_spec.rb21
-rw-r--r--spec/ruby/library/rexml/element/has_text_spec.rb19
-rw-r--r--spec/ruby/library/rexml/element/inspect_spec.rb30
-rw-r--r--spec/ruby/library/rexml/element/instructions_spec.rb24
-rw-r--r--spec/ruby/library/rexml/element/namespace_spec.rb30
-rw-r--r--spec/ruby/library/rexml/element/namespaces_spec.rb35
-rw-r--r--spec/ruby/library/rexml/element/new_spec.rb38
-rw-r--r--spec/ruby/library/rexml/element/next_element_spec.rb22
-rw-r--r--spec/ruby/library/rexml/element/node_type_spec.rb11
-rw-r--r--spec/ruby/library/rexml/element/prefixes_spec.rb26
-rw-r--r--spec/ruby/library/rexml/element/previous_element_spec.rb23
-rw-r--r--spec/ruby/library/rexml/element/raw_spec.rb27
-rw-r--r--spec/ruby/library/rexml/element/root_spec.rb31
-rw-r--r--spec/ruby/library/rexml/element/text_spec.rb49
-rw-r--r--spec/ruby/library/rexml/element/texts_spec.rb19
-rw-r--r--spec/ruby/library/rexml/element/whitespace_spec.rb26
-rw-r--r--spec/ruby/library/rexml/node/each_recursive_spec.rb24
-rw-r--r--spec/ruby/library/rexml/node/find_first_recursive_spec.rb28
-rw-r--r--spec/ruby/library/rexml/node/index_in_parent_spec.rb18
-rw-r--r--spec/ruby/library/rexml/node/next_sibling_node_spec.rb24
-rw-r--r--spec/ruby/library/rexml/node/parent_spec.rb23
-rw-r--r--spec/ruby/library/rexml/node/previous_sibling_node_spec.rb24
-rw-r--r--spec/ruby/library/rexml/shared/each_element.rb36
-rw-r--r--spec/ruby/library/rexml/shared/elements_to_a.rb34
-rw-r--r--spec/ruby/library/rexml/text/append_spec.rb13
-rw-r--r--spec/ruby/library/rexml/text/clone_spec.rb13
-rw-r--r--spec/ruby/library/rexml/text/comparison_spec.rb28
-rw-r--r--spec/ruby/library/rexml/text/empty_spec.rb15
-rw-r--r--spec/ruby/library/rexml/text/indent_text_spec.rb26
-rw-r--r--spec/ruby/library/rexml/text/inspect_spec.rb11
-rw-r--r--spec/ruby/library/rexml/text/new_spec.rb51
-rw-r--r--spec/ruby/library/rexml/text/node_type_spec.rb11
-rw-r--r--spec/ruby/library/rexml/text/normalize_spec.rb11
-rw-r--r--spec/ruby/library/rexml/text/read_with_substitution_spec.rb15
-rw-r--r--spec/ruby/library/rexml/text/to_s_spec.rb20
-rw-r--r--spec/ruby/library/rexml/text/unnormalize_spec.rb11
-rw-r--r--spec/ruby/library/rexml/text/value_spec.rb40
-rw-r--r--spec/ruby/library/rexml/text/wrap_spec.rb23
-rw-r--r--spec/ruby/library/rexml/text/write_with_substitution_spec.rb36
-rw-r--r--spec/ruby/library/set/compare_by_identity_spec.rb2
-rw-r--r--spec/ruby/library/set/comparison_spec.rb38
-rw-r--r--spec/ruby/library/set/divide_spec.rb35
-rw-r--r--spec/ruby/library/set/enumerable/to_set_spec.rb8
-rw-r--r--spec/ruby/library/set/flatten_spec.rb9
-rw-r--r--spec/ruby/library/set/initialize_clone_spec.rb22
-rw-r--r--spec/ruby/library/set/join_spec.rb42
-rw-r--r--spec/ruby/library/set/proper_subset_spec.rb9
-rw-r--r--spec/ruby/library/set/set_spec.rb12
-rw-r--r--spec/ruby/library/set/shared/inspect.rb12
-rw-r--r--spec/ruby/library/set/sortedset/add_spec.rb42
-rw-r--r--spec/ruby/library/set/sortedset/append_spec.rb10
-rw-r--r--spec/ruby/library/set/sortedset/case_equality_spec.rb10
-rw-r--r--spec/ruby/library/set/sortedset/classify_spec.rb30
-rw-r--r--spec/ruby/library/set/sortedset/clear_spec.rb20
-rw-r--r--spec/ruby/library/set/sortedset/collect_spec.rb10
-rw-r--r--spec/ruby/library/set/sortedset/constructor_spec.rb18
-rw-r--r--spec/ruby/library/set/sortedset/delete_if_spec.rb41
-rw-r--r--spec/ruby/library/set/sortedset/delete_spec.rb40
-rw-r--r--spec/ruby/library/set/sortedset/difference_spec.rb10
-rw-r--r--spec/ruby/library/set/sortedset/divide_spec.rb37
-rw-r--r--spec/ruby/library/set/sortedset/each_spec.rb29
-rw-r--r--spec/ruby/library/set/sortedset/empty_spec.rb13
-rw-r--r--spec/ruby/library/set/sortedset/eql_spec.rb19
-rw-r--r--spec/ruby/library/set/sortedset/equal_value_spec.rb16
-rw-r--r--spec/ruby/library/set/sortedset/exclusion_spec.rb21
-rw-r--r--spec/ruby/library/set/sortedset/filter_spec.rb10
-rw-r--r--spec/ruby/library/set/sortedset/flatten_merge_spec.rb11
-rw-r--r--spec/ruby/library/set/sortedset/flatten_spec.rb47
-rw-r--r--spec/ruby/library/set/sortedset/hash_spec.rb16
-rw-r--r--spec/ruby/library/set/sortedset/include_spec.rb10
-rw-r--r--spec/ruby/library/set/sortedset/initialize_spec.rb33
-rw-r--r--spec/ruby/library/set/sortedset/inspect_spec.rb13
-rw-r--r--spec/ruby/library/set/sortedset/intersection_spec.rb14
-rw-r--r--spec/ruby/library/set/sortedset/keep_if_spec.rb34
-rw-r--r--spec/ruby/library/set/sortedset/length_spec.rb10
-rw-r--r--spec/ruby/library/set/sortedset/map_spec.rb10
-rw-r--r--spec/ruby/library/set/sortedset/member_spec.rb10
-rw-r--r--spec/ruby/library/set/sortedset/merge_spec.rb22
-rw-r--r--spec/ruby/library/set/sortedset/minus_spec.rb10
-rw-r--r--spec/ruby/library/set/sortedset/plus_spec.rb10
-rw-r--r--spec/ruby/library/set/sortedset/pretty_print_cycle_spec.rb13
-rw-r--r--spec/ruby/library/set/sortedset/pretty_print_spec.rb20
-rw-r--r--spec/ruby/library/set/sortedset/proper_subset_spec.rb36
-rw-r--r--spec/ruby/library/set/sortedset/proper_superset_spec.rb36
-rw-r--r--spec/ruby/library/set/sortedset/reject_spec.rb45
-rw-r--r--spec/ruby/library/set/sortedset/replace_spec.rb20
-rw-r--r--spec/ruby/library/set/sortedset/select_spec.rb10
-rw-r--r--spec/ruby/library/set/sortedset/shared/add.rb14
-rw-r--r--spec/ruby/library/set/sortedset/shared/collect.rb20
-rw-r--r--spec/ruby/library/set/sortedset/shared/difference.rb15
-rw-r--r--spec/ruby/library/set/sortedset/shared/include.rb7
-rw-r--r--spec/ruby/library/set/sortedset/shared/intersection.rb15
-rw-r--r--spec/ruby/library/set/sortedset/shared/length.rb6
-rw-r--r--spec/ruby/library/set/sortedset/shared/select.rb35
-rw-r--r--spec/ruby/library/set/sortedset/shared/union.rb15
-rw-r--r--spec/ruby/library/set/sortedset/size_spec.rb10
-rw-r--r--spec/ruby/library/set/sortedset/sortedset_spec.rb24
-rw-r--r--spec/ruby/library/set/sortedset/subset_spec.rb36
-rw-r--r--spec/ruby/library/set/sortedset/subtract_spec.rb20
-rw-r--r--spec/ruby/library/set/sortedset/superset_spec.rb36
-rw-r--r--spec/ruby/library/set/sortedset/to_a_spec.rb20
-rw-r--r--spec/ruby/library/set/sortedset/union_spec.rb14
-rw-r--r--spec/ruby/library/set/subset_spec.rb9
-rw-r--r--spec/ruby/library/shellwords/shellwords_spec.rb15
-rw-r--r--spec/ruby/library/socket/addrinfo/shared/to_sockaddr.rb4
-rw-r--r--spec/ruby/library/socket/basicsocket/read_nonblock_spec.rb32
-rw-r--r--spec/ruby/library/socket/basicsocket/read_spec.rb47
-rw-r--r--spec/ruby/library/socket/basicsocket/recv_nonblock_spec.rb2
-rw-r--r--spec/ruby/library/socket/basicsocket/recv_spec.rb21
-rw-r--r--spec/ruby/library/socket/basicsocket/send_spec.rb8
-rw-r--r--spec/ruby/library/socket/basicsocket/shutdown_spec.rb20
-rw-r--r--spec/ruby/library/socket/fixtures/classes.rb2
-rw-r--r--spec/ruby/library/socket/ipsocket/getaddress_spec.rb2
-rw-r--r--spec/ruby/library/socket/shared/pack_sockaddr.rb3
-rw-r--r--spec/ruby/library/socket/shared/partially_closable_sockets.rb2
-rw-r--r--spec/ruby/library/socket/socket/getnameinfo_spec.rb2
-rw-r--r--spec/ruby/library/socket/socket/new_spec.rb2
-rw-r--r--spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb2
-rw-r--r--spec/ruby/library/socket/socket/pair_spec.rb2
-rw-r--r--spec/ruby/library/socket/socket/socketpair_spec.rb2
-rw-r--r--spec/ruby/library/socket/tcpserver/accept_spec.rb15
-rw-r--r--spec/ruby/library/socket/tcpserver/new_spec.rb6
-rw-r--r--spec/ruby/library/socket/tcpsocket/initialize_spec.rb34
-rw-r--r--spec/ruby/library/socket/tcpsocket/partially_closable_spec.rb2
-rw-r--r--spec/ruby/library/socket/tcpsocket/shared/new.rb16
-rw-r--r--spec/ruby/library/socket/udpsocket/initialize_spec.rb13
-rw-r--r--spec/ruby/library/socket/udpsocket/new_spec.rb6
-rw-r--r--spec/ruby/library/socket/udpsocket/send_spec.rb2
-rw-r--r--spec/ruby/library/socket/unixserver/accept_nonblock_spec.rb7
-rw-r--r--spec/ruby/library/socket/unixserver/accept_spec.rb13
-rw-r--r--spec/ruby/library/socket/unixserver/for_fd_spec.rb4
-rw-r--r--spec/ruby/library/socket/unixserver/new_spec.rb12
-rw-r--r--spec/ruby/library/socket/unixserver/open_spec.rb6
-rw-r--r--spec/ruby/library/socket/unixserver/shared/new.rb26
-rw-r--r--spec/ruby/library/socket/unixsocket/addr_spec.rb5
-rw-r--r--spec/ruby/library/socket/unixsocket/initialize_spec.rb10
-rw-r--r--spec/ruby/library/socket/unixsocket/inspect_spec.rb4
-rw-r--r--spec/ruby/library/socket/unixsocket/local_address_spec.rb2
-rw-r--r--spec/ruby/library/socket/unixsocket/new_spec.rb12
-rw-r--r--spec/ruby/library/socket/unixsocket/open_spec.rb10
-rw-r--r--spec/ruby/library/socket/unixsocket/pair_spec.rb7
-rw-r--r--spec/ruby/library/socket/unixsocket/partially_closable_spec.rb6
-rw-r--r--spec/ruby/library/socket/unixsocket/path_spec.rb6
-rw-r--r--spec/ruby/library/socket/unixsocket/peeraddr_spec.rb6
-rw-r--r--spec/ruby/library/socket/unixsocket/recv_io_spec.rb7
-rw-r--r--spec/ruby/library/socket/unixsocket/recvfrom_spec.rb7
-rw-r--r--spec/ruby/library/socket/unixsocket/send_io_spec.rb7
-rw-r--r--spec/ruby/library/socket/unixsocket/shared/new.rb28
-rw-r--r--spec/ruby/library/stringio/append_spec.rb15
-rw-r--r--spec/ruby/library/stringio/binmode_spec.rb2
-rw-r--r--spec/ruby/library/stringio/bytes_spec.rb29
-rw-r--r--spec/ruby/library/stringio/chars_spec.rb29
-rw-r--r--spec/ruby/library/stringio/close_read_spec.rb4
-rw-r--r--spec/ruby/library/stringio/close_write_spec.rb6
-rw-r--r--spec/ruby/library/stringio/closed_read_spec.rb2
-rw-r--r--spec/ruby/library/stringio/closed_spec.rb4
-rw-r--r--spec/ruby/library/stringio/closed_write_spec.rb2
-rw-r--r--spec/ruby/library/stringio/codepoints_spec.rb19
-rw-r--r--spec/ruby/library/stringio/flush_spec.rb2
-rw-r--r--spec/ruby/library/stringio/fsync_spec.rb2
-rw-r--r--spec/ruby/library/stringio/gets_spec.rb2
-rw-r--r--spec/ruby/library/stringio/initialize_spec.rb101
-rw-r--r--spec/ruby/library/stringio/lines_spec.rb53
-rw-r--r--spec/ruby/library/stringio/new_spec.rb6
-rw-r--r--spec/ruby/library/stringio/open_spec.rb60
-rw-r--r--spec/ruby/library/stringio/print_spec.rb8
-rw-r--r--spec/ruby/library/stringio/printf_spec.rb8
-rw-r--r--spec/ruby/library/stringio/putc_spec.rb10
-rw-r--r--spec/ruby/library/stringio/puts_spec.rb8
-rw-r--r--spec/ruby/library/stringio/read_nonblock_spec.rb4
-rw-r--r--spec/ruby/library/stringio/read_spec.rb2
-rw-r--r--spec/ruby/library/stringio/readline_spec.rb2
-rw-r--r--spec/ruby/library/stringio/readlines_spec.rb2
-rw-r--r--spec/ruby/library/stringio/readpartial_spec.rb6
-rw-r--r--spec/ruby/library/stringio/reopen_spec.rb68
-rw-r--r--spec/ruby/library/stringio/shared/codepoints.rb2
-rw-r--r--spec/ruby/library/stringio/shared/each.rb6
-rw-r--r--spec/ruby/library/stringio/shared/each_byte.rb2
-rw-r--r--spec/ruby/library/stringio/shared/each_char.rb2
-rw-r--r--spec/ruby/library/stringio/shared/getc.rb2
-rw-r--r--spec/ruby/library/stringio/shared/isatty.rb2
-rw-r--r--spec/ruby/library/stringio/shared/read.rb12
-rw-r--r--spec/ruby/library/stringio/shared/readchar.rb2
-rw-r--r--spec/ruby/library/stringio/shared/sysread.rb2
-rw-r--r--spec/ruby/library/stringio/shared/write.rb17
-rw-r--r--spec/ruby/library/stringio/truncate_spec.rb8
-rw-r--r--spec/ruby/library/stringio/ungetc_spec.rb10
-rw-r--r--spec/ruby/library/stringio/write_nonblock_spec.rb2
-rw-r--r--spec/ruby/library/stringscanner/getch_spec.rb2
-rw-r--r--spec/ruby/library/stringscanner/shared/concat.rb2
-rw-r--r--spec/ruby/library/stringscanner/string_spec.rb2
-rw-r--r--spec/ruby/library/time/to_datetime_spec.rb5
-rw-r--r--spec/ruby/library/uri/generic/host_spec.rb2
-rw-r--r--spec/ruby/library/uri/generic/to_s_spec.rb2
-rw-r--r--spec/ruby/library/yaml/dump_spec.rb14
-rw-r--r--spec/ruby/library/yaml/dump_stream_spec.rb3
-rw-r--r--spec/ruby/library/yaml/fixtures/common.rb4
-rw-r--r--spec/ruby/library/yaml/fixtures/strings.rb56
-rw-r--r--spec/ruby/library/yaml/load_file_spec.rb13
-rw-r--r--spec/ruby/library/yaml/load_stream_spec.rb3
-rw-r--r--spec/ruby/library/yaml/parse_file_spec.rb8
-rw-r--r--spec/ruby/library/yaml/parse_spec.rb7
-rw-r--r--spec/ruby/library/yaml/shared/each_document.rb5
-rw-r--r--spec/ruby/library/yaml/shared/load.rb12
-rw-r--r--spec/ruby/library/yaml/to_yaml_spec.rb20
-rw-r--r--spec/ruby/library/zlib/deflate/deflate_spec.rb4
-rw-r--r--spec/ruby/library/zlib/deflate/new_spec.rb1
-rw-r--r--spec/ruby/library/zlib/deflate/params_spec.rb2
-rw-r--r--spec/ruby/library/zlib/gzipreader/new_spec.rb1
-rw-r--r--spec/ruby/library/zlib/inflate/inflate_spec.rb8
-rw-r--r--spec/ruby/library/zlib/inflate/new_spec.rb1
-rw-r--r--spec/ruby/optional/capi/array_spec.rb34
-rw-r--r--spec/ruby/optional/capi/class_spec.rb32
-rw-r--r--spec/ruby/optional/capi/debug_spec.rb9
-rw-r--r--spec/ruby/optional/capi/encoding_spec.rb62
-rw-r--r--spec/ruby/optional/capi/exception_spec.rb34
-rw-r--r--spec/ruby/optional/capi/ext/array_spec.c28
-rw-r--r--spec/ruby/optional/capi/ext/class_spec.c8
-rw-r--r--spec/ruby/optional/capi/ext/debug_spec.c2
-rw-r--r--spec/ruby/optional/capi/ext/encoding_spec.c28
-rw-r--r--spec/ruby/optional/capi/ext/exception_spec.c19
-rw-r--r--spec/ruby/optional/capi/ext/gc_spec.c56
-rw-r--r--spec/ruby/optional/capi/ext/hash_spec.c9
-rw-r--r--spec/ruby/optional/capi/ext/integer_spec.c5
-rw-r--r--spec/ruby/optional/capi/ext/io_spec.c22
-rw-r--r--spec/ruby/optional/capi/ext/kernel_spec.c29
-rw-r--r--spec/ruby/optional/capi/ext/object_spec.c72
-rw-r--r--spec/ruby/optional/capi/ext/proc_spec.c15
-rw-r--r--spec/ruby/optional/capi/ext/range_spec.c2
-rw-r--r--spec/ruby/optional/capi/ext/rbasic_spec.c54
-rw-r--r--spec/ruby/optional/capi/ext/rubyspec.h23
-rw-r--r--spec/ruby/optional/capi/ext/string_spec.c53
-rw-r--r--spec/ruby/optional/capi/ext/struct_spec.c13
-rw-r--r--spec/ruby/optional/capi/ext/thread_spec.c8
-rw-r--r--spec/ruby/optional/capi/ext/tracepoint_spec.c2
-rw-r--r--spec/ruby/optional/capi/ext/typed_data_spec.c12
-rw-r--r--spec/ruby/optional/capi/ext/util_spec.c17
-rw-r--r--spec/ruby/optional/capi/file_spec.rb2
-rw-r--r--spec/ruby/optional/capi/fixtures/kernel.rb19
-rw-r--r--spec/ruby/optional/capi/gc_spec.rb31
-rw-r--r--spec/ruby/optional/capi/hash_spec.rb16
-rw-r--r--spec/ruby/optional/capi/integer_spec.rb17
-rw-r--r--spec/ruby/optional/capi/io_spec.rb26
-rw-r--r--spec/ruby/optional/capi/kernel_spec.rb170
-rw-r--r--spec/ruby/optional/capi/object_spec.rb12
-rw-r--r--spec/ruby/optional/capi/proc_spec.rb53
-rw-r--r--spec/ruby/optional/capi/rbasic_spec.rb2
-rw-r--r--spec/ruby/optional/capi/shared/rbasic.rb1
-rw-r--r--spec/ruby/optional/capi/string_spec.rb129
-rw-r--r--spec/ruby/optional/capi/typed_data_spec.rb12
-rw-r--r--spec/ruby/optional/capi/util_spec.rb95
-rw-r--r--spec/ruby/security/cve_2010_1330_spec.rb2
-rw-r--r--spec/ruby/security/cve_2014_8080_spec.rb34
-rw-r--r--spec/ruby/security/cve_2017_17742_spec.rb37
-rw-r--r--spec/ruby/security/cve_2019_8323_spec.rb14
-rw-r--r--spec/ruby/shared/file/exist.rb5
-rw-r--r--spec/ruby/shared/kernel/at_exit.rb5
-rw-r--r--spec/ruby/shared/kernel/object_id.rb28
-rw-r--r--spec/ruby/shared/kernel/raise.rb11
-rw-r--r--spec/ruby/shared/queue/deque.rb57
-rw-r--r--spec/ruby/shared/rational/coerce.rb34
-rw-r--r--spec/ruby/shared/rational/marshal_dump.rb5
-rw-r--r--spec/ruby/shared/rational/marshal_load.rb5
-rw-r--r--spec/ruby/shared/rational/quo.rb5
-rw-r--r--spec/ruby/shared/sizedqueue/enque.rb58
-rw-r--r--spec/ruby/shared/string/end_with.rb4
-rw-r--r--spec/ruby/shared/string/start_with.rb12
-rw-r--r--spec/ruby/shared/string/times.rb20
-rw-r--r--spec/ruby/shared/types/rb_num2dbl_fails.rb17
-rw-r--r--spec/ruby/spec_helper.rb2
-rw-r--r--spec/syntax_suggest/integration/ruby_command_line_spec.rb8
-rw-r--r--spec/syntax_suggest/integration/syntax_suggest_spec.rb16
-rw-r--r--spec/syntax_suggest/unit/api_spec.rb6
-rw-r--r--spec/syntax_suggest/unit/around_block_scan_spec.rb2
-rw-r--r--spec/syntax_suggest/unit/block_expand_spec.rb4
-rw-r--r--spec/syntax_suggest/unit/capture/before_after_keyword_ends_spec.rb4
-rw-r--r--spec/syntax_suggest/unit/capture/falling_indent_lines_spec.rb4
-rw-r--r--spec/syntax_suggest/unit/capture_code_context_spec.rb32
-rw-r--r--spec/syntax_suggest/unit/clean_document_spec.rb14
-rw-r--r--spec/syntax_suggest/unit/code_line_spec.rb12
-rw-r--r--spec/syntax_suggest/unit/code_search_spec.rb82
-rw-r--r--spec/syntax_suggest/unit/core_ext_spec.rb2
-rw-r--r--spec/syntax_suggest/unit/explain_syntax_spec.rb4
-rw-r--r--spec/syntax_suggest/unit/lex_all_spec.rb3
-rw-r--r--spec/syntax_suggest/unit/pathname_from_message_spec.rb9
-rw-r--r--spec/syntax_suggest/unit/scan_history_spec.rb6
-rw-r--r--st.c140
-rw-r--r--string.c1065
-rw-r--r--string.rb44
-rw-r--r--struct.c83
-rw-r--r--symbol.c140
-rw-r--r--symbol.h5
-rw-r--r--symbol.rb15
-rw-r--r--template/Doxyfile.tmpl359
-rw-r--r--template/GNUmakefile.in12
-rw-r--r--template/Makefile.in64
-rw-r--r--template/configure-ext.mk.tmpl2
-rw-r--r--template/encdb.h.tmpl36
-rw-r--r--template/extinit.c.tmpl2
-rw-r--r--template/exts.mk.tmpl9
-rw-r--r--template/fake.rb.in4
-rw-r--r--template/id.h.tmpl9
-rw-r--r--template/known_errors.inc.tmpl8
-rw-r--r--template/prelude.c.tmpl57
-rw-r--r--template/transdb.h.tmpl39
-rwxr-xr-xtemplate/unicode_properties.rdoc.tmpl59
-rw-r--r--test/-ext-/bug_reporter/test_bug_reporter.rb15
-rw-r--r--test/-ext-/debug/test_debug.rb2
-rw-r--r--test/-ext-/debug/test_profile_frames.rb67
-rw-r--r--test/-ext-/load/test_resolve_symbol.rb27
-rw-r--r--test/-ext-/load/test_stringify_symbols.rb35
-rw-r--r--test/-ext-/marshal/test_internal_ivar.rb2
-rw-r--r--test/-ext-/postponed_job/test_postponed_job.rb72
-rw-r--r--test/-ext-/string/test_capacity.rb4
-rw-r--r--test/-ext-/string/test_chilled.rb19
-rw-r--r--test/-ext-/string/test_fstring.rb20
-rw-r--r--test/-ext-/string/test_set_len.rb29
-rw-r--r--test/-ext-/string/test_too_many_dummy_encodings.rb2
-rw-r--r--test/-ext-/struct/test_data.rb18
-rw-r--r--test/-ext-/symbol/test_type.rb5
-rw-r--r--test/-ext-/test_bug-3571.rb4
-rw-r--r--test/-ext-/thread/helper.rb51
-rw-r--r--test/-ext-/thread/test_instrumentation_api.rb274
-rw-r--r--test/-ext-/thread/test_lock_native_thread.rb50
-rw-r--r--test/-ext-/tracepoint/test_tracepoint.rb1
-rw-r--r--test/.excludes-prism/TestAssignment.rb1
-rw-r--r--test/.excludes-prism/TestAssignmentGen.rb1
-rw-r--r--test/.excludes-prism/TestCall.rb3
-rw-r--r--test/.excludes-prism/TestCoverage.rb1
-rw-r--r--test/.excludes-prism/TestIRB/RubyLexTest.rb1
-rw-r--r--test/.excludes-prism/TestISeq.rb3
-rw-r--r--test/.excludes-prism/TestM17N.rb7
-rw-r--r--test/.excludes-prism/TestMixedUnicodeEscape.rb1
-rw-r--r--test/.excludes-prism/TestParse.rb28
-rw-r--r--test/.excludes-prism/TestPatternMatching.rb2
-rw-r--r--test/.excludes-prism/TestRegexp.rb6
-rw-r--r--test/.excludes-prism/TestRequire.rb1
-rw-r--r--test/.excludes-prism/TestRubyLiteral.rb5
-rw-r--r--test/.excludes-prism/TestRubyOptimization.rb1
-rw-r--r--test/.excludes-prism/TestRubyVM.rb1
-rw-r--r--test/.excludes-prism/TestSetTraceFunc.rb1
-rw-r--r--test/.excludes-prism/TestSyntax.rb29
-rw-r--r--test/.excludes-prism/TestUnicodeEscape.rb1
-rw-r--r--test/.excludes/TestArray.rb (renamed from test/excludes/TestArray.rb)0
-rw-r--r--test/.excludes/TestArraySubclass.rb (renamed from test/excludes/TestArraySubclass.rb)0
-rw-r--r--test/.excludes/TestException.rb (renamed from test/excludes/TestException.rb)0
-rw-r--r--test/.excludes/TestGem.rb (renamed from test/excludes/TestGem.rb)0
-rw-r--r--test/.excludes/TestIO_Console.rb (renamed from test/excludes/TestIO_Console.rb)0
-rw-r--r--test/.excludes/TestISeq.rb (renamed from test/excludes/TestISeq.rb)0
-rw-r--r--test/.excludes/TestThread.rb18
-rw-r--r--test/.excludes/TestThreadQueue.rb (renamed from test/excludes/TestThreadQueue.rb)0
-rw-r--r--test/.excludes/_appveyor/TestArray.rb (renamed from test/excludes/_appveyor/TestArray.rb)0
-rw-r--r--test/base64/test_base64.rb115
-rw-r--r--test/bigdecimal/helper.rb39
-rw-r--r--test/bigdecimal/test_bigdecimal.rb2300
-rw-r--r--test/bigdecimal/test_bigdecimal_util.rb141
-rw-r--r--test/bigdecimal/test_bigmath.rb81
-rw-r--r--test/bigdecimal/test_ractor.rb23
-rw-r--r--test/cgi/test_cgi_util.rb27
-rw-r--r--test/coverage/test_coverage.rb106
-rw-r--r--test/csv/helper.rb42
-rw-r--r--test/csv/interface/test_delegation.rb47
-rw-r--r--test/csv/interface/test_read.rb381
-rw-r--r--test/csv/interface/test_read_write.rb124
-rw-r--r--test/csv/interface/test_write.rb217
-rw-r--r--test/csv/line_endings.gzbin59 -> 0 bytes
-rw-r--r--test/csv/parse/test_column_separator.rb40
-rw-r--r--test/csv/parse/test_convert.rb165
-rw-r--r--test/csv/parse/test_each.rb23
-rw-r--r--test/csv/parse/test_general.rb348
-rw-r--r--test/csv/parse/test_header.rb342
-rw-r--r--test/csv/parse/test_inputs_scanner.rb63
-rw-r--r--test/csv/parse/test_invalid.rb52
-rw-r--r--test/csv/parse/test_liberal_parsing.rb171
-rw-r--r--test/csv/parse/test_quote_char_nil.rb93
-rw-r--r--test/csv/parse/test_read.rb27
-rw-r--r--test/csv/parse/test_rewind.rb40
-rw-r--r--test/csv/parse/test_row_separator.rb16
-rw-r--r--test/csv/parse/test_skip_lines.rb118
-rw-r--r--test/csv/parse/test_strip.rb112
-rw-r--r--test/csv/parse/test_unconverted_fields.rb117
-rw-r--r--test/csv/test_data_converters.rb190
-rw-r--r--test/csv/test_encodings.rb403
-rw-r--r--test/csv/test_features.rb359
-rw-r--r--test/csv/test_patterns.rb27
-rw-r--r--test/csv/test_row.rb435
-rw-r--r--test/csv/test_table.rb691
-rw-r--r--test/csv/write/test_converters.rb53
-rw-r--r--test/csv/write/test_force_quotes.rb78
-rw-r--r--test/csv/write/test_general.rb246
-rw-r--r--test/csv/write/test_quote_empty.rb70
-rw-r--r--test/date/test_date_parse.rb24
-rw-r--r--test/did_you_mean/core_ext/test_name_error_extension.rb2
-rw-r--r--test/drb/drbtest.rb396
-rw-r--r--test/drb/ignore_test_drb.rb14
-rw-r--r--test/drb/test_acl.rb207
-rw-r--r--test/drb/test_drb.rb371
-rw-r--r--test/drb/test_drbobject.rb69
-rw-r--r--test/drb/test_drbssl.rb80
-rw-r--r--test/drb/test_drbunix.rb60
-rw-r--r--test/drb/ut_array.rb17
-rw-r--r--test/drb/ut_array_drbssl.rb39
-rw-r--r--test/drb/ut_array_drbunix.rb17
-rw-r--r--test/drb/ut_drb.rb189
-rw-r--r--test/drb/ut_drb_drbssl.rb40
-rw-r--r--test/drb/ut_drb_drbunix.rb18
-rw-r--r--test/drb/ut_eq.rb37
-rw-r--r--test/drb/ut_large.rb62
-rw-r--r--test/drb/ut_port.rb16
-rw-r--r--test/drb/ut_safe1.rb17
-rw-r--r--test/drb/ut_timerholder.rb74
-rw-r--r--test/error_highlight/test_error_highlight.rb18
-rw-r--r--test/excludes/TestThread.rb14
-rw-r--r--test/fiber/scheduler.rb22
-rw-r--r--test/fiber/test_address_resolve.rb2
-rw-r--r--test/fiber/test_enumerator.rb8
-rw-r--r--test/fiber/test_io.rb15
-rw-r--r--test/fiber/test_mutex.rb2
-rw-r--r--test/fiber/test_process.rb27
-rw-r--r--test/fiber/test_queue.rb20
-rw-r--r--test/fiber/test_scheduler.rb2
-rw-r--r--test/fiber/test_thread.rb2
-rw-r--r--test/fiddle/test_c_struct_entry.rb8
-rw-r--r--test/fiddle/test_closure.rb12
-rw-r--r--test/fiddle/test_cparser.rb40
-rw-r--r--test/fiddle/test_func.rb17
-rw-r--r--test/fiddle/test_handle.rb3
-rw-r--r--test/fiddle/test_pointer.rb16
-rw-r--r--test/io/console/test_io_console.rb30
-rw-r--r--test/io/wait/test_io_wait.rb1
-rw-r--r--test/irb/command/test_custom_command.rb127
-rw-r--r--test/irb/command/test_force_exit.rb51
-rw-r--r--test/irb/command/test_help.rb75
-rw-r--r--test/irb/command/test_multi_irb_commands.rb50
-rw-r--r--test/irb/command/test_show_source.rb397
-rw-r--r--test/irb/helper.rb145
-rw-r--r--test/irb/test_cmd.rb947
-rw-r--r--test/irb/test_color.rb5
-rw-r--r--test/irb/test_color_printer.rb5
-rw-r--r--test/irb/test_command.rb971
-rw-r--r--test/irb/test_completion.rb277
-rw-r--r--test/irb/test_context.rb370
-rw-r--r--test/irb/test_debug_cmd.rb299
-rw-r--r--test/irb/test_debugger_integration.rb480
-rw-r--r--test/irb/test_eval_history.rb69
-rw-r--r--test/irb/test_evaluation.rb44
-rw-r--r--test/irb/test_helper_method.rb134
-rw-r--r--test/irb/test_history.rb410
-rw-r--r--test/irb/test_init.rb183
-rw-r--r--test/irb/test_input_method.rb116
-rw-r--r--test/irb/test_irb.rb826
-rw-r--r--test/irb/test_nesting_parser.rb341
-rw-r--r--test/irb/test_option.rb2
-rw-r--r--test/irb/test_raise_exception.rb74
-rw-r--r--test/irb/test_raise_no_backtrace_exception.rb56
-rw-r--r--test/irb/test_ruby_lex.rb886
-rw-r--r--test/irb/test_tracer.rb90
-rw-r--r--test/irb/test_type_completor.rb88
-rw-r--r--test/irb/test_workspace.rb5
-rw-r--r--test/irb/yamatanooroti/test_rendering.rb401
-rw-r--r--test/json/json_addition_test.rb6
-rw-r--r--test/json/json_common_interface_test.rb34
-rw-r--r--test/json/json_encoding_test.rb9
-rw-r--r--test/json/json_ext_parser_test.rb21
-rw-r--r--test/json/json_fixtures_test.rb2
-rwxr-xr-x[-rw-r--r--]test/json/json_generator_test.rb54
-rw-r--r--test/json/json_generic_object_test.rb4
-rw-r--r--test/json/json_parser_test.rb83
-rw-r--r--test/json/json_string_matching_test.rb2
-rw-r--r--test/json/ractor_test.rb8
-rw-r--r--test/lib/!Nothing_to_test.rb5
-rw-r--r--test/lib/jit_support.rb8
-rw-r--r--test/mkmf/test_config.rb27
-rw-r--r--test/monitor/test_monitor.rb2
-rw-r--r--test/net/fixtures/Makefile6
-rw-r--r--test/net/fixtures/cacert.pem44
-rw-r--r--test/net/fixtures/server.crt99
-rw-r--r--test/net/fixtures/server.key55
-rw-r--r--test/net/http/test_http.rb14
-rw-r--r--test/net/http/test_httpresponse.rb35
-rw-r--r--test/net/http/test_https.rb6
-rw-r--r--test/nkf/test_kconv.rb82
-rw-r--r--test/nkf/test_nkf.rb23
-rw-r--r--test/objspace/test_objspace.rb48
-rw-r--r--test/open-uri/test_open-uri.rb32
-rw-r--r--test/openssl/fixtures/pkey/dh1024.pem5
-rw-r--r--test/openssl/fixtures/pkey/dh2048_ffdhe2048.pem8
-rw-r--r--test/openssl/fixtures/pkey/dsa2048.pem15
-rw-r--r--test/openssl/fixtures/ssl/openssl_fips.cnf.tmpl19
-rw-r--r--test/openssl/test_asn1.rb8
-rw-r--r--test/openssl/test_bn.rb4
-rw-r--r--test/openssl/test_cipher.rb2
-rw-r--r--test/openssl/test_config.rb12
-rw-r--r--test/openssl/test_digest.rb22
-rw-r--r--test/openssl/test_engine.rb2
-rw-r--r--test/openssl/test_fips.rb12
-rw-r--r--test/openssl/test_ns_spki.rb6
-rw-r--r--test/openssl/test_ocsp.rb2
-rw-r--r--test/openssl/test_ossl.rb13
-rw-r--r--test/openssl/test_pair.rb11
-rw-r--r--test/openssl/test_pkcs12.rb4
-rw-r--r--test/openssl/test_pkey.rb74
-rw-r--r--test/openssl/test_pkey_dh.rb64
-rw-r--r--test/openssl/test_pkey_dsa.rb42
-rw-r--r--test/openssl/test_pkey_ec.rb29
-rw-r--r--test/openssl/test_pkey_rsa.rb2
-rw-r--r--test/openssl/test_provider.rb72
-rw-r--r--test/openssl/test_ssl.rb82
-rw-r--r--test/openssl/test_ssl_session.rb2
-rw-r--r--test/openssl/test_x509ext.rb37
-rw-r--r--test/openssl/test_x509req.rb7
-rw-r--r--test/openssl/utils.rb61
-rw-r--r--test/optparse/test_acceptable.rb5
-rw-r--r--test/optparse/test_optarg.rb16
-rw-r--r--test/optparse/test_optparse.rb88
-rw-r--r--test/optparse/test_placearg.rb20
-rw-r--r--test/optparse/test_reqarg.rb6
-rw-r--r--test/ostruct/test_ostruct.rb19
-rw-r--r--test/prism/attribute_write_test.rb60
-rw-r--r--test/prism/bom_test.rb59
-rw-r--r--test/prism/command_line_test.rb111
-rw-r--r--test/prism/comments_test.rb138
-rw-r--r--test/prism/compiler_test.rb31
-rw-r--r--test/prism/constant_path_node_test.rb91
-rw-r--r--test/prism/desugar_compiler_test.rb80
-rw-r--r--test/prism/dispatcher_test.rb46
-rw-r--r--test/prism/encoding_test.rb577
-rw-r--r--test/prism/errors_test.rb2241
-rw-r--r--test/prism/fixtures/alias.txt23
-rw-r--r--test/prism/fixtures/arithmetic.txt13
-rw-r--r--test/prism/fixtures/arrays.txt122
-rw-r--r--test/prism/fixtures/begin_ensure.txt21
-rw-r--r--test/prism/fixtures/begin_rescue.txt79
-rw-r--r--test/prism/fixtures/blocks.txt54
-rw-r--r--test/prism/fixtures/boolean_operators.txt5
-rw-r--r--test/prism/fixtures/booleans.txt3
-rw-r--r--test/prism/fixtures/break.txt25
-rw-r--r--test/prism/fixtures/case.txt55
-rw-r--r--test/prism/fixtures/classes.txt35
-rw-r--r--test/prism/fixtures/command_method_call.txt41
-rw-r--r--test/prism/fixtures/comments.txt24
-rw-r--r--test/prism/fixtures/constants.txt184
-rw-r--r--test/prism/fixtures/dash_heredocs.txt63
-rw-r--r--test/prism/fixtures/defined.txt10
-rw-r--r--test/prism/fixtures/dos_endings.txt20
-rw-r--r--test/prism/fixtures/dstring.txt29
-rw-r--r--test/prism/fixtures/dsym_str.txt2
-rw-r--r--test/prism/fixtures/embdoc_no_newline_at_end.txt2
-rw-r--r--test/prism/fixtures/emoji_method_calls.txt1
-rw-r--r--test/prism/fixtures/endless_methods.txt5
-rw-r--r--test/prism/fixtures/endless_range_in_conditional.txt3
-rw-r--r--test/prism/fixtures/for.txt19
-rw-r--r--test/prism/fixtures/global_variables.txt93
-rw-r--r--test/prism/fixtures/hashes.txt28
-rw-r--r--test/prism/fixtures/heredoc.txt2
-rw-r--r--test/prism/fixtures/heredoc_with_carriage_returns.txt2
-rw-r--r--test/prism/fixtures/heredoc_with_comment.txt3
-rw-r--r--test/prism/fixtures/heredoc_with_escaped_newline_at_start.txt7
-rw-r--r--test/prism/fixtures/heredoc_with_trailing_newline.txt2
-rw-r--r--test/prism/fixtures/heredocs_leading_whitespace.txt29
-rw-r--r--test/prism/fixtures/heredocs_nested.txt22
-rw-r--r--test/prism/fixtures/heredocs_with_ignored_newlines.txt14
-rw-r--r--test/prism/fixtures/heredocs_with_ignored_newlines_and_non_empty.txt4
-rw-r--r--test/prism/fixtures/if.txt42
-rw-r--r--test/prism/fixtures/indented_file_end.txt4
-rw-r--r--test/prism/fixtures/integer_operations.txt63
-rw-r--r--test/prism/fixtures/keyword_method_names.txt29
-rw-r--r--test/prism/fixtures/keywords.txt11
-rw-r--r--test/prism/fixtures/lambda.txt11
-rw-r--r--test/prism/fixtures/method_calls.txt156
-rw-r--r--test/prism/fixtures/methods.txt183
-rw-r--r--test/prism/fixtures/modules.txt18
-rw-r--r--test/prism/fixtures/multi_write.txt4
-rw-r--r--test/prism/fixtures/newline_terminated.txtbin0 -> 212 bytes
-rw-r--r--test/prism/fixtures/next.txt24
-rw-r--r--test/prism/fixtures/nils.txt13
-rw-r--r--test/prism/fixtures/non_alphanumeric_methods.txt105
-rw-r--r--test/prism/fixtures/not.txt37
-rw-r--r--test/prism/fixtures/numbers.txt67
-rw-r--r--test/prism/fixtures/patterns.txt217
-rw-r--r--test/prism/fixtures/procs.txt27
-rw-r--r--test/prism/fixtures/range_begin_open_exclusive.txt1
-rw-r--r--test/prism/fixtures/range_begin_open_inclusive.txt1
-rw-r--r--test/prism/fixtures/range_end_open_exclusive.txt1
-rw-r--r--test/prism/fixtures/range_end_open_inclusive.txt1
-rw-r--r--test/prism/fixtures/ranges.txt49
-rw-r--r--test/prism/fixtures/regex.txt46
-rw-r--r--test/prism/fixtures/regex_char_width.txt3
-rw-r--r--test/prism/fixtures/repeat_parameters.txt38
-rw-r--r--test/prism/fixtures/rescue.txt35
-rw-r--r--test/prism/fixtures/return.txt24
-rw-r--r--test/prism/fixtures/seattlerb/BEGIN.txt1
-rw-r--r--test/prism/fixtures/seattlerb/README.rdoc113
-rw-r--r--test/prism/fixtures/seattlerb/TestRubyParserShared.txt92
-rw-r--r--test/prism/fixtures/seattlerb/__ENCODING__.txt1
-rw-r--r--test/prism/fixtures/seattlerb/alias_gvar_backref.txt1
-rw-r--r--test/prism/fixtures/seattlerb/alias_resword.txt1
-rw-r--r--test/prism/fixtures/seattlerb/and_multi.txt3
-rw-r--r--test/prism/fixtures/seattlerb/aref_args_assocs.txt1
-rw-r--r--test/prism/fixtures/seattlerb/aref_args_lit_assocs.txt1
-rw-r--r--test/prism/fixtures/seattlerb/args_kw_block.txt1
-rw-r--r--test/prism/fixtures/seattlerb/array_line_breaks.txt4
-rw-r--r--test/prism/fixtures/seattlerb/array_lits_trailing_calls.txt3
-rw-r--r--test/prism/fixtures/seattlerb/assoc__bare.txt1
-rw-r--r--test/prism/fixtures/seattlerb/assoc_label.txt1
-rw-r--r--test/prism/fixtures/seattlerb/attr_asgn_colon_id.txt1
-rw-r--r--test/prism/fixtures/seattlerb/attrasgn_array_arg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/attrasgn_array_lhs.txt1
-rw-r--r--test/prism/fixtures/seattlerb/attrasgn_primary_dot_constant.txt1
-rw-r--r--test/prism/fixtures/seattlerb/backticks_interpolation_line.txt1
-rw-r--r--test/prism/fixtures/seattlerb/bang_eq.txt1
-rw-r--r--test/prism/fixtures/seattlerb/bdot2.txt3
-rw-r--r--test/prism/fixtures/seattlerb/bdot3.txt3
-rw-r--r--test/prism/fixtures/seattlerb/begin_ensure_no_bodies.txt3
-rw-r--r--test/prism/fixtures/seattlerb/begin_rescue_else_ensure_bodies.txt9
-rw-r--r--test/prism/fixtures/seattlerb/begin_rescue_else_ensure_no_bodies.txt9
-rw-r--r--test/prism/fixtures/seattlerb/begin_rescue_ensure_no_bodies.txt4
-rw-r--r--test/prism/fixtures/seattlerb/block_arg__bare.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_arg_kwsplat.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_arg_opt_arg_block.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_arg_opt_splat.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_arg_opt_splat_arg_block_omfg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_arg_optional.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_arg_scope.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_arg_scope2.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_arg_splat_arg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_args_kwargs.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_args_no_kwargs.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_args_opt1.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_args_opt2.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_args_opt2_2.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_args_opt3.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_call_defn_call_block_call.txt4
-rw-r--r--test/prism/fixtures/seattlerb/block_call_dot_op2_brace_block.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_call_dot_op2_cmd_args_do_block.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_call_operation_colon.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_call_operation_dot.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_call_paren_call_block_call.txt2
-rw-r--r--test/prism/fixtures/seattlerb/block_command_operation_colon.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_command_operation_dot.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_decomp_anon_splat_arg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_decomp_arg_splat.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_decomp_arg_splat_arg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_decomp_splat.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_kw.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_kw__required.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_kwarg_lvar.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_kwarg_lvar_multiple.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_opt_arg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_opt_splat.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_opt_splat_arg_block_omfg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_optarg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_paren_splat.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_reg_optarg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_return.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_scope.txt1
-rw-r--r--test/prism/fixtures/seattlerb/block_splat_reg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/bug169.txt1
-rw-r--r--test/prism/fixtures/seattlerb/bug179.txt1
-rw-r--r--test/prism/fixtures/seattlerb/bug190.txt1
-rw-r--r--test/prism/fixtures/seattlerb/bug191.txt3
-rw-r--r--test/prism/fixtures/seattlerb/bug202.txt2
-rw-r--r--test/prism/fixtures/seattlerb/bug236.txt3
-rw-r--r--test/prism/fixtures/seattlerb/bug290.txt3
-rw-r--r--test/prism/fixtures/seattlerb/bug_187.txt3
-rw-r--r--test/prism/fixtures/seattlerb/bug_215.txt1
-rw-r--r--test/prism/fixtures/seattlerb/bug_249.txt4
-rw-r--r--test/prism/fixtures/seattlerb/bug_and.txt4
-rw-r--r--test/prism/fixtures/seattlerb/bug_args__19.txt1
-rw-r--r--test/prism/fixtures/seattlerb/bug_args_masgn.txt1
-rw-r--r--test/prism/fixtures/seattlerb/bug_args_masgn2.txt1
-rw-r--r--test/prism/fixtures/seattlerb/bug_args_masgn_outer_parens__19.txt1
-rw-r--r--test/prism/fixtures/seattlerb/bug_call_arglist_parens.txt11
-rw-r--r--test/prism/fixtures/seattlerb/bug_case_when_regexp.txt1
-rw-r--r--test/prism/fixtures/seattlerb/bug_comma.txt1
-rw-r--r--test/prism/fixtures/seattlerb/bug_cond_pct.txt1
-rw-r--r--test/prism/fixtures/seattlerb/bug_hash_args.txt1
-rw-r--r--test/prism/fixtures/seattlerb/bug_hash_args_trailing_comma.txt1
-rw-r--r--test/prism/fixtures/seattlerb/bug_hash_interp_array.txt1
-rw-r--r--test/prism/fixtures/seattlerb/bug_masgn_right.txt1
-rw-r--r--test/prism/fixtures/seattlerb/bug_not_parens.txt1
-rw-r--r--test/prism/fixtures/seattlerb/bug_op_asgn_rescue.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_and.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_arg_assoc.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_arg_assoc_kwsplat.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_arg_kwsplat.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_args_assoc_quoted.txt5
-rw-r--r--test/prism/fixtures/seattlerb/call_args_assoc_trailing_comma.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_args_command.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_array_arg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_array_block_call.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_array_lambda_block_call.txt2
-rw-r--r--test/prism/fixtures/seattlerb/call_array_lit_inline_hash.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_assoc.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_assoc_new.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_assoc_new_if_multiline.txt5
-rw-r--r--test/prism/fixtures/seattlerb/call_assoc_trailing_comma.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_bang_command_call.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_bang_squiggle.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_begin_call_block_call.txt3
-rw-r--r--test/prism/fixtures/seattlerb/call_block_arg_named.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_carat.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_colon2.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_colon_parens.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_div.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_dot_parens.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_env.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_eq3.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_gt.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_kwsplat.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_leading_dots.txt3
-rw-r--r--test/prism/fixtures/seattlerb/call_leading_dots_comment.txt4
-rw-r--r--test/prism/fixtures/seattlerb/call_lt.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_lte.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_not.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_pipe.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_rshift.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_self_brackets.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_spaceship.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_stabby_do_end_with_block.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_stabby_with_braces_block.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_star.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_star2.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_trailing_comma.txt1
-rw-r--r--test/prism/fixtures/seattlerb/call_trailing_dots.txt3
-rw-r--r--test/prism/fixtures/seattlerb/call_unary_bang.txt1
-rw-r--r--test/prism/fixtures/seattlerb/case_in.txt111
-rw-r--r--test/prism/fixtures/seattlerb/case_in_31.txt4
-rw-r--r--test/prism/fixtures/seattlerb/case_in_37.txt4
-rw-r--r--test/prism/fixtures/seattlerb/case_in_42.txt3
-rw-r--r--test/prism/fixtures/seattlerb/case_in_42_2.txt3
-rw-r--r--test/prism/fixtures/seattlerb/case_in_47.txt4
-rw-r--r--test/prism/fixtures/seattlerb/case_in_67.txt3
-rw-r--r--test/prism/fixtures/seattlerb/case_in_86.txt3
-rw-r--r--test/prism/fixtures/seattlerb/case_in_86_2.txt3
-rw-r--r--test/prism/fixtures/seattlerb/case_in_array_pat_const.txt4
-rw-r--r--test/prism/fixtures/seattlerb/case_in_array_pat_const2.txt4
-rw-r--r--test/prism/fixtures/seattlerb/case_in_array_pat_paren_assign.txt4
-rw-r--r--test/prism/fixtures/seattlerb/case_in_const.txt4
-rw-r--r--test/prism/fixtures/seattlerb/case_in_else.txt7
-rw-r--r--test/prism/fixtures/seattlerb/case_in_find.txt3
-rw-r--r--test/prism/fixtures/seattlerb/case_in_find_array.txt3
-rw-r--r--test/prism/fixtures/seattlerb/case_in_hash_pat.txt5
-rw-r--r--test/prism/fixtures/seattlerb/case_in_hash_pat_assign.txt4
-rw-r--r--test/prism/fixtures/seattlerb/case_in_hash_pat_paren_assign.txt4
-rw-r--r--test/prism/fixtures/seattlerb/case_in_hash_pat_paren_true.txt5
-rw-r--r--test/prism/fixtures/seattlerb/case_in_hash_pat_rest.txt3
-rw-r--r--test/prism/fixtures/seattlerb/case_in_hash_pat_rest_solo.txt3
-rw-r--r--test/prism/fixtures/seattlerb/case_in_if_unless_post_mod.txt6
-rw-r--r--test/prism/fixtures/seattlerb/case_in_multiple.txt6
-rw-r--r--test/prism/fixtures/seattlerb/case_in_or.txt5
-rw-r--r--test/prism/fixtures/seattlerb/class_comments.txt9
-rw-r--r--test/prism/fixtures/seattlerb/cond_unary_minus.txt1
-rw-r--r--test/prism/fixtures/seattlerb/const_2_op_asgn_or2.txt1
-rw-r--r--test/prism/fixtures/seattlerb/const_3_op_asgn_or.txt1
-rw-r--r--test/prism/fixtures/seattlerb/const_op_asgn_and1.txt1
-rw-r--r--test/prism/fixtures/seattlerb/const_op_asgn_and2.txt1
-rw-r--r--test/prism/fixtures/seattlerb/const_op_asgn_or.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defined_eh_parens.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defn_arg_asplat_arg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defn_arg_forward_args.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defn_args_forward_args.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defn_comments.txt5
-rw-r--r--test/prism/fixtures/seattlerb/defn_endless_command.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defn_endless_command_rescue.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defn_forward_args.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defn_forward_args__no_parens.txt3
-rw-r--r--test/prism/fixtures/seattlerb/defn_kwarg_env.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defn_kwarg_kwarg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defn_kwarg_kwsplat.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defn_kwarg_kwsplat_anon.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defn_kwarg_lvar.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defn_kwarg_no_parens.txt2
-rw-r--r--test/prism/fixtures/seattlerb/defn_kwarg_val.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defn_no_kwargs.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defn_oneliner.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defn_oneliner_eq2.txt3
-rw-r--r--test/prism/fixtures/seattlerb/defn_oneliner_noargs.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defn_oneliner_noargs_parentheses.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defn_oneliner_rescue.txt13
-rw-r--r--test/prism/fixtures/seattlerb/defn_opt_last_arg.txt2
-rw-r--r--test/prism/fixtures/seattlerb/defn_opt_reg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defn_opt_splat_arg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defn_powarg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defn_reg_opt_reg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defn_splat_arg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defn_unary_not.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defns_reserved.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defs_as_arg_with_do_block_inside.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defs_comments.txt5
-rw-r--r--test/prism/fixtures/seattlerb/defs_endless_command.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defs_endless_command_rescue.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defs_kwarg.txt2
-rw-r--r--test/prism/fixtures/seattlerb/defs_oneliner.txt1
-rw-r--r--test/prism/fixtures/seattlerb/defs_oneliner_eq2.txt3
-rw-r--r--test/prism/fixtures/seattlerb/defs_oneliner_rescue.txt13
-rw-r--r--test/prism/fixtures/seattlerb/difficult0_.txt4
-rw-r--r--test/prism/fixtures/seattlerb/difficult1_line_numbers.txt13
-rw-r--r--test/prism/fixtures/seattlerb/difficult1_line_numbers2.txt8
-rw-r--r--test/prism/fixtures/seattlerb/difficult2_.txt2
-rw-r--r--test/prism/fixtures/seattlerb/difficult3_.txt1
-rw-r--r--test/prism/fixtures/seattlerb/difficult3_2.txt1
-rw-r--r--test/prism/fixtures/seattlerb/difficult3_3.txt1
-rw-r--r--test/prism/fixtures/seattlerb/difficult3_4.txt1
-rw-r--r--test/prism/fixtures/seattlerb/difficult3_5.txt1
-rw-r--r--test/prism/fixtures/seattlerb/difficult3__10.txt1
-rw-r--r--test/prism/fixtures/seattlerb/difficult3__11.txt1
-rw-r--r--test/prism/fixtures/seattlerb/difficult3__12.txt1
-rw-r--r--test/prism/fixtures/seattlerb/difficult3__6.txt1
-rw-r--r--test/prism/fixtures/seattlerb/difficult3__7.txt1
-rw-r--r--test/prism/fixtures/seattlerb/difficult3__8.txt1
-rw-r--r--test/prism/fixtures/seattlerb/difficult3__9.txt1
-rw-r--r--test/prism/fixtures/seattlerb/difficult4__leading_dots.txt2
-rw-r--r--test/prism/fixtures/seattlerb/difficult4__leading_dots2.txt2
-rw-r--r--test/prism/fixtures/seattlerb/difficult6_.txt1
-rw-r--r--test/prism/fixtures/seattlerb/difficult6__7.txt1
-rw-r--r--test/prism/fixtures/seattlerb/difficult6__8.txt1
-rw-r--r--test/prism/fixtures/seattlerb/difficult7_.txt5
-rw-r--r--test/prism/fixtures/seattlerb/do_bug.txt4
-rw-r--r--test/prism/fixtures/seattlerb/do_lambda.txt1
-rw-r--r--test/prism/fixtures/seattlerb/dot2_nil__26.txt1
-rw-r--r--test/prism/fixtures/seattlerb/dot3_nil__26.txt1
-rw-r--r--test/prism/fixtures/seattlerb/dstr_evstr.txt1
-rw-r--r--test/prism/fixtures/seattlerb/dstr_evstr_empty_end.txt1
-rw-r--r--test/prism/fixtures/seattlerb/dstr_lex_state.txt1
-rw-r--r--test/prism/fixtures/seattlerb/dstr_str.txt1
-rw-r--r--test/prism/fixtures/seattlerb/dsym_esc_to_sym.txt1
-rw-r--r--test/prism/fixtures/seattlerb/dsym_to_sym.txt3
-rw-r--r--test/prism/fixtures/seattlerb/eq_begin_line_numbers.txt6
-rw-r--r--test/prism/fixtures/seattlerb/eq_begin_why_wont_people_use_their_spacebar.txt3
-rw-r--r--test/prism/fixtures/seattlerb/evstr_evstr.txt1
-rw-r--r--test/prism/fixtures/seattlerb/evstr_str.txt1
-rw-r--r--test/prism/fixtures/seattlerb/expr_not_bang.txt1
-rw-r--r--test/prism/fixtures/seattlerb/f_kw.txt1
-rw-r--r--test/prism/fixtures/seattlerb/f_kw__required.txt1
-rw-r--r--test/prism/fixtures/seattlerb/flip2_env_lvar.txt1
-rw-r--r--test/prism/fixtures/seattlerb/float_with_if_modifier.txt1
-rw-r--r--test/prism/fixtures/seattlerb/heredoc__backslash_dos_format.txt5
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_backslash_nl.txt8
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_bad_hex_escape.txt3
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_bad_oct_escape.txt5
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_comma_arg.txt7
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_lineno.txt7
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_nested.txt7
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_squiggly.txt7
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_squiggly_blank_line_plus_interpolation.txt4
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_squiggly_blank_lines.txt7
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_squiggly_empty.txt2
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_squiggly_interp.txt5
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_squiggly_no_indent.txt3
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_squiggly_tabs.txt6
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_squiggly_tabs_extra.txt6
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_squiggly_visually_blank_lines.txt7
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_trailing_slash_continued_call.txt4
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_unicode.txt4
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_with_carriage_return_escapes.txt5
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_with_carriage_return_escapes_windows.txt5
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_with_extra_carriage_horrible_mix.txt4
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_with_extra_carriage_returns.txt5
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_with_extra_carriage_returns_windows.txt5
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_with_interpolation_and_carriage_return_escapes.txt4
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_with_interpolation_and_carriage_return_escapes_windows.txt4
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_with_not_global_interpolation.txt3
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_with_only_carriage_returns.txt6
-rw-r--r--test/prism/fixtures/seattlerb/heredoc_with_only_carriage_returns_windows.txt6
-rw-r--r--test/prism/fixtures/seattlerb/if_elsif.txt1
-rw-r--r--test/prism/fixtures/seattlerb/if_symbol.txt1
-rw-r--r--test/prism/fixtures/seattlerb/in_expr_no_case.txt1
-rw-r--r--test/prism/fixtures/seattlerb/index_0.txt1
-rw-r--r--test/prism/fixtures/seattlerb/index_0_opasgn.txt1
-rw-r--r--test/prism/fixtures/seattlerb/integer_with_if_modifier.txt1
-rw-r--r--test/prism/fixtures/seattlerb/interpolated_symbol_array_line_breaks.txt5
-rw-r--r--test/prism/fixtures/seattlerb/interpolated_word_array_line_breaks.txt5
-rw-r--r--test/prism/fixtures/seattlerb/iter_args_1.txt1
-rw-r--r--test/prism/fixtures/seattlerb/iter_args_10_1.txt1
-rw-r--r--test/prism/fixtures/seattlerb/iter_args_10_2.txt1
-rw-r--r--test/prism/fixtures/seattlerb/iter_args_11_1.txt1
-rw-r--r--test/prism/fixtures/seattlerb/iter_args_11_2.txt1
-rw-r--r--test/prism/fixtures/seattlerb/iter_args_2__19.txt1
-rw-r--r--test/prism/fixtures/seattlerb/iter_args_3.txt1
-rw-r--r--test/prism/fixtures/seattlerb/iter_args_4.txt1
-rw-r--r--test/prism/fixtures/seattlerb/iter_args_5.txt1
-rw-r--r--test/prism/fixtures/seattlerb/iter_args_6.txt1
-rw-r--r--test/prism/fixtures/seattlerb/iter_args_7_1.txt1
-rw-r--r--test/prism/fixtures/seattlerb/iter_args_7_2.txt1
-rw-r--r--test/prism/fixtures/seattlerb/iter_args_8_1.txt1
-rw-r--r--test/prism/fixtures/seattlerb/iter_args_8_2.txt1
-rw-r--r--test/prism/fixtures/seattlerb/iter_args_9_1.txt1
-rw-r--r--test/prism/fixtures/seattlerb/iter_args_9_2.txt1
-rw-r--r--test/prism/fixtures/seattlerb/iter_kwarg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/iter_kwarg_kwsplat.txt1
-rw-r--r--test/prism/fixtures/seattlerb/label_vs_string.txt2
-rw-r--r--test/prism/fixtures/seattlerb/lambda_do_vs_brace.txt7
-rw-r--r--test/prism/fixtures/seattlerb/lasgn_arg_rescue_arg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/lasgn_call_bracket_rescue_arg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/lasgn_call_nobracket_rescue_arg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/lasgn_command.txt1
-rw-r--r--test/prism/fixtures/seattlerb/lasgn_env.txt1
-rw-r--r--test/prism/fixtures/seattlerb/lasgn_ivar_env.txt1
-rw-r--r--test/prism/fixtures/seattlerb/lasgn_lasgn_command_call.txt1
-rw-r--r--test/prism/fixtures/seattlerb/lasgn_middle_splat.txt1
-rw-r--r--test/prism/fixtures/seattlerb/magic_encoding_comment.txt4
-rw-r--r--test/prism/fixtures/seattlerb/masgn_anon_splat_arg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/masgn_arg_colon_arg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/masgn_arg_ident.txt1
-rw-r--r--test/prism/fixtures/seattlerb/masgn_arg_splat_arg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/masgn_colon2.txt1
-rw-r--r--test/prism/fixtures/seattlerb/masgn_colon3.txt1
-rw-r--r--test/prism/fixtures/seattlerb/masgn_command_call.txt1
-rw-r--r--test/prism/fixtures/seattlerb/masgn_double_paren.txt1
-rw-r--r--test/prism/fixtures/seattlerb/masgn_lhs_splat.txt1
-rw-r--r--test/prism/fixtures/seattlerb/masgn_paren.txt1
-rw-r--r--test/prism/fixtures/seattlerb/masgn_splat_arg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/masgn_splat_arg_arg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/masgn_star.txt1
-rw-r--r--test/prism/fixtures/seattlerb/masgn_var_star_var.txt1
-rw-r--r--test/prism/fixtures/seattlerb/messy_op_asgn_lineno.txt1
-rw-r--r--test/prism/fixtures/seattlerb/method_call_assoc_trailing_comma.txt1
-rw-r--r--test/prism/fixtures/seattlerb/method_call_trailing_comma.txt1
-rw-r--r--test/prism/fixtures/seattlerb/mlhs_back_anonsplat.txt1
-rw-r--r--test/prism/fixtures/seattlerb/mlhs_back_splat.txt1
-rw-r--r--test/prism/fixtures/seattlerb/mlhs_front_anonsplat.txt1
-rw-r--r--test/prism/fixtures/seattlerb/mlhs_front_splat.txt1
-rw-r--r--test/prism/fixtures/seattlerb/mlhs_keyword.txt1
-rw-r--r--test/prism/fixtures/seattlerb/mlhs_mid_anonsplat.txt1
-rw-r--r--test/prism/fixtures/seattlerb/mlhs_mid_splat.txt1
-rw-r--r--test/prism/fixtures/seattlerb/mlhs_rescue.txt1
-rw-r--r--test/prism/fixtures/seattlerb/module_comments.txt10
-rw-r--r--test/prism/fixtures/seattlerb/multiline_hash_declaration.txt8
-rw-r--r--test/prism/fixtures/seattlerb/non_interpolated_symbol_array_line_breaks.txt5
-rw-r--r--test/prism/fixtures/seattlerb/non_interpolated_word_array_line_breaks.txt5
-rw-r--r--test/prism/fixtures/seattlerb/op_asgn_command_call.txt1
-rw-r--r--test/prism/fixtures/seattlerb/op_asgn_dot_ident_command_call.txt1
-rw-r--r--test/prism/fixtures/seattlerb/op_asgn_index_command_call.txt1
-rw-r--r--test/prism/fixtures/seattlerb/op_asgn_primary_colon_const_command_call.txt1
-rw-r--r--test/prism/fixtures/seattlerb/op_asgn_primary_colon_identifier1.txt1
-rw-r--r--test/prism/fixtures/seattlerb/op_asgn_primary_colon_identifier_command_call.txt1
-rw-r--r--test/prism/fixtures/seattlerb/op_asgn_val_dot_ident_command_call.txt1
-rw-r--r--test/prism/fixtures/seattlerb/parse_def_special_name.txt1
-rw-r--r--test/prism/fixtures/seattlerb/parse_if_not_canonical.txt2
-rw-r--r--test/prism/fixtures/seattlerb/parse_if_not_noncanonical.txt2
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_block.txt2
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_block_inline_comment.txt3
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_block_inline_comment_leading_newlines.txt7
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_block_inline_multiline_comment.txt4
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_call_ivar_arg_no_parens_line_break.txt2
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_call_ivar_line_break_paren.txt2
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_call_no_args.txt3
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_defn_complex.txt5
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_defn_no_parens.txt6
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_defn_no_parens_args.txt2
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_dot2.txt5
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_dot2_open.txt3
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_dot3.txt5
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_dot3_open.txt3
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_dstr_escaped_newline.txt3
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_dstr_soft_newline.txt4
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_evstr_after_break.txt2
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_hash_lit.txt3
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_heredoc.txt5
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_heredoc_evstr.txt4
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_heredoc_hardnewline.txt7
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_heredoc_regexp_chars.txt5
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_iter_call_no_parens.txt3
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_iter_call_parens.txt3
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_multiline_str.txt3
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_multiline_str_literal_n.txt2
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_newlines.txt3
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_op_asgn.txt4
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_postexe.txt3
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_preexe.txt3
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_rescue.txt8
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_return.txt6
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_str_with_newline_escape.txt1
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_to_ary.txt3
-rw-r--r--test/prism/fixtures/seattlerb/parse_line_trailing_newlines.txt2
-rw-r--r--test/prism/fixtures/seattlerb/parse_opt_call_args_assocs_comma.txt1
-rw-r--r--test/prism/fixtures/seattlerb/parse_opt_call_args_lit_comma.txt1
-rw-r--r--test/prism/fixtures/seattlerb/parse_pattern_019.txt5
-rw-r--r--test/prism/fixtures/seattlerb/parse_pattern_044.txt5
-rw-r--r--test/prism/fixtures/seattlerb/parse_pattern_051.txt5
-rw-r--r--test/prism/fixtures/seattlerb/parse_pattern_058.txt5
-rw-r--r--test/prism/fixtures/seattlerb/parse_pattern_058_2.txt5
-rw-r--r--test/prism/fixtures/seattlerb/parse_pattern_069.txt5
-rw-r--r--test/prism/fixtures/seattlerb/parse_pattern_076.txt5
-rw-r--r--test/prism/fixtures/seattlerb/parse_until_not_canonical.txt3
-rw-r--r--test/prism/fixtures/seattlerb/parse_until_not_noncanonical.txt3
-rw-r--r--test/prism/fixtures/seattlerb/parse_while_not_canonical.txt3
-rw-r--r--test/prism/fixtures/seattlerb/parse_while_not_noncanonical.txt3
-rw-r--r--test/prism/fixtures/seattlerb/pctW_lineno.txt5
-rw-r--r--test/prism/fixtures/seattlerb/pct_Q_backslash_nl.txt2
-rw-r--r--test/prism/fixtures/seattlerb/pct_nl.txt3
-rw-r--r--test/prism/fixtures/seattlerb/pct_w_heredoc_interp_nested.txt4
-rw-r--r--test/prism/fixtures/seattlerb/pipe_semicolon.txt1
-rw-r--r--test/prism/fixtures/seattlerb/pipe_space.txt1
-rw-r--r--test/prism/fixtures/seattlerb/qWords_space.txt1
-rw-r--r--test/prism/fixtures/seattlerb/qsymbols.txt1
-rw-r--r--test/prism/fixtures/seattlerb/qsymbols_empty.txt1
-rw-r--r--test/prism/fixtures/seattlerb/qsymbols_empty_space.txt1
-rw-r--r--test/prism/fixtures/seattlerb/qsymbols_interp.txt1
-rw-r--r--test/prism/fixtures/seattlerb/quoted_symbol_hash_arg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/quoted_symbol_keys.txt1
-rw-r--r--test/prism/fixtures/seattlerb/qw_escape.txt1
-rw-r--r--test/prism/fixtures/seattlerb/qw_escape_term.txt1
-rw-r--r--test/prism/fixtures/seattlerb/qwords_empty.txt1
-rw-r--r--test/prism/fixtures/seattlerb/read_escape_unicode_curlies.txt1
-rw-r--r--test/prism/fixtures/seattlerb/read_escape_unicode_h4.txt1
-rw-r--r--test/prism/fixtures/seattlerb/regexp.txt9
-rw-r--r--test/prism/fixtures/seattlerb/regexp_esc_C_slash.txt1
-rw-r--r--test/prism/fixtures/seattlerb/regexp_esc_u.txt1
-rw-r--r--test/prism/fixtures/seattlerb/regexp_escape_extended.txt1
-rw-r--r--test/prism/fixtures/seattlerb/regexp_unicode_curlies.txt3
-rw-r--r--test/prism/fixtures/seattlerb/required_kwarg_no_value.txt2
-rw-r--r--test/prism/fixtures/seattlerb/rescue_do_end_ensure_result.txt5
-rw-r--r--test/prism/fixtures/seattlerb/rescue_do_end_no_raise.txt9
-rw-r--r--test/prism/fixtures/seattlerb/rescue_do_end_raised.txt5
-rw-r--r--test/prism/fixtures/seattlerb/rescue_do_end_rescued.txt9
-rw-r--r--test/prism/fixtures/seattlerb/rescue_in_block.txt4
-rw-r--r--test/prism/fixtures/seattlerb/rescue_parens.txt1
-rw-r--r--test/prism/fixtures/seattlerb/return_call_assocs.txt11
-rw-r--r--test/prism/fixtures/seattlerb/rhs_asgn.txt1
-rw-r--r--test/prism/fixtures/seattlerb/ruby21_numbers.txt1
-rw-r--r--test/prism/fixtures/seattlerb/safe_attrasgn.txt1
-rw-r--r--test/prism/fixtures/seattlerb/safe_attrasgn_constant.txt1
-rw-r--r--test/prism/fixtures/seattlerb/safe_call.txt1
-rw-r--r--test/prism/fixtures/seattlerb/safe_call_after_newline.txt2
-rw-r--r--test/prism/fixtures/seattlerb/safe_call_dot_parens.txt1
-rw-r--r--test/prism/fixtures/seattlerb/safe_call_newline.txt2
-rw-r--r--test/prism/fixtures/seattlerb/safe_call_operator.txt1
-rw-r--r--test/prism/fixtures/seattlerb/safe_call_rhs_newline.txt2
-rw-r--r--test/prism/fixtures/seattlerb/safe_calls.txt1
-rw-r--r--test/prism/fixtures/seattlerb/safe_op_asgn.txt1
-rw-r--r--test/prism/fixtures/seattlerb/safe_op_asgn2.txt2
-rw-r--r--test/prism/fixtures/seattlerb/slashy_newlines_within_string.txt7
-rw-r--r--test/prism/fixtures/seattlerb/stabby_arg_no_paren.txt1
-rw-r--r--test/prism/fixtures/seattlerb/stabby_arg_opt_splat_arg_block_omfg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/stabby_block_iter_call.txt4
-rw-r--r--test/prism/fixtures/seattlerb/stabby_block_iter_call_no_target_with_arg.txt4
-rw-r--r--test/prism/fixtures/seattlerb/stabby_block_kw.txt1
-rw-r--r--test/prism/fixtures/seattlerb/stabby_block_kw__required.txt1
-rw-r--r--test/prism/fixtures/seattlerb/stabby_proc_scope.txt1
-rw-r--r--test/prism/fixtures/seattlerb/str_backslashes.txt1
-rw-r--r--test/prism/fixtures/seattlerb/str_double_double_escaped_newline.txt1
-rw-r--r--test/prism/fixtures/seattlerb/str_double_escaped_newline.txt1
-rw-r--r--test/prism/fixtures/seattlerb/str_double_newline.txt2
-rw-r--r--test/prism/fixtures/seattlerb/str_evstr.txt1
-rw-r--r--test/prism/fixtures/seattlerb/str_evstr_escape.txt1
-rw-r--r--test/prism/fixtures/seattlerb/str_heredoc_interp.txt5
-rw-r--r--test/prism/fixtures/seattlerb/str_interp_ternary_or_label.txt1
-rw-r--r--test/prism/fixtures/seattlerb/str_lit_concat_bad_encodings.txt2
-rw-r--r--test/prism/fixtures/seattlerb/str_newline_hash_line_number.txt2
-rw-r--r--test/prism/fixtures/seattlerb/str_pct_Q_nested.txt1
-rw-r--r--test/prism/fixtures/seattlerb/str_pct_nested_nested.txt1
-rw-r--r--test/prism/fixtures/seattlerb/str_pct_q.txt1
-rw-r--r--test/prism/fixtures/seattlerb/str_single_double_escaped_newline.txt1
-rw-r--r--test/prism/fixtures/seattlerb/str_single_escaped_newline.txt1
-rw-r--r--test/prism/fixtures/seattlerb/str_single_newline.txt2
-rw-r--r--test/prism/fixtures/seattlerb/str_str.txt1
-rw-r--r--test/prism/fixtures/seattlerb/str_str_str.txt1
-rw-r--r--test/prism/fixtures/seattlerb/super_arg.txt1
-rw-r--r--test/prism/fixtures/seattlerb/symbol_empty.txt1
-rw-r--r--test/prism/fixtures/seattlerb/symbol_list.txt1
-rw-r--r--test/prism/fixtures/seattlerb/symbols.txt1
-rw-r--r--test/prism/fixtures/seattlerb/symbols_empty.txt1
-rw-r--r--test/prism/fixtures/seattlerb/symbols_empty_space.txt1
-rw-r--r--test/prism/fixtures/seattlerb/symbols_interp.txt1
-rw-r--r--test/prism/fixtures/seattlerb/thingy.txt3
-rw-r--r--test/prism/fixtures/seattlerb/uminus_float.txt1
-rw-r--r--test/prism/fixtures/seattlerb/unary_minus.txt1
-rw-r--r--test/prism/fixtures/seattlerb/unary_plus.txt1
-rw-r--r--test/prism/fixtures/seattlerb/unary_plus_on_literal.txt1
-rw-r--r--test/prism/fixtures/seattlerb/unary_tilde.txt1
-rw-r--r--test/prism/fixtures/seattlerb/utf8_bom.txt3
-rw-r--r--test/prism/fixtures/seattlerb/when_splat.txt1
-rw-r--r--test/prism/fixtures/seattlerb/words_interp.txt1
-rw-r--r--test/prism/fixtures/single_method_call_with_bang.txt1
-rw-r--r--test/prism/fixtures/single_quote_heredocs.txt3
-rw-r--r--test/prism/fixtures/spanning_heredoc.txt63
-rw-r--r--test/prism/fixtures/spanning_heredoc_newlines.txt23
-rw-r--r--test/prism/fixtures/strings.txt105
-rw-r--r--test/prism/fixtures/super.txt17
-rw-r--r--test/prism/fixtures/symbols.txt93
-rw-r--r--test/prism/fixtures/ternary_operator.txt15
-rw-r--r--test/prism/fixtures/tilde_heredocs.txt97
-rw-r--r--test/prism/fixtures/undef.txt17
-rw-r--r--test/prism/fixtures/unescaping.txt9
-rw-r--r--test/prism/fixtures/unless.txt14
-rw-r--r--test/prism/fixtures/unparser/LICENSE20
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/alias.txt2
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/assignment.txt53
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/block.txt96
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/case.txt37
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/class.txt35
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/def.txt134
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/defined.txt3
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/defs.txt40
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/dstr.txt37
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/empty.txt0
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/empty_begin.txt1
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/flipflop.txt10
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/for.txt12
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/hookexe.txt7
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/if.txt36
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/kwbegin.txt80
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/lambda.txt13
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/literal.txt91
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/module.txt16
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/opasgn.txt24
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/pattern.txt41
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/pragma.txt4
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/range.txt4
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/rescue.txt3
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/send.txt84
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/since/27.txt4
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/since/30.txt4
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/since/31.txt7
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/since/32.txt11
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/singletons.txt4
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/super.txt21
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/unary.txt8
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/undef.txt2
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/variables.txt10
-rw-r--r--test/prism/fixtures/unparser/corpus/literal/while.txt73
-rw-r--r--test/prism/fixtures/unparser/corpus/semantic/and.txt8
-rw-r--r--test/prism/fixtures/unparser/corpus/semantic/block.txt26
-rw-r--r--test/prism/fixtures/unparser/corpus/semantic/def.txt7
-rw-r--r--test/prism/fixtures/unparser/corpus/semantic/dstr.txt127
-rw-r--r--test/prism/fixtures/unparser/corpus/semantic/kwbegin.txt42
-rw-r--r--test/prism/fixtures/unparser/corpus/semantic/literal.txt14
-rw-r--r--test/prism/fixtures/unparser/corpus/semantic/opasgn.txt1
-rw-r--r--test/prism/fixtures/unparser/corpus/semantic/send.txt6
-rw-r--r--test/prism/fixtures/unparser/corpus/semantic/undef.txt2
-rw-r--r--test/prism/fixtures/unparser/corpus/semantic/while.txt25
-rw-r--r--test/prism/fixtures/until.txt13
-rw-r--r--test/prism/fixtures/variables.txt47
-rw-r--r--test/prism/fixtures/while.txt23
-rw-r--r--test/prism/fixtures/whitequark/LICENSE25
-rw-r--r--test/prism/fixtures/whitequark/__ENCODING__.txt1
-rw-r--r--test/prism/fixtures/whitequark/__ENCODING___legacy_.txt1
-rw-r--r--test/prism/fixtures/whitequark/alias.txt1
-rw-r--r--test/prism/fixtures/whitequark/alias_gvar.txt3
-rw-r--r--test/prism/fixtures/whitequark/ambiuous_quoted_label_in_ternary_operator.txt1
-rw-r--r--test/prism/fixtures/whitequark/and.txt3
-rw-r--r--test/prism/fixtures/whitequark/and_asgn.txt3
-rw-r--r--test/prism/fixtures/whitequark/and_or_masgn.txt3
-rw-r--r--test/prism/fixtures/whitequark/anonymous_blockarg.txt1
-rw-r--r--test/prism/fixtures/whitequark/arg.txt3
-rw-r--r--test/prism/fixtures/whitequark/arg_duplicate_ignored.txt3
-rw-r--r--test/prism/fixtures/whitequark/arg_label.txt6
-rw-r--r--test/prism/fixtures/whitequark/arg_scope.txt1
-rw-r--r--test/prism/fixtures/whitequark/args.txt63
-rw-r--r--test/prism/fixtures/whitequark/args_args_assocs.txt3
-rw-r--r--test/prism/fixtures/whitequark/args_args_assocs_comma.txt1
-rw-r--r--test/prism/fixtures/whitequark/args_args_comma.txt1
-rw-r--r--test/prism/fixtures/whitequark/args_args_star.txt3
-rw-r--r--test/prism/fixtures/whitequark/args_assocs_comma.txt1
-rw-r--r--test/prism/fixtures/whitequark/args_block_pass.txt1
-rw-r--r--test/prism/fixtures/whitequark/args_cmd.txt1
-rw-r--r--test/prism/fixtures/whitequark/args_star.txt3
-rw-r--r--test/prism/fixtures/whitequark/array_assocs.txt3
-rw-r--r--test/prism/fixtures/whitequark/array_plain.txt1
-rw-r--r--test/prism/fixtures/whitequark/array_splat.txt5
-rw-r--r--test/prism/fixtures/whitequark/array_symbols.txt1
-rw-r--r--test/prism/fixtures/whitequark/array_symbols_empty.txt3
-rw-r--r--test/prism/fixtures/whitequark/array_symbols_interp.txt3
-rw-r--r--test/prism/fixtures/whitequark/array_words.txt1
-rw-r--r--test/prism/fixtures/whitequark/array_words_empty.txt3
-rw-r--r--test/prism/fixtures/whitequark/array_words_interp.txt3
-rw-r--r--test/prism/fixtures/whitequark/asgn_cmd.txt3
-rw-r--r--test/prism/fixtures/whitequark/asgn_mrhs.txt5
-rw-r--r--test/prism/fixtures/whitequark/back_ref.txt1
-rw-r--r--test/prism/fixtures/whitequark/bang.txt1
-rw-r--r--test/prism/fixtures/whitequark/bang_cmd.txt1
-rw-r--r--test/prism/fixtures/whitequark/begin_cmdarg.txt1
-rw-r--r--test/prism/fixtures/whitequark/beginless_erange_after_newline.txt2
-rw-r--r--test/prism/fixtures/whitequark/beginless_irange_after_newline.txt2
-rw-r--r--test/prism/fixtures/whitequark/beginless_range.txt3
-rw-r--r--test/prism/fixtures/whitequark/blockarg.txt1
-rw-r--r--test/prism/fixtures/whitequark/blockargs.txt71
-rw-r--r--test/prism/fixtures/whitequark/bug_435.txt1
-rw-r--r--test/prism/fixtures/whitequark/bug_447.txt3
-rw-r--r--test/prism/fixtures/whitequark/bug_452.txt1
-rw-r--r--test/prism/fixtures/whitequark/bug_466.txt1
-rw-r--r--test/prism/fixtures/whitequark/bug_473.txt1
-rw-r--r--test/prism/fixtures/whitequark/bug_480.txt1
-rw-r--r--test/prism/fixtures/whitequark/bug_481.txt1
-rw-r--r--test/prism/fixtures/whitequark/bug_ascii_8bit_in_literal.txt2
-rw-r--r--test/prism/fixtures/whitequark/bug_cmd_string_lookahead.txt1
-rw-r--r--test/prism/fixtures/whitequark/bug_cmdarg.txt5
-rw-r--r--test/prism/fixtures/whitequark/bug_def_no_paren_eql_begin.txt4
-rw-r--r--test/prism/fixtures/whitequark/bug_do_block_in_call_args.txt1
-rw-r--r--test/prism/fixtures/whitequark/bug_do_block_in_cmdarg.txt1
-rw-r--r--test/prism/fixtures/whitequark/bug_do_block_in_hash_brace.txt9
-rw-r--r--test/prism/fixtures/whitequark/bug_heredoc_do.txt3
-rw-r--r--test/prism/fixtures/whitequark/bug_interp_single.txt3
-rw-r--r--test/prism/fixtures/whitequark/bug_lambda_leakage.txt1
-rw-r--r--test/prism/fixtures/whitequark/bug_regex_verification.txt1
-rw-r--r--test/prism/fixtures/whitequark/bug_rescue_empty_else.txt1
-rw-r--r--test/prism/fixtures/whitequark/bug_while_not_parens_do.txt1
-rw-r--r--test/prism/fixtures/whitequark/case_cond.txt1
-rw-r--r--test/prism/fixtures/whitequark/case_cond_else.txt1
-rw-r--r--test/prism/fixtures/whitequark/case_expr.txt1
-rw-r--r--test/prism/fixtures/whitequark/case_expr_else.txt1
-rw-r--r--test/prism/fixtures/whitequark/casgn_scoped.txt1
-rw-r--r--test/prism/fixtures/whitequark/casgn_toplevel.txt1
-rw-r--r--test/prism/fixtures/whitequark/casgn_unscoped.txt1
-rw-r--r--test/prism/fixtures/whitequark/character.txt1
-rw-r--r--test/prism/fixtures/whitequark/class.txt3
-rw-r--r--test/prism/fixtures/whitequark/class_super.txt1
-rw-r--r--test/prism/fixtures/whitequark/class_super_label.txt1
-rw-r--r--test/prism/fixtures/whitequark/comments_before_leading_dot__27.txt19
-rw-r--r--test/prism/fixtures/whitequark/complex.txt7
-rw-r--r--test/prism/fixtures/whitequark/cond_begin.txt1
-rw-r--r--test/prism/fixtures/whitequark/cond_begin_masgn.txt1
-rw-r--r--test/prism/fixtures/whitequark/cond_eflipflop.txt3
-rw-r--r--test/prism/fixtures/whitequark/cond_eflipflop_with_beginless_range.txt1
-rw-r--r--test/prism/fixtures/whitequark/cond_eflipflop_with_endless_range.txt1
-rw-r--r--test/prism/fixtures/whitequark/cond_iflipflop.txt3
-rw-r--r--test/prism/fixtures/whitequark/cond_iflipflop_with_beginless_range.txt1
-rw-r--r--test/prism/fixtures/whitequark/cond_iflipflop_with_endless_range.txt1
-rw-r--r--test/prism/fixtures/whitequark/cond_match_current_line.txt3
-rw-r--r--test/prism/fixtures/whitequark/const_op_asgn.txt9
-rw-r--r--test/prism/fixtures/whitequark/const_scoped.txt1
-rw-r--r--test/prism/fixtures/whitequark/const_toplevel.txt1
-rw-r--r--test/prism/fixtures/whitequark/const_unscoped.txt1
-rw-r--r--test/prism/fixtures/whitequark/cpath.txt3
-rw-r--r--test/prism/fixtures/whitequark/cvar.txt1
-rw-r--r--test/prism/fixtures/whitequark/cvasgn.txt1
-rw-r--r--test/prism/fixtures/whitequark/dedenting_heredoc.txt75
-rw-r--r--test/prism/fixtures/whitequark/dedenting_interpolating_heredoc_fake_line_continuation.txt4
-rw-r--r--test/prism/fixtures/whitequark/dedenting_non_interpolating_heredoc_line_continuation.txt4
-rw-r--r--test/prism/fixtures/whitequark/def.txt11
-rw-r--r--test/prism/fixtures/whitequark/defined.txt5
-rw-r--r--test/prism/fixtures/whitequark/defs.txt9
-rw-r--r--test/prism/fixtures/whitequark/empty_stmt.txt1
-rw-r--r--test/prism/fixtures/whitequark/endless_comparison_method.txt11
-rw-r--r--test/prism/fixtures/whitequark/endless_method.txt7
-rw-r--r--test/prism/fixtures/whitequark/endless_method_command_syntax.txt15
-rw-r--r--test/prism/fixtures/whitequark/endless_method_forwarded_args_legacy.txt1
-rw-r--r--test/prism/fixtures/whitequark/endless_method_with_rescue_mod.txt3
-rw-r--r--test/prism/fixtures/whitequark/endless_method_without_args.txt7
-rw-r--r--test/prism/fixtures/whitequark/ensure.txt1
-rw-r--r--test/prism/fixtures/whitequark/ensure_empty.txt1
-rw-r--r--test/prism/fixtures/whitequark/false.txt1
-rw-r--r--test/prism/fixtures/whitequark/float.txt3
-rw-r--r--test/prism/fixtures/whitequark/for.txt3
-rw-r--r--test/prism/fixtures/whitequark/for_mlhs.txt1
-rw-r--r--test/prism/fixtures/whitequark/forward_arg.txt1
-rw-r--r--test/prism/fixtures/whitequark/forward_arg_with_open_args.txt27
-rw-r--r--test/prism/fixtures/whitequark/forward_args_legacy.txt5
-rw-r--r--test/prism/fixtures/whitequark/forwarded_argument_with_kwrestarg.txt1
-rw-r--r--test/prism/fixtures/whitequark/forwarded_argument_with_restarg.txt1
-rw-r--r--test/prism/fixtures/whitequark/forwarded_kwrestarg.txt1
-rw-r--r--test/prism/fixtures/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt1
-rw-r--r--test/prism/fixtures/whitequark/forwarded_restarg.txt1
-rw-r--r--test/prism/fixtures/whitequark/gvar.txt1
-rw-r--r--test/prism/fixtures/whitequark/gvasgn.txt1
-rw-r--r--test/prism/fixtures/whitequark/hash_empty.txt1
-rw-r--r--test/prism/fixtures/whitequark/hash_hashrocket.txt3
-rw-r--r--test/prism/fixtures/whitequark/hash_kwsplat.txt1
-rw-r--r--test/prism/fixtures/whitequark/hash_label.txt1
-rw-r--r--test/prism/fixtures/whitequark/hash_label_end.txt5
-rw-r--r--test/prism/fixtures/whitequark/hash_pair_value_omission.txt5
-rw-r--r--test/prism/fixtures/whitequark/heredoc.txt14
-rw-r--r--test/prism/fixtures/whitequark/if.txt3
-rw-r--r--test/prism/fixtures/whitequark/if_else.txt3
-rw-r--r--test/prism/fixtures/whitequark/if_elsif.txt1
-rw-r--r--test/prism/fixtures/whitequark/if_masgn__24.txt1
-rw-r--r--test/prism/fixtures/whitequark/if_mod.txt1
-rw-r--r--test/prism/fixtures/whitequark/if_nl_then.txt2
-rw-r--r--test/prism/fixtures/whitequark/int.txt5
-rw-r--r--test/prism/fixtures/whitequark/int___LINE__.txt1
-rw-r--r--test/prism/fixtures/whitequark/interp_digit_var.txt87
-rw-r--r--test/prism/fixtures/whitequark/ivar.txt1
-rw-r--r--test/prism/fixtures/whitequark/ivasgn.txt1
-rw-r--r--test/prism/fixtures/whitequark/keyword_argument_omission.txt1
-rw-r--r--test/prism/fixtures/whitequark/kwarg.txt1
-rw-r--r--test/prism/fixtures/whitequark/kwbegin_compstmt.txt1
-rw-r--r--test/prism/fixtures/whitequark/kwnilarg.txt5
-rw-r--r--test/prism/fixtures/whitequark/kwoptarg.txt1
-rw-r--r--test/prism/fixtures/whitequark/kwoptarg_with_kwrestarg_and_forwarded_args.txt1
-rw-r--r--test/prism/fixtures/whitequark/kwrestarg_named.txt1
-rw-r--r--test/prism/fixtures/whitequark/kwrestarg_unnamed.txt1
-rw-r--r--test/prism/fixtures/whitequark/lbrace_arg_after_command_args.txt1
-rw-r--r--test/prism/fixtures/whitequark/lparenarg_after_lvar__since_25.txt3
-rw-r--r--test/prism/fixtures/whitequark/lvar.txt1
-rw-r--r--test/prism/fixtures/whitequark/lvar_injecting_match.txt1
-rw-r--r--test/prism/fixtures/whitequark/lvasgn.txt1
-rw-r--r--test/prism/fixtures/whitequark/masgn.txt5
-rw-r--r--test/prism/fixtures/whitequark/masgn_attr.txt5
-rw-r--r--test/prism/fixtures/whitequark/masgn_cmd.txt1
-rw-r--r--test/prism/fixtures/whitequark/masgn_const.txt3
-rw-r--r--test/prism/fixtures/whitequark/masgn_nested.txt3
-rw-r--r--test/prism/fixtures/whitequark/masgn_splat.txt19
-rw-r--r--test/prism/fixtures/whitequark/module.txt1
-rw-r--r--test/prism/fixtures/whitequark/multiple_pattern_matches.txt5
-rw-r--r--test/prism/fixtures/whitequark/newline_in_hash_argument.txt14
-rw-r--r--test/prism/fixtures/whitequark/nil.txt1
-rw-r--r--test/prism/fixtures/whitequark/nil_expression.txt3
-rw-r--r--test/prism/fixtures/whitequark/non_lvar_injecting_match.txt1
-rw-r--r--test/prism/fixtures/whitequark/not.txt5
-rw-r--r--test/prism/fixtures/whitequark/not_cmd.txt1
-rw-r--r--test/prism/fixtures/whitequark/not_masgn__24.txt1
-rw-r--r--test/prism/fixtures/whitequark/nth_ref.txt1
-rw-r--r--test/prism/fixtures/whitequark/numbered_args_after_27.txt7
-rw-r--r--test/prism/fixtures/whitequark/numparam_outside_block.txt9
-rw-r--r--test/prism/fixtures/whitequark/numparam_ruby_bug_19025.txt1
-rw-r--r--test/prism/fixtures/whitequark/op_asgn.txt5
-rw-r--r--test/prism/fixtures/whitequark/op_asgn_cmd.txt7
-rw-r--r--test/prism/fixtures/whitequark/op_asgn_index.txt1
-rw-r--r--test/prism/fixtures/whitequark/op_asgn_index_cmd.txt1
-rw-r--r--test/prism/fixtures/whitequark/optarg.txt3
-rw-r--r--test/prism/fixtures/whitequark/or.txt3
-rw-r--r--test/prism/fixtures/whitequark/or_asgn.txt3
-rw-r--r--test/prism/fixtures/whitequark/parser_bug_272.txt1
-rw-r--r--test/prism/fixtures/whitequark/parser_bug_490.txt5
-rw-r--r--test/prism/fixtures/whitequark/parser_bug_507.txt1
-rw-r--r--test/prism/fixtures/whitequark/parser_bug_518.txt2
-rw-r--r--test/prism/fixtures/whitequark/parser_bug_525.txt1
-rw-r--r--test/prism/fixtures/whitequark/parser_bug_604.txt1
-rw-r--r--test/prism/fixtures/whitequark/parser_bug_640.txt4
-rw-r--r--test/prism/fixtures/whitequark/parser_bug_645.txt1
-rw-r--r--test/prism/fixtures/whitequark/parser_bug_830.txt1
-rw-r--r--test/prism/fixtures/whitequark/parser_bug_989.txt3
-rw-r--r--test/prism/fixtures/whitequark/parser_drops_truncated_parts_of_squiggly_heredoc.txt3
-rw-r--r--test/prism/fixtures/whitequark/parser_slash_slash_n_escaping_in_literals.txt62
-rw-r--r--test/prism/fixtures/whitequark/pattern_matching__FILE__LINE_literals.txt4
-rw-r--r--test/prism/fixtures/whitequark/pattern_matching_blank_else.txt1
-rw-r--r--test/prism/fixtures/whitequark/pattern_matching_else.txt1
-rw-r--r--test/prism/fixtures/whitequark/pattern_matching_single_line.txt3
-rw-r--r--test/prism/fixtures/whitequark/pattern_matching_single_line_allowed_omission_of_parentheses.txt11
-rw-r--r--test/prism/fixtures/whitequark/postexe.txt1
-rw-r--r--test/prism/fixtures/whitequark/preexe.txt1
-rw-r--r--test/prism/fixtures/whitequark/procarg0.txt3
-rw-r--r--test/prism/fixtures/whitequark/range_exclusive.txt1
-rw-r--r--test/prism/fixtures/whitequark/range_inclusive.txt1
-rw-r--r--test/prism/fixtures/whitequark/rational.txt3
-rw-r--r--test/prism/fixtures/whitequark/regex_interp.txt1
-rw-r--r--test/prism/fixtures/whitequark/regex_plain.txt1
-rw-r--r--test/prism/fixtures/whitequark/resbody_list.txt1
-rw-r--r--test/prism/fixtures/whitequark/resbody_list_mrhs.txt1
-rw-r--r--test/prism/fixtures/whitequark/resbody_list_var.txt1
-rw-r--r--test/prism/fixtures/whitequark/resbody_var.txt3
-rw-r--r--test/prism/fixtures/whitequark/rescue.txt1
-rw-r--r--test/prism/fixtures/whitequark/rescue_else.txt1
-rw-r--r--test/prism/fixtures/whitequark/rescue_else_ensure.txt1
-rw-r--r--test/prism/fixtures/whitequark/rescue_ensure.txt1
-rw-r--r--test/prism/fixtures/whitequark/rescue_in_lambda_block.txt1
-rw-r--r--test/prism/fixtures/whitequark/rescue_mod.txt1
-rw-r--r--test/prism/fixtures/whitequark/rescue_mod_asgn.txt1
-rw-r--r--test/prism/fixtures/whitequark/rescue_mod_masgn.txt1
-rw-r--r--test/prism/fixtures/whitequark/rescue_mod_op_assign.txt1
-rw-r--r--test/prism/fixtures/whitequark/rescue_without_begin_end.txt1
-rw-r--r--test/prism/fixtures/whitequark/restarg_named.txt1
-rw-r--r--test/prism/fixtures/whitequark/restarg_unnamed.txt1
-rw-r--r--test/prism/fixtures/whitequark/return.txt7
-rw-r--r--test/prism/fixtures/whitequark/return_block.txt1
-rw-r--r--test/prism/fixtures/whitequark/ruby_bug_10279.txt1
-rw-r--r--test/prism/fixtures/whitequark/ruby_bug_10653.txt5
-rw-r--r--test/prism/fixtures/whitequark/ruby_bug_11107.txt1
-rw-r--r--test/prism/fixtures/whitequark/ruby_bug_11380.txt1
-rw-r--r--test/prism/fixtures/whitequark/ruby_bug_11873.txt23
-rw-r--r--test/prism/fixtures/whitequark/ruby_bug_11873_a.txt39
-rw-r--r--test/prism/fixtures/whitequark/ruby_bug_11873_b.txt1
-rw-r--r--test/prism/fixtures/whitequark/ruby_bug_11989.txt3
-rw-r--r--test/prism/fixtures/whitequark/ruby_bug_11990.txt3
-rw-r--r--test/prism/fixtures/whitequark/ruby_bug_12073.txt3
-rw-r--r--test/prism/fixtures/whitequark/ruby_bug_12402.txt27
-rw-r--r--test/prism/fixtures/whitequark/ruby_bug_12669.txt7
-rw-r--r--test/prism/fixtures/whitequark/ruby_bug_12686.txt1
-rw-r--r--test/prism/fixtures/whitequark/ruby_bug_13547.txt1
-rw-r--r--test/prism/fixtures/whitequark/ruby_bug_14690.txt1
-rw-r--r--test/prism/fixtures/whitequark/ruby_bug_15789.txt3
-rw-r--r--test/prism/fixtures/whitequark/ruby_bug_9669.txt8
-rw-r--r--test/prism/fixtures/whitequark/sclass.txt1
-rw-r--r--test/prism/fixtures/whitequark/self.txt1
-rw-r--r--test/prism/fixtures/whitequark/send_attr_asgn.txt7
-rw-r--r--test/prism/fixtures/whitequark/send_attr_asgn_conditional.txt1
-rw-r--r--test/prism/fixtures/whitequark/send_binary_op.txt41
-rw-r--r--test/prism/fixtures/whitequark/send_block_chain_cmd.txt13
-rw-r--r--test/prism/fixtures/whitequark/send_block_conditional.txt1
-rw-r--r--test/prism/fixtures/whitequark/send_call.txt3
-rw-r--r--test/prism/fixtures/whitequark/send_conditional.txt1
-rw-r--r--test/prism/fixtures/whitequark/send_index.txt1
-rw-r--r--test/prism/fixtures/whitequark/send_index_asgn.txt1
-rw-r--r--test/prism/fixtures/whitequark/send_index_asgn_legacy.txt1
-rw-r--r--test/prism/fixtures/whitequark/send_index_cmd.txt1
-rw-r--r--test/prism/fixtures/whitequark/send_index_legacy.txt1
-rw-r--r--test/prism/fixtures/whitequark/send_lambda.txt5
-rw-r--r--test/prism/fixtures/whitequark/send_lambda_args.txt3
-rw-r--r--test/prism/fixtures/whitequark/send_lambda_args_noparen.txt3
-rw-r--r--test/prism/fixtures/whitequark/send_lambda_args_shadow.txt1
-rw-r--r--test/prism/fixtures/whitequark/send_lambda_legacy.txt1
-rw-r--r--test/prism/fixtures/whitequark/send_op_asgn_conditional.txt1
-rw-r--r--test/prism/fixtures/whitequark/send_plain.txt5
-rw-r--r--test/prism/fixtures/whitequark/send_plain_cmd.txt5
-rw-r--r--test/prism/fixtures/whitequark/send_self.txt5
-rw-r--r--test/prism/fixtures/whitequark/send_self_block.txt7
-rw-r--r--test/prism/fixtures/whitequark/send_unary_op.txt5
-rw-r--r--test/prism/fixtures/whitequark/slash_newline_in_heredocs.txt13
-rw-r--r--test/prism/fixtures/whitequark/space_args_arg.txt1
-rw-r--r--test/prism/fixtures/whitequark/space_args_arg_block.txt5
-rw-r--r--test/prism/fixtures/whitequark/space_args_arg_call.txt1
-rw-r--r--test/prism/fixtures/whitequark/space_args_arg_newline.txt2
-rw-r--r--test/prism/fixtures/whitequark/space_args_block.txt1
-rw-r--r--test/prism/fixtures/whitequark/space_args_cmd.txt1
-rw-r--r--test/prism/fixtures/whitequark/string___FILE__.txt1
-rw-r--r--test/prism/fixtures/whitequark/string_concat.txt1
-rw-r--r--test/prism/fixtures/whitequark/string_dvar.txt1
-rw-r--r--test/prism/fixtures/whitequark/string_interp.txt1
-rw-r--r--test/prism/fixtures/whitequark/string_plain.txt3
-rw-r--r--test/prism/fixtures/whitequark/super.txt5
-rw-r--r--test/prism/fixtures/whitequark/super_block.txt3
-rw-r--r--test/prism/fixtures/whitequark/symbol_interp.txt1
-rw-r--r--test/prism/fixtures/whitequark/symbol_plain.txt3
-rw-r--r--test/prism/fixtures/whitequark/ternary.txt1
-rw-r--r--test/prism/fixtures/whitequark/ternary_ambiguous_symbol.txt1
-rw-r--r--test/prism/fixtures/whitequark/trailing_forward_arg.txt1
-rw-r--r--test/prism/fixtures/whitequark/true.txt1
-rw-r--r--test/prism/fixtures/whitequark/unary_num_pow_precedence.txt5
-rw-r--r--test/prism/fixtures/whitequark/undef.txt1
-rw-r--r--test/prism/fixtures/whitequark/unless.txt3
-rw-r--r--test/prism/fixtures/whitequark/unless_else.txt3
-rw-r--r--test/prism/fixtures/whitequark/unless_mod.txt1
-rw-r--r--test/prism/fixtures/whitequark/until.txt3
-rw-r--r--test/prism/fixtures/whitequark/until_mod.txt1
-rw-r--r--test/prism/fixtures/whitequark/until_post.txt1
-rw-r--r--test/prism/fixtures/whitequark/var_and_asgn.txt1
-rw-r--r--test/prism/fixtures/whitequark/var_op_asgn.txt7
-rw-r--r--test/prism/fixtures/whitequark/var_op_asgn_cmd.txt1
-rw-r--r--test/prism/fixtures/whitequark/var_or_asgn.txt1
-rw-r--r--test/prism/fixtures/whitequark/when_multi.txt1
-rw-r--r--test/prism/fixtures/whitequark/when_splat.txt1
-rw-r--r--test/prism/fixtures/whitequark/when_then.txt1
-rw-r--r--test/prism/fixtures/whitequark/while.txt3
-rw-r--r--test/prism/fixtures/whitequark/while_mod.txt1
-rw-r--r--test/prism/fixtures/whitequark/while_post.txt1
-rw-r--r--test/prism/fixtures/whitequark/xstring_interp.txt1
-rw-r--r--test/prism/fixtures/whitequark/xstring_plain.txt1
-rw-r--r--test/prism/fixtures/whitequark/zsuper.txt1
-rw-r--r--test/prism/fixtures/xstring.txt13
-rw-r--r--test/prism/fixtures/xstring_with_backslash.txt1
-rw-r--r--test/prism/fixtures/yield.txt7
-rw-r--r--test/prism/format_errors_test.rb24
-rw-r--r--test/prism/fuzzer_test.rb67
-rw-r--r--test/prism/heredoc_dedent_test.rb27
-rw-r--r--test/prism/index_write_test.rb89
-rw-r--r--test/prism/integer_parse_test.rb45
-rw-r--r--test/prism/library_symbols_test.rb106
-rw-r--r--test/prism/locals_test.rb58
-rw-r--r--test/prism/location_test.rb947
-rw-r--r--test/prism/magic_comment_test.rb33
-rw-r--r--test/prism/memsize_test.rb17
-rw-r--r--test/prism/newline_test.rb93
-rw-r--r--test/prism/parameters_signature_test.rb93
-rw-r--r--test/prism/parse_comments_test.rb21
-rw-r--r--test/prism/parse_stream_test.rb74
-rw-r--r--test/prism/parse_test.rb371
-rw-r--r--test/prism/parser_test.rb186
-rw-r--r--test/prism/pattern_test.rb132
-rw-r--r--test/prism/redundant_return_test.rb73
-rw-r--r--test/prism/reflection_test.rb22
-rw-r--r--test/prism/regexp_test.rb263
-rw-r--r--test/prism/ripper_test.rb85
-rw-r--r--test/prism/ruby_api_test.rb275
-rw-r--r--test/prism/ruby_parser_test.rb144
-rw-r--r--test/prism/snapshots/alias.txt194
-rw-r--r--test/prism/snapshots/arithmetic.txt255
-rw-r--r--test/prism/snapshots/arrays.txt1837
-rw-r--r--test/prism/snapshots/begin_ensure.txt251
-rw-r--r--test/prism/snapshots/begin_rescue.txt699
-rw-r--r--test/prism/snapshots/blocks.txt774
-rw-r--r--test/prism/snapshots/boolean_operators.txt54
-rw-r--r--test/prism/snapshots/booleans.txt7
-rw-r--r--test/prism/snapshots/break.txt401
-rw-r--r--test/prism/snapshots/case.txt495
-rw-r--r--test/prism/snapshots/classes.txt365
-rw-r--r--test/prism/snapshots/command_method_call.txt755
-rw-r--r--test/prism/snapshots/comments.txt145
-rw-r--r--test/prism/snapshots/constants.txt1242
-rw-r--r--test/prism/snapshots/dash_heredocs.txt260
-rw-r--r--test/prism/snapshots/defined.txt88
-rw-r--r--test/prism/snapshots/dos_endings.txt110
-rw-r--r--test/prism/snapshots/dstring.txt85
-rw-r--r--test/prism/snapshots/dsym_str.txt11
-rw-r--r--test/prism/snapshots/embdoc_no_newline_at_end.txt5
-rw-r--r--test/prism/snapshots/emoji_method_calls.txt31
-rw-r--r--test/prism/snapshots/endless_methods.txt107
-rw-r--r--test/prism/snapshots/endless_range_in_conditional.txt53
-rw-r--r--test/prism/snapshots/for.txt188
-rw-r--r--test/prism/snapshots/global_variables.txt191
-rw-r--r--test/prism/snapshots/hashes.txt384
-rw-r--r--test/prism/snapshots/heredoc.txt11
-rw-r--r--test/prism/snapshots/heredoc_with_carriage_returns.txt11
-rw-r--r--test/prism/snapshots/heredoc_with_comment.txt21
-rw-r--r--test/prism/snapshots/heredoc_with_escaped_newline_at_start.txt67
-rw-r--r--test/prism/snapshots/heredoc_with_trailing_newline.txt11
-rw-r--r--test/prism/snapshots/heredocs_leading_whitespace.txt63
-rw-r--r--test/prism/snapshots/heredocs_nested.txt94
-rw-r--r--test/prism/snapshots/heredocs_with_ignored_newlines.txt70
-rw-r--r--test/prism/snapshots/heredocs_with_ignored_newlines_and_non_empty.txt11
-rw-r--r--test/prism/snapshots/if.txt531
-rw-r--r--test/prism/snapshots/indented_file_end.txt18
-rw-r--r--test/prism/snapshots/integer_operations.txt635
-rw-r--r--test/prism/snapshots/keyword_method_names.txt173
-rw-r--r--test/prism/snapshots/keywords.txt47
-rw-r--r--test/prism/snapshots/lambda.txt202
-rw-r--r--test/prism/snapshots/method_calls.txt2459
-rw-r--r--test/prism/snapshots/methods.txt2054
-rw-r--r--test/prism/snapshots/modules.txt184
-rw-r--r--test/prism/snapshots/multi_write.txt93
-rw-r--r--test/prism/snapshots/newline_terminated.txt107
-rw-r--r--test/prism/snapshots/next.txt329
-rw-r--r--test/prism/snapshots/nils.txt34
-rw-r--r--test/prism/snapshots/non_alphanumeric_methods.txt503
-rw-r--r--test/prism/snapshots/not.txt351
-rw-r--r--test/prism/snapshots/numbers.txt142
-rw-r--r--test/prism/snapshots/patterns.txt4921
-rw-r--r--test/prism/snapshots/procs.txt403
-rw-r--r--test/prism/snapshots/range_begin_open_exclusive.txt13
-rw-r--r--test/prism/snapshots/range_begin_open_inclusive.txt13
-rw-r--r--test/prism/snapshots/range_end_open_exclusive.txt13
-rw-r--r--test/prism/snapshots/range_end_open_inclusive.txt13
-rw-r--r--test/prism/snapshots/ranges.txt533
-rw-r--r--test/prism/snapshots/regex.txt510
-rw-r--r--test/prism/snapshots/regex_char_width.txt50
-rw-r--r--test/prism/snapshots/repeat_parameters.txt473
-rw-r--r--test/prism/snapshots/rescue.txt528
-rw-r--r--test/prism/snapshots/return.txt165
-rw-r--r--test/prism/snapshots/seattlerb/BEGIN.txt15
-rw-r--r--test/prism/snapshots/seattlerb/TestRubyParserShared.txt361
-rw-r--r--test/prism/snapshots/seattlerb/__ENCODING__.txt6
-rw-r--r--test/prism/snapshots/seattlerb/alias_gvar_backref.txt13
-rw-r--r--test/prism/snapshots/seattlerb/alias_resword.txt21
-rw-r--r--test/prism/snapshots/seattlerb/and_multi.txt26
-rw-r--r--test/prism/snapshots/seattlerb/aref_args_assocs.txt23
-rw-r--r--test/prism/snapshots/seattlerb/aref_args_lit_assocs.txt26
-rw-r--r--test/prism/snapshots/seattlerb/args_kw_block.txt39
-rw-r--r--test/prism/snapshots/seattlerb/array_line_breaks.txt25
-rw-r--r--test/prism/snapshots/seattlerb/array_lits_trailing_calls.txt35
-rw-r--r--test/prism/snapshots/seattlerb/assoc__bare.txt31
-rw-r--r--test/prism/snapshots/seattlerb/assoc_label.txt34
-rw-r--r--test/prism/snapshots/seattlerb/attr_asgn_colon_id.txt23
-rw-r--r--test/prism/snapshots/seattlerb/attrasgn_array_arg.txt42
-rw-r--r--test/prism/snapshots/seattlerb/attrasgn_array_lhs.txt83
-rw-r--r--test/prism/snapshots/seattlerb/attrasgn_primary_dot_constant.txt31
-rw-r--r--test/prism/snapshots/seattlerb/backticks_interpolation_line.txt38
-rw-r--r--test/prism/snapshots/seattlerb/bang_eq.txt24
-rw-r--r--test/prism/snapshots/seattlerb/bdot2.txt38
-rw-r--r--test/prism/snapshots/seattlerb/bdot3.txt38
-rw-r--r--test/prism/snapshots/seattlerb/begin_ensure_no_bodies.txt16
-rw-r--r--test/prism/snapshots/seattlerb/begin_rescue_else_ensure_bodies.txt47
-rw-r--r--test/prism/snapshots/seattlerb/begin_rescue_else_ensure_no_bodies.txt27
-rw-r--r--test/prism/snapshots/seattlerb/begin_rescue_ensure_no_bodies.txt23
-rw-r--r--test/prism/snapshots/seattlerb/block_arg__bare.txt31
-rw-r--r--test/prism/snapshots/seattlerb/block_arg_kwsplat.txt39
-rw-r--r--test/prism/snapshots/seattlerb/block_arg_opt_arg_block.txt54
-rw-r--r--test/prism/snapshots/seattlerb/block_arg_opt_splat.txt51
-rw-r--r--test/prism/snapshots/seattlerb/block_arg_opt_splat_arg_block_omfg.txt59
-rw-r--r--test/prism/snapshots/seattlerb/block_arg_optional.txt43
-rw-r--r--test/prism/snapshots/seattlerb/block_arg_scope.txt40
-rw-r--r--test/prism/snapshots/seattlerb/block_arg_scope2.txt43
-rw-r--r--test/prism/snapshots/seattlerb/block_arg_splat_arg.txt45
-rw-r--r--test/prism/snapshots/seattlerb/block_args_kwargs.txt44
-rw-r--r--test/prism/snapshots/seattlerb/block_args_no_kwargs.txt37
-rw-r--r--test/prism/snapshots/seattlerb/block_args_opt1.txt59
-rw-r--r--test/prism/snapshots/seattlerb/block_args_opt2.txt52
-rw-r--r--test/prism/snapshots/seattlerb/block_args_opt2_2.txt71
-rw-r--r--test/prism/snapshots/seattlerb/block_args_opt3.txt79
-rw-r--r--test/prism/snapshots/seattlerb/block_call_defn_call_block_call.txt80
-rw-r--r--test/prism/snapshots/seattlerb/block_call_dot_op2_brace_block.txt100
-rw-r--r--test/prism/snapshots/seattlerb/block_call_dot_op2_cmd_args_do_block.txt113
-rw-r--r--test/prism/snapshots/seattlerb/block_call_operation_colon.txt54
-rw-r--r--test/prism/snapshots/seattlerb/block_call_operation_dot.txt54
-rw-r--r--test/prism/snapshots/seattlerb/block_call_paren_call_block_call.txt60
-rw-r--r--test/prism/snapshots/seattlerb/block_command_operation_colon.txt49
-rw-r--r--test/prism/snapshots/seattlerb/block_command_operation_dot.txt49
-rw-r--r--test/prism/snapshots/seattlerb/block_decomp_anon_splat_arg.txt46
-rw-r--r--test/prism/snapshots/seattlerb/block_decomp_arg_splat.txt46
-rw-r--r--test/prism/snapshots/seattlerb/block_decomp_arg_splat_arg.txt52
-rw-r--r--test/prism/snapshots/seattlerb/block_decomp_splat.txt46
-rw-r--r--test/prism/snapshots/seattlerb/block_kw.txt42
-rw-r--r--test/prism/snapshots/seattlerb/block_kw__required.txt38
-rw-r--r--test/prism/snapshots/seattlerb/block_kwarg_lvar.txt50
-rw-r--r--test/prism/snapshots/seattlerb/block_kwarg_lvar_multiple.txt61
-rw-r--r--test/prism/snapshots/seattlerb/block_opt_arg.txt46
-rw-r--r--test/prism/snapshots/seattlerb/block_opt_splat.txt48
-rw-r--r--test/prism/snapshots/seattlerb/block_opt_splat_arg_block_omfg.txt56
-rw-r--r--test/prism/snapshots/seattlerb/block_optarg.txt46
-rw-r--r--test/prism/snapshots/seattlerb/block_paren_splat.txt49
-rw-r--r--test/prism/snapshots/seattlerb/block_reg_optarg.txt49
-rw-r--r--test/prism/snapshots/seattlerb/block_return.txt57
-rw-r--r--test/prism/snapshots/seattlerb/block_scope.txt29
-rw-r--r--test/prism/snapshots/seattlerb/block_splat_reg.txt42
-rw-r--r--test/prism/snapshots/seattlerb/bug169.txt28
-rw-r--r--test/prism/snapshots/seattlerb/bug179.txt28
-rw-r--r--test/prism/snapshots/seattlerb/bug190.txt11
-rw-r--r--test/prism/snapshots/seattlerb/bug191.txt87
-rw-r--r--test/prism/snapshots/seattlerb/bug202.txt22
-rw-r--r--test/prism/snapshots/seattlerb/bug236.txt70
-rw-r--r--test/prism/snapshots/seattlerb/bug290.txt24
-rw-r--r--test/prism/snapshots/seattlerb/bug_187.txt59
-rw-r--r--test/prism/snapshots/seattlerb/bug_215.txt14
-rw-r--r--test/prism/snapshots/seattlerb/bug_249.txt86
-rw-r--r--test/prism/snapshots/seattlerb/bug_and.txt21
-rw-r--r--test/prism/snapshots/seattlerb/bug_args__19.txt58
-rw-r--r--test/prism/snapshots/seattlerb/bug_args_masgn.txt49
-rw-r--r--test/prism/snapshots/seattlerb/bug_args_masgn2.txt58
-rw-r--r--test/prism/snapshots/seattlerb/bug_args_masgn_outer_parens__19.txt55
-rw-r--r--test/prism/snapshots/seattlerb/bug_call_arglist_parens.txt110
-rw-r--r--test/prism/snapshots/seattlerb/bug_case_when_regexp.txt28
-rw-r--r--test/prism/snapshots/seattlerb/bug_comma.txt41
-rw-r--r--test/prism/snapshots/seattlerb/bug_cond_pct.txt22
-rw-r--r--test/prism/snapshots/seattlerb/bug_hash_args.txt38
-rw-r--r--test/prism/snapshots/seattlerb/bug_hash_args_trailing_comma.txt38
-rw-r--r--test/prism/snapshots/seattlerb/bug_hash_interp_array.txt26
-rw-r--r--test/prism/snapshots/seattlerb/bug_masgn_right.txt49
-rw-r--r--test/prism/snapshots/seattlerb/bug_not_parens.txt25
-rw-r--r--test/prism/snapshots/seattlerb/bug_op_asgn_rescue.txt26
-rw-r--r--test/prism/snapshots/seattlerb/call_and.txt24
-rw-r--r--test/prism/snapshots/seattlerb/call_arg_assoc.txt34
-rw-r--r--test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt43
-rw-r--r--test/prism/snapshots/seattlerb/call_arg_kwsplat.txt37
-rw-r--r--test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt106
-rw-r--r--test/prism/snapshots/seattlerb/call_args_assoc_trailing_comma.txt34
-rw-r--r--test/prism/snapshots/seattlerb/call_args_command.txt54
-rw-r--r--test/prism/snapshots/seattlerb/call_array_arg.txt38
-rw-r--r--test/prism/snapshots/seattlerb/call_array_block_call.txt40
-rw-r--r--test/prism/snapshots/seattlerb/call_array_lambda_block_call.txt41
-rw-r--r--test/prism/snapshots/seattlerb/call_array_lit_inline_hash.txt45
-rw-r--r--test/prism/snapshots/seattlerb/call_assoc.txt31
-rw-r--r--test/prism/snapshots/seattlerb/call_assoc_new.txt34
-rw-r--r--test/prism/snapshots/seattlerb/call_assoc_new_if_multiline.txt58
-rw-r--r--test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt31
-rw-r--r--test/prism/snapshots/seattlerb/call_bang_command_call.txt41
-rw-r--r--test/prism/snapshots/seattlerb/call_bang_squiggle.txt24
-rw-r--r--test/prism/snapshots/seattlerb/call_begin_call_block_call.txt53
-rw-r--r--test/prism/snapshots/seattlerb/call_block_arg_named.txt28
-rw-r--r--test/prism/snapshots/seattlerb/call_carat.txt24
-rw-r--r--test/prism/snapshots/seattlerb/call_colon2.txt17
-rw-r--r--test/prism/snapshots/seattlerb/call_colon_parens.txt18
-rw-r--r--test/prism/snapshots/seattlerb/call_div.txt24
-rw-r--r--test/prism/snapshots/seattlerb/call_dot_parens.txt18
-rw-r--r--test/prism/snapshots/seattlerb/call_env.txt25
-rw-r--r--test/prism/snapshots/seattlerb/call_eq3.txt24
-rw-r--r--test/prism/snapshots/seattlerb/call_gt.txt24
-rw-r--r--test/prism/snapshots/seattlerb/call_kwsplat.txt27
-rw-r--r--test/prism/snapshots/seattlerb/call_leading_dots.txt35
-rw-r--r--test/prism/snapshots/seattlerb/call_leading_dots_comment.txt35
-rw-r--r--test/prism/snapshots/seattlerb/call_lt.txt24
-rw-r--r--test/prism/snapshots/seattlerb/call_lte.txt24
-rw-r--r--test/prism/snapshots/seattlerb/call_not.txt18
-rw-r--r--test/prism/snapshots/seattlerb/call_pipe.txt24
-rw-r--r--test/prism/snapshots/seattlerb/call_rshift.txt24
-rw-r--r--test/prism/snapshots/seattlerb/call_self_brackets.txt22
-rw-r--r--test/prism/snapshots/seattlerb/call_spaceship.txt24
-rw-r--r--test/prism/snapshots/seattlerb/call_stabby_do_end_with_block.txt41
-rw-r--r--test/prism/snapshots/seattlerb/call_stabby_with_braces_block.txt41
-rw-r--r--test/prism/snapshots/seattlerb/call_star.txt24
-rw-r--r--test/prism/snapshots/seattlerb/call_star2.txt24
-rw-r--r--test/prism/snapshots/seattlerb/call_trailing_comma.txt21
-rw-r--r--test/prism/snapshots/seattlerb/call_trailing_dots.txt35
-rw-r--r--test/prism/snapshots/seattlerb/call_unary_bang.txt18
-rw-r--r--test/prism/snapshots/seattlerb/case_in.txt976
-rw-r--r--test/prism/snapshots/seattlerb/case_in_31.txt49
-rw-r--r--test/prism/snapshots/seattlerb/case_in_37.txt58
-rw-r--r--test/prism/snapshots/seattlerb/case_in_42.txt44
-rw-r--r--test/prism/snapshots/seattlerb/case_in_42_2.txt40
-rw-r--r--test/prism/snapshots/seattlerb/case_in_47.txt52
-rw-r--r--test/prism/snapshots/seattlerb/case_in_67.txt33
-rw-r--r--test/prism/snapshots/seattlerb/case_in_86.txt52
-rw-r--r--test/prism/snapshots/seattlerb/case_in_86_2.txt52
-rw-r--r--test/prism/snapshots/seattlerb/case_in_array_pat_const.txt42
-rw-r--r--test/prism/snapshots/seattlerb/case_in_array_pat_const2.txt48
-rw-r--r--test/prism/snapshots/seattlerb/case_in_array_pat_paren_assign.txt48
-rw-r--r--test/prism/snapshots/seattlerb/case_in_const.txt28
-rw-r--r--test/prism/snapshots/seattlerb/case_in_else.txt40
-rw-r--r--test/prism/snapshots/seattlerb/case_in_find.txt47
-rw-r--r--test/prism/snapshots/seattlerb/case_in_find_array.txt44
-rw-r--r--test/prism/snapshots/seattlerb/case_in_hash_pat.txt68
-rw-r--r--test/prism/snapshots/seattlerb/case_in_hash_pat_assign.txt86
-rw-r--r--test/prism/snapshots/seattlerb/case_in_hash_pat_paren_assign.txt51
-rw-r--r--test/prism/snapshots/seattlerb/case_in_hash_pat_paren_true.txt47
-rw-r--r--test/prism/snapshots/seattlerb/case_in_hash_pat_rest.txt55
-rw-r--r--test/prism/snapshots/seattlerb/case_in_hash_pat_rest_solo.txt42
-rw-r--r--test/prism/snapshots/seattlerb/case_in_if_unless_post_mod.txt67
-rw-r--r--test/prism/snapshots/seattlerb/case_in_multiple.txt59
-rw-r--r--test/prism/snapshots/seattlerb/case_in_or.txt38
-rw-r--r--test/prism/snapshots/seattlerb/class_comments.txt31
-rw-r--r--test/prism/snapshots/seattlerb/cond_unary_minus.txt15
-rw-r--r--test/prism/snapshots/seattlerb/const_2_op_asgn_or2.txt24
-rw-r--r--test/prism/snapshots/seattlerb/const_3_op_asgn_or.txt18
-rw-r--r--test/prism/snapshots/seattlerb/const_op_asgn_and1.txt19
-rw-r--r--test/prism/snapshots/seattlerb/const_op_asgn_and2.txt18
-rw-r--r--test/prism/snapshots/seattlerb/const_op_asgn_or.txt20
-rw-r--r--test/prism/snapshots/seattlerb/dasgn_icky2.txt61
-rw-r--r--test/prism/snapshots/seattlerb/defined_eh_parens.txt13
-rw-r--r--test/prism/snapshots/seattlerb/defn_arg_asplat_arg.txt37
-rw-r--r--test/prism/snapshots/seattlerb/defn_arg_forward_args.txt49
-rw-r--r--test/prism/snapshots/seattlerb/defn_args_forward_args.txt61
-rw-r--r--test/prism/snapshots/seattlerb/defn_comments.txt18
-rw-r--r--test/prism/snapshots/seattlerb/defn_endless_command.txt36
-rw-r--r--test/prism/snapshots/seattlerb/defn_endless_command_rescue.txt43
-rw-r--r--test/prism/snapshots/seattlerb/defn_forward_args.txt43
-rw-r--r--test/prism/snapshots/seattlerb/defn_forward_args__no_parens.txt43
-rw-r--r--test/prism/snapshots/seattlerb/defn_kwarg_env.txt55
-rw-r--r--test/prism/snapshots/seattlerb/defn_kwarg_kwarg.txt45
-rw-r--r--test/prism/snapshots/seattlerb/defn_kwarg_kwsplat.txt39
-rw-r--r--test/prism/snapshots/seattlerb/defn_kwarg_kwsplat_anon.txt39
-rw-r--r--test/prism/snapshots/seattlerb/defn_kwarg_lvar.txt42
-rw-r--r--test/prism/snapshots/seattlerb/defn_kwarg_no_parens.txt34
-rw-r--r--test/prism/snapshots/seattlerb/defn_kwarg_val.txt37
-rw-r--r--test/prism/snapshots/seattlerb/defn_no_kwargs.txt29
-rw-r--r--test/prism/snapshots/seattlerb/defn_oneliner.txt47
-rw-r--r--test/prism/snapshots/seattlerb/defn_oneliner_eq2.txt47
-rw-r--r--test/prism/snapshots/seattlerb/defn_oneliner_noargs.txt30
-rw-r--r--test/prism/snapshots/seattlerb/defn_oneliner_noargs_parentheses.txt30
-rw-r--r--test/prism/snapshots/seattlerb/defn_oneliner_rescue.txt158
-rw-r--r--test/prism/snapshots/seattlerb/defn_opt_last_arg.txt33
-rw-r--r--test/prism/snapshots/seattlerb/defn_opt_reg.txt36
-rw-r--r--test/prism/snapshots/seattlerb/defn_opt_splat_arg.txt43
-rw-r--r--test/prism/snapshots/seattlerb/defn_powarg.txt31
-rw-r--r--test/prism/snapshots/seattlerb/defn_reg_opt_reg.txt44
-rw-r--r--test/prism/snapshots/seattlerb/defn_splat_arg.txt34
-rw-r--r--test/prism/snapshots/seattlerb/defn_unary_not.txt21
-rw-r--r--test/prism/snapshots/seattlerb/defns_reserved.txt19
-rw-r--r--test/prism/snapshots/seattlerb/defs_as_arg_with_do_block_inside.txt60
-rw-r--r--test/prism/snapshots/seattlerb/defs_comments.txt19
-rw-r--r--test/prism/snapshots/seattlerb/defs_endless_command.txt46
-rw-r--r--test/prism/snapshots/seattlerb/defs_endless_command_rescue.txt53
-rw-r--r--test/prism/snapshots/seattlerb/defs_kwarg.txt35
-rw-r--r--test/prism/snapshots/seattlerb/defs_oneliner.txt48
-rw-r--r--test/prism/snapshots/seattlerb/defs_oneliner_eq2.txt48
-rw-r--r--test/prism/snapshots/seattlerb/defs_oneliner_rescue.txt161
-rw-r--r--test/prism/snapshots/seattlerb/difficult0_.txt73
-rw-r--r--test/prism/snapshots/seattlerb/difficult1_line_numbers.txt267
-rw-r--r--test/prism/snapshots/seattlerb/difficult1_line_numbers2.txt78
-rw-r--r--test/prism/snapshots/seattlerb/difficult2_.txt74
-rw-r--r--test/prism/snapshots/seattlerb/difficult3_.txt52
-rw-r--r--test/prism/snapshots/seattlerb/difficult3_2.txt42
-rw-r--r--test/prism/snapshots/seattlerb/difficult3_3.txt47
-rw-r--r--test/prism/snapshots/seattlerb/difficult3_4.txt38
-rw-r--r--test/prism/snapshots/seattlerb/difficult3_5.txt48
-rw-r--r--test/prism/snapshots/seattlerb/difficult3__10.txt52
-rw-r--r--test/prism/snapshots/seattlerb/difficult3__11.txt46
-rw-r--r--test/prism/snapshots/seattlerb/difficult3__12.txt49
-rw-r--r--test/prism/snapshots/seattlerb/difficult3__6.txt55
-rw-r--r--test/prism/snapshots/seattlerb/difficult3__7.txt49
-rw-r--r--test/prism/snapshots/seattlerb/difficult3__8.txt52
-rw-r--r--test/prism/snapshots/seattlerb/difficult3__9.txt49
-rw-r--r--test/prism/snapshots/seattlerb/difficult4__leading_dots.txt25
-rw-r--r--test/prism/snapshots/seattlerb/difficult4__leading_dots2.txt16
-rw-r--r--test/prism/snapshots/seattlerb/difficult6_.txt61
-rw-r--r--test/prism/snapshots/seattlerb/difficult6__7.txt55
-rw-r--r--test/prism/snapshots/seattlerb/difficult6__8.txt55
-rw-r--r--test/prism/snapshots/seattlerb/difficult7_.txt93
-rw-r--r--test/prism/snapshots/seattlerb/do_bug.txt63
-rw-r--r--test/prism/snapshots/seattlerb/do_lambda.txt17
-rw-r--r--test/prism/snapshots/seattlerb/dot2_nil__26.txt20
-rw-r--r--test/prism/snapshots/seattlerb/dot3_nil__26.txt20
-rw-r--r--test/prism/snapshots/seattlerb/dstr_evstr.txt38
-rw-r--r--test/prism/snapshots/seattlerb/dstr_evstr_empty_end.txt25
-rw-r--r--test/prism/snapshots/seattlerb/dstr_lex_state.txt35
-rw-r--r--test/prism/snapshots/seattlerb/dstr_str.txt28
-rw-r--r--test/prism/snapshots/seattlerb/dsym_esc_to_sym.txt11
-rw-r--r--test/prism/snapshots/seattlerb/dsym_to_sym.txt37
-rw-r--r--test/prism/snapshots/seattlerb/eq_begin_line_numbers.txt11
-rw-r--r--test/prism/snapshots/seattlerb/eq_begin_why_wont_people_use_their_spacebar.txt50
-rw-r--r--test/prism/snapshots/seattlerb/evstr_evstr.txt42
-rw-r--r--test/prism/snapshots/seattlerb/evstr_str.txt32
-rw-r--r--test/prism/snapshots/seattlerb/expr_not_bang.txt38
-rw-r--r--test/prism/snapshots/seattlerb/f_kw.txt34
-rw-r--r--test/prism/snapshots/seattlerb/f_kw__required.txt30
-rw-r--r--test/prism/snapshots/seattlerb/flip2_env_lvar.txt37
-rw-r--r--test/prism/snapshots/seattlerb/float_with_if_modifier.txt17
-rw-r--r--test/prism/snapshots/seattlerb/heredoc__backslash_dos_format.txt17
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_backslash_nl.txt17
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_bad_hex_escape.txt17
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_bad_oct_escape.txt17
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_comma_arg.txt27
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_lineno.txt26
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_nested.txt42
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_squiggly.txt34
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_squiggly_blank_line_plus_interpolation.txt67
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_squiggly_blank_lines.txt34
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_squiggly_empty.txt11
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_squiggly_interp.txt49
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_squiggly_no_indent.txt11
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_squiggly_tabs.txt28
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_squiggly_tabs_extra.txt28
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_squiggly_visually_blank_lines.txt34
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_trailing_slash_continued_call.txt21
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_unicode.txt11
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_with_carriage_return_escapes.txt11
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_with_carriage_return_escapes_windows.txt11
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_with_extra_carriage_horrible_mix.txt11
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_with_extra_carriage_returns.txt11
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_with_extra_carriage_returns_windows.txt11
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_with_interpolation_and_carriage_return_escapes.txt27
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_with_interpolation_and_carriage_return_escapes_windows.txt27
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_with_not_global_interpolation.txt11
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_with_only_carriage_returns.txt11
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_with_only_carriage_returns_windows.txt11
-rw-r--r--test/prism/snapshots/seattlerb/if_elsif.txt25
-rw-r--r--test/prism/snapshots/seattlerb/if_symbol.txt31
-rw-r--r--test/prism/snapshots/seattlerb/in_expr_no_case.txt17
-rw-r--r--test/prism/snapshots/seattlerb/index_0.txt38
-rw-r--r--test/prism/snapshots/seattlerb/index_0_opasgn.txt36
-rw-r--r--test/prism/snapshots/seattlerb/integer_with_if_modifier.txt18
-rw-r--r--test/prism/snapshots/seattlerb/interpolated_symbol_array_line_breaks.txt25
-rw-r--r--test/prism/snapshots/seattlerb/interpolated_word_array_line_breaks.txt25
-rw-r--r--test/prism/snapshots/seattlerb/iter_args_1.txt40
-rw-r--r--test/prism/snapshots/seattlerb/iter_args_10_1.txt51
-rw-r--r--test/prism/snapshots/seattlerb/iter_args_10_2.txt56
-rw-r--r--test/prism/snapshots/seattlerb/iter_args_11_1.txt54
-rw-r--r--test/prism/snapshots/seattlerb/iter_args_11_2.txt59
-rw-r--r--test/prism/snapshots/seattlerb/iter_args_2__19.txt46
-rw-r--r--test/prism/snapshots/seattlerb/iter_args_3.txt52
-rw-r--r--test/prism/snapshots/seattlerb/iter_args_4.txt45
-rw-r--r--test/prism/snapshots/seattlerb/iter_args_5.txt42
-rw-r--r--test/prism/snapshots/seattlerb/iter_args_6.txt49
-rw-r--r--test/prism/snapshots/seattlerb/iter_args_7_1.txt48
-rw-r--r--test/prism/snapshots/seattlerb/iter_args_7_2.txt53
-rw-r--r--test/prism/snapshots/seattlerb/iter_args_8_1.txt51
-rw-r--r--test/prism/snapshots/seattlerb/iter_args_8_2.txt56
-rw-r--r--test/prism/snapshots/seattlerb/iter_args_9_1.txt46
-rw-r--r--test/prism/snapshots/seattlerb/iter_args_9_2.txt51
-rw-r--r--test/prism/snapshots/seattlerb/iter_kwarg.txt42
-rw-r--r--test/prism/snapshots/seattlerb/iter_kwarg_kwsplat.txt47
-rw-r--r--test/prism/snapshots/seattlerb/label_vs_string.txt34
-rw-r--r--test/prism/snapshots/seattlerb/lambda_do_vs_brace.txt95
-rw-r--r--test/prism/snapshots/seattlerb/lasgn_arg_rescue_arg.txt21
-rw-r--r--test/prism/snapshots/seattlerb/lasgn_call_bracket_rescue_arg.txt34
-rw-r--r--test/prism/snapshots/seattlerb/lasgn_call_nobracket_rescue_arg.txt34
-rw-r--r--test/prism/snapshots/seattlerb/lasgn_command.txt37
-rw-r--r--test/prism/snapshots/seattlerb/lasgn_env.txt14
-rw-r--r--test/prism/snapshots/seattlerb/lasgn_ivar_env.txt13
-rw-r--r--test/prism/snapshots/seattlerb/lasgn_lasgn_command_call.txt33
-rw-r--r--test/prism/snapshots/seattlerb/lasgn_middle_splat.txt49
-rw-r--r--test/prism/snapshots/seattlerb/magic_encoding_comment.txt46
-rw-r--r--test/prism/snapshots/seattlerb/masgn_anon_splat_arg.txt29
-rw-r--r--test/prism/snapshots/seattlerb/masgn_arg_colon_arg.txt42
-rw-r--r--test/prism/snapshots/seattlerb/masgn_arg_ident.txt42
-rw-r--r--test/prism/snapshots/seattlerb/masgn_arg_splat_arg.txt35
-rw-r--r--test/prism/snapshots/seattlerb/masgn_colon2.txt43
-rw-r--r--test/prism/snapshots/seattlerb/masgn_colon3.txt36
-rw-r--r--test/prism/snapshots/seattlerb/masgn_command_call.txt43
-rw-r--r--test/prism/snapshots/seattlerb/masgn_double_paren.txt35
-rw-r--r--test/prism/snapshots/seattlerb/masgn_lhs_splat.txt33
-rw-r--r--test/prism/snapshots/seattlerb/masgn_paren.txt39
-rw-r--r--test/prism/snapshots/seattlerb/masgn_splat_arg.txt32
-rw-r--r--test/prism/snapshots/seattlerb/masgn_splat_arg_arg.txt35
-rw-r--r--test/prism/snapshots/seattlerb/masgn_star.txt19
-rw-r--r--test/prism/snapshots/seattlerb/masgn_var_star_var.txt32
-rw-r--r--test/prism/snapshots/seattlerb/messy_op_asgn_lineno.txt60
-rw-r--r--test/prism/snapshots/seattlerb/method_call_assoc_trailing_comma.txt41
-rw-r--r--test/prism/snapshots/seattlerb/method_call_trailing_comma.txt31
-rw-r--r--test/prism/snapshots/seattlerb/mlhs_back_anonsplat.txt35
-rw-r--r--test/prism/snapshots/seattlerb/mlhs_back_splat.txt38
-rw-r--r--test/prism/snapshots/seattlerb/mlhs_front_anonsplat.txt35
-rw-r--r--test/prism/snapshots/seattlerb/mlhs_front_splat.txt38
-rw-r--r--test/prism/snapshots/seattlerb/mlhs_keyword.txt30
-rw-r--r--test/prism/snapshots/seattlerb/mlhs_mid_anonsplat.txt44
-rw-r--r--test/prism/snapshots/seattlerb/mlhs_mid_splat.txt47
-rw-r--r--test/prism/snapshots/seattlerb/mlhs_rescue.txt36
-rw-r--r--test/prism/snapshots/seattlerb/module_comments.txt29
-rw-r--r--test/prism/snapshots/seattlerb/multiline_hash_declaration.txt95
-rw-r--r--test/prism/snapshots/seattlerb/non_interpolated_symbol_array_line_breaks.txt25
-rw-r--r--test/prism/snapshots/seattlerb/non_interpolated_word_array_line_breaks.txt25
-rw-r--r--test/prism/snapshots/seattlerb/op_asgn_command_call.txt37
-rw-r--r--test/prism/snapshots/seattlerb/op_asgn_dot_ident_command_call.txt32
-rw-r--r--test/prism/snapshots/seattlerb/op_asgn_index_command_call.txt53
-rw-r--r--test/prism/snapshots/seattlerb/op_asgn_primary_colon_const_command_call.txt41
-rw-r--r--test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier1.txt20
-rw-r--r--test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier_command_call.txt40
-rw-r--r--test/prism/snapshots/seattlerb/op_asgn_val_dot_ident_command_call.txt40
-rw-r--r--test/prism/snapshots/seattlerb/parse_def_special_name.txt18
-rw-r--r--test/prism/snapshots/seattlerb/parse_if_not_canonical.txt62
-rw-r--r--test/prism/snapshots/seattlerb/parse_if_not_noncanonical.txt62
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_block.txt30
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_block_inline_comment.txt35
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_block_inline_comment_leading_newlines.txt35
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_block_inline_multiline_comment.txt35
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_call_ivar_arg_no_parens_line_break.txt20
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_call_ivar_line_break_paren.txt20
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_call_no_args.txt61
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_defn_complex.txt67
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_defn_no_parens.txt31
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_defn_no_parens_args.txt29
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_dot2.txt51
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_dot2_open.txt38
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_dot3.txt51
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_dot3_open.txt38
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_dstr_escaped_newline.txt21
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_dstr_soft_newline.txt21
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_evstr_after_break.txt37
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_hash_lit.txt22
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_heredoc.txt43
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_heredoc_evstr.txt38
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_heredoc_hardnewline.txt22
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_heredoc_regexp_chars.txt33
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_iter_call_no_parens.txt74
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_iter_call_parens.txt74
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_multiline_str.txt14
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_multiline_str_literal_n.txt14
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_newlines.txt6
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_op_asgn.txt32
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_postexe.txt22
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_preexe.txt22
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_rescue.txt62
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_return.txt40
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_str_with_newline_escape.txt25
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_to_ary.txt39
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_trailing_newlines.txt25
-rw-r--r--test/prism/snapshots/seattlerb/parse_opt_call_args_assocs_comma.txt34
-rw-r--r--test/prism/snapshots/seattlerb/parse_opt_call_args_lit_comma.txt24
-rw-r--r--test/prism/snapshots/seattlerb/parse_pattern_019.txt33
-rw-r--r--test/prism/snapshots/seattlerb/parse_pattern_044.txt38
-rw-r--r--test/prism/snapshots/seattlerb/parse_pattern_051.txt47
-rw-r--r--test/prism/snapshots/seattlerb/parse_pattern_058.txt73
-rw-r--r--test/prism/snapshots/seattlerb/parse_pattern_058_2.txt67
-rw-r--r--test/prism/snapshots/seattlerb/parse_pattern_069.txt48
-rw-r--r--test/prism/snapshots/seattlerb/parse_pattern_076.txt58
-rw-r--r--test/prism/snapshots/seattlerb/parse_until_not_canonical.txt49
-rw-r--r--test/prism/snapshots/seattlerb/parse_until_not_noncanonical.txt49
-rw-r--r--test/prism/snapshots/seattlerb/parse_while_not_canonical.txt49
-rw-r--r--test/prism/snapshots/seattlerb/parse_while_not_noncanonical.txt49
-rw-r--r--test/prism/snapshots/seattlerb/pctW_lineno.txt52
-rw-r--r--test/prism/snapshots/seattlerb/pct_Q_backslash_nl.txt11
-rw-r--r--test/prism/snapshots/seattlerb/pct_nl.txt17
-rw-r--r--test/prism/snapshots/seattlerb/pct_w_heredoc_interp_nested.txt51
-rw-r--r--test/prism/snapshots/seattlerb/pipe_semicolon.txt39
-rw-r--r--test/prism/snapshots/seattlerb/pipe_space.txt36
-rw-r--r--test/prism/snapshots/seattlerb/qWords_space.txt10
-rw-r--r--test/prism/snapshots/seattlerb/qsymbols.txt28
-rw-r--r--test/prism/snapshots/seattlerb/qsymbols_empty.txt10
-rw-r--r--test/prism/snapshots/seattlerb/qsymbols_empty_space.txt10
-rw-r--r--test/prism/snapshots/seattlerb/qsymbols_interp.txt57
-rw-r--r--test/prism/snapshots/seattlerb/quoted_symbol_hash_arg.txt35
-rw-r--r--test/prism/snapshots/seattlerb/quoted_symbol_keys.txt25
-rw-r--r--test/prism/snapshots/seattlerb/qw_escape.txt11
-rw-r--r--test/prism/snapshots/seattlerb/qw_escape_term.txt11
-rw-r--r--test/prism/snapshots/seattlerb/qwords_empty.txt10
-rw-r--r--test/prism/snapshots/seattlerb/read_escape_unicode_curlies.txt11
-rw-r--r--test/prism/snapshots/seattlerb/read_escape_unicode_h4.txt11
-rw-r--r--test/prism/snapshots/seattlerb/regexp.txt35
-rw-r--r--test/prism/snapshots/seattlerb/regexp_esc_C_slash.txt11
-rw-r--r--test/prism/snapshots/seattlerb/regexp_esc_u.txt11
-rw-r--r--test/prism/snapshots/seattlerb/regexp_escape_extended.txt11
-rw-r--r--test/prism/snapshots/seattlerb/regexp_unicode_curlies.txt17
-rw-r--r--test/prism/snapshots/seattlerb/required_kwarg_no_value.txt34
-rw-r--r--test/prism/snapshots/seattlerb/rescue_do_end_ensure_result.txt58
-rw-r--r--test/prism/snapshots/seattlerb/rescue_do_end_no_raise.txt75
-rw-r--r--test/prism/snapshots/seattlerb/rescue_do_end_raised.txt52
-rw-r--r--test/prism/snapshots/seattlerb/rescue_do_end_rescued.txt79
-rw-r--r--test/prism/snapshots/seattlerb/rescue_in_block.txt47
-rw-r--r--test/prism/snapshots/seattlerb/rescue_parens.txt48
-rw-r--r--test/prism/snapshots/seattlerb/return_call_assocs.txt218
-rw-r--r--test/prism/snapshots/seattlerb/rhs_asgn.txt15
-rw-r--r--test/prism/snapshots/seattlerb/ruby21_numbers.txt27
-rw-r--r--test/prism/snapshots/seattlerb/safe_attrasgn.txt31
-rw-r--r--test/prism/snapshots/seattlerb/safe_attrasgn_constant.txt31
-rw-r--r--test/prism/snapshots/seattlerb/safe_call.txt25
-rw-r--r--test/prism/snapshots/seattlerb/safe_call_after_newline.txt25
-rw-r--r--test/prism/snapshots/seattlerb/safe_call_dot_parens.txt25
-rw-r--r--test/prism/snapshots/seattlerb/safe_call_newline.txt25
-rw-r--r--test/prism/snapshots/seattlerb/safe_call_operator.txt31
-rw-r--r--test/prism/snapshots/seattlerb/safe_call_rhs_newline.txt31
-rw-r--r--test/prism/snapshots/seattlerb/safe_calls.txt41
-rw-r--r--test/prism/snapshots/seattlerb/safe_op_asgn.txt41
-rw-r--r--test/prism/snapshots/seattlerb/safe_op_asgn2.txt34
-rw-r--r--test/prism/snapshots/seattlerb/slashy_newlines_within_string.txt57
-rw-r--r--test/prism/snapshots/seattlerb/stabby_arg_no_paren.txt28
-rw-r--r--test/prism/snapshots/seattlerb/stabby_arg_opt_splat_arg_block_omfg.txt50
-rw-r--r--test/prism/snapshots/seattlerb/stabby_block_iter_call.txt58
-rw-r--r--test/prism/snapshots/seattlerb/stabby_block_iter_call_no_target_with_arg.txt54
-rw-r--r--test/prism/snapshots/seattlerb/stabby_block_kw.txt33
-rw-r--r--test/prism/snapshots/seattlerb/stabby_block_kw__required.txt29
-rw-r--r--test/prism/snapshots/seattlerb/stabby_proc_scope.txt31
-rw-r--r--test/prism/snapshots/seattlerb/str_backslashes.txt24
-rw-r--r--test/prism/snapshots/seattlerb/str_double_double_escaped_newline.txt34
-rw-r--r--test/prism/snapshots/seattlerb/str_double_escaped_newline.txt34
-rw-r--r--test/prism/snapshots/seattlerb/str_double_newline.txt34
-rw-r--r--test/prism/snapshots/seattlerb/str_evstr.txt32
-rw-r--r--test/prism/snapshots/seattlerb/str_evstr_escape.txt38
-rw-r--r--test/prism/snapshots/seattlerb/str_heredoc_interp.txt32
-rw-r--r--test/prism/snapshots/seattlerb/str_interp_ternary_or_label.txt105
-rw-r--r--test/prism/snapshots/seattlerb/str_lit_concat_bad_encodings.txt22
-rw-r--r--test/prism/snapshots/seattlerb/str_newline_hash_line_number.txt14
-rw-r--r--test/prism/snapshots/seattlerb/str_pct_Q_nested.txt38
-rw-r--r--test/prism/snapshots/seattlerb/str_pct_nested_nested.txt42
-rw-r--r--test/prism/snapshots/seattlerb/str_pct_q.txt11
-rw-r--r--test/prism/snapshots/seattlerb/str_single_double_escaped_newline.txt34
-rw-r--r--test/prism/snapshots/seattlerb/str_single_escaped_newline.txt34
-rw-r--r--test/prism/snapshots/seattlerb/str_single_newline.txt34
-rw-r--r--test/prism/snapshots/seattlerb/str_str.txt28
-rw-r--r--test/prism/snapshots/seattlerb/str_str_str.txt34
-rw-r--r--test/prism/snapshots/seattlerb/super_arg.txt17
-rw-r--r--test/prism/snapshots/seattlerb/symbol_empty.txt11
-rw-r--r--test/prism/snapshots/seattlerb/symbol_list.txt50
-rw-r--r--test/prism/snapshots/seattlerb/symbols.txt28
-rw-r--r--test/prism/snapshots/seattlerb/symbols_empty.txt10
-rw-r--r--test/prism/snapshots/seattlerb/symbols_empty_space.txt10
-rw-r--r--test/prism/snapshots/seattlerb/symbols_interp.txt28
-rw-r--r--test/prism/snapshots/seattlerb/thingy.txt57
-rw-r--r--test/prism/snapshots/seattlerb/uminus_float.txt7
-rw-r--r--test/prism/snapshots/seattlerb/unary_minus.txt25
-rw-r--r--test/prism/snapshots/seattlerb/unary_plus.txt25
-rw-r--r--test/prism/snapshots/seattlerb/unary_plus_on_literal.txt21
-rw-r--r--test/prism/snapshots/seattlerb/unary_tilde.txt25
-rw-r--r--test/prism/snapshots/seattlerb/utf8_bom.txt21
-rw-r--r--test/prism/snapshots/seattlerb/when_splat.txt39
-rw-r--r--test/prism/snapshots/seattlerb/words_interp.txt30
-rw-r--r--test/prism/snapshots/seattlerb/yield_arg.txt16
-rw-r--r--test/prism/snapshots/seattlerb/yield_call_assocs.txt224
-rw-r--r--test/prism/snapshots/seattlerb/yield_empty_parens.txt10
-rw-r--r--test/prism/snapshots/single_method_call_with_bang.txt15
-rw-r--r--test/prism/snapshots/single_quote_heredocs.txt11
-rw-r--r--test/prism/snapshots/spanning_heredoc.txt413
-rw-r--r--test/prism/snapshots/spanning_heredoc_newlines.txt155
-rw-r--r--test/prism/snapshots/strings.txt534
-rw-r--r--test/prism/snapshots/super.txt132
-rw-r--r--test/prism/snapshots/symbols.txt464
-rw-r--r--test/prism/snapshots/ternary_operator.txt295
-rw-r--r--test/prism/snapshots/tilde_heredocs.txt405
-rw-r--r--test/prism/snapshots/undef.txt117
-rw-r--r--test/prism/snapshots/unescaping.txt34
-rw-r--r--test/prism/snapshots/unless.txt173
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/alias.txt29
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/assignment.txt1080
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/block.txt1402
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/case.txt446
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/class.txt233
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/def.txt1204
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/defined.txt55
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/defs.txt360
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/dstr.txt353
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/empty.txt5
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/empty_begin.txt9
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/flipflop.txt237
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/for.txt171
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/hookexe.txt49
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/if.txt288
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/kwbegin.txt491
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/lambda.txt151
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/literal.txt1198
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/module.txt107
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/opasgn.txt509
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/pattern.txt446
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/pragma.txt20
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/range.txt55
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/rescue.txt103
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/send.txt2190
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/since/27.txt49
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/since/30.txt88
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/since/31.txt90
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/since/32.txt156
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/singletons.txt9
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/super.txt277
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/unary.txt248
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/undef.txt29
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/variables.txt53
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/while.txt704
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/yield.txt56
-rw-r--r--test/prism/snapshots/unparser/corpus/semantic/and.txt235
-rw-r--r--test/prism/snapshots/unparser/corpus/semantic/block.txt191
-rw-r--r--test/prism/snapshots/unparser/corpus/semantic/def.txt90
-rw-r--r--test/prism/snapshots/unparser/corpus/semantic/dstr.txt598
-rw-r--r--test/prism/snapshots/unparser/corpus/semantic/kwbegin.txt259
-rw-r--r--test/prism/snapshots/unparser/corpus/semantic/literal.txt103
-rw-r--r--test/prism/snapshots/unparser/corpus/semantic/opasgn.txt69
-rw-r--r--test/prism/snapshots/unparser/corpus/semantic/send.txt163
-rw-r--r--test/prism/snapshots/unparser/corpus/semantic/undef.txt29
-rw-r--r--test/prism/snapshots/unparser/corpus/semantic/while.txt277
-rw-r--r--test/prism/snapshots/until.txt180
-rw-r--r--test/prism/snapshots/variables.txt408
-rw-r--r--test/prism/snapshots/while.txt470
-rw-r--r--test/prism/snapshots/whitequark/__ENCODING__.txt6
-rw-r--r--test/prism/snapshots/whitequark/__ENCODING___legacy_.txt6
-rw-r--r--test/prism/snapshots/whitequark/alias.txt21
-rw-r--r--test/prism/snapshots/whitequark/alias_gvar.txt21
-rw-r--r--test/prism/snapshots/whitequark/ambiuous_quoted_label_in_ternary_operator.txt60
-rw-r--r--test/prism/snapshots/whitequark/and.txt53
-rw-r--r--test/prism/snapshots/whitequark/and_asgn.txt59
-rw-r--r--test/prism/snapshots/whitequark/and_or_masgn.txt93
-rw-r--r--test/prism/snapshots/whitequark/anonymous_blockarg.txt46
-rw-r--r--test/prism/snapshots/whitequark/arg.txt56
-rw-r--r--test/prism/snapshots/whitequark/arg_duplicate_ignored.txt59
-rw-r--r--test/prism/snapshots/whitequark/arg_label.txt115
-rw-r--r--test/prism/snapshots/whitequark/arg_scope.txt34
-rw-r--r--test/prism/snapshots/whitequark/args.txt1075
-rw-r--r--test/prism/snapshots/whitequark/args_args_assocs.txt96
-rw-r--r--test/prism/snapshots/whitequark/args_args_assocs_comma.txt54
-rw-r--r--test/prism/snapshots/whitequark/args_args_comma.txt38
-rw-r--r--test/prism/snapshots/whitequark/args_args_star.txt90
-rw-r--r--test/prism/snapshots/whitequark/args_assocs.txt195
-rw-r--r--test/prism/snapshots/whitequark/args_assocs_comma.txt44
-rw-r--r--test/prism/snapshots/whitequark/args_assocs_legacy.txt195
-rw-r--r--test/prism/snapshots/whitequark/args_block_pass.txt28
-rw-r--r--test/prism/snapshots/whitequark/args_cmd.txt41
-rw-r--r--test/prism/snapshots/whitequark/args_star.txt70
-rw-r--r--test/prism/snapshots/whitequark/array_assocs.txt44
-rw-r--r--test/prism/snapshots/whitequark/array_plain.txt16
-rw-r--r--test/prism/snapshots/whitequark/array_splat.txt68
-rw-r--r--test/prism/snapshots/whitequark/array_symbols.txt22
-rw-r--r--test/prism/snapshots/whitequark/array_symbols_empty.txt15
-rw-r--r--test/prism/snapshots/whitequark/array_symbols_interp.txt67
-rw-r--r--test/prism/snapshots/whitequark/array_words.txt22
-rw-r--r--test/prism/snapshots/whitequark/array_words_empty.txt15
-rw-r--r--test/prism/snapshots/whitequark/array_words_interp.txt80
-rw-r--r--test/prism/snapshots/whitequark/asgn_cmd.txt55
-rw-r--r--test/prism/snapshots/whitequark/asgn_mrhs.txt87
-rw-r--r--test/prism/snapshots/whitequark/back_ref.txt7
-rw-r--r--test/prism/snapshots/whitequark/bang.txt25
-rw-r--r--test/prism/snapshots/whitequark/bang_cmd.txt38
-rw-r--r--test/prism/snapshots/whitequark/begin_cmdarg.txt51
-rw-r--r--test/prism/snapshots/whitequark/beginless_erange_after_newline.txt23
-rw-r--r--test/prism/snapshots/whitequark/beginless_irange_after_newline.txt23
-rw-r--r--test/prism/snapshots/whitequark/beginless_range.txt21
-rw-r--r--test/prism/snapshots/whitequark/blockarg.txt31
-rw-r--r--test/prism/snapshots/whitequark/blockargs.txt1339
-rw-r--r--test/prism/snapshots/whitequark/bug_435.txt39
-rw-r--r--test/prism/snapshots/whitequark/bug_447.txt56
-rw-r--r--test/prism/snapshots/whitequark/bug_452.txt63
-rw-r--r--test/prism/snapshots/whitequark/bug_466.txt70
-rw-r--r--test/prism/snapshots/whitequark/bug_473.txt34
-rw-r--r--test/prism/snapshots/whitequark/bug_480.txt37
-rw-r--r--test/prism/snapshots/whitequark/bug_481.txt50
-rw-r--r--test/prism/snapshots/whitequark/bug_ascii_8bit_in_literal.txt11
-rw-r--r--test/prism/snapshots/whitequark/bug_cmd_string_lookahead.txt30
-rw-r--r--test/prism/snapshots/whitequark/bug_cmdarg.txt106
-rw-r--r--test/prism/snapshots/whitequark/bug_def_no_paren_eql_begin.txt18
-rw-r--r--test/prism/snapshots/whitequark/bug_do_block_in_call_args.txt50
-rw-r--r--test/prism/snapshots/whitequark/bug_do_block_in_cmdarg.txt40
-rw-r--r--test/prism/snapshots/whitequark/bug_do_block_in_hash_brace.txt383
-rw-r--r--test/prism/snapshots/whitequark/bug_heredoc_do.txt30
-rw-r--r--test/prism/snapshots/whitequark/bug_interp_single.txt38
-rw-r--r--test/prism/snapshots/whitequark/bug_lambda_leakage.txt38
-rw-r--r--test/prism/snapshots/whitequark/bug_regex_verification.txt11
-rw-r--r--test/prism/snapshots/whitequark/bug_rescue_empty_else.txt25
-rw-r--r--test/prism/snapshots/whitequark/bug_while_not_parens_do.txt28
-rw-r--r--test/prism/snapshots/whitequark/case_cond.txt34
-rw-r--r--test/prism/snapshots/whitequark/case_cond_else.txt46
-rw-r--r--test/prism/snapshots/whitequark/case_expr.txt44
-rw-r--r--test/prism/snapshots/whitequark/case_expr_else.txt60
-rw-r--r--test/prism/snapshots/whitequark/casgn_scoped.txt20
-rw-r--r--test/prism/snapshots/whitequark/casgn_toplevel.txt18
-rw-r--r--test/prism/snapshots/whitequark/casgn_unscoped.txt13
-rw-r--r--test/prism/snapshots/whitequark/character.txt11
-rw-r--r--test/prism/snapshots/whitequark/class.txt27
-rw-r--r--test/prism/snapshots/whitequark/class_super.txt18
-rw-r--r--test/prism/snapshots/whitequark/class_super_label.txt35
-rw-r--r--test/prism/snapshots/whitequark/comments_before_leading_dot__27.txt85
-rw-r--r--test/prism/snapshots/whitequark/complex.txt27
-rw-r--r--test/prism/snapshots/whitequark/cond_begin.txt40
-rw-r--r--test/prism/snapshots/whitequark/cond_begin_masgn.txt52
-rw-r--r--test/prism/snapshots/whitequark/cond_eflipflop.txt78
-rw-r--r--test/prism/snapshots/whitequark/cond_eflipflop_with_beginless_range.txt27
-rw-r--r--test/prism/snapshots/whitequark/cond_eflipflop_with_endless_range.txt27
-rw-r--r--test/prism/snapshots/whitequark/cond_iflipflop.txt78
-rw-r--r--test/prism/snapshots/whitequark/cond_iflipflop_with_beginless_range.txt27
-rw-r--r--test/prism/snapshots/whitequark/cond_iflipflop_with_endless_range.txt27
-rw-r--r--test/prism/snapshots/whitequark/cond_match_current_line.txt34
-rw-r--r--test/prism/snapshots/whitequark/const_op_asgn.txt101
-rw-r--r--test/prism/snapshots/whitequark/const_scoped.txt13
-rw-r--r--test/prism/snapshots/whitequark/const_toplevel.txt11
-rw-r--r--test/prism/snapshots/whitequark/const_unscoped.txt7
-rw-r--r--test/prism/snapshots/whitequark/cpath.txt33
-rw-r--r--test/prism/snapshots/whitequark/cvar.txt7
-rw-r--r--test/prism/snapshots/whitequark/cvasgn.txt13
-rw-r--r--test/prism/snapshots/whitequark/dedenting_heredoc.txt496
-rw-r--r--test/prism/snapshots/whitequark/dedenting_interpolating_heredoc_fake_line_continuation.txt22
-rw-r--r--test/prism/snapshots/whitequark/dedenting_non_interpolating_heredoc_line_continuation.txt22
-rw-r--r--test/prism/snapshots/whitequark/def.txt83
-rw-r--r--test/prism/snapshots/whitequark/defined.txt42
-rw-r--r--test/prism/snapshots/whitequark/defs.txt90
-rw-r--r--test/prism/snapshots/whitequark/empty_stmt.txt5
-rw-r--r--test/prism/snapshots/whitequark/endless_comparison_method.txt221
-rw-r--r--test/prism/snapshots/whitequark/endless_method.txt151
-rw-r--r--test/prism/snapshots/whitequark/endless_method_command_syntax.txt394
-rw-r--r--test/prism/snapshots/whitequark/endless_method_forwarded_args_legacy.txt43
-rw-r--r--test/prism/snapshots/whitequark/endless_method_with_rescue_mod.txt56
-rw-r--r--test/prism/snapshots/whitequark/endless_method_without_args.txt89
-rw-r--r--test/prism/snapshots/whitequark/ensure.txt40
-rw-r--r--test/prism/snapshots/whitequark/ensure_empty.txt16
-rw-r--r--test/prism/snapshots/whitequark/false.txt6
-rw-r--r--test/prism/snapshots/whitequark/float.txt9
-rw-r--r--test/prism/snapshots/whitequark/for.txt83
-rw-r--r--test/prism/snapshots/whitequark/for_mlhs.txt56
-rw-r--r--test/prism/snapshots/whitequark/forward_arg.txt43
-rw-r--r--test/prism/snapshots/whitequark/forward_arg_with_open_args.txt404
-rw-r--r--test/prism/snapshots/whitequark/forward_args_legacy.txt99
-rw-r--r--test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt58
-rw-r--r--test/prism/snapshots/whitequark/forwarded_argument_with_restarg.txt55
-rw-r--r--test/prism/snapshots/whitequark/forwarded_kwrestarg.txt52
-rw-r--r--test/prism/snapshots/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt63
-rw-r--r--test/prism/snapshots/whitequark/forwarded_restarg.txt49
-rw-r--r--test/prism/snapshots/whitequark/gvar.txt7
-rw-r--r--test/prism/snapshots/whitequark/gvasgn.txt13
-rw-r--r--test/prism/snapshots/whitequark/hash_empty.txt9
-rw-r--r--test/prism/snapshots/whitequark/hash_hashrocket.txt49
-rw-r--r--test/prism/snapshots/whitequark/hash_kwsplat.txt35
-rw-r--r--test/prism/snapshots/whitequark/hash_label.txt22
-rw-r--r--test/prism/snapshots/whitequark/hash_label_end.txt100
-rw-r--r--test/prism/snapshots/whitequark/hash_pair_value_omission.txt97
-rw-r--r--test/prism/snapshots/whitequark/heredoc.txt23
-rw-r--r--test/prism/snapshots/whitequark/if.txt63
-rw-r--r--test/prism/snapshots/whitequark/if_else.txt95
-rw-r--r--test/prism/snapshots/whitequark/if_elsif.txt65
-rw-r--r--test/prism/snapshots/whitequark/if_masgn__24.txt42
-rw-r--r--test/prism/snapshots/whitequark/if_mod.txt34
-rw-r--r--test/prism/snapshots/whitequark/if_nl_then.txt34
-rw-r--r--test/prism/snapshots/whitequark/int.txt14
-rw-r--r--test/prism/snapshots/whitequark/int___LINE__.txt6
-rw-r--r--test/prism/snapshots/whitequark/interp_digit_var.txt273
-rw-r--r--test/prism/snapshots/whitequark/ivar.txt7
-rw-r--r--test/prism/snapshots/whitequark/ivasgn.txt13
-rw-r--r--test/prism/snapshots/whitequark/keyword_argument_omission.txt65
-rw-r--r--test/prism/snapshots/whitequark/kwarg.txt30
-rw-r--r--test/prism/snapshots/whitequark/kwbegin_compstmt.txt34
-rw-r--r--test/prism/snapshots/whitequark/kwnilarg.txt84
-rw-r--r--test/prism/snapshots/whitequark/kwoptarg.txt34
-rw-r--r--test/prism/snapshots/whitequark/kwoptarg_with_kwrestarg_and_forwarded_args.txt58
-rw-r--r--test/prism/snapshots/whitequark/kwrestarg_named.txt31
-rw-r--r--test/prism/snapshots/whitequark/kwrestarg_unnamed.txt31
-rw-r--r--test/prism/snapshots/whitequark/lbrace_arg_after_command_args.txt54
-rw-r--r--test/prism/snapshots/whitequark/lparenarg_after_lvar__since_25.txt67
-rw-r--r--test/prism/snapshots/whitequark/lvar.txt15
-rw-r--r--test/prism/snapshots/whitequark/lvar_injecting_match.txt39
-rw-r--r--test/prism/snapshots/whitequark/lvasgn.txt17
-rw-r--r--test/prism/snapshots/whitequark/masgn.txt83
-rw-r--r--test/prism/snapshots/whitequark/masgn_attr.txt82
-rw-r--r--test/prism/snapshots/whitequark/masgn_cmd.txt35
-rw-r--r--test/prism/snapshots/whitequark/masgn_const.txt46
-rw-r--r--test/prism/snapshots/whitequark/masgn_nested.txt66
-rw-r--r--test/prism/snapshots/whitequark/masgn_splat.txt284
-rw-r--r--test/prism/snapshots/whitequark/module.txt14
-rw-r--r--test/prism/snapshots/whitequark/multiple_pattern_matches.txt173
-rw-r--r--test/prism/snapshots/whitequark/newline_in_hash_argument.txt163
-rw-r--r--test/prism/snapshots/whitequark/nil.txt6
-rw-r--r--test/prism/snapshots/whitequark/nil_expression.txt16
-rw-r--r--test/prism/snapshots/whitequark/non_lvar_injecting_match.txt44
-rw-r--r--test/prism/snapshots/whitequark/not.txt55
-rw-r--r--test/prism/snapshots/whitequark/not_cmd.txt38
-rw-r--r--test/prism/snapshots/whitequark/not_masgn__24.txt45
-rw-r--r--test/prism/snapshots/whitequark/nth_ref.txt7
-rw-r--r--test/prism/snapshots/whitequark/numbered_args_after_27.txt143
-rw-r--r--test/prism/snapshots/whitequark/numparam_outside_block.txt114
-rw-r--r--test/prism/snapshots/whitequark/numparam_ruby_bug_19025.txt49
-rw-r--r--test/prism/snapshots/whitequark/op_asgn.txt74
-rw-r--r--test/prism/snapshots/whitequark/op_asgn_cmd.txt178
-rw-r--r--test/prism/snapshots/whitequark/op_asgn_index.txt38
-rw-r--r--test/prism/snapshots/whitequark/op_asgn_index_cmd.txt58
-rw-r--r--test/prism/snapshots/whitequark/optarg.txt74
-rw-r--r--test/prism/snapshots/whitequark/or.txt53
-rw-r--r--test/prism/snapshots/whitequark/or_asgn.txt59
-rw-r--r--test/prism/snapshots/whitequark/parser_bug_272.txt42
-rw-r--r--test/prism/snapshots/whitequark/parser_bug_490.txt106
-rw-r--r--test/prism/snapshots/whitequark/parser_bug_507.txt36
-rw-r--r--test/prism/snapshots/whitequark/parser_bug_518.txt18
-rw-r--r--test/prism/snapshots/whitequark/parser_bug_525.txt65
-rw-r--r--test/prism/snapshots/whitequark/parser_bug_604.txt57
-rw-r--r--test/prism/snapshots/whitequark/parser_bug_640.txt22
-rw-r--r--test/prism/snapshots/whitequark/parser_bug_645.txt35
-rw-r--r--test/prism/snapshots/whitequark/parser_bug_830.txt11
-rw-r--r--test/prism/snapshots/whitequark/parser_bug_989.txt11
-rw-r--r--test/prism/snapshots/whitequark/parser_drops_truncated_parts_of_squiggly_heredoc.txt20
-rw-r--r--test/prism/snapshots/whitequark/parser_slash_slash_n_escaping_in_literals.txt139
-rw-r--r--test/prism/snapshots/whitequark/pattern_matching__FILE__LINE_literals.txt54
-rw-r--r--test/prism/snapshots/whitequark/pattern_matching_blank_else.txt31
-rw-r--r--test/prism/snapshots/whitequark/pattern_matching_else.txt36
-rw-r--r--test/prism/snapshots/whitequark/pattern_matching_single_line.txt45
-rw-r--r--test/prism/snapshots/whitequark/pattern_matching_single_line_allowed_omission_of_parentheses.txt249
-rw-r--r--test/prism/snapshots/whitequark/postexe.txt15
-rw-r--r--test/prism/snapshots/whitequark/preexe.txt15
-rw-r--r--test/prism/snapshots/whitequark/procarg0.txt78
-rw-r--r--test/prism/snapshots/whitequark/range_exclusive.txt16
-rw-r--r--test/prism/snapshots/whitequark/range_inclusive.txt16
-rw-r--r--test/prism/snapshots/whitequark/rational.txt14
-rw-r--r--test/prism/snapshots/whitequark/regex_interp.txt38
-rw-r--r--test/prism/snapshots/whitequark/regex_plain.txt11
-rw-r--r--test/prism/snapshots/whitequark/resbody_list.txt45
-rw-r--r--test/prism/snapshots/whitequark/resbody_list_mrhs.txt55
-rw-r--r--test/prism/snapshots/whitequark/resbody_list_var.txt56
-rw-r--r--test/prism/snapshots/whitequark/resbody_var.txt86
-rw-r--r--test/prism/snapshots/whitequark/rescue.txt43
-rw-r--r--test/prism/snapshots/whitequark/rescue_else.txt59
-rw-r--r--test/prism/snapshots/whitequark/rescue_else_ensure.txt75
-rw-r--r--test/prism/snapshots/whitequark/rescue_ensure.txt59
-rw-r--r--test/prism/snapshots/whitequark/rescue_in_lambda_block.txt26
-rw-r--r--test/prism/snapshots/whitequark/rescue_mod.txt29
-rw-r--r--test/prism/snapshots/whitequark/rescue_mod_asgn.txt35
-rw-r--r--test/prism/snapshots/whitequark/rescue_mod_masgn.txt44
-rw-r--r--test/prism/snapshots/whitequark/rescue_mod_op_assign.txt36
-rw-r--r--test/prism/snapshots/whitequark/rescue_without_begin_end.txt59
-rw-r--r--test/prism/snapshots/whitequark/restarg_named.txt31
-rw-r--r--test/prism/snapshots/whitequark/restarg_unnamed.txt31
-rw-r--r--test/prism/snapshots/whitequark/return.txt60
-rw-r--r--test/prism/snapshots/whitequark/return_block.txt41
-rw-r--r--test/prism/snapshots/whitequark/ruby_bug_10279.txt32
-rw-r--r--test/prism/snapshots/whitequark/ruby_bug_10653.txt173
-rw-r--r--test/prism/snapshots/whitequark/ruby_bug_11107.txt48
-rw-r--r--test/prism/snapshots/whitequark/ruby_bug_11380.txt55
-rw-r--r--test/prism/snapshots/whitequark/ruby_bug_11873.txt767
-rw-r--r--test/prism/snapshots/whitequark/ruby_bug_11873_a.txt1231
-rw-r--r--test/prism/snapshots/whitequark/ruby_bug_11873_b.txt98
-rw-r--r--test/prism/snapshots/whitequark/ruby_bug_11989.txt24
-rw-r--r--test/prism/snapshots/whitequark/ruby_bug_11990.txt35
-rw-r--r--test/prism/snapshots/whitequark/ruby_bug_12073.txt96
-rw-r--r--test/prism/snapshots/whitequark/ruby_bug_12402.txt567
-rw-r--r--test/prism/snapshots/whitequark/ruby_bug_12669.txt133
-rw-r--r--test/prism/snapshots/whitequark/ruby_bug_12686.txt39
-rw-r--r--test/prism/snapshots/whitequark/ruby_bug_13547.txt31
-rw-r--r--test/prism/snapshots/whitequark/ruby_bug_14690.txt59
-rw-r--r--test/prism/snapshots/whitequark/ruby_bug_15789.txt120
-rw-r--r--test/prism/snapshots/whitequark/ruby_bug_9669.txt59
-rw-r--r--test/prism/snapshots/whitequark/sclass.txt25
-rw-r--r--test/prism/snapshots/whitequark/self.txt6
-rw-r--r--test/prism/snapshots/whitequark/send_attr_asgn.txt106
-rw-r--r--test/prism/snapshots/whitequark/send_attr_asgn_conditional.txt31
-rw-r--r--test/prism/snapshots/whitequark/send_binary_op.txt551
-rw-r--r--test/prism/snapshots/whitequark/send_block_chain_cmd.txt325
-rw-r--r--test/prism/snapshots/whitequark/send_block_conditional.txt31
-rw-r--r--test/prism/snapshots/whitequark/send_call.txt57
-rw-r--r--test/prism/snapshots/whitequark/send_conditional.txt25
-rw-r--r--test/prism/snapshots/whitequark/send_index.txt34
-rw-r--r--test/prism/snapshots/whitequark/send_index_asgn.txt37
-rw-r--r--test/prism/snapshots/whitequark/send_index_asgn_legacy.txt37
-rw-r--r--test/prism/snapshots/whitequark/send_index_cmd.txt51
-rw-r--r--test/prism/snapshots/whitequark/send_index_legacy.txt34
-rw-r--r--test/prism/snapshots/whitequark/send_lambda.txt44
-rw-r--r--test/prism/snapshots/whitequark/send_lambda_args.txt51
-rw-r--r--test/prism/snapshots/whitequark/send_lambda_args_noparen.txt57
-rw-r--r--test/prism/snapshots/whitequark/send_lambda_args_shadow.txt34
-rw-r--r--test/prism/snapshots/whitequark/send_lambda_legacy.txt12
-rw-r--r--test/prism/snapshots/whitequark/send_op_asgn_conditional.txt27
-rw-r--r--test/prism/snapshots/whitequark/send_plain.txt65
-rw-r--r--test/prism/snapshots/whitequark/send_plain_cmd.txt104
-rw-r--r--test/prism/snapshots/whitequark/send_self.txt41
-rw-r--r--test/prism/snapshots/whitequark/send_self_block.txt75
-rw-r--r--test/prism/snapshots/whitequark/send_unary_op.txt65
-rw-r--r--test/prism/snapshots/whitequark/slash_newline_in_heredocs.txt34
-rw-r--r--test/prism/snapshots/whitequark/space_args_arg.txt27
-rw-r--r--test/prism/snapshots/whitequark/space_args_arg_block.txt109
-rw-r--r--test/prism/snapshots/whitequark/space_args_arg_call.txt37
-rw-r--r--test/prism/snapshots/whitequark/space_args_arg_newline.txt27
-rw-r--r--test/prism/snapshots/whitequark/space_args_block.txt28
-rw-r--r--test/prism/snapshots/whitequark/space_args_cmd.txt47
-rw-r--r--test/prism/snapshots/whitequark/string___FILE__.txt8
-rw-r--r--test/prism/snapshots/whitequark/string_concat.txt32
-rw-r--r--test/prism/snapshots/whitequark/string_dvar.txt37
-rw-r--r--test/prism/snapshots/whitequark/string_interp.txt38
-rw-r--r--test/prism/snapshots/whitequark/string_plain.txt17
-rw-r--r--test/prism/snapshots/whitequark/super.txt49
-rw-r--r--test/prism/snapshots/whitequark/super_block.txt48
-rw-r--r--test/prism/snapshots/whitequark/symbol_interp.txt37
-rw-r--r--test/prism/snapshots/whitequark/symbol_plain.txt17
-rw-r--r--test/prism/snapshots/whitequark/ternary.txt36
-rw-r--r--test/prism/snapshots/whitequark/ternary_ambiguous_symbol.txt50
-rw-r--r--test/prism/snapshots/whitequark/trailing_forward_arg.txt55
-rw-r--r--test/prism/snapshots/whitequark/true.txt6
-rw-r--r--test/prism/snapshots/whitequark/unary_num_pow_precedence.txt80
-rw-r--r--test/prism/snapshots/whitequark/undef.txt39
-rw-r--r--test/prism/snapshots/whitequark/unless.txt63
-rw-r--r--test/prism/snapshots/whitequark/unless_else.txt95
-rw-r--r--test/prism/snapshots/whitequark/unless_mod.txt34
-rw-r--r--test/prism/snapshots/whitequark/until.txt61
-rw-r--r--test/prism/snapshots/whitequark/until_mod.txt33
-rw-r--r--test/prism/snapshots/whitequark/until_post.txt42
-rw-r--r--test/prism/snapshots/whitequark/var_and_asgn.txt14
-rw-r--r--test/prism/snapshots/whitequark/var_op_asgn.txt57
-rw-r--r--test/prism/snapshots/whitequark/var_op_asgn_cmd.txt28
-rw-r--r--test/prism/snapshots/whitequark/var_or_asgn.txt14
-rw-r--r--test/prism/snapshots/whitequark/when_multi.txt50
-rw-r--r--test/prism/snapshots/whitequark/when_splat.txt72
-rw-r--r--test/prism/snapshots/whitequark/when_then.txt44
-rw-r--r--test/prism/snapshots/whitequark/while.txt61
-rw-r--r--test/prism/snapshots/whitequark/while_mod.txt33
-rw-r--r--test/prism/snapshots/whitequark/while_post.txt42
-rw-r--r--test/prism/snapshots/whitequark/xstring_interp.txt37
-rw-r--r--test/prism/snapshots/whitequark/xstring_plain.txt11
-rw-r--r--test/prism/snapshots/whitequark/yield.txt51
-rw-r--r--test/prism/snapshots/whitequark/zsuper.txt7
-rw-r--r--test/prism/snapshots/xstring.txt67
-rw-r--r--test/prism/snapshots/xstring_with_backslash.txt11
-rw-r--r--test/prism/snapshots/yield.txt103
-rw-r--r--test/prism/static_inspect_test.rb90
-rw-r--r--test/prism/static_literals_test.rb92
-rw-r--r--test/prism/test_helper.rb126
-rw-r--r--test/prism/unescape_test.rb235
-rw-r--r--test/prism/version_test.rb11
-rw-r--r--test/prism/warnings_test.rb237
-rw-r--r--test/psych/test_numeric.rb9
-rw-r--r--test/psych/test_object_references.rb8
-rw-r--r--test/psych/test_parser.rb19
-rw-r--r--test/psych/test_psych.rb26
-rw-r--r--test/psych/test_scalar_scanner.rb2
-rw-r--r--test/psych/test_set.rb7
-rw-r--r--test/psych/test_string.rb12
-rw-r--r--test/psych/visitors/test_emitter.rb16
-rw-r--r--test/psych/visitors/test_to_ruby.rb2
-rw-r--r--test/psych/visitors/test_yaml_tree.rb8
-rw-r--r--test/racc/assets/cadenza.y170
-rw-r--r--test/racc/assets/cast.y926
-rw-r--r--test/racc/assets/chk.y126
-rw-r--r--test/racc/assets/conf.y16
-rw-r--r--test/racc/assets/csspool.y729
-rw-r--r--test/racc/assets/digraph.y29
-rw-r--r--test/racc/assets/echk.y118
-rw-r--r--test/racc/assets/edtf.y583
-rw-r--r--test/racc/assets/err.y60
-rw-r--r--test/racc/assets/error_recovery.y35
-rw-r--r--test/racc/assets/expect.y7
-rw-r--r--test/racc/assets/firstline.y4
-rw-r--r--test/racc/assets/huia.y318
-rw-r--r--test/racc/assets/ichk.y102
-rw-r--r--test/racc/assets/ifelse.y14
-rw-r--r--test/racc/assets/intp.y546
-rw-r--r--test/racc/assets/journey.y47
-rw-r--r--test/racc/assets/liquor.y313
-rw-r--r--test/racc/assets/machete.y423
-rw-r--r--test/racc/assets/macruby.y2197
-rw-r--r--test/racc/assets/mailp.y437
-rw-r--r--test/racc/assets/mediacloth.y599
-rw-r--r--test/racc/assets/mof.y649
-rw-r--r--test/racc/assets/namae.y302
-rw-r--r--test/racc/assets/nasl.y626
-rw-r--r--test/racc/assets/newsyn.y25
-rw-r--r--test/racc/assets/noend.y4
-rw-r--r--test/racc/assets/nokogiri-css.y255
-rw-r--r--test/racc/assets/nonass.y41
-rw-r--r--test/racc/assets/normal.y27
-rw-r--r--test/racc/assets/norule.y4
-rw-r--r--test/racc/assets/nullbug1.y25
-rw-r--r--test/racc/assets/nullbug2.y15
-rw-r--r--test/racc/assets/opal.y1807
-rw-r--r--test/racc/assets/opt.y123
-rw-r--r--test/racc/assets/percent.y35
-rw-r--r--test/racc/assets/php_serialization.y98
-rw-r--r--test/racc/assets/recv.y97
-rw-r--r--test/racc/assets/riml.y665
-rw-r--r--test/racc/assets/rrconf.y14
-rw-r--r--test/racc/assets/ruby18.y1943
-rw-r--r--test/racc/assets/ruby19.y2174
-rw-r--r--test/racc/assets/ruby20.y2350
-rw-r--r--test/racc/assets/ruby21.y2359
-rw-r--r--test/racc/assets/ruby22.y2381
-rw-r--r--test/racc/assets/scan.y72
-rw-r--r--test/racc/assets/syntax.y50
-rw-r--r--test/racc/assets/tp_plus.y622
-rw-r--r--test/racc/assets/twowaysql.y278
-rw-r--r--test/racc/assets/unterm.y5
-rw-r--r--test/racc/assets/useless.y12
-rw-r--r--test/racc/assets/yyerr.y46
-rw-r--r--test/racc/bench.y36
-rw-r--r--test/racc/case.rb111
-rw-r--r--test/racc/infini.y8
-rw-r--r--test/racc/regress/README.txt7
-rw-r--r--test/racc/regress/cadenza798
-rw-r--r--test/racc/regress/cast3947
-rw-r--r--test/racc/regress/csspool2316
-rw-r--r--test/racc/regress/edtf1796
-rw-r--r--test/racc/regress/huia1683
-rw-r--r--test/racc/regress/journey224
-rw-r--r--test/racc/regress/liquor887
-rw-r--r--test/racc/regress/machete835
-rw-r--r--test/racc/regress/mediacloth1465
-rw-r--r--test/racc/regress/mof1370
-rw-r--r--test/racc/regress/namae636
-rw-r--r--test/racc/regress/nasl2550
-rw-r--r--test/racc/regress/nokogiri-css838
-rw-r--r--test/racc/regress/opal10109
-rw-r--r--test/racc/regress/php_serialization338
-rw-r--r--test/racc/regress/riml4039
-rw-r--r--test/racc/regress/ruby189947
-rw-r--r--test/racc/regress/ruby2211182
-rw-r--r--test/racc/regress/tp_plus1935
-rw-r--r--test/racc/regress/twowaysql558
-rw-r--r--test/racc/scandata/brace7
-rw-r--r--test/racc/scandata/gvar1
-rw-r--r--test/racc/scandata/normal4
-rw-r--r--test/racc/scandata/percent18
-rw-r--r--test/racc/scandata/slash10
-rw-r--r--test/racc/src.intp34
-rw-r--r--test/racc/start.y20
-rw-r--r--test/racc/test_chk_y.rb52
-rw-r--r--test/racc/test_grammar_file_parser.rb15
-rw-r--r--test/racc/test_racc_command.rb339
-rw-r--r--test/racc/test_scan_y.rb52
-rw-r--r--test/racc/testscanner.rb51
-rw-r--r--test/rdoc/test_rdoc_any_method.rb38
-rw-r--r--test/rdoc/test_rdoc_context.rb6
-rw-r--r--test/rdoc/test_rdoc_generator_json_index.rb14
-rw-r--r--test/rdoc/test_rdoc_markdown.rb19
-rw-r--r--test/rdoc/test_rdoc_markup_formatter.rb6
-rw-r--r--test/rdoc/test_rdoc_markup_to_html.rb40
-rw-r--r--test/rdoc/test_rdoc_markup_to_html_crossref.rb12
-rw-r--r--test/rdoc/test_rdoc_markup_to_markdown.rb7
-rw-r--r--test/rdoc/test_rdoc_markup_to_rdoc.rb18
-rw-r--r--test/rdoc/test_rdoc_options.rb23
-rw-r--r--test/rdoc/test_rdoc_parser.rb14
-rw-r--r--test/rdoc/test_rdoc_parser_c.rb43
-rw-r--r--test/rdoc/test_rdoc_parser_ruby.rb30
-rw-r--r--test/rdoc/test_rdoc_rdoc.rb14
-rw-r--r--test/rdoc/test_rdoc_store.rb8
-rw-r--r--test/rdoc/test_rdoc_token_stream.rb52
-rw-r--r--test/rdoc/xref_data.rb12
-rw-r--r--test/reline/helper.rb60
-rw-r--r--test/reline/test_ansi_with_terminfo.rb20
-rw-r--r--test/reline/test_ansi_without_terminfo.rb4
-rw-r--r--test/reline/test_config.rb125
-rw-r--r--test/reline/test_face.rb257
-rw-r--r--test/reline/test_history.rb2
-rw-r--r--test/reline/test_key_actor_emacs.rb1845
-rw-r--r--test/reline/test_key_actor_vi.rb1294
-rw-r--r--test/reline/test_key_stroke.rb26
-rw-r--r--test/reline/test_line_editor.rb188
-rw-r--r--test/reline/test_macro.rb3
-rw-r--r--test/reline/test_reline.rb11
-rw-r--r--test/reline/test_reline_key.rb1
-rw-r--r--test/reline/test_string_processing.rb14
-rw-r--r--test/reline/test_terminfo.rb2
-rw-r--r--test/reline/test_unicode.rb31
-rw-r--r--test/reline/test_within_pipe.rb6
-rwxr-xr-xtest/reline/yamatanooroti/multiline_repl33
-rw-r--r--test/reline/yamatanooroti/test_rendering.rb390
-rw-r--r--test/resolv/test_dns.rb243
-rw-r--r--test/resolv/test_resource.rb68
-rw-r--r--test/resolv/test_svcb_https.rb231
-rw-r--r--test/rinda/test_rinda.rb912
-rw-r--r--test/rinda/test_tuplebag.rb173
-rw-r--r--test/ripper/assert_parse_files.rb2
-rw-r--r--test/ripper/dummyparser.rb53
-rw-r--r--test/ripper/test_lexer.rb72
-rw-r--r--test/ripper/test_parser_events.rb56
-rw-r--r--test/ripper/test_ripper.rb31
-rw-r--r--test/ripper/test_scanner_events.rb19
-rw-r--r--test/ripper/test_sexp.rb28
-rw-r--r--test/ruby/enc/test_case_mapping.rb10
-rw-r--r--test/ruby/enc/test_case_options.rb12
-rw-r--r--test/ruby/rjit/test_assembler.rb14
-rw-r--r--test/ruby/test_allocation.rb656
-rw-r--r--test/ruby/test_argf.rb110
-rw-r--r--test/ruby/test_array.rb36
-rw-r--r--test/ruby/test_ast.rb178
-rw-r--r--test/ruby/test_autoload.rb5
-rw-r--r--test/ruby/test_backtrace.rb60
-rw-r--r--test/ruby/test_beginendblock.rb9
-rw-r--r--test/ruby/test_bignum.rb2
-rw-r--r--test/ruby/test_call.rb236
-rw-r--r--test/ruby/test_class.rb13
-rw-r--r--test/ruby/test_clone.rb7
-rw-r--r--test/ruby/test_comparable.rb6
-rw-r--r--test/ruby/test_compile_prism.rb2671
-rw-r--r--test/ruby/test_complex.rb128
-rw-r--r--test/ruby/test_continuation.rb4
-rw-r--r--test/ruby/test_data.rb17
-rw-r--r--test/ruby/test_default_gems.rb4
-rw-r--r--test/ruby/test_defined.rb12
-rw-r--r--test/ruby/test_dir.rb89
-rw-r--r--test/ruby/test_dir_m17n.rb46
-rw-r--r--test/ruby/test_econv.rb2
-rw-r--r--test/ruby/test_encoding.rb2
-rw-r--r--test/ruby/test_enum.rb2
-rw-r--r--test/ruby/test_enumerator.rb73
-rw-r--r--test/ruby/test_env.rb4
-rw-r--r--test/ruby/test_eval.rb4
-rw-r--r--test/ruby/test_exception.rb130
-rw-r--r--test/ruby/test_fiber.rb16
-rw-r--r--test/ruby/test_file.rb246
-rw-r--r--test/ruby/test_file_exhaustive.rb2
-rw-r--r--test/ruby/test_gc.rb355
-rw-r--r--test/ruby/test_gc_compact.rb121
-rw-r--r--test/ruby/test_hash.rb815
-rw-r--r--test/ruby/test_integer.rb71
-rw-r--r--test/ruby/test_io.rb131
-rw-r--r--test/ruby/test_io_buffer.rb84
-rw-r--r--test/ruby/test_io_m17n.rb32
-rw-r--r--test/ruby/test_iseq.rb98
-rw-r--r--test/ruby/test_keyword.rb117
-rw-r--r--test/ruby/test_lambda.rb26
-rw-r--r--test/ruby/test_lazy_enumerator.rb5
-rw-r--r--test/ruby/test_literal.rb22
-rw-r--r--test/ruby/test_m17n.rb18
-rw-r--r--test/ruby/test_marshal.rb12
-rw-r--r--test/ruby/test_math.rb10
-rw-r--r--test/ruby/test_method.rb146
-rw-r--r--test/ruby/test_module.rb53
-rw-r--r--test/ruby/test_nomethod_error.rb2
-rw-r--r--test/ruby/test_object.rb43
-rw-r--r--test/ruby/test_objectspace.rb5
-rw-r--r--test/ruby/test_optimization.rb86
-rw-r--r--test/ruby/test_pack.rb40
-rw-r--r--test/ruby/test_parse.rb446
-rw-r--r--test/ruby/test_pattern_matching.rb14
-rw-r--r--test/ruby/test_proc.rb83
-rw-r--r--test/ruby/test_process.rb306
-rw-r--r--test/ruby/test_random_formatter.rb55
-rw-r--r--test/ruby/test_range.rb299
-rw-r--r--test/ruby/test_refinement.rb100
-rw-r--r--test/ruby/test_regexp.rb316
-rw-r--r--test/ruby/test_require.rb72
-rw-r--r--test/ruby/test_require_lib.rb31
-rw-r--r--test/ruby/test_rubyoptions.rb284
-rw-r--r--test/ruby/test_settracefunc.rb250
-rw-r--r--test/ruby/test_shapes.rb591
-rw-r--r--test/ruby/test_stack.rb1
-rw-r--r--test/ruby/test_string.rb616
-rw-r--r--test/ruby/test_string_memory.rb55
-rw-r--r--test/ruby/test_struct.rb22
-rw-r--r--test/ruby/test_super.rb12
-rw-r--r--test/ruby/test_symbol.rb9
-rw-r--r--test/ruby/test_syntax.rb382
-rw-r--r--test/ruby/test_thread.rb35
-rw-r--r--test/ruby/test_thread_queue.rb9
-rw-r--r--test/ruby/test_time.rb23
-rw-r--r--test/ruby/test_time_tz.rb8
-rw-r--r--test/ruby/test_transcode.rb500
-rw-r--r--test/ruby/test_variable.rb12
-rw-r--r--test/ruby/test_vm_dump.rb7
-rw-r--r--test/ruby/test_weakkeymap.rb22
-rw-r--r--test/ruby/test_weakmap.rb33
-rw-r--r--test/ruby/test_whileuntil.rb18
-rw-r--r--test/ruby/test_yjit.rb452
-rw-r--r--test/ruby/test_yjit_exit_locations.rb18
-rw-r--r--test/rubygems/bundler_test_gem.rb20
-rw-r--r--test/rubygems/helper.rb102
-rw-r--r--test/rubygems/installer_test_case.rb16
-rw-r--r--test/rubygems/multifactor_auth_utilities.rb111
-rw-r--r--test/rubygems/specifications/rubyforge-0.0.1.gemspec21
-rw-r--r--test/rubygems/test_bundled_ca.rb6
-rw-r--r--test/rubygems/test_gem.rb38
-rw-r--r--test/rubygems/test_gem_ci_detector.rb44
-rw-r--r--test/rubygems/test_gem_command.rb2
-rw-r--r--test/rubygems/test_gem_command_manager.rb10
-rw-r--r--test/rubygems/test_gem_commands_build_command.rb9
-rw-r--r--test/rubygems/test_gem_commands_cert_command.rb3
-rw-r--r--test/rubygems/test_gem_commands_cleanup_command.rb10
-rw-r--r--test/rubygems/test_gem_commands_environment_command.rb2
-rw-r--r--test/rubygems/test_gem_commands_exec_command.rb10
-rw-r--r--test/rubygems/test_gem_commands_generate_index_command.rb81
-rw-r--r--test/rubygems/test_gem_commands_info_command.rb12
-rw-r--r--test/rubygems/test_gem_commands_install_command.rb44
-rw-r--r--test/rubygems/test_gem_commands_open_command.rb7
-rw-r--r--test/rubygems/test_gem_commands_owner_command.rb116
-rw-r--r--test/rubygems/test_gem_commands_pristine_command.rb5
-rw-r--r--test/rubygems/test_gem_commands_push_command.rb120
-rw-r--r--test/rubygems/test_gem_commands_rebuild_command.rb145
-rw-r--r--test/rubygems/test_gem_commands_setup_command.rb2
-rw-r--r--test/rubygems/test_gem_commands_signin_command.rb75
-rw-r--r--test/rubygems/test_gem_commands_stale_command.rb4
-rw-r--r--test/rubygems/test_gem_commands_uninstall_command.rb26
-rw-r--r--test/rubygems/test_gem_commands_update_command.rb52
-rw-r--r--test/rubygems/test_gem_commands_yank_command.rb118
-rw-r--r--test/rubygems/test_gem_config_file.rb42
-rw-r--r--test/rubygems/test_gem_console_ui.rb19
-rw-r--r--test/rubygems/test_gem_dependency.rb10
-rw-r--r--test/rubygems/test_gem_dependency_installer.rb110
-rw-r--r--test/rubygems/test_gem_ext_builder.rb133
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder.rb4
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock44
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml2
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock32
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml2
-rw-r--r--test/rubygems/test_gem_ext_ext_conf_builder.rb4
-rw-r--r--test/rubygems/test_gem_gemcutter_utilities.rb205
-rw-r--r--test/rubygems/test_gem_indexer.rb380
-rw-r--r--test/rubygems/test_gem_install_update_options.rb23
-rw-r--r--test/rubygems/test_gem_installer.rb142
-rw-r--r--test/rubygems/test_gem_local_remote_options.rb12
-rw-r--r--test/rubygems/test_gem_name_tuple.rb33
-rw-r--r--test/rubygems/test_gem_package.rb119
-rw-r--r--test/rubygems/test_gem_package_tar_header.rb50
-rw-r--r--test/rubygems/test_gem_package_tar_reader.rb15
-rw-r--r--test/rubygems/test_gem_package_tar_reader_entry.rb54
-rw-r--r--test/rubygems/test_gem_package_task.rb4
-rw-r--r--test/rubygems/test_gem_platform.rb8
-rw-r--r--test/rubygems/test_gem_remote_fetcher.rb109
-rw-r--r--test/rubygems/test_gem_request.rb73
-rw-r--r--test/rubygems/test_gem_request_connection_pools.rb20
-rw-r--r--test/rubygems/test_gem_request_set.rb20
-rw-r--r--test/rubygems/test_gem_request_set_gem_dependency_api.rb94
-rw-r--r--test/rubygems/test_gem_requirement.rb14
-rw-r--r--test/rubygems/test_gem_resolver.rb2
-rw-r--r--test/rubygems/test_gem_resolver_api_set.rb50
-rw-r--r--test/rubygems/test_gem_resolver_api_specification.rb68
-rw-r--r--test/rubygems/test_gem_resolver_best_set.rb6
-rw-r--r--test/rubygems/test_gem_resolver_specification.rb2
-rw-r--r--test/rubygems/test_gem_safe_marshal.rb404
-rw-r--r--test/rubygems/test_gem_safe_yaml.rb24
-rw-r--r--test/rubygems/test_gem_security_policy.rb24
-rw-r--r--test/rubygems/test_gem_security_signer.rb4
-rw-r--r--test/rubygems/test_gem_security_trust_dir.rb2
-rw-r--r--test/rubygems/test_gem_silent_ui.rb1
-rw-r--r--test/rubygems/test_gem_source.rb19
-rw-r--r--test/rubygems/test_gem_source_git.rb2
-rw-r--r--test/rubygems/test_gem_source_list.rb4
-rw-r--r--test/rubygems/test_gem_source_lock.rb2
-rw-r--r--test/rubygems/test_gem_source_subpath_problem.rb4
-rw-r--r--test/rubygems/test_gem_spec_fetcher.rb2
-rw-r--r--test/rubygems/test_gem_specification.rb279
-rw-r--r--test/rubygems/test_gem_stream_ui.rb44
-rw-r--r--test/rubygems/test_gem_uninstaller.rb68
-rw-r--r--test/rubygems/test_gem_update_suggestion.rb114
-rw-r--r--test/rubygems/test_gem_version_option.rb48
-rw-r--r--test/rubygems/test_kernel.rb16
-rw-r--r--test/rubygems/test_require.rb72
-rw-r--r--test/rubygems/test_rubygems.rb8
-rw-r--r--test/rubygems/test_webauthn_listener.rb57
-rw-r--r--test/rubygems/test_webauthn_listener_response.rb16
-rw-r--r--test/rubygems/test_webauthn_poller.rb124
-rw-r--r--test/rubygems/utilities.rb33
-rw-r--r--test/runner.rb13
-rw-r--r--test/set/fixtures/fake_sorted_set_gem/sorted_set.rb (renamed from test/fixtures/fake_sorted_set_gem/sorted_set.rb)0
-rw-r--r--test/set/test_set.rb (renamed from test/test_set.rb)0
-rw-r--r--test/set/test_sorted_set.rb (renamed from test/test_sorted_set.rb)0
-rw-r--r--test/socket/test_addrinfo.rb4
-rw-r--r--test/socket/test_socket.rb269
-rw-r--r--test/socket/test_tcp.rb2
-rw-r--r--test/socket/test_unix.rb68
-rw-r--r--test/stringio/test_ractor.rb2
-rw-r--r--test/stringio/test_stringio.rb112
-rw-r--r--test/strscan/test_stringscanner.rb48
-rw-r--r--test/syslog/test_syslog_logger.rb588
-rw-r--r--test/test_abbrev.rb55
-rw-r--r--test/test_delegate.rb9
-rw-r--r--test/test_extlibs.rb6
-rw-r--r--test/test_forwardable.rb2
-rw-r--r--test/test_getoptlong.rb163
-rw-r--r--test/test_ipaddr.rb95
-rw-r--r--test/test_mutex_m.rb79
-rw-r--r--test/test_observer.rb66
-rw-r--r--test/test_pp.rb44
-rw-r--r--test/test_syslog.rb193
-rw-r--r--test/test_tempfile.rb27
-rw-r--r--test/test_timeout.rb97
-rw-r--r--test/test_tmpdir.rb16
-rw-r--r--test/test_trick.rb65
-rw-r--r--test/uri/test_generic.rb15
-rw-r--r--test/uri/test_parser.rb30
-rw-r--r--test/win32ole/available_ole.rb14
-rw-r--r--test/win32ole/err_in_callback.rb4
-rw-r--r--test/win32ole/test_err_in_callback.rb4
-rw-r--r--test/win32ole/test_folderitem2_invokeverb.rb2
-rw-r--r--test/win32ole/test_nil2vtempty.rb2
-rw-r--r--test/win32ole/test_propertyputref.rb2
-rw-r--r--test/win32ole/test_thread.rb2
-rw-r--r--test/win32ole/test_win32ole.rb41
-rw-r--r--test/win32ole/test_win32ole_event.rb70
-rw-r--r--test/win32ole/test_win32ole_method.rb48
-rw-r--r--test/win32ole/test_win32ole_method_event.rb10
-rw-r--r--test/win32ole/test_win32ole_param.rb42
-rw-r--r--test/win32ole/test_win32ole_param_event.rb2
-rw-r--r--test/win32ole/test_win32ole_record.rb60
-rw-r--r--test/win32ole/test_win32ole_type.rb67
-rw-r--r--test/win32ole/test_win32ole_type_event.rb4
-rw-r--r--test/win32ole/test_win32ole_typelib.rb66
-rw-r--r--test/win32ole/test_win32ole_variable.rb16
-rw-r--r--test/win32ole/test_win32ole_variant.rb308
-rw-r--r--test/win32ole/test_win32ole_variant_m.rb5
-rw-r--r--test/win32ole/test_win32ole_variant_outarg.rb6
-rw-r--r--test/win32ole/test_word.rb2
-rw-r--r--test/yaml/test_store.rb2
-rw-r--r--test/zlib/test_zlib.rb48
-rw-r--r--thread.c699
-rw-r--r--thread_none.c65
-rw-r--r--thread_none.h1
-rw-r--r--thread_pthread.c3014
-rw-r--r--thread_pthread.h96
-rw-r--r--thread_pthread_mn.c1061
-rw-r--r--thread_sync.c147
-rw-r--r--thread_win32.c210
-rw-r--r--thread_win32.h2
-rw-r--r--time.c125
-rw-r--r--timev.rb65
-rw-r--r--tool/bundler/dev_gems.rb8
-rw-r--r--tool/bundler/dev_gems.rb.lock66
-rw-r--r--tool/bundler/rubocop_gems.rb2
-rw-r--r--tool/bundler/rubocop_gems.rb.lock73
-rw-r--r--tool/bundler/standard_gems.rb.lock81
-rw-r--r--tool/bundler/test_gems.rb10
-rw-r--r--tool/bundler/test_gems.rb.lock45
-rw-r--r--tool/bundler/vendor_gems.rb15
-rw-r--r--tool/ci_functions.sh29
-rwxr-xr-xtool/darwin-cc3
-rw-r--r--tool/downloader.rb35
-rw-r--r--tool/dummy-rake-compiler/rake/extensiontask.rb9
-rwxr-xr-xtool/enc-unicode.rb26
-rw-r--r--tool/fake.rb9
-rwxr-xr-xtool/fetch-bundled_gems.rb15
-rwxr-xr-xtool/format-release4
-rwxr-xr-xtool/gen-github-release.rb19
-rw-r--r--tool/generic_erb.rb6
-rwxr-xr-xtool/leaked-globals57
-rw-r--r--tool/lib/_tmpdir.rb100
-rw-r--r--tool/lib/bundled_gem.rb11
-rw-r--r--tool/lib/colorize.rb33
-rw-r--r--tool/lib/core_assertions.rb63
-rw-r--r--tool/lib/envutil.rb58
-rw-r--r--tool/lib/helper.rb4
-rw-r--r--tool/lib/iseq_loader_checker.rb9
-rw-r--r--tool/lib/output.rb23
-rw-r--r--tool/lib/path.rb101
-rw-r--r--tool/lib/test/unit.rb298
-rw-r--r--tool/lib/test/unit/parallel.rb10
-rw-r--r--tool/lib/test/unit/testcase.rb22
-rw-r--r--tool/lib/vcs.rb67
-rw-r--r--tool/lib/webrick/httprequest.rb2
-rwxr-xr-xtool/ln_sr.rb2
-rw-r--r--tool/lrama/LEGAL.md12
-rw-r--r--tool/lrama/MIT21
-rw-r--r--tool/lrama/NEWS.md382
-rwxr-xr-xtool/lrama/exe/lrama1
-rw-r--r--tool/lrama/lib/lrama.rb3
-rw-r--r--tool/lrama/lib/lrama/command.rb167
-rw-r--r--tool/lrama/lib/lrama/context.rb60
-rw-r--r--tool/lrama/lib/lrama/counterexamples.rb286
-rw-r--r--tool/lrama/lib/lrama/counterexamples/derivation.rb63
-rw-r--r--tool/lrama/lib/lrama/counterexamples/example.rb124
-rw-r--r--tool/lrama/lib/lrama/counterexamples/path.rb23
-rw-r--r--tool/lrama/lib/lrama/counterexamples/production_path.rb17
-rw-r--r--tool/lrama/lib/lrama/counterexamples/start_path.rb21
-rw-r--r--tool/lrama/lib/lrama/counterexamples/state_item.rb6
-rw-r--r--tool/lrama/lib/lrama/counterexamples/transition_path.rb17
-rw-r--r--tool/lrama/lib/lrama/counterexamples/triple.rb21
-rw-r--r--tool/lrama/lib/lrama/digraph.rb8
-rw-r--r--tool/lrama/lib/lrama/grammar.rb769
-rw-r--r--tool/lrama/lib/lrama/grammar/auxiliary.rb7
-rw-r--r--tool/lrama/lib/lrama/grammar/binding.rb24
-rw-r--r--tool/lrama/lib/lrama/grammar/code.rb51
-rw-r--r--tool/lrama/lib/lrama/grammar/code/destructor_code.rb40
-rw-r--r--tool/lrama/lib/lrama/grammar/code/initial_action_code.rb34
-rw-r--r--tool/lrama/lib/lrama/grammar/code/no_reference_code.rb28
-rw-r--r--tool/lrama/lib/lrama/grammar/code/printer_code.rb40
-rw-r--r--tool/lrama/lib/lrama/grammar/code/rule_action.rb88
-rw-r--r--tool/lrama/lib/lrama/grammar/counter.rb15
-rw-r--r--tool/lrama/lib/lrama/grammar/destructor.rb9
-rw-r--r--tool/lrama/lib/lrama/grammar/error_token.rb9
-rw-r--r--tool/lrama/lib/lrama/grammar/parameterizing_rule.rb3
-rw-r--r--tool/lrama/lib/lrama/grammar/parameterizing_rule/resolver.rb56
-rw-r--r--tool/lrama/lib/lrama/grammar/parameterizing_rule/rhs.rb15
-rw-r--r--tool/lrama/lib/lrama/grammar/parameterizing_rule/rule.rb17
-rw-r--r--tool/lrama/lib/lrama/grammar/percent_code.rb12
-rw-r--r--tool/lrama/lib/lrama/grammar/precedence.rb11
-rw-r--r--tool/lrama/lib/lrama/grammar/printer.rb9
-rw-r--r--tool/lrama/lib/lrama/grammar/reference.rb14
-rw-r--r--tool/lrama/lib/lrama/grammar/rule.rb60
-rw-r--r--tool/lrama/lib/lrama/grammar/rule_builder.rb268
-rw-r--r--tool/lrama/lib/lrama/grammar/stdlib.y122
-rw-r--r--tool/lrama/lib/lrama/grammar/symbol.rb103
-rw-r--r--tool/lrama/lib/lrama/grammar/symbols.rb1
-rw-r--r--tool/lrama/lib/lrama/grammar/symbols/resolver.rb293
-rw-r--r--tool/lrama/lib/lrama/grammar/type.rb18
-rw-r--r--tool/lrama/lib/lrama/grammar/union.rb10
-rw-r--r--tool/lrama/lib/lrama/lexer.rb477
-rw-r--r--tool/lrama/lib/lrama/lexer/grammar_file.rb31
-rw-r--r--tool/lrama/lib/lrama/lexer/location.rb97
-rw-r--r--tool/lrama/lib/lrama/lexer/token.rb56
-rw-r--r--tool/lrama/lib/lrama/lexer/token/char.rb8
-rw-r--r--tool/lrama/lib/lrama/lexer/token/ident.rb8
-rw-r--r--tool/lrama/lib/lrama/lexer/token/instantiate_rule.rb23
-rw-r--r--tool/lrama/lib/lrama/lexer/token/tag.rb12
-rw-r--r--tool/lrama/lib/lrama/lexer/token/user_code.rb77
-rw-r--r--tool/lrama/lib/lrama/option_parser.rb142
-rw-r--r--tool/lrama/lib/lrama/options.rb24
-rw-r--r--tool/lrama/lib/lrama/output.rb160
-rw-r--r--tool/lrama/lib/lrama/parser.rb2432
-rw-r--r--tool/lrama/lib/lrama/parser/token_scanner.rb55
-rw-r--r--tool/lrama/lib/lrama/report.rb49
-rw-r--r--tool/lrama/lib/lrama/report/duration.rb25
-rw-r--r--tool/lrama/lib/lrama/report/profile.rb14
-rw-r--r--tool/lrama/lib/lrama/state.rb72
-rw-r--r--tool/lrama/lib/lrama/state/reduce_reduce_conflict.rb9
-rw-r--r--tool/lrama/lib/lrama/state/resolved_conflict.rb29
-rw-r--r--tool/lrama/lib/lrama/state/shift_reduce_conflict.rb9
-rw-r--r--tool/lrama/lib/lrama/states.rb106
-rw-r--r--tool/lrama/lib/lrama/states/item.rb81
-rw-r--r--tool/lrama/lib/lrama/states_reporter.rb69
-rw-r--r--tool/lrama/lib/lrama/version.rb2
-rw-r--r--tool/lrama/template/bison/_yacc.h71
-rw-r--r--tool/lrama/template/bison/yacc.c470
-rw-r--r--tool/lrama/template/bison/yacc.h74
-rw-r--r--tool/m4/ruby_default_arch.m425
-rw-r--r--tool/m4/ruby_shared_gc.m419
-rw-r--r--tool/m4/ruby_try_cflags.m413
-rw-r--r--tool/m4/ruby_universal_arch.m46
-rw-r--r--tool/m4/ruby_wasm_tools.m43
-rwxr-xr-xtool/make-snapshot29
-rwxr-xr-xtool/merger.rb12
-rwxr-xr-xtool/missing-baseruby.bat19
-rw-r--r--tool/mk_builtin_loader.rb15
-rwxr-xr-xtool/mkrunnable.rb99
-rwxr-xr-xtool/outdate-bundled-gems.rb101
-rw-r--r--tool/rakelib/sync_tool.rake17
-rwxr-xr-xtool/rbinstall.rb669
-rw-r--r--tool/rbs_skip_tests35
-rwxr-xr-xtool/rbuninstall.rb36
-rwxr-xr-xtool/rdoc-srcdir20
-rwxr-xr-xtool/rjit/bindgen.rb24
-rw-r--r--tool/ruby_vm/helpers/c_escape.rb5
-rw-r--r--tool/ruby_vm/helpers/dumper.rb6
-rw-r--r--tool/ruby_vm/views/_insn_entry.erb5
-rw-r--r--tool/ruby_vm/views/_leaf_helpers.erb25
-rw-r--r--tool/ruby_vm/views/opt_sc.inc.erb40
-rw-r--r--tool/rubyspec_temp.rb13
-rwxr-xr-xtool/runruby.rb9
-rwxr-xr-xtool/sync_default_gems.rb527
-rwxr-xr-xtool/sync_test_lib.rb101
-rw-r--r--tool/test-bundled-gems.rb25
-rw-r--r--tool/test/init.rb18
-rw-r--r--tool/test/runner.rb11
-rwxr-xr-xtool/test/test_sync_default_gems.rb181
-rw-r--r--tool/test/testunit/test4test_timeout.rb15
-rw-r--r--tool/test/testunit/test_hideskip.rb2
-rw-r--r--tool/test/testunit/test_launchable.rb69
-rw-r--r--tool/test/testunit/test_load_failure.rb2
-rw-r--r--tool/test/testunit/test_parallel.rb27
-rw-r--r--tool/test/testunit/test_sorting.rb2
-rw-r--r--tool/test/testunit/test_timeout.rb10
-rw-r--r--tool/test/testunit/tests_for_parallel/slow_helper.rb8
-rw-r--r--tool/test/testunit/tests_for_parallel/test4test_slow_0.rb5
-rw-r--r--tool/test/testunit/tests_for_parallel/test4test_slow_1.rb5
-rw-r--r--tool/test/webrick/test_cgi.rb32
-rw-r--r--tool/test/webrick/test_filehandler.rb10
-rw-r--r--tool/test/webrick/utils.rb20
-rwxr-xr-x[-rw-r--r--]tool/test/webrick/webrick.cgi4
-rw-r--r--tool/test_for_warn_bundled_gems/.gitignore1
-rw-r--r--tool/test_for_warn_bundled_gems/Gemfile0
-rw-r--r--tool/test_for_warn_bundled_gems/Gemfile.lock11
-rw-r--r--tool/test_for_warn_bundled_gems/README.md3
-rwxr-xr-xtool/test_for_warn_bundled_gems/test.sh53
-rw-r--r--tool/test_for_warn_bundled_gems/test_no_warn_dash_gem.rb8
-rw-r--r--tool/test_for_warn_bundled_gems/test_no_warn_dependency.rb10
-rw-r--r--tool/test_for_warn_bundled_gems/test_no_warn_sub_feature.rb8
-rw-r--r--tool/test_for_warn_bundled_gems/test_warn_bootsnap.rb11
-rw-r--r--tool/test_for_warn_bundled_gems/test_warn_bootsnap_rubyarchdir_gem.rb11
-rw-r--r--tool/test_for_warn_bundled_gems/test_warn_bundle_exec.rb1
-rwxr-xr-xtool/test_for_warn_bundled_gems/test_warn_bundle_exec_shebang.rb3
-rw-r--r--tool/test_for_warn_bundled_gems/test_warn_bundled_gems.rb8
-rw-r--r--tool/test_for_warn_bundled_gems/test_warn_dash_gem.rb7
-rw-r--r--tool/test_for_warn_bundled_gems/test_warn_dependency.rb8
-rw-r--r--tool/test_for_warn_bundled_gems/test_warn_sub_feature.rb7
-rw-r--r--tool/test_for_warn_bundled_gems/test_warn_zeitwerk.rb12
-rw-r--r--tool/transcode-tblgen.rb6
-rwxr-xr-xtool/update-NEWS-gemlist.rb12
-rw-r--r--tool/update-NEWS-refs.rb7
-rwxr-xr-xtool/update-bundled_gems.rb43
-rwxr-xr-xtool/update-deps24
-rw-r--r--trace_point.rb16
-rw-r--r--transcode.c27
-rw-r--r--transient_heap.c976
-rw-r--r--transient_heap.h63
-rw-r--r--universal_parser.c233
-rw-r--r--util.c15
-rw-r--r--variable.c1182
-rw-r--r--variable.h13
-rw-r--r--vcpkg.json11
-rw-r--r--version.c88
-rw-r--r--version.h10
-rw-r--r--vm.c953
-rw-r--r--vm_args.c216
-rw-r--r--vm_backtrace.c516
-rw-r--r--vm_callinfo.h113
-rw-r--r--vm_core.h278
-rw-r--r--vm_debug.h2
-rw-r--r--vm_dump.c383
-rw-r--r--vm_eval.c404
-rw-r--r--vm_exec.c59
-rw-r--r--vm_exec.h23
-rw-r--r--vm_insnhelper.c731
-rw-r--r--vm_insnhelper.h50
-rw-r--r--vm_method.c365
-rw-r--r--vm_opts.h7
-rw-r--r--vm_sync.c112
-rw-r--r--vm_trace.c429
-rw-r--r--vsnprintf.c2
-rw-r--r--warning.rb2
-rw-r--r--wasm/runtime.c7
-rw-r--r--wasm/setjmp.c29
-rw-r--r--weakmap.c1057
-rw-r--r--win32/Makefile.sub51
-rw-r--r--win32/file.c33
-rw-r--r--win32/file.h3
-rwxr-xr-xwin32/mkexports.rb2
-rw-r--r--win32/setup.mak2
-rw-r--r--win32/win32.c153
-rw-r--r--yjit.c216
-rw-r--r--yjit.h39
-rw-r--r--yjit.rb270
-rw-r--r--yjit/Cargo.lock15
-rw-r--r--yjit/Cargo.toml5
-rw-r--r--yjit/bindgen/Cargo.lock163
-rw-r--r--yjit/bindgen/Cargo.toml2
-rw-r--r--yjit/bindgen/src/main.rs54
-rw-r--r--yjit/src/asm/arm64/arg/bitmask_imm.rs2
-rw-r--r--yjit/src/asm/arm64/inst/madd.rs73
-rw-r--r--yjit/src/asm/arm64/inst/mod.rs4
-rw-r--r--yjit/src/asm/arm64/inst/smulh.rs60
-rw-r--r--yjit/src/asm/arm64/mod.rs102
-rw-r--r--yjit/src/asm/mod.rs88
-rw-r--r--yjit/src/asm/x86_64/mod.rs77
-rw-r--r--yjit/src/asm/x86_64/tests.rs18
-rw-r--r--yjit/src/backend/arm64/mod.rs370
-rw-r--r--yjit/src/backend/ir.rs496
-rw-r--r--yjit/src/backend/mod.rs6
-rw-r--r--yjit/src/backend/tests.rs45
-rw-r--r--yjit/src/backend/x86_64/mod.rs390
-rw-r--r--yjit/src/codegen.rs5495
-rw-r--r--yjit/src/core.rs1248
-rw-r--r--yjit/src/cruby.rs143
-rw-r--r--yjit/src/cruby_bindings.inc.rs765
-rw-r--r--yjit/src/disasm.rs39
-rw-r--r--yjit/src/invariants.rs189
-rw-r--r--yjit/src/lib.rs3
-rw-r--r--yjit/src/options.rs211
-rw-r--r--yjit/src/stats.rs480
-rw-r--r--yjit/src/utils.rs24
-rw-r--r--yjit/src/virtualmem.rs90
-rw-r--r--yjit/src/yjit.rs133
-rw-r--r--yjit/yjit.mk17
5951 files changed, 340972 insertions, 243733 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
deleted file mode 100644
index 2762a5759c..0000000000
--- a/.appveyor.yml
+++ /dev/null
@@ -1,132 +0,0 @@
----
-version: '{build}'
-init:
- - git config --global user.name git
- - git config --global user.email svn-admin@ruby-lang.org
- - git config --global core.autocrlf false
- - git config --global core.eol lf
- - git config --global advice.detachedHead 0
-shallow_clone: true
-clone_depth: 10
-platform:
- - x64
-skip_commits:
- message: /\[DOC\]/
- files:
- - doc/*
- - '**/*.md'
- - '**/*.rdoc'
- - '**/.document'
- - '**/*.[1-8]'
- - '**/*.ronn'
-environment:
- ruby_version: "25-%Platform%"
- matrix:
- # Test only the oldest supported version because AppVeyor is unstable, its concurrency
- # is limited, and compatibility issues that happen only in newer versions are rare.
- # You may test some other stuff on GitHub Actions instead.
- - build: vs
- vs: 120
- ssl: OpenSSL-v111
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
- GEMS_FOR_TEST: ""
- RELINE_TEST_ENCODING: "UTF-8"
-cache:
- - c:\Tools\vcpkg\installed\
-for:
--
- matrix:
- only:
- - build: vs
- install:
- - ver
- - chcp
- - SET BITS=%Platform:x86=32%
- - SET BITS=%BITS:x=%
- - SET OPENSSL_DIR=C:\%ssl%-Win%BITS%
- - cd C:\Tools\vcpkg
- - git pull -q
- - .\bootstrap-vcpkg.bat
- - cd %APPVEYOR_BUILD_FOLDER%
- - vcpkg --triplet %Platform%-windows install --x-use-aria2 libffi libyaml readline zlib
- - CALL SET vcvars=%%^VS%VS%COMNTOOLS^%%..\..\VC\vcvarsall.bat
- - SET vcvars
- - '"%vcvars%" %Platform:x64=amd64%'
- - SET ruby_path=C:\Ruby%ruby_version:-x86=%
- - SET PATH=\usr\local\bin;%ruby_path%\bin;%PATH%;C:\msys64\mingw64\bin;C:\msys64\usr\bin
- - ruby --version
- - 'cl'
- - echo> Makefile srcdir=.
- - echo>> Makefile MSC_VER=0
- - echo>> Makefile RT=none
- - echo>> Makefile RT_VER=0
- - echo>> Makefile BUILTIN_ENCOBJS=nul
- - type win32\Makefile.sub >> Makefile
- - nmake %mflags% up VCSUP="echo Update OK"
- - nmake %mflags% extract-extlibs
- - del Makefile
- - mkdir \usr\local\bin
- - mkdir \usr\local\include
- - mkdir \usr\local\lib
- - for %%I in (%OPENSSL_DIR%\*.dll) do mklink /h \usr\local\bin\%%~nxI %%I
- - for %%I in (c:\Tools\vcpkg\installed\%Platform%-windows\bin\*.dll) do (
- if not %%~nI == readline mklink \usr\local\bin\%%~nxI %%I
- )
- - attrib +r /s /d
- - mkdir %Platform%-mswin_%vs%
- build_script:
- - set HAVE_GIT=no
- - cd %APPVEYOR_BUILD_FOLDER%
- - cd %Platform%-mswin_%vs%
- - >-
- ..\win32\configure.bat
- --with-opt-dir="/usr/local;c:/Tools/vcpkg/installed/%Platform%-windows"
- --with-openssl-dir=%OPENSSL_DIR:\=/%
- - nmake -l
- - nmake install-nodoc
- - \usr\bin\ruby -v -e "p :locale => Encoding.find('locale'), :filesystem => Encoding.find('filesystem')"
- - if not "%GEMS_FOR_TEST%" == "" \usr\bin\gem install --no-document %GEMS_FOR_TEST%
- - \usr\bin\ruby -ropenssl -e "puts 'Build ' + OpenSSL::OPENSSL_VERSION, 'Runtime ' + OpenSSL::OPENSSL_LIBRARY_VERSION"
- test_script:
- - set /a JOBS=%NUMBER_OF_PROCESSORS%
- - nmake -l "TESTOPTS=-v -q" btest
- - nmake -l "TESTOPTS=-v -q" test-basic
- - >-
- nmake -l "TESTOPTS=-v --timeout-scale=3.0
- --excludes=../test/excludes/_appveyor -j%JOBS%
- --exclude win32ole
- --exclude test_bignum
- --exclude test_syntax
- --exclude test_open-uri
- --exclude test_bundled_ca
- " test-all
- # separately execute tests without -j which may crash worker with -j.
- - >-
- nmake -l
- "TESTOPTS=-v --timeout-scale=3.0 --excludes=../test/excludes/_appveyor"
- TESTS="
- ../test/win32ole
- ../test/ruby/test_bignum.rb
- ../test/ruby/test_syntax.rb
- ../test/open-uri/test_open-uri.rb
- ../test/rubygems/test_bundled_ca.rb
- " test-all
- - nmake -l test-spec MSPECOPT=-fs # not using `-j` because sometimes `mspec -j` silently dies on Windows
-notifications:
- - provider: Webhook
- method: POST
- url:
- secure: CcFlJNDJ/a6to7u3Z4Fnz6dScEPNx7hTha2GkSRlV+1U6dqmxY/7uBcLXYb9gR3jfQk6w+2o/HrjNAyXMNGU/JOka3s2WRI4VKitzM+lQ08owvJIh0R7LxrGH0J2e81U # ruby-lang slack: ruby/simpler-alerts-bot
- body: >-
- {{^isPullRequest}}
- {
- "ci": "AppVeyor CI",
- "env": "Visual Studio 2013",
- "url": "{{buildUrl}}",
- "commit": "{{commitId}}",
- "branch": "{{branch}}"
- }
- {{/isPullRequest}}
- on_build_success: false
- on_build_failure: true
- on_build_status_changed: false
diff --git a/.cirrus.yml b/.cirrus.yml
deleted file mode 100644
index be76e4ab4a..0000000000
--- a/.cirrus.yml
+++ /dev/null
@@ -1,133 +0,0 @@
-# This CI is used to test Arm cases. We can set the maximum 16 tasks.
-# The entire testing design is inspired from .github/workflows/compilers.yml.
-
-# By default, Cirrus mounts an empty volume to `/tmp`
-# which triggers all sorts of warnings like "system temporary path is world-writable: /tmp".
-# Lets workaround it by specifying a custom volume mount point.
-env:
- CIRRUS_VOLUME: /cirrus-ci-volume
- LANG: C.UTF-8
-
-task:
- name: Arm64 Graviton2 / $CC
- skip: "changesIncludeOnly('doc/**', '**.{md,rdoc,ronn,[1-8]}', '.document')"
- arm_container:
- # We use the arm64 images at https://github.com/ruby/ruby-ci-image/pkgs/container/ruby-ci-image .
- image: ghcr.io/ruby/ruby-ci-image:$CC
- # Define the used cpu core in each matrix task. We can use total 16 cpu
- # cores in entire matrix. [cpu] = [total cpu: 16] / [number of tasks]
- cpu: 8
- # We can request maximum 4 GB per cpu.
- # [memory per task] = [memory per cpu: 4 GB] * [cpu]
- memory: 32G
- env:
- CIRRUS_CLONE_DEPTH: 50
- optflags: '-O1'
- debugflags: '-ggdb3'
- RUBY_PREFIX: /tmp/ruby-prefix
- RUBY_DEBUG: ci rgengc
- RUBY_TESTOPTS: >-
- -q
- --color=always
- --tty=no
- matrix:
- CC: clang-12
- CC: gcc-11
- id_script: id
- set_env_script:
- # Set `GNUMAKEFLAGS`, because the flags are GNU make specific. Note using
- # the `make` environment variable used in compilers.yml causes some rubygems
- # tests to fail.
- # https://github.com/rubygems/rubygems/issues/4921
- - echo "GNUMAKEFLAGS=-s -j$((1 + $CIRRUS_CPU))" >> "$CIRRUS_ENV"
- - cat "$CIRRUS_ENV"
- # Arm containers are executed in AWS's EKS, and it's not yet supporting IPv6
- # See https://github.com/aws/containers-roadmap/issues/835
- disable_ipv6_script: sudo ./tool/disable_ipv6.sh
- autogen_script: ./autogen.sh
- configure_script: >-
- ./configure -C
- --enable-debug-env
- --disable-install-doc
- --with-ext=-test-/cxxanyargs,+
- --prefix="$RUBY_PREFIX"
- make_extract-extlibs_script: make extract-extlibs
- make_incs_script: make incs
- make_script: make
- make_test_script: make test
- make_install_script: make install
- install_gems_for_test_script: $RUBY_PREFIX/bin/gem install --no-doc timezone tzinfo
- make_test-tool_script: make test-tool
- make_test-all_script: make test-all
- make_test-spec_script: make test-spec
-
-# The following is to test YJIT on ARM64 CPUs available on Cirrus CI
-yjit_task:
- name: Arm64 Graviton2 / $CC YJIT
- auto_cancellation: $CIRRUS_BRANCH != 'master'
- skip: "changesIncludeOnly('doc/**', '**.{md,rdoc,ronn,[1-8]}', '.document')"
- arm_container:
- # We use the arm64 images at https://github.com/ruby/ruby-ci-image/pkgs/container/ruby-ci-image .
- image: ghcr.io/ruby/ruby-ci-image:$CC
- # Define the used cpu core in each matrix task. We can use total 16 cpu
- # cores in entire matrix. [cpu] = [total cpu: 16] / [number of tasks]
- cpu: 8
- # We can request maximum 4 GB per cpu.
- # [memory per task] = [memory per cpu: 4 GB] * [cpu]
- memory: 32G
- env:
- CIRRUS_CLONE_DEPTH: 50
- optflags: '-O1'
- debugflags: '-ggdb3'
- RUBY_PREFIX: /tmp/ruby-prefix
- RUBY_DEBUG: ci rgengc
- RUBY_TESTOPTS: >-
- -q
- --color=always
- --tty=no
- matrix:
- - CC: clang-12
- configure: --enable-yjit=dev
- rustup_init: --default-toolchain=1.58.0
- - CC: gcc-11
- configure: --enable-yjit
- id_script: id
- set_env_script:
- # Set `GNUMAKEFLAGS`, because the flags are GNU make specific. Note using
- # the `make` environment variable used in compilers.yml causes some rubygems
- # tests to fail.
- # https://github.com/rubygems/rubygems/issues/4921
- - echo "GNUMAKEFLAGS=-s -j$((1 + $CIRRUS_CPU))" >> "$CIRRUS_ENV"
- - echo RUST_BACKTRACE=1 >> "$CIRRUS_ENV"
- - cat "$CIRRUS_ENV"
- # Arm containers are executed in AWS's EKS, and it's not yet supporting IPv6
- # See https://github.com/aws/containers-roadmap/issues/835
- disable_ipv6_script: sudo ./tool/disable_ipv6.sh
- install_rust_script:
- - sudo apt-get update -y
- - sudo apt-get install -y curl
- - "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y $rustup_init"
- autogen_script: ./autogen.sh
- configure_script: >-
- source $HOME/.cargo/env && ./configure -C
- --enable-debug-env
- --disable-install-doc
- --with-ext=-test-/cxxanyargs,+
- --prefix="$RUBY_PREFIX"
- $configure
- make_miniruby_script: source $HOME/.cargo/env && make miniruby
- make_bindgen_script: |
- if [[ "$CC" = "clang-12" ]]; then
- source $HOME/.cargo/env && make yjit-bindgen
- else
- echo "only running bindgen on clang image"
- fi
- boot_miniruby_script: ./miniruby --yjit-call-threshold=1 -e0
- test_dump_insns_script: ./miniruby --yjit-call-threshold=1 --yjit-dump-insns -e0
- output_stats_script: ./miniruby --yjit-call-threshold=1 --yjit-stats -e0
- full_build_script: source $HOME/.cargo/env && make
- cargo_test_script: source $HOME/.cargo/env && cd yjit && cargo test
- make_test_script: source $HOME/.cargo/env && make test RUN_OPTS="--yjit-call-threshold=1 --yjit-verify-ctx"
- make_test_all_script: source $HOME/.cargo/env && make test-all RUN_OPTS="--yjit-call-threshold=1 --yjit-verify-ctx" TESTOPTS="$RUBY_TESTOPTS"
- make_test_spec_script: source $HOME/.cargo/env && make test-spec RUN_OPTS="--yjit-call-threshold=1 --yjit-verify-ctx"
- clippy_script: source $HOME/.cargo/env && cd yjit && cargo clippy --all-targets --all-features
diff --git a/.document b/.document
index e875e42546..0665d415b9 100644
--- a/.document
+++ b/.document
@@ -31,6 +31,9 @@ trace_point.rb
warning.rb
yjit.rb
+# Errno::*
+known_errors.inc
+
# the lib/ directory (which has its own .document file)
lib
diff --git a/.gdbinit b/.gdbinit
index f5980388f2..7dd3336e28 100644
--- a/.gdbinit
+++ b/.gdbinit
@@ -689,11 +689,6 @@ define nd_stts
end
-define nd_entry
- printf "%su3.entry%s: ", $color_highlite, $color_end
- p ($arg0).u3.entry
-end
-
define nd_vid
printf "%su1.id%s: ", $color_highlite, $color_end
p ($arg0).u1.id
@@ -1279,7 +1274,7 @@ document rb_count_objects
Counts all objects grouped by type.
end
-# Details: https://bugs.ruby-lang.org/projects/ruby-master/wiki/MachineInstructionsTraceWithGDB
+# Details: https://github.com/ruby/ruby/wiki/Machine-Instructions-Trace-with-GDB
define trace_machine_instructions
set logging enabled
set height 0
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index 6c35dbcf83..1cafd3df75 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -22,3 +22,11 @@ f28287d34c03f472ffe90ea262bdde9affd4b965
# Make benchmark indentation consistent
fc4acf8cae82e5196186d3278d831f2438479d91
+
+# Make prism_compile.c indentation consistent
+40b2c8e5e7e6e5f83cee9276dc9c1922a69292d6
+d2c5867357ed88eccc28c2b3bd4a46e206e7ff85
+
+# Miss-and-revived commits
+a0f7de814ae5c299d6ce99bed5fb308a05d50ba0
+d4e24021d39e1f80f0055b55d91f8d5f22e15084
diff --git a/.github/actions/launchable/setup/action.yml b/.github/actions/launchable/setup/action.yml
new file mode 100644
index 0000000000..6d50318ded
--- /dev/null
+++ b/.github/actions/launchable/setup/action.yml
@@ -0,0 +1,144 @@
+name: Set up Launchable
+description: >-
+ Install the required dependencies and execute the necessary Launchable commands for test recording
+
+inputs:
+ report-path:
+ default: launchable_reports.json
+ required: true
+ description: The file path of the test report for uploading to Launchable
+
+ os:
+ required: true
+ description: The operating system that CI runs on. This value is used in Launchable flavor.
+
+ test-opts:
+ default: none
+ required: false
+ description: >-
+ Test options that determine how tests are run.
+ This value is used in the Launchable flavor.
+
+ launchable-token:
+ required: false
+ description: >-
+ Launchable token is needed if you want to run Launchable on your forked repository.
+ See https://github.com/ruby/ruby/wiki/CI-Servers#launchable-ci for details.
+
+ builddir:
+ required: false
+ default: ${{ github.workspace }}
+ description: >-
+ Directory to create Launchable report file.
+
+ srcdir:
+ required: false
+ default: ${{ github.workspace }}
+ description: >-
+ Directory to (re-)checkout source codes. Launchable retrives the commit information
+ from the directory.
+
+runs:
+ using: composite
+
+ steps:
+ - name: Enable Launchable conditionally
+ id: enable-launchable
+ run: echo "enable-launchable=true" >> $GITHUB_OUTPUT
+ shell: bash
+ if: >-
+ ${{
+ (github.repository == 'ruby/ruby' ||
+ (github.repository != 'ruby/ruby' && env.LAUNCHABLE_TOKEN)) &&
+ (matrix.test_task == 'check' || matrix.test_task == 'test-all')
+ }}
+
+ # Launchable CLI requires Python and Java.
+ # https://www.launchableinc.com/docs/resources/cli-reference/
+ - name: Set up Python
+ uses: actions/setup-python@871daa956ca9ea99f3c3e30acb424b7960676734 # v5.0.0
+ with:
+ python-version: "3.x"
+ if: steps.enable-launchable.outputs.enable-launchable
+
+ - name: Set up Java
+ uses: actions/setup-java@7a445ee88d4e23b52c33fdc7601e40278616c7f8 # v4.0.0
+ with:
+ distribution: 'temurin'
+ java-version: '17'
+ if: steps.enable-launchable.outputs.enable-launchable
+
+ - name: Set environment variables for Launchable
+ shell: bash
+ run: |
+ : # GITHUB_PULL_REQUEST_URL are used for commenting test reports in Launchable Github App.
+ : # https://github.com/launchableinc/cli/blob/v1.80.1/launchable/utils/link.py#L42
+ echo "GITHUB_PULL_REQUEST_URL=${{ github.event.pull_request.html_url }}" >> $GITHUB_ENV
+ : # The following envs are necessary in Launchable tokenless authentication.
+ : # https://github.com/launchableinc/cli/blob/v1.80.1/launchable/utils/authentication.py#L20
+ echo "LAUNCHABLE_ORGANIZATION=${{ github.repository_owner }}" >> $GITHUB_ENV
+ echo "LAUNCHABLE_WORKSPACE=${{ github.event.repository.name }}" >> $GITHUB_ENV
+ : # https://github.com/launchableinc/cli/blob/v1.80.1/launchable/utils/authentication.py#L71
+ echo "GITHUB_PR_HEAD_SHA=${{ github.event.pull_request.head.sha || github.sha }}" >> $GITHUB_ENV
+ echo "LAUNCHABLE_TOKEN=${{ inputs.launchable-token }}" >> $GITHUB_ENV
+ if: steps.enable-launchable.outputs.enable-launchable
+
+ - name: Set up Launchable
+ shell: bash
+ working-directory: ${{ inputs.srcdir }}
+ run: |
+ set -x
+ PATH=$PATH:$(python -msite --user-base)/bin
+ echo "PATH=$PATH" >> $GITHUB_ENV
+ pip install --user launchable
+ launchable verify || true
+ : # The build name cannot include a slash, so we replace the string here.
+ github_ref="${{ github.ref }}"
+ github_ref="${github_ref//\//_}"
+ : # With the --name option, we need to configure a unique identifier for this build.
+ : # To avoid setting the same build name as the CI which runs on other branches, we use the branch name here.
+ : #
+ : # FIXME: Need to fix `WARNING: Failed to process a change to a file`.
+ : # https://github.com/launchableinc/cli/issues/786
+ launchable record build --name ${github_ref}_${GITHUB_PR_HEAD_SHA}
+ echo "TESTS=${TESTS} --launchable-test-reports=${{ inputs.report-path }}" >> $GITHUB_ENV
+ if: steps.enable-launchable.outputs.enable-launchable
+
+ - name: Variables to report Launchable
+ id: variables
+ shell: bash
+ run: |
+ set -x
+ : # flavor
+ test_opts="${{ inputs.test-opts }}"
+ test_opts="${test_opts// /}"
+ test_opts="${test_opts//=/:}"
+ echo test-opts="$test_opts" >> $GITHUB_OUTPUT
+ : # report-path from srcdir
+ if [ "${srcdir}" = "${{ github.workspace }}" ]; then
+ dir=
+ else
+ # srcdir must be equal to or under workspace
+ dir=$(echo ${srcdir:+${srcdir}/} | sed 's:[^/][^/]*/:../:g')
+ fi
+ report_path="${dir}${builddir:+${builddir}/}${report_path}"
+ echo report-path="${report_path}" >> $GITHUB_OUTPUT
+ if: steps.enable-launchable.outputs.enable-launchable
+ env:
+ srcdir: ${{ inputs.srcdir }}
+ builddir: ${{ inputs.builddir }}
+ report_path: ${{ inputs.report-path }}
+
+ - name: Record test results in Launchable
+ uses: gacts/run-and-post-run@674528335da98a7afc80915ff2b4b860a0b3553a # v1.4.0
+ with:
+ shell: bash
+ working-directory: ${{ inputs.srcdir }}
+ post: |
+ : # record
+ launchable record tests --flavor os=${{ inputs.os }} --flavor test_task=${{ matrix.test_task }} --flavor test_opts=${test_opts} raw ${report_path}
+ rm -f ${report_path}
+ if: ${{ always() && steps.enable-launchable.outputs.enable-launchable }}
+ env:
+ test_opts: ${{ steps.variables.outputs.test-opts }}
+ report_path: ${{ steps.variables.outputs.report-path }}
diff --git a/.github/actions/setup/directories/action.yml b/.github/actions/setup/directories/action.yml
new file mode 100644
index 0000000000..a59f575c99
--- /dev/null
+++ b/.github/actions/setup/directories/action.yml
@@ -0,0 +1,174 @@
+name: Setup directories etc.
+description: >-
+ Set up the source code and build directories (plus some
+ environmental tweaks)
+
+inputs:
+ srcdir:
+ required: false
+ default: ${{ github.workspace }}
+ description: >-
+ Directory to (re-)checkout source codes. This will be created
+ if absent. If there is no `configure` file that is also
+ generated inside.
+
+ builddir:
+ required: false
+ default: ${{ github.workspace }}
+ description: >-
+ Where binaries and other generated contents go. This will be
+ created if absent.
+
+ makeup:
+ required: false
+ type: boolean
+ # Note that `default: false` evaluates to a string constant
+ # `'false'`, which is a truthy value :sigh:
+ # https://github.com/actions/runner/issues/2238
+ default: ''
+ description: >-
+ If set to true, additionally runs `make up`.
+
+ checkout:
+ required: false
+ type: boolean
+ default: true
+ description: >-
+ If set to '' (false), skip running actions/checkout. This is useful when
+ you don't want to overwrite a GitHub token that is already set up.
+
+ dummy-files:
+ required: false
+ type: boolean
+ default: ''
+ description: >-
+ If set to true, creates dummy files in build dir.
+
+ fetch-depth:
+ required: false
+ default: '1'
+ description: The depth of commit history fetched from the remote repository
+
+ clean:
+ required: false
+ type: boolean
+ default: ''
+ description: >-
+ If set to true, clean build directory.
+
+outputs: {} # nothing?
+
+runs:
+ using: composite
+
+ steps:
+ # Note that `shell: bash` works on both Windows and Linux, but not
+ # `shell: sh`. This is because GitHub hosted Windows runners have
+ # their bash manually installed.
+ - shell: bash
+ run: |
+ mkdir -p ${{ inputs.srcdir }}
+ mkdir -p ${{ inputs.builddir }}
+
+ # Did you know that actions/checkout works without git(1)? We are
+ # checking that here.
+ - id: which
+ shell: bash
+ run: |
+ echo "git=`command -v git`" >> "$GITHUB_OUTPUT"
+ echo "sudo=`command -v sudo`" >> "$GITHUB_OUTPUT"
+ echo "autoreconf=`command -v autoreconf`" >> "$GITHUB_OUTPUT"
+
+ - if: steps.which.outputs.git
+ shell: bash
+ run: |
+ git config --global core.autocrlf false
+ git config --global core.eol lf
+ git config --global advice.detachedHead 0
+ git config --global init.defaultBranch garbage
+
+ - if: inputs.checkout
+ uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
+ with:
+ path: ${{ inputs.srcdir }}
+ fetch-depth: ${{ inputs.fetch-depth }}
+
+ - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
+ with:
+ path: ${{ inputs.srcdir }}/.downloaded-cache
+ key: downloaded-cache
+
+ - if: steps.which.outputs.autoreconf
+ shell: bash
+ working-directory: ${{ inputs.srcdir }}
+ run: ./autogen.sh
+
+ # This is for MinGW.
+ - if: runner.os == 'Windows'
+ shell: bash
+ run: echo "GNUMAKEFLAGS=-j$((2 * NUMBER_OF_PROCESSORS))" >> $GITHUB_ENV
+
+ - if: runner.os == 'Linux'
+ shell: bash
+ run: echo "GNUMAKEFLAGS=-sj$((1 + $(nproc --all)))" >> "$GITHUB_ENV"
+
+ # macOS' GNU make is so old that they doesn't understand `GNUMAKEFLAGS`.
+ - if: runner.os == 'macOS'
+ shell: bash
+ run: echo "MAKEFLAGS=-j$((1 + $(sysctl -n hw.activecpu)))" >> "$GITHUB_ENV"
+
+ - if: inputs.makeup
+ shell: bash
+ working-directory: ${{ inputs.srcdir }}
+ run: |
+ touch config.status
+ touch .rbconfig.time
+ sed -f tool/prereq.status template/Makefile.in > Makefile
+ sed -f tool/prereq.status template/GNUmakefile.in > GNUmakefile
+ make up
+
+ # Cleanup, runs even on failure
+ - if: always() && inputs.makeup
+ shell: bash
+ working-directory: ${{ inputs.srcdir }}
+ run: rm -f config.status Makefile rbconfig.rb .rbconfig.time
+
+ - if: steps.which.outputs.sudo
+ shell: bash
+ run: |
+ sudo chmod -R go-w /usr/share
+ chmod -v go-w $HOME $HOME/.config || :
+ sudo bash -c 'IFS=:; for d in '"$PATH"'; do chmod -v go-w $d; done' || :
+
+ - if: inputs.dummy-files == 'true'
+ shell: bash
+ id: dummy-files
+ working-directory: ${{ inputs.builddir }}
+ run: |
+ : Create dummy files in build dir
+ set {{a..z},{A..Z},{0..9},foo,bar,test,zzz}.rb
+ for file; do \
+ echo > $file "raise 'do not load $file'"; \
+ done
+ # drop {a..z}.rb if case-insensitive filesystem
+ grep -F A.rb a.rb > /dev/null && set "${@:27}"
+ echo clean="cd ${{ inputs.builddir }} && rm $*" >> $GITHUB_OUTPUT
+
+ - if: inputs.clean == 'true'
+ shell: bash
+ id: clean
+ run: |
+ echo distclean='make -C ${{ inputs.builddir }} distclean' >> $GITHUB_OUTPUT
+ echo remained-files='find ${{ inputs.builddir }} -ls' >> $GITHUB_OUTPUT
+ [ "${{ inputs.builddir }}" = "${{ inputs.srcdir }}" ] ||
+ echo final='rmdir ${{ inputs.builddir }}' >> $GITHUB_OUTPUT
+
+ - name: clean
+ uses: gacts/run-and-post-run@7aec950f3b114c4fcf6012070c3709ecff0eb6f8 # v1.4.0
+ with:
+ working-directory:
+ post: |
+ ${{ steps.dummy-files.outputs.clean }}
+ ${{ steps.clean.outputs.distclean }}
+ ${{ steps.clean.outputs.remained-files }}
+ ${{ steps.clean.outputs.final }}
diff --git a/.github/actions/setup/macos/action.yml b/.github/actions/setup/macos/action.yml
new file mode 100644
index 0000000000..896930de84
--- /dev/null
+++ b/.github/actions/setup/macos/action.yml
@@ -0,0 +1,24 @@
+name: Setup macOS environment
+description: >-
+ Installs necessary packages via Homebrew.
+
+inputs: {} # nothing?
+
+outputs: {} # nothing?
+
+runs:
+ using: composite
+
+ steps:
+ - name: brew
+ shell: bash
+ run: |
+ brew install --quiet gmp libffi openssl@1.1 zlib autoconf automake libtool
+
+ - name: Set ENV
+ shell: bash
+ run: |
+ for lib in openssl@1.1 gmp; do
+ CONFIGURE_ARGS="${CONFIGURE_ARGS:+$CONFIGURE_ARGS }--with-${lib%@*}-dir=$(brew --prefix $lib)"
+ done
+ echo CONFIGURE_ARGS="${CONFIGURE_ARGS}" >> $GITHUB_ENV
diff --git a/.github/actions/setup/ubuntu/action.yml b/.github/actions/setup/ubuntu/action.yml
new file mode 100644
index 0000000000..a9e5b41951
--- /dev/null
+++ b/.github/actions/setup/ubuntu/action.yml
@@ -0,0 +1,53 @@
+name: Setup ubuntu environment
+description: >-
+ At the beginning there was no way but to copy & paste `apt-get`
+ everywhere. But now that we have composite actions, it seems better
+ merge them into one.
+
+inputs:
+ arch:
+ required: false
+ default: ''
+ description: >-
+ Architecture. Because we run this on a GitHub-hosted runner
+ acceptable value for this input is very limited.
+
+outputs:
+ arch:
+ value: ${{ steps.uname.outputs.uname }}
+ description: >-
+ Actual architecture. This could be different from the one
+ passed to the `inputs.arch`. For instance giving `i386` to this
+ action yields `i686`.
+
+runs:
+ using: composite
+
+ steps:
+ - name: set SETARCH
+ shell: bash
+ run: echo "SETARCH=${setarch}" >> "$GITHUB_ENV"
+ env:
+ setarch: ${{ inputs.arch && format('setarch {0} --', inputs.arch) }}
+
+ - id: uname
+ name: uname
+ shell: bash
+ run: |
+ echo uname=`${SETARCH} uname -m` >> "$GITHUB_OUTPUT"
+ echo dpkg=`${SETARCH} uname -m | sed s/686/386/` >> "$GITHUB_OUTPUT"
+
+ - name: apt-get
+ shell: bash
+ env:
+ arch: ${{ inputs.arch && format(':{0}', steps.uname.outputs.dpkg) || '' }}
+ run: |
+ set -x
+ ${arch:+sudo dpkg --add-architecture ${arch#:}}
+ sudo apt-get update -qq || :
+ sudo apt-get install --no-install-recommends -qq -y -o=Dpkg::Use-Pty=0 \
+ ${arch:+cross}build-essential${arch/:/-} \
+ libssl-dev${arch} libyaml-dev${arch} libreadline6-dev${arch} \
+ zlib1g-dev${arch} libncurses5-dev${arch} libffi-dev${arch} \
+ autoconf ruby
+ sudo apt-get install -qq -y pkg-config${arch} || :
diff --git a/.github/actions/slack/action.yml b/.github/actions/slack/action.yml
new file mode 100644
index 0000000000..c98be085a8
--- /dev/null
+++ b/.github/actions/slack/action.yml
@@ -0,0 +1,39 @@
+name: Post a message to slack
+description: >-
+ We have our ruby/action-slack webhook. However its arguments are
+ bit verbose to be listed in every workflow files. Better merge them
+ into one.
+
+inputs:
+ SLACK_WEBHOOK_URL:
+ required: true
+ description: >-
+ The URL to post the payload. This is an input because it tends
+ to be stored in a secrets valut and a composite action cannot
+ look into one.
+
+ label:
+ required: false
+ description: >-
+ Human-readable description of the run, something like "DEBUG=1".
+ This need not be unique among runs.
+
+outputs: {} # Nothing?
+
+runs:
+ using: composite
+
+ steps:
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ with:
+ payload: |
+ {
+ "ci": "GitHub Actions",
+ "env": "${{ github.workflow }}${{ inputs.label && format(' / {0}', inputs.label) }}",
+ "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
+ "commit": "${{ github.sha }}",
+ "branch": "${{ github.ref_name }}"
+ }
+ env:
+ SLACK_WEBHOOK_URL: ${{ inputs.SLACK_WEBHOOK_URL }}
+ if: ${{github.event_name == 'push' && startsWith(github.repository, 'ruby/')}}
diff --git a/.github/auto_request_review.yml b/.github/auto_request_review.yml
index f694a1c72f..8726df577d 100644
--- a/.github/auto_request_review.yml
+++ b/.github/auto_request_review.yml
@@ -5,7 +5,6 @@ files:
'doc/yjit/*': [team:yjit]
'bootstraptest/test_yjit*': [team:yjit]
'test/ruby/test_yjit*': [team:yjit]
- '.github/workflows/yjit*': [team:yjit]
options:
ignore_draft: true
# This currently doesn't work as intended. We want to skip reviews when only
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 6778b0493a..426893be2a 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -4,3 +4,15 @@ updates:
directory: '/'
schedule:
interval: 'daily'
+ - package-ecosystem: 'github-actions'
+ directory: '/.github/actions/slack'
+ schedule:
+ interval: 'daily'
+ - package-ecosystem: 'github-actions'
+ directory: '/.github/actions/setup/directories'
+ schedule:
+ interval: 'daily'
+ - package-ecosystem: 'cargo'
+ directory: '/yjit'
+ schedule:
+ interval: 'daily'
diff --git a/.github/workflows/annocheck.yml b/.github/workflows/annocheck.yml
index 7c26f9276d..89e11d103e 100644
--- a/.github/workflows/annocheck.yml
+++ b/.github/workflows/annocheck.yml
@@ -8,6 +8,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
pull_request:
paths-ignore:
- 'doc/**'
@@ -15,146 +16,96 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
merge_group:
- paths-ignore:
- - 'doc/**'
- - '**/man'
- - '**.md'
- - '**.rdoc'
- - '**/.document'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
-# GitHub actions does not support YAML anchors. This creative use of
-# environment variables (plus the "echo $GITHUB_ENV" hack) is to reroute that
-# restriction.
-env:
- default_cc: clang-15
- append_cc: ''
-
- # -O1 is faster than -O3 in our tests... Majority of time are consumed trying
- # to optimize binaries. Also GitHub Actions run on relatively modern CPUs
- # compared to, say, GCC 4 or Clang 3. We don't specify `-march=native`
- # because compilers tend not understand what the CPU is.
- optflags: '-O1'
-
- # -g0 disables backtraces when SEGV. Do not set that.
- debugflags: '-ggdb3'
-
- default_configure: >-
- --enable-debug-env
- --disable-install-doc
- --with-ext=-test-/cxxanyargs,+
- append_configure: >-
- --without-valgrind
- --without-jemalloc
- --without-gmp
-
- CONFIGURE_TTY: never
- GITPULLOPTIONS: --no-tags origin ${{github.ref}}
- RUBY_DEBUG: ci rgengc
- RUBY_TESTOPTS: >-
- -q
- --color=always
- --tty=no
-
permissions:
contents: read
jobs:
compile:
- strategy:
- fail-fast: false
- matrix:
- env:
- - {}
- entry:
- - name: 'gcc-11 annocheck'
- container: gcc-11
- env:
- # Minimal flags to pass the check.
- default_cc: 'gcc-11 -fcf-protection -Wa,--generate-missing-build-notes=yes'
- optflags: '-O2'
- LDFLAGS: '-Wl,-z,now'
- # FIXME: Drop skipping options
- # https://bugs.ruby-lang.org/issues/18061
- # https://sourceware.org/annobin/annobin.html/Test-pie.html
- TEST_ANNOCHECK_OPTS: "--skip-pie --skip-gaps"
- check: true
-
- name: ${{ matrix.entry.name }}
+ name: test-annocheck
+
runs-on: ubuntu-latest
+
container:
- image: ghcr.io/ruby/ruby-ci-image:${{ matrix.entry.container || matrix.entry.env.default_cc || 'clang-15' }}
+ image: ghcr.io/ruby/ruby-ci-image:gcc-11
options: --user root
- if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
- env: ${{ matrix.entry.env || matrix.env }}
+
+ if: >-
+ ${{!(false
+ || contains(github.event.head_commit.message, '[DOC]')
+ || contains(github.event.head_commit.message, 'Document')
+ || contains(github.event.pull_request.title, '[DOC]')
+ || contains(github.event.pull_request.title, 'Document')
+ || contains(github.event.pull_request.labels.*.name, 'Document')
+ || (github.event_name == 'push' && github.actor == 'dependabot[bot]')
+ )}}
+
+ env:
+ CONFIGURE_TTY: never
+ GITPULLOPTIONS: --no-tags origin ${{ github.ref }}
+ RUBY_DEBUG: ci rgengc
+ RUBY_TESTOPTS: >-
+ -q
+ --color=always
+ --tty=no
+ # FIXME: Drop skipping options
+ # https://bugs.ruby-lang.org/issues/18061
+ # https://sourceware.org/annobin/annobin.html/Test-pie.html
+ TEST_ANNOCHECK_OPTS: '--skip-pie --skip-gaps'
+
steps:
- run: id
working-directory:
- - run: mkdir build
- working-directory:
- - name: setenv
- run: |
- echo "GNUMAKEFLAGS=-sj$((1 + $(nproc --all)))" >> $GITHUB_ENV
- - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+
+ - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
+ with:
+ sparse-checkout-cone-mode: false
+ sparse-checkout: /.github
+
+ - uses: ./.github/actions/setup/directories
with:
- path: src
- - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
+ srcdir: src
+ builddir: build
+ makeup: true
+
+ - uses: ruby/setup-ruby@1198b074305f9356bd56dd4b311757cc0dab2f1c # v1.175.1
with:
- path: src/.downloaded-cache
- key: downloaded-cache
- - name: autogen
- run: |
- if [ ! -f ./autogen.sh ]; then
- ls -la
- fi
- ./autogen.sh
- working-directory: src
+ ruby-version: '3.0'
+ bundler: none
+
+ # Minimal flags to pass the check.
+ # -g0 disables backtraces when SEGV. Do not set that.
- name: Run configure
run: >
- ../src/configure -C ${default_configure} ${append_configure}
- --${{
- matrix.entry.crosshost && 'host' || 'with-gcc'
- }}=${{
- matrix.entry.crosshost || '"${default_cc}${append_cc:+ $append_cc}"'
- }}
- --${{ matrix.entry.shared || 'enable' }}-shared
- - run: make extract-extlibs
- - run: make incs
+ ../src/configure -C
+ --enable-debug-env
+ --disable-install-doc
+ --with-ext=-test-/cxxanyargs,+
+ --without-valgrind
+ --without-jemalloc
+ --without-gmp
+ --with-gcc="gcc-11 -fcf-protection -Wa,--generate-missing-build-notes=yes"
+ --enable-shared
+ debugflags=-ggdb3
+ optflags=-O2
+ LDFLAGS=-Wl,-z,now
+
- run: make showflags
+
- run: make
- - run: make test
- - run: make install
- if: ${{ matrix.entry.check }}
- - run: make test-tool
- if: ${{ matrix.entry.check }}
- ### test-all doesn't work: https://github.com/ruby/ruby/actions/runs/4340112185/jobs/7578344307
- # - run: make test-all TESTS='-- ruby -ext-'
- # if: ${{ matrix.entry.check }}
- ### test-spec doesn't work: https://github.com/ruby/ruby/actions/runs/4340193212/jobs/7578505652
- # - run: make test-spec
- # env:
- # CHECK_LEAKS: true
- # if: ${{ matrix.entry.check }}
+
- run: make test-annocheck
- if: ${{ matrix.entry.check && endsWith(matrix.entry.name, 'annocheck') }}
- - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ - uses: ./.github/actions/slack
with:
- payload: |
- {
- "ci": "GitHub Actions",
- "env": "${{ github.workflow }} / ${{ matrix.entry.name }}",
- "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
- "commit": "${{ github.sha }}",
- "branch": "${{ github.ref_name }}"
- }
- env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
- if: ${{ failure() && github.event_name == 'push' }}
+ if: ${{ failure() }}
defaults:
run:
diff --git a/.github/workflows/auto_request_review.yml b/.github/workflows/auto_request_review.yml
index d9eb774e3f..ca27244b46 100644
--- a/.github/workflows/auto_request_review.yml
+++ b/.github/workflows/auto_request_review.yml
@@ -10,10 +10,10 @@ jobs:
auto-request-review:
name: Auto Request Review
runs-on: ubuntu-latest
- if: ${{ github.repository == 'ruby/ruby' }}
+ if: ${{ github.repository == 'ruby/ruby' && github.base_ref == 'master' }}
steps:
- name: Request review based on files changes and/or groups the author belongs to
- uses: necojackarc/auto-request-review@6a51cebffe2c084705d9a7b394abd802e0119633 # v0.12.0
+ uses: necojackarc/auto-request-review@e89da1a8cd7c8c16d9de9c6e763290b6b0e3d424 # v0.13.0
with:
# scope: public_repo
token: ${{ secrets.MATZBOT_GITHUB_TOKEN }}
diff --git a/.github/workflows/baseruby.yml b/.github/workflows/baseruby.yml
index 9c38e0998b..4155cdb1ab 100644
--- a/.github/workflows/baseruby.yml
+++ b/.github/workflows/baseruby.yml
@@ -8,6 +8,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
pull_request:
paths-ignore:
- 'doc/**'
@@ -15,13 +16,8 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
merge_group:
- paths-ignore:
- - 'doc/**'
- - '**/man'
- - '**.md'
- - '**.rdoc'
- - '**/.document'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -33,46 +29,49 @@ permissions:
jobs:
baseruby:
name: BASERUBY
+
runs-on: ubuntu-20.04
- if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+
+ if: >-
+ ${{!(false
+ || contains(github.event.head_commit.message, '[DOC]')
+ || contains(github.event.head_commit.message, 'Document')
+ || contains(github.event.pull_request.title, '[DOC]')
+ || contains(github.event.pull_request.title, 'Document')
+ || contains(github.event.pull_request.labels.*.name, 'Document')
+ || (github.event_name == 'push' && github.actor == 'dependabot[bot]')
+ )}}
+
strategy:
matrix:
ruby:
- - ruby-2.5
-# - ruby-2.6
-# - ruby-2.7
- ruby-3.0
- ruby-3.1
- ruby-3.2
+ - ruby-3.3
steps:
- - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
- with:
- path: .downloaded-cache
- key: downloaded-cache
- - uses: ruby/setup-ruby@8a45918450651f5e4784b6031db26f4b9f76b251 # v1.150.0
+ - uses: ruby/setup-ruby@1198b074305f9356bd56dd4b311757cc0dab2f1c # v1.175.1
with:
ruby-version: ${{ matrix.ruby }}
bundler: none
- - run: echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
- - run: sudo apt-get install build-essential autoconf libyaml-dev
- - run: ./autogen.sh
+
+ - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
+
+ - uses: ./.github/actions/setup/ubuntu
+
+ - uses: ./.github/actions/setup/directories
+ with:
+ makeup: true
+
- run: ./configure --disable-install-doc
- - run: make common-srcs
- - run: make incs
+
- run: make all
+
- run: make test
- - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+
+ - uses: ./.github/actions/slack
with:
- payload: |
- {
- "ci": "GitHub Actions",
- "env": "${{ github.workflow }} / BASERUBY @ ${{ matrix.ruby }}",
- "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
- "commit": "${{ github.sha }}",
- "branch": "${{ github.ref_name }}"
- }
- env:
+ label: ${{ matrix.ruby }}
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
- if: ${{ failure() && github.event_name == 'push' }}
+ if: ${{ failure() }}
diff --git a/.github/workflows/bundled_gems.yml b/.github/workflows/bundled_gems.yml
index c871388f93..fcc005239b 100644
--- a/.github/workflows/bundled_gems.yml
+++ b/.github/workflows/bundled_gems.yml
@@ -2,54 +2,48 @@ name: bundled_gems
on:
push:
- branches: [ "master" ]
+ branches: ['master']
paths:
- '.github/workflows/bundled_gems.yml'
- 'gems/bundled_gems'
pull_request:
- branches: [ "master" ]
+ branches: ['master']
paths:
- '.github/workflows/bundled_gems.yml'
- 'gems/bundled_gems'
merge_group:
- branches: [ "master" ]
- paths:
- - '.github/workflows/bundled_gems.yml'
- - 'gems/bundled_gems'
schedule:
- cron: '45 6 * * *'
workflow_dispatch:
-permissions: # added using https://github.com/step-security/secure-workflows
+permissions: # added using https://github.com/step-security/secure-workflows
contents: read
jobs:
update:
permissions:
- contents: write # for Git to git push
+ contents: write # for Git to git push
+
if: ${{ github.event_name != 'schedule' || github.repository == 'ruby/ruby' }}
+
name: update ${{ github.workflow }}
+
runs-on: ubuntu-latest
+
steps:
- - name: git config
- run: |
- git config --global advice.detachedHead 0
- git config --global init.defaultBranch garbage
+ - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
+ with:
+ token: ${{ (github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull')) && secrets.MATZBOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+
+ - uses: ./.github/actions/setup/directories
+ with:
+ # Skip overwriting MATZBOT_GITHUB_TOKEN
+ checkout: '' # false (ref: https://github.com/actions/runner/issues/2238)
- name: Set ENV
run: |
- echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
echo "TODAY=$(date +%F)" >> $GITHUB_ENV
- - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
-
- - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
- with:
- path: .downloaded-cache
- key: downloaded-cache-${{ github.sha }}
- restore-keys: |
- downloaded-cache
-
- name: Download previous gems list
run: |
data=bundled_gems.json
@@ -58,8 +52,16 @@ jobs:
curl -O -R -z ./$data https://stdgems.org/$data
- name: Update bundled gems list
+ id: bundled_gems
+ run: |
+ ruby -i~ tool/update-bundled_gems.rb gems/bundled_gems >> $GITHUB_OUTPUT
+
+ - name: Update spec/bundler/support/builders.rb
run: |
- ruby -i~ tool/update-bundled_gems.rb gems/bundled_gems
+ #!ruby
+ rake_version = File.read("gems/bundled_gems")[/^rake\s+(\S+)/, 1]
+ print ARGF.read.sub(/^ *def rake_version\s*\K".*?"/) {rake_version.dump}
+ shell: ruby -i~ {0} spec/bundler/support/builders.rb
- name: Maintain updated gems list in NEWS
run: |
@@ -68,71 +70,66 @@ jobs:
- name: Check diffs
id: diff
run: |
- git add -- NEWS.md
- git diff --no-ext-diff --ignore-submodules --quiet -- gems/bundled_gems
- continue-on-error: true
+ news= gems=
+ git diff --color --no-ext-diff --ignore-submodules --exit-code -- NEWS.md ||
+ news=true
+ git diff --color --no-ext-diff --ignore-submodules --exit-code -- gems/bundled_gems ||
+ gems=true
+ git add -- NEWS.md gems/bundled_gems
+ git add -- spec/bundler/support/builders.rb
+ echo news=$news >> $GITHUB_OUTPUT
+ echo gems=$gems >> $GITHUB_OUTPUT
+ echo update=${news:-$gems} >> $GITHUB_OUTPUT
- name: Install libraries
- run: |
- set -x
- sudo apt-get update -q || :
- sudo apt-get install --no-install-recommends -q -y build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev autoconf ruby
- if: ${{ steps.diff.outcome == 'failure' }}
+ uses: ./.github/actions/setup/ubuntu
+ if: ${{ steps.diff.outputs.gems }}
- name: Build
run: |
./autogen.sh
./configure -C --disable-install-doc
make
- if: ${{ steps.diff.outcome == 'failure' }}
+ if: ${{ steps.diff.outputs.gems }}
- name: Prepare bundled gems
run: |
make -s prepare-gems
- if: ${{ steps.diff.outcome == 'failure' }}
+ if: ${{ steps.diff.outputs.gems }}
- name: Test bundled gems
run: |
make -s test-bundled-gems
- git add -- gems/bundled_gems
timeout-minutes: 30
env:
- RUBY_TESTOPTS: "-q --tty=no"
- TEST_BUNDLED_GEMS_ALLOW_FAILURES: ""
- if: ${{ steps.diff.outcome == 'failure' }}
-
- - name: Show diffs
- id: show
- run: |
- git diff --cached --color --no-ext-diff --ignore-submodules --exit-code --
- continue-on-error: true
+ RUBY_TESTOPTS: '-q --tty=no'
+ TEST_BUNDLED_GEMS_ALLOW_FAILURES: ''
+ if: ${{ steps.diff.outputs.gems }}
- name: Commit
run: |
git pull --ff-only origin ${GITHUB_REF#refs/heads/}
- message="Update bundled gems list at "
- if [ ${{ steps.diff.outcome }} = success ]; then
- git commit --message="${message}${GITHUB_SHA:0:30} [ci skip]"
+ message="Update bundled gems list"
+ if [ -z "${gems}" ]; then
+ git commit --message="${message} at ${GITHUB_SHA:0:30} [ci skip]"
else
- git commit --message="${message}${TODAY}"
+ git commit --message="${message} as of ${TODAY}"
fi
git push origin ${GITHUB_REF#refs/heads/}
env:
+ TODAY: ${{ steps.bundled_gems.outputs.latest_date || env.TODAY }}
EMAIL: svn-admin@ruby-lang.org
GIT_AUTHOR_NAME: git
GIT_COMMITTER_NAME: git
- if: ${{ github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull') && steps.show.outcome == 'failure' }}
-
- - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ gems: ${{ steps.diff.outputs.gems }}
+ if: >-
+ ${{
+ github.repository == 'ruby/ruby' &&
+ !startsWith(github.event_name, 'pull') &&
+ steps.diff.outputs.update
+ }}
+
+ - uses: ./.github/actions/slack
with:
- payload: |
- {
- "ci": "GitHub Actions",
- "env": "${{ github.workflow }} / update",
- "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
- "commit": "${{ github.sha }}",
- "branch": "${{ github.ref_name }}"
- }
- env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
- if: ${{ failure() && github.event_name == 'push' }}
+ if: ${{ failure() }}
diff --git a/.github/workflows/check_dependencies.yml b/.github/workflows/check_dependencies.yml
index 642c3255ce..bd7013446a 100644
--- a/.github/workflows/check_dependencies.yml
+++ b/.github/workflows/check_dependencies.yml
@@ -7,6 +7,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
pull_request:
paths-ignore:
- 'doc/**'
@@ -14,13 +15,8 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
merge_group:
- paths-ignore:
- - 'doc/**'
- - '**/man'
- - '**.md'
- - '**.rdoc'
- - '**/.document'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -35,45 +31,46 @@ jobs:
matrix:
os: [ubuntu-20.04]
fail-fast: true
+
runs-on: ${{ matrix.os }}
- if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+
+ if: >-
+ ${{!(false
+ || contains(github.event.head_commit.message, '[DOC]')
+ || contains(github.event.head_commit.message, 'Document')
+ || contains(github.event.pull_request.title, '[DOC]')
+ || contains(github.event.pull_request.title, 'Document')
+ || contains(github.event.pull_request.labels.*.name, 'Document')
+ || (github.event_name == 'push' && github.actor == 'dependabot[bot]')
+ )}}
+
steps:
- - name: Install libraries
- run: |
- set -x
- sudo apt-get update -q || :
- sudo apt-get install --no-install-recommends -q -y build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev autoconf ruby
+ - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
+
+ - uses: ./.github/actions/setup/ubuntu
if: ${{ contains(matrix.os, 'ubuntu') }}
- - name: Install libraries
- run: |
- brew upgrade
- brew install gmp libffi openssl@1.1 zlib autoconf automake libtool readline
+
+ - uses: ./.github/actions/setup/macos
if: ${{ contains(matrix.os, 'macos') }}
- - name: git config
- run: |
- git config --global advice.detachedHead 0
- git config --global init.defaultBranch garbage
- - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
+
+ - uses: ./.github/actions/setup/directories
+
+ - uses: ruby/setup-ruby@1198b074305f9356bd56dd4b311757cc0dab2f1c # v1.175.1
with:
- path: .downloaded-cache
- key: downloaded-cache
- - run: ./autogen.sh
+ ruby-version: '3.0'
+ bundler: none
+
- name: Run configure
run: ./configure -C --disable-install-doc --disable-rubygems --with-gcc 'optflags=-O0' 'debugflags=-save-temps=obj -g'
+
- run: make all golf
+
- run: ruby tool/update-deps --fix
+
- run: git diff --no-ext-diff --ignore-submodules --exit-code
- - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+
+ - uses: ./.github/actions/slack
with:
- payload: |
- {
- "ci": "GitHub Actions",
- "env": "${{ matrix.os }} / Dependencies need to update",
- "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
- "commit": "${{ github.sha }}",
- "branch": "${{ github.ref_name }}"
- }
- env:
+ label: ${{ matrix.os }} / Dependencies need to update
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
- if: ${{ failure() && github.event_name == 'push' }}
+ if: ${{ failure() }}
diff --git a/.github/workflows/check_misc.yml b/.github/workflows/check_misc.yml
index 403fa26d1f..48434a47a9 100644
--- a/.github/workflows/check_misc.yml
+++ b/.github/workflows/check_misc.yml
@@ -1,4 +1,4 @@
-name: Miscellaneous checks
+name: Misc
on: [push, pull_request, merge_group]
concurrency:
@@ -10,21 +10,37 @@ permissions:
jobs:
checks:
+ name: Miscellaneous checks
+
permissions:
- contents: write # for Git to git push
+ contents: write # for Git to git push
+
runs-on: ubuntu-latest
+
steps:
- - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+ - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
+ with:
+ token: ${{ (github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull')) && secrets.MATZBOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+
+ - uses: ./.github/actions/setup/directories
+ with:
+ makeup: true
+ # Skip overwriting MATZBOT_GITHUB_TOKEN
+ checkout: '' # false (ref: https://github.com/actions/runner/issues/2238)
+
- name: Check if C-sources are US-ASCII
run: |
- grep -r -n '[^ -~]' -- *.[chy] include internal win32/*.[ch] && exit 1 || :
+ grep -r -n --include='*.[chyS]' --include='*.asm' $'[^\t-~]' -- . && exit 1 || :
+
- name: Check for trailing spaces
run: |
- git grep -I -n '[ ]$' -- '*.rb' '*.[chy]' '*.rs' && exit 1 || :
- git grep -n '^[ ][ ]*$' -- '*.md' && exit 1 || :
+ git grep -I -n $'[\t ]$' -- '*.rb' '*.[chy]' '*.rs' '*.yml' && exit 1 || :
+ git grep -n $'^[\t ][\t ]*$' -- '*.md' && exit 1 || :
+
- name: Check for bash specific substitution in configure.ac
run: |
git grep -n '\${[A-Za-z_0-9]*/' -- configure.ac && exit 1 || :
+
- name: Check for header macros
run: |
fail=
@@ -40,21 +56,13 @@ jobs:
run: true
if: ${{ github.ref == 'refs/heads/master' }}
- - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
- with:
- path: .downloaded-cache
- key: downloaded-cache-${{ github.sha }}
- restore-keys: |
- downloaded-cache
- if: steps.gems.outcome == 'success'
-
- name: Download previous gems list
run: |
data=default_gems.json
mkdir -p .downloaded-cache
ln -s .downloaded-cache/$data .
curl -O -R -z ./$data https://stdgems.org/$data
- if: steps.gems.outcome == 'success'
+ if: ${{ steps.gems.outcome == 'success' }}
- name: Make default gems list
run: |
@@ -73,19 +81,20 @@ jobs:
f.puts gems
end
shell: ruby --disable=gems {0}
- if: steps.gems.outcome == 'success'
+ if: ${{ steps.gems.outcome == 'success' }}
- name: Maintain updated gems list in NEWS
run: |
ruby tool/update-NEWS-gemlist.rb default
- if: steps.gems.outcome == 'success'
+ if: ${{ steps.gems.outcome == 'success' }}
- name: Check diffs
id: diff
run: |
- git diff --color --no-ext-diff --ignore-submodules --exit-code NEWS.md
- continue-on-error: true
- if: steps.gems.outcome == 'success'
+ git diff --color --no-ext-diff --ignore-submodules --exit-code NEWS.md ||
+ echo update=true >> $GITHUB_OUTPUT
+ if: ${{ steps.gems.outcome == 'success' }}
+
- name: Commit
run: |
git pull --ff-only origin ${GITHUB_REF#refs/heads/}
@@ -95,18 +104,14 @@ jobs:
EMAIL: svn-admin@ruby-lang.org
GIT_AUTHOR_NAME: git
GIT_COMMITTER_NAME: git
- if: ${{ github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull') && steps.diff.outcome == 'failure' }}
+ if: >-
+ ${{
+ github.repository == 'ruby/ruby' &&
+ !startsWith(github.event_name, 'pull') &&
+ steps.diff.outputs.update
+ }}
- - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ - uses: ./.github/actions/slack
with:
- payload: |
- {
- "ci": "GitHub Actions",
- "env": "${{ github.workflow }}",
- "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
- "commit": "${{ github.sha }}",
- "branch": "${{ github.ref_name }}"
- }
- env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
- if: ${{ failure() && github.event_name == 'push' }}
+ if: ${{ failure() }}
diff --git a/.github/workflows/cirrus-notify.yml b/.github/workflows/cirrus-notify.yml
deleted file mode 100644
index f6c11a65d3..0000000000
--- a/.github/workflows/cirrus-notify.yml
+++ /dev/null
@@ -1,46 +0,0 @@
-on:
- check_suite:
- type: ['completed']
-name: Cirrus CI failure notification
-
-permissions:
- contents: read
-
-jobs:
- cirrus-notify:
- name: After Cirrus CI Failure
- if: >-
- github.event.check_suite.app.name == 'Cirrus CI'
- && github.event.check_suite.conclusion != 'success'
- && github.event.check_suite.conclusion != 'cancelled'
- && github.event.check_suite.conclusion != 'skipped'
- && github.event.check_suite.head_branch == 'master'
- runs-on: ubuntu-latest
- steps:
- - uses: octokit/request-action@352d2ae93e1805721b5fe308598555ba3bd2c8e2 # v2.1.8
- id: get_failed_check_run
- with:
- route: GET /repos/${{ github.repository }}/check-suites/${{ github.event.check_suite.id }}/check-runs?status=completed
- mediaType: '{"previews": ["antiope"]}'
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- - name: Dump GitHub context
- env:
- GITHUB_CONTEXT: ${{ toJson(github) }}
- run: echo "$GITHUB_CONTEXT"
- - name: Dump check_runs
- env:
- CHECK_RUNS: ${{ steps.get_failed_check_run.outputs.data }}
- run: echo "$CHECK_RUNS"
- - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
- with:
- payload: |
- {
- "ci": "Cirrus CI",
- "env": "Cirrus CI",
- "url": "${{ fromJson(steps.get_failed_check_run.outputs.data).check_runs[0].html_url }}",
- "commit": "${{ github.event.check_suite.head_commit.id }}",
- "branch": "${{ github.event.check_suite.head_branch }}"
- }
- env:
- SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index e250835aa2..c7d0def7ae 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -1,14 +1,15 @@
-name: "CodeQL"
+name: 'CodeQL'
on:
push:
- branches: [ "master" ]
+ branches: ['master']
paths-ignore:
- 'doc/**'
- '**/man'
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
pull_request:
paths-ignore:
- 'doc/**'
@@ -16,6 +17,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
schedule:
- cron: '0 12 * * *'
workflow_dispatch:
@@ -24,19 +26,27 @@ concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
-permissions: # added using https://github.com/step-security/secure-workflows
+permissions: # added using https://github.com/step-security/secure-workflows
contents: read
jobs:
analyze:
name: Analyze
- runs-on: ubuntu-latest
+ runs-on: ${{ matrix.os }}
permissions:
- actions: read # for github/codeql-action/init to get workflow details
- contents: read # for actions/checkout to fetch code
- security-events: write # for github/codeql-action/autobuild to send a status report
+ actions: read # for github/codeql-action/init to get workflow details
+ contents: read # for actions/checkout to fetch code
+ security-events: write # for github/codeql-action/autobuild to send a status report
# CodeQL fails to run pull requests from dependabot due to missing write access to upload results.
- if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') && github.event.head_commit.pusher.name != 'dependabot[bot]' }}
+ if: >-
+ ${{!(false
+ || contains(github.event.head_commit.message, '[DOC]')
+ || contains(github.event.head_commit.message, 'Document')
+ || contains(github.event.pull_request.title, '[DOC]')
+ || contains(github.event.pull_request.title, 'Document')
+ || contains(github.event.pull_request.labels.*.name, 'Document')
+ || (github.event_name == 'push' && github.actor == 'dependabot[bot]')
+ )}}
env:
enable_install_doc: no
@@ -44,67 +54,71 @@ jobs:
strategy:
fail-fast: false
matrix:
- language: [ 'cpp', 'ruby' ]
+ include:
+ - language: cpp
+ os: ubuntu-latest
+ # ruby analysis used large memory. We need to use a larger runner.
+ - language: ruby
+ os: ${{ github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'ubuntu-latest' }}
steps:
- - name: Install libraries
- run: |
- set -x
- sudo apt-get update -q || :
- sudo apt-get install --no-install-recommends -q -y build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev autoconf ruby
-
- - name: Checkout repository
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
-
- - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
- with:
- path: .downloaded-cache
- key: downloaded-cache
-
- - name: Remove an obsolete rubygems vendored file
- run: sudo rm /usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb
-
- - name: Initialize CodeQL
- uses: github/codeql-action/init@0225834cc549ee0ca93cb085b92954821a145866 # v2.3.5
- with:
- languages: ${{ matrix.language }}
-
- - name: Set ENV
- run: echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
-
- - name: Autobuild
- uses: github/codeql-action/autobuild@0225834cc549ee0ca93cb085b92954821a145866 # v2.3.5
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@0225834cc549ee0ca93cb085b92954821a145866 # v2.3.5
- with:
- category: "/language:${{matrix.language}}"
- upload: False
- output: sarif-results
-
- - name: filter-sarif
- uses: advanced-security/filter-sarif@f3b8118a9349d88f7b1c0c488476411145b6270d # v1.0
- with:
- patterns: |
- +**/*.rb
- -lib/uri/mailto.rb:rb/overly-large-range
- -lib/uri/rfc3986_parser.rb:rb/overly-large-range
- -lib/bundler/vendor/uri/lib/uri/mailto.rb:rb/overly-large-range
- -lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb:rb/overly-large-range
- -test/ruby/test_io.rb:rb/non-constant-kernel-open
- -test/open-uri/test_open-uri.rb:rb/non-constant-kernel-open
- -test/open-uri/test_ssl.rb:rb/non-constant-kernel-open
- -spec/ruby/core/io/binread_spec.rb:rb/non-constant-kernel-open
- -spec/ruby/core/io/readlines_spec.rb:rb/non-constant-kernel-open
- -spec/ruby/core/io/foreach_spec.rb:rb/non-constant-kernel-open
- -spec/ruby/core/io/write_spec.rb:rb/non-constant-kernel-open
- -spec/ruby/core/io/read_spec.rb:rb/non-constant-kernel-open
- -spec/ruby/core/kernel/open_spec.rb:rb/non-constant-kernel-open
- input: sarif-results/${{ matrix.language }}.sarif
- output: sarif-results/${{ matrix.language }}.sarif
- if: ${{ matrix.language == 'ruby' }}
-
- - name: Upload SARIF
- uses: github/codeql-action/upload-sarif@0225834cc549ee0ca93cb085b92954821a145866 # v2.3.5
- with:
- sarif_file: sarif-results/${{ matrix.language }}.sarif
+ - name: Checkout repository
+ uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
+
+ - name: Install libraries
+ if: ${{ contains(matrix.os, 'macos') }}
+ uses: ./.github/actions/setup/macos
+
+ - name: Install libraries
+ if : ${{ matrix.os == 'ubuntu-latest' }}
+ uses: ./.github/actions/setup/ubuntu
+
+ - uses: ./.github/actions/setup/directories
+
+ - name: Remove an obsolete rubygems vendored file
+ if: ${{ matrix.os == 'ubuntu-latest' }}
+ run: sudo rm /usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb
+
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3
+ with:
+ languages: ${{ matrix.language }}
+
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3
+ with:
+ category: '/language:${{ matrix.language }}'
+ upload: False
+ output: sarif-results
+
+ - name: filter-sarif
+ uses: advanced-security/filter-sarif@f3b8118a9349d88f7b1c0c488476411145b6270d # v1.0.1
+ with:
+ patterns: |
+ +**/*.rb
+ -lib/uri/mailto.rb:rb/overly-large-range
+ -lib/uri/rfc3986_parser.rb:rb/overly-large-range
+ -lib/bundler/vendor/uri/lib/uri/mailto.rb:rb/overly-large-range
+ -lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb:rb/overly-large-range
+ -test/ruby/test_io.rb:rb/non-constant-kernel-open
+ -test/open-uri/test_open-uri.rb:rb/non-constant-kernel-open
+ -test/open-uri/test_ssl.rb:rb/non-constant-kernel-open
+ -spec/ruby/core/io/binread_spec.rb:rb/non-constant-kernel-open
+ -spec/ruby/core/io/readlines_spec.rb:rb/non-constant-kernel-open
+ -spec/ruby/core/io/foreach_spec.rb:rb/non-constant-kernel-open
+ -spec/ruby/core/io/write_spec.rb:rb/non-constant-kernel-open
+ -spec/ruby/core/io/read_spec.rb:rb/non-constant-kernel-open
+ -spec/ruby/core/kernel/open_spec.rb:rb/non-constant-kernel-open
+ input: sarif-results/${{ matrix.language }}.sarif
+ output: sarif-results/${{ matrix.language }}.sarif
+ if: ${{ matrix.language == 'ruby' }}
+ continue-on-error: true
+
+ - name: Upload SARIF
+ uses: github/codeql-action/upload-sarif@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3
+ with:
+ sarif_file: sarif-results/${{ matrix.language }}.sarif
+ continue-on-error: true
diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml
index f7951a7237..5e5eb69b6f 100644
--- a/.github/workflows/compilers.yml
+++ b/.github/workflows/compilers.yml
@@ -8,6 +8,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
pull_request:
paths-ignore:
- 'doc/**'
@@ -15,13 +16,8 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
merge_group:
- paths-ignore:
- - 'doc/**'
- - '**/man'
- - '**.md'
- - '**.rdoc'
- - '**/.document'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -31,7 +27,7 @@ concurrency:
# environment variables (plus the "echo $GITHUB_ENV" hack) is to reroute that
# restriction.
env:
- default_cc: clang-16
+ default_cc: clang-18
append_cc: ''
# -O1 is faster than -O3 in our tests... Majority of time are consumed trying
@@ -53,7 +49,7 @@ env:
--without-gmp
CONFIGURE_TTY: never
- GITPULLOPTIONS: --no-tags origin ${{github.ref}}
+ GITPULLOPTIONS: --no-tags origin ${{ github.ref }}
RUBY_DEBUG: ci rgengc
RUBY_TESTOPTS: >-
-q
@@ -78,13 +74,15 @@ jobs:
- { name: gcc-9, env: { default_cc: gcc-9 } }
- { name: gcc-8, env: { default_cc: gcc-8 } }
- { name: gcc-7, env: { default_cc: gcc-7 } }
- - name: 'gcc-11 LTO'
- container: gcc-11
+ - name: 'gcc-13 LTO'
+ container: gcc-13
env:
- default_cc: 'gcc-11 -flto=auto -ffat-lto-objects'
+ default_cc: 'gcc-13 -flto=auto -ffat-lto-objects -Werror=lto-type-mismatch'
optflags: '-O2'
shared: disable
# check: true
+ - { name: clang-19, env: { default_cc: clang-19 } }
+ - { name: clang-18, env: { default_cc: clang-18 } }
- { name: clang-17, env: { default_cc: clang-17 } }
- { name: clang-16, env: { default_cc: clang-16 } }
- { name: clang-15, env: { default_cc: clang-15 } }
@@ -93,19 +91,20 @@ jobs:
- { name: clang-12, env: { default_cc: clang-12 } }
- { name: clang-11, env: { default_cc: clang-11 } }
- { name: clang-10, env: { default_cc: clang-10 } }
- - { name: clang-9, env: { default_cc: clang-9 } }
- - { name: clang-8, env: { default_cc: clang-8 } }
- - { name: clang-7, env: { default_cc: clang-7 } }
- - { name: clang-6.0, env: { default_cc: clang-6.0 } }
- - name: 'clang-14 LTO'
- container: clang-14
+ # llvm-objcopy<=9 doesn't have --wildcard. It compiles, but leaves Rust symbols in libyjit.o.
+ - { name: clang-9, env: { default_cc: clang-9, append_configure: '--disable-yjit' } }
+ - { name: clang-8, env: { default_cc: clang-8, append_configure: '--disable-yjit' } }
+ - { name: clang-7, env: { default_cc: clang-7, append_configure: '--disable-yjit' } }
+ - { name: clang-6.0, env: { default_cc: clang-6.0, append_configure: '--disable-yjit' } }
+ - name: 'clang-16 LTO'
+ container: clang-16
env:
- default_cc: 'clang-14 -flto=auto'
+ default_cc: 'clang-16 -flto=auto'
optflags: '-O2'
shared: disable
# check: true
- - { name: ext/Setup }
+ - { name: ext/Setup, static-exts: 'etc,json/*,*/escape' }
# - { name: aarch64-linux-gnu, crosshost: aarch64-linux-gnu, container: crossbuild-essential-arm64 }
# - { name: arm-linux-gnueabi, crosshost: arm-linux-gnueabi }
@@ -122,15 +121,17 @@ jobs:
# warning generates a lot of noise from use of ANYARGS in
# rb_define_method() and friends.
# See: https://github.com/llvm/llvm-project/commit/11da1b53d8cd3507959022cd790d5a7ad4573d94
- - { name: c99, env: { append_cc: '-std=c99 -Werror=pedantic -pedantic-errors -Wno-strict-prototypes' } }
-# - { name: c11, env: { append_cc: '-std=c11 -Werror=pedantic -pedantic-errors -Wno-strict-prototypes' } }
-# - { name: c17, env: { append_cc: '-std=c17 -Werror=pedantic -pedantic-errors -Wno-strict-prototypes' } }
- - { name: c2x, env: { append_cc: '-std=c2x -Werror=pedantic -pedantic-errors -Wno-strict-prototypes' } }
+ - { name: c99, env: { CFLAGS: '-std=c99 -Werror=pedantic -pedantic-errors -Wno-strict-prototypes' } }
+# - { name: c11, env: { CFLAGS: '-std=c11 -Werror=pedantic -pedantic-errors -Wno-strict-prototypes' } }
+# - { name: c17, env: { CFLAGS: '-std=c17 -Werror=pedantic -pedantic-errors -Wno-strict-prototypes' } }
+ - { name: c23, env: { CFLAGS: '-std=c2x -Werror=pedantic -pedantic-errors -Wno-strict-prototypes' } }
- { name: c++98, env: { CXXFLAGS: '-std=c++98 -Werror=pedantic -pedantic-errors -Wno-c++11-long-long' } }
# - { name: c++11, env: { CXXFLAGS: '-std=c++11 -Werror=pedantic -pedantic-errors -Wno-c++11-long-long' } }
# - { name: c++14, env: { CXXFLAGS: '-std=c++14 -Werror=pedantic -pedantic-errors -Wno-c++11-long-long' } }
# - { name: c++17, env: { CXXFLAGS: '-std=c++17 -Werror=pedantic -pedantic-errors -Wno-c++11-long-long' } }
- - { name: c++2a, env: { CXXFLAGS: '-std=c++2a -Werror=pedantic -pedantic-errors -Wno-c++11-long-long' } }
+# - { name: c++20, env: { CXXFLAGS: '-std=c++20 -Werror=pedantic -pedantic-errors -Wno-c++11-long-long' } }
+# - { name: c++23, env: { CXXFLAGS: '-std=c++23 -Werror=pedantic -pedantic-errors -Wno-c++11-long-long' } }
+ - { name: c++26, env: { CXXFLAGS: '-std=c++26 -Werror=pedantic -pedantic-errors -Wno-c++11-long-long' } }
- { name: '-O0', env: { optflags: '-O0 -march=x86-64 -mtune=generic' } }
# - { name: '-O3', env: { optflags: '-O3 -march=x86-64 -mtune=generic' }, check: true }
@@ -140,15 +141,15 @@ jobs:
- { name: valgrind, env: { append_configure: '--with-valgrind' } }
- { name: 'coroutine=ucontext', env: { append_configure: '--with-coroutine=ucontext' } }
- { name: 'coroutine=pthread', env: { append_configure: '--with-coroutine=pthread' } }
- - { name: disable-jit-support, env: { append_configure: '--disable-jit-support' } }
+ - { name: disable-jit, env: { append_configure: '--disable-yjit --disable-rjit' } }
- { name: disable-dln, env: { append_configure: '--disable-dln' } }
- { name: enable-mkmf-verbose, env: { append_configure: '--enable-mkmf-verbose' } }
- { name: disable-rubygems, env: { append_configure: '--disable-rubygems' } }
- { name: RUBY_DEVEL, env: { append_configure: '--enable-devel' } }
+ - { name: OPT_THREADED_CODE=0, env: { cppflags: '-DOPT_THREADED_CODE=0' } }
- { name: OPT_THREADED_CODE=1, env: { cppflags: '-DOPT_THREADED_CODE=1' } }
- { name: OPT_THREADED_CODE=2, env: { cppflags: '-DOPT_THREADED_CODE=2' } }
- - { name: OPT_THREADED_CODE=3, env: { cppflags: '-DOPT_THREADED_CODE=3' } }
- { name: NDEBUG, env: { cppflags: '-DNDEBUG' } }
- { name: RUBY_DEBUG, env: { cppflags: '-DRUBY_DEBUG' } }
@@ -164,24 +165,27 @@ jobs:
# - { name: SYMBOL_DEBUG, env: { cppflags: '-DSYMBOL_DEBUG' } }
# - { name: RGENGC_CHECK_MODE, env: { cppflags: '-DRGENGC_CHECK_MODE' } }
-# - { name: TRANSIENT_HEAP_CHECK_MODE, env: { cppflags: '-DTRANSIENT_HEAP_CHECK_MODE' } }
# - { name: VM_CHECK_MODE, env: { cppflags: '-DVM_CHECK_MODE' } }
- - { name: USE_EMBED_CI=0, env: { cppflags: '-DUSE_EMBED_CI=0' } }
- - { name: USE_FLONUM=0, env: { cppflags: '-DUSE_FLONUM=0' } }
+# - { name: USE_EMBED_CI=0, env: { cppflags: '-DUSE_EMBED_CI=0' } }
+ - name: USE_FLONUM=0
+ env:
+ cppflags: '-DUSE_FLONUM=0'
+ # yjit requires FLONUM for the pointer tagging scheme
+ append_configure: '--disable-yjit'
# - { name: USE_GC_MALLOC_OBJ_INFO_DETAILS, env: { cppflags: '-DUSE_GC_MALLOC_OBJ_INFO_DETAILS' } }
- - { name: USE_LAZY_LOAD, env: { cppflags: '-DUSE_LAZY_LOAD' } }
+# - { name: USE_LAZY_LOAD, env: { cppflags: '-DUSE_LAZY_LOAD' } }
# - { name: USE_SYMBOL_GC=0, env: { cppflags: '-DUSE_SYMBOL_GC=0' } }
# - { name: USE_THREAD_CACHE=0, env: { cppflags: '-DUSE_THREAD_CACHE=0' } }
-# - { name: USE_TRANSIENT_HEAP=0, env: { cppflags: '-DUSE_TRANSIENT_HEAP=0' } }
- { name: USE_RUBY_DEBUG_LOG=1, env: { cppflags: '-DUSE_RUBY_DEBUG_LOG=1' } }
# - { name: USE_DEBUG_COUNTER, env: { cppflags: '-DUSE_DEBUG_COUNTER=1', RUBY_DEBUG_COUNTER_DISABLE: '1' } }
+ - { name: SHARABLE_MIDDLE_SUBSTRING, env: { cppflags: '-DSHARABLE_MIDDLE_SUBSTRING=1' } }
- - { name: DEBUG_FIND_TIME_NUMGUESS, env: { cppflags: '-DDEBUG_FIND_TIME_NUMGUESS' } }
- - { name: DEBUG_INTEGER_PACK, env: { cppflags: '-DDEBUG_INTEGER_PACK' } }
+# - { name: DEBUG_FIND_TIME_NUMGUESS, env: { cppflags: '-DDEBUG_FIND_TIME_NUMGUESS' } }
+# - { name: DEBUG_INTEGER_PACK, env: { cppflags: '-DDEBUG_INTEGER_PACK' } }
# - { name: ENABLE_PATH_CHECK, env: { cppflags: '-DENABLE_PATH_CHECK' } }
- - { name: GC_DEBUG_STRESS_TO_CLASS, env: { cppflags: '-DGC_DEBUG_STRESS_TO_CLASS' } }
+# - { name: GC_DEBUG_STRESS_TO_CLASS, env: { cppflags: '-DGC_DEBUG_STRESS_TO_CLASS' } }
# - { name: GC_ENABLE_LAZY_SWEEP=0, env: { cppflags: '-DGC_ENABLE_LAZY_SWEEP=0' } }
# - { name: GC_PROFILE_DETAIL_MEMOTY, env: { cppflags: '-DGC_PROFILE_DETAIL_MEMOTY' } }
# - { name: GC_PROFILE_MORE_DETAIL, env: { cppflags: '-DGC_PROFILE_MORE_DETAIL' } }
@@ -194,49 +198,53 @@ jobs:
# - { name: RGENGC_ESTIMATE_OLDMALLOC, env: { cppflags: '-DRGENGC_ESTIMATE_OLDMALLOC' } }
# - { name: RGENGC_FORCE_MAJOR_GC, env: { cppflags: '-DRGENGC_FORCE_MAJOR_GC' } }
# - { name: RGENGC_OBJ_INFO, env: { cppflags: '-DRGENGC_OBJ_INFO' } }
-# - { name: RGENGC_OLD_NEWOBJ_CHECK, env: { cppflags: '-DRGENGC_OLD_NEWOBJ_CHECK' } }
# - { name: RGENGC_PROFILE, env: { cppflags: '-DRGENGC_PROFILE' } }
# - { name: VM_DEBUG_BP_CHECK, env: { cppflags: '-DVM_DEBUG_BP_CHECK' } }
# - { name: VM_DEBUG_VERIFY_METHOD_CACHE, env: { cppflags: '-DVM_DEBUG_VERIFY_METHOD_CACHE' } }
- - { name: enable-yjit, env: { append_configure: '--enable-yjit --disable-rjit' }, rust: true }
+ - { name: enable-yjit, env: { append_configure: '--enable-yjit --disable-rjit' } }
- { name: enable-rjit, env: { append_configure: '--enable-rjit --disable-yjit' } }
- - { name: YJIT_FORCE_ENABLE, env: { cppflags: '-DYJIT_FORCE_ENABLE' }, rust: true }
+ - { name: YJIT_FORCE_ENABLE, env: { cppflags: '-DYJIT_FORCE_ENABLE' } }
# - { name: RJIT_FORCE_ENABLE, env: { cppflags: '-DRJIT_FORCE_ENABLE' } }
+ - { name: UNIVERSAL_PARSER, env: { cppflags: '-DUNIVERSAL_PARSER' } }
name: ${{ matrix.entry.name }}
+
runs-on: ubuntu-latest
+
container:
- image: ghcr.io/ruby/ruby-ci-image:${{ matrix.entry.container || matrix.entry.env.default_cc || 'clang-16' }}
+ image: ghcr.io/ruby/ruby-ci-image:${{ matrix.entry.container || matrix.entry.env.default_cc || 'clang-18' }}
options: --user root
- if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+
+ if: >-
+ ${{!(false
+ || contains(github.event.head_commit.message, '[DOC]')
+ || contains(github.event.head_commit.message, 'Document')
+ || contains(github.event.pull_request.title, '[DOC]')
+ || contains(github.event.pull_request.title, 'Document')
+ || contains(github.event.pull_request.labels.*.name, 'Document')
+ || (github.event_name == 'push' && github.actor == 'dependabot[bot]')
+ )}}
+
env: ${{ matrix.entry.env || matrix.env }}
+
steps:
- run: id
working-directory:
- - run: mkdir build
- working-directory:
- - name: setenv
- run: |
- echo "GNUMAKEFLAGS=-sj$((1 + $(nproc --all)))" >> $GITHUB_ENV
- - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+
+ - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
with:
- path: src
- - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
+ sparse-checkout-cone-mode: false
+ sparse-checkout: /.github
+
+ - uses: ./.github/actions/setup/directories
with:
- path: src/.downloaded-cache
- key: downloaded-cache
- - name: Install Rust
- if: ${{ matrix.entry.rust }}
- run: sudo apt-get update && sudo apt install -y rustc
- - name: autogen
- run: |
- if [ ! -f ./autogen.sh ]; then
- ls -la
- fi
- ./autogen.sh
- working-directory: src
+ srcdir: src
+ builddir: build
+ makeup: true
+ clean: true
+
- name: Run configure
run: >
../src/configure -C ${default_configure} ${append_configure}
@@ -246,40 +254,50 @@ jobs:
matrix.entry.crosshost || '"${default_cc}${append_cc:+ $append_cc}"'
}}
--${{ matrix.entry.shared || 'enable' }}-shared
- - name: Add to ext/Setup # statically link just the etc extension
- run: mkdir ext && echo etc >> ext/Setup
- if: ${{ matrix.entry.name == 'ext/Setup' }}
- - run: make extract-extlibs
- - run: make incs
+
+ - name: Add to ext/Setup
+ id: ext-setup
+ run: |
+ mkdir ext
+ cd ext
+ for ext in {${{ matrix.entry.static-exts }}}; do
+ echo "${ext}"
+ done >> Setup
+ if: ${{ (matrix.entry.static-exts || '') != '' }}
+
+ - name: Clean up ext/Setup
+ uses: gacts/run-and-post-run@7aec950f3b114c4fcf6012070c3709ecff0eb6f8 # v1.4.0
+ with:
+ shell: bash
+ working-directory: build
+ post: rm ext/Setup
+ if: ${{ steps.ext-setup.outcome == 'success' }}
+
- run: make showflags
+
- run: make
+
- run: make test
+
- run: make install
if: ${{ matrix.entry.check }}
+
- run: make test-tool
if: ${{ matrix.entry.check }}
+
- run: make test-all TESTS='-- ruby -ext-'
if: ${{ matrix.entry.check }}
+
- run: make test-spec
env:
CHECK_LEAKS: true
if: ${{ matrix.entry.check }}
- - run: make test-annocheck
- if: ${{ matrix.entry.check && endsWith(matrix.entry.name, 'annocheck') }}
- - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ - uses: ./.github/actions/slack
with:
- payload: |
- {
- "ci": "GitHub Actions",
- "env": "${{ github.workflow }} / ${{ matrix.entry.name }}",
- "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
- "commit": "${{ github.sha }}",
- "branch": "${{ github.ref_name }}"
- }
- env:
+ label: ${{ matrix.entry.name }}
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
- if: ${{ failure() && github.event_name == 'push' }}
+ if: ${{ failure() }}
defaults:
run:
diff --git a/.github/workflows/dependabot_automerge.yml b/.github/workflows/dependabot_automerge.yml
index 67fd0a9c5c..80112a0af3 100644
--- a/.github/workflows/dependabot_automerge.yml
+++ b/.github/workflows/dependabot_automerge.yml
@@ -6,21 +6,25 @@ on:
jobs:
automerge:
runs-on: ubuntu-latest
+
if: ${{ github.actor == 'dependabot[bot]' }}
+
steps:
- name: Dependabot metadata
- uses: dependabot/fetch-metadata@cd6e996708b8cfe0b639401134a3b9a3177be7b2 # v1.5.1
+ uses: dependabot/fetch-metadata@5e5f99653a5b510e8555840e80cbf1514ad4af38 # v2.1.0
id: metadata
+
- name: Wait for status checks
- uses: lewagon/wait-on-check-action@e106e5c43e8ca1edea6383a39a01c5ca495fd812 # v1.3.1
+ uses: lewagon/wait-on-check-action@ccfb013c15c8afb7bf2b7c028fb74dc5a068cccc # v1.3.4
with:
repo-token: ${{ secrets.MATZBOT_GITHUB_TOKEN }}
ref: ${{ github.event.pull_request.head.sha || github.sha }}
check-regexp: 'make \(check, .*\)'
wait-interval: 30
+
- name: Auto-merge for Dependabot PRs
- if: ${{ steps.metadata.outputs.update-type == 'version-update:semver-minor' || steps.metadata.outputs.update-type == 'version-update:semver-patch'}}
+ if: ${{ steps.metadata.outputs.update-type == 'version-update:semver-minor' || steps.metadata.outputs.update-type == 'version-update:semver-patch' }}
run: gh pr merge --auto --rebase "$PR_URL"
env:
- PR_URL: ${{github.event.pull_request.html_url}}
+ PR_URL: ${{ github.event.pull_request.html_url }}
GITHUB_TOKEN: ${{ secrets.MATZBOT_GITHUB_TOKEN }}
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
index 908a3f5b61..54e30129e4 100644
--- a/.github/workflows/macos.yml
+++ b/.github/workflows/macos.yml
@@ -7,20 +7,11 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
pull_request:
- paths-ignore:
- - 'doc/**'
- - '**/man'
- - '**.md'
- - '**.rdoc'
- - '**/.document'
+ # Do not use paths-ignore for required status checks
+ # https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks#handling-skipped-but-required-checks
merge_group:
- paths-ignore:
- - 'doc/**'
- - '**/man'
- - '**.md'
- - '**.rdoc'
- - '**/.document'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -33,81 +24,115 @@ jobs:
make:
strategy:
matrix:
- test_task: ["check"] # "test-bundler-parallel", "test-bundled-gems"
- os:
- - macos-11
- - macos-12
+ include:
+ - test_task: check
+ - test_task: test-all
+ test_opts: --repeat-count=2
+ - test_task: test-bundler-parallel
+ - test_task: test-bundled-gems
+ - test_task: check
+ os: macos-12
+ - test_task: check
+ os: macos-13
fail-fast: false
+
env:
- GITPULLOPTIONS: --no-tags origin ${{github.ref}}
- runs-on: ${{ matrix.os }}
- if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+ GITPULLOPTIONS: --no-tags origin ${{ github.ref }}
+
+ runs-on: ${{ matrix.os || (github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'macos-14')}}
+
+ if: >-
+ ${{!(false
+ || contains(github.event.head_commit.message, '[DOC]')
+ || contains(github.event.head_commit.message, 'Document')
+ || contains(github.event.pull_request.title, '[DOC]')
+ || contains(github.event.pull_request.title, 'Document')
+ || contains(github.event.pull_request.labels.*.name, 'Document')
+ || (github.event_name == 'push' && github.actor == 'dependabot[bot]')
+ )}}
+
steps:
- - run: mkdir build
- working-directory:
- - name: git config
- run: |
- git config --global advice.detachedHead 0
- git config --global init.defaultBranch garbage
- - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+ - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
with:
- path: src
- - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
- with:
- path: src/.downloaded-cache
- key: downloaded-cache
+ sparse-checkout-cone-mode: false
+ sparse-checkout: /.github
+
- name: Install libraries
- run: |
- brew upgrade
- brew install gmp libffi openssl@1.1 zlib autoconf automake libtool readline
- working-directory: src
- - name: Set ENV
- run: |
- echo "MAKEFLAGS=-j$((1 + $(sysctl -n hw.activecpu)))" >> $GITHUB_ENV
- for lib in openssl@1.1 readline; do
- CONFIGURE_ARGS="${CONFIGURE_ARGS:+$CONFIGURE_ARGS }--with-${lib%@*}-dir=$(brew --prefix $lib)"
- done
- echo CONFIGURE_ARGS="${CONFIGURE_ARGS}" >> $GITHUB_ENV
- - run: ./autogen.sh
- working-directory: src
+ uses: ./.github/actions/setup/macos
+
+ - uses: ./.github/actions/setup/directories
+ with:
+ srcdir: src
+ builddir: build
+ makeup: true
+ clean: true
+ dummy-files: ${{ matrix.test_task == 'check' }}
+ # Set fetch-depth: 0 so that Launchable can receive commits information.
+ fetch-depth: 10
+
- name: Run configure
run: ../src/configure -C --disable-install-doc
- - run: make incs
+
- run: make prepare-gems
if: ${{ matrix.test_task == 'test-bundled-gems' }}
+
- run: make
+
+ - name: Set test options for skipped tests
+ run: |
+ set -x
+ TESTS="$(echo "${{ matrix.skipped_tests }}" | sed 's| |$$/ -n!/|g;s|^|-n!/|;s|$|$$/|')"
+ echo "TESTS=${TESTS}" >> $GITHUB_ENV
+ if: ${{ matrix.test_task == 'check' && matrix.skipped_tests }}
+
+ - name: Set up Launchable
+ uses: ./.github/actions/launchable/setup
+ with:
+ os: ${{ matrix.os || (github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'macos-14')}}
+ test-opts: ${{ matrix.test_opts }}
+ launchable-token: ${{ secrets.LAUNCHABLE_TOKEN }}
+ builddir: build
+ srcdir: src
+ continue-on-error: true
+
+ - name: Set extra test options
+ run: echo "TESTS=$TESTS ${{ matrix.test_opts }}" >> $GITHUB_ENV
+ if: matrix.test_opts
+
- name: make ${{ matrix.test_task }}
run: |
- make -s ${{ matrix.test_task }} ${TESTS:+TESTS=`echo "$TESTS" | sed 's| |$$/ -n!/|g;s|^|-n!/|;s|$|$$/|'`}
- timeout-minutes: 40
+ make -s ${{ matrix.test_task }} ${TESTS:+TESTS="$TESTS"}
+ timeout-minutes: 60
env:
- RUBY_TESTOPTS: "-q --tty=no"
- TESTS: ${{ matrix.test_task == 'check' && matrix.skipped_tests || '' }}
- TEST_BUNDLED_GEMS_ALLOW_FAILURES: ""
- PRECHECK_BUNDLED_GEMS: "no"
+ RUBY_TESTOPTS: '-q --tty=no'
+ TEST_BUNDLED_GEMS_ALLOW_FAILURES: ''
+ PRECHECK_BUNDLED_GEMS: 'no'
+
- name: make skipped tests
run: |
- make -s test-all TESTS=`echo "$TESTS" | sed 's| |$$/ -n/|g;s|^|-n/|;s|$|$$/|'`
+ make -s test-all TESTS="${TESTS//-n!\//-n/}"
env:
- GNUMAKEFLAGS: ""
- RUBY_TESTOPTS: "-v --tty=no"
- TESTS: ${{ matrix.skipped_tests }}
- PRECHECK_BUNDLED_GEMS: "no"
- if: ${{ matrix.test_task == 'check' && matrix.skipped_tests != '' }}
+ GNUMAKEFLAGS: ''
+ RUBY_TESTOPTS: '-v --tty=no'
+ PRECHECK_BUNDLED_GEMS: 'no'
+ if: ${{ matrix.test_task == 'check' && matrix.skipped_tests }}
continue-on-error: ${{ matrix.continue-on-skipped_tests || false }}
- - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+
+ - uses: ./.github/actions/slack
with:
- payload: |
- {
- "ci": "GitHub Actions",
- "env": "${{ matrix.os }} / ${{ matrix.test_task }}${{ matrix.configure }}",
- "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
- "commit": "${{ github.sha }}",
- "branch": "${{ github.ref_name }}"
- }
- env:
+ label: ${{ matrix.os }} / ${{ matrix.test_task }}
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
- if: ${{ failure() && github.event_name == 'push' }}
+ if: ${{ failure() }}
+
+ result:
+ if: ${{ always() }}
+ name: ${{ github.workflow }} result
+ runs-on: macos-latest
+ needs: [make]
+ steps:
+ - run: exit 1
+ working-directory:
+ if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
defaults:
run:
diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml
index 158d962747..ed8850ebcb 100644
--- a/.github/workflows/mingw.yml
+++ b/.github/workflows/mingw.yml
@@ -7,6 +7,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
pull_request:
paths-ignore:
- 'doc/**'
@@ -14,13 +15,8 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
merge_group:
- paths-ignore:
- - 'doc/**'
- - '**/man'
- - '**.md'
- - '**.rdoc'
- - '**/.document'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -35,49 +31,44 @@ permissions:
jobs:
make:
runs-on: windows-2022
+
name: ${{ github.workflow }} (${{ matrix.msystem }})
+
env:
MSYSTEM: ${{ matrix.msystem }}
MSYS2_ARCH: x86_64
- CHOST: "x86_64-w64-mingw32"
- CFLAGS: "-march=x86-64 -mtune=generic -O3 -pipe"
- CXXFLAGS: "-march=x86-64 -mtune=generic -O3 -pipe"
- CPPFLAGS: "-D_FORTIFY_SOURCE=2 -D__USE_MINGW_ANSI_STDIO=1 -DFD_SETSIZE=2048"
- LDFLAGS: "-pipe"
- GITPULLOPTIONS: --no-tags origin ${{github.ref}}
+ CHOST: 'x86_64-w64-mingw32'
+ CFLAGS: '-march=x86-64 -mtune=generic -O3 -pipe'
+ CXXFLAGS: '-march=x86-64 -mtune=generic -O3 -pipe'
+ CPPFLAGS: '-D_FORTIFY_SOURCE=2 -D__USE_MINGW_ANSI_STDIO=1 -DFD_SETSIZE=2048'
+ LDFLAGS: '-pipe'
+ GITPULLOPTIONS: --no-tags origin ${{ github.ref }}
+
strategy:
matrix:
include:
# To mitigate flakiness of MinGW CI, we test only one runtime that newer MSYS2 uses.
- - msystem: "UCRT64"
- base_ruby: head
- test_task: "check"
- test-all-opts: "--name=!/TestObjSpace#test_reachable_objects_during_iteration/"
+ - msystem: 'UCRT64'
+ baseruby: '3.0'
+ test_task: 'check'
+ test-all-opts: '--name=!/TestObjSpace#test_reachable_objects_during_iteration/'
fail-fast: false
- if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+
+ if: >-
+ ${{!(false
+ || contains(github.event.head_commit.message, '[DOC]')
+ || contains(github.event.head_commit.message, 'Document')
+ || contains(github.event.pull_request.title, '[DOC]')
+ || contains(github.event.pull_request.title, 'Document')
+ || contains(github.event.pull_request.labels.*.name, 'Document')
+ || (github.event_name == 'push' && github.actor == 'dependabot[bot]')
+ )}}
+
steps:
- - run: mkdir build
- working-directory:
- - name: git config
- run: |
- git config --global core.autocrlf false
- git config --global core.eol lf
- git config --global advice.detachedHead 0
- git config --global init.defaultBranch garbage
- - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- with:
- path: src
- - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
- with:
- path: src/.downloaded-cache
- key: downloaded-cache
- name: Set up Ruby & MSYS2
- uses: ruby/setup-ruby@8a45918450651f5e4784b6031db26f4b9f76b251 # v1.150.0
+ uses: ruby/setup-ruby@1198b074305f9356bd56dd4b311757cc0dab2f1c # v1.175.1
with:
- ruby-version: ${{ matrix.base_ruby }}
- - name: set env
- run: |
- echo "GNUMAKEFLAGS=-j$((2 * NUMBER_OF_PROCESSORS))" >> $GITHUB_ENV
+ ruby-version: ${{ matrix.baseruby }}
- name: where check
run: |
@@ -86,11 +77,12 @@ jobs:
mv /c/Windows/System32/libssl-1_1-x64.dll /c/Windows/System32/libssl-1_1-x64.dll_
result=true
for e in gcc.exe ragel.exe make.exe libcrypto-1_1-x64.dll libssl-1_1-x64.dll; do
- echo '##['group']'$'\033[93m'$e$'\033[m'
+ echo ::group::$'\033[93m'$e$'\033[m'
where $e || result=false
- echo '##['endgroup']'
+ echo ::endgroup::
done
$result
+ working-directory:
- name: version check
run: |
@@ -98,80 +90,69 @@ jobs:
result=true
for e in gcc ragel make "openssl version"; do
case "$e" in *" "*) ;; *) e="$e --version";; esac
- echo '##['group']'$'\033[93m'$e$'\033[m'
+ echo ::group::$'\033[93m'$e$'\033[m'
$e || result=false
- echo '##['endgroup']'
+ echo ::endgroup::
done
$result
+ working-directory:
- - name: autogen
- run: |
- ./autogen.sh
- working-directory: src
+ - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
+ with:
+ sparse-checkout-cone-mode: false
+ sparse-checkout: /.github
+
+ - uses: ./.github/actions/setup/directories
+ with:
+ srcdir: src
+ builddir: build
+ makeup: true
- name: configure
run: >
../src/configure --disable-install-doc --prefix=/.
--build=$CHOST --host=$CHOST --target=$CHOST
- - name: update
- run: |
- make incs
-
- - name: download gems
- run: |
- make update-gems
-
- name: make all
timeout-minutes: 30
- run: |
- make
+ run: make
- name: make install
- run: |
- make DESTDIR=../install install-nodoc
+ run: make DESTDIR=../install install-nodoc
- name: test
timeout-minutes: 30
- run: |
- make test
+ run: make test
+ shell: cmd
env:
- GNUMAKEFLAGS: ""
- RUBY_TESTOPTS: "-v --tty=no"
- if: ${{matrix.test_task == 'check' || matrix.test_task == 'test'}}
+ GNUMAKEFLAGS: ''
+ RUBY_TESTOPTS: '-v --tty=no'
+ if: ${{ matrix.test_task == 'check' || matrix.test_task == 'test' }}
- name: test-all
timeout-minutes: 45
+ shell: cmd
run: |
- # Actions uses UTF8, causes test failures, similar to normal OS setup
- chcp.com 437
make ${{ StartsWith(matrix.test_task, 'test/') && matrix.test_task || 'test-all' }}
env:
RUBY_TESTOPTS: >-
--retry --job-status=normal --show-skip --timeout-scale=1.5
${{ matrix.test-all-opts }}
BUNDLER_VERSION:
- if: ${{matrix.test_task == 'check' || matrix.test_task == 'test-all' || StartsWith(matrix.test_task, 'test/')}}
+ if: ${{ matrix.test_task == 'check' || matrix.test_task == 'test-all' || StartsWith(matrix.test_task, 'test/') }}
- name: test-spec
timeout-minutes: 10
run: |
make ${{ StartsWith(matrix.test_task, 'spec/') && matrix.test_task || 'test-spec' }}
- if: ${{matrix.test_task == 'check' || matrix.test_task == 'test-spec' || StartsWith(matrix.test_task, 'spec/')}}
+ shell: cmd
+ if: ${{ matrix.test_task == 'check' || matrix.test_task == 'test-spec' || StartsWith(matrix.test_task, 'spec/') }}
- - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ - uses: ./src/.github/actions/slack
with:
- payload: |
- {
- "ci": "GitHub Actions",
- "env": "${{ github.workflow }} ${{ matrix.msystem }} / ${{ matrix.test_task }}",
- "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
- "commit": "${{ github.sha }}",
- "branch": "${{ github.ref_name }}"
- }
- env:
+ label: ${{ matrix.msystem }} / ${{ matrix.test_task }}
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
- if: ${{ failure() && github.event_name == 'push' }}
+ if: ${{ failure() }}
defaults:
run:
diff --git a/.github/workflows/pr-playground.yml b/.github/workflows/pr-playground.yml
new file mode 100644
index 0000000000..cc06006142
--- /dev/null
+++ b/.github/workflows/pr-playground.yml
@@ -0,0 +1,127 @@
+name: Post Playground link to PR
+on:
+ pull_request_target:
+ types: [labeled]
+ workflow_run:
+ workflows: ["WebAssembly"]
+ types: [completed]
+
+jobs:
+ post-summary:
+ name: Post Playground link
+ runs-on: ubuntu-latest
+ permissions:
+ pull-requests: write
+ # Post a comment only if the PR status check is passed and the PR is labeled with `Playground`.
+ # Triggered twice: when the PR is labeled and when PR build is passed.
+ if: >-
+ ${{ false
+ || (true
+ && github.event_name == 'pull_request_target'
+ && contains(github.event.pull_request.labels.*.name, 'Playground'))
+ || (true
+ && github.event_name == 'workflow_run'
+ && github.event.workflow_run.conclusion == 'success'
+ && github.event.workflow_run.event == 'pull_request')
+ }}
+ steps:
+ - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const fs = require('fs/promises');
+
+ const buildWorkflowPath = '.github/workflows/wasm.yml';
+ const findSuccessfuBuildRun = async (pr) => {
+ const opts = github.rest.actions.listWorkflowRunsForRepo.endpoint.merge({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ status: 'success',
+ branch: pr.head.ref,
+ });
+ const runs = await github.paginate(opts);
+ const buildRun = runs.find(run => run.path == buildWorkflowPath);
+ return buildRun;
+ }
+
+ const postComment = async (body, pr) => {
+ const { data: comments } = await github.rest.issues.listComments({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: pr.number,
+ });
+
+ const commentOpts = { owner: context.repo.owner, repo: context.repo.repo, body: comment };
+
+ const existingComment = comments.find(comment => comment.body.startsWith(magicComment));
+ if (existingComment) {
+ core.info(`Updating existing comment: ${existingComment.html_url}`);
+ await github.rest.issues.updateComment({
+ ...commentOpts, comment_id: existingComment.id
+ });
+ } else {
+ await github.rest.issues.createComment({
+ ...commentOpts, issue_number: pr.number
+ });
+ }
+ }
+
+ const derivePRNumber = async () => {
+ if (context.payload.pull_request) {
+ return context.payload.pull_request.number;
+ }
+ // Workaround for https://github.com/orgs/community/discussions/25220
+
+ const { data: { artifacts } } = await github.rest.actions.listWorkflowRunArtifacts({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ run_id: context.payload.workflow_run.id,
+ });
+ const artifact = artifacts.find(artifact => artifact.name == 'github-pr-info');
+ if (!artifact) {
+ throw new Error('Cannot find github-pr-info.txt artifact');
+ }
+
+ const { data } = await github.rest.actions.downloadArtifact({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ artifact_id: artifact.id,
+ archive_format: 'zip',
+ });
+
+ await fs.writeFile('pr-info.zip', Buffer.from(data));
+ await exec.exec('unzip', ['pr-info.zip']);
+ return await fs.readFile('github-pr-info.txt', 'utf8');
+ }
+
+ const prNumber = await derivePRNumber();
+
+ const { data: pr } = await github.rest.pulls.get({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ pull_number: prNumber,
+ });
+
+ core.info(`Checking if the PR ${prNumber} is labeled with Playground...`);
+ if (!pr.labels.some(label => label.name == 'Playground')) {
+ core.info(`The PR is not labeled with Playground.`);
+ return;
+ }
+
+ core.info(`Checking if the build is successful for ${pr.head.ref} in ${pr.head.repo.owner.login}/${pr.head.repo.name}...`);
+ const buildRun = await findSuccessfuBuildRun(pr);
+ if (!buildRun) {
+ core.info(`No successful build run found for ${buildWorkflowPath} on ${pr.head.ref} yet.`);
+ return;
+ }
+ core.info(`Found a successful build run: ${buildRun.html_url}`);
+
+ const runLink = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
+ const magicComment = `<!-- AUTO-GENERATED-COMMENT-PR-PLAYGROUND -->`;
+ const comment = `${magicComment}
+ **Try on Playground**: https://ruby.github.io/play-ruby?run=${buildRun.id}
+ This is an automated comment by [\`pr-playground.yml\`](${runLink}) workflow.
+ `;
+ core.info(`Comment: ${comment}`);
+ await postComment(comment, pr);
+
diff --git a/.github/workflows/prism.yml b/.github/workflows/prism.yml
new file mode 100644
index 0000000000..95a36f1634
--- /dev/null
+++ b/.github/workflows/prism.yml
@@ -0,0 +1,114 @@
+name: Prism
+on:
+ push:
+ paths-ignore:
+ - 'doc/**'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ - '**.[1-8]'
+ - '**.ronn'
+ - '.*.yml'
+ pull_request:
+ paths-ignore:
+ - 'doc/**'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ - '**.[1-8]'
+ - '**.ronn'
+ - '.*.yml'
+ merge_group:
+
+concurrency:
+ group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
+ cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+
+permissions:
+ contents: read
+
+jobs:
+ make:
+ strategy:
+ matrix:
+ # main variables included in the job name
+ test_task: [check]
+ run_opts: ['--parser=prism']
+ arch: ['']
+ fail-fast: false
+
+ env:
+ GITPULLOPTIONS: --no-tags origin ${{ github.ref }}
+ RUBY_DEBUG: ci
+ SETARCH: ${{ matrix.arch && format('setarch {0}', matrix.arch) }}
+
+ runs-on: ubuntu-22.04
+
+ if: >-
+ ${{!(false
+ || contains(github.event.head_commit.message, '[DOC]')
+ || contains(github.event.head_commit.message, 'Document')
+ || contains(github.event.pull_request.title, '[DOC]')
+ || contains(github.event.pull_request.title, 'Document')
+ || contains(github.event.pull_request.labels.*.name, 'Document')
+ || (github.event_name == 'push' && github.actor == 'dependabot[bot]')
+ )}}
+
+ steps:
+ - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
+ with:
+ sparse-checkout-cone-mode: false
+ sparse-checkout: /.github
+
+ - uses: ./.github/actions/setup/ubuntu
+
+ - uses: ./.github/actions/setup/directories
+ with:
+ srcdir: src
+ builddir: build
+ makeup: true
+
+ - name: Run configure
+ env:
+ arch: ${{ matrix.arch }}
+ run: >-
+ $SETARCH ../src/configure -C --disable-install-doc cppflags=-DRUBY_DEBUG
+ ${arch:+--target=$arch-$OSTYPE --host=$arch-$OSTYPE}
+
+ - run: $SETARCH make
+
+ - name: make test
+ run: |
+ $SETARCH make -s test RUN_OPTS="$RUN_OPTS"
+ timeout-minutes: 30
+ env:
+ GNUMAKEFLAGS: ''
+ RUBY_TESTOPTS: '-v --tty=no'
+ RUN_OPTS: ${{ matrix.run_opts }}
+
+ - name: make test-all
+ run: |
+ $SETARCH make -s test-all RUN_OPTS="$RUN_OPTS"
+ timeout-minutes: 40
+ env:
+ GNUMAKEFLAGS: ''
+ RUBY_TESTOPTS: '-q --tty=no --excludes-dir="../src/test/.excludes-prism" --exclude="test_ast.rb" --exclude="error_highlight/test_error_highlight.rb" --exclude="prism/encoding_test.rb" --exclude="prism/locals_test.rb" --exclude="prism/newline_test.rb"'
+ RUN_OPTS: ${{ matrix.run_opts }}
+
+ - name: make test-prism-spec
+ run: |
+ $SETARCH make -s test-prism-spec SPECOPTS="$SPECOPTS"
+ timeout-minutes: 10
+ env:
+ GNUMAKEFLAGS: ''
+ SPECOPTS: "-T -W:no-experimental -T --parser=prism"
+
+ - uses: ./.github/actions/slack
+ with:
+ label: ${{ matrix.run_opts }}
+ SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
+ if: ${{ failure() }}
+
+defaults:
+ run:
+ working-directory: build
diff --git a/.github/workflows/rjit-bindgen.yml b/.github/workflows/rjit-bindgen.yml
index a8cf5853d1..5ba544c673 100644
--- a/.github/workflows/rjit-bindgen.yml
+++ b/.github/workflows/rjit-bindgen.yml
@@ -7,6 +7,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
pull_request:
paths-ignore:
- 'doc/**'
@@ -14,13 +15,8 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
merge_group:
- paths-ignore:
- - 'doc/**'
- - '**/man'
- - '**.md'
- - '**.rdoc'
- - '**/.document'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -36,68 +32,54 @@ jobs:
include:
- task: rjit-bindgen
fail-fast: false
+
runs-on: ubuntu-20.04
- if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+
+ if: >-
+ ${{!(false
+ || contains(github.event.head_commit.message, '[DOC]')
+ || contains(github.event.head_commit.message, 'Document')
+ || contains(github.event.pull_request.title, '[DOC]')
+ || contains(github.event.pull_request.title, 'Document')
+ || contains(github.event.pull_request.labels.*.name, 'Document')
+ || (github.event_name == 'push' && github.actor == 'dependabot[bot]')
+ )}}
+
steps:
- - run: mkdir build
- working-directory:
- - name: Set ENV
- run: |
- echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
- - name: Install libraries
- run: |
- set -x
- sudo apt-get update -q || :
- sudo apt-get install --no-install-recommends -q -y \
- build-essential \
- libssl-dev libyaml-dev libreadline6-dev \
- zlib1g-dev libncurses5-dev libffi-dev \
- libclang1-10 \
- autoconf
- sudo apt-get install -q -y pkg-config || :
- name: Set up Ruby
- uses: ruby/setup-ruby@8a45918450651f5e4784b6031db26f4b9f76b251 # v1.150.0
+ uses: ruby/setup-ruby@1198b074305f9356bd56dd4b311757cc0dab2f1c # v1.175.1
with:
ruby-version: '3.1'
- - name: git config
- run: |
- git config --global advice.detachedHead 0
- git config --global init.defaultBranch garbage
- - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+
+ - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
with:
- path: src
- - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
+ sparse-checkout-cone-mode: false
+ sparse-checkout: /.github
+
+ - uses: ./.github/actions/setup/ubuntu
+
+ - uses: ./.github/actions/setup/directories
with:
- path: src/.downloaded-cache
- key: downloaded-cache
- - name: Fixed world writable dirs
- run: |
- chmod -v go-w $HOME $HOME/.config
- sudo chmod -R go-w /usr/share
- sudo bash -c 'IFS=:; for d in '"$PATH"'; do chmod -v go-w $d; done' || :
- - run: ./autogen.sh
- working-directory: src
+ srcdir: src
+ builddir: build
+ makeup: true
+
- name: Run configure
run: ../src/configure -C --disable-install-doc --prefix=$(pwd)/install --enable-yjit=dev_nodebug
- - run: make incs
+
- run: make
+
- run: make install
+
- run: make ${{ matrix.task }}
+
- run: git diff --exit-code
working-directory: src
- - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+
+ - uses: ./.github/actions/slack
with:
- payload: |
- {
- "ci": "GitHub Actions",
- "env": "RJIT / bindgen",
- "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
- "commit": "${{ github.sha }}",
- "branch": "${{ github.ref_name }}"
- }
- env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
- if: ${{ failure() && github.event_name == 'push' }}
+ if: ${{ failure() }}
defaults:
run:
diff --git a/.github/workflows/rjit.yml b/.github/workflows/rjit.yml
index f7dc9ad0ee..7a5e6cf7c0 100644
--- a/.github/workflows/rjit.yml
+++ b/.github/workflows/rjit.yml
@@ -8,6 +8,7 @@ on:
- '**/.document'
- '**.[1-8]'
- '**.ronn'
+ - '.*.yml'
pull_request:
paths-ignore:
- 'doc/**'
@@ -16,14 +17,8 @@ on:
- '**/.document'
- '**.[1-8]'
- '**.ronn'
+ - '.*.yml'
merge_group:
- paths-ignore:
- - 'doc/**'
- - '**.md'
- - '**.rdoc'
- - '**/.document'
- - '**.[1-8]'
- - '**.ronn'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -41,93 +36,78 @@ jobs:
run_opts: ['--rjit-call-threshold=1']
arch: ['']
fail-fast: false
+
env:
- GITPULLOPTIONS: --no-tags origin ${{github.ref}}
+ GITPULLOPTIONS: --no-tags origin ${{ github.ref }}
RUBY_DEBUG: ci
SETARCH: ${{ matrix.arch && format('setarch {0}', matrix.arch) }}
+
runs-on: ubuntu-22.04
- if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+
+ if: >-
+ ${{!(false
+ || contains(github.event.head_commit.message, '[DOC]')
+ || contains(github.event.head_commit.message, 'Document')
+ || contains(github.event.pull_request.title, '[DOC]')
+ || contains(github.event.pull_request.title, 'Document')
+ || contains(github.event.pull_request.labels.*.name, 'Document')
+ || (github.event_name == 'push' && github.actor == 'dependabot[bot]')
+ )}}
+
steps:
- - run: mkdir build
- working-directory:
- - name: Set ENV
- run: |
- echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
- - name: Install libraries
- env:
- arch: ${{matrix.arch}}
- run: |
- set -x
- arch=${arch:+:${arch/i[3-6]86/i386}}
- ${arch:+sudo dpkg --add-architecture ${arch#:}}
- sudo apt-get update -q || :
- sudo apt-get install --no-install-recommends -q -y \
- ${arch:+cross}build-essential${arch/:/-} \
- libssl-dev${arch} libyaml-dev${arch} libreadline6-dev${arch} \
- zlib1g-dev${arch} libncurses5-dev${arch} libffi-dev${arch} \
- autoconf ruby libcapstone-dev
- sudo apt-get install -q -y pkg-config${arch} || :
- - name: git config
- run: |
- git config --global advice.detachedHead 0
- git config --global init.defaultBranch garbage
- - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.1.0
+ - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
with:
- path: src
- - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.0.11
+ sparse-checkout-cone-mode: false
+ sparse-checkout: /.github
+
+ - uses: ./.github/actions/setup/ubuntu
+
+ - uses: ./.github/actions/setup/directories
with:
- path: src/.downloaded-cache
- key: downloaded-cache
- - name: Fixed world writable dirs
- run: |
- chmod -v go-w $HOME $HOME/.config
- sudo chmod -R go-w /usr/share
- sudo bash -c 'IFS=:; for d in '"$PATH"'; do chmod -v go-w $d; done' || :
- - run: ./autogen.sh
- working-directory: src
+ srcdir: src
+ builddir: build
+ makeup: true
+
- name: Run configure
env:
- arch: ${{matrix.arch}}
+ arch: ${{ matrix.arch }}
run: >-
$SETARCH ../src/configure -C --disable-install-doc cppflags=-DRUBY_DEBUG
${arch:+--target=$arch-$OSTYPE --host=$arch-$OSTYPE}
+
- run: $SETARCH make
+
- name: make test
run: |
$SETARCH make -s test RUN_OPTS="$RUN_OPTS"
timeout-minutes: 30
env:
- GNUMAKEFLAGS: ""
- RUBY_TESTOPTS: "-v --tty=no"
+ GNUMAKEFLAGS: ''
+ RUBY_TESTOPTS: '-v --tty=no'
RUN_OPTS: ${{ matrix.run_opts }}
+
- name: make test-all
run: |
$SETARCH make -s test-all RUN_OPTS="$RUN_OPTS"
timeout-minutes: 40
env:
- GNUMAKEFLAGS: ""
- RUBY_TESTOPTS: "-v --tty=no"
+ GNUMAKEFLAGS: ''
+ RUBY_TESTOPTS: '-q --tty=no'
RUN_OPTS: ${{ matrix.run_opts }}
+
- name: make test-spec
run: |
$SETARCH make -s test-spec RUN_OPTS="$RUN_OPTS"
timeout-minutes: 10
env:
- GNUMAKEFLAGS: ""
+ GNUMAKEFLAGS: ''
RUN_OPTS: ${{ matrix.run_opts }}
- - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+
+ - uses: ./.github/actions/slack
with:
- payload: |
- {
- "ci": "GitHub Actions",
- "env": "RJIT / ${{ matrix.run_opts }}",
- "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
- "commit": "${{ github.sha }}",
- "branch": "${{ github.ref_name }}"
- }
- env:
+ label: ${{ matrix.run_opts }}
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
- if: ${{ failure() && github.event_name == 'push' }}
+ if: ${{ failure() }}
defaults:
run:
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
index df87330a58..00a0638e96 100644
--- a/.github/workflows/scorecards.yml
+++ b/.github/workflows/scorecards.yml
@@ -31,13 +31,13 @@ jobs:
# actions: read
steps:
- - name: "Checkout code"
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+ - name: 'Checkout code'
+ uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
with:
persist-credentials: false
- - name: "Run analysis"
- uses: ossf/scorecard-action@80e868c13c90f172d68d1f4501dee99e2479f7af # v2.1.3
+ - name: 'Run analysis'
+ uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1
with:
results_file: results.sarif
results_format: sarif
@@ -66,7 +66,7 @@ jobs:
# retention-days: 5
# Upload the results to GitHub's code scanning dashboard.
- - name: "Upload to code-scanning"
- uses: github/codeql-action/upload-sarif@0225834cc549ee0ca93cb085b92954821a145866 # v2.1.27
+ - name: 'Upload to code-scanning'
+ uses: github/codeql-action/upload-sarif@d39d31e687223d841ef683f52467bd88e9b21c14 # v2.1.27
with:
sarif_file: results.sarif
diff --git a/.github/workflows/spec_guards.yml b/.github/workflows/spec_guards.yml
index bf784021e0..4338228e17 100644
--- a/.github/workflows/spec_guards.yml
+++ b/.github/workflows/spec_guards.yml
@@ -6,13 +6,10 @@ on:
- 'spec/**'
- '!spec/*.md'
pull_request:
- paths-ignore:
+ paths:
- 'spec/**'
- '!spec/*.md'
merge_group:
- paths-ignore:
- - 'spec/**'
- - '!spec/*.md'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -24,8 +21,19 @@ permissions:
jobs:
rubyspec:
name: Rubyspec
+
runs-on: ubuntu-20.04
- if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+
+ if: >-
+ ${{!(false
+ || contains(github.event.head_commit.message, '[DOC]')
+ || contains(github.event.head_commit.message, 'Document')
+ || contains(github.event.pull_request.title, '[DOC]')
+ || contains(github.event.pull_request.title, 'Document')
+ || contains(github.event.pull_request.labels.*.name, 'Document')
+ || (github.event_name == 'push' && github.actor == 'dependabot[bot]')
+ )}}
+
strategy:
matrix:
# Specs from ruby/spec should still run on all supported Ruby versions.
@@ -34,28 +42,25 @@ jobs:
- ruby-3.0
- ruby-3.1
- ruby-3.2
+ - ruby-3.3
steps:
- - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- - uses: ruby/setup-ruby@8a45918450651f5e4784b6031db26f4b9f76b251 # v1.150.0
+ - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
+
+ - uses: ruby/setup-ruby@1198b074305f9356bd56dd4b311757cc0dab2f1c # v1.175.1
with:
ruby-version: ${{ matrix.ruby }}
bundler: none
+
- run: gem install webrick
+
- run: ruby ../mspec/bin/mspec
working-directory: spec/ruby
env:
CHECK_LEAKS: true
- - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+
+ - uses: ./.github/actions/slack
with:
- payload: |
- {
- "ci": "GitHub Actions",
- "env": "${{ github.workflow }} / rubyspec @ ${{ matrix.ruby }}",
- "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
- "commit": "${{ github.sha }}",
- "branch": "${{ github.ref_name }}"
- }
- env:
+ label: ${{ matrix.ruby }}
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
- if: ${{ failure() && github.event_name == 'push' }}
+ if: ${{ failure() }}
diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml
index d8a557ab4f..ca8f04f0c4 100644
--- a/.github/workflows/ubuntu.yml
+++ b/.github/workflows/ubuntu.yml
@@ -7,20 +7,11 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
pull_request:
- paths-ignore:
- - 'doc/**'
- - '**/man'
- - '**.md'
- - '**.rdoc'
- - '**/.document'
+ # Do not use paths-ignore for required status checks
+ # https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks#handling-skipped-but-required-checks
merge_group:
- paths-ignore:
- - 'doc/**'
- - '**/man'
- - '**.md'
- - '**.rdoc'
- - '**/.document'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -33,111 +24,129 @@ jobs:
make:
strategy:
matrix:
- test_task: [check]
- arch: ['']
- configure: ['cppflags=-DVM_CHECK_MODE']
- # specifying everything else with `include` to avoid redundant tests
include:
- test_task: check
+ configure: 'cppflags=-DVM_CHECK_MODE'
+ - test_task: check
arch: i686
- test_task: check
+ configure: '--disable-yjit'
+ - test_task: check
configure: '--enable-shared --enable-load-relative'
- - test_task: test-all TESTS=--repeat-count=2
+ - test_task: check
+ configure: '--with-shared-gc'
- test_task: test-bundler-parallel
- test_task: test-bundled-gems
+ - test_task: check
+ os: ubuntu-20.04
fail-fast: false
+
env:
- GITPULLOPTIONS: --no-tags origin ${{github.ref}}
+ GITPULLOPTIONS: --no-tags origin ${{ github.ref }}
RUBY_DEBUG: ci
- SETARCH: ${{ matrix.arch && format('setarch {0}', matrix.arch) }}
- runs-on: ubuntu-20.04
- if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+
+ runs-on: ${{ matrix.os || 'ubuntu-22.04' }}
+
+ if: >-
+ ${{!(false
+ || contains(github.event.head_commit.message, '[DOC]')
+ || contains(github.event.head_commit.message, 'Document')
+ || contains(github.event.pull_request.title, '[DOC]')
+ || contains(github.event.pull_request.title, 'Document')
+ || contains(github.event.pull_request.labels.*.name, 'Document')
+ || (github.event_name == 'push' && github.actor == 'dependabot[bot]')
+ )}}
+
steps:
- - run: mkdir build
- working-directory:
- - name: Set ENV
- run: |
- echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
- - name: Install libraries
- env:
- arch: ${{matrix.arch}}
- run: |
- set -x
- arch=${arch:+:${arch/i[3-6]86/i386}}
- ${arch:+sudo dpkg --add-architecture ${arch#:}}
- sudo apt-get update -q || :
- sudo apt-get install --no-install-recommends -q -y \
- ${arch:+cross}build-essential${arch/:/-} \
- libssl-dev${arch} libyaml-dev${arch} libreadline6-dev${arch} \
- zlib1g-dev${arch} libncurses5-dev${arch} libffi-dev${arch} \
- autoconf ruby
- sudo apt-get install -q -y pkg-config${arch} || :
- - name: git config
- run: |
- git config --global advice.detachedHead 0
- git config --global init.defaultBranch garbage
- - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+ - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
with:
- path: src
- - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
+ sparse-checkout-cone-mode: false
+ sparse-checkout: /.github
+
+ - uses: ./.github/actions/setup/ubuntu
with:
- path: src/.downloaded-cache
- key: downloaded-cache
- - name: Fixed world writable dirs
- run: |
- chmod -v go-w $HOME $HOME/.config
- sudo chmod -R go-w /usr/share
- sudo bash -c 'IFS=:; for d in '"$PATH"'; do chmod -v go-w $d; done' || :
- - run: ./autogen.sh
- working-directory: src
+ arch: ${{ matrix.arch }}
+
+ - uses: ruby/setup-ruby@1198b074305f9356bd56dd4b311757cc0dab2f1c # v1.175.1
+ with:
+ ruby-version: '3.0'
+ bundler: none
+
+ - uses: ./.github/actions/setup/directories
+ with:
+ srcdir: src
+ builddir: build
+ makeup: true
+ clean: true
+ dummy-files: ${{ matrix.test_task == 'check' }}
+ # Set fetch-depth: 10 so that Launchable can receive commits information.
+ fetch-depth: 10
+
- name: Run configure
env:
- arch: ${{matrix.arch}}
- configure: ${{matrix.configure}}
+ arch: ${{ matrix.arch }}
+ configure: ${{ matrix.configure }}
run: >-
$SETARCH ../src/configure -C --disable-install-doc ${configure:-cppflags=-DRUBY_DEBUG}
${arch:+--target=$arch-$OSTYPE --host=$arch-$OSTYPE}
- - run: $SETARCH make incs
+
- run: $SETARCH make prepare-gems
if: ${{ matrix.test_task == 'test-bundled-gems' }}
+
- run: $SETARCH make
- - name: Create dummy files in build dir
+
+ - name: Set test options for skipped tests
run: |
- $SETARCH ./miniruby -e '(("a".."z").to_a+("A".."Z").to_a+("0".."9").to_a+%w[foo bar test zzz]).each{|basename|File.write("#{basename}.rb", "raise %(do not load #{basename}.rb)")}'
- if: ${{ matrix.test_task == 'check' }}
+ set -x
+ TESTS="$(echo "${{ matrix.skipped_tests }}" | sed 's| |$$/ -n!/|g;s|^|-n!/|;s|$|$$/|')"
+ echo "TESTS=${TESTS}" >> $GITHUB_ENV
+ if: ${{ matrix.test_task == 'check' && matrix.skipped_tests }}
+
+ - name: Set up Launchable
+ uses: ./.github/actions/launchable/setup
+ with:
+ os: ${{ matrix.os || 'ubuntu-22.04' }}
+ test-opts: ${{ matrix.configure }}
+ launchable-token: ${{ secrets.LAUNCHABLE_TOKEN }}
+ builddir: build
+ srcdir: src
+ continue-on-error: true
+
- name: make ${{ matrix.test_task }}
run: >-
$SETARCH make -s ${{ matrix.test_task }}
- ${TESTS:+TESTS=`echo "$TESTS" | sed 's| |$$/ -n!/|g;s|^|-n!/|;s|$|$$/|'`}
- ${{ contains(matrix.test_task, 'bundle') && '' || 'RUBYOPT=-w' }}
+ ${TESTS:+TESTS="$TESTS"}
+ ${{ !contains(matrix.test_task, 'bundle') && 'RUBYOPT=-w' || '' }}
timeout-minutes: 40
env:
- RUBY_TESTOPTS: "-q --tty=no"
- TESTS: ${{ matrix.test_task == 'check' && matrix.skipped_tests || '' }}
- TEST_BUNDLED_GEMS_ALLOW_FAILURES: ""
- PRECHECK_BUNDLED_GEMS: "no"
+ RUBY_TESTOPTS: '-q --tty=no'
+ TEST_BUNDLED_GEMS_ALLOW_FAILURES: ''
+ PRECHECK_BUNDLED_GEMS: 'no'
+
- name: make skipped tests
run: |
- $SETARCH make -s test-all TESTS=`echo "$TESTS" | sed 's| |$$/ -n/|g;s|^|-n/|;s|$|$$/|'`
+ $SETARCH make -s test-all TESTS="${TESTS//-n!\//-n/}"
env:
- GNUMAKEFLAGS: ""
- RUBY_TESTOPTS: "-v --tty=no"
- TESTS: ${{ matrix.skipped_tests }}
- if: ${{ matrix.test_task == 'check' && matrix.skipped_tests != '' }}
+ GNUMAKEFLAGS: ''
+ RUBY_TESTOPTS: '-v --tty=no'
+ if: ${{ matrix.test_task == 'check' && matrix.skipped_tests }}
continue-on-error: ${{ matrix.continue-on-skipped_tests || false }}
- - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+
+ - uses: ./.github/actions/slack
with:
- payload: |
- {
- "ci": "GitHub Actions",
- "env": "${{ github.workflow }} / ${{ matrix.test_task }} ${{ matrix.configure }}${{ matrix.arch }}",
- "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
- "commit": "${{ github.sha }}",
- "branch": "${{ github.ref_name }}"
- }
- env:
+ label: ${{ matrix.test_task }} ${{ matrix.configure }}${{ matrix.arch }}
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
- if: ${{ failure() && github.event_name == 'push' }}
+ if: ${{ failure() }}
+
+ result:
+ if: ${{ always() }}
+ name: ${{ github.workflow }} result
+ runs-on: ubuntu-latest
+ needs: [make]
+ steps:
+ - run: exit 1
+ working-directory:
+ if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
defaults:
run:
diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml
index 9e123889d6..d91ef093e9 100644
--- a/.github/workflows/wasm.yml
+++ b/.github/workflows/wasm.yml
@@ -7,6 +7,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
pull_request:
paths-ignore:
- 'doc/**'
@@ -14,19 +15,14 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
merge_group:
- paths-ignore:
- - 'doc/**'
- - '**/man'
- - '**.md'
- - '**.rdoc'
- - '**/.document'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
-permissions: # added using https://github.com/step-security/secure-workflows
+permissions: # added using https://github.com/step-security/secure-workflows
contents: read
jobs:
@@ -35,33 +31,47 @@ jobs:
matrix:
entry:
# # wasmtime can't compile non-optimized Asyncified binary due to locals explosion
-# - { name: O0-debuginfo, optflags: "-O0", debugflags: "-g", wasmoptflags: "-O1" }
-# - { name: O1, optflags: "-O1", debugflags: "" , wasmoptflags: "-O1" }
- - { name: O2, optflags: "-O2", debugflags: "" , wasmoptflags: "-O2" }
-# - { name: O3, optflags: "-O3", debugflags: "" , wasmoptflags: "-O3" }
+# - { name: O0-debuginfo, optflags: '-O0', debugflags: '-g', wasmoptflags: '-O1' }
+# - { name: O1, optflags: '-O1', debugflags: '' , wasmoptflags: '-O1' }
+ - { name: O2, optflags: '-O2', debugflags: '', wasmoptflags: '-O2' }
+# - { name: O3, optflags: '-O3', debugflags: '' , wasmoptflags: '-O3' }
# # -O4 is equivalent to -O3 in clang, but it's different in wasm-opt
-# - { name: O4, optflags: "-O3", debugflags: "" , wasmoptflags: "-O4" }
-# - { name: Oz, optflags: "-Oz", debugflags: "" , wasmoptflags: "-Oz" }
+# - { name: O4, optflags: '-O3', debugflags: '' , wasmoptflags: '-O4' }
+# - { name: Oz, optflags: '-Oz', debugflags: '' , wasmoptflags: '-Oz' }
fail-fast: false
+
env:
RUBY_TESTOPTS: '-q --tty=no'
- GITPULLOPTIONS: --no-tags origin ${{github.ref}}
- WASI_SDK_VERSION_MAJOR: 14
+ GITPULLOPTIONS: --no-tags origin ${{ github.ref }}
+ WASI_SDK_VERSION_MAJOR: 20
WASI_SDK_VERSION_MINOR: 0
- BINARYEN_VERSION: 109
- WASMTIME_VERSION: v0.33.0
+ BINARYEN_VERSION: 113
+ WASMTIME_VERSION: v15.0.0
+
runs-on: ubuntu-20.04
- if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+
+ if: >-
+ ${{!(false
+ || contains(github.event.head_commit.message, '[DOC]')
+ || contains(github.event.head_commit.message, 'Document')
+ || contains(github.event.pull_request.title, '[DOC]')
+ || contains(github.event.pull_request.title, 'Document')
+ || contains(github.event.pull_request.labels.*.name, 'Document')
+ || (github.event_name == 'push' && github.actor == 'dependabot[bot]')
+ )}}
+
steps:
- - run: mkdir build
- working-directory:
- - name: git config
- run: |
- git config --global advice.detachedHead 0
- git config --global init.defaultBranch garbage
- - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+ - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
with:
- path: src
+ sparse-checkout-cone-mode: false
+ sparse-checkout: /.github
+
+ - uses: ./.github/actions/setup/directories
+ with:
+ srcdir: src
+ builddir: build
+ makeup: true
+
- name: Install libraries
run: |
set -ex
@@ -85,17 +95,32 @@ jobs:
wget -O - "$binaryen_url" | tar xfz -
sudo ln -fs "$PWD/binaryen-version_${BINARYEN_VERSION}/bin/wasm-opt" /usr/local/bin/wasm-opt
working-directory: src
+
- name: Set ENV
run: |
- echo "MAKEFLAGS=-j$((1 + $(sysctl -n hw.activecpu)))" >> $GITHUB_ENV
echo "WASI_SDK_PATH=/opt/wasi-sdk" >> $GITHUB_ENV
- - run: ./autogen.sh
- working-directory: src
+
+ - uses: ruby/setup-ruby@1198b074305f9356bd56dd4b311757cc0dab2f1c # v1.175.1
+ with:
+ ruby-version: '3.0'
+ bundler: none
+
+ - name: Build baseruby
+ run: |
+ set -ex
+ mkdir ../baseruby
+ pushd ../baseruby
+ ../src/configure --prefix=$PWD/install
+ make
+ make install
+
- name: Run configure
run: |
../src/configure \
--host wasm32-unknown-wasi \
+ --with-baseruby=$PWD/../baseruby/install/bin/ruby \
--with-static-linked-ext \
+ --with-ext=cgi/escape,continuation,coverage,date,digest/bubblebabble,digest,digest/md5,digest/rmd160,digest/sha1,digest/sha2,etc,fcntl,json,json/generator,json/parser,objspace,pathname,rbconfig/sizeof,ripper,stringio,strscan,monitor \
LDFLAGS=" \
-Xlinker --stack-first \
-Xlinker -z -Xlinker stack-size=16777216 \
@@ -106,28 +131,44 @@ jobs:
# miniruby may not be built when cross-compling
- run: make mini ruby
+
+ - run: make install DESTDIR=$PWD/../install
+ - run: tar cfz ../install.tar.gz -C ../install .
+
+ - name: Upload artifacts
+ uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
+ with:
+ name: ruby-wasm-install
+ path: ${{ github.workspace }}/install.tar.gz
+ - name: Show Playground URL to try the build
+ run: |
+ echo "Try on Playground: https://ruby.github.io/play-ruby?run=$GITHUB_RUN_ID" >> $GITHUB_STEP_SUMMARY
+
- name: Run basictest
run: wasmtime run ./../build/miniruby --mapdir /::./ -- basictest/test.rb
working-directory: src
+
- name: Run bootstraptest (no thread)
run: |
NO_THREAD_TESTS="$(grep -L Thread -R ./bootstraptest | awk -F/ '{ print $NF }' | uniq | sed -n 's/test_\(.*\).rb/\1/p' | paste -s -d, -)"
ruby ./bootstraptest/runner.rb --ruby="$(which wasmtime) run $PWD/../build/ruby --mapdir /::./ -- " --verbose "--sets=$NO_THREAD_TESTS"
working-directory: src
- - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ - uses: ./.github/actions/slack
with:
- payload: |
- {
- "ci": "GitHub Actions",
- "env": "${{ github.workflow }} / ${{ matrix.name }}",
- "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
- "commit": "${{ github.sha }}",
- "branch": "${{ github.ref_name }}"
- }
- env:
+ label: ${{ matrix.entry.name }}
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
- if: ${{ failure() && github.event_name == 'push' }}
+ if: ${{ failure() }}
+
+ # Workaround for https://github.com/orgs/community/discussions/25220
+ - name: Save Pull Request number
+ if: ${{ github.event_name == 'pull_request' }}
+ run: echo "${{ github.event.pull_request.number }}" >> ${{ github.workspace }}/github-pr-info.txt
+ - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
+ if: ${{ github.event_name == 'pull_request' }}
+ with:
+ name: github-pr-info
+ path: ${{ github.workspace }}/github-pr-info.txt
defaults:
run:
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index 79564e5834..5d22a7d7c8 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -7,6 +7,7 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
pull_request:
paths-ignore:
- 'doc/**'
@@ -14,13 +15,8 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
merge_group:
- paths-ignore:
- - 'doc/**'
- - '**/man'
- - '**.md'
- - '**.rdoc'
- - '**/.document'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -34,121 +30,179 @@ jobs:
strategy:
matrix:
include:
+ - vc: 2015
+ vs: 2019
+ vcvars: '10.0.14393.0 -vcvars_ver=14.0' # The oldest Windows 10 SDK w/ VC++ 2015 toolset (v140)
- vs: 2019
- vs: 2022
fail-fast: false
+
runs-on: windows-${{ matrix.vs < 2022 && '2019' || matrix.vs }}
- if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
- name: VisualStudio ${{ matrix.vs }}
+
+ if: >-
+ ${{!(false
+ || contains(github.event.head_commit.message, '[DOC]')
+ || contains(github.event.head_commit.message, 'Document')
+ || contains(github.event.pull_request.title, '[DOC]')
+ || contains(github.event.pull_request.title, 'Document')
+ || contains(github.event.pull_request.labels.*.name, 'Document')
+ || (github.event_name == 'push' && github.actor == 'dependabot[bot]')
+ )}}
+
+ name: VisualStudio ${{ matrix.vc || matrix.vs }}
+
env:
- GITPULLOPTIONS: --no-tags origin ${{github.ref}}
- PATCH: C:\msys64\usr\bin\patch.exe
+ GITPULLOPTIONS: --no-tags origin ${{ github.ref }}
OS_VER: windows-${{ matrix.vs < 2022 && '2019' || matrix.vs }}
+ # FIXME: This is a workaround for the vcpkg's issue present as of openssl 3.1.1
+ # where OpenSSL's default modules directory is incorrectly set to C:\vcpkg\packages\openssl_x64-windows\bin
+ # cf. https://github.com/ruby/openssl/pull/635#issuecomment-1596833720
+ OPENSSL_MODULES: C:\vcpkg\installed\x64-windows\bin
+
steps:
- run: md build
working-directory:
+
+ - name: find tools
+ id: find-tools
+ run: |
+ ::- find needed tools
+ set NEEDS=
+ for %%I in (%NEEDED_TOOLS%) do if "%%~$PATH:I" == "" (
+ call set NEEDS=%%NEEDS%% %%~nI
+ ) else (
+ echo %%I: %%~$PATH:I
+ )
+ echo.needs=%NEEDS%>>%GITHUB_OUTPUT%
+ if "%NEEDS%" == "" (
+ echo [debug] All needed tools found
+ ) else (
+ echo [warning^]Needs%NEEDS%
+ )
+ env:
+ NEEDED_TOOLS: >-
+ patch.exe
+
- uses: msys2/setup-msys2@d40200dc2db4c351366b048a9565ad82919e1c24 # v2
id: setup-msys2
with:
update: true
install: >-
- patch
- if: ${{ env.OS_VER != 'windows-2019' }}
- - name: patch path
- shell: msys2 {0}
- run: echo PATCH=$(cygpath -wa $(command -v patch)) >> $GITHUB_ENV
- if: ${{ steps.setup-msys2.outcome == 'success' }}
- - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
+ ${{ steps.find-tools.outputs.needs }}
+ if: ${{ steps.find-tools.outputs.needs != '' }}
+
+ - uses: ruby/setup-ruby@1198b074305f9356bd56dd4b311757cc0dab2f1c # v1.175.1
+ with:
+ ruby-version: '3.0'
+ bundler: none
+ windows-toolchain: none
+
+ - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
with:
path: C:\vcpkg\downloads
key: ${{ runner.os }}-vcpkg-download-${{ env.OS_VER }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-vcpkg-download-${{ env.OS_VER }}-
${{ runner.os }}-vcpkg-download-
- - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
+
+ - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
with:
path: C:\vcpkg\installed
- key: ${{ runner.os }}-vcpkg-installed-${{ matrix.os }}-${{ github.sha }}
+ key: ${{ runner.os }}-vcpkg-installed-${{ env.OS_VER }}-${{ github.sha }}
restore-keys: |
- ${{ runner.os }}-vcpkg-installed-${{ matrix.os }}-
+ ${{ runner.os }}-vcpkg-installed-${{ env.OS_VER }}-
${{ runner.os }}-vcpkg-installed-
+
- name: Install libraries with vcpkg
run: |
vcpkg --triplet x64-windows install libffi libyaml openssl readline zlib
+
- name: Install libraries with scoop
run: |
iex "& {$(irm get.scoop.sh)} -RunAsAdmin"
Join-Path (Resolve-Path ~).Path "scoop\shims" >> $Env:GITHUB_PATH
shell: pwsh
- - name: git config
- run: |
- git config --global core.autocrlf false
- git config --global core.eol lf
- git config --global advice.detachedHead 0
- git config --global init.defaultBranch garbage
- - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+
+ - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
with:
- path: src
- - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
+ sparse-checkout-cone-mode: false
+ sparse-checkout: /.github
+
+ - uses: ./.github/actions/setup/directories
with:
- path: src/.downloaded-cache
- key: downloaded-cache
+ srcdir: src
+ builddir: build
+
- name: setup env
+ # Available Ruby versions: https://github.com/actions/runner-images/blob/main/images/windows/Windows2019-Readme.md#ruby
# %TEMP% is inconsistent with %TMP% and test-all expects they are consistent.
# https://github.com/actions/virtual-environments/issues/712#issuecomment-613004302
run: |
set VS=${{ matrix.vs }}
- set VCVARS=${{ matrix.vcvars }}
- if not "%VCVARS%" == "" goto :vcset
- set VCVARS="C:\Program Files (x86)\Microsoft Visual Studio\%VS%\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
- if not exist %VCVARS% set VCVARS="C:\Program Files\Microsoft Visual Studio\%VS%\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
- :vcset
+ set VCVARS="C:\Program Files (x86)\Microsoft Visual Studio\%VS%\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
+ if not exist %VCVARS% set VCVARS="C:\Program Files\Microsoft Visual Studio\%VS%\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
set | C:\msys64\usr\bin\sort > old.env
- call %VCVARS%
+ call %VCVARS% ${{ matrix.vcvars || '' }}
+ nmake -f nul
set TMP=%USERPROFILE%\AppData\Local\Temp
set TEMP=%USERPROFILE%\AppData\Local\Temp
+ set MAKEFLAGS=l
set /a TEST_JOBS=(15 * %NUMBER_OF_PROCESSORS% / 10) > nul
set | C:\msys64\usr\bin\sort > new.env
C:\msys64\usr\bin\comm -13 old.env new.env >> %GITHUB_ENV%
del *.env
+
+ - name: baseruby version
+ run: ruby -v
+
- name: compiler version
run: cl
+
- name: link libraries
run: |
for %%I in (C:\vcpkg\installed\x64-windows\bin\*.dll) do (
if not %%~nI == readline mklink %%~nxI %%I
)
+ # We use OpenSSL instealled by vcpkg instead
+ - name: disable system OpenSSL
+ run: |
for %%I in (libcrypto-1_1-x64 libssl-1_1-x64) do (
ren c:\Windows\System32\%%I.dll %%I.dll_
)
+ # windows-2019 image doesn't have OpenSSL as of 2023/9/14
+ if: ${{ matrix.vs != 2019 }}
+
- name: Configure
run: >-
../src/win32/configure.bat --disable-install-doc
--with-opt-dir=C:/vcpkg/installed/x64-windows
+
- run: nmake incs
+
- run: nmake extract-extlibs
+
+ # On all other platforms, test-spec depending on extract-gems (in common.mk) is enough.
+ # But not for this Visual Studio workflow. So here we extract gems before building.
+ - run: nmake extract-gems
+
- run: nmake
+
- run: nmake test
timeout-minutes: 5
- - run: nmake test-spec MSPECOPT="-V -fspec"
+
+ - run: nmake test-spec
timeout-minutes: 10
+
- run: nmake test-all
env:
- RUBY_TESTOPTS: -j${{env.TEST_JOBS}} --job-status=normal
+ RUBY_TESTOPTS: -j${{ env.TEST_JOBS || 4 }} --job-status=normal
timeout-minutes: 60
- - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+
+ - uses: ./.github/actions/slack
with:
- payload: |
- {
- "ci": "GitHub Actions",
- "env": "VS${{ matrix.vs }} / ${{ matrix.test_task || 'check' }}",
- "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
- "commit": "${{ github.sha }}",
- "branch": "${{ github.ref_name }}"
- }
- env:
+ label: VS${{ matrix.vc || matrix.vs }} / ${{ matrix.test_task || 'check' }}
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
- if: ${{ failure() && github.event_name == 'push' }}
+ if: ${{ failure() }}
defaults:
run:
diff --git a/.github/workflows/yjit-macos.yml b/.github/workflows/yjit-macos.yml
new file mode 100644
index 0000000000..45a6a0578f
--- /dev/null
+++ b/.github/workflows/yjit-macos.yml
@@ -0,0 +1,169 @@
+name: YJIT macOS Arm64
+on:
+ push:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ - '.*.yml'
+ pull_request:
+ # Do not use paths-ignore for required status checks
+ # https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks#handling-skipped-but-required-checks
+ merge_group:
+
+concurrency:
+ group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
+ cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+
+permissions:
+ contents: read
+
+jobs:
+ cargo:
+ name: cargo test
+
+ runs-on: ${{ github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'macos-14' }}
+
+ if: >-
+ ${{!(false
+ || contains(github.event.head_commit.message, '[DOC]')
+ || contains(github.event.head_commit.message, 'Document')
+ || contains(github.event.pull_request.title, '[DOC]')
+ || contains(github.event.pull_request.title, 'Document')
+ || contains(github.event.pull_request.labels.*.name, 'Document')
+ || (github.event_name == 'push' && github.actor == 'dependabot[bot]')
+ )}}
+
+ steps:
+ - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
+
+ - run: RUST_BACKTRACE=1 cargo test
+ working-directory: yjit
+
+ # Also compile and test with all features enabled
+ - run: RUST_BACKTRACE=1 cargo test --all-features
+ working-directory: yjit
+
+ # Check that we can build in release mode too
+ - run: cargo build --release
+ working-directory: yjit
+
+ make:
+ strategy:
+ matrix:
+ include:
+ - test_task: 'check'
+ configure: '--enable-yjit'
+ yjit_opts: '--yjit'
+ - test_task: 'check'
+ configure: '--enable-yjit=dev'
+ yjit_opts: '--yjit-call-threshold=1 --yjit-verify-ctx --yjit-code-gc'
+ specopts: '-T --yjit-call-threshold=1 -T --yjit-verify-ctx -T --yjit-code-gc'
+ fail-fast: false
+
+ env:
+ GITPULLOPTIONS: --no-tags origin ${{ github.ref }}
+ RUN_OPTS: ${{ matrix.yjit_opts }}
+ SPECOPTS: ${{ matrix.specopts }}
+
+ runs-on: ${{ github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'macos-14' }}
+
+ if: >-
+ ${{!(false
+ || contains(github.event.head_commit.message, '[DOC]')
+ || contains(github.event.head_commit.message, 'Document')
+ || contains(github.event.pull_request.title, '[DOC]')
+ || contains(github.event.pull_request.title, 'Document')
+ || contains(github.event.pull_request.labels.*.name, 'Document')
+ || (github.event_name == 'push' && github.actor == 'dependabot[bot]')
+ )}}
+
+ steps:
+ - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
+ with:
+ sparse-checkout-cone-mode: false
+ sparse-checkout: /.github
+
+ - name: Install libraries
+ uses: ./.github/actions/setup/macos
+
+ - uses: ./.github/actions/setup/directories
+ with:
+ srcdir: src
+ builddir: build
+ makeup: true
+ dummy-files: ${{ matrix.test_task == 'check' }}
+ # Set fetch-depth: 10 so that Launchable can receive commits information.
+ fetch-depth: 10
+
+ - name: Run configure
+ run: ../src/configure -C --disable-install-doc ${{ matrix.configure }}
+
+ - run: make prepare-gems
+ if: ${{ matrix.test_task == 'test-bundled-gems' }}
+
+ - run: make
+
+ - name: Enable YJIT through ENV
+ run: echo "RUBY_YJIT_ENABLE=1" >> $GITHUB_ENV
+
+ - name: Set test options for skipped tests
+ run: |
+ set -x
+ TESTS="$(echo "${{ matrix.skipped_tests }}" | sed 's| |$$/ -n!/|g;s|^|-n!/|;s|$|$$/|')"
+ echo "TESTS=${TESTS}" >> $GITHUB_ENV
+ if: ${{ matrix.test_task == 'check' && matrix.skipped_tests }}
+
+ - name: Set up Launchable
+ uses: ./.github/actions/launchable/setup
+ with:
+ os: ${{ github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'macos-14' }}
+ test-opts: ${{ matrix.configure }}
+ launchable-token: ${{ secrets.LAUNCHABLE_TOKEN }}
+ builddir: build
+ srcdir: src
+ continue-on-error: true
+
+ - name: make ${{ matrix.test_task }}
+ run: >-
+ make -s ${{ matrix.test_task }} ${TESTS:+TESTS="$TESTS"}
+ RUN_OPTS="$RUN_OPTS"
+ SPECOPTS="$SPECOPTS"
+ timeout-minutes: 60
+ env:
+ RUBY_TESTOPTS: '-q --tty=no'
+ TEST_BUNDLED_GEMS_ALLOW_FAILURES: ''
+ PRECHECK_BUNDLED_GEMS: 'no'
+ continue-on-error: ${{ matrix.continue-on-test_task || false }}
+
+ - name: make skipped tests
+ run: |
+ make -s test-all TESTS="${TESTS//-n!\//-n/}"
+ env:
+ GNUMAKEFLAGS: ''
+ RUBY_TESTOPTS: '-v --tty=no'
+ PRECHECK_BUNDLED_GEMS: 'no'
+ if: ${{ matrix.test_task == 'check' && matrix.skipped_tests }}
+ continue-on-error: ${{ matrix.continue-on-skipped_tests || false }}
+
+ - uses: ./.github/actions/slack
+ with:
+ label: ${{ matrix.test_task }} ${{ matrix.configure }} ${{ matrix.yjit_opts }}
+ SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
+ if: ${{ failure() }}
+
+ result:
+ if: ${{ always() }}
+ name: ${{ github.workflow }} result
+ runs-on: ${{ github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'macos-14' }}
+ needs: [make]
+ steps:
+ - run: exit 1
+ working-directory:
+ if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
+
+defaults:
+ run:
+ working-directory: build
diff --git a/.github/workflows/yjit-ubuntu.yml b/.github/workflows/yjit-ubuntu.yml
index 9cb5c87120..e9f3c3b879 100644
--- a/.github/workflows/yjit-ubuntu.yml
+++ b/.github/workflows/yjit-ubuntu.yml
@@ -7,20 +7,11 @@ on:
- '**.md'
- '**.rdoc'
- '**/.document'
+ - '.*.yml'
pull_request:
- paths-ignore:
- - 'doc/**'
- - '**/man'
- - '**.md'
- - '**.rdoc'
- - '**/.document'
+ # Do not use paths-ignore for required status checks
+ # https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks#handling-skipped-but-required-checks
merge_group:
- paths-ignore:
- - 'doc/**'
- - '**/man'
- - '**.md'
- - '**.rdoc'
- - '**/.document'
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
@@ -31,146 +22,199 @@ permissions:
jobs:
cargo:
- name: Rust cargo test
+ name: cargo test
+
# GitHub Action's image seems to already contain a Rust 1.58.0.
runs-on: ubuntu-20.04
+
+ if: >-
+ ${{!(false
+ || contains(github.event.head_commit.message, '[DOC]')
+ || contains(github.event.pull_request.title, '[DOC]')
+ || contains(github.event.pull_request.labels.*.name, 'Document')
+ || (github.event_name == 'push' && github.actor == 'dependabot[bot]')
+ )}}
+
steps:
- - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+ - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
+
# For now we can't run cargo test --offline because it complains about the
# capstone dependency, even though the dependency is optional
#- run: cargo test --offline
+
- run: RUST_BACKTRACE=1 cargo test
working-directory: yjit
+
# Also compile and test with all features enabled
- run: RUST_BACKTRACE=1 cargo test --all-features
working-directory: yjit
+
# Check that we can build in release mode too
- run: cargo build --release
working-directory: yjit
+
lint:
- name: Rust lint
+ name: cargo clippy
+
# GitHub Action's image seems to already contain a Rust 1.58.0.
runs-on: ubuntu-20.04
+
+ if: >-
+ ${{!(false
+ || contains(github.event.head_commit.message, '[DOC]')
+ || contains(github.event.pull_request.title, '[DOC]')
+ || contains(github.event.pull_request.labels.*.name, 'Document')
+ || (github.event_name == 'push' && github.actor == 'dependabot[bot]')
+ )}}
+
steps:
- - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+ - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
+
# Check that we don't have linting errors in release mode, too
- run: cargo clippy --all-targets --all-features
working-directory: yjit
+
make:
strategy:
fail-fast: false
matrix:
include:
- - test_task: "yjit-bindgen"
- hint: "To fix: use patch in logs"
- configure: "--with-gcc=clang-12 --enable-yjit=dev"
+ - test_task: 'yjit-bindgen'
+ hint: 'To fix: use patch in logs'
+ configure: '--with-gcc=clang-12 --enable-yjit=dev'
- - test_task: "check"
+ - test_task: 'check'
# YJIT should be automatically built in release mode on x86-64 Linux with rustc present
#configure: "--enable-yjit RUSTC='rustc +1.58.0'"
configure: "RUSTC='rustc +1.58.0'"
- rust_version: "1.58.0"
+ rust_version: '1.58.0'
- - test_task: "check"
- configure: "--enable-yjit=dev"
+ - test_task: 'check'
+ configure: '--enable-yjit=dev'
- - test_task: "check"
- configure: "--enable-yjit=dev"
- yjit_opts: "--yjit-call-threshold=1 --yjit-verify-ctx"
+ - test_task: 'check'
+ configure: '--enable-yjit=dev'
+ yjit_opts: '--yjit-call-threshold=1 --yjit-verify-ctx --yjit-code-gc'
+ specopts: '-T --yjit-call-threshold=1 -T --yjit-verify-ctx -T --yjit-code-gc'
- - test_task: "test-all TESTS=--repeat-count=2"
- configure: "--enable-yjit=dev"
+ - test_task: 'test-bundled-gems'
+ configure: '--enable-yjit=dev'
- - test_task: "test-bundled-gems"
- configure: "--enable-yjit=dev"
+ - test_task: 'yjit-bench'
+ configure: '--enable-yjit=dev'
+ yjit_bench_opts: '--yjit-stats'
+ continue-on-test_task: true
- - test_task: "yjit-bench"
- configure: "--enable-yjit=dev"
- yjit_bench_opts: "--yjit-stats"
env:
- GITPULLOPTIONS: --no-tags origin ${{github.ref}}
+ GITPULLOPTIONS: --no-tags origin ${{ github.ref }}
RUN_OPTS: ${{ matrix.yjit_opts }}
YJIT_BENCH_OPTS: ${{ matrix.yjit_bench_opts }}
+ SPECOPTS: ${{ matrix.specopts }}
RUBY_DEBUG: ci
BUNDLE_JOBS: 8 # for yjit-bench
RUST_BACKTRACE: 1
+
runs-on: ubuntu-20.04
- if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+
+ if: >-
+ ${{!(false
+ || contains(github.event.head_commit.message, '[DOC]')
+ || contains(github.event.head_commit.message, 'Document')
+ || contains(github.event.pull_request.title, '[DOC]')
+ || contains(github.event.pull_request.title, 'Document')
+ || contains(github.event.pull_request.labels.*.name, 'Document')
+ || (github.event_name == 'push' && github.actor == 'dependabot[bot]')
+ )}}
+
steps:
- - run: mkdir build
- working-directory:
- - name: Install libraries
- run: |
- set -x
- sudo apt-get update -q || :
- sudo apt-get install --no-install-recommends -q -y build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev autoconf ruby
+ - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
+ with:
+ sparse-checkout-cone-mode: false
+ sparse-checkout: /.github
+
+ - uses: ./.github/actions/setup/ubuntu
+
+ - uses: ./.github/actions/setup/directories
+ with:
+ srcdir: src
+ builddir: build
+ makeup: true
+ dummy-files: ${{ matrix.test_task == 'check' }}
+ # Set fetch-depth: 10 so that Launchable can receive commits information.
+ fetch-depth: 10
+
- name: Install Rust
if: ${{ matrix.rust_version }}
run: rustup install ${{ matrix.rust_version }} --profile minimal
- - name: git config
- run: |
- git config --global advice.detachedHead 0
- git config --global init.defaultBranch garbage
- - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- with:
- path: src
- - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
+
+ - uses: ruby/setup-ruby@1198b074305f9356bd56dd4b311757cc0dab2f1c # v1.175.1
with:
- path: src/.downloaded-cache
- key: downloaded-cache
- - name: Fixed world writable dirs
- run: |
- chmod -v go-w $HOME $HOME/.config
- sudo chmod -R go-w /usr/share
- sudo bash -c 'IFS=:; for d in '"$PATH"'; do chmod -v go-w $d; done' || :
- - name: Set ENV
- run: |
- echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
- - run: ./autogen.sh
- working-directory: src
+ ruby-version: '3.0'
+ bundler: none
+
- name: Run configure
run: ../src/configure -C --disable-install-doc --prefix=$(pwd)/install ${{ matrix.configure }}
+
- run: make incs
+
- run: make prepare-gems
if: ${{ matrix.test_task == 'test-bundled-gems' }}
- - run: make -j
- - name: Create dummy files in build dir
- run: |
- ./miniruby -e '(("a".."z").to_a+("A".."Z").to_a+("0".."9").to_a+%w[foo bar test zzz]).each{|basename|File.write("#{basename}.rb", "raise %(do not load #{basename}.rb)")}'
- if: ${{ matrix.test_task == 'check' }}
+
+ - run: make
+
- name: Enable YJIT through ENV
run: echo "RUBY_YJIT_ENABLE=1" >> $GITHUB_ENV
+
# Check that the binary was built with YJIT
- name: Check YJIT enabled
run: ./miniruby --yjit -v | grep "+YJIT"
+
+ - name: Set up Launchable
+ uses: ./.github/actions/launchable/setup
+ with:
+ os: ubuntu-20.04
+ test-opts: ${{ matrix.configure }}
+ launchable-token: ${{ secrets.LAUNCHABLE_TOKEN }}
+ builddir: build
+ srcdir: src
+ continue-on-error: true
+
- name: make ${{ matrix.test_task }}
- run: make -s -j ${{ matrix.test_task }} RUN_OPTS="$RUN_OPTS" YJIT_BENCH_OPTS="$YJIT_BENCH_OPTS"
- timeout-minutes: 60
+ run: >-
+ make -s ${{ matrix.test_task }} ${TESTS:+TESTS="$TESTS"}
+ RUN_OPTS="$RUN_OPTS" MSPECOPT=--debug SPECOPTS="$SPECOPTS"
+ YJIT_BENCH_OPTS="$YJIT_BENCH_OPTS" YJIT_BINDGEN_DIFF_OPTS="$YJIT_BINDGEN_DIFF_OPTS"
+ timeout-minutes: 90
env:
- RUBY_TESTOPTS: "-q --tty=no"
- TEST_BUNDLED_GEMS_ALLOW_FAILURES: ""
- PRECHECK_BUNDLED_GEMS: "no"
- SYNTAX_SUGGEST_TIMEOUT: "5"
- continue-on-error: ${{ matrix.test_task == 'yjit-bench' }}
+ RUBY_TESTOPTS: '-q --tty=no'
+ TEST_BUNDLED_GEMS_ALLOW_FAILURES: ''
+ PRECHECK_BUNDLED_GEMS: 'no'
+ SYNTAX_SUGGEST_TIMEOUT: '5'
+ YJIT_BINDGEN_DIFF_OPTS: '--exit-code'
+ continue-on-error: ${{ matrix.continue-on-test_task || false }}
+
- name: Show ${{ github.event.pull_request.base.ref }} GitHub URL for yjit-bench comparison
run: echo "https://github.com/${BASE_REPO}/commit/${BASE_SHA}"
env:
BASE_REPO: ${{ github.event.pull_request.base.repo.full_name }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
if: ${{ matrix.test_task == 'yjit-bench' && startsWith(github.event_name, 'pull') }}
- - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+
+ - uses: ./.github/actions/slack
with:
- payload: |
- {
- "ci": "GitHub Actions",
- "env": "${{ github.workflow }} / ${{ matrix.test_task }} ${{ matrix.configure }}",
- "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
- "commit": "${{ github.sha }}",
- "branch": "${{ github.ref_name }}"
- }
- env:
+ label: ${{ matrix.test_task }} ${{ matrix.configure }}
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
- if: ${{ failure() && github.event_name == 'push' }}
+ if: ${{ failure() }}
+
+ result:
+ if: ${{ always() }}
+ name: ${{ github.workflow }} result
+ runs-on: ubuntu-latest
+ needs: [make]
+ steps:
+ - run: exit 1
+ working-directory:
+ if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
defaults:
run:
diff --git a/.gitignore b/.gitignore
index ca1783479d..b6beba3b3e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -138,6 +138,7 @@ lcov*.info
/test.rb
/test-coverage.dat
/tmp
+/vcpkg_installed
/transdb.h
/uncommon.mk
/verconf.h
@@ -166,6 +167,7 @@ lcov*.info
# /coroutine/
!/coroutine/**/*.s
+!/coroutine/**/*.S
# /enc/trans/
/enc/trans/*.c
@@ -205,8 +207,10 @@ lcov*.info
# /ext/ripper/
/ext/ripper/eventids1.c
+/ext/ripper/eventids1.h
/ext/ripper/.eventids2-check
/ext/ripper/eventids2table.c
+/ext/ripper/ripper_init.c
/ext/ripper/ripper.*
/ext/ripper/ids1
/ext/ripper/ids2
@@ -228,8 +232,9 @@ lcov*.info
# /misc/
/misc/**/__pycache__
-# /spec/bundler
+# for `make test-bundler`
/.rspec_status
+/tool/bundler/*.lock
# /tool/
/tool/config.guess
@@ -251,3 +256,28 @@ lcov*.info
# /wasm/
/wasm/tests/*.wasm
+
+# prism
+/lib/prism/compiler.rb
+/lib/prism/dispatcher.rb
+/lib/prism/dot_visitor.rb
+/lib/prism/dsl.rb
+/lib/prism/inspect_visitor.rb
+/lib/prism/mutation_compiler.rb
+/lib/prism/node.rb
+/lib/prism/reflection.rb
+/lib/prism/serialize.rb
+/lib/prism/visitor.rb
+/prism/api_node.c
+/prism/ast.h
+/prism/diagnostic.c
+/prism/diagnostic.h
+/prism/node.c
+/prism/prettyprint.c
+/prism/serialize.c
+/prism/token_type.c
+
+# tool/update-NEWS-gemlist.rb
+/bundled_gems.json
+/default_gems.json
+/gems/default_gems
diff --git a/.mailmap b/.mailmap
new file mode 100644
index 0000000000..213a0f4916
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1,431 @@
+git[bot] <svn-admin@ruby-lang.org>
+git[bot] <svn-admin@ruby-lang.org> git <svn@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+svn[bot] <svn-admin@ruby-lang.org> svn <svn@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# a_matsuda
+Akira Matsuda <ronnie@dio.jp>
+Akira Matsuda <ronnie@dio.jp> <a_matsuda@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# aamine
+Minero Aoki <aamine@loveruby.net>
+Minero Aoki <aamine@loveruby.net> <aamine@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# akira
+akira yamada <akira@ruby-lang.org>
+## akira yamada <akira@ruby-lang.org> <akira@rice.p.arika.org>
+akira yamada <akira@ruby-lang.org> <akira@arika.org>
+akira yamada <akira@ruby-lang.org> <akira@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# akiyoshi
+AKIYOSHI, Masamichi <masamichi.akiyoshi@hp.com>
+AKIYOSHI, Masamichi <masamichi.akiyoshi@hp.com> <akiyoshi@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# akr
+Tanaka Akira <akr@fsij.org>
+Tanaka Akira <akr@fsij.org> <akr@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# arai
+Koji Arai <jca02266@nifty.ne.jp>
+Koji Arai <jca02266@nifty.ne.jp> <arai@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# arton
+Akio Tajima <artonx@yahoo.co.jp>
+Akio Tajima <artonx@yahoo.co.jp> <arton@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# aycabta
+aycabta <aycabta@gmail.com>
+aycabta <aycabta@gmail.com> <aycabta@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# ayumin
+Ayumu AIZAWA <ayumu.aizawa@gmail.com>
+Ayumu AIZAWA <ayumu.aizawa@gmail.com> <ayumin@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# azav
+Alexander Zavorine <alexandre.zavorine@nokia.com>
+Alexander Zavorine <alexandre.zavorine@nokia.com> <azav@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# charliesome
+Charlie Somerville <charliesome@ruby-lang.org>
+Charlie Somerville <charliesome@ruby-lang.org> <charliesome@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# dave
+Dave Thomas <dave@pragprog.com>
+Dave Thomas <dave@pragprog.com> <dave@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# davidflanagan
+David Flanagan <davidflanagan@ruby-lang.org>
+David Flanagan <davidflanagan@ruby-lang.org> <david@think32>
+David Flanagan <davidflanagan@ruby-lang.org> <david@davidflanagan.com>
+David Flanagan <davidflanagan@ruby-lang.org> <davidflanagan@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# dblack
+David A. Black <dblack@rubypal.com>
+David A. Black <dblack@rubypal.com> <dblack@wobblini.net>
+David A. Black <dblack@rubypal.com> <dblack@superlink.net>
+David A. Black <dblack@rubypal.com> <dblack@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# drbrain
+Eric Hodel <drbrain@segment7.net>
+Eric Hodel <drbrain@segment7.net> <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# duerst
+Martin Dürst <duerst@it.aoyama.ac.jp>
+Martin Dürst <duerst@it.aoyama.ac.jp> <duerst@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# eban
+WATANABE Hirofumi <eban@ruby-lang.org>
+WATANABE Hirofumi <eban@ruby-lang.org> <eban@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# emboss
+Martin Bosslet <Martin.Bosslet@gmail.com>
+Martin Bosslet <Martin.Bosslet@gmail.com> <Martin.Bosslet@googlemail.com>
+Martin Bosslet <Martin.Bosslet@gmail.com> <emboss@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# eregon
+Benoit Daloze <eregontp@gmail.com>
+Benoit Daloze <eregontp@gmail.com> <eregon@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# evan
+Evan Phoenix <evan@ruby-lang.org>
+Evan Phoenix <evan@ruby-lang.org> <evan@fallingsnow.net>
+Evan Phoenix <evan@ruby-lang.org> <evan@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# glass
+Masaki Matsushita <glass.saga@gmail.com>
+Masaki Matsushita <glass.saga@gmail.com> <glass@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# gogotanaka
+Kazuki Tanaka <gogotanaka@ruby-lang.org>
+Kazuki Tanaka <gogotanaka@ruby-lang.org> <gogotanaka@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# gotoken
+Kentaro Goto <gotoken@gmail.com>
+Kentaro Goto <gotoken@gmail.com> <gotoken@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# gotoyuzo
+GOTOU Yuuzou <gotoyuzo@notwork.org>
+GOTOU Yuuzou <gotoyuzo@notwork.org> <gotoyuzo@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# gsinclair
+Gavin Sinclair <gsinclair@soyabean.com.au>
+Gavin Sinclair <gsinclair@soyabean.com.au> <gsinclair@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# H_Konishi
+KONISHI Hiromasa <konishih@fd6.so-net.ne.jp>
+KONISHI Hiromasa <konishih@fd6.so-net.ne.jp> <H_Konishi@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# headius
+Charles Oliver Nutter <headius@headius.com>
+Charles Oliver Nutter <headius@headius.com> <headius@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# hone
+Terence Lee <hone@heroku.com>
+Terence Lee <hone@heroku.com> <hone@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# hsbt
+Hiroshi SHIBATA <hsbt@ruby-lang.org>
+Hiroshi SHIBATA <hsbt@ruby-lang.org> <hsbt@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# iwamatsu
+Nobuhiro Iwamatsu <iwamatsu@nigauri.org>
+Nobuhiro Iwamatsu <iwamatsu@nigauri.org> <iwamatsu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# jeg2
+James Edward Gray II <james@graysoftinc.com>
+James Edward Gray II <james@graysoftinc.com> <jeg2@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# jim
+Jim Weirich <jim@tardis.local>
+Jim Weirich <jim@tardis.local> <jim@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# k0kubun
+Takashi Kokubun <takashikkbn@gmail.com>
+Takashi Kokubun <takashikkbn@gmail.com> <k0kubun@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# kanemoto
+Yutaka Kanemoto <kanemoto@ruby-lang.org>
+Yutaka Kanemoto <kanemoto@ruby-lang.org> <kanemoto@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# katsu
+UENO Katsuhiro <katsu@blue.sky.or.jp>
+UENO Katsuhiro <katsu@blue.sky.or.jp> <katsu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# kazu
+Kazuhiro NISHIYAMA <zn@mbf.nifty.com>
+Kazuhiro NISHIYAMA <zn@mbf.nifty.com> <kazu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# keiju
+Keiju Ishitsuka <keiju@ishitsuka.com>
+Keiju Ishitsuka <keiju@ishitsuka.com> <keiju@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# knu
+Akinori MUSHA <knu@iDaemons.org>
+Akinori MUSHA <knu@iDaemons.org> <knu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# ko1
+Koichi Sasada <ko1@atdot.net>
+Koichi Sasada <ko1@atdot.net> <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# kosaki
+KOSAKI Motohiro <kosaki.motohiro@gmail.com>
+KOSAKI Motohiro <kosaki.motohiro@gmail.com> <kosaki@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# kosako
+K.Kosako <sndgk393@ybb.ne.jp>
+K.Kosako <sndgk393@ybb.ne.jp> <kosako@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# kou
+Sutou Kouhei <kou@clear-code.com>
+Sutou Kouhei <kou@clear-code.com> <kou@cozmixng.org>
+Sutou Kouhei <kou@clear-code.com> <kou@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# kouji
+Kouji Takao <kouji.takao@gmail.com>
+Kouji Takao <kouji.takao@gmail.com> <kouji@takao7.net>
+Kouji Takao <kouji.takao@gmail.com> <kouji@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# ksaito
+Kazuo Saito <ksaito@uranus.dti.ne.jp>
+Kazuo Saito <ksaito@uranus.dti.ne.jp> <ksaito@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# ktsj
+Kazuki Tsujimoto <kazuki@callcc.net>
+Kazuki Tsujimoto <kazuki@callcc.net> <ktsj@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# luislavena
+Luis Lavena <luislavena@gmail.com>
+Luis Lavena <luislavena@gmail.com> <luislavena@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# mame
+Yusuke Endoh <mame@ruby-lang.org>
+## Yusuke Endoh <mame@ruby-lang.org> <mame@tsg.ne.jp>
+Yusuke Endoh <mame@ruby-lang.org> <mame@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# marcandre
+Marc-Andre Lafortune <github@marc-andre.ca>
+Marc-Andre Lafortune <ruby-core@marc-andre.ca>
+Marc-Andre Lafortune <ruby-core@marc-andre.ca> <marcandre@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# matz
+Yukihiro "Matz" Matsumoto <matz@ruby.or.jp>
+Yukihiro "Matz" Matsumoto <matz@ruby.or.jp> <matz@ruby-lang.org>
+Yukihiro "Matz" Matsumoto <matz@ruby.or.jp> <matz@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# michal
+Michal Rokos <michal@ruby-lang.org>
+Michal Rokos <michal@ruby-lang.org> <michal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# mneumann
+Michael Neumann <mneumann@ruby-lang.org>
+Michael Neumann <mneumann@ruby-lang.org> <mneumann@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# mrkn
+Kenta Murata <mrkn@mrkn.jp>
+Kenta Murata <mrkn@mrkn.jp> <muraken@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+Kenta Murata <mrkn@mrkn.jp> <3959+mrkn@users.noreply.github.com>
+Kenta Murata <mrkn@mrkn.jp> <mrkn@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# nagachika
+nagachika <nagachika@ruby-lang.org>
+nagachika <nagachika@ruby-lang.org> <nagachika@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# nagai
+Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp>
+Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp> <nagai@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# nahi
+Hiroshi Nakamura <nahi@ruby-lang.org>
+Hiroshi Nakamura <nahi@ruby-lang.org> <nahi@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# nari
+Narihiro Nakamura <authornari@gmail.com>
+Narihiro Nakamura <authornari@gmail.com> <nari@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# naruse
+NARUSE, Yui <naruse@airemix.jp>
+NARUSE, Yui <naruse@airemix.jp> <naruse@ruby-lang.org>
+NARUSE, Yui <naruse@airemix.jp> <naruse@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# ngoto
+Naohisa Goto <ngotogenome@gmail.com>
+Naohisa Goto <ngotogenome@gmail.com> <ngoto@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# nobu
+Nobuyoshi Nakada <nobu@ruby-lang.org>
+Nobuyoshi Nakada <nobu@ruby-lang.org> <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# normal
+Eric Wong <normal@ruby-lang.org>
+Eric Wong <normal@ruby-lang.org> <e@80x24.org>
+Eric Wong <normal@ruby-lang.org> <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# ntalbott
+Nathaniel Talbott <ntalbott@ruby-lang.org>
+Nathaniel Talbott <ntalbott@ruby-lang.org> <ntalbott@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# ocean
+Hirokazu Yamamoto <ocean@m2.ccsnet.ne.jp>
+Hirokazu Yamamoto <ocean@m2.ccsnet.ne.jp> <ocean@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# odaira
+Rei Odaira <rodaira@us.ibm.com>
+Rei Odaira <rodaira@us.ibm.com> <Rei.Odaira@gmail.com>
+Rei Odaira <rodaira@us.ibm.com> <odaira@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# okkez
+okkez <okkez000@gmail.com>
+okkez <okkez000@gmail.com> <okkez@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# rhe
+Kazuki Yamaguchi <k@rhe.jp>
+Kazuki Yamaguchi <k@rhe.jp> <rhe@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# ryan
+Ryan Davis <ryand-github@zenspider.com>
+Ryan Davis <ryand-github@zenspider.com> <ryand-ruby@zenspider.com>
+Ryan Davis <ryand-github@zenspider.com> <ryan@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# samuel
+Samuel Williams <samuel.williams@oriontransfer.co.nz>
+Samuel Williams <samuel.williams@oriontransfer.co.nz> <samuel@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# seki
+Masatoshi SEKI <m_seki@mva.biglobe.ne.jp>
+Masatoshi SEKI <m_seki@mva.biglobe.ne.jp> <seki@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# ser
+Sean Russell <ser@germane-software.com>
+Sean Russell <ser@germane-software.com> <ser@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# shigek
+Shigeo Kobayashi <shigek@ruby-lang.org>
+Shigeo Kobayashi <shigek@ruby-lang.org> <shigek@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# shirosaki
+Hiroshi Shirosaki <h.shirosaki@gmail.com>
+Hiroshi Shirosaki <h.shirosaki@gmail.com> <shirosaki@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# sho-h
+Sho Hashimoto <sho-h@ruby-lang.org>
+Sho Hashimoto <sho-h@ruby-lang.org> <sho-h@netlab.jp>
+Sho Hashimoto <sho-h@ruby-lang.org> <sho.hsmt@gmail.com>
+Sho Hashimoto <sho-h@ruby-lang.org> <sho-h@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# shugo
+Shugo Maeda <shugo@ruby-lang.org>
+Shugo Maeda <shugo@ruby-lang.org> <shugo@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# shyouhei
+åœéƒ¨æ˜Œå¹³ <shyouhei@ruby-lang.org>
+åœéƒ¨æ˜Œå¹³ <shyouhei@ruby-lang.org> <shyouhei@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# siena
+Siena. <siena@faculty.chiba-u.jp>
+Siena. <siena@faculty.chiba-u.jp> <siena@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# sonots
+sonots <sonots@gmail.com>
+sonots <sonots@gmail.com> <sonots@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# sorah
+Sorah Fukumori <her@sorah.jp>
+Sorah Fukumori <her@sorah.jp> <sorah@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# stomar
+Marcus Stollsteimer <sto.mar@web.de>
+Marcus Stollsteimer <sto.mar@web.de> <stomar@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# suke
+Masaki Suketa <masaki.suketa@nifty.ne.jp>
+Masaki Suketa <masaki.suketa@nifty.ne.jp> <suke@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# tadd
+Tadashi Saito <tad.a.digger@gmail.com>
+Tadashi Saito <tad.a.digger@gmail.com> <tadd@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# tadf
+Tadayoshi Funaba <tadf@dotrb.org>
+Tadayoshi Funaba <tadf@dotrb.org> <tadf@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# takano32
+TAKANO Mitsuhiro <takano32@gmail.com>
+TAKANO Mitsuhiro <takano32@gmail.com> <takano32@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# tarui
+Masaya Tarui <tarui@ruby-lang.org>
+Masaya Tarui <tarui@ruby-lang.org> <tarui@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# technorama
+Technorama Ltd. <oss-ruby@technorama.net>
+Technorama Ltd. <oss-ruby@technorama.net> <technorama@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# tenderlove
+Aaron Patterson <tenderlove@ruby-lang.org>
+Aaron Patterson <tenderlove@ruby-lang.org> <tenderlove@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# tmm1
+Aman Gupta <ruby@tmm1.net>
+Aman Gupta <ruby@tmm1.net> <tmm1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# ts
+Guy Decoux <ts@moulon.inra.fr>
+Guy Decoux <ts@moulon.inra.fr> <ts@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# ttate
+Takaaki Tateishi <ttate@ttsky.net>
+## Takaaki Tateishi <ttate@ttsky.net> <ttate@kt.jaist.ac.jp>
+Takaaki Tateishi <ttate@ttsky.net> <ttate@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# uema2
+Takaaki Uematsu <uema2x@jcom.home.ne.jp>
+Takaaki Uematsu <uema2x@jcom.home.ne.jp> <uema2@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# usa
+U.Nakamura <usa@ruby-lang.org>
+U.Nakamura <usa@ruby-lang.org> <usa@garbagecollect.jp>
+U.Nakamura <usa@ruby-lang.org> <usa@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# wakou
+Wakou Aoyama <wakou@ruby-lang.org>
+Wakou Aoyama <wakou@ruby-lang.org> <wakou@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# wanabe
+wanabe <s.wanabe@gmail.com>
+wanabe <s.wanabe@gmail.com> <wanabe@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# watson1978
+Watson <watson1978@gmail.com>
+Watson <watson1978@gmail.com> <watson1978@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# wew
+William Webber <william@williamwebber.com>
+William Webber <william@williamwebber.com> <wew@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# why
+why the lucky stiff <why@ruby-lang.org>
+why the lucky stiff <why@ruby-lang.org> <why@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# xibbar
+Takeyuki FUJIOKA <xibbar@ruby-lang.org>
+Takeyuki FUJIOKA <xibbar@ruby-lang.org> <xibbar@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# yugui
+Yuki Yugui Sonoda <yugui@yugui.jp>
+Yuki Yugui Sonoda <yugui@yugui.jp> <yugui@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# yui-knk
+yui-knk <spiketeika@gmail.com>
+yui-knk <spiketeika@gmail.com> <yui-knk@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# yuki
+Yuki Nishijima <yuki24@hey.com>
+Yuki Nishijima <yuki24@hey.com> <mail@yukinishijima.net>
+Yuki Nishijima <yuki24@hey.com> <yuki@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# zsombor
+Dee Zsombor <zsombor@ruby-lang.org>
+Dee Zsombor <zsombor@ruby-lang.org> <zsombor@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
+
+# zzak
+zzak <zzakscott@gmail.com>
+zzak <zzakscott@gmail.com> <zzak@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
diff --git a/.rdoc_options b/.rdoc_options
index 760507c7a2..57e03c9dd7 100644
--- a/.rdoc_options
+++ b/.rdoc_options
@@ -1,4 +1,7 @@
---
page_dir: doc
+charset: UTF-8
+encoding: UTF-8
main_page: README.md
title: Documentation for Ruby development version
+visibility: :private
diff --git a/.travis.yml b/.travis.yml
index 665feba914..06de3dd493 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,63 +6,46 @@
# conditions mentioned in the file COPYING are met. Consult the file for
# details.
-# We only manage non-amd64 free pipelines.
-# https://docs.travis-ci.com/user/billing-overview/
+# When you see Travis CI issues, or you are interested in understanding how to
+# manage, please check the link below.
+# https://github.com/ruby/ruby/wiki/CI-Servers#travis-ci
+
+# We enable Travis on the specific branches or forked repositories here.
+if: >-
+ (repo != ruby/ruby OR branch = master OR branch =~ /^ruby_\d_\d$/)
+ AND (commit_message !~ /\[DOC\]/)
language: c
os: linux
-if: commit_message !~ /\[DOC\]/
-
-dist: focal
+dist: jammy
git:
quiet: true
-cache:
- ccache: true
- directories:
- - $HOME/config_2nd
- - $HOME/.downloaded-cache
-
env:
global:
- # The tests skipped in `make test-all`.
- - TEST_ALL_SKIPPED_TESTS=
- # The tests executed separately by `make test-all`.
- - TEST_ALL_SEPARATED_TESTS=
- # Reset timestamps early
- - _=$(touch NEWS && find . -type f -exec touch -r NEWS {} +)
- - CONFIGURE_TTY=no
- - CCACHE_COMPILERCHECK=none
- - CCACHE_NOCOMPRESS=1
- - CCACHE_MAXSIZE=512Mi
- - NPROC="`nproc`"
- # JOBS and SETARCH are overridden when necessary; see below.
- - JOBS=-j$((1+${NPROC}))
+ - NPROC="$(nproc)"
+ - JOBS="-j${NPROC}"
+ # SETARCH are overridden when necessary. See below.
- SETARCH=
- - RUBY_PREFIX=/tmp/ruby-prefix
- - GEMS_FOR_TEST='timezone tzinfo'
# https://github.com/travis-ci/travis-build/blob/e411371dda21430a60f61b8f3f57943d2fe4d344/lib/travis/build/bash/travis_apt_get_options.bash#L7
- travis_apt_get_options='--allow-downgrades --allow-remove-essential --allow-change-held-packages'
- travis_apt_get_options="-yq --no-install-suggests --no-install-recommends $travis_apt_get_options"
- # -O1 is faster than -O3 in our tests.
- - optflags=-O1
- # -g0 disables backtraces when SEGV. Do not set that.
+ # -g0 disables backtraces when SEGV. Do not set that.
- debugflags=-ggdb3
+ - RUBY_TESTOPTS="$JOBS -q --tty=no"
.org.ruby-lang.ci.matrix-definitions:
-
- - &gcc-10
- compiler: gcc-10
+ - &gcc-11
+ compiler: gcc-11
before_install:
- tool/travis_retry.sh sudo bash -c "rm -rf '${TRAVIS_ROOT}/var/lib/apt/lists/'* && exec apt-get update -yq"
- >-
tool/travis_retry.sh sudo -E apt-get $travis_apt_get_options install
- ccache
- gcc-10
- g++-10
+ gcc-11
+ g++-11
libffi-dev
libncurses-dev
libncursesw5-dev
@@ -71,40 +54,42 @@ env:
libyaml-dev
openssl
zlib1g-dev
-
- # --------
-
+ - gcc-11 --version
- &arm64-linux
name: arm64-linux
arch: arm64
- <<: *gcc-10
-
+ <<: *gcc-11
- &ppc64le-linux
name: ppc64le-linux
arch: ppc64le
- <<: *gcc-10
-
+ <<: *gcc-11
- &s390x-linux
name: s390x-linux
arch: s390x
- <<: *gcc-10
-
+ <<: *gcc-11
+ env:
+ # Avoid possible test failures with the zlib applying the following patch
+ # on s390x CPU architecture.
+ # https://github.com/madler/zlib/pull/410
+ - DFLTCC=0
- &arm32-linux
name: arm32-linux
arch: arm64
- # https://packages.ubuntu.com/focal/crossbuild-essential-armhf
+ # https://packages.ubuntu.com/jammy/crossbuild-essential-armhf
compiler: arm-linux-gnueabihf-gcc
env:
- SETARCH='setarch linux32 --verbose --32bit'
- # The "TestReadline#test_interrupt_in_other_thread" started failing on arm32
- # from https://www.travis-ci.com/github/ruby/ruby/jobs/529005145
- - TEST_ALL_SKIPPED_TESTS=test_interrupt_in_other_thread
+ # Still keep the -O1 for only arm32, while we want to test with the
+ # default optflags -O3.
+ # Because bootstraptest/test_ractor.rb fails with segfualt with the
+ # default -O3.
+ # https://bugs.ruby-lang.org/issues/19981
+ - optflags=-O1
before_install:
- sudo dpkg --add-architecture armhf
- tool/travis_retry.sh sudo bash -c "rm -rf '${TRAVIS_ROOT}/var/lib/apt/lists/'* && exec apt-get update -yq"
- >-
tool/travis_retry.sh sudo -E apt-get $travis_apt_get_options install
- ccache
crossbuild-essential-armhf
libc6:armhf
libstdc++-10-dev:armhf
@@ -119,117 +104,50 @@ env:
matrix:
include:
- # Build every commit (Allowed Failures):
- - <<: *arm32-linux
- # Comment out as the 2nd arm64 pipeline is unstable.
- # - <<: *arm64-linux
+ - <<: *arm64-linux
- <<: *ppc64le-linux
- <<: *s390x-linux
+ # FIXME: lib/rubygems/util.rb:104 glob_files_in_dir -
+ # <internal:dir>:411:in glob: File name too long - (Errno::ENAMETOOLONG)
+ # https://github.com/rubygems/rubygems/issues/7132
+ - <<: *arm32-linux
allow_failures:
- # We see multiple errors indicating errors on the Travis environment itself in a short while:
- # https://app.travis-ci.com/github/ruby/ruby/jobs/544382885
- # https://app.travis-ci.com/github/ruby/ruby/jobs/544361370
- # It's not a fault of Ruby's arm32 support but just Travis arm32 seems unsable.
- - name: arm32-linux
+ # Allow failures for the unstable jobs.
# - name: arm64-linux
- # We see "Some worker was crashed." in about 40% of recent ppc64le-linux jobs
- # e.g. https://app.travis-ci.com/github/ruby/ruby/jobs/530959548
- name: ppc64le-linux
- # Tentatively disable, because often hungs up **after** all tests
- # have finished successfully and saving caches.
- name: s390x-linux
+ # The 2nd arm64 pipeline may be unstable.
+ # - name: arm32-linux
fast_finish: true
before_script:
- - . tool/ci_functions.sh
- - |-
- if [ -n "${TEST_ALL_SKIPPED_TESTS}" ]; then
- TEST_ALL_OPTS="${TEST_ALL_OPTS} $(ci_to_excluded_test_opts "${TEST_ALL_SKIPPED_TESTS}")"
- if [ -z "${TEST_ALL_SEPARATED_TESTS}" ]; then
- TEST_ALL_SEPARATED_TESTS="${TEST_ALL_SKIPPED_TESTS}"
- fi
- fi
- - |-
- if [ -n "${TEST_ALL_SEPARATED_TESTS}" ]; then
- TEST_ALL_OPTS_SEPARATED="$(ci_to_included_test_opts "${TEST_ALL_SEPARATED_TESTS}")"
- fi
- - echo TEST_ALL_OPTS="${TEST_ALL_OPTS}" TEST_ALL_OPTS_SEPARATED="${TEST_ALL_OPTS_SEPARATED}"
- - rm -fr .ext autom4te.cache
- - |-
- [ -d ~/.downloaded-cache ] ||
- mkdir ~/.downloaded-cache
- - ln -s ~/.downloaded-cache
- - "> config.status"
- - "> .rbconfig.time"
- - sed -f tool/prereq.status template/Makefile.in common.mk > Makefile
- - make -s $JOBS up
- - make -s $JOBS srcs
- - rm -f config.status Makefile rbconfig.rb .rbconfig.time
- - |-
- if [ -d ~/config_2nd ]; then
- cp -pr ~/config_2nd build
- else
- mkdir build
- fi
- - mkdir config_1st config_2nd gems/src
- - chmod -R a-w .
- - chmod -R u+w build config_1st config_2nd gems/src
+ - lscpu
+ - ./autogen.sh
+ - mkdir build
- cd build
- - |-
- case "$CC" in
- gcc*) CC="ccache $CC${GCC_FLAGS:+ }$GCC_FLAGS -fno-diagnostics-color";;
- clang*) CC="ccache $CC${GCC_FLAGS:+ }$GCC_FLAGS -fno-color-diagnostics";;
- esac
- - |-
- [ ! -f config.cache ] ||
- [ "$CC" = "`sed -n s/^ac_cv_prog_CC=//p config.cache`" ] ||
- (set -x; exec rm config.cache)
- - $SETARCH ../configure -C --disable-install-doc --prefix=$RUBY_PREFIX $CONFIG_FLAG
- - cp -pr config.cache config.status .ext/include ../config_1st
- - $SETARCH make reconfig
- - cp -pr config.cache config.status .ext/include ../config_2nd
- - (cd .. && exec diff -ru config_1st config_2nd)
- - chmod u+w ..
- - rm -rf ~/config_2nd
- - mv ../config_2nd ~
- - chmod u-w ..
+ - $SETARCH ../configure -C --disable-install-doc --prefix=$(pwd)/install
- $SETARCH make -s $JOBS
- - make -s install
- - |-
- [ -z "${GEMS_FOR_TEST}" ] ||
- $RUBY_PREFIX/bin/gem install --no-document $GEMS_FOR_TEST
- - echo "raise 'do not load ~/.irbrc in test'" > ~/.irbrc
+ - make -s $JOBS install
+ # Useful info to report issues to the Ruby.
+ - $SETARCH $(pwd)/install/bin/ruby -v
+ # Useful info To report issues to the RubyGems.
+ - $SETARCH $(pwd)/install/bin/gem env
script:
- - $SETARCH make -s test -o showflags TESTOPTS="${TESTOPTS=$JOBS -q --tty=no}"
- - ../tool/travis_wait.sh $SETARCH make -s test-all -o exts TESTOPTS="$JOBS -q --tty=no ${TEST_ALL_OPTS}" RUBYOPT="-w"
- # Run the failing tests separately returning ok status to check if it works,
- # visualize them.
- - |
- if [ -n "${TEST_ALL_OPTS_SEPARATED}" ]; then
- $SETARCH make -s test-all -o exts TESTOPTS="$JOBS -v --tty=no ${TEST_ALL_OPTS_SEPARATED}" RUBYOPT="-w" || :
- fi
- - $SETARCH make -s test-spec MSPECOPT=-ff # not using `-j` because sometimes `mspec -j` silently dies
- - $SETARCH make -s -o showflags leaked-globals
-
-# We enable Travis on the specific branches or forked repositories here.
-if: (repo = ruby/ruby AND (branch = master OR branch =~ /^ruby_\d_\d$/)) OR repo != ruby/ruby
+ - $SETARCH make -s test
+ - ../tool/travis_wait.sh $SETARCH make -s test-all RUBYOPT="-w"
+ - ../tool/travis_wait.sh $SETARCH make -s test-spec
# We want to be notified when something happens.
notifications:
- irc:
- channels:
- - "chat.freenode.net#ruby-core"
- on_success: change # [always|never|change] # default: always
- on_failure: always # [always|never|change] # default: always
- template:
- - "%{message} by @%{author}: See %{build_url}"
-
webhooks:
urls:
- - secure: mRsoS/UbqDkKkW5p3AEqM27d4SZnV6Gsylo3bm8T/deltQzTsGzZwrm7OIBXZv0UFZdE68XmPlyHfZFLSP2V9QZ7apXMf9/vw0GtcSe1gchtnjpAPF6lYBn7nMCbVPPx9cS0dwL927fjdRM1vj7IKZ2bk4F0lAJ25R25S6teqdk= # ruby-lang slack: ruby/simpler-alerts-bot (travis)
+ # ruby-lang slack: ruby/simpler-alerts-bot (travis)
+ - secure: mRsoS/UbqDkKkW5p3AEqM27d4SZnV6Gsylo3bm8T/deltQzTsGzZwrm7OIBXZv0UFZdE68XmPlyHfZFLSP2V9QZ7apXMf9/vw0GtcSe1gchtnjpAPF6lYBn7nMCbVPPx9cS0dwL927fjdRM1vj7IKZ2bk4F0lAJ25R25S6teqdk=
on_success: never
on_failure: always
-
email:
- - jaruga@ruby-lang.org
+ recipients:
+ - jaruga@ruby-lang.org
+ on_success: never
+ on_failure: always
diff --git a/LEGAL b/LEGAL
index e352c55ee5..c931291c8a 100644
--- a/LEGAL
+++ b/LEGAL
@@ -58,12 +58,12 @@ mentioned below.
[ccan/list/list.h]
- This file is licensed under the {MIT License}[rdoc-label:label-MIT+License].
+ This file is licensed under the {MIT License}[rdoc-ref:@MIT+License].
[coroutine]
Unless otherwise specified, these files are licensed under the
- {MIT License}[rdoc-label:label-MIT+License].
+ {MIT License}[rdoc-ref:@MIT+License].
[include/ruby/onigmo.h]
[include/ruby/oniguruma.h]
@@ -546,7 +546,7 @@ mentioned below.
[vsnprintf.c]
- This file is under the {old-style BSD license}[rdoc-label:label-Old-style+BSD+license].
+ This file is under the {old-style BSD license}[rdoc-ref:@Old-style+BSD+license].
>>>
Copyright (c) 1990, 1993::
@@ -577,7 +577,7 @@ mentioned below.
[missing/crypt.c]
- This file is under the {old-style BSD license}[rdoc-label:label-Old-style+BSD+license].
+ This file is under the {old-style BSD license}[rdoc-ref:@Old-style+BSD+license].
>>>
Copyright (c) 1989, 1993::
@@ -588,7 +588,7 @@ mentioned below.
[missing/setproctitle.c]
- This file is under the {old-style BSD license}[rdoc-label:label-Old-style+BSD+license].
+ This file is under the {old-style BSD license}[rdoc-ref:@Old-style+BSD+license].
>>>
Copyright 2003:: Damien Miller
@@ -879,7 +879,7 @@ mentioned below.
>>>
RubyGems is copyrighted free software by Chad Fowler, Rich Kilmer, Jim
Weirich and others. You can redistribute it and/or modify it under
- either the terms of the {MIT license}[rdoc-label:label-MIT+License], or the conditions
+ either the terms of the {MIT license}[rdoc-ref:@MIT+License], or the conditions
below:
1. You may make and give away verbatim copies of the source form of the
@@ -941,7 +941,7 @@ mentioned below.
Portions copyright (c) 2010:: Andre Arko
Portions copyright (c) 2009:: Engine Yard
- {MIT License}[rdoc-label:label-MIT+License]
+ {MIT License}[rdoc-ref:@MIT+License]
[lib/bundler/vendor/thor]
@@ -950,16 +950,16 @@ mentioned below.
>>>
Copyright (c) 2008 Yehuda Katz, Eric Hodel, et al.
- {MIT License}[rdoc-label:label-MIT+License]
+ {MIT License}[rdoc-ref:@MIT+License]
-[lib/rubygems/resolver/molinillo]
+[lib/rubygems/vendor/molinillo]
molinillo is under the following license.
>>>
Copyright (c) 2014 Samuel E. Giddins segiddins@segiddins.me
- {MIT License}[rdoc-label:label-MIT+License]
+ {MIT License}[rdoc-ref:@MIT+License]
[lib/bundler/vendor/pub_grub]
@@ -968,7 +968,7 @@ mentioned below.
>>>
Copyright (c) 2018 John Hawthorn
- {MIT License}[rdoc-label:label-MIT+License]
+ {MIT License}[rdoc-ref:@MIT+License]
[lib/bundler/vendor/connection_pool]
@@ -977,7 +977,7 @@ mentioned below.
>>>
Copyright (c) 2011 Mike Perham
- {MIT License}[rdoc-label:label-MIT+License]
+ {MIT License}[rdoc-ref:@MIT+License]
[lib/bundler/vendor/net-http-persistent]
@@ -986,7 +986,7 @@ mentioned below.
>>>
Copyright (c) Eric Hodel, Aaron Patterson
- {MIT License}[rdoc-label:label-MIT+License]
+ {MIT License}[rdoc-ref:@MIT+License]
[lib/did_you_mean]
[lib/did_you_mean.rb]
@@ -997,7 +997,7 @@ mentioned below.
>>>
Copyright (c) 2014-2016 Yuki Nishijima
- {MIT License}[rdoc-label:label-MIT+License]
+ {MIT License}[rdoc-ref:@MIT+License]
[lib/error_highlight]
[lib/error_highlight.rb]
@@ -1008,7 +1008,7 @@ mentioned below.
>>>
Copyright (c) 2021 Yusuke Endoh
- {MIT License}[rdoc-label:label-MIT+License]
+ {MIT License}[rdoc-ref:@MIT+License]
[benchmark/so_ackermann.rb]
[benchmark/so_array.rb]
diff --git a/NEWS.md b/NEWS.md
index 9f9e75d16f..c6dd44c435 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,130 +1,143 @@
-# NEWS for Ruby 3.3.0
+# NEWS for Ruby 3.4.0
This document is a list of user-visible feature changes
-since the **3.2.0** release, except for bug fixes.
+since the **3.3.0** release, except for bug fixes.
Note that each entry is kept to a minimum, see links for details.
## Language changes
-## Command line options
+* String literals in files without a `frozen_string_literal` comment now behave
+ as if they were frozen. If they are mutated a deprecation warning is emitted.
+ These warnings can be enabled with `-W:deprecated` or by setting `Warning[:deprecated] = true`.
+ To disable this change, you can run Ruby with the `--disable-frozen-string-literal`
+ command line argument. [[Feature #20205]]
-* A new `performance` warning category was introduced.
- They are not displayed by default even in verbose mode.
- Turn them on with `-W:performance` or `Warning[:performance] = true`. [[Feature #19538]]
+* `it` is added to reference a block parameter. [[Feature #18980]]
-## Core classes updates
-
-Note: We're only listing outstanding class updates.
+* Keyword splatting `nil` when calling methods is now supported.
+ `**nil` is treated similarly to `**{}`, passing no keywords,
+ and not calling any conversion methods. [[Bug #20064]]
-* Array
+* Block passing is no longer allowed in index. [[Bug #19918]]
- * `Array#pack` now raises ArgumentError for unknown directives. [[Bug #19150]]
+* Keyword arguments are no longer allowed in index. [[Bug #20218]]
-* Dir
-
- * `Dir.for_fd` added for returning a Dir object for the directory specified
- by the provided directory file descriptor. [[Feature #19347]]
- * `Dir.fchdir` added for changing the directory to the directory specified
- by the provided directory file descriptor. [[Feature #19347]]
- * `Dir#chdir` added for changing the directory to the directory specified
- by the provided `Dir` object. [[Feature #19347]]
-
-* MatchData
+## Core classes updates
- * MatchData#named_captures now accepts optional `symbolize_names` keyword. [[Feature #19591]]
+Note: We're only listing outstanding class updates.
-* String
+* Exception
- * `String#unpack` now raises ArgumentError for unknown directives. [[Bug #19150]]
- * `String#bytesplice` now accepts new arguments index/length or range of the source string to be copied. [[Feature #19314]]
+ * Exception#set_backtrace now accepts arrays of `Thread::Backtrace::Location`.
+ `Kernel#raise`, `Thread#raise` and `Fiber#raise` also accept this new format. [[Feature #13557]]
-* ObjectSpace::WeakKeyMap
+* Range
- * New core class to build collections with weak references.
- The class use equality semantic to lookup keys like a regular hash,
- but it doesn't hold strong references on the keys. [[Feature #18498]]
+ * Range#size now raises TypeError if the range is not iterable. [[Misc #18984]]
## Stdlib updates
The following default gems are updated.
-* RubyGems 3.5.0.dev
-* bigdecimal 3.1.4
-* bundler 2.5.0.dev
-* csv 3.2.7
-* fiddle 1.1.2
-* fileutils 1.7.1
-* irb 1.7.0
-* nkf 0.1.3
-* optparse 0.4.0.pre.1
-* psych 5.1.0
-* reline 0.3.5
-* stringio 3.0.8
-* strscan 3.0.7
-* syntax_suggest 1.1.0
-* time 0.2.2
-* timeout 0.3.2
-* uri 0.12.1
+* RubyGems 3.6.0.dev
+* bundler 2.6.0.dev
+* erb 4.0.4
+* fiddle 1.1.3
+* io-console 0.7.2
+* irb 1.12.0
+* json 2.7.2
+* net-http 0.4.1
+* optparse 0.5.0
+* prism 0.27.0
+* rdoc 6.6.3.1
+* reline 0.5.4
+* resolv 0.4.0
+* stringio 3.1.1
+* strscan 3.1.1
The following bundled gems are updated.
-* minitest 5.18.0
-* test-unit 3.5.9
-* rbs 3.1.0
-* typeprof 0.21.7
-* debug 1.8.0
-
-See GitHub releases like [Logger](https://github.com/ruby/logger/releases) or
-changelog for details of the default gems or bundled gems.
+* minitest 5.22.3
+* rake 13.2.1
+* test-unit 3.6.2
+* net-ftp 0.3.4
+* net-imap 0.4.10
+* net-smtp 0.5.0
+* rbs 3.4.4
+* typeprof 0.21.11
+* debug 1.9.2
+
+The following bundled gems are promoted from default gems.
+
+* mutex_m 0.2.0
+* getoptlong 0.2.1
+* base64 0.2.0
+* bigdecimal 3.1.7
+* observer 0.1.2
+* abbrev 0.1.2
+* resolv-replace 0.1.1
+* rinda 0.2.0
+* drb 2.2.1
+* nkf 0.2.0
+* syslog 0.1.2
+* csv 3.3.0
+
+See GitHub releases like [GitHub Releases of Logger](https://github.com/ruby/logger/releases) or changelog for details of the default gems or bundled gems.
## Supported platforms
## Compatibility issues
-## Stdlib compatibility issues
+* Error messages and backtrace displays have been changed.
+ * Use a single quote instead of a backtick as a opening quote. [[Feature #16495]]
+ * Display a class name before a method name (only when the class has a permanent name). [[Feature #19117]]
+ * `Kernel#caller`, `Thread::Backtrace::Location`'s methods, etc. are also changed accordingly.
+ ```
+ Old:
+ test.rb:1:in `foo': undefined method `time' for an instance of Integer
+ from test.rb:2:in `<main>'
-### `ext/readline` is retired
+ New:
+ test.rb:1:in 'Object#foo': undefined method 'time' for an instance of Integer
+ from test.rb:2:in `<main>'
+ ```
-* We have `reline` that is pure Ruby implementation compatible with `ext/readline` API. We rely on `reline` in the future. If you need to use `ext/readline`, you can install `ext/readline` via rubygems.org with `gem install readline-ext`.
-* We no longer need to install libraries like `libreadline` or `libedit`.
+## Stdlib compatibility issues
## C API updates
+* `rb_newobj` and `rb_newobj_of` (and corresponding macros `RB_NEWOBJ`, `RB_NEWOBJ_OF`, `NEWOBJ`, `NEWOBJ_OF`) have been removed. [[Feature #20265]]
+* Removed deprecated function `rb_gc_force_recycle`. [[Feature #18290]]
+
## Implementation improvements
-* `defined?(@ivar)` is optimized with Object Shapes.
-
-### YJIT
-
-* Significant performance improvements over 3.2
- * Splat and rest arguments support has been improved.
- * Registers are allocated for stack operations of the virtual machine.
- * More calls with optional arguments are compiled.
- * `Integer#!=`, `String#!=`, `Kernel#block_given?`, `Kernel#is_a?`,
- `Kernel#instance_of?`, `Module#===` are specially optimized.
- * Instance variables no longer exit to the interpreter
- with megamorphic Object Shapes.
-* Metadata for compiled code uses a lot less memory.
-* Improved code generation on ARM64
-* Option to start YJIT in paused mode and then later enable it manually
- * `--yjit-pause` and `RubyVM::YJIT.resume`
- * This can be used to enable YJIT only once your application is done booting
-* Exit tracing option now supports sampling
- * `--trace-exits-sample-rate=N`
-* Multiple bug fixes
-
-### RJIT
-
-* Introduced a pure-Ruby JIT compiler RJIT and replaced MJIT.
- * RJIT supports only x86\_64 architecture on Unix platforms.
- * Unlike MJIT, it doesn't require a C compiler at runtime.
-* RJIT exists only for experimental purposes.
- * You should keep using YJIT in production.
-
-[Feature #18498]: https://bugs.ruby-lang.org/issues/18498
-[Bug #19150]: https://bugs.ruby-lang.org/issues/19150
-[Feature #19314]: https://bugs.ruby-lang.org/issues/19314
-[Feature #19347]: https://bugs.ruby-lang.org/issues/19347
-[Feature #19538]: https://bugs.ruby-lang.org/issues/19538
-[Feature #19591]: https://bugs.ruby-lang.org/issues/19591
+* `Array#each` is rewritten in Ruby for better performance [[Feature #20182]].
+
+## JIT
+
+## Miscellaneous changes
+
+* Passing a block to a method which doesn't use the passed block will show
+ a warning on verbose mode (`-w`).
+ [[Feature #15554]]
+
+* Redefining some core methods that are specially optimized by the interpeter
+ and JIT like `String.freeze` or `Integer#+` now emits a performance class
+ warning (`-W:performance` or `Warning[:performance] = true`).
+ [[Feature #20429]]
+
+[Feature #13557]: https://bugs.ruby-lang.org/issues/13557
+[Feature #15554]: https://bugs.ruby-lang.org/issues/15554
+[Feature #16495]: https://bugs.ruby-lang.org/issues/16495
+[Feature #18290]: https://bugs.ruby-lang.org/issues/18290
+[Feature #18980]: https://bugs.ruby-lang.org/issues/18980
+[Misc #18984]: https://bugs.ruby-lang.org/issues/18984
+[Feature #19117]: https://bugs.ruby-lang.org/issues/19117
+[Bug #19918]: https://bugs.ruby-lang.org/issues/19918
+[Bug #20064]: https://bugs.ruby-lang.org/issues/20064
+[Feature #20182]: https://bugs.ruby-lang.org/issues/20182
+[Feature #20205]: https://bugs.ruby-lang.org/issues/20205
+[Bug #20218]: https://bugs.ruby-lang.org/issues/20218
+[Feature #20265]: https://bugs.ruby-lang.org/issues/20265
+[Feature #20429]: https://bugs.ruby-lang.org/issues/20429
diff --git a/README.ja.md b/README.ja.md
index 66f9e4e37f..0d2d309fb8 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -26,7 +26,7 @@ Rubyã¯ãƒ†ã‚­ã‚¹ãƒˆå‡¦ç†é–¢ä¿‚ã®èƒ½åŠ›ãªã©ã«å„ªã‚Œï¼ŒPerlã¨åŒã˜ãらã„
* ダイナミックローディング (アーキテクãƒãƒ£ã«ã‚ˆã‚‹)
* 移æ¤æ€§ãŒé«˜ã„.多ãã®Unix-like/POSIX互æ›ãƒ—ラットフォーム上ã§å‹•ãã ã‘ã§ãªã,Windows, macOS,
Haikuãªã©ã®ä¸Šã§ã‚‚å‹•ã cf.
- https://github.com/ruby/ruby/blob/master/doc/contributing.rdoc#platform-maintainers
+ https://docs.ruby-lang.org/en/master/maintainers_md.html#label-Platform+Maintainers
## 入手法
@@ -152,7 +152,7 @@ UNIXã§ã‚れ㰠`configure` ãŒã»ã¨ã‚“ã©ã®å·®ç•°ã‚’å¸åŽã—ã¦ãれるã¯
## é…布æ¡ä»¶
-[COPYING.ja](COPYING.ja) ファイルをå‚ç…§ã—ã¦ãã ã•ã„.
+[COPYING.ja](https://docs.ruby-lang.org/en/master/COPYING_ja.html) ファイルをå‚ç…§ã—ã¦ãã ã•ã„.
## フィードãƒãƒƒã‚¯
diff --git a/README.md b/README.md
index 715f53287a..eb24a73ee3 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,7 @@
[![Actions Status: RJIT](https://github.com/ruby/ruby/workflows/RJIT/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"RJIT")
[![Actions Status: Ubuntu](https://github.com/ruby/ruby/workflows/Ubuntu/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"Ubuntu")
[![Actions Status: Windows](https://github.com/ruby/ruby/workflows/Windows/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"Windows")
-[![AppVeyor status](https://ci.appveyor.com/api/projects/status/0sy8rrxut4o0k960/branch/master?svg=true)](https://ci.appveyor.com/project/ruby/ruby/branch/master)
[![Travis Status](https://app.travis-ci.com/ruby/ruby.svg?branch=master)](https://app.travis-ci.com/ruby/ruby)
-[![Cirrus Status](https://api.cirrus-ci.com/github/ruby/ruby.svg)](https://cirrus-ci.com/github/ruby/ruby/master)
# What is Ruby?
@@ -25,7 +23,7 @@ It is simple, straightforward, and extensible.
* Dynamic Loading of Object Files (on some architectures)
* Highly Portable (works on many Unix-like/POSIX compatible platforms as
well as Windows, macOS, etc.) cf.
- https://github.com/ruby/ruby/blob/master/doc/maintainers.md#platform-maintainers
+ https://docs.ruby-lang.org/en/master/maintainers_md.html#label-Platform+Maintainers
## How to get Ruby
@@ -53,7 +51,7 @@ if you are a committer.
## How to build
-see [Building Ruby](doc/contributing/building_ruby.md)
+See [Building Ruby](https://docs.ruby-lang.org/en/master/contributing/building_ruby_md.html)
## Ruby home page
diff --git a/addr2line.c b/addr2line.c
index 2f54c14162..02a3e617a6 100644
--- a/addr2line.c
+++ b/addr2line.c
@@ -8,10 +8,14 @@
**********************************************************************/
-#if defined(__clang__)
+#if defined(__clang__) && defined(__has_warning)
+#if __has_warning("-Wgnu-empty-initializer")
#pragma clang diagnostic ignored "-Wgnu-empty-initializer"
+#endif
+#if __has_warning("-Wgcc-compat")
#pragma clang diagnostic ignored "-Wgcc-compat"
#endif
+#endif
#include "ruby/internal/config.h"
#include "ruby/defines.h"
@@ -57,8 +61,21 @@ void *alloca();
# endif
# endif /* AIX */
# endif /* HAVE_ALLOCA_H */
+# ifndef UNREACHABLE
+# define UNREACHABLE __builtin_unreachable()
+# endif
+# ifndef UNREACHABLE_RETURN
+# define UNREACHABLE_RETURN(_) __builtin_unreachable()
+# endif
#endif /* __GNUC__ */
+#ifndef UNREACHABLE
+# define UNREACHABLE abort()
+#endif
+#ifndef UNREACHABLE_RETURN
+# define UNREACHABLE_RETURN(_) return (abort(), (_))
+#endif
+
#ifdef HAVE_DLADDR
# include <dlfcn.h>
#endif
@@ -127,7 +144,7 @@ void *alloca();
#define DW_LNE_define_file 0x03
#define DW_LNE_set_discriminator 0x04 /* DWARF4 */
-PRINTF_ARGS(static int kprintf(const char *fmt, ...), 1, 2);
+#define kprintf(...) fprintf(errout, "" __VA_ARGS__)
typedef struct line_info {
const char *dirname;
@@ -184,7 +201,7 @@ obj_dwarf_section_at(obj_info_t *obj, int n)
&obj->debug_line_str
};
if (n < 0 || DWARF_SECTION_COUNT <= n) {
- abort();
+ UNREACHABLE_RETURN(0);
}
return ary[n];
}
@@ -237,7 +254,7 @@ sleb128(const char **p)
}
static const char *
-get_nth_dirname(unsigned long dir, const char *p)
+get_nth_dirname(unsigned long dir, const char *p, FILE *errout)
{
if (!dir--) {
return "";
@@ -254,10 +271,14 @@ get_nth_dirname(unsigned long dir, const char *p)
return p;
}
-static const char *parse_ver5_debug_line_header(const char *p, int idx, uint8_t format, obj_info_t *obj, const char **out_path, uint64_t *out_directory_index);
+static const char *parse_ver5_debug_line_header(
+ const char *p, int idx, uint8_t format,
+ obj_info_t *obj, const char **out_path,
+ uint64_t *out_directory_index, FILE *errout);
static void
-fill_filename(int file, uint8_t format, uint16_t version, const char *include_directories, const char *filenames, line_info_t *line, obj_info_t *obj)
+fill_filename(int file, uint8_t format, uint16_t version, const char *include_directories,
+ const char *filenames, line_info_t *line, obj_info_t *obj, FILE *errout)
{
int i;
const char *p = filenames;
@@ -266,9 +287,9 @@ fill_filename(int file, uint8_t format, uint16_t version, const char *include_di
if (version >= 5) {
const char *path;
uint64_t directory_index = -1;
- parse_ver5_debug_line_header(filenames, file, format, obj, &path, &directory_index);
+ parse_ver5_debug_line_header(filenames, file, format, obj, &path, &directory_index, errout);
line->filename = path;
- parse_ver5_debug_line_header(include_directories, (int)directory_index, format, obj, &path, NULL);
+ parse_ver5_debug_line_header(include_directories, (int)directory_index, format, obj, &path, NULL, errout);
line->dirname = path;
}
else {
@@ -290,7 +311,7 @@ fill_filename(int file, uint8_t format, uint16_t version, const char *include_di
if (i == file) {
line->filename = filename;
- line->dirname = get_nth_dirname(dir, include_directories);
+ line->dirname = get_nth_dirname(dir, include_directories, errout);
}
}
}
@@ -299,7 +320,7 @@ fill_filename(int file, uint8_t format, uint16_t version, const char *include_di
static void
fill_line(int num_traces, void **traces, uintptr_t addr, int file, int line,
uint8_t format, uint16_t version, const char *include_directories, const char *filenames,
- obj_info_t *obj, line_info_t *lines, int offset)
+ obj_info_t *obj, line_info_t *lines, int offset, FILE *errout)
{
int i;
addr += obj->base_addr - obj->vmaddr;
@@ -308,7 +329,7 @@ fill_line(int num_traces, void **traces, uintptr_t addr, int file, int line,
/* We assume one line code doesn't result >100 bytes of native code.
We may want more reliable way eventually... */
if (addr < a && a < addr + 100) {
- fill_filename(file, format, version, include_directories, filenames, &lines[i], obj);
+ fill_filename(file, format, version, include_directories, filenames, &lines[i], obj, errout);
lines[i].line = line;
}
}
@@ -333,7 +354,7 @@ struct LineNumberProgramHeader {
};
static int
-parse_debug_line_header(obj_info_t *obj, const char **pp, struct LineNumberProgramHeader *header)
+parse_debug_line_header(obj_info_t *obj, const char **pp, struct LineNumberProgramHeader *header, FILE *errout)
{
const char *p = *pp;
header->unit_length = *(uint32_t *)p;
@@ -379,7 +400,7 @@ parse_debug_line_header(obj_info_t *obj, const char **pp, struct LineNumberProgr
if (header->version >= 5) {
header->include_directories = p;
- p = parse_ver5_debug_line_header(p, -1, header->format, obj, NULL, NULL);
+ p = parse_ver5_debug_line_header(p, -1, header->format, obj, NULL, NULL, errout);
header->filenames = p;
}
else {
@@ -406,7 +427,7 @@ parse_debug_line_header(obj_info_t *obj, const char **pp, struct LineNumberProgr
static int
parse_debug_line_cu(int num_traces, void **traces, const char **debug_line,
- obj_info_t *obj, line_info_t *lines, int offset)
+ obj_info_t *obj, line_info_t *lines, int offset, FILE *errout)
{
const char *p = (const char *)*debug_line;
struct LineNumberProgramHeader header;
@@ -423,7 +444,7 @@ parse_debug_line_cu(int num_traces, void **traces, const char **debug_line,
/* int epilogue_begin = 0; */
/* unsigned int isa = 0; */
- if (parse_debug_line_header(obj, &p, &header))
+ if (parse_debug_line_header(obj, &p, &header, errout))
return -1;
is_stmt = header.default_is_stmt;
@@ -434,7 +455,7 @@ parse_debug_line_cu(int num_traces, void **traces, const char **debug_line,
header.version, \
header.include_directories, \
header.filenames, \
- obj, lines, offset); \
+ obj, lines, offset, errout); \
/*basic_block = prologue_end = epilogue_begin = 0;*/ \
} while (0)
@@ -534,11 +555,11 @@ parse_debug_line_cu(int num_traces, void **traces, const char **debug_line,
static int
parse_debug_line(int num_traces, void **traces,
const char *debug_line, unsigned long size,
- obj_info_t *obj, line_info_t *lines, int offset)
+ obj_info_t *obj, line_info_t *lines, int offset, FILE *errout)
{
const char *debug_line_end = debug_line + size;
while (debug_line < debug_line_end) {
- if (parse_debug_line_cu(num_traces, traces, &debug_line, obj, lines, offset))
+ if (parse_debug_line_cu(num_traces, traces, &debug_line, obj, lines, offset, errout))
return -1;
}
if (debug_line != debug_line_end) {
@@ -551,7 +572,7 @@ parse_debug_line(int num_traces, void **traces,
/* read file and fill lines */
static uintptr_t
fill_lines(int num_traces, void **traces, int check_debuglink,
- obj_info_t **objp, line_info_t *lines, int offset);
+ obj_info_t **objp, line_info_t *lines, int offset, FILE *errout);
static void
append_obj(obj_info_t **objp)
@@ -579,7 +600,7 @@ append_obj(obj_info_t **objp)
// check the path pattern of "/usr/lib/debug/usr/bin/ruby.debug"
static void
follow_debuglink(const char *debuglink, int num_traces, void **traces,
- obj_info_t **objp, line_info_t *lines, int offset)
+ obj_info_t **objp, line_info_t *lines, int offset, FILE *errout)
{
static const char global_debug_dir[] = "/usr/lib/debug";
const size_t global_debug_dir_len = sizeof(global_debug_dir) - 1;
@@ -605,13 +626,13 @@ follow_debuglink(const char *debuglink, int num_traces, void **traces,
o2 = *objp;
o2->base_addr = o1->base_addr;
o2->path = o1->path;
- fill_lines(num_traces, traces, 0, objp, lines, offset);
+ fill_lines(num_traces, traces, 0, objp, lines, offset, errout);
}
// check the path pattern of "/usr/lib/debug/.build-id/ab/cdef1234.debug"
static void
follow_debuglink_build_id(const char *build_id, size_t build_id_size, int num_traces, void **traces,
- obj_info_t **objp, line_info_t *lines, int offset)
+ obj_info_t **objp, line_info_t *lines, int offset, FILE *errout)
{
static const char global_debug_dir[] = "/usr/lib/debug/.build-id/";
const size_t global_debug_dir_len = sizeof(global_debug_dir) - 1;
@@ -636,7 +657,7 @@ follow_debuglink_build_id(const char *build_id, size_t build_id_size, int num_tr
o2 = *objp;
o2->base_addr = o1->base_addr;
o2->path = o1->path;
- fill_lines(num_traces, traces, 0, objp, lines, offset);
+ fill_lines(num_traces, traces, 0, objp, lines, offset, errout);
}
#endif
@@ -1062,13 +1083,13 @@ di_read_debug_abbrev_cu(DebugInfoReader *reader)
}
static int
-di_read_debug_line_cu(DebugInfoReader *reader)
+di_read_debug_line_cu(DebugInfoReader *reader, FILE *errout)
{
const char *p;
struct LineNumberProgramHeader header;
p = (const char *)reader->debug_line_cu_end;
- if (parse_debug_line_header(reader->obj, &p, &header))
+ if (parse_debug_line_header(reader->obj, &p, &header, errout))
return -1;
reader->debug_line_cu_end = (char *)header.cu_end;
@@ -1148,19 +1169,32 @@ resolve_strx(DebugInfoReader *reader, uint64_t idx)
return reader->obj->debug_str.ptr + off;
}
-static void
-debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoValue *v)
+static bool
+debug_info_reader_read_addr_value_member(DebugInfoReader *reader, DebugInfoValue *v, int size)
+{
+ if (size == 4) {
+ set_uint_value(v, read_uint32(&reader->p));
+ } else if (size == 8) {
+ set_uint_value(v, read_uint64(&reader->p));
+ } else {
+ return false;
+ }
+ return true;
+}
+
+#define debug_info_reader_read_addr_value(reader, v, mem) \
+ if (!debug_info_reader_read_addr_value_member((reader), (v), (reader)->mem)) { \
+ kprintf("unknown " #mem ":%d", (reader)->mem); \
+ return false; \
+ }
+
+
+static bool
+debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoValue *v, FILE *errout)
{
switch (form) {
case DW_FORM_addr:
- if (reader->address_size == 4) {
- set_uint_value(v, read_uint32(&reader->p));
- } else if (reader->address_size == 8) {
- set_uint_value(v, read_uint64(&reader->p));
- } else {
- fprintf(stderr,"unknown address_size:%d", reader->address_size);
- abort();
- }
+ debug_info_reader_read_addr_value(reader, v, address_size);
break;
case DW_FORM_block2:
v->size = read_uint16(&reader->p);
@@ -1212,13 +1246,12 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa
set_uint_value(v, read_uleb128(reader));
break;
case DW_FORM_ref_addr:
- if (reader->format == 4) {
- set_uint_value(v, read_uint32(&reader->p));
- } else if (reader->format == 8) {
- set_uint_value(v, read_uint64(&reader->p));
+ if (reader->current_version <= 2) {
+ // DWARF Version 2 specifies that references have
+ // the same size as an address on the target system
+ debug_info_reader_read_addr_value(reader, v, address_size);
} else {
- fprintf(stderr,"unknown format:%d", reader->format);
- abort();
+ debug_info_reader_read_addr_value(reader, v, format);
}
break;
case DW_FORM_ref1:
@@ -1331,16 +1364,16 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa
goto fail;
break;
}
- return;
+ return true;
fail:
- fprintf(stderr, "%d: unsupported form: %#"PRIx64"\n", __LINE__, form);
- exit(1);
+ kprintf("%d: unsupported form: %#"PRIx64"\n", __LINE__, form);
+ return false;
}
/* find abbrev in current compilation unit */
static const char *
-di_find_abbrev(DebugInfoReader *reader, uint64_t abbrev_number)
+di_find_abbrev(DebugInfoReader *reader, uint64_t abbrev_number, FILE *errout)
{
const char *p;
if (abbrev_number < ABBREV_TABLE_SIZE) {
@@ -1353,8 +1386,8 @@ di_find_abbrev(DebugInfoReader *reader, uint64_t abbrev_number)
di_skip_die_attributes(&p);
for (uint64_t n = uleb128(&p); abbrev_number != n; n = uleb128(&p)) {
if (n == 0) {
- fprintf(stderr,"%d: Abbrev Number %"PRId64" not found\n",__LINE__, abbrev_number);
- exit(1);
+ kprintf("%d: Abbrev Number %"PRId64" not found\n",__LINE__, abbrev_number);
+ return NULL;
}
uleb128(&p); /* tag */
p++; /* has_children */
@@ -1365,52 +1398,52 @@ di_find_abbrev(DebugInfoReader *reader, uint64_t abbrev_number)
#if 0
static void
-hexdump0(const unsigned char *p, size_t n)
+hexdump0(const unsigned char *p, size_t n, FILE *errout)
{
size_t i;
- fprintf(stderr, " 0 1 2 3 4 5 6 7 8 9 A B C D E F\n");
+ kprintf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F\n");
for (i=0; i < n; i++){
switch (i & 15) {
case 0:
- fprintf(stderr, "%02" PRIdSIZE ": %02X ", i/16, p[i]);
+ kprintf("%02" PRIdSIZE ": %02X ", i/16, p[i]);
break;
case 15:
- fprintf(stderr, "%02X\n", p[i]);
+ kprintf("%02X\n", p[i]);
break;
default:
- fprintf(stderr, "%02X ", p[i]);
+ kprintf("%02X ", p[i]);
break;
}
}
if ((i & 15) != 15) {
- fprintf(stderr, "\n");
+ kprintf("\n");
}
}
-#define hexdump(p,n) hexdump0((const unsigned char *)p, n)
+#define hexdump(p,n,e) hexdump0((const unsigned char *)p, n, e)
static void
-div_inspect(DebugInfoValue *v)
+div_inspect(DebugInfoValue *v, FILE *errout)
{
switch (v->type) {
case VAL_uint:
- fprintf(stderr,"%d: type:%d size:%" PRIxSIZE " v:%"PRIx64"\n",__LINE__,v->type,v->size,v->as.uint64);
+ kprintf("%d: type:%d size:%" PRIxSIZE " v:%"PRIx64"\n",__LINE__,v->type,v->size,v->as.uint64);
break;
case VAL_int:
- fprintf(stderr,"%d: type:%d size:%" PRIxSIZE " v:%"PRId64"\n",__LINE__,v->type,v->size,(int64_t)v->as.uint64);
+ kprintf("%d: type:%d size:%" PRIxSIZE " v:%"PRId64"\n",__LINE__,v->type,v->size,(int64_t)v->as.uint64);
break;
case VAL_cstr:
- fprintf(stderr,"%d: type:%d size:%" PRIxSIZE " v:'%s'\n",__LINE__,v->type,v->size,v->as.ptr);
+ kprintf("%d: type:%d size:%" PRIxSIZE " v:'%s'\n",__LINE__,v->type,v->size,v->as.ptr);
break;
case VAL_data:
- fprintf(stderr,"%d: type:%d size:%" PRIxSIZE " v:\n",__LINE__,v->type,v->size);
- hexdump(v->as.ptr, 16);
+ kprintf("%d: type:%d size:%" PRIxSIZE " v:\n",__LINE__,v->type,v->size);
+ hexdump(v->as.ptr, 16, errout);
break;
}
}
#endif
static DIE *
-di_read_die(DebugInfoReader *reader, DIE *die)
+di_read_die(DebugInfoReader *reader, DIE *die, FILE *errout)
{
uint64_t abbrev_number = uleb128(&reader->p);
if (abbrev_number == 0) {
@@ -1418,7 +1451,7 @@ di_read_die(DebugInfoReader *reader, DIE *die)
return NULL;
}
- reader->q = di_find_abbrev(reader, abbrev_number);
+ if (!(reader->q = di_find_abbrev(reader, abbrev_number, errout))) return NULL;
die->pos = reader->p - reader->obj->debug_info.ptr - 1;
die->tag = (int)uleb128(&reader->q); /* tag */
@@ -1430,26 +1463,26 @@ di_read_die(DebugInfoReader *reader, DIE *die)
}
static DebugInfoValue *
-di_read_record(DebugInfoReader *reader, DebugInfoValue *vp)
+di_read_record(DebugInfoReader *reader, DebugInfoValue *vp, FILE *errout)
{
uint64_t at = uleb128(&reader->q);
uint64_t form = uleb128(&reader->q);
if (!at || !form) return NULL;
vp->at = at;
vp->form = form;
- debug_info_reader_read_value(reader, form, vp);
+ if (!debug_info_reader_read_value(reader, form, vp, errout)) return NULL;
return vp;
}
-static void
-di_skip_records(DebugInfoReader *reader)
+static bool
+di_skip_records(DebugInfoReader *reader, FILE *errout)
{
for (;;) {
- DebugInfoValue v = {{}};
+ DebugInfoValue v = {{0}};
uint64_t at = uleb128(&reader->q);
uint64_t form = uleb128(&reader->q);
- if (!at || !form) return;
- debug_info_reader_read_value(reader, form, &v);
+ if (!at || !form) return true;
+ if (!debug_info_reader_read_value(reader, form, &v, errout)) return false;
}
}
@@ -1461,13 +1494,14 @@ typedef struct addr_header {
/* uint8_t segment_selector_size; */
} addr_header_t;
-static void
-addr_header_init(obj_info_t *obj, addr_header_t *header) {
+static bool
+addr_header_init(obj_info_t *obj, addr_header_t *header, FILE *errout)
+{
const char *p = obj->debug_addr.ptr;
header->ptr = p;
- if (!p) return;
+ if (!p) return true;
header->unit_length = *(uint32_t *)p;
p += sizeof(uint32_t);
@@ -1481,7 +1515,12 @@ addr_header_init(obj_info_t *obj, addr_header_t *header) {
p += 2; /* version */
header->address_size = *p++;
+ if (header->address_size != 4 && header->address_size != 8) {
+ kprintf("unknown address_size:%d", header->address_size);
+ return false;
+ }
p++; /* segment_selector_size */
+ return true;
}
static uint64_t
@@ -1501,11 +1540,12 @@ typedef struct rnglists_header {
uint32_t offset_entry_count;
} rnglists_header_t;
-static void
-rnglists_header_init(obj_info_t *obj, rnglists_header_t *header) {
+static bool
+rnglists_header_init(obj_info_t *obj, rnglists_header_t *header, FILE *errout)
+{
const char *p = obj->debug_rnglists.ptr;
- if (!p) return;
+ if (!p) return true;
header->unit_length = *(uint32_t *)p;
p += sizeof(uint32_t);
@@ -1519,8 +1559,13 @@ rnglists_header_init(obj_info_t *obj, rnglists_header_t *header) {
p += 2; /* version */
header->address_size = *p++;
+ if (header->address_size != 4 && header->address_size != 8) {
+ kprintf("unknown address_size:%d", header->address_size);
+ return false;
+ }
p++; /* segment_selector_size */
header->offset_entry_count = *(uint32_t *)p;
+ return true;
}
typedef struct {
@@ -1564,26 +1609,23 @@ ranges_set(ranges_t *ptr, DebugInfoValue *v, addr_header_t *addr_header, uint64_
}
static uint64_t
-read_dw_form_addr(DebugInfoReader *reader, const char **ptr)
+read_dw_form_addr(DebugInfoReader *reader, const char **ptr, FILE *errout)
{
const char *p = *ptr;
*ptr = p + reader->address_size;
if (reader->address_size == 4) {
return read_uint32(&p);
- } else if (reader->address_size == 8) {
- return read_uint64(&p);
} else {
- fprintf(stderr,"unknown address_size:%d", reader->address_size);
- abort();
+ return read_uint64(&p);
}
}
static uintptr_t
-ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr, rnglists_header_t *rnglists_header)
+ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr, rnglists_header_t *rnglists_header, FILE *errout)
{
if (ptr->high_pc_set) {
if (ptr->ranges_set || !ptr->low_pc_set) {
- exit(1);
+ return UINTPTR_MAX;
}
if (ptr->low_pc <= addr && addr <= ptr->high_pc) {
return (uintptr_t)ptr->low_pc;
@@ -1632,15 +1674,15 @@ ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr, rnglists_h
to = (uintptr_t)base + uleb128(&p);
break;
case DW_RLE_base_address:
- base = read_dw_form_addr(reader, &p);
+ base = read_dw_form_addr(reader, &p, errout);
base_valid = true;
break;
case DW_RLE_start_end:
- from = (uintptr_t)read_dw_form_addr(reader, &p);
- to = (uintptr_t)read_dw_form_addr(reader, &p);
+ from = (uintptr_t)read_dw_form_addr(reader, &p, errout);
+ to = (uintptr_t)read_dw_form_addr(reader, &p, errout);
break;
case DW_RLE_start_length:
- from = (uintptr_t)read_dw_form_addr(reader, &p);
+ from = (uintptr_t)read_dw_form_addr(reader, &p, errout);
to = from + uleb128(&p);
break;
}
@@ -1648,7 +1690,7 @@ ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr, rnglists_h
return from;
}
}
- return false;
+ return 0;
}
p = reader->obj->debug_ranges.ptr + ptr->ranges;
for (;;) {
@@ -1669,42 +1711,42 @@ ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr, rnglists_h
return (uintptr_t)ptr->low_pc;
}
}
- return false;
+ return 0;
}
#if 0
static void
-ranges_inspect(DebugInfoReader *reader, ranges_t *ptr)
+ranges_inspect(DebugInfoReader *reader, ranges_t *ptr, FILE *errout)
{
if (ptr->high_pc_set) {
if (ptr->ranges_set || !ptr->low_pc_set) {
- fprintf(stderr,"low_pc_set:%d high_pc_set:%d ranges_set:%d\n",ptr->low_pc_set,ptr->high_pc_set,ptr->ranges_set);
- exit(1);
+ kprintf("low_pc_set:%d high_pc_set:%d ranges_set:%d\n",ptr->low_pc_set,ptr->high_pc_set,ptr->ranges_set);
+ return;
}
- fprintf(stderr,"low_pc:%"PRIx64" high_pc:%"PRIx64"\n",ptr->low_pc,ptr->high_pc);
+ kprintf("low_pc:%"PRIx64" high_pc:%"PRIx64"\n",ptr->low_pc,ptr->high_pc);
}
else if (ptr->ranges_set) {
char *p = reader->obj->debug_ranges.ptr + ptr->ranges;
- fprintf(stderr,"low_pc:%"PRIx64" ranges:%"PRIx64" %lx ",ptr->low_pc,ptr->ranges, p-reader->obj->mapped);
+ kprintf("low_pc:%"PRIx64" ranges:%"PRIx64" %lx ",ptr->low_pc,ptr->ranges, p-reader->obj->mapped);
for (;;) {
uintptr_t from = read_uintptr(&p);
uintptr_t to = read_uintptr(&p);
if (!from && !to) break;
- fprintf(stderr,"%"PRIx64"-%"PRIx64" ",ptr->low_pc+from,ptr->low_pc+to);
+ kprintf("%"PRIx64"-%"PRIx64" ",ptr->low_pc+from,ptr->low_pc+to);
}
- fprintf(stderr,"\n");
+ kprintf("\n");
}
else if (ptr->low_pc_set) {
- fprintf(stderr,"low_pc:%"PRIx64"\n",ptr->low_pc);
+ kprintf("low_pc:%"PRIx64"\n",ptr->low_pc);
}
else {
- fprintf(stderr,"empty\n");
+ kprintf("empty\n");
}
}
#endif
static int
-di_read_cu(DebugInfoReader *reader)
+di_read_cu(DebugInfoReader *reader, FILE *errout)
{
uint64_t unit_length;
uint16_t version;
@@ -1731,19 +1773,23 @@ di_read_cu(DebugInfoReader *reader)
debug_abbrev_offset = read_uint(reader);
reader->address_size = read_uint8(&reader->p);
}
+ if (reader->address_size != 4 && reader->address_size != 8) {
+ kprintf("unknown address_size:%d", reader->address_size);
+ return -1;
+ }
reader->q0 = reader->obj->debug_abbrev.ptr + debug_abbrev_offset;
reader->level = 0;
di_read_debug_abbrev_cu(reader);
- if (di_read_debug_line_cu(reader)) return -1;
+ if (di_read_debug_line_cu(reader, errout)) return -1;
do {
DIE die;
- if (!di_read_die(reader, &die)) continue;
+ if (!di_read_die(reader, &die, errout)) continue;
if (die.tag != DW_TAG_compile_unit) {
- di_skip_records(reader);
+ if (!di_skip_records(reader, errout)) return -1;
break;
}
@@ -1751,11 +1797,11 @@ di_read_cu(DebugInfoReader *reader)
reader->current_addr_base = 0;
reader->current_rnglists_base = 0;
- DebugInfoValue low_pc = {{}};
+ DebugInfoValue low_pc = {{0}};
/* enumerate abbrev */
for (;;) {
- DebugInfoValue v = {{}};
- if (!di_read_record(reader, &v)) break;
+ DebugInfoValue v = {{0}};
+ if (!di_read_record(reader, &v, errout)) break;
switch (v.at) {
case DW_AT_low_pc:
// clang may output DW_AT_addr_base after DW_AT_low_pc.
@@ -1780,8 +1826,8 @@ di_read_cu(DebugInfoReader *reader)
break;
case VAL_addr:
{
- addr_header_t header = {};
- addr_header_init(reader->obj, &header);
+ addr_header_t header = {0};
+ if (!addr_header_init(reader->obj, &header, errout)) return -1;
reader->current_low_pc = read_addr(&header, reader->current_addr_base, low_pc.as.addr_idx);
}
break;
@@ -1792,7 +1838,7 @@ di_read_cu(DebugInfoReader *reader)
}
static void
-read_abstract_origin(DebugInfoReader *reader, uint64_t form, uint64_t abstract_origin, line_info_t *line)
+read_abstract_origin(DebugInfoReader *reader, uint64_t form, uint64_t abstract_origin, line_info_t *line, FILE *errout)
{
const char *p = reader->p;
const char *q = reader->q;
@@ -1817,12 +1863,12 @@ read_abstract_origin(DebugInfoReader *reader, uint64_t form, uint64_t abstract_o
default:
goto finish;
}
- if (!di_read_die(reader, &die)) goto finish;
+ if (!di_read_die(reader, &die, errout)) goto finish;
/* enumerate abbrev */
for (;;) {
- DebugInfoValue v = {{}};
- if (!di_read_record(reader, &v)) break;
+ DebugInfoValue v = {{0}};
+ if (!di_read_record(reader, &v, errout)) break;
switch (v.at) {
case DW_AT_name:
line->sname = get_cstr_value(&v);
@@ -1836,43 +1882,44 @@ read_abstract_origin(DebugInfoReader *reader, uint64_t form, uint64_t abstract_o
reader->level = level;
}
-static void
+static bool
debug_info_read(DebugInfoReader *reader, int num_traces, void **traces,
- line_info_t *lines, int offset) {
+ line_info_t *lines, int offset, FILE *errout)
+{
- addr_header_t addr_header = {};
- addr_header_init(reader->obj, &addr_header);
+ addr_header_t addr_header = {0};
+ if (!addr_header_init(reader->obj, &addr_header, errout)) return false;
- rnglists_header_t rnglists_header = {};
- rnglists_header_init(reader->obj, &rnglists_header);
+ rnglists_header_t rnglists_header = {0};
+ if (!rnglists_header_init(reader->obj, &rnglists_header, errout)) return false;
while (reader->p < reader->cu_end) {
DIE die;
- ranges_t ranges = {};
- line_info_t line = {};
+ ranges_t ranges = {0};
+ line_info_t line = {0};
- if (!di_read_die(reader, &die)) continue;
- /* fprintf(stderr,"%d:%tx: <%d>\n",__LINE__,die.pos,reader->level,die.tag); */
+ if (!di_read_die(reader, &die, errout)) continue;
+ /* kprintf("%d:%tx: <%d>\n",__LINE__,die.pos,reader->level,die.tag); */
if (die.tag != DW_TAG_subprogram && die.tag != DW_TAG_inlined_subroutine) {
skip_die:
- di_skip_records(reader);
+ if (!di_skip_records(reader, errout)) return false;
continue;
}
/* enumerate abbrev */
for (;;) {
- DebugInfoValue v = {{}};
+ DebugInfoValue v = {{0}};
/* ptrdiff_t pos = reader->p - reader->p0; */
- if (!di_read_record(reader, &v)) break;
- /* fprintf(stderr,"\n%d:%tx: AT:%lx FORM:%lx\n",__LINE__,pos,v.at,v.form); */
- /* div_inspect(&v); */
+ if (!di_read_record(reader, &v, errout)) break;
+ /* kprintf("\n%d:%tx: AT:%lx FORM:%lx\n",__LINE__,pos,v.at,v.form); */
+ /* div_inspect(&v, errout); */
switch (v.at) {
case DW_AT_name:
line.sname = get_cstr_value(&v);
break;
case DW_AT_call_file:
- fill_filename((int)v.as.uint64, reader->debug_line_format, reader->debug_line_version, reader->debug_line_directories, reader->debug_line_files, &line, reader->obj);
+ fill_filename((int)v.as.uint64, reader->debug_line_format, reader->debug_line_version, reader->debug_line_directories, reader->debug_line_files, &line, reader->obj, errout);
break;
case DW_AT_call_line:
line.line = (int)v.as.uint64;
@@ -1888,18 +1935,19 @@ debug_info_read(DebugInfoReader *reader, int num_traces, void **traces,
/* 1 or 3 */
break; /* goto skip_die; */
case DW_AT_abstract_origin:
- read_abstract_origin(reader, v.form, v.as.uint64, &line);
+ read_abstract_origin(reader, v.form, v.as.uint64, &line, errout);
break; /* goto skip_die; */
}
}
- /* ranges_inspect(reader, &ranges); */
- /* fprintf(stderr,"%d:%tx: %x ",__LINE__,diepos,die.tag); */
+ /* ranges_inspect(reader, &ranges, errout); */
+ /* kprintf("%d:%tx: %x ",__LINE__,diepos,die.tag); */
for (int i=offset; i < num_traces; i++) {
uintptr_t addr = (uintptr_t)traces[i];
uintptr_t offset = addr - reader->obj->base_addr + reader->obj->vmaddr;
- uintptr_t saddr = ranges_include(reader, &ranges, offset, &rnglists_header);
+ uintptr_t saddr = ranges_include(reader, &ranges, offset, &rnglists_header, errout);
+ if (saddr == UINTPTR_MAX) return false;
if (saddr) {
- /* fprintf(stdout, "%d:%tx: %d %lx->%lx %x %s: %s/%s %d %s %s %s\n",__LINE__,die.pos, i,addr,offset, die.tag,line.sname,line.dirname,line.filename,line.line,reader->obj->path,line.sname,lines[i].sname); */
+ /* kprintf("%d:%tx: %d %lx->%lx %x %s: %s/%s %d %s %s %s\n",__LINE__,die.pos, i,addr,offset, die.tag,line.sname,line.dirname,line.filename,line.line,reader->obj->path,line.sname,lines[i].sname); */
if (lines[i].sname) {
line_info_t *lp = malloc(sizeof(line_info_t));
memcpy(lp, &lines[i], sizeof(line_info_t));
@@ -1916,6 +1964,7 @@ debug_info_read(DebugInfoReader *reader, int num_traces, void **traces,
}
}
}
+ return true;
}
// This function parses the following attributes of Line Number Program Header in DWARF 5:
@@ -1934,7 +1983,10 @@ debug_info_read(DebugInfoReader *reader, int num_traces, void **traces,
//
// It records DW_LNCT_path and DW_LNCT_directory_index at the index "idx".
static const char *
-parse_ver5_debug_line_header(const char *p, int idx, uint8_t format, obj_info_t *obj, const char **out_path, uint64_t *out_directory_index) {
+parse_ver5_debug_line_header(const char *p, int idx, uint8_t format,
+ obj_info_t *obj, const char **out_path,
+ uint64_t *out_directory_index, FILE *errout)
+{
int i, j;
int entry_format_count = *(uint8_t *)p++;
const char *entry_format = p;
@@ -1944,17 +1996,17 @@ parse_ver5_debug_line_header(const char *p, int idx, uint8_t format, obj_info_t
int entry_count = (int)uleb128(&p);
- DebugInfoReader reader;
+ DebugInfoReader reader = {0};
debug_info_reader_init(&reader, obj);
reader.format = format;
reader.p = p;
for (j = 0; j < entry_count; j++) {
const char *format = entry_format;
for (i = 0; i < entry_format_count; i++) {
- DebugInfoValue v = {{}};
+ DebugInfoValue v = {{0}};
unsigned long dw_lnct = uleb128(&format);
unsigned long dw_form = uleb128(&format);
- debug_info_reader_read_value(&reader, dw_form, &v);
+ if (!debug_info_reader_read_value(&reader, dw_form, &v, errout)) return 0;
if (dw_lnct == 1 /* DW_LNCT_path */ && v.type == VAL_cstr && out_path)
*out_path = v.as.ptr + v.off;
if (dw_lnct == 2 /* DW_LNCT_directory_index */ && v.type == VAL_uint && out_directory_index)
@@ -1999,7 +2051,7 @@ fail:
/* read file and fill lines */
static uintptr_t
fill_lines(int num_traces, void **traces, int check_debuglink,
- obj_info_t **objp, line_info_t *lines, int offset)
+ obj_info_t **objp, line_info_t *lines, int offset, FILE *errout)
{
int i, j;
char *shstr;
@@ -2159,9 +2211,10 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
debug_info_reader_init(&reader, obj);
i = 0;
while (reader.p < reader.pend) {
- /* fprintf(stderr, "%d:%tx: CU[%d]\n", __LINE__, reader.p - reader.obj->debug_info.ptr, i++); */
- if (di_read_cu(&reader)) goto use_symtab;
- debug_info_read(&reader, num_traces, traces, lines, offset);
+ /* kprintf("%d:%tx: CU[%d]\n", __LINE__, reader.p - reader.obj->debug_info.ptr, i++); */
+ if (di_read_cu(&reader, errout)) goto use_symtab;
+ if (!debug_info_read(&reader, num_traces, traces, lines, offset, errout))
+ goto use_symtab;
}
}
else {
@@ -2201,14 +2254,14 @@ use_symtab:
if (gnu_debuglink_shdr && check_debuglink) {
follow_debuglink(file + gnu_debuglink_shdr->sh_offset,
num_traces, traces,
- objp, lines, offset);
+ objp, lines, offset, errout);
}
if (note_gnu_build_id && check_debuglink) {
ElfW(Nhdr) *nhdr = (ElfW(Nhdr)*) (file + note_gnu_build_id->sh_offset);
const char *build_id = (char *)(nhdr + 1) + nhdr->n_namesz;
follow_debuglink_build_id(build_id, nhdr->n_descsz,
num_traces, traces,
- objp, lines, offset);
+ objp, lines, offset, errout);
}
goto finish;
}
@@ -2216,7 +2269,7 @@ use_symtab:
if (parse_debug_line(num_traces, traces,
obj->debug_line.ptr,
obj->debug_line.size,
- obj, lines, offset) == -1)
+ obj, lines, offset, errout) == -1)
goto fail;
finish:
@@ -2228,7 +2281,7 @@ fail:
/* read file and fill lines */
static uintptr_t
fill_lines(int num_traces, void **traces, int check_debuglink,
- obj_info_t **objp, line_info_t *lines, int offset)
+ obj_info_t **objp, line_info_t *lines, int offset, FILE *errout)
{
# ifdef __LP64__
# define LP(x) x##_64
@@ -2307,13 +2360,13 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
struct fat_header *fat = (struct fat_header *)file;
char *q = file + sizeof(*fat);
uint32_t nfat_arch = __builtin_bswap32(fat->nfat_arch);
- /* fprintf(stderr,"%d: fat:%s %d\n",__LINE__, binary_filename,nfat_arch); */
+ /* kprintf("%d: fat:%s %d\n",__LINE__, binary_filename,nfat_arch); */
for (uint32_t i = 0; i < nfat_arch; i++) {
struct fat_arch *arch = (struct fat_arch *)q;
cpu_type_t cputype = __builtin_bswap32(arch->cputype);
cpu_subtype_t cpusubtype = __builtin_bswap32(arch->cpusubtype);
uint32_t offset = __builtin_bswap32(arch->offset);
- /* fprintf(stderr,"%d: fat %d %x/%x %x/%x\n",__LINE__, i, mhp->cputype,mhp->cpusubtype, cputype,cpusubtype); */
+ /* kprintf("%d: fat %d %x/%x %x/%x\n",__LINE__, i, mhp->cputype,mhp->cpusubtype, cputype,cpusubtype); */
if (mhp->cputype == cputype &&
(cpu_subtype_t)(mhp->cpusubtype & ~CPU_SUBTYPE_MASK) == cpusubtype) {
p = file + offset;
@@ -2331,13 +2384,14 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
goto fail;
}
else {
- kprintf("'%s' is not a "
# ifdef __LP64__
- "64"
+# define bitsize "64"
# else
- "32"
+# define bitsize "32"
# endif
+ kprintf("'%s' is not a " bitsize
"-bit Mach-O file!\n",binary_filename);
+# undef bitsize
close(fd);
goto fail;
}
@@ -2429,15 +2483,16 @@ found_mach_header:
DebugInfoReader reader;
debug_info_reader_init(&reader, obj);
while (reader.p < reader.pend) {
- if (di_read_cu(&reader)) goto fail;
- debug_info_read(&reader, num_traces, traces, lines, offset);
+ if (di_read_cu(&reader, errout)) goto fail;
+ if (!debug_info_read(&reader, num_traces, traces, lines, offset, errout))
+ goto fail;
}
}
if (parse_debug_line(num_traces, traces,
obj->debug_line.ptr,
obj->debug_line.size,
- obj, lines, offset) == -1)
+ obj, lines, offset, errout) == -1)
goto fail;
return dladdr_fbase;
@@ -2450,7 +2505,7 @@ fail:
#if defined(__FreeBSD__) || defined(__DragonFly__)
# include <sys/sysctl.h>
#endif
-/* ssize_t main_exe_path(void)
+/* ssize_t main_exe_path(FILE *errout)
*
* store the path of the main executable to `binary_filename`,
* and returns strlen(binary_filename).
@@ -2458,7 +2513,7 @@ fail:
*/
#if defined(__linux__) || defined(__NetBSD__)
static ssize_t
-main_exe_path(void)
+main_exe_path(FILE *errout)
{
# if defined(__linux__)
# define PROC_SELF_EXE "/proc/self/exe"
@@ -2472,7 +2527,7 @@ main_exe_path(void)
}
#elif defined(__FreeBSD__) || defined(__DragonFly__)
static ssize_t
-main_exe_path(void)
+main_exe_path(FILE *errout)
{
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
size_t len = PATH_MAX;
@@ -2486,7 +2541,7 @@ main_exe_path(void)
}
#elif defined(HAVE_LIBPROC_H)
static ssize_t
-main_exe_path(void)
+main_exe_path(FILE *errout)
{
int len = proc_pidpath(getpid(), binary_filename, PATH_MAX);
if (len == 0) return 0;
@@ -2498,7 +2553,7 @@ main_exe_path(void)
#endif
static void
-print_line0(line_info_t *line, void *address)
+print_line0(line_info_t *line, void *address, FILE *errout)
{
uintptr_t addr = (uintptr_t)address;
uintptr_t d = addr - line->saddr;
@@ -2539,16 +2594,16 @@ print_line0(line_info_t *line, void *address)
}
static void
-print_line(line_info_t *line, void *address)
+print_line(line_info_t *line, void *address, FILE *errout)
{
- print_line0(line, address);
+ print_line0(line, address, errout);
if (line->next) {
- print_line(line->next, NULL);
+ print_line(line->next, NULL, errout);
}
}
void
-rb_dump_backtrace_with_lines(int num_traces, void **traces)
+rb_dump_backtrace_with_lines(int num_traces, void **traces, FILE *errout)
{
int i;
/* async-signal unsafe */
@@ -2560,14 +2615,14 @@ rb_dump_backtrace_with_lines(int num_traces, void **traces)
#ifdef HAVE_MAIN_EXE_PATH
char *main_path = NULL; /* used on printing backtrace */
ssize_t len;
- if ((len = main_exe_path()) > 0) {
+ if ((len = main_exe_path(errout)) > 0) {
main_path = (char *)alloca(len + 1);
if (main_path) {
uintptr_t addr;
memcpy(main_path, binary_filename, len+1);
append_obj(&obj);
obj->path = main_path;
- addr = fill_lines(num_traces, traces, 1, &obj, lines, -1);
+ addr = fill_lines(num_traces, traces, 1, &obj, lines, -1, errout);
if (addr != (uintptr_t)-1) {
dladdr_fbases[0] = (void *)addr;
}
@@ -2604,7 +2659,7 @@ rb_dump_backtrace_with_lines(int num_traces, void **traces)
lines[i].saddr = (uintptr_t)info.dli_saddr;
}
strlcpy(binary_filename, path, PATH_MAX);
- if (fill_lines(num_traces, traces, 1, &obj, lines, i) == (uintptr_t)-1)
+ if (fill_lines(num_traces, traces, 1, &obj, lines, i, errout) == (uintptr_t)-1)
break;
}
next_line:
@@ -2613,7 +2668,7 @@ next_line:
/* output */
for (i = 0; i < num_traces; i++) {
- print_line(&lines[i], traces[i]);
+ print_line(&lines[i], traces[i], errout);
/* FreeBSD's backtrace may show _start and so on */
if (lines[i].sname && strcmp("main", lines[i].sname) == 0)
@@ -2647,435 +2702,8 @@ next_line:
free(dladdr_fbases);
}
-/* From FreeBSD's lib/libstand/printf.c */
-/*-
- * Copyright (c) 1986, 1988, 1991, 1993
- * The Regents of the University of California. All rights reserved.
- * (c) UNIX System Laboratories, Inc.
- * All or some portions of this file are derived from material licensed
- * to the University of California by American Telephone and Telegraph
- * Co. or Unix System Laboratories, Inc. and are reproduced herein with
- * the permission of UNIX System Laboratories, Inc.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * @(#)subr_prf.c 8.3 (Berkeley) 1/21/94
- */
-
-#include <stdarg.h>
-#define MAXNBUF (sizeof(intmax_t) * CHAR_BIT + 1)
-static inline int toupper(int c) { return ('A' <= c && c <= 'Z') ? (c&0x5f) : c; }
-#define hex2ascii(hex) (hex2ascii_data[hex])
-static const char hex2ascii_data[] = "0123456789abcdefghijklmnopqrstuvwxyz";
-static inline int imax(int a, int b) { return (a > b ? a : b); }
-static int kvprintf(char const *fmt, void (*func)(int), void *arg, int radix, va_list ap);
-
-static void putce(int c)
-{
- char s[1];
- ssize_t ret;
-
- s[0] = (char)c;
- ret = write(2, s, 1);
- (void)ret;
-}
-
-static int
-kprintf(const char *fmt, ...)
-{
- va_list ap;
- int retval;
+#undef kprintf
- va_start(ap, fmt);
- retval = kvprintf(fmt, putce, NULL, 10, ap);
- va_end(ap);
- return retval;
-}
-
-/*
- * Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse
- * order; return an optional length and a pointer to the last character
- * written in the buffer (i.e., the first character of the string).
- * The buffer pointed to by `nbuf' must have length >= MAXNBUF.
- */
-static char *
-ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
-{
- char *p, c;
-
- p = nbuf;
- *p = '\0';
- do {
- c = hex2ascii(num % base);
- *++p = upper ? toupper(c) : c;
- } while (num /= base);
- if (lenp)
- *lenp = (int)(p - nbuf);
- return (p);
-}
-
-/*
- * Scaled down version of printf(3).
- *
- * Two additional formats:
- *
- * The format %b is supported to decode error registers.
- * Its usage is:
- *
- * printf("reg=%b\n", regval, "<base><arg>*");
- *
- * where <base> is the output base expressed as a control character, e.g.
- * \10 gives octal; \20 gives hex. Each arg is a sequence of characters,
- * the first of which gives the bit number to be inspected (origin 1), and
- * the next characters (up to a control character, i.e. a character <= 32),
- * give the name of the register. Thus:
- *
- * kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n");
- *
- * would produce output:
- *
- * reg=3<BITTWO,BITONE>
- *
- * XXX: %D -- Hexdump, takes pointer and separator string:
- * ("%6D", ptr, ":") -> XX:XX:XX:XX:XX:XX
- * ("%*D", len, ptr, " " -> XX XX XX XX ...
- */
-static int
-kvprintf(char const *fmt, void (*func)(int), void *arg, int radix, va_list ap)
-{
-#define PCHAR(c) {int cc=(c); if (func) (*func)(cc); else *d++ = cc; retval++; }
- char nbuf[MAXNBUF];
- char *d;
- const char *p, *percent, *q;
- unsigned char *up;
- int ch, n;
- uintmax_t num;
- int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot;
- int cflag, hflag, jflag, tflag, zflag;
- int dwidth, upper;
- char padc;
- int stop = 0, retval = 0;
-
- num = 0;
- if (!func)
- d = (char *) arg;
- else
- d = NULL;
-
- if (fmt == NULL)
- fmt = "(fmt null)\n";
-
- if (radix < 2 || radix > 36)
- radix = 10;
-
- for (;;) {
- padc = ' ';
- width = 0;
- while ((ch = (unsigned char)*fmt++) != '%' || stop) {
- if (ch == '\0')
- return (retval);
- PCHAR(ch);
- }
- percent = fmt - 1;
- qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0;
- sign = 0; dot = 0; dwidth = 0; upper = 0;
- cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0;
-reswitch: switch (ch = (unsigned char)*fmt++) {
- case '.':
- dot = 1;
- goto reswitch;
- case '#':
- sharpflag = 1;
- goto reswitch;
- case '+':
- sign = 1;
- goto reswitch;
- case '-':
- ladjust = 1;
- goto reswitch;
- case '%':
- PCHAR(ch);
- break;
- case '*':
- if (!dot) {
- width = va_arg(ap, int);
- if (width < 0) {
- ladjust = !ladjust;
- width = -width;
- }
- } else {
- dwidth = va_arg(ap, int);
- }
- goto reswitch;
- case '0':
- if (!dot) {
- padc = '0';
- goto reswitch;
- }
- case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- for (n = 0;; ++fmt) {
- n = n * 10 + ch - '0';
- ch = *fmt;
- if (ch < '0' || ch > '9')
- break;
- }
- if (dot)
- dwidth = n;
- else
- width = n;
- goto reswitch;
- case 'b':
- num = (unsigned int)va_arg(ap, int);
- p = va_arg(ap, char *);
- for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;)
- PCHAR(*q--);
-
- if (num == 0)
- break;
-
- for (tmp = 0; *p;) {
- n = *p++;
- if (num & (1 << (n - 1))) {
- PCHAR(tmp ? ',' : '<');
- for (; (n = *p) > ' '; ++p)
- PCHAR(n);
- tmp = 1;
- } else
- for (; *p > ' '; ++p)
- continue;
- }
- if (tmp)
- PCHAR('>');
- break;
- case 'c':
- PCHAR(va_arg(ap, int));
- break;
- case 'D':
- up = va_arg(ap, unsigned char *);
- p = va_arg(ap, char *);
- if (!width)
- width = 16;
- while(width--) {
- PCHAR(hex2ascii(*up >> 4));
- PCHAR(hex2ascii(*up & 0x0f));
- up++;
- if (width)
- for (q=p;*q;q++)
- PCHAR(*q);
- }
- break;
- case 'd':
- case 'i':
- base = 10;
- sign = 1;
- goto handle_sign;
- case 'h':
- if (hflag) {
- hflag = 0;
- cflag = 1;
- } else
- hflag = 1;
- goto reswitch;
- case 'j':
- jflag = 1;
- goto reswitch;
- case 'l':
- if (lflag) {
- lflag = 0;
- qflag = 1;
- } else
- lflag = 1;
- goto reswitch;
- case 'n':
- if (jflag)
- *(va_arg(ap, intmax_t *)) = retval;
- else if (qflag)
- *(va_arg(ap, int64_t *)) = retval;
- else if (lflag)
- *(va_arg(ap, long *)) = retval;
- else if (zflag)
- *(va_arg(ap, size_t *)) = retval;
- else if (hflag)
- *(va_arg(ap, short *)) = retval;
- else if (cflag)
- *(va_arg(ap, char *)) = retval;
- else
- *(va_arg(ap, int *)) = retval;
- break;
- case 'o':
- base = 8;
- goto handle_nosign;
- case 'p':
- base = 16;
- sharpflag = (width == 0);
- sign = 0;
- num = (uintptr_t)va_arg(ap, void *);
- goto number;
- case 'q':
- qflag = 1;
- goto reswitch;
- case 'r':
- base = radix;
- if (sign)
- goto handle_sign;
- goto handle_nosign;
- case 's':
- p = va_arg(ap, char *);
- if (p == NULL)
- p = "(null)";
- if (!dot)
- n = (int)strlen (p);
- else
- for (n = 0; n < dwidth && p[n]; n++)
- continue;
-
- width -= n;
-
- if (!ladjust && width > 0)
- while (width--)
- PCHAR(padc);
- while (n--)
- PCHAR(*p++);
- if (ladjust && width > 0)
- while (width--)
- PCHAR(padc);
- break;
- case 't':
- tflag = 1;
- goto reswitch;
- case 'u':
- base = 10;
- goto handle_nosign;
- case 'X':
- upper = 1;
- case 'x':
- base = 16;
- goto handle_nosign;
- case 'y':
- base = 16;
- sign = 1;
- goto handle_sign;
- case 'z':
- zflag = 1;
- goto reswitch;
-handle_nosign:
- sign = 0;
- if (jflag)
- num = va_arg(ap, uintmax_t);
- else if (qflag)
- num = va_arg(ap, uint64_t);
- else if (tflag)
- num = va_arg(ap, ptrdiff_t);
- else if (lflag)
- num = va_arg(ap, unsigned long);
- else if (zflag)
- num = va_arg(ap, size_t);
- else if (hflag)
- num = (unsigned short)va_arg(ap, int);
- else if (cflag)
- num = (unsigned char)va_arg(ap, int);
- else
- num = va_arg(ap, unsigned int);
- goto number;
-handle_sign:
- if (jflag)
- num = va_arg(ap, intmax_t);
- else if (qflag)
- num = va_arg(ap, int64_t);
- else if (tflag)
- num = va_arg(ap, ptrdiff_t);
- else if (lflag)
- num = va_arg(ap, long);
- else if (zflag)
- num = va_arg(ap, ssize_t);
- else if (hflag)
- num = (short)va_arg(ap, int);
- else if (cflag)
- num = (char)va_arg(ap, int);
- else
- num = va_arg(ap, int);
-number:
- if (sign && (intmax_t)num < 0) {
- neg = 1;
- num = -(intmax_t)num;
- }
- p = ksprintn(nbuf, num, base, &n, upper);
- tmp = 0;
- if (sharpflag && num != 0) {
- if (base == 8)
- tmp++;
- else if (base == 16)
- tmp += 2;
- }
- if (neg)
- tmp++;
-
- if (!ladjust && padc == '0')
- dwidth = width - tmp;
- width -= tmp + imax(dwidth, n);
- dwidth -= n;
- if (!ladjust)
- while (width-- > 0)
- PCHAR(' ');
- if (neg)
- PCHAR('-');
- if (sharpflag && num != 0) {
- if (base == 8) {
- PCHAR('0');
- } else if (base == 16) {
- PCHAR('0');
- PCHAR('x');
- }
- }
- while (dwidth-- > 0)
- PCHAR('0');
-
- while (*p)
- PCHAR(*p--);
-
- if (ladjust)
- while (width-- > 0)
- PCHAR(' ');
-
- break;
- default:
- while (percent < fmt)
- PCHAR(*percent++);
- /*
- * Since we ignore an formatting argument it is no
- * longer safe to obey the remaining formatting
- * arguments as the arguments will no longer match
- * the format specs.
- */
- stop = 1;
- break;
- }
- }
-#undef PCHAR
-}
#else /* defined(USE_ELF) */
#error not supported
#endif
diff --git a/addr2line.h b/addr2line.h
index f09b665800..ff8e476b92 100644
--- a/addr2line.h
+++ b/addr2line.h
@@ -12,8 +12,10 @@
#if (defined(USE_ELF) || defined(HAVE_MACH_O_LOADER_H))
+#include <stdio.h>
+
void
-rb_dump_backtrace_with_lines(int num_traces, void **traces);
+rb_dump_backtrace_with_lines(int num_traces, void **traces, FILE *errout);
#endif /* USE_ELF */
diff --git a/array.c b/array.c
index a99fd9c73d..56f3584e1d 100644
--- a/array.c
+++ b/array.c
@@ -28,7 +28,7 @@
#include "ruby/encoding.h"
#include "ruby/st.h"
#include "ruby/util.h"
-#include "transient_heap.h"
+#include "vm_core.h"
#include "builtin.h"
#if !ARRAY_DEBUG
@@ -58,8 +58,6 @@ VALUE rb_cArray;
* they cannot be modified. Not updating the reference count
* improves copy-on-write performance. Their reference count is
* assumed to be infinity.
- * 13: RARRAY_TRANSIENT_FLAG
- * The buffer of the array is allocated on the transient heap.
* 14: RARRAY_PTR_IN_USE_FLAG
* The buffer of the array is in use. This is only used during
* debugging.
@@ -79,48 +77,47 @@ should_be_T_ARRAY(VALUE ary)
return RB_TYPE_P(ary, T_ARRAY);
}
-#define ARY_HEAP_PTR(a) (assert(!ARY_EMBED_P(a)), RARRAY(a)->as.heap.ptr)
-#define ARY_HEAP_LEN(a) (assert(!ARY_EMBED_P(a)), RARRAY(a)->as.heap.len)
-#define ARY_HEAP_CAPA(a) (assert(!ARY_EMBED_P(a)), assert(!ARY_SHARED_ROOT_P(a)), \
+#define ARY_HEAP_PTR(a) (RUBY_ASSERT(!ARY_EMBED_P(a)), RARRAY(a)->as.heap.ptr)
+#define ARY_HEAP_LEN(a) (RUBY_ASSERT(!ARY_EMBED_P(a)), RARRAY(a)->as.heap.len)
+#define ARY_HEAP_CAPA(a) (RUBY_ASSERT(!ARY_EMBED_P(a)), RUBY_ASSERT(!ARY_SHARED_ROOT_P(a)), \
RARRAY(a)->as.heap.aux.capa)
-#define ARY_EMBED_PTR(a) (assert(ARY_EMBED_P(a)), RARRAY(a)->as.ary)
+#define ARY_EMBED_PTR(a) (RUBY_ASSERT(ARY_EMBED_P(a)), RARRAY(a)->as.ary)
#define ARY_EMBED_LEN(a) \
- (assert(ARY_EMBED_P(a)), \
+ (RUBY_ASSERT(ARY_EMBED_P(a)), \
(long)((RBASIC(a)->flags >> RARRAY_EMBED_LEN_SHIFT) & \
(RARRAY_EMBED_LEN_MASK >> RARRAY_EMBED_LEN_SHIFT)))
-#define ARY_HEAP_SIZE(a) (assert(!ARY_EMBED_P(a)), assert(ARY_OWNS_HEAP_P(a)), ARY_CAPA(a) * sizeof(VALUE))
+#define ARY_HEAP_SIZE(a) (RUBY_ASSERT(!ARY_EMBED_P(a)), RUBY_ASSERT(ARY_OWNS_HEAP_P(a)), ARY_CAPA(a) * sizeof(VALUE))
-#define ARY_OWNS_HEAP_P(a) (assert(should_be_T_ARRAY((VALUE)(a))), \
+#define ARY_OWNS_HEAP_P(a) (RUBY_ASSERT(should_be_T_ARRAY((VALUE)(a))), \
!FL_TEST_RAW((a), RARRAY_SHARED_FLAG|RARRAY_EMBED_FLAG))
#define FL_SET_EMBED(a) do { \
- assert(!ARY_SHARED_P(a)); \
+ RUBY_ASSERT(!ARY_SHARED_P(a)); \
FL_SET((a), RARRAY_EMBED_FLAG); \
- RARY_TRANSIENT_UNSET(a); \
ary_verify(a); \
} while (0)
#define FL_UNSET_EMBED(ary) FL_UNSET((ary), RARRAY_EMBED_FLAG|RARRAY_EMBED_LEN_MASK)
#define FL_SET_SHARED(ary) do { \
- assert(!ARY_EMBED_P(ary)); \
+ RUBY_ASSERT(!ARY_EMBED_P(ary)); \
FL_SET((ary), RARRAY_SHARED_FLAG); \
} while (0)
#define FL_UNSET_SHARED(ary) FL_UNSET((ary), RARRAY_SHARED_FLAG)
#define ARY_SET_PTR(ary, p) do { \
- assert(!ARY_EMBED_P(ary)); \
- assert(!OBJ_FROZEN(ary)); \
+ RUBY_ASSERT(!ARY_EMBED_P(ary)); \
+ RUBY_ASSERT(!OBJ_FROZEN(ary)); \
RARRAY(ary)->as.heap.ptr = (p); \
} while (0)
#define ARY_SET_EMBED_LEN(ary, n) do { \
long tmp_n = (n); \
- assert(ARY_EMBED_P(ary)); \
+ RUBY_ASSERT(ARY_EMBED_P(ary)); \
RBASIC(ary)->flags &= ~RARRAY_EMBED_LEN_MASK; \
RBASIC(ary)->flags |= (tmp_n) << RARRAY_EMBED_LEN_SHIFT; \
} while (0)
#define ARY_SET_HEAP_LEN(ary, n) do { \
- assert(!ARY_EMBED_P(ary)); \
+ RUBY_ASSERT(!ARY_EMBED_P(ary)); \
RARRAY(ary)->as.heap.len = (n); \
} while (0)
#define ARY_SET_LEN(ary, n) do { \
@@ -130,15 +127,15 @@ should_be_T_ARRAY(VALUE ary)
else { \
ARY_SET_HEAP_LEN((ary), (n)); \
} \
- assert(RARRAY_LEN(ary) == (n)); \
+ RUBY_ASSERT(RARRAY_LEN(ary) == (n)); \
} while (0)
#define ARY_INCREASE_PTR(ary, n) do { \
- assert(!ARY_EMBED_P(ary)); \
- assert(!OBJ_FROZEN(ary)); \
+ RUBY_ASSERT(!ARY_EMBED_P(ary)); \
+ RUBY_ASSERT(!OBJ_FROZEN(ary)); \
RARRAY(ary)->as.heap.ptr += (n); \
} while (0)
#define ARY_INCREASE_LEN(ary, n) do { \
- assert(!OBJ_FROZEN(ary)); \
+ RUBY_ASSERT(!OBJ_FROZEN(ary)); \
if (ARY_EMBED_P(ary)) { \
ARY_SET_EMBED_LEN((ary), RARRAY_LEN(ary)+(n)); \
} \
@@ -150,31 +147,30 @@ should_be_T_ARRAY(VALUE ary)
#define ARY_CAPA(ary) (ARY_EMBED_P(ary) ? ary_embed_capa(ary) : \
ARY_SHARED_ROOT_P(ary) ? RARRAY_LEN(ary) : ARY_HEAP_CAPA(ary))
#define ARY_SET_CAPA(ary, n) do { \
- assert(!ARY_EMBED_P(ary)); \
- assert(!ARY_SHARED_P(ary)); \
- assert(!OBJ_FROZEN(ary)); \
+ RUBY_ASSERT(!ARY_EMBED_P(ary)); \
+ RUBY_ASSERT(!ARY_SHARED_P(ary)); \
+ RUBY_ASSERT(!OBJ_FROZEN(ary)); \
RARRAY(ary)->as.heap.aux.capa = (n); \
} while (0)
#define ARY_SHARED_ROOT_OCCUPIED(ary) (!OBJ_FROZEN(ary) && ARY_SHARED_ROOT_REFCNT(ary) == 1)
#define ARY_SET_SHARED_ROOT_REFCNT(ary, value) do { \
- assert(ARY_SHARED_ROOT_P(ary)); \
- assert(!OBJ_FROZEN(ary)); \
- assert((value) >= 0); \
+ RUBY_ASSERT(ARY_SHARED_ROOT_P(ary)); \
+ RUBY_ASSERT(!OBJ_FROZEN(ary)); \
+ RUBY_ASSERT((value) >= 0); \
RARRAY(ary)->as.heap.aux.capa = (value); \
} while (0)
#define FL_SET_SHARED_ROOT(ary) do { \
- assert(!OBJ_FROZEN(ary)); \
- assert(!ARY_EMBED_P(ary)); \
- assert(!RARRAY_TRANSIENT_P(ary)); \
+ RUBY_ASSERT(!OBJ_FROZEN(ary)); \
+ RUBY_ASSERT(!ARY_EMBED_P(ary)); \
FL_SET((ary), RARRAY_SHARED_ROOT_FLAG); \
} while (0)
static inline void
ARY_SET(VALUE a, long i, VALUE v)
{
- assert(!ARY_SHARED_P(a));
- assert(!OBJ_FROZEN(a));
+ RUBY_ASSERT(!ARY_SHARED_P(a));
+ RUBY_ASSERT(!OBJ_FROZEN(a));
RARRAY_ASET(a, i, v);
}
@@ -184,7 +180,7 @@ static long
ary_embed_capa(VALUE ary)
{
size_t size = rb_gc_obj_slot_size(ary) - offsetof(struct RArray, as.ary);
- assert(size % sizeof(VALUE) == 0);
+ RUBY_ASSERT(size % sizeof(VALUE) == 0);
return size / sizeof(VALUE);
}
@@ -238,25 +234,23 @@ rb_ary_size_as_embedded(VALUE ary)
static VALUE
ary_verify_(VALUE ary, const char *file, int line)
{
- assert(RB_TYPE_P(ary, T_ARRAY));
+ RUBY_ASSERT(RB_TYPE_P(ary, T_ARRAY));
if (ARY_SHARED_P(ary)) {
VALUE root = ARY_SHARED_ROOT(ary);
const VALUE *ptr = ARY_HEAP_PTR(ary);
- const VALUE *root_ptr = RARRAY_CONST_PTR_TRANSIENT(root);
+ const VALUE *root_ptr = RARRAY_CONST_PTR(root);
long len = ARY_HEAP_LEN(ary), root_len = RARRAY_LEN(root);
- assert(ARY_SHARED_ROOT_P(root) || OBJ_FROZEN(root));
- assert(root_ptr <= ptr && ptr + len <= root_ptr + root_len);
+ RUBY_ASSERT(ARY_SHARED_ROOT_P(root) || OBJ_FROZEN(root));
+ RUBY_ASSERT(root_ptr <= ptr && ptr + len <= root_ptr + root_len);
ary_verify(root);
}
else if (ARY_EMBED_P(ary)) {
- assert(!RARRAY_TRANSIENT_P(ary));
- assert(!ARY_SHARED_P(ary));
- assert(RARRAY_LEN(ary) <= ary_embed_capa(ary));
+ RUBY_ASSERT(!ARY_SHARED_P(ary));
+ RUBY_ASSERT(RARRAY_LEN(ary) <= ary_embed_capa(ary));
}
else {
-#if 1
- const VALUE *ptr = RARRAY_CONST_PTR_TRANSIENT(ary);
+ const VALUE *ptr = RARRAY_CONST_PTR(ary);
long i, len = RARRAY_LEN(ary);
volatile VALUE v;
if (len > 1) len = 1; /* check only HEAD */
@@ -264,16 +258,7 @@ ary_verify_(VALUE ary, const char *file, int line)
v = ptr[i]; /* access check */
}
v = v;
-#endif
- }
-
-#if USE_TRANSIENT_HEAP
- if (RARRAY_TRANSIENT_P(ary)) {
- assert(rb_transient_heap_managed_ptr_p(RARRAY_CONST_PTR_TRANSIENT(ary)));
}
-#endif
-
- rb_transient_heap_verify();
return ary;
}
@@ -293,7 +278,7 @@ rb_ary_ptr_use_start(VALUE ary)
#if ARRAY_DEBUG
FL_SET_RAW(ary, RARRAY_PTR_IN_USE_FLAG);
#endif
- return (VALUE *)RARRAY_CONST_PTR_TRANSIENT(ary);
+ return (VALUE *)RARRAY_CONST_PTR(ary);
}
void
@@ -315,7 +300,7 @@ rb_mem_clear(VALUE *mem, long size)
static void
ary_mem_clear(VALUE ary, long beg, long size)
{
- RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ RARRAY_PTR_USE(ary, ptr, {
rb_mem_clear(ptr + beg, size);
});
}
@@ -331,7 +316,7 @@ memfill(register VALUE *mem, register long size, register VALUE val)
static void
ary_memfill(VALUE ary, long beg, long size, VALUE val)
{
- RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ RARRAY_PTR_USE(ary, ptr, {
memfill(ptr + beg, size, val);
RB_OBJ_WRITTEN(ary, Qundef, val);
});
@@ -340,17 +325,17 @@ ary_memfill(VALUE ary, long beg, long size, VALUE val)
static void
ary_memcpy0(VALUE ary, long beg, long argc, const VALUE *argv, VALUE buff_owner_ary)
{
- assert(!ARY_SHARED_P(buff_owner_ary));
+ RUBY_ASSERT(!ARY_SHARED_P(buff_owner_ary));
if (argc > (int)(128/sizeof(VALUE)) /* is magic number (cache line size) */) {
rb_gc_writebarrier_remember(buff_owner_ary);
- RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ RARRAY_PTR_USE(ary, ptr, {
MEMCPY(ptr+beg, argv, VALUE, argc);
});
}
else {
int i;
- RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ RARRAY_PTR_USE(ary, ptr, {
for (i=0; i<argc; i++) {
RB_OBJ_WRITE(buff_owner_ary, &ptr[i+beg], argv[i]);
}
@@ -365,158 +350,61 @@ ary_memcpy(VALUE ary, long beg, long argc, const VALUE *argv)
}
static VALUE *
-ary_heap_alloc(VALUE ary, size_t capa)
+ary_heap_alloc(size_t capa)
{
- VALUE *ptr = rb_transient_heap_alloc(ary, sizeof(VALUE) * capa);
-
- if (ptr != NULL) {
- RARY_TRANSIENT_SET(ary);
- }
- else {
- RARY_TRANSIENT_UNSET(ary);
- ptr = ALLOC_N(VALUE, capa);
- }
-
- return ptr;
+ return ALLOC_N(VALUE, capa);
}
static void
ary_heap_free_ptr(VALUE ary, const VALUE *ptr, long size)
{
- if (RARRAY_TRANSIENT_P(ary)) {
- /* ignore it */
- }
- else {
- ruby_sized_xfree((void *)ptr, size);
- }
+ ruby_sized_xfree((void *)ptr, size);
}
static void
ary_heap_free(VALUE ary)
{
- if (RARRAY_TRANSIENT_P(ary)) {
- RARY_TRANSIENT_UNSET(ary);
- }
- else {
- ary_heap_free_ptr(ary, ARY_HEAP_PTR(ary), ARY_HEAP_SIZE(ary));
- }
+ ary_heap_free_ptr(ary, ARY_HEAP_PTR(ary), ARY_HEAP_SIZE(ary));
}
static size_t
ary_heap_realloc(VALUE ary, size_t new_capa)
{
- size_t alloc_capa = new_capa;
- size_t old_capa = ARY_HEAP_CAPA(ary);
-
- if (RARRAY_TRANSIENT_P(ary)) {
- if (new_capa <= old_capa) {
- /* do nothing */
- alloc_capa = old_capa;
- }
- else {
- VALUE *new_ptr = rb_transient_heap_alloc(ary, sizeof(VALUE) * new_capa);
-
- if (new_ptr == NULL) {
- new_ptr = ALLOC_N(VALUE, new_capa);
- RARY_TRANSIENT_UNSET(ary);
- }
-
- MEMCPY(new_ptr, ARY_HEAP_PTR(ary), VALUE, old_capa);
- ARY_SET_PTR(ary, new_ptr);
- }
- }
- else {
- SIZED_REALLOC_N(RARRAY(ary)->as.heap.ptr, VALUE, new_capa, old_capa);
- }
- ary_verify(ary);
-
- return alloc_capa;
-}
-
-#if USE_TRANSIENT_HEAP
-static inline void
-rb_ary_transient_heap_evacuate_(VALUE ary, int transient, int promote)
-{
- if (transient) {
- assert(!ARY_SHARED_ROOT_P(ary));
-
- VALUE *new_ptr;
- const VALUE *old_ptr = ARY_HEAP_PTR(ary);
- long capa = ARY_HEAP_CAPA(ary);
-
- assert(ARY_OWNS_HEAP_P(ary));
- assert(RARRAY_TRANSIENT_P(ary));
- assert(!ARY_PTR_USING_P(ary));
-
- if (promote) {
- new_ptr = ALLOC_N(VALUE, capa);
- RARY_TRANSIENT_UNSET(ary);
- }
- else {
- new_ptr = ary_heap_alloc(ary, capa);
- }
-
- MEMCPY(new_ptr, old_ptr, VALUE, capa);
- /* do not use ARY_SET_PTR() because they assert !frozen */
- RARRAY(ary)->as.heap.ptr = new_ptr;
- }
-
+ SIZED_REALLOC_N(RARRAY(ary)->as.heap.ptr, VALUE, new_capa, ARY_HEAP_CAPA(ary));
ary_verify(ary);
-}
-
-void
-rb_ary_transient_heap_evacuate(VALUE ary, int promote)
-{
- rb_ary_transient_heap_evacuate_(ary, RARRAY_TRANSIENT_P(ary), promote);
-}
-void
-rb_ary_detransient(VALUE ary)
-{
- assert(RARRAY_TRANSIENT_P(ary));
- rb_ary_transient_heap_evacuate_(ary, TRUE, TRUE);
-}
-#else
-void
-rb_ary_detransient(VALUE ary)
-{
- /* do nothing */
+ return new_capa;
}
-#endif
void
rb_ary_make_embedded(VALUE ary)
{
- assert(rb_ary_embeddable_p(ary));
+ RUBY_ASSERT(rb_ary_embeddable_p(ary));
if (!ARY_EMBED_P(ary)) {
const VALUE *buf = ARY_HEAP_PTR(ary);
long len = ARY_HEAP_LEN(ary);
- bool was_transient = RARRAY_TRANSIENT_P(ary);
- // FL_SET_EMBED also unsets the transient flag
FL_SET_EMBED(ary);
ARY_SET_EMBED_LEN(ary, len);
MEMCPY((void *)ARY_EMBED_PTR(ary), (void *)buf, VALUE, len);
- if (!was_transient) {
- ary_heap_free_ptr(ary, buf, len * sizeof(VALUE));
- }
+ ary_heap_free_ptr(ary, buf, len * sizeof(VALUE));
}
}
static void
ary_resize_capa(VALUE ary, long capacity)
{
- assert(RARRAY_LEN(ary) <= capacity);
- assert(!OBJ_FROZEN(ary));
- assert(!ARY_SHARED_P(ary));
+ RUBY_ASSERT(RARRAY_LEN(ary) <= capacity);
+ RUBY_ASSERT(!OBJ_FROZEN(ary));
+ RUBY_ASSERT(!ARY_SHARED_P(ary));
if (capacity > ary_embed_capa(ary)) {
size_t new_capa = capacity;
if (ARY_EMBED_P(ary)) {
long len = ARY_EMBED_LEN(ary);
- VALUE *ptr = ary_heap_alloc(ary, capacity);
+ VALUE *ptr = ary_heap_alloc(capacity);
MEMCPY(ptr, ARY_EMBED_PTR(ary), VALUE, len);
FL_UNSET_EMBED(ary);
@@ -551,8 +439,8 @@ ary_shrink_capa(VALUE ary)
{
long capacity = ARY_HEAP_LEN(ary);
long old_capa = ARY_HEAP_CAPA(ary);
- assert(!ARY_SHARED_P(ary));
- assert(old_capa >= capacity);
+ RUBY_ASSERT(!ARY_SHARED_P(ary));
+ RUBY_ASSERT(old_capa >= capacity);
if (old_capa > capacity) ary_heap_realloc(ary, capacity);
ary_verify(ary);
@@ -611,7 +499,7 @@ rb_ary_increment_share(VALUE shared_root)
{
if (!OBJ_FROZEN(shared_root)) {
long num = ARY_SHARED_ROOT_REFCNT(shared_root);
- assert(num >= 0);
+ RUBY_ASSERT(num >= 0);
ARY_SET_SHARED_ROOT_REFCNT(shared_root, num + 1);
}
return shared_root;
@@ -620,9 +508,9 @@ rb_ary_increment_share(VALUE shared_root)
static void
rb_ary_set_shared(VALUE ary, VALUE shared_root)
{
- assert(!ARY_EMBED_P(ary));
- assert(!OBJ_FROZEN(ary));
- assert(ARY_SHARED_ROOT_P(shared_root) || OBJ_FROZEN(shared_root));
+ RUBY_ASSERT(!ARY_EMBED_P(ary));
+ RUBY_ASSERT(!OBJ_FROZEN(ary));
+ RUBY_ASSERT(ARY_SHARED_ROOT_P(shared_root) || OBJ_FROZEN(shared_root));
rb_ary_increment_share(shared_root);
FL_SET_SHARED(ary);
@@ -656,18 +544,18 @@ rb_ary_cancel_sharing(VALUE ary)
ARY_SET_EMBED_LEN(ary, len);
}
else if (ARY_SHARED_ROOT_OCCUPIED(shared_root) && len > ((shared_len = RARRAY_LEN(shared_root))>>1)) {
- long shift = RARRAY_CONST_PTR_TRANSIENT(ary) - RARRAY_CONST_PTR_TRANSIENT(shared_root);
+ long shift = RARRAY_CONST_PTR(ary) - RARRAY_CONST_PTR(shared_root);
FL_UNSET_SHARED(ary);
- ARY_SET_PTR(ary, RARRAY_CONST_PTR_TRANSIENT(shared_root));
+ ARY_SET_PTR(ary, RARRAY_CONST_PTR(shared_root));
ARY_SET_CAPA(ary, shared_len);
- RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ RARRAY_PTR_USE(ary, ptr, {
MEMMOVE(ptr, ptr+shift, VALUE, len);
});
FL_SET_EMBED(shared_root);
rb_ary_decrement_share(shared_root);
}
else {
- VALUE *ptr = ary_heap_alloc(ary, len);
+ VALUE *ptr = ary_heap_alloc(len);
MEMCPY(ptr, ARY_HEAP_PTR(ary), VALUE, len);
rb_ary_unshare(ary);
ARY_SET_CAPA(ary, len);
@@ -700,7 +588,7 @@ ary_ensure_room_for_push(VALUE ary, long add_len)
if (new_len > ary_embed_capa(ary)) {
VALUE shared_root = ARY_SHARED_ROOT(ary);
if (ARY_SHARED_ROOT_OCCUPIED(shared_root)) {
- if (ARY_HEAP_PTR(ary) - RARRAY_CONST_PTR_TRANSIENT(shared_root) + new_len <= RARRAY_LEN(shared_root)) {
+ if (ARY_HEAP_PTR(ary) - RARRAY_CONST_PTR(shared_root) + new_len <= RARRAY_LEN(shared_root)) {
rb_ary_modify_check(ary);
ary_verify(ary);
@@ -745,7 +633,7 @@ ary_ensure_room_for_push(VALUE ary, long add_len)
* a.freeze
* a.frozen? # => true
*
- * An attempt to modify a frozen \Array raises FrozenError.
+ * An attempt to modify a frozen +Array+ raises FrozenError.
*/
VALUE
@@ -777,7 +665,7 @@ static VALUE
ary_alloc_embed(VALUE klass, long capa)
{
size_t size = ary_embed_size(capa);
- assert(rb_gc_size_allocatable_p(size));
+ RUBY_ASSERT(rb_gc_size_allocatable_p(size));
NEWOBJ_OF(ary, struct RArray, klass,
T_ARRAY | RARRAY_EMBED_FLAG | (RGENGC_WB_PROTECTED_ARRAY ? FL_WB_PROTECTED : 0),
size, 0);
@@ -807,7 +695,7 @@ empty_ary_alloc(VALUE klass)
static VALUE
ary_new(VALUE klass, long capa)
{
- VALUE ary,*ptr;
+ VALUE ary;
if (capa < 0) {
rb_raise(rb_eArgError, "negative array size (or size too big)");
@@ -824,10 +712,9 @@ ary_new(VALUE klass, long capa)
else {
ary = ary_alloc_heap(klass);
ARY_SET_CAPA(ary, capa);
- assert(!ARY_EMBED_P(ary));
+ RUBY_ASSERT(!ARY_EMBED_P(ary));
- ptr = ary_heap_alloc(ary, capa);
- ARY_SET_PTR(ary, ptr);
+ ARY_SET_PTR(ary, ary_heap_alloc(capa));
ARY_SET_HEAP_LEN(ary, 0);
}
@@ -889,7 +776,7 @@ static VALUE
ec_ary_alloc_embed(rb_execution_context_t *ec, VALUE klass, long capa)
{
size_t size = ary_embed_size(capa);
- assert(rb_gc_size_allocatable_p(size));
+ RUBY_ASSERT(rb_gc_size_allocatable_p(size));
NEWOBJ_OF(ary, struct RArray, klass,
T_ARRAY | RARRAY_EMBED_FLAG | (RGENGC_WB_PROTECTED_ARRAY ? FL_WB_PROTECTED : 0),
size, ec);
@@ -912,7 +799,7 @@ ec_ary_alloc_heap(rb_execution_context_t *ec, VALUE klass)
static VALUE
ec_ary_new(rb_execution_context_t *ec, VALUE klass, long capa)
{
- VALUE ary,*ptr;
+ VALUE ary;
if (capa < 0) {
rb_raise(rb_eArgError, "negative array size (or size too big)");
@@ -929,10 +816,9 @@ ec_ary_new(rb_execution_context_t *ec, VALUE klass, long capa)
else {
ary = ec_ary_alloc_heap(ec, klass);
ARY_SET_CAPA(ary, capa);
- assert(!ARY_EMBED_P(ary));
+ RUBY_ASSERT(!ARY_EMBED_P(ary));
- ptr = ary_heap_alloc(ary, capa);
- ARY_SET_PTR(ary, ptr);
+ ARY_SET_PTR(ary, ary_heap_alloc(capa));
ARY_SET_HEAP_LEN(ary, 0);
}
@@ -957,7 +843,6 @@ VALUE
rb_ary_hidden_new(long capa)
{
VALUE ary = ary_new(0, capa);
- rb_ary_transient_heap_evacuate(ary, TRUE);
return ary;
}
@@ -980,13 +865,8 @@ rb_ary_free(VALUE ary)
RB_DEBUG_COUNTER_INC(obj_ary_extracapa);
}
- if (RARRAY_TRANSIENT_P(ary)) {
- RB_DEBUG_COUNTER_INC(obj_ary_transient);
- }
- else {
- RB_DEBUG_COUNTER_INC(obj_ary_ptr);
- ary_heap_free(ary);
- }
+ RB_DEBUG_COUNTER_INC(obj_ary_ptr);
+ ary_heap_free(ary);
}
else {
RB_DEBUG_COUNTER_INC(obj_ary_embed);
@@ -1000,7 +880,7 @@ rb_ary_free(VALUE ary)
}
}
-RUBY_FUNC_EXPORTED size_t
+size_t
rb_ary_memsize(VALUE ary)
{
if (ARY_OWNS_HEAP_P(ary)) {
@@ -1024,14 +904,11 @@ ary_make_shared(VALUE ary)
}
else if (OBJ_FROZEN(ary)) {
if (!ARY_EMBED_P(ary)) {
- rb_ary_transient_heap_evacuate(ary, TRUE);
ary_shrink_capa(ary);
}
return ary;
}
else {
- rb_ary_transient_heap_evacuate(ary, TRUE);
-
long capa = ARY_CAPA(ary);
long len = RARRAY_LEN(ary);
@@ -1041,9 +918,7 @@ ary_make_shared(VALUE ary)
FL_SET_SHARED_ROOT(shared);
if (ARY_EMBED_P(ary)) {
- /* Cannot use ary_heap_alloc because we don't want to allocate
- * on the transient heap. */
- VALUE *ptr = ALLOC_N(VALUE, capa);
+ VALUE *ptr = ary_heap_alloc(capa);
ARY_SET_PTR(shared, ptr);
ary_memcpy(shared, 0, len, RARRAY_CONST_PTR(ary));
@@ -1073,9 +948,9 @@ ary_make_substitution(VALUE ary)
if (ary_embeddable_p(len)) {
VALUE subst = rb_ary_new_capa(len);
- assert(ARY_EMBED_P(subst));
+ RUBY_ASSERT(ARY_EMBED_P(subst));
- ary_memcpy(subst, 0, len, RARRAY_CONST_PTR_TRANSIENT(ary));
+ ary_memcpy(subst, 0, len, RARRAY_CONST_PTR(ary));
ARY_SET_EMBED_LEN(subst, len);
return subst;
}
@@ -1119,14 +994,14 @@ rb_to_array(VALUE ary)
* call-seq:
* Array.try_convert(object) -> object, new_array, or nil
*
- * If +object+ is an \Array object, returns +object+.
+ * If +object+ is an +Array+ object, returns +object+.
*
* Otherwise if +object+ responds to <tt>:to_ary</tt>,
* calls <tt>object.to_ary</tt> and returns the result.
*
* Returns +nil+ if +object+ does not respond to <tt>:to_ary</tt>
*
- * Raises an exception unless <tt>object.to_ary</tt> returns an \Array object.
+ * Raises an exception unless <tt>object.to_ary</tt> returns an +Array+ object.
*/
static VALUE
@@ -1167,33 +1042,33 @@ rb_ary_s_new(int argc, VALUE *argv, VALUE klass)
* Array.new(size, default_value) -> new_array
* Array.new(size) {|index| ... } -> new_array
*
- * Returns a new \Array.
+ * Returns a new +Array+.
*
- * With no block and no arguments, returns a new empty \Array object.
+ * With no block and no arguments, returns a new empty +Array+ object.
*
- * With no block and a single \Array argument +array+,
- * returns a new \Array formed from +array+:
+ * With no block and a single +Array+ argument +array+,
+ * returns a new +Array+ formed from +array+:
*
* a = Array.new([:foo, 'bar', 2])
* a.class # => Array
* a # => [:foo, "bar", 2]
*
- * With no block and a single \Integer argument +size+,
- * returns a new \Array of the given size
+ * With no block and a single Integer argument +size+,
+ * returns a new +Array+ of the given size
* whose elements are all +nil+:
*
* a = Array.new(3)
* a # => [nil, nil, nil]
*
* With no block and arguments +size+ and +default_value+,
- * returns an \Array of the given size;
+ * returns an +Array+ of the given size;
* each element is that same +default_value+:
*
* a = Array.new(3, 'x')
* a # => ['x', 'x', 'x']
*
* With a block and argument +size+,
- * returns an \Array of the given size;
+ * returns an +Array+ of the given size;
* the block is called with each successive integer +index+;
* the element for that +index+ is the return value from the block:
*
@@ -1204,7 +1079,7 @@ rb_ary_s_new(int argc, VALUE *argv, VALUE klass)
*
* With a block and no argument,
* or a single argument +0+,
- * ignores the block and returns a new empty \Array.
+ * ignores the block and returns a new empty +Array+.
*/
static VALUE
@@ -1216,8 +1091,8 @@ rb_ary_initialize(int argc, VALUE *argv, VALUE ary)
rb_ary_modify(ary);
if (argc == 0) {
rb_ary_reset(ary);
- assert(ARY_EMBED_P(ary));
- assert(ARY_EMBED_LEN(ary) == 0);
+ RUBY_ASSERT(ARY_EMBED_P(ary));
+ RUBY_ASSERT(ARY_EMBED_LEN(ary) == 0);
if (rb_block_given_p()) {
rb_warning("given block not used");
}
@@ -1314,25 +1189,26 @@ rb_ary_store(VALUE ary, long idx, VALUE val)
static VALUE
ary_make_partial(VALUE ary, VALUE klass, long offset, long len)
{
- assert(offset >= 0);
- assert(len >= 0);
- assert(offset+len <= RARRAY_LEN(ary));
+ RUBY_ASSERT(offset >= 0);
+ RUBY_ASSERT(len >= 0);
+ RUBY_ASSERT(offset+len <= RARRAY_LEN(ary));
- const size_t rarray_embed_capa_max = (sizeof(struct RArray) - offsetof(struct RArray, as.ary)) / sizeof(VALUE);
-
- if ((size_t)len <= rarray_embed_capa_max && ary_embeddable_p(len)) {
- VALUE result = ary_alloc_embed(klass, len);
- ary_memcpy(result, 0, len, RARRAY_CONST_PTR_TRANSIENT(ary) + offset);
+ VALUE result = ary_alloc_heap(klass);
+ size_t embed_capa = ary_embed_capa(result);
+ if ((size_t)len <= embed_capa) {
+ FL_SET_EMBED(result);
+ ary_memcpy(result, 0, len, RARRAY_CONST_PTR(ary) + offset);
ARY_SET_EMBED_LEN(result, len);
- return result;
}
else {
VALUE shared = ary_make_shared(ary);
- VALUE result = ary_alloc_heap(klass);
- assert(!ARY_EMBED_P(result));
+ /* The ary_make_shared call may allocate, which can trigger a GC
+ * compaction. This can cause the array to be embedded because it has
+ * a length of 0. */
+ FL_UNSET_EMBED(result);
- ARY_SET_PTR(result, RARRAY_CONST_PTR_TRANSIENT(ary));
+ ARY_SET_PTR(result, RARRAY_CONST_PTR(ary));
ARY_SET_LEN(result, RARRAY_LEN(ary));
rb_ary_set_shared(result, shared);
@@ -1340,25 +1216,27 @@ ary_make_partial(VALUE ary, VALUE klass, long offset, long len)
ARY_SET_LEN(result, len);
ary_verify(shared);
- ary_verify(result);
- return result;
}
+
+ ary_verify(result);
+ return result;
}
static VALUE
ary_make_partial_step(VALUE ary, VALUE klass, long offset, long len, long step)
{
- assert(offset >= 0);
- assert(len >= 0);
- assert(offset+len <= RARRAY_LEN(ary));
- assert(step != 0);
+ RUBY_ASSERT(offset >= 0);
+ RUBY_ASSERT(len >= 0);
+ RUBY_ASSERT(offset+len <= RARRAY_LEN(ary));
+ RUBY_ASSERT(step != 0);
- const VALUE *values = RARRAY_CONST_PTR_TRANSIENT(ary);
const long orig_len = len;
if (step > 0 && step >= len) {
VALUE result = ary_new(klass, 1);
VALUE *ptr = (VALUE *)ARY_EMBED_PTR(result);
+ const VALUE *values = RARRAY_CONST_PTR(ary);
+
RB_OBJ_WRITE(result, ptr, values[offset]);
ARY_SET_EMBED_LEN(result, 1);
return result;
@@ -1376,6 +1254,8 @@ ary_make_partial_step(VALUE ary, VALUE klass, long offset, long len, long step)
VALUE result = ary_new(klass, len);
if (ARY_EMBED_P(result)) {
VALUE *ptr = (VALUE *)ARY_EMBED_PTR(result);
+ const VALUE *values = RARRAY_CONST_PTR(ary);
+
for (i = 0; i < len; ++i) {
RB_OBJ_WRITE(result, ptr+i, values[j]);
j += step;
@@ -1383,7 +1263,9 @@ ary_make_partial_step(VALUE ary, VALUE klass, long offset, long len, long step)
ARY_SET_EMBED_LEN(result, len);
}
else {
- RARRAY_PTR_USE_TRANSIENT(result, ptr, {
+ const VALUE *values = RARRAY_CONST_PTR(ary);
+
+ RARRAY_PTR_USE(result, ptr, {
for (i = 0; i < len; ++i) {
RB_OBJ_WRITE(result, ptr+i, values[j]);
j += step;
@@ -1445,7 +1327,7 @@ ary_take_first_or_last(int argc, const VALUE *argv, VALUE ary, enum ary_take_pos
* a = [:foo, 'bar', 2]
* a << :baz # => [:foo, "bar", 2, :baz]
*
- * Appends +object+ as one element, even if it is another \Array:
+ * Appends +object+ as one element, even if it is another +Array+:
*
* a = [:foo, 'bar', 2]
* a1 = a << [3, 4]
@@ -1458,7 +1340,7 @@ rb_ary_push(VALUE ary, VALUE item)
{
long idx = RARRAY_LEN((ary_verify(ary), ary));
VALUE target_ary = ary_ensure_room_for_push(ary, 1);
- RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ RARRAY_PTR_USE(ary, ptr, {
RB_OBJ_WRITE(target_ary, &ptr[idx], item);
});
ARY_SET_LEN(ary, idx + 1);
@@ -1487,7 +1369,7 @@ rb_ary_cat(VALUE ary, const VALUE *argv, long len)
* a = [:foo, 'bar', 2]
* a.push(:baz, :bat) # => [:foo, "bar", 2, :baz, :bat]
*
- * Appends each argument as one element, even if it is another \Array:
+ * Appends each argument as one element, even if it is another +Array+:
*
* a = [:foo, 'bar', 2]
* a1 = a.push([:baz, :bat], [:bam, :bad])
@@ -1537,9 +1419,9 @@ rb_ary_pop(VALUE ary)
*
* Returns +nil+ if the array is empty.
*
- * When a non-negative \Integer argument +n+ is given and is in range,
+ * When a non-negative Integer argument +n+ is given and is in range,
*
- * removes and returns the last +n+ elements in a new \Array:
+ * removes and returns the last +n+ elements in a new +Array+:
* a = [:foo, 'bar', 2]
* a.pop(2) # => ["bar", 2]
*
@@ -1601,20 +1483,20 @@ rb_ary_shift(VALUE ary)
*
* Returns +nil+ if +self+ is empty.
*
- * When positive \Integer argument +n+ is given, removes the first +n+ elements;
- * returns those elements in a new \Array:
+ * When positive Integer argument +n+ is given, removes the first +n+ elements;
+ * returns those elements in a new +Array+:
*
* a = [:foo, 'bar', 2]
* a.shift(2) # => [:foo, 'bar']
* a # => [2]
*
* If +n+ is as large as or larger than <tt>self.length</tt>,
- * removes all elements; returns those elements in a new \Array:
+ * removes all elements; returns those elements in a new +Array+:
*
* a = [:foo, 'bar', 2]
* a.shift(3) # => [:foo, 'bar', 2]
*
- * If +n+ is zero, returns a new empty \Array; +self+ is unmodified.
+ * If +n+ is zero, returns a new empty +Array+; +self+ is unmodified.
*
* Related: #push, #pop, #unshift.
*/
@@ -1648,7 +1530,7 @@ rb_ary_behead(VALUE ary, long n)
if (!ARY_SHARED_P(ary)) {
if (ARY_EMBED_P(ary) || RARRAY_LEN(ary) < ARY_DEFAULT_SIZE) {
- RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ RARRAY_PTR_USE(ary, ptr, {
MEMMOVE(ptr, ptr + n, VALUE, RARRAY_LEN(ary) - n);
}); /* WB: no new reference */
ARY_INCREASE_LEN(ary, -n);
@@ -1681,7 +1563,7 @@ make_room_for_unshift(VALUE ary, const VALUE *head, VALUE *sharedp, int argc, lo
head = sharedp + argc + room;
}
ARY_SET_PTR(ary, head - argc);
- assert(ARY_SHARED_ROOT_OCCUPIED(ARY_SHARED_ROOT(ary)));
+ RUBY_ASSERT(ARY_SHARED_ROOT_OCCUPIED(ARY_SHARED_ROOT(ary)));
ary_verify(ary);
return ARY_SHARED_ROOT(ary);
@@ -1709,12 +1591,12 @@ ary_modify_for_unshift(VALUE ary, int argc)
capa = ARY_CAPA(ary);
ary_make_shared(ary);
- head = sharedp = RARRAY_CONST_PTR_TRANSIENT(ary);
+ head = sharedp = RARRAY_CONST_PTR(ary);
return make_room_for_unshift(ary, head, (void *)sharedp, argc, capa, len);
}
else {
/* sliding items */
- RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ RARRAY_PTR_USE(ary, ptr, {
MEMMOVE(ptr + argc, ptr, VALUE, len);
});
@@ -1746,8 +1628,8 @@ ary_ensure_room_for_unshift(VALUE ary, int argc)
return ary_modify_for_unshift(ary, argc);
}
else {
- const VALUE * head = RARRAY_CONST_PTR_TRANSIENT(ary);
- void *sharedp = (void *)RARRAY_CONST_PTR_TRANSIENT(shared_root);
+ const VALUE * head = RARRAY_CONST_PTR(ary);
+ void *sharedp = (void *)RARRAY_CONST_PTR(shared_root);
rb_ary_modify_check(ary);
return make_room_for_unshift(ary, head, sharedp, argc, capa, len);
@@ -1851,7 +1733,17 @@ static VALUE rb_ary_aref2(VALUE ary, VALUE b, VALUE e);
*
* Returns elements from +self+; does not modify +self+.
*
- * When a single \Integer argument +index+ is given, returns the element at offset +index+:
+ * In brief:
+ *
+ * a = [:foo, 'bar', 2]
+ * a[0] # => :foo
+ * a[-1] # => 2
+ * a[1, 2] # => ["bar", 2]
+ * a[0..1] # => [:foo, "bar"]
+ * a[0..-2] # => [:foo, "bar"]
+ * a[-2..2] # => ["bar", 2]
+ *
+ * When a single Integer argument +index+ is given, returns the element at offset +index+:
*
* a = [:foo, 'bar', 2]
* a[0] # => :foo
@@ -1866,8 +1758,8 @@ static VALUE rb_ary_aref2(VALUE ary, VALUE b, VALUE e);
*
* If +index+ is out of range, returns +nil+.
*
- * When two \Integer arguments +start+ and +length+ are given,
- * returns a new \Array of size +length+ containing successive elements beginning at offset +start+:
+ * When two Integer arguments +start+ and +length+ are given,
+ * returns a new +Array+ of size +length+ containing successive elements beginning at offset +start+:
*
* a = [:foo, 'bar', 2]
* a[0, 2] # => [:foo, "bar"]
@@ -1882,11 +1774,11 @@ static VALUE rb_ary_aref2(VALUE ary, VALUE b, VALUE e);
* a[2, 2] # => [2]
*
* If <tt>start == self.size</tt> and <tt>length >= 0</tt>,
- * returns a new empty \Array.
+ * returns a new empty +Array+.
*
* If +length+ is negative, returns +nil+.
*
- * When a single \Range argument +range+ is given,
+ * When a single Range argument +range+ is given,
* treats <tt>range.min</tt> as +start+ above
* and <tt>range.size</tt> as +length+ above:
*
@@ -1894,7 +1786,7 @@ static VALUE rb_ary_aref2(VALUE ary, VALUE b, VALUE e);
* a[0..1] # => [:foo, "bar"]
* a[1..2] # => ["bar", 2]
*
- * Special case: If <tt>range.start == a.size</tt>, returns a new empty \Array.
+ * Special case: If <tt>range.start == a.size</tt>, returns a new empty +Array+.
*
* If <tt>range.end</tt> is negative, calculates the end index from the end:
*
@@ -1918,7 +1810,7 @@ static VALUE rb_ary_aref2(VALUE ary, VALUE b, VALUE e);
* a[4..-1] # => nil
*
* When a single Enumerator::ArithmeticSequence argument +aseq+ is given,
- * returns an \Array of elements corresponding to the indexes produced by
+ * returns an +Array+ of elements corresponding to the indexes produced by
* the sequence.
*
* a = ['--', 'data1', '--', 'data2', '--', 'data3']
@@ -1989,7 +1881,7 @@ rb_ary_aref1(VALUE ary, VALUE arg)
* call-seq:
* array.at(index) -> object
*
- * Returns the element at \Integer offset +index+; does not modify +self+.
+ * Returns the element at Integer offset +index+; does not modify +self+.
* a = [:foo, 'bar', 2]
* a.at(0) # => :foo
* a.at(2) # => 2
@@ -2048,7 +1940,7 @@ rb_ary_last(int argc, const VALUE *argv, VALUE ary) // used by parse.y
*
* Returns the element at offset +index+.
*
- * With the single \Integer argument +index+,
+ * With the single Integer argument +index+,
* returns the element at offset +index+:
*
* a = [:foo, 'bar', 2]
@@ -2192,7 +2084,7 @@ rb_ary_index(int argc, VALUE *argv, VALUE ary)
*
* Returns +nil+ if the block never returns a truthy value.
*
- * When neither an argument nor a block is given, returns a new \Enumerator:
+ * When neither an argument nor a block is given, returns a new Enumerator:
*
* a = [:foo, 'bar', 2, 'bar']
* e = a.rindex
@@ -2264,7 +2156,7 @@ rb_ary_splice(VALUE ary, long beg, long len, const VALUE *rptr, long rlen)
}
{
- const VALUE *optr = RARRAY_CONST_PTR_TRANSIENT(ary);
+ const VALUE *optr = RARRAY_CONST_PTR(ary);
rofs = (rptr >= optr && rptr < optr + olen) ? rptr - optr : -1;
}
@@ -2277,7 +2169,7 @@ rb_ary_splice(VALUE ary, long beg, long len, const VALUE *rptr, long rlen)
len = beg + rlen;
ary_mem_clear(ary, olen, beg - olen);
if (rlen > 0) {
- if (rofs != -1) rptr = RARRAY_CONST_PTR_TRANSIENT(ary) + rofs;
+ if (rofs != -1) rptr = RARRAY_CONST_PTR(ary) + rofs;
ary_memcpy0(ary, beg, rlen, rptr, target_ary);
}
ARY_SET_LEN(ary, len);
@@ -2295,20 +2187,25 @@ rb_ary_splice(VALUE ary, long beg, long len, const VALUE *rptr, long rlen)
}
if (len != rlen) {
- RARRAY_PTR_USE_TRANSIENT(ary, ptr,
+ RARRAY_PTR_USE(ary, ptr,
MEMMOVE(ptr + beg + rlen, ptr + beg + len,
VALUE, olen - (beg + len)));
ARY_SET_LEN(ary, alen);
}
if (rlen > 0) {
- if (rofs != -1) rptr = RARRAY_CONST_PTR_TRANSIENT(ary) + rofs;
- /* give up wb-protected ary */
- RB_OBJ_WB_UNPROTECT_FOR(ARRAY, ary);
+ if (rofs == -1) {
+ rb_gc_writebarrier_remember(ary);
+ }
+ else {
+ /* In this case, we're copying from a region in this array, so
+ * we don't need to fire the write barrier. */
+ rptr = RARRAY_CONST_PTR(ary) + rofs;
+ }
/* do not use RARRAY_PTR() because it can causes GC.
* ary can contain T_NONE object because it is not cleared.
*/
- RARRAY_PTR_USE_TRANSIENT(ary, ptr,
+ RARRAY_PTR_USE(ary, ptr,
MEMMOVE(ptr + beg, rptr, VALUE, rlen));
}
}
@@ -2353,9 +2250,8 @@ rb_ary_resize(VALUE ary, long len)
else if (len <= ary_embed_capa(ary)) {
const VALUE *ptr = ARY_HEAP_PTR(ary);
long ptr_capa = ARY_HEAP_SIZE(ary);
- bool is_malloc_ptr = !ARY_SHARED_P(ary) && !RARRAY_TRANSIENT_P(ary);
+ bool is_malloc_ptr = !ARY_SHARED_P(ary);
- FL_UNSET(ary, RARRAY_TRANSIENT_FLAG);
FL_SET_EMBED(ary);
MEMCPY((VALUE *)ARY_EMBED_PTR(ary), ptr, VALUE, len); /* WB: no new reference */
@@ -2385,7 +2281,7 @@ static VALUE
ary_aset_by_rb_ary_splice(VALUE ary, long beg, long len, VALUE val)
{
VALUE rpl = rb_ary_to_ary(val);
- rb_ary_splice(ary, beg, len, RARRAY_CONST_PTR_TRANSIENT(rpl), RARRAY_LEN(rpl));
+ rb_ary_splice(ary, beg, len, RARRAY_CONST_PTR(rpl), RARRAY_LEN(rpl));
RB_GC_GUARD(rpl);
return val;
}
@@ -2398,7 +2294,32 @@ ary_aset_by_rb_ary_splice(VALUE ary, long beg, long len, VALUE val)
*
* Assigns elements in +self+; returns the given +object+.
*
- * When \Integer argument +index+ is given, assigns +object+ to an element in +self+.
+ * In brief:
+ *
+ * a_orig = [:foo, 'bar', 2]
+ * # With argument index.
+ * a = a_orig.dup
+ * a[0] = 'foo' # => "foo"
+ * a # => ["foo", "bar", 2]
+ * a = a_orig.dup
+ * a[7] = 'foo' # => "foo"
+ * a # => [:foo, "bar", 2, nil, nil, nil, nil, "foo"]
+ * # With arguments start and length.
+ * a = a_orig.dup
+ * a[0, 2] = 'foo' # => "foo"
+ * a # => ["foo", 2]
+ * a = a_orig.dup
+ * a[6, 50] = 'foo' # => "foo"
+ * a # => [:foo, "bar", 2, nil, nil, nil, "foo"]
+ * # With argument range.
+ * a = a_orig.dup
+ * a[0..1] = 'foo' # => "foo"
+ * a # => ["foo", 2]
+ * a = a_orig.dup
+ * a[6..50] = 'foo' # => "foo"
+ * a # => [:foo, "bar", 2, nil, nil, nil, "foo"]
+ *
+ * When Integer argument +index+ is given, assigns +object+ to an element in +self+.
*
* If +index+ is non-negative, assigns +object+ the element at offset +index+:
*
@@ -2418,7 +2339,7 @@ ary_aset_by_rb_ary_splice(VALUE ary, long beg, long len, VALUE val)
* a[-1] = 'two' # => "two"
* a # => [:foo, "bar", "two"]
*
- * When \Integer arguments +start+ and +length+ are given and +object+ is not an \Array,
+ * When Integer arguments +start+ and +length+ are given and +object+ is not an +Array+,
* removes <tt>length - 1</tt> elements beginning at offset +start+,
* and assigns +object+ at offset +start+:
*
@@ -2453,7 +2374,7 @@ ary_aset_by_rb_ary_splice(VALUE ary, long beg, long len, VALUE val)
* a[1, 5] = 'foo' # => "foo"
* a # => [:foo, "foo"]
*
- * When \Range argument +range+ is given and +object+ is an \Array,
+ * When Range argument +range+ is given and +object+ is not an +Array+,
* removes <tt>length - 1</tt> elements beginning at offset +start+,
* and assigns +object+ at offset +start+:
*
@@ -2468,7 +2389,8 @@ ary_aset_by_rb_ary_splice(VALUE ary, long beg, long len, VALUE val)
* a # => [:foo, "foo"]
*
* If the array length is less than <tt>range.begin</tt>,
- * assigns +object+ at offset <tt>range.begin</tt>, and ignores +length+:
+ * extends the array with +nil+, assigns +object+ at offset <tt>range.begin</tt>,
+ * and ignores +length+:
*
* a = [:foo, 'bar', 2]
* a[6..50] = 'foo' # => "foo"
@@ -2533,7 +2455,7 @@ rb_ary_aset(int argc, VALUE *argv, VALUE ary)
* call-seq:
* array.insert(index, *objects) -> self
*
- * Inserts given +objects+ before or after the element at \Integer index +offset+;
+ * Inserts given +objects+ before or after the element at Integer index +offset+;
* returns +self+.
*
* When +index+ is non-negative, inserts all given +objects+
@@ -2598,50 +2520,19 @@ ary_enum_length(VALUE ary, VALUE args, VALUE eobj)
return rb_ary_length(ary);
}
-/*
- * call-seq:
- * array.each {|element| ... } -> self
- * array.each -> Enumerator
- *
- * Iterates over array elements.
- *
- * When a block given, passes each successive array element to the block;
- * returns +self+:
- *
- * a = [:foo, 'bar', 2]
- * a.each {|element| puts "#{element.class} #{element}" }
- *
- * Output:
- *
- * Symbol foo
- * String bar
- * Integer 2
- *
- * Allows the array to be modified during iteration:
- *
- * a = [:foo, 'bar', 2]
- * a.each {|element| puts element; a.clear if element.to_s.start_with?('b') }
- *
- * Output:
- *
- * foo
- * bar
- *
- * When no block given, returns a new \Enumerator:
- * a = [:foo, 'bar', 2]
- *
- * e = a.each
- * e # => #<Enumerator: [:foo, "bar", 2]:each>
- * a1 = e.each {|element| puts "#{element.class} #{element}" }
- *
- * Output:
- *
- * Symbol foo
- * String bar
- * Integer 2
- *
- * Related: #each_index, #reverse_each.
- */
+// Primitive to avoid a race condition in Array#each.
+// Return `true` and write `value` and `index` if the element exists.
+static VALUE
+ary_fetch_next(VALUE self, VALUE *index, VALUE *value)
+{
+ long i = NUM2LONG(*index);
+ if (i >= RARRAY_LEN(self)) {
+ return Qfalse;
+ }
+ *value = RARRAY_AREF(self, i);
+ *index = LONG2NUM(i + 1);
+ return Qtrue;
+}
VALUE
rb_ary_each(VALUE ary)
@@ -2684,7 +2575,7 @@ rb_ary_each(VALUE ary)
* 0
* 1
*
- * When no block given, returns a new \Enumerator:
+ * When no block given, returns a new Enumerator:
*
* a = [:foo, 'bar', 2]
* e = a.each_index
@@ -2741,7 +2632,7 @@ rb_ary_each_index(VALUE ary)
* 2
* bar
*
- * When no block given, returns a new \Enumerator:
+ * When no block given, returns a new Enumerator:
*
* a = [:foo, 'bar', 2]
* e = a.reverse_each
@@ -2808,7 +2699,7 @@ rb_ary_dup(VALUE ary)
{
long len = RARRAY_LEN(ary);
VALUE dup = rb_ary_new2(len);
- ary_memcpy(dup, 0, len, RARRAY_CONST_PTR_TRANSIENT(ary));
+ ary_memcpy(dup, 0, len, RARRAY_CONST_PTR(ary));
ARY_SET_LEN(dup, len);
ary_verify(ary);
@@ -2961,7 +2852,7 @@ rb_ary_join(VALUE ary, VALUE sep)
* array.join ->new_string
* array.join(separator = $,) -> new_string
*
- * Returns the new \String formed by joining the array elements after conversion.
+ * Returns the new String formed by joining the array elements after conversion.
* For each element +element+:
*
* - Uses <tt>element.to_s</tt> if +element+ is not a <tt>kind_of?(Array)</tt>.
@@ -3021,7 +2912,7 @@ inspect_ary(VALUE ary, VALUE dummy, int recur)
* call-seq:
* array.inspect -> new_string
*
- * Returns the new \String formed by calling method <tt>#inspect</tt>
+ * Returns the new String formed by calling method <tt>#inspect</tt>
* on each array element:
*
* a = [:foo, 'bar', 2]
@@ -3046,12 +2937,12 @@ rb_ary_to_s(VALUE ary)
* call-seq:
* to_a -> self or new_array
*
- * When +self+ is an instance of \Array, returns +self+:
+ * When +self+ is an instance of +Array+, returns +self+:
*
* a = [:foo, 'bar', 2]
* a.to_a # => [:foo, "bar", 2]
*
- * Otherwise, returns a new \Array containing the elements of +self+:
+ * Otherwise, returns a new +Array+ containing the elements of +self+:
*
* class MyArray < Array; end
* a = MyArray.new(['foo', 'bar', 'two'])
@@ -3079,18 +2970,18 @@ rb_ary_to_a(VALUE ary)
* array.to_h -> new_hash
* array.to_h {|item| ... } -> new_hash
*
- * Returns a new \Hash formed from +self+.
+ * Returns a new Hash formed from +self+.
*
* When a block is given, calls the block with each array element;
- * the block must return a 2-element \Array whose two elements
- * form a key-value pair in the returned \Hash:
+ * the block must return a 2-element +Array+ whose two elements
+ * form a key-value pair in the returned Hash:
*
* a = ['foo', :bar, 1, [2, 3], {baz: 4}]
* h = a.to_h {|item| [item, item] }
* h # => {"foo"=>"foo", :bar=>:bar, 1=>1, [2, 3]=>[2, 3], {:baz=>4}=>{:baz=>4}}
*
- * When no block is given, +self+ must be an \Array of 2-element sub-arrays,
- * each sub-array is formed into a key-value pair in the new \Hash:
+ * When no block is given, +self+ must be an +Array+ of 2-element sub-arrays,
+ * each sub-array is formed into a key-value pair in the new Hash:
*
* [].to_h # => {}
* a = [['foo', 'zero'], ['bar', 'one'], ['baz', 'two']]
@@ -3154,7 +3045,7 @@ rb_ary_reverse(VALUE ary)
rb_ary_modify(ary);
if (len > 1) {
- RARRAY_PTR_USE_TRANSIENT(ary, p1, {
+ RARRAY_PTR_USE(ary, p1, {
p2 = p1 + len - 1; /* points last item */
ary_reverse(p1, p2);
}); /* WB: no new reference */
@@ -3183,7 +3074,7 @@ rb_ary_reverse_bang(VALUE ary)
* call-seq:
* array.reverse -> new_array
*
- * Returns a new \Array with the elements of +self+ in reverse order:
+ * Returns a new +Array+ with the elements of +self+ in reverse order:
*
* a = ['foo', 'bar', 'two']
* a1 = a.reverse
@@ -3198,8 +3089,8 @@ rb_ary_reverse_m(VALUE ary)
VALUE dup = rb_ary_new2(len);
if (len > 0) {
- const VALUE *p1 = RARRAY_CONST_PTR_TRANSIENT(ary);
- VALUE *p2 = (VALUE *)RARRAY_CONST_PTR_TRANSIENT(dup) + len - 1;
+ const VALUE *p1 = RARRAY_CONST_PTR(ary);
+ VALUE *p2 = (VALUE *)RARRAY_CONST_PTR(dup) + len - 1;
do *p2-- = *p1++; while (--len > 0);
}
ARY_SET_LEN(dup, RARRAY_LEN(ary));
@@ -3241,7 +3132,7 @@ rb_ary_rotate(VALUE ary, long cnt)
if (cnt != 0) {
long len = RARRAY_LEN(ary);
if (len > 1 && (cnt = rotate_count(cnt, len)) > 0) {
- RARRAY_PTR_USE_TRANSIENT(ary, ptr, ary_rotate_ptr(ptr, len, cnt));
+ RARRAY_PTR_USE(ary, ptr, ary_rotate_ptr(ptr, len, cnt));
return ary;
}
}
@@ -3260,7 +3151,7 @@ rb_ary_rotate(VALUE ary, long cnt)
* a = [:foo, 'bar', 2, 'bar']
* a.rotate! # => ["bar", 2, "bar", :foo]
*
- * When given a non-negative \Integer +count+,
+ * When given a non-negative Integer +count+,
* rotates +count+ elements from the beginning to the end:
*
* a = [:foo, 'bar', 2]
@@ -3307,18 +3198,18 @@ rb_ary_rotate_bang(int argc, VALUE *argv, VALUE ary)
* array.rotate -> new_array
* array.rotate(count) -> new_array
*
- * Returns a new \Array formed from +self+ with elements
+ * Returns a new +Array+ formed from +self+ with elements
* rotated from one end to the other.
*
- * When no argument given, returns a new \Array that is like +self+,
+ * When no argument given, returns a new +Array+ that is like +self+,
* except that the first element has been rotated to the last position:
*
* a = [:foo, 'bar', 2, 'bar']
* a1 = a.rotate
* a1 # => ["bar", 2, "bar", :foo]
*
- * When given a non-negative \Integer +count+,
- * returns a new \Array with +count+ elements rotated from the beginning to the end:
+ * When given a non-negative Integer +count+,
+ * returns a new +Array+ with +count+ elements rotated from the beginning to the end:
*
* a = [:foo, 'bar', 2]
* a1 = a.rotate(2)
@@ -3336,7 +3227,7 @@ rb_ary_rotate_bang(int argc, VALUE *argv, VALUE ary)
* a1 = a.rotate(0)
* a1 # => [:foo, "bar", 2]
*
- * When given a negative \Integer +count+, rotates in the opposite direction,
+ * When given a negative Integer +count+, rotates in the opposite direction,
* from end to beginning:
*
* a = [:foo, 'bar', 2]
@@ -3363,7 +3254,7 @@ rb_ary_rotate_m(int argc, VALUE *argv, VALUE ary)
rotated = rb_ary_new2(len);
if (len > 0) {
cnt = rotate_count(cnt, len);
- ptr = RARRAY_CONST_PTR_TRANSIENT(ary);
+ ptr = RARRAY_CONST_PTR(ary);
len -= cnt;
ary_memcpy(rotated, 0, len, ptr + cnt);
ary_memcpy(rotated, len, cnt, ptr);
@@ -3484,7 +3375,7 @@ VALUE
rb_ary_sort_bang(VALUE ary)
{
rb_ary_modify(ary);
- assert(!ARY_SHARED_P(ary));
+ RUBY_ASSERT(!ARY_SHARED_P(ary));
if (RARRAY_LEN(ary) > 1) {
VALUE tmp = ary_make_substitution(ary); /* only ary refers tmp */
struct ary_sort_data data;
@@ -3502,6 +3393,9 @@ rb_ary_sort_bang(VALUE ary)
rb_ary_unshare(ary);
FL_SET_EMBED(ary);
}
+ if (ARY_EMBED_LEN(tmp) > ARY_CAPA(ary)) {
+ ary_resize_capa(ary, ARY_EMBED_LEN(tmp));
+ }
ary_memcpy(ary, 0, ARY_EMBED_LEN(tmp), ARY_EMBED_PTR(tmp));
ARY_SET_LEN(ary, ARY_EMBED_LEN(tmp));
}
@@ -3511,7 +3405,7 @@ rb_ary_sort_bang(VALUE ary)
ARY_SET_CAPA(ary, RARRAY_LEN(tmp));
}
else {
- assert(!ARY_SHARED_P(tmp));
+ RUBY_ASSERT(!ARY_SHARED_P(tmp));
if (ARY_EMBED_P(ary)) {
FL_UNSET_EMBED(ary);
}
@@ -3544,7 +3438,7 @@ rb_ary_sort_bang(VALUE ary)
* array.sort -> new_array
* array.sort {|a, b| ... } -> new_array
*
- * Returns a new \Array whose elements are those from +self+, sorted.
+ * Returns a new +Array+ whose elements are those from +self+, sorted.
*
* With no block, compares elements using operator <tt><=></tt>
* (see Comparable):
@@ -3648,8 +3542,8 @@ rb_ary_bsearch_index(VALUE ary)
const VALUE zero = INT2FIX(0);
switch (rb_cmpint(rb_funcallv(v, id_cmp, 1, &zero), v, zero)) {
case 0: return INT2FIX(mid);
- case 1: smaller = 1; break;
- case -1: smaller = 0;
+ case 1: smaller = 0; break;
+ case -1: smaller = 1;
}
}
else {
@@ -3694,7 +3588,7 @@ sort_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, dummy))
* a.sort_by! {|element| element.size }
* a # => ["d", "cc", "bbb", "aaaa"]
*
- * Returns a new \Enumerator if no block given:
+ * Returns a new Enumerator if no block given:
*
* a = ['aaaa', 'bbb', 'cc', 'd']
* a.sort_by! # => #<Enumerator: ["aaaa", "bbb", "cc", "d"]:sort_by!>
@@ -3720,13 +3614,13 @@ rb_ary_sort_by_bang(VALUE ary)
* array.map -> new_enumerator
*
* Calls the block, if given, with each element of +self+;
- * returns a new \Array whose elements are the return values from the block:
+ * returns a new +Array+ whose elements are the return values from the block:
*
* a = [:foo, 'bar', 2]
* a1 = a.map {|element| element.class }
* a1 # => [Symbol, String, Integer]
*
- * Returns a new \Enumerator if no block given:
+ * Returns a new Enumerator if no block given:
* a = [:foo, 'bar', 2]
* a1 = a.map
* a1 # => #<Enumerator: [:foo, "bar", 2]:map>
@@ -3759,7 +3653,7 @@ rb_ary_collect(VALUE ary)
* a = [:foo, 'bar', 2]
* a.map! { |element| element.class } # => [Symbol, String, Integer]
*
- * Returns a new \Enumerator if no block given:
+ * Returns a new Enumerator if no block given:
*
* a = [:foo, 'bar', 2]
* a1 = a.map!
@@ -3816,7 +3710,7 @@ append_values_at_single(VALUE result, VALUE ary, long olen, VALUE idx)
/* check if idx is Range */
else if (rb_range_beg_len(idx, &beg, &len, olen, 1)) {
if (len > 0) {
- const VALUE *const src = RARRAY_CONST_PTR_TRANSIENT(ary);
+ const VALUE *const src = RARRAY_CONST_PTR(ary);
const long end = beg + len;
const long prevlen = RARRAY_LEN(result);
if (beg < olen) {
@@ -3838,8 +3732,8 @@ append_values_at_single(VALUE result, VALUE ary, long olen, VALUE idx)
* call-seq:
* array.values_at(*indexes) -> new_array
*
- * Returns a new \Array whose elements are the elements
- * of +self+ at the given \Integer or \Range +indexes+.
+ * Returns a new +Array+ whose elements are the elements
+ * of +self+ at the given Integer or Range +indexes+.
*
* For each positive +index+, returns the element at offset +index+:
*
@@ -3858,7 +3752,7 @@ append_values_at_single(VALUE result, VALUE ary, long olen, VALUE idx)
* a = [:foo, 'bar', 2]
* a.values_at(0, 3, 1, 3) # => [:foo, nil, "bar", nil]
*
- * Returns a new empty \Array if no arguments given.
+ * Returns a new empty +Array+ if no arguments given.
*
* For each negative +index+, counts backward from the end of the array:
*
@@ -3896,14 +3790,14 @@ rb_ary_values_at(int argc, VALUE *argv, VALUE ary)
* array.select -> new_enumerator
*
* Calls the block, if given, with each element of +self+;
- * returns a new \Array containing those elements of +self+
+ * returns a new +Array+ containing those elements of +self+
* for which the block returns a truthy value:
*
* a = [:foo, 'bar', 2, :bam]
* a1 = a.select {|element| element.to_s.start_with?('b') }
* a1 # => ["bar", :bam]
*
- * Returns a new \Enumerator if no block given:
+ * Returns a new Enumerator if no block given:
*
* a = [:foo, 'bar', 2, :bam]
* a.select # => #<Enumerator: [:foo, "bar", 2, :bam]:select>
@@ -3962,7 +3856,7 @@ select_bang_ensure(VALUE a)
rb_ary_modify(ary);
if (i1 < len) {
tail = len - i1;
- RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ RARRAY_PTR_USE(ary, ptr, {
MEMMOVE(ptr + i2, ptr + i1, VALUE, tail);
});
}
@@ -3986,7 +3880,7 @@ select_bang_ensure(VALUE a)
*
* Returns +nil+ if no elements were removed.
*
- * Returns a new \Enumerator if no block given:
+ * Returns a new Enumerator if no block given:
*
* a = [:foo, 'bar', 2, :bam]
* a.select! # => #<Enumerator: [:foo, "bar", 2, :bam]:select!>
@@ -4017,7 +3911,7 @@ rb_ary_select_bang(VALUE ary)
* a = [:foo, 'bar', 2, :bam]
* a.keep_if {|element| element.to_s.start_with?('b') } # => ["bar", :bam]
*
- * Returns a new \Enumerator if no block given:
+ * Returns a new Enumerator if no block given:
*
* a = [:foo, 'bar', 2, :bam]
* a.keep_if # => #<Enumerator: [:foo, "bar", 2, :bam]:keep_if>
@@ -4149,7 +4043,7 @@ rb_ary_delete_at(VALUE ary, long pos)
rb_ary_modify(ary);
del = RARRAY_AREF(ary, pos);
- RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ RARRAY_PTR_USE(ary, ptr, {
MEMMOVE(ptr+pos, ptr+pos+1, VALUE, len-pos-1);
});
ARY_INCREASE_LEN(ary, -1);
@@ -4161,7 +4055,7 @@ rb_ary_delete_at(VALUE ary, long pos)
* call-seq:
* array.delete_at(index) -> deleted_object or nil
*
- * Deletes an element from +self+, per the given \Integer +index+.
+ * Deletes an element from +self+, per the given Integer +index+.
*
* When +index+ is non-negative, deletes the element at offset +index+:
*
@@ -4210,7 +4104,7 @@ ary_slice_bang_by_rb_ary_splice(VALUE ary, long pos, long len)
return rb_ary_new2(0);
}
else {
- VALUE arg2 = rb_ary_new4(len, RARRAY_CONST_PTR_TRANSIENT(ary)+pos);
+ VALUE arg2 = rb_ary_new4(len, RARRAY_CONST_PTR(ary)+pos);
rb_ary_splice(ary, pos, len, 0, 0);
return arg2;
}
@@ -4224,7 +4118,7 @@ ary_slice_bang_by_rb_ary_splice(VALUE ary, long pos, long len)
*
* Removes and returns elements from +self+.
*
- * When the only argument is an \Integer +n+,
+ * When the only argument is an Integer +n+,
* removes and returns the _nth_ element in +self+:
*
* a = [:foo, 'bar', 2]
@@ -4241,7 +4135,7 @@ ary_slice_bang_by_rb_ary_splice(VALUE ary, long pos, long len)
*
* When the only arguments are Integers +start+ and +length+,
* removes +length+ elements from +self+ beginning at offset +start+;
- * returns the deleted objects in a new \Array:
+ * returns the deleted objects in a new +Array+:
*
* a = [:foo, 'bar', 2]
* a.slice!(0, 2) # => [:foo, "bar"]
@@ -4255,18 +4149,18 @@ ary_slice_bang_by_rb_ary_splice(VALUE ary, long pos, long len)
* a # => [:foo]
*
* If <tt>start == a.size</tt> and +length+ is non-negative,
- * returns a new empty \Array.
+ * returns a new empty +Array+.
*
* If +length+ is negative, returns +nil+.
*
- * When the only argument is a \Range object +range+,
+ * When the only argument is a Range object +range+,
* treats <tt>range.min</tt> as +start+ above and <tt>range.size</tt> as +length+ above:
*
* a = [:foo, 'bar', 2]
* a.slice!(1..2) # => ["bar", 2]
* a # => [:foo]
*
- * If <tt>range.start == a.size</tt>, returns a new empty \Array.
+ * If <tt>range.start == a.size</tt>, returns a new empty +Array+.
*
* If <tt>range.start</tt> is larger than the array size, returns +nil+.
*
@@ -4375,7 +4269,7 @@ ary_reject_bang(VALUE ary)
*
* Returns +nil+ if no elements removed.
*
- * Returns a new \Enumerator if no block given:
+ * Returns a new Enumerator if no block given:
*
* a = [:foo, 'bar', 2]
* a.reject! # => #<Enumerator: [:foo, "bar", 2]:reject!>
@@ -4395,14 +4289,14 @@ rb_ary_reject_bang(VALUE ary)
* array.reject {|element| ... } -> new_array
* array.reject -> new_enumerator
*
- * Returns a new \Array whose elements are all those from +self+
+ * Returns a new +Array+ whose elements are all those from +self+
* for which the block returns +false+ or +nil+:
*
* a = [:foo, 'bar', 2, 'bat']
* a1 = a.reject {|element| element.to_s.start_with?('b') }
* a1 # => [:foo, 2]
*
- * Returns a new \Enumerator if no block given:
+ * Returns a new Enumerator if no block given:
*
* a = [:foo, 'bar', 2]
* a.reject # => #<Enumerator: [:foo, "bar", 2]:reject>
@@ -4431,12 +4325,12 @@ rb_ary_reject(VALUE ary)
* a = [:foo, 'bar', 2, 'bat']
* a.delete_if {|element| element.to_s.start_with?('b') } # => [:foo, 2]
*
- * Returns a new \Enumerator if no block given:
+ * Returns a new Enumerator if no block given:
*
* a = [:foo, 'bar', 2]
* a.delete_if # => #<Enumerator: [:foo, "bar", 2]:delete_if>
*
-3 */
+ */
static VALUE
rb_ary_delete_if(VALUE ary)
@@ -4479,7 +4373,7 @@ take_items(VALUE obj, long n)
* array.zip(*other_arrays) -> new_array
* array.zip(*other_arrays) {|other_array| ... } -> nil
*
- * When no block given, returns a new \Array +new_array+ of size <tt>self.size</tt>
+ * When no block given, returns a new +Array+ +new_array+ of size <tt>self.size</tt>
* whose elements are Arrays.
*
* Each nested array <tt>new_array[n]</tt> is of size <tt>other_arrays.size+1</tt>,
@@ -4514,6 +4408,13 @@ take_items(VALUE obj, long n)
* d = a.zip(b, c)
* d # => [[:a0, :b0, :c0], [:a1, :b1, :c1], [:a2, :b2, :c2], [:a3, :b3, :c3]]
*
+ * If an argument is not an array, it extracts the values by calling #each:
+ *
+ * a = [:a0, :a1, :a2, :a2]
+ * b = 1..4
+ * c = a.zip(b)
+ * c # => [[:a0, 1], [:a1, 2], [:a2, 3], [:a2, 4]]
+ *
* When a block is given, calls the block with each of the sub-arrays (formed as above); returns +nil+:
*
* a = [:a0, :a1, :a2, :a3]
@@ -4592,7 +4493,7 @@ rb_ary_zip(int argc, VALUE *argv, VALUE ary)
* call-seq:
* array.transpose -> new_array
*
- * Transposes the rows and columns in an \Array of Arrays;
+ * Transposes the rows and columns in an +Array+ of Arrays;
* the nested Arrays must all be the same size:
*
* a = [[:a0, :a1], [:b0, :b1], [:c0, :c1]]
@@ -4650,15 +4551,15 @@ rb_ary_replace(VALUE copy, VALUE orig)
/* orig has enough space to embed the contents of orig. */
if (RARRAY_LEN(orig) <= ary_embed_capa(copy)) {
- assert(ARY_EMBED_P(copy));
- ary_memcpy(copy, 0, RARRAY_LEN(orig), RARRAY_CONST_PTR_TRANSIENT(orig));
+ RUBY_ASSERT(ARY_EMBED_P(copy));
+ ary_memcpy(copy, 0, RARRAY_LEN(orig), RARRAY_CONST_PTR(orig));
ARY_SET_EMBED_LEN(copy, RARRAY_LEN(orig));
}
/* orig is embedded but copy does not have enough space to embed the
* contents of orig. */
else if (ARY_EMBED_P(orig)) {
long len = ARY_EMBED_LEN(orig);
- VALUE *ptr = ary_heap_alloc(copy, len);
+ VALUE *ptr = ary_heap_alloc(len);
FL_UNSET_EMBED(copy);
ARY_SET_PTR(copy, ptr);
@@ -4667,7 +4568,7 @@ rb_ary_replace(VALUE copy, VALUE orig)
// No allocation and exception expected that could leave `copy` in a
// bad state from the edits above.
- ary_memcpy(copy, 0, len, RARRAY_CONST_PTR_TRANSIENT(orig));
+ ary_memcpy(copy, 0, len, RARRAY_CONST_PTR(orig));
}
/* Otherwise, orig is on heap and copy does not have enough space to embed
* the contents of orig. */
@@ -4733,7 +4634,7 @@ rb_ary_clear(VALUE ary)
* a # => ["a", "b", "c", "d"]
* a.fill(:X) # => [:X, :X, :X, :X]
*
- * With arguments +obj+ and \Integer +start+, and no block given,
+ * With arguments +obj+ and Integer +start+, and no block given,
* replaces elements based on the given start.
*
* If +start+ is in range (<tt>0 <= start < array.size</tt>),
@@ -4761,7 +4662,7 @@ rb_ary_clear(VALUE ary)
* a = ['a', 'b', 'c', 'd']
* a.fill(:X, -50) # => [:X, :X, :X, :X]
*
- * With arguments +obj+, \Integer +start+, and \Integer +length+, and no block given,
+ * With arguments +obj+, Integer +start+, and Integer +length+, and no block given,
* replaces elements based on the given +start+ and +length+.
*
* If +start+ is in range, replaces +length+ elements beginning at offset +start+:
@@ -4787,7 +4688,7 @@ rb_ary_clear(VALUE ary)
* a.fill(:X, 1, 0) # => ["a", "b", "c", "d"]
* a.fill(:X, 1, -1) # => ["a", "b", "c", "d"]
*
- * With arguments +obj+ and \Range +range+, and no block given,
+ * With arguments +obj+ and Range +range+, and no block given,
* replaces elements based on the given range.
*
* If the range is positive and ascending (<tt>0 < range.begin <= range.end</tt>),
@@ -4978,7 +4879,7 @@ rb_ary_fill(int argc, VALUE *argv, VALUE ary)
* call-seq:
* array + other_array -> new_array
*
- * Returns a new \Array containing all elements of +array+
+ * Returns a new +Array+ containing all elements of +array+
* followed by all elements of +other_array+:
*
* a = [0, 1] + [2, 3]
@@ -4999,8 +4900,8 @@ rb_ary_plus(VALUE x, VALUE y)
len = xlen + ylen;
z = rb_ary_new2(len);
- ary_memcpy(z, 0, xlen, RARRAY_CONST_PTR_TRANSIENT(x));
- ary_memcpy(z, xlen, ylen, RARRAY_CONST_PTR_TRANSIENT(y));
+ ary_memcpy(z, 0, xlen, RARRAY_CONST_PTR(x));
+ ary_memcpy(z, xlen, ylen, RARRAY_CONST_PTR(y));
ARY_SET_LEN(z, len);
return z;
}
@@ -5010,7 +4911,7 @@ ary_append(VALUE x, VALUE y)
{
long n = RARRAY_LEN(y);
if (n > 0) {
- rb_ary_splice(x, RARRAY_LEN(x), 0, RARRAY_CONST_PTR_TRANSIENT(y), n);
+ rb_ary_splice(x, RARRAY_LEN(x), 0, RARRAY_CONST_PTR(y), n);
}
RB_GC_GUARD(y);
return x;
@@ -5020,7 +4921,7 @@ ary_append(VALUE x, VALUE y)
* call-seq:
* array.concat(*other_arrays) -> self
*
- * Adds to +array+ all elements from each \Array in +other_arrays+; returns +self+:
+ * Adds to +array+ all elements from each +Array+ in +other_arrays+; returns +self+:
*
* a = [0, 1]
* a.concat([2, 3], [4, 5]) # => [0, 1, 2, 3, 4, 5]
@@ -5058,13 +4959,13 @@ rb_ary_concat(VALUE x, VALUE y)
* array * n -> new_array
* array * string_separator -> new_string
*
- * When non-negative argument \Integer +n+ is given,
- * returns a new \Array built by concatenating the +n+ copies of +self+:
+ * When non-negative argument Integer +n+ is given,
+ * returns a new +Array+ built by concatenating the +n+ copies of +self+:
*
* a = ['x', 'y']
* a * 3 # => ["x", "y", "x", "y", "x", "y"]
*
- * When \String argument +string_separator+ is given,
+ * When String argument +string_separator+ is given,
* equivalent to <tt>array.join(string_separator)</tt>:
*
* [0, [0, 1], {foo: 0}] * ', ' # => "0, 0, 1, {:foo=>0}"
@@ -5099,16 +5000,16 @@ rb_ary_times(VALUE ary, VALUE times)
ary2 = ary_new(rb_cArray, len);
ARY_SET_LEN(ary2, len);
- ptr = RARRAY_CONST_PTR_TRANSIENT(ary);
+ ptr = RARRAY_CONST_PTR(ary);
t = RARRAY_LEN(ary);
if (0 < t) {
ary_memcpy(ary2, 0, t, ptr);
while (t <= len/2) {
- ary_memcpy(ary2, t, t, RARRAY_CONST_PTR_TRANSIENT(ary2));
+ ary_memcpy(ary2, t, t, RARRAY_CONST_PTR(ary2));
t *= 2;
}
if (t < len) {
- ary_memcpy(ary2, t, len-t, RARRAY_CONST_PTR_TRANSIENT(ary2));
+ ary_memcpy(ary2, t, len-t, RARRAY_CONST_PTR(ary2));
}
}
out:
@@ -5119,7 +5020,7 @@ rb_ary_times(VALUE ary, VALUE times)
* call-seq:
* array.assoc(obj) -> found_array or nil
*
- * Returns the first element in +self+ that is an \Array
+ * Returns the first element in +self+ that is an +Array+
* whose first element <tt>==</tt> +obj+:
*
* a = [{foo: 0}, [2, 4], [4, 5, 6], [4, 5]]
@@ -5149,7 +5050,7 @@ rb_ary_assoc(VALUE ary, VALUE key)
* call-seq:
* array.rassoc(obj) -> found_array or nil
*
- * Returns the first element in +self+ that is an \Array
+ * Returns the first element in +self+ that is an +Array+
* whose second element <tt>==</tt> +obj+:
*
* a = [{foo: 0}, [2, 4], [4, 5, 6], [4, 5]]
@@ -5167,7 +5068,7 @@ rb_ary_rassoc(VALUE ary, VALUE value)
VALUE v;
for (i = 0; i < RARRAY_LEN(ary); ++i) {
- v = RARRAY_AREF(ary, i);
+ v = rb_check_array_type(RARRAY_AREF(ary, i));
if (RB_TYPE_P(v, T_ARRAY) &&
RARRAY_LEN(v) > 1 &&
rb_equal(RARRAY_AREF(v, 1), value))
@@ -5239,7 +5140,7 @@ rb_ary_equal(VALUE ary1, VALUE ary2)
return rb_equal(ary2, ary1);
}
if (RARRAY_LEN(ary1) != RARRAY_LEN(ary2)) return Qfalse;
- if (RARRAY_CONST_PTR_TRANSIENT(ary1) == RARRAY_CONST_PTR_TRANSIENT(ary2)) return Qtrue;
+ if (RARRAY_CONST_PTR(ary1) == RARRAY_CONST_PTR(ary2)) return Qtrue;
return rb_exec_recursive_paired(recursive_equal, ary1, ary2, ary2);
}
@@ -5258,10 +5159,10 @@ recursive_eql(VALUE ary1, VALUE ary2, int recur)
/*
* call-seq:
- * array.eql? other_array -> true or false
+ * array.eql?(other_array) -> true or false
*
* Returns +true+ if +self+ and +other_array+ are the same size,
- * and if, for each index +i+ in +self+, <tt>self[i].eql? other_array[i]</tt>:
+ * and if, for each index +i+ in +self+, <tt>self[i].eql?(other_array[i])</tt>:
*
* a0 = [:foo, 'bar', 2]
* a1 = [:foo, 'bar', 2]
@@ -5279,7 +5180,7 @@ rb_ary_eql(VALUE ary1, VALUE ary2)
if (ary1 == ary2) return Qtrue;
if (!RB_TYPE_P(ary2, T_ARRAY)) return Qfalse;
if (RARRAY_LEN(ary1) != RARRAY_LEN(ary2)) return Qfalse;
- if (RARRAY_CONST_PTR_TRANSIENT(ary1) == RARRAY_CONST_PTR_TRANSIENT(ary2)) return Qtrue;
+ if (RARRAY_CONST_PTR(ary1) == RARRAY_CONST_PTR(ary2)) return Qtrue;
return rb_exec_recursive_paired(recursive_eql, ary1, ary2, ary2);
}
@@ -5306,7 +5207,7 @@ rb_ary_hash_values(long len, const VALUE *elements)
*
* Returns the integer hash value for +self+.
*
- * Two arrays with the same content will have the same hash code (and will compare using eql?):
+ * Two arrays with the same content will have the same hash code (and will compare using #eql?):
*
* [0, 1, 2].hash == [0, 1, 2].hash # => true
* [0, 1, 2].hash == [0, 1, 3].hash # => false
@@ -5480,8 +5381,8 @@ ary_make_hash_by(VALUE ary)
* call-seq:
* array - other_array -> new_array
*
- * Returns a new \Array containing only those elements from +array+
- * that are not found in \Array +other_array+;
+ * Returns a new +Array+ containing only those elements from +array+
+ * that are not found in +Array+ +other_array+;
* items are compared using <tt>eql?</tt>;
* the order from +array+ is preserved:
*
@@ -5525,7 +5426,7 @@ rb_ary_diff(VALUE ary1, VALUE ary2)
* call-seq:
* array.difference(*other_arrays) -> new_array
*
- * Returns a new \Array containing only those elements from +self+
+ * Returns a new +Array+ containing only those elements from +self+
* that are not found in any of the Arrays +other_arrays+;
* items are compared using <tt>eql?</tt>; order from +self+ is preserved:
*
@@ -5579,7 +5480,7 @@ rb_ary_difference_multi(int argc, VALUE *argv, VALUE ary)
* call-seq:
* array & other_array -> new_array
*
- * Returns a new \Array containing each element found in both +array+ and \Array +other_array+;
+ * Returns a new +Array+ containing each element found in both +array+ and +Array+ +other_array+;
* duplicates are omitted; items are compared using <tt>eql?</tt>
* (items must also implement +hash+ correctly):
*
@@ -5632,7 +5533,7 @@ rb_ary_and(VALUE ary1, VALUE ary2)
* call-seq:
* array.intersection(*other_arrays) -> new_array
*
- * Returns a new \Array containing each element found both in +self+
+ * Returns a new +Array+ containing each element found both in +self+
* and in all of the given Arrays +other_arrays+;
* duplicates are omitted; items are compared using <tt>eql?</tt>
* (items must also implement +hash+ correctly):
@@ -5697,7 +5598,7 @@ rb_ary_union_hash(VALUE hash, VALUE ary2)
* call-seq:
* array | other_array -> new_array
*
- * Returns the union of +array+ and \Array +other_array+;
+ * Returns the union of +array+ and +Array+ +other_array+;
* duplicates are removed; order is preserved;
* items are compared using <tt>eql?</tt>:
*
@@ -5731,7 +5632,7 @@ rb_ary_or(VALUE ary1, VALUE ary2)
* call-seq:
* array.union(*other_arrays) -> new_array
*
- * Returns a new \Array that is the union of +self+ and all given Arrays +other_arrays+;
+ * Returns a new +Array+ that is the union of +self+ and all given Arrays +other_arrays+;
* duplicates are removed; order is preserved; items are compared using <tt>eql?</tt>:
*
* [0, 1, 2, 3].union([4, 5], [6, 7]) # => [0, 1, 2, 3, 4, 5, 6, 7]
@@ -5784,7 +5685,7 @@ rb_ary_union_multi(int argc, VALUE *argv, VALUE ary)
* a.intersect?(b) #=> true
* a.intersect?(c) #=> false
*
- * Array elements are compared using <tt>eql?</tt>
+ * +Array+ elements are compared using <tt>eql?</tt>
* (items must also implement +hash+ correctly).
*/
@@ -5927,30 +5828,30 @@ ary_max_opt_string(VALUE ary, long i, VALUE vmax)
* Returns one of the following:
*
* - The maximum-valued element from +self+.
- * - A new \Array of maximum-valued elements selected from +self+.
+ * - A new +Array+ of maximum-valued elements selected from +self+.
*
* When no block is given, each element in +self+ must respond to method <tt><=></tt>
- * with an \Integer.
+ * with an Integer.
*
* With no argument and no block, returns the element in +self+
* having the maximum value per method <tt><=></tt>:
*
* [0, 1, 2].max # => 2
*
- * With an argument \Integer +n+ and no block, returns a new \Array with at most +n+ elements,
+ * With an argument Integer +n+ and no block, returns a new +Array+ with at most +n+ elements,
* in descending order per method <tt><=></tt>:
*
* [0, 1, 2, 3].max(3) # => [3, 2, 1]
* [0, 1, 2, 3].max(6) # => [3, 2, 1, 0]
*
- * When a block is given, the block must return an \Integer.
+ * When a block is given, the block must return an Integer.
*
* With a block and no argument, calls the block <tt>self.size-1</tt> times to compare elements;
* returns the element having the maximum value per the block:
*
* ['0', '00', '000'].max {|a, b| a.size <=> b.size } # => "000"
*
- * With an argument +n+ and a block, returns a new \Array with at most +n+ elements,
+ * With an argument +n+ and a block, returns a new +Array+ with at most +n+ elements,
* in descending order per the block:
*
* ['0', '00', '000'].max(2) {|a, b| a.size <=> b.size } # => ["000", "00"]
@@ -6095,17 +5996,17 @@ ary_min_opt_string(VALUE ary, long i, VALUE vmin)
* Returns one of the following:
*
* - The minimum-valued element from +self+.
- * - A new \Array of minimum-valued elements selected from +self+.
+ * - A new +Array+ of minimum-valued elements selected from +self+.
*
* When no block is given, each element in +self+ must respond to method <tt><=></tt>
- * with an \Integer.
+ * with an Integer.
*
* With no argument and no block, returns the element in +self+
* having the minimum value per method <tt><=></tt>:
*
* [0, 1, 2].min # => 0
*
- * With \Integer argument +n+ and no block, returns a new \Array with at most +n+ elements,
+ * With Integer argument +n+ and no block, returns a new +Array+ with at most +n+ elements,
* in ascending order per method <tt><=></tt>:
*
* [0, 1, 2, 3].min(3) # => [0, 1, 2]
@@ -6118,7 +6019,7 @@ ary_min_opt_string(VALUE ary, long i, VALUE vmin)
*
* ['0', '00', '000'].min { |a, b| a.size <=> b.size } # => "0"
*
- * With an argument +n+ and a block, returns a new \Array with at most +n+ elements,
+ * With an argument +n+ and a block, returns a new +Array+ with at most +n+ elements,
* in ascending order per the block:
*
* ['0', '00', '000'].min(2) {|a, b| a.size <=> b.size } # => ["0", "00"]
@@ -6169,19 +6070,19 @@ rb_ary_min(int argc, VALUE *argv, VALUE ary)
* array.minmax -> [min_val, max_val]
* array.minmax {|a, b| ... } -> [min_val, max_val]
*
- * Returns a new 2-element \Array containing the minimum and maximum values
+ * Returns a new 2-element +Array+ containing the minimum and maximum values
* from +self+, either per method <tt><=></tt> or per a given block:.
*
* When no block is given, each element in +self+ must respond to method <tt><=></tt>
- * with an \Integer;
- * returns a new 2-element \Array containing the minimum and maximum values
+ * with an Integer;
+ * returns a new 2-element +Array+ containing the minimum and maximum values
* from +self+, per method <tt><=></tt>:
*
* [0, 1, 2].minmax # => [0, 2]
*
- * When a block is given, the block must return an \Integer;
+ * When a block is given, the block must return an Integer;
* the block is called <tt>self.size-1</tt> times to compare elements;
- * returns a new 2-element \Array containing the minimum and maximum values
+ * returns a new 2-element +Array+ containing the minimum and maximum values
* from +self+, per the block:
*
* ['0', '00', '000'].minmax {|a, b| a.size <=> b.size } # => ["0", "000"]
@@ -6267,7 +6168,7 @@ rb_ary_uniq_bang(VALUE ary)
* array.uniq -> new_array
* array.uniq {|element| ... } -> new_array
*
- * Returns a new \Array containing those elements from +self+ that are not duplicates,
+ * Returns a new +Array+ containing those elements from +self+ that are not duplicates,
* the first occurrence always being retained.
*
* With no block given, identifies and omits duplicates using method <tt>eql?</tt>
@@ -6322,14 +6223,14 @@ rb_ary_compact_bang(VALUE ary)
long n;
rb_ary_modify(ary);
- p = t = (VALUE *)RARRAY_CONST_PTR_TRANSIENT(ary); /* WB: no new reference */
+ p = t = (VALUE *)RARRAY_CONST_PTR(ary); /* WB: no new reference */
end = p + RARRAY_LEN(ary);
while (t < end) {
if (NIL_P(*t)) t++;
else *p++ = *t++;
}
- n = p - RARRAY_CONST_PTR_TRANSIENT(ary);
+ n = p - RARRAY_CONST_PTR(ary);
if (RARRAY_LEN(ary) == n) {
return Qnil;
}
@@ -6342,7 +6243,7 @@ rb_ary_compact_bang(VALUE ary)
* call-seq:
* array.compact -> new_array
*
- * Returns a new \Array containing all non-+nil+ elements from +self+:
+ * Returns a new +Array+ containing all non-+nil+ elements from +self+:
*
* a = [nil, 0, nil, 1, nil, 2, nil]
* a.compact # => [0, 1, 2]
@@ -6416,16 +6317,9 @@ rb_ary_count(int argc, VALUE *argv, VALUE ary)
static VALUE
flatten(VALUE ary, int level)
{
- static const rb_data_type_t flatten_memo_data_type = {
- .wrap_struct_name = "array_flatten_memo_data_type",
- .function = { NULL, (RUBY_DATA_FUNC)st_free_table },
- NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
- };
-
long i;
- VALUE stack, result, tmp = 0, elt, vmemo;
- st_table *memo = 0;
- st_data_t id;
+ VALUE stack, result, tmp = 0, elt;
+ VALUE memo = Qfalse;
for (i = 0; i < RARRAY_LEN(ary); i++) {
elt = RARRAY_AREF(ary, i);
@@ -6439,7 +6333,7 @@ flatten(VALUE ary, int level)
}
result = ary_new(0, RARRAY_LEN(ary));
- ary_memcpy(result, 0, i, RARRAY_CONST_PTR_TRANSIENT(ary));
+ ary_memcpy(result, 0, i, RARRAY_CONST_PTR(ary));
ARY_SET_LEN(result, i);
stack = ary_new(0, ARY_DEFAULT_SIZE);
@@ -6447,10 +6341,9 @@ flatten(VALUE ary, int level)
rb_ary_push(stack, LONG2NUM(i + 1));
if (level < 0) {
- memo = st_init_numtable();
- vmemo = TypedData_Wrap_Struct(0, &flatten_memo_data_type, memo);
- st_insert(memo, (st_data_t)ary, (st_data_t)Qtrue);
- st_insert(memo, (st_data_t)tmp, (st_data_t)Qtrue);
+ memo = rb_obj_hide(rb_ident_hash_new());
+ rb_hash_aset(memo, ary, Qtrue);
+ rb_hash_aset(memo, tmp, Qtrue);
}
ary = tmp;
@@ -6465,9 +6358,8 @@ flatten(VALUE ary, int level)
}
tmp = rb_check_array_type(elt);
if (RBASIC(result)->klass) {
- if (memo) {
- RB_GC_GUARD(vmemo);
- st_clear(memo);
+ if (RTEST(memo)) {
+ rb_hash_clear(memo);
}
rb_raise(rb_eRuntimeError, "flatten reentered");
}
@@ -6476,12 +6368,11 @@ flatten(VALUE ary, int level)
}
else {
if (memo) {
- id = (st_data_t)tmp;
- if (st_is_member(memo, id)) {
- st_clear(memo);
+ if (rb_hash_aref(memo, tmp) == Qtrue) {
+ rb_hash_clear(memo);
rb_raise(rb_eArgError, "tried to flatten recursive array");
}
- st_insert(memo, id, (st_data_t)Qtrue);
+ rb_hash_aset(memo, tmp, Qtrue);
}
rb_ary_push(stack, ary);
rb_ary_push(stack, LONG2NUM(i));
@@ -6493,8 +6384,7 @@ flatten(VALUE ary, int level)
break;
}
if (memo) {
- id = (st_data_t)ary;
- st_delete(memo, &id, 0);
+ rb_hash_delete(memo, ary);
}
tmp = rb_ary_pop(stack);
i = NUM2LONG(tmp);
@@ -6502,7 +6392,7 @@ flatten(VALUE ary, int level)
}
if (memo) {
- st_clear(memo);
+ rb_hash_clear(memo);
}
RBASIC_SET_CLASS(result, rb_cArray);
@@ -6514,10 +6404,10 @@ flatten(VALUE ary, int level)
* array.flatten! -> self or nil
* array.flatten!(level) -> self or nil
*
- * Replaces each nested \Array in +self+ with the elements from that \Array;
+ * Replaces each nested +Array+ in +self+ with the elements from that +Array+;
* returns +self+ if any changes, +nil+ otherwise.
*
- * With non-negative \Integer argument +level+, flattens recursively through +level+ levels:
+ * With non-negative Integer argument +level+, flattens recursively through +level+ levels:
*
* a = [ 0, [ 1, [2, 3], 4 ], 5 ]
* a.flatten!(1) # => [0, 1, [2, 3], 4, 5]
@@ -6567,11 +6457,11 @@ rb_ary_flatten_bang(int argc, VALUE *argv, VALUE ary)
* array.flatten -> new_array
* array.flatten(level) -> new_array
*
- * Returns a new \Array that is a recursive flattening of +self+:
+ * Returns a new +Array+ that is a recursive flattening of +self+:
* - Each non-Array element is unchanged.
- * - Each \Array is replaced by its individual elements.
+ * - Each +Array+ is replaced by its individual elements.
*
- * With non-negative \Integer argument +level+, flattens recursively through +level+ levels:
+ * With non-negative Integer argument +level+, flattens recursively through +level+ levels:
*
* a = [ 0, [ 1, [2, 3], 4 ], 5 ]
* a.flatten(0) # => [0, [1, [2, 3], 4], 5]
@@ -6627,7 +6517,7 @@ rb_ary_shuffle_bang(rb_execution_context_t *ec, VALUE ary, VALUE randgen)
while (i) {
long j = RAND_UPTO(i);
VALUE tmp;
- if (len != RARRAY_LEN(ary) || ptr != RARRAY_CONST_PTR_TRANSIENT(ary)) {
+ if (len != RARRAY_LEN(ary) || ptr != RARRAY_CONST_PTR(ary)) {
rb_raise(rb_eRuntimeError, "modified during shuffle");
}
tmp = ptr[--i];
@@ -6719,7 +6609,7 @@ ary_sample(rb_execution_context_t *ec, VALUE ary, VALUE randgen, VALUE nv, VALUE
sorted[j] = idx[i] = k;
}
result = rb_ary_new_capa(n);
- RARRAY_PTR_USE_TRANSIENT(result, ptr_result, {
+ RARRAY_PTR_USE(result, ptr_result, {
for (i=0; i<n; i++) {
ptr_result[i] = RARRAY_AREF(ary, idx[i]);
}
@@ -6742,7 +6632,7 @@ ary_sample(rb_execution_context_t *ec, VALUE ary, VALUE randgen, VALUE nv, VALUE
len = RARRAY_LEN(ary);
if (len <= max_idx) n = 0;
else if (n > len) n = len;
- RARRAY_PTR_USE_TRANSIENT(ary, ptr_ary, {
+ RARRAY_PTR_USE(ary, ptr_ary, {
for (i=0; i<n; i++) {
long j2 = j = ptr_result[i];
long i2 = i;
@@ -6756,6 +6646,7 @@ ary_sample(rb_execution_context_t *ec, VALUE ary, VALUE randgen, VALUE nv, VALUE
});
DATA_PTR(vmemo) = 0;
st_free_table(memo);
+ RB_GC_GUARD(vmemo);
}
else {
result = rb_ary_dup(ary);
@@ -6805,7 +6696,7 @@ rb_ary_cycle_size(VALUE self, VALUE args, VALUE eobj)
* array.cycle -> new_enumerator
* array.cycle(count) -> new_enumerator
*
- * When called with positive \Integer argument +count+ and a block,
+ * When called with positive Integer argument +count+ and a block,
* calls the block with each element, then does so again,
* until it has done so +count+ times; returns +nil+:
*
@@ -6824,7 +6715,7 @@ rb_ary_cycle_size(VALUE self, VALUE args, VALUE eobj)
* [0, 1].cycle {|element| puts element }
* [0, 1].cycle(nil) {|element| puts element }
*
- * When no block is given, returns a new \Enumerator:
+ * When no block is given, returns a new Enumerator:
*
* [0, 1].cycle(2) # => #<Enumerator: [0, 1]:cycle(2)>
* [0, 1].cycle # => # => #<Enumerator: [0, 1]:cycle>
@@ -6981,7 +6872,7 @@ rb_ary_permutation_size(VALUE ary, VALUE args, VALUE eobj)
* When invoked with a block, yield all permutations of elements of +self+; returns +self+.
* The order of permutations is indeterminate.
*
- * When a block and an in-range positive \Integer argument +n+ (<tt>0 < n <= self.size</tt>)
+ * When a block and an in-range positive Integer argument +n+ (<tt>0 < n <= self.size</tt>)
* are given, calls the block with all +n+-tuple permutations of +self+.
*
* Example:
@@ -7012,7 +6903,7 @@ rb_ary_permutation_size(VALUE ary, VALUE args, VALUE eobj)
* [2, 0, 1]
* [2, 1, 0]
*
- * When +n+ is zero, calls the block once with a new empty \Array:
+ * When +n+ is zero, calls the block once with a new empty +Array+:
*
* a = [0, 1, 2]
* a.permutation(0) {|permutation| p permutation }
@@ -7043,7 +6934,7 @@ rb_ary_permutation_size(VALUE ary, VALUE args, VALUE eobj)
* [2, 0, 1]
* [2, 1, 0]
*
- * Returns a new \Enumerator if no block given:
+ * Returns a new Enumerator if no block given:
*
* a = [0, 1, 2]
* a.permutation # => #<Enumerator: [0, 1, 2]:permutation>
@@ -7127,7 +7018,7 @@ rb_ary_combination_size(VALUE ary, VALUE args, VALUE eobj)
* Calls the block, if given, with combinations of elements of +self+;
* returns +self+. The order of combinations is indeterminate.
*
- * When a block and an in-range positive \Integer argument +n+ (<tt>0 < n <= self.size</tt>)
+ * When a block and an in-range positive Integer argument +n+ (<tt>0 < n <= self.size</tt>)
* are given, calls the block with all +n+-tuple combinations of +self+.
*
* Example:
@@ -7150,7 +7041,7 @@ rb_ary_combination_size(VALUE ary, VALUE args, VALUE eobj)
*
* [0, 1, 2]
*
- * When +n+ is zero, calls the block once with a new empty \Array:
+ * When +n+ is zero, calls the block once with a new empty +Array+:
*
* a = [0, 1, 2]
* a1 = a.combination(0) {|combination| p combination }
@@ -7166,7 +7057,7 @@ rb_ary_combination_size(VALUE ary, VALUE args, VALUE eobj)
* a.combination(-1) {|combination| fail 'Cannot happen' }
* a.combination(4) {|combination| fail 'Cannot happen' }
*
- * Returns a new \Enumerator if no block given:
+ * Returns a new Enumerator if no block given:
*
* a = [0, 1, 2]
* a.combination(2) # => #<Enumerator: [0, 1, 2]:combination(2)>
@@ -7261,10 +7152,10 @@ rb_ary_repeated_permutation_size(VALUE ary, VALUE args, VALUE eobj)
* array.repeated_permutation(n) -> new_enumerator
*
* Calls the block with each repeated permutation of length +n+ of the elements of +self+;
- * each permutation is an \Array;
+ * each permutation is an +Array+;
* returns +self+. The order of the permutations is indeterminate.
*
- * When a block and a positive \Integer argument +n+ are given, calls the block with each
+ * When a block and a positive Integer argument +n+ are given, calls the block with each
* +n+-tuple repeated permutation of the elements of +self+.
* The number of permutations is <tt>self.size**n</tt>.
*
@@ -7295,13 +7186,13 @@ rb_ary_repeated_permutation_size(VALUE ary, VALUE args, VALUE eobj)
* [2, 1]
* [2, 2]
*
- * If +n+ is zero, calls the block once with an empty \Array.
+ * If +n+ is zero, calls the block once with an empty +Array+.
*
* If +n+ is negative, does not call the block:
*
* a.repeated_permutation(-1) {|permutation| fail 'Cannot happen' }
*
- * Returns a new \Enumerator if no block given:
+ * Returns a new Enumerator if no block given:
*
* a = [0, 1, 2]
* a.repeated_permutation(2) # => #<Enumerator: [0, 1, 2]:permutation(2)>
@@ -7393,10 +7284,10 @@ rb_ary_repeated_combination_size(VALUE ary, VALUE args, VALUE eobj)
* array.repeated_combination(n) -> new_enumerator
*
* Calls the block with each repeated combination of length +n+ of the elements of +self+;
- * each combination is an \Array;
+ * each combination is an +Array+;
* returns +self+. The order of the combinations is indeterminate.
*
- * When a block and a positive \Integer argument +n+ are given, calls the block with each
+ * When a block and a positive Integer argument +n+ are given, calls the block with each
* +n+-tuple repeated combination of the elements of +self+.
* The number of combinations is <tt>(n+1)(n+2)/2</tt>.
*
@@ -7424,13 +7315,13 @@ rb_ary_repeated_combination_size(VALUE ary, VALUE args, VALUE eobj)
* [1, 2]
* [2, 2]
*
- * If +n+ is zero, calls the block once with an empty \Array.
+ * If +n+ is zero, calls the block once with an empty +Array+.
*
* If +n+ is negative, does not call the block:
*
* a.repeated_combination(-1) {|combination| fail 'Cannot happen' }
*
- * Returns a new \Enumerator if no block given:
+ * Returns a new Enumerator if no block given:
*
* a = [0, 1, 2]
* a.repeated_combination(2) # => #<Enumerator: [0, 1, 2]:combination(2)>
@@ -7497,7 +7388,7 @@ rb_ary_repeated_combination(VALUE ary, VALUE num)
* including both +self+ and +other_arrays+.
* - The order of the returned combinations is indeterminate.
*
- * When no block is given, returns the combinations as an \Array of Arrays:
+ * When no block is given, returns the combinations as an +Array+ of Arrays:
*
* a = [0, 1, 2]
* a1 = [3, 4]
@@ -7509,14 +7400,14 @@ rb_ary_repeated_combination(VALUE ary, VALUE num)
* p.size # => 12 # a.size * a1.size * a2.size
* p # => [[0, 3, 5], [0, 3, 6], [0, 4, 5], [0, 4, 6], [1, 3, 5], [1, 3, 6], [1, 4, 5], [1, 4, 6], [2, 3, 5], [2, 3, 6], [2, 4, 5], [2, 4, 6]]
*
- * If any argument is an empty \Array, returns an empty \Array.
+ * If any argument is an empty +Array+, returns an empty +Array+.
*
- * If no argument is given, returns an \Array of 1-element Arrays,
+ * If no argument is given, returns an +Array+ of 1-element Arrays,
* each containing an element of +self+:
*
* a.product # => [[0], [1], [2]]
*
- * When a block is given, yields each combination as an \Array; returns +self+:
+ * When a block is given, yields each combination as an +Array+; returns +self+:
*
* a.product(a1) {|combination| p combination }
*
@@ -7529,11 +7420,11 @@ rb_ary_repeated_combination(VALUE ary, VALUE num)
* [2, 3]
* [2, 4]
*
- * If any argument is an empty \Array, does not call the block:
+ * If any argument is an empty +Array+, does not call the block:
*
* a.product(a1, a2, []) {|combination| fail 'Cannot happen' }
*
- * If no argument is given, yields each element of +self+ as a 1-element \Array:
+ * If no argument is given, yields each element of +self+ as a 1-element +Array+:
*
* a.product {|combination| p combination }
*
@@ -7637,8 +7528,8 @@ done:
* call-seq:
* array.take(n) -> new_array
*
- * Returns a new \Array containing the first +n+ element of +self+,
- * where +n+ is a non-negative \Integer;
+ * Returns a new +Array+ containing the first +n+ element of +self+,
+ * where +n+ is a non-negative Integer;
* does not modify +self+.
*
* Examples:
@@ -7666,19 +7557,19 @@ rb_ary_take(VALUE obj, VALUE n)
* array.take_while {|element| ... } -> new_array
* array.take_while -> new_enumerator
*
- * Returns a new \Array containing zero or more leading elements of +self+;
+ * Returns a new +Array+ containing zero or more leading elements of +self+;
* does not modify +self+.
*
* With a block given, calls the block with each successive element of +self+;
* stops if the block returns +false+ or +nil+;
- * returns a new \Array containing those elements for which the block returned a truthy value:
+ * returns a new +Array+ containing those elements for which the block returned a truthy value:
*
* a = [0, 1, 2, 3, 4, 5]
* a.take_while {|element| element < 3 } # => [0, 1, 2]
* a.take_while {|element| true } # => [0, 1, 2, 3, 4, 5]
* a # => [0, 1, 2, 3, 4, 5]
*
- * With no block given, returns a new \Enumerator:
+ * With no block given, returns a new Enumerator:
*
* [0, 1].take_while # => #<Enumerator: [0, 1]:take_while>
*
@@ -7700,8 +7591,8 @@ rb_ary_take_while(VALUE ary)
* call-seq:
* array.drop(n) -> new_array
*
- * Returns a new \Array containing all but the first +n+ element of +self+,
- * where +n+ is a non-negative \Integer;
+ * Returns a new +Array+ containing all but the first +n+ element of +self+,
+ * where +n+ is a non-negative Integer;
* does not modify +self+.
*
* Examples:
@@ -7732,17 +7623,17 @@ rb_ary_drop(VALUE ary, VALUE n)
* array.drop_while {|element| ... } -> new_array
* array.drop_while -> new_enumerator
- * Returns a new \Array containing zero or more trailing elements of +self+;
+ * Returns a new +Array+ containing zero or more trailing elements of +self+;
* does not modify +self+.
*
* With a block given, calls the block with each successive element of +self+;
* stops if the block returns +false+ or +nil+;
- * returns a new \Array _omitting_ those elements for which the block returned a truthy value:
+ * returns a new +Array+ _omitting_ those elements for which the block returned a truthy value:
*
* a = [0, 1, 2, 3, 4, 5]
* a.drop_while {|element| element < 3 } # => [3, 4, 5]
*
- * With no block given, returns a new \Enumerator:
+ * With no block given, returns a new Enumerator:
*
* [0, 1].drop_while # => # => #<Enumerator: [0, 1]:drop_while>
*
@@ -8099,7 +7990,7 @@ finish_exact_sum(long n, VALUE r, VALUE v, int z)
* Notes:
*
* - Array#join and Array#flatten may be faster than Array#sum
- * for an \Array of Strings or an \Array of Arrays.
+ * for an +Array+ of Strings or an +Array+ of Arrays.
* - Array#sum method may not respect method redefinition of "+" methods such as Integer#+.
*
*/
@@ -8221,6 +8112,7 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary)
return v;
}
+/* :nodoc: */
static VALUE
rb_ary_deconstruct(VALUE ary)
{
@@ -8228,13 +8120,13 @@ rb_ary_deconstruct(VALUE ary)
}
/*
- * An \Array is an ordered, integer-indexed collection of objects, called _elements_.
+ * An +Array+ is an ordered, integer-indexed collection of objects, called _elements_.
* Any object (even another array) may be an array element,
* and an array can contain objects of different types.
*
- * == \Array Indexes
+ * == +Array+ Indexes
*
- * \Array indexing starts at 0, as in C or Java.
+ * +Array+ indexing starts at 0, as in C or Java.
*
* A positive index is an offset from the first element:
*
@@ -8261,14 +8153,14 @@ rb_ary_deconstruct(VALUE ary)
* - Index -4 is out of range.
*
* Although the effective index into an array is always an integer,
- * some methods (both within and outside of class \Array)
+ * some methods (both within and outside of class +Array+)
* accept one or more non-integer arguments that are
* {integer-convertible objects}[rdoc-ref:implicit_conversion.rdoc@Integer-Convertible+Objects].
*
*
* == Creating Arrays
*
- * You can create an \Array object explicitly with:
+ * You can create an +Array+ object explicitly with:
*
* - An {array literal}[rdoc-ref:literals.rdoc@Array+Literals]:
*
@@ -8349,7 +8241,7 @@ rb_ary_deconstruct(VALUE ary)
* == Example Usage
*
* In addition to the methods it mixes in through the Enumerable module, the
- * \Array class has proprietary methods for accessing, searching and otherwise
+ * +Array+ class has proprietary methods for accessing, searching and otherwise
* manipulating arrays.
*
* Some of the more common ones are illustrated below.
@@ -8397,7 +8289,7 @@ rb_ary_deconstruct(VALUE ary)
*
* arr.drop(3) #=> [4, 5, 6]
*
- * == Obtaining Information about an \Array
+ * == Obtaining Information about an +Array+
*
* Arrays keep track of their own length at all times. To query an array
* about the number of elements it contains, use #length, #count or #size.
@@ -8435,7 +8327,7 @@ rb_ary_deconstruct(VALUE ary)
* arr.insert(3, 'orange', 'pear', 'grapefruit')
* #=> [0, 1, 2, "orange", "pear", "grapefruit", "apple", 3, 4, 5, 6]
*
- * == Removing Items from an \Array
+ * == Removing Items from an +Array+
*
* The method #pop removes the last element in an array and returns it:
*
@@ -8477,9 +8369,9 @@ rb_ary_deconstruct(VALUE ary)
*
* == Iterating over Arrays
*
- * Like all classes that include the Enumerable module, \Array has an each
+ * Like all classes that include the Enumerable module, +Array+ has an each
* method, which defines what elements should be iterated over and how. In
- * case of Array's #each, all elements in the \Array instance are yielded to
+ * case of Array's #each, all elements in the +Array+ instance are yielded to
* the supplied block in sequence.
*
* Note that this operation leaves the array unchanged.
@@ -8506,7 +8398,7 @@ rb_ary_deconstruct(VALUE ary)
* arr #=> [1, 4, 9, 16, 25]
*
*
- * == Selecting Items from an \Array
+ * == Selecting Items from an +Array+
*
* Elements can be selected from an array according to criteria defined in a
* block. The selection can happen in a destructive or a non-destructive
@@ -8539,13 +8431,13 @@ rb_ary_deconstruct(VALUE ary)
*
* == What's Here
*
- * First, what's elsewhere. \Class \Array:
+ * First, what's elsewhere. \Class +Array+:
*
* - Inherits from {class Object}[rdoc-ref:Object@What-27s+Here].
* - Includes {module Enumerable}[rdoc-ref:Enumerable@What-27s+Here],
* which provides dozens of additional methods.
*
- * Here, class \Array provides methods that are useful for:
+ * Here, class +Array+ provides methods that are useful for:
*
* - {Creating an Array}[rdoc-ref:Array@Methods+for+Creating+an+Array]
* - {Querying}[rdoc-ref:Array@Methods+for+Querying]
@@ -8558,7 +8450,7 @@ rb_ary_deconstruct(VALUE ary)
* - {Converting}[rdoc-ref:Array@Methods+for+Converting]
* - {And more....}[rdoc-ref:Array@Other+Methods]
*
- * === Methods for Creating an \Array
+ * === Methods for Creating an +Array+
*
* - ::[]: Returns a new array populated with given objects.
* - ::new: Returns a new array.
@@ -8761,7 +8653,6 @@ Init_Array(void)
rb_define_method(rb_cArray, "unshift", rb_ary_unshift_m, -1);
rb_define_alias(rb_cArray, "prepend", "unshift");
rb_define_method(rb_cArray, "insert", rb_ary_insert, -1);
- rb_define_method(rb_cArray, "each", rb_ary_each, 0);
rb_define_method(rb_cArray, "each_index", rb_ary_each_index, 0);
rb_define_method(rb_cArray, "reverse_each", rb_ary_reverse_each, 0);
rb_define_method(rb_cArray, "length", rb_ary_length, 0);
diff --git a/array.rb b/array.rb
index 358b6a5d79..f63ff00056 100644
--- a/array.rb
+++ b/array.rb
@@ -1,5 +1,62 @@
class Array
# call-seq:
+ # array.each {|element| ... } -> self
+ # array.each -> Enumerator
+ #
+ # Iterates over array elements.
+ #
+ # When a block given, passes each successive array element to the block;
+ # returns +self+:
+ #
+ # a = [:foo, 'bar', 2]
+ # a.each {|element| puts "#{element.class} #{element}" }
+ #
+ # Output:
+ #
+ # Symbol foo
+ # String bar
+ # Integer 2
+ #
+ # Allows the array to be modified during iteration:
+ #
+ # a = [:foo, 'bar', 2]
+ # a.each {|element| puts element; a.clear if element.to_s.start_with?('b') }
+ #
+ # Output:
+ #
+ # foo
+ # bar
+ #
+ # When no block given, returns a new Enumerator:
+ # a = [:foo, 'bar', 2]
+ #
+ # e = a.each
+ # e # => #<Enumerator: [:foo, "bar", 2]:each>
+ # a1 = e.each {|element| puts "#{element.class} #{element}" }
+ #
+ # Output:
+ #
+ # Symbol foo
+ # String bar
+ # Integer 2
+ #
+ # Related: #each_index, #reverse_each.
+ def each
+ Primitive.attr! :inline_block
+ Primitive.attr! :use_block
+
+ unless defined?(yield)
+ return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 0, 0, ary_enum_length)'
+ end
+ _i = 0
+ value = nil
+ while Primitive.cexpr!(%q{ ary_fetch_next(self, LOCAL_PTR(_i), LOCAL_PTR(value)) })
+ yield value
+ end
+ self
+ end
+
+ # call-seq:
# array.shuffle!(random: Random) -> array
#
# Shuffles the elements of +self+ in place.
@@ -39,7 +96,7 @@ class Array
# a.sample # => 8
# If +self+ is empty, returns +nil+.
#
- # When argument +n+ is given, returns a new \Array containing +n+ random
+ # When argument +n+ is given, returns a new +Array+ containing +n+ random
# elements from +self+:
# a.sample(3) # => [8, 9, 2]
# a.sample(6) # => [9, 6, 10, 3, 1, 4]
@@ -51,7 +108,7 @@ class Array
# a.sample(a.size * 2) # => [1, 1, 3, 2, 1, 2]
# The argument +n+ must be a non-negative numeric value.
# The order of the result array is unrelated to the order of +self+.
- # Returns a new empty \Array if +self+ is empty.
+ # Returns a new empty +Array+ if +self+ is empty.
#
# The optional +random+ argument will be used as the random number generator:
# a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
@@ -81,8 +138,8 @@ class Array
#
# If +self+ is empty, returns +nil+.
#
- # When non-negative \Integer argument +n+ is given,
- # returns the first +n+ elements in a new \Array:
+ # When non-negative Integer argument +n+ is given,
+ # returns the first +n+ elements in a new +Array+:
#
# a = [:foo, 'bar', 2]
# a.first(2) # => [:foo, "bar"]
@@ -92,7 +149,7 @@ class Array
# a = [:foo, 'bar', 2]
# a.first(50) # => [:foo, "bar", 2]
#
- # If <tt>n == 0</tt> returns an new empty \Array:
+ # If <tt>n == 0</tt> returns an new empty +Array+:
#
# a = [:foo, 'bar', 2]
# a.first(0) # []
@@ -125,8 +182,8 @@ class Array
#
# If +self+ is empty, returns +nil+.
#
- # When non-negative \Integer argument +n+ is given,
- # returns the last +n+ elements in a new \Array:
+ # When non-negative Integer argument +n+ is given,
+ # returns the last +n+ elements in a new +Array+:
#
# a = [:foo, 'bar', 2]
# a.last(2) # => ["bar", 2]
@@ -136,7 +193,7 @@ class Array
# a = [:foo, 'bar', 2]
# a.last(50) # => [:foo, "bar", 2]
#
- # If <tt>n == 0</tt>, returns an new empty \Array:
+ # If <tt>n == 0</tt>, returns an new empty +Array+:
#
# a = [:foo, 'bar', 2]
# a.last(0) # []
diff --git a/ast.c b/ast.c
index d60e5d3fcf..cc35a69a22 100644
--- a/ast.c
+++ b/ast.c
@@ -1,6 +1,6 @@
/* indent-tabs-mode: nil */
#include "internal.h"
-#include "internal/parse.h"
+#include "internal/ruby_parser.h"
#include "internal/symbol.h"
#include "internal/warnings.h"
#include "iseq.h"
@@ -16,7 +16,7 @@ static VALUE rb_mAST;
static VALUE rb_cNode;
struct ASTNodeData {
- rb_ast_t *ast;
+ VALUE vast;
const NODE *node;
};
@@ -24,14 +24,16 @@ static void
node_gc_mark(void *ptr)
{
struct ASTNodeData *data = (struct ASTNodeData *)ptr;
- rb_gc_mark((VALUE)data->ast);
+ rb_gc_mark(data->vast);
}
static size_t
node_memsize(const void *ptr)
{
struct ASTNodeData *data = (struct ASTNodeData *)ptr;
- return rb_ast_memsize(data->ast);
+ rb_ast_t *ast = rb_ruby_ast_data_get(data->vast);
+
+ return sizeof(struct ASTNodeData) + rb_ast_memsize(ast);
}
static const rb_data_type_t rb_node_type = {
@@ -44,22 +46,22 @@ static const rb_data_type_t rb_node_type = {
static VALUE rb_ast_node_alloc(VALUE klass);
static void
-setup_node(VALUE obj, rb_ast_t *ast, const NODE *node)
+setup_node(VALUE obj, VALUE vast, const NODE *node)
{
struct ASTNodeData *data;
TypedData_Get_Struct(obj, struct ASTNodeData, &rb_node_type, data);
- data->ast = ast;
+ data->vast = vast;
data->node = node;
}
static VALUE
-ast_new_internal(rb_ast_t *ast, const NODE *node)
+ast_new_internal(VALUE vast, const NODE *node)
{
VALUE obj;
obj = rb_ast_node_alloc(rb_cNode);
- setup_node(obj, ast, node);
+ setup_node(obj, vast, node);
return obj;
}
@@ -74,14 +76,16 @@ ast_parse_new(void)
}
static VALUE
-ast_parse_done(rb_ast_t *ast)
+ast_parse_done(VALUE vast)
{
+ rb_ast_t *ast = rb_ruby_ast_data_get(vast);
+
if (!ast->body.root) {
rb_ast_dispose(ast);
rb_exc_raise(GET_EC()->errinfo);
}
- return ast_new_internal(ast, (NODE *)ast->body.root);
+ return ast_new_internal(vast, (NODE *)ast->body.root);
}
static VALUE
@@ -93,15 +97,15 @@ ast_s_parse(rb_execution_context_t *ec, VALUE module, VALUE str, VALUE keep_scri
static VALUE
rb_ast_parse_str(VALUE str, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens)
{
- rb_ast_t *ast = 0;
+ VALUE vast;
StringValue(str);
VALUE vparser = ast_parse_new();
- if (RTEST(keep_script_lines)) rb_parser_keep_script_lines(vparser);
+ if (RTEST(keep_script_lines)) rb_parser_set_script_lines(vparser);
if (RTEST(error_tolerant)) rb_parser_error_tolerant(vparser);
if (RTEST(keep_tokens)) rb_parser_keep_tokens(vparser);
- ast = rb_parser_compile_string_path(vparser, Qnil, str, 1);
- return ast_parse_done(ast);
+ vast = rb_parser_compile_string_path(vparser, Qnil, str, 1);
+ return ast_parse_done(vast);
}
static VALUE
@@ -114,49 +118,35 @@ static VALUE
rb_ast_parse_file(VALUE path, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens)
{
VALUE f;
- rb_ast_t *ast = 0;
+ VALUE vast = Qnil;
rb_encoding *enc = rb_utf8_encoding();
- FilePathValue(path);
f = rb_file_open_str(path, "r");
rb_funcall(f, rb_intern("set_encoding"), 2, rb_enc_from_encoding(enc), rb_str_new_cstr("-"));
VALUE vparser = ast_parse_new();
- if (RTEST(keep_script_lines)) rb_parser_keep_script_lines(vparser);
+ if (RTEST(keep_script_lines)) rb_parser_set_script_lines(vparser);
if (RTEST(error_tolerant)) rb_parser_error_tolerant(vparser);
if (RTEST(keep_tokens)) rb_parser_keep_tokens(vparser);
- ast = rb_parser_compile_file_path(vparser, Qnil, f, 1);
+ vast = rb_parser_compile_file_path(vparser, Qnil, f, 1);
rb_io_close(f);
- return ast_parse_done(ast);
-}
-
-static VALUE
-lex_array(VALUE array, int index)
-{
- VALUE str = rb_ary_entry(array, index);
- if (!NIL_P(str)) {
- StringValue(str);
- if (!rb_enc_asciicompat(rb_enc_get(str))) {
- rb_raise(rb_eArgError, "invalid source encoding");
- }
- }
- return str;
+ return ast_parse_done(vast);
}
static VALUE
rb_ast_parse_array(VALUE array, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens)
{
- rb_ast_t *ast = 0;
+ VALUE vast = Qnil;
array = rb_check_array_type(array);
VALUE vparser = ast_parse_new();
- if (RTEST(keep_script_lines)) rb_parser_keep_script_lines(vparser);
+ if (RTEST(keep_script_lines)) rb_parser_set_script_lines(vparser);
if (RTEST(error_tolerant)) rb_parser_error_tolerant(vparser);
if (RTEST(keep_tokens)) rb_parser_keep_tokens(vparser);
- ast = rb_parser_compile_generic(vparser, lex_array, Qnil, array, 1);
- return ast_parse_done(ast);
+ vast = rb_parser_compile_array(vparser, Qnil, array, 1);
+ return ast_parse_done(vast);
}
-static VALUE node_children(rb_ast_t*, const NODE*);
+static VALUE node_children(VALUE, const NODE*);
static VALUE
node_find(VALUE self, const int node_id)
@@ -168,7 +158,7 @@ node_find(VALUE self, const int node_id)
if (nd_node_id(data->node) == node_id) return self;
- ary = node_children(data->ast, data->node);
+ ary = node_children(data->vast, data->node);
for (i = 0; i < RARRAY_LEN(ary); i++) {
VALUE child = RARRAY_AREF(ary, i);
@@ -185,20 +175,6 @@ node_find(VALUE self, const int node_id)
extern VALUE rb_e_script;
static VALUE
-script_lines(VALUE path)
-{
- VALUE hash, lines;
- ID script_lines;
- CONST_ID(script_lines, "SCRIPT_LINES__");
- if (!rb_const_defined_at(rb_cObject, script_lines)) return Qnil;
- hash = rb_const_get_at(rb_cObject, script_lines);
- if (!RB_TYPE_P(hash, T_HASH)) return Qnil;
- lines = rb_hash_lookup(hash, path);
- if (!RB_TYPE_P(lines, T_ARRAY)) return Qnil;
- return lines;
-}
-
-static VALUE
node_id_for_backtrace_location(rb_execution_context_t *ec, VALUE module, VALUE location)
{
int node_id;
@@ -245,6 +221,11 @@ ast_s_of(rb_execution_context_t *ec, VALUE module, VALUE body, VALUE keep_script
if (!iseq) {
return Qnil;
}
+
+ if (ISEQ_BODY(iseq)->prism) {
+ rb_raise(rb_eRuntimeError, "cannot get AST for ISEQ compiled by prism");
+ }
+
lines = ISEQ_BODY(iseq)->variable.script_lines;
VALUE path = rb_iseq_path(iseq);
@@ -254,7 +235,7 @@ ast_s_of(rb_execution_context_t *ec, VALUE module, VALUE body, VALUE keep_script
rb_raise(rb_eArgError, "cannot get AST for method defined in eval");
}
- if (!NIL_P(lines) || !NIL_P(lines = script_lines(path))) {
+ if (!NIL_P(lines)) {
node = rb_ast_parse_array(lines, keep_script_lines, error_tolerant, keep_tokens);
}
else if (e_option) {
@@ -300,10 +281,10 @@ ast_node_node_id(rb_execution_context_t *ec, VALUE self)
return INT2FIX(nd_node_id(data->node));
}
-#define NEW_CHILD(ast, node) node ? ast_new_internal(ast, node) : Qnil
+#define NEW_CHILD(vast, node) (node ? ast_new_internal(vast, node) : Qnil)
static VALUE
-rb_ary_new_from_node_args(rb_ast_t *ast, long n, ...)
+rb_ary_new_from_node_args(VALUE vast, long n, ...)
{
va_list ar;
VALUE ary;
@@ -315,39 +296,39 @@ rb_ary_new_from_node_args(rb_ast_t *ast, long n, ...)
for (i=0; i<n; i++) {
NODE *node;
node = va_arg(ar, NODE *);
- rb_ary_push(ary, NEW_CHILD(ast, node));
+ rb_ary_push(ary, NEW_CHILD(vast, node));
}
va_end(ar);
return ary;
}
static VALUE
-dump_block(rb_ast_t *ast, const NODE *node)
+dump_block(VALUE vast, const struct RNode_BLOCK *node)
{
VALUE ary = rb_ary_new();
do {
- rb_ary_push(ary, NEW_CHILD(ast, node->nd_head));
+ rb_ary_push(ary, NEW_CHILD(vast, node->nd_head));
} while (node->nd_next &&
nd_type_p(node->nd_next, NODE_BLOCK) &&
- (node = node->nd_next, 1));
+ (node = RNODE_BLOCK(node->nd_next), 1));
if (node->nd_next) {
- rb_ary_push(ary, NEW_CHILD(ast, node->nd_next));
+ rb_ary_push(ary, NEW_CHILD(vast, node->nd_next));
}
return ary;
}
static VALUE
-dump_array(rb_ast_t *ast, const NODE *node)
+dump_array(VALUE vast, const struct RNode_LIST *node)
{
VALUE ary = rb_ary_new();
- rb_ary_push(ary, NEW_CHILD(ast, node->nd_head));
+ rb_ary_push(ary, NEW_CHILD(vast, node->nd_head));
while (node->nd_next && nd_type_p(node->nd_next, NODE_LIST)) {
- node = node->nd_next;
- rb_ary_push(ary, NEW_CHILD(ast, node->nd_head));
+ node = RNODE_LIST(node->nd_next);
+ rb_ary_push(ary, NEW_CHILD(vast, node->nd_head));
}
- rb_ary_push(ary, NEW_CHILD(ast, node->nd_next));
+ rb_ary_push(ary, NEW_CHILD(vast, node->nd_next));
return ary;
}
@@ -369,307 +350,341 @@ no_name_rest(void)
}
static VALUE
-rest_arg(rb_ast_t *ast, const NODE *rest_arg)
+rest_arg(VALUE vast, const NODE *rest_arg)
{
- return NODE_NAMED_REST_P(rest_arg) ? NEW_CHILD(ast, rest_arg) : no_name_rest();
+ return NODE_NAMED_REST_P(rest_arg) ? NEW_CHILD(vast, rest_arg) : no_name_rest();
}
static VALUE
-node_children(rb_ast_t *ast, const NODE *node)
+node_children(VALUE vast, const NODE *node)
{
char name[sizeof("$") + DECIMAL_SIZE_OF(long)];
enum node_type type = nd_type(node);
switch (type) {
case NODE_BLOCK:
- return dump_block(ast, node);
+ return dump_block(vast, RNODE_BLOCK(node));
case NODE_IF:
- return rb_ary_new_from_node_args(ast, 3, node->nd_cond, node->nd_body, node->nd_else);
+ return rb_ary_new_from_node_args(vast, 3, RNODE_IF(node)->nd_cond, RNODE_IF(node)->nd_body, RNODE_IF(node)->nd_else);
case NODE_UNLESS:
- return rb_ary_new_from_node_args(ast, 3, node->nd_cond, node->nd_body, node->nd_else);
+ return rb_ary_new_from_node_args(vast, 3, RNODE_UNLESS(node)->nd_cond, RNODE_UNLESS(node)->nd_body, RNODE_UNLESS(node)->nd_else);
case NODE_CASE:
- return rb_ary_new_from_node_args(ast, 2, node->nd_head, node->nd_body);
+ return rb_ary_new_from_node_args(vast, 2, RNODE_CASE(node)->nd_head, RNODE_CASE(node)->nd_body);
case NODE_CASE2:
- return rb_ary_new_from_node_args(ast, 2, node->nd_head, node->nd_body);
+ return rb_ary_new_from_node_args(vast, 2, RNODE_CASE2(node)->nd_head, RNODE_CASE2(node)->nd_body);
case NODE_CASE3:
- return rb_ary_new_from_node_args(ast, 2, node->nd_head, node->nd_body);
+ return rb_ary_new_from_node_args(vast, 2, RNODE_CASE3(node)->nd_head, RNODE_CASE3(node)->nd_body);
case NODE_WHEN:
- return rb_ary_new_from_node_args(ast, 3, node->nd_head, node->nd_body, node->nd_next);
+ return rb_ary_new_from_node_args(vast, 3, RNODE_WHEN(node)->nd_head, RNODE_WHEN(node)->nd_body, RNODE_WHEN(node)->nd_next);
case NODE_IN:
- return rb_ary_new_from_node_args(ast, 3, node->nd_head, node->nd_body, node->nd_next);
+ return rb_ary_new_from_node_args(vast, 3, RNODE_IN(node)->nd_head, RNODE_IN(node)->nd_body, RNODE_IN(node)->nd_next);
case NODE_WHILE:
case NODE_UNTIL:
- return rb_ary_push(rb_ary_new_from_node_args(ast, 2, node->nd_cond, node->nd_body),
- RBOOL(node->nd_state));
+ return rb_ary_push(rb_ary_new_from_node_args(vast, 2, RNODE_WHILE(node)->nd_cond, RNODE_WHILE(node)->nd_body),
+ RBOOL(RNODE_WHILE(node)->nd_state));
case NODE_ITER:
case NODE_FOR:
- return rb_ary_new_from_node_args(ast, 2, node->nd_iter, node->nd_body);
+ return rb_ary_new_from_node_args(vast, 2, RNODE_ITER(node)->nd_iter, RNODE_ITER(node)->nd_body);
case NODE_FOR_MASGN:
- return rb_ary_new_from_node_args(ast, 1, node->nd_var);
+ return rb_ary_new_from_node_args(vast, 1, RNODE_FOR_MASGN(node)->nd_var);
case NODE_BREAK:
+ return rb_ary_new_from_node_args(vast, 1, RNODE_BREAK(node)->nd_stts);
case NODE_NEXT:
+ return rb_ary_new_from_node_args(vast, 1, RNODE_NEXT(node)->nd_stts);
case NODE_RETURN:
- return rb_ary_new_from_node_args(ast, 1, node->nd_stts);
+ return rb_ary_new_from_node_args(vast, 1, RNODE_RETURN(node)->nd_stts);
case NODE_REDO:
- return rb_ary_new_from_node_args(ast, 0);
+ return rb_ary_new_from_node_args(vast, 0);
case NODE_RETRY:
- return rb_ary_new_from_node_args(ast, 0);
+ return rb_ary_new_from_node_args(vast, 0);
case NODE_BEGIN:
- return rb_ary_new_from_node_args(ast, 1, node->nd_body);
+ return rb_ary_new_from_node_args(vast, 1, RNODE_BEGIN(node)->nd_body);
case NODE_RESCUE:
- return rb_ary_new_from_node_args(ast, 3, node->nd_head, node->nd_resq, node->nd_else);
+ return rb_ary_new_from_node_args(vast, 3, RNODE_RESCUE(node)->nd_head, RNODE_RESCUE(node)->nd_resq, RNODE_RESCUE(node)->nd_else);
case NODE_RESBODY:
- return rb_ary_new_from_node_args(ast, 3, node->nd_args, node->nd_body, node->nd_head);
+ return rb_ary_new_from_node_args(vast, 3, RNODE_RESBODY(node)->nd_args, RNODE_RESBODY(node)->nd_body, RNODE_RESBODY(node)->nd_next);
case NODE_ENSURE:
- return rb_ary_new_from_node_args(ast, 2, node->nd_head, node->nd_ensr);
+ return rb_ary_new_from_node_args(vast, 2, RNODE_ENSURE(node)->nd_head, RNODE_ENSURE(node)->nd_ensr);
case NODE_AND:
case NODE_OR:
{
VALUE ary = rb_ary_new();
while (1) {
- rb_ary_push(ary, NEW_CHILD(ast, node->nd_1st));
- if (!node->nd_2nd || !nd_type_p(node->nd_2nd, type))
+ rb_ary_push(ary, NEW_CHILD(vast, RNODE_AND(node)->nd_1st));
+ if (!RNODE_AND(node)->nd_2nd || !nd_type_p(RNODE_AND(node)->nd_2nd, type))
break;
- node = node->nd_2nd;
+ node = RNODE_AND(node)->nd_2nd;
}
- rb_ary_push(ary, NEW_CHILD(ast, node->nd_2nd));
+ rb_ary_push(ary, NEW_CHILD(vast, RNODE_AND(node)->nd_2nd));
return ary;
}
case NODE_MASGN:
- if (NODE_NAMED_REST_P(node->nd_args)) {
- return rb_ary_new_from_node_args(ast, 3, node->nd_value, node->nd_head, node->nd_args);
+ if (NODE_NAMED_REST_P(RNODE_MASGN(node)->nd_args)) {
+ return rb_ary_new_from_node_args(vast, 3, RNODE_MASGN(node)->nd_value, RNODE_MASGN(node)->nd_head, RNODE_MASGN(node)->nd_args);
}
else {
- return rb_ary_new_from_args(3, NEW_CHILD(ast, node->nd_value),
- NEW_CHILD(ast, node->nd_head),
+ return rb_ary_new_from_args(3, NEW_CHILD(vast, RNODE_MASGN(node)->nd_value),
+ NEW_CHILD(vast, RNODE_MASGN(node)->nd_head),
no_name_rest());
}
case NODE_LASGN:
+ if (NODE_REQUIRED_KEYWORD_P(RNODE_LASGN(node)->nd_value)) {
+ return rb_ary_new_from_args(2, var_name(RNODE_LASGN(node)->nd_vid), ID2SYM(rb_intern("NODE_SPECIAL_REQUIRED_KEYWORD")));
+ }
+ return rb_ary_new_from_args(2, var_name(RNODE_LASGN(node)->nd_vid), NEW_CHILD(vast, RNODE_LASGN(node)->nd_value));
case NODE_DASGN:
+ if (NODE_REQUIRED_KEYWORD_P(RNODE_DASGN(node)->nd_value)) {
+ return rb_ary_new_from_args(2, var_name(RNODE_DASGN(node)->nd_vid), ID2SYM(rb_intern("NODE_SPECIAL_REQUIRED_KEYWORD")));
+ }
+ return rb_ary_new_from_args(2, var_name(RNODE_DASGN(node)->nd_vid), NEW_CHILD(vast, RNODE_DASGN(node)->nd_value));
case NODE_IASGN:
+ return rb_ary_new_from_args(2, var_name(RNODE_IASGN(node)->nd_vid), NEW_CHILD(vast, RNODE_IASGN(node)->nd_value));
case NODE_CVASGN:
+ return rb_ary_new_from_args(2, var_name(RNODE_CVASGN(node)->nd_vid), NEW_CHILD(vast, RNODE_CVASGN(node)->nd_value));
case NODE_GASGN:
- if (NODE_REQUIRED_KEYWORD_P(node)) {
- return rb_ary_new_from_args(2, var_name(node->nd_vid), ID2SYM(rb_intern("NODE_SPECIAL_REQUIRED_KEYWORD")));
- }
- return rb_ary_new_from_args(2, var_name(node->nd_vid), NEW_CHILD(ast, node->nd_value));
+ return rb_ary_new_from_args(2, var_name(RNODE_GASGN(node)->nd_vid), NEW_CHILD(vast, RNODE_GASGN(node)->nd_value));
case NODE_CDECL:
- if (node->nd_vid) {
- return rb_ary_new_from_args(2, ID2SYM(node->nd_vid), NEW_CHILD(ast, node->nd_value));
+ if (RNODE_CDECL(node)->nd_vid) {
+ return rb_ary_new_from_args(2, ID2SYM(RNODE_CDECL(node)->nd_vid), NEW_CHILD(vast, RNODE_CDECL(node)->nd_value));
}
- return rb_ary_new_from_args(3, NEW_CHILD(ast, node->nd_else), ID2SYM(node->nd_else->nd_mid), NEW_CHILD(ast, node->nd_value));
+ return rb_ary_new_from_args(3, NEW_CHILD(vast, RNODE_CDECL(node)->nd_else), ID2SYM(RNODE_COLON2(RNODE_CDECL(node)->nd_else)->nd_mid), NEW_CHILD(vast, RNODE_CDECL(node)->nd_value));
case NODE_OP_ASGN1:
- return rb_ary_new_from_args(4, NEW_CHILD(ast, node->nd_recv),
- ID2SYM(node->nd_mid),
- NEW_CHILD(ast, node->nd_args->nd_head),
- NEW_CHILD(ast, node->nd_args->nd_body));
+ return rb_ary_new_from_args(4, NEW_CHILD(vast, RNODE_OP_ASGN1(node)->nd_recv),
+ ID2SYM(RNODE_OP_ASGN1(node)->nd_mid),
+ NEW_CHILD(vast, RNODE_OP_ASGN1(node)->nd_index),
+ NEW_CHILD(vast, RNODE_OP_ASGN1(node)->nd_rvalue));
case NODE_OP_ASGN2:
- return rb_ary_new_from_args(5, NEW_CHILD(ast, node->nd_recv),
- RBOOL(node->nd_next->nd_aid),
- ID2SYM(node->nd_next->nd_vid),
- ID2SYM(node->nd_next->nd_mid),
- NEW_CHILD(ast, node->nd_value));
+ return rb_ary_new_from_args(5, NEW_CHILD(vast, RNODE_OP_ASGN2(node)->nd_recv),
+ RBOOL(RNODE_OP_ASGN2(node)->nd_aid),
+ ID2SYM(RNODE_OP_ASGN2(node)->nd_vid),
+ ID2SYM(RNODE_OP_ASGN2(node)->nd_mid),
+ NEW_CHILD(vast, RNODE_OP_ASGN2(node)->nd_value));
case NODE_OP_ASGN_AND:
- return rb_ary_new_from_args(3, NEW_CHILD(ast, node->nd_head), ID2SYM(idANDOP),
- NEW_CHILD(ast, node->nd_value));
+ return rb_ary_new_from_args(3, NEW_CHILD(vast, RNODE_OP_ASGN_AND(node)->nd_head), ID2SYM(idANDOP),
+ NEW_CHILD(vast, RNODE_OP_ASGN_AND(node)->nd_value));
case NODE_OP_ASGN_OR:
- return rb_ary_new_from_args(3, NEW_CHILD(ast, node->nd_head), ID2SYM(idOROP),
- NEW_CHILD(ast, node->nd_value));
+ return rb_ary_new_from_args(3, NEW_CHILD(vast, RNODE_OP_ASGN_OR(node)->nd_head), ID2SYM(idOROP),
+ NEW_CHILD(vast, RNODE_OP_ASGN_OR(node)->nd_value));
case NODE_OP_CDECL:
- return rb_ary_new_from_args(3, NEW_CHILD(ast, node->nd_head),
- ID2SYM(node->nd_aid),
- NEW_CHILD(ast, node->nd_value));
+ return rb_ary_new_from_args(3, NEW_CHILD(vast, RNODE_OP_CDECL(node)->nd_head),
+ ID2SYM(RNODE_OP_CDECL(node)->nd_aid),
+ NEW_CHILD(vast, RNODE_OP_CDECL(node)->nd_value));
case NODE_CALL:
+ return rb_ary_new_from_args(3, NEW_CHILD(vast, RNODE_CALL(node)->nd_recv),
+ ID2SYM(RNODE_CALL(node)->nd_mid),
+ NEW_CHILD(vast, RNODE_CALL(node)->nd_args));
case NODE_OPCALL:
+ return rb_ary_new_from_args(3, NEW_CHILD(vast, RNODE_OPCALL(node)->nd_recv),
+ ID2SYM(RNODE_OPCALL(node)->nd_mid),
+ NEW_CHILD(vast, RNODE_OPCALL(node)->nd_args));
case NODE_QCALL:
- return rb_ary_new_from_args(3, NEW_CHILD(ast, node->nd_recv),
- ID2SYM(node->nd_mid),
- NEW_CHILD(ast, node->nd_args));
+ return rb_ary_new_from_args(3, NEW_CHILD(vast, RNODE_QCALL(node)->nd_recv),
+ ID2SYM(RNODE_QCALL(node)->nd_mid),
+ NEW_CHILD(vast, RNODE_QCALL(node)->nd_args));
case NODE_FCALL:
- return rb_ary_new_from_args(2, ID2SYM(node->nd_mid),
- NEW_CHILD(ast, node->nd_args));
+ return rb_ary_new_from_args(2, ID2SYM(RNODE_FCALL(node)->nd_mid),
+ NEW_CHILD(vast, RNODE_FCALL(node)->nd_args));
case NODE_VCALL:
- return rb_ary_new_from_args(1, ID2SYM(node->nd_mid));
+ return rb_ary_new_from_args(1, ID2SYM(RNODE_VCALL(node)->nd_mid));
case NODE_SUPER:
- return rb_ary_new_from_node_args(ast, 1, node->nd_args);
+ return rb_ary_new_from_node_args(vast, 1, RNODE_SUPER(node)->nd_args);
case NODE_ZSUPER:
- return rb_ary_new_from_node_args(ast, 0);
+ return rb_ary_new_from_node_args(vast, 0);
case NODE_LIST:
- case NODE_VALUES:
- return dump_array(ast, node);
+ return dump_array(vast, RNODE_LIST(node));
case NODE_ZLIST:
- return rb_ary_new_from_node_args(ast, 0);
+ return rb_ary_new_from_node_args(vast, 0);
case NODE_HASH:
- return rb_ary_new_from_node_args(ast, 1, node->nd_head);
+ return rb_ary_new_from_node_args(vast, 1, RNODE_HASH(node)->nd_head);
case NODE_YIELD:
- return rb_ary_new_from_node_args(ast, 1, node->nd_head);
+ return rb_ary_new_from_node_args(vast, 1, RNODE_YIELD(node)->nd_head);
case NODE_LVAR:
+ return rb_ary_new_from_args(1, var_name(RNODE_LVAR(node)->nd_vid));
case NODE_DVAR:
- return rb_ary_new_from_args(1, var_name(node->nd_vid));
+ return rb_ary_new_from_args(1, var_name(RNODE_DVAR(node)->nd_vid));
case NODE_IVAR:
+ return rb_ary_new_from_args(1, ID2SYM(RNODE_IVAR(node)->nd_vid));
case NODE_CONST:
+ return rb_ary_new_from_args(1, ID2SYM(RNODE_CONST(node)->nd_vid));
case NODE_CVAR:
+ return rb_ary_new_from_args(1, ID2SYM(RNODE_CVAR(node)->nd_vid));
case NODE_GVAR:
- return rb_ary_new_from_args(1, ID2SYM(node->nd_vid));
+ return rb_ary_new_from_args(1, ID2SYM(RNODE_GVAR(node)->nd_vid));
case NODE_NTH_REF:
- snprintf(name, sizeof(name), "$%ld", node->nd_nth);
+ snprintf(name, sizeof(name), "$%ld", RNODE_NTH_REF(node)->nd_nth);
return rb_ary_new_from_args(1, ID2SYM(rb_intern(name)));
case NODE_BACK_REF:
name[0] = '$';
- name[1] = (char)node->nd_nth;
+ name[1] = (char)RNODE_BACK_REF(node)->nd_nth;
name[2] = '\0';
return rb_ary_new_from_args(1, ID2SYM(rb_intern(name)));
+ case NODE_MATCH:
+ return rb_ary_new_from_args(1, rb_node_regx_string_val(node));
case NODE_MATCH2:
- if (node->nd_args) {
- return rb_ary_new_from_node_args(ast, 3, node->nd_recv, node->nd_value, node->nd_args);
+ if (RNODE_MATCH2(node)->nd_args) {
+ return rb_ary_new_from_node_args(vast, 3, RNODE_MATCH2(node)->nd_recv, RNODE_MATCH2(node)->nd_value, RNODE_MATCH2(node)->nd_args);
}
- return rb_ary_new_from_node_args(ast, 2, node->nd_recv, node->nd_value);
+ return rb_ary_new_from_node_args(vast, 2, RNODE_MATCH2(node)->nd_recv, RNODE_MATCH2(node)->nd_value);
case NODE_MATCH3:
- return rb_ary_new_from_node_args(ast, 2, node->nd_recv, node->nd_value);
- case NODE_MATCH:
- case NODE_LIT:
+ return rb_ary_new_from_node_args(vast, 2, RNODE_MATCH3(node)->nd_recv, RNODE_MATCH3(node)->nd_value);
case NODE_STR:
case NODE_XSTR:
- return rb_ary_new_from_args(1, node->nd_lit);
+ return rb_ary_new_from_args(1, rb_node_str_string_val(node));
+ case NODE_INTEGER:
+ return rb_ary_new_from_args(1, rb_node_integer_literal_val(node));
+ case NODE_FLOAT:
+ return rb_ary_new_from_args(1, rb_node_float_literal_val(node));
+ case NODE_RATIONAL:
+ return rb_ary_new_from_args(1, rb_node_rational_literal_val(node));
+ case NODE_IMAGINARY:
+ return rb_ary_new_from_args(1, rb_node_imaginary_literal_val(node));
+ case NODE_REGX:
+ return rb_ary_new_from_args(1, rb_node_regx_string_val(node));
case NODE_ONCE:
- return rb_ary_new_from_node_args(ast, 1, node->nd_body);
+ return rb_ary_new_from_node_args(vast, 1, RNODE_ONCE(node)->nd_body);
case NODE_DSTR:
case NODE_DXSTR:
case NODE_DREGX:
case NODE_DSYM:
{
- NODE *n = node->nd_next;
+ struct RNode_LIST *n = RNODE_DSTR(node)->nd_next;
VALUE head = Qnil, next = Qnil;
if (n) {
- head = NEW_CHILD(ast, n->nd_head);
- next = NEW_CHILD(ast, n->nd_next);
+ head = NEW_CHILD(vast, n->nd_head);
+ next = NEW_CHILD(vast, n->nd_next);
}
- return rb_ary_new_from_args(3, node->nd_lit, head, next);
+ return rb_ary_new_from_args(3, rb_node_dstr_string_val(node), head, next);
}
+ case NODE_SYM:
+ return rb_ary_new_from_args(1, rb_node_sym_string_val(node));
case NODE_EVSTR:
- return rb_ary_new_from_node_args(ast, 1, node->nd_body);
+ return rb_ary_new_from_node_args(vast, 1, RNODE_EVSTR(node)->nd_body);
case NODE_ARGSCAT:
- return rb_ary_new_from_node_args(ast, 2, node->nd_head, node->nd_body);
+ return rb_ary_new_from_node_args(vast, 2, RNODE_ARGSCAT(node)->nd_head, RNODE_ARGSCAT(node)->nd_body);
case NODE_ARGSPUSH:
- return rb_ary_new_from_node_args(ast, 2, node->nd_head, node->nd_body);
+ return rb_ary_new_from_node_args(vast, 2, RNODE_ARGSPUSH(node)->nd_head, RNODE_ARGSPUSH(node)->nd_body);
case NODE_SPLAT:
- return rb_ary_new_from_node_args(ast, 1, node->nd_head);
+ return rb_ary_new_from_node_args(vast, 1, RNODE_SPLAT(node)->nd_head);
case NODE_BLOCK_PASS:
- return rb_ary_new_from_node_args(ast, 2, node->nd_head, node->nd_body);
+ return rb_ary_new_from_node_args(vast, 2, RNODE_BLOCK_PASS(node)->nd_head, RNODE_BLOCK_PASS(node)->nd_body);
case NODE_DEFN:
- return rb_ary_new_from_args(2, ID2SYM(node->nd_mid), NEW_CHILD(ast, node->nd_defn));
+ return rb_ary_new_from_args(2, ID2SYM(RNODE_DEFN(node)->nd_mid), NEW_CHILD(vast, RNODE_DEFN(node)->nd_defn));
case NODE_DEFS:
- return rb_ary_new_from_args(3, NEW_CHILD(ast, node->nd_recv), ID2SYM(node->nd_mid), NEW_CHILD(ast, node->nd_defn));
+ return rb_ary_new_from_args(3, NEW_CHILD(vast, RNODE_DEFS(node)->nd_recv), ID2SYM(RNODE_DEFS(node)->nd_mid), NEW_CHILD(vast, RNODE_DEFS(node)->nd_defn));
case NODE_ALIAS:
- return rb_ary_new_from_node_args(ast, 2, node->nd_1st, node->nd_2nd);
+ return rb_ary_new_from_node_args(vast, 2, RNODE_ALIAS(node)->nd_1st, RNODE_ALIAS(node)->nd_2nd);
case NODE_VALIAS:
- return rb_ary_new_from_args(2, ID2SYM(node->nd_alias), ID2SYM(node->nd_orig));
+ return rb_ary_new_from_args(2, ID2SYM(RNODE_VALIAS(node)->nd_alias), ID2SYM(RNODE_VALIAS(node)->nd_orig));
case NODE_UNDEF:
- return rb_ary_new_from_node_args(ast, 1, node->nd_undef);
+ return rb_ary_new_from_node_args(vast, 1, RNODE_UNDEF(node)->nd_undef);
case NODE_CLASS:
- return rb_ary_new_from_node_args(ast, 3, node->nd_cpath, node->nd_super, node->nd_body);
+ return rb_ary_new_from_node_args(vast, 3, RNODE_CLASS(node)->nd_cpath, RNODE_CLASS(node)->nd_super, RNODE_CLASS(node)->nd_body);
case NODE_MODULE:
- return rb_ary_new_from_node_args(ast, 2, node->nd_cpath, node->nd_body);
+ return rb_ary_new_from_node_args(vast, 2, RNODE_MODULE(node)->nd_cpath, RNODE_MODULE(node)->nd_body);
case NODE_SCLASS:
- return rb_ary_new_from_node_args(ast, 2, node->nd_recv, node->nd_body);
+ return rb_ary_new_from_node_args(vast, 2, RNODE_SCLASS(node)->nd_recv, RNODE_SCLASS(node)->nd_body);
case NODE_COLON2:
- return rb_ary_new_from_args(2, NEW_CHILD(ast, node->nd_head), ID2SYM(node->nd_mid));
+ return rb_ary_new_from_args(2, NEW_CHILD(vast, RNODE_COLON2(node)->nd_head), ID2SYM(RNODE_COLON2(node)->nd_mid));
case NODE_COLON3:
- return rb_ary_new_from_args(1, ID2SYM(node->nd_mid));
+ return rb_ary_new_from_args(1, ID2SYM(RNODE_COLON3(node)->nd_mid));
case NODE_DOT2:
case NODE_DOT3:
case NODE_FLIP2:
case NODE_FLIP3:
- return rb_ary_new_from_node_args(ast, 2, node->nd_beg, node->nd_end);
+ return rb_ary_new_from_node_args(vast, 2, RNODE_DOT2(node)->nd_beg, RNODE_DOT2(node)->nd_end);
case NODE_SELF:
- return rb_ary_new_from_node_args(ast, 0);
+ return rb_ary_new_from_node_args(vast, 0);
case NODE_NIL:
- return rb_ary_new_from_node_args(ast, 0);
+ return rb_ary_new_from_node_args(vast, 0);
case NODE_TRUE:
- return rb_ary_new_from_node_args(ast, 0);
+ return rb_ary_new_from_node_args(vast, 0);
case NODE_FALSE:
- return rb_ary_new_from_node_args(ast, 0);
+ return rb_ary_new_from_node_args(vast, 0);
case NODE_ERRINFO:
- return rb_ary_new_from_node_args(ast, 0);
+ return rb_ary_new_from_node_args(vast, 0);
case NODE_DEFINED:
- return rb_ary_new_from_node_args(ast, 1, node->nd_head);
+ return rb_ary_new_from_node_args(vast, 1, RNODE_DEFINED(node)->nd_head);
case NODE_POSTEXE:
- return rb_ary_new_from_node_args(ast, 1, node->nd_body);
+ return rb_ary_new_from_node_args(vast, 1, RNODE_POSTEXE(node)->nd_body);
case NODE_ATTRASGN:
- return rb_ary_new_from_args(3, NEW_CHILD(ast, node->nd_recv), ID2SYM(node->nd_mid), NEW_CHILD(ast, node->nd_args));
+ return rb_ary_new_from_args(3, NEW_CHILD(vast, RNODE_ATTRASGN(node)->nd_recv), ID2SYM(RNODE_ATTRASGN(node)->nd_mid), NEW_CHILD(vast, RNODE_ATTRASGN(node)->nd_args));
case NODE_LAMBDA:
- return rb_ary_new_from_node_args(ast, 1, node->nd_body);
+ return rb_ary_new_from_node_args(vast, 1, RNODE_LAMBDA(node)->nd_body);
case NODE_OPT_ARG:
- return rb_ary_new_from_node_args(ast, 2, node->nd_body, node->nd_next);
+ return rb_ary_new_from_node_args(vast, 2, RNODE_OPT_ARG(node)->nd_body, RNODE_OPT_ARG(node)->nd_next);
case NODE_KW_ARG:
- return rb_ary_new_from_node_args(ast, 2, node->nd_body, node->nd_next);
+ return rb_ary_new_from_node_args(vast, 2, RNODE_KW_ARG(node)->nd_body, RNODE_KW_ARG(node)->nd_next);
case NODE_POSTARG:
- if (NODE_NAMED_REST_P(node->nd_1st)) {
- return rb_ary_new_from_node_args(ast, 2, node->nd_1st, node->nd_2nd);
+ if (NODE_NAMED_REST_P(RNODE_POSTARG(node)->nd_1st)) {
+ return rb_ary_new_from_node_args(vast, 2, RNODE_POSTARG(node)->nd_1st, RNODE_POSTARG(node)->nd_2nd);
}
return rb_ary_new_from_args(2, no_name_rest(),
- NEW_CHILD(ast, node->nd_2nd));
+ NEW_CHILD(vast, RNODE_POSTARG(node)->nd_2nd));
case NODE_ARGS:
{
- struct rb_args_info *ainfo = node->nd_ainfo;
+ struct rb_args_info *ainfo = &RNODE_ARGS(node)->nd_ainfo;
return rb_ary_new_from_args(10,
INT2NUM(ainfo->pre_args_num),
- NEW_CHILD(ast, ainfo->pre_init),
- NEW_CHILD(ast, ainfo->opt_args),
+ NEW_CHILD(vast, ainfo->pre_init),
+ NEW_CHILD(vast, (NODE *)ainfo->opt_args),
var_name(ainfo->first_post_arg),
INT2NUM(ainfo->post_args_num),
- NEW_CHILD(ast, ainfo->post_init),
+ NEW_CHILD(vast, ainfo->post_init),
(ainfo->rest_arg == NODE_SPECIAL_EXCESSIVE_COMMA
? ID2SYM(rb_intern("NODE_SPECIAL_EXCESSIVE_COMMA"))
: var_name(ainfo->rest_arg)),
- (ainfo->no_kwarg ? Qfalse : NEW_CHILD(ast, ainfo->kw_args)),
- (ainfo->no_kwarg ? Qfalse : NEW_CHILD(ast, ainfo->kw_rest_arg)),
+ (ainfo->no_kwarg ? Qfalse : NEW_CHILD(vast, (NODE *)ainfo->kw_args)),
+ (ainfo->no_kwarg ? Qfalse : NEW_CHILD(vast, ainfo->kw_rest_arg)),
var_name(ainfo->block_arg));
}
case NODE_SCOPE:
{
- rb_ast_id_table_t *tbl = node->nd_tbl;
+ rb_ast_id_table_t *tbl = RNODE_SCOPE(node)->nd_tbl;
int i, size = tbl ? tbl->size : 0;
VALUE locals = rb_ary_new_capa(size);
for (i = 0; i < size; i++) {
rb_ary_push(locals, var_name(tbl->ids[i]));
}
- return rb_ary_new_from_args(3, locals, NEW_CHILD(ast, node->nd_args), NEW_CHILD(ast, node->nd_body));
+ return rb_ary_new_from_args(3, locals, NEW_CHILD(vast, (NODE *)RNODE_SCOPE(node)->nd_args), NEW_CHILD(vast, RNODE_SCOPE(node)->nd_body));
}
case NODE_ARYPTN:
{
- struct rb_ary_pattern_info *apinfo = node->nd_apinfo;
- VALUE rest = rest_arg(ast, apinfo->rest_arg);
+ VALUE rest = rest_arg(vast, RNODE_ARYPTN(node)->rest_arg);
return rb_ary_new_from_args(4,
- NEW_CHILD(ast, node->nd_pconst),
- NEW_CHILD(ast, apinfo->pre_args),
+ NEW_CHILD(vast, RNODE_ARYPTN(node)->nd_pconst),
+ NEW_CHILD(vast, RNODE_ARYPTN(node)->pre_args),
rest,
- NEW_CHILD(ast, apinfo->post_args));
+ NEW_CHILD(vast, RNODE_ARYPTN(node)->post_args));
}
case NODE_FNDPTN:
{
- struct rb_fnd_pattern_info *fpinfo = node->nd_fpinfo;
- VALUE pre_rest = rest_arg(ast, fpinfo->pre_rest_arg);
- VALUE post_rest = rest_arg(ast, fpinfo->post_rest_arg);
+ VALUE pre_rest = rest_arg(vast, RNODE_FNDPTN(node)->pre_rest_arg);
+ VALUE post_rest = rest_arg(vast, RNODE_FNDPTN(node)->post_rest_arg);
return rb_ary_new_from_args(4,
- NEW_CHILD(ast, node->nd_pconst),
+ NEW_CHILD(vast, RNODE_FNDPTN(node)->nd_pconst),
pre_rest,
- NEW_CHILD(ast, fpinfo->args),
+ NEW_CHILD(vast, RNODE_FNDPTN(node)->args),
post_rest);
}
case NODE_HSHPTN:
{
- VALUE kwrest = node->nd_pkwrestarg == NODE_SPECIAL_NO_REST_KEYWORD ? ID2SYM(rb_intern("NODE_SPECIAL_NO_REST_KEYWORD")) :
- NEW_CHILD(ast, node->nd_pkwrestarg);
+ VALUE kwrest = RNODE_HSHPTN(node)->nd_pkwrestarg == NODE_SPECIAL_NO_REST_KEYWORD ? ID2SYM(rb_intern("NODE_SPECIAL_NO_REST_KEYWORD")) :
+ NEW_CHILD(vast, RNODE_HSHPTN(node)->nd_pkwrestarg);
return rb_ary_new_from_args(3,
- NEW_CHILD(ast, node->nd_pconst),
- NEW_CHILD(ast, node->nd_pkwargs),
+ NEW_CHILD(vast, RNODE_HSHPTN(node)->nd_pconst),
+ NEW_CHILD(vast, RNODE_HSHPTN(node)->nd_pkwargs),
kwrest);
}
+ case NODE_LINE:
+ return rb_ary_new_from_args(1, rb_node_line_lineno_val(node));
+ case NODE_FILE:
+ return rb_ary_new_from_args(1, rb_node_file_path_val(node));
+ case NODE_ENCODING:
+ return rb_ary_new_from_args(1, rb_node_encoding_val(node));
case NODE_ERROR:
- return rb_ary_new_from_node_args(ast, 0);
+ return rb_ary_new_from_node_args(vast, 0);
case NODE_ARGS_AUX:
case NODE_LAST:
break;
@@ -684,7 +699,7 @@ ast_node_children(rb_execution_context_t *ec, VALUE self)
struct ASTNodeData *data;
TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
- return node_children(data->ast, data->node);
+ return node_children(data->vast, data->node);
}
static VALUE
@@ -726,10 +741,37 @@ ast_node_last_column(rb_execution_context_t *ec, VALUE self)
static VALUE
ast_node_all_tokens(rb_execution_context_t *ec, VALUE self)
{
+ long i;
struct ASTNodeData *data;
+ rb_ast_t *ast;
+ rb_parser_ary_t *parser_tokens;
+ rb_parser_ast_token_t *parser_token;
+ VALUE str, loc, token, all_tokens;
+
TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
+ ast = rb_ruby_ast_data_get(data->vast);
+
+ parser_tokens = ast->node_buffer->tokens;
+ if (parser_tokens == NULL) {
+ return Qnil;
+ }
- return rb_ast_tokens(data->ast);
+ all_tokens = rb_ary_new2(parser_tokens->len);
+ for (i = 0; i < parser_tokens->len; i++) {
+ parser_token = parser_tokens->data[i];
+ str = rb_str_new(parser_token->str->ptr, parser_token->str->len);
+ loc = rb_ary_new_from_args(4,
+ INT2FIX(parser_token->loc.beg_pos.lineno),
+ INT2FIX(parser_token->loc.beg_pos.column),
+ INT2FIX(parser_token->loc.end_pos.lineno),
+ INT2FIX(parser_token->loc.end_pos.column)
+ );
+ token = rb_ary_new_from_args(4, INT2FIX(parser_token->id), ID2SYM(rb_intern(parser_token->type_name)), str, loc);
+ rb_ary_push(all_tokens, token);
+ }
+ rb_obj_freeze(all_tokens);
+
+ return all_tokens;
}
static VALUE
@@ -756,10 +798,11 @@ static VALUE
ast_node_script_lines(rb_execution_context_t *ec, VALUE self)
{
struct ASTNodeData *data;
+ rb_ast_t *ast;
TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
- VALUE ret = data->ast->body.script_lines;
- if (!RB_TYPE_P(ret, T_ARRAY)) return Qnil;
- return ret;
+ ast = rb_ruby_ast_data_get(data->vast);
+ rb_parser_ary_t *ret = ast->body.script_lines;
+ return rb_parser_build_script_lines_from(ret);
}
#include "ast.rbinc"
diff --git a/ast.rb b/ast.rb
index ec870d8c7a..51ee5b3d59 100644
--- a/ast.rb
+++ b/ast.rb
@@ -20,7 +20,7 @@
module RubyVM::AbstractSyntaxTree
# call-seq:
- # RubyVM::AbstractSyntaxTree.parse(string, keep_script_lines: false, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node
+ # RubyVM::AbstractSyntaxTree.parse(string, keep_script_lines: RubyVM.keep_script_lines, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node
#
# Parses the given _string_ into an abstract syntax tree,
# returning the root node of that tree.
@@ -55,12 +55,12 @@ module RubyVM::AbstractSyntaxTree
#
# Note that parsing continues even after the errored expression.
#
- def self.parse string, keep_script_lines: false, error_tolerant: false, keep_tokens: false
+ def self.parse string, keep_script_lines: RubyVM.keep_script_lines, error_tolerant: false, keep_tokens: false
Primitive.ast_s_parse string, keep_script_lines, error_tolerant, keep_tokens
end
# call-seq:
- # RubyVM::AbstractSyntaxTree.parse_file(pathname, keep_script_lines: false, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node
+ # RubyVM::AbstractSyntaxTree.parse_file(pathname, keep_script_lines: RubyVM.keep_script_lines, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node
#
# Reads the file from _pathname_, then parses it like ::parse,
# returning the root node of the abstract syntax tree.
@@ -72,13 +72,13 @@ module RubyVM::AbstractSyntaxTree
# # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-31:3>
#
# See ::parse for explanation of keyword argument meaning and usage.
- def self.parse_file pathname, keep_script_lines: false, error_tolerant: false, keep_tokens: false
+ def self.parse_file pathname, keep_script_lines: RubyVM.keep_script_lines, error_tolerant: false, keep_tokens: false
Primitive.ast_s_parse_file pathname, keep_script_lines, error_tolerant, keep_tokens
end
# call-seq:
- # RubyVM::AbstractSyntaxTree.of(proc, keep_script_lines: false, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node
- # RubyVM::AbstractSyntaxTree.of(method, keep_script_lines: false, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node
+ # RubyVM::AbstractSyntaxTree.of(proc, keep_script_lines: RubyVM.keep_script_lines, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node
+ # RubyVM::AbstractSyntaxTree.of(method, keep_script_lines: RubyVM.keep_script_lines, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node
#
# Returns AST nodes of the given _proc_ or _method_.
#
@@ -93,7 +93,7 @@ module RubyVM::AbstractSyntaxTree
# # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-3:3>
#
# See ::parse for explanation of keyword argument meaning and usage.
- def self.of body, keep_script_lines: false, error_tolerant: false, keep_tokens: false
+ def self.of body, keep_script_lines: RubyVM.keep_script_lines, error_tolerant: false, keep_tokens: false
Primitive.ast_s_of body, keep_script_lines, error_tolerant, keep_tokens
end
@@ -265,8 +265,8 @@ module RubyVM::AbstractSyntaxTree
lines = script_lines
if lines
lines = lines[first_lineno - 1 .. last_lineno - 1]
- lines[-1] = lines[-1][0...last_column]
- lines[0] = lines[0][first_column..-1]
+ lines[-1] = lines[-1].byteslice(0...last_column)
+ lines[0] = lines[0].byteslice(first_column..-1)
lines.join
else
nil
diff --git a/basictest/test.rb b/basictest/test.rb
index 95875b52a6..711e4f4ab3 100755
--- a/basictest/test.rb
+++ b/basictest/test.rb
@@ -879,7 +879,7 @@ $x.sort!{|a,b| b-a} # reverse sort
test_ok($x == [7,5,3,2,1])
# split test
-$x = "The Book of Mormon"
+$x = +"The Book of Mormon"
test_ok($x.split(//).reverse!.join == $x.reverse)
test_ok($x.reverse == $x.reverse!)
test_ok("1 byte string".split(//).reverse.join(":") == "g:n:i:r:t:s: :e:t:y:b: :1")
@@ -1643,7 +1643,7 @@ test_ok(/^(?:ab+)+/ =~ "ababb" && $& == "ababb")
test_ok(/(\s+\d+){2}/ =~ " 1 2" && $& == " 1 2")
test_ok(/(?:\s+\d+){2}/ =~ " 1 2" && $& == " 1 2")
-$x = <<END;
+$x = +<<END;
ABCD
ABCD
END
@@ -1682,12 +1682,12 @@ test_ok(?a == ?a)
test_ok(?\C-a == "\1")
test_ok(?\M-a == "\341")
test_ok(?\M-\C-a == "\201")
-test_ok("a".upcase![0] == ?A)
-test_ok("A".downcase![0] == ?a)
-test_ok("abc".tr!("a-z", "A-Z") == "ABC")
-test_ok("aabbcccc".tr_s!("a-z", "A-Z") == "ABC")
-test_ok("abcc".squeeze!("a-z") == "abc")
-test_ok("abcd".delete!("bc") == "ad")
+test_ok("a".dup.upcase![0] == ?A)
+test_ok("A".dup.downcase![0] == ?a)
+test_ok("abc".dup.tr!("a-z", "A-Z") == "ABC")
+test_ok("aabbcccc".dup.tr_s!("a-z", "A-Z") == "ABC")
+test_ok("abcc".dup.squeeze!("a-z") == "abc")
+test_ok("abcd".dup.delete!("bc") == "ad")
$x = "abcdef"
$y = [ ?a, ?b, ?c, ?d, ?e, ?f ]
@@ -1700,7 +1700,7 @@ $x.each_byte {|i|
}
test_ok(!$bad)
-s = "a string"
+s = +"a string"
s[0..s.size]="another string"
test_ok(s == "another string")
diff --git a/benchmark/array_large_literal.yml b/benchmark/array_large_literal.yml
new file mode 100644
index 0000000000..423d68391f
--- /dev/null
+++ b/benchmark/array_large_literal.yml
@@ -0,0 +1,19 @@
+prelude: |
+ def def_array(size)
+ Object.class_eval(<<-END)
+ def array_#{size}
+ x = 1
+ [#{(['x'] * size).join(',')}]
+ end
+ END
+ end
+ def_array(100)
+ def_array(1000)
+ def_array(10000)
+ def_array(100000)
+benchmark:
+ array_100: array_100
+ array_1000: array_1000
+ array_10000: array_10000
+ array_100000: array_100000
+
diff --git a/benchmark/hash_key.yml b/benchmark/hash_key.yml
new file mode 100644
index 0000000000..cab4cf9ca4
--- /dev/null
+++ b/benchmark/hash_key.yml
@@ -0,0 +1,5 @@
+prelude: |
+ obj = Object.new
+ hash = { obj => true }
+benchmark: hash.key?(obj)
+loop_count: 30000000
diff --git a/benchmark/loop_each.yml b/benchmark/loop_each.yml
new file mode 100644
index 0000000000..1c757185a8
--- /dev/null
+++ b/benchmark/loop_each.yml
@@ -0,0 +1,4 @@
+prelude: |
+ arr = [nil] * 30_000_000
+benchmark:
+ loop_each: arr.each{|e|}
diff --git a/benchmark/loop_times_megamorphic.yml b/benchmark/loop_times_megamorphic.yml
new file mode 100644
index 0000000000..f9343ba897
--- /dev/null
+++ b/benchmark/loop_times_megamorphic.yml
@@ -0,0 +1,7 @@
+prelude: |
+ eval(<<~EOS)
+ def loop_times_megamorphic
+ #{"1.times {|i|};" * 1000}
+ end
+ EOS
+benchmark: loop_times_megamorphic
diff --git a/benchmark/object_allocate.yml b/benchmark/object_allocate.yml
index 93ff463e41..bdbd4536db 100644
--- a/benchmark/object_allocate.yml
+++ b/benchmark/object_allocate.yml
@@ -11,6 +11,26 @@ prelude: |
class OneTwentyEight
128.times { include(Module.new) }
end
+ class OnePositional
+ def initialize a; end
+ end
+ class TwoPositional
+ def initialize a, b; end
+ end
+ class ThreePositional
+ def initialize a, b, c; end
+ end
+ class FourPositional
+ def initialize a, b, c, d; end
+ end
+ class KWArg
+ def initialize a:, b:, c:, d:
+ end
+ end
+ class Mixed
+ def initialize a, b, c:, d:
+ end
+ end
# Disable GC to see raw throughput:
GC.disable
benchmark:
@@ -18,4 +38,11 @@ benchmark:
allocate_32_deep: ThirtyTwo.new
allocate_64_deep: SixtyFour.new
allocate_128_deep: OneTwentyEight.new
+ allocate_1_positional_params: OnePositional.new(1)
+ allocate_2_positional_params: TwoPositional.new(1, 2)
+ allocate_3_positional_params: ThreePositional.new(1, 2, 3)
+ allocate_4_positional_params: FourPositional.new(1, 2, 3, 4)
+ allocate_kwarg_params: "KWArg.new(a: 1, b: 2, c: 3, d: 4)"
+ allocate_mixed_params: "Mixed.new(1, 2, c: 3, d: 4)"
+ allocate_no_params: "Object.new"
loop_count: 100000
diff --git a/benchmark/range_bsearch_bignum.yml b/benchmark/range_bsearch_bignum.yml
new file mode 100644
index 0000000000..5730c93fcf
--- /dev/null
+++ b/benchmark/range_bsearch_bignum.yml
@@ -0,0 +1,10 @@
+prelude: |
+ first = 2**100
+ last = 2**1000
+ mid = (first + last) / 2
+ r = first..last
+
+benchmark:
+ first: r.bsearch { |x| x >= first }
+ mid: r.bsearch { |x| x >= mid }
+ last: r.bsearch { |x| x >= last }
diff --git a/benchmark/range_bsearch_endpointless.yml b/benchmark/range_bsearch_endpointless.yml
new file mode 100644
index 0000000000..8d7bedb662
--- /dev/null
+++ b/benchmark/range_bsearch_endpointless.yml
@@ -0,0 +1,21 @@
+prelude: |
+ re = (1..)
+ rb = (..0)
+
+benchmark:
+ 'endless 10**0': re.bsearch { |x| x >= 1 }
+ 'endless 10**1': re.bsearch { |x| x >= 10 }
+ 'endless 10**2': re.bsearch { |x| x >= 100 }
+ 'endless 10**3': re.bsearch { |x| x >= 1000 }
+ 'endless 10**4': re.bsearch { |x| x >= 10000 }
+ 'endless 10**5': re.bsearch { |x| x >= 100000 }
+ 'endless 10**10': re.bsearch { |x| x >= 10000000000 }
+ 'endless 10**100': re.bsearch { |x| x >= 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 }
+ 'beginless -10**0': rb.bsearch { |x| x >= -1 }
+ 'beginless -10**1': rb.bsearch { |x| x >= -10 }
+ 'beginless -10**2': rb.bsearch { |x| x >= -100 }
+ 'beginless -10**3': rb.bsearch { |x| x >= -1000 }
+ 'beginless -10**4': rb.bsearch { |x| x >= -10000 }
+ 'beginless -10**5': rb.bsearch { |x| x >= -100000 }
+ 'beginless -10**10': rb.bsearch { |x| x >= -10000000000 }
+ 'beginless -10**100': rb.bsearch { |x| x >= -10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 }
diff --git a/benchmark/range_bsearch_fixnum.yml b/benchmark/range_bsearch_fixnum.yml
new file mode 100644
index 0000000000..59416531b9
--- /dev/null
+++ b/benchmark/range_bsearch_fixnum.yml
@@ -0,0 +1,10 @@
+prelude: |
+ first = 1
+ last = 10000
+ mid = (first + last) / 2
+ r = first..last
+
+benchmark:
+ first: r.bsearch { |x| x >= first }
+ mid: r.bsearch { |x| x >= mid }
+ last: r.bsearch { |x| x >= last }
diff --git a/benchmark/range_count.yml b/benchmark/range_count.yml
new file mode 100644
index 0000000000..58f53a0236
--- /dev/null
+++ b/benchmark/range_count.yml
@@ -0,0 +1,11 @@
+prelude: |
+ r_1 = 1..1
+ r_1k = 1..1000
+ r_1m = 1..1000000
+ r_str = 'a'..'z'
+
+benchmark:
+ 'int 1': r_1.count
+ 'int 1K': r_1k.count
+ 'int 1M': r_1m.count
+ string: r_str.count
diff --git a/benchmark/range_overlap.yml b/benchmark/range_overlap.yml
new file mode 100644
index 0000000000..700a00053c
--- /dev/null
+++ b/benchmark/range_overlap.yml
@@ -0,0 +1,19 @@
+prelude: |
+ class Range
+ unless method_defined?(:overlap?)
+ def overlap?(other)
+ other.begin == self.begin || cover?(other.begin) || other.cover?(self.begin)
+ end
+ end
+ end
+
+benchmark:
+ - (2..3).overlap?(1..1)
+ - (2..3).overlap?(2..4)
+ - (2..3).overlap?(4..5)
+ - (2..3).overlap?(2..1)
+ - (2..3).overlap?(0..1)
+ - (2..3).overlap?(...1)
+ - (2...3).overlap?(..2)
+ - (2...3).overlap?(3...)
+ - (2..3).overlap?('a'..'d')
diff --git a/benchmark/range_reverse_each.yml b/benchmark/range_reverse_each.yml
new file mode 100644
index 0000000000..a32efeccc6
--- /dev/null
+++ b/benchmark/range_reverse_each.yml
@@ -0,0 +1,16 @@
+prelude: |
+ rf_1 = 0..1
+ rf_1k = 0..1000
+ rf_1m = 0..1000000
+ big = 2**1000
+ rb_1 = big..big+1
+ rb_1k = big..big+1000
+ rb_1m = big..big+1000000
+
+benchmark:
+ "Fixnum 1": rf_1.reverse_each { _1 }
+ "Fixnum 1K": rf_1k.reverse_each { _1 }
+ "Fixnum 1M": rf_1m.reverse_each { _1 }
+ "Bignum 1": rb_1.reverse_each { _1 }
+ "Bignum 1K": rb_1k.reverse_each { _1 }
+ "Bignum 1M": rb_1m.reverse_each { _1 }
diff --git a/benchmark/realpath.yml b/benchmark/realpath.yml
index 90a029d5b9..6b6a4836b0 100644
--- a/benchmark/realpath.yml
+++ b/benchmark/realpath.yml
@@ -12,6 +12,9 @@ prelude: |
relative_dir = 'b/c'
absolute_dir = File.join(pwd, relative_dir)
file_dir = 'c'
+teardown: |
+ require 'fileutils'
+ FileUtils.rm_rf('b')
benchmark:
relative_nil: "f.realpath(relative, nil)"
absolute_nil: "f.realpath(absolute, nil)"
diff --git a/benchmark/regexp_dup.yml b/benchmark/regexp_dup.yml
new file mode 100644
index 0000000000..52f89991cd
--- /dev/null
+++ b/benchmark/regexp_dup.yml
@@ -0,0 +1,6 @@
+prelude: |
+ str = "a" * 1000
+ re = Regexp.new(str)
+
+benchmark:
+ dup: re.dup
diff --git a/benchmark/regexp_new.yml b/benchmark/regexp_new.yml
new file mode 100644
index 0000000000..bc9ab3ca21
--- /dev/null
+++ b/benchmark/regexp_new.yml
@@ -0,0 +1,7 @@
+prelude: |
+ str = "a" * 1000
+ re = Regexp.new(str)
+
+benchmark:
+ string: Regexp.new(str)
+ regexp: Regexp.new(re)
diff --git a/benchmark/so_count_words.yml b/benchmark/so_count_words.yml
index 99683505f9..f7322a8541 100644
--- a/benchmark/so_count_words.yml
+++ b/benchmark/so_count_words.yml
@@ -15,13 +15,13 @@ prelude: |
Newsgroups: rec.games.roguelike.nethack
X-Mailer: Mozilla 1.1N (Macintosh; I; 68K)
- Hello there, Izchak Miller was my father. When I was younger I spent
- many a night, hunched over the keyboard with a cup of tea, playing
- nethack with him and my brother. my dad was a philosopher with a strong
- weakness for fantasy/sci fi. I remember when he started to get involved
- with the Nethack team- my brother's Dungeons and Dragons monster book
- found a regular place beside my dad's desk. it's nice to see him living
- on in the game he loved so much :-).
+ Hello there, Izchak Miller was my father. When I was younger I spent
+ many a night, hunched over the keyboard with a cup of tea, playing
+ nethack with him and my brother. my dad was a philosopher with a strong
+ weakness for fantasy/sci fi. I remember when he started to get involved
+ with the Nethack team- my brother's Dungeons and Dragons monster book
+ found a regular place beside my dad's desk. it's nice to see him living
+ on in the game he loved so much :-).
Tamar Miller
The following is a really long word of 5000 characters:
@@ -38,8 +38,9 @@ prelude: |
13.times{
data << data
}
- open(wcinput, 'w'){|f| f.write data}
+ File.write(wcinput, data)
end
+ at_exit {File.unlink(wcinput) rescue nil}
end
prepare_wc_input(wc_input_base)
@@ -49,16 +50,16 @@ benchmark:
# $Id: wc-ruby.code,v 1.4 2004/11/13 07:43:32 bfulgham Exp $
# http://www.bagley.org/~doug/shootout/
# with help from Paul Brannan
- input = open(File.join(File.dirname($0), 'wc.input'), 'rb')
nl = nw = nc = 0
- while true
- tmp = input.read(4096) or break
- data = tmp << (input.gets || "")
- nc += data.length
- nl += data.count("\n")
- ((data.strip! || data).tr!("\n", " ") || data).squeeze!
- nw += data.count(" ") + 1
+ File.open(File.join(File.dirname($0), 'wc.input'), 'rb') do |input|
+ while tmp = input.read(4096)
+ data = tmp << (input.gets || "")
+ nc += data.length
+ nl += data.count("\n")
+ ((data.strip! || data).tr!("\n", " ") || data).squeeze!
+ nw += data.count(" ") + 1
+ end
end
# STDERR.puts "#{nl} #{nw} #{nc}"
diff --git a/benchmark/string_dup.yml b/benchmark/string_dup.yml
new file mode 100644
index 0000000000..90793f9f2a
--- /dev/null
+++ b/benchmark/string_dup.yml
@@ -0,0 +1,7 @@
+prelude: |
+ # frozen_string_literal: true
+benchmark:
+ uplus: |
+ +"A"
+ dup: |
+ "A".dup
diff --git a/benchmark/string_rpartition.yml b/benchmark/string_rpartition.yml
new file mode 100644
index 0000000000..37e9d1b071
--- /dev/null
+++ b/benchmark/string_rpartition.yml
@@ -0,0 +1,18 @@
+prelude: |
+ str1 = [*"a".."z",*"0".."9"].join("")
+ str10 = str1 * 10 + ":"
+ str100 = str1 * 100 + ":"
+ str1000 = str1 * 1000 + ":"
+ nonascii1 = [*"\u{e0}".."\u{ff}"].join("")
+ nonascii10 = nonascii1 * 10 + ":"
+ nonascii100 = nonascii1 * 100 + ":"
+ nonascii1000 = nonascii1 * 1000 + ":"
+benchmark:
+ rpartition-1: str1.rpartition(":")
+ rpartition-10: str10.rpartition(":")
+ rpartition-100: str100.rpartition(":")
+ rpartition-1000: str1000.rpartition(":")
+ rpartition-nonascii1: nonascii1.rpartition(":")
+ rpartition-nonascii10: nonascii10.rpartition(":")
+ rpartition-nonascii100: nonascii100.rpartition(":")
+ rpartition-nonascii1000: nonascii1000.rpartition(":")
diff --git a/benchmark/struct_accessor.yml b/benchmark/struct_accessor.yml
new file mode 100644
index 0000000000..61176cfdd4
--- /dev/null
+++ b/benchmark/struct_accessor.yml
@@ -0,0 +1,25 @@
+prelude: |
+ C = Struct.new(:x) do
+ class_eval <<-END
+ def r
+ #{'x;'*256}
+ end
+ def w
+ #{'self.x = nil;'*256}
+ end
+ def rm
+ m = method(:x)
+ #{'m.call;'*256}
+ end
+ def wm
+ m = method(:x=)
+ #{'m.call(nil);'*256}
+ end
+ END
+ end
+ obj = C.new(nil)
+benchmark:
+ member_reader: "obj.r"
+ member_writer: "obj.w"
+ member_reader_method: "obj.rm"
+ member_writer_method: "obj.wm"
diff --git a/benchmark/vm_call_kw_and_kw_splat.yml b/benchmark/vm_call_kw_and_kw_splat.yml
new file mode 100644
index 0000000000..aa6e549e0c
--- /dev/null
+++ b/benchmark/vm_call_kw_and_kw_splat.yml
@@ -0,0 +1,25 @@
+prelude: |
+ h1, h10, h100, h1000 = [1, 10, 100, 1000].map do |n|
+ h = {kw: 1}
+ n.times{|i| h[i.to_s.to_sym] = i}
+ h
+ end
+ eh = {}
+ def kw(kw: nil, **kws) end
+benchmark:
+ 1: |
+ kw(**h1)
+ 1_mutable: |
+ kw(**eh, **h1)
+ 10: |
+ kw(**h10)
+ 10_mutable: |
+ kw(**eh, **h10)
+ 100: |
+ kw(**h100)
+ 100_mutable: |
+ kw(**eh, **h100)
+ 1000: |
+ kw(**h1000)
+ 1000_mutable: |
+ kw(**eh, **h1000)
diff --git a/benchmark/vm_ivar_ic_miss.yml b/benchmark/vm_ivar_ic_miss.yml
new file mode 100644
index 0000000000..944fb1a9e6
--- /dev/null
+++ b/benchmark/vm_ivar_ic_miss.yml
@@ -0,0 +1,20 @@
+prelude: |
+ class Foo
+ def initialize diverge
+ if diverge
+ @a = 1
+ end
+
+ @a0 = @a1 = @a2 = @a3 = @a4 = @a5 = @a6 = @a7 = @a8 = @a9 = @a10 = @a11 = @a12 = @a13 = @a14 = @a15 = @a16 = @a17 = @a18 = @a19 = @a20 = @a21 = @a22 = @a23 = @a24 = @a25 = @a26 = @a27 = @a28 = @a29 = @a30 = @a31 = @a32 = @a33 = @a34 = @a35 = @a36 = @a37 = @a38 = @a39 = @a40 = @a41 = @a42 = @a43 = @a44 = @a45 = @a46 = @a47 = @a48 = @a49 = @a50 = @a51 = @a52 = @a53 = @a54 = @a55 = @a56 = @a57 = @a58 = @a59 = @a60 = @a61 = @a62 = @a63 = @a64 = @a65 = @a66 = @a67 = @a68 = @a69 = @a70 = @a71 = @a72 = @a73 = @a74 = @b = 1
+ end
+
+ def b; @b; end
+ end
+
+ a = Foo.new false
+ b = Foo.new true
+benchmark:
+ vm_ivar_ic_miss: |
+ a.b
+ b.b
+loop_count: 30000000
diff --git a/benchmark/vm_ivar_memoize.yml b/benchmark/vm_ivar_memoize.yml
new file mode 100644
index 0000000000..90f6b07f05
--- /dev/null
+++ b/benchmark/vm_ivar_memoize.yml
@@ -0,0 +1,85 @@
+prelude: |
+ IVARS = 60
+ class Record
+ def initialize(offset = false)
+ @offset = 1 if offset
+ @first = 0
+ IVARS.times do |i|
+ instance_variable_set("@ivar_#{i}", i)
+ end
+ end
+
+ def first
+ @first
+ end
+
+ def lazy_set
+ @lazy_set ||= 123
+ end
+
+ def undef
+ @undef
+ end
+ end
+
+ Record.new # Need one alloc to right size
+
+ BASE = Record.new
+ LAZY = Record.new
+ LAZY.lazy_set
+
+ class Miss < Record
+ @first = 0
+ IVARS.times do |i|
+ instance_variable_set("@i_#{i}", i)
+ end
+ end
+
+ Miss.new # Need one alloc to right size
+ MISS = Miss.new
+
+ DIVERGENT = Record.new(true)
+
+benchmark:
+ vm_ivar_stable_shape: |
+ BASE.first
+ BASE.first
+ BASE.first
+ BASE.first
+ BASE.first
+ BASE.first
+ vm_ivar_memoize_unstable_shape: |
+ BASE.first
+ LAZY.first
+ BASE.first
+ LAZY.first
+ BASE.first
+ LAZY.first
+ vm_ivar_memoize_unstable_shape_miss: |
+ BASE.first
+ MISS.first
+ BASE.first
+ MISS.first
+ BASE.first
+ MISS.first
+ vm_ivar_unstable_undef: |
+ BASE.undef
+ LAZY.undef
+ BASE.undef
+ LAZY.undef
+ BASE.undef
+ LAZY.undef
+ vm_ivar_divergent_shape: |
+ BASE.first
+ DIVERGENT.first
+ BASE.first
+ DIVERGENT.first
+ BASE.first
+ DIVERGENT.first
+ vm_ivar_divergent_shape_imbalanced: |
+ BASE.first
+ DIVERGENT.first
+ DIVERGENT.first
+ DIVERGENT.first
+ DIVERGENT.first
+ DIVERGENT.first
diff --git a/benchmark/vm_method_splat_calls.yml b/benchmark/vm_method_splat_calls.yml
new file mode 100644
index 0000000000..f2f366e99c
--- /dev/null
+++ b/benchmark/vm_method_splat_calls.yml
@@ -0,0 +1,13 @@
+prelude: |
+ def f(x=0, y: 0) end
+ a = [1]
+ ea = []
+ kw = {y: 1}
+ b = lambda{}
+benchmark:
+ arg_splat: "f(1, *ea)"
+ arg_splat_block: "f(1, *ea, &b)"
+ splat_kw_splat: "f(*a, **kw)"
+ splat_kw_splat_block: "f(*a, **kw, &b)"
+ splat_kw: "f(*a, y: 1)"
+ splat_kw_block: "f(*a, y: 1, &b)"
diff --git a/benchmark/vm_method_splat_calls2.yml b/benchmark/vm_method_splat_calls2.yml
new file mode 100644
index 0000000000..d33dcd7e8b
--- /dev/null
+++ b/benchmark/vm_method_splat_calls2.yml
@@ -0,0 +1,27 @@
+prelude: |
+ def named_arg_splat(*a) end
+ def named_arg_kw_splat(*a, **kw) end
+ def anon_arg_splat(*) end
+ def anon_kw_splat(**) end
+ def anon_arg_kw_splat(*, **) end
+ def anon_fw_to_named(*, **) named_arg_kw_splat(*, **) end
+ def fw_to_named(...) named_arg_kw_splat(...) end
+ def fw_to_anon_to_named(...) anon_fw_to_named(...) end
+ def fw_no_kw(...) named_arg_splat(...) end
+ a = [1]
+ kw = {y: 1}
+benchmark:
+ named_multi_arg_splat: "named_arg_splat(*a, *a)"
+ named_post_splat: "named_arg_splat(*a, a)"
+ anon_arg_splat: "anon_arg_splat(*a)"
+ anon_arg_kw_splat: "anon_arg_kw_splat(*a, **kw)"
+ anon_multi_arg_splat: "anon_arg_splat(*a, *a)"
+ anon_post_splat: "anon_arg_splat(*a, a)"
+ anon_kw_splat: "anon_kw_splat(**kw)"
+ anon_fw_to_named_splat: "anon_fw_to_named(*a, **kw)"
+ anon_fw_to_named_no_splat: "anon_fw_to_named(1, y: 1)"
+ fw_to_named_splat: "fw_to_named(*a, **kw)"
+ fw_to_named_no_splat: "fw_to_named(1, y: 1)"
+ fw_to_anon_to_named_splat: "fw_to_anon_to_named(*a, **kw)"
+ fw_to_anon_to_named_no_splat: "fw_to_anon_to_named(1, y: 1)"
+ fw_no_kw: "fw_no_kw(1, 2)"
diff --git a/benchmark/vm_super_splat_calls.yml b/benchmark/vm_super_splat_calls.yml
new file mode 100644
index 0000000000..795e44e4da
--- /dev/null
+++ b/benchmark/vm_super_splat_calls.yml
@@ -0,0 +1,25 @@
+prelude: |
+ @a = [1].freeze
+ @ea = [].freeze
+ @kw = {y: 1}.freeze
+ @b = lambda{}
+ extend(Module.new{def arg_splat(x=0, y: 0) end})
+ extend(Module.new{def arg_splat_block(x=0, y: 0) end})
+ extend(Module.new{def splat_kw_splat(x=0, y: 0) end})
+ extend(Module.new{def splat_kw_splat_block(x=0, y: 0) end})
+ extend(Module.new{def splat_kw(x=0, y: 0) end})
+ extend(Module.new{def splat_kw_block(x=0, y: 0) end})
+
+ extend(Module.new{def arg_splat; super(1, *@ea) end})
+ extend(Module.new{def arg_splat_block; super(1, *@ea, &@b) end})
+ extend(Module.new{def splat_kw_splat; super(*@a, **@kw) end})
+ extend(Module.new{def splat_kw_splat_block; super(*@a, **@kw, &@b) end})
+ extend(Module.new{def splat_kw; super(*@a, y: 1) end})
+ extend(Module.new{def splat_kw_block; super(*@a, y: 1, &@b) end})
+benchmark:
+ arg_splat: "arg_splat"
+ arg_splat_block: "arg_splat_block"
+ splat_kw_splat: "splat_kw_splat"
+ splat_kw_splat_block: "splat_kw_splat_block"
+ splat_kw: "splat_kw"
+ splat_kw_block: "splat_kw_block"
diff --git a/benchmark/vm_zsuper_splat_calls.yml b/benchmark/vm_zsuper_splat_calls.yml
new file mode 100644
index 0000000000..82dc22349d
--- /dev/null
+++ b/benchmark/vm_zsuper_splat_calls.yml
@@ -0,0 +1,28 @@
+prelude: |
+ a = [1].freeze
+ ea = [].freeze
+ kw = {y: 1}.freeze
+ b = lambda{}
+ extend(Module.new{def arg_splat(x=0, y: 0) end})
+ extend(Module.new{def arg_splat_block(x=0, y: 0) end})
+ extend(Module.new{def arg_splat_post(x=0, y: 0) end})
+ extend(Module.new{def splat_kw_splat(x=0, y: 0) end})
+ extend(Module.new{def splat_kw_splat_block(x=0, y: 0) end})
+ extend(Module.new{def splat_kw(x=0, y: 0) end})
+ extend(Module.new{def splat_kw_block(x=0, y: 0) end})
+
+ extend(Module.new{def arg_splat(x, *a) super end})
+ extend(Module.new{def arg_splat_block(x, *a, &b) super end})
+ extend(Module.new{def arg_splat_post(*a, x) super end})
+ extend(Module.new{def splat_kw_splat(*a, **kw) super end})
+ extend(Module.new{def splat_kw_splat_block(*a, **kw, &b) super end})
+ extend(Module.new{def splat_kw(*a, y: 1) super end})
+ extend(Module.new{def splat_kw_block(*a, y: 1, &b) super end})
+benchmark:
+ arg_splat: "arg_splat(1, *ea)"
+ arg_splat_block: "arg_splat_block(1, *ea, &b)"
+ arg_splat_post: "arg_splat_post(1, *ea, &b)"
+ splat_kw_splat: "splat_kw_splat(*a, **kw)"
+ splat_kw_splat_block: "splat_kw_splat_block(*a, **kw, &b)"
+ splat_kw: "splat_kw(*a, y: 1)"
+ splat_kw_block: "splat_kw_block(*a, y: 1, &b)"
diff --git a/bignum.c b/bignum.c
index bef92f5a33..4b01316d22 100644
--- a/bignum.c
+++ b/bignum.c
@@ -48,6 +48,14 @@
#include "ruby/util.h"
#include "ruby_assert.h"
+static const bool debug_integer_pack = (
+#ifdef DEBUG_INTEGER_PACK
+ DEBUG_INTEGER_PACK+0
+#else
+ RUBY_DEBUG
+#endif
+ ) != 0;
+
const char ruby_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz";
#ifndef SIZEOF_BDIGIT_DBL
@@ -341,7 +349,7 @@ maxpow_in_bdigit_dbl(int base, int *exp_ret)
BDIGIT_DBL maxpow;
int exponent;
- assert(2 <= base && base <= 36);
+ RUBY_ASSERT(2 <= base && base <= 36);
{
#if SIZEOF_BDIGIT_DBL == 2
@@ -373,7 +381,7 @@ maxpow_in_bdigit_dbl(int base, int *exp_ret)
static inline BDIGIT_DBL
bary2bdigitdbl(const BDIGIT *ds, size_t n)
{
- assert(n <= 2);
+ RUBY_ASSERT(n <= 2);
if (n == 2)
return ds[0] | BIGUP(ds[1]);
@@ -385,7 +393,7 @@ bary2bdigitdbl(const BDIGIT *ds, size_t n)
static inline void
bdigitdbl2bary(BDIGIT *ds, size_t n, BDIGIT_DBL num)
{
- assert(n == 2);
+ RUBY_ASSERT(n == 2);
ds[0] = BIGLO(num);
ds[1] = (BDIGIT)BIGDN(num);
@@ -416,7 +424,7 @@ bary_small_lshift(BDIGIT *zds, const BDIGIT *xds, size_t n, int shift)
{
size_t i;
BDIGIT_DBL num = 0;
- assert(0 <= shift && shift < BITSPERDIG);
+ RUBY_ASSERT(0 <= shift && shift < BITSPERDIG);
for (i=0; i<n; i++) {
num = num | (BDIGIT_DBL)*xds++ << shift;
@@ -432,7 +440,7 @@ bary_small_rshift(BDIGIT *zds, const BDIGIT *xds, size_t n, int shift, BDIGIT hi
size_t i;
BDIGIT_DBL num = 0;
- assert(0 <= shift && shift < BITSPERDIG);
+ RUBY_ASSERT(0 <= shift && shift < BITSPERDIG);
num = BIGUP(higher_bdigit);
for (i = 0; i < n; i++) {
@@ -1049,15 +1057,13 @@ integer_unpack_num_bdigits(size_t numwords, size_t wordsize, size_t nails, int *
if (numwords <= (SIZE_MAX - (BITSPERDIG-1)) / CHAR_BIT / wordsize) {
num_bdigits = integer_unpack_num_bdigits_small(numwords, wordsize, nails, nlp_bits_ret);
-#ifdef DEBUG_INTEGER_PACK
- {
+ if (debug_integer_pack) {
int nlp_bits1;
size_t num_bdigits1 = integer_unpack_num_bdigits_generic(numwords, wordsize, nails, &nlp_bits1);
- assert(num_bdigits == num_bdigits1);
- assert(*nlp_bits_ret == nlp_bits1);
+ RUBY_ASSERT(num_bdigits == num_bdigits1);
+ RUBY_ASSERT(*nlp_bits_ret == nlp_bits1);
(void)num_bdigits1;
}
-#endif
}
else {
num_bdigits = integer_unpack_num_bdigits_generic(numwords, wordsize, nails, nlp_bits_ret);
@@ -1265,7 +1271,7 @@ bary_unpack_internal(BDIGIT *bdigits, size_t num_bdigits, const void *words, siz
}
if (dd)
*dp++ = (BDIGIT)dd;
- assert(dp <= de);
+ RUBY_ASSERT(dp <= de);
while (dp < de)
*dp++ = 0;
#undef PUSH_BITS
@@ -1324,7 +1330,7 @@ bary_unpack(BDIGIT *bdigits, size_t num_bdigits, const void *words, size_t numwo
num_bdigits0 = integer_unpack_num_bdigits(numwords, wordsize, nails, &nlp_bits);
- assert(num_bdigits0 <= num_bdigits);
+ RUBY_ASSERT(num_bdigits0 <= num_bdigits);
sign = bary_unpack_internal(bdigits, num_bdigits0, words, numwords, wordsize, nails, flags, nlp_bits);
@@ -1343,8 +1349,8 @@ bary_subb(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT *yd
size_t i;
size_t sn;
- assert(xn <= zn);
- assert(yn <= zn);
+ RUBY_ASSERT(xn <= zn);
+ RUBY_ASSERT(yn <= zn);
sn = xn < yn ? xn : yn;
@@ -1405,8 +1411,8 @@ bary_addc(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT *yd
BDIGIT_DBL num;
size_t i;
- assert(xn <= zn);
- assert(yn <= zn);
+ RUBY_ASSERT(xn <= zn);
+ RUBY_ASSERT(yn <= zn);
if (xn > yn) {
const BDIGIT *tds;
@@ -1470,7 +1476,7 @@ bary_mul_single(BDIGIT *zds, size_t zn, BDIGIT x, BDIGIT y)
{
BDIGIT_DBL n;
- assert(2 <= zn);
+ RUBY_ASSERT(2 <= zn);
n = (BDIGIT_DBL)x * y;
bdigitdbl2bary(zds, 2, n);
@@ -1484,7 +1490,7 @@ bary_muladd_1xN(BDIGIT *zds, size_t zn, BDIGIT x, const BDIGIT *yds, size_t yn)
BDIGIT_DBL dd;
size_t j;
- assert(zn > yn);
+ RUBY_ASSERT(zn > yn);
if (x == 0)
return 0;
@@ -1519,7 +1525,7 @@ bigdivrem_mulsub(BDIGIT *zds, size_t zn, BDIGIT x, const BDIGIT *yds, size_t yn)
BDIGIT_DBL t2;
BDIGIT_DBL_SIGNED num;
- assert(zn == yn + 1);
+ RUBY_ASSERT(zn == yn + 1);
num = 0;
t2 = 0;
@@ -1544,7 +1550,7 @@ bary_mulsub_1xN(BDIGIT *zds, size_t zn, BDIGIT x, const BDIGIT *yds, size_t yn)
{
BDIGIT_DBL_SIGNED num;
- assert(zn == yn + 1);
+ RUBY_ASSERT(zn == yn + 1);
num = bigdivrem_mulsub(zds, zn, x, yds, yn);
zds[yn] = BIGLO(num);
@@ -1558,7 +1564,7 @@ bary_mul_normal(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIG
{
size_t i;
- assert(xn + yn <= zn);
+ RUBY_ASSERT(xn + yn <= zn);
BDIGITS_ZERO(zds, zn);
for (i = 0; i < xn; i++) {
@@ -1589,7 +1595,7 @@ bary_sq_fast(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn)
BDIGIT vl;
int vh;
- assert(xn * 2 <= zn);
+ RUBY_ASSERT(xn * 2 <= zn);
BDIGITS_ZERO(zds, zn);
@@ -1661,9 +1667,9 @@ bary_mul_balance_with_mulfunc(BDIGIT *const zds, const size_t zn,
VALUE work = 0;
size_t n;
- assert(xn + yn <= zn);
- assert(xn <= yn);
- assert(!KARATSUBA_BALANCED(xn, yn) || !TOOM3_BALANCED(xn, yn));
+ RUBY_ASSERT(xn + yn <= zn);
+ RUBY_ASSERT(xn <= yn);
+ RUBY_ASSERT(!KARATSUBA_BALANCED(xn, yn) || !TOOM3_BALANCED(xn, yn));
BDIGITS_ZERO(zds, xn);
@@ -1745,9 +1751,9 @@ bary_mul_karatsuba(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const B
const BDIGIT *xds0, *xds1, *yds0, *yds1;
BDIGIT *zds0, *zds1, *zds2, *zds3;
- assert(xn + yn <= zn);
- assert(xn <= yn);
- assert(yn < 2 * xn);
+ RUBY_ASSERT(xn + yn <= zn);
+ RUBY_ASSERT(xn <= yn);
+ RUBY_ASSERT(yn < 2 * xn);
sq = xds == yds && xn == yn;
@@ -1762,7 +1768,7 @@ bary_mul_karatsuba(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const B
n = yn / 2;
- assert(n < xn);
+ RUBY_ASSERT(n < xn);
if (wn < n) {
/* This function itself needs only n BDIGITs for work area.
@@ -1883,7 +1889,7 @@ bary_mul_karatsuba(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const B
for (x = 0, i = xn-1; 0 <= i; i--) { x <<= SIZEOF_BDIGIT*CHAR_BIT; x |= xds[i]; }
for (y = 0, i = yn-1; 0 <= i; i--) { y <<= SIZEOF_BDIGIT*CHAR_BIT; y |= yds[i]; }
for (z = 0, i = zn-1; 0 <= i; i--) { z <<= SIZEOF_BDIGIT*CHAR_BIT; z |= zds[i]; }
- assert(z == x * y);
+ RUBY_ASSERT(z == x * y);
}
*/
@@ -1951,11 +1957,11 @@ bary_mul_toom3(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGI
int sq = xds == yds && xn == yn;
- assert(xn <= yn); /* assume y >= x */
- assert(xn + yn <= zn);
+ RUBY_ASSERT(xn <= yn); /* assume y >= x */
+ RUBY_ASSERT(xn + yn <= zn);
n = (yn + 2) / 3;
- assert(2*n < xn);
+ RUBY_ASSERT(2*n < xn);
wnc = 0;
@@ -2142,19 +2148,19 @@ bary_mul_toom3(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGI
/* z(1) : t1 <- u1 * v1 */
bary_mul_toom3_start(t1ds, t1n, u1ds, u1n, v1ds, v1n, wds, wn);
t1p = u1p == v1p;
- assert(t1ds[t1n-1] == 0);
+ RUBY_ASSERT(t1ds[t1n-1] == 0);
t1n--;
/* z(-1) : t2 <- u2 * v2 */
bary_mul_toom3_start(t2ds, t2n, u2ds, u2n, v2ds, v2n, wds, wn);
t2p = u2p == v2p;
- assert(t2ds[t2n-1] == 0);
+ RUBY_ASSERT(t2ds[t2n-1] == 0);
t2n--;
/* z(-2) : t3 <- u3 * v3 */
bary_mul_toom3_start(t3ds, t3n, u3ds, u3n, v3ds, v3n, wds, wn);
t3p = u3p == v3p;
- assert(t3ds[t3n-1] == 0);
+ RUBY_ASSERT(t3ds[t3n-1] == 0);
t3n--;
/* z(inf) : t4 <- x2 * y2 */
@@ -2330,7 +2336,7 @@ bary_mul_gmp(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT
mpz_t x, y, z;
size_t count;
- assert(xn + yn <= zn);
+ RUBY_ASSERT(xn + yn <= zn);
mpz_init(x);
mpz_init(y);
@@ -2365,7 +2371,7 @@ rb_big_mul_gmp(VALUE x, VALUE y)
static void
bary_short_mul(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT *yds, size_t yn)
{
- assert(xn + yn <= zn);
+ RUBY_ASSERT(xn + yn <= zn);
if (xn == 1 && yn == 1) {
bary_mul_single(zds, zn, xds[0], yds[0]);
@@ -2401,7 +2407,7 @@ bary_mul_precheck(BDIGIT **zdsp, size_t *znp, const BDIGIT **xdsp, size_t *xnp,
const BDIGIT *yds = *ydsp;
size_t yn = *ynp;
- assert(xn + yn <= zn);
+ RUBY_ASSERT(xn + yn <= zn);
nlsz = 0;
@@ -2450,7 +2456,7 @@ bary_mul_precheck(BDIGIT **zdsp, size_t *znp, const BDIGIT **xdsp, size_t *xnp,
tds = xds; xds = yds; yds = tds;
tn = xn; xn = yn; yn = tn;
}
- assert(xn <= yn);
+ RUBY_ASSERT(xn <= yn);
if (xn <= 1) {
if (xn == 0) {
@@ -2633,8 +2639,8 @@ rb_big_stop(void *ptr)
static BDIGIT
bigdivrem_single1(BDIGIT *qds, const BDIGIT *xds, size_t xn, BDIGIT x_higher_bdigit, BDIGIT y)
{
- assert(0 < xn);
- assert(x_higher_bdigit < y);
+ RUBY_ASSERT(0 < xn);
+ RUBY_ASSERT(x_higher_bdigit < y);
if (POW2_P(y)) {
BDIGIT r;
r = xds[0] & (y-1);
@@ -2666,9 +2672,9 @@ bigdivrem_restoring(BDIGIT *zds, size_t zn, BDIGIT *yds, size_t yn)
struct big_div_struct bds;
size_t ynzero;
- assert(yn < zn);
- assert(BDIGIT_MSB(yds[yn-1]));
- assert(zds[zn-1] < yds[yn-1]);
+ RUBY_ASSERT(yn < zn);
+ RUBY_ASSERT(BDIGIT_MSB(yds[yn-1]));
+ RUBY_ASSERT(zds[zn-1] < yds[yn-1]);
for (ynzero = 0; !yds[ynzero]; ynzero++);
@@ -2707,9 +2713,9 @@ bary_divmod_normal(BDIGIT *qds, size_t qn, BDIGIT *rds, size_t rn, const BDIGIT
size_t zn;
VALUE tmpyz = 0;
- assert(yn < xn || (xn == yn && yds[yn - 1] <= xds[xn - 1]));
- assert(qds ? (xn - yn + 1) <= qn : 1);
- assert(rds ? yn <= rn : 1);
+ RUBY_ASSERT(yn < xn || (xn == yn && yds[yn - 1] <= xds[xn - 1]));
+ RUBY_ASSERT(qds ? (xn - yn + 1) <= qn : 1);
+ RUBY_ASSERT(rds ? yn <= rn : 1);
zn = xn + BIGDIVREM_EXTRA_WORDS;
@@ -2801,10 +2807,10 @@ bary_divmod_gmp(BDIGIT *qds, size_t qn, BDIGIT *rds, size_t rn, const BDIGIT *xd
mpz_t x, y, q, r;
size_t count;
- assert(yn < xn || (xn == yn && yds[yn - 1] <= xds[xn - 1]));
- assert(qds ? (xn - yn + 1) <= qn : 1);
- assert(rds ? yn <= rn : 1);
- assert(qds || rds);
+ RUBY_ASSERT(yn < xn || (xn == yn && yds[yn - 1] <= xds[xn - 1]));
+ RUBY_ASSERT(qds ? (xn - yn + 1) <= qn : 1);
+ RUBY_ASSERT(rds ? yn <= rn : 1);
+ RUBY_ASSERT(qds || rds);
mpz_init(x);
mpz_init(y);
@@ -2890,8 +2896,8 @@ bary_divmod_branch(BDIGIT *qds, size_t qn, BDIGIT *rds, size_t rn, const BDIGIT
static void
bary_divmod(BDIGIT *qds, size_t qn, BDIGIT *rds, size_t rn, const BDIGIT *xds, size_t xn, const BDIGIT *yds, size_t yn)
{
- assert(xn <= qn);
- assert(yn <= rn);
+ RUBY_ASSERT(xn <= qn);
+ RUBY_ASSERT(yn <= rn);
BARY_TRUNC(yds, yn);
if (yn == 0)
@@ -3423,15 +3429,13 @@ rb_absint_numwords(VALUE val, size_t word_numbits, size_t *nlz_bits_ret)
if (numbytes <= SIZE_MAX / CHAR_BIT) {
numwords = absint_numwords_small(numbytes, nlz_bits_in_msbyte, word_numbits, &nlz_bits);
-#ifdef DEBUG_INTEGER_PACK
- {
+ if (debug_integer_pack) {
size_t numwords0, nlz_bits0;
numwords0 = absint_numwords_generic(numbytes, nlz_bits_in_msbyte, word_numbits, &nlz_bits0);
- assert(numwords0 == numwords);
- assert(nlz_bits0 == nlz_bits);
+ RUBY_ASSERT(numwords0 == numwords);
+ RUBY_ASSERT(nlz_bits0 == nlz_bits);
(void)numwords0;
}
-#endif
}
else {
numwords = absint_numwords_generic(numbytes, nlz_bits_in_msbyte, word_numbits, &nlz_bits);
@@ -3844,7 +3848,7 @@ str2big_poweroftwo(
if (numbits) {
*dp++ = BIGLO(dd);
}
- assert((size_t)(dp - BDIGITS(z)) == num_bdigits);
+ RUBY_ASSERT((size_t)(dp - BDIGITS(z)) == num_bdigits);
return z;
}
@@ -3887,7 +3891,7 @@ str2big_normal(
}
break;
}
- assert(blen <= num_bdigits);
+ RUBY_ASSERT(blen <= num_bdigits);
}
return z;
@@ -3945,7 +3949,7 @@ str2big_karatsuba(
current_base = 1;
}
}
- assert(i == num_bdigits);
+ RUBY_ASSERT(i == num_bdigits);
for (unit = 2; unit < num_bdigits; unit *= 2) {
for (i = 0; i < num_bdigits; i += unit*2) {
if (2*unit <= num_bdigits - i) {
@@ -4090,8 +4094,8 @@ rb_int_parse_cstr(const char *str, ssize_t len, char **endp, size_t *ndigits,
len -= (n); \
} while (0)
#define ASSERT_LEN() do {\
- assert(len != 0); \
- if (len0 >= 0) assert(s + len0 == str + len); \
+ RUBY_ASSERT(len != 0); \
+ if (len0 >= 0) RUBY_ASSERT(s + len0 == str + len); \
} while (0)
if (!str) {
@@ -4636,8 +4640,8 @@ big_shift2(VALUE x, int lshift_p, VALUE y)
size_t shift_numdigits;
int shift_numbits;
- assert(POW2_P(CHAR_BIT));
- assert(POW2_P(BITSPERDIG));
+ RUBY_ASSERT(POW2_P(CHAR_BIT));
+ RUBY_ASSERT(POW2_P(BITSPERDIG));
if (BIGZEROP(x))
return INT2FIX(0);
@@ -4724,7 +4728,7 @@ power_cache_get_power(int base, int power_level, size_t *numdigits_ret)
rb_obj_hide(power);
base36_power_cache[base - 2][power_level] = power;
base36_numdigits_cache[base - 2][power_level] = numdigits;
- rb_gc_register_mark_object(power);
+ rb_vm_register_global_object(power);
}
if (numdigits_ret)
*numdigits_ret = base36_numdigits_cache[base - 2][power_level];
@@ -4760,7 +4764,7 @@ big2str_2bdigits(struct big2str_struct *b2s, BDIGIT *xds, size_t xn, size_t tail
int beginning = !b2s->ptr;
size_t len = 0;
- assert(xn <= 2);
+ RUBY_ASSERT(xn <= 2);
num = bary2bdigitdbl(xds, xn);
if (beginning) {
@@ -4888,7 +4892,7 @@ big2str_karatsuba(struct big2str_struct *b2s, BDIGIT *xds, size_t xn, size_t wn,
/* bigdivrem_restoring will modify y.
* So use temporary buffer. */
tds = xds + qn;
- assert(qn + bn <= xn + wn);
+ RUBY_ASSERT(qn + bn <= xn + wn);
bary_small_lshift(tds, bds, bn, shift);
xds[xn] = bary_small_lshift(xds, xds, xn, shift);
}
@@ -4906,7 +4910,7 @@ big2str_karatsuba(struct big2str_struct *b2s, BDIGIT *xds, size_t xn, size_t wn,
}
BARY_TRUNC(qds, qn);
- assert(qn <= bn);
+ RUBY_ASSERT(qn <= bn);
big2str_karatsuba(b2s, qds, qn, xn+wn - (rn+qn), lower_power_level, lower_numdigits+taillen);
BARY_TRUNC(rds, rn);
big2str_karatsuba(b2s, rds, rn, xn+wn - rn, lower_power_level, taillen);
@@ -4971,7 +4975,7 @@ big2str_generic(VALUE x, int base)
invalid_radix(base);
if (xn >= LONG_MAX/BITSPERDIG) {
- rb_raise(rb_eRangeError, "bignum too big to convert into `string'");
+ rb_raise(rb_eRangeError, "bignum too big to convert into 'string'");
}
power_level = 0;
@@ -4981,7 +4985,7 @@ big2str_generic(VALUE x, int base)
power_level++;
power = power_cache_get_power(base, power_level, NULL);
}
- assert(power_level != MAX_BASE36_POWER_TABLE_ENTRIES);
+ RUBY_ASSERT(power_level != MAX_BASE36_POWER_TABLE_ENTRIES);
if ((size_t)BIGNUM_LEN(power) <= xn) {
/*
@@ -5096,7 +5100,7 @@ rb_big2str1(VALUE x, int base)
invalid_radix(base);
if (xn >= LONG_MAX/BITSPERDIG) {
- rb_raise(rb_eRangeError, "bignum too big to convert into `string'");
+ rb_raise(rb_eRangeError, "bignum too big to convert into 'string'");
}
if (POW2_P(base)) {
@@ -5132,7 +5136,7 @@ big2ulong(VALUE x, const char *type)
if (len == 0)
return 0;
if (BIGSIZE(x) > sizeof(long)) {
- rb_raise(rb_eRangeError, "bignum too big to convert into `%s'", type);
+ rb_raise(rb_eRangeError, "bignum too big to convert into '%s'", type);
}
ds = BDIGITS(x);
#if SIZEOF_LONG <= SIZEOF_BDIGIT
@@ -5175,7 +5179,7 @@ rb_big2long(VALUE x)
if (num <= 1+(unsigned long)(-(LONG_MIN+1)))
return -(long)(num-1)-1;
}
- rb_raise(rb_eRangeError, "bignum too big to convert into `long'");
+ rb_raise(rb_eRangeError, "bignum too big to convert into 'long'");
}
#if HAVE_LONG_LONG
@@ -5193,7 +5197,7 @@ big2ull(VALUE x, const char *type)
if (len == 0)
return 0;
if (BIGSIZE(x) > SIZEOF_LONG_LONG)
- rb_raise(rb_eRangeError, "bignum too big to convert into `%s'", type);
+ rb_raise(rb_eRangeError, "bignum too big to convert into '%s'", type);
#if SIZEOF_LONG_LONG <= SIZEOF_BDIGIT
num = (unsigned LONG_LONG)ds[0];
#else
@@ -5234,7 +5238,7 @@ rb_big2ll(VALUE x)
if (num <= 1+(unsigned LONG_LONG)(-(LLONG_MIN+1)))
return -(LONG_LONG)(num-1)-1;
}
- rb_raise(rb_eRangeError, "bignum too big to convert into `long long'");
+ rb_raise(rb_eRangeError, "bignum too big to convert into 'long long'");
}
#endif /* HAVE_LONG_LONG */
@@ -5497,10 +5501,10 @@ big_op(VALUE x, VALUE y, enum big_op_t op)
n = FIX2INT(rel);
switch (op) {
- case big_op_gt: return RBOOL(n > 0);
- case big_op_ge: return RBOOL(n >= 0);
- case big_op_lt: return RBOOL(n < 0);
- case big_op_le: return RBOOL(n <= 0);
+ case big_op_gt: return RBOOL(n > 0);
+ case big_op_ge: return RBOOL(n >= 0);
+ case big_op_lt: return RBOOL(n < 0);
+ case big_op_le: return RBOOL(n <= 0);
}
return Qundef;
}
@@ -5656,7 +5660,7 @@ bigsub_int(VALUE x, long y0)
zds = BDIGITS(z);
#if SIZEOF_BDIGIT >= SIZEOF_LONG
- assert(xn == zn);
+ RUBY_ASSERT(xn == zn);
num = (BDIGIT_DBL_SIGNED)xds[0] - y;
if (xn == 1 && num < 0) {
BIGNUM_NEGATE(z);
@@ -5719,7 +5723,7 @@ bigsub_int(VALUE x, long y0)
goto finish;
finish:
- assert(num == 0 || num == -1);
+ RUBY_ASSERT(num == 0 || num == -1);
if (num < 0) {
get2comp(z);
BIGNUM_NEGATE(z);
@@ -6874,63 +6878,11 @@ BDIGIT rb_bdigit_dbl_isqrt(BDIGIT_DBL);
# define BDIGIT_DBL_TO_DOUBLE(n) (double)(n)
#endif
-static BDIGIT *
-estimate_initial_sqrt(VALUE *xp, const size_t xn, const BDIGIT *nds, size_t len)
-{
- enum {dbl_per_bdig = roomof(DBL_MANT_DIG,BITSPERDIG)};
- const int zbits = nlz(nds[len-1]);
- VALUE x = *xp = bignew_1(0, xn, 1); /* division may release the GVL */
- BDIGIT *xds = BDIGITS(x);
- BDIGIT_DBL d = bary2bdigitdbl(nds+len-dbl_per_bdig, dbl_per_bdig);
- BDIGIT lowbits = 1;
- int rshift = (int)((BITSPERDIG*2-zbits+(len&BITSPERDIG&1) - DBL_MANT_DIG + 1) & ~1);
- double f;
-
- if (rshift > 0) {
- lowbits = (BDIGIT)d & ~(~(BDIGIT)1U << rshift);
- d >>= rshift;
- }
- else if (rshift < 0) {
- d <<= -rshift;
- d |= nds[len-dbl_per_bdig-1] >> (BITSPERDIG+rshift);
- }
- f = sqrt(BDIGIT_DBL_TO_DOUBLE(d));
- d = (BDIGIT_DBL)ceil(f);
- if (BDIGIT_DBL_TO_DOUBLE(d) == f) {
- if (lowbits || (lowbits = !bary_zero_p(nds, len-dbl_per_bdig)))
- ++d;
- }
- else {
- lowbits = 1;
- }
- rshift /= 2;
- rshift += (2-(len&1))*BITSPERDIG/2;
- if (rshift >= 0) {
- if (nlz((BDIGIT)d) + rshift >= BITSPERDIG) {
- /* (d << rshift) does cause overflow.
- * example: Integer.sqrt(0xffff_ffff_ffff_ffff ** 2)
- */
- d = ~(BDIGIT_DBL)0;
- }
- else {
- d <<= rshift;
- }
- }
- BDIGITS_ZERO(xds, xn-2);
- bdigitdbl2bary(&xds[xn-2], 2, d);
-
- if (!lowbits) return NULL; /* special case, exact result */
- return xds;
-}
-
VALUE
rb_big_isqrt(VALUE n)
{
BDIGIT *nds = BDIGITS(n);
size_t len = BIGNUM_LEN(n);
- size_t xn = (len+1) / 2;
- VALUE x;
- BDIGIT *xds;
if (len <= 2) {
BDIGIT sq = rb_bdigit_dbl_isqrt(bary2bdigitdbl(nds, len));
@@ -6940,25 +6892,19 @@ rb_big_isqrt(VALUE n)
return ULONG2NUM(sq);
#endif
}
- else if ((xds = estimate_initial_sqrt(&x, xn, nds, len)) != 0) {
- size_t tn = xn + BIGDIVREM_EXTRA_WORDS;
- VALUE t = bignew_1(0, tn, 1);
- BDIGIT *tds = BDIGITS(t);
- tn = BIGNUM_LEN(t);
-
- /* t = n/x */
- while (bary_divmod_branch(tds, tn, NULL, 0, nds, len, xds, xn),
- bary_cmp(tds, tn, xds, xn) < 0) {
- int carry;
- BARY_TRUNC(tds, tn);
- /* x = (x+t)/2 */
- carry = bary_add(xds, xn, xds, xn, tds, tn);
- bary_small_rshift(xds, xds, xn, 1, carry);
- tn = BIGNUM_LEN(t);
+ else {
+ size_t shift = FIX2LONG(rb_big_bit_length(n)) / 4;
+ VALUE n2 = rb_int_rshift(n, SIZET2NUM(2 * shift));
+ VALUE x = FIXNUM_P(n2) ? LONG2FIX(rb_ulong_isqrt(FIX2ULONG(n2))) : rb_big_isqrt(n2);
+ /* x = (x+n/x)/2 */
+ x = rb_int_plus(rb_int_lshift(x, SIZET2NUM(shift - 1)), rb_int_idiv(rb_int_rshift(n, SIZET2NUM(shift + 1)), x));
+ VALUE xx = rb_int_mul(x, x);
+ while (rb_int_gt(xx, n)) {
+ xx = rb_int_minus(xx, rb_int_minus(rb_int_plus(x, x), INT2FIX(1)));
+ x = rb_int_minus(x, INT2FIX(1));
}
+ return x;
}
- RBASIC_SET_CLASS_RAW(x, rb_cInteger);
- return x;
}
#if USE_GMP
@@ -6997,7 +6943,7 @@ int_pow_tmp3(VALUE x, VALUE y, VALUE m, int nega_flg)
if (FIXNUM_P(y)) {
y = rb_int2big(FIX2LONG(y));
}
- assert(RB_BIGNUM_TYPE_P(m));
+ RUBY_ASSERT(RB_BIGNUM_TYPE_P(m));
xn = BIGNUM_LEN(x);
yn = BIGNUM_LEN(y);
mn = BIGNUM_LEN(m);
diff --git a/bootstraptest/runner.rb b/bootstraptest/runner.rb
index c8ba824407..120b78246c 100755
--- a/bootstraptest/runner.rb
+++ b/bootstraptest/runner.rb
@@ -6,7 +6,6 @@
# Never use optparse in this file.
# Never use test/unit in this file.
# Never use Ruby extensions in this file.
-# Maintain Ruby 1.8 compatibility for now
$start_time = Time.now
@@ -77,6 +76,8 @@ bt = Struct.new(:ruby,
:width,
:indent,
:platform,
+ :timeout,
+ :timeout_scale,
)
BT = Class.new(bt) do
def indent=(n)
@@ -144,6 +145,10 @@ BT = Class.new(bt) do
end
super wn
end
+
+ def apply_timeout_scale(timeout)
+ timeout&.*(timeout_scale)
+ end
end.new
BT_STATE = Struct.new(:count, :error).new
@@ -156,6 +161,8 @@ def main
BT.color = nil
BT.tty = nil
BT.quiet = false
+ BT.timeout = 180
+ BT.timeout_scale = (defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? ? 3 : 1) # for --jit-wait
# BT.wn = 1
dir = nil
quiet = false
@@ -186,14 +193,18 @@ def main
warn "unknown --tty argument: #$3" if $3
BT.tty = !$1 || !$2
true
- when /\A(-q|--q(uiet))\z/
+ when /\A(-q|--q(uiet)?)\z/
quiet = true
BT.quiet = true
true
when /\A-j(\d+)?/
BT.wn = $1.to_i
true
- when /\A(-v|--v(erbose))\z/
+ when /\A--timeout=(\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?)(?::(\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?))?/
+ BT.timeout = $1.to_f
+ BT.timeout_scale = $2.to_f if defined?($2)
+ true
+ when /\A(-v|--v(erbose)?)\z/
BT.verbose = true
BT.quiet = false
true
@@ -205,6 +216,7 @@ Usage: #{File.basename($0, '.*')} --ruby=PATH [--sets=NAME,NAME,...]
default: /tmp/bootstraptestXXXXX.tmpwd
--color[=WHEN] Colorize the output. WHEN defaults to 'always'
or can be 'never' or 'auto'.
+ --timeout=TIMEOUT Default timeout in seconds.
-s, --stress stress test.
-v, --verbose Output test name before exec.
-q, --quiet Don\'t print header message.
@@ -428,7 +440,7 @@ class Assertion < Struct.new(:src, :path, :lineno, :proc)
def initialize(*args)
super
self.class.add self
- @category = self.path.match(/test_(.+)\.rb/)[1]
+ @category = self.path[/\Atest_(.+)\.rb\z/, 1]
end
def call
@@ -526,14 +538,16 @@ class Assertion < Struct.new(:src, :path, :lineno, :proc)
end
end
- def get_result_string(opt = '', **argh)
+ def get_result_string(opt = '', timeout: BT.timeout, **argh)
if BT.ruby
+ timeout = BT.apply_timeout_scale(timeout)
filename = make_srcfile(**argh)
begin
kw = self.err ? {err: self.err} : {}
out = IO.popen("#{BT.ruby} -W0 #{opt} #{filename}", **kw)
pid = out.pid
- out.read.tap{ Process.waitpid(pid); out.close }
+ th = Thread.new {out.read.tap {Process.waitpid(pid); out.close}}
+ th.value if th.join(timeout)
ensure
raise Interrupt if $? and $?.signaled? && $?.termsig == Signal.list["INT"]
@@ -551,9 +565,14 @@ class Assertion < Struct.new(:src, :path, :lineno, :proc)
def make_srcfile(frozen_string_literal: nil)
filename = "bootstraptest.#{self.path}_#{self.lineno}_#{self.id}.rb"
File.open(filename, 'w') {|f|
- f.puts "#frozen_string_literal:true" if frozen_string_literal
- f.puts "GC.stress = true" if $stress
- f.puts "print(begin; #{self.src}; end)"
+ f.puts "#frozen_string_literal:#{frozen_string_literal}" unless frozen_string_literal.nil?
+ if $stress
+ f.puts "GC.stress = true" if $stress
+ else
+ f.puts ""
+ end
+ f.puts "class BT_Skip < Exception; end; def skip(msg) = raise(BT_Skip, msg.to_s)"
+ f.puts "print(begin; #{self.src}; rescue BT_Skip; $!.message; end)"
}
filename
end
@@ -567,9 +586,9 @@ def add_assertion src, pr
Assertion.new(src, path, lineno, pr)
end
-def assert_equal(expected, testsrc, message = '', opt = '', **argh)
+def assert_equal(expected, testsrc, message = '', opt = '', **kwargs)
add_assertion testsrc, -> as do
- as.assert_check(message, opt, **argh) {|result|
+ as.assert_check(message, opt, **kwargs) {|result|
if expected == result
nil
else
@@ -580,9 +599,9 @@ def assert_equal(expected, testsrc, message = '', opt = '', **argh)
end
end
-def assert_match(expected_pattern, testsrc, message = '')
+def assert_match(expected_pattern, testsrc, message = '', **argh)
add_assertion testsrc, -> as do
- as.assert_check(message) {|result|
+ as.assert_check(message, **argh) {|result|
if expected_pattern =~ result
nil
else
@@ -614,8 +633,9 @@ def assert_valid_syntax(testsrc, message = '')
end
end
-def assert_normal_exit(testsrc, *rest, timeout: nil, **opt)
+def assert_normal_exit(testsrc, *rest, timeout: BT.timeout, **opt)
add_assertion testsrc, -> as do
+ timeout = BT.apply_timeout_scale(timeout)
message, ignore_signals = rest
message ||= ''
as.show_progress(message) {
@@ -669,9 +689,7 @@ end
def assert_finish(timeout_seconds, testsrc, message = '')
add_assertion testsrc, -> as do
- if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? # for --jit-wait
- timeout_seconds *= 3
- end
+ timeout_seconds = BT.apply_timeout_scale(timeout_seconds)
as.show_progress(message) {
faildesc = nil
@@ -784,4 +802,13 @@ def check_coredump
end
end
+def yjit_enabled?
+ ENV.key?('RUBY_YJIT_ENABLE') || ENV.fetch('RUN_OPTS', '').include?('yjit') || BT.ruby.include?('yjit')
+end
+
+def rjit_enabled?
+ # Don't check `RubyVM::RJIT.enabled?`. On btest-bruby, target Ruby != runner Ruby.
+ ENV.fetch('RUN_OPTS', '').include?('rjit')
+end
+
exit main
diff --git a/bootstraptest/test_eval.rb b/bootstraptest/test_eval.rb
index a9f389c673..d923a957bc 100644
--- a/bootstraptest/test_eval.rb
+++ b/bootstraptest/test_eval.rb
@@ -227,6 +227,16 @@ assert_equal %q{[10, main]}, %q{
}, '[ruby-dev:31372]'
end
+assert_normal_exit %{
+ $stderr = STDOUT
+ 5000.times do
+ begin
+ eval "0 rescue break"
+ rescue SyntaxError
+ end
+ end
+}
+
assert_normal_exit %q{
$stderr = STDOUT
class Foo
@@ -354,3 +364,34 @@ assert_normal_exit %q{
end
}, 'check escaping the internal value th->base_block'
+assert_equal "false", <<~RUBY, "literal strings are mutable", "--disable-frozen-string-literal"
+ eval("'test'").frozen?
+RUBY
+
+assert_equal "false", <<~RUBY, "literal strings are mutable", "--disable-frozen-string-literal", frozen_string_literal: true
+ eval("'test'").frozen?
+RUBY
+
+assert_equal "true", <<~RUBY, "literal strings are frozen", "--enable-frozen-string-literal"
+ eval("'test'").frozen?
+RUBY
+
+assert_equal "true", <<~RUBY, "literal strings are frozen", "--enable-frozen-string-literal", frozen_string_literal: false
+ eval("'test'").frozen?
+RUBY
+
+assert_equal "false", <<~RUBY, "__FILE__ is mutable", "--disable-frozen-string-literal"
+ eval("__FILE__").frozen?
+RUBY
+
+assert_equal "false", <<~RUBY, "__FILE__ is mutable", "--disable-frozen-string-literal", frozen_string_literal: true
+ eval("__FILE__").frozen?
+RUBY
+
+assert_equal "true", <<~RUBY, "__FILE__ is frozen", "--enable-frozen-string-literal"
+ eval("__FILE__").frozen?
+RUBY
+
+assert_equal "true", <<~RUBY, "__FILE__ is frozen", "--enable-frozen-string-literal", frozen_string_literal: false
+ eval("__FILE__").frozen?
+RUBY
diff --git a/bootstraptest/test_exception.rb b/bootstraptest/test_exception.rb
index 0fb6f552b8..decfdc08a3 100644
--- a/bootstraptest/test_exception.rb
+++ b/bootstraptest/test_exception.rb
@@ -370,7 +370,7 @@ assert_equal %q{}, %q{
}
##
-assert_match /undefined method `foo\'/, %q{#`
+assert_match /undefined method 'foo\'/, %q{#`
STDERR.reopen(STDOUT)
class C
def inspect
diff --git a/bootstraptest/test_finalizer.rb b/bootstraptest/test_finalizer.rb
index 22a16b1220..ccfa0b55d6 100644
--- a/bootstraptest/test_finalizer.rb
+++ b/bootstraptest/test_finalizer.rb
@@ -6,3 +6,11 @@ ObjectSpace.define_finalizer(b1,proc{b1.inspect})
ObjectSpace.define_finalizer(a2,proc{a1.inspect})
ObjectSpace.define_finalizer(a1,proc{})
}, '[ruby-dev:35778]'
+
+assert_equal 'true', %q{
+ obj = Object.new
+ id = obj.object_id
+
+ ObjectSpace.define_finalizer(obj, proc { |i| print(id == i) })
+ nil
+}
diff --git a/bootstraptest/test_flow.rb b/bootstraptest/test_flow.rb
index 35f19db588..15528a4213 100644
--- a/bootstraptest/test_flow.rb
+++ b/bootstraptest/test_flow.rb
@@ -363,7 +363,7 @@ assert_equal %q{[1, 2, 3, 5, 2, 3, 5, 7, 8]}, %q{$a = []; begin; ; $a << 1
; $a << 8
; rescue Exception; $a << 99; end; $a}
assert_equal %q{[1, 2, 6, 3, 5, 7, 8]}, %q{$a = []; begin; ; $a << 1
- o = "test"; $a << 2
+ o = "test".dup; $a << 2
def o.test(a); $a << 3
return a; $a << 4
ensure; $a << 5
diff --git a/bootstraptest/test_gc.rb b/bootstraptest/test_gc.rb
index 9f17e14814..17bc497822 100644
--- a/bootstraptest/test_gc.rb
+++ b/bootstraptest/test_gc.rb
@@ -14,7 +14,7 @@ ms = "a".."k"
o.send(meth)
end
end
-}, '[ruby-dev:39453]' unless ENV.fetch('RUN_OPTS', '').include?('rjit') # speed up RJIT CI
+}, '[ruby-dev:39453]' unless rjit_enabled? # speed up RJIT CI
assert_normal_exit %q{
a = []
diff --git a/bootstraptest/test_insns.rb b/bootstraptest/test_insns.rb
index d2e799f855..06828a7f7a 100644
--- a/bootstraptest/test_insns.rb
+++ b/bootstraptest/test_insns.rb
@@ -92,7 +92,7 @@ tests = [
[ 'intern', %q{ :"#{true}" }, ],
[ 'newarray', %q{ ["true"][0] }, ],
- [ 'newarraykwsplat', %q{ [**{x:'true'}][0][:x] }, ],
+ [ 'pushtoarraykwsplat', %q{ [**{x:'true'}][0][:x] }, ],
[ 'duparray', %q{ [ true ][0] }, ],
[ 'expandarray', %q{ y = [ true, false, nil ]; x, = y; x }, ],
[ 'expandarray', %q{ y = [ true, false, nil ]; x, *z = y; x }, ],
@@ -354,7 +354,7 @@ tests = [
[ 'opt_ge', %q{ +0.0.next_float >= 0.0 }, ],
[ 'opt_ge', %q{ ?z >= ?a }, ],
- [ 'opt_ltlt', %q{ '' << 'true' }, ],
+ [ 'opt_ltlt', %q{ +'' << 'true' }, ],
[ 'opt_ltlt', %q{ ([] << 'true').join }, ],
[ 'opt_ltlt', %q{ (1 << 31) == 2147483648 }, ],
@@ -363,7 +363,7 @@ tests = [
[ 'opt_aref', %q{ 'true'[0] == ?t }, ],
[ 'opt_aset', %q{ [][0] = true }, ],
[ 'opt_aset', %q{ {}[0] = true }, ],
- [ 'opt_aset', %q{ x = 'frue'; x[0] = 't'; x }, ],
+ [ 'opt_aset', %q{ x = +'frue'; x[0] = 't'; x }, ],
[ 'opt_aset', <<-'},', ], # {
# opt_aref / opt_aset mixup situation
class X; def x; {}; end; end
diff --git a/bootstraptest/test_jump.rb b/bootstraptest/test_jump.rb
index d07c47a56d..8751343b1f 100644
--- a/bootstraptest/test_jump.rb
+++ b/bootstraptest/test_jump.rb
@@ -292,7 +292,7 @@ assert_equal "true", %q{
end
end
end
- s = "foo"
+ s = +"foo"
s.return_eigenclass == class << s; self; end
}, '[ruby-core:21379]'
diff --git a/bootstraptest/test_literal.rb b/bootstraptest/test_literal.rb
index a0d4ee08c6..a30661a796 100644
--- a/bootstraptest/test_literal.rb
+++ b/bootstraptest/test_literal.rb
@@ -70,6 +70,7 @@ if /wasi/ !~ target_platform
assert_equal "foo\n", %q(`echo foo`)
assert_equal "foo\n", %q(s = "foo"; `echo #{s}`)
end
+assert_equal "ECHO FOO", %q(def `(s) s.upcase; end; `echo foo`)
# regexp
assert_equal '', '//.source'
diff --git a/bootstraptest/test_literal_suffix.rb b/bootstraptest/test_literal_suffix.rb
index c36fa7078f..7a4d67d0fa 100644
--- a/bootstraptest/test_literal_suffix.rb
+++ b/bootstraptest/test_literal_suffix.rb
@@ -46,9 +46,9 @@ assert_equal '1', '1rescue nil'
assert_equal '10000000000000000001/10000000000000000000',
'1.0000000000000000001r'
-assert_equal 'syntax error, unexpected local variable or method, expecting end-of-input',
- %q{begin eval('1ir', nil, '', 0); rescue SyntaxError => e; e.message[/\A:(?:\d+:)? (.*)/, 1] end}
-assert_equal 'syntax error, unexpected local variable or method, expecting end-of-input',
- %q{begin eval('1.2ir', nil, '', 0); rescue SyntaxError => e; e.message[/\A:(?:\d+:)? (.*)/, 1] end}
-assert_equal 'syntax error, unexpected local variable or method, expecting end-of-input',
- %q{begin eval('1e1r', nil, '', 0); rescue SyntaxError => e; e.message[/\A:(?:\d+:)? (.*)/, 1] end}
+assert_equal 'unexpected local variable or method, expecting end-of-input',
+ %q{begin eval('1ir', nil, '', 0); rescue SyntaxError => e; e.message[/(?:\^~*|\A:(?:\d+:)? syntax error,) (.*)/, 1]; end}
+assert_equal 'unexpected local variable or method, expecting end-of-input',
+ %q{begin eval('1.2ir', nil, '', 0); rescue SyntaxError => e; e.message[/(?:\^~*|\A:(?:\d+:)? syntax error,) (.*)/, 1]; end}
+assert_equal 'unexpected local variable or method, expecting end-of-input',
+ %q{begin eval('1e1r', nil, '', 0); rescue SyntaxError => e; e.message[/(?:\^~*|\A:(?:\d+:)? syntax error,) (.*)/, 1]; end}
diff --git a/bootstraptest/test_method.rb b/bootstraptest/test_method.rb
index 04c9eb2d11..d1d1f57d55 100644
--- a/bootstraptest/test_method.rb
+++ b/bootstraptest/test_method.rb
@@ -340,24 +340,6 @@ assert_equal '1', %q( class C; def m() 7 end; private :m end
assert_equal '1', %q( class C; def m() 1 end; private :m end
C.new.send(:m) )
-# with block
-assert_equal '[[:ok1, :foo], [:ok2, :foo, :bar]]',
-%q{
- class C
- def [](a)
- $ary << [yield, a]
- end
- def []=(a, b)
- $ary << [yield, a, b]
- end
- end
-
- $ary = []
- C.new[:foo, &lambda{:ok1}]
- C.new[:foo, &lambda{:ok2}] = :bar
- $ary
-}
-
# with
assert_equal '[:ok1, [:ok2, 11]]', %q{
class C
@@ -404,7 +386,6 @@ $result
# aset and splat
assert_equal '4', %q{class Foo;def []=(a,b,c,d);end;end;Foo.new[1,*a=[2,3]]=4}
-assert_equal '4', %q{class Foo;def []=(a,b,c,d);end;end;def m(&blk)Foo.new[1,*a=[2,3],&blk]=4;end;m{}}
# post test
assert_equal %q{[1, 2, :o1, :o2, [], 3, 4, NilClass, nil, nil]}, %q{
@@ -1107,10 +1088,6 @@ assert_equal 'ok', %q{
'ok'
end
}
-assert_equal 'ok', %q{
- [0][0, &proc{}] += 21
- 'ok'
-}, '[ruby-core:30534]'
# should not cache when splat
assert_equal 'ok', %q{
@@ -1190,3 +1167,12 @@ assert_equal 'DC', %q{
test2 o1, [], block
$result.join
}
+
+assert_equal 'ok', %q{
+ def foo
+ binding
+ ["ok"].first
+ end
+ foo
+ foo
+}, '[Bug #20178]'
diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb
index f92b604d2a..451b58e793 100644
--- a/bootstraptest/test_ractor.rb
+++ b/bootstraptest/test_ractor.rb
@@ -601,7 +601,7 @@ assert_equal '{:ok=>3}', %q{
end
3.times.map{Ractor.receive}.tally
-}
+} unless yjit_enabled? # `[BUG] Bus Error at 0x000000010b7002d0` in jit_exec()
# unshareable object are copied
assert_equal 'false', %q{
@@ -628,7 +628,7 @@ assert_equal "allocator undefined for Thread", %q{
}
# send shareable and unshareable objects
-assert_equal "ok", %q{
+assert_equal "ok", <<~'RUBY', frozen_string_literal: false
echo_ractor = Ractor.new do
loop do
v = Ractor.receive
@@ -695,10 +695,10 @@ assert_equal "ok", %q{
else
results.inspect
end
-}
+RUBY
# frozen Objects are shareable
-assert_equal [false, true, false].inspect, %q{
+assert_equal [false, true, false].inspect, <<~'RUBY', frozen_string_literal: false
class C
def initialize freeze
@a = 1
@@ -721,11 +721,11 @@ assert_equal [false, true, false].inspect, %q{
results << check(C.new(true)) # false
results << check(C.new(true).freeze) # true
results << check(C.new(false).freeze) # false
-}
+RUBY
# move example2: String
# touching moved object causes an error
-assert_equal 'hello world', %q{
+assert_equal 'hello world', <<~'RUBY', frozen_string_literal: false
# move
r = Ractor.new do
obj = Ractor.receive
@@ -743,7 +743,7 @@ assert_equal 'hello world', %q{
else
raise 'unreachable'
end
-}
+RUBY
# move example2: Array
assert_equal '[0, 1]', %q{
@@ -946,7 +946,7 @@ assert_equal 'ArgumentError', %q{
}
# ivar in shareable-objects are not allowed to access from non-main Ractor
-assert_equal "can not get unshareable values from instance variables of classes/modules from non-main Ractors", %q{
+assert_equal "can not get unshareable values from instance variables of classes/modules from non-main Ractors", <<~'RUBY', frozen_string_literal: false
class C
@iv = 'str'
end
@@ -957,13 +957,12 @@ assert_equal "can not get unshareable values from instance variables of classes/
end
end
-
begin
r.take
rescue Ractor::RemoteError => e
e.cause.message
end
-}
+RUBY
# ivar in shareable-objects are not allowed to access from non-main Ractor
assert_equal 'can not access instance variables of shareable objects from non-main Ractors', %q{
@@ -1087,6 +1086,27 @@ assert_equal '333', %q{
a + b + c + d + e + f
}
+# moved objects have their shape properly set to original object's shape
+assert_equal '1234', %q{
+class Obj
+ attr_accessor :a, :b, :c, :d
+ def initialize
+ @a = 1
+ @b = 2
+ @c = 3
+ end
+end
+r = Ractor.new do
+ obj = receive
+ obj.d = 4
+ [obj.a, obj.b, obj.c, obj.d]
+end
+obj = Obj.new
+r.send(obj, move: true)
+values = r.take
+values.join
+}
+
# cvar in shareable-objects are not allowed to access from non-main Ractor
assert_equal 'can not access class variables from non-main Ractors', %q{
class C
@@ -1129,7 +1149,7 @@ assert_equal 'can not access class variables from non-main Ractors', %q{
}
# Getting non-shareable objects via constants by other Ractors is not allowed
-assert_equal 'can not access non-shareable objects in constant C::CONST by non-main Ractor.', %q{
+assert_equal 'can not access non-shareable objects in constant C::CONST by non-main Ractor.', <<~'RUBY', frozen_string_literal: false
class C
CONST = 'str'
end
@@ -1141,10 +1161,10 @@ assert_equal 'can not access non-shareable objects in constant C::CONST by non-m
rescue Ractor::RemoteError => e
e.cause.message
end
-}
+ RUBY
# Constant cache should care about non-sharable constants
-assert_equal "can not access non-shareable objects in constant Object::STR by non-main Ractor.", %q{
+assert_equal "can not access non-shareable objects in constant Object::STR by non-main Ractor.", <<~'RUBY', frozen_string_literal: false
STR = "hello"
def str; STR; end
s = str() # fill const cache
@@ -1153,10 +1173,10 @@ assert_equal "can not access non-shareable objects in constant Object::STR by no
rescue Ractor::RemoteError => e
e.cause.message
end
-}
+RUBY
# Setting non-shareable objects into constants by other Ractors is not allowed
-assert_equal 'can not set constants with non-shareable objects by non-main Ractors', %q{
+assert_equal 'can not set constants with non-shareable objects by non-main Ractors', <<~'RUBY', frozen_string_literal: false
class C
end
r = Ractor.new do
@@ -1167,7 +1187,7 @@ assert_equal 'can not set constants with non-shareable objects by non-main Racto
rescue Ractor::RemoteError => e
e.cause.message
end
-}
+RUBY
# define_method is not allowed
assert_equal "defined with an un-shareable Proc in a different Ractor", %q{
@@ -1220,7 +1240,7 @@ assert_equal '0', %q{
}
# ObjectSpace._id2ref can not handle unshareable objects with Ractors
-assert_equal 'ok', %q{
+assert_equal 'ok', <<~'RUBY', frozen_string_literal: false
s = 'hello'
Ractor.new s.object_id do |id ;s|
@@ -1230,10 +1250,10 @@ assert_equal 'ok', %q{
:ok
end
end.take
-}
+RUBY
# Ractor.make_shareable(obj)
-assert_equal 'true', %q{
+assert_equal 'true', <<~'RUBY', frozen_string_literal: false
class C
def initialize
@a = 'foo'
@@ -1304,7 +1324,7 @@ assert_equal 'true', %q{
}
Ractor.shareable?(a)
-}
+RUBY
# Ractor.make_shareable(obj) doesn't freeze shareable objects
assert_equal 'true', %q{
@@ -1401,14 +1421,14 @@ assert_equal '[false, false, true, true]', %q{
}
# TracePoint with normal Proc should be Ractor local
-assert_equal '[4, 8]', %q{
+assert_equal '[6, 10]', %q{
rs = []
TracePoint.new(:line){|tp| rs << tp.lineno if tp.path == __FILE__}.enable do
- Ractor.new{ # line 4
+ Ractor.new{ # line 5
a = 1
b = 2
}.take
- c = 3 # line 8
+ c = 3 # line 9
end
rs
}
@@ -1482,7 +1502,7 @@ assert_equal "#{n}#{n}", %Q{
2.times.map{
Ractor.new do
#{n}.times do
- obj = ''
+ obj = +''
obj.instance_variable_set("@a", 1)
obj.instance_variable_set("@b", 1)
obj.instance_variable_set("@c", 1)
@@ -1532,7 +1552,7 @@ assert_equal "ok", %q{
1_000.times { idle_worker, tmp_reporter = Ractor.select(*workers) }
"ok"
-} unless ENV['RUN_OPTS'] =~ /rjit/ # flaky
+} unless yjit_enabled? || rjit_enabled? # flaky
assert_equal "ok", %q{
def foo(*); ->{ super }; end
@@ -1641,18 +1661,40 @@ assert_match /\Atest_ractor\.rb:1:\s+warning:\s+Ractor is experimental/, %q{
Warning[:experimental] = $VERBOSE = true
STDERR.reopen(STDOUT)
eval("Ractor.new{}.take", nil, "test_ractor.rb", 1)
+}, frozen_string_literal: false
+
+# check moved object
+assert_equal 'ok', %q{
+ r = Ractor.new do
+ Ractor.receive
+ GC.start
+ :ok
+ end
+
+ obj = begin
+ raise
+ rescue => e
+ e = Marshal.load(Marshal.dump(e))
+ end
+
+ r.send obj, move: true
+ r.take
}
## Ractor::Selector
# Selector#empty? returns true
assert_equal 'true', %q{
+ skip true unless defined? Ractor::Selector
+
s = Ractor::Selector.new
s.empty?
}
# Selector#empty? returns false if there is target ractors
assert_equal 'false', %q{
+ skip false unless defined? Ractor::Selector
+
s = Ractor::Selector.new
s.add Ractor.new{}
s.empty?
@@ -1660,6 +1702,8 @@ assert_equal 'false', %q{
# Selector#clear removes all ractors from the waiting list
assert_equal 'true', %q{
+ skip true unless defined? Ractor::Selector
+
s = Ractor::Selector.new
s.add Ractor.new{10}
s.add Ractor.new{20}
@@ -1669,6 +1713,8 @@ assert_equal 'true', %q{
# Selector#wait can wait multiple ractors
assert_equal '[10, 20, true]', %q{
+ skip [10, 20, true] unless defined? Ractor::Selector
+
s = Ractor::Selector.new
s.add Ractor.new{10}
s.add Ractor.new{20}
@@ -1678,10 +1724,12 @@ assert_equal '[10, 20, true]', %q{
r, v = s.wait
vs << v
[*vs.sort, s.empty?]
-}
+} if defined? Ractor::Selector
# Selector#wait can wait multiple ractors with receiving.
assert_equal '30', %q{
+ skip 30 unless defined? Ractor::Selector
+
RN = 30
rs = RN.times.map{
Ractor.new{ :v }
@@ -1698,11 +1746,12 @@ assert_equal '30', %q{
end
results.size
-}
+} if defined? Ractor::Selector
# Selector#wait can support dynamic addition
-yjit_enabled = ENV.key?('RUBY_YJIT_ENABLE') || ENV.fetch('RUN_OPTS', '').include?('yjit') || BT.ruby.include?('yjit')
assert_equal '600', %q{
+ skip 600 unless defined? Ractor::Selector
+
RN = 100
s = Ractor::Selector.new
rs = RN.times.map{
@@ -1728,10 +1777,12 @@ assert_equal '600', %q{
end
h.sum{|k, v| v}
-} unless yjit_enabled # http://ci.rvm.jp/results/trunk-yjit@ruby-sp2-docker/4466770
+} unless yjit_enabled? # http://ci.rvm.jp/results/trunk-yjit@ruby-sp2-docker/4466770
-# Selector should be GCed (free'ed) withtou trouble
+# Selector should be GCed (free'ed) without trouble
assert_equal 'ok', %q{
+ skip :ok unless defined? Ractor::Selector
+
RN = 30
rs = RN.times.map{
Ractor.new{ :v }
@@ -1741,3 +1792,14 @@ assert_equal 'ok', %q{
}
end # if !ENV['GITHUB_WORKFLOW']
+
+# Chilled strings are not shareable
+assert_equal 'false', %q{
+ Ractor.shareable?("chilled")
+}
+
+# Chilled strings can be made shareable
+assert_equal 'true', %q{
+ shareable = Ractor.make_shareable("chilled")
+ shareable == "chilled" && Ractor.shareable?(shareable)
+}
diff --git a/bootstraptest/test_rjit.rb b/bootstraptest/test_rjit.rb
index 464af7a6e6..e123f35160 100644
--- a/bootstraptest/test_rjit.rb
+++ b/bootstraptest/test_rjit.rb
@@ -42,3 +42,17 @@ assert_equal '1', %q{
entry
}
+
+# Updating local type in Context
+assert_normal_exit %q{
+ def foo(flag, object)
+ klass = if flag
+ object
+ end
+ klass ||= object
+ return klass.new
+ end
+
+ foo(false, Object)
+ foo(true, Object)
+}
diff --git a/bootstraptest/test_syntax.rb b/bootstraptest/test_syntax.rb
index 948e2d7809..fbc9c6f62e 100644
--- a/bootstraptest/test_syntax.rb
+++ b/bootstraptest/test_syntax.rb
@@ -528,24 +528,24 @@ assert_equal %q{1}, %q{
i
}
def assert_syntax_error expected, code, message = ''
- assert_equal "#{expected}",
- "begin eval(%q{#{code}}, nil, '', 0)"'; rescue SyntaxError => e; e.message[/\A:(?:\d+:)? (.*)/, 1] end', message
+ assert_match /^#{Regexp.escape(expected)}/,
+ "begin eval(%q{#{code}}, nil, '', 0)"'; rescue SyntaxError => e; e.message[/(?:\^~*|\A:(?:\d+:)?(?! syntax errors? found)(?: syntax error,)?) (.*)/, 1] end', message
end
assert_syntax_error "unterminated string meets end of file", '().."', '[ruby-dev:29732]'
assert_equal %q{[]}, %q{$&;[]}, '[ruby-dev:31068]'
-assert_syntax_error "syntax error, unexpected *, expecting '}'", %q{{*0}}, '[ruby-dev:31072]'
-assert_syntax_error "`@0' is not allowed as an instance variable name", %q{@0..0}, '[ruby-dev:31095]'
-assert_syntax_error "identifier $00 is not valid to get", %q{$00..0}, '[ruby-dev:31100]'
-assert_syntax_error "identifier $00 is not valid to set", %q{0..$00=1}
+assert_syntax_error "unexpected *, expecting '}'", %q{{*0}}, '[ruby-dev:31072]'
+assert_syntax_error "'@0' is not allowed as an instance variable name", %q{@0..0}, '[ruby-dev:31095]'
+assert_syntax_error "'$00' is not allowed as a global variable name", %q{$00..0}, '[ruby-dev:31100]'
+assert_syntax_error "'$00' is not allowed as a global variable name", %q{0..$00=1}
assert_equal %q{0}, %q{[*0];0}, '[ruby-dev:31102]'
-assert_syntax_error "syntax error, unexpected ')'", %q{v0,(*,v1,) = 0}, '[ruby-dev:31104]'
+assert_syntax_error "unexpected ')'", %q{v0,(*,v1,) = 0}, '[ruby-dev:31104]'
assert_equal %q{1}, %q{
class << (ary=[]); def []; 0; end; def []=(x); super(0,x);end;end; ary[]+=1
}, '[ruby-dev:31110]'
assert_syntax_error "Can't set variable $1", %q{0..$1=1}, '[ruby-dev:31118]'
assert_valid_syntax %q{1.times{1+(1&&next)}}, '[ruby-dev:31119]'
assert_valid_syntax %q{x=-1;loop{x+=1&&redo if (x+=1).zero?}}, '[ruby-dev:31119]'
-assert_syntax_error %q{syntax error, unexpected end-of-input}, %q{!}, '[ruby-dev:31243]'
+assert_syntax_error %q{unexpected end-of-input}, %q{!}, '[ruby-dev:31243]'
assert_equal %q{[nil]}, %q{[()]}, '[ruby-dev:31252]'
assert_equal %q{true}, %q{!_=()}, '[ruby-dev:31263]'
assert_equal 'ok', %q{while true; redo; end if 1 == 2; :ok}, '[ruby-dev:31360]'
@@ -629,7 +629,7 @@ assert_equal '2', %q{
assert_match /invalid multibyte char/, %q{
$stderr = STDOUT
- eval("\"\xf0".force_encoding("utf-8"))
+ eval("\"\xf0".dup.force_encoding("utf-8"))
}, '[ruby-dev:32429]'
# method ! and !=
@@ -904,3 +904,35 @@ assert_normal_exit %q{
Class
end
}, '[ruby-core:30293]'
+
+assert_equal "false", <<~RUBY, "literal strings are mutable", "--disable-frozen-string-literal"
+ 'test'.frozen?
+RUBY
+
+assert_equal "true", <<~RUBY, "literal strings are frozen", "--disable-frozen-string-literal", frozen_string_literal: true
+ 'test'.frozen?
+RUBY
+
+assert_equal "true", <<~RUBY, "literal strings are frozen", "--enable-frozen-string-literal"
+ 'test'.frozen?
+RUBY
+
+assert_equal "false", <<~RUBY, "literal strings are mutable", "--enable-frozen-string-literal", frozen_string_literal: false
+ 'test'.frozen?
+RUBY
+
+assert_equal "false", <<~RUBY, "__FILE__ is mutable", "--disable-frozen-string-literal"
+ __FILE__.frozen?
+RUBY
+
+assert_equal "true", <<~RUBY, "__FILE__ is frozen", "--disable-frozen-string-literal", frozen_string_literal: true
+ __FILE__.frozen?
+RUBY
+
+assert_equal "true", <<~RUBY, "__FILE__ is frozen", "--enable-frozen-string-literal"
+ __FILE__.frozen?
+RUBY
+
+assert_equal "false", <<~RUBY, "__FILE__ is mutable", "--enable-frozen-string-literal", frozen_string_literal: false
+ __FILE__.frozen?
+RUBY
diff --git a/bootstraptest/test_thread.rb b/bootstraptest/test_thread.rb
index d7d19e97c9..a4d46e2f10 100644
--- a/bootstraptest/test_thread.rb
+++ b/bootstraptest/test_thread.rb
@@ -242,6 +242,20 @@ assert_equal 'true', %{
end
}
+assert_equal 'true', %{
+ Thread.new{}.join
+ begin
+ Process.waitpid2 fork{
+ Thread.new{
+ sleep 0.1
+ }.join
+ }
+ true
+ rescue NotImplementedError
+ true
+ end
+}
+
assert_equal 'ok', %{
open("zzz_t1.rb", "w") do |f|
f.puts <<-END
@@ -289,7 +303,7 @@ assert_normal_exit %q{
}.each {|t|
t.join
}
-}
+} unless rjit_enabled? # flaky
assert_equal 'ok', %q{
def m
@@ -483,7 +497,7 @@ assert_equal 'foo', %q{
[th1, th2].each {|t| t.join }
GC.start
f.call.source
-} unless ENV['RUN_OPTS'] =~ /rjit/ # flaky
+} unless rjit_enabled? # flaky
assert_normal_exit %q{
class C
def inspect
diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb
index a6525e7af9..31d2aff994 100644
--- a/bootstraptest/test_yjit.rb
+++ b/bootstraptest/test_yjit.rb
@@ -1,3 +1,280 @@
+# To run the tests in this file only, with YJIT enabled:
+# make btest BTESTS=bootstraptest/test_yjit.rb RUN_OPTS="--yjit-call-threshold=1"
+
+# regression test for popping before side exit
+assert_equal "ok", %q{
+ def foo(a, *) = a
+
+ def call(args, &)
+ foo(1) # spill at where the block arg will be
+ foo(*args, &)
+ end
+
+ call([1, 2])
+
+ begin
+ call([])
+ rescue ArgumentError
+ :ok
+ end
+}
+
+# regression test for send processing before side exit
+assert_equal "ok", %q{
+ def foo(a, *) = :foo
+
+ def call(args)
+ send(:foo, *args)
+ end
+
+ call([1, 2])
+
+ begin
+ call([])
+ rescue ArgumentError
+ :ok
+ end
+}
+
+# test discarding extra yield arguments
+assert_equal "2210150001501015", %q{
+ def splat_kw(ary) = yield *ary, a: 1
+
+ def splat(ary) = yield *ary
+
+ def kw = yield 1, 2, a: 0
+
+ def simple = yield 0, 1
+
+ def calls
+ [
+ splat([1, 1, 2]) { |x, y| x + y },
+ splat([1, 1, 2]) { |y, opt = raise| opt + y},
+ splat_kw([0, 1]) { |a:| a },
+ kw { |a:| a },
+ kw { |a| a },
+ simple { 5.itself },
+ simple { |a| a },
+ simple { |opt = raise| opt },
+ simple { |*rest| rest },
+ simple { |opt_kw: 5| opt_kw },
+ # autosplat ineractions
+ [0, 1, 2].yield_self { |a, b| [a, b] },
+ [0, 1, 2].yield_self { |a, opt = raise| [a, opt] },
+ [1].yield_self { |a, opt = 4| a + opt },
+ ]
+ end
+
+ calls.join
+}
+
+# test autosplat with empty splat
+assert_equal "ok", %q{
+ def m(pos, splat) = yield pos, *splat
+
+ m([:ok], []) {|v0,| v0 }
+}
+
+# regression test for send stack shifting
+assert_normal_exit %q{
+ def foo(a, b)
+ a.singleton_methods(b)
+ end
+
+ def call_foo
+ [1, 1, 1, 1, 1, 1, send(:foo, 1, 1)]
+ end
+
+ call_foo
+}
+
+# regression test for keyword splat with yield
+assert_equal 'nil', %q{
+ def splat_kw(kwargs) = yield(**kwargs)
+
+ splat_kw({}) { _1 }.inspect
+}
+
+# regression test for arity check with splat
+assert_equal '[:ae, :ae]', %q{
+ def req_one(a_, b_ = 1) = raise
+
+ def test(args)
+ req_one *args
+ rescue ArgumentError
+ :ae
+ end
+
+ [test(Array.new 5), test([])]
+} unless rjit_enabled? # Not yet working on RJIT
+
+# regression test for arity check with splat and send
+assert_equal '[:ae, :ae]', %q{
+ def two_reqs(a, b_, _ = 1) = a.gsub(a, a)
+
+ def test(name, args)
+ send(name, *args)
+ rescue ArgumentError
+ :ae
+ end
+
+ [test(:two_reqs, ["g", nil, nil, nil]), test(:two_reqs, ["g"])]
+}
+
+# regression test for GC marking stubs in invalidated code
+assert_normal_exit %q{
+ garbage = Array.new(10_000) { [] } # create garbage to cause iseq movement
+ eval(<<~RUBY)
+ def foo(n, garbage)
+ if n == 2
+ # 1.times.each to create a cfunc frame to preserve the JIT frame
+ # which will return to a stub housed in an invalidated block
+ return 1.times.each do
+ Object.define_method(:foo) {}
+ garbage.clear
+ GC.verify_compaction_references(toward: :empty, expand_heap: true)
+ end
+ end
+
+ foo(n + 1, garbage)
+ end
+ RUBY
+
+ foo(1, garbage)
+}
+
+# regression test for callee block handler overlapping with arguments
+assert_equal '3', %q{
+ def foo(_req, *args) = args.last
+
+ def call_foo = foo(0, 1, 2, 3, &->{})
+
+ call_foo
+}
+
+# call leaf builtin with a block argument
+assert_equal '0', "0.abs(&nil)"
+
+# regression test for invokeblock iseq guard
+assert_equal 'ok', %q{
+ return :ok unless defined?(GC.compact)
+ def foo = yield
+ 10.times do |i|
+ ret = eval("foo { #{i} }")
+ raise "failed at #{i}" unless ret == i
+ GC.compact
+ end
+ :ok
+} unless rjit_enabled? # Not yet working on RJIT
+
+# regression test for overly generous guard elision
+assert_equal '[0, :sum, 0, :sum]', %q{
+ # In faulty versions, the following happens:
+ # 1. YJIT puts object on the temp stack with type knowledge
+ # (CArray or CString) about RBASIC_CLASS(object).
+ # 2. In iter=0, due to the type knowledge, YJIT generates
+ # a call to sum() without any guard on RBASIC_CLASS(object).
+ # 3. In iter=1, a singleton class is added to the object,
+ # changing RBASIC_CLASS(object), falsifying the type knowledge.
+ # 4. Because the code from (1) has no class guard, it is incorrectly
+ # reused and the wrong method is invoked.
+ # Putting a literal is important for gaining type knowledge.
+ def carray(iter)
+ array = []
+ array.sum(iter.times { def array.sum(_) = :sum })
+ end
+
+ def cstring(iter)
+ string = "".dup
+ string.sum(iter.times { def string.sum(_) = :sum })
+ end
+
+ [carray(0), carray(1), cstring(0), cstring(1)]
+}
+
+# regression test for return type of Integer#/
+# It can return a T_BIGNUM when inputs are T_FIXNUM.
+assert_equal 0x3fffffffffffffff.to_s, %q{
+ def call(fixnum_min)
+ (fixnum_min / -1) - 1
+ end
+
+ call(-(2**62))
+}
+
+# regression test for return type of String#<<
+assert_equal 'Sub', %q{
+ def call(sub) = (sub << sub).itself
+
+ class Sub < String; end
+
+ call(Sub.new('o')).class
+}
+
+# test splat filling required and feeding rest
+assert_equal '[0, 1, 2, [3, 4]]', %q{
+ public def lead_rest(a, b, *rest)
+ [self, a, b, rest]
+ end
+
+ def call(args) = 0.lead_rest(*args)
+
+ call([1, 2, 3, 4])
+}
+
+# test missing opts are nil initialized
+assert_equal '[[0, 1, nil, 3], [0, 1, nil, 3], [0, 1, nil, 3, []], [0, 1, nil, 3, []]]', %q{
+ public def lead_opts(a, b=binding.local_variable_get(:c), c=3)
+ [self, a, b, c]
+ end
+
+ public def opts_rest(a=raise, b=binding.local_variable_get(:c), c=3, *rest)
+ [self, a, b, c, rest]
+ end
+
+ def call(args)
+ [
+ 0.lead_opts(1),
+ 0.lead_opts(*args),
+
+ 0.opts_rest(1),
+ 0.opts_rest(*args),
+ ]
+ end
+
+ call([1])
+}
+
+# test filled optionals with unspecified keyword param
+assert_equal 'ok', %q{
+ def opt_rest_opt_kw(_=1, *, k: :ok) = k
+
+ def call = opt_rest_opt_kw(0)
+
+ call
+}
+
+# test splat empty array with rest param
+assert_equal '[0, 1, 2, []]', %q{
+ public def foo(a=1, b=2, *rest)
+ [self, a, b, rest]
+ end
+
+ def call(args) = 0.foo(*args)
+
+ call([])
+}
+
+# Regression test for yielding with autosplat to block with
+# optional parameters. https://github.com/Shopify/yjit/issues/313
+assert_equal '[:a, :b, :a, :b]', %q{
+ def yielder(arg) = yield(arg) + yield(arg)
+
+ yielder([:a, :b]) do |c = :c, d = :d|
+ [c, d]
+ end
+}
+
# Regression test for GC mishap while doing shape transition
assert_equal '[:ok]', %q{
# [Bug #19601]
@@ -16,7 +293,7 @@ assert_equal '[:ok]', %q{
# Used to crash due to GC run in rb_ensure_iv_list_size()
# not marking the newly allocated [:ok].
RegressionTest.new.extender.itself
-} unless RUBY_DESCRIPTION.include?('+RJIT') # Skip on RJIT since this uncovers a crash
+} unless rjit_enabled? # Skip on RJIT since this uncovers a crash
assert_equal 'true', %q{
# regression test for tracking type of locals for too long
@@ -66,7 +343,7 @@ assert_normal_exit %q{
}
assert_normal_exit %q{
- # Test to ensure send on overriden c functions
+ # Test to ensure send on overridden c functions
# doesn't corrupt the stack
class Bar
def bar(x)
@@ -146,6 +423,33 @@ assert_equal '["instance-variable", 5]', %q{
Foo.new.foo
}
+# getinstancevariable with shape too complex
+assert_normal_exit %q{
+ class Foo
+ def initialize
+ @a = 1
+ end
+
+ def getter
+ @foobar
+ end
+ end
+
+ # Initialize ivars in changing order, making the Foo
+ # class have shape too complex
+ 100.times do |x|
+ foo = Foo.new
+ foo.instance_variable_set(:"@a#{x}", 1)
+ foo.instance_variable_set(:"@foobar", 777)
+
+ # The getter method eventually sees shape too complex
+ r = foo.getter
+ if r != 777
+ raise "error"
+ end
+ end
+}
+
assert_equal '0', %q{
# This is a regression test for incomplete invalidation from
# opt_setinlinecache. This test might be brittle, so
@@ -946,7 +1250,7 @@ assert_equal "good", %q{
# Test polymorphic getinstancevariable. T_OBJECT -> T_STRING
assert_equal 'ok', %q{
@hello = @h1 = @h2 = @h3 = @h4 = 'ok'
- str = ""
+ str = +""
str.instance_variable_set(:@hello, 'ok')
public def get
@@ -1090,6 +1394,18 @@ assert_equal '[42, :default]', %q{
]
}
+# Test default value block for Hash with opt_aref_with
+assert_equal "false", <<~RUBY, frozen_string_literal: false
+ def index_with_string(h)
+ h["foo"]
+ end
+
+ h = Hash.new { |h, k| k.frozen? }
+
+ index_with_string(h)
+ index_with_string(h)
+RUBY
+
# A regression test for making sure cfp->sp is proper when
# hitting stubs. See :stub-sp-flush:
assert_equal 'ok', %q{
@@ -1588,7 +1904,7 @@ assert_equal 'foo', %q{
}
# Test that String unary plus returns the same object ID for an unfrozen string.
-assert_equal 'true', %q{
+assert_equal 'true', <<~RUBY, frozen_string_literal: false
def jittable_method
str = "bar"
@@ -1598,7 +1914,7 @@ assert_equal 'true', %q{
uplus_str.object_id == old_obj_id
end
jittable_method
-}
+RUBY
# Test that String unary plus returns a different unfrozen string when given a frozen string
assert_equal 'false', %q{
@@ -1712,6 +2028,85 @@ assert_equal 'true', %q{
jittable_method
}
+# test getbyte on string class
+assert_equal '[97, :nil, 97, :nil, :raised]', %q{
+ def getbyte(s, i)
+ byte = begin
+ s.getbyte(i)
+ rescue TypeError
+ :raised
+ end
+
+ byte || :nil
+ end
+
+ getbyte("a", 0)
+ getbyte("a", 0)
+
+ [getbyte("a", 0), getbyte("a", 1), getbyte("a", -1), getbyte("a", -2), getbyte("a", "a")]
+} unless rjit_enabled? # Not yet working on RJIT
+
+# Basic test for String#setbyte
+assert_equal 'AoZ', %q{
+ s = +"foo"
+ s.setbyte(0, 65)
+ s.setbyte(-1, 90)
+ s
+}
+
+# String#setbyte IndexError
+assert_equal 'String#setbyte', %q{
+ def ccall = "".setbyte(1, 0)
+ begin
+ ccall
+ rescue => e
+ e.backtrace.first.split("'").last
+ end
+}
+
+# String#setbyte TypeError
+assert_equal 'String#setbyte', %q{
+ def ccall = "".setbyte(nil, 0)
+ begin
+ ccall
+ rescue => e
+ e.backtrace.first.split("'").last
+ end
+}
+
+# String#setbyte FrozenError
+assert_equal 'String#setbyte', %q{
+ def ccall = "a".freeze.setbyte(0, 0)
+ begin
+ ccall
+ rescue => e
+ e.backtrace.first.split("'").last
+ end
+}
+
+# non-leaf String#setbyte
+assert_equal 'String#setbyte', %q{
+ def to_int
+ @caller = caller
+ 0
+ end
+
+ def ccall = "a".dup.setbyte(self, 98)
+ ccall
+
+ @caller.first.split("'").last
+}
+
+# non-leaf String#byteslice
+assert_equal 'TypeError', %q{
+ def ccall = "".byteslice(nil, nil)
+ begin
+ ccall
+ rescue => e
+ e.class
+ end
+}
+
# Test << operator on string subclass
assert_equal 'abab', %q{
class MyString < String; end
@@ -1922,6 +2317,19 @@ assert_equal '123', %q{
foo(Foo)
}
+# Test EP == BP invalidation with moving ISEQs
+assert_equal 'ok', %q{
+ def entry
+ ok = proc { :ok } # set #entry as an EP-escaping ISEQ
+ [nil].reverse_each do # avoid exiting the JIT frame on the constant
+ GC.compact # move #entry ISEQ
+ end
+ ok # should be read off of escaped EP
+ end
+
+ entry.call
+}
+
# invokesuper edge case
assert_equal '[:A, [:A, :B]]', %q{
class B
@@ -2110,6 +2518,18 @@ assert_equal '[0, 2]', %q{
B.new.foo
}
+# invokesuper zsuper in a bmethod
+assert_equal 'ok', %q{
+ class Foo
+ define_method(:itself) { super }
+ end
+ begin
+ Foo.new.itself
+ rescue RuntimeError
+ :ok
+ end
+}
+
# Call to fixnum
assert_equal '[true, false]', %q{
def is_odd(obj)
@@ -2150,6 +2570,16 @@ assert_equal '[true, false, true, false]', %q{
[is_odd(123), is_odd(456), is_odd(bignum), is_odd(bignum+1)]
}
+# Flonum and Flonum
+assert_equal '[2.0, 0.0, 1.0, 4.0]', %q{
+ [1.0 + 1.0, 1.0 - 1.0, 1.0 * 1.0, 8.0 / 2.0]
+}
+
+# Flonum and Fixnum
+assert_equal '[2.0, 0.0, 1.0, 4.0]', %q{
+ [1.0 + 1, 1.0 - 1, 1.0 * 1, 8.0 / 2]
+}
+
# Call to static and dynamic symbol
assert_equal 'bar', %q{
def to_string(obj)
@@ -2190,6 +2620,30 @@ assert_equal '[1, 2, 3, 4, 5]', %q{
splatarray
}
+# splatkw
+assert_equal '[1, 2]', %q{
+ def foo(a:) = [a, yield]
+
+ def entry(&block)
+ a = { a: 1 }
+ foo(**a, &block)
+ end
+
+ entry { 2 }
+}
+assert_equal '[1, 2]', %q{
+ def foo(a:) = [a, yield]
+
+ def entry(obj, &block)
+ foo(**obj, &block)
+ end
+
+ entry({ a: 3 }) { 2 }
+ obj = Object.new
+ def obj.to_hash = { a: 1 }
+ entry(obj) { 2 }
+}
+
assert_equal '[1, 1, 2, 1, 2, 3]', %q{
def expandarray
arr = [1, 2, 3]
@@ -2244,6 +2698,23 @@ assert_equal '[:not_array, nil, nil]', %q{
expandarray_not_array(obj)
}
+assert_equal '[1, 2]', %q{
+ class NilClass
+ private
+ def to_ary
+ [1, 2]
+ end
+ end
+
+ def expandarray_redefined_nilclass
+ a, b = nil
+ [a, b]
+ end
+
+ expandarray_redefined_nilclass
+ expandarray_redefined_nilclass
+} unless rjit_enabled?
+
assert_equal '[1, 2, nil]', %q{
def expandarray_rhs_too_small
a, b, c = [1, 2]
@@ -2254,6 +2725,17 @@ assert_equal '[1, 2, nil]', %q{
expandarray_rhs_too_small
}
+assert_equal '[nil, 2, nil]', %q{
+ def foo(arr)
+ a, b, c = arr
+ end
+
+ a, b, c1 = foo([0, 1])
+ a, b, c2 = foo([0, 1, 2])
+ a, b, c3 = foo([0, 1])
+ [c1, c2, c3]
+}
+
assert_equal '[1, [2]]', %q{
def expandarray_splat
a, *b = [1, 2]
@@ -2343,7 +2825,7 @@ assert_equal '[[:c_return, :String, :string_alias, "events_to_str"]]', %q{
events.compiled(events)
events
-} unless defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? # RJIT calls extra Ruby methods
+} unless rjit_enabled? # RJIT calls extra Ruby methods
# test enabling a TracePoint that targets a particular line in a C method call
assert_equal '[true]', %q{
@@ -2425,7 +2907,7 @@ assert_equal '[[:c_call, :itself]]', %q{
tp.enable { shouldnt_compile }
events
-} unless defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? # RJIT calls extra Ruby methods
+} unless rjit_enabled? # RJIT calls extra Ruby methods
# test enabling c_return tracing before compiling
assert_equal '[[:c_return, :itself, main]]', %q{
@@ -2440,7 +2922,7 @@ assert_equal '[[:c_return, :itself, main]]', %q{
tp.enable { shouldnt_compile }
events
-} unless defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? # RJIT calls extra Ruby methods
+} unless rjit_enabled? # RJIT calls extra Ruby methods
# test c_call invalidation
assert_equal '[[:c_call, :itself]]', %q{
@@ -3698,7 +4180,7 @@ assert_equal '2', %q{
assert_equal 'Hello World', %q{
def bar
args = ["Hello "]
- greeting = "World"
+ greeting = +"World"
greeting.insert(0, *args)
greeting
end
@@ -3934,7 +4416,7 @@ assert_equal '[true, true, true, true, true]', %q{
calling_my_func
}
-# Regresssion test: rest and optional and splat
+# Regression test: rest and optional and splat
assert_equal 'true', %q{
def my_func(base=nil, *args)
[base, args]
@@ -3962,4 +4444,523 @@ assert_equal 'true', %q{
rescue ArgumentError
true
end
-} unless defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? # Not yet working on RJIT
+} unless rjit_enabled? # Not yet working on RJIT
+
+# Regression test: register allocator on expandarray
+assert_equal '[]', %q{
+ func = proc { [] }
+ proc do
+ _x, _y = func.call
+ end.call
+}
+
+# Catch TAG_BREAK in a non-FINISH frame with JIT code
+assert_equal '1', %q{
+ def entry
+ catch_break
+ end
+
+ def catch_break
+ while_true do
+ break
+ end
+ 1
+ end
+
+ def while_true
+ while true
+ yield
+ end
+ end
+
+ entry
+}
+
+assert_equal '6', %q{
+ class Base
+ def number = 1 + yield
+ end
+
+ class Sub < Base
+ def number = super + 2
+ end
+
+ Sub.new.number { 3 }
+}
+
+# Integer multiplication and overflow
+assert_equal '[6, -6, 9671406556917033397649408, -9671406556917033397649408, 21267647932558653966460912964485513216]', %q{
+ def foo(a, b)
+ a * b
+ end
+
+ r1 = foo(2, 3)
+ r2 = foo(2, -3)
+ r3 = foo(2 << 40, 2 << 41)
+ r4 = foo(2 << 40, -2 << 41)
+ r5 = foo(1 << 62, 1 << 62)
+
+ [r1, r2, r3, r4, r5]
+}
+
+# Integer multiplication and overflow (minimized regression test from test-basic)
+assert_equal '8515157028618240000', %q{2128789257154560000 * 4}
+
+# Inlined method calls
+assert_equal 'nil', %q{
+ def putnil = nil
+ def entry = putnil
+ entry.inspect
+}
+assert_equal '1', %q{
+ def putobject_1 = 1
+ def entry = putobject_1
+ entry
+}
+assert_equal 'false', %q{
+ def putobject(_unused_arg1) = false
+ def entry = putobject(nil)
+ entry
+}
+assert_equal 'true', %q{
+ def entry = yield
+ entry { true }
+}
+assert_equal 'sym', %q{
+ def entry = :sym.to_sym
+ entry
+}
+
+assert_normal_exit %q{
+ ivars = 1024.times.map { |i| "@iv_#{i} = #{i}\n" }.join
+ Foo = Class.new
+ Foo.class_eval "def initialize() #{ivars} end"
+ Foo.new
+}
+
+assert_equal '0', %q{
+ def spill
+ 1.to_i # not inlined
+ end
+
+ def inline(_stack1, _stack2, _stack3, _stack4, _stack5)
+ 0 # inlined
+ end
+
+ def entry
+ # RegTemps is 00111110 prior to the #inline call.
+ # Its return value goes to stack_idx=0, which conflicts with stack_idx=5.
+ inline(spill, 2, 3, 4, 5)
+ end
+
+ entry
+}
+
+# Integer succ and overflow
+assert_equal '[2, 4611686018427387904]', %q{
+ [1.succ, 4611686018427387903.succ]
+}
+
+# Integer right shift
+assert_equal '[0, 1, -4]', %q{
+ [0 >> 1, 2 >> 1, -7 >> 1]
+}
+
+# Integer XOR
+assert_equal '[0, 0, 4]', %q{
+ [0 ^ 0, 1 ^ 1, 7 ^ 3]
+}
+
+assert_equal '[nil, "yield"]', %q{
+ def defined_yield = defined?(yield)
+ [defined_yield, defined_yield {}]
+}
+
+# splat with ruby2_keywords into rest parameter
+assert_equal '[[{:a=>1}], {}]', %q{
+ ruby2_keywords def foo(*args) = args
+
+ def bar(*args, **kw) = [args, kw]
+
+ def pass_bar(*args) = bar(*args)
+
+ def body
+ args = foo(a: 1)
+ pass_bar(*args)
+ end
+
+ body
+}
+
+# concatarray
+assert_equal '[1, 2]', %q{
+ def foo(a, b) = [a, b]
+ arr = [2]
+ foo(*[1], *arr)
+}
+
+# pushtoarray
+assert_equal '[1, 2]', %q{
+ def foo(a, b) = [a, b]
+ arr = [1]
+ foo(*arr, 2)
+}
+
+# pop before fallback
+assert_normal_exit %q{
+ class Foo
+ attr_reader :foo
+
+ def try = foo(0, &nil)
+ end
+
+ Foo.new.try
+}
+
+# a kwrest case
+assert_equal '[1, 2, {:complete=>false}]', %q{
+ def rest(foo: 1, bar: 2, **kwrest)
+ [foo, bar, kwrest]
+ end
+
+ def callsite = rest(complete: false)
+
+ callsite
+}
+
+# splat+kw_splat+opt+rest
+assert_equal '[1, []]', %q{
+ def opt_rest(a = 0, *rest) = [a, rest]
+
+ def call_site(args) = opt_rest(*args, **nil)
+
+ call_site([1])
+}
+
+# splat and nil kw_splat
+assert_equal 'ok', %q{
+ def identity(x) = x
+
+ def splat_nil_kw_splat(args) = identity(*args, **nil)
+
+ splat_nil_kw_splat([:ok])
+}
+
+# empty splat and kwsplat into leaf builtins
+assert_equal '[1, 1, 1]', %q{
+ empty = []
+ [1.abs(*empty), 1.abs(**nil), 1.bit_length(*empty, **nil)]
+}
+
+# splat into C methods with -1 arity
+assert_equal '[[1, 2, 3], [0, 2, 3], [1, 2, 3], [2, 2, 3], [], [], [{}]]', %q{
+ class Foo < Array
+ def push(args) = super(1, *args)
+ end
+
+ def test_cfunc_vargs_splat(sub_instance, array_class, empty_kw_hash)
+ splat = [2, 3]
+ kw_splat = [empty_kw_hash]
+ [
+ sub_instance.push(splat),
+ array_class[0, *splat, **nil],
+ array_class[1, *splat, &nil],
+ array_class[2, *splat, **nil, &nil],
+ array_class.send(:[], *kw_splat),
+ # kw_splat disables keywords hash handling
+ array_class[*kw_splat],
+ array_class[*kw_splat, **nil],
+ ]
+ end
+
+ test_cfunc_vargs_splat(Foo.new, Array, Hash.ruby2_keywords_hash({}))
+}
+
+# Class#new (arity=-1), splat, and ruby2_keywords
+assert_equal '[0, {1=>1}]', %q{
+ class KwInit
+ attr_reader :init_args
+ def initialize(x = 0, **kw)
+ @init_args = [x, kw]
+ end
+ end
+
+ def test(klass, args)
+ klass.new(*args).init_args
+ end
+
+ test(KwInit, [Hash.ruby2_keywords_hash({1 => 1})])
+}
+
+# Chilled string setivar trigger warning
+assert_equal 'literal string will be frozen in the future', %q{
+ Warning[:deprecated] = true
+ $VERBOSE = true
+ $warning = "no-warning"
+ module ::Warning
+ def self.warn(message)
+ $warning = message.split("warning: ").last.strip
+ end
+ end
+
+ class String
+ def setivar!
+ @ivar = 42
+ end
+ end
+
+ def setivar!(str)
+ str.setivar!
+ end
+
+ 10.times { setivar!("mutable".dup) }
+ 10.times do
+ setivar!("frozen".freeze)
+ rescue FrozenError
+ end
+
+ setivar!("chilled") # Emit warning
+ $warning
+}
+
+# arity=-2 cfuncs
+assert_equal '["", "1/2", [0, [:ok, 1]]]', %q{
+ def test_cases(file, chain)
+ new_chain = chain.allocate # to call initialize directly
+ new_chain.send(:initialize, [0], ok: 1)
+
+ [
+ file.join,
+ file.join("1", "2"),
+ new_chain.to_a,
+ ]
+ end
+
+ test_cases(File, Enumerator::Chain)
+}
+
+# singleton class should invalidate Type::CString assumption
+assert_equal 'foo', %q{
+ def define_singleton(str, define)
+ if define
+ # Wrap a C method frame to avoid exiting JIT code on defineclass
+ [nil].reverse_each do
+ class << str
+ def +(_)
+ "foo"
+ end
+ end
+ end
+ end
+ "bar"
+ end
+
+ def entry(define)
+ str = ""
+ # When `define` is false, #+ compiles to rb_str_plus() without a class guard.
+ # When the code is reused with `define` is true, the class of `str` is changed
+ # to a singleton class, so the block should be invalidated.
+ str + define_singleton(str, define)
+ end
+
+ entry(false)
+ entry(true)
+}
+
+assert_equal '[:ok, :ok, :ok]', %q{
+ def identity(x) = x
+ def foo(x, _) = x
+ def bar(_, _, _, _, x) = x
+
+ def tests
+ [
+ identity(:ok),
+ foo(:ok, 2),
+ bar(1, 2, 3, 4, :ok),
+ ]
+ end
+
+ tests
+}
+
+# regression test for invalidating an empty block
+assert_equal '0', %q{
+ def foo = (* = 1).pred
+
+ foo # compile it
+
+ class Integer
+ def to_ary = [] # invalidate
+ end
+
+ foo # try again
+} unless rjit_enabled? # doesn't work on RJIT
+
+# test integer left shift with constant rhs
+assert_equal [0x80000000000, 'a+', :ok].inspect, %q{
+ def shift(val) = val << 43
+
+ def tests
+ int = shift(1)
+ str = shift("a")
+
+ Integer.define_method(:<<) { |_| :ok }
+ redef = shift(1)
+
+ [int, str, redef]
+ end
+
+ tests
+}
+
+# test String#stebyte with arguments that need conversion
+assert_equal "abc", %q{
+ str = +"a00"
+ def change_bytes(str, one, two)
+ str.setbyte(one, "b".ord)
+ str.setbyte(2, two)
+ end
+
+ to_int_1 = Object.new
+ to_int_99 = Object.new
+ def to_int_1.to_int = 1
+ def to_int_99.to_int = 99
+
+ change_bytes(str, to_int_1, to_int_99)
+ str
+}
+
+# test --yjit-verify-ctx for arrays with a singleton class
+assert_equal "ok", %q{
+ class Array
+ def foo
+ self.singleton_class.define_method(:first) { :ok }
+ first
+ end
+ end
+
+ def test = [].foo
+
+ test
+}
+
+assert_equal '["raised", "Module", "Object"]', %q{
+ def foo(obj)
+ obj.superclass.name
+ end
+
+ ret = []
+
+ begin
+ foo(Class.allocate)
+ rescue TypeError
+ ret << 'raised'
+ end
+
+ ret += [foo(Class), foo(Class.new)]
+}
+
+# test TrueClass#=== before and after redefining TrueClass#==
+assert_equal '[[true, false, false], [true, true, false], [true, :error, :error]]', %q{
+ def true_eqq(x)
+ true === x
+ rescue NoMethodError
+ :error
+ end
+
+ def test
+ [
+ # first one is always true because rb_equal does object comparison before calling #==
+ true_eqq(true),
+ # these will use TrueClass#==
+ true_eqq(false),
+ true_eqq(:truthy),
+ ]
+ end
+
+ results = [test]
+
+ class TrueClass
+ def ==(x)
+ !x
+ end
+ end
+
+ results << test
+
+ class TrueClass
+ undef_method :==
+ end
+
+ results << test
+} unless rjit_enabled? # Not yet working on RJIT
+
+# test FalseClass#=== before and after redefining FalseClass#==
+assert_equal '[[true, false, false], [true, false, true], [true, :error, :error]]', %q{
+ def case_equal(x, y)
+ x === y
+ rescue NoMethodError
+ :error
+ end
+
+ def test
+ [
+ # first one is always true because rb_equal does object comparison before calling #==
+ case_equal(false, false),
+ # these will use #==
+ case_equal(false, true),
+ case_equal(false, nil),
+ ]
+ end
+
+ results = [test]
+
+ class FalseClass
+ def ==(x)
+ !x
+ end
+ end
+
+ results << test
+
+ class FalseClass
+ undef_method :==
+ end
+
+ results << test
+} unless rjit_enabled? # Not yet working on RJIT
+
+# test NilClass#=== before and after redefining NilClass#==
+assert_equal '[[true, false, false], [true, false, true], [true, :error, :error]]', %q{
+ def case_equal(x, y)
+ x === y
+ rescue NoMethodError
+ :error
+ end
+
+ def test
+ [
+ # first one is always true because rb_equal does object comparison before calling #==
+ case_equal(nil, nil),
+ # these will use #==
+ case_equal(nil, true),
+ case_equal(nil, false),
+ ]
+ end
+
+ results = [test]
+
+ class NilClass
+ def ==(x)
+ !x
+ end
+ end
+
+ results << test
+
+ class NilClass
+ undef_method :==
+ end
+
+ results << test
+} unless rjit_enabled? # Not yet working on RJIT
diff --git a/builtin.c b/builtin.c
index aef5b2c2d4..fbc11bf1b4 100644
--- a/builtin.c
+++ b/builtin.c
@@ -3,15 +3,15 @@
#include "iseq.h"
#include "builtin.h"
-#ifdef CROSS_COMPILING
+#include "builtin_binary.inc"
+
+#ifndef BUILTIN_BINARY_SIZE
#define INCLUDED_BY_BUILTIN_C 1
#include "mini_builtin.c"
#else
-#include "builtin_binary.inc"
-
static const unsigned char *
bin4feature(const struct builtin_binary *bb, const char *feature, size_t *psize)
{
@@ -46,7 +46,6 @@ rb_load_with_builtin_functions(const char *feature_name, const struct rb_builtin
rb_vm_t *vm = GET_VM();
if (vm->builtin_function_table != NULL) rb_bug("vm->builtin_function_table should be NULL.");
vm->builtin_function_table = table;
- vm->builtin_inline_index = 0;
const rb_iseq_t *iseq = rb_iseq_ibf_load_bytes((const char *)bin, size);
ASSUME(iseq); // otherwise an exception should have raised
vm->builtin_function_table = NULL;
@@ -58,6 +57,12 @@ rb_load_with_builtin_functions(const char *feature_name, const struct rb_builtin
#endif
void
+rb_free_loaded_builtin_table(void)
+{
+ // do nothing
+}
+
+void
Init_builtin(void)
{
// nothing
diff --git a/builtin.h b/builtin.h
index 85fd1a009a..24aa7c2fdb 100644
--- a/builtin.h
+++ b/builtin.h
@@ -106,6 +106,8 @@ rb_vm_lvar(rb_execution_context_t *ec, int index)
#endif
}
+#define LOCAL_PTR(local) local ## __ptr
+
// dump/load
struct builtin_binary {
diff --git a/ccan/list/list.h b/ccan/list/list.h
index 30b2af04e9..bf692a6937 100644
--- a/ccan/list/list.h
+++ b/ccan/list/list.h
@@ -635,14 +635,16 @@ static inline void ccan_list_prepend_list_(struct ccan_list_head *to,
/* internal macros, do not use directly */
#define ccan_list_for_each_off_dir_(h, i, off, dir) \
- for (i = ccan_list_node_to_off_(ccan_list_debug(h, CCAN_LIST_LOC)->n.dir, \
+ for (i = 0, \
+ i = ccan_list_node_to_off_(ccan_list_debug(h, CCAN_LIST_LOC)->n.dir, \
(off)); \
ccan_list_node_from_off_((void *)i, (off)) != &(h)->n; \
i = ccan_list_node_to_off_(ccan_list_node_from_off_((void *)i, (off))->dir, \
(off)))
#define ccan_list_for_each_safe_off_dir_(h, i, nxt, off, dir) \
- for (i = ccan_list_node_to_off_(ccan_list_debug(h, CCAN_LIST_LOC)->n.dir, \
+ for (i = 0, \
+ i = ccan_list_node_to_off_(ccan_list_debug(h, CCAN_LIST_LOC)->n.dir, \
(off)), \
nxt = ccan_list_node_to_off_(ccan_list_node_from_off_(i, (off))->dir, \
(off)); \
diff --git a/class.c b/class.c
index 4ffb47c27a..5cce99e334 100644
--- a/class.c
+++ b/class.c
@@ -29,12 +29,18 @@
#include "internal/variable.h"
#include "ruby/st.h"
#include "vm_core.h"
+#include "yjit.h"
/* Flags of T_CLASS
*
- * 2: RCLASS_SUPERCLASSES_INCLUDE_SELF
- * The RCLASS_SUPERCLASSES contains the class as the last element.
- * This means that this class owns the RCLASS_SUPERCLASSES list.
+ * 0: RCLASS_IS_ROOT
+ * The class has been added to the VM roots. Will always be marked and pinned.
+ * This is done for classes defined from C to allow storing them in global variables.
+ * 1: RUBY_FL_SINGLETON
+ * This class is a singleton class.
+ * 2: RCLASS_SUPERCLASSES_INCLUDE_SELF
+ * The RCLASS_SUPERCLASSES contains the class as the last element.
+ * This means that this class owns the RCLASS_SUPERCLASSES list.
* if !SHAPE_IN_BASIC_FLAGS
* 4-19: SHAPE_FLAG_MASK
* Shape ID for the class.
@@ -54,6 +60,9 @@
/* Flags of T_MODULE
*
+ * 0: RCLASS_IS_ROOT
+ * The class has been added to the VM roots. Will always be marked and pinned.
+ * This is done for classes defined from C to allow storing them in global variables.
* 1: RMODULE_ALLOCATED_BUT_NOT_INITIALIZED
* Module has not been initialized.
* 2: RCLASS_SUPERCLASSES_INCLUDE_SELF
@@ -217,14 +226,14 @@ rb_class_detach_module_subclasses(VALUE klass)
/**
* Allocates a struct RClass for a new class.
*
- * \param flags initial value for basic.flags of the returned class.
- * \param klass the class of the returned class.
- * \return an uninitialized Class object.
- * \pre \p klass must refer \c Class class or an ancestor of Class.
- * \pre \code (flags | T_CLASS) != 0 \endcode
- * \post the returned class can safely be \c #initialize 'd.
+ * @param flags initial value for basic.flags of the returned class.
+ * @param klass the class of the returned class.
+ * @return an uninitialized Class object.
+ * @pre `klass` must refer `Class` class or an ancestor of Class.
+ * @pre `(flags | T_CLASS) != 0`
+ * @post the returned class can safely be `#initialize` 'd.
*
- * \note this function is not Class#allocate.
+ * @note this function is not Class#allocate.
*/
static VALUE
class_alloc(VALUE flags, VALUE klass)
@@ -232,7 +241,6 @@ class_alloc(VALUE flags, VALUE klass)
size_t alloc_size = sizeof(struct RClass) + sizeof(rb_classext_t);
flags &= T_MASK;
- flags |= FL_PROMOTED1 /* start from age == 2 */;
if (RGENGC_WB_PROTECTED_CLASS) flags |= FL_WB_PROTECTED;
NEWOBJ_OF(obj, struct RClass, klass, flags, alloc_size, 0);
@@ -249,7 +257,7 @@ class_alloc(VALUE flags, VALUE klass)
*/
RCLASS_SET_ORIGIN((VALUE)obj, (VALUE)obj);
RB_OBJ_WRITE(obj, &RCLASS_REFINED_CLASS(obj), Qnil);
- RCLASS_SET_ALLOCATOR((VALUE)obj, NULL);
+ RCLASS_SET_ALLOCATOR((VALUE)obj, 0);
return (VALUE)obj;
}
@@ -260,14 +268,14 @@ RCLASS_M_TBL_INIT(VALUE c)
RCLASS_M_TBL(c) = rb_id_table_create(0);
}
-/*!
+/**
* A utility function that wraps class_alloc.
*
* allocates a class and initializes safely.
- * \param super a class from which the new class derives.
- * \return a class object.
- * \pre \a super must be a class.
- * \post the metaclass of the new class is Class.
+ * @param super a class from which the new class derives.
+ * @return a class object.
+ * @pre `super` must be a class.
+ * @post the metaclass of the new class is Class.
*/
VALUE
rb_class_boot(VALUE super)
@@ -339,7 +347,7 @@ rb_check_inheritable(VALUE super)
rb_raise(rb_eTypeError, "superclass must be an instance of Class (given an instance of %"PRIsVALUE")",
rb_obj_class(super));
}
- if (RBASIC(super)->flags & FL_SINGLETON) {
+ if (RCLASS_SINGLETON_P(super)) {
rb_raise(rb_eTypeError, "can't make subclass of singleton class");
}
if (super == rb_cClass) {
@@ -425,7 +433,7 @@ class_init_copy_check(VALUE clone, VALUE orig)
if (RCLASS_SUPER(clone) != 0 || clone == rb_cBasicObject) {
rb_raise(rb_eTypeError, "already initialized class");
}
- if (FL_TEST(orig, FL_SINGLETON)) {
+ if (RCLASS_SINGLETON_P(orig)) {
rb_raise(rb_eTypeError, "can't copy singleton class");
}
}
@@ -472,6 +480,7 @@ copy_tables(VALUE clone, VALUE orig)
rb_id_table_foreach(rb_cvc_tbl, cvc_table_copy, &ctx);
RCLASS_CVC_TBL(clone) = rb_cvc_tbl_dup;
}
+ rb_id_table_free(RCLASS_M_TBL(clone));
RCLASS_M_TBL(clone) = 0;
if (!RB_TYPE_P(clone, T_ICLASS)) {
st_data_t id;
@@ -542,7 +551,7 @@ rb_mod_init_copy(VALUE clone, VALUE orig)
RCLASS_EXT(clone)->cloned = true;
RCLASS_EXT(orig)->cloned = true;
- if (!FL_TEST(CLASS_OF(clone), FL_SINGLETON)) {
+ if (!RCLASS_SINGLETON_P(CLASS_OF(clone))) {
RBASIC_SET_CLASS(clone, rb_singleton_class_clone(orig));
rb_singleton_class_attached(METACLASS_OF(clone), (VALUE)clone);
}
@@ -578,9 +587,12 @@ rb_mod_init_copy(VALUE clone, VALUE orig)
rb_bug("non iclass between module/class and origin");
}
clone_p = class_alloc(RBASIC(p)->flags, METACLASS_OF(p));
+ /* We should set the m_tbl right after allocation before anything
+ * that can trigger GC to avoid clone_p from becoming old and
+ * needing to fire write barriers. */
+ RCLASS_SET_M_TBL(clone_p, RCLASS_M_TBL(p));
RCLASS_SET_SUPER(prev_clone_p, clone_p);
prev_clone_p = clone_p;
- RCLASS_M_TBL(clone_p) = RCLASS_M_TBL(p);
RCLASS_CONST_TBL(clone_p) = RCLASS_CONST_TBL(p);
RCLASS_SET_ALLOCATOR(clone_p, RCLASS_ALLOCATOR(p));
if (RB_TYPE_P(clone, T_CLASS)) {
@@ -645,7 +657,7 @@ rb_singleton_class_clone_and_attach(VALUE obj, VALUE attach)
// attached to an object other than `obj`. In which case `obj` does not have
// a material singleton class attached yet and there is no singleton class
// to clone.
- if (!(FL_TEST(klass, FL_SINGLETON) && RCLASS_ATTACHED_OBJECT(klass) == obj)) {
+ if (!(RCLASS_SINGLETON_P(klass) && RCLASS_ATTACHED_OBJECT(klass) == obj)) {
// nothing to clone
return klass;
}
@@ -696,7 +708,7 @@ rb_singleton_class_clone_and_attach(VALUE obj, VALUE attach)
void
rb_singleton_class_attached(VALUE klass, VALUE obj)
{
- if (FL_TEST(klass, FL_SINGLETON)) {
+ if (RCLASS_SINGLETON_P(klass)) {
RCLASS_SET_ATTACHED_OBJECT(klass, obj);
}
}
@@ -721,7 +733,7 @@ rb_singleton_class_internal_p(VALUE sklass)
!rb_singleton_class_has_metaclass_p(sklass));
}
-/*!
+/**
* whether k has a metaclass
* @retval 1 if \a k has a metaclass
* @retval 0 otherwise
@@ -730,25 +742,25 @@ rb_singleton_class_internal_p(VALUE sklass)
(FL_TEST(METACLASS_OF(k), FL_SINGLETON) && \
rb_singleton_class_has_metaclass_p(k))
-/*!
- * ensures \a klass belongs to its own eigenclass.
- * @return the eigenclass of \a klass
- * @post \a klass belongs to the returned eigenclass.
- * i.e. the attached object of the eigenclass is \a klass.
+/**
+ * ensures `klass` belongs to its own eigenclass.
+ * @return the eigenclass of `klass`
+ * @post `klass` belongs to the returned eigenclass.
+ * i.e. the attached object of the eigenclass is `klass`.
* @note this macro creates a new eigenclass if necessary.
*/
#define ENSURE_EIGENCLASS(klass) \
(HAVE_METACLASS_P(klass) ? METACLASS_OF(klass) : make_metaclass(klass))
-/*!
- * Creates a metaclass of \a klass
- * \param klass a class
- * \return created metaclass for the class
- * \pre \a klass is a Class object
- * \pre \a klass has no singleton class.
- * \post the class of \a klass is the returned class.
- * \post the returned class is meta^(n+1)-class when \a klass is a meta^(n)-klass for n >= 0
+/**
+ * Creates a metaclass of `klass`
+ * @param klass a class
+ * @return created metaclass for the class
+ * @pre `klass` is a Class object
+ * @pre `klass` has no singleton class.
+ * @post the class of `klass` is the returned class.
+ * @post the returned class is meta^(n+1)-class when `klass` is a meta^(n)-klass for n >= 0
*/
static inline VALUE
make_metaclass(VALUE klass)
@@ -779,11 +791,11 @@ make_metaclass(VALUE klass)
return metaclass;
}
-/*!
- * Creates a singleton class for \a obj.
- * \pre \a obj must not a immediate nor a special const.
- * \pre \a obj must not a Class object.
- * \pre \a obj has no singleton class.
+/**
+ * Creates a singleton class for `obj`.
+ * @pre `obj` must not be an immediate nor a special const.
+ * @pre `obj` must not be a Class object.
+ * @pre `obj` has no singleton class.
*/
static inline VALUE
make_singleton_class(VALUE obj)
@@ -794,6 +806,7 @@ make_singleton_class(VALUE obj)
FL_SET(klass, FL_SINGLETON);
RBASIC_SET_CLASS(obj, klass);
rb_singleton_class_attached(klass, obj);
+ rb_yjit_invalidate_no_singleton_class(orig_class);
SET_METACLASS_OF(klass, METACLASS_OF(rb_class_real(orig_class)));
return klass;
@@ -807,7 +820,7 @@ boot_defclass(const char *name, VALUE super)
ID id = rb_intern(name);
rb_const_set((rb_cObject ? rb_cObject : obj), id, obj);
- rb_vm_add_root_module(obj);
+ rb_vm_register_global_object(obj);
return obj;
}
@@ -889,7 +902,7 @@ Init_class_hierarchy(void)
{
rb_cBasicObject = boot_defclass("BasicObject", 0);
rb_cObject = boot_defclass("Object", rb_cBasicObject);
- rb_gc_register_mark_object(rb_cObject);
+ rb_vm_register_global_object(rb_cObject);
/* resolve class name ASAP for order-independence */
rb_set_class_path_string(rb_cObject, rb_cObject, rb_fstring_lit("Object"));
@@ -914,15 +927,15 @@ Init_class_hierarchy(void)
}
-/*!
- * \internal
+/**
+ * @internal
* Creates a new *singleton class* for an object.
*
- * \pre \a obj has no singleton class.
- * \note DO NOT USE the function in an extension libraries. Use \ref rb_singleton_class.
- * \param obj An object.
- * \param unused ignored.
- * \return The singleton class of the object.
+ * @pre `obj` has no singleton class.
+ * @note DO NOT USE the function in an extension libraries. Use @ref rb_singleton_class.
+ * @param obj An object.
+ * @param unused ignored.
+ * @return The singleton class of the object.
*/
VALUE
rb_make_metaclass(VALUE obj, VALUE unused)
@@ -948,13 +961,13 @@ rb_define_class_id(ID id, VALUE super)
}
-/*!
+/**
* Calls Class#inherited.
- * \param super A class which will be called #inherited.
+ * @param super A class which will be called #inherited.
* NULL means Object class.
- * \param klass A Class object which derived from \a super
- * \return the value \c Class#inherited's returns
- * \pre Each of \a super and \a klass must be a \c Class object.
+ * @param klass A Class object which derived from `super`
+ * @return the value `Class#inherited` returns
+ * @pre Each of `super` and `klass` must be a `Class` object.
*/
VALUE
rb_class_inherited(VALUE super, VALUE klass)
@@ -983,14 +996,14 @@ rb_define_class(const char *name, VALUE super)
}
/* Class may have been defined in Ruby and not pin-rooted */
- rb_vm_add_root_module(klass);
+ rb_vm_register_global_object(klass);
return klass;
}
if (!super) {
- rb_raise(rb_eArgError, "no super class for `%s'", name);
+ rb_raise(rb_eArgError, "no super class for '%s'", name);
}
klass = rb_define_class_id(id, super);
- rb_vm_add_root_module(klass);
+ rb_vm_register_global_object(klass);
rb_const_set(rb_cObject, id, klass);
rb_class_inherited(super, klass);
@@ -1004,7 +1017,7 @@ rb_define_class_under(VALUE outer, const char *name, VALUE super)
}
VALUE
-rb_define_class_id_under(VALUE outer, ID id, VALUE super)
+rb_define_class_id_under_no_pin(VALUE outer, ID id, VALUE super)
{
VALUE klass;
@@ -1021,25 +1034,30 @@ rb_define_class_id_under(VALUE outer, ID id, VALUE super)
" (%"PRIsVALUE" is given but was %"PRIsVALUE")",
outer, rb_id2str(id), RCLASS_SUPER(klass), super);
}
- /* Class may have been defined in Ruby and not pin-rooted */
- rb_vm_add_root_module(klass);
return klass;
}
if (!super) {
- rb_raise(rb_eArgError, "no super class for `%"PRIsVALUE"::%"PRIsVALUE"'",
+ rb_raise(rb_eArgError, "no super class for '%"PRIsVALUE"::%"PRIsVALUE"'",
rb_class_path(outer), rb_id2str(id));
}
klass = rb_define_class_id(id, super);
rb_set_class_path_string(klass, outer, rb_id2str(id));
rb_const_set(outer, id, klass);
rb_class_inherited(super, klass);
- rb_vm_add_root_module(klass);
return klass;
}
VALUE
+rb_define_class_id_under(VALUE outer, ID id, VALUE super)
+{
+ VALUE klass = rb_define_class_id_under_no_pin(outer, id, super);
+ rb_vm_register_global_object(klass);
+ return klass;
+}
+
+VALUE
rb_module_s_alloc(VALUE klass)
{
VALUE mod = class_alloc(T_MODULE, klass);
@@ -1089,11 +1107,11 @@ rb_define_module(const char *name)
name, rb_obj_class(module));
}
/* Module may have been defined in Ruby and not pin-rooted */
- rb_vm_add_root_module(module);
+ rb_vm_register_global_object(module);
return module;
}
module = rb_module_new();
- rb_vm_add_root_module(module);
+ rb_vm_register_global_object(module);
rb_const_set(rb_cObject, id, module);
return module;
@@ -1118,13 +1136,13 @@ rb_define_module_id_under(VALUE outer, ID id)
outer, rb_id2str(id), rb_obj_class(module));
}
/* Module may have been defined in Ruby and not pin-rooted */
- rb_gc_register_mark_object(module);
+ rb_vm_register_global_object(module);
return module;
}
module = rb_module_new();
rb_const_set(outer, id, module);
rb_set_class_path_string(module, outer, rb_id2str(id));
- rb_gc_register_mark_object(module);
+ rb_vm_register_global_object(module);
return module;
}
@@ -1134,7 +1152,7 @@ rb_include_class_new(VALUE module, VALUE super)
{
VALUE klass = class_alloc(T_ICLASS, rb_cClass);
- RCLASS_M_TBL(klass) = RCLASS_M_TBL(module);
+ RCLASS_SET_M_TBL(klass, RCLASS_M_TBL(module));
RCLASS_SET_ORIGIN(klass, klass);
if (BUILTIN_TYPE(module) == T_ICLASS) {
@@ -1410,10 +1428,10 @@ ensure_origin(VALUE klass)
VALUE origin = RCLASS_ORIGIN(klass);
if (origin == klass) {
origin = class_alloc(T_ICLASS, klass);
+ RCLASS_SET_M_TBL(origin, RCLASS_M_TBL(klass));
RCLASS_SET_SUPER(origin, RCLASS_SUPER(klass));
RCLASS_SET_SUPER(klass, origin);
RCLASS_SET_ORIGIN(klass, origin);
- RCLASS_M_TBL(origin) = RCLASS_M_TBL(klass);
RCLASS_M_TBL_INIT(klass);
rb_id_table_foreach(RCLASS_M_TBL(origin), cache_clear_refined_method, (void *)klass);
rb_id_table_foreach(RCLASS_M_TBL(origin), move_refined_method, (void *)klass);
@@ -1597,7 +1615,7 @@ class_descendants_recursive(VALUE klass, VALUE v)
{
struct subclass_traverse_data *data = (struct subclass_traverse_data *) v;
- if (BUILTIN_TYPE(klass) == T_CLASS && !FL_TEST(klass, FL_SINGLETON)) {
+ if (BUILTIN_TYPE(klass) == T_CLASS && !RCLASS_SINGLETON_P(klass)) {
if (data->buffer && data->count < data->maxcount && !rb_objspace_garbage_object_p(klass)) {
// assumes that this does not cause GC as long as the length does not exceed the capacity
rb_ary_push(data->buffer, klass);
@@ -1702,8 +1720,8 @@ rb_class_subclasses(VALUE klass)
VALUE
rb_class_attached_object(VALUE klass)
{
- if (!FL_TEST(klass, FL_SINGLETON)) {
- rb_raise(rb_eTypeError, "`%"PRIsVALUE"' is not a singleton class", klass);
+ if (!RCLASS_SINGLETON_P(klass)) {
+ rb_raise(rb_eTypeError, "'%"PRIsVALUE"' is not a singleton class", klass);
}
return RCLASS_ATTACHED_OBJECT(klass);
@@ -1805,7 +1823,7 @@ static bool
particular_class_p(VALUE mod)
{
if (!mod) return false;
- if (FL_TEST(mod, FL_SINGLETON)) return true;
+ if (RCLASS_SINGLETON_P(mod)) return true;
if (BUILTIN_TYPE(mod) == T_ICLASS) return true;
return false;
}
@@ -2082,19 +2100,19 @@ rb_obj_singleton_methods(int argc, const VALUE *argv, VALUE obj)
int recur = TRUE;
if (rb_check_arity(argc, 0, 1)) recur = RTEST(argv[0]);
- if (RB_TYPE_P(obj, T_CLASS) && FL_TEST(obj, FL_SINGLETON)) {
+ if (RCLASS_SINGLETON_P(obj)) {
rb_singleton_class(obj);
}
klass = CLASS_OF(obj);
origin = RCLASS_ORIGIN(klass);
me_arg.list = st_init_numtable();
me_arg.recur = recur;
- if (klass && FL_TEST(klass, FL_SINGLETON)) {
+ if (klass && RCLASS_SINGLETON_P(klass)) {
if ((mtbl = RCLASS_M_TBL(origin)) != 0) rb_id_table_foreach(mtbl, method_entry_i, &me_arg);
klass = RCLASS_SUPER(klass);
}
if (recur) {
- while (klass && (FL_TEST(klass, FL_SINGLETON) || RB_TYPE_P(klass, T_ICLASS))) {
+ while (klass && (RCLASS_SINGLETON_P(klass) || RB_TYPE_P(klass, T_ICLASS))) {
if (klass != origin && (mtbl = RCLASS_M_TBL(klass)) != 0) rb_id_table_foreach(mtbl, method_entry_i, &me_arg);
klass = RCLASS_SUPER(klass);
}
@@ -2198,13 +2216,13 @@ rb_special_singleton_class(VALUE obj)
return special_singleton_class_of(obj);
}
-/*!
- * \internal
- * Returns the singleton class of \a obj. Creates it if necessary.
+/**
+ * @internal
+ * Returns the singleton class of `obj`. Creates it if necessary.
*
- * \note DO NOT expose the returned singleton class to
+ * @note DO NOT expose the returned singleton class to
* outside of class.c.
- * Use \ref rb_singleton_class instead for
+ * Use @ref rb_singleton_class instead for
* consistency of the metaclass hierarchy.
*/
static VALUE
@@ -2228,13 +2246,16 @@ singleton_class_of(VALUE obj)
return klass;
case T_STRING:
- if (FL_TEST_RAW(obj, RSTRING_FSTR)) {
+ if (CHILLED_STRING_P(obj)) {
+ CHILLED_STRING_MUTATED(obj);
+ }
+ else if (FL_TEST_RAW(obj, RSTRING_FSTR)) {
rb_raise(rb_eTypeError, "can't define singleton");
}
}
klass = METACLASS_OF(obj);
- if (!(FL_TEST(klass, FL_SINGLETON) &&
+ if (!(RCLASS_SINGLETON_P(klass) &&
RCLASS_ATTACHED_OBJECT(klass) == obj)) {
klass = rb_make_metaclass(obj, klass);
}
@@ -2248,21 +2269,21 @@ void
rb_freeze_singleton_class(VALUE x)
{
/* should not propagate to meta-meta-class, and so on */
- if (!(RBASIC(x)->flags & FL_SINGLETON)) {
+ if (!RCLASS_SINGLETON_P(x)) {
VALUE klass = RBASIC_CLASS(x);
if (klass && // no class when hidden from ObjectSpace
FL_TEST(klass, (FL_SINGLETON|FL_FREEZE)) == FL_SINGLETON) {
- OBJ_FREEZE_RAW(klass);
+ OBJ_FREEZE(klass);
}
}
}
-/*!
- * Returns the singleton class of \a obj, or nil if obj is not a
+/**
+ * Returns the singleton class of `obj`, or nil if obj is not a
* singleton object.
*
- * \param obj an arbitrary object.
- * \return the singleton class or nil.
+ * @param obj an arbitrary object.
+ * @return the singleton class or nil.
*/
VALUE
rb_singleton_class_get(VALUE obj)
@@ -2273,7 +2294,7 @@ rb_singleton_class_get(VALUE obj)
return rb_special_singleton_class(obj);
}
klass = METACLASS_OF(obj);
- if (!FL_TEST(klass, FL_SINGLETON)) return Qnil;
+ if (!RCLASS_SINGLETON_P(klass)) return Qnil;
if (RCLASS_ATTACHED_OBJECT(klass) != obj) return Qnil;
return klass;
}
diff --git a/common.mk b/common.mk
index 71ae5eb6e6..1345709a2d 100644
--- a/common.mk
+++ b/common.mk
@@ -45,7 +45,8 @@ RUN_OPTS = --disable-gems
# GITPULLOPTIONS = --no-tags
-INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir) -I$(srcdir) -I$(UNICODE_HDR_DIR)
+PRISM_SRCDIR = $(srcdir)/prism
+INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir) -I$(srcdir) -I$(PRISM_SRCDIR) -I$(UNICODE_HDR_DIR) $(incflags)
GEM_HOME =
GEM_PATH =
@@ -63,25 +64,54 @@ LIBRUBY_EXTS = ./.libruby-with-ext.time
REVISION_H = ./.revision.time
PLATFORM_D = $(TIMESTAMPDIR)/.$(PLATFORM_DIR).time
ENC_TRANS_D = $(TIMESTAMPDIR)/.enc-trans.time
-RDOC = $(XRUBY) "$(srcdir)/libexec/rdoc" --root "$(srcdir)" --encoding=UTF-8 --all
+RDOC = $(XRUBY) "$(tooldir)/rdoc-srcdir"
RDOCOUT = $(EXTOUT)/rdoc
HTMLOUT = $(EXTOUT)/html
CAPIOUT = doc/capi
INSTALL_DOC_OPTS = --rdoc-output="$(RDOCOUT)" --html-output="$(HTMLOUT)"
-RDOC_GEN_OPTS = --page-dir "$(srcdir)/doc" --no-force-update \
+RDOC_GEN_OPTS = --no-force-update \
--title "Documentation for Ruby $(RUBY_API_VERSION)" \
- --main README.md
+ $(empty)
INITOBJS = dmyext.$(OBJEXT) dmyenc.$(OBJEXT)
NORMALMAINOBJ = main.$(OBJEXT)
MAINOBJ = $(NORMALMAINOBJ)
DLDOBJS = $(INITOBJS)
EXTSOLIBS =
-MINIOBJS = $(ARCHMINIOBJS) miniinit.$(OBJEXT) dmyext.$(OBJEXT)
+MINIOBJS = $(ARCHMINIOBJS) miniinit.$(OBJEXT)
ENC_MK = enc.mk
MAKE_ENC = -f $(ENC_MK) V="$(V)" UNICODE_HDR_DIR="$(UNICODE_HDR_DIR)" \
RUBY="$(BOOTSTRAPRUBY)" MINIRUBY="$(BOOTSTRAPRUBY)" $(mflags)
+PRISM_BUILD_DIR = prism
+
+PRISM_FILES = prism/api_node.$(OBJEXT) \
+ prism/api_pack.$(OBJEXT) \
+ prism/diagnostic.$(OBJEXT) \
+ prism/encoding.$(OBJEXT) \
+ prism/extension.$(OBJEXT) \
+ prism/node.$(OBJEXT) \
+ prism/options.$(OBJEXT) \
+ prism/pack.$(OBJEXT) \
+ prism/prettyprint.$(OBJEXT) \
+ prism/regexp.$(OBJEXT) \
+ prism/serialize.$(OBJEXT) \
+ prism/static_literals.$(OBJEXT) \
+ prism/token_type.$(OBJEXT) \
+ prism/util/pm_buffer.$(OBJEXT) \
+ prism/util/pm_char.$(OBJEXT) \
+ prism/util/pm_constant_pool.$(OBJEXT) \
+ prism/util/pm_integer.$(OBJEXT) \
+ prism/util/pm_list.$(OBJEXT) \
+ prism/util/pm_memchr.$(OBJEXT) \
+ prism/util/pm_newline_list.$(OBJEXT) \
+ prism/util/pm_string.$(OBJEXT) \
+ prism/util/pm_string_list.$(OBJEXT) \
+ prism/util/pm_strncasecmp.$(OBJEXT) \
+ prism/util/pm_strpbrk.$(OBJEXT) \
+ prism/prism.$(OBJEXT) \
+ prism_init.$(OBJEXT)
+
COMMONOBJS = array.$(OBJEXT) \
ast.$(OBJEXT) \
bignum.$(OBJEXT) \
@@ -103,6 +133,7 @@ COMMONOBJS = array.$(OBJEXT) \
gc.$(OBJEXT) \
hash.$(OBJEXT) \
inits.$(OBJEXT) \
+ imemo.$(OBJEXT) \
io.$(OBJEXT) \
io_buffer.$(OBJEXT) \
iseq.$(OBJEXT) \
@@ -113,10 +144,12 @@ COMMONOBJS = array.$(OBJEXT) \
rjit.$(OBJEXT) \
rjit_c.$(OBJEXT) \
node.$(OBJEXT) \
+ node_dump.$(OBJEXT) \
numeric.$(OBJEXT) \
object.$(OBJEXT) \
pack.$(OBJEXT) \
parse.$(OBJEXT) \
+ parser_st.$(OBJEXT) \
proc.$(OBJEXT) \
process.$(OBJEXT) \
ractor.$(OBJEXT) \
@@ -131,6 +164,7 @@ COMMONOBJS = array.$(OBJEXT) \
regparse.$(OBJEXT) \
regsyntax.$(OBJEXT) \
ruby.$(OBJEXT) \
+ ruby_parser.$(OBJEXT) \
scheduler.$(OBJEXT) \
shape.$(OBJEXT) \
signal.$(OBJEXT) \
@@ -143,7 +177,6 @@ COMMONOBJS = array.$(OBJEXT) \
thread.$(OBJEXT) \
time.$(OBJEXT) \
transcode.$(OBJEXT) \
- transient_heap.$(OBJEXT) \
util.$(OBJEXT) \
variable.$(OBJEXT) \
version.$(OBJEXT) \
@@ -153,6 +186,7 @@ COMMONOBJS = array.$(OBJEXT) \
vm_sync.$(OBJEXT) \
vm_trace.$(OBJEXT) \
weakmap.$(OBJEXT) \
+ $(PRISM_FILES) \
$(YJIT_OBJ) \
$(YJIT_LIBOBJ) \
$(COROUTINE_OBJ) \
@@ -161,13 +195,96 @@ COMMONOBJS = array.$(OBJEXT) \
$(BUILTIN_TRANSOBJS) \
$(MISSING)
+$(PRISM_FILES): $(PRISM_BUILD_DIR)/.time $(PRISM_BUILD_DIR)/util/.time
+
+$(PRISM_BUILD_DIR)/.time $(PRISM_BUILD_DIR)/util/.time:
+ $(Q) $(MAKEDIRS) $(@D)
+ @$(NULLCMD) > $@
+
+main: $(srcdir)/lib/prism/compiler.rb
+srcs: $(srcdir)/lib/prism/compiler.rb
+$(srcdir)/lib/prism/compiler.rb: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/lib/prism/compiler.rb.erb
+ $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb lib/prism/compiler.rb $(srcdir)/lib/prism/compiler.rb
+
+main: $(srcdir)/lib/prism/dispatcher.rb
+srcs: $(srcdir)/lib/prism/dispatcher.rb
+$(srcdir)/lib/prism/dispatcher.rb: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/lib/prism/dispatcher.rb.erb
+ $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb lib/prism/dispatcher.rb $(srcdir)/lib/prism/dispatcher.rb
+
+main: $(srcdir)/lib/prism/dsl.rb
+srcs: $(srcdir)/lib/prism/dsl.rb
+$(srcdir)/lib/prism/dsl.rb: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/lib/prism/dsl.rb.erb
+ $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb lib/prism/dsl.rb $(srcdir)/lib/prism/dsl.rb
+
+main: $(srcdir)/lib/prism/inspect_visitor.rb
+srcs: $(srcdir)/lib/prism/inspect_visitor.rb
+$(srcdir)/lib/prism/inspect_visitor.rb: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/lib/prism/inspect_visitor.rb.erb
+ $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb lib/prism/inspect_visitor.rb $(srcdir)/lib/prism/inspect_visitor.rb
+
+main: $(srcdir)/lib/prism/mutation_compiler.rb
+srcs: $(srcdir)/lib/prism/mutation_compiler.rb
+$(srcdir)/lib/prism/mutation_compiler.rb: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/lib/prism/mutation_compiler.rb.erb
+ $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb lib/prism/mutation_compiler.rb $(srcdir)/lib/prism/mutation_compiler.rb
+
+main: $(srcdir)/lib/prism/node.rb
+srcs: $(srcdir)/lib/prism/node.rb
+$(srcdir)/lib/prism/node.rb: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/lib/prism/node.rb.erb
+ $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb lib/prism/node.rb $(srcdir)/lib/prism/node.rb
+
+main: $(srcdir)/lib/prism/reflection.rb
+srcs: $(srcdir)/lib/prism/reflection.rb
+$(srcdir)/lib/prism/reflection.rb: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/lib/prism/reflection.rb.erb
+ $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb lib/prism/reflection.rb $(srcdir)/lib/prism/reflection.rb
+
+main: $(srcdir)/lib/prism/serialize.rb
+srcs: $(srcdir)/lib/prism/serialize.rb
+$(srcdir)/lib/prism/serialize.rb: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/lib/prism/serialize.rb.erb
+ $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb lib/prism/serialize.rb $(srcdir)/lib/prism/serialize.rb
+
+main: $(srcdir)/lib/prism/visitor.rb
+srcs: $(srcdir)/lib/prism/visitor.rb
+$(srcdir)/lib/prism/visitor.rb: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/lib/prism/visitor.rb.erb
+ $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb lib/prism/visitor.rb $(srcdir)/lib/prism/visitor.rb
+
+srcs: prism/api_node.c
+prism/api_node.c: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/ext/prism/api_node.c.erb
+ $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb ext/prism/api_node.c $@
+
+srcs: prism/ast.h
+prism/ast.h: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/include/prism/ast.h.erb
+ $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb include/prism/ast.h $@
+
+srcs: prism/diagnostic.c
+prism/diagnostic.c: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/src/diagnostic.c.erb
+ $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb src/diagnostic.c $@
+
+srcs: prism/diagnostic.h
+prism/diagnostic.h: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/include/prism/diagnostic.h.erb
+ $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb include/prism/diagnostic.h $@
+
+srcs: prism/node.c
+prism/node.c: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/src/node.c.erb
+ $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb src/node.c $@
+
+srcs: prism/prettyprint.c
+prism/prettyprint.c: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/src/prettyprint.c.erb
+ $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb src/prettyprint.c $@
+
+srcs: prism/serialize.c
+prism/serialize.c: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/src/serialize.c.erb
+ $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb src/serialize.c $@
+
+srcs: prism/token_type.c
+prism/token_type.c: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/src/token_type.c.erb
+ $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb src/token_type.c $@
+
EXPORTOBJS = $(DLNOBJ) \
localeinit.$(OBJEXT) \
loadpath.$(OBJEXT) \
$(COMMONOBJS)
OBJS = $(EXPORTOBJS) builtin.$(OBJEXT)
-ALLOBJS = $(NORMALMAINOBJ) $(MINIOBJS) $(COMMONOBJS) $(INITOBJS)
+ALLOBJS = $(OBJS) $(MINIOBJS) $(INITOBJS) $(MAINOBJ)
GOLFOBJS = goruby.$(OBJEXT)
@@ -199,7 +316,7 @@ INSTALL_DATA_MODE = 0644
BOOTSTRAPRUBY_COMMAND = $(BOOTSTRAPRUBY) $(BOOTSTRAPRUBY_OPT)
TESTSDIR = $(srcdir)/test
TOOL_TESTSDIR = $(tooldir)/test
-TEST_EXCLUDES = --excludes-dir=$(TESTSDIR)/excludes --name=!/memory_leak/
+TEST_EXCLUDES = --excludes-dir=$(TESTSDIR)/.excludes --name=!/memory_leak/
TESTWORKDIR = testwork
TESTOPTS = $(RUBY_TESTOPTS)
@@ -229,7 +346,7 @@ YJIT_RUSTC_ARGS = --crate-name=yjit \
'--out-dir=$(CARGO_TARGET_DIR)/release/' \
$(top_srcdir)/yjit/src/lib.rs
-all: $(SHOWFLAGS) main docs
+all: $(SHOWFLAGS) main
main: $(SHOWFLAGS) exts $(ENCSTATIC:static=lib)encs
@$(NULLCMD)
@@ -299,7 +416,7 @@ configure-ext: $(EXTS_MK)
build-ext: $(EXTS_MK)
$(Q)$(MAKE) -f $(EXTS_MK) $(mflags) libdir="$(libdir)" LIBRUBY_EXTS=$(LIBRUBY_EXTS) \
EXTENCS="$(ENCOBJS)" BASERUBY="$(BASERUBY)" MINIRUBY="$(MINIRUBY)" \
- UPDATE_LIBRARIES=no $(EXTSTATIC)
+ $(EXTSTATIC)
$(Q)$(MAKE) $(EXTS_NOTE)
exts-note: $(EXTS_MK)
@@ -315,7 +432,7 @@ programs: $(PROGRAM) $(WPROGRAM) $(arch)-fake.rb
$(PREP): $(MKFILES)
-miniruby$(EXEEXT): config.status $(ALLOBJS) $(ARCHFILE)
+miniruby$(EXEEXT): config.status $(NORMALMAINOBJ) $(MINIOBJS) $(COMMONOBJS) $(ARCHFILE)
objs: $(ALLOBJS)
@@ -342,8 +459,8 @@ Doxyfile: $(srcdir)/template/Doxyfile.tmpl $(PREP) $(tooldir)/generic_erb.rb $(R
$(Q) $(MINIRUBY) $(tooldir)/generic_erb.rb -o $@ $(srcdir)/template/Doxyfile.tmpl \
--srcdir="$(srcdir)" --miniruby="$(MINIRUBY)"
-program: $(SHOWFLAGS) $(PROGRAM)
-wprogram: $(SHOWFLAGS) $(WPROGRAM)
+program: $(SHOWFLAGS) $(DOT_WAIT) $(PROGRAM)
+wprogram: $(SHOWFLAGS) $(DOT_WAIT) $(WPROGRAM)
mini: PHONY miniruby$(EXEEXT)
$(PROGRAM) $(WPROGRAM): $(LIBRUBY) $(MAINOBJ) $(OBJS) $(EXTOBJS) $(SETUP) $(PREP)
@@ -363,19 +480,17 @@ ruby.imp: $(COMMONOBJS)
$(Q){ \
$(NM) -Pgp $(COMMONOBJS) | \
awk 'BEGIN{print "#!"}; $$2~/^[A-TV-Z]$$/&&$$1!~/^$(SYMBOL_PREFIX)(Init_|InitVM_|ruby_static_id_|.*_threadptr_|rb_ec_)|^\./{print $$1}'; \
- ($(CHDIR) $(srcdir) && \
- exec sed -n '/^RJIT_FUNC_EXPORTED/!d;N;s/.*\n\(rb_[a-zA-Z_0-9]*\).*/$(SYMBOL_PREFIX)\1/p' cont.c gc.c thread*c vm*.c) \
} | \
sort -u -o $@
install: install-$(INSTALLDOC)
-docs: $(DOCTARGETS)
+docs: srcs-doc $(DOCTARGETS)
pkgconfig-data: $(ruby_pc)
$(ruby_pc): $(srcdir)/template/ruby.pc.in config.status
-install-all: docs pre-install-all do-install-all post-install-all
+install-all: pre-install-all do-install-all post-install-all
pre-install-all:: all pre-install-local pre-install-ext pre-install-gem pre-install-doc
-do-install-all: pre-install-all
+do-install-all: pre-install-all $(DOT_WAIT) docs
$(INSTRUBY) --make="$(MAKE)" $(INSTRUBY_ARGS) --install=all $(INSTALL_DOC_OPTS)
post-install-all:: post-install-local post-install-ext post-install-gem post-install-doc
@$(NULLCMD)
@@ -563,18 +678,24 @@ do-install-dbg: $(PROGRAM) pre-install-dbg
post-install-dbg::
@$(NULLCMD)
-rdoc: PHONY main
+rdoc: PHONY main srcs-doc
@echo Generating RDoc documentation
- $(Q) $(RDOC) --ri --op "$(RDOCOUT)" $(RDOC_GEN_OPTS) $(RDOCFLAGS) "$(srcdir)"
+ $(Q) $(RDOC) --ri --op "$(RDOCOUT)" $(RDOC_GEN_OPTS) $(RDOCFLAGS) .
-html: PHONY main
+html: PHONY main srcs-doc
@echo Generating RDoc HTML files
- $(Q) $(RDOC) --op "$(HTMLOUT)" $(RDOC_GEN_OPTS) $(RDOCFLAGS) "$(srcdir)"
+ $(Q) $(RDOC) --op "$(HTMLOUT)" $(RDOC_GEN_OPTS) $(RDOCFLAGS) .
-rdoc-coverage: PHONY main
+rdoc-coverage: PHONY main srcs-doc
@echo Generating RDoc coverage report
$(Q) $(RDOC) --quiet -C $(RDOCFLAGS) "$(srcdir)"
+undocumented: PHONY main srcs-doc
+ $(Q) $(RDOC) --quiet -C $(RDOCFLAGS) "$(srcdir)" | \
+ sed -n \
+ -e '/^ *# in file /{' -e 's///;N;s/\n/: /p' -e '}' \
+ -e 's/^ *\(.*[^ ]\) *# in file \(.*\)/\2: \1/p'
+
RDOCBENCHOUT=/tmp/rdocbench
GCBENCH_ITEM=null
@@ -604,20 +725,25 @@ clear-installed-list: PHONY
clean: clean-ext clean-enc clean-golf clean-docs clean-extout clean-local clean-platform clean-spec
clean-local:: clean-runnable
- $(Q)$(RM) $(OBJS) $(MINIOBJS) $(INITOBJS) $(MAINOBJ) $(LIBRUBY_A) $(LIBRUBY_SO) $(LIBRUBY) $(LIBRUBY_ALIASES)
+ $(Q)$(RM) $(ALLOBJS) $(LIBRUBY_A) $(LIBRUBY_SO) $(LIBRUBY) $(LIBRUBY_ALIASES)
$(Q)$(RM) $(PROGRAM) $(WPROGRAM) miniruby$(EXEEXT) dmyext.$(OBJEXT) dmyenc.$(OBJEXT) $(ARCHFILE) .*.time
$(Q)$(RM) y.tab.c y.output encdb.h transdb.h config.log rbconfig.rb $(ruby_pc) $(COROUTINE_H:/Context.h=/.time)
$(Q)$(RM) probes.h probes.$(OBJEXT) probes.stamp ruby-glommed.$(OBJEXT) ruby.imp ChangeLog $(STATIC_RUBY)$(EXEEXT)
$(Q)$(RM) GNUmakefile.old Makefile.old $(arch)-fake.rb bisect.sh $(ENC_TRANS_D) builtin_binary.inc
+ $(Q)$(RM) $(PRISM_BUILD_DIR)/.time $(PRISM_BUILD_DIR)/*/.time yjit_exit_locations.dump
-$(Q)$(RMALL) yjit/target
- -$(Q) $(RMDIR) enc/jis enc/trans enc $(COROUTINE_H:/Context.h=) coroutine yjit 2> $(NULL) || $(NULLCMD)
+ -$(Q) $(RMDIR) enc/jis enc/trans enc $(COROUTINE_H:/Context.h=) coroutine yjit \
+ $(PRISM_BUILD_DIR)/*/ $(PRISM_BUILD_DIR) tmp \
+ 2> $(NULL) || $(NULLCMD)
bin/clean-runnable:: PHONY
$(Q)$(CHDIR) bin 2>$(NULL) && $(RM) $(PROGRAM) $(WPROGRAM) $(GORUBY)$(EXEEXT) bin/*.$(DLEXT) 2>$(NULL) || $(NULLCMD)
lib/clean-runnable:: PHONY
- $(Q)$(CHDIR) lib 2>$(NULL) && $(RM) $(LIBRUBY_A) $(LIBRUBY) $(LIBRUBY_ALIASES) $(RUBY_BASE_NAME)/$(RUBY_PROGRAM_VERSION) $(RUBY_BASE_NAME)/vendor_ruby 2>$(NULL) || $(NULLCMD)
+ $(Q)$(CHDIR) lib 2>$(NULL) && $(RM) $(LIBRUBY_A) $(LIBRUBY) $(LIBRUBY_ALIASES) $(RUBY_BASE_NAME)/$(ruby_version) $(RUBY_BASE_NAME)/vendor_ruby 2>$(NULL) || $(NULLCMD)
clean-runnable:: bin/clean-runnable lib/clean-runnable PHONY
$(Q)$(RMDIR) lib/$(RUBY_BASE_NAME) lib bin 2>$(NULL) || $(NULLCMD)
+ -$(Q)$(RM) $(EXTOUT)/$(arch)/rbconfig.rb $(EXTOUT)/common/$(arch)
+ -$(Q)$(RMALL) exe/
clean-ext:: PHONY
clean-golf: PHONY
$(Q)$(RM) $(GORUBY)$(EXEEXT) $(GOLFOBJS)
@@ -758,7 +884,8 @@ $(arch:noarch=ignore)-fake.rb: $(srcdir)/template/fake.rb.in $(tooldir)/generic_
$(ECHO) generating $@
$(Q) $(CPP) -DRUBY_EXPORT $(INCFLAGS) $(CPPFLAGS) "$(srcdir)/version.c" | \
$(BOOTSTRAPRUBY) "$(tooldir)/generic_erb.rb" -o $@ "$(srcdir)/template/fake.rb.in" \
- i=- srcdir="$(srcdir)" BASERUBY="$(BASERUBY)"
+ i=- srcdir="$(srcdir)" BASERUBY="$(BASERUBY)" \
+ LIBPATHENV="$(LIBPATHENV)" PRELOADENV="$(PRELOADENV)" LIBRUBY_SO="$(LIBRUBY_SO)"
noarch-fake.rb: # prerequisite of yes-fake
$(Q) exit > $@
@@ -823,16 +950,28 @@ test-sample: test-basic # backward compatibility for mswin-build
test-short: btest-ruby $(DOT_WAIT) test-knownbug $(DOT_WAIT) test-basic
test: test-short
+# Separate to skip updating encs and exts by `make -o test-precheck`
+# for GNU make.
+test-precheck: $(ENCSTATIC:static=lib)encs exts PHONY $(DOT_WAIT)
+yes-test-all-precheck: programs $(DOT_WAIT) test-precheck
+
+PRECHECK_TEST_ALL = yes-test-all-precheck
+
# $ make test-all TESTOPTS="--help" displays more detail
# for example, make test-all TESTOPTS="-j2 -v -n test-name -- test-file-name"
test-all: $(TEST_RUNNABLE)-test-all
-yes-test-all: programs PHONY
+yes-test-all: $(PRECHECK_TEST_ALL)
$(ACTIONS_GROUP)
- $(gnumake_recursive)$(Q)$(exec) $(RUNRUBY) "$(TESTSDIR)/runner.rb" --ruby="$(RUNRUBY)" $(TEST_EXCLUDES) $(TESTOPTS) $(TESTS)
+ $(gnumake_recursive)$(Q)$(exec) $(RUNRUBY) -r$(tooldir)/lib/_tmpdir \
+ "$(TESTSDIR)/runner.rb" --ruby="$(RUNRUBY)" \
+ $(TEST_EXCLUDES) $(TESTOPTS) $(TESTS)
$(ACTIONS_ENDGROUP)
TESTS_BUILD = mkmf
no-test-all: PHONY
- $(gnumake_recursive)$(MINIRUBY) -I"$(srcdir)/lib" "$(TESTSDIR)/runner.rb" $(TESTOPTS) $(TESTS_BUILD)
+ $(ACTIONS_GROUP)
+ $(gnumake_recursive)$(MINIRUBY) -I"$(srcdir)/lib" -r$(tooldir)/lib/_tmpdir \
+ "$(TESTSDIR)/runner.rb" $(TESTOPTS) $(TESTS_BUILD)
+ $(ACTIONS_ENDGROUP)
test-almost: test-all
yes-test-almost: yes-test-all
@@ -868,17 +1007,26 @@ $(RBCONFIG): $(tooldir)/mkconfig.rb config.status $(srcdir)/version.h $(srcdir)/
test-rubyspec: test-spec
yes-test-rubyspec: yes-test-spec
-test-spec-precheck: programs yes-fake
+yes-test-spec-precheck: yes-test-all-precheck yes-fake
test-spec: $(TEST_RUNNABLE)-test-spec
-yes-test-spec: test-spec-precheck
+yes-test-spec: yes-test-spec-precheck
$(ACTIONS_GROUP)
$(gnumake_recursive)$(Q) \
- $(RUNRUBY) -r./$(arch)-fake -r$(tooldir)/rubyspec_temp \
+ $(RUNRUBY) -r./$(arch)-fake -r$(tooldir)/lib/_tmpdir \
$(srcdir)/spec/mspec/bin/mspec run -B $(srcdir)/spec/default.mspec $(MSPECOPT) $(SPECOPTS)
$(ACTIONS_ENDGROUP)
no-test-spec:
+test-prism-spec: $(TEST_RUNNABLE)-test-prism-spec
+yes-test-prism-spec: yes-test-spec-precheck
+ $(ACTIONS_GROUP)
+ $(gnumake_recursive)$(Q) \
+ $(RUNRUBY) -r./$(arch)-fake -r$(tooldir)/lib/_tmpdir \
+ $(srcdir)/spec/mspec/bin/mspec run -B $(srcdir)/spec/default.mspec -B $(srcdir)/spec/prism.mspec $(MSPECOPT) $(SPECOPTS)
+ $(ACTIONS_ENDGROUP)
+no-test-prism-spec:
+
check: $(DOT_WAIT) test-spec
RUNNABLE = $(LIBRUBY_RELATIVE:no=un)-runnable
@@ -925,12 +1073,13 @@ PHONY:
{$(srcdir)}.y.c:
$(ECHO) generating $@
$(Q)$(BASERUBY) $(tooldir)/id2token.rb $(SRC_FILE) | \
- $(YACC) -d $(YFLAGS) -o$@ -h$*.h - parse.y
+ $(YACC) $(YFLAGS) -o$@ -H$*.h - parse.y
$(PLATFORM_D):
$(Q) $(MAKEDIRS) $(PLATFORM_DIR) $(@D)
@$(NULLCMD) > $@
+exe/$(PROGRAM): $(TIMESTAMPDIR)/$(arch)/.time
exe/$(PROGRAM): ruby-runner.c ruby-runner.h exe/.time $(PREP) {$(VPATH)}config.h
$(Q) $(CC) $(CFLAGS) $(INCFLAGS) $(CPPFLAGS) -DRUBY_INSTALL_NAME=$(@F) $(COUTFLAG)ruby-runner.$(OBJEXT) -c $(CSRCFLAG)$(srcdir)/ruby-runner.c
$(Q) $(PURIFY) $(CC) $(CFLAGS) $(LDFLAGS) $(OUTFLAG)$@ ruby-runner.$(OBJEXT) $(LIBS)
@@ -943,6 +1092,8 @@ exe/$(PROGRAM): ruby-runner.c ruby-runner.h exe/.time $(PREP) {$(VPATH)}config.h
-e ' File.symlink(prog, dest)' \
-e 'end' \
$(@F) $(@D) $(RUBY_INSTALL_NAME)$(EXEEXT)
+ $(Q) $(BOOTSTRAPRUBY) -r$(srcdir)/lib/fileutils \
+ -e 'FileUtils::Verbose.ln_sr(*ARGV, force: true)' rbconfig.rb $(EXTOUT)/$(arch)
exe/.time:
$(Q) $(MAKEDIRS) $(@D)
@@ -1010,7 +1161,7 @@ parse.$(OBJEXT): {$(VPATH)}parse.c
miniprelude.$(OBJEXT): {$(VPATH)}miniprelude.c
# dependencies for optional sources.
-compile.$(OBJEXT): {$(VPATH)}opt_sc.inc {$(VPATH)}optunifs.inc
+compile.$(OBJEXT): {$(VPATH)}optunifs.inc
win32/win32.$(OBJEXT): {$(VPATH)}win32/win32.c {$(VPATH)}win32/file.h \
{$(VPATH)}dln.h {$(VPATH)}dln_find.c {$(VPATH)}encindex.h \
@@ -1037,7 +1188,6 @@ INSNS2VMOPT = --srcdir="$(srcdir)"
srcs_vpath = {$(VPATH)}
inc_common_headers = $(tooldir)/ruby_vm/views/_copyright.erb $(tooldir)/ruby_vm/views/_notice.erb
-$(srcs_vpath)opt_sc.inc: $(tooldir)/ruby_vm/views/opt_sc.inc.erb $(inc_common_headers)
$(srcs_vpath)optinsn.inc: $(tooldir)/ruby_vm/views/optinsn.inc.erb $(inc_common_headers)
$(srcs_vpath)optunifs.inc: $(tooldir)/ruby_vm/views/optunifs.inc.erb $(inc_common_headers)
$(srcs_vpath)insns.inc: $(tooldir)/ruby_vm/views/insns.inc.erb $(inc_common_headers)
@@ -1081,9 +1231,16 @@ common-srcs: $(srcs_vpath)parse.c $(srcs_vpath)lex.c $(srcs_vpath)enc/trans/newl
missing-srcs: $(srcdir)/missing/des_tables.c
-srcs: common-srcs missing-srcs srcs-enc
+srcs: common-srcs missing-srcs srcs-enc srcs-doc
-EXT_SRCS = $(srcdir)/ext/ripper/ripper.c \
+RIPPER_SRCS = $(srcdir)/ext/ripper/ripper.c \
+ $(srcdir)/ext/ripper/ripper_init.c \
+ $(srcdir)/ext/ripper/eventids1.h \
+ $(srcdir)/ext/ripper/eventids1.c \
+ $(srcdir)/ext/ripper/eventids2table.c \
+ # RIPPER_SRCS
+
+EXT_SRCS = ripper_srcs \
$(srcdir)/ext/rbconfig/sizeof/sizes.c \
$(srcdir)/ext/rbconfig/sizeof/limits.c \
$(srcdir)/ext/socket/constdefs.c \
@@ -1129,9 +1286,9 @@ id.c: $(tooldir)/generic_erb.rb $(srcdir)/template/id.c.tmpl $(srcdir)/defs/id.d
$(Q) $(BASERUBY) $(tooldir)/generic_erb.rb --output=$@ \
$(srcdir)/template/id.c.tmpl
-node_name.inc: $(tooldir)/node_name.rb $(srcdir)/node.h
+node_name.inc: $(tooldir)/node_name.rb $(srcdir)/rubyparser.h
$(ECHO) generating $@
- $(Q) $(BASERUBY) -n $(tooldir)/node_name.rb < $(srcdir)/node.h > $@
+ $(Q) $(BASERUBY) -n $(tooldir)/node_name.rb < $(srcdir)/rubyparser.h > $@
encdb.h: $(RBCONFIG) $(tooldir)/generic_erb.rb $(srcdir)/template/encdb.h.tmpl
$(ECHO) generating $@
@@ -1183,9 +1340,13 @@ preludes: {$(srcdir)}golf_prelude.c
$(ECHO) making $@
$(Q) $(BASERUBY) $(tooldir)/mk_builtin_loader.rb $<
-builtin_binary.inc: $(PREP) $(BUILTIN_RB_SRCS) $(srcdir)/template/builtin_binary.inc.tmpl
+$(BUILTIN_BINARY:yes=built)in_binary.inc: $(PREP) $(BUILTIN_RB_SRCS) $(srcdir)/template/builtin_binary.inc.tmpl
$(Q) $(MINIRUBY) $(tooldir)/generic_erb.rb -o $@ \
- $(srcdir)/template/builtin_binary.inc.tmpl -- --cross=$(CROSS_COMPILING)
+ $(srcdir)/template/builtin_binary.inc.tmpl
+ -$(Q) sha256sum $@ 2> $(NULL) || $(NULLCMD)
+
+$(BUILTIN_BINARY:no=builtin)_binary.inc:
+ $(Q) echo> $@ // empty $(@F)
$(BUILTIN_RB_INCS): $(top_srcdir)/tool/mk_builtin_loader.rb
@@ -1199,7 +1360,11 @@ $(REVISION_H)$(yes_baseruby:yes=~disabled~):
# uncommon.mk: $(REVISION_H)
# $(MKFILES): $(REVISION_H)
-$(srcdir)/ext/ripper/ripper.c: $(srcdir)/ext/ripper/tools/preproc.rb $(srcdir)/parse.y $(srcdir)/defs/id.def $(srcdir)/ext/ripper/depend
+ripper_srcs: $(RIPPER_SRCS)
+
+$(RIPPER_SRCS): $(srcdir)/parse.y $(srcdir)/defs/id.def
+$(RIPPER_SRCS): $(srcdir)/ext/ripper/tools/preproc.rb $(srcdir)/ext/ripper/tools/dsl.rb
+$(RIPPER_SRCS): $(srcdir)/ext/ripper/ripper_init.c.tmpl $(srcdir)/ext/ripper/eventids2.c
$(ECHO) generating $@
$(Q) $(CHDIR) $(@D) && \
$(CAT_DEPEND) depend | \
@@ -1233,7 +1398,7 @@ $(srcdir)/ext/rbconfig/sizeof/limits.c: $(srcdir)/ext/rbconfig/sizeof/depend \
$(exec) $(MAKE) -f - $(mflags) \
Q=$(Q) ECHO=$(ECHO) top_srcdir=../../.. srcdir=. VPATH=../../.. RUBY="$(BASERUBY)" $(@F)
-$(srcdir)/ext/socket/constdefs.c: $(srcdir)/ext/socket/depend
+$(srcdir)/ext/socket/constdefs.c: $(srcdir)/ext/socket/depend $(srcdir)/ext/socket/mkconstants.rb
$(Q) $(CHDIR) $(@D) && \
$(CAT_DEPEND) depend | \
$(exec) $(MAKE) -f - $(mflags) \
@@ -1401,7 +1566,9 @@ clone-bundled-gems-src: PHONY
gems/bundled_gems
outdate-bundled-gems: PHONY
- $(Q) $(BASERUBY) $(tooldir)/$@.rb --make="$(MAKE)" --mflags="$(MFLAGS)" "$(srcdir)"
+ $(Q) $(BASERUBY) $(tooldir)/$@.rb --make="$(MAKE)" --mflags="$(MFLAGS)" \
+ --ruby-platform=$(arch) --ruby-version=$(ruby_version) \
+ "$(srcdir)"
update-bundled_gems: PHONY
$(Q) $(RUNRUBY) -rrubygems \
@@ -1429,11 +1596,11 @@ no-test-bundled-gems-prepare: no-test-bundled-gems-precheck
yes-test-bundled-gems-prepare: yes-test-bundled-gems-precheck
$(ACTIONS_GROUP)
$(XRUBY) -C "$(srcdir)" bin/gem install --no-document \
- --install-dir .bundle --conservative "bundler" "minitest:~> 5" "test-unit" "rake" "hoe" "yard" "pry" "packnga" "rexml" "json-schema" "test-unit-rr"
+ --install-dir .bundle --conservative "hoe" "json-schema" "test-unit-rr"
$(ACTIONS_ENDGROUP)
PREPARE_BUNDLED_GEMS = test-bundled-gems-prepare
-test-bundled-gems: $(TEST_RUNNABLE)-test-bundled-gems
+test-bundled-gems: $(TEST_RUNNABLE)-test-bundled-gems $(TEST_RUNNABLE)-test-bundled-gems-spec
yes-test-bundled-gems: test-bundled-gems-run
no-test-bundled-gems:
@@ -1444,6 +1611,15 @@ BUNDLED_GEMS =
test-bundled-gems-run: $(PREPARE_BUNDLED_GEMS)
$(gnumake_recursive)$(Q) $(XRUBY) $(tooldir)/test-bundled-gems.rb $(BUNDLED_GEMS)
+test-bundled-gems-spec: $(TEST_RUNNABLE)-test-bundled-gems-spec
+yes-test-bundled-gems-spec: yes-test-spec-precheck $(PREPARE_BUNDLED_GEMS)
+ $(ACTIONS_GROUP)
+ $(gnumake_recursive)$(Q) \
+ $(RUNRUBY) -r./$(arch)-fake -r$(tooldir)/lib/_tmpdir \
+ $(srcdir)/spec/mspec/bin/mspec run -B $(srcdir)/spec/bundled_gems.mspec $(MSPECOPT) $(SPECOPTS)
+ $(ACTIONS_ENDGROUP)
+no-test-bundled-gems-spec:
+
test-syntax-suggest-precheck: $(TEST_RUNNABLE)-test-syntax-suggest-precheck
no-test-syntax-suggest-precheck:
yes-test-syntax-suggest-precheck: main
@@ -1453,25 +1629,30 @@ no-test-syntax-suggest-prepare: no-test-syntax-suggest-precheck
yes-test-syntax-suggest-prepare: yes-test-syntax-suggest-precheck
$(ACTIONS_GROUP)
$(XRUBY) -C "$(srcdir)" bin/gem install --no-document \
- --install-dir .bundle --conservative "bundler" "rake" "rspec:~> 3"
+ --install-dir .bundle --conservative "rspec:~> 3"
$(ACTIONS_ENDGROUP)
RSPECOPTS =
SYNTAX_SUGGEST_SPECS =
-PREPARE_SYNTAX_SUGGEST = test-syntax-suggest-prepare
+PREPARE_SYNTAX_SUGGEST = $(TEST_RUNNABLE)-test-syntax-suggest-prepare
test-syntax-suggest: $(TEST_RUNNABLE)-test-syntax-suggest
-yes-test-syntax-suggest: yes-$(PREPARE_SYNTAX_SUGGEST)
+yes-test-syntax-suggest: $(PREPARE_SYNTAX_SUGGEST)
+ $(ACTIONS_GROUP)
$(XRUBY) -C $(srcdir) -Ispec/syntax_suggest:spec/lib .bundle/bin/rspec \
+ --require rspec/expectations \
--require spec_helper --require formatter_overrides --require spec_coverage \
$(RSPECOPTS) spec/syntax_suggest/$(SYNTAX_SUGGEST_SPECS)
+ $(ACTIONS_ENDGROUP)
no-test-syntax-suggest:
-check: $(DOT_WAIT) $(TEST_RUNNABLE)-$(PREPARE_SYNTAX_SUGGEST) test-syntax-suggest
+check: $(DOT_WAIT) $(PREPARE_SYNTAX_SUGGEST) test-syntax-suggest
test-bundler-precheck: $(TEST_RUNNABLE)-test-bundler-precheck
no-test-bundler-precheck:
yes-test-bundler-precheck: main $(arch)-fake.rb
+yes-test-bundler-parallel-precheck: yes-test-bundler-precheck
+test-bundler-prepare: $(TEST_RUNNABLE)-test-bundler-prepare
no-test-bundler-prepare: no-test-bundler-precheck
yes-test-bundler-prepare: yes-test-bundler-precheck
$(ACTIONS_GROUP)
@@ -1485,7 +1666,7 @@ yes-test-bundler-prepare: yes-test-bundler-precheck
RSPECOPTS =
BUNDLER_SPECS =
-PREPARE_BUNDLER = yes-test-bundler-prepare
+PREPARE_BUNDLER = $(TEST_RUNNABLE)-test-bundler-prepare
test-bundler: $(TEST_RUNNABLE)-test-bundler
yes-test-bundler: $(PREPARE_BUNDLER)
$(gnumake_recursive)$(XRUBY) \
@@ -1657,6 +1838,16 @@ $(UNICODE_HDR_DIR)/name2ctype.h:
$(UNICODE_SRC_DATA_DIR) $(UNICODE_SRC_EMOJI_DATA_DIR) > $@.new
$(MV) $@.new $@
+srcs-doc: $(srcdir)/doc/regexp/unicode_properties.rdoc
+$(srcdir)/doc/regexp/$(ALWAYS_UPDATE_UNICODE:yes=unicode_properties.rdoc): \
+ $(UNICODE_HDR_DIR)/name2ctype.h $(UNICODE_PROPERTY_FILES)
+
+$(srcdir)/doc/regexp/unicode_properties.rdoc:
+ $(Q) $(BOOTSTRAPRUBY) $(tooldir)/generic_erb.rb -c -o $@ \
+ $(srcdir)/template/unicode_properties.rdoc.tmpl \
+ $(UNICODE_SRC_DATA_DIR) $(UNICODE_HDR_DIR)/name2ctype.h || \
+ $(TOUCH) $@
+
# the next non-comment line was:
# $(UNICODE_HDR_DIR)/casefold.h: $(tooldir)/enc-case-folding.rb \
# but was changed to make sure CI works on systems that don't have gperf
@@ -1722,7 +1913,7 @@ update-man-date: PHONY
ChangeLog:
$(ECHO) Generating $@
-$(Q) $(BASERUBY) -I"$(tooldir)/lib" -rvcs \
- -e 'VCS.detect(ARGV[0]).export_changelog("@", nil, nil, ARGV[1])' \
+ -e 'VCS.detect(ARGV[0]).export_changelog(path: ARGV[1])' \
"$(srcdir)" $@
HELP_EXTRA_TASKS = ""
@@ -1768,7 +1959,7 @@ help: PHONY
" golf: build goruby for golfers" \
$(HELP_EXTRA_TASKS) \
"see DeveloperHowto for more detail: " \
- " https://bugs.ruby-lang.org/projects/ruby/wiki/DeveloperHowto" \
+ " https://github.com/ruby/ruby/wiki/Developer-How-To" \
$(MESSAGE_END)
$(CROSS_COMPILING:yes=)builtin.$(OBJEXT): {$(VPATH)}mini_builtin.c
@@ -1851,6 +2042,7 @@ array.$(OBJEXT): $(top_srcdir)/internal/numeric.h
array.$(OBJEXT): $(top_srcdir)/internal/object.h
array.$(OBJEXT): $(top_srcdir)/internal/proc.h
array.$(OBJEXT): $(top_srcdir)/internal/rational.h
+array.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
array.$(OBJEXT): $(top_srcdir)/internal/serial.h
array.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
array.$(OBJEXT): $(top_srcdir)/internal/variable.h
@@ -2021,6 +2213,7 @@ array.$(OBJEXT): {$(VPATH)}internal/special_consts.h
array.$(OBJEXT): {$(VPATH)}internal/static_assert.h
array.$(OBJEXT): {$(VPATH)}internal/stdalign.h
array.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+array.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
array.$(OBJEXT): {$(VPATH)}internal/symbol.h
array.$(OBJEXT): {$(VPATH)}internal/value.h
array.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -2036,12 +2229,12 @@ array.$(OBJEXT): {$(VPATH)}probes.dmyh
array.$(OBJEXT): {$(VPATH)}probes.h
array.$(OBJEXT): {$(VPATH)}ruby_assert.h
array.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+array.$(OBJEXT): {$(VPATH)}rubyparser.h
array.$(OBJEXT): {$(VPATH)}shape.h
array.$(OBJEXT): {$(VPATH)}st.h
array.$(OBJEXT): {$(VPATH)}subst.h
array.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
array.$(OBJEXT): {$(VPATH)}thread_native.h
-array.$(OBJEXT): {$(VPATH)}transient_heap.h
array.$(OBJEXT): {$(VPATH)}util.h
array.$(OBJEXT): {$(VPATH)}vm_core.h
array.$(OBJEXT): {$(VPATH)}vm_opts.h
@@ -2053,16 +2246,45 @@ ast.$(OBJEXT): $(hdrdir)/ruby.h
ast.$(OBJEXT): $(hdrdir)/ruby/ruby.h
ast.$(OBJEXT): $(top_srcdir)/internal/array.h
ast.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
+ast.$(OBJEXT): $(top_srcdir)/internal/bignum.h
+ast.$(OBJEXT): $(top_srcdir)/internal/bits.h
ast.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+ast.$(OBJEXT): $(top_srcdir)/internal/complex.h
+ast.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
ast.$(OBJEXT): $(top_srcdir)/internal/gc.h
ast.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+ast.$(OBJEXT): $(top_srcdir)/internal/numeric.h
ast.$(OBJEXT): $(top_srcdir)/internal/parse.h
+ast.$(OBJEXT): $(top_srcdir)/internal/rational.h
+ast.$(OBJEXT): $(top_srcdir)/internal/ruby_parser.h
+ast.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
ast.$(OBJEXT): $(top_srcdir)/internal/serial.h
ast.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
ast.$(OBJEXT): $(top_srcdir)/internal/symbol.h
ast.$(OBJEXT): $(top_srcdir)/internal/variable.h
ast.$(OBJEXT): $(top_srcdir)/internal/vm.h
ast.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+ast.$(OBJEXT): $(top_srcdir)/prism/defines.h
+ast.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+ast.$(OBJEXT): $(top_srcdir)/prism/node.h
+ast.$(OBJEXT): $(top_srcdir)/prism/options.h
+ast.$(OBJEXT): $(top_srcdir)/prism/pack.h
+ast.$(OBJEXT): $(top_srcdir)/prism/parser.h
+ast.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+ast.$(OBJEXT): $(top_srcdir)/prism/prism.h
+ast.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+ast.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+ast.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+ast.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+ast.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+ast.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+ast.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+ast.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+ast.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+ast.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+ast.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+ast.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+ast.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
ast.$(OBJEXT): {$(VPATH)}assert.h
ast.$(OBJEXT): {$(VPATH)}ast.c
ast.$(OBJEXT): {$(VPATH)}ast.rbinc
@@ -2227,6 +2449,7 @@ ast.$(OBJEXT): {$(VPATH)}internal/special_consts.h
ast.$(OBJEXT): {$(VPATH)}internal/static_assert.h
ast.$(OBJEXT): {$(VPATH)}internal/stdalign.h
ast.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+ast.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
ast.$(OBJEXT): {$(VPATH)}internal/symbol.h
ast.$(OBJEXT): {$(VPATH)}internal/value.h
ast.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -2239,8 +2462,13 @@ ast.$(OBJEXT): {$(VPATH)}missing.h
ast.$(OBJEXT): {$(VPATH)}node.h
ast.$(OBJEXT): {$(VPATH)}onigmo.h
ast.$(OBJEXT): {$(VPATH)}oniguruma.h
+ast.$(OBJEXT): {$(VPATH)}prism/ast.h
+ast.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+ast.$(OBJEXT): {$(VPATH)}prism/version.h
+ast.$(OBJEXT): {$(VPATH)}prism_compile.h
ast.$(OBJEXT): {$(VPATH)}ruby_assert.h
ast.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+ast.$(OBJEXT): {$(VPATH)}rubyparser.h
ast.$(OBJEXT): {$(VPATH)}shape.h
ast.$(OBJEXT): {$(VPATH)}st.h
ast.$(OBJEXT): {$(VPATH)}subst.h
@@ -2287,6 +2515,7 @@ bignum.$(OBJEXT): {$(VPATH)}bignum.c
bignum.$(OBJEXT): {$(VPATH)}config.h
bignum.$(OBJEXT): {$(VPATH)}constant.h
bignum.$(OBJEXT): {$(VPATH)}defines.h
+bignum.$(OBJEXT): {$(VPATH)}encoding.h
bignum.$(OBJEXT): {$(VPATH)}id.h
bignum.$(OBJEXT): {$(VPATH)}id_table.h
bignum.$(OBJEXT): {$(VPATH)}intern.h
@@ -2362,6 +2591,15 @@ bignum.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
bignum.$(OBJEXT): {$(VPATH)}internal/ctype.h
bignum.$(OBJEXT): {$(VPATH)}internal/dllexport.h
bignum.$(OBJEXT): {$(VPATH)}internal/dosish.h
+bignum.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
+bignum.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
+bignum.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
+bignum.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
+bignum.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
+bignum.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
+bignum.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
+bignum.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
+bignum.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
bignum.$(OBJEXT): {$(VPATH)}internal/error.h
bignum.$(OBJEXT): {$(VPATH)}internal/eval.h
bignum.$(OBJEXT): {$(VPATH)}internal/event.h
@@ -2424,6 +2662,7 @@ bignum.$(OBJEXT): {$(VPATH)}internal/special_consts.h
bignum.$(OBJEXT): {$(VPATH)}internal/static_assert.h
bignum.$(OBJEXT): {$(VPATH)}internal/stdalign.h
bignum.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+bignum.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
bignum.$(OBJEXT): {$(VPATH)}internal/symbol.h
bignum.$(OBJEXT): {$(VPATH)}internal/value.h
bignum.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -2433,8 +2672,11 @@ bignum.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
bignum.$(OBJEXT): {$(VPATH)}method.h
bignum.$(OBJEXT): {$(VPATH)}missing.h
bignum.$(OBJEXT): {$(VPATH)}node.h
+bignum.$(OBJEXT): {$(VPATH)}onigmo.h
+bignum.$(OBJEXT): {$(VPATH)}oniguruma.h
bignum.$(OBJEXT): {$(VPATH)}ruby_assert.h
bignum.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+bignum.$(OBJEXT): {$(VPATH)}rubyparser.h
bignum.$(OBJEXT): {$(VPATH)}shape.h
bignum.$(OBJEXT): {$(VPATH)}st.h
bignum.$(OBJEXT): {$(VPATH)}subst.h
@@ -2454,11 +2696,33 @@ builtin.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
builtin.$(OBJEXT): $(top_srcdir)/internal/compilers.h
builtin.$(OBJEXT): $(top_srcdir)/internal/gc.h
builtin.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+builtin.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
builtin.$(OBJEXT): $(top_srcdir)/internal/serial.h
builtin.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
builtin.$(OBJEXT): $(top_srcdir)/internal/variable.h
builtin.$(OBJEXT): $(top_srcdir)/internal/vm.h
builtin.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+builtin.$(OBJEXT): $(top_srcdir)/prism/defines.h
+builtin.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+builtin.$(OBJEXT): $(top_srcdir)/prism/node.h
+builtin.$(OBJEXT): $(top_srcdir)/prism/options.h
+builtin.$(OBJEXT): $(top_srcdir)/prism/pack.h
+builtin.$(OBJEXT): $(top_srcdir)/prism/parser.h
+builtin.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+builtin.$(OBJEXT): $(top_srcdir)/prism/prism.h
+builtin.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+builtin.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+builtin.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+builtin.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+builtin.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+builtin.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+builtin.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+builtin.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+builtin.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+builtin.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+builtin.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+builtin.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+builtin.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
builtin.$(OBJEXT): {$(VPATH)}assert.h
builtin.$(OBJEXT): {$(VPATH)}atomic.h
builtin.$(OBJEXT): {$(VPATH)}backward/2/assume.h
@@ -2476,6 +2740,7 @@ builtin.$(OBJEXT): {$(VPATH)}builtin_binary.inc
builtin.$(OBJEXT): {$(VPATH)}config.h
builtin.$(OBJEXT): {$(VPATH)}constant.h
builtin.$(OBJEXT): {$(VPATH)}defines.h
+builtin.$(OBJEXT): {$(VPATH)}encoding.h
builtin.$(OBJEXT): {$(VPATH)}id.h
builtin.$(OBJEXT): {$(VPATH)}id_table.h
builtin.$(OBJEXT): {$(VPATH)}intern.h
@@ -2551,6 +2816,15 @@ builtin.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
builtin.$(OBJEXT): {$(VPATH)}internal/ctype.h
builtin.$(OBJEXT): {$(VPATH)}internal/dllexport.h
builtin.$(OBJEXT): {$(VPATH)}internal/dosish.h
+builtin.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
+builtin.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
+builtin.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
+builtin.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
+builtin.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
+builtin.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
+builtin.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
+builtin.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
+builtin.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
builtin.$(OBJEXT): {$(VPATH)}internal/error.h
builtin.$(OBJEXT): {$(VPATH)}internal/eval.h
builtin.$(OBJEXT): {$(VPATH)}internal/event.h
@@ -2613,6 +2887,7 @@ builtin.$(OBJEXT): {$(VPATH)}internal/special_consts.h
builtin.$(OBJEXT): {$(VPATH)}internal/static_assert.h
builtin.$(OBJEXT): {$(VPATH)}internal/stdalign.h
builtin.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+builtin.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
builtin.$(OBJEXT): {$(VPATH)}internal/symbol.h
builtin.$(OBJEXT): {$(VPATH)}internal/value.h
builtin.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -2623,8 +2898,15 @@ builtin.$(OBJEXT): {$(VPATH)}iseq.h
builtin.$(OBJEXT): {$(VPATH)}method.h
builtin.$(OBJEXT): {$(VPATH)}missing.h
builtin.$(OBJEXT): {$(VPATH)}node.h
+builtin.$(OBJEXT): {$(VPATH)}onigmo.h
+builtin.$(OBJEXT): {$(VPATH)}oniguruma.h
+builtin.$(OBJEXT): {$(VPATH)}prism/ast.h
+builtin.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+builtin.$(OBJEXT): {$(VPATH)}prism/version.h
+builtin.$(OBJEXT): {$(VPATH)}prism_compile.h
builtin.$(OBJEXT): {$(VPATH)}ruby_assert.h
builtin.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+builtin.$(OBJEXT): {$(VPATH)}rubyparser.h
builtin.$(OBJEXT): {$(VPATH)}shape.h
builtin.$(OBJEXT): {$(VPATH)}st.h
builtin.$(OBJEXT): {$(VPATH)}subst.h
@@ -2646,6 +2928,7 @@ class.$(OBJEXT): $(top_srcdir)/internal/gc.h
class.$(OBJEXT): $(top_srcdir)/internal/hash.h
class.$(OBJEXT): $(top_srcdir)/internal/imemo.h
class.$(OBJEXT): $(top_srcdir)/internal/object.h
+class.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
class.$(OBJEXT): $(top_srcdir)/internal/serial.h
class.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
class.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -2815,6 +3098,7 @@ class.$(OBJEXT): {$(VPATH)}internal/special_consts.h
class.$(OBJEXT): {$(VPATH)}internal/static_assert.h
class.$(OBJEXT): {$(VPATH)}internal/stdalign.h
class.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+class.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
class.$(OBJEXT): {$(VPATH)}internal/symbol.h
class.$(OBJEXT): {$(VPATH)}internal/value.h
class.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -2828,14 +3112,19 @@ class.$(OBJEXT): {$(VPATH)}onigmo.h
class.$(OBJEXT): {$(VPATH)}oniguruma.h
class.$(OBJEXT): {$(VPATH)}ruby_assert.h
class.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+class.$(OBJEXT): {$(VPATH)}rubyparser.h
class.$(OBJEXT): {$(VPATH)}shape.h
class.$(OBJEXT): {$(VPATH)}st.h
class.$(OBJEXT): {$(VPATH)}subst.h
class.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
class.$(OBJEXT): {$(VPATH)}thread_native.h
class.$(OBJEXT): {$(VPATH)}vm_core.h
+class.$(OBJEXT): {$(VPATH)}vm_debug.h
class.$(OBJEXT): {$(VPATH)}vm_opts.h
+class.$(OBJEXT): {$(VPATH)}vm_sync.h
+class.$(OBJEXT): {$(VPATH)}yjit.h
compar.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+compar.$(OBJEXT): $(hdrdir)/ruby/version.h
compar.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
compar.$(OBJEXT): $(top_srcdir)/internal/compar.h
compar.$(OBJEXT): $(top_srcdir)/internal/compilers.h
@@ -3003,6 +3292,7 @@ compar.$(OBJEXT): {$(VPATH)}internal/special_consts.h
compar.$(OBJEXT): {$(VPATH)}internal/static_assert.h
compar.$(OBJEXT): {$(VPATH)}internal/stdalign.h
compar.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+compar.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
compar.$(OBJEXT): {$(VPATH)}internal/symbol.h
compar.$(OBJEXT): {$(VPATH)}internal/value.h
compar.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -3019,6 +3309,7 @@ compile.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
compile.$(OBJEXT): $(CCAN_DIR)/list/list.h
compile.$(OBJEXT): $(CCAN_DIR)/str/str.h
compile.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+compile.$(OBJEXT): $(hdrdir)/ruby/version.h
compile.$(OBJEXT): $(top_srcdir)/internal/array.h
compile.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
compile.$(OBJEXT): $(top_srcdir)/internal/bignum.h
@@ -3033,10 +3324,14 @@ compile.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
compile.$(OBJEXT): $(top_srcdir)/internal/gc.h
compile.$(OBJEXT): $(top_srcdir)/internal/hash.h
compile.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+compile.$(OBJEXT): $(top_srcdir)/internal/io.h
compile.$(OBJEXT): $(top_srcdir)/internal/numeric.h
compile.$(OBJEXT): $(top_srcdir)/internal/object.h
+compile.$(OBJEXT): $(top_srcdir)/internal/parse.h
compile.$(OBJEXT): $(top_srcdir)/internal/rational.h
compile.$(OBJEXT): $(top_srcdir)/internal/re.h
+compile.$(OBJEXT): $(top_srcdir)/internal/ruby_parser.h
+compile.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
compile.$(OBJEXT): $(top_srcdir)/internal/serial.h
compile.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
compile.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -3045,6 +3340,28 @@ compile.$(OBJEXT): $(top_srcdir)/internal/thread.h
compile.$(OBJEXT): $(top_srcdir)/internal/variable.h
compile.$(OBJEXT): $(top_srcdir)/internal/vm.h
compile.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+compile.$(OBJEXT): $(top_srcdir)/prism/defines.h
+compile.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+compile.$(OBJEXT): $(top_srcdir)/prism/node.h
+compile.$(OBJEXT): $(top_srcdir)/prism/options.h
+compile.$(OBJEXT): $(top_srcdir)/prism/pack.h
+compile.$(OBJEXT): $(top_srcdir)/prism/parser.h
+compile.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+compile.$(OBJEXT): $(top_srcdir)/prism/prism.h
+compile.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+compile.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+compile.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+compile.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+compile.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+compile.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+compile.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+compile.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+compile.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+compile.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+compile.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+compile.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+compile.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
+compile.$(OBJEXT): $(top_srcdir)/prism_compile.c
compile.$(OBJEXT): {$(VPATH)}assert.h
compile.$(OBJEXT): {$(VPATH)}atomic.h
compile.$(OBJEXT): {$(VPATH)}backward/2/assume.h
@@ -3214,12 +3531,14 @@ compile.$(OBJEXT): {$(VPATH)}internal/special_consts.h
compile.$(OBJEXT): {$(VPATH)}internal/static_assert.h
compile.$(OBJEXT): {$(VPATH)}internal/stdalign.h
compile.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+compile.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
compile.$(OBJEXT): {$(VPATH)}internal/symbol.h
compile.$(OBJEXT): {$(VPATH)}internal/value.h
compile.$(OBJEXT): {$(VPATH)}internal/value_type.h
compile.$(OBJEXT): {$(VPATH)}internal/variable.h
compile.$(OBJEXT): {$(VPATH)}internal/warning_push.h
compile.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+compile.$(OBJEXT): {$(VPATH)}io.h
compile.$(OBJEXT): {$(VPATH)}iseq.h
compile.$(OBJEXT): {$(VPATH)}method.h
compile.$(OBJEXT): {$(VPATH)}missing.h
@@ -3227,10 +3546,18 @@ compile.$(OBJEXT): {$(VPATH)}node.h
compile.$(OBJEXT): {$(VPATH)}onigmo.h
compile.$(OBJEXT): {$(VPATH)}oniguruma.h
compile.$(OBJEXT): {$(VPATH)}optinsn.inc
+compile.$(OBJEXT): {$(VPATH)}prism/ast.h
+compile.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+compile.$(OBJEXT): {$(VPATH)}prism/prism.h
+compile.$(OBJEXT): {$(VPATH)}prism/version.h
+compile.$(OBJEXT): {$(VPATH)}prism_compile.c
+compile.$(OBJEXT): {$(VPATH)}prism_compile.h
+compile.$(OBJEXT): {$(VPATH)}ractor.h
compile.$(OBJEXT): {$(VPATH)}re.h
compile.$(OBJEXT): {$(VPATH)}regex.h
compile.$(OBJEXT): {$(VPATH)}ruby_assert.h
compile.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+compile.$(OBJEXT): {$(VPATH)}rubyparser.h
compile.$(OBJEXT): {$(VPATH)}shape.h
compile.$(OBJEXT): {$(VPATH)}st.h
compile.$(OBJEXT): {$(VPATH)}subst.h
@@ -3241,6 +3568,8 @@ compile.$(OBJEXT): {$(VPATH)}vm_callinfo.h
compile.$(OBJEXT): {$(VPATH)}vm_core.h
compile.$(OBJEXT): {$(VPATH)}vm_debug.h
compile.$(OBJEXT): {$(VPATH)}vm_opts.h
+compile.$(OBJEXT): {$(VPATH)}vm_sync.h
+compile.$(OBJEXT): {$(VPATH)}yjit.h
complex.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
complex.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
complex.$(OBJEXT): $(CCAN_DIR)/list/list.h
@@ -3260,8 +3589,10 @@ complex.$(OBJEXT): $(top_srcdir)/internal/math.h
complex.$(OBJEXT): $(top_srcdir)/internal/numeric.h
complex.$(OBJEXT): $(top_srcdir)/internal/object.h
complex.$(OBJEXT): $(top_srcdir)/internal/rational.h
+complex.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
complex.$(OBJEXT): $(top_srcdir)/internal/serial.h
complex.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+complex.$(OBJEXT): $(top_srcdir)/internal/string.h
complex.$(OBJEXT): $(top_srcdir)/internal/variable.h
complex.$(OBJEXT): $(top_srcdir)/internal/vm.h
complex.$(OBJEXT): $(top_srcdir)/internal/warnings.h
@@ -3279,7 +3610,9 @@ complex.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
complex.$(OBJEXT): {$(VPATH)}complex.c
complex.$(OBJEXT): {$(VPATH)}config.h
complex.$(OBJEXT): {$(VPATH)}constant.h
+complex.$(OBJEXT): {$(VPATH)}debug_counter.h
complex.$(OBJEXT): {$(VPATH)}defines.h
+complex.$(OBJEXT): {$(VPATH)}encoding.h
complex.$(OBJEXT): {$(VPATH)}id.h
complex.$(OBJEXT): {$(VPATH)}id_table.h
complex.$(OBJEXT): {$(VPATH)}intern.h
@@ -3355,6 +3688,15 @@ complex.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
complex.$(OBJEXT): {$(VPATH)}internal/ctype.h
complex.$(OBJEXT): {$(VPATH)}internal/dllexport.h
complex.$(OBJEXT): {$(VPATH)}internal/dosish.h
+complex.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
+complex.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
+complex.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
+complex.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
+complex.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
+complex.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
+complex.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
+complex.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
+complex.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
complex.$(OBJEXT): {$(VPATH)}internal/error.h
complex.$(OBJEXT): {$(VPATH)}internal/eval.h
complex.$(OBJEXT): {$(VPATH)}internal/event.h
@@ -3417,6 +3759,7 @@ complex.$(OBJEXT): {$(VPATH)}internal/special_consts.h
complex.$(OBJEXT): {$(VPATH)}internal/static_assert.h
complex.$(OBJEXT): {$(VPATH)}internal/stdalign.h
complex.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+complex.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
complex.$(OBJEXT): {$(VPATH)}internal/symbol.h
complex.$(OBJEXT): {$(VPATH)}internal/value.h
complex.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -3426,21 +3769,27 @@ complex.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
complex.$(OBJEXT): {$(VPATH)}method.h
complex.$(OBJEXT): {$(VPATH)}missing.h
complex.$(OBJEXT): {$(VPATH)}node.h
+complex.$(OBJEXT): {$(VPATH)}onigmo.h
+complex.$(OBJEXT): {$(VPATH)}oniguruma.h
complex.$(OBJEXT): {$(VPATH)}ruby_assert.h
complex.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+complex.$(OBJEXT): {$(VPATH)}rubyparser.h
complex.$(OBJEXT): {$(VPATH)}shape.h
complex.$(OBJEXT): {$(VPATH)}st.h
complex.$(OBJEXT): {$(VPATH)}subst.h
complex.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
complex.$(OBJEXT): {$(VPATH)}thread_native.h
complex.$(OBJEXT): {$(VPATH)}vm_core.h
+complex.$(OBJEXT): {$(VPATH)}vm_debug.h
complex.$(OBJEXT): {$(VPATH)}vm_opts.h
+complex.$(OBJEXT): {$(VPATH)}vm_sync.h
cont.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
cont.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
cont.$(OBJEXT): $(CCAN_DIR)/list/list.h
cont.$(OBJEXT): $(CCAN_DIR)/str/str.h
cont.$(OBJEXT): $(hdrdir)/ruby.h
cont.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+cont.$(OBJEXT): $(hdrdir)/ruby/version.h
cont.$(OBJEXT): $(top_srcdir)/internal/array.h
cont.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
cont.$(OBJEXT): $(top_srcdir)/internal/compilers.h
@@ -3457,6 +3806,27 @@ cont.$(OBJEXT): $(top_srcdir)/internal/thread.h
cont.$(OBJEXT): $(top_srcdir)/internal/variable.h
cont.$(OBJEXT): $(top_srcdir)/internal/vm.h
cont.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+cont.$(OBJEXT): $(top_srcdir)/prism/defines.h
+cont.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+cont.$(OBJEXT): $(top_srcdir)/prism/node.h
+cont.$(OBJEXT): $(top_srcdir)/prism/options.h
+cont.$(OBJEXT): $(top_srcdir)/prism/pack.h
+cont.$(OBJEXT): $(top_srcdir)/prism/parser.h
+cont.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+cont.$(OBJEXT): $(top_srcdir)/prism/prism.h
+cont.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+cont.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+cont.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+cont.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+cont.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+cont.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+cont.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+cont.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+cont.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+cont.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+cont.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+cont.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+cont.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
cont.$(OBJEXT): {$(VPATH)}$(COROUTINE_H)
cont.$(OBJEXT): {$(VPATH)}assert.h
cont.$(OBJEXT): {$(VPATH)}atomic.h
@@ -3623,6 +3993,7 @@ cont.$(OBJEXT): {$(VPATH)}internal/special_consts.h
cont.$(OBJEXT): {$(VPATH)}internal/static_assert.h
cont.$(OBJEXT): {$(VPATH)}internal/stdalign.h
cont.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+cont.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
cont.$(OBJEXT): {$(VPATH)}internal/symbol.h
cont.$(OBJEXT): {$(VPATH)}internal/value.h
cont.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -3635,11 +4006,16 @@ cont.$(OBJEXT): {$(VPATH)}missing.h
cont.$(OBJEXT): {$(VPATH)}node.h
cont.$(OBJEXT): {$(VPATH)}onigmo.h
cont.$(OBJEXT): {$(VPATH)}oniguruma.h
+cont.$(OBJEXT): {$(VPATH)}prism/ast.h
+cont.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+cont.$(OBJEXT): {$(VPATH)}prism/version.h
+cont.$(OBJEXT): {$(VPATH)}prism_compile.h
cont.$(OBJEXT): {$(VPATH)}ractor.h
cont.$(OBJEXT): {$(VPATH)}ractor_core.h
cont.$(OBJEXT): {$(VPATH)}rjit.h
cont.$(OBJEXT): {$(VPATH)}ruby_assert.h
cont.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+cont.$(OBJEXT): {$(VPATH)}rubyparser.h
cont.$(OBJEXT): {$(VPATH)}shape.h
cont.$(OBJEXT): {$(VPATH)}st.h
cont.$(OBJEXT): {$(VPATH)}subst.h
@@ -3661,6 +4037,7 @@ debug.$(OBJEXT): $(top_srcdir)/internal/class.h
debug.$(OBJEXT): $(top_srcdir)/internal/compilers.h
debug.$(OBJEXT): $(top_srcdir)/internal/gc.h
debug.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+debug.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
debug.$(OBJEXT): $(top_srcdir)/internal/serial.h
debug.$(OBJEXT): $(top_srcdir)/internal/signal.h
debug.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
@@ -3832,6 +4209,7 @@ debug.$(OBJEXT): {$(VPATH)}internal/special_consts.h
debug.$(OBJEXT): {$(VPATH)}internal/static_assert.h
debug.$(OBJEXT): {$(VPATH)}internal/stdalign.h
debug.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+debug.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
debug.$(OBJEXT): {$(VPATH)}internal/symbol.h
debug.$(OBJEXT): {$(VPATH)}internal/value.h
debug.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -3848,6 +4226,7 @@ debug.$(OBJEXT): {$(VPATH)}ractor.h
debug.$(OBJEXT): {$(VPATH)}ractor_core.h
debug.$(OBJEXT): {$(VPATH)}ruby_assert.h
debug.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+debug.$(OBJEXT): {$(VPATH)}rubyparser.h
debug.$(OBJEXT): {$(VPATH)}shape.h
debug.$(OBJEXT): {$(VPATH)}st.h
debug.$(OBJEXT): {$(VPATH)}subst.h
@@ -3859,6 +4238,7 @@ debug.$(OBJEXT): {$(VPATH)}vm_callinfo.h
debug.$(OBJEXT): {$(VPATH)}vm_core.h
debug.$(OBJEXT): {$(VPATH)}vm_debug.h
debug.$(OBJEXT): {$(VPATH)}vm_opts.h
+debug.$(OBJEXT): {$(VPATH)}vm_sync.h
debug_counter.$(OBJEXT): $(hdrdir)/ruby/ruby.h
debug_counter.$(OBJEXT): {$(VPATH)}assert.h
debug_counter.$(OBJEXT): {$(VPATH)}backward/2/assume.h
@@ -4008,6 +4388,7 @@ debug_counter.$(OBJEXT): {$(VPATH)}internal/special_consts.h
debug_counter.$(OBJEXT): {$(VPATH)}internal/static_assert.h
debug_counter.$(OBJEXT): {$(VPATH)}internal/stdalign.h
debug_counter.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+debug_counter.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
debug_counter.$(OBJEXT): {$(VPATH)}internal/symbol.h
debug_counter.$(OBJEXT): {$(VPATH)}internal/value.h
debug_counter.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -4023,6 +4404,7 @@ dir.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
dir.$(OBJEXT): $(CCAN_DIR)/list/list.h
dir.$(OBJEXT): $(CCAN_DIR)/str/str.h
dir.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+dir.$(OBJEXT): $(hdrdir)/ruby/version.h
dir.$(OBJEXT): $(top_srcdir)/internal/array.h
dir.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
dir.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -4035,6 +4417,7 @@ dir.$(OBJEXT): $(top_srcdir)/internal/gc.h
dir.$(OBJEXT): $(top_srcdir)/internal/imemo.h
dir.$(OBJEXT): $(top_srcdir)/internal/io.h
dir.$(OBJEXT): $(top_srcdir)/internal/object.h
+dir.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
dir.$(OBJEXT): $(top_srcdir)/internal/serial.h
dir.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
dir.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -4206,6 +4589,7 @@ dir.$(OBJEXT): {$(VPATH)}internal/special_consts.h
dir.$(OBJEXT): {$(VPATH)}internal/static_assert.h
dir.$(OBJEXT): {$(VPATH)}internal/stdalign.h
dir.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+dir.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
dir.$(OBJEXT): {$(VPATH)}internal/symbol.h
dir.$(OBJEXT): {$(VPATH)}internal/value.h
dir.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -4220,6 +4604,7 @@ dir.$(OBJEXT): {$(VPATH)}onigmo.h
dir.$(OBJEXT): {$(VPATH)}oniguruma.h
dir.$(OBJEXT): {$(VPATH)}ruby_assert.h
dir.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+dir.$(OBJEXT): {$(VPATH)}rubyparser.h
dir.$(OBJEXT): {$(VPATH)}shape.h
dir.$(OBJEXT): {$(VPATH)}st.h
dir.$(OBJEXT): {$(VPATH)}subst.h
@@ -4381,6 +4766,7 @@ dln.$(OBJEXT): {$(VPATH)}internal/special_consts.h
dln.$(OBJEXT): {$(VPATH)}internal/static_assert.h
dln.$(OBJEXT): {$(VPATH)}internal/stdalign.h
dln.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+dln.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
dln.$(OBJEXT): {$(VPATH)}internal/symbol.h
dln.$(OBJEXT): {$(VPATH)}internal/value.h
dln.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -4538,6 +4924,7 @@ dln_find.$(OBJEXT): {$(VPATH)}internal/special_consts.h
dln_find.$(OBJEXT): {$(VPATH)}internal/static_assert.h
dln_find.$(OBJEXT): {$(VPATH)}internal/stdalign.h
dln_find.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+dln_find.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
dln_find.$(OBJEXT): {$(VPATH)}internal/symbol.h
dln_find.$(OBJEXT): {$(VPATH)}internal/value.h
dln_find.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -4694,6 +5081,7 @@ dmydln.$(OBJEXT): {$(VPATH)}internal/special_consts.h
dmydln.$(OBJEXT): {$(VPATH)}internal/static_assert.h
dmydln.$(OBJEXT): {$(VPATH)}internal/stdalign.h
dmydln.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+dmydln.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
dmydln.$(OBJEXT): {$(VPATH)}internal/symbol.h
dmydln.$(OBJEXT): {$(VPATH)}internal/value.h
dmydln.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -5523,14 +5911,22 @@ enc/utf_8.$(OBJEXT): {$(VPATH)}oniguruma.h
enc/utf_8.$(OBJEXT): {$(VPATH)}regenc.h
enc/utf_8.$(OBJEXT): {$(VPATH)}st.h
enc/utf_8.$(OBJEXT): {$(VPATH)}subst.h
+encoding.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
+encoding.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
+encoding.$(OBJEXT): $(CCAN_DIR)/list/list.h
+encoding.$(OBJEXT): $(CCAN_DIR)/str/str.h
encoding.$(OBJEXT): $(hdrdir)/ruby.h
encoding.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+encoding.$(OBJEXT): $(hdrdir)/ruby/version.h
+encoding.$(OBJEXT): $(top_srcdir)/internal/array.h
+encoding.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
encoding.$(OBJEXT): $(top_srcdir)/internal/class.h
encoding.$(OBJEXT): $(top_srcdir)/internal/compilers.h
encoding.$(OBJEXT): $(top_srcdir)/internal/enc.h
encoding.$(OBJEXT): $(top_srcdir)/internal/encoding.h
encoding.$(OBJEXT): $(top_srcdir)/internal/error.h
encoding.$(OBJEXT): $(top_srcdir)/internal/gc.h
+encoding.$(OBJEXT): $(top_srcdir)/internal/imemo.h
encoding.$(OBJEXT): $(top_srcdir)/internal/inits.h
encoding.$(OBJEXT): $(top_srcdir)/internal/load.h
encoding.$(OBJEXT): $(top_srcdir)/internal/object.h
@@ -5541,6 +5937,7 @@ encoding.$(OBJEXT): $(top_srcdir)/internal/variable.h
encoding.$(OBJEXT): $(top_srcdir)/internal/vm.h
encoding.$(OBJEXT): $(top_srcdir)/internal/warnings.h
encoding.$(OBJEXT): {$(VPATH)}assert.h
+encoding.$(OBJEXT): {$(VPATH)}atomic.h
encoding.$(OBJEXT): {$(VPATH)}backward/2/assume.h
encoding.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
encoding.$(OBJEXT): {$(VPATH)}backward/2/bool.h
@@ -5557,6 +5954,7 @@ encoding.$(OBJEXT): {$(VPATH)}defines.h
encoding.$(OBJEXT): {$(VPATH)}encindex.h
encoding.$(OBJEXT): {$(VPATH)}encoding.c
encoding.$(OBJEXT): {$(VPATH)}encoding.h
+encoding.$(OBJEXT): {$(VPATH)}id.h
encoding.$(OBJEXT): {$(VPATH)}id_table.h
encoding.$(OBJEXT): {$(VPATH)}intern.h
encoding.$(OBJEXT): {$(VPATH)}internal.h
@@ -5702,22 +6100,31 @@ encoding.$(OBJEXT): {$(VPATH)}internal/special_consts.h
encoding.$(OBJEXT): {$(VPATH)}internal/static_assert.h
encoding.$(OBJEXT): {$(VPATH)}internal/stdalign.h
encoding.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+encoding.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
encoding.$(OBJEXT): {$(VPATH)}internal/symbol.h
encoding.$(OBJEXT): {$(VPATH)}internal/value.h
encoding.$(OBJEXT): {$(VPATH)}internal/value_type.h
encoding.$(OBJEXT): {$(VPATH)}internal/variable.h
encoding.$(OBJEXT): {$(VPATH)}internal/warning_push.h
encoding.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+encoding.$(OBJEXT): {$(VPATH)}method.h
encoding.$(OBJEXT): {$(VPATH)}missing.h
+encoding.$(OBJEXT): {$(VPATH)}node.h
encoding.$(OBJEXT): {$(VPATH)}onigmo.h
encoding.$(OBJEXT): {$(VPATH)}oniguruma.h
encoding.$(OBJEXT): {$(VPATH)}regenc.h
encoding.$(OBJEXT): {$(VPATH)}ruby_assert.h
+encoding.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+encoding.$(OBJEXT): {$(VPATH)}rubyparser.h
encoding.$(OBJEXT): {$(VPATH)}shape.h
encoding.$(OBJEXT): {$(VPATH)}st.h
encoding.$(OBJEXT): {$(VPATH)}subst.h
+encoding.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
+encoding.$(OBJEXT): {$(VPATH)}thread_native.h
encoding.$(OBJEXT): {$(VPATH)}util.h
+encoding.$(OBJEXT): {$(VPATH)}vm_core.h
encoding.$(OBJEXT): {$(VPATH)}vm_debug.h
+encoding.$(OBJEXT): {$(VPATH)}vm_opts.h
encoding.$(OBJEXT): {$(VPATH)}vm_sync.h
enum.$(OBJEXT): $(hdrdir)/ruby/ruby.h
enum.$(OBJEXT): $(top_srcdir)/internal/array.h
@@ -5903,6 +6310,7 @@ enum.$(OBJEXT): {$(VPATH)}internal/special_consts.h
enum.$(OBJEXT): {$(VPATH)}internal/static_assert.h
enum.$(OBJEXT): {$(VPATH)}internal/stdalign.h
enum.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+enum.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
enum.$(OBJEXT): {$(VPATH)}internal/symbol.h
enum.$(OBJEXT): {$(VPATH)}internal/value.h
enum.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -5923,6 +6331,7 @@ enumerator.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
enumerator.$(OBJEXT): $(CCAN_DIR)/list/list.h
enumerator.$(OBJEXT): $(CCAN_DIR)/str/str.h
enumerator.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+enumerator.$(OBJEXT): $(hdrdir)/ruby/version.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/array.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/bignum.h
@@ -5938,6 +6347,7 @@ enumerator.$(OBJEXT): $(top_srcdir)/internal/imemo.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/numeric.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/range.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/rational.h
+enumerator.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/serial.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
enumerator.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -5958,6 +6368,7 @@ enumerator.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
enumerator.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
enumerator.$(OBJEXT): {$(VPATH)}config.h
enumerator.$(OBJEXT): {$(VPATH)}constant.h
+enumerator.$(OBJEXT): {$(VPATH)}debug_counter.h
enumerator.$(OBJEXT): {$(VPATH)}defines.h
enumerator.$(OBJEXT): {$(VPATH)}encoding.h
enumerator.$(OBJEXT): {$(VPATH)}enumerator.c
@@ -6107,6 +6518,7 @@ enumerator.$(OBJEXT): {$(VPATH)}internal/special_consts.h
enumerator.$(OBJEXT): {$(VPATH)}internal/static_assert.h
enumerator.$(OBJEXT): {$(VPATH)}internal/stdalign.h
enumerator.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+enumerator.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
enumerator.$(OBJEXT): {$(VPATH)}internal/symbol.h
enumerator.$(OBJEXT): {$(VPATH)}internal/value.h
enumerator.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -6120,18 +6532,22 @@ enumerator.$(OBJEXT): {$(VPATH)}onigmo.h
enumerator.$(OBJEXT): {$(VPATH)}oniguruma.h
enumerator.$(OBJEXT): {$(VPATH)}ruby_assert.h
enumerator.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+enumerator.$(OBJEXT): {$(VPATH)}rubyparser.h
enumerator.$(OBJEXT): {$(VPATH)}shape.h
enumerator.$(OBJEXT): {$(VPATH)}st.h
enumerator.$(OBJEXT): {$(VPATH)}subst.h
enumerator.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
enumerator.$(OBJEXT): {$(VPATH)}thread_native.h
enumerator.$(OBJEXT): {$(VPATH)}vm_core.h
+enumerator.$(OBJEXT): {$(VPATH)}vm_debug.h
enumerator.$(OBJEXT): {$(VPATH)}vm_opts.h
+enumerator.$(OBJEXT): {$(VPATH)}vm_sync.h
error.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
error.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
error.$(OBJEXT): $(CCAN_DIR)/list/list.h
error.$(OBJEXT): $(CCAN_DIR)/str/str.h
error.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+error.$(OBJEXT): $(hdrdir)/ruby/version.h
error.$(OBJEXT): $(top_srcdir)/internal/array.h
error.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
error.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -6144,6 +6560,8 @@ error.$(OBJEXT): $(top_srcdir)/internal/imemo.h
error.$(OBJEXT): $(top_srcdir)/internal/io.h
error.$(OBJEXT): $(top_srcdir)/internal/load.h
error.$(OBJEXT): $(top_srcdir)/internal/object.h
+error.$(OBJEXT): $(top_srcdir)/internal/process.h
+error.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
error.$(OBJEXT): $(top_srcdir)/internal/serial.h
error.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
error.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -6166,6 +6584,7 @@ error.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
error.$(OBJEXT): {$(VPATH)}builtin.h
error.$(OBJEXT): {$(VPATH)}config.h
error.$(OBJEXT): {$(VPATH)}constant.h
+error.$(OBJEXT): {$(VPATH)}debug_counter.h
error.$(OBJEXT): {$(VPATH)}defines.h
error.$(OBJEXT): {$(VPATH)}encoding.h
error.$(OBJEXT): {$(VPATH)}error.c
@@ -6315,6 +6734,7 @@ error.$(OBJEXT): {$(VPATH)}internal/special_consts.h
error.$(OBJEXT): {$(VPATH)}internal/static_assert.h
error.$(OBJEXT): {$(VPATH)}internal/stdalign.h
error.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+error.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
error.$(OBJEXT): {$(VPATH)}internal/symbol.h
error.$(OBJEXT): {$(VPATH)}internal/value.h
error.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -6330,20 +6750,26 @@ error.$(OBJEXT): {$(VPATH)}onigmo.h
error.$(OBJEXT): {$(VPATH)}oniguruma.h
error.$(OBJEXT): {$(VPATH)}ruby_assert.h
error.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+error.$(OBJEXT): {$(VPATH)}rubyparser.h
error.$(OBJEXT): {$(VPATH)}shape.h
error.$(OBJEXT): {$(VPATH)}st.h
error.$(OBJEXT): {$(VPATH)}subst.h
error.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
error.$(OBJEXT): {$(VPATH)}thread_native.h
+error.$(OBJEXT): {$(VPATH)}util.h
error.$(OBJEXT): {$(VPATH)}vm_core.h
+error.$(OBJEXT): {$(VPATH)}vm_debug.h
error.$(OBJEXT): {$(VPATH)}vm_opts.h
+error.$(OBJEXT): {$(VPATH)}vm_sync.h
error.$(OBJEXT): {$(VPATH)}warning.rbinc
+error.$(OBJEXT): {$(VPATH)}yjit.h
eval.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
eval.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
eval.$(OBJEXT): $(CCAN_DIR)/list/list.h
eval.$(OBJEXT): $(CCAN_DIR)/str/str.h
eval.$(OBJEXT): $(hdrdir)/ruby.h
eval.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+eval.$(OBJEXT): $(hdrdir)/ruby/version.h
eval.$(OBJEXT): $(top_srcdir)/internal/array.h
eval.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
eval.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -6357,6 +6783,7 @@ eval.$(OBJEXT): $(top_srcdir)/internal/imemo.h
eval.$(OBJEXT): $(top_srcdir)/internal/inits.h
eval.$(OBJEXT): $(top_srcdir)/internal/io.h
eval.$(OBJEXT): $(top_srcdir)/internal/object.h
+eval.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
eval.$(OBJEXT): $(top_srcdir)/internal/serial.h
eval.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
eval.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -6364,6 +6791,27 @@ eval.$(OBJEXT): $(top_srcdir)/internal/thread.h
eval.$(OBJEXT): $(top_srcdir)/internal/variable.h
eval.$(OBJEXT): $(top_srcdir)/internal/vm.h
eval.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+eval.$(OBJEXT): $(top_srcdir)/prism/defines.h
+eval.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+eval.$(OBJEXT): $(top_srcdir)/prism/node.h
+eval.$(OBJEXT): $(top_srcdir)/prism/options.h
+eval.$(OBJEXT): $(top_srcdir)/prism/pack.h
+eval.$(OBJEXT): $(top_srcdir)/prism/parser.h
+eval.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+eval.$(OBJEXT): $(top_srcdir)/prism/prism.h
+eval.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+eval.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+eval.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+eval.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+eval.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+eval.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+eval.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+eval.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+eval.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+eval.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+eval.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+eval.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+eval.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
eval.$(OBJEXT): {$(VPATH)}assert.h
eval.$(OBJEXT): {$(VPATH)}atomic.h
eval.$(OBJEXT): {$(VPATH)}backward/2/assume.h
@@ -6531,6 +6979,7 @@ eval.$(OBJEXT): {$(VPATH)}internal/special_consts.h
eval.$(OBJEXT): {$(VPATH)}internal/static_assert.h
eval.$(OBJEXT): {$(VPATH)}internal/stdalign.h
eval.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+eval.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
eval.$(OBJEXT): {$(VPATH)}internal/symbol.h
eval.$(OBJEXT): {$(VPATH)}internal/value.h
eval.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -6544,6 +6993,10 @@ eval.$(OBJEXT): {$(VPATH)}missing.h
eval.$(OBJEXT): {$(VPATH)}node.h
eval.$(OBJEXT): {$(VPATH)}onigmo.h
eval.$(OBJEXT): {$(VPATH)}oniguruma.h
+eval.$(OBJEXT): {$(VPATH)}prism/ast.h
+eval.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+eval.$(OBJEXT): {$(VPATH)}prism/version.h
+eval.$(OBJEXT): {$(VPATH)}prism_compile.h
eval.$(OBJEXT): {$(VPATH)}probes.dmyh
eval.$(OBJEXT): {$(VPATH)}probes.h
eval.$(OBJEXT): {$(VPATH)}probes_helper.h
@@ -6552,6 +7005,7 @@ eval.$(OBJEXT): {$(VPATH)}ractor_core.h
eval.$(OBJEXT): {$(VPATH)}rjit.h
eval.$(OBJEXT): {$(VPATH)}ruby_assert.h
eval.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+eval.$(OBJEXT): {$(VPATH)}rubyparser.h
eval.$(OBJEXT): {$(VPATH)}shape.h
eval.$(OBJEXT): {$(VPATH)}st.h
eval.$(OBJEXT): {$(VPATH)}subst.h
@@ -6561,6 +7015,7 @@ eval.$(OBJEXT): {$(VPATH)}vm.h
eval.$(OBJEXT): {$(VPATH)}vm_core.h
eval.$(OBJEXT): {$(VPATH)}vm_debug.h
eval.$(OBJEXT): {$(VPATH)}vm_opts.h
+eval.$(OBJEXT): {$(VPATH)}vm_sync.h
explicit_bzero.$(OBJEXT): {$(VPATH)}config.h
explicit_bzero.$(OBJEXT): {$(VPATH)}explicit_bzero.c
explicit_bzero.$(OBJEXT): {$(VPATH)}internal/attr/format.h
@@ -6581,6 +7036,7 @@ file.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
file.$(OBJEXT): $(CCAN_DIR)/list/list.h
file.$(OBJEXT): $(CCAN_DIR)/str/str.h
file.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+file.$(OBJEXT): $(hdrdir)/ruby/version.h
file.$(OBJEXT): $(top_srcdir)/internal/array.h
file.$(OBJEXT): $(top_srcdir)/internal/class.h
file.$(OBJEXT): $(top_srcdir)/internal/compilers.h
@@ -6763,6 +7219,7 @@ file.$(OBJEXT): {$(VPATH)}internal/special_consts.h
file.$(OBJEXT): {$(VPATH)}internal/static_assert.h
file.$(OBJEXT): {$(VPATH)}internal/stdalign.h
file.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+file.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
file.$(OBJEXT): {$(VPATH)}internal/symbol.h
file.$(OBJEXT): {$(VPATH)}internal/value.h
file.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -6785,6 +7242,7 @@ gc.$(OBJEXT): $(CCAN_DIR)/list/list.h
gc.$(OBJEXT): $(CCAN_DIR)/str/str.h
gc.$(OBJEXT): $(hdrdir)/ruby.h
gc.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+gc.$(OBJEXT): $(hdrdir)/ruby/version.h
gc.$(OBJEXT): $(top_srcdir)/internal/array.h
gc.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
gc.$(OBJEXT): $(top_srcdir)/internal/bignum.h
@@ -6815,6 +7273,27 @@ gc.$(OBJEXT): $(top_srcdir)/internal/thread.h
gc.$(OBJEXT): $(top_srcdir)/internal/variable.h
gc.$(OBJEXT): $(top_srcdir)/internal/vm.h
gc.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+gc.$(OBJEXT): $(top_srcdir)/prism/defines.h
+gc.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+gc.$(OBJEXT): $(top_srcdir)/prism/node.h
+gc.$(OBJEXT): $(top_srcdir)/prism/options.h
+gc.$(OBJEXT): $(top_srcdir)/prism/pack.h
+gc.$(OBJEXT): $(top_srcdir)/prism/parser.h
+gc.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+gc.$(OBJEXT): $(top_srcdir)/prism/prism.h
+gc.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+gc.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+gc.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+gc.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+gc.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+gc.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+gc.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+gc.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+gc.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+gc.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+gc.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+gc.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+gc.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
gc.$(OBJEXT): {$(VPATH)}assert.h
gc.$(OBJEXT): {$(VPATH)}atomic.h
gc.$(OBJEXT): {$(VPATH)}backward/2/assume.h
@@ -6829,6 +7308,7 @@ gc.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
gc.$(OBJEXT): {$(VPATH)}builtin.h
gc.$(OBJEXT): {$(VPATH)}config.h
gc.$(OBJEXT): {$(VPATH)}constant.h
+gc.$(OBJEXT): {$(VPATH)}darray.h
gc.$(OBJEXT): {$(VPATH)}debug.h
gc.$(OBJEXT): {$(VPATH)}debug_counter.h
gc.$(OBJEXT): {$(VPATH)}defines.h
@@ -6983,6 +7463,7 @@ gc.$(OBJEXT): {$(VPATH)}internal/special_consts.h
gc.$(OBJEXT): {$(VPATH)}internal/static_assert.h
gc.$(OBJEXT): {$(VPATH)}internal/stdalign.h
gc.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+gc.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
gc.$(OBJEXT): {$(VPATH)}internal/symbol.h
gc.$(OBJEXT): {$(VPATH)}internal/value.h
gc.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -6996,6 +7477,10 @@ gc.$(OBJEXT): {$(VPATH)}missing.h
gc.$(OBJEXT): {$(VPATH)}node.h
gc.$(OBJEXT): {$(VPATH)}onigmo.h
gc.$(OBJEXT): {$(VPATH)}oniguruma.h
+gc.$(OBJEXT): {$(VPATH)}prism/ast.h
+gc.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+gc.$(OBJEXT): {$(VPATH)}prism/version.h
+gc.$(OBJEXT): {$(VPATH)}prism_compile.h
gc.$(OBJEXT): {$(VPATH)}probes.dmyh
gc.$(OBJEXT): {$(VPATH)}probes.h
gc.$(OBJEXT): {$(VPATH)}ractor.h
@@ -7007,6 +7492,7 @@ gc.$(OBJEXT): {$(VPATH)}regint.h
gc.$(OBJEXT): {$(VPATH)}rjit.h
gc.$(OBJEXT): {$(VPATH)}ruby_assert.h
gc.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+gc.$(OBJEXT): {$(VPATH)}rubyparser.h
gc.$(OBJEXT): {$(VPATH)}shape.h
gc.$(OBJEXT): {$(VPATH)}st.h
gc.$(OBJEXT): {$(VPATH)}subst.h
@@ -7014,7 +7500,6 @@ gc.$(OBJEXT): {$(VPATH)}symbol.h
gc.$(OBJEXT): {$(VPATH)}thread.h
gc.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
gc.$(OBJEXT): {$(VPATH)}thread_native.h
-gc.$(OBJEXT): {$(VPATH)}transient_heap.h
gc.$(OBJEXT): {$(VPATH)}util.h
gc.$(OBJEXT): {$(VPATH)}vm_callinfo.h
gc.$(OBJEXT): {$(VPATH)}vm_core.h
@@ -7029,14 +7514,44 @@ goruby.$(OBJEXT): $(hdrdir)/ruby.h
goruby.$(OBJEXT): $(hdrdir)/ruby/ruby.h
goruby.$(OBJEXT): $(top_srcdir)/internal/array.h
goruby.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
+goruby.$(OBJEXT): $(top_srcdir)/internal/bignum.h
+goruby.$(OBJEXT): $(top_srcdir)/internal/bits.h
goruby.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+goruby.$(OBJEXT): $(top_srcdir)/internal/complex.h
+goruby.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
goruby.$(OBJEXT): $(top_srcdir)/internal/gc.h
goruby.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+goruby.$(OBJEXT): $(top_srcdir)/internal/numeric.h
+goruby.$(OBJEXT): $(top_srcdir)/internal/parse.h
+goruby.$(OBJEXT): $(top_srcdir)/internal/rational.h
+goruby.$(OBJEXT): $(top_srcdir)/internal/ruby_parser.h
+goruby.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
goruby.$(OBJEXT): $(top_srcdir)/internal/serial.h
goruby.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
goruby.$(OBJEXT): $(top_srcdir)/internal/variable.h
goruby.$(OBJEXT): $(top_srcdir)/internal/vm.h
goruby.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+goruby.$(OBJEXT): $(top_srcdir)/prism/defines.h
+goruby.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+goruby.$(OBJEXT): $(top_srcdir)/prism/node.h
+goruby.$(OBJEXT): $(top_srcdir)/prism/options.h
+goruby.$(OBJEXT): $(top_srcdir)/prism/pack.h
+goruby.$(OBJEXT): $(top_srcdir)/prism/parser.h
+goruby.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+goruby.$(OBJEXT): $(top_srcdir)/prism/prism.h
+goruby.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+goruby.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+goruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+goruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+goruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+goruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+goruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+goruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+goruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+goruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+goruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+goruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+goruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
goruby.$(OBJEXT): {$(VPATH)}assert.h
goruby.$(OBJEXT): {$(VPATH)}atomic.h
goruby.$(OBJEXT): {$(VPATH)}backward.h
@@ -7052,6 +7567,7 @@ goruby.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
goruby.$(OBJEXT): {$(VPATH)}config.h
goruby.$(OBJEXT): {$(VPATH)}constant.h
goruby.$(OBJEXT): {$(VPATH)}defines.h
+goruby.$(OBJEXT): {$(VPATH)}encoding.h
goruby.$(OBJEXT): {$(VPATH)}golf_prelude.c
goruby.$(OBJEXT): {$(VPATH)}goruby.c
goruby.$(OBJEXT): {$(VPATH)}id.h
@@ -7129,6 +7645,15 @@ goruby.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
goruby.$(OBJEXT): {$(VPATH)}internal/ctype.h
goruby.$(OBJEXT): {$(VPATH)}internal/dllexport.h
goruby.$(OBJEXT): {$(VPATH)}internal/dosish.h
+goruby.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
+goruby.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
+goruby.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
+goruby.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
+goruby.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
+goruby.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
+goruby.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
+goruby.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
+goruby.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
goruby.$(OBJEXT): {$(VPATH)}internal/error.h
goruby.$(OBJEXT): {$(VPATH)}internal/eval.h
goruby.$(OBJEXT): {$(VPATH)}internal/event.h
@@ -7191,6 +7716,7 @@ goruby.$(OBJEXT): {$(VPATH)}internal/special_consts.h
goruby.$(OBJEXT): {$(VPATH)}internal/static_assert.h
goruby.$(OBJEXT): {$(VPATH)}internal/stdalign.h
goruby.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+goruby.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
goruby.$(OBJEXT): {$(VPATH)}internal/symbol.h
goruby.$(OBJEXT): {$(VPATH)}internal/value.h
goruby.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -7202,8 +7728,15 @@ goruby.$(OBJEXT): {$(VPATH)}main.c
goruby.$(OBJEXT): {$(VPATH)}method.h
goruby.$(OBJEXT): {$(VPATH)}missing.h
goruby.$(OBJEXT): {$(VPATH)}node.h
+goruby.$(OBJEXT): {$(VPATH)}onigmo.h
+goruby.$(OBJEXT): {$(VPATH)}oniguruma.h
+goruby.$(OBJEXT): {$(VPATH)}prism/ast.h
+goruby.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+goruby.$(OBJEXT): {$(VPATH)}prism/version.h
+goruby.$(OBJEXT): {$(VPATH)}prism_compile.h
goruby.$(OBJEXT): {$(VPATH)}ruby_assert.h
goruby.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+goruby.$(OBJEXT): {$(VPATH)}rubyparser.h
goruby.$(OBJEXT): {$(VPATH)}shape.h
goruby.$(OBJEXT): {$(VPATH)}st.h
goruby.$(OBJEXT): {$(VPATH)}subst.h
@@ -7217,6 +7750,7 @@ hash.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
hash.$(OBJEXT): $(CCAN_DIR)/list/list.h
hash.$(OBJEXT): $(CCAN_DIR)/str/str.h
hash.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+hash.$(OBJEXT): $(hdrdir)/ruby/version.h
hash.$(OBJEXT): $(top_srcdir)/internal/array.h
hash.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
hash.$(OBJEXT): $(top_srcdir)/internal/bignum.h
@@ -7230,7 +7764,9 @@ hash.$(OBJEXT): $(top_srcdir)/internal/hash.h
hash.$(OBJEXT): $(top_srcdir)/internal/imemo.h
hash.$(OBJEXT): $(top_srcdir)/internal/object.h
hash.$(OBJEXT): $(top_srcdir)/internal/proc.h
+hash.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
hash.$(OBJEXT): $(top_srcdir)/internal/serial.h
+hash.$(OBJEXT): $(top_srcdir)/internal/st.h
hash.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
hash.$(OBJEXT): $(top_srcdir)/internal/string.h
hash.$(OBJEXT): $(top_srcdir)/internal/symbol.h
@@ -7239,6 +7775,27 @@ hash.$(OBJEXT): $(top_srcdir)/internal/time.h
hash.$(OBJEXT): $(top_srcdir)/internal/variable.h
hash.$(OBJEXT): $(top_srcdir)/internal/vm.h
hash.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+hash.$(OBJEXT): $(top_srcdir)/prism/defines.h
+hash.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+hash.$(OBJEXT): $(top_srcdir)/prism/node.h
+hash.$(OBJEXT): $(top_srcdir)/prism/options.h
+hash.$(OBJEXT): $(top_srcdir)/prism/pack.h
+hash.$(OBJEXT): $(top_srcdir)/prism/parser.h
+hash.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+hash.$(OBJEXT): $(top_srcdir)/prism/prism.h
+hash.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+hash.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+hash.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+hash.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+hash.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+hash.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+hash.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+hash.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+hash.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+hash.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+hash.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+hash.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+hash.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
hash.$(OBJEXT): {$(VPATH)}assert.h
hash.$(OBJEXT): {$(VPATH)}atomic.h
hash.$(OBJEXT): {$(VPATH)}backward/2/assume.h
@@ -7399,9 +7956,11 @@ hash.$(OBJEXT): {$(VPATH)}internal/module.h
hash.$(OBJEXT): {$(VPATH)}internal/newobj.h
hash.$(OBJEXT): {$(VPATH)}internal/scan_args.h
hash.$(OBJEXT): {$(VPATH)}internal/special_consts.h
+hash.$(OBJEXT): {$(VPATH)}internal/st.h
hash.$(OBJEXT): {$(VPATH)}internal/static_assert.h
hash.$(OBJEXT): {$(VPATH)}internal/stdalign.h
hash.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+hash.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
hash.$(OBJEXT): {$(VPATH)}internal/symbol.h
hash.$(OBJEXT): {$(VPATH)}internal/value.h
hash.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -7414,23 +7973,232 @@ hash.$(OBJEXT): {$(VPATH)}missing.h
hash.$(OBJEXT): {$(VPATH)}node.h
hash.$(OBJEXT): {$(VPATH)}onigmo.h
hash.$(OBJEXT): {$(VPATH)}oniguruma.h
+hash.$(OBJEXT): {$(VPATH)}prism/ast.h
+hash.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+hash.$(OBJEXT): {$(VPATH)}prism/version.h
+hash.$(OBJEXT): {$(VPATH)}prism_compile.h
hash.$(OBJEXT): {$(VPATH)}probes.dmyh
hash.$(OBJEXT): {$(VPATH)}probes.h
hash.$(OBJEXT): {$(VPATH)}ractor.h
hash.$(OBJEXT): {$(VPATH)}ruby_assert.h
hash.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+hash.$(OBJEXT): {$(VPATH)}rubyparser.h
hash.$(OBJEXT): {$(VPATH)}shape.h
hash.$(OBJEXT): {$(VPATH)}st.h
hash.$(OBJEXT): {$(VPATH)}subst.h
hash.$(OBJEXT): {$(VPATH)}symbol.h
hash.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
hash.$(OBJEXT): {$(VPATH)}thread_native.h
-hash.$(OBJEXT): {$(VPATH)}transient_heap.h
hash.$(OBJEXT): {$(VPATH)}util.h
hash.$(OBJEXT): {$(VPATH)}vm_core.h
hash.$(OBJEXT): {$(VPATH)}vm_debug.h
hash.$(OBJEXT): {$(VPATH)}vm_opts.h
hash.$(OBJEXT): {$(VPATH)}vm_sync.h
+imemo.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
+imemo.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
+imemo.$(OBJEXT): $(CCAN_DIR)/list/list.h
+imemo.$(OBJEXT): $(CCAN_DIR)/str/str.h
+imemo.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+imemo.$(OBJEXT): $(top_srcdir)/internal/array.h
+imemo.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
+imemo.$(OBJEXT): $(top_srcdir)/internal/class.h
+imemo.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+imemo.$(OBJEXT): $(top_srcdir)/internal/gc.h
+imemo.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+imemo.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
+imemo.$(OBJEXT): $(top_srcdir)/internal/serial.h
+imemo.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+imemo.$(OBJEXT): $(top_srcdir)/internal/variable.h
+imemo.$(OBJEXT): $(top_srcdir)/internal/vm.h
+imemo.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+imemo.$(OBJEXT): {$(VPATH)}assert.h
+imemo.$(OBJEXT): {$(VPATH)}atomic.h
+imemo.$(OBJEXT): {$(VPATH)}backward/2/assume.h
+imemo.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
+imemo.$(OBJEXT): {$(VPATH)}backward/2/bool.h
+imemo.$(OBJEXT): {$(VPATH)}backward/2/gcc_version_since.h
+imemo.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h
+imemo.$(OBJEXT): {$(VPATH)}backward/2/limits.h
+imemo.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
+imemo.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
+imemo.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
+imemo.$(OBJEXT): {$(VPATH)}config.h
+imemo.$(OBJEXT): {$(VPATH)}constant.h
+imemo.$(OBJEXT): {$(VPATH)}debug_counter.h
+imemo.$(OBJEXT): {$(VPATH)}defines.h
+imemo.$(OBJEXT): {$(VPATH)}encoding.h
+imemo.$(OBJEXT): {$(VPATH)}id.h
+imemo.$(OBJEXT): {$(VPATH)}id_table.h
+imemo.$(OBJEXT): {$(VPATH)}imemo.c
+imemo.$(OBJEXT): {$(VPATH)}intern.h
+imemo.$(OBJEXT): {$(VPATH)}internal.h
+imemo.$(OBJEXT): {$(VPATH)}internal/abi.h
+imemo.$(OBJEXT): {$(VPATH)}internal/anyargs.h
+imemo.$(OBJEXT): {$(VPATH)}internal/arithmetic.h
+imemo.$(OBJEXT): {$(VPATH)}internal/arithmetic/char.h
+imemo.$(OBJEXT): {$(VPATH)}internal/arithmetic/double.h
+imemo.$(OBJEXT): {$(VPATH)}internal/arithmetic/fixnum.h
+imemo.$(OBJEXT): {$(VPATH)}internal/arithmetic/gid_t.h
+imemo.$(OBJEXT): {$(VPATH)}internal/arithmetic/int.h
+imemo.$(OBJEXT): {$(VPATH)}internal/arithmetic/intptr_t.h
+imemo.$(OBJEXT): {$(VPATH)}internal/arithmetic/long.h
+imemo.$(OBJEXT): {$(VPATH)}internal/arithmetic/long_long.h
+imemo.$(OBJEXT): {$(VPATH)}internal/arithmetic/mode_t.h
+imemo.$(OBJEXT): {$(VPATH)}internal/arithmetic/off_t.h
+imemo.$(OBJEXT): {$(VPATH)}internal/arithmetic/pid_t.h
+imemo.$(OBJEXT): {$(VPATH)}internal/arithmetic/short.h
+imemo.$(OBJEXT): {$(VPATH)}internal/arithmetic/size_t.h
+imemo.$(OBJEXT): {$(VPATH)}internal/arithmetic/st_data_t.h
+imemo.$(OBJEXT): {$(VPATH)}internal/arithmetic/uid_t.h
+imemo.$(OBJEXT): {$(VPATH)}internal/assume.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/alloc_size.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/artificial.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/cold.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/const.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/constexpr.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/deprecated.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/diagnose_if.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/enum_extensibility.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/error.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/flag_enum.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/forceinline.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/format.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/maybe_unused.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/noalias.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/returns_nonnull.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/warning.h
+imemo.$(OBJEXT): {$(VPATH)}internal/attr/weakref.h
+imemo.$(OBJEXT): {$(VPATH)}internal/cast.h
+imemo.$(OBJEXT): {$(VPATH)}internal/compiler_is.h
+imemo.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h
+imemo.$(OBJEXT): {$(VPATH)}internal/compiler_is/clang.h
+imemo.$(OBJEXT): {$(VPATH)}internal/compiler_is/gcc.h
+imemo.$(OBJEXT): {$(VPATH)}internal/compiler_is/intel.h
+imemo.$(OBJEXT): {$(VPATH)}internal/compiler_is/msvc.h
+imemo.$(OBJEXT): {$(VPATH)}internal/compiler_is/sunpro.h
+imemo.$(OBJEXT): {$(VPATH)}internal/compiler_since.h
+imemo.$(OBJEXT): {$(VPATH)}internal/config.h
+imemo.$(OBJEXT): {$(VPATH)}internal/constant_p.h
+imemo.$(OBJEXT): {$(VPATH)}internal/core.h
+imemo.$(OBJEXT): {$(VPATH)}internal/core/rarray.h
+imemo.$(OBJEXT): {$(VPATH)}internal/core/rbasic.h
+imemo.$(OBJEXT): {$(VPATH)}internal/core/rbignum.h
+imemo.$(OBJEXT): {$(VPATH)}internal/core/rclass.h
+imemo.$(OBJEXT): {$(VPATH)}internal/core/rdata.h
+imemo.$(OBJEXT): {$(VPATH)}internal/core/rfile.h
+imemo.$(OBJEXT): {$(VPATH)}internal/core/rhash.h
+imemo.$(OBJEXT): {$(VPATH)}internal/core/robject.h
+imemo.$(OBJEXT): {$(VPATH)}internal/core/rregexp.h
+imemo.$(OBJEXT): {$(VPATH)}internal/core/rstring.h
+imemo.$(OBJEXT): {$(VPATH)}internal/core/rstruct.h
+imemo.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
+imemo.$(OBJEXT): {$(VPATH)}internal/ctype.h
+imemo.$(OBJEXT): {$(VPATH)}internal/dllexport.h
+imemo.$(OBJEXT): {$(VPATH)}internal/dosish.h
+imemo.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
+imemo.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
+imemo.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
+imemo.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
+imemo.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
+imemo.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
+imemo.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
+imemo.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
+imemo.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
+imemo.$(OBJEXT): {$(VPATH)}internal/error.h
+imemo.$(OBJEXT): {$(VPATH)}internal/eval.h
+imemo.$(OBJEXT): {$(VPATH)}internal/event.h
+imemo.$(OBJEXT): {$(VPATH)}internal/fl_type.h
+imemo.$(OBJEXT): {$(VPATH)}internal/gc.h
+imemo.$(OBJEXT): {$(VPATH)}internal/glob.h
+imemo.$(OBJEXT): {$(VPATH)}internal/globals.h
+imemo.$(OBJEXT): {$(VPATH)}internal/has/attribute.h
+imemo.$(OBJEXT): {$(VPATH)}internal/has/builtin.h
+imemo.$(OBJEXT): {$(VPATH)}internal/has/c_attribute.h
+imemo.$(OBJEXT): {$(VPATH)}internal/has/cpp_attribute.h
+imemo.$(OBJEXT): {$(VPATH)}internal/has/declspec_attribute.h
+imemo.$(OBJEXT): {$(VPATH)}internal/has/extension.h
+imemo.$(OBJEXT): {$(VPATH)}internal/has/feature.h
+imemo.$(OBJEXT): {$(VPATH)}internal/has/warning.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/array.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/bignum.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/class.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/compar.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/complex.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/cont.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/dir.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/enum.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/enumerator.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/error.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/eval.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/file.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/hash.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/io.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/load.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/marshal.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/numeric.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/object.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/parse.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/proc.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/process.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/random.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/range.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/rational.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/re.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/ruby.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/select.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/select/largesize.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/signal.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/sprintf.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/string.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/struct.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/thread.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/time.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/variable.h
+imemo.$(OBJEXT): {$(VPATH)}internal/intern/vm.h
+imemo.$(OBJEXT): {$(VPATH)}internal/interpreter.h
+imemo.$(OBJEXT): {$(VPATH)}internal/iterator.h
+imemo.$(OBJEXT): {$(VPATH)}internal/memory.h
+imemo.$(OBJEXT): {$(VPATH)}internal/method.h
+imemo.$(OBJEXT): {$(VPATH)}internal/module.h
+imemo.$(OBJEXT): {$(VPATH)}internal/newobj.h
+imemo.$(OBJEXT): {$(VPATH)}internal/scan_args.h
+imemo.$(OBJEXT): {$(VPATH)}internal/special_consts.h
+imemo.$(OBJEXT): {$(VPATH)}internal/static_assert.h
+imemo.$(OBJEXT): {$(VPATH)}internal/stdalign.h
+imemo.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+imemo.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
+imemo.$(OBJEXT): {$(VPATH)}internal/symbol.h
+imemo.$(OBJEXT): {$(VPATH)}internal/value.h
+imemo.$(OBJEXT): {$(VPATH)}internal/value_type.h
+imemo.$(OBJEXT): {$(VPATH)}internal/variable.h
+imemo.$(OBJEXT): {$(VPATH)}internal/warning_push.h
+imemo.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+imemo.$(OBJEXT): {$(VPATH)}method.h
+imemo.$(OBJEXT): {$(VPATH)}missing.h
+imemo.$(OBJEXT): {$(VPATH)}node.h
+imemo.$(OBJEXT): {$(VPATH)}onigmo.h
+imemo.$(OBJEXT): {$(VPATH)}oniguruma.h
+imemo.$(OBJEXT): {$(VPATH)}ruby_assert.h
+imemo.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+imemo.$(OBJEXT): {$(VPATH)}rubyparser.h
+imemo.$(OBJEXT): {$(VPATH)}shape.h
+imemo.$(OBJEXT): {$(VPATH)}st.h
+imemo.$(OBJEXT): {$(VPATH)}subst.h
+imemo.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
+imemo.$(OBJEXT): {$(VPATH)}thread_native.h
+imemo.$(OBJEXT): {$(VPATH)}vm_callinfo.h
+imemo.$(OBJEXT): {$(VPATH)}vm_core.h
+imemo.$(OBJEXT): {$(VPATH)}vm_debug.h
+imemo.$(OBJEXT): {$(VPATH)}vm_opts.h
+imemo.$(OBJEXT): {$(VPATH)}vm_sync.h
inits.$(OBJEXT): $(hdrdir)/ruby.h
inits.$(OBJEXT): $(hdrdir)/ruby/ruby.h
inits.$(OBJEXT): $(top_srcdir)/internal/compilers.h
@@ -7584,6 +8352,7 @@ inits.$(OBJEXT): {$(VPATH)}internal/special_consts.h
inits.$(OBJEXT): {$(VPATH)}internal/static_assert.h
inits.$(OBJEXT): {$(VPATH)}internal/stdalign.h
inits.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+inits.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
inits.$(OBJEXT): {$(VPATH)}internal/symbol.h
inits.$(OBJEXT): {$(VPATH)}internal/value.h
inits.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -7599,6 +8368,7 @@ io.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
io.$(OBJEXT): $(CCAN_DIR)/list/list.h
io.$(OBJEXT): $(CCAN_DIR)/str/str.h
io.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+io.$(OBJEXT): $(hdrdir)/ruby/version.h
io.$(OBJEXT): $(top_srcdir)/internal/array.h
io.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
io.$(OBJEXT): $(top_srcdir)/internal/bignum.h
@@ -7615,6 +8385,7 @@ io.$(OBJEXT): $(top_srcdir)/internal/io.h
io.$(OBJEXT): $(top_srcdir)/internal/numeric.h
io.$(OBJEXT): $(top_srcdir)/internal/object.h
io.$(OBJEXT): $(top_srcdir)/internal/process.h
+io.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
io.$(OBJEXT): $(top_srcdir)/internal/serial.h
io.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
io.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -7637,6 +8408,7 @@ io.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
io.$(OBJEXT): {$(VPATH)}builtin.h
io.$(OBJEXT): {$(VPATH)}config.h
io.$(OBJEXT): {$(VPATH)}constant.h
+io.$(OBJEXT): {$(VPATH)}debug_counter.h
io.$(OBJEXT): {$(VPATH)}defines.h
io.$(OBJEXT): {$(VPATH)}dln.h
io.$(OBJEXT): {$(VPATH)}encindex.h
@@ -7788,6 +8560,7 @@ io.$(OBJEXT): {$(VPATH)}internal/special_consts.h
io.$(OBJEXT): {$(VPATH)}internal/static_assert.h
io.$(OBJEXT): {$(VPATH)}internal/stdalign.h
io.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+io.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
io.$(OBJEXT): {$(VPATH)}internal/symbol.h
io.$(OBJEXT): {$(VPATH)}internal/value.h
io.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -7806,6 +8579,7 @@ io.$(OBJEXT): {$(VPATH)}oniguruma.h
io.$(OBJEXT): {$(VPATH)}ractor.h
io.$(OBJEXT): {$(VPATH)}ruby_assert.h
io.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+io.$(OBJEXT): {$(VPATH)}rubyparser.h
io.$(OBJEXT): {$(VPATH)}shape.h
io.$(OBJEXT): {$(VPATH)}st.h
io.$(OBJEXT): {$(VPATH)}subst.h
@@ -7814,12 +8588,15 @@ io.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
io.$(OBJEXT): {$(VPATH)}thread_native.h
io.$(OBJEXT): {$(VPATH)}util.h
io.$(OBJEXT): {$(VPATH)}vm_core.h
+io.$(OBJEXT): {$(VPATH)}vm_debug.h
io.$(OBJEXT): {$(VPATH)}vm_opts.h
+io.$(OBJEXT): {$(VPATH)}vm_sync.h
io_buffer.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
io_buffer.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
io_buffer.$(OBJEXT): $(CCAN_DIR)/list/list.h
io_buffer.$(OBJEXT): $(CCAN_DIR)/str/str.h
io_buffer.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+io_buffer.$(OBJEXT): $(hdrdir)/ruby/version.h
io_buffer.$(OBJEXT): $(top_srcdir)/internal/array.h
io_buffer.$(OBJEXT): $(top_srcdir)/internal/bignum.h
io_buffer.$(OBJEXT): $(top_srcdir)/internal/bits.h
@@ -7990,6 +8767,7 @@ io_buffer.$(OBJEXT): {$(VPATH)}internal/special_consts.h
io_buffer.$(OBJEXT): {$(VPATH)}internal/static_assert.h
io_buffer.$(OBJEXT): {$(VPATH)}internal/stdalign.h
io_buffer.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+io_buffer.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
io_buffer.$(OBJEXT): {$(VPATH)}internal/symbol.h
io_buffer.$(OBJEXT): {$(VPATH)}internal/value.h
io_buffer.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -8011,18 +8789,26 @@ iseq.$(OBJEXT): $(CCAN_DIR)/list/list.h
iseq.$(OBJEXT): $(CCAN_DIR)/str/str.h
iseq.$(OBJEXT): $(hdrdir)/ruby.h
iseq.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+iseq.$(OBJEXT): $(hdrdir)/ruby/version.h
iseq.$(OBJEXT): $(top_srcdir)/internal/array.h
iseq.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
+iseq.$(OBJEXT): $(top_srcdir)/internal/bignum.h
iseq.$(OBJEXT): $(top_srcdir)/internal/bits.h
iseq.$(OBJEXT): $(top_srcdir)/internal/class.h
iseq.$(OBJEXT): $(top_srcdir)/internal/compile.h
iseq.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+iseq.$(OBJEXT): $(top_srcdir)/internal/complex.h
iseq.$(OBJEXT): $(top_srcdir)/internal/error.h
iseq.$(OBJEXT): $(top_srcdir)/internal/file.h
+iseq.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
iseq.$(OBJEXT): $(top_srcdir)/internal/gc.h
iseq.$(OBJEXT): $(top_srcdir)/internal/hash.h
iseq.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+iseq.$(OBJEXT): $(top_srcdir)/internal/io.h
+iseq.$(OBJEXT): $(top_srcdir)/internal/numeric.h
iseq.$(OBJEXT): $(top_srcdir)/internal/parse.h
+iseq.$(OBJEXT): $(top_srcdir)/internal/rational.h
+iseq.$(OBJEXT): $(top_srcdir)/internal/ruby_parser.h
iseq.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
iseq.$(OBJEXT): $(top_srcdir)/internal/serial.h
iseq.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
@@ -8032,6 +8818,27 @@ iseq.$(OBJEXT): $(top_srcdir)/internal/thread.h
iseq.$(OBJEXT): $(top_srcdir)/internal/variable.h
iseq.$(OBJEXT): $(top_srcdir)/internal/vm.h
iseq.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+iseq.$(OBJEXT): $(top_srcdir)/prism/defines.h
+iseq.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+iseq.$(OBJEXT): $(top_srcdir)/prism/node.h
+iseq.$(OBJEXT): $(top_srcdir)/prism/options.h
+iseq.$(OBJEXT): $(top_srcdir)/prism/pack.h
+iseq.$(OBJEXT): $(top_srcdir)/prism/parser.h
+iseq.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+iseq.$(OBJEXT): $(top_srcdir)/prism/prism.h
+iseq.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+iseq.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+iseq.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+iseq.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+iseq.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+iseq.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+iseq.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+iseq.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+iseq.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+iseq.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+iseq.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+iseq.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+iseq.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
iseq.$(OBJEXT): {$(VPATH)}assert.h
iseq.$(OBJEXT): {$(VPATH)}atomic.h
iseq.$(OBJEXT): {$(VPATH)}backward/2/assume.h
@@ -8199,12 +9006,14 @@ iseq.$(OBJEXT): {$(VPATH)}internal/special_consts.h
iseq.$(OBJEXT): {$(VPATH)}internal/static_assert.h
iseq.$(OBJEXT): {$(VPATH)}internal/stdalign.h
iseq.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+iseq.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
iseq.$(OBJEXT): {$(VPATH)}internal/symbol.h
iseq.$(OBJEXT): {$(VPATH)}internal/value.h
iseq.$(OBJEXT): {$(VPATH)}internal/value_type.h
iseq.$(OBJEXT): {$(VPATH)}internal/variable.h
iseq.$(OBJEXT): {$(VPATH)}internal/warning_push.h
iseq.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+iseq.$(OBJEXT): {$(VPATH)}io.h
iseq.$(OBJEXT): {$(VPATH)}iseq.c
iseq.$(OBJEXT): {$(VPATH)}iseq.h
iseq.$(OBJEXT): {$(VPATH)}method.h
@@ -8212,10 +9021,16 @@ iseq.$(OBJEXT): {$(VPATH)}missing.h
iseq.$(OBJEXT): {$(VPATH)}node.h
iseq.$(OBJEXT): {$(VPATH)}onigmo.h
iseq.$(OBJEXT): {$(VPATH)}oniguruma.h
+iseq.$(OBJEXT): {$(VPATH)}prism/ast.h
+iseq.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+iseq.$(OBJEXT): {$(VPATH)}prism/prism.h
+iseq.$(OBJEXT): {$(VPATH)}prism/version.h
+iseq.$(OBJEXT): {$(VPATH)}prism_compile.h
iseq.$(OBJEXT): {$(VPATH)}ractor.h
iseq.$(OBJEXT): {$(VPATH)}rjit.h
iseq.$(OBJEXT): {$(VPATH)}ruby_assert.h
iseq.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+iseq.$(OBJEXT): {$(VPATH)}rubyparser.h
iseq.$(OBJEXT): {$(VPATH)}shape.h
iseq.$(OBJEXT): {$(VPATH)}st.h
iseq.$(OBJEXT): {$(VPATH)}subst.h
@@ -8224,23 +9039,35 @@ iseq.$(OBJEXT): {$(VPATH)}thread_native.h
iseq.$(OBJEXT): {$(VPATH)}util.h
iseq.$(OBJEXT): {$(VPATH)}vm_callinfo.h
iseq.$(OBJEXT): {$(VPATH)}vm_core.h
+iseq.$(OBJEXT): {$(VPATH)}vm_debug.h
iseq.$(OBJEXT): {$(VPATH)}vm_opts.h
+iseq.$(OBJEXT): {$(VPATH)}vm_sync.h
iseq.$(OBJEXT): {$(VPATH)}yjit.h
load.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
load.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
load.$(OBJEXT): $(CCAN_DIR)/list/list.h
load.$(OBJEXT): $(CCAN_DIR)/str/str.h
load.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+load.$(OBJEXT): $(hdrdir)/ruby/version.h
load.$(OBJEXT): $(top_srcdir)/internal/array.h
load.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
+load.$(OBJEXT): $(top_srcdir)/internal/bignum.h
+load.$(OBJEXT): $(top_srcdir)/internal/bits.h
load.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+load.$(OBJEXT): $(top_srcdir)/internal/complex.h
load.$(OBJEXT): $(top_srcdir)/internal/dir.h
load.$(OBJEXT): $(top_srcdir)/internal/error.h
load.$(OBJEXT): $(top_srcdir)/internal/file.h
+load.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
load.$(OBJEXT): $(top_srcdir)/internal/gc.h
+load.$(OBJEXT): $(top_srcdir)/internal/hash.h
load.$(OBJEXT): $(top_srcdir)/internal/imemo.h
load.$(OBJEXT): $(top_srcdir)/internal/load.h
+load.$(OBJEXT): $(top_srcdir)/internal/numeric.h
load.$(OBJEXT): $(top_srcdir)/internal/parse.h
+load.$(OBJEXT): $(top_srcdir)/internal/rational.h
+load.$(OBJEXT): $(top_srcdir)/internal/ruby_parser.h
+load.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
load.$(OBJEXT): $(top_srcdir)/internal/serial.h
load.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
load.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -8248,6 +9075,27 @@ load.$(OBJEXT): $(top_srcdir)/internal/thread.h
load.$(OBJEXT): $(top_srcdir)/internal/variable.h
load.$(OBJEXT): $(top_srcdir)/internal/vm.h
load.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+load.$(OBJEXT): $(top_srcdir)/prism/defines.h
+load.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+load.$(OBJEXT): $(top_srcdir)/prism/node.h
+load.$(OBJEXT): $(top_srcdir)/prism/options.h
+load.$(OBJEXT): $(top_srcdir)/prism/pack.h
+load.$(OBJEXT): $(top_srcdir)/prism/parser.h
+load.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+load.$(OBJEXT): $(top_srcdir)/prism/prism.h
+load.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+load.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+load.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+load.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+load.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+load.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+load.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+load.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+load.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+load.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+load.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+load.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+load.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
load.$(OBJEXT): {$(VPATH)}assert.h
load.$(OBJEXT): {$(VPATH)}atomic.h
load.$(OBJEXT): {$(VPATH)}backward/2/assume.h
@@ -8412,6 +9260,7 @@ load.$(OBJEXT): {$(VPATH)}internal/special_consts.h
load.$(OBJEXT): {$(VPATH)}internal/static_assert.h
load.$(OBJEXT): {$(VPATH)}internal/stdalign.h
load.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+load.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
load.$(OBJEXT): {$(VPATH)}internal/symbol.h
load.$(OBJEXT): {$(VPATH)}internal/value.h
load.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -8425,10 +9274,15 @@ load.$(OBJEXT): {$(VPATH)}missing.h
load.$(OBJEXT): {$(VPATH)}node.h
load.$(OBJEXT): {$(VPATH)}onigmo.h
load.$(OBJEXT): {$(VPATH)}oniguruma.h
+load.$(OBJEXT): {$(VPATH)}prism/ast.h
+load.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+load.$(OBJEXT): {$(VPATH)}prism/version.h
+load.$(OBJEXT): {$(VPATH)}prism_compile.h
load.$(OBJEXT): {$(VPATH)}probes.dmyh
load.$(OBJEXT): {$(VPATH)}probes.h
load.$(OBJEXT): {$(VPATH)}ruby_assert.h
load.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+load.$(OBJEXT): {$(VPATH)}rubyparser.h
load.$(OBJEXT): {$(VPATH)}shape.h
load.$(OBJEXT): {$(VPATH)}st.h
load.$(OBJEXT): {$(VPATH)}subst.h
@@ -8585,6 +9439,7 @@ loadpath.$(OBJEXT): {$(VPATH)}internal/special_consts.h
loadpath.$(OBJEXT): {$(VPATH)}internal/static_assert.h
loadpath.$(OBJEXT): {$(VPATH)}internal/stdalign.h
loadpath.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+loadpath.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
loadpath.$(OBJEXT): {$(VPATH)}internal/symbol.h
loadpath.$(OBJEXT): {$(VPATH)}internal/value.h
loadpath.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -8754,6 +9609,7 @@ localeinit.$(OBJEXT): {$(VPATH)}internal/special_consts.h
localeinit.$(OBJEXT): {$(VPATH)}internal/static_assert.h
localeinit.$(OBJEXT): {$(VPATH)}internal/stdalign.h
localeinit.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+localeinit.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
localeinit.$(OBJEXT): {$(VPATH)}internal/symbol.h
localeinit.$(OBJEXT): {$(VPATH)}internal/value.h
localeinit.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -8768,11 +9624,15 @@ localeinit.$(OBJEXT): {$(VPATH)}st.h
localeinit.$(OBJEXT): {$(VPATH)}subst.h
main.$(OBJEXT): $(hdrdir)/ruby.h
main.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+main.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+main.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
+main.$(OBJEXT): $(top_srcdir)/internal/warnings.h
main.$(OBJEXT): {$(VPATH)}assert.h
main.$(OBJEXT): {$(VPATH)}backward.h
main.$(OBJEXT): {$(VPATH)}backward/2/assume.h
main.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
main.$(OBJEXT): {$(VPATH)}backward/2/bool.h
+main.$(OBJEXT): {$(VPATH)}backward/2/gcc_version_since.h
main.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h
main.$(OBJEXT): {$(VPATH)}backward/2/limits.h
main.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
@@ -8914,6 +9774,7 @@ main.$(OBJEXT): {$(VPATH)}internal/special_consts.h
main.$(OBJEXT): {$(VPATH)}internal/static_assert.h
main.$(OBJEXT): {$(VPATH)}internal/stdalign.h
main.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+main.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
main.$(OBJEXT): {$(VPATH)}internal/symbol.h
main.$(OBJEXT): {$(VPATH)}internal/value.h
main.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -8930,6 +9791,7 @@ marshal.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
marshal.$(OBJEXT): $(CCAN_DIR)/list/list.h
marshal.$(OBJEXT): $(CCAN_DIR)/str/str.h
marshal.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+marshal.$(OBJEXT): $(hdrdir)/ruby/version.h
marshal.$(OBJEXT): $(top_srcdir)/internal/array.h
marshal.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
marshal.$(OBJEXT): $(top_srcdir)/internal/bignum.h
@@ -8944,6 +9806,7 @@ marshal.$(OBJEXT): $(top_srcdir)/internal/hash.h
marshal.$(OBJEXT): $(top_srcdir)/internal/imemo.h
marshal.$(OBJEXT): $(top_srcdir)/internal/numeric.h
marshal.$(OBJEXT): $(top_srcdir)/internal/object.h
+marshal.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
marshal.$(OBJEXT): $(top_srcdir)/internal/serial.h
marshal.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
marshal.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -8967,6 +9830,7 @@ marshal.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
marshal.$(OBJEXT): {$(VPATH)}builtin.h
marshal.$(OBJEXT): {$(VPATH)}config.h
marshal.$(OBJEXT): {$(VPATH)}constant.h
+marshal.$(OBJEXT): {$(VPATH)}debug_counter.h
marshal.$(OBJEXT): {$(VPATH)}defines.h
marshal.$(OBJEXT): {$(VPATH)}encindex.h
marshal.$(OBJEXT): {$(VPATH)}encoding.h
@@ -9116,6 +9980,7 @@ marshal.$(OBJEXT): {$(VPATH)}internal/special_consts.h
marshal.$(OBJEXT): {$(VPATH)}internal/static_assert.h
marshal.$(OBJEXT): {$(VPATH)}internal/stdalign.h
marshal.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+marshal.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
marshal.$(OBJEXT): {$(VPATH)}internal/symbol.h
marshal.$(OBJEXT): {$(VPATH)}internal/value.h
marshal.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -9132,6 +9997,7 @@ marshal.$(OBJEXT): {$(VPATH)}onigmo.h
marshal.$(OBJEXT): {$(VPATH)}oniguruma.h
marshal.$(OBJEXT): {$(VPATH)}ruby_assert.h
marshal.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+marshal.$(OBJEXT): {$(VPATH)}rubyparser.h
marshal.$(OBJEXT): {$(VPATH)}shape.h
marshal.$(OBJEXT): {$(VPATH)}st.h
marshal.$(OBJEXT): {$(VPATH)}subst.h
@@ -9139,7 +10005,9 @@ marshal.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
marshal.$(OBJEXT): {$(VPATH)}thread_native.h
marshal.$(OBJEXT): {$(VPATH)}util.h
marshal.$(OBJEXT): {$(VPATH)}vm_core.h
+marshal.$(OBJEXT): {$(VPATH)}vm_debug.h
marshal.$(OBJEXT): {$(VPATH)}vm_opts.h
+marshal.$(OBJEXT): {$(VPATH)}vm_sync.h
math.$(OBJEXT): $(hdrdir)/ruby/ruby.h
math.$(OBJEXT): $(top_srcdir)/internal/bignum.h
math.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -9302,6 +10170,7 @@ math.$(OBJEXT): {$(VPATH)}internal/special_consts.h
math.$(OBJEXT): {$(VPATH)}internal/static_assert.h
math.$(OBJEXT): {$(VPATH)}internal/stdalign.h
math.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+math.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
math.$(OBJEXT): {$(VPATH)}internal/symbol.h
math.$(OBJEXT): {$(VPATH)}internal/value.h
math.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -9324,6 +10193,7 @@ memory_view.$(OBJEXT): $(top_srcdir)/internal/compilers.h
memory_view.$(OBJEXT): $(top_srcdir)/internal/gc.h
memory_view.$(OBJEXT): $(top_srcdir)/internal/hash.h
memory_view.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+memory_view.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
memory_view.$(OBJEXT): $(top_srcdir)/internal/serial.h
memory_view.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
memory_view.$(OBJEXT): $(top_srcdir)/internal/variable.h
@@ -9344,6 +10214,7 @@ memory_view.$(OBJEXT): {$(VPATH)}config.h
memory_view.$(OBJEXT): {$(VPATH)}constant.h
memory_view.$(OBJEXT): {$(VPATH)}debug_counter.h
memory_view.$(OBJEXT): {$(VPATH)}defines.h
+memory_view.$(OBJEXT): {$(VPATH)}encoding.h
memory_view.$(OBJEXT): {$(VPATH)}id.h
memory_view.$(OBJEXT): {$(VPATH)}id_table.h
memory_view.$(OBJEXT): {$(VPATH)}intern.h
@@ -9419,6 +10290,15 @@ memory_view.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
memory_view.$(OBJEXT): {$(VPATH)}internal/ctype.h
memory_view.$(OBJEXT): {$(VPATH)}internal/dllexport.h
memory_view.$(OBJEXT): {$(VPATH)}internal/dosish.h
+memory_view.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
+memory_view.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
+memory_view.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
+memory_view.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
+memory_view.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
+memory_view.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
+memory_view.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
+memory_view.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
+memory_view.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
memory_view.$(OBJEXT): {$(VPATH)}internal/error.h
memory_view.$(OBJEXT): {$(VPATH)}internal/eval.h
memory_view.$(OBJEXT): {$(VPATH)}internal/event.h
@@ -9481,6 +10361,7 @@ memory_view.$(OBJEXT): {$(VPATH)}internal/special_consts.h
memory_view.$(OBJEXT): {$(VPATH)}internal/static_assert.h
memory_view.$(OBJEXT): {$(VPATH)}internal/stdalign.h
memory_view.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+memory_view.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
memory_view.$(OBJEXT): {$(VPATH)}internal/symbol.h
memory_view.$(OBJEXT): {$(VPATH)}internal/value.h
memory_view.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -9492,8 +10373,11 @@ memory_view.$(OBJEXT): {$(VPATH)}memory_view.h
memory_view.$(OBJEXT): {$(VPATH)}method.h
memory_view.$(OBJEXT): {$(VPATH)}missing.h
memory_view.$(OBJEXT): {$(VPATH)}node.h
+memory_view.$(OBJEXT): {$(VPATH)}onigmo.h
+memory_view.$(OBJEXT): {$(VPATH)}oniguruma.h
memory_view.$(OBJEXT): {$(VPATH)}ruby_assert.h
memory_view.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+memory_view.$(OBJEXT): {$(VPATH)}rubyparser.h
memory_view.$(OBJEXT): {$(VPATH)}shape.h
memory_view.$(OBJEXT): {$(VPATH)}st.h
memory_view.$(OBJEXT): {$(VPATH)}subst.h
@@ -9512,14 +10396,44 @@ miniinit.$(OBJEXT): $(hdrdir)/ruby/ruby.h
miniinit.$(OBJEXT): $(srcdir)/rjit_c.rb
miniinit.$(OBJEXT): $(top_srcdir)/internal/array.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
+miniinit.$(OBJEXT): $(top_srcdir)/internal/bignum.h
+miniinit.$(OBJEXT): $(top_srcdir)/internal/bits.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+miniinit.$(OBJEXT): $(top_srcdir)/internal/complex.h
+miniinit.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/gc.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+miniinit.$(OBJEXT): $(top_srcdir)/internal/numeric.h
+miniinit.$(OBJEXT): $(top_srcdir)/internal/parse.h
+miniinit.$(OBJEXT): $(top_srcdir)/internal/rational.h
+miniinit.$(OBJEXT): $(top_srcdir)/internal/ruby_parser.h
+miniinit.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/serial.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/variable.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/vm.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+miniinit.$(OBJEXT): $(top_srcdir)/prism/defines.h
+miniinit.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+miniinit.$(OBJEXT): $(top_srcdir)/prism/node.h
+miniinit.$(OBJEXT): $(top_srcdir)/prism/options.h
+miniinit.$(OBJEXT): $(top_srcdir)/prism/pack.h
+miniinit.$(OBJEXT): $(top_srcdir)/prism/parser.h
+miniinit.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+miniinit.$(OBJEXT): $(top_srcdir)/prism/prism.h
+miniinit.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+miniinit.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+miniinit.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+miniinit.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+miniinit.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+miniinit.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+miniinit.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+miniinit.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+miniinit.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+miniinit.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+miniinit.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+miniinit.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+miniinit.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
miniinit.$(OBJEXT): {$(VPATH)}array.rb
miniinit.$(OBJEXT): {$(VPATH)}assert.h
miniinit.$(OBJEXT): {$(VPATH)}ast.rb
@@ -9687,6 +10601,7 @@ miniinit.$(OBJEXT): {$(VPATH)}internal/special_consts.h
miniinit.$(OBJEXT): {$(VPATH)}internal/static_assert.h
miniinit.$(OBJEXT): {$(VPATH)}internal/stdalign.h
miniinit.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+miniinit.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
miniinit.$(OBJEXT): {$(VPATH)}internal/symbol.h
miniinit.$(OBJEXT): {$(VPATH)}internal/value.h
miniinit.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -9709,11 +10624,16 @@ miniinit.$(OBJEXT): {$(VPATH)}onigmo.h
miniinit.$(OBJEXT): {$(VPATH)}oniguruma.h
miniinit.$(OBJEXT): {$(VPATH)}pack.rb
miniinit.$(OBJEXT): {$(VPATH)}prelude.rb
+miniinit.$(OBJEXT): {$(VPATH)}prism/ast.h
+miniinit.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+miniinit.$(OBJEXT): {$(VPATH)}prism/version.h
+miniinit.$(OBJEXT): {$(VPATH)}prism_compile.h
miniinit.$(OBJEXT): {$(VPATH)}ractor.rb
miniinit.$(OBJEXT): {$(VPATH)}rjit.rb
miniinit.$(OBJEXT): {$(VPATH)}rjit_c.rb
miniinit.$(OBJEXT): {$(VPATH)}ruby_assert.h
miniinit.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+miniinit.$(OBJEXT): {$(VPATH)}rubyparser.h
miniinit.$(OBJEXT): {$(VPATH)}shape.h
miniinit.$(OBJEXT): {$(VPATH)}st.h
miniinit.$(OBJEXT): {$(VPATH)}subst.h
@@ -9738,6 +10658,7 @@ node.$(OBJEXT): $(top_srcdir)/internal/compilers.h
node.$(OBJEXT): $(top_srcdir)/internal/gc.h
node.$(OBJEXT): $(top_srcdir)/internal/hash.h
node.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+node.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
node.$(OBJEXT): $(top_srcdir)/internal/serial.h
node.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
node.$(OBJEXT): $(top_srcdir)/internal/variable.h
@@ -9757,6 +10678,7 @@ node.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
node.$(OBJEXT): {$(VPATH)}config.h
node.$(OBJEXT): {$(VPATH)}constant.h
node.$(OBJEXT): {$(VPATH)}defines.h
+node.$(OBJEXT): {$(VPATH)}encoding.h
node.$(OBJEXT): {$(VPATH)}id.h
node.$(OBJEXT): {$(VPATH)}id_table.h
node.$(OBJEXT): {$(VPATH)}intern.h
@@ -9832,6 +10754,15 @@ node.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
node.$(OBJEXT): {$(VPATH)}internal/ctype.h
node.$(OBJEXT): {$(VPATH)}internal/dllexport.h
node.$(OBJEXT): {$(VPATH)}internal/dosish.h
+node.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
+node.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
+node.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
+node.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
+node.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
+node.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
+node.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
+node.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
+node.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
node.$(OBJEXT): {$(VPATH)}internal/error.h
node.$(OBJEXT): {$(VPATH)}internal/eval.h
node.$(OBJEXT): {$(VPATH)}internal/event.h
@@ -9894,6 +10825,7 @@ node.$(OBJEXT): {$(VPATH)}internal/special_consts.h
node.$(OBJEXT): {$(VPATH)}internal/static_assert.h
node.$(OBJEXT): {$(VPATH)}internal/stdalign.h
node.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+node.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
node.$(OBJEXT): {$(VPATH)}internal/symbol.h
node.$(OBJEXT): {$(VPATH)}internal/value.h
node.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -9905,8 +10837,11 @@ node.$(OBJEXT): {$(VPATH)}missing.h
node.$(OBJEXT): {$(VPATH)}node.c
node.$(OBJEXT): {$(VPATH)}node.h
node.$(OBJEXT): {$(VPATH)}node_name.inc
+node.$(OBJEXT): {$(VPATH)}onigmo.h
+node.$(OBJEXT): {$(VPATH)}oniguruma.h
node.$(OBJEXT): {$(VPATH)}ruby_assert.h
node.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+node.$(OBJEXT): {$(VPATH)}rubyparser.h
node.$(OBJEXT): {$(VPATH)}shape.h
node.$(OBJEXT): {$(VPATH)}st.h
node.$(OBJEXT): {$(VPATH)}subst.h
@@ -9914,6 +10849,219 @@ node.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
node.$(OBJEXT): {$(VPATH)}thread_native.h
node.$(OBJEXT): {$(VPATH)}vm_core.h
node.$(OBJEXT): {$(VPATH)}vm_opts.h
+node_dump.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
+node_dump.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
+node_dump.$(OBJEXT): $(CCAN_DIR)/list/list.h
+node_dump.$(OBJEXT): $(CCAN_DIR)/str/str.h
+node_dump.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+node_dump.$(OBJEXT): $(top_srcdir)/internal/array.h
+node_dump.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
+node_dump.$(OBJEXT): $(top_srcdir)/internal/bignum.h
+node_dump.$(OBJEXT): $(top_srcdir)/internal/bits.h
+node_dump.$(OBJEXT): $(top_srcdir)/internal/class.h
+node_dump.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+node_dump.$(OBJEXT): $(top_srcdir)/internal/complex.h
+node_dump.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
+node_dump.$(OBJEXT): $(top_srcdir)/internal/gc.h
+node_dump.$(OBJEXT): $(top_srcdir)/internal/hash.h
+node_dump.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+node_dump.$(OBJEXT): $(top_srcdir)/internal/numeric.h
+node_dump.$(OBJEXT): $(top_srcdir)/internal/parse.h
+node_dump.$(OBJEXT): $(top_srcdir)/internal/rational.h
+node_dump.$(OBJEXT): $(top_srcdir)/internal/ruby_parser.h
+node_dump.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
+node_dump.$(OBJEXT): $(top_srcdir)/internal/serial.h
+node_dump.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+node_dump.$(OBJEXT): $(top_srcdir)/internal/variable.h
+node_dump.$(OBJEXT): $(top_srcdir)/internal/vm.h
+node_dump.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+node_dump.$(OBJEXT): {$(VPATH)}assert.h
+node_dump.$(OBJEXT): {$(VPATH)}atomic.h
+node_dump.$(OBJEXT): {$(VPATH)}backward/2/assume.h
+node_dump.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
+node_dump.$(OBJEXT): {$(VPATH)}backward/2/bool.h
+node_dump.$(OBJEXT): {$(VPATH)}backward/2/gcc_version_since.h
+node_dump.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h
+node_dump.$(OBJEXT): {$(VPATH)}backward/2/limits.h
+node_dump.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
+node_dump.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
+node_dump.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
+node_dump.$(OBJEXT): {$(VPATH)}config.h
+node_dump.$(OBJEXT): {$(VPATH)}constant.h
+node_dump.$(OBJEXT): {$(VPATH)}debug_counter.h
+node_dump.$(OBJEXT): {$(VPATH)}defines.h
+node_dump.$(OBJEXT): {$(VPATH)}encoding.h
+node_dump.$(OBJEXT): {$(VPATH)}id.h
+node_dump.$(OBJEXT): {$(VPATH)}id_table.h
+node_dump.$(OBJEXT): {$(VPATH)}intern.h
+node_dump.$(OBJEXT): {$(VPATH)}internal.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/abi.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/anyargs.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/arithmetic.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/arithmetic/char.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/arithmetic/double.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/arithmetic/fixnum.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/arithmetic/gid_t.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/arithmetic/int.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/arithmetic/intptr_t.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/arithmetic/long.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/arithmetic/long_long.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/arithmetic/mode_t.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/arithmetic/off_t.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/arithmetic/pid_t.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/arithmetic/short.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/arithmetic/size_t.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/arithmetic/st_data_t.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/arithmetic/uid_t.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/assume.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/alloc_size.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/artificial.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/cold.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/const.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/constexpr.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/deprecated.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/diagnose_if.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/enum_extensibility.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/error.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/flag_enum.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/forceinline.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/format.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/maybe_unused.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/noalias.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/returns_nonnull.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/warning.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/attr/weakref.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/cast.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/compiler_is.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/compiler_is/clang.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/compiler_is/gcc.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/compiler_is/intel.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/compiler_is/msvc.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/compiler_is/sunpro.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/compiler_since.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/config.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/constant_p.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/core.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/core/rarray.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/core/rbasic.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/core/rbignum.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/core/rclass.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/core/rdata.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/core/rfile.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/core/rhash.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/core/robject.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/core/rregexp.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/core/rstring.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/core/rstruct.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/ctype.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/dllexport.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/dosish.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/error.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/eval.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/event.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/fl_type.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/gc.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/glob.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/globals.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/has/attribute.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/has/builtin.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/has/c_attribute.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/has/cpp_attribute.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/has/declspec_attribute.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/has/extension.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/has/feature.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/has/warning.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/array.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/bignum.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/class.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/compar.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/complex.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/cont.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/dir.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/enum.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/enumerator.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/error.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/eval.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/file.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/hash.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/io.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/load.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/marshal.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/numeric.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/object.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/parse.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/proc.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/process.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/random.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/range.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/rational.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/re.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/ruby.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/select.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/select/largesize.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/signal.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/sprintf.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/string.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/struct.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/thread.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/time.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/variable.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/intern/vm.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/interpreter.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/iterator.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/memory.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/method.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/module.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/newobj.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/scan_args.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/special_consts.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/static_assert.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/stdalign.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/symbol.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/value.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/value_type.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/variable.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/warning_push.h
+node_dump.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+node_dump.$(OBJEXT): {$(VPATH)}method.h
+node_dump.$(OBJEXT): {$(VPATH)}missing.h
+node_dump.$(OBJEXT): {$(VPATH)}node.h
+node_dump.$(OBJEXT): {$(VPATH)}node_dump.c
+node_dump.$(OBJEXT): {$(VPATH)}onigmo.h
+node_dump.$(OBJEXT): {$(VPATH)}oniguruma.h
+node_dump.$(OBJEXT): {$(VPATH)}ruby_assert.h
+node_dump.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+node_dump.$(OBJEXT): {$(VPATH)}rubyparser.h
+node_dump.$(OBJEXT): {$(VPATH)}shape.h
+node_dump.$(OBJEXT): {$(VPATH)}st.h
+node_dump.$(OBJEXT): {$(VPATH)}subst.h
+node_dump.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
+node_dump.$(OBJEXT): {$(VPATH)}thread_native.h
+node_dump.$(OBJEXT): {$(VPATH)}vm_core.h
+node_dump.$(OBJEXT): {$(VPATH)}vm_debug.h
+node_dump.$(OBJEXT): {$(VPATH)}vm_opts.h
+node_dump.$(OBJEXT): {$(VPATH)}vm_sync.h
numeric.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
numeric.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
numeric.$(OBJEXT): $(CCAN_DIR)/list/list.h
@@ -9934,6 +11082,7 @@ numeric.$(OBJEXT): $(top_srcdir)/internal/imemo.h
numeric.$(OBJEXT): $(top_srcdir)/internal/numeric.h
numeric.$(OBJEXT): $(top_srcdir)/internal/object.h
numeric.$(OBJEXT): $(top_srcdir)/internal/rational.h
+numeric.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
numeric.$(OBJEXT): $(top_srcdir)/internal/serial.h
numeric.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
numeric.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -10103,6 +11252,7 @@ numeric.$(OBJEXT): {$(VPATH)}internal/special_consts.h
numeric.$(OBJEXT): {$(VPATH)}internal/static_assert.h
numeric.$(OBJEXT): {$(VPATH)}internal/stdalign.h
numeric.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+numeric.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
numeric.$(OBJEXT): {$(VPATH)}internal/symbol.h
numeric.$(OBJEXT): {$(VPATH)}internal/value.h
numeric.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -10118,6 +11268,7 @@ numeric.$(OBJEXT): {$(VPATH)}onigmo.h
numeric.$(OBJEXT): {$(VPATH)}oniguruma.h
numeric.$(OBJEXT): {$(VPATH)}ruby_assert.h
numeric.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+numeric.$(OBJEXT): {$(VPATH)}rubyparser.h
numeric.$(OBJEXT): {$(VPATH)}shape.h
numeric.$(OBJEXT): {$(VPATH)}st.h
numeric.$(OBJEXT): {$(VPATH)}subst.h
@@ -10131,6 +11282,7 @@ object.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
object.$(OBJEXT): $(CCAN_DIR)/list/list.h
object.$(OBJEXT): $(CCAN_DIR)/str/str.h
object.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+object.$(OBJEXT): $(hdrdir)/ruby/version.h
object.$(OBJEXT): $(top_srcdir)/internal/array.h
object.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
object.$(OBJEXT): $(top_srcdir)/internal/bignum.h
@@ -10145,7 +11297,9 @@ object.$(OBJEXT): $(top_srcdir)/internal/imemo.h
object.$(OBJEXT): $(top_srcdir)/internal/inits.h
object.$(OBJEXT): $(top_srcdir)/internal/numeric.h
object.$(OBJEXT): $(top_srcdir)/internal/object.h
+object.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
object.$(OBJEXT): $(top_srcdir)/internal/serial.h
+object.$(OBJEXT): $(top_srcdir)/internal/st.h
object.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
object.$(OBJEXT): $(top_srcdir)/internal/string.h
object.$(OBJEXT): $(top_srcdir)/internal/struct.h
@@ -10167,6 +11321,7 @@ object.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
object.$(OBJEXT): {$(VPATH)}builtin.h
object.$(OBJEXT): {$(VPATH)}config.h
object.$(OBJEXT): {$(VPATH)}constant.h
+object.$(OBJEXT): {$(VPATH)}debug_counter.h
object.$(OBJEXT): {$(VPATH)}defines.h
object.$(OBJEXT): {$(VPATH)}encoding.h
object.$(OBJEXT): {$(VPATH)}id.h
@@ -10312,9 +11467,11 @@ object.$(OBJEXT): {$(VPATH)}internal/module.h
object.$(OBJEXT): {$(VPATH)}internal/newobj.h
object.$(OBJEXT): {$(VPATH)}internal/scan_args.h
object.$(OBJEXT): {$(VPATH)}internal/special_consts.h
+object.$(OBJEXT): {$(VPATH)}internal/st.h
object.$(OBJEXT): {$(VPATH)}internal/static_assert.h
object.$(OBJEXT): {$(VPATH)}internal/stdalign.h
object.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+object.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
object.$(OBJEXT): {$(VPATH)}internal/symbol.h
object.$(OBJEXT): {$(VPATH)}internal/value.h
object.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -10333,6 +11490,7 @@ object.$(OBJEXT): {$(VPATH)}probes.dmyh
object.$(OBJEXT): {$(VPATH)}probes.h
object.$(OBJEXT): {$(VPATH)}ruby_assert.h
object.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+object.$(OBJEXT): {$(VPATH)}rubyparser.h
object.$(OBJEXT): {$(VPATH)}shape.h
object.$(OBJEXT): {$(VPATH)}st.h
object.$(OBJEXT): {$(VPATH)}subst.h
@@ -10341,7 +11499,10 @@ object.$(OBJEXT): {$(VPATH)}thread_native.h
object.$(OBJEXT): {$(VPATH)}util.h
object.$(OBJEXT): {$(VPATH)}variable.h
object.$(OBJEXT): {$(VPATH)}vm_core.h
+object.$(OBJEXT): {$(VPATH)}vm_debug.h
object.$(OBJEXT): {$(VPATH)}vm_opts.h
+object.$(OBJEXT): {$(VPATH)}vm_sync.h
+object.$(OBJEXT): {$(VPATH)}yjit.h
pack.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
pack.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
pack.$(OBJEXT): $(CCAN_DIR)/list/list.h
@@ -10353,6 +11514,7 @@ pack.$(OBJEXT): $(top_srcdir)/internal/bits.h
pack.$(OBJEXT): $(top_srcdir)/internal/compilers.h
pack.$(OBJEXT): $(top_srcdir)/internal/gc.h
pack.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+pack.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
pack.$(OBJEXT): $(top_srcdir)/internal/serial.h
pack.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
pack.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -10522,6 +11684,7 @@ pack.$(OBJEXT): {$(VPATH)}internal/special_consts.h
pack.$(OBJEXT): {$(VPATH)}internal/static_assert.h
pack.$(OBJEXT): {$(VPATH)}internal/stdalign.h
pack.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+pack.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
pack.$(OBJEXT): {$(VPATH)}internal/symbol.h
pack.$(OBJEXT): {$(VPATH)}internal/value.h
pack.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -10537,6 +11700,7 @@ pack.$(OBJEXT): {$(VPATH)}pack.c
pack.$(OBJEXT): {$(VPATH)}pack.rbinc
pack.$(OBJEXT): {$(VPATH)}ruby_assert.h
pack.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+pack.$(OBJEXT): {$(VPATH)}rubyparser.h
pack.$(OBJEXT): {$(VPATH)}shape.h
pack.$(OBJEXT): {$(VPATH)}st.h
pack.$(OBJEXT): {$(VPATH)}subst.h
@@ -10551,6 +11715,7 @@ parse.$(OBJEXT): $(CCAN_DIR)/list/list.h
parse.$(OBJEXT): $(CCAN_DIR)/str/str.h
parse.$(OBJEXT): $(hdrdir)/ruby.h
parse.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+parse.$(OBJEXT): $(hdrdir)/ruby/version.h
parse.$(OBJEXT): $(top_srcdir)/internal/array.h
parse.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
parse.$(OBJEXT): $(top_srcdir)/internal/bignum.h
@@ -10569,6 +11734,8 @@ parse.$(OBJEXT): $(top_srcdir)/internal/numeric.h
parse.$(OBJEXT): $(top_srcdir)/internal/parse.h
parse.$(OBJEXT): $(top_srcdir)/internal/rational.h
parse.$(OBJEXT): $(top_srcdir)/internal/re.h
+parse.$(OBJEXT): $(top_srcdir)/internal/ruby_parser.h
+parse.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
parse.$(OBJEXT): $(top_srcdir)/internal/serial.h
parse.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
parse.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -10739,6 +11906,7 @@ parse.$(OBJEXT): {$(VPATH)}internal/special_consts.h
parse.$(OBJEXT): {$(VPATH)}internal/static_assert.h
parse.$(OBJEXT): {$(VPATH)}internal/stdalign.h
parse.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+parse.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
parse.$(OBJEXT): {$(VPATH)}internal/symbol.h
parse.$(OBJEXT): {$(VPATH)}internal/value.h
parse.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -10755,6 +11923,8 @@ parse.$(OBJEXT): {$(VPATH)}oniguruma.h
parse.$(OBJEXT): {$(VPATH)}parse.c
parse.$(OBJEXT): {$(VPATH)}parse.h
parse.$(OBJEXT): {$(VPATH)}parse.y
+parse.$(OBJEXT): {$(VPATH)}parser_node.h
+parse.$(OBJEXT): {$(VPATH)}parser_st.h
parse.$(OBJEXT): {$(VPATH)}probes.dmyh
parse.$(OBJEXT): {$(VPATH)}probes.h
parse.$(OBJEXT): {$(VPATH)}ractor.h
@@ -10762,6 +11932,7 @@ parse.$(OBJEXT): {$(VPATH)}regenc.h
parse.$(OBJEXT): {$(VPATH)}regex.h
parse.$(OBJEXT): {$(VPATH)}ruby_assert.h
parse.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+parse.$(OBJEXT): {$(VPATH)}rubyparser.h
parse.$(OBJEXT): {$(VPATH)}shape.h
parse.$(OBJEXT): {$(VPATH)}st.h
parse.$(OBJEXT): {$(VPATH)}subst.h
@@ -10771,11 +11942,1072 @@ parse.$(OBJEXT): {$(VPATH)}thread_native.h
parse.$(OBJEXT): {$(VPATH)}util.h
parse.$(OBJEXT): {$(VPATH)}vm_core.h
parse.$(OBJEXT): {$(VPATH)}vm_opts.h
+parser_st.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+parser_st.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+parser_st.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+parser_st.$(OBJEXT): {$(VPATH)}assert.h
+parser_st.$(OBJEXT): {$(VPATH)}backward/2/assume.h
+parser_st.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
+parser_st.$(OBJEXT): {$(VPATH)}backward/2/bool.h
+parser_st.$(OBJEXT): {$(VPATH)}backward/2/gcc_version_since.h
+parser_st.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
+parser_st.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
+parser_st.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
+parser_st.$(OBJEXT): {$(VPATH)}config.h
+parser_st.$(OBJEXT): {$(VPATH)}defines.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/assume.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/attr/alloc_size.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/attr/cold.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/attr/const.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/attr/deprecated.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/attr/error.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/attr/forceinline.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/attr/format.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/attr/maybe_unused.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/attr/returns_nonnull.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/attr/warning.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/cast.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/compiler_is.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/compiler_is/clang.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/compiler_is/gcc.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/compiler_is/intel.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/compiler_is/msvc.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/compiler_is/sunpro.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/compiler_since.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/config.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/dllexport.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/dosish.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/has/attribute.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/has/builtin.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/has/c_attribute.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/has/cpp_attribute.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/has/declspec_attribute.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/has/extension.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/has/feature.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/has/warning.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/static_assert.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/stdalign.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/warning_push.h
+parser_st.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+parser_st.$(OBJEXT): {$(VPATH)}missing.h
+parser_st.$(OBJEXT): {$(VPATH)}parser_bits.h
+parser_st.$(OBJEXT): {$(VPATH)}parser_st.c
+parser_st.$(OBJEXT): {$(VPATH)}parser_st.h
+parser_st.$(OBJEXT): {$(VPATH)}parser_value.h
+parser_st.$(OBJEXT): {$(VPATH)}st.c
+prism/api_node.$(OBJEXT): $(hdrdir)/ruby.h
+prism/api_node.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+prism/api_node.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/api_node.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+prism/api_node.$(OBJEXT): $(top_srcdir)/prism/extension.h
+prism/api_node.$(OBJEXT): $(top_srcdir)/prism/node.h
+prism/api_node.$(OBJEXT): $(top_srcdir)/prism/options.h
+prism/api_node.$(OBJEXT): $(top_srcdir)/prism/pack.h
+prism/api_node.$(OBJEXT): $(top_srcdir)/prism/parser.h
+prism/api_node.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+prism/api_node.$(OBJEXT): $(top_srcdir)/prism/prism.h
+prism/api_node.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+prism/api_node.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+prism/api_node.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+prism/api_node.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+prism/api_node.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+prism/api_node.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+prism/api_node.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+prism/api_node.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+prism/api_node.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+prism/api_node.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+prism/api_node.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+prism/api_node.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+prism/api_node.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
+prism/api_node.$(OBJEXT): {$(VPATH)}assert.h
+prism/api_node.$(OBJEXT): {$(VPATH)}backward/2/assume.h
+prism/api_node.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
+prism/api_node.$(OBJEXT): {$(VPATH)}backward/2/bool.h
+prism/api_node.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h
+prism/api_node.$(OBJEXT): {$(VPATH)}backward/2/limits.h
+prism/api_node.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
+prism/api_node.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
+prism/api_node.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
+prism/api_node.$(OBJEXT): {$(VPATH)}config.h
+prism/api_node.$(OBJEXT): {$(VPATH)}defines.h
+prism/api_node.$(OBJEXT): {$(VPATH)}encoding.h
+prism/api_node.$(OBJEXT): {$(VPATH)}intern.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/abi.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/anyargs.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/arithmetic.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/arithmetic/char.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/arithmetic/double.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/arithmetic/fixnum.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/arithmetic/gid_t.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/arithmetic/int.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/arithmetic/intptr_t.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/arithmetic/long.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/arithmetic/long_long.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/arithmetic/mode_t.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/arithmetic/off_t.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/arithmetic/pid_t.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/arithmetic/short.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/arithmetic/size_t.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/arithmetic/st_data_t.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/arithmetic/uid_t.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/assume.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/alloc_size.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/artificial.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/cold.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/const.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/constexpr.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/deprecated.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/diagnose_if.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/enum_extensibility.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/error.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/flag_enum.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/forceinline.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/format.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/maybe_unused.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/noalias.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/returns_nonnull.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/warning.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/attr/weakref.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/cast.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/compiler_is.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/compiler_is/clang.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/compiler_is/gcc.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/compiler_is/intel.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/compiler_is/msvc.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/compiler_is/sunpro.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/compiler_since.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/config.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/constant_p.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/core.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/core/rarray.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/core/rbasic.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/core/rbignum.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/core/rclass.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/core/rdata.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/core/rfile.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/core/rhash.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/core/robject.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/core/rregexp.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/core/rstring.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/core/rstruct.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/ctype.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/dllexport.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/dosish.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/error.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/eval.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/event.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/fl_type.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/gc.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/glob.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/globals.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/has/attribute.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/has/builtin.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/has/c_attribute.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/has/cpp_attribute.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/has/declspec_attribute.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/has/extension.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/has/feature.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/has/warning.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/array.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/bignum.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/class.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/compar.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/complex.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/cont.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/dir.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/enum.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/enumerator.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/error.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/eval.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/file.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/hash.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/io.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/load.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/marshal.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/numeric.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/object.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/parse.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/proc.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/process.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/random.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/range.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/rational.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/re.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/ruby.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/select.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/select/largesize.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/signal.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/sprintf.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/string.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/struct.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/thread.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/time.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/variable.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/intern/vm.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/interpreter.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/iterator.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/memory.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/method.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/module.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/newobj.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/scan_args.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/special_consts.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/static_assert.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/stdalign.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/symbol.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/value.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/value_type.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/variable.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/warning_push.h
+prism/api_node.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+prism/api_node.$(OBJEXT): {$(VPATH)}missing.h
+prism/api_node.$(OBJEXT): {$(VPATH)}onigmo.h
+prism/api_node.$(OBJEXT): {$(VPATH)}oniguruma.h
+prism/api_node.$(OBJEXT): {$(VPATH)}prism/api_node.c
+prism/api_node.$(OBJEXT): {$(VPATH)}prism/ast.h
+prism/api_node.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+prism/api_node.$(OBJEXT): {$(VPATH)}prism/version.h
+prism/api_node.$(OBJEXT): {$(VPATH)}st.h
+prism/api_node.$(OBJEXT): {$(VPATH)}subst.h
+prism/api_pack.$(OBJEXT): $(hdrdir)/ruby.h
+prism/api_pack.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/api_pack.c
+prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/extension.h
+prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/node.h
+prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/options.h
+prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/pack.h
+prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/parser.h
+prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/prism.h
+prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}assert.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}backward/2/assume.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}backward/2/bool.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}backward/2/limits.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}config.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}defines.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}encoding.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}intern.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/abi.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/anyargs.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/arithmetic.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/arithmetic/char.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/arithmetic/double.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/arithmetic/fixnum.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/arithmetic/gid_t.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/arithmetic/int.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/arithmetic/intptr_t.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/arithmetic/long.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/arithmetic/long_long.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/arithmetic/mode_t.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/arithmetic/off_t.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/arithmetic/pid_t.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/arithmetic/short.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/arithmetic/size_t.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/arithmetic/st_data_t.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/arithmetic/uid_t.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/assume.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/alloc_size.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/artificial.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/cold.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/const.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/constexpr.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/deprecated.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/diagnose_if.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/enum_extensibility.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/error.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/flag_enum.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/forceinline.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/format.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/maybe_unused.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/noalias.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/returns_nonnull.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/warning.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/attr/weakref.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/cast.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/compiler_is.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/compiler_is/clang.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/compiler_is/gcc.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/compiler_is/intel.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/compiler_is/msvc.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/compiler_is/sunpro.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/compiler_since.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/config.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/constant_p.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/core.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/core/rarray.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/core/rbasic.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/core/rbignum.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/core/rclass.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/core/rdata.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/core/rfile.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/core/rhash.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/core/robject.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/core/rregexp.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/core/rstring.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/core/rstruct.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/ctype.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/dllexport.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/dosish.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/error.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/eval.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/event.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/fl_type.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/gc.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/glob.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/globals.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/has/attribute.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/has/builtin.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/has/c_attribute.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/has/cpp_attribute.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/has/declspec_attribute.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/has/extension.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/has/feature.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/has/warning.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/array.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/bignum.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/class.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/compar.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/complex.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/cont.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/dir.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/enum.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/enumerator.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/error.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/eval.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/file.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/hash.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/io.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/load.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/marshal.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/numeric.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/object.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/parse.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/proc.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/process.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/random.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/range.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/rational.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/re.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/ruby.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/select.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/select/largesize.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/signal.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/sprintf.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/string.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/struct.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/thread.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/time.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/variable.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/intern/vm.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/interpreter.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/iterator.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/memory.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/method.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/module.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/newobj.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/scan_args.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/special_consts.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/static_assert.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/stdalign.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/symbol.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/value.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/value_type.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/variable.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/warning_push.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}missing.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}onigmo.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}oniguruma.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}prism/ast.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}prism/version.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}st.h
+prism/api_pack.$(OBJEXT): {$(VPATH)}subst.h
+prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+prism/diagnostic.$(OBJEXT): {$(VPATH)}prism/ast.h
+prism/diagnostic.$(OBJEXT): {$(VPATH)}prism/diagnostic.c
+prism/diagnostic.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+prism/encoding.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/encoding.$(OBJEXT): $(top_srcdir)/prism/encoding.c
+prism/encoding.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+prism/encoding.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+prism/extension.$(OBJEXT): $(hdrdir)/ruby.h
+prism/extension.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+prism/extension.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/extension.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+prism/extension.$(OBJEXT): $(top_srcdir)/prism/extension.c
+prism/extension.$(OBJEXT): $(top_srcdir)/prism/extension.h
+prism/extension.$(OBJEXT): $(top_srcdir)/prism/node.h
+prism/extension.$(OBJEXT): $(top_srcdir)/prism/options.h
+prism/extension.$(OBJEXT): $(top_srcdir)/prism/pack.h
+prism/extension.$(OBJEXT): $(top_srcdir)/prism/parser.h
+prism/extension.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+prism/extension.$(OBJEXT): $(top_srcdir)/prism/prism.h
+prism/extension.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+prism/extension.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+prism/extension.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+prism/extension.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+prism/extension.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+prism/extension.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+prism/extension.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+prism/extension.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+prism/extension.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+prism/extension.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+prism/extension.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+prism/extension.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+prism/extension.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
+prism/extension.$(OBJEXT): {$(VPATH)}assert.h
+prism/extension.$(OBJEXT): {$(VPATH)}backward/2/assume.h
+prism/extension.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
+prism/extension.$(OBJEXT): {$(VPATH)}backward/2/bool.h
+prism/extension.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h
+prism/extension.$(OBJEXT): {$(VPATH)}backward/2/limits.h
+prism/extension.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
+prism/extension.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
+prism/extension.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
+prism/extension.$(OBJEXT): {$(VPATH)}config.h
+prism/extension.$(OBJEXT): {$(VPATH)}defines.h
+prism/extension.$(OBJEXT): {$(VPATH)}encoding.h
+prism/extension.$(OBJEXT): {$(VPATH)}intern.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/abi.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/anyargs.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/arithmetic.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/arithmetic/char.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/arithmetic/double.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/arithmetic/fixnum.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/arithmetic/gid_t.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/arithmetic/int.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/arithmetic/intptr_t.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/arithmetic/long.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/arithmetic/long_long.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/arithmetic/mode_t.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/arithmetic/off_t.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/arithmetic/pid_t.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/arithmetic/short.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/arithmetic/size_t.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/arithmetic/st_data_t.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/arithmetic/uid_t.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/assume.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/alloc_size.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/artificial.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/cold.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/const.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/constexpr.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/deprecated.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/diagnose_if.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/enum_extensibility.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/error.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/flag_enum.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/forceinline.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/format.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/maybe_unused.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/noalias.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/returns_nonnull.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/warning.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/attr/weakref.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/cast.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/compiler_is.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/compiler_is/clang.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/compiler_is/gcc.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/compiler_is/intel.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/compiler_is/msvc.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/compiler_is/sunpro.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/compiler_since.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/config.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/constant_p.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/core.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/core/rarray.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/core/rbasic.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/core/rbignum.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/core/rclass.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/core/rdata.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/core/rfile.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/core/rhash.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/core/robject.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/core/rregexp.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/core/rstring.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/core/rstruct.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/ctype.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/dllexport.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/dosish.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/error.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/eval.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/event.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/fl_type.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/gc.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/glob.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/globals.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/has/attribute.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/has/builtin.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/has/c_attribute.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/has/cpp_attribute.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/has/declspec_attribute.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/has/extension.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/has/feature.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/has/warning.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/array.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/bignum.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/class.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/compar.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/complex.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/cont.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/dir.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/enum.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/enumerator.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/error.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/eval.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/file.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/hash.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/io.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/load.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/marshal.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/numeric.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/object.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/parse.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/proc.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/process.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/random.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/range.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/rational.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/re.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/ruby.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/select.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/select/largesize.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/signal.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/sprintf.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/string.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/struct.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/thread.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/time.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/variable.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/intern/vm.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/interpreter.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/iterator.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/memory.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/method.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/module.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/newobj.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/scan_args.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/special_consts.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/static_assert.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/stdalign.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/symbol.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/value.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/value_type.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/variable.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/warning_push.h
+prism/extension.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+prism/extension.$(OBJEXT): {$(VPATH)}missing.h
+prism/extension.$(OBJEXT): {$(VPATH)}onigmo.h
+prism/extension.$(OBJEXT): {$(VPATH)}oniguruma.h
+prism/extension.$(OBJEXT): {$(VPATH)}prism/ast.h
+prism/extension.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+prism/extension.$(OBJEXT): {$(VPATH)}prism/version.h
+prism/extension.$(OBJEXT): {$(VPATH)}st.h
+prism/extension.$(OBJEXT): {$(VPATH)}subst.h
+prism/node.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/node.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+prism/node.$(OBJEXT): $(top_srcdir)/prism/node.h
+prism/node.$(OBJEXT): $(top_srcdir)/prism/options.h
+prism/node.$(OBJEXT): $(top_srcdir)/prism/pack.h
+prism/node.$(OBJEXT): $(top_srcdir)/prism/parser.h
+prism/node.$(OBJEXT): $(top_srcdir)/prism/prism.h
+prism/node.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+prism/node.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+prism/node.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+prism/node.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+prism/node.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+prism/node.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+prism/node.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+prism/node.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+prism/node.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+prism/node.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+prism/node.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+prism/node.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
+prism/node.$(OBJEXT): {$(VPATH)}prism/ast.h
+prism/node.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+prism/node.$(OBJEXT): {$(VPATH)}prism/node.c
+prism/options.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/options.$(OBJEXT): $(top_srcdir)/prism/options.c
+prism/options.$(OBJEXT): $(top_srcdir)/prism/options.h
+prism/options.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+prism/pack.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/pack.$(OBJEXT): $(top_srcdir)/prism/pack.c
+prism/pack.$(OBJEXT): $(top_srcdir)/prism/pack.h
+prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/options.h
+prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/parser.h
+prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+prism/prettyprint.$(OBJEXT): {$(VPATH)}prism/ast.h
+prism/prettyprint.$(OBJEXT): {$(VPATH)}prism/prettyprint.c
+prism/prism.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/prism.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+prism/prism.$(OBJEXT): $(top_srcdir)/prism/node.h
+prism/prism.$(OBJEXT): $(top_srcdir)/prism/options.h
+prism/prism.$(OBJEXT): $(top_srcdir)/prism/pack.h
+prism/prism.$(OBJEXT): $(top_srcdir)/prism/parser.h
+prism/prism.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+prism/prism.$(OBJEXT): $(top_srcdir)/prism/prism.c
+prism/prism.$(OBJEXT): $(top_srcdir)/prism/prism.h
+prism/prism.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+prism/prism.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+prism/prism.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+prism/prism.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+prism/prism.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+prism/prism.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+prism/prism.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+prism/prism.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+prism/prism.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+prism/prism.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+prism/prism.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+prism/prism.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+prism/prism.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
+prism/prism.$(OBJEXT): $(top_srcdir)/prism/version.h
+prism/prism.$(OBJEXT): {$(VPATH)}prism/ast.h
+prism/prism.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+prism/prism.$(OBJEXT): {$(VPATH)}prism/version.h
+prism/regexp.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/regexp.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+prism/regexp.$(OBJEXT): $(top_srcdir)/prism/options.h
+prism/regexp.$(OBJEXT): $(top_srcdir)/prism/parser.h
+prism/regexp.$(OBJEXT): $(top_srcdir)/prism/regexp.c
+prism/regexp.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+prism/regexp.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+prism/regexp.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+prism/regexp.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+prism/regexp.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+prism/regexp.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+prism/regexp.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+prism/regexp.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+prism/regexp.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+prism/regexp.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+prism/regexp.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+prism/regexp.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+prism/regexp.$(OBJEXT): {$(VPATH)}prism/ast.h
+prism/serialize.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/serialize.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+prism/serialize.$(OBJEXT): $(top_srcdir)/prism/node.h
+prism/serialize.$(OBJEXT): $(top_srcdir)/prism/options.h
+prism/serialize.$(OBJEXT): $(top_srcdir)/prism/pack.h
+prism/serialize.$(OBJEXT): $(top_srcdir)/prism/parser.h
+prism/serialize.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+prism/serialize.$(OBJEXT): $(top_srcdir)/prism/prism.h
+prism/serialize.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+prism/serialize.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+prism/serialize.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+prism/serialize.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+prism/serialize.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+prism/serialize.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+prism/serialize.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+prism/serialize.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+prism/serialize.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+prism/serialize.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+prism/serialize.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+prism/serialize.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+prism/serialize.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
+prism/serialize.$(OBJEXT): {$(VPATH)}prism/ast.h
+prism/serialize.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+prism/serialize.$(OBJEXT): {$(VPATH)}prism/serialize.c
+prism/serialize.$(OBJEXT): {$(VPATH)}prism/version.h
+prism/static_literals.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/static_literals.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+prism/static_literals.$(OBJEXT): $(top_srcdir)/prism/node.h
+prism/static_literals.$(OBJEXT): $(top_srcdir)/prism/options.h
+prism/static_literals.$(OBJEXT): $(top_srcdir)/prism/parser.h
+prism/static_literals.$(OBJEXT): $(top_srcdir)/prism/static_literals.c
+prism/static_literals.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+prism/static_literals.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+prism/static_literals.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+prism/static_literals.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+prism/static_literals.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+prism/static_literals.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+prism/static_literals.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+prism/static_literals.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+prism/static_literals.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+prism/static_literals.$(OBJEXT): {$(VPATH)}prism/ast.h
+prism/token_type.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/token_type.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+prism/token_type.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+prism/token_type.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+prism/token_type.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+prism/token_type.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+prism/token_type.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+prism/token_type.$(OBJEXT): {$(VPATH)}prism/ast.h
+prism/token_type.$(OBJEXT): {$(VPATH)}prism/token_type.c
+prism/util/pm_buffer.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/util/pm_buffer.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.c
+prism/util/pm_buffer.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+prism/util/pm_buffer.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+prism/util/pm_buffer.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+prism/util/pm_char.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/util/pm_char.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.c
+prism/util/pm_char.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+prism/util/pm_char.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+prism/util/pm_constant_pool.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/util/pm_constant_pool.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.c
+prism/util/pm_constant_pool.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+prism/util/pm_integer.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/util/pm_integer.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+prism/util/pm_integer.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+prism/util/pm_integer.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.c
+prism/util/pm_integer.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+prism/util/pm_integer.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+prism/util/pm_list.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/util/pm_list.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.c
+prism/util/pm_list.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+prism/util/pm_memchr.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/util/pm_memchr.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+prism/util/pm_memchr.$(OBJEXT): $(top_srcdir)/prism/parser.h
+prism/util/pm_memchr.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+prism/util/pm_memchr.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+prism/util/pm_memchr.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.c
+prism/util/pm_memchr.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+prism/util/pm_memchr.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+prism/util/pm_memchr.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+prism/util/pm_memchr.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+prism/util/pm_memchr.$(OBJEXT): {$(VPATH)}prism/ast.h
+prism/util/pm_newline_list.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/util/pm_newline_list.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.c
+prism/util/pm_newline_list.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+prism/util/pm_string.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/util/pm_string.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.c
+prism/util/pm_string.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+prism/util/pm_string_list.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/util/pm_string_list.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+prism/util/pm_string_list.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.c
+prism/util/pm_string_list.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+prism/util/pm_strncasecmp.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/util/pm_strncasecmp.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.c
+prism/util/pm_strncasecmp.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/options.h
+prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/parser.h
+prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.c
+prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
+prism/util/pm_strpbrk.$(OBJEXT): {$(VPATH)}prism/ast.h
+prism/util/pm_strpbrk.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+prism_init.$(OBJEXT): $(hdrdir)/ruby.h
+prism_init.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+prism_init.$(OBJEXT): $(top_srcdir)/prism/defines.h
+prism_init.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+prism_init.$(OBJEXT): $(top_srcdir)/prism/extension.h
+prism_init.$(OBJEXT): $(top_srcdir)/prism/node.h
+prism_init.$(OBJEXT): $(top_srcdir)/prism/options.h
+prism_init.$(OBJEXT): $(top_srcdir)/prism/pack.h
+prism_init.$(OBJEXT): $(top_srcdir)/prism/parser.h
+prism_init.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+prism_init.$(OBJEXT): $(top_srcdir)/prism/prism.h
+prism_init.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+prism_init.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+prism_init.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+prism_init.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+prism_init.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+prism_init.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+prism_init.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+prism_init.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+prism_init.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+prism_init.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+prism_init.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+prism_init.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+prism_init.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
+prism_init.$(OBJEXT): $(top_srcdir)/prism_init.c
+prism_init.$(OBJEXT): {$(VPATH)}assert.h
+prism_init.$(OBJEXT): {$(VPATH)}backward/2/assume.h
+prism_init.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
+prism_init.$(OBJEXT): {$(VPATH)}backward/2/bool.h
+prism_init.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h
+prism_init.$(OBJEXT): {$(VPATH)}backward/2/limits.h
+prism_init.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
+prism_init.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
+prism_init.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
+prism_init.$(OBJEXT): {$(VPATH)}config.h
+prism_init.$(OBJEXT): {$(VPATH)}defines.h
+prism_init.$(OBJEXT): {$(VPATH)}encoding.h
+prism_init.$(OBJEXT): {$(VPATH)}intern.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/abi.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/anyargs.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/arithmetic.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/arithmetic/char.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/arithmetic/double.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/arithmetic/fixnum.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/arithmetic/gid_t.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/arithmetic/int.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/arithmetic/intptr_t.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/arithmetic/long.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/arithmetic/long_long.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/arithmetic/mode_t.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/arithmetic/off_t.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/arithmetic/pid_t.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/arithmetic/short.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/arithmetic/size_t.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/arithmetic/st_data_t.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/arithmetic/uid_t.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/assume.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/alloc_size.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/artificial.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/cold.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/const.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/constexpr.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/deprecated.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/diagnose_if.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/enum_extensibility.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/error.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/flag_enum.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/forceinline.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/format.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/maybe_unused.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/noalias.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/returns_nonnull.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/warning.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/attr/weakref.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/cast.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/compiler_is.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/compiler_is/clang.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/compiler_is/gcc.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/compiler_is/intel.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/compiler_is/msvc.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/compiler_is/sunpro.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/compiler_since.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/config.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/constant_p.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/core.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/core/rarray.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/core/rbasic.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/core/rbignum.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/core/rclass.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/core/rdata.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/core/rfile.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/core/rhash.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/core/robject.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/core/rregexp.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/core/rstring.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/core/rstruct.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/ctype.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/dllexport.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/dosish.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/error.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/eval.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/event.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/fl_type.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/gc.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/glob.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/globals.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/has/attribute.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/has/builtin.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/has/c_attribute.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/has/cpp_attribute.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/has/declspec_attribute.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/has/extension.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/has/feature.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/has/warning.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/array.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/bignum.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/class.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/compar.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/complex.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/cont.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/dir.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/enum.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/enumerator.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/error.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/eval.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/file.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/hash.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/io.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/load.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/marshal.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/numeric.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/object.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/parse.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/proc.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/process.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/random.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/range.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/rational.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/re.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/ruby.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/select.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/select/largesize.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/signal.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/sprintf.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/string.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/struct.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/thread.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/time.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/variable.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/intern/vm.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/interpreter.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/iterator.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/memory.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/method.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/module.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/newobj.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/scan_args.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/special_consts.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/static_assert.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/stdalign.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/symbol.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/value.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/value_type.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/variable.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/warning_push.h
+prism_init.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+prism_init.$(OBJEXT): {$(VPATH)}missing.h
+prism_init.$(OBJEXT): {$(VPATH)}onigmo.h
+prism_init.$(OBJEXT): {$(VPATH)}oniguruma.h
+prism_init.$(OBJEXT): {$(VPATH)}prism/ast.h
+prism_init.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+prism_init.$(OBJEXT): {$(VPATH)}prism/version.h
+prism_init.$(OBJEXT): {$(VPATH)}prism_init.c
+prism_init.$(OBJEXT): {$(VPATH)}st.h
+prism_init.$(OBJEXT): {$(VPATH)}subst.h
proc.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
proc.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
proc.$(OBJEXT): $(CCAN_DIR)/list/list.h
proc.$(OBJEXT): $(CCAN_DIR)/str/str.h
proc.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+proc.$(OBJEXT): $(hdrdir)/ruby/version.h
proc.$(OBJEXT): $(top_srcdir)/internal/array.h
proc.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
proc.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -10786,6 +13018,7 @@ proc.$(OBJEXT): $(top_srcdir)/internal/gc.h
proc.$(OBJEXT): $(top_srcdir)/internal/imemo.h
proc.$(OBJEXT): $(top_srcdir)/internal/object.h
proc.$(OBJEXT): $(top_srcdir)/internal/proc.h
+proc.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
proc.$(OBJEXT): $(top_srcdir)/internal/serial.h
proc.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
proc.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -10793,6 +13026,27 @@ proc.$(OBJEXT): $(top_srcdir)/internal/symbol.h
proc.$(OBJEXT): $(top_srcdir)/internal/variable.h
proc.$(OBJEXT): $(top_srcdir)/internal/vm.h
proc.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+proc.$(OBJEXT): $(top_srcdir)/prism/defines.h
+proc.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+proc.$(OBJEXT): $(top_srcdir)/prism/node.h
+proc.$(OBJEXT): $(top_srcdir)/prism/options.h
+proc.$(OBJEXT): $(top_srcdir)/prism/pack.h
+proc.$(OBJEXT): $(top_srcdir)/prism/parser.h
+proc.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+proc.$(OBJEXT): $(top_srcdir)/prism/prism.h
+proc.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+proc.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+proc.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+proc.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+proc.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+proc.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+proc.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+proc.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+proc.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+proc.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+proc.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+proc.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+proc.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
proc.$(OBJEXT): {$(VPATH)}assert.h
proc.$(OBJEXT): {$(VPATH)}atomic.h
proc.$(OBJEXT): {$(VPATH)}backward/2/assume.h
@@ -10806,6 +13060,7 @@ proc.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
proc.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
proc.$(OBJEXT): {$(VPATH)}config.h
proc.$(OBJEXT): {$(VPATH)}constant.h
+proc.$(OBJEXT): {$(VPATH)}debug_counter.h
proc.$(OBJEXT): {$(VPATH)}defines.h
proc.$(OBJEXT): {$(VPATH)}encoding.h
proc.$(OBJEXT): {$(VPATH)}eval_intern.h
@@ -10955,6 +13210,7 @@ proc.$(OBJEXT): {$(VPATH)}internal/special_consts.h
proc.$(OBJEXT): {$(VPATH)}internal/static_assert.h
proc.$(OBJEXT): {$(VPATH)}internal/stdalign.h
proc.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+proc.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
proc.$(OBJEXT): {$(VPATH)}internal/symbol.h
proc.$(OBJEXT): {$(VPATH)}internal/value.h
proc.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -10967,16 +13223,23 @@ proc.$(OBJEXT): {$(VPATH)}missing.h
proc.$(OBJEXT): {$(VPATH)}node.h
proc.$(OBJEXT): {$(VPATH)}onigmo.h
proc.$(OBJEXT): {$(VPATH)}oniguruma.h
+proc.$(OBJEXT): {$(VPATH)}prism/ast.h
+proc.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+proc.$(OBJEXT): {$(VPATH)}prism/version.h
+proc.$(OBJEXT): {$(VPATH)}prism_compile.h
proc.$(OBJEXT): {$(VPATH)}proc.c
proc.$(OBJEXT): {$(VPATH)}ruby_assert.h
proc.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+proc.$(OBJEXT): {$(VPATH)}rubyparser.h
proc.$(OBJEXT): {$(VPATH)}shape.h
proc.$(OBJEXT): {$(VPATH)}st.h
proc.$(OBJEXT): {$(VPATH)}subst.h
proc.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
proc.$(OBJEXT): {$(VPATH)}thread_native.h
proc.$(OBJEXT): {$(VPATH)}vm_core.h
+proc.$(OBJEXT): {$(VPATH)}vm_debug.h
proc.$(OBJEXT): {$(VPATH)}vm_opts.h
+proc.$(OBJEXT): {$(VPATH)}vm_sync.h
proc.$(OBJEXT): {$(VPATH)}yjit.h
process.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
process.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
@@ -10984,6 +13247,7 @@ process.$(OBJEXT): $(CCAN_DIR)/list/list.h
process.$(OBJEXT): $(CCAN_DIR)/str/str.h
process.$(OBJEXT): $(hdrdir)/ruby.h
process.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+process.$(OBJEXT): $(hdrdir)/ruby/version.h
process.$(OBJEXT): $(top_srcdir)/internal/array.h
process.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
process.$(OBJEXT): $(top_srcdir)/internal/bignum.h
@@ -11001,6 +13265,7 @@ process.$(OBJEXT): $(top_srcdir)/internal/io.h
process.$(OBJEXT): $(top_srcdir)/internal/numeric.h
process.$(OBJEXT): $(top_srcdir)/internal/object.h
process.$(OBJEXT): $(top_srcdir)/internal/process.h
+process.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
process.$(OBJEXT): $(top_srcdir)/internal/serial.h
process.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
process.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -11174,6 +13439,7 @@ process.$(OBJEXT): {$(VPATH)}internal/special_consts.h
process.$(OBJEXT): {$(VPATH)}internal/static_assert.h
process.$(OBJEXT): {$(VPATH)}internal/stdalign.h
process.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+process.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
process.$(OBJEXT): {$(VPATH)}internal/symbol.h
process.$(OBJEXT): {$(VPATH)}internal/value.h
process.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -11191,6 +13457,7 @@ process.$(OBJEXT): {$(VPATH)}ractor.h
process.$(OBJEXT): {$(VPATH)}rjit.h
process.$(OBJEXT): {$(VPATH)}ruby_assert.h
process.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+process.$(OBJEXT): {$(VPATH)}rubyparser.h
process.$(OBJEXT): {$(VPATH)}shape.h
process.$(OBJEXT): {$(VPATH)}st.h
process.$(OBJEXT): {$(VPATH)}subst.h
@@ -11199,13 +13466,16 @@ process.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
process.$(OBJEXT): {$(VPATH)}thread_native.h
process.$(OBJEXT): {$(VPATH)}util.h
process.$(OBJEXT): {$(VPATH)}vm_core.h
+process.$(OBJEXT): {$(VPATH)}vm_debug.h
process.$(OBJEXT): {$(VPATH)}vm_opts.h
+process.$(OBJEXT): {$(VPATH)}vm_sync.h
ractor.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
ractor.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
ractor.$(OBJEXT): $(CCAN_DIR)/list/list.h
ractor.$(OBJEXT): $(CCAN_DIR)/str/str.h
ractor.$(OBJEXT): $(hdrdir)/ruby.h
ractor.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+ractor.$(OBJEXT): $(hdrdir)/ruby/version.h
ractor.$(OBJEXT): $(top_srcdir)/internal/array.h
ractor.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
ractor.$(OBJEXT): $(top_srcdir)/internal/bignum.h
@@ -11219,6 +13489,7 @@ ractor.$(OBJEXT): $(top_srcdir)/internal/hash.h
ractor.$(OBJEXT): $(top_srcdir)/internal/imemo.h
ractor.$(OBJEXT): $(top_srcdir)/internal/numeric.h
ractor.$(OBJEXT): $(top_srcdir)/internal/rational.h
+ractor.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
ractor.$(OBJEXT): $(top_srcdir)/internal/serial.h
ractor.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
ractor.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -11391,6 +13662,7 @@ ractor.$(OBJEXT): {$(VPATH)}internal/special_consts.h
ractor.$(OBJEXT): {$(VPATH)}internal/static_assert.h
ractor.$(OBJEXT): {$(VPATH)}internal/stdalign.h
ractor.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+ractor.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
ractor.$(OBJEXT): {$(VPATH)}internal/symbol.h
ractor.$(OBJEXT): {$(VPATH)}internal/value.h
ractor.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -11409,13 +13681,13 @@ ractor.$(OBJEXT): {$(VPATH)}ractor_core.h
ractor.$(OBJEXT): {$(VPATH)}rjit.h
ractor.$(OBJEXT): {$(VPATH)}ruby_assert.h
ractor.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+ractor.$(OBJEXT): {$(VPATH)}rubyparser.h
ractor.$(OBJEXT): {$(VPATH)}shape.h
ractor.$(OBJEXT): {$(VPATH)}st.h
ractor.$(OBJEXT): {$(VPATH)}subst.h
ractor.$(OBJEXT): {$(VPATH)}thread.h
ractor.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
ractor.$(OBJEXT): {$(VPATH)}thread_native.h
-ractor.$(OBJEXT): {$(VPATH)}transient_heap.h
ractor.$(OBJEXT): {$(VPATH)}variable.h
ractor.$(OBJEXT): {$(VPATH)}vm_core.h
ractor.$(OBJEXT): {$(VPATH)}vm_debug.h
@@ -11457,6 +13729,7 @@ random.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
random.$(OBJEXT): {$(VPATH)}config.h
random.$(OBJEXT): {$(VPATH)}constant.h
random.$(OBJEXT): {$(VPATH)}defines.h
+random.$(OBJEXT): {$(VPATH)}encoding.h
random.$(OBJEXT): {$(VPATH)}id.h
random.$(OBJEXT): {$(VPATH)}id_table.h
random.$(OBJEXT): {$(VPATH)}intern.h
@@ -11532,6 +13805,15 @@ random.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
random.$(OBJEXT): {$(VPATH)}internal/ctype.h
random.$(OBJEXT): {$(VPATH)}internal/dllexport.h
random.$(OBJEXT): {$(VPATH)}internal/dosish.h
+random.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
+random.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
+random.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
+random.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
+random.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
+random.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
+random.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
+random.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
+random.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
random.$(OBJEXT): {$(VPATH)}internal/error.h
random.$(OBJEXT): {$(VPATH)}internal/eval.h
random.$(OBJEXT): {$(VPATH)}internal/event.h
@@ -11594,6 +13876,7 @@ random.$(OBJEXT): {$(VPATH)}internal/special_consts.h
random.$(OBJEXT): {$(VPATH)}internal/static_assert.h
random.$(OBJEXT): {$(VPATH)}internal/stdalign.h
random.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+random.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
random.$(OBJEXT): {$(VPATH)}internal/symbol.h
random.$(OBJEXT): {$(VPATH)}internal/value.h
random.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -11604,11 +13887,14 @@ random.$(OBJEXT): {$(VPATH)}method.h
random.$(OBJEXT): {$(VPATH)}missing.h
random.$(OBJEXT): {$(VPATH)}mt19937.c
random.$(OBJEXT): {$(VPATH)}node.h
+random.$(OBJEXT): {$(VPATH)}onigmo.h
+random.$(OBJEXT): {$(VPATH)}oniguruma.h
random.$(OBJEXT): {$(VPATH)}ractor.h
random.$(OBJEXT): {$(VPATH)}random.c
random.$(OBJEXT): {$(VPATH)}random.h
random.$(OBJEXT): {$(VPATH)}ruby_assert.h
random.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+random.$(OBJEXT): {$(VPATH)}rubyparser.h
random.$(OBJEXT): {$(VPATH)}shape.h
random.$(OBJEXT): {$(VPATH)}siphash.c
random.$(OBJEXT): {$(VPATH)}siphash.h
@@ -11619,6 +13905,7 @@ random.$(OBJEXT): {$(VPATH)}thread_native.h
random.$(OBJEXT): {$(VPATH)}vm_core.h
random.$(OBJEXT): {$(VPATH)}vm_opts.h
range.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+range.$(OBJEXT): $(hdrdir)/ruby/version.h
range.$(OBJEXT): $(top_srcdir)/internal/array.h
range.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
range.$(OBJEXT): $(top_srcdir)/internal/bignum.h
@@ -11796,6 +14083,7 @@ range.$(OBJEXT): {$(VPATH)}internal/special_consts.h
range.$(OBJEXT): {$(VPATH)}internal/static_assert.h
range.$(OBJEXT): {$(VPATH)}internal/stdalign.h
range.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+range.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
range.$(OBJEXT): {$(VPATH)}internal/symbol.h
range.$(OBJEXT): {$(VPATH)}internal/value.h
range.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -11827,6 +14115,7 @@ rational.$(OBJEXT): $(top_srcdir)/internal/imemo.h
rational.$(OBJEXT): $(top_srcdir)/internal/numeric.h
rational.$(OBJEXT): $(top_srcdir)/internal/object.h
rational.$(OBJEXT): $(top_srcdir)/internal/rational.h
+rational.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
rational.$(OBJEXT): $(top_srcdir)/internal/serial.h
rational.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
rational.$(OBJEXT): $(top_srcdir)/internal/variable.h
@@ -11846,6 +14135,7 @@ rational.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
rational.$(OBJEXT): {$(VPATH)}config.h
rational.$(OBJEXT): {$(VPATH)}constant.h
rational.$(OBJEXT): {$(VPATH)}defines.h
+rational.$(OBJEXT): {$(VPATH)}encoding.h
rational.$(OBJEXT): {$(VPATH)}id.h
rational.$(OBJEXT): {$(VPATH)}id_table.h
rational.$(OBJEXT): {$(VPATH)}intern.h
@@ -11921,6 +14211,15 @@ rational.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
rational.$(OBJEXT): {$(VPATH)}internal/ctype.h
rational.$(OBJEXT): {$(VPATH)}internal/dllexport.h
rational.$(OBJEXT): {$(VPATH)}internal/dosish.h
+rational.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
+rational.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
+rational.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
+rational.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
+rational.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
+rational.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
+rational.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
+rational.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
+rational.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
rational.$(OBJEXT): {$(VPATH)}internal/error.h
rational.$(OBJEXT): {$(VPATH)}internal/eval.h
rational.$(OBJEXT): {$(VPATH)}internal/event.h
@@ -11983,6 +14282,7 @@ rational.$(OBJEXT): {$(VPATH)}internal/special_consts.h
rational.$(OBJEXT): {$(VPATH)}internal/static_assert.h
rational.$(OBJEXT): {$(VPATH)}internal/stdalign.h
rational.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+rational.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
rational.$(OBJEXT): {$(VPATH)}internal/symbol.h
rational.$(OBJEXT): {$(VPATH)}internal/value.h
rational.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -11992,9 +14292,12 @@ rational.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
rational.$(OBJEXT): {$(VPATH)}method.h
rational.$(OBJEXT): {$(VPATH)}missing.h
rational.$(OBJEXT): {$(VPATH)}node.h
+rational.$(OBJEXT): {$(VPATH)}onigmo.h
+rational.$(OBJEXT): {$(VPATH)}oniguruma.h
rational.$(OBJEXT): {$(VPATH)}rational.c
rational.$(OBJEXT): {$(VPATH)}ruby_assert.h
rational.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+rational.$(OBJEXT): {$(VPATH)}rubyparser.h
rational.$(OBJEXT): {$(VPATH)}shape.h
rational.$(OBJEXT): {$(VPATH)}st.h
rational.$(OBJEXT): {$(VPATH)}subst.h
@@ -12020,6 +14323,7 @@ re.$(OBJEXT): $(top_srcdir)/internal/imemo.h
re.$(OBJEXT): $(top_srcdir)/internal/object.h
re.$(OBJEXT): $(top_srcdir)/internal/ractor.h
re.$(OBJEXT): $(top_srcdir)/internal/re.h
+re.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
re.$(OBJEXT): $(top_srcdir)/internal/serial.h
re.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
re.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -12191,6 +14495,7 @@ re.$(OBJEXT): {$(VPATH)}internal/special_consts.h
re.$(OBJEXT): {$(VPATH)}internal/static_assert.h
re.$(OBJEXT): {$(VPATH)}internal/stdalign.h
re.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+re.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
re.$(OBJEXT): {$(VPATH)}internal/symbol.h
re.$(OBJEXT): {$(VPATH)}internal/value.h
re.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -12209,6 +14514,7 @@ re.$(OBJEXT): {$(VPATH)}regex.h
re.$(OBJEXT): {$(VPATH)}regint.h
re.$(OBJEXT): {$(VPATH)}ruby_assert.h
re.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+re.$(OBJEXT): {$(VPATH)}rubyparser.h
re.$(OBJEXT): {$(VPATH)}shape.h
re.$(OBJEXT): {$(VPATH)}st.h
re.$(OBJEXT): {$(VPATH)}subst.h
@@ -12364,6 +14670,7 @@ regcomp.$(OBJEXT): {$(VPATH)}internal/special_consts.h
regcomp.$(OBJEXT): {$(VPATH)}internal/static_assert.h
regcomp.$(OBJEXT): {$(VPATH)}internal/stdalign.h
regcomp.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+regcomp.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
regcomp.$(OBJEXT): {$(VPATH)}internal/symbol.h
regcomp.$(OBJEXT): {$(VPATH)}internal/value.h
regcomp.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -12525,6 +14832,7 @@ regenc.$(OBJEXT): {$(VPATH)}internal/special_consts.h
regenc.$(OBJEXT): {$(VPATH)}internal/static_assert.h
regenc.$(OBJEXT): {$(VPATH)}internal/stdalign.h
regenc.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+regenc.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
regenc.$(OBJEXT): {$(VPATH)}internal/symbol.h
regenc.$(OBJEXT): {$(VPATH)}internal/value.h
regenc.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -12685,6 +14993,7 @@ regerror.$(OBJEXT): {$(VPATH)}internal/special_consts.h
regerror.$(OBJEXT): {$(VPATH)}internal/static_assert.h
regerror.$(OBJEXT): {$(VPATH)}internal/stdalign.h
regerror.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+regerror.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
regerror.$(OBJEXT): {$(VPATH)}internal/symbol.h
regerror.$(OBJEXT): {$(VPATH)}internal/value.h
regerror.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -12845,6 +15154,7 @@ regexec.$(OBJEXT): {$(VPATH)}internal/special_consts.h
regexec.$(OBJEXT): {$(VPATH)}internal/static_assert.h
regexec.$(OBJEXT): {$(VPATH)}internal/stdalign.h
regexec.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+regexec.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
regexec.$(OBJEXT): {$(VPATH)}internal/symbol.h
regexec.$(OBJEXT): {$(VPATH)}internal/value.h
regexec.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -13009,6 +15319,7 @@ regparse.$(OBJEXT): {$(VPATH)}internal/special_consts.h
regparse.$(OBJEXT): {$(VPATH)}internal/static_assert.h
regparse.$(OBJEXT): {$(VPATH)}internal/stdalign.h
regparse.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+regparse.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
regparse.$(OBJEXT): {$(VPATH)}internal/symbol.h
regparse.$(OBJEXT): {$(VPATH)}internal/value.h
regparse.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -13170,6 +15481,7 @@ regsyntax.$(OBJEXT): {$(VPATH)}internal/special_consts.h
regsyntax.$(OBJEXT): {$(VPATH)}internal/static_assert.h
regsyntax.$(OBJEXT): {$(VPATH)}internal/stdalign.h
regsyntax.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+regsyntax.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
regsyntax.$(OBJEXT): {$(VPATH)}internal/symbol.h
regsyntax.$(OBJEXT): {$(VPATH)}internal/value.h
regsyntax.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -13202,6 +15514,7 @@ rjit.$(OBJEXT): $(top_srcdir)/internal/gc.h
rjit.$(OBJEXT): $(top_srcdir)/internal/hash.h
rjit.$(OBJEXT): $(top_srcdir)/internal/imemo.h
rjit.$(OBJEXT): $(top_srcdir)/internal/process.h
+rjit.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
rjit.$(OBJEXT): $(top_srcdir)/internal/serial.h
rjit.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
rjit.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -13209,6 +15522,27 @@ rjit.$(OBJEXT): $(top_srcdir)/internal/struct.h
rjit.$(OBJEXT): $(top_srcdir)/internal/variable.h
rjit.$(OBJEXT): $(top_srcdir)/internal/vm.h
rjit.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+rjit.$(OBJEXT): $(top_srcdir)/prism/defines.h
+rjit.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+rjit.$(OBJEXT): $(top_srcdir)/prism/node.h
+rjit.$(OBJEXT): $(top_srcdir)/prism/options.h
+rjit.$(OBJEXT): $(top_srcdir)/prism/pack.h
+rjit.$(OBJEXT): $(top_srcdir)/prism/parser.h
+rjit.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+rjit.$(OBJEXT): $(top_srcdir)/prism/prism.h
+rjit.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+rjit.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+rjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+rjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+rjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+rjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+rjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+rjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+rjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+rjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+rjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+rjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+rjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
rjit.$(OBJEXT): {$(VPATH)}assert.h
rjit.$(OBJEXT): {$(VPATH)}atomic.h
rjit.$(OBJEXT): {$(VPATH)}backward/2/assume.h
@@ -13377,6 +15711,7 @@ rjit.$(OBJEXT): {$(VPATH)}internal/special_consts.h
rjit.$(OBJEXT): {$(VPATH)}internal/static_assert.h
rjit.$(OBJEXT): {$(VPATH)}internal/stdalign.h
rjit.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+rjit.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
rjit.$(OBJEXT): {$(VPATH)}internal/symbol.h
rjit.$(OBJEXT): {$(VPATH)}internal/value.h
rjit.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -13389,6 +15724,10 @@ rjit.$(OBJEXT): {$(VPATH)}missing.h
rjit.$(OBJEXT): {$(VPATH)}node.h
rjit.$(OBJEXT): {$(VPATH)}onigmo.h
rjit.$(OBJEXT): {$(VPATH)}oniguruma.h
+rjit.$(OBJEXT): {$(VPATH)}prism/ast.h
+rjit.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+rjit.$(OBJEXT): {$(VPATH)}prism/version.h
+rjit.$(OBJEXT): {$(VPATH)}prism_compile.h
rjit.$(OBJEXT): {$(VPATH)}ractor.h
rjit.$(OBJEXT): {$(VPATH)}ractor_core.h
rjit.$(OBJEXT): {$(VPATH)}rjit.c
@@ -13397,6 +15736,7 @@ rjit.$(OBJEXT): {$(VPATH)}rjit.rbinc
rjit.$(OBJEXT): {$(VPATH)}rjit_c.h
rjit.$(OBJEXT): {$(VPATH)}ruby_assert.h
rjit.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+rjit.$(OBJEXT): {$(VPATH)}rubyparser.h
rjit.$(OBJEXT): {$(VPATH)}shape.h
rjit.$(OBJEXT): {$(VPATH)}st.h
rjit.$(OBJEXT): {$(VPATH)}subst.h
@@ -13436,6 +15776,27 @@ rjit_c.$(OBJEXT): $(top_srcdir)/internal/struct.h
rjit_c.$(OBJEXT): $(top_srcdir)/internal/variable.h
rjit_c.$(OBJEXT): $(top_srcdir)/internal/vm.h
rjit_c.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+rjit_c.$(OBJEXT): $(top_srcdir)/prism/defines.h
+rjit_c.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+rjit_c.$(OBJEXT): $(top_srcdir)/prism/node.h
+rjit_c.$(OBJEXT): $(top_srcdir)/prism/options.h
+rjit_c.$(OBJEXT): $(top_srcdir)/prism/pack.h
+rjit_c.$(OBJEXT): $(top_srcdir)/prism/parser.h
+rjit_c.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+rjit_c.$(OBJEXT): $(top_srcdir)/prism/prism.h
+rjit_c.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+rjit_c.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+rjit_c.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+rjit_c.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+rjit_c.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+rjit_c.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+rjit_c.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+rjit_c.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+rjit_c.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+rjit_c.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+rjit_c.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+rjit_c.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+rjit_c.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
rjit_c.$(OBJEXT): {$(VPATH)}assert.h
rjit_c.$(OBJEXT): {$(VPATH)}atomic.h
rjit_c.$(OBJEXT): {$(VPATH)}backward/2/assume.h
@@ -13603,6 +15964,7 @@ rjit_c.$(OBJEXT): {$(VPATH)}internal/special_consts.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/static_assert.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/stdalign.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+rjit_c.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/symbol.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/value.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -13615,6 +15977,10 @@ rjit_c.$(OBJEXT): {$(VPATH)}missing.h
rjit_c.$(OBJEXT): {$(VPATH)}node.h
rjit_c.$(OBJEXT): {$(VPATH)}onigmo.h
rjit_c.$(OBJEXT): {$(VPATH)}oniguruma.h
+rjit_c.$(OBJEXT): {$(VPATH)}prism/ast.h
+rjit_c.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+rjit_c.$(OBJEXT): {$(VPATH)}prism/version.h
+rjit_c.$(OBJEXT): {$(VPATH)}prism_compile.h
rjit_c.$(OBJEXT): {$(VPATH)}probes.dmyh
rjit_c.$(OBJEXT): {$(VPATH)}probes.h
rjit_c.$(OBJEXT): {$(VPATH)}probes_helper.h
@@ -13625,6 +15991,7 @@ rjit_c.$(OBJEXT): {$(VPATH)}rjit_c.rb
rjit_c.$(OBJEXT): {$(VPATH)}rjit_c.rbinc
rjit_c.$(OBJEXT): {$(VPATH)}ruby_assert.h
rjit_c.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+rjit_c.$(OBJEXT): {$(VPATH)}rubyparser.h
rjit_c.$(OBJEXT): {$(VPATH)}shape.h
rjit_c.$(OBJEXT): {$(VPATH)}st.h
rjit_c.$(OBJEXT): {$(VPATH)}subst.h
@@ -13632,9 +15999,11 @@ rjit_c.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
rjit_c.$(OBJEXT): {$(VPATH)}thread_native.h
rjit_c.$(OBJEXT): {$(VPATH)}vm_callinfo.h
rjit_c.$(OBJEXT): {$(VPATH)}vm_core.h
+rjit_c.$(OBJEXT): {$(VPATH)}vm_debug.h
rjit_c.$(OBJEXT): {$(VPATH)}vm_exec.h
rjit_c.$(OBJEXT): {$(VPATH)}vm_insnhelper.h
rjit_c.$(OBJEXT): {$(VPATH)}vm_opts.h
+rjit_c.$(OBJEXT): {$(VPATH)}vm_sync.h
rjit_c.$(OBJEXT): {$(VPATH)}yjit.h
ruby-runner.$(OBJEXT): {$(VPATH)}config.h
ruby-runner.$(OBJEXT): {$(VPATH)}internal/compiler_is.h
@@ -13657,12 +16026,16 @@ ruby.$(OBJEXT): $(hdrdir)/ruby/ruby.h
ruby.$(OBJEXT): $(hdrdir)/ruby/version.h
ruby.$(OBJEXT): $(top_srcdir)/internal/array.h
ruby.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
+ruby.$(OBJEXT): $(top_srcdir)/internal/bignum.h
+ruby.$(OBJEXT): $(top_srcdir)/internal/bits.h
ruby.$(OBJEXT): $(top_srcdir)/internal/class.h
ruby.$(OBJEXT): $(top_srcdir)/internal/cmdlineopt.h
ruby.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+ruby.$(OBJEXT): $(top_srcdir)/internal/complex.h
ruby.$(OBJEXT): $(top_srcdir)/internal/cont.h
ruby.$(OBJEXT): $(top_srcdir)/internal/error.h
ruby.$(OBJEXT): $(top_srcdir)/internal/file.h
+ruby.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
ruby.$(OBJEXT): $(top_srcdir)/internal/gc.h
ruby.$(OBJEXT): $(top_srcdir)/internal/imemo.h
ruby.$(OBJEXT): $(top_srcdir)/internal/inits.h
@@ -13670,14 +16043,40 @@ ruby.$(OBJEXT): $(top_srcdir)/internal/io.h
ruby.$(OBJEXT): $(top_srcdir)/internal/load.h
ruby.$(OBJEXT): $(top_srcdir)/internal/loadpath.h
ruby.$(OBJEXT): $(top_srcdir)/internal/missing.h
+ruby.$(OBJEXT): $(top_srcdir)/internal/numeric.h
ruby.$(OBJEXT): $(top_srcdir)/internal/object.h
ruby.$(OBJEXT): $(top_srcdir)/internal/parse.h
+ruby.$(OBJEXT): $(top_srcdir)/internal/rational.h
+ruby.$(OBJEXT): $(top_srcdir)/internal/ruby_parser.h
+ruby.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
ruby.$(OBJEXT): $(top_srcdir)/internal/serial.h
ruby.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
ruby.$(OBJEXT): $(top_srcdir)/internal/string.h
+ruby.$(OBJEXT): $(top_srcdir)/internal/thread.h
ruby.$(OBJEXT): $(top_srcdir)/internal/variable.h
ruby.$(OBJEXT): $(top_srcdir)/internal/vm.h
ruby.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+ruby.$(OBJEXT): $(top_srcdir)/prism/defines.h
+ruby.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+ruby.$(OBJEXT): $(top_srcdir)/prism/node.h
+ruby.$(OBJEXT): $(top_srcdir)/prism/options.h
+ruby.$(OBJEXT): $(top_srcdir)/prism/pack.h
+ruby.$(OBJEXT): $(top_srcdir)/prism/parser.h
+ruby.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+ruby.$(OBJEXT): $(top_srcdir)/prism/prism.h
+ruby.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+ruby.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+ruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+ruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+ruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+ruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+ruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+ruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+ruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+ruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+ruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+ruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+ruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
ruby.$(OBJEXT): {$(VPATH)}assert.h
ruby.$(OBJEXT): {$(VPATH)}atomic.h
ruby.$(OBJEXT): {$(VPATH)}backward/2/assume.h
@@ -13842,6 +16241,7 @@ ruby.$(OBJEXT): {$(VPATH)}internal/special_consts.h
ruby.$(OBJEXT): {$(VPATH)}internal/static_assert.h
ruby.$(OBJEXT): {$(VPATH)}internal/stdalign.h
ruby.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+ruby.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
ruby.$(OBJEXT): {$(VPATH)}internal/symbol.h
ruby.$(OBJEXT): {$(VPATH)}internal/value.h
ruby.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -13855,10 +16255,15 @@ ruby.$(OBJEXT): {$(VPATH)}missing.h
ruby.$(OBJEXT): {$(VPATH)}node.h
ruby.$(OBJEXT): {$(VPATH)}onigmo.h
ruby.$(OBJEXT): {$(VPATH)}oniguruma.h
+ruby.$(OBJEXT): {$(VPATH)}prism/ast.h
+ruby.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+ruby.$(OBJEXT): {$(VPATH)}prism/version.h
+ruby.$(OBJEXT): {$(VPATH)}prism_compile.h
ruby.$(OBJEXT): {$(VPATH)}rjit.h
ruby.$(OBJEXT): {$(VPATH)}ruby.c
ruby.$(OBJEXT): {$(VPATH)}ruby_assert.h
ruby.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+ruby.$(OBJEXT): {$(VPATH)}rubyparser.h
ruby.$(OBJEXT): {$(VPATH)}shape.h
ruby.$(OBJEXT): {$(VPATH)}st.h
ruby.$(OBJEXT): {$(VPATH)}subst.h
@@ -13869,6 +16274,197 @@ ruby.$(OBJEXT): {$(VPATH)}util.h
ruby.$(OBJEXT): {$(VPATH)}vm_core.h
ruby.$(OBJEXT): {$(VPATH)}vm_opts.h
ruby.$(OBJEXT): {$(VPATH)}yjit.h
+ruby_parser.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+ruby_parser.$(OBJEXT): $(top_srcdir)/internal/array.h
+ruby_parser.$(OBJEXT): $(top_srcdir)/internal/bignum.h
+ruby_parser.$(OBJEXT): $(top_srcdir)/internal/bits.h
+ruby_parser.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+ruby_parser.$(OBJEXT): $(top_srcdir)/internal/complex.h
+ruby_parser.$(OBJEXT): $(top_srcdir)/internal/error.h
+ruby_parser.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
+ruby_parser.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+ruby_parser.$(OBJEXT): $(top_srcdir)/internal/numeric.h
+ruby_parser.$(OBJEXT): $(top_srcdir)/internal/parse.h
+ruby_parser.$(OBJEXT): $(top_srcdir)/internal/rational.h
+ruby_parser.$(OBJEXT): $(top_srcdir)/internal/re.h
+ruby_parser.$(OBJEXT): $(top_srcdir)/internal/ruby_parser.h
+ruby_parser.$(OBJEXT): $(top_srcdir)/internal/serial.h
+ruby_parser.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+ruby_parser.$(OBJEXT): $(top_srcdir)/internal/string.h
+ruby_parser.$(OBJEXT): $(top_srcdir)/internal/vm.h
+ruby_parser.$(OBJEXT): {$(VPATH)}assert.h
+ruby_parser.$(OBJEXT): {$(VPATH)}backward/2/assume.h
+ruby_parser.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
+ruby_parser.$(OBJEXT): {$(VPATH)}backward/2/bool.h
+ruby_parser.$(OBJEXT): {$(VPATH)}backward/2/gcc_version_since.h
+ruby_parser.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h
+ruby_parser.$(OBJEXT): {$(VPATH)}backward/2/limits.h
+ruby_parser.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
+ruby_parser.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
+ruby_parser.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
+ruby_parser.$(OBJEXT): {$(VPATH)}config.h
+ruby_parser.$(OBJEXT): {$(VPATH)}defines.h
+ruby_parser.$(OBJEXT): {$(VPATH)}encoding.h
+ruby_parser.$(OBJEXT): {$(VPATH)}intern.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/abi.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/anyargs.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/arithmetic.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/arithmetic/char.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/arithmetic/double.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/arithmetic/fixnum.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/arithmetic/gid_t.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/arithmetic/int.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/arithmetic/intptr_t.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/arithmetic/long.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/arithmetic/long_long.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/arithmetic/mode_t.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/arithmetic/off_t.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/arithmetic/pid_t.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/arithmetic/short.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/arithmetic/size_t.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/arithmetic/st_data_t.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/arithmetic/uid_t.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/assume.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/alloc_size.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/artificial.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/cold.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/const.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/constexpr.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/deprecated.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/diagnose_if.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/enum_extensibility.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/error.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/flag_enum.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/forceinline.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/format.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/maybe_unused.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/noalias.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/returns_nonnull.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/warning.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/attr/weakref.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/cast.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/compiler_is.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/compiler_is/clang.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/compiler_is/gcc.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/compiler_is/intel.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/compiler_is/msvc.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/compiler_is/sunpro.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/compiler_since.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/config.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/constant_p.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/core.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/core/rarray.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/core/rbasic.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/core/rbignum.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/core/rclass.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/core/rdata.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/core/rfile.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/core/rhash.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/core/robject.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/core/rregexp.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/core/rstring.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/core/rstruct.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/ctype.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/dllexport.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/dosish.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/error.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/eval.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/event.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/fl_type.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/gc.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/glob.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/globals.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/has/attribute.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/has/builtin.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/has/c_attribute.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/has/cpp_attribute.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/has/declspec_attribute.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/has/extension.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/has/feature.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/has/warning.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/array.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/bignum.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/class.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/compar.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/complex.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/cont.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/dir.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/enum.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/enumerator.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/error.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/eval.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/file.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/hash.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/io.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/load.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/marshal.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/numeric.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/object.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/parse.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/proc.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/process.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/random.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/range.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/rational.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/re.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/ruby.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/select.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/select/largesize.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/signal.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/sprintf.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/string.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/struct.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/thread.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/time.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/variable.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/intern/vm.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/interpreter.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/iterator.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/memory.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/method.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/module.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/newobj.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/scan_args.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/special_consts.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/static_assert.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/stdalign.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/symbol.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/value.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/value_type.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/variable.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/warning_push.h
+ruby_parser.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+ruby_parser.$(OBJEXT): {$(VPATH)}missing.h
+ruby_parser.$(OBJEXT): {$(VPATH)}node.h
+ruby_parser.$(OBJEXT): {$(VPATH)}onigmo.h
+ruby_parser.$(OBJEXT): {$(VPATH)}oniguruma.h
+ruby_parser.$(OBJEXT): {$(VPATH)}ruby_assert.h
+ruby_parser.$(OBJEXT): {$(VPATH)}ruby_parser.c
+ruby_parser.$(OBJEXT): {$(VPATH)}rubyparser.h
+ruby_parser.$(OBJEXT): {$(VPATH)}st.h
+ruby_parser.$(OBJEXT): {$(VPATH)}subst.h
scheduler.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
scheduler.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
scheduler.$(OBJEXT): $(CCAN_DIR)/list/list.h
@@ -13879,6 +16475,7 @@ scheduler.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
scheduler.$(OBJEXT): $(top_srcdir)/internal/compilers.h
scheduler.$(OBJEXT): $(top_srcdir)/internal/gc.h
scheduler.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+scheduler.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
scheduler.$(OBJEXT): $(top_srcdir)/internal/serial.h
scheduler.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
scheduler.$(OBJEXT): $(top_srcdir)/internal/thread.h
@@ -14047,6 +16644,7 @@ scheduler.$(OBJEXT): {$(VPATH)}internal/special_consts.h
scheduler.$(OBJEXT): {$(VPATH)}internal/static_assert.h
scheduler.$(OBJEXT): {$(VPATH)}internal/stdalign.h
scheduler.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+scheduler.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
scheduler.$(OBJEXT): {$(VPATH)}internal/symbol.h
scheduler.$(OBJEXT): {$(VPATH)}internal/value.h
scheduler.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -14062,6 +16660,7 @@ scheduler.$(OBJEXT): {$(VPATH)}onigmo.h
scheduler.$(OBJEXT): {$(VPATH)}oniguruma.h
scheduler.$(OBJEXT): {$(VPATH)}ruby_assert.h
scheduler.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+scheduler.$(OBJEXT): {$(VPATH)}rubyparser.h
scheduler.$(OBJEXT): {$(VPATH)}scheduler.c
scheduler.$(OBJEXT): {$(VPATH)}shape.h
scheduler.$(OBJEXT): {$(VPATH)}st.h
@@ -14217,6 +16816,7 @@ setproctitle.$(OBJEXT): {$(VPATH)}internal/special_consts.h
setproctitle.$(OBJEXT): {$(VPATH)}internal/static_assert.h
setproctitle.$(OBJEXT): {$(VPATH)}internal/stdalign.h
setproctitle.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+setproctitle.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
setproctitle.$(OBJEXT): {$(VPATH)}internal/symbol.h
setproctitle.$(OBJEXT): {$(VPATH)}internal/value.h
setproctitle.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -14233,6 +16833,7 @@ shape.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
shape.$(OBJEXT): $(CCAN_DIR)/list/list.h
shape.$(OBJEXT): $(CCAN_DIR)/str/str.h
shape.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+shape.$(OBJEXT): $(hdrdir)/ruby/version.h
shape.$(OBJEXT): $(top_srcdir)/internal/array.h
shape.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
shape.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -14240,6 +16841,8 @@ shape.$(OBJEXT): $(top_srcdir)/internal/compilers.h
shape.$(OBJEXT): $(top_srcdir)/internal/error.h
shape.$(OBJEXT): $(top_srcdir)/internal/gc.h
shape.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+shape.$(OBJEXT): $(top_srcdir)/internal/object.h
+shape.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
shape.$(OBJEXT): $(top_srcdir)/internal/serial.h
shape.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
shape.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -14409,6 +17012,7 @@ shape.$(OBJEXT): {$(VPATH)}internal/special_consts.h
shape.$(OBJEXT): {$(VPATH)}internal/static_assert.h
shape.$(OBJEXT): {$(VPATH)}internal/stdalign.h
shape.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+shape.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
shape.$(OBJEXT): {$(VPATH)}internal/symbol.h
shape.$(OBJEXT): {$(VPATH)}internal/value.h
shape.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -14422,6 +17026,7 @@ shape.$(OBJEXT): {$(VPATH)}onigmo.h
shape.$(OBJEXT): {$(VPATH)}oniguruma.h
shape.$(OBJEXT): {$(VPATH)}ruby_assert.h
shape.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+shape.$(OBJEXT): {$(VPATH)}rubyparser.h
shape.$(OBJEXT): {$(VPATH)}shape.c
shape.$(OBJEXT): {$(VPATH)}shape.h
shape.$(OBJEXT): {$(VPATH)}st.h
@@ -14439,6 +17044,7 @@ signal.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
signal.$(OBJEXT): $(CCAN_DIR)/list/list.h
signal.$(OBJEXT): $(CCAN_DIR)/str/str.h
signal.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+signal.$(OBJEXT): $(hdrdir)/ruby/version.h
signal.$(OBJEXT): $(top_srcdir)/internal/array.h
signal.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
signal.$(OBJEXT): $(top_srcdir)/internal/compilers.h
@@ -14618,6 +17224,7 @@ signal.$(OBJEXT): {$(VPATH)}internal/special_consts.h
signal.$(OBJEXT): {$(VPATH)}internal/static_assert.h
signal.$(OBJEXT): {$(VPATH)}internal/stdalign.h
signal.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+signal.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
signal.$(OBJEXT): {$(VPATH)}internal/symbol.h
signal.$(OBJEXT): {$(VPATH)}internal/value.h
signal.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -14633,6 +17240,7 @@ signal.$(OBJEXT): {$(VPATH)}ractor.h
signal.$(OBJEXT): {$(VPATH)}ractor_core.h
signal.$(OBJEXT): {$(VPATH)}ruby_assert.h
signal.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+signal.$(OBJEXT): {$(VPATH)}rubyparser.h
signal.$(OBJEXT): {$(VPATH)}shape.h
signal.$(OBJEXT): {$(VPATH)}signal.c
signal.$(OBJEXT): {$(VPATH)}st.h
@@ -14643,6 +17251,7 @@ signal.$(OBJEXT): {$(VPATH)}vm_core.h
signal.$(OBJEXT): {$(VPATH)}vm_debug.h
signal.$(OBJEXT): {$(VPATH)}vm_opts.h
sprintf.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+sprintf.$(OBJEXT): $(hdrdir)/ruby/version.h
sprintf.$(OBJEXT): $(top_srcdir)/internal/bignum.h
sprintf.$(OBJEXT): $(top_srcdir)/internal/bits.h
sprintf.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -14822,6 +17431,7 @@ sprintf.$(OBJEXT): {$(VPATH)}internal/special_consts.h
sprintf.$(OBJEXT): {$(VPATH)}internal/static_assert.h
sprintf.$(OBJEXT): {$(VPATH)}internal/stdalign.h
sprintf.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+sprintf.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
sprintf.$(OBJEXT): {$(VPATH)}internal/symbol.h
sprintf.$(OBJEXT): {$(VPATH)}internal/value.h
sprintf.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -14844,6 +17454,7 @@ st.$(OBJEXT): $(top_srcdir)/internal/bits.h
st.$(OBJEXT): $(top_srcdir)/internal/compilers.h
st.$(OBJEXT): $(top_srcdir)/internal/hash.h
st.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
+st.$(OBJEXT): $(top_srcdir)/internal/st.h
st.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
st.$(OBJEXT): $(top_srcdir)/internal/warnings.h
st.$(OBJEXT): {$(VPATH)}assert.h
@@ -14990,9 +17601,11 @@ st.$(OBJEXT): {$(VPATH)}internal/module.h
st.$(OBJEXT): {$(VPATH)}internal/newobj.h
st.$(OBJEXT): {$(VPATH)}internal/scan_args.h
st.$(OBJEXT): {$(VPATH)}internal/special_consts.h
+st.$(OBJEXT): {$(VPATH)}internal/st.h
st.$(OBJEXT): {$(VPATH)}internal/static_assert.h
st.$(OBJEXT): {$(VPATH)}internal/stdalign.h
st.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+st.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
st.$(OBJEXT): {$(VPATH)}internal/symbol.h
st.$(OBJEXT): {$(VPATH)}internal/value.h
st.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -15167,6 +17780,7 @@ strftime.$(OBJEXT): {$(VPATH)}internal/special_consts.h
strftime.$(OBJEXT): {$(VPATH)}internal/static_assert.h
strftime.$(OBJEXT): {$(VPATH)}internal/stdalign.h
strftime.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+strftime.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
strftime.$(OBJEXT): {$(VPATH)}internal/symbol.h
strftime.$(OBJEXT): {$(VPATH)}internal/value.h
strftime.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -15186,6 +17800,7 @@ string.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
string.$(OBJEXT): $(CCAN_DIR)/list/list.h
string.$(OBJEXT): $(CCAN_DIR)/str/str.h
string.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+string.$(OBJEXT): $(hdrdir)/ruby/version.h
string.$(OBJEXT): $(top_srcdir)/internal/array.h
string.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
string.$(OBJEXT): $(top_srcdir)/internal/bignum.h
@@ -15374,6 +17989,7 @@ string.$(OBJEXT): {$(VPATH)}internal/special_consts.h
string.$(OBJEXT): {$(VPATH)}internal/static_assert.h
string.$(OBJEXT): {$(VPATH)}internal/stdalign.h
string.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+string.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
string.$(OBJEXT): {$(VPATH)}internal/symbol.h
string.$(OBJEXT): {$(VPATH)}internal/value.h
string.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -15391,6 +18007,7 @@ string.$(OBJEXT): {$(VPATH)}re.h
string.$(OBJEXT): {$(VPATH)}regex.h
string.$(OBJEXT): {$(VPATH)}ruby_assert.h
string.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+string.$(OBJEXT): {$(VPATH)}rubyparser.h
string.$(OBJEXT): {$(VPATH)}shape.h
string.$(OBJEXT): {$(VPATH)}st.h
string.$(OBJEXT): {$(VPATH)}string.c
@@ -15437,6 +18054,7 @@ struct.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
struct.$(OBJEXT): $(CCAN_DIR)/list/list.h
struct.$(OBJEXT): $(CCAN_DIR)/str/str.h
struct.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+struct.$(OBJEXT): $(hdrdir)/ruby/version.h
struct.$(OBJEXT): $(top_srcdir)/internal/array.h
struct.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
struct.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -15447,6 +18065,7 @@ struct.$(OBJEXT): $(top_srcdir)/internal/hash.h
struct.$(OBJEXT): $(top_srcdir)/internal/imemo.h
struct.$(OBJEXT): $(top_srcdir)/internal/object.h
struct.$(OBJEXT): $(top_srcdir)/internal/proc.h
+struct.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
struct.$(OBJEXT): $(top_srcdir)/internal/serial.h
struct.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
struct.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -15469,6 +18088,7 @@ struct.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
struct.$(OBJEXT): {$(VPATH)}builtin.h
struct.$(OBJEXT): {$(VPATH)}config.h
struct.$(OBJEXT): {$(VPATH)}constant.h
+struct.$(OBJEXT): {$(VPATH)}debug_counter.h
struct.$(OBJEXT): {$(VPATH)}defines.h
struct.$(OBJEXT): {$(VPATH)}encoding.h
struct.$(OBJEXT): {$(VPATH)}id.h
@@ -15617,6 +18237,7 @@ struct.$(OBJEXT): {$(VPATH)}internal/special_consts.h
struct.$(OBJEXT): {$(VPATH)}internal/static_assert.h
struct.$(OBJEXT): {$(VPATH)}internal/stdalign.h
struct.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+struct.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
struct.$(OBJEXT): {$(VPATH)}internal/symbol.h
struct.$(OBJEXT): {$(VPATH)}internal/value.h
struct.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -15630,20 +18251,23 @@ struct.$(OBJEXT): {$(VPATH)}onigmo.h
struct.$(OBJEXT): {$(VPATH)}oniguruma.h
struct.$(OBJEXT): {$(VPATH)}ruby_assert.h
struct.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+struct.$(OBJEXT): {$(VPATH)}rubyparser.h
struct.$(OBJEXT): {$(VPATH)}shape.h
struct.$(OBJEXT): {$(VPATH)}st.h
struct.$(OBJEXT): {$(VPATH)}struct.c
struct.$(OBJEXT): {$(VPATH)}subst.h
struct.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
struct.$(OBJEXT): {$(VPATH)}thread_native.h
-struct.$(OBJEXT): {$(VPATH)}transient_heap.h
struct.$(OBJEXT): {$(VPATH)}vm_core.h
+struct.$(OBJEXT): {$(VPATH)}vm_debug.h
struct.$(OBJEXT): {$(VPATH)}vm_opts.h
+struct.$(OBJEXT): {$(VPATH)}vm_sync.h
symbol.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
symbol.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
symbol.$(OBJEXT): $(CCAN_DIR)/list/list.h
symbol.$(OBJEXT): $(CCAN_DIR)/str/str.h
symbol.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+symbol.$(OBJEXT): $(hdrdir)/ruby/version.h
symbol.$(OBJEXT): $(top_srcdir)/internal/array.h
symbol.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
symbol.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -15653,6 +18277,7 @@ symbol.$(OBJEXT): $(top_srcdir)/internal/gc.h
symbol.$(OBJEXT): $(top_srcdir)/internal/hash.h
symbol.$(OBJEXT): $(top_srcdir)/internal/imemo.h
symbol.$(OBJEXT): $(top_srcdir)/internal/object.h
+symbol.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
symbol.$(OBJEXT): $(top_srcdir)/internal/serial.h
symbol.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
symbol.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -15825,6 +18450,7 @@ symbol.$(OBJEXT): {$(VPATH)}internal/special_consts.h
symbol.$(OBJEXT): {$(VPATH)}internal/static_assert.h
symbol.$(OBJEXT): {$(VPATH)}internal/stdalign.h
symbol.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+symbol.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
symbol.$(OBJEXT): {$(VPATH)}internal/symbol.h
symbol.$(OBJEXT): {$(VPATH)}internal/value.h
symbol.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -15840,6 +18466,7 @@ symbol.$(OBJEXT): {$(VPATH)}probes.dmyh
symbol.$(OBJEXT): {$(VPATH)}probes.h
symbol.$(OBJEXT): {$(VPATH)}ruby_assert.h
symbol.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+symbol.$(OBJEXT): {$(VPATH)}rubyparser.h
symbol.$(OBJEXT): {$(VPATH)}shape.h
symbol.$(OBJEXT): {$(VPATH)}st.h
symbol.$(OBJEXT): {$(VPATH)}subst.h
@@ -15859,6 +18486,7 @@ thread.$(OBJEXT): $(CCAN_DIR)/list/list.h
thread.$(OBJEXT): $(CCAN_DIR)/str/str.h
thread.$(OBJEXT): $(hdrdir)/ruby.h
thread.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+thread.$(OBJEXT): $(hdrdir)/ruby/version.h
thread.$(OBJEXT): $(top_srcdir)/internal/array.h
thread.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
thread.$(OBJEXT): $(top_srcdir)/internal/bits.h
@@ -15872,6 +18500,7 @@ thread.$(OBJEXT): $(top_srcdir)/internal/imemo.h
thread.$(OBJEXT): $(top_srcdir)/internal/io.h
thread.$(OBJEXT): $(top_srcdir)/internal/object.h
thread.$(OBJEXT): $(top_srcdir)/internal/proc.h
+thread.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
thread.$(OBJEXT): $(top_srcdir)/internal/serial.h
thread.$(OBJEXT): $(top_srcdir)/internal/signal.h
thread.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
@@ -15881,6 +18510,28 @@ thread.$(OBJEXT): $(top_srcdir)/internal/time.h
thread.$(OBJEXT): $(top_srcdir)/internal/variable.h
thread.$(OBJEXT): $(top_srcdir)/internal/vm.h
thread.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+thread.$(OBJEXT): $(top_srcdir)/prism/defines.h
+thread.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+thread.$(OBJEXT): $(top_srcdir)/prism/node.h
+thread.$(OBJEXT): $(top_srcdir)/prism/options.h
+thread.$(OBJEXT): $(top_srcdir)/prism/pack.h
+thread.$(OBJEXT): $(top_srcdir)/prism/parser.h
+thread.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+thread.$(OBJEXT): $(top_srcdir)/prism/prism.h
+thread.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+thread.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+thread.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+thread.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+thread.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+thread.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+thread.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+thread.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+thread.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+thread.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+thread.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+thread.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+thread.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
+thread.$(OBJEXT): {$(VPATH)}$(COROUTINE_H)
thread.$(OBJEXT): {$(VPATH)}assert.h
thread.$(OBJEXT): {$(VPATH)}atomic.h
thread.$(OBJEXT): {$(VPATH)}backward/2/assume.h
@@ -16048,6 +18699,7 @@ thread.$(OBJEXT): {$(VPATH)}internal/special_consts.h
thread.$(OBJEXT): {$(VPATH)}internal/static_assert.h
thread.$(OBJEXT): {$(VPATH)}internal/stdalign.h
thread.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+thread.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
thread.$(OBJEXT): {$(VPATH)}internal/symbol.h
thread.$(OBJEXT): {$(VPATH)}internal/value.h
thread.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -16061,11 +18713,16 @@ thread.$(OBJEXT): {$(VPATH)}missing.h
thread.$(OBJEXT): {$(VPATH)}node.h
thread.$(OBJEXT): {$(VPATH)}onigmo.h
thread.$(OBJEXT): {$(VPATH)}oniguruma.h
+thread.$(OBJEXT): {$(VPATH)}prism/ast.h
+thread.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+thread.$(OBJEXT): {$(VPATH)}prism/version.h
+thread.$(OBJEXT): {$(VPATH)}prism_compile.h
thread.$(OBJEXT): {$(VPATH)}ractor.h
thread.$(OBJEXT): {$(VPATH)}ractor_core.h
thread.$(OBJEXT): {$(VPATH)}rjit.h
thread.$(OBJEXT): {$(VPATH)}ruby_assert.h
thread.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+thread.$(OBJEXT): {$(VPATH)}rubyparser.h
thread.$(OBJEXT): {$(VPATH)}shape.h
thread.$(OBJEXT): {$(VPATH)}st.h
thread.$(OBJEXT): {$(VPATH)}subst.h
@@ -16074,6 +18731,7 @@ thread.$(OBJEXT): {$(VPATH)}thread.h
thread.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).c
thread.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
thread.$(OBJEXT): {$(VPATH)}thread_native.h
+thread.$(OBJEXT): {$(VPATH)}thread_pthread_mn.c
thread.$(OBJEXT): {$(VPATH)}thread_sync.c
thread.$(OBJEXT): {$(VPATH)}thread_sync.rbinc
thread.$(OBJEXT): {$(VPATH)}timev.h
@@ -16098,6 +18756,7 @@ time.$(OBJEXT): $(top_srcdir)/internal/hash.h
time.$(OBJEXT): $(top_srcdir)/internal/imemo.h
time.$(OBJEXT): $(top_srcdir)/internal/numeric.h
time.$(OBJEXT): $(top_srcdir)/internal/rational.h
+time.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
time.$(OBJEXT): $(top_srcdir)/internal/serial.h
time.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
time.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -16267,6 +18926,7 @@ time.$(OBJEXT): {$(VPATH)}internal/special_consts.h
time.$(OBJEXT): {$(VPATH)}internal/static_assert.h
time.$(OBJEXT): {$(VPATH)}internal/stdalign.h
time.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+time.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
time.$(OBJEXT): {$(VPATH)}internal/symbol.h
time.$(OBJEXT): {$(VPATH)}internal/value.h
time.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -16280,6 +18940,7 @@ time.$(OBJEXT): {$(VPATH)}onigmo.h
time.$(OBJEXT): {$(VPATH)}oniguruma.h
time.$(OBJEXT): {$(VPATH)}ruby_assert.h
time.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+time.$(OBJEXT): {$(VPATH)}rubyparser.h
time.$(OBJEXT): {$(VPATH)}shape.h
time.$(OBJEXT): {$(VPATH)}st.h
time.$(OBJEXT): {$(VPATH)}subst.h
@@ -16463,6 +19124,7 @@ transcode.$(OBJEXT): {$(VPATH)}internal/special_consts.h
transcode.$(OBJEXT): {$(VPATH)}internal/static_assert.h
transcode.$(OBJEXT): {$(VPATH)}internal/stdalign.h
transcode.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+transcode.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
transcode.$(OBJEXT): {$(VPATH)}internal/symbol.h
transcode.$(OBJEXT): {$(VPATH)}internal/value.h
transcode.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -16477,198 +19139,6 @@ transcode.$(OBJEXT): {$(VPATH)}st.h
transcode.$(OBJEXT): {$(VPATH)}subst.h
transcode.$(OBJEXT): {$(VPATH)}transcode.c
transcode.$(OBJEXT): {$(VPATH)}transcode_data.h
-transient_heap.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
-transient_heap.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
-transient_heap.$(OBJEXT): $(CCAN_DIR)/list/list.h
-transient_heap.$(OBJEXT): $(CCAN_DIR)/str/str.h
-transient_heap.$(OBJEXT): $(hdrdir)/ruby/ruby.h
-transient_heap.$(OBJEXT): $(top_srcdir)/internal/array.h
-transient_heap.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
-transient_heap.$(OBJEXT): $(top_srcdir)/internal/compilers.h
-transient_heap.$(OBJEXT): $(top_srcdir)/internal/gc.h
-transient_heap.$(OBJEXT): $(top_srcdir)/internal/imemo.h
-transient_heap.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
-transient_heap.$(OBJEXT): $(top_srcdir)/internal/serial.h
-transient_heap.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
-transient_heap.$(OBJEXT): $(top_srcdir)/internal/struct.h
-transient_heap.$(OBJEXT): $(top_srcdir)/internal/variable.h
-transient_heap.$(OBJEXT): $(top_srcdir)/internal/vm.h
-transient_heap.$(OBJEXT): $(top_srcdir)/internal/warnings.h
-transient_heap.$(OBJEXT): {$(VPATH)}assert.h
-transient_heap.$(OBJEXT): {$(VPATH)}atomic.h
-transient_heap.$(OBJEXT): {$(VPATH)}backward/2/assume.h
-transient_heap.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
-transient_heap.$(OBJEXT): {$(VPATH)}backward/2/bool.h
-transient_heap.$(OBJEXT): {$(VPATH)}backward/2/gcc_version_since.h
-transient_heap.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h
-transient_heap.$(OBJEXT): {$(VPATH)}backward/2/limits.h
-transient_heap.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
-transient_heap.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
-transient_heap.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
-transient_heap.$(OBJEXT): {$(VPATH)}config.h
-transient_heap.$(OBJEXT): {$(VPATH)}constant.h
-transient_heap.$(OBJEXT): {$(VPATH)}debug.h
-transient_heap.$(OBJEXT): {$(VPATH)}debug_counter.h
-transient_heap.$(OBJEXT): {$(VPATH)}defines.h
-transient_heap.$(OBJEXT): {$(VPATH)}id.h
-transient_heap.$(OBJEXT): {$(VPATH)}id_table.h
-transient_heap.$(OBJEXT): {$(VPATH)}intern.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/abi.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/anyargs.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/arithmetic.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/arithmetic/char.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/arithmetic/double.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/arithmetic/fixnum.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/arithmetic/gid_t.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/arithmetic/int.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/arithmetic/intptr_t.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/arithmetic/long.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/arithmetic/long_long.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/arithmetic/mode_t.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/arithmetic/off_t.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/arithmetic/pid_t.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/arithmetic/short.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/arithmetic/size_t.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/arithmetic/st_data_t.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/arithmetic/uid_t.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/assume.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/alloc_size.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/artificial.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/cold.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/const.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/constexpr.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/deprecated.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/diagnose_if.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/enum_extensibility.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/error.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/flag_enum.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/forceinline.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/format.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/maybe_unused.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/noalias.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/returns_nonnull.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/warning.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/attr/weakref.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/cast.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/compiler_is.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/compiler_is/clang.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/compiler_is/gcc.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/compiler_is/intel.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/compiler_is/msvc.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/compiler_is/sunpro.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/compiler_since.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/config.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/constant_p.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/core.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/core/rarray.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/core/rbasic.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/core/rbignum.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/core/rclass.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/core/rdata.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/core/rfile.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/core/rhash.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/core/robject.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/core/rregexp.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/core/rstring.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/core/rstruct.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/ctype.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/dllexport.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/dosish.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/error.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/eval.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/event.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/fl_type.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/gc.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/glob.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/globals.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/has/attribute.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/has/builtin.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/has/c_attribute.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/has/cpp_attribute.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/has/declspec_attribute.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/has/extension.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/has/feature.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/has/warning.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/array.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/bignum.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/class.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/compar.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/complex.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/cont.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/dir.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/enum.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/enumerator.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/error.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/eval.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/file.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/hash.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/io.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/load.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/marshal.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/numeric.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/object.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/parse.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/proc.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/process.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/random.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/range.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/rational.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/re.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/ruby.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/select.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/select/largesize.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/signal.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/sprintf.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/string.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/struct.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/thread.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/time.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/variable.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/intern/vm.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/interpreter.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/iterator.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/memory.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/method.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/module.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/newobj.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/scan_args.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/special_consts.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/static_assert.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/stdalign.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/stdbool.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/symbol.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/value.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/value_type.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/variable.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/warning_push.h
-transient_heap.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
-transient_heap.$(OBJEXT): {$(VPATH)}method.h
-transient_heap.$(OBJEXT): {$(VPATH)}missing.h
-transient_heap.$(OBJEXT): {$(VPATH)}node.h
-transient_heap.$(OBJEXT): {$(VPATH)}ruby_assert.h
-transient_heap.$(OBJEXT): {$(VPATH)}ruby_atomic.h
-transient_heap.$(OBJEXT): {$(VPATH)}shape.h
-transient_heap.$(OBJEXT): {$(VPATH)}st.h
-transient_heap.$(OBJEXT): {$(VPATH)}subst.h
-transient_heap.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
-transient_heap.$(OBJEXT): {$(VPATH)}thread_native.h
-transient_heap.$(OBJEXT): {$(VPATH)}transient_heap.c
-transient_heap.$(OBJEXT): {$(VPATH)}transient_heap.h
-transient_heap.$(OBJEXT): {$(VPATH)}vm_core.h
-transient_heap.$(OBJEXT): {$(VPATH)}vm_debug.h
-transient_heap.$(OBJEXT): {$(VPATH)}vm_opts.h
-transient_heap.$(OBJEXT): {$(VPATH)}vm_sync.h
util.$(OBJEXT): $(hdrdir)/ruby/ruby.h
util.$(OBJEXT): $(top_srcdir)/internal/compilers.h
util.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
@@ -16823,6 +19293,7 @@ util.$(OBJEXT): {$(VPATH)}internal/special_consts.h
util.$(OBJEXT): {$(VPATH)}internal/static_assert.h
util.$(OBJEXT): {$(VPATH)}internal/stdalign.h
util.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+util.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
util.$(OBJEXT): {$(VPATH)}internal/symbol.h
util.$(OBJEXT): {$(VPATH)}internal/value.h
util.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -16840,6 +19311,7 @@ variable.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
variable.$(OBJEXT): $(CCAN_DIR)/list/list.h
variable.$(OBJEXT): $(CCAN_DIR)/str/str.h
variable.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+variable.$(OBJEXT): $(hdrdir)/ruby/version.h
variable.$(OBJEXT): $(top_srcdir)/internal/array.h
variable.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
variable.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -16851,6 +19323,7 @@ variable.$(OBJEXT): $(top_srcdir)/internal/hash.h
variable.$(OBJEXT): $(top_srcdir)/internal/imemo.h
variable.$(OBJEXT): $(top_srcdir)/internal/object.h
variable.$(OBJEXT): $(top_srcdir)/internal/re.h
+variable.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
variable.$(OBJEXT): $(top_srcdir)/internal/serial.h
variable.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
variable.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -17021,6 +19494,7 @@ variable.$(OBJEXT): {$(VPATH)}internal/special_consts.h
variable.$(OBJEXT): {$(VPATH)}internal/static_assert.h
variable.$(OBJEXT): {$(VPATH)}internal/stdalign.h
variable.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+variable.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
variable.$(OBJEXT): {$(VPATH)}internal/symbol.h
variable.$(OBJEXT): {$(VPATH)}internal/value.h
variable.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -17036,12 +19510,13 @@ variable.$(OBJEXT): {$(VPATH)}ractor.h
variable.$(OBJEXT): {$(VPATH)}ractor_core.h
variable.$(OBJEXT): {$(VPATH)}ruby_assert.h
variable.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+variable.$(OBJEXT): {$(VPATH)}rubyparser.h
variable.$(OBJEXT): {$(VPATH)}shape.h
variable.$(OBJEXT): {$(VPATH)}st.h
variable.$(OBJEXT): {$(VPATH)}subst.h
+variable.$(OBJEXT): {$(VPATH)}symbol.h
variable.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
variable.$(OBJEXT): {$(VPATH)}thread_native.h
-variable.$(OBJEXT): {$(VPATH)}transient_heap.h
variable.$(OBJEXT): {$(VPATH)}util.h
variable.$(OBJEXT): {$(VPATH)}variable.c
variable.$(OBJEXT): {$(VPATH)}variable.h
@@ -17062,6 +19537,7 @@ version.$(OBJEXT): $(top_srcdir)/internal/cmdlineopt.h
version.$(OBJEXT): $(top_srcdir)/internal/compilers.h
version.$(OBJEXT): $(top_srcdir)/internal/gc.h
version.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+version.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
version.$(OBJEXT): $(top_srcdir)/internal/serial.h
version.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
version.$(OBJEXT): $(top_srcdir)/internal/variable.h
@@ -17083,6 +19559,7 @@ version.$(OBJEXT): {$(VPATH)}config.h
version.$(OBJEXT): {$(VPATH)}constant.h
version.$(OBJEXT): {$(VPATH)}debug_counter.h
version.$(OBJEXT): {$(VPATH)}defines.h
+version.$(OBJEXT): {$(VPATH)}encoding.h
version.$(OBJEXT): {$(VPATH)}id.h
version.$(OBJEXT): {$(VPATH)}id_table.h
version.$(OBJEXT): {$(VPATH)}intern.h
@@ -17158,6 +19635,15 @@ version.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
version.$(OBJEXT): {$(VPATH)}internal/ctype.h
version.$(OBJEXT): {$(VPATH)}internal/dllexport.h
version.$(OBJEXT): {$(VPATH)}internal/dosish.h
+version.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
+version.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
+version.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
+version.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
+version.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
+version.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
+version.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
+version.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
+version.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
version.$(OBJEXT): {$(VPATH)}internal/error.h
version.$(OBJEXT): {$(VPATH)}internal/eval.h
version.$(OBJEXT): {$(VPATH)}internal/event.h
@@ -17220,6 +19706,7 @@ version.$(OBJEXT): {$(VPATH)}internal/special_consts.h
version.$(OBJEXT): {$(VPATH)}internal/static_assert.h
version.$(OBJEXT): {$(VPATH)}internal/stdalign.h
version.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+version.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
version.$(OBJEXT): {$(VPATH)}internal/symbol.h
version.$(OBJEXT): {$(VPATH)}internal/value.h
version.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -17229,10 +19716,13 @@ version.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
version.$(OBJEXT): {$(VPATH)}method.h
version.$(OBJEXT): {$(VPATH)}missing.h
version.$(OBJEXT): {$(VPATH)}node.h
+version.$(OBJEXT): {$(VPATH)}onigmo.h
+version.$(OBJEXT): {$(VPATH)}oniguruma.h
version.$(OBJEXT): {$(VPATH)}revision.h
version.$(OBJEXT): {$(VPATH)}rjit.h
version.$(OBJEXT): {$(VPATH)}ruby_assert.h
version.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+version.$(OBJEXT): {$(VPATH)}rubyparser.h
version.$(OBJEXT): {$(VPATH)}shape.h
version.$(OBJEXT): {$(VPATH)}st.h
version.$(OBJEXT): {$(VPATH)}subst.h
@@ -17248,6 +19738,7 @@ vm.$(OBJEXT): $(CCAN_DIR)/list/list.h
vm.$(OBJEXT): $(CCAN_DIR)/str/str.h
vm.$(OBJEXT): $(hdrdir)/ruby.h
vm.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+vm.$(OBJEXT): $(hdrdir)/ruby/version.h
vm.$(OBJEXT): $(top_srcdir)/internal/array.h
vm.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
vm.$(OBJEXT): $(top_srcdir)/internal/bignum.h
@@ -17256,7 +19747,9 @@ vm.$(OBJEXT): $(top_srcdir)/internal/class.h
vm.$(OBJEXT): $(top_srcdir)/internal/compar.h
vm.$(OBJEXT): $(top_srcdir)/internal/compile.h
vm.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+vm.$(OBJEXT): $(top_srcdir)/internal/complex.h
vm.$(OBJEXT): $(top_srcdir)/internal/cont.h
+vm.$(OBJEXT): $(top_srcdir)/internal/encoding.h
vm.$(OBJEXT): $(top_srcdir)/internal/error.h
vm.$(OBJEXT): $(top_srcdir)/internal/eval.h
vm.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
@@ -17264,12 +19757,15 @@ vm.$(OBJEXT): $(top_srcdir)/internal/gc.h
vm.$(OBJEXT): $(top_srcdir)/internal/hash.h
vm.$(OBJEXT): $(top_srcdir)/internal/imemo.h
vm.$(OBJEXT): $(top_srcdir)/internal/inits.h
+vm.$(OBJEXT): $(top_srcdir)/internal/missing.h
vm.$(OBJEXT): $(top_srcdir)/internal/numeric.h
vm.$(OBJEXT): $(top_srcdir)/internal/object.h
vm.$(OBJEXT): $(top_srcdir)/internal/parse.h
vm.$(OBJEXT): $(top_srcdir)/internal/proc.h
vm.$(OBJEXT): $(top_srcdir)/internal/random.h
+vm.$(OBJEXT): $(top_srcdir)/internal/rational.h
vm.$(OBJEXT): $(top_srcdir)/internal/re.h
+vm.$(OBJEXT): $(top_srcdir)/internal/ruby_parser.h
vm.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
vm.$(OBJEXT): $(top_srcdir)/internal/serial.h
vm.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
@@ -17277,9 +19773,31 @@ vm.$(OBJEXT): $(top_srcdir)/internal/string.h
vm.$(OBJEXT): $(top_srcdir)/internal/struct.h
vm.$(OBJEXT): $(top_srcdir)/internal/symbol.h
vm.$(OBJEXT): $(top_srcdir)/internal/thread.h
+vm.$(OBJEXT): $(top_srcdir)/internal/transcode.h
vm.$(OBJEXT): $(top_srcdir)/internal/variable.h
vm.$(OBJEXT): $(top_srcdir)/internal/vm.h
vm.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+vm.$(OBJEXT): $(top_srcdir)/prism/defines.h
+vm.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+vm.$(OBJEXT): $(top_srcdir)/prism/node.h
+vm.$(OBJEXT): $(top_srcdir)/prism/options.h
+vm.$(OBJEXT): $(top_srcdir)/prism/pack.h
+vm.$(OBJEXT): $(top_srcdir)/prism/parser.h
+vm.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+vm.$(OBJEXT): $(top_srcdir)/prism/prism.h
+vm.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+vm.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+vm.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+vm.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+vm.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+vm.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+vm.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+vm.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+vm.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+vm.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+vm.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+vm.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+vm.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
vm.$(OBJEXT): {$(VPATH)}assert.h
vm.$(OBJEXT): {$(VPATH)}atomic.h
vm.$(OBJEXT): {$(VPATH)}backward/2/assume.h
@@ -17448,6 +19966,7 @@ vm.$(OBJEXT): {$(VPATH)}internal/special_consts.h
vm.$(OBJEXT): {$(VPATH)}internal/static_assert.h
vm.$(OBJEXT): {$(VPATH)}internal/stdalign.h
vm.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+vm.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
vm.$(OBJEXT): {$(VPATH)}internal/symbol.h
vm.$(OBJEXT): {$(VPATH)}internal/value.h
vm.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -17460,6 +19979,10 @@ vm.$(OBJEXT): {$(VPATH)}missing.h
vm.$(OBJEXT): {$(VPATH)}node.h
vm.$(OBJEXT): {$(VPATH)}onigmo.h
vm.$(OBJEXT): {$(VPATH)}oniguruma.h
+vm.$(OBJEXT): {$(VPATH)}prism/ast.h
+vm.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+vm.$(OBJEXT): {$(VPATH)}prism/version.h
+vm.$(OBJEXT): {$(VPATH)}prism_compile.h
vm.$(OBJEXT): {$(VPATH)}probes.dmyh
vm.$(OBJEXT): {$(VPATH)}probes.h
vm.$(OBJEXT): {$(VPATH)}probes_helper.h
@@ -17468,6 +19991,7 @@ vm.$(OBJEXT): {$(VPATH)}ractor_core.h
vm.$(OBJEXT): {$(VPATH)}rjit.h
vm.$(OBJEXT): {$(VPATH)}ruby_assert.h
vm.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+vm.$(OBJEXT): {$(VPATH)}rubyparser.h
vm.$(OBJEXT): {$(VPATH)}shape.h
vm.$(OBJEXT): {$(VPATH)}st.h
vm.$(OBJEXT): {$(VPATH)}subst.h
@@ -17497,6 +20021,7 @@ vm_backtrace.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
vm_backtrace.$(OBJEXT): $(CCAN_DIR)/list/list.h
vm_backtrace.$(OBJEXT): $(CCAN_DIR)/str/str.h
vm_backtrace.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+vm_backtrace.$(OBJEXT): $(hdrdir)/ruby/version.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/array.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/class.h
@@ -17504,12 +20029,34 @@ vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/compilers.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/error.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/gc.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/serial.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/string.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/variable.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/vm.h
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/defines.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/node.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/options.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/pack.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/parser.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/prism.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
vm_backtrace.$(OBJEXT): {$(VPATH)}assert.h
vm_backtrace.$(OBJEXT): {$(VPATH)}atomic.h
vm_backtrace.$(OBJEXT): {$(VPATH)}backward/2/assume.h
@@ -17524,6 +20071,7 @@ vm_backtrace.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
vm_backtrace.$(OBJEXT): {$(VPATH)}config.h
vm_backtrace.$(OBJEXT): {$(VPATH)}constant.h
vm_backtrace.$(OBJEXT): {$(VPATH)}debug.h
+vm_backtrace.$(OBJEXT): {$(VPATH)}debug_counter.h
vm_backtrace.$(OBJEXT): {$(VPATH)}defines.h
vm_backtrace.$(OBJEXT): {$(VPATH)}encoding.h
vm_backtrace.$(OBJEXT): {$(VPATH)}eval_intern.h
@@ -17673,6 +20221,7 @@ vm_backtrace.$(OBJEXT): {$(VPATH)}internal/special_consts.h
vm_backtrace.$(OBJEXT): {$(VPATH)}internal/static_assert.h
vm_backtrace.$(OBJEXT): {$(VPATH)}internal/stdalign.h
vm_backtrace.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+vm_backtrace.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
vm_backtrace.$(OBJEXT): {$(VPATH)}internal/symbol.h
vm_backtrace.$(OBJEXT): {$(VPATH)}internal/value.h
vm_backtrace.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -17685,8 +20234,13 @@ vm_backtrace.$(OBJEXT): {$(VPATH)}missing.h
vm_backtrace.$(OBJEXT): {$(VPATH)}node.h
vm_backtrace.$(OBJEXT): {$(VPATH)}onigmo.h
vm_backtrace.$(OBJEXT): {$(VPATH)}oniguruma.h
+vm_backtrace.$(OBJEXT): {$(VPATH)}prism/ast.h
+vm_backtrace.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+vm_backtrace.$(OBJEXT): {$(VPATH)}prism/version.h
+vm_backtrace.$(OBJEXT): {$(VPATH)}prism_compile.h
vm_backtrace.$(OBJEXT): {$(VPATH)}ruby_assert.h
vm_backtrace.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+vm_backtrace.$(OBJEXT): {$(VPATH)}rubyparser.h
vm_backtrace.$(OBJEXT): {$(VPATH)}shape.h
vm_backtrace.$(OBJEXT): {$(VPATH)}st.h
vm_backtrace.$(OBJEXT): {$(VPATH)}subst.h
@@ -17694,7 +20248,9 @@ vm_backtrace.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
vm_backtrace.$(OBJEXT): {$(VPATH)}thread_native.h
vm_backtrace.$(OBJEXT): {$(VPATH)}vm_backtrace.c
vm_backtrace.$(OBJEXT): {$(VPATH)}vm_core.h
+vm_backtrace.$(OBJEXT): {$(VPATH)}vm_debug.h
vm_backtrace.$(OBJEXT): {$(VPATH)}vm_opts.h
+vm_backtrace.$(OBJEXT): {$(VPATH)}vm_sync.h
vm_dump.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
vm_dump.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
vm_dump.$(OBJEXT): $(CCAN_DIR)/list/list.h
@@ -17705,11 +20261,33 @@ vm_dump.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
vm_dump.$(OBJEXT): $(top_srcdir)/internal/compilers.h
vm_dump.$(OBJEXT): $(top_srcdir)/internal/gc.h
vm_dump.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+vm_dump.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
vm_dump.$(OBJEXT): $(top_srcdir)/internal/serial.h
vm_dump.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
vm_dump.$(OBJEXT): $(top_srcdir)/internal/variable.h
vm_dump.$(OBJEXT): $(top_srcdir)/internal/vm.h
vm_dump.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+vm_dump.$(OBJEXT): $(top_srcdir)/prism/defines.h
+vm_dump.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+vm_dump.$(OBJEXT): $(top_srcdir)/prism/node.h
+vm_dump.$(OBJEXT): $(top_srcdir)/prism/options.h
+vm_dump.$(OBJEXT): $(top_srcdir)/prism/pack.h
+vm_dump.$(OBJEXT): $(top_srcdir)/prism/parser.h
+vm_dump.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+vm_dump.$(OBJEXT): $(top_srcdir)/prism/prism.h
+vm_dump.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+vm_dump.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+vm_dump.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+vm_dump.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+vm_dump.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+vm_dump.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+vm_dump.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+vm_dump.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+vm_dump.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+vm_dump.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+vm_dump.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+vm_dump.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+vm_dump.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
vm_dump.$(OBJEXT): {$(VPATH)}addr2line.h
vm_dump.$(OBJEXT): {$(VPATH)}assert.h
vm_dump.$(OBJEXT): {$(VPATH)}atomic.h
@@ -17725,6 +20303,7 @@ vm_dump.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
vm_dump.$(OBJEXT): {$(VPATH)}config.h
vm_dump.$(OBJEXT): {$(VPATH)}constant.h
vm_dump.$(OBJEXT): {$(VPATH)}defines.h
+vm_dump.$(OBJEXT): {$(VPATH)}encoding.h
vm_dump.$(OBJEXT): {$(VPATH)}id.h
vm_dump.$(OBJEXT): {$(VPATH)}id_table.h
vm_dump.$(OBJEXT): {$(VPATH)}intern.h
@@ -17800,6 +20379,15 @@ vm_dump.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
vm_dump.$(OBJEXT): {$(VPATH)}internal/ctype.h
vm_dump.$(OBJEXT): {$(VPATH)}internal/dllexport.h
vm_dump.$(OBJEXT): {$(VPATH)}internal/dosish.h
+vm_dump.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
+vm_dump.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
+vm_dump.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
+vm_dump.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
+vm_dump.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
+vm_dump.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
+vm_dump.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
+vm_dump.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
+vm_dump.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
vm_dump.$(OBJEXT): {$(VPATH)}internal/error.h
vm_dump.$(OBJEXT): {$(VPATH)}internal/eval.h
vm_dump.$(OBJEXT): {$(VPATH)}internal/event.h
@@ -17862,6 +20450,7 @@ vm_dump.$(OBJEXT): {$(VPATH)}internal/special_consts.h
vm_dump.$(OBJEXT): {$(VPATH)}internal/static_assert.h
vm_dump.$(OBJEXT): {$(VPATH)}internal/stdalign.h
vm_dump.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+vm_dump.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
vm_dump.$(OBJEXT): {$(VPATH)}internal/symbol.h
vm_dump.$(OBJEXT): {$(VPATH)}internal/value.h
vm_dump.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -17872,11 +20461,18 @@ vm_dump.$(OBJEXT): {$(VPATH)}iseq.h
vm_dump.$(OBJEXT): {$(VPATH)}method.h
vm_dump.$(OBJEXT): {$(VPATH)}missing.h
vm_dump.$(OBJEXT): {$(VPATH)}node.h
+vm_dump.$(OBJEXT): {$(VPATH)}onigmo.h
+vm_dump.$(OBJEXT): {$(VPATH)}oniguruma.h
+vm_dump.$(OBJEXT): {$(VPATH)}prism/ast.h
+vm_dump.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+vm_dump.$(OBJEXT): {$(VPATH)}prism/version.h
+vm_dump.$(OBJEXT): {$(VPATH)}prism_compile.h
vm_dump.$(OBJEXT): {$(VPATH)}procstat_vm.c
vm_dump.$(OBJEXT): {$(VPATH)}ractor.h
vm_dump.$(OBJEXT): {$(VPATH)}ractor_core.h
vm_dump.$(OBJEXT): {$(VPATH)}ruby_assert.h
vm_dump.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+vm_dump.$(OBJEXT): {$(VPATH)}rubyparser.h
vm_dump.$(OBJEXT): {$(VPATH)}shape.h
vm_dump.$(OBJEXT): {$(VPATH)}st.h
vm_dump.$(OBJEXT): {$(VPATH)}subst.h
@@ -17896,6 +20492,7 @@ vm_sync.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
vm_sync.$(OBJEXT): $(top_srcdir)/internal/compilers.h
vm_sync.$(OBJEXT): $(top_srcdir)/internal/gc.h
vm_sync.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+vm_sync.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
vm_sync.$(OBJEXT): $(top_srcdir)/internal/serial.h
vm_sync.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
vm_sync.$(OBJEXT): $(top_srcdir)/internal/thread.h
@@ -17917,6 +20514,7 @@ vm_sync.$(OBJEXT): {$(VPATH)}config.h
vm_sync.$(OBJEXT): {$(VPATH)}constant.h
vm_sync.$(OBJEXT): {$(VPATH)}debug_counter.h
vm_sync.$(OBJEXT): {$(VPATH)}defines.h
+vm_sync.$(OBJEXT): {$(VPATH)}encoding.h
vm_sync.$(OBJEXT): {$(VPATH)}id.h
vm_sync.$(OBJEXT): {$(VPATH)}id_table.h
vm_sync.$(OBJEXT): {$(VPATH)}intern.h
@@ -17992,6 +20590,15 @@ vm_sync.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
vm_sync.$(OBJEXT): {$(VPATH)}internal/ctype.h
vm_sync.$(OBJEXT): {$(VPATH)}internal/dllexport.h
vm_sync.$(OBJEXT): {$(VPATH)}internal/dosish.h
+vm_sync.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
+vm_sync.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
+vm_sync.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
+vm_sync.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
+vm_sync.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
+vm_sync.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
+vm_sync.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
+vm_sync.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
+vm_sync.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
vm_sync.$(OBJEXT): {$(VPATH)}internal/error.h
vm_sync.$(OBJEXT): {$(VPATH)}internal/eval.h
vm_sync.$(OBJEXT): {$(VPATH)}internal/event.h
@@ -18054,6 +20661,7 @@ vm_sync.$(OBJEXT): {$(VPATH)}internal/special_consts.h
vm_sync.$(OBJEXT): {$(VPATH)}internal/static_assert.h
vm_sync.$(OBJEXT): {$(VPATH)}internal/stdalign.h
vm_sync.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+vm_sync.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
vm_sync.$(OBJEXT): {$(VPATH)}internal/symbol.h
vm_sync.$(OBJEXT): {$(VPATH)}internal/value.h
vm_sync.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -18063,10 +20671,13 @@ vm_sync.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
vm_sync.$(OBJEXT): {$(VPATH)}method.h
vm_sync.$(OBJEXT): {$(VPATH)}missing.h
vm_sync.$(OBJEXT): {$(VPATH)}node.h
+vm_sync.$(OBJEXT): {$(VPATH)}onigmo.h
+vm_sync.$(OBJEXT): {$(VPATH)}oniguruma.h
vm_sync.$(OBJEXT): {$(VPATH)}ractor.h
vm_sync.$(OBJEXT): {$(VPATH)}ractor_core.h
vm_sync.$(OBJEXT): {$(VPATH)}ruby_assert.h
vm_sync.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+vm_sync.$(OBJEXT): {$(VPATH)}rubyparser.h
vm_sync.$(OBJEXT): {$(VPATH)}shape.h
vm_sync.$(OBJEXT): {$(VPATH)}st.h
vm_sync.$(OBJEXT): {$(VPATH)}subst.h
@@ -18085,17 +20696,41 @@ vm_trace.$(OBJEXT): $(hdrdir)/ruby.h
vm_trace.$(OBJEXT): $(hdrdir)/ruby/ruby.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/array.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
+vm_trace.$(OBJEXT): $(top_srcdir)/internal/bits.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/class.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/compilers.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/gc.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/hash.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/imemo.h
+vm_trace.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/serial.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/symbol.h
+vm_trace.$(OBJEXT): $(top_srcdir)/internal/thread.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/variable.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/vm.h
vm_trace.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+vm_trace.$(OBJEXT): $(top_srcdir)/prism/defines.h
+vm_trace.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+vm_trace.$(OBJEXT): $(top_srcdir)/prism/node.h
+vm_trace.$(OBJEXT): $(top_srcdir)/prism/options.h
+vm_trace.$(OBJEXT): $(top_srcdir)/prism/pack.h
+vm_trace.$(OBJEXT): $(top_srcdir)/prism/parser.h
+vm_trace.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+vm_trace.$(OBJEXT): $(top_srcdir)/prism/prism.h
+vm_trace.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+vm_trace.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+vm_trace.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+vm_trace.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+vm_trace.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+vm_trace.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+vm_trace.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+vm_trace.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+vm_trace.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+vm_trace.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+vm_trace.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+vm_trace.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+vm_trace.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
vm_trace.$(OBJEXT): {$(VPATH)}assert.h
vm_trace.$(OBJEXT): {$(VPATH)}atomic.h
vm_trace.$(OBJEXT): {$(VPATH)}backward/2/assume.h
@@ -18261,6 +20896,7 @@ vm_trace.$(OBJEXT): {$(VPATH)}internal/special_consts.h
vm_trace.$(OBJEXT): {$(VPATH)}internal/static_assert.h
vm_trace.$(OBJEXT): {$(VPATH)}internal/stdalign.h
vm_trace.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+vm_trace.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
vm_trace.$(OBJEXT): {$(VPATH)}internal/symbol.h
vm_trace.$(OBJEXT): {$(VPATH)}internal/value.h
vm_trace.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -18273,10 +20909,15 @@ vm_trace.$(OBJEXT): {$(VPATH)}missing.h
vm_trace.$(OBJEXT): {$(VPATH)}node.h
vm_trace.$(OBJEXT): {$(VPATH)}onigmo.h
vm_trace.$(OBJEXT): {$(VPATH)}oniguruma.h
+vm_trace.$(OBJEXT): {$(VPATH)}prism/ast.h
+vm_trace.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+vm_trace.$(OBJEXT): {$(VPATH)}prism/version.h
+vm_trace.$(OBJEXT): {$(VPATH)}prism_compile.h
vm_trace.$(OBJEXT): {$(VPATH)}ractor.h
vm_trace.$(OBJEXT): {$(VPATH)}rjit.h
vm_trace.$(OBJEXT): {$(VPATH)}ruby_assert.h
vm_trace.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+vm_trace.$(OBJEXT): {$(VPATH)}rubyparser.h
vm_trace.$(OBJEXT): {$(VPATH)}shape.h
vm_trace.$(OBJEXT): {$(VPATH)}st.h
vm_trace.$(OBJEXT): {$(VPATH)}subst.h
@@ -18284,7 +20925,9 @@ vm_trace.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
vm_trace.$(OBJEXT): {$(VPATH)}thread_native.h
vm_trace.$(OBJEXT): {$(VPATH)}trace_point.rbinc
vm_trace.$(OBJEXT): {$(VPATH)}vm_core.h
+vm_trace.$(OBJEXT): {$(VPATH)}vm_debug.h
vm_trace.$(OBJEXT): {$(VPATH)}vm_opts.h
+vm_trace.$(OBJEXT): {$(VPATH)}vm_sync.h
vm_trace.$(OBJEXT): {$(VPATH)}vm_trace.c
vm_trace.$(OBJEXT): {$(VPATH)}yjit.h
weakmap.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
@@ -18317,6 +20960,7 @@ weakmap.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
weakmap.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
weakmap.$(OBJEXT): {$(VPATH)}config.h
weakmap.$(OBJEXT): {$(VPATH)}defines.h
+weakmap.$(OBJEXT): {$(VPATH)}encoding.h
weakmap.$(OBJEXT): {$(VPATH)}id.h
weakmap.$(OBJEXT): {$(VPATH)}intern.h
weakmap.$(OBJEXT): {$(VPATH)}internal.h
@@ -18391,6 +21035,15 @@ weakmap.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
weakmap.$(OBJEXT): {$(VPATH)}internal/ctype.h
weakmap.$(OBJEXT): {$(VPATH)}internal/dllexport.h
weakmap.$(OBJEXT): {$(VPATH)}internal/dosish.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
weakmap.$(OBJEXT): {$(VPATH)}internal/error.h
weakmap.$(OBJEXT): {$(VPATH)}internal/eval.h
weakmap.$(OBJEXT): {$(VPATH)}internal/event.h
@@ -18453,6 +21106,7 @@ weakmap.$(OBJEXT): {$(VPATH)}internal/special_consts.h
weakmap.$(OBJEXT): {$(VPATH)}internal/static_assert.h
weakmap.$(OBJEXT): {$(VPATH)}internal/stdalign.h
weakmap.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
weakmap.$(OBJEXT): {$(VPATH)}internal/symbol.h
weakmap.$(OBJEXT): {$(VPATH)}internal/value.h
weakmap.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -18462,8 +21116,11 @@ weakmap.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
weakmap.$(OBJEXT): {$(VPATH)}method.h
weakmap.$(OBJEXT): {$(VPATH)}missing.h
weakmap.$(OBJEXT): {$(VPATH)}node.h
+weakmap.$(OBJEXT): {$(VPATH)}onigmo.h
+weakmap.$(OBJEXT): {$(VPATH)}oniguruma.h
weakmap.$(OBJEXT): {$(VPATH)}ruby_assert.h
weakmap.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+weakmap.$(OBJEXT): {$(VPATH)}rubyparser.h
weakmap.$(OBJEXT): {$(VPATH)}st.h
weakmap.$(OBJEXT): {$(VPATH)}subst.h
weakmap.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
@@ -18496,6 +21153,27 @@ yjit.$(OBJEXT): $(top_srcdir)/internal/string.h
yjit.$(OBJEXT): $(top_srcdir)/internal/variable.h
yjit.$(OBJEXT): $(top_srcdir)/internal/vm.h
yjit.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+yjit.$(OBJEXT): $(top_srcdir)/prism/defines.h
+yjit.$(OBJEXT): $(top_srcdir)/prism/encoding.h
+yjit.$(OBJEXT): $(top_srcdir)/prism/node.h
+yjit.$(OBJEXT): $(top_srcdir)/prism/options.h
+yjit.$(OBJEXT): $(top_srcdir)/prism/pack.h
+yjit.$(OBJEXT): $(top_srcdir)/prism/parser.h
+yjit.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
+yjit.$(OBJEXT): $(top_srcdir)/prism/prism.h
+yjit.$(OBJEXT): $(top_srcdir)/prism/regexp.h
+yjit.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
+yjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
+yjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
+yjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
+yjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
+yjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
+yjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
+yjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
+yjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
+yjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_string_list.h
+yjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
+yjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
yjit.$(OBJEXT): {$(VPATH)}assert.h
yjit.$(OBJEXT): {$(VPATH)}atomic.h
yjit.$(OBJEXT): {$(VPATH)}backward/2/assume.h
@@ -18664,6 +21342,7 @@ yjit.$(OBJEXT): {$(VPATH)}internal/special_consts.h
yjit.$(OBJEXT): {$(VPATH)}internal/static_assert.h
yjit.$(OBJEXT): {$(VPATH)}internal/stdalign.h
yjit.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+yjit.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
yjit.$(OBJEXT): {$(VPATH)}internal/symbol.h
yjit.$(OBJEXT): {$(VPATH)}internal/value.h
yjit.$(OBJEXT): {$(VPATH)}internal/value_type.h
@@ -18676,11 +21355,16 @@ yjit.$(OBJEXT): {$(VPATH)}missing.h
yjit.$(OBJEXT): {$(VPATH)}node.h
yjit.$(OBJEXT): {$(VPATH)}onigmo.h
yjit.$(OBJEXT): {$(VPATH)}oniguruma.h
+yjit.$(OBJEXT): {$(VPATH)}prism/ast.h
+yjit.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
+yjit.$(OBJEXT): {$(VPATH)}prism/version.h
+yjit.$(OBJEXT): {$(VPATH)}prism_compile.h
yjit.$(OBJEXT): {$(VPATH)}probes.dmyh
yjit.$(OBJEXT): {$(VPATH)}probes.h
yjit.$(OBJEXT): {$(VPATH)}probes_helper.h
yjit.$(OBJEXT): {$(VPATH)}ruby_assert.h
yjit.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+yjit.$(OBJEXT): {$(VPATH)}rubyparser.h
yjit.$(OBJEXT): {$(VPATH)}shape.h
yjit.$(OBJEXT): {$(VPATH)}st.h
yjit.$(OBJEXT): {$(VPATH)}subst.h
diff --git a/compar.c b/compar.c
index 2b34ebb062..081b4e2dea 100644
--- a/compar.c
+++ b/compar.c
@@ -187,6 +187,12 @@ cmp_between(VALUE x, VALUE min, VALUE max)
* 'd'.clamp('a', 'f') #=> 'd'
* 'z'.clamp('a', 'f') #=> 'f'
*
+ * If _min_ is +nil+, it is considered smaller than _obj_,
+ * and if _max_ is +nil+, it is considered greater than _obj_.
+ *
+ * -20.clamp(0, nil) #=> 0
+ * 523.clamp(nil, 100) #=> 100
+ *
* In <code>(range)</code> form, returns _range.begin_ if _obj_
* <code><=></code> _range.begin_ is less than zero, _range.end_
* if _obj_ <code><=></code> _range.end_ is greater than zero, and
@@ -257,25 +263,28 @@ cmp_clamp(int argc, VALUE *argv, VALUE x)
* <code>==</code>, <code>>=</code>, and <code>></code>) and the
* method <code>between?</code>.
*
- * class SizeMatters
+ * class StringSorter
* include Comparable
+ *
* attr :str
* def <=>(other)
* str.size <=> other.str.size
* end
+ *
* def initialize(str)
* @str = str
* end
+ *
* def inspect
* @str
* end
* end
*
- * s1 = SizeMatters.new("Z")
- * s2 = SizeMatters.new("YY")
- * s3 = SizeMatters.new("XXX")
- * s4 = SizeMatters.new("WWWW")
- * s5 = SizeMatters.new("VVVVV")
+ * s1 = StringSorter.new("Z")
+ * s2 = StringSorter.new("YY")
+ * s3 = StringSorter.new("XXX")
+ * s4 = StringSorter.new("WWWW")
+ * s5 = StringSorter.new("VVVVV")
*
* s1 < s2 #=> true
* s4.between?(s1, s3) #=> false
diff --git a/compile.c b/compile.c
index 7ceb936f06..588ce0c7ee 100644
--- a/compile.c
+++ b/compile.c
@@ -26,19 +26,23 @@
#include "internal/error.h"
#include "internal/gc.h"
#include "internal/hash.h"
+#include "internal/io.h"
#include "internal/numeric.h"
#include "internal/object.h"
#include "internal/rational.h"
#include "internal/re.h"
+#include "internal/ruby_parser.h"
#include "internal/symbol.h"
#include "internal/thread.h"
#include "internal/variable.h"
#include "iseq.h"
+#include "ruby/ractor.h"
#include "ruby/re.h"
#include "ruby/util.h"
#include "vm_core.h"
#include "vm_callinfo.h"
#include "vm_debug.h"
+#include "yjit.h"
#include "builtin.h"
#include "insns.inc"
@@ -118,7 +122,7 @@ struct ensure_range {
};
struct iseq_compile_data_ensure_node_stack {
- const NODE *ensure_node;
+ const void *ensure_node;
struct iseq_compile_data_ensure_node_stack *prev;
struct ensure_range *erange;
};
@@ -219,30 +223,34 @@ const ID rb_iseq_shared_exc_local_tbl[] = {idERROR_INFO};
/* add an instruction */
#define ADD_INSN(seq, line_node, insn) \
- ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (line_node), BIN(insn), 0))
+ ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, nd_line(line_node), nd_node_id(line_node), BIN(insn), 0))
+
+/* add an instruction with the given line number and node id */
+#define ADD_SYNTHETIC_INSN(seq, line_no, node_id, insn) \
+ ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (line_no), (node_id), BIN(insn), 0))
/* insert an instruction before next */
-#define INSERT_BEFORE_INSN(next, line_node, insn) \
- ELEM_INSERT_PREV(&(next)->link, (LINK_ELEMENT *) new_insn_body(iseq, (line_node), BIN(insn), 0))
+#define INSERT_BEFORE_INSN(next, line_no, node_id, insn) \
+ ELEM_INSERT_PREV(&(next)->link, (LINK_ELEMENT *) new_insn_body(iseq, line_no, node_id, BIN(insn), 0))
/* insert an instruction after prev */
-#define INSERT_AFTER_INSN(prev, line_node, insn) \
- ELEM_INSERT_NEXT(&(prev)->link, (LINK_ELEMENT *) new_insn_body(iseq, (line_node), BIN(insn), 0))
+#define INSERT_AFTER_INSN(prev, line_no, node_id, insn) \
+ ELEM_INSERT_NEXT(&(prev)->link, (LINK_ELEMENT *) new_insn_body(iseq, line_no, node_id, BIN(insn), 0))
/* add an instruction with some operands (1, 2, 3, 5) */
#define ADD_INSN1(seq, line_node, insn, op1) \
ADD_ELEM((seq), (LINK_ELEMENT *) \
- new_insn_body(iseq, (line_node), BIN(insn), 1, (VALUE)(op1)))
+ new_insn_body(iseq, nd_line(line_node), nd_node_id(line_node), BIN(insn), 1, (VALUE)(op1)))
/* insert an instruction with some operands (1, 2, 3, 5) before next */
-#define INSERT_BEFORE_INSN1(next, line_node, insn, op1) \
+#define INSERT_BEFORE_INSN1(next, line_no, node_id, insn, op1) \
ELEM_INSERT_PREV(&(next)->link, (LINK_ELEMENT *) \
- new_insn_body(iseq, (line_node), BIN(insn), 1, (VALUE)(op1)))
+ new_insn_body(iseq, line_no, node_id, BIN(insn), 1, (VALUE)(op1)))
/* insert an instruction with some operands (1, 2, 3, 5) after prev */
-#define INSERT_AFTER_INSN1(prev, line_node, insn, op1) \
+#define INSERT_AFTER_INSN1(prev, line_no, node_id, insn, op1) \
ELEM_INSERT_NEXT(&(prev)->link, (LINK_ELEMENT *) \
- new_insn_body(iseq, (line_node), BIN(insn), 1, (VALUE)(op1)))
+ new_insn_body(iseq, line_no, node_id, BIN(insn), 1, (VALUE)(op1)))
#define LABEL_REF(label) ((label)->refcnt++)
@@ -251,11 +259,11 @@ const ID rb_iseq_shared_exc_local_tbl[] = {idERROR_INFO};
#define ADD_INSN2(seq, line_node, insn, op1, op2) \
ADD_ELEM((seq), (LINK_ELEMENT *) \
- new_insn_body(iseq, (line_node), BIN(insn), 2, (VALUE)(op1), (VALUE)(op2)))
+ new_insn_body(iseq, nd_line(line_node), nd_node_id(line_node), BIN(insn), 2, (VALUE)(op1), (VALUE)(op2)))
#define ADD_INSN3(seq, line_node, insn, op1, op2, op3) \
ADD_ELEM((seq), (LINK_ELEMENT *) \
- new_insn_body(iseq, (line_node), BIN(insn), 3, (VALUE)(op1), (VALUE)(op2), (VALUE)(op3)))
+ new_insn_body(iseq, nd_line(line_node), nd_node_id(line_node), BIN(insn), 3, (VALUE)(op1), (VALUE)(op2), (VALUE)(op3)))
/* Specific Insn factory */
#define ADD_SEND(seq, line_node, id, argc) \
@@ -277,7 +285,7 @@ const ID rb_iseq_shared_exc_local_tbl[] = {idERROR_INFO};
ADD_SEND_R((seq), (line_node), (id), (argc), (block), (VALUE)INT2FIX(VM_CALL_FCALL), NULL)
#define ADD_SEND_R(seq, line_node, id, argc, block, flag, keywords) \
- ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_send(iseq, (line_node), (id), (VALUE)(argc), (block), (VALUE)(flag), (keywords)))
+ ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_send(iseq, nd_line(line_node), nd_node_id(line_node), (id), (VALUE)(argc), (block), (VALUE)(flag), (keywords)))
#define ADD_TRACE(seq, event) \
ADD_ELEM((seq), (LINK_ELEMENT *)new_trace_body(iseq, (event), 0))
@@ -332,10 +340,10 @@ static void iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NOD
(debug_compile("== " desc "\n", \
iseq_compile_each(iseq, (anchor), (node), (popped))))
-#define COMPILE_RECV(anchor, desc, node) \
+#define COMPILE_RECV(anchor, desc, node, recv) \
(private_recv_p(node) ? \
(ADD_INSN(anchor, node, putself), VM_CALL_FCALL) : \
- COMPILE(anchor, desc, node->nd_recv) ? 0 : -1)
+ COMPILE(anchor, desc, recv) ? 0 : -1)
#define OPERAND_AT(insn, idx) \
(((INSN*)(insn))->operands[(idx)])
@@ -470,7 +478,7 @@ static void dump_disasm_list(const LINK_ELEMENT *elem);
static int insn_data_length(INSN *iobj);
static int calc_sp_depth(int depth, INSN *iobj);
-static INSN *new_insn_body(rb_iseq_t *iseq, const NODE *const line_node, enum ruby_vminsn_type insn_id, int argc, ...);
+static INSN *new_insn_body(rb_iseq_t *iseq, int line_no, int node_id, enum ruby_vminsn_type insn_id, int argc, ...);
static LABEL *new_label_body(rb_iseq_t *iseq, long line);
static ADJUST *new_adjust_body(rb_iseq_t *iseq, LABEL *label, int line);
static TRACE *new_trace_body(rb_iseq_t *iseq, rb_event_flag_t event, long data);
@@ -486,7 +494,6 @@ static int iseq_set_local_table(rb_iseq_t *iseq, const rb_ast_id_table_t *tbl);
static int iseq_set_exception_local_table(rb_iseq_t *iseq);
static int iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const anchor, const NODE *const node);
-static int iseq_set_sequence_stackcaching(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
static int iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
static int iseq_set_exception_table(rb_iseq_t *iseq);
static int iseq_set_optargs_table(rb_iseq_t *iseq);
@@ -605,11 +612,13 @@ branch_coverage_valid_p(rb_iseq_t *iseq, int first_line)
return 1;
}
+#define PTR2NUM(x) (rb_int2inum((intptr_t)(void *)(x)))
+
static VALUE
-decl_branch_base(rb_iseq_t *iseq, const NODE *node, const char *type)
+decl_branch_base(rb_iseq_t *iseq, VALUE key, const rb_code_location_t *loc, const char *type)
{
- const int first_lineno = nd_first_lineno(node), first_column = nd_first_column(node);
- const int last_lineno = nd_last_lineno(node), last_column = nd_last_column(node);
+ const int first_lineno = loc->beg_pos.lineno, first_column = loc->beg_pos.column;
+ const int last_lineno = loc->end_pos.lineno, last_column = loc->end_pos.column;
if (!branch_coverage_valid_p(iseq, first_lineno)) return Qundef;
@@ -622,7 +631,6 @@ decl_branch_base(rb_iseq_t *iseq, const NODE *node, const char *type)
*/
VALUE structure = RARRAY_AREF(ISEQ_BRANCH_COVERAGE(iseq), 0);
- VALUE key = (VALUE)node | 1; // FIXNUM for hash key
VALUE branch_base = rb_hash_aref(structure, key);
VALUE branches;
@@ -655,10 +663,10 @@ generate_dummy_line_node(int lineno, int node_id)
}
static void
-add_trace_branch_coverage(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *node, int branch_id, const char *type, VALUE branches)
+add_trace_branch_coverage(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const rb_code_location_t *loc, int node_id, int branch_id, const char *type, VALUE branches)
{
- const int first_lineno = nd_first_lineno(node), first_column = nd_first_column(node);
- const int last_lineno = nd_last_lineno(node), last_column = nd_last_column(node);
+ const int first_lineno = loc->beg_pos.lineno, first_column = loc->beg_pos.column;
+ const int last_lineno = loc->end_pos.lineno, last_column = loc->end_pos.column;
if (!branch_coverage_valid_p(iseq, first_lineno)) return;
@@ -692,9 +700,7 @@ add_trace_branch_coverage(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *n
}
ADD_TRACE_WITH_DATA(seq, RUBY_EVENT_COVERAGE_BRANCH, counter_idx);
-
- NODE dummy_line_node = generate_dummy_line_node(last_lineno, nd_node_id(node));
- ADD_INSN(seq, &dummy_line_node, nop);
+ ADD_SYNTHETIC_INSN(seq, last_lineno, node_id, nop);
}
#define ISEQ_LAST_LINE(iseq) (ISEQ_COMPILE_DATA(iseq)->last_line)
@@ -721,6 +727,129 @@ validate_labels(rb_iseq_t *iseq, st_table *labels_table)
st_free_table(labels_table);
}
+static NODE *
+get_nd_recv(const NODE *node)
+{
+ switch (nd_type(node)) {
+ case NODE_CALL:
+ return RNODE_CALL(node)->nd_recv;
+ case NODE_OPCALL:
+ return RNODE_OPCALL(node)->nd_recv;
+ case NODE_FCALL:
+ return 0;
+ case NODE_QCALL:
+ return RNODE_QCALL(node)->nd_recv;
+ case NODE_VCALL:
+ return 0;
+ case NODE_ATTRASGN:
+ return RNODE_ATTRASGN(node)->nd_recv;
+ case NODE_OP_ASGN1:
+ return RNODE_OP_ASGN1(node)->nd_recv;
+ case NODE_OP_ASGN2:
+ return RNODE_OP_ASGN2(node)->nd_recv;
+ default:
+ rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
+ }
+}
+
+static ID
+get_node_call_nd_mid(const NODE *node)
+{
+ switch (nd_type(node)) {
+ case NODE_CALL:
+ return RNODE_CALL(node)->nd_mid;
+ case NODE_OPCALL:
+ return RNODE_OPCALL(node)->nd_mid;
+ case NODE_FCALL:
+ return RNODE_FCALL(node)->nd_mid;
+ case NODE_QCALL:
+ return RNODE_QCALL(node)->nd_mid;
+ case NODE_VCALL:
+ return RNODE_VCALL(node)->nd_mid;
+ case NODE_ATTRASGN:
+ return RNODE_ATTRASGN(node)->nd_mid;
+ default:
+ rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
+ }
+}
+
+static NODE *
+get_nd_args(const NODE *node)
+{
+ switch (nd_type(node)) {
+ case NODE_CALL:
+ return RNODE_CALL(node)->nd_args;
+ case NODE_OPCALL:
+ return RNODE_OPCALL(node)->nd_args;
+ case NODE_FCALL:
+ return RNODE_FCALL(node)->nd_args;
+ case NODE_QCALL:
+ return RNODE_QCALL(node)->nd_args;
+ case NODE_VCALL:
+ return 0;
+ case NODE_ATTRASGN:
+ return RNODE_ATTRASGN(node)->nd_args;
+ default:
+ rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
+ }
+}
+
+static ID
+get_node_colon_nd_mid(const NODE *node)
+{
+ switch (nd_type(node)) {
+ case NODE_COLON2:
+ return RNODE_COLON2(node)->nd_mid;
+ case NODE_COLON3:
+ return RNODE_COLON3(node)->nd_mid;
+ default:
+ rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
+ }
+}
+
+static ID
+get_nd_vid(const NODE *node)
+{
+ switch (nd_type(node)) {
+ case NODE_LASGN:
+ return RNODE_LASGN(node)->nd_vid;
+ case NODE_DASGN:
+ return RNODE_DASGN(node)->nd_vid;
+ case NODE_IASGN:
+ return RNODE_IASGN(node)->nd_vid;
+ case NODE_CVASGN:
+ return RNODE_CVASGN(node)->nd_vid;
+ default:
+ rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
+ }
+}
+
+static NODE *
+get_nd_value(const NODE *node)
+{
+ switch (nd_type(node)) {
+ case NODE_LASGN:
+ return RNODE_LASGN(node)->nd_value;
+ case NODE_DASGN:
+ return RNODE_DASGN(node)->nd_value;
+ default:
+ rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
+ }
+}
+
+static VALUE
+get_string_value(const NODE *node)
+{
+ switch (nd_type(node)) {
+ case NODE_STR:
+ return rb_node_str_string_val(node);
+ case NODE_FILE:
+ return rb_node_file_path_val(node);
+ default:
+ rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
+ }
+}
+
VALUE
rb_iseq_compile_callback(rb_iseq_t *iseq, const struct rb_iseq_new_with_callback_callback_func * ifunc)
{
@@ -729,8 +858,7 @@ rb_iseq_compile_callback(rb_iseq_t *iseq, const struct rb_iseq_new_with_callback
(*ifunc->func)(iseq, ret, ifunc->data);
- NODE dummy_line_node = generate_dummy_line_node(ISEQ_COMPILE_DATA(iseq)->last_line, -1);
- ADD_INSN(ret, &dummy_line_node, leave);
+ ADD_SYNTHETIC_INSN(ret, ISEQ_COMPILE_DATA(iseq)->last_line, -1, leave);
CHECK(iseq_setup_insn(iseq, ret));
return iseq_setup(iseq, ret);
@@ -742,10 +870,6 @@ rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node)
DECL_ANCHOR(ret);
INIT_ANCHOR(ret);
- if (IMEMO_TYPE_P(node, imemo_ifunc)) {
- rb_raise(rb_eArgError, "unexpected imemo_ifunc");
- }
-
if (node == 0) {
NO_CHECK(COMPILE(ret, "nil", node));
iseq_set_local_table(iseq, 0);
@@ -753,8 +877,8 @@ rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node)
/* assume node is T_NODE */
else if (nd_type_p(node, NODE_SCOPE)) {
/* iseq type of top, method, class, block */
- iseq_set_local_table(iseq, node->nd_tbl);
- iseq_set_arguments(iseq, ret, node->nd_args);
+ iseq_set_local_table(iseq, RNODE_SCOPE(node)->nd_tbl);
+ iseq_set_arguments(iseq, ret, (NODE *)RNODE_SCOPE(node)->nd_args);
switch (ISEQ_BODY(iseq)->type) {
case ISEQ_TYPE_BLOCK:
@@ -766,10 +890,9 @@ rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node)
end->rescued = LABEL_RESCUE_END;
ADD_TRACE(ret, RUBY_EVENT_B_CALL);
- NODE dummy_line_node = generate_dummy_line_node(ISEQ_BODY(iseq)->location.first_lineno, -1);
- ADD_INSN (ret, &dummy_line_node, nop);
+ ADD_SYNTHETIC_INSN(ret, ISEQ_BODY(iseq)->location.first_lineno, -1, nop);
ADD_LABEL(ret, start);
- CHECK(COMPILE(ret, "block body", node->nd_body));
+ CHECK(COMPILE(ret, "block body", RNODE_SCOPE(node)->nd_body));
ADD_LABEL(ret, end);
ADD_TRACE(ret, RUBY_EVENT_B_RETURN);
ISEQ_COMPILE_DATA(iseq)->last_line = ISEQ_BODY(iseq)->location.code_location.end_pos.lineno;
@@ -782,23 +905,23 @@ rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node)
case ISEQ_TYPE_CLASS:
{
ADD_TRACE(ret, RUBY_EVENT_CLASS);
- CHECK(COMPILE(ret, "scoped node", node->nd_body));
+ CHECK(COMPILE(ret, "scoped node", RNODE_SCOPE(node)->nd_body));
ADD_TRACE(ret, RUBY_EVENT_END);
ISEQ_COMPILE_DATA(iseq)->last_line = nd_line(node);
break;
}
case ISEQ_TYPE_METHOD:
{
- ISEQ_COMPILE_DATA(iseq)->root_node = node->nd_body;
+ ISEQ_COMPILE_DATA(iseq)->root_node = RNODE_SCOPE(node)->nd_body;
ADD_TRACE(ret, RUBY_EVENT_CALL);
- CHECK(COMPILE(ret, "scoped node", node->nd_body));
- ISEQ_COMPILE_DATA(iseq)->root_node = node->nd_body;
+ CHECK(COMPILE(ret, "scoped node", RNODE_SCOPE(node)->nd_body));
+ ISEQ_COMPILE_DATA(iseq)->root_node = RNODE_SCOPE(node)->nd_body;
ADD_TRACE(ret, RUBY_EVENT_RETURN);
ISEQ_COMPILE_DATA(iseq)->last_line = nd_line(node);
break;
}
default: {
- CHECK(COMPILE(ret, "scoped node", node->nd_body));
+ CHECK(COMPILE(ret, "scoped node", RNODE_SCOPE(node)->nd_body));
break;
}
}
@@ -841,8 +964,7 @@ rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node)
ADD_INSN1(ret, &dummy_line_node, throw, INT2FIX(0) /* continue throw */ );
}
else {
- NODE dummy_line_node = generate_dummy_line_node(ISEQ_COMPILE_DATA(iseq)->last_line, -1);
- ADD_INSN(ret, &dummy_line_node, leave);
+ ADD_SYNTHETIC_INSN(ret, ISEQ_COMPILE_DATA(iseq)->last_line, -1, leave);
}
#if OPT_SUPPORT_JOKE
@@ -872,6 +994,12 @@ rb_iseq_translate_threaded_code(rb_iseq_t *iseq)
}
FL_SET((VALUE)iseq, ISEQ_TRANSLATED);
#endif
+
+#if USE_YJIT
+ rb_yjit_live_iseq_count++;
+ rb_yjit_iseq_alloc_count++;
+#endif
+
return COMPILE_OK;
}
@@ -1243,9 +1371,34 @@ new_adjust_body(rb_iseq_t *iseq, LABEL *label, int line)
return adjust;
}
+static void
+iseq_insn_each_markable_object(INSN *insn, void (*func)(VALUE, VALUE), VALUE data)
+{
+ const char *types = insn_op_types(insn->insn_id);
+ for (int j = 0; types[j]; j++) {
+ char type = types[j];
+ switch (type) {
+ case TS_CDHASH:
+ case TS_ISEQ:
+ case TS_VALUE:
+ case TS_IC: // constant path array
+ case TS_CALLDATA: // ci is stored.
+ func(OPERAND_AT(insn, j), data);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void
+iseq_insn_each_object_write_barrier(VALUE obj, VALUE iseq)
+{
+ RB_OBJ_WRITTEN(iseq, Qundef, obj);
+}
+
static INSN *
-new_insn_core(rb_iseq_t *iseq, const NODE *line_node,
- int insn_id, int argc, VALUE *argv)
+new_insn_core(rb_iseq_t *iseq, int line_no, int node_id, int insn_id, int argc, VALUE *argv)
{
INSN *iobj = compile_data_alloc_insn(iseq);
@@ -1254,17 +1407,20 @@ new_insn_core(rb_iseq_t *iseq, const NODE *line_node,
iobj->link.type = ISEQ_ELEMENT_INSN;
iobj->link.next = 0;
iobj->insn_id = insn_id;
- iobj->insn_info.line_no = nd_line(line_node);
- iobj->insn_info.node_id = nd_node_id(line_node);
+ iobj->insn_info.line_no = line_no;
+ iobj->insn_info.node_id = node_id;
iobj->insn_info.events = 0;
iobj->operands = argv;
iobj->operand_size = argc;
iobj->sc_state = 0;
+
+ iseq_insn_each_markable_object(iobj, iseq_insn_each_object_write_barrier, (VALUE)iseq);
+
return iobj;
}
static INSN *
-new_insn_body(rb_iseq_t *iseq, const NODE *const line_node, enum ruby_vminsn_type insn_id, int argc, ...)
+new_insn_body(rb_iseq_t *iseq, int line_no, int node_id, enum ruby_vminsn_type insn_id, int argc, ...)
{
VALUE *operands = 0;
va_list argv;
@@ -1278,7 +1434,7 @@ new_insn_body(rb_iseq_t *iseq, const NODE *const line_node, enum ruby_vminsn_typ
}
va_end(argv);
}
- return new_insn_core(iseq, line_node, insn_id, argc, operands);
+ return new_insn_core(iseq, line_no, node_id, insn_id, argc, operands);
}
static const struct rb_callinfo *
@@ -1303,7 +1459,7 @@ new_callinfo(rb_iseq_t *iseq, ID mid, int argc, unsigned int flag, struct rb_cal
}
static INSN *
-new_insn_send(rb_iseq_t *iseq, const NODE *const line_node, ID id, VALUE argc, const rb_iseq_t *blockiseq, VALUE flag, struct rb_callinfo_kwarg *keywords)
+new_insn_send(rb_iseq_t *iseq, int line_no, int node_id, ID id, VALUE argc, const rb_iseq_t *blockiseq, VALUE flag, struct rb_callinfo_kwarg *keywords)
{
VALUE *operands = compile_data_calloc2(iseq, sizeof(VALUE), 2);
VALUE ci = (VALUE)new_callinfo(iseq, id, FIX2INT(argc), FIX2INT(flag), keywords, blockiseq != NULL);
@@ -1312,7 +1468,7 @@ new_insn_send(rb_iseq_t *iseq, const NODE *const line_node, ID id, VALUE argc, c
if (blockiseq) {
RB_OBJ_WRITTEN(iseq, Qundef, blockiseq);
}
- INSN *insn = new_insn_core(iseq, line_node, BIN(send), 2, operands);
+ INSN *insn = new_insn_core(iseq, line_no, node_id, BIN(send), 2, operands);
RB_OBJ_WRITTEN(iseq, Qundef, ci);
RB_GC_GUARD(ci);
return insn;
@@ -1323,19 +1479,16 @@ new_child_iseq(rb_iseq_t *iseq, const NODE *const node,
VALUE name, const rb_iseq_t *parent, enum rb_iseq_type type, int line_no)
{
rb_iseq_t *ret_iseq;
- rb_ast_body_t ast;
-
- ast.root = node;
- ast.compile_option = 0;
- ast.script_lines = ISEQ_BODY(iseq)->variable.script_lines;
+ VALUE vast = rb_ruby_ast_new(node);
debugs("[new_child_iseq]> ---------------------------------------\n");
int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth;
- ret_iseq = rb_iseq_new_with_opt(&ast, name,
+ ret_iseq = rb_iseq_new_with_opt(vast, name,
rb_iseq_path(iseq), rb_iseq_realpath(iseq),
line_no, parent,
isolated_depth ? isolated_depth + 1 : 0,
- type, ISEQ_COMPILE_DATA(iseq)->option);
+ type, ISEQ_COMPILE_DATA(iseq)->option,
+ ISEQ_BODY(iseq)->variable.script_lines);
debugs("[new_child_iseq]< ---------------------------------------\n");
return ret_iseq;
}
@@ -1415,9 +1568,9 @@ iseq_insert_nop_between_end_and_cont(rb_iseq_t *iseq)
VALUE catch_table_ary = ISEQ_COMPILE_DATA(iseq)->catch_table_ary;
if (NIL_P(catch_table_ary)) return;
unsigned int i, tlen = (unsigned int)RARRAY_LEN(catch_table_ary);
- const VALUE *tptr = RARRAY_CONST_PTR_TRANSIENT(catch_table_ary);
+ const VALUE *tptr = RARRAY_CONST_PTR(catch_table_ary);
for (i = 0; i < tlen; i++) {
- const VALUE *ptr = RARRAY_CONST_PTR_TRANSIENT(tptr[i]);
+ const VALUE *ptr = RARRAY_CONST_PTR(tptr[i]);
LINK_ELEMENT *end = (LINK_ELEMENT *)(ptr[2] & ~1);
LINK_ELEMENT *cont = (LINK_ELEMENT *)(ptr[4] & ~1);
LINK_ELEMENT *e;
@@ -1430,14 +1583,15 @@ iseq_insert_nop_between_end_and_cont(rb_iseq_t *iseq)
for (e = end; e && (IS_LABEL(e) || IS_TRACE(e)); e = e->next) {
if (e == cont) {
- NODE dummy_line_node = generate_dummy_line_node(0, -1);
- INSN *nop = new_insn_core(iseq, &dummy_line_node, BIN(nop), 0, 0);
+ INSN *nop = new_insn_core(iseq, 0, -1, BIN(nop), 0, 0);
ELEM_INSERT_NEXT(end, &nop->link);
break;
}
}
}
}
+
+ RB_GC_GUARD(catch_table_ary);
}
static int
@@ -1464,13 +1618,6 @@ iseq_setup_insn(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
dump_disasm_list(FIRST_ELEMENT(anchor));
}
- if (ISEQ_COMPILE_DATA(iseq)->option->stack_caching) {
- debugs("[compile step 3.3 (iseq_set_sequence_stackcaching)]\n");
- iseq_set_sequence_stackcaching(iseq, anchor);
- if (compile_debug > 5)
- dump_disasm_list(FIRST_ELEMENT(anchor));
- }
-
debugs("[compile step 3.4 (iseq_insert_nop_between_end_and_cont)]\n");
iseq_insert_nop_between_end_and_cont(iseq);
if (compile_debug > 5)
@@ -1641,7 +1788,7 @@ access_outer_variables(const rb_iseq_t *iseq, int level, ID id, bool write)
COMPILE_ERROR(iseq, ISEQ_LAST_LINE(iseq), "can not yield from isolated Proc");
}
else {
- COMPILE_ERROR(iseq, ISEQ_LAST_LINE(iseq), "can not access variable `%s' from isolated Proc", rb_id2name(id));
+ COMPILE_ERROR(iseq, ISEQ_LAST_LINE(iseq), "can not access variable '%s' from isolated Proc", rb_id2name(id));
}
}
@@ -1746,7 +1893,7 @@ static int
iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *const optargs,
const struct rb_args_info *args, int arg_size)
{
- const NODE *node = args->kw_args;
+ const rb_node_kw_arg_t *node = args->kw_args;
struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
struct rb_iseq_param_keyword *keyword;
const VALUE default_values = rb_ary_hidden_new(1);
@@ -1765,7 +1912,7 @@ iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *const optargs,
node = args->kw_args;
while (node) {
- const NODE *val_node = node->nd_body->nd_value;
+ const NODE *val_node = get_nd_value(node->nd_body);
VALUE dv;
if (val_node == NODE_SPECIAL_REQUIRED_KEYWORD) {
@@ -1773,8 +1920,29 @@ iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *const optargs,
}
else {
switch (nd_type(val_node)) {
- case NODE_LIT:
- dv = val_node->nd_lit;
+ case NODE_SYM:
+ dv = rb_node_sym_string_val(val_node);
+ break;
+ case NODE_REGX:
+ dv = rb_node_regx_string_val(val_node);
+ break;
+ case NODE_LINE:
+ dv = rb_node_line_lineno_val(val_node);
+ break;
+ case NODE_INTEGER:
+ dv = rb_node_integer_literal_val(val_node);
+ break;
+ case NODE_FLOAT:
+ dv = rb_node_float_literal_val(val_node);
+ break;
+ case NODE_RATIONAL:
+ dv = rb_node_rational_literal_val(val_node);
+ break;
+ case NODE_IMAGINARY:
+ dv = rb_node_imaginary_literal_val(val_node);
+ break;
+ case NODE_ENCODING:
+ dv = rb_node_encoding_val(val_node);
break;
case NODE_NIL:
dv = Qnil;
@@ -1786,7 +1954,7 @@ iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *const optargs,
dv = Qfalse;
break;
default:
- NO_CHECK(COMPILE_POPPED(optargs, "kwarg", node)); /* nd_type_p(node, NODE_KW_ARG) */
+ NO_CHECK(COMPILE_POPPED(optargs, "kwarg", RNODE(node))); /* nd_type_p(node, NODE_KW_ARG) */
dv = complex_mark;
}
@@ -1799,9 +1967,12 @@ iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *const optargs,
keyword->num = kw;
- if (args->kw_rest_arg->nd_vid != 0) {
+ if (RNODE_DVAR(args->kw_rest_arg)->nd_vid != 0) {
+ ID kw_id = iseq->body->local_table[arg_size];
keyword->rest_start = arg_size++;
body->param.flags.has_kwrest = TRUE;
+
+ if (kw_id == idPow) body->param.flags.anon_kwrest = TRUE;
}
keyword->required_num = rkw;
keyword->table = &body->local_table[keyword->bits_start - keyword->num];
@@ -1823,6 +1994,22 @@ iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *const optargs,
return arg_size;
}
+static void
+iseq_set_use_block(rb_iseq_t *iseq)
+{
+ struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
+ if (!body->param.flags.use_block) {
+ body->param.flags.use_block = 1;
+
+ rb_vm_t *vm = GET_VM();
+
+ if (!vm->unused_block_warning_strict) {
+ st_data_t key = (st_data_t)rb_intern_str(body->location.label); // String -> ID
+ st_insert(vm->unused_block_warning_table, key, 1);
+ }
+ }
+}
+
static int
iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *const node_args)
{
@@ -1830,7 +2017,7 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *cons
if (node_args) {
struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
- struct rb_args_info *args = node_args->nd_ainfo;
+ struct rb_args_info *args = &RNODE_ARGS(node_args)->nd_ainfo;
ID rest_id = 0;
int last_comma = 0;
ID block_id = 0;
@@ -1851,14 +2038,14 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *cons
block_id = args->block_arg;
if (args->opt_args) {
- const NODE *node = args->opt_args;
+ const rb_node_opt_arg_t *node = args->opt_args;
LABEL *label;
VALUE labels = rb_ary_hidden_new(1);
VALUE *opt_table;
int i = 0, j;
while (node) {
- label = NEW_LABEL(nd_line(node));
+ label = NEW_LABEL(nd_line(RNODE(node)));
rb_ary_push(labels, (VALUE)label | 1);
ADD_LABEL(optargs, label);
NO_CHECK(COMPILE_POPPED(optargs, "optarg", node->nd_body));
@@ -1873,7 +2060,7 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *cons
opt_table = ALLOC_N(VALUE, i+1);
- MEMCPY(opt_table, RARRAY_CONST_PTR_TRANSIENT(labels), VALUE, i+1);
+ MEMCPY(opt_table, RARRAY_CONST_PTR(labels), VALUE, i+1);
for (j = 0; j < i+1; j++) {
opt_table[j] &= ~1;
}
@@ -1888,7 +2075,8 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *cons
if (rest_id) {
body->param.rest_start = arg_size++;
body->param.flags.has_rest = TRUE;
- assert(body->param.rest_start != -1);
+ if (rest_id == '*') body->param.flags.anon_rest = TRUE;
+ RUBY_ASSERT(body->param.rest_start != -1);
}
if (args->first_post_arg) {
@@ -1906,10 +2094,15 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *cons
arg_size = iseq_set_arguments_keywords(iseq, optargs, args, arg_size);
}
else if (args->kw_rest_arg) {
+ ID kw_id = iseq->body->local_table[arg_size];
struct rb_iseq_param_keyword *keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
keyword->rest_start = arg_size++;
body->param.keyword = keyword;
body->param.flags.has_kwrest = TRUE;
+
+ static ID anon_kwrest = 0;
+ if (!anon_kwrest) anon_kwrest = rb_intern("**");
+ if (kw_id == anon_kwrest) body->param.flags.anon_kwrest = TRUE;
}
else if (args->no_kwarg) {
body->param.flags.accepts_no_kwarg = TRUE;
@@ -1918,6 +2111,7 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *cons
if (block_id) {
body->param.block_start = arg_size++;
body->param.flags.has_block = TRUE;
+ iseq_set_use_block(iseq);
}
iseq_calc_param_size(iseq);
@@ -2359,7 +2553,12 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
generated_iseq = ALLOC_N(VALUE, code_index);
insns_info = ALLOC_N(struct iseq_insn_info_entry, insn_num);
positions = ALLOC_N(unsigned int, insn_num);
- body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, ISEQ_IS_SIZE(body));
+ if (ISEQ_IS_SIZE(body)) {
+ body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, ISEQ_IS_SIZE(body));
+ }
+ else {
+ body->is_entries = NULL;
+ }
body->call_data = ZALLOC_N(struct rb_call_data, body->ci_size);
ISEQ_COMPILE_DATA(iseq)->ci_index = 0;
@@ -2495,11 +2694,11 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
break;
}
- case TS_CALLDATA:
+ case TS_CALLDATA:
{
const struct rb_callinfo *source_ci = (const struct rb_callinfo *)operands[j];
+ RUBY_ASSERT(ISEQ_COMPILE_DATA(iseq)->ci_index <= body->ci_size);
struct rb_call_data *cd = &body->call_data[ISEQ_COMPILE_DATA(iseq)->ci_index++];
- assert(ISEQ_COMPILE_DATA(iseq)->ci_index <= body->ci_size);
cd->ci = source_ci;
cd->cc = vm_cc_empty();
generated_iseq[code_index + 1 + j] = (VALUE)cd;
@@ -2637,16 +2836,18 @@ iseq_set_exception_table(rb_iseq_t *iseq)
struct iseq_catch_table_entry *entry;
ISEQ_BODY(iseq)->catch_table = NULL;
- if (NIL_P(ISEQ_COMPILE_DATA(iseq)->catch_table_ary)) return COMPILE_OK;
- tlen = (int)RARRAY_LEN(ISEQ_COMPILE_DATA(iseq)->catch_table_ary);
- tptr = RARRAY_CONST_PTR_TRANSIENT(ISEQ_COMPILE_DATA(iseq)->catch_table_ary);
+
+ VALUE catch_table_ary = ISEQ_COMPILE_DATA(iseq)->catch_table_ary;
+ if (NIL_P(catch_table_ary)) return COMPILE_OK;
+ tlen = (int)RARRAY_LEN(catch_table_ary);
+ tptr = RARRAY_CONST_PTR(catch_table_ary);
if (tlen > 0) {
struct iseq_catch_table *table = xmalloc(iseq_catch_table_bytes(tlen));
table->size = tlen;
for (i = 0; i < table->size; i++) {
- ptr = RARRAY_CONST_PTR_TRANSIENT(tptr[i]);
+ ptr = RARRAY_CONST_PTR(tptr[i]);
entry = UNALIGNED_MEMBER_PTR(table, entries[i]);
entry->type = (enum rb_catch_type)(ptr[0] & 0xffff);
entry->start = label_get_position((LABEL *)(ptr[1] & ~1));
@@ -2664,6 +2865,7 @@ iseq_set_exception_table(rb_iseq_t *iseq)
if (entry->type == CATCH_TYPE_RESCUE ||
entry->type == CATCH_TYPE_BREAK ||
entry->type == CATCH_TYPE_NEXT) {
+ RUBY_ASSERT(entry->sp > 0);
entry->sp--;
}
}
@@ -2675,6 +2877,8 @@ iseq_set_exception_table(rb_iseq_t *iseq)
RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->catch_table_ary, 0); /* free */
}
+ RB_GC_GUARD(catch_table_ary);
+
return COMPILE_OK;
}
@@ -2958,7 +3162,6 @@ optimize_checktype(rb_iseq_t *iseq, INSN *iobj)
}
line = ciobj->insn_info.line_no;
node_id = ciobj->insn_info.node_id;
- NODE dummy_line_node = generate_dummy_line_node(line, node_id);
if (!dest) {
if (niobj->link.next && IS_LABEL(niobj->link.next)) {
dest = (LABEL *)niobj->link.next; /* reuse label */
@@ -2968,9 +3171,9 @@ optimize_checktype(rb_iseq_t *iseq, INSN *iobj)
ELEM_INSERT_NEXT(&niobj->link, &dest->link);
}
}
- INSERT_AFTER_INSN1(iobj, &dummy_line_node, jump, dest);
+ INSERT_AFTER_INSN1(iobj, line, node_id, jump, dest);
LABEL_REF(dest);
- if (!dup) INSERT_AFTER_INSN(iobj, &dummy_line_node, pop);
+ if (!dup) INSERT_AFTER_INSN(iobj, line, node_id, pop);
return TRUE;
}
@@ -2996,6 +3199,34 @@ ci_argc_set(const rb_iseq_t *iseq, const struct rb_callinfo *ci, int argc)
return nci;
}
+static bool
+optimize_args_splat_no_copy(rb_iseq_t *iseq, INSN *insn, LINK_ELEMENT *niobj,
+ unsigned int set_flags, unsigned int unset_flags, unsigned int remove_flags)
+{
+ LINK_ELEMENT *iobj = (LINK_ELEMENT *)insn;
+ if ((set_flags & VM_CALL_ARGS_BLOCKARG) && (set_flags & VM_CALL_KW_SPLAT) &&
+ IS_NEXT_INSN_ID(niobj, splatkw)) {
+ niobj = niobj->next;
+ }
+ if (!IS_NEXT_INSN_ID(niobj, send) && !IS_NEXT_INSN_ID(niobj, invokesuper)) {
+ return false;
+ }
+ niobj = niobj->next;
+
+ const struct rb_callinfo *ci = (const struct rb_callinfo *)OPERAND_AT(niobj, 0);
+ unsigned int flags = vm_ci_flag(ci);
+ if ((flags & set_flags) == set_flags && !(flags & unset_flags)) {
+ RUBY_ASSERT(flags & VM_CALL_ARGS_SPLAT_MUT);
+ OPERAND_AT(iobj, 0) = Qfalse;
+ const struct rb_callinfo *nci = vm_ci_new(vm_ci_mid(ci),
+ flags & ~(VM_CALL_ARGS_SPLAT_MUT|remove_flags), vm_ci_argc(ci), vm_ci_kwarg(ci));
+ RB_OBJ_WRITTEN(iseq, ci, nci);
+ OPERAND_AT(niobj, 0) = (VALUE)nci;
+ return true;
+ }
+ return false;
+}
+
static int
iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcallopt)
{
@@ -3104,8 +3335,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
* pop
* jump L1
*/
- NODE dummy_line_node = generate_dummy_line_node(iobj->insn_info.line_no, iobj->insn_info.node_id);
- INSN *popiobj = new_insn_core(iseq, &dummy_line_node, BIN(pop), 0, 0);
+ INSN *popiobj = new_insn_core(iseq, iobj->insn_info.line_no, iobj->insn_info.node_id, BIN(pop), 0, 0);
ELEM_REPLACE(&piobj->link, &popiobj->link);
}
}
@@ -3150,15 +3380,15 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
/*
* ...
* duparray [...]
- * concatarray
+ * concatarray | concattoarray
* =>
* ...
* putobject [...]
- * concatarray
+ * concatarray | concattoarray
*/
if (IS_INSN_ID(iobj, duparray)) {
LINK_ELEMENT *next = iobj->link.next;
- if (IS_INSN(next) && IS_INSN_ID(next, concatarray)) {
+ if (IS_INSN(next) && (IS_INSN_ID(next, concatarray) || IS_INSN_ID(next, concattoarray))) {
iobj->insn_id = BIN(putobject);
}
}
@@ -3284,14 +3514,12 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
ELEM_REMOVE(iobj->link.prev);
}
else if (!iseq_pop_newarray(iseq, pobj)) {
- NODE dummy_line_node = generate_dummy_line_node(pobj->insn_info.line_no, pobj->insn_info.node_id);
- pobj = new_insn_core(iseq, &dummy_line_node, BIN(pop), 0, NULL);
+ pobj = new_insn_core(iseq, pobj->insn_info.line_no, pobj->insn_info.node_id, BIN(pop), 0, NULL);
ELEM_INSERT_PREV(&iobj->link, &pobj->link);
}
if (cond) {
if (prev_dup) {
- NODE dummy_line_node = generate_dummy_line_node(pobj->insn_info.line_no, pobj->insn_info.node_id);
- pobj = new_insn_core(iseq, &dummy_line_node, BIN(putnil), 0, NULL);
+ pobj = new_insn_core(iseq, pobj->insn_info.line_no, pobj->insn_info.node_id, BIN(putnil), 0, NULL);
ELEM_INSERT_NEXT(&iobj->link, &pobj->link);
}
iobj->insn_id = BIN(jump);
@@ -3337,8 +3565,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
}
else if (previ == BIN(concatarray)) {
INSN *piobj = (INSN *)prev;
- NODE dummy_line_node = generate_dummy_line_node(piobj->insn_info.line_no, piobj->insn_info.node_id);
- INSERT_BEFORE_INSN1(piobj, &dummy_line_node, splatarray, Qfalse);
+ INSERT_BEFORE_INSN1(piobj, piobj->insn_info.line_no, piobj->insn_info.node_id, splatarray, Qfalse);
INSN_OF(piobj) = BIN(pop);
}
else if (previ == BIN(concatstrings)) {
@@ -3355,7 +3582,6 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
if (IS_INSN_ID(iobj, newarray) ||
IS_INSN_ID(iobj, duparray) ||
- IS_INSN_ID(iobj, expandarray) ||
IS_INSN_ID(iobj, concatarray) ||
IS_INSN_ID(iobj, splatarray) ||
0) {
@@ -3404,7 +3630,6 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
}
}
else {
- NODE dummy_line_node = generate_dummy_line_node(iobj->insn_info.line_no, iobj->insn_info.node_id);
long diff = FIX2LONG(op1) - FIX2LONG(op2);
INSN_OF(iobj) = BIN(opt_reverse);
OPERAND_AT(iobj, 0) = OPERAND_AT(next, 0);
@@ -3418,7 +3643,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
* opt_reverse Y
*/
for (; diff > 0; diff--) {
- INSERT_BEFORE_INSN(iobj, &dummy_line_node, pop);
+ INSERT_BEFORE_INSN(iobj, iobj->insn_info.line_no, iobj->insn_info.node_id, pop);
}
}
else { /* (op1 < op2) */
@@ -3430,7 +3655,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
* opt_reverse Y
*/
for (; diff < 0; diff++) {
- INSERT_BEFORE_INSN(iobj, &dummy_line_node, putnil);
+ INSERT_BEFORE_INSN(iobj, iobj->insn_info.line_no, iobj->insn_info.node_id, putnil);
}
}
}
@@ -3654,7 +3879,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
iobj->insn_id = BIN(opt_invokebuiltin_delegate_leave);
const struct rb_builtin_function *bf = (const struct rb_builtin_function *)iobj->operands[0];
if (iobj == (INSN *)list && bf->argc == 0 && (iseq->body->builtin_attrs & BUILTIN_ATTR_LEAF)) {
- iseq->body->builtin_attrs |= BUILTIN_ATTR_SINGLE_NOARG_INLINE;
+ iseq->body->builtin_attrs |= BUILTIN_ATTR_SINGLE_NOARG_LEAF;
}
}
}
@@ -3673,6 +3898,135 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
}
}
+ if (IS_INSN_ID(iobj, splatarray) && OPERAND_AT(iobj, 0) == Qtrue) {
+ LINK_ELEMENT *niobj = &iobj->link;
+
+ /*
+ * Eliminate array allocation for f(1, *a)
+ *
+ * splatarray true
+ * send ARGS_SPLAT and not KW_SPLAT|ARGS_BLOCKARG
+ * =>
+ * splatarray false
+ * send
+ */
+ if (optimize_args_splat_no_copy(iseq, iobj, niobj,
+ VM_CALL_ARGS_SPLAT, VM_CALL_KW_SPLAT|VM_CALL_ARGS_BLOCKARG, 0)) goto optimized_splat;
+
+ if (IS_NEXT_INSN_ID(niobj, getlocal) || IS_NEXT_INSN_ID(niobj, getinstancevariable)) {
+ niobj = niobj->next;
+
+ /*
+ * Eliminate array allocation for f(1, *a, &lvar) and f(1, *a, &@iv)
+ *
+ * splatarray true
+ * getlocal / getinstancevariable
+ * send ARGS_SPLAT|ARGS_BLOCKARG and not KW_SPLAT
+ * =>
+ * splatarray false
+ * getlocal / getinstancevariable
+ * send
+ */
+ if (optimize_args_splat_no_copy(iseq, iobj, niobj,
+ VM_CALL_ARGS_SPLAT|VM_CALL_ARGS_BLOCKARG, VM_CALL_KW_SPLAT, 0)) goto optimized_splat;
+
+ /*
+ * Eliminate array allocation for f(*a, **lvar) and f(*a, **@iv)
+ *
+ * splatarray true
+ * getlocal / getinstancevariable
+ * send ARGS_SPLAT|KW_SPLAT and not ARGS_BLOCKARG
+ * =>
+ * splatarray false
+ * getlocal / getinstancevariable
+ * send
+ */
+ if (optimize_args_splat_no_copy(iseq, iobj, niobj,
+ VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT, VM_CALL_ARGS_BLOCKARG, 0)) goto optimized_splat;
+
+ if (IS_NEXT_INSN_ID(niobj, getlocal) || IS_NEXT_INSN_ID(niobj, getinstancevariable) ||
+ IS_NEXT_INSN_ID(niobj, getblockparamproxy)) {
+ niobj = niobj->next;
+
+ /*
+ * Eliminate array allocation for f(*a, **lvar, &{arg,lvar,@iv})
+ *
+ * splatarray true
+ * getlocal / getinstancevariable
+ * getlocal / getinstancevariable / getblockparamproxy
+ * send ARGS_SPLAT|KW_SPLAT|ARGS_BLOCKARG
+ * =>
+ * splatarray false
+ * getlocal / getinstancevariable
+ * getlocal / getinstancevariable / getblockparamproxy
+ * send
+ */
+ optimize_args_splat_no_copy(iseq, iobj, niobj,
+ VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT|VM_CALL_ARGS_BLOCKARG, 0, 0);
+ }
+ } else if (IS_NEXT_INSN_ID(niobj, getblockparamproxy)) {
+ /*
+ * Eliminate array allocation for f(1, *a, &arg)
+ *
+ * splatarray true
+ * getblockparamproxy
+ * send ARGS_SPLAT|ARGS_BLOCKARG and not KW_SPLAT
+ * =>
+ * splatarray false
+ * getblockparamproxy
+ * send
+ */
+ optimize_args_splat_no_copy(iseq, iobj, niobj,
+ VM_CALL_ARGS_SPLAT|VM_CALL_ARGS_BLOCKARG, VM_CALL_KW_SPLAT, 0);
+ } else if (IS_NEXT_INSN_ID(niobj, duphash)) {
+ niobj = niobj->next;
+
+ /*
+ * Eliminate array and hash allocation for f(*a, kw: 1)
+ *
+ * splatarray true
+ * duphash
+ * send ARGS_SPLAT|KW_SPLAT|KW_SPLAT_MUT and not ARGS_BLOCKARG
+ * =>
+ * splatarray false
+ * putobject
+ * send ARGS_SPLAT|KW_SPLAT
+ */
+ if (optimize_args_splat_no_copy(iseq, iobj, niobj,
+ VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT|VM_CALL_KW_SPLAT_MUT, VM_CALL_ARGS_BLOCKARG, VM_CALL_KW_SPLAT_MUT)) {
+
+ ((INSN*)niobj)->insn_id = BIN(putobject);
+ OPERAND_AT(niobj, 0) = rb_hash_freeze(rb_hash_resurrect(OPERAND_AT(niobj, 0)));
+
+ goto optimized_splat;
+ }
+
+ if (IS_NEXT_INSN_ID(niobj, getlocal) || IS_NEXT_INSN_ID(niobj, getinstancevariable) ||
+ IS_NEXT_INSN_ID(niobj, getblockparamproxy)) {
+ /*
+ * Eliminate array and hash allocation for f(*a, kw: 1, &{arg,lvar,@iv})
+ *
+ * splatarray true
+ * duphash
+ * getlocal / getinstancevariable / getblockparamproxy
+ * send ARGS_SPLAT|KW_SPLAT|KW_SPLAT_MUT|ARGS_BLOCKARG
+ * =>
+ * splatarray false
+ * putobject
+ * getlocal / getinstancevariable / getblockparamproxy
+ * send ARGS_SPLAT|KW_SPLAT|ARGS_BLOCKARG
+ */
+ if (optimize_args_splat_no_copy(iseq, iobj, niobj->next,
+ VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT|VM_CALL_KW_SPLAT_MUT|VM_CALL_ARGS_BLOCKARG, 0, VM_CALL_KW_SPLAT_MUT)) {
+
+ ((INSN*)niobj)->insn_id = BIN(putobject);
+ OPERAND_AT(niobj, 0) = rb_hash_freeze(rb_hash_resurrect(OPERAND_AT(niobj, 0)));
+ }
+ }
+ }
+ }
+ optimized_splat:
+
return COMPILE_OK;
}
@@ -3711,11 +4065,11 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
case idMin:
case idHash:
{
- rb_num_t num = (rb_num_t)iobj->operands[0];
+ VALUE num = iobj->operands[0];
iobj->insn_id = BIN(opt_newarray_send);
iobj->operands = compile_data_calloc2(iseq, insn_len(iobj->insn_id) - 1, sizeof(VALUE));
- iobj->operands[0] = (VALUE)num;
- iobj->operands[1] = (VALUE)rb_id2sym(vm_ci_mid(ci));
+ iobj->operands[0] = num;
+ iobj->operands[1] = rb_id2sym(vm_ci_mid(ci));
iobj->operand_size = insn_len(iobj->insn_id) - 1;
ELEM_REMOVE(&niobj->link);
return COMPILE_OK;
@@ -3890,8 +4244,7 @@ new_unified_insn(rb_iseq_t *iseq,
list = list->next;
}
- NODE dummy_line_node = generate_dummy_line_node(iobj->insn_info.line_no, iobj->insn_info.node_id);
- return new_insn_core(iseq, &dummy_line_node, insn_id, argc, operands);
+ return new_insn_core(iseq, iobj->insn_info.line_no, iobj->insn_info.node_id, insn_id, argc, operands);
}
#endif
@@ -3951,185 +4304,24 @@ iseq_insns_unification(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
return COMPILE_OK;
}
-#if OPT_STACK_CACHING
-
-#define SC_INSN(insn, stat) sc_insn_info[(insn)][(stat)]
-#define SC_NEXT(insn) sc_insn_next[(insn)]
-
-#include "opt_sc.inc"
-
-static int
-insn_set_sc_state(rb_iseq_t *iseq, const LINK_ELEMENT *anchor, INSN *iobj, int state)
-{
- int nstate;
- int insn_id;
-
- insn_id = iobj->insn_id;
- iobj->insn_id = SC_INSN(insn_id, state);
- nstate = SC_NEXT(iobj->insn_id);
-
- if (insn_id == BIN(jump) ||
- insn_id == BIN(branchif) || insn_id == BIN(branchunless)) {
- LABEL *lobj = (LABEL *)OPERAND_AT(iobj, 0);
-
- if (lobj->sc_state != 0) {
- if (lobj->sc_state != nstate) {
- BADINSN_DUMP(anchor, iobj, lobj);
- COMPILE_ERROR(iseq, iobj->insn_info.line_no,
- "insn_set_sc_state error: %d at "LABEL_FORMAT
- ", %d expected\n",
- lobj->sc_state, lobj->label_no, nstate);
- return COMPILE_NG;
- }
- }
- else {
- lobj->sc_state = nstate;
- }
- if (insn_id == BIN(jump)) {
- nstate = SCS_XX;
- }
- }
- else if (insn_id == BIN(leave)) {
- nstate = SCS_XX;
- }
-
- return nstate;
-}
-
-static int
-label_set_sc_state(LABEL *lobj, int state)
-{
- if (lobj->sc_state != 0) {
- if (lobj->sc_state != state) {
- state = lobj->sc_state;
- }
- }
- else {
- lobj->sc_state = state;
- }
-
- return state;
-}
-
-
-#endif
-
-static int
-iseq_set_sequence_stackcaching(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
-{
-#if OPT_STACK_CACHING
- LINK_ELEMENT *list;
- int state, insn_id;
-
- /* initialize */
- state = SCS_XX;
- list = FIRST_ELEMENT(anchor);
- /* dump_disasm_list(list); */
-
- /* for each list element */
- while (list) {
- redo_point:
- switch (list->type) {
- case ISEQ_ELEMENT_INSN:
- {
- INSN *iobj = (INSN *)list;
- insn_id = iobj->insn_id;
-
- /* dump_disasm_list(list); */
-
- switch (insn_id) {
- case BIN(nop):
- {
- /* exception merge point */
- if (state != SCS_AX) {
- NODE dummy_line_node = generate_dummy_line_node(0, -1);
- INSN *rpobj =
- new_insn_body(iseq, &dummy_line_node, BIN(reput), 0);
-
- /* replace this insn */
- ELEM_REPLACE(list, (LINK_ELEMENT *)rpobj);
- list = (LINK_ELEMENT *)rpobj;
- goto redo_point;
- }
- break;
- }
- case BIN(swap):
- {
- if (state == SCS_AB || state == SCS_BA) {
- state = (state == SCS_AB ? SCS_BA : SCS_AB);
-
- ELEM_REMOVE(list);
- list = list->next;
- goto redo_point;
- }
- break;
- }
- case BIN(pop):
- {
- switch (state) {
- case SCS_AX:
- case SCS_BX:
- state = SCS_XX;
- break;
- case SCS_AB:
- state = SCS_AX;
- break;
- case SCS_BA:
- state = SCS_BX;
- break;
- case SCS_XX:
- goto normal_insn;
- default:
- COMPILE_ERROR(iseq, iobj->insn_info.line_no,
- "unreachable");
- return COMPILE_NG;
- }
- /* remove useless pop */
- ELEM_REMOVE(list);
- list = list->next;
- goto redo_point;
- }
- default:;
- /* none */
- } /* end of switch */
- normal_insn:
- state = insn_set_sc_state(iseq, anchor, iobj, state);
- break;
- }
- case ISEQ_ELEMENT_LABEL:
- {
- LABEL *lobj;
- lobj = (LABEL *)list;
-
- state = label_set_sc_state(lobj, state);
- }
- default:
- break;
- }
- list = list->next;
- }
-#endif
- return COMPILE_OK;
-}
-
static int
all_string_result_p(const NODE *node)
{
if (!node) return FALSE;
switch (nd_type(node)) {
- case NODE_STR: case NODE_DSTR:
+ case NODE_STR: case NODE_DSTR: case NODE_FILE:
return TRUE;
case NODE_IF: case NODE_UNLESS:
- if (!node->nd_body || !node->nd_else) return FALSE;
- if (all_string_result_p(node->nd_body))
- return all_string_result_p(node->nd_else);
+ if (!RNODE_IF(node)->nd_body || !RNODE_IF(node)->nd_else) return FALSE;
+ if (all_string_result_p(RNODE_IF(node)->nd_body))
+ return all_string_result_p(RNODE_IF(node)->nd_else);
return FALSE;
case NODE_AND: case NODE_OR:
- if (!node->nd_2nd)
- return all_string_result_p(node->nd_1st);
- if (!all_string_result_p(node->nd_1st))
+ if (!RNODE_AND(node)->nd_2nd)
+ return all_string_result_p(RNODE_AND(node)->nd_1st);
+ if (!all_string_result_p(RNODE_AND(node)->nd_1st))
return FALSE;
- return all_string_result_p(node->nd_2nd);
+ return all_string_result_p(RNODE_AND(node)->nd_2nd);
default:
return FALSE;
}
@@ -4138,8 +4330,8 @@ all_string_result_p(const NODE *node)
static int
compile_dstr_fragments(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int *cntp)
{
- const NODE *list = node->nd_next;
- VALUE lit = node->nd_lit;
+ const struct RNode_LIST *list = RNODE_DSTR(node)->nd_next;
+ VALUE lit = rb_node_dstr_string_val(node);
LINK_ELEMENT *first_lit = 0;
int cnt = 0;
@@ -4160,7 +4352,7 @@ compile_dstr_fragments(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *cons
while (list) {
const NODE *const head = list->nd_head;
if (nd_type_p(head, NODE_STR)) {
- lit = rb_fstring(head->nd_lit);
+ lit = rb_node_str_string_val(head);
ADD_INSN1(ret, head, putobject, lit);
RB_OBJ_WRITTEN(iseq, Qundef, lit);
lit = Qnil;
@@ -4169,7 +4361,7 @@ compile_dstr_fragments(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *cons
CHECK(COMPILE(ret, "each string", head));
}
cnt++;
- list = list->nd_next;
+ list = (struct RNode_LIST *)list->nd_next;
}
if (NIL_P(lit) && first_lit) {
ELEM_REMOVE(first_lit);
@@ -4184,12 +4376,12 @@ static int
compile_block(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int popped)
{
while (node && nd_type_p(node, NODE_BLOCK)) {
- CHECK(COMPILE_(ret, "BLOCK body", node->nd_head,
- (node->nd_next ? 1 : popped)));
- node = node->nd_next;
+ CHECK(COMPILE_(ret, "BLOCK body", RNODE_BLOCK(node)->nd_head,
+ (RNODE_BLOCK(node)->nd_next ? 1 : popped)));
+ node = RNODE_BLOCK(node)->nd_next;
}
if (node) {
- CHECK(COMPILE_(ret, "BLOCK next", node->nd_next, popped));
+ CHECK(COMPILE_(ret, "BLOCK next", RNODE_BLOCK(node)->nd_next, popped));
}
return COMPILE_OK;
}
@@ -4198,8 +4390,8 @@ static int
compile_dstr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node)
{
int cnt;
- if (!node->nd_next) {
- VALUE lit = rb_fstring(node->nd_lit);
+ if (!RNODE_DSTR(node)->nd_next) {
+ VALUE lit = rb_node_dstr_string_val(node);
ADD_INSN1(ret, node, putstring, lit);
RB_OBJ_WRITTEN(iseq, Qundef, lit);
}
@@ -4211,11 +4403,27 @@ compile_dstr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node)
}
static int
-compile_dregx(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node)
+compile_dregx(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
{
int cnt;
+
+ if (!RNODE_DREGX(node)->nd_next) {
+ if (!popped) {
+ VALUE src = rb_node_dregx_string_val(node);
+ VALUE match = rb_reg_compile(src, (int)RNODE_DREGX(node)->nd_cflag, NULL, 0);
+ ADD_INSN1(ret, node, putobject, match);
+ RB_OBJ_WRITTEN(iseq, Qundef, match);
+ }
+ return COMPILE_OK;
+ }
+
CHECK(compile_dstr_fragments(iseq, ret, node, &cnt));
- ADD_INSN2(ret, node, toregexp, INT2FIX(node->nd_cflag), INT2FIX(cnt));
+ ADD_INSN2(ret, node, toregexp, INT2FIX(RNODE_DREGX(node)->nd_cflag), INT2FIX(cnt));
+
+ if (popped) {
+ ADD_INSN(ret, node, pop);
+ }
+
return COMPILE_OK;
}
@@ -4233,7 +4441,7 @@ compile_flip_flop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const nod
ADD_INSNL(ret, node, branchif, lend);
/* *flip == 0 */
- CHECK(COMPILE(ret, "flip2 beg", node->nd_beg));
+ CHECK(COMPILE(ret, "flip2 beg", RNODE_FLIP2(node)->nd_beg));
ADD_INSNL(ret, node, branchunless, else_label);
ADD_INSN1(ret, node, putobject, Qtrue);
ADD_INSN1(ret, node, setspecial, key);
@@ -4243,7 +4451,7 @@ compile_flip_flop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const nod
/* *flip == 1 */
ADD_LABEL(ret, lend);
- CHECK(COMPILE(ret, "flip2 end", node->nd_end));
+ CHECK(COMPILE(ret, "flip2 end", RNODE_FLIP2(node)->nd_end));
ADD_INSNL(ret, node, branchunless, then_label);
ADD_INSN1(ret, node, putobject, Qfalse);
ADD_INSN1(ret, node, setspecial, key);
@@ -4253,9 +4461,10 @@ compile_flip_flop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const nod
}
static int
-compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *cond,
+compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE *cond,
LABEL *then_label, LABEL *else_label);
+#define COMPILE_SINGLE 2
static int
compile_logical(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *cond,
LABEL *then_label, LABEL *else_label)
@@ -4274,32 +4483,51 @@ compile_logical(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *cond,
return COMPILE_OK;
}
if (!label->refcnt) {
- ADD_INSN(seq, cond, putnil);
- }
- else {
- ADD_LABEL(seq, label);
+ return COMPILE_SINGLE;
}
+ ADD_LABEL(seq, label);
ADD_SEQ(ret, seq);
return COMPILE_OK;
}
static int
-compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *cond,
+compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE *cond,
LABEL *then_label, LABEL *else_label)
{
+ int ok;
+ DECL_ANCHOR(ignore);
+
again:
switch (nd_type(cond)) {
case NODE_AND:
- CHECK(compile_logical(iseq, ret, cond->nd_1st, NULL, else_label));
- cond = cond->nd_2nd;
+ CHECK(ok = compile_logical(iseq, ret, RNODE_AND(cond)->nd_1st, NULL, else_label));
+ cond = RNODE_AND(cond)->nd_2nd;
+ if (ok == COMPILE_SINGLE) {
+ INIT_ANCHOR(ignore);
+ ret = ignore;
+ then_label = NEW_LABEL(nd_line(cond));
+ }
goto again;
case NODE_OR:
- CHECK(compile_logical(iseq, ret, cond->nd_1st, then_label, NULL));
- cond = cond->nd_2nd;
+ CHECK(ok = compile_logical(iseq, ret, RNODE_OR(cond)->nd_1st, then_label, NULL));
+ cond = RNODE_OR(cond)->nd_2nd;
+ if (ok == COMPILE_SINGLE) {
+ INIT_ANCHOR(ignore);
+ ret = ignore;
+ else_label = NEW_LABEL(nd_line(cond));
+ }
goto again;
- case NODE_LIT: /* NODE_LIT is always true */
+ case NODE_SYM:
+ case NODE_LINE:
+ case NODE_FILE:
+ case NODE_ENCODING:
+ case NODE_INTEGER: /* NODE_INTEGER is always true */
+ case NODE_FLOAT: /* NODE_FLOAT is always true */
+ case NODE_RATIONAL: /* NODE_RATIONAL is always true */
+ case NODE_IMAGINARY: /* NODE_IMAGINARY is always true */
case NODE_TRUE:
case NODE_STR:
+ case NODE_REGX:
case NODE_ZLIST:
case NODE_LAMBDA:
/* printf("useless condition eliminate (%s)\n", ruby_node_name(nd_type(cond))); */
@@ -4362,7 +4590,40 @@ compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *co
static int
keyword_node_p(const NODE *const node)
{
- return nd_type_p(node, NODE_HASH) && (node->nd_brace & HASH_BRACE) != HASH_BRACE;
+ return nd_type_p(node, NODE_HASH) && (RNODE_HASH(node)->nd_brace & HASH_BRACE) != HASH_BRACE;
+}
+
+static VALUE
+get_symbol_value(rb_iseq_t *iseq, const NODE *node)
+{
+ switch (nd_type(node)) {
+ case NODE_SYM:
+ return rb_node_sym_string_val(node);
+ default:
+ UNKNOWN_NODE("get_symbol_value", node, Qnil);
+ }
+}
+
+static VALUE
+node_hash_unique_key_index(rb_iseq_t *iseq, rb_node_hash_t *node_hash, int *count_ptr)
+{
+ NODE *node = node_hash->nd_head;
+ VALUE hash = rb_hash_new();
+ VALUE ary = rb_ary_new();
+
+ for (int i = 0; node != NULL; i++, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) {
+ VALUE key = get_symbol_value(iseq, RNODE_LIST(node)->nd_head);
+ VALUE idx = rb_hash_aref(hash, key);
+ if (!NIL_P(idx)) {
+ rb_ary_store(ary, FIX2INT(idx), Qfalse);
+ (*count_ptr)--;
+ }
+ rb_hash_aset(hash, key, INT2FIX(i));
+ rb_ary_store(ary, i, Qtrue);
+ (*count_ptr)++;
+ }
+
+ return ary;
}
static int
@@ -4375,22 +4636,22 @@ compile_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
RUBY_ASSERT(kw_arg_ptr != NULL);
RUBY_ASSERT(flag != NULL);
- if (root_node->nd_head && nd_type_p(root_node->nd_head, NODE_LIST)) {
- const NODE *node = root_node->nd_head;
+ if (RNODE_HASH(root_node)->nd_head && nd_type_p(RNODE_HASH(root_node)->nd_head, NODE_LIST)) {
+ const NODE *node = RNODE_HASH(root_node)->nd_head;
int seen_nodes = 0;
while (node) {
- const NODE *key_node = node->nd_head;
+ const NODE *key_node = RNODE_LIST(node)->nd_head;
seen_nodes++;
- assert(nd_type_p(node, NODE_LIST));
- if (key_node && nd_type_p(key_node, NODE_LIT) && SYMBOL_P(key_node->nd_lit)) {
+ RUBY_ASSERT(nd_type_p(node, NODE_LIST));
+ if (key_node && nd_type_p(key_node, NODE_SYM)) {
/* can be keywords */
}
else {
if (flag) {
*flag |= VM_CALL_KW_SPLAT;
- if (seen_nodes > 1 || node->nd_next->nd_next) {
+ if (seen_nodes > 1 || RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) {
/* A new hash will be created for the keyword arguments
* in this case, so mark the method as passing mutable
* keyword splat.
@@ -4400,29 +4661,37 @@ compile_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
}
return FALSE;
}
- node = node->nd_next; /* skip value node */
- node = node->nd_next;
+ node = RNODE_LIST(node)->nd_next; /* skip value node */
+ node = RNODE_LIST(node)->nd_next;
}
/* may be keywords */
- node = root_node->nd_head;
+ node = RNODE_HASH(root_node)->nd_head;
{
- int len = (int)node->nd_alen / 2;
+ int len = 0;
+ VALUE key_index = node_hash_unique_key_index(iseq, RNODE_HASH(root_node), &len);
struct rb_callinfo_kwarg *kw_arg =
rb_xmalloc_mul_add(len, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
VALUE *keywords = kw_arg->keywords;
int i = 0;
+ int j = 0;
+ kw_arg->references = 0;
kw_arg->keyword_len = len;
*kw_arg_ptr = kw_arg;
- for (i=0; node != NULL; i++, node = node->nd_next->nd_next) {
- const NODE *key_node = node->nd_head;
- const NODE *val_node = node->nd_next->nd_head;
- keywords[i] = key_node->nd_lit;
- NO_CHECK(COMPILE(ret, "keyword values", val_node));
+ for (i=0; node != NULL; i++, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) {
+ const NODE *key_node = RNODE_LIST(node)->nd_head;
+ const NODE *val_node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head;
+ int popped = TRUE;
+ if (rb_ary_entry(key_index, i)) {
+ keywords[j] = get_symbol_value(iseq, key_node);
+ j++;
+ popped = FALSE;
+ }
+ NO_CHECK(COMPILE_(ret, "keyword values", val_node, popped));
}
- assert(i == len);
+ RUBY_ASSERT(j == len);
return TRUE;
}
}
@@ -4434,34 +4703,48 @@ compile_args(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, NODE **k
{
int len = 0;
- for (; node; len++, node = node->nd_next) {
+ for (; node; len++, node = RNODE_LIST(node)->nd_next) {
if (CPDEBUG > 0) {
EXPECT_NODE("compile_args", node, NODE_LIST, -1);
}
- if (node->nd_next == NULL && keyword_node_p(node->nd_head)) { /* last node is kwnode */
- *kwnode_ptr = node->nd_head;
+ if (RNODE_LIST(node)->nd_next == NULL && keyword_node_p(RNODE_LIST(node)->nd_head)) { /* last node is kwnode */
+ *kwnode_ptr = RNODE_LIST(node)->nd_head;
}
else {
- RUBY_ASSERT(!keyword_node_p(node->nd_head));
- NO_CHECK(COMPILE_(ret, "array element", node->nd_head, FALSE));
+ RUBY_ASSERT(!keyword_node_p(RNODE_LIST(node)->nd_head));
+ NO_CHECK(COMPILE_(ret, "array element", RNODE_LIST(node)->nd_head, FALSE));
}
}
return len;
}
-static inline int
-static_literal_node_p(const NODE *node, const rb_iseq_t *iseq)
+static inline bool
+frozen_string_literal_p(const rb_iseq_t *iseq)
+{
+ return ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal > 0;
+}
+
+static inline bool
+static_literal_node_p(const NODE *node, const rb_iseq_t *iseq, bool hash_key)
{
switch (nd_type(node)) {
- case NODE_LIT:
+ case NODE_SYM:
+ case NODE_REGX:
+ case NODE_LINE:
+ case NODE_ENCODING:
+ case NODE_INTEGER:
+ case NODE_FLOAT:
+ case NODE_RATIONAL:
+ case NODE_IMAGINARY:
case NODE_NIL:
case NODE_TRUE:
case NODE_FALSE:
return TRUE;
case NODE_STR:
- return ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal;
+ case NODE_FILE:
+ return hash_key || frozen_string_literal_p(iseq);
default:
return FALSE;
}
@@ -4471,30 +4754,46 @@ static inline VALUE
static_literal_value(const NODE *node, rb_iseq_t *iseq)
{
switch (nd_type(node)) {
+ case NODE_INTEGER:
+ return rb_node_integer_literal_val(node);
+ case NODE_FLOAT:
+ return rb_node_float_literal_val(node);
+ case NODE_RATIONAL:
+ return rb_node_rational_literal_val(node);
+ case NODE_IMAGINARY:
+ return rb_node_imaginary_literal_val(node);
case NODE_NIL:
return Qnil;
case NODE_TRUE:
return Qtrue;
case NODE_FALSE:
return Qfalse;
+ case NODE_SYM:
+ return rb_node_sym_string_val(node);
+ case NODE_REGX:
+ return rb_node_regx_string_val(node);
+ case NODE_LINE:
+ return rb_node_line_lineno_val(node);
+ case NODE_ENCODING:
+ return rb_node_encoding_val(node);
+ case NODE_FILE:
case NODE_STR:
if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
- VALUE lit;
VALUE debug_info = rb_ary_new_from_args(2, rb_iseq_path(iseq), INT2FIX((int)nd_line(node)));
- lit = rb_str_dup(node->nd_lit);
+ VALUE lit = rb_str_dup(get_string_value(node));
rb_ivar_set(lit, id_debug_created_info, rb_obj_freeze(debug_info));
return rb_str_freeze(lit);
}
else {
- return rb_fstring(node->nd_lit);
+ return get_string_value(node);
}
default:
- return node->nd_lit;
+ rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
}
}
static int
-compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int popped)
+compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int popped, bool first_chunk)
{
const NODE *line_node = node;
@@ -4508,8 +4807,8 @@ compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int pop
EXPECT_NODE("compile_array", node, NODE_LIST, -1);
if (popped) {
- for (; node; node = node->nd_next) {
- NO_CHECK(COMPILE_(ret, "array element", node->nd_head, popped));
+ for (; node; node = RNODE_LIST(node)->nd_next) {
+ NO_CHECK(COMPILE_(ret, "array element", RNODE_LIST(node)->nd_head, popped));
}
return 1;
}
@@ -4529,8 +4828,8 @@ compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int pop
*
* [x1,x2,...,x10000] =>
* push x1 ; push x2 ; ...; push x256; newarray 256;
- * push x257; push x258; ...; push x512; newarray 256; concatarray;
- * push x513; push x514; ...; push x768; newarray 256; concatarray;
+ * push x257; push x258; ...; push x512; pushtoarray 256;
+ * push x513; push x514; ...; push x768; pushtoarray 256;
* ...
*
* - Long subarray can be optimized by pre-allocating a hidden array.
@@ -4540,38 +4839,38 @@ compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int pop
*
* [x, 1,2,3,...,100, z] =>
* push x; newarray 1;
- * putobject [1,2,3,...,100] (<- hidden array); concatarray;
- * push z; newarray 1; concatarray
+ * putobject [1,2,3,...,100] (<- hidden array); concattoarray;
+ * push z; pushtoarray 1;
*
- * - If the last element is a keyword, newarraykwsplat should be emitted
- * to check and remove empty keyword arguments hash from array.
+ * - If the last element is a keyword, pushtoarraykwsplat should be emitted
+ * to only push it onto the array if it is not empty
* (Note: a keyword is NODE_HASH which is not static_literal_node_p.)
*
* [1,2,3,**kw] =>
- * putobject 1; putobject 2; putobject 3; push kw; newarraykwsplat
+ * putobject 1; putobject 2; putobject 3; newarray 3; ...; pushtoarraykwsplat kw
*/
const int max_stack_len = 0x100;
const int min_tmp_ary_len = 0x40;
int stack_len = 0;
- int first_chunk = 1;
- /* Convert pushed elements to an array, and concatarray if needed */
-#define FLUSH_CHUNK(newarrayinsn) \
+ /* Either create a new array, or push to the existing array */
+#define FLUSH_CHUNK \
if (stack_len) { \
- ADD_INSN1(ret, line_node, newarrayinsn, INT2FIX(stack_len)); \
- if (!first_chunk) ADD_INSN(ret, line_node, concatarray); \
- first_chunk = stack_len = 0; \
+ if (first_chunk) ADD_INSN1(ret, line_node, newarray, INT2FIX(stack_len)); \
+ else ADD_INSN1(ret, line_node, pushtoarray, INT2FIX(stack_len)); \
+ first_chunk = FALSE; \
+ stack_len = 0; \
}
while (node) {
int count = 1;
/* pre-allocation check (this branch can be omittable) */
- if (static_literal_node_p(node->nd_head, iseq)) {
+ if (static_literal_node_p(RNODE_LIST(node)->nd_head, iseq, false)) {
/* count the elements that are optimizable */
- const NODE *node_tmp = node->nd_next;
- for (; node_tmp && static_literal_node_p(node_tmp->nd_head, iseq); node_tmp = node_tmp->nd_next)
+ const NODE *node_tmp = RNODE_LIST(node)->nd_next;
+ for (; node_tmp && static_literal_node_p(RNODE_LIST(node_tmp)->nd_head, iseq, false); node_tmp = RNODE_LIST(node_tmp)->nd_next)
count++;
if ((first_chunk && stack_len == 0 && !node_tmp) || count >= min_tmp_ary_len) {
@@ -4579,72 +4878,61 @@ compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int pop
VALUE ary = rb_ary_hidden_new(count);
/* Create a hidden array */
- for (; count; count--, node = node->nd_next)
- rb_ary_push(ary, static_literal_value(node->nd_head, iseq));
+ for (; count; count--, node = RNODE_LIST(node)->nd_next)
+ rb_ary_push(ary, static_literal_value(RNODE_LIST(node)->nd_head, iseq));
OBJ_FREEZE(ary);
/* Emit optimized code */
- FLUSH_CHUNK(newarray);
+ FLUSH_CHUNK;
if (first_chunk) {
ADD_INSN1(ret, line_node, duparray, ary);
- first_chunk = 0;
+ first_chunk = FALSE;
}
else {
ADD_INSN1(ret, line_node, putobject, ary);
- ADD_INSN(ret, line_node, concatarray);
+ ADD_INSN(ret, line_node, concattoarray);
}
RB_OBJ_WRITTEN(iseq, Qundef, ary);
}
}
/* Base case: Compile "count" elements */
- for (; count; count--, node = node->nd_next) {
+ for (; count; count--, node = RNODE_LIST(node)->nd_next) {
if (CPDEBUG > 0) {
EXPECT_NODE("compile_array", node, NODE_LIST, -1);
}
- NO_CHECK(COMPILE_(ret, "array element", node->nd_head, 0));
- stack_len++;
-
- if (!node->nd_next && keyword_node_p(node->nd_head)) {
- /* Reached the end, and the last element is a keyword */
- FLUSH_CHUNK(newarraykwsplat);
+ if (!RNODE_LIST(node)->nd_next && keyword_node_p(RNODE_LIST(node)->nd_head)) {
+ /* Create array or push existing non-keyword elements onto array */
+ if (stack_len == 0 && first_chunk) {
+ ADD_INSN1(ret, line_node, newarray, INT2FIX(0));
+ }
+ else {
+ FLUSH_CHUNK;
+ }
+ NO_CHECK(COMPILE_(ret, "array element", RNODE_LIST(node)->nd_head, 0));
+ ADD_INSN(ret, line_node, pushtoarraykwsplat);
return 1;
}
+ else {
+ NO_CHECK(COMPILE_(ret, "array element", RNODE_LIST(node)->nd_head, 0));
+ stack_len++;
+ }
/* If there are many pushed elements, flush them to avoid stack overflow */
- if (stack_len >= max_stack_len) FLUSH_CHUNK(newarray);
+ if (stack_len >= max_stack_len) FLUSH_CHUNK;
}
}
- FLUSH_CHUNK(newarray);
+ FLUSH_CHUNK;
#undef FLUSH_CHUNK
return 1;
}
-/* Compile an array containing the single element represented by node */
-static int
-compile_array_1(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node)
-{
- if (static_literal_node_p(node, iseq)) {
- VALUE ary = rb_ary_hidden_new(1);
- rb_ary_push(ary, static_literal_value(node, iseq));
- OBJ_FREEZE(ary);
-
- ADD_INSN1(ret, node, duparray, ary);
- }
- else {
- CHECK(COMPILE_(ret, "array element", node, FALSE));
- ADD_INSN1(ret, node, newarray, INT2FIX(1));
- }
-
- return 1;
-}
-
static inline int
static_literal_node_pair_p(const NODE *node, const rb_iseq_t *iseq)
{
- return node->nd_head && static_literal_node_p(node->nd_head, iseq) && static_literal_node_p(node->nd_next->nd_head, iseq);
+ return RNODE_LIST(node)->nd_head && static_literal_node_p(RNODE_LIST(node)->nd_head, iseq, true) && static_literal_node_p(RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head, iseq, false);
}
static int
@@ -4652,7 +4940,7 @@ compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int meth
{
const NODE *line_node = node;
- node = node->nd_head;
+ node = RNODE_HASH(node)->nd_head;
if (!node || nd_type_p(node, NODE_ZLIST)) {
if (!popped) {
@@ -4664,8 +4952,8 @@ compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int meth
EXPECT_NODE("compile_hash", node, NODE_LIST, -1);
if (popped) {
- for (; node; node = node->nd_next) {
- NO_CHECK(COMPILE_(ret, "hash element", node->nd_head, popped));
+ for (; node; node = RNODE_LIST(node)->nd_next) {
+ NO_CHECK(COMPILE_(ret, "hash element", RNODE_LIST(node)->nd_head, popped));
}
return 1;
}
@@ -4718,8 +5006,8 @@ compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int meth
/* pre-allocation check (this branch can be omittable) */
if (static_literal_node_pair_p(node, iseq)) {
/* count the elements that are optimizable */
- const NODE *node_tmp = node->nd_next->nd_next;
- for (; node_tmp && static_literal_node_pair_p(node_tmp, iseq); node_tmp = node_tmp->nd_next->nd_next)
+ const NODE *node_tmp = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next;
+ for (; node_tmp && static_literal_node_pair_p(node_tmp, iseq); node_tmp = RNODE_LIST(RNODE_LIST(node_tmp)->nd_next)->nd_next)
count++;
if ((first_chunk && stack_len == 0 && !node_tmp) || count >= min_tmp_hash_len) {
@@ -4727,14 +5015,14 @@ compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int meth
VALUE ary = rb_ary_hidden_new(count);
/* Create a hidden hash */
- for (; count; count--, node = node->nd_next->nd_next) {
+ for (; count; count--, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) {
VALUE elem[2];
- elem[0] = static_literal_value(node->nd_head, iseq);
- elem[1] = static_literal_value(node->nd_next->nd_head, iseq);
+ elem[0] = static_literal_value(RNODE_LIST(node)->nd_head, iseq);
+ elem[1] = static_literal_value(RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head, iseq);
rb_ary_cat(ary, elem, 2);
}
VALUE hash = rb_hash_new_with_size(RARRAY_LEN(ary) / 2);
- rb_hash_bulk_insert(RARRAY_LEN(ary), RARRAY_CONST_PTR_TRANSIENT(ary), hash);
+ rb_hash_bulk_insert(RARRAY_LEN(ary), RARRAY_CONST_PTR(ary), hash);
hash = rb_obj_hide(hash);
OBJ_FREEZE(hash);
@@ -4757,16 +5045,16 @@ compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int meth
}
/* Base case: Compile "count" elements */
- for (; count; count--, node = node->nd_next->nd_next) {
+ for (; count; count--, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) {
if (CPDEBUG > 0) {
EXPECT_NODE("compile_hash", node, NODE_LIST, -1);
}
- if (node->nd_head) {
+ if (RNODE_LIST(node)->nd_head) {
/* Normal key-value pair */
- NO_CHECK(COMPILE_(anchor, "hash key element", node->nd_head, 0));
- NO_CHECK(COMPILE_(anchor, "hash value element", node->nd_next->nd_head, 0));
+ NO_CHECK(COMPILE_(anchor, "hash key element", RNODE_LIST(node)->nd_head, 0));
+ NO_CHECK(COMPILE_(anchor, "hash value element", RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head, 0));
stack_len += 2;
/* If there are many pushed elements, flush them to avoid stack overflow */
@@ -4776,12 +5064,13 @@ compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int meth
/* kwsplat case: foo(..., **kw, ...) */
FLUSH_CHUNK();
- const NODE *kw = node->nd_next->nd_head;
- int empty_kw = nd_type_p(kw, NODE_LIT) && RB_TYPE_P(kw->nd_lit, T_HASH); /* foo( ..., **{}, ...) */
+ const NODE *kw = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head;
+ int empty_kw = nd_type_p(kw, NODE_HASH) && (!RNODE_HASH(kw)->nd_head); /* foo( ..., **{}, ...) */
int first_kw = first_chunk && stack_len == 0; /* foo(1,2,3, **kw, ...) */
- int last_kw = !node->nd_next->nd_next; /* foo( ..., **kw) */
+ int last_kw = !RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next; /* foo( ..., **kw) */
int only_kw = last_kw && first_kw; /* foo(1,2,3, **kw) */
+ empty_kw = empty_kw || nd_type_p(kw, NODE_NIL); /* foo( ..., **nil, ...) */
if (empty_kw) {
if (only_kw && method_call_keywords) {
/* **{} appears at the only keyword argument in method call,
@@ -4841,29 +5130,34 @@ VALUE
rb_node_case_when_optimizable_literal(const NODE *const node)
{
switch (nd_type(node)) {
- case NODE_LIT: {
- VALUE v = node->nd_lit;
+ case NODE_INTEGER:
+ return rb_node_integer_literal_val(node);
+ case NODE_FLOAT: {
+ VALUE v = rb_node_float_literal_val(node);
double ival;
- if (RB_FLOAT_TYPE_P(v) &&
- modf(RFLOAT_VALUE(v), &ival) == 0.0) {
+
+ if (modf(RFLOAT_VALUE(v), &ival) == 0.0) {
return FIXABLE(ival) ? LONG2FIX((long)ival) : rb_dbl2big(ival);
}
- if (RB_TYPE_P(v, T_RATIONAL) || RB_TYPE_P(v, T_COMPLEX)) {
- return Qundef;
- }
- if (SYMBOL_P(v) || rb_obj_is_kind_of(v, rb_cNumeric)) {
- return v;
- }
- break;
+ return v;
}
+ case NODE_RATIONAL:
+ case NODE_IMAGINARY:
+ return Qundef;
case NODE_NIL:
return Qnil;
case NODE_TRUE:
return Qtrue;
case NODE_FALSE:
return Qfalse;
+ case NODE_SYM:
+ return rb_node_sym_string_val(node);
+ case NODE_LINE:
+ return rb_node_line_lineno_val(node);
case NODE_STR:
- return rb_fstring(node->nd_lit);
+ return rb_node_str_string_val(node);
+ case NODE_FILE:
+ return rb_node_file_path_val(node);
}
return Qundef;
}
@@ -4873,7 +5167,7 @@ when_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals,
LABEL *l1, int only_special_literals, VALUE literals)
{
while (vals) {
- const NODE *val = vals->nd_head;
+ const NODE *val = RNODE_LIST(vals)->nd_head;
VALUE lit = rb_node_case_when_optimizable_literal(val);
if (UNDEF_P(lit)) {
@@ -4883,9 +5177,9 @@ when_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals,
rb_hash_aset(literals, lit, (VALUE)(l1) | 1);
}
- if (nd_type_p(val, NODE_STR)) {
- debugp_param("nd_lit", val->nd_lit);
- lit = rb_fstring(val->nd_lit);
+ if (nd_type_p(val, NODE_STR) || nd_type_p(val, NODE_FILE)) {
+ debugp_param("nd_lit", get_string_value(val));
+ lit = get_string_value(val);
ADD_INSN1(cond_seq, val, putobject, lit);
RB_OBJ_WRITTEN(iseq, Qundef, lit);
}
@@ -4897,7 +5191,7 @@ when_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals,
ADD_INSN1(cond_seq, vals, topn, INT2FIX(1));
ADD_CALL(cond_seq, vals, idEqq, INT2FIX(1));
ADD_INSNL(cond_seq, val, branchif, l1);
- vals = vals->nd_next;
+ vals = RNODE_LIST(vals)->nd_next;
}
return only_special_literals;
}
@@ -4915,19 +5209,19 @@ when_splat_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals,
break;
case NODE_SPLAT:
ADD_INSN (cond_seq, line_node, dup);
- CHECK(COMPILE(cond_seq, "when splat", vals->nd_head));
+ CHECK(COMPILE(cond_seq, "when splat", RNODE_SPLAT(vals)->nd_head));
ADD_INSN1(cond_seq, line_node, splatarray, Qfalse);
ADD_INSN1(cond_seq, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY));
ADD_INSNL(cond_seq, line_node, branchif, l1);
break;
case NODE_ARGSCAT:
- CHECK(when_splat_vals(iseq, cond_seq, vals->nd_head, l1, only_special_literals, literals));
- CHECK(when_splat_vals(iseq, cond_seq, vals->nd_body, l1, only_special_literals, literals));
+ CHECK(when_splat_vals(iseq, cond_seq, RNODE_ARGSCAT(vals)->nd_head, l1, only_special_literals, literals));
+ CHECK(when_splat_vals(iseq, cond_seq, RNODE_ARGSCAT(vals)->nd_body, l1, only_special_literals, literals));
break;
case NODE_ARGSPUSH:
- CHECK(when_splat_vals(iseq, cond_seq, vals->nd_head, l1, only_special_literals, literals));
+ CHECK(when_splat_vals(iseq, cond_seq, RNODE_ARGSPUSH(vals)->nd_head, l1, only_special_literals, literals));
ADD_INSN (cond_seq, line_node, dup);
- CHECK(COMPILE(cond_seq, "when argspush body", vals->nd_body));
+ CHECK(COMPILE(cond_seq, "when argspush body", RNODE_ARGSPUSH(vals)->nd_body));
ADD_INSN1(cond_seq, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
ADD_INSNL(cond_seq, line_node, branchif, l1);
break;
@@ -5113,11 +5407,35 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const
ADD_ELEM(lhs, (LINK_ELEMENT *)iobj);
if (vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT) {
int argc = vm_ci_argc(ci);
+ bool dupsplat = false;
ci = ci_argc_set(iseq, ci, argc - 1);
+ if (!(vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT_MUT)) {
+ /* Given h[*a], _ = ary
+ * setup_args sets VM_CALL_ARGS_SPLAT and not VM_CALL_ARGS_SPLAT_MUT
+ * `a` must be dupped, because it will be appended with ary[0]
+ * Since you are dupping `a`, you can set VM_CALL_ARGS_SPLAT_MUT
+ */
+ dupsplat = true;
+ ci = ci_flag_set(iseq, ci, VM_CALL_ARGS_SPLAT_MUT);
+ }
OPERAND_AT(iobj, 0) = (VALUE)ci;
RB_OBJ_WRITTEN(iseq, Qundef, iobj);
- INSERT_BEFORE_INSN1(iobj, line_node, newarray, INT2FIX(1));
- INSERT_BEFORE_INSN(iobj, line_node, concatarray);
+
+ /* Given: h[*a], h[*b, 1] = ary
+ * h[*a] uses splatarray false and does not set VM_CALL_ARGS_SPLAT_MUT,
+ * so this uses splatarray true on a to dup it before using pushtoarray
+ * h[*b, 1] uses splatarray true and sets VM_CALL_ARGS_SPLAT_MUT,
+ * so you can use pushtoarray directly
+ */
+ int line_no = nd_line(line_node);
+ int node_id = nd_node_id(line_node);
+
+ if (dupsplat) {
+ INSERT_BEFORE_INSN(iobj, line_no, node_id, swap);
+ INSERT_BEFORE_INSN1(iobj, line_no, node_id, splatarray, Qtrue);
+ INSERT_BEFORE_INSN(iobj, line_no, node_id, swap);
+ }
+ INSERT_BEFORE_INSN1(iobj, line_no, node_id, pushtoarray, INT2FIX(1));
}
ADD_INSN(lhs, line_node, pop);
if (argc != 1) {
@@ -5147,7 +5465,7 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const
break;
}
case NODE_CDECL:
- if (!node->nd_vid) {
+ if (!RNODE_CDECL(node)->nd_vid) {
/* Special handling only needed for expr::C, not for C */
INSN *iobj;
@@ -5185,8 +5503,8 @@ static int
compile_massign_opt_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *lhsn)
{
if (lhsn) {
- CHECK(compile_massign_opt_lhs(iseq, ret, lhsn->nd_next));
- CHECK(compile_massign_lhs(iseq, ret, ret, ret, ret, lhsn->nd_head, NULL, 0));
+ CHECK(compile_massign_opt_lhs(iseq, ret, RNODE_LIST(lhsn)->nd_next));
+ CHECK(compile_massign_lhs(iseq, ret, ret, ret, ret, RNODE_LIST(lhsn)->nd_head, NULL, 0));
}
return COMPILE_OK;
}
@@ -5216,31 +5534,29 @@ compile_massign_opt(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
}
while (lhsn) {
- const NODE *ln = lhsn->nd_head;
+ const NODE *ln = RNODE_LIST(lhsn)->nd_head;
switch (nd_type(ln)) {
case NODE_LASGN:
- MEMORY(ln->nd_vid);
- break;
case NODE_DASGN:
case NODE_IASGN:
case NODE_CVASGN:
- MEMORY(ln->nd_vid);
+ MEMORY(get_nd_vid(ln));
break;
default:
return 0;
}
- lhsn = lhsn->nd_next;
+ lhsn = RNODE_LIST(lhsn)->nd_next;
llen++;
}
while (rhsn) {
if (llen <= rlen) {
- NO_CHECK(COMPILE_POPPED(ret, "masgn val (popped)", rhsn->nd_head));
+ NO_CHECK(COMPILE_POPPED(ret, "masgn val (popped)", RNODE_LIST(rhsn)->nd_head));
}
else {
- NO_CHECK(COMPILE(ret, "masgn val", rhsn->nd_head));
+ NO_CHECK(COMPILE(ret, "masgn val", RNODE_LIST(rhsn)->nd_head));
}
- rhsn = rhsn->nd_next;
+ rhsn = RNODE_LIST(rhsn)->nd_next;
rlen++;
}
@@ -5257,32 +5573,31 @@ compile_massign_opt(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
static int
compile_massign0(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const rhs, LINK_ANCHOR *const lhs, LINK_ANCHOR *const post, const NODE *const node, struct masgn_state *state, int popped)
{
- const NODE *rhsn = node->nd_value;
- const NODE *splatn = node->nd_args;
- const NODE *lhsn = node->nd_head;
+ const NODE *rhsn = RNODE_MASGN(node)->nd_value;
+ const NODE *splatn = RNODE_MASGN(node)->nd_args;
+ const NODE *lhsn = RNODE_MASGN(node)->nd_head;
const NODE *lhsn_count = lhsn;
int lhs_splat = (splatn && NODE_NAMED_REST_P(splatn)) ? 1 : 0;
int llen = 0;
int lpos = 0;
- int expand = 1;
while (lhsn_count) {
llen++;
- lhsn_count = lhsn_count->nd_next;
+ lhsn_count = RNODE_LIST(lhsn_count)->nd_next;
}
while (lhsn) {
- CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, lhsn->nd_head, state, (llen - lpos) + lhs_splat + state->lhs_level));
+ CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, RNODE_LIST(lhsn)->nd_head, state, (llen - lpos) + lhs_splat + state->lhs_level));
lpos++;
- lhsn = lhsn->nd_next;
+ lhsn = RNODE_LIST(lhsn)->nd_next;
}
if (lhs_splat) {
if (nd_type_p(splatn, NODE_POSTARG)) {
/*a, b, *r, p1, p2 */
- const NODE *postn = splatn->nd_2nd;
- const NODE *restn = splatn->nd_1st;
- int plen = (int)postn->nd_alen;
+ const NODE *postn = RNODE_POSTARG(splatn)->nd_2nd;
+ const NODE *restn = RNODE_POSTARG(splatn)->nd_1st;
+ int plen = (int)RNODE_LIST(postn)->as.nd_alen;
int ppos = 0;
int flag = 0x02 | (NODE_NAMED_REST_P(restn) ? 0x01 : 0x00);
@@ -5292,9 +5607,9 @@ compile_massign0(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const rhs
CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, restn, state, 1 + plen + state->lhs_level));
}
while (postn) {
- CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, postn->nd_head, state, (plen - ppos) + state->lhs_level));
+ CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, RNODE_LIST(postn)->nd_head, state, (plen - ppos) + state->lhs_level));
ppos++;
- postn = postn->nd_next;
+ postn = RNODE_LIST(postn)->nd_next;
}
}
else {
@@ -5303,7 +5618,6 @@ compile_massign0(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const rhs
}
}
-
if (!state->nested) {
NO_CHECK(COMPILE(rhs, "normal masgn rhs", rhsn));
}
@@ -5311,16 +5625,14 @@ compile_massign0(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const rhs
if (!popped) {
ADD_INSN(rhs, node, dup);
}
- if (expand) {
- ADD_INSN2(rhs, node, expandarray, INT2FIX(llen), INT2FIX(lhs_splat));
- }
+ ADD_INSN2(rhs, node, expandarray, INT2FIX(llen), INT2FIX(lhs_splat));
return COMPILE_OK;
}
static int
compile_massign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
{
- if (!popped || node->nd_args || !compile_massign_opt(iseq, ret, node->nd_value, node->nd_head)) {
+ if (!popped || RNODE_MASGN(node)->nd_args || !compile_massign_opt(iseq, ret, RNODE_MASGN(node)->nd_value, RNODE_MASGN(node)->nd_head)) {
struct masgn_state state;
state.lhs_level = popped ? 0 : 1;
state.nested = 0;
@@ -5342,7 +5654,7 @@ compile_massign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
while (memo) {
VALUE topn_arg = INT2FIX((state.num_args - memo->argn) + memo->lhs_pos);
for (int i = 0; i < memo->num_args; i++) {
- INSERT_BEFORE_INSN1(memo->before_insn, memo->line_node, topn, topn_arg);
+ INSERT_BEFORE_INSN1(memo->before_insn, nd_line(memo->line_node), nd_node_id(memo->line_node), topn, topn_arg);
}
tmp_memo = memo->next;
free(memo);
@@ -5369,15 +5681,15 @@ collect_const_segments(rb_iseq_t *iseq, const NODE *node)
for (;;) {
switch (nd_type(node)) {
case NODE_CONST:
- rb_ary_unshift(arr, ID2SYM(node->nd_vid));
+ rb_ary_unshift(arr, ID2SYM(RNODE_CONST(node)->nd_vid));
return arr;
case NODE_COLON3:
- rb_ary_unshift(arr, ID2SYM(node->nd_mid));
+ rb_ary_unshift(arr, ID2SYM(RNODE_COLON3(node)->nd_mid));
rb_ary_unshift(arr, ID2SYM(idNULL));
return arr;
case NODE_COLON2:
- rb_ary_unshift(arr, ID2SYM(node->nd_mid));
- node = node->nd_head;
+ rb_ary_unshift(arr, ID2SYM(RNODE_COLON2(node)->nd_mid));
+ node = RNODE_COLON2(node)->nd_head;
break;
default:
return Qfalse;
@@ -5391,22 +5703,22 @@ compile_const_prefix(rb_iseq_t *iseq, const NODE *const node,
{
switch (nd_type(node)) {
case NODE_CONST:
- debugi("compile_const_prefix - colon", node->nd_vid);
+ debugi("compile_const_prefix - colon", RNODE_CONST(node)->nd_vid);
ADD_INSN1(body, node, putobject, Qtrue);
- ADD_INSN1(body, node, getconstant, ID2SYM(node->nd_vid));
+ ADD_INSN1(body, node, getconstant, ID2SYM(RNODE_CONST(node)->nd_vid));
break;
case NODE_COLON3:
- debugi("compile_const_prefix - colon3", node->nd_mid);
+ debugi("compile_const_prefix - colon3", RNODE_COLON3(node)->nd_mid);
ADD_INSN(body, node, pop);
ADD_INSN1(body, node, putobject, rb_cObject);
ADD_INSN1(body, node, putobject, Qtrue);
- ADD_INSN1(body, node, getconstant, ID2SYM(node->nd_mid));
+ ADD_INSN1(body, node, getconstant, ID2SYM(RNODE_COLON3(node)->nd_mid));
break;
case NODE_COLON2:
- CHECK(compile_const_prefix(iseq, node->nd_head, pref, body));
- debugi("compile_const_prefix - colon2", node->nd_mid);
+ CHECK(compile_const_prefix(iseq, RNODE_COLON2(node)->nd_head, pref, body));
+ debugi("compile_const_prefix - colon2", RNODE_COLON2(node)->nd_mid);
ADD_INSN1(body, node, putobject, Qfalse);
- ADD_INSN1(body, node, getconstant, ID2SYM(node->nd_mid));
+ ADD_INSN1(body, node, getconstant, ID2SYM(RNODE_COLON2(node)->nd_mid));
break;
default:
CHECK(COMPILE(pref, "const colon2 prefix", node));
@@ -5423,9 +5735,9 @@ compile_cpath(LINK_ANCHOR *const ret, rb_iseq_t *iseq, const NODE *cpath)
ADD_INSN1(ret, cpath, putobject, rb_cObject);
return VM_DEFINECLASS_FLAG_SCOPED;
}
- else if (cpath->nd_head) {
+ else if (nd_type_p(cpath, NODE_COLON2) && RNODE_COLON2(cpath)->nd_head) {
/* Bar::Foo */
- NO_CHECK(COMPILE(ret, "nd_else->nd_head", cpath->nd_head));
+ NO_CHECK(COMPILE(ret, "nd_else->nd_head", RNODE_COLON2(cpath)->nd_head));
return VM_DEFINECLASS_FLAG_SCOPED;
}
else {
@@ -5439,9 +5751,9 @@ compile_cpath(LINK_ANCHOR *const ret, rb_iseq_t *iseq, const NODE *cpath)
static inline int
private_recv_p(const NODE *node)
{
- if (nd_type_p(node->nd_recv, NODE_SELF)) {
- NODE *self = node->nd_recv;
- return self->nd_state != 0;
+ NODE *recv = get_nd_recv(node);
+ if (recv && nd_type_p(recv, NODE_SELF)) {
+ return RNODE_SELF(recv)->nd_state != 0;
}
return 0;
}
@@ -5483,17 +5795,25 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
const NODE *vals = node;
do {
- defined_expr0(iseq, ret, vals->nd_head, lfinish, Qfalse, false);
+ defined_expr0(iseq, ret, RNODE_LIST(vals)->nd_head, lfinish, Qfalse, false);
if (!lfinish[1]) {
lfinish[1] = NEW_LABEL(line);
}
ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
- } while ((vals = vals->nd_next) != NULL);
+ } while ((vals = RNODE_LIST(vals)->nd_next) != NULL);
}
/* fall through */
case NODE_STR:
- case NODE_LIT:
+ case NODE_SYM:
+ case NODE_REGX:
+ case NODE_LINE:
+ case NODE_FILE:
+ case NODE_ENCODING:
+ case NODE_INTEGER:
+ case NODE_FLOAT:
+ case NODE_RATIONAL:
+ case NODE_IMAGINARY:
case NODE_ZLIST:
case NODE_AND:
case NODE_OR:
@@ -5510,47 +5830,47 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
#define PUSH_VAL(type) (needstr == Qfalse ? Qtrue : rb_iseq_defined_string(type))
case NODE_IVAR:
ADD_INSN3(ret, line_node, definedivar,
- ID2SYM(node->nd_vid), get_ivar_ic_value(iseq,node->nd_vid), PUSH_VAL(DEFINED_IVAR));
+ ID2SYM(RNODE_IVAR(node)->nd_vid), get_ivar_ic_value(iseq,RNODE_IVAR(node)->nd_vid), PUSH_VAL(DEFINED_IVAR));
return;
case NODE_GVAR:
ADD_INSN(ret, line_node, putnil);
ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_GVAR),
- ID2SYM(node->nd_entry), PUSH_VAL(DEFINED_GVAR));
+ ID2SYM(RNODE_GVAR(node)->nd_vid), PUSH_VAL(DEFINED_GVAR));
return;
case NODE_CVAR:
ADD_INSN(ret, line_node, putnil);
ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_CVAR),
- ID2SYM(node->nd_vid), PUSH_VAL(DEFINED_CVAR));
+ ID2SYM(RNODE_CVAR(node)->nd_vid), PUSH_VAL(DEFINED_CVAR));
return;
case NODE_CONST:
ADD_INSN(ret, line_node, putnil);
ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_CONST),
- ID2SYM(node->nd_vid), PUSH_VAL(DEFINED_CONST));
+ ID2SYM(RNODE_CONST(node)->nd_vid), PUSH_VAL(DEFINED_CONST));
return;
case NODE_COLON2:
if (!lfinish[1]) {
lfinish[1] = NEW_LABEL(line);
}
- defined_expr0(iseq, ret, node->nd_head, lfinish, Qfalse, false);
+ defined_expr0(iseq, ret, RNODE_COLON2(node)->nd_head, lfinish, Qfalse, false);
ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
- NO_CHECK(COMPILE(ret, "defined/colon2#nd_head", node->nd_head));
+ NO_CHECK(COMPILE(ret, "defined/colon2#nd_head", RNODE_COLON2(node)->nd_head));
- if (rb_is_const_id(node->nd_mid)) {
+ if (rb_is_const_id(RNODE_COLON2(node)->nd_mid)) {
ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_CONST_FROM),
- ID2SYM(node->nd_mid), PUSH_VAL(DEFINED_CONST));
+ ID2SYM(RNODE_COLON2(node)->nd_mid), PUSH_VAL(DEFINED_CONST));
}
else {
ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_METHOD),
- ID2SYM(node->nd_mid), PUSH_VAL(DEFINED_METHOD));
+ ID2SYM(RNODE_COLON2(node)->nd_mid), PUSH_VAL(DEFINED_METHOD));
}
return;
case NODE_COLON3:
ADD_INSN1(ret, line_node, putobject, rb_cObject);
ADD_INSN3(ret, line_node, defined,
- INT2FIX(DEFINED_CONST_FROM), ID2SYM(node->nd_mid), PUSH_VAL(DEFINED_CONST));
+ INT2FIX(DEFINED_CONST_FROM), ID2SYM(RNODE_COLON3(node)->nd_mid), PUSH_VAL(DEFINED_CONST));
return;
/* method dispatch */
@@ -5563,7 +5883,7 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
(type == NODE_CALL || type == NODE_OPCALL ||
(type == NODE_ATTRASGN && !private_recv_p(node)));
- if (node->nd_args || explicit_receiver) {
+ if (get_nd_args(node) || explicit_receiver) {
if (!lfinish[1]) {
lfinish[1] = NEW_LABEL(line);
}
@@ -5571,31 +5891,31 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
lfinish[2] = NEW_LABEL(line);
}
}
- if (node->nd_args) {
- defined_expr0(iseq, ret, node->nd_args, lfinish, Qfalse, false);
+ if (get_nd_args(node)) {
+ defined_expr0(iseq, ret, get_nd_args(node), lfinish, Qfalse, false);
ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
}
if (explicit_receiver) {
- defined_expr0(iseq, ret, node->nd_recv, lfinish, Qfalse, true);
- switch (nd_type(node->nd_recv)) {
+ defined_expr0(iseq, ret, get_nd_recv(node), lfinish, Qfalse, true);
+ switch (nd_type(get_nd_recv(node))) {
case NODE_CALL:
case NODE_OPCALL:
case NODE_VCALL:
case NODE_FCALL:
case NODE_ATTRASGN:
ADD_INSNL(ret, line_node, branchunless, lfinish[2]);
- compile_call(iseq, ret, node->nd_recv, nd_type(node->nd_recv), line_node, 0, true);
+ compile_call(iseq, ret, get_nd_recv(node), nd_type(get_nd_recv(node)), line_node, 0, true);
break;
default:
ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
- NO_CHECK(COMPILE(ret, "defined/recv", node->nd_recv));
+ NO_CHECK(COMPILE(ret, "defined/recv", get_nd_recv(node)));
break;
}
if (keep_result) {
ADD_INSN(ret, line_node, dup);
}
ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_METHOD),
- ID2SYM(node->nd_mid), PUSH_VAL(DEFINED_METHOD));
+ ID2SYM(get_node_call_nd_mid(node)), PUSH_VAL(DEFINED_METHOD));
}
else {
ADD_INSN(ret, line_node, putself);
@@ -5603,7 +5923,7 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
ADD_INSN(ret, line_node, dup);
}
ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_FUNC),
- ID2SYM(node->nd_mid), PUSH_VAL(DEFINED_METHOD));
+ ID2SYM(get_node_call_nd_mid(node)), PUSH_VAL(DEFINED_METHOD));
}
return;
}
@@ -5612,13 +5932,14 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
ADD_INSN(ret, line_node, putnil);
ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_YIELD), 0,
PUSH_VAL(DEFINED_YIELD));
+ iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
return;
case NODE_BACK_REF:
case NODE_NTH_REF:
ADD_INSN(ret, line_node, putnil);
ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_REF),
- INT2FIX((node->nd_nth << 1) | (type == NODE_BACK_REF)),
+ INT2FIX((RNODE_BACK_REF(node)->nd_nth << 1) | (type == NODE_BACK_REF)),
PUSH_VAL(DEFINED_GVAR));
return;
@@ -5641,11 +5962,12 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
case NODE_IASGN:
case NODE_CDECL:
case NODE_CVASGN:
+ case NODE_OP_CDECL:
expr_type = DEFINED_ASGN;
break;
}
- assert(expr_type != DEFINED_NOT_DEFINED);
+ RUBY_ASSERT(expr_type != DEFINED_NOT_DEFINED);
if (needstr != Qfalse) {
VALUE str = rb_iseq_defined_string(expr_type);
@@ -5659,8 +5981,7 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
static void
build_defined_rescue_iseq(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const void *unused)
{
- NODE dummy_line_node = generate_dummy_line_node(0, -1);
- ADD_INSN(ret, &dummy_line_node, putnil);
+ ADD_SYNTHETIC_INSN(ret, 0, -1, putnil);
iseq_set_exception_local_table(iseq);
}
@@ -5694,7 +6015,7 @@ compile_defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const
{
const int line = nd_line(node);
const NODE *line_node = node;
- if (!node->nd_head) {
+ if (!RNODE_DEFINED(node)->nd_head) {
VALUE str = rb_iseq_defined_string(DEFINED_NIL);
ADD_INSN1(ret, line_node, putobject, str);
}
@@ -5704,9 +6025,9 @@ compile_defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const
lfinish[0] = NEW_LABEL(line);
lfinish[1] = 0;
lfinish[2] = 0;
- defined_expr(iseq, ret, node->nd_head, lfinish, needstr);
+ defined_expr(iseq, ret, RNODE_DEFINED(node)->nd_head, lfinish, needstr);
if (lfinish[1]) {
- ELEM_INSERT_NEXT(last, &new_insn_body(iseq, line_node, BIN(putnil), 0)->link);
+ ELEM_INSERT_NEXT(last, &new_insn_body(iseq, nd_line(line_node), nd_node_id(line_node), BIN(putnil), 0)->link);
ADD_INSN(ret, line_node, swap);
if (lfinish[2]) {
ADD_LABEL(ret, lfinish[2]);
@@ -5745,7 +6066,7 @@ make_name_for_block(const rb_iseq_t *orig_iseq)
static void
push_ensure_entry(rb_iseq_t *iseq,
struct iseq_compile_data_ensure_node_stack *enl,
- struct ensure_range *er, const NODE *const node)
+ struct ensure_range *er, const void *const node)
{
enl->ensure_node = node;
enl->prev = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack; /* prev */
@@ -5787,7 +6108,7 @@ can_add_ensure_iseq(const rb_iseq_t *iseq)
static void
add_ensure_iseq(LINK_ANCHOR *const ret, rb_iseq_t *iseq, int is_return)
{
- assert(can_add_ensure_iseq(iseq));
+ RUBY_ASSERT(can_add_ensure_iseq(iseq));
struct iseq_compile_data_ensure_node_stack *enlp =
ISEQ_COMPILE_DATA(iseq)->ensure_node_stack;
@@ -5828,10 +6149,10 @@ check_keyword(const NODE *node)
/* This check is essentially a code clone of compile_keyword_arg. */
if (nd_type_p(node, NODE_LIST)) {
- while (node->nd_next) {
- node = node->nd_next;
+ while (RNODE_LIST(node)->nd_next) {
+ node = RNODE_LIST(node)->nd_next;
}
- node = node->nd_head;
+ node = RNODE_LIST(node)->nd_head;
}
return keyword_node_p(node);
@@ -5843,9 +6164,9 @@ keyword_node_single_splat_p(NODE *kwnode)
{
RUBY_ASSERT(keyword_node_p(kwnode));
- NODE *node = kwnode->nd_head;
- return node->nd_head == NULL &&
- node->nd_next->nd_next == NULL;
+ NODE *node = RNODE_HASH(kwnode)->nd_head;
+ return RNODE_LIST(node)->nd_head == NULL &&
+ RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next == NULL;
}
static int
@@ -5858,99 +6179,101 @@ setup_args_core(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
switch (nd_type(argn)) {
case NODE_LIST: {
- // f(x, y, z)
- int len = compile_args(iseq, args, argn, &kwnode);
- RUBY_ASSERT(flag_ptr == NULL || (*flag_ptr & VM_CALL_ARGS_SPLAT) == 0);
+ // f(x, y, z)
+ int len = compile_args(iseq, args, argn, &kwnode);
+ RUBY_ASSERT(flag_ptr == NULL || (*flag_ptr & VM_CALL_ARGS_SPLAT) == 0);
- if (kwnode) {
- if (compile_keyword_arg(iseq, args, kwnode, kwarg_ptr, flag_ptr)) {
- len -= 1;
- }
- else {
- compile_hash(iseq, args, kwnode, TRUE, FALSE);
- }
- }
+ if (kwnode) {
+ if (compile_keyword_arg(iseq, args, kwnode, kwarg_ptr, flag_ptr)) {
+ len -= 1;
+ }
+ else {
+ compile_hash(iseq, args, kwnode, TRUE, FALSE);
+ }
+ }
- return len;
+ return len;
}
case NODE_SPLAT: {
- // f(*a)
- NO_CHECK(COMPILE(args, "args (splat)", argn->nd_head));
- ADD_INSN1(args, argn, splatarray, RBOOL(dup_rest));
- if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT;
- RUBY_ASSERT(flag_ptr == NULL || (*flag_ptr & VM_CALL_KW_SPLAT) == 0);
- return 1;
+ // f(*a)
+ NO_CHECK(COMPILE(args, "args (splat)", RNODE_SPLAT(argn)->nd_head));
+ ADD_INSN1(args, argn, splatarray, RBOOL(dup_rest));
+ if (flag_ptr) {
+ *flag_ptr |= VM_CALL_ARGS_SPLAT;
+ if (dup_rest) *flag_ptr |= VM_CALL_ARGS_SPLAT_MUT;
+ }
+ RUBY_ASSERT(flag_ptr == NULL || (*flag_ptr & VM_CALL_KW_SPLAT) == 0);
+ return 1;
}
case NODE_ARGSCAT: {
- if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT;
- int argc = setup_args_core(iseq, args, argn->nd_head, 1, NULL, NULL);
+ if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_SPLAT_MUT;
+ int argc = setup_args_core(iseq, args, RNODE_ARGSCAT(argn)->nd_head, 1, NULL, NULL);
+ bool args_pushed = false;
- if (nd_type_p(argn->nd_body, NODE_LIST)) {
- int rest_len = compile_args(iseq, args, argn->nd_body, &kwnode);
- if (kwnode) rest_len--;
- ADD_INSN1(args, argn, newarray, INT2FIX(rest_len));
- }
- else {
- RUBY_ASSERT(!check_keyword(argn->nd_body));
- NO_CHECK(COMPILE(args, "args (cat: splat)", argn->nd_body));
- }
+ if (nd_type_p(RNODE_ARGSCAT(argn)->nd_body, NODE_LIST)) {
+ int rest_len = compile_args(iseq, args, RNODE_ARGSCAT(argn)->nd_body, &kwnode);
+ if (kwnode) rest_len--;
+ ADD_INSN1(args, argn, pushtoarray, INT2FIX(rest_len));
+ args_pushed = true;
+ }
+ else {
+ RUBY_ASSERT(!check_keyword(RNODE_ARGSCAT(argn)->nd_body));
+ NO_CHECK(COMPILE(args, "args (cat: splat)", RNODE_ARGSCAT(argn)->nd_body));
+ }
- if (nd_type_p(argn->nd_head, NODE_LIST)) {
- ADD_INSN1(args, argn, splatarray, Qtrue);
- argc += 1;
- }
- else {
- ADD_INSN1(args, argn, splatarray, Qfalse);
- ADD_INSN(args, argn, concatarray);
- }
+ if (nd_type_p(RNODE_ARGSCAT(argn)->nd_head, NODE_LIST)) {
+ ADD_INSN1(args, argn, splatarray, Qtrue);
+ argc += 1;
+ }
+ else if (!args_pushed) {
+ ADD_INSN(args, argn, concattoarray);
+ }
- // f(..., *a, ..., k1:1, ...) #=> f(..., *[*a, ...], **{k1:1, ...})
- if (kwnode) {
- // kwsplat
- *flag_ptr |= VM_CALL_KW_SPLAT;
- *flag_ptr |= VM_CALL_KW_SPLAT_MUT;
- compile_hash(iseq, args, kwnode, TRUE, FALSE);
- argc += 1;
- }
+ // f(..., *a, ..., k1:1, ...) #=> f(..., *[*a, ...], **{k1:1, ...})
+ if (kwnode) {
+ // kwsplat
+ *flag_ptr |= VM_CALL_KW_SPLAT;
+ *flag_ptr |= VM_CALL_KW_SPLAT_MUT;
+ compile_hash(iseq, args, kwnode, TRUE, FALSE);
+ argc += 1;
+ }
- return argc;
+ return argc;
}
case NODE_ARGSPUSH: {
- if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT;
- int argc = setup_args_core(iseq, args, argn->nd_head, 1, NULL, NULL);
-
- if (nd_type_p(argn->nd_body, NODE_LIST)) {
- int rest_len = compile_args(iseq, args, argn->nd_body, &kwnode);
- if (kwnode) rest_len--;
- ADD_INSN1(args, argn, newarray, INT2FIX(rest_len));
- ADD_INSN1(args, argn, newarray, INT2FIX(1));
- ADD_INSN(args, argn, concatarray);
- }
- else {
- if (keyword_node_p(argn->nd_body)) {
- kwnode = argn->nd_body;
- }
- else {
- NO_CHECK(COMPILE(args, "args (cat: splat)", argn->nd_body));
- ADD_INSN1(args, argn, newarray, INT2FIX(1));
- ADD_INSN(args, argn, concatarray);
- }
- }
+ if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_SPLAT_MUT;
+ int argc = setup_args_core(iseq, args, RNODE_ARGSPUSH(argn)->nd_head, 1, NULL, NULL);
- if (kwnode) {
- // f(*a, k:1)
- *flag_ptr |= VM_CALL_KW_SPLAT;
- if (!keyword_node_single_splat_p(kwnode)) {
- *flag_ptr |= VM_CALL_KW_SPLAT_MUT;
- }
- compile_hash(iseq, args, kwnode, TRUE, FALSE);
- argc += 1;
- }
+ if (nd_type_p(RNODE_ARGSPUSH(argn)->nd_body, NODE_LIST)) {
+ int rest_len = compile_args(iseq, args, RNODE_ARGSPUSH(argn)->nd_body, &kwnode);
+ if (kwnode) rest_len--;
+ ADD_INSN1(args, argn, newarray, INT2FIX(rest_len));
+ ADD_INSN1(args, argn, pushtoarray, INT2FIX(1));
+ }
+ else {
+ if (keyword_node_p(RNODE_ARGSPUSH(argn)->nd_body)) {
+ kwnode = RNODE_ARGSPUSH(argn)->nd_body;
+ }
+ else {
+ NO_CHECK(COMPILE(args, "args (cat: splat)", RNODE_ARGSPUSH(argn)->nd_body));
+ ADD_INSN1(args, argn, pushtoarray, INT2FIX(1));
+ }
+ }
+
+ if (kwnode) {
+ // f(*a, k:1)
+ *flag_ptr |= VM_CALL_KW_SPLAT;
+ if (!keyword_node_single_splat_p(kwnode)) {
+ *flag_ptr |= VM_CALL_KW_SPLAT_MUT;
+ }
+ compile_hash(iseq, args, kwnode, TRUE, FALSE);
+ argc += 1;
+ }
- return argc;
+ return argc;
}
default: {
- UNKNOWN_NODE("setup_arg", argn, Qnil);
+ UNKNOWN_NODE("setup_arg", argn, Qnil);
}
}
}
@@ -5964,7 +6287,7 @@ setup_args(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
unsigned int dup_rest = 1;
DECL_ANCHOR(arg_block);
INIT_ANCHOR(arg_block);
- NO_CHECK(COMPILE(arg_block, "block", argn->nd_body));
+ NO_CHECK(COMPILE(arg_block, "block", RNODE_BLOCK_PASS(argn)->nd_body));
*flag |= VM_CALL_ARGS_BLOCKARG;
@@ -5978,7 +6301,7 @@ setup_args(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
dup_rest = 0;
}
}
- ret = INT2FIX(setup_args_core(iseq, args, argn->nd_head, dup_rest, flag, keywords));
+ ret = INT2FIX(setup_args_core(iseq, args, RNODE_BLOCK_PASS(argn)->nd_head, dup_rest, flag, keywords));
ADD_SEQ(args, arg_block);
}
else {
@@ -6018,19 +6341,19 @@ compile_named_capture_assign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE
ADD_INSN(ret, line_node, dup);
ADD_INSNL(ret, line_node, branchunless, fail_label);
- for (vars = node; vars; vars = vars->nd_next) {
+ for (vars = node; vars; vars = RNODE_BLOCK(vars)->nd_next) {
INSN *cap;
- if (vars->nd_next) {
+ if (RNODE_BLOCK(vars)->nd_next) {
ADD_INSN(ret, line_node, dup);
}
last = ret->last;
- NO_CHECK(COMPILE_POPPED(ret, "capture", vars->nd_head));
+ NO_CHECK(COMPILE_POPPED(ret, "capture", RNODE_BLOCK(vars)->nd_head));
last = last->next; /* putobject :var */
- cap = new_insn_send(iseq, line_node, idAREF, INT2FIX(1),
+ cap = new_insn_send(iseq, nd_line(line_node), nd_node_id(line_node), idAREF, INT2FIX(1),
NULL, INT2FIX(0), NULL);
ELEM_INSERT_PREV(last->next, (LINK_ELEMENT *)cap);
#if !defined(NAMED_CAPTURE_SINGLE_OPT) || NAMED_CAPTURE_SINGLE_OPT-0
- if (!vars->nd_next && vars == node) {
+ if (!RNODE_BLOCK(vars)->nd_next && vars == node) {
/* only one name */
DECL_ANCHOR(nom);
@@ -6051,9 +6374,9 @@ compile_named_capture_assign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE
ADD_INSNL(ret, line_node, jump, end_label);
ADD_LABEL(ret, fail_label);
ADD_INSN(ret, line_node, pop);
- for (vars = node; vars; vars = vars->nd_next) {
+ for (vars = node; vars; vars = RNODE_BLOCK(vars)->nd_next) {
last = ret->last;
- NO_CHECK(COMPILE_POPPED(ret, "capture", vars->nd_head));
+ NO_CHECK(COMPILE_POPPED(ret, "capture", RNODE_BLOCK(vars)->nd_head));
last = last->next; /* putobject :var */
((INSN*)last)->insn_id = BIN(putnil);
((INSN*)last)->operand_size = 0;
@@ -6066,8 +6389,10 @@ optimizable_range_item_p(const NODE *n)
{
if (!n) return FALSE;
switch (nd_type(n)) {
- case NODE_LIT:
- return RB_INTEGER_TYPE_P(n->nd_lit);
+ case NODE_LINE:
+ return TRUE;
+ case NODE_INTEGER:
+ return TRUE;
case NODE_NIL:
return TRUE;
default:
@@ -6075,11 +6400,32 @@ optimizable_range_item_p(const NODE *n)
}
}
+static VALUE
+optimized_range_item(const NODE *n)
+{
+ switch (nd_type(n)) {
+ case NODE_LINE:
+ return rb_node_line_lineno_val(n);
+ case NODE_INTEGER:
+ return rb_node_integer_literal_val(n);
+ case NODE_FLOAT:
+ return rb_node_float_literal_val(n);
+ case NODE_RATIONAL:
+ return rb_node_rational_literal_val(n);
+ case NODE_IMAGINARY:
+ return rb_node_imaginary_literal_val(n);
+ case NODE_NIL:
+ return Qnil;
+ default:
+ rb_bug("unexpected node: %s", ruby_node_name(nd_type(n)));
+ }
+}
+
static int
compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
{
- const NODE *const node_body = type == NODE_IF ? node->nd_body : node->nd_else;
- const NODE *const node_else = type == NODE_IF ? node->nd_else : node->nd_body;
+ const NODE *const node_body = type == NODE_IF ? RNODE_IF(node)->nd_body : RNODE_UNLESS(node)->nd_else;
+ const NODE *const node_else = type == NODE_IF ? RNODE_IF(node)->nd_else : RNODE_UNLESS(node)->nd_body;
const int line = nd_line(node);
const NODE *line_node = node;
@@ -6092,11 +6438,11 @@ compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int
else_label = NEW_LABEL(line);
end_label = 0;
- compile_branch_condition(iseq, cond_seq, node->nd_cond, then_label, else_label);
+ compile_branch_condition(iseq, cond_seq, RNODE_IF(node)->nd_cond, then_label, else_label);
ADD_SEQ(ret, cond_seq);
if (then_label->refcnt && else_label->refcnt) {
- branches = decl_branch_base(iseq, node, type == NODE_IF ? "if" : "unless");
+ branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), type == NODE_IF ? "if" : "unless");
}
if (then_label->refcnt) {
@@ -6107,10 +6453,12 @@ compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int
CHECK(COMPILE_(then_seq, "then", node_body, popped));
if (else_label->refcnt) {
+ const NODE *const coverage_node = node_body ? node_body : node;
add_trace_branch_coverage(
iseq,
ret,
- node_body ? node_body : node,
+ nd_code_loc(coverage_node),
+ nd_node_id(coverage_node),
0,
type == NODE_IF ? "then" : "else",
branches);
@@ -6131,10 +6479,12 @@ compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int
CHECK(COMPILE_(else_seq, "else", node_else, popped));
if (then_label->refcnt) {
+ const NODE *const coverage_node = node_else ? node_else : node;
add_trace_branch_coverage(
iseq,
ret,
- node_else ? node_else : node,
+ nd_code_loc(coverage_node),
+ nd_node_id(coverage_node),
1,
type == NODE_IF ? "else" : "then",
branches);
@@ -6172,11 +6522,11 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_nod
RHASH_TBL_RAW(literals)->type = &cdhash_type;
- CHECK(COMPILE(head, "case base", node->nd_head));
+ CHECK(COMPILE(head, "case base", RNODE_CASE(node)->nd_head));
- branches = decl_branch_base(iseq, node, "case");
+ branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "case");
- node = node->nd_body;
+ node = RNODE_CASE(node)->nd_body;
EXPECT_NODE("NODE_CASE", node, NODE_WHEN, COMPILE_NG);
type = nd_type(node);
line = nd_line(node);
@@ -6193,17 +6543,21 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_nod
l1 = NEW_LABEL(line);
ADD_LABEL(body_seq, l1);
ADD_INSN(body_seq, line_node, pop);
+
+ const NODE *const coverage_node = RNODE_WHEN(node)->nd_body ? RNODE_WHEN(node)->nd_body : node;
add_trace_branch_coverage(
iseq,
body_seq,
- node->nd_body ? node->nd_body : node,
+ nd_code_loc(coverage_node),
+ nd_node_id(coverage_node),
branch_id++,
"when",
branches);
- CHECK(COMPILE_(body_seq, "when body", node->nd_body, popped));
+
+ CHECK(COMPILE_(body_seq, "when body", RNODE_WHEN(node)->nd_body, popped));
ADD_INSNL(body_seq, line_node, jump, endlabel);
- vals = node->nd_head;
+ vals = RNODE_WHEN(node)->nd_head;
if (vals) {
switch (nd_type(vals)) {
case NODE_LIST:
@@ -6224,7 +6578,7 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_nod
EXPECT_NODE_NONULL("NODE_CASE", node, NODE_LIST, COMPILE_NG);
}
- node = node->nd_next;
+ node = RNODE_WHEN(node)->nd_next;
if (!node) {
break;
}
@@ -6236,7 +6590,7 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_nod
if (node) {
ADD_LABEL(cond_seq, elselabel);
ADD_INSN(cond_seq, line_node, pop);
- add_trace_branch_coverage(iseq, cond_seq, node, branch_id, "else", branches);
+ add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(node), nd_node_id(node), branch_id, "else", branches);
CHECK(COMPILE_(cond_seq, "else", node, popped));
ADD_INSNL(cond_seq, line_node, jump, endlabel);
}
@@ -6244,7 +6598,7 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_nod
debugs("== else (implicit)\n");
ADD_LABEL(cond_seq, elselabel);
ADD_INSN(cond_seq, orig_node, pop);
- add_trace_branch_coverage(iseq, cond_seq, orig_node, branch_id, "else", branches);
+ add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(orig_node), nd_node_id(orig_node), branch_id, "else", branches);
if (!popped) {
ADD_INSN(cond_seq, orig_node, putnil);
}
@@ -6269,13 +6623,13 @@ compile_case2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no
{
const NODE *vals;
const NODE *val;
- const NODE *node = orig_node->nd_body;
+ const NODE *node = RNODE_CASE2(orig_node)->nd_body;
LABEL *endlabel;
DECL_ANCHOR(body_seq);
VALUE branches = Qfalse;
int branch_id = 0;
- branches = decl_branch_base(iseq, orig_node, "case");
+ branches = decl_branch_base(iseq, PTR2NUM(orig_node), nd_code_loc(orig_node), "case");
INIT_ANCHOR(body_seq);
endlabel = NEW_LABEL(nd_line(node));
@@ -6284,17 +6638,21 @@ compile_case2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no
const int line = nd_line(node);
LABEL *l1 = NEW_LABEL(line);
ADD_LABEL(body_seq, l1);
+
+ const NODE *const coverage_node = RNODE_WHEN(node)->nd_body ? RNODE_WHEN(node)->nd_body : node;
add_trace_branch_coverage(
iseq,
body_seq,
- node->nd_body ? node->nd_body : node,
+ nd_code_loc(coverage_node),
+ nd_node_id(coverage_node),
branch_id++,
"when",
branches);
- CHECK(COMPILE_(body_seq, "when", node->nd_body, popped));
+
+ CHECK(COMPILE_(body_seq, "when", RNODE_WHEN(node)->nd_body, popped));
ADD_INSNL(body_seq, node, jump, endlabel);
- vals = node->nd_head;
+ vals = RNODE_WHEN(node)->nd_head;
if (!vals) {
EXPECT_NODE_NONULL("NODE_WHEN", node, NODE_LIST, COMPILE_NG);
}
@@ -6302,12 +6660,12 @@ compile_case2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no
case NODE_LIST:
while (vals) {
LABEL *lnext;
- val = vals->nd_head;
+ val = RNODE_LIST(vals)->nd_head;
lnext = NEW_LABEL(nd_line(val));
debug_compile("== when2\n", (void)0);
CHECK(compile_branch_condition(iseq, ret, val, l1, lnext));
ADD_LABEL(ret, lnext);
- vals = vals->nd_next;
+ vals = RNODE_LIST(vals)->nd_next;
}
break;
case NODE_SPLAT:
@@ -6321,13 +6679,15 @@ compile_case2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no
default:
UNKNOWN_NODE("NODE_WHEN", vals, COMPILE_NG);
}
- node = node->nd_next;
+ node = RNODE_WHEN(node)->nd_next;
}
/* else */
+ const NODE *const coverage_node = node ? node : orig_node;
add_trace_branch_coverage(
iseq,
ret,
- node ? node : orig_node,
+ nd_code_loc(coverage_node),
+ nd_node_id(coverage_node),
branch_id,
"else",
branches);
@@ -6413,14 +6773,13 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
* match_failed:
* goto unmatched
*/
- struct rb_ary_pattern_info *apinfo = node->nd_apinfo;
- const NODE *args = apinfo->pre_args;
- const int pre_args_num = apinfo->pre_args ? rb_long2int(apinfo->pre_args->nd_alen) : 0;
- const int post_args_num = apinfo->post_args ? rb_long2int(apinfo->post_args->nd_alen) : 0;
+ const NODE *args = RNODE_ARYPTN(node)->pre_args;
+ const int pre_args_num = RNODE_ARYPTN(node)->pre_args ? rb_long2int(RNODE_LIST(RNODE_ARYPTN(node)->pre_args)->as.nd_alen) : 0;
+ const int post_args_num = RNODE_ARYPTN(node)->post_args ? rb_long2int(RNODE_LIST(RNODE_ARYPTN(node)->post_args)->as.nd_alen) : 0;
const int min_argc = pre_args_num + post_args_num;
- const int use_rest_num = apinfo->rest_arg && (NODE_NAMED_REST_P(apinfo->rest_arg) ||
- (!NODE_NAMED_REST_P(apinfo->rest_arg) && post_args_num > 0));
+ const int use_rest_num = RNODE_ARYPTN(node)->rest_arg && (NODE_NAMED_REST_P(RNODE_ARYPTN(node)->rest_arg) ||
+ (!NODE_NAMED_REST_P(RNODE_ARYPTN(node)->rest_arg) && post_args_num > 0));
LABEL *match_failed, *type_error, *deconstruct, *deconstructed;
int i;
@@ -6444,10 +6803,10 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
ADD_INSN(ret, line_node, dup);
ADD_SEND(ret, line_node, idLength, INT2FIX(0));
ADD_INSN1(ret, line_node, putobject, INT2FIX(min_argc));
- ADD_SEND(ret, line_node, apinfo->rest_arg ? idGE : idEq, INT2FIX(1)); // (1)
+ ADD_SEND(ret, line_node, RNODE_ARYPTN(node)->rest_arg ? idGE : idEq, INT2FIX(1)); // (1)
if (in_single_pattern) {
CHECK(iseq_compile_pattern_set_length_errmsg(iseq, ret, node,
- apinfo->rest_arg ? rb_fstring_lit("%p length mismatch (given %p, expected %p+)") :
+ RNODE_ARYPTN(node)->rest_arg ? rb_fstring_lit("%p length mismatch (given %p, expected %p+)") :
rb_fstring_lit("%p length mismatch (given %p, expected %p)"),
INT2FIX(min_argc), base_index + 1 /* (1) */));
}
@@ -6457,12 +6816,12 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
ADD_INSN(ret, line_node, dup);
ADD_INSN1(ret, line_node, putobject, INT2FIX(i));
ADD_SEND(ret, line_node, idAREF, INT2FIX(1)); // (2)
- CHECK(iseq_compile_pattern_match(iseq, ret, args->nd_head, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (2) */, false));
- args = args->nd_next;
+ CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_LIST(args)->nd_head, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (2) */, false));
+ args = RNODE_LIST(args)->nd_next;
}
- if (apinfo->rest_arg) {
- if (NODE_NAMED_REST_P(apinfo->rest_arg)) {
+ if (RNODE_ARYPTN(node)->rest_arg) {
+ if (NODE_NAMED_REST_P(RNODE_ARYPTN(node)->rest_arg)) {
ADD_INSN(ret, line_node, dup);
ADD_INSN1(ret, line_node, putobject, INT2FIX(pre_args_num));
ADD_INSN1(ret, line_node, topn, INT2FIX(1));
@@ -6472,7 +6831,7 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
ADD_INSN1(ret, line_node, setn, INT2FIX(4));
ADD_SEND(ret, line_node, idAREF, INT2FIX(2)); // (3)
- CHECK(iseq_compile_pattern_match(iseq, ret, apinfo->rest_arg, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (3) */, false));
+ CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_ARYPTN(node)->rest_arg, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (3) */, false));
}
else {
if (post_args_num > 0) {
@@ -6486,7 +6845,7 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
}
}
- args = apinfo->post_args;
+ args = RNODE_ARYPTN(node)->post_args;
for (i = 0; i < post_args_num; i++) {
ADD_INSN(ret, line_node, dup);
@@ -6495,8 +6854,8 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
ADD_SEND(ret, line_node, idPLUS, INT2FIX(1));
ADD_SEND(ret, line_node, idAREF, INT2FIX(1)); // (4)
- CHECK(iseq_compile_pattern_match(iseq, ret, args->nd_head, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (4) */, false));
- args = args->nd_next;
+ CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_LIST(args)->nd_head, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (4) */, false));
+ args = RNODE_LIST(args)->nd_next;
}
ADD_INSN(ret, line_node, pop);
@@ -6574,9 +6933,8 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
* match_failed:
* goto unmatched
*/
- struct rb_fnd_pattern_info *fpinfo = node->nd_fpinfo;
- const NODE *args = fpinfo->args;
- const int args_num = fpinfo->args ? rb_long2int(fpinfo->args->nd_alen) : 0;
+ const NODE *args = RNODE_FNDPTN(node)->args;
+ const int args_num = RNODE_FNDPTN(node)->args ? rb_long2int(RNODE_LIST(RNODE_FNDPTN(node)->args)->as.nd_alen) : 0;
LABEL *match_failed, *type_error, *deconstruct, *deconstructed;
match_failed = NEW_LABEL(line);
@@ -6629,25 +6987,25 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
}
ADD_SEND(ret, line_node, idAREF, INT2FIX(1)); // (5)
- CHECK(iseq_compile_pattern_match(iseq, ret, args->nd_head, next_loop, in_single_pattern, in_alt_pattern, base_index + 4 /* (2), (3), (4), (5) */, false));
- args = args->nd_next;
+ CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_LIST(args)->nd_head, next_loop, in_single_pattern, in_alt_pattern, base_index + 4 /* (2), (3), (4), (5) */, false));
+ args = RNODE_LIST(args)->nd_next;
}
- if (NODE_NAMED_REST_P(fpinfo->pre_rest_arg)) {
+ if (NODE_NAMED_REST_P(RNODE_FNDPTN(node)->pre_rest_arg)) {
ADD_INSN1(ret, line_node, topn, INT2FIX(3));
ADD_INSN1(ret, line_node, putobject, INT2FIX(0));
ADD_INSN1(ret, line_node, topn, INT2FIX(2));
ADD_SEND(ret, line_node, idAREF, INT2FIX(2)); // (6)
- CHECK(iseq_compile_pattern_match(iseq, ret, fpinfo->pre_rest_arg, find_failed, in_single_pattern, in_alt_pattern, base_index + 4 /* (2), (3), (4), (6) */, false));
+ CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_FNDPTN(node)->pre_rest_arg, find_failed, in_single_pattern, in_alt_pattern, base_index + 4 /* (2), (3), (4), (6) */, false));
}
- if (NODE_NAMED_REST_P(fpinfo->post_rest_arg)) {
+ if (NODE_NAMED_REST_P(RNODE_FNDPTN(node)->post_rest_arg)) {
ADD_INSN1(ret, line_node, topn, INT2FIX(3));
ADD_INSN1(ret, line_node, topn, INT2FIX(1));
ADD_INSN1(ret, line_node, putobject, INT2FIX(args_num));
ADD_SEND(ret, line_node, idPLUS, INT2FIX(1));
ADD_INSN1(ret, line_node, topn, INT2FIX(3));
ADD_SEND(ret, line_node, idAREF, INT2FIX(2)); // (7)
- CHECK(iseq_compile_pattern_match(iseq, ret, fpinfo->post_rest_arg, find_failed, in_single_pattern, in_alt_pattern, base_index + 4 /* (2), (3),(4), (7) */, false));
+ CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_FNDPTN(node)->post_rest_arg, find_failed, in_single_pattern, in_alt_pattern, base_index + 4 /* (2), (3),(4), (7) */, false));
}
ADD_INSNL(ret, line_node, jump, find_succeeded);
@@ -6761,12 +7119,12 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
match_failed = NEW_LABEL(line);
type_error = NEW_LABEL(line);
- if (node->nd_pkwargs && !node->nd_pkwrestarg) {
- const NODE *kw_args = node->nd_pkwargs->nd_head;
- keys = rb_ary_new_capa(kw_args ? kw_args->nd_alen/2 : 0);
+ if (RNODE_HSHPTN(node)->nd_pkwargs && !RNODE_HSHPTN(node)->nd_pkwrestarg) {
+ const NODE *kw_args = RNODE_HASH(RNODE_HSHPTN(node)->nd_pkwargs)->nd_head;
+ keys = rb_ary_new_capa(kw_args ? RNODE_LIST(kw_args)->as.nd_alen/2 : 0);
while (kw_args) {
- rb_ary_push(keys, kw_args->nd_head->nd_lit);
- kw_args = kw_args->nd_next->nd_next;
+ rb_ary_push(keys, get_symbol_value(iseq, RNODE_LIST(kw_args)->nd_head));
+ kw_args = RNODE_LIST(RNODE_LIST(kw_args)->nd_next)->nd_next;
}
}
@@ -6793,28 +7151,23 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
ADD_INSN1(ret, line_node, checktype, INT2FIX(T_HASH));
ADD_INSNL(ret, line_node, branchunless, type_error);
- if (node->nd_pkwrestarg) {
+ if (RNODE_HSHPTN(node)->nd_pkwrestarg) {
ADD_SEND(ret, line_node, rb_intern("dup"), INT2FIX(0));
}
- if (node->nd_pkwargs) {
+ if (RNODE_HSHPTN(node)->nd_pkwargs) {
int i;
int keys_num;
const NODE *args;
- args = node->nd_pkwargs->nd_head;
+ args = RNODE_HASH(RNODE_HSHPTN(node)->nd_pkwargs)->nd_head;
if (args) {
DECL_ANCHOR(match_values);
INIT_ANCHOR(match_values);
- keys_num = rb_long2int(args->nd_alen) / 2;
+ keys_num = rb_long2int(RNODE_LIST(args)->as.nd_alen) / 2;
for (i = 0; i < keys_num; i++) {
- NODE *key_node = args->nd_head;
- NODE *value_node = args->nd_next->nd_head;
- VALUE key;
-
- if (!nd_type_p(key_node, NODE_LIT)) {
- UNKNOWN_NODE("NODE_IN", key_node, COMPILE_NG);
- }
- key = key_node->nd_lit;
+ NODE *key_node = RNODE_LIST(args)->nd_head;
+ NODE *value_node = RNODE_LIST(RNODE_LIST(args)->nd_next)->nd_head;
+ VALUE key = get_symbol_value(iseq, key_node);
ADD_INSN(ret, line_node, dup);
ADD_INSN1(ret, line_node, putobject, key);
@@ -6843,9 +7196,9 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
ADD_INSN(match_values, line_node, dup);
ADD_INSN1(match_values, line_node, putobject, key);
- ADD_SEND(match_values, line_node, node->nd_pkwrestarg ? rb_intern("delete") : idAREF, INT2FIX(1)); // (8)
+ ADD_SEND(match_values, line_node, RNODE_HSHPTN(node)->nd_pkwrestarg ? rb_intern("delete") : idAREF, INT2FIX(1)); // (8)
CHECK(iseq_compile_pattern_match(iseq, match_values, value_node, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (8) */, false));
- args = args->nd_next->nd_next;
+ args = RNODE_LIST(RNODE_LIST(args)->nd_next)->nd_next;
}
ADD_SEQ(ret, match_values);
}
@@ -6859,8 +7212,8 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
ADD_INSNL(ret, line_node, branchunless, match_failed);
}
- if (node->nd_pkwrestarg) {
- if (node->nd_pkwrestarg == NODE_SPECIAL_NO_REST_KEYWORD) {
+ if (RNODE_HSHPTN(node)->nd_pkwrestarg) {
+ if (RNODE_HSHPTN(node)->nd_pkwrestarg == NODE_SPECIAL_NO_REST_KEYWORD) {
ADD_INSN(ret, line_node, dup);
ADD_SEND(ret, line_node, idEmptyP, INT2FIX(0)); // (10)
if (in_single_pattern) {
@@ -6870,7 +7223,7 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
}
else {
ADD_INSN(ret, line_node, dup); // (11)
- CHECK(iseq_compile_pattern_match(iseq, ret, node->nd_pkwrestarg, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (11) */, false));
+ CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_HSHPTN(node)->nd_pkwrestarg, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (11) */, false));
}
}
@@ -6890,7 +7243,15 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
ADD_INSNL(ret, line_node, jump, unmatched);
break;
}
- case NODE_LIT:
+ case NODE_SYM:
+ case NODE_REGX:
+ case NODE_LINE:
+ case NODE_INTEGER:
+ case NODE_FLOAT:
+ case NODE_RATIONAL:
+ case NODE_IMAGINARY:
+ case NODE_FILE:
+ case NODE_ENCODING:
case NODE_STR:
case NODE_XSTR:
case NODE_DSTR:
@@ -6914,6 +7275,8 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
case NODE_COLON2:
case NODE_COLON3:
case NODE_BEGIN:
+ case NODE_BLOCK:
+ case NODE_ONCE:
CHECK(COMPILE(ret, "case in literal", node)); // (1)
if (in_single_pattern) {
ADD_INSN1(ret, line_node, dupn, INT2FIX(2));
@@ -6927,7 +7290,7 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
break;
case NODE_LASGN: {
struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
- ID id = node->nd_vid;
+ ID id = RNODE_LASGN(node)->nd_vid;
int idx = ISEQ_BODY(body->local_iseq)->local_table_size - get_local_var_idx(iseq, id);
if (in_alt_pattern) {
@@ -6945,7 +7308,7 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
}
case NODE_DASGN: {
int idx, lv, ls;
- ID id = node->nd_vid;
+ ID id = RNODE_DASGN(node)->nd_vid;
idx = get_dyna_var_idx(iseq, id, &lv, &ls);
@@ -6971,8 +7334,8 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
case NODE_UNLESS: {
LABEL *match_failed;
match_failed = unmatched;
- CHECK(iseq_compile_pattern_match(iseq, ret, node->nd_body, unmatched, in_single_pattern, in_alt_pattern, base_index, use_deconstructed_cache));
- CHECK(COMPILE(ret, "case in if", node->nd_cond));
+ CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_IF(node)->nd_body, unmatched, in_single_pattern, in_alt_pattern, base_index, use_deconstructed_cache));
+ CHECK(COMPILE(ret, "case in if", RNODE_IF(node)->nd_cond));
if (in_single_pattern) {
LABEL *match_succeeded;
match_succeeded = NEW_LABEL(line);
@@ -7009,15 +7372,15 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
LABEL *match_failed;
match_failed = NEW_LABEL(line);
- n = node->nd_head;
- if (! (nd_type_p(n, NODE_LIST) && n->nd_alen == 2)) {
+ n = RNODE_HASH(node)->nd_head;
+ if (! (nd_type_p(n, NODE_LIST) && RNODE_LIST(n)->as.nd_alen == 2)) {
COMPILE_ERROR(ERROR_ARGS "unexpected node");
return COMPILE_NG;
}
ADD_INSN(ret, line_node, dup); // (1)
- CHECK(iseq_compile_pattern_match(iseq, ret, n->nd_head, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (1) */, use_deconstructed_cache));
- CHECK(iseq_compile_pattern_each(iseq, ret, n->nd_next->nd_head, matched, match_failed, in_single_pattern, in_alt_pattern, base_index, false));
+ CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_LIST(n)->nd_head, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (1) */, use_deconstructed_cache));
+ CHECK(iseq_compile_pattern_each(iseq, ret, RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_head, matched, match_failed, in_single_pattern, in_alt_pattern, base_index, false));
ADD_INSN(ret, line_node, putnil);
ADD_LABEL(ret, match_failed);
@@ -7031,13 +7394,13 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
fin = NEW_LABEL(line);
ADD_INSN(ret, line_node, dup); // (1)
- CHECK(iseq_compile_pattern_each(iseq, ret, node->nd_1st, match_succeeded, fin, in_single_pattern, true, base_index + 1 /* (1) */, use_deconstructed_cache));
+ CHECK(iseq_compile_pattern_each(iseq, ret, RNODE_OR(node)->nd_1st, match_succeeded, fin, in_single_pattern, true, base_index + 1 /* (1) */, use_deconstructed_cache));
ADD_LABEL(ret, match_succeeded);
ADD_INSN(ret, line_node, pop);
ADD_INSNL(ret, line_node, jump, matched);
ADD_INSN(ret, line_node, putnil);
ADD_LABEL(ret, fin);
- CHECK(iseq_compile_pattern_each(iseq, ret, node->nd_2nd, matched, unmatched, in_single_pattern, true, base_index, use_deconstructed_cache));
+ CHECK(iseq_compile_pattern_each(iseq, ret, RNODE_OR(node)->nd_2nd, matched, unmatched, in_single_pattern, true, base_index, use_deconstructed_cache));
break;
}
default:
@@ -7060,9 +7423,9 @@ iseq_compile_pattern_constant(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NOD
{
const NODE *line_node = node;
- if (node->nd_pconst) {
+ if (RNODE_ARYPTN(node)->nd_pconst) {
ADD_INSN(ret, line_node, dup); // (1)
- CHECK(COMPILE(ret, "constant", node->nd_pconst)); // (2)
+ CHECK(COMPILE(ret, "constant", RNODE_ARYPTN(node)->nd_pconst)); // (2)
if (in_single_pattern) {
ADD_INSN1(ret, line_node, dupn, INT2FIX(2));
}
@@ -7263,14 +7626,14 @@ compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no
INIT_ANCHOR(body_seq);
INIT_ANCHOR(cond_seq);
- branches = decl_branch_base(iseq, node, "case");
+ branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "case");
- node = node->nd_body;
+ node = RNODE_CASE3(node)->nd_body;
EXPECT_NODE("NODE_CASE3", node, NODE_IN, COMPILE_NG);
type = nd_type(node);
line = nd_line(node);
line_node = node;
- single_pattern = !node->nd_next;
+ single_pattern = !RNODE_IN(node)->nd_next;
endlabel = NEW_LABEL(line);
elselabel = NEW_LABEL(line);
@@ -7284,7 +7647,7 @@ compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no
}
ADD_INSN(head, line_node, putnil); /* allocate stack for cached #deconstruct value */
- CHECK(COMPILE(head, "case base", orig_node->nd_head));
+ CHECK(COMPILE(head, "case base", RNODE_CASE3(orig_node)->nd_head));
ADD_SEQ(ret, head); /* case VAL */
@@ -7297,17 +7660,21 @@ compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no
l1 = NEW_LABEL(line);
ADD_LABEL(body_seq, l1);
ADD_INSN1(body_seq, line_node, adjuststack, INT2FIX(single_pattern ? 6 : 2));
+
+ const NODE *const coverage_node = RNODE_IN(node)->nd_body ? RNODE_IN(node)->nd_body : node;
add_trace_branch_coverage(
iseq,
body_seq,
- node->nd_body ? node->nd_body : node,
+ nd_code_loc(coverage_node),
+ nd_node_id(coverage_node),
branch_id++,
"in",
branches);
- CHECK(COMPILE_(body_seq, "in body", node->nd_body, popped));
+
+ CHECK(COMPILE_(body_seq, "in body", RNODE_IN(node)->nd_body, popped));
ADD_INSNL(body_seq, line_node, jump, endlabel);
- pattern = node->nd_head;
+ pattern = RNODE_IN(node)->nd_head;
if (pattern) {
int pat_line = nd_line(pattern);
LABEL *next_pat = NEW_LABEL(pat_line);
@@ -7322,7 +7689,7 @@ compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no
return COMPILE_NG;
}
- node = node->nd_next;
+ node = RNODE_IN(node)->nd_next;
if (!node) {
break;
}
@@ -7335,7 +7702,7 @@ compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no
ADD_LABEL(cond_seq, elselabel);
ADD_INSN(cond_seq, line_node, pop);
ADD_INSN(cond_seq, line_node, pop); /* discard cached #deconstruct value */
- add_trace_branch_coverage(iseq, cond_seq, node, branch_id, "else", branches);
+ add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(node), nd_node_id(node), branch_id, "else", branches);
CHECK(COMPILE_(cond_seq, "else", node, popped));
ADD_INSNL(cond_seq, line_node, jump, endlabel);
ADD_INSN(cond_seq, line_node, putnil);
@@ -7346,7 +7713,7 @@ compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no
else {
debugs("== else (implicit)\n");
ADD_LABEL(cond_seq, elselabel);
- add_trace_branch_coverage(iseq, cond_seq, orig_node, branch_id, "else", branches);
+ add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(orig_node), nd_node_id(orig_node), branch_id, "else", branches);
ADD_INSN1(cond_seq, orig_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
if (single_pattern) {
@@ -7364,6 +7731,7 @@ compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no
fin = NEW_LABEL(line);
kw_arg = rb_xmalloc_mul_add(2, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
+ kw_arg->references = 0;
kw_arg->keyword_len = 2;
kw_arg->keywords[0] = ID2SYM(rb_intern("matchee"));
kw_arg->keywords[1] = ID2SYM(rb_intern("key"));
@@ -7447,7 +7815,7 @@ compile_loop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
ISEQ_COMPILE_DATA(iseq)->loopval_popped = 0;
push_ensure_entry(iseq, &enl, NULL, NULL);
- if (node->nd_state == 1) {
+ if (RNODE_WHILE(node)->nd_state == 1) {
ADD_INSNL(ret, line_node, jump, next_label);
}
else {
@@ -7462,31 +7830,35 @@ compile_loop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
if (tmp_label) ADD_LABEL(ret, tmp_label);
ADD_LABEL(ret, redo_label);
- branches = decl_branch_base(iseq, node, type == NODE_WHILE ? "while" : "until");
+ branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), type == NODE_WHILE ? "while" : "until");
+
+ const NODE *const coverage_node = RNODE_WHILE(node)->nd_body ? RNODE_WHILE(node)->nd_body : node;
add_trace_branch_coverage(
iseq,
ret,
- node->nd_body ? node->nd_body : node,
+ nd_code_loc(coverage_node),
+ nd_node_id(coverage_node),
0,
"body",
branches);
- CHECK(COMPILE_POPPED(ret, "while body", node->nd_body));
+
+ CHECK(COMPILE_POPPED(ret, "while body", RNODE_WHILE(node)->nd_body));
ADD_LABEL(ret, next_label); /* next */
if (type == NODE_WHILE) {
- compile_branch_condition(iseq, ret, node->nd_cond,
+ compile_branch_condition(iseq, ret, RNODE_WHILE(node)->nd_cond,
redo_label, end_label);
}
else {
/* until */
- compile_branch_condition(iseq, ret, node->nd_cond,
+ compile_branch_condition(iseq, ret, RNODE_WHILE(node)->nd_cond,
end_label, redo_label);
}
ADD_LABEL(ret, end_label);
ADD_ADJUST_RESTORE(ret, adjust_label);
- if (UNDEF_P(node->nd_state)) {
+ if (UNDEF_P(RNODE_WHILE(node)->nd_state)) {
/* ADD_INSN(ret, line_node, putundef); */
COMPILE_ERROR(ERROR_ARGS "unsupported: putundef");
return COMPILE_NG;
@@ -7528,18 +7900,18 @@ compile_iter(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
ADD_LABEL(ret, retry_label);
if (nd_type_p(node, NODE_FOR)) {
- CHECK(COMPILE(ret, "iter caller (for)", node->nd_iter));
+ CHECK(COMPILE(ret, "iter caller (for)", RNODE_FOR(node)->nd_iter));
ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq =
- NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq),
+ NEW_CHILD_ISEQ(RNODE_FOR(node)->nd_body, make_name_for_block(iseq),
ISEQ_TYPE_BLOCK, line);
ADD_SEND_WITH_BLOCK(ret, line_node, idEach, INT2FIX(0), child_iseq);
}
else {
ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq =
- NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq),
+ NEW_CHILD_ISEQ(RNODE_ITER(node)->nd_body, make_name_for_block(iseq),
ISEQ_TYPE_BLOCK, line);
- CHECK(COMPILE(ret, "iter caller", node->nd_iter));
+ CHECK(COMPILE(ret, "iter caller", RNODE_ITER(node)->nd_iter));
}
{
@@ -7583,7 +7955,7 @@ compile_for_masgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const nod
* (args.length == 1 && Array.try_convert(args[0])) || args
*/
const NODE *line_node = node;
- const NODE *var = node->nd_var;
+ const NODE *var = RNODE_FOR_MASGN(node)->nd_var;
LABEL *not_single = NEW_LABEL(nd_line(var));
LABEL *not_ary = NEW_LABEL(nd_line(var));
CHECK(COMPILE(ret, "for var", var));
@@ -7618,7 +7990,7 @@ compile_break(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
LABEL *splabel = NEW_LABEL(0);
ADD_LABEL(ret, splabel);
ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label);
- CHECK(COMPILE_(ret, "break val (while/until)", node->nd_stts,
+ CHECK(COMPILE_(ret, "break val (while/until)", RNODE_BREAK(node)->nd_stts,
ISEQ_COMPILE_DATA(iseq)->loopval_popped));
add_ensure_iseq(ret, iseq, 0);
ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
@@ -7653,7 +8025,7 @@ compile_break(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
}
/* escape from block */
- CHECK(COMPILE(ret, "break val (block)", node->nd_stts));
+ CHECK(COMPILE(ret, "break val (block)", RNODE_BREAK(node)->nd_stts));
ADD_INSN1(ret, line_node, throw, INT2FIX(throw_flag | TAG_BREAK));
if (popped) {
ADD_INSN(ret, line_node, pop);
@@ -7676,7 +8048,7 @@ compile_next(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
LABEL *splabel = NEW_LABEL(0);
debugs("next in while loop\n");
ADD_LABEL(ret, splabel);
- CHECK(COMPILE(ret, "next val/valid syntax?", node->nd_stts));
+ CHECK(COMPILE(ret, "next val/valid syntax?", RNODE_NEXT(node)->nd_stts));
add_ensure_iseq(ret, iseq, 0);
ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label);
ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
@@ -7690,7 +8062,7 @@ compile_next(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
debugs("next in block\n");
ADD_LABEL(ret, splabel);
ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->start_label);
- CHECK(COMPILE(ret, "next val", node->nd_stts));
+ CHECK(COMPILE(ret, "next val", RNODE_NEXT(node)->nd_stts));
add_ensure_iseq(ret, iseq, 0);
ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
ADD_ADJUST_RESTORE(ret, splabel);
@@ -7725,7 +8097,7 @@ compile_next(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
ip = ISEQ_BODY(ip)->parent_iseq;
}
if (ip != 0) {
- CHECK(COMPILE(ret, "next val", node->nd_stts));
+ CHECK(COMPILE(ret, "next val", RNODE_NEXT(node)->nd_stts));
ADD_INSN1(ret, line_node, throw, INT2FIX(throw_flag | TAG_NEXT));
if (popped) {
@@ -7837,7 +8209,7 @@ compile_rescue(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
LABEL *lstart = NEW_LABEL(line);
LABEL *lend = NEW_LABEL(line);
LABEL *lcont = NEW_LABEL(line);
- const rb_iseq_t *rescue = NEW_CHILD_ISEQ(node->nd_resq,
+ const rb_iseq_t *rescue = NEW_CHILD_ISEQ(RNODE_RESCUE(node)->nd_resq,
rb_str_concat(rb_str_new2("rescue in "),
ISEQ_BODY(iseq)->location.label),
ISEQ_TYPE_RESCUE, line);
@@ -7849,14 +8221,14 @@ compile_rescue(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
bool prev_in_rescue = ISEQ_COMPILE_DATA(iseq)->in_rescue;
ISEQ_COMPILE_DATA(iseq)->in_rescue = true;
{
- CHECK(COMPILE(ret, "rescue head", node->nd_head));
+ CHECK(COMPILE(ret, "rescue head", RNODE_RESCUE(node)->nd_head));
}
ISEQ_COMPILE_DATA(iseq)->in_rescue = prev_in_rescue;
ADD_LABEL(ret, lend);
- if (node->nd_else) {
+ if (RNODE_RESCUE(node)->nd_else) {
ADD_INSN(ret, line_node, pop);
- CHECK(COMPILE(ret, "rescue else", node->nd_else));
+ CHECK(COMPILE(ret, "rescue else", RNODE_RESCUE(node)->nd_else));
}
ADD_INSN(ret, line_node, nop);
ADD_LABEL(ret, lcont);
@@ -7884,16 +8256,16 @@ compile_resbody(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
label_miss = NEW_LABEL(line);
label_hit = NEW_LABEL(line);
- narg = resq->nd_args;
+ narg = RNODE_RESBODY(resq)->nd_args;
if (narg) {
switch (nd_type(narg)) {
case NODE_LIST:
while (narg) {
ADD_GETLOCAL(ret, line_node, LVAR_ERRINFO, 0);
- CHECK(COMPILE(ret, "rescue arg", narg->nd_head));
+ CHECK(COMPILE(ret, "rescue arg", RNODE_LIST(narg)->nd_head));
ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
ADD_INSNL(ret, line_node, branchif, label_hit);
- narg = narg->nd_next;
+ narg = RNODE_LIST(narg)->nd_next;
}
break;
case NODE_SPLAT:
@@ -7916,13 +8288,22 @@ compile_resbody(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
}
ADD_INSNL(ret, line_node, jump, label_miss);
ADD_LABEL(ret, label_hit);
- CHECK(COMPILE(ret, "resbody body", resq->nd_body));
+ ADD_TRACE(ret, RUBY_EVENT_RESCUE);
+
+ if (nd_type(RNODE_RESBODY(resq)->nd_body) == NODE_BEGIN && RNODE_BEGIN(RNODE_RESBODY(resq)->nd_body)->nd_body == NULL) {
+ // empty body
+ ADD_SYNTHETIC_INSN(ret, nd_line(RNODE_RESBODY(resq)->nd_body), -1, putnil);
+ }
+ else {
+ CHECK(COMPILE(ret, "resbody body", RNODE_RESBODY(resq)->nd_body));
+ }
+
if (ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization) {
ADD_INSN(ret, line_node, nop);
}
ADD_INSN(ret, line_node, leave);
ADD_LABEL(ret, label_miss);
- resq = resq->nd_head;
+ resq = RNODE_RESBODY(resq)->nd_next;
}
return COMPILE_OK;
}
@@ -7933,7 +8314,7 @@ compile_ensure(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
const int line = nd_line(node);
const NODE *line_node = node;
DECL_ANCHOR(ensr);
- const rb_iseq_t *ensure = NEW_CHILD_ISEQ(node->nd_ensr,
+ const rb_iseq_t *ensure = NEW_CHILD_ISEQ(RNODE_ENSURE(node)->nd_ensr,
rb_str_concat(rb_str_new2 ("ensure in "), ISEQ_BODY(iseq)->location.label),
ISEQ_TYPE_ENSURE, line);
LABEL *lstart = NEW_LABEL(line);
@@ -7946,17 +8327,17 @@ compile_ensure(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
struct ensure_range *erange;
INIT_ANCHOR(ensr);
- CHECK(COMPILE_POPPED(ensr, "ensure ensr", node->nd_ensr));
+ CHECK(COMPILE_POPPED(ensr, "ensure ensr", RNODE_ENSURE(node)->nd_ensr));
last = ensr->last;
last_leave = last && IS_INSN(last) && IS_INSN_ID(last, leave);
er.begin = lstart;
er.end = lend;
er.next = 0;
- push_ensure_entry(iseq, &enl, &er, node->nd_ensr);
+ push_ensure_entry(iseq, &enl, &er, RNODE_ENSURE(node)->nd_ensr);
ADD_LABEL(ret, lstart);
- CHECK(COMPILE_(ret, "ensure head", node->nd_head, (popped | last_leave)));
+ CHECK(COMPILE_(ret, "ensure head", RNODE_ENSURE(node)->nd_head, (popped | last_leave)));
ADD_LABEL(ret, lend);
ADD_SEQ(ret, ensr);
if (!popped && last_leave) ADD_INSN(ret, line_node, putnil);
@@ -7985,7 +8366,7 @@ compile_return(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
enum rb_iseq_type type = ISEQ_BODY(iseq)->type;
const rb_iseq_t *is = iseq;
enum rb_iseq_type t = type;
- const NODE *retval = node->nd_stts;
+ const NODE *retval = RNODE_RETURN(node)->nd_stts;
LABEL *splabel = 0;
while (t == ISEQ_TYPE_RESCUE || t == ISEQ_TYPE_ENSURE) {
@@ -8068,11 +8449,11 @@ qcall_branch_start(rb_iseq_t *iseq, LINK_ANCHOR *const recv, VALUE *branches, co
LABEL *else_label = NEW_LABEL(nd_line(line_node));
VALUE br = 0;
- br = decl_branch_base(iseq, node, "&.");
+ br = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "&.");
*branches = br;
ADD_INSN(recv, line_node, dup);
ADD_INSNL(recv, line_node, branchnil, else_label);
- add_trace_branch_coverage(iseq, recv, node, 0, "then", br);
+ add_trace_branch_coverage(iseq, recv, nd_code_loc(node), nd_node_id(node), 0, "then", br);
return else_label;
}
@@ -8084,7 +8465,7 @@ qcall_branch_end(rb_iseq_t *iseq, LINK_ANCHOR *const ret, LABEL *else_label, VAL
end_label = NEW_LABEL(nd_line(line_node));
ADD_INSNL(ret, line_node, jump, end_label);
ADD_LABEL(ret, else_label);
- add_trace_branch_coverage(iseq, ret, node, 1, "else", branches);
+ add_trace_branch_coverage(iseq, ret, nd_code_loc(node), nd_node_id(node), 1, "else", branches);
ADD_LABEL(ret, end_label);
}
@@ -8094,13 +8475,14 @@ compile_call_precheck_freeze(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE
/* optimization shortcut
* "literal".freeze -> opt_str_freeze("literal")
*/
- if (node->nd_recv && nd_type_p(node->nd_recv, NODE_STR) &&
- (node->nd_mid == idFreeze || node->nd_mid == idUMinus) &&
- node->nd_args == NULL &&
+ if (get_nd_recv(node) &&
+ (nd_type_p(get_nd_recv(node), NODE_STR) || nd_type_p(get_nd_recv(node), NODE_FILE)) &&
+ (get_node_call_nd_mid(node) == idFreeze || get_node_call_nd_mid(node) == idUMinus) &&
+ get_nd_args(node) == NULL &&
ISEQ_COMPILE_DATA(iseq)->current_block == NULL &&
ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
- VALUE str = rb_fstring(node->nd_recv->nd_lit);
- if (node->nd_mid == idUMinus) {
+ VALUE str = get_string_value(get_nd_recv(node));
+ if (get_node_call_nd_mid(node) == idUMinus) {
ADD_INSN2(ret, line_node, opt_str_uminus, str,
new_callinfo(iseq, idUMinus, 0, 0, NULL, FALSE));
}
@@ -8117,14 +8499,14 @@ compile_call_precheck_freeze(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE
/* optimization shortcut
* obj["literal"] -> opt_aref_with(obj, "literal")
*/
- if (node->nd_mid == idAREF && !private_recv_p(node) && node->nd_args &&
- nd_type_p(node->nd_args, NODE_LIST) && node->nd_args->nd_alen == 1 &&
- nd_type_p(node->nd_args->nd_head, NODE_STR) &&
+ if (get_node_call_nd_mid(node) == idAREF && !private_recv_p(node) && get_nd_args(node) &&
+ nd_type_p(get_nd_args(node), NODE_LIST) && RNODE_LIST(get_nd_args(node))->as.nd_alen == 1 &&
+ (nd_type_p(RNODE_LIST(get_nd_args(node))->nd_head, NODE_STR) || nd_type_p(RNODE_LIST(get_nd_args(node))->nd_head, NODE_FILE)) &&
ISEQ_COMPILE_DATA(iseq)->current_block == NULL &&
- !ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal &&
+ !frozen_string_literal_p(iseq) &&
ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
- VALUE str = rb_fstring(node->nd_args->nd_head->nd_lit);
- CHECK(COMPILE(ret, "recv", node->nd_recv));
+ VALUE str = get_string_value(RNODE_LIST(get_nd_args(node))->nd_head);
+ CHECK(COMPILE(ret, "recv", get_nd_recv(node)));
ADD_INSN2(ret, line_node, opt_aref_with, str,
new_callinfo(iseq, idAREF, 1, 0, NULL, FALSE));
RB_OBJ_WRITTEN(iseq, Qundef, str);
@@ -8167,12 +8549,12 @@ iseq_builtin_function_name(const enum node_type type, const NODE *recv, ID mid)
if (recv) {
switch (nd_type(recv)) {
case NODE_VCALL:
- if (recv->nd_mid == rb_intern("__builtin")) {
+ if (RNODE_VCALL(recv)->nd_mid == rb_intern("__builtin")) {
return name;
}
break;
case NODE_CONST:
- if (recv->nd_vid == rb_intern("Primitive")) {
+ if (RNODE_CONST(recv)->nd_vid == rb_intern("Primitive")) {
return name;
}
break;
@@ -8262,21 +8644,29 @@ compile_builtin_attr(rb_iseq_t *iseq, const NODE *node)
if (!node) goto no_arg;
while (node) {
if (!nd_type_p(node, NODE_LIST)) goto bad_arg;
- const NODE *next = node->nd_next;
+ const NODE *next = RNODE_LIST(node)->nd_next;
- node = node->nd_head;
+ node = RNODE_LIST(node)->nd_head;
if (!node) goto no_arg;
- if (!nd_type_p(node, NODE_LIT)) goto bad_arg;
+ switch (nd_type(node)) {
+ case NODE_SYM:
+ symbol = rb_node_sym_string_val(node);
+ break;
+ default:
+ goto bad_arg;
+ }
- symbol = node->nd_lit;
if (!SYMBOL_P(symbol)) goto non_symbol_arg;
string = rb_sym_to_s(symbol);
if (strcmp(RSTRING_PTR(string), "leaf") == 0) {
ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_LEAF;
}
- else if (strcmp(RSTRING_PTR(string), "no_gc") == 0) {
- ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_NO_GC;
+ else if (strcmp(RSTRING_PTR(string), "inline_block") == 0) {
+ ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_INLINE_BLOCK;
+ }
+ else if (strcmp(RSTRING_PTR(string), "use_block") == 0) {
+ iseq_set_use_block(iseq);
}
else {
goto unknown_arg;
@@ -8300,13 +8690,20 @@ compile_builtin_attr(rb_iseq_t *iseq, const NODE *node)
static int
compile_builtin_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, const NODE *line_node, int popped)
{
+ VALUE name;
+
if (!node) goto no_arg;
if (!nd_type_p(node, NODE_LIST)) goto bad_arg;
- if (node->nd_next) goto too_many_arg;
- node = node->nd_head;
+ if (RNODE_LIST(node)->nd_next) goto too_many_arg;
+ node = RNODE_LIST(node)->nd_head;
if (!node) goto no_arg;
- if (!nd_type_p(node, NODE_LIT)) goto bad_arg;
- VALUE name = node->nd_lit;
+ switch (nd_type(node)) {
+ case NODE_SYM:
+ name = rb_node_sym_string_val(node);
+ break;
+ default:
+ goto bad_arg;
+ }
if (!SYMBOL_P(name)) goto non_symbol_arg;
if (!popped) {
compile_lvar(iseq, ret, line_node, SYM2ID(name));
@@ -8330,8 +8727,8 @@ static NODE *
mandatory_node(const rb_iseq_t *iseq, const NODE *cond_node)
{
const NODE *node = ISEQ_COMPILE_DATA(iseq)->root_node;
- if (nd_type(node) == NODE_IF && node->nd_cond == cond_node) {
- return node->nd_body;
+ if (nd_type(node) == NODE_IF && RNODE_IF(node)->nd_cond == cond_node) {
+ return RNODE_IF(node)->nd_body;
}
else {
rb_bug("mandatory_node: can't find mandatory node");
@@ -8345,8 +8742,9 @@ compile_builtin_mandatory_only_method(rb_iseq_t *iseq, const NODE *node, const N
struct rb_args_info args = {
.pre_args_num = ISEQ_BODY(iseq)->param.lead_num,
};
- NODE args_node;
- rb_node_init(&args_node, NODE_ARGS, 0, 0, (VALUE)&args);
+ rb_node_args_t args_node;
+ rb_node_init(RNODE(&args_node), NODE_ARGS);
+ args_node.nd_ainfo = args;
// local table without non-mandatory parameters
const int skip_local_size = ISEQ_BODY(iseq)->param.size - ISEQ_BODY(iseq)->param.lead_num;
@@ -8367,20 +8765,20 @@ compile_builtin_mandatory_only_method(rb_iseq_t *iseq, const NODE *node, const N
tbl->ids[i] = ISEQ_BODY(iseq)->local_table[i + skip_local_size];
}
- NODE scope_node;
- rb_node_init(&scope_node, NODE_SCOPE, (VALUE)tbl, (VALUE)mandatory_node(iseq, node), (VALUE)&args_node);
+ rb_node_scope_t scope_node;
+ rb_node_init(RNODE(&scope_node), NODE_SCOPE);
+ scope_node.nd_tbl = tbl;
+ scope_node.nd_body = mandatory_node(iseq, node);
+ scope_node.nd_args = &args_node;
- rb_ast_body_t ast = {
- .root = &scope_node,
- .compile_option = 0,
- .script_lines = ISEQ_BODY(iseq)->variable.script_lines,
- };
+ VALUE vast = rb_ruby_ast_new(RNODE(&scope_node));
ISEQ_BODY(iseq)->mandatory_only_iseq =
- rb_iseq_new_with_opt(&ast, rb_iseq_base_label(iseq),
+ rb_iseq_new_with_opt(vast, rb_iseq_base_label(iseq),
rb_iseq_path(iseq), rb_iseq_realpath(iseq),
nd_line(line_node), NULL, 0,
- ISEQ_TYPE_METHOD, ISEQ_COMPILE_DATA(iseq)->option);
+ ISEQ_TYPE_METHOD, ISEQ_COMPILE_DATA(iseq)->option,
+ ISEQ_BODY(iseq)->variable.script_lines);
ALLOCV_END(idtmp);
return COMPILE_OK;
@@ -8390,7 +8788,7 @@ static int
compile_builtin_function_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, const NODE *line_node, int popped,
const rb_iseq_t *parent_block, LINK_ANCHOR *args, const char *builtin_func)
{
- NODE *args_node = node->nd_args;
+ NODE *args_node = get_nd_args(node);
if (parent_block != NULL) {
COMPILE_ERROR(ERROR_ARGS_AT(line_node) "should not call builtins here.");
@@ -8413,7 +8811,6 @@ compile_builtin_function_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NOD
}
else if (strcmp("cinit!", builtin_func) == 0) {
// ignore
- GET_VM()->builtin_inline_index++;
return COMPILE_OK;
}
else if (strcmp("attr!", builtin_func) == 0) {
@@ -8441,10 +8838,7 @@ compile_builtin_function_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NOD
return COMPILE_NG;
}
- if (GET_VM()->builtin_inline_index == INT_MAX) {
- rb_bug("builtin inline function index overflow:%s", builtin_func);
- }
- int inline_index = GET_VM()->builtin_inline_index++;
+ int inline_index = nd_line(node);
snprintf(inline_func, sizeof(inline_func), BUILTIN_INLINE_PREFIX "%d", inline_index);
builtin_func = inline_func;
args_node = NULL;
@@ -8493,7 +8887,7 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, co
*/
DECL_ANCHOR(recv);
DECL_ANCHOR(args);
- ID mid = node->nd_mid;
+ ID mid = get_node_call_nd_mid(node);
VALUE argc;
unsigned int flag = 0;
struct rb_callinfo_kwarg *keywords = NULL;
@@ -8541,20 +8935,7 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, co
labels_table = st_init_numtable();
ISEQ_COMPILE_DATA(iseq)->labels_table = labels_table;
}
- if (nd_type_p(node->nd_args->nd_head, NODE_LIT) &&
- SYMBOL_P(node->nd_args->nd_head->nd_lit)) {
-
- label_name = node->nd_args->nd_head->nd_lit;
- if (!st_lookup(labels_table, (st_data_t)label_name, &data)) {
- label = NEW_LABEL(nd_line(line_node));
- label->position = nd_line(line_node);
- st_insert(labels_table, (st_data_t)label_name, (st_data_t)label);
- }
- else {
- label = (LABEL *)data;
- }
- }
- else {
+ {
COMPILE_ERROR(ERROR_ARGS "invalid goto/label format");
return COMPILE_NG;
}
@@ -8572,7 +8953,7 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, co
const char *builtin_func;
if (UNLIKELY(iseq_has_builtin_function_table(iseq)) &&
- (builtin_func = iseq_builtin_function_name(type, node->nd_recv, mid)) != NULL) {
+ (builtin_func = iseq_builtin_function_name(type, get_nd_recv(node), mid)) != NULL) {
return compile_builtin_function_call(iseq, ret, node, line_node, popped, parent_block, args, builtin_func);
}
@@ -8582,16 +8963,16 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, co
int idx, level;
if (mid == idCall &&
- nd_type_p(node->nd_recv, NODE_LVAR) &&
- iseq_block_param_id_p(iseq, node->nd_recv->nd_vid, &idx, &level)) {
- ADD_INSN2(recv, node->nd_recv, getblockparamproxy, INT2FIX(idx + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
+ nd_type_p(get_nd_recv(node), NODE_LVAR) &&
+ iseq_block_param_id_p(iseq, RNODE_LVAR(get_nd_recv(node))->nd_vid, &idx, &level)) {
+ ADD_INSN2(recv, get_nd_recv(node), getblockparamproxy, INT2FIX(idx + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
}
else if (private_recv_p(node)) {
ADD_INSN(recv, node, putself);
flag |= VM_CALL_FCALL;
}
else {
- CHECK(COMPILE(recv, "recv", node->nd_recv));
+ CHECK(COMPILE(recv, "recv", get_nd_recv(node)));
}
if (type == NODE_QCALL) {
@@ -8605,7 +8986,7 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, co
/* args */
if (type != NODE_VCALL) {
- argc = setup_args(iseq, args, node->nd_args, &flag, &keywords);
+ argc = setup_args(iseq, args, get_nd_args(node), &flag, &keywords);
CHECK(!NIL_P(argc));
}
else {
@@ -8626,6 +9007,9 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, co
flag |= VM_CALL_FCALL;
}
+ if ((flag & VM_CALL_ARGS_BLOCKARG) && (flag & VM_CALL_KW_SPLAT) && !(flag & VM_CALL_KW_SPLAT_MUT)) {
+ ADD_INSN(ret, line_node, splatkw);
+ }
ADD_SEND_R(ret, line_node, mid, argc, parent_block, INT2FIX(flag), keywords);
qcall_branch_end(iseq, ret, else_label, branches, node, line_node);
@@ -8642,8 +9026,7 @@ compile_op_asgn1(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
VALUE argc;
unsigned int flag = 0;
int asgnflag = 0;
- ID id = node->nd_mid;
- int boff = 0;
+ ID id = RNODE_OP_ASGN1(node)->nd_mid;
/*
* a[x] (op)= y
@@ -8671,22 +9054,20 @@ compile_op_asgn1(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
if (!popped) {
ADD_INSN(ret, node, putnil);
}
- asgnflag = COMPILE_RECV(ret, "NODE_OP_ASGN1 recv", node);
+ asgnflag = COMPILE_RECV(ret, "NODE_OP_ASGN1 recv", node, RNODE_OP_ASGN1(node)->nd_recv);
CHECK(asgnflag != -1);
- switch (nd_type(node->nd_args->nd_head)) {
+ switch (nd_type(RNODE_OP_ASGN1(node)->nd_index)) {
case NODE_ZLIST:
argc = INT2FIX(0);
break;
- case NODE_BLOCK_PASS:
- boff = 1;
- /* fall through */
default:
- argc = setup_args(iseq, ret, node->nd_args->nd_head, &flag, NULL);
+ argc = setup_args(iseq, ret, RNODE_OP_ASGN1(node)->nd_index, &flag, NULL);
CHECK(!NIL_P(argc));
}
- ADD_INSN1(ret, node, dupn, FIXNUM_INC(argc, 1 + boff));
+ int dup_argn = FIX2INT(argc) + 1;
+ ADD_INSN1(ret, node, dupn, INT2FIX(dup_argn));
flag |= asgnflag;
- ADD_SEND_WITH_FLAG(ret, node, idAREF, argc, INT2FIX(flag));
+ ADD_SEND_R(ret, node, idAREF, argc, NULL, INT2FIX(flag & ~VM_CALL_ARGS_SPLAT_MUT), NULL);
if (id == idOROP || id == idANDOP) {
/* a[x] ||= y or a[x] &&= y
@@ -8709,64 +9090,63 @@ compile_op_asgn1(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
}
ADD_INSN(ret, node, pop);
- CHECK(COMPILE(ret, "NODE_OP_ASGN1 args->body: ", node->nd_args->nd_body));
+ CHECK(COMPILE(ret, "NODE_OP_ASGN1 nd_rvalue: ", RNODE_OP_ASGN1(node)->nd_rvalue));
if (!popped) {
- ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 2+boff));
+ ADD_INSN1(ret, node, setn, INT2FIX(dup_argn+1));
}
if (flag & VM_CALL_ARGS_SPLAT) {
- ADD_INSN1(ret, node, newarray, INT2FIX(1));
- if (boff > 0) {
- ADD_INSN1(ret, node, dupn, INT2FIX(3));
+ if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
ADD_INSN(ret, node, swap);
- ADD_INSN(ret, node, pop);
- }
- ADD_INSN(ret, node, concatarray);
- if (boff > 0) {
- ADD_INSN1(ret, node, setn, INT2FIX(3));
- ADD_INSN(ret, node, pop);
- ADD_INSN(ret, node, pop);
+ ADD_INSN1(ret, node, splatarray, Qtrue);
+ ADD_INSN(ret, node, swap);
+ flag |= VM_CALL_ARGS_SPLAT_MUT;
}
- ADD_SEND_WITH_FLAG(ret, node, idASET, argc, INT2FIX(flag));
+ ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
+ ADD_SEND_R(ret, node, idASET, argc, NULL, INT2FIX(flag), NULL);
}
else {
- if (boff > 0)
- ADD_INSN(ret, node, swap);
- ADD_SEND_WITH_FLAG(ret, node, idASET, FIXNUM_INC(argc, 1), INT2FIX(flag));
+ ADD_SEND_R(ret, node, idASET, FIXNUM_INC(argc, 1), NULL, INT2FIX(flag), NULL);
}
ADD_INSN(ret, node, pop);
ADD_INSNL(ret, node, jump, lfin);
ADD_LABEL(ret, label);
if (!popped) {
- ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 2+boff));
+ ADD_INSN1(ret, node, setn, INT2FIX(dup_argn+1));
}
- ADD_INSN1(ret, node, adjuststack, FIXNUM_INC(argc, 2+boff));
+ ADD_INSN1(ret, node, adjuststack, INT2FIX(dup_argn+1));
ADD_LABEL(ret, lfin);
}
else {
- CHECK(COMPILE(ret, "NODE_OP_ASGN1 args->body: ", node->nd_args->nd_body));
+ CHECK(COMPILE(ret, "NODE_OP_ASGN1 nd_rvalue: ", RNODE_OP_ASGN1(node)->nd_rvalue));
ADD_SEND(ret, node, id, INT2FIX(1));
if (!popped) {
- ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 2+boff));
+ ADD_INSN1(ret, node, setn, INT2FIX(dup_argn+1));
}
if (flag & VM_CALL_ARGS_SPLAT) {
- ADD_INSN1(ret, node, newarray, INT2FIX(1));
- if (boff > 0) {
- ADD_INSN1(ret, node, dupn, INT2FIX(3));
+ if (flag & VM_CALL_KW_SPLAT) {
+ ADD_INSN1(ret, node, topn, INT2FIX(2));
+ if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
+ ADD_INSN1(ret, node, splatarray, Qtrue);
+ flag |= VM_CALL_ARGS_SPLAT_MUT;
+ }
ADD_INSN(ret, node, swap);
+ ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
+ ADD_INSN1(ret, node, setn, INT2FIX(2));
ADD_INSN(ret, node, pop);
}
- ADD_INSN(ret, node, concatarray);
- if (boff > 0) {
- ADD_INSN1(ret, node, setn, INT2FIX(3));
- ADD_INSN(ret, node, pop);
- ADD_INSN(ret, node, pop);
+ else {
+ if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
+ ADD_INSN(ret, node, swap);
+ ADD_INSN1(ret, node, splatarray, Qtrue);
+ ADD_INSN(ret, node, swap);
+ flag |= VM_CALL_ARGS_SPLAT_MUT;
+ }
+ ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
}
- ADD_SEND_WITH_FLAG(ret, node, idASET, argc, INT2FIX(flag));
+ ADD_SEND_R(ret, node, idASET, argc, NULL, INT2FIX(flag), NULL);
}
else {
- if (boff > 0)
- ADD_INSN(ret, node, swap);
- ADD_SEND_WITH_FLAG(ret, node, idASET, FIXNUM_INC(argc, 1), INT2FIX(flag));
+ ADD_SEND_R(ret, node, idASET, FIXNUM_INC(argc, 1), NULL, INT2FIX(flag), NULL);
}
ADD_INSN(ret, node, pop);
}
@@ -8777,8 +9157,8 @@ static int
compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
{
const int line = nd_line(node);
- ID atype = node->nd_next->nd_mid;
- ID vid = node->nd_next->nd_vid, aid = rb_id_attrset(vid);
+ ID atype = RNODE_OP_ASGN2(node)->nd_mid;
+ ID vid = RNODE_OP_ASGN2(node)->nd_vid, aid = rb_id_attrset(vid);
int asgnflag;
LABEL *lfin = NEW_LABEL(line);
LABEL *lcfin = NEW_LABEL(line);
@@ -8836,9 +9216,9 @@ compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
*/
- asgnflag = COMPILE_RECV(ret, "NODE_OP_ASGN2#recv", node);
+ asgnflag = COMPILE_RECV(ret, "NODE_OP_ASGN2#recv", node, RNODE_OP_ASGN2(node)->nd_recv);
CHECK(asgnflag != -1);
- if (node->nd_next->nd_aid) {
+ if (RNODE_OP_ASGN2(node)->nd_aid) {
lskip = NEW_LABEL(line);
ADD_INSN(ret, node, dup);
ADD_INSNL(ret, node, branchnil, lskip);
@@ -8859,7 +9239,7 @@ compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
if (!popped) {
ADD_INSN(ret, node, pop);
}
- CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value));
+ CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", RNODE_OP_ASGN2(node)->nd_value));
if (!popped) {
ADD_INSN(ret, node, swap);
ADD_INSN1(ret, node, topn, INT2FIX(1));
@@ -8875,7 +9255,7 @@ compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
ADD_LABEL(ret, lfin);
}
else {
- CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value));
+ CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", RNODE_OP_ASGN2(node)->nd_value));
ADD_SEND(ret, node, atype, INT2FIX(1));
if (!popped) {
ADD_INSN(ret, node, swap);
@@ -8893,6 +9273,8 @@ compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
return COMPILE_OK;
}
+static int compile_shareable_constant_value(rb_iseq_t *iseq, LINK_ANCHOR *ret, enum rb_parser_shareability shareable, const NODE *lhs, const NODE *value);
+
static int
compile_op_cdecl(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
{
@@ -8901,21 +9283,21 @@ compile_op_cdecl(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
LABEL *lassign = 0;
ID mid;
- switch (nd_type(node->nd_head)) {
+ switch (nd_type(RNODE_OP_CDECL(node)->nd_head)) {
case NODE_COLON3:
ADD_INSN1(ret, node, putobject, rb_cObject);
break;
case NODE_COLON2:
- CHECK(COMPILE(ret, "NODE_OP_CDECL/colon2#nd_head", node->nd_head->nd_head));
+ CHECK(COMPILE(ret, "NODE_OP_CDECL/colon2#nd_head", RNODE_COLON2(RNODE_OP_CDECL(node)->nd_head)->nd_head));
break;
default:
COMPILE_ERROR(ERROR_ARGS "%s: invalid node in NODE_OP_CDECL",
- ruby_node_name(nd_type(node->nd_head)));
+ ruby_node_name(nd_type(RNODE_OP_CDECL(node)->nd_head)));
return COMPILE_NG;
}
- mid = node->nd_head->nd_mid;
+ mid = get_node_colon_nd_mid(RNODE_OP_CDECL(node)->nd_head);
/* cref */
- if (node->nd_aid == idOROP) {
+ if (RNODE_OP_CDECL(node)->nd_aid == idOROP) {
lassign = NEW_LABEL(line);
ADD_INSN(ret, node, dup); /* cref cref */
ADD_INSN3(ret, node, defined, INT2FIX(DEFINED_CONST_FROM),
@@ -8926,17 +9308,17 @@ compile_op_cdecl(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
ADD_INSN1(ret, node, putobject, Qtrue);
ADD_INSN1(ret, node, getconstant, ID2SYM(mid)); /* cref obj */
- if (node->nd_aid == idOROP || node->nd_aid == idANDOP) {
+ if (RNODE_OP_CDECL(node)->nd_aid == idOROP || RNODE_OP_CDECL(node)->nd_aid == idANDOP) {
lfin = NEW_LABEL(line);
if (!popped) ADD_INSN(ret, node, dup); /* cref [obj] obj */
- if (node->nd_aid == idOROP)
+ if (RNODE_OP_CDECL(node)->nd_aid == idOROP)
ADD_INSNL(ret, node, branchif, lfin);
else /* idANDOP */
ADD_INSNL(ret, node, branchunless, lfin);
/* cref [obj] */
if (!popped) ADD_INSN(ret, node, pop); /* cref */
if (lassign) ADD_LABEL(ret, lassign);
- CHECK(COMPILE(ret, "NODE_OP_CDECL#nd_value", node->nd_value));
+ CHECK(compile_shareable_constant_value(iseq, ret, RNODE_OP_CDECL(node)->shareability, RNODE_OP_CDECL(node)->nd_head, RNODE_OP_CDECL(node)->nd_value));
/* cref value */
if (popped)
ADD_INSN1(ret, node, topn, INT2FIX(1)); /* cref value cref */
@@ -8950,9 +9332,9 @@ compile_op_cdecl(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
ADD_INSN(ret, node, pop); /* [value] */
}
else {
- CHECK(COMPILE(ret, "NODE_OP_CDECL#nd_value", node->nd_value));
+ CHECK(compile_shareable_constant_value(iseq, ret, RNODE_OP_CDECL(node)->shareability, RNODE_OP_CDECL(node)->nd_head, RNODE_OP_CDECL(node)->nd_value));
/* cref obj value */
- ADD_CALL(ret, node, node->nd_aid, INT2FIX(1));
+ ADD_CALL(ret, node, RNODE_OP_CDECL(node)->nd_aid, INT2FIX(1));
/* cref value */
ADD_INSN(ret, node, swap); /* value cref */
if (!popped) {
@@ -8971,11 +9353,11 @@ compile_op_log(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
LABEL *lfin = NEW_LABEL(line);
LABEL *lassign;
- if (type == NODE_OP_ASGN_OR && !nd_type_p(node->nd_head, NODE_IVAR)) {
+ if (type == NODE_OP_ASGN_OR && !nd_type_p(RNODE_OP_ASGN_OR(node)->nd_head, NODE_IVAR)) {
LABEL *lfinish[2];
lfinish[0] = lfin;
lfinish[1] = 0;
- defined_expr(iseq, ret, node->nd_head, lfinish, Qfalse);
+ defined_expr(iseq, ret, RNODE_OP_ASGN_OR(node)->nd_head, lfinish, Qfalse);
lassign = lfinish[1];
if (!lassign) {
lassign = NEW_LABEL(line);
@@ -8986,7 +9368,7 @@ compile_op_log(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
lassign = NEW_LABEL(line);
}
- CHECK(COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_head", node->nd_head));
+ CHECK(COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_head", RNODE_OP_ASGN_OR(node)->nd_head));
if (!popped) {
ADD_INSN(ret, node, dup);
@@ -9004,7 +9386,7 @@ compile_op_log(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
}
ADD_LABEL(ret, lassign);
- CHECK(COMPILE_(ret, "NODE_OP_ASGN_AND/OR#nd_value", node->nd_value, popped));
+ CHECK(COMPILE_(ret, "NODE_OP_ASGN_AND/OR#nd_value", RNODE_OP_ASGN_OR(node)->nd_value, popped));
ADD_LABEL(ret, lfin);
return COMPILE_OK;
}
@@ -9018,13 +9400,22 @@ compile_super(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
unsigned int flag = 0;
struct rb_callinfo_kwarg *keywords = NULL;
const rb_iseq_t *parent_block = ISEQ_COMPILE_DATA(iseq)->current_block;
+ int use_block = 1;
INIT_ANCHOR(args);
ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
+
if (type == NODE_SUPER) {
- VALUE vargc = setup_args(iseq, args, node->nd_args, &flag, &keywords);
+ VALUE vargc = setup_args(iseq, args, RNODE_SUPER(node)->nd_args, &flag, &keywords);
CHECK(!NIL_P(vargc));
argc = FIX2INT(vargc);
+ if ((flag & VM_CALL_ARGS_BLOCKARG) && (flag & VM_CALL_KW_SPLAT) && !(flag & VM_CALL_KW_SPLAT_MUT)) {
+ ADD_INSN(args, node, splatkw);
+ }
+
+ if (flag & VM_CALL_ARGS_BLOCKARG) {
+ use_block = 0;
+ }
}
else {
/* NODE_ZSUPER */
@@ -9056,7 +9447,7 @@ compile_super(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
/* rest argument */
int idx = local_body->local_table_size - local_body->param.rest_start;
ADD_GETLOCAL(args, node, idx, lvar_level);
- ADD_INSN1(args, node, splatarray, Qfalse);
+ ADD_INSN1(args, node, splatarray, RBOOL(local_body->param.flags.has_post));
argc = local_body->param.rest_start + 1;
flag |= VM_CALL_ARGS_SPLAT;
@@ -9072,8 +9463,8 @@ compile_super(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
int idx = local_body->local_table_size - (post_start + j);
ADD_GETLOCAL(args, node, idx, lvar_level);
}
- ADD_INSN1(args, node, newarray, INT2FIX(j));
- ADD_INSN (args, node, concatarray);
+ ADD_INSN1(args, node, pushtoarray, INT2FIX(j));
+ flag |= VM_CALL_ARGS_SPLAT_MUT;
/* argc is settled at above */
}
else {
@@ -9095,14 +9486,11 @@ compile_super(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
if (local_body->param.flags.has_kwrest) {
int idx = local_body->local_table_size - local_kwd->rest_start;
ADD_GETLOCAL(args, node, idx, lvar_level);
- if (local_kwd->num > 0) {
- ADD_SEND (args, node, rb_intern("dup"), INT2FIX(0));
- flag |= VM_CALL_KW_SPLAT_MUT;
- }
+ RUBY_ASSERT(local_kwd->num > 0);
+ ADD_SEND (args, node, rb_intern("dup"), INT2FIX(0));
}
else {
ADD_INSN1(args, node, newhash, INT2FIX(0));
- flag |= VM_CALL_KW_SPLAT_MUT;
}
for (i = 0; i < local_kwd->num; ++i) {
ID id = local_kwd->table[i];
@@ -9111,16 +9499,20 @@ compile_super(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
ADD_GETLOCAL(args, node, idx, lvar_level);
}
ADD_SEND(args, node, id_core_hash_merge_ptr, INT2FIX(i * 2 + 1));
- flag |= VM_CALL_KW_SPLAT;
+ flag |= VM_CALL_KW_SPLAT| VM_CALL_KW_SPLAT_MUT;
}
else if (local_body->param.flags.has_kwrest) {
int idx = local_body->local_table_size - local_kwd->rest_start;
ADD_GETLOCAL(args, node, idx, lvar_level);
argc++;
- flag |= VM_CALL_KW_SPLAT | VM_CALL_KW_SPLAT_MUT;
+ flag |= VM_CALL_KW_SPLAT;
}
}
+ if (use_block && parent_block == NULL) {
+ iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
+ }
+
flag |= VM_CALL_SUPER | VM_CALL_FCALL;
if (type == NODE_ZSUPER) flag |= VM_CALL_ZSUPER;
ADD_INSN(ret, node, putself);
@@ -9154,8 +9546,8 @@ compile_yield(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
default: /* valid */;
}
- if (node->nd_head) {
- argc = setup_args(iseq, args, node->nd_head, &flag, &keywords);
+ if (RNODE_YIELD(node)->nd_head) {
+ argc = setup_args(iseq, args, RNODE_YIELD(node)->nd_head, &flag, &keywords);
CHECK(!NIL_P(argc));
}
else {
@@ -9164,6 +9556,7 @@ compile_yield(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
ADD_SEQ(ret, args);
ADD_INSN1(ret, node, invokeblock, new_callinfo(iseq, 0, FIX2INT(argc), flag, keywords, FALSE));
+ iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
if (popped) {
ADD_INSN(ret, node, pop);
@@ -9189,17 +9582,17 @@ compile_match(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
INIT_ANCHOR(val);
switch ((int)type) {
case NODE_MATCH:
- ADD_INSN1(recv, node, putobject, node->nd_lit);
+ ADD_INSN1(recv, node, putobject, rb_node_regx_string_val(node));
ADD_INSN2(val, node, getspecial, INT2FIX(0),
INT2FIX(0));
break;
case NODE_MATCH2:
- CHECK(COMPILE(recv, "receiver", node->nd_recv));
- CHECK(COMPILE(val, "value", node->nd_value));
+ CHECK(COMPILE(recv, "receiver", RNODE_MATCH2(node)->nd_recv));
+ CHECK(COMPILE(val, "value", RNODE_MATCH2(node)->nd_value));
break;
case NODE_MATCH3:
- CHECK(COMPILE(recv, "receiver", node->nd_value));
- CHECK(COMPILE(val, "value", node->nd_recv));
+ CHECK(COMPILE(recv, "receiver", RNODE_MATCH3(node)->nd_value));
+ CHECK(COMPILE(val, "value", RNODE_MATCH3(node)->nd_recv));
break;
}
@@ -9207,8 +9600,8 @@ compile_match(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
ADD_SEQ(ret, val);
ADD_SEND(ret, node, idEqTilde, INT2FIX(1));
- if (node->nd_args) {
- compile_named_capture_assign(iseq, ret, node->nd_args);
+ if (nd_type_p(node, NODE_MATCH2) && RNODE_MATCH2(node)->nd_args) {
+ compile_named_capture_assign(iseq, ret, RNODE_MATCH2(node)->nd_args);
}
if (popped) {
@@ -9220,7 +9613,7 @@ compile_match(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
static int
compile_colon2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
{
- if (rb_is_const_id(node->nd_mid)) {
+ if (rb_is_const_id(RNODE_COLON2(node)->nd_mid)) {
/* constant */
VALUE segments;
if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache &&
@@ -9250,8 +9643,8 @@ compile_colon2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
else {
/* function call */
ADD_CALL_RECEIVER(ret, node);
- CHECK(COMPILE(ret, "colon2#nd_head", node->nd_head));
- ADD_CALL(ret, node, node->nd_mid, INT2FIX(1));
+ CHECK(COMPILE(ret, "colon2#nd_head", RNODE_COLON2(node)->nd_head));
+ ADD_CALL(ret, node, RNODE_COLON2(node)->nd_mid, INT2FIX(1));
}
if (popped) {
ADD_INSN(ret, node, pop);
@@ -9262,19 +9655,19 @@ compile_colon2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
static int
compile_colon3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
{
- debugi("colon3#nd_mid", node->nd_mid);
+ debugi("colon3#nd_mid", RNODE_COLON3(node)->nd_mid);
/* add cache insn */
if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
ISEQ_BODY(iseq)->ic_size++;
- VALUE segments = rb_ary_new_from_args(2, ID2SYM(idNULL), ID2SYM(node->nd_mid));
+ VALUE segments = rb_ary_new_from_args(2, ID2SYM(idNULL), ID2SYM(RNODE_COLON3(node)->nd_mid));
ADD_INSN1(ret, node, opt_getconstant_path, segments);
RB_OBJ_WRITTEN(iseq, Qundef, segments);
}
else {
ADD_INSN1(ret, node, putobject, rb_cObject);
ADD_INSN1(ret, node, putobject, Qtrue);
- ADD_INSN1(ret, node, getconstant, ID2SYM(node->nd_mid));
+ ADD_INSN1(ret, node, getconstant, ID2SYM(RNODE_COLON3(node)->nd_mid));
}
if (popped) {
@@ -9287,13 +9680,13 @@ static int
compile_dots(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const int excl)
{
VALUE flag = INT2FIX(excl);
- const NODE *b = node->nd_beg;
- const NODE *e = node->nd_end;
+ const NODE *b = RNODE_DOT2(node)->nd_beg;
+ const NODE *e = RNODE_DOT2(node)->nd_end;
if (optimizable_range_item_p(b) && optimizable_range_item_p(e)) {
if (!popped) {
- VALUE bv = nd_type_p(b, NODE_LIT) ? b->nd_lit : Qnil;
- VALUE ev = nd_type_p(e, NODE_LIT) ? e->nd_lit : Qnil;
+ VALUE bv = optimized_range_item(b);
+ VALUE ev = optimized_range_item(e);
VALUE val = rb_range_new(bv, ev, excl);
ADD_INSN1(ret, node, putobject, val);
RB_OBJ_WRITTEN(iseq, Qundef, val);
@@ -9342,14 +9735,20 @@ compile_kw_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
{
struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
LABEL *end_label = NEW_LABEL(nd_line(node));
- const NODE *default_value = node->nd_body->nd_value;
+ const NODE *default_value = get_nd_value(RNODE_KW_ARG(node)->nd_body);
if (default_value == NODE_SPECIAL_REQUIRED_KEYWORD) {
/* required argument. do nothing */
COMPILE_ERROR(ERROR_ARGS "unreachable");
return COMPILE_NG;
}
- else if (nd_type_p(default_value, NODE_LIT) ||
+ else if (nd_type_p(default_value, NODE_SYM) ||
+ nd_type_p(default_value, NODE_REGX) ||
+ nd_type_p(default_value, NODE_LINE) ||
+ nd_type_p(default_value, NODE_INTEGER) ||
+ nd_type_p(default_value, NODE_FLOAT) ||
+ nd_type_p(default_value, NODE_RATIONAL) ||
+ nd_type_p(default_value, NODE_IMAGINARY) ||
nd_type_p(default_value, NODE_NIL) ||
nd_type_p(default_value, NODE_TRUE) ||
nd_type_p(default_value, NODE_FALSE)) {
@@ -9366,7 +9765,7 @@ compile_kw_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
ADD_INSN2(ret, node, checkkeyword, INT2FIX(kw_bits_idx + VM_ENV_DATA_SIZE - 1), INT2FIX(keyword_idx));
ADD_INSNL(ret, node, branchif, end_label);
- CHECK(COMPILE_POPPED(ret, "keyword default argument", node->nd_body));
+ CHECK(COMPILE_POPPED(ret, "keyword default argument", RNODE_KW_ARG(node)->nd_body));
ADD_LABEL(ret, end_label);
}
return COMPILE_OK;
@@ -9378,7 +9777,7 @@ compile_attrasgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
DECL_ANCHOR(recv);
DECL_ANCHOR(args);
unsigned int flag = 0;
- ID mid = node->nd_mid;
+ ID mid = RNODE_ATTRASGN(node)->nd_mid;
VALUE argc;
LABEL *else_label = NULL;
VALUE branches = Qfalse;
@@ -9386,16 +9785,16 @@ compile_attrasgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
/* optimization shortcut
* obj["literal"] = value -> opt_aset_with(obj, "literal", value)
*/
- if (mid == idASET && !private_recv_p(node) && node->nd_args &&
- nd_type_p(node->nd_args, NODE_LIST) && node->nd_args->nd_alen == 2 &&
- nd_type_p(node->nd_args->nd_head, NODE_STR) &&
+ if (mid == idASET && !private_recv_p(node) && RNODE_ATTRASGN(node)->nd_args &&
+ nd_type_p(RNODE_ATTRASGN(node)->nd_args, NODE_LIST) && RNODE_LIST(RNODE_ATTRASGN(node)->nd_args)->as.nd_alen == 2 &&
+ (nd_type_p(RNODE_LIST(RNODE_ATTRASGN(node)->nd_args)->nd_head, NODE_STR) || nd_type_p(RNODE_LIST(RNODE_ATTRASGN(node)->nd_args)->nd_head, NODE_FILE)) &&
ISEQ_COMPILE_DATA(iseq)->current_block == NULL &&
- !ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal &&
+ !frozen_string_literal_p(iseq) &&
ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction)
{
- VALUE str = rb_fstring(node->nd_args->nd_head->nd_lit);
- CHECK(COMPILE(ret, "recv", node->nd_recv));
- CHECK(COMPILE(ret, "value", node->nd_args->nd_next->nd_head));
+ VALUE str = get_string_value(RNODE_LIST(RNODE_ATTRASGN(node)->nd_args)->nd_head);
+ CHECK(COMPILE(ret, "recv", RNODE_ATTRASGN(node)->nd_recv));
+ CHECK(COMPILE(ret, "value", RNODE_LIST(RNODE_LIST(RNODE_ATTRASGN(node)->nd_args)->nd_next)->nd_head));
if (!popped) {
ADD_INSN(ret, node, swap);
ADD_INSN1(ret, node, topn, INT2FIX(1));
@@ -9409,10 +9808,10 @@ compile_attrasgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
INIT_ANCHOR(recv);
INIT_ANCHOR(args);
- argc = setup_args(iseq, args, node->nd_args, &flag, NULL);
+ argc = setup_args(iseq, args, RNODE_ATTRASGN(node)->nd_args, &flag, NULL);
CHECK(!NIL_P(argc));
- int asgnflag = COMPILE_RECV(recv, "recv", node);
+ int asgnflag = COMPILE_RECV(recv, "recv", node, RNODE_ATTRASGN(node)->nd_recv);
CHECK(asgnflag != -1);
flag |= (unsigned int)asgnflag;
@@ -9429,16 +9828,7 @@ compile_attrasgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
ADD_SEQ(ret, recv);
ADD_SEQ(ret, args);
- if (flag & VM_CALL_ARGS_BLOCKARG) {
- ADD_INSN1(ret, node, topn, INT2FIX(1));
- if (flag & VM_CALL_ARGS_SPLAT) {
- ADD_INSN1(ret, node, putobject, INT2FIX(-1));
- ADD_SEND_WITH_FLAG(ret, node, idAREF, INT2FIX(1), INT2FIX(asgnflag));
- }
- ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 3));
- ADD_INSN (ret, node, pop);
- }
- else if (flag & VM_CALL_ARGS_SPLAT) {
+ if (flag & VM_CALL_ARGS_SPLAT) {
ADD_INSN(ret, node, dup);
ADD_INSN1(ret, node, putobject, INT2FIX(-1));
ADD_SEND_WITH_FLAG(ret, node, idAREF, INT2FIX(1), INT2FIX(asgnflag));
@@ -9459,6 +9849,367 @@ compile_attrasgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
return COMPILE_OK;
}
+static int
+compile_make_shareable_node(rb_iseq_t *iseq, LINK_ANCHOR *ret, LINK_ANCHOR *sub, const NODE *value, bool copy)
+{
+ ADD_INSN1(ret, value, putobject, rb_mRubyVMFrozenCore);
+ ADD_SEQ(ret, sub);
+
+ if (copy) {
+ /*
+ * NEW_CALL(fcore, rb_intern("make_shareable_copy"),
+ * NEW_LIST(value, loc), loc);
+ */
+ ADD_SEND_WITH_FLAG(ret, value, rb_intern("make_shareable_copy"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
+ }
+ else {
+ /*
+ * NEW_CALL(fcore, rb_intern("make_shareable"),
+ * NEW_LIST(value, loc), loc);
+ */
+ ADD_SEND_WITH_FLAG(ret, value, rb_intern("make_shareable"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
+ }
+
+ return COMPILE_OK;
+}
+
+static VALUE
+node_const_decl_val(const NODE *node)
+{
+ VALUE path;
+ switch (nd_type(node)) {
+ case NODE_CDECL:
+ if (RNODE_CDECL(node)->nd_vid) {
+ path = rb_id2str(RNODE_CDECL(node)->nd_vid);
+ goto end;
+ }
+ else {
+ node = RNODE_CDECL(node)->nd_else;
+ }
+ break;
+ case NODE_COLON2:
+ break;
+ case NODE_COLON3:
+ // ::Const
+ path = rb_str_new_cstr("::");
+ rb_str_append(path, rb_id2str(RNODE_COLON3(node)->nd_mid));
+ goto end;
+ default:
+ rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
+ UNREACHABLE_RETURN(0);
+ }
+
+ path = rb_ary_new();
+ if (node) {
+ for (; node && nd_type_p(node, NODE_COLON2); node = RNODE_COLON2(node)->nd_head) {
+ rb_ary_push(path, rb_id2str(RNODE_COLON2(node)->nd_mid));
+ }
+ if (node && nd_type_p(node, NODE_CONST)) {
+ // Const::Name
+ rb_ary_push(path, rb_id2str(RNODE_CONST(node)->nd_vid));
+ }
+ else if (node && nd_type_p(node, NODE_COLON3)) {
+ // ::Const::Name
+ rb_ary_push(path, rb_id2str(RNODE_COLON3(node)->nd_mid));
+ rb_ary_push(path, rb_str_new(0, 0));
+ }
+ else {
+ // expression::Name
+ rb_ary_push(path, rb_str_new_cstr("..."));
+ }
+ path = rb_ary_join(rb_ary_reverse(path), rb_str_new_cstr("::"));
+ }
+ end:
+ path = rb_fstring(path);
+ return path;
+}
+
+static VALUE
+const_decl_path(NODE *dest)
+{
+ VALUE path = Qnil;
+ if (!nd_type_p(dest, NODE_CALL)) {
+ path = node_const_decl_val(dest);
+ }
+ return path;
+}
+
+static int
+compile_ensure_shareable_node(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *dest, const NODE *value)
+{
+ /*
+ *. RubyVM::FrozenCore.ensure_shareable(value, const_decl_path(dest))
+ */
+ VALUE path = const_decl_path(dest);
+ ADD_INSN1(ret, value, putobject, rb_mRubyVMFrozenCore);
+ CHECK(COMPILE(ret, "compile_ensure_shareable_node", value));
+ ADD_INSN1(ret, value, putobject, path);
+ RB_OBJ_WRITTEN(iseq, Qundef, path);
+ ADD_SEND_WITH_FLAG(ret, value, rb_intern("ensure_shareable"), INT2FIX(2), INT2FIX(VM_CALL_ARGS_SIMPLE));
+
+ return COMPILE_OK;
+}
+
+#ifndef SHAREABLE_BARE_EXPRESSION
+#define SHAREABLE_BARE_EXPRESSION 1
+#endif
+
+static int
+compile_shareable_literal_constant(rb_iseq_t *iseq, LINK_ANCHOR *ret, enum rb_parser_shareability shareable, NODE *dest, const NODE *node, size_t level, VALUE *value_p, int *shareable_literal_p)
+{
+# define compile_shareable_literal_constant_next(node, anchor, value_p, shareable_literal_p) \
+ compile_shareable_literal_constant(iseq, anchor, shareable, dest, node, level+1, value_p, shareable_literal_p)
+ VALUE lit = Qnil;
+ DECL_ANCHOR(anchor);
+
+ enum node_type type = nd_type(node);
+ switch (type) {
+ case NODE_TRUE:
+ *value_p = Qtrue;
+ goto compile;
+ case NODE_FALSE:
+ *value_p = Qfalse;
+ goto compile;
+ case NODE_NIL:
+ *value_p = Qnil;
+ goto compile;
+ case NODE_SYM:
+ *value_p = rb_node_sym_string_val(node);
+ goto compile;
+ case NODE_REGX:
+ *value_p = rb_node_regx_string_val(node);
+ goto compile;
+ case NODE_LINE:
+ *value_p = rb_node_line_lineno_val(node);
+ goto compile;
+ case NODE_INTEGER:
+ *value_p = rb_node_integer_literal_val(node);
+ goto compile;
+ case NODE_FLOAT:
+ *value_p = rb_node_float_literal_val(node);
+ goto compile;
+ case NODE_RATIONAL:
+ *value_p = rb_node_rational_literal_val(node);
+ goto compile;
+ case NODE_IMAGINARY:
+ *value_p = rb_node_imaginary_literal_val(node);
+ goto compile;
+ case NODE_ENCODING:
+ *value_p = rb_node_encoding_val(node);
+
+ compile:
+ CHECK(COMPILE(ret, "shareable_literal_constant", node));
+ *shareable_literal_p = 1;
+ return COMPILE_OK;
+
+ case NODE_DSTR:
+ CHECK(COMPILE(ret, "shareable_literal_constant", node));
+ if (shareable == rb_parser_shareable_literal) {
+ /*
+ * NEW_CALL(node, idUMinus, 0, loc);
+ *
+ * -"#{var}"
+ */
+ ADD_SEND_WITH_FLAG(ret, node, idUMinus, INT2FIX(0), INT2FIX(VM_CALL_ARGS_SIMPLE));
+ }
+ *value_p = Qundef;
+ *shareable_literal_p = 1;
+ return COMPILE_OK;
+
+ case NODE_STR:{
+ VALUE lit = rb_node_str_string_val(node);
+ ADD_INSN1(ret, node, putobject, lit);
+ RB_OBJ_WRITTEN(iseq, Qundef, lit);
+ *value_p = lit;
+ *shareable_literal_p = 1;
+
+ return COMPILE_OK;
+ }
+
+ case NODE_FILE:{
+ VALUE lit = rb_node_file_path_val(node);
+ ADD_INSN1(ret, node, putobject, lit);
+ RB_OBJ_WRITTEN(iseq, Qundef, lit);
+ *value_p = lit;
+ *shareable_literal_p = 1;
+
+ return COMPILE_OK;
+ }
+
+ case NODE_ZLIST:{
+ VALUE lit = rb_ary_new();
+ OBJ_FREEZE(lit);
+ ADD_INSN1(ret, node, putobject, lit);
+ RB_OBJ_WRITTEN(iseq, Qundef, lit);
+ *value_p = lit;
+ *shareable_literal_p = 1;
+
+ return COMPILE_OK;
+ }
+
+ case NODE_LIST:{
+ INIT_ANCHOR(anchor);
+ lit = rb_ary_new();
+ for (NODE *n = (NODE *)node; n; n = RNODE_LIST(n)->nd_next) {
+ VALUE val;
+ int shareable_literal_p2;
+ NODE *elt = RNODE_LIST(n)->nd_head;
+ if (elt) {
+ CHECK(compile_shareable_literal_constant_next(elt, anchor, &val, &shareable_literal_p2));
+ if (shareable_literal_p2) {
+ /* noop */
+ }
+ else if (RTEST(lit)) {
+ rb_ary_clear(lit);
+ lit = Qfalse;
+ }
+ }
+ if (RTEST(lit)) {
+ if (!UNDEF_P(val)) {
+ rb_ary_push(lit, val);
+ }
+ else {
+ rb_ary_clear(lit);
+ lit = Qnil; /* make shareable at runtime */
+ }
+ }
+ }
+ break;
+ }
+ case NODE_HASH:{
+ if (!RNODE_HASH(node)->nd_brace) {
+ *value_p = Qundef;
+ *shareable_literal_p = 0;
+ return COMPILE_OK;
+ }
+
+ INIT_ANCHOR(anchor);
+ lit = rb_hash_new();
+ for (NODE *n = RNODE_HASH(node)->nd_head; n; n = RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_next) {
+ VALUE key_val;
+ VALUE value_val;
+ int shareable_literal_p2;
+ NODE *key = RNODE_LIST(n)->nd_head;
+ NODE *val = RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_head;
+ if (key) {
+ CHECK(compile_shareable_literal_constant_next(key, anchor, &key_val, &shareable_literal_p2));
+ if (shareable_literal_p2) {
+ /* noop */
+ }
+ else if (RTEST(lit)) {
+ rb_hash_clear(lit);
+ lit = Qfalse;
+ }
+ }
+ if (val) {
+ CHECK(compile_shareable_literal_constant_next(val, anchor, &value_val, &shareable_literal_p2));
+ if (shareable_literal_p2) {
+ /* noop */
+ }
+ else if (RTEST(lit)) {
+ rb_hash_clear(lit);
+ lit = Qfalse;
+ }
+ }
+ if (RTEST(lit)) {
+ if (!UNDEF_P(key_val) && !UNDEF_P(value_val)) {
+ rb_hash_aset(lit, key_val, value_val);
+ }
+ else {
+ rb_hash_clear(lit);
+ lit = Qnil; /* make shareable at runtime */
+ }
+ }
+ }
+ break;
+ }
+
+ default:
+ if (shareable == rb_parser_shareable_literal &&
+ (SHAREABLE_BARE_EXPRESSION || level > 0)) {
+ CHECK(compile_ensure_shareable_node(iseq, ret, dest, node));
+ *value_p = Qundef;
+ *shareable_literal_p = 1;
+ return COMPILE_OK;
+ }
+ CHECK(COMPILE(ret, "shareable_literal_constant", node));
+ *value_p = Qundef;
+ *shareable_literal_p = 0;
+ return COMPILE_OK;
+ }
+
+ /* Array or Hash */
+ if (!lit) {
+ if (nd_type(node) == NODE_LIST) {
+ ADD_INSN1(anchor, node, newarray, INT2FIX(RNODE_LIST(node)->as.nd_alen));
+ }
+ else if (nd_type(node) == NODE_HASH) {
+ int len = (int)RNODE_LIST(RNODE_HASH(node)->nd_head)->as.nd_alen;
+ ADD_INSN1(anchor, node, newhash, INT2FIX(len));
+ }
+ *value_p = Qundef;
+ *shareable_literal_p = 0;
+ ADD_SEQ(ret, anchor);
+ return COMPILE_OK;
+ }
+ if (NIL_P(lit)) {
+ // if shareable_literal, all elements should have been ensured
+ // as shareable
+ if (nd_type(node) == NODE_LIST) {
+ ADD_INSN1(anchor, node, newarray, INT2FIX(RNODE_LIST(node)->as.nd_alen));
+ }
+ else if (nd_type(node) == NODE_HASH) {
+ int len = (int)RNODE_LIST(RNODE_HASH(node)->nd_head)->as.nd_alen;
+ ADD_INSN1(anchor, node, newhash, INT2FIX(len));
+ }
+ CHECK(compile_make_shareable_node(iseq, ret, anchor, node, false));
+ *value_p = Qundef;
+ *shareable_literal_p = 1;
+ }
+ else {
+ VALUE val = rb_ractor_make_shareable(lit);
+ ADD_INSN1(ret, node, putobject, val);
+ RB_OBJ_WRITTEN(iseq, Qundef, val);
+ *value_p = val;
+ *shareable_literal_p = 1;
+ }
+
+ return COMPILE_OK;
+}
+
+static int
+compile_shareable_constant_value(rb_iseq_t *iseq, LINK_ANCHOR *ret, enum rb_parser_shareability shareable, const NODE *lhs, const NODE *value)
+{
+ int literal_p = 0;
+ VALUE val;
+ DECL_ANCHOR(anchor);
+ INIT_ANCHOR(anchor);
+
+ switch (shareable) {
+ case rb_parser_shareable_none:
+ CHECK(COMPILE(ret, "compile_shareable_constant_value", value));
+ return COMPILE_OK;
+
+ case rb_parser_shareable_literal:
+ CHECK(compile_shareable_literal_constant(iseq, anchor, shareable, (NODE *)lhs, value, 0, &val, &literal_p));
+ ADD_SEQ(ret, anchor);
+ return COMPILE_OK;
+
+ case rb_parser_shareable_copy:
+ case rb_parser_shareable_everything:
+ CHECK(compile_shareable_literal_constant(iseq, anchor, shareable, (NODE *)lhs, value, 0, &val, &literal_p));
+ if (!literal_p) {
+ CHECK(compile_make_shareable_node(iseq, ret, anchor, value, shareable == rb_parser_shareable_copy));
+ }
+ else {
+ ADD_SEQ(ret, anchor);
+ }
+ return COMPILE_OK;
+ default:
+ rb_bug("unexpected rb_parser_shareability: %d", shareable);
+ }
+}
+
static int iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped);
/**
compile each node
@@ -9475,8 +10226,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE *node, int poppe
int lineno = ISEQ_COMPILE_DATA(iseq)->last_line;
if (lineno == 0) lineno = FIX2INT(rb_iseq_first_lineno(iseq));
debugs("node: NODE_NIL(implicit)\n");
- NODE dummy_line_node = generate_dummy_line_node(lineno, -1);
- ADD_INSN(ret, &dummy_line_node, putnil);
+ ADD_SYNTHETIC_INSN(ret, lineno, -1, putnil);
}
return COMPILE_OK;
}
@@ -9494,7 +10244,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
/* ignore */
}
else {
- if (node->flags & NODE_FL_NEWLINE) {
+ if (nd_fl_newline(node)) {
int event = RUBY_EVENT_LINE;
ISEQ_COMPILE_DATA(iseq)->last_line = line;
if (ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq)) {
@@ -9549,7 +10299,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
CHECK(compile_retry(iseq, ret, node, popped));
break;
case NODE_BEGIN:{
- CHECK(COMPILE_(ret, "NODE_BEGIN", node->nd_body, popped));
+ CHECK(COMPILE_(ret, "NODE_BEGIN", RNODE_BEGIN(node)->nd_body, popped));
break;
}
case NODE_RESCUE:
@@ -9565,7 +10315,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
case NODE_AND:
case NODE_OR:{
LABEL *end_label = NEW_LABEL(line);
- CHECK(COMPILE(ret, "nd_1st", node->nd_1st));
+ CHECK(COMPILE(ret, "nd_1st", RNODE_OR(node)->nd_1st));
if (!popped) {
ADD_INSN(ret, node, dup);
}
@@ -9578,7 +10328,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
if (!popped) {
ADD_INSN(ret, node, pop);
}
- CHECK(COMPILE_(ret, "nd_2nd", node->nd_2nd, popped));
+ CHECK(COMPILE_(ret, "nd_2nd", RNODE_OR(node)->nd_2nd, popped));
ADD_LABEL(ret, end_label);
break;
}
@@ -9589,11 +10339,11 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
}
case NODE_LASGN:{
- ID id = node->nd_vid;
+ ID id = RNODE_LASGN(node)->nd_vid;
int idx = ISEQ_BODY(body->local_iseq)->local_table_size - get_local_var_idx(iseq, id);
debugs("lvar: %s idx: %d\n", rb_id2name(id), idx);
- CHECK(COMPILE(ret, "rvalue", node->nd_value));
+ CHECK(COMPILE(ret, "rvalue", RNODE_LASGN(node)->nd_value));
if (!popped) {
ADD_INSN(ret, node, dup);
@@ -9603,8 +10353,8 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
}
case NODE_DASGN: {
int idx, lv, ls;
- ID id = node->nd_vid;
- CHECK(COMPILE(ret, "dvalue", node->nd_value));
+ ID id = RNODE_DASGN(node)->nd_vid;
+ CHECK(COMPILE(ret, "dvalue", RNODE_DASGN(node)->nd_value));
debugi("dassn id", rb_id2str(id) ? id : '*');
if (!popped) {
@@ -9622,27 +10372,27 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
break;
}
case NODE_GASGN:{
- CHECK(COMPILE(ret, "lvalue", node->nd_value));
+ CHECK(COMPILE(ret, "lvalue", RNODE_GASGN(node)->nd_value));
if (!popped) {
ADD_INSN(ret, node, dup);
}
- ADD_INSN1(ret, node, setglobal, ID2SYM(node->nd_entry));
+ ADD_INSN1(ret, node, setglobal, ID2SYM(RNODE_GASGN(node)->nd_vid));
break;
}
case NODE_IASGN:{
- CHECK(COMPILE(ret, "lvalue", node->nd_value));
+ CHECK(COMPILE(ret, "lvalue", RNODE_IASGN(node)->nd_value));
if (!popped) {
ADD_INSN(ret, node, dup);
}
ADD_INSN2(ret, node, setinstancevariable,
- ID2SYM(node->nd_vid),
- get_ivar_ic_value(iseq,node->nd_vid));
+ ID2SYM(RNODE_IASGN(node)->nd_vid),
+ get_ivar_ic_value(iseq,RNODE_IASGN(node)->nd_vid));
break;
}
case NODE_CDECL:{
- if (node->nd_vid) {
- CHECK(COMPILE(ret, "lvalue", node->nd_value));
+ if (RNODE_CDECL(node)->nd_vid) {
+ CHECK(compile_shareable_constant_value(iseq, ret, RNODE_CDECL(node)->shareability, node, RNODE_CDECL(node)->nd_value));
if (!popped) {
ADD_INSN(ret, node, dup);
@@ -9650,11 +10400,11 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
ADD_INSN1(ret, node, putspecialobject,
INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
- ADD_INSN1(ret, node, setconstant, ID2SYM(node->nd_vid));
+ ADD_INSN1(ret, node, setconstant, ID2SYM(RNODE_CDECL(node)->nd_vid));
}
else {
- compile_cpath(ret, iseq, node->nd_else);
- CHECK(COMPILE(ret, "lvalue", node->nd_value));
+ compile_cpath(ret, iseq, RNODE_CDECL(node)->nd_else);
+ CHECK(compile_shareable_constant_value(iseq, ret, RNODE_CDECL(node)->shareability, node, RNODE_CDECL(node)->nd_value));
ADD_INSN(ret, node, swap);
if (!popped) {
@@ -9662,18 +10412,18 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
ADD_INSN(ret, node, swap);
}
- ADD_INSN1(ret, node, setconstant, ID2SYM(node->nd_else->nd_mid));
+ ADD_INSN1(ret, node, setconstant, ID2SYM(get_node_colon_nd_mid(RNODE_CDECL(node)->nd_else)));
}
break;
}
case NODE_CVASGN:{
- CHECK(COMPILE(ret, "cvasgn val", node->nd_value));
+ CHECK(COMPILE(ret, "cvasgn val", RNODE_CVASGN(node)->nd_value));
if (!popped) {
ADD_INSN(ret, node, dup);
}
ADD_INSN2(ret, node, setclassvariable,
- ID2SYM(node->nd_vid),
- get_cvar_ic_value(iseq,node->nd_vid));
+ ID2SYM(RNODE_CVASGN(node)->nd_vid),
+ get_cvar_ic_value(iseq, RNODE_CVASGN(node)->nd_vid));
break;
}
case NODE_OP_ASGN1:
@@ -9706,7 +10456,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
CHECK(compile_super(iseq, ret, node, popped, type));
break;
case NODE_LIST:{
- CHECK(compile_array(iseq, ret, node, popped) >= 0);
+ CHECK(compile_array(iseq, ret, node, popped, TRUE) >= 0);
break;
}
case NODE_ZLIST:{
@@ -9715,18 +10465,6 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
}
break;
}
- case NODE_VALUES:{
- const NODE *n = node;
- if (popped) {
- COMPILE_ERROR(ERROR_ARGS "NODE_VALUES: must not be popped");
- }
- while (n) {
- CHECK(COMPILE(ret, "values item", n->nd_head));
- n = n->nd_next;
- }
- ADD_INSN1(ret, node, newarray, INT2FIX(node->nd_alen));
- break;
- }
case NODE_HASH:
CHECK(compile_hash(iseq, ret, node, FALSE, popped) >= 0);
break;
@@ -9738,18 +10476,18 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
break;
case NODE_LVAR:{
if (!popped) {
- compile_lvar(iseq, ret, node, node->nd_vid);
+ compile_lvar(iseq, ret, node, RNODE_LVAR(node)->nd_vid);
}
break;
}
case NODE_DVAR:{
int lv, idx, ls;
- debugi("nd_vid", node->nd_vid);
+ debugi("nd_vid", RNODE_DVAR(node)->nd_vid);
if (!popped) {
- idx = get_dyna_var_idx(iseq, node->nd_vid, &lv, &ls);
+ idx = get_dyna_var_idx(iseq, RNODE_DVAR(node)->nd_vid, &lv, &ls);
if (idx < 0) {
COMPILE_ERROR(ERROR_ARGS "unknown dvar (%"PRIsVALUE")",
- rb_id2str(node->nd_vid));
+ rb_id2str(RNODE_DVAR(node)->nd_vid));
goto ng;
}
ADD_GETLOCAL(ret, node, ls - idx, lv);
@@ -9757,34 +10495,34 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
break;
}
case NODE_GVAR:{
- ADD_INSN1(ret, node, getglobal, ID2SYM(node->nd_entry));
+ ADD_INSN1(ret, node, getglobal, ID2SYM(RNODE_GVAR(node)->nd_vid));
if (popped) {
ADD_INSN(ret, node, pop);
}
break;
}
case NODE_IVAR:{
- debugi("nd_vid", node->nd_vid);
+ debugi("nd_vid", RNODE_IVAR(node)->nd_vid);
if (!popped) {
ADD_INSN2(ret, node, getinstancevariable,
- ID2SYM(node->nd_vid),
- get_ivar_ic_value(iseq,node->nd_vid));
+ ID2SYM(RNODE_IVAR(node)->nd_vid),
+ get_ivar_ic_value(iseq, RNODE_IVAR(node)->nd_vid));
}
break;
}
case NODE_CONST:{
- debugi("nd_vid", node->nd_vid);
+ debugi("nd_vid", RNODE_CONST(node)->nd_vid);
if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
body->ic_size++;
- VALUE segments = rb_ary_new_from_args(1, ID2SYM(node->nd_vid));
+ VALUE segments = rb_ary_new_from_args(1, ID2SYM(RNODE_CONST(node)->nd_vid));
ADD_INSN1(ret, node, opt_getconstant_path, segments);
RB_OBJ_WRITTEN(iseq, Qundef, segments);
}
else {
ADD_INSN(ret, node, putnil);
ADD_INSN1(ret, node, putobject, Qtrue);
- ADD_INSN1(ret, node, getconstant, ID2SYM(node->nd_vid));
+ ADD_INSN1(ret, node, getconstant, ID2SYM(RNODE_CONST(node)->nd_vid));
}
if (popped) {
@@ -9795,26 +10533,26 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
case NODE_CVAR:{
if (!popped) {
ADD_INSN2(ret, node, getclassvariable,
- ID2SYM(node->nd_vid),
- get_cvar_ic_value(iseq,node->nd_vid));
+ ID2SYM(RNODE_CVAR(node)->nd_vid),
+ get_cvar_ic_value(iseq, RNODE_CVAR(node)->nd_vid));
}
break;
}
case NODE_NTH_REF:{
if (!popped) {
- if (!node->nd_nth) {
+ if (!RNODE_NTH_REF(node)->nd_nth) {
ADD_INSN(ret, node, putnil);
break;
}
ADD_INSN2(ret, node, getspecial, INT2FIX(1) /* '~' */,
- INT2FIX(node->nd_nth << 1));
+ INT2FIX(RNODE_NTH_REF(node)->nd_nth << 1));
}
break;
}
case NODE_BACK_REF:{
if (!popped) {
ADD_INSN2(ret, node, getspecial, INT2FIX(1) /* '~' */,
- INT2FIX(0x01 | (node->nd_nth << 1)));
+ INT2FIX(0x01 | (RNODE_BACK_REF(node)->nd_nth << 1)));
}
break;
}
@@ -9823,35 +10561,86 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
case NODE_MATCH3:
CHECK(compile_match(iseq, ret, node, popped, type));
break;
- case NODE_LIT:{
- debugp_param("lit", node->nd_lit);
+ case NODE_SYM:{
+ if (!popped) {
+ ADD_INSN1(ret, node, putobject, rb_node_sym_string_val(node));
+ }
+ break;
+ }
+ case NODE_LINE:{
+ if (!popped) {
+ ADD_INSN1(ret, node, putobject, rb_node_line_lineno_val(node));
+ }
+ break;
+ }
+ case NODE_ENCODING:{
+ if (!popped) {
+ ADD_INSN1(ret, node, putobject, rb_node_encoding_val(node));
+ }
+ break;
+ }
+ case NODE_INTEGER:{
+ VALUE lit = rb_node_integer_literal_val(node);
+ debugp_param("integer", lit);
+ if (!popped) {
+ ADD_INSN1(ret, node, putobject, lit);
+ RB_OBJ_WRITTEN(iseq, Qundef, lit);
+ }
+ break;
+ }
+ case NODE_FLOAT:{
+ VALUE lit = rb_node_float_literal_val(node);
+ debugp_param("float", lit);
+ if (!popped) {
+ ADD_INSN1(ret, node, putobject, lit);
+ RB_OBJ_WRITTEN(iseq, Qundef, lit);
+ }
+ break;
+ }
+ case NODE_RATIONAL:{
+ VALUE lit = rb_node_rational_literal_val(node);
+ debugp_param("rational", lit);
if (!popped) {
- ADD_INSN1(ret, node, putobject, node->nd_lit);
- RB_OBJ_WRITTEN(iseq, Qundef, node->nd_lit);
+ ADD_INSN1(ret, node, putobject, lit);
+ RB_OBJ_WRITTEN(iseq, Qundef, lit);
}
break;
}
+ case NODE_IMAGINARY:{
+ VALUE lit = rb_node_imaginary_literal_val(node);
+ debugp_param("imaginary", lit);
+ if (!popped) {
+ ADD_INSN1(ret, node, putobject, lit);
+ RB_OBJ_WRITTEN(iseq, Qundef, lit);
+ }
+ break;
+ }
+ case NODE_FILE:
case NODE_STR:{
- debugp_param("nd_lit", node->nd_lit);
+ debugp_param("nd_lit", get_string_value(node));
if (!popped) {
- VALUE lit = node->nd_lit;
- if (!ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal) {
- lit = rb_fstring(lit);
+ VALUE lit = get_string_value(node);
+ switch (ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal) {
+ case ISEQ_FROZEN_STRING_LITERAL_UNSET:
+ ADD_INSN1(ret, node, putchilledstring, lit);
+ RB_OBJ_WRITTEN(iseq, Qundef, lit);
+ break;
+ case ISEQ_FROZEN_STRING_LITERAL_DISABLED:
ADD_INSN1(ret, node, putstring, lit);
RB_OBJ_WRITTEN(iseq, Qundef, lit);
- }
- else {
+ break;
+ case ISEQ_FROZEN_STRING_LITERAL_ENABLED:
if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
VALUE debug_info = rb_ary_new_from_args(2, rb_iseq_path(iseq), INT2FIX(line));
lit = rb_str_dup(lit);
rb_ivar_set(lit, id_debug_created_info, rb_obj_freeze(debug_info));
lit = rb_str_freeze(lit);
}
- else {
- lit = rb_fstring(lit);
- }
ADD_INSN1(ret, node, putobject, lit);
RB_OBJ_WRITTEN(iseq, Qundef, lit);
+ break;
+ default:
+ rb_bug("invalid frozen_string_literal");
}
}
break;
@@ -9866,7 +10655,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
}
case NODE_XSTR:{
ADD_CALL_RECEIVER(ret, node);
- VALUE str = rb_fstring(node->nd_lit);
+ VALUE str = rb_node_str_string_val(node);
ADD_INSN1(ret, node, putobject, str);
RB_OBJ_WRITTEN(iseq, Qundef, str);
ADD_CALL(ret, node, idBackquote, INT2FIX(1));
@@ -9887,20 +10676,23 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
break;
}
case NODE_EVSTR:
- CHECK(compile_evstr(iseq, ret, node->nd_body, popped));
+ CHECK(compile_evstr(iseq, ret, RNODE_EVSTR(node)->nd_body, popped));
break;
- case NODE_DREGX:{
- compile_dregx(iseq, ret, node);
-
- if (popped) {
- ADD_INSN(ret, node, pop);
+ case NODE_REGX:{
+ if (!popped) {
+ VALUE lit = rb_node_regx_string_val(node);
+ ADD_INSN1(ret, node, putobject, lit);
+ RB_OBJ_WRITTEN(iseq, Qundef, lit);
}
break;
}
+ case NODE_DREGX:
+ compile_dregx(iseq, ret, node, popped);
+ break;
case NODE_ONCE:{
int ic_index = body->ise_size++;
const rb_iseq_t *block_iseq;
- block_iseq = NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq), ISEQ_TYPE_PLAIN, line);
+ block_iseq = NEW_CHILD_ISEQ(RNODE_ONCE(node)->nd_body, make_name_for_block(iseq), ISEQ_TYPE_PLAIN, line);
ADD_INSN2(ret, node, once, block_iseq, INT2FIX(ic_index));
RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)block_iseq);
@@ -9912,36 +10704,53 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
}
case NODE_ARGSCAT:{
if (popped) {
- CHECK(COMPILE(ret, "argscat head", node->nd_head));
+ CHECK(COMPILE(ret, "argscat head", RNODE_ARGSCAT(node)->nd_head));
ADD_INSN1(ret, node, splatarray, Qfalse);
ADD_INSN(ret, node, pop);
- CHECK(COMPILE(ret, "argscat body", node->nd_body));
+ CHECK(COMPILE(ret, "argscat body", RNODE_ARGSCAT(node)->nd_body));
ADD_INSN1(ret, node, splatarray, Qfalse);
ADD_INSN(ret, node, pop);
}
else {
- CHECK(COMPILE(ret, "argscat head", node->nd_head));
- CHECK(COMPILE(ret, "argscat body", node->nd_body));
- ADD_INSN(ret, node, concatarray);
+ CHECK(COMPILE(ret, "argscat head", RNODE_ARGSCAT(node)->nd_head));
+ const NODE *body_node = RNODE_ARGSCAT(node)->nd_body;
+ if (nd_type_p(body_node, NODE_LIST)) {
+ CHECK(compile_array(iseq, ret, body_node, popped, FALSE) >= 0);
+ }
+ else {
+ CHECK(COMPILE(ret, "argscat body", body_node));
+ ADD_INSN(ret, node, concattoarray);
+ }
}
break;
}
case NODE_ARGSPUSH:{
if (popped) {
- CHECK(COMPILE(ret, "argspush head", node->nd_head));
+ CHECK(COMPILE(ret, "argspush head", RNODE_ARGSPUSH(node)->nd_head));
ADD_INSN1(ret, node, splatarray, Qfalse);
ADD_INSN(ret, node, pop);
- CHECK(COMPILE_(ret, "argspush body", node->nd_body, popped));
+ CHECK(COMPILE_(ret, "argspush body", RNODE_ARGSPUSH(node)->nd_body, popped));
}
else {
- CHECK(COMPILE(ret, "argspush head", node->nd_head));
- CHECK(compile_array_1(iseq, ret, node->nd_body));
- ADD_INSN(ret, node, concatarray);
+ CHECK(COMPILE(ret, "argspush head", RNODE_ARGSPUSH(node)->nd_head));
+ const NODE *body_node = RNODE_ARGSPUSH(node)->nd_body;
+ if (keyword_node_p(body_node)) {
+ CHECK(COMPILE_(ret, "array element", body_node, FALSE));
+ ADD_INSN(ret, node, pushtoarraykwsplat);
+ }
+ else if (static_literal_node_p(body_node, iseq, false)) {
+ ADD_INSN1(ret, body_node, putobject, static_literal_value(body_node, iseq));
+ ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
+ }
+ else {
+ CHECK(COMPILE_(ret, "array element", body_node, FALSE));
+ ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
+ }
}
break;
}
case NODE_SPLAT:{
- CHECK(COMPILE(ret, "splat", node->nd_head));
+ CHECK(COMPILE(ret, "splat", RNODE_SPLAT(node)->nd_head));
ADD_INSN1(ret, node, splatarray, Qtrue);
if (popped) {
@@ -9950,8 +10759,8 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
break;
}
case NODE_DEFN:{
- ID mid = node->nd_mid;
- const rb_iseq_t *method_iseq = NEW_ISEQ(node->nd_defn,
+ ID mid = RNODE_DEFN(node)->nd_mid;
+ const rb_iseq_t *method_iseq = NEW_ISEQ(RNODE_DEFN(node)->nd_defn,
rb_id2str(mid),
ISEQ_TYPE_METHOD, line);
@@ -9966,13 +10775,13 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
break;
}
case NODE_DEFS:{
- ID mid = node->nd_mid;
- const rb_iseq_t * singleton_method_iseq = NEW_ISEQ(node->nd_defn,
+ ID mid = RNODE_DEFS(node)->nd_mid;
+ const rb_iseq_t * singleton_method_iseq = NEW_ISEQ(RNODE_DEFS(node)->nd_defn,
rb_id2str(mid),
ISEQ_TYPE_METHOD, line);
debugp_param("defs/iseq", rb_iseqw_new(singleton_method_iseq));
- CHECK(COMPILE(ret, "defs: recv", node->nd_recv));
+ CHECK(COMPILE(ret, "defs: recv", RNODE_DEFS(node)->nd_recv));
ADD_INSN2(ret, node, definesmethod, ID2SYM(mid), singleton_method_iseq);
RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)singleton_method_iseq);
@@ -9984,8 +10793,8 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
case NODE_ALIAS:{
ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
- CHECK(COMPILE(ret, "alias arg1", node->nd_1st));
- CHECK(COMPILE(ret, "alias arg2", node->nd_2nd));
+ CHECK(COMPILE(ret, "alias arg1", RNODE_ALIAS(node)->nd_1st));
+ CHECK(COMPILE(ret, "alias arg2", RNODE_ALIAS(node)->nd_2nd));
ADD_SEND(ret, node, id_core_set_method_alias, INT2FIX(3));
if (popped) {
@@ -9995,8 +10804,8 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
}
case NODE_VALIAS:{
ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
- ADD_INSN1(ret, node, putobject, ID2SYM(node->nd_alias));
- ADD_INSN1(ret, node, putobject, ID2SYM(node->nd_orig));
+ ADD_INSN1(ret, node, putobject, ID2SYM(RNODE_VALIAS(node)->nd_alias));
+ ADD_INSN1(ret, node, putobject, ID2SYM(RNODE_VALIAS(node)->nd_orig));
ADD_SEND(ret, node, id_core_set_variable_alias, INT2FIX(2));
if (popped) {
@@ -10007,7 +10816,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
case NODE_UNDEF:{
ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
- CHECK(COMPILE(ret, "undef arg", node->nd_undef));
+ CHECK(COMPILE(ret, "undef arg", RNODE_UNDEF(node)->nd_undef));
ADD_SEND(ret, node, id_core_undef_method, INT2FIX(2));
if (popped) {
@@ -10016,15 +10825,15 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
break;
}
case NODE_CLASS:{
- const rb_iseq_t *class_iseq = NEW_CHILD_ISEQ(node->nd_body,
- rb_str_freeze(rb_sprintf("<class:%"PRIsVALUE">", rb_id2str(node->nd_cpath->nd_mid))),
+ const rb_iseq_t *class_iseq = NEW_CHILD_ISEQ(RNODE_CLASS(node)->nd_body,
+ rb_str_freeze(rb_sprintf("<class:%"PRIsVALUE">", rb_id2str(get_node_colon_nd_mid(RNODE_CLASS(node)->nd_cpath)))),
ISEQ_TYPE_CLASS, line);
const int flags = VM_DEFINECLASS_TYPE_CLASS |
- (node->nd_super ? VM_DEFINECLASS_FLAG_HAS_SUPERCLASS : 0) |
- compile_cpath(ret, iseq, node->nd_cpath);
+ (RNODE_CLASS(node)->nd_super ? VM_DEFINECLASS_FLAG_HAS_SUPERCLASS : 0) |
+ compile_cpath(ret, iseq, RNODE_CLASS(node)->nd_cpath);
- CHECK(COMPILE(ret, "super", node->nd_super));
- ADD_INSN3(ret, node, defineclass, ID2SYM(node->nd_cpath->nd_mid), class_iseq, INT2FIX(flags));
+ CHECK(COMPILE(ret, "super", RNODE_CLASS(node)->nd_super));
+ ADD_INSN3(ret, node, defineclass, ID2SYM(get_node_colon_nd_mid(RNODE_CLASS(node)->nd_cpath)), class_iseq, INT2FIX(flags));
RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)class_iseq);
if (popped) {
@@ -10033,14 +10842,14 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
break;
}
case NODE_MODULE:{
- const rb_iseq_t *module_iseq = NEW_CHILD_ISEQ(node->nd_body,
- rb_str_freeze(rb_sprintf("<module:%"PRIsVALUE">", rb_id2str(node->nd_cpath->nd_mid))),
+ const rb_iseq_t *module_iseq = NEW_CHILD_ISEQ(RNODE_MODULE(node)->nd_body,
+ rb_str_freeze(rb_sprintf("<module:%"PRIsVALUE">", rb_id2str(get_node_colon_nd_mid(RNODE_MODULE(node)->nd_cpath)))),
ISEQ_TYPE_CLASS, line);
const int flags = VM_DEFINECLASS_TYPE_MODULE |
- compile_cpath(ret, iseq, node->nd_cpath);
+ compile_cpath(ret, iseq, RNODE_MODULE(node)->nd_cpath);
ADD_INSN (ret, node, putnil); /* dummy */
- ADD_INSN3(ret, node, defineclass, ID2SYM(node->nd_cpath->nd_mid), module_iseq, INT2FIX(flags));
+ ADD_INSN3(ret, node, defineclass, ID2SYM(get_node_colon_nd_mid(RNODE_MODULE(node)->nd_cpath)), module_iseq, INT2FIX(flags));
RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)module_iseq);
if (popped) {
@@ -10050,10 +10859,10 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
}
case NODE_SCLASS:{
ID singletonclass;
- const rb_iseq_t *singleton_class = NEW_ISEQ(node->nd_body, rb_fstring_lit("singleton class"),
+ const rb_iseq_t *singleton_class = NEW_ISEQ(RNODE_SCLASS(node)->nd_body, rb_fstring_lit("singleton class"),
ISEQ_TYPE_CLASS, line);
- CHECK(COMPILE(ret, "sclass#recv", node->nd_recv));
+ CHECK(COMPILE(ret, "sclass#recv", RNODE_SCLASS(node)->nd_recv));
ADD_INSN (ret, node, putnil);
CONST_ID(singletonclass, "singletonclass");
ADD_INSN3(ret, node, defineclass,
@@ -10131,7 +10940,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
*/
int is_index = body->ise_size++;
struct rb_iseq_new_with_callback_callback_func *ifunc =
- rb_iseq_new_with_callback_new_callback(build_postexe_iseq, node->nd_body);
+ rb_iseq_new_with_callback_new_callback(build_postexe_iseq, RNODE_POSTEXE(node)->nd_body);
const rb_iseq_t *once_iseq =
new_child_iseq_with_callback(iseq, ifunc,
rb_fstring(make_name_for_block(iseq)), iseq, ISEQ_TYPE_BLOCK, line);
@@ -10162,7 +10971,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
break;
case NODE_LAMBDA:{
/* compile same as lambda{...} */
- const rb_iseq_t *block = NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, line);
+ const rb_iseq_t *block = NEW_CHILD_ISEQ(RNODE_LAMBDA(node)->nd_body, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, line);
VALUE argc = INT2FIX(0);
ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
@@ -10347,7 +11156,7 @@ dump_disasm_list_with_cursor(const LINK_ELEMENT *link, const LINK_ELEMENT *curr,
case ISEQ_ELEMENT_LABEL:
{
lobj = (LABEL *)link;
- printf(LABEL_FORMAT" [sp: %d]%s\n", lobj->label_no, lobj->sp,
+ printf(LABEL_FORMAT" [sp: %d, unremovable: %d, refcnt: %d]%s\n", lobj->label_no, lobj->sp, lobj->unremovable, lobj->refcnt,
dest == lobj ? " <---" : "");
break;
}
@@ -10373,12 +11182,6 @@ dump_disasm_list_with_cursor(const LINK_ELEMENT *link, const LINK_ELEMENT *curr,
fflush(stdout);
}
-bool
-rb_insns_leaf_p(int i)
-{
- return insn_leaf_p(i);
-}
-
int
rb_insn_len(VALUE insn)
{
@@ -10548,6 +11351,7 @@ iseq_build_callinfo_from_hash(rb_iseq_t *iseq, VALUE op)
size_t n = rb_callinfo_kwarg_bytes(len);
kw_arg = xmalloc(n);
+ kw_arg->references = 0;
kw_arg->keyword_len = len;
for (i = 0; i < len; i++) {
VALUE kw = RARRAY_AREF(vkw_arg, i);
@@ -10573,6 +11377,7 @@ event_name_to_flag(VALUE sym)
CHECK_EVENT(RUBY_EVENT_RETURN);
CHECK_EVENT(RUBY_EVENT_B_CALL);
CHECK_EVENT(RUBY_EVENT_B_RETURN);
+ CHECK_EVENT(RUBY_EVENT_RESCUE);
#undef CHECK_EVENT
return RUBY_EVENT_NONE;
}
@@ -10643,9 +11448,8 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor,
argv = compile_data_calloc2(iseq, sizeof(VALUE), argc);
// add element before operand setup to make GC root
- NODE dummy_line_node = generate_dummy_line_node(line_no, node_id);
ADD_ELEM(anchor,
- (LINK_ELEMENT*)new_insn_core(iseq, &dummy_line_node,
+ (LINK_ELEMENT*)new_insn_core(iseq, line_no, node_id,
(enum ruby_vminsn_type)insn_id, argc, argv));
for (j=0; j<argc; j++) {
@@ -10753,9 +11557,8 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor,
}
}
else {
- NODE dummy_line_node = generate_dummy_line_node(line_no, node_id);
ADD_ELEM(anchor,
- (LINK_ELEMENT*)new_insn_core(iseq, &dummy_line_node,
+ (LINK_ELEMENT*)new_insn_core(iseq, line_no, node_id,
(enum ruby_vminsn_type)insn_id, argc, NULL));
}
}
@@ -10764,6 +11567,7 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor,
}
}
DATA_PTR(labels_wrapper) = 0;
+ RB_GC_GUARD(labels_wrapper);
validate_labels(iseq, labels_table);
if (!ret) return ret;
return iseq_setup(iseq, anchor);
@@ -10856,8 +11660,14 @@ iseq_build_kw(rb_iseq_t *iseq, VALUE params, VALUE keywords)
return keyword;
}
+static void
+iseq_insn_each_object_mark_and_pin(VALUE obj, VALUE _)
+{
+ rb_gc_mark(obj);
+}
+
void
-rb_iseq_mark_and_move_insn_storage(struct iseq_compile_data_storage *storage)
+rb_iseq_mark_and_pin_insn_storage(struct iseq_compile_data_storage *storage)
{
INSN *iobj = 0;
size_t size = sizeof(INSN);
@@ -10882,23 +11692,7 @@ rb_iseq_mark_and_move_insn_storage(struct iseq_compile_data_storage *storage)
iobj = (INSN *)&storage->buff[pos];
if (iobj->operands) {
- int j;
- const char *types = insn_op_types(iobj->insn_id);
-
- for (j = 0; types[j]; j++) {
- char type = types[j];
- switch (type) {
- case TS_CDHASH:
- case TS_ISEQ:
- case TS_VALUE:
- case TS_IC: // constant path array
- case TS_CALLDATA: // ci is stored.
- rb_gc_mark_and_move(&OPERAND_AT(iobj, j));
- break;
- default:
- break;
- }
- }
+ iseq_insn_each_markable_object(iobj, iseq_insn_each_object_mark_and_pin, (VALUE)0);
}
pos += (int)size;
}
@@ -10996,6 +11790,10 @@ rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc, VALUE locals, VALUE params,
ISEQ_BODY(iseq)->param.flags.ambiguous_param0 = TRUE;
}
+ if (Qtrue == rb_hash_aref(params, SYM(use_block))) {
+ ISEQ_BODY(iseq)->param.flags.use_block = TRUE;
+ }
+
if (int_param(&i, params, SYM(kwrest))) {
struct rb_iseq_param_keyword *keyword = (struct rb_iseq_param_keyword *)ISEQ_BODY(iseq)->param.keyword;
if (keyword == NULL) {
@@ -11071,7 +11869,7 @@ rb_local_defined(ID id, const rb_iseq_t *iseq)
#define IBF_ISEQ_ENABLE_LOCAL_BUFFER 0
#endif
-typedef unsigned int ibf_offset_t;
+typedef uint32_t ibf_offset_t;
#define IBF_OFFSET(ptr) ((ibf_offset_t)(VALUE)(ptr))
#define IBF_MAJOR_VERSION ISEQ_MAJOR_VERSION
@@ -11082,17 +11880,27 @@ typedef unsigned int ibf_offset_t;
#define IBF_MINOR_VERSION ISEQ_MINOR_VERSION
#endif
+static const char IBF_ENDIAN_MARK =
+#ifdef WORDS_BIGENDIAN
+ 'b'
+#else
+ 'l'
+#endif
+ ;
+
struct ibf_header {
char magic[4]; /* YARB */
- unsigned int major_version;
- unsigned int minor_version;
- unsigned int size;
- unsigned int extra_size;
+ uint32_t major_version;
+ uint32_t minor_version;
+ uint32_t size;
+ uint32_t extra_size;
- unsigned int iseq_list_size;
- unsigned int global_object_list_size;
+ uint32_t iseq_list_size;
+ uint32_t global_object_list_size;
ibf_offset_t iseq_list_offset;
ibf_offset_t global_object_list_offset;
+ uint8_t endian;
+ uint8_t wordsize; /* assume no 2048-bit CPU */
};
struct ibf_dump_buffer {
@@ -11127,7 +11935,7 @@ struct ibf_load {
struct pinned_list {
long size;
- VALUE * buffer;
+ VALUE buffer[1];
};
static void
@@ -11142,25 +11950,14 @@ pinned_list_mark(void *ptr)
}
}
-static void
-pinned_list_free(void *ptr)
-{
- struct pinned_list *list = (struct pinned_list *)ptr;
- xfree(list->buffer);
- xfree(ptr);
-}
-
-static size_t
-pinned_list_memsize(const void *ptr)
-{
- struct pinned_list *list = (struct pinned_list *)ptr;
- return sizeof(struct pinned_list) + (list->size * sizeof(VALUE *));
-}
-
static const rb_data_type_t pinned_list_type = {
"pinned_list",
- {pinned_list_mark, pinned_list_free, pinned_list_memsize,},
- 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
+ {
+ pinned_list_mark,
+ RUBY_DEFAULT_FREE,
+ NULL, // No external memory to report,
+ },
+ 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_EMBEDDABLE
};
static VALUE
@@ -11194,13 +11991,10 @@ pinned_list_store(VALUE list, long offset, VALUE object)
static VALUE
pinned_list_new(long size)
{
- struct pinned_list * ptr;
- VALUE obj_list =
- TypedData_Make_Struct(0, struct pinned_list, &pinned_list_type, ptr);
-
- ptr->buffer = xcalloc(size, sizeof(VALUE));
+ size_t memsize = offsetof(struct pinned_list, buffer) + size * sizeof(VALUE);
+ VALUE obj_list = rb_data_typed_object_zalloc(0, memsize, &pinned_list_type);
+ struct pinned_list * ptr = RTYPEDDATA_GET_DATA(obj_list);
ptr->size = size;
-
return obj_list;
}
@@ -11349,6 +12143,10 @@ ibf_load_id(const struct ibf_load *load, const ID id_index)
return 0;
}
VALUE sym = ibf_load_object(load, id_index);
+ if (rb_integer_type_p(sym)) {
+ /* Load hidden local variables as indexes */
+ return NUM2ULONG(sym);
+ }
return rb_sym2id(sym);
}
@@ -11548,7 +12346,7 @@ ibf_dump_code(struct ibf_dump *dump, const rb_iseq_t *iseq)
ibf_dump_write_small_value(dump, wv);
skip_wv:;
}
- assert(insn_len(insn) == op_index+1);
+ RUBY_ASSERT(insn_len(insn) == op_index+1);
}
return offset;
@@ -11713,8 +12511,8 @@ ibf_load_code(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t bytecod
}
}
- assert(code_index == iseq_size);
- assert(reading_pos == bytecode_offset + bytecode_size);
+ RUBY_ASSERT(code_index == iseq_size);
+ RUBY_ASSERT(reading_pos == bytecode_offset + bytecode_size);
return code;
}
@@ -11872,7 +12670,12 @@ ibf_dump_local_table(struct ibf_dump *dump, const rb_iseq_t *iseq)
int i;
for (i=0; i<size; i++) {
- table[i] = ibf_dump_id(dump, body->local_table[i]);
+ VALUE v = ibf_dump_id(dump, body->local_table[i]);
+ if (v == 0) {
+ /* Dump hidden local variables as indexes, so load_from_binary will work with them */
+ v = ibf_dump_object(dump, ULONG2NUM(body->local_table[i]));
+ }
+ table[i] = v;
}
IBF_W_ALIGN(ID);
@@ -11993,15 +12796,36 @@ ibf_dump_ci_entries(struct ibf_dump *dump, const rb_iseq_t *iseq)
return offset;
}
+struct outer_variable_pair {
+ ID id;
+ VALUE name;
+ VALUE val;
+};
+
+struct outer_variable_list {
+ size_t num;
+ struct outer_variable_pair pairs[1];
+};
+
static enum rb_id_table_iterator_result
-dump_outer_variable(ID id, VALUE val, void *dump)
+store_outer_variable(ID id, VALUE val, void *dump)
{
- ibf_dump_write_small_value(dump, ibf_dump_id(dump, id));
- ibf_dump_write_small_value(dump, val);
-
+ struct outer_variable_list *ovlist = dump;
+ struct outer_variable_pair *pair = &ovlist->pairs[ovlist->num++];
+ pair->id = id;
+ pair->name = rb_id2str(id);
+ pair->val = val;
return ID_TABLE_CONTINUE;
}
+static int
+outer_variable_cmp(const void *a, const void *b, void *arg)
+{
+ const struct outer_variable_pair *ap = (const struct outer_variable_pair *)a;
+ const struct outer_variable_pair *bp = (const struct outer_variable_pair *)b;
+ return rb_str_cmp(ap->name, bp->name);
+}
+
static ibf_offset_t
ibf_dump_outer_variables(struct ibf_dump *dump, const rb_iseq_t *iseq)
{
@@ -12009,12 +12833,24 @@ ibf_dump_outer_variables(struct ibf_dump *dump, const rb_iseq_t *iseq)
ibf_offset_t offset = ibf_dump_pos(dump);
- if (ovs) {
- ibf_dump_write_small_value(dump, (VALUE)rb_id_table_size(ovs));
- rb_id_table_foreach(ovs, dump_outer_variable, (void *)dump);
- }
- else {
- ibf_dump_write_small_value(dump, (VALUE)0);
+ size_t size = ovs ? rb_id_table_size(ovs) : 0;
+ ibf_dump_write_small_value(dump, (VALUE)size);
+ if (size > 0) {
+ VALUE buff;
+ size_t buffsize =
+ rb_size_mul_add_or_raise(sizeof(struct outer_variable_pair), size,
+ offsetof(struct outer_variable_list, pairs),
+ rb_eArgError);
+ struct outer_variable_list *ovlist = RB_ALLOCV(buff, buffsize);
+ ovlist->num = 0;
+ rb_id_table_foreach(ovs, store_outer_variable, ovlist);
+ ruby_qsort(ovlist->pairs, size, sizeof(struct outer_variable_pair), outer_variable_cmp, NULL);
+ for (size_t i = 0; i < size; ++i) {
+ ID id = ovlist->pairs[i].id;
+ ID val = ovlist->pairs[i].val;
+ ibf_dump_write_small_value(dump, ibf_dump_id(dump, id));
+ ibf_dump_write_small_value(dump, val);
+ }
}
return offset;
@@ -12045,6 +12881,7 @@ ibf_load_ci_entries(const struct ibf_load *load,
int kwlen = (int)ibf_load_small_value(load, &reading_pos);
if (kwlen > 0) {
kwarg = rb_xmalloc_mul_add(kwlen, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
+ kwarg->references = 0;
kwarg->keyword_len = kwlen;
for (int j=0; j<kwlen; j++) {
VALUE keyword = ibf_load_small_value(load, &reading_pos);
@@ -12090,7 +12927,7 @@ ibf_load_outer_variables(const struct ibf_load * load, ibf_offset_t outer_variab
static ibf_offset_t
ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq)
{
- assert(dump->current_buffer == &dump->global_buffer);
+ RUBY_ASSERT(dump->current_buffer == &dump->global_buffer);
unsigned int *positions;
@@ -12149,7 +12986,10 @@ ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq)
(body->param.flags.has_block << 6) |
(body->param.flags.ambiguous_param0 << 7) |
(body->param.flags.accepts_no_kwarg << 8) |
- (body->param.flags.ruby2_keywords << 9);
+ (body->param.flags.ruby2_keywords << 9) |
+ (body->param.flags.anon_rest << 10) |
+ (body->param.flags.anon_kwrest << 11) |
+ (body->param.flags.use_block << 12);
#if IBF_ISEQ_ENABLE_LOCAL_BUFFER
# define IBF_BODY_OFFSET(x) (x)
@@ -12200,6 +13040,7 @@ ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq)
ibf_dump_write_small_value(dump, body->ci_size);
ibf_dump_write_small_value(dump, body->stack_max);
ibf_dump_write_small_value(dump, body->builtin_attrs);
+ ibf_dump_write_small_value(dump, body->prism ? 1 : 0);
#undef IBF_BODY_OFFSET
@@ -12312,6 +13153,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
const unsigned int ci_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
const unsigned int stack_max = (unsigned int)ibf_load_small_value(load, &reading_pos);
const unsigned int builtin_attrs = (unsigned int)ibf_load_small_value(load, &reading_pos);
+ const bool prism = (bool)ibf_load_small_value(load, &reading_pos);
// setup fname and dummy frame
VALUE path = ibf_load_object(load, location_pathobj_index);
@@ -12361,6 +13203,9 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
load_body->param.flags.ambiguous_param0 = (param_flags >> 7) & 1;
load_body->param.flags.accepts_no_kwarg = (param_flags >> 8) & 1;
load_body->param.flags.ruby2_keywords = (param_flags >> 9) & 1;
+ load_body->param.flags.anon_rest = (param_flags >> 10) & 1;
+ load_body->param.flags.anon_kwrest = (param_flags >> 11) & 1;
+ load_body->param.flags.use_block = (param_flags >> 12) & 1;
load_body->param.size = param_size;
load_body->param.lead_num = param_lead_num;
load_body->param.opt_num = param_opt_num;
@@ -12384,12 +13229,19 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
load_body->location.code_location.end_pos.lineno = location_code_location_end_pos_lineno;
load_body->location.code_location.end_pos.column = location_code_location_end_pos_column;
load_body->builtin_attrs = builtin_attrs;
+ load_body->prism = prism;
load_body->ivc_size = ivc_size;
load_body->icvarc_size = icvarc_size;
load_body->ise_size = ise_size;
load_body->ic_size = ic_size;
- load_body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, ISEQ_IS_SIZE(load_body));
+
+ if (ISEQ_IS_SIZE(load_body)) {
+ load_body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, ISEQ_IS_SIZE(load_body));
+ }
+ else {
+ load_body->is_entries = NULL;
+ }
ibf_load_ci_entries(load, ci_entries_offset, ci_size, &load_body->call_data);
load_body->outer_variables = ibf_load_outer_variables(load, outer_variables_offset);
load_body->param.opt_table = ibf_load_param_opt_table(load, param_opt_table_offset, param_opt_num);
@@ -12829,8 +13681,12 @@ ibf_load_object_bignum(const struct ibf_load *load, const struct ibf_object_head
const struct ibf_object_bignum *bignum = IBF_OBJBODY(struct ibf_object_bignum, offset);
int sign = bignum->slen > 0;
ssize_t len = sign > 0 ? bignum->slen : -1 * bignum->slen;
- VALUE obj = rb_integer_unpack(bignum->digits, len * 2, 2, 0,
- INTEGER_PACK_LITTLE_ENDIAN | (sign == 0 ? INTEGER_PACK_NEGATIVE : 0));
+ const int big_unpack_flags = /* c.f. rb_big_unpack() */
+ INTEGER_PACK_LSWORD_FIRST |
+ INTEGER_PACK_NATIVE_BYTE_ORDER;
+ VALUE obj = rb_integer_unpack(bignum->digits, len, sizeof(BDIGIT), 0,
+ big_unpack_flags |
+ (sign == 0 ? INTEGER_PACK_NEGATIVE : 0));
if (header->internal) rb_obj_hide(obj);
if (header->frozen) rb_obj_freeze(obj);
return obj;
@@ -13158,14 +14014,13 @@ ibf_dump_free(void *ptr)
st_free_table(dump->iseq_table);
dump->iseq_table = 0;
}
- ruby_xfree(dump);
}
static size_t
ibf_dump_memsize(const void *ptr)
{
struct ibf_dump *dump = (struct ibf_dump *)ptr;
- size_t size = sizeof(*dump);
+ size_t size = 0;
if (dump->iseq_table) size += st_memsize(dump->iseq_table);
if (dump->global_buffer.obj_table) size += st_memsize(dump->global_buffer.obj_table);
return size;
@@ -13174,7 +14029,7 @@ ibf_dump_memsize(const void *ptr)
static const rb_data_type_t ibf_dump_type = {
"ibf_dump",
{ibf_dump_mark, ibf_dump_free, ibf_dump_memsize,},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_EMBEDDABLE
};
static void
@@ -13210,7 +14065,6 @@ rb_iseq_ibf_dump(const rb_iseq_t *iseq, VALUE opt)
ibf_dump_setup(dump, dump_obj);
ibf_dump_write(dump, &header, sizeof(header));
- ibf_dump_write(dump, RUBY_PLATFORM, strlen(RUBY_PLATFORM) + 1);
ibf_dump_iseq(dump, iseq);
header.magic[0] = 'Y'; /* YARB */
@@ -13219,6 +14073,8 @@ rb_iseq_ibf_dump(const rb_iseq_t *iseq, VALUE opt)
header.magic[3] = 'B';
header.major_version = IBF_MAJOR_VERSION;
header.minor_version = IBF_MINOR_VERSION;
+ header.endian = IBF_ENDIAN_MARK;
+ header.wordsize = (uint8_t)SIZEOF_VALUE;
ibf_dump_iseq_list(dump, &header);
ibf_dump_object_list(dump, &header.global_object_list_offset, &header.global_object_list_size);
header.size = ibf_dump_pos(dump);
@@ -13236,8 +14092,6 @@ rb_iseq_ibf_dump(const rb_iseq_t *iseq, VALUE opt)
ibf_dump_overwrite(dump, &header, sizeof(header), 0);
str = dump->global_buffer.str;
- ibf_dump_free(dump);
- DATA_PTR(dump_obj) = NULL;
RB_GC_GUARD(dump_obj);
return str;
}
@@ -13311,16 +14165,12 @@ ibf_load_iseq(const struct ibf_load *load, const rb_iseq_t *index_iseq)
#endif
pinned_list_store(load->iseq_list, iseq_index, (VALUE)iseq);
-#if !USE_LAZY_LOAD
+ if (!USE_LAZY_LOAD || GET_VM()->builtin_function_table) {
#if IBF_ISEQ_DEBUG
- fprintf(stderr, "ibf_load_iseq: loading iseq=%p\n", (void *)iseq);
+ fprintf(stderr, "ibf_load_iseq: loading iseq=%p\n", (void *)iseq);
#endif
- rb_ibf_load_iseq_complete(iseq);
-#else
- if (GET_VM()->builtin_function_table) {
rb_ibf_load_iseq_complete(iseq);
}
-#endif /* !USE_LAZY_LOAD */
#if IBF_ISEQ_DEBUG
fprintf(stderr, "ibf_load_iseq: iseq=%p loaded %p\n",
@@ -13334,35 +14184,39 @@ ibf_load_iseq(const struct ibf_load *load, const rb_iseq_t *index_iseq)
static void
ibf_load_setup_bytes(struct ibf_load *load, VALUE loader_obj, const char *bytes, size_t size)
{
+ struct ibf_header *header = (struct ibf_header *)bytes;
load->loader_obj = loader_obj;
load->global_buffer.buff = bytes;
- load->header = (struct ibf_header *)load->global_buffer.buff;
- load->global_buffer.size = load->header->size;
- load->global_buffer.obj_list_offset = load->header->global_object_list_offset;
- load->global_buffer.obj_list_size = load->header->global_object_list_size;
- RB_OBJ_WRITE(loader_obj, &load->iseq_list, pinned_list_new(load->header->iseq_list_size));
+ load->header = header;
+ load->global_buffer.size = header->size;
+ load->global_buffer.obj_list_offset = header->global_object_list_offset;
+ load->global_buffer.obj_list_size = header->global_object_list_size;
+ RB_OBJ_WRITE(loader_obj, &load->iseq_list, pinned_list_new(header->iseq_list_size));
RB_OBJ_WRITE(loader_obj, &load->global_buffer.obj_list, pinned_list_new(load->global_buffer.obj_list_size));
load->iseq = NULL;
load->current_buffer = &load->global_buffer;
- if (size < load->header->size) {
+ if (size < header->size) {
rb_raise(rb_eRuntimeError, "broken binary format");
}
- if (strncmp(load->header->magic, "YARB", 4) != 0) {
+ if (strncmp(header->magic, "YARB", 4) != 0) {
rb_raise(rb_eRuntimeError, "unknown binary format");
}
- if (load->header->major_version != IBF_MAJOR_VERSION ||
- load->header->minor_version != IBF_MINOR_VERSION) {
+ if (header->major_version != IBF_MAJOR_VERSION ||
+ header->minor_version != IBF_MINOR_VERSION) {
rb_raise(rb_eRuntimeError, "unmatched version file (%u.%u for %u.%u)",
- load->header->major_version, load->header->minor_version, IBF_MAJOR_VERSION, IBF_MINOR_VERSION);
+ header->major_version, header->minor_version, IBF_MAJOR_VERSION, IBF_MINOR_VERSION);
+ }
+ if (header->endian != IBF_ENDIAN_MARK) {
+ rb_raise(rb_eRuntimeError, "unmatched endian: %c", header->endian);
}
- if (strcmp(load->global_buffer.buff + sizeof(struct ibf_header), RUBY_PLATFORM) != 0) {
- rb_raise(rb_eRuntimeError, "unmatched platform");
+ if (header->wordsize != SIZEOF_VALUE) {
+ rb_raise(rb_eRuntimeError, "unmatched word size: %d", header->wordsize);
}
- if (load->header->iseq_list_offset % RUBY_ALIGNOF(ibf_offset_t)) {
+ if (header->iseq_list_offset % RUBY_ALIGNOF(ibf_offset_t)) {
rb_raise(rb_eArgError, "unaligned iseq list offset: %u",
- load->header->iseq_list_offset);
+ header->iseq_list_offset);
}
if (load->global_buffer.obj_list_offset % RUBY_ALIGNOF(ibf_offset_t)) {
rb_raise(rb_eArgError, "unaligned object list offset: %u",
@@ -13373,13 +14227,15 @@ ibf_load_setup_bytes(struct ibf_load *load, VALUE loader_obj, const char *bytes,
static void
ibf_load_setup(struct ibf_load *load, VALUE loader_obj, VALUE str)
{
+ StringValue(str);
+
if (RSTRING_LENINT(str) < (int)sizeof(struct ibf_header)) {
rb_raise(rb_eRuntimeError, "broken binary format");
}
-#if USE_LAZY_LOAD
- str = rb_str_new(RSTRING_PTR(str), RSTRING_LEN(str));
-#endif
+ if (USE_LAZY_LOAD) {
+ str = rb_str_new(RSTRING_PTR(str), RSTRING_LEN(str));
+ }
ibf_load_setup_bytes(load, loader_obj, StringValuePtr(str), RSTRING_LEN(str));
RB_OBJ_WRITE(loader_obj, &load->str, str);
@@ -13453,3 +14309,5 @@ rb_iseq_ibf_load_extra_data(VALUE str)
RB_GC_GUARD(loader_obj);
return extra_str;
}
+
+#include "prism_compile.c"
diff --git a/complex.c b/complex.c
index c9272778bb..ff278f80fa 100644
--- a/complex.c
+++ b/complex.c
@@ -24,6 +24,7 @@
#include "internal/numeric.h"
#include "internal/object.h"
#include "internal/rational.h"
+#include "internal/string.h"
#include "ruby_assert.h"
#define ZERO INT2FIX(0)
@@ -396,7 +397,7 @@ nucomp_s_new_internal(VALUE klass, VALUE real, VALUE imag)
RCOMPLEX_SET_REAL(obj, real);
RCOMPLEX_SET_IMAG(obj, imag);
- OBJ_FREEZE_RAW((VALUE)obj);
+ OBJ_FREEZE((VALUE)obj);
return (VALUE)obj;
}
@@ -410,15 +411,15 @@ nucomp_s_alloc(VALUE klass)
inline static VALUE
f_complex_new_bang1(VALUE klass, VALUE x)
{
- assert(!RB_TYPE_P(x, T_COMPLEX));
+ RUBY_ASSERT(!RB_TYPE_P(x, T_COMPLEX));
return nucomp_s_new_internal(klass, x, ZERO);
}
inline static VALUE
f_complex_new_bang2(VALUE klass, VALUE x, VALUE y)
{
- assert(!RB_TYPE_P(x, T_COMPLEX));
- assert(!RB_TYPE_P(y, T_COMPLEX));
+ RUBY_ASSERT(!RB_TYPE_P(x, T_COMPLEX));
+ RUBY_ASSERT(!RB_TYPE_P(y, T_COMPLEX));
return nucomp_s_new_internal(klass, x, y);
}
@@ -431,7 +432,7 @@ nucomp_real_check(VALUE num)
!RB_TYPE_P(num, T_RATIONAL)) {
if (RB_TYPE_P(num, T_COMPLEX) && nucomp_real_p(num)) {
VALUE real = RCOMPLEX(num)->real;
- assert(!RB_TYPE_P(real, T_COMPLEX));
+ RUBY_ASSERT(!RB_TYPE_P(real, T_COMPLEX));
return real;
}
if (!k_numeric_p(num) || !f_real_p(num))
@@ -474,12 +475,19 @@ nucomp_s_canonicalize_internal(VALUE klass, VALUE real, VALUE imag)
/*
* call-seq:
- * Complex.rect(real[, imag]) -> complex
- * Complex.rectangular(real[, imag]) -> complex
+ * Complex.rect(real, imag = 0) -> complex
*
- * Returns a complex object which denotes the given rectangular form.
+ * Returns a new \Complex object formed from the arguments,
+ * each of which must be an instance of Numeric,
+ * or an instance of one of its subclasses:
+ * \Complex, Float, Integer, Rational;
+ * see {Rectangular Coordinates}[rdoc-ref:Complex@Rectangular+Coordinates]:
*
- * Complex.rectangular(1, 2) #=> (1+2i)
+ * Complex.rect(3) # => (3+0i)
+ * Complex.rect(3, Math::PI) # => (3+3.141592653589793i)
+ * Complex.rect(-3, -Math::PI) # => (-3-3.141592653589793i)
+ *
+ * \Complex.rectangular is an alias for \Complex.rect.
*/
static VALUE
nucomp_s_new(int argc, VALUE *argv, VALUE klass)
@@ -516,39 +524,54 @@ static VALUE nucomp_s_convert(int argc, VALUE *argv, VALUE klass);
/*
* call-seq:
- * Complex(x[, y], exception: true) -> numeric or nil
- *
- * Returns x+i*y;
- *
- * Complex(1, 2) #=> (1+2i)
- * Complex('1+2i') #=> (1+2i)
- * Complex(nil) #=> TypeError
- * Complex(1, nil) #=> TypeError
- *
- * Complex(1, nil, exception: false) #=> nil
- * Complex('1+2', exception: false) #=> nil
- *
- * Syntax of string form:
- *
- * string form = extra spaces , complex , extra spaces ;
- * complex = real part | [ sign ] , imaginary part
- * | real part , sign , imaginary part
- * | rational , "@" , rational ;
- * real part = rational ;
- * imaginary part = imaginary unit | unsigned rational , imaginary unit ;
- * rational = [ sign ] , unsigned rational ;
- * unsigned rational = numerator | numerator , "/" , denominator ;
- * numerator = integer part | fractional part | integer part , fractional part ;
- * denominator = digits ;
- * integer part = digits ;
- * fractional part = "." , digits , [ ( "e" | "E" ) , [ sign ] , digits ] ;
- * imaginary unit = "i" | "I" | "j" | "J" ;
- * sign = "-" | "+" ;
- * digits = digit , { digit | "_" , digit };
- * digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
- * extra spaces = ? \s* ? ;
- *
- * See String#to_c.
+ * Complex(real, imag = 0, exception: true) -> complex or nil
+ * Complex(s, exception: true) -> complex or nil
+ *
+ * Returns a new \Complex object if the arguments are valid;
+ * otherwise raises an exception if +exception+ is +true+;
+ * otherwise returns +nil+.
+ *
+ * With Numeric arguments +real+ and +imag+,
+ * returns <tt>Complex.rect(real, imag)</tt> if the arguments are valid.
+ *
+ * With string argument +s+, returns a new \Complex object if the argument is valid;
+ * the string may have:
+ *
+ * - One or two numeric substrings,
+ * each of which specifies a Complex, Float, Integer, Numeric, or Rational value,
+ * specifying {rectangular coordinates}[rdoc-ref:Complex@Rectangular+Coordinates]:
+ *
+ * - Sign-separated real and imaginary numeric substrings
+ * (with trailing character <tt>'i'</tt>):
+ *
+ * Complex('1+2i') # => (1+2i)
+ * Complex('+1+2i') # => (1+2i)
+ * Complex('+1-2i') # => (1-2i)
+ * Complex('-1+2i') # => (-1+2i)
+ * Complex('-1-2i') # => (-1-2i)
+ *
+ * - Real-only numeric string (without trailing character <tt>'i'</tt>):
+ *
+ * Complex('1') # => (1+0i)
+ * Complex('+1') # => (1+0i)
+ * Complex('-1') # => (-1+0i)
+ *
+ * - Imaginary-only numeric string (with trailing character <tt>'i'</tt>):
+ *
+ * Complex('1i') # => (0+1i)
+ * Complex('+1i') # => (0+1i)
+ * Complex('-1i') # => (0-1i)
+ *
+ * - At-sign separated real and imaginary rational substrings,
+ * each of which specifies a Rational value,
+ * specifying {polar coordinates}[rdoc-ref:Complex@Polar+Coordinates]:
+ *
+ * Complex('1/2@3/4') # => (0.36584443443691045+0.34081938001166706i)
+ * Complex('+1/2@+3/4') # => (0.36584443443691045+0.34081938001166706i)
+ * Complex('+1/2@-3/4') # => (0.36584443443691045-0.34081938001166706i)
+ * Complex('-1/2@+3/4') # => (-0.36584443443691045-0.34081938001166706i)
+ * Complex('-1/2@-3/4') # => (-0.36584443443691045+0.34081938001166706i)
+ *
*/
static VALUE
nucomp_f_complex(int argc, VALUE *argv, VALUE klass)
@@ -698,14 +721,19 @@ rb_dbl_complex_new_polar_pi(double abs, double ang)
/*
* call-seq:
- * Complex.polar(abs[, arg]) -> complex
+ * Complex.polar(abs, arg = 0) -> complex
*
- * Returns a complex object which denotes the given polar form.
+ * Returns a new \Complex object formed from the arguments,
+ * each of which must be an instance of Numeric,
+ * or an instance of one of its subclasses:
+ * \Complex, Float, Integer, Rational.
+ * Argument +arg+ is given in radians;
+ * see {Polar Coordinates}[rdoc-ref:Complex@Polar+Coordinates]:
+ *
+ * Complex.polar(3) # => (3+0i)
+ * Complex.polar(3, 2.0) # => (-1.2484405096414273+2.727892280477045i)
+ * Complex.polar(-3, -2.0) # => (1.2484405096414273+2.727892280477045i)
*
- * Complex.polar(3, 0) #=> (3.0+0.0i)
- * Complex.polar(3, Math::PI/2) #=> (1.836909530733566e-16+3.0i)
- * Complex.polar(3, Math::PI) #=> (-3.0+3.673819061467132e-16i)
- * Complex.polar(3, -Math::PI/2) #=> (1.836909530733566e-16-3.0i)
*/
static VALUE
nucomp_s_polar(int argc, VALUE *argv, VALUE klass)
@@ -725,12 +753,19 @@ nucomp_s_polar(int argc, VALUE *argv, VALUE klass)
/*
* call-seq:
- * cmp.real -> real
+ * real -> numeric
+ *
+ * Returns the real value for +self+:
+ *
+ * Complex.rect(7).real # => 7
+ * Complex.rect(9, -4).real # => 9
+ *
+ * If +self+ was created with
+ * {polar coordinates}[rdoc-ref:Complex@Polar+Coordinates], the returned value
+ * is computed, and may be inexact:
*
- * Returns the real part.
+ * Complex.polar(1, Math::PI/4).real # => 0.7071067811865476 # Square root of 2.
*
- * Complex(7).real #=> 7
- * Complex(9, -4).real #=> 9
*/
VALUE
rb_complex_real(VALUE self)
@@ -741,13 +776,19 @@ rb_complex_real(VALUE self)
/*
* call-seq:
- * cmp.imag -> real
- * cmp.imaginary -> real
+ * imag -> numeric
*
- * Returns the imaginary part.
+ * Returns the imaginary value for +self+:
+ *
+ * Complex.rect(7).imag # => 0
+ * Complex.rect(9, -4).imag # => -4
+ *
+ * If +self+ was created with
+ * {polar coordinates}[rdoc-ref:Complex@Polar+Coordinates], the returned value
+ * is computed, and may be inexact:
+ *
+ * Complex.polar(1, Math::PI/4).imag # => 0.7071067811865476 # Square root of 2.
*
- * Complex(7).imaginary #=> 0
- * Complex(9, -4).imaginary #=> -4
*/
VALUE
rb_complex_imag(VALUE self)
@@ -758,11 +799,13 @@ rb_complex_imag(VALUE self)
/*
* call-seq:
- * -cmp -> complex
+ * -complex -> new_complex
*
- * Returns negation of the value.
+ * Returns the negation of +self+, which is the negation of each of its parts:
+ *
+ * -Complex.rect(1, 2) # => (-1-2i)
+ * -Complex.rect(-1, -2) # => (1+2i)
*
- * -Complex(1, 2) #=> (-1-2i)
*/
VALUE
rb_complex_uminus(VALUE self)
@@ -774,15 +817,16 @@ rb_complex_uminus(VALUE self)
/*
* call-seq:
- * cmp + numeric -> complex
+ * complex + numeric -> new_complex
+ *
+ * Returns the sum of +self+ and +numeric+:
*
- * Performs addition.
+ * Complex.rect(2, 3) + Complex.rect(2, 3) # => (4+6i)
+ * Complex.rect(900) + Complex.rect(1) # => (901+0i)
+ * Complex.rect(-2, 9) + Complex.rect(-9, 2) # => (-11+11i)
+ * Complex.rect(9, 8) + 4 # => (13+8i)
+ * Complex.rect(20, 9) + 9.8 # => (29.8+9i)
*
- * Complex(2, 3) + Complex(2, 3) #=> (4+6i)
- * Complex(900) + Complex(1) #=> (901+0i)
- * Complex(-2, 9) + Complex(-9, 2) #=> (-11+11i)
- * Complex(9, 8) + 4 #=> (13+8i)
- * Complex(20, 9) + 9.8 #=> (29.8+9i)
*/
VALUE
rb_complex_plus(VALUE self, VALUE other)
@@ -808,15 +852,16 @@ rb_complex_plus(VALUE self, VALUE other)
/*
* call-seq:
- * cmp - numeric -> complex
+ * complex - numeric -> new_complex
*
- * Performs subtraction.
+ * Returns the difference of +self+ and +numeric+:
+ *
+ * Complex.rect(2, 3) - Complex.rect(2, 3) # => (0+0i)
+ * Complex.rect(900) - Complex.rect(1) # => (899+0i)
+ * Complex.rect(-2, 9) - Complex.rect(-9, 2) # => (7+7i)
+ * Complex.rect(9, 8) - 4 # => (5+8i)
+ * Complex.rect(20, 9) - 9.8 # => (10.2+9i)
*
- * Complex(2, 3) - Complex(2, 3) #=> (0+0i)
- * Complex(900) - Complex(1) #=> (899+0i)
- * Complex(-2, 9) - Complex(-9, 2) #=> (7+7i)
- * Complex(9, 8) - 4 #=> (5+8i)
- * Complex(20, 9) - 9.8 #=> (10.2+9i)
*/
VALUE
rb_complex_minus(VALUE self, VALUE other)
@@ -868,15 +913,16 @@ comp_mul(VALUE areal, VALUE aimag, VALUE breal, VALUE bimag, VALUE *real, VALUE
/*
* call-seq:
- * cmp * numeric -> complex
+ * complex * numeric -> new_complex
+ *
+ * Returns the product of +self+ and +numeric+:
*
- * Performs multiplication.
+ * Complex.rect(2, 3) * Complex.rect(2, 3) # => (-5+12i)
+ * Complex.rect(900) * Complex.rect(1) # => (900+0i)
+ * Complex.rect(-2, 9) * Complex.rect(-9, 2) # => (0-85i)
+ * Complex.rect(9, 8) * 4 # => (36+32i)
+ * Complex.rect(20, 9) * 9.8 # => (196.0+88.2i)
*
- * Complex(2, 3) * Complex(2, 3) #=> (-5+12i)
- * Complex(900) * Complex(1) #=> (900+0i)
- * Complex(-2, 9) * Complex(-9, 2) #=> (0-85i)
- * Complex(9, 8) * 4 #=> (36+32i)
- * Complex(20, 9) * 9.8 #=> (196.0+88.2i)
*/
VALUE
rb_complex_mul(VALUE self, VALUE other)
@@ -943,16 +989,16 @@ f_divide(VALUE self, VALUE other,
/*
* call-seq:
- * cmp / numeric -> complex
- * cmp.quo(numeric) -> complex
+ * complex / numeric -> new_complex
+ *
+ * Returns the quotient of +self+ and +numeric+:
*
- * Performs division.
+ * Complex.rect(2, 3) / Complex.rect(2, 3) # => (1+0i)
+ * Complex.rect(900) / Complex.rect(1) # => (900+0i)
+ * Complex.rect(-2, 9) / Complex.rect(-9, 2) # => ((36/85)-(77/85)*i)
+ * Complex.rect(9, 8) / 4 # => ((9/4)+2i)
+ * Complex.rect(20, 9) / 9.8 # => (2.0408163265306123+0.9183673469387754i)
*
- * Complex(2, 3) / Complex(2, 3) #=> ((1/1)+(0/1)*i)
- * Complex(900) / Complex(1) #=> ((900/1)+(0/1)*i)
- * Complex(-2, 9) / Complex(-9, 2) #=> ((36/85)-(77/85)*i)
- * Complex(9, 8) / 4 #=> ((9/4)+(2/1)*i)
- * Complex(20, 9) / 9.8 #=> (2.0408163265306123+0.9183673469387754i)
*/
VALUE
rb_complex_div(VALUE self, VALUE other)
@@ -964,11 +1010,12 @@ rb_complex_div(VALUE self, VALUE other)
/*
* call-seq:
- * cmp.fdiv(numeric) -> complex
+ * fdiv(numeric) -> new_complex
*
- * Performs division as each part is a float, never returns a float.
+ * Returns <tt>Complex.rect(self.real/numeric, self.imag/numeric)</tt>:
+ *
+ * Complex.rect(11, 22).fdiv(3) # => (3.6666666666666665+7.333333333333333i)
*
- * Complex(11, 22).fdiv(3) #=> (3.6666666666666665+7.333333333333333i)
*/
static VALUE
nucomp_fdiv(VALUE self, VALUE other)
@@ -982,14 +1029,95 @@ f_reciprocal(VALUE x)
return f_quo(ONE, x);
}
+static VALUE
+zero_for(VALUE x)
+{
+ if (RB_FLOAT_TYPE_P(x))
+ return DBL2NUM(0);
+ if (RB_TYPE_P(x, T_RATIONAL))
+ return rb_rational_new(INT2FIX(0), INT2FIX(1));
+
+ return INT2FIX(0);
+}
+
+static VALUE
+complex_pow_for_special_angle(VALUE self, VALUE other)
+{
+ if (!rb_integer_type_p(other)) {
+ return Qundef;
+ }
+
+ get_dat1(self);
+ VALUE x = Qundef;
+ int dir;
+ if (f_zero_p(dat->imag)) {
+ x = dat->real;
+ dir = 0;
+ }
+ else if (f_zero_p(dat->real)) {
+ x = dat->imag;
+ dir = 2;
+ }
+ else if (f_eqeq_p(dat->real, dat->imag)) {
+ x = dat->real;
+ dir = 1;
+ }
+ else if (f_eqeq_p(dat->real, f_negate(dat->imag))) {
+ x = dat->imag;
+ dir = 3;
+ } else {
+ dir = 0;
+ }
+
+ if (UNDEF_P(x)) return x;
+
+ if (f_negative_p(x)) {
+ x = f_negate(x);
+ dir += 4;
+ }
+
+ VALUE zx;
+ if (dir % 2 == 0) {
+ zx = rb_num_pow(x, other);
+ }
+ else {
+ zx = rb_num_pow(
+ rb_funcall(rb_int_mul(TWO, x), '*', 1, x),
+ rb_int_div(other, TWO)
+ );
+ if (rb_int_odd_p(other)) {
+ zx = rb_funcall(zx, '*', 1, x);
+ }
+ }
+ static const int dirs[][2] = {
+ {1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}, {-1, -1}, {0, -1}, {1, -1}
+ };
+ int z_dir = FIX2INT(rb_int_modulo(rb_int_mul(INT2FIX(dir), other), INT2FIX(8)));
+
+ VALUE zr = Qfalse, zi = Qfalse;
+ switch (dirs[z_dir][0]) {
+ case 0: zr = zero_for(zx); break;
+ case 1: zr = zx; break;
+ case -1: zr = f_negate(zx); break;
+ }
+ switch (dirs[z_dir][1]) {
+ case 0: zi = zero_for(zx); break;
+ case 1: zi = zx; break;
+ case -1: zi = f_negate(zx); break;
+ }
+ return nucomp_s_new_internal(CLASS_OF(self), zr, zi);
+}
+
+
/*
* call-seq:
- * cmp ** numeric -> complex
+ * complex ** numeric -> new_complex
+ *
+ * Returns +self+ raised to power +numeric+:
*
- * Performs exponentiation.
+ * Complex.rect(0, 1) ** 2 # => (-1+0i)
+ * Complex.rect(-8) ** Rational(1, 3) # => (1.0000000000000002+1.7320508075688772i)
*
- * Complex('i') ** 2 #=> (-1+0i)
- * Complex(-8) ** Rational(1, 3) #=> (1.0000000000000002+1.7320508075688772i)
*/
VALUE
rb_complex_pow(VALUE self, VALUE other)
@@ -1007,6 +1135,14 @@ rb_complex_pow(VALUE self, VALUE other)
other = dat->real; /* c14n */
}
+ if (other == ONE) {
+ get_dat1(self);
+ return nucomp_s_new_internal(CLASS_OF(self), dat->real, dat->imag);
+ }
+
+ VALUE result = complex_pow_for_special_angle(self, other);
+ if (!UNDEF_P(result)) return result;
+
if (RB_TYPE_P(other, T_COMPLEX)) {
VALUE r, theta, nr, ntheta;
@@ -1079,15 +1215,13 @@ rb_complex_pow(VALUE self, VALUE other)
/*
* call-seq:
- * cmp == object -> true or false
+ * complex == object -> true or false
+ *
+ * Returns +true+ if <tt>self.real == object.real</tt>
+ * and <tt>self.imag == object.imag</tt>:
*
- * Returns true if cmp equals object numerically.
+ * Complex.rect(2, 3) == Complex.rect(2.0, 3.0) # => true
*
- * Complex(2, 3) == Complex(2, 3) #=> true
- * Complex(5) == 5 #=> true
- * Complex(0) == 0.0 #=> true
- * Complex('1/3') == 0.33 #=> false
- * Complex('1/2') == '1/2' #=> false
*/
static VALUE
nucomp_eqeq_p(VALUE self, VALUE other)
@@ -1115,17 +1249,26 @@ nucomp_real_p(VALUE self)
/*
* call-seq:
- * cmp <=> object -> 0, 1, -1, or nil
+ * complex <=> object -> -1, 0, 1, or nil
*
- * If +cmp+'s imaginary part is zero, and +object+ is also a
- * real number (or a Complex number where the imaginary part is zero),
- * compare the real part of +cmp+ to object. Otherwise, return nil.
+ * Returns:
+ *
+ * - <tt>self.real <=> object.real</tt> if both of the following are true:
+ *
+ * - <tt>self.imag == 0</tt>.
+ * - <tt>object.imag == 0</tt>. # Always true if object is numeric but not complex.
+ *
+ * - +nil+ otherwise.
+ *
+ * Examples:
+ *
+ * Complex.rect(2) <=> 3 # => -1
+ * Complex.rect(2) <=> 2 # => 0
+ * Complex.rect(2) <=> 1 # => 1
+ * Complex.rect(2, 1) <=> 1 # => nil # self.imag not zero.
+ * Complex.rect(1) <=> Complex.rect(1, 1) # => nil # object.imag not zero.
+ * Complex.rect(1) <=> 'Foo' # => nil # object.imag not defined.
*
- * Complex(2, 3) <=> Complex(2, 3) #=> nil
- * Complex(2, 3) <=> 1 #=> nil
- * Complex(2) <=> 1 #=> 1
- * Complex(2) <=> 2 #=> 0
- * Complex(2) <=> 3 #=> -1
*/
static VALUE
nucomp_cmp(VALUE self, VALUE other)
@@ -1170,13 +1313,19 @@ nucomp_coerce(VALUE self, VALUE other)
/*
* call-seq:
- * cmp.abs -> real
- * cmp.magnitude -> real
+ * abs -> float
*
- * Returns the absolute part of its polar form.
+ * Returns the absolute value (magnitude) for +self+;
+ * see {polar coordinates}[rdoc-ref:Complex@Polar+Coordinates]:
+ *
+ * Complex.polar(-1, 0).abs # => 1.0
+ *
+ * If +self+ was created with
+ * {rectangular coordinates}[rdoc-ref:Complex@Rectangular+Coordinates], the returned value
+ * is computed, and may be inexact:
+ *
+ * Complex.rectangular(1, 1).abs # => 1.4142135623730951 # The square root of 2.
*
- * Complex(-1).abs #=> 1
- * Complex(3.0, -4.0).abs #=> 5.0
*/
VALUE
rb_complex_abs(VALUE self)
@@ -1200,12 +1349,19 @@ rb_complex_abs(VALUE self)
/*
* call-seq:
- * cmp.abs2 -> real
+ * abs2 -> float
+ *
+ * Returns square of the absolute value (magnitude) for +self+;
+ * see {polar coordinates}[rdoc-ref:Complex@Polar+Coordinates]:
*
- * Returns square of the absolute value.
+ * Complex.polar(2, 2).abs2 # => 4.0
+ *
+ * If +self+ was created with
+ * {rectangular coordinates}[rdoc-ref:Complex@Rectangular+Coordinates], the returned value
+ * is computed, and may be inexact:
+ *
+ * Complex.rectangular(1.0/3, 1.0/3).abs2 # => 0.2222222222222222
*
- * Complex(-1).abs2 #=> 1
- * Complex(3.0, -4.0).abs2 #=> 25.0
*/
static VALUE
nucomp_abs2(VALUE self)
@@ -1217,13 +1373,19 @@ nucomp_abs2(VALUE self)
/*
* call-seq:
- * cmp.arg -> float
- * cmp.angle -> float
- * cmp.phase -> float
+ * arg -> float
+ *
+ * Returns the argument (angle) for +self+ in radians;
+ * see {polar coordinates}[rdoc-ref:Complex@Polar+Coordinates]:
+ *
+ * Complex.polar(3, Math::PI/2).arg # => 1.57079632679489660
+ *
+ * If +self+ was created with
+ * {rectangular coordinates}[rdoc-ref:Complex@Rectangular+Coordinates], the returned value
+ * is computed, and may be inexact:
*
- * Returns the angle part of its polar form.
+ * Complex.polar(1, 1.0/3).arg # => 0.33333333333333326
*
- * Complex.polar(3, Math::PI/2).arg #=> 1.5707963267948966
*/
VALUE
rb_complex_arg(VALUE self)
@@ -1234,12 +1396,22 @@ rb_complex_arg(VALUE self)
/*
* call-seq:
- * cmp.rect -> array
- * cmp.rectangular -> array
+ * rect -> array
+ *
+ * Returns the array <tt>[self.real, self.imag]</tt>:
+ *
+ * Complex.rect(1, 2).rect # => [1, 2]
+ *
+ * See {Rectangular Coordinates}[rdoc-ref:Complex@Rectangular+Coordinates].
+ *
+ * If +self+ was created with
+ * {polar coordinates}[rdoc-ref:Complex@Polar+Coordinates], the returned value
+ * is computed, and may be inexact:
+ *
+ * Complex.polar(1.0, 1.0).rect # => [0.5403023058681398, 0.8414709848078965]
*
- * Returns an array; [cmp.real, cmp.imag].
*
- * Complex(1, 2).rectangular #=> [1, 2]
+ * Complex#rectangular is an alias for Complex#rect.
*/
static VALUE
nucomp_rect(VALUE self)
@@ -1250,11 +1422,20 @@ nucomp_rect(VALUE self)
/*
* call-seq:
- * cmp.polar -> array
+ * polar -> array
*
- * Returns an array; [cmp.abs, cmp.arg].
+ * Returns the array <tt>[self.abs, self.arg]</tt>:
+ *
+ * Complex.polar(1, 2).polar # => [1.0, 2.0]
+ *
+ * See {Polar Coordinates}[rdoc-ref:Complex@Polar+Coordinates].
+ *
+ * If +self+ was created with
+ * {rectangular coordinates}[rdoc-ref:Complex@Rectangular+Coordinates], the returned value
+ * is computed, and may be inexact:
+ *
+ * Complex.rect(1, 1).polar # => [1.4142135623730951, 0.7853981633974483]
*
- * Complex(1, 2).polar #=> [2.23606797749979, 1.1071487177940904]
*/
static VALUE
nucomp_polar(VALUE self)
@@ -1264,12 +1445,12 @@ nucomp_polar(VALUE self)
/*
* call-seq:
- * cmp.conj -> complex
- * cmp.conjugate -> complex
+ * conj -> complex
+ *
+ * Returns the conjugate of +self+, <tt>Complex.rect(self.imag, self.real)</tt>:
*
- * Returns the complex conjugate.
+ * Complex.rect(1, 2).conj # => (1-2i)
*
- * Complex(1, 2).conjugate #=> (1-2i)
*/
VALUE
rb_complex_conjugate(VALUE self)
@@ -1280,10 +1461,9 @@ rb_complex_conjugate(VALUE self)
/*
* call-seq:
- * Complex(1).real? -> false
- * Complex(1, 2).real? -> false
+ * real? -> false
*
- * Returns false, even if the complex number has no imaginary part.
+ * Returns +false+; for compatibility with Numeric#real?.
*/
static VALUE
nucomp_real_p_m(VALUE self)
@@ -1293,11 +1473,17 @@ nucomp_real_p_m(VALUE self)
/*
* call-seq:
- * cmp.denominator -> integer
+ * denominator -> integer
*
- * Returns the denominator (lcm of both denominator - real and imag).
+ * Returns the denominator of +self+, which is
+ * the {least common multiple}[https://en.wikipedia.org/wiki/Least_common_multiple]
+ * of <tt>self.real.denominator</tt> and <tt>self.imag.denominator</tt>:
*
- * See numerator.
+ * Complex.rect(Rational(1, 2), Rational(2, 3)).denominator # => 6
+ *
+ * Note that <tt>n.denominator</tt> of a non-rational numeric is +1+.
+ *
+ * Related: Complex#numerator.
*/
static VALUE
nucomp_denominator(VALUE self)
@@ -1308,21 +1494,23 @@ nucomp_denominator(VALUE self)
/*
* call-seq:
- * cmp.numerator -> numeric
+ * numerator -> new_complex
*
- * Returns the numerator.
+ * Returns the \Complex object created from the numerators
+ * of the real and imaginary parts of +self+,
+ * after converting each part to the
+ * {lowest common denominator}[https://en.wikipedia.org/wiki/Lowest_common_denominator]
+ * of the two:
*
- * 1 2 3+4i <- numerator
- * - + -i -> ----
- * 2 3 6 <- denominator
+ * c = Complex.rect(Rational(2, 3), Rational(3, 4)) # => ((2/3)+(3/4)*i)
+ * c.numerator # => (8+9i)
*
- * c = Complex('1/2+2/3i') #=> ((1/2)+(2/3)*i)
- * n = c.numerator #=> (3+4i)
- * d = c.denominator #=> 6
- * n / d #=> ((1/2)+(2/3)*i)
- * Complex(Rational(n.real, d), Rational(n.imag, d))
- * #=> ((1/2)+(2/3)*i)
- * See denominator.
+ * In this example, the lowest common denominator of the two parts is 12;
+ * the two converted parts may be thought of as \Rational(8, 12) and \Rational(9, 12),
+ * whose numerators, respectively, are 8 and 9;
+ * so the returned value of <tt>c.numerator</tt> is <tt>Complex.rect(8, 9)</tt>.
+ *
+ * Related: Complex#denominator.
*/
static VALUE
nucomp_numerator(VALUE self)
@@ -1355,6 +1543,18 @@ rb_complex_hash(VALUE self)
return v;
}
+/*
+ * :call-seq:
+ * hash -> integer
+ *
+ * Returns the integer hash value for +self+.
+ *
+ * Two \Complex objects created from the same values will have the same hash value
+ * (and will compare using #eql?):
+ *
+ * Complex.rect(1, 2).hash == Complex.rect(1, 2).hash # => true
+ *
+ */
static VALUE
nucomp_hash(VALUE self)
{
@@ -1415,15 +1615,16 @@ f_format(VALUE self, VALUE (*func)(VALUE))
/*
* call-seq:
- * cmp.to_s -> string
+ * to_s -> string
*
- * Returns the value as a string.
+ * Returns a string representation of +self+:
+ *
+ * Complex.rect(2).to_s # => "2+0i"
+ * Complex.rect(-8, 6).to_s # => "-8+6i"
+ * Complex.rect(0, Rational(1, 2)).to_s # => "0+1/2i"
+ * Complex.rect(0, Float::INFINITY).to_s # => "0+Infinity*i"
+ * Complex.rect(Float::NAN, Float::NAN).to_s # => "NaN+NaN*i"
*
- * Complex(2).to_s #=> "2+0i"
- * Complex('-8/6').to_s #=> "-4/3+0i"
- * Complex('1/2i').to_s #=> "0+1/2i"
- * Complex(0, Float::INFINITY).to_s #=> "0+Infinity*i"
- * Complex(Float::NAN, Float::NAN).to_s #=> "NaN+NaN*i"
*/
static VALUE
nucomp_to_s(VALUE self)
@@ -1433,15 +1634,16 @@ nucomp_to_s(VALUE self)
/*
* call-seq:
- * cmp.inspect -> string
+ * inspect -> string
+ *
+ * Returns a string representation of +self+:
*
- * Returns the value as a string for inspection.
+ * Complex.rect(2).inspect # => "(2+0i)"
+ * Complex.rect(-8, 6).inspect # => "(-8+6i)"
+ * Complex.rect(0, Rational(1, 2)).inspect # => "(0+(1/2)*i)"
+ * Complex.rect(0, Float::INFINITY).inspect # => "(0+Infinity*i)"
+ * Complex.rect(Float::NAN, Float::NAN).inspect # => "(NaN+NaN*i)"
*
- * Complex(2).inspect #=> "(2+0i)"
- * Complex('-8/6').inspect #=> "((-4/3)+0i)"
- * Complex('1/2i').inspect #=> "(0+(1/2)*i)"
- * Complex(0, Float::INFINITY).inspect #=> "(0+Infinity*i)"
- * Complex(Float::NAN, Float::NAN).inspect #=> "(NaN+NaN*i)"
*/
static VALUE
nucomp_inspect(VALUE self)
@@ -1459,10 +1661,15 @@ nucomp_inspect(VALUE self)
/*
* call-seq:
- * cmp.finite? -> true or false
+ * finite? -> true or false
+ *
+ * Returns +true+ if both <tt>self.real.finite?</tt> and <tt>self.imag.finite?</tt>
+ * are true, +false+ otherwise:
*
- * Returns +true+ if +cmp+'s real and imaginary parts are both finite numbers,
- * otherwise returns +false+.
+ * Complex.rect(1, 1).finite? # => true
+ * Complex.rect(Float::INFINITY, 0).finite? # => false
+ *
+ * Related: Numeric#finite?, Float#finite?.
*/
static VALUE
rb_complex_finite_p(VALUE self)
@@ -1474,15 +1681,15 @@ rb_complex_finite_p(VALUE self)
/*
* call-seq:
- * cmp.infinite? -> nil or 1
+ * infinite? -> 1 or nil
*
- * Returns +1+ if +cmp+'s real or imaginary part is an infinite number,
- * otherwise returns +nil+.
+ * Returns +1+ if either <tt>self.real.infinite?</tt> or <tt>self.imag.infinite?</tt>
+ * is true, +nil+ otherwise:
*
- * For example:
+ * Complex.rect(Float::INFINITY, 0).infinite? # => 1
+ * Complex.rect(1, 1).infinite? # => nil
*
- * (1+1i).infinite? #=> nil
- * (Float::INFINITY + 1i).infinite? #=> 1
+ * Related: Numeric#infinite?, Float#infinite?.
*/
static VALUE
rb_complex_infinite_p(VALUE self)
@@ -1510,7 +1717,7 @@ nucomp_loader(VALUE self, VALUE a)
RCOMPLEX_SET_REAL(dat, rb_ivar_get(a, id_i_real));
RCOMPLEX_SET_IMAG(dat, rb_ivar_get(a, id_i_imag));
- OBJ_FREEZE_RAW(self);
+ OBJ_FREEZE(self);
return self;
}
@@ -1580,14 +1787,15 @@ rb_dbl_complex_new(double real, double imag)
/*
* call-seq:
- * cmp.to_i -> integer
+ * to_i -> integer
*
- * Returns the value as an integer if possible (the imaginary part
- * should be exactly zero).
+ * Returns the value of <tt>self.real</tt> as an Integer, if possible:
*
- * Complex(1, 0).to_i #=> 1
- * Complex(1, 0.0).to_i # RangeError
- * Complex(1, 2).to_i # RangeError
+ * Complex.rect(1, 0).to_i # => 1
+ * Complex.rect(1, Rational(0, 1)).to_i # => 1
+ *
+ * Raises RangeError if <tt>self.imag</tt> is not exactly zero
+ * (either <tt>Integer(0)</tt> or <tt>Rational(0, _n_)</tt>).
*/
static VALUE
nucomp_to_i(VALUE self)
@@ -1603,14 +1811,15 @@ nucomp_to_i(VALUE self)
/*
* call-seq:
- * cmp.to_f -> float
+ * to_f -> float
+ *
+ * Returns the value of <tt>self.real</tt> as a Float, if possible:
*
- * Returns the value as a float if possible (the imaginary part should
- * be exactly zero).
+ * Complex.rect(1, 0).to_f # => 1.0
+ * Complex.rect(1, Rational(0, 1)).to_f # => 1.0
*
- * Complex(1, 0).to_f #=> 1.0
- * Complex(1, 0.0).to_f # RangeError
- * Complex(1, 2).to_f # RangeError
+ * Raises RangeError if <tt>self.imag</tt> is not exactly zero
+ * (either <tt>Integer(0)</tt> or <tt>Rational(0, _n_)</tt>).
*/
static VALUE
nucomp_to_f(VALUE self)
@@ -1626,41 +1835,69 @@ nucomp_to_f(VALUE self)
/*
* call-seq:
- * cmp.to_r -> rational
+ * to_r -> rational
+ *
+ * Returns the value of <tt>self.real</tt> as a Rational, if possible:
*
- * Returns the value as a rational if possible (the imaginary part
- * should be exactly zero).
+ * Complex.rect(1, 0).to_r # => (1/1)
+ * Complex.rect(1, Rational(0, 1)).to_r # => (1/1)
+ * Complex.rect(1, 0.0).to_r # => (1/1)
*
- * Complex(1, 0).to_r #=> (1/1)
- * Complex(1, 0.0).to_r # RangeError
- * Complex(1, 2).to_r # RangeError
+ * Raises RangeError if <tt>self.imag</tt> is not exactly zero
+ * (either <tt>Integer(0)</tt> or <tt>Rational(0, _n_)</tt>)
+ * and <tt>self.imag.to_r</tt> is not exactly zero.
*
- * See rationalize.
+ * Related: Complex#rationalize.
*/
static VALUE
nucomp_to_r(VALUE self)
{
get_dat1(self);
- if (!k_exact_zero_p(dat->imag)) {
- rb_raise(rb_eRangeError, "can't convert %"PRIsVALUE" into Rational",
- self);
+ if (RB_FLOAT_TYPE_P(dat->imag) && FLOAT_ZERO_P(dat->imag)) {
+ /* Do nothing here */
+ }
+ else if (!k_exact_zero_p(dat->imag)) {
+ VALUE imag = rb_check_convert_type_with_id(dat->imag, T_RATIONAL, "Rational", idTo_r);
+ if (NIL_P(imag) || !k_exact_zero_p(imag)) {
+ rb_raise(rb_eRangeError, "can't convert %"PRIsVALUE" into Rational",
+ self);
+ }
}
return f_to_r(dat->real);
}
/*
* call-seq:
- * cmp.rationalize([eps]) -> rational
- *
- * Returns the value as a rational if possible (the imaginary part
- * should be exactly zero).
- *
- * Complex(1.0/3, 0).rationalize #=> (1/3)
- * Complex(1, 0.0).rationalize # RangeError
- * Complex(1, 2).rationalize # RangeError
- *
- * See to_r.
+ * rationalize(epsilon = nil) -> rational
+ *
+ * Returns a Rational object whose value is exactly or approximately
+ * equivalent to that of <tt>self.real</tt>.
+ *
+ * With no argument +epsilon+ given, returns a \Rational object
+ * whose value is exactly equal to that of <tt>self.real.rationalize</tt>:
+ *
+ * Complex.rect(1, 0).rationalize # => (1/1)
+ * Complex.rect(1, Rational(0, 1)).rationalize # => (1/1)
+ * Complex.rect(3.14159, 0).rationalize # => (314159/100000)
+ *
+ * With argument +epsilon+ given, returns a \Rational object
+ * whose value is exactly or approximately equal to that of <tt>self.real</tt>
+ * to the given precision:
+ *
+ * Complex.rect(3.14159, 0).rationalize(0.1) # => (16/5)
+ * Complex.rect(3.14159, 0).rationalize(0.01) # => (22/7)
+ * Complex.rect(3.14159, 0).rationalize(0.001) # => (201/64)
+ * Complex.rect(3.14159, 0).rationalize(0.0001) # => (333/106)
+ * Complex.rect(3.14159, 0).rationalize(0.00001) # => (355/113)
+ * Complex.rect(3.14159, 0).rationalize(0.000001) # => (7433/2366)
+ * Complex.rect(3.14159, 0).rationalize(0.0000001) # => (9208/2931)
+ * Complex.rect(3.14159, 0).rationalize(0.00000001) # => (47460/15107)
+ * Complex.rect(3.14159, 0).rationalize(0.000000001) # => (76149/24239)
+ * Complex.rect(3.14159, 0).rationalize(0.0000000001) # => (314159/100000)
+ * Complex.rect(3.14159, 0).rationalize(0.0) # => (3537115888337719/1125899906842624)
+ *
+ * Related: Complex#to_r.
*/
static VALUE
nucomp_rationalize(int argc, VALUE *argv, VALUE self)
@@ -1678,12 +1915,9 @@ nucomp_rationalize(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * complex.to_c -> self
- *
- * Returns self.
+ * to_c -> self
*
- * Complex(2).to_c #=> (2+0i)
- * Complex(-8, 6).to_c #=> (-8+6i)
+ * Returns +self+.
*/
static VALUE
nucomp_to_c(VALUE self)
@@ -1708,9 +1942,9 @@ nilclass_to_c(VALUE self)
/*
* call-seq:
- * num.to_c -> complex
+ * to_c -> complex
*
- * Returns the value as a complex.
+ * Returns +self+ as a Complex object.
*/
static VALUE
numeric_to_c(VALUE self)
@@ -1990,23 +2224,14 @@ string_to_c_strict(VALUE self, int raise)
rb_must_asciicompat(self);
- s = RSTRING_PTR(self);
-
- if (!s || memchr(s, '\0', RSTRING_LEN(self))) {
- if (!raise) return Qnil;
- rb_raise(rb_eArgError, "string contains null byte");
+ if (raise) {
+ s = StringValueCStr(self);
}
-
- if (s && s[RSTRING_LEN(self)]) {
- rb_str_modify(self);
- s = RSTRING_PTR(self);
- s[RSTRING_LEN(self)] = '\0';
+ else if (!(s = rb_str_to_cstr(self))) {
+ return Qnil;
}
- if (!s)
- s = (char *)"";
-
- if (!parse_comp(s, 1, &num)) {
+ if (!parse_comp(s, TRUE, &num)) {
if (!raise) return Qnil;
rb_raise(rb_eArgError, "invalid value for convert(): %+"PRIsVALUE,
self);
@@ -2017,53 +2242,39 @@ string_to_c_strict(VALUE self, int raise)
/*
* call-seq:
- * str.to_c -> complex
- *
- * Returns a complex which denotes the string form. The parser
- * ignores leading whitespaces and trailing garbage. Any digit
- * sequences can be separated by an underscore. Returns zero for null
- * or garbage string.
- *
- * '9'.to_c #=> (9+0i)
- * '2.5'.to_c #=> (2.5+0i)
- * '2.5/1'.to_c #=> ((5/2)+0i)
- * '-3/2'.to_c #=> ((-3/2)+0i)
- * '-i'.to_c #=> (0-1i)
- * '45i'.to_c #=> (0+45i)
- * '3-4i'.to_c #=> (3-4i)
- * '-4e2-4e-2i'.to_c #=> (-400.0-0.04i)
- * '-0.0-0.0i'.to_c #=> (-0.0-0.0i)
- * '1/2+3/4i'.to_c #=> ((1/2)+(3/4)*i)
- * 'ruby'.to_c #=> (0+0i)
- *
- * Polar form:
- * include Math
- * "1.0@0".to_c #=> (1+0.0i)
- * "1.0@#{PI/2}".to_c #=> (0.0+1i)
- * "1.0@#{PI}".to_c #=> (-1+0.0i)
- *
- * See Kernel.Complex.
+ * to_c -> complex
+ *
+ * Returns +self+ interpreted as a Complex object;
+ * leading whitespace and trailing garbage are ignored:
+ *
+ * '9'.to_c # => (9+0i)
+ * '2.5'.to_c # => (2.5+0i)
+ * '2.5/1'.to_c # => ((5/2)+0i)
+ * '-3/2'.to_c # => ((-3/2)+0i)
+ * '-i'.to_c # => (0-1i)
+ * '45i'.to_c # => (0+45i)
+ * '3-4i'.to_c # => (3-4i)
+ * '-4e2-4e-2i'.to_c # => (-400.0-0.04i)
+ * '-0.0-0.0i'.to_c # => (-0.0-0.0i)
+ * '1/2+3/4i'.to_c # => ((1/2)+(3/4)*i)
+ * '1.0@0'.to_c # => (1+0.0i)
+ * "1.0@#{Math::PI/2}".to_c # => (0.0+1i)
+ * "1.0@#{Math::PI}".to_c # => (-1+0.0i)
+ *
+ * Returns \Complex zero if the string cannot be converted:
+ *
+ * 'ruby'.to_c # => (0+0i)
+ *
+ * See Kernel#Complex.
*/
static VALUE
string_to_c(VALUE self)
{
- char *s;
VALUE num;
rb_must_asciicompat(self);
- s = RSTRING_PTR(self);
-
- if (s && s[RSTRING_LEN(self)]) {
- rb_str_modify(self);
- s = RSTRING_PTR(self);
- s[RSTRING_LEN(self)] = '\0';
- }
-
- if (!s)
- s = (char *)"";
-
- (void)parse_comp(s, 0, &num);
+ (void)parse_comp(rb_str_fill_terminator(self, 1), FALSE, &num);
return num;
}
@@ -2120,8 +2331,11 @@ nucomp_convert(VALUE klass, VALUE a1, VALUE a2, int raise)
return a1;
/* should raise exception for consistency */
if (!k_numeric_p(a1)) {
- if (!raise)
- return rb_protect(to_complex, a1, NULL);
+ if (!raise) {
+ a1 = rb_protect(to_complex, a1, NULL);
+ rb_set_errinfo(Qnil);
+ return a1;
+ }
return to_complex(a1);
}
}
@@ -2165,9 +2379,9 @@ nucomp_s_convert(int argc, VALUE *argv, VALUE klass)
/*
* call-seq:
- * num.abs2 -> real
+ * abs2 -> real
*
- * Returns square of self.
+ * Returns the square of +self+.
*/
static VALUE
numeric_abs2(VALUE self)
@@ -2177,11 +2391,9 @@ numeric_abs2(VALUE self)
/*
* call-seq:
- * num.arg -> 0 or float
- * num.angle -> 0 or float
- * num.phase -> 0 or float
+ * arg -> 0 or Math::PI
*
- * Returns 0 if the value is positive, pi otherwise.
+ * Returns zero if +self+ is positive, Math::PI otherwise.
*/
static VALUE
numeric_arg(VALUE self)
@@ -2193,10 +2405,9 @@ numeric_arg(VALUE self)
/*
* call-seq:
- * num.rect -> array
- * num.rectangular -> array
+ * rect -> array
*
- * Returns an array; [num, 0].
+ * Returns array <tt>[self, 0]</tt>.
*/
static VALUE
numeric_rect(VALUE self)
@@ -2206,9 +2417,9 @@ numeric_rect(VALUE self)
/*
* call-seq:
- * num.polar -> array
+ * polar -> array
*
- * Returns an array; [num.abs, num.arg].
+ * Returns array <tt>[self.abs, self.arg]</tt>.
*/
static VALUE
numeric_polar(VALUE self)
@@ -2236,11 +2447,9 @@ numeric_polar(VALUE self)
/*
* call-seq:
- * flo.arg -> 0 or float
- * flo.angle -> 0 or float
- * flo.phase -> 0 or float
+ * arg -> 0 or Math::PI
*
- * Returns 0 if the value is positive, pi otherwise.
+ * Returns 0 if +self+ is positive, Math::PI otherwise.
*/
static VALUE
float_arg(VALUE self)
@@ -2253,45 +2462,137 @@ float_arg(VALUE self)
}
/*
- * A complex number can be represented as a paired real number with
- * imaginary unit; a+bi. Where a is real part, b is imaginary part
- * and i is imaginary unit. Real a equals complex a+0i
- * mathematically.
+ * A \Complex object houses a pair of values,
+ * given when the object is created as either <i>rectangular coordinates</i>
+ * or <i>polar coordinates</i>.
+ *
+ * == Rectangular Coordinates
+ *
+ * The rectangular coordinates of a complex number
+ * are called the _real_ and _imaginary_ parts;
+ * see {Complex number definition}[https://en.wikipedia.org/wiki/Complex_number#Definition].
+ *
+ * You can create a \Complex object from rectangular coordinates with:
+ *
+ * - A {complex literal}[rdoc-ref:doc/syntax/literals.rdoc@Complex+Literals].
+ * - \Method Complex.rect.
+ * - \Method Kernel#Complex, either with numeric arguments or with certain string arguments.
+ * - \Method String#to_c, for certain strings.
+ *
+ * Note that each of the stored parts may be a an instance one of the classes
+ * Complex, Float, Integer, or Rational;
+ * they may be retrieved:
+ *
+ * - Separately, with methods Complex#real and Complex#imaginary.
+ * - Together, with method Complex#rect.
+ *
+ * The corresponding (computed) polar values may be retrieved:
+ *
+ * - Separately, with methods Complex#abs and Complex#arg.
+ * - Together, with method Complex#polar.
+ *
+ * == Polar Coordinates
+ *
+ * The polar coordinates of a complex number
+ * are called the _absolute_ and _argument_ parts;
+ * see {Complex polar plane}[https://en.wikipedia.org/wiki/Complex_number#Polar_complex_plane].
+ *
+ * In this class, the argument part
+ * in expressed {radians}[https://en.wikipedia.org/wiki/Radian]
+ * (not {degrees}[https://en.wikipedia.org/wiki/Degree_(angle)]).
+ *
+ * You can create a \Complex object from polar coordinates with:
+ *
+ * - \Method Complex.polar.
+ * - \Method Kernel#Complex, with certain string arguments.
+ * - \Method String#to_c, for certain strings.
+ *
+ * Note that each of the stored parts may be a an instance one of the classes
+ * Complex, Float, Integer, or Rational;
+ * they may be retrieved:
+ *
+ * - Separately, with methods Complex#abs and Complex#arg.
+ * - Together, with method Complex#polar.
+ *
+ * The corresponding (computed) rectangular values may be retrieved:
+ *
+ * - Separately, with methods Complex#real and Complex#imag.
+ * - Together, with method Complex#rect.
+ *
+ * == What's Here
+ *
+ * First, what's elsewhere:
+ *
+ * - \Class \Complex inherits (directly or indirectly)
+ * from classes {Numeric}[rdoc-ref:Numeric@What-27s+Here]
+ * and {Object}[rdoc-ref:Object@What-27s+Here].
+ * - Includes (indirectly) module {Comparable}[rdoc-ref:Comparable@What-27s+Here].
+ *
+ * Here, class \Complex has methods for:
+ *
+ * === Creating \Complex Objects
+ *
+ * - ::polar: Returns a new \Complex object based on given polar coordinates.
+ * - ::rect (and its alias ::rectangular):
+ * Returns a new \Complex object based on given rectangular coordinates.
+ *
+ * === Querying
+ *
+ * - #abs (and its alias #magnitude): Returns the absolute value for +self+.
+ * - #arg (and its aliases #angle and #phase):
+ * Returns the argument (angle) for +self+ in radians.
+ * - #denominator: Returns the denominator of +self+.
+ * - #finite?: Returns whether both +self.real+ and +self.image+ are finite.
+ * - #hash: Returns the integer hash value for +self+.
+ * - #imag (and its alias #imaginary): Returns the imaginary value for +self+.
+ * - #infinite?: Returns whether +self.real+ or +self.image+ is infinite.
+ * - #numerator: Returns the numerator of +self+.
+ * - #polar: Returns the array <tt>[self.abs, self.arg]</tt>.
+ * - #inspect: Returns a string representation of +self+.
+ * - #real: Returns the real value for +self+.
+ * - #real?: Returns +false+; for compatibility with Numeric#real?.
+ * - #rect (and its alias #rectangular):
+ * Returns the array <tt>[self.real, self.imag]</tt>.
*
- * You can create a \Complex object explicitly with:
+ * === Comparing
*
- * - A {complex literal}[rdoc-ref:syntax/literals.rdoc@Complex+Literals].
+ * - #<=>: Returns whether +self+ is less than, equal to, or greater than the given argument.
+ * - #==: Returns whether +self+ is equal to the given argument.
*
- * You can convert certain objects to \Complex objects with:
+ * === Converting
*
- * - \Method #Complex.
+ * - #rationalize: Returns a Rational object whose value is exactly
+ * or approximately equivalent to that of <tt>self.real</tt>.
+ * - #to_c: Returns +self+.
+ * - #to_d: Returns the value as a BigDecimal object.
+ * - #to_f: Returns the value of <tt>self.real</tt> as a Float, if possible.
+ * - #to_i: Returns the value of <tt>self.real</tt> as an Integer, if possible.
+ * - #to_r: Returns the value of <tt>self.real</tt> as a Rational, if possible.
+ * - #to_s: Returns a string representation of +self+.
*
- * Complex object can be created as literal, and also by using
- * Kernel#Complex, Complex::rect, Complex::polar or to_c method.
+ * === Performing Complex Arithmetic
*
- * 2+1i #=> (2+1i)
- * Complex(1) #=> (1+0i)
- * Complex(2, 3) #=> (2+3i)
- * Complex.polar(2, 3) #=> (-1.9799849932008908+0.2822400161197344i)
- * 3.to_c #=> (3+0i)
+ * - #*: Returns the product of +self+ and the given numeric.
+ * - #**: Returns +self+ raised to power of the given numeric.
+ * - #+: Returns the sum of +self+ and the given numeric.
+ * - #-: Returns the difference of +self+ and the given numeric.
+ * - #-@: Returns the negation of +self+.
+ * - #/: Returns the quotient of +self+ and the given numeric.
+ * - #abs2: Returns square of the absolute value (magnitude) for +self+.
+ * - #conj (and its alias #conjugate): Returns the conjugate of +self+.
+ * - #fdiv: Returns <tt>Complex.rect(self.real/numeric, self.imag/numeric)</tt>.
*
- * You can also create complex object from floating-point numbers or
- * strings.
+ * === Working with JSON
*
- * Complex(0.3) #=> (0.3+0i)
- * Complex('0.3-0.5i') #=> (0.3-0.5i)
- * Complex('2/3+3/4i') #=> ((2/3)+(3/4)*i)
- * Complex('1@2') #=> (-0.4161468365471424+0.9092974268256817i)
+ * - ::json_create: Returns a new \Complex object,
+ * deserialized from the given serialized hash.
+ * - #as_json: Returns a serialized hash constructed from +self+.
+ * - #to_json: Returns a JSON string representing +self+.
*
- * 0.3.to_c #=> (0.3+0i)
- * '0.3-0.5i'.to_c #=> (0.3-0.5i)
- * '2/3+3/4i'.to_c #=> ((2/3)+(3/4)*i)
- * '1@2'.to_c #=> (-0.4161468365471424+0.9092974268256817i)
+ * These methods are provided by the {JSON gem}[https://github.com/flori/json]. To make these methods available:
*
- * A complex object is either an exact or an inexact number.
+ * require 'json/add/complex'
*
- * Complex(1, 1) / 2 #=> ((1/2)+(1/2)*i)
- * Complex(1, 1) / 2.0 #=> (0.5+0.5i)
*/
void
Init_Complex(void)
@@ -2412,13 +2713,17 @@ Init_Complex(void)
rb_define_method(rb_cFloat, "phase", float_arg, 0);
/*
- * The imaginary unit.
+ * Equivalent
+ * to <tt>Complex.rect(0, 1)</tt>:
+ *
+ * Complex::I # => (0+1i)
+ *
*/
rb_define_const(rb_cComplex, "I",
f_complex_new_bang2(rb_cComplex, ZERO, ONE));
#if !USE_FLONUM
- rb_gc_register_mark_object(RFLOAT_0 = DBL2NUM(0.0));
+ rb_vm_register_global_object(RFLOAT_0 = DBL2NUM(0.0));
#endif
rb_provide("complex.so"); /* for backward compatibility */
diff --git a/configure.ac b/configure.ac
index 6da801a5c3..e9a452ebee 100644
--- a/configure.ac
+++ b/configure.ac
@@ -38,6 +38,7 @@ m4_include([tool/m4/ruby_replace_type.m4])dnl
m4_include([tool/m4/ruby_require_funcs.m4])dnl
m4_include([tool/m4/ruby_rm_recursive.m4])dnl
m4_include([tool/m4/ruby_setjmp_type.m4])dnl
+m4_include([tool/m4/ruby_shared_gc.m4])dnl
m4_include([tool/m4/ruby_stack_grow_direction.m4])dnl
m4_include([tool/m4/ruby_thread.m4])dnl
m4_include([tool/m4/ruby_try_cflags.m4])dnl
@@ -47,11 +48,22 @@ m4_include([tool/m4/ruby_universal_arch.m4])dnl
m4_include([tool/m4/ruby_wasm_tools.m4])dnl
m4_include([tool/m4/ruby_werror_flag.m4])dnl
+AS_IF([test "x${GITHUB_ACTIONS}" = xtrue],
+[AC_REQUIRE([_COLORIZE_RESULT_PREPARE])dnl
+dnl 93(bright yellow) is copied from .github/workflows/mingw.yml
+ begin_group() { AS_ECHO(["::group::@<:@93m$[]1@<:@m"]);}
+ end_group() { AS_ECHO(["::endgroup::"]);}
+],
+[dnl
+ begin_group() { :;}
+ end_group() { :;}
+])
+
AC_ARG_VAR([cflags], [additional CFLAGS (ignored when CFLAGS is given)])dnl
AC_ARG_VAR([cppflags], [additional CPPFLAGS (ignored when CPPFLAGS is given)])dnl
AC_ARG_VAR([cxxflags], [additional CXXFLAGS (ignored when CXXFLAGS is given)])dnl
-: "environment section" && {
+[begin]_group "environment section" && {
HAVE_BASERUBY=yes
BASERUBY_VERSION=
AC_ARG_WITH(baseruby,
@@ -64,8 +76,10 @@ AC_ARG_WITH(baseruby,
[
AC_PATH_PROG([BASERUBY], [ruby], [false])
])
-# BASERUBY must be >= 2.5.0. Note that `"2.5.0" > "2.5"` is true.
-AS_IF([test "$HAVE_BASERUBY" != no -a "`RUBYOPT=- $BASERUBY --disable=gems -e 'print 42 if RUBY_VERSION > "2.5"' 2>/dev/null`" = 42], [
+AS_IF([test "$HAVE_BASERUBY" != no], [
+ RUBYOPT=- $BASERUBY --disable=gems "${tooldir}/missing-baseruby.bat" || HAVE_BASERUBY=no
+])
+AS_IF([test "${HAVE_BASERUBY:=no}" != no], [
AS_CASE(["$build_os"], [mingw*], [
# Can MSys shell run a command with a drive letter?
RUBYOPT=- `cygpath -ma "$BASERUBY"` --disable=gems -e exit 2>/dev/null || HAVE_BASERUBY=no
@@ -73,12 +87,10 @@ AS_IF([test "$HAVE_BASERUBY" != no -a "`RUBYOPT=- $BASERUBY --disable=gems -e 'p
RUBY_APPEND_OPTION(BASERUBY, "--disable=gems")
BASERUBY_VERSION=`$BASERUBY -v`
$BASERUBY -C "$srcdir" tool/downloader.rb -d tool -e gnu config.guess config.sub >&AS_MESSAGE_FD
-], [
- HAVE_BASERUBY=no
])
AS_IF([test "$HAVE_BASERUBY" = no], [
AS_IF([test "$cross_compiling" = yes], [AC_MSG_ERROR([executable host ruby is required for cross-compiling])])
- BASERUBY="echo executable host ruby is required. use --with-baseruby option.; false"
+ BASERUBY=${tooldir}/missing-baseruby.bat
])
AC_SUBST(BASERUBY)
AC_SUBST(HAVE_BASERUBY)
@@ -266,7 +278,7 @@ AC_CHECK_TOOLS([STRIP], [gstrip strip], [:])
# nm errors with Rust's LLVM bitcode when Rust uses a newer LLVM version than nm.
# In case we're working with llvm-nm, tell it to not worry about the bitcode.
-AS_IF([${NM} --help | grep -q 'llvm-bc'], [NM="$NM --no-llvm-bc"])
+AS_IF([${NM} --help 2>&1 | grep -q 'llvm-bc'], [NM="$NM --no-llvm-bc"])
AS_IF([test ! $rb_test_CFLAGS], [AS_UNSET(CFLAGS)]); AS_UNSET(rb_test_CFLAGS)
AS_IF([test ! $rb_test_CXXFLAGS], [AS_UNSET(CXXFLAGS)]); AS_UNSET(rb_save_CXXFLAGS)
@@ -409,33 +421,28 @@ AC_SUBST(OUTFLAG)
AC_SUBST(COUTFLAG)
AC_SUBST(CSRCFLAG)
-: ${RJIT_CC=$CC}
-AS_IF([test "x$cross_compiling" = xno], [
- AC_PATH_PROG([RJIT_CC], ${RJIT_CC})
-
- # if $CC is in /usr/lib/ccache/$CC, search original $CC (disable ccache)
- AS_IF([echo $RUBY_DEBUG | grep ci > /dev/null &&
- echo $RJIT_CC | grep ^/usr/lib/ccache > /dev/null], [
- PATH=`echo $PATH | sed "s/\/usr\/lib\/ccache://"` RJIT_CC=`which $CC`])
-
- AS_CASE([$target_os],
- [*mingw*], [command -v cygpath > /dev/null && RJIT_CC=`cygpath -ma $RJIT_CC`])
- shift 2
- RJIT_CC="$RJIT_CC${1+ }$*"
-])
-
AS_CASE(["$build_os"],
- [darwin1*.*], [
+ [darwin*], [
+ # gcc 13 warns duplicate -l options, which are added by the
+ # default spec.
# Xcode linker warns for deprecated architecture and wrongly
# installed TBD files.
- CC_WRAPPER=""
+ AC_MSG_CHECKING(for $CC linker warning)
+ suppress_ld_waring=no
echo 'int main(void) {return 0;}' > conftest.c
- AS_IF([$CC -framework Foundation -o conftest conftest.c 2>&1 |
- grep '^ld: warning: text-based stub file' >/dev/null], [
- CC_WRAPPER=`cd -P "${tooldir}" && pwd`/darwin-cc
- CC="$CC_WRAPPER $CC"
+ AS_IF([$CC -framework Foundation -o conftest -ggdb3 conftest.c 2>&1 |
+ grep \
+ -e '^ld: warning: ignoring duplicate libraries:' \
+ -e '^ld: warning: text-based stub file' \
+ -e '^ld: warning: -multiply_defined is obsolete' \
+ -e "^warning: '\.debug_macinfo'" \
+ -e '^note: while processing' \
+ >/dev/null], [
+ suppress_ld_waring=yes
])
rm -fr conftest*
+ test $suppress_ld_waring = yes && warnflags="${warnflags:+${warnflags} }-Wl,-w"
+ AC_MSG_RESULT($suppress_ld_waring)
])
AS_CASE(["$target_os"],
[wasi*], [
@@ -462,8 +469,8 @@ AC_SUBST(CC_VERSION_MESSAGE, $cc_version_message)
: ${DLDFLAGS="$LDFLAGS"}
RUBY_UNIVERSAL_ARCH
-AS_IF([test "$target_cpu" != "$host_cpu" -a "$GCC" = yes -a "$cross_compiling" = no -a "${universal_binary:-no}" = no], [
- RUBY_DEFAULT_ARCH("$target_cpu")
+AS_IF([test "$target_cpu" != "$host_cpu" -a "$GCC" = yes -a "${universal_binary:-no}" = no], [
+ RUBY_DEFAULT_ARCH($target_cpu)
])
host_os=$target_os
host_vendor=$target_vendor
@@ -507,10 +514,10 @@ AS_CASE(["$target_os"],
])
rb_cv_binary_elf=no
: ${enable_shared=yes}
+ AS_IF([$WINDRES --version | grep LLVM > /dev/null], [USE_LLVM_WINDRES=yes], [USE_LLVM_WINDRES=no])
],
[hiuxmpp*], [AC_DEFINE(__HIUX_MPP__)]) # by TOYODA Eizi <toyoda@npd.kishou.go.jp>
-
AC_PROG_LN_S
AC_PROG_MAKE_SET
AC_PROG_INSTALL
@@ -591,7 +598,7 @@ AC_MSG_RESULT([$CHDIR])
AC_SUBST(CHDIR)
}
-: "compiler section" && {
+[begin]_group "compiler section" && {
RUBY_WERROR_FLAG([
AC_MSG_CHECKING([whether CFLAGS is valid])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],
@@ -686,6 +693,10 @@ AS_CASE(["$GCC:${warnflags+set}:${extra_warnflags:+set}:"],
AS_IF([test $gcc_major -le 6], [
extra_warnflags="$extra_warnflags -Wno-maybe-uninitialized"
])
+ AS_CASE([ $CFLAGS ], [*" -save-temps="*|*" -save-temps "*], [], [
+ extra_warnflags="$extra_warnflags -Werror=misleading-indentation"
+ ])
+
# ICC doesn't support -Werror=
AS_IF([test $icc_version -gt 0], [
particular_werror_flags=no
@@ -697,7 +708,6 @@ AS_CASE(["$GCC:${warnflags+set}:${extra_warnflags:+set}:"],
-Werror=duplicated-cond \
-Werror=implicit-function-declaration \
-Werror=implicit-int \
- -Werror=misleading-indentation \
-Werror=pointer-arith \
-Werror=shorten-64-to-32 \
-Werror=write-strings \
@@ -828,7 +838,10 @@ AS_IF([test "$GCC" = yes], [
AS_FOR(option, opt, [-mbranch-protection=pac-ret -msign-return-address=all], [
RUBY_TRY_CFLAGS(option, [branch_protection=yes], [branch_protection=no])
AS_IF([test "x$branch_protection" = xyes], [
+ # C compiler and assembler must be consistent for -mbranch-protection
+ # since they both check `__ARM_FEATURE_PAC_DEFAULT` definition.
RUBY_APPEND_OPTION(XCFLAGS, option)
+ RUBY_APPEND_OPTION(ASFLAGS, option)
break
])
])
@@ -968,9 +981,10 @@ AS_IF([test "x$OPT_DIR" != x], [
LDFLAGS="${LDFLAGS:+$LDFLAGS }$val"
DLDFLAGS="${DLDFLAGS:+$DLDFLAGS }$val"
LDFLAGS_OPTDIR="$val"
- CPPFLAGS="${CPPFLAGS:+$CPPFLAGS }"`echo "$OPT_DIR" | tr "${PATH_SEPARATOR}" '\012' |
+ INCFLAGS="${INCFLAGS:+$INCFLAGS }"`echo "$OPT_DIR" | tr "${PATH_SEPARATOR}" '\012' |
sed '/^$/d;s|^|-I|;s|$|/include|' | tr '\012' ' ' | sed 's/ *$//'`
])
+AC_SUBST(incflags, "$INCFLAGS")
test -z "${ac_env_CFLAGS_set}" -a -n "${cflags+set}" && eval CFLAGS="\"$cflags $ARCH_FLAG\""
test -z "${ac_env_CXXFLAGS_set}" -a -n "${cxxflags+set}" && eval CXXFLAGS="\"$cxxflags $ARCH_FLAG\""
@@ -985,7 +999,7 @@ AS_IF([test "$rb_cv_have_stmt_and_decl_in_expr" = yes], [
AC_DEFINE(HAVE_STMT_AND_DECL_IN_EXPR)
])
-: "header and library section" && {
+[begin]_group "header and library section" && {
AC_ARG_WITH(winnt-ver,
AS_HELP_STRING([--with-winnt-ver=0xXXXX], [target Windows NT version (default to 0x0600)]),
[with_winnt_ver="$withval"], [with_winnt_ver="0x0600"])
@@ -1088,21 +1102,23 @@ main()
])
POSTLINK=""
AC_CHECK_PROGS(codesign, codesign)
- AC_CHECK_PROGS(dsymutil, dsymutil)
+ dsymutils=
+ AS_CASE("$CC_NO_WRAPPER", [gcc*-1[[3-9]]], [
+ dsymutils=${CC_NO_WRAPPER@%:@gcc}
+ dsymutils=dsymutil${dsymutils%-1*}
+ dsymutils="$dsymutils-19 $dsymutils-18 $dsymutils-17"
+ ])
+ AC_CHECK_PROGS(dsymutil, $dsymutils dsymutil)
AS_IF([test -n "$codesign"], [
- POSTLINK="{ test -z '\$(RUBY_CODESIGN)' || $codesign -s '\$(RUBY_CODESIGN)' -f \$@; }${POSTLINK:+; $POSTLINK}"
+ POSTLINK="{ test -z '\$(RUBY_CODESIGN)' || $codesign -s '\$(RUBY_CODESIGN)' \$@; }${POSTLINK:+; $POSTLINK}"
])
AS_IF([test -n "$dsymutil"], [
- POSTLINK="$dsymutil \$@${POSTLINK:+; $POSTLINK}"
- ])
- AS_IF([test -n "${POSTLINK}"], [
- LINK_SO="$LINK_SO
-\$(POSTLINK)"
+ POSTLINK="$dsymutil \$@ 2>/dev/null${POSTLINK:+; $POSTLINK}"
])
AC_CHECK_HEADERS(crt_externs.h, [], [], [
#include <crt_externs.h>
])
- cleanlibs='$(TARGET_SO).dSYM'
+ cleanlibs='$(TARGET_SO:=.dSYM)'
],
[solaris*], [ LIBS="-lm $LIBS"
ac_cv_func_vfork=no
@@ -1255,7 +1271,7 @@ main()
[wasi*],[ LIBS="-lm -lwasi-emulated-mman -lwasi-emulated-signal -lwasi-emulated-getpid -lwasi-emulated-process-clocks $LIBS"
RUBY_APPEND_OPTIONS(CFLAGS, -D_WASI_EMULATED_SIGNAL -D_WASI_EMULATED_MMAN -D_WASI_EMULATED_GETPID -D_WASI_EMULATED_PROCESS_CLOCKS)
RUBY_APPEND_OPTIONS(CPPFLAGS, -D_WASI_EMULATED_SIGNAL -D_WASI_EMULATED_MMAN -D_WASI_EMULATED_GETPID -D_WASI_EMULATED_PROCESS_CLOCKS)
- POSTLINK="\$(WASMOPT) --asyncify \$(wasmoptflags) --pass-arg=asyncify-ignore-imports -o \$@ \$@${POSTLINK:+; $POSTLINK}"
+ POSTLINK="\$(WASMOPT) --asyncify \$(wasmoptflags) -o \$@ \$@${POSTLINK:+; $POSTLINK}"
# wasi-libc's sys/socket.h is not compatible with -std=gnu99,
# so re-declare shutdown in include/ruby/missing.h
ac_cv_func_shutdown=no
@@ -1263,6 +1279,13 @@ main()
[ LIBS="-lm $LIBS"])
: ${ORIG_LIBS=$LIBS}
+AS_IF([test -n "${POSTLINK}"], [
+ # NOTE: A (part of) link commands used link shared extension libraries. If
+ # the first line of the value is empty, mkmf prepends default link steps.
+ LINK_SO="$LINK_SO
+\$(POSTLINK)"
+])
+
AS_IF([test -n "${rb_there_is_in_fact_no_gplusplus_but_autoconf_is_cheating_us}"], [
AC_MSG_NOTICE([Test skipped due to lack of a C++ compiler.])
],
@@ -1342,6 +1365,10 @@ AC_CHECK_HEADERS(syscall.h)
AC_CHECK_HEADERS(time.h)
AC_CHECK_HEADERS(ucontext.h)
AC_CHECK_HEADERS(utime.h)
+AC_CHECK_HEADERS(sys/epoll.h)
+AC_CHECK_HEADERS(sys/event.h)
+AC_CHECK_HEADERS(stdckdint.h)
+
AS_CASE("$target_cpu", [x64|x86_64|i[3-6]86*], [
AC_CHECK_HEADERS(x86intrin.h)
])
@@ -1414,7 +1441,7 @@ AC_SYS_LARGEFILE
# which is not added by AC_SYS_LARGEFILE.
AS_IF([test x"$enable_largefile" != xno], [
AS_CASE(["$target_os"], [solaris*], [
- AC_MSG_CHECKING([wheather _LARGEFILE_SOURCE should be defined])
+ AC_MSG_CHECKING([whether _LARGEFILE_SOURCE should be defined])
AS_CASE(["${ac_cv_sys_file_offset_bits}:${ac_cv_sys_large_files}"],
["64:"|"64:no"|"64:unknown"], [
# insert _LARGEFILE_SOURCE before _FILE_OFFSET_BITS line
@@ -1475,6 +1502,25 @@ RUBY_CHECK_SIZEOF(float)
RUBY_CHECK_SIZEOF(double)
RUBY_CHECK_SIZEOF(time_t, [long "long long"], [], [@%:@include <time.h>])
RUBY_CHECK_SIZEOF(clock_t, [], [], [@%:@include <time.h>])
+AC_SUBST(X_BUILTIN_BINARY, yes)
+AS_IF([test "$cross_compiling" = yes],
+[dnl miniruby cannot run if cross compiling
+ X_BUILTIN_BINARY=no
+],
+[
+ AS_CASE([ac_cv_sizeof_voidp],
+ [[1-9]*], [dnl fixed value
+ ],
+ [
+ AC_CACHE_CHECK([word size], [rb_cv_word_size],
+ [for w in 4 8; do
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@if SIZEOF_VOIDP != ${w}
+ @%:@error SIZEOF_VOIDP
+ @%:@endif]])], [rb_cv_word_size=${w}; break])
+ done])
+ AS_IF([test -z $rb_cv_word_size], [X_BUILTIN_BINARY=no])
+ ])
+])
AC_CACHE_CHECK(packed struct attribute, rb_cv_packed_struct,
[rb_cv_packed_struct=no
@@ -1730,7 +1776,7 @@ AC_CACHE_CHECK(for function name string predefined identifier,
[AS_CASE(["$target_os"],[openbsd*],[
rb_cv_function_name_string=__func__
],[
- rb_cv_function_name_string=no
+ rb_cv_function_name_string=no
RUBY_WERROR_FLAG([
for func in __func__ __FUNCTION__; do
AC_LINK_IFELSE([AC_LANG_PROGRAM([[@%:@include <stdio.h>]],
@@ -1738,7 +1784,8 @@ AC_CACHE_CHECK(for function name string predefined identifier,
[rb_cv_function_name_string=$func
break])
done
- ])])]
+ ])
+ ])]
)
AS_IF([test "$rb_cv_function_name_string" != no], [
AC_DEFINE_UNQUOTED(RUBY_FUNCTION_NAME_STRING, [$rb_cv_function_name_string])
@@ -2062,7 +2109,6 @@ AC_CHECK_FUNCS(gettimeofday) # for making ac_cv_func_gettimeofday
AC_CHECK_FUNCS(getuid)
AC_CHECK_FUNCS(getuidx)
AC_CHECK_FUNCS(gmtime_r)
-AC_CHECK_FUNCS(grantpt)
AC_CHECK_FUNCS(initgroups)
AC_CHECK_FUNCS(ioctl)
AC_CHECK_FUNCS(isfinite)
@@ -2079,6 +2125,7 @@ AC_CHECK_FUNCS(lstat)
AC_CHECK_FUNCS(lutimes)
AC_CHECK_FUNCS(malloc_usable_size)
AC_CHECK_FUNCS(malloc_size)
+AC_CHECK_FUNCS(malloc_trim)
AC_CHECK_FUNCS(mblen)
AC_CHECK_FUNCS(memalign)
AC_CHECK_FUNCS(memset_s)
@@ -2132,6 +2179,7 @@ AC_CHECK_FUNCS(sigaction)
AC_CHECK_FUNCS(sigaltstack)
AC_CHECK_FUNCS(sigprocmask)
AC_CHECK_FUNCS(sinh)
+AC_CHECK_FUNCS(snprintf)
AC_CHECK_FUNCS(spawnv)
AC_CHECK_FUNCS(symlink)
AC_CHECK_FUNCS(syscall)
@@ -2156,9 +2204,6 @@ AC_CHECK_FUNCS(__sinpi)
AS_IF([test "x$ac_cv_member_struct_statx_stx_btime" = xyes],
[AC_CHECK_FUNCS(statx)])
-AS_CASE(["$ac_cv_func_memset_s:$ac_cv_func_qsort_s"], [*yes*],
- [RUBY_DEFINE_IF([!defined __STDC_WANT_LIB_EXT1__], [__STDC_WANT_LIB_EXT1__], 1)])
-
AS_IF([test "$ac_cv_func_getcwd" = yes], [
AC_CACHE_CHECK(if getcwd allocates buffer if NULL is given, [rb_cv_getcwd_malloc],
[AC_RUN_IFELSE([AC_LANG_SOURCE([[
@@ -2227,6 +2272,27 @@ RUBY_CHECK_BUILTIN_FUNC(__builtin_types_compatible_p, [__builtin_types_compatibl
RUBY_CHECK_BUILTIN_FUNC(__builtin_trap, [__builtin_trap()])
RUBY_CHECK_BUILTIN_FUNC(__builtin_expect, [__builtin_expect(0, 0)])
+AS_IF([test "$rb_cv_builtin___builtin_mul_overflow" != no], [
+ AC_CACHE_CHECK(for __builtin_mul_overflow with long long arguments, rb_cv_use___builtin_mul_overflow_long_long, [
+ AC_LINK_IFELSE([AC_LANG_SOURCE([[
+#pragma clang optimize off
+
+int
+main(void)
+{
+ long long x = 0, y;
+ __builtin_mul_overflow(x, x, &y);
+
+ return 0;
+}
+]])],
+ rb_cv_use___builtin_mul_overflow_long_long=yes,
+ rb_cv_use___builtin_mul_overflow_long_long=no)])
+])
+AS_IF([test "$rb_cv_use___builtin_mul_overflow_long_long" = yes], [
+ AC_DEFINE(USE___BUILTIN_MUL_OVERFLOW_LONG_LONG, 1)
+])
+
AS_IF([test "$ac_cv_func_qsort_r" != no], [
AC_CACHE_CHECK(whether qsort_r is GNU version, rb_cv_gnu_qsort_r,
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
@@ -2614,6 +2680,9 @@ AS_CASE([$coroutine_type], [yes|''], [
[*86-mingw*], [
coroutine_type=win32
],
+ [aarch64-mingw*], [
+ coroutine_type=arm64
+ ],
[arm*-linux*], [
coroutine_type=arm32
],
@@ -2912,8 +2981,8 @@ AS_IF([test "x$ac_cv_func_ioctl" = xyes], [
}
-: "runtime section" && {
-dnl wheather use dln_a_out or not
+[begin]_group "runtime section" && {
+dnl whether use dln_a_out or not
AC_ARG_WITH(dln-a-out,
AS_HELP_STRING([--with-dln-a-out], [dln_a_out is deprecated]),
[
@@ -2948,14 +3017,6 @@ LIBEXT=a
AC_SUBST(DLDFLAGS)dnl
AC_SUBST(ARCH_FLAG)dnl
-AC_SUBST(RJIT_CC)dnl
-AS_CASE(["$GCC:$target_os"],
- [yes:aix*], [rjit_std_cflag="-std=gnu99"],
- [rjit_std_cflag=])
-AC_SUBST(RJIT_CFLAGS, [${RJIT_CFLAGS-"-w ${rjit_std_cflag} ${orig_cflags}"}])dnl
-AC_SUBST(RJIT_OPTFLAGS, [${RJIT_OPTFLAGS-'$(optflags)'}])dnl
-AC_SUBST(RJIT_DEBUGFLAGS, [${RJIT_DEBUGFLAGS-'$(debugflags)'}])dnl
-AC_SUBST(RJIT_LDSHARED)dnl
AC_SUBST(STATIC)dnl
AC_SUBST(CCDLFLAGS)dnl
@@ -3069,11 +3130,10 @@ AC_SUBST(EXTOBJS)
[darwin*], [ : ${LDSHARED='$(CC) -dynamic -bundle'}
: ${DLDSHARED='$(CC) -dynamiclib'}
: ${LDFLAGS=""}
- : ${LIBPATHENV=DYLD_FALLBACK_LIBRARY_PATH}
+ : ${LIBPATHENV=DYLD_LIBRARY_PATH}
: ${PRELOADENV=DYLD_INSERT_LIBRARIES}
AS_IF([test x"$enable_shared" = xyes], [
- # Resolve symbols from libruby.dylib when --enable-shared
- EXTDLDFLAGS='$(LIBRUBYARG_SHARED)'
+ # Resolve symbols from libruby.dylib in $(LIBS) when --enable-shared
], [test "x$EXTSTATIC" = x], [
# When building exts as bundles, a mach-o bundle needs to know its loader
# program to bind symbols from the ruby executable
@@ -3109,6 +3169,7 @@ AC_SUBST(EXTOBJS)
[hiuxmpp], [ : ${LDSHARED='$(LD) -r'}],
[atheos*], [ : ${LDSHARED='$(CC) -shared'}
rb_cv_dlopen=yes],
+ [wasi*], [ : ${LDSHARED='$(LD) -shared -Xlinker --export-dynamic'}],
[ : ${LDSHARED='$(LD)'}])
AC_MSG_RESULT($rb_cv_dlopen)
}
@@ -3507,7 +3568,7 @@ AS_CASE("$enable_shared", [yes], [
RUBY_APPEND_OPTIONS(LIBRUBY_DLDFLAGS, ['-Wl,-soname,$(LIBRUBY_SONAME)' "$LDFLAGS_OPTDIR"])
LIBRUBY_ALIASES='$(LIBRUBY_SONAME) lib$(RUBY_SO_NAME).$(SOEXT)'
AS_IF([test "$load_relative" = yes], [
- libprefix="'\$\${ORIGIN}/../${libdir_basename}'"
+ libprefix="'\$\${ORIGIN}/../${multiarch+../../}${libdir_basename}'"
LIBRUBY_RPATHFLAGS="-Wl,-rpath,${libprefix}"
LIBRUBY_RELATIVE=yes
])
@@ -3519,7 +3580,7 @@ AS_CASE("$enable_shared", [yes], [
LIBRUBY_SO="$LIBRUBY_SO.\$(TEENY)"
LIBRUBY_ALIASES=''
], [test "$load_relative" = yes], [
- libprefix="'\$\$ORIGIN/../${libdir_basename}'"
+ libprefix="'\$\$ORIGIN/../${multiarch+../../}${libdir_basename}'"
LIBRUBY_RPATHFLAGS="-Wl,-rpath,${libprefix}"
LIBRUBY_RELATIVE=yes
])
@@ -3543,7 +3604,7 @@ AS_CASE("$enable_shared", [yes], [
LIBRUBY_ALIASES='$(LIBRUBY_SONAME) lib$(RUBY_SO_NAME).$(SOEXT)'
RUBY_APPEND_OPTIONS(LIBRUBY_DLDFLAGS, ["${linker_flag}-h${linker_flag:+,}"'$(@F)'])
AS_IF([test "$load_relative" = yes], [
- libprefix="'\$\$ORIGIN/../${libdir_basename}'"
+ libprefix="'\$\$ORIGIN/../${multiarch+../../}${libdir_basename}'"
LIBRUBY_RPATHFLAGS="-R${libprefix}"
LIBRUBY_RELATIVE=yes
], [
@@ -3560,7 +3621,7 @@ AS_CASE("$enable_shared", [yes], [
LIBRUBY_SONAME='$(LIBRUBY_SO)'
LIBRUBY_ALIASES='lib$(RUBY_INSTALL_NAME).$(SOEXT)'
AS_IF([test "$load_relative" = yes], [
- libprefix="@executable_path/../${libdir_basename}"
+ libprefix="@executable_path/../${multiarch+../../}${libdir_basename}"
LIBRUBY_RELATIVE=yes
])
LIBRUBY_DLDFLAGS="$LIBRUBY_DLDFLAGS -install_name ${libprefix}"'/$(LIBRUBY_SONAME)'
@@ -3699,9 +3760,10 @@ AS_IF([test x"$gcov" = xyes], [
])
RUBY_SETJMP_TYPE
+RUBY_SHARED_GC
}
-: "build section" && {
+[begin]_group "installation section" && {
dnl build rdoc index if requested
RDOCTARGET=""
CAPITARGET=""
@@ -3749,6 +3811,19 @@ AC_SUBST(CAPITARGET)
AS_CASE(["$RDOCTARGET:$CAPITARGET"],[nodoc:nodoc],[INSTALLDOC=nodoc],[INSTALLDOC=all])
AC_SUBST(INSTALLDOC)
+AC_ARG_ENABLE(install-static-library,
+ AS_HELP_STRING([--disable-install-static-library], [do not install static ruby library]),
+ [INSTALL_STATIC_LIBRARY=$enableval
+ AS_IF([test x"$enable_shared" = xno -a x"$INSTALL_STATIC_LIBRARY" = xno],
+ [AC_MSG_ERROR([must install either static or shared library])],
+ [])],
+ AS_IF([test x"$enable_shared" = xyes],
+ [INSTALL_STATIC_LIBRARY=no],
+ [INSTALL_STATIC_LIBRARY=yes]))
+AC_SUBST(INSTALL_STATIC_LIBRARY)
+}
+
+[begin]_group "JIT section" && {
AC_CHECK_PROG(RUSTC, [rustc], [rustc], [no]) dnl no ac_tool_prefix
dnl check if rustc is recent enough to build YJIT (rustc >= 1.58.0)
@@ -3859,49 +3934,42 @@ AC_SUBST(CARGO_BUILD_ARGS)dnl for selecting Rust build profiles
AC_SUBST(YJIT_LIBS)dnl for optionally building the Rust parts of YJIT
AC_SUBST(YJIT_OBJ)dnl for optionally building the C parts of YJIT
-dnl Currently, RJIT only supports Unix x86_64 platforms.
+dnl RJIT supports only x86_64 platforms, but allows arm64/aarch64 for custom JITs.
RJIT_TARGET_OK=no
AS_IF([test "$cross_compiling" = no],
AS_CASE(["$target_cpu-$target_os"],
[*android*], [
RJIT_TARGET_OK=no
],
- [x86_64-darwin*], [
+ [arm64-darwin*|aarch64-darwin*|x86_64-darwin*], [
RJIT_TARGET_OK=yes
],
- [x86_64-*linux*], [
+ [arm64-*linux*|aarch64-*linux*|x86_64-*linux*], [
RJIT_TARGET_OK=yes
],
- [x86_64-*bsd*], [
+ [arm64-*bsd*|aarch64-*bsd*|x86_64-*bsd*], [
RJIT_TARGET_OK=yes
]
)
)
-dnl Build RJIT on Unix x86_64 platforms or if --enable-rjit is specified.
+dnl Build RJIT on supported platforms or if --enable-rjit is specified.
AC_ARG_ENABLE(rjit,
AS_HELP_STRING([--enable-rjit],
[enable pure-Ruby JIT compiler. enabled by default on Unix x86_64 platforms]),
[RJIT_SUPPORT=$enableval],
- [AS_CASE(["$YJIT_TARGET_OK"],
+ [AS_CASE(["$RJIT_TARGET_OK"],
[yes], [RJIT_SUPPORT=yes],
[RJIT_SUPPORT=no]
)]
)
AS_CASE(["$RJIT_SUPPORT"],
-[yes|dev|disasm], [
+[yes|dev], [
AS_CASE(["$RJIT_SUPPORT"],
[dev], [
# Link libcapstone for --rjit-dump-disasm
AC_CHECK_LIB([capstone], [cs_disasm])
-
- # Enable extra stats (vm_insns_count, ratio_in_rjit)
- AC_DEFINE(RJIT_STATS, 1)
- ],
- [disasm], [
- # Link libcapstone for --rjit-dump-disasm
- AC_CHECK_LIB([capstone], [cs_disasm])
])
AC_DEFINE(USE_RJIT, 1)
@@ -3910,18 +3978,9 @@ AS_CASE(["$RJIT_SUPPORT"],
])
AC_SUBST(RJIT_SUPPORT)
+}
-AC_ARG_ENABLE(install-static-library,
- AS_HELP_STRING([--disable-install-static-library], [do not install static ruby library]),
- [INSTALL_STATIC_LIBRARY=$enableval
- AS_IF([test x"$enable_shared" = xno -a x"$INSTALL_STATIC_LIBRARY" = xno],
- [AC_MSG_ERROR([must install either static or shared library])],
- [])],
- AS_IF([test x"$enable_shared" = xyes],
- [INSTALL_STATIC_LIBRARY=no],
- [INSTALL_STATIC_LIBRARY=yes]))
-AC_SUBST(INSTALL_STATIC_LIBRARY)
-
+[begin]_group "build section" && {
AC_CACHE_CHECK([for prefix of external symbols], rb_cv_symbol_prefix, [
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[extern void conftest_external(void) {}]], [[]])],[
rb_cv_symbol_prefix=`$NM conftest.$ac_objext |
@@ -3932,6 +3991,23 @@ AC_CACHE_CHECK([for prefix of external symbols], rb_cv_symbol_prefix, [
])
SYMBOL_PREFIX="$rb_cv_symbol_prefix"
test "x$SYMBOL_PREFIX" = xNONE && SYMBOL_PREFIX=''
+
+AS_IF([test x"$enable_shared" = xyes], [
+ AC_CACHE_CHECK([for default symbols in empty shared library], rb_cv_symbols_in_emptylib, [
+ save_CC="$CC"
+ eval CC=\"`printf "%s" "${DLDSHARED}" | sed ['s/\$(CC)/${CC}/']`\"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM()],[
+ rb_cv_symbols_in_emptylib=`$NM -Pgp conftest$ac_exeext |
+ sed ["/ [A-TV-Z] .*/!d;s///;s/^${SYMBOL_PREFIX}//;/^main$/d"]`
+ ])
+ set dummy ${rb_cv_symbols_in_emptylib}
+ shift
+ rb_cv_symbols_in_emptylib="$*"
+ CC="$save_CC"
+ ])
+])
+AC_SUBST(XSYMBOLS_IN_EMPTYLIB, "${rb_cv_symbols_in_emptylib}")
+
DLNOBJ=dln.o
AC_ARG_ENABLE(dln,
AS_HELP_STRING([--disable-dln], [disable dynamic link feature]),
@@ -4109,8 +4185,9 @@ AS_IF([test "${universal_binary-no}" = yes ], [
const char arch[[]] = __ARCHITECTURE__;]], [[puts(arch);]])],
[rb_cv_architecture_available=yes], [rb_cv_architecture_available=no]))
])
+}
-: ${RJIT_LDSHARED=`echo "$LDSHARED" | sed ['s|\$(LD)|'"${LD}"'|g;s|\$(CC)|$(RJIT_CC)|g']`}
+[end]_group
MAINLIBS="$LIBS"
LIBS=$ORIG_LIBS
@@ -4176,6 +4253,7 @@ AC_SUBST(MINIOBJS)
AC_SUBST(THREAD_MODEL)
AC_SUBST(COROUTINE_TYPE, ${coroutine_type})
AC_SUBST(PLATFORM_DIR)
+AC_SUBST(USE_LLVM_WINDRES)
firstmf=`echo $FIRSTMAKEFILE | sed 's/:.*//'`
firsttmpl=`echo $FIRSTMAKEFILE | sed 's/.*://'`
@@ -4327,6 +4405,10 @@ AS_IF([test "${universal_binary-no}" = yes ], [
arch="${target_cpu}-${target_os}"
])
AC_DEFINE_UNQUOTED(RUBY_PLATFORM, "$arch")
+
+ AS_IF([test "$arch" = "s390x-linux"], [
+ AC_DEFINE_UNQUOTED(USE_MN_THREADS, 0)
+ ])
])
unset sitearch
@@ -4431,7 +4513,7 @@ guard=INCLUDE_RUBY_CONFIG_H
{
echo "#ifndef $guard"
echo "#define $guard 1"
- grep -v "^#define PACKAGE_" confdefs.h
+ sed "/^@%:@define PACKAGE_/d;s/ *$//" confdefs.h
echo "#endif /* $guard */"
} | tr -d '\015' |
(
@@ -4549,7 +4631,6 @@ AC_SUBST(DESTDIR)
AC_OUTPUT
}
-}
AS_IF([test "$silent" = yes], [], [
AS_IF([${FOLD+:} false], [], [
@@ -4586,6 +4667,7 @@ config_summary "target OS" "$target_os"
config_summary "compiler" "$CC"
config_summary "with thread" "$THREAD_MODEL"
config_summary "with coroutine" "$coroutine_type"
+config_summary "with shared GC" "$with_shared_gc"
config_summary "enable shared libs" "$ENABLE_SHARED"
config_summary "dynamic library ext" "$DLEXT"
config_summary "CFLAGS" "$cflags"
diff --git a/cont.c b/cont.c
index 4d1b9f0b7a..8f222dfef8 100644
--- a/cont.c
+++ b/cont.c
@@ -71,8 +71,6 @@ static VALUE rb_cFiberPool;
#define FIBER_POOL_ALLOCATION_FREE
#endif
-#define jit_cont_enabled (rb_rjit_enabled || rb_yjit_enabled_p())
-
enum context_type {
CONTINUATION_CONTEXT = 0,
FIBER_CONTEXT = 1
@@ -277,6 +275,12 @@ struct rb_fiber_struct {
static struct fiber_pool shared_fiber_pool = {NULL, NULL, 0, 0, 0, 0};
+void
+rb_free_shared_fiber_pool(void)
+{
+ xfree(shared_fiber_pool.allocations);
+}
+
static ID fiber_initialize_keywords[3] = {0};
/*
@@ -792,6 +796,9 @@ static inline void
ec_switch(rb_thread_t *th, rb_fiber_t *fiber)
{
rb_execution_context_t *ec = &fiber->cont.saved_ec;
+#ifdef RUBY_ASAN_ENABLED
+ ec->machine.asan_fake_stack_handle = asan_get_thread_fake_stack_handle();
+#endif
rb_ractor_set_current_ec(th->ractor, th->ec = ec);
// ruby_current_execution_context_ptr = th->ec = ec;
@@ -1019,13 +1026,8 @@ cont_mark(void *ptr)
cont->machine.stack + cont->machine.stack_size);
}
else {
- /* fiber */
- const rb_fiber_t *fiber = (rb_fiber_t*)cont;
-
- if (!FIBER_TERMINATED_P(fiber)) {
- rb_gc_mark_locations(cont->machine.stack,
- cont->machine.stack + cont->machine.stack_size);
- }
+ /* fiber machine context is marked as part of rb_execution_context_mark, no need to
+ * do anything here. */
}
}
@@ -1062,10 +1064,8 @@ cont_free(void *ptr)
RUBY_FREE_UNLESS_NULL(cont->saved_vm_stack.ptr);
- if (jit_cont_enabled) {
- VM_ASSERT(cont->jit_cont != NULL);
- jit_cont_free(cont->jit_cont);
- }
+ VM_ASSERT(cont->jit_cont != NULL);
+ jit_cont_free(cont->jit_cont);
/* free rb_cont_t or rb_fiber_t */
ruby_xfree(ptr);
RUBY_FREE_LEAVE("cont");
@@ -1294,26 +1294,42 @@ rb_jit_cont_each_iseq(rb_iseq_callback callback, void *data)
if (cont->ec->vm_stack == NULL)
continue;
- const rb_control_frame_t *cfp;
- for (cfp = RUBY_VM_END_CONTROL_FRAME(cont->ec) - 1; ; cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) {
- const rb_iseq_t *iseq;
- if (cfp->pc && (iseq = cfp->iseq) != NULL && imemo_type((VALUE)iseq) == imemo_iseq) {
- callback(iseq, data);
+ const rb_control_frame_t *cfp = cont->ec->cfp;
+ while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(cont->ec, cfp)) {
+ if (cfp->pc && cfp->iseq && imemo_type((VALUE)cfp->iseq) == imemo_iseq) {
+ callback(cfp->iseq, data);
}
+ cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
+ }
+ }
+}
+
+#if USE_YJIT
+// Update the jit_return of all CFPs to leave_exit unless it's leave_exception or not set.
+// This prevents jit_exec_exception from jumping to the caller after invalidation.
+void
+rb_yjit_cancel_jit_return(void *leave_exit, void *leave_exception)
+{
+ struct rb_jit_cont *cont;
+ for (cont = first_jit_cont; cont != NULL; cont = cont->next) {
+ if (cont->ec->vm_stack == NULL)
+ continue;
- if (cfp == cont->ec->cfp)
- break; // reached the most recent cfp
+ const rb_control_frame_t *cfp = cont->ec->cfp;
+ while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(cont->ec, cfp)) {
+ if (cfp->jit_return && cfp->jit_return != leave_exception) {
+ ((rb_control_frame_t *)cfp)->jit_return = leave_exit;
+ }
+ cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
}
}
}
+#endif
// Finish working with jit_cont.
void
rb_jit_cont_finish(void)
{
- if (!jit_cont_enabled)
- return;
-
struct rb_jit_cont *cont, *next;
for (cont = first_jit_cont; cont != NULL; cont = next) {
next = cont->next;
@@ -1326,9 +1342,8 @@ static void
cont_init_jit_cont(rb_context_t *cont)
{
VM_ASSERT(cont->jit_cont == NULL);
- if (jit_cont_enabled) {
- cont->jit_cont = jit_cont_new(&(cont->saved_ec));
- }
+ // We always allocate this since YJIT may be enabled later
+ cont->jit_cont = jit_cont_new(&(cont->saved_ec));
}
struct rb_execution_context_struct *
@@ -1375,15 +1390,11 @@ rb_fiberptr_blocking(struct rb_fiber_struct *fiber)
return fiber->blocking;
}
-// Start working with jit_cont.
+// Initialize the jit_cont_lock
void
rb_jit_cont_init(void)
{
- if (!jit_cont_enabled)
- return;
-
rb_native_mutex_initialize(&jit_cont_lock);
- cont_init_jit_cont(&GET_EC()->fiber_ptr->cont);
}
#if 0
@@ -1555,11 +1566,10 @@ fiber_setcontext(rb_fiber_t *new_fiber, rb_fiber_t *old_fiber)
}
}
- /* exchange machine_stack_start between old_fiber and new_fiber */
+ /* these values are used in rb_gc_mark_machine_context to mark the fiber's stack. */
old_fiber->cont.saved_ec.machine.stack_start = th->ec->machine.stack_start;
+ old_fiber->cont.saved_ec.machine.stack_end = FIBER_TERMINATED_P(old_fiber) ? NULL : th->ec->machine.stack_end;
- /* old_fiber->machine.stack_end should be NULL */
- old_fiber->cont.saved_ec.machine.stack_end = NULL;
// if (DEBUG) fprintf(stderr, "fiber_setcontext: %p[%p] -> %p[%p]\n", (void*)old_fiber, old_fiber->stack.base, (void*)new_fiber, new_fiber->stack.base);
@@ -1762,6 +1772,13 @@ rb_callcc(VALUE self)
return rb_yield(val);
}
}
+#ifdef RUBY_ASAN_ENABLED
+/* callcc can't possibly work with ASAN; see bug #20273. Also this function
+ * definition below avoids a "defined and not used" warning. */
+MAYBE_UNUSED(static void notusing_callcc(void)) { rb_callcc(Qnil); }
+# define rb_callcc rb_f_notimplement
+#endif
+
static VALUE
make_passing_arg(int argc, const VALUE *argv)
@@ -2118,7 +2135,7 @@ rb_fiber_storage_get(VALUE self)
static int
fiber_storage_validate_each(VALUE key, VALUE value, VALUE _argument)
{
- rb_check_id(&key);
+ Check_Type(key, T_SYMBOL);
return ST_CONTINUE;
}
@@ -2190,8 +2207,7 @@ rb_fiber_storage_set(VALUE self, VALUE value)
static VALUE
rb_fiber_storage_aref(VALUE class, VALUE key)
{
- ID id = rb_check_id(&key);
- if (!id) return Qnil;
+ Check_Type(key, T_SYMBOL);
VALUE storage = fiber_storage_get(fiber_current(), FALSE);
if (storage == Qnil) return Qnil;
@@ -2212,8 +2228,7 @@ rb_fiber_storage_aref(VALUE class, VALUE key)
static VALUE
rb_fiber_storage_aset(VALUE class, VALUE key, VALUE value)
{
- ID id = rb_check_id(&key);
- if (!id) return Qnil;
+ Check_Type(key, T_SYMBOL);
VALUE storage = fiber_storage_get(fiber_current(), value != Qnil);
if (storage == Qnil) return Qnil;
@@ -2367,7 +2382,7 @@ rb_fiber_initialize(int argc, VALUE* argv, VALUE self)
VALUE
rb_fiber_new_storage(rb_block_call_func_t func, VALUE obj, VALUE storage)
{
- return fiber_initialize(fiber_alloc(rb_cFiber), rb_proc_new(func, obj), rb_fiber_pool_default(Qnil), 1, storage);
+ return fiber_initialize(fiber_alloc(rb_cFiber), rb_proc_new(func, obj), rb_fiber_pool_default(Qnil), 0, storage);
}
VALUE
@@ -2501,7 +2516,6 @@ rb_fiber_start(rb_fiber_t *fiber)
rb_proc_t *proc;
enum ruby_tag_type state;
- int need_interrupt = TRUE;
VM_ASSERT(th->ec == GET_EC());
VM_ASSERT(FIBER_RESUMED_P(fiber));
@@ -2527,6 +2541,7 @@ rb_fiber_start(rb_fiber_t *fiber)
}
EC_POP_TAG();
+ int need_interrupt = TRUE;
VALUE err = Qfalse;
if (state) {
err = th->ec->errinfo;
@@ -2554,11 +2569,10 @@ rb_fiber_start(rb_fiber_t *fiber)
void
rb_threadptr_root_fiber_setup(rb_thread_t *th)
{
- rb_fiber_t *fiber = ruby_mimmalloc(sizeof(rb_fiber_t));
+ rb_fiber_t *fiber = ruby_mimcalloc(1, sizeof(rb_fiber_t));
if (!fiber) {
rb_bug("%s", strerror(errno)); /* ... is it possible to call rb_bug here? */
}
- MEMZERO(fiber, rb_fiber_t, 1);
fiber->cont.type = FIBER_CONTEXT;
fiber->cont.saved_ec.fiber_ptr = fiber;
fiber->cont.saved_ec.thread_ptr = th;
@@ -2566,10 +2580,6 @@ rb_threadptr_root_fiber_setup(rb_thread_t *th)
fiber->killed = 0;
fiber_status_set(fiber, FIBER_RESUMED); /* skip CREATED */
th->ec = &fiber->cont.saved_ec;
- // When rb_threadptr_root_fiber_setup is called for the first time, rb_rjit_enabled and
- // rb_yjit_enabled_p() are still false. So this does nothing and rb_jit_cont_init() that is
- // called later will take care of it. However, you still have to call cont_init_jit_cont()
- // here for other Ractors, which are not initialized by rb_jit_cont_init().
cont_init_jit_cont(&fiber->cont);
}
@@ -2580,12 +2590,12 @@ rb_threadptr_root_fiber_release(rb_thread_t *th)
/* ignore. A root fiber object will free th->ec */
}
else {
- rb_execution_context_t *ec = GET_EC();
+ rb_execution_context_t *ec = rb_current_execution_context(false);
VM_ASSERT(th->ec->fiber_ptr->cont.type == FIBER_CONTEXT);
VM_ASSERT(th->ec->fiber_ptr->cont.self == 0);
- if (th->ec == ec) {
+ if (ec && th->ec == ec) {
rb_ractor_set_current_ec(th->ractor, NULL);
}
fiber_free(th->ec->fiber_ptr);
@@ -2810,6 +2820,8 @@ fiber_blocking_yield(VALUE fiber_value)
rb_fiber_t *fiber = fiber_ptr(fiber_value);
rb_thread_t * volatile th = fiber->cont.saved_ec.thread_ptr;
+ VM_ASSERT(fiber->blocking == 0);
+
// fiber->blocking is `unsigned int : 1`, so we use it as a boolean:
fiber->blocking = 1;
@@ -3214,7 +3226,13 @@ rb_fiber_s_yield(int argc, VALUE *argv, VALUE klass)
static VALUE
fiber_raise(rb_fiber_t *fiber, VALUE exception)
{
- if (FIBER_SUSPENDED_P(fiber) && !fiber->yielding) {
+ if (fiber == fiber_current()) {
+ rb_exc_raise(exception);
+ }
+ else if (fiber->resuming_fiber) {
+ return fiber_raise(fiber->resuming_fiber, exception);
+ }
+ else if (FIBER_SUSPENDED_P(fiber) && !fiber->yielding) {
return fiber_transfer_kw(fiber, -1, &exception, RB_NO_KEYWORDS);
}
else {
@@ -3250,6 +3268,10 @@ rb_fiber_raise(VALUE fiber, int argc, const VALUE *argv)
* the exception, and the third parameter is an array of callback information.
* Exceptions are caught by the +rescue+ clause of <code>begin...end</code>
* blocks.
+ *
+ * Raises +FiberError+ if called on a Fiber belonging to another +Thread+.
+ *
+ * See Kernel#raise for more information.
*/
static VALUE
rb_fiber_m_raise(int argc, VALUE *argv, VALUE self)
@@ -3261,12 +3283,18 @@ rb_fiber_m_raise(int argc, VALUE *argv, VALUE self)
* call-seq:
* fiber.kill -> nil
*
- * Terminates +fiber+ by raising an uncatchable exception, returning
- * the terminated Fiber.
+ * Terminates the fiber by raising an uncatchable exception.
+ * It only terminates the given fiber and no other fiber, returning +nil+ to
+ * another fiber if that fiber was calling #resume or #transfer.
+ *
+ * <tt>Fiber#kill</tt> only interrupts another fiber when it is in Fiber.yield.
+ * If called on the current fiber then it raises that exception at the <tt>Fiber#kill</tt> call site.
*
* If the fiber has not been started, transition directly to the terminated state.
*
* If the fiber is already terminated, does nothing.
+ *
+ * Raises FiberError if called on a fiber belonging to another thread.
*/
static VALUE
rb_fiber_m_kill(VALUE self)
@@ -3282,7 +3310,8 @@ rb_fiber_m_kill(VALUE self)
else if (fiber->status != FIBER_TERMINATED) {
if (fiber_current() == fiber) {
fiber_check_killed(fiber);
- } else {
+ }
+ else {
fiber_raise(fiber_ptr(self), Qnil);
}
}
diff --git a/coroutine/amd64/Context.S b/coroutine/amd64/Context.S
index d50732adbc..056c276a31 100644
--- a/coroutine/amd64/Context.S
+++ b/coroutine/amd64/Context.S
@@ -13,29 +13,35 @@
.globl PREFIXED_SYMBOL(SYMBOL_PREFIX,coroutine_transfer)
PREFIXED_SYMBOL(SYMBOL_PREFIX,coroutine_transfer):
- # Save caller state
- pushq %rbp
- pushq %rbx
- pushq %r12
- pushq %r13
- pushq %r14
- pushq %r15
-
- # Save caller stack pointer
+ # Make space on the stack for 6 registers:
+ subq $48, %rsp
+
+ # Save caller state:
+ movq %rbp, 40(%rsp)
+ movq %rbx, 32(%rsp)
+ movq %r12, 24(%rsp)
+ movq %r13, 16(%rsp)
+ movq %r14, 8(%rsp)
+ movq %r15, (%rsp)
+
+ # Save caller stack pointer:
movq %rsp, (%rdi)
- # Restore callee stack pointer
+ # Restore callee stack pointer:
movq (%rsi), %rsp
# Restore callee state
- popq %r15
- popq %r14
- popq %r13
- popq %r12
- popq %rbx
- popq %rbp
-
- # Put the first argument into the return value
+ movq 40(%rsp), %rbp
+ movq 32(%rsp), %rbx
+ movq 24(%rsp), %r12
+ movq 16(%rsp), %r13
+ movq 8(%rsp), %r14
+ movq (%rsp), %r15
+
+ # Adjust stack pointer back:
+ addq $48, %rsp
+
+ # Put the first argument into the return value:
movq %rdi, %rax
# We pop the return address and jump to it
diff --git a/coroutine/arm64/Context.S b/coroutine/arm64/Context.S
index 07d50d30df..5251ab214d 100644
--- a/coroutine/arm64/Context.S
+++ b/coroutine/arm64/Context.S
@@ -18,11 +18,25 @@
.align 2
#endif
+## NOTE(PAC): Use we HINT mnemonics instead of PAC mnemonics to
+## keep compatibility with those assemblers that don't support PAC.
+##
+## See "Providing protection for complex software" for more details about PAC/BTI
+## https://developer.arm.com/architectures/learn-the-architecture/providing-protection-for-complex-software
+
.global PREFIXED_SYMBOL(SYMBOL_PREFIX,coroutine_transfer)
PREFIXED_SYMBOL(SYMBOL_PREFIX,coroutine_transfer):
+#if defined(__ARM_FEATURE_PAC_DEFAULT) && (__ARM_FEATURE_PAC_DEFAULT != 0)
+ # paciasp (it also acts as BTI landing pad, so no need to insert BTI also)
+ hint #25
+#elif defined(__ARM_FEATURE_BTI_DEFAULT) && (__ARM_FEATURE_BTI_DEFAULT != 0)
+ # For the case PAC is not enabled but BTI is.
+ # bti c
+ hint #34
+#endif
# Make space on the stack for caller registers
- sub sp, sp, 0xb0
+ sub sp, sp, 0xa0
# Save caller registers
stp d8, d9, [sp, 0x00]
@@ -36,9 +50,6 @@ PREFIXED_SYMBOL(SYMBOL_PREFIX,coroutine_transfer):
stp x27, x28, [sp, 0x80]
stp x29, x30, [sp, 0x90]
- # Save return address
- str x30, [sp, 0xa0]
-
# Save stack pointer to x0 (first argument)
mov x2, sp
str x2, [x0, 0]
@@ -59,15 +70,51 @@ PREFIXED_SYMBOL(SYMBOL_PREFIX,coroutine_transfer):
ldp x27, x28, [sp, 0x80]
ldp x29, x30, [sp, 0x90]
- # Load return address into x4
- ldr x4, [sp, 0xa0]
-
# Pop stack frame
- add sp, sp, 0xb0
+ add sp, sp, 0xa0
- # Jump to return address (in x4)
- ret x4
+#if defined(__ARM_FEATURE_PAC_DEFAULT) && (__ARM_FEATURE_PAC_DEFAULT != 0)
+ # autiasp: Authenticate x30 (LR) with SP and key A
+ hint #29
+#endif
+
+ # Jump to return address (in x30)
+ ret
#if defined(__linux__) && defined(__ELF__)
.section .note.GNU-stack,"",%progbits
#endif
+
+#if __ARM_FEATURE_BTI_DEFAULT != 0 || __ARM_FEATURE_PAC_DEFAULT != 0
+/* See "ELF for the Arm 64-bit Architecture (AArch64)"
+ https://github.com/ARM-software/abi-aa/blob/2023Q3/aaelf64/aaelf64.rst#program-property */
+# define GNU_PROPERTY_AARCH64_FEATURE_1_BTI (1<<0)
+# define GNU_PROPERTY_AARCH64_FEATURE_1_PAC (1<<1)
+
+# if __ARM_FEATURE_BTI_DEFAULT != 0
+# define BTI_FLAG GNU_PROPERTY_AARCH64_FEATURE_1_BTI
+# else
+# define BTI_FLAG 0
+# endif
+# if __ARM_FEATURE_PAC_DEFAULT != 0
+# define PAC_FLAG GNU_PROPERTY_AARCH64_FEATURE_1_PAC
+# else
+# define PAC_FLAG 0
+# endif
+
+ # The note section format is described by Note Section in Chapter 5
+ # of "System V Application Binary Interface, Edition 4.1".
+ .pushsection .note.gnu.property, "a"
+ .p2align 3
+ .long 0x4 /* Name size ("GNU\0") */
+ .long 0x10 /* Descriptor size */
+ .long 0x5 /* Type: NT_GNU_PROPERTY_TYPE_0 */
+ .asciz "GNU" /* Name */
+ # Begin descriptor
+ .long 0xc0000000 /* Property type: GNU_PROPERTY_AARCH64_FEATURE_1_AND */
+ .long 0x4 /* Property size */
+ .long (BTI_FLAG|PAC_FLAG)
+ .long 0x0 /* 8-byte alignment padding */
+ # End descriptor
+ .popsection
+#endif
diff --git a/coroutine/arm64/Context.h b/coroutine/arm64/Context.h
index 1472621f48..eb66fbea0f 100644
--- a/coroutine/arm64/Context.h
+++ b/coroutine/arm64/Context.h
@@ -17,7 +17,7 @@
#define COROUTINE __attribute__((noreturn)) void
-enum {COROUTINE_REGISTERS = 0xb0 / 8};
+enum {COROUTINE_REGISTERS = 0xa0 / 8};
#if defined(__SANITIZE_ADDRESS__)
#define COROUTINE_SANITIZE_ADDRESS
@@ -50,6 +50,20 @@ static inline void coroutine_initialize_main(struct coroutine_context * context)
context->stack_pointer = NULL;
}
+static inline void *ptrauth_sign_instruction_addr(void *addr, void *modifier) {
+#if defined(__ARM_FEATURE_PAC_DEFAULT) && __ARM_FEATURE_PAC_DEFAULT != 0
+ // Sign the given instruction address with the given modifier and key A
+ register void *r17 __asm("r17") = addr;
+ register void *r16 __asm("r16") = modifier;
+ // Use HINT mnemonic instead of PACIA1716 for compatibility with older assemblers.
+ __asm ("hint #8;" : "+r"(r17) : "r"(r16));
+ addr = r17;
+#else
+ // No-op if PAC is not enabled
+#endif
+ return addr;
+}
+
static inline void coroutine_initialize(
struct coroutine_context *context,
coroutine_start start,
@@ -66,12 +80,13 @@ static inline void coroutine_initialize(
// Stack grows down. Force 16-byte alignment.
char * top = (char*)stack + size;
- context->stack_pointer = (void**)((uintptr_t)top & ~0xF);
+ top = (char *)((uintptr_t)top & ~0xF);
+ context->stack_pointer = (void**)top;
context->stack_pointer -= COROUTINE_REGISTERS;
memset(context->stack_pointer, 0, sizeof(void*) * COROUTINE_REGISTERS);
- context->stack_pointer[0xa0 / 8] = (void*)start;
+ context->stack_pointer[0x98 / 8] = ptrauth_sign_instruction_addr((void*)start, (void*)top);
}
struct coroutine_context * coroutine_transfer(struct coroutine_context * current, struct coroutine_context * target);
diff --git a/coroutine/asyncify/Context.h b/coroutine/asyncify/Context.h
index 7dba829a1d..71791a4004 100644
--- a/coroutine/asyncify/Context.h
+++ b/coroutine/asyncify/Context.h
@@ -13,6 +13,7 @@
#include <stddef.h>
#include <stdio.h>
+#include <stdint.h>
#include "wasm/asyncify.h"
#include "wasm/machine.h"
#include "wasm/fiber.h"
@@ -47,10 +48,13 @@ static inline void coroutine_initialize_main(struct coroutine_context * context)
static inline void coroutine_initialize(struct coroutine_context *context, coroutine_start start, void *stack, size_t size)
{
- if (ASYNCIFY_CORO_DEBUG) fprintf(stderr, "[%s] entry (context = %p, stack = %p ... %p)\n", __func__, context, stack, (char *)stack + size);
+ // Linear stack pointer must be always aligned down to 16 bytes.
+ // https://github.com/WebAssembly/tool-conventions/blob/c74267a5897c1bdc9aa60adeaf41816387d3cd12/BasicCABI.md#the-linear-stack
+ uintptr_t sp = ((uintptr_t)stack + size) & ~0xF;
+ if (ASYNCIFY_CORO_DEBUG) fprintf(stderr, "[%s] entry (context = %p, stack = %p ... %p)\n", __func__, context, stack, (char *)sp);
rb_wasm_init_context(&context->fc, coroutine_trampoline, start, context);
// record the initial stack pointer position to restore it after resumption
- context->current_sp = (char *)stack + size;
+ context->current_sp = (char *)sp;
context->stack_base = stack;
context->size = size;
}
diff --git a/coroutine/ppc/Context.S b/coroutine/ppc/Context.S
index cdda93e179..e2431a9250 100644
--- a/coroutine/ppc/Context.S
+++ b/coroutine/ppc/Context.S
@@ -3,7 +3,7 @@
; Some relevant examples: https://github.com/gcc-mirror/gcc/blob/master/libphobos/libdruntime/config/powerpc/switchcontext.S
; https://github.com/gcc-mirror/gcc/blob/master/libgcc/config/rs6000/darwin-gpsave.S
; https://www.ibm.com/docs/en/aix/7.2?topic=epilogs-saving-gprs-only
-; ppc32 version may be re-written compactly with stmw/lwm, but the code wonʼt be faster, see: https://github.com/ruby/ruby/pull/5927#issuecomment-1139730541
+; ppc32 version may be re-written compactly with stmw/lwm, but the code won't be faster, see: https://github.com/ruby/ruby/pull/5927#issuecomment-1139730541
; Notice that this code is only for Darwin (macOS). Darwin ABI differs from AIX and ELF.
; To add support for AIX, *BSD or *Linux, please make separate implementations.
diff --git a/coroutine/ppc/Context.h b/coroutine/ppc/Context.h
index 1fce112579..8035d08556 100644
--- a/coroutine/ppc/Context.h
+++ b/coroutine/ppc/Context.h
@@ -13,7 +13,7 @@
enum {
COROUTINE_REGISTERS =
- 20 /* 19 general purpose registers (r13–r31) and 1 return address */
+ 20 /* 19 general purpose registers (r13-r31) and 1 return address */
+ 4 /* space for fiber_entry() to store the link register */
};
diff --git a/coroutine/ppc64/Context.h b/coroutine/ppc64/Context.h
index 3e6f77f55a..085b475ed5 100644
--- a/coroutine/ppc64/Context.h
+++ b/coroutine/ppc64/Context.h
@@ -12,7 +12,7 @@
enum {
COROUTINE_REGISTERS =
- 20 /* 19 general purpose registers (r13–r31) and 1 return address */
+ 20 /* 19 general purpose registers (r13-r31) and 1 return address */
+ 4 /* space for fiber_entry() to store the link register */
};
diff --git a/coroutine/win64/Context.h b/coroutine/win64/Context.h
index aaa4caeaf9..d85ebf8e0e 100644
--- a/coroutine/win64/Context.h
+++ b/coroutine/win64/Context.h
@@ -30,7 +30,7 @@ struct coroutine_context
typedef void(* coroutine_start)(struct coroutine_context *from, struct coroutine_context *self);
-void coroutine_trampoline();
+void coroutine_trampoline(void);
static inline void coroutine_initialize_main(struct coroutine_context * context) {
context->stack_pointer = NULL;
diff --git a/cygwin/GNUmakefile.in b/cygwin/GNUmakefile.in
index 43e92a27f0..192a8cc711 100644
--- a/cygwin/GNUmakefile.in
+++ b/cygwin/GNUmakefile.in
@@ -3,9 +3,14 @@ gnumake = yes
include Makefile
DLLWRAP = @DLLWRAP@ --target=$(target_os) --driver-name="$(CC)"
-windres-cpp := $(CPP) -xc
-windres-cpp := --preprocessor=$(firstword $(windres-cpp)) \
- $(addprefix --preprocessor-arg=,$(wordlist 2,$(words $(windres-cpp)),$(windres-cpp)))
+ifeq (@USE_LLVM_WINDRES@,yes) # USE_LLVM_WINDRES
+ # llvm-windres fails when preprocessor options are added
+ windres-cpp :=
+else
+ windres-cpp := $(CPP) -xc
+ windres-cpp := --preprocessor=$(firstword $(windres-cpp)) \
+ $(addprefix --preprocessor-arg=,$(wordlist 2,$(words $(windres-cpp)),$(windres-cpp)))
+endif
WINDRES = @WINDRES@ $(windres-cpp) -DRC_INVOKED
STRIP = @STRIP@
@@ -41,6 +46,7 @@ SOLIBS := $(DLL_BASE_NAME).res.$(OBJEXT) $(SOLIBS)
override EXTOBJS += $(if $(filter-out $(RUBYW_INSTALL_NAME),$(@:$(EXEEXT)=)),$(RUBY_INSTALL_NAME),$(@:$(EXEEXT)=)).res.$(OBJEXT)
RCFILES = $(RUBY_INSTALL_NAME).rc $(RUBYW_INSTALL_NAME).rc $(DLL_BASE_NAME).rc
RUBYDEF = $(DLL_BASE_NAME).def
+override LIBRUBY_FOR_LEAKED_GLOBALS := # DLL shows symbols from import library
ruby: $(PROGRAM)
rubyw: $(WPROGRAM)
diff --git a/darray.h b/darray.h
index 7183041f03..d24e3c3eb5 100644
--- a/darray.h
+++ b/darray.h
@@ -5,6 +5,8 @@
#include <stddef.h>
#include <stdlib.h>
+#include "internal/bits.h"
+
// Type for a dynamic array. Use to declare a dynamic array.
// It is a pointer so it fits in st_table nicely. Designed
// to be fairly type-safe.
@@ -37,12 +39,16 @@
//
#define rb_darray_ref(ary, idx) (&((ary)->data[(idx)]))
-// Copy a new element into the array. ptr_to_ary is evaluated multiple times.
-//
-// void rb_darray_append(rb_darray(T) *ptr_to_ary, T element);
-//
-#define rb_darray_append(ptr_to_ary, element) do { \
- rb_darray_ensure_space((ptr_to_ary), sizeof(**(ptr_to_ary)), \
+/* Copy a new element into the array. ptr_to_ary is evaluated multiple times.
+ *
+ * void rb_darray_append(rb_darray(T) *ptr_to_ary, T element);
+ */
+#define rb_darray_append(ptr_to_ary, element) \
+ rb_darray_append_impl(ptr_to_ary, element)
+
+#define rb_darray_append_impl(ptr_to_ary, element) do { \
+ rb_darray_ensure_space((ptr_to_ary), \
+ sizeof(**(ptr_to_ary)), \
sizeof((*(ptr_to_ary))->data[0])); \
rb_darray_set(*(ptr_to_ary), \
(*(ptr_to_ary))->meta.size, \
@@ -50,21 +56,6 @@
(*(ptr_to_ary))->meta.size++; \
} while (0)
-
-// Last element of the array
-//
-#define rb_darray_back(ary) ((ary)->data[(ary)->meta.size - 1])
-
-// Remove the last element of the array.
-//
-#define rb_darray_pop_back(ary) ((ary)->meta.size--)
-
-// Remove element at idx and replace it by the last element
-#define rb_darray_remove_unordered(ary, idx) do { \
- rb_darray_set(ary, idx, rb_darray_back(ary)); \
- rb_darray_pop_back(ary); \
-} while (0);
-
// Iterate over items of the array in a for loop
//
#define rb_darray_foreach(ary, idx_name, elem_ptr_var) \
@@ -75,28 +66,42 @@
#define rb_darray_for(ary, idx_name) \
for (size_t idx_name = 0; idx_name < rb_darray_size(ary); ++idx_name)
-// Make a dynamic array of a certain size. All bytes backing the elements are set to zero.
-//
-// Note that NULL is a valid empty dynamic array.
-//
-// void rb_darray_make(rb_darray(T) *ptr_to_ary, size_t size);
-//
+/* Make a dynamic array of a certain size. All bytes backing the elements are set to zero.
+ * Return 1 on success and 0 on failure.
+ *
+ * Note that NULL is a valid empty dynamic array.
+ *
+ * void rb_darray_make(rb_darray(T) *ptr_to_ary, size_t size);
+ */
#define rb_darray_make(ptr_to_ary, size) \
- rb_darray_make_impl((ptr_to_ary), size, sizeof(**(ptr_to_ary)), \
- sizeof((*(ptr_to_ary))->data[0]))
+ rb_darray_make_impl((ptr_to_ary), size, sizeof(**(ptr_to_ary)), sizeof((*(ptr_to_ary))->data[0]))
-#define rb_darray_data_ptr(ary) ((ary)->data)
+/* Resize the darray to a new capacity. The new capacity must be greater than
+ * or equal to the size of the darray.
+ *
+ * void rb_darray_resize_capa(rb_darray(T) *ptr_to_ary, size_t capa);
+ */
+#define rb_darray_resize_capa(ptr_to_ary, capa) \
+ rb_darray_resize_capa_impl((ptr_to_ary), capa, sizeof(**(ptr_to_ary)), sizeof((*(ptr_to_ary))->data[0]))
-// Set the size of the array to zero without freeing the backing memory.
-// Allows reusing the same array.
-//
-#define rb_darray_clear(ary) (ary->meta.size = 0)
+#define rb_darray_data_ptr(ary) ((ary)->data)
typedef struct rb_darray_meta {
size_t size;
size_t capa;
} rb_darray_meta_t;
+/* Set the size of the array to zero without freeing the backing memory.
+ * Allows reusing the same array. */
+static inline void
+rb_darray_clear(void *ary)
+{
+ rb_darray_meta_t *meta = ary;
+ if (meta) {
+ meta->size = 0;
+ }
+}
+
// Get the size of the dynamic array.
//
static inline size_t
@@ -115,13 +120,36 @@ rb_darray_capa(const void *ary)
return meta ? meta->capa : 0;
}
-// Free the dynamic array.
-//
+/* Free the dynamic array. */
static inline void
rb_darray_free(void *ary)
{
- rb_darray_meta_t *meta = ary;
- ruby_sized_xfree(ary, meta->capa);
+ xfree(ary);
+}
+
+/* Internal function. Resizes the capacity of a darray. The new capacity must
+ * be greater than or equal to the size of the darray. */
+static inline void
+rb_darray_resize_capa_impl(void *ptr_to_ary, size_t new_capa, size_t header_size, size_t element_size)
+{
+ rb_darray_meta_t **ptr_to_ptr_to_meta = ptr_to_ary;
+ rb_darray_meta_t *meta = *ptr_to_ptr_to_meta;
+
+ rb_darray_meta_t *new_ary = xrealloc(meta, new_capa * element_size + header_size);
+
+ if (meta == NULL) {
+ /* First allocation. Initialize size. On subsequence allocations
+ * realloc takes care of carrying over the size. */
+ new_ary->size = 0;
+ }
+
+ RUBY_ASSERT(new_ary->size <= new_capa);
+
+ new_ary->capa = new_capa;
+
+ // We don't have access to the type of the dynamic array in function context.
+ // Write out result with memcpy to avoid strict aliasing issue.
+ memcpy(ptr_to_ary, &new_ary, sizeof(new_ary));
}
// Internal function
@@ -138,21 +166,7 @@ rb_darray_ensure_space(void *ptr_to_ary, size_t header_size, size_t element_size
// Double the capacity
size_t new_capa = current_capa == 0 ? 1 : current_capa * 2;
- rb_darray_meta_t *doubled_ary = rb_xrealloc_mul_add(meta, new_capa, element_size, header_size);
- // rb_xrealloc functions guarantee that NULL is not returned
- assert(doubled_ary != NULL);
-
- if (meta == NULL) {
- // First allocation. Initialize size. On subsequence allocations
- // realloc takes care of carrying over the size.
- doubled_ary->size = 0;
- }
-
- doubled_ary->capa = new_capa;
-
- // We don't have access to the type of the dynamic array in function context.
- // Write out result with memcpy to avoid strict aliasing issue.
- memcpy(ptr_to_ary, &doubled_ary, sizeof(doubled_ary));
+ rb_darray_resize_capa_impl(ptr_to_ary, new_capa, header_size, element_size);
}
static inline void
@@ -164,9 +178,7 @@ rb_darray_make_impl(void *ptr_to_ary, size_t array_size, size_t header_size, siz
return;
}
- rb_darray_meta_t *meta = rb_xcalloc_mul_add(array_size, element_size, header_size);
- // rb_xcalloc functions guarantee that NULL is not returned
- assert(meta != NULL);
+ rb_darray_meta_t *meta = xcalloc(array_size * element_size + header_size, 1);
meta->size = array_size;
meta->capa = array_size;
diff --git a/debug.c b/debug.c
index b5cba590ba..4717a0bc9c 100644
--- a/debug.c
+++ b/debug.c
@@ -147,13 +147,21 @@ NODE *
ruby_debug_print_node(int level, int debug_level, const char *header, const NODE *node)
{
if (level < debug_level) {
- fprintf(stderr, "DBG> %s: %s (%u)\n", header,
- ruby_node_name(nd_type(node)), nd_line(node));
+ fprintf(stderr, "DBG> %s: %s (id: %d, line: %d, location: (%d,%d)-(%d,%d))\n",
+ header, ruby_node_name(nd_type(node)), nd_node_id(node), nd_line(node),
+ nd_first_lineno(node), nd_first_column(node),
+ nd_last_lineno(node), nd_last_column(node));
}
return (NODE *)node;
}
void
+ruby_debug_print_n(const NODE *node)
+{
+ ruby_debug_print_node(0, 1, "", node);
+}
+
+void
ruby_debug_breakpoint(void)
{
/* */
@@ -177,9 +185,9 @@ ruby_env_debug_option(const char *str, int len, void *arg)
int ov;
size_t retlen;
unsigned long n;
+#define NAME_MATCH(name) (len == sizeof(name) - 1 && strncmp(str, (name), len) == 0)
#define SET_WHEN(name, var, val) do { \
- if (len == sizeof(name) - 1 && \
- strncmp(str, (name), len) == 0) { \
+ if (NAME_MATCH(name)) { \
(var) = (val); \
return 1; \
} \
@@ -207,31 +215,31 @@ ruby_env_debug_option(const char *str, int len, void *arg)
--len; \
} \
if (len > 0) { \
- fprintf(stderr, "ignored "name" option: `%.*s'\n", len, str); \
+ fprintf(stderr, "ignored "name" option: '%.*s'\n", len, str); \
} \
} while (0)
#define SET_WHEN_UINT(name, vals, num, req) \
- if (NAME_MATCH_VALUE(name)) SET_UINT_LIST(name, vals, num);
+ if (NAME_MATCH_VALUE(name)) { \
+ if (!len) req; \
+ else SET_UINT_LIST(name, vals, num); \
+ return 1; \
+ }
- SET_WHEN("gc_stress", *ruby_initial_gc_stress_ptr, Qtrue);
- SET_WHEN("core", ruby_enable_coredump, 1);
- SET_WHEN("ci", ruby_on_ci, 1);
- if (NAME_MATCH_VALUE("rgengc")) {
- if (!len) ruby_rgengc_debug = 1;
- else SET_UINT_LIST("rgengc", &ruby_rgengc_debug, 1);
+ if (NAME_MATCH("gc_stress")) {
+ rb_gc_initial_stress_set(Qtrue);
return 1;
}
+ SET_WHEN("core", ruby_enable_coredump, 1);
+ SET_WHEN("ci", ruby_on_ci, 1);
+ SET_WHEN_UINT("rgengc", &ruby_rgengc_debug, 1, ruby_rgengc_debug = 1);
#if defined _WIN32
# if RUBY_MSVCRT_VERSION >= 80
SET_WHEN("rtc_error", ruby_w32_rtc_error, 1);
# endif
#endif
#if defined _WIN32 || defined __CYGWIN__
- if (NAME_MATCH_VALUE("codepage")) {
- if (!len) fprintf(stderr, "missing codepage argument");
- else SET_UINT_LIST("codepage", ruby_w32_codepage, numberof(ruby_w32_codepage));
- return 1;
- }
+ SET_WHEN_UINT("codepage", ruby_w32_codepage, numberof(ruby_w32_codepage),
+ fprintf(stderr, "missing codepage argument"));
#endif
return 0;
}
@@ -285,6 +293,12 @@ static const char *dlf_type_names[] = {
"func",
};
+#ifdef MAX_PATH
+#define DEBUG_LOG_MAX_PATH (MAX_PATH-1)
+#else
+#define DEBUG_LOG_MAX_PATH 255
+#endif
+
static struct {
char *mem;
unsigned int cnt;
@@ -292,6 +306,7 @@ static struct {
unsigned int filters_num;
bool show_pid;
rb_nativethread_lock_t lock;
+ char output_file[DEBUG_LOG_MAX_PATH+1];
FILE *output;
} debug_log;
@@ -393,7 +408,39 @@ setup_debug_log(void)
}
else {
ruby_debug_log_mode |= ruby_debug_log_file;
- if ((debug_log.output = fopen(log_config, "w")) == NULL) {
+
+ // pid extension with %p
+ unsigned long len = strlen(log_config);
+
+ for (unsigned long i=0, j=0; i<len; i++) {
+ const char c = log_config[i];
+
+ if (c == '%') {
+ i++;
+ switch (log_config[i]) {
+ case '%':
+ debug_log.output_file[j++] = '%';
+ break;
+ case 'p':
+ snprintf(debug_log.output_file + j, DEBUG_LOG_MAX_PATH - j, "%d", getpid());
+ j = strlen(debug_log.output_file);
+ break;
+ default:
+ fprintf(stderr, "can not parse RUBY_DEBUG_LOG filename: %s\n", log_config);
+ exit(1);
+ }
+ }
+ else {
+ debug_log.output_file[j++] = c;
+ }
+
+ if (j >= DEBUG_LOG_MAX_PATH) {
+ fprintf(stderr, "RUBY_DEBUG_LOG=%s is too long\n", log_config);
+ exit(1);
+ }
+ }
+
+ if ((debug_log.output = fopen(debug_log.output_file, "w")) == NULL) {
fprintf(stderr, "can not open %s for RUBY_DEBUG_LOG\n", log_config);
exit(1);
}
@@ -404,6 +451,10 @@ setup_debug_log(void)
(ruby_debug_log_mode & ruby_debug_log_memory) ? "[mem]" : "",
(ruby_debug_log_mode & ruby_debug_log_stderr) ? "[stderr]" : "",
(ruby_debug_log_mode & ruby_debug_log_file) ? "[file]" : "");
+ if (debug_log.output_file[0]) {
+ fprintf(stderr, "RUBY_DEBUG_LOG filename=%s\n", debug_log.output_file);
+ }
+
rb_nativethread_lock_initialize(&debug_log.lock);
setup_debug_log_filter();
@@ -570,10 +621,11 @@ ruby_debug_log(const char *file, int line, const char *func_name, const char *fm
// ractor information
if (ruby_single_main_ractor == NULL) {
rb_ractor_t *cr = th ? th->ractor : NULL;
+ rb_vm_t *vm = GET_VM();
if (r && len < MAX_DEBUG_LOG_MESSAGE_LEN) {
- r = snprintf(buff + len, MAX_DEBUG_LOG_MESSAGE_LEN - len, "\tr:#%d/%u",
- cr ? (int)rb_ractor_id(cr) : -1, GET_VM()->ractor.cnt);
+ r = snprintf(buff + len, MAX_DEBUG_LOG_MESSAGE_LEN - len, "\tr:#%d/%u (%u)",
+ cr ? (int)rb_ractor_id(cr) : -1, vm->ractor.cnt, vm->ractor.sched.running_cnt);
if (r < 0) rb_bug("ruby_debug_log returns %d", r);
len += r;
@@ -631,7 +683,7 @@ debug_log_dump(FILE *out, unsigned int n)
int index = current_index - size + i;
if (index < 0) index += MAX_DEBUG_LOG;
VM_ASSERT(index <= MAX_DEBUG_LOG);
- const char *mesg = RUBY_DEBUG_LOG_MEM_ENTRY(index);;
+ const char *mesg = RUBY_DEBUG_LOG_MEM_ENTRY(index);
fprintf(out, "%4u: %s\n", debug_log.cnt - size + i, mesg);
}
}
diff --git a/debug_counter.h b/debug_counter.h
index f1208d83a8..a8b95edded 100644
--- a/debug_counter.h
+++ b/debug_counter.h
@@ -100,6 +100,13 @@ RB_DEBUG_COUNTER(ccf_opt_block_call)
RB_DEBUG_COUNTER(ccf_opt_struct_aref)
RB_DEBUG_COUNTER(ccf_opt_struct_aset)
RB_DEBUG_COUNTER(ccf_super_method)
+RB_DEBUG_COUNTER(ccf_cfunc_other)
+RB_DEBUG_COUNTER(ccf_cfunc_only_splat)
+RB_DEBUG_COUNTER(ccf_cfunc_only_splat_kw)
+RB_DEBUG_COUNTER(ccf_iseq_bmethod)
+RB_DEBUG_COUNTER(ccf_noniseq_bmethod)
+RB_DEBUG_COUNTER(ccf_opt_send_complex)
+RB_DEBUG_COUNTER(ccf_opt_send_simple)
/*
* control frame push counts.
@@ -207,7 +214,6 @@ RB_DEBUG_COUNTER(gc_isptr_maybe)
* * [attr]
* * _ptr: R?? is not embed.
* * _embed: R?? is embed.
- * * _transient: R?? uses transient heap.
* * type specific attr.
* * str_shared: str is shared.
* * str_nofree: nofree
@@ -233,7 +239,6 @@ RB_DEBUG_COUNTER(obj_promote)
RB_DEBUG_COUNTER(obj_wb_unprotect)
RB_DEBUG_COUNTER(obj_obj_embed)
-RB_DEBUG_COUNTER(obj_obj_transient)
RB_DEBUG_COUNTER(obj_obj_ptr)
RB_DEBUG_COUNTER(obj_obj_too_complex)
@@ -244,7 +249,6 @@ RB_DEBUG_COUNTER(obj_str_nofree)
RB_DEBUG_COUNTER(obj_str_fstr)
RB_DEBUG_COUNTER(obj_ary_embed)
-RB_DEBUG_COUNTER(obj_ary_transient)
RB_DEBUG_COUNTER(obj_ary_ptr)
RB_DEBUG_COUNTER(obj_ary_extracapa)
/*
@@ -268,11 +272,9 @@ RB_DEBUG_COUNTER(obj_hash_g8)
RB_DEBUG_COUNTER(obj_hash_null)
RB_DEBUG_COUNTER(obj_hash_ar)
RB_DEBUG_COUNTER(obj_hash_st)
-RB_DEBUG_COUNTER(obj_hash_transient)
RB_DEBUG_COUNTER(obj_hash_force_convert)
RB_DEBUG_COUNTER(obj_struct_embed)
-RB_DEBUG_COUNTER(obj_struct_transient)
RB_DEBUG_COUNTER(obj_struct_ptr)
RB_DEBUG_COUNTER(obj_data_empty)
@@ -327,11 +329,6 @@ RB_DEBUG_COUNTER(heap_xmalloc)
RB_DEBUG_COUNTER(heap_xrealloc)
RB_DEBUG_COUNTER(heap_xfree)
-/* transient_heap */
-RB_DEBUG_COUNTER(theap_alloc)
-RB_DEBUG_COUNTER(theap_alloc_fail)
-RB_DEBUG_COUNTER(theap_evacuate)
-
// VM sync
RB_DEBUG_COUNTER(vm_sync_lock)
RB_DEBUG_COUNTER(vm_sync_lock_enter)
diff --git a/defs/gmake.mk b/defs/gmake.mk
index 2c03022434..c914b39690 100644
--- a/defs/gmake.mk
+++ b/defs/gmake.mk
@@ -8,14 +8,12 @@ MSPECOPT += $(if $(filter -j%,$(MFLAGS)),-j)
nproc = $(subst -j,,$(filter -j%,$(MFLAGS)))
ifeq ($(GITHUB_ACTIONS),true)
-override ACTIONS_GROUP = @echo "\#\#[group]$(patsubst yes-%,%,$@)"
-override ACTIONS_ENDGROUP = @echo "\#\#[endgroup]"
+# 93(bright yellow) is copied from .github/workflows/mingw.yml
+override ACTIONS_GROUP = @echo "::group::$(@:yes-%=%)"
+override ACTIONS_ENDGROUP = @echo "::endgroup::"
endif
ifneq ($(filter darwin%,$(target_os)),)
-# Remove debug option not to generate thousands of .dSYM
-RJIT_DEBUGFLAGS := $(filter-out -g%,$(RJIT_DEBUGFLAGS))
-
INSTRUBY_ENV += SDKROOT=
endif
INSTRUBY_ARGS += --gnumake
@@ -27,7 +25,7 @@ TEST_TARGETS := $(filter $(CHECK_TARGETS),$(MAKECMDGOALS))
TEST_DEPENDS := $(filter-out commit $(TEST_TARGETS),$(MAKECMDGOALS))
TEST_TARGETS := $(patsubst great,exam,$(TEST_TARGETS))
TEST_DEPENDS := $(filter-out great $(TEST_TARGETS),$(TEST_DEPENDS))
-TEST_TARGETS := $(patsubst exam,test-bundled-gems yes-test-bundler-parallel check,$(TEST_TARGETS))
+TEST_TARGETS := $(patsubst exam,test-bundled-gems test-bundler-parallel check,$(TEST_TARGETS))
TEST_TARGETS := $(patsubst check,test-syntax-suggest test-spec test-all test-tool test-short,$(TEST_TARGETS))
TEST_TARGETS := $(patsubst test-rubyspec,test-spec,$(TEST_TARGETS))
TEST_DEPENDS := $(filter-out exam check test-spec $(TEST_TARGETS),$(TEST_DEPENDS))
@@ -38,6 +36,7 @@ TEST_DEPENDS := $(filter-out test-all $(TEST_TARGETS),$(TEST_DEPENDS))
TEST_TARGETS := $(patsubst test,test-short,$(TEST_TARGETS))
TEST_DEPENDS := $(filter-out test $(TEST_TARGETS),$(TEST_DEPENDS))
TEST_TARGETS := $(patsubst test-short,btest-ruby test-knownbug test-basic,$(TEST_TARGETS))
+TEST_TARGETS := $(patsubst test-basic,test-basic test-leaked-globals,$(TEST_TARGETS))
TEST_TARGETS := $(patsubst test-bundled-gems,test-bundled-gems-run,$(TEST_TARGETS))
TEST_TARGETS := $(patsubst test-bundled-gems-run,test-bundled-gems-run $(PREPARE_BUNDLED_GEMS),$(TEST_TARGETS))
TEST_TARGETS := $(patsubst test-bundled-gems-prepare,test-bundled-gems-prepare $(PRECHECK_BUNDLED_GEMS) test-bundled-gems-fetch,$(TEST_TARGETS))
@@ -45,6 +44,7 @@ TEST_TARGETS := $(patsubst test-bundler-parallel,test-bundler-parallel $(PREPARE
TEST_TARGETS := $(patsubst test-syntax-suggest,test-syntax-suggest $(PREPARE_SYNTAX_SUGGEST),$(TEST_TARGETS))
TEST_DEPENDS := $(filter-out test-short $(TEST_TARGETS),$(TEST_DEPENDS))
TEST_DEPENDS += $(if $(filter great exam love check,$(MAKECMDGOALS)),all exts)
+TEST_TARGETS := $(patsubst yes-%,%,$(filter-out no-%,$(TEST_TARGETS)))
endif
in-srcdir := $(if $(filter-out .,$(srcdir)),$(CHDIR) $(srcdir) &&)
@@ -91,16 +91,31 @@ $(addprefix yes-,$(TEST_TARGETS)): $(TEST_DEPENDS)
endif
ORDERED_TEST_TARGETS := $(filter $(TEST_TARGETS), \
- btest-ruby test-knownbug test-basic \
+ btest-ruby test-knownbug test-leaked-globals test-basic \
test-testframework test-tool test-ruby test-all \
test-spec test-syntax-suggest-prepare test-syntax-suggest \
test-bundler-prepare test-bundler test-bundler-parallel \
test-bundled-gems-precheck test-bundled-gems-fetch \
test-bundled-gems-prepare test-bundled-gems-run \
)
-prev_test := $(if $(filter test-spec,$(ORDERED_TEST_TARGETS)),test-spec-precheck)
+
+# grep ^yes-test-.*-precheck: template/Makefile.in defs/gmake.mk common.mk
+test_prechecks := $(filter $(ORDERED_TEST_TARGETS),\
+ test-leaked-globals \
+ test-all \
+ test-spec \
+ test-syntax-suggest \
+ test-bundler \
+ test-bundler-parallel \
+ test-bundled-gems\
+ )
+prev_test := $(subst test-bundler-parallel,test-bundler,$(test_prechecks))
+prev_test := $(addsuffix -precheck,$(prev_test))
+first_test_prechecks := $(prev_test)
+
$(foreach test,$(ORDERED_TEST_TARGETS), \
- $(eval yes-$(value test) no-$(value test): $(value prev_test)); \
+ $(eval yes-$(value test): $(addprefix yes-,$(value prev_test))); \
+ $(eval no-$(value test): $(addprefix no-,$(value prev_test))); \
$(eval prev_test := $(value test)))
endif
@@ -178,15 +193,22 @@ $(SCRIPTBINDIR):
$(Q) mkdir $@
.PHONY: commit
-commit: $(if $(filter commit,$(MAKECMDGOALS)),$(filter-out commit,$(MAKECMDGOALS))) up
+COMMIT_PREPARE := $(subst :,\:,$(filter-out commit do-commit,$(MAKECMDGOALS))) up
+
+commit: pre-commit $(DOT_WAIT) do-commit $(DOT_WAIT) post_commit
+pre-commit: $(COMMIT_PREPARE)
+do-commit: $(if $(DOT_WAIT),,pre-commit)
@$(BASERUBY) -C "$(srcdir)" -I./tool/lib -rvcs -e 'VCS.detect(".").commit'
+post-commit: $(if $(DOT_WAIT),,do-commit)
+$(Q) \
{ \
$(in-srcdir) \
exec sed -f tool/prereq.status defs/gmake.mk template/Makefile.in common.mk; \
} | \
- $(MAKE) $(mflags) Q=$(Q) ECHO=$(ECHO) srcdir="$(srcdir)" srcs_vpath="" CHDIR="$(CHDIR)" \
- BOOTSTRAPRUBY="$(BOOTSTRAPRUBY)" MINIRUBY="$(BASERUBY)" BASERUBY="$(BASERUBY)" \
+ $(MAKE) $(mflags) Q=$(Q) ECHO=$(ECHO) \
+ top_srcdir="$(top_srcdir)" srcdir="$(srcdir)" srcs_vpath="" CHDIR="$(CHDIR)" \
+ BOOTSTRAPRUBY="$(BOOTSTRAPRUBY)" BOOTSTRAPRUBY_OPT="$(BOOTSTRAPRUBY_OPT)" \
+ MINIRUBY="$(BASERUBY)" BASERUBY="$(BASERUBY)" HAVE_BASERUBY="$(HAVE_BASERUBY)" \
VCSUP="" ENC_MK=.top-enc.mk REVISION_FORCE=PHONY CONFIGURE="$(CONFIGURE)" -f - \
update-src srcs all-incs
@@ -304,6 +326,7 @@ foreach-bundled-gems-rev-0 = \
bundled-gem-gemfile = $(srcdir)/gems/$(1)-$(2).gem
bundled-gem-gemspec = $(srcdir)/gems/src/$(1)/$(1).gemspec
bundled-gem-extracted = $(srcdir)/.bundle/gems/$(1)-$(2)
+bundled-gem-revision = $(srcdir)/.bundle/.timestamp/$(1).revision
update-gems: | $(patsubst %,$(srcdir)/gems/%.gem,$(bundled-gems))
update-gems: | $(call foreach-bundled-gems-rev,bundled-gem-gemfile)
@@ -343,24 +366,25 @@ $(srcdir)/gems/src/$(1)/.git: | $(srcdir)/gems/src
$(ECHO) Cloning $(4)
$(Q) $(GIT) clone $(4) $$(@D)
-$(srcdir)/.bundle/.timestamp/$(1).revision: \
+$(bundled-gem-revision): \
$(if $(if $(wildcard $$(@)),$(filter $(3),$(shell cat $$(@)))),,PHONY) \
| $(srcdir)/.bundle/.timestamp $(srcdir)/gems/src/$(1)/.git
$(ECHO) Update $(1) to $(3)
$(Q) $(CHDIR) "$(srcdir)/gems/src/$(1)" && \
- $(GIT) fetch origin $(3) && \
- $(GIT) checkout --detach $(3) && \
- :
+ if [ `$(GIT) rev-parse HEAD` != $(3) ]; then \
+ $(GIT) fetch origin $(3) && \
+ $(GIT) checkout --detach $(3) && \
+ :; \
+ fi
echo $(3) | $(IFCHANGE) $$(@) -
# The repository of minitest does not include minitest.gemspec because it uses hoe.
# This creates a dummy gemspec.
-$(srcdir)/gems/src/$(1)/$(1).gemspec: $(srcdir)/.bundle/.timestamp/$(1).revision \
+$(bundled-gem-gemspec): $(bundled-gem-revision) \
| $(srcdir)/gems/src/$(1)/.git
$(Q) $(BASERUBY) -I$(tooldir)/lib -rbundled_gem -e 'BundledGem.dummy_gemspec(*ARGV)' $$(@)
-$(srcdir)/gems/$(1)-$(2).gem: $(srcdir)/gems/src/$(1)/$(1).gemspec \
- $(srcdir)/.bundle/.timestamp/$(1).revision
+$(bundled-gem-gemfile): $(bundled-gem-gemspec) $(bundled-gem-revision)
$(ECHO) Building $(1)@$(3) to $$(@)
$(Q) $(BASERUBY) -C "$(srcdir)" \
-Itool/lib -rbundled_gem \
@@ -392,6 +416,17 @@ endif
.SECONDARY: update-unicode-ucd-emoji-files
.SECONDARY: update-unicode-emoji-files
+ifneq ($(DOT_WAIT),)
+.NOTPARALLEL: update-unicode
+.NOTPARALLEL: update-unicode-files
+.NOTPARALLEL: update-unicode-auxiliary-files
+.NOTPARALLEL: update-unicode-ucd-emoji-files
+.NOTPARALLEL: update-unicode-emoji-files
+.NOTPARALLEL: $(UNICODE_FILES) $(UNICODE_PROPERTY_FILES)
+.NOTPARALLEL: $(UNICODE_AUXILIARY_FILES)
+.NOTPARALLEL: $(UNICODE_UCD_EMOJI_FILES) $(UNICODE_EMOJI_FILES)
+endif
+
ifeq ($(HAVE_GIT),yes)
REVISION_LATEST := $(shell $(CHDIR) $(srcdir) && $(GIT) log -1 --format=%H 2>/dev/null)
else
@@ -434,6 +469,10 @@ benchmark/%: miniruby$(EXEEXT) update-benchmark-driver PHONY
--executables="built-ruby::$(BENCH_RUBY) --disable-gem" \
$(srcdir)/$@ $(BENCH_OPTS) $(OPTS)
+clean-local:: TARGET_SO = $(PROGRAM) $(WPROGRAM) $(LIBRUBY_SO) $(STATIC_RUBY) miniruby goruby
+clean-local::
+ -$(Q)$(RMALL) $(cleanlibs)
+
clean-srcs-ext::
$(Q)$(RM) $(patsubst $(srcdir)/%,%,$(EXT_SRCS))
@@ -469,12 +508,17 @@ $(RUBYSPEC_CAPIEXT)/%.$(DLEXT): $(srcdir)/$(RUBYSPEC_CAPIEXT)/%.c $(srcdir)/$(RU
$(ECHO) building $@
$(Q) $(MAKEDIRS) $(@D)
$(Q) $(DLDSHARED) -L. $(XDLDFLAGS) $(XLDFLAGS) $(LDFLAGS) $(INCFLAGS) $(CPPFLAGS) $(OUTFLAG)$@ $< $(LIBRUBYARG)
+ifneq ($(POSTLINK),)
+ $(Q) $(POSTLINK)
+endif
$(Q) $(RMALL) $@.*
-rubyspec-capiext: $(patsubst %.c,$(RUBYSPEC_CAPIEXT)/%.$(DLEXT),$(notdir $(wildcard $(srcdir)/$(RUBYSPEC_CAPIEXT)/*.c)))
+RUBYSPEC_CAPIEXT_SO := $(patsubst %.c,$(RUBYSPEC_CAPIEXT)/%.$(DLEXT),$(notdir $(wildcard $(srcdir)/$(RUBYSPEC_CAPIEXT)/*.c)))
+rubyspec-capiext: $(RUBYSPEC_CAPIEXT_SO)
@ $(NULLCMD)
ifeq ($(ENABLE_SHARED),yes)
+ruby: $(if $(LIBRUBY_SO_UPDATE),$(RUBYSPEC_CAPIEXT_SO))
exts: rubyspec-capiext
endif
@@ -484,14 +528,42 @@ spec/%/ spec/%_spec.rb: programs exts PHONY
ruby.pc: $(filter-out ruby.pc,$(ruby_pc))
matz: up
+ $(eval OLD := $(MAJOR).$(MINOR).0)
$(eval MINOR := $(shell expr $(MINOR) + 1))
- $(eval message := Development of $(MAJOR).$(MINOR).0 started.)
+ $(eval NEW := $(MAJOR).$(MINOR).0)
+ $(eval message := Development of $(NEW) started.)
$(eval files := include/ruby/version.h include/ruby/internal/abi.h)
+ $(GIT) -C $(srcdir) mv -f NEWS.md doc/NEWS/NEWS-$(OLD).md
+ $(GIT) -C $(srcdir) commit -m "[DOC] Flush NEWS.md"
sed -i~ \
-e "s/^\(#define RUBY_API_VERSION_MINOR\) .*/\1 $(MINOR)/" \
-e "s/^\(#define RUBY_ABI_VERSION\) .*/\1 0/" \
$(files:%=$(srcdir)/%)
- $(GIT) -C $(srcdir) commit -m "$(message)" $(files)
+ $(GIT) -C $(srcdir) add $(files)
+ $(BASERUBY) -C $(srcdir) -p -00 \
+ -e 'BEGIN {old, new = ARGV.shift(2); STDOUT.reopen("NEWS.md")}' \
+ -e 'case $$.' \
+ -e 'when 1; $$_.sub!(/Ruby \K[0-9.]+/, new)' \
+ -e 'when 2; $$_.sub!(/\*\*\K[0-9.]+(?=\*\*)/, old)' \
+ -e 'end' \
+ -e 'next if /^[\[ *]/ =~ $$_' \
+ -e '$$_.sub!(/\n{2,}\z/, "\n\n")' \
+ $(OLD) $(NEW) doc/NEWS/NEWS-$(OLD).md
+ $(GIT) -C $(srcdir) add NEWS.md
+ $(GIT) -C $(srcdir) commit -m "$(message)"
tags:
$(MAKE) GIT="$(GIT)" -C "$(srcdir)" -f defs/tags.mk
+
+
+# ripper_srcs makes all sources at once. invoking this target multiple
+# times in parallel means all sources will be built for the number of
+# sources times respectively.
+ifneq ($(DOT_WAIT),)
+.NOTPARALLEL: ripper_srcs
+else
+ripper_src =
+$(foreach r,$(RIPPER_SRCS),$(eval $(value r): | $(value ripper_src))\
+ $(eval ripper_src := $(value r)))
+ripper_srcs: $(ripper_src)
+endif
diff --git a/defs/known_errors.def b/defs/known_errors.def
index e9694cfbda..14b3cff265 100644
--- a/defs/known_errors.def
+++ b/defs/known_errors.def
@@ -1,57 +1,57 @@
-E2BIG
-EACCES
-EADDRINUSE
-EADDRNOTAVAIL
+E2BIG Argument list too long
+EACCES Permission denied
+EADDRINUSE Address in use
+EADDRNOTAVAIL Address not available
EADV
-EAFNOSUPPORT
-EAGAIN
-EALREADY
+EAFNOSUPPORT Address family not supported
+EAGAIN Resource unavailable, try again (may be the same value as [EWOULDBLOCK])
+EALREADY Connection already in progress
EAUTH
EBADARCH
EBADE
EBADEXEC
-EBADF
+EBADF Bad file descriptor
EBADFD
EBADMACHO
-EBADMSG
+EBADMSG Bad message
EBADR
EBADRPC
EBADRQC
EBADSLT
EBFONT
-EBUSY
-ECANCELED
+EBUSY Device or resource busy
+ECANCELED Operation canceled
ECAPMODE
-ECHILD
+ECHILD No child processes
ECHRNG
ECOMM
-ECONNABORTED
-ECONNREFUSED
-ECONNRESET
-EDEADLK
+ECONNABORTED Connection aborted
+ECONNREFUSED Connection refused
+ECONNRESET Connection reset
+EDEADLK Resource deadlock would occur
EDEADLOCK
-EDESTADDRREQ
+EDESTADDRREQ Destination address required
EDEVERR
-EDOM
+EDOM Mathematics argument out of domain of function
EDOOFUS
EDOTDOT
-EDQUOT
-EEXIST
-EFAULT
-EFBIG
+EDQUOT Reserved
+EEXIST File exists
+EFAULT Bad address
+EFBIG File too large
EFTYPE
EHOSTDOWN
-EHOSTUNREACH
+EHOSTUNREACH Host is unreachable
EHWPOISON
-EIDRM
-EILSEQ
-EINPROGRESS
-EINTR
-EINVAL
-EIO
+EIDRM Identifier removed
+EILSEQ Illegal byte sequence
+EINPROGRESS Operation in progress
+EINTR Interrupted function
+EINVAL Invalid argument
+EIO I/O error
EIPSEC
-EISCONN
-EISDIR
+EISCONN Socket is connected
+EISDIR Is a directory
EISNAM
EKEYEXPIRED
EKEYREJECTED
@@ -60,98 +60,98 @@ EL2HLT
EL2NSYNC
EL3HLT
EL3RST
-ELAST
ELIBACC
ELIBBAD
ELIBEXEC
ELIBMAX
ELIBSCN
ELNRNG
-ELOOP
+ELOOP Too many levels of symbolic links
EMEDIUMTYPE
-EMFILE
-EMLINK
-EMSGSIZE
-EMULTIHOP
-ENAMETOOLONG
+EMFILE File descriptor value too large
+EMLINK Too many links
+EMSGSIZE Message too large
+EMULTIHOP Reserved
+ENAMETOOLONG Filename too long
ENAVAIL
ENEEDAUTH
-ENETDOWN
-ENETRESET
-ENETUNREACH
-ENFILE
+ENETDOWN Network is down
+ENETRESET Connection aborted by network
+ENETUNREACH Network unreachable
+ENFILE Too many files open in system
ENOANO
ENOATTR
-ENOBUFS
+ENOBUFS No buffer space available
ENOCSI
-ENODATA
-ENODEV
-ENOENT
-ENOEXEC
+ENODATA [OB XSR] [Option Start] No message is available on the STREAM head read queue [Option End]
+ENODEV No such device
+ENOENT No such file or directory
+ENOEXEC Executable file format error
ENOKEY
-ENOLCK
-ENOLINK
+ENOLCK No locks available
+ENOLINK Reserved
ENOMEDIUM
-ENOMEM
-ENOMSG
+ENOMEM Not enough space
+ENOMSG No message of the desired type
ENONET
ENOPKG
ENOPOLICY
-ENOPROTOOPT
-ENOSPC
-ENOSR
-ENOSTR
-ENOSYS
+ENOPROTOOPT Protocol not available
+ENOSPC No space left on device
+ENOSR [OB XSR] [Option Start] No STREAM resources [Option End]
+ENOSTR [OB XSR] [Option Start] Not a STREAM [Option End]
+ENOSYS Functionality not supported
ENOTBLK
ENOTCAPABLE
-ENOTCONN
-ENOTDIR
-ENOTEMPTY
+ENOTCONN The socket is not connected
+ENOTDIR Not a directory or a symbolic link to a directory
+ENOTEMPTY Directory not empty
ENOTNAM
-ENOTRECOVERABLE
-ENOTSOCK
-ENOTSUP
-ENOTTY
+ENOTRECOVERABLE State not recoverable
+ENOTSOCK Not a socket
+ENOTSUP Not supported (may be the same value as [EOPNOTSUPP])
+ENOTTY Inappropriate I/O control operation
ENOTUNIQ
-ENXIO
-EOPNOTSUPP
-EOVERFLOW
-EOWNERDEAD
-EPERM
+ENXIO No such device or address
+EOPNOTSUPP Operation not supported on socket (may be the same value as [ENOTSUP])
+EOVERFLOW Value too large to be stored in data type
+EOWNERDEAD Previous owner died
+EPERM Operation not permitted
EPFNOSUPPORT
-EPIPE
+EPIPE Broken pipe
EPROCLIM
EPROCUNAVAIL
EPROGMISMATCH
EPROGUNAVAIL
-EPROTO
-EPROTONOSUPPORT
-EPROTOTYPE
+EPROTO Protocol error
+EPROTONOSUPPORT Protocol not supported
+EPROTOTYPE Protocol wrong type for socket
EPWROFF
EQFULL
-ERANGE
+ERANGE Result too large
EREMCHG
EREMOTE
EREMOTEIO
ERESTART
ERFKILL
-EROFS
+EROFS Read-only file system
ERPCMISMATCH
ESHLIBVERS
ESHUTDOWN
ESOCKTNOSUPPORT
-ESPIPE
-ESRCH
+ESPIPE Invalid seek
+ESRCH No such process
ESRMNT
-ESTALE
+ESTALE Reserved
ESTRPIPE
-ETIME
-ETIMEDOUT
+ETIME [OB XSR] [Option Start] Stream ioctl() timeout [Option End]
+ETIMEDOUT Connection timed out
ETOOMANYREFS
-ETXTBSY
+ETXTBSY Text file busy
EUCLEAN
EUNATCH
EUSERS
-EWOULDBLOCK
-EXDEV
+EWOULDBLOCK Operation would block (may be the same value as [EAGAIN])
+EXDEV Cross-device link
EXFULL
+ELAST Largest errno
diff --git a/dir.c b/dir.c
index fc7d9049fc..84ef5ee6f5 100644
--- a/dir.c
+++ b/dir.c
@@ -471,23 +471,21 @@ dir_free(void *ptr)
struct dir_data *dir = ptr;
if (dir->dir) closedir(dir->dir);
- xfree(dir);
}
-static size_t
-dir_memsize(const void *ptr)
-{
- return sizeof(struct dir_data);
-}
-
-RUBY_REFERENCES_START(dir_refs)
- REF_EDGE(dir_data, path),
-RUBY_REFERENCES_END
+RUBY_REFERENCES(dir_refs) = {
+ RUBY_REF_EDGE(struct dir_data, path),
+ RUBY_REF_END
+};
static const rb_data_type_t dir_data_type = {
"dir",
- {REFS_LIST_PTR(dir_refs), dir_free, dir_memsize,},
- 0, NULL, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_DECL_MARKING
+ {
+ RUBY_REFS_LIST_PTR(dir_refs),
+ dir_free,
+ NULL, // Nothing allocated externally, so don't need a memsize function
+ },
+ 0, NULL, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_DECL_MARKING | RUBY_TYPED_EMBEDDABLE
};
static VALUE dir_close(VALUE);
@@ -521,7 +519,7 @@ opendir_without_gvl(const char *path)
u.in = path;
- return rb_thread_call_without_gvl(nogvl_opendir, u.out, RUBY_UBF_IO, 0);
+ return IO_WITHOUT_GVL(nogvl_opendir, u.out);
}
else
return opendir(path);
@@ -590,23 +588,24 @@ dir_s_close(rb_execution_context_t *ec, VALUE klass, VALUE dir)
# if defined(HAVE_FDOPENDIR) && defined(HAVE_DIRFD)
/*
- * call-seq:
- * Dir.for_fd(integer) -> aDir
+ * call-seq:
+ * Dir.for_fd(fd) -> dir
+ *
+ * Returns a new \Dir object representing the directory specified by the given
+ * integer directory file descriptor +fd+:
*
- * Returns a Dir representing the directory specified by the given
- * directory file descriptor. Note that the returned Dir will not
- * have an associated path.
+ * d0 = Dir.new('..')
+ * d1 = Dir.for_fd(d0.fileno)
*
- * d1 = Dir.new('..')
- * d2 = Dir.for_fd(d1.fileno)
- * d1.path # => '..'
- * d2.path # => nil
- * d1.chdir{Dir.pwd} == d2.chdir{Dir.pwd} # => true
+ * Note that the returned +d1+ does not have an associated path:
*
- * This method uses fdopendir() function defined by POSIX 2008.
- * NotImplementedError is raised on other platforms, such as Windows,
- * which doesn't provide the function.
+ * d0.path # => '..'
+ * d1.path # => nil
*
+ * This method uses the
+ * {fdopendir()}[https://www.man7.org/linux/man-pages/man3/fdopendir.3p.html]
+ * function defined by POSIX 2008;
+ * the method is not implemented on non-POSIX platforms (raises NotImplementedError).
*/
static VALUE
dir_s_for_fd(VALUE klass, VALUE fd)
@@ -653,10 +652,13 @@ dir_check(VALUE dir)
/*
- * call-seq:
- * dir.inspect -> string
+ * call-seq:
+ * inspect -> string
+ *
+ * Returns a string description of +self+:
+ *
+ * Dir.new('example').inspect # => "#<Dir:example>"
*
- * Return a string describing this Dir object.
*/
static VALUE
dir_inspect(VALUE dir)
@@ -690,18 +692,18 @@ dir_inspect(VALUE dir)
#ifdef HAVE_DIRFD
/*
- * call-seq:
- * dir.fileno -> integer
- *
- * Returns the file descriptor used in <em>dir</em>.
+ * call-seq:
+ * fileno -> integer
*
- * d = Dir.new("..")
- * d.fileno #=> 8
+ * Returns the file descriptor used in <em>dir</em>.
*
- * This method uses dirfd() function defined by POSIX 2008.
- * NotImplementedError is raised on other platforms, such as Windows,
- * which doesn't provide the function.
+ * d = Dir.new('..')
+ * d.fileno # => 8
*
+ * This method uses the
+ * {dirfd()}[https://www.man7.org/linux/man-pages/man3/dirfd.3.html]
+ * function defined by POSIX 2008;
+ * the method is not implemented on non-POSIX platforms (raises NotImplementedError).
*/
static VALUE
dir_fileno(VALUE dir)
@@ -720,14 +722,14 @@ dir_fileno(VALUE dir)
#endif
/*
- * call-seq:
- * dir.path -> string or nil
- * dir.to_path -> string or nil
+ * call-seq:
+ * path -> string or nil
*
- * Returns the path parameter passed to <em>dir</em>'s constructor.
+ * Returns the +dirpath+ string that was used to create +self+
+ * (or +nil+ if created by method Dir.for_fd):
+ *
+ * Dir.new('example').path # => "example"
*
- * d = Dir.new("..")
- * d.path #=> ".."
*/
static VALUE
dir_path(VALUE dir)
@@ -781,16 +783,18 @@ to_be_skipped(const struct dirent *dp)
}
/*
- * call-seq:
- * dir.read -> string or nil
+ * call-seq:
+ * read -> string or nil
*
- * Reads the next entry from <em>dir</em> and returns it as a string.
- * Returns <code>nil</code> at the end of the stream.
+ * Reads and returns the next entry name from +self+;
+ * returns +nil+ if at end-of-stream;
+ * see {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like]:
+ *
+ * dir = Dir.new('example')
+ * dir.read # => "."
+ * dir.read # => ".."
+ * dir.read # => "config.h"
*
- * d = Dir.new("testdir")
- * d.read #=> "."
- * d.read #=> ".."
- * d.read #=> "config.h"
*/
static VALUE
dir_read(VALUE dir)
@@ -799,7 +803,7 @@ dir_read(VALUE dir)
struct dirent *dp;
GetDIR(dir, dirp);
- errno = 0;
+ rb_errno_set(0);
if ((dp = READDIR(dirp->dir, dirp->enc)) != NULL) {
return rb_external_str_new_with_enc(dp->d_name, NAMLEN(dp), dirp->enc);
}
@@ -819,24 +823,23 @@ dir_yield(VALUE arg, VALUE path)
}
/*
- * call-seq:
- * dir.each { |filename| block } -> dir
- * dir.each -> an_enumerator
+ * call-seq:
+ * each {|entry_name| ... } -> self
*
- * Calls the block once for each entry in this directory, passing the
- * filename of each entry as a parameter to the block.
+ * Calls the block with each entry name in +self+:
*
- * If no block is given, an enumerator is returned instead.
+ * Dir.new('example').each {|entry_name| p entry_name }
*
- * d = Dir.new("testdir")
- * d.each {|x| puts "Got #{x}" }
+ * Output:
+
+ * "."
+ * ".."
+ * "config.h"
+ * "lib"
+ * "main.rb"
*
- * <em>produces:</em>
+ * With no block given, returns an Enumerator.
*
- * Got .
- * Got ..
- * Got config.h
- * Got main.rb
*/
static VALUE
dir_each(VALUE dir)
@@ -879,16 +882,17 @@ dir_each_entry(VALUE dir, VALUE (*each)(VALUE, VALUE), VALUE arg, int children_o
#ifdef HAVE_TELLDIR
/*
- * call-seq:
- * dir.pos -> integer
- * dir.tell -> integer
+ * call-seq:
+ * tell -> integer
+ *
+ * Returns the current position of +self+;
+ * see {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like]:
*
- * Returns the current position in <em>dir</em>. See also Dir#seek.
+ * dir = Dir.new('example')
+ * dir.tell # => 0
+ * dir.read # => "."
+ * dir.tell # => 1
*
- * d = Dir.new("testdir")
- * d.tell #=> 0
- * d.read #=> "."
- * d.tell #=> 12
*/
static VALUE
dir_tell(VALUE dir)
@@ -906,18 +910,24 @@ dir_tell(VALUE dir)
#ifdef HAVE_SEEKDIR
/*
- * call-seq:
- * dir.seek( integer ) -> dir
- *
- * Seeks to a particular location in <em>dir</em>. <i>integer</i>
- * must be a value returned by Dir#tell.
- *
- * d = Dir.new("testdir") #=> #<Dir:0x401b3c40>
- * d.read #=> "."
- * i = d.tell #=> 12
- * d.read #=> ".."
- * d.seek(i) #=> #<Dir:0x401b3c40>
- * d.read #=> ".."
+ * call-seq:
+ * seek(position) -> self
+ *
+ * Sets the position in +self+ and returns +self+.
+ * The value of +position+ should have been returned from an earlier call to #tell;
+ * if not, the return values from subsequent calls to #read are unspecified.
+ *
+ * See {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like].
+ *
+ * Examples:
+ *
+ * dir = Dir.new('example')
+ * dir.pos # => 0
+ * dir.seek(3) # => #<Dir:example>
+ * dir.pos # => 3
+ * dir.seek(30) # => #<Dir:example>
+ * dir.pos # => 5
+ *
*/
static VALUE
dir_seek(VALUE dir, VALUE pos)
@@ -935,17 +945,24 @@ dir_seek(VALUE dir, VALUE pos)
#ifdef HAVE_SEEKDIR
/*
- * call-seq:
- * dir.pos = integer -> integer
+ * call-seq:
+ * pos = position -> integer
+ *
+ * Sets the position in +self+ and returns +position+.
+ * The value of +position+ should have been returned from an earlier call to #tell;
+ * if not, the return values from subsequent calls to #read are unspecified.
+ *
+ * See {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like].
+ *
+ * Examples:
*
- * Synonym for Dir#seek, but returns the position parameter.
+ * dir = Dir.new('example')
+ * dir.pos # => 0
+ * dir.pos = 3 # => 3
+ * dir.pos # => 3
+ * dir.pos = 30 # => 30
+ * dir.pos # => 5
*
- * d = Dir.new("testdir") #=> #<Dir:0x401b3c40>
- * d.read #=> "."
- * i = d.pos #=> 12
- * d.read #=> ".."
- * d.pos = i #=> 12
- * d.read #=> ".."
*/
static VALUE
dir_set_pos(VALUE dir, VALUE pos)
@@ -958,15 +975,19 @@ dir_set_pos(VALUE dir, VALUE pos)
#endif
/*
- * call-seq:
- * dir.rewind -> dir
+ * call-seq:
+ * rewind -> self
+ *
+ * Sets the position in +self+ to zero;
+ * see {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like]:
*
- * Repositions <em>dir</em> to the first entry.
+ * dir = Dir.new('example')
+ * dir.read # => "."
+ * dir.read # => ".."
+ * dir.pos # => 2
+ * dir.rewind # => #<Dir:example>
+ * dir.pos # => 0
*
- * d = Dir.new("testdir")
- * d.read #=> "."
- * d.rewind #=> #<Dir:0x401b3fb0>
- * d.read #=> "."
*/
static VALUE
dir_rewind(VALUE dir)
@@ -979,14 +1000,18 @@ dir_rewind(VALUE dir)
}
/*
- * call-seq:
- * dir.close -> nil
+ * call-seq:
+ * close -> nil
*
- * Closes the directory stream.
- * Calling this method on closed Dir object is ignored since Ruby 2.3.
+ * Closes the stream in +self+, if it is open, and returns +nil+;
+ * ignored if +self+ is already closed:
+ *
+ * dir = Dir.new('example')
+ * dir.read # => "."
+ * dir.close # => nil
+ * dir.close # => nil
+ * dir.read # Raises IOError.
*
- * d = Dir.new("testdir")
- * d.close #=> nil
*/
static VALUE
dir_close(VALUE dir)
@@ -1016,12 +1041,64 @@ dir_chdir0(VALUE path)
rb_sys_fail_path(path);
}
-static int chdir_blocking = 0;
-static VALUE chdir_thread = Qnil;
+static struct {
+ VALUE thread;
+ VALUE path;
+ int line;
+ int blocking;
+} chdir_lock = {
+ .blocking = 0, .thread = Qnil,
+ .path = Qnil, .line = 0,
+};
+
+static void
+chdir_enter(void)
+{
+ if (chdir_lock.blocking == 0) {
+ chdir_lock.path = rb_source_location(&chdir_lock.line);
+ }
+ chdir_lock.blocking++;
+ if (NIL_P(chdir_lock.thread)) {
+ chdir_lock.thread = rb_thread_current();
+ }
+}
+
+static void
+chdir_leave(void)
+{
+ chdir_lock.blocking--;
+ if (chdir_lock.blocking == 0) {
+ chdir_lock.thread = Qnil;
+ chdir_lock.path = Qnil;
+ chdir_lock.line = 0;
+ }
+}
+
+static int
+chdir_alone_block_p(void)
+{
+ int block_given = rb_block_given_p();
+ if (chdir_lock.blocking > 0) {
+ if (rb_thread_current() != chdir_lock.thread)
+ rb_raise(rb_eRuntimeError, "conflicting chdir during another chdir block");
+ if (!block_given) {
+ if (!NIL_P(chdir_lock.path)) {
+ rb_warn("conflicting chdir during another chdir block\n"
+ "%" PRIsVALUE ":%d: note: previous chdir was here",
+ chdir_lock.path, chdir_lock.line);
+ }
+ else {
+ rb_warn("conflicting chdir during another chdir block");
+ }
+ }
+ }
+ return block_given;
+}
struct chdir_data {
VALUE old_path, new_path;
int done;
+ bool yield_path;
};
static VALUE
@@ -1030,10 +1107,8 @@ chdir_yield(VALUE v)
struct chdir_data *args = (void *)v;
dir_chdir0(args->new_path);
args->done = TRUE;
- chdir_blocking++;
- if (NIL_P(chdir_thread))
- chdir_thread = rb_thread_current();
- return rb_yield(args->new_path);
+ chdir_enter();
+ return args->yield_path ? rb_yield(args->new_path) : rb_yield_values2(0, NULL);
}
static VALUE
@@ -1041,53 +1116,96 @@ chdir_restore(VALUE v)
{
struct chdir_data *args = (void *)v;
if (args->done) {
- chdir_blocking--;
- if (chdir_blocking == 0)
- chdir_thread = Qnil;
+ chdir_leave();
dir_chdir0(args->old_path);
}
return Qnil;
}
+static VALUE
+chdir_path(VALUE path, bool yield_path)
+{
+ if (chdir_alone_block_p()) {
+ struct chdir_data args;
+
+ args.old_path = rb_str_encode_ospath(rb_dir_getwd());
+ args.new_path = path;
+ args.done = FALSE;
+ args.yield_path = yield_path;
+ return rb_ensure(chdir_yield, (VALUE)&args, chdir_restore, (VALUE)&args);
+ }
+ else {
+ char *p = RSTRING_PTR(path);
+ int r = IO_WITHOUT_GVL_INT(nogvl_chdir, p);
+ if (r < 0)
+ rb_sys_fail_path(path);
+ }
+
+ return INT2FIX(0);
+}
+
/*
- * call-seq:
- * Dir.chdir( [ string] ) -> 0
- * Dir.chdir( [ string] ) {| path | block } -> anObject
- *
- * Changes the current working directory of the process to the given
- * string. When called without an argument, changes the directory to
- * the value of the environment variable <code>HOME</code>, or
- * <code>LOGDIR</code>. SystemCallError (probably Errno::ENOENT) if
- * the target directory does not exist.
- *
- * If a block is given, it is passed the name of the new current
- * directory, and the block is executed with that as the current
- * directory. The original working directory is restored when the block
- * exits. The return value of <code>chdir</code> is the value of the
- * block. <code>chdir</code> blocks can be nested, but in a
- * multi-threaded program an error will be raised if a thread attempts
- * to open a <code>chdir</code> block while another thread has one
- * open or a call to <code>chdir</code> without a block occurs inside
- * a block passed to <code>chdir</code> (even in the same thread).
- *
- * Dir.chdir("/var/spool/mail")
- * puts Dir.pwd
- * Dir.chdir("/tmp") do
- * puts Dir.pwd
- * Dir.chdir("/usr") do
- * puts Dir.pwd
- * end
- * puts Dir.pwd
+ * call-seq:
+ * Dir.chdir(new_dirpath) -> 0
+ * Dir.chdir -> 0
+ * Dir.chdir(new_dirpath) {|new_dirpath| ... } -> object
+ * Dir.chdir {|cur_dirpath| ... } -> object
+ *
+ * Changes the current working directory.
+ *
+ * With argument +new_dirpath+ and no block,
+ * changes to the given +dirpath+:
+ *
+ * Dir.pwd # => "/example"
+ * Dir.chdir('..') # => 0
+ * Dir.pwd # => "/"
+ *
+ * With no argument and no block:
+ *
+ * - Changes to the value of environment variable +HOME+ if defined.
+ * - Otherwise changes to the value of environment variable +LOGDIR+ if defined.
+ * - Otherwise makes no change.
+ *
+ * With argument +new_dirpath+ and a block, temporarily changes the working directory:
+ *
+ * - Calls the block with the argument.
+ * - Changes to the given directory.
+ * - Executes the block (yielding the new path).
+ * - Restores the previous working directory.
+ * - Returns the block's return value.
+ *
+ * Example:
+ *
+ * Dir.chdir('/var/spool/mail')
+ * Dir.pwd # => "/var/spool/mail"
+ * Dir.chdir('/tmp') do
+ * Dir.pwd # => "/tmp"
+ * end
+ * Dir.pwd # => "/var/spool/mail"
+ *
+ * With no argument and a block,
+ * calls the block with the current working directory (string)
+ * and returns the block's return value.
+ *
+ * Calls to \Dir.chdir with blocks may be nested:
+ *
+ * Dir.chdir('/var/spool/mail')
+ * Dir.pwd # => "/var/spool/mail"
+ * Dir.chdir('/tmp') do
+ * Dir.pwd # => "/tmp"
+ * Dir.chdir('/usr') do
+ * Dir.pwd # => "/usr"
* end
- * puts Dir.pwd
+ * Dir.pwd # => "/tmp"
+ * end
+ * Dir.pwd # => "/var/spool/mail"
*
- * <em>produces:</em>
+ * In a multi-threaded program an error is raised if a thread attempts
+ * to open a +chdir+ block while another thread has one open,
+ * or a call to +chdir+ without a block occurs inside
+ * a block passed to +chdir+ (even in the same thread).
*
- * /var/spool/mail
- * /tmp
- * /usr
- * /tmp
- * /var/spool/mail
+ * Raises an exception if the target directory does not exist.
*/
static VALUE
dir_s_chdir(int argc, VALUE *argv, VALUE obj)
@@ -1106,30 +1224,7 @@ dir_s_chdir(int argc, VALUE *argv, VALUE obj)
path = rb_str_new2(dist);
}
- if (chdir_blocking > 0) {
- if (rb_thread_current() != chdir_thread)
- rb_raise(rb_eRuntimeError, "conflicting chdir during another chdir block");
- if (!rb_block_given_p())
- rb_warn("conflicting chdir during another chdir block");
- }
-
- if (rb_block_given_p()) {
- struct chdir_data args;
-
- args.old_path = rb_str_encode_ospath(rb_dir_getwd());
- args.new_path = path;
- args.done = FALSE;
- return rb_ensure(chdir_yield, (VALUE)&args, chdir_restore, (VALUE)&args);
- }
- else {
- char *p = RSTRING_PTR(path);
- int r = (int)(VALUE)rb_thread_call_without_gvl(nogvl_chdir, p,
- RUBY_UBF_IO, 0);
- if (r < 0)
- rb_sys_fail_path(path);
- }
-
- return INT2FIX(0);
+ return chdir_path(path, true);
}
#if defined(HAVE_FCHDIR) && defined(HAVE_DIRFD) && HAVE_FCHDIR && HAVE_DIRFD
@@ -1160,9 +1255,7 @@ fchdir_yield(VALUE v)
struct fchdir_data *args = (void *)v;
dir_fchdir(args->fd);
args->done = TRUE;
- chdir_blocking++;
- if (NIL_P(chdir_thread))
- chdir_thread = rb_thread_current();
+ chdir_enter();
return rb_yield_values(0);
}
@@ -1171,9 +1264,7 @@ fchdir_restore(VALUE v)
{
struct fchdir_data *args = (void *)v;
if (args->done) {
- chdir_blocking--;
- if (chdir_blocking == 0)
- chdir_thread = Qnil;
+ chdir_leave();
dir_fchdir(RB_NUM2INT(dir_fileno(args->old_dir)));
}
dir_close(args->old_dir);
@@ -1181,66 +1272,63 @@ fchdir_restore(VALUE v)
}
/*
- * call-seq:
- * Dir.fchdir( integer ) -> 0
- * Dir.fchdir( integer ) { block } -> anObject
- *
- * Changes the current working directory of the process to the directory
- * specified by the given file descriptor integer. If the file descriptor
- * is not valid, raises SystemCallError. One reason to use
- * <code>fchdir</code> instead of <code>chdir</code> is when passing
- * directory file descriptors over a UNIX socket or to child processes,
- * to avoid TOCTOU (time-of-check to time-of-use) vulnerabilities.
- *
- * If a block is given, the current working directory is changed for the
- * duration of the block, and the original working directory is restored
- * when the block exits. The return value of <code>fchdir</code> is the
- * value of the block. <code>fchdir</code> and <code>chdir</code> blocks
- * can be nested, but in a multi-threaded program an error will be raised
- * if a thread attempts to open a <code>fchdir</code> or <code>chdir</code>
- * block while another thread has one open or a call to <code>fchdir</code>
- * or <code>chdir</code> without a block occurs inside a block passed to
- * <code>fchdir</code> or <code>chdir</code> (even in the same thread).
- *
- * When generating directory file descriptors from a +Dir+ instance,
- * make sure the +Dir+ instance is not garbage collected before the
- * directory file descriptor is passed to another process. Otherwise,
- * the directory file descriptor will be closed before it is passed.
- *
- * dir = Dir.new("/var/spool/mail")
- * dir2 = Dir.new("/usr")
- * fd = dir.fileno
- * fd2 = dir2.fileno
- * Dir.fchdir(fd) do
- * puts Dir.pwd
- * Dir.fchdir(fd2) do
- * puts Dir.pwd
- * end
- * puts Dir.pwd
- * end
- * puts Dir.pwd
- *
- * <em>produces:</em>
- *
- * /var/spool/mail
- * /tmp
- * /usr
- * /tmp
- * /var/spool/mail
+ * call-seq:
+ * Dir.fchdir(fd) -> 0
+ * Dir.fchdir(fd) { ... } -> object
+ *
+ * Changes the current working directory to the directory
+ * specified by the integer file descriptor +fd+.
+ *
+ * When passing a file descriptor over a UNIX socket or to a child process,
+ * using +fchdir+ instead of +chdir+ avoids the
+ * {time-of-check to time-of-use vulnerability}[https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use]
+ *
+ * With no block, changes to the directory given by +fd+:
+ *
+ * Dir.chdir('/var/spool/mail')
+ * Dir.pwd # => "/var/spool/mail"
+ * dir = Dir.new('/usr')
+ * fd = dir.fileno
+ * Dir.fchdir(fd)
+ * Dir.pwd # => "/usr"
+ *
+ * With a block, temporarily changes the working directory:
+ *
+ * - Calls the block with the argument.
+ * - Changes to the given directory.
+ * - Executes the block (yields no args).
+ * - Restores the previous working directory.
+ * - Returns the block's return value.
+ *
+ * Example:
+ *
+ * Dir.chdir('/var/spool/mail')
+ * Dir.pwd # => "/var/spool/mail"
+ * dir = Dir.new('/tmp')
+ * fd = dir.fileno
+ * Dir.fchdir(fd) do
+ * Dir.pwd # => "/tmp"
+ * end
+ * Dir.pwd # => "/var/spool/mail"
+ *
+ * This method uses the
+ * {fchdir()}[https://www.man7.org/linux/man-pages/man3/fchdir.3p.html]
+ * function defined by POSIX 2008;
+ * the method is not implemented on non-POSIX platforms (raises NotImplementedError).
+ *
+ * Raises an exception if the file descriptor is not valid.
+ *
+ * In a multi-threaded program an error is raised if a thread attempts
+ * to open a +chdir+ block while another thread has one open,
+ * or a call to +chdir+ without a block occurs inside
+ * a block passed to +chdir+ (even in the same thread).
*/
static VALUE
dir_s_fchdir(VALUE klass, VALUE fd_value)
{
int fd = RB_NUM2INT(fd_value);
- if (chdir_blocking > 0) {
- if (rb_thread_current() != chdir_thread)
- rb_raise(rb_eRuntimeError, "conflicting chdir during another chdir block");
- if (!rb_block_given_p())
- rb_warn("conflicting chdir during another chdir block");
- }
-
- if (rb_block_given_p()) {
+ if (chdir_alone_block_p()) {
struct fchdir_data args;
args.old_dir = dir_s_alloc(klass);
dir_initialize(NULL, args.old_dir, rb_fstring_cstr("."), Qnil);
@@ -1249,8 +1337,7 @@ dir_s_fchdir(VALUE klass, VALUE fd_value)
return rb_ensure(fchdir_yield, (VALUE)&args, fchdir_restore, (VALUE)&args);
}
else {
- int r = (int)(VALUE)rb_thread_call_without_gvl(nogvl_fchdir, &fd,
- RUBY_UBF_IO, 0);
+ int r = IO_WITHOUT_GVL_INT(nogvl_fchdir, &fd);
if (r < 0)
rb_sys_fail("fchdir");
}
@@ -1262,26 +1349,36 @@ dir_s_fchdir(VALUE klass, VALUE fd_value)
#endif
/*
- * call-seq:
- * dir.chdir -> nil
+ * call-seq:
+ * chdir -> 0
+ * chdir { ... } -> object
+ *
+ * Changes the current working directory to +self+:
*
- * Changes the current working directory to the receiver.
+ * Dir.pwd # => "/"
+ * dir = Dir.new('example')
+ * dir.chdir
+ * Dir.pwd # => "/example"
*
- * # Assume current directory is /path
- * Dir.new("testdir").chdir
- * Dir.pwd # => '/path/testdir'
+ * With a block, temporarily changes the working directory:
+ *
+ * - Calls the block.
+ * - Changes to the given directory.
+ * - Executes the block (yields no args).
+ * - Restores the previous working directory.
+ * - Returns the block's return value.
+ *
+ * Uses Dir.fchdir if available, and Dir.chdir if not, see those
+ * methods for caveats.
*/
static VALUE
dir_chdir(VALUE dir)
{
#if defined(HAVE_FCHDIR) && defined(HAVE_DIRFD) && HAVE_FCHDIR && HAVE_DIRFD
- dir_s_fchdir(rb_cDir, dir_fileno(dir));
+ return dir_s_fchdir(rb_cDir, dir_fileno(dir));
#else
- VALUE path = dir_get(dir)->path;
- dir_s_chdir(1, &path, rb_cDir);
+ return chdir_path(dir_get(dir)->path, false);
#endif
-
- return Qnil;
}
#ifndef _WIN32
@@ -1330,16 +1427,14 @@ rb_dir_getwd(void)
}
/*
- * call-seq:
- * Dir.getwd -> string
- * Dir.pwd -> string
+ * call-seq:
+ * Dir.pwd -> string
*
- * Returns the path to the current working directory of this process as
- * a string.
+ * Returns the path to the current working directory:
+ *
+ * Dir.chdir("/tmp") # => 0
+ * Dir.pwd # => "/tmp"
*
- * Dir.chdir("/tmp") #=> 0
- * Dir.getwd #=> "/tmp"
- * Dir.pwd #=> "/tmp"
*/
static VALUE
dir_s_getwd(VALUE dir)
@@ -1369,13 +1464,16 @@ check_dirname(VALUE dir)
#if defined(HAVE_CHROOT)
/*
- * call-seq:
- * Dir.chroot( string ) -> 0
+ * call-seq:
+ * Dir.chroot(dirpath) -> 0
+ *
+ * Changes the root directory of the calling process to that specified in +dirpath+.
+ * The new root directory is used for pathnames beginning with <tt>'/'</tt>.
+ * The root directory is inherited by all children of the calling process.
*
- * Changes this process's idea of the file system root. Only a
- * privileged process may make this call. Not available on all
- * platforms. On Unix systems, see <code>chroot(2)</code> for more
- * information.
+ * Only a privileged process may call +chroot+.
+ *
+ * See {Linux chroot}[https://man7.org/linux/man-pages/man2/chroot.2.html].
*/
static VALUE
dir_s_chroot(VALUE dir, VALUE path)
@@ -1404,18 +1502,20 @@ nogvl_mkdir(void *ptr)
}
/*
- * call-seq:
- * Dir.mkdir( string [, integer] ) -> 0
+ * call-seq:
+ * Dir.mkdir(dirpath, permissions = 0775) -> 0
*
- * Makes a new directory named by <i>string</i>, with permissions
- * specified by the optional parameter <i>anInteger</i>. The
- * permissions may be modified by the value of File::umask, and are
- * ignored on NT. Raises a SystemCallError if the directory cannot be
- * created. See also the discussion of permissions in the class
- * documentation for File.
+ * Creates a directory in the underlying file system
+ * at +dirpath+ with the given +permissions+;
+ * returns zero:
*
- * Dir.mkdir(File.join(Dir.home, ".foo"), 0700) #=> 0
+ * Dir.mkdir('foo')
+ * File.stat(Dir.new('foo')).mode.to_s(8)[1..4] # => "0755"
+ * Dir.mkdir('bar', 0644)
+ * File.stat(Dir.new('bar')).mode.to_s(8)[1..4] # => "0644"
*
+ * See {File Permissions}[rdoc-ref:File@File+Permissions].
+ * Note that argument +permissions+ is ignored on Windows.
*/
static VALUE
dir_s_mkdir(int argc, VALUE *argv, VALUE obj)
@@ -1433,7 +1533,7 @@ dir_s_mkdir(int argc, VALUE *argv, VALUE obj)
path = check_dirname(path);
m.path = RSTRING_PTR(path);
- r = (int)(VALUE)rb_thread_call_without_gvl(nogvl_mkdir, &m, RUBY_UBF_IO, 0);
+ r = IO_WITHOUT_GVL_INT(nogvl_mkdir, &m);
if (r < 0)
rb_sys_fail_path(path);
@@ -1449,13 +1549,14 @@ nogvl_rmdir(void *ptr)
}
/*
- * call-seq:
- * Dir.delete( string ) -> 0
- * Dir.rmdir( string ) -> 0
- * Dir.unlink( string ) -> 0
+ * call-seq:
+ * Dir.rmdir(dirpath) -> 0
+ *
+ * Removes the directory at +dirpath+ from the underlying file system:
+ *
+ * Dir.rmdir('foo') # => 0
*
- * Deletes the named directory. Raises a subclass of SystemCallError
- * if the directory isn't empty.
+ * Raises an exception if the directory is not empty.
*/
static VALUE
dir_s_rmdir(VALUE obj, VALUE dir)
@@ -1465,7 +1566,7 @@ dir_s_rmdir(VALUE obj, VALUE dir)
dir = check_dirname(dir);
p = RSTRING_PTR(dir);
- r = (int)(VALUE)rb_thread_call_without_gvl(nogvl_rmdir, (void *)p, RUBY_UBF_IO, 0);
+ r = IO_WITHOUT_GVL_INT(nogvl_rmdir, (void *)p);
if (r < 0)
rb_sys_fail_path(dir);
@@ -1663,7 +1764,7 @@ nogvl_opendir_at(void *ptr)
/* fallthrough*/
case 0:
if (fd >= 0) close(fd);
- errno = e;
+ rb_errno_set(e);
}
}
#else /* !USE_OPENDIR_AT */
@@ -1684,7 +1785,7 @@ opendir_at(int basefd, const char *path)
oaa.path = path;
if (vm_initialized)
- return rb_thread_call_without_gvl(nogvl_opendir_at, &oaa, RUBY_UBF_IO, 0);
+ return IO_WITHOUT_GVL(nogvl_opendir_at, &oaa);
else
return nogvl_opendir_at(&oaa);
}
@@ -3056,7 +3157,7 @@ push_glob(VALUE ary, VALUE str, VALUE base, int flags)
fd = AT_FDCWD;
if (!NIL_P(base)) {
if (!RB_TYPE_P(base, T_STRING) || !rb_enc_check(str, base)) {
- struct dir_data *dirp = DATA_PTR(base);
+ struct dir_data *dirp = RTYPEDDATA_GET_DATA(base);
if (!dirp->dir) dir_closed();
#ifdef HAVE_DIRFD
if ((fd = dirfd(dirp->dir)) == -1)
@@ -3180,26 +3281,35 @@ dir_open_dir(int argc, VALUE *argv)
/*
- * call-seq:
- * Dir.foreach( dirname ) {| filename | block } -> nil
- * Dir.foreach( dirname, encoding: enc ) {| filename | block } -> nil
- * Dir.foreach( dirname ) -> an_enumerator
- * Dir.foreach( dirname, encoding: enc ) -> an_enumerator
+ * call-seq:
+ * Dir.foreach(dirpath, encoding: 'UTF-8') {|entry_name| ... } -> nil
*
- * Calls the block once for each entry in the named directory, passing
- * the filename of each entry as a parameter to the block.
+ * Calls the block with each entry name in the directory at +dirpath+;
+ * sets the given encoding onto each passed +entry_name+:
*
- * If no block is given, an enumerator is returned instead.
+ * Dir.foreach('/example') {|entry_name| p entry_name }
*
- * Dir.foreach("testdir") {|x| puts "Got #{x}" }
+ * Output:
*
- * <em>produces:</em>
+ * "config.h"
+ * "lib"
+ * "main.rb"
+ * ".."
+ * "."
*
- * Got .
- * Got ..
- * Got config.h
- * Got main.rb
+ * Encoding:
*
+ * Dir.foreach('/example') {|entry_name| p entry_name.encoding; break }
+ * Dir.foreach('/example', encoding: 'US-ASCII') {|entry_name| p entry_name.encoding; break }
+ *
+ * Output:
+ *
+ * #<Encoding:UTF-8>
+ * #<Encoding:US-ASCII>
+ *
+ * See {String Encoding}[rdoc-ref:encodings.rdoc@String+Encoding].
+ *
+ * Returns an enumerator if no block is given.
*/
static VALUE
dir_foreach(int argc, VALUE *argv, VALUE io)
@@ -3221,19 +3331,21 @@ dir_collect(VALUE dir)
}
/*
- * call-seq:
- * Dir.entries( dirname ) -> array
- * Dir.entries( dirname, encoding: enc ) -> array
+ * call-seq:
+ * Dir.entries(dirname, encoding: 'UTF-8') -> array
*
- * Returns an array containing all of the filenames in the given
- * directory. Will raise a SystemCallError if the named directory
- * doesn't exist.
+ * Returns an array of the entry names in the directory at +dirpath+;
+ * sets the given encoding onto each returned entry name:
*
- * The optional <i>encoding</i> keyword argument specifies the encoding of the
- * directory. If not specified, the filesystem encoding is used.
+ * Dir.entries('/example') # => ["config.h", "lib", "main.rb", "..", "."]
+ * Dir.entries('/example').first.encoding
+ * # => #<Encoding:UTF-8>
+ * Dir.entries('/example', encoding: 'US-ASCII').first.encoding
+ * # => #<Encoding:US-ASCII>
*
- * Dir.entries("testdir") #=> [".", "..", "config.h", "main.rb"]
+ * See {String Encoding}[rdoc-ref:encodings.rdoc@String+Encoding].
*
+ * Raises an exception if the directory does not exist.
*/
static VALUE
dir_entries(int argc, VALUE *argv, VALUE io)
@@ -3251,25 +3363,12 @@ dir_each_child(VALUE dir)
}
/*
- * call-seq:
- * Dir.each_child( dirname ) {| filename | block } -> nil
- * Dir.each_child( dirname, encoding: enc ) {| filename | block } -> nil
- * Dir.each_child( dirname ) -> an_enumerator
- * Dir.each_child( dirname, encoding: enc ) -> an_enumerator
- *
- * Calls the block once for each entry except for "." and ".." in the
- * named directory, passing the filename of each entry as a parameter
- * to the block.
- *
- * If no block is given, an enumerator is returned instead.
- *
- * Dir.each_child("testdir") {|x| puts "Got #{x}" }
- *
- * <em>produces:</em>
- *
- * Got config.h
- * Got main.rb
+ * call-seq:
+ * Dir.each_child(dirpath) {|entry_name| ... } -> nil
+ * Dir.each_child(dirpath, encoding: 'UTF-8') {|entry_name| ... } -> nil
*
+ * Like Dir.foreach, except that entries <tt>'.'</tt> and <tt>'..'</tt>
+ * are not included.
*/
static VALUE
dir_s_each_child(int argc, VALUE *argv, VALUE io)
@@ -3283,24 +3382,22 @@ dir_s_each_child(int argc, VALUE *argv, VALUE io)
}
/*
- * call-seq:
- * dir.each_child {| filename | block } -> dir
- * dir.each_child -> an_enumerator
- *
- * Calls the block once for each entry except for "." and ".." in
- * this directory, passing the filename of each entry as a parameter
- * to the block.
+ * call-seq:
+ * each_child {|entry_name| ... } -> self
*
- * If no block is given, an enumerator is returned instead.
+ * Calls the block with each entry name in +self+
+ * except <tt>'.'</tt> and <tt>'..'</tt>:
*
- * d = Dir.new("testdir")
- * d.each_child {|x| puts "Got #{x}" }
+ * dir = Dir.new('/example')
+ * dir.each_child {|entry_name| p entry_name }
*
- * <em>produces:</em>
+ * Output:
*
- * Got config.h
- * Got main.rb
+ * "config.h"
+ * "lib"
+ * "main.rb"
*
+ * If no block is given, returns an enumerator.
*/
static VALUE
dir_each_child_m(VALUE dir)
@@ -3310,14 +3407,14 @@ dir_each_child_m(VALUE dir)
}
/*
- * call-seq:
- * dir.children -> array
+ * call-seq:
+ * children -> array
*
- * Returns an array containing all of the filenames except for "."
- * and ".." in this directory.
+ * Returns an array of the entry names in +self+
+ * except for <tt>'.'</tt> and <tt>'..'</tt>:
*
- * d = Dir.new("testdir")
- * d.children #=> ["config.h", "main.rb"]
+ * dir = Dir.new('/example')
+ * dir.children # => ["config.h", "lib", "main.rb"]
*
*/
static VALUE
@@ -3329,19 +3426,23 @@ dir_collect_children(VALUE dir)
}
/*
- * call-seq:
- * Dir.children( dirname ) -> array
- * Dir.children( dirname, encoding: enc ) -> array
+ * call-seq:
+ * Dir.children(dirpath) -> array
+ * Dir.children(dirpath, encoding: 'UTF-8') -> array
*
- * Returns an array containing all of the filenames except for "."
- * and ".." in the given directory. Will raise a SystemCallError if
- * the named directory doesn't exist.
+ * Returns an array of the entry names in the directory at +dirpath+
+ * except for <tt>'.'</tt> and <tt>'..'</tt>;
+ * sets the given encoding onto each returned entry name:
*
- * The optional <i>encoding</i> keyword argument specifies the encoding of the
- * directory. If not specified, the filesystem encoding is used.
+ * Dir.children('/example') # => ["config.h", "lib", "main.rb"]
+ * Dir.children('/example').first.encoding
+ * # => #<Encoding:UTF-8>
+ * Dir.children('/example', encoding: 'US-ASCII').first.encoding
+ * # => #<Encoding:US-ASCII>
*
- * Dir.children("testdir") #=> ["config.h", "main.rb"]
+ * See {String Encoding}[rdoc-ref:encodings.rdoc@String+Encoding].
*
+ * Raises an exception if the directory does not exist.
*/
static VALUE
dir_s_children(int argc, VALUE *argv, VALUE io)
@@ -3415,12 +3516,16 @@ file_s_fnmatch(int argc, VALUE *argv, VALUE obj)
}
/*
- * call-seq:
- * Dir.home() -> "/home/me"
- * Dir.home("root") -> "/root"
+ * call-seq:
+ * Dir.home(user_name = nil) -> dirpath
+ *
+ * Retruns the home directory path of the user specified with +user_name+
+ * if it is not +nil+, or the current login user:
+ *
+ * Dir.home # => "/home/me"
+ * Dir.home('root') # => "/root"
*
- * Returns the home directory of the current user or the named user
- * if given.
+ * Raises ArgumentError if +user_name+ is not a user name.
*/
static VALUE
dir_s_home(int argc, VALUE *argv, VALUE obj)
@@ -3431,7 +3536,7 @@ dir_s_home(int argc, VALUE *argv, VALUE obj)
rb_check_arity(argc, 0, 1);
user = (argc > 0) ? argv[0] : Qnil;
if (!NIL_P(user)) {
- SafeStringValue(user);
+ StringValue(user);
rb_must_asciicompat(user);
u = StringValueCStr(user);
if (*u) {
@@ -3445,10 +3550,15 @@ dir_s_home(int argc, VALUE *argv, VALUE obj)
#if 0
/*
* call-seq:
- * Dir.exist?(file_name) -> true or false
+ * Dir.exist?(dirpath) -> true or false
+ *
+ * Returns whether +dirpath+ is a directory in the underlying file system:
+ *
+ * Dir.exist?('/example') # => true
+ * Dir.exist?('/nosuch') # => false
+ * Dir.exist?('/example/main.rb') # => false
*
- * Returns <code>true</code> if the named file is a directory,
- * <code>false</code> otherwise.
+ * Same as File.directory?.
*
*/
VALUE
@@ -3490,10 +3600,18 @@ nogvl_dir_empty_p(void *ptr)
/*
* call-seq:
- * Dir.empty?(path_name) -> true or false
+ * Dir.empty?(dirpath) -> true or false
*
- * Returns <code>true</code> if the named file is an empty directory,
- * <code>false</code> if it is not a directory or non-empty.
+ * Returns whether +dirpath+ specifies an empty directory:
+ *
+ * dirpath = '/tmp/foo'
+ * Dir.mkdir(dirpath)
+ * Dir.empty?(dirpath) # => true
+ * Dir.empty?('/example') # => false
+ * Dir.empty?('/example/main.rb') # => false
+ *
+ * Raises an exception if +dirpath+ does not specify a directory or file
+ * in the underlying file system.
*/
static VALUE
rb_dir_s_empty_p(VALUE obj, VALUE dirname)
@@ -3527,8 +3645,7 @@ rb_dir_s_empty_p(VALUE obj, VALUE dirname)
}
#endif
- result = (VALUE)rb_thread_call_without_gvl(nogvl_dir_empty_p, (void *)path,
- RUBY_UBF_IO, 0);
+ result = (VALUE)IO_WITHOUT_GVL(nogvl_dir_empty_p, (void *)path);
if (FIXNUM_P(result)) {
rb_syserr_fail_path((int)FIX2LONG(result), orig);
}
@@ -3538,6 +3655,9 @@ rb_dir_s_empty_p(VALUE obj, VALUE dirname)
void
Init_Dir(void)
{
+ rb_gc_register_address(&chdir_lock.path);
+ rb_gc_register_address(&chdir_lock.thread);
+
rb_cDir = rb_define_class("Dir", rb_cObject);
rb_include_module(rb_cDir, rb_mEnumerable);
@@ -3582,51 +3702,26 @@ Init_Dir(void)
rb_define_singleton_method(rb_cFile,"fnmatch", file_s_fnmatch, -1);
rb_define_singleton_method(rb_cFile,"fnmatch?", file_s_fnmatch, -1);
- /* Document-const: File::Constants::FNM_NOESCAPE
- *
- * Disables escapes in File.fnmatch and Dir.glob patterns
- */
+ /* Document-const: FNM_NOESCAPE
+ * {File::FNM_NOESCAPE}[rdoc-ref:File::Constants@File-3A-3AFNM_NOESCAPE] */
rb_file_const("FNM_NOESCAPE", INT2FIX(FNM_NOESCAPE));
-
- /* Document-const: File::Constants::FNM_PATHNAME
- *
- * Wildcards in File.fnmatch and Dir.glob patterns do not match directory
- * separators
- */
+ /* Document-const: FNM_PATHNAME
+ * {File::FNM_PATHNAME}[rdoc-ref:File::Constants@File-3A-3AFNM_PATHNAME] */
rb_file_const("FNM_PATHNAME", INT2FIX(FNM_PATHNAME));
-
- /* Document-const: File::Constants::FNM_DOTMATCH
- *
- * The '*' wildcard matches filenames starting with "." in File.fnmatch
- * and Dir.glob patterns
- */
+ /* Document-const: FNM_DOTMATCH
+ * {File::FNM_DOTMATCH}[rdoc-ref:File::Constants@File-3A-3AFNM_DOTMATCH] */
rb_file_const("FNM_DOTMATCH", INT2FIX(FNM_DOTMATCH));
-
- /* Document-const: File::Constants::FNM_CASEFOLD
- *
- * Makes File.fnmatch patterns case insensitive (but not Dir.glob
- * patterns).
- */
+ /* Document-const: FNM_CASEFOLD
+ * {File::FNM_CASEFOLD}[rdoc-ref:File::Constants@File-3A-3AFNM_CASEFOLD] */
rb_file_const("FNM_CASEFOLD", INT2FIX(FNM_CASEFOLD));
-
- /* Document-const: File::Constants::FNM_EXTGLOB
- *
- * Allows file globbing through "{a,b}" in File.fnmatch patterns.
- */
+ /* Document-const: FNM_EXTGLOB
+ * {File::FNM_EXTGLOB}[rdoc-ref:File::Constants@File-3A-3AFNM_EXTGLOB] */
rb_file_const("FNM_EXTGLOB", INT2FIX(FNM_EXTGLOB));
-
- /* Document-const: File::Constants::FNM_SYSCASE
- *
- * System default case insensitiveness, equals to FNM_CASEFOLD or
- * 0.
- */
+ /* Document-const: FNM_SYSCASE
+ * {File::FNM_SYSCASE}[rdoc-ref:File::Constants@File-3A-3AFNM_SYSCASE] */
rb_file_const("FNM_SYSCASE", INT2FIX(FNM_SYSCASE));
-
- /* Document-const: File::Constants::FNM_SHORTNAME
- *
- * Makes patterns to match short names if existing. Valid only
- * on Microsoft Windows.
- */
+ /* Document-const: FNM_SHORTNAME
+ * {File::FNM_SHORTNAME}[rdoc-ref:File::Constants@File-3A-3AFNM_SHORTNAME] */
rb_file_const("FNM_SHORTNAME", INT2FIX(FNM_SHORTNAME));
}
diff --git a/dir.rb b/dir.rb
index 2e426b0881..42b475ab2c 100644
--- a/dir.rb
+++ b/dir.rb
@@ -1,11 +1,85 @@
-# Objects of class Dir are directory streams representing
-# directories in the underlying file system. They provide a variety
-# of ways to list directories and their contents. See also File.
+# An object of class \Dir represents a directory in the underlying file system.
#
-# The directory used in these examples contains the two regular files
-# (<code>config.h</code> and <code>main.rb</code>), the parent
-# directory (<code>..</code>), and the directory itself
-# (<code>.</code>).
+# It consists mainly of:
+#
+# - A string _path_, given when the object is created,
+# that specifies a directory in the underlying file system;
+# method #path returns the path.
+# - A collection of string <i>entry names</i>,
+# each of which is the name of a directory or file in the underlying file system;
+# the entry names may be retrieved
+# in an {array-like fashion}[rdoc-ref:Dir@Dir+As+Array-Like]
+# or in a {stream-like fashion}[rdoc-ref:Dir@Dir+As+Stream-Like].
+#
+# == About the Examples
+#
+# Some examples on this page use this simple file tree:
+#
+# example/
+# ├── config.h
+# ├── lib/
+# │ ├── song/
+# │ │ └── karaoke.rb
+# │ └── song.rb
+# └── main.rb
+#
+# Others use the file tree for the
+# {Ruby project itself}[https://github.com/ruby/ruby].
+#
+# == \Dir As \Array-Like
+#
+# A \Dir object is in some ways array-like:
+#
+# - It has instance methods #children, #each, and #each_child.
+# - It includes {module Enumerable}[rdoc-ref:Enumerable@What-27s+Here].
+#
+# == \Dir As Stream-Like
+#
+# A \Dir object is in some ways stream-like.
+#
+# The stream is initially open for reading,
+# but may be closed manually (using method #close),
+# and will be closed on block exit if created by Dir.open called with a block.
+# The closed stream may not be further manipulated,
+# and may not be reopened.
+#
+# The stream has a _position_, which is the index of an entry in the directory:
+#
+# - The initial position is zero (before the first entry).
+# - \Method #tell (aliased as #pos) returns the position.
+# - \Method #pos= sets the position (but ignores a value outside the stream),
+# and returns the position.
+# - \Method #seek is like #pos=, but returns +self+ (convenient for chaining).
+# - \Method #read, if not at end-of-stream, reads the next entry and increments
+# the position;
+# if at end-of-stream, does not increment the position.
+# - \Method #rewind sets the position to zero.
+#
+# Examples (using the {simple file tree}[rdoc-ref:Dir@About+the+Examples]):
+#
+# dir = Dir.new('example') # => #<Dir:example>
+# dir.pos # => 0
+#
+# dir.read # => "."
+# dir.read # => ".."
+# dir.read # => "config.h"
+# dir.read # => "lib"
+# dir.read # => "main.rb"
+# dir.pos # => 5
+# dir.read # => nil
+# dir.pos # => 5
+#
+# dir.rewind # => #<Dir:example>
+# dir.pos # => 0
+#
+# dir.pos = 3 # => 3
+# dir.pos # => 3
+#
+# dir.seek(4) # => #<Dir:example>
+# dir.pos # => 4
+#
+# dir.close # => nil
+# dir.read # Raises IOError.
#
# == What's Here
#
@@ -80,20 +154,32 @@
# closing it upon block exit.
# - ::unlink (aliased as ::delete and ::rmdir): Removes the given directory.
# - #inspect: Returns a string description of +self+.
+#
class Dir
# call-seq:
- # Dir.open( string ) -> aDir
- # Dir.open( string, encoding: enc ) -> aDir
- # Dir.open( string ) {| aDir | block } -> anObject
- # Dir.open( string, encoding: enc ) {| aDir | block } -> anObject
- #
- # The optional <i>encoding</i> keyword argument specifies the encoding of the directory.
- # If not specified, the filesystem encoding is used.
- #
- # With no block, <code>open</code> is a synonym for Dir::new. If a
- # block is present, it is passed <i>aDir</i> as a parameter. The
- # directory is closed at the end of the block, and Dir::open returns
- # the value of the block.
+ # Dir.open(dirpath) -> dir
+ # Dir.open(dirpath, encoding: nil) -> dir
+ # Dir.open(dirpath) {|dir| ... } -> object
+ # Dir.open(dirpath, encoding: nil) {|dir| ... } -> object
+ #
+ # Creates a new \Dir object _dir_ for the directory at +dirpath+.
+ #
+ # With no block, the method equivalent to Dir.new(dirpath, encoding):
+ #
+ # Dir.open('.') # => #<Dir:.>
+ #
+ # With a block given, the block is called with the created _dir_;
+ # on block exit _dir_ is closed and the block's value is returned:
+ #
+ # Dir.open('.') {|dir| dir.inspect } # => "#<Dir:.>"
+ #
+ # The value given with optional keyword argument +encoding+
+ # specifies the encoding for the directory entry names;
+ # if +nil+ (the default), the file system's encoding is used:
+ #
+ # Dir.open('.').read.encoding # => #<Encoding:UTF-8>
+ # Dir.open('.', encoding: 'US-ASCII').read.encoding # => #<Encoding:US-ASCII>
+ #
def self.open(name, encoding: nil, &block)
dir = Primitive.dir_s_open(name, encoding)
if block
@@ -108,115 +194,221 @@ class Dir
end
# call-seq:
- # Dir.new( string ) -> aDir
- # Dir.new( string, encoding: enc ) -> aDir
+ # Dir.new(dirpath) -> dir
+ # Dir.new(dirpath, encoding: nil) -> dir
+ #
+ # Returns a new \Dir object for the directory at +dirpath+:
+ #
+ # Dir.new('.') # => #<Dir:.>
#
- # Returns a new directory object for the named directory.
+ # The value given with optional keyword argument +encoding+
+ # specifies the encoding for the directory entry names;
+ # if +nil+ (the default), the file system's encoding is used:
+ #
+ # Dir.new('.').read.encoding # => #<Encoding:UTF-8>
+ # Dir.new('.', encoding: 'US-ASCII').read.encoding # => #<Encoding:US-ASCII>
#
- # The optional <i>encoding</i> keyword argument specifies the encoding of the directory.
- # If not specified, the filesystem encoding is used.
def initialize(name, encoding: nil)
Primitive.dir_initialize(name, encoding)
end
# call-seq:
- # Dir[ string [, string ...] [, base: path] [, sort: true] ] -> array
+ # Dir[*patterns, base: nil, sort: true] -> array
+ #
+ # Calls Dir.glob with argument +patterns+
+ # and the values of keyword arguments +base+ and +sort+;
+ # returns the array of selected entry names.
#
- # Equivalent to calling
- # <code>Dir.glob([</code><i>string,...</i><code>], 0)</code>.
def self.[](*args, base: nil, sort: true)
Primitive.dir_s_aref(args, base, sort)
end
# call-seq:
- # Dir.glob( pattern, [flags], [base: path] [, sort: true] ) -> array
- # Dir.glob( pattern, [flags], [base: path] [, sort: true] ) { |filename| block } -> nil
+ # Dir.glob(*patterns, flags: 0, base: nil, sort: true) -> array
+ # Dir.glob(*patterns, flags: 0, base: nil, sort: true) {|entry_name| ... } -> nil
#
- # Expands +pattern+, which is a pattern string or an Array of pattern
- # strings, and returns an array containing the matching filenames.
- # If a block is given, calls the block once for each matching filename,
- # passing the filename as a parameter to the block.
+ # Forms an array _entry_names_ of the entry names selected by the arguments.
#
- # The optional +base+ keyword argument specifies the base directory for
- # interpreting relative pathnames instead of the current working directory.
- # As the results are not prefixed with the base directory name in this
- # case, you will need to prepend the base directory name if you want real
- # paths.
+ # Argument +patterns+ is a string pattern or an array of string patterns;
+ # note that these are not regexps; see below.
#
- # The results which matched single wildcard or character set are sorted in
- # binary ascending order, unless +false+ is given as the optional +sort+
- # keyword argument. The order of an Array of pattern strings and braces
- # are preserved.
+ # Notes for the following examples:
#
- # Note that the pattern is not a regexp, it's closer to a shell glob.
- # See File::fnmatch for the meaning of the +flags+ parameter.
- # Case sensitivity depends on your system (+File::FNM_CASEFOLD+ is ignored).
+ # - <tt>'*'</tt> is the pattern that matches any entry name
+ # except those that begin with <tt>'.'</tt>.
+ # - We use method Array#take to shorten returned arrays
+ # that otherwise would be very large.
#
- # <code>*</code>::
- # Matches any file. Can be restricted by other values in the glob.
- # Equivalent to <code>/.*/mx</code> in regexp.
+ # With no block, returns array _entry_names_;
+ # example (using the {simple file tree}[rdoc-ref:Dir@About+the+Examples]):
#
- # <code>*</code>:: Matches all files
- # <code>c*</code>:: Matches all files beginning with <code>c</code>
- # <code>*c</code>:: Matches all files ending with <code>c</code>
- # <code>\*c\*</code>:: Match all files that have <code>c</code> in them
- # (including at the beginning or end).
+ # Dir.glob('*') # => ["config.h", "lib", "main.rb"]
#
- # Note, this will not match Unix-like hidden files (dotfiles). In order
- # to include those in the match results, you must use the
- # File::FNM_DOTMATCH flag or something like <code>"{*,.*}"</code>.
+ # With a block, calls the block with each of the _entry_names_
+ # and returns +nil+:
#
- # <code>**</code>::
- # Matches directories recursively if followed by <code>/</code>. If
- # this path segment contains any other characters, it is the same as the
- # usual <code>*</code>.
+ # Dir.glob('*') {|entry_name| puts entry_name } # => nil
#
- # <code>?</code>::
- # Matches any one character. Equivalent to <code>/.{1}/</code> in regexp.
+ # Output:
#
- # <code>[set]</code>::
- # Matches any one character in +set+. Behaves exactly like character sets
- # in Regexp, including set negation (<code>[^a-z]</code>).
+ # config.h
+ # lib
+ # main.rb
#
- # <code>{p,q}</code>::
- # Matches either literal <code>p</code> or literal <code>q</code>.
- # Equivalent to pattern alternation in regexp.
+ # If optional keyword argument +flags+ is given,
+ # the value modifies the matching; see below.
#
- # Matching literals may be more than one character in length. More than
- # two literals may be specified.
+ # If optional keyword argument +base+ is given,
+ # its value specifies the base directory.
+ # Each pattern string specifies entries relative to the base directory;
+ # the default is <tt>'.'</tt>.
+ # The base directory is not prepended to the entry names in the result:
#
- # <code>\\</code>::
- # Escapes the next metacharacter.
+ # Dir.glob(pattern, base: 'lib').take(5)
+ # # => ["abbrev.gemspec", "abbrev.rb", "base64.gemspec", "base64.rb", "benchmark.gemspec"]
+ # Dir.glob(pattern, base: 'lib/irb').take(5)
+ # # => ["cmd", "color.rb", "color_printer.rb", "completion.rb", "context.rb"]
#
- # Note that this means you cannot use backslash on windows as part of a
- # glob, i.e. <code>Dir["c:\\foo*"]</code> will not work, use
- # <code>Dir["c:/foo*"]</code> instead.
+ # If optional keyword +sort+ is given, its value specifies whether
+ # the array is to be sorted; the default is +true+.
+ # Passing value +false+ with that keyword disables sorting
+ # (though the underlying file system may already have sorted the array).
#
- # Examples:
+ # <b>Patterns</b>
+ #
+ # Each pattern string is expanded
+ # according to certain metacharacters;
+ # examples below use the {Ruby file tree}[rdoc-ref:Dir@About+the+Examples]:
+ #
+ # - <tt>'*'</tt>: Matches any substring in an entry name,
+ # similar in meaning to regexp <tt>/.*/mx</tt>;
+ # may be restricted by other values in the pattern strings:
+ #
+ # - <tt>'*'</tt> matches all entry names:
+ #
+ # Dir.glob('*').take(3) # => ["BSDL", "CONTRIBUTING.md", "COPYING"]
+ #
+ # - <tt>'c*'</tt> matches entry names beginning with <tt>'c'</tt>:
+ #
+ # Dir.glob('c*').take(3) # => ["CONTRIBUTING.md", "COPYING", "COPYING.ja"]
+ #
+ # - <tt>'*c'</tt> matches entry names ending with <tt>'c'</tt>:
+ #
+ # Dir.glob('*c').take(3) # => ["addr2line.c", "array.c", "ast.c"]
+ #
+ # - <tt>'\*c\*'</tt> matches entry names that contain <tt>'c'</tt>,
+ # even at the beginning or end:
+ #
+ # Dir.glob('*c*').take(3) # => ["CONTRIBUTING.md", "COPYING", "COPYING.ja"]
+ #
+ # Does not match Unix-like hidden entry names ("dot files").
+ # To include those in the matched entry names,
+ # use flag IO::FNM_DOTMATCH or something like <tt>'{*,.*}'</tt>.
+ #
+ # - <tt>'**'</tt>: Matches entry names recursively
+ # if followed by the slash character <tt>'/'</tt>:
+ #
+ # Dir.glob('**/').take(3) # => ["basictest/", "benchmark/", "benchmark/gc/"]
+ #
+ # If the string pattern contains other characters
+ # or is not followed by a slash character,
+ # it is equivalent to <tt>'*'</tt>.
+ #
+ # - <tt>'?'</tt> Matches any single character;
+ # similar in meaning to regexp <tt>/./</tt>:
+ #
+ # Dir.glob('io.?') # => ["io.c"]
+ #
+ # - <tt>'[_set_]'</tt>: Matches any one character in the string _set_;
+ # behaves like a {Regexp character class}[rdoc-ref:Regexp@Character+Classes],
+ # including set negation (<tt>'[^a-z]'</tt>):
+ #
+ # Dir.glob('*.[a-z][a-z]').take(3)
+ # # => ["CONTRIBUTING.md", "COPYING.ja", "KNOWNBUGS.rb"]
+ #
+ # - <tt>'{_abc_,_xyz_}'</tt>:
+ # Matches either string _abc_ or string _xyz_;
+ # behaves like {Regexp alternation}[rdoc-ref:Regexp@Alternation]:
+ #
+ # Dir.glob('{LEGAL,BSDL}') # => ["LEGAL", "BSDL"]
+ #
+ # More than two alternatives may be given.
+ #
+ # - <tt>\\</tt>: Escapes the following metacharacter.
+ #
+ # Note that on Windows, the backslash character may not be used
+ # in a string pattern:
+ # <tt>Dir['c:\\foo*']</tt> will not work, use <tt>Dir['c:/foo*']</tt> instead.
+ #
+ # More examples (using the {simple file tree}[rdoc-ref:Dir@About+the+Examples]):
+ #
+ # # We're in the example directory.
+ # File.basename(Dir.pwd) # => "example"
+ # Dir.glob('config.?') # => ["config.h"]
+ # Dir.glob('*.[a-z][a-z]') # => ["main.rb"]
+ # Dir.glob('*.[^r]*') # => ["config.h"]
+ # Dir.glob('*.{rb,h}') # => ["main.rb", "config.h"]
+ # Dir.glob('*') # => ["config.h", "lib", "main.rb"]
+ # Dir.glob('*', File::FNM_DOTMATCH) # => [".", "config.h", "lib", "main.rb"]
+ # Dir.glob(["*.rb", "*.h"]) # => ["main.rb", "config.h"]
+ #
+ # Dir.glob('**/*.rb')
+ # => ["lib/song/karaoke.rb", "lib/song.rb", "main.rb"]
+ #
+ # Dir.glob('**/*.rb', base: 'lib') # => ["song/karaoke.rb", "song.rb"]
+ #
+ # Dir.glob('**/lib') # => ["lib"]
+ #
+ # Dir.glob('**/lib/**/*.rb') # => ["lib/song/karaoke.rb", "lib/song.rb"]
+ #
+ # Dir.glob('**/lib/*.rb') # => ["lib/song.rb"]
+ #
+ # <b>Flags</b>
+ #
+ # If optional keyword argument +flags+ is given (the default is zero -- no flags),
+ # its value should be the bitwise OR of one or more of the constants
+ # defined in module File::Constants.
+ #
+ # Example:
+ #
+ # flags = File::FNM_EXTGLOB | File::FNM_DOTMATCH
+ #
+ # Specifying flags can extend, restrict, or otherwise modify the matching.
+ #
+ # The flags for this method (other constants in File::Constants do not apply):
+ #
+ # - File::FNM_DOTMATCH:
+ # specifies that entry names beginning with <tt>'.'</tt>
+ # should be considered for matching:
+ #
+ # Dir.glob('*').take(5)
+ # # => ["BSDL", "CONTRIBUTING.md", "COPYING", "COPYING.ja", "GPL"]
+ # Dir.glob('*', flags: File::FNM_DOTMATCH).take(5)
+ # # => [".", ".appveyor.yml", ".cirrus.yml", ".dir-locals.el", ".document"]
#
- # Dir["config.?"] #=> ["config.h"]
- # Dir.glob("config.?") #=> ["config.h"]
- # Dir.glob("*.[a-z][a-z]") #=> ["main.rb"]
- # Dir.glob("*.[^r]*") #=> ["config.h"]
- # Dir.glob("*.{rb,h}") #=> ["main.rb", "config.h"]
- # Dir.glob("*") #=> ["config.h", "main.rb"]
- # Dir.glob("*", File::FNM_DOTMATCH) #=> [".", "config.h", "main.rb"]
- # Dir.glob(["*.rb", "*.h"]) #=> ["main.rb", "config.h"]
+ # - File::FNM_EXTGLOB:
+ # enables the pattern extension
+ # <tt>'{_a_,_b_}'</tt>, which matches pattern _a_ and pattern _b_;
+ # behaves like a
+ # {regexp union}[rdoc-ref:Regexp.union]
+ # (e.g., <tt>'(?:_a_|_b_)'</tt>):
#
- # Dir.glob("**/*.rb") #=> ["main.rb",
- # # "lib/song.rb",
- # # "lib/song/karaoke.rb"]
+ # pattern = '{LEGAL,BSDL}'
+ # Dir.glob(pattern) # => ["LEGAL", "BSDL"]
#
- # Dir.glob("**/*.rb", base: "lib") #=> ["song.rb",
- # # "song/karaoke.rb"]
+ # - File::FNM_NOESCAPE:
+ # specifies that escaping with the backslash character <tt>'\'</tt>
+ # is disabled; the character is not an escape character.
#
- # Dir.glob("**/lib") #=> ["lib"]
+ # - File::FNM_PATHNAME:
+ # specifies that metacharacters <tt>'*'</tt> and <tt>'?'</tt>
+ # do not match directory separators.
#
- # Dir.glob("**/lib/**/*.rb") #=> ["lib/song.rb",
- # # "lib/song/karaoke.rb"]
+ # - File::FNM_SHORTNAME:
+ # specifies that patterns may match short names if they exist; Windows only.
#
- # Dir.glob("**/lib/*.rb") #=> ["lib/song.rb"]
def self.glob(pattern, _flags = 0, flags: _flags, base: nil, sort: true)
+ Primitive.attr! :use_block
Primitive.dir_s_glob(pattern, flags, base, sort)
end
end
diff --git a/dln.c b/dln.c
index 77bfe91b28..89f16b54f0 100644
--- a/dln.c
+++ b/dln.c
@@ -76,6 +76,12 @@ void *xrealloc();
# include <unistd.h>
#endif
+bool
+dln_supported_p(void)
+{
+ return true;
+}
+
#ifndef dln_loaderror
static void
dln_loaderror(const char *format, ...)
@@ -107,36 +113,45 @@ dln_loaderror(const char *format, ...)
#endif
#if defined(_WIN32) || defined(USE_DLN_DLOPEN)
-static size_t
-init_funcname_len(const char **file)
+struct string_part {
+ const char *ptr;
+ size_t len;
+};
+
+static struct string_part
+init_funcname_len(const char *file)
{
- const char *p = *file, *base, *dot = NULL;
+ const char *p = file, *base, *dot = NULL;
/* Load the file as an object one */
for (base = p; *p; p++) { /* Find position of last '/' */
if (*p == '.' && !dot) dot = p;
if (isdirsep(*p)) base = p+1, dot = NULL;
}
- *file = base;
/* Delete suffix if it exists */
- return (dot ? dot : p) - base;
+ const size_t len = (dot ? dot : p) - base;
+ return (struct string_part){base, len};
+}
+
+static inline char *
+concat_funcname(char *buf, const char *prefix, size_t plen, const struct string_part base)
+{
+ if (!buf) {
+ dln_memerror();
+ }
+ memcpy(buf, prefix, plen);
+ memcpy(buf + plen, base.ptr, base.len);
+ buf[plen + base.len] = '\0';
+ return buf;
}
-static const char funcname_prefix[sizeof(FUNCNAME_PREFIX) - 1] = FUNCNAME_PREFIX;
-
-#define init_funcname(buf, file) do {\
- const char *base = (file);\
- const size_t flen = init_funcname_len(&base);\
- const size_t plen = sizeof(funcname_prefix);\
- char *const tmp = ALLOCA_N(char, plen+flen+1);\
- if (!tmp) {\
- dln_memerror();\
- }\
- memcpy(tmp, funcname_prefix, plen);\
- memcpy(tmp+plen, base, flen);\
- tmp[plen+flen] = '\0';\
- *(buf) = tmp;\
+#define build_funcname(prefix, buf, file) do {\
+ const struct string_part f = init_funcname_len(file);\
+ const size_t plen = sizeof(prefix "") - 1;\
+ *(buf) = concat_funcname(ALLOCA_N(char, plen+f.len+1), prefix, plen, f);\
} while (0)
+
+#define init_funcname(buf, file) build_funcname(FUNCNAME_PREFIX, buf, file)
#endif
#ifdef USE_DLN_DLOPEN
@@ -185,7 +200,6 @@ dln_strerror(char *message, size_t size)
}
return message;
}
-#define dln_strerror() dln_strerror(message, sizeof message)
#elif defined USE_DLN_DLOPEN
static const char *
dln_strerror(void)
@@ -272,13 +286,15 @@ rb_w32_check_imported(HMODULE ext, HMODULE mine)
static bool
dln_incompatible_func(void *handle, const char *funcname, void *const fp, const char **libname)
{
- Dl_info dli;
void *ex = dlsym(handle, funcname);
if (!ex) return false;
if (ex == fp) return false;
+# if defined(HAVE_DLADDR)
+ Dl_info dli;
if (dladdr(ex, &dli)) {
*libname = dli.dli_fname;
}
+# endif
return true;
}
@@ -328,16 +344,13 @@ dln_disable_dlclose(void)
#endif
#if defined(_WIN32) || defined(USE_DLN_DLOPEN)
-static void *
-dln_open(const char *file)
+void *
+dln_open(const char *file, char *error, size_t size)
{
static const char incompatible[] = "incompatible library version";
- const char *error = NULL;
void *handle;
#if defined(_WIN32)
- char message[1024];
-
/* Convert the file path to wide char */
WCHAR *winfile = rb_w32_mbstr_to_wstr(CP_UTF8, file, -1, NULL);
if (!winfile) {
@@ -349,15 +362,15 @@ dln_open(const char *file)
free(winfile);
if (!handle) {
- error = dln_strerror();
- goto failed;
+ strlcpy(error, dln_strerror(error, size), size);
+ return NULL;
}
# if defined(RUBY_EXPORT)
if (!rb_w32_check_imported(handle, rb_libruby_handle())) {
FreeLibrary(handle);
- error = incompatible;
- goto failed;
+ strlcpy(error, incompatible, size);
+ return NULL;
}
# endif
@@ -376,8 +389,8 @@ dln_open(const char *file)
/* Load file */
handle = dlopen(file, RTLD_LAZY|RTLD_GLOBAL);
if (handle == NULL) {
- error = dln_strerror();
- goto failed;
+ strlcpy(error, dln_strerror(), size);
+ return NULL;
}
# if defined(RUBY_EXPORT)
@@ -399,11 +412,15 @@ dln_open(const char *file)
libruby_name = tmp;
}
dlclose(handle);
+
if (libruby_name) {
- dln_loaderror("linked to incompatible %s - %s", libruby_name, file);
+ snprintf(error, size, "linked to incompatible %s - %s", libruby_name, file);
+ }
+ else {
+ strlcpy(error, incompatible, size);
}
- error = incompatible;
- goto failed;
+
+ return NULL;
}
}
}
@@ -411,41 +428,68 @@ dln_open(const char *file)
#endif
return handle;
-
- failed:
- dln_loaderror("%s - %s", error, file);
}
-static void *
+void *
dln_sym(void *handle, const char *symbol)
{
- void *func;
- const char *error;
-
#if defined(_WIN32)
- char message[1024];
+ return GetProcAddress(handle, symbol);
+#elif defined(USE_DLN_DLOPEN)
+ return dlsym(handle, symbol);
+#endif
+}
- func = GetProcAddress(handle, symbol);
- if (func == NULL) {
- error = dln_strerror();
- goto failed;
- }
+static void *
+dln_sym_func(void *handle, const char *symbol)
+{
+ void *func = dln_sym(handle, symbol);
-#elif defined(USE_DLN_DLOPEN)
- func = dlsym(handle, symbol);
if (func == NULL) {
+ const char *error;
+#if defined(_WIN32)
+ char message[1024];
+ error = dln_strerror(message, sizeof(message));
+#elif defined(USE_DLN_DLOPEN)
const size_t errlen = strlen(error = dln_strerror()) + 1;
error = memcpy(ALLOCA_N(char, errlen), error, errlen);
- goto failed;
- }
#endif
-
+ dln_loaderror("%s - %s", error, symbol);
+ }
return func;
-
- failed:
- dln_loaderror("%s - %s", error, symbol);
}
+
+#define dln_sym_callable(rettype, argtype, handle, symbol) \
+ (*(rettype (*)argtype)dln_sym_func(handle, symbol))
+#endif
+
+void *
+dln_symbol(void *handle, const char *symbol)
+{
+#if defined(_WIN32) || defined(USE_DLN_DLOPEN)
+ if (EXTERNAL_PREFIX[0]) {
+ const size_t symlen = strlen(symbol);
+ char *const tmp = ALLOCA_N(char, symlen + sizeof(EXTERNAL_PREFIX));
+ if (!tmp) dln_memerror();
+ memcpy(tmp, EXTERNAL_PREFIX, sizeof(EXTERNAL_PREFIX) - 1);
+ memcpy(tmp + sizeof(EXTERNAL_PREFIX) - 1, symbol, symlen + 1);
+ symbol = tmp;
+ }
+ if (handle == NULL) {
+# if defined(USE_DLN_DLOPEN)
+ handle = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL);
+# elif defined(_WIN32)
+ handle = rb_libruby_handle();
+# else
+ return NULL;
+# endif
+ }
+ return dln_sym(handle, symbol);
+#else
+ return NULL;
#endif
+}
+
#if defined(RUBY_DLN_CHECK_ABI) && defined(USE_DLN_DLOPEN)
static bool
@@ -460,22 +504,27 @@ void *
dln_load(const char *file)
{
#if defined(_WIN32) || defined(USE_DLN_DLOPEN)
- void *handle = dln_open(file);
+ char error[1024];
+ void *handle = dln_open(file, error, sizeof(error));
+
+ if (handle == NULL) {
+ dln_loaderror("%s - %s", error, file);
+ }
#ifdef RUBY_DLN_CHECK_ABI
- unsigned long long (*abi_version_fct)(void) = (unsigned long long(*)(void))dln_sym(handle, "ruby_abi_version");
- unsigned long long binary_abi_version = (*abi_version_fct)();
- if (binary_abi_version != ruby_abi_version() && abi_check_enabled_p()) {
+ typedef unsigned long long abi_version_number;
+ abi_version_number binary_abi_version =
+ dln_sym_callable(abi_version_number, (void), handle, EXTERNAL_PREFIX "ruby_abi_version")();
+ if (binary_abi_version != RUBY_ABI_VERSION && abi_check_enabled_p()) {
dln_loaderror("incompatible ABI version of binary - %s", file);
}
#endif
char *init_fct_name;
init_funcname(&init_fct_name, file);
- void (*init_fct)(void) = (void(*)(void))dln_sym(handle, init_fct_name);
/* Call the init code */
- (*init_fct)();
+ dln_sym_callable(void, (void), handle, init_fct_name)();
return handle;
diff --git a/dln.h b/dln.h
index 902f753450..26df5266f7 100644
--- a/dln.h
+++ b/dln.h
@@ -22,9 +22,12 @@ RUBY_SYMBOL_EXPORT_BEGIN
#define DLN_FIND_EXTRA_ARG_DECL
#endif
+bool dln_supported_p(void);
char *dln_find_exe_r(const char*,const char*,char*,size_t DLN_FIND_EXTRA_ARG_DECL);
char *dln_find_file_r(const char*,const char*,char*,size_t DLN_FIND_EXTRA_ARG_DECL);
void *dln_load(const char*);
+void *dln_open(const char *file, char *error, size_t size);
+void *dln_symbol(void*,const char*);
RUBY_SYMBOL_EXPORT_END
diff --git a/dln_find.c b/dln_find.c
index 5d380f5d39..ae37b9dde4 100644
--- a/dln_find.c
+++ b/dln_find.c
@@ -11,11 +11,9 @@
#ifdef RUBY_EXPORT
#include "ruby/ruby.h"
-#define dln_warning rb_warning
-#define dln_warning_arg
+#define dln_warning(...) rb_warning(__VA_ARGS__)
#else
-#define dln_warning fprintf
-#define dln_warning_arg stderr,
+#define dln_warning(...) fprintf(stderr, __VA_ARGS__)
#endif
#include "dln.h"
@@ -75,7 +73,7 @@ dln_find_exe_r(const char *fname, const char *path, char *buf, size_t size
".";
}
buf = dln_find_1(fname, path, buf, size, 1 DLN_FIND_EXTRA_ARG);
- if (envpath) free(envpath);
+ free(envpath);
return buf;
}
@@ -109,7 +107,7 @@ dln_find_1(const char *fname, const char *path, char *fbuf, size_t size,
static const char pathname_too_long[] = "openpath: pathname too long (ignored)\n\
\tDirectory \"%.*s\"%s\n\tFile \"%.*s\"%s\n";
-#define PATHNAME_TOO_LONG() dln_warning(dln_warning_arg pathname_too_long, \
+#define PATHNAME_TOO_LONG() dln_warning(pathname_too_long, \
((bp - fbuf) > 100 ? 100 : (int)(bp - fbuf)), fbuf, \
((bp - fbuf) > 100 ? "..." : ""), \
(fnlen > 100 ? 100 : (int)fnlen), fname, \
@@ -120,8 +118,7 @@ dln_find_1(const char *fname, const char *path, char *fbuf, size_t size,
RETURN_IF(!fname);
fnlen = strlen(fname);
if (fnlen >= size) {
- dln_warning(dln_warning_arg
- "openpath: pathname too long (ignored)\n\tFile \"%.*s\"%s\n",
+ dln_warning("openpath: pathname too long (ignored)\n\tFile \"%.*s\"%s\n",
(fnlen > 100 ? 100 : (int)fnlen), fname,
(fnlen > 100 ? "..." : ""));
return NULL;
diff --git a/dmydln.c b/dmydln.c
index d05cda0b8e..1f5b59022b 100644
--- a/dmydln.c
+++ b/dmydln.c
@@ -1,5 +1,14 @@
+// This file is used by miniruby, not ruby.
+// ruby uses dln.c.
+
#include "ruby/ruby.h"
+bool
+dln_supported_p(void)
+{
+ return false;
+}
+
NORETURN(void *dln_load(const char *));
void*
dln_load(const char *file)
@@ -8,3 +17,21 @@ dln_load(const char *file)
UNREACHABLE_RETURN(NULL);
}
+
+NORETURN(void *dln_symbol(void*,const char*));
+void*
+dln_symbol(void *handle, const char *symbol)
+{
+ rb_loaderror("this executable file can't load extension libraries");
+
+ UNREACHABLE_RETURN(NULL);
+}
+
+void*
+dln_open(const char *library, char *error, size_t size)
+{
+ static const char *error_str = "this executable file can't load extension libraries";
+ strlcpy(error, error_str, size);
+ return NULL;
+}
+
diff --git a/dmyenc.c b/dmyenc.c
index 75b8a2da43..4771841281 100644
--- a/dmyenc.c
+++ b/dmyenc.c
@@ -1,3 +1,17 @@
+// This file is used by dynamically-linked ruby, which has no
+// statically-linked encodings other than the builtin encodings.
+//
+// - miniruby does not use this Init_enc. Instead, "miniinit.c"
+// provides Init_enc, which defines only the builtin encodings.
+//
+// - Dynamically-linked ruby uses this Init_enc, which requires
+// "enc/encdb.so" to load the builtin encodings and set up the
+// optional encodings.
+//
+// - Statically-linked ruby does not use this Init_enc. Instead,
+// "enc/encinit.c" (which is a generated file) defines Init_enc,
+// which activates the encodings.
+
#define require(name) ruby_require_internal(name, (unsigned int)sizeof(name)-1)
int ruby_require_internal(const char *, int);
diff --git a/dmyext.c b/dmyext.c
index 4d273f7faf..66be95ab4e 100644
--- a/dmyext.c
+++ b/dmyext.c
@@ -1,3 +1,17 @@
+// This file is used by dynamically-linked ruby, which has no
+// statically-linked extension libraries.
+//
+// - miniruby does not use this Init_ext. Instead, "miniinit.c"
+// provides Init_enc, which does nothing too. It does not support
+// require'ing extension libraries.
+//
+// - Dynamically-linked ruby uses this Init_ext, which does
+// nothing. It loads extension libraries by dlopen, etc.
+//
+// - Statically-linked ruby does not use this Init_ext. Instead,
+// "ext/extinit.c" (which is a generated file) defines Init_ext,
+// which activates the (statically-linked) extension libraries.
+
void
Init_ext(void)
{
diff --git a/doc/.document b/doc/.document
index 5ef2d99651..8174394f48 100644
--- a/doc/.document
+++ b/doc/.document
@@ -1,8 +1,12 @@
*.md
*.rb
-*.rdoc
+[^_]*.rdoc
contributing
NEWS
syntax
optparse
rdoc
+regexp
+rjit
+yjit
+ruby
diff --git a/doc/ChangeLog/ChangeLog-2.1.0 b/doc/ChangeLog/ChangeLog-2.1.0
index 5b670b31c9..7964a682eb 100644
--- a/doc/ChangeLog/ChangeLog-2.1.0
+++ b/doc/ChangeLog/ChangeLog-2.1.0
@@ -659,7 +659,7 @@ Mon Dec 9 02:10:32 2013 NARUSE, Yui <naruse@ruby-lang.org>
* lib/net/http/responses.rb:
Add `HTTPIMUsed`, as it is also supported by rack/rails.
- RFC - http://tools.ietf.org/html/rfc3229
+ RFC - https://www.rfc-editor.org/rfc/rfc3229
by Vipul A M <vipulnsward@gmail.com>
https://github.com/ruby/ruby/pull/447 fix GH-447
diff --git a/doc/ChangeLog/ChangeLog-2.2.0 b/doc/ChangeLog/ChangeLog-2.2.0
index 5a7dbf826d..0edcf0122b 100644
--- a/doc/ChangeLog/ChangeLog-2.2.0
+++ b/doc/ChangeLog/ChangeLog-2.2.0
@@ -5648,7 +5648,7 @@ Wed Aug 6 04:16:05 2014 NARUSE, Yui <naruse@ruby-lang.org>
* lib/net/http/requests.rb (Net::HTTP::Options::RESPONSE_HAS_BODY):
OPTIONS requests may have response bodies. [Feature #8429]
- http://tools.ietf.org/html/rfc7231#section-4.3.7
+ https://www.rfc-editor.org/rfc/rfc7231#section-4.3.7
Wed Aug 6 03:18:04 2014 NARUSE, Yui <naruse@ruby-lang.org>
diff --git a/doc/ChangeLog/ChangeLog-2.4.0 b/doc/ChangeLog/ChangeLog-2.4.0
index a297a579d1..30e9ccab3d 100644
--- a/doc/ChangeLog/ChangeLog-2.4.0
+++ b/doc/ChangeLog/ChangeLog-2.4.0
@@ -7356,7 +7356,7 @@ Thu Mar 17 11:51:48 2016 NARUSE, Yui <naruse@ruby-lang.org>
Note: CryptGenRandom function is PRNG and doesn't check its entropy,
so it won't block. [Bug #12139]
https://msdn.microsoft.com/ja-jp/library/windows/desktop/aa379942.aspx
- https://tools.ietf.org/html/rfc4086#section-7.1.3
+ https://www.rfc-editor.org/rfc/rfc4086#section-7.1.3
https://eprint.iacr.org/2007/419.pdf
http://www.cs.huji.ac.il/~dolev/pubs/thesis/msc-thesis-leo.pdf
diff --git a/doc/NEWS/NEWS-3.3.0.md b/doc/NEWS/NEWS-3.3.0.md
new file mode 100644
index 0000000000..364786d754
--- /dev/null
+++ b/doc/NEWS/NEWS-3.3.0.md
@@ -0,0 +1,529 @@
+# NEWS for Ruby 3.3.0
+
+This document is a list of user-visible feature changes
+since the **3.2.0** release, except for bug fixes.
+
+Note that each entry is kept to a minimum, see links for details.
+
+## Command line options
+
+* A new `performance` warning category was introduced.
+ They are not displayed by default even in verbose mode.
+ Turn them on with `-W:performance` or `Warning[:performance] = true`. [[Feature #19538]]
+
+* A new `RUBY_CRASH_REPORT` environment variable was introduced to allow
+ redirecting Ruby crash reports to a file or sub command. See the `BUG REPORT ENVIRONMENT`
+ section of the ruby manpage for further details. [[Feature #19790]]
+
+## Core classes updates
+
+Note: We're only listing outstanding class updates.
+
+* Array
+
+ * Array#pack now raises ArgumentError for unknown directives. [[Bug #19150]]
+
+* Dir
+
+ * Dir.for_fd added for returning a Dir object for the directory specified
+ by the provided directory file descriptor. [[Feature #19347]]
+ * Dir.fchdir added for changing the directory to the directory specified
+ by the provided directory file descriptor. [[Feature #19347]]
+ * Dir#chdir added for changing the directory to the directory specified by
+ the provided `Dir` object. [[Feature #19347]]
+
+* Encoding
+
+ * `Encoding#replicate` has been removed, it was already deprecated. [[Feature #18949]]
+
+* Fiber
+
+ * Introduce Fiber#kill. [[Bug #595]]
+
+ ```ruby
+ fiber = Fiber.new do
+ while true
+ puts "Yielding..."
+ Fiber.yield
+ end
+ ensure
+ puts "Exiting..."
+ end
+
+ fiber.resume
+ # Yielding...
+ fiber.kill
+ # Exiting...
+ ```
+
+* MatchData
+
+ * MatchData#named_captures now accepts optional `symbolize_names`
+ keyword. [[Feature #19591]]
+
+* Module
+
+ * Module#set_temporary_name added for setting a temporary name for a
+ module. [[Feature #19521]]
+
+* ObjectSpace::WeakKeyMap
+
+ * New core class to build collections with weak references.
+ The class use equality semantic to lookup keys like a regular hash,
+ but it doesn't hold strong references on the keys. [[Feature #18498]]
+
+* ObjectSpace::WeakMap
+
+ * ObjectSpace::WeakMap#delete was added to eagerly clear weak map
+ entries. [[Feature #19561]]
+
+* Proc
+ * Now Proc#dup and Proc#clone call `#initialize_dup` and `#initialize_clone`
+ hooks respectively. [[Feature #19362]]
+
+* Process
+
+ * New Process.warmup method that notify the Ruby virtual machine that the boot sequence is finished,
+ and that now is a good time to optimize the application. This is useful
+ for long-running applications. The actual optimizations performed are entirely
+ implementation-specific and may change in the future without notice. [[Feature #18885]]
+
+* Process::Status
+
+ * Process::Status#& and Process::Status#>> are deprecated. [[Bug #19868]]
+
+* Range
+
+ * Range#reverse_each can now process beginless ranges with an Integer endpoint. [[Feature #18515]]
+ * Range#reverse_each now raises TypeError for endless ranges. [[Feature #18551]]
+ * Range#overlap? added for checking if two ranges overlap. [[Feature #19839]]
+
+* Refinement
+
+ * Add Refinement#target as an alternative of Refinement#refined_class.
+ Refinement#refined_class is deprecated and will be removed in Ruby
+ 3.4. [[Feature #19714]]
+
+* Regexp
+
+ * The cache-based optimization now supports lookarounds and atomic groupings. That is, match
+ for Regexp containing these extensions can now also be performed in linear time to the length
+ of the input string. However, these cannot contain captures and cannot be nested. [[Feature #19725]]
+
+* String
+
+ * String#unpack now raises ArgumentError for unknown directives. [[Bug #19150]]
+ * String#bytesplice now accepts new arguments index/length or range of the
+ source string to be copied. [[Feature #19314]]
+
+* Thread::Queue
+
+ * Thread::Queue#freeze now raises TypeError. [[Bug #17146]]
+
+* Thread::SizedQueue
+
+ * Thread::SizedQueue#freeze now raises TypeError. [[Bug #17146]]
+
+* Time
+
+ * Time.new with a string argument became stricter. [[Bug #19293]]
+
+ ```ruby
+ Time.new('2023-12-20')
+ # no time information (ArgumentError)
+ ```
+
+* TracePoint
+
+ * TracePoint supports `rescue` event. When the raised exception was rescued,
+ the TracePoint will fire the hook. `rescue` event only supports Ruby-level
+ `rescue`. [[Feature #19572]]
+
+## Stdlib updates
+
+* RubyGems and Bundler warn if users do `require` the following gems without adding them to Gemfile or gemspec.
+ This is because they will become the bundled gems in the future version of Ruby. This warning is suppressed
+ if you use bootsnap gem. We recommend to run your application with `DISABLE_BOOTSNAP=1` environmental variable
+ at least once. This is limitation of this version.
+ [[Feature #19351]] [[Feature #19776]] [[Feature #19843]]
+ * abbrev
+ * base64
+ * bigdecimal
+ * csv
+ * drb
+ * getoptlong
+ * mutex_m
+ * nkf
+ * observer
+ * racc
+ * resolv-replace
+ * rinda
+ * syslog
+
+* Socket#recv and Socket#recv_nonblock returns `nil` instead of an empty string on closed
+ connections. Socket#recvmsg and Socket#recvmsg_nonblock returns `nil` instead of an empty packet on closed
+ connections. [[Bug #19012]]
+
+* Name resolution such as Socket.getaddrinfo, Socket.getnameinfo, Addrinfo.getaddrinfo, etc.
+ can now be interrupted. [[Feature #19965]]
+
+* Random::Formatter#alphanumeric is extended to accept optional `chars`
+ keyword argument. [[Feature #18183]]
+
+The following default gem is added.
+
+* prism 0.19.0
+
+The following default gems are updated.
+
+* RubyGems 3.5.3
+* abbrev 0.1.2
+* base64 0.2.0
+* benchmark 0.3.0
+* bigdecimal 3.1.5
+* bundler 2.5.3
+* cgi 0.4.1
+* csv 3.2.8
+* date 3.3.4
+* delegate 0.3.1
+* drb 2.2.0
+* english 0.8.0
+* erb 4.0.3
+* error_highlight 0.6.0
+* etc 1.4.3
+* fcntl 1.1.0
+* fiddle 1.1.2
+* fileutils 1.7.2
+* find 0.2.0
+* getoptlong 0.2.1
+* io-console 0.7.1
+* io-nonblock 0.3.0
+* io-wait 0.3.1
+* ipaddr 1.2.6
+* irb 1.11.0
+* json 2.7.1
+* logger 1.6.0
+* mutex_m 0.2.0
+* net-http 0.4.0
+* net-protocol 0.2.2
+* nkf 0.1.3
+* observer 0.1.2
+* open-uri 0.4.1
+* open3 0.2.1
+* openssl 3.2.0
+* optparse 0.4.0
+* ostruct 0.6.0
+* pathname 0.3.0
+* pp 0.5.0
+* prettyprint 0.2.0
+* pstore 0.1.3
+* psych 5.1.2
+* rdoc 6.6.2
+* readline 0.0.4
+* reline 0.4.1
+* resolv 0.3.0
+* rinda 0.2.0
+* securerandom 0.3.1
+* set 1.1.0
+* shellwords 0.2.0
+* singleton 0.2.0
+* stringio 3.1.0
+* strscan 3.0.7
+* syntax_suggest 2.0.0
+* syslog 0.1.2
+* tempfile 0.2.1
+* time 0.3.0
+* timeout 0.4.1
+* tmpdir 0.2.0
+* tsort 0.2.0
+* un 0.3.0
+* uri 0.13.0
+* weakref 0.1.3
+* win32ole 1.8.10
+* yaml 0.3.0
+* zlib 3.1.0
+
+The following bundled gem is promoted from default gems.
+
+* racc 1.7.3
+
+The following bundled gems are updated.
+
+* minitest 5.20.0
+* rake 13.1.0
+* test-unit 3.6.1
+* rexml 3.2.6
+* rss 0.3.0
+* net-ftp 0.3.3
+* net-imap 0.4.9
+* net-smtp 0.4.0
+* rbs 3.4.0
+* typeprof 0.21.9
+* debug 1.9.1
+
+See GitHub releases like [Logger](https://github.com/ruby/logger/releases) or
+changelog for details of the default gems or bundled gems.
+
+### Prism
+
+* Introduced [the Prism parser](https://github.com/ruby/prism) as a default gem
+ * Prism is a portable, error tolerant, and maintainable recursive descent parser for the Ruby language
+* Prism is production ready and actively maintained, you can use it in place of Ripper
+ * There is [extensive documentation](https://ruby.github.io/prism/) on how to use Prism
+ * Prism is both a C library that will be used internally by CRuby and a Ruby gem that can be used by any tooling which needs to parse Ruby code
+ * Notable methods in the Prism API are:
+ * `Prism.parse(source)` which returns the AST as part of a parse result object
+ * `Prism.parse_comments(source)` which returns the comments
+ * `Prism.parse_success?(source)` which returns true if there are no errors
+* You can make pull requests or issues directly on [the Prism repository](https://github.com/ruby/prism) if you are interested in contributing
+* You can now use `ruby --parser=prism` or `RUBYOPT="--parser=prism"` to experiment with the Prism compiler. Please note that this flag is for debugging only.
+
+## Compatibility issues
+
+* Subprocess creation/forking via the following file open methods is deprecated. [[Feature #19630]]
+ * Kernel#open
+ * URI.open
+ * IO.binread
+ * IO.foreach
+ * IO.readlines
+ * IO.read
+ * IO.write
+
+* When given a non-lambda, non-literal block, Kernel#lambda with now raises
+ ArgumentError instead of returning it unmodified. These usages have been
+ issuing warnings under the `Warning[:deprecated]` category since Ruby 3.0.0.
+ [[Feature #19777]]
+
+* The `RUBY_GC_HEAP_INIT_SLOTS` environment variable has been deprecated and
+ removed. Environment variables `RUBY_GC_HEAP_%d_INIT_SLOTS` should be
+ used instead. [[Feature #19785]]
+
+* `it` calls without arguments in a block with no ordinary parameters are
+ deprecated. `it` will be a reference to the first block parameter in Ruby 3.4.
+ [[Feature #18980]]
+
+* Error message for NoMethodError have changed to not use the target object's `#inspect`
+ for efficiency, and says "instance of ClassName" instead. [[Feature #18285]]
+
+ ```ruby
+ ([1] * 100).nonexisting
+ # undefined method `nonexisting' for an instance of Array (NoMethodError)
+ ```
+
+* Now anonymous parameters forwarding is disallowed inside a block
+ that uses anonymous parameters. [[Feature #19370]]
+
+## Stdlib compatibility issues
+
+* `racc` is promoted to bundled gems.
+ * You need to add `racc` to your `Gemfile` if you use `racc` under bundler environment.
+* `ext/readline` is retired
+ * We have `reline` that is pure Ruby implementation compatible with `ext/readline` API.
+ We rely on `reline` in the future. If you need to use `ext/readline`, you can install
+ `ext/readline` via rubygems.org with `gem install readline-ext`.
+ * We no longer need to install libraries like `libreadline` or `libedit`.
+
+## C API updates
+
+* `rb_postponed_job` updates
+ * New APIs and deprecated APIs (see comments for details)
+ * added: `rb_postponed_job_preregister()`
+ * added: `rb_postponed_job_trigger()`
+ * deprecated: `rb_postponed_job_register()` (and semantic change. see below)
+ * deprecated: `rb_postponed_job_register_one()`
+ * The postponed job APIs have been changed to address some rare crashes.
+ To solve the issue, we introduced new two APIs and deprecated current APIs.
+ The semantics of these functions have also changed slightly; `rb_postponed_job_register`
+ now behaves like the `once` variant in that multiple calls with the same
+ `func` might be coalesced into a single execution of the `func`
+ [[Feature #20057]]
+
+* Some updates for internal thread event hook APIs
+ * `rb_internal_thread_event_data_t` with a target Ruby thread (VALUE)
+ and callback functions (`rb_internal_thread_event_callback`) receive it.
+ https://github.com/ruby/ruby/pull/8885
+ * The following functions are introduced to manipulate Ruby thread local data
+ from internal thread event hook APIs (they are introduced since Ruby 3.2).
+ https://github.com/ruby/ruby/pull/8936
+ * `rb_internal_thread_specific_key_create()`
+ * `rb_internal_thread_specific_get()`
+ * `rb_internal_thread_specific_set()`
+
+* `rb_profile_thread_frames()` is introduced to get a frames from
+ a specific thread.
+ [[Feature #10602]]
+
+* `rb_data_define()` is introduced to define `Data`. [[Feature #19757]]
+
+* `rb_ext_resolve_symbol()` is introduced to search a function from
+ extension libraries. [[Feature #20005]]
+
+* IO related updates:
+ * The details of `rb_io_t` will be hidden and deprecated attributes
+ are added for each members. [[Feature #19057]]
+ * `rb_io_path(VALUE io)` is introduced to get a path of `io`.
+ * `rb_io_closed_p(VALUE io)` to get opening or closing of `io`.
+ * `rb_io_mode(VALUE io)` to get the mode of `io`.
+ * `rb_io_open_descriptor()` is introduced to make an IO object from a file
+ descriptor.
+
+## Implementation improvements
+
+### Parser
+
+* Replace Bison with [Lrama LALR parser generator](https://github.com/ruby/lrama).
+ No need to install Bison to build Ruby from source code anymore.
+ We will no longer suffer bison compatibility issues and we can use new features by just implementing it to Lrama. [[Feature #19637]]
+ * See [The future vision of Ruby Parser](https://rubykaigi.org/2023/presentations/spikeolaf.html) for detail.
+ * Lrama internal parser is a LR parser generated by Racc for maintainability.
+ * Parameterizing Rules `(?, *, +)` are supported, it will be used in Ruby parse.y.
+
+### GC / Memory management
+
+* Major performance improvements over Ruby 3.2
+ * Young objects referenced by old objects are no longer immediately
+ promoted to the old generation. This significantly reduces the frequency of
+ major GC collections. [[Feature #19678]]
+ * A new `REMEMBERED_WB_UNPROTECTED_OBJECTS_LIMIT_RATIO` tuning variable was
+ introduced to control the number of unprotected objects cause a major GC
+ collection to trigger. The default is set to `0.01` (1%). This significantly
+ reduces the frequency of major GC collection. [[Feature #19571]]
+ * Write Barriers were implemented for many core types that were missing them,
+ notably `Time`, `Enumerator`, `MatchData`, `Method`, `File::Stat`, `BigDecimal`
+ and several others. This significantly reduces minor GC collection time and major
+ GC collection frequency.
+ * Most core classes are now using Variable Width Allocation, notably `Hash`, `Time`,
+ `Thread::Backtrace`, `Thread::Backtrace::Location`, `File::Stat`, `Method`.
+ This makes these classes faster to allocate and free, use less memory and reduce
+ heap fragmentation.
+* `defined?(@ivar)` is optimized with Object Shapes.
+
+### YJIT
+
+* Major performance improvements over Ruby 3.2
+ * Support for splat and rest arguments has been improved.
+ * Registers are allocated for stack operations of the virtual machine.
+ * More calls with optional arguments are compiled. Exception handlers are also compiled.
+ * Unsupported call types and megamorphic call sites no longer exit to the interpreter.
+ * Basic methods like Rails `#blank?` and
+ [specialized `#present?`](https://github.com/rails/rails/pull/49909) are inlined.
+ * `Integer#*`, `Integer#!=`, `String#!=`, `String#getbyte`,
+ `Kernel#block_given?`, `Kernel#is_a?`, `Kernel#instance_of?`, and `Module#===`
+ are specially optimized.
+ * Compilation speed is now slightly faster than Ruby 3.2.
+ * Now more than 3x faster than the interpreter on Optcarrot!
+* Significantly improved memory usage over Ruby 3.2
+ * Metadata for compiled code uses a lot less memory.
+ * `--yjit-call-threshold` is automatically raised from 30 to 120
+ when the application has more than 40,000 ISEQs.
+ * `--yjit-cold-threshold` is added to skip compiling cold ISEQs.
+ * More compact code is generated on Arm64.
+* Code GC is now disabled by default
+ * `--yjit-exec-mem-size` is treated as a hard limit where compilation of new code stops.
+ * No sudden drops in performance due to code GC.
+ Better copy-on-write behavior on servers reforking with
+ [Pitchfork](https://github.com/shopify/pitchfork).
+ * You can still enable code GC if desired with `--yjit-code-gc`
+* Add `RubyVM::YJIT.enable` that can enable YJIT at run-time
+ * You can start YJIT without modifying command-line arguments or environment variables.
+ Rails 7.2 will [enable YJIT by default](https://github.com/rails/rails/pull/49947)
+ using this method.
+ * This can also be used to enable YJIT only once your application is
+ done booting. `--yjit-disable` can be used if you want to use other
+ YJIT options while disabling YJIT at boot.
+* More YJIT stats are available by default
+ * `yjit_alloc_size` and several more metadata-related stats are now available by default.
+ * `ratio_in_yjit` stat produced by `--yjit-stats` is now available in release builds,
+ a special stats or dev build is no longer required to access most stats.
+* Add more profiling capabilities
+ * `--yjit-perf` is added to facilitate profiling with Linux perf.
+ * `--yjit-trace-exits` now supports sampling with `--yjit-trace-exits-sample-rate=N`
+* More thorough testing and multiple bug fixes
+* `--yjit-stats=quiet` is added to avoid printing stats on exit.
+
+### MJIT
+
+* MJIT is removed.
+ * `--disable-jit-support` is removed. Consider using `--disable-yjit --disable-rjit` instead.
+
+### RJIT
+
+* Introduced a pure-Ruby JIT compiler RJIT.
+ * RJIT supports only x86\_64 architecture on Unix platforms.
+ * Unlike MJIT, it doesn't require a C compiler at runtime.
+* RJIT exists only for experimental purposes.
+ * You should keep using YJIT in production.
+
+### M:N Thread scheduler
+
+* M:N Thread scheduler is introduced. [[Feature #19842]]
+ * Background: Ruby 1.8 and before, M:1 thread scheduler (M Ruby threads
+ with 1 native thread. Called as User level threads or Green threads)
+ is used. Ruby 1.9 and later, 1:1 thread scheduler (1 Ruby thread with
+ 1 native thread). M:1 threads takes lower resources compare with 1:1
+ threads because it needs only 1 native threads. However it is difficult
+ to support context switching for all of blocking operation so 1:1
+ threads are employed from Ruby 1.9. M:N thread scheduler uses N native
+ threads for M Ruby threads (N is small number in general). It doesn't
+ need same number of native threads as Ruby threads (similar to the M:1
+ thread scheduler). Also our M:N threads supports blocking operations
+ well same as 1:1 threads. See the ticket for more details.
+ Our M:N thread scheduler refers on the goroutine scheduler in the
+ Go language.
+ * In a ractor, only 1 thread can run in a same time because of
+ implementation. Therefore, applications that use only one Ractor
+ (most applications) M:N thread scheduler works as M:1 thread scheduler
+ with further extension from Ruby 1.8.
+ * M:N thread scheduler can introduce incompatibility for C-extensions,
+ so it is disabled by default on the main Ractors.
+ `RUBY_MN_THREADS=1` environment variable will enable it.
+ On non-main Ractors, M:N thread scheduler is enabled (and can not
+ disable it now).
+ * `N` (the number of native threads) can be specified with `RUBY_MAX_CPU`
+ environment variable. The default is 8.
+ Note that more than `N` native threads are used to support many kind of
+ blocking operations.
+
+[Bug #595]: https://bugs.ruby-lang.org/issues/595
+[Feature #10602]: https://bugs.ruby-lang.org/issues/10602
+[Bug #17146]: https://bugs.ruby-lang.org/issues/17146
+[Feature #18183]: https://bugs.ruby-lang.org/issues/18183
+[Feature #18285]: https://bugs.ruby-lang.org/issues/18285
+[Feature #18498]: https://bugs.ruby-lang.org/issues/18498
+[Feature #18515]: https://bugs.ruby-lang.org/issues/18515
+[Feature #18551]: https://bugs.ruby-lang.org/issues/18551
+[Feature #18885]: https://bugs.ruby-lang.org/issues/18885
+[Feature #18949]: https://bugs.ruby-lang.org/issues/18949
+[Feature #18980]: https://bugs.ruby-lang.org/issues/18980
+[Bug #19012]: https://bugs.ruby-lang.org/issues/19012
+[Feature #19057]: https://bugs.ruby-lang.org/issues/19057
+[Bug #19150]: https://bugs.ruby-lang.org/issues/19150
+[Bug #19293]: https://bugs.ruby-lang.org/issues/19293
+[Feature #19314]: https://bugs.ruby-lang.org/issues/19314
+[Feature #19347]: https://bugs.ruby-lang.org/issues/19347
+[Feature #19351]: https://bugs.ruby-lang.org/issues/19351
+[Feature #19362]: https://bugs.ruby-lang.org/issues/19362
+[Feature #19370]: https://bugs.ruby-lang.org/issues/19370
+[Feature #19521]: https://bugs.ruby-lang.org/issues/19521
+[Feature #19538]: https://bugs.ruby-lang.org/issues/19538
+[Feature #19561]: https://bugs.ruby-lang.org/issues/19561
+[Feature #19571]: https://bugs.ruby-lang.org/issues/19571
+[Feature #19572]: https://bugs.ruby-lang.org/issues/19572
+[Feature #19591]: https://bugs.ruby-lang.org/issues/19591
+[Feature #19630]: https://bugs.ruby-lang.org/issues/19630
+[Feature #19637]: https://bugs.ruby-lang.org/issues/19637
+[Feature #19678]: https://bugs.ruby-lang.org/issues/19678
+[Feature #19714]: https://bugs.ruby-lang.org/issues/19714
+[Feature #19725]: https://bugs.ruby-lang.org/issues/19725
+[Feature #19757]: https://bugs.ruby-lang.org/issues/19757
+[Feature #19776]: https://bugs.ruby-lang.org/issues/19776
+[Feature #19777]: https://bugs.ruby-lang.org/issues/19777
+[Feature #19785]: https://bugs.ruby-lang.org/issues/19785
+[Feature #19790]: https://bugs.ruby-lang.org/issues/19790
+[Feature #19839]: https://bugs.ruby-lang.org/issues/19839
+[Feature #19842]: https://bugs.ruby-lang.org/issues/19842
+[Feature #19843]: https://bugs.ruby-lang.org/issues/19843
+[Bug #19868]: https://bugs.ruby-lang.org/issues/19868
+[Feature #19965]: https://bugs.ruby-lang.org/issues/19965
+[Feature #20005]: https://bugs.ruby-lang.org/issues/20005
+[Feature #20057]: https://bugs.ruby-lang.org/issues/20057
diff --git a/doc/_regexp.rdoc b/doc/_regexp.rdoc
new file mode 100644
index 0000000000..7b71eee984
--- /dev/null
+++ b/doc/_regexp.rdoc
@@ -0,0 +1,1276 @@
+A {regular expression}[https://en.wikipedia.org/wiki/Regular_expression]
+(also called a _regexp_) is a <i>match pattern</i> (also simply called a _pattern_).
+
+A common notation for a regexp uses enclosing slash characters:
+
+ /foo/
+
+A regexp may be applied to a <i>target string</i>;
+The part of the string (if any) that matches the pattern is called a _match_,
+and may be said <i>to match</i>:
+
+ re = /red/
+ re.match?('redirect') # => true # Match at beginning of target.
+ re.match?('bored') # => true # Match at end of target.
+ re.match?('credit') # => true # Match within target.
+ re.match?('foo') # => false # No match.
+
+== \Regexp Uses
+
+A regexp may be used:
+
+- To extract substrings based on a given pattern:
+
+ re = /foo/ # => /foo/
+ re.match('food') # => #<MatchData "foo">
+ re.match('good') # => nil
+
+ See sections {Method match}[rdoc-ref:Regexp@Method+match]
+ and {Operator =~}[rdoc-ref:Regexp@Operator+-3D~].
+
+- To determine whether a string matches a given pattern:
+
+ re.match?('food') # => true
+ re.match?('good') # => false
+
+ See section {Method match?}[rdoc-ref:Regexp@Method+match-3F].
+
+- As an argument for calls to certain methods in other classes and modules;
+ most such methods accept an argument that may be either a string
+ or the (much more powerful) regexp.
+
+ See {Regexp Methods}[rdoc-ref:regexp/methods.rdoc].
+
+== \Regexp Objects
+
+A regexp object has:
+
+- A source; see {Sources}[rdoc-ref:Regexp@Sources].
+
+- Several modes; see {Modes}[rdoc-ref:Regexp@Modes].
+
+- A timeout; see {Timeouts}[rdoc-ref:Regexp@Timeouts].
+
+- An encoding; see {Encodings}[rdoc-ref:Regexp@Encodings].
+
+== Creating a \Regexp
+
+A regular expression may be created with:
+
+- A regexp literal using slash characters
+ (see {Regexp Literals}[rdoc-ref:syntax/literals.rdoc@Regexp+Literals]):
+
+ # This is a very common usage.
+ /foo/ # => /foo/
+
+- A <tt>%r</tt> regexp literal
+ (see {%r: Regexp Literals}[rdoc-ref:syntax/literals.rdoc@25r-3A+Regexp+Literals]):
+
+ # Same delimiter character at beginning and end;
+ # useful for avoiding escaping characters
+ %r/name\/value pair/ # => /name\/value pair/
+ %r:name/value pair: # => /name\/value pair/
+ %r|name/value pair| # => /name\/value pair/
+
+ # Certain "paired" characters can be delimiters.
+ %r[foo] # => /foo/
+ %r{foo} # => /foo/
+ %r(foo) # => /foo/
+ %r<foo> # => /foo/
+
+- \Method Regexp.new.
+
+== \Method <tt>match</tt>
+
+Each of the methods Regexp#match, String#match, and Symbol#match
+returns a MatchData object if a match was found, +nil+ otherwise;
+each also sets {global variables}[rdoc-ref:Regexp@Global+Variables]:
+
+ 'food'.match(/foo/) # => #<MatchData "foo">
+ 'food'.match(/bar/) # => nil
+
+== Operator <tt>=~</tt>
+
+Each of the operators Regexp#=~, String#=~, and Symbol#=~
+returns an integer offset if a match was found, +nil+ otherwise;
+each also sets {global variables}[rdoc-ref:Regexp@Global+Variables]:
+
+ /bar/ =~ 'foo bar' # => 4
+ 'foo bar' =~ /bar/ # => 4
+ /baz/ =~ 'foo bar' # => nil
+
+== \Method <tt>match?</tt>
+
+Each of the methods Regexp#match?, String#match?, and Symbol#match?
+returns +true+ if a match was found, +false+ otherwise;
+none sets {global variables}[rdoc-ref:Regexp@Global+Variables]:
+
+ 'food'.match?(/foo/) # => true
+ 'food'.match?(/bar/) # => false
+
+== Global Variables
+
+Certain regexp-oriented methods assign values to global variables:
+
+- <tt>#match</tt>: see {Method match}[rdoc-ref:Regexp@Method+match].
+- <tt>#=~</tt>: see {Operator =~}[rdoc-ref:Regexp@Operator+-3D~].
+
+The affected global variables are:
+
+- <tt>$~</tt>: Returns a MatchData object, or +nil+.
+- <tt>$&</tt>: Returns the matched part of the string, or +nil+.
+- <tt>$`</tt>: Returns the part of the string to the left of the match, or +nil+.
+- <tt>$'</tt>: Returns the part of the string to the right of the match, or +nil+.
+- <tt>$+</tt>: Returns the last group matched, or +nil+.
+- <tt>$1</tt>, <tt>$2</tt>, etc.: Returns the first, second, etc.,
+ matched group, or +nil+.
+ Note that <tt>$0</tt> is quite different;
+ it returns the name of the currently executing program.
+
+Examples:
+
+ # Matched string, but no matched groups.
+ 'foo bar bar baz'.match('bar')
+ $~ # => #<MatchData "bar">
+ $& # => "bar"
+ $` # => "foo "
+ $' # => " bar baz"
+ $+ # => nil
+ $1 # => nil
+
+ # Matched groups.
+ /s(\w{2}).*(c)/.match('haystack')
+ $~ # => #<MatchData "stac" 1:"ta" 2:"c">
+ $& # => "stac"
+ $` # => "hay"
+ $' # => "k"
+ $+ # => "c"
+ $1 # => "ta"
+ $2 # => "c"
+ $3 # => nil
+
+ # No match.
+ 'foo'.match('bar')
+ $~ # => nil
+ $& # => nil
+ $` # => nil
+ $' # => nil
+ $+ # => nil
+ $1 # => nil
+
+Note that Regexp#match?, String#match?, and Symbol#match?
+do not set global variables.
+
+== Sources
+
+As seen above, the simplest regexp uses a literal expression as its source:
+
+ re = /foo/ # => /foo/
+ re.match('food') # => #<MatchData "foo">
+ re.match('good') # => nil
+
+A rich collection of available _subexpressions_
+gives the regexp great power and flexibility:
+
+- {Special characters}[rdoc-ref:Regexp@Special+Characters]
+- {Source literals}[rdoc-ref:Regexp@Source+Literals]
+- {Character classes}[rdoc-ref:Regexp@Character+Classes]
+- {Shorthand character classes}[rdoc-ref:Regexp@Shorthand+Character+Classes]
+- {Anchors}[rdoc-ref:Regexp@Anchors]
+- {Alternation}[rdoc-ref:Regexp@Alternation]
+- {Quantifiers}[rdoc-ref:Regexp@Quantifiers]
+- {Groups and captures}[rdoc-ref:Regexp@Groups+and+Captures]
+- {Unicode}[rdoc-ref:Regexp@Unicode]
+- {POSIX Bracket Expressions}[rdoc-ref:Regexp@POSIX+Bracket+Expressions]
+- {Comments}[rdoc-ref:Regexp@Comments]
+
+=== Special Characters
+
+\Regexp special characters, called _metacharacters_,
+have special meanings in certain contexts;
+depending on the context, these are sometimes metacharacters:
+
+ . ? - + * ^ \ | $ ( ) [ ] { }
+
+To match a metacharacter literally, backslash-escape it:
+
+ # Matches one or more 'o' characters.
+ /o+/.match('foo') # => #<MatchData "oo">
+ # Would match 'o+'.
+ /o\+/.match('foo') # => nil
+
+To match a backslash literally, backslash-escape it:
+
+ /\./.match('\.') # => #<MatchData ".">
+ /\\./.match('\.') # => #<MatchData "\\.">
+
+Method Regexp.escape returns an escaped string:
+
+ Regexp.escape('.?-+*^\|$()[]{}')
+ # => "\\.\\?\\-\\+\\*\\^\\\\\\|\\$\\(\\)\\[\\]\\{\\}"
+
+=== Source Literals
+
+The source literal largely behaves like a double-quoted string;
+see {String Literals}[rdoc-ref:syntax/literals.rdoc@String+Literals].
+
+In particular, a source literal may contain interpolated expressions:
+
+ s = 'foo' # => "foo"
+ /#{s}/ # => /foo/
+ /#{s.capitalize}/ # => /Foo/
+ /#{2 + 2}/ # => /4/
+
+There are differences between an ordinary string literal and a source literal;
+see {Shorthand Character Classes}[rdoc-ref:Regexp@Shorthand+Character+Classes].
+
+- <tt>\s</tt> in an ordinary string literal is equivalent to a space character;
+ in a source literal, it's shorthand for matching a whitespace character.
+- In an ordinary string literal, these are (needlessly) escaped characters;
+ in a source literal, they are shorthands for various matching characters:
+
+ \w \W \d \D \h \H \S \R
+
+=== Character Classes
+
+A <i>character class</i> is delimited by square brackets;
+it specifies that certain characters match at a given point in the target string:
+
+ # This character class will match any vowel.
+ re = /B[aeiou]rd/
+ re.match('Bird') # => #<MatchData "Bird">
+ re.match('Bard') # => #<MatchData "Bard">
+ re.match('Byrd') # => nil
+
+A character class may contain hyphen characters to specify ranges of characters:
+
+ # These regexps have the same effect.
+ /[abcdef]/.match('foo') # => #<MatchData "f">
+ /[a-f]/.match('foo') # => #<MatchData "f">
+ /[a-cd-f]/.match('foo') # => #<MatchData "f">
+
+When the first character of a character class is a caret (<tt>^</tt>),
+the sense of the class is inverted: it matches any character _except_ those specified.
+
+ /[^a-eg-z]/.match('f') # => #<MatchData "f">
+
+A character class may contain another character class.
+By itself this isn't useful because <tt>[a-z[0-9]]</tt>
+describes the same set as <tt>[a-z0-9]</tt>.
+
+However, character classes also support the <tt>&&</tt> operator,
+which performs set intersection on its arguments.
+The two can be combined as follows:
+
+ /[a-w&&[^c-g]z]/ # ([a-w] AND ([^c-g] OR z))
+
+This is equivalent to:
+
+ /[abh-w]/
+
+=== Shorthand Character Classes
+
+Each of the following metacharacters serves as a shorthand
+for a character class:
+
+- <tt>/./</tt>: Matches any character except a newline:
+
+ /./.match('foo') # => #<MatchData "f">
+ /./.match("\n") # => nil
+
+- <tt>/./m</tt>: Matches any character, including a newline;
+ see {Multiline Mode}[rdoc-ref:Regexp@Multiline+Mode]:
+
+ /./m.match("\n") # => #<MatchData "\n">
+
+- <tt>/\w/</tt>: Matches a word character: equivalent to <tt>[a-zA-Z0-9_]</tt>:
+
+ /\w/.match(' foo') # => #<MatchData "f">
+ /\w/.match(' _') # => #<MatchData "_">
+ /\w/.match(' ') # => nil
+
+- <tt>/\W/</tt>: Matches a non-word character: equivalent to <tt>[^a-zA-Z0-9_]</tt>:
+
+ /\W/.match(' ') # => #<MatchData " ">
+ /\W/.match('_') # => nil
+
+- <tt>/\d/</tt>: Matches a digit character: equivalent to <tt>[0-9]</tt>:
+
+ /\d/.match('THX1138') # => #<MatchData "1">
+ /\d/.match('foo') # => nil
+
+- <tt>/\D/</tt>: Matches a non-digit character: equivalent to <tt>[^0-9]</tt>:
+
+ /\D/.match('123Jump!') # => #<MatchData "J">
+ /\D/.match('123') # => nil
+
+- <tt>/\h/</tt>: Matches a hexdigit character: equivalent to <tt>[0-9a-fA-F]</tt>:
+
+ /\h/.match('xyz fedcba9876543210') # => #<MatchData "f">
+ /\h/.match('xyz') # => nil
+
+- <tt>/\H/</tt>: Matches a non-hexdigit character: equivalent to <tt>[^0-9a-fA-F]</tt>:
+
+ /\H/.match('fedcba9876543210xyz') # => #<MatchData "x">
+ /\H/.match('fedcba9876543210') # => nil
+
+- <tt>/\s/</tt>: Matches a whitespace character: equivalent to <tt>/[ \t\r\n\f\v]/</tt>:
+
+ /\s/.match('foo bar') # => #<MatchData " ">
+ /\s/.match('foo') # => nil
+
+- <tt>/\S/</tt>: Matches a non-whitespace character: equivalent to <tt>/[^ \t\r\n\f\v]/</tt>:
+
+ /\S/.match(" \t\r\n\f\v foo") # => #<MatchData "f">
+ /\S/.match(" \t\r\n\f\v") # => nil
+
+- <tt>/\R/</tt>: Matches a linebreak, platform-independently:
+
+ /\R/.match("\r") # => #<MatchData "\r"> # Carriage return (CR)
+ /\R/.match("\n") # => #<MatchData "\n"> # Newline (LF)
+ /\R/.match("\f") # => #<MatchData "\f"> # Formfeed (FF)
+ /\R/.match("\v") # => #<MatchData "\v"> # Vertical tab (VT)
+ /\R/.match("\r\n") # => #<MatchData "\r\n"> # CRLF
+ /\R/.match("\u0085") # => #<MatchData "\u0085"> # Next line (NEL)
+ /\R/.match("\u2028") # => #<MatchData "\u2028"> # Line separator (LSEP)
+ /\R/.match("\u2029") # => #<MatchData "\u2029"> # Paragraph separator (PSEP)
+
+=== Anchors
+
+An anchor is a metasequence that matches a zero-width position between
+characters in the target string.
+
+For a subexpression with no anchor,
+matching may begin anywhere in the target string:
+
+ /real/.match('surrealist') # => #<MatchData "real">
+
+For a subexpression with an anchor,
+matching must begin at the matched anchor.
+
+==== Boundary Anchors
+
+Each of these anchors matches a boundary:
+
+- <tt>^</tt>: Matches the beginning of a line:
+
+ /^bar/.match("foo\nbar") # => #<MatchData "bar">
+ /^ar/.match("foo\nbar") # => nil
+
+- <tt>$</tt>: Matches the end of a line:
+
+ /bar$/.match("foo\nbar") # => #<MatchData "bar">
+ /ba$/.match("foo\nbar") # => nil
+
+- <tt>\A</tt>: Matches the beginning of the string:
+
+ /\Afoo/.match('foo bar') # => #<MatchData "foo">
+ /\Afoo/.match(' foo bar') # => nil
+
+- <tt>\Z</tt>: Matches the end of the string;
+ if string ends with a single newline,
+ it matches just before the ending newline:
+
+ /foo\Z/.match('bar foo') # => #<MatchData "foo">
+ /foo\Z/.match('foo bar') # => nil
+ /foo\Z/.match("bar foo\n") # => #<MatchData "foo">
+ /foo\Z/.match("bar foo\n\n") # => nil
+
+- <tt>\z</tt>: Matches the end of the string:
+
+ /foo\z/.match('bar foo') # => #<MatchData "foo">
+ /foo\z/.match('foo bar') # => nil
+ /foo\z/.match("bar foo\n") # => nil
+
+- <tt>\b</tt>: Matches word boundary when not inside brackets;
+ matches backspace (<tt>"0x08"</tt>) when inside brackets:
+
+ /foo\b/.match('foo bar') # => #<MatchData "foo">
+ /foo\b/.match('foobar') # => nil
+
+- <tt>\B</tt>: Matches non-word boundary:
+
+ /foo\B/.match('foobar') # => #<MatchData "foo">
+ /foo\B/.match('foo bar') # => nil
+
+- <tt>\G</tt>: Matches first matching position:
+
+ In methods like String#gsub and String#scan, it changes on each iteration.
+ It initially matches the beginning of subject, and in each following iteration it matches where the last match finished.
+
+ " a b c".gsub(/ /, '_') # => "____a_b_c"
+ " a b c".gsub(/\G /, '_') # => "____a b c"
+
+ In methods like Regexp#match and String#match
+ that take an optional offset, it matches where the search begins.
+
+ "hello, world".match(/,/, 3) # => #<MatchData ",">
+ "hello, world".match(/\G,/, 3) # => nil
+
+==== Lookaround Anchors
+
+Lookahead anchors:
+
+- <tt>(?=_pat_)</tt>: Positive lookahead assertion:
+ ensures that the following characters match _pat_,
+ but doesn't include those characters in the matched substring.
+
+- <tt>(?!_pat_)</tt>: Negative lookahead assertion:
+ ensures that the following characters <i>do not</i> match _pat_,
+ but doesn't include those characters in the matched substring.
+
+Lookbehind anchors:
+
+- <tt>(?<=_pat_)</tt>: Positive lookbehind assertion:
+ ensures that the preceding characters match _pat_, but
+ doesn't include those characters in the matched substring.
+
+- <tt>(?<!_pat_)</tt>: Negative lookbehind assertion:
+ ensures that the preceding characters do not match
+ _pat_, but doesn't include those characters in the matched substring.
+
+The pattern below uses positive lookahead and positive lookbehind to match
+text appearing in <tt><b></tt>...<tt></b></tt> tags
+without including the tags in the match:
+
+ /(?<=<b>)\w+(?=<\/b>)/.match("Fortune favors the <b>bold</b>.")
+ # => #<MatchData "bold">
+
+==== Match-Reset Anchor
+
+- <tt>\K</tt>: Match reset:
+ the matched content preceding <tt>\K</tt> in the regexp is excluded from the result.
+ For example, the following two regexps are almost equivalent:
+
+ /ab\Kc/.match('abc') # => #<MatchData "c">
+ /(?<=ab)c/.match('abc') # => #<MatchData "c">
+
+ These match same string and <tt>$&</tt> equals <tt>'c'</tt>,
+ while the matched position is different.
+
+ As are the following two regexps:
+
+ /(a)\K(b)\Kc/
+ /(?<=(?<=(a))(b))c/
+
+=== Alternation
+
+The vertical bar metacharacter (<tt>|</tt>) may be used within parentheses
+to express alternation:
+two or more subexpressions any of which may match the target string.
+
+Two alternatives:
+
+ re = /(a|b)/
+ re.match('foo') # => nil
+ re.match('bar') # => #<MatchData "b" 1:"b">
+
+Four alternatives:
+
+ re = /(a|b|c|d)/
+ re.match('shazam') # => #<MatchData "a" 1:"a">
+ re.match('cold') # => #<MatchData "c" 1:"c">
+
+Each alternative is a subexpression, and may be composed of other subexpressions:
+
+ re = /([a-c]|[x-z])/
+ re.match('bar') # => #<MatchData "b" 1:"b">
+ re.match('ooz') # => #<MatchData "z" 1:"z">
+
+\Method Regexp.union provides a convenient way to construct
+a regexp with alternatives.
+
+=== Quantifiers
+
+A simple regexp matches one character:
+
+ /\w/.match('Hello') # => #<MatchData "H">
+
+An added _quantifier_ specifies how many matches are required or allowed:
+
+- <tt>*</tt> - Matches zero or more times:
+
+ /\w*/.match('')
+ # => #<MatchData "">
+ /\w*/.match('x')
+ # => #<MatchData "x">
+ /\w*/.match('xyz')
+ # => #<MatchData "yz">
+
+- <tt>+</tt> - Matches one or more times:
+
+ /\w+/.match('') # => nil
+ /\w+/.match('x') # => #<MatchData "x">
+ /\w+/.match('xyz') # => #<MatchData "xyz">
+
+- <tt>?</tt> - Matches zero or one times:
+
+ /\w?/.match('') # => #<MatchData "">
+ /\w?/.match('x') # => #<MatchData "x">
+ /\w?/.match('xyz') # => #<MatchData "x">
+
+- <tt>{</tt>_n_<tt>}</tt> - Matches exactly _n_ times:
+
+ /\w{2}/.match('') # => nil
+ /\w{2}/.match('x') # => nil
+ /\w{2}/.match('xyz') # => #<MatchData "xy">
+
+- <tt>{</tt>_min_<tt>,}</tt> - Matches _min_ or more times:
+
+ /\w{2,}/.match('') # => nil
+ /\w{2,}/.match('x') # => nil
+ /\w{2,}/.match('xy') # => #<MatchData "xy">
+ /\w{2,}/.match('xyz') # => #<MatchData "xyz">
+
+- <tt>{,</tt>_max_<tt>}</tt> - Matches _max_ or fewer times:
+
+ /\w{,2}/.match('') # => #<MatchData "">
+ /\w{,2}/.match('x') # => #<MatchData "x">
+ /\w{,2}/.match('xyz') # => #<MatchData "xy">
+
+- <tt>{</tt>_min_<tt>,</tt>_max_<tt>}</tt> -
+ Matches at least _min_ times and at most _max_ times:
+
+ /\w{1,2}/.match('') # => nil
+ /\w{1,2}/.match('x') # => #<MatchData "x">
+ /\w{1,2}/.match('xyz') # => #<MatchData "xy">
+
+==== Greedy, Lazy, or Possessive Matching
+
+Quantifier matching may be greedy, lazy, or possessive:
+
+- In _greedy_ matching, as many occurrences as possible are matched
+ while still allowing the overall match to succeed.
+ Greedy quantifiers: <tt>*</tt>, <tt>+</tt>, <tt>?</tt>,
+ <tt>{min, max}</tt> and its variants.
+- In _lazy_ matching, the minimum number of occurrences are matched.
+ Lazy quantifiers: <tt>*?</tt>, <tt>+?</tt>, <tt>??</tt>,
+ <tt>{min, max}?</tt> and its variants.
+- In _possessive_ matching, once a match is found, there is no backtracking;
+ that match is retained, even if it jeopardises the overall match.
+ Possessive quantifiers: <tt>*+</tt>, <tt>++</tt>, <tt>?+</tt>.
+ Note that <tt>{min, max}</tt> and its variants do _not_ support possessive matching.
+
+More:
+
+- About greedy and lazy matching, see
+ {Choosing Minimal or Maximal Repetition}[https://doc.lagout.org/programmation/Regular%20Expressions/Regular%20Expressions%20Cookbook_%20Detailed%20Solutions%20in%20Eight%20Programming%20Languages%20%282nd%20ed.%29%20%5BGoyvaerts%20%26%20Levithan%202012-09-06%5D.pdf#tutorial-backtrack].
+- About possessive matching, see
+ {Eliminate Needless Backtracking}[https://doc.lagout.org/programmation/Regular%20Expressions/Regular%20Expressions%20Cookbook_%20Detailed%20Solutions%20in%20Eight%20Programming%20Languages%20%282nd%20ed.%29%20%5BGoyvaerts%20%26%20Levithan%202012-09-06%5D.pdf#tutorial-backtrack].
+
+=== Groups and Captures
+
+A simple regexp has (at most) one match:
+
+ re = /\d\d\d\d-\d\d-\d\d/
+ re.match('1943-02-04') # => #<MatchData "1943-02-04">
+ re.match('1943-02-04').size # => 1
+ re.match('foo') # => nil
+
+Adding one or more pairs of parentheses, <tt>(_subexpression_)</tt>,
+defines _groups_, which may result in multiple matched substrings,
+called _captures_:
+
+ re = /(\d\d\d\d)-(\d\d)-(\d\d)/
+ re.match('1943-02-04') # => #<MatchData "1943-02-04" 1:"1943" 2:"02" 3:"04">
+ re.match('1943-02-04').size # => 4
+
+The first capture is the entire matched string;
+the other captures are the matched substrings from the groups.
+
+A group may have a {quantifier}[rdoc-ref:Regexp@Quantifiers]:
+
+ re = /July 4(th)?/
+ re.match('July 4') # => #<MatchData "July 4" 1:nil>
+ re.match('July 4th') # => #<MatchData "July 4th" 1:"th">
+
+ re = /(foo)*/
+ re.match('') # => #<MatchData "" 1:nil>
+ re.match('foo') # => #<MatchData "foo" 1:"foo">
+ re.match('foofoo') # => #<MatchData "foofoo" 1:"foo">
+
+ re = /(foo)+/
+ re.match('') # => nil
+ re.match('foo') # => #<MatchData "foo" 1:"foo">
+ re.match('foofoo') # => #<MatchData "foofoo" 1:"foo">
+
+The returned \MatchData object gives access to the matched substrings:
+
+ re = /(\d\d\d\d)-(\d\d)-(\d\d)/
+ md = re.match('1943-02-04')
+ # => #<MatchData "1943-02-04" 1:"1943" 2:"02" 3:"04">
+ md[0] # => "1943-02-04"
+ md[1] # => "1943"
+ md[2] # => "02"
+ md[3] # => "04"
+
+==== Non-Capturing Groups
+
+A group may be made non-capturing;
+it is still a group (and, for example, can have a quantifier),
+but its matching substring is not included among the captures.
+
+A non-capturing group begins with <tt>?:</tt> (inside the parentheses):
+
+ # Don't capture the year.
+ re = /(?:\d\d\d\d)-(\d\d)-(\d\d)/
+ md = re.match('1943-02-04') # => #<MatchData "1943-02-04" 1:"02" 2:"04">
+
+==== Backreferences
+
+A group match may also be referenced within the regexp itself;
+such a reference is called a +backreference+:
+
+ /[csh](..) [csh]\1 in/.match('The cat sat in the hat')
+ # => #<MatchData "cat sat in" 1:"at">
+
+This table shows how each subexpression in the regexp above
+matches a substring in the target string:
+
+ | Subexpression in Regexp | Matching Substring in Target String |
+ |---------------------------|-------------------------------------|
+ | First '[csh]' | Character 'c' |
+ | '(..)' | First substring 'at' |
+ | First space ' ' | First space character ' ' |
+ | Second '[csh]' | Character 's' |
+ | '\1' (backreference 'at') | Second substring 'at' |
+ | ' in' | Substring ' in' |
+
+A regexp may contain any number of groups:
+
+- For a large number of groups:
+
+ - The ordinary <tt>\\_n_</tt> notation applies only for _n_ in range (1..9).
+ - The <tt>MatchData[_n_]</tt> notation applies for any non-negative _n_.
+
+- <tt>\0</tt> is a special backreference, referring to the entire matched string;
+ it may not be used within the regexp itself,
+ but may be used outside it (for example, in a substitution method call):
+
+ 'The cat sat in the hat'.gsub(/[csh]at/, '\0s')
+ # => "The cats sats in the hats"
+
+==== Named Captures
+
+As seen above, a capture can be referred to by its number.
+A capture can also have a name,
+prefixed as <tt>?<_name_></tt> or <tt>?'_name_'</tt>,
+and the name (symbolized) may be used as an index in <tt>MatchData[]</tt>:
+
+ md = /\$(?<dollars>\d+)\.(?'cents'\d+)/.match("$3.67")
+ # => #<MatchData "$3.67" dollars:"3" cents:"67">
+ md[:dollars] # => "3"
+ md[:cents] # => "67"
+ # The capture numbers are still valid.
+ md[2] # => "67"
+
+When a regexp contains a named capture, there are no unnamed captures:
+
+ /\$(?<dollars>\d+)\.(\d+)/.match("$3.67")
+ # => #<MatchData "$3.67" dollars:"3">
+
+A named group may be backreferenced as <tt>\k<_name_></tt>:
+
+ /(?<vowel>[aeiou]).\k<vowel>.\k<vowel>/.match('ototomy')
+ # => #<MatchData "ototo" vowel:"o">
+
+When (and only when) a regexp contains named capture groups
+and appears before the <tt>=~</tt> operator,
+the captured substrings are assigned to local variables with corresponding names:
+
+ /\$(?<dollars>\d+)\.(?<cents>\d+)/ =~ '$3.67'
+ dollars # => "3"
+ cents # => "67"
+
+\Method Regexp#named_captures returns a hash of the capture names and substrings;
+method Regexp#names returns an array of the capture names.
+
+==== Atomic Grouping
+
+A group may be made _atomic_ with <tt>(?></tt>_subexpression_<tt>)</tt>.
+
+This causes the subexpression to be matched
+independently of the rest of the expression,
+so that the matched substring becomes fixed for the remainder of the match,
+unless the entire subexpression must be abandoned and subsequently revisited.
+
+In this way _subexpression_ is treated as a non-divisible whole.
+Atomic grouping is typically used to optimise patterns
+to prevent needless backtracking .
+
+Example (without atomic grouping):
+
+ /".*"/.match('"Quote"') # => #<MatchData "\"Quote\"">
+
+Analysis:
+
+1. The leading subexpression <tt>"</tt> in the pattern matches the first character
+ <tt>"</tt> in the target string.
+2. The next subexpression <tt>.*</tt> matches the next substring <tt>Quote“</tt>
+ (including the trailing double-quote).
+3. Now there is nothing left in the target string to match
+ the trailing subexpression <tt>"</tt> in the pattern;
+ this would cause the overall match to fail.
+4. The matched substring is backtracked by one position: <tt>Quote</tt>.
+5. The final subexpression <tt>"</tt> now matches the final substring <tt>"</tt>,
+ and the overall match succeeds.
+
+If subexpression <tt>.*</tt> is grouped atomically,
+the backtracking is disabled, and the overall match fails:
+
+ /"(?>.*)"/.match('"Quote"') # => nil
+
+Atomic grouping can affect performance;
+see {Atomic Group}[https://www.regular-expressions.info/atomic.html].
+
+==== Subexpression Calls
+
+As seen above, a backreference number (<tt>\\_n_</tt>) or name (<tt>\k<_name_></tt>)
+gives access to a captured _substring_;
+the corresponding regexp _subexpression_ may also be accessed,
+via the number (<tt>\\g<i>n</i></tt>) or name (<tt>\g<_name_></tt>):
+
+ /\A(?<paren>\(\g<paren>*\))*\z/.match('(())')
+ # ^1
+ # ^2
+ # ^3
+ # ^4
+ # ^5
+ # ^6
+ # ^7
+ # ^8
+ # ^9
+ # ^10
+
+The pattern:
+
+1. Matches at the beginning of the string, i.e. before the first character.
+2. Enters a named group +paren+.
+3. Matches the first character in the string, <tt>'('</tt>.
+4. Calls the +paren+ group again, i.e. recurses back to the second step.
+5. Re-enters the +paren+ group.
+6. Matches the second character in the string, <tt>'('</tt>.
+7. Attempts to call +paren+ a third time,
+ but fails because doing so would prevent an overall successful match.
+8. Matches the third character in the string, <tt>')'</tt>;
+ marks the end of the second recursive call
+9. Matches the fourth character in the string, <tt>')'</tt>.
+10. Matches the end of the string.
+
+See {Subexpression calls}[https://learnbyexample.github.io/Ruby_Regexp/groupings-and-backreferences.html?highlight=subexpression#subexpression-calls].
+
+==== Conditionals
+
+The conditional construct takes the form <tt>(?(_cond_)_yes_|_no_)</tt>, where:
+
+- _cond_ may be a capture number or name.
+- The match to be applied is _yes_ if _cond_ is captured;
+ otherwise the match to be applied is _no_.
+- If not needed, <tt>|_no_</tt> may be omitted.
+
+Examples:
+
+ re = /\A(foo)?(?(1)(T)|(F))\z/
+ re.match('fooT') # => #<MatchData "fooT" 1:"foo" 2:"T" 3:nil>
+ re.match('F') # => #<MatchData "F" 1:nil 2:nil 3:"F">
+ re.match('fooF') # => nil
+ re.match('T') # => nil
+
+ re = /\A(?<xyzzy>foo)?(?(<xyzzy>)(T)|(F))\z/
+ re.match('fooT') # => #<MatchData "fooT" xyzzy:"foo">
+ re.match('F') # => #<MatchData "F" xyzzy:nil>
+ re.match('fooF') # => nil
+ re.match('T') # => nil
+
+
+==== Absence Operator
+
+The absence operator is a special group that matches anything which does _not_ match the contained subexpressions.
+
+ /(?~real)/.match('surrealist') # => #<MatchData "surrea">
+ /(?~real)ist/.match('surrealist') # => #<MatchData "ealist">
+ /sur(?~real)ist/.match('surrealist') # => nil
+
+=== Unicode
+
+==== Unicode Properties
+
+The <tt>/\p{_property_name_}/</tt> construct (with lowercase +p+)
+matches characters using a Unicode property name,
+much like a character class;
+property +Alpha+ specifies alphabetic characters:
+
+ /\p{Alpha}/.match('a') # => #<MatchData "a">
+ /\p{Alpha}/.match('1') # => nil
+
+A property can be inverted
+by prefixing the name with a caret character (<tt>^</tt>):
+
+ /\p{^Alpha}/.match('1') # => #<MatchData "1">
+ /\p{^Alpha}/.match('a') # => nil
+
+Or by using <tt>\P</tt> (uppercase +P+):
+
+ /\P{Alpha}/.match('1') # => #<MatchData "1">
+ /\P{Alpha}/.match('a') # => nil
+
+See {Unicode Properties}[rdoc-ref:regexp/unicode_properties.rdoc]
+for regexps based on the numerous properties.
+
+Some commonly-used properties correspond to POSIX bracket expressions:
+
+- <tt>/\p{Alnum}/</tt>: Alphabetic and numeric character
+- <tt>/\p{Alpha}/</tt>: Alphabetic character
+- <tt>/\p{Blank}/</tt>: Space or tab
+- <tt>/\p{Cntrl}/</tt>: Control character
+- <tt>/\p{Digit}/</tt>: Digit
+ characters, and similar)
+- <tt>/\p{Lower}/</tt>: Lowercase alphabetical character
+- <tt>/\p{Print}/</tt>: Like <tt>\p{Graph}</tt>, but includes the space character
+- <tt>/\p{Punct}/</tt>: Punctuation character
+- <tt>/\p{Space}/</tt>: Whitespace character (<tt>[:blank:]</tt>, newline,
+ carriage return, etc.)
+- <tt>/\p{Upper}/</tt>: Uppercase alphabetical
+- <tt>/\p{XDigit}/</tt>: Digit allowed in a hexadecimal number (i.e., 0-9a-fA-F)
+
+These are also commonly used:
+
+- <tt>/\p{Emoji}/</tt>: Unicode emoji.
+- <tt>/\p{Graph}/</tt>: Non-blank character
+ (excludes spaces, control characters, and similar).
+- <tt>/\p{Word}/</tt>: A member in one of these Unicode character
+ categories (see below) or having one of these Unicode properties:
+
+ - Unicode categories:
+ - +Mark+ (+M+).
+ - <tt>Decimal Number</tt> (+Nd+)
+ - <tt>Connector Punctuation</tt> (+Pc+).
+
+ - Unicode properties:
+ - +Alpha+
+ - <tt>Join_Control</tt>
+
+- <tt>/\p{ASCII}/</tt>: A character in the ASCII character set.
+- <tt>/\p{Any}/</tt>: Any Unicode character (including unassigned characters).
+- <tt>/\p{Assigned}/</tt>: An assigned character.
+
+==== Unicode Character Categories
+
+A Unicode character category name:
+
+- May be either its full name or its abbreviated name.
+- Is case-insensitive.
+- Treats a space, a hyphen, and an underscore as equivalent.
+
+Examples:
+
+ /\p{lu}/ # => /\p{lu}/
+ /\p{LU}/ # => /\p{LU}/
+ /\p{Uppercase Letter}/ # => /\p{Uppercase Letter}/
+ /\p{Uppercase_Letter}/ # => /\p{Uppercase_Letter}/
+ /\p{UPPERCASE-LETTER}/ # => /\p{UPPERCASE-LETTER}/
+
+Below are the Unicode character category abbreviations and names.
+Enumerations of characters in each category are at the links.
+
+Letters:
+
+- +L+, +Letter+: +LC+, +Lm+, or +Lo+.
+- +LC+, +Cased_Letter+: +Ll+, +Lt+, or +Lu+.
+- {Lu, Lowercase_Letter}[https://www.compart.com/en/unicode/category/Ll].
+- {Lu, Modifier_Letter}[https://www.compart.com/en/unicode/category/Lm].
+- {Lu, Other_Letter}[https://www.compart.com/en/unicode/category/Lo].
+- {Lu, Titlecase_Letter}[https://www.compart.com/en/unicode/category/Lt].
+- {Lu, Uppercase_Letter}[https://www.compart.com/en/unicode/category/Lu].
+
+Marks:
+
+- +M+, +Mark+: +Mc+, +Me+, or +Mn+.
+- {Mc, Spacing_Mark}[https://www.compart.com/en/unicode/category/Mc].
+- {Me, Enclosing_Mark}[https://www.compart.com/en/unicode/category/Me].
+- {Mn, Nonapacing_Mark}[https://www.compart.com/en/unicode/category/Mn].
+
+Numbers:
+
+- +N+, +Number+: +Nd+, +Nl+, or +No+.
+- {Nd, Decimal_Number}[https://www.compart.com/en/unicode/category/Nd].
+- {Nl, Letter_Number}[https://www.compart.com/en/unicode/category/Nl].
+- {No, Other_Number}[https://www.compart.com/en/unicode/category/No].
+
+Punctation:
+
+- +P+, +Punctuation+: +Pc+, +Pd+, +Pe+, +Pf+, +Pi+, +Po+, or +Ps+.
+- {Pc, Connector_Punctuation}[https://www.compart.com/en/unicode/category/Pc].
+- {Pd, Dash_Punctuation}[https://www.compart.com/en/unicode/category/Pd].
+- {Pe, Close_Punctuation}[https://www.compart.com/en/unicode/category/Pe].
+- {Pf, Final_Punctuation}[https://www.compart.com/en/unicode/category/Pf].
+- {Pi, Initial_Punctuation}[https://www.compart.com/en/unicode/category/Pi].
+- {Po, Other_Punctuation}[https://www.compart.com/en/unicode/category/Po].
+- {Ps, Open_Punctuation}[https://www.compart.com/en/unicode/category/Ps].
+
+- +S+, +Symbol+: +Sc+, +Sk+, +Sm+, or +So+.
+- {Sc, Currency_Symbol}[https://www.compart.com/en/unicode/category/Sc].
+- {Sk, Modifier_Symbol}[https://www.compart.com/en/unicode/category/Sk].
+- {Sm, Math_Symbol}[https://www.compart.com/en/unicode/category/Sm].
+- {So, Other_Symbol}[https://www.compart.com/en/unicode/category/So].
+
+- +Z+, +Separator+: +Zl+, +Zp+, or +Zs+.
+- {Zl, Line_Separator}[https://www.compart.com/en/unicode/category/Zl].
+- {Zp, Paragraph_Separator}[https://www.compart.com/en/unicode/category/Zp].
+- {Zs, Space_Separator}[https://www.compart.com/en/unicode/category/Zs].
+
+- +C+, +Other+: +Cc+, +Cf+, +Cn+, +Co+, or +Cs+.
+- {Cc, Control}[https://www.compart.com/en/unicode/category/Cc].
+- {Cf, Format}[https://www.compart.com/en/unicode/category/Cf].
+- {Cn, Unassigned}[https://www.compart.com/en/unicode/category/Cn].
+- {Co, Private_Use}[https://www.compart.com/en/unicode/category/Co].
+- {Cs, Surrogate}[https://www.compart.com/en/unicode/category/Cs].
+
+==== Unicode Scripts and Blocks
+
+Among the Unicode properties are:
+
+- {Unicode scripts}[https://en.wikipedia.org/wiki/Script_(Unicode)];
+ see {supported scripts}[https://www.unicode.org/standard/supported.html].
+- {Unicode blocks}[https://en.wikipedia.org/wiki/Unicode_block];
+ see {supported blocks}[http://www.unicode.org/Public/UNIDATA/Blocks.txt].
+
+=== POSIX Bracket Expressions
+
+A POSIX <i>bracket expression</i> is also similar to a character class.
+These expressions provide a portable alternative to the above,
+with the added benefit of encompassing non-ASCII characters:
+
+- <tt>/\d/</tt> matches only ASCII decimal digits +0+ through +9+.
+- <tt>/[[:digit:]]/</tt> matches any character in the Unicode
+ <tt>Decimal Number</tt> (+Nd+) category;
+ see below.
+
+The POSIX bracket expressions:
+
+- <tt>/[[:digit:]]/</tt>: Matches a {Unicode digit}[https://www.compart.com/en/unicode/category/Nd]:
+
+ /[[:digit:]]/.match('9') # => #<MatchData "9">
+ /[[:digit:]]/.match("\u1fbf9") # => #<MatchData "9">
+
+- <tt>/[[:xdigit:]]/</tt>: Matches a digit allowed in a hexadecimal number;
+ equivalent to <tt>[0-9a-fA-F]</tt>.
+
+- <tt>/[[:upper:]]/</tt>: Matches a {Unicode uppercase letter}[https://www.compart.com/en/unicode/category/Lu]:
+
+ /[[:upper:]]/.match('A') # => #<MatchData "A">
+ /[[:upper:]]/.match("\u00c6") # => #<MatchData "Æ">
+
+- <tt>/[[:lower:]]/</tt>: Matches a {Unicode lowercase letter}[https://www.compart.com/en/unicode/category/Ll]:
+
+ /[[:lower:]]/.match('a') # => #<MatchData "a">
+ /[[:lower:]]/.match("\u01fd") # => #<MatchData "ǽ">
+
+- <tt>/[[:alpha:]]/</tt>: Matches <tt>/[[:upper:]]/</tt> or <tt>/[[:lower:]]/</tt>.
+
+- <tt>/[[:alnum:]]/</tt>: Matches <tt>/[[:alpha:]]/</tt> or <tt>/[[:digit:]]/</tt>.
+
+- <tt>/[[:space:]]/</tt>: Matches {Unicode space character}[https://www.compart.com/en/unicode/category/Zs]:
+
+ /[[:space:]]/.match(' ') # => #<MatchData " ">
+ /[[:space:]]/.match("\u2005") # => #<MatchData " ">
+
+- <tt>/[[:blank:]]/</tt>: Matches <tt>/[[:space:]]/</tt> or tab character:
+
+ /[[:blank:]]/.match(' ') # => #<MatchData " ">
+ /[[:blank:]]/.match("\u2005") # => #<MatchData " ">
+ /[[:blank:]]/.match("\t") # => #<MatchData "\t">
+
+- <tt>/[[:cntrl:]]/</tt>: Matches {Unicode control character}[https://www.compart.com/en/unicode/category/Cc]:
+
+ /[[:cntrl:]]/.match("\u0000") # => #<MatchData "\u0000">
+ /[[:cntrl:]]/.match("\u009f") # => #<MatchData "\u009F">
+
+- <tt>/[[:graph:]]/</tt>: Matches any character
+ except <tt>/[[:space:]]/</tt> or <tt>/[[:cntrl:]]/</tt>.
+
+- <tt>/[[:print:]]/</tt>: Matches <tt>/[[:graph:]]/</tt> or space character.
+
+- <tt>/[[:punct:]]/</tt>: Matches any (Unicode punctuation character}[https://www.compart.com/en/unicode/category/Po]:
+
+Ruby also supports these (non-POSIX) bracket expressions:
+
+- <tt>/[[:ascii:]]/</tt>: Matches a character in the ASCII character set.
+- <tt>/[[:word:]]/</tt>: Matches a character in one of these Unicode character
+ categories or having one of these Unicode properties:
+
+ - Unicode categories:
+ - +Mark+ (+M+).
+ - <tt>Decimal Number</tt> (+Nd+)
+ - <tt>Connector Punctuation</tt> (+Pc+).
+
+ - Unicode properties:
+ - +Alpha+
+ - <tt>Join_Control</tt>
+
+=== Comments
+
+A comment may be included in a regexp pattern
+using the <tt>(?#</tt>_comment_<tt>)</tt> construct,
+where _comment_ is a substring that is to be ignored.
+arbitrary text ignored by the regexp engine:
+
+ /foo(?#Ignore me)bar/.match('foobar') # => #<MatchData "foobar">
+
+The comment may not include an unescaped terminator character.
+
+See also {Extended Mode}[rdoc-ref:Regexp@Extended+Mode].
+
+== Modes
+
+Each of these modifiers sets a mode for the regexp:
+
+- +i+: <tt>/_pattern_/i</tt> sets
+ {Case-Insensitive Mode}[rdoc-ref:Regexp@Case-Insensitive+Mode].
+- +m+: <tt>/_pattern_/m</tt> sets
+ {Multiline Mode}[rdoc-ref:Regexp@Multiline+Mode].
+- +x+: <tt>/_pattern_/x</tt> sets
+ {Extended Mode}[rdoc-ref:Regexp@Extended+Mode].
+- +o+: <tt>/_pattern_/o</tt> sets
+ {Interpolation Mode}[rdoc-ref:Regexp@Interpolation+Mode].
+
+Any, all, or none of these may be applied.
+
+Modifiers +i+, +m+, and +x+ may be applied to subexpressions:
+
+- <tt>(?_modifier_)</tt> turns the mode "on" for ensuing subexpressions
+- <tt>(?-_modifier_)</tt> turns the mode "off" for ensuing subexpressions
+- <tt>(?_modifier_:_subexp_)</tt> turns the mode "on" for _subexp_ within the group
+- <tt>(?-_modifier_:_subexp_)</tt> turns the mode "off" for _subexp_ within the group
+
+Example:
+
+ re = /(?i)te(?-i)st/
+ re.match('test') # => #<MatchData "test">
+ re.match('TEst') # => #<MatchData "TEst">
+ re.match('TEST') # => nil
+ re.match('teST') # => nil
+
+ re = /t(?i:e)st/
+ re.match('test') # => #<MatchData "test">
+ re.match('tEst') # => #<MatchData "tEst">
+ re.match('tEST') # => nil
+
+\Method Regexp#options returns an integer whose value showing
+the settings for case-insensitivity mode, multiline mode, and extended mode.
+
+=== Case-Insensitive Mode
+
+By default, a regexp is case-sensitive:
+
+ /foo/.match('FOO') # => nil
+
+Modifier +i+ enables case-insensitive mode:
+
+ /foo/i.match('FOO')
+ # => #<MatchData "FOO">
+
+\Method Regexp#casefold? returns whether the mode is case-insensitive.
+
+=== Multiline Mode
+
+The multiline-mode in Ruby is what is commonly called a "dot-all mode":
+
+- Without the +m+ modifier, the subexpression <tt>.</tt> does not match newlines:
+
+ /a.c/.match("a\nc") # => nil
+
+- With the modifier, it does match:
+
+ /a.c/m.match("a\nc") # => #<MatchData "a\nc">
+
+Unlike other languages, the modifier +m+ does not affect the anchors <tt>^</tt> and <tt>$</tt>.
+These anchors always match at line-boundaries in Ruby.
+
+=== Extended Mode
+
+Modifier +x+ enables extended mode, which means that:
+
+- Literal white space in the pattern is to be ignored.
+- Character <tt>#</tt> marks the remainder of its containing line as a comment,
+ which is also to be ignored for matching purposes.
+
+In extended mode, whitespace and comments may be used
+to form a self-documented regexp.
+
+Regexp not in extended mode (matches some Roman numerals):
+
+ pattern = '^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$'
+ re = /#{pattern}/
+ re.match('MCMXLIII') # => #<MatchData "MCMXLIII" 1:"CM" 2:"XL" 3:"III">
+
+Regexp in extended mode:
+
+ pattern = <<-EOT
+ ^ # beginning of string
+ M{0,3} # thousands - 0 to 3 Ms
+ (CM|CD|D?C{0,3}) # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 Cs),
+ # or 500-800 (D, followed by 0 to 3 Cs)
+ (XC|XL|L?X{0,3}) # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 Xs),
+ # or 50-80 (L, followed by 0 to 3 Xs)
+ (IX|IV|V?I{0,3}) # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 Is),
+ # or 5-8 (V, followed by 0 to 3 Is)
+ $ # end of string
+ EOT
+ re = /#{pattern}/x
+ re.match('MCMXLIII') # => #<MatchData "MCMXLIII" 1:"CM" 2:"XL" 3:"III">
+
+=== Interpolation Mode
+
+Modifier +o+ means that the first time a literal regexp with interpolations
+is encountered,
+the generated Regexp object is saved and used for all future evaluations
+of that literal regexp.
+Without modifier +o+, the generated Regexp is not saved,
+so each evaluation of the literal regexp generates a new Regexp object.
+
+Without modifier +o+:
+
+ def letters; sleep 5; /[A-Z][a-z]/; end
+ words = %w[abc def xyz]
+ start = Time.now
+ words.each {|word| word.match(/\A[#{letters}]+\z/) }
+ Time.now - start # => 15.0174892
+
+With modifier +o+:
+
+ start = Time.now
+ words.each {|word| word.match(/\A[#{letters}]+\z/o) }
+ Time.now - start # => 5.0010866
+
+Note that if the literal regexp does not have interpolations,
+the +o+ behavior is the default.
+
+== Encodings
+
+By default, a regexp with only US-ASCII characters has US-ASCII encoding:
+
+ re = /foo/
+ re.source.encoding # => #<Encoding:US-ASCII>
+ re.encoding # => #<Encoding:US-ASCII>
+
+A regular expression containing non-US-ASCII characters
+is assumed to use the source encoding.
+This can be overridden with one of the following modifiers.
+
+- <tt>/_pat_/n</tt>: US-ASCII if only containing US-ASCII characters,
+ otherwise ASCII-8BIT:
+
+ /foo/n.encoding # => #<Encoding:US-ASCII>
+ /foo\xff/n.encoding # => #<Encoding:ASCII-8BIT>
+ /foo\x7f/n.encoding # => #<Encoding:US-ASCII>
+
+- <tt>/_pat_/u</tt>: UTF-8
+
+ /foo/u.encoding # => #<Encoding:UTF-8>
+
+- <tt>/_pat_/e</tt>: EUC-JP
+
+ /foo/e.encoding # => #<Encoding:EUC-JP>
+
+- <tt>/_pat_/s</tt>: Windows-31J
+
+ /foo/s.encoding # => #<Encoding:Windows-31J>
+
+A regexp can be matched against a target string when either:
+
+- They have the same encoding.
+- The regexp's encoding is a fixed encoding and the string
+ contains only ASCII characters.
+ Method Regexp#fixed_encoding? returns whether the regexp
+ has a <i>fixed</i> encoding.
+
+If a match between incompatible encodings is attempted an
+<tt>Encoding::CompatibilityError</tt> exception is raised.
+
+Example:
+
+ re = eval("# encoding: ISO-8859-1\n/foo\\xff?/")
+ re.encoding # => #<Encoding:ISO-8859-1>
+ re =~ "foo".encode("UTF-8") # => 0
+ re =~ "foo\u0100" # Raises Encoding::CompatibilityError
+
+The encoding may be explicitly fixed by including Regexp::FIXEDENCODING
+in the second argument for Regexp.new:
+
+ # Regexp with encoding ISO-8859-1.
+ re = Regexp.new("a".force_encoding('iso-8859-1'), Regexp::FIXEDENCODING)
+ re.encoding # => #<Encoding:ISO-8859-1>
+ # Target string with encoding UTF-8.
+ s = "a\u3042"
+ s.encoding # => #<Encoding:UTF-8>
+ re.match(s) # Raises Encoding::CompatibilityError.
+
+== Timeouts
+
+When either a regexp source or a target string comes from untrusted input,
+malicious values could become a denial-of-service attack;
+to prevent such an attack, it is wise to set a timeout.
+
+\Regexp has two timeout values:
+
+- A class default timeout, used for a regexp whose instance timeout is +nil+;
+ this default is initially +nil+, and may be set by method Regexp.timeout=:
+
+ Regexp.timeout # => nil
+ Regexp.timeout = 3.0
+ Regexp.timeout # => 3.0
+
+- An instance timeout, which defaults to +nil+ and may be set in Regexp.new:
+
+ re = Regexp.new('foo', timeout: 5.0)
+ re.timeout # => 5.0
+
+When regexp.timeout is +nil+, the timeout "falls through" to Regexp.timeout;
+when regexp.timeout is non-+nil+, that value controls timing out:
+
+ | regexp.timeout Value | Regexp.timeout Value | Result |
+ |----------------------|----------------------|-----------------------------|
+ | nil | nil | Never times out. |
+ | nil | Float | Times out in Float seconds. |
+ | Float | Any | Times out in Float seconds. |
+
+== Optimization
+
+For certain values of the pattern and target string,
+matching time can grow polynomially or exponentially in relation to the input size;
+the potential vulnerability arising from this is the {regular expression denial-of-service}[https://en.wikipedia.org/wiki/ReDoS] (ReDoS) attack.
+
+\Regexp matching can apply an optimization to prevent ReDoS attacks.
+When the optimization is applied, matching time increases linearly (not polynomially or exponentially)
+in relation to the input size, and a ReDoS attach is not possible.
+
+This optimization is applied if the pattern meets these criteria:
+
+- No backreferences.
+- No subexpression calls.
+- No nested lookaround anchors or atomic groups.
+- No nested quantifiers with counting (i.e. no nested <tt>{n}</tt>,
+ <tt>{min,}</tt>, <tt>{,max}</tt>, or <tt>{min,max}</tt> style quantifiers)
+
+You can use method Regexp.linear_time? to determine whether a pattern meets these criteria:
+
+ Regexp.linear_time?(/a*/) # => true
+ Regexp.linear_time?('a*') # => true
+ Regexp.linear_time?(/(a*)\1/) # => false
+
+However, an untrusted source may not be safe even if the method returns +true+,
+because the optimization uses memoization (which may invoke large memory consumption).
+
+== References
+
+Read (online PDF books):
+
+- {Mastering Regular Expressions}[https://ia902508.us.archive.org/10/items/allitebooks-02/Mastering%20Regular%20Expressions%2C%203rd%20Edition.pdf]
+ by Jeffrey E.F. Friedl.
+- {Regular Expressions Cookbook}[https://doc.lagout.org/programmation/Regular%20Expressions/Regular%20Expressions%20Cookbook_%20Detailed%20Solutions%20in%20Eight%20Programming%20Languages%20%282nd%20ed.%29%20%5BGoyvaerts%20%26%20Levithan%202012-09-06%5D.pdf]
+ by Jan Goyvaerts & Steven Levithan.
+
+Explore, test (interactive online editor):
+
+- {Rubular}[https://rubular.com/].
diff --git a/doc/_timezones.rdoc b/doc/_timezones.rdoc
new file mode 100644
index 0000000000..c5230ea67d
--- /dev/null
+++ b/doc/_timezones.rdoc
@@ -0,0 +1,156 @@
+== Timezone Specifiers
+
+Certain +Time+ methods accept arguments that specify timezones:
+
+- Time.at: keyword argument +in:+.
+- Time.new: positional argument +zone+ or keyword argument +in:+.
+- Time.now: keyword argument +in:+.
+- Time#getlocal: positional argument +zone+.
+- Time#localtime: positional argument +zone+.
+
+The value given with any of these must be one of the following
+(each detailed below):
+
+- {Hours/minutes offset}[rdoc-ref:Time@Hours-2FMinutes+Offsets].
+- {Single-letter offset}[rdoc-ref:Time@Single-Letter+Offsets].
+- {Integer offset}[rdoc-ref:Time@Integer+Offsets].
+- {Timezone object}[rdoc-ref:Time@Timezone+Objects].
+- {Timezone name}[rdoc-ref:Time@Timezone+Names].
+
+=== Hours/Minutes Offsets
+
+The zone value may be a string offset from UTC
+in the form <tt>'+HH:MM'</tt> or <tt>'-HH:MM'</tt>,
+where:
+
+- +HH+ is the 2-digit hour in the range <tt>0..23</tt>.
+- +MM+ is the 2-digit minute in the range <tt>0..59</tt>.
+
+Examples:
+
+ t = Time.utc(2000, 1, 1, 20, 15, 1) # => 2000-01-01 20:15:01 UTC
+ Time.at(t, in: '-23:59') # => 1999-12-31 20:16:01 -2359
+ Time.at(t, in: '+23:59') # => 2000-01-02 20:14:01 +2359
+
+=== Single-Letter Offsets
+
+The zone value may be a letter in the range <tt>'A'..'I'</tt>
+or <tt>'K'..'Z'</tt>;
+see {List of military time zones}[https://en.wikipedia.org/wiki/List_of_military_time_zones]:
+
+ t = Time.utc(2000, 1, 1, 20, 15, 1) # => 2000-01-01 20:15:01 UTC
+ Time.at(t, in: 'A') # => 2000-01-01 21:15:01 +0100
+ Time.at(t, in: 'I') # => 2000-01-02 05:15:01 +0900
+ Time.at(t, in: 'K') # => 2000-01-02 06:15:01 +1000
+ Time.at(t, in: 'Y') # => 2000-01-01 08:15:01 -1200
+ Time.at(t, in: 'Z') # => 2000-01-01 20:15:01 UTC
+
+=== \Integer Offsets
+
+The zone value may be an integer number of seconds
+in the range <tt>-86399..86399</tt>:
+
+ t = Time.utc(2000, 1, 1, 20, 15, 1) # => 2000-01-01 20:15:01 UTC
+ Time.at(t, in: -86399) # => 1999-12-31 20:15:02 -235959
+ Time.at(t, in: 86399) # => 2000-01-02 20:15:00 +235959
+
+=== Timezone Objects
+
+The zone value may be an object responding to certain timezone methods, an
+instance of {Timezone}[https://github.com/panthomakos/timezone] and
+{TZInfo}[https://tzinfo.github.io] for example.
+
+The timezone methods are:
+
+- +local_to_utc+:
+
+ - Called when Time.new is invoked with +tz+
+ as the value of positional argument +zone+ or keyword argument +in:+.
+ - Argument: a {Time-like object}[rdoc-ref:Time@Time-Like+Objects].
+ - Returns: a {Time-like object}[rdoc-ref:Time@Time-Like+Objects] in the UTC timezone.
+
+- +utc_to_local+:
+
+ - Called when Time.at or Time.now is invoked with +tz+
+ as the value for keyword argument +in:+,
+ and when Time#getlocal or Time#localtime is called with +tz+
+ as the value for positional argument +zone+.
+ - Argument: a {Time-like object}[rdoc-ref:Time@Time-Like+Objects].
+ - Returns: a {Time-like object}[rdoc-ref:Time@Time-Like+Objects] in the local timezone.
+
+A custom timezone class may have these instance methods,
+which will be called if defined:
+
+- +abbr+:
+
+ - Called when Time#strftime is invoked with a format involving <tt>%Z</tt>.
+ - Argument: a {Time-like object}[rdoc-ref:Time@Time-Like+Objects].
+ - Returns: a string abbreviation for the timezone name.
+
+- +dst?+:
+
+ - Called when Time.at or Time.now is invoked with +tz+
+ as the value for keyword argument +in:+,
+ and when Time#getlocal or Time#localtime is called with +tz+
+ as the value for positional argument +zone+.
+ - Argument: a {Time-like object}[rdoc-ref:Time@Time-Like+Objects].
+ - Returns: whether the time is daylight saving time.
+
+- +name+:
+
+ - Called when <tt>Marshal.dump(t)</tt> is invoked
+ - Argument: none.
+ - Returns: the string name of the timezone.
+
+==== +Time+-Like Objects
+
+A +Time+-like object is a container object capable of interfacing with
+timezone libraries for timezone conversion.
+
+The argument to the timezone conversion methods above will have attributes
+similar to Time, except that timezone related attributes are meaningless.
+
+The objects returned by +local_to_utc+ and +utc_to_local+ methods of the
+timezone object may be of the same class as their arguments, of arbitrary
+object classes, or of class Integer.
+
+For a returned class other than +Integer+, the class must have the
+following methods:
+
+- +year+
+- +mon+
+- +mday+
+- +hour+
+- +min+
+- +sec+
+- +isdst+
+- +to_i+
+
+For a returned +Integer+, its components, decomposed in UTC, are
+interpreted as times in the specified timezone.
+
+=== Timezone Names
+
+If the class (the receiver of class methods, or the class of the receiver
+of instance methods) has +find_timezone+ singleton method, this method is
+called to achieve the corresponding timezone object from a timezone name.
+
+For example, using {Timezone}[https://github.com/panthomakos/timezone]:
+ class TimeWithTimezone < Time
+ require 'timezone'
+ def self.find_timezone(z) = Timezone[z]
+ end
+
+ TimeWithTimezone.now(in: "America/New_York") #=> 2023-12-25 00:00:00 -0500
+ TimeWithTimezone.new("2023-12-25 America/New_York") #=> 2023-12-25 00:00:00 -0500
+
+Or, using {TZInfo}[https://tzinfo.github.io]:
+ class TimeWithTZInfo < Time
+ require 'tzinfo'
+ def self.find_timezone(z) = TZInfo::Timezone.get(z)
+ end
+
+ TimeWithTZInfo.now(in: "America/New_York") #=> 2023-12-25 00:00:00 -0500
+ TimeWithTZInfo.new("2023-12-25 America/New_York") #=> 2023-12-25 00:00:00 -0500
+
+You can define this method per subclasses, or on the toplevel Time class.
diff --git a/doc/bsearch.rdoc b/doc/bsearch.rdoc
index ca8091fc0d..90705853d7 100644
--- a/doc/bsearch.rdoc
+++ b/doc/bsearch.rdoc
@@ -1,4 +1,4 @@
-== Binary Searching
+= Binary Searching
A few Ruby methods support binary searching in a collection:
diff --git a/doc/case_mapping.rdoc b/doc/case_mapping.rdoc
index 3c42154973..57c037b9d8 100644
--- a/doc/case_mapping.rdoc
+++ b/doc/case_mapping.rdoc
@@ -1,4 +1,4 @@
-== Case Mapping
+= Case Mapping
Some string-oriented methods use case mapping.
@@ -24,7 +24,7 @@ In Symbol:
- Symbol#swapcase
- Symbol#upcase
-=== Default Case Mapping
+== Default Case Mapping
By default, all of these methods use full Unicode case mapping,
which is suitable for most languages.
@@ -60,7 +60,7 @@ Case changes may not be reversible:
Case changing methods may not maintain Unicode normalization.
See String#unicode_normalize).
-=== Options for Case Mapping
+== Options for Case Mapping
Except for +casecmp+ and +casecmp?+,
each of the case-mapping methods listed above
diff --git a/doc/character_selectors.rdoc b/doc/character_selectors.rdoc
index e01b0e6a25..47cf242be7 100644
--- a/doc/character_selectors.rdoc
+++ b/doc/character_selectors.rdoc
@@ -1,6 +1,6 @@
-== Character Selectors
+= Character Selectors
-=== Character Selector
+== Character Selector
A _character_ _selector_ is a string argument accepted by certain Ruby methods.
Each of these instance methods accepts one or more character selectors:
@@ -70,7 +70,7 @@ In a character selector, these three characters get special treatment:
"hello\r\nworld".delete("\\r") # => "hello\r\nwold"
"hello\r\nworld".delete("\\\r") # => "hello\nworld"
-=== Multiple Character Selectors
+== Multiple Character Selectors
These instance methods accept multiple character selectors:
diff --git a/doc/command_injection.rdoc b/doc/command_injection.rdoc
index af09be23f0..ee33d4a04e 100644
--- a/doc/command_injection.rdoc
+++ b/doc/command_injection.rdoc
@@ -1,4 +1,4 @@
-== Command Injection
+= Command Injection
Some Ruby core methods accept string data
that includes text to be executed as a system command.
@@ -7,19 +7,27 @@ They should not be called with unknown or unsanitized commands.
These methods include:
+- Kernel.exec
+- Kernel.spawn
- Kernel.system
- {\`command` (backtick method)}[rdoc-ref:Kernel#`]
(also called by the expression <tt>%x[command]</tt>).
-- IO.popen(command).
+- IO.popen (when called with other than <tt>"-"</tt>).
+
+Some methods execute a system command only if the given path name starts
+with a <tt>|</tt>:
+
+- Kernel.open(command).
- IO.read(command).
- IO.write(command).
- IO.binread(command).
- IO.binwrite(command).
- IO.readlines(command).
- IO.foreach(command).
+- URI.open(command).
Note that some of these methods do not execute commands when called
-from subclass \File:
+from subclass +File+:
- File.read(path).
- File.write(path).
diff --git a/doc/command_line/environment.md b/doc/command_line/environment.md
new file mode 100644
index 0000000000..abdfd5cfeb
--- /dev/null
+++ b/doc/command_line/environment.md
@@ -0,0 +1,173 @@
+## Environment
+
+Certain command-line options affect the execution environment
+of the invoked Ruby program.
+
+### About the Examples
+
+The examples here use command-line option `-e`,
+which passes the Ruby code to be executed on the command line itself:
+
+```sh
+$ ruby -e 'puts "Hello, World."'
+```
+
+### Option `-C`
+
+The argument to option `-C` specifies a working directory
+for the invoked Ruby program;
+does not change the working directory for the current process:
+
+```sh
+$ basename `pwd`
+ruby
+$ ruby -C lib -e 'puts File.basename(Dir.pwd)'
+lib
+$ basename `pwd`
+ruby
+```
+
+Whitespace between the option and its argument may be omitted.
+
+### Option `-I`
+
+The argument to option `-I` specifies a directory
+to be added to the array in global variable `$LOAD_PATH`;
+the option may be given more than once:
+
+```sh
+$ pushd /tmp
+$ ruby -e 'p $LOAD_PATH.size'
+8
+$ ruby -I my_lib -I some_lib -e 'p $LOAD_PATH.size'
+10
+$ ruby -I my_lib -I some_lib -e 'p $LOAD_PATH.take(2)'
+["/tmp/my_lib", "/tmp/some_lib"]
+$ popd
+```
+
+Whitespace between the option and its argument may be omitted.
+
+### Option `-r`
+
+The argument to option `-r` specifies a library to be required
+before executing the Ruby program;
+the option may be given more than once:
+
+```sh
+$ ruby -e 'p defined?(JSON); p defined?(CSV)'
+nil
+nil
+$ ruby -r CSV -r JSON -e 'p defined?(JSON); p defined?(CSV)'
+"constant"
+"constant"
+```
+
+Whitespace between the option and its argument may be omitted.
+
+### Option `-0`
+
+Option `-0` defines the input record separator `$/`
+for the invoked Ruby program.
+
+The optional argument to the option must be octal digits,
+each in the range `0..7`;
+these digits are prefixed with digit `0` to form an octal value:
+
+- If no argument is given, the input record separator is `0x00`.
+- If the argument is `0`, the input record separator is `''`;
+ see {Special Line Separator Values}[rdoc-ref:IO@Special+Line+Separator+Values].
+- If the argument is in range `(1..0377)`,
+ it becomes the character value of the input record separator `$/`.
+- Otherwise, the input record separator is `nil`.
+
+Examples:
+
+```sh
+$ ruby -0 -e 'p $/'
+"\x00"
+ruby -00 -e 'p $/'
+""
+$ ruby -012 -e 'p $/'
+"\n"
+$ ruby -015 -e 'p $/'
+"\r"
+$ ruby -0377 -e 'p $/'
+"\xFF"
+$ ruby -0400 -e 'p $/'
+nil
+```
+
+The option may not be separated from its argument by whitespace.
+
+### Option `-d`
+
+Some code in (or called by) the Ruby program may include statements or blocks
+conditioned by the global variable `$DEBUG` (e.g., `if $DEBUG`);
+these commonly write to `$stdout` or `$stderr`.
+
+The default value for `$DEBUG` is `false`;
+option `-d` (or `--debug`) sets it to `true`:
+
+```sh
+$ ruby -e 'p $DEBUG'
+false
+$ ruby -d -e 'p $DEBUG'
+true
+```
+
+### Option '-w'
+
+Option `-w` (lowercase letter) is equivalent to option `-W1` (uppercase letter).
+
+### Option `-W`
+
+Any Ruby code can create a <i>warning message</i> by calling method Kernel#warn;
+methods in the Ruby core and standard libraries can also create warning messages.
+Such a message may be printed on `$stderr`
+(or not, depending on certain settings).
+
+Option `-W` helps determine whether a particular warning message
+will be written,
+by setting the initial value of global variable `$-W`:
+
+- `-W0`: Sets `$-W` to `0` (silent; no warnings).
+- `-W1`: Sets `$-W` to `1` (moderate verbosity).
+- `-W2`: Sets `$-W` to `2` (high verbosity).
+- `-W`: Same as `-W2` (high verbosity).
+- Option not given: Same as `-W1` (moderate verbosity).
+
+The value of `$-W`, in turn, determines which warning messages (if any)
+are to be printed to `$stdout` (see Kernel#warn):
+
+```sh
+$ ruby -W1 -e 'p $foo'
+nil
+$ ruby -W2 -e 'p $foo'
+-e:1: warning: global variable '$foo' not initialized
+nil
+```
+
+Ruby code may also define warnings for certain categories;
+these are the default settings for the defined categories:
+
+```
+Warning[:experimental] # => true
+Warning[:deprecated] # => false
+Warning[:performance] # => false
+```
+
+They may also be set:
+```
+Warning[:experimental] = false
+Warning[:deprecated] = true
+Warning[:performance] = true
+```
+
+You can suppress a category by prefixing `no-` to the category name:
+
+```
+$ ruby -W:no-experimental -e 'p IO::Buffer.new'
+#<IO::Buffer>
+```
+
diff --git a/doc/contributing/building_ruby.md b/doc/contributing/building_ruby.md
index d4cedbcb69..96cee40cb4 100644
--- a/doc/contributing/building_ruby.md
+++ b/doc/contributing/building_ruby.md
@@ -17,7 +17,7 @@
* autoconf - 2.67 or later
* gperf - 3.1 or later
* Usually unneeded; only if you edit some source files using gperf
- * ruby - 2.5 or later
+ * ruby - 3.0 or later
* We can upgrade this version to system ruby version of the latest Ubuntu LTS.
2. Install optional, recommended dependencies:
@@ -25,7 +25,8 @@
* libffi (to build fiddle)
* gmp (if you with to accelerate Bignum operations)
* libexecinfo (FreeBSD)
- * rustc - 1.58.0 or later (if you wish to build [YJIT](/doc/yjit/yjit.md))
+ * rustc - 1.58.0 or later, if you wish to build
+ [YJIT](https://docs.ruby-lang.org/en/master/RubyVM/YJIT.html).
If you installed the libraries needed for extensions (openssl, readline, libyaml, zlib) into other than the OS default place,
typically using Homebrew on macOS, add `--with-EXTLIB-dir` options to `CONFIGURE_ARGS` environment variable.
@@ -41,29 +42,32 @@
1. Download ruby source code:
+ Select one of the below.
+
1. Build from the tarball:
- Download the latest tarball from [ruby-lang.org](https://www.ruby-lang.org/en/downloads/) and
- extract it. Example for Ruby 3.0.2:
+ Download the latest tarball from [ruby-lang.org](https://www.ruby-lang.org/en/downloads/) and
+ extract it. Example for Ruby 3.0.2:
- ``` shell
- tar -xzf ruby-3.0.2.tar.gz
- cd ruby-3.0.2
- ```
+ ``` shell
+ tar -xzf ruby-3.0.2.tar.gz
+ cd ruby-3.0.2
+ ```
2. Build from the git repository:
- Checkout the CRuby source code:
+ Checkout the CRuby source code:
- ``` shell
- git clone https://github.com/ruby/ruby.git
- ```
+ ``` shell
+ git clone https://github.com/ruby/ruby.git
+ cd ruby
+ ```
- Generate the configure file:
+ Generate the configure file:
- ``` shell
- ./autogen.sh
- ```
+ ``` shell
+ ./autogen.sh
+ ```
2. Create a `build` directory separate from the source directory:
@@ -85,20 +89,33 @@
../configure --prefix="${HOME}/.rubies/ruby-master"
```
- - If you are frequently building Ruby, add the `--disable-install-doc` flag to not build documentation which will speed up the build process.
+ - Also `-C` (or `--config-cache`) would reduce time to configure from the next time.
5. Build Ruby:
``` shell
- make install
+ make
```
6. [Run tests](testing_ruby.md) to confirm your build succeeded.
+7. Install Ruby:
+
+ ``` shell
+ make install
+ ```
+
+ - If you need to run `make install` with `sudo` and want to avoid document generation with different permissions, you can use
+ `make SUDO=sudo install`.
+
### Unexplainable Build Errors
If you are having unexplainable build errors, after saving all your work, try running `git clean -xfd` in the source root to remove all git ignored local files. If you are working from a source directory that's been updated several times, you may have temporary build artifacts from previous releases which can cause build failures.
+## Building on Windows
+
+The documentation for building on Windows can be found [here](../windows.md).
+
## More details
If you're interested in continuing development on Ruby, here are more details
@@ -153,19 +170,37 @@ with the Ruby script you'd like to run. You can use the following make targets:
* `make lldb-ruby`: Runs `test.rb` using Ruby in lldb
* `make gdb-ruby`: Runs `test.rb` using Ruby in gdb
+### Compiling for Debugging
+
+You should configure Ruby without optimization and other flags that may interfere with debugging:
+
+``` shell
+./configure --enable-debug-env optflags="-O0 -fno-omit-frame-pointer"
+```
+
### Building with Address Sanitizer
-Using the address sanitizer is a great way to detect memory issues.
+Using the address sanitizer (ASAN) is a great way to detect memory issues. It can detect memory safety issues in Ruby itself, and also in any C extensions compiled with and loaded into a Ruby compiled with ASAN.
``` shell
./autogen.sh
mkdir build && cd build
-export ASAN_OPTIONS="halt_on_error=0:use_sigaltstack=0:detect_leaks=0"
-../configure cppflags="-fsanitize=address -fno-omit-frame-pointer" optflags=-O0 LDFLAGS="-fsanitize=address -fno-omit-frame-pointer"
+../configure CC=clang cflags="-fsanitize=address -fno-omit-frame-pointer -DUSE_MN_THREADS=0" # and any other options you might like
make
```
+The compiled Ruby will now automatically crash with a report and a backtrace if ASAN detects a memory safety issue. To run Ruby's test suite under ASAN, issue the following command. Note that this will take quite a long time (over two hours on my laptop); the `RUBY_TEST_TIMEOUT_SCALE` and `SYNTAX_SUGEST_TIMEOUT` variables are required to make sure tests don't spuriously fail with timeouts when in fact they're just slow.
+
+``` shell
+RUBY_TEST_TIMEOUT_SCALE=5 SYNTAX_SUGGEST_TIMEOUT=600 make check
+```
+
+Please note, however, the following caveats!
-On Linux it is important to specify `-O0` when debugging. This is especially true for ASAN which sometimes works incorrectly at higher optimisation levels.
+* ASAN will not work properly on any currently released version of Ruby; the necessary support is currently only present on Ruby's master branch (and the whole test suite passes only as of commit [9d0a5148ae062a0481a4a18fbeb9cfd01dc10428](https://bugs.ruby-lang.org/projects/ruby-master/repository/git/revisions/9d0a5148ae062a0481a4a18fbeb9cfd01dc10428))
+* Due to [this bug](https://bugs.ruby-lang.org/issues/20243), Clang generates code for threadlocal variables which doesn't work with M:N threading. Thus, it's necessary to disable M:N threading support at build time for now (with the `-DUSE_MN_THREADS=0` configure argument).
+* Currently, ASAN will only work correctly when using a recent head build of LLVM/Clang - it requires [this bugfix](https://github.com/llvm/llvm-project/pull/75290) related to multithreaded `fork`, which is not yet in any released version. See [here](https://llvm.org/docs/CMake.html) for instructions on how to build LLVM/Clang from source (note you will need at least the `clang` and `compiler-rt` projects enabled). Then, you will need to replace `CC=clang` in the instructions with an explicit path to your built Clang binary.
+* ASAN has only been tested so far with Clang on Linux. It may or may not work with other compilers or on other platforms - please file an issue on [https://bugs.ruby-lang.org](https://bugs.ruby-lang.org) if you run into problems with such configurations (or, to report that they actually work properly!)
+* In particular, although I have not yet tried it, I have reason to believe ASAN will _not_ work properly on macOS yet - the fix for the multithreaded fork issue was actually reverted for macOS (see [here](https://github.com/llvm/llvm-project/commit/2a03854e4ce9bb1bcd79a211063bc63c4657f92c)). Please open an issue on [https://bugs.ruby-lang.org](https://bugs.ruby-lang.org) if this is a problem for you.
## How to measure coverage of C and Ruby code
diff --git a/doc/contributing/documentation_guide.md b/doc/contributing/documentation_guide.md
index 907d935de9..59953d0d52 100644
--- a/doc/contributing/documentation_guide.md
+++ b/doc/contributing/documentation_guide.md
@@ -20,10 +20,12 @@ build directory:
make html
```
+If you don't have a build directory, follow the [quick start
+guide](building_ruby.md#label-Quick+start+guide) up to step 4.
+
Then you can preview your changes by opening
`{build folder}/.ext/html/index.html` file in your browser.
-
## Goal
The goal of Ruby documentation is to impart the most important
@@ -41,16 +43,17 @@ Use your judgment about what the user needs to know.
- Write short declarative or imperative sentences.
- Group sentences into (ideally short) paragraphs,
each covering a single topic.
-- Organize material with [headers](rdoc-ref:RDoc::Markup@Headers).
+- Organize material with
+ [headings](rdoc-ref:RDoc::MarkupReference@Headings).
- Refer to authoritative and relevant sources using
- [links](rdoc-ref:RDoc::Markup@Links).
+ [links](rdoc-ref:RDoc::MarkupReference@Links).
- Use simple verb tenses: simple present, simple past, simple future.
- Use simple sentence structure, not compound or complex structure.
- Avoid:
- Excessive comma-separated phrases;
- consider a [list](rdoc-ref:RDoc::Markup@Simple+Lists).
+ consider a [list](rdoc-ref:RDoc::MarkupReference@Lists).
- Idioms and culture-specific references.
- - Overuse of headers.
+ - Overuse of headings.
- Using US-ASCII-incompatible characters in C source files;
see [Characters](#label-Characters) below.
@@ -124,16 +127,23 @@ a.shuffle! #=> [2, 3, 1]
a #=> [2, 3, 1]
```
-### Headers
+### Headings
+
+Organize a long discussion for a class or module with [headings](rdoc-ref:RDoc::MarkupReference@Headings).
+
+Do not use formal headings in the documentation for a method or constant.
-Organize a long discussion with [headers](rdoc-ref:RDoc::Markup@Headers).
+In the rare case where heading-like structures are needed
+within the documentation for a method or constant, use
+[bold text](rdoc-ref:RDoc::MarkupReference@Bold)
+as pseudo-headings.
### Blank Lines
A blank line begins a new paragraph.
-A [code block](rdoc-ref:RDoc::Markup@Paragraphs+and+Verbatim)
-or [list](rdoc-ref:RDoc::Markup@Simple+Lists)
+A [code block](rdoc-ref:RDoc::MarkupReference@Code+Blocks)
+or [list](rdoc-ref:RDoc::MarkupReference@Lists)
should be preceded by and followed by a blank line.
This is unnecessary for the HTML output, but helps in the `ri` output.
@@ -150,19 +160,76 @@ For a method name in text:
or a hash mark for an instance method:
<tt>Foo.bar</tt>, <tt>Foo#baz</tt>.
+### Embedded Code and Commands
+
+Code or commands embedded in running text (i.e., not in a code block)
+should marked up as
+[monofont](rdoc-ref:RDoc::MarkupReference@Monofont).
+
+Code that is a simple string should include the quote marks.
+
### Auto-Linking
In general, \RDoc's auto-linking should not be suppressed.
For example, we should write `Array`, not `\Array`.
-We might consider whether to suppress when:
+However, suppress when the word in question:
-- The word in question does not refer to a Ruby entity
+- Does not refer to a Ruby entity
(e.g., some uses of _Class_ or _English_).
-- The reference is to the current class document
- (e.g., _Array_ in the documentation for class `Array`).
-- The same reference is repeated many times
- (e.g., _RDoc_ on this page).
+- Refers to the current document
+ (e.g., _Array_ in the documentation for class `Array`);
+ in that case, the word should be forced to
+ [monofont](rdoc-ref:RDoc::MarkupReference@Monofont).
+
+Most often, the name of a class, module, or method
+will be auto-linked:
+
+- Array.
+- Enumerable.
+- File.new
+- File#read.
+
+If not, or if you suppress autolinking, consider forcing
+[monofont](rdoc-ref:RDoc::MarkupReference@Monofont).
+
+### Explicit Links
+
+When writing an explicit link, follow these guidelines.
+
+#### +rdoc-ref+ Scheme
+
+Use the +rdoc-ref+ scheme for:
+
+- A link in core documentation to other core documentation.
+- A link in core documentation to documentation in a standard library package.
+- A link in a standard library package to other documentation in that same
+ standard library package.
+
+See section "+rdoc-ref+ Scheme" in {Links}[rdoc-ref:RDoc::MarkupReference@Links].
+
+#### URL-Based Link
+
+Use a full URL-based link for:
+
+- A link in standard library documentation to documentation in the core.
+- A link in standard library documentation to documentation in a different
+ standard library package.
+
+Doing so ensures that the link will valid even when the package documentation
+is built independently (separately from the core documentation).
+
+The link should lead to a target in https://docs.ruby-lang.org/en/master/.
+
+Also use a full URL-based link for a link to an off-site document.
+
+### Variable Names
+
+The name of a variable (as specified in its call-seq) should be marked up as
+[monofont](rdoc-ref:RDoc::MarkupReference@Monofont).
+
+Also, use monofont text for the name of a transient variable
+(i.e., one defined and used only in the discussion, such as +n+).
### HTML Tags
@@ -175,16 +242,21 @@ may not render them properly.
In particular, avoid building tables with HTML tags
(<tt><table></tt>, etc.).
-Alternatives are:
-
-- The GFM (GitHub Flavored Markdown) table extension,
- which is enabled by default. See
- {GFM tables extension}[https://github.github.com/gfm/#tables-extension-].
+Alternatives:
- A {verbatim text block}[rdoc-ref:RDoc::MarkupReference@Verbatim+Text+Blocks],
- using spaces and punctuation to format the text.
- Note that {text markup}[rdoc-ref:RDoc::MarkupReference@Text+Markup]
- will not be honored.
+ using spaces and punctuation to format the text;
+ note that {text markup}[rdoc-ref:RDoc::MarkupReference@Text+Markup]
+ will not be honored:
+
+ - Example {source}[https://github.com/ruby/ruby/blob/34d802f32f00df1ac0220b62f72605827c16bad8/file.c#L6570-L6596].
+ - Corresponding {output}[rdoc-ref:File@Read-2FWrite+Mode].
+
+- (Markdown format only): A {Github Flavored Markdown (GFM) table}[https://github.github.com/gfm/#tables-extension-],
+ using special formatting for the text:
+
+ - Example {source}[https://github.com/ruby/ruby/blob/34d802f32f00df1ac0220b62f72605827c16bad8/doc/contributing/glossary.md?plain=1].
+ - Corresponding {output}[https://docs.ruby-lang.org/en/master/contributing/glossary_md.html].
## Documenting Classes and Modules
@@ -214,9 +286,12 @@ Guidelines:
- The section title is `What's Here`.
- Consider listing the parent class and any included modules; consider
- [links](rdoc-ref:RDoc::Markup@Links)
+ [links](rdoc-ref:RDoc::MarkupReference@Links)
to their "What's Here" sections if those exist.
-- List methods as a bullet list:
+- All methods mentioned in the left-pane table of contents
+ should be listed (including any methods extended from another class).
+- Attributes (which are not included in the TOC) may also be listed.
+- Display methods as items in one or more bullet lists:
- Begin each item with the method name, followed by a colon
and a short description.
@@ -224,9 +299,9 @@ Guidelines:
(and do not list the aliases separately).
- Check the rendered documentation to determine whether \RDoc has recognized
the method and linked to it; if not, manually insert a
- [link](rdoc-ref:RDoc::Markup@Links).
+ [link](rdoc-ref:RDoc::MarkupReference@Links).
-- If there are numerous entries, consider grouping them into subsections with headers.
+- If there are numerous entries, consider grouping them into subsections with headings.
- If there are more than a few such subsections,
consider adding a table of contents just below the main section title.
@@ -249,7 +324,7 @@ For methods written in Ruby, \RDoc documents the calling sequence automatically.
For methods written in C, \RDoc cannot determine what arguments
the method accepts, so those need to be documented using \RDoc directive
-[`call-seq:`](rdoc-ref:RDoc::Markup@Method+arguments).
+[`call-seq:`](rdoc-ref:RDoc::MarkupReference@Directives+for+Method+Documentation).
For a singleton method, use the form:
@@ -388,7 +463,7 @@ argument passed if it is not obvious, not explicitly mentioned in the
details, and not implicitly shown in the examples.
If there is more than one argument or block argument, use a
-[labeled list](rdoc-ref:RDoc::Markup@Labeled+Lists).
+[labeled list](rdoc-ref:RDoc::MarkupReference@Labeled+Lists).
### Corner Cases and Exceptions
diff --git a/doc/contributing/glossary.md b/doc/contributing/glossary.md
index 0d27d28c96..86c6671fbd 100644
--- a/doc/contributing/glossary.md
+++ b/doc/contributing/glossary.md
@@ -10,7 +10,7 @@ Just a list of acronyms I've run across in the Ruby source code and their meanin
| `cd` | Call Data. A data structure that points at the `ci` and the `cc`. `iseq` objects points at the `cd`, and access call information and call caches via this structure |
| `cfp`| Control Frame Pointer. Represents a Ruby stack frame. Calling a method pushes a new frame (cfp), returning pops a frame. Points at the `pc`, `sp`, `ep`, and the corresponding `iseq`|
| `ci` | Call Information. Refers to an `rb_callinfo` struct. Contains call information about the call site, including number of parameters to be passed, whether it they are keyword arguments or not, etc. Used in conjunction with the `cc` and `cd`. |
-| `cref` | Class reference. A structure pointing to the class reference where `klass_or_self`, visibility scope, and refinements are stored. It also stores a pointer to the next class in the hierachy referenced by `rb_cref_struct * next`. The Class reference is lexically scoped. |
+| `cref` | Class reference. A structure pointing to the class reference where `klass_or_self`, visibility scope, and refinements are stored. It also stores a pointer to the next class in the hierarchy referenced by `rb_cref_struct * next`. The Class reference is lexically scoped. |
| CRuby | Implementation of Ruby written in C |
| `cvar` | Class Variable. Refers to a Ruby class variable like `@@foo` |
| `dvar` | Dynamic Variable. Used by the parser to refer to local variables that are defined outside of the current lexical scope. For example `def foo; bar = 1; -> { p bar }; end` the "bar" inside the block is a `dvar` |
diff --git a/doc/contributing/testing_ruby.md b/doc/contributing/testing_ruby.md
index 167ab55c55..dfb7fb3a65 100644
--- a/doc/contributing/testing_ruby.md
+++ b/doc/contributing/testing_ruby.md
@@ -82,10 +82,10 @@ We can run any of the make scripts [in parallel](building_ruby.md#label-Running+
We can display the help of the `TESTS` option:
```
- $ make test-all TESTS=--help
+ make test-all TESTS=--help
```
- If we would like to run both the `test/` and `bootstraptest/` test suites, we can run
+ If we would like to run the `test/`, `bootstraptest/` and `spec/` test suites (the `spec/` is explained in a later section), we can run
```
make check
@@ -99,28 +99,28 @@ We can run any of the make scripts [in parallel](building_ruby.md#label-Running+
make test-spec
```
- To run a specific directory, we can use `MSPECOPT` to specify the directory:
+ To run a specific directory, we can use `SPECOPTS` to specify the directory:
```
- make test-spec MSPECOPT=spec/ruby/core/array
+ make test-spec SPECOPTS=spec/ruby/core/array
```
- To run a specific file, we can also use `MSPECOPT` to specify the file:
+ To run a specific file, we can also use `SPECOPTS` to specify the file:
```
- make test-spec MSPECOPT=spec/ruby/core/array/any_spec.rb
+ make test-spec SPECOPTS=spec/ruby/core/array/any_spec.rb
```
To run a specific test, we can use the `--example` flag to match against the test name:
```
- make test-spec MSPECOPT="../spec/ruby/core/array/any_spec.rb --example='is false if the array is empty'"
+ make test-spec SPECOPTS="../spec/ruby/core/array/any_spec.rb --example='is false if the array is empty'"
```
To run these specs with logs, we can use:
```
- make test-spec MSPECOPT=-Vfs
+ make test-spec SPECOPTS=-Vfs
```
To run a ruby-spec file or directory with GNU make, we can use
@@ -140,5 +140,17 @@ We can run any of the make scripts [in parallel](building_ruby.md#label-Running+
To run a specific bundler spec file, we can use `BUNDLER_SPECS` as follows:
```
- $ make test-bundler BUNDLER_SPECS=commands/exec_spec.rb
+ make test-bundler BUNDLER_SPECS=commands/exec_spec.rb
```
+
+## Troubleshooting
+
+### Running test suites on s390x CPU Architecture
+
+If we see failing tests related to the zlib library on s390x CPU architecture, we can run the test suites with `DFLTCC=0` to pass:
+
+```
+DFLTCC=0 make check
+```
+
+The failures can happen with the zlib library applying the patch [madler/zlib#410](https://github.com/madler/zlib/pull/410) to enable the deflate algorithm producing a different compressed byte stream. We manage this issue at [[ruby-core:114942][Bug #19909]](https://bugs.ruby-lang.org/issues/19909).
diff --git a/doc/csv/options/parsing/liberal_parsing.rdoc b/doc/csv/options/parsing/liberal_parsing.rdoc
index b8b9b00c98..603de28613 100644
--- a/doc/csv/options/parsing/liberal_parsing.rdoc
+++ b/doc/csv/options/parsing/liberal_parsing.rdoc
@@ -1,13 +1,13 @@
====== Option +liberal_parsing+
-Specifies the boolean value that determines whether
+Specifies the boolean or hash value that determines whether
CSV will attempt to parse input not conformant with RFC 4180,
such as double quotes in unquoted fields.
Default value:
CSV::DEFAULT_OPTIONS.fetch(:liberal_parsing) # => false
-For examples in this section:
+For the next two examples:
str = 'is,this "three, or four",fields'
Without +liberal_parsing+:
@@ -17,3 +17,22 @@ Without +liberal_parsing+:
With +liberal_parsing+:
ary = CSV.parse_line(str, liberal_parsing: true)
ary # => ["is", "this \"three", " or four\"", "fields"]
+
+Use the +backslash_quote+ sub-option to parse values that use
+a backslash to escape a double-quote character. This
+causes the parser to treat <code>\"</code> as if it were
+<code>""</code>.
+
+For the next two examples:
+ str = 'Show,"Harry \"Handcuff\" Houdini, the one and only","Tampa Theater"'
+
+With +liberal_parsing+, but without the +backslash_quote+ sub-option:
+ # Incorrect interpretation of backslash; incorrectly interprets the quoted comma as a field separator.
+ ary = CSV.parse_line(str, liberal_parsing: true)
+ ary # => ["Show", "\"Harry \\\"Handcuff\\\" Houdini", " the one and only\"", "Tampa Theater"]
+ puts ary[1] # => "Harry \"Handcuff\" Houdini
+
+With +liberal_parsing+ and its +backslash_quote+ sub-option:
+ ary = CSV.parse_line(str, liberal_parsing: { backslash_quote: true })
+ ary # => ["Show", "Harry \"Handcuff\" Houdini, the one and only", "Tampa Theater"]
+ puts ary[1] # => Harry "Handcuff" Houdini, the one and only
diff --git a/doc/csv/recipes/generating.rdoc b/doc/csv/recipes/generating.rdoc
index a6bd88a714..e61838d31a 100644
--- a/doc/csv/recipes/generating.rdoc
+++ b/doc/csv/recipes/generating.rdoc
@@ -163,7 +163,7 @@ This example defines and uses two custom write converters to strip and upcase ge
=== RFC 4180 Compliance
By default, \CSV generates data that is compliant with
-{RFC 4180}[https://tools.ietf.org/html/rfc4180]
+{RFC 4180}[https://www.rfc-editor.org/rfc/rfc4180]
with respect to:
- Column separator.
- Quote character.
diff --git a/doc/csv/recipes/parsing.rdoc b/doc/csv/recipes/parsing.rdoc
index f3528fbdf1..1b7071e33f 100644
--- a/doc/csv/recipes/parsing.rdoc
+++ b/doc/csv/recipes/parsing.rdoc
@@ -191,7 +191,7 @@ Output:
=== RFC 4180 Compliance
By default, \CSV parses data that is compliant with
-{RFC 4180}[https://tools.ietf.org/html/rfc4180]
+{RFC 4180}[https://www.rfc-editor.org/rfc/rfc4180]
with respect to:
- Row separator.
- Column separator.
diff --git a/doc/encodings.rdoc b/doc/encodings.rdoc
index 1f3c54d740..97c0d22616 100644
--- a/doc/encodings.rdoc
+++ b/doc/encodings.rdoc
@@ -1,6 +1,6 @@
-== Encodings
+= Encodings
-=== The Basics
+== The Basics
A {character encoding}[https://en.wikipedia.org/wiki/Character_encoding],
often shortened to _encoding_, is a mapping between:
@@ -30,9 +30,9 @@ Other characters, such as the Euro symbol, are multi-byte:
s = "\u20ac" # => "€"
s.bytes # => [226, 130, 172]
-=== The \Encoding \Class
+== The \Encoding \Class
-==== \Encoding Objects
+=== \Encoding Objects
Ruby encodings are defined by constants in class \Encoding.
There can be only one instance of \Encoding for each of these constants.
@@ -43,7 +43,7 @@ There can be only one instance of \Encoding for each of these constants.
Encoding.list.take(3)
# => [#<Encoding:ASCII-8BIT>, #<Encoding:UTF-8>, #<Encoding:US-ASCII>]
-==== Names and Aliases
+=== Names and Aliases
\Method Encoding#name returns the name of an \Encoding:
@@ -78,7 +78,7 @@ because it includes both the names and their aliases.
Encoding.find("US-ASCII") # => #<Encoding:US-ASCII>
Encoding.find("US-ASCII").class # => Encoding
-==== Default Encodings
+=== Default Encodings
\Method Encoding.find, above, also returns a default \Encoding
for each of these special names:
@@ -118,7 +118,7 @@ for each of these special names:
Encoding.default_internal = 'US-ASCII' # => "US-ASCII"
Encoding.default_internal # => #<Encoding:US-ASCII>
-==== Compatible Encodings
+=== Compatible Encodings
\Method Encoding.compatible? returns whether two given objects are encoding-compatible
(that is, whether they can be concatenated);
@@ -132,20 +132,21 @@ returns the \Encoding of the concatenated string, or +nil+ if incompatible:
s1 = "\xa1\xa1".force_encoding('euc-jp') # => "\x{A1A1}"
Encoding.compatible?(s0, s1) # => nil
-=== \String \Encoding
+== \String \Encoding
A Ruby String object has an encoding that is an instance of class \Encoding.
The encoding may be retrieved by method String#encoding.
-The default encoding for a string literal is the script encoding
-(see Encoding@Script+encoding):
+The default encoding for a string literal is the script encoding;
+see {Script Encoding}[rdoc-ref:encodings.rdoc@Script+Encoding].
's'.encoding # => #<Encoding:UTF-8>
The default encoding for a string created with method String.new is:
- For a \String object argument, the encoding of that string.
-- For a string literal, the script encoding (see Encoding@Script+encoding).
+- For a string literal, the script encoding;
+ see {Script Encoding}[rdoc-ref:encodings.rdoc@Script+Encoding].
In either case, any encoding may be specified:
@@ -182,7 +183,7 @@ Here are a couple of useful query methods:
s = "\xc2".force_encoding("UTF-8") # => "\xC2"
s.valid_encoding? # => false
-=== \Symbol and \Regexp Encodings
+== \Symbol and \Regexp Encodings
The string stored in a Symbol or Regexp object also has an encoding;
the encoding may be retrieved by method Symbol#encoding or Regexp#encoding.
@@ -190,22 +191,23 @@ the encoding may be retrieved by method Symbol#encoding or Regexp#encoding.
The default encoding for these, however, is:
- US-ASCII, if all characters are US-ASCII.
-- The script encoding, otherwise (see Encoding@Script+encoding).
+- The script encoding, otherwise;
+ see (Script Encoding)[rdoc-ref:encodings.rdoc@Script+Encoding].
-=== Filesystem \Encoding
+== Filesystem \Encoding
The filesystem encoding is the default \Encoding for a string from the filesystem:
Encoding.find("filesystem") # => #<Encoding:UTF-8>
-=== Locale \Encoding
+== Locale \Encoding
The locale encoding is the default encoding for a string from the environment,
other than from the filesystem:
Encoding.find('locale') # => #<Encoding:IBM437>
-=== Stream Encodings
+== Stream Encodings
Certain stream objects can have two encodings; these objects include instances of:
@@ -220,7 +222,7 @@ The two encodings are:
- An _internal_ _encoding_, which (if not +nil+) specifies the encoding
to be used for the string constructed from the stream.
-==== External \Encoding
+=== External \Encoding
The external encoding, which is an \Encoding object, specifies how bytes read
from the stream are to be interpreted as characters.
@@ -248,7 +250,7 @@ For an \IO, \File, \ARGF, or \StringIO object, the external encoding may be set
- \Methods +set_encoding+ or (except for \ARGF) +set_encoding_by_bom+.
-==== Internal \Encoding
+=== Internal \Encoding
The internal encoding, which is an \Encoding object or +nil+,
specifies how characters read from the stream
@@ -274,7 +276,7 @@ For an \IO, \File, \ARGF, or \StringIO object, the internal encoding may be set
- \Method +set_encoding+.
-=== Script \Encoding
+== Script \Encoding
A Ruby script has a script encoding, which may be retrieved by:
@@ -289,7 +291,7 @@ followed by a colon, space and the Encoding name or alias:
# encoding: ISO-8859-1
__ENCODING__ #=> #<Encoding:ISO-8859-1>
-=== Transcoding
+== Transcoding
_Transcoding_ is the process of changing a sequence of characters
from one encoding to another.
@@ -300,7 +302,7 @@ but the bytes that represent them may change.
The handling for characters that cannot be represented in the destination encoding
may be specified by @Encoding+Options.
-==== Transcoding a \String
+=== Transcoding a \String
Each of these methods transcodes a string:
@@ -315,7 +317,7 @@ Each of these methods transcodes a string:
- String#unicode_normalize!: Like String#unicode_normalize,
but transcodes +self+ in place.
-=== Transcoding a Stream
+== Transcoding a Stream
Each of these methods may transcode a stream;
whether it does so depends on the external and internal encodings:
@@ -350,7 +352,7 @@ Output:
"R\xE9sum\xE9"
"Résumé"
-=== \Encoding Options
+== \Encoding Options
A number of methods in the Ruby core accept keyword arguments as encoding options.
diff --git a/doc/extension.ja.rdoc b/doc/extension.ja.rdoc
index bde907c916..2f7856f3d4 100644
--- a/doc/extension.ja.rdoc
+++ b/doc/extension.ja.rdoc
@@ -780,9 +780,14 @@ RUBY_TYPED_WB_PROTECTED ::
メソッドã®å®Ÿè£…ã«é©åˆ‡ã«ãƒ©ã‚¤ãƒˆãƒãƒªã‚¢ã‚’挿入ã™ã‚‹è²¬ä»»ãŒã‚ã‚Šã¾ã™ï¼Ž
ã•ã‚‚ãªãã°Rubyã¯å®Ÿè¡Œæ™‚ã«ã‚¯ãƒ©ãƒƒã‚·ãƒ¥ã™ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ï¼Ž
- ライトãƒãƒªã‚¢ã«ã¤ã„ã¦ã¯doc/extension.ja.rdocã®Appendix D
- "世代別GC"ã‚‚å‚ç…§ã—ã¦ãã ã•ã„.
+ ライトãƒãƒªã‚¢ã«ã¤ã„ã¦ã¯{世代別
+ GC}[rdoc-ref:@Appendix+D.+-E4-B8-96-E4-BB-A3-E5-88-A5GC]
+ ã‚‚å‚ç…§ã—ã¦ãã ã•ã„.
+ã“ã®ãƒžã‚¯ãƒ­ã¯ä¾‹å¤–を発生ã•ã›ã‚‹å¯èƒ½æ€§ãŒã‚ã‚‹ã“ã¨ã«æ³¨æ„ã—ã¦ãã ã•
+ã„. ラップã•ã‚Œã‚‹ sval ãŒï¼Œè§£æ”¾ã™ã‚‹å¿…è¦ãŒã‚るリソース (割り
+当ã¦ã‚‰ã‚ŒãŸãƒ¡ãƒ¢ãƒªï¼Œå¤–部ライブラリã‹ã‚‰ã®ãƒãƒ³ãƒ‰ãƒ«ãªã©) ã‚’ä¿æŒã—
+ã¦ã„ã‚‹å ´åˆã¯ï¼Œrb_protect を使用ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ï¼Ž
Cã®æ§‹é€ ä½“ã®å‰²å½“ã¨å¯¾å¿œã™ã‚‹ã‚ªãƒ–ジェクトã®ç”Ÿæˆã‚’åŒæ™‚ã«è¡Œã†ãƒžã‚¯
ロã¨ã—ã¦ä»¥ä¸‹ã®ã‚‚ã®ãŒæä¾›ã•ã‚Œã¦ã„ã¾ã™ï¼Ž
@@ -1731,7 +1736,7 @@ have_func(func, header) ::
ックã™ã‚‹ï¼ŽfuncãŒæ¨™æº–ã§ã¯ãƒªãƒ³ã‚¯ã•ã‚Œãªã„ライブラリ内ã®ã‚‚ã®ã§
ã‚る時ã«ã¯å…ˆã«have_libraryã§ãã®ãƒ©ã‚¤ãƒ–ラリをãƒã‚§ãƒƒã‚¯ã—ã¦ãŠ
ã事.ãƒã‚§ãƒƒã‚¯ã«æˆåŠŸã™ã‚‹ã¨ï¼Œãƒ—リプロセッサマクロ
- `HAVE_{FUNC}` を定義ã—,trueã‚’è¿”ã™ï¼Ž
+ <tt>HAVE_{FUNC}</tt> を定義ã—,trueã‚’è¿”ã™ï¼Ž
have_var(var, header) ::
@@ -1739,44 +1744,44 @@ have_var(var, header) ::
クã™ã‚‹ï¼ŽvarãŒæ¨™æº–ã§ã¯ãƒªãƒ³ã‚¯ã•ã‚Œãªã„ライブラリ内ã®ã‚‚ã®ã§ã‚
る時ã«ã¯å…ˆã«have_libraryã§ãã®ãƒ©ã‚¤ãƒ–ラリをãƒã‚§ãƒƒã‚¯ã—ã¦ãŠã
事.ãƒã‚§ãƒƒã‚¯ã«æˆåŠŸã™ã‚‹ã¨ï¼Œãƒ—リプロセッサマクロ
- `HAVE_{VAR}` を定義ã—,trueã‚’è¿”ã™ï¼Ž
+ <tt>HAVE_{VAR}</tt> を定義ã—,trueã‚’è¿”ã™ï¼Ž
have_header(header) ::
ヘッダファイルã®å­˜åœ¨ã‚’ãƒã‚§ãƒƒã‚¯ã™ã‚‹ï¼Žãƒã‚§ãƒƒã‚¯ã«æˆåŠŸã™ã‚‹ã¨ï¼Œ
- プリプロセッサマクロ `HAVE_{HEADER_H}` を定義ã—,trueã‚’è¿”ã™ï¼Ž
+ プリプロセッサマクロ <tt>HAVE_{HEADER_H}</tt> を定義ã—,trueã‚’è¿”ã™ï¼Ž
(スラッシュやドットã¯ã‚¢ãƒ³ãƒ€ãƒ¼ã‚¹ã‚³ã‚¢ã«ç½®æ›ã•ã‚Œã‚‹)
find_header(header, path...) ::
ヘッダファイルheaderã®å­˜åœ¨ã‚’ -Ipath を追加ã—ãªãŒã‚‰ãƒã‚§ãƒƒã‚¯
ã™ã‚‹ï¼Žãƒã‚§ãƒƒã‚¯ã«æˆåŠŸã™ã‚‹ã¨ï¼Œãƒ—リプロセッサマクロ
- `HAVE_{HEADER_H}` を定義ã—,trueã‚’è¿”ã™ï¼Ž
+ <tt>HAVE_{HEADER_H}</tt> を定義ã—,trueã‚’è¿”ã™ï¼Ž
(スラッシュやドットã¯ã‚¢ãƒ³ãƒ€ãƒ¼ã‚¹ã‚³ã‚¢ã«ç½®æ›ã•ã‚Œã‚‹)
have_struct_member(type, member[, header[, opt]]) ::
ヘッダファイルheaderをインクルードã—ã¦åž‹typeãŒå®šç¾©ã•ã‚Œï¼Œ
ãªãŠã‹ã¤ãƒ¡ãƒ³ãƒmemberãŒå­˜åœ¨ã™ã‚‹ã‹ã‚’ãƒã‚§ãƒƒã‚¯ã™ã‚‹ï¼Žãƒã‚§ãƒƒã‚¯ã«
- æˆåŠŸã™ã‚‹ã¨ï¼Œãƒ—リプロセッサマクロ `HAVE_{TYPE}_{MEMBER}` ã‚’
+ æˆåŠŸã™ã‚‹ã¨ï¼Œãƒ—リプロセッサマクロ <tt>HAVE_{TYPE}_{MEMBER}</tt> ã‚’
定義ã—,trueã‚’è¿”ã™ï¼Ž
have_type(type, header, opt) ::
ヘッダファイルheaderをインクルードã—ã¦åž‹typeãŒå­˜åœ¨ã™ã‚‹ã‹ã‚’
ãƒã‚§ãƒƒã‚¯ã™ã‚‹ï¼Žãƒã‚§ãƒƒã‚¯ã«æˆåŠŸã™ã‚‹ã¨ï¼Œãƒ—リプロセッサマクロ
- `HAVE_TYPE_{TYPE}` を定義ã—,trueã‚’è¿”ã™ï¼Ž
+ <tt>HAVE_TYPE_{TYPE}</tt> を定義ã—,trueã‚’è¿”ã™ï¼Ž
check_sizeof(type, header) ::
ヘッダファイルheaderをインクルードã—ã¦åž‹typeã®charå˜ä½ã‚µã‚¤
ズを調ã¹ã‚‹ï¼Žãƒã‚§ãƒƒã‚¯ã«æˆåŠŸã™ã‚‹ã¨ï¼Œãƒ—リプロセッサマクロ
- `SIZEOF_{TYPE}` を定義ã—,ãã®ã‚µã‚¤ã‚ºã‚’è¿”ã™ï¼Žå®šç¾©ã•ã‚Œã¦ã„ãª
+ <tt>SIZEOF_{TYPE}</tt> を定義ã—,ãã®ã‚µã‚¤ã‚ºã‚’è¿”ã™ï¼Žå®šç¾©ã•ã‚Œã¦ã„ãª
ã„ã¨ãã¯nilã‚’è¿”ã™ï¼Ž
-append_cppflags(array-of-flags[, opt])
-append_cflags(array-of-flags[, opt])
-append_ldflags(array-of-flags[, opt])
+append_cppflags(array-of-flags[, opt]) ::
+append_cflags(array-of-flags[, opt]) ::
+append_ldflags(array-of-flags[, opt]) ::
å„flagãŒä½¿ç”¨å¯èƒ½ã§ã‚ã‚Œã°ï¼Œãã‚Œãžã‚Œ$CPPFLAGS, $CFLAGS,
$LDFLAGSã«è¿½åŠ ã™ã‚‹ï¼Žã‚³ãƒ³ãƒ‘イラã®ãƒ•ãƒ©ã‚°ã«ã¯ç§»æ¤æ€§ãŒãªã„ã®ã§ï¼Œ
@@ -1850,8 +1855,9 @@ RGenGCã¯ï¼ŒéŽåŽ»ã®æ‹¡å¼µãƒ©ã‚¤ãƒ–ラリã«ï¼ˆã»ã¼ï¼‰äº’æ›æ€§ã‚’ä¿ã¤ã‚ˆã
スã™ã‚‹ã‚ˆã†ãªã‚³ãƒ¼ãƒ‰ã¯æ›¸ã‹ãªã„よã†ã«ã—ã¦ä¸‹ã•ã„.代ã‚ã‚Šã«ï¼Œrb_ary_aref(),
rb_ary_store() ãªã©ã®ï¼Œé©åˆ‡ãª API 関数を利用ã™ã‚‹ã‚ˆã†ã«ã—ã¦ä¸‹ã•ã„.
-ãã®ã»ã‹ï¼Œå¯¾å¿œã«ã¤ã„ã¦ã®è©³ç´°ã¯ extension.rdoc ã®ã€ŒAppendix D. Generational
-GCã€ã‚’å‚ç…§ã—ã¦ä¸‹ã•ã„.
+ãã®ã»ã‹ï¼Œå¯¾å¿œã«ã¤ã„ã¦ã®è©³ç´°ã¯ {Appendix D. Generational
+GC}[rdoc-ref:extension.rdoc@Appendix+D.+Generational+GC]ã‚’å‚
+ç…§ã—ã¦ä¸‹ã•ã„.
== Appendix E. Ractor サãƒãƒ¼ãƒˆ
@@ -1860,10 +1866,12 @@ Ruby 3.0 ã‹ã‚‰ã€Ruby プログラムを並列ã«å®Ÿè¡Œã™ã‚‹ãŸã‚ã®ä»•çµ„ã¿
ãªã‚Šã¾ã™ã€‚サãƒãƒ¼ãƒˆã—ã¦ã„ãªã„ライブラリã¯ã€ãƒ¡ã‚¤ãƒ³ Ractor 以外ã§å®Ÿè¡Œã™ã‚‹ã¨
エラーã«ãªã‚Šã¾ã™ï¼ˆRactor::UnsafeError)。
-Ractor をサãƒãƒ¼ãƒˆã™ã‚‹ãŸã‚ã®è©³ç´°ã¯ã€extension.rdoc ã®ã€ŒAppendix F. Ractor
-supportã€ã‚’å‚ç…§ã—ã¦ãã ã•ã„。
+Ractor をサãƒãƒ¼ãƒˆã™ã‚‹ãŸã‚ã®è©³ç´°ã¯ã€{Appendix F. Ractor
+support}[rdoc-ref:extension.rdoc@Appendix+F.+Ractor+support]
+ã‚’å‚ç…§ã—ã¦ãã ã•ã„。
-
-:enddoc: Local variables:
-:enddoc: fill-column: 60
-:enddoc: end:
+--
+Local variables:
+fill-column: 60
+end:
+++
diff --git a/doc/extension.rdoc b/doc/extension.rdoc
index 5089272599..ba59d107ab 100644
--- a/doc/extension.rdoc
+++ b/doc/extension.rdoc
@@ -747,18 +747,24 @@ RUBY_TYPED_WB_PROTECTED ::
barriers in all implementations of methods of that object as
appropriate. Otherwise Ruby might crash while running.
- More about write barriers can be found in "Generational GC" in
- Appendix D.
+ More about write barriers can be found in {Generational
+ GC}[rdoc-ref:@Appendix+D.+Generational+GC].
RUBY_TYPED_FROZEN_SHAREABLE ::
- This flag indicates that the object is shareable object
- if the object is frozen. See Appendix F more details.
+ This flag indicates that the object is shareable object if the object
+ is frozen. See {Ractor support}[rdoc-ref:@Appendix+F.+Ractor+support]
+ more details.
If this flag is not set, the object can not become a shareable
object by Ractor.make_shareable() method.
-You can allocate and wrap the structure in one step.
+Note that this macro can raise an exception. If sval to be wrapped
+holds a resource needs to be released (e.g., allocated memory, handle
+from an external library, and etc), you will have to use rb_protect.
+
+You can allocate and wrap the structure in one step, in more
+preferable manner.
TypedData_Make_Struct(klass, type, data_type, sval)
@@ -767,6 +773,10 @@ the structure, which is also allocated. This macro works like:
(sval = ZALLOC(type), TypedData_Wrap_Struct(klass, data_type, sval))
+However, you should use this macro instead of "allocation then wrap"
+like the above code if it is simply allocated, because the latter can
+raise a NoMemoryError and sval will be memory leaked in that case.
+
Arguments klass and data_type work like their counterparts in
TypedData_Wrap_Struct(). A pointer to the allocated structure will
be assigned to sval, which should be a pointer of the type specified.
@@ -779,26 +789,26 @@ approach to marking and reference updating is provided, by declaring offset
references to the VALUES in your struct.
Doing this allows the Ruby GC to support marking these references and GC
-compaction without the need to define the `dmark` and `dcompact` callbacks.
+compaction without the need to define the +dmark+ and +dcompact+ callbacks.
You must define a static list of VALUE pointers to the offsets within your
struct where the references are located, and set the "data" member to point to
-this reference list. The reference list must end with `END_REFS`
+this reference list. The reference list must end with +RUBY_END_REFS+.
Some Macros have been provided to make edge referencing easier:
-* <code>RUBY_TYPED_DECL_MARKING</code> =A flag that can be set on the `ruby_data_type_t` to indicate that references are being declared as edges.
+* <code>RUBY_TYPED_DECL_MARKING</code> =A flag that can be set on the +ruby_data_type_t+ to indicate that references are being declared as edges.
-* <code>RUBY_REFERENCES_START(ref_list_name)</code> - Define `ref_list_name` as a list of references
+* <code>RUBY_REFERENCES(ref_list_name)</code> - Define _ref_list_name_ as a list of references
-* <code>RUBY_REFERENCES_END</code> - Mark the end of the references list. This will take care of terminating the list correctly
+* <code>RUBY_REF_END</code> - The end mark of the references list.
-* <code>RUBY_REF_EDGE\(struct, member\)</code> - Declare `member` as a VALUE edge from `struct`. Use this after `RUBY_REFERENCES_START`
+* <code>RUBY_REF_EDGE(struct, member)</code> - Declare _member_ as a VALUE edge from _struct_. Use this after +RUBY_REFERENCES_START+
-* +REFS_LIST_PTR+ - Coerce the reference list into a format that can be
- accepted by the existing `dmark` interface.
+* +RUBY_REFS_LIST_PTR+ - Coerce the reference list into a format that can be
+ accepted by the existing +dmark+ interface.
-The example below is from `Dir` (defined in `dir.c`)
+The example below is from Dir (defined in +dir.c+)
// The struct being wrapped. Notice this contains 3 members of which the second
// is a VALUE reference to another ruby object.
@@ -808,18 +818,19 @@ The example below is from `Dir` (defined in `dir.c`)
rb_encoding *enc;
}
- // Define a reference list `dir_refs` containing a single entry to `path`, and
- // terminating with RUBY_REF_END
- RUBY_REFERENCES_START(dir_refs)
- REF_EDGE(dir_data, path),
- RUBY_REFERENCES_END
+ // Define a reference list `dir_refs` containing a single entry to `path`.
+ // Needs terminating with RUBY_REF_END
+ RUBY_REFERENCES(dir_refs) = {
+ RUBY_REF_EDGE(dir_data, path),
+ RUBY_REF_END
+ };
// Override the "dmark" field with the defined reference list now that we
// no longer need a marking callback and add RUBY_TYPED_DECL_MARKING to the
// flags field
static const rb_data_type_t dir_data_type = {
"dir",
- {REFS_LIST_PTR(dir_refs), dir_free, dir_memsize,},
+ {RUBY_REFS_LIST_PTR(dir_refs), dir_free, dir_memsize,},
0, NULL, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_DECL_MARKING
};
@@ -2208,70 +2219,72 @@ Ractor safety around C extensions has the following properties:
To make a "Ractor-safe" C extension, we need to check the following points:
-(1) Do not share unshareable objects between ractors
+1. Do not share unshareable objects between ractors
-For example, C's global variable can lead sharing an unshareable objects
-between ractors.
+ For example, C's global variable can lead sharing an unshareable objects
+ between ractors.
- VALUE g_var;
- VALUE set(VALUE self, VALUE v){ return g_var = v; }
- VALUE get(VALUE self){ return g_var; }
+ VALUE g_var;
+ VALUE set(VALUE self, VALUE v){ return g_var = v; }
+ VALUE get(VALUE self){ return g_var; }
-set() and get() pair can share an unshareable objects using g_var, and
-it is Ractor-unsafe.
+ set() and get() pair can share an unshareable objects using g_var, and
+ it is Ractor-unsafe.
-Not only using global variables directly, some indirect data structure
-such as global st_table can share the objects, so please take care.
+ Not only using global variables directly, some indirect data structure
+ such as global st_table can share the objects, so please take care.
-Note that class and module objects are shareable objects, so you can
-keep the code "cFoo = rb_define_class(...)" with C's global variables.
+ Note that class and module objects are shareable objects, so you can
+ keep the code "cFoo = rb_define_class(...)" with C's global variables.
-(2) Check the thread-safety of the extension
+2. Check the thread-safety of the extension
-An extension should be thread-safe. For example, the following code is
-not thread-safe:
+ An extension should be thread-safe. For example, the following code is
+ not thread-safe:
- bool g_called = false;
- VALUE call(VALUE self) {
- if (g_called) rb_raise("recursive call is not allowed.");
- g_called = true;
- VALUE ret = do_something();
- g_called = false;
- return ret;
- }
+ bool g_called = false;
+ VALUE call(VALUE self) {
+ if (g_called) rb_raise("recursive call is not allowed.");
+ g_called = true;
+ VALUE ret = do_something();
+ g_called = false;
+ return ret;
+ }
-because g_called global variable should be synchronized by other
-ractor's threads. To avoid such data-race, some synchronization should
-be used. Check include/ruby/thread_native.h and include/ruby/atomic.h.
+ because g_called global variable should be synchronized by other
+ ractor's threads. To avoid such data-race, some synchronization should
+ be used. Check include/ruby/thread_native.h and include/ruby/atomic.h.
-With Ractors, all objects given as method parameters and the receiver (self)
-are guaranteed to be from the current Ractor or to be shareable. As a
-consequence, it is easier to make code ractor-safe than to make code generally
-thread-safe. For example, we don't need to lock an array object to access the
-element of it.
+ With Ractors, all objects given as method parameters and the receiver (self)
+ are guaranteed to be from the current Ractor or to be shareable. As a
+ consequence, it is easier to make code ractor-safe than to make code generally
+ thread-safe. For example, we don't need to lock an array object to access the
+ element of it.
-(3) Check the thread-safety of any used library
+3. Check the thread-safety of any used library
-If the extension relies on an external library, such as a function foo() from
-a library libfoo, the function libfoo foo() should be thread safe.
+ If the extension relies on an external library, such as a function foo() from
+ a library libfoo, the function libfoo foo() should be thread safe.
-(4) Make an object shareable
+4. Make an object shareable
-This is not required to make an extension Ractor-safe.
+ This is not required to make an extension Ractor-safe.
-If an extension provides special objects defined by rb_data_type_t,
-consider these objects can become shareable or not.
+ If an extension provides special objects defined by rb_data_type_t,
+ consider these objects can become shareable or not.
-RUBY_TYPED_FROZEN_SHAREABLE flag indicates that these objects can be
-shareable objects if the object is frozen. This means that if the object
-is frozen, the mutation of wrapped data is not allowed.
+ RUBY_TYPED_FROZEN_SHAREABLE flag indicates that these objects can be
+ shareable objects if the object is frozen. This means that if the object
+ is frozen, the mutation of wrapped data is not allowed.
-(5) Others
+5. Others
-There are possibly other points or requirements which must be considered in the
-making of a Ractor-safe extension. This document will be extended as they are
-discovered.
+ There are possibly other points or requirements which must be considered in the
+ making of a Ractor-safe extension. This document will be extended as they are
+ discovered.
-:enddoc: Local variables:
-:enddoc: fill-column: 70
-:enddoc: end:
+--
+Local variables:
+fill-column: 70
+end:
+++
diff --git a/doc/format_specifications.rdoc b/doc/format_specifications.rdoc
index e589524f27..1111575e74 100644
--- a/doc/format_specifications.rdoc
+++ b/doc/format_specifications.rdoc
@@ -1,4 +1,4 @@
-== Format Specifications
+= Format Specifications
Several Ruby core classes have instance method +printf+ or +sprintf+:
@@ -37,12 +37,12 @@ It consists of:
Except for the leading percent character,
the only required part is the type specifier, so we begin with that.
-=== Type Specifiers
+== Type Specifiers
This section provides a brief explanation of each type specifier.
The links lead to the details and examples.
-==== \Integer Type Specifiers
+=== \Integer Type Specifiers
- +b+ or +B+: Format +argument+ as a binary integer.
See {Specifiers b and B}[rdoc-ref:format_specifications.rdoc@Specifiers+b+and+B].
@@ -54,7 +54,7 @@ The links lead to the details and examples.
- +x+ or +X+: Format +argument+ as a hexadecimal integer.
See {Specifiers x and X}[rdoc-ref:format_specifications.rdoc@Specifiers+x+and+X].
-==== Floating-Point Type Specifiers
+=== Floating-Point Type Specifiers
- +a+ or +A+: Format +argument+ as hexadecimal floating-point number.
See {Specifiers a and A}[rdoc-ref:format_specifications.rdoc@Specifiers+a+and+A].
@@ -65,7 +65,7 @@ The links lead to the details and examples.
- +g+ or +G+: Format +argument+ in a "general" format.
See {Specifiers g and G}[rdoc-ref:format_specifications.rdoc@Specifiers+g+and+G].
-==== Other Type Specifiers
+=== Other Type Specifiers
- +c+: Format +argument+ as a character.
See {Specifier c}[rdoc-ref:format_specifications.rdoc@Specifier+c].
@@ -76,7 +76,7 @@ The links lead to the details and examples.
- <tt>%</tt>: Format +argument+ (<tt>'%'</tt>) as a single percent character.
See {Specifier %}[rdoc-ref:format_specifications.rdoc@Specifier+-25].
-=== Flags
+== Flags
The effect of a flag may vary greatly among type specifiers.
These remarks are general in nature.
@@ -85,7 +85,7 @@ See {type-specific details}[rdoc-ref:format_specifications.rdoc@Type+Specifier+D
Multiple flags may be given with single type specifier;
order does not matter.
-==== <tt>' '</tt> Flag
+=== <tt>' '</tt> Flag
Insert a space before a non-negative number:
@@ -97,49 +97,49 @@ Insert a minus sign for negative value:
sprintf('%d', -10) # => "-10"
sprintf('% d', -10) # => "-10"
-==== <tt>'#'</tt> Flag
+=== <tt>'#'</tt> Flag
Use an alternate format; varies among types:
sprintf('%x', 100) # => "64"
sprintf('%#x', 100) # => "0x64"
-==== <tt>'+'</tt> Flag
+=== <tt>'+'</tt> Flag
Add a leading plus sign for a non-negative number:
sprintf('%x', 100) # => "64"
sprintf('%+x', 100) # => "+64"
-==== <tt>'-'</tt> Flag
+=== <tt>'-'</tt> Flag
Left justify the value in its field:
sprintf('%6d', 100) # => " 100"
sprintf('%-6d', 100) # => "100 "
-==== <tt>'0'</tt> Flag
+=== <tt>'0'</tt> Flag
Left-pad with zeros instead of spaces:
sprintf('%6d', 100) # => " 100"
sprintf('%06d', 100) # => "000100"
-==== <tt>'*'</tt> Flag
+=== <tt>'*'</tt> Flag
Use the next argument as the field width:
sprintf('%d', 20, 14) # => "20"
sprintf('%*d', 20, 14) # => " 14"
-==== <tt>'n$'</tt> Flag
+=== <tt>'n$'</tt> Flag
Format the (1-based) <tt>n</tt>th argument into this field:
sprintf("%s %s", 'world', 'hello') # => "world hello"
sprintf("%2$s %1$s", 'world', 'hello') # => "hello world"
-=== Width Specifier
+== Width Specifier
In general, a width specifier determines the minimum width (in characters)
of the formatted field:
@@ -152,7 +152,7 @@ of the formatted field:
# Ignore if too small.
sprintf('%1d', 100) # => "100"
-=== Precision Specifier
+== Precision Specifier
A precision specifier is a decimal point followed by zero or more
decimal digits.
@@ -194,9 +194,9 @@ the number of characters to write:
sprintf('%s', Time.now) # => "2022-05-04 11:59:16 -0400"
sprintf('%.10s', Time.now) # => "2022-05-04"
-=== Type Specifier Details and Examples
+== Type Specifier Details and Examples
-==== Specifiers +a+ and +A+
+=== Specifiers +a+ and +A+
Format +argument+ as hexadecimal floating-point number:
@@ -209,7 +209,7 @@ Format +argument+ as hexadecimal floating-point number:
sprintf('%A', 4096) # => "0X1P+12"
sprintf('%A', -4096) # => "-0X1P+12"
-==== Specifiers +b+ and +B+
+=== Specifiers +b+ and +B+
The two specifiers +b+ and +B+ behave identically
except when flag <tt>'#'</tt>+ is used.
@@ -226,14 +226,14 @@ Format +argument+ as a binary integer:
sprintf('%#b', 4) # => "0b100"
sprintf('%#B', 4) # => "0B100"
-==== Specifier +c+
+=== Specifier +c+
Format +argument+ as a single character:
sprintf('%c', 'A') # => "A"
sprintf('%c', 65) # => "A"
-==== Specifier +d+
+=== Specifier +d+
Format +argument+ as a decimal integer:
@@ -242,7 +242,7 @@ Format +argument+ as a decimal integer:
Flag <tt>'#'</tt> does not apply.
-==== Specifiers +e+ and +E+
+=== Specifiers +e+ and +E+
Format +argument+ in
{scientific notation}[https://en.wikipedia.org/wiki/Scientific_notation]:
@@ -250,7 +250,7 @@ Format +argument+ in
sprintf('%e', 3.14159) # => "3.141590e+00"
sprintf('%E', -3.14159) # => "-3.141590E+00"
-==== Specifier +f+
+=== Specifier +f+
Format +argument+ as a floating-point number:
@@ -259,7 +259,7 @@ Format +argument+ as a floating-point number:
Flag <tt>'#'</tt> does not apply.
-==== Specifiers +g+ and +G+
+=== Specifiers +g+ and +G+
Format +argument+ using exponential form (+e+/+E+ specifier)
if the exponent is less than -4 or greater than or equal to the precision.
@@ -281,7 +281,7 @@ Otherwise format +argument+ using floating-point form (+f+ specifier):
sprintf('%#G', 100000000000) # => "1.00000E+11"
sprintf('%#G', 0.000000000001) # => "1.00000E-12"
-==== Specifier +o+
+=== Specifier +o+
Format +argument+ as an octal integer.
If +argument+ is negative, it will be formatted as a two's complement
@@ -296,14 +296,14 @@ prefixed with +..7+:
sprintf('%#o', 16) # => "020"
sprintf('%#o', -16) # => "..760"
-==== Specifier +p+
+=== Specifier +p+
Format +argument+ as a string via <tt>argument.inspect</tt>:
t = Time.now
sprintf('%p', t) # => "2022-05-01 13:42:07.1645683 -0500"
-==== Specifier +s+
+=== Specifier +s+
Format +argument+ as a string via <tt>argument.to_s</tt>:
@@ -312,7 +312,7 @@ Format +argument+ as a string via <tt>argument.to_s</tt>:
Flag <tt>'#'</tt> does not apply.
-==== Specifiers +x+ and +X+
+=== Specifiers +x+ and +X+
Format +argument+ as a hexadecimal integer.
If +argument+ is negative, it will be formatted as a two's complement
@@ -329,7 +329,7 @@ prefixed with +..f+:
# Alternate format for negative value.
sprintf('%#x', -100) # => "0x..f9c"
-==== Specifier <tt>%</tt>
+=== Specifier <tt>%</tt>
Format +argument+ (<tt>'%'</tt>) as a single percent character:
@@ -337,7 +337,7 @@ Format +argument+ (<tt>'%'</tt>) as a single percent character:
Flags do not apply.
-=== Reference by Name
+== Reference by Name
For more complex formatting, Ruby supports a reference by name.
%<name>s style uses format style, but %{name} style doesn't.
diff --git a/doc/globals.rdoc b/doc/globals.rdoc
index 1d7cda69f9..1b51bb1b36 100644
--- a/doc/globals.rdoc
+++ b/doc/globals.rdoc
@@ -1,69 +1,422 @@
-# -*- mode: rdoc; coding: utf-8; fill-column: 74; -*-
-
-== Pre-defined global variables
-
-$!:: The Exception object set by Kernel#raise.
-$@:: The same as <code>$!.backtrace</code>.
-$~:: The information about the last match in the current scope (thread-local and frame-local).
-$&:: The string matched by the last successful match.
-$`:: The string to the left of the last successful match.
-$':: The string to the right of the last successful match.
-$+:: The highest group matched by the last successful match.
-$1:: The Nth group of the last successful match. May be > 1.
-$=:: This variable is no longer effective. Deprecated.
-$/:: The input record separator, newline by default. Aliased to $-0.
-$\:: The output record separator for Kernel#print and IO#write. Default is +nil+.
-$,:: The output field separator for Kernel#print and Array#join. Non-nil $, will be deprecated.
-$;:: The default separator for String#split. Non-nil $; will be deprecated. Aliased to $-F.
-$.:: The current input line number of the last file that was read.
-$<:: The same as ARGF.
-$>:: The default output stream for Kernel#print and Kernel#printf. $stdout by default.
-$_:: The last input line of string by gets or readline.
-$0:: Contains the name of the script being executed. May be assignable.
-$*:: The same as ARGV.
-$$:: The process number of the Ruby running this script. Same as Process.pid.
-$?:: The status of the last executed child process (thread-local).
-$LOAD_PATH:: Load path for searching Ruby scripts and extension libraries used
- by Kernel#load and Kernel#require. Aliased to $: and $-I.
- Has a singleton method <code>$LOAD_PATH.resolve_feature_path(feature)</code>
- that returns [+:rb+ or +:so+, path], which resolves the feature to
- the path the original Kernel#require method would load.
-$LOADED_FEATURES:: The array contains the module names loaded by require.
- Aliased to $".
-$DEBUG:: The debug flag, which is set by the <tt>-d</tt> switch. Enabling debug
- output prints each exception raised to $stderr (but not its
- backtrace). Setting this to a true value enables debug output as
- if <tt>-d</tt> were given on the command line. Setting this to a false
- value disables debug output. Aliased to $-d.
-$FILENAME:: Current input filename from ARGF. Same as ARGF.filename.
-$stderr:: The current standard error output.
-$stdin:: The current standard input.
-$stdout:: The current standard output.
-$VERBOSE:: The verbose flag, which is set by the <tt>-w</tt> or <tt>-v</tt> switch.
- Setting this to a true value enables warnings as if <tt>-w</tt> or <tt>-v</tt> were given
- on the command line. Setting this to +nil+ disables warnings,
- including from Kernel#warn. Aliased to $-v and $-w.
-$-a:: True if option <tt>-a</tt> is set. Read-only variable.
-$-i:: In in-place-edit mode, this variable holds the extension, otherwise +nil+.
-$-l:: True if option <tt>-l</tt> is set. Read-only variable.
-$-p:: True if option <tt>-p</tt> is set. Read-only variable.
-
-== Pre-defined global constants
-
-STDIN:: The standard input. The default value for $stdin.
-STDOUT:: The standard output. The default value for $stdout.
-STDERR:: The standard error output. The default value for $stderr.
-ENV:: The hash contains current environment variables.
-ARGF:: The virtual concatenation of the files given on command line (or from $stdin if no files were given).
-ARGV:: An Array of command line arguments given for the script.
-DATA:: The file object of the script, pointing just after <code>__END__</code>.
-TOPLEVEL_BINDING:: The Binding of the top level scope.
-RUBY_VERSION:: The Ruby language version.
-RUBY_RELEASE_DATE:: The release date string.
-RUBY_PLATFORM:: The platform identifier.
-RUBY_PATCHLEVEL:: The patchlevel for this Ruby. If this is a development build of Ruby the patchlevel will be -1.
-RUBY_REVISION:: The GIT commit hash for this Ruby.
-RUBY_COPYRIGHT:: The copyright string for Ruby.
-RUBY_ENGINE:: The name of the Ruby implementation.
-RUBY_ENGINE_VERSION:: The version of the Ruby implementation.
-RUBY_DESCRIPTION:: The same as <tt>ruby --version</tt>, a String describing various aspects of the Ruby implementation.
+= Pre-Defined Global Variables
+
+Some of the pre-defined global variables have synonyms
+that are available via module English.
+For each of those, the \English synonym is given.
+
+To use the module:
+
+ require 'English'
+
+== Exceptions
+
+=== <tt>$!</tt> (\Exception)
+
+Contains the Exception object set by Kernel#raise:
+
+ begin
+ raise RuntimeError.new('Boo!')
+ rescue RuntimeError
+ p $!
+ end
+
+Output:
+
+ #<RuntimeError: Boo!>
+
+English - <tt>$ERROR_INFO</tt>
+
+=== <tt>$@</tt> (Backtrace)
+
+Same as <tt>$!.backtrace</tt>;
+returns an array of backtrace positions:
+
+ begin
+ raise RuntimeError.new('Boo!')
+ rescue RuntimeError
+ pp $@.take(4)
+ end
+
+Output:
+
+ ["(irb):338:in `<top (required)>'",
+ "/snap/ruby/317/lib/ruby/3.2.0/irb/workspace.rb:119:in `eval'",
+ "/snap/ruby/317/lib/ruby/3.2.0/irb/workspace.rb:119:in `evaluate'",
+ "/snap/ruby/317/lib/ruby/3.2.0/irb/context.rb:502:in `evaluate'"]
+
+English - <tt>$ERROR_POSITION</tt>.
+
+== Pattern Matching
+
+These global variables store information about the most recent
+successful match in the current scope.
+
+For details and examples,
+see {Regexp Global Variables}[rdoc-ref:Regexp@Global+Variables].
+
+=== <tt>$~</tt> (\MatchData)
+
+MatchData object created from the match;
+thread-local and frame-local.
+
+English - <tt>$LAST_MATCH_INFO</tt>.
+
+=== <tt>$&</tt> (Matched Substring)
+
+The matched string.
+
+English - <tt>$MATCH</tt>.
+
+=== <tt>$`</tt> (Pre-Match Substring)
+
+The string to the left of the match.
+
+English - <tt>$PREMATCH</tt>.
+
+=== <tt>$'</tt> (Post-Match Substring)
+
+The string to the right of the match.
+
+English - <tt>$POSTMATCH</tt>.
+
+=== <tt>$+</tt> (Last Matched Group)
+
+The last group matched.
+
+English - <tt>$LAST_PAREN_MATCH</tt>.
+
+=== <tt>$1</tt>, <tt>$2</tt>, \Etc. (Matched Group)
+
+For <tt>$_n_</tt> the _nth_ group of the match.
+
+No \English.
+
+== Separators
+
+=== <tt>$/</tt> (Input Record Separator)
+
+An input record separator, initially newline.
+
+English - <tt>$INPUT_RECORD_SEPARATOR</tt>, <tt>$RS</tt>.
+
+Aliased as <tt>$-0</tt>.
+
+=== <tt>$;</tt> (Input Field Separator)
+
+An input field separator, initially +nil+.
+
+English - <tt>$FIELD_SEPARATOR</tt>, <tt>$FS</tt>.
+
+Aliased as <tt>$-F</tt>.
+
+=== <tt>$\\</tt> (Output Record Separator)
+
+An output record separator, initially +nil+.
+
+English - <tt>$OUTPUT_RECORD_SEPARATOR</tt>, <tt>$ORS</tt>.
+
+== Streams
+
+=== <tt>$stdin</tt> (Standard Input)
+
+The current standard input stream; initially:
+
+ $stdin # => #<IO:<STDIN>>
+
+=== <tt>$stdout</tt> (Standard Output)
+
+The current standard output stream; initially:
+
+ $stdout # => #<IO:<STDOUT>>
+
+=== <tt>$stderr</tt> (Standard Error)
+
+The current standard error stream; initially:
+
+ $stderr # => #<IO:<STDERR>>
+
+=== <tt>$<</tt> (\ARGF or $stdin)
+
+Points to stream ARGF if not empty, else to stream $stdin; read-only.
+
+English - <tt>$DEFAULT_INPUT</tt>.
+
+=== <tt>$></tt> (Default Standard Output)
+
+An output stream, initially <tt>$stdout</tt>.
+
+English - <tt>$DEFAULT_OUTPUT
+
+=== <tt>$.</tt> (Input Position)
+
+The input position (line number) in the most recently read stream.
+
+English - <tt>$INPUT_LINE_NUMBER</tt>, <tt>$NR</tt>
+
+=== <tt>$_</tt> (Last Read Line)
+
+The line (string) from the most recently read stream.
+
+English - <tt>$LAST_READ_LINE</tt>.
+
+== Processes
+
+=== <tt>$0</tt>
+
+Initially, contains the name of the script being executed;
+may be reassigned.
+
+=== <tt>$*</tt> (\ARGV)
+
+Points to ARGV.
+
+English - <tt>$ARGV</tt>.
+
+=== <tt>$$</tt> (Process ID)
+
+The process ID of the current process. Same as Process.pid.
+
+English - <tt>$PROCESS_ID</tt>, <tt>$PID</tt>.
+
+=== <tt>$?</tt> (Child Status)
+
+Initially +nil+, otherwise the Process::Status object
+created for the most-recently exited child process;
+thread-local.
+
+English - <tt>$CHILD_STATUS</tt>.
+
+=== <tt>$LOAD_PATH</tt> (Load Path)
+
+Contains the array of paths to be searched
+by Kernel#load and Kernel#require.
+
+Singleton method <tt>$LOAD_PATH.resolve_feature_path(feature)</tt>
+returns:
+
+- <tt>[:rb, _path_]</tt>, where +path+ is the path to the Ruby file
+ to be loaded for the given +feature+.
+- <tt>[:so+ _path_]</tt>, where +path+ is the path to the shared object file
+ to be loaded for the given +feature+.
+- +nil+ if there is no such +feature+ and +path+.
+
+Examples:
+
+ $LOAD_PATH.resolve_feature_path('timeout')
+ # => [:rb, "/snap/ruby/317/lib/ruby/3.2.0/timeout.rb"]
+ $LOAD_PATH.resolve_feature_path('date_core')
+ # => [:so, "/snap/ruby/317/lib/ruby/3.2.0/x86_64-linux/date_core.so"]
+ $LOAD_PATH.resolve_feature_path('foo')
+ # => nil
+
+Aliased as <tt>$:</tt> and <tt>$-I</tt>.
+
+=== <tt>$LOADED_FEATURES</tt>
+
+Contains an array of the paths to the loaded files:
+
+ $LOADED_FEATURES.take(10)
+ # =>
+ ["enumerator.so",
+ "thread.rb",
+ "fiber.so",
+ "rational.so",
+ "complex.so",
+ "ruby2_keywords.rb",
+ "/snap/ruby/317/lib/ruby/3.2.0/x86_64-linux/enc/encdb.so",
+ "/snap/ruby/317/lib/ruby/3.2.0/x86_64-linux/enc/trans/transdb.so",
+ "/snap/ruby/317/lib/ruby/3.2.0/x86_64-linux/rbconfig.rb",
+ "/snap/ruby/317/lib/ruby/3.2.0/rubygems/compatibility.rb"]
+
+Aliased as <tt>$"</tt>.
+
+== Debugging
+
+=== <tt>$FILENAME</tt>
+
+The value returned by method ARGF.filename.
+
+=== <tt>$DEBUG</tt>
+
+Initially +true+ if command-line option <tt>-d</tt> or <tt>--debug</tt> is given,
+otherwise initially +false+;
+may be set to either value in the running program.
+
+When +true+, prints each raised exception to <tt>$stderr</tt>.
+
+Aliased as <tt>$-d</tt>.
+
+=== <tt>$VERBOSE</tt>
+
+Initially +true+ if command-line option <tt>-v</tt> or <tt>-w</tt> is given,
+otherwise initially +false+;
+may be set to either value, or to +nil+, in the running program.
+
+When +true+, enables Ruby warnings.
+
+When +nil+, disables warnings, including those from Kernel#warn.
+
+Aliased as <tt>$-v</tt> and <tt>$-w</tt>.
+
+== Other Variables
+
+=== <tt>$-a</tt>
+
+Whether command-line option <tt>-a</tt> was given; read-only.
+
+=== <tt>$-i</tt>
+
+Contains the extension given with command-line option <tt>-i</tt>,
+or +nil+ if none.
+
+An alias of ARGF.inplace_mode.
+
+=== <tt>$-l</tt>
+
+Whether command-line option <tt>-l</tt> was set; read-only.
+
+=== <tt>$-p</tt>
+
+Whether command-line option <tt>-p</tt> was given; read-only.
+
+== Deprecated
+
+=== <tt>$=</tt>
+
+=== <tt>$,</tt>
+
+= Pre-Defined Global Constants
+
+= Streams
+
+=== <tt>STDIN</tt>
+
+The standard input stream (the default value for <tt>$stdin</tt>):
+
+ STDIN # => #<IO:<STDIN>>
+
+=== <tt>STDOUT</tt>
+
+The standard output stream (the default value for <tt>$stdout</tt>):
+
+ STDOUT # => #<IO:<STDOUT>>
+
+=== <tt>STDERR</tt>
+
+The standard error stream (the default value for <tt>$stderr</tt>):
+
+ STDERR # => #<IO:<STDERR>>
+
+== Environment
+
+=== ENV
+
+A hash of the contains current environment variables names and values:
+
+ ENV.take(5)
+ # =>
+ [["COLORTERM", "truecolor"],
+ ["DBUS_SESSION_BUS_ADDRESS", "unix:path=/run/user/1000/bus"],
+ ["DESKTOP_SESSION", "ubuntu"],
+ ["DISPLAY", ":0"],
+ ["GDMSESSION", "ubuntu"]]
+
+=== ARGF
+
+The virtual concatenation of the files given on the command line, or from
+<tt>$stdin</tt> if no files were given, <tt>"-"</tt> is given, or after
+all files have been read.
+
+=== <tt>ARGV</tt>
+
+An array of the given command-line arguments.
+
+=== <tt>TOPLEVEL_BINDING</tt>
+
+The Binding of the top level scope:
+
+ TOPLEVEL_BINDING # => #<Binding:0x00007f58da0da7c0>
+
+=== <tt>RUBY_VERSION</tt>
+
+The Ruby version:
+
+ RUBY_VERSION # => "3.2.2"
+
+=== <tt>RUBY_RELEASE_DATE</tt>
+
+The release date string:
+
+ RUBY_RELEASE_DATE # => "2023-03-30"
+
+=== <tt>RUBY_PLATFORM</tt>
+
+The platform identifier:
+
+ RUBY_PLATFORM # => "x86_64-linux"
+
+=== <tt>RUBY_PATCHLEVEL</tt>
+
+The integer patch level for this Ruby:
+
+ RUBY_PATCHLEVEL # => 53
+
+For a development build the patch level will be -1.
+
+=== <tt>RUBY_REVISION</tt>
+
+The git commit hash for this Ruby:
+
+ RUBY_REVISION # => "e51014f9c05aa65cbf203442d37fef7c12390015"
+
+=== <tt>RUBY_COPYRIGHT</tt>
+
+The copyright string:
+
+ RUBY_COPYRIGHT
+ # => "ruby - Copyright (C) 1993-2023 Yukihiro Matsumoto"
+
+=== <tt>RUBY_ENGINE</tt>
+
+The name of the Ruby implementation:
+
+ RUBY_ENGINE # => "ruby"
+
+=== <tt>RUBY_ENGINE_VERSION</tt>
+
+The version of the Ruby implementation:
+
+ RUBY_ENGINE_VERSION # => "3.2.2"
+
+=== <tt>RUBY_DESCRIPTION</tt>
+
+The description of the Ruby implementation:
+
+ RUBY_DESCRIPTION
+ # => "ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x86_64-linux]"
+
+== Embedded \Data
+
+=== <tt>DATA</tt>
+
+Defined if and only if the program has this line:
+
+ __END__
+
+When defined, <tt>DATA</tt> is a File object
+containing the lines following the <tt>__END__</tt>,
+positioned at the first of those lines:
+
+ p DATA
+ DATA.each_line { |line| p line }
+ __END__
+ Foo
+ Bar
+ Baz
+
+Output:
+
+ #<File:t.rb>
+ "Foo\n"
+ "Bar\n"
+ "Baz\n"
diff --git a/doc/implicit_conversion.rdoc b/doc/implicit_conversion.rdoc
index ba15fa4bf4..e244096125 100644
--- a/doc/implicit_conversion.rdoc
+++ b/doc/implicit_conversion.rdoc
@@ -1,4 +1,4 @@
-== Implicit Conversions
+= Implicit Conversions
Some Ruby methods accept one or more objects
that can be either:
@@ -15,7 +15,7 @@ a specific conversion method:
* Integer: +to_int+
* String: +to_str+
-=== Array-Convertible Objects
+== Array-Convertible Objects
An <i>Array-convertible object</i> is an object that:
@@ -69,7 +69,7 @@ This class is not Array-convertible (method +to_ary+ returns non-Array):
# Raises TypeError (can't convert NotArrayConvertible to Array (NotArrayConvertible#to_ary gives Symbol))
a.replace(NotArrayConvertible.new)
-=== Hash-Convertible Objects
+== Hash-Convertible Objects
A <i>Hash-convertible object</i> is an object that:
@@ -123,7 +123,7 @@ This class is not Hash-convertible (method +to_hash+ returns non-Hash):
# Raises TypeError (can't convert NotHashConvertible to Hash (ToHashReturnsNonHash#to_hash gives Symbol))
h.merge(NotHashConvertible.new)
-=== Integer-Convertible Objects
+== Integer-Convertible Objects
An <i>Integer-convertible object</i> is an object that:
@@ -171,7 +171,7 @@ This class is not Integer-convertible (method +to_int+ returns non-Integer):
# Raises TypeError (can't convert NotIntegerConvertible to Integer (NotIntegerConvertible#to_int gives Symbol))
Array.new(NotIntegerConvertible.new)
-=== String-Convertible Objects
+== String-Convertible Objects
A <i>String-convertible object</i> is an object that:
* Has instance method +to_str+.
diff --git a/doc/irb/indexes.md b/doc/irb/indexes.md
new file mode 100644
index 0000000000..24a67b9698
--- /dev/null
+++ b/doc/irb/indexes.md
@@ -0,0 +1,189 @@
+## Indexes
+
+### Index of Command-Line Options
+
+These are the \IRB command-line options, with links to explanatory text:
+
+- `-d`: Set `$DEBUG` and {$VERBOSE}[rdoc-ref:IRB@Verbosity]
+ to `true`.
+- `-E _ex_[:_in_]`: Set initial external (ex) and internal (in)
+ {encodings}[rdoc-ref:IRB@Encodings] (same as `ruby -E>`).
+- `-f`: Don't initialize from {configuration file}[rdoc-ref:IRB@Configuration+File].
+- `-I _dirpath_`: Specify {$LOAD_PATH directory}[rdoc-ref:IRB@Load+Modules]
+ (same as `ruby -I`).
+- `-r _load-module_`: Require {load-module}[rdoc-ref:IRB@Load+Modules]
+ (same as `ruby -r`).
+- `-U`: Set external and internal {encodings}[rdoc-ref:IRB@Encodings] to UTF-8.
+- `-w`: Suppress {warnings}[rdoc-ref:IRB@Warnings] (same as `ruby -w`).
+- `-W[_level_]`: Set {warning level}[rdoc-ref:IRB@Warnings];
+ 0=silence, 1=medium, 2=verbose (same as `ruby -W`).
+- `--autocomplete`: Use {auto-completion}[rdoc-ref:IRB@Automatic+Completion].
+- `--back-trace-limit _n_`: Set a {backtrace limit}[rdoc-ref:IRB@Tracer];
+ display at most the top `n` and bottom `n` entries.
+- `--colorize`: Use {color-highlighting}[rdoc-ref:IRB@Color+Highlighting]
+ for input and output.
+- `--context-mode _n_`: Select method to create Binding object
+ for new {workspace}[rdoc-ref:IRB@Commands]; `n` in range `0..4`.
+- `--echo`: Print ({echo}[rdoc-ref:IRB@Return-Value+Printing+-28Echoing-29])
+ return values.
+- `--extra-doc-dir _dirpath_`:
+ Add a {documentation directory}[rdoc-ref:IRB@RI+Documentation+Directories]
+ for the documentation dialog.
+- `--inf-ruby-mode`: Set prompt mode to {:INF_RUBY}[rdoc-ref:IRB@Pre-Defined+Prompts]
+ (appropriate for `inf-ruby-mode` on Emacs);
+ suppresses --multiline and --singleline.
+- `--inspect`: Use method `inspect` for printing ({echoing}[rdoc-ref:IRB@Return-Value+Printing+-28Echoing-29])
+ return values.
+- `--multiline`: Use the multiline editor as the {input method}[rdoc-ref:IRB@Input+Method].
+- `--noautocomplete`: Don't use {auto-completion}[rdoc-ref:IRB@Automatic+Completion].
+- `--nocolorize`: Don't use {color-highlighting}[rdoc-ref:IRB@Color+Highlighting]
+ for input and output.
+- `--noecho`: Don't print ({echo}[rdoc-ref:IRB@Return-Value+Printing+-28Echoing-29])
+ return values.
+- `--noecho-on-assignment`: Don't print ({echo}[rdoc-ref:IRB@Return-Value+Printing+-28Echoing-29])
+ result on assignment.
+- `--noinspect`: Don't se method `inspect` for printing ({echoing}[rdoc-ref:IRB@Return-Value+Printing+-28Echoing-29])
+ return values.
+- `--nomultiline`: Don't use the multiline editor as the {input method}[rdoc-ref:IRB@Input+Method].
+- `--noprompt`: Don't print {prompts}[rdoc-ref:IRB@Prompt+and+Return+Formats].
+- `--noscript`: Treat the first command-line argument as a normal
+ {command-line argument}[rdoc-ref:IRB@Initialization+Script],
+ and include it in `ARGV`.
+- `--nosingleline`: Don't use the singleline editor as the {input method}[rdoc-ref:IRB@Input+Method].
+- `--noverbose`Don't print {verbose}[rdoc-ref:IRB@Verbosity] details.
+- `--prompt _mode_`, `--prompt-mode _mode_`:
+ Set {prompt and return formats}[rdoc-ref:IRB@Prompt+and+Return+Formats];
+ `mode` may be a {pre-defined prompt}[rdoc-ref:IRB@Pre-Defined+Prompts]
+ or the name of a {custom prompt}[rdoc-ref:IRB@Custom+Prompts].
+- `--script`: Treat the first command-line argument as the path to an
+ {initialization script}[rdoc-ref:IRB@Initialization+Script],
+ and omit it from `ARGV`.
+- `--simple-prompt`, `--sample-book-mode`:
+ Set prompt mode to {:SIMPLE}[rdoc-ref:IRB@Pre-Defined+Prompts].
+- `--singleline`: Use the singleline editor as the {input method}[rdoc-ref:IRB@Input+Method].
+- `--tracer`: Use {Tracer}[rdoc-ref:IRB@Tracer] to print a stack trace for each input command.
+- `--truncate-echo-on-assignment`: Print ({echo}[rdoc-ref:IRB@Return-Value+Printing+-28Echoing-29])
+ truncated result on assignment.
+- `--verbose`Print {verbose}[rdoc-ref:IRB@Verbosity] details.
+- `-v`, `--version`: Print the {IRB version}[rdoc-ref:IRB@Version].
+- `-h`, `--help`: Print the {IRB help text}[rdoc-ref:IRB@Help].
+- `--`: Separate options from {arguments}[rdoc-ref:IRB@Command-Line+Arguments]
+ on the command-line.
+
+### Index of \IRB.conf Entries
+
+These are the keys for hash \IRB.conf entries, with links to explanatory text;
+for each entry that is pre-defined, the initial value is given:
+
+- `:AP_NAME`: \IRB {application name}[rdoc-ref:IRB@Application+Name];
+ initial value: `'irb'`.
+- `:AT_EXIT`: Array of hooks to call
+ {at exit}[rdoc-ref:IRB@IRB];
+ initial value: `[]`.
+- `:AUTO_INDENT`: Whether {automatic indentation}[rdoc-ref:IRB@Automatic+Indentation]
+ is enabled; initial value: `true`.
+- `:BACK_TRACE_LIMIT`: Sets the {back trace limit}[rdoc-ref:IRB@Tracer];
+ initial value: `16`.
+- `:COMMAND_ALIASES`: Defines input {command aliases}[rdoc-ref:IRB@Command+Aliases];
+ initial value:
+
+ {
+ "$": :show_source,
+ "@": :whereami,
+ }
+
+- `:CONTEXT_MODE`: Sets the {context mode}[rdoc-ref:IRB@Context+Mode],
+ the type of binding to be used when evaluating statements;
+ initial value: `4`.
+- `:ECHO`: Whether to print ({echo}[rdoc-ref:IRB@Return-Value+Printing+-28Echoing-29])
+ return values;
+ initial value: `nil`, which would set `conf.echo` to `true`.
+- `:ECHO_ON_ASSIGNMENT`: Whether to print ({echo}[rdoc-ref:IRB@Return-Value+Printing+-28Echoing-29])
+ return values on assignment;
+ initial value: `nil`, which would set `conf.echo_on_assignment` to `:truncate`.
+- `:EVAL_HISTORY`: How much {evaluation history}[rdoc-ref:IRB@Evaluation+History]
+ is to be stored; initial value: `nil`.
+- `:EXTRA_DOC_DIRS`: \Array of
+ {RI documentation directories}[rdoc-ref:IRB@RI+Documentation+Directories]
+ to be parsed for the documentation dialog;
+ initial value: `[]`.
+- `:IGNORE_EOF`: Whether to ignore {end-of-file}[rdoc-ref:IRB@End-of-File];
+ initial value: `false`.
+- `:IGNORE_SIGINT`: Whether to ignore {SIGINT}[rdoc-ref:IRB@SIGINT];
+ initial value: `true`.
+- `:INSPECT_MODE`: Whether to use method `inspect` for printing
+ ({echoing}[rdoc-ref:IRB@Return-Value+Printing+-28Echoing-29]) return values;
+ initial value: `true`.
+- `:IRB_LIB_PATH`: The path to the {IRB library directory}[rdoc-ref:IRB@IRB+Library+Directory]; initial value:
+
+ - `"<i>RUBY_DIR</i>/lib/ruby/gems/<i>RUBY_VER_NUM</i>/gems/irb-<i>IRB_VER_NUM</i>/lib/irb"`,
+
+ where:
+
+ - <i>RUBY_DIR</i> is the Ruby installation dirpath.
+ - <i>RUBY_VER_NUM</i> is the Ruby version number.
+ - <i>IRB_VER_NUM</i> is the \IRB version number.
+
+- `:IRB_NAME`: {IRB name}[rdoc-ref:IRB@IRB+Name];
+ initial value: `'irb'`.
+- `:IRB_RC`: {Configuration monitor}[rdoc-ref:IRB@Configuration+Monitor];
+ initial value: `nil`.
+- `:LC_MESSAGES`: {Locale}[rdoc-ref:IRB@Locale];
+ initial value: IRB::Locale object.
+- `:LOAD_MODULES`: deprecated.
+- `:MAIN_CONTEXT`: The {context}[rdoc-ref:IRB@Session+Context] for the main \IRB session;
+ initial value: IRB::Context object.
+- `:MEASURE`: Whether to
+ {measure performance}[rdoc-ref:IRB@Performance+Measurement];
+ initial value: `false`.
+- `:MEASURE_CALLBACKS`: Callback methods for
+ {performance measurement}[rdoc-ref:IRB@Performance+Measurement];
+ initial value: `[]`.
+- `:MEASURE_PROC`: Procs for
+ {performance measurement}[rdoc-ref:IRB@Performance+Measurement];
+ initial value:
+
+ {
+ :TIME=>#<Proc:0x0000556e271c6598 /var/lib/gems/3.0.0/gems/irb-1.8.3/lib/irb/init.rb:106>,
+ :STACKPROF=>#<Proc:0x0000556e271c6548 /var/lib/gems/3.0.0/gems/irb-1.8.3/lib/irb/init.rb:116>
+ }
+
+- `:PROMPT`: \Hash of {defined prompts}[rdoc-ref:IRB@Prompt+and+Return+Formats];
+ initial value:
+
+ {
+ :NULL=>{:PROMPT_I=>nil, :PROMPT_S=>nil, :PROMPT_C=>nil, :RETURN=>"%s\n"},
+ :DEFAULT=>{:PROMPT_I=>"%N(%m):%03n> ", :PROMPT_S=>"%N(%m):%03n%l ", :PROMPT_C=>"%N(%m):%03n* ", :RETURN=>"=> %s\n"},
+ :CLASSIC=>{:PROMPT_I=>"%N(%m):%03n:%i> ", :PROMPT_S=>"%N(%m):%03n:%i%l ", :PROMPT_C=>"%N(%m):%03n:%i* ", :RETURN=>"%s\n"},
+ :SIMPLE=>{:PROMPT_I=>">> ", :PROMPT_S=>"%l> ", :PROMPT_C=>"?> ", :RETURN=>"=> %s\n"},
+ :INF_RUBY=>{:PROMPT_I=>"%N(%m):%03n> ", :PROMPT_S=>nil, :PROMPT_C=>nil, :RETURN=>"%s\n", :AUTO_INDENT=>true},
+ :XMP=>{:PROMPT_I=>nil, :PROMPT_S=>nil, :PROMPT_C=>nil, :RETURN=>" ==>%s\n"}
+ }
+
+- `:PROMPT_MODE`: Name of {current prompt}[rdoc-ref:IRB@Prompt+and+Return+Formats];
+ initial value: `:DEFAULT`.
+- `:RC`: Whether a {configuration file}[rdoc-ref:IRB@Configuration+File]
+ was found and interpreted;
+ initial value: `true` if a configuration file was found, `false` otherwise.
+- `:SAVE_HISTORY`: Number of commands to save in
+ {input command history}[rdoc-ref:IRB@Input+Command+History];
+ initial value: `1000`.
+- `:SINGLE_IRB`: Whether command-line option `--single-irb` was given;
+ initial value: `true` if the option was given, `false` otherwise.
+ See {Single-IRB Mode}[rdoc-ref:IRB@Single-IRB+Mode].
+- `:USE_AUTOCOMPLETE`: Whether to use
+ {automatic completion}[rdoc-ref:IRB@Automatic+Completion];
+ initial value: `true`.
+- `:USE_COLORIZE`: Whether to use
+ {color highlighting}[rdoc-ref:IRB@Color+Highlighting];
+ initial value: `true`.
+- `:USE_LOADER`: Whether to use the
+ {IRB loader}[rdoc-ref:IRB@IRB+Loader] for `require` and `load`;
+ initial value: `false`.
+- `:USE_TRACER`: Whether to use the
+ {IRB tracer}[rdoc-ref:IRB@Tracer];
+ initial value: `false`.
+- `:VERBOSE`: Whether to print {verbose output}[rdoc-ref:IRB@Verbosity];
+ initial value: `nil`.
+- `:__MAIN__`: The main \IRB object;
+ initial value: `main`.
diff --git a/doc/irb/irb.rd.ja b/doc/irb/irb.rd.ja
index 633c08cbd4..c51e0bd60d 100644
--- a/doc/irb/irb.rd.ja
+++ b/doc/irb/irb.rd.ja
@@ -125,7 +125,6 @@ irb起動時ã«``~/.irbrc''を読ã¿è¾¼ã¿ã¾ã™. ã‚‚ã—存在ã—ãªã„å ´åˆã¯
IRB.conf[:PROMPT][:MY_PROMPT] = { # プロンプトモードã®åå‰
:PROMPT_I => nil, # 通常ã®ãƒ—ロンプト
- :PROMPT_N => nil, # 継続行ã®ãƒ—ロンプト
:PROMPT_S => nil, # 文字列ãªã©ã®ç¶™ç¶šè¡Œã®ãƒ—ロンプト
:PROMPT_C => nil, # å¼ãŒç¶™ç¶šã—ã¦ã„る時ã®ãƒ—ロンプト
:RETURN => " ==>%s\n" # リターン時ã®ãƒ—ロンプト
@@ -140,7 +139,7 @@ OKã§ã™.
IRB.conf[:PROMPT_MODE] = :MY_PROMPT
-PROMPT_I, PROMPT_N, PROMPT_S, PROMPT_Cã¯, フォーマットを指定ã—ã¾ã™.
+PROMPT_I, PROMPT_S, PROMPT_Cã¯, フォーマットを指定ã—ã¾ã™.
%N èµ·å‹•ã—ã¦ã„るコマンドåãŒå‡ºåŠ›ã•ã‚Œã‚‹.
%m mainオブジェクト(self)ãŒto_sã§å‡ºåŠ›ã•ã‚Œã‚‹.
@@ -155,7 +154,6 @@ PROMPT_I, PROMPT_N, PROMPT_S, PROMPT_Cã¯, フォーマットを指定ã—ã¾ã™.
IRB.conf[:PROMPT][:DEFAULT] = {
:PROMPT_I => "%N(%m):%03n:%i> ",
- :PROMPT_N => "%N(%m):%03n:%i> ",
:PROMPT_S => "%N(%m):%03n:%i%l ",
:PROMPT_C => "%N(%m):%03n:%i* ",
:RETURN => "=> %s\n"
diff --git a/doc/keywords.rdoc b/doc/keywords.rdoc
index cb1cff33f0..7c368205ef 100644
--- a/doc/keywords.rdoc
+++ b/doc/keywords.rdoc
@@ -1,4 +1,4 @@
-== Keywords
+= Keywords
The following keywords are used by Ruby.
diff --git a/doc/maintainers.md b/doc/maintainers.md
index a6cec398ed..dabbdc0cb6 100644
--- a/doc/maintainers.md
+++ b/doc/maintainers.md
@@ -1,5 +1,12 @@
# Maintainers
-This page describes the current module, library, and extension maintainers of Ruby.
+This page describes the current branch, module, library, and extension maintainers of Ruby.
+
+## Branch Maintainers
+
+A branch maintainer is responsible for backporting commits into stable branches
+and publishing Ruby patch releases.
+
+[The list of current branch maintainers is available in the wiki](https://github.com/ruby/ruby/wiki/Release-Engineering).
## Module Maintainers
A module maintainer is responsible for a certain part of Ruby.
@@ -72,15 +79,6 @@ have commit right, others don't.
## Default gems Maintainers
### Libraries
-#### lib/abbrev.rb
-* Akinori MUSHA (knu)
-* https://github.com/ruby/abbrev
-* https://rubygems.org/gems/abbrev
-
-#### lib/base64.rb
-* Yusuke Endoh (mame)
-* https://github.com/ruby/base64
-* https://rubygems.org/gems/base64
#### lib/benchmark.rb
* *unmaintained*
@@ -97,12 +95,6 @@ have commit right, others don't.
* https://github.com/ruby/cgi
* https://rubygems.org/gems/cgi
-#### lib/csv.rb
-* Kenta Murata (mrkn)
-* Kouhei Sutou (kou)
-* https://github.com/ruby/csv
-* https://rubygems.org/gems/csv
-
#### lib/English.rb
* *unmaintained*
* https://github.com/ruby/English
@@ -123,11 +115,6 @@ have commit right, others don't.
* https://github.com/ruby/digest
* https://rubygems.org/gems/digest
-#### lib/drb.rb, lib/drb/*
-* Masatoshi SEKI (seki)
-* https://github.com/ruby/drb
-* https://rubygems.org/gems/drb
-
#### lib/erb.rb
* Masatoshi SEKI (seki)
* Takashi Kokubun (k0kubun)
@@ -154,18 +141,16 @@ have commit right, others don't.
* https://github.com/ruby/forwardable
* https://rubygems.org/gems/forwardable
-#### lib/getoptlong.rb
-* *unmaintained*
-* https://github.com/ruby/getoptlong
-* https://rubygems.org/gems/getoptlong
-
#### lib/ipaddr.rb
* Akinori MUSHA (knu)
* https://github.com/ruby/ipaddr
* https://rubygems.org/gems/ipaddr
#### lib/irb.rb, lib/irb/*
-* aycabta
+* Stan Lo (st0012)
+* Tomoya Ishida (tompng)
+* Mari Imaizumi (ima1zumi)
+* Hitoshi Hasumi (hasumikin)
* https://github.com/ruby/irb
* https://rubygems.org/gems/irb
@@ -178,11 +163,6 @@ have commit right, others don't.
* https://github.com/ruby/logger
* https://rubygems.org/gems/logger
-#### lib/mutex_m.rb
-* Keiju ISHITSUKA (keiju)
-* https://github.com/ruby/mutex_m
-* https://rubygems.org/gems/mutex_m
-
#### lib/net/http.rb, lib/net/https.rb
* NARUSE, Yui (naruse)
* https://github.com/ruby/net-http
@@ -193,11 +173,6 @@ have commit right, others don't.
* https://github.com/ruby/net-protocol
* https://rubygems.org/gems/net-protocol
-#### lib/observer.rb
-* *unmaintained*
-* https://github.com/ruby/observer
-* https://rubygems.org/gems/observer
-
#### lib/open3.rb
* *unmaintained*
* https://github.com/ruby/open3
@@ -222,17 +197,17 @@ have commit right, others don't.
* https://github.com/ruby/prettyprint
* https://rubygems.org/gems/prettyprint
+#### lib/prism.rb
+* Kevin Newton (kddnewton)
+* Jemma Issroff (jemmaissroff)
+* https://github.com/ruby/prism
+* https://rubygems.org/gems/prism
+
#### lib/pstore.rb
* *unmaintained*
* https://github.com/ruby/pstore
* https://rubygems.org/gems/pstore
-#### lib/racc.rb, lib/racc/*
-* Aaron Patterson (tenderlove)
-* Hiroshi SHIBATA (hsbt)
-* https://github.com/ruby/racc
-* https://rubygems.org/gems/racc
-
#### lib/readline.rb
* aycabta
* https://github.com/ruby/readline
@@ -243,11 +218,6 @@ have commit right, others don't.
* https://github.com/ruby/resolv
* https://rubygems.org/gems/resolv
-#### lib/resolv-replace.rb
-* Tanaka Akira (akr)
-* https://github.com/ruby/resolv-replace
-* https://rubygems.org/gems/resolv-replace
-
#### lib/rdoc.rb, lib/rdoc/*
* Eric Hodel (drbrain)
* Hiroshi SHIBATA (hsbt)
@@ -255,15 +225,13 @@ have commit right, others don't.
* https://rubygems.org/gems/rdoc
#### lib/reline.rb, lib/reline/*
-* aycabta
+* Tomoya Ishida (tompng)
+* Mari Imaizumi (ima1zumi)
+* Stan Lo (st0012)
+* Hitoshi Hasumi (hasumikin)
* https://github.com/ruby/reline
* https://rubygems.org/gems/reline
-#### lib/rinda/*
-* Masatoshi SEKI (seki)
-* https://github.com/ruby/rinda
-* https://rubygems.org/gems/rinda
-
#### lib/securerandom.rb
* Tanaka Akira (akr)
* https://github.com/ruby/securerandom
@@ -320,7 +288,7 @@ have commit right, others don't.
* https://rubygems.org/gems/un
#### lib/uri.rb, lib/uri/*
-* YAMADA, Akira (akira)
+* NARUSE, Yui (naruse)
* https://github.com/ruby/uri
* https://rubygems.org/gems/uri
@@ -336,9 +304,6 @@ have commit right, others don't.
* https://rubygems.org/gems/weakref
### Extensions
-#### ext/bigdecimal
-* Kenta Murata (mrkn) https://github.com/ruby/bigdecimal
-* https://rubygems.org/gems/bigdecimal
#### ext/cgi
* Nobuyoshi Nakada (nobu)
@@ -386,11 +351,6 @@ have commit right, others don't.
* https://github.com/flori/json
* https://rubygems.org/gems/json
-#### ext/nkf
-* NARUSE, Yui (naruse)
-* https://github.com/ruby/nkf
-* https://rubygems.org/gems/nkf
-
#### ext/openssl
* Kazuki Yamaguchi (rhe)
* https://github.com/ruby/openssl
@@ -407,12 +367,6 @@ have commit right, others don't.
* https://github.com/ruby/psych
* https://rubygems.org/gems/psych
-#### ext/racc
-* Aaron Patterson (tenderlove)
-* Hiroshi SHIBATA (hsbt)
-* https://github.com/ruby/racc
-* https://rubygems.org/gems/racc
-
#### ext/stringio
* Nobuyuki Nakada (nobu)
* https://github.com/ruby/stringio
@@ -423,11 +377,6 @@ have commit right, others don't.
* https://github.com/ruby/strscan
* https://rubygems.org/gems/strscan
-#### ext/syslog
-* Akinori MUSHA (knu)
-* https://github.com/ruby/syslog
-* https://rubygems.org/gems/syslog
-
#### ext/win32ole
* Masaki Suketa (suke)
* https://github.com/ruby/win32ole
@@ -440,7 +389,7 @@ have commit right, others don't.
## Bundled gems upstream repositories
### minitest
-* https://github.com/seattlerb/minitest
+* https://github.com/minitest/minitest
### power_assert
* https://github.com/ruby/power_assert
@@ -484,6 +433,45 @@ have commit right, others don't.
### debug
* https://github.com/ruby/debug
+### racc
+* https://github.com/ruby/racc
+
+#### mutex_m
+* https://github.com/ruby/mutex_m
+
+#### getoptlong
+* https://github.com/ruby/getoptlong
+
+#### base64
+* https://github.com/ruby/base64
+
+#### bigdecimal
+* https://github.com/ruby/bigdecimal
+
+#### observer
+* https://github.com/ruby/observer
+
+#### abbrev
+* https://github.com/ruby/abbrev
+
+#### resolv-replace
+* https://github.com/ruby/resolv-replace
+
+#### rinda
+* https://github.com/ruby/rinda
+
+#### drb
+* https://github.com/ruby/drb
+
+#### nkf
+* https://github.com/ruby/nkf
+
+#### syslog
+* https://github.com/ruby/syslog
+
+#### csv
+* https://github.com/ruby/csv
+
## Platform Maintainers
### mswin64 (Microsoft Windows)
* NAKAMURA Usaku (usa)
diff --git a/doc/optparse/argument_converters.rdoc b/doc/optparse/argument_converters.rdoc
index ac659da8c5..4b4b30e8de 100644
--- a/doc/optparse/argument_converters.rdoc
+++ b/doc/optparse/argument_converters.rdoc
@@ -1,7 +1,7 @@
== Argument Converters
An option can specify that its argument is to be converted
-from the default \String to an instance of another class.
+from the default +String+ to an instance of another class.
=== Contents
@@ -27,13 +27,13 @@ from the default \String to an instance of another class.
=== Built-In Argument Converters
-\OptionParser has a number of built-in argument converters,
++OptionParser+ has a number of built-in argument converters,
which are demonstrated below.
-==== \Date
+==== +Date+
File +date.rb+
-defines an option whose argument is to be converted to a \Date object.
+defines an option whose argument is to be converted to a +Date+ object.
The argument is converted by method Date#parse.
:include: ruby/date.rb
@@ -47,10 +47,10 @@ Executions:
$ ruby date.rb --date "3rd Feb 2001"
[#<Date: 2001-02-03 ((2451944j,0s,0n),+0s,2299161j)>, Date]
-==== \DateTime
+==== +DateTime+
File +datetime.rb+
-defines an option whose argument is to be converted to a \DateTime object.
+defines an option whose argument is to be converted to a +DateTime+ object.
The argument is converted by method DateTime#parse.
:include: ruby/datetime.rb
@@ -64,10 +64,10 @@ Executions:
$ ruby datetime.rb --datetime "3rd Feb 2001 04:05:06 PM"
[#<DateTime: 2001-02-03T16:05:06+00:00 ((2451944j,57906s,0n),+0s,2299161j)>, DateTime]
-==== \Time
+==== +Time+
File +time.rb+
-defines an option whose argument is to be converted to a \Time object.
+defines an option whose argument is to be converted to a +Time+ object.
The argument is converted by method Time#httpdate or Time#parse.
:include: ruby/time.rb
@@ -79,10 +79,10 @@ Executions:
$ ruby time.rb --time 2010-10-31
[2010-10-31 00:00:00 -0500, Time]
-==== \URI
+==== +URI+
File +uri.rb+
-defines an option whose argument is to be converted to a \URI object.
+defines an option whose argument is to be converted to a +URI+ object.
The argument is converted by method URI#parse.
:include: ruby/uri.rb
@@ -96,10 +96,10 @@ Executions:
$ ruby uri.rb --uri file://~/var
[#<URI::File file://~/var>, URI::File]
-==== \Shellwords
+==== +Shellwords+
File +shellwords.rb+
-defines an option whose argument is to be converted to an \Array object by method
+defines an option whose argument is to be converted to an +Array+ object by method
Shellwords#shellwords.
:include: ruby/shellwords.rb
@@ -111,10 +111,10 @@ Executions:
$ ruby shellwords.rb --shellwords "here are 'two words'"
[["here", "are", "two words"], Array]
-==== \Integer
+==== +Integer+
File +integer.rb+
-defines an option whose argument is to be converted to an \Integer object.
+defines an option whose argument is to be converted to an +Integer+ object.
The argument is converted by method Kernel#Integer.
:include: ruby/integer.rb
@@ -132,10 +132,10 @@ Executions:
$ ruby integer.rb --integer 0b100
[4, Integer]
-==== \Float
+==== +Float+
File +float.rb+
-defines an option whose argument is to be converted to a \Float object.
+defines an option whose argument is to be converted to a +Float+ object.
The argument is converted by method Kernel#Float.
:include: ruby/float.rb
@@ -151,11 +151,11 @@ Executions:
$ ruby float.rb --float 1.234E-2
[0.01234, Float]
-==== \Numeric
+==== +Numeric+
File +numeric.rb+
defines an option whose argument is to be converted to an instance
-of \Rational, \Float, or \Integer.
+of +Rational+, +Float+, or +Integer+.
The argument is converted by method Kernel#Rational,
Kernel#Float, or Kernel#Integer.
@@ -170,10 +170,10 @@ Executions:
$ ruby numeric.rb --numeric 3
[3, Integer]
-==== \DecimalInteger
+==== +DecimalInteger+
File +decimal_integer.rb+
-defines an option whose argument is to be converted to an \Integer object.
+defines an option whose argument is to be converted to an +Integer+ object.
The argument is converted by method Kernel#Integer.
:include: ruby/decimal_integer.rb
@@ -192,10 +192,10 @@ Executions:
$ ruby decimal_integer.rb --decimal_integer -0100
[-100, Integer]
-==== \OctalInteger
+==== +OctalInteger+
File +octal_integer.rb+
-defines an option whose argument is to be converted to an \Integer object.
+defines an option whose argument is to be converted to an +Integer+ object.
The argument is converted by method Kernel#Integer.
:include: ruby/octal_integer.rb
@@ -212,10 +212,10 @@ Executions:
$ ruby octal_integer.rb --octal_integer 0100
[64, Integer]
-==== \DecimalNumeric
+==== +DecimalNumeric+
File +decimal_numeric.rb+
-defines an option whose argument is to be converted to an \Integer object.
+defines an option whose argument is to be converted to an +Integer+ object.
The argument is converted by method Kernel#Integer
:include: ruby/decimal_numeric.rb
@@ -232,7 +232,7 @@ Executions:
$ ruby decimal_numeric.rb --decimal_numeric 0100
[64, Integer]
-==== \TrueClass
+==== +TrueClass+
File +true_class.rb+
defines an option whose argument is to be converted to +true+ or +false+.
@@ -259,7 +259,7 @@ Executions:
$ ruby true_class.rb --true_class nil
[false, FalseClass]
-==== \FalseClass
+==== +FalseClass+
File +false_class.rb+
defines an option whose argument is to be converted to +true+ or +false+.
@@ -286,10 +286,10 @@ Executions:
$ ruby false_class.rb --false_class +
[true, TrueClass]
-==== \Object
+==== +Object+
File +object.rb+
-defines an option whose argument is not to be converted from \String.
+defines an option whose argument is not to be converted from +String+.
:include: ruby/object.rb
@@ -300,10 +300,10 @@ Executions:
$ ruby object.rb --object nil
["nil", String]
-==== \String
+==== +String+
File +string.rb+
-defines an option whose argument is not to be converted from \String.
+defines an option whose argument is not to be converted from +String+.
:include: ruby/string.rb
@@ -314,10 +314,10 @@ Executions:
$ ruby string.rb --string nil
["nil", String]
-==== \Array
+==== +Array+
File +array.rb+
-defines an option whose argument is to be converted from \String
+defines an option whose argument is to be converted from +String+
to an array of strings, based on comma-separated substrings.
:include: ruby/array.rb
@@ -331,10 +331,10 @@ Executions:
$ ruby array.rb --array "foo, bar, baz"
[["foo", " bar", " baz"], Array]
-==== \Regexp
+==== +Regexp+
File +regexp.rb+
-defines an option whose argument is to be converted to a \Regexp object.
+defines an option whose argument is to be converted to a +Regexp+ object.
:include: ruby/regexp.rb
@@ -352,7 +352,7 @@ To create a custom converter, call OptionParser#accept with:
- A block that accepts the argument and returns the converted value.
This custom converter accepts any argument and converts it,
-if possible, to a \Complex object.
+if possible, to a +Complex+ object.
:include: ruby/custom_converter.rb
diff --git a/doc/optparse/option_params.rdoc b/doc/optparse/option_params.rdoc
index ace2c4283f..35db8b5a55 100644
--- a/doc/optparse/option_params.rdoc
+++ b/doc/optparse/option_params.rdoc
@@ -1,6 +1,6 @@
== Parameters for New Options
-Option-creating methods in \OptionParser
+Option-creating methods in +OptionParser+
accept arguments that determine the behavior of a new option:
- OptionParser#on
@@ -31,7 +31,7 @@ Contents:
- {Long Names with Optional Arguments}[#label-Long+Names+with+Optional+Arguments]
- {Long Names with Negation}[#label-Long+Names+with+Negation]
- {Mixed Names}[#label-Mixed+Names]
-- {Argument Styles}[#label-Argument+Styles]
+- {Argument Strings}[#label-Argument+Strings]
- {Argument Values}[#label-Argument+Values]
- {Explicit Argument Values}[#label-Explicit+Argument+Values]
- {Explicit Values in Array}[#label-Explicit+Values+in+Array]
@@ -405,7 +405,7 @@ Executions:
=== Argument Converters
An option can specify that its argument is to be converted
-from the default \String to an instance of another class.
+from the default +String+ to an instance of another class.
There are a number of built-in converters.
You can also define custom converters.
diff --git a/doc/optparse/ruby/argument_abbreviation.rb b/doc/optparse/ruby/argument_abbreviation.rb
new file mode 100644
index 0000000000..49007ebe69
--- /dev/null
+++ b/doc/optparse/ruby/argument_abbreviation.rb
@@ -0,0 +1,9 @@
+require 'optparse'
+parser = OptionParser.new
+parser.on('-x', '--xxx=VALUE', %w[ABC def], 'Argument abbreviations') do |value|
+ p ['--xxx', value]
+end
+parser.on('-y', '--yyy=VALUE', {"abc"=>"XYZ", def: "FOO"}, 'Argument abbreviations') do |value|
+ p ['--yyy', value]
+end
+parser.parse!
diff --git a/doc/optparse/tutorial.rdoc b/doc/optparse/tutorial.rdoc
index b95089826d..6f56bbf92d 100644
--- a/doc/optparse/tutorial.rdoc
+++ b/doc/optparse/tutorial.rdoc
@@ -1,10 +1,10 @@
== Tutorial
-=== Why \OptionParser?
+=== Why +OptionParser+?
When a Ruby program executes, it captures its command-line arguments
and options into variable ARGV.
-This simple program just prints its \ARGV:
+This simple program just prints its +ARGV+:
:include: ruby/argv.rb
@@ -18,7 +18,7 @@ the command-line options.
OptionParser offers methods for parsing and handling those options.
-With \OptionParser, you can define options so that for each option:
+With +OptionParser+, you can define options so that for each option:
- The code that defines the option and code that handles that option
are in the same place.
@@ -55,7 +55,7 @@ The class also has method #help, which displays automatically-generated help tex
- {Argument Converters}[#label-Argument+Converters]
- {Help}[#label-Help]
- {Top List and Base List}[#label-Top+List+and+Base+List]
-- {Defining Options}[#label-Defining+Options]
+- {Methods for Defining Options}[#label-Methods+for+Defining+Options]
- {Parsing}[#label-Parsing]
- {Method parse!}[#label-Method+parse-21]
- {Method parse}[#label-Method+parse]
@@ -66,10 +66,10 @@ The class also has method #help, which displays automatically-generated help tex
=== To Begin With
-To use \OptionParser:
+To use +OptionParser+:
-1. Require the \OptionParser code.
-2. Create an \OptionParser object.
+1. Require the +OptionParser+ code.
+2. Create an +OptionParser+ object.
3. Define one or more options.
4. Parse the command line.
@@ -92,9 +92,9 @@ the block defined for the option is called with the argument value.
An invalid option raises an exception.
Method #parse!, which is used most often in this tutorial,
-removes from \ARGV the options and arguments it finds,
+removes from +ARGV+ the options and arguments it finds,
leaving other non-option arguments for the program to handle on its own.
-The method returns the possibly-reduced \ARGV array.
+The method returns the possibly-reduced +ARGV+ array.
Executions:
@@ -115,7 +115,7 @@ Executions:
=== Defining Options
-A common way to define an option in \OptionParser
+A common way to define an option in +OptionParser+
is with instance method OptionParser#on.
The method may be called with any number of arguments
@@ -351,6 +351,29 @@ Executions:
Omitting an optional argument does not raise an error.
+==== Argument Abbreviations
+
+Specify an argument list as an Array or a Hash.
+
+ :include: ruby/argument_abbreviation.rb
+
+When an argument is abbreviated, the expanded argument yielded.
+
+Executions:
+
+ $ ruby argument_abbreviation.rb --help
+ Usage: argument_abbreviation [options]
+ Usage: argument_abbreviation [options]
+ -x, --xxx=VALUE Argument abbreviations
+ -y, --yyy=VALUE Argument abbreviations
+ $ ruby argument_abbreviation.rb --xxx A
+ ["--xxx", "ABC"]
+ $ ruby argument_abbreviation.rb --xxx c
+ argument_abbreviation.rb:9:in `<main>': invalid argument: --xxx c (OptionParser::InvalidArgument)
+ $ ruby argument_abbreviation.rb --yyy a --yyy d
+ ["--yyy", "XYZ"]
+ ["--yyy", "FOO"]
+
=== Argument Values
Permissible argument values may be restricted
@@ -522,11 +545,11 @@ Executions:
=== Argument Converters
An option can specify that its argument is to be converted
-from the default \String to an instance of another class.
+from the default +String+ to an instance of another class.
There are a number of built-in converters.
Example: File +date.rb+
-defines an option whose argument is to be converted to a \Date object.
+defines an option whose argument is to be converted to a +Date+ object.
The argument is converted by method Date#parse.
:include: ruby/date.rb
@@ -546,7 +569,7 @@ for both built-in and custom converters.
=== Help
-\OptionParser makes automatically generated help text available.
++OptionParser+ makes automatically generated help text available.
The help text consists of:
@@ -614,49 +637,49 @@ Execution:
=== Top List and Base List
-An \OptionParser object maintains a stack of \OptionParser::List objects,
+An +OptionParser+ object maintains a stack of OptionParser::List objects,
each of which has a collection of zero or more options.
It is unlikely that you'll need to add or take away from that stack.
The stack includes:
-- The <em>top list</em>, given by \OptionParser#top.
-- The <em>base list</em>, given by \OptionParser#base.
+- The <em>top list</em>, given by OptionParser#top.
+- The <em>base list</em>, given by OptionParser#base.
-When \OptionParser builds its help text, the options in the top list
+When +OptionParser+ builds its help text, the options in the top list
precede those in the base list.
-=== Defining Options
+=== Methods for Defining Options
Option-defining methods allow you to create an option, and also append/prepend it
to the top list or append it to the base list.
Each of these next three methods accepts a sequence of parameter arguments and a block,
-creates an option object using method \Option#make_switch (see below),
+creates an option object using method OptionParser#make_switch (see below),
and returns the created option:
-- \Method \OptionParser#define appends the created option to the top list.
+- \Method OptionParser#define appends the created option to the top list.
-- \Method \OptionParser#define_head prepends the created option to the top list.
+- \Method OptionParser#define_head prepends the created option to the top list.
-- \Method \OptionParser#define_tail appends the created option to the base list.
+- \Method OptionParser#define_tail appends the created option to the base list.
These next three methods are identical to the three above,
except for their return values:
-- \Method \OptionParser#on is identical to method \OptionParser#define,
+- \Method OptionParser#on is identical to method OptionParser#define,
except that it returns the parser object +self+.
-- \Method \OptionParser#on_head is identical to method \OptionParser#define_head,
+- \Method OptionParser#on_head is identical to method OptionParser#define_head,
except that it returns the parser object +self+.
-- \Method \OptionParser#on_tail is identical to method \OptionParser#define_tail,
+- \Method OptionParser#on_tail is identical to method OptionParser#define_tail,
except that it returns the parser object +self+.
Though you may never need to call it directly,
here's the core method for defining an option:
-- \Method \OptionParser#make_switch accepts an array of parameters and a block.
+- \Method OptionParser#make_switch accepts an array of parameters and a block.
See {Parameters for New Options}[optparse/option_params.rdoc].
This method is unlike others here in that it:
- Accepts an <em>array of parameters</em>;
@@ -668,7 +691,7 @@ here's the core method for defining an option:
=== Parsing
-\OptionParser has six instance methods for parsing.
++OptionParser+ has six instance methods for parsing.
Three have names ending with a "bang" (<tt>!</tt>):
@@ -699,9 +722,9 @@ Each of these methods:
(see {Keyword Argument into}[#label-Keyword+Argument+into]).
- Returns +argv+, possibly with some elements removed.
-==== \Method parse!
+==== \Method +parse!+
-\Method parse!:
+\Method +parse!+:
- Accepts an optional array of string arguments +argv+;
if not given, +argv+ defaults to the value of OptionParser#default_argv,
@@ -756,9 +779,9 @@ Processing ended by non-option found when +POSIXLY_CORRECT+ is defined:
["--xxx", true]
Returned: ["input_file.txt", "output_file.txt", "-yyy", "FOO"] (Array)
-==== \Method parse
+==== \Method +parse+
-\Method parse:
+\Method +parse+:
- Accepts an array of string arguments
_or_ zero or more string arguments.
@@ -810,25 +833,25 @@ Processing ended by non-option found when +POSIXLY_CORRECT+ is defined:
["--xxx", true]
Returned: ["input_file.txt", "output_file.txt", "-yyy", "FOO"] (Array)
-==== \Method order!
+==== \Method +order!+
Calling method OptionParser#order! gives exactly the same result as
calling method OptionParser#parse! with environment variable
+POSIXLY_CORRECT+ defined.
-==== \Method order
+==== \Method +order+
Calling method OptionParser#order gives exactly the same result as
calling method OptionParser#parse with environment variable
+POSIXLY_CORRECT+ defined.
-==== \Method permute!
+==== \Method +permute!+
Calling method OptionParser#permute! gives exactly the same result as
calling method OptionParser#parse! with environment variable
+POSIXLY_CORRECT+ _not_ defined.
-==== \Method permute
+==== \Method +permute+
Calling method OptionParser#permute gives exactly the same result as
calling method OptionParser#parse with environment variable
diff --git a/doc/packed_data.rdoc b/doc/packed_data.rdoc
index 05d9ab758a..17bbf92023 100644
--- a/doc/packed_data.rdoc
+++ b/doc/packed_data.rdoc
@@ -1,4 +1,4 @@
-== Packed \Data
+= Packed \Data
Certain Ruby core methods deal with packing and unpacking data:
@@ -64,7 +64,7 @@ If elements don't fit the provided directive, only least significant bits are en
[257].pack("C").unpack("C") # => [1]
-=== Packing \Method
+== Packing \Method
\Method Array#pack accepts optional keyword argument
+buffer+ that specifies the target string (instead of a new string):
@@ -76,7 +76,7 @@ The method can accept a block:
# Packed string is passed to the block.
[65, 66].pack('C*') {|s| p s } # => "AB"
-=== Unpacking Methods
+== Unpacking Methods
Methods String#unpack and String#unpack1 each accept
an optional keyword argument +offset+ that specifies an offset
@@ -95,12 +95,12 @@ Both methods can accept a block:
# The single unpacked object is passed to the block.
'AB'.unpack1('C*') {|ele| p ele } # => 65
-=== \Integer Directives
+== \Integer Directives
Each integer directive specifies the packing or unpacking
for one element in the input or output array.
-==== 8-Bit \Integer Directives
+=== 8-Bit \Integer Directives
- <tt>'c'</tt> - 8-bit signed integer
(like C <tt>signed char</tt>):
@@ -116,7 +116,7 @@ for one element in the input or output array.
s = [0, 1, -1].pack('C*') # => "\x00\x01\xFF"
s.unpack('C*') # => [0, 1, 255]
-==== 16-Bit \Integer Directives
+=== 16-Bit \Integer Directives
- <tt>'s'</tt> - 16-bit signed integer, native-endian
(like C <tt>int16_t</tt>):
@@ -146,7 +146,7 @@ for one element in the input or output array.
s.unpack('v*')
# => [0, 1, 65535, 32767, 32768, 65535]
-==== 32-Bit \Integer Directives
+=== 32-Bit \Integer Directives
- <tt>'l'</tt> - 32-bit signed integer, native-endian
(like C <tt>int32_t</tt>):
@@ -178,7 +178,7 @@ for one element in the input or output array.
s.unpack('v*')
# => [0, 0, 1, 0, 65535, 65535]
-==== 64-Bit \Integer Directives
+=== 64-Bit \Integer Directives
- <tt>'q'</tt> - 64-bit signed integer, native-endian
(like C <tt>int64_t</tt>):
@@ -196,7 +196,7 @@ for one element in the input or output array.
s.unpack('Q*')
# => [578437695752307201, 17940646550795321087]
-==== Platform-Dependent \Integer Directives
+=== Platform-Dependent \Integer Directives
- <tt>'i'</tt> - Platform-dependent width signed integer,
native-endian (like C <tt>int</tt>):
@@ -214,25 +214,23 @@ for one element in the input or output array.
s.unpack('I*')
# => [67305985, 4244504319]
-==== Pointer Directives
-
-- <tt>'j'</tt> - 64-bit pointer-width signed integer,
- native-endian (like C <tt>intptr_t</tt>):
+- <tt>'j'</tt> - Pointer-width signed integer, native-endian
+ (like C <tt>intptr_t</tt>):
s = [67305985, -50462977].pack('j*')
# => "\x01\x02\x03\x04\x00\x00\x00\x00\xFF\xFE\xFD\xFC\xFF\xFF\xFF\xFF"
s.unpack('j*')
# => [67305985, -50462977]
-- <tt>'j'</tt> - 64-bit pointer-width unsigned integer,
- native-endian (like C <tt>uintptr_t</tt>):
+- <tt>'J'</tt> - Pointer-width unsigned integer, native-endian
+ (like C <tt>uintptr_t</tt>):
s = [67305985, 4244504319].pack('J*')
# => "\x01\x02\x03\x04\x00\x00\x00\x00\xFF\xFE\xFD\xFC\x00\x00\x00\x00"
s.unpack('J*')
# => [67305985, 4244504319]
-==== Other \Integer Directives
+=== Other \Integer Directives
- <tt>'U'</tt> - UTF-8 character:
@@ -242,38 +240,37 @@ for one element in the input or output array.
# => [4194304]
- <tt>'w'</tt> - BER-encoded integer
- (see {BER enocding}[https://en.wikipedia.org/wiki/X.690#BER_encoding]):
+ (see {BER encoding}[https://en.wikipedia.org/wiki/X.690#BER_encoding]):
s = [1073741823].pack('w*')
# => "\x83\xFF\xFF\xFF\x7F"
s.unpack('w*')
# => [1073741823]
-==== Modifiers for \Integer Directives
-
-For directives in
-<tt>'i'</tt>,
-<tt>'I'</tt>,
-<tt>'s'</tt>,
-<tt>'S'</tt>,
-<tt>'l'</tt>,
-<tt>'L'</tt>,
-<tt>'q'</tt>,
-<tt>'Q'</tt>,
-<tt>'j'</tt>, and
-<tt>'J'</tt>,
-these modifiers may be suffixed:
-
-- <tt>'!'</tt> or <tt>'_'</tt> - Underlying platform’s native size.
+=== Modifiers for \Integer Directives
+
+For the following directives, <tt>'!'</tt> or <tt>'_'</tt> modifiers may be
+suffixed as underlying platform’s native size.
+
+- <tt>'i'</tt>, <tt>'I'</tt> - C <tt>int</tt>, always native size.
+- <tt>'s'</tt>, <tt>'S'</tt> - C <tt>short</tt>.
+- <tt>'l'</tt>, <tt>'L'</tt> - C <tt>long</tt>.
+- <tt>'q'</tt>, <tt>'Q'</tt> - C <tt>long long</tt>, if available.
+- <tt>'j'</tt>, <tt>'J'</tt> - C <tt>intptr_t</tt>, always native size.
+
+Native size modifiers are silently ignored for always native size directives.
+
+The endian modifiers also may be suffixed in the directives above:
+
- <tt>'>'</tt> - Big-endian.
- <tt>'<'</tt> - Little-endian.
-=== \Float Directives
+== \Float Directives
Each float directive specifies the packing or unpacking
for one element in the input or output array.
-==== Single-Precision \Float Directives
+=== Single-Precision \Float Directives
- <tt>'F'</tt> or <tt>'f'</tt> - Native format:
@@ -290,7 +287,7 @@ for one element in the input or output array.
s = [3.0].pack('g') # => "@@\x00\x00"
s.unpack('g') # => [3.0]
-==== Double-Precision \Float Directives
+=== Double-Precision \Float Directives
- <tt>'D'</tt> or <tt>'d'</tt> - Native format:
@@ -317,12 +314,12 @@ A float directive may be infinity or not-a-number:
[nan].pack('f') # => "\x00\x00\xC0\x7F"
"\x00\x00\xC0\x7F".unpack('f') # => [NaN]
-=== \String Directives
+== \String Directives
Each string directive specifies the packing or unpacking
for one byte in the input or output string.
-==== Binary \String Directives
+=== Binary \String Directives
- <tt>'A'</tt> - Arbitrary binary string (space padded; count is width);
+nil+ is treated as the empty string:
@@ -380,7 +377,7 @@ for one byte in the input or output string.
"foo".unpack('Z*') # => ["foo"]
"foo\0bar".unpack('Z*') # => ["foo"] # Does not read past "\0".
-==== Bit \String Directives
+=== Bit \String Directives
- <tt>'B'</tt> - Bit string (high byte first):
@@ -424,7 +421,7 @@ for one byte in the input or output string.
"\x01".unpack("b2") # => ["10"]
"\x01".unpack("b3") # => ["100"]
-==== Hex \String Directives
+=== Hex \String Directives
- <tt>'H'</tt> - Hex string (high nibble first):
@@ -470,7 +467,7 @@ for one byte in the input or output string.
"\x01\xfe".unpack('h4') # => ["10ef"]
"\x01\xfe".unpack('h5') # => ["10ef"]
-==== Pointer \String Directives
+=== Pointer \String Directives
- <tt>'P'</tt> - Pointer to a structure (fixed-length string):
@@ -488,7 +485,7 @@ for one byte in the input or output string.
("\0" * 8).unpack("p") # => [nil]
[nil].pack("p") # => "\x00\x00\x00\x00\x00\x00\x00\x00"
-==== Other \String Directives
+=== Other \String Directives
- <tt>'M'</tt> - Quoted printable, MIME encoding;
text mode, but input must use LF and output LF;
@@ -557,12 +554,14 @@ for one byte in the input or output string.
- <tt>'u'</tt> - UU-encoded string:
- [0].pack("U") # => "\u0000"
- [0x3fffffff].pack("U") # => "\xFC\xBF\xBF\xBF\xBF\xBF"
- [0x40000000].pack("U") # => "\xFD\x80\x80\x80\x80\x80"
- [0x7fffffff].pack("U") # => "\xFD\xBF\xBF\xBF\xBF\xBF"
+ [""].pack("u") # => ""
+ ["a"].pack("u") # => "!80``\n"
+ ["aaa"].pack("u") # => "#86%A\n"
+
+ "".unpack("u") # => [""]
+ "#86)C\n".unpack("u") # => ["abc"]
-=== Offset Directives
+== Offset Directives
- <tt>'@'</tt> - Begin packing at the given byte offset;
for packing, null fill if necessary:
@@ -580,7 +579,7 @@ for one byte in the input or output string.
[0, 1, 2].pack("CCX2C") # => "\x02"
"\x00\x02".unpack("CCXC") # => [0, 2, 2]
-=== Null Byte Direcive
+== Null Byte Directive
- <tt>'x'</tt> - Null byte:
diff --git a/doc/pty/README.expect.ja b/doc/pty/README.expect.ja
index 7c0456f24f..a4eb6b01df 100644
--- a/doc/pty/README.expect.ja
+++ b/doc/pty/README.expect.ja
@@ -1,21 +1,23 @@
- README for expect
+= README for expect
by A. Ito, 28 October, 1998
- Expectライブラリã¯ï¼Œtcl ã® expect パッケージã¨ä¼¼ãŸã‚ˆã†ãªæ©Ÿèƒ½ã‚’
+Expectライブラリã¯ï¼Œtcl ã® expect パッケージã¨ä¼¼ãŸã‚ˆã†ãªæ©Ÿèƒ½ã‚’
IOクラスã«è¿½åŠ ã—ã¾ã™ï¼Ž
- 追加ã•ã‚Œã‚‹ãƒ¡ã‚½ãƒƒãƒ‰ã®ä½¿ã„æ–¹ã¯æ¬¡ã®é€šã‚Šã§ã™ï¼Ž
+追加ã•ã‚Œã‚‹ãƒ¡ã‚½ãƒƒãƒ‰ã®ä½¿ã„æ–¹ã¯æ¬¡ã®é€šã‚Šã§ã™ï¼Ž
- IO#expect(pattern,timeout=9999999)
+[IO#expect(pattern,timeout=9999999)]
-pattern 㯠String ã‹ Regexp ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ï¼Œtimeout 㯠Fixnum
-ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã§ã™ï¼Žtimeout ã¯çœç•¥ã§ãã¾ã™ï¼Ž
- ã“ã®ãƒ¡ã‚½ãƒƒãƒ‰ãŒãƒ–ロックãªã—ã§å‘¼ã°ã‚ŒãŸå ´åˆï¼Œã¾ãšãƒ¬ã‚·ãƒ¼ãƒã§ã‚ã‚‹
-IOオブジェクトã‹ã‚‰ pattern ã«ãƒžãƒƒãƒã™ã‚‹ãƒ‘ターンãŒèª­ã¿ã“ã¾ã‚Œã‚‹
-ã¾ã§å¾…ã¡ã¾ã™ï¼Žãƒ‘ターンãŒå¾—られãŸã‚‰ï¼Œãã®ãƒ‘ターンã«é–¢ã™ã‚‹é…列を
-è¿”ã—ã¾ã™ï¼Žé…列ã®æœ€åˆã®è¦ç´ ã¯ï¼Œpattern ã«ãƒžãƒƒãƒã™ã‚‹ã¾ã§ã«èª­ã¿ã“
-ã¾ã‚ŒãŸå†…容ã®æ–‡å­—列ã§ã™ï¼Ž2番目以é™ã®è¦ç´ ã¯ï¼Œpattern ã®æ­£è¦è¡¨ç¾
-ã®ä¸­ã«ã‚¢ãƒ³ã‚«ãƒ¼ãŒã‚ã£ãŸå ´åˆã«ï¼Œãã®ã‚¢ãƒ³ã‚«ãƒ¼ã«ãƒžãƒƒãƒã™ã‚‹éƒ¨åˆ†ã§ã™ï¼Ž
-ã‚‚ã—タイムアウトãŒèµ·ããŸå ´åˆã¯ï¼Œã“ã®ãƒ¡ã‚½ãƒƒãƒ‰ã¯nilã‚’è¿”ã—ã¾ã™ï¼Ž
- ã“ã®ãƒ¡ã‚½ãƒƒãƒ‰ãŒãƒ–ロック付ãã§å‘¼ã°ã‚ŒãŸå ´åˆã«ã¯ï¼Œãƒžãƒƒãƒã—ãŸè¦ç´ ã®
-é…列ãŒãƒ–ロック引数ã¨ã—ã¦æ¸¡ã•ã‚Œï¼Œãƒ–ロックãŒè©•ä¾¡ã•ã‚Œã¾ã™ï¼Ž
+ _pattern_ 㯠String ã‹ Regexp ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ï¼Œ_timeout_ 㯠Fixnum
+ ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã§ã™ï¼Ž_timeout_ ã¯çœç•¥ã§ãã¾ã™ï¼Ž
+
+ ã“ã®ãƒ¡ã‚½ãƒƒãƒ‰ãŒãƒ–ロックãªã—ã§å‘¼ã°ã‚ŒãŸå ´åˆï¼Œã¾ãšãƒ¬ã‚·ãƒ¼ãƒã§ã‚ã‚‹
+ IOオブジェクトã‹ã‚‰ _pattern_ ã«ãƒžãƒƒãƒã™ã‚‹ãƒ‘ターンãŒèª­ã¿ã“ã¾ã‚Œã‚‹
+ ã¾ã§å¾…ã¡ã¾ã™ï¼Žãƒ‘ターンãŒå¾—られãŸã‚‰ï¼Œãã®ãƒ‘ターンã«é–¢ã™ã‚‹é…列を
+ è¿”ã—ã¾ã™ï¼Žé…列ã®æœ€åˆã®è¦ç´ ã¯ï¼Œ_pattern_ ã«ãƒžãƒƒãƒã™ã‚‹ã¾ã§ã«èª­ã¿ã“
+ ã¾ã‚ŒãŸå†…容ã®æ–‡å­—列ã§ã™ï¼Ž2番目以é™ã®è¦ç´ ã¯ï¼Œ_pattern_ ã®æ­£è¦è¡¨ç¾
+ ã®ä¸­ã«ã‚¢ãƒ³ã‚«ãƒ¼ãŒã‚ã£ãŸå ´åˆã«ï¼Œãã®ã‚¢ãƒ³ã‚«ãƒ¼ã«ãƒžãƒƒãƒã™ã‚‹éƒ¨åˆ†ã§ã™ï¼Ž
+ ã‚‚ã—タイムアウトãŒèµ·ããŸå ´åˆã¯ï¼Œã“ã®ãƒ¡ã‚½ãƒƒãƒ‰ã¯ +nil+ ã‚’è¿”ã—ã¾ã™ï¼Ž
+
+ ã“ã®ãƒ¡ã‚½ãƒƒãƒ‰ãŒãƒ–ロック付ãã§å‘¼ã°ã‚ŒãŸå ´åˆã«ã¯ï¼Œãƒžãƒƒãƒã—ãŸè¦ç´ ã®
+ é…列ãŒãƒ–ロック引数ã¨ã—ã¦æ¸¡ã•ã‚Œï¼Œãƒ–ロックãŒè©•ä¾¡ã•ã‚Œã¾ã™ï¼Ž
diff --git a/doc/pty/README.ja b/doc/pty/README.ja
index 2d83ffa033..a26b4932ff 100644
--- a/doc/pty/README.ja
+++ b/doc/pty/README.ja
@@ -1,27 +1,26 @@
-pty 拡張モジュール version 0.3 by A.ito
+= pty 拡張モジュール version 0.3 by A.ito
1. ã¯ã˜ã‚ã«
-ã“ã®æ‹¡å¼µãƒ¢ã‚¸ãƒ¥ãƒ¼ãƒ«ã¯ï¼Œä»®æƒ³tty (pty) を通ã—ã¦é©å½“ãªã‚³ãƒžãƒ³ãƒ‰ã‚’
-実行ã™ã‚‹æ©Ÿèƒ½ã‚’ ruby ã«æä¾›ã—ã¾ã™ï¼Ž
+ ã“ã®æ‹¡å¼µãƒ¢ã‚¸ãƒ¥ãƒ¼ãƒ«ã¯ï¼Œä»®æƒ³tty (pty) を通ã—ã¦é©å½“ãªã‚³ãƒžãƒ³ãƒ‰ã‚’
+ 実行ã™ã‚‹æ©Ÿèƒ½ã‚’ ruby ã«æä¾›ã—ã¾ã™ï¼Ž
2. インストール
-次ã®ã‚ˆã†ã«ã—ã¦ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã—ã¦ãã ã•ã„.
+ 次ã®ã‚ˆã†ã«ã—ã¦ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã—ã¦ãã ã•ã„.
-(1) ruby extconf.rb
+ 1. <tt>ruby extconf.rb</tt>
+ を実行ã™ã‚‹ã¨ Makefile ãŒç”Ÿæˆã•ã‚Œã¾ã™ï¼Ž
- を実行ã™ã‚‹ã¨ Makefile ãŒç”Ÿæˆã•ã‚Œã¾ã™ï¼Ž
-
-(2) make; make install を実行ã—ã¦ãã ã•ã„.
+ 2. <tt>make; make install</tt> を実行ã—ã¦ãã ã•ã„.
3. 何ãŒã§ãã‚‹ã‹
-ã“ã®æ‹¡å¼µãƒ¢ã‚¸ãƒ¥ãƒ¼ãƒ«ã¯ï¼ŒPTY ã¨ã„ã†ãƒ¢ã‚¸ãƒ¥ãƒ¼ãƒ«ã‚’定義ã—ã¾ã™ï¼Žãã®ä¸­
-ã«ã¯ï¼Œæ¬¡ã®ã‚ˆã†ãªãƒ¢ã‚¸ãƒ¥ãƒ¼ãƒ«é–¢æ•°ãŒå«ã¾ã‚Œã¦ã„ã¾ã™ï¼Ž
+ ã“ã®æ‹¡å¼µãƒ¢ã‚¸ãƒ¥ãƒ¼ãƒ«ã¯ï¼ŒPTY ã¨ã„ã†ãƒ¢ã‚¸ãƒ¥ãƒ¼ãƒ«ã‚’定義ã—ã¾ã™ï¼Žãã®ä¸­
+ ã«ã¯ï¼Œæ¬¡ã®ã‚ˆã†ãªãƒ¢ã‚¸ãƒ¥ãƒ¼ãƒ«é–¢æ•°ãŒå«ã¾ã‚Œã¦ã„ã¾ã™ï¼Ž
- getpty(command)
- spawn(command)
+ [PTY.getpty(command)]
+ [PTY.spawn(command)]
ã“ã®é–¢æ•°ã¯ï¼Œä»®æƒ³ttyを確ä¿ã—,指定ã•ã‚ŒãŸã‚³ãƒžãƒ³ãƒ‰ã‚’ãã®ä»®æƒ³tty
ã®å‘ã“ã†ã§å®Ÿè¡Œã—,é…列を返ã—ã¾ã™ï¼Žæˆ»ã‚Šå€¤ã¯3ã¤ã®è¦ç´ ã‹ã‚‰ãªã‚‹
@@ -35,12 +34,7 @@ pty 拡張モジュール version 0.3 by A.ito
ã®ã¿ä¾‹å¤–ãŒç™ºç”Ÿã—ã¾ã™ï¼Žå­ãƒ—ロセスをモニターã—ã¦ã„るスレッドã¯ãƒ–ロッ
クを抜ã‘ã‚‹ã¨ãã«çµ‚了ã—ã¾ã™ï¼Ž
- protect_signal
- reset_signal
-
- 廃止予定ã§ã™ï¼Ž
-
- PTY.open
+ [PTY.open]
仮想ttyを確ä¿ã—,マスターå´ã«å¯¾å¿œã™ã‚‹IOオブジェクトã¨ã‚¹ãƒ¬ãƒ¼ãƒ–å´ã«
対応ã™ã‚‹Fileオブジェクトã®é…列を返ã—ã¾ã™ï¼Žãƒ–ロック付ãã§å‘¼ã³å‡ºã•
@@ -48,7 +42,7 @@ pty 拡張モジュール version 0.3 by A.ito
クã‹ã‚‰è¿”ã•ã‚ŒãŸçµæžœã‚’è¿”ã—ã¾ã™ï¼Žã¾ãŸã€ã“ã®ãƒžã‚¹ã‚¿ãƒ¼IOã¨ã‚¹ãƒ¬ãƒ¼ãƒ–File
ã¯ã€ãƒ–ロックを抜ã‘ã‚‹ã¨ãã«ã‚¯ãƒ­ãƒ¼ã‚ºæ¸ˆã¿ã§ãªã‘ã‚Œã°ã‚¯ãƒ­ãƒ¼ã‚ºã•ã‚Œã¾ã™ï¼Ž
- PTY.check(pid[, raise=false])
+ [PTY.check(pid[, raise=false])]
pidã§æŒ‡å®šã•ã‚ŒãŸå­ãƒ—ロセスã®çŠ¶æ…‹ã‚’ãƒã‚§ãƒƒã‚¯ã—,実行中ã§ã‚ã‚Œã°nilã‚’
è¿”ã—ã¾ã™ï¼Žçµ‚了ã—ã¦ã„ã‚‹ã‹åœæ­¢ã—ã¦ã„ã‚‹å ´åˆã€ç¬¬äºŒå¼•æ•°ãŒå½ã§ã‚ã‚Œã°ã€
@@ -57,20 +51,20 @@ pty 拡張モジュール version 0.3 by A.ito
4. 利用ã«ã¤ã„ã¦
-伊藤彰則ãŒè‘—作権をä¿æœ‰ã—ã¾ã™ï¼Ž
+ 伊藤彰則ãŒè‘—作権をä¿æœ‰ã—ã¾ã™ï¼Ž
-ソースプログラムã¾ãŸã¯ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã«å…ƒã®è‘—作権表示ãŒæ”¹å¤‰ã•ã‚Œãšã«
-表示ã•ã‚Œã¦ã„ã‚‹å ´åˆã«é™ã‚Šï¼Œèª°ã§ã‚‚,ã“ã®ã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ã‚’ç„¡å„Ÿã‹ã¤è‘—作
-権者ã«ç„¡æ–­ã§åˆ©ç”¨ãƒ»é…布・改変ã§ãã¾ã™ï¼Žåˆ©ç”¨ç›®çš„ã¯é™å®šã•ã‚Œã¦ã„ã¾ã›
-ん.
+ ソースプログラムã¾ãŸã¯ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã«å…ƒã®è‘—作権表示ãŒæ”¹å¤‰ã•ã‚Œãšã«
+ 表示ã•ã‚Œã¦ã„ã‚‹å ´åˆã«é™ã‚Šï¼Œèª°ã§ã‚‚,ã“ã®ã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ã‚’ç„¡å„Ÿã‹ã¤è‘—作
+ 権者ã«ç„¡æ–­ã§åˆ©ç”¨ãƒ»é…布・改変ã§ãã¾ã™ï¼Žåˆ©ç”¨ç›®çš„ã¯é™å®šã•ã‚Œã¦ã„ã¾ã›
+ ん.
-ã“ã®ãƒ—ログラムã®åˆ©ç”¨ãƒ»é…布ãã®ä»–ã“ã®ãƒ—ログラムã«é–¢ä¿‚ã™ã‚‹è¡Œç‚ºã«ã‚ˆ
-ã£ã¦ç”Ÿã˜ãŸã„ã‹ãªã‚‹æ害ã«å¯¾ã—ã¦ã‚‚,作者ã¯ä¸€åˆ‡è²¬ä»»ã‚’è² ã„ã¾ã›ã‚“.
+ ã“ã®ãƒ—ログラムã®åˆ©ç”¨ãƒ»é…布ãã®ä»–ã“ã®ãƒ—ログラムã«é–¢ä¿‚ã™ã‚‹è¡Œç‚ºã«ã‚ˆ
+ ã£ã¦ç”Ÿã˜ãŸã„ã‹ãªã‚‹æ害ã«å¯¾ã—ã¦ã‚‚,作者ã¯ä¸€åˆ‡è²¬ä»»ã‚’è² ã„ã¾ã›ã‚“.
5. ãƒã‚°å ±å‘Šç­‰
-ãƒã‚°ãƒ¬ãƒãƒ¼ãƒˆã¯æ­“è¿Žã—ã¾ã™ï¼Ž
+ ãƒã‚°ãƒ¬ãƒãƒ¼ãƒˆã¯æ­“è¿Žã—ã¾ã™ï¼Ž
aito@ei5sun.yz.yamagata-u.ac.jp
-ã¾ã§é›»å­ãƒ¡ãƒ¼ãƒ«ã§ãƒã‚°ãƒ¬ãƒãƒ¼ãƒˆã‚’ãŠé€ã‚Šãã ã•ã„.
+ ã¾ã§é›»å­ãƒ¡ãƒ¼ãƒ«ã§ãƒã‚°ãƒ¬ãƒãƒ¼ãƒˆã‚’ãŠé€ã‚Šãã ã•ã„.
diff --git a/doc/ractor.md b/doc/ractor.md
index 3ead510501..7a69e839de 100644
--- a/doc/ractor.md
+++ b/doc/ractor.md
@@ -204,8 +204,8 @@ For message sending and receiving, there are two types of APIs: push type and pu
* When a Ractor is terminated, the Ractor's ports are closed.
* There are 3 ways to send an object as a message
* (1) Send a reference: Sending a shareable object, send only a reference to the object (fast)
- * (2) Copy an object: Sending an unshareable object by copying an object deeply (slow). Note that you can not send an object which does not support deep copy. Some `T_DATA` objects are not supported.
- * (3) Move an object: Sending an unshareable object reference with a membership. Sender Ractor can not access moved objects anymore (raise an exception) after moving it. Current implementation makes new object as a moved object for receiver Ractor and copies references of sending object to moved object.
+ * (2) Copy an object: Sending an unshareable object by copying an object deeply (slow). Note that you can not send an object which does not support deep copy. Some `T_DATA` objects (objects whose class is defined in a C extension, such as `StringIO`) are not supported.
+ * (3) Move an object: Sending an unshareable object reference with a membership. Sender Ractor can not access moved objects anymore (raise an exception) after moving it. Current implementation makes new object as a moved object for receiver Ractor and copies references of sending object to moved object. `T_DATA` objects are not supported.
* You can choose "Copy" and "Move" by the `move:` keyword, `Ractor#send(obj, move: true/false)` and `Ractor.yield(obj, move: true/false)` (default is `false` (COPY)).
### Sending/Receiving ports
diff --git a/doc/rdoc/markup_reference.rb b/doc/rdoc/markup_reference.rb
index e63d625b55..bfc84abd5a 100644
--- a/doc/rdoc/markup_reference.rb
+++ b/doc/rdoc/markup_reference.rb
@@ -7,72 +7,61 @@ require 'rdoc'
# attributes, and constants -- are solely for illustrating \RDoc markup,
# and have no other legitimate use.
#
-# = \RDoc Markup Reference
-#
-# Notes:
+# == About the Examples
#
# - Examples in this reference are Ruby code and comments;
# certain differences from other sources
# (such as C code and comments) are noted.
+# - Almost all examples on this page are all RDoc-like;
+# that is, they have no explicit comment markers like Ruby <tt>#</tt>
+# or C <tt>/* ... */</tt>.
# - An example that shows rendered HTML output
# displays that output in a blockquote:
#
-# Rendered HTML:
# >>>
# Some stuff
#
-# \RDoc-generated documentation is derived from and controlled by:
-#
-# - Single-line or multi-line comments that precede certain definitions;
-# see {Markup in Comments}[rdoc-ref:RDoc::MarkupReference@Markup+in+Comments].
-# - \RDoc directives in trailing comments (on the same line as code);
-# see <tt>:nodoc:</tt>, <tt>:doc:</tt>, and <tt>:notnew</tt>.
-# - \RDoc directives in single-line comments;
-# see other {Directives}[rdoc-ref:RDoc::MarkupReference@Directives].
-# - The Ruby code itself (but not from C code);
-# see {Documentation Derived from Ruby Code}[rdoc-ref:RDoc::MarkupReference@Documentation+Derived+from+Ruby+Code].
-#
-# == Markup in Comments
-#
-# The treatment of markup in comments varies according to the type of file:
-#
-# - <tt>.rb</tt> (Ruby code file): markup is parsed from Ruby comments.
-# - <tt>.c</tt> (C code file): markup is parsed from C comments.
-# - <tt>.rdoc</tt> (RDoc text file): markup is parsed from the entire file.
-#
-# The comment associated with
-# a Ruby class, module, method, alias, constant, or attribute
-# becomes the documentation for that defined object:
-#
-# - In a Ruby file, that comment immediately precedes
-# the definition of the object.
-# - In a C file, that comment immediately precedes
-# the function that implements a method,
-# or otherwise immediately precedes the definition of the object.
+# == \RDoc Sources
#
-# In either a Ruby or a C file,
-# \RDoc ignores comments that do not precede object definitions.
+# The sources of \RDoc documentation vary according to the type of file:
#
-# In an \RDoc file, the text is not associated with any code object,
-# but may (depending on how the documentation is built),
-# become a separate page.
+# - <tt>.rb</tt> (Ruby code file):
#
-# Almost all examples on this page are all RDoc-like;
-# that is, they have no comment markers like Ruby <tt>#</tt>
-# or C <tt>/* ... */</tt>.
+# - Markup may be found in Ruby comments:
+# A comment that immediately precedes the definition
+# of a Ruby class, module, method, alias, constant, or attribute
+# becomes the documentation for that defined object.
+# - An \RDoc directive may be found in:
#
-# === Margins
+# - A trailing comment (on the same line as code);
+# see <tt>:nodoc:</tt>, <tt>:doc:</tt>, and <tt>:notnew:</tt>.
+# - A single-line comment;
+# see other {Directives}[rdoc-ref:RDoc::MarkupReference@Directives].
#
-# In a multi-line comment,
-# \RDoc looks for the comment's natural left margin,
-# which becomes the <em>base margin</em> for the comment
-# and is the initial <em>current margin</em> for the comment.
+# - Documentation may be derived from the Ruby code itself;
+# see {Documentation Derived from Ruby Code}[rdoc-ref:RDoc::MarkupReference@Documentation+Derived+from+Ruby+Code].
#
-# The current margin can change, and does so, for example in a list.
+# - <tt>.c</tt> (C code file): markup is parsed from C comments.
+# A comment that immediately precedes
+# a function that implements a Ruby method,
+# or otherwise immediately precedes the definition of a Ruby object,
+# becomes the documentation for that object.
+# - <tt>.rdoc</tt> (\RDoc markup text file) or <tt>.md</tt> (\RDoc markdown text file):
+# markup is parsed from the entire file.
+# The text is not associated with any code object,
+# but may (depending on how the documentation is built)
+# become a separate page.
+#
+# An <i>RDoc document</i>:
+#
+# - A (possibly multi-line) comment in a Ruby or C file
+# that generates \RDoc documentation (as above).
+# - The entire markup (<tt>.rdoc</tt>) file or markdown (<tt>.md</tt>) file
+# (which is usually multi-line).
#
# === Blocks
#
-# It's convenient to think of \RDoc markup input as a sequence of _blocks_
+# It's convenient to think of an \RDoc document as a sequence of _blocks_
# of various types (details at the links):
#
# - {Paragraph}[rdoc-ref:RDoc::MarkupReference@Paragraphs]:
@@ -88,7 +77,7 @@ require 'rdoc'
# - {List}[rdoc-ref:RDoc::MarkupReference@Lists]: items for
# a bullet list, numbered list, lettered list, or labeled list.
# - {Heading}[rdoc-ref:RDoc::MarkupReference@Headings]:
-# a section heading.
+# a heading.
# - {Horizontal rule}[rdoc-ref:RDoc::MarkupReference@Horizontal+Rules]:
# a line across the rendered page.
# - {Directive}[rdoc-ref:RDoc::MarkupReference@Directives]:
@@ -103,6 +92,10 @@ require 'rdoc'
# - Any block may appear independently
# (that is, not nested in another block);
# some blocks may be nested, as detailed below.
+# - In a multi-line block,
+# \RDoc looks for the block's natural left margin,
+# which becomes the <em>base margin</em> for the block
+# and is the initial <em>current margin</em> for the block.
#
# ==== Paragraphs
#
@@ -467,8 +460,32 @@ require 'rdoc'
#
# - Appended to a line of code
# that defines a class, module, method, alias, constant, or attribute.
+#
# - Specifies that the defined object should not be documented.
#
+# - For a method definition in C code, it the directive must be in the comment line
+# immediately preceding the definition:
+#
+# /* :nodoc: */
+# static VALUE
+# some_method(VALUE self)
+# {
+# return self;
+# }
+#
+# Note that this directive has <em>no effect at all</em>
+# when placed at the method declaration:
+#
+# /* :nodoc: */
+# rb_define_method(cMyClass, "do_something", something_func, 0);
+#
+# The above comment is just a comment and has nothing to do with \RDoc.
+# Therefore, +do_something+ method will be reported as "undocumented"
+# unless that method or function is documented elsewhere.
+#
+# - For a constant definition in C code, this directive <em>can not work</em>
+# because there is no "implementation" place for a constant.
+#
# - <tt># :nodoc: all</tt>:
#
# - Appended to a line of code
@@ -480,7 +497,7 @@ require 'rdoc'
#
# - Appended to a line of code
# that defines a class, module, method, alias, constant, or attribute.
-# - Specifies the defined object should be documented, even if otherwise
+# - Specifies the defined object should be documented, even if it otherwise
# would not be documented.
#
# - <tt># :notnew:</tt> (aliased as <tt>:not_new:</tt> and <tt>:not-new:</tt>):
@@ -502,8 +519,8 @@ require 'rdoc'
# #++
# # Documented.
#
-# For C code, any of directives <tt>:startdoc:</tt>, <tt>:enddoc:</tt>,
-# and <tt>:nodoc:</tt> may appear in a stand-alone comment:
+# For C code, any of directives <tt>:startdoc:</tt>, <tt>:stopdoc:</tt>,
+# and <tt>:enddoc:</tt> may appear in a stand-alone comment:
#
# /* :startdoc: */
# /* :stopdoc: */
@@ -624,9 +641,9 @@ require 'rdoc'
# The file content is shifted to have the same indentation as the colon
# at the start of the directive.
#
-# The file is searched for in the directories
-# given with the <tt>--include</tt> command-line option,
-# or by default in the current directory.
+# The file is searched for in the directory containing the current file,
+# and then in each of the directories given with the <tt>--include</tt>
+# command-line option.
#
# For C code, the directive may appear in a stand-alone comment
#
@@ -1192,13 +1209,26 @@ require 'rdoc'
#
class RDoc::MarkupReference
+ # Example class.
class DummyClass; end
+
+ # Example module.
module DummyModule; end
+
+ # Example singleton method.
def self.dummy_singleton_method(foo, bar); end
+
+ # Example instance method.
def dummy_instance_method(foo, bar); end;
+
alias dummy_instance_alias dummy_instance_method
+
+ # Example attribute.
attr_accessor :dummy_attribute
+
alias dummy_attribute_alias dummy_attribute
+
+ # Example constant.
DUMMY_CONSTANT = ''
# :call-seq:
diff --git a/doc/regexp.rdoc b/doc/regexp.rdoc
deleted file mode 100644
index b9c89b1c86..0000000000
--- a/doc/regexp.rdoc
+++ /dev/null
@@ -1,827 +0,0 @@
-# -*- mode: rdoc; coding: utf-8; fill-column: 74; -*-
-
-Regular expressions (<i>regexp</i>s) are patterns which describe the
-contents of a string. They're used for testing whether a string contains a
-given pattern, or extracting the portions that match. They are created
-with the <tt>/</tt><i>pat</i><tt>/</tt> and
-<tt>%r{</tt><i>pat</i><tt>}</tt> literals or the <tt>Regexp.new</tt>
-constructor.
-
-A regexp is usually delimited with forward slashes (<tt>/</tt>). For
-example:
-
- /hay/ =~ 'haystack' #=> 0
- /y/.match('haystack') #=> #<MatchData "y">
-
-If a string contains the pattern it is said to <i>match</i>. A literal
-string matches itself.
-
-Here 'haystack' does not contain the pattern 'needle', so it doesn't match:
-
- /needle/.match('haystack') #=> nil
-
-Here 'haystack' contains the pattern 'hay', so it matches:
-
- /hay/.match('haystack') #=> #<MatchData "hay">
-
-Specifically, <tt>/st/</tt> requires that the string contains the letter
-_s_ followed by the letter _t_, so it matches _haystack_, also.
-
-Note that any Regexp matching will raise a RuntimeError if timeout is set and
-exceeded. See {"Timeout"}[#label-Timeout] section in detail.
-
-== \Regexp Interpolation
-
-A regexp may contain interpolated strings; trivially:
-
- foo = 'bar'
- /#{foo}/ # => /bar/
-
-== <tt>=~</tt> and Regexp#match
-
-Pattern matching may be achieved by using <tt>=~</tt> operator or Regexp#match
-method.
-
-=== <tt>=~</tt> Operator
-
-<tt>=~</tt> is Ruby's basic pattern-matching operator. When one operand is a
-regular expression and the other is a string then the regular expression is
-used as a pattern to match against the string. (This operator is equivalently
-defined by Regexp and String so the order of String and Regexp do not matter.
-Other classes may have different implementations of <tt>=~</tt>.) If a match
-is found, the operator returns index of first match in string, otherwise it
-returns +nil+.
-
- /hay/ =~ 'haystack' #=> 0
- 'haystack' =~ /hay/ #=> 0
- /a/ =~ 'haystack' #=> 1
- /u/ =~ 'haystack' #=> nil
-
-Using <tt>=~</tt> operator with a String and Regexp the <tt>$~</tt> global
-variable is set after a successful match. <tt>$~</tt> holds a MatchData
-object. Regexp.last_match is equivalent to <tt>$~</tt>.
-
-=== Regexp#match Method
-
-The #match method returns a MatchData object:
-
- /st/.match('haystack') #=> #<MatchData "st">
-
-== Metacharacters and Escapes
-
-The following are <i>metacharacters</i> <tt>(</tt>, <tt>)</tt>,
-<tt>[</tt>, <tt>]</tt>, <tt>{</tt>, <tt>}</tt>, <tt>.</tt>, <tt>?</tt>,
-<tt>+</tt>, <tt>*</tt>. They have a specific meaning when appearing in a
-pattern. To match them literally they must be backslash-escaped. To match
-a backslash literally, backslash-escape it: <tt>\\\\</tt>.
-
- /1 \+ 2 = 3\?/.match('Does 1 + 2 = 3?') #=> #<MatchData "1 + 2 = 3?">
- /a\\\\b/.match('a\\\\b') #=> #<MatchData "a\\b">
-
-Patterns behave like double-quoted strings and can contain the same
-backslash escapes (the meaning of <tt>\s</tt> is different, however,
-see below[#label-Character+Classes]).
-
- /\s\u{6771 4eac 90fd}/.match("Go to æ±äº¬éƒ½")
- #=> #<MatchData " æ±äº¬éƒ½">
-
-Arbitrary Ruby expressions can be embedded into patterns with the
-<tt>#{...}</tt> construct.
-
- place = "æ±äº¬éƒ½"
- /#{place}/.match("Go to æ±äº¬éƒ½")
- #=> #<MatchData "æ±äº¬éƒ½">
-
-== Character Classes
-
-A <i>character class</i> is delimited with square brackets (<tt>[</tt>,
-<tt>]</tt>) and lists characters that may appear at that point in the
-match. <tt>/[ab]/</tt> means _a_ or _b_, as opposed to <tt>/ab/</tt> which
-means _a_ followed by _b_.
-
- /W[aeiou]rd/.match("Word") #=> #<MatchData "Word">
-
-Within a character class the hyphen (<tt>-</tt>) is a metacharacter
-denoting an inclusive range of characters. <tt>[abcd]</tt> is equivalent
-to <tt>[a-d]</tt>. A range can be followed by another range, so
-<tt>[abcdwxyz]</tt> is equivalent to <tt>[a-dw-z]</tt>. The order in which
-ranges or individual characters appear inside a character class is
-irrelevant.
-
- /[0-9a-f]/.match('9f') #=> #<MatchData "9">
- /[9f]/.match('9f') #=> #<MatchData "9">
-
-If the first character of a character class is a caret (<tt>^</tt>) the
-class is inverted: it matches any character _except_ those named.
-
- /[^a-eg-z]/.match('f') #=> #<MatchData "f">
-
-A character class may contain another character class. By itself this
-isn't useful because <tt>[a-z[0-9]]</tt> describes the same set as
-<tt>[a-z0-9]</tt>. However, character classes also support the <tt>&&</tt>
-operator which performs set intersection on its arguments. The two can be
-combined as follows:
-
- /[a-w&&[^c-g]z]/ # ([a-w] AND ([^c-g] OR z))
-
-This is equivalent to:
-
- /[abh-w]/
-
-The following metacharacters also behave like character classes:
-
-* <tt>/./</tt> - Any character except a newline.
-* <tt>/./m</tt> - Any character (the +m+ modifier enables multiline mode)
-* <tt>/\w/</tt> - A word character (<tt>[a-zA-Z0-9_]</tt>)
-* <tt>/\W/</tt> - A non-word character (<tt>[^a-zA-Z0-9_]</tt>).
- Please take a look at {Bug #4044}[https://bugs.ruby-lang.org/issues/4044] if
- using <tt>/\W/</tt> with the <tt>/i</tt> modifier.
-* <tt>/\d/</tt> - A digit character (<tt>[0-9]</tt>)
-* <tt>/\D/</tt> - A non-digit character (<tt>[^0-9]</tt>)
-* <tt>/\h/</tt> - A hexdigit character (<tt>[0-9a-fA-F]</tt>)
-* <tt>/\H/</tt> - A non-hexdigit character (<tt>[^0-9a-fA-F]</tt>)
-* <tt>/\s/</tt> - A whitespace character: <tt>/[ \t\r\n\f\v]/</tt>
-* <tt>/\S/</tt> - A non-whitespace character: <tt>/[^ \t\r\n\f\v]/</tt>
-* <tt>/\R/</tt> - A linebreak: <tt>\n</tt>, <tt>\v</tt>, <tt>\f</tt>, <tt>\r</tt>
- <tt>\u0085</tt> (NEXT LINE), <tt>\u2028</tt> (LINE SEPARATOR), <tt>\u2029</tt> (PARAGRAPH SEPARATOR)
- or <tt>\r\n</tt>.
-
-POSIX <i>bracket expressions</i> are also similar to character classes.
-They provide a portable alternative to the above, with the added benefit
-that they encompass non-ASCII characters. For instance, <tt>/\d/</tt>
-matches only the ASCII decimal digits (0-9); whereas <tt>/[[:digit:]]/</tt>
-matches any character in the Unicode _Nd_ category.
-
-* <tt>/[[:alnum:]]/</tt> - Alphabetic and numeric character
-* <tt>/[[:alpha:]]/</tt> - Alphabetic character
-* <tt>/[[:blank:]]/</tt> - Space or tab
-* <tt>/[[:cntrl:]]/</tt> - Control character
-* <tt>/[[:digit:]]/</tt> - Digit
-* <tt>/[[:graph:]]/</tt> - Non-blank character (excludes spaces, control
- characters, and similar)
-* <tt>/[[:lower:]]/</tt> - Lowercase alphabetical character
-* <tt>/[[:print:]]/</tt> - Like [:graph:], but includes the space character
-* <tt>/[[:punct:]]/</tt> - Punctuation character
-* <tt>/[[:space:]]/</tt> - Whitespace character (<tt>[:blank:]</tt>, newline,
- carriage return, etc.)
-* <tt>/[[:upper:]]/</tt> - Uppercase alphabetical
-* <tt>/[[:xdigit:]]/</tt> - Digit allowed in a hexadecimal number (i.e.,
- 0-9a-fA-F)
-
-Ruby also supports the following non-POSIX character classes:
-
-* <tt>/[[:word:]]/</tt> - A character in one of the following Unicode
- general categories _Letter_, _Mark_, _Number_,
- <i>Connector_Punctuation</i>
-* <tt>/[[:ascii:]]/</tt> - A character in the ASCII character set
-
- # U+06F2 is "EXTENDED ARABIC-INDIC DIGIT TWO"
- /[[:digit:]]/.match("\u06F2") #=> #<MatchData "\u{06F2}">
- /[[:upper:]][[:lower:]]/.match("Hello") #=> #<MatchData "He">
- /[[:xdigit:]][[:xdigit:]]/.match("A6") #=> #<MatchData "A6">
-
-== Repetition
-
-The constructs described so far match a single character. They can be
-followed by a repetition metacharacter to specify how many times they need
-to occur. Such metacharacters are called <i>quantifiers</i>.
-
-* <tt>*</tt> - Zero or more times
-* <tt>+</tt> - One or more times
-* <tt>?</tt> - Zero or one times (optional)
-* <tt>{</tt><i>n</i><tt>}</tt> - Exactly <i>n</i> times
-* <tt>{</tt><i>n</i><tt>,}</tt> - <i>n</i> or more times
-* <tt>{,</tt><i>m</i><tt>}</tt> - <i>m</i> or less times
-* <tt>{</tt><i>n</i><tt>,</tt><i>m</i><tt>}</tt> - At least <i>n</i> and
- at most <i>m</i> times
-
-At least one uppercase character ('H'), at least one lowercase character
-('e'), two 'l' characters, then one 'o':
-
- "Hello".match(/[[:upper:]]+[[:lower:]]+l{2}o/) #=> #<MatchData "Hello">
-
-=== Greedy Match
-
-Repetition is <i>greedy</i> by default: as many occurrences as possible
-are matched while still allowing the overall match to succeed. By
-contrast, <i>lazy</i> matching makes the minimal amount of matches
-necessary for overall success. Most greedy metacharacters can be made lazy
-by following them with <tt>?</tt>. For the <tt>{n}</tt> pattern, because
-it specifies an exact number of characters to match and not a variable
-number of characters, the <tt>?</tt> metacharacter instead makes the
-repeated pattern optional.
-
-Both patterns below match the string. The first uses a greedy quantifier so
-'.+' matches '<a><b>'; the second uses a lazy quantifier so '.+?' matches
-'<a>':
-
- /<.+>/.match("<a><b>") #=> #<MatchData "<a><b>">
- /<.+?>/.match("<a><b>") #=> #<MatchData "<a>">
-
-=== Possessive Match
-
-A quantifier followed by <tt>+</tt> matches <i>possessively</i>: once it
-has matched it does not backtrack. They behave like greedy quantifiers,
-but having matched they refuse to "give up" their match even if this
-jeopardises the overall match.
-
- /<.*><.+>/.match("<a><b>") #=> #<MatchData "<a><b>">
- /<.*+><.+>/.match("<a><b>") #=> nil
- /<.*><.++>/.match("<a><b>") #=> nil
-
-== Capturing
-
-Parentheses can be used for <i>capturing</i>. The text enclosed by the
-<i>n</i>th group of parentheses can be subsequently referred to
-with <i>n</i>. Within a pattern use the <i>backreference</i>
-<tt>\n</tt> (e.g. <tt>\1</tt>); outside of the pattern use
-<tt>MatchData[n]</tt> (e.g. <tt>MatchData[1]</tt>).
-
-In this example, <tt>'at'</tt> is captured by the first group of
-parentheses, then referred to later with <tt>\1</tt>:
-
- /[csh](..) [csh]\1 in/.match("The cat sat in the hat")
- #=> #<MatchData "cat sat in" 1:"at">
-
-Regexp#match returns a MatchData object which makes the captured text
-available with its #[] method:
-
- /[csh](..) [csh]\1 in/.match("The cat sat in the hat")[1] #=> 'at'
-
-While Ruby supports an arbitrary number of numbered captured groups,
-only groups 1-9 are supported using the <tt>\n</tt> backreference
-syntax.
-
-Ruby also supports <tt>\0</tt> as a special backreference, which
-references the entire matched string. This is also available at
-<tt>MatchData[0]</tt>. Note that the <tt>\0</tt> backreference cannot
-be used inside the regexp, as backreferences can only be used after the
-end of the capture group, and the <tt>\0</tt> backreference uses the
-implicit capture group of the entire match. However, you can use
-this backreference when doing substitution:
-
- "The cat sat in the hat".gsub(/[csh]at/, '\0s')
- # => "The cats sats in the hats"
-
-=== Named Captures
-
-Capture groups can be referred to by name when defined with the
-<tt>(?<</tt><i>name</i><tt>>)</tt> or <tt>(?'</tt><i>name</i><tt>')</tt>
-constructs.
-
- /\$(?<dollars>\d+)\.(?<cents>\d+)/.match("$3.67")
- #=> #<MatchData "$3.67" dollars:"3" cents:"67">
- /\$(?<dollars>\d+)\.(?<cents>\d+)/.match("$3.67")[:dollars] #=> "3"
-
-Named groups can be backreferenced with <tt>\k<</tt><i>name</i><tt>></tt>,
-where _name_ is the group name.
-
- /(?<vowel>[aeiou]).\k<vowel>.\k<vowel>/.match('ototomy')
- #=> #<MatchData "ototo" vowel:"o">
-
-*Note*: A regexp can't use named backreferences and numbered
-backreferences simultaneously. Also, if a named capture is used in a
-regexp, then parentheses used for grouping which would otherwise result
-in a unnamed capture are treated as non-capturing.
-
- /(\w)(\w)/.match("ab").captures # => ["a", "b"]
- /(\w)(\w)/.match("ab").named_captures # => {}
-
- /(?<c>\w)(\w)/.match("ab").captures # => ["a"]
- /(?<c>\w)(\w)/.match("ab").named_captures # => {"c"=>"a"}
-
-When named capture groups are used with a literal regexp on the left-hand
-side of an expression and the <tt>=~</tt> operator, the captured text is
-also assigned to local variables with corresponding names.
-
- /\$(?<dollars>\d+)\.(?<cents>\d+)/ =~ "$3.67" #=> 0
- dollars #=> "3"
-
-== Grouping
-
-Parentheses also <i>group</i> the terms they enclose, allowing them to be
-quantified as one <i>atomic</i> whole.
-
-The pattern below matches a vowel followed by 2 word characters:
-
- /[aeiou]\w{2}/.match("Caenorhabditis elegans") #=> #<MatchData "aen">
-
-Whereas the following pattern matches a vowel followed by a word character,
-twice, i.e. <tt>[aeiou]\w[aeiou]\w</tt>: 'enor'.
-
- /([aeiou]\w){2}/.match("Caenorhabditis elegans")
- #=> #<MatchData "enor" 1:"or">
-
-The <tt>(?:</tt>...<tt>)</tt> construct provides grouping without
-capturing. That is, it combines the terms it contains into an atomic whole
-without creating a backreference. This benefits performance at the slight
-expense of readability.
-
-The first group of parentheses captures 'n' and the second 'ti'. The second
-group is referred to later with the backreference <tt>\2</tt>:
-
- /I(n)ves(ti)ga\2ons/.match("Investigations")
- #=> #<MatchData "Investigations" 1:"n" 2:"ti">
-
-The first group of parentheses is now made non-capturing with '?:', so it
-still matches 'n', but doesn't create the backreference. Thus, the
-backreference <tt>\1</tt> now refers to 'ti'.
-
- /I(?:n)ves(ti)ga\1ons/.match("Investigations")
- #=> #<MatchData "Investigations" 1:"ti">
-
-=== Atomic Grouping
-
-Grouping can be made <i>atomic</i> with
-<tt>(?></tt><i>pat</i><tt>)</tt>. This causes the subexpression <i>pat</i>
-to be matched independently of the rest of the expression such that what
-it matches becomes fixed for the remainder of the match, unless the entire
-subexpression must be abandoned and subsequently revisited. In this
-way <i>pat</i> is treated as a non-divisible whole. Atomic grouping is
-typically used to optimise patterns so as to prevent the regular
-expression engine from backtracking needlessly.
-
-The <tt>"</tt> in the pattern below matches the first character of the string,
-then <tt>.*</tt> matches <i>Quote"</i>. This causes the overall match to fail,
-so the text matched by <tt>.*</tt> is backtracked by one position, which
-leaves the final character of the string available to match <tt>"</tt>
-
- /".*"/.match('"Quote"') #=> #<MatchData "\"Quote\"">
-
-If <tt>.*</tt> is grouped atomically, it refuses to backtrack <i>Quote"</i>,
-even though this means that the overall match fails
-
- /"(?>.*)"/.match('"Quote"') #=> nil
-
-== Subexpression Calls
-
-The <tt>\g<</tt><i>name</i><tt>></tt> syntax matches the previous
-subexpression named _name_, which can be a group name or number, again.
-This differs from backreferences in that it re-executes the group rather
-than simply trying to re-match the same text.
-
-This pattern matches a <i>(</i> character and assigns it to the <tt>paren</tt>
-group, tries to call that the <tt>paren</tt> sub-expression again but fails,
-then matches a literal <i>)</i>:
-
- /\A(?<paren>\(\g<paren>*\))*\z/ =~ '()'
-
-
- /\A(?<paren>\(\g<paren>*\))*\z/ =~ '(())' #=> 0
- # ^1
- # ^2
- # ^3
- # ^4
- # ^5
- # ^6
- # ^7
- # ^8
- # ^9
- # ^10
-
-1. Matches at the beginning of the string, i.e. before the first
- character.
-2. Enters a named capture group called <tt>paren</tt>
-3. Matches a literal <i>(</i>, the first character in the string
-4. Calls the <tt>paren</tt> group again, i.e. recurses back to the
- second step
-5. Re-enters the <tt>paren</tt> group
-6. Matches a literal <i>(</i>, the second character in the
- string
-7. Try to call <tt>paren</tt> a third time, but fail because
- doing so would prevent an overall successful match
-8. Match a literal <i>)</i>, the third character in the string.
- Marks the end of the second recursive call
-9. Match a literal <i>)</i>, the fourth character in the string
-10. Match the end of the string
-
-== Alternation
-
-The vertical bar metacharacter (<tt>|</tt>) combines several expressions into
-a single one that matches any of the expressions. Each expression is an
-<i>alternative</i>.
-
- /\w(and|or)\w/.match("Feliformia") #=> #<MatchData "form" 1:"or">
- /\w(and|or)\w/.match("furandi") #=> #<MatchData "randi" 1:"and">
- /\w(and|or)\w/.match("dissemblance") #=> nil
-
-== Condition
-
-The <tt>(?(</tt><i>cond</i><tt>)</tt><i>yes</i><tt>|</tt><i>no</i><tt>)</tt>
-syntax matches _yes_ part if _cond_ is captured, otherwise matches _no_ part.
-In the case _no_ part is empty, also <tt>|</tt> can be omitted.
-
-The _cond_ may be a backreference number or a captured name. A backreference
-number is an absolute position, but can not be a relative position.
-
-== Character Properties
-
-The <tt>\p{}</tt> construct matches characters with the named property,
-much like POSIX bracket classes.
-
-* <tt>/\p{Alnum}/</tt> - Alphabetic and numeric character
-* <tt>/\p{Alpha}/</tt> - Alphabetic character
-* <tt>/\p{Blank}/</tt> - Space or tab
-* <tt>/\p{Cntrl}/</tt> - Control character
-* <tt>/\p{Digit}/</tt> - Digit
-* <tt>/\p{Emoji}/</tt> - Unicode emoji
-* <tt>/\p{Graph}/</tt> - Non-blank character (excludes spaces, control
- characters, and similar)
-* <tt>/\p{Lower}/</tt> - Lowercase alphabetical character
-* <tt>/\p{Print}/</tt> - Like <tt>\p{Graph}</tt>, but includes the space character
-* <tt>/\p{Punct}/</tt> - Punctuation character
-* <tt>/\p{Space}/</tt> - Whitespace character (<tt>[:blank:]</tt>, newline,
- carriage return, etc.)
-* <tt>/\p{Upper}/</tt> - Uppercase alphabetical
-* <tt>/\p{XDigit}/</tt> - Digit allowed in a hexadecimal number (i.e., 0-9a-fA-F)
-* <tt>/\p{Word}/</tt> - A member of one of the following Unicode general
- category <i>Letter</i>, <i>Mark</i>, <i>Number</i>,
- <i>Connector\_Punctuation</i>
-* <tt>/\p{ASCII}/</tt> - A character in the ASCII character set
-* <tt>/\p{Any}/</tt> - Any Unicode character (including unassigned
- characters)
-* <tt>/\p{Assigned}/</tt> - An assigned character
-
-A Unicode character's <i>General Category</i> value can also be matched
-with <tt>\p{</tt><i>Ab</i><tt>}</tt> where <i>Ab</i> is the category's
-abbreviation as described below:
-
-* <tt>/\p{L}/</tt> - 'Letter'
-* <tt>/\p{Ll}/</tt> - 'Letter: Lowercase'
-* <tt>/\p{Lm}/</tt> - 'Letter: Mark'
-* <tt>/\p{Lo}/</tt> - 'Letter: Other'
-* <tt>/\p{Lt}/</tt> - 'Letter: Titlecase'
-* <tt>/\p{Lu}/</tt> - 'Letter: Uppercase
-* <tt>/\p{Lo}/</tt> - 'Letter: Other'
-* <tt>/\p{M}/</tt> - 'Mark'
-* <tt>/\p{Mn}/</tt> - 'Mark: Nonspacing'
-* <tt>/\p{Mc}/</tt> - 'Mark: Spacing Combining'
-* <tt>/\p{Me}/</tt> - 'Mark: Enclosing'
-* <tt>/\p{N}/</tt> - 'Number'
-* <tt>/\p{Nd}/</tt> - 'Number: Decimal Digit'
-* <tt>/\p{Nl}/</tt> - 'Number: Letter'
-* <tt>/\p{No}/</tt> - 'Number: Other'
-* <tt>/\p{P}/</tt> - 'Punctuation'
-* <tt>/\p{Pc}/</tt> - 'Punctuation: Connector'
-* <tt>/\p{Pd}/</tt> - 'Punctuation: Dash'
-* <tt>/\p{Ps}/</tt> - 'Punctuation: Open'
-* <tt>/\p{Pe}/</tt> - 'Punctuation: Close'
-* <tt>/\p{Pi}/</tt> - 'Punctuation: Initial Quote'
-* <tt>/\p{Pf}/</tt> - 'Punctuation: Final Quote'
-* <tt>/\p{Po}/</tt> - 'Punctuation: Other'
-* <tt>/\p{S}/</tt> - 'Symbol'
-* <tt>/\p{Sm}/</tt> - 'Symbol: Math'
-* <tt>/\p{Sc}/</tt> - 'Symbol: Currency'
-* <tt>/\p{Sc}/</tt> - 'Symbol: Currency'
-* <tt>/\p{Sk}/</tt> - 'Symbol: Modifier'
-* <tt>/\p{So}/</tt> - 'Symbol: Other'
-* <tt>/\p{Z}/</tt> - 'Separator'
-* <tt>/\p{Zs}/</tt> - 'Separator: Space'
-* <tt>/\p{Zl}/</tt> - 'Separator: Line'
-* <tt>/\p{Zp}/</tt> - 'Separator: Paragraph'
-* <tt>/\p{C}/</tt> - 'Other'
-* <tt>/\p{Cc}/</tt> - 'Other: Control'
-* <tt>/\p{Cf}/</tt> - 'Other: Format'
-* <tt>/\p{Cn}/</tt> - 'Other: Not Assigned'
-* <tt>/\p{Co}/</tt> - 'Other: Private Use'
-* <tt>/\p{Cs}/</tt> - 'Other: Surrogate'
-
-Lastly, <tt>\p{}</tt> matches a character's Unicode <i>script</i>. The
-following scripts are supported: <i>Arabic</i>, <i>Armenian</i>,
-<i>Balinese</i>, <i>Bengali</i>, <i>Bopomofo</i>, <i>Braille</i>,
-<i>Buginese</i>, <i>Buhid</i>, <i>Canadian_Aboriginal</i>, <i>Carian</i>,
-<i>Cham</i>, <i>Cherokee</i>, <i>Common</i>, <i>Coptic</i>,
-<i>Cuneiform</i>, <i>Cypriot</i>, <i>Cyrillic</i>, <i>Deseret</i>,
-<i>Devanagari</i>, <i>Ethiopic</i>, <i>Georgian</i>, <i>Glagolitic</i>,
-<i>Gothic</i>, <i>Greek</i>, <i>Gujarati</i>, <i>Gurmukhi</i>, <i>Han</i>,
-<i>Hangul</i>, <i>Hanunoo</i>, <i>Hebrew</i>, <i>Hiragana</i>,
-<i>Inherited</i>, <i>Kannada</i>, <i>Katakana</i>, <i>Kayah_Li</i>,
-<i>Kharoshthi</i>, <i>Khmer</i>, <i>Lao</i>, <i>Latin</i>, <i>Lepcha</i>,
-<i>Limbu</i>, <i>Linear_B</i>, <i>Lycian</i>, <i>Lydian</i>,
-<i>Malayalam</i>, <i>Mongolian</i>, <i>Myanmar</i>, <i>New_Tai_Lue</i>,
-<i>Nko</i>, <i>Ogham</i>, <i>Ol_Chiki</i>, <i>Old_Italic</i>,
-<i>Old_Persian</i>, <i>Oriya</i>, <i>Osmanya</i>, <i>Phags_Pa</i>,
-<i>Phoenician</i>, <i>Rejang</i>, <i>Runic</i>, <i>Saurashtra</i>,
-<i>Shavian</i>, <i>Sinhala</i>, <i>Sundanese</i>, <i>Syloti_Nagri</i>,
-<i>Syriac</i>, <i>Tagalog</i>, <i>Tagbanwa</i>, <i>Tai_Le</i>,
-<i>Tamil</i>, <i>Telugu</i>, <i>Thaana</i>, <i>Thai</i>, <i>Tibetan</i>,
-<i>Tifinagh</i>, <i>Ugaritic</i>, <i>Vai</i>, and <i>Yi</i>.
-
-Unicode codepoint U+06E9 is named "ARABIC PLACE OF SAJDAH" and belongs to the
-Arabic script:
-
- /\p{Arabic}/.match("\u06E9") #=> #<MatchData "\u06E9">
-
-All character properties can be inverted by prefixing their name with a
-caret (<tt>^</tt>).
-
-Letter 'A' is not in the Unicode Ll (Letter; Lowercase) category, so this
-match succeeds:
-
- /\p{^Ll}/.match("A") #=> #<MatchData "A">
-
-== Anchors
-
-Anchors are metacharacter that match the zero-width positions between
-characters, <i>anchoring</i> the match to a specific position.
-
-* <tt>^</tt> - Matches beginning of line
-* <tt>$</tt> - Matches end of line
-* <tt>\A</tt> - Matches beginning of string.
-* <tt>\Z</tt> - Matches end of string. If string ends with a newline,
- it matches just before newline
-* <tt>\z</tt> - Matches end of string
-* <tt>\G</tt> - Matches first matching position:
-
- In methods like <tt>String#gsub</tt> and <tt>String#scan</tt>, it changes on each iteration.
- It initially matches the beginning of subject, and in each following iteration it matches where the last match finished.
-
- " a b c".gsub(/ /, '_') #=> "____a_b_c"
- " a b c".gsub(/\G /, '_') #=> "____a b c"
-
- In methods like <tt>Regexp#match</tt> and <tt>String#match</tt> that take an (optional) offset, it matches where the search begins.
-
- "hello, world".match(/,/, 3) #=> #<MatchData ",">
- "hello, world".match(/\G,/, 3) #=> nil
-
-* <tt>\b</tt> - Matches word boundaries when outside brackets;
- backspace (0x08) when inside brackets
-* <tt>\B</tt> - Matches non-word boundaries
-* <tt>(?=</tt><i>pat</i><tt>)</tt> - <i>Positive lookahead</i> assertion:
- ensures that the following characters match <i>pat</i>, but doesn't
- include those characters in the matched text
-* <tt>(?!</tt><i>pat</i><tt>)</tt> - <i>Negative lookahead</i> assertion:
- ensures that the following characters do not match <i>pat</i>, but
- doesn't include those characters in the matched text
-* <tt>(?<=</tt><i>pat</i><tt>)</tt> - <i>Positive lookbehind</i>
- assertion: ensures that the preceding characters match <i>pat</i>, but
- doesn't include those characters in the matched text
-* <tt>(?<!</tt><i>pat</i><tt>)</tt> - <i>Negative lookbehind</i>
- assertion: ensures that the preceding characters do not match
- <i>pat</i>, but doesn't include those characters in the matched text
-
-* <tt>\K</tt> - <i>Match reset</i>: the matched content preceding
- <tt>\K</tt> in the regexp is excluded from the result. For example,
- the following two regexps are almost equivalent:
-
- /ab\Kc/ =~ "abc" #=> 0
- /(?<=ab)c/ =~ "abc" #=> 2
-
- These match same string and <i>$&</i> equals <tt>"c"</tt>, while the
- matched position is different.
-
- As are the following two regexps:
-
- /(a)\K(b)\Kc/
- /(?<=(?<=(a))(b))c/
-
-If a pattern isn't anchored it can begin at any point in the string:
-
- /real/.match("surrealist") #=> #<MatchData "real">
-
-Anchoring the pattern to the beginning of the string forces the match to start
-there. 'real' doesn't occur at the beginning of the string, so now the match
-fails:
-
- /\Areal/.match("surrealist") #=> nil
-
-The match below fails because although 'Demand' contains 'and', the pattern
-does not occur at a word boundary.
-
- /\band/.match("Demand")
-
-Whereas in the following example 'and' has been anchored to a non-word
-boundary so instead of matching the first 'and' it matches from the fourth
-letter of 'demand' instead:
-
- /\Band.+/.match("Supply and demand curve") #=> #<MatchData "and curve">
-
-The pattern below uses positive lookahead and positive lookbehind to match
-text appearing in <b></b> tags without including the tags in the match:
-
- /(?<=<b>)\w+(?=<\/b>)/.match("Fortune favours the <b>bold</b>")
- #=> #<MatchData "bold">
-
-== Absent operator
-
-Absent operator <tt>(?~</tt><i>pat</i><tt>)</tt> matches string which does
-not match <i>pat</i>.
-
-For example, a regexp to match C comment, which is enclosed by <tt>/*</tt>
-and <tt>*/</tt> and does not include <tt>*/</tt>, using absent operator:
-
- %r[/\*(?~\*/)\*/] =~ "/* comment */ not-comment */"
- #=> #<MatchData "/* comment */">
-
-This is often shorter and clearer than without absent operator:
-
- %r[/\*[^\*]*\*+(?:[^\*/][^\*]*\*+)*/]
- %r[/\*(?:(?!\*/).)*\*/]
- %r[/\*(?>.*?\*/)]
-
-== Options
-
-The end delimiter for a regexp can be followed by one or more single-letter
-options which control how the pattern can match.
-
-* <tt>/pat/i</tt> - Ignore case
-* <tt>/pat/m</tt> - Treat a newline as a character matched by <tt>.</tt>
-* <tt>/pat/x</tt> - Ignore whitespace and comments in the pattern
-* <tt>/pat/o</tt> - Perform <tt>#{}</tt> interpolation only once
-
-<tt>i</tt>, <tt>m</tt>, and <tt>x</tt> can also be applied on the
-subexpression level with the
-<tt>(?</tt><i>on</i><tt>-</tt><i>off</i><tt>)</tt> construct, which
-enables options <i>on</i>, and disables options <i>off</i> for the
-expression enclosed by the parentheses:
-
- /a(?i:b)c/.match('aBc') #=> #<MatchData "aBc">
- /a(?-i:b)c/i.match('ABC') #=> nil
-
-Additionally, these options can also be toggled for the remainder of the
-pattern:
-
- /a(?i)bc/.match('abC') #=> #<MatchData "abC">
-
-Options may also be used with <tt>Regexp.new</tt>:
-
- Regexp.new("abc", Regexp::IGNORECASE) #=> /abc/i
- Regexp.new("abc", Regexp::MULTILINE) #=> /abc/m
- Regexp.new("abc # Comment", Regexp::EXTENDED) #=> /abc # Comment/x
- Regexp.new("abc", Regexp::IGNORECASE | Regexp::MULTILINE) #=> /abc/mi
-
- Regexp.new("abc", "i") #=> /abc/i
- Regexp.new("abc", "m") #=> /abc/m
- Regexp.new("abc # Comment", "x") #=> /abc # Comment/x
- Regexp.new("abc", "im") #=> /abc/mi
-
-== Free-Spacing Mode and Comments
-
-As mentioned above, the <tt>x</tt> option enables <i>free-spacing</i>
-mode. Literal white space inside the pattern is ignored, and the
-octothorpe (<tt>#</tt>) character introduces a comment until the end of
-the line. This allows the components of the pattern to be organized in a
-potentially more readable fashion.
-
-A contrived pattern to match a number with optional decimal places:
-
- float_pat = /\A
- [[:digit:]]+ # 1 or more digits before the decimal point
- (\. # Decimal point
- [[:digit:]]+ # 1 or more digits after the decimal point
- )? # The decimal point and following digits are optional
- \Z/x
- float_pat.match('3.14') #=> #<MatchData "3.14" 1:".14">
-
-There are a number of strategies for matching whitespace:
-
-* Use a pattern such as <tt>\s</tt> or <tt>\p{Space}</tt>.
-* Use escaped whitespace such as <tt>\ </tt>, i.e. a space preceded by a backslash.
-* Use a character class such as <tt>[ ]</tt>.
-
-Comments can be included in a non-<tt>x</tt> pattern with the
-<tt>(?#</tt><i>comment</i><tt>)</tt> construct, where <i>comment</i> is
-arbitrary text ignored by the regexp engine.
-
-Comments in regexp literals cannot include unescaped terminator
-characters.
-
-== Encoding
-
-Regular expressions are assumed to use the source encoding. This can be
-overridden with one of the following modifiers.
-
-* <tt>/</tt><i>pat</i><tt>/u</tt> - UTF-8
-* <tt>/</tt><i>pat</i><tt>/e</tt> - EUC-JP
-* <tt>/</tt><i>pat</i><tt>/s</tt> - Windows-31J
-* <tt>/</tt><i>pat</i><tt>/n</tt> - ASCII-8BIT
-
-A regexp can be matched against a string when they either share an
-encoding, or the regexp's encoding is _US-ASCII_ and the string's encoding
-is ASCII-compatible.
-
-If a match between incompatible encodings is attempted an
-<tt>Encoding::CompatibilityError</tt> exception is raised.
-
-The <tt>Regexp#fixed_encoding?</tt> predicate indicates whether the regexp
-has a <i>fixed</i> encoding, that is one incompatible with ASCII. A
-regexp's encoding can be explicitly fixed by supplying
-<tt>Regexp::FIXEDENCODING</tt> as the second argument of
-<tt>Regexp.new</tt>:
-
- r = Regexp.new("a".force_encoding("iso-8859-1"),Regexp::FIXEDENCODING)
- r =~ "a\u3042"
- # raises Encoding::CompatibilityError: incompatible encoding regexp match
- # (ISO-8859-1 regexp with UTF-8 string)
-
-== \Regexp Global Variables
-
-Pattern matching sets some global variables :
-
-* <tt>$~</tt> is equivalent to Regexp.last_match;
-* <tt>$&</tt> contains the complete matched text;
-* <tt>$`</tt> contains string before match;
-* <tt>$'</tt> contains string after match;
-* <tt>$1</tt>, <tt>$2</tt> and so on contain text matching first, second, etc
- capture group;
-* <tt>$+</tt> contains last capture group.
-
-Example:
-
- m = /s(\w{2}).*(c)/.match('haystack') #=> #<MatchData "stac" 1:"ta" 2:"c">
- $~ #=> #<MatchData "stac" 1:"ta" 2:"c">
- Regexp.last_match #=> #<MatchData "stac" 1:"ta" 2:"c">
-
- $& #=> "stac"
- # same as m[0]
- $` #=> "hay"
- # same as m.pre_match
- $' #=> "k"
- # same as m.post_match
- $1 #=> "ta"
- # same as m[1]
- $2 #=> "c"
- # same as m[2]
- $3 #=> nil
- # no third group in pattern
- $+ #=> "c"
- # same as m[-1]
-
-These global variables are thread-local and method-local variables.
-
-== Performance
-
-Certain pathological combinations of constructs can lead to abysmally bad
-performance.
-
-Consider a string of 25 <i>a</i>s, a <i>d</i>, 4 <i>a</i>s, and a
-<i>c</i>.
-
- s = 'a' * 25 + 'd' + 'a' * 4 + 'c'
- #=> "aaaaaaaaaaaaaaaaaaaaaaaaadaaaac"
-
-The following patterns match instantly as you would expect:
-
- /(b|a)/ =~ s #=> 0
- /(b|a+)/ =~ s #=> 0
- /(b|a+)*/ =~ s #=> 0
-
-However, the following pattern takes appreciably longer:
-
- /(b|a+)*c/ =~ s #=> 26
-
-This happens because an atom in the regexp is quantified by both an
-immediate <tt>+</tt> and an enclosing <tt>*</tt> with nothing to
-differentiate which is in control of any particular character. The
-nondeterminism that results produces super-linear performance. (Consult
-<i>Mastering Regular Expressions</i> (3rd ed.), pp 222, by
-<i>Jeffery Friedl</i>, for an in-depth analysis). This particular case
-can be fixed by use of atomic grouping, which prevents the unnecessary
-backtracking:
-
- (start = Time.now) && /(b|a+)*c/ =~ s && (Time.now - start)
- #=> 24.702736882
- (start = Time.now) && /(?>b|a+)*c/ =~ s && (Time.now - start)
- #=> 0.000166571
-
-A similar case is typified by the following example, which takes
-approximately 60 seconds to execute for me:
-
-Match a string of 29 <i>a</i>s against a pattern of 29 optional <i>a</i>s
-followed by 29 mandatory <i>a</i>s:
-
- Regexp.new('a?' * 29 + 'a' * 29) =~ 'a' * 29
-
-The 29 optional <i>a</i>s match the string, but this prevents the 29
-mandatory <i>a</i>s that follow from matching. Ruby must then backtrack
-repeatedly so as to satisfy as many of the optional matches as it can
-while still matching the mandatory 29. It is plain to us that none of the
-optional matches can succeed, but this fact unfortunately eludes Ruby.
-
-The best way to improve performance is to significantly reduce the amount of
-backtracking needed. For this case, instead of individually matching 29
-optional <i>a</i>s, a range of optional <i>a</i>s can be matched all at once
-with <i>a{0,29}</i>:
-
- Regexp.new('a{0,29}' + 'a' * 29) =~ 'a' * 29
-
-== Timeout
-
-There are two APIs to set timeout. One is Regexp.timeout=, which is
-process-global configuration of timeout for Regexp matching.
-
- Regexp.timeout = 3
- s = 'a' * 25 + 'd' + 'a' * 4 + 'c'
- /(b|a+)*c/ =~ s #=> This raises an exception in three seconds
-
-The other is timeout keyword of Regexp.new.
-
- re = Regexp.new("(b|a+)*c", timeout: 3)
- s = 'a' * 25 + 'd' + 'a' * 4 + 'c'
- /(b|a+)*c/ =~ s #=> This raises an exception in three seconds
-
-When using Regexps to process untrusted input, you should use the timeout
-feature to avoid excessive backtracking. Otherwise, a malicious user can
-provide input to Regexp causing Denial-of-Service attack.
-Note that the timeout is not set by default because an appropriate limit
-highly depends on an application requirement and context.
diff --git a/doc/regexp/methods.rdoc b/doc/regexp/methods.rdoc
new file mode 100644
index 0000000000..356156ac9a
--- /dev/null
+++ b/doc/regexp/methods.rdoc
@@ -0,0 +1,41 @@
+== \Regexp Methods
+
+Each of these Ruby core methods can accept a regexp as an argument:
+
+- Enumerable#all?
+- Enumerable#any?
+- Enumerable#grep
+- Enumerable#grep_v
+- Enumerable#none?
+- Enumerable#one?
+- Enumerable#slice_after
+- Enumerable#slice_before
+- Regexp#=~
+- Regexp#match
+- Regexp#match?
+- Regexp.new
+- Regexp.union
+- String#=~
+- String#[]=
+- String#byteindex
+- String#byterindex
+- String#gsub
+- String#gsub!
+- String#index
+- String#match
+- String#match?
+- String#partition
+- String#rindex
+- String#rpartition
+- String#scan
+- String#slice
+- String#slice!
+- String#split
+- String#start_with?
+- String#sub
+- String#sub!
+- Symbol#=~
+- Symbol#match
+- Symbol#match?
+- Symbol#slice
+- Symbol#start_with?
diff --git a/doc/regexp/unicode_properties.rdoc b/doc/regexp/unicode_properties.rdoc
new file mode 100644
index 0000000000..a1d7ecc380
--- /dev/null
+++ b/doc/regexp/unicode_properties.rdoc
@@ -0,0 +1,678 @@
+== \Regexps Based on Unicode Properties
+
+The properties shown here are those currently supported in Ruby.
+Older versions may not support all of these.
+
+=== POSIX brackets
+
+- <tt>\p{ASCII}</tt>
+- <tt>\p{Alnum}</tt>
+- <tt>\p{Alphabetic}</tt>, <tt>\p{Alpha}</tt>
+- <tt>\p{Blank}</tt>
+- <tt>\p{Cntrl}</tt>
+- <tt>\p{Digit}</tt>
+- <tt>\p{Graph}</tt>
+- <tt>\p{Lowercase}</tt>, <tt>\p{Lower}</tt>
+- <tt>\p{Print}</tt>
+- <tt>\p{Punct}</tt>
+- <tt>\p{Space}</tt>
+- <tt>\p{Uppercase}</tt>, <tt>\p{Upper}</tt>
+- <tt>\p{Word}</tt>
+- <tt>\p{XDigit}</tt>
+- <tt>\p{XPosixPunct}</tt>
+
+=== Special
+
+- <tt>\p{Any}</tt>
+- <tt>\p{Assigned}</tt>
+
+=== Major and General Categories
+
+- <tt>\p{Cased_Letter}</tt>, <tt>\p{LC}</tt>
+- <tt>\p{Close_Punctuation}</tt>, <tt>\p{Pe}</tt>
+- <tt>\p{Connector_Punctuation}</tt>, <tt>\p{Pc}</tt>
+- <tt>\p{Control}</tt>, <tt>\p{Cc}</tt>
+- <tt>\p{Currency_Symbol}</tt>, <tt>\p{Sc}</tt>
+- <tt>\p{Dash_Punctuation}</tt>, <tt>\p{Pd}</tt>
+- <tt>\p{Decimal_Number}</tt>, <tt>\p{Nd}</tt>
+- <tt>\p{Enclosing_Mark}</tt>, <tt>\p{Me}</tt>
+- <tt>\p{Final_Punctuation}</tt>, <tt>\p{Pf}</tt>
+- <tt>\p{Format}</tt>, <tt>\p{Cf}</tt>
+- <tt>\p{Initial_Punctuation}</tt>, <tt>\p{Pi}</tt>
+- <tt>\p{Letter}</tt>, <tt>\p{L}</tt>
+- <tt>\p{Letter_Number}</tt>, <tt>\p{Nl}</tt>
+- <tt>\p{Line_Separator}</tt>, <tt>\p{Zl}</tt>
+- <tt>\p{Lowercase_Letter}</tt>, <tt>\p{Ll}</tt>
+- <tt>\p{Mark}</tt>, <tt>\p{M}</tt>
+- <tt>\p{Math_Symbol}</tt>, <tt>\p{Sm}</tt>
+- <tt>\p{Modifier_Letter}</tt>, <tt>\p{Lm}</tt>
+- <tt>\p{Modifier_Symbol}</tt>, <tt>\p{Sk}</tt>
+- <tt>\p{Nonspacing_Mark}</tt>, <tt>\p{Mn}</tt>
+- <tt>\p{Number}</tt>, <tt>\p{N}</tt>
+- <tt>\p{Open_Punctuation}</tt>, <tt>\p{Ps}</tt>
+- <tt>\p{Other}</tt>, <tt>\p{C}</tt>
+- <tt>\p{Other_Letter}</tt>, <tt>\p{Lo}</tt>
+- <tt>\p{Other_Number}</tt>, <tt>\p{No}</tt>
+- <tt>\p{Other_Punctuation}</tt>, <tt>\p{Po}</tt>
+- <tt>\p{Other_Symbol}</tt>, <tt>\p{So}</tt>
+- <tt>\p{Paragraph_Separator}</tt>, <tt>\p{Zp}</tt>
+- <tt>\p{Private_Use}</tt>, <tt>\p{Co}</tt>
+- <tt>\p{Punctuation}</tt>, <tt>\p{P}</tt>
+- <tt>\p{Separator}</tt>, <tt>\p{Z}</tt>
+- <tt>\p{Space_Separator}</tt>, <tt>\p{Zs}</tt>
+- <tt>\p{Spacing_Mark}</tt>, <tt>\p{Mc}</tt>
+- <tt>\p{Surrogate}</tt>, <tt>\p{Cs}</tt>
+- <tt>\p{Symbol}</tt>, <tt>\p{S}</tt>
+- <tt>\p{Titlecase_Letter}</tt>, <tt>\p{Lt}</tt>
+- <tt>\p{Unassigned}</tt>, <tt>\p{Cn}</tt>
+- <tt>\p{Uppercase_Letter}</tt>, <tt>\p{Lu}</tt>
+
+=== Prop List
+
+- <tt>\p{ASCII_Hex_Digit}</tt>, <tt>\p{AHex}</tt>
+- <tt>\p{Bidi_Control}</tt>, <tt>\p{Bidi_C}</tt>
+- <tt>\p{Dash}</tt>
+- <tt>\p{Deprecated}</tt>, <tt>\p{Dep}</tt>
+- <tt>\p{Diacritic}</tt>, <tt>\p{Dia}</tt>
+- <tt>\p{Extender}</tt>, <tt>\p{Ext}</tt>
+- <tt>\p{Hex_Digit}</tt>, <tt>\p{Hex}</tt>
+- <tt>\p{Hyphen}</tt>
+- <tt>\p{IDS_Binary_Operator}</tt>, <tt>\p{IDSB}</tt>
+- <tt>\p{IDS_Trinary_Operator}</tt>, <tt>\p{IDST}</tt>
+- <tt>\p{Ideographic}</tt>, <tt>\p{Ideo}</tt>
+- <tt>\p{Join_Control}</tt>, <tt>\p{Join_C}</tt>
+- <tt>\p{Logical_Order_Exception}</tt>, <tt>\p{LOE}</tt>
+- <tt>\p{Noncharacter_Code_Point}</tt>, <tt>\p{NChar}</tt>
+- <tt>\p{Other_Alphabetic}</tt>, <tt>\p{OAlpha}</tt>
+- <tt>\p{Other_Default_Ignorable_Code_Point}</tt>, <tt>\p{ODI}</tt>
+- <tt>\p{Other_Grapheme_Extend}</tt>, <tt>\p{OGr_Ext}</tt>
+- <tt>\p{Other_ID_Continue}</tt>, <tt>\p{OIDC}</tt>
+- <tt>\p{Other_ID_Start}</tt>, <tt>\p{OIDS}</tt>
+- <tt>\p{Other_Lowercase}</tt>, <tt>\p{OLower}</tt>
+- <tt>\p{Other_Math}</tt>, <tt>\p{OMath}</tt>
+- <tt>\p{Other_Uppercase}</tt>, <tt>\p{OUpper}</tt>
+- <tt>\p{Pattern_Syntax}</tt>, <tt>\p{Pat_Syn}</tt>
+- <tt>\p{Pattern_White_Space}</tt>, <tt>\p{Pat_WS}</tt>
+- <tt>\p{Prepended_Concatenation_Mark}</tt>, <tt>\p{PCM}</tt>
+- <tt>\p{Quotation_Mark}</tt>, <tt>\p{QMark}</tt>
+- <tt>\p{Radical}</tt>
+- <tt>\p{Regional_Indicator}</tt>, <tt>\p{RI}</tt>
+- <tt>\p{Sentence_Terminal}</tt>, <tt>\p{STerm}</tt>
+- <tt>\p{Soft_Dotted}</tt>, <tt>\p{SD}</tt>
+- <tt>\p{Terminal_Punctuation}</tt>, <tt>\p{Term}</tt>
+- <tt>\p{Unified_Ideograph}</tt>, <tt>\p{UIdeo}</tt>
+- <tt>\p{Variation_Selector}</tt>, <tt>\p{VS}</tt>
+- <tt>\p{White_Space}</tt>, <tt>\p{WSpace}</tt>
+
+=== Derived Core Properties
+
+- <tt>\p{Alphabetic}</tt>, <tt>\p{Alpha}</tt>
+- <tt>\p{Case_Ignorable}</tt>, <tt>\p{CI}</tt>
+- <tt>\p{Cased}</tt>
+- <tt>\p{Changes_When_Casefolded}</tt>, <tt>\p{CWCF}</tt>
+- <tt>\p{Changes_When_Casemapped}</tt>, <tt>\p{CWCM}</tt>
+- <tt>\p{Changes_When_Lowercased}</tt>, <tt>\p{CWL}</tt>
+- <tt>\p{Changes_When_Titlecased}</tt>, <tt>\p{CWT}</tt>
+- <tt>\p{Changes_When_Uppercased}</tt>, <tt>\p{CWU}</tt>
+- <tt>\p{Default_Ignorable_Code_Point}</tt>, <tt>\p{DI}</tt>
+- <tt>\p{Grapheme_Base}</tt>, <tt>\p{Gr_Base}</tt>
+- <tt>\p{Grapheme_Extend}</tt>, <tt>\p{Gr_Ext}</tt>
+- <tt>\p{Grapheme_Link}</tt>, <tt>\p{Gr_Link}</tt>
+- <tt>\p{ID_Continue}</tt>, <tt>\p{IDC}</tt>
+- <tt>\p{ID_Start}</tt>, <tt>\p{IDS}</tt>
+- <tt>\p{Lowercase}</tt>, <tt>\p{Lower}</tt>
+- <tt>\p{Math}</tt>
+- <tt>\p{Uppercase}</tt>, <tt>\p{Upper}</tt>
+- <tt>\p{XID_Continue}</tt>, <tt>\p{XIDC}</tt>
+- <tt>\p{XID_Start}</tt>, <tt>\p{XIDS}</tt>
+
+=== Scripts
+
+- <tt>\p{Adlam}</tt>, <tt>\p{Adlm}</tt>
+- <tt>\p{Ahom}</tt>
+- <tt>\p{Anatolian_Hieroglyphs}</tt>, <tt>\p{Hluw}</tt>
+- <tt>\p{Arabic}</tt>, <tt>\p{Arab}</tt>
+- <tt>\p{Armenian}</tt>, <tt>\p{Armn}</tt>
+- <tt>\p{Avestan}</tt>, <tt>\p{Avst}</tt>
+- <tt>\p{Balinese}</tt>, <tt>\p{Bali}</tt>
+- <tt>\p{Bamum}</tt>, <tt>\p{Bamu}</tt>
+- <tt>\p{Bassa_Vah}</tt>, <tt>\p{Bass}</tt>
+- <tt>\p{Batak}</tt>, <tt>\p{Batk}</tt>
+- <tt>\p{Bengali}</tt>, <tt>\p{Beng}</tt>
+- <tt>\p{Bhaiksuki}</tt>, <tt>\p{Bhks}</tt>
+- <tt>\p{Bopomofo}</tt>, <tt>\p{Bopo}</tt>
+- <tt>\p{Brahmi}</tt>, <tt>\p{Brah}</tt>
+- <tt>\p{Braille}</tt>, <tt>\p{Brai}</tt>
+- <tt>\p{Buginese}</tt>, <tt>\p{Bugi}</tt>
+- <tt>\p{Buhid}</tt>, <tt>\p{Buhd}</tt>
+- <tt>\p{Canadian_Aboriginal}</tt>, <tt>\p{Cans}</tt>
+- <tt>\p{Carian}</tt>, <tt>\p{Cari}</tt>
+- <tt>\p{Caucasian_Albanian}</tt>, <tt>\p{Aghb}</tt>
+- <tt>\p{Chakma}</tt>, <tt>\p{Cakm}</tt>
+- <tt>\p{Cham}</tt>
+- <tt>\p{Cherokee}</tt>, <tt>\p{Cher}</tt>
+- <tt>\p{Chorasmian}</tt>, <tt>\p{Chrs}</tt>
+- <tt>\p{Common}</tt>, <tt>\p{Zyyy}</tt>
+- <tt>\p{Coptic}</tt>, <tt>\p{Copt}</tt>
+- <tt>\p{Cuneiform}</tt>, <tt>\p{Xsux}</tt>
+- <tt>\p{Cypriot}</tt>, <tt>\p{Cprt}</tt>
+- <tt>\p{Cypro_Minoan}</tt>, <tt>\p{Cpmn}</tt>
+- <tt>\p{Cyrillic}</tt>, <tt>\p{Cyrl}</tt>
+- <tt>\p{Deseret}</tt>, <tt>\p{Dsrt}</tt>
+- <tt>\p{Devanagari}</tt>, <tt>\p{Deva}</tt>
+- <tt>\p{Dives_Akuru}</tt>, <tt>\p{Diak}</tt>
+- <tt>\p{Dogra}</tt>, <tt>\p{Dogr}</tt>
+- <tt>\p{Duployan}</tt>, <tt>\p{Dupl}</tt>
+- <tt>\p{Egyptian_Hieroglyphs}</tt>, <tt>\p{Egyp}</tt>
+- <tt>\p{Elbasan}</tt>, <tt>\p{Elba}</tt>
+- <tt>\p{Elymaic}</tt>, <tt>\p{Elym}</tt>
+- <tt>\p{Ethiopic}</tt>, <tt>\p{Ethi}</tt>
+- <tt>\p{Georgian}</tt>, <tt>\p{Geor}</tt>
+- <tt>\p{Glagolitic}</tt>, <tt>\p{Glag}</tt>
+- <tt>\p{Gothic}</tt>, <tt>\p{Goth}</tt>
+- <tt>\p{Grantha}</tt>, <tt>\p{Gran}</tt>
+- <tt>\p{Greek}</tt>, <tt>\p{Grek}</tt>
+- <tt>\p{Gujarati}</tt>, <tt>\p{Gujr}</tt>
+- <tt>\p{Gunjala_Gondi}</tt>, <tt>\p{Gong}</tt>
+- <tt>\p{Gurmukhi}</tt>, <tt>\p{Guru}</tt>
+- <tt>\p{Han}</tt>, <tt>\p{Hani}</tt>
+- <tt>\p{Hangul}</tt>, <tt>\p{Hang}</tt>
+- <tt>\p{Hanifi_Rohingya}</tt>, <tt>\p{Rohg}</tt>
+- <tt>\p{Hanunoo}</tt>, <tt>\p{Hano}</tt>
+- <tt>\p{Hatran}</tt>, <tt>\p{Hatr}</tt>
+- <tt>\p{Hebrew}</tt>, <tt>\p{Hebr}</tt>
+- <tt>\p{Hiragana}</tt>, <tt>\p{Hira}</tt>
+- <tt>\p{Imperial_Aramaic}</tt>, <tt>\p{Armi}</tt>
+- <tt>\p{Inherited}</tt>, <tt>\p{Zinh}</tt>
+- <tt>\p{Inscriptional_Pahlavi}</tt>, <tt>\p{Phli}</tt>
+- <tt>\p{Inscriptional_Parthian}</tt>, <tt>\p{Prti}</tt>
+- <tt>\p{Javanese}</tt>, <tt>\p{Java}</tt>
+- <tt>\p{Kaithi}</tt>, <tt>\p{Kthi}</tt>
+- <tt>\p{Kannada}</tt>, <tt>\p{Knda}</tt>
+- <tt>\p{Katakana}</tt>, <tt>\p{Kana}</tt>
+- <tt>\p{Kawi}</tt>
+- <tt>\p{Kayah_Li}</tt>, <tt>\p{Kali}</tt>
+- <tt>\p{Kharoshthi}</tt>, <tt>\p{Khar}</tt>
+- <tt>\p{Khitan_Small_Script}</tt>, <tt>\p{Kits}</tt>
+- <tt>\p{Khmer}</tt>, <tt>\p{Khmr}</tt>
+- <tt>\p{Khojki}</tt>, <tt>\p{Khoj}</tt>
+- <tt>\p{Khudawadi}</tt>, <tt>\p{Sind}</tt>
+- <tt>\p{Lao}</tt>, <tt>\p{Laoo}</tt>
+- <tt>\p{Latin}</tt>, <tt>\p{Latn}</tt>
+- <tt>\p{Lepcha}</tt>, <tt>\p{Lepc}</tt>
+- <tt>\p{Limbu}</tt>, <tt>\p{Limb}</tt>
+- <tt>\p{Linear_A}</tt>, <tt>\p{Lina}</tt>
+- <tt>\p{Linear_B}</tt>, <tt>\p{Linb}</tt>
+- <tt>\p{Lisu}</tt>
+- <tt>\p{Lycian}</tt>, <tt>\p{Lyci}</tt>
+- <tt>\p{Lydian}</tt>, <tt>\p{Lydi}</tt>
+- <tt>\p{Mahajani}</tt>, <tt>\p{Mahj}</tt>
+- <tt>\p{Makasar}</tt>, <tt>\p{Maka}</tt>
+- <tt>\p{Malayalam}</tt>, <tt>\p{Mlym}</tt>
+- <tt>\p{Mandaic}</tt>, <tt>\p{Mand}</tt>
+- <tt>\p{Manichaean}</tt>, <tt>\p{Mani}</tt>
+- <tt>\p{Marchen}</tt>, <tt>\p{Marc}</tt>
+- <tt>\p{Masaram_Gondi}</tt>, <tt>\p{Gonm}</tt>
+- <tt>\p{Medefaidrin}</tt>, <tt>\p{Medf}</tt>
+- <tt>\p{Meetei_Mayek}</tt>, <tt>\p{Mtei}</tt>
+- <tt>\p{Mende_Kikakui}</tt>, <tt>\p{Mend}</tt>
+- <tt>\p{Meroitic_Cursive}</tt>, <tt>\p{Merc}</tt>
+- <tt>\p{Meroitic_Hieroglyphs}</tt>, <tt>\p{Mero}</tt>
+- <tt>\p{Miao}</tt>, <tt>\p{Plrd}</tt>
+- <tt>\p{Modi}</tt>
+- <tt>\p{Mongolian}</tt>, <tt>\p{Mong}</tt>
+- <tt>\p{Mro}</tt>, <tt>\p{Mroo}</tt>
+- <tt>\p{Multani}</tt>, <tt>\p{Mult}</tt>
+- <tt>\p{Myanmar}</tt>, <tt>\p{Mymr}</tt>
+- <tt>\p{Nabataean}</tt>, <tt>\p{Nbat}</tt>
+- <tt>\p{Nag_Mundari}</tt>, <tt>\p{Nagm}</tt>
+- <tt>\p{Nandinagari}</tt>, <tt>\p{Nand}</tt>
+- <tt>\p{New_Tai_Lue}</tt>, <tt>\p{Talu}</tt>
+- <tt>\p{Newa}</tt>
+- <tt>\p{Nko}</tt>, <tt>\p{Nkoo}</tt>
+- <tt>\p{Nushu}</tt>, <tt>\p{Nshu}</tt>
+- <tt>\p{Nyiakeng_Puachue_Hmong}</tt>, <tt>\p{Hmnp}</tt>
+- <tt>\p{Ogham}</tt>, <tt>\p{Ogam}</tt>
+- <tt>\p{Ol_Chiki}</tt>, <tt>\p{Olck}</tt>
+- <tt>\p{Old_Hungarian}</tt>, <tt>\p{Hung}</tt>
+- <tt>\p{Old_Italic}</tt>, <tt>\p{Ital}</tt>
+- <tt>\p{Old_North_Arabian}</tt>, <tt>\p{Narb}</tt>
+- <tt>\p{Old_Permic}</tt>, <tt>\p{Perm}</tt>
+- <tt>\p{Old_Persian}</tt>, <tt>\p{Xpeo}</tt>
+- <tt>\p{Old_Sogdian}</tt>, <tt>\p{Sogo}</tt>
+- <tt>\p{Old_South_Arabian}</tt>, <tt>\p{Sarb}</tt>
+- <tt>\p{Old_Turkic}</tt>, <tt>\p{Orkh}</tt>
+- <tt>\p{Old_Uyghur}</tt>, <tt>\p{Ougr}</tt>
+- <tt>\p{Oriya}</tt>, <tt>\p{Orya}</tt>
+- <tt>\p{Osage}</tt>, <tt>\p{Osge}</tt>
+- <tt>\p{Osmanya}</tt>, <tt>\p{Osma}</tt>
+- <tt>\p{Pahawh_Hmong}</tt>, <tt>\p{Hmng}</tt>
+- <tt>\p{Palmyrene}</tt>, <tt>\p{Palm}</tt>
+- <tt>\p{Pau_Cin_Hau}</tt>, <tt>\p{Pauc}</tt>
+- <tt>\p{Phags_Pa}</tt>, <tt>\p{Phag}</tt>
+- <tt>\p{Phoenician}</tt>, <tt>\p{Phnx}</tt>
+- <tt>\p{Psalter_Pahlavi}</tt>, <tt>\p{Phlp}</tt>
+- <tt>\p{Rejang}</tt>, <tt>\p{Rjng}</tt>
+- <tt>\p{Runic}</tt>, <tt>\p{Runr}</tt>
+- <tt>\p{Samaritan}</tt>, <tt>\p{Samr}</tt>
+- <tt>\p{Saurashtra}</tt>, <tt>\p{Saur}</tt>
+- <tt>\p{Sharada}</tt>, <tt>\p{Shrd}</tt>
+- <tt>\p{Shavian}</tt>, <tt>\p{Shaw}</tt>
+- <tt>\p{Siddham}</tt>, <tt>\p{Sidd}</tt>
+- <tt>\p{SignWriting}</tt>, <tt>\p{Sgnw}</tt>
+- <tt>\p{Sinhala}</tt>, <tt>\p{Sinh}</tt>
+- <tt>\p{Sogdian}</tt>, <tt>\p{Sogd}</tt>
+- <tt>\p{Sora_Sompeng}</tt>, <tt>\p{Sora}</tt>
+- <tt>\p{Soyombo}</tt>, <tt>\p{Soyo}</tt>
+- <tt>\p{Sundanese}</tt>, <tt>\p{Sund}</tt>
+- <tt>\p{Syloti_Nagri}</tt>, <tt>\p{Sylo}</tt>
+- <tt>\p{Syriac}</tt>, <tt>\p{Syrc}</tt>
+- <tt>\p{Tagalog}</tt>, <tt>\p{Tglg}</tt>
+- <tt>\p{Tagbanwa}</tt>, <tt>\p{Tagb}</tt>
+- <tt>\p{Tai_Le}</tt>, <tt>\p{Tale}</tt>
+- <tt>\p{Tai_Tham}</tt>, <tt>\p{Lana}</tt>
+- <tt>\p{Tai_Viet}</tt>, <tt>\p{Tavt}</tt>
+- <tt>\p{Takri}</tt>, <tt>\p{Takr}</tt>
+- <tt>\p{Tamil}</tt>, <tt>\p{Taml}</tt>
+- <tt>\p{Tangsa}</tt>, <tt>\p{Tnsa}</tt>
+- <tt>\p{Tangut}</tt>, <tt>\p{Tang}</tt>
+- <tt>\p{Telugu}</tt>, <tt>\p{Telu}</tt>
+- <tt>\p{Thaana}</tt>, <tt>\p{Thaa}</tt>
+- <tt>\p{Thai}</tt>
+- <tt>\p{Tibetan}</tt>, <tt>\p{Tibt}</tt>
+- <tt>\p{Tifinagh}</tt>, <tt>\p{Tfng}</tt>
+- <tt>\p{Tirhuta}</tt>, <tt>\p{Tirh}</tt>
+- <tt>\p{Toto}</tt>
+- <tt>\p{Ugaritic}</tt>, <tt>\p{Ugar}</tt>
+- <tt>\p{Unknown}</tt>, <tt>\p{Zzzz}</tt>
+- <tt>\p{Vai}</tt>, <tt>\p{Vaii}</tt>
+- <tt>\p{Vithkuqi}</tt>, <tt>\p{Vith}</tt>
+- <tt>\p{Wancho}</tt>, <tt>\p{Wcho}</tt>
+- <tt>\p{Warang_Citi}</tt>, <tt>\p{Wara}</tt>
+- <tt>\p{Yezidi}</tt>, <tt>\p{Yezi}</tt>
+- <tt>\p{Yi}</tt>, <tt>\p{Yiii}</tt>
+- <tt>\p{Zanabazar_Square}</tt>, <tt>\p{Zanb}</tt>
+
+=== Blocks
+
+- <tt>\p{In_Adlam}</tt>
+- <tt>\p{In_Aegean_Numbers}</tt>
+- <tt>\p{In_Ahom}</tt>
+- <tt>\p{In_Alchemical_Symbols}</tt>
+- <tt>\p{In_Alphabetic_Presentation_Forms}</tt>
+- <tt>\p{In_Anatolian_Hieroglyphs}</tt>
+- <tt>\p{In_Ancient_Greek_Musical_Notation}</tt>
+- <tt>\p{In_Ancient_Greek_Numbers}</tt>
+- <tt>\p{In_Ancient_Symbols}</tt>
+- <tt>\p{In_Arabic}</tt>
+- <tt>\p{In_Arabic_Extended_A}</tt>
+- <tt>\p{In_Arabic_Extended_B}</tt>
+- <tt>\p{In_Arabic_Extended_C}</tt>
+- <tt>\p{In_Arabic_Mathematical_Alphabetic_Symbols}</tt>
+- <tt>\p{In_Arabic_Presentation_Forms_A}</tt>
+- <tt>\p{In_Arabic_Presentation_Forms_B}</tt>
+- <tt>\p{In_Arabic_Supplement}</tt>
+- <tt>\p{In_Armenian}</tt>
+- <tt>\p{In_Arrows}</tt>
+- <tt>\p{In_Avestan}</tt>
+- <tt>\p{In_Balinese}</tt>
+- <tt>\p{In_Bamum}</tt>
+- <tt>\p{In_Bamum_Supplement}</tt>
+- <tt>\p{In_Basic_Latin}</tt>
+- <tt>\p{In_Bassa_Vah}</tt>
+- <tt>\p{In_Batak}</tt>
+- <tt>\p{In_Bengali}</tt>
+- <tt>\p{In_Bhaiksuki}</tt>
+- <tt>\p{In_Block_Elements}</tt>
+- <tt>\p{In_Bopomofo}</tt>
+- <tt>\p{In_Bopomofo_Extended}</tt>
+- <tt>\p{In_Box_Drawing}</tt>
+- <tt>\p{In_Brahmi}</tt>
+- <tt>\p{In_Braille_Patterns}</tt>
+- <tt>\p{In_Buginese}</tt>
+- <tt>\p{In_Buhid}</tt>
+- <tt>\p{In_Byzantine_Musical_Symbols}</tt>
+- <tt>\p{In_CJK_Compatibility}</tt>
+- <tt>\p{In_CJK_Compatibility_Forms}</tt>
+- <tt>\p{In_CJK_Compatibility_Ideographs}</tt>
+- <tt>\p{In_CJK_Compatibility_Ideographs_Supplement}</tt>
+- <tt>\p{In_CJK_Radicals_Supplement}</tt>
+- <tt>\p{In_CJK_Strokes}</tt>
+- <tt>\p{In_CJK_Symbols_and_Punctuation}</tt>
+- <tt>\p{In_CJK_Unified_Ideographs}</tt>
+- <tt>\p{In_CJK_Unified_Ideographs_Extension_A}</tt>
+- <tt>\p{In_CJK_Unified_Ideographs_Extension_B}</tt>
+- <tt>\p{In_CJK_Unified_Ideographs_Extension_C}</tt>
+- <tt>\p{In_CJK_Unified_Ideographs_Extension_D}</tt>
+- <tt>\p{In_CJK_Unified_Ideographs_Extension_E}</tt>
+- <tt>\p{In_CJK_Unified_Ideographs_Extension_F}</tt>
+- <tt>\p{In_CJK_Unified_Ideographs_Extension_G}</tt>
+- <tt>\p{In_CJK_Unified_Ideographs_Extension_H}</tt>
+- <tt>\p{In_Carian}</tt>
+- <tt>\p{In_Caucasian_Albanian}</tt>
+- <tt>\p{In_Chakma}</tt>
+- <tt>\p{In_Cham}</tt>
+- <tt>\p{In_Cherokee}</tt>
+- <tt>\p{In_Cherokee_Supplement}</tt>
+- <tt>\p{In_Chess_Symbols}</tt>
+- <tt>\p{In_Chorasmian}</tt>
+- <tt>\p{In_Combining_Diacritical_Marks}</tt>
+- <tt>\p{In_Combining_Diacritical_Marks_Extended}</tt>
+- <tt>\p{In_Combining_Diacritical_Marks_Supplement}</tt>
+- <tt>\p{In_Combining_Diacritical_Marks_for_Symbols}</tt>
+- <tt>\p{In_Combining_Half_Marks}</tt>
+- <tt>\p{In_Common_Indic_Number_Forms}</tt>
+- <tt>\p{In_Control_Pictures}</tt>
+- <tt>\p{In_Coptic}</tt>
+- <tt>\p{In_Coptic_Epact_Numbers}</tt>
+- <tt>\p{In_Counting_Rod_Numerals}</tt>
+- <tt>\p{In_Cuneiform}</tt>
+- <tt>\p{In_Cuneiform_Numbers_and_Punctuation}</tt>
+- <tt>\p{In_Currency_Symbols}</tt>
+- <tt>\p{In_Cypriot_Syllabary}</tt>
+- <tt>\p{In_Cypro_Minoan}</tt>
+- <tt>\p{In_Cyrillic}</tt>
+- <tt>\p{In_Cyrillic_Extended_A}</tt>
+- <tt>\p{In_Cyrillic_Extended_B}</tt>
+- <tt>\p{In_Cyrillic_Extended_C}</tt>
+- <tt>\p{In_Cyrillic_Extended_D}</tt>
+- <tt>\p{In_Cyrillic_Supplement}</tt>
+- <tt>\p{In_Deseret}</tt>
+- <tt>\p{In_Devanagari}</tt>
+- <tt>\p{In_Devanagari_Extended}</tt>
+- <tt>\p{In_Devanagari_Extended_A}</tt>
+- <tt>\p{In_Dingbats}</tt>
+- <tt>\p{In_Dives_Akuru}</tt>
+- <tt>\p{In_Dogra}</tt>
+- <tt>\p{In_Domino_Tiles}</tt>
+- <tt>\p{In_Duployan}</tt>
+- <tt>\p{In_Early_Dynastic_Cuneiform}</tt>
+- <tt>\p{In_Egyptian_Hieroglyph_Format_Controls}</tt>
+- <tt>\p{In_Egyptian_Hieroglyphs}</tt>
+- <tt>\p{In_Elbasan}</tt>
+- <tt>\p{In_Elymaic}</tt>
+- <tt>\p{In_Emoticons}</tt>
+- <tt>\p{In_Enclosed_Alphanumeric_Supplement}</tt>
+- <tt>\p{In_Enclosed_Alphanumerics}</tt>
+- <tt>\p{In_Enclosed_CJK_Letters_and_Months}</tt>
+- <tt>\p{In_Enclosed_Ideographic_Supplement}</tt>
+- <tt>\p{In_Ethiopic}</tt>
+- <tt>\p{In_Ethiopic_Extended}</tt>
+- <tt>\p{In_Ethiopic_Extended_A}</tt>
+- <tt>\p{In_Ethiopic_Extended_B}</tt>
+- <tt>\p{In_Ethiopic_Supplement}</tt>
+- <tt>\p{In_General_Punctuation}</tt>
+- <tt>\p{In_Geometric_Shapes}</tt>
+- <tt>\p{In_Geometric_Shapes_Extended}</tt>
+- <tt>\p{In_Georgian}</tt>
+- <tt>\p{In_Georgian_Extended}</tt>
+- <tt>\p{In_Georgian_Supplement}</tt>
+- <tt>\p{In_Glagolitic}</tt>
+- <tt>\p{In_Glagolitic_Supplement}</tt>
+- <tt>\p{In_Gothic}</tt>
+- <tt>\p{In_Grantha}</tt>
+- <tt>\p{In_Greek_Extended}</tt>
+- <tt>\p{In_Greek_and_Coptic}</tt>
+- <tt>\p{In_Gujarati}</tt>
+- <tt>\p{In_Gunjala_Gondi}</tt>
+- <tt>\p{In_Gurmukhi}</tt>
+- <tt>\p{In_Halfwidth_and_Fullwidth_Forms}</tt>
+- <tt>\p{In_Hangul_Compatibility_Jamo}</tt>
+- <tt>\p{In_Hangul_Jamo}</tt>
+- <tt>\p{In_Hangul_Jamo_Extended_A}</tt>
+- <tt>\p{In_Hangul_Jamo_Extended_B}</tt>
+- <tt>\p{In_Hangul_Syllables}</tt>
+- <tt>\p{In_Hanifi_Rohingya}</tt>
+- <tt>\p{In_Hanunoo}</tt>
+- <tt>\p{In_Hatran}</tt>
+- <tt>\p{In_Hebrew}</tt>
+- <tt>\p{In_High_Private_Use_Surrogates}</tt>
+- <tt>\p{In_High_Surrogates}</tt>
+- <tt>\p{In_Hiragana}</tt>
+- <tt>\p{In_IPA_Extensions}</tt>
+- <tt>\p{In_Ideographic_Description_Characters}</tt>
+- <tt>\p{In_Ideographic_Symbols_and_Punctuation}</tt>
+- <tt>\p{In_Imperial_Aramaic}</tt>
+- <tt>\p{In_Indic_Siyaq_Numbers}</tt>
+- <tt>\p{In_Inscriptional_Pahlavi}</tt>
+- <tt>\p{In_Inscriptional_Parthian}</tt>
+- <tt>\p{In_Javanese}</tt>
+- <tt>\p{In_Kaithi}</tt>
+- <tt>\p{In_Kaktovik_Numerals}</tt>
+- <tt>\p{In_Kana_Extended_A}</tt>
+- <tt>\p{In_Kana_Extended_B}</tt>
+- <tt>\p{In_Kana_Supplement}</tt>
+- <tt>\p{In_Kanbun}</tt>
+- <tt>\p{In_Kangxi_Radicals}</tt>
+- <tt>\p{In_Kannada}</tt>
+- <tt>\p{In_Katakana}</tt>
+- <tt>\p{In_Katakana_Phonetic_Extensions}</tt>
+- <tt>\p{In_Kawi}</tt>
+- <tt>\p{In_Kayah_Li}</tt>
+- <tt>\p{In_Kharoshthi}</tt>
+- <tt>\p{In_Khitan_Small_Script}</tt>
+- <tt>\p{In_Khmer}</tt>
+- <tt>\p{In_Khmer_Symbols}</tt>
+- <tt>\p{In_Khojki}</tt>
+- <tt>\p{In_Khudawadi}</tt>
+- <tt>\p{In_Lao}</tt>
+- <tt>\p{In_Latin_1_Supplement}</tt>
+- <tt>\p{In_Latin_Extended_A}</tt>
+- <tt>\p{In_Latin_Extended_Additional}</tt>
+- <tt>\p{In_Latin_Extended_B}</tt>
+- <tt>\p{In_Latin_Extended_C}</tt>
+- <tt>\p{In_Latin_Extended_D}</tt>
+- <tt>\p{In_Latin_Extended_E}</tt>
+- <tt>\p{In_Latin_Extended_F}</tt>
+- <tt>\p{In_Latin_Extended_G}</tt>
+- <tt>\p{In_Lepcha}</tt>
+- <tt>\p{In_Letterlike_Symbols}</tt>
+- <tt>\p{In_Limbu}</tt>
+- <tt>\p{In_Linear_A}</tt>
+- <tt>\p{In_Linear_B_Ideograms}</tt>
+- <tt>\p{In_Linear_B_Syllabary}</tt>
+- <tt>\p{In_Lisu}</tt>
+- <tt>\p{In_Lisu_Supplement}</tt>
+- <tt>\p{In_Low_Surrogates}</tt>
+- <tt>\p{In_Lycian}</tt>
+- <tt>\p{In_Lydian}</tt>
+- <tt>\p{In_Mahajani}</tt>
+- <tt>\p{In_Mahjong_Tiles}</tt>
+- <tt>\p{In_Makasar}</tt>
+- <tt>\p{In_Malayalam}</tt>
+- <tt>\p{In_Mandaic}</tt>
+- <tt>\p{In_Manichaean}</tt>
+- <tt>\p{In_Marchen}</tt>
+- <tt>\p{In_Masaram_Gondi}</tt>
+- <tt>\p{In_Mathematical_Alphanumeric_Symbols}</tt>
+- <tt>\p{In_Mathematical_Operators}</tt>
+- <tt>\p{In_Mayan_Numerals}</tt>
+- <tt>\p{In_Medefaidrin}</tt>
+- <tt>\p{In_Meetei_Mayek}</tt>
+- <tt>\p{In_Meetei_Mayek_Extensions}</tt>
+- <tt>\p{In_Mende_Kikakui}</tt>
+- <tt>\p{In_Meroitic_Cursive}</tt>
+- <tt>\p{In_Meroitic_Hieroglyphs}</tt>
+- <tt>\p{In_Miao}</tt>
+- <tt>\p{In_Miscellaneous_Mathematical_Symbols_A}</tt>
+- <tt>\p{In_Miscellaneous_Mathematical_Symbols_B}</tt>
+- <tt>\p{In_Miscellaneous_Symbols}</tt>
+- <tt>\p{In_Miscellaneous_Symbols_and_Arrows}</tt>
+- <tt>\p{In_Miscellaneous_Symbols_and_Pictographs}</tt>
+- <tt>\p{In_Miscellaneous_Technical}</tt>
+- <tt>\p{In_Modi}</tt>
+- <tt>\p{In_Modifier_Tone_Letters}</tt>
+- <tt>\p{In_Mongolian}</tt>
+- <tt>\p{In_Mongolian_Supplement}</tt>
+- <tt>\p{In_Mro}</tt>
+- <tt>\p{In_Multani}</tt>
+- <tt>\p{In_Musical_Symbols}</tt>
+- <tt>\p{In_Myanmar}</tt>
+- <tt>\p{In_Myanmar_Extended_A}</tt>
+- <tt>\p{In_Myanmar_Extended_B}</tt>
+- <tt>\p{In_NKo}</tt>
+- <tt>\p{In_Nabataean}</tt>
+- <tt>\p{In_Nag_Mundari}</tt>
+- <tt>\p{In_Nandinagari}</tt>
+- <tt>\p{In_New_Tai_Lue}</tt>
+- <tt>\p{In_Newa}</tt>
+- <tt>\p{In_No_Block}</tt>
+- <tt>\p{In_Number_Forms}</tt>
+- <tt>\p{In_Nushu}</tt>
+- <tt>\p{In_Nyiakeng_Puachue_Hmong}</tt>
+- <tt>\p{In_Ogham}</tt>
+- <tt>\p{In_Ol_Chiki}</tt>
+- <tt>\p{In_Old_Hungarian}</tt>
+- <tt>\p{In_Old_Italic}</tt>
+- <tt>\p{In_Old_North_Arabian}</tt>
+- <tt>\p{In_Old_Permic}</tt>
+- <tt>\p{In_Old_Persian}</tt>
+- <tt>\p{In_Old_Sogdian}</tt>
+- <tt>\p{In_Old_South_Arabian}</tt>
+- <tt>\p{In_Old_Turkic}</tt>
+- <tt>\p{In_Old_Uyghur}</tt>
+- <tt>\p{In_Optical_Character_Recognition}</tt>
+- <tt>\p{In_Oriya}</tt>
+- <tt>\p{In_Ornamental_Dingbats}</tt>
+- <tt>\p{In_Osage}</tt>
+- <tt>\p{In_Osmanya}</tt>
+- <tt>\p{In_Ottoman_Siyaq_Numbers}</tt>
+- <tt>\p{In_Pahawh_Hmong}</tt>
+- <tt>\p{In_Palmyrene}</tt>
+- <tt>\p{In_Pau_Cin_Hau}</tt>
+- <tt>\p{In_Phags_pa}</tt>
+- <tt>\p{In_Phaistos_Disc}</tt>
+- <tt>\p{In_Phoenician}</tt>
+- <tt>\p{In_Phonetic_Extensions}</tt>
+- <tt>\p{In_Phonetic_Extensions_Supplement}</tt>
+- <tt>\p{In_Playing_Cards}</tt>
+- <tt>\p{In_Private_Use_Area}</tt>
+- <tt>\p{In_Psalter_Pahlavi}</tt>
+- <tt>\p{In_Rejang}</tt>
+- <tt>\p{In_Rumi_Numeral_Symbols}</tt>
+- <tt>\p{In_Runic}</tt>
+- <tt>\p{In_Samaritan}</tt>
+- <tt>\p{In_Saurashtra}</tt>
+- <tt>\p{In_Sharada}</tt>
+- <tt>\p{In_Shavian}</tt>
+- <tt>\p{In_Shorthand_Format_Controls}</tt>
+- <tt>\p{In_Siddham}</tt>
+- <tt>\p{In_Sinhala}</tt>
+- <tt>\p{In_Sinhala_Archaic_Numbers}</tt>
+- <tt>\p{In_Small_Form_Variants}</tt>
+- <tt>\p{In_Small_Kana_Extension}</tt>
+- <tt>\p{In_Sogdian}</tt>
+- <tt>\p{In_Sora_Sompeng}</tt>
+- <tt>\p{In_Soyombo}</tt>
+- <tt>\p{In_Spacing_Modifier_Letters}</tt>
+- <tt>\p{In_Specials}</tt>
+- <tt>\p{In_Sundanese}</tt>
+- <tt>\p{In_Sundanese_Supplement}</tt>
+- <tt>\p{In_Superscripts_and_Subscripts}</tt>
+- <tt>\p{In_Supplemental_Arrows_A}</tt>
+- <tt>\p{In_Supplemental_Arrows_B}</tt>
+- <tt>\p{In_Supplemental_Arrows_C}</tt>
+- <tt>\p{In_Supplemental_Mathematical_Operators}</tt>
+- <tt>\p{In_Supplemental_Punctuation}</tt>
+- <tt>\p{In_Supplemental_Symbols_and_Pictographs}</tt>
+- <tt>\p{In_Supplementary_Private_Use_Area_A}</tt>
+- <tt>\p{In_Supplementary_Private_Use_Area_B}</tt>
+- <tt>\p{In_Sutton_SignWriting}</tt>
+- <tt>\p{In_Syloti_Nagri}</tt>
+- <tt>\p{In_Symbols_and_Pictographs_Extended_A}</tt>
+- <tt>\p{In_Symbols_for_Legacy_Computing}</tt>
+- <tt>\p{In_Syriac}</tt>
+- <tt>\p{In_Syriac_Supplement}</tt>
+- <tt>\p{In_Tagalog}</tt>
+- <tt>\p{In_Tagbanwa}</tt>
+- <tt>\p{In_Tags}</tt>
+- <tt>\p{In_Tai_Le}</tt>
+- <tt>\p{In_Tai_Tham}</tt>
+- <tt>\p{In_Tai_Viet}</tt>
+- <tt>\p{In_Tai_Xuan_Jing_Symbols}</tt>
+- <tt>\p{In_Takri}</tt>
+- <tt>\p{In_Tamil}</tt>
+- <tt>\p{In_Tamil_Supplement}</tt>
+- <tt>\p{In_Tangsa}</tt>
+- <tt>\p{In_Tangut}</tt>
+- <tt>\p{In_Tangut_Components}</tt>
+- <tt>\p{In_Tangut_Supplement}</tt>
+- <tt>\p{In_Telugu}</tt>
+- <tt>\p{In_Thaana}</tt>
+- <tt>\p{In_Thai}</tt>
+- <tt>\p{In_Tibetan}</tt>
+- <tt>\p{In_Tifinagh}</tt>
+- <tt>\p{In_Tirhuta}</tt>
+- <tt>\p{In_Toto}</tt>
+- <tt>\p{In_Transport_and_Map_Symbols}</tt>
+- <tt>\p{In_Ugaritic}</tt>
+- <tt>\p{In_Unified_Canadian_Aboriginal_Syllabics}</tt>
+- <tt>\p{In_Unified_Canadian_Aboriginal_Syllabics_Extended}</tt>
+- <tt>\p{In_Unified_Canadian_Aboriginal_Syllabics_Extended_A}</tt>
+- <tt>\p{In_Vai}</tt>
+- <tt>\p{In_Variation_Selectors}</tt>
+- <tt>\p{In_Variation_Selectors_Supplement}</tt>
+- <tt>\p{In_Vedic_Extensions}</tt>
+- <tt>\p{In_Vertical_Forms}</tt>
+- <tt>\p{In_Vithkuqi}</tt>
+- <tt>\p{In_Wancho}</tt>
+- <tt>\p{In_Warang_Citi}</tt>
+- <tt>\p{In_Yezidi}</tt>
+- <tt>\p{In_Yi_Radicals}</tt>
+- <tt>\p{In_Yi_Syllables}</tt>
+- <tt>\p{In_Yijing_Hexagram_Symbols}</tt>
+- <tt>\p{In_Zanabazar_Square}</tt>
+- <tt>\p{In_Znamenny_Musical_Notation}</tt>
+
+=== Emoji
+
+- <tt>\p{Emoji}</tt>
+- <tt>\p{Emoji_Component}</tt>, <tt>\p{EComp}</tt>
+- <tt>\p{Emoji_Modifier}</tt>, <tt>\p{EMod}</tt>
+- <tt>\p{Emoji_Modifier_Base}</tt>, <tt>\p{EBase}</tt>
+- <tt>\p{Emoji_Presentation}</tt>, <tt>\p{EPres}</tt>
+- <tt>\p{Extended_Pictographic}</tt>, <tt>\p{ExtPict}</tt>
+
+=== Graphemes
+
+- <tt>\p{Grapheme_Cluster_Break_CR}</tt>
+- <tt>\p{Grapheme_Cluster_Break_Control}</tt>
+- <tt>\p{Grapheme_Cluster_Break_Extend}</tt>
+- <tt>\p{Grapheme_Cluster_Break_L}</tt>
+- <tt>\p{Grapheme_Cluster_Break_LF}</tt>
+- <tt>\p{Grapheme_Cluster_Break_LV}</tt>
+- <tt>\p{Grapheme_Cluster_Break_LVT}</tt>
+- <tt>\p{Grapheme_Cluster_Break_Prepend}</tt>
+- <tt>\p{Grapheme_Cluster_Break_Regional_Indicator}</tt>
+- <tt>\p{Grapheme_Cluster_Break_SpacingMark}</tt>
+- <tt>\p{Grapheme_Cluster_Break_T}</tt>
+- <tt>\p{Grapheme_Cluster_Break_V}</tt>
+- <tt>\p{Grapheme_Cluster_Break_ZWJ}</tt>
+
+=== Derived Ages
+
+- <tt>\p{Age_10_0}</tt>
+- <tt>\p{Age_11_0}</tt>
+- <tt>\p{Age_12_0}</tt>
+- <tt>\p{Age_12_1}</tt>
+- <tt>\p{Age_13_0}</tt>
+- <tt>\p{Age_14_0}</tt>
+- <tt>\p{Age_15_0}</tt>
+- <tt>\p{Age_1_1}</tt>
+- <tt>\p{Age_2_0}</tt>
+- <tt>\p{Age_2_1}</tt>
+- <tt>\p{Age_3_0}</tt>
+- <tt>\p{Age_3_1}</tt>
+- <tt>\p{Age_3_2}</tt>
+- <tt>\p{Age_4_0}</tt>
+- <tt>\p{Age_4_1}</tt>
+- <tt>\p{Age_5_0}</tt>
+- <tt>\p{Age_5_1}</tt>
+- <tt>\p{Age_5_2}</tt>
+- <tt>\p{Age_6_0}</tt>
+- <tt>\p{Age_6_1}</tt>
+- <tt>\p{Age_6_2}</tt>
+- <tt>\p{Age_6_3}</tt>
+- <tt>\p{Age_7_0}</tt>
+- <tt>\p{Age_8_0}</tt>
+- <tt>\p{Age_9_0}</tt>
diff --git a/doc/reline/face.md b/doc/reline/face.md
new file mode 100644
index 0000000000..1fa916123b
--- /dev/null
+++ b/doc/reline/face.md
@@ -0,0 +1,111 @@
+# Face
+
+With the `Reline::Face` class, you can modify the text color and text decorations in your terminal emulator.
+This is primarily used to customize the appearance of the method completion dialog in IRB.
+
+## Usage
+
+### ex: Change the background color of the completion dialog cyan to blue
+
+```ruby
+Reline::Face.config(:completion_dialog) do |conf|
+ conf.define :default, foreground: :white, background: :blue
+ # ^^^^^ `:cyan` by default
+ conf.define :enhanced, foreground: :white, background: :magenta
+ conf.define :scrollbar, foreground: :white, background: :blue
+end
+```
+
+If you provide the above code to an IRB session in some way, you can apply the configuration.
+It's generally done by writing it in `.irbrc`.
+
+Regarding `.irbrc`, please refer to the following link: [https://docs.ruby-lang.org/en/master/IRB.html](https://docs.ruby-lang.org/en/master/IRB.html)
+
+## Available parameters
+
+`Reline::Face` internally creates SGR (Select Graphic Rendition) code according to the block parameter of `Reline::Face.config` method.
+
+| Key | Value | SGR Code (numeric part following "\e[")|
+|:------------|:------------------|-----:|
+| :foreground | :black | 30 |
+| | :red | 31 |
+| | :green | 32 |
+| | :yellow | 33 |
+| | :blue | 34 |
+| | :magenta | 35 |
+| | :cyan | 36 |
+| | :white | 37 |
+| | :bright_black | 90 |
+| | :gray | 90 |
+| | :bright_red | 91 |
+| | :bright_green | 92 |
+| | :bright_yellow | 93 |
+| | :bright_blue | 94 |
+| | :bright_magenta | 95 |
+| | :bright_cyan | 96 |
+| | :bright_white | 97 |
+| :background | :black | 40 |
+| | :red | 41 |
+| | :green | 42 |
+| | :yellow | 43 |
+| | :blue | 44 |
+| | :magenta | 45 |
+| | :cyan | 46 |
+| | :white | 47 |
+| | :bright_black | 100 |
+| | :gray | 100 |
+| | :bright_red | 101 |
+| | :bright_green | 102 |
+| | :bright_yellow | 103 |
+| | :bright_blue | 104 |
+| | :bright_magenta | 105 |
+| | :bright_cyan | 106 |
+| | :bright_white | 107 |
+| :style | :reset | 0 |
+| | :bold | 1 |
+| | :faint | 2 |
+| | :italicized | 3 |
+| | :underlined | 4 |
+| | :slowly_blinking | 5 |
+| | :blinking | 5 |
+| | :rapidly_blinking | 6 |
+| | :negative | 7 |
+| | :concealed | 8 |
+| | :crossed_out | 9 |
+
+- The value for `:style` can be both a Symbol and an Array
+ ```ruby
+ # Single symbol
+ conf.define :default, style: :bold
+ # Array
+ conf.define :default, style: [:bold, :negative]
+ ```
+- The availability of specific SGR codes depends on your terminal emulator
+- You can specify a hex color code to `:foreground` and `:background` color like `foreground: "#FF1020"`. Its availability also depends on your terminal emulator
+
+## Debugging
+
+You can see the current Face configuration by `Reline::Face.configs` method
+
+Example:
+
+```ruby
+irb(main):001:0> Reline::Face.configs
+=>
+{:default=>
+ {:default=>{:style=>:reset, :escape_sequence=>"\e[0m"},
+ :enhanced=>{:style=>:reset, :escape_sequence=>"\e[0m"},
+ :scrollbar=>{:style=>:reset, :escape_sequence=>"\e[0m"}},
+ :completion_dialog=>
+ {:default=>{:foreground=>:white, :background=>:cyan, :escape_sequence=>"\e[0m\e[37;46m"},
+ :enhanced=>{:foreground=>:white, :background=>:magenta, :escape_sequence=>"\e[0m\e[37;45m"},
+ :scrollbar=>{:foreground=>:white, :background=>:cyan, :escape_sequence=>"\e[0m\e[37;46m"}}}
+```
+
+## 256-Color and TrueColor
+
+Reline will automatically detect if your terminal emulator supports truecolor with `ENV['COLORTERM] in 'truecolor' | '24bit'`. When this env is not set, Reline will fallback to 256-color.
+If your terminal emulator supports truecolor but does not set COLORTERM env, add this line to `.irbrc`.
+```ruby
+Reline::Face.force_truecolor
+```
diff --git a/doc/rjit/README.md b/doc/rjit/README.md
deleted file mode 100644
index e3795e35b8..0000000000
--- a/doc/rjit/README.md
+++ /dev/null
@@ -1,53 +0,0 @@
-# RJIT: Ruby JIT
-
-This document has some tips that might be useful when you work on RJIT.
-
-## Project purpose
-
-This project is for experimental purposes.
-For production deployment, consider using YJIT instead.
-
-## Supported platforms
-
-The following platforms are assumed to work. `linux-x86_64` is tested on CI.
-
-* OS: Linux, macOS, BSD
-* Arch: x86\_64
-
-## configure
-### --enable-rjit
-
-On supported platforms, `--enable-rjit` is set by default. You usually don't need to specify this.
-You may still manually pass `--enable-rjit` to try RJIT on unsupported platforms.
-
-### --enable-rjit=dev
-
-It enables `--rjit-dump-disasm` if libcapstone is available.
-
-It also enables `vm_insns_count` and `ratio_in_rjit` in `--rjit-stats`.
-However, it makes the interpreter a little slower.
-
-### --enable-rjit=disasm
-
-It enables `--rjit-dump-disasm` if libcapstone is available.
-
-## make
-### rjit-bindgen
-
-If you see an "RJIT bindgen" GitHub Actions failure, please commit the `git diff` shown on the failed job.
-
-For doing the same thing locally, run `make rjit-bindgen` after installing libclang.
-macOS seems to have libclang by default. On Ubuntu, you can install it with `apt install libclang1`.
-
-## ruby
-### --rjit-stats
-
-This prints RJIT stats at exit. Some stats are available only with `--enable-rjit=dev` on configure.
-
-### --rjit-dump-disasm
-
-This dumps all JIT code. You need to install libcapstone before configure and use `--enable-rjit=dev`
-or `--enable-rjit=disasm` on configure.
-
-* Ubuntu: `sudo apt-get install -y libcapstone-dev`
-* macOS: `brew install capstone`
diff --git a/doc/rjit/rjit.md b/doc/rjit/rjit.md
new file mode 100644
index 0000000000..9e60e43ce3
--- /dev/null
+++ b/doc/rjit/rjit.md
@@ -0,0 +1,45 @@
+# RJIT: Ruby JIT
+
+This document has some tips that might be useful when you work on RJIT.
+
+## Project purpose
+
+This project is for experimental purposes.
+For production deployment, consider using YJIT instead.
+
+## Supported platforms
+
+The following platforms are assumed to work. `linux-x86_64` is tested on CI.
+
+* OS: Linux, macOS, BSD
+* Arch: x86\_64
+
+## configure
+### --enable-rjit
+
+On supported platforms, `--enable-rjit` is set by default. You usually don't need to specify this.
+You may still manually pass `--enable-rjit` to try RJIT on unsupported platforms.
+
+### --enable-rjit=dev
+
+It enables `--rjit-dump-disasm` if libcapstone is available.
+
+## make
+### rjit-bindgen
+
+If you see an "RJIT bindgen" GitHub Actions failure, please commit the `git diff` shown on the failed job.
+
+For doing the same thing locally, run `make rjit-bindgen` after installing libclang.
+macOS seems to have libclang by default. On Ubuntu, you can install it with `apt install libclang1`.
+
+## ruby
+### --rjit-stats
+
+This prints RJIT stats at exit.
+
+### --rjit-dump-disasm
+
+This dumps all JIT code. You need to install libcapstone before configure and use `--enable-rjit=dev` on configure.
+
+* Ubuntu: `sudo apt-get install -y libcapstone-dev`
+* macOS: `brew install capstone`
diff --git a/doc/ruby/option_dump.md b/doc/ruby/option_dump.md
new file mode 100644
index 0000000000..00d0ec77d5
--- /dev/null
+++ b/doc/ruby/option_dump.md
@@ -0,0 +1,297 @@
+# Option `--dump`
+
+For other argument values,
+see {Option --dump}[options_md.html#label--dump-3A+Dump+Items].
+
+For the examples here, we use this program:
+
+```sh
+$ cat t.rb
+puts 'Foo'
+```
+
+The supported dump items:
+
+- `insns`: Instruction sequences:
+
+ ```sh
+ $ ruby --dump=insns t.rb
+ == disasm: #<ISeq:<main>@t.rb:1 (1,0)-(1,10)> (catch: FALSE)
+ 0000 putself ( 1)[Li]
+ 0001 putstring "Foo"
+ 0003 opt_send_without_block <calldata!mid:puts, argc:1, FCALL|ARGS_SIMPLE>
+ 0005 leave
+ ```
+
+- `parsetree`: {Abstract syntax tree}[https://en.wikipedia.org/wiki/Abstract_syntax_tree]
+ (AST):
+
+ ```sh
+ $ ruby --dump=parsetree t.rb
+ ###########################################################
+ ## Do NOT use this node dump for any purpose other than ##
+ ## debug and research. Compatibility is not guaranteed. ##
+ ###########################################################
+
+ # @ NODE_SCOPE (line: 1, location: (1,0)-(1,10))
+ # +- nd_tbl: (empty)
+ # +- nd_args:
+ # | (null node)
+ # +- nd_body:
+ # @ NODE_FCALL (line: 1, location: (1,0)-(1,10))*
+ # +- nd_mid: :puts
+ # +- nd_args:
+ # @ NODE_LIST (line: 1, location: (1,5)-(1,10))
+ # +- nd_alen: 1
+ # +- nd_head:
+ # | @ NODE_STR (line: 1, location: (1,5)-(1,10))
+ # | +- nd_lit: "Foo"
+ # +- nd_next:
+ # (null node)
+ ```
+
+- `parsetree_with_comment`: AST with comments:
+
+ ```sh
+ $ ruby --dump=parsetree_with_comment t.rb
+ ###########################################################
+ ## Do NOT use this node dump for any purpose other than ##
+ ## debug and research. Compatibility is not guaranteed. ##
+ ###########################################################
+
+ # @ NODE_SCOPE (line: 1, location: (1,0)-(1,10))
+ # | # new scope
+ # | # format: [nd_tbl]: local table, [nd_args]: arguments, [nd_body]: body
+ # +- nd_tbl (local table): (empty)
+ # +- nd_args (arguments):
+ # | (null node)
+ # +- nd_body (body):
+ # @ NODE_FCALL (line: 1, location: (1,0)-(1,10))*
+ # | # function call
+ # | # format: [nd_mid]([nd_args])
+ # | # example: foo(1)
+ # +- nd_mid (method id): :puts
+ # +- nd_args (arguments):
+ # @ NODE_LIST (line: 1, location: (1,5)-(1,10))
+ # | # list constructor
+ # | # format: [ [nd_head], [nd_next].. ] (length: [nd_alen])
+ # | # example: [1, 2, 3]
+ # +- nd_alen (length): 1
+ # +- nd_head (element):
+ # | @ NODE_STR (line: 1, location: (1,5)-(1,10))
+ # | | # string literal
+ # | | # format: [nd_lit]
+ # | | # example: 'foo'
+ # | +- nd_lit (literal): "Foo"
+ # +- nd_next (next element):
+ # (null node)
+ ```
+
+- `yydebug`: Debugging information from yacc parser generator:
+
+ ```sh
+ $ ruby --dump=yydebug t.rb
+ Starting parse
+ Entering state 0
+ Reducing stack by rule 1 (line 1295):
+ lex_state: NONE -> BEG at line 1296
+ vtable_alloc:12392: 0x0000558453df1a00
+ vtable_alloc:12393: 0x0000558453df1a60
+ cmdarg_stack(push): 0 at line 12406
+ cond_stack(push): 0 at line 12407
+ -> $$ = nterm $@1 (1.0-1.0: )
+ Stack now 0
+ Entering state 2
+ Reading a token:
+ lex_state: BEG -> CMDARG at line 9049
+ Next token is token "local variable or method" (1.0-1.4: puts)
+ Shifting token "local variable or method" (1.0-1.4: puts)
+ Entering state 35
+ Reading a token: Next token is token "string literal" (1.5-1.6: )
+ Reducing stack by rule 742 (line 5567):
+ $1 = token "local variable or method" (1.0-1.4: puts)
+ -> $$ = nterm operation (1.0-1.4: )
+ Stack now 0 2
+ Entering state 126
+ Reducing stack by rule 78 (line 1794):
+ $1 = nterm operation (1.0-1.4: )
+ -> $$ = nterm fcall (1.0-1.4: )
+ Stack now 0 2
+ Entering state 80
+ Next token is token "string literal" (1.5-1.6: )
+ Reducing stack by rule 292 (line 2723):
+ cmdarg_stack(push): 1 at line 2737
+ -> $$ = nterm $@16 (1.4-1.4: )
+ Stack now 0 2 80
+ Entering state 235
+ Next token is token "string literal" (1.5-1.6: )
+ Shifting token "string literal" (1.5-1.6: )
+ Entering state 216
+ Reducing stack by rule 607 (line 4706):
+ -> $$ = nterm string_contents (1.6-1.6: )
+ Stack now 0 2 80 235 216
+ Entering state 437
+ Reading a token: Next token is token "literal content" (1.6-1.9: "Foo")
+ Shifting token "literal content" (1.6-1.9: "Foo")
+ Entering state 503
+ Reducing stack by rule 613 (line 4802):
+ $1 = token "literal content" (1.6-1.9: "Foo")
+ -> $$ = nterm string_content (1.6-1.9: )
+ Stack now 0 2 80 235 216 437
+ Entering state 507
+ Reducing stack by rule 608 (line 4716):
+ $1 = nterm string_contents (1.6-1.6: )
+ $2 = nterm string_content (1.6-1.9: )
+ -> $$ = nterm string_contents (1.6-1.9: )
+ Stack now 0 2 80 235 216
+ Entering state 437
+ Reading a token:
+ lex_state: CMDARG -> END at line 7276
+ Next token is token "terminator" (1.9-1.10: )
+ Shifting token "terminator" (1.9-1.10: )
+ Entering state 508
+ Reducing stack by rule 590 (line 4569):
+ $1 = token "string literal" (1.5-1.6: )
+ $2 = nterm string_contents (1.6-1.9: )
+ $3 = token "terminator" (1.9-1.10: )
+ -> $$ = nterm string1 (1.5-1.10: )
+ Stack now 0 2 80 235
+ Entering state 109
+ Reducing stack by rule 588 (line 4559):
+ $1 = nterm string1 (1.5-1.10: )
+ -> $$ = nterm string (1.5-1.10: )
+ Stack now 0 2 80 235
+ Entering state 108
+ Reading a token:
+ lex_state: END -> BEG at line 9200
+ Next token is token '\n' (1.10-1.10: )
+ Reducing stack by rule 586 (line 4541):
+ $1 = nterm string (1.5-1.10: )
+ -> $$ = nterm strings (1.5-1.10: )
+ Stack now 0 2 80 235
+ Entering state 107
+ Reducing stack by rule 307 (line 2837):
+ $1 = nterm strings (1.5-1.10: )
+ -> $$ = nterm primary (1.5-1.10: )
+ Stack now 0 2 80 235
+ Entering state 90
+ Next token is token '\n' (1.10-1.10: )
+ Reducing stack by rule 261 (line 2553):
+ $1 = nterm primary (1.5-1.10: )
+ -> $$ = nterm arg (1.5-1.10: )
+ Stack now 0 2 80 235
+ Entering state 220
+ Next token is token '\n' (1.10-1.10: )
+ Reducing stack by rule 270 (line 2586):
+ $1 = nterm arg (1.5-1.10: )
+ -> $$ = nterm arg_value (1.5-1.10: )
+ Stack now 0 2 80 235
+ Entering state 221
+ Next token is token '\n' (1.10-1.10: )
+ Reducing stack by rule 297 (line 2779):
+ $1 = nterm arg_value (1.5-1.10: )
+ -> $$ = nterm args (1.5-1.10: )
+ Stack now 0 2 80 235
+ Entering state 224
+ Next token is token '\n' (1.10-1.10: )
+ Reducing stack by rule 772 (line 5626):
+ -> $$ = nterm none (1.10-1.10: )
+ Stack now 0 2 80 235 224
+ Entering state 442
+ Reducing stack by rule 296 (line 2773):
+ $1 = nterm none (1.10-1.10: )
+
+ -> $$ = nterm opt_block_arg (1.10-1.10: )
+ Stack now 0 2 80 235 224
+ Entering state 441
+ Reducing stack by rule 288 (line 2696):
+ $1 = nterm args (1.5-1.10: )
+ $2 = nterm opt_block_arg (1.10-1.10: )
+ -> $$ = nterm call_args (1.5-1.10: )
+ Stack now 0 2 80 235
+ Entering state 453
+ Reducing stack by rule 293 (line 2723):
+ $1 = nterm $@16 (1.4-1.4: )
+ $2 = nterm call_args (1.5-1.10: )
+ cmdarg_stack(pop): 0 at line 2754
+ -> $$ = nterm command_args (1.4-1.10: )
+ Stack now 0 2 80
+ Entering state 333
+ Next token is token '\n' (1.10-1.10: )
+ Reducing stack by rule 79 (line 1804):
+ $1 = nterm fcall (1.0-1.4: )
+ $2 = nterm command_args (1.4-1.10: )
+ -> $$ = nterm command (1.0-1.10: )
+ Stack now 0 2
+ Entering state 81
+ Next token is token '\n' (1.10-1.10: )
+ Reducing stack by rule 73 (line 1770):
+ $1 = nterm command (1.0-1.10: )
+ -> $$ = nterm command_call (1.0-1.10: )
+ Stack now 0 2
+ Entering state 78
+ Reducing stack by rule 51 (line 1659):
+ $1 = nterm command_call (1.0-1.10: )
+ -> $$ = nterm expr (1.0-1.10: )
+ Stack now 0 2
+ Entering state 75
+ Next token is token '\n' (1.10-1.10: )
+ Reducing stack by rule 39 (line 1578):
+ $1 = nterm expr (1.0-1.10: )
+ -> $$ = nterm stmt (1.0-1.10: )
+ Stack now 0 2
+ Entering state 73
+ Next token is token '\n' (1.10-1.10: )
+ Reducing stack by rule 8 (line 1354):
+ $1 = nterm stmt (1.0-1.10: )
+ -> $$ = nterm top_stmt (1.0-1.10: )
+ Stack now 0 2
+ Entering state 72
+ Reducing stack by rule 5 (line 1334):
+ $1 = nterm top_stmt (1.0-1.10: )
+ -> $$ = nterm top_stmts (1.0-1.10: )
+ Stack now 0 2
+ Entering state 71
+ Next token is token '\n' (1.10-1.10: )
+ Shifting token '\n' (1.10-1.10: )
+ Entering state 311
+ Reducing stack by rule 769 (line 5618):
+ $1 = token '\n' (1.10-1.10: )
+ -> $$ = nterm term (1.10-1.10: )
+ Stack now 0 2 71
+ Entering state 313
+ Reducing stack by rule 770 (line 5621):
+ $1 = nterm term (1.10-1.10: )
+ -> $$ = nterm terms (1.10-1.10: )
+ Stack now 0 2 71
+ Entering state 314
+ Reading a token: Now at end of input.
+ Reducing stack by rule 759 (line 5596):
+ $1 = nterm terms (1.10-1.10: )
+ -> $$ = nterm opt_terms (1.10-1.10: )
+ Stack now 0 2 71
+ Entering state 312
+ Reducing stack by rule 3 (line 1321):
+ $1 = nterm top_stmts (1.0-1.10: )
+ $2 = nterm opt_terms (1.10-1.10: )
+ -> $$ = nterm top_compstmt (1.0-1.10: )
+ Stack now 0 2
+ Entering state 70
+ Reducing stack by rule 2 (line 1295):
+ $1 = nterm $@1 (1.0-1.0: )
+ $2 = nterm top_compstmt (1.0-1.10: )
+ vtable_free:12426: p->lvtbl->args(0x0000558453df1a00)
+ vtable_free:12427: p->lvtbl->vars(0x0000558453df1a60)
+ cmdarg_stack(pop): 0 at line 12428
+ cond_stack(pop): 0 at line 12429
+ -> $$ = nterm program (1.0-1.10: )
+ Stack now 0
+ Entering state 1
+ Now at end of input.
+ Shifting token "end-of-input" (1.10-1.10: )
+ Entering state 3
+ Stack now 0 1 3
+ Cleanup: popping token "end-of-input" (1.10-1.10: )
+ Cleanup: popping nterm program (1.0-1.10: )
+ ```
+
diff --git a/doc/ruby/options.md b/doc/ruby/options.md
new file mode 100644
index 0000000000..143c8925f1
--- /dev/null
+++ b/doc/ruby/options.md
@@ -0,0 +1,723 @@
+# Ruby Command-Line Options
+
+## About the Examples
+
+Some examples here use command-line option `-e`,
+which passes the Ruby code to be executed on the command line itself:
+
+```sh
+$ ruby -e 'puts "Hello, World."'
+```
+
+Some examples here assume that file `desiderata.txt` exists:
+
+```
+$ cat desiderata.txt
+Go placidly amid the noise and the haste,
+and remember what peace there may be in silence.
+As far as possible, without surrender,
+be on good terms with all persons.
+```
+
+## Options
+
+### `-0`: \Set `$/` (Input Record Separator)
+
+Option `-0` defines the input record separator `$/`
+for the invoked Ruby program.
+
+The optional argument to the option must be octal digits,
+each in the range `0..7`;
+these digits are prefixed with digit `0` to form an octal value.
+
+If no argument is given, the input record separator is `0x00`.
+
+If an argument is given, it must immediately follow the option
+(no intervening whitespace or equal-sign character `'='`);
+argument values:
+
+- `0`: the input record separator is `''`;
+ see {Special Line Separator Values}[rdoc-ref:IO@Special+Line+Separator+Values].
+- In range `(1..0377)`:
+ the input record separator `$/` is set to the character value of the argument.
+- Any other octal value: the input record separator is `nil`.
+
+Examples:
+
+```sh
+$ ruby -0 -e 'p $/'
+"\x00"
+ruby -00 -e 'p $/'
+""
+$ ruby -012 -e 'p $/'
+"\n"
+$ ruby -015 -e 'p $/'
+"\r"
+$ ruby -0377 -e 'p $/'
+"\xFF"
+$ ruby -0400 -e 'p $/'
+nil
+```
+
+See also:
+
+- {Option -a}[rdoc-ref:ruby/options.md@a-3A+Split+Input+Lines+into+Fields]:
+ Split input lines into fields.
+- {Option -F}[rdoc-ref:ruby/options.md@F-3A+Set+Input+Field+Separator]:
+ \Set input field separator.
+- {Option -l}[rdoc-ref:ruby/options.md@l-3A+Set+Output+Record+Separator-3B+Chop+Lines]:
+ \Set output record separator; chop lines.
+- {Option -n}[rdoc-ref:ruby/options.md@n-3A+Run+Program+in+gets+Loop]:
+ Run program in `gets` loop.
+- {Option -p}[rdoc-ref:ruby/options.md@p-3A+-n-2C+with+Printing]:
+ `-n`, with printing.
+
+### `-a`: Split Input Lines into Fields
+
+Option `-a`, when given with either of options `-n` or `-p`,
+splits the string at `$_` into an array of strings at `$F`:
+
+```sh
+$ ruby -an -e 'p $F' desiderata.txt
+["Go", "placidly", "amid", "the", "noise", "and", "the", "haste,"]
+["and", "remember", "what", "peace", "there", "may", "be", "in", "silence."]
+["As", "far", "as", "possible,", "without", "surrender,"]
+["be", "on", "good", "terms", "with", "all", "persons."]
+```
+
+For the splitting,
+the default record separator is `$/`,
+and the default field separator is `$;`.
+
+See also:
+
+- {Option -0}[rdoc-ref:ruby/options.md@0-3A+Set+-24-2F+-28Input+Record+Separator-29]:
+ \Set `$/` (input record separator).
+- {Option -F}[rdoc-ref:ruby/options.md@F-3A+Set+Input+Field+Separator]:
+ \Set input field separator.
+- {Option -l}[rdoc-ref:ruby/options.md@l-3A+Set+Output+Record+Separator-3B+Chop+Lines]:
+ \Set output record separator; chop lines.
+- {Option -n}[rdoc-ref:ruby/options.md@n-3A+Run+Program+in+gets+Loop]:
+ Run program in `gets` loop.
+- {Option -p}[rdoc-ref:ruby/options.md@p-3A+-n-2C+with+Printing]:
+ `-n`, with printing.
+
+### `-c`: Check Syntax
+
+Option `-c` specifies that the specified Ruby program
+should be checked for syntax, but not actually executed:
+
+```
+$ ruby -e 'puts "Foo"'
+Foo
+$ ruby -c -e 'puts "Foo"'
+Syntax OK
+```
+
+### `-C`: \Set Working Directory
+
+The argument to option `-C` specifies a working directory
+for the invoked Ruby program;
+does not change the working directory for the current process:
+
+```sh
+$ basename `pwd`
+ruby
+$ ruby -C lib -e 'puts File.basename(Dir.pwd)'
+lib
+$ basename `pwd`
+ruby
+```
+
+Whitespace between the option and its argument may be omitted.
+
+### `-d`: \Set `$DEBUG` to `true`
+
+Some code in (or called by) the Ruby program may include statements or blocks
+conditioned by the global variable `$DEBUG` (e.g., `if $DEBUG`);
+these commonly write to `$stdout` or `$stderr`.
+
+The default value for `$DEBUG` is `false`;
+option `-d` sets it to `true`:
+
+```sh
+$ ruby -e 'p $DEBUG'
+false
+$ ruby -d -e 'p $DEBUG'
+true
+```
+
+Option `--debug` is an alias for option `-d`.
+
+### `-e`: Execute Given Ruby Code
+
+Option `-e` requires an argument, which is Ruby code to be executed;
+the option may be given more than once:
+
+```
+$ ruby -e 'puts "Foo"' -e 'puts "Bar"'
+Foo
+Bar
+```
+
+Whitespace between the option and its argument may be omitted.
+
+The command may include other options,
+but should not include arguments (which, if given, are ignored).
+
+### `-E`: \Set Default Encodings
+
+Option `-E` requires an argument, which specifies either the default external encoding,
+or both the default external and internal encodings for the invoked Ruby program:
+
+```
+# No option -E.
+$ ruby -e 'p [Encoding::default_external, Encoding::default_internal]'
+[#<Encoding:UTF-8>, nil]
+# Option -E with default external encoding.
+$ ruby -E cesu-8 -e 'p [Encoding::default_external, Encoding::default_internal]'
+[#<Encoding:CESU-8>, nil]
+# Option -E with default external and internal encodings.
+$ ruby -E utf-8:cesu-8 -e 'p [Encoding::default_external, Encoding::default_internal]'
+[#<Encoding:UTF-8>, #<Encoding:CESU-8>]
+```
+
+Whitespace between the option and its argument may be omitted.
+
+See also:
+
+- {Option --external-encoding}[options_md.html#label--external-encoding-3A+Set+Default+External+Encoding]:
+ \Set default external encoding.
+- {Option --internal-encoding}[options_md.html#label--internal-encoding-3A+Set+Default+Internal+Encoding]:
+ \Set default internal encoding.
+
+Option `--encoding` is an alias for option `-E`.
+
+### `-F`: \Set Input Field Separator
+
+Option `-F`, when given with option `-a`,
+specifies that its argument is to be the input field separator to be used for splitting:
+
+```sh
+$ ruby -an -Fs -e 'p $F' desiderata.txt
+["Go placidly amid the noi", "e and the ha", "te,\n"]
+["and remember what peace there may be in ", "ilence.\n"]
+["A", " far a", " po", "", "ible, without ", "urrender,\n"]
+["be on good term", " with all per", "on", ".\n"]
+```
+
+The argument may be a regular expression:
+
+```
+$ ruby -an -F'[.,]\s*' -e 'p $F' desiderata.txt
+["Go placidly amid the noise and the haste"]
+["and remember what peace there may be in silence"]
+["As far as possible", "without surrender"]
+["be on good terms with all persons"]
+```
+
+The argument must immediately follow the option
+(no intervening whitespace or equal-sign character `'='`).
+
+See also:
+
+- {Option -0}[rdoc-ref:ruby/options.md@0-3A+Set+-24-2F+-28Input+Record+Separator-29]:
+ \Set `$/` (input record separator).
+- {Option -a}[rdoc-ref:ruby/options.md@a-3A+Split+Input+Lines+into+Fields]:
+ Split input lines into fields.
+- {Option -l}[rdoc-ref:ruby/options.md@l-3A+Set+Output+Record+Separator-3B+Chop+Lines]:
+ \Set output record separator; chop lines.
+- {Option -n}[rdoc-ref:ruby/options.md@n-3A+Run+Program+in+gets+Loop]:
+ Run program in `gets` loop.
+- {Option -p}[rdoc-ref:ruby/options.md@p-3A+-n-2C+with+Printing]:
+ `-n`, with printing.
+
+### `-h`: Print Short Help Message
+
+Option `-h` prints a short help message
+that includes single-hyphen options (e.g. `-I`),
+and largely omits double-hyphen options (e.g., `--version`).
+
+Arguments and additional options are ignored.
+
+For a longer help message, use option `--help`.
+
+### `-i`: \Set \ARGF In-Place Mode
+
+Option `-i` sets the \ARGF in-place mode for the invoked Ruby program;
+see ARGF#inplace_mode=:
+
+```
+$ ruby -e 'p ARGF.inplace_mode'
+nil
+$ ruby -i -e 'p ARGF.inplace_mode'
+""
+$ ruby -i.bak -e 'p ARGF.inplace_mode'
+".bak"
+```
+
+### `-I`: Add to `$LOAD_PATH`
+
+The argument to option `-I` specifies a directory
+to be added to the array in global variable `$LOAD_PATH`;
+the option may be given more than once:
+
+```sh
+$ pushd /tmp
+$ ruby -e 'p $LOAD_PATH.size'
+8
+$ ruby -I my_lib -I some_lib -e 'p $LOAD_PATH.size'
+10
+$ ruby -I my_lib -I some_lib -e 'p $LOAD_PATH.take(2)'
+["/tmp/my_lib", "/tmp/some_lib"]
+$ popd
+```
+
+Whitespace between the option and its argument may be omitted.
+
+### `-l`: \Set Output Record Separator; Chop Lines
+
+Option `-l`, when given with option `-n` or `-p`,
+modifies line-ending processing by:
+
+- Setting global variable output record separator `$\`
+ to the current value of input record separator `$/`;
+ this affects line-oriented output (such a the output from Kernel#puts).
+- Calling String#chop! on each line read.
+
+Without option `-l` (unchopped):
+
+```sh
+$ ruby -n -e 'p $_' desiderata.txt
+"Go placidly amid the noise and the haste,\n"
+"and remember what peace there may be in silence.\n"
+"As far as possible, without surrender,\n"
+"be on good terms with all persons.\n"
+```
+
+With option `-l' (chopped):
+
+```sh
+$ ruby -ln -e 'p $_' desiderata.txt
+"Go placidly amid the noise and the haste,"
+"and remember what peace there may be in silence."
+"As far as possible, without surrender,"
+"be on good terms with all persons."
+```
+
+See also:
+
+- {Option -0}[rdoc-ref:ruby/options.md@0-3A+Set+-24-2F+-28Input+Record+Separator-29]:
+ \Set `$/` (input record separator).
+- {Option -a}[rdoc-ref:ruby/options.md@a-3A+Split+Input+Lines+into+Fields]:
+ Split input lines into fields.
+- {Option -F}[rdoc-ref:ruby/options.md@F-3A+Set+Input+Field+Separator]:
+ \Set input field separator.
+- {Option -n}[rdoc-ref:ruby/options.md@n-3A+Run+Program+in+gets+Loop]:
+ Run program in `gets` loop.
+- {Option -p}[rdoc-ref:ruby/options.md@p-3A+-n-2C+with+Printing]:
+ `-n`, with printing.
+
+### `-n`: Run Program in `gets` Loop
+
+Option `-n` runs your program in a Kernel#gets loop:
+
+```
+while gets
+ # Your Ruby code.
+end
+```
+
+Note that `gets` reads the next line and sets global variable `$_`
+to the last read line:
+
+```sh
+$ ruby -n -e 'puts $_' desiderata.txt
+Go placidly amid the noise and the haste,
+and remember what peace there may be in silence.
+As far as possible, without surrender,
+be on good terms with all persons.
+```
+
+See also:
+
+- {Option -0}[rdoc-ref:ruby/options.md@0-3A+Set+-24-2F+-28Input+Record+Separator-29]:
+ \Set `$/` (input record separator).
+- {Option -a}[rdoc-ref:ruby/options.md@a-3A+Split+Input+Lines+into+Fields]:
+ Split input lines into fields.
+- {Option -F}[rdoc-ref:ruby/options.md@F-3A+Set+Input+Field+Separator]:
+ \Set input field separator.
+- {Option -l}[rdoc-ref:ruby/options.md@l-3A+Set+Output+Record+Separator-3B+Chop+Lines]:
+ \Set output record separator; chop lines.
+- {Option -p}[rdoc-ref:ruby/options.md@p-3A+-n-2C+with+Printing]:
+ `-n`, with printing.
+
+### `-p`: `-n`, with Printing
+
+Option `-p` is like option `-n`, but also prints each line:
+
+```sh
+$ ruby -p -e 'puts $_.size' desiderata.txt
+42
+Go placidly amid the noise and the haste,
+49
+and remember what peace there may be in silence.
+39
+As far as possible, without surrender,
+35
+be on good terms with all persons.
+```
+
+See also:
+
+- {Option -0}[rdoc-ref:ruby/options.md@0-3A+Set+-24-2F+-28Input+Record+Separator-29]:
+ \Set `$/` (input record separator).
+- {Option -a}[rdoc-ref:ruby/options.md@a-3A+Split+Input+Lines+into+Fields]:
+ Split input lines into fields.
+- {Option -F}[rdoc-ref:ruby/options.md@F-3A+Set+Input+Field+Separator]:
+ \Set input field separator.
+- {Option -l}[rdoc-ref:ruby/options.md@l-3A+Set+Output+Record+Separator-3B+Chop+Lines]:
+ \Set output record separator; chop lines.
+- {Option -n}[rdoc-ref:ruby/options.md@n-3A+Run+Program+in+gets+Loop]:
+ Run program in `gets` loop.
+
+### `-r`: Require Library
+
+The argument to option `-r` specifies a library to be required
+before executing the Ruby program;
+the option may be given more than once:
+
+```sh
+$ ruby -e 'p defined?(JSON); p defined?(CSV)'
+nil
+nil
+$ ruby -r CSV -r JSON -e 'p defined?(JSON); p defined?(CSV)'
+"constant"
+"constant"
+```
+
+Whitespace between the option and its argument may be omitted.
+
+### `-s`: Define Global Variable
+
+Option `-s` specifies that a "custom option" is to define a global variable
+in the invoked Ruby program:
+
+- The custom option must appear _after_ the program name.
+- The custom option must begin with single hyphen (e.g., `-foo`),
+ not two hyphens (e.g., `--foo`).
+- The name of the global variable is based on the option name:
+ global variable `$foo` for custom option`-foo`.
+- The value of the global variable is the string option argument if given,
+ `true` otherwise.
+
+More than one custom option may be given:
+
+```
+$ cat t.rb
+p [$foo, $bar]
+$ ruby t.rb
+[nil, nil]
+$ ruby -s t.rb -foo=baz
+["baz", nil]
+$ ruby -s t.rb -foo
+[true, nil]
+$ ruby -s t.rb -foo=baz -bar=bat
+["baz", "bat"]
+```
+
+The option may not be used with
+{option -e}[rdoc-ref:ruby/options.md@e-3A+Execute+Given+Ruby+Code]
+
+### `-S`: Search Directories in `ENV['PATH']`
+
+Option `-S` specifies that the Ruby interpreter
+is to search (if necessary) the directories whose paths are in the program's
+`PATH` environment variable;
+the program is executed in the shell's current working directory
+(not necessarily in the directory where the program is found).
+
+This example uses adds path `'tmp/'` to the `PATH` environment variable:
+
+```sh
+$ export PATH=/tmp:$PATH
+$ echo "puts File.basename(Dir.pwd)" > /tmp/t.rb
+$ ruby -S t.rb
+ruby
+```
+
+### `-v`: Print Version; \Set `$VERBOSE`
+
+Options `-v` prints the Ruby version and sets global variable `$VERBOSE`:
+
+```
+$ ruby -e 'p $VERBOSE'
+false
+$ ruby -v -e 'p $VERBOSE'
+ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [x64-mingw-ucrt]
+true
+```
+
+### `-w`: Synonym for `-W1`
+
+Option `-w` (lowercase letter) is equivalent to option `-W1` (uppercase letter).
+
+### `-W`: \Set \Warning Policy
+
+Any Ruby code can create a <i>warning message</i> by calling method Kernel#warn;
+methods in the Ruby core and standard libraries can also create warning messages.
+Such a message may be printed on `$stderr`
+(or not, depending on certain settings).
+
+Option `-W` helps determine whether a particular warning message
+will be written,
+by setting the initial value of global variable `$-W`:
+
+- `-W0`: Sets `$-W` to `0` (silent; no warnings).
+- `-W1`: Sets `$-W` to `1` (moderate verbosity).
+- `-W2`: Sets `$-W` to `2` (high verbosity).
+- `-W`: Same as `-W2` (high verbosity).
+- Option not given: Same as `-W1` (moderate verbosity).
+
+The value of `$-W`, in turn, determines which warning messages (if any)
+are to be printed to `$stdout` (see Kernel#warn):
+
+```sh
+$ ruby -W1 -e 'p $foo'
+nil
+$ ruby -W2 -e 'p $foo'
+-e:1: warning: global variable '$foo' not initialized
+nil
+```
+
+Ruby code may also define warnings for certain categories;
+these are the default settings for the defined categories:
+
+```
+Warning[:experimental] # => true
+Warning[:deprecated] # => false
+Warning[:performance] # => false
+```
+
+They may also be set:
+```
+Warning[:experimental] = false
+Warning[:deprecated] = true
+Warning[:performance] = true
+```
+
+You can suppress a category by prefixing `no-` to the category name:
+
+```
+$ ruby -W:no-experimental -e 'p IO::Buffer.new'
+#<IO::Buffer>
+```
+
+### `-x`: Execute Ruby Code Found in Text
+
+Option `-x` executes a Ruby program whose code is embedded
+in other, non-code, text:
+
+The ruby code:
+
+- Begins after the first line beginning with `'#!` and containing string `'ruby'`.
+- Ends before any one of:
+
+ - End-of-file.
+ - A line consisting of `'__END__'`,
+ - Character `Ctrl-D` or `Ctrl-Z`.
+
+Example:
+
+```sh
+$ cat t.txt
+Leading garbage.
+#!ruby
+puts File.basename(Dir.pwd)
+__END__
+Trailing garbage.
+
+$ ruby -x t.txt
+ruby
+```
+
+The optional argument specifies the directory where the text file
+is to be found;
+the Ruby code is executed in that directory:
+
+```sh
+$ cp t.txt /tmp/
+$ ruby -x/tmp t.txt
+tmp
+$
+
+```
+
+If an argument is given, it must immediately follow the option
+(no intervening whitespace or equal-sign character `'='`).
+
+### `--backtrace-limit`: \Set Backtrace Limit
+
+Option `--backtrace-limit` sets a limit on the number of entries
+to be displayed in a backtrace.
+
+See Thread::Backtrace.limit.
+
+### `--copyright`: Print Ruby Copyright
+
+Option `--copyright` prints a copyright message:
+
+```sh
+$ ruby --copyright
+ruby - Copyright (C) 1993-2024 Yukihiro Matsumoto
+```
+
+### `--debug`: Alias for `-d`
+
+Option `--debug` is an alias for
+{option -d}[rdoc-ref:ruby/options.md@d-3A+Set+-24DEBUG+to+true].
+
+### `--disable`: Disable Features
+
+Option `--disable` specifies features to be disabled;
+the argument is a comma-separated list of the features to be disabled:
+
+```sh
+ruby --disable=gems,rubyopt t.rb
+```
+
+The supported features:
+
+- `gems`: Rubygems (default: enabled).
+- `did_you_mean`: [`did_you_mean`](https://github.com/ruby/did_you_mean) (default: enabled).
+- `rubyopt`: `RUBYOPT` environment variable (default: enabled).
+- `frozen-string-literal`: Freeze all string literals (default: disabled).
+- `jit`: JIT compiler (default: disabled).
+
+See also {option --enable}[options_md.html#label--enable-3A+Enable+Features].
+
+### `--dump`: Dump Items
+
+Option `--dump` specifies items to be dumped;
+the argument is a comma-separated list of the items.
+
+Some of the argument values cause the command to behave as if a different
+option was given:
+
+- `--dump=copyright`:
+ Same as {option \-\-copyright}[options_md.html#label--copyright-3A+Print+Ruby+Copyright].
+- `--dump=help`:
+ Same as {option \-\-help}[options_md.html#label--help-3A+Print+Help+Message].
+- `--dump=syntax`:
+ Same as {option -c}[rdoc-ref:ruby/options.md@c-3A+Check+Syntax].
+- `--dump=usage`:
+ Same as {option -h}[rdoc-ref:ruby/options.md@h-3A+Print+Short+Help+Message].
+- `--dump=version`:
+ Same as {option \-\-version}[options_md.html#label--version-3A+Print+Ruby+Version].
+
+For other argument values and examples,
+see {Option --dump}[option_dump_md.html].
+
+### `--enable`: Enable Features
+
+Option `--enable` specifies features to be enabled;
+the argument is a comma-separated list of the features to be enabled.
+
+```sh
+ruby --enable=gems,rubyopt t.rb
+```
+
+For the features,
+see {option --disable}[options_md.html#label--disable-3A+Disable+Features].
+
+### `--encoding`: Alias for `-E`.
+
+Option `--encoding` is an alias for
+{option -E}[rdoc-ref:ruby/options.md@E-3A+Set+Default+Encodings].
+
+### `--external-encoding`: \Set Default External \Encoding
+
+Option `--external-encoding`
+sets the default external encoding for the invoked Ruby program;
+for values of +encoding+,
+see {Encoding: Names and Aliases}[rdoc-ref:encodings.rdoc@Names+and+Aliases].
+
+```sh
+$ ruby -e 'puts Encoding::default_external'
+UTF-8
+$ ruby --external-encoding=cesu-8 -e 'puts Encoding::default_external'
+CESU-8
+```
+
+### `--help`: Print Help Message
+
+Option `--help` prints a long help message.
+
+Arguments and additional options are ignored.
+
+For a shorter help message, use option `-h`.
+
+### `--internal-encoding`: \Set Default Internal \Encoding
+
+Option `--internal-encoding`
+sets the default internal encoding for the invoked Ruby program;
+for values of +encoding+,
+see {Encoding: Names and Aliases}[rdoc-ref:encodings.rdoc@Names+and+Aliases].
+
+```sh
+$ ruby -e 'puts Encoding::default_internal.nil?'
+true
+$ ruby --internal-encoding=cesu-8 -e 'puts Encoding::default_internal'
+CESU-8
+```
+
+### `--verbose`: \Set `$VERBOSE`
+
+Option `--verbose` sets global variable `$VERBOSE` to `true`
+and disables input from `$stdin`.
+
+### `--version`: Print Ruby Version
+
+Option `--version` prints the version of the Ruby interpreter, then exits.
+
+## Experimental Options
+
+These options are experimental in the current Ruby release,
+and may be modified or withdrawn in later releases.
+
+### `--jit`
+
+Option `-jit` enables JIT compilation with the default option.
+
+#### `--jit-debug`
+
+Option `--jit-debug` enables JIT debugging (very slow);
+adds compiler flags if given.
+
+#### `--jit-max-cache=num`
+
+Option `--jit-max-cache=num` sets the maximum number of methods
+to be JIT-ed in a cache; default: 100).
+
+#### `--jit-min-calls=num`
+
+Option `jit-min-calls=num` sets the minimum number of calls to trigger JIT
+(for testing); default: 10000).
+
+#### `--jit-save-temps`
+
+Option `--jit-save-temps` saves JIT temporary files in $TMP or /tmp (for testing).
+
+#### `--jit-verbose`
+
+Option `--jit-verbose` prints JIT logs of level `num` or less
+to `$stderr`; default: 0.
+
+#### `--jit-wait`
+
+Option `--jit-wait` waits until JIT compilation finishes every time (for testing).
+
+#### `--jit-warnings`
+
+Option `--jit-warnings` enables printing of JIT warnings.
+
diff --git a/doc/security.rdoc b/doc/security.rdoc
index ae20ed30fa..e428036cf5 100644
--- a/doc/security.rdoc
+++ b/doc/security.rdoc
@@ -37,7 +37,7 @@ programs for configuration and database persistence of Ruby object trees.
Similar to +Marshal+, it is able to deserialize into arbitrary Ruby classes.
For example, the following YAML data will create an +ERB+ object when
-deserialized:
+deserialized, using the `unsafe_load` method:
!ruby/object:ERB
src: puts `uname`
diff --git a/doc/standard_library.rdoc b/doc/standard_library.rdoc
index e290a5b0f0..a9fca632f8 100644
--- a/doc/standard_library.rdoc
+++ b/doc/standard_library.rdoc
@@ -33,41 +33,33 @@ Socket:: Access underlying OS socket implementations
== Libraries
-Abbrev:: Calculates a set of unique abbreviations for a given set of strings
-Base64:: Support for encoding and decoding binary data using a Base64 representation
Benchmark:: Provides methods to measure and report the time used to execute code
Bundler:: Manage your Ruby application's gem dependencies
CGI:: Support for the Common Gateway Interface protocol
-CSV:: Provides an interface to read and write CSV files and data
Delegator:: Provides three abilities to delegate method calls to an object
DidYouMean:: "Did you mean?" experience in Ruby
-DRb:: Distributed object system for Ruby
English:: Provides references to special global variables with less cryptic names
ERB:: An easy to use but powerful templating system for Ruby
ErrorHighlight:: Highlight error location in your code
FileUtils:: Several file utility methods for copying, moving, removing, etc
Find:: This module supports top-down traversal of a set of file paths
Forwardable:: Provides delegation of specified methods to a designated object
-GetoptLong:: Parse command line options similar to the GNU C getopt_long()
IPAddr:: Provides methods to manipulate IPv4 and IPv6 IP addresses
IRB:: Interactive Ruby command-line tool for REPL (Read Eval Print Loop)
OptionParser:: Ruby-oriented class for command-line option analysis
Logger:: Provides a simple logging utility for outputting messages
-Mutex_m:: Mixin to extend objects to be handled like a Mutex
Net::HTTP:: HTTP client api for Ruby
-Observable:: Provides a mechanism for publish/subscribe pattern in Ruby
Open3:: Provides access to stdin, stdout and stderr when running other programs
OpenStruct:: Class to build custom data structures, similar to a Hash
OpenURI:: An easy-to-use wrapper for Net::HTTP, Net::HTTPS and Net::FTP
PP:: Provides a PrettyPrinter for Ruby objects
PrettyPrinter:: Implements a pretty printing algorithm for readable structure
+Prism:: A portable, error-tolerant Ruby parser
PStore:: Implements a file based persistence mechanism based on a Hash
Readline:: Wrapper for Readline extencion and Reline
Reline:: GNU Readline and Editline by pure Ruby implementation.
Resolv:: Thread-aware DNS resolver library in Ruby
-resolv-replace.rb:: Replace Socket DNS with Resolv
RDoc:: Produces HTML and command-line documentation for Ruby
-Rinda:: The Linda distributed computing paradigm in Ruby
SecureRandom:: Interface for secure random number generator
Set:: Provides a class to deal with collections of unordered, unique values
Shellwords:: Manipulates strings with word parsing rules of UNIX Bourne shell
@@ -84,7 +76,6 @@ WeakRef:: Allows a referenced object to be garbage-collected
== Extensions
-BigDecimal:: Provides arbitrary-precision floating point decimal arithmetic
Date:: A subclass of Object includes Comparable module for handling dates
DateTime:: Subclass of Date to handling dates, hours, minutes, seconds, offsets
Digest:: Provides a framework for message digest libraries
@@ -93,14 +84,11 @@ Fcntl:: Loads constants defined in the OS fcntl.h C header file
Fiddle:: A libffi wrapper for Ruby
IO:: Extensions for Ruby IO class, including #wait, #nonblock and ::console
JSON:: Implements Javascript Object Notation for Ruby
-NKF:: Ruby extension for Network Kanji Filter
OpenSSL:: Provides SSL, TLS and general purpose cryptography for Ruby
Pathname:: Representation of the name of a file or directory on the filesystem
Psych:: A YAML parser and emitter for Ruby
-Racc:: A LALR(1) parser generator written in Ruby.
StringIO:: Pseudo I/O on String objects
StringScanner:: Provides lexical scanning operations on a String
-Syslog:: Ruby interface for the POSIX system logging facility
WIN32OLE:: Provides an interface for OLE Automation in Ruby
Zlib:: Ruby interface for the zlib compression/decompression library
@@ -129,3 +117,16 @@ Prime:: Prime numbers and factorization library
RBS:: RBS is a language to describe the structure of Ruby programs
TypeProf:: A type analysis tool for Ruby code based on abstract interpretation
DEBUGGER__:: Debugging functionality for Ruby
+Racc:: A LALR(1) parser generator written in Ruby.
+Mutex_m:: Mixin to extend objects to be handled like a Mutex
+GetoptLong:: Parse command line options similar to the GNU C getopt_long()
+Base64:: Support for encoding and decoding binary data using a Base64 representation
+BigDecimal:: Provides arbitrary-precision floating point decimal arithmetic
+Observable:: Provides a mechanism for publish/subscribe pattern in Ruby
+Abbrev:: Calculates a set of unique abbreviations for a given set of strings
+resolv-replace.rb:: Replace Socket DNS with Resolv
+Rinda:: The Linda distributed computing paradigm in Ruby
+DRb:: Distributed object system for Ruby
+NKF:: Ruby extension for Network Kanji Filter
+Syslog:: Ruby interface for the POSIX system logging facility
+CSV:: Provides an interface to read and write CSV files and data
diff --git a/doc/strftime_formatting.rdoc b/doc/strftime_formatting.rdoc
index 30a629bf68..5c7b33155d 100644
--- a/doc/strftime_formatting.rdoc
+++ b/doc/strftime_formatting.rdoc
@@ -1,4 +1,4 @@
-== Formats for Dates and Times
+= Formats for Dates and Times
Several Ruby time-related classes have instance method +strftime+,
which returns a formatted string representing all or part of a date or time:
@@ -32,9 +32,9 @@ It consists of:
Except for the leading percent character,
the only required part is the conversion specifier, so we begin with that.
-=== Conversion Specifiers
+== Conversion Specifiers
-==== \Date (Year, Month, Day)
+=== \Date (Year, Month, Day)
- <tt>%Y</tt> - Year including century, zero-padded:
@@ -87,7 +87,7 @@ the only required part is the conversion specifier, so we begin with that.
Time.new(2002, 1, 1).strftime('%j') # => "001"
Time.new(2002, 12, 31).strftime('%j') # => "365"
-==== \Time (Hour, Minute, Second, Subsecond)
+=== \Time (Hour, Minute, Second, Subsecond)
- <tt>%H</tt> - Hour of the day, in range (0..23), zero-padded:
@@ -152,7 +152,7 @@ the only required part is the conversion specifier, so we begin with that.
Time.now.strftime('%s') # => "1656505136"
-==== Timezone
+=== Timezone
- <tt>%z</tt> - Timezone as hour and minute offset from UTC:
@@ -162,7 +162,7 @@ the only required part is the conversion specifier, so we begin with that.
Time.now.strftime('%Z') # => "Central Daylight Time"
-==== Weekday
+=== Weekday
- <tt>%A</tt> - Full weekday name:
@@ -184,7 +184,7 @@ the only required part is the conversion specifier, so we begin with that.
t.strftime('%a') # => "Sun"
t.strftime('%w') # => "0"
-==== Week Number
+=== Week Number
- <tt>%U</tt> - Week number of the year, in range (0..53), zero-padded,
where each week begins on a Sunday:
@@ -200,7 +200,7 @@ the only required part is the conversion specifier, so we begin with that.
t.strftime('%a') # => "Sun"
t.strftime('%W') # => "25"
-==== Week Dates
+=== Week Dates
See {ISO 8601 week dates}[https://en.wikipedia.org/wiki/ISO_8601#Week_dates].
@@ -223,7 +223,7 @@ See {ISO 8601 week dates}[https://en.wikipedia.org/wiki/ISO_8601#Week_dates].
t0.strftime('%V') # => "52"
t1.strftime('%V') # => "01"
-==== Literals
+=== Literals
- <tt>%n</tt> - Newline character "\n":
@@ -237,7 +237,7 @@ See {ISO 8601 week dates}[https://en.wikipedia.org/wiki/ISO_8601#Week_dates].
Time.now.strftime('%%') # => "%"
-==== Shorthand Conversion Specifiers
+=== Shorthand Conversion Specifiers
Each shorthand specifier here is shown with its corresponding
longhand specifier.
@@ -294,14 +294,14 @@ longhand specifier.
DateTime.now.strftime('%a %b %e %H:%M:%S %Z %Y')
# => "Wed Jun 29 08:32:18 -05:00 2022"
-=== Flags
+== Flags
Flags may affect certain formatting specifications.
Multiple flags may be given with a single conversion specified;
order does not matter.
-==== Padding Flags
+=== Padding Flags
- <tt>0</tt> - Pad with zeroes:
@@ -315,7 +315,7 @@ order does not matter.
Time.new(10).strftime('%-Y') # => "10"
-==== Casing Flags
+=== Casing Flags
- <tt>^</tt> - Upcase result:
@@ -328,7 +328,7 @@ order does not matter.
Time.now.strftime('%^p') # => "AM"
Time.now.strftime('%#p') # => "am"
-==== Timezone Flags
+=== Timezone Flags
- <tt>:</tt> - Put timezone as colon-separated hours and minutes:
@@ -338,7 +338,7 @@ order does not matter.
Time.now.strftime('%::z') # => "-05:00:00"
-=== Width Specifiers
+== Width Specifiers
The integer width specifier gives a minimum width for the returned string:
@@ -348,15 +348,15 @@ The integer width specifier gives a minimum width for the returned string:
Time.new(2002, 12).strftime('%10B') # => " December"
Time.new(2002, 12).strftime('%3B') # => "December" # Ignored if too small.
-== Specialized Format Strings
+= Specialized Format Strings
Here are a few specialized format strings,
each based on an external standard.
-=== HTTP Format
+== HTTP Format
The HTTP date format is based on
-{RFC 2616}[https://datatracker.ietf.org/doc/html/rfc2616],
+{RFC 2616}[https://www.rfc-editor.org/rfc/rfc2616],
and treats dates in the format <tt>'%a, %d %b %Y %T GMT'</tt>:
d = Date.new(2001, 2, 3) # => #<Date: 2001-02-03>
@@ -368,10 +368,10 @@ and treats dates in the format <tt>'%a, %d %b %Y %T GMT'</tt>:
Date._httpdate(httpdate)
# => {:wday=>6, :mday=>3, :mon=>2, :year=>2001, :hour=>0, :min=>0, :sec=>0, :zone=>"GMT", :offset=>0}
-=== RFC 3339 Format
+== RFC 3339 Format
The RFC 3339 date format is based on
-{RFC 3339}[https://datatracker.ietf.org/doc/html/rfc3339]:
+{RFC 3339}[https://www.rfc-editor.org/rfc/rfc3339]:
d = Date.new(2001, 2, 3) # => #<Date: 2001-02-03>
# Return 3339-formatted string.
@@ -382,10 +382,10 @@ The RFC 3339 date format is based on
Date._rfc3339(rfc3339)
# => {:year=>2001, :mon=>2, :mday=>3, :hour=>0, :min=>0, :sec=>0, :zone=>"+00:00", :offset=>0}
-=== RFC 2822 Format
+== RFC 2822 Format
The RFC 2822 date format is based on
-{RFC 2822}[https://datatracker.ietf.org/doc/html/rfc2822],
+{RFC 2822}[https://www.rfc-editor.org/rfc/rfc2822],
and treats dates in the format <tt>'%a, %-d %b %Y %T %z'</tt>]:
d = Date.new(2001, 2, 3) # => #<Date: 2001-02-03>
@@ -397,7 +397,7 @@ and treats dates in the format <tt>'%a, %-d %b %Y %T %z'</tt>]:
Date._rfc2822(rfc2822)
# => {:wday=>6, :mday=>3, :mon=>2, :year=>2001, :hour=>0, :min=>0, :sec=>0, :zone=>"+0000", :offset=>0}
-=== JIS X 0301 Format
+== JIS X 0301 Format
The JIS X 0301 format includes the
{Japanese era name}[https://en.wikipedia.org/wiki/Japanese_era_name],
@@ -412,7 +412,7 @@ with the first letter of the romanized era name prefixed:
# Return hash parsed from 0301-formatted string.
Date._jisx0301(jisx0301) # => {:year=>2001, :mon=>2, :mday=>3}
-=== ISO 8601 Format Specifications
+== ISO 8601 Format Specifications
This section shows format specifications that are compatible with
{ISO 8601}[https://en.wikipedia.org/wiki/ISO_8601].
@@ -422,7 +422,7 @@ Examples in this section assume:
t = Time.now # => 2022-06-29 16:49:25.465246 -0500
-==== Dates
+=== Dates
See {ISO 8601 dates}[https://en.wikipedia.org/wiki/ISO_8601#Dates].
@@ -473,7 +473,7 @@ See {ISO 8601 dates}[https://en.wikipedia.org/wiki/ISO_8601#Dates].
t.strftime('%Y-%j') # => "2022-180"
-==== Times
+=== Times
See {ISO 8601 times}[https://en.wikipedia.org/wiki/ISO_8601#Times].
@@ -514,7 +514,7 @@ See {ISO 8601 times}[https://en.wikipedia.org/wiki/ISO_8601#Times].
- {Coordinated Universal Time (UTC)}[https://en.wikipedia.org/wiki/ISO_8601#Coordinated_Universal_Time_(UTC)].
- {Time offsets from UTC}[https://en.wikipedia.org/wiki/ISO_8601#Time_offsets_from_UTC].
-==== Combined \Date and \Time
+=== Combined \Date and \Time
See {ISO 8601 Combined date and time representations}[https://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations].
diff --git a/doc/string/split.rdoc b/doc/string/split.rdoc
index 2b5e14ddb6..5ab065093b 100644
--- a/doc/string/split.rdoc
+++ b/doc/string/split.rdoc
@@ -9,7 +9,7 @@ When +field_sep+ is <tt>$;</tt>:
(see below).
- If <tt>$;</tt> is a string,
- the split ocurs just as if +field_sep+ were given as that string
+ the split occurs just as if +field_sep+ were given as that string
(see below).
When +field_sep+ is <tt>' '</tt> and +limit+ is +nil+,
diff --git a/doc/syntax.rdoc b/doc/syntax.rdoc
index 5895673f36..6ca5843512 100644
--- a/doc/syntax.rdoc
+++ b/doc/syntax.rdoc
@@ -37,3 +37,6 @@ Miscellaneous[rdoc-ref:syntax/miscellaneous.rdoc] ::
Comments[rdoc-ref:syntax/comments.rdoc] ::
Line and block code comments
+
+Operators[rdoc-ref:syntax/operators.rdoc] ::
+ Operator method behaviors
diff --git a/doc/syntax/assignment.rdoc b/doc/syntax/assignment.rdoc
index e30cb35adf..f45f5bc0ea 100644
--- a/doc/syntax/assignment.rdoc
+++ b/doc/syntax/assignment.rdoc
@@ -162,9 +162,7 @@ Here is an example of instance variable usage:
p object1.value # prints "some value"
p object2.value # prints "other value"
-An uninitialized instance variable has a value of +nil+. If you run Ruby with
-warnings enabled, you will get a warning when accessing an uninitialized
-instance variable.
+An uninitialized instance variable has a value of +nil+.
The +value+ method has access to the value set by the +initialize+ method, but
only for the same object.
diff --git a/doc/syntax/calling_methods.rdoc b/doc/syntax/calling_methods.rdoc
index da061dbfdb..c2c6c61a10 100644
--- a/doc/syntax/calling_methods.rdoc
+++ b/doc/syntax/calling_methods.rdoc
@@ -210,7 +210,7 @@ definition. If a keyword argument is given that the method did not list,
and the method definition does not accept arbitrary keyword arguments, an
ArgumentError will be raised.
-Keyword argument value can be omitted, meaning the value will be be fetched
+Keyword argument value can be omitted, meaning the value will be fetched
from the context by the name of the key
keyword1 = 'some value'
@@ -322,18 +322,6 @@ Both are equivalent to:
my_method(1, 2, 3)
-If the method accepts keyword arguments, the splat operator will convert a
-hash at the end of the array into keyword arguments:
-
- def my_method(a, b, c: 3)
- end
-
- arguments = [1, 2, { c: 4 }]
- my_method(*arguments)
-
-Note that this behavior is currently deprecated and will emit a warning.
-This behavior will be removed in Ruby 3.0.
-
You may also use the <code>**</code> (described next) to convert a Hash into
keyword arguments.
diff --git a/doc/syntax/comments.rdoc b/doc/syntax/comments.rdoc
index dbc7816984..00d19d588a 100644
--- a/doc/syntax/comments.rdoc
+++ b/doc/syntax/comments.rdoc
@@ -196,7 +196,7 @@ The method Module#const_set is not affected.
In this mode, all values assigned to constants are deeply copied and
made shareable. It is safer mode than +experimental_everything+.
- # shareable_constant_value: experimental_everything
+ # shareable_constant_value: experimental_copy
var = [{foo: []}]
var.frozen? # => false (assignment was made to local variable)
X = var # => calls `Ractor.make_shareable(var, copy: true)`
diff --git a/doc/syntax/control_expressions.rdoc b/doc/syntax/control_expressions.rdoc
index 5350585f15..9126289389 100644
--- a/doc/syntax/control_expressions.rdoc
+++ b/doc/syntax/control_expressions.rdoc
@@ -189,7 +189,7 @@ The same is true for +unless+.
The +case+ expression can be used in two ways.
The most common way is to compare an object against multiple patterns. The
-patterns are matched using the +===+ method which is aliased to +==+ on
+patterns are matched using the <tt>===</tt> method which is aliased to <tt>==</tt> on
Object. Other classes must override it to give meaningful behavior. See
Module#=== and Regexp#=== for examples.
diff --git a/doc/syntax/exceptions.rdoc b/doc/syntax/exceptions.rdoc
index 31e2f0175c..ac5ff78a95 100644
--- a/doc/syntax/exceptions.rdoc
+++ b/doc/syntax/exceptions.rdoc
@@ -86,7 +86,7 @@ To always run some code whether an exception was raised or not, use +ensure+:
rescue
# ...
ensure
- # this always runs
+ # this always runs BUT does not implicitly return the last evaluated statement.
end
You may also run some code when an exception is not raised:
@@ -96,7 +96,11 @@ You may also run some code when an exception is not raised:
rescue
# ...
else
- # this runs only when no exception was raised
+ # this runs only when no exception was raised AND return the last evaluated statement
ensure
- # ...
+ # this always runs.
+ # It is evaluated after the evaluation of either the `rescue` or the `else` block.
+ # It will not return implicitly.
end
+
+NB : Without explicit +return+ in the +ensure+ block, +begin+/+end+ block will return the last evaluated statement before entering in the `ensure` block.
diff --git a/doc/syntax/literals.rdoc b/doc/syntax/literals.rdoc
index b641433249..0c1e4a434b 100644
--- a/doc/syntax/literals.rdoc
+++ b/doc/syntax/literals.rdoc
@@ -414,9 +414,9 @@ slash (<tt>'/'</tt>) characters:
re = /foo/ # => /foo/
re.class # => Regexp
-The trailing slash may be followed by one or more _flag_ characters
-that modify the behavior.
-See {Regexp options}[rdoc-ref:Regexp@Options] for details.
+The trailing slash may be followed by one or more modifiers characters
+that set modes for the regexp.
+See {Regexp modes}[rdoc-ref:Regexp@Modes] for details.
Interpolation may be used inside regular expressions along with escaped
characters. Note that a regular expression may require additional escaped
@@ -523,9 +523,9 @@ A few "symmetrical" character pairs may be used as delimiters:
%r(foo) # => /foo/
%r<foo> # => /foo/
-The trailing delimiter may be followed by one or more _flag_ characters
-that modify the behavior.
-See {Regexp options}[rdoc-ref:Regexp@Options] for details.
+The trailing delimiter may be followed by one or more modifier characters
+that set modes for the regexp.
+See {Regexp modes}[rdoc-ref:Regexp@Modes] for details.
=== <tt>%x</tt>: Backtick Literals
diff --git a/doc/syntax/modules_and_classes.rdoc b/doc/syntax/modules_and_classes.rdoc
index 024815a5a6..9e05c5c774 100644
--- a/doc/syntax/modules_and_classes.rdoc
+++ b/doc/syntax/modules_and_classes.rdoc
@@ -40,9 +40,9 @@ functionality:
remove_method :my_method
end
-Reopening classes is a very powerful feature of Ruby, but it is best to only
-reopen classes you own. Reopening classes you do not own may lead to naming
-conflicts or difficult to diagnose bugs.
+Reopening modules (or classes) is a very powerful feature of Ruby, but it is
+best to only reopen modules you own. Reopening modules you do not own may lead
+to naming conflicts or difficult to diagnose bugs.
== Nesting
@@ -259,6 +259,28 @@ includes a minimum of built-in methods. You can use BasicObject to create an
independent inheritance structure. See the BasicObject documentation for
further details.
+Just like modules, classes can also be reopened. You can omit its superclass
+when you reopen a class. Specifying a different superclass than the previous
+definition will raise an error.
+
+ class C
+ end
+
+ class D < C
+ end
+
+ # OK
+ class D < C
+ end
+
+ # OK
+ class D
+ end
+
+ # TypeError: superclass mismatch for class D
+ class D < String
+ end
+
== Inheritance
Any method defined on a class is callable from its subclass:
diff --git a/doc/syntax/operators.rdoc b/doc/syntax/operators.rdoc
new file mode 100644
index 0000000000..d3045ac99e
--- /dev/null
+++ b/doc/syntax/operators.rdoc
@@ -0,0 +1,75 @@
+= Operators
+
+In Ruby, operators such as <code>+</code>, are defined as methods on the class.
+Literals[rdoc-ref:syntax/literals.rdoc] define their methods within the lower
+level, C language. String class, for example.
+
+Ruby objects can define or overload their own implementation for most operators.
+
+Here is an example:
+
+ class Foo < String
+ def +(str)
+ self.concat(str).concat("another string")
+ end
+ end
+
+ foobar = Foo.new("test ")
+ puts foobar + "baz "
+
+This prints:
+
+ test baz another string
+
+What operators are available is dependent on the implementing class.
+
+== Operator Behavior
+
+How a class behaves to a given operator is specific to that class, since
+operators are method implementations.
+
+When using an operator, it's the expression on the left-hand side of the
+operation that specifies the behavior.
+
+ 'a' * 3 #=> "aaa"
+ 3 * 'a' # TypeError: String can't be coerced into Integer
+
+== Logical Operators
+
+Logical operators are not methods, and therefore cannot be
+redefined/overloaded. They are tokenized at a lower level.
+
+Short-circuit logical operators (<code>&&</code>, <code>||</code>,
+<code>and</code>, and <code>or</code>) do not always result in a boolean value.
+Similar to blocks, it's the last evaluated expression that defines the result
+of the operation.
+
+=== <code>&&</code>, <code>and</code>
+
+Both <code>&&</code>/<code>and</code> operators provide short-circuiting by executing each
+side of the operator, left to right, and stopping at the first occurrence of a
+falsey expression. The expression that defines the result is the last one
+executed, whether it be the final expression, or the first occurrence of a falsey
+expression.
+
+Some examples:
+
+ true && 9 && "string" #=> "string"
+ (1 + 2) && nil && "string" #=> nil
+ (a = 1) && (b = false) && (c = "string") #=> false
+
+ puts a #=> 1
+ puts b #=> false
+ puts c #=> nil
+
+In this last example, <code>c</code> was initialized, but not defined.
+
+=== <code>||</code>, <code>or</code>
+
+The means by which <code>||</code>/<code>or</code> short-circuits, is to return the result of
+the first expression that is truthy.
+
+Some examples:
+
+ (1 + 2) || true || "string" #=> 3
+ false || nil || "string" #=> "string"
diff --git a/doc/syntax/refinements.rdoc b/doc/syntax/refinements.rdoc
index c900ab1bdc..17d5e67c21 100644
--- a/doc/syntax/refinements.rdoc
+++ b/doc/syntax/refinements.rdoc
@@ -279,6 +279,6 @@ Refinements in descendants have higher precedence than those of ancestors.
== Further Reading
-See https://bugs.ruby-lang.org/projects/ruby-master/wiki/RefinementsSpec for the
+See https://github.com/ruby/ruby/wiki/Refinements-Spec for the
current specification for implementing refinements. The specification also
contains more details.
diff --git a/doc/timezones.rdoc b/doc/timezones.rdoc
deleted file mode 100644
index c3aae88fde..0000000000
--- a/doc/timezones.rdoc
+++ /dev/null
@@ -1,108 +0,0 @@
-== Timezones
-
-=== Timezone Specifiers
-
-Certain \Time methods accept arguments that specify timezones:
-
-- Time.at: keyword argument +in:+.
-- Time.new: positional argument +zone+ or keyword argument +in:+.
-- Time.now: keyword argument +in:+.
-- Time#getlocal: positional argument +zone+.
-- Time#localtime: positional argument +zone+.
-
-The value given with any of these must be one of the following
-(each detailed below):
-
-- {Hours/minutes offset}[rdoc-ref:timezones.rdoc@Hours-2FMinutes+Offsets].
-- {Single-letter offset}[rdoc-ref:timezones.rdoc@Single-Letter+Offsets].
-- {Integer offset}[rdoc-ref:timezones.rdoc@Integer+Offsets].
-- {Timezone object}[rdoc-ref:timezones.rdoc@Timezone+Objects].
-
-==== Hours/Minutes Offsets
-
-The zone value may be a string offset from UTC
-in the form <tt>'+HH:MM'</tt> or <tt>'-HH:MM'</tt>,
-where:
-
-- +HH+ is the 2-digit hour in the range <tt>0..23</tt>.
-- +MM+ is the 2-digit minute in the range <tt>0..59</tt>.
-
-Examples:
-
- t = Time.utc(2000, 1, 1, 20, 15, 1) # => 2000-01-01 20:15:01 UTC
- Time.at(t, in: '-23:59') # => 1999-12-31 20:16:01 -2359
- Time.at(t, in: '+23:59') # => 2000-01-02 20:14:01 +2359
-
-==== Single-Letter Offsets
-
-The zone value may be a letter in the range <tt>'A'..'I'</tt>
-or <tt>'K'..'Z'</tt>;
-see {List of military time zones}[https://en.wikipedia.org/wiki/List_of_military_time_zones]:
-
- t = Time.utc(2000, 1, 1, 20, 15, 1) # => 2000-01-01 20:15:01 UTC
- Time.at(t, in: 'A') # => 2000-01-01 21:15:01 +0100
- Time.at(t, in: 'I') # => 2000-01-02 05:15:01 +0900
- Time.at(t, in: 'K') # => 2000-01-02 06:15:01 +1000
- Time.at(t, in: 'Y') # => 2000-01-01 08:15:01 -1200
- Time.at(t, in: 'Z') # => 2000-01-01 20:15:01 UTC
-
-==== \Integer Offsets
-
-The zone value may be an integer number of seconds
-in the range <tt>-86399..86399</tt>:
-
- t = Time.utc(2000, 1, 1, 20, 15, 1) # => 2000-01-01 20:15:01 UTC
- Time.at(t, in: -86399) # => 1999-12-31 20:15:02 -235959
- Time.at(t, in: 86399) # => 2000-01-02 20:15:00 +235959
-
-==== Timezone Objects
-
-In most cases, the zone value may be an object
-responding to certain timezone methods.
-
-\Exceptions (timezone object not allowed):
-
-- Time.new with positional argument +zone+.
-- Time.now with keyword argument +in:+.
-
-The timezone methods are:
-
-- +local_to_utc+:
-
- - Called when Time.new is invoked with +tz+
- as the value of positional argument +zone+ or keyword argument +in:+.
- - Argument: a <tt>Time::tm</tt> object.
- - Returns: a \Time-like object in the UTC timezone.
-
-- +utc_to_local+:
-
- - Called when Time.at or Time.now is invoked with +tz+
- as the value for keyword argument +in:+,
- and when Time#getlocal or Time#localtime is called with +tz+
- as the value for positional argument +zone+.
- - Argument: a <tt>Time::tm</tt> object.
- - Returns: a \Time-like object in the local timezone.
-
-A custom timezone class may have these instance methods,
-which will be called if defined:
-
-- +abbr+:
-
- - Called when Time#strftime is invoked with a format involving <tt>%Z</tt>.
- - Argument: a <tt>Time::tm</tt> object.
- - Returns: a string abbreviation for the timezone name.
-
-- +dst?+:
-
- - Called when Time.at or Time.now is invoked with +tz+
- as the value for keyword argument +in:+,
- and when Time#getlocal or Time#localtime is called with +tz+
- as the value for positional argument +zone+.
- - Argument: a <tt>Time::tm</tt> object.
- - Returns: whether the time is daylight saving time.
-
-- +name+:
-
- - Called when <tt>Marshal.dump(t) is invoked
- - Argument: none.
- - Returns: the string name of the timezone.
diff --git a/doc/windows.md b/doc/windows.md
index 8159b25861..2020eec9cf 100644
--- a/doc/windows.md
+++ b/doc/windows.md
@@ -2,9 +2,9 @@
Ruby supports a few native build platforms for Windows.
-* mswin: Build using Microsoft Visual C++ compiler
+* mswin: Build using Microsoft Visual C++ compiler with vcruntimeXXX.dll
* mingw-msvcrt: Build using compiler for Mingw with msvcrtXX.dll
-* mingw-ucrt: Build using compiler for Mingw with vcruntime.dll
+* mingw-ucrt: Build using compiler for Mingw with Windows Universal CRT
## Building Ruby using Mingw with UCRT
@@ -19,7 +19,7 @@ Ruby core development can be done either in Windows `cmd` like:
```
ridk enable ucrt64
-pacman -S --needed %MINGW_PACKAGE_PREFIX%-openssl %MINGW_PACKAGE_PREFIX%-libyaml
+pacman -S --needed %MINGW_PACKAGE_PREFIX%-openssl %MINGW_PACKAGE_PREFIX%-libyaml %MINGW_PACKAGE_PREFIX%-libffi
cd c:\
mkdir work
@@ -38,7 +38,7 @@ or in MSYS2 `bash` like:
ridk enable ucrt64
bash
-pacman -S --needed $MINGW_PACKAGE_PREFIX-openssl $MINGW_PACKAGE_PREFIX-libyaml
+pacman -S --needed $MINGW_PACKAGE_PREFIX-openssl $MINGW_PACKAGE_PREFIX-libyaml $MINGW_PACKAGE_PREFIX-libffi
cd /c/
mkdir work
@@ -59,9 +59,9 @@ make
### Requirement
-1. Windows 7 or later.
+1. Windows 10/Windows Server 2016 or later.
-2. Visual C++ 12.0 (2013) or later.
+2. Visual C++ 14.0 (2015) or later.
**Note** if you want to build x64 version, use native compiler for
x64.
@@ -80,7 +80,7 @@ make
4. If you want to build from GIT source, following commands are required.
* patch
* sed
- * ruby 2.0 or later
+ * ruby 3.0 or later
You can use [scoop](https://scoop.sh/) to install them like:
@@ -88,10 +88,11 @@ make
scoop install git ruby sed patch
```
-5. You need to install required libraries using [vcpkg](https://vcpkg.io/) like:
+5. You need to install required libraries using [vcpkg](https://vcpkg.io/) on
+ directory of ruby repository like:
```
- vcpkg --triplet x64-windows install openssl libffi libyaml zlib
+ vcpkg --triplet x64-windows install
```
6. Enable Command Extension of your command line. It's the default behavior
@@ -117,7 +118,7 @@ make
executable without console window if also you want.
3. You need specify vcpkg directory to use `--with-opt-dir`
- option like `configure --with-opt-dir=C:\vcpkg\installed\x64-windows`
+ option like `win32\configure.bat --with-opt-dir=vcpkg_installed\x64-windows`
4. Run `nmake up` if you are building from GIT source.
diff --git a/doc/yjit/yjit.md b/doc/yjit/yjit.md
index b9d5d725f4..8ea3409e48 100644
--- a/doc/yjit/yjit.md
+++ b/doc/yjit/yjit.md
@@ -4,26 +4,27 @@
</a>
</p>
-
YJIT - Yet Another Ruby JIT
===========================
YJIT is a lightweight, minimalistic Ruby JIT built inside CRuby.
It lazily compiles code using a Basic Block Versioning (BBV) architecture.
-The target use case is that of servers running Ruby on Rails.
YJIT is currently supported for macOS, Linux and BSD on x86-64 and arm64/aarch64 CPUs.
This project is open source and falls under the same license as CRuby.
<p align="center"><b>
If you're using YJIT in production, please
<a href="mailto:maxime.chevalierboisvert@shopify.com">share your success stories with us!</a>
- </b></p>
+</b></p>
If you wish to learn more about the approach taken, here are some conference talks and publications:
+- RubyKaigi 2023 keynote: [Optimizing YJIT’s Performance, from Inception to Production](https://www.youtube.com/watch?v=X0JRhh8w_4I)
+- RubyKaigi 2023 keynote: [Fitting Rust YJIT into CRuby](https://www.youtube.com/watch?v=GI7vvAgP_Qs)
- RubyKaigi 2022 keynote: [Stories from developing YJIT](https://www.youtube.com/watch?v=EMchdR9C8XM)
- RubyKaigi 2022 talk: [Building a Lightweight IR and Backend for YJIT](https://www.youtube.com/watch?v=BbLGqTxTRp0)
- RubyKaigi 2021 talk: [YJIT: Building a New JIT Compiler Inside CRuby](https://www.youtube.com/watch?v=PBVLf3yfMs8)
- Blog post: [YJIT: Building a New JIT Compiler Inside CRuby](https://pointersgonewild.com/2021/06/02/yjit-building-a-new-jit-compiler-inside-cruby/)
+- MPLR 2023 paper: [Evaluating YJIT’s Performance in a Production Context: A Pragmatic Approach](https://dl.acm.org/doi/10.1145/3617651.3622982)
- VMIL 2021 paper: [YJIT: A Basic Block Versioning JIT Compiler for CRuby](https://dl.acm.org/doi/10.1145/3486606.3486781)
- MoreVMs 2021 talk: [YJIT: Building a New JIT Compiler Inside CRuby](https://www.youtube.com/watch?v=vucLAqv7qpc)
- ECOOP 2016 talk: [Interprocedural Type Specialization of JavaScript Programs Without Type Analysis](https://www.youtube.com/watch?v=sRNBY7Ss97A)
@@ -31,31 +32,31 @@ If you wish to learn more about the approach taken, here are some conference tal
- ECOOP 2015 talk: [Simple and Effective Type Check Removal through Lazy Basic Block Versioning](https://www.youtube.com/watch?v=S-aHBuoiYE0)
- ECOOP 2015 paper: [Simple and Effective Type Check Removal through Lazy Basic Block Versioning](https://arxiv.org/pdf/1411.0352.pdf)
-To cite YJIT in your publications, please cite the VMIL 2021 paper:
+To cite YJIT in your publications, please cite the MPLR 2023 paper:
```
-@inproceedings{yjit_vmil2021,
-author = {Chevalier-Boisvert, Maxime and Gibbs, Noah and Boussier, Jean and Wu, Si Xing (Alan) and Patterson, Aaron and Newton, Kevin and Hawthorn, John},
-title = {YJIT: A Basic Block Versioning JIT Compiler for CRuby},
-year = {2021},
-isbn = {9781450391092},
+@inproceedings{yjit_mplr_2023,
+author = {Chevalier-Boisvert, Maxime and Kokubun, Takashi and Gibbs, Noah and Wu, Si Xing (Alan) and Patterson, Aaron and Issroff, Jemma},
+title = {Evaluating YJIT’s Performance in a Production Context: A Pragmatic Approach},
+year = {2023},
+isbn = {9798400703805},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
-url = {https://doi.org/10.1145/3486606.3486781},
-doi = {10.1145/3486606.3486781},
-booktitle = {Proceedings of the 13th ACM SIGPLAN International Workshop on Virtual Machines and Intermediate Languages},
-pages = {25–32},
-numpages = {8},
-keywords = {ruby, dynamically typed, compiler, optimization, just-in-time, bytecode},
-location = {Chicago, IL, USA},
-series = {VMIL 2021}
+url = {https://doi.org/10.1145/3617651.3622982},
+doi = {10.1145/3617651.3622982},
+booktitle = {Proceedings of the 20th ACM SIGPLAN International Conference on Managed Programming Languages and Runtimes},
+pages = {20–33},
+numpages = {14},
+keywords = {dynamically typed, optimization, just-in-time, virtual machine, ruby, compiler, bytecode},
+location = {Cascais, Portugal},
+series = {MPLR 2023}
}
```
## Current Limitations
-YJIT may not be suitable for certain applications. It currently only supports macOS and Linux on x86-64 and arm64/aarch64 CPUs. YJIT will use more memory than the Ruby interpreter because the JIT compiler needs to generate machine code in memory and maintain additional state information.
-You can change how much executable memory is allocated using [YJIT's command-line options](#command-line-options). There is a slight performance tradeoff because allocating less executable memory could result in the generated machine code being collected more often.
+YJIT may not be suitable for certain applications. It currently only supports macOS, Linux and BSD on x86-64 and arm64/aarch64 CPUs. YJIT will use more memory than the Ruby interpreter because the JIT compiler needs to generate machine code in memory and maintain additional state information.
+You can change how much executable memory is allocated using [YJIT's command-line options](#command-line-options).
## Installation
@@ -81,13 +82,13 @@ git clone https://github.com/ruby/ruby yjit
cd yjit
```
-The YJIT `ruby` binary can be built with either GCC or Clang. It can be built either in dev (debug) mode or in release mode. For maximum performance, compile YJIT in release mode with GCC. More detailed build instructions are provided in the [Ruby README](https://github.com/ruby/ruby#how-to-compile-and-install).
+The YJIT `ruby` binary can be built with either GCC or Clang. It can be built either in dev (debug) mode or in release mode. For maximum performance, compile YJIT in release mode with GCC. More detailed build instructions are provided in the [Ruby README](https://github.com/ruby/ruby#how-to-build).
```sh
# Configure in release mode for maximum performance, build and install
./autogen.sh
./configure --enable-yjit --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc
-make -j install
+make -j && make install
```
or
@@ -96,7 +97,7 @@ or
# Configure in lower-performance dev (debug) mode for development, build and install
./autogen.sh
./configure --enable-yjit=dev --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc
-make -j install
+make -j && make install
```
Dev mode includes extended YJIT statistics, but can be slow. For only statistics you can configure in stats mode:
@@ -105,7 +106,7 @@ Dev mode includes extended YJIT statistics, but can be slow. For only statistics
# Configure in extended-stats mode without slow runtime checks, build and install
./autogen.sh
./configure --enable-yjit=stats --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc
-make -j install
+make -j && make install
```
On macOS, you may need to specify where to find some libraries:
@@ -117,7 +118,7 @@ brew install openssl libyaml
# Configure in dev (debug) mode for development, build and install
./autogen.sh
./configure --enable-yjit=dev --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc --with-opt-dir="$(brew --prefix openssl):$(brew --prefix readline):$(brew --prefix libyaml)"
-make -j install
+make -j && make install
```
Typically configure will choose the default C compiler. To specify the C compiler, use
@@ -164,59 +165,127 @@ The machine code generated for a given method can be printed by adding `puts Rub
YJIT supports all command-line options supported by upstream CRuby, but also adds a few YJIT-specific options:
- `--yjit`: enable YJIT (disabled by default)
-- `--yjit-call-threshold=N`: number of calls after which YJIT begins to compile a function (default 30)
-- `--yjit-exec-mem-size=N`: size of the executable memory block to allocate, in MiB (default 64 MiB)
-- `--yjit-stats`: produce statistics after the execution of a program (incurs a small run-time cost)
-- `--yjit-trace-exits`: produce a Marshal dump of backtraces from specific exits. Automatically enables `--yjit-stats` (must configure and build with `--enable-yjit=stats` to use this)
-- `--yjit-max-versions=N`: maximum number of versions to generate per basic block (default 4)
-- `--yjit-greedy-versioning`: greedy versioning mode (disabled by default, may increase code size)
+- `--yjit-exec-mem-size=N`: size of the executable memory block to allocate, in MiB (default 48 MiB)
+- `--yjit-call-threshold=N`: number of calls after which YJIT begins to compile a function.
+ It defaults to 30, and it's then increased to 120 when the number of ISEQs in the process reaches 40,000.
+- `--yjit-cold-threshold=N`: number of global calls after which an ISEQ is considered cold and not
+ compiled, lower values mean less code is compiled (default 200K)
+- `--yjit-stats`: print statistics after the execution of a program (incurs a run-time cost)
+- `--yjit-stats=quiet`: gather statistics while running a program but don't print them. Stats are accessible through `RubyVM::YJIT.runtime_stats`. (incurs a run-time cost)
+- `--yjit-disable`: disable YJIT despite other `--yjit*` flags for lazily enabling it with `RubyVM::YJIT.enable`
+- `--yjit-code-gc`: enable code GC (disabled by default as of Ruby 3.3).
+ It will cause all machine code to be discarded when the executable memory size limit is hit, meaning JIT compilation will then start over.
+ This can allow you to use a lower executable memory size limit, but may cause a slight drop in performance when the limit is hit.
+- `--yjit-perf`: enable frame pointers and profiling with the `perf` tool
+- `--yjit-trace-exits`: produce a Marshal dump of backtraces from all exits. Automatically enables `--yjit-stats`
+- `--yjit-trace-exits=COUNTER`: produce a Marshal dump of backtraces from specified exits. Automatically enables `--yjit-stats`
+- `--yjit-trace-exits-sample-rate=N`: trace exit locations only every Nth occurrence. Automatically enables `--yjit-trace-exits`
Note that there is also an environment variable `RUBY_YJIT_ENABLE` which can be used to enable YJIT.
This can be useful for some deployment scripts where specifying an extra command-line option to Ruby is not practical.
-You can verify that YJIT is enabled by checking that `ruby -v --yjit` includes the string `+YJIT`:
+You can also enable YJIT at run-time using `RubyVM::YJIT.enable`. This can allow you to enable YJIT after your application is done
+booting, which makes it possible to avoid compiling any initialization code.
+
+You can verify that YJIT is enabled using `RubyVM::YJIT.enabled?` or by checking that `ruby --yjit -v` includes the string `+YJIT`:
```sh
-ruby -v --yjit
+ruby --yjit -v
ruby 3.3.0dev (2023-01-31T15:11:10Z master 2a0bf269c9) +YJIT dev [x86_64-darwin22]
+
+ruby --yjit -e "p RubyVM::YJIT.enabled?"
+true
+
+ruby -e "RubyVM::YJIT.enable; p RubyVM::YJIT.enabled?"
+true
```
### Benchmarking
-We have collected a set of benchmarks and implemented a simple benchmarking harness in the [yjit-bench](https://github.com/Shopify/yjit-bench) repository. This benchmarking harness is designed to disable CPU frequency scaling, set process affinity and disable address space randomization so that the variance between benchmarking runs will be as small as possible. Please kindly note that we are at an early stage in this project.
+We have collected a set of benchmarks and implemented a simple benchmarking harness in the [yjit-bench](https://github.com/Shopify/yjit-bench) repository. This benchmarking harness is designed to disable CPU frequency scaling, set process affinity and disable address space randomization so that the variance between benchmarking runs will be as small as possible.
+
+## Performance Tips for Production Deployments
+
+While YJIT options default to what we think would work well for most workloads,
+they might not necessarily be the best configuration for your application.
+This section covers tips on improving YJIT performance in case YJIT does not
+speed up your application in production.
+
+### Increasing --yjit-exec-mem-size
+
+When JIT code size (`RubyVM::YJIT.runtime_stats[:code_region_size]`) reaches this value,
+YJIT stops compiling new code. Increasing the executable memory size means more code
+can be optimized by YJIT, at the cost of more memory usage.
+
+If you start Ruby with `--yjit-stats`, e.g. using an environment variable `RUBYOPT=--yjit-stats`,
+`RubyVM::YJIT.runtime_stats[:ratio_in_yjit]` shows the ratio of YJIT-executed instructions in %.
+Ideally, `ratio_in_yjit` should be as large as 99%, and increasing `--yjit-exec-mem-size` often
+helps improving `ratio_in_yjit`.
+
+### Running workers as long as possible
+
+It's helpful to call the same code as many times as possible before a process restarts.
+If a process is killed too frequently, the time taken for compiling methods may outweigh
+the speedup obtained by compiling them.
-### Performance Tips
+You should monitor the number of requests each process has served.
+If you're periodically killing worker processes, e.g. with `unicorn-worker-killer` or `puma_worker_killer`,
+you may want to reduce the killing frequency or increase the limit.
+
+## Reducing YJIT Memory Usage
+
+YJIT allocates memory for JIT code and metadata. Enabling YJIT generally results in more memory usage.
+This section goes over tips on minimizing YJIT memory usage in case it uses more than your capacity.
+
+### Decreasing --yjit-exec-mem-size
+
+The `--yjit-exec-mem-size` option specifies the JIT code size, but YJIT also uses memory for its metadata,
+which often consumes more memory than JIT code. Generally, YJIT adds memory overhead by roughly
+3-4x of `--yjit-exec-mem-size` in production as of Ruby 3.3. You should multiply that by the number
+of worker processes to estimate the worst case memory overhead.
+
+`--yjit-exec-mem-size=48` is the default since Ruby 3.3.1,
+but smaller values like 32 MiB might make sense for your application.
+While doing so, you may want to monitor `RubyVM::YJIT.runtime_stats[:ratio_in_yjit]` as explained above.
+
+### Enabling YJIT lazily
+
+If you enable YJIT by `--yjit` options or `RUBY_YJIT_ENABLE=1`, YJIT may compile code that is
+used only during the application boot. `RubyVM::YJIT.enable` allows you to enable YJIT from Ruby code,
+and you can call this after your application is initialized, e.g. on Unicorn's `after_fork` hook.
+If you use any YJIT options (`--yjit-*`), YJIT will start at boot by default, but `--yjit-disable`
+allows you to start Ruby with the YJIT-disabled mode while passing YJIT tuning options.
+
+## Code Optimization Tips
This section contains tips on writing Ruby code that will run as fast as possible on YJIT. Some of this advice is based on current limitations of YJIT, while other advice is broadly applicable. It probably won't be practical to apply these tips everywhere in your codebase. You should ideally start by profiling your application using a tool such as [stackprof](https://github.com/tmm1/stackprof) so that you can determine which methods make up most of the execution time. You can then refactor the specific methods that make up the largest fractions of the execution time. We do not recommend modifying your entire codebase based on the current limitations of YJIT.
-- Use exceptions for error recovery only, not as part of normal control-flow
+- Avoid using `OpenStruct`
- Avoid redefining basic integer operations (i.e. +, -, <, >, etc.)
- Avoid redefining the meaning of `nil`, equality, etc.
- Avoid allocating objects in the hot parts of your code
- Minimize layers of indirection
- - Avoid classes that wrap objects if you can
- - Avoid methods that just call another method, trivial one liner methods
-- Try to write code so that the same variables always have the same type
-- Use while loops if you can, instead of `integer.times`
- - This is not idiomatic Ruby, but could help in hot methods
-- CRuby method calls are costly. Avoid things such as methods that only return a value from a hash or return a constant.
+ - Avoid writing wrapper classes if you can (e.g. a class that only wraps a Ruby hash)
+ - Avoid methods that just call another method
+- Ruby method calls are costly. Avoid things such as methods that only return a value from a hash
+- Try to write code so that the same variables and method arguments always have the same type
+- Avoid using `TracePoint` as it can cause YJIT to deoptimize code
+- Avoid using `Binding` as it can cause YJIT to deoptimize code
You can also use the `--yjit-stats` command-line option to see which bytecodes cause YJIT to exit, and refactor your code to avoid using these instructions in the hottest methods of your code.
### Other Statistics
-If you configure with --enable-yjit=dev or --enable-yjit=stats and run with `--yjit --yjit-stats`, YJIT will track and return performance statistics in `RubyVM::YJIT.runtime_stats`.
-
-You will get a much more limited set of statistics with YJIT enabled but not in dev or stats configuration.
+If you run `ruby` with `--yjit-stats`, YJIT will track and return performance statistics in `RubyVM::YJIT.runtime_stats`.
```rb
-$ RUBYOPT="--yjit --yjit-stats" irb
+$ RUBYOPT="--yjit-stats" irb
irb(main):001:0> RubyVM::YJIT.runtime_stats
=>
{:inline_code_size=>340745,
:outlined_code_size=>297664,
:all_stats=>true,
- :exec_instruction=>1547816,
+ :yjit_insns_count=>1547816,
:send_callsite_not_simple=>7267,
:send_kw_splat=>7,
:send_ivar_set_method=>72,
@@ -225,38 +294,38 @@ irb(main):001:0> RubyVM::YJIT.runtime_stats
Some of the counters include:
-:exec_instruction - how many Ruby bytecode instructions have been executed
-:yjit_insns_count - how many Ruby bytecode instruction have been executed in compiled code
-:binding_allocations - number of bindings allocated
-:binding_set - number of variables set via a binding
-:code_gc_count - number of garbage collections of compiled code since process start
-:vm_insns_count - number of instructions executed by the Ruby interpreter
-:compiled_iseq_count - number of bytecode sequences compiled
-:inline_code_size - size in bytes of compiled YJIT blocks
-:outline_code_size - size in bytes of YJIT error-handling compiled code
-:side_exit_count - number of side exits taken at runtime
-:total_exit_count - number of exits, including side exits, taken at runtime
-:avg_len_in_yjit - avg. number of instructions in compiled blocks before exiting to interpreter
+* :yjit_insns_count - how many Ruby bytecode instructions have been executed
+* :binding_allocations - number of bindings allocated
+* :binding_set - number of variables set via a binding
+* :code_gc_count - number of garbage collections of compiled code since process start
+* :vm_insns_count - number of instructions executed by the Ruby interpreter
+* :compiled_iseq_count - number of bytecode sequences compiled
+* :inline_code_size - size in bytes of compiled YJIT blocks
+* :outline_code_size - size in bytes of YJIT error-handling compiled code
+* :side_exit_count - number of side exits taken at runtime
+* :total_exit_count - number of exits, including side exits, taken at runtime
+* :avg_len_in_yjit - avg. number of instructions in compiled blocks before exiting to interpreter
-Counters starting with "exit_" show reasons for YJIT code taking a side exit (return to the interpreter.) See yjit_hacking.md for more details.
+Counters starting with "exit_" show reasons for YJIT code taking a side exit (return to the interpreter.)
-Performance counter names are not guaranteed to remain the same between Ruby versions. If you're curious what one does, it's usually best to search the source code for it &mdash; but it may change in a later Ruby version.
+Performance counter names are not guaranteed to remain the same between Ruby versions. If you're curious what each counter means,
+it's usually best to search the source code for it &mdash; but it may change in a later Ruby version.
-Some of these counters are available in non-stats configuration, but most counters are only in stats or dev configuration. The printed text after a --yjit-stats run includes other information that may be named differently than the information in runtime_stats.
+The printed text after a `--yjit-stats` run includes other information that may be named differently than the information in `RubyVM::YJIT.runtime_stats`.
## Contributing
-We welcome open source contributors. You should feel free to open new issues to report bugs or just to ask questions.
+We welcome open source contributions. You should feel free to open new issues to report bugs or just to ask questions.
Suggestions on how to make this readme file more helpful for new contributors are most welcome.
Bug fixes and bug reports are very valuable to us. If you find a bug in YJIT, it's very possible be that nobody has reported it before,
or that we don't have a good reproduction for it, so please open an issue and provide as much information as you can about your configuration and a description of how you encountered the problem. List the commands you used to run YJIT so that we can easily reproduce the issue on our end and investigate it. If you are able to produce a small program reproducing the error to help us track it down, that is very much appreciated as well.
-If you would like to contribute a large patch to YJIT, we suggest opening an issue or a discussion on this repository so that
+If you would like to contribute a large patch to YJIT, we suggest opening an issue or a discussion on the [Shopify/ruby repository](https://github.com/Shopify/ruby/issues) so that
we can have an active discussion. A common problem is that sometimes people submit large pull requests to open source projects
without prior communication, and we have to reject them because the work they implemented does not fit within the design of the
-project. We want to save you time and frustration, so please reach out and we can have a productive discussion as to how
-you can contribute things we will want to merge into YJIT.
+project. We want to save you time and frustration, so please reach out so we can have a productive discussion as to how
+you can contribute patches we will want to merge into YJIT.
### Source Code Organization
@@ -269,8 +338,8 @@ The YJIT source code is divided between:
- `yjit/src/core.rb`: basic block versioning logic, core structure of YJIT
- `yjit/src/stats.rs`: gathering of run-time statistics
- `yjit/src/options.rs`: handling of command-line options
-- `yjit/bindgen/src/main.rs`: C bindings exposed to the Rust codebase through bindgen
- `yjit/src/cruby.rs`: C bindings manually exposed to the Rust codebase
+- `yjit/bindgen/src/main.rs`: C bindings exposed to the Rust codebase through bindgen
The core of CRuby's interpreter logic is found in:
- `insns.def`: defines Ruby's bytecode instructions (gets compiled into `vm.inc`)
@@ -378,3 +447,71 @@ While in your i386 shell, install Cargo and Homebrew, then hack away!
2. Cargo will install in $HOME/.cargo by default, and I don't know a good way to change architectures after install
If you use Fish shell you can [read this link](https://tenderlovemaking.com/2022/01/07/homebrew-rosetta-and-ruby.html) for information on making the dev environment easier.
+
+## Profiling with Linux perf
+
+`--yjit-perf` allows you to profile JIT-ed methods along with other native functions using Linux perf.
+When you run Ruby with `perf record`, perf looks up `/tmp/perf-{pid}.map` to resolve symbols in JIT code,
+and this option lets YJIT write method symbols into that file as well as enabling frame pointers.
+
+### Call graph
+
+Here's an example way to use this option with [Firefox Profiler](https://profiler.firefox.com)
+(See also: [Profiling with Linux perf](https://profiler.firefox.com/docs/#/./guide-perf-profiling)):
+
+```bash
+# Compile the interpreter with frame pointers enabled
+./configure --enable-yjit --prefix=$HOME/.rubies/ruby-yjit --disable-install-doc cflags=-fno-omit-frame-pointer
+make -j && make install
+
+# [Optional] Allow running perf without sudo
+echo 0 | sudo tee /proc/sys/kernel/kptr_restrict
+echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid
+
+# Profile Ruby with --yjit-perf
+cd ../yjit-bench
+PERF="record --call-graph fp" ruby --yjit-perf -Iharness-perf benchmarks/liquid-render/benchmark.rb
+
+# View results on Firefox Profiler https://profiler.firefox.com.
+# Create /tmp/test.perf as below and upload it using "Load a profile from file".
+perf script --fields +pid > /tmp/test.perf
+```
+
+### YJIT codegen
+
+You can also profile the number of cycles consumed by code generated by each YJIT function.
+
+```bash
+# Install perf
+apt-get install linux-tools-common linux-tools-generic linux-tools-`uname -r`
+
+# [Optional] Allow running perf without sudo
+echo 0 | sudo tee /proc/sys/kernel/kptr_restrict
+echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid
+
+# Profile Ruby with --yjit-perf=codegen
+cd ../yjit-bench
+PERF=record ruby --yjit-perf=codegen -Iharness-perf benchmarks/lobsters/benchmark.rb
+
+# Aggregate results
+perf script > /tmp/perf.txt
+../ruby/misc/yjit_perf.py /tmp/perf.txt
+```
+
+#### Building perf with Python support
+
+The above instructions work fine for most people, but you could also use
+a handy `perf script -s` interface if you build perf from source.
+
+```bash
+# Build perf from source for Python support
+sudo apt-get install libpython3-dev python3-pip flex libtraceevent-dev \
+ libelf-dev libunwind-dev libaudit-dev libslang2-dev libdw-dev
+git clone --depth=1 https://github.com/torvalds/linux
+cd linux/tools/perf
+make
+make install
+
+# Aggregate results
+perf script -s ../ruby/misc/yjit_perf.py
+```
diff --git a/enc/Makefile.in b/enc/Makefile.in
index dd8ca1b528..6920bc9520 100644
--- a/enc/Makefile.in
+++ b/enc/Makefile.in
@@ -40,6 +40,7 @@ BUILTRUBY = $(topdir)/miniruby$(EXEEXT)
empty =
AR = @AR@
+LD = @LD@
CC = @CC@
ARFLAGS = @ARFLAGS@$(empty)
RANLIB = @RANLIB@
@@ -56,6 +57,7 @@ DEFS = @DEFS@
CPPFLAGS = @CPPFLAGS@ -DONIG_ENC_REGISTER=rb_enc_register
LDFLAGS = @LDFLAGS@
LDSHARED = @LDSHARED@
+POSTLINK = @POSTLINK@
ldflags = $(LDFLAGS)
dldflags = @DLDFLAGS@
extdldflags = @EXTDLDFLAGS@
@@ -70,6 +72,7 @@ WORKDIRS = @WORKDIRS@
NULLCMD = @NULLCMD@
RM = @RM@
+RMALL = @RMALL@
RMDIR = @RMDIR@
RMDIRS = @RMDIRS@
MAKEDIRS = @MAKEDIRS@
@@ -81,6 +84,9 @@ all:
make-workdir:
$(Q)$(MAKEDIRS) $(WORKDIRS)
+.PHONY: encs all modencs libencs enc libenc trans libtrans srcs
+.PHONY: clean distclean realclean clean-srcs
+
clean:
distclean: clean
diff --git a/enc/depend b/enc/depend
index 47a20e957a..2918a90a05 100644
--- a/enc/depend
+++ b/enc/depend
@@ -35,6 +35,7 @@ ENCSOS =<%ENCS.map {|e|%> $(ENCSODIR)/<%=e%>.$(DLEXT) \
<%}%> #
ENCCLEANLIBS = <%=cleanlibs.map {|clean|
clean.gsub(/\$\*(\.\w+)?/) {"$(ENCOBJS#{$1 ? ":.#{CONFIG["OBJEXT"]}=#{$1}" : ""})"}
+ .gsub(/\$\(\KTARGET_SO(?=[:\)])/) {"ENCSOS"}
}.join(" ")%>
ENCCLEANOBJS = <%=cleanobjs.map {|clean|
clean.gsub(/\$\*(\.\w+)?/) {"$(ENCOBJS#{$1 ? ":.#{CONFIG["OBJEXT"]}=#{$1}" : ""})"}
@@ -51,6 +52,7 @@ TRANSSOS =<%TRANS.map {|e|%> $(ENCSODIR)/<%=e%>.$(DLEXT) \
<%}%> #
TRANSCLEANLIBS = <%=cleanlibs.map {|clean|
clean.gsub(/\$\*(\.\w+)?/) {"$(TRANSOBJS#{$1 ? ":.#{CONFIG["OBJEXT"]}=#{$1}" : ""})"}
+ .gsub(/\$\(\KTARGET_SO(?=[:\)])/) {"TRANSSOS"}
}.join(" ")%>
TRANSCLEANOBJS = <%=cleanobjs.map {|clean|
clean.gsub(/\$\*(\.\w+)?/) {"$(TRANSOBJS#{$1 ? ":.#{CONFIG["OBJEXT"]}=#{$1}" : ""})"}
@@ -58,12 +60,8 @@ TRANSCLEANOBJS = <%=cleanobjs.map {|clean|
LIBTRANS=enc/libtrans.$(LIBEXT)
UNICODE_HDR_DIR = --missing-unicode-header-dir--
-encs: all
-% if MODULE_TYPE == :static
-all: libenc libtrans
-% else
-all: enc trans
-%end
+encs all: <%= MODULE_TYPE == :static ? "lib" : "mod" %>encs
+modencs: enc trans
libencs: libenc libtrans
enc: $(ENCSOS)
libenc: $(LIBENC)
@@ -155,7 +153,7 @@ enc/trans/transdb.$(OBJEXT): transdb.h
clean:
% %w[$(ENCSOS) $(LIBENC) $(ENCOBJS) $(ENCCLEANOBJS) $(ENCCLEANLIBS) $(TRANSSOS) $(LIBTRANS) $(TRANSOBJS) $(TRANSCLEANOBJS) $(TRANSCLEANLIBS) $(ENC_TRANS_D) $(ENC_TRANS_SO_D)].each do |clean|
- $(Q)$(RM) <%=pathrep[clean]%>
+ $(Q)$(RMALL) <%=pathrep[clean]%>
% end
% unless inplace
$(Q)$(RM) enc/unicode/*/casefold.h enc/unicode/*/name2ctype.h
@@ -338,6 +336,7 @@ enc/ascii.$(OBJEXT): internal/special_consts.h
enc/ascii.$(OBJEXT): internal/static_assert.h
enc/ascii.$(OBJEXT): internal/stdalign.h
enc/ascii.$(OBJEXT): internal/stdbool.h
+enc/ascii.$(OBJEXT): internal/stdckdint.h
enc/ascii.$(OBJEXT): internal/symbol.h
enc/ascii.$(OBJEXT): internal/value.h
enc/ascii.$(OBJEXT): internal/value_type.h
@@ -499,6 +498,7 @@ enc/big5.$(OBJEXT): internal/special_consts.h
enc/big5.$(OBJEXT): internal/static_assert.h
enc/big5.$(OBJEXT): internal/stdalign.h
enc/big5.$(OBJEXT): internal/stdbool.h
+enc/big5.$(OBJEXT): internal/stdckdint.h
enc/big5.$(OBJEXT): internal/symbol.h
enc/big5.$(OBJEXT): internal/value.h
enc/big5.$(OBJEXT): internal/value_type.h
@@ -670,6 +670,7 @@ enc/cesu_8.$(OBJEXT): internal/special_consts.h
enc/cesu_8.$(OBJEXT): internal/static_assert.h
enc/cesu_8.$(OBJEXT): internal/stdalign.h
enc/cesu_8.$(OBJEXT): internal/stdbool.h
+enc/cesu_8.$(OBJEXT): internal/stdckdint.h
enc/cesu_8.$(OBJEXT): internal/symbol.h
enc/cesu_8.$(OBJEXT): internal/value.h
enc/cesu_8.$(OBJEXT): internal/value_type.h
@@ -831,6 +832,7 @@ enc/cp949.$(OBJEXT): internal/special_consts.h
enc/cp949.$(OBJEXT): internal/static_assert.h
enc/cp949.$(OBJEXT): internal/stdalign.h
enc/cp949.$(OBJEXT): internal/stdbool.h
+enc/cp949.$(OBJEXT): internal/stdckdint.h
enc/cp949.$(OBJEXT): internal/symbol.h
enc/cp949.$(OBJEXT): internal/value.h
enc/cp949.$(OBJEXT): internal/value_type.h
@@ -991,6 +993,7 @@ enc/emacs_mule.$(OBJEXT): internal/special_consts.h
enc/emacs_mule.$(OBJEXT): internal/static_assert.h
enc/emacs_mule.$(OBJEXT): internal/stdalign.h
enc/emacs_mule.$(OBJEXT): internal/stdbool.h
+enc/emacs_mule.$(OBJEXT): internal/stdckdint.h
enc/emacs_mule.$(OBJEXT): internal/symbol.h
enc/emacs_mule.$(OBJEXT): internal/value.h
enc/emacs_mule.$(OBJEXT): internal/value_type.h
@@ -1161,6 +1164,7 @@ enc/encdb.$(OBJEXT): internal/special_consts.h
enc/encdb.$(OBJEXT): internal/static_assert.h
enc/encdb.$(OBJEXT): internal/stdalign.h
enc/encdb.$(OBJEXT): internal/stdbool.h
+enc/encdb.$(OBJEXT): internal/stdckdint.h
enc/encdb.$(OBJEXT): internal/symbol.h
enc/encdb.$(OBJEXT): internal/value.h
enc/encdb.$(OBJEXT): internal/value_type.h
@@ -1324,6 +1328,7 @@ enc/euc_jp.$(OBJEXT): internal/special_consts.h
enc/euc_jp.$(OBJEXT): internal/static_assert.h
enc/euc_jp.$(OBJEXT): internal/stdalign.h
enc/euc_jp.$(OBJEXT): internal/stdbool.h
+enc/euc_jp.$(OBJEXT): internal/stdckdint.h
enc/euc_jp.$(OBJEXT): internal/symbol.h
enc/euc_jp.$(OBJEXT): internal/value.h
enc/euc_jp.$(OBJEXT): internal/value_type.h
@@ -1484,6 +1489,7 @@ enc/euc_kr.$(OBJEXT): internal/special_consts.h
enc/euc_kr.$(OBJEXT): internal/static_assert.h
enc/euc_kr.$(OBJEXT): internal/stdalign.h
enc/euc_kr.$(OBJEXT): internal/stdbool.h
+enc/euc_kr.$(OBJEXT): internal/stdckdint.h
enc/euc_kr.$(OBJEXT): internal/symbol.h
enc/euc_kr.$(OBJEXT): internal/value.h
enc/euc_kr.$(OBJEXT): internal/value_type.h
@@ -1644,6 +1650,7 @@ enc/euc_tw.$(OBJEXT): internal/special_consts.h
enc/euc_tw.$(OBJEXT): internal/static_assert.h
enc/euc_tw.$(OBJEXT): internal/stdalign.h
enc/euc_tw.$(OBJEXT): internal/stdbool.h
+enc/euc_tw.$(OBJEXT): internal/stdckdint.h
enc/euc_tw.$(OBJEXT): internal/symbol.h
enc/euc_tw.$(OBJEXT): internal/value.h
enc/euc_tw.$(OBJEXT): internal/value_type.h
@@ -1804,6 +1811,7 @@ enc/gb18030.$(OBJEXT): internal/special_consts.h
enc/gb18030.$(OBJEXT): internal/static_assert.h
enc/gb18030.$(OBJEXT): internal/stdalign.h
enc/gb18030.$(OBJEXT): internal/stdbool.h
+enc/gb18030.$(OBJEXT): internal/stdckdint.h
enc/gb18030.$(OBJEXT): internal/symbol.h
enc/gb18030.$(OBJEXT): internal/value.h
enc/gb18030.$(OBJEXT): internal/value_type.h
@@ -1964,6 +1972,7 @@ enc/gb2312.$(OBJEXT): internal/special_consts.h
enc/gb2312.$(OBJEXT): internal/static_assert.h
enc/gb2312.$(OBJEXT): internal/stdalign.h
enc/gb2312.$(OBJEXT): internal/stdbool.h
+enc/gb2312.$(OBJEXT): internal/stdckdint.h
enc/gb2312.$(OBJEXT): internal/symbol.h
enc/gb2312.$(OBJEXT): internal/value.h
enc/gb2312.$(OBJEXT): internal/value_type.h
@@ -2124,6 +2133,7 @@ enc/gbk.$(OBJEXT): internal/special_consts.h
enc/gbk.$(OBJEXT): internal/static_assert.h
enc/gbk.$(OBJEXT): internal/stdalign.h
enc/gbk.$(OBJEXT): internal/stdbool.h
+enc/gbk.$(OBJEXT): internal/stdckdint.h
enc/gbk.$(OBJEXT): internal/symbol.h
enc/gbk.$(OBJEXT): internal/value.h
enc/gbk.$(OBJEXT): internal/value_type.h
@@ -2285,6 +2295,7 @@ enc/iso_8859_1.$(OBJEXT): internal/special_consts.h
enc/iso_8859_1.$(OBJEXT): internal/static_assert.h
enc/iso_8859_1.$(OBJEXT): internal/stdalign.h
enc/iso_8859_1.$(OBJEXT): internal/stdbool.h
+enc/iso_8859_1.$(OBJEXT): internal/stdckdint.h
enc/iso_8859_1.$(OBJEXT): internal/symbol.h
enc/iso_8859_1.$(OBJEXT): internal/value.h
enc/iso_8859_1.$(OBJEXT): internal/value_type.h
@@ -2446,6 +2457,7 @@ enc/iso_8859_10.$(OBJEXT): internal/special_consts.h
enc/iso_8859_10.$(OBJEXT): internal/static_assert.h
enc/iso_8859_10.$(OBJEXT): internal/stdalign.h
enc/iso_8859_10.$(OBJEXT): internal/stdbool.h
+enc/iso_8859_10.$(OBJEXT): internal/stdckdint.h
enc/iso_8859_10.$(OBJEXT): internal/symbol.h
enc/iso_8859_10.$(OBJEXT): internal/value.h
enc/iso_8859_10.$(OBJEXT): internal/value_type.h
@@ -2606,6 +2618,7 @@ enc/iso_8859_11.$(OBJEXT): internal/special_consts.h
enc/iso_8859_11.$(OBJEXT): internal/static_assert.h
enc/iso_8859_11.$(OBJEXT): internal/stdalign.h
enc/iso_8859_11.$(OBJEXT): internal/stdbool.h
+enc/iso_8859_11.$(OBJEXT): internal/stdckdint.h
enc/iso_8859_11.$(OBJEXT): internal/symbol.h
enc/iso_8859_11.$(OBJEXT): internal/value.h
enc/iso_8859_11.$(OBJEXT): internal/value_type.h
@@ -2767,6 +2780,7 @@ enc/iso_8859_13.$(OBJEXT): internal/special_consts.h
enc/iso_8859_13.$(OBJEXT): internal/static_assert.h
enc/iso_8859_13.$(OBJEXT): internal/stdalign.h
enc/iso_8859_13.$(OBJEXT): internal/stdbool.h
+enc/iso_8859_13.$(OBJEXT): internal/stdckdint.h
enc/iso_8859_13.$(OBJEXT): internal/symbol.h
enc/iso_8859_13.$(OBJEXT): internal/value.h
enc/iso_8859_13.$(OBJEXT): internal/value_type.h
@@ -2928,6 +2942,7 @@ enc/iso_8859_14.$(OBJEXT): internal/special_consts.h
enc/iso_8859_14.$(OBJEXT): internal/static_assert.h
enc/iso_8859_14.$(OBJEXT): internal/stdalign.h
enc/iso_8859_14.$(OBJEXT): internal/stdbool.h
+enc/iso_8859_14.$(OBJEXT): internal/stdckdint.h
enc/iso_8859_14.$(OBJEXT): internal/symbol.h
enc/iso_8859_14.$(OBJEXT): internal/value.h
enc/iso_8859_14.$(OBJEXT): internal/value_type.h
@@ -3089,6 +3104,7 @@ enc/iso_8859_15.$(OBJEXT): internal/special_consts.h
enc/iso_8859_15.$(OBJEXT): internal/static_assert.h
enc/iso_8859_15.$(OBJEXT): internal/stdalign.h
enc/iso_8859_15.$(OBJEXT): internal/stdbool.h
+enc/iso_8859_15.$(OBJEXT): internal/stdckdint.h
enc/iso_8859_15.$(OBJEXT): internal/symbol.h
enc/iso_8859_15.$(OBJEXT): internal/value.h
enc/iso_8859_15.$(OBJEXT): internal/value_type.h
@@ -3250,6 +3266,7 @@ enc/iso_8859_16.$(OBJEXT): internal/special_consts.h
enc/iso_8859_16.$(OBJEXT): internal/static_assert.h
enc/iso_8859_16.$(OBJEXT): internal/stdalign.h
enc/iso_8859_16.$(OBJEXT): internal/stdbool.h
+enc/iso_8859_16.$(OBJEXT): internal/stdckdint.h
enc/iso_8859_16.$(OBJEXT): internal/symbol.h
enc/iso_8859_16.$(OBJEXT): internal/value.h
enc/iso_8859_16.$(OBJEXT): internal/value_type.h
@@ -3411,6 +3428,7 @@ enc/iso_8859_2.$(OBJEXT): internal/special_consts.h
enc/iso_8859_2.$(OBJEXT): internal/static_assert.h
enc/iso_8859_2.$(OBJEXT): internal/stdalign.h
enc/iso_8859_2.$(OBJEXT): internal/stdbool.h
+enc/iso_8859_2.$(OBJEXT): internal/stdckdint.h
enc/iso_8859_2.$(OBJEXT): internal/symbol.h
enc/iso_8859_2.$(OBJEXT): internal/value.h
enc/iso_8859_2.$(OBJEXT): internal/value_type.h
@@ -3572,6 +3590,7 @@ enc/iso_8859_3.$(OBJEXT): internal/special_consts.h
enc/iso_8859_3.$(OBJEXT): internal/static_assert.h
enc/iso_8859_3.$(OBJEXT): internal/stdalign.h
enc/iso_8859_3.$(OBJEXT): internal/stdbool.h
+enc/iso_8859_3.$(OBJEXT): internal/stdckdint.h
enc/iso_8859_3.$(OBJEXT): internal/symbol.h
enc/iso_8859_3.$(OBJEXT): internal/value.h
enc/iso_8859_3.$(OBJEXT): internal/value_type.h
@@ -3733,6 +3752,7 @@ enc/iso_8859_4.$(OBJEXT): internal/special_consts.h
enc/iso_8859_4.$(OBJEXT): internal/static_assert.h
enc/iso_8859_4.$(OBJEXT): internal/stdalign.h
enc/iso_8859_4.$(OBJEXT): internal/stdbool.h
+enc/iso_8859_4.$(OBJEXT): internal/stdckdint.h
enc/iso_8859_4.$(OBJEXT): internal/symbol.h
enc/iso_8859_4.$(OBJEXT): internal/value.h
enc/iso_8859_4.$(OBJEXT): internal/value_type.h
@@ -3893,6 +3913,7 @@ enc/iso_8859_5.$(OBJEXT): internal/special_consts.h
enc/iso_8859_5.$(OBJEXT): internal/static_assert.h
enc/iso_8859_5.$(OBJEXT): internal/stdalign.h
enc/iso_8859_5.$(OBJEXT): internal/stdbool.h
+enc/iso_8859_5.$(OBJEXT): internal/stdckdint.h
enc/iso_8859_5.$(OBJEXT): internal/symbol.h
enc/iso_8859_5.$(OBJEXT): internal/value.h
enc/iso_8859_5.$(OBJEXT): internal/value_type.h
@@ -4053,6 +4074,7 @@ enc/iso_8859_6.$(OBJEXT): internal/special_consts.h
enc/iso_8859_6.$(OBJEXT): internal/static_assert.h
enc/iso_8859_6.$(OBJEXT): internal/stdalign.h
enc/iso_8859_6.$(OBJEXT): internal/stdbool.h
+enc/iso_8859_6.$(OBJEXT): internal/stdckdint.h
enc/iso_8859_6.$(OBJEXT): internal/symbol.h
enc/iso_8859_6.$(OBJEXT): internal/value.h
enc/iso_8859_6.$(OBJEXT): internal/value_type.h
@@ -4213,6 +4235,7 @@ enc/iso_8859_7.$(OBJEXT): internal/special_consts.h
enc/iso_8859_7.$(OBJEXT): internal/static_assert.h
enc/iso_8859_7.$(OBJEXT): internal/stdalign.h
enc/iso_8859_7.$(OBJEXT): internal/stdbool.h
+enc/iso_8859_7.$(OBJEXT): internal/stdckdint.h
enc/iso_8859_7.$(OBJEXT): internal/symbol.h
enc/iso_8859_7.$(OBJEXT): internal/value.h
enc/iso_8859_7.$(OBJEXT): internal/value_type.h
@@ -4373,6 +4396,7 @@ enc/iso_8859_8.$(OBJEXT): internal/special_consts.h
enc/iso_8859_8.$(OBJEXT): internal/static_assert.h
enc/iso_8859_8.$(OBJEXT): internal/stdalign.h
enc/iso_8859_8.$(OBJEXT): internal/stdbool.h
+enc/iso_8859_8.$(OBJEXT): internal/stdckdint.h
enc/iso_8859_8.$(OBJEXT): internal/symbol.h
enc/iso_8859_8.$(OBJEXT): internal/value.h
enc/iso_8859_8.$(OBJEXT): internal/value_type.h
@@ -4534,6 +4558,7 @@ enc/iso_8859_9.$(OBJEXT): internal/special_consts.h
enc/iso_8859_9.$(OBJEXT): internal/static_assert.h
enc/iso_8859_9.$(OBJEXT): internal/stdalign.h
enc/iso_8859_9.$(OBJEXT): internal/stdbool.h
+enc/iso_8859_9.$(OBJEXT): internal/stdckdint.h
enc/iso_8859_9.$(OBJEXT): internal/symbol.h
enc/iso_8859_9.$(OBJEXT): internal/value.h
enc/iso_8859_9.$(OBJEXT): internal/value_type.h
@@ -4694,6 +4719,7 @@ enc/koi8_r.$(OBJEXT): internal/special_consts.h
enc/koi8_r.$(OBJEXT): internal/static_assert.h
enc/koi8_r.$(OBJEXT): internal/stdalign.h
enc/koi8_r.$(OBJEXT): internal/stdbool.h
+enc/koi8_r.$(OBJEXT): internal/stdckdint.h
enc/koi8_r.$(OBJEXT): internal/symbol.h
enc/koi8_r.$(OBJEXT): internal/value.h
enc/koi8_r.$(OBJEXT): internal/value_type.h
@@ -4854,6 +4880,7 @@ enc/koi8_u.$(OBJEXT): internal/special_consts.h
enc/koi8_u.$(OBJEXT): internal/static_assert.h
enc/koi8_u.$(OBJEXT): internal/stdalign.h
enc/koi8_u.$(OBJEXT): internal/stdbool.h
+enc/koi8_u.$(OBJEXT): internal/stdckdint.h
enc/koi8_u.$(OBJEXT): internal/symbol.h
enc/koi8_u.$(OBJEXT): internal/value.h
enc/koi8_u.$(OBJEXT): internal/value_type.h
@@ -5017,6 +5044,7 @@ enc/shift_jis.$(OBJEXT): internal/special_consts.h
enc/shift_jis.$(OBJEXT): internal/static_assert.h
enc/shift_jis.$(OBJEXT): internal/stdalign.h
enc/shift_jis.$(OBJEXT): internal/stdbool.h
+enc/shift_jis.$(OBJEXT): internal/stdckdint.h
enc/shift_jis.$(OBJEXT): internal/symbol.h
enc/shift_jis.$(OBJEXT): internal/value.h
enc/shift_jis.$(OBJEXT): internal/value_type.h
@@ -5176,6 +5204,7 @@ enc/trans/big5.$(OBJEXT): internal/special_consts.h
enc/trans/big5.$(OBJEXT): internal/static_assert.h
enc/trans/big5.$(OBJEXT): internal/stdalign.h
enc/trans/big5.$(OBJEXT): internal/stdbool.h
+enc/trans/big5.$(OBJEXT): internal/stdckdint.h
enc/trans/big5.$(OBJEXT): internal/symbol.h
enc/trans/big5.$(OBJEXT): internal/value.h
enc/trans/big5.$(OBJEXT): internal/value_type.h
@@ -5334,6 +5363,7 @@ enc/trans/cesu_8.$(OBJEXT): internal/special_consts.h
enc/trans/cesu_8.$(OBJEXT): internal/static_assert.h
enc/trans/cesu_8.$(OBJEXT): internal/stdalign.h
enc/trans/cesu_8.$(OBJEXT): internal/stdbool.h
+enc/trans/cesu_8.$(OBJEXT): internal/stdckdint.h
enc/trans/cesu_8.$(OBJEXT): internal/symbol.h
enc/trans/cesu_8.$(OBJEXT): internal/value.h
enc/trans/cesu_8.$(OBJEXT): internal/value_type.h
@@ -5492,6 +5522,7 @@ enc/trans/chinese.$(OBJEXT): internal/special_consts.h
enc/trans/chinese.$(OBJEXT): internal/static_assert.h
enc/trans/chinese.$(OBJEXT): internal/stdalign.h
enc/trans/chinese.$(OBJEXT): internal/stdbool.h
+enc/trans/chinese.$(OBJEXT): internal/stdckdint.h
enc/trans/chinese.$(OBJEXT): internal/symbol.h
enc/trans/chinese.$(OBJEXT): internal/value.h
enc/trans/chinese.$(OBJEXT): internal/value_type.h
@@ -5650,6 +5681,7 @@ enc/trans/ebcdic.$(OBJEXT): internal/special_consts.h
enc/trans/ebcdic.$(OBJEXT): internal/static_assert.h
enc/trans/ebcdic.$(OBJEXT): internal/stdalign.h
enc/trans/ebcdic.$(OBJEXT): internal/stdbool.h
+enc/trans/ebcdic.$(OBJEXT): internal/stdckdint.h
enc/trans/ebcdic.$(OBJEXT): internal/symbol.h
enc/trans/ebcdic.$(OBJEXT): internal/value.h
enc/trans/ebcdic.$(OBJEXT): internal/value_type.h
@@ -5808,6 +5840,7 @@ enc/trans/emoji.$(OBJEXT): internal/special_consts.h
enc/trans/emoji.$(OBJEXT): internal/static_assert.h
enc/trans/emoji.$(OBJEXT): internal/stdalign.h
enc/trans/emoji.$(OBJEXT): internal/stdbool.h
+enc/trans/emoji.$(OBJEXT): internal/stdckdint.h
enc/trans/emoji.$(OBJEXT): internal/symbol.h
enc/trans/emoji.$(OBJEXT): internal/value.h
enc/trans/emoji.$(OBJEXT): internal/value_type.h
@@ -5966,6 +5999,7 @@ enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/special_consts.h
enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/static_assert.h
enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/stdalign.h
enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/stdbool.h
+enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/stdckdint.h
enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/symbol.h
enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/value.h
enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/value_type.h
@@ -6124,6 +6158,7 @@ enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/special_consts.h
enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/static_assert.h
enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/stdalign.h
enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/stdbool.h
+enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/stdckdint.h
enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/symbol.h
enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/value.h
enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/value_type.h
@@ -6282,6 +6317,7 @@ enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/special_consts.h
enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/static_assert.h
enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/stdalign.h
enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/stdbool.h
+enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/stdckdint.h
enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/symbol.h
enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/value.h
enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/value_type.h
@@ -6440,6 +6476,7 @@ enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/special_consts.h
enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/static_assert.h
enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/stdalign.h
enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/stdbool.h
+enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/stdckdint.h
enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/symbol.h
enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/value.h
enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/value_type.h
@@ -6598,6 +6635,7 @@ enc/trans/escape.$(OBJEXT): internal/special_consts.h
enc/trans/escape.$(OBJEXT): internal/static_assert.h
enc/trans/escape.$(OBJEXT): internal/stdalign.h
enc/trans/escape.$(OBJEXT): internal/stdbool.h
+enc/trans/escape.$(OBJEXT): internal/stdckdint.h
enc/trans/escape.$(OBJEXT): internal/symbol.h
enc/trans/escape.$(OBJEXT): internal/value.h
enc/trans/escape.$(OBJEXT): internal/value_type.h
@@ -6756,6 +6794,7 @@ enc/trans/gb18030.$(OBJEXT): internal/special_consts.h
enc/trans/gb18030.$(OBJEXT): internal/static_assert.h
enc/trans/gb18030.$(OBJEXT): internal/stdalign.h
enc/trans/gb18030.$(OBJEXT): internal/stdbool.h
+enc/trans/gb18030.$(OBJEXT): internal/stdckdint.h
enc/trans/gb18030.$(OBJEXT): internal/symbol.h
enc/trans/gb18030.$(OBJEXT): internal/value.h
enc/trans/gb18030.$(OBJEXT): internal/value_type.h
@@ -6914,6 +6953,7 @@ enc/trans/gbk.$(OBJEXT): internal/special_consts.h
enc/trans/gbk.$(OBJEXT): internal/static_assert.h
enc/trans/gbk.$(OBJEXT): internal/stdalign.h
enc/trans/gbk.$(OBJEXT): internal/stdbool.h
+enc/trans/gbk.$(OBJEXT): internal/stdckdint.h
enc/trans/gbk.$(OBJEXT): internal/symbol.h
enc/trans/gbk.$(OBJEXT): internal/value.h
enc/trans/gbk.$(OBJEXT): internal/value_type.h
@@ -7072,6 +7112,7 @@ enc/trans/iso2022.$(OBJEXT): internal/special_consts.h
enc/trans/iso2022.$(OBJEXT): internal/static_assert.h
enc/trans/iso2022.$(OBJEXT): internal/stdalign.h
enc/trans/iso2022.$(OBJEXT): internal/stdbool.h
+enc/trans/iso2022.$(OBJEXT): internal/stdckdint.h
enc/trans/iso2022.$(OBJEXT): internal/symbol.h
enc/trans/iso2022.$(OBJEXT): internal/value.h
enc/trans/iso2022.$(OBJEXT): internal/value_type.h
@@ -7230,6 +7271,7 @@ enc/trans/japanese.$(OBJEXT): internal/special_consts.h
enc/trans/japanese.$(OBJEXT): internal/static_assert.h
enc/trans/japanese.$(OBJEXT): internal/stdalign.h
enc/trans/japanese.$(OBJEXT): internal/stdbool.h
+enc/trans/japanese.$(OBJEXT): internal/stdckdint.h
enc/trans/japanese.$(OBJEXT): internal/symbol.h
enc/trans/japanese.$(OBJEXT): internal/value.h
enc/trans/japanese.$(OBJEXT): internal/value_type.h
@@ -7388,6 +7430,7 @@ enc/trans/japanese_euc.$(OBJEXT): internal/special_consts.h
enc/trans/japanese_euc.$(OBJEXT): internal/static_assert.h
enc/trans/japanese_euc.$(OBJEXT): internal/stdalign.h
enc/trans/japanese_euc.$(OBJEXT): internal/stdbool.h
+enc/trans/japanese_euc.$(OBJEXT): internal/stdckdint.h
enc/trans/japanese_euc.$(OBJEXT): internal/symbol.h
enc/trans/japanese_euc.$(OBJEXT): internal/value.h
enc/trans/japanese_euc.$(OBJEXT): internal/value_type.h
@@ -7546,6 +7589,7 @@ enc/trans/japanese_sjis.$(OBJEXT): internal/special_consts.h
enc/trans/japanese_sjis.$(OBJEXT): internal/static_assert.h
enc/trans/japanese_sjis.$(OBJEXT): internal/stdalign.h
enc/trans/japanese_sjis.$(OBJEXT): internal/stdbool.h
+enc/trans/japanese_sjis.$(OBJEXT): internal/stdckdint.h
enc/trans/japanese_sjis.$(OBJEXT): internal/symbol.h
enc/trans/japanese_sjis.$(OBJEXT): internal/value.h
enc/trans/japanese_sjis.$(OBJEXT): internal/value_type.h
@@ -7704,6 +7748,7 @@ enc/trans/korean.$(OBJEXT): internal/special_consts.h
enc/trans/korean.$(OBJEXT): internal/static_assert.h
enc/trans/korean.$(OBJEXT): internal/stdalign.h
enc/trans/korean.$(OBJEXT): internal/stdbool.h
+enc/trans/korean.$(OBJEXT): internal/stdckdint.h
enc/trans/korean.$(OBJEXT): internal/symbol.h
enc/trans/korean.$(OBJEXT): internal/value.h
enc/trans/korean.$(OBJEXT): internal/value_type.h
@@ -7861,6 +7906,7 @@ enc/trans/newline.$(OBJEXT): internal/special_consts.h
enc/trans/newline.$(OBJEXT): internal/static_assert.h
enc/trans/newline.$(OBJEXT): internal/stdalign.h
enc/trans/newline.$(OBJEXT): internal/stdbool.h
+enc/trans/newline.$(OBJEXT): internal/stdckdint.h
enc/trans/newline.$(OBJEXT): internal/symbol.h
enc/trans/newline.$(OBJEXT): internal/value.h
enc/trans/newline.$(OBJEXT): internal/value_type.h
@@ -8019,6 +8065,7 @@ enc/trans/single_byte.$(OBJEXT): internal/special_consts.h
enc/trans/single_byte.$(OBJEXT): internal/static_assert.h
enc/trans/single_byte.$(OBJEXT): internal/stdalign.h
enc/trans/single_byte.$(OBJEXT): internal/stdbool.h
+enc/trans/single_byte.$(OBJEXT): internal/stdckdint.h
enc/trans/single_byte.$(OBJEXT): internal/symbol.h
enc/trans/single_byte.$(OBJEXT): internal/value.h
enc/trans/single_byte.$(OBJEXT): internal/value_type.h
@@ -8177,6 +8224,7 @@ enc/trans/transdb.$(OBJEXT): internal/special_consts.h
enc/trans/transdb.$(OBJEXT): internal/static_assert.h
enc/trans/transdb.$(OBJEXT): internal/stdalign.h
enc/trans/transdb.$(OBJEXT): internal/stdbool.h
+enc/trans/transdb.$(OBJEXT): internal/stdckdint.h
enc/trans/transdb.$(OBJEXT): internal/symbol.h
enc/trans/transdb.$(OBJEXT): internal/value.h
enc/trans/transdb.$(OBJEXT): internal/value_type.h
@@ -8336,6 +8384,7 @@ enc/trans/utf8_mac.$(OBJEXT): internal/special_consts.h
enc/trans/utf8_mac.$(OBJEXT): internal/static_assert.h
enc/trans/utf8_mac.$(OBJEXT): internal/stdalign.h
enc/trans/utf8_mac.$(OBJEXT): internal/stdbool.h
+enc/trans/utf8_mac.$(OBJEXT): internal/stdckdint.h
enc/trans/utf8_mac.$(OBJEXT): internal/symbol.h
enc/trans/utf8_mac.$(OBJEXT): internal/value.h
enc/trans/utf8_mac.$(OBJEXT): internal/value_type.h
@@ -8494,6 +8543,7 @@ enc/trans/utf_16_32.$(OBJEXT): internal/special_consts.h
enc/trans/utf_16_32.$(OBJEXT): internal/static_assert.h
enc/trans/utf_16_32.$(OBJEXT): internal/stdalign.h
enc/trans/utf_16_32.$(OBJEXT): internal/stdbool.h
+enc/trans/utf_16_32.$(OBJEXT): internal/stdckdint.h
enc/trans/utf_16_32.$(OBJEXT): internal/symbol.h
enc/trans/utf_16_32.$(OBJEXT): internal/value.h
enc/trans/utf_16_32.$(OBJEXT): internal/value_type.h
@@ -8655,6 +8705,7 @@ enc/unicode.$(OBJEXT): internal/special_consts.h
enc/unicode.$(OBJEXT): internal/static_assert.h
enc/unicode.$(OBJEXT): internal/stdalign.h
enc/unicode.$(OBJEXT): internal/stdbool.h
+enc/unicode.$(OBJEXT): internal/stdckdint.h
enc/unicode.$(OBJEXT): internal/symbol.h
enc/unicode.$(OBJEXT): internal/value.h
enc/unicode.$(OBJEXT): internal/value_type.h
@@ -8825,6 +8876,7 @@ enc/us_ascii.$(OBJEXT): internal/special_consts.h
enc/us_ascii.$(OBJEXT): internal/static_assert.h
enc/us_ascii.$(OBJEXT): internal/stdalign.h
enc/us_ascii.$(OBJEXT): internal/stdbool.h
+enc/us_ascii.$(OBJEXT): internal/stdckdint.h
enc/us_ascii.$(OBJEXT): internal/symbol.h
enc/us_ascii.$(OBJEXT): internal/value.h
enc/us_ascii.$(OBJEXT): internal/value_type.h
@@ -8987,6 +9039,7 @@ enc/utf_16be.$(OBJEXT): internal/special_consts.h
enc/utf_16be.$(OBJEXT): internal/static_assert.h
enc/utf_16be.$(OBJEXT): internal/stdalign.h
enc/utf_16be.$(OBJEXT): internal/stdbool.h
+enc/utf_16be.$(OBJEXT): internal/stdckdint.h
enc/utf_16be.$(OBJEXT): internal/symbol.h
enc/utf_16be.$(OBJEXT): internal/value.h
enc/utf_16be.$(OBJEXT): internal/value_type.h
@@ -9148,6 +9201,7 @@ enc/utf_16le.$(OBJEXT): internal/special_consts.h
enc/utf_16le.$(OBJEXT): internal/static_assert.h
enc/utf_16le.$(OBJEXT): internal/stdalign.h
enc/utf_16le.$(OBJEXT): internal/stdbool.h
+enc/utf_16le.$(OBJEXT): internal/stdckdint.h
enc/utf_16le.$(OBJEXT): internal/symbol.h
enc/utf_16le.$(OBJEXT): internal/value.h
enc/utf_16le.$(OBJEXT): internal/value_type.h
@@ -9309,6 +9363,7 @@ enc/utf_32be.$(OBJEXT): internal/special_consts.h
enc/utf_32be.$(OBJEXT): internal/static_assert.h
enc/utf_32be.$(OBJEXT): internal/stdalign.h
enc/utf_32be.$(OBJEXT): internal/stdbool.h
+enc/utf_32be.$(OBJEXT): internal/stdckdint.h
enc/utf_32be.$(OBJEXT): internal/symbol.h
enc/utf_32be.$(OBJEXT): internal/value.h
enc/utf_32be.$(OBJEXT): internal/value_type.h
@@ -9470,6 +9525,7 @@ enc/utf_32le.$(OBJEXT): internal/special_consts.h
enc/utf_32le.$(OBJEXT): internal/static_assert.h
enc/utf_32le.$(OBJEXT): internal/stdalign.h
enc/utf_32le.$(OBJEXT): internal/stdbool.h
+enc/utf_32le.$(OBJEXT): internal/stdckdint.h
enc/utf_32le.$(OBJEXT): internal/symbol.h
enc/utf_32le.$(OBJEXT): internal/value.h
enc/utf_32le.$(OBJEXT): internal/value_type.h
@@ -9640,6 +9696,7 @@ enc/utf_8.$(OBJEXT): internal/special_consts.h
enc/utf_8.$(OBJEXT): internal/static_assert.h
enc/utf_8.$(OBJEXT): internal/stdalign.h
enc/utf_8.$(OBJEXT): internal/stdbool.h
+enc/utf_8.$(OBJEXT): internal/stdckdint.h
enc/utf_8.$(OBJEXT): internal/symbol.h
enc/utf_8.$(OBJEXT): internal/value.h
enc/utf_8.$(OBJEXT): internal/value_type.h
@@ -9802,6 +9859,7 @@ enc/windows_1250.$(OBJEXT): internal/special_consts.h
enc/windows_1250.$(OBJEXT): internal/static_assert.h
enc/windows_1250.$(OBJEXT): internal/stdalign.h
enc/windows_1250.$(OBJEXT): internal/stdbool.h
+enc/windows_1250.$(OBJEXT): internal/stdckdint.h
enc/windows_1250.$(OBJEXT): internal/symbol.h
enc/windows_1250.$(OBJEXT): internal/value.h
enc/windows_1250.$(OBJEXT): internal/value_type.h
@@ -9962,6 +10020,7 @@ enc/windows_1251.$(OBJEXT): internal/special_consts.h
enc/windows_1251.$(OBJEXT): internal/static_assert.h
enc/windows_1251.$(OBJEXT): internal/stdalign.h
enc/windows_1251.$(OBJEXT): internal/stdbool.h
+enc/windows_1251.$(OBJEXT): internal/stdckdint.h
enc/windows_1251.$(OBJEXT): internal/symbol.h
enc/windows_1251.$(OBJEXT): internal/value.h
enc/windows_1251.$(OBJEXT): internal/value_type.h
@@ -10123,6 +10182,7 @@ enc/windows_1252.$(OBJEXT): internal/special_consts.h
enc/windows_1252.$(OBJEXT): internal/static_assert.h
enc/windows_1252.$(OBJEXT): internal/stdalign.h
enc/windows_1252.$(OBJEXT): internal/stdbool.h
+enc/windows_1252.$(OBJEXT): internal/stdckdint.h
enc/windows_1252.$(OBJEXT): internal/symbol.h
enc/windows_1252.$(OBJEXT): internal/value.h
enc/windows_1252.$(OBJEXT): internal/value_type.h
@@ -10283,6 +10343,7 @@ enc/windows_1253.$(OBJEXT): internal/special_consts.h
enc/windows_1253.$(OBJEXT): internal/static_assert.h
enc/windows_1253.$(OBJEXT): internal/stdalign.h
enc/windows_1253.$(OBJEXT): internal/stdbool.h
+enc/windows_1253.$(OBJEXT): internal/stdckdint.h
enc/windows_1253.$(OBJEXT): internal/symbol.h
enc/windows_1253.$(OBJEXT): internal/value.h
enc/windows_1253.$(OBJEXT): internal/value_type.h
@@ -10444,6 +10505,7 @@ enc/windows_1254.$(OBJEXT): internal/special_consts.h
enc/windows_1254.$(OBJEXT): internal/static_assert.h
enc/windows_1254.$(OBJEXT): internal/stdalign.h
enc/windows_1254.$(OBJEXT): internal/stdbool.h
+enc/windows_1254.$(OBJEXT): internal/stdckdint.h
enc/windows_1254.$(OBJEXT): internal/symbol.h
enc/windows_1254.$(OBJEXT): internal/value.h
enc/windows_1254.$(OBJEXT): internal/value_type.h
@@ -10605,6 +10667,7 @@ enc/windows_1257.$(OBJEXT): internal/special_consts.h
enc/windows_1257.$(OBJEXT): internal/static_assert.h
enc/windows_1257.$(OBJEXT): internal/stdalign.h
enc/windows_1257.$(OBJEXT): internal/stdbool.h
+enc/windows_1257.$(OBJEXT): internal/stdckdint.h
enc/windows_1257.$(OBJEXT): internal/symbol.h
enc/windows_1257.$(OBJEXT): internal/value.h
enc/windows_1257.$(OBJEXT): internal/value_type.h
@@ -10768,6 +10831,7 @@ enc/windows_31j.$(OBJEXT): internal/special_consts.h
enc/windows_31j.$(OBJEXT): internal/static_assert.h
enc/windows_31j.$(OBJEXT): internal/stdalign.h
enc/windows_31j.$(OBJEXT): internal/stdbool.h
+enc/windows_31j.$(OBJEXT): internal/stdckdint.h
enc/windows_31j.$(OBJEXT): internal/symbol.h
enc/windows_31j.$(OBJEXT): internal/value.h
enc/windows_31j.$(OBJEXT): internal/value_type.h
diff --git a/enc/ebcdic.h b/enc/ebcdic.h
index a3b380a327..5109bf7065 100644
--- a/enc/ebcdic.h
+++ b/enc/ebcdic.h
@@ -7,5 +7,5 @@ ENC_ALIAS("ebcdic-cp-us", "IBM037");
* hopefully the most widely used one.
*
* See http://www.iana.org/assignments/character-sets/character-sets.xhtml
- * http://tools.ietf.org/html/rfc1345
+ * https://www.rfc-editor.org/rfc/rfc1345
*/
diff --git a/enc/encinit.c.erb b/enc/encinit.c.erb
index 120408f8e3..3662ba200d 100644
--- a/enc/encinit.c.erb
+++ b/enc/encinit.c.erb
@@ -1,3 +1,6 @@
+/* Automatically generated from <%= erb.filename %>
+ * Do not edit<%# directly%>.
+ */
/* Copyright 2012 Google Inc. Some Rights Reserved.
* Author: yugui@google.com (Yugui Sonoda)
*/
diff --git a/enc/make_encmake.rb b/enc/make_encmake.rb
index fcfc2c9267..96d1944bcb 100755
--- a/enc/make_encmake.rb
+++ b/enc/make_encmake.rb
@@ -121,39 +121,32 @@ ENCS, ENC_DEPS = target_encodings
ATRANS, TRANS = target_transcoders
if File.exist?(depend = File.join($srcdir, "depend"))
- if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
- erb = ERB.new(File.read(depend), trim_mode: '%')
- else
- erb = ERB.new(File.read(depend), nil, '%')
- end
+ erb = ERB.new(File.read(depend), trim_mode: '%')
erb.filename = depend
tmp = erb.result(binding)
- dep = "\n#### depend ####\n\n" << depend_rules(tmp).join
+ dep = "\n#### depend ####\n\n" + depend_rules(tmp).join
else
dep = ""
end
mkin = File.read(File.join($srcdir, "Makefile.in"))
-mkin.gsub!(/@(#{CONFIG.keys.join('|')})@/) {CONFIG[$1]}
+# Variables that should not be expanded in Makefile.in to allow
+# overriding inherited variables at make-time.
+not_expand_vars = %w(CFLAGS)
+mkin.gsub!(/@(#{RbConfig::CONFIG.keys.join('|')})@/) do
+ not_expand_vars.include?($1) ? CONFIG[$1] : RbConfig::CONFIG[$1]
+end
File.open(ARGV[0], 'wb') {|f|
f.puts mkin, dep
}
if MODULE_TYPE == :static
filename = "encinit.c.erb"
- if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
- erb = ERB.new(File.read(File.join($srcdir, filename)), trim_mode: '%-')
- else
- erb = ERB.new(File.read(File.join($srcdir, filename)), nil, '%-')
- end
+ erb = ERB.new(File.read(File.join($srcdir, filename)), trim_mode: '%-')
erb.filename = "enc/#{filename}"
tmp = erb.result(binding)
begin
Dir.mkdir 'enc'
rescue Errno::EEXIST
end
- File.open("enc/encinit.c", "w") {|f|
- f.puts "/* Automatically generated from enc/encinit.c.erb"
- f.puts " * Do not edit."
- f.puts " */"
- f.puts tmp
- }
+ require 'tool/lib/output'
+ Output.new(path: "enc/encinit.c", ifchange: true).write(tmp)
end
diff --git a/enc/unicode/15.0.0/name2ctype.h b/enc/unicode/15.0.0/name2ctype.h
index a2c996423d..6bbbb3512f 100644
--- a/enc/unicode/15.0.0/name2ctype.h
+++ b/enc/unicode/15.0.0/name2ctype.h
@@ -5402,7 +5402,7 @@ static const OnigCodePoint CR_ASCII[] = {
0x0000, 0x007f,
}; /* CR_ASCII */
-/* 'Punct' */
+/* 'Punct': [[:Punct:]] */
static const OnigCodePoint CR_Punct[] = {
191,
0x0021, 0x0023,
diff --git a/encoding.c b/encoding.c
index 8bfab73177..480cc8bdc6 100644
--- a/encoding.c
+++ b/encoding.c
@@ -71,6 +71,24 @@ static struct enc_table {
st_table *names;
} global_enc_table;
+static int
+enc_names_free_i(st_data_t name, st_data_t idx, st_data_t args)
+{
+ ruby_xfree((void *)name);
+ return ST_DELETE;
+}
+
+void
+rb_free_global_enc_table(void)
+{
+ for (size_t i = 0; i < ENCODING_LIST_CAPA; i++) {
+ xfree((void *)global_enc_table.list[i].enc);
+ }
+
+ st_foreach(global_enc_table.names, enc_names_free_i, (st_data_t)0);
+ st_free_table(global_enc_table.names);
+}
+
static rb_encoding *global_enc_ascii,
*global_enc_utf_8,
*global_enc_us_ascii;
@@ -997,13 +1015,22 @@ rb_enc_get(VALUE obj)
return rb_enc_from_index(rb_enc_get_index(obj));
}
+const char *
+rb_enc_inspect_name(rb_encoding *enc)
+{
+ if (enc == global_enc_ascii) {
+ return "BINARY (ASCII-8BIT)";
+ }
+ return enc->name;
+}
+
static rb_encoding*
rb_encoding_check(rb_encoding* enc, VALUE str1, VALUE str2)
{
if (!enc)
rb_raise(rb_eEncCompatError, "incompatible character encodings: %s and %s",
- rb_enc_name(rb_enc_get(str1)),
- rb_enc_name(rb_enc_get(str2)));
+ rb_enc_inspect_name(rb_enc_get(str1)),
+ rb_enc_inspect_name(rb_enc_get(str2)));
return enc;
}
@@ -1245,9 +1272,10 @@ enc_inspect(VALUE self)
if (!(enc = DATA_PTR(self)) || rb_enc_from_index(rb_enc_to_index(enc)) != enc) {
rb_raise(rb_eTypeError, "broken Encoding");
}
+
return rb_enc_sprintf(rb_usascii_encoding(),
"#<%"PRIsVALUE":%s%s%s>", rb_obj_class(self),
- rb_enc_name(enc),
+ rb_enc_inspect_name(enc),
(ENC_DUMMY_P(enc) ? " (dummy)" : ""),
rb_enc_autoload_p(enc) ? " (autoload)" : "");
}
@@ -1525,7 +1553,14 @@ enc_set_default_encoding(struct default_encoding *def, VALUE encoding, const cha
if (NIL_P(encoding)) {
def->index = -1;
def->enc = 0;
- st_insert(enc_table->names, (st_data_t)strdup(name),
+ char *name_dup = strdup(name);
+
+ st_data_t existing_name = (st_data_t)name_dup;
+ if (st_delete(enc_table->names, &existing_name, NULL)) {
+ xfree((void *)existing_name);
+ }
+
+ st_insert(enc_table->names, (st_data_t)name_dup,
(st_data_t)UNSPECIFIED_ENCODING);
}
else {
@@ -1902,7 +1937,7 @@ Init_Encoding(void)
list = rb_encoding_list = rb_ary_new2(ENCODING_LIST_CAPA);
RBASIC_CLEAR_CLASS(list);
- rb_gc_register_mark_object(list);
+ rb_vm_register_global_object(list);
for (i = 0; i < enc_table->count; ++i) {
rb_ary_push(list, enc_new(enc_table->list[i].enc));
diff --git a/enum.c b/enum.c
index 6fc2fc9b17..54ccf61c78 100644
--- a/enum.c
+++ b/enum.c
@@ -354,7 +354,7 @@ find_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, memop))
* {foo: 0, bar: 1, baz: 2}.find {|key, value| key.start_with?('b') } # => [:bar, 1]
* {foo: 0, bar: 1, baz: 2}.find(proc {[]}) {|key, value| key.start_with?('c') } # => []
*
- * With no block given, returns an \Enumerator.
+ * With no block given, returns an Enumerator.
*
*/
static VALUE
@@ -424,7 +424,7 @@ find_index_iter_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, memop))
* ['a', 'b', 'c', 'b'].find_index {|element| element.start_with?('b') } # => 1
* {foo: 0, bar: 1, baz: 2}.find_index {|key, value| value > 1 } # => 2
*
- * With no argument and no block given, returns an \Enumerator.
+ * With no argument and no block given, returns an Enumerator.
*
*/
@@ -501,7 +501,7 @@ enum_size_over_p(VALUE obj, long n)
* a = {foo: 0, bar: 1, baz: 2}.select {|key, value| key.start_with?('b') }
* a # => {:bar=>1, :baz=>2}
*
- * With no block given, returns an \Enumerator.
+ * With no block given, returns an Enumerator.
*
* Related: #reject.
*/
@@ -543,7 +543,7 @@ filter_map_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
* (0..9).filter_map {|i| i * 2 if i.even? } # => [0, 4, 8, 12, 16]
* {foo: 0, bar: 1, baz: 2}.filter_map {|key, value| key if value.even? } # => [:foo, :baz]
*
- * When no block given, returns an \Enumerator.
+ * When no block given, returns an Enumerator.
*
*/
static VALUE
@@ -584,7 +584,7 @@ reject_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
* (0..9).reject {|i| i * 2 if i.even? } # => [1, 3, 5, 7, 9]
* {foo: 0, bar: 1, baz: 2}.reject {|key, value| key if value.odd? } # => {:foo=>0, :baz=>2}
*
- * When no block given, returns an \Enumerator.
+ * When no block given, returns an Enumerator.
*
* Related: #select.
*/
@@ -631,7 +631,7 @@ collect_all(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
* (0..4).map {|i| i*i } # => [0, 1, 4, 9, 16]
* {foo: 0, bar: 1, baz: 2}.map {|key, value| value*2} # => [0, 2, 4]
*
- * With no block given, returns an \Enumerator.
+ * With no block given, returns an Enumerator.
*
*/
static VALUE
@@ -681,7 +681,7 @@ flat_map_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
* [[0, 1], [2, 3]].flat_map {|e| e + [100] } # => [0, 1, 100, 2, 3, 100]
* {foo: 0, bar: 1, baz: 2}.flat_map {|key, value| [key, value] } # => [:foo, 0, :bar, 1, :baz, 2]
*
- * With no block given, returns an \Enumerator.
+ * With no block given, returns an Enumerator.
*
* Alias: #collect_concat.
*/
@@ -4538,7 +4538,7 @@ struct enum_sum_memo {
static void
sum_iter_normalize_memo(struct enum_sum_memo *memo)
{
- assert(FIXABLE(memo->n));
+ RUBY_ASSERT(FIXABLE(memo->n));
memo->v = rb_fix_plus(LONG2FIX(memo->n), memo->v);
memo->n = 0;
@@ -4640,7 +4640,7 @@ sum_iter_Kahan_Babuska(VALUE i, struct enum_sum_memo *memo)
static void
sum_iter(VALUE i, struct enum_sum_memo *memo)
{
- assert(memo != NULL);
+ RUBY_ASSERT(memo != NULL);
if (memo->block_given) {
i = rb_yield(i);
}
@@ -4691,8 +4691,8 @@ hash_sum_i(VALUE key, VALUE value, VALUE arg)
static void
hash_sum(VALUE hash, struct enum_sum_memo *memo)
{
- assert(RB_TYPE_P(hash, T_HASH));
- assert(memo != NULL);
+ RUBY_ASSERT(RB_TYPE_P(hash, T_HASH));
+ RUBY_ASSERT(memo != NULL);
rb_hash_foreach(hash, hash_sum_i, (VALUE)memo);
}
@@ -4824,13 +4824,13 @@ uniq_iter(RB_BLOCK_CALL_FUNC_ARGLIST(i, hash))
* %w[a b c c b a a b c].uniq # => ["a", "b", "c"]
* [0, 1, 2, 2, 1, 0, 0, 1, 2].uniq # => [0, 1, 2]
*
- * With a block, returns a new array containing only for which the block
+ * With a block, returns a new array containing elements only for which the block
* returns a unique value:
*
* a = [0, 1, 2, 3, 4, 5, 5, 4, 3, 2, 1]
* a.uniq {|i| i.even? ? i : 0 } # => [0, 2, 4]
* a = %w[a b c d e e d c b a a b c d e]
- a.uniq {|c| c < 'c' } # => ["a", "c"]
+ * a.uniq {|c| c < 'c' } # => ["a", "c"]
*
*/
@@ -4905,7 +4905,7 @@ enum_compact(VALUE obj)
* - #one?: Returns +true+ if exactly one element meets a specified criterion; +false+ otherwise.
* - #count: Returns the count of elements,
* based on an argument or block criterion, if given.
- * - #tally: Returns a new \Hash containing the counts of occurrences of each element.
+ * - #tally: Returns a new Hash containing the counts of occurrences of each element.
*
* === Methods for Fetching
*
@@ -4926,21 +4926,21 @@ enum_compact(VALUE obj)
* as determined by <tt><=></tt> or a given block.
* - #max: Returns the elements whose values are largest among the elements,
* as determined by <tt><=></tt> or a given block.
- * - #minmax: Returns a 2-element \Array containing the smallest and largest elements.
+ * - #minmax: Returns a 2-element Array containing the smallest and largest elements.
* - #min_by: Returns the smallest element, as determined by the given block.
* - #max_by: Returns the largest element, as determined by the given block.
* - #minmax_by: Returns the smallest and largest elements, as determined by the given block.
*
* <i>Groups, slices, and partitions</i>:
*
- * - #group_by: Returns a \Hash that partitions the elements into groups.
+ * - #group_by: Returns a Hash that partitions the elements into groups.
* - #partition: Returns elements partitioned into two new Arrays, as determined by the given block.
- * - #slice_after: Returns a new \Enumerator whose entries are a partition of +self+,
- based either on a given +object+ or a given block.
- * - #slice_before: Returns a new \Enumerator whose entries are a partition of +self+,
- based either on a given +object+ or a given block.
- * - #slice_when: Returns a new \Enumerator whose entries are a partition of +self+
- based on the given block.
+ * - #slice_after: Returns a new Enumerator whose entries are a partition of +self+,
+ * based either on a given +object+ or a given block.
+ * - #slice_before: Returns a new Enumerator whose entries are a partition of +self+,
+ * based either on a given +object+ or a given block.
+ * - #slice_when: Returns a new Enumerator whose entries are a partition of +self+
+ * based on the given block.
* - #chunk: Returns elements organized into chunks as specified by the given block.
* - #chunk_while: Returns elements organized into chunks as specified by the given block.
*
@@ -5040,18 +5040,18 @@ enum_compact(VALUE obj)
*
* Virtually all methods in \Enumerable call method +#each+ in the including class:
*
- * - <tt>Hash#each</tt> yields the next key-value pair as a 2-element \Array.
- * - <tt>Struct#each</tt> yields the next name-value pair as a 2-element \Array.
+ * - <tt>Hash#each</tt> yields the next key-value pair as a 2-element Array.
+ * - <tt>Struct#each</tt> yields the next name-value pair as a 2-element Array.
* - For the other classes above, +#each+ yields the next object from the collection.
*
* == About the Examples
*
* The example code snippets for the \Enumerable methods:
*
- * - Always show the use of one or more \Array-like classes (often \Array itself).
- * - Sometimes show the use of a \Hash-like class.
+ * - Always show the use of one or more Array-like classes (often Array itself).
+ * - Sometimes show the use of a Hash-like class.
* For some methods, though, the usage would not make sense,
- * and so it is not shown. Example: #tally would find exactly one of each \Hash entry.
+ * and so it is not shown. Example: #tally would find exactly one of each Hash entry.
*
*/
diff --git a/enumerator.c b/enumerator.c
index b33c171371..193a865dbc 100644
--- a/enumerator.c
+++ b/enumerator.c
@@ -85,12 +85,16 @@
* puts e.next # => 3
* puts e.next # raises StopIteration
*
- * +next+, +next_values+, +peek+ and +peek_values+ are the only methods
- * which use external iteration (and Array#zip(Enumerable-not-Array) which uses +next+).
+ * +next+, +next_values+, +peek+, and +peek_values+ are the only methods
+ * which use external iteration (and Array#zip(Enumerable-not-Array) which uses +next+ internally).
*
* These methods do not affect other internal enumeration methods,
* unless the underlying iteration method itself has side-effect, e.g. IO#each_line.
*
+ * FrozenError will be raised if these methods are called against a frozen enumerator.
+ * Since +rewind+ and +feed+ also change state for external iteration,
+ * these methods may raise FrozenError too.
+ *
* External iteration differs *significantly* from internal iteration
* due to using a Fiber:
* - The Fiber adds some overhead compared to internal enumeration.
@@ -165,7 +169,10 @@ static VALUE sym_each, sym_cycle, sym_yield;
static VALUE lazy_use_super_method;
+extern ID ruby_static_id_cause;
+
#define id_call idCall
+#define id_cause ruby_static_id_cause
#define id_each idEach
#define id_eqq idEqq
#define id_initialize idInitialize
@@ -188,17 +195,18 @@ struct enumerator {
int kw_splat;
};
-RUBY_REFERENCES_START(enumerator_refs)
- REF_EDGE(enumerator, obj),
- REF_EDGE(enumerator, args),
- REF_EDGE(enumerator, fib),
- REF_EDGE(enumerator, dst),
- REF_EDGE(enumerator, lookahead),
- REF_EDGE(enumerator, feedvalue),
- REF_EDGE(enumerator, stop_exc),
- REF_EDGE(enumerator, size),
- REF_EDGE(enumerator, procs),
-RUBY_REFERENCES_END
+RUBY_REFERENCES(enumerator_refs) = {
+ RUBY_REF_EDGE(struct enumerator, obj),
+ RUBY_REF_EDGE(struct enumerator, args),
+ RUBY_REF_EDGE(struct enumerator, fib),
+ RUBY_REF_EDGE(struct enumerator, dst),
+ RUBY_REF_EDGE(struct enumerator, lookahead),
+ RUBY_REF_EDGE(struct enumerator, feedvalue),
+ RUBY_REF_EDGE(struct enumerator, stop_exc),
+ RUBY_REF_EDGE(struct enumerator, size),
+ RUBY_REF_EDGE(struct enumerator, procs),
+ RUBY_REF_END
+};
static VALUE rb_cGenerator, rb_cYielder, rb_cEnumProducer;
@@ -249,23 +257,15 @@ struct enum_product {
VALUE rb_cArithSeq;
-#define enumerator_free RUBY_TYPED_DEFAULT_FREE
-
-static size_t
-enumerator_memsize(const void *p)
-{
- return sizeof(struct enumerator);
-}
-
static const rb_data_type_t enumerator_data_type = {
"enumerator",
{
- REFS_LIST_PTR(enumerator_refs),
- enumerator_free,
- enumerator_memsize,
+ RUBY_REFS_LIST_PTR(enumerator_refs),
+ RUBY_TYPED_DEFAULT_FREE,
+ NULL, // Nothing allocated externally, so don't need a memsize function
NULL,
},
- 0, NULL, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_DECL_MARKING
+ 0, NULL, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_DECL_MARKING | RUBY_TYPED_EMBEDDABLE
};
static struct enumerator *
@@ -296,22 +296,15 @@ proc_entry_compact(void *p)
ptr->memo = rb_gc_location(ptr->memo);
}
-#define proc_entry_free RUBY_TYPED_DEFAULT_FREE
-
-static size_t
-proc_entry_memsize(const void *p)
-{
- return p ? sizeof(struct proc_entry) : 0;
-}
-
static const rb_data_type_t proc_entry_data_type = {
"proc_entry",
{
proc_entry_mark,
- proc_entry_free,
- proc_entry_memsize,
+ RUBY_TYPED_DEFAULT_FREE,
+ NULL, // Nothing allocated externally, so don't need a memsize function
proc_entry_compact,
},
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
};
static struct proc_entry *
@@ -396,7 +389,7 @@ obj_to_enum(int argc, VALUE *argv, VALUE obj)
}
enumerator = rb_enumeratorize_with_size(obj, meth, argc, argv, 0);
if (rb_block_given_p()) {
- enumerator_ptr(enumerator)->size = rb_block_proc();
+ RB_OBJ_WRITE(enumerator, &enumerator_ptr(enumerator)->size, rb_block_proc());
}
return enumerator;
}
@@ -425,15 +418,15 @@ enumerator_init(VALUE enum_obj, VALUE obj, VALUE meth, int argc, const VALUE *ar
rb_raise(rb_eArgError, "unallocated enumerator");
}
- ptr->obj = obj;
+ RB_OBJ_WRITE(enum_obj, &ptr->obj, obj);
ptr->meth = rb_to_id(meth);
- if (argc) ptr->args = rb_ary_new4(argc, argv);
+ if (argc) RB_OBJ_WRITE(enum_obj, &ptr->args, rb_ary_new4(argc, argv));
ptr->fib = 0;
ptr->dst = Qnil;
ptr->lookahead = Qundef;
ptr->feedvalue = Qundef;
ptr->stop_exc = Qfalse;
- ptr->size = size;
+ RB_OBJ_WRITE(enum_obj, &ptr->size, size);
ptr->size_fn = size_fn;
ptr->kw_splat = kw_splat;
@@ -512,13 +505,13 @@ enumerator_init_copy(VALUE obj, VALUE orig)
rb_raise(rb_eArgError, "unallocated enumerator");
}
- ptr1->obj = ptr0->obj;
- ptr1->meth = ptr0->meth;
- ptr1->args = ptr0->args;
+ RB_OBJ_WRITE(obj, &ptr1->obj, ptr0->obj);
+ RB_OBJ_WRITE(obj, &ptr1->meth, ptr0->meth);
+ RB_OBJ_WRITE(obj, &ptr1->args, ptr0->args);
ptr1->fib = 0;
ptr1->lookahead = Qundef;
ptr1->feedvalue = Qundef;
- ptr1->size = ptr0->size;
+ RB_OBJ_WRITE(obj, &ptr1->size, ptr0->size);
ptr1->size_fn = ptr0->size_fn;
return obj;
@@ -566,11 +559,17 @@ enumerator_block_call(VALUE obj, rb_block_call_func *func, VALUE arg)
const struct enumerator *e = enumerator_ptr(obj);
ID meth = e->meth;
- if (e->args) {
- argc = RARRAY_LENINT(e->args);
- argv = RARRAY_CONST_PTR(e->args);
+ VALUE args = e->args;
+ if (args) {
+ argc = RARRAY_LENINT(args);
+ argv = RARRAY_CONST_PTR(args);
}
- return rb_block_call_kw(e->obj, meth, argc, argv, func, arg, e->kw_splat);
+
+ VALUE ret = rb_block_call_kw(e->obj, meth, argc, argv, func, arg, e->kw_splat);
+
+ RB_GC_GUARD(args);
+
+ return ret;
}
/*
@@ -627,7 +626,7 @@ enumerator_each(int argc, VALUE *argv, VALUE obj)
else {
args = rb_ary_new4(argc, argv);
}
- e->args = args;
+ RB_OBJ_WRITE(obj, &e->args, args);
e->size = Qnil;
e->size_fn = 0;
}
@@ -768,7 +767,7 @@ next_i(RB_BLOCK_CALL_FUNC_ARGLIST(_, obj))
VALUE result;
result = rb_block_call(obj, id_each, 0, 0, next_ii, obj);
- e->stop_exc = rb_exc_new2(rb_eStopIteration, "iteration reached an end");
+ RB_OBJ_WRITE(obj, &e->stop_exc, rb_exc_new2(rb_eStopIteration, "iteration reached an end"));
rb_ivar_set(e->stop_exc, id_result, result);
return rb_fiber_yield(1, &nil);
}
@@ -777,8 +776,8 @@ static void
next_init(VALUE obj, struct enumerator *e)
{
VALUE curr = rb_fiber_current();
- e->dst = curr;
- e->fib = rb_fiber_new(next_i, obj);
+ RB_OBJ_WRITE(obj, &e->dst, curr);
+ RB_OBJ_WRITE(obj, &e->fib, rb_fiber_new(next_i, obj));
e->lookahead = Qundef;
}
@@ -787,8 +786,16 @@ get_next_values(VALUE obj, struct enumerator *e)
{
VALUE curr, vs;
- if (e->stop_exc)
- rb_exc_raise(e->stop_exc);
+ if (e->stop_exc) {
+ VALUE exc = e->stop_exc;
+ VALUE result = rb_attr_get(exc, id_result);
+ VALUE mesg = rb_attr_get(exc, idMesg);
+ if (!NIL_P(mesg)) mesg = rb_str_dup(mesg);
+ VALUE stop_exc = rb_exc_new_str(rb_eStopIteration, mesg);
+ rb_ivar_set(stop_exc, id_cause, exc);
+ rb_ivar_set(stop_exc, id_result, result);
+ rb_exc_raise(stop_exc);
+ }
curr = rb_fiber_current();
@@ -858,6 +865,8 @@ enumerator_next_values(VALUE obj)
struct enumerator *e = enumerator_ptr(obj);
VALUE vs;
+ rb_check_frozen(obj);
+
if (!UNDEF_P(e->lookahead)) {
vs = e->lookahead;
e->lookahead = Qundef;
@@ -919,9 +928,12 @@ enumerator_peek_values(VALUE obj)
{
struct enumerator *e = enumerator_ptr(obj);
+ rb_check_frozen(obj);
+
if (UNDEF_P(e->lookahead)) {
- e->lookahead = get_next_values(obj, e);
+ RB_OBJ_WRITE(obj, &e->lookahead, get_next_values(obj, e));
}
+
return e->lookahead;
}
@@ -1043,10 +1055,12 @@ enumerator_feed(VALUE obj, VALUE v)
{
struct enumerator *e = enumerator_ptr(obj);
+ rb_check_frozen(obj);
+
if (!UNDEF_P(e->feedvalue)) {
rb_raise(rb_eTypeError, "feed value already set");
}
- e->feedvalue = v;
+ RB_OBJ_WRITE(obj, &e->feedvalue, v);
return Qnil;
}
@@ -1065,6 +1079,8 @@ enumerator_rewind(VALUE obj)
{
struct enumerator *e = enumerator_ptr(obj);
+ rb_check_frozen(obj);
+
rb_check_funcall(e->obj, id_rewind, 0, 0);
e->fib = 0;
@@ -1278,23 +1294,15 @@ yielder_compact(void *p)
ptr->proc = rb_gc_location(ptr->proc);
}
-#define yielder_free RUBY_TYPED_DEFAULT_FREE
-
-static size_t
-yielder_memsize(const void *p)
-{
- return sizeof(struct yielder);
-}
-
static const rb_data_type_t yielder_data_type = {
"yielder",
{
yielder_mark,
- yielder_free,
- yielder_memsize,
+ RUBY_TYPED_DEFAULT_FREE,
+ NULL,
yielder_compact,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
};
static struct yielder *
@@ -1333,7 +1341,7 @@ yielder_init(VALUE obj, VALUE proc)
rb_raise(rb_eArgError, "unallocated yielder");
}
- ptr->proc = proc;
+ RB_OBJ_WRITE(obj, &ptr->proc, proc);
return obj;
}
@@ -1418,23 +1426,15 @@ generator_compact(void *p)
ptr->obj = rb_gc_location(ptr->obj);
}
-#define generator_free RUBY_TYPED_DEFAULT_FREE
-
-static size_t
-generator_memsize(const void *p)
-{
- return sizeof(struct generator);
-}
-
static const rb_data_type_t generator_data_type = {
"generator",
{
generator_mark,
- generator_free,
- generator_memsize,
+ RUBY_TYPED_DEFAULT_FREE,
+ NULL,
generator_compact,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
};
static struct generator *
@@ -1474,7 +1474,7 @@ generator_init(VALUE obj, VALUE proc)
rb_raise(rb_eArgError, "unallocated generator");
}
- ptr->proc = proc;
+ RB_OBJ_WRITE(obj, &ptr->proc, proc);
return obj;
}
@@ -1522,7 +1522,7 @@ generator_init_copy(VALUE obj, VALUE orig)
rb_raise(rb_eArgError, "unallocated generator");
}
- ptr1->proc = ptr0->proc;
+ RB_OBJ_WRITE(obj, &ptr1->proc, ptr0->proc);
return obj;
}
@@ -1689,7 +1689,7 @@ lazy_generator_init(VALUE enumerator, VALUE procs)
lazy_init_block, rb_ary_new3(2, obj, procs));
gen_ptr = generator_ptr(generator);
- gen_ptr->obj = obj;
+ RB_OBJ_WRITE(generator, &gen_ptr->obj, obj);
return generator;
}
@@ -1874,10 +1874,10 @@ lazy_add_method(VALUE obj, int argc, VALUE *argv, VALUE args, VALUE memo,
VALUE entry_obj = TypedData_Make_Struct(rb_cObject, struct proc_entry,
&proc_entry_data_type, entry);
if (rb_block_given_p()) {
- entry->proc = rb_block_proc();
+ RB_OBJ_WRITE(entry_obj, &entry->proc, rb_block_proc());
}
entry->fn = fn;
- entry->memo = args;
+ RB_OBJ_WRITE(entry_obj, &entry->memo, args);
lazy_set_args(entry_obj, memo);
@@ -1886,9 +1886,9 @@ lazy_add_method(VALUE obj, int argc, VALUE *argv, VALUE args, VALUE memo,
rb_ary_push(new_procs, entry_obj);
new_obj = enumerator_init_copy(enumerator_allocate(rb_cLazy), obj);
- new_e = DATA_PTR(new_obj);
- new_e->obj = new_generator;
- new_e->procs = new_procs;
+ new_e = RTYPEDDATA_GET_DATA(new_obj);
+ RB_OBJ_WRITE(new_obj, &new_e->obj, new_generator);
+ RB_OBJ_WRITE(new_obj, &new_e->procs, new_procs);
if (argc > 0) {
new_e->meth = rb_to_id(*argv++);
@@ -1897,7 +1897,9 @@ lazy_add_method(VALUE obj, int argc, VALUE *argv, VALUE args, VALUE memo,
else {
new_e->meth = id_each;
}
- new_e->args = rb_ary_new4(argc, argv);
+
+ RB_OBJ_WRITE(new_obj, &new_e->args, rb_ary_new4(argc, argv));
+
return new_obj;
}
@@ -1983,7 +1985,7 @@ lazy_to_enum(int argc, VALUE *argv, VALUE self)
}
lazy = lazy_to_enum_i(self, meth, argc, argv, 0, rb_keyword_given_p());
if (rb_block_given_p()) {
- enumerator_ptr(lazy)->size = rb_block_proc();
+ RB_OBJ_WRITE(lazy, &enumerator_ptr(lazy)->size, rb_block_proc());
}
return lazy;
}
@@ -2369,7 +2371,6 @@ lazy_zip_arrays_func(VALUE proc_entry, struct MEMO *result, VALUE memos, long me
rb_ary_push(ary, rb_ary_entry(RARRAY_AREF(arrays, i), count));
}
LAZY_MEMO_SET_VALUE(result, ary);
- LAZY_MEMO_SET_PACKED(result);
rb_ary_store(memos, memo_index, LONG2NUM(++count));
return result;
}
@@ -2944,7 +2945,7 @@ static const rb_data_type_t producer_data_type = {
producer_memsize,
producer_compact,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
};
static struct producer *
@@ -2984,8 +2985,8 @@ producer_init(VALUE obj, VALUE init, VALUE proc)
rb_raise(rb_eArgError, "unallocated producer");
}
- ptr->init = init;
- ptr->proc = proc;
+ RB_OBJ_WRITE(obj, &ptr->init, init);
+ RB_OBJ_WRITE(obj, &ptr->proc, proc);
return obj;
}
@@ -3535,10 +3536,19 @@ static VALUE
enum_product_total_size(VALUE enums)
{
VALUE total = INT2FIX(1);
+ VALUE sizes = rb_ary_hidden_new(RARRAY_LEN(enums));
long i;
for (i = 0; i < RARRAY_LEN(enums); i++) {
VALUE size = enum_size(RARRAY_AREF(enums, i));
+ if (size == INT2FIX(0)) {
+ rb_ary_resize(sizes, 0);
+ return size;
+ }
+ rb_ary_push(sizes, size);
+ }
+ for (i = 0; i < RARRAY_LEN(sizes); i++) {
+ VALUE size = RARRAY_AREF(sizes, i);
if (NIL_P(size) || (RB_TYPE_P(size, T_FLOAT) && isinf(NUM2DBL(size)))) {
return size;
@@ -4561,7 +4571,7 @@ InitVM_Enumerator(void)
rb_hash_aset(lazy_use_super_method, sym("uniq"), sym("_enumerable_uniq"));
rb_hash_aset(lazy_use_super_method, sym("with_index"), sym("_enumerable_with_index"));
rb_obj_freeze(lazy_use_super_method);
- rb_gc_register_mark_object(lazy_use_super_method);
+ rb_vm_register_global_object(lazy_use_super_method);
#if 0 /* for RDoc */
rb_define_method(rb_cLazy, "to_a", lazy_to_a, 0);
diff --git a/error.c b/error.c
index cb954dbf9e..dc032df651 100644
--- a/error.c
+++ b/error.c
@@ -23,25 +23,33 @@
# include <unistd.h>
#endif
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
#if defined __APPLE__
# include <AvailabilityMacros.h>
#endif
#include "internal.h"
+#include "internal/class.h"
#include "internal/error.h"
#include "internal/eval.h"
#include "internal/hash.h"
#include "internal/io.h"
#include "internal/load.h"
#include "internal/object.h"
+#include "internal/process.h"
#include "internal/string.h"
#include "internal/symbol.h"
#include "internal/thread.h"
#include "internal/variable.h"
#include "ruby/encoding.h"
#include "ruby/st.h"
+#include "ruby/util.h"
#include "ruby_assert.h"
#include "vm_core.h"
+#include "yjit.h"
#include "builtin.h"
@@ -201,14 +209,19 @@ rb_warning_category_enabled_p(rb_warning_category_t category)
* Returns the flag to show the warning messages for +category+.
* Supported categories are:
*
- * +:deprecated+ :: deprecation warnings
- * * assignment of non-nil value to <code>$,</code> and <code>$;</code>
- * * keyword arguments
- * * proc/lambda without block
- * etc.
+ * +:deprecated+ ::
+ * deprecation warnings
+ * * assignment of non-nil value to <code>$,</code> and <code>$;</code>
+ * * keyword arguments
+ * etc.
+ *
+ * +:experimental+ ::
+ * experimental features
+ * * Pattern matching
*
- * +:experimental+ :: experimental features
- * * Pattern matching
+ * +:performance+ ::
+ * performance hints
+ * * Shape variation limit
*/
static VALUE
@@ -241,6 +254,26 @@ rb_warning_s_aset(VALUE mod, VALUE category, VALUE flag)
/*
* call-seq:
+ * categories -> array
+ *
+ * Returns a list of the supported category symbols.
+ */
+
+static VALUE
+rb_warning_s_categories(VALUE mod)
+{
+ st_index_t num = warning_categories.id2enum->num_entries;
+ ID *ids = ALLOCA_N(ID, num);
+ num = st_keys(warning_categories.id2enum, ids, num);
+ VALUE ary = rb_ary_new_capa(num);
+ for (st_index_t i = 0; i < num; ++i) {
+ rb_ary_push(ary, ID2SYM(ids[i]));
+ }
+ return rb_ary_freeze(ary);
+}
+
+/*
+ * call-seq:
* warn(msg, category: nil) -> nil
*
* Writes warning message +msg+ to $stderr. This method is called by
@@ -280,11 +313,11 @@ rb_warning_s_warn(int argc, VALUE *argv, VALUE mod)
*
* Changing the behavior of Warning.warn is useful to customize how warnings are
* handled by Ruby, for instance by filtering some warnings, and/or outputting
- * warnings somewhere other than $stderr.
+ * warnings somewhere other than <tt>$stderr</tt>.
*
* If you want to change the behavior of Warning.warn you should use
- * +Warning.extend(MyNewModuleWithWarnMethod)+ and you can use `super`
- * to get the default behavior of printing the warning to $stderr.
+ * <tt>Warning.extend(MyNewModuleWithWarnMethod)</tt> and you can use +super+
+ * to get the default behavior of printing the warning to <tt>$stderr</tt>.
*
* Example:
* module MyWarningFilter
@@ -301,7 +334,7 @@ rb_warning_s_warn(int argc, VALUE *argv, VALUE mod)
* You should never redefine Warning#warn (the instance method), as that will
* then no longer provide a way to use the default behavior.
*
- * The +warning+ gem provides convenient ways to customize Warning.warn.
+ * The warning[https://rubygems.org/gems/warning] gem provides convenient ways to customize Warning.warn.
*/
static VALUE
@@ -617,18 +650,239 @@ rb_bug_reporter_add(void (*func)(FILE *, void *), void *data)
return 1;
}
+/* returns true if x can not be used as file name */
+static bool
+path_sep_p(char x)
+{
+#if defined __CYGWIN__ || defined DOSISH
+# define PATH_SEP_ENCODING 1
+ // Assume that "/" is only the first byte in any encoding.
+ if (x == ':') return true; // drive letter or ADS
+ if (x == '\\') return true;
+#endif
+ return x == '/';
+}
+
+struct path_string {
+ const char *ptr;
+ size_t len;
+};
+
+static const char PATHSEP_REPLACE = '!';
+
+static char *
+append_pathname(char *p, const char *pe, VALUE str)
+{
+#ifdef PATH_SEP_ENCODING
+ rb_encoding *enc = rb_enc_get(str);
+#endif
+ const char *s = RSTRING_PTR(str);
+ const char *const se = s + RSTRING_LEN(str);
+ char c;
+
+ --pe; // for terminator
+
+ while (p < pe && s < se && (c = *s) != '\0') {
+ if (c == '.') {
+ if (s == se || !*s) break; // chomp "." basename
+ if (path_sep_p(s[1])) goto skipsep; // skip "./"
+ }
+ else if (path_sep_p(c)) {
+ // squeeze successive separators
+ *p++ = PATHSEP_REPLACE;
+ skipsep:
+ while (++s < se && path_sep_p(*s));
+ continue;
+ }
+ const char *const ss = s;
+ while (p < pe && s < se && *s && !path_sep_p(*s)) {
+#ifdef PATH_SEP_ENCODING
+ int n = rb_enc_mbclen(s, se, enc);
+#else
+ const int n = 1;
+#endif
+ p += n;
+ s += n;
+ }
+ if (s > ss) memcpy(p - (s - ss), ss, s - ss);
+ }
+
+ return p;
+}
+
+static char *
+append_basename(char *p, const char *pe, struct path_string *path, VALUE str)
+{
+ if (!path->ptr) {
+#ifdef PATH_SEP_ENCODING
+ rb_encoding *enc = rb_enc_get(str);
+#endif
+ const char *const b = RSTRING_PTR(str), *const e = RSTRING_END(str), *p = e;
+
+ while (p > b) {
+ if (path_sep_p(p[-1])) {
+#ifdef PATH_SEP_ENCODING
+ const char *t = rb_enc_prev_char(b, p, e, enc);
+ if (t == p-1) break;
+ p = t;
+#else
+ break;
+#endif
+ }
+ else {
+ --p;
+ }
+ }
+
+ path->ptr = p;
+ path->len = e - p;
+ }
+ size_t n = path->len;
+ if (p + n > pe) n = pe - p;
+ memcpy(p, path->ptr, n);
+ return p + n;
+}
+
+static void
+finish_report(FILE *out, rb_pid_t pid)
+{
+ if (out != stdout && out != stderr) fclose(out);
+#ifdef HAVE_WORKING_FORK
+ if (pid > 0) waitpid(pid, NULL, 0);
+#endif
+}
+
+struct report_expansion {
+ struct path_string exe, script;
+ rb_pid_t pid;
+ time_t time;
+};
+
+/*
+ * Open a bug report file to write. The `RUBY_CRASH_REPORT`
+ * environment variable can be set to define a template that is used
+ * to name bug report files. The template can contain % specifiers
+ * which are substituted by the following values when a bug report
+ * file is created:
+ *
+ * %% A single % character.
+ * %e The base name of the executable filename.
+ * %E Pathname of executable, with slashes ('/') replaced by
+ * exclamation marks ('!').
+ * %f Similar to %e with the main script filename.
+ * %F Similar to %E with the main script filename.
+ * %p PID of dumped process in decimal.
+ * %t Time of dump, expressed as seconds since the Epoch,
+ * 1970-01-01 00:00:00 +0000 (UTC).
+ * %NNN Octal char code, upto 3 digits.
+ */
+static char *
+expand_report_argument(const char **input_template, struct report_expansion *values,
+ char *buf, size_t size, bool word)
+{
+ char *p = buf;
+ char *end = buf + size;
+ const char *template = *input_template;
+ bool store = true;
+
+ if (p >= end-1 || !*template) return NULL;
+ do {
+ char c = *template++;
+ if (word && ISSPACE(c)) break;
+ if (!store) continue;
+ if (c == '%') {
+ size_t n;
+ switch (c = *template++) {
+ case 'e':
+ p = append_basename(p, end, &values->exe, rb_argv0);
+ continue;
+ case 'E':
+ p = append_pathname(p, end, rb_argv0);
+ continue;
+ case 'f':
+ p = append_basename(p, end, &values->script, GET_VM()->orig_progname);
+ continue;
+ case 'F':
+ p = append_pathname(p, end, GET_VM()->orig_progname);
+ continue;
+ case 'p':
+ if (!values->pid) values->pid = getpid();
+ snprintf(p, end-p, "%" PRI_PIDT_PREFIX "d", values->pid);
+ p += strlen(p);
+ continue;
+ case 't':
+ if (!values->time) values->time = time(NULL);
+ snprintf(p, end-p, "%" PRI_TIMET_PREFIX "d", values->time);
+ p += strlen(p);
+ continue;
+ default:
+ if (c >= '0' && c <= '7') {
+ c = (unsigned char)ruby_scan_oct(template-1, 3, &n);
+ template += n - 1;
+ if (!c) store = false;
+ }
+ break;
+ }
+ }
+ if (p < end-1) *p++ = c;
+ } while (*template);
+ *input_template = template;
+ *p = '\0';
+ return ++p;
+}
+
+FILE *ruby_popen_writer(char *const *argv, rb_pid_t *pid);
+
+static FILE *
+open_report_path(const char *template, char *buf, size_t size, rb_pid_t *pid)
+{
+ struct report_expansion values = {{0}};
+
+ if (!template) return NULL;
+ if (0) fprintf(stderr, "RUBY_CRASH_REPORT=%s\n", buf);
+ if (*template == '|') {
+ char *argv[16], *bufend = buf + size, *p;
+ int argc;
+ template++;
+ for (argc = 0; argc < numberof(argv) - 1; ++argc) {
+ while (*template && ISSPACE(*template)) template++;
+ p = expand_report_argument(&template, &values, buf, bufend-buf, true);
+ if (!p) break;
+ argv[argc] = buf;
+ buf = p;
+ }
+ argv[argc] = NULL;
+ if (!p) return ruby_popen_writer(argv, pid);
+ }
+ else if (*template) {
+ expand_report_argument(&template, &values, buf, size, false);
+ return fopen(buf, "w");
+ }
+ return NULL;
+}
+
+static const char *crash_report;
+
/* SIGSEGV handler might have a very small stack. Thus we need to use it carefully. */
#define REPORT_BUG_BUFSIZ 256
static FILE *
-bug_report_file(const char *file, int line)
+bug_report_file(const char *file, int line, rb_pid_t *pid)
{
char buf[REPORT_BUG_BUFSIZ];
- FILE *out = stderr;
+ const char *report = crash_report;
+ if (!report) report = getenv("RUBY_CRASH_REPORT");
+ FILE *out = open_report_path(report, buf, sizeof(buf), pid);
int len = err_position_0(buf, sizeof(buf), file, line);
- if ((ssize_t)fwrite(buf, 1, len, out) == (ssize_t)len ||
- (ssize_t)fwrite(buf, 1, len, (out = stdout)) == (ssize_t)len) {
- return out;
+ if (out) {
+ if ((ssize_t)fwrite(buf, 1, len, out) == (ssize_t)len) return out;
+ fclose(out);
+ }
+ if ((ssize_t)fwrite(buf, 1, len, stderr) == (ssize_t)len) {
+ return stderr;
+ }
+ if ((ssize_t)fwrite(buf, 1, len, stdout) == (ssize_t)len) {
+ return stdout;
}
return NULL;
@@ -735,7 +989,7 @@ bug_report_begin_valist(FILE *out, const char *fmt, va_list args)
} while (0)
static void
-bug_report_end(FILE *out)
+bug_report_end(FILE *out, rb_pid_t pid)
{
/* call additional bug reporters */
{
@@ -746,26 +1000,45 @@ bug_report_end(FILE *out)
}
}
postscript_dump(out);
+ finish_report(out, pid);
}
#define report_bug(file, line, fmt, ctx) do { \
- FILE *out = bug_report_file(file, line); \
+ rb_pid_t pid = -1; \
+ FILE *out = bug_report_file(file, line, &pid); \
if (out) { \
bug_report_begin(out, fmt); \
- rb_vm_bugreport(ctx); \
- bug_report_end(out); \
+ rb_vm_bugreport(ctx, out); \
+ bug_report_end(out, pid); \
} \
} while (0) \
#define report_bug_valist(file, line, fmt, ctx, args) do { \
- FILE *out = bug_report_file(file, line); \
+ rb_pid_t pid = -1; \
+ FILE *out = bug_report_file(file, line, &pid); \
if (out) { \
bug_report_begin_valist(out, fmt, args); \
- rb_vm_bugreport(ctx); \
- bug_report_end(out); \
+ rb_vm_bugreport(ctx, out); \
+ bug_report_end(out, pid); \
} \
} while (0) \
+void
+ruby_set_crash_report(const char *template)
+{
+ crash_report = template;
+#if RUBY_DEBUG
+ rb_pid_t pid = -1;
+ char buf[REPORT_BUG_BUFSIZ];
+ FILE *out = open_report_path(template, buf, sizeof(buf), &pid);
+ if (out) {
+ time_t t = time(NULL);
+ fprintf(out, "ruby_test_bug_report: %s", ctime(&t));
+ finish_report(out, pid);
+ }
+#endif
+}
+
NORETURN(static void die(void));
static void
die(void)
@@ -815,6 +1088,7 @@ rb_bug_for_fatal_signal(ruby_sighandler_t default_sighandler, int sig, const voi
if (default_sighandler) default_sighandler(sig);
+ ruby_default_signal(sig);
die();
}
@@ -871,13 +1145,28 @@ rb_report_bug_valist(VALUE file, int line, const char *fmt, va_list args)
void
rb_assert_failure(const char *file, int line, const char *name, const char *expr)
{
+ rb_assert_failure_detail(file, line, name, expr, NULL);
+}
+
+void
+rb_assert_failure_detail(const char *file, int line, const char *name, const char *expr,
+ const char *fmt, ...)
+{
FILE *out = stderr;
fprintf(out, "Assertion Failed: %s:%d:", file, line);
if (name) fprintf(out, "%s:", name);
fprintf(out, "%s\n%s\n\n", expr, rb_dynamic_description);
+
+ if (fmt && *fmt) {
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(out, fmt, args);
+ va_end(args);
+ }
+
preface_dump(out);
- rb_vm_bugreport(NULL);
- bug_report_end(out);
+ rb_vm_bugreport(NULL, out);
+ bug_report_end(out, -1);
die();
}
@@ -1071,7 +1360,7 @@ rb_check_typeddata(VALUE obj, const rb_data_type_t *data_type)
actual = rb_str_new_cstr(name); /* or rb_fstring_cstr? not sure... */
}
else {
- return DATA_PTR(obj);
+ return RTYPEDDATA_GET_DATA(obj);
}
const char *expected = data_type->wrap_struct_name;
@@ -1142,6 +1431,7 @@ rb_exc_new_cstr(VALUE etype, const char *s)
VALUE
rb_exc_new_str(VALUE etype, VALUE str)
{
+ rb_yjit_lazy_push_frame(GET_EC()->cfp->pc);
StringValue(str);
return rb_class_new_instance(1, &str, etype);
}
@@ -1410,7 +1700,7 @@ exc_detailed_message(int argc, VALUE *argv, VALUE exc)
VALUE highlight = check_highlight_keyword(opt, 0);
- extern VALUE rb_decorate_message(const VALUE eclass, const VALUE emesg, int highlight);
+ extern VALUE rb_decorate_message(const VALUE eclass, VALUE emesg, int highlight);
return rb_decorate_message(CLASS_OF(exc), rb_get_message(exc), RTEST(highlight));
}
@@ -1549,7 +1839,7 @@ static VALUE
rb_check_backtrace(VALUE bt)
{
long i;
- static const char err[] = "backtrace must be Array of String";
+ static const char err[] = "backtrace must be an Array of String or an Array of Thread::Backtrace::Location";
if (!NIL_P(bt)) {
if (RB_TYPE_P(bt, T_STRING)) return rb_ary_new3(1, bt);
@@ -1572,15 +1862,23 @@ rb_check_backtrace(VALUE bt)
* exc.set_backtrace(backtrace) -> array
*
* Sets the backtrace information associated with +exc+. The +backtrace+ must
- * be an array of String objects or a single String in the format described
- * in Exception#backtrace.
+ * be an array of Thread::Backtrace::Location objects or an array of String objects
+ * or a single String in the format described in Exception#backtrace.
*
*/
static VALUE
exc_set_backtrace(VALUE exc, VALUE bt)
{
- return rb_ivar_set(exc, id_bt, rb_check_backtrace(bt));
+ VALUE btobj = rb_location_ary_to_backtrace(bt);
+ if (RTEST(btobj)) {
+ rb_ivar_set(exc, id_bt, btobj);
+ rb_ivar_set(exc, id_bt_locations, btobj);
+ return bt;
+ }
+ else {
+ return rb_ivar_set(exc, id_bt, rb_check_backtrace(bt));
+ }
}
VALUE
@@ -1819,7 +2117,9 @@ name_err_init_attr(VALUE exc, VALUE recv, VALUE method)
cfp = rb_vm_get_ruby_level_next_cfp(ec, cfp);
rb_ivar_set(exc, id_name, method);
err_init_recv(exc, recv);
- if (cfp) rb_ivar_set(exc, id_iseq, rb_iseqw_new(cfp->iseq));
+ if (cfp && VM_FRAME_TYPE(cfp) != VM_FRAME_MAGIC_DUMMY) {
+ rb_ivar_set(exc, id_iseq, rb_iseqw_new(cfp->iseq));
+ }
return exc;
}
@@ -1949,50 +2249,50 @@ rb_nomethod_err_new(VALUE mesg, VALUE recv, VALUE method, VALUE args, int priv)
return nometh_err_init_attr(exc, args, priv);
}
-/* :nodoc: */
-enum {
- NAME_ERR_MESG__MESG,
- NAME_ERR_MESG__RECV,
- NAME_ERR_MESG__NAME,
- NAME_ERR_MESG_COUNT
-};
+typedef struct name_error_message_struct {
+ VALUE mesg;
+ VALUE recv;
+ VALUE name;
+} name_error_message_t;
static void
name_err_mesg_mark(void *p)
{
- VALUE *ptr = p;
- rb_gc_mark_locations(ptr, ptr+NAME_ERR_MESG_COUNT);
+ name_error_message_t *ptr = (name_error_message_t *)p;
+ rb_gc_mark_movable(ptr->mesg);
+ rb_gc_mark_movable(ptr->recv);
+ rb_gc_mark_movable(ptr->name);
}
-#define name_err_mesg_free RUBY_TYPED_DEFAULT_FREE
-
-static size_t
-name_err_mesg_memsize(const void *p)
+static void
+name_err_mesg_update(void *p)
{
- return NAME_ERR_MESG_COUNT * sizeof(VALUE);
+ name_error_message_t *ptr = (name_error_message_t *)p;
+ ptr->mesg = rb_gc_location(ptr->mesg);
+ ptr->recv = rb_gc_location(ptr->recv);
+ ptr->name = rb_gc_location(ptr->name);
}
static const rb_data_type_t name_err_mesg_data_type = {
"name_err_mesg",
{
name_err_mesg_mark,
- name_err_mesg_free,
- name_err_mesg_memsize,
+ RUBY_TYPED_DEFAULT_FREE,
+ NULL, // No external memory to report,
+ name_err_mesg_update,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
};
/* :nodoc: */
static VALUE
-rb_name_err_mesg_init(VALUE klass, VALUE mesg, VALUE recv, VALUE method)
+rb_name_err_mesg_init(VALUE klass, VALUE mesg, VALUE recv, VALUE name)
{
- VALUE result = TypedData_Wrap_Struct(klass, &name_err_mesg_data_type, 0);
- VALUE *ptr = ALLOC_N(VALUE, NAME_ERR_MESG_COUNT);
-
- ptr[NAME_ERR_MESG__MESG] = mesg;
- ptr[NAME_ERR_MESG__RECV] = recv;
- ptr[NAME_ERR_MESG__NAME] = method;
- RTYPEDDATA_DATA(result) = ptr;
+ name_error_message_t *message;
+ VALUE result = TypedData_Make_Struct(klass, name_error_message_t, &name_err_mesg_data_type, message);
+ RB_OBJ_WRITE(result, &message->mesg, mesg);
+ RB_OBJ_WRITE(result, &message->recv, recv);
+ RB_OBJ_WRITE(result, &message->name, name);
return result;
}
@@ -2014,14 +2314,16 @@ name_err_mesg_alloc(VALUE klass)
static VALUE
name_err_mesg_init_copy(VALUE obj1, VALUE obj2)
{
- VALUE *ptr1, *ptr2;
-
if (obj1 == obj2) return obj1;
rb_obj_init_copy(obj1, obj2);
- TypedData_Get_Struct(obj1, VALUE, &name_err_mesg_data_type, ptr1);
- TypedData_Get_Struct(obj2, VALUE, &name_err_mesg_data_type, ptr2);
- MEMCPY(ptr1, ptr2, VALUE, NAME_ERR_MESG_COUNT);
+ name_error_message_t *ptr1, *ptr2;
+ TypedData_Get_Struct(obj1, name_error_message_t, &name_err_mesg_data_type, ptr1);
+ TypedData_Get_Struct(obj2, name_error_message_t, &name_err_mesg_data_type, ptr2);
+
+ RB_OBJ_WRITE(obj1, &ptr1->mesg, ptr2->mesg);
+ RB_OBJ_WRITE(obj1, &ptr1->recv, ptr2->recv);
+ RB_OBJ_WRITE(obj1, &ptr1->name, ptr2->name);
return obj1;
}
@@ -2029,19 +2331,18 @@ name_err_mesg_init_copy(VALUE obj1, VALUE obj2)
static VALUE
name_err_mesg_equal(VALUE obj1, VALUE obj2)
{
- VALUE *ptr1, *ptr2;
- int i;
-
if (obj1 == obj2) return Qtrue;
+
if (rb_obj_class(obj2) != rb_cNameErrorMesg)
return Qfalse;
- TypedData_Get_Struct(obj1, VALUE, &name_err_mesg_data_type, ptr1);
- TypedData_Get_Struct(obj2, VALUE, &name_err_mesg_data_type, ptr2);
- for (i=0; i<NAME_ERR_MESG_COUNT; i++) {
- if (!rb_equal(ptr1[i], ptr2[i]))
- return Qfalse;
- }
+ name_error_message_t *ptr1, *ptr2;
+ TypedData_Get_Struct(obj1, name_error_message_t, &name_err_mesg_data_type, ptr1);
+ TypedData_Get_Struct(obj2, name_error_message_t, &name_err_mesg_data_type, ptr2);
+
+ if (!rb_equal(ptr1->mesg, ptr2->mesg)) return Qfalse;
+ if (!rb_equal(ptr1->recv, ptr2->recv)) return Qfalse;
+ if (!rb_equal(ptr1->name, ptr2->name)) return Qfalse;
return Qtrue;
}
@@ -2060,10 +2361,10 @@ name_err_mesg_receiver_name(VALUE obj)
static VALUE
name_err_mesg_to_str(VALUE obj)
{
- VALUE *ptr, mesg;
- TypedData_Get_Struct(obj, VALUE, &name_err_mesg_data_type, ptr);
+ name_error_message_t *ptr;
+ TypedData_Get_Struct(obj, name_error_message_t, &name_err_mesg_data_type, ptr);
- mesg = ptr[NAME_ERR_MESG__MESG];
+ VALUE mesg = ptr->mesg;
if (NIL_P(mesg)) return Qnil;
else {
struct RString s_str, c_str, d_str;
@@ -2073,7 +2374,7 @@ name_err_mesg_to_str(VALUE obj)
#define FAKE_CSTR(v, str) rb_setup_fake_str((v), (str), rb_strlen_lit(str), usascii)
c = s = FAKE_CSTR(&s_str, "");
- obj = ptr[NAME_ERR_MESG__RECV];
+ obj = ptr->recv;
switch (obj) {
case Qnil:
c = d = FAKE_CSTR(&d_str, "nil");
@@ -2116,7 +2417,7 @@ name_err_mesg_to_str(VALUE obj)
VALUE klass;
object:
klass = CLASS_OF(obj);
- if (RB_TYPE_P(klass, T_CLASS) && FL_TEST(klass, FL_SINGLETON)) {
+ if (RB_TYPE_P(klass, T_CLASS) && RCLASS_SINGLETON_P(klass)) {
s = FAKE_CSTR(&s_str, "");
if (obj == rb_vm_top_self()) {
c = FAKE_CSTR(&c_str, "main");
@@ -2144,7 +2445,7 @@ name_err_mesg_to_str(VALUE obj)
c = c2;
break;
}
- args[0] = rb_obj_as_string(ptr[NAME_ERR_MESG__NAME]);
+ args[0] = rb_obj_as_string(ptr->name);
args[1] = d;
args[2] = s;
args[3] = c;
@@ -2177,17 +2478,17 @@ name_err_mesg_load(VALUE klass, VALUE str)
static VALUE
name_err_receiver(VALUE self)
{
- VALUE *ptr, recv, mesg;
-
- recv = rb_ivar_lookup(self, id_recv, Qundef);
+ VALUE recv = rb_ivar_lookup(self, id_recv, Qundef);
if (!UNDEF_P(recv)) return recv;
- mesg = rb_attr_get(self, id_mesg);
+ VALUE mesg = rb_attr_get(self, id_mesg);
if (!rb_typeddata_is_kind_of(mesg, &name_err_mesg_data_type)) {
rb_raise(rb_eArgError, "no receiver is available");
}
- ptr = DATA_PTR(mesg);
- return ptr[NAME_ERR_MESG__RECV];
+
+ name_error_message_t *ptr;
+ TypedData_Get_Struct(mesg, name_error_message_t, &name_err_mesg_data_type, ptr);
+ return ptr->recv;
}
/*
@@ -2439,38 +2740,54 @@ syntax_error_with_path(VALUE exc, VALUE path, VALUE *mesg, rb_encoding *enc)
static st_table *syserr_tbl;
-static VALUE
-set_syserr(int n, const char *name)
+void
+rb_free_warning(void)
{
- st_data_t error;
+ st_free_table(warning_categories.id2enum);
+ st_free_table(warning_categories.enum2id);
+ st_free_table(syserr_tbl);
+}
- if (!st_lookup(syserr_tbl, n, &error)) {
- error = rb_define_class_under(rb_mErrno, name, rb_eSystemCallError);
+static VALUE
+setup_syserr(int n, const char *name)
+{
+ VALUE error = rb_define_class_under(rb_mErrno, name, rb_eSystemCallError);
- /* capture nonblock errnos for WaitReadable/WaitWritable subclasses */
- switch (n) {
- case EAGAIN:
- rb_eEAGAIN = error;
+ /* capture nonblock errnos for WaitReadable/WaitWritable subclasses */
+ switch (n) {
+ case EAGAIN:
+ rb_eEAGAIN = error;
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
- break;
- case EWOULDBLOCK:
+ break;
+ case EWOULDBLOCK:
#endif
- rb_eEWOULDBLOCK = error;
- break;
- case EINPROGRESS:
- rb_eEINPROGRESS = error;
- break;
- }
+ rb_eEWOULDBLOCK = error;
+ break;
+ case EINPROGRESS:
+ rb_eEINPROGRESS = error;
+ break;
+ }
- rb_define_const(error, "Errno", INT2NUM(n));
- st_add_direct(syserr_tbl, n, error);
+ rb_define_const(error, "Errno", INT2NUM(n));
+ st_add_direct(syserr_tbl, n, (st_data_t)error);
+ return error;
+}
+
+static VALUE
+set_syserr(int n, const char *name)
+{
+ st_data_t error;
+
+ if (!st_lookup(syserr_tbl, n, &error)) {
+ return setup_syserr(n, name);
}
else {
- rb_define_const(rb_mErrno, name, error);
+ VALUE errclass = (VALUE)error;
+ rb_define_const(rb_mErrno, name, errclass);
+ return errclass;
}
- return error;
}
static VALUE
@@ -2479,12 +2796,12 @@ get_syserr(int n)
st_data_t error;
if (!st_lookup(syserr_tbl, n, &error)) {
- char name[8]; /* some Windows' errno have 5 digits. */
+ char name[DECIMAL_SIZE_OF(n) + sizeof("E-")];
snprintf(name, sizeof(name), "E%03d", n);
- error = set_syserr(n, name);
+ return setup_syserr(n, name);
}
- return error;
+ return (VALUE)error;
}
/*
@@ -2798,7 +3115,7 @@ syserr_eqq(VALUE self, VALUE exc)
*
* <em>raises the exception:</em>
*
- * NoMethodError: undefined method `to_ary' for "hello":String
+ * NoMethodError: undefined method `to_ary' for an instance of String
*/
/*
@@ -2871,7 +3188,7 @@ syserr_eqq(VALUE self, VALUE exc)
/*
* Document-class: fatal
*
- * fatal is an Exception that is raised when Ruby has encountered a fatal
+ * +fatal+ is an Exception that is raised when Ruby has encountered a fatal
* error and must exit.
*/
@@ -2994,9 +3311,9 @@ exception_dumper(VALUE exc)
}
static int
-ivar_copy_i(st_data_t key, st_data_t val, st_data_t exc)
+ivar_copy_i(ID key, VALUE val, st_data_t exc)
{
- rb_ivar_set((VALUE) exc, (ID) key, (VALUE) val);
+ rb_ivar_set((VALUE)exc, key, val);
return ST_CONTINUE;
}
@@ -3124,6 +3441,7 @@ Init_Exception(void)
rb_mWarning = rb_define_module("Warning");
rb_define_singleton_method(rb_mWarning, "[]", rb_warning_s_aref, 1);
rb_define_singleton_method(rb_mWarning, "[]=", rb_warning_s_aset, 2);
+ rb_define_singleton_method(rb_mWarning, "categories", rb_warning_s_categories, 0);
rb_define_method(rb_mWarning, "warn", rb_warning_s_warn, -1);
rb_extend_object(rb_mWarning, rb_mWarning);
@@ -3249,7 +3567,7 @@ rb_fatal(const char *fmt, ...)
/* The thread has no GVL. Object allocation impossible (cant run GC),
* thus no message can be printed out. */
fprintf(stderr, "[FATAL] rb_fatal() outside of GVL\n");
- rb_print_backtrace();
+ rb_print_backtrace(stderr);
die();
}
@@ -3541,6 +3859,13 @@ inspect_frozen_obj(VALUE obj, VALUE mesg, int recur)
void
rb_error_frozen_object(VALUE frozen_obj)
{
+ rb_yjit_lazy_push_frame(GET_EC()->cfp->pc);
+
+ if (CHILLED_STRING_P(frozen_obj)) {
+ CHILLED_STRING_MUTATED(frozen_obj);
+ return;
+ }
+
VALUE debug_info;
const ID created_info = id_debug_created_info;
VALUE mesg = rb_sprintf("can't modify frozen %"PRIsVALUE": ",
@@ -3577,9 +3902,13 @@ rb_check_copyable(VALUE obj, VALUE orig)
void
Init_syserr(void)
{
- rb_eNOERROR = set_syserr(0, "NOERROR");
+ rb_eNOERROR = setup_syserr(0, "NOERROR");
+#if 0
+ /* No error */
+ rb_define_const(rb_mErrno, "NOERROR", rb_eNOERROR);
+#endif
#define defined_error(name, num) set_syserr((num), (name));
-#define undefined_error(name) set_syserr(0, (name));
+#define undefined_error(name) rb_define_const(rb_mErrno, (name), rb_eNOERROR);
#include "known_errors.inc"
#undef defined_error
#undef undefined_error
diff --git a/eval.c b/eval.c
index 6fc3969690..e8da342ac6 100644
--- a/eval.c
+++ b/eval.c
@@ -70,8 +70,6 @@ ruby_setup(void)
if (GET_VM())
return 0;
- ruby_init_stack((void *)&state);
-
/*
* Disable THP early before mallocs happen because we want this to
* affect as many future pages as possible for CoW-friendliness
@@ -80,7 +78,6 @@ ruby_setup(void)
prctl(PR_SET_THP_DISABLE, 1, 0, 0, 0);
#endif
Init_BareVM();
- Init_heap();
rb_vm_encoded_insn_data_table_init();
Init_vm_objects();
@@ -115,10 +112,9 @@ ruby_options(int argc, char **argv)
enum ruby_tag_type state;
void *volatile iseq = 0;
- ruby_init_stack((void *)&iseq);
EC_PUSH_TAG(ec);
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
- SAVE_ROOT_JMPBUF(GET_THREAD(), iseq = ruby_process_options(argc, argv));
+ iseq = ruby_process_options(argc, argv);
}
else {
rb_ec_clear_current_thread_trace_func(ec);
@@ -200,16 +196,15 @@ rb_ec_cleanup(rb_execution_context_t *ec, enum ruby_tag_type ex)
EC_PUSH_TAG(ec);
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
- SAVE_ROOT_JMPBUF(th, { RUBY_VM_CHECK_INTS(ec); });
+ RUBY_VM_CHECK_INTS(ec);
step_0: step++;
save_error = ec->errinfo;
if (THROW_DATA_P(ec->errinfo)) ec->errinfo = Qnil;
- ruby_init_stack(&message);
/* exits with failure but silently when an exception raised
* here */
- SAVE_ROOT_JMPBUF(th, rb_ec_teardown(ec));
+ rb_ec_teardown(ec);
step_1: step++;
VALUE err = ec->errinfo;
@@ -227,7 +222,7 @@ rb_ec_cleanup(rb_execution_context_t *ec, enum ruby_tag_type ex)
mode1 = exiting_split(err, (mode0 & EXITING_WITH_STATUS) ? NULL : &sysex, &signaled);
if (mode1 & EXITING_WITH_MESSAGE) {
buf = rb_str_new(NULL, 0);
- SAVE_ROOT_JMPBUF(th, rb_ec_error_print_detailed(ec, err, buf, Qundef));
+ rb_ec_error_print_detailed(ec, err, buf, Qundef);
message = buf;
}
}
@@ -236,7 +231,7 @@ rb_ec_cleanup(rb_execution_context_t *ec, enum ruby_tag_type ex)
/* protect from Thread#raise */
th->status = THREAD_KILLED;
- SAVE_ROOT_JMPBUF(th, rb_ractor_terminate_all());
+ rb_ractor_terminate_all();
step_3: step++;
if (!NIL_P(buf = message)) {
@@ -283,10 +278,7 @@ rb_ec_exec_node(rb_execution_context_t *ec, void *n)
EC_PUSH_TAG(ec);
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
- rb_thread_t *const th = rb_ec_thread_ptr(ec);
- SAVE_ROOT_JMPBUF(th, {
- rb_iseq_eval_main(iseq);
- });
+ rb_iseq_eval_main(iseq);
}
EC_POP_TAG();
return state;
@@ -324,14 +316,12 @@ ruby_run_node(void *n)
rb_ec_cleanup(ec, (NIL_P(ec->errinfo) ? TAG_NONE : TAG_RAISE));
return status;
}
- ruby_init_stack((void *)&status);
return rb_ec_cleanup(ec, rb_ec_exec_node(ec, n));
}
int
ruby_exec_node(void *n)
{
- ruby_init_stack((void *)&n);
return rb_ec_exec_node(GET_EC(), n);
}
@@ -419,11 +409,11 @@ rb_mod_s_constants(int argc, VALUE *argv, VALUE mod)
return rb_const_list(data);
}
-/*!
- * Asserts that \a klass is not a frozen class.
- * \param[in] klass a \c Module object
- * \exception RuntimeError if \a klass is not a class or frozen.
- * \ingroup class
+/**
+ * Asserts that `klass` is not a frozen class.
+ * @param[in] klass a `Module` object
+ * @exception RuntimeError if `klass` is not a class or frozen.
+ * @ingroup class
*/
void
rb_class_modify_check(VALUE klass)
@@ -437,7 +427,7 @@ rb_class_modify_check(VALUE klass)
if (OBJ_FROZEN(klass)) {
const char *desc;
- if (FL_TEST(klass, FL_SINGLETON)) {
+ if (RCLASS_SINGLETON_P(klass)) {
desc = "object";
klass = RCLASS_ATTACHED_OBJECT(klass);
if (!SPECIAL_CONST_P(klass)) {
@@ -472,7 +462,7 @@ rb_class_modify_check(VALUE klass)
}
}
-NORETURN(static void rb_longjmp(rb_execution_context_t *, int, volatile VALUE, VALUE));
+NORETURN(static void rb_longjmp(rb_execution_context_t *, enum ruby_tag_type, volatile VALUE, VALUE));
static VALUE get_errinfo(void);
#define get_ec_errinfo(ec) rb_ec_get_errinfo(ec)
@@ -550,7 +540,7 @@ exc_setup_message(const rb_execution_context_t *ec, VALUE mesg, VALUE *cause)
}
static void
-setup_exception(rb_execution_context_t *ec, int tag, volatile VALUE mesg, VALUE cause)
+setup_exception(rb_execution_context_t *ec, enum ruby_tag_type tag, volatile VALUE mesg, VALUE cause)
{
VALUE e;
int line;
@@ -598,15 +588,15 @@ setup_exception(rb_execution_context_t *ec, int tag, volatile VALUE mesg, VALUE
e = rb_obj_as_string(mesg);
ec->errinfo = mesg;
if (file && line) {
- e = rb_sprintf("Exception `%"PRIsVALUE"' at %s:%d - %"PRIsVALUE"\n",
+ e = rb_sprintf("Exception '%"PRIsVALUE"' at %s:%d - %"PRIsVALUE"\n",
rb_obj_class(mesg), file, line, e);
}
else if (file) {
- e = rb_sprintf("Exception `%"PRIsVALUE"' at %s - %"PRIsVALUE"\n",
+ e = rb_sprintf("Exception '%"PRIsVALUE"' at %s - %"PRIsVALUE"\n",
rb_obj_class(mesg), file, e);
}
else {
- e = rb_sprintf("Exception `%"PRIsVALUE"' - %"PRIsVALUE"\n",
+ e = rb_sprintf("Exception '%"PRIsVALUE"' - %"PRIsVALUE"\n",
rb_obj_class(mesg), e);
}
warn_print_str(e);
@@ -654,7 +644,7 @@ rb_ec_setup_exception(const rb_execution_context_t *ec, VALUE mesg, VALUE cause)
}
static void
-rb_longjmp(rb_execution_context_t *ec, int tag, volatile VALUE mesg, VALUE cause)
+rb_longjmp(rb_execution_context_t *ec, enum ruby_tag_type tag, volatile VALUE mesg, VALUE cause)
{
mesg = exc_setup_message(ec, mesg, &cause);
setup_exception(ec, tag, mesg, cause);
@@ -664,10 +654,10 @@ rb_longjmp(rb_execution_context_t *ec, int tag, volatile VALUE mesg, VALUE cause
static VALUE make_exception(int argc, const VALUE *argv, int isstr);
-NORETURN(static void rb_exc_exception(VALUE mesg, int tag, VALUE cause));
+NORETURN(static void rb_exc_exception(VALUE mesg, enum ruby_tag_type tag, VALUE cause));
static void
-rb_exc_exception(VALUE mesg, int tag, VALUE cause)
+rb_exc_exception(VALUE mesg, enum ruby_tag_type tag, VALUE cause)
{
if (!NIL_P(mesg)) {
mesg = make_exception(1, &mesg, FALSE);
@@ -675,12 +665,12 @@ rb_exc_exception(VALUE mesg, int tag, VALUE cause)
rb_longjmp(GET_EC(), tag, mesg, cause);
}
-/*!
+/**
* Raises an exception in the current thread.
- * \param[in] mesg an Exception class or an \c Exception object.
- * \exception always raises an instance of the given exception class or
- * the given \c Exception object.
- * \ingroup exception
+ * @param[in] mesg an Exception class or an `Exception` object.
+ * @exception always raises an instance of the given exception class or
+ * the given `Exception` object.
+ * @ingroup exception
*/
void
rb_exc_raise(VALUE mesg)
@@ -770,7 +760,8 @@ rb_f_raise(int argc, VALUE *argv)
* object that returns an +Exception+ object when sent an +exception+
* message). The optional second parameter sets the message associated with
* the exception (accessible via Exception#message), and the third parameter
- * is an array of callback information (accessible via Exception#backtrace).
+ * is an array of callback information (accessible via
+ * Exception#backtrace_locations or Exception#backtrace).
* The +cause+ of the generated exception (accessible via Exception#cause)
* is automatically set to the "current" exception (<code>$!</code>), if any.
* An alternative value, either an +Exception+ object or +nil+, can be
@@ -780,7 +771,7 @@ rb_f_raise(int argc, VALUE *argv)
* <code>begin...end</code> blocks.
*
* raise "Failed to create socket"
- * raise ArgumentError, "No parameters", caller
+ * raise ArgumentError, "No parameters", caller_locations
*/
static VALUE
@@ -980,7 +971,7 @@ rb_protect(VALUE (* proc) (VALUE), VALUE data, int *pstate)
EC_PUSH_TAG(ec);
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
- SAVE_ROOT_JMPBUF(rb_ec_thread_ptr(ec), result = (*proc) (data));
+ result = (*proc)(data);
}
else {
rb_vm_rewind_cfp(ec, cfp);
@@ -994,7 +985,7 @@ rb_protect(VALUE (* proc) (VALUE), VALUE data, int *pstate)
VALUE
rb_ensure(VALUE (*b_proc)(VALUE), VALUE data1, VALUE (*e_proc)(VALUE), VALUE data2)
{
- int state;
+ enum ruby_tag_type state;
volatile VALUE result = Qnil;
VALUE errinfo;
rb_execution_context_t * volatile ec = GET_EC();
@@ -1285,12 +1276,6 @@ rb_using_refinement(rb_cref_t *cref, VALUE klass, VALUE module)
RCLASS_M_TBL(c) = RCLASS_M_TBL(module);
- module = RCLASS_SUPER(module);
- while (module && module != klass) {
- c = RCLASS_SET_SUPER(c, rb_include_class_new(module, RCLASS_SUPER(c)));
- RB_OBJ_WRITE(c, &RCLASS_REFINED_CLASS(c), klass);
- module = RCLASS_SUPER(module);
- }
rb_hash_aset(CREF_REFINEMENTS(cref), klass, iclass);
}
@@ -1341,14 +1326,21 @@ rb_using_module(const rb_cref_t *cref, VALUE module)
{
Check_Type(module, T_MODULE);
using_module_recursive(cref, module);
- rb_clear_method_cache_all();
+ rb_clear_all_refinement_method_cache();
}
/*
* call-seq:
- * refined_class -> class
+ * target -> class_or_module
*
- * Return the class refined by the receiver.
+ * Return the class or module refined by the receiver.
+ *
+ * module M
+ * refine String do
+ * end
+ * end
+ *
+ * M.refinements[0].target # => String
*/
VALUE
rb_refinement_module_get_refined_class(VALUE module)
@@ -1359,6 +1351,21 @@ rb_refinement_module_get_refined_class(VALUE module)
return rb_attr_get(module, id_refined_class);
}
+/*
+ * call-seq:
+ * refined_class -> class
+ *
+ * Deprecated; prefer #target.
+ *
+ * Return the class refined by the receiver.
+ */
+static VALUE
+rb_refinement_refined_class(VALUE module)
+{
+ rb_warn_deprecated_to_remove("3.4", "Refinement#refined_class", "Refinement#target");
+ return rb_refinement_module_get_refined_class(module);
+}
+
static void
add_activated_refinement(VALUE activated_refinements,
VALUE klass, VALUE refinement)
@@ -1775,6 +1782,16 @@ rb_obj_extend(int argc, VALUE *argv, VALUE obj)
return obj;
}
+VALUE
+rb_top_main_class(const char *method)
+{
+ VALUE klass = GET_THREAD()->top_wrapper;
+
+ if (!klass) return rb_cObject;
+ rb_warning("main.%s in the wrapped load is effective only in wrapper module", method);
+ return klass;
+}
+
/*
* call-seq:
* include(module, ...) -> self
@@ -1787,13 +1804,7 @@ rb_obj_extend(int argc, VALUE *argv, VALUE obj)
static VALUE
top_include(int argc, VALUE *argv, VALUE self)
{
- rb_thread_t *th = GET_THREAD();
-
- if (th->top_wrapper) {
- rb_warning("main.include in the wrapped load is effective only in wrapper module");
- return rb_mod_include(argc, argv, th->top_wrapper);
- }
- return rb_mod_include(argc, argv, rb_cObject);
+ return rb_mod_include(argc, argv, rb_top_main_class("include"));
}
/*
@@ -1807,7 +1818,7 @@ top_include(int argc, VALUE *argv, VALUE self)
static VALUE
top_using(VALUE self, VALUE module)
{
- const rb_cref_t *cref = CREF_NEXT(rb_vm_cref());;
+ const rb_cref_t *cref = CREF_NEXT(rb_vm_cref());
rb_control_frame_t *prev_cfp = previous_frame(GET_EC());
rb_thread_t *th = GET_THREAD();
@@ -2000,7 +2011,7 @@ f_global_variables(VALUE _)
* +Proc+ object) or block is executed whenever the variable
* is assigned. The block or +Proc+ object receives the
* variable's new value as a parameter. Also see
- * Kernel::untrace_var.
+ * #untrace_var.
*
* trace_var :$_, proc {|v| puts "$_ is now '#{v}'" }
* $_ = "hello"
@@ -2067,7 +2078,8 @@ Init_eval(void)
rb_mod_s_used_refinements, 0);
rb_undef_method(rb_cClass, "refine");
rb_define_private_method(rb_cRefinement, "import_methods", refinement_import_methods, -1);
- rb_define_method(rb_cRefinement, "refined_class", rb_refinement_module_get_refined_class, 0);
+ rb_define_method(rb_cRefinement, "target", rb_refinement_module_get_refined_class, 0);
+ rb_define_method(rb_cRefinement, "refined_class", rb_refinement_refined_class, 0);
rb_undef_method(rb_cRefinement, "append_features");
rb_undef_method(rb_cRefinement, "prepend_features");
rb_undef_method(rb_cRefinement, "extend_object");
@@ -2096,3 +2108,21 @@ Init_eval(void)
id_signo = rb_intern_const("signo");
id_status = rb_intern_const("status");
}
+
+int
+rb_errno(void)
+{
+ return *rb_orig_errno_ptr();
+}
+
+void
+rb_errno_set(int e)
+{
+ *rb_orig_errno_ptr() = e;
+}
+
+int *
+rb_errno_ptr(void)
+{
+ return rb_orig_errno_ptr();
+}
diff --git a/eval_error.c b/eval_error.c
index 04891f4369..f3d05a1043 100644
--- a/eval_error.c
+++ b/eval_error.c
@@ -47,7 +47,7 @@ error_pos_str(void)
return rb_sprintf("%"PRIsVALUE": ", sourcefile);
}
else if ((caller_name = rb_frame_callee()) != 0) {
- return rb_sprintf("%"PRIsVALUE":%d:in `%"PRIsVALUE"': ",
+ return rb_sprintf("%"PRIsVALUE":%d:in '%"PRIsVALUE"': ",
sourcefile, sourceline,
rb_id2str(caller_name));
}
@@ -125,7 +125,7 @@ print_errinfo(const VALUE eclass, const VALUE errat, const VALUE emesg, const VA
}
VALUE
-rb_decorate_message(const VALUE eclass, const VALUE emesg, int highlight)
+rb_decorate_message(const VALUE eclass, VALUE emesg, int highlight)
{
const char *einfo = "";
long elen = 0;
@@ -210,6 +210,8 @@ rb_decorate_message(const VALUE eclass, const VALUE emesg, int highlight)
}
}
+ RB_GC_GUARD(emesg);
+
return str;
}
@@ -390,7 +392,7 @@ rb_ec_error_print(rb_execution_context_t *volatile ec, volatile VALUE errinfo)
rb_ec_error_print_detailed(ec, errinfo, Qnil, Qundef);
}
-#define undef_mesg_for(v, k) rb_fstring_lit("undefined"v" method `%1$s' for "k" `%2$s'")
+#define undef_mesg_for(v, k) rb_fstring_lit("undefined"v" method '%1$s' for "k" '%2$s'")
#define undef_mesg(v) ( \
is_mod ? \
undef_mesg_for(v, "module") : \
@@ -418,7 +420,7 @@ rb_print_undef_str(VALUE klass, VALUE name)
rb_name_err_raise_str(undef_mesg(""), klass, name);
}
-#define inaccessible_mesg_for(v, k) rb_fstring_lit("method `%1$s' for "k" `%2$s' is "v)
+#define inaccessible_mesg_for(v, k) rb_fstring_lit("method '%1$s' for "k" '%2$s' is "v)
#define inaccessible_mesg(v) ( \
is_mod ? \
inaccessible_mesg_for(v, "module") : \
@@ -461,7 +463,12 @@ exiting_split(VALUE errinfo, volatile int *exitcode, volatile int *sigstatus)
if (NIL_P(errinfo)) return 0;
- if (rb_obj_is_kind_of(errinfo, rb_eSystemExit)) {
+ if (THROW_DATA_P(errinfo)) {
+ int throw_state = ((const struct vm_throw_data *)errinfo)->throw_state;
+ ex = throw_state & VM_THROW_STATE_MASK;
+ result |= EXITING_WITH_STATUS;
+ }
+ else if (rb_obj_is_kind_of(errinfo, rb_eSystemExit)) {
ex = sysexit_status(errinfo);
result |= EXITING_WITH_STATUS;
}
diff --git a/eval_intern.h b/eval_intern.h
index 6cbaa51361..9a551120ff 100644
--- a/eval_intern.h
+++ b/eval_intern.h
@@ -95,14 +95,6 @@ extern int select_large_fdset(int, fd_set *, fd_set *, fd_set *, struct timeval
#include <sys/stat.h>
-
-#define SAVE_ROOT_JMPBUF(th, stmt) do \
- if (true) { \
- stmt; \
- } \
- else if (th) { /* suppress unused-variable warning */ \
- } while (0)
-
#define EC_PUSH_TAG(ec) do { \
rb_execution_context_t * const _ec = (ec); \
struct rb_vm_tag _tag; \
@@ -110,9 +102,11 @@ extern int select_large_fdset(int, fd_set *, fd_set *, fd_set *, struct timeval
_tag.tag = Qundef; \
_tag.prev = _ec->tag; \
_tag.lock_rec = rb_ec_vm_lock_rec(_ec); \
+ rb_vm_tag_jmpbuf_init(&_tag.buf); \
#define EC_POP_TAG() \
_ec->tag = _tag.prev; \
+ rb_vm_tag_jmpbuf_deinit(&_tag.buf); \
} while (0)
#define EC_TMPPOP_TAG() \
@@ -151,6 +145,8 @@ rb_ec_tag_state(const rb_execution_context_t *ec)
enum ruby_tag_type state = tag->state;
tag->state = TAG_NONE;
rb_ec_vm_lock_rec_check(ec, tag->lock_rec);
+ RBIMPL_ASSUME(state > TAG_NONE);
+ RBIMPL_ASSUME(state <= TAG_FATAL);
return state;
}
@@ -158,8 +154,9 @@ NORETURN(static inline void rb_ec_tag_jump(const rb_execution_context_t *ec, enu
static inline void
rb_ec_tag_jump(const rb_execution_context_t *ec, enum ruby_tag_type st)
{
+ RUBY_ASSERT(st > TAG_NONE && st <= TAG_FATAL, ": Invalid tag jump: %d", (int)st);
ec->tag->state = st;
- ruby_longjmp(ec->tag->buf, 1);
+ ruby_longjmp(RB_VM_TAG_JMPBUF_GET(ec->tag->buf), 1);
}
/*
@@ -167,7 +164,7 @@ rb_ec_tag_jump(const rb_execution_context_t *ec, enum ruby_tag_type st)
[ISO/IEC 9899:1999] 7.13.1.1
*/
#define EC_EXEC_TAG() \
- (ruby_setjmp(_tag.buf) ? rb_ec_tag_state(VAR_FROM_MEMORY(_ec)) : (EC_REPUSH_TAG(), 0))
+ (UNLIKELY(ruby_setjmp(RB_VM_TAG_JMPBUF_GET(_tag.buf))) ? rb_ec_tag_state(VAR_FROM_MEMORY(_ec)) : (EC_REPUSH_TAG(), 0))
#define EC_JUMP_TAG(ec, st) rb_ec_tag_jump(ec, st)
@@ -293,9 +290,9 @@ NORETURN(void rb_print_undef(VALUE, ID, rb_method_visibility_t));
NORETURN(void rb_print_undef_str(VALUE, VALUE));
NORETURN(void rb_print_inaccessible(VALUE, ID, rb_method_visibility_t));
NORETURN(void rb_vm_localjump_error(const char *,VALUE, int));
-NORETURN(void rb_vm_jump_tag_but_local_jump(int));
+NORETURN(void rb_vm_jump_tag_but_local_jump(enum ruby_tag_type));
-VALUE rb_vm_make_jump_tag_but_local_jump(int state, VALUE val);
+VALUE rb_vm_make_jump_tag_but_local_jump(enum ruby_tag_type state, VALUE val);
rb_cref_t *rb_vm_cref(void);
rb_cref_t *rb_vm_cref_replace_with_duplicated_cref(void);
VALUE rb_vm_call_cfunc(VALUE recv, VALUE (*func)(VALUE), VALUE arg, VALUE block_handler, VALUE filename);
@@ -324,16 +321,4 @@ rb_char_next(const char *p)
# endif
#endif
-#if defined DOSISH || defined __CYGWIN__
-static inline void
-translit_char(char *p, int from, int to)
-{
- while (*p) {
- if ((unsigned char)*p == from)
- *p = to;
- p = CharNext(p);
- }
-}
-#endif
-
#endif /* RUBY_EVAL_INTERN_H */
diff --git a/ext/-test-/RUBY_ALIGNOF/depend b/ext/-test-/RUBY_ALIGNOF/depend
index 3011b637e5..25364e55fb 100644
--- a/ext/-test-/RUBY_ALIGNOF/depend
+++ b/ext/-test-/RUBY_ALIGNOF/depend
@@ -147,6 +147,7 @@ c.o: $(hdrdir)/ruby/internal/special_consts.h
c.o: $(hdrdir)/ruby/internal/static_assert.h
c.o: $(hdrdir)/ruby/internal/stdalign.h
c.o: $(hdrdir)/ruby/internal/stdbool.h
+c.o: $(hdrdir)/ruby/internal/stdckdint.h
c.o: $(hdrdir)/ruby/internal/symbol.h
c.o: $(hdrdir)/ruby/internal/value.h
c.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/arith_seq/beg_len_step/depend b/ext/-test-/arith_seq/beg_len_step/depend
index dc807eaa99..702a0037a8 100644
--- a/ext/-test-/arith_seq/beg_len_step/depend
+++ b/ext/-test-/arith_seq/beg_len_step/depend
@@ -146,6 +146,7 @@ beg_len_step.o: $(hdrdir)/ruby/internal/special_consts.h
beg_len_step.o: $(hdrdir)/ruby/internal/static_assert.h
beg_len_step.o: $(hdrdir)/ruby/internal/stdalign.h
beg_len_step.o: $(hdrdir)/ruby/internal/stdbool.h
+beg_len_step.o: $(hdrdir)/ruby/internal/stdckdint.h
beg_len_step.o: $(hdrdir)/ruby/internal/symbol.h
beg_len_step.o: $(hdrdir)/ruby/internal/value.h
beg_len_step.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/arith_seq/extract/depend b/ext/-test-/arith_seq/extract/depend
index 231736b277..fdbd71dfbc 100644
--- a/ext/-test-/arith_seq/extract/depend
+++ b/ext/-test-/arith_seq/extract/depend
@@ -146,6 +146,7 @@ extract.o: $(hdrdir)/ruby/internal/special_consts.h
extract.o: $(hdrdir)/ruby/internal/static_assert.h
extract.o: $(hdrdir)/ruby/internal/stdalign.h
extract.o: $(hdrdir)/ruby/internal/stdbool.h
+extract.o: $(hdrdir)/ruby/internal/stdckdint.h
extract.o: $(hdrdir)/ruby/internal/symbol.h
extract.o: $(hdrdir)/ruby/internal/value.h
extract.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/array/concat/depend b/ext/-test-/array/concat/depend
index d66e7a540f..f2213a42ea 100644
--- a/ext/-test-/array/concat/depend
+++ b/ext/-test-/array/concat/depend
@@ -147,6 +147,7 @@ to_ary_concat.o: $(hdrdir)/ruby/internal/special_consts.h
to_ary_concat.o: $(hdrdir)/ruby/internal/static_assert.h
to_ary_concat.o: $(hdrdir)/ruby/internal/stdalign.h
to_ary_concat.o: $(hdrdir)/ruby/internal/stdbool.h
+to_ary_concat.o: $(hdrdir)/ruby/internal/stdckdint.h
to_ary_concat.o: $(hdrdir)/ruby/internal/symbol.h
to_ary_concat.o: $(hdrdir)/ruby/internal/value.h
to_ary_concat.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/array/resize/depend b/ext/-test-/array/resize/depend
index a9c02b3db2..f88a9d03c1 100644
--- a/ext/-test-/array/resize/depend
+++ b/ext/-test-/array/resize/depend
@@ -146,6 +146,7 @@ resize.o: $(hdrdir)/ruby/internal/special_consts.h
resize.o: $(hdrdir)/ruby/internal/static_assert.h
resize.o: $(hdrdir)/ruby/internal/stdalign.h
resize.o: $(hdrdir)/ruby/internal/stdbool.h
+resize.o: $(hdrdir)/ruby/internal/stdckdint.h
resize.o: $(hdrdir)/ruby/internal/symbol.h
resize.o: $(hdrdir)/ruby/internal/value.h
resize.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/asan/asan.c b/ext/-test-/asan/asan.c
new file mode 100644
index 0000000000..45b6253fda
--- /dev/null
+++ b/ext/-test-/asan/asan.c
@@ -0,0 +1,24 @@
+#include "ruby/ruby.h"
+
+static VALUE
+asan_enabled_p(VALUE self)
+{
+#if defined(__has_feature)
+ /* clang uses __has_feature for determining asan */
+ return __has_feature(address_sanitizer) ? Qtrue : Qfalse;
+#elif defined(__SANITIZE_ADDRESS__)
+ /* GCC sets __SANITIZE_ADDRESS__ for determining asan */
+ return Qtrue;
+#else
+ return Qfalse;
+#endif
+}
+
+void
+Init_asan(void)
+{
+ VALUE m = rb_define_module("Test");
+ VALUE c = rb_define_class_under(m, "ASAN", rb_cObject);
+ rb_define_singleton_method(c, "enabled?", asan_enabled_p, 0);
+}
+
diff --git a/ext/-test-/asan/extconf.rb b/ext/-test-/asan/extconf.rb
new file mode 100644
index 0000000000..ec02742b81
--- /dev/null
+++ b/ext/-test-/asan/extconf.rb
@@ -0,0 +1,2 @@
+require 'mkmf'
+create_makefile('-test-/asan')
diff --git a/ext/-test-/bignum/depend b/ext/-test-/bignum/depend
index d4392bb6a1..078915ab72 100644
--- a/ext/-test-/bignum/depend
+++ b/ext/-test-/bignum/depend
@@ -146,6 +146,7 @@ big2str.o: $(hdrdir)/ruby/internal/special_consts.h
big2str.o: $(hdrdir)/ruby/internal/static_assert.h
big2str.o: $(hdrdir)/ruby/internal/stdalign.h
big2str.o: $(hdrdir)/ruby/internal/stdbool.h
+big2str.o: $(hdrdir)/ruby/internal/stdckdint.h
big2str.o: $(hdrdir)/ruby/internal/symbol.h
big2str.o: $(hdrdir)/ruby/internal/value.h
big2str.o: $(hdrdir)/ruby/internal/value_type.h
@@ -305,6 +306,7 @@ bigzero.o: $(hdrdir)/ruby/internal/special_consts.h
bigzero.o: $(hdrdir)/ruby/internal/static_assert.h
bigzero.o: $(hdrdir)/ruby/internal/stdalign.h
bigzero.o: $(hdrdir)/ruby/internal/stdbool.h
+bigzero.o: $(hdrdir)/ruby/internal/stdckdint.h
bigzero.o: $(hdrdir)/ruby/internal/symbol.h
bigzero.o: $(hdrdir)/ruby/internal/value.h
bigzero.o: $(hdrdir)/ruby/internal/value_type.h
@@ -464,6 +466,7 @@ div.o: $(hdrdir)/ruby/internal/special_consts.h
div.o: $(hdrdir)/ruby/internal/static_assert.h
div.o: $(hdrdir)/ruby/internal/stdalign.h
div.o: $(hdrdir)/ruby/internal/stdbool.h
+div.o: $(hdrdir)/ruby/internal/stdckdint.h
div.o: $(hdrdir)/ruby/internal/symbol.h
div.o: $(hdrdir)/ruby/internal/value.h
div.o: $(hdrdir)/ruby/internal/value_type.h
@@ -624,6 +627,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h
init.o: $(hdrdir)/ruby/internal/static_assert.h
init.o: $(hdrdir)/ruby/internal/stdalign.h
init.o: $(hdrdir)/ruby/internal/stdbool.h
+init.o: $(hdrdir)/ruby/internal/stdckdint.h
init.o: $(hdrdir)/ruby/internal/symbol.h
init.o: $(hdrdir)/ruby/internal/value.h
init.o: $(hdrdir)/ruby/internal/value_type.h
@@ -782,6 +786,7 @@ intpack.o: $(hdrdir)/ruby/internal/special_consts.h
intpack.o: $(hdrdir)/ruby/internal/static_assert.h
intpack.o: $(hdrdir)/ruby/internal/stdalign.h
intpack.o: $(hdrdir)/ruby/internal/stdbool.h
+intpack.o: $(hdrdir)/ruby/internal/stdckdint.h
intpack.o: $(hdrdir)/ruby/internal/symbol.h
intpack.o: $(hdrdir)/ruby/internal/value.h
intpack.o: $(hdrdir)/ruby/internal/value_type.h
@@ -941,6 +946,7 @@ mul.o: $(hdrdir)/ruby/internal/special_consts.h
mul.o: $(hdrdir)/ruby/internal/static_assert.h
mul.o: $(hdrdir)/ruby/internal/stdalign.h
mul.o: $(hdrdir)/ruby/internal/stdbool.h
+mul.o: $(hdrdir)/ruby/internal/stdckdint.h
mul.o: $(hdrdir)/ruby/internal/symbol.h
mul.o: $(hdrdir)/ruby/internal/value.h
mul.o: $(hdrdir)/ruby/internal/value_type.h
@@ -1100,6 +1106,7 @@ str2big.o: $(hdrdir)/ruby/internal/special_consts.h
str2big.o: $(hdrdir)/ruby/internal/static_assert.h
str2big.o: $(hdrdir)/ruby/internal/stdalign.h
str2big.o: $(hdrdir)/ruby/internal/stdbool.h
+str2big.o: $(hdrdir)/ruby/internal/stdckdint.h
str2big.o: $(hdrdir)/ruby/internal/symbol.h
str2big.o: $(hdrdir)/ruby/internal/value.h
str2big.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/bug-14834/depend b/ext/-test-/bug-14834/depend
index bf26a571aa..695094fa7a 100644
--- a/ext/-test-/bug-14834/depend
+++ b/ext/-test-/bug-14834/depend
@@ -147,6 +147,7 @@ bug-14384.o: $(hdrdir)/ruby/internal/special_consts.h
bug-14384.o: $(hdrdir)/ruby/internal/static_assert.h
bug-14384.o: $(hdrdir)/ruby/internal/stdalign.h
bug-14384.o: $(hdrdir)/ruby/internal/stdbool.h
+bug-14384.o: $(hdrdir)/ruby/internal/stdckdint.h
bug-14384.o: $(hdrdir)/ruby/internal/symbol.h
bug-14384.o: $(hdrdir)/ruby/internal/value.h
bug-14384.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/bug-3571/depend b/ext/-test-/bug-3571/depend
index 9105093b0d..84517a9c15 100644
--- a/ext/-test-/bug-3571/depend
+++ b/ext/-test-/bug-3571/depend
@@ -147,6 +147,7 @@ bug.o: $(hdrdir)/ruby/internal/special_consts.h
bug.o: $(hdrdir)/ruby/internal/static_assert.h
bug.o: $(hdrdir)/ruby/internal/stdalign.h
bug.o: $(hdrdir)/ruby/internal/stdbool.h
+bug.o: $(hdrdir)/ruby/internal/stdckdint.h
bug.o: $(hdrdir)/ruby/internal/symbol.h
bug.o: $(hdrdir)/ruby/internal/value.h
bug.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/bug-5832/depend b/ext/-test-/bug-5832/depend
index 9105093b0d..84517a9c15 100644
--- a/ext/-test-/bug-5832/depend
+++ b/ext/-test-/bug-5832/depend
@@ -147,6 +147,7 @@ bug.o: $(hdrdir)/ruby/internal/special_consts.h
bug.o: $(hdrdir)/ruby/internal/static_assert.h
bug.o: $(hdrdir)/ruby/internal/stdalign.h
bug.o: $(hdrdir)/ruby/internal/stdbool.h
+bug.o: $(hdrdir)/ruby/internal/stdckdint.h
bug.o: $(hdrdir)/ruby/internal/symbol.h
bug.o: $(hdrdir)/ruby/internal/value.h
bug.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/bug_reporter/depend b/ext/-test-/bug_reporter/depend
index 20882708d1..1c73234247 100644
--- a/ext/-test-/bug_reporter/depend
+++ b/ext/-test-/bug_reporter/depend
@@ -147,6 +147,7 @@ bug_reporter.o: $(hdrdir)/ruby/internal/special_consts.h
bug_reporter.o: $(hdrdir)/ruby/internal/static_assert.h
bug_reporter.o: $(hdrdir)/ruby/internal/stdalign.h
bug_reporter.o: $(hdrdir)/ruby/internal/stdbool.h
+bug_reporter.o: $(hdrdir)/ruby/internal/stdckdint.h
bug_reporter.o: $(hdrdir)/ruby/internal/symbol.h
bug_reporter.o: $(hdrdir)/ruby/internal/value.h
bug_reporter.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/class/depend b/ext/-test-/class/depend
index 0a805f815e..b0595fdc46 100644
--- a/ext/-test-/class/depend
+++ b/ext/-test-/class/depend
@@ -146,6 +146,7 @@ class2name.o: $(hdrdir)/ruby/internal/special_consts.h
class2name.o: $(hdrdir)/ruby/internal/static_assert.h
class2name.o: $(hdrdir)/ruby/internal/stdalign.h
class2name.o: $(hdrdir)/ruby/internal/stdbool.h
+class2name.o: $(hdrdir)/ruby/internal/stdckdint.h
class2name.o: $(hdrdir)/ruby/internal/symbol.h
class2name.o: $(hdrdir)/ruby/internal/value.h
class2name.o: $(hdrdir)/ruby/internal/value_type.h
@@ -305,6 +306,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h
init.o: $(hdrdir)/ruby/internal/static_assert.h
init.o: $(hdrdir)/ruby/internal/stdalign.h
init.o: $(hdrdir)/ruby/internal/stdbool.h
+init.o: $(hdrdir)/ruby/internal/stdckdint.h
init.o: $(hdrdir)/ruby/internal/symbol.h
init.o: $(hdrdir)/ruby/internal/value.h
init.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/debug/depend b/ext/-test-/debug/depend
index 5feeea6d98..67e32c6aa6 100644
--- a/ext/-test-/debug/depend
+++ b/ext/-test-/debug/depend
@@ -147,6 +147,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h
init.o: $(hdrdir)/ruby/internal/static_assert.h
init.o: $(hdrdir)/ruby/internal/stdalign.h
init.o: $(hdrdir)/ruby/internal/stdbool.h
+init.o: $(hdrdir)/ruby/internal/stdckdint.h
init.o: $(hdrdir)/ruby/internal/symbol.h
init.o: $(hdrdir)/ruby/internal/value.h
init.o: $(hdrdir)/ruby/internal/value_type.h
@@ -306,6 +307,7 @@ inspector.o: $(hdrdir)/ruby/internal/special_consts.h
inspector.o: $(hdrdir)/ruby/internal/static_assert.h
inspector.o: $(hdrdir)/ruby/internal/stdalign.h
inspector.o: $(hdrdir)/ruby/internal/stdbool.h
+inspector.o: $(hdrdir)/ruby/internal/stdckdint.h
inspector.o: $(hdrdir)/ruby/internal/symbol.h
inspector.o: $(hdrdir)/ruby/internal/value.h
inspector.o: $(hdrdir)/ruby/internal/value_type.h
@@ -465,6 +467,7 @@ profile_frames.o: $(hdrdir)/ruby/internal/special_consts.h
profile_frames.o: $(hdrdir)/ruby/internal/static_assert.h
profile_frames.o: $(hdrdir)/ruby/internal/stdalign.h
profile_frames.o: $(hdrdir)/ruby/internal/stdbool.h
+profile_frames.o: $(hdrdir)/ruby/internal/stdckdint.h
profile_frames.o: $(hdrdir)/ruby/internal/symbol.h
profile_frames.o: $(hdrdir)/ruby/internal/value.h
profile_frames.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/debug/profile_frames.c b/ext/-test-/debug/profile_frames.c
index d2bba7d183..f9a77a5a78 100644
--- a/ext/-test-/debug/profile_frames.c
+++ b/ext/-test-/debug/profile_frames.c
@@ -37,8 +37,29 @@ profile_frames(VALUE self, VALUE start_v, VALUE num_v)
return result;
}
+static VALUE
+profile_thread_frames(VALUE self, VALUE thread, VALUE start_v, VALUE num_v)
+{
+ int i, collected_size;
+ int start = NUM2INT(start_v);
+ int buff_size = NUM2INT(num_v);
+ VALUE buff[MAX_BUF_SIZE];
+ int lines[MAX_BUF_SIZE];
+ VALUE result = rb_ary_new();
+
+ if (buff_size > MAX_BUF_SIZE) rb_raise(rb_eRuntimeError, "too long buff_size");
+
+ collected_size = rb_profile_thread_frames(thread, start, buff_size, buff, lines);
+ for (i=0; i<collected_size; i++) {
+ rb_ary_push(result, rb_profile_frame_full_label(buff[i]));
+ }
+
+ return result;
+}
+
void
Init_profile_frames(VALUE klass)
{
rb_define_module_function(klass, "profile_frames", profile_frames, 2);
+ rb_define_module_function(klass, "profile_thread_frames", profile_thread_frames, 3);
}
diff --git a/ext/-test-/dln/empty/depend b/ext/-test-/dln/empty/depend
index a460159087..d3e606df57 100644
--- a/ext/-test-/dln/empty/depend
+++ b/ext/-test-/dln/empty/depend
@@ -147,6 +147,7 @@ empty.o: $(hdrdir)/ruby/internal/special_consts.h
empty.o: $(hdrdir)/ruby/internal/static_assert.h
empty.o: $(hdrdir)/ruby/internal/stdalign.h
empty.o: $(hdrdir)/ruby/internal/stdbool.h
+empty.o: $(hdrdir)/ruby/internal/stdckdint.h
empty.o: $(hdrdir)/ruby/internal/symbol.h
empty.o: $(hdrdir)/ruby/internal/value.h
empty.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/enumerator_kw/depend b/ext/-test-/enumerator_kw/depend
index 49ea495421..85daa55b53 100644
--- a/ext/-test-/enumerator_kw/depend
+++ b/ext/-test-/enumerator_kw/depend
@@ -147,6 +147,7 @@ enumerator_kw.o: $(hdrdir)/ruby/internal/special_consts.h
enumerator_kw.o: $(hdrdir)/ruby/internal/static_assert.h
enumerator_kw.o: $(hdrdir)/ruby/internal/stdalign.h
enumerator_kw.o: $(hdrdir)/ruby/internal/stdbool.h
+enumerator_kw.o: $(hdrdir)/ruby/internal/stdckdint.h
enumerator_kw.o: $(hdrdir)/ruby/internal/symbol.h
enumerator_kw.o: $(hdrdir)/ruby/internal/value.h
enumerator_kw.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/exception/depend b/ext/-test-/exception/depend
index 818b4c79df..7a0544b1e1 100644
--- a/ext/-test-/exception/depend
+++ b/ext/-test-/exception/depend
@@ -146,6 +146,7 @@ dataerror.o: $(hdrdir)/ruby/internal/special_consts.h
dataerror.o: $(hdrdir)/ruby/internal/static_assert.h
dataerror.o: $(hdrdir)/ruby/internal/stdalign.h
dataerror.o: $(hdrdir)/ruby/internal/stdbool.h
+dataerror.o: $(hdrdir)/ruby/internal/stdckdint.h
dataerror.o: $(hdrdir)/ruby/internal/symbol.h
dataerror.o: $(hdrdir)/ruby/internal/value.h
dataerror.o: $(hdrdir)/ruby/internal/value_type.h
@@ -315,6 +316,7 @@ enc_raise.o: $(hdrdir)/ruby/internal/special_consts.h
enc_raise.o: $(hdrdir)/ruby/internal/static_assert.h
enc_raise.o: $(hdrdir)/ruby/internal/stdalign.h
enc_raise.o: $(hdrdir)/ruby/internal/stdbool.h
+enc_raise.o: $(hdrdir)/ruby/internal/stdckdint.h
enc_raise.o: $(hdrdir)/ruby/internal/symbol.h
enc_raise.o: $(hdrdir)/ruby/internal/value.h
enc_raise.o: $(hdrdir)/ruby/internal/value_type.h
@@ -476,6 +478,7 @@ ensured.o: $(hdrdir)/ruby/internal/special_consts.h
ensured.o: $(hdrdir)/ruby/internal/static_assert.h
ensured.o: $(hdrdir)/ruby/internal/stdalign.h
ensured.o: $(hdrdir)/ruby/internal/stdbool.h
+ensured.o: $(hdrdir)/ruby/internal/stdckdint.h
ensured.o: $(hdrdir)/ruby/internal/symbol.h
ensured.o: $(hdrdir)/ruby/internal/value.h
ensured.o: $(hdrdir)/ruby/internal/value_type.h
@@ -635,6 +638,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h
init.o: $(hdrdir)/ruby/internal/static_assert.h
init.o: $(hdrdir)/ruby/internal/stdalign.h
init.o: $(hdrdir)/ruby/internal/stdbool.h
+init.o: $(hdrdir)/ruby/internal/stdckdint.h
init.o: $(hdrdir)/ruby/internal/symbol.h
init.o: $(hdrdir)/ruby/internal/value.h
init.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/fatal/depend b/ext/-test-/fatal/depend
index 730a72e52a..36b0ad4205 100644
--- a/ext/-test-/fatal/depend
+++ b/ext/-test-/fatal/depend
@@ -1,4 +1,325 @@
# AUTOGENERATED DEPENDENCIES START
+
+init.o: $(RUBY_EXTCONF_H)
+init.o: $(arch_hdrdir)/ruby/config.h
+init.o: $(hdrdir)/ruby.h
+init.o: $(hdrdir)/ruby/assert.h
+init.o: $(hdrdir)/ruby/backward.h
+init.o: $(hdrdir)/ruby/backward/2/assume.h
+init.o: $(hdrdir)/ruby/backward/2/attributes.h
+init.o: $(hdrdir)/ruby/backward/2/bool.h
+init.o: $(hdrdir)/ruby/backward/2/inttypes.h
+init.o: $(hdrdir)/ruby/backward/2/limits.h
+init.o: $(hdrdir)/ruby/backward/2/long_long.h
+init.o: $(hdrdir)/ruby/backward/2/stdalign.h
+init.o: $(hdrdir)/ruby/backward/2/stdarg.h
+init.o: $(hdrdir)/ruby/defines.h
+init.o: $(hdrdir)/ruby/intern.h
+init.o: $(hdrdir)/ruby/internal/abi.h
+init.o: $(hdrdir)/ruby/internal/anyargs.h
+init.o: $(hdrdir)/ruby/internal/arithmetic.h
+init.o: $(hdrdir)/ruby/internal/arithmetic/char.h
+init.o: $(hdrdir)/ruby/internal/arithmetic/double.h
+init.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h
+init.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h
+init.o: $(hdrdir)/ruby/internal/arithmetic/int.h
+init.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h
+init.o: $(hdrdir)/ruby/internal/arithmetic/long.h
+init.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h
+init.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h
+init.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h
+init.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h
+init.o: $(hdrdir)/ruby/internal/arithmetic/short.h
+init.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h
+init.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h
+init.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h
+init.o: $(hdrdir)/ruby/internal/assume.h
+init.o: $(hdrdir)/ruby/internal/attr/alloc_size.h
+init.o: $(hdrdir)/ruby/internal/attr/artificial.h
+init.o: $(hdrdir)/ruby/internal/attr/cold.h
+init.o: $(hdrdir)/ruby/internal/attr/const.h
+init.o: $(hdrdir)/ruby/internal/attr/constexpr.h
+init.o: $(hdrdir)/ruby/internal/attr/deprecated.h
+init.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h
+init.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h
+init.o: $(hdrdir)/ruby/internal/attr/error.h
+init.o: $(hdrdir)/ruby/internal/attr/flag_enum.h
+init.o: $(hdrdir)/ruby/internal/attr/forceinline.h
+init.o: $(hdrdir)/ruby/internal/attr/format.h
+init.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h
+init.o: $(hdrdir)/ruby/internal/attr/noalias.h
+init.o: $(hdrdir)/ruby/internal/attr/nodiscard.h
+init.o: $(hdrdir)/ruby/internal/attr/noexcept.h
+init.o: $(hdrdir)/ruby/internal/attr/noinline.h
+init.o: $(hdrdir)/ruby/internal/attr/nonnull.h
+init.o: $(hdrdir)/ruby/internal/attr/noreturn.h
+init.o: $(hdrdir)/ruby/internal/attr/packed_struct.h
+init.o: $(hdrdir)/ruby/internal/attr/pure.h
+init.o: $(hdrdir)/ruby/internal/attr/restrict.h
+init.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h
+init.o: $(hdrdir)/ruby/internal/attr/warning.h
+init.o: $(hdrdir)/ruby/internal/attr/weakref.h
+init.o: $(hdrdir)/ruby/internal/cast.h
+init.o: $(hdrdir)/ruby/internal/compiler_is.h
+init.o: $(hdrdir)/ruby/internal/compiler_is/apple.h
+init.o: $(hdrdir)/ruby/internal/compiler_is/clang.h
+init.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h
+init.o: $(hdrdir)/ruby/internal/compiler_is/intel.h
+init.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h
+init.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h
+init.o: $(hdrdir)/ruby/internal/compiler_since.h
+init.o: $(hdrdir)/ruby/internal/config.h
+init.o: $(hdrdir)/ruby/internal/constant_p.h
+init.o: $(hdrdir)/ruby/internal/core.h
+init.o: $(hdrdir)/ruby/internal/core/rarray.h
+init.o: $(hdrdir)/ruby/internal/core/rbasic.h
+init.o: $(hdrdir)/ruby/internal/core/rbignum.h
+init.o: $(hdrdir)/ruby/internal/core/rclass.h
+init.o: $(hdrdir)/ruby/internal/core/rdata.h
+init.o: $(hdrdir)/ruby/internal/core/rfile.h
+init.o: $(hdrdir)/ruby/internal/core/rhash.h
+init.o: $(hdrdir)/ruby/internal/core/robject.h
+init.o: $(hdrdir)/ruby/internal/core/rregexp.h
+init.o: $(hdrdir)/ruby/internal/core/rstring.h
+init.o: $(hdrdir)/ruby/internal/core/rstruct.h
+init.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
+init.o: $(hdrdir)/ruby/internal/ctype.h
+init.o: $(hdrdir)/ruby/internal/dllexport.h
+init.o: $(hdrdir)/ruby/internal/dosish.h
+init.o: $(hdrdir)/ruby/internal/error.h
+init.o: $(hdrdir)/ruby/internal/eval.h
+init.o: $(hdrdir)/ruby/internal/event.h
+init.o: $(hdrdir)/ruby/internal/fl_type.h
+init.o: $(hdrdir)/ruby/internal/gc.h
+init.o: $(hdrdir)/ruby/internal/glob.h
+init.o: $(hdrdir)/ruby/internal/globals.h
+init.o: $(hdrdir)/ruby/internal/has/attribute.h
+init.o: $(hdrdir)/ruby/internal/has/builtin.h
+init.o: $(hdrdir)/ruby/internal/has/c_attribute.h
+init.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h
+init.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h
+init.o: $(hdrdir)/ruby/internal/has/extension.h
+init.o: $(hdrdir)/ruby/internal/has/feature.h
+init.o: $(hdrdir)/ruby/internal/has/warning.h
+init.o: $(hdrdir)/ruby/internal/intern/array.h
+init.o: $(hdrdir)/ruby/internal/intern/bignum.h
+init.o: $(hdrdir)/ruby/internal/intern/class.h
+init.o: $(hdrdir)/ruby/internal/intern/compar.h
+init.o: $(hdrdir)/ruby/internal/intern/complex.h
+init.o: $(hdrdir)/ruby/internal/intern/cont.h
+init.o: $(hdrdir)/ruby/internal/intern/dir.h
+init.o: $(hdrdir)/ruby/internal/intern/enum.h
+init.o: $(hdrdir)/ruby/internal/intern/enumerator.h
+init.o: $(hdrdir)/ruby/internal/intern/error.h
+init.o: $(hdrdir)/ruby/internal/intern/eval.h
+init.o: $(hdrdir)/ruby/internal/intern/file.h
+init.o: $(hdrdir)/ruby/internal/intern/hash.h
+init.o: $(hdrdir)/ruby/internal/intern/io.h
+init.o: $(hdrdir)/ruby/internal/intern/load.h
+init.o: $(hdrdir)/ruby/internal/intern/marshal.h
+init.o: $(hdrdir)/ruby/internal/intern/numeric.h
+init.o: $(hdrdir)/ruby/internal/intern/object.h
+init.o: $(hdrdir)/ruby/internal/intern/parse.h
+init.o: $(hdrdir)/ruby/internal/intern/proc.h
+init.o: $(hdrdir)/ruby/internal/intern/process.h
+init.o: $(hdrdir)/ruby/internal/intern/random.h
+init.o: $(hdrdir)/ruby/internal/intern/range.h
+init.o: $(hdrdir)/ruby/internal/intern/rational.h
+init.o: $(hdrdir)/ruby/internal/intern/re.h
+init.o: $(hdrdir)/ruby/internal/intern/ruby.h
+init.o: $(hdrdir)/ruby/internal/intern/select.h
+init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h
+init.o: $(hdrdir)/ruby/internal/intern/signal.h
+init.o: $(hdrdir)/ruby/internal/intern/sprintf.h
+init.o: $(hdrdir)/ruby/internal/intern/string.h
+init.o: $(hdrdir)/ruby/internal/intern/struct.h
+init.o: $(hdrdir)/ruby/internal/intern/thread.h
+init.o: $(hdrdir)/ruby/internal/intern/time.h
+init.o: $(hdrdir)/ruby/internal/intern/variable.h
+init.o: $(hdrdir)/ruby/internal/intern/vm.h
+init.o: $(hdrdir)/ruby/internal/interpreter.h
+init.o: $(hdrdir)/ruby/internal/iterator.h
+init.o: $(hdrdir)/ruby/internal/memory.h
+init.o: $(hdrdir)/ruby/internal/method.h
+init.o: $(hdrdir)/ruby/internal/module.h
+init.o: $(hdrdir)/ruby/internal/newobj.h
+init.o: $(hdrdir)/ruby/internal/scan_args.h
+init.o: $(hdrdir)/ruby/internal/special_consts.h
+init.o: $(hdrdir)/ruby/internal/static_assert.h
+init.o: $(hdrdir)/ruby/internal/stdalign.h
+init.o: $(hdrdir)/ruby/internal/stdbool.h
+init.o: $(hdrdir)/ruby/internal/stdckdint.h
+init.o: $(hdrdir)/ruby/internal/symbol.h
+init.o: $(hdrdir)/ruby/internal/value.h
+init.o: $(hdrdir)/ruby/internal/value_type.h
+init.o: $(hdrdir)/ruby/internal/variable.h
+init.o: $(hdrdir)/ruby/internal/warning_push.h
+init.o: $(hdrdir)/ruby/internal/xmalloc.h
+init.o: $(hdrdir)/ruby/missing.h
+init.o: $(hdrdir)/ruby/ruby.h
+init.o: $(hdrdir)/ruby/st.h
+init.o: $(hdrdir)/ruby/subst.h
+init.o: init.c
+invalid.o: $(RUBY_EXTCONF_H)
+invalid.o: $(arch_hdrdir)/ruby/config.h
+invalid.o: $(hdrdir)/ruby.h
+invalid.o: $(hdrdir)/ruby/assert.h
+invalid.o: $(hdrdir)/ruby/backward.h
+invalid.o: $(hdrdir)/ruby/backward/2/assume.h
+invalid.o: $(hdrdir)/ruby/backward/2/attributes.h
+invalid.o: $(hdrdir)/ruby/backward/2/bool.h
+invalid.o: $(hdrdir)/ruby/backward/2/inttypes.h
+invalid.o: $(hdrdir)/ruby/backward/2/limits.h
+invalid.o: $(hdrdir)/ruby/backward/2/long_long.h
+invalid.o: $(hdrdir)/ruby/backward/2/stdalign.h
+invalid.o: $(hdrdir)/ruby/backward/2/stdarg.h
+invalid.o: $(hdrdir)/ruby/defines.h
+invalid.o: $(hdrdir)/ruby/intern.h
+invalid.o: $(hdrdir)/ruby/internal/abi.h
+invalid.o: $(hdrdir)/ruby/internal/anyargs.h
+invalid.o: $(hdrdir)/ruby/internal/arithmetic.h
+invalid.o: $(hdrdir)/ruby/internal/arithmetic/char.h
+invalid.o: $(hdrdir)/ruby/internal/arithmetic/double.h
+invalid.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h
+invalid.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h
+invalid.o: $(hdrdir)/ruby/internal/arithmetic/int.h
+invalid.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h
+invalid.o: $(hdrdir)/ruby/internal/arithmetic/long.h
+invalid.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h
+invalid.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h
+invalid.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h
+invalid.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h
+invalid.o: $(hdrdir)/ruby/internal/arithmetic/short.h
+invalid.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h
+invalid.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h
+invalid.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h
+invalid.o: $(hdrdir)/ruby/internal/assume.h
+invalid.o: $(hdrdir)/ruby/internal/attr/alloc_size.h
+invalid.o: $(hdrdir)/ruby/internal/attr/artificial.h
+invalid.o: $(hdrdir)/ruby/internal/attr/cold.h
+invalid.o: $(hdrdir)/ruby/internal/attr/const.h
+invalid.o: $(hdrdir)/ruby/internal/attr/constexpr.h
+invalid.o: $(hdrdir)/ruby/internal/attr/deprecated.h
+invalid.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h
+invalid.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h
+invalid.o: $(hdrdir)/ruby/internal/attr/error.h
+invalid.o: $(hdrdir)/ruby/internal/attr/flag_enum.h
+invalid.o: $(hdrdir)/ruby/internal/attr/forceinline.h
+invalid.o: $(hdrdir)/ruby/internal/attr/format.h
+invalid.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h
+invalid.o: $(hdrdir)/ruby/internal/attr/noalias.h
+invalid.o: $(hdrdir)/ruby/internal/attr/nodiscard.h
+invalid.o: $(hdrdir)/ruby/internal/attr/noexcept.h
+invalid.o: $(hdrdir)/ruby/internal/attr/noinline.h
+invalid.o: $(hdrdir)/ruby/internal/attr/nonnull.h
+invalid.o: $(hdrdir)/ruby/internal/attr/noreturn.h
+invalid.o: $(hdrdir)/ruby/internal/attr/packed_struct.h
+invalid.o: $(hdrdir)/ruby/internal/attr/pure.h
+invalid.o: $(hdrdir)/ruby/internal/attr/restrict.h
+invalid.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h
+invalid.o: $(hdrdir)/ruby/internal/attr/warning.h
+invalid.o: $(hdrdir)/ruby/internal/attr/weakref.h
+invalid.o: $(hdrdir)/ruby/internal/cast.h
+invalid.o: $(hdrdir)/ruby/internal/compiler_is.h
+invalid.o: $(hdrdir)/ruby/internal/compiler_is/apple.h
+invalid.o: $(hdrdir)/ruby/internal/compiler_is/clang.h
+invalid.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h
+invalid.o: $(hdrdir)/ruby/internal/compiler_is/intel.h
+invalid.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h
+invalid.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h
+invalid.o: $(hdrdir)/ruby/internal/compiler_since.h
+invalid.o: $(hdrdir)/ruby/internal/config.h
+invalid.o: $(hdrdir)/ruby/internal/constant_p.h
+invalid.o: $(hdrdir)/ruby/internal/core.h
+invalid.o: $(hdrdir)/ruby/internal/core/rarray.h
+invalid.o: $(hdrdir)/ruby/internal/core/rbasic.h
+invalid.o: $(hdrdir)/ruby/internal/core/rbignum.h
+invalid.o: $(hdrdir)/ruby/internal/core/rclass.h
+invalid.o: $(hdrdir)/ruby/internal/core/rdata.h
+invalid.o: $(hdrdir)/ruby/internal/core/rfile.h
+invalid.o: $(hdrdir)/ruby/internal/core/rhash.h
+invalid.o: $(hdrdir)/ruby/internal/core/robject.h
+invalid.o: $(hdrdir)/ruby/internal/core/rregexp.h
+invalid.o: $(hdrdir)/ruby/internal/core/rstring.h
+invalid.o: $(hdrdir)/ruby/internal/core/rstruct.h
+invalid.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
+invalid.o: $(hdrdir)/ruby/internal/ctype.h
+invalid.o: $(hdrdir)/ruby/internal/dllexport.h
+invalid.o: $(hdrdir)/ruby/internal/dosish.h
+invalid.o: $(hdrdir)/ruby/internal/error.h
+invalid.o: $(hdrdir)/ruby/internal/eval.h
+invalid.o: $(hdrdir)/ruby/internal/event.h
+invalid.o: $(hdrdir)/ruby/internal/fl_type.h
+invalid.o: $(hdrdir)/ruby/internal/gc.h
+invalid.o: $(hdrdir)/ruby/internal/glob.h
+invalid.o: $(hdrdir)/ruby/internal/globals.h
+invalid.o: $(hdrdir)/ruby/internal/has/attribute.h
+invalid.o: $(hdrdir)/ruby/internal/has/builtin.h
+invalid.o: $(hdrdir)/ruby/internal/has/c_attribute.h
+invalid.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h
+invalid.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h
+invalid.o: $(hdrdir)/ruby/internal/has/extension.h
+invalid.o: $(hdrdir)/ruby/internal/has/feature.h
+invalid.o: $(hdrdir)/ruby/internal/has/warning.h
+invalid.o: $(hdrdir)/ruby/internal/intern/array.h
+invalid.o: $(hdrdir)/ruby/internal/intern/bignum.h
+invalid.o: $(hdrdir)/ruby/internal/intern/class.h
+invalid.o: $(hdrdir)/ruby/internal/intern/compar.h
+invalid.o: $(hdrdir)/ruby/internal/intern/complex.h
+invalid.o: $(hdrdir)/ruby/internal/intern/cont.h
+invalid.o: $(hdrdir)/ruby/internal/intern/dir.h
+invalid.o: $(hdrdir)/ruby/internal/intern/enum.h
+invalid.o: $(hdrdir)/ruby/internal/intern/enumerator.h
+invalid.o: $(hdrdir)/ruby/internal/intern/error.h
+invalid.o: $(hdrdir)/ruby/internal/intern/eval.h
+invalid.o: $(hdrdir)/ruby/internal/intern/file.h
+invalid.o: $(hdrdir)/ruby/internal/intern/hash.h
+invalid.o: $(hdrdir)/ruby/internal/intern/io.h
+invalid.o: $(hdrdir)/ruby/internal/intern/load.h
+invalid.o: $(hdrdir)/ruby/internal/intern/marshal.h
+invalid.o: $(hdrdir)/ruby/internal/intern/numeric.h
+invalid.o: $(hdrdir)/ruby/internal/intern/object.h
+invalid.o: $(hdrdir)/ruby/internal/intern/parse.h
+invalid.o: $(hdrdir)/ruby/internal/intern/proc.h
+invalid.o: $(hdrdir)/ruby/internal/intern/process.h
+invalid.o: $(hdrdir)/ruby/internal/intern/random.h
+invalid.o: $(hdrdir)/ruby/internal/intern/range.h
+invalid.o: $(hdrdir)/ruby/internal/intern/rational.h
+invalid.o: $(hdrdir)/ruby/internal/intern/re.h
+invalid.o: $(hdrdir)/ruby/internal/intern/ruby.h
+invalid.o: $(hdrdir)/ruby/internal/intern/select.h
+invalid.o: $(hdrdir)/ruby/internal/intern/select/largesize.h
+invalid.o: $(hdrdir)/ruby/internal/intern/signal.h
+invalid.o: $(hdrdir)/ruby/internal/intern/sprintf.h
+invalid.o: $(hdrdir)/ruby/internal/intern/string.h
+invalid.o: $(hdrdir)/ruby/internal/intern/struct.h
+invalid.o: $(hdrdir)/ruby/internal/intern/thread.h
+invalid.o: $(hdrdir)/ruby/internal/intern/time.h
+invalid.o: $(hdrdir)/ruby/internal/intern/variable.h
+invalid.o: $(hdrdir)/ruby/internal/intern/vm.h
+invalid.o: $(hdrdir)/ruby/internal/interpreter.h
+invalid.o: $(hdrdir)/ruby/internal/iterator.h
+invalid.o: $(hdrdir)/ruby/internal/memory.h
+invalid.o: $(hdrdir)/ruby/internal/method.h
+invalid.o: $(hdrdir)/ruby/internal/module.h
+invalid.o: $(hdrdir)/ruby/internal/newobj.h
+invalid.o: $(hdrdir)/ruby/internal/scan_args.h
+invalid.o: $(hdrdir)/ruby/internal/special_consts.h
+invalid.o: $(hdrdir)/ruby/internal/static_assert.h
+invalid.o: $(hdrdir)/ruby/internal/stdalign.h
+invalid.o: $(hdrdir)/ruby/internal/stdbool.h
+invalid.o: $(hdrdir)/ruby/internal/stdckdint.h
+invalid.o: $(hdrdir)/ruby/internal/symbol.h
+invalid.o: $(hdrdir)/ruby/internal/value.h
+invalid.o: $(hdrdir)/ruby/internal/value_type.h
+invalid.o: $(hdrdir)/ruby/internal/variable.h
+invalid.o: $(hdrdir)/ruby/internal/warning_push.h
+invalid.o: $(hdrdir)/ruby/internal/xmalloc.h
+invalid.o: $(hdrdir)/ruby/missing.h
+invalid.o: $(hdrdir)/ruby/ruby.h
+invalid.o: $(hdrdir)/ruby/st.h
+invalid.o: $(hdrdir)/ruby/subst.h
+invalid.o: invalid.c
rb_fatal.o: $(RUBY_EXTCONF_H)
rb_fatal.o: $(arch_hdrdir)/ruby/config.h
rb_fatal.o: $(hdrdir)/ruby.h
@@ -147,6 +468,7 @@ rb_fatal.o: $(hdrdir)/ruby/internal/special_consts.h
rb_fatal.o: $(hdrdir)/ruby/internal/static_assert.h
rb_fatal.o: $(hdrdir)/ruby/internal/stdalign.h
rb_fatal.o: $(hdrdir)/ruby/internal/stdbool.h
+rb_fatal.o: $(hdrdir)/ruby/internal/stdckdint.h
rb_fatal.o: $(hdrdir)/ruby/internal/symbol.h
rb_fatal.o: $(hdrdir)/ruby/internal/value.h
rb_fatal.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/fatal/extconf.rb b/ext/-test-/fatal/extconf.rb
index d5849c0733..ca51178a18 100644
--- a/ext/-test-/fatal/extconf.rb
+++ b/ext/-test-/fatal/extconf.rb
@@ -1,2 +1,3 @@
# frozen_string_literal: false
-create_makefile("-test-/fatal/rb_fatal")
+require_relative "../auto_ext.rb"
+auto_ext
diff --git a/ext/-test-/fatal/init.c b/ext/-test-/fatal/init.c
new file mode 100644
index 0000000000..3b71708789
--- /dev/null
+++ b/ext/-test-/fatal/init.c
@@ -0,0 +1,10 @@
+#include "ruby.h"
+
+#define init(n) {void Init_##n(VALUE klass); Init_##n(klass);}
+
+void
+Init_fatal(void)
+{
+ VALUE klass = rb_define_module("Bug");
+ TEST_INIT_FUNCS(init);
+}
diff --git a/ext/-test-/fatal/invalid.c b/ext/-test-/fatal/invalid.c
new file mode 100644
index 0000000000..f0726edb52
--- /dev/null
+++ b/ext/-test-/fatal/invalid.c
@@ -0,0 +1,28 @@
+#include <ruby.h>
+
+#if SIZEOF_LONG == SIZEOF_VOIDP
+# define NUM2PTR(x) (void *)NUM2ULONG(x)
+#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
+# define NUM2PTR(x) (void *)NUM2ULL(x)
+#endif
+
+static VALUE
+invalid_call(VALUE obj, VALUE address)
+{
+ typedef VALUE (*func_type)(VALUE);
+
+ return (*(func_type)NUM2PTR(address))(obj);
+}
+
+static VALUE
+invalid_access(VALUE obj, VALUE address)
+{
+ return *(VALUE *)NUM2PTR(address) == obj ? Qtrue : Qfalse;
+}
+
+void
+Init_invalid(VALUE mBug)
+{
+ rb_define_singleton_method(mBug, "invalid_call", invalid_call, 1);
+ rb_define_singleton_method(mBug, "invalid_access", invalid_access, 1);
+}
diff --git a/ext/-test-/fatal/rb_fatal.c b/ext/-test-/fatal/rb_fatal.c
index eedbc51f8b..6c7bb89628 100644
--- a/ext/-test-/fatal/rb_fatal.c
+++ b/ext/-test-/fatal/rb_fatal.c
@@ -13,8 +13,7 @@ ruby_fatal(VALUE obj, VALUE msg)
}
void
-Init_rb_fatal(void)
+Init_rb_fatal(VALUE mBug)
{
- VALUE mBug = rb_define_module("Bug");
rb_define_singleton_method(mBug, "rb_fatal", ruby_fatal, 1);
}
diff --git a/ext/-test-/file/depend b/ext/-test-/file/depend
index 662136f1ba..e985f914b2 100644
--- a/ext/-test-/file/depend
+++ b/ext/-test-/file/depend
@@ -156,6 +156,7 @@ fs.o: $(hdrdir)/ruby/internal/special_consts.h
fs.o: $(hdrdir)/ruby/internal/static_assert.h
fs.o: $(hdrdir)/ruby/internal/stdalign.h
fs.o: $(hdrdir)/ruby/internal/stdbool.h
+fs.o: $(hdrdir)/ruby/internal/stdckdint.h
fs.o: $(hdrdir)/ruby/internal/symbol.h
fs.o: $(hdrdir)/ruby/internal/value.h
fs.o: $(hdrdir)/ruby/internal/value_type.h
@@ -318,6 +319,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h
init.o: $(hdrdir)/ruby/internal/static_assert.h
init.o: $(hdrdir)/ruby/internal/stdalign.h
init.o: $(hdrdir)/ruby/internal/stdbool.h
+init.o: $(hdrdir)/ruby/internal/stdckdint.h
init.o: $(hdrdir)/ruby/internal/symbol.h
init.o: $(hdrdir)/ruby/internal/value.h
init.o: $(hdrdir)/ruby/internal/value_type.h
@@ -329,6 +331,178 @@ init.o: $(hdrdir)/ruby/ruby.h
init.o: $(hdrdir)/ruby/st.h
init.o: $(hdrdir)/ruby/subst.h
init.o: init.c
+newline_conv.o: $(RUBY_EXTCONF_H)
+newline_conv.o: $(arch_hdrdir)/ruby/config.h
+newline_conv.o: $(hdrdir)/ruby/assert.h
+newline_conv.o: $(hdrdir)/ruby/backward.h
+newline_conv.o: $(hdrdir)/ruby/backward/2/assume.h
+newline_conv.o: $(hdrdir)/ruby/backward/2/attributes.h
+newline_conv.o: $(hdrdir)/ruby/backward/2/bool.h
+newline_conv.o: $(hdrdir)/ruby/backward/2/inttypes.h
+newline_conv.o: $(hdrdir)/ruby/backward/2/limits.h
+newline_conv.o: $(hdrdir)/ruby/backward/2/long_long.h
+newline_conv.o: $(hdrdir)/ruby/backward/2/stdalign.h
+newline_conv.o: $(hdrdir)/ruby/backward/2/stdarg.h
+newline_conv.o: $(hdrdir)/ruby/defines.h
+newline_conv.o: $(hdrdir)/ruby/encoding.h
+newline_conv.o: $(hdrdir)/ruby/intern.h
+newline_conv.o: $(hdrdir)/ruby/internal/abi.h
+newline_conv.o: $(hdrdir)/ruby/internal/anyargs.h
+newline_conv.o: $(hdrdir)/ruby/internal/arithmetic.h
+newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/char.h
+newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/double.h
+newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h
+newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h
+newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/int.h
+newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h
+newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/long.h
+newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h
+newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h
+newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h
+newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h
+newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/short.h
+newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h
+newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h
+newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h
+newline_conv.o: $(hdrdir)/ruby/internal/assume.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/alloc_size.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/artificial.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/cold.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/const.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/constexpr.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/deprecated.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/error.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/flag_enum.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/forceinline.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/format.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/noalias.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/nodiscard.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/noexcept.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/noinline.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/nonnull.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/noreturn.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/packed_struct.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/pure.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/restrict.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/warning.h
+newline_conv.o: $(hdrdir)/ruby/internal/attr/weakref.h
+newline_conv.o: $(hdrdir)/ruby/internal/cast.h
+newline_conv.o: $(hdrdir)/ruby/internal/compiler_is.h
+newline_conv.o: $(hdrdir)/ruby/internal/compiler_is/apple.h
+newline_conv.o: $(hdrdir)/ruby/internal/compiler_is/clang.h
+newline_conv.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h
+newline_conv.o: $(hdrdir)/ruby/internal/compiler_is/intel.h
+newline_conv.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h
+newline_conv.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h
+newline_conv.o: $(hdrdir)/ruby/internal/compiler_since.h
+newline_conv.o: $(hdrdir)/ruby/internal/config.h
+newline_conv.o: $(hdrdir)/ruby/internal/constant_p.h
+newline_conv.o: $(hdrdir)/ruby/internal/core.h
+newline_conv.o: $(hdrdir)/ruby/internal/core/rarray.h
+newline_conv.o: $(hdrdir)/ruby/internal/core/rbasic.h
+newline_conv.o: $(hdrdir)/ruby/internal/core/rbignum.h
+newline_conv.o: $(hdrdir)/ruby/internal/core/rclass.h
+newline_conv.o: $(hdrdir)/ruby/internal/core/rdata.h
+newline_conv.o: $(hdrdir)/ruby/internal/core/rfile.h
+newline_conv.o: $(hdrdir)/ruby/internal/core/rhash.h
+newline_conv.o: $(hdrdir)/ruby/internal/core/robject.h
+newline_conv.o: $(hdrdir)/ruby/internal/core/rregexp.h
+newline_conv.o: $(hdrdir)/ruby/internal/core/rstring.h
+newline_conv.o: $(hdrdir)/ruby/internal/core/rstruct.h
+newline_conv.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
+newline_conv.o: $(hdrdir)/ruby/internal/ctype.h
+newline_conv.o: $(hdrdir)/ruby/internal/dllexport.h
+newline_conv.o: $(hdrdir)/ruby/internal/dosish.h
+newline_conv.o: $(hdrdir)/ruby/internal/encoding/coderange.h
+newline_conv.o: $(hdrdir)/ruby/internal/encoding/ctype.h
+newline_conv.o: $(hdrdir)/ruby/internal/encoding/encoding.h
+newline_conv.o: $(hdrdir)/ruby/internal/encoding/pathname.h
+newline_conv.o: $(hdrdir)/ruby/internal/encoding/re.h
+newline_conv.o: $(hdrdir)/ruby/internal/encoding/sprintf.h
+newline_conv.o: $(hdrdir)/ruby/internal/encoding/string.h
+newline_conv.o: $(hdrdir)/ruby/internal/encoding/symbol.h
+newline_conv.o: $(hdrdir)/ruby/internal/encoding/transcode.h
+newline_conv.o: $(hdrdir)/ruby/internal/error.h
+newline_conv.o: $(hdrdir)/ruby/internal/eval.h
+newline_conv.o: $(hdrdir)/ruby/internal/event.h
+newline_conv.o: $(hdrdir)/ruby/internal/fl_type.h
+newline_conv.o: $(hdrdir)/ruby/internal/gc.h
+newline_conv.o: $(hdrdir)/ruby/internal/glob.h
+newline_conv.o: $(hdrdir)/ruby/internal/globals.h
+newline_conv.o: $(hdrdir)/ruby/internal/has/attribute.h
+newline_conv.o: $(hdrdir)/ruby/internal/has/builtin.h
+newline_conv.o: $(hdrdir)/ruby/internal/has/c_attribute.h
+newline_conv.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h
+newline_conv.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h
+newline_conv.o: $(hdrdir)/ruby/internal/has/extension.h
+newline_conv.o: $(hdrdir)/ruby/internal/has/feature.h
+newline_conv.o: $(hdrdir)/ruby/internal/has/warning.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/array.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/bignum.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/class.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/compar.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/complex.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/cont.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/dir.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/enum.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/enumerator.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/error.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/eval.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/file.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/hash.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/io.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/load.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/marshal.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/numeric.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/object.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/parse.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/proc.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/process.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/random.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/range.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/rational.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/re.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/ruby.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/select.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/select/largesize.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/signal.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/sprintf.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/string.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/struct.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/thread.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/time.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/variable.h
+newline_conv.o: $(hdrdir)/ruby/internal/intern/vm.h
+newline_conv.o: $(hdrdir)/ruby/internal/interpreter.h
+newline_conv.o: $(hdrdir)/ruby/internal/iterator.h
+newline_conv.o: $(hdrdir)/ruby/internal/memory.h
+newline_conv.o: $(hdrdir)/ruby/internal/method.h
+newline_conv.o: $(hdrdir)/ruby/internal/module.h
+newline_conv.o: $(hdrdir)/ruby/internal/newobj.h
+newline_conv.o: $(hdrdir)/ruby/internal/scan_args.h
+newline_conv.o: $(hdrdir)/ruby/internal/special_consts.h
+newline_conv.o: $(hdrdir)/ruby/internal/static_assert.h
+newline_conv.o: $(hdrdir)/ruby/internal/stdalign.h
+newline_conv.o: $(hdrdir)/ruby/internal/stdbool.h
+newline_conv.o: $(hdrdir)/ruby/internal/stdckdint.h
+newline_conv.o: $(hdrdir)/ruby/internal/symbol.h
+newline_conv.o: $(hdrdir)/ruby/internal/value.h
+newline_conv.o: $(hdrdir)/ruby/internal/value_type.h
+newline_conv.o: $(hdrdir)/ruby/internal/variable.h
+newline_conv.o: $(hdrdir)/ruby/internal/warning_push.h
+newline_conv.o: $(hdrdir)/ruby/internal/xmalloc.h
+newline_conv.o: $(hdrdir)/ruby/io.h
+newline_conv.o: $(hdrdir)/ruby/missing.h
+newline_conv.o: $(hdrdir)/ruby/onigmo.h
+newline_conv.o: $(hdrdir)/ruby/oniguruma.h
+newline_conv.o: $(hdrdir)/ruby/ruby.h
+newline_conv.o: $(hdrdir)/ruby/st.h
+newline_conv.o: $(hdrdir)/ruby/subst.h
+newline_conv.o: newline_conv.c
stat.o: $(RUBY_EXTCONF_H)
stat.o: $(arch_hdrdir)/ruby/config.h
stat.o: $(hdrdir)/ruby/assert.h
@@ -486,6 +660,7 @@ stat.o: $(hdrdir)/ruby/internal/special_consts.h
stat.o: $(hdrdir)/ruby/internal/static_assert.h
stat.o: $(hdrdir)/ruby/internal/stdalign.h
stat.o: $(hdrdir)/ruby/internal/stdbool.h
+stat.o: $(hdrdir)/ruby/internal/stdckdint.h
stat.o: $(hdrdir)/ruby/internal/symbol.h
stat.o: $(hdrdir)/ruby/internal/value.h
stat.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/file/newline_conv.c b/ext/-test-/file/newline_conv.c
new file mode 100644
index 0000000000..2ac5aef801
--- /dev/null
+++ b/ext/-test-/file/newline_conv.c
@@ -0,0 +1,73 @@
+#include "ruby/ruby.h"
+#include "ruby/io.h"
+#include <fcntl.h>
+
+static VALUE
+open_with_rb_file_open(VALUE self, VALUE filename, VALUE read_or_write, VALUE binary_or_text)
+{
+ char fmode[3] = { 0 };
+ if (rb_sym2id(read_or_write) == rb_intern("read")) {
+ fmode[0] = 'r';
+ }
+ else if (rb_sym2id(read_or_write) == rb_intern("write")) {
+ fmode[0] = 'w';
+ }
+ else {
+ rb_raise(rb_eArgError, "read_or_write param must be :read or :write");
+ }
+
+ if (rb_sym2id(binary_or_text) == rb_intern("binary")) {
+ fmode[1] = 'b';
+ }
+ else if (rb_sym2id(binary_or_text) == rb_intern("text")) {
+
+ }
+ else {
+ rb_raise(rb_eArgError, "binary_or_text param must be :binary or :text");
+ }
+
+ return rb_file_open(StringValueCStr(filename), fmode);
+}
+
+static VALUE
+open_with_rb_io_fdopen(VALUE self, VALUE filename, VALUE read_or_write, VALUE binary_or_text)
+{
+ int omode = 0;
+ if (rb_sym2id(read_or_write) == rb_intern("read")) {
+ omode |= O_RDONLY;
+ }
+ else if (rb_sym2id(read_or_write) == rb_intern("write")) {
+ omode |= O_WRONLY;
+ }
+ else {
+ rb_raise(rb_eArgError, "read_or_write param must be :read or :write");
+ }
+
+ if (rb_sym2id(binary_or_text) == rb_intern("binary")) {
+#ifdef O_BINARY
+ omode |= O_BINARY;
+#endif
+ }
+ else if (rb_sym2id(binary_or_text) == rb_intern("text")) {
+
+ }
+ else {
+ rb_raise(rb_eArgError, "binary_or_text param must be :binary or :text");
+ }
+
+ int fd = rb_cloexec_open(StringValueCStr(filename), omode, 0);
+ if (fd < 0) {
+ rb_raise(rb_eIOError, "failed to open the file");
+ }
+
+ rb_update_max_fd(fd);
+ return rb_io_fdopen(fd, omode, StringValueCStr(filename));
+}
+
+void
+Init_newline_conv(VALUE module)
+{
+ VALUE newline_conv = rb_define_module_under(module, "NewlineConv");
+ rb_define_module_function(newline_conv, "rb_file_open", open_with_rb_file_open, 3);
+ rb_define_module_function(newline_conv, "rb_io_fdopen", open_with_rb_io_fdopen, 3);
+}
diff --git a/ext/-test-/float/depend b/ext/-test-/float/depend
index 9580a0416c..3e34818d5f 100644
--- a/ext/-test-/float/depend
+++ b/ext/-test-/float/depend
@@ -150,6 +150,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h
init.o: $(hdrdir)/ruby/internal/static_assert.h
init.o: $(hdrdir)/ruby/internal/stdalign.h
init.o: $(hdrdir)/ruby/internal/stdbool.h
+init.o: $(hdrdir)/ruby/internal/stdckdint.h
init.o: $(hdrdir)/ruby/internal/symbol.h
init.o: $(hdrdir)/ruby/internal/value.h
init.o: $(hdrdir)/ruby/internal/value_type.h
@@ -309,6 +310,7 @@ nextafter.o: $(hdrdir)/ruby/internal/special_consts.h
nextafter.o: $(hdrdir)/ruby/internal/static_assert.h
nextafter.o: $(hdrdir)/ruby/internal/stdalign.h
nextafter.o: $(hdrdir)/ruby/internal/stdbool.h
+nextafter.o: $(hdrdir)/ruby/internal/stdckdint.h
nextafter.o: $(hdrdir)/ruby/internal/symbol.h
nextafter.o: $(hdrdir)/ruby/internal/value.h
nextafter.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/funcall/depend b/ext/-test-/funcall/depend
index 6719e4e676..a5a30873ac 100644
--- a/ext/-test-/funcall/depend
+++ b/ext/-test-/funcall/depend
@@ -147,6 +147,7 @@ funcall.o: $(hdrdir)/ruby/internal/special_consts.h
funcall.o: $(hdrdir)/ruby/internal/static_assert.h
funcall.o: $(hdrdir)/ruby/internal/stdalign.h
funcall.o: $(hdrdir)/ruby/internal/stdbool.h
+funcall.o: $(hdrdir)/ruby/internal/stdckdint.h
funcall.o: $(hdrdir)/ruby/internal/symbol.h
funcall.o: $(hdrdir)/ruby/internal/value.h
funcall.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/gvl/call_without_gvl/depend b/ext/-test-/gvl/call_without_gvl/depend
index a4987a65ca..6463d4527d 100644
--- a/ext/-test-/gvl/call_without_gvl/depend
+++ b/ext/-test-/gvl/call_without_gvl/depend
@@ -146,6 +146,7 @@ call_without_gvl.o: $(hdrdir)/ruby/internal/special_consts.h
call_without_gvl.o: $(hdrdir)/ruby/internal/static_assert.h
call_without_gvl.o: $(hdrdir)/ruby/internal/stdalign.h
call_without_gvl.o: $(hdrdir)/ruby/internal/stdbool.h
+call_without_gvl.o: $(hdrdir)/ruby/internal/stdckdint.h
call_without_gvl.o: $(hdrdir)/ruby/internal/symbol.h
call_without_gvl.o: $(hdrdir)/ruby/internal/value.h
call_without_gvl.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/hash/depend b/ext/-test-/hash/depend
index 58d9a6247e..4bc4bfcdd6 100644
--- a/ext/-test-/hash/depend
+++ b/ext/-test-/hash/depend
@@ -147,6 +147,7 @@ delete.o: $(hdrdir)/ruby/internal/special_consts.h
delete.o: $(hdrdir)/ruby/internal/static_assert.h
delete.o: $(hdrdir)/ruby/internal/stdalign.h
delete.o: $(hdrdir)/ruby/internal/stdbool.h
+delete.o: $(hdrdir)/ruby/internal/stdckdint.h
delete.o: $(hdrdir)/ruby/internal/symbol.h
delete.o: $(hdrdir)/ruby/internal/value.h
delete.o: $(hdrdir)/ruby/internal/value_type.h
@@ -306,6 +307,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h
init.o: $(hdrdir)/ruby/internal/static_assert.h
init.o: $(hdrdir)/ruby/internal/stdalign.h
init.o: $(hdrdir)/ruby/internal/stdbool.h
+init.o: $(hdrdir)/ruby/internal/stdckdint.h
init.o: $(hdrdir)/ruby/internal/symbol.h
init.o: $(hdrdir)/ruby/internal/value.h
init.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/integer/depend b/ext/-test-/integer/depend
index b68a68ce73..d89965e3d9 100644
--- a/ext/-test-/integer/depend
+++ b/ext/-test-/integer/depend
@@ -147,6 +147,7 @@ core_ext.o: $(hdrdir)/ruby/internal/special_consts.h
core_ext.o: $(hdrdir)/ruby/internal/static_assert.h
core_ext.o: $(hdrdir)/ruby/internal/stdalign.h
core_ext.o: $(hdrdir)/ruby/internal/stdbool.h
+core_ext.o: $(hdrdir)/ruby/internal/stdckdint.h
core_ext.o: $(hdrdir)/ruby/internal/symbol.h
core_ext.o: $(hdrdir)/ruby/internal/value.h
core_ext.o: $(hdrdir)/ruby/internal/value_type.h
@@ -314,6 +315,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h
init.o: $(hdrdir)/ruby/internal/static_assert.h
init.o: $(hdrdir)/ruby/internal/stdalign.h
init.o: $(hdrdir)/ruby/internal/stdbool.h
+init.o: $(hdrdir)/ruby/internal/stdckdint.h
init.o: $(hdrdir)/ruby/internal/symbol.h
init.o: $(hdrdir)/ruby/internal/value.h
init.o: $(hdrdir)/ruby/internal/value_type.h
@@ -473,6 +475,7 @@ my_integer.o: $(hdrdir)/ruby/internal/special_consts.h
my_integer.o: $(hdrdir)/ruby/internal/static_assert.h
my_integer.o: $(hdrdir)/ruby/internal/stdalign.h
my_integer.o: $(hdrdir)/ruby/internal/stdbool.h
+my_integer.o: $(hdrdir)/ruby/internal/stdckdint.h
my_integer.o: $(hdrdir)/ruby/internal/symbol.h
my_integer.o: $(hdrdir)/ruby/internal/value.h
my_integer.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/iseq_load/depend b/ext/-test-/iseq_load/depend
index 30deb6e039..fd07b3199c 100644
--- a/ext/-test-/iseq_load/depend
+++ b/ext/-test-/iseq_load/depend
@@ -147,6 +147,7 @@ iseq_load.o: $(hdrdir)/ruby/internal/special_consts.h
iseq_load.o: $(hdrdir)/ruby/internal/static_assert.h
iseq_load.o: $(hdrdir)/ruby/internal/stdalign.h
iseq_load.o: $(hdrdir)/ruby/internal/stdbool.h
+iseq_load.o: $(hdrdir)/ruby/internal/stdckdint.h
iseq_load.o: $(hdrdir)/ruby/internal/symbol.h
iseq_load.o: $(hdrdir)/ruby/internal/value.h
iseq_load.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/iter/depend b/ext/-test-/iter/depend
index 077a283532..cff4b1bb43 100644
--- a/ext/-test-/iter/depend
+++ b/ext/-test-/iter/depend
@@ -147,6 +147,7 @@ break.o: $(hdrdir)/ruby/internal/special_consts.h
break.o: $(hdrdir)/ruby/internal/static_assert.h
break.o: $(hdrdir)/ruby/internal/stdalign.h
break.o: $(hdrdir)/ruby/internal/stdbool.h
+break.o: $(hdrdir)/ruby/internal/stdckdint.h
break.o: $(hdrdir)/ruby/internal/symbol.h
break.o: $(hdrdir)/ruby/internal/value.h
break.o: $(hdrdir)/ruby/internal/value_type.h
@@ -306,6 +307,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h
init.o: $(hdrdir)/ruby/internal/static_assert.h
init.o: $(hdrdir)/ruby/internal/stdalign.h
init.o: $(hdrdir)/ruby/internal/stdbool.h
+init.o: $(hdrdir)/ruby/internal/stdckdint.h
init.o: $(hdrdir)/ruby/internal/symbol.h
init.o: $(hdrdir)/ruby/internal/value.h
init.o: $(hdrdir)/ruby/internal/value_type.h
@@ -465,6 +467,7 @@ yield.o: $(hdrdir)/ruby/internal/special_consts.h
yield.o: $(hdrdir)/ruby/internal/static_assert.h
yield.o: $(hdrdir)/ruby/internal/stdalign.h
yield.o: $(hdrdir)/ruby/internal/stdbool.h
+yield.o: $(hdrdir)/ruby/internal/stdckdint.h
yield.o: $(hdrdir)/ruby/internal/symbol.h
yield.o: $(hdrdir)/ruby/internal/value.h
yield.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/load/dot.dot/depend b/ext/-test-/load/dot.dot/depend
index be835d3ea5..f9be79e957 100644
--- a/ext/-test-/load/dot.dot/depend
+++ b/ext/-test-/load/dot.dot/depend
@@ -147,6 +147,7 @@ dot.dot.o: $(hdrdir)/ruby/internal/special_consts.h
dot.dot.o: $(hdrdir)/ruby/internal/static_assert.h
dot.dot.o: $(hdrdir)/ruby/internal/stdalign.h
dot.dot.o: $(hdrdir)/ruby/internal/stdbool.h
+dot.dot.o: $(hdrdir)/ruby/internal/stdckdint.h
dot.dot.o: $(hdrdir)/ruby/internal/symbol.h
dot.dot.o: $(hdrdir)/ruby/internal/value.h
dot.dot.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/load/protect/depend b/ext/-test-/load/protect/depend
index 57cf029901..324c17237a 100644
--- a/ext/-test-/load/protect/depend
+++ b/ext/-test-/load/protect/depend
@@ -147,6 +147,7 @@ protect.o: $(hdrdir)/ruby/internal/special_consts.h
protect.o: $(hdrdir)/ruby/internal/static_assert.h
protect.o: $(hdrdir)/ruby/internal/stdalign.h
protect.o: $(hdrdir)/ruby/internal/stdbool.h
+protect.o: $(hdrdir)/ruby/internal/stdckdint.h
protect.o: $(hdrdir)/ruby/internal/symbol.h
protect.o: $(hdrdir)/ruby/internal/value.h
protect.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/load/resolve_symbol_resolver/extconf.rb b/ext/-test-/load/resolve_symbol_resolver/extconf.rb
new file mode 100644
index 0000000000..2299efcfd3
--- /dev/null
+++ b/ext/-test-/load/resolve_symbol_resolver/extconf.rb
@@ -0,0 +1 @@
+create_makefile('-test-/load/resolve_symbol_resolver')
diff --git a/ext/-test-/load/resolve_symbol_resolver/resolve_symbol_resolver.c b/ext/-test-/load/resolve_symbol_resolver/resolve_symbol_resolver.c
new file mode 100644
index 0000000000..a856319cfb
--- /dev/null
+++ b/ext/-test-/load/resolve_symbol_resolver/resolve_symbol_resolver.c
@@ -0,0 +1,55 @@
+#include <ruby.h>
+#include "ruby/internal/intern/load.h"
+
+typedef VALUE(*target_func)(VALUE);
+
+static target_func rst_any_method;
+
+VALUE
+rsr_any_method(VALUE klass)
+{
+ return rst_any_method((VALUE)NULL);
+}
+
+VALUE
+rsr_try_resolve_fname(VALUE klass)
+{
+ target_func rst_something_missing =
+ (target_func) rb_ext_resolve_symbol("-test-/load/resolve_symbol_missing", "rst_any_method");
+ if (rst_something_missing == NULL) {
+ // This should be done in Init_*, so the error is LoadError
+ rb_raise(rb_eLoadError, "symbol not found: missing fname");
+ }
+ return Qtrue;
+}
+
+VALUE
+rsr_try_resolve_sname(VALUE klass)
+{
+ target_func rst_something_missing =
+ (target_func)rb_ext_resolve_symbol("-test-/load/resolve_symbol_target", "rst_something_missing");
+ if (rst_something_missing == NULL) {
+ // This should be done in Init_*, so the error is LoadError
+ rb_raise(rb_eLoadError, "symbol not found: missing sname");
+ }
+ return Qtrue;
+}
+
+void
+Init_resolve_symbol_resolver(void)
+{
+ /*
+ * Resolving symbols at the head of Init_ because it raises LoadError (in cases).
+ * If the module and methods are defined before raising LoadError, retrying `require "this.so"` will
+ * cause re-defining those methods (and will be warned).
+ */
+ rst_any_method = (target_func)rb_ext_resolve_symbol("-test-/load/resolve_symbol_target", "rst_any_method");
+ if (rst_any_method == NULL) {
+ rb_raise(rb_eLoadError, "resolve_symbol_target is not loaded");
+ }
+
+ VALUE mod = rb_define_module("ResolveSymbolResolver");
+ rb_define_singleton_method(mod, "any_method", rsr_any_method, 0);
+ rb_define_singleton_method(mod, "try_resolve_fname", rsr_try_resolve_fname, 0);
+ rb_define_singleton_method(mod, "try_resolve_sname", rsr_try_resolve_sname, 0);
+}
diff --git a/ext/-test-/load/resolve_symbol_target/extconf.rb b/ext/-test-/load/resolve_symbol_target/extconf.rb
new file mode 100644
index 0000000000..b5a99ca7f1
--- /dev/null
+++ b/ext/-test-/load/resolve_symbol_target/extconf.rb
@@ -0,0 +1 @@
+create_makefile('-test-/load/resolve_symbol_target')
diff --git a/ext/-test-/load/resolve_symbol_target/resolve_symbol_target.c b/ext/-test-/load/resolve_symbol_target/resolve_symbol_target.c
new file mode 100644
index 0000000000..b5bc9e8ee0
--- /dev/null
+++ b/ext/-test-/load/resolve_symbol_target/resolve_symbol_target.c
@@ -0,0 +1,15 @@
+#include <ruby.h>
+#include "resolve_symbol_target.h"
+
+VALUE
+rst_any_method(VALUE klass)
+{
+ return rb_str_new_cstr("from target");
+}
+
+void
+Init_resolve_symbol_target(void)
+{
+ VALUE mod = rb_define_module("ResolveSymbolTarget");
+ rb_define_singleton_method(mod, "any_method", rst_any_method, 0);
+}
diff --git a/ext/-test-/load/resolve_symbol_target/resolve_symbol_target.def b/ext/-test-/load/resolve_symbol_target/resolve_symbol_target.def
new file mode 100644
index 0000000000..c2ed3610fe
--- /dev/null
+++ b/ext/-test-/load/resolve_symbol_target/resolve_symbol_target.def
@@ -0,0 +1,4 @@
+LIBRARY resolve_symbol_target
+EXPORTS
+ Init_resolve_symbol_target
+ rst_any_method
diff --git a/ext/-test-/load/resolve_symbol_target/resolve_symbol_target.h b/ext/-test-/load/resolve_symbol_target/resolve_symbol_target.h
new file mode 100644
index 0000000000..847dcb7dd3
--- /dev/null
+++ b/ext/-test-/load/resolve_symbol_target/resolve_symbol_target.h
@@ -0,0 +1,4 @@
+#include <ruby.h>
+#include "ruby/internal/dllexport.h"
+
+RUBY_FUNC_EXPORTED VALUE rst_any_method(VALUE);
diff --git a/ext/-test-/load/stringify_symbols/extconf.rb b/ext/-test-/load/stringify_symbols/extconf.rb
new file mode 100644
index 0000000000..ac39c15f09
--- /dev/null
+++ b/ext/-test-/load/stringify_symbols/extconf.rb
@@ -0,0 +1 @@
+create_makefile('-test-/load/stringify_symbols')
diff --git a/ext/-test-/load/stringify_symbols/stringify_symbols.c b/ext/-test-/load/stringify_symbols/stringify_symbols.c
new file mode 100644
index 0000000000..11a5ee3bc5
--- /dev/null
+++ b/ext/-test-/load/stringify_symbols/stringify_symbols.c
@@ -0,0 +1,29 @@
+#include <ruby.h>
+#include "ruby/internal/intern/load.h"
+#include "ruby/util.h"
+
+#if SIZEOF_INTPTR_T == SIZEOF_LONG_LONG
+# define UINTPTR2NUM ULL2NUM
+#elif SIZEOF_INTPTR_T == SIZEOF_LONG
+# define UINTPTR2NUM ULONG2NUM
+#else
+# define UINTPTR2NUM UINT2NUM
+#endif
+
+static VALUE
+stringify_symbol(VALUE klass, VALUE fname, VALUE sname)
+{
+ void *ptr = rb_ext_resolve_symbol(StringValueCStr(fname), StringValueCStr(sname));
+ if (ptr == NULL) {
+ return Qnil;
+ }
+ uintptr_t uintptr = (uintptr_t)ptr;
+ return UINTPTR2NUM(uintptr);
+}
+
+void
+Init_stringify_symbols(void)
+{
+ VALUE mod = rb_define_module("StringifySymbols");
+ rb_define_singleton_method(mod, "stringify_symbol", stringify_symbol, 2);
+}
diff --git a/ext/-test-/load/stringify_target/extconf.rb b/ext/-test-/load/stringify_target/extconf.rb
new file mode 100644
index 0000000000..4aa201cb09
--- /dev/null
+++ b/ext/-test-/load/stringify_target/extconf.rb
@@ -0,0 +1 @@
+create_makefile('-test-/load/stringify_target')
diff --git a/ext/-test-/load/stringify_target/stringify_target.c b/ext/-test-/load/stringify_target/stringify_target.c
new file mode 100644
index 0000000000..ce09b8fd77
--- /dev/null
+++ b/ext/-test-/load/stringify_target/stringify_target.c
@@ -0,0 +1,15 @@
+#include <ruby.h>
+#include "stringify_target.h"
+
+VALUE
+stt_any_method(VALUE klass)
+{
+ return rb_str_new_cstr("from target");
+}
+
+void
+Init_stringify_target(void)
+{
+ VALUE mod = rb_define_module("StringifyTarget");
+ rb_define_singleton_method(mod, "any_method", stt_any_method, 0);
+}
diff --git a/ext/-test-/load/stringify_target/stringify_target.def b/ext/-test-/load/stringify_target/stringify_target.def
new file mode 100644
index 0000000000..89c2b762de
--- /dev/null
+++ b/ext/-test-/load/stringify_target/stringify_target.def
@@ -0,0 +1,4 @@
+LIBRARY stringify_target
+EXPORTS
+ Init_stringify_target
+ stt_any_method
diff --git a/ext/-test-/load/stringify_target/stringify_target.h b/ext/-test-/load/stringify_target/stringify_target.h
new file mode 100644
index 0000000000..d95fb65d7c
--- /dev/null
+++ b/ext/-test-/load/stringify_target/stringify_target.h
@@ -0,0 +1,4 @@
+#include <ruby.h>
+#include "ruby/internal/dllexport.h"
+
+RUBY_FUNC_EXPORTED VALUE stt_any_method(VALUE);
diff --git a/ext/-test-/marshal/compat/depend b/ext/-test-/marshal/compat/depend
index ff675ccabb..8bcd9f8b5e 100644
--- a/ext/-test-/marshal/compat/depend
+++ b/ext/-test-/marshal/compat/depend
@@ -147,6 +147,7 @@ usrcompat.o: $(hdrdir)/ruby/internal/special_consts.h
usrcompat.o: $(hdrdir)/ruby/internal/static_assert.h
usrcompat.o: $(hdrdir)/ruby/internal/stdalign.h
usrcompat.o: $(hdrdir)/ruby/internal/stdbool.h
+usrcompat.o: $(hdrdir)/ruby/internal/stdckdint.h
usrcompat.o: $(hdrdir)/ruby/internal/symbol.h
usrcompat.o: $(hdrdir)/ruby/internal/value.h
usrcompat.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/marshal/internal_ivar/depend b/ext/-test-/marshal/internal_ivar/depend
index 4fe36834d8..f8be031efc 100644
--- a/ext/-test-/marshal/internal_ivar/depend
+++ b/ext/-test-/marshal/internal_ivar/depend
@@ -147,6 +147,7 @@ internal_ivar.o: $(hdrdir)/ruby/internal/special_consts.h
internal_ivar.o: $(hdrdir)/ruby/internal/static_assert.h
internal_ivar.o: $(hdrdir)/ruby/internal/stdalign.h
internal_ivar.o: $(hdrdir)/ruby/internal/stdbool.h
+internal_ivar.o: $(hdrdir)/ruby/internal/stdckdint.h
internal_ivar.o: $(hdrdir)/ruby/internal/symbol.h
internal_ivar.o: $(hdrdir)/ruby/internal/value.h
internal_ivar.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/marshal/usr/depend b/ext/-test-/marshal/usr/depend
index 1d56cc8202..09e8207d3a 100644
--- a/ext/-test-/marshal/usr/depend
+++ b/ext/-test-/marshal/usr/depend
@@ -147,6 +147,7 @@ usrmarshal.o: $(hdrdir)/ruby/internal/special_consts.h
usrmarshal.o: $(hdrdir)/ruby/internal/static_assert.h
usrmarshal.o: $(hdrdir)/ruby/internal/stdalign.h
usrmarshal.o: $(hdrdir)/ruby/internal/stdbool.h
+usrmarshal.o: $(hdrdir)/ruby/internal/stdckdint.h
usrmarshal.o: $(hdrdir)/ruby/internal/symbol.h
usrmarshal.o: $(hdrdir)/ruby/internal/value.h
usrmarshal.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/memory_view/depend b/ext/-test-/memory_view/depend
index c9ef06a15c..0c92fc1236 100644
--- a/ext/-test-/memory_view/depend
+++ b/ext/-test-/memory_view/depend
@@ -147,6 +147,7 @@ memory_view.o: $(hdrdir)/ruby/internal/special_consts.h
memory_view.o: $(hdrdir)/ruby/internal/static_assert.h
memory_view.o: $(hdrdir)/ruby/internal/stdalign.h
memory_view.o: $(hdrdir)/ruby/internal/stdbool.h
+memory_view.o: $(hdrdir)/ruby/internal/stdckdint.h
memory_view.o: $(hdrdir)/ruby/internal/symbol.h
memory_view.o: $(hdrdir)/ruby/internal/value.h
memory_view.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/memory_view/memory_view.c b/ext/-test-/memory_view/memory_view.c
index c1df0353cf..63f0beb81e 100644
--- a/ext/-test-/memory_view/memory_view.c
+++ b/ext/-test-/memory_view/memory_view.c
@@ -313,8 +313,8 @@ mdview_get_memory_view(VALUE obj, rb_memory_view_t *view, int flags)
static bool
mdview_release_memory_view(VALUE obj, rb_memory_view_t *view)
{
- if (view->shape) xfree((void *)view->shape);
- if (view->strides) xfree((void *)view->strides);
+ xfree((void *)view->shape);
+ xfree((void *)view->strides);
return true;
}
diff --git a/ext/-test-/method/depend b/ext/-test-/method/depend
index dbf513f48f..dce2a815a4 100644
--- a/ext/-test-/method/depend
+++ b/ext/-test-/method/depend
@@ -147,6 +147,7 @@ arity.o: $(hdrdir)/ruby/internal/special_consts.h
arity.o: $(hdrdir)/ruby/internal/static_assert.h
arity.o: $(hdrdir)/ruby/internal/stdalign.h
arity.o: $(hdrdir)/ruby/internal/stdbool.h
+arity.o: $(hdrdir)/ruby/internal/stdckdint.h
arity.o: $(hdrdir)/ruby/internal/symbol.h
arity.o: $(hdrdir)/ruby/internal/value.h
arity.o: $(hdrdir)/ruby/internal/value_type.h
@@ -306,6 +307,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h
init.o: $(hdrdir)/ruby/internal/static_assert.h
init.o: $(hdrdir)/ruby/internal/stdalign.h
init.o: $(hdrdir)/ruby/internal/stdbool.h
+init.o: $(hdrdir)/ruby/internal/stdckdint.h
init.o: $(hdrdir)/ruby/internal/symbol.h
init.o: $(hdrdir)/ruby/internal/value.h
init.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/notimplement/depend b/ext/-test-/notimplement/depend
index 9105093b0d..84517a9c15 100644
--- a/ext/-test-/notimplement/depend
+++ b/ext/-test-/notimplement/depend
@@ -147,6 +147,7 @@ bug.o: $(hdrdir)/ruby/internal/special_consts.h
bug.o: $(hdrdir)/ruby/internal/static_assert.h
bug.o: $(hdrdir)/ruby/internal/stdalign.h
bug.o: $(hdrdir)/ruby/internal/stdbool.h
+bug.o: $(hdrdir)/ruby/internal/stdckdint.h
bug.o: $(hdrdir)/ruby/internal/symbol.h
bug.o: $(hdrdir)/ruby/internal/value.h
bug.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/num2int/depend b/ext/-test-/num2int/depend
index 4e05d1e8c1..5550033be7 100644
--- a/ext/-test-/num2int/depend
+++ b/ext/-test-/num2int/depend
@@ -147,6 +147,7 @@ num2int.o: $(hdrdir)/ruby/internal/special_consts.h
num2int.o: $(hdrdir)/ruby/internal/static_assert.h
num2int.o: $(hdrdir)/ruby/internal/stdalign.h
num2int.o: $(hdrdir)/ruby/internal/stdbool.h
+num2int.o: $(hdrdir)/ruby/internal/stdckdint.h
num2int.o: $(hdrdir)/ruby/internal/symbol.h
num2int.o: $(hdrdir)/ruby/internal/value.h
num2int.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/path_to_class/depend b/ext/-test-/path_to_class/depend
index 8fe6ee37c2..a1657c9574 100644
--- a/ext/-test-/path_to_class/depend
+++ b/ext/-test-/path_to_class/depend
@@ -147,6 +147,7 @@ path_to_class.o: $(hdrdir)/ruby/internal/special_consts.h
path_to_class.o: $(hdrdir)/ruby/internal/static_assert.h
path_to_class.o: $(hdrdir)/ruby/internal/stdalign.h
path_to_class.o: $(hdrdir)/ruby/internal/stdbool.h
+path_to_class.o: $(hdrdir)/ruby/internal/stdckdint.h
path_to_class.o: $(hdrdir)/ruby/internal/symbol.h
path_to_class.o: $(hdrdir)/ruby/internal/value.h
path_to_class.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/popen_deadlock/depend b/ext/-test-/popen_deadlock/depend
index fb58ca30e9..1904e64e59 100644
--- a/ext/-test-/popen_deadlock/depend
+++ b/ext/-test-/popen_deadlock/depend
@@ -147,6 +147,7 @@ infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/special_consts.h
infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/static_assert.h
infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/stdalign.h
infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/stdbool.h
+infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/stdckdint.h
infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/symbol.h
infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/value.h
infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/postponed_job/depend b/ext/-test-/postponed_job/depend
index e44d9d51b7..72250896b0 100644
--- a/ext/-test-/postponed_job/depend
+++ b/ext/-test-/postponed_job/depend
@@ -148,6 +148,7 @@ postponed_job.o: $(hdrdir)/ruby/internal/special_consts.h
postponed_job.o: $(hdrdir)/ruby/internal/static_assert.h
postponed_job.o: $(hdrdir)/ruby/internal/stdalign.h
postponed_job.o: $(hdrdir)/ruby/internal/stdbool.h
+postponed_job.o: $(hdrdir)/ruby/internal/stdckdint.h
postponed_job.o: $(hdrdir)/ruby/internal/symbol.h
postponed_job.o: $(hdrdir)/ruby/internal/value.h
postponed_job.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/postponed_job/postponed_job.c b/ext/-test-/postponed_job/postponed_job.c
index fa57bef6f5..9ac866ae77 100644
--- a/ext/-test-/postponed_job/postponed_job.c
+++ b/ext/-test-/postponed_job/postponed_job.c
@@ -1,6 +1,29 @@
#include "ruby.h"
#include "ruby/debug.h"
+// We're testing deprecated things, don't print the compiler warnings
+#if 0
+
+#elif defined(_MSC_VER)
+#pragma warning(disable : 4996)
+
+#elif defined(__INTEL_COMPILER)
+#pragma warning(disable : 1786)
+
+#elif defined(__clang__)
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+#elif defined(__GNUC__)
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
+#elif defined(__SUNPRO_CC)
+#pragma error_messages (off,symdeprecated)
+
+#else
+// :FIXME: improve here for your compiler.
+
+#endif
+
static int counter;
static void
@@ -58,6 +81,22 @@ pjob_call_direct(VALUE self, VALUE obj)
return self;
}
+static void pjob_noop_callback(void *data) { }
+
+static VALUE
+pjob_register_one_same(VALUE self)
+{
+ rb_gc_start();
+ int r1 = rb_postponed_job_register_one(0, pjob_noop_callback, NULL);
+ int r2 = rb_postponed_job_register_one(0, pjob_noop_callback, NULL);
+ int r3 = rb_postponed_job_register_one(0, pjob_noop_callback, NULL);
+ VALUE ary = rb_ary_new();
+ rb_ary_push(ary, INT2FIX(r1));
+ rb_ary_push(ary, INT2FIX(r2));
+ rb_ary_push(ary, INT2FIX(r3));
+ return ary;
+}
+
#ifdef HAVE_PTHREAD_H
#include <pthread.h>
@@ -86,6 +125,93 @@ pjob_register_in_c_thread(VALUE self, VALUE obj)
}
#endif
+static void
+pjob_preregistered_callback(void *data)
+{
+ VALUE ary = (VALUE)data;
+ Check_Type(ary, T_ARRAY);
+ rb_ary_push(ary, INT2FIX(counter));
+}
+
+static VALUE
+pjob_preregister_and_call_with_sleep(VALUE self, VALUE obj)
+{
+ counter = 0;
+ rb_postponed_job_handle_t h = rb_postponed_job_preregister(0, pjob_preregistered_callback, (void *)obj);
+ counter++;
+ rb_postponed_job_trigger(h);
+ rb_thread_sleep(0);
+ counter++;
+ rb_postponed_job_trigger(h);
+ rb_thread_sleep(0);
+ counter++;
+ rb_postponed_job_trigger(h);
+ rb_thread_sleep(0);
+ return self;
+}
+
+static VALUE
+pjob_preregister_and_call_without_sleep(VALUE self, VALUE obj)
+{
+ counter = 0;
+ rb_postponed_job_handle_t h = rb_postponed_job_preregister(0, pjob_preregistered_callback, (void *)obj);
+ counter = 3;
+ rb_postponed_job_trigger(h);
+ rb_postponed_job_trigger(h);
+ rb_postponed_job_trigger(h);
+ return self;
+}
+
+static VALUE
+pjob_preregister_multiple_times(VALUE self)
+{
+ int r1 = rb_postponed_job_preregister(0, pjob_noop_callback, NULL);
+ int r2 = rb_postponed_job_preregister(0, pjob_noop_callback, NULL);
+ int r3 = rb_postponed_job_preregister(0, pjob_noop_callback, NULL);
+ VALUE ary = rb_ary_new();
+ rb_ary_push(ary, INT2FIX(r1));
+ rb_ary_push(ary, INT2FIX(r2));
+ rb_ary_push(ary, INT2FIX(r3));
+ return ary;
+
+}
+
+struct pjob_append_data_args {
+ VALUE ary;
+ VALUE data;
+};
+
+static void
+pjob_append_data_callback(void *vctx) {
+ struct pjob_append_data_args *ctx = (struct pjob_append_data_args *)vctx;
+ Check_Type(ctx->ary, T_ARRAY);
+ rb_ary_push(ctx->ary, ctx->data);
+}
+
+static VALUE
+pjob_preregister_calls_with_last_argument(VALUE self)
+{
+ VALUE ary = rb_ary_new();
+
+ struct pjob_append_data_args arg1 = { .ary = ary, .data = INT2FIX(1) };
+ struct pjob_append_data_args arg2 = { .ary = ary, .data = INT2FIX(2) };
+ struct pjob_append_data_args arg3 = { .ary = ary, .data = INT2FIX(3) };
+ struct pjob_append_data_args arg4 = { .ary = ary, .data = INT2FIX(4) };
+
+ rb_postponed_job_handle_t h;
+ h = rb_postponed_job_preregister(0, pjob_append_data_callback, &arg1);
+ rb_postponed_job_preregister(0, pjob_append_data_callback, &arg2);
+ rb_postponed_job_trigger(h);
+ rb_postponed_job_preregister(0, pjob_append_data_callback, &arg3);
+ rb_thread_sleep(0); // should execute with arg3
+
+ rb_postponed_job_preregister(0, pjob_append_data_callback, &arg4);
+ rb_postponed_job_trigger(h);
+ rb_thread_sleep(0); // should execute with arg4
+
+ return ary;
+}
+
void
Init_postponed_job(VALUE self)
{
@@ -93,8 +219,13 @@ Init_postponed_job(VALUE self)
rb_define_module_function(mBug, "postponed_job_register", pjob_register, 1);
rb_define_module_function(mBug, "postponed_job_register_one", pjob_register_one, 1);
rb_define_module_function(mBug, "postponed_job_call_direct", pjob_call_direct, 1);
+ rb_define_module_function(mBug, "postponed_job_register_one_same", pjob_register_one_same, 0);
#ifdef HAVE_PTHREAD_H
rb_define_module_function(mBug, "postponed_job_register_in_c_thread", pjob_register_in_c_thread, 1);
#endif
+ rb_define_module_function(mBug, "postponed_job_preregister_and_call_with_sleep", pjob_preregister_and_call_with_sleep, 1);
+ rb_define_module_function(mBug, "postponed_job_preregister_and_call_without_sleep", pjob_preregister_and_call_without_sleep, 1);
+ rb_define_module_function(mBug, "postponed_job_preregister_multiple_times", pjob_preregister_multiple_times, 0);
+ rb_define_module_function(mBug, "postponed_job_preregister_calls_with_last_argument", pjob_preregister_calls_with_last_argument, 0);
}
diff --git a/ext/-test-/printf/depend b/ext/-test-/printf/depend
index b397041103..0530df78bf 100644
--- a/ext/-test-/printf/depend
+++ b/ext/-test-/printf/depend
@@ -157,6 +157,7 @@ printf.o: $(hdrdir)/ruby/internal/special_consts.h
printf.o: $(hdrdir)/ruby/internal/static_assert.h
printf.o: $(hdrdir)/ruby/internal/stdalign.h
printf.o: $(hdrdir)/ruby/internal/stdbool.h
+printf.o: $(hdrdir)/ruby/internal/stdckdint.h
printf.o: $(hdrdir)/ruby/internal/symbol.h
printf.o: $(hdrdir)/ruby/internal/value.h
printf.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/proc/depend b/ext/-test-/proc/depend
index 7e78aa6f83..45e12bcd09 100644
--- a/ext/-test-/proc/depend
+++ b/ext/-test-/proc/depend
@@ -147,6 +147,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h
init.o: $(hdrdir)/ruby/internal/static_assert.h
init.o: $(hdrdir)/ruby/internal/stdalign.h
init.o: $(hdrdir)/ruby/internal/stdbool.h
+init.o: $(hdrdir)/ruby/internal/stdckdint.h
init.o: $(hdrdir)/ruby/internal/symbol.h
init.o: $(hdrdir)/ruby/internal/value.h
init.o: $(hdrdir)/ruby/internal/value_type.h
@@ -306,6 +307,7 @@ receiver.o: $(hdrdir)/ruby/internal/special_consts.h
receiver.o: $(hdrdir)/ruby/internal/static_assert.h
receiver.o: $(hdrdir)/ruby/internal/stdalign.h
receiver.o: $(hdrdir)/ruby/internal/stdbool.h
+receiver.o: $(hdrdir)/ruby/internal/stdckdint.h
receiver.o: $(hdrdir)/ruby/internal/symbol.h
receiver.o: $(hdrdir)/ruby/internal/value.h
receiver.o: $(hdrdir)/ruby/internal/value_type.h
@@ -465,6 +467,7 @@ super.o: $(hdrdir)/ruby/internal/special_consts.h
super.o: $(hdrdir)/ruby/internal/static_assert.h
super.o: $(hdrdir)/ruby/internal/stdalign.h
super.o: $(hdrdir)/ruby/internal/stdbool.h
+super.o: $(hdrdir)/ruby/internal/stdckdint.h
super.o: $(hdrdir)/ruby/internal/symbol.h
super.o: $(hdrdir)/ruby/internal/value.h
super.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/random/depend b/ext/-test-/random/depend
index 3f9a52be44..71f5f6e1e6 100644
--- a/ext/-test-/random/depend
+++ b/ext/-test-/random/depend
@@ -146,6 +146,7 @@ bad_version.o: $(hdrdir)/ruby/internal/special_consts.h
bad_version.o: $(hdrdir)/ruby/internal/static_assert.h
bad_version.o: $(hdrdir)/ruby/internal/stdalign.h
bad_version.o: $(hdrdir)/ruby/internal/stdbool.h
+bad_version.o: $(hdrdir)/ruby/internal/stdckdint.h
bad_version.o: $(hdrdir)/ruby/internal/symbol.h
bad_version.o: $(hdrdir)/ruby/internal/value.h
bad_version.o: $(hdrdir)/ruby/internal/value_type.h
@@ -306,6 +307,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h
init.o: $(hdrdir)/ruby/internal/static_assert.h
init.o: $(hdrdir)/ruby/internal/stdalign.h
init.o: $(hdrdir)/ruby/internal/stdbool.h
+init.o: $(hdrdir)/ruby/internal/stdckdint.h
init.o: $(hdrdir)/ruby/internal/symbol.h
init.o: $(hdrdir)/ruby/internal/value.h
init.o: $(hdrdir)/ruby/internal/value_type.h
@@ -464,6 +466,7 @@ loop.o: $(hdrdir)/ruby/internal/special_consts.h
loop.o: $(hdrdir)/ruby/internal/static_assert.h
loop.o: $(hdrdir)/ruby/internal/stdalign.h
loop.o: $(hdrdir)/ruby/internal/stdbool.h
+loop.o: $(hdrdir)/ruby/internal/stdckdint.h
loop.o: $(hdrdir)/ruby/internal/symbol.h
loop.o: $(hdrdir)/ruby/internal/value.h
loop.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/rational/depend b/ext/-test-/rational/depend
index cff2eae38d..363d779302 100644
--- a/ext/-test-/rational/depend
+++ b/ext/-test-/rational/depend
@@ -151,6 +151,7 @@ rat.o: $(hdrdir)/ruby/internal/special_consts.h
rat.o: $(hdrdir)/ruby/internal/static_assert.h
rat.o: $(hdrdir)/ruby/internal/stdalign.h
rat.o: $(hdrdir)/ruby/internal/stdbool.h
+rat.o: $(hdrdir)/ruby/internal/stdckdint.h
rat.o: $(hdrdir)/ruby/internal/symbol.h
rat.o: $(hdrdir)/ruby/internal/value.h
rat.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/rb_call_super_kw/depend b/ext/-test-/rb_call_super_kw/depend
index a42ddc85ac..04a0fac12c 100644
--- a/ext/-test-/rb_call_super_kw/depend
+++ b/ext/-test-/rb_call_super_kw/depend
@@ -147,6 +147,7 @@ rb_call_super_kw.o: $(hdrdir)/ruby/internal/special_consts.h
rb_call_super_kw.o: $(hdrdir)/ruby/internal/static_assert.h
rb_call_super_kw.o: $(hdrdir)/ruby/internal/stdalign.h
rb_call_super_kw.o: $(hdrdir)/ruby/internal/stdbool.h
+rb_call_super_kw.o: $(hdrdir)/ruby/internal/stdckdint.h
rb_call_super_kw.o: $(hdrdir)/ruby/internal/symbol.h
rb_call_super_kw.o: $(hdrdir)/ruby/internal/value.h
rb_call_super_kw.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/recursion/depend b/ext/-test-/recursion/depend
index 49377250ef..2a65c98b09 100644
--- a/ext/-test-/recursion/depend
+++ b/ext/-test-/recursion/depend
@@ -147,6 +147,7 @@ recursion.o: $(hdrdir)/ruby/internal/special_consts.h
recursion.o: $(hdrdir)/ruby/internal/static_assert.h
recursion.o: $(hdrdir)/ruby/internal/stdalign.h
recursion.o: $(hdrdir)/ruby/internal/stdbool.h
+recursion.o: $(hdrdir)/ruby/internal/stdckdint.h
recursion.o: $(hdrdir)/ruby/internal/symbol.h
recursion.o: $(hdrdir)/ruby/internal/value.h
recursion.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/regexp/depend b/ext/-test-/regexp/depend
index 484f0320dd..0127a66a2e 100644
--- a/ext/-test-/regexp/depend
+++ b/ext/-test-/regexp/depend
@@ -147,6 +147,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h
init.o: $(hdrdir)/ruby/internal/static_assert.h
init.o: $(hdrdir)/ruby/internal/stdalign.h
init.o: $(hdrdir)/ruby/internal/stdbool.h
+init.o: $(hdrdir)/ruby/internal/stdckdint.h
init.o: $(hdrdir)/ruby/internal/symbol.h
init.o: $(hdrdir)/ruby/internal/value.h
init.o: $(hdrdir)/ruby/internal/value_type.h
@@ -306,6 +307,7 @@ parse_depth_limit.o: $(hdrdir)/ruby/internal/special_consts.h
parse_depth_limit.o: $(hdrdir)/ruby/internal/static_assert.h
parse_depth_limit.o: $(hdrdir)/ruby/internal/stdalign.h
parse_depth_limit.o: $(hdrdir)/ruby/internal/stdbool.h
+parse_depth_limit.o: $(hdrdir)/ruby/internal/stdckdint.h
parse_depth_limit.o: $(hdrdir)/ruby/internal/symbol.h
parse_depth_limit.o: $(hdrdir)/ruby/internal/value.h
parse_depth_limit.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/scan_args/depend b/ext/-test-/scan_args/depend
index 3bedc6a7cf..922e5bbd5c 100644
--- a/ext/-test-/scan_args/depend
+++ b/ext/-test-/scan_args/depend
@@ -147,6 +147,7 @@ scan_args.o: $(hdrdir)/ruby/internal/special_consts.h
scan_args.o: $(hdrdir)/ruby/internal/static_assert.h
scan_args.o: $(hdrdir)/ruby/internal/stdalign.h
scan_args.o: $(hdrdir)/ruby/internal/stdbool.h
+scan_args.o: $(hdrdir)/ruby/internal/stdckdint.h
scan_args.o: $(hdrdir)/ruby/internal/symbol.h
scan_args.o: $(hdrdir)/ruby/internal/value.h
scan_args.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/st/foreach/depend b/ext/-test-/st/foreach/depend
index fdfe356805..36273f8df8 100644
--- a/ext/-test-/st/foreach/depend
+++ b/ext/-test-/st/foreach/depend
@@ -147,6 +147,7 @@ foreach.o: $(hdrdir)/ruby/internal/special_consts.h
foreach.o: $(hdrdir)/ruby/internal/static_assert.h
foreach.o: $(hdrdir)/ruby/internal/stdalign.h
foreach.o: $(hdrdir)/ruby/internal/stdbool.h
+foreach.o: $(hdrdir)/ruby/internal/stdckdint.h
foreach.o: $(hdrdir)/ruby/internal/symbol.h
foreach.o: $(hdrdir)/ruby/internal/value.h
foreach.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/st/numhash/depend b/ext/-test-/st/numhash/depend
index ef28c892f3..a0916183b6 100644
--- a/ext/-test-/st/numhash/depend
+++ b/ext/-test-/st/numhash/depend
@@ -147,6 +147,7 @@ numhash.o: $(hdrdir)/ruby/internal/special_consts.h
numhash.o: $(hdrdir)/ruby/internal/static_assert.h
numhash.o: $(hdrdir)/ruby/internal/stdalign.h
numhash.o: $(hdrdir)/ruby/internal/stdbool.h
+numhash.o: $(hdrdir)/ruby/internal/stdckdint.h
numhash.o: $(hdrdir)/ruby/internal/symbol.h
numhash.o: $(hdrdir)/ruby/internal/value.h
numhash.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/st/update/depend b/ext/-test-/st/update/depend
index 2d5ff224a2..96ba194df0 100644
--- a/ext/-test-/st/update/depend
+++ b/ext/-test-/st/update/depend
@@ -147,6 +147,7 @@ update.o: $(hdrdir)/ruby/internal/special_consts.h
update.o: $(hdrdir)/ruby/internal/static_assert.h
update.o: $(hdrdir)/ruby/internal/stdalign.h
update.o: $(hdrdir)/ruby/internal/stdbool.h
+update.o: $(hdrdir)/ruby/internal/stdckdint.h
update.o: $(hdrdir)/ruby/internal/symbol.h
update.o: $(hdrdir)/ruby/internal/value.h
update.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/string/chilled.c b/ext/-test-/string/chilled.c
new file mode 100644
index 0000000000..c98fc72c47
--- /dev/null
+++ b/ext/-test-/string/chilled.c
@@ -0,0 +1,13 @@
+#include "ruby.h"
+
+static VALUE
+bug_s_rb_str_chilled_p(VALUE self, VALUE str)
+{
+ return rb_str_chilled_p(str) ? Qtrue : Qfalse;
+}
+
+void
+Init_string_chilled(VALUE klass)
+{
+ rb_define_singleton_method(klass, "rb_str_chilled_p", bug_s_rb_str_chilled_p, 1);
+}
diff --git a/ext/-test-/string/cstr.c b/ext/-test-/string/cstr.c
index ecca793145..b0b1ef5374 100644
--- a/ext/-test-/string/cstr.c
+++ b/ext/-test-/string/cstr.c
@@ -61,13 +61,12 @@ bug_str_unterminated_substring(VALUE str, VALUE vbeg, VALUE vlen)
if (RSTRING_LEN(str) < beg) rb_raise(rb_eIndexError, "beg: %ld", beg);
if (RSTRING_LEN(str) < beg + len) rb_raise(rb_eIndexError, "end: %ld", beg + len);
str = rb_str_new_shared(str);
+ RSTRING(str)->len = len;
if (STR_EMBED_P(str)) {
- RSTRING(str)->as.embed.len = (short)len;
memmove(RSTRING(str)->as.embed.ary, RSTRING(str)->as.embed.ary + beg, len);
}
else {
RSTRING(str)->as.heap.ptr += beg;
- RSTRING(str)->as.heap.len = len;
}
return str;
}
@@ -114,7 +113,7 @@ bug_str_s_cstr_noembed(VALUE self, VALUE str)
RBASIC(str2)->flags &= ~(STR_SHARED | FL_USER5 | FL_USER6);
RSTRING(str2)->as.heap.aux.capa = capacity;
RSTRING(str2)->as.heap.ptr = buf;
- RSTRING(str2)->as.heap.len = RSTRING_LEN(str);
+ RSTRING(str2)->len = RSTRING_LEN(str);
TERM_FILL(RSTRING_END(str2), TERM_LEN(str));
return str2;
}
diff --git a/ext/-test-/string/depend b/ext/-test-/string/depend
index 26720282b2..044b6109ff 100644
--- a/ext/-test-/string/depend
+++ b/ext/-test-/string/depend
@@ -158,6 +158,7 @@ capacity.o: $(hdrdir)/ruby/internal/special_consts.h
capacity.o: $(hdrdir)/ruby/internal/static_assert.h
capacity.o: $(hdrdir)/ruby/internal/stdalign.h
capacity.o: $(hdrdir)/ruby/internal/stdbool.h
+capacity.o: $(hdrdir)/ruby/internal/stdckdint.h
capacity.o: $(hdrdir)/ruby/internal/symbol.h
capacity.o: $(hdrdir)/ruby/internal/value.h
capacity.o: $(hdrdir)/ruby/internal/value_type.h
@@ -173,6 +174,166 @@ capacity.o: $(hdrdir)/ruby/subst.h
capacity.o: $(top_srcdir)/internal/compilers.h
capacity.o: $(top_srcdir)/internal/string.h
capacity.o: capacity.c
+chilled.o: $(RUBY_EXTCONF_H)
+chilled.o: $(arch_hdrdir)/ruby/config.h
+chilled.o: $(hdrdir)/ruby.h
+chilled.o: $(hdrdir)/ruby/assert.h
+chilled.o: $(hdrdir)/ruby/backward.h
+chilled.o: $(hdrdir)/ruby/backward/2/assume.h
+chilled.o: $(hdrdir)/ruby/backward/2/attributes.h
+chilled.o: $(hdrdir)/ruby/backward/2/bool.h
+chilled.o: $(hdrdir)/ruby/backward/2/inttypes.h
+chilled.o: $(hdrdir)/ruby/backward/2/limits.h
+chilled.o: $(hdrdir)/ruby/backward/2/long_long.h
+chilled.o: $(hdrdir)/ruby/backward/2/stdalign.h
+chilled.o: $(hdrdir)/ruby/backward/2/stdarg.h
+chilled.o: $(hdrdir)/ruby/defines.h
+chilled.o: $(hdrdir)/ruby/intern.h
+chilled.o: $(hdrdir)/ruby/internal/abi.h
+chilled.o: $(hdrdir)/ruby/internal/anyargs.h
+chilled.o: $(hdrdir)/ruby/internal/arithmetic.h
+chilled.o: $(hdrdir)/ruby/internal/arithmetic/char.h
+chilled.o: $(hdrdir)/ruby/internal/arithmetic/double.h
+chilled.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h
+chilled.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h
+chilled.o: $(hdrdir)/ruby/internal/arithmetic/int.h
+chilled.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h
+chilled.o: $(hdrdir)/ruby/internal/arithmetic/long.h
+chilled.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h
+chilled.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h
+chilled.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h
+chilled.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h
+chilled.o: $(hdrdir)/ruby/internal/arithmetic/short.h
+chilled.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h
+chilled.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h
+chilled.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h
+chilled.o: $(hdrdir)/ruby/internal/assume.h
+chilled.o: $(hdrdir)/ruby/internal/attr/alloc_size.h
+chilled.o: $(hdrdir)/ruby/internal/attr/artificial.h
+chilled.o: $(hdrdir)/ruby/internal/attr/cold.h
+chilled.o: $(hdrdir)/ruby/internal/attr/const.h
+chilled.o: $(hdrdir)/ruby/internal/attr/constexpr.h
+chilled.o: $(hdrdir)/ruby/internal/attr/deprecated.h
+chilled.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h
+chilled.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h
+chilled.o: $(hdrdir)/ruby/internal/attr/error.h
+chilled.o: $(hdrdir)/ruby/internal/attr/flag_enum.h
+chilled.o: $(hdrdir)/ruby/internal/attr/forceinline.h
+chilled.o: $(hdrdir)/ruby/internal/attr/format.h
+chilled.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h
+chilled.o: $(hdrdir)/ruby/internal/attr/noalias.h
+chilled.o: $(hdrdir)/ruby/internal/attr/nodiscard.h
+chilled.o: $(hdrdir)/ruby/internal/attr/noexcept.h
+chilled.o: $(hdrdir)/ruby/internal/attr/noinline.h
+chilled.o: $(hdrdir)/ruby/internal/attr/nonnull.h
+chilled.o: $(hdrdir)/ruby/internal/attr/noreturn.h
+chilled.o: $(hdrdir)/ruby/internal/attr/packed_struct.h
+chilled.o: $(hdrdir)/ruby/internal/attr/pure.h
+chilled.o: $(hdrdir)/ruby/internal/attr/restrict.h
+chilled.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h
+chilled.o: $(hdrdir)/ruby/internal/attr/warning.h
+chilled.o: $(hdrdir)/ruby/internal/attr/weakref.h
+chilled.o: $(hdrdir)/ruby/internal/cast.h
+chilled.o: $(hdrdir)/ruby/internal/compiler_is.h
+chilled.o: $(hdrdir)/ruby/internal/compiler_is/apple.h
+chilled.o: $(hdrdir)/ruby/internal/compiler_is/clang.h
+chilled.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h
+chilled.o: $(hdrdir)/ruby/internal/compiler_is/intel.h
+chilled.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h
+chilled.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h
+chilled.o: $(hdrdir)/ruby/internal/compiler_since.h
+chilled.o: $(hdrdir)/ruby/internal/config.h
+chilled.o: $(hdrdir)/ruby/internal/constant_p.h
+chilled.o: $(hdrdir)/ruby/internal/core.h
+chilled.o: $(hdrdir)/ruby/internal/core/rarray.h
+chilled.o: $(hdrdir)/ruby/internal/core/rbasic.h
+chilled.o: $(hdrdir)/ruby/internal/core/rbignum.h
+chilled.o: $(hdrdir)/ruby/internal/core/rclass.h
+chilled.o: $(hdrdir)/ruby/internal/core/rdata.h
+chilled.o: $(hdrdir)/ruby/internal/core/rfile.h
+chilled.o: $(hdrdir)/ruby/internal/core/rhash.h
+chilled.o: $(hdrdir)/ruby/internal/core/robject.h
+chilled.o: $(hdrdir)/ruby/internal/core/rregexp.h
+chilled.o: $(hdrdir)/ruby/internal/core/rstring.h
+chilled.o: $(hdrdir)/ruby/internal/core/rstruct.h
+chilled.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
+chilled.o: $(hdrdir)/ruby/internal/ctype.h
+chilled.o: $(hdrdir)/ruby/internal/dllexport.h
+chilled.o: $(hdrdir)/ruby/internal/dosish.h
+chilled.o: $(hdrdir)/ruby/internal/error.h
+chilled.o: $(hdrdir)/ruby/internal/eval.h
+chilled.o: $(hdrdir)/ruby/internal/event.h
+chilled.o: $(hdrdir)/ruby/internal/fl_type.h
+chilled.o: $(hdrdir)/ruby/internal/gc.h
+chilled.o: $(hdrdir)/ruby/internal/glob.h
+chilled.o: $(hdrdir)/ruby/internal/globals.h
+chilled.o: $(hdrdir)/ruby/internal/has/attribute.h
+chilled.o: $(hdrdir)/ruby/internal/has/builtin.h
+chilled.o: $(hdrdir)/ruby/internal/has/c_attribute.h
+chilled.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h
+chilled.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h
+chilled.o: $(hdrdir)/ruby/internal/has/extension.h
+chilled.o: $(hdrdir)/ruby/internal/has/feature.h
+chilled.o: $(hdrdir)/ruby/internal/has/warning.h
+chilled.o: $(hdrdir)/ruby/internal/intern/array.h
+chilled.o: $(hdrdir)/ruby/internal/intern/bignum.h
+chilled.o: $(hdrdir)/ruby/internal/intern/class.h
+chilled.o: $(hdrdir)/ruby/internal/intern/compar.h
+chilled.o: $(hdrdir)/ruby/internal/intern/complex.h
+chilled.o: $(hdrdir)/ruby/internal/intern/cont.h
+chilled.o: $(hdrdir)/ruby/internal/intern/dir.h
+chilled.o: $(hdrdir)/ruby/internal/intern/enum.h
+chilled.o: $(hdrdir)/ruby/internal/intern/enumerator.h
+chilled.o: $(hdrdir)/ruby/internal/intern/error.h
+chilled.o: $(hdrdir)/ruby/internal/intern/eval.h
+chilled.o: $(hdrdir)/ruby/internal/intern/file.h
+chilled.o: $(hdrdir)/ruby/internal/intern/hash.h
+chilled.o: $(hdrdir)/ruby/internal/intern/io.h
+chilled.o: $(hdrdir)/ruby/internal/intern/load.h
+chilled.o: $(hdrdir)/ruby/internal/intern/marshal.h
+chilled.o: $(hdrdir)/ruby/internal/intern/numeric.h
+chilled.o: $(hdrdir)/ruby/internal/intern/object.h
+chilled.o: $(hdrdir)/ruby/internal/intern/parse.h
+chilled.o: $(hdrdir)/ruby/internal/intern/proc.h
+chilled.o: $(hdrdir)/ruby/internal/intern/process.h
+chilled.o: $(hdrdir)/ruby/internal/intern/random.h
+chilled.o: $(hdrdir)/ruby/internal/intern/range.h
+chilled.o: $(hdrdir)/ruby/internal/intern/rational.h
+chilled.o: $(hdrdir)/ruby/internal/intern/re.h
+chilled.o: $(hdrdir)/ruby/internal/intern/ruby.h
+chilled.o: $(hdrdir)/ruby/internal/intern/select.h
+chilled.o: $(hdrdir)/ruby/internal/intern/select/largesize.h
+chilled.o: $(hdrdir)/ruby/internal/intern/signal.h
+chilled.o: $(hdrdir)/ruby/internal/intern/sprintf.h
+chilled.o: $(hdrdir)/ruby/internal/intern/string.h
+chilled.o: $(hdrdir)/ruby/internal/intern/struct.h
+chilled.o: $(hdrdir)/ruby/internal/intern/thread.h
+chilled.o: $(hdrdir)/ruby/internal/intern/time.h
+chilled.o: $(hdrdir)/ruby/internal/intern/variable.h
+chilled.o: $(hdrdir)/ruby/internal/intern/vm.h
+chilled.o: $(hdrdir)/ruby/internal/interpreter.h
+chilled.o: $(hdrdir)/ruby/internal/iterator.h
+chilled.o: $(hdrdir)/ruby/internal/memory.h
+chilled.o: $(hdrdir)/ruby/internal/method.h
+chilled.o: $(hdrdir)/ruby/internal/module.h
+chilled.o: $(hdrdir)/ruby/internal/newobj.h
+chilled.o: $(hdrdir)/ruby/internal/scan_args.h
+chilled.o: $(hdrdir)/ruby/internal/special_consts.h
+chilled.o: $(hdrdir)/ruby/internal/static_assert.h
+chilled.o: $(hdrdir)/ruby/internal/stdalign.h
+chilled.o: $(hdrdir)/ruby/internal/stdbool.h
+chilled.o: $(hdrdir)/ruby/internal/stdckdint.h
+chilled.o: $(hdrdir)/ruby/internal/symbol.h
+chilled.o: $(hdrdir)/ruby/internal/value.h
+chilled.o: $(hdrdir)/ruby/internal/value_type.h
+chilled.o: $(hdrdir)/ruby/internal/variable.h
+chilled.o: $(hdrdir)/ruby/internal/warning_push.h
+chilled.o: $(hdrdir)/ruby/internal/xmalloc.h
+chilled.o: $(hdrdir)/ruby/missing.h
+chilled.o: $(hdrdir)/ruby/ruby.h
+chilled.o: $(hdrdir)/ruby/st.h
+chilled.o: $(hdrdir)/ruby/subst.h
+chilled.o: chilled.c
coderange.o: $(RUBY_EXTCONF_H)
coderange.o: $(arch_hdrdir)/ruby/config.h
coderange.o: $(hdrdir)/ruby/assert.h
@@ -330,6 +491,7 @@ coderange.o: $(hdrdir)/ruby/internal/special_consts.h
coderange.o: $(hdrdir)/ruby/internal/static_assert.h
coderange.o: $(hdrdir)/ruby/internal/stdalign.h
coderange.o: $(hdrdir)/ruby/internal/stdbool.h
+coderange.o: $(hdrdir)/ruby/internal/stdckdint.h
coderange.o: $(hdrdir)/ruby/internal/symbol.h
coderange.o: $(hdrdir)/ruby/internal/value.h
coderange.o: $(hdrdir)/ruby/internal/value_type.h
@@ -501,6 +663,7 @@ cstr.o: $(hdrdir)/ruby/internal/special_consts.h
cstr.o: $(hdrdir)/ruby/internal/static_assert.h
cstr.o: $(hdrdir)/ruby/internal/stdalign.h
cstr.o: $(hdrdir)/ruby/internal/stdbool.h
+cstr.o: $(hdrdir)/ruby/internal/stdckdint.h
cstr.o: $(hdrdir)/ruby/internal/symbol.h
cstr.o: $(hdrdir)/ruby/internal/value.h
cstr.o: $(hdrdir)/ruby/internal/value_type.h
@@ -665,6 +828,7 @@ ellipsize.o: $(hdrdir)/ruby/internal/special_consts.h
ellipsize.o: $(hdrdir)/ruby/internal/static_assert.h
ellipsize.o: $(hdrdir)/ruby/internal/stdalign.h
ellipsize.o: $(hdrdir)/ruby/internal/stdbool.h
+ellipsize.o: $(hdrdir)/ruby/internal/stdckdint.h
ellipsize.o: $(hdrdir)/ruby/internal/symbol.h
ellipsize.o: $(hdrdir)/ruby/internal/value.h
ellipsize.o: $(hdrdir)/ruby/internal/value_type.h
@@ -834,6 +998,7 @@ enc_associate.o: $(hdrdir)/ruby/internal/special_consts.h
enc_associate.o: $(hdrdir)/ruby/internal/static_assert.h
enc_associate.o: $(hdrdir)/ruby/internal/stdalign.h
enc_associate.o: $(hdrdir)/ruby/internal/stdbool.h
+enc_associate.o: $(hdrdir)/ruby/internal/stdckdint.h
enc_associate.o: $(hdrdir)/ruby/internal/symbol.h
enc_associate.o: $(hdrdir)/ruby/internal/value.h
enc_associate.o: $(hdrdir)/ruby/internal/value_type.h
@@ -1005,6 +1170,7 @@ enc_dummy.o: $(hdrdir)/ruby/internal/special_consts.h
enc_dummy.o: $(hdrdir)/ruby/internal/static_assert.h
enc_dummy.o: $(hdrdir)/ruby/internal/stdalign.h
enc_dummy.o: $(hdrdir)/ruby/internal/stdbool.h
+enc_dummy.o: $(hdrdir)/ruby/internal/stdckdint.h
enc_dummy.o: $(hdrdir)/ruby/internal/symbol.h
enc_dummy.o: $(hdrdir)/ruby/internal/value.h
enc_dummy.o: $(hdrdir)/ruby/internal/value_type.h
@@ -1175,6 +1341,7 @@ enc_str_buf_cat.o: $(hdrdir)/ruby/internal/special_consts.h
enc_str_buf_cat.o: $(hdrdir)/ruby/internal/static_assert.h
enc_str_buf_cat.o: $(hdrdir)/ruby/internal/stdalign.h
enc_str_buf_cat.o: $(hdrdir)/ruby/internal/stdbool.h
+enc_str_buf_cat.o: $(hdrdir)/ruby/internal/stdckdint.h
enc_str_buf_cat.o: $(hdrdir)/ruby/internal/symbol.h
enc_str_buf_cat.o: $(hdrdir)/ruby/internal/value.h
enc_str_buf_cat.o: $(hdrdir)/ruby/internal/value_type.h
@@ -1196,6 +1363,7 @@ fstring.o: $(hdrdir)/ruby/backward.h
fstring.o: $(hdrdir)/ruby/backward/2/assume.h
fstring.o: $(hdrdir)/ruby/backward/2/attributes.h
fstring.o: $(hdrdir)/ruby/backward/2/bool.h
+fstring.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h
fstring.o: $(hdrdir)/ruby/backward/2/inttypes.h
fstring.o: $(hdrdir)/ruby/backward/2/limits.h
fstring.o: $(hdrdir)/ruby/backward/2/long_long.h
@@ -1346,6 +1514,7 @@ fstring.o: $(hdrdir)/ruby/internal/special_consts.h
fstring.o: $(hdrdir)/ruby/internal/static_assert.h
fstring.o: $(hdrdir)/ruby/internal/stdalign.h
fstring.o: $(hdrdir)/ruby/internal/stdbool.h
+fstring.o: $(hdrdir)/ruby/internal/stdckdint.h
fstring.o: $(hdrdir)/ruby/internal/symbol.h
fstring.o: $(hdrdir)/ruby/internal/value.h
fstring.o: $(hdrdir)/ruby/internal/value_type.h
@@ -1358,6 +1527,8 @@ fstring.o: $(hdrdir)/ruby/oniguruma.h
fstring.o: $(hdrdir)/ruby/ruby.h
fstring.o: $(hdrdir)/ruby/st.h
fstring.o: $(hdrdir)/ruby/subst.h
+fstring.o: $(top_srcdir)/internal/compilers.h
+fstring.o: $(top_srcdir)/internal/string.h
fstring.o: fstring.c
init.o: $(RUBY_EXTCONF_H)
init.o: $(arch_hdrdir)/ruby/config.h
@@ -1507,6 +1678,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h
init.o: $(hdrdir)/ruby/internal/static_assert.h
init.o: $(hdrdir)/ruby/internal/stdalign.h
init.o: $(hdrdir)/ruby/internal/stdbool.h
+init.o: $(hdrdir)/ruby/internal/stdckdint.h
init.o: $(hdrdir)/ruby/internal/symbol.h
init.o: $(hdrdir)/ruby/internal/value.h
init.o: $(hdrdir)/ruby/internal/value_type.h
@@ -1666,6 +1838,7 @@ modify.o: $(hdrdir)/ruby/internal/special_consts.h
modify.o: $(hdrdir)/ruby/internal/static_assert.h
modify.o: $(hdrdir)/ruby/internal/stdalign.h
modify.o: $(hdrdir)/ruby/internal/stdbool.h
+modify.o: $(hdrdir)/ruby/internal/stdckdint.h
modify.o: $(hdrdir)/ruby/internal/symbol.h
modify.o: $(hdrdir)/ruby/internal/value.h
modify.o: $(hdrdir)/ruby/internal/value_type.h
@@ -1835,6 +2008,7 @@ new.o: $(hdrdir)/ruby/internal/special_consts.h
new.o: $(hdrdir)/ruby/internal/static_assert.h
new.o: $(hdrdir)/ruby/internal/stdalign.h
new.o: $(hdrdir)/ruby/internal/stdbool.h
+new.o: $(hdrdir)/ruby/internal/stdckdint.h
new.o: $(hdrdir)/ruby/internal/symbol.h
new.o: $(hdrdir)/ruby/internal/value.h
new.o: $(hdrdir)/ruby/internal/value_type.h
@@ -1996,6 +2170,7 @@ nofree.o: $(hdrdir)/ruby/internal/special_consts.h
nofree.o: $(hdrdir)/ruby/internal/static_assert.h
nofree.o: $(hdrdir)/ruby/internal/stdalign.h
nofree.o: $(hdrdir)/ruby/internal/stdbool.h
+nofree.o: $(hdrdir)/ruby/internal/stdckdint.h
nofree.o: $(hdrdir)/ruby/internal/symbol.h
nofree.o: $(hdrdir)/ruby/internal/value.h
nofree.o: $(hdrdir)/ruby/internal/value_type.h
@@ -2164,6 +2339,7 @@ normalize.o: $(hdrdir)/ruby/internal/special_consts.h
normalize.o: $(hdrdir)/ruby/internal/static_assert.h
normalize.o: $(hdrdir)/ruby/internal/stdalign.h
normalize.o: $(hdrdir)/ruby/internal/stdbool.h
+normalize.o: $(hdrdir)/ruby/internal/stdckdint.h
normalize.o: $(hdrdir)/ruby/internal/symbol.h
normalize.o: $(hdrdir)/ruby/internal/value.h
normalize.o: $(hdrdir)/ruby/internal/value_type.h
@@ -2336,6 +2512,7 @@ qsort.o: $(hdrdir)/ruby/internal/special_consts.h
qsort.o: $(hdrdir)/ruby/internal/static_assert.h
qsort.o: $(hdrdir)/ruby/internal/stdalign.h
qsort.o: $(hdrdir)/ruby/internal/stdbool.h
+qsort.o: $(hdrdir)/ruby/internal/stdckdint.h
qsort.o: $(hdrdir)/ruby/internal/symbol.h
qsort.o: $(hdrdir)/ruby/internal/value.h
qsort.o: $(hdrdir)/ruby/internal/value_type.h
@@ -2498,6 +2675,7 @@ rb_interned_str.o: $(hdrdir)/ruby/internal/special_consts.h
rb_interned_str.o: $(hdrdir)/ruby/internal/static_assert.h
rb_interned_str.o: $(hdrdir)/ruby/internal/stdalign.h
rb_interned_str.o: $(hdrdir)/ruby/internal/stdbool.h
+rb_interned_str.o: $(hdrdir)/ruby/internal/stdckdint.h
rb_interned_str.o: $(hdrdir)/ruby/internal/symbol.h
rb_interned_str.o: $(hdrdir)/ruby/internal/value.h
rb_interned_str.o: $(hdrdir)/ruby/internal/value_type.h
@@ -2657,6 +2835,7 @@ rb_str_dup.o: $(hdrdir)/ruby/internal/special_consts.h
rb_str_dup.o: $(hdrdir)/ruby/internal/static_assert.h
rb_str_dup.o: $(hdrdir)/ruby/internal/stdalign.h
rb_str_dup.o: $(hdrdir)/ruby/internal/stdbool.h
+rb_str_dup.o: $(hdrdir)/ruby/internal/stdckdint.h
rb_str_dup.o: $(hdrdir)/ruby/internal/symbol.h
rb_str_dup.o: $(hdrdir)/ruby/internal/value.h
rb_str_dup.o: $(hdrdir)/ruby/internal/value_type.h
@@ -2816,6 +2995,7 @@ set_len.o: $(hdrdir)/ruby/internal/special_consts.h
set_len.o: $(hdrdir)/ruby/internal/static_assert.h
set_len.o: $(hdrdir)/ruby/internal/stdalign.h
set_len.o: $(hdrdir)/ruby/internal/stdbool.h
+set_len.o: $(hdrdir)/ruby/internal/stdckdint.h
set_len.o: $(hdrdir)/ruby/internal/symbol.h
set_len.o: $(hdrdir)/ruby/internal/value.h
set_len.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/string/fstring.c b/ext/-test-/string/fstring.c
index 2374319fe3..71c4b7f97e 100644
--- a/ext/-test-/string/fstring.c
+++ b/ext/-test-/string/fstring.c
@@ -1,30 +1,38 @@
#include "ruby.h"
#include "ruby/encoding.h"
-
-VALUE rb_fstring(VALUE str);
+#include "internal/string.h"
VALUE
bug_s_fstring(VALUE self, VALUE str)
{
- return rb_fstring(str);
+ return rb_str_to_interned_str(str);
+}
+
+VALUE
+bug_s_fstring_fake_str(VALUE self)
+{
+ static const char literal[] = "abcdefghijklmnopqrstuvwxyz";
+ struct RString fake_str;
+ return rb_str_to_interned_str(rb_setup_fake_str(&fake_str, literal, sizeof(literal) - 1, 0));
}
VALUE
bug_s_rb_enc_interned_str(VALUE self, VALUE encoding)
{
- return rb_enc_interned_str("foo", 3, RDATA(encoding)->data);
+ return rb_enc_interned_str("foo", 3, NIL_P(encoding) ? NULL : RDATA(encoding)->data);
}
VALUE
bug_s_rb_enc_str_new(VALUE self, VALUE encoding)
{
- return rb_enc_str_new("foo", 3, RDATA(encoding)->data);
+ return rb_enc_str_new("foo", 3, NIL_P(encoding) ? NULL : RDATA(encoding)->data);
}
void
Init_string_fstring(VALUE klass)
{
rb_define_singleton_method(klass, "fstring", bug_s_fstring, 1);
+ rb_define_singleton_method(klass, "fstring_fake_str", bug_s_fstring_fake_str, 0);
rb_define_singleton_method(klass, "rb_enc_interned_str", bug_s_rb_enc_interned_str, 1);
rb_define_singleton_method(klass, "rb_enc_str_new", bug_s_rb_enc_str_new, 1);
}
diff --git a/ext/-test-/string/set_len.c b/ext/-test-/string/set_len.c
index 219cea404c..049da2cdb5 100644
--- a/ext/-test-/string/set_len.c
+++ b/ext/-test-/string/set_len.c
@@ -7,8 +7,18 @@ bug_str_set_len(VALUE str, VALUE len)
return str;
}
+static VALUE
+bug_str_append(VALUE str, VALUE addendum)
+{
+ StringValue(addendum);
+ rb_str_modify_expand(str, RSTRING_LEN(addendum));
+ memcpy(RSTRING_END(str), RSTRING_PTR(addendum), RSTRING_LEN(addendum));
+ return str;
+}
+
void
Init_string_set_len(VALUE klass)
{
rb_define_method(klass, "set_len", bug_str_set_len, 1);
+ rb_define_method(klass, "append", bug_str_append, 1);
}
diff --git a/ext/-test-/struct/data.c b/ext/-test-/struct/data.c
new file mode 100644
index 0000000000..5841c342e7
--- /dev/null
+++ b/ext/-test-/struct/data.c
@@ -0,0 +1,13 @@
+#include "ruby.h"
+
+static VALUE
+bug_data_new(VALUE self, VALUE super)
+{
+ return rb_data_define(super, "mem1", "mem2", NULL);
+}
+
+void
+Init_data(VALUE klass)
+{
+ rb_define_singleton_method(klass, "data_new", bug_data_new, 1);
+}
diff --git a/ext/-test-/struct/depend b/ext/-test-/struct/depend
index 191408cc90..951dddd5dd 100644
--- a/ext/-test-/struct/depend
+++ b/ext/-test-/struct/depend
@@ -1,4 +1,164 @@
# AUTOGENERATED DEPENDENCIES START
+data.o: $(RUBY_EXTCONF_H)
+data.o: $(arch_hdrdir)/ruby/config.h
+data.o: $(hdrdir)/ruby.h
+data.o: $(hdrdir)/ruby/assert.h
+data.o: $(hdrdir)/ruby/backward.h
+data.o: $(hdrdir)/ruby/backward/2/assume.h
+data.o: $(hdrdir)/ruby/backward/2/attributes.h
+data.o: $(hdrdir)/ruby/backward/2/bool.h
+data.o: $(hdrdir)/ruby/backward/2/inttypes.h
+data.o: $(hdrdir)/ruby/backward/2/limits.h
+data.o: $(hdrdir)/ruby/backward/2/long_long.h
+data.o: $(hdrdir)/ruby/backward/2/stdalign.h
+data.o: $(hdrdir)/ruby/backward/2/stdarg.h
+data.o: $(hdrdir)/ruby/defines.h
+data.o: $(hdrdir)/ruby/intern.h
+data.o: $(hdrdir)/ruby/internal/abi.h
+data.o: $(hdrdir)/ruby/internal/anyargs.h
+data.o: $(hdrdir)/ruby/internal/arithmetic.h
+data.o: $(hdrdir)/ruby/internal/arithmetic/char.h
+data.o: $(hdrdir)/ruby/internal/arithmetic/double.h
+data.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h
+data.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h
+data.o: $(hdrdir)/ruby/internal/arithmetic/int.h
+data.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h
+data.o: $(hdrdir)/ruby/internal/arithmetic/long.h
+data.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h
+data.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h
+data.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h
+data.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h
+data.o: $(hdrdir)/ruby/internal/arithmetic/short.h
+data.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h
+data.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h
+data.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h
+data.o: $(hdrdir)/ruby/internal/assume.h
+data.o: $(hdrdir)/ruby/internal/attr/alloc_size.h
+data.o: $(hdrdir)/ruby/internal/attr/artificial.h
+data.o: $(hdrdir)/ruby/internal/attr/cold.h
+data.o: $(hdrdir)/ruby/internal/attr/const.h
+data.o: $(hdrdir)/ruby/internal/attr/constexpr.h
+data.o: $(hdrdir)/ruby/internal/attr/deprecated.h
+data.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h
+data.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h
+data.o: $(hdrdir)/ruby/internal/attr/error.h
+data.o: $(hdrdir)/ruby/internal/attr/flag_enum.h
+data.o: $(hdrdir)/ruby/internal/attr/forceinline.h
+data.o: $(hdrdir)/ruby/internal/attr/format.h
+data.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h
+data.o: $(hdrdir)/ruby/internal/attr/noalias.h
+data.o: $(hdrdir)/ruby/internal/attr/nodiscard.h
+data.o: $(hdrdir)/ruby/internal/attr/noexcept.h
+data.o: $(hdrdir)/ruby/internal/attr/noinline.h
+data.o: $(hdrdir)/ruby/internal/attr/nonnull.h
+data.o: $(hdrdir)/ruby/internal/attr/noreturn.h
+data.o: $(hdrdir)/ruby/internal/attr/packed_struct.h
+data.o: $(hdrdir)/ruby/internal/attr/pure.h
+data.o: $(hdrdir)/ruby/internal/attr/restrict.h
+data.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h
+data.o: $(hdrdir)/ruby/internal/attr/warning.h
+data.o: $(hdrdir)/ruby/internal/attr/weakref.h
+data.o: $(hdrdir)/ruby/internal/cast.h
+data.o: $(hdrdir)/ruby/internal/compiler_is.h
+data.o: $(hdrdir)/ruby/internal/compiler_is/apple.h
+data.o: $(hdrdir)/ruby/internal/compiler_is/clang.h
+data.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h
+data.o: $(hdrdir)/ruby/internal/compiler_is/intel.h
+data.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h
+data.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h
+data.o: $(hdrdir)/ruby/internal/compiler_since.h
+data.o: $(hdrdir)/ruby/internal/config.h
+data.o: $(hdrdir)/ruby/internal/constant_p.h
+data.o: $(hdrdir)/ruby/internal/core.h
+data.o: $(hdrdir)/ruby/internal/core/rarray.h
+data.o: $(hdrdir)/ruby/internal/core/rbasic.h
+data.o: $(hdrdir)/ruby/internal/core/rbignum.h
+data.o: $(hdrdir)/ruby/internal/core/rclass.h
+data.o: $(hdrdir)/ruby/internal/core/rdata.h
+data.o: $(hdrdir)/ruby/internal/core/rfile.h
+data.o: $(hdrdir)/ruby/internal/core/rhash.h
+data.o: $(hdrdir)/ruby/internal/core/robject.h
+data.o: $(hdrdir)/ruby/internal/core/rregexp.h
+data.o: $(hdrdir)/ruby/internal/core/rstring.h
+data.o: $(hdrdir)/ruby/internal/core/rstruct.h
+data.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
+data.o: $(hdrdir)/ruby/internal/ctype.h
+data.o: $(hdrdir)/ruby/internal/dllexport.h
+data.o: $(hdrdir)/ruby/internal/dosish.h
+data.o: $(hdrdir)/ruby/internal/error.h
+data.o: $(hdrdir)/ruby/internal/eval.h
+data.o: $(hdrdir)/ruby/internal/event.h
+data.o: $(hdrdir)/ruby/internal/fl_type.h
+data.o: $(hdrdir)/ruby/internal/gc.h
+data.o: $(hdrdir)/ruby/internal/glob.h
+data.o: $(hdrdir)/ruby/internal/globals.h
+data.o: $(hdrdir)/ruby/internal/has/attribute.h
+data.o: $(hdrdir)/ruby/internal/has/builtin.h
+data.o: $(hdrdir)/ruby/internal/has/c_attribute.h
+data.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h
+data.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h
+data.o: $(hdrdir)/ruby/internal/has/extension.h
+data.o: $(hdrdir)/ruby/internal/has/feature.h
+data.o: $(hdrdir)/ruby/internal/has/warning.h
+data.o: $(hdrdir)/ruby/internal/intern/array.h
+data.o: $(hdrdir)/ruby/internal/intern/bignum.h
+data.o: $(hdrdir)/ruby/internal/intern/class.h
+data.o: $(hdrdir)/ruby/internal/intern/compar.h
+data.o: $(hdrdir)/ruby/internal/intern/complex.h
+data.o: $(hdrdir)/ruby/internal/intern/cont.h
+data.o: $(hdrdir)/ruby/internal/intern/dir.h
+data.o: $(hdrdir)/ruby/internal/intern/enum.h
+data.o: $(hdrdir)/ruby/internal/intern/enumerator.h
+data.o: $(hdrdir)/ruby/internal/intern/error.h
+data.o: $(hdrdir)/ruby/internal/intern/eval.h
+data.o: $(hdrdir)/ruby/internal/intern/file.h
+data.o: $(hdrdir)/ruby/internal/intern/hash.h
+data.o: $(hdrdir)/ruby/internal/intern/io.h
+data.o: $(hdrdir)/ruby/internal/intern/load.h
+data.o: $(hdrdir)/ruby/internal/intern/marshal.h
+data.o: $(hdrdir)/ruby/internal/intern/numeric.h
+data.o: $(hdrdir)/ruby/internal/intern/object.h
+data.o: $(hdrdir)/ruby/internal/intern/parse.h
+data.o: $(hdrdir)/ruby/internal/intern/proc.h
+data.o: $(hdrdir)/ruby/internal/intern/process.h
+data.o: $(hdrdir)/ruby/internal/intern/random.h
+data.o: $(hdrdir)/ruby/internal/intern/range.h
+data.o: $(hdrdir)/ruby/internal/intern/rational.h
+data.o: $(hdrdir)/ruby/internal/intern/re.h
+data.o: $(hdrdir)/ruby/internal/intern/ruby.h
+data.o: $(hdrdir)/ruby/internal/intern/select.h
+data.o: $(hdrdir)/ruby/internal/intern/select/largesize.h
+data.o: $(hdrdir)/ruby/internal/intern/signal.h
+data.o: $(hdrdir)/ruby/internal/intern/sprintf.h
+data.o: $(hdrdir)/ruby/internal/intern/string.h
+data.o: $(hdrdir)/ruby/internal/intern/struct.h
+data.o: $(hdrdir)/ruby/internal/intern/thread.h
+data.o: $(hdrdir)/ruby/internal/intern/time.h
+data.o: $(hdrdir)/ruby/internal/intern/variable.h
+data.o: $(hdrdir)/ruby/internal/intern/vm.h
+data.o: $(hdrdir)/ruby/internal/interpreter.h
+data.o: $(hdrdir)/ruby/internal/iterator.h
+data.o: $(hdrdir)/ruby/internal/memory.h
+data.o: $(hdrdir)/ruby/internal/method.h
+data.o: $(hdrdir)/ruby/internal/module.h
+data.o: $(hdrdir)/ruby/internal/newobj.h
+data.o: $(hdrdir)/ruby/internal/scan_args.h
+data.o: $(hdrdir)/ruby/internal/special_consts.h
+data.o: $(hdrdir)/ruby/internal/static_assert.h
+data.o: $(hdrdir)/ruby/internal/stdalign.h
+data.o: $(hdrdir)/ruby/internal/stdbool.h
+data.o: $(hdrdir)/ruby/internal/stdckdint.h
+data.o: $(hdrdir)/ruby/internal/symbol.h
+data.o: $(hdrdir)/ruby/internal/value.h
+data.o: $(hdrdir)/ruby/internal/value_type.h
+data.o: $(hdrdir)/ruby/internal/variable.h
+data.o: $(hdrdir)/ruby/internal/warning_push.h
+data.o: $(hdrdir)/ruby/internal/xmalloc.h
+data.o: $(hdrdir)/ruby/missing.h
+data.o: $(hdrdir)/ruby/ruby.h
+data.o: $(hdrdir)/ruby/st.h
+data.o: $(hdrdir)/ruby/subst.h
+data.o: data.c
duplicate.o: $(RUBY_EXTCONF_H)
duplicate.o: $(arch_hdrdir)/ruby/config.h
duplicate.o: $(hdrdir)/ruby.h
@@ -147,6 +307,7 @@ duplicate.o: $(hdrdir)/ruby/internal/special_consts.h
duplicate.o: $(hdrdir)/ruby/internal/static_assert.h
duplicate.o: $(hdrdir)/ruby/internal/stdalign.h
duplicate.o: $(hdrdir)/ruby/internal/stdbool.h
+duplicate.o: $(hdrdir)/ruby/internal/stdckdint.h
duplicate.o: $(hdrdir)/ruby/internal/symbol.h
duplicate.o: $(hdrdir)/ruby/internal/value.h
duplicate.o: $(hdrdir)/ruby/internal/value_type.h
@@ -306,6 +467,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h
init.o: $(hdrdir)/ruby/internal/static_assert.h
init.o: $(hdrdir)/ruby/internal/stdalign.h
init.o: $(hdrdir)/ruby/internal/stdbool.h
+init.o: $(hdrdir)/ruby/internal/stdckdint.h
init.o: $(hdrdir)/ruby/internal/symbol.h
init.o: $(hdrdir)/ruby/internal/value.h
init.o: $(hdrdir)/ruby/internal/value_type.h
@@ -465,6 +627,7 @@ len.o: $(hdrdir)/ruby/internal/special_consts.h
len.o: $(hdrdir)/ruby/internal/static_assert.h
len.o: $(hdrdir)/ruby/internal/stdalign.h
len.o: $(hdrdir)/ruby/internal/stdbool.h
+len.o: $(hdrdir)/ruby/internal/stdckdint.h
len.o: $(hdrdir)/ruby/internal/symbol.h
len.o: $(hdrdir)/ruby/internal/value.h
len.o: $(hdrdir)/ruby/internal/value_type.h
@@ -624,6 +787,7 @@ member.o: $(hdrdir)/ruby/internal/special_consts.h
member.o: $(hdrdir)/ruby/internal/static_assert.h
member.o: $(hdrdir)/ruby/internal/stdalign.h
member.o: $(hdrdir)/ruby/internal/stdbool.h
+member.o: $(hdrdir)/ruby/internal/stdckdint.h
member.o: $(hdrdir)/ruby/internal/symbol.h
member.o: $(hdrdir)/ruby/internal/value.h
member.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/struct/member.c b/ext/-test-/struct/member.c
index f5400fe477..29ddff93e8 100644
--- a/ext/-test-/struct/member.c
+++ b/ext/-test-/struct/member.c
@@ -6,7 +6,7 @@ bug_struct_get(VALUE obj, VALUE name)
ID id = rb_check_id(&name);
if (!id) {
- rb_name_error_str(name, "`%"PRIsVALUE"' is not a struct member", name);
+ rb_name_error_str(name, "'%"PRIsVALUE"' is not a struct member", name);
}
return rb_struct_getmember(obj, id);
}
diff --git a/ext/-test-/symbol/depend b/ext/-test-/symbol/depend
index dd1b5c305f..7c76596fdf 100644
--- a/ext/-test-/symbol/depend
+++ b/ext/-test-/symbol/depend
@@ -147,6 +147,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h
init.o: $(hdrdir)/ruby/internal/static_assert.h
init.o: $(hdrdir)/ruby/internal/stdalign.h
init.o: $(hdrdir)/ruby/internal/stdbool.h
+init.o: $(hdrdir)/ruby/internal/stdckdint.h
init.o: $(hdrdir)/ruby/internal/symbol.h
init.o: $(hdrdir)/ruby/internal/value.h
init.o: $(hdrdir)/ruby/internal/value_type.h
@@ -306,6 +307,7 @@ type.o: $(hdrdir)/ruby/internal/special_consts.h
type.o: $(hdrdir)/ruby/internal/static_assert.h
type.o: $(hdrdir)/ruby/internal/stdalign.h
type.o: $(hdrdir)/ruby/internal/stdbool.h
+type.o: $(hdrdir)/ruby/internal/stdckdint.h
type.o: $(hdrdir)/ruby/internal/symbol.h
type.o: $(hdrdir)/ruby/internal/value.h
type.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/thread/instrumentation/depend b/ext/-test-/thread/instrumentation/depend
index b03f51870f..a37e4d5675 100644
--- a/ext/-test-/thread/instrumentation/depend
+++ b/ext/-test-/thread/instrumentation/depend
@@ -147,6 +147,7 @@ instrumentation.o: $(hdrdir)/ruby/internal/special_consts.h
instrumentation.o: $(hdrdir)/ruby/internal/static_assert.h
instrumentation.o: $(hdrdir)/ruby/internal/stdalign.h
instrumentation.o: $(hdrdir)/ruby/internal/stdbool.h
+instrumentation.o: $(hdrdir)/ruby/internal/stdckdint.h
instrumentation.o: $(hdrdir)/ruby/internal/symbol.h
instrumentation.o: $(hdrdir)/ruby/internal/value.h
instrumentation.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/thread/instrumentation/instrumentation.c b/ext/-test-/thread/instrumentation/instrumentation.c
index edb8738a29..d81bc0f2a7 100644
--- a/ext/-test-/thread/instrumentation/instrumentation.c
+++ b/ext/-test-/thread/instrumentation/instrumentation.c
@@ -2,85 +2,137 @@
#include "ruby/atomic.h"
#include "ruby/thread.h"
-static rb_atomic_t started_count = 0;
-static rb_atomic_t ready_count = 0;
-static rb_atomic_t resumed_count = 0;
-static rb_atomic_t suspended_count = 0;
-static rb_atomic_t exited_count = 0;
-
#ifndef RB_THREAD_LOCAL_SPECIFIER
# define RB_THREAD_LOCAL_SPECIFIER
#endif
-static RB_THREAD_LOCAL_SPECIFIER unsigned int local_ready_count = 0;
-static RB_THREAD_LOCAL_SPECIFIER unsigned int local_resumed_count = 0;
-static RB_THREAD_LOCAL_SPECIFIER unsigned int local_suspended_count = 0;
+static VALUE last_thread = Qnil;
+static VALUE timeline_value = Qnil;
+
+struct thread_event {
+ VALUE thread;
+ rb_event_flag_t event;
+};
+
+#define MAX_EVENTS 1024
+static struct thread_event event_timeline[MAX_EVENTS];
+static rb_atomic_t timeline_cursor;
static void
-ex_callback(rb_event_flag_t event, const rb_internal_thread_event_data_t *event_data, void *user_data)
+event_timeline_gc_mark(void *ptr) {
+ rb_atomic_t cursor;
+ for (cursor = 0; cursor < timeline_cursor; cursor++) {
+ rb_gc_mark(event_timeline[cursor].thread);
+ }
+}
+
+static const rb_data_type_t event_timeline_type = {
+ "TestThreadInstrumentation/event_timeline",
+ {event_timeline_gc_mark, NULL, NULL,},
+ 0, 0,
+ RUBY_TYPED_FREE_IMMEDIATELY,
+};
+
+static void
+reset_timeline(void)
+{
+ timeline_cursor = 0;
+ memset(event_timeline, 0, sizeof(struct thread_event) * MAX_EVENTS);
+}
+
+static rb_event_flag_t
+find_last_event(VALUE thread)
+{
+ rb_atomic_t cursor = timeline_cursor;
+ if (cursor) {
+ do {
+ if (event_timeline[cursor].thread == thread){
+ return event_timeline[cursor].event;
+ }
+ cursor--;
+ } while (cursor > 0);
+ }
+ return 0;
+}
+
+static const char *
+event_name(rb_event_flag_t event)
{
switch (event) {
case RUBY_INTERNAL_THREAD_EVENT_STARTED:
- RUBY_ATOMIC_INC(started_count);
- break;
+ return "started";
case RUBY_INTERNAL_THREAD_EVENT_READY:
- RUBY_ATOMIC_INC(ready_count);
- local_ready_count++;
- break;
+ return "ready";
case RUBY_INTERNAL_THREAD_EVENT_RESUMED:
- RUBY_ATOMIC_INC(resumed_count);
- local_resumed_count++;
- break;
+ return "resumed";
case RUBY_INTERNAL_THREAD_EVENT_SUSPENDED:
- RUBY_ATOMIC_INC(suspended_count);
- local_suspended_count++;
- break;
+ return "suspended";
case RUBY_INTERNAL_THREAD_EVENT_EXITED:
- RUBY_ATOMIC_INC(exited_count);
- break;
+ return "exited";
}
+ return "no-event";
}
-static rb_internal_thread_event_hook_t * single_hook = NULL;
-
-static VALUE
-thread_counters(VALUE thread)
+static void
+unexpected(bool strict, const char *format, VALUE thread, rb_event_flag_t last_event)
{
- VALUE array = rb_ary_new2(5);
- rb_ary_push(array, UINT2NUM(started_count));
- rb_ary_push(array, UINT2NUM(ready_count));
- rb_ary_push(array, UINT2NUM(resumed_count));
- rb_ary_push(array, UINT2NUM(suspended_count));
- rb_ary_push(array, UINT2NUM(exited_count));
- return array;
+ const char *last_event_name = event_name(last_event);
+ if (strict) {
+ rb_bug(format, thread, last_event_name);
+ }
+ else {
+ fprintf(stderr, format, thread, last_event_name);
+ fprintf(stderr, "\n");
+ }
}
-static VALUE
-thread_local_counters(VALUE thread)
+static void
+ex_callback(rb_event_flag_t event, const rb_internal_thread_event_data_t *event_data, void *user_data)
{
- VALUE array = rb_ary_new2(3);
- rb_ary_push(array, UINT2NUM(local_ready_count));
- rb_ary_push(array, UINT2NUM(local_resumed_count));
- rb_ary_push(array, UINT2NUM(local_suspended_count));
- return array;
-}
+ rb_event_flag_t last_event = find_last_event(event_data->thread);
+ bool strict = (bool)user_data;
-static VALUE
-thread_reset_counters(VALUE thread)
-{
- RUBY_ATOMIC_SET(started_count, 0);
- RUBY_ATOMIC_SET(ready_count, 0);
- RUBY_ATOMIC_SET(resumed_count, 0);
- RUBY_ATOMIC_SET(suspended_count, 0);
- RUBY_ATOMIC_SET(exited_count, 0);
- local_ready_count = 0;
- local_resumed_count = 0;
- local_suspended_count = 0;
- return Qtrue;
+ if (last_event != 0) {
+ switch (event) {
+ case RUBY_INTERNAL_THREAD_EVENT_STARTED:
+ unexpected(strict, "[thread=%"PRIxVALUE"] `started` event can't be preceded by `%s`", event_data->thread, last_event);
+ break;
+ case RUBY_INTERNAL_THREAD_EVENT_READY:
+ if (last_event != RUBY_INTERNAL_THREAD_EVENT_STARTED && last_event != RUBY_INTERNAL_THREAD_EVENT_SUSPENDED) {
+ unexpected(strict, "[thread=%"PRIxVALUE"] `ready` must be preceded by `started` or `suspended`, got: `%s`", event_data->thread, last_event);
+ }
+ break;
+ case RUBY_INTERNAL_THREAD_EVENT_RESUMED:
+ if (last_event != RUBY_INTERNAL_THREAD_EVENT_READY) {
+ unexpected(strict, "[thread=%"PRIxVALUE"] `resumed` must be preceded by `ready`, got: `%s`", event_data->thread, last_event);
+ }
+ break;
+ case RUBY_INTERNAL_THREAD_EVENT_SUSPENDED:
+ if (last_event != RUBY_INTERNAL_THREAD_EVENT_RESUMED) {
+ unexpected(strict, "[thread=%"PRIxVALUE"] `suspended` must be preceded by `resumed`, got: `%s`", event_data->thread, last_event);
+ }
+ break;
+ case RUBY_INTERNAL_THREAD_EVENT_EXITED:
+ if (last_event != RUBY_INTERNAL_THREAD_EVENT_RESUMED && last_event != RUBY_INTERNAL_THREAD_EVENT_SUSPENDED) {
+ unexpected(strict, "[thread=%"PRIxVALUE"] `exited` must be preceded by `resumed` or `suspended`, got: `%s`", event_data->thread, last_event);
+ }
+ break;
+ }
+ }
+
+ rb_atomic_t cursor = RUBY_ATOMIC_FETCH_ADD(timeline_cursor, 1);
+ if (cursor >= MAX_EVENTS) {
+ rb_bug("TestThreadInstrumentation: ran out of event_timeline space");
+ }
+
+ event_timeline[cursor].thread = event_data->thread;
+ event_timeline[cursor].event = event;
}
+static rb_internal_thread_event_hook_t * single_hook = NULL;
+
static VALUE
-thread_register_callback(VALUE thread)
+thread_register_callback(VALUE thread, VALUE strict)
{
single_hook = rb_internal_thread_add_event_hook(
ex_callback,
@@ -89,13 +141,33 @@ thread_register_callback(VALUE thread)
RUBY_INTERNAL_THREAD_EVENT_RESUMED |
RUBY_INTERNAL_THREAD_EVENT_SUSPENDED |
RUBY_INTERNAL_THREAD_EVENT_EXITED,
- NULL
+ (void *)RTEST(strict)
);
return Qnil;
}
static VALUE
+event_symbol(rb_event_flag_t event)
+{
+ switch (event) {
+ case RUBY_INTERNAL_THREAD_EVENT_STARTED:
+ return rb_id2sym(rb_intern("started"));
+ case RUBY_INTERNAL_THREAD_EVENT_READY:
+ return rb_id2sym(rb_intern("ready"));
+ case RUBY_INTERNAL_THREAD_EVENT_RESUMED:
+ return rb_id2sym(rb_intern("resumed"));
+ case RUBY_INTERNAL_THREAD_EVENT_SUSPENDED:
+ return rb_id2sym(rb_intern("suspended"));
+ case RUBY_INTERNAL_THREAD_EVENT_EXITED:
+ return rb_id2sym(rb_intern("exited"));
+ default:
+ rb_bug("TestThreadInstrumentation: Unexpected event");
+ break;
+ }
+}
+
+static VALUE
thread_unregister_callback(VALUE thread)
{
if (single_hook) {
@@ -103,7 +175,18 @@ thread_unregister_callback(VALUE thread)
single_hook = NULL;
}
- return Qnil;
+ VALUE events = rb_ary_new_capa(timeline_cursor);
+ rb_atomic_t cursor;
+ for (cursor = 0; cursor < timeline_cursor; cursor++) {
+ VALUE pair = rb_ary_new_capa(2);
+ rb_ary_push(pair, event_timeline[cursor].thread);
+ rb_ary_push(pair, event_symbol(event_timeline[cursor].event));
+ rb_ary_push(events, pair);
+ }
+
+ reset_timeline();
+
+ return events;
}
static VALUE
@@ -127,10 +210,11 @@ Init_instrumentation(void)
{
VALUE mBug = rb_define_module("Bug");
VALUE klass = rb_define_module_under(mBug, "ThreadInstrumentation");
- rb_define_singleton_method(klass, "counters", thread_counters, 0);
- rb_define_singleton_method(klass, "local_counters", thread_local_counters, 0);
- rb_define_singleton_method(klass, "reset_counters", thread_reset_counters, 0);
- rb_define_singleton_method(klass, "register_callback", thread_register_callback, 0);
+ rb_global_variable(&timeline_value);
+ timeline_value = TypedData_Wrap_Struct(0, &event_timeline_type, 0);
+
+ rb_global_variable(&last_thread);
+ rb_define_singleton_method(klass, "register_callback", thread_register_callback, 1);
rb_define_singleton_method(klass, "unregister_callback", thread_unregister_callback, 0);
rb_define_singleton_method(klass, "register_and_unregister_callbacks", thread_register_and_unregister_callback, 0);
}
diff --git a/ext/-test-/thread/lock_native_thread/extconf.rb b/ext/-test-/thread/lock_native_thread/extconf.rb
new file mode 100644
index 0000000000..832bfde01a
--- /dev/null
+++ b/ext/-test-/thread/lock_native_thread/extconf.rb
@@ -0,0 +1,2 @@
+# frozen_string_literal: false
+create_makefile("-test-/thread/lock_native_thread")
diff --git a/ext/-test-/thread/lock_native_thread/lock_native_thread.c b/ext/-test-/thread/lock_native_thread/lock_native_thread.c
new file mode 100644
index 0000000000..2eb75809a9
--- /dev/null
+++ b/ext/-test-/thread/lock_native_thread/lock_native_thread.c
@@ -0,0 +1,50 @@
+
+#include "ruby/ruby.h"
+#include "ruby/thread.h"
+
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+
+static pthread_key_t tls_key;
+
+static VALUE
+get_tls(VALUE self)
+{
+ return (VALUE)pthread_getspecific(tls_key);
+}
+
+static VALUE
+set_tls(VALUE self, VALUE vn)
+{
+ pthread_setspecific(tls_key, (void *)vn);
+ return Qnil;
+}
+
+static VALUE
+lock_native_thread(VALUE self)
+{
+ return rb_thread_lock_native_thread() ? Qtrue : Qfalse;
+}
+
+void
+Init_lock_native_thread(void)
+{
+ int r;
+
+ if ((r = pthread_key_create(&tls_key, NULL)) != 0) {
+ rb_bug("pthread_key_create() returns %d", r);
+ }
+ pthread_setspecific(tls_key, NULL);
+
+ rb_define_method(rb_cThread, "lock_native_thread", lock_native_thread, 0);
+ rb_define_method(rb_cThread, "get_tls", get_tls, 0);
+ rb_define_method(rb_cThread, "set_tls", set_tls, 1);
+}
+
+#else // HAVE_PTHREAD_H
+void
+Init_lock_native_thread(void)
+{
+ // do nothing
+}
+#endif // HAVE_PTHREAD_H
diff --git a/ext/-test-/thread_fd/depend b/ext/-test-/thread_fd/depend
index d4cc772526..0fda9f6dbf 100644
--- a/ext/-test-/thread_fd/depend
+++ b/ext/-test-/thread_fd/depend
@@ -146,6 +146,7 @@ thread_fd.o: $(hdrdir)/ruby/internal/special_consts.h
thread_fd.o: $(hdrdir)/ruby/internal/static_assert.h
thread_fd.o: $(hdrdir)/ruby/internal/stdalign.h
thread_fd.o: $(hdrdir)/ruby/internal/stdbool.h
+thread_fd.o: $(hdrdir)/ruby/internal/stdckdint.h
thread_fd.o: $(hdrdir)/ruby/internal/symbol.h
thread_fd.o: $(hdrdir)/ruby/internal/value.h
thread_fd.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/time/depend b/ext/-test-/time/depend
index c015588b09..5ed791bcc5 100644
--- a/ext/-test-/time/depend
+++ b/ext/-test-/time/depend
@@ -147,6 +147,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h
init.o: $(hdrdir)/ruby/internal/static_assert.h
init.o: $(hdrdir)/ruby/internal/stdalign.h
init.o: $(hdrdir)/ruby/internal/stdbool.h
+init.o: $(hdrdir)/ruby/internal/stdckdint.h
init.o: $(hdrdir)/ruby/internal/symbol.h
init.o: $(hdrdir)/ruby/internal/value.h
init.o: $(hdrdir)/ruby/internal/value_type.h
@@ -307,6 +308,7 @@ leap_second.o: $(hdrdir)/ruby/internal/special_consts.h
leap_second.o: $(hdrdir)/ruby/internal/static_assert.h
leap_second.o: $(hdrdir)/ruby/internal/stdalign.h
leap_second.o: $(hdrdir)/ruby/internal/stdbool.h
+leap_second.o: $(hdrdir)/ruby/internal/stdckdint.h
leap_second.o: $(hdrdir)/ruby/internal/symbol.h
leap_second.o: $(hdrdir)/ruby/internal/value.h
leap_second.o: $(hdrdir)/ruby/internal/value_type.h
@@ -470,6 +472,7 @@ new.o: $(hdrdir)/ruby/internal/special_consts.h
new.o: $(hdrdir)/ruby/internal/static_assert.h
new.o: $(hdrdir)/ruby/internal/stdalign.h
new.o: $(hdrdir)/ruby/internal/stdbool.h
+new.o: $(hdrdir)/ruby/internal/stdckdint.h
new.o: $(hdrdir)/ruby/internal/symbol.h
new.o: $(hdrdir)/ruby/internal/value.h
new.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/tracepoint/depend b/ext/-test-/tracepoint/depend
index 396004926e..133663b3bd 100644
--- a/ext/-test-/tracepoint/depend
+++ b/ext/-test-/tracepoint/depend
@@ -147,6 +147,7 @@ gc_hook.o: $(hdrdir)/ruby/internal/special_consts.h
gc_hook.o: $(hdrdir)/ruby/internal/static_assert.h
gc_hook.o: $(hdrdir)/ruby/internal/stdalign.h
gc_hook.o: $(hdrdir)/ruby/internal/stdbool.h
+gc_hook.o: $(hdrdir)/ruby/internal/stdckdint.h
gc_hook.o: $(hdrdir)/ruby/internal/symbol.h
gc_hook.o: $(hdrdir)/ruby/internal/value.h
gc_hook.o: $(hdrdir)/ruby/internal/value_type.h
@@ -306,6 +307,7 @@ tracepoint.o: $(hdrdir)/ruby/internal/special_consts.h
tracepoint.o: $(hdrdir)/ruby/internal/static_assert.h
tracepoint.o: $(hdrdir)/ruby/internal/stdalign.h
tracepoint.o: $(hdrdir)/ruby/internal/stdbool.h
+tracepoint.o: $(hdrdir)/ruby/internal/stdckdint.h
tracepoint.o: $(hdrdir)/ruby/internal/symbol.h
tracepoint.o: $(hdrdir)/ruby/internal/value.h
tracepoint.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/tracepoint/gc_hook.c b/ext/-test-/tracepoint/gc_hook.c
index a3f4e7f68a..54c06c54a5 100644
--- a/ext/-test-/tracepoint/gc_hook.c
+++ b/ext/-test-/tracepoint/gc_hook.c
@@ -33,7 +33,9 @@ gc_start_end_i(VALUE tpval, void *data)
}
if (invoking == 0) {
- rb_postponed_job_register(0, invoke_proc, data);
+ /* will overwrite the existing handle with new data on the second and subsequent call */
+ rb_postponed_job_handle_t h = rb_postponed_job_preregister(0, invoke_proc, data);
+ rb_postponed_job_trigger(h);
}
}
diff --git a/ext/-test-/typeddata/depend b/ext/-test-/typeddata/depend
index cbeafa8000..6c0847c82d 100644
--- a/ext/-test-/typeddata/depend
+++ b/ext/-test-/typeddata/depend
@@ -147,6 +147,7 @@ typeddata.o: $(hdrdir)/ruby/internal/special_consts.h
typeddata.o: $(hdrdir)/ruby/internal/static_assert.h
typeddata.o: $(hdrdir)/ruby/internal/stdalign.h
typeddata.o: $(hdrdir)/ruby/internal/stdbool.h
+typeddata.o: $(hdrdir)/ruby/internal/stdckdint.h
typeddata.o: $(hdrdir)/ruby/internal/symbol.h
typeddata.o: $(hdrdir)/ruby/internal/value.h
typeddata.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/vm/depend b/ext/-test-/vm/depend
index f0b3f3b1f4..422b40a32d 100644
--- a/ext/-test-/vm/depend
+++ b/ext/-test-/vm/depend
@@ -146,6 +146,7 @@ at_exit.o: $(hdrdir)/ruby/internal/special_consts.h
at_exit.o: $(hdrdir)/ruby/internal/static_assert.h
at_exit.o: $(hdrdir)/ruby/internal/stdalign.h
at_exit.o: $(hdrdir)/ruby/internal/stdbool.h
+at_exit.o: $(hdrdir)/ruby/internal/stdckdint.h
at_exit.o: $(hdrdir)/ruby/internal/symbol.h
at_exit.o: $(hdrdir)/ruby/internal/value.h
at_exit.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/-test-/wait/depend b/ext/-test-/wait/depend
index 2e4887559c..7997a16709 100644
--- a/ext/-test-/wait/depend
+++ b/ext/-test-/wait/depend
@@ -156,6 +156,7 @@ wait.o: $(hdrdir)/ruby/internal/special_consts.h
wait.o: $(hdrdir)/ruby/internal/static_assert.h
wait.o: $(hdrdir)/ruby/internal/stdalign.h
wait.o: $(hdrdir)/ruby/internal/stdbool.h
+wait.o: $(hdrdir)/ruby/internal/stdckdint.h
wait.o: $(hdrdir)/ruby/internal/symbol.h
wait.o: $(hdrdir)/ruby/internal/value.h
wait.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/.document b/ext/.document
index f2fa5b5ba2..0efd511a61 100644
--- a/ext/.document
+++ b/ext/.document
@@ -29,8 +29,7 @@ fiddle/pinned.c
fiddle/pointer.c
fiddle/handle.c
fiddle/lib
-io/console/console.c
-io/console/lib
+io/console/
io/nonblock/nonblock.c
io/wait/wait.c
json/generator/generator.c
diff --git a/ext/Setup.atheos b/ext/Setup.atheos
index 6a8b9903af..91f73f32f9 100644
--- a/ext/Setup.atheos
+++ b/ext/Setup.atheos
@@ -13,7 +13,7 @@ io/wait
nkf
#openssl
pty
-racc/cparse
+#racc/cparse
readline
ripper
socket
diff --git a/ext/Setup.nt b/ext/Setup.nt
index 067f153d36..1278f183e4 100644
--- a/ext/Setup.nt
+++ b/ext/Setup.nt
@@ -14,7 +14,7 @@ fcntl
nkf
#openssl
#pty
-racc/cparse
+#racc/cparse
#readline
#ripper
socket
diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c
deleted file mode 100644
index 8cbe686e71..0000000000
--- a/ext/bigdecimal/bigdecimal.c
+++ /dev/null
@@ -1,7730 +0,0 @@
-/*
- *
- * Ruby BigDecimal(Variable decimal precision) extension library.
- *
- * Copyright(C) 2002 by Shigeo Kobayashi(shigeo@tinyforest.gr.jp)
- *
- */
-
-/* #define BIGDECIMAL_DEBUG 1 */
-
-#include "bigdecimal.h"
-#include "ruby/util.h"
-
-#ifndef BIGDECIMAL_DEBUG
-# undef NDEBUG
-# define NDEBUG
-#endif
-#include <assert.h>
-
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <math.h>
-
-#ifdef HAVE_IEEEFP_H
-#include <ieeefp.h>
-#endif
-
-#include "bits.h"
-#include "static_assert.h"
-
-#define BIGDECIMAL_VERSION "3.1.4"
-
-/* #define ENABLE_NUMERIC_STRING */
-
-#define SIGNED_VALUE_MAX INTPTR_MAX
-#define SIGNED_VALUE_MIN INTPTR_MIN
-#define MUL_OVERFLOW_SIGNED_VALUE_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, SIGNED_VALUE_MIN, SIGNED_VALUE_MAX)
-
-VALUE rb_cBigDecimal;
-VALUE rb_mBigMath;
-
-static ID id_BigDecimal_exception_mode;
-static ID id_BigDecimal_rounding_mode;
-static ID id_BigDecimal_precision_limit;
-
-static ID id_up;
-static ID id_down;
-static ID id_truncate;
-static ID id_half_up;
-static ID id_default;
-static ID id_half_down;
-static ID id_half_even;
-static ID id_banker;
-static ID id_ceiling;
-static ID id_ceil;
-static ID id_floor;
-static ID id_to_r;
-static ID id_eq;
-static ID id_half;
-
-#define RBD_NUM_ROUNDING_MODES 11
-
-static struct {
- ID id;
- uint8_t mode;
-} rbd_rounding_modes[RBD_NUM_ROUNDING_MODES];
-
-/* MACRO's to guard objects from GC by keeping them in stack */
-#ifdef RBIMPL_ATTR_MAYBE_UNUSED
-#define ENTER(n) RBIMPL_ATTR_MAYBE_UNUSED() volatile VALUE vStack[n];int iStack=0
-#else
-#define ENTER(n) volatile VALUE RB_UNUSED_VAR(vStack[n]);int iStack=0
-#endif
-#define PUSH(x) (vStack[iStack++] = (VALUE)(x))
-#define SAVE(p) PUSH((p)->obj)
-#define GUARD_OBJ(p,y) ((p)=(y), SAVE(p))
-
-#define BASE_FIG BIGDECIMAL_COMPONENT_FIGURES
-#define BASE BIGDECIMAL_BASE
-
-#define HALF_BASE (BASE/2)
-#define BASE1 (BASE/10)
-
-#define LOG10_2 0.3010299956639812
-
-#ifndef RRATIONAL_ZERO_P
-# define RRATIONAL_ZERO_P(x) (FIXNUM_P(rb_rational_num(x)) && \
- FIX2LONG(rb_rational_num(x)) == 0)
-#endif
-
-#ifndef RRATIONAL_NEGATIVE_P
-# define RRATIONAL_NEGATIVE_P(x) RTEST(rb_funcall((x), '<', 1, INT2FIX(0)))
-#endif
-
-#ifndef DECIMAL_SIZE_OF_BITS
-#define DECIMAL_SIZE_OF_BITS(n) (((n) * 3010 + 9998) / 9999)
-/* an approximation of ceil(n * log10(2)), upto 65536 at least */
-#endif
-
-#ifdef PRIsVALUE
-# define RB_OBJ_CLASSNAME(obj) rb_obj_class(obj)
-# define RB_OBJ_STRING(obj) (obj)
-#else
-# define PRIsVALUE "s"
-# define RB_OBJ_CLASSNAME(obj) rb_obj_classname(obj)
-# define RB_OBJ_STRING(obj) StringValueCStr(obj)
-#endif
-
-#ifndef MAYBE_UNUSED
-# define MAYBE_UNUSED(x) x
-#endif
-
-#define BIGDECIMAL_POSITIVE_P(bd) ((bd)->sign > 0)
-#define BIGDECIMAL_NEGATIVE_P(bd) ((bd)->sign < 0)
-
-/*
- * ================== Memory allocation ============================
- */
-
-#ifdef BIGDECIMAL_DEBUG
-static size_t rbd_allocation_count = 0; /* Memory allocation counter */
-static inline void
-atomic_allocation_count_inc(void)
-{
- RUBY_ATOMIC_SIZE_INC(rbd_allocation_count);
-}
-static inline void
-atomic_allocation_count_dec_nounderflow(void)
-{
- if (rbd_allocation_count == 0) return;
- RUBY_ATOMIC_SIZE_DEC(rbd_allocation_count);
-}
-static void
-check_allocation_count_nonzero(void)
-{
- if (rbd_allocation_count != 0) return;
- rb_bug("[bigdecimal][rbd_free_struct] Too many memory free calls");
-}
-#else
-# define atomic_allocation_count_inc() /* nothing */
-# define atomic_allocation_count_dec_nounderflow() /* nothing */
-# define check_allocation_count_nonzero() /* nothing */
-#endif /* BIGDECIMAL_DEBUG */
-
-PUREFUNC(static inline size_t rbd_struct_size(size_t const));
-
-static inline size_t
-rbd_struct_size(size_t const internal_digits)
-{
- size_t const frac_len = (internal_digits == 0) ? 1 : internal_digits;
- return offsetof(Real, frac) + frac_len * sizeof(DECDIG);
-}
-
-static inline Real *
-rbd_allocate_struct(size_t const internal_digits)
-{
- size_t const size = rbd_struct_size(internal_digits);
- Real *real = ruby_xcalloc(1, size);
- atomic_allocation_count_inc();
- real->MaxPrec = internal_digits;
- return real;
-}
-
-static size_t
-rbd_calculate_internal_digits(size_t const digits, bool limit_precision)
-{
- size_t const len = roomof(digits, BASE_FIG);
- if (limit_precision) {
- size_t const prec_limit = VpGetPrecLimit();
- if (prec_limit > 0) {
- /* NOTE: 2 more digits for rounding and division */
- size_t const max_len = roomof(prec_limit, BASE_FIG) + 2;
- if (len > max_len)
- return max_len;
- }
- }
-
- return len;
-}
-
-static inline Real *
-rbd_allocate_struct_decimal_digits(size_t const decimal_digits, bool limit_precision)
-{
- size_t const internal_digits = rbd_calculate_internal_digits(decimal_digits, limit_precision);
- return rbd_allocate_struct(internal_digits);
-}
-
-static VALUE BigDecimal_wrap_struct(VALUE obj, Real *vp);
-
-static Real *
-rbd_reallocate_struct(Real *real, size_t const internal_digits)
-{
- size_t const size = rbd_struct_size(internal_digits);
- VALUE obj = real ? real->obj : 0;
- Real *new_real = (Real *)ruby_xrealloc(real, size);
- new_real->MaxPrec = internal_digits;
- if (obj) {
- new_real->obj = 0;
- BigDecimal_wrap_struct(obj, new_real);
- }
- return new_real;
-}
-
-static void
-rbd_free_struct(Real *real)
-{
- if (real != NULL) {
- check_allocation_count_nonzero();
- ruby_xfree(real);
- atomic_allocation_count_dec_nounderflow();
- }
-}
-
-#define NewZero rbd_allocate_struct_zero
-static Real *
-rbd_allocate_struct_zero(int sign, size_t const digits, bool limit_precision)
-{
- Real *real = rbd_allocate_struct_decimal_digits(digits, limit_precision);
- VpSetZero(real, sign);
- return real;
-}
-
-MAYBE_UNUSED(static inline Real * rbd_allocate_struct_zero_limited(int sign, size_t const digits));
-#define NewZeroLimited rbd_allocate_struct_zero_limited
-static inline Real *
-rbd_allocate_struct_zero_limited(int sign, size_t const digits)
-{
- return rbd_allocate_struct_zero(sign, digits, true);
-}
-
-MAYBE_UNUSED(static inline Real * rbd_allocate_struct_zero_nolimit(int sign, size_t const digits));
-#define NewZeroNolimit rbd_allocate_struct_zero_nolimit
-static inline Real *
-rbd_allocate_struct_zero_nolimit(int sign, size_t const digits)
-{
- return rbd_allocate_struct_zero(sign, digits, false);
-}
-
-#define NewOne rbd_allocate_struct_one
-static Real *
-rbd_allocate_struct_one(int sign, size_t const digits, bool limit_precision)
-{
- Real *real = rbd_allocate_struct_decimal_digits(digits, limit_precision);
- VpSetOne(real);
- if (sign < 0)
- VpSetSign(real, VP_SIGN_NEGATIVE_FINITE);
- return real;
-}
-
-MAYBE_UNUSED(static inline Real * rbd_allocate_struct_one_limited(int sign, size_t const digits));
-#define NewOneLimited rbd_allocate_struct_one_limited
-static inline Real *
-rbd_allocate_struct_one_limited(int sign, size_t const digits)
-{
- return rbd_allocate_struct_one(sign, digits, true);
-}
-
-MAYBE_UNUSED(static inline Real * rbd_allocate_struct_one_nolimit(int sign, size_t const digits));
-#define NewOneNolimit rbd_allocate_struct_one_nolimit
-static inline Real *
-rbd_allocate_struct_one_nolimit(int sign, size_t const digits)
-{
- return rbd_allocate_struct_one(sign, digits, false);
-}
-
-/*
- * ================== Ruby Interface part ==========================
- */
-#define DoSomeOne(x,y,f) rb_num_coerce_bin(x,y,f)
-
-/*
- * VP routines used in BigDecimal part
- */
-static unsigned short VpGetException(void);
-static void VpSetException(unsigned short f);
-static void VpCheckException(Real *p, bool always);
-static VALUE VpCheckGetValue(Real *p);
-static void VpInternalRound(Real *c, size_t ixDigit, DECDIG vPrev, DECDIG v);
-static int VpLimitRound(Real *c, size_t ixDigit);
-static Real *VpCopy(Real *pv, Real const* const x);
-static int VPrint(FILE *fp,const char *cntl_chr,Real *a);
-
-/*
- * **** BigDecimal part ****
- */
-
-static VALUE BigDecimal_nan(void);
-static VALUE BigDecimal_positive_infinity(void);
-static VALUE BigDecimal_negative_infinity(void);
-static VALUE BigDecimal_positive_zero(void);
-static VALUE BigDecimal_negative_zero(void);
-
-static void
-BigDecimal_delete(void *pv)
-{
- rbd_free_struct(pv);
-}
-
-static size_t
-BigDecimal_memsize(const void *ptr)
-{
- const Real *pv = ptr;
- return (sizeof(*pv) + pv->MaxPrec * sizeof(DECDIG));
-}
-
-#ifndef HAVE_RB_EXT_RACTOR_SAFE
-# undef RUBY_TYPED_FROZEN_SHAREABLE
-# define RUBY_TYPED_FROZEN_SHAREABLE 0
-#endif
-
-static const rb_data_type_t BigDecimal_data_type = {
- "BigDecimal",
- { 0, BigDecimal_delete, BigDecimal_memsize, },
-#ifdef RUBY_TYPED_FREE_IMMEDIATELY
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_WB_PROTECTED
-#endif
-};
-
-static Real *
-rbd_allocate_struct_zero_wrap_klass(VALUE klass, int sign, size_t const digits, bool limit_precision)
-{
- Real *real = rbd_allocate_struct_zero(sign, digits, limit_precision);
- if (real != NULL) {
- VALUE obj = TypedData_Wrap_Struct(klass, &BigDecimal_data_type, 0);
- BigDecimal_wrap_struct(obj, real);
- }
- return real;
-}
-
-MAYBE_UNUSED(static inline Real * rbd_allocate_struct_zero_limited_wrap(int sign, size_t const digits));
-#define NewZeroWrapLimited rbd_allocate_struct_zero_limited_wrap
-static inline Real *
-rbd_allocate_struct_zero_limited_wrap(int sign, size_t const digits)
-{
- return rbd_allocate_struct_zero_wrap_klass(rb_cBigDecimal, sign, digits, true);
-}
-
-MAYBE_UNUSED(static inline Real * rbd_allocate_struct_zero_nolimit_wrap(int sign, size_t const digits));
-#define NewZeroWrapNolimit rbd_allocate_struct_zero_nolimit_wrap
-static inline Real *
-rbd_allocate_struct_zero_nolimit_wrap(int sign, size_t const digits)
-{
- return rbd_allocate_struct_zero_wrap_klass(rb_cBigDecimal, sign, digits, false);
-}
-
-static Real *
-rbd_allocate_struct_one_wrap_klass(VALUE klass, int sign, size_t const digits, bool limit_precision)
-{
- Real *real = rbd_allocate_struct_one(sign, digits, limit_precision);
- if (real != NULL) {
- VALUE obj = TypedData_Wrap_Struct(klass, &BigDecimal_data_type, 0);
- BigDecimal_wrap_struct(obj, real);
- }
- return real;
-}
-
-MAYBE_UNUSED(static inline Real * rbd_allocate_struct_one_limited_wrap(int sign, size_t const digits));
-#define NewOneWrapLimited rbd_allocate_struct_one_limited_wrap
-static inline Real *
-rbd_allocate_struct_one_limited_wrap(int sign, size_t const digits)
-{
- return rbd_allocate_struct_one_wrap_klass(rb_cBigDecimal, sign, digits, true);
-}
-
-MAYBE_UNUSED(static inline Real * rbd_allocate_struct_one_nolimit_wrap(int sign, size_t const digits));
-#define NewOneWrapNolimit rbd_allocate_struct_one_nolimit_wrap
-static inline Real *
-rbd_allocate_struct_one_nolimit_wrap(int sign, size_t const digits)
-{
- return rbd_allocate_struct_one_wrap_klass(rb_cBigDecimal, sign, digits, false);
-}
-
-static inline int
-is_kind_of_BigDecimal(VALUE const v)
-{
- return rb_typeddata_is_kind_of(v, &BigDecimal_data_type);
-}
-
-NORETURN(static void cannot_be_coerced_into_BigDecimal(VALUE, VALUE));
-
-static void
-cannot_be_coerced_into_BigDecimal(VALUE exc_class, VALUE v)
-{
- VALUE str;
-
- if (rb_special_const_p(v)) {
- str = rb_inspect(v);
- }
- else {
- str = rb_class_name(rb_obj_class(v));
- }
-
- str = rb_str_cat2(rb_str_dup(str), " can't be coerced into BigDecimal");
- rb_exc_raise(rb_exc_new3(exc_class, str));
-}
-
-static inline VALUE BigDecimal_div2(VALUE, VALUE, VALUE);
-static VALUE rb_inum_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception);
-static VALUE rb_float_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception);
-static VALUE rb_rational_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception);
-static VALUE rb_cstr_convert_to_BigDecimal(const char *c_str, size_t digs, int raise_exception);
-static VALUE rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception);
-
-static Real*
-GetVpValueWithPrec(VALUE v, long prec, int must)
-{
- const size_t digs = prec < 0 ? SIZE_MAX : (size_t)prec;
-
- switch(TYPE(v)) {
- case T_FLOAT:
- v = rb_float_convert_to_BigDecimal(v, digs, must);
- break;
-
- case T_RATIONAL:
- v = rb_rational_convert_to_BigDecimal(v, digs, must);
- break;
-
- case T_DATA:
- if (!is_kind_of_BigDecimal(v)) {
- goto SomeOneMayDoIt;
- }
- break;
-
- case T_FIXNUM: {
- char szD[128];
- snprintf(szD, 128, "%ld", FIX2LONG(v));
- v = rb_cstr_convert_to_BigDecimal(szD, VpBaseFig() * 2 + 1, must);
- break;
- }
-
-#ifdef ENABLE_NUMERIC_STRING
- case T_STRING: {
- const char *c_str = StringValueCStr(v);
- v = rb_cstr_convert_to_BigDecimal(c_str, RSTRING_LEN(v) + VpBaseFig() + 1, must);
- break;
- }
-#endif /* ENABLE_NUMERIC_STRING */
-
- case T_BIGNUM: {
- VALUE bg = rb_big2str(v, 10);
- v = rb_cstr_convert_to_BigDecimal(RSTRING_PTR(bg), RSTRING_LEN(bg) + VpBaseFig() + 1, must);
- RB_GC_GUARD(bg);
- break;
- }
-
- default:
- goto SomeOneMayDoIt;
- }
-
- Real *vp;
- TypedData_Get_Struct(v, Real, &BigDecimal_data_type, vp);
- return vp;
-
-SomeOneMayDoIt:
- if (must) {
- cannot_be_coerced_into_BigDecimal(rb_eTypeError, v);
- }
- return NULL; /* NULL means to coerce */
-}
-
-static inline Real*
-GetVpValue(VALUE v, int must)
-{
- return GetVpValueWithPrec(v, -1, must);
-}
-
-/* call-seq:
- * BigDecimal.double_fig -> integer
- *
- * Returns the number of digits a Float object is allowed to have;
- * the result is system-dependent:
- *
- * BigDecimal.double_fig # => 16
- *
- */
-static inline VALUE
-BigDecimal_double_fig(VALUE self)
-{
- return INT2FIX(VpDblFig());
-}
-
-/* call-seq:
- * precs -> array
- *
- * Returns an Array of two Integer values that represent platform-dependent
- * internal storage properties.
- *
- * This method is deprecated and will be removed in the future.
- * Instead, use BigDecimal#n_significant_digits for obtaining the number of
- * significant digits in scientific notation, and BigDecimal#precision for
- * obtaining the number of digits in decimal notation.
- *
- */
-
-static VALUE
-BigDecimal_prec(VALUE self)
-{
- ENTER(1);
- Real *p;
- VALUE obj;
-
- rb_category_warn(RB_WARN_CATEGORY_DEPRECATED,
- "BigDecimal#precs is deprecated and will be removed in the future; "
- "use BigDecimal#precision instead.");
-
- GUARD_OBJ(p, GetVpValue(self, 1));
- obj = rb_assoc_new(SIZET2NUM(p->Prec*VpBaseFig()),
- SIZET2NUM(p->MaxPrec*VpBaseFig()));
- return obj;
-}
-
-static void
-BigDecimal_count_precision_and_scale(VALUE self, ssize_t *out_precision, ssize_t *out_scale)
-{
- ENTER(1);
-
- if (out_precision == NULL && out_scale == NULL)
- return;
-
- Real *p;
- GUARD_OBJ(p, GetVpValue(self, 1));
- if (VpIsZero(p) || !VpIsDef(p)) {
- zero:
- if (out_precision) *out_precision = 0;
- if (out_scale) *out_scale = 0;
- return;
- }
-
- DECDIG x;
-
- ssize_t n = p->Prec; /* The length of frac without zeros. */
- while (n > 0 && p->frac[n-1] == 0) --n;
- if (n == 0) goto zero;
-
- int nlz = BASE_FIG;
- for (x = p->frac[0]; x > 0; x /= 10) --nlz;
-
- int ntz = 0;
- for (x = p->frac[n-1]; x > 0 && x % 10 == 0; x /= 10) ++ntz;
-
- /*
- * Calculate the precision and the scale
- * -------------------------------------
- *
- * The most significant digit is frac[0], and the least significant digit
- * is frac[Prec-1]. When the exponent is zero, the decimal point is
- * located just before frac[0].
- *
- * When the exponent is negative, the decimal point moves to leftward.
- * In this case, the precision can be calculated by
- *
- * precision = BASE_FIG * (-exponent + n) - ntz,
- *
- * and the scale is the same as precision.
- *
- * 0 . 0000 0000 | frac[0] ... frac[n-1] |
- * |<----------| exponent == -2 |
- * |---------------------------------->| precision
- * |---------------------------------->| scale
- *
- *
- * Conversely, when the exponent is positive, the decimal point moves to
- * rightward. In this case, the scale equals to
- *
- * BASE_FIG * (n - exponent) - ntz.
- *
- * the precision equals to
- *
- * scale + BASE_FIG * exponent - nlz.
- *
- * | frac[0] frac[1] . frac[2] ... frac[n-1] |
- * |---------------->| exponent == 2 |
- * | |---------------------->| scale
- * |---------------------------------------->| precision
- */
-
- ssize_t ex = p->exponent;
-
- /* Count the number of decimal digits before frac[1]. */
- ssize_t n_digits_head = BASE_FIG;
- if (ex < 0) {
- n_digits_head += (-ex) * BASE_FIG; /* The number of leading zeros before frac[0]. */
- ex = 0;
- }
- else if (ex > 0) {
- /* Count the number of decimal digits without the leading zeros in
- * the most significant digit in the integral part.
- */
- n_digits_head -= nlz; /* Make the number of digits */
- }
-
- if (out_precision) {
- ssize_t precision = n_digits_head;
-
- /* Count the number of decimal digits after frac[0]. */
- if (ex > (ssize_t)n) {
- /* In this case the number is an integer with some trailing zeros. */
- precision += (ex - 1) * BASE_FIG;
- }
- else if (n > 0) {
- precision += (n - 1) * BASE_FIG;
-
- if (ex < (ssize_t)n) {
- precision -= ntz;
- }
- }
-
- *out_precision = precision;
- }
-
- if (out_scale) {
- ssize_t scale = 0;
-
- if (p->exponent < 0) {
- scale = n_digits_head + (n - 1) * BASE_FIG - ntz;
- }
- else if (n > p->exponent) {
- scale = (n - p->exponent) * BASE_FIG - ntz;
- }
-
- *out_scale = scale;
- }
-}
-
-/*
- * call-seq:
- * precision -> integer
- *
- * Returns the number of decimal digits in +self+:
- *
- * BigDecimal("0").precision # => 0
- * BigDecimal("1").precision # => 1
- * BigDecimal("1.1").precision # => 2
- * BigDecimal("3.1415").precision # => 5
- * BigDecimal("-1e20").precision # => 21
- * BigDecimal("1e-20").precision # => 20
- * BigDecimal("Infinity").precision # => 0
- * BigDecimal("-Infinity").precision # => 0
- * BigDecimal("NaN").precision # => 0
- *
- */
-static VALUE
-BigDecimal_precision(VALUE self)
-{
- ssize_t precision;
- BigDecimal_count_precision_and_scale(self, &precision, NULL);
- return SSIZET2NUM(precision);
-}
-
-/*
- * call-seq:
- * scale -> integer
- *
- * Returns the number of decimal digits following the decimal digits in +self+.
- *
- * BigDecimal("0").scale # => 0
- * BigDecimal("1").scale # => 1
- * BigDecimal("1.1").scale # => 1
- * BigDecimal("3.1415").scale # => 4
- * BigDecimal("-1e20").precision # => 0
- * BigDecimal("1e-20").precision # => 20
- * BigDecimal("Infinity").scale # => 0
- * BigDecimal("-Infinity").scale # => 0
- * BigDecimal("NaN").scale # => 0
- */
-static VALUE
-BigDecimal_scale(VALUE self)
-{
- ssize_t scale;
- BigDecimal_count_precision_and_scale(self, NULL, &scale);
- return SSIZET2NUM(scale);
-}
-
-/*
- * call-seq:
- * precision_scale -> [integer, integer]
- *
- * Returns a 2-length array; the first item is the result of
- * BigDecimal#precision and the second one is of BigDecimal#scale.
- *
- * See BigDecimal#precision.
- * See BigDecimal#scale.
- */
-static VALUE
-BigDecimal_precision_scale(VALUE self)
-{
- ssize_t precision, scale;
- BigDecimal_count_precision_and_scale(self, &precision, &scale);
- return rb_assoc_new(SSIZET2NUM(precision), SSIZET2NUM(scale));
-}
-
-/*
- * call-seq:
- * n_significant_digits -> integer
- *
- * Returns the number of decimal significant digits in +self+.
- *
- * BigDecimal("0").n_significant_digits # => 0
- * BigDecimal("1").n_significant_digits # => 1
- * BigDecimal("1.1").n_significant_digits # => 2
- * BigDecimal("3.1415").n_significant_digits # => 5
- * BigDecimal("-1e20").n_significant_digits # => 1
- * BigDecimal("1e-20").n_significant_digits # => 1
- * BigDecimal("Infinity").n_significant_digits # => 0
- * BigDecimal("-Infinity").n_significant_digits # => 0
- * BigDecimal("NaN").n_significant_digits # => 0
- */
-static VALUE
-BigDecimal_n_significant_digits(VALUE self)
-{
- ENTER(1);
-
- Real *p;
- GUARD_OBJ(p, GetVpValue(self, 1));
- if (VpIsZero(p) || !VpIsDef(p)) {
- return INT2FIX(0);
- }
-
- ssize_t n = p->Prec; /* The length of frac without trailing zeros. */
- for (n = p->Prec; n > 0 && p->frac[n-1] == 0; --n);
- if (n == 0) return INT2FIX(0);
-
- DECDIG x;
- int nlz = BASE_FIG;
- for (x = p->frac[0]; x > 0; x /= 10) --nlz;
-
- int ntz = 0;
- for (x = p->frac[n-1]; x > 0 && x % 10 == 0; x /= 10) ++ntz;
-
- ssize_t n_significant_digits = BASE_FIG*n - nlz - ntz;
- return SSIZET2NUM(n_significant_digits);
-}
-
-/*
- * call-seq:
- * hash -> integer
- *
- * Returns the integer hash value for +self+.
- *
- * Two instances of \BigDecimal have the same hash value if and only if
- * they have equal:
- *
- * - Sign.
- * - Fractional part.
- * - Exponent.
- *
- */
-static VALUE
-BigDecimal_hash(VALUE self)
-{
- ENTER(1);
- Real *p;
- st_index_t hash;
-
- GUARD_OBJ(p, GetVpValue(self, 1));
- hash = (st_index_t)p->sign;
- /* hash!=2: the case for 0(1),NaN(0) or +-Infinity(3) is sign itself */
- if(hash == 2 || hash == (st_index_t)-2) {
- hash ^= rb_memhash(p->frac, sizeof(DECDIG)*p->Prec);
- hash += p->exponent;
- }
- return ST2FIX(hash);
-}
-
-/*
- * call-seq:
- * _dump -> string
- *
- * Returns a string representing the marshalling of +self+.
- * See module Marshal.
- *
- * inf = BigDecimal('Infinity') # => Infinity
- * dumped = inf._dump # => "9:Infinity"
- * BigDecimal._load(dumped) # => Infinity
- *
- */
-static VALUE
-BigDecimal_dump(int argc, VALUE *argv, VALUE self)
-{
- ENTER(5);
- Real *vp;
- char *psz;
- VALUE dummy;
- volatile VALUE dump;
- size_t len;
-
- rb_scan_args(argc, argv, "01", &dummy);
- GUARD_OBJ(vp,GetVpValue(self, 1));
- dump = rb_str_new(0, VpNumOfChars(vp, "E")+50);
- psz = RSTRING_PTR(dump);
- snprintf(psz, RSTRING_LEN(dump), "%"PRIuSIZE":", VpMaxPrec(vp)*VpBaseFig());
- len = strlen(psz);
- VpToString(vp, psz+len, RSTRING_LEN(dump)-len, 0, 0);
- rb_str_resize(dump, strlen(psz));
- return dump;
-}
-
-/*
- * Internal method used to provide marshalling support. See the Marshal module.
- */
-static VALUE
-BigDecimal_load(VALUE self, VALUE str)
-{
- ENTER(2);
- Real *pv;
- unsigned char *pch;
- unsigned char ch;
- unsigned long m=0;
-
- pch = (unsigned char *)StringValueCStr(str);
- /* First get max prec */
- while((*pch) != (unsigned char)'\0' && (ch = *pch++) != (unsigned char)':') {
- if(!ISDIGIT(ch)) {
- rb_raise(rb_eTypeError, "load failed: invalid character in the marshaled string");
- }
- m = m*10 + (unsigned long)(ch-'0');
- }
- if (m > VpBaseFig()) m -= VpBaseFig();
- GUARD_OBJ(pv, VpNewRbClass(m, (char *)pch, self, true, true));
- m /= VpBaseFig();
- if (m && pv->MaxPrec > m) {
- pv->MaxPrec = m+1;
- }
- return VpCheckGetValue(pv);
-}
-
-static unsigned short
-check_rounding_mode_option(VALUE const opts)
-{
- VALUE mode;
- char const *s;
- long l;
-
- assert(RB_TYPE_P(opts, T_HASH));
-
- if (NIL_P(opts))
- goto no_opt;
-
- mode = rb_hash_lookup2(opts, ID2SYM(id_half), Qundef);
- if (mode == Qundef || NIL_P(mode))
- goto no_opt;
-
- if (SYMBOL_P(mode))
- mode = rb_sym2str(mode);
- else if (!RB_TYPE_P(mode, T_STRING)) {
- VALUE str_mode = rb_check_string_type(mode);
- if (NIL_P(str_mode))
- goto invalid;
- mode = str_mode;
- }
- s = RSTRING_PTR(mode);
- l = RSTRING_LEN(mode);
- switch (l) {
- case 2:
- if (strncasecmp(s, "up", 2) == 0)
- return VP_ROUND_HALF_UP;
- break;
- case 4:
- if (strncasecmp(s, "even", 4) == 0)
- return VP_ROUND_HALF_EVEN;
- else if (strncasecmp(s, "down", 4) == 0)
- return VP_ROUND_HALF_DOWN;
- break;
- default:
- break;
- }
-
- invalid:
- rb_raise(rb_eArgError, "invalid rounding mode (%"PRIsVALUE")", mode);
-
- no_opt:
- return VpGetRoundMode();
-}
-
-static unsigned short
-check_rounding_mode(VALUE const v)
-{
- unsigned short sw;
- ID id;
- if (RB_TYPE_P(v, T_SYMBOL)) {
- int i;
- id = SYM2ID(v);
- for (i = 0; i < RBD_NUM_ROUNDING_MODES; ++i) {
- if (rbd_rounding_modes[i].id == id) {
- return rbd_rounding_modes[i].mode;
- }
- }
- rb_raise(rb_eArgError, "invalid rounding mode (%"PRIsVALUE")", v);
- }
- else {
- sw = NUM2USHORT(v);
- if (!VpIsRoundMode(sw)) {
- rb_raise(rb_eArgError, "invalid rounding mode (%"PRIsVALUE")", v);
- }
- return sw;
- }
-}
-
-/* call-seq:
- * BigDecimal.mode(mode, setting = nil) -> integer
- *
- * Returns an integer representing the mode settings
- * for exception handling and rounding.
- *
- * These modes control exception handling:
- *
- * - \BigDecimal::EXCEPTION_NaN.
- * - \BigDecimal::EXCEPTION_INFINITY.
- * - \BigDecimal::EXCEPTION_UNDERFLOW.
- * - \BigDecimal::EXCEPTION_OVERFLOW.
- * - \BigDecimal::EXCEPTION_ZERODIVIDE.
- * - \BigDecimal::EXCEPTION_ALL.
- *
- * Values for +setting+ for exception handling:
- *
- * - +true+: sets the given +mode+ to +true+.
- * - +false+: sets the given +mode+ to +false+.
- * - +nil+: does not modify the mode settings.
- *
- * You can use method BigDecimal.save_exception_mode
- * to temporarily change, and then automatically restore, exception modes.
- *
- * For clarity, some examples below begin by setting all
- * exception modes to +false+.
- *
- * This mode controls the way rounding is to be performed:
- *
- * - \BigDecimal::ROUND_MODE
- *
- * You can use method BigDecimal.save_rounding_mode
- * to temporarily change, and then automatically restore, the rounding mode.
- *
- * <b>NaNs</b>
- *
- * Mode \BigDecimal::EXCEPTION_NaN controls behavior
- * when a \BigDecimal NaN is created.
- *
- * Settings:
- *
- * - +false+ (default): Returns <tt>BigDecimal('NaN')</tt>.
- * - +true+: Raises FloatDomainError.
- *
- * Examples:
- *
- * BigDecimal.mode(BigDecimal::EXCEPTION_ALL, false) # => 0
- * BigDecimal('NaN') # => NaN
- * BigDecimal.mode(BigDecimal::EXCEPTION_NaN, true) # => 2
- * BigDecimal('NaN') # Raises FloatDomainError
- *
- * <b>Infinities</b>
- *
- * Mode \BigDecimal::EXCEPTION_INFINITY controls behavior
- * when a \BigDecimal Infinity or -Infinity is created.
- * Settings:
- *
- * - +false+ (default): Returns <tt>BigDecimal('Infinity')</tt>
- * or <tt>BigDecimal('-Infinity')</tt>.
- * - +true+: Raises FloatDomainError.
- *
- * Examples:
- *
- * BigDecimal.mode(BigDecimal::EXCEPTION_ALL, false) # => 0
- * BigDecimal('Infinity') # => Infinity
- * BigDecimal('-Infinity') # => -Infinity
- * BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, true) # => 1
- * BigDecimal('Infinity') # Raises FloatDomainError
- * BigDecimal('-Infinity') # Raises FloatDomainError
- *
- * <b>Underflow</b>
- *
- * Mode \BigDecimal::EXCEPTION_UNDERFLOW controls behavior
- * when a \BigDecimal underflow occurs.
- * Settings:
- *
- * - +false+ (default): Returns <tt>BigDecimal('0')</tt>
- * or <tt>BigDecimal('-Infinity')</tt>.
- * - +true+: Raises FloatDomainError.
- *
- * Examples:
- *
- * BigDecimal.mode(BigDecimal::EXCEPTION_ALL, false) # => 0
- * def flow_under
- * x = BigDecimal('0.1')
- * 100.times { x *= x }
- * end
- * flow_under # => 100
- * BigDecimal.mode(BigDecimal::EXCEPTION_UNDERFLOW, true) # => 4
- * flow_under # Raises FloatDomainError
- *
- * <b>Overflow</b>
- *
- * Mode \BigDecimal::EXCEPTION_OVERFLOW controls behavior
- * when a \BigDecimal overflow occurs.
- * Settings:
- *
- * - +false+ (default): Returns <tt>BigDecimal('Infinity')</tt>
- * or <tt>BigDecimal('-Infinity')</tt>.
- * - +true+: Raises FloatDomainError.
- *
- * Examples:
- *
- * BigDecimal.mode(BigDecimal::EXCEPTION_ALL, false) # => 0
- * def flow_over
- * x = BigDecimal('10')
- * 100.times { x *= x }
- * end
- * flow_over # => 100
- * BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, true) # => 1
- * flow_over # Raises FloatDomainError
- *
- * <b>Zero Division</b>
- *
- * Mode \BigDecimal::EXCEPTION_ZERODIVIDE controls behavior
- * when a zero-division occurs.
- * Settings:
- *
- * - +false+ (default): Returns <tt>BigDecimal('Infinity')</tt>
- * or <tt>BigDecimal('-Infinity')</tt>.
- * - +true+: Raises FloatDomainError.
- *
- * Examples:
- *
- * BigDecimal.mode(BigDecimal::EXCEPTION_ALL, false) # => 0
- * one = BigDecimal('1')
- * zero = BigDecimal('0')
- * one / zero # => Infinity
- * BigDecimal.mode(BigDecimal::EXCEPTION_ZERODIVIDE, true) # => 16
- * one / zero # Raises FloatDomainError
- *
- * <b>All Exceptions</b>
- *
- * Mode \BigDecimal::EXCEPTION_ALL controls all of the above:
- *
- * BigDecimal.mode(BigDecimal::EXCEPTION_ALL, false) # => 0
- * BigDecimal.mode(BigDecimal::EXCEPTION_ALL, true) # => 23
- *
- * <b>Rounding</b>
- *
- * Mode \BigDecimal::ROUND_MODE controls the way rounding is to be performed;
- * its +setting+ values are:
- *
- * - +ROUND_UP+: Round away from zero.
- * Aliased as +:up+.
- * - +ROUND_DOWN+: Round toward zero.
- * Aliased as +:down+ and +:truncate+.
- * - +ROUND_HALF_UP+: Round toward the nearest neighbor;
- * if the neighbors are equidistant, round away from zero.
- * Aliased as +:half_up+ and +:default+.
- * - +ROUND_HALF_DOWN+: Round toward the nearest neighbor;
- * if the neighbors are equidistant, round toward zero.
- * Aliased as +:half_down+.
- * - +ROUND_HALF_EVEN+ (Banker's rounding): Round toward the nearest neighbor;
- * if the neighbors are equidistant, round toward the even neighbor.
- * Aliased as +:half_even+ and +:banker+.
- * - +ROUND_CEILING+: Round toward positive infinity.
- * Aliased as +:ceiling+ and +:ceil+.
- * - +ROUND_FLOOR+: Round toward negative infinity.
- * Aliased as +:floor:+.
- *
- */
-static VALUE
-BigDecimal_mode(int argc, VALUE *argv, VALUE self)
-{
- VALUE which;
- VALUE val;
- unsigned long f,fo;
-
- rb_scan_args(argc, argv, "11", &which, &val);
- f = (unsigned long)NUM2INT(which);
-
- if (f & VP_EXCEPTION_ALL) {
- /* Exception mode setting */
- fo = VpGetException();
- if (val == Qnil) return INT2FIX(fo);
- if (val != Qfalse && val!=Qtrue) {
- rb_raise(rb_eArgError, "second argument must be true or false");
- return Qnil; /* Not reached */
- }
- if (f & VP_EXCEPTION_INFINITY) {
- VpSetException((unsigned short)((val == Qtrue) ? (fo | VP_EXCEPTION_INFINITY) :
- (fo & (~VP_EXCEPTION_INFINITY))));
- }
- fo = VpGetException();
- if (f & VP_EXCEPTION_NaN) {
- VpSetException((unsigned short)((val == Qtrue) ? (fo | VP_EXCEPTION_NaN) :
- (fo & (~VP_EXCEPTION_NaN))));
- }
- fo = VpGetException();
- if (f & VP_EXCEPTION_UNDERFLOW) {
- VpSetException((unsigned short)((val == Qtrue) ? (fo | VP_EXCEPTION_UNDERFLOW) :
- (fo & (~VP_EXCEPTION_UNDERFLOW))));
- }
- fo = VpGetException();
- if(f & VP_EXCEPTION_ZERODIVIDE) {
- VpSetException((unsigned short)((val == Qtrue) ? (fo | VP_EXCEPTION_ZERODIVIDE) :
- (fo & (~VP_EXCEPTION_ZERODIVIDE))));
- }
- fo = VpGetException();
- return INT2FIX(fo);
- }
- if (VP_ROUND_MODE == f) {
- /* Rounding mode setting */
- unsigned short sw;
- fo = VpGetRoundMode();
- if (NIL_P(val)) return INT2FIX(fo);
- sw = check_rounding_mode(val);
- fo = VpSetRoundMode(sw);
- return INT2FIX(fo);
- }
- rb_raise(rb_eTypeError, "first argument for BigDecimal.mode invalid");
- return Qnil;
-}
-
-static size_t
-GetAddSubPrec(Real *a, Real *b)
-{
- size_t mxs;
- size_t mx = a->Prec;
- SIGNED_VALUE d;
-
- if (!VpIsDef(a) || !VpIsDef(b)) return (size_t)-1L;
- if (mx < b->Prec) mx = b->Prec;
- if (a->exponent != b->exponent) {
- mxs = mx;
- d = a->exponent - b->exponent;
- if (d < 0) d = -d;
- mx = mx + (size_t)d;
- if (mx < mxs) {
- return VpException(VP_EXCEPTION_INFINITY, "Exponent overflow", 0);
- }
- }
- return mx;
-}
-
-static inline SIGNED_VALUE
-check_int_precision(VALUE v)
-{
- SIGNED_VALUE n;
-#if SIZEOF_VALUE <= SIZEOF_LONG
- n = (SIGNED_VALUE)NUM2LONG(v);
-#elif SIZEOF_VALUE <= SIZEOF_LONG_LONG
- n = (SIGNED_VALUE)NUM2LL(v);
-#else
-# error SIZEOF_VALUE is too large
-#endif
- if (n < 0) {
- rb_raise(rb_eArgError, "negative precision");
- }
- return n;
-}
-
-static VALUE
-BigDecimal_wrap_struct(VALUE obj, Real *vp)
-{
- assert(is_kind_of_BigDecimal(obj));
- assert(vp != NULL);
-
- if (vp->obj == obj && RTYPEDDATA_DATA(obj) == vp)
- return obj;
-
- assert(RTYPEDDATA_DATA(obj) == NULL);
- assert(vp->obj == 0);
-
- RTYPEDDATA_DATA(obj) = vp;
- vp->obj = obj;
- RB_OBJ_FREEZE(obj);
- return obj;
-}
-
-VP_EXPORT Real *
-VpNewRbClass(size_t mx, const char *str, VALUE klass, bool strict_p, bool raise_exception)
-{
- VALUE obj = TypedData_Wrap_Struct(klass, &BigDecimal_data_type, 0);
- Real *pv = VpAlloc(mx, str, strict_p, raise_exception);
- if (!pv)
- return NULL;
- BigDecimal_wrap_struct(obj, pv);
- return pv;
-}
-
-VP_EXPORT Real *
-VpCreateRbObject(size_t mx, const char *str, bool raise_exception)
-{
- return VpNewRbClass(mx, str, rb_cBigDecimal, true, raise_exception);
-}
-
-static Real *
-VpCopy(Real *pv, Real const* const x)
-{
- assert(x != NULL);
-
- pv = rbd_reallocate_struct(pv, x->MaxPrec);
- pv->MaxPrec = x->MaxPrec;
- pv->Prec = x->Prec;
- pv->exponent = x->exponent;
- pv->sign = x->sign;
- pv->flag = x->flag;
- MEMCPY(pv->frac, x->frac, DECDIG, pv->MaxPrec);
-
- return pv;
-}
-
-/* Returns True if the value is Not a Number. */
-static VALUE
-BigDecimal_IsNaN(VALUE self)
-{
- Real *p = GetVpValue(self, 1);
- if (VpIsNaN(p)) return Qtrue;
- return Qfalse;
-}
-
-/* Returns nil, -1, or +1 depending on whether the value is finite,
- * -Infinity, or +Infinity.
- */
-static VALUE
-BigDecimal_IsInfinite(VALUE self)
-{
- Real *p = GetVpValue(self, 1);
- if (VpIsPosInf(p)) return INT2FIX(1);
- if (VpIsNegInf(p)) return INT2FIX(-1);
- return Qnil;
-}
-
-/* Returns True if the value is finite (not NaN or infinite). */
-static VALUE
-BigDecimal_IsFinite(VALUE self)
-{
- Real *p = GetVpValue(self, 1);
- if (VpIsNaN(p)) return Qfalse;
- if (VpIsInf(p)) return Qfalse;
- return Qtrue;
-}
-
-static void
-BigDecimal_check_num(Real *p)
-{
- VpCheckException(p, true);
-}
-
-static VALUE BigDecimal_split(VALUE self);
-
-/* Returns the value as an Integer.
- *
- * If the BigDecimal is infinity or NaN, raises FloatDomainError.
- */
-static VALUE
-BigDecimal_to_i(VALUE self)
-{
- ENTER(5);
- ssize_t e, nf;
- Real *p;
-
- GUARD_OBJ(p, GetVpValue(self, 1));
- BigDecimal_check_num(p);
-
- e = VpExponent10(p);
- if (e <= 0) return INT2FIX(0);
- nf = VpBaseFig();
- if (e <= nf) {
- return LONG2NUM((long)(VpGetSign(p) * (DECDIG_DBL_SIGNED)p->frac[0]));
- }
- else {
- VALUE a = BigDecimal_split(self);
- VALUE digits = RARRAY_AREF(a, 1);
- VALUE numerator = rb_funcall(digits, rb_intern("to_i"), 0);
- VALUE ret;
- ssize_t dpower = e - (ssize_t)RSTRING_LEN(digits);
-
- if (BIGDECIMAL_NEGATIVE_P(p)) {
- numerator = rb_funcall(numerator, '*', 1, INT2FIX(-1));
- }
- if (dpower < 0) {
- ret = rb_funcall(numerator, rb_intern("div"), 1,
- rb_funcall(INT2FIX(10), rb_intern("**"), 1,
- INT2FIX(-dpower)));
- }
- else {
- ret = rb_funcall(numerator, '*', 1,
- rb_funcall(INT2FIX(10), rb_intern("**"), 1,
- INT2FIX(dpower)));
- }
- if (RB_TYPE_P(ret, T_FLOAT)) {
- rb_raise(rb_eFloatDomainError, "Infinity");
- }
- return ret;
- }
-}
-
-/* Returns a new Float object having approximately the same value as the
- * BigDecimal number. Normal accuracy limits and built-in errors of binary
- * Float arithmetic apply.
- */
-static VALUE
-BigDecimal_to_f(VALUE self)
-{
- ENTER(1);
- Real *p;
- double d;
- SIGNED_VALUE e;
- char *buf;
- volatile VALUE str;
-
- GUARD_OBJ(p, GetVpValue(self, 1));
- if (VpVtoD(&d, &e, p) != 1)
- return rb_float_new(d);
- if (e > (SIGNED_VALUE)(DBL_MAX_10_EXP+BASE_FIG))
- goto overflow;
- if (e < (SIGNED_VALUE)(DBL_MIN_10_EXP-BASE_FIG))
- goto underflow;
-
- str = rb_str_new(0, VpNumOfChars(p, "E"));
- buf = RSTRING_PTR(str);
- VpToString(p, buf, RSTRING_LEN(str), 0, 0);
- errno = 0;
- d = strtod(buf, 0);
- if (errno == ERANGE) {
- if (d == 0.0) goto underflow;
- if (fabs(d) >= HUGE_VAL) goto overflow;
- }
- return rb_float_new(d);
-
-overflow:
- VpException(VP_EXCEPTION_OVERFLOW, "BigDecimal to Float conversion", 0);
- if (BIGDECIMAL_NEGATIVE_P(p))
- return rb_float_new(VpGetDoubleNegInf());
- else
- return rb_float_new(VpGetDoublePosInf());
-
-underflow:
- VpException(VP_EXCEPTION_UNDERFLOW, "BigDecimal to Float conversion", 0);
- if (BIGDECIMAL_NEGATIVE_P(p))
- return rb_float_new(-0.0);
- else
- return rb_float_new(0.0);
-}
-
-
-/* Converts a BigDecimal to a Rational.
- */
-static VALUE
-BigDecimal_to_r(VALUE self)
-{
- Real *p;
- ssize_t sign, power, denomi_power;
- VALUE a, digits, numerator;
-
- p = GetVpValue(self, 1);
- BigDecimal_check_num(p);
-
- sign = VpGetSign(p);
- power = VpExponent10(p);
- a = BigDecimal_split(self);
- digits = RARRAY_AREF(a, 1);
- denomi_power = power - RSTRING_LEN(digits);
- numerator = rb_funcall(digits, rb_intern("to_i"), 0);
-
- if (sign < 0) {
- numerator = rb_funcall(numerator, '*', 1, INT2FIX(-1));
- }
- if (denomi_power < 0) {
- return rb_Rational(numerator,
- rb_funcall(INT2FIX(10), rb_intern("**"), 1,
- INT2FIX(-denomi_power)));
- }
- else {
- return rb_Rational1(rb_funcall(numerator, '*', 1,
- rb_funcall(INT2FIX(10), rb_intern("**"), 1,
- INT2FIX(denomi_power))));
- }
-}
-
-/* The coerce method provides support for Ruby type coercion. It is not
- * enabled by default.
- *
- * This means that binary operations like + * / or - can often be performed
- * on a BigDecimal and an object of another type, if the other object can
- * be coerced into a BigDecimal value.
- *
- * e.g.
- * a = BigDecimal("1.0")
- * b = a / 2.0 #=> 0.5
- *
- * Note that coercing a String to a BigDecimal is not supported by default;
- * it requires a special compile-time option when building Ruby.
- */
-static VALUE
-BigDecimal_coerce(VALUE self, VALUE other)
-{
- ENTER(2);
- VALUE obj;
- Real *b;
-
- if (RB_TYPE_P(other, T_FLOAT)) {
- GUARD_OBJ(b, GetVpValueWithPrec(other, 0, 1));
- obj = rb_assoc_new(VpCheckGetValue(b), self);
- }
- else {
- if (RB_TYPE_P(other, T_RATIONAL)) {
- Real* pv = DATA_PTR(self);
- GUARD_OBJ(b, GetVpValueWithPrec(other, pv->Prec*VpBaseFig(), 1));
- }
- else {
- GUARD_OBJ(b, GetVpValue(other, 1));
- }
- obj = rb_assoc_new(b->obj, self);
- }
-
- return obj;
-}
-
-/*
- * call-seq:
- * +big_decimal -> self
- *
- * Returns +self+:
- *
- * +BigDecimal(5) # => 0.5e1
- * +BigDecimal(-5) # => -0.5e1
- *
- */
-
-static VALUE
-BigDecimal_uplus(VALUE self)
-{
- return self;
-}
-
- /*
- * call-seq:
- * self + value -> bigdecimal
- *
- * Returns the \BigDecimal sum of +self+ and +value+:
- *
- * b = BigDecimal('111111.111') # => 0.111111111e6
- * b + 2 # => 0.111113111e6
- * b + 2.0 # => 0.111113111e6
- * b + Rational(2, 1) # => 0.111113111e6
- * b + Complex(2, 0) # => (0.111113111e6+0i)
- *
- * See the {Note About Precision}[BigDecimal.html#class-BigDecimal-label-A+Note+About+Precision].
- *
- */
-
-static VALUE
-BigDecimal_add(VALUE self, VALUE r)
-{
- ENTER(5);
- Real *c, *a, *b;
- size_t mx;
-
- GUARD_OBJ(a, GetVpValue(self, 1));
- if (RB_TYPE_P(r, T_FLOAT)) {
- b = GetVpValueWithPrec(r, 0, 1);
- }
- else if (RB_TYPE_P(r, T_RATIONAL)) {
- b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
- }
- else {
- b = GetVpValue(r, 0);
- }
-
- if (!b) return DoSomeOne(self,r,'+');
- SAVE(b);
-
- if (VpIsNaN(b)) return b->obj;
- if (VpIsNaN(a)) return a->obj;
-
- mx = GetAddSubPrec(a, b);
- if (mx == (size_t)-1L) {
- GUARD_OBJ(c, NewZeroWrapLimited(1, VpBaseFig() + 1));
- VpAddSub(c, a, b, 1);
- }
- else {
- GUARD_OBJ(c, NewZeroWrapLimited(1, mx * (VpBaseFig() + 1)));
- if (!mx) {
- VpSetInf(c, VpGetSign(a));
- }
- else {
- VpAddSub(c, a, b, 1);
- }
- }
- return VpCheckGetValue(c);
-}
-
- /* call-seq:
- * self - value -> bigdecimal
- *
- * Returns the \BigDecimal difference of +self+ and +value+:
- *
- * b = BigDecimal('333333.333') # => 0.333333333e6
- * b - 2 # => 0.333331333e6
- * b - 2.0 # => 0.333331333e6
- * b - Rational(2, 1) # => 0.333331333e6
- * b - Complex(2, 0) # => (0.333331333e6+0i)
- *
- * See the {Note About Precision}[BigDecimal.html#class-BigDecimal-label-A+Note+About+Precision].
- *
- */
-static VALUE
-BigDecimal_sub(VALUE self, VALUE r)
-{
- ENTER(5);
- Real *c, *a, *b;
- size_t mx;
-
- GUARD_OBJ(a, GetVpValue(self,1));
- if (RB_TYPE_P(r, T_FLOAT)) {
- b = GetVpValueWithPrec(r, 0, 1);
- }
- else if (RB_TYPE_P(r, T_RATIONAL)) {
- b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
- }
- else {
- b = GetVpValue(r,0);
- }
-
- if (!b) return DoSomeOne(self,r,'-');
- SAVE(b);
-
- if (VpIsNaN(b)) return b->obj;
- if (VpIsNaN(a)) return a->obj;
-
- mx = GetAddSubPrec(a,b);
- if (mx == (size_t)-1L) {
- GUARD_OBJ(c, NewZeroWrapLimited(1, VpBaseFig() + 1));
- VpAddSub(c, a, b, -1);
- }
- else {
- GUARD_OBJ(c, NewZeroWrapLimited(1, mx *(VpBaseFig() + 1)));
- if (!mx) {
- VpSetInf(c,VpGetSign(a));
- }
- else {
- VpAddSub(c, a, b, -1);
- }
- }
- return VpCheckGetValue(c);
-}
-
-static VALUE
-BigDecimalCmp(VALUE self, VALUE r,char op)
-{
- ENTER(5);
- SIGNED_VALUE e;
- Real *a, *b=0;
- GUARD_OBJ(a, GetVpValue(self, 1));
- switch (TYPE(r)) {
- case T_DATA:
- if (!is_kind_of_BigDecimal(r)) break;
- /* fall through */
- case T_FIXNUM:
- /* fall through */
- case T_BIGNUM:
- GUARD_OBJ(b, GetVpValue(r, 0));
- break;
-
- case T_FLOAT:
- GUARD_OBJ(b, GetVpValueWithPrec(r, 0, 0));
- break;
-
- case T_RATIONAL:
- GUARD_OBJ(b, GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 0));
- break;
-
- default:
- break;
- }
- if (b == NULL) {
- ID f = 0;
-
- switch (op) {
- case '*':
- return rb_num_coerce_cmp(self, r, rb_intern("<=>"));
-
- case '=':
- return RTEST(rb_num_coerce_cmp(self, r, rb_intern("=="))) ? Qtrue : Qfalse;
-
- case 'G':
- f = rb_intern(">=");
- break;
-
- case 'L':
- f = rb_intern("<=");
- break;
-
- case '>':
- /* fall through */
- case '<':
- f = (ID)op;
- break;
-
- default:
- break;
- }
- return rb_num_coerce_relop(self, r, f);
- }
- SAVE(b);
- e = VpComp(a, b);
- if (e == 999)
- return (op == '*') ? Qnil : Qfalse;
- switch (op) {
- case '*':
- return INT2FIX(e); /* any op */
-
- case '=':
- if (e == 0) return Qtrue;
- return Qfalse;
-
- case 'G':
- if (e >= 0) return Qtrue;
- return Qfalse;
-
- case '>':
- if (e > 0) return Qtrue;
- return Qfalse;
-
- case 'L':
- if (e <= 0) return Qtrue;
- return Qfalse;
-
- case '<':
- if (e < 0) return Qtrue;
- return Qfalse;
-
- default:
- break;
- }
-
- rb_bug("Undefined operation in BigDecimalCmp()");
-
- UNREACHABLE;
-}
-
-/* Returns True if the value is zero. */
-static VALUE
-BigDecimal_zero(VALUE self)
-{
- Real *a = GetVpValue(self, 1);
- return VpIsZero(a) ? Qtrue : Qfalse;
-}
-
-/* Returns self if the value is non-zero, nil otherwise. */
-static VALUE
-BigDecimal_nonzero(VALUE self)
-{
- Real *a = GetVpValue(self, 1);
- return VpIsZero(a) ? Qnil : self;
-}
-
-/* The comparison operator.
- * a <=> b is 0 if a == b, 1 if a > b, -1 if a < b.
- */
-static VALUE
-BigDecimal_comp(VALUE self, VALUE r)
-{
- return BigDecimalCmp(self, r, '*');
-}
-
-/*
- * Tests for value equality; returns true if the values are equal.
- *
- * The == and === operators and the eql? method have the same implementation
- * for BigDecimal.
- *
- * Values may be coerced to perform the comparison:
- *
- * BigDecimal('1.0') == 1.0 #=> true
- */
-static VALUE
-BigDecimal_eq(VALUE self, VALUE r)
-{
- return BigDecimalCmp(self, r, '=');
-}
-
-/* call-seq:
- * self < other -> true or false
- *
- * Returns +true+ if +self+ is less than +other+, +false+ otherwise:
- *
- * b = BigDecimal('1.5') # => 0.15e1
- * b < 2 # => true
- * b < 2.0 # => true
- * b < Rational(2, 1) # => true
- * b < 1.5 # => false
- *
- * Raises an exception if the comparison cannot be made.
- *
- */
-static VALUE
-BigDecimal_lt(VALUE self, VALUE r)
-{
- return BigDecimalCmp(self, r, '<');
-}
-
-/* call-seq:
- * self <= other -> true or false
- *
- * Returns +true+ if +self+ is less or equal to than +other+, +false+ otherwise:
- *
- * b = BigDecimal('1.5') # => 0.15e1
- * b <= 2 # => true
- * b <= 2.0 # => true
- * b <= Rational(2, 1) # => true
- * b <= 1.5 # => true
- * b < 1 # => false
- *
- * Raises an exception if the comparison cannot be made.
- *
- */
-static VALUE
-BigDecimal_le(VALUE self, VALUE r)
-{
- return BigDecimalCmp(self, r, 'L');
-}
-
-/* call-seq:
- * self > other -> true or false
- *
- * Returns +true+ if +self+ is greater than +other+, +false+ otherwise:
- *
- * b = BigDecimal('1.5')
- * b > 1 # => true
- * b > 1.0 # => true
- * b > Rational(1, 1) # => true
- * b > 2 # => false
- *
- * Raises an exception if the comparison cannot be made.
- *
- */
-static VALUE
-BigDecimal_gt(VALUE self, VALUE r)
-{
- return BigDecimalCmp(self, r, '>');
-}
-
-/* call-seq:
- * self >= other -> true or false
- *
- * Returns +true+ if +self+ is greater than or equal to +other+, +false+ otherwise:
- *
- * b = BigDecimal('1.5')
- * b >= 1 # => true
- * b >= 1.0 # => true
- * b >= Rational(1, 1) # => true
- * b >= 1.5 # => true
- * b > 2 # => false
- *
- * Raises an exception if the comparison cannot be made.
- *
- */
-static VALUE
-BigDecimal_ge(VALUE self, VALUE r)
-{
- return BigDecimalCmp(self, r, 'G');
-}
-
-/*
- * call-seq:
- * -self -> bigdecimal
- *
- * Returns the \BigDecimal negation of self:
- *
- * b0 = BigDecimal('1.5')
- * b1 = -b0 # => -0.15e1
- * b2 = -b1 # => 0.15e1
- *
- */
-
-static VALUE
-BigDecimal_neg(VALUE self)
-{
- ENTER(5);
- Real *c, *a;
- GUARD_OBJ(a, GetVpValue(self, 1));
- GUARD_OBJ(c, NewZeroWrapLimited(1, a->Prec *(VpBaseFig() + 1)));
- VpAsgn(c, a, -1);
- return VpCheckGetValue(c);
-}
-
-static VALUE
-BigDecimal_mult(VALUE self, VALUE r)
-{
- ENTER(5);
- Real *c, *a, *b;
- size_t mx;
-
- GUARD_OBJ(a, GetVpValue(self, 1));
- if (RB_TYPE_P(r, T_FLOAT)) {
- b = GetVpValueWithPrec(r, 0, 1);
- }
- else if (RB_TYPE_P(r, T_RATIONAL)) {
- b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
- }
- else {
- b = GetVpValue(r,0);
- }
-
- if (!b) return DoSomeOne(self, r, '*');
- SAVE(b);
-
- mx = a->Prec + b->Prec;
- GUARD_OBJ(c, NewZeroWrapLimited(1, mx * (VpBaseFig() + 1)));
- VpMult(c, a, b);
- return VpCheckGetValue(c);
-}
-
-static VALUE
-BigDecimal_divide(VALUE self, VALUE r, Real **c, Real **res, Real **div)
-/* For c = self.div(r): with round operation */
-{
- ENTER(5);
- Real *a, *b;
- ssize_t a_prec, b_prec;
- size_t mx;
-
- TypedData_Get_Struct(self, Real, &BigDecimal_data_type, a);
- SAVE(a);
-
- VALUE rr = r;
- if (is_kind_of_BigDecimal(rr)) {
- /* do nothing */
- }
- else if (RB_INTEGER_TYPE_P(r)) {
- rr = rb_inum_convert_to_BigDecimal(r, 0, true);
- }
- else if (RB_TYPE_P(r, T_FLOAT)) {
- rr = rb_float_convert_to_BigDecimal(r, 0, true);
- }
- else if (RB_TYPE_P(r, T_RATIONAL)) {
- rr = rb_rational_convert_to_BigDecimal(r, a->Prec*BASE_FIG, true);
- }
-
- if (!is_kind_of_BigDecimal(rr)) {
- return DoSomeOne(self, r, '/');
- }
-
- TypedData_Get_Struct(rr, Real, &BigDecimal_data_type, b);
- SAVE(b);
- *div = b;
-
- BigDecimal_count_precision_and_scale(self, &a_prec, NULL);
- BigDecimal_count_precision_and_scale(rr, &b_prec, NULL);
- mx = (a_prec > b_prec) ? a_prec : b_prec;
- mx *= 2;
-
- if (2*BIGDECIMAL_DOUBLE_FIGURES > mx)
- mx = 2*BIGDECIMAL_DOUBLE_FIGURES;
-
- GUARD_OBJ((*c), NewZeroWrapNolimit(1, mx + 2*BASE_FIG));
- GUARD_OBJ((*res), NewZeroWrapNolimit(1, (mx + 1)*2 + 2*BASE_FIG));
- VpDivd(*c, *res, a, b);
-
- return Qnil;
-}
-
-static VALUE BigDecimal_DoDivmod(VALUE self, VALUE r, Real **div, Real **mod);
-
-/* call-seq:
- * a / b -> bigdecimal
- *
- * Divide by the specified value.
- *
- * The result precision will be the precision of the larger operand,
- * but its minimum is 2*Float::DIG.
- *
- * See BigDecimal#div.
- * See BigDecimal#quo.
- */
-static VALUE
-BigDecimal_div(VALUE self, VALUE r)
-/* For c = self/r: with round operation */
-{
- ENTER(5);
- Real *c=NULL, *res=NULL, *div = NULL;
- r = BigDecimal_divide(self, r, &c, &res, &div);
- if (!NIL_P(r)) return r; /* coerced by other */
- SAVE(c); SAVE(res); SAVE(div);
- /* a/b = c + r/b */
- /* c xxxxx
- r 00000yyyyy ==> (y/b)*BASE >= HALF_BASE
- */
- /* Round */
- if (VpHasVal(div)) { /* frac[0] must be zero for NaN,INF,Zero */
- VpInternalRound(c, 0, c->frac[c->Prec-1], (DECDIG)(VpBaseVal() * (DECDIG_DBL)res->frac[0] / div->frac[0]));
- }
- return VpCheckGetValue(c);
-}
-
-static VALUE BigDecimal_round(int argc, VALUE *argv, VALUE self);
-
-/* call-seq:
- * quo(value) -> bigdecimal
- * quo(value, digits) -> bigdecimal
- *
- * Divide by the specified value.
- *
- * digits:: If specified and less than the number of significant digits of
- * the result, the result is rounded to the given number of digits,
- * according to the rounding mode indicated by BigDecimal.mode.
- *
- * If digits is 0 or omitted, the result is the same as for the
- * / operator.
- *
- * See BigDecimal#/.
- * See BigDecimal#div.
- */
-static VALUE
-BigDecimal_quo(int argc, VALUE *argv, VALUE self)
-{
- VALUE value, digits, result;
- SIGNED_VALUE n = -1;
-
- argc = rb_scan_args(argc, argv, "11", &value, &digits);
- if (argc > 1) {
- n = check_int_precision(digits);
- }
-
- if (n > 0) {
- result = BigDecimal_div2(self, value, digits);
- }
- else {
- result = BigDecimal_div(self, value);
- }
-
- return result;
-}
-
-/*
- * %: mod = a%b = a - (a.to_f/b).floor * b
- * div = (a.to_f/b).floor
- */
-static VALUE
-BigDecimal_DoDivmod(VALUE self, VALUE r, Real **div, Real **mod)
-{
- ENTER(8);
- Real *c=NULL, *d=NULL, *res=NULL;
- Real *a, *b;
- ssize_t a_prec, b_prec;
- size_t mx;
-
- TypedData_Get_Struct(self, Real, &BigDecimal_data_type, a);
- SAVE(a);
-
- VALUE rr = r;
- if (is_kind_of_BigDecimal(rr)) {
- /* do nothing */
- }
- else if (RB_INTEGER_TYPE_P(r)) {
- rr = rb_inum_convert_to_BigDecimal(r, 0, true);
- }
- else if (RB_TYPE_P(r, T_FLOAT)) {
- rr = rb_float_convert_to_BigDecimal(r, 0, true);
- }
- else if (RB_TYPE_P(r, T_RATIONAL)) {
- rr = rb_rational_convert_to_BigDecimal(r, a->Prec*BASE_FIG, true);
- }
-
- if (!is_kind_of_BigDecimal(rr)) {
- return Qfalse;
- }
-
- TypedData_Get_Struct(rr, Real, &BigDecimal_data_type, b);
- SAVE(b);
-
- if (VpIsNaN(a) || VpIsNaN(b)) goto NaN;
- if (VpIsInf(a) && VpIsInf(b)) goto NaN;
- if (VpIsZero(b)) {
- rb_raise(rb_eZeroDivError, "divided by 0");
- }
- if (VpIsInf(a)) {
- if (VpGetSign(a) == VpGetSign(b)) {
- VALUE inf = BigDecimal_positive_infinity();
- TypedData_Get_Struct(inf, Real, &BigDecimal_data_type, *div);
- }
- else {
- VALUE inf = BigDecimal_negative_infinity();
- TypedData_Get_Struct(inf, Real, &BigDecimal_data_type, *div);
- }
- VALUE nan = BigDecimal_nan();
- TypedData_Get_Struct(nan, Real, &BigDecimal_data_type, *mod);
- return Qtrue;
- }
- if (VpIsInf(b)) {
- VALUE zero = BigDecimal_positive_zero();
- TypedData_Get_Struct(zero, Real, &BigDecimal_data_type, *div);
- *mod = a;
- return Qtrue;
- }
- if (VpIsZero(a)) {
- VALUE zero = BigDecimal_positive_zero();
- TypedData_Get_Struct(zero, Real, &BigDecimal_data_type, *div);
- TypedData_Get_Struct(zero, Real, &BigDecimal_data_type, *mod);
- return Qtrue;
- }
-
- BigDecimal_count_precision_and_scale(self, &a_prec, NULL);
- BigDecimal_count_precision_and_scale(rr, &b_prec, NULL);
-
- mx = (a_prec > b_prec) ? a_prec : b_prec;
- mx *= 2;
-
- if (2*BIGDECIMAL_DOUBLE_FIGURES > mx)
- mx = 2*BIGDECIMAL_DOUBLE_FIGURES;
-
- GUARD_OBJ(c, NewZeroWrapLimited(1, mx + 2*BASE_FIG));
- GUARD_OBJ(res, NewZeroWrapNolimit(1, mx*2 + 2*BASE_FIG));
- VpDivd(c, res, a, b);
-
- mx = c->Prec * BASE_FIG;
- GUARD_OBJ(d, NewZeroWrapLimited(1, mx));
- VpActiveRound(d, c, VP_ROUND_DOWN, 0);
-
- VpMult(res, d, b);
- VpAddSub(c, a, res, -1);
-
- if (!VpIsZero(c) && (VpGetSign(a) * VpGetSign(b) < 0)) {
- /* result adjustment for negative case */
- res = rbd_reallocate_struct(res, d->MaxPrec);
- res->MaxPrec = d->MaxPrec;
- VpAddSub(res, d, VpOne(), -1);
- GUARD_OBJ(d, NewZeroWrapLimited(1, GetAddSubPrec(c, b) * 2*BASE_FIG));
- VpAddSub(d, c, b, 1);
- *div = res;
- *mod = d;
- }
- else {
- *div = d;
- *mod = c;
- }
- return Qtrue;
-
- NaN:
- {
- VALUE nan = BigDecimal_nan();
- TypedData_Get_Struct(nan, Real, &BigDecimal_data_type, *div);
- TypedData_Get_Struct(nan, Real, &BigDecimal_data_type, *mod);
- }
- return Qtrue;
-}
-
-/* call-seq:
- * a % b
- * a.modulo(b)
- *
- * Returns the modulus from dividing by b.
- *
- * See BigDecimal#divmod.
- */
-static VALUE
-BigDecimal_mod(VALUE self, VALUE r) /* %: a%b = a - (a.to_f/b).floor * b */
-{
- ENTER(3);
- Real *div = NULL, *mod = NULL;
-
- if (BigDecimal_DoDivmod(self, r, &div, &mod)) {
- SAVE(div); SAVE(mod);
- return VpCheckGetValue(mod);
- }
- return DoSomeOne(self, r, '%');
-}
-
-static VALUE
-BigDecimal_divremain(VALUE self, VALUE r, Real **dv, Real **rv)
-{
- ENTER(10);
- size_t mx;
- Real *a = NULL, *b = NULL, *c = NULL, *res = NULL, *d = NULL, *rr = NULL, *ff = NULL;
- Real *f = NULL;
-
- GUARD_OBJ(a, GetVpValue(self, 1));
- if (RB_TYPE_P(r, T_FLOAT)) {
- b = GetVpValueWithPrec(r, 0, 1);
- }
- else if (RB_TYPE_P(r, T_RATIONAL)) {
- b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
- }
- else {
- b = GetVpValue(r, 0);
- }
-
- if (!b) return DoSomeOne(self, r, rb_intern("remainder"));
- SAVE(b);
-
- if (VpIsPosInf(b) || VpIsNegInf(b)) {
- GUARD_OBJ(*dv, NewZeroWrapLimited(1, 1));
- VpSetZero(*dv, 1);
- *rv = a;
- return Qnil;
- }
-
- mx = (a->MaxPrec + b->MaxPrec) *VpBaseFig();
- GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
- GUARD_OBJ(res, NewZeroWrapNolimit(1, (mx+1) * 2 + (VpBaseFig() + 1)));
- GUARD_OBJ(rr, NewZeroWrapNolimit(1, (mx+1) * 2 + (VpBaseFig() + 1)));
- GUARD_OBJ(ff, NewZeroWrapNolimit(1, (mx+1) * 2 + (VpBaseFig() + 1)));
-
- VpDivd(c, res, a, b);
-
- mx = c->Prec *(VpBaseFig() + 1);
-
- GUARD_OBJ(d, NewZeroWrapLimited(1, mx));
- GUARD_OBJ(f, NewZeroWrapLimited(1, mx));
-
- VpActiveRound(d, c, VP_ROUND_DOWN, 0); /* 0: round off */
-
- VpFrac(f, c);
- VpMult(rr, f, b);
- VpAddSub(ff, res, rr, 1);
-
- *dv = d;
- *rv = ff;
- return Qnil;
-}
-
-/* call-seq:
- * remainder(value)
- *
- * Returns the remainder from dividing by the value.
- *
- * x.remainder(y) means x-y*(x/y).truncate
- */
-static VALUE
-BigDecimal_remainder(VALUE self, VALUE r) /* remainder */
-{
- VALUE f;
- Real *d, *rv = 0;
- f = BigDecimal_divremain(self, r, &d, &rv);
- if (!NIL_P(f)) return f;
- return VpCheckGetValue(rv);
-}
-
-/* call-seq:
- * divmod(value)
- *
- * Divides by the specified value, and returns the quotient and modulus
- * as BigDecimal numbers. The quotient is rounded towards negative infinity.
- *
- * For example:
- *
- * require 'bigdecimal'
- *
- * a = BigDecimal("42")
- * b = BigDecimal("9")
- *
- * q, m = a.divmod(b)
- *
- * c = q * b + m
- *
- * a == c #=> true
- *
- * The quotient q is (a/b).floor, and the modulus is the amount that must be
- * added to q * b to get a.
- */
-static VALUE
-BigDecimal_divmod(VALUE self, VALUE r)
-{
- ENTER(5);
- Real *div = NULL, *mod = NULL;
-
- if (BigDecimal_DoDivmod(self, r, &div, &mod)) {
- SAVE(div); SAVE(mod);
- return rb_assoc_new(VpCheckGetValue(div), VpCheckGetValue(mod));
- }
- return DoSomeOne(self,r,rb_intern("divmod"));
-}
-
-/*
- * Do the same manner as Float#div when n is nil.
- * Do the same manner as BigDecimal#quo when n is 0.
- */
-static inline VALUE
-BigDecimal_div2(VALUE self, VALUE b, VALUE n)
-{
- ENTER(5);
- SIGNED_VALUE ix;
-
- if (NIL_P(n)) { /* div in Float sense */
- Real *div = NULL;
- Real *mod;
- if (BigDecimal_DoDivmod(self, b, &div, &mod)) {
- return BigDecimal_to_i(VpCheckGetValue(div));
- }
- return DoSomeOne(self, b, rb_intern("div"));
- }
-
- /* div in BigDecimal sense */
- ix = check_int_precision(n);
- if (ix == 0) {
- return BigDecimal_div(self, b);
- }
- else {
- Real *res = NULL;
- Real *av = NULL, *bv = NULL, *cv = NULL;
- size_t mx = ix + VpBaseFig()*2;
- size_t b_prec = ix;
- size_t pl = VpSetPrecLimit(0);
-
- GUARD_OBJ(cv, NewZeroWrapLimited(1, mx + VpBaseFig()));
- GUARD_OBJ(av, GetVpValue(self, 1));
- /* TODO: I want to refactor this precision control for a float value later
- * by introducing an implicit conversion function instead of
- * GetVpValueWithPrec. */
- if (RB_FLOAT_TYPE_P(b) && b_prec > BIGDECIMAL_DOUBLE_FIGURES) {
- b_prec = BIGDECIMAL_DOUBLE_FIGURES;
- }
- GUARD_OBJ(bv, GetVpValueWithPrec(b, b_prec, 1));
- mx = av->Prec + bv->Prec + 2;
- if (mx <= cv->MaxPrec) mx = cv->MaxPrec + 1;
- GUARD_OBJ(res, NewZeroWrapNolimit(1, (mx * 2 + 2)*VpBaseFig()));
- VpDivd(cv, res, av, bv);
- VpSetPrecLimit(pl);
- VpLeftRound(cv, VpGetRoundMode(), ix);
- return VpCheckGetValue(cv);
- }
-}
-
- /*
- * Document-method: BigDecimal#div
- *
- * call-seq:
- * div(value) -> integer
- * div(value, digits) -> bigdecimal or integer
- *
- * Divide by the specified value.
- *
- * digits:: If specified and less than the number of significant digits of the
- * result, the result is rounded to that number of digits, according
- * to BigDecimal.mode.
- *
- * If digits is 0, the result is the same as for the / operator
- * or #quo.
- *
- * If digits is not specified, the result is an integer,
- * by analogy with Float#div; see also BigDecimal#divmod.
- *
- * See BigDecimal#/.
- * See BigDecimal#quo.
- *
- * Examples:
- *
- * a = BigDecimal("4")
- * b = BigDecimal("3")
- *
- * a.div(b, 3) # => 0.133e1
- *
- * a.div(b, 0) # => 0.1333333333333333333e1
- * a / b # => 0.1333333333333333333e1
- * a.quo(b) # => 0.1333333333333333333e1
- *
- * a.div(b) # => 1
- */
-static VALUE
-BigDecimal_div3(int argc, VALUE *argv, VALUE self)
-{
- VALUE b,n;
-
- rb_scan_args(argc, argv, "11", &b, &n);
-
- return BigDecimal_div2(self, b, n);
-}
-
- /*
- * call-seq:
- * add(value, ndigits) -> new_bigdecimal
- *
- * Returns the \BigDecimal sum of +self+ and +value+
- * with a precision of +ndigits+ decimal digits.
- *
- * When +ndigits+ is less than the number of significant digits
- * in the sum, the sum is rounded to that number of digits,
- * according to the current rounding mode; see BigDecimal.mode.
- *
- * Examples:
- *
- * # Set the rounding mode.
- * BigDecimal.mode(BigDecimal::ROUND_MODE, :half_up)
- * b = BigDecimal('111111.111')
- * b.add(1, 0) # => 0.111112111e6
- * b.add(1, 3) # => 0.111e6
- * b.add(1, 6) # => 0.111112e6
- * b.add(1, 15) # => 0.111112111e6
- * b.add(1.0, 15) # => 0.111112111e6
- * b.add(Rational(1, 1), 15) # => 0.111112111e6
- *
- */
-
-static VALUE
-BigDecimal_add2(VALUE self, VALUE b, VALUE n)
-{
- ENTER(2);
- Real *cv;
- SIGNED_VALUE mx = check_int_precision(n);
- if (mx == 0) return BigDecimal_add(self, b);
- else {
- size_t pl = VpSetPrecLimit(0);
- VALUE c = BigDecimal_add(self, b);
- VpSetPrecLimit(pl);
- GUARD_OBJ(cv, GetVpValue(c, 1));
- VpLeftRound(cv, VpGetRoundMode(), mx);
- return VpCheckGetValue(cv);
- }
-}
-
-/* call-seq:
- * sub(value, digits) -> bigdecimal
- *
- * Subtract the specified value.
- *
- * e.g.
- * c = a.sub(b,n)
- *
- * digits:: If specified and less than the number of significant digits of the
- * result, the result is rounded to that number of digits, according
- * to BigDecimal.mode.
- *
- */
-static VALUE
-BigDecimal_sub2(VALUE self, VALUE b, VALUE n)
-{
- ENTER(2);
- Real *cv;
- SIGNED_VALUE mx = check_int_precision(n);
- if (mx == 0) return BigDecimal_sub(self, b);
- else {
- size_t pl = VpSetPrecLimit(0);
- VALUE c = BigDecimal_sub(self, b);
- VpSetPrecLimit(pl);
- GUARD_OBJ(cv, GetVpValue(c, 1));
- VpLeftRound(cv, VpGetRoundMode(), mx);
- return VpCheckGetValue(cv);
- }
-}
-
- /*
- * call-seq:
- * mult(other, ndigits) -> bigdecimal
- *
- * Returns the \BigDecimal product of +self+ and +value+
- * with a precision of +ndigits+ decimal digits.
- *
- * When +ndigits+ is less than the number of significant digits
- * in the sum, the sum is rounded to that number of digits,
- * according to the current rounding mode; see BigDecimal.mode.
- *
- * Examples:
- *
- * # Set the rounding mode.
- * BigDecimal.mode(BigDecimal::ROUND_MODE, :half_up)
- * b = BigDecimal('555555.555')
- * b.mult(3, 0) # => 0.1666666665e7
- * b.mult(3, 3) # => 0.167e7
- * b.mult(3, 6) # => 0.166667e7
- * b.mult(3, 15) # => 0.1666666665e7
- * b.mult(3.0, 0) # => 0.1666666665e7
- * b.mult(Rational(3, 1), 0) # => 0.1666666665e7
- * b.mult(Complex(3, 0), 0) # => (0.1666666665e7+0.0i)
- *
- */
-
-static VALUE
-BigDecimal_mult2(VALUE self, VALUE b, VALUE n)
-{
- ENTER(2);
- Real *cv;
- SIGNED_VALUE mx = check_int_precision(n);
- if (mx == 0) return BigDecimal_mult(self, b);
- else {
- size_t pl = VpSetPrecLimit(0);
- VALUE c = BigDecimal_mult(self, b);
- VpSetPrecLimit(pl);
- GUARD_OBJ(cv, GetVpValue(c, 1));
- VpLeftRound(cv, VpGetRoundMode(), mx);
- return VpCheckGetValue(cv);
- }
-}
-
-/*
- * call-seq:
- * abs -> bigdecimal
- *
- * Returns the \BigDecimal absolute value of +self+:
- *
- * BigDecimal('5').abs # => 0.5e1
- * BigDecimal('-3').abs # => 0.3e1
- *
- */
-
-static VALUE
-BigDecimal_abs(VALUE self)
-{
- ENTER(5);
- Real *c, *a;
- size_t mx;
-
- GUARD_OBJ(a, GetVpValue(self, 1));
- mx = a->Prec *(VpBaseFig() + 1);
- GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
- VpAsgn(c, a, 1);
- VpChangeSign(c, 1);
- return VpCheckGetValue(c);
-}
-
-/* call-seq:
- * sqrt(n)
- *
- * Returns the square root of the value.
- *
- * Result has at least n significant digits.
- */
-static VALUE
-BigDecimal_sqrt(VALUE self, VALUE nFig)
-{
- ENTER(5);
- Real *c, *a;
- size_t mx, n;
-
- GUARD_OBJ(a, GetVpValue(self, 1));
- mx = a->Prec * (VpBaseFig() + 1);
-
- n = check_int_precision(nFig);
- n += VpDblFig() + VpBaseFig();
- if (mx <= n) mx = n;
- GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
- VpSqrt(c, a);
- return VpCheckGetValue(c);
-}
-
-/* Return the integer part of the number, as a BigDecimal.
- */
-static VALUE
-BigDecimal_fix(VALUE self)
-{
- ENTER(5);
- Real *c, *a;
- size_t mx;
-
- GUARD_OBJ(a, GetVpValue(self, 1));
- mx = a->Prec *(VpBaseFig() + 1);
- GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
- VpActiveRound(c, a, VP_ROUND_DOWN, 0); /* 0: round off */
- return VpCheckGetValue(c);
-}
-
-/* call-seq:
- * round(n, mode)
- *
- * Round to the nearest integer (by default), returning the result as a
- * BigDecimal if n is specified, or as an Integer if it isn't.
- *
- * BigDecimal('3.14159').round #=> 3
- * BigDecimal('8.7').round #=> 9
- * BigDecimal('-9.9').round #=> -10
- *
- * BigDecimal('3.14159').round(2).class.name #=> "BigDecimal"
- * BigDecimal('3.14159').round.class.name #=> "Integer"
- *
- * If n is specified and positive, the fractional part of the result has no
- * more than that many digits.
- *
- * If n is specified and negative, at least that many digits to the left of the
- * decimal point will be 0 in the result, and return value will be an Integer.
- *
- * BigDecimal('3.14159').round(3) #=> 3.142
- * BigDecimal('13345.234').round(-2) #=> 13300
- *
- * The value of the optional mode argument can be used to determine how
- * rounding is performed; see BigDecimal.mode.
- */
-static VALUE
-BigDecimal_round(int argc, VALUE *argv, VALUE self)
-{
- ENTER(5);
- Real *c, *a;
- int iLoc = 0;
- VALUE vLoc;
- VALUE vRound;
- int round_to_int = 0;
- size_t mx, pl;
-
- unsigned short sw = VpGetRoundMode();
-
- switch (rb_scan_args(argc, argv, "02", &vLoc, &vRound)) {
- case 0:
- iLoc = 0;
- round_to_int = 1;
- break;
- case 1:
- if (RB_TYPE_P(vLoc, T_HASH)) {
- sw = check_rounding_mode_option(vLoc);
- }
- else {
- iLoc = NUM2INT(vLoc);
- if (iLoc < 1) round_to_int = 1;
- }
- break;
- case 2:
- iLoc = NUM2INT(vLoc);
- if (RB_TYPE_P(vRound, T_HASH)) {
- sw = check_rounding_mode_option(vRound);
- }
- else {
- sw = check_rounding_mode(vRound);
- }
- break;
- default:
- break;
- }
-
- pl = VpSetPrecLimit(0);
- GUARD_OBJ(a, GetVpValue(self, 1));
- mx = a->Prec * (VpBaseFig() + 1);
- GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
- VpSetPrecLimit(pl);
- VpActiveRound(c, a, sw, iLoc);
- if (round_to_int) {
- return BigDecimal_to_i(VpCheckGetValue(c));
- }
- return VpCheckGetValue(c);
-}
-
-/* call-seq:
- * truncate(n)
- *
- * Truncate to the nearest integer (by default), returning the result as a
- * BigDecimal.
- *
- * BigDecimal('3.14159').truncate #=> 3
- * BigDecimal('8.7').truncate #=> 8
- * BigDecimal('-9.9').truncate #=> -9
- *
- * If n is specified and positive, the fractional part of the result has no
- * more than that many digits.
- *
- * If n is specified and negative, at least that many digits to the left of the
- * decimal point will be 0 in the result.
- *
- * BigDecimal('3.14159').truncate(3) #=> 3.141
- * BigDecimal('13345.234').truncate(-2) #=> 13300.0
- */
-static VALUE
-BigDecimal_truncate(int argc, VALUE *argv, VALUE self)
-{
- ENTER(5);
- Real *c, *a;
- int iLoc;
- VALUE vLoc;
- size_t mx, pl = VpSetPrecLimit(0);
-
- if (rb_scan_args(argc, argv, "01", &vLoc) == 0) {
- iLoc = 0;
- }
- else {
- iLoc = NUM2INT(vLoc);
- }
-
- GUARD_OBJ(a, GetVpValue(self, 1));
- mx = a->Prec * (VpBaseFig() + 1);
- GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
- VpSetPrecLimit(pl);
- VpActiveRound(c, a, VP_ROUND_DOWN, iLoc); /* 0: truncate */
- if (argc == 0) {
- return BigDecimal_to_i(VpCheckGetValue(c));
- }
- return VpCheckGetValue(c);
-}
-
-/* Return the fractional part of the number, as a BigDecimal.
- */
-static VALUE
-BigDecimal_frac(VALUE self)
-{
- ENTER(5);
- Real *c, *a;
- size_t mx;
-
- GUARD_OBJ(a, GetVpValue(self, 1));
- mx = a->Prec * (VpBaseFig() + 1);
- GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
- VpFrac(c, a);
- return VpCheckGetValue(c);
-}
-
-/* call-seq:
- * floor(n)
- *
- * Return the largest integer less than or equal to the value, as a BigDecimal.
- *
- * BigDecimal('3.14159').floor #=> 3
- * BigDecimal('-9.1').floor #=> -10
- *
- * If n is specified and positive, the fractional part of the result has no
- * more than that many digits.
- *
- * If n is specified and negative, at least that
- * many digits to the left of the decimal point will be 0 in the result.
- *
- * BigDecimal('3.14159').floor(3) #=> 3.141
- * BigDecimal('13345.234').floor(-2) #=> 13300.0
- */
-static VALUE
-BigDecimal_floor(int argc, VALUE *argv, VALUE self)
-{
- ENTER(5);
- Real *c, *a;
- int iLoc;
- VALUE vLoc;
- size_t mx, pl = VpSetPrecLimit(0);
-
- if (rb_scan_args(argc, argv, "01", &vLoc)==0) {
- iLoc = 0;
- }
- else {
- iLoc = NUM2INT(vLoc);
- }
-
- GUARD_OBJ(a, GetVpValue(self, 1));
- mx = a->Prec * (VpBaseFig() + 1);
- GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
- VpSetPrecLimit(pl);
- VpActiveRound(c, a, VP_ROUND_FLOOR, iLoc);
-#ifdef BIGDECIMAL_DEBUG
- VPrint(stderr, "floor: c=%\n", c);
-#endif
- if (argc == 0) {
- return BigDecimal_to_i(VpCheckGetValue(c));
- }
- return VpCheckGetValue(c);
-}
-
-/* call-seq:
- * ceil(n)
- *
- * Return the smallest integer greater than or equal to the value, as a BigDecimal.
- *
- * BigDecimal('3.14159').ceil #=> 4
- * BigDecimal('-9.1').ceil #=> -9
- *
- * If n is specified and positive, the fractional part of the result has no
- * more than that many digits.
- *
- * If n is specified and negative, at least that
- * many digits to the left of the decimal point will be 0 in the result.
- *
- * BigDecimal('3.14159').ceil(3) #=> 3.142
- * BigDecimal('13345.234').ceil(-2) #=> 13400.0
- */
-static VALUE
-BigDecimal_ceil(int argc, VALUE *argv, VALUE self)
-{
- ENTER(5);
- Real *c, *a;
- int iLoc;
- VALUE vLoc;
- size_t mx, pl = VpSetPrecLimit(0);
-
- if (rb_scan_args(argc, argv, "01", &vLoc) == 0) {
- iLoc = 0;
- } else {
- iLoc = NUM2INT(vLoc);
- }
-
- GUARD_OBJ(a, GetVpValue(self, 1));
- mx = a->Prec * (VpBaseFig() + 1);
- GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
- VpSetPrecLimit(pl);
- VpActiveRound(c, a, VP_ROUND_CEIL, iLoc);
- if (argc == 0) {
- return BigDecimal_to_i(VpCheckGetValue(c));
- }
- return VpCheckGetValue(c);
-}
-
-/* call-seq:
- * to_s(s)
- *
- * Converts the value to a string.
- *
- * The default format looks like 0.xxxxEnn.
- *
- * The optional parameter s consists of either an integer; or an optional '+'
- * or ' ', followed by an optional number, followed by an optional 'E' or 'F'.
- *
- * If there is a '+' at the start of s, positive values are returned with
- * a leading '+'.
- *
- * A space at the start of s returns positive values with a leading space.
- *
- * If s contains a number, a space is inserted after each group of that many
- * fractional digits.
- *
- * If s ends with an 'E', engineering notation (0.xxxxEnn) is used.
- *
- * If s ends with an 'F', conventional floating point notation is used.
- *
- * Examples:
- *
- * BigDecimal('-123.45678901234567890').to_s('5F')
- * #=> '-123.45678 90123 45678 9'
- *
- * BigDecimal('123.45678901234567890').to_s('+8F')
- * #=> '+123.45678901 23456789'
- *
- * BigDecimal('123.45678901234567890').to_s(' F')
- * #=> ' 123.4567890123456789'
- */
-static VALUE
-BigDecimal_to_s(int argc, VALUE *argv, VALUE self)
-{
- ENTER(5);
- int fmt = 0; /* 0: E format, 1: F format */
- int fPlus = 0; /* 0: default, 1: set ' ' before digits, 2: set '+' before digits. */
- Real *vp;
- volatile VALUE str;
- char *psz;
- char ch;
- size_t nc, mc = 0;
- SIGNED_VALUE m;
- VALUE f;
-
- GUARD_OBJ(vp, GetVpValue(self, 1));
-
- if (rb_scan_args(argc, argv, "01", &f) == 1) {
- if (RB_TYPE_P(f, T_STRING)) {
- psz = StringValueCStr(f);
- if (*psz == ' ') {
- fPlus = 1;
- psz++;
- }
- else if (*psz == '+') {
- fPlus = 2;
- psz++;
- }
- while ((ch = *psz++) != 0) {
- if (ISSPACE(ch)) {
- continue;
- }
- if (!ISDIGIT(ch)) {
- if (ch == 'F' || ch == 'f') {
- fmt = 1; /* F format */
- }
- break;
- }
- mc = mc*10 + ch - '0';
- }
- }
- else {
- m = NUM2INT(f);
- if (m <= 0) {
- rb_raise(rb_eArgError, "argument must be positive");
- }
- mc = (size_t)m;
- }
- }
- if (fmt) {
- nc = VpNumOfChars(vp, "F");
- }
- else {
- nc = VpNumOfChars(vp, "E");
- }
- if (mc > 0) {
- nc += (nc + mc - 1) / mc + 1;
- }
-
- str = rb_usascii_str_new(0, nc);
- psz = RSTRING_PTR(str);
-
- if (fmt) {
- VpToFString(vp, psz, RSTRING_LEN(str), mc, fPlus);
- }
- else {
- VpToString (vp, psz, RSTRING_LEN(str), mc, fPlus);
- }
- rb_str_resize(str, strlen(psz));
- return str;
-}
-
-/* Splits a BigDecimal number into four parts, returned as an array of values.
- *
- * The first value represents the sign of the BigDecimal, and is -1 or 1, or 0
- * if the BigDecimal is Not a Number.
- *
- * The second value is a string representing the significant digits of the
- * BigDecimal, with no leading zeros.
- *
- * The third value is the base used for arithmetic (currently always 10) as an
- * Integer.
- *
- * The fourth value is an Integer exponent.
- *
- * If the BigDecimal can be represented as 0.xxxxxx*10**n, then xxxxxx is the
- * string of significant digits with no leading zeros, and n is the exponent.
- *
- * From these values, you can translate a BigDecimal to a float as follows:
- *
- * sign, significant_digits, base, exponent = a.split
- * f = sign * "0.#{significant_digits}".to_f * (base ** exponent)
- *
- * (Note that the to_f method is provided as a more convenient way to translate
- * a BigDecimal to a Float.)
- */
-static VALUE
-BigDecimal_split(VALUE self)
-{
- ENTER(5);
- Real *vp;
- VALUE obj,str;
- ssize_t e, s;
- char *psz1;
-
- GUARD_OBJ(vp, GetVpValue(self, 1));
- str = rb_str_new(0, VpNumOfChars(vp, "E"));
- psz1 = RSTRING_PTR(str);
- VpSzMantissa(vp, psz1, RSTRING_LEN(str));
- s = 1;
- if(psz1[0] == '-') {
- size_t len = strlen(psz1 + 1);
-
- memmove(psz1, psz1 + 1, len);
- psz1[len] = '\0';
- s = -1;
- }
- if (psz1[0] == 'N') s = 0; /* NaN */
- e = VpExponent10(vp);
- obj = rb_ary_new2(4);
- rb_ary_push(obj, INT2FIX(s));
- rb_ary_push(obj, str);
- rb_str_resize(str, strlen(psz1));
- rb_ary_push(obj, INT2FIX(10));
- rb_ary_push(obj, SSIZET2NUM(e));
- return obj;
-}
-
-/* Returns the exponent of the BigDecimal number, as an Integer.
- *
- * If the number can be represented as 0.xxxxxx*10**n where xxxxxx is a string
- * of digits with no leading zeros, then n is the exponent.
- */
-static VALUE
-BigDecimal_exponent(VALUE self)
-{
- ssize_t e = VpExponent10(GetVpValue(self, 1));
- return SSIZET2NUM(e);
-}
-
-/* Returns a string representation of self.
- *
- * BigDecimal("1234.5678").inspect
- * #=> "0.12345678e4"
- */
-static VALUE
-BigDecimal_inspect(VALUE self)
-{
- ENTER(5);
- Real *vp;
- volatile VALUE str;
- size_t nc;
-
- GUARD_OBJ(vp, GetVpValue(self, 1));
- nc = VpNumOfChars(vp, "E");
-
- str = rb_str_new(0, nc);
- VpToString(vp, RSTRING_PTR(str), RSTRING_LEN(str), 0, 0);
- rb_str_resize(str, strlen(RSTRING_PTR(str)));
- return str;
-}
-
-static VALUE BigMath_s_exp(VALUE, VALUE, VALUE);
-static VALUE BigMath_s_log(VALUE, VALUE, VALUE);
-
-#define BigMath_exp(x, n) BigMath_s_exp(rb_mBigMath, (x), (n))
-#define BigMath_log(x, n) BigMath_s_log(rb_mBigMath, (x), (n))
-
-inline static int
-is_integer(VALUE x)
-{
- return (RB_TYPE_P(x, T_FIXNUM) || RB_TYPE_P(x, T_BIGNUM));
-}
-
-inline static int
-is_negative(VALUE x)
-{
- if (FIXNUM_P(x)) {
- return FIX2LONG(x) < 0;
- }
- else if (RB_TYPE_P(x, T_BIGNUM)) {
- return FIX2INT(rb_big_cmp(x, INT2FIX(0))) < 0;
- }
- else if (RB_TYPE_P(x, T_FLOAT)) {
- return RFLOAT_VALUE(x) < 0.0;
- }
- return RTEST(rb_funcall(x, '<', 1, INT2FIX(0)));
-}
-
-#define is_positive(x) (!is_negative(x))
-
-inline static int
-is_zero(VALUE x)
-{
- VALUE num;
-
- switch (TYPE(x)) {
- case T_FIXNUM:
- return FIX2LONG(x) == 0;
-
- case T_BIGNUM:
- return Qfalse;
-
- case T_RATIONAL:
- num = rb_rational_num(x);
- return FIXNUM_P(num) && FIX2LONG(num) == 0;
-
- default:
- break;
- }
-
- return RTEST(rb_funcall(x, id_eq, 1, INT2FIX(0)));
-}
-
-inline static int
-is_one(VALUE x)
-{
- VALUE num, den;
-
- switch (TYPE(x)) {
- case T_FIXNUM:
- return FIX2LONG(x) == 1;
-
- case T_BIGNUM:
- return Qfalse;
-
- case T_RATIONAL:
- num = rb_rational_num(x);
- den = rb_rational_den(x);
- return FIXNUM_P(den) && FIX2LONG(den) == 1 &&
- FIXNUM_P(num) && FIX2LONG(num) == 1;
-
- default:
- break;
- }
-
- return RTEST(rb_funcall(x, id_eq, 1, INT2FIX(1)));
-}
-
-inline static int
-is_even(VALUE x)
-{
- switch (TYPE(x)) {
- case T_FIXNUM:
- return (FIX2LONG(x) % 2) == 0;
-
- case T_BIGNUM:
- {
- unsigned long l;
- rb_big_pack(x, &l, 1);
- return l % 2 == 0;
- }
-
- default:
- break;
- }
-
- return 0;
-}
-
-static VALUE
-bigdecimal_power_by_bigdecimal(Real const* x, Real const* exp, ssize_t const n)
-{
- VALUE log_x, multiplied, y;
- volatile VALUE obj = exp->obj;
-
- if (VpIsZero(exp)) {
- return VpCheckGetValue(NewOneWrapLimited(1, n));
- }
-
- log_x = BigMath_log(x->obj, SSIZET2NUM(n+1));
- multiplied = BigDecimal_mult2(exp->obj, log_x, SSIZET2NUM(n+1));
- y = BigMath_exp(multiplied, SSIZET2NUM(n));
- RB_GC_GUARD(obj);
-
- return y;
-}
-
-/* call-seq:
- * power(n)
- * power(n, prec)
- *
- * Returns the value raised to the power of n.
- *
- * Note that n must be an Integer.
- *
- * Also available as the operator **.
- */
-static VALUE
-BigDecimal_power(int argc, VALUE*argv, VALUE self)
-{
- ENTER(5);
- VALUE vexp, prec;
- Real* exp = NULL;
- Real *x, *y;
- ssize_t mp, ma, n;
- SIGNED_VALUE int_exp;
- double d;
-
- rb_scan_args(argc, argv, "11", &vexp, &prec);
-
- GUARD_OBJ(x, GetVpValue(self, 1));
- n = NIL_P(prec) ? (ssize_t)(x->Prec*VpBaseFig()) : NUM2SSIZET(prec);
-
- if (VpIsNaN(x)) {
- y = NewZeroWrapLimited(1, n);
- VpSetNaN(y);
- RB_GC_GUARD(y->obj);
- return VpCheckGetValue(y);
- }
-
- retry:
- switch (TYPE(vexp)) {
- case T_FIXNUM:
- break;
-
- case T_BIGNUM:
- break;
-
- case T_FLOAT:
- d = RFLOAT_VALUE(vexp);
- if (d == round(d)) {
- if (FIXABLE(d)) {
- vexp = LONG2FIX((long)d);
- }
- else {
- vexp = rb_dbl2big(d);
- }
- goto retry;
- }
- if (NIL_P(prec)) {
- n += BIGDECIMAL_DOUBLE_FIGURES;
- }
- exp = GetVpValueWithPrec(vexp, 0, 1);
- break;
-
- case T_RATIONAL:
- if (is_zero(rb_rational_num(vexp))) {
- if (is_positive(vexp)) {
- vexp = INT2FIX(0);
- goto retry;
- }
- }
- else if (is_one(rb_rational_den(vexp))) {
- vexp = rb_rational_num(vexp);
- goto retry;
- }
- exp = GetVpValueWithPrec(vexp, n, 1);
- if (NIL_P(prec)) {
- n += n;
- }
- break;
-
- case T_DATA:
- if (is_kind_of_BigDecimal(vexp)) {
- VALUE zero = INT2FIX(0);
- VALUE rounded = BigDecimal_round(1, &zero, vexp);
- if (RTEST(BigDecimal_eq(vexp, rounded))) {
- vexp = BigDecimal_to_i(vexp);
- goto retry;
- }
- if (NIL_P(prec)) {
- GUARD_OBJ(y, GetVpValue(vexp, 1));
- n += y->Prec*VpBaseFig();
- }
- exp = DATA_PTR(vexp);
- break;
- }
- /* fall through */
- default:
- rb_raise(rb_eTypeError,
- "wrong argument type %"PRIsVALUE" (expected scalar Numeric)",
- RB_OBJ_CLASSNAME(vexp));
- }
-
- if (VpIsZero(x)) {
- if (is_negative(vexp)) {
- y = NewZeroWrapNolimit(1, n);
- if (BIGDECIMAL_NEGATIVE_P(x)) {
- if (is_integer(vexp)) {
- if (is_even(vexp)) {
- /* (-0) ** (-even_integer) -> Infinity */
- VpSetPosInf(y);
- }
- else {
- /* (-0) ** (-odd_integer) -> -Infinity */
- VpSetNegInf(y);
- }
- }
- else {
- /* (-0) ** (-non_integer) -> Infinity */
- VpSetPosInf(y);
- }
- }
- else {
- /* (+0) ** (-num) -> Infinity */
- VpSetPosInf(y);
- }
- RB_GC_GUARD(y->obj);
- return VpCheckGetValue(y);
- }
- else if (is_zero(vexp)) {
- return VpCheckGetValue(NewOneWrapLimited(1, n));
- }
- else {
- return VpCheckGetValue(NewZeroWrapLimited(1, n));
- }
- }
-
- if (is_zero(vexp)) {
- return VpCheckGetValue(NewOneWrapLimited(1, n));
- }
- else if (is_one(vexp)) {
- return self;
- }
-
- if (VpIsInf(x)) {
- if (is_negative(vexp)) {
- if (BIGDECIMAL_NEGATIVE_P(x)) {
- if (is_integer(vexp)) {
- if (is_even(vexp)) {
- /* (-Infinity) ** (-even_integer) -> +0 */
- return VpCheckGetValue(NewZeroWrapLimited(1, n));
- }
- else {
- /* (-Infinity) ** (-odd_integer) -> -0 */
- return VpCheckGetValue(NewZeroWrapLimited(-1, n));
- }
- }
- else {
- /* (-Infinity) ** (-non_integer) -> -0 */
- return VpCheckGetValue(NewZeroWrapLimited(-1, n));
- }
- }
- else {
- return VpCheckGetValue(NewZeroWrapLimited(1, n));
- }
- }
- else {
- y = NewZeroWrapLimited(1, n);
- if (BIGDECIMAL_NEGATIVE_P(x)) {
- if (is_integer(vexp)) {
- if (is_even(vexp)) {
- VpSetPosInf(y);
- }
- else {
- VpSetNegInf(y);
- }
- }
- else {
- /* TODO: support complex */
- rb_raise(rb_eMathDomainError,
- "a non-integral exponent for a negative base");
- }
- }
- else {
- VpSetPosInf(y);
- }
- return VpCheckGetValue(y);
- }
- }
-
- if (exp != NULL) {
- return bigdecimal_power_by_bigdecimal(x, exp, n);
- }
- else if (RB_TYPE_P(vexp, T_BIGNUM)) {
- VALUE abs_value = BigDecimal_abs(self);
- if (is_one(abs_value)) {
- return VpCheckGetValue(NewOneWrapLimited(1, n));
- }
- else if (RTEST(rb_funcall(abs_value, '<', 1, INT2FIX(1)))) {
- if (is_negative(vexp)) {
- y = NewZeroWrapLimited(1, n);
- VpSetInf(y, (is_even(vexp) ? 1 : -1) * VpGetSign(x));
- return VpCheckGetValue(y);
- }
- else if (BIGDECIMAL_NEGATIVE_P(x) && is_even(vexp)) {
- return VpCheckGetValue(NewZeroWrapLimited(-1, n));
- }
- else {
- return VpCheckGetValue(NewZeroWrapLimited(1, n));
- }
- }
- else {
- if (is_positive(vexp)) {
- y = NewZeroWrapLimited(1, n);
- VpSetInf(y, (is_even(vexp) ? 1 : -1) * VpGetSign(x));
- return VpCheckGetValue(y);
- }
- else if (BIGDECIMAL_NEGATIVE_P(x) && is_even(vexp)) {
- return VpCheckGetValue(NewZeroWrapLimited(-1, n));
- }
- else {
- return VpCheckGetValue(NewZeroWrapLimited(1, n));
- }
- }
- }
-
- int_exp = FIX2LONG(vexp);
- ma = int_exp;
- if (ma < 0) ma = -ma;
- if (ma == 0) ma = 1;
-
- if (VpIsDef(x)) {
- mp = x->Prec * (VpBaseFig() + 1);
- GUARD_OBJ(y, NewZeroWrapLimited(1, mp * (ma + 1)));
- }
- else {
- GUARD_OBJ(y, NewZeroWrapLimited(1, 1));
- }
- VpPowerByInt(y, x, int_exp);
- if (!NIL_P(prec) && VpIsDef(y)) {
- VpMidRound(y, VpGetRoundMode(), n);
- }
- return VpCheckGetValue(y);
-}
-
-/* call-seq:
- * self ** other -> bigdecimal
- *
- * Returns the \BigDecimal value of +self+ raised to power +other+:
- *
- * b = BigDecimal('3.14')
- * b ** 2 # => 0.98596e1
- * b ** 2.0 # => 0.98596e1
- * b ** Rational(2, 1) # => 0.98596e1
- *
- * Related: BigDecimal#power.
- *
- */
-static VALUE
-BigDecimal_power_op(VALUE self, VALUE exp)
-{
- return BigDecimal_power(1, &exp, self);
-}
-
-/* :nodoc:
- *
- * private method for dup and clone the provided BigDecimal +other+
- */
-static VALUE
-BigDecimal_initialize_copy(VALUE self, VALUE other)
-{
- Real *pv = rb_check_typeddata(self, &BigDecimal_data_type);
- Real *x = rb_check_typeddata(other, &BigDecimal_data_type);
-
- if (self != other) {
- DATA_PTR(self) = VpCopy(pv, x);
- }
- return self;
-}
-
-static VALUE
-BigDecimal_clone(VALUE self)
-{
- return self;
-}
-
-#ifdef HAVE_RB_OPTS_EXCEPTION_P
-int rb_opts_exception_p(VALUE opts, int default_value);
-#define opts_exception_p(opts) rb_opts_exception_p((opts), 1)
-#else
-static int
-opts_exception_p(VALUE opts)
-{
- static ID kwds[1];
- VALUE exception;
- if (!kwds[0]) {
- kwds[0] = rb_intern_const("exception");
- }
- if (!rb_get_kwargs(opts, kwds, 0, 1, &exception)) return 1;
- switch (exception) {
- case Qtrue: case Qfalse:
- break;
- default:
- rb_raise(rb_eArgError, "true or false is expected as exception: %+"PRIsVALUE,
- exception);
- }
- return exception != Qfalse;
-}
-#endif
-
-static VALUE
-check_exception(VALUE bd)
-{
- assert(is_kind_of_BigDecimal(bd));
-
- Real *vp;
- TypedData_Get_Struct(bd, Real, &BigDecimal_data_type, vp);
- VpCheckGetValue(vp); /* VpCheckGetValue performs exception check */
-
- return bd;
-}
-
-static VALUE
-rb_uint64_convert_to_BigDecimal(uint64_t uval, RB_UNUSED_VAR(size_t digs), int raise_exception)
-{
- VALUE obj = TypedData_Wrap_Struct(rb_cBigDecimal, &BigDecimal_data_type, 0);
-
- Real *vp;
- if (uval == 0) {
- vp = rbd_allocate_struct(1);
- vp->MaxPrec = 1;
- vp->Prec = 1;
- vp->exponent = 1;
- VpSetZero(vp, 1);
- vp->frac[0] = 0;
- }
- else if (uval < BASE) {
- vp = rbd_allocate_struct(1);
- vp->MaxPrec = 1;
- vp->Prec = 1;
- vp->exponent = 1;
- VpSetSign(vp, 1);
- vp->frac[0] = (DECDIG)uval;
- }
- else {
- DECDIG buf[BIGDECIMAL_INT64_MAX_LENGTH] = {0,};
- DECDIG r = uval % BASE;
- size_t len = 0, ntz = 0;
- if (r == 0) {
- // Count and skip trailing zeros
- for (; r == 0 && uval > 0; ++ntz) {
- uval /= BASE;
- r = uval % BASE;
- }
- }
- for (; uval > 0; ++len) {
- // Store digits
- buf[BIGDECIMAL_INT64_MAX_LENGTH - len - 1] = r;
- uval /= BASE;
- r = uval % BASE;
- }
-
- const size_t exp = len + ntz;
- vp = rbd_allocate_struct(len);
- vp->MaxPrec = len;
- vp->Prec = len;
- vp->exponent = exp;
- VpSetSign(vp, 1);
- MEMCPY(vp->frac, buf + BIGDECIMAL_INT64_MAX_LENGTH - len, DECDIG, len);
- }
-
- return BigDecimal_wrap_struct(obj, vp);
-}
-
-static VALUE
-rb_int64_convert_to_BigDecimal(int64_t ival, size_t digs, int raise_exception)
-{
- const uint64_t uval = (ival < 0) ? (((uint64_t)-(ival+1))+1) : (uint64_t)ival;
- VALUE bd = rb_uint64_convert_to_BigDecimal(uval, digs, raise_exception);
- if (ival < 0) {
- Real *vp;
- TypedData_Get_Struct(bd, Real, &BigDecimal_data_type, vp);
- VpSetSign(vp, -1);
- }
- return bd;
-}
-
-static VALUE
-rb_big_convert_to_BigDecimal(VALUE val, RB_UNUSED_VAR(size_t digs), int raise_exception)
-{
- assert(RB_TYPE_P(val, T_BIGNUM));
-
- int leading_zeros;
- size_t size = rb_absint_size(val, &leading_zeros);
- int sign = FIX2INT(rb_big_cmp(val, INT2FIX(0)));
- if (sign < 0 && leading_zeros == 0) {
- size += 1;
- }
- if (size <= sizeof(long)) {
- if (sign < 0) {
- return rb_int64_convert_to_BigDecimal(NUM2LONG(val), digs, raise_exception);
- }
- else {
- return rb_uint64_convert_to_BigDecimal(NUM2ULONG(val), digs, raise_exception);
- }
- }
-#if defined(SIZEOF_LONG_LONG) && SIZEOF_LONG < SIZEOF_LONG_LONG
- else if (size <= sizeof(LONG_LONG)) {
- if (sign < 0) {
- return rb_int64_convert_to_BigDecimal(NUM2LL(val), digs, raise_exception);
- }
- else {
- return rb_uint64_convert_to_BigDecimal(NUM2ULL(val), digs, raise_exception);
- }
- }
-#endif
- else {
- VALUE str = rb_big2str(val, 10);
- Real *vp = VpCreateRbObject(RSTRING_LEN(str) + BASE_FIG + 1,
- RSTRING_PTR(str), true);
- RB_GC_GUARD(str);
- return check_exception(vp->obj);
- }
-}
-
-static VALUE
-rb_inum_convert_to_BigDecimal(VALUE val, RB_UNUSED_VAR(size_t digs), int raise_exception)
-{
- assert(RB_INTEGER_TYPE_P(val));
- if (FIXNUM_P(val)) {
- return rb_int64_convert_to_BigDecimal(FIX2LONG(val), digs, raise_exception);
- }
- else {
- return rb_big_convert_to_BigDecimal(val, digs, raise_exception);
- }
-}
-
-static VALUE
-rb_float_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
-{
- assert(RB_FLOAT_TYPE_P(val));
-
- double d = RFLOAT_VALUE(val);
-
- if (isnan(d)) {
- VALUE obj = BigDecimal_nan();
- return check_exception(obj);
- }
- else if (isinf(d)) {
- VALUE obj;
- if (d > 0) {
- obj = BigDecimal_positive_infinity();
- }
- else {
- obj = BigDecimal_negative_infinity();
- }
- return check_exception(obj);
- }
- else if (d == 0.0) {
- if (1/d < 0.0) {
- return BigDecimal_negative_zero();
- }
- else {
- return BigDecimal_positive_zero();
- }
- }
-
- if (digs == SIZE_MAX) {
- if (!raise_exception)
- return Qnil;
- rb_raise(rb_eArgError,
- "can't omit precision for a %"PRIsVALUE".",
- CLASS_OF(val));
- }
- else if (digs > BIGDECIMAL_DOUBLE_FIGURES) {
- if (!raise_exception)
- return Qnil;
- rb_raise(rb_eArgError, "precision too large.");
- }
-
- /* Use the same logic in flo_to_s to convert a float to a decimal string */
- char buf[BIGDECIMAL_DOUBLE_FIGURES + BASE_FIG + 2 + 1]; /* sizeof(buf) == 28 in the typical case */
- int decpt, negative_p;
- char *e;
- const int mode = digs == 0 ? 0 : 2;
- char *p = BigDecimal_dtoa(d, mode, (int)digs, &decpt, &negative_p, &e);
- int len10 = (int)(e - p);
- if (len10 > BIGDECIMAL_DOUBLE_FIGURES) {
- /* TODO: Presumably, rounding should be done here. */
- len10 = BIGDECIMAL_DOUBLE_FIGURES;
- }
- memcpy(buf, p, len10);
- xfree(p);
-
- VALUE inum;
- size_t RB_UNUSED_VAR(prec) = 0;
- SIGNED_VALUE exp = 0;
- if (decpt > 0) {
- if (decpt < len10) {
- /*
- * len10 |---------------|
- * : |-------| frac_len10 = len10 - decpt
- * decpt |-------| |--| ntz10 = BASE_FIG - frac_len10 % BASE_FIG
- * : : :
- * 00 dd dddd.dddd dd 00
- * prec |-----.----.----.-----| prec = exp + roomof(frac_len, BASE_FIG)
- * exp |-----.----| exp = roomof(decpt, BASE_FIG)
- */
- const size_t frac_len10 = len10 - decpt;
- const size_t ntz10 = BASE_FIG - frac_len10 % BASE_FIG;
- memset(buf + len10, '0', ntz10);
- buf[len10 + ntz10] = '\0';
- inum = rb_cstr_to_inum(buf, 10, false);
-
- exp = roomof(decpt, BASE_FIG);
- prec = exp + roomof(frac_len10, BASE_FIG);
- }
- else {
- /*
- * decpt |-----------------------|
- * len10 |----------| :
- * : |------------| exp10
- * : : :
- * 00 dd dddd dd 00 0000 0000.0
- * : : : :
- * : |--| ntz10 = exp10 % BASE_FIG
- * prec |-----.----.-----| :
- * : |----.----| exp10 / BASE_FIG
- * exp |-----.----.-----.----.----|
- */
- const size_t exp10 = decpt - len10;
- const size_t ntz10 = exp10 % BASE_FIG;
-
- memset(buf + len10, '0', ntz10);
- buf[len10 + ntz10] = '\0';
- inum = rb_cstr_to_inum(buf, 10, false);
-
- prec = roomof(len10 + ntz10, BASE_FIG);
- exp = prec + exp10 / BASE_FIG;
- }
- }
- else if (decpt == 0) {
- /*
- * len10 |------------|
- * : :
- * 0.dddd dddd dd 00
- * : : :
- * : |--| ntz10 = prec * BASE_FIG - len10
- * prec |----.----.-----| roomof(len10, BASE_FIG)
- */
- prec = roomof(len10, BASE_FIG);
- const size_t ntz10 = prec * BASE_FIG - len10;
-
- memset(buf + len10, '0', ntz10);
- buf[len10 + ntz10] = '\0';
- inum = rb_cstr_to_inum(buf, 10, false);
- }
- else {
- /*
- * len10 |---------------|
- * : :
- * decpt |-------| |--| ntz10 = prec * BASE_FIG - nlz10 - len10
- * : : :
- * 0.0000 00 dd dddd dddd dd 00
- * : : :
- * nlz10 |--| : decpt % BASE_FIG
- * prec |-----.----.----.-----| roomof(decpt + len10, BASE_FIG) - exp
- * exp |----| decpt / BASE_FIG
- */
- decpt = -decpt;
-
- const size_t nlz10 = decpt % BASE_FIG;
- exp = decpt / BASE_FIG;
- prec = roomof(decpt + len10, BASE_FIG) - exp;
- const size_t ntz10 = prec * BASE_FIG - nlz10 - len10;
-
- if (nlz10 > 0) {
- memmove(buf + nlz10, buf, len10);
- memset(buf, '0', nlz10);
- }
- memset(buf + nlz10 + len10, '0', ntz10);
- buf[nlz10 + len10 + ntz10] = '\0';
- inum = rb_cstr_to_inum(buf, 10, false);
-
- exp = -exp;
- }
-
- VALUE bd = rb_inum_convert_to_BigDecimal(inum, SIZE_MAX, raise_exception);
- Real *vp;
- TypedData_Get_Struct(bd, Real, &BigDecimal_data_type, vp);
- assert(vp->Prec == prec);
- vp->exponent = exp;
-
- if (negative_p) VpSetSign(vp, -1);
- return bd;
-}
-
-static VALUE
-rb_rational_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
-{
- assert(RB_TYPE_P(val, T_RATIONAL));
-
- if (digs == SIZE_MAX) {
- if (!raise_exception)
- return Qnil;
- rb_raise(rb_eArgError,
- "can't omit precision for a %"PRIsVALUE".",
- CLASS_OF(val));
- }
-
- VALUE num = rb_inum_convert_to_BigDecimal(rb_rational_num(val), 0, raise_exception);
- VALUE d = BigDecimal_div2(num, rb_rational_den(val), SIZET2NUM(digs));
- return d;
-}
-
-static VALUE
-rb_cstr_convert_to_BigDecimal(const char *c_str, size_t digs, int raise_exception)
-{
- if (digs == SIZE_MAX)
- digs = 0;
-
- Real *vp = VpCreateRbObject(digs, c_str, raise_exception);
- if (!vp)
- return Qnil;
- return VpCheckGetValue(vp);
-}
-
-static inline VALUE
-rb_str_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
-{
- const char *c_str = StringValueCStr(val);
- return rb_cstr_convert_to_BigDecimal(c_str, digs, raise_exception);
-}
-
-static VALUE
-rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
-{
- switch (val) {
- case Qnil:
- case Qtrue:
- case Qfalse:
- if (raise_exception) {
- const char *cname = NIL_P(val) ? "nil" :
- val == Qtrue ? "true" :
- val == Qfalse ? "false" :
- NULL;
- rb_raise(rb_eTypeError,
- "can't convert %s into BigDecimal", cname);
- }
- return Qnil;
-
- default:
- break;
- }
-
- if (is_kind_of_BigDecimal(val)) {
- if (digs == SIZE_MAX)
- return check_exception(val);
-
- Real *vp;
- TypedData_Get_Struct(val, Real, &BigDecimal_data_type, vp);
-
- VALUE copy = TypedData_Wrap_Struct(rb_cBigDecimal, &BigDecimal_data_type, 0);
- vp = VpCopy(NULL, vp);
- /* TODO: rounding */
- BigDecimal_wrap_struct(copy, vp);
- return VpCheckGetValue(vp);
- }
- else if (RB_INTEGER_TYPE_P(val)) {
- return rb_inum_convert_to_BigDecimal(val, digs, raise_exception);
- }
- else if (RB_FLOAT_TYPE_P(val)) {
- return rb_float_convert_to_BigDecimal(val, digs, raise_exception);
- }
- else if (RB_TYPE_P(val, T_RATIONAL)) {
- return rb_rational_convert_to_BigDecimal(val, digs, raise_exception);
- }
- else if (RB_TYPE_P(val, T_COMPLEX)) {
- VALUE im = rb_complex_imag(val);
- if (!is_zero(im)) {
- /* TODO: handle raise_exception */
- rb_raise(rb_eArgError,
- "Unable to make a BigDecimal from non-zero imaginary number");
- }
- return rb_convert_to_BigDecimal(rb_complex_real(val), digs, raise_exception);
- }
- else if (RB_TYPE_P(val, T_STRING)) {
- return rb_str_convert_to_BigDecimal(val, digs, raise_exception);
- }
-
- /* TODO: chheck to_d */
- /* TODO: chheck to_int */
-
- VALUE str = rb_check_convert_type(val, T_STRING, "String", "to_str");
- if (!RB_TYPE_P(str, T_STRING)) {
- if (raise_exception) {
- rb_raise(rb_eTypeError,
- "can't convert %"PRIsVALUE" into BigDecimal", rb_obj_class(val));
- }
- return Qnil;
- }
- return rb_str_convert_to_BigDecimal(str, digs, raise_exception);
-}
-
-/* call-seq:
- * BigDecimal(value, exception: true) -> bigdecimal
- * BigDecimal(value, ndigits, exception: true) -> bigdecimal
- *
- * Returns the \BigDecimal converted from +value+
- * with a precision of +ndigits+ decimal digits.
- *
- * When +ndigits+ is less than the number of significant digits
- * in the value, the result is rounded to that number of digits,
- * according to the current rounding mode; see BigDecimal.mode.
- *
- * When +ndigits+ is 0, the number of digits to correctly represent a float number
- * is determined automatically.
- *
- * Returns +value+ converted to a \BigDecimal, depending on the type of +value+:
- *
- * - Integer, Float, Rational, Complex, or BigDecimal: converted directly:
- *
- * # Integer, Complex, or BigDecimal value does not require ndigits; ignored if given.
- * BigDecimal(2) # => 0.2e1
- * BigDecimal(Complex(2, 0)) # => 0.2e1
- * BigDecimal(BigDecimal(2)) # => 0.2e1
- * # Float or Rational value requires ndigits.
- * BigDecimal(2.0, 0) # => 0.2e1
- * BigDecimal(Rational(2, 1), 0) # => 0.2e1
- *
- * - String: converted by parsing if it contains an integer or floating-point literal;
- * leading and trailing whitespace is ignored:
- *
- * # String does not require ndigits; ignored if given.
- * BigDecimal('2') # => 0.2e1
- * BigDecimal('2.0') # => 0.2e1
- * BigDecimal('0.2e1') # => 0.2e1
- * BigDecimal(' 2.0 ') # => 0.2e1
- *
- * - Other type that responds to method <tt>:to_str</tt>:
- * first converted to a string, then converted to a \BigDecimal, as above.
- *
- * - Other type:
- *
- * - Raises an exception if keyword argument +exception+ is +true+.
- * - Returns +nil+ if keyword argument +exception+ is +false+.
- *
- * Raises an exception if +value+ evaluates to a Float
- * and +digits+ is larger than Float::DIG + 1.
- *
- */
-static VALUE
-f_BigDecimal(int argc, VALUE *argv, VALUE self)
-{
- VALUE val, digs_v, opts = Qnil;
- argc = rb_scan_args(argc, argv, "11:", &val, &digs_v, &opts);
- int exception = opts_exception_p(opts);
-
- size_t digs = SIZE_MAX; /* this means digs is omitted */
- if (argc > 1) {
- digs_v = rb_to_int(digs_v);
- if (FIXNUM_P(digs_v)) {
- long n = FIX2LONG(digs_v);
- if (n < 0)
- goto negative_digs;
- digs = (size_t)n;
- }
- else {
- if (RBIGNUM_NEGATIVE_P(digs_v)) {
- negative_digs:
- if (!exception)
- return Qnil;
- rb_raise(rb_eArgError, "negative precision");
- }
- digs = NUM2SIZET(digs_v);
- }
- }
-
- return rb_convert_to_BigDecimal(val, digs, exception);
-}
-
-static VALUE
-BigDecimal_s_interpret_loosely(VALUE klass, VALUE str)
-{
- char const *c_str = StringValueCStr(str);
- Real *vp = VpNewRbClass(0, c_str, klass, false, true);
- if (!vp)
- return Qnil;
- else
- return VpCheckGetValue(vp);
-}
-
- /* call-seq:
- * BigDecimal.limit(digits)
- *
- * Limit the number of significant digits in newly created BigDecimal
- * numbers to the specified value. Rounding is performed as necessary,
- * as specified by BigDecimal.mode.
- *
- * A limit of 0, the default, means no upper limit.
- *
- * The limit specified by this method takes less priority over any limit
- * specified to instance methods such as ceil, floor, truncate, or round.
- */
-static VALUE
-BigDecimal_limit(int argc, VALUE *argv, VALUE self)
-{
- VALUE nFig;
- VALUE nCur = SIZET2NUM(VpGetPrecLimit());
-
- if (rb_scan_args(argc, argv, "01", &nFig) == 1) {
- int nf;
- if (NIL_P(nFig)) return nCur;
- nf = NUM2INT(nFig);
- if (nf < 0) {
- rb_raise(rb_eArgError, "argument must be positive");
- }
- VpSetPrecLimit(nf);
- }
- return nCur;
-}
-
-/* Returns the sign of the value.
- *
- * Returns a positive value if > 0, a negative value if < 0.
- * It behaves the same with zeros -
- * it returns a positive value for a positive zero (BigDecimal('0')) and
- * a negative value for a negative zero (BigDecimal('-0')).
- *
- * The specific value returned indicates the type and sign of the BigDecimal,
- * as follows:
- *
- * BigDecimal::SIGN_NaN:: value is Not a Number
- * BigDecimal::SIGN_POSITIVE_ZERO:: value is +0
- * BigDecimal::SIGN_NEGATIVE_ZERO:: value is -0
- * BigDecimal::SIGN_POSITIVE_INFINITE:: value is +Infinity
- * BigDecimal::SIGN_NEGATIVE_INFINITE:: value is -Infinity
- * BigDecimal::SIGN_POSITIVE_FINITE:: value is positive
- * BigDecimal::SIGN_NEGATIVE_FINITE:: value is negative
- */
-static VALUE
-BigDecimal_sign(VALUE self)
-{ /* sign */
- int s = GetVpValue(self, 1)->sign;
- return INT2FIX(s);
-}
-
-/*
- * call-seq: BigDecimal.save_exception_mode { ... }
- *
- * Execute the provided block, but preserve the exception mode
- *
- * BigDecimal.save_exception_mode do
- * BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- * BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- *
- * BigDecimal(BigDecimal('Infinity'))
- * BigDecimal(BigDecimal('-Infinity'))
- * BigDecimal(BigDecimal('NaN'))
- * end
- *
- * For use with the BigDecimal::EXCEPTION_*
- *
- * See BigDecimal.mode
- */
-static VALUE
-BigDecimal_save_exception_mode(VALUE self)
-{
- unsigned short const exception_mode = VpGetException();
- int state;
- VALUE ret = rb_protect(rb_yield, Qnil, &state);
- VpSetException(exception_mode);
- if (state) rb_jump_tag(state);
- return ret;
-}
-
-/*
- * call-seq: BigDecimal.save_rounding_mode { ... }
- *
- * Execute the provided block, but preserve the rounding mode
- *
- * BigDecimal.save_rounding_mode do
- * BigDecimal.mode(BigDecimal::ROUND_MODE, :up)
- * puts BigDecimal.mode(BigDecimal::ROUND_MODE)
- * end
- *
- * For use with the BigDecimal::ROUND_*
- *
- * See BigDecimal.mode
- */
-static VALUE
-BigDecimal_save_rounding_mode(VALUE self)
-{
- unsigned short const round_mode = VpGetRoundMode();
- int state;
- VALUE ret = rb_protect(rb_yield, Qnil, &state);
- VpSetRoundMode(round_mode);
- if (state) rb_jump_tag(state);
- return ret;
-}
-
-/*
- * call-seq: BigDecimal.save_limit { ... }
- *
- * Execute the provided block, but preserve the precision limit
- *
- * BigDecimal.limit(100)
- * puts BigDecimal.limit
- * BigDecimal.save_limit do
- * BigDecimal.limit(200)
- * puts BigDecimal.limit
- * end
- * puts BigDecimal.limit
- *
- */
-static VALUE
-BigDecimal_save_limit(VALUE self)
-{
- size_t const limit = VpGetPrecLimit();
- int state;
- VALUE ret = rb_protect(rb_yield, Qnil, &state);
- VpSetPrecLimit(limit);
- if (state) rb_jump_tag(state);
- return ret;
-}
-
-/* call-seq:
- * BigMath.exp(decimal, numeric) -> BigDecimal
- *
- * Computes the value of e (the base of natural logarithms) raised to the
- * power of +decimal+, to the specified number of digits of precision.
- *
- * If +decimal+ is infinity, returns Infinity.
- *
- * If +decimal+ is NaN, returns NaN.
- */
-static VALUE
-BigMath_s_exp(VALUE klass, VALUE x, VALUE vprec)
-{
- ssize_t prec, n, i;
- Real* vx = NULL;
- VALUE one, d, y;
- int negative = 0;
- int infinite = 0;
- int nan = 0;
- double flo;
-
- prec = NUM2SSIZET(vprec);
- if (prec <= 0) {
- rb_raise(rb_eArgError, "Zero or negative precision for exp");
- }
-
- /* TODO: the following switch statement is almost same as one in the
- * BigDecimalCmp function. */
- switch (TYPE(x)) {
- case T_DATA:
- if (!is_kind_of_BigDecimal(x)) break;
- vx = DATA_PTR(x);
- negative = BIGDECIMAL_NEGATIVE_P(vx);
- infinite = VpIsPosInf(vx) || VpIsNegInf(vx);
- nan = VpIsNaN(vx);
- break;
-
- case T_FIXNUM:
- /* fall through */
- case T_BIGNUM:
- vx = GetVpValue(x, 0);
- break;
-
- case T_FLOAT:
- flo = RFLOAT_VALUE(x);
- negative = flo < 0;
- infinite = isinf(flo);
- nan = isnan(flo);
- if (!infinite && !nan) {
- vx = GetVpValueWithPrec(x, 0, 0);
- }
- break;
-
- case T_RATIONAL:
- vx = GetVpValueWithPrec(x, prec, 0);
- break;
-
- default:
- break;
- }
- if (infinite) {
- if (negative) {
- return VpCheckGetValue(GetVpValueWithPrec(INT2FIX(0), prec, 1));
- }
- else {
- Real* vy = NewZeroWrapNolimit(1, prec);
- VpSetInf(vy, VP_SIGN_POSITIVE_INFINITE);
- RB_GC_GUARD(vy->obj);
- return VpCheckGetValue(vy);
- }
- }
- else if (nan) {
- Real* vy = NewZeroWrapNolimit(1, prec);
- VpSetNaN(vy);
- RB_GC_GUARD(vy->obj);
- return VpCheckGetValue(vy);
- }
- else if (vx == NULL) {
- cannot_be_coerced_into_BigDecimal(rb_eArgError, x);
- }
- x = vx->obj;
-
- n = prec + BIGDECIMAL_DOUBLE_FIGURES;
- negative = BIGDECIMAL_NEGATIVE_P(vx);
- if (negative) {
- VALUE x_zero = INT2NUM(1);
- VALUE x_copy = f_BigDecimal(1, &x_zero, klass);
- x = BigDecimal_initialize_copy(x_copy, x);
- vx = DATA_PTR(x);
- VpSetSign(vx, 1);
- }
-
- one = VpCheckGetValue(NewOneWrapLimited(1, 1));
- y = one;
- d = y;
- i = 1;
-
- while (!VpIsZero((Real*)DATA_PTR(d))) {
- SIGNED_VALUE const ey = VpExponent10(DATA_PTR(y));
- SIGNED_VALUE const ed = VpExponent10(DATA_PTR(d));
- ssize_t m = n - vabs(ey - ed);
-
- rb_thread_check_ints();
-
- if (m <= 0) {
- break;
- }
- else if ((size_t)m < BIGDECIMAL_DOUBLE_FIGURES) {
- m = BIGDECIMAL_DOUBLE_FIGURES;
- }
-
- d = BigDecimal_mult(d, x); /* d <- d * x */
- d = BigDecimal_div2(d, SSIZET2NUM(i), SSIZET2NUM(m)); /* d <- d / i */
- y = BigDecimal_add(y, d); /* y <- y + d */
- ++i; /* i <- i + 1 */
- }
-
- if (negative) {
- return BigDecimal_div2(one, y, vprec);
- }
- else {
- vprec = SSIZET2NUM(prec - VpExponent10(DATA_PTR(y)));
- return BigDecimal_round(1, &vprec, y);
- }
-
- RB_GC_GUARD(one);
- RB_GC_GUARD(x);
- RB_GC_GUARD(y);
- RB_GC_GUARD(d);
-}
-
-/* call-seq:
- * BigMath.log(decimal, numeric) -> BigDecimal
- *
- * Computes the natural logarithm of +decimal+ to the specified number of
- * digits of precision, +numeric+.
- *
- * If +decimal+ is zero or negative, raises Math::DomainError.
- *
- * If +decimal+ is positive infinity, returns Infinity.
- *
- * If +decimal+ is NaN, returns NaN.
- */
-static VALUE
-BigMath_s_log(VALUE klass, VALUE x, VALUE vprec)
-{
- ssize_t prec, n, i;
- SIGNED_VALUE expo;
- Real* vx = NULL;
- VALUE vn, one, two, w, x2, y, d;
- int zero = 0;
- int negative = 0;
- int infinite = 0;
- int nan = 0;
- double flo;
- long fix;
-
- if (!is_integer(vprec)) {
- rb_raise(rb_eArgError, "precision must be an Integer");
- }
-
- prec = NUM2SSIZET(vprec);
- if (prec <= 0) {
- rb_raise(rb_eArgError, "Zero or negative precision for exp");
- }
-
- /* TODO: the following switch statement is almost same as one in the
- * BigDecimalCmp function. */
- switch (TYPE(x)) {
- case T_DATA:
- if (!is_kind_of_BigDecimal(x)) break;
- vx = DATA_PTR(x);
- zero = VpIsZero(vx);
- negative = BIGDECIMAL_NEGATIVE_P(vx);
- infinite = VpIsPosInf(vx) || VpIsNegInf(vx);
- nan = VpIsNaN(vx);
- break;
-
- case T_FIXNUM:
- fix = FIX2LONG(x);
- zero = fix == 0;
- negative = fix < 0;
- goto get_vp_value;
-
- case T_BIGNUM:
- i = FIX2INT(rb_big_cmp(x, INT2FIX(0)));
- zero = i == 0;
- negative = i < 0;
-get_vp_value:
- if (zero || negative) break;
- vx = GetVpValue(x, 0);
- break;
-
- case T_FLOAT:
- flo = RFLOAT_VALUE(x);
- zero = flo == 0;
- negative = flo < 0;
- infinite = isinf(flo);
- nan = isnan(flo);
- if (!zero && !negative && !infinite && !nan) {
- vx = GetVpValueWithPrec(x, 0, 1);
- }
- break;
-
- case T_RATIONAL:
- zero = RRATIONAL_ZERO_P(x);
- negative = RRATIONAL_NEGATIVE_P(x);
- if (zero || negative) break;
- vx = GetVpValueWithPrec(x, prec, 1);
- break;
-
- case T_COMPLEX:
- rb_raise(rb_eMathDomainError,
- "Complex argument for BigMath.log");
-
- default:
- break;
- }
- if (infinite && !negative) {
- Real *vy = NewZeroWrapNolimit(1, prec);
- RB_GC_GUARD(vy->obj);
- VpSetInf(vy, VP_SIGN_POSITIVE_INFINITE);
- return VpCheckGetValue(vy);
- }
- else if (nan) {
- Real* vy = NewZeroWrapNolimit(1, prec);
- RB_GC_GUARD(vy->obj);
- VpSetNaN(vy);
- return VpCheckGetValue(vy);
- }
- else if (zero || negative) {
- rb_raise(rb_eMathDomainError,
- "Zero or negative argument for log");
- }
- else if (vx == NULL) {
- cannot_be_coerced_into_BigDecimal(rb_eArgError, x);
- }
- x = VpCheckGetValue(vx);
-
- one = VpCheckGetValue(NewOneWrapLimited(1, 1));
- two = VpCheckGetValue(VpCreateRbObject(1, "2", true));
-
- n = prec + BIGDECIMAL_DOUBLE_FIGURES;
- vn = SSIZET2NUM(n);
- expo = VpExponent10(vx);
- if (expo < 0 || expo >= 3) {
- char buf[DECIMAL_SIZE_OF_BITS(SIZEOF_VALUE * CHAR_BIT) + 4];
- snprintf(buf, sizeof(buf), "1E%"PRIdVALUE, -expo);
- x = BigDecimal_mult2(x, VpCheckGetValue(VpCreateRbObject(1, buf, true)), vn);
- }
- else {
- expo = 0;
- }
- w = BigDecimal_sub(x, one);
- x = BigDecimal_div2(w, BigDecimal_add(x, one), vn);
- x2 = BigDecimal_mult2(x, x, vn);
- y = x;
- d = y;
- i = 1;
- while (!VpIsZero((Real*)DATA_PTR(d))) {
- SIGNED_VALUE const ey = VpExponent10(DATA_PTR(y));
- SIGNED_VALUE const ed = VpExponent10(DATA_PTR(d));
- ssize_t m = n - vabs(ey - ed);
- if (m <= 0) {
- break;
- }
- else if ((size_t)m < BIGDECIMAL_DOUBLE_FIGURES) {
- m = BIGDECIMAL_DOUBLE_FIGURES;
- }
-
- x = BigDecimal_mult2(x2, x, vn);
- i += 2;
- d = BigDecimal_div2(x, SSIZET2NUM(i), SSIZET2NUM(m));
- y = BigDecimal_add(y, d);
- }
-
- y = BigDecimal_mult(y, two);
- if (expo != 0) {
- VALUE log10, vexpo, dy;
- log10 = BigMath_s_log(klass, INT2FIX(10), vprec);
- vexpo = VpCheckGetValue(GetVpValue(SSIZET2NUM(expo), 1));
- dy = BigDecimal_mult(log10, vexpo);
- y = BigDecimal_add(y, dy);
- }
-
- RB_GC_GUARD(one);
- RB_GC_GUARD(two);
- RB_GC_GUARD(vn);
- RB_GC_GUARD(x2);
- RB_GC_GUARD(y);
- RB_GC_GUARD(d);
-
- return y;
-}
-
-static VALUE BIGDECIMAL_NAN = Qnil;
-
-static VALUE
-BigDecimal_nan(void)
-{
- return BIGDECIMAL_NAN;
-}
-
-static VALUE BIGDECIMAL_POSITIVE_INFINITY = Qnil;
-
-static VALUE
-BigDecimal_positive_infinity(void)
-{
- return BIGDECIMAL_POSITIVE_INFINITY;
-}
-
-static VALUE BIGDECIMAL_NEGATIVE_INFINITY = Qnil;
-
-static VALUE
-BigDecimal_negative_infinity(void)
-{
- return BIGDECIMAL_NEGATIVE_INFINITY;
-}
-
-static VALUE BIGDECIMAL_POSITIVE_ZERO = Qnil;
-
-static VALUE
-BigDecimal_positive_zero(void)
-{
- return BIGDECIMAL_POSITIVE_ZERO;
-}
-
-static VALUE BIGDECIMAL_NEGATIVE_ZERO = Qnil;
-
-static VALUE
-BigDecimal_negative_zero(void)
-{
- return BIGDECIMAL_NEGATIVE_ZERO;
-}
-
-/* Document-class: BigDecimal
- * BigDecimal provides arbitrary-precision floating point decimal arithmetic.
- *
- * == Introduction
- *
- * Ruby provides built-in support for arbitrary precision integer arithmetic.
- *
- * For example:
- *
- * 42**13 #=> 1265437718438866624512
- *
- * BigDecimal provides similar support for very large or very accurate floating
- * point numbers.
- *
- * Decimal arithmetic is also useful for general calculation, because it
- * provides the correct answers people expect--whereas normal binary floating
- * point arithmetic often introduces subtle errors because of the conversion
- * between base 10 and base 2.
- *
- * For example, try:
- *
- * sum = 0
- * 10_000.times do
- * sum = sum + 0.0001
- * end
- * print sum #=> 0.9999999999999062
- *
- * and contrast with the output from:
- *
- * require 'bigdecimal'
- *
- * sum = BigDecimal("0")
- * 10_000.times do
- * sum = sum + BigDecimal("0.0001")
- * end
- * print sum #=> 0.1E1
- *
- * Similarly:
- *
- * (BigDecimal("1.2") - BigDecimal("1.0")) == BigDecimal("0.2") #=> true
- *
- * (1.2 - 1.0) == 0.2 #=> false
- *
- * == A Note About Precision
- *
- * For a calculation using a \BigDecimal and another +value+,
- * the precision of the result depends on the type of +value+:
- *
- * - If +value+ is a \Float,
- * the precision is Float::DIG + 1.
- * - If +value+ is a \Rational, the precision is larger than Float::DIG + 1.
- * - If +value+ is a \BigDecimal, the precision is +value+'s precision in the
- * internal representation, which is platform-dependent.
- * - If +value+ is other object, the precision is determined by the result of +BigDecimal(value)+.
- *
- * == Special features of accurate decimal arithmetic
- *
- * Because BigDecimal is more accurate than normal binary floating point
- * arithmetic, it requires some special values.
- *
- * === Infinity
- *
- * BigDecimal sometimes needs to return infinity, for example if you divide
- * a value by zero.
- *
- * BigDecimal("1.0") / BigDecimal("0.0") #=> Infinity
- * BigDecimal("-1.0") / BigDecimal("0.0") #=> -Infinity
- *
- * You can represent infinite numbers to BigDecimal using the strings
- * <code>'Infinity'</code>, <code>'+Infinity'</code> and
- * <code>'-Infinity'</code> (case-sensitive)
- *
- * === Not a Number
- *
- * When a computation results in an undefined value, the special value +NaN+
- * (for 'not a number') is returned.
- *
- * Example:
- *
- * BigDecimal("0.0") / BigDecimal("0.0") #=> NaN
- *
- * You can also create undefined values.
- *
- * NaN is never considered to be the same as any other value, even NaN itself:
- *
- * n = BigDecimal('NaN')
- * n == 0.0 #=> false
- * n == n #=> false
- *
- * === Positive and negative zero
- *
- * If a computation results in a value which is too small to be represented as
- * a BigDecimal within the currently specified limits of precision, zero must
- * be returned.
- *
- * If the value which is too small to be represented is negative, a BigDecimal
- * value of negative zero is returned.
- *
- * BigDecimal("1.0") / BigDecimal("-Infinity") #=> -0.0
- *
- * If the value is positive, a value of positive zero is returned.
- *
- * BigDecimal("1.0") / BigDecimal("Infinity") #=> 0.0
- *
- * (See BigDecimal.mode for how to specify limits of precision.)
- *
- * Note that +-0.0+ and +0.0+ are considered to be the same for the purposes of
- * comparison.
- *
- * Note also that in mathematics, there is no particular concept of negative
- * or positive zero; true mathematical zero has no sign.
- *
- * == bigdecimal/util
- *
- * When you require +bigdecimal/util+, the #to_d method will be
- * available on BigDecimal and the native Integer, Float, Rational,
- * and String classes:
- *
- * require 'bigdecimal/util'
- *
- * 42.to_d # => 0.42e2
- * 0.5.to_d # => 0.5e0
- * (2/3r).to_d(3) # => 0.667e0
- * "0.5".to_d # => 0.5e0
- *
- * == License
- *
- * Copyright (C) 2002 by Shigeo Kobayashi <shigeo@tinyforest.gr.jp>.
- *
- * BigDecimal is released under the Ruby and 2-clause BSD licenses.
- * See LICENSE.txt for details.
- *
- * Maintained by mrkn <mrkn@mrkn.jp> and ruby-core members.
- *
- * Documented by zzak <zachary@zacharyscott.net>, mathew <meta@pobox.com>, and
- * many other contributors.
- */
-void
-Init_bigdecimal(void)
-{
-#ifdef HAVE_RB_EXT_RACTOR_SAFE
- rb_ext_ractor_safe(true);
-#endif
- VALUE arg;
-
- id_BigDecimal_exception_mode = rb_intern_const("BigDecimal.exception_mode");
- id_BigDecimal_rounding_mode = rb_intern_const("BigDecimal.rounding_mode");
- id_BigDecimal_precision_limit = rb_intern_const("BigDecimal.precision_limit");
-
- /* Initialize VP routines */
- VpInit(0UL);
-
- /* Class and method registration */
- rb_cBigDecimal = rb_define_class("BigDecimal", rb_cNumeric);
-
- /* Global function */
- rb_define_global_function("BigDecimal", f_BigDecimal, -1);
-
- /* Class methods */
- rb_undef_alloc_func(rb_cBigDecimal);
- rb_undef_method(CLASS_OF(rb_cBigDecimal), "new");
- rb_define_singleton_method(rb_cBigDecimal, "interpret_loosely", BigDecimal_s_interpret_loosely, 1);
- rb_define_singleton_method(rb_cBigDecimal, "mode", BigDecimal_mode, -1);
- rb_define_singleton_method(rb_cBigDecimal, "limit", BigDecimal_limit, -1);
- rb_define_singleton_method(rb_cBigDecimal, "double_fig", BigDecimal_double_fig, 0);
- rb_define_singleton_method(rb_cBigDecimal, "_load", BigDecimal_load, 1);
-
- rb_define_singleton_method(rb_cBigDecimal, "save_exception_mode", BigDecimal_save_exception_mode, 0);
- rb_define_singleton_method(rb_cBigDecimal, "save_rounding_mode", BigDecimal_save_rounding_mode, 0);
- rb_define_singleton_method(rb_cBigDecimal, "save_limit", BigDecimal_save_limit, 0);
-
- /* Constants definition */
-
- /*
- * The version of bigdecimal library
- */
- rb_define_const(rb_cBigDecimal, "VERSION", rb_str_new2(BIGDECIMAL_VERSION));
-
- /*
- * Base value used in internal calculations. On a 32 bit system, BASE
- * is 10000, indicating that calculation is done in groups of 4 digits.
- * (If it were larger, BASE**2 wouldn't fit in 32 bits, so you couldn't
- * guarantee that two groups could always be multiplied together without
- * overflow.)
- */
- rb_define_const(rb_cBigDecimal, "BASE", INT2FIX((SIGNED_VALUE)VpBaseVal()));
-
- /* Exceptions */
-
- /*
- * 0xff: Determines whether overflow, underflow or zero divide result in
- * an exception being thrown. See BigDecimal.mode.
- */
- rb_define_const(rb_cBigDecimal, "EXCEPTION_ALL", INT2FIX(VP_EXCEPTION_ALL));
-
- /*
- * 0x02: Determines what happens when the result of a computation is not a
- * number (NaN). See BigDecimal.mode.
- */
- rb_define_const(rb_cBigDecimal, "EXCEPTION_NaN", INT2FIX(VP_EXCEPTION_NaN));
-
- /*
- * 0x01: Determines what happens when the result of a computation is
- * infinity. See BigDecimal.mode.
- */
- rb_define_const(rb_cBigDecimal, "EXCEPTION_INFINITY", INT2FIX(VP_EXCEPTION_INFINITY));
-
- /*
- * 0x04: Determines what happens when the result of a computation is an
- * underflow (a result too small to be represented). See BigDecimal.mode.
- */
- rb_define_const(rb_cBigDecimal, "EXCEPTION_UNDERFLOW", INT2FIX(VP_EXCEPTION_UNDERFLOW));
-
- /*
- * 0x01: Determines what happens when the result of a computation is an
- * overflow (a result too large to be represented). See BigDecimal.mode.
- */
- rb_define_const(rb_cBigDecimal, "EXCEPTION_OVERFLOW", INT2FIX(VP_EXCEPTION_OVERFLOW));
-
- /*
- * 0x10: Determines what happens when a division by zero is performed.
- * See BigDecimal.mode.
- */
- rb_define_const(rb_cBigDecimal, "EXCEPTION_ZERODIVIDE", INT2FIX(VP_EXCEPTION_ZERODIVIDE));
-
- /*
- * 0x100: Determines what happens when a result must be rounded in order to
- * fit in the appropriate number of significant digits. See
- * BigDecimal.mode.
- */
- rb_define_const(rb_cBigDecimal, "ROUND_MODE", INT2FIX(VP_ROUND_MODE));
-
- /* 1: Indicates that values should be rounded away from zero. See
- * BigDecimal.mode.
- */
- rb_define_const(rb_cBigDecimal, "ROUND_UP", INT2FIX(VP_ROUND_UP));
-
- /* 2: Indicates that values should be rounded towards zero. See
- * BigDecimal.mode.
- */
- rb_define_const(rb_cBigDecimal, "ROUND_DOWN", INT2FIX(VP_ROUND_DOWN));
-
- /* 3: Indicates that digits >= 5 should be rounded up, others rounded down.
- * See BigDecimal.mode. */
- rb_define_const(rb_cBigDecimal, "ROUND_HALF_UP", INT2FIX(VP_ROUND_HALF_UP));
-
- /* 4: Indicates that digits >= 6 should be rounded up, others rounded down.
- * See BigDecimal.mode.
- */
- rb_define_const(rb_cBigDecimal, "ROUND_HALF_DOWN", INT2FIX(VP_ROUND_HALF_DOWN));
- /* 5: Round towards +Infinity. See BigDecimal.mode. */
- rb_define_const(rb_cBigDecimal, "ROUND_CEILING", INT2FIX(VP_ROUND_CEIL));
-
- /* 6: Round towards -Infinity. See BigDecimal.mode. */
- rb_define_const(rb_cBigDecimal, "ROUND_FLOOR", INT2FIX(VP_ROUND_FLOOR));
-
- /* 7: Round towards the even neighbor. See BigDecimal.mode. */
- rb_define_const(rb_cBigDecimal, "ROUND_HALF_EVEN", INT2FIX(VP_ROUND_HALF_EVEN));
-
- /* 0: Indicates that a value is not a number. See BigDecimal.sign. */
- rb_define_const(rb_cBigDecimal, "SIGN_NaN", INT2FIX(VP_SIGN_NaN));
-
- /* 1: Indicates that a value is +0. See BigDecimal.sign. */
- rb_define_const(rb_cBigDecimal, "SIGN_POSITIVE_ZERO", INT2FIX(VP_SIGN_POSITIVE_ZERO));
-
- /* -1: Indicates that a value is -0. See BigDecimal.sign. */
- rb_define_const(rb_cBigDecimal, "SIGN_NEGATIVE_ZERO", INT2FIX(VP_SIGN_NEGATIVE_ZERO));
-
- /* 2: Indicates that a value is positive and finite. See BigDecimal.sign. */
- rb_define_const(rb_cBigDecimal, "SIGN_POSITIVE_FINITE", INT2FIX(VP_SIGN_POSITIVE_FINITE));
-
- /* -2: Indicates that a value is negative and finite. See BigDecimal.sign. */
- rb_define_const(rb_cBigDecimal, "SIGN_NEGATIVE_FINITE", INT2FIX(VP_SIGN_NEGATIVE_FINITE));
-
- /* 3: Indicates that a value is positive and infinite. See BigDecimal.sign. */
- rb_define_const(rb_cBigDecimal, "SIGN_POSITIVE_INFINITE", INT2FIX(VP_SIGN_POSITIVE_INFINITE));
-
- /* -3: Indicates that a value is negative and infinite. See BigDecimal.sign. */
- rb_define_const(rb_cBigDecimal, "SIGN_NEGATIVE_INFINITE", INT2FIX(VP_SIGN_NEGATIVE_INFINITE));
-
- /* Positive zero value. */
- arg = rb_str_new2("+0");
- BIGDECIMAL_POSITIVE_ZERO = f_BigDecimal(1, &arg, rb_cBigDecimal);
- rb_gc_register_mark_object(BIGDECIMAL_POSITIVE_ZERO);
-
- /* Negative zero value. */
- arg = rb_str_new2("-0");
- BIGDECIMAL_NEGATIVE_ZERO = f_BigDecimal(1, &arg, rb_cBigDecimal);
- rb_gc_register_mark_object(BIGDECIMAL_NEGATIVE_ZERO);
-
- /* Positive infinity value. */
- arg = rb_str_new2("+Infinity");
- BIGDECIMAL_POSITIVE_INFINITY = f_BigDecimal(1, &arg, rb_cBigDecimal);
- rb_gc_register_mark_object(BIGDECIMAL_POSITIVE_INFINITY);
-
- /* Negative infinity value. */
- arg = rb_str_new2("-Infinity");
- BIGDECIMAL_NEGATIVE_INFINITY = f_BigDecimal(1, &arg, rb_cBigDecimal);
- rb_gc_register_mark_object(BIGDECIMAL_NEGATIVE_INFINITY);
-
- /* 'Not a Number' value. */
- arg = rb_str_new2("NaN");
- BIGDECIMAL_NAN = f_BigDecimal(1, &arg, rb_cBigDecimal);
- rb_gc_register_mark_object(BIGDECIMAL_NAN);
-
- /* Special value constants */
- rb_define_const(rb_cBigDecimal, "INFINITY", BIGDECIMAL_POSITIVE_INFINITY);
- rb_define_const(rb_cBigDecimal, "NAN", BIGDECIMAL_NAN);
-
- /* instance methods */
- rb_define_method(rb_cBigDecimal, "precs", BigDecimal_prec, 0);
- rb_define_method(rb_cBigDecimal, "precision", BigDecimal_precision, 0);
- rb_define_method(rb_cBigDecimal, "scale", BigDecimal_scale, 0);
- rb_define_method(rb_cBigDecimal, "precision_scale", BigDecimal_precision_scale, 0);
- rb_define_method(rb_cBigDecimal, "n_significant_digits", BigDecimal_n_significant_digits, 0);
-
- rb_define_method(rb_cBigDecimal, "add", BigDecimal_add2, 2);
- rb_define_method(rb_cBigDecimal, "sub", BigDecimal_sub2, 2);
- rb_define_method(rb_cBigDecimal, "mult", BigDecimal_mult2, 2);
- rb_define_method(rb_cBigDecimal, "div", BigDecimal_div3, -1);
- rb_define_method(rb_cBigDecimal, "hash", BigDecimal_hash, 0);
- rb_define_method(rb_cBigDecimal, "to_s", BigDecimal_to_s, -1);
- rb_define_method(rb_cBigDecimal, "to_i", BigDecimal_to_i, 0);
- rb_define_method(rb_cBigDecimal, "to_int", BigDecimal_to_i, 0);
- rb_define_method(rb_cBigDecimal, "to_r", BigDecimal_to_r, 0);
- rb_define_method(rb_cBigDecimal, "split", BigDecimal_split, 0);
- rb_define_method(rb_cBigDecimal, "+", BigDecimal_add, 1);
- rb_define_method(rb_cBigDecimal, "-", BigDecimal_sub, 1);
- rb_define_method(rb_cBigDecimal, "+@", BigDecimal_uplus, 0);
- rb_define_method(rb_cBigDecimal, "-@", BigDecimal_neg, 0);
- rb_define_method(rb_cBigDecimal, "*", BigDecimal_mult, 1);
- rb_define_method(rb_cBigDecimal, "/", BigDecimal_div, 1);
- rb_define_method(rb_cBigDecimal, "quo", BigDecimal_quo, -1);
- rb_define_method(rb_cBigDecimal, "%", BigDecimal_mod, 1);
- rb_define_method(rb_cBigDecimal, "modulo", BigDecimal_mod, 1);
- rb_define_method(rb_cBigDecimal, "remainder", BigDecimal_remainder, 1);
- rb_define_method(rb_cBigDecimal, "divmod", BigDecimal_divmod, 1);
- rb_define_method(rb_cBigDecimal, "clone", BigDecimal_clone, 0);
- rb_define_method(rb_cBigDecimal, "dup", BigDecimal_clone, 0);
- rb_define_method(rb_cBigDecimal, "to_f", BigDecimal_to_f, 0);
- rb_define_method(rb_cBigDecimal, "abs", BigDecimal_abs, 0);
- rb_define_method(rb_cBigDecimal, "sqrt", BigDecimal_sqrt, 1);
- rb_define_method(rb_cBigDecimal, "fix", BigDecimal_fix, 0);
- rb_define_method(rb_cBigDecimal, "round", BigDecimal_round, -1);
- rb_define_method(rb_cBigDecimal, "frac", BigDecimal_frac, 0);
- rb_define_method(rb_cBigDecimal, "floor", BigDecimal_floor, -1);
- rb_define_method(rb_cBigDecimal, "ceil", BigDecimal_ceil, -1);
- rb_define_method(rb_cBigDecimal, "power", BigDecimal_power, -1);
- rb_define_method(rb_cBigDecimal, "**", BigDecimal_power_op, 1);
- rb_define_method(rb_cBigDecimal, "<=>", BigDecimal_comp, 1);
- rb_define_method(rb_cBigDecimal, "==", BigDecimal_eq, 1);
- rb_define_method(rb_cBigDecimal, "===", BigDecimal_eq, 1);
- rb_define_method(rb_cBigDecimal, "eql?", BigDecimal_eq, 1);
- rb_define_method(rb_cBigDecimal, "<", BigDecimal_lt, 1);
- rb_define_method(rb_cBigDecimal, "<=", BigDecimal_le, 1);
- rb_define_method(rb_cBigDecimal, ">", BigDecimal_gt, 1);
- rb_define_method(rb_cBigDecimal, ">=", BigDecimal_ge, 1);
- rb_define_method(rb_cBigDecimal, "zero?", BigDecimal_zero, 0);
- rb_define_method(rb_cBigDecimal, "nonzero?", BigDecimal_nonzero, 0);
- rb_define_method(rb_cBigDecimal, "coerce", BigDecimal_coerce, 1);
- rb_define_method(rb_cBigDecimal, "inspect", BigDecimal_inspect, 0);
- rb_define_method(rb_cBigDecimal, "exponent", BigDecimal_exponent, 0);
- rb_define_method(rb_cBigDecimal, "sign", BigDecimal_sign, 0);
- rb_define_method(rb_cBigDecimal, "nan?", BigDecimal_IsNaN, 0);
- rb_define_method(rb_cBigDecimal, "infinite?", BigDecimal_IsInfinite, 0);
- rb_define_method(rb_cBigDecimal, "finite?", BigDecimal_IsFinite, 0);
- rb_define_method(rb_cBigDecimal, "truncate", BigDecimal_truncate, -1);
- rb_define_method(rb_cBigDecimal, "_dump", BigDecimal_dump, -1);
-
- rb_mBigMath = rb_define_module("BigMath");
- rb_define_singleton_method(rb_mBigMath, "exp", BigMath_s_exp, 2);
- rb_define_singleton_method(rb_mBigMath, "log", BigMath_s_log, 2);
-
-#define ROUNDING_MODE(i, name, value) \
- id_##name = rb_intern_const(#name); \
- rbd_rounding_modes[i].id = id_##name; \
- rbd_rounding_modes[i].mode = value;
-
- ROUNDING_MODE(0, up, RBD_ROUND_UP);
- ROUNDING_MODE(1, down, RBD_ROUND_DOWN);
- ROUNDING_MODE(2, half_up, RBD_ROUND_HALF_UP);
- ROUNDING_MODE(3, half_down, RBD_ROUND_HALF_DOWN);
- ROUNDING_MODE(4, ceil, RBD_ROUND_CEIL);
- ROUNDING_MODE(5, floor, RBD_ROUND_FLOOR);
- ROUNDING_MODE(6, half_even, RBD_ROUND_HALF_EVEN);
-
- ROUNDING_MODE(7, default, RBD_ROUND_DEFAULT);
- ROUNDING_MODE(8, truncate, RBD_ROUND_TRUNCATE);
- ROUNDING_MODE(9, banker, RBD_ROUND_BANKER);
- ROUNDING_MODE(10, ceiling, RBD_ROUND_CEILING);
-
-#undef ROUNDING_MODE
-
- id_to_r = rb_intern_const("to_r");
- id_eq = rb_intern_const("==");
- id_half = rb_intern_const("half");
-
- (void)VPrint; /* suppress unused warning */
-}
-
-/*
- *
- * ============================================================================
- *
- * vp_ routines begin from here.
- *
- * ============================================================================
- *
- */
-#ifdef BIGDECIMAL_DEBUG
-static int gfDebug = 1; /* Debug switch */
-#if 0
-static int gfCheckVal = 1; /* Value checking flag in VpNmlz() */
-#endif
-#endif /* BIGDECIMAL_DEBUG */
-
-static Real *VpConstOne; /* constant 1.0 */
-static Real *VpConstPt5; /* constant 0.5 */
-#define maxnr 100UL /* Maximum iterations for calculating sqrt. */
- /* used in VpSqrt() */
-
-/* ETC */
-#define MemCmp(x,y,z) memcmp(x,y,z)
-#define StrCmp(x,y) strcmp(x,y)
-
-enum op_sw {
- OP_SW_ADD = 1, /* + */
- OP_SW_SUB, /* - */
- OP_SW_MULT, /* * */
- OP_SW_DIV /* / */
-};
-
-static int VpIsDefOP(Real *c, Real *a, Real *b, enum op_sw sw);
-static int AddExponent(Real *a, SIGNED_VALUE n);
-static DECDIG VpAddAbs(Real *a,Real *b,Real *c);
-static DECDIG VpSubAbs(Real *a,Real *b,Real *c);
-static size_t VpSetPTR(Real *a, Real *b, Real *c, size_t *a_pos, size_t *b_pos, size_t *c_pos, DECDIG *av, DECDIG *bv);
-static int VpNmlz(Real *a);
-static void VpFormatSt(char *psz, size_t fFmt);
-static int VpRdup(Real *m, size_t ind_m);
-
-#ifdef BIGDECIMAL_DEBUG
-# ifdef HAVE_RB_EXT_RACTOR_SAFE
-# error Need to make rewiting gnAlloc atomic
-# endif
-static int gnAlloc = 0; /* Memory allocation counter */
-#endif /* BIGDECIMAL_DEBUG */
-
-/*
- * EXCEPTION Handling.
- */
-
-#define bigdecimal_set_thread_local_exception_mode(mode) \
- rb_thread_local_aset( \
- rb_thread_current(), \
- id_BigDecimal_exception_mode, \
- INT2FIX((int)(mode)) \
- )
-
-static unsigned short
-VpGetException (void)
-{
- VALUE const vmode = rb_thread_local_aref(
- rb_thread_current(),
- id_BigDecimal_exception_mode
- );
-
- if (NIL_P(vmode)) {
- bigdecimal_set_thread_local_exception_mode(BIGDECIMAL_EXCEPTION_MODE_DEFAULT);
- return BIGDECIMAL_EXCEPTION_MODE_DEFAULT;
- }
-
- return NUM2USHORT(vmode);
-}
-
-static void
-VpSetException(unsigned short f)
-{
- bigdecimal_set_thread_local_exception_mode(f);
-}
-
-static void
-VpCheckException(Real *p, bool always)
-{
- if (VpIsNaN(p)) {
- VpException(VP_EXCEPTION_NaN, "Computation results in 'NaN' (Not a Number)", always);
- }
- else if (VpIsPosInf(p)) {
- VpException(VP_EXCEPTION_INFINITY, "Computation results in 'Infinity'", always);
- }
- else if (VpIsNegInf(p)) {
- VpException(VP_EXCEPTION_INFINITY, "Computation results in '-Infinity'", always);
- }
-}
-
-static VALUE
-VpCheckGetValue(Real *p)
-{
- VpCheckException(p, false);
- return p->obj;
-}
-
-/*
- * Precision limit.
- */
-
-#define bigdecimal_set_thread_local_precision_limit(limit) \
- rb_thread_local_aset( \
- rb_thread_current(), \
- id_BigDecimal_precision_limit, \
- SIZET2NUM(limit) \
- )
-#define BIGDECIMAL_PRECISION_LIMIT_DEFAULT ((size_t)0)
-
-/* These 2 functions added at v1.1.7 */
-VP_EXPORT size_t
-VpGetPrecLimit(void)
-{
- VALUE const vlimit = rb_thread_local_aref(
- rb_thread_current(),
- id_BigDecimal_precision_limit
- );
-
- if (NIL_P(vlimit)) {
- bigdecimal_set_thread_local_precision_limit(BIGDECIMAL_PRECISION_LIMIT_DEFAULT);
- return BIGDECIMAL_PRECISION_LIMIT_DEFAULT;
- }
-
- return NUM2SIZET(vlimit);
-}
-
-VP_EXPORT size_t
-VpSetPrecLimit(size_t n)
-{
- size_t const s = VpGetPrecLimit();
- bigdecimal_set_thread_local_precision_limit(n);
- return s;
-}
-
-/*
- * Rounding mode.
- */
-
-#define bigdecimal_set_thread_local_rounding_mode(mode) \
- rb_thread_local_aset( \
- rb_thread_current(), \
- id_BigDecimal_rounding_mode, \
- INT2FIX((int)(mode)) \
- )
-
-VP_EXPORT unsigned short
-VpGetRoundMode(void)
-{
- VALUE const vmode = rb_thread_local_aref(
- rb_thread_current(),
- id_BigDecimal_rounding_mode
- );
-
- if (NIL_P(vmode)) {
- bigdecimal_set_thread_local_rounding_mode(BIGDECIMAL_ROUNDING_MODE_DEFAULT);
- return BIGDECIMAL_ROUNDING_MODE_DEFAULT;
- }
-
- return NUM2USHORT(vmode);
-}
-
-VP_EXPORT int
-VpIsRoundMode(unsigned short n)
-{
- switch (n) {
- case VP_ROUND_UP:
- case VP_ROUND_DOWN:
- case VP_ROUND_HALF_UP:
- case VP_ROUND_HALF_DOWN:
- case VP_ROUND_CEIL:
- case VP_ROUND_FLOOR:
- case VP_ROUND_HALF_EVEN:
- return 1;
-
- default:
- return 0;
- }
-}
-
-VP_EXPORT unsigned short
-VpSetRoundMode(unsigned short n)
-{
- if (VpIsRoundMode(n)) {
- bigdecimal_set_thread_local_rounding_mode(n);
- return n;
- }
-
- return VpGetRoundMode();
-}
-
-/*
- * 0.0 & 1.0 generator
- * These gZero_..... and gOne_..... can be any name
- * referenced from nowhere except Zero() and One().
- * gZero_..... and gOne_..... must have global scope
- * (to let the compiler know they may be changed in outside
- * (... but not actually..)).
- */
-volatile const double gOne_ABCED9B4_CE73__00400511F31D = 1.0;
-
-static double
-One(void)
-{
- return gOne_ABCED9B4_CE73__00400511F31D;
-}
-
-/*
- ----------------------------------------------------------------
- Value of sign in Real structure is reserved for future use.
- short sign;
- ==0 : NaN
- 1 : Positive zero
- -1 : Negative zero
- 2 : Positive number
- -2 : Negative number
- 3 : Positive infinite number
- -3 : Negative infinite number
- ----------------------------------------------------------------
-*/
-
-VP_EXPORT double
-VpGetDoubleNaN(void) /* Returns the value of NaN */
-{
- return nan("");
-}
-
-VP_EXPORT double
-VpGetDoublePosInf(void) /* Returns the value of +Infinity */
-{
- return HUGE_VAL;
-}
-
-VP_EXPORT double
-VpGetDoubleNegInf(void) /* Returns the value of -Infinity */
-{
- return -HUGE_VAL;
-}
-
-VP_EXPORT double
-VpGetDoubleNegZero(void) /* Returns the value of -0 */
-{
- static double nzero = 1000.0;
- if (nzero != 0.0) nzero = (One()/VpGetDoubleNegInf());
- return nzero;
-}
-
-#if 0 /* unused */
-VP_EXPORT int
-VpIsNegDoubleZero(double v)
-{
- double z = VpGetDoubleNegZero();
- return MemCmp(&v,&z,sizeof(v))==0;
-}
-#endif
-
-VP_EXPORT int
-VpException(unsigned short f, const char *str,int always)
-{
- unsigned short const exception_mode = VpGetException();
-
- if (f == VP_EXCEPTION_OP) always = 1;
-
- if (always || (exception_mode & f)) {
- switch(f) {
- /* case VP_EXCEPTION_OVERFLOW: */
- case VP_EXCEPTION_ZERODIVIDE:
- case VP_EXCEPTION_INFINITY:
- case VP_EXCEPTION_NaN:
- case VP_EXCEPTION_UNDERFLOW:
- case VP_EXCEPTION_OP:
- rb_raise(rb_eFloatDomainError, "%s", str);
- break;
- default:
- rb_fatal("%s", str);
- }
- }
- return 0; /* 0 Means VpException() raised no exception */
-}
-
-/* Throw exception or returns 0,when resulting c is Inf or NaN */
-/* sw=1:+ 2:- 3:* 4:/ */
-static int
-VpIsDefOP(Real *c, Real *a, Real *b, enum op_sw sw)
-{
- if (VpIsNaN(a) || VpIsNaN(b)) {
- /* at least a or b is NaN */
- VpSetNaN(c);
- goto NaN;
- }
-
- if (VpIsInf(a)) {
- if (VpIsInf(b)) {
- switch(sw) {
- case OP_SW_ADD: /* + */
- if (VpGetSign(a) == VpGetSign(b)) {
- VpSetInf(c, VpGetSign(a));
- goto Inf;
- }
- else {
- VpSetNaN(c);
- goto NaN;
- }
- case OP_SW_SUB: /* - */
- if (VpGetSign(a) != VpGetSign(b)) {
- VpSetInf(c, VpGetSign(a));
- goto Inf;
- }
- else {
- VpSetNaN(c);
- goto NaN;
- }
- case OP_SW_MULT: /* * */
- VpSetInf(c, VpGetSign(a)*VpGetSign(b));
- goto Inf;
- case OP_SW_DIV: /* / */
- VpSetNaN(c);
- goto NaN;
- }
- VpSetNaN(c);
- goto NaN;
- }
- /* Inf op Finite */
- switch(sw) {
- case OP_SW_ADD: /* + */
- case OP_SW_SUB: /* - */
- VpSetInf(c, VpGetSign(a));
- break;
- case OP_SW_MULT: /* * */
- if (VpIsZero(b)) {
- VpSetNaN(c);
- goto NaN;
- }
- VpSetInf(c, VpGetSign(a)*VpGetSign(b));
- break;
- case OP_SW_DIV: /* / */
- VpSetInf(c, VpGetSign(a)*VpGetSign(b));
- }
- goto Inf;
- }
-
- if (VpIsInf(b)) {
- switch(sw) {
- case OP_SW_ADD: /* + */
- VpSetInf(c, VpGetSign(b));
- break;
- case OP_SW_SUB: /* - */
- VpSetInf(c, -VpGetSign(b));
- break;
- case OP_SW_MULT: /* * */
- if (VpIsZero(a)) {
- VpSetNaN(c);
- goto NaN;
- }
- VpSetInf(c, VpGetSign(a)*VpGetSign(b));
- break;
- case OP_SW_DIV: /* / */
- VpSetZero(c, VpGetSign(a)*VpGetSign(b));
- }
- goto Inf;
- }
- return 1; /* Results OK */
-
-Inf:
- if (VpIsPosInf(c)) {
- return VpException(VP_EXCEPTION_INFINITY, "Computation results to 'Infinity'", 0);
- }
- else {
- return VpException(VP_EXCEPTION_INFINITY, "Computation results to '-Infinity'", 0);
- }
-
-NaN:
- return VpException(VP_EXCEPTION_NaN, "Computation results to 'NaN'", 0);
-}
-
-/*
- ----------------------------------------------------------------
-*/
-
-/*
- * returns number of chars needed to represent vp in specified format.
- */
-VP_EXPORT size_t
-VpNumOfChars(Real *vp,const char *pszFmt)
-{
- SIGNED_VALUE ex;
- size_t nc;
-
- if (vp == NULL) return BASE_FIG*2+6;
- if (!VpIsDef(vp)) return 32; /* not sure,may be OK */
-
- switch(*pszFmt) {
- case 'F':
- nc = BASE_FIG*(vp->Prec + 1)+2;
- ex = vp->exponent;
- if (ex < 0) {
- nc += BASE_FIG*(size_t)(-ex);
- }
- else {
- if ((size_t)ex > vp->Prec) {
- nc += BASE_FIG*((size_t)ex - vp->Prec);
- }
- }
- break;
- case 'E':
- /* fall through */
- default:
- nc = BASE_FIG*(vp->Prec + 2)+6; /* 3: sign + exponent chars */
- }
- return nc;
-}
-
-/*
- * Initializer for Vp routines and constants used.
- * [Input]
- * BaseVal: Base value(assigned to BASE) for Vp calculation.
- * It must be the form BaseVal=10**n.(n=1,2,3,...)
- * If Base <= 0L,then the BASE will be calculated so
- * that BASE is as large as possible satisfying the
- * relation MaxVal <= BASE*(BASE+1). Where the value
- * MaxVal is the largest value which can be represented
- * by one DECDIG word in the computer used.
- *
- * [Returns]
- * BIGDECIMAL_DOUBLE_FIGURES ... OK
- */
-VP_EXPORT size_t
-VpInit(DECDIG BaseVal)
-{
- /* Setup +/- Inf NaN -0 */
- VpGetDoubleNegZero();
-
- /* Const 1.0 */
- VpConstOne = NewOneNolimit(1, 1);
-
- /* Const 0.5 */
- VpConstPt5 = NewOneNolimit(1, 1);
- VpConstPt5->exponent = 0;
- VpConstPt5->frac[0] = 5*BASE1;
-
-#ifdef BIGDECIMAL_DEBUG
- gnAlloc = 0;
-#endif /* BIGDECIMAL_DEBUG */
-
-#ifdef BIGDECIMAL_DEBUG
- if (gfDebug) {
- printf("VpInit: BaseVal = %"PRIuDECDIG"\n", BaseVal);
- printf("\tBASE = %"PRIuDECDIG"\n", BASE);
- printf("\tHALF_BASE = %"PRIuDECDIG"\n", HALF_BASE);
- printf("\tBASE1 = %"PRIuDECDIG"\n", BASE1);
- printf("\tBASE_FIG = %u\n", BASE_FIG);
- printf("\tBIGDECIMAL_DOUBLE_FIGURES = %d\n", BIGDECIMAL_DOUBLE_FIGURES);
- }
-#endif /* BIGDECIMAL_DEBUG */
-
- return BIGDECIMAL_DOUBLE_FIGURES;
-}
-
-VP_EXPORT Real *
-VpOne(void)
-{
- return VpConstOne;
-}
-
-/* If exponent overflows,then raise exception or returns 0 */
-static int
-AddExponent(Real *a, SIGNED_VALUE n)
-{
- SIGNED_VALUE e = a->exponent;
- SIGNED_VALUE m = e+n;
- SIGNED_VALUE eb, mb;
- if (e > 0) {
- if (n > 0) {
- if (MUL_OVERFLOW_SIGNED_VALUE_P(m, (SIGNED_VALUE)BASE_FIG) ||
- MUL_OVERFLOW_SIGNED_VALUE_P(e, (SIGNED_VALUE)BASE_FIG))
- goto overflow;
- mb = m*(SIGNED_VALUE)BASE_FIG;
- eb = e*(SIGNED_VALUE)BASE_FIG;
- if (eb - mb > 0) goto overflow;
- }
- }
- else if (n < 0) {
- if (MUL_OVERFLOW_SIGNED_VALUE_P(m, (SIGNED_VALUE)BASE_FIG) ||
- MUL_OVERFLOW_SIGNED_VALUE_P(e, (SIGNED_VALUE)BASE_FIG))
- goto underflow;
- mb = m*(SIGNED_VALUE)BASE_FIG;
- eb = e*(SIGNED_VALUE)BASE_FIG;
- if (mb - eb > 0) goto underflow;
- }
- a->exponent = m;
- return 1;
-
-/* Overflow/Underflow ==> Raise exception or returns 0 */
-underflow:
- VpSetZero(a, VpGetSign(a));
- return VpException(VP_EXCEPTION_UNDERFLOW, "Exponent underflow", 0);
-
-overflow:
- VpSetInf(a, VpGetSign(a));
- return VpException(VP_EXCEPTION_OVERFLOW, "Exponent overflow", 0);
-}
-
-Real *
-bigdecimal_parse_special_string(const char *str)
-{
- static const struct {
- const char *str;
- size_t len;
- int sign;
- } table[] = {
- { SZ_INF, sizeof(SZ_INF) - 1, VP_SIGN_POSITIVE_INFINITE },
- { SZ_PINF, sizeof(SZ_PINF) - 1, VP_SIGN_POSITIVE_INFINITE },
- { SZ_NINF, sizeof(SZ_NINF) - 1, VP_SIGN_NEGATIVE_INFINITE },
- { SZ_NaN, sizeof(SZ_NaN) - 1, VP_SIGN_NaN }
- };
- static const size_t table_length = sizeof(table) / sizeof(table[0]);
- size_t i;
-
- for (i = 0; i < table_length; ++i) {
- const char *p;
- if (strncmp(str, table[i].str, table[i].len) != 0) {
- continue;
- }
-
- p = str + table[i].len;
- while (*p && ISSPACE(*p)) ++p;
- if (*p == '\0') {
- Real *vp = rbd_allocate_struct(1);
- vp->MaxPrec = 1;
- switch (table[i].sign) {
- default:
- UNREACHABLE; break;
- case VP_SIGN_POSITIVE_INFINITE:
- VpSetPosInf(vp);
- return vp;
- case VP_SIGN_NEGATIVE_INFINITE:
- VpSetNegInf(vp);
- return vp;
- case VP_SIGN_NaN:
- VpSetNaN(vp);
- return vp;
- }
- }
- }
-
- return NULL;
-}
-
-/*
- * Allocates variable.
- * [Input]
- * mx ... The number of decimal digits to be allocated, if zero then mx is determined by szVal.
- * The mx will be the number of significant digits can to be stored.
- * szVal ... The value assigned(char). If szVal==NULL, then zero is assumed.
- * If szVal[0]=='#' then MaxPrec is not affected by the precision limit
- * so that the full precision specified by szVal is allocated.
- *
- * [Returns]
- * Pointer to the newly allocated variable, or
- * NULL be returned if memory allocation is failed,or any error.
- */
-VP_EXPORT Real *
-VpAlloc(size_t mx, const char *szVal, int strict_p, int exc)
-{
- const char *orig_szVal = szVal;
- size_t i, j, ni, ipf, nf, ipe, ne, dot_seen, exp_seen, nalloc;
- size_t len;
- char v, *psz;
- int sign=1;
- Real *vp = NULL;
- VALUE buf;
-
- if (szVal == NULL) {
- return_zero:
- /* necessary to be able to store */
- /* at least mx digits. */
- /* szVal==NULL ==> allocate zero value. */
- vp = rbd_allocate_struct(mx);
- vp->MaxPrec = rbd_calculate_internal_digits(mx, false); /* Must false */
- VpSetZero(vp, 1); /* initialize vp to zero. */
- return vp;
- }
-
- /* Skipping leading spaces */
- while (ISSPACE(*szVal)) szVal++;
-
- /* Check on Inf & NaN */
- if ((vp = bigdecimal_parse_special_string(szVal)) != NULL) {
- return vp;
- }
-
- /* Processing the leading one `#` */
- if (*szVal != '#') {
- len = rbd_calculate_internal_digits(mx, true);
- }
- else {
- len = rbd_calculate_internal_digits(mx, false);
- ++szVal;
- }
-
- /* Scanning digits */
-
- /* A buffer for keeping scanned digits */
- buf = rb_str_tmp_new(strlen(szVal) + 1);
- psz = RSTRING_PTR(buf);
-
- /* cursor: i for psz, and j for szVal */
- i = j = 0;
-
- /* Scanning: sign part */
- v = psz[i] = szVal[j];
- if ((v == '-') || (v == '+')) {
- sign = -(v == '-');
- ++i;
- ++j;
- }
-
- /* Scanning: integer part */
- ni = 0; /* number of digits in the integer part */
- while ((v = psz[i] = szVal[j]) != '\0') {
- if (!strict_p && ISSPACE(v)) {
- v = psz[i] = '\0';
- break;
- }
- if (v == '_') {
- if (ni > 0) {
- v = szVal[j+1];
- if (v == '\0' || ISSPACE(v) || ISDIGIT(v)) {
- ++j;
- continue;
- }
- if (!strict_p) {
- v = psz[i] = '\0';
- break;
- }
- }
- goto invalid_value;
- }
- if (!ISDIGIT(v)) {
- break;
- }
- ++ni;
- ++i;
- ++j;
- }
-
- /* Scanning: fractional part */
- nf = 0; /* number of digits in the fractional part */
- ne = 0; /* number of digits in the exponential part */
- ipf = 0; /* index of the beginning of the fractional part */
- ipe = 0; /* index of the beginning of the exponential part */
- dot_seen = 0;
- exp_seen = 0;
-
- if (v != '\0') {
- /* Scanning fractional part */
- if ((psz[i] = szVal[j]) == '.') {
- dot_seen = 1;
- ++i;
- ++j;
- ipf = i;
- while ((v = psz[i] = szVal[j]) != '\0') {
- if (!strict_p && ISSPACE(v)) {
- v = psz[i] = '\0';
- break;
- }
- if (v == '_') {
- if (nf > 0 && ISDIGIT(szVal[j+1])) {
- ++j;
- continue;
- }
- if (!strict_p) {
- v = psz[i] = '\0';
- if (nf == 0) {
- dot_seen = 0;
- }
- break;
- }
- goto invalid_value;
- }
- if (!ISDIGIT(v)) break;
- ++i;
- ++j;
- ++nf;
- }
- }
-
- /* Scanning exponential part */
- if (v != '\0') {
- switch ((psz[i] = szVal[j])) {
- case '\0':
- break;
- case 'e': case 'E':
- case 'd': case 'D':
- exp_seen = 1;
- ++i;
- ++j;
- ipe = i;
- v = psz[i] = szVal[j];
- if ((v == '-') || (v == '+')) {
- ++i;
- ++j;
- }
- while ((v = psz[i] = szVal[j]) != '\0') {
- if (!strict_p && ISSPACE(v)) {
- v = psz[i] = '\0';
- break;
- }
- if (v == '_') {
- if (ne > 0 && ISDIGIT(szVal[j+1])) {
- ++j;
- continue;
- }
- if (!strict_p) {
- v = psz[i] = '\0';
- if (ne == 0) {
- exp_seen = 0;
- }
- break;
- }
- goto invalid_value;
- }
- if (!ISDIGIT(v)) break;
- ++i;
- ++j;
- ++ne;
- }
- break;
- default:
- break;
- }
- }
-
- if (v != '\0') {
- /* Scanning trailing spaces */
- while (ISSPACE(szVal[j])) ++j;
-
- /* Invalid character */
- if (szVal[j] && strict_p) {
- goto invalid_value;
- }
- }
- }
-
- psz[i] = '\0';
-
- if (strict_p && (((ni == 0 || dot_seen) && nf == 0) || (exp_seen && ne == 0))) {
- VALUE str;
- invalid_value:
- if (!strict_p) {
- goto return_zero;
- }
- if (!exc) {
- return NULL;
- }
- str = rb_str_new2(orig_szVal);
- rb_raise(rb_eArgError, "invalid value for BigDecimal(): \"%"PRIsVALUE"\"", str);
- }
-
- nalloc = (ni + nf + BASE_FIG - 1) / BASE_FIG + 1; /* set effective allocation */
- /* units for szVal[] */
- if (len == 0) len = 1;
- nalloc = Max(nalloc, len);
- len = nalloc;
- vp = rbd_allocate_struct(len);
- vp->MaxPrec = len; /* set max precision */
- VpSetZero(vp, sign);
- VpCtoV(vp, psz, ni, psz + ipf, nf, psz + ipe, ne);
- rb_str_resize(buf, 0);
- return vp;
-}
-
-/*
- * Assignment(c=a).
- * [Input]
- * a ... RHSV
- * isw ... switch for assignment.
- * c = a when isw > 0
- * c = -a when isw < 0
- * if c->MaxPrec < a->Prec,then round operation
- * will be performed.
- * [Output]
- * c ... LHSV
- */
-VP_EXPORT size_t
-VpAsgn(Real *c, Real *a, int isw)
-{
- size_t n;
- if (VpIsNaN(a)) {
- VpSetNaN(c);
- return 0;
- }
- if (VpIsInf(a)) {
- VpSetInf(c, isw * VpGetSign(a));
- return 0;
- }
-
- /* check if the RHS is zero */
- if (!VpIsZero(a)) {
- c->exponent = a->exponent; /* store exponent */
- VpSetSign(c, isw * VpGetSign(a)); /* set sign */
- n = (a->Prec < c->MaxPrec) ? (a->Prec) : (c->MaxPrec);
- c->Prec = n;
- memcpy(c->frac, a->frac, n * sizeof(DECDIG));
- /* Needs round ? */
- if (isw != 10) {
- /* Not in ActiveRound */
- if(c->Prec < a->Prec) {
- VpInternalRound(c, n, (n>0) ? a->frac[n-1] : 0, a->frac[n]);
- }
- else {
- VpLimitRound(c,0);
- }
- }
- }
- else {
- /* The value of 'a' is zero. */
- VpSetZero(c, isw * VpGetSign(a));
- return 1;
- }
- return c->Prec * BASE_FIG;
-}
-
-/*
- * c = a + b when operation = 1 or 2
- * c = a - b when operation = -1 or -2.
- * Returns number of significant digits of c
- */
-VP_EXPORT size_t
-VpAddSub(Real *c, Real *a, Real *b, int operation)
-{
- short sw, isw;
- Real *a_ptr, *b_ptr;
- size_t n, na, nb, i;
- DECDIG mrv;
-
-#ifdef BIGDECIMAL_DEBUG
- if (gfDebug) {
- VPrint(stdout, "VpAddSub(enter) a=% \n", a);
- VPrint(stdout, " b=% \n", b);
- printf(" operation=%d\n", operation);
- }
-#endif /* BIGDECIMAL_DEBUG */
-
- if (!VpIsDefOP(c, a, b, (operation > 0) ? OP_SW_ADD : OP_SW_SUB)) return 0; /* No significant digits */
-
- /* check if a or b is zero */
- if (VpIsZero(a)) {
- /* a is zero,then assign b to c */
- if (!VpIsZero(b)) {
- VpAsgn(c, b, operation);
- }
- else {
- /* Both a and b are zero. */
- if (VpGetSign(a) < 0 && operation * VpGetSign(b) < 0) {
- /* -0 -0 */
- VpSetZero(c, -1);
- }
- else {
- VpSetZero(c, 1);
- }
- return 1; /* 0: 1 significant digits */
- }
- return c->Prec * BASE_FIG;
- }
- if (VpIsZero(b)) {
- /* b is zero,then assign a to c. */
- VpAsgn(c, a, 1);
- return c->Prec*BASE_FIG;
- }
-
- if (operation < 0) sw = -1;
- else sw = 1;
-
- /* compare absolute value. As a result,|a_ptr|>=|b_ptr| */
- if (a->exponent > b->exponent) {
- a_ptr = a;
- b_ptr = b;
- } /* |a|>|b| */
- else if (a->exponent < b->exponent) {
- a_ptr = b;
- b_ptr = a;
- } /* |a|<|b| */
- else {
- /* Exponent part of a and b is the same,then compare fraction */
- /* part */
- na = a->Prec;
- nb = b->Prec;
- n = Min(na, nb);
- for (i=0; i < n; ++i) {
- if (a->frac[i] > b->frac[i]) {
- a_ptr = a;
- b_ptr = b;
- goto end_if;
- }
- else if (a->frac[i] < b->frac[i]) {
- a_ptr = b;
- b_ptr = a;
- goto end_if;
- }
- }
- if (na > nb) {
- a_ptr = a;
- b_ptr = b;
- goto end_if;
- }
- else if (na < nb) {
- a_ptr = b;
- b_ptr = a;
- goto end_if;
- }
- /* |a| == |b| */
- if (VpGetSign(a) + sw *VpGetSign(b) == 0) {
- VpSetZero(c, 1); /* abs(a)=abs(b) and operation = '-' */
- return c->Prec * BASE_FIG;
- }
- a_ptr = a;
- b_ptr = b;
- }
-
-end_if:
- isw = VpGetSign(a) + sw *VpGetSign(b);
- /*
- * isw = 0 ...( 1)+(-1),( 1)-( 1),(-1)+(1),(-1)-(-1)
- * = 2 ...( 1)+( 1),( 1)-(-1)
- * =-2 ...(-1)+(-1),(-1)-( 1)
- * If isw==0, then c =(Sign a_ptr)(|a_ptr|-|b_ptr|)
- * else c =(Sign ofisw)(|a_ptr|+|b_ptr|)
- */
- if (isw) { /* addition */
- VpSetSign(c, 1);
- mrv = VpAddAbs(a_ptr, b_ptr, c);
- VpSetSign(c, isw / 2);
- }
- else { /* subtraction */
- VpSetSign(c, 1);
- mrv = VpSubAbs(a_ptr, b_ptr, c);
- if (a_ptr == a) {
- VpSetSign(c,VpGetSign(a));
- }
- else {
- VpSetSign(c, VpGetSign(a_ptr) * sw);
- }
- }
- VpInternalRound(c, 0, (c->Prec > 0) ? c->frac[c->Prec-1] : 0, mrv);
-
-#ifdef BIGDECIMAL_DEBUG
- if (gfDebug) {
- VPrint(stdout, "VpAddSub(result) c=% \n", c);
- VPrint(stdout, " a=% \n", a);
- VPrint(stdout, " b=% \n", b);
- printf(" operation=%d\n", operation);
- }
-#endif /* BIGDECIMAL_DEBUG */
- return c->Prec * BASE_FIG;
-}
-
-/*
- * Addition of two values with variable precision
- * a and b assuming abs(a)>abs(b).
- * c = abs(a) + abs(b) ; where |a|>=|b|
- */
-static DECDIG
-VpAddAbs(Real *a, Real *b, Real *c)
-{
- size_t word_shift;
- size_t ap;
- size_t bp;
- size_t cp;
- size_t a_pos;
- size_t b_pos, b_pos_with_word_shift;
- size_t c_pos;
- DECDIG av, bv, carry, mrv;
-
-#ifdef BIGDECIMAL_DEBUG
- if (gfDebug) {
- VPrint(stdout, "VpAddAbs called: a = %\n", a);
- VPrint(stdout, " b = %\n", b);
- }
-#endif /* BIGDECIMAL_DEBUG */
-
- word_shift = VpSetPTR(a, b, c, &ap, &bp, &cp, &av, &bv);
- a_pos = ap;
- b_pos = bp;
- c_pos = cp;
-
- if (word_shift == (size_t)-1L) return 0; /* Overflow */
- if (b_pos == (size_t)-1L) goto Assign_a;
-
- mrv = av + bv; /* Most right val. Used for round. */
-
- /* Just assign the last few digits of b to c because a has no */
- /* corresponding digits to be added. */
- if (b_pos > 0) {
- while (b_pos > 0 && b_pos + word_shift > a_pos) {
- c->frac[--c_pos] = b->frac[--b_pos];
- }
- }
- if (b_pos == 0 && word_shift > a_pos) {
- while (word_shift-- > a_pos) {
- c->frac[--c_pos] = 0;
- }
- }
-
- /* Just assign the last few digits of a to c because b has no */
- /* corresponding digits to be added. */
- b_pos_with_word_shift = b_pos + word_shift;
- while (a_pos > b_pos_with_word_shift) {
- c->frac[--c_pos] = a->frac[--a_pos];
- }
- carry = 0; /* set first carry be zero */
-
- /* Now perform addition until every digits of b will be */
- /* exhausted. */
- while (b_pos > 0) {
- c->frac[--c_pos] = a->frac[--a_pos] + b->frac[--b_pos] + carry;
- if (c->frac[c_pos] >= BASE) {
- c->frac[c_pos] -= BASE;
- carry = 1;
- }
- else {
- carry = 0;
- }
- }
-
- /* Just assign the first few digits of a with considering */
- /* the carry obtained so far because b has been exhausted. */
- while (a_pos > 0) {
- c->frac[--c_pos] = a->frac[--a_pos] + carry;
- if (c->frac[c_pos] >= BASE) {
- c->frac[c_pos] -= BASE;
- carry = 1;
- }
- else {
- carry = 0;
- }
- }
- if (c_pos) c->frac[c_pos - 1] += carry;
- goto Exit;
-
-Assign_a:
- VpAsgn(c, a, 1);
- mrv = 0;
-
-Exit:
-
-#ifdef BIGDECIMAL_DEBUG
- if (gfDebug) {
- VPrint(stdout, "VpAddAbs exit: c=% \n", c);
- }
-#endif /* BIGDECIMAL_DEBUG */
- return mrv;
-}
-
-/*
- * c = abs(a) - abs(b)
- */
-static DECDIG
-VpSubAbs(Real *a, Real *b, Real *c)
-{
- size_t word_shift;
- size_t ap;
- size_t bp;
- size_t cp;
- size_t a_pos;
- size_t b_pos, b_pos_with_word_shift;
- size_t c_pos;
- DECDIG av, bv, borrow, mrv;
-
-#ifdef BIGDECIMAL_DEBUG
- if (gfDebug) {
- VPrint(stdout, "VpSubAbs called: a = %\n", a);
- VPrint(stdout, " b = %\n", b);
- }
-#endif /* BIGDECIMAL_DEBUG */
-
- word_shift = VpSetPTR(a, b, c, &ap, &bp, &cp, &av, &bv);
- a_pos = ap;
- b_pos = bp;
- c_pos = cp;
- if (word_shift == (size_t)-1L) return 0; /* Overflow */
- if (b_pos == (size_t)-1L) goto Assign_a;
-
- if (av >= bv) {
- mrv = av - bv;
- borrow = 0;
- }
- else {
- mrv = 0;
- borrow = 1;
- }
-
- /* Just assign the values which are the BASE subtracted by */
- /* each of the last few digits of the b because the a has no */
- /* corresponding digits to be subtracted. */
- if (b_pos + word_shift > a_pos) {
- while (b_pos > 0 && b_pos + word_shift > a_pos) {
- c->frac[--c_pos] = BASE - b->frac[--b_pos] - borrow;
- borrow = 1;
- }
- if (b_pos == 0) {
- while (word_shift > a_pos) {
- --word_shift;
- c->frac[--c_pos] = BASE - borrow;
- borrow = 1;
- }
- }
- }
- /* Just assign the last few digits of a to c because b has no */
- /* corresponding digits to subtract. */
-
- b_pos_with_word_shift = b_pos + word_shift;
- while (a_pos > b_pos_with_word_shift) {
- c->frac[--c_pos] = a->frac[--a_pos];
- }
-
- /* Now perform subtraction until every digits of b will be */
- /* exhausted. */
- while (b_pos > 0) {
- --c_pos;
- if (a->frac[--a_pos] < b->frac[--b_pos] + borrow) {
- c->frac[c_pos] = BASE + a->frac[a_pos] - b->frac[b_pos] - borrow;
- borrow = 1;
- }
- else {
- c->frac[c_pos] = a->frac[a_pos] - b->frac[b_pos] - borrow;
- borrow = 0;
- }
- }
-
- /* Just assign the first few digits of a with considering */
- /* the borrow obtained so far because b has been exhausted. */
- while (a_pos > 0) {
- --c_pos;
- if (a->frac[--a_pos] < borrow) {
- c->frac[c_pos] = BASE + a->frac[a_pos] - borrow;
- borrow = 1;
- }
- else {
- c->frac[c_pos] = a->frac[a_pos] - borrow;
- borrow = 0;
- }
- }
- if (c_pos) c->frac[c_pos - 1] -= borrow;
- goto Exit;
-
-Assign_a:
- VpAsgn(c, a, 1);
- mrv = 0;
-
-Exit:
-#ifdef BIGDECIMAL_DEBUG
- if (gfDebug) {
- VPrint(stdout, "VpSubAbs exit: c=% \n", c);
- }
-#endif /* BIGDECIMAL_DEBUG */
- return mrv;
-}
-
-/*
- * Note: If(av+bv)>= HALF_BASE,then 1 will be added to the least significant
- * digit of c(In case of addition).
- * ------------------------- figure of output -----------------------------------
- * a = xxxxxxxxxxx
- * b = xxxxxxxxxx
- * c =xxxxxxxxxxxxxxx
- * word_shift = | |
- * right_word = | | (Total digits in RHSV)
- * left_word = | | (Total digits in LHSV)
- * a_pos = |
- * b_pos = |
- * c_pos = |
- */
-static size_t
-VpSetPTR(Real *a, Real *b, Real *c, size_t *a_pos, size_t *b_pos, size_t *c_pos, DECDIG *av, DECDIG *bv)
-{
- size_t left_word, right_word, word_shift;
-
- size_t const round_limit = (VpGetPrecLimit() + BASE_FIG - 1) / BASE_FIG;
-
- assert(a->exponent >= b->exponent);
-
- c->frac[0] = 0;
- *av = *bv = 0;
-
- word_shift = (a->exponent - b->exponent);
- left_word = b->Prec + word_shift;
- right_word = Max(a->Prec, left_word);
- left_word = c->MaxPrec - 1; /* -1 ... prepare for round up */
-
- /*
- * check if 'round' is needed.
- */
- if (right_word > left_word) { /* round ? */
- /*---------------------------------
- * Actual size of a = xxxxxxAxx
- * Actual size of b = xxxBxxxxx
- * Max. size of c = xxxxxx
- * Round off = |-----|
- * c_pos = |
- * right_word = |
- * a_pos = |
- */
- *c_pos = right_word = left_word + 1; /* Set resulting precision */
- /* be equal to that of c */
- if (a->Prec >= c->MaxPrec) {
- /*
- * a = xxxxxxAxxx
- * c = xxxxxx
- * a_pos = |
- */
- *a_pos = left_word;
- if (*a_pos <= round_limit) {
- *av = a->frac[*a_pos]; /* av is 'A' shown in above. */
- }
- }
- else {
- /*
- * a = xxxxxxx
- * c = xxxxxxxxxx
- * a_pos = |
- */
- *a_pos = a->Prec;
- }
- if (b->Prec + word_shift >= c->MaxPrec) {
- /*
- * a = xxxxxxxxx
- * b = xxxxxxxBxxx
- * c = xxxxxxxxxxx
- * b_pos = |
- */
- if (c->MaxPrec >= word_shift + 1) {
- *b_pos = c->MaxPrec - word_shift - 1;
- if (*b_pos + word_shift <= round_limit) {
- *bv = b->frac[*b_pos];
- }
- }
- else {
- *b_pos = -1L;
- }
- }
- else {
- /*
- * a = xxxxxxxxxxxxxxxx
- * b = xxxxxx
- * c = xxxxxxxxxxxxx
- * b_pos = |
- */
- *b_pos = b->Prec;
- }
- }
- else { /* The MaxPrec of c - 1 > The Prec of a + b */
- /*
- * a = xxxxxxx
- * b = xxxxxx
- * c = xxxxxxxxxxx
- * c_pos = |
- */
- *b_pos = b->Prec;
- *a_pos = a->Prec;
- *c_pos = right_word + 1;
- }
- c->Prec = *c_pos;
- c->exponent = a->exponent;
- if (!AddExponent(c, 1)) return (size_t)-1L;
- return word_shift;
-}
-
-/*
- * Return number of significant digits
- * c = a * b , Where a = a0a1a2 ... an
- * b = b0b1b2 ... bm
- * c = c0c1c2 ... cl
- * a0 a1 ... an * bm
- * a0 a1 ... an * bm-1
- * . . .
- * . . .
- * a0 a1 .... an * b0
- * +_____________________________
- * c0 c1 c2 ...... cl
- * nc <---|
- * MaxAB |--------------------|
- */
-VP_EXPORT size_t
-VpMult(Real *c, Real *a, Real *b)
-{
- size_t MxIndA, MxIndB, MxIndAB, MxIndC;
- size_t ind_c, i, ii, nc;
- size_t ind_as, ind_ae, ind_bs;
- DECDIG carry;
- DECDIG_DBL s;
- Real *w;
-
-#ifdef BIGDECIMAL_DEBUG
- if (gfDebug) {
- VPrint(stdout, "VpMult(Enter): a=% \n", a);
- VPrint(stdout, " b=% \n", b);
- }
-#endif /* BIGDECIMAL_DEBUG */
-
- if (!VpIsDefOP(c, a, b, OP_SW_MULT)) return 0; /* No significant digit */
-
- if (VpIsZero(a) || VpIsZero(b)) {
- /* at least a or b is zero */
- VpSetZero(c, VpGetSign(a) * VpGetSign(b));
- return 1; /* 0: 1 significant digit */
- }
-
- if (VpIsOne(a)) {
- VpAsgn(c, b, VpGetSign(a));
- goto Exit;
- }
- if (VpIsOne(b)) {
- VpAsgn(c, a, VpGetSign(b));
- goto Exit;
- }
- if (b->Prec > a->Prec) {
- /* Adjust so that digits(a)>digits(b) */
- w = a;
- a = b;
- b = w;
- }
- w = NULL;
- MxIndA = a->Prec - 1;
- MxIndB = b->Prec - 1;
- MxIndC = c->MaxPrec - 1;
- MxIndAB = a->Prec + b->Prec - 1;
-
- if (MxIndC < MxIndAB) { /* The Max. prec. of c < Prec(a)+Prec(b) */
- w = c;
- c = NewZeroNolimit(1, (size_t)((MxIndAB + 1) * BASE_FIG));
- MxIndC = MxIndAB;
- }
-
- /* set LHSV c info */
-
- c->exponent = a->exponent; /* set exponent */
- if (!AddExponent(c, b->exponent)) {
- if (w) rbd_free_struct(c);
- return 0;
- }
- VpSetSign(c, VpGetSign(a) * VpGetSign(b)); /* set sign */
- carry = 0;
- nc = ind_c = MxIndAB;
- memset(c->frac, 0, (nc + 1) * sizeof(DECDIG)); /* Initialize c */
- c->Prec = nc + 1; /* set precision */
- for (nc = 0; nc < MxIndAB; ++nc, --ind_c) {
- if (nc < MxIndB) { /* The left triangle of the Fig. */
- ind_as = MxIndA - nc;
- ind_ae = MxIndA;
- ind_bs = MxIndB;
- }
- else if (nc <= MxIndA) { /* The middle rectangular of the Fig. */
- ind_as = MxIndA - nc;
- ind_ae = MxIndA - (nc - MxIndB);
- ind_bs = MxIndB;
- }
- else /* if (nc > MxIndA) */ { /* The right triangle of the Fig. */
- ind_as = 0;
- ind_ae = MxIndAB - nc - 1;
- ind_bs = MxIndB - (nc - MxIndA);
- }
-
- for (i = ind_as; i <= ind_ae; ++i) {
- s = (DECDIG_DBL)a->frac[i] * b->frac[ind_bs--];
- carry = (DECDIG)(s / BASE);
- s -= (DECDIG_DBL)carry * BASE;
- c->frac[ind_c] += (DECDIG)s;
- if (c->frac[ind_c] >= BASE) {
- s = c->frac[ind_c] / BASE;
- carry += (DECDIG)s;
- c->frac[ind_c] -= (DECDIG)(s * BASE);
- }
- if (carry) {
- ii = ind_c;
- while (ii-- > 0) {
- c->frac[ii] += carry;
- if (c->frac[ii] >= BASE) {
- carry = c->frac[ii] / BASE;
- c->frac[ii] -= (carry * BASE);
- }
- else {
- break;
- }
- }
- }
- }
- }
- if (w != NULL) { /* free work variable */
- VpNmlz(c);
- VpAsgn(w, c, 1);
- rbd_free_struct(c);
- c = w;
- }
- else {
- VpLimitRound(c,0);
- }
-
-Exit:
-#ifdef BIGDECIMAL_DEBUG
- if (gfDebug) {
- VPrint(stdout, "VpMult(c=a*b): c=% \n", c);
- VPrint(stdout, " a=% \n", a);
- VPrint(stdout, " b=% \n", b);
- }
-#endif /*BIGDECIMAL_DEBUG */
- return c->Prec*BASE_FIG;
-}
-
-/*
- * c = a / b, remainder = r
- */
-VP_EXPORT size_t
-VpDivd(Real *c, Real *r, Real *a, Real *b)
-{
- size_t word_a, word_b, word_c, word_r;
- size_t i, n, ind_a, ind_b, ind_c, ind_r;
- size_t nLoop;
- DECDIG_DBL q, b1, b1p1, b1b2, b1b2p1, r1r2;
- DECDIG borrow, borrow1, borrow2;
- DECDIG_DBL qb;
-
-#ifdef BIGDECIMAL_DEBUG
- if (gfDebug) {
- VPrint(stdout, " VpDivd(c=a/b) a=% \n", a);
- VPrint(stdout, " b=% \n", b);
- }
-#endif /*BIGDECIMAL_DEBUG */
-
- VpSetNaN(r);
- if (!VpIsDefOP(c, a, b, OP_SW_DIV)) goto Exit;
- if (VpIsZero(a) && VpIsZero(b)) {
- VpSetNaN(c);
- return VpException(VP_EXCEPTION_NaN, "Computation results to 'NaN'", 0);
- }
- if (VpIsZero(b)) {
- VpSetInf(c, VpGetSign(a) * VpGetSign(b));
- return VpException(VP_EXCEPTION_ZERODIVIDE, "Divide by zero", 0);
- }
- if (VpIsZero(a)) {
- /* numerator a is zero */
- VpSetZero(c, VpGetSign(a) * VpGetSign(b));
- VpSetZero(r, VpGetSign(a) * VpGetSign(b));
- goto Exit;
- }
- if (VpIsOne(b)) {
- /* divide by one */
- VpAsgn(c, a, VpGetSign(b));
- VpSetZero(r, VpGetSign(a));
- goto Exit;
- }
-
- word_a = a->Prec;
- word_b = b->Prec;
- word_c = c->MaxPrec;
- word_r = r->MaxPrec;
-
- if (word_a >= word_r) goto space_error;
-
- ind_r = 1;
- r->frac[0] = 0;
- while (ind_r <= word_a) {
- r->frac[ind_r] = a->frac[ind_r - 1];
- ++ind_r;
- }
- while (ind_r < word_r) r->frac[ind_r++] = 0;
-
- ind_c = 0;
- while (ind_c < word_c) c->frac[ind_c++] = 0;
-
- /* initial procedure */
- b1 = b1p1 = b->frac[0];
- if (b->Prec <= 1) {
- b1b2p1 = b1b2 = b1p1 * BASE;
- }
- else {
- b1p1 = b1 + 1;
- b1b2p1 = b1b2 = b1 * BASE + b->frac[1];
- if (b->Prec > 2) ++b1b2p1;
- }
-
- /* */
- /* loop start */
- ind_c = word_r - 1;
- nLoop = Min(word_c,ind_c);
- ind_c = 1;
- while (ind_c < nLoop) {
- if (r->frac[ind_c] == 0) {
- ++ind_c;
- continue;
- }
- r1r2 = (DECDIG_DBL)r->frac[ind_c] * BASE + r->frac[ind_c + 1];
- if (r1r2 == b1b2) {
- /* The first two word digits is the same */
- ind_b = 2;
- ind_a = ind_c + 2;
- while (ind_b < word_b) {
- if (r->frac[ind_a] < b->frac[ind_b]) goto div_b1p1;
- if (r->frac[ind_a] > b->frac[ind_b]) break;
- ++ind_a;
- ++ind_b;
- }
- /* The first few word digits of r and b is the same and */
- /* the first different word digit of w is greater than that */
- /* of b, so quotient is 1 and just subtract b from r. */
- borrow = 0; /* quotient=1, then just r-b */
- ind_b = b->Prec - 1;
- ind_r = ind_c + ind_b;
- if (ind_r >= word_r) goto space_error;
- n = ind_b;
- for (i = 0; i <= n; ++i) {
- if (r->frac[ind_r] < b->frac[ind_b] + borrow) {
- r->frac[ind_r] += (BASE - (b->frac[ind_b] + borrow));
- borrow = 1;
- }
- else {
- r->frac[ind_r] = r->frac[ind_r] - b->frac[ind_b] - borrow;
- borrow = 0;
- }
- --ind_r;
- --ind_b;
- }
- ++c->frac[ind_c];
- goto carry;
- }
- /* The first two word digits is not the same, */
- /* then compare magnitude, and divide actually. */
- if (r1r2 >= b1b2p1) {
- q = r1r2 / b1b2p1; /* q == (DECDIG)q */
- c->frac[ind_c] += (DECDIG)q;
- ind_r = b->Prec + ind_c - 1;
- goto sub_mult;
- }
-
-div_b1p1:
- if (ind_c + 1 >= word_c) goto out_side;
- q = r1r2 / b1p1; /* q == (DECDIG)q */
- c->frac[ind_c + 1] += (DECDIG)q;
- ind_r = b->Prec + ind_c;
-
-sub_mult:
- borrow1 = borrow2 = 0;
- ind_b = word_b - 1;
- if (ind_r >= word_r) goto space_error;
- n = ind_b;
- for (i = 0; i <= n; ++i) {
- /* now, perform r = r - q * b */
- qb = q * b->frac[ind_b];
- if (qb < BASE) borrow1 = 0;
- else {
- borrow1 = (DECDIG)(qb / BASE);
- qb -= (DECDIG_DBL)borrow1 * BASE; /* get qb < BASE */
- }
- if(r->frac[ind_r] < qb) {
- r->frac[ind_r] += (DECDIG)(BASE - qb);
- borrow2 = borrow2 + borrow1 + 1;
- }
- else {
- r->frac[ind_r] -= (DECDIG)qb;
- borrow2 += borrow1;
- }
- if (borrow2) {
- if(r->frac[ind_r - 1] < borrow2) {
- r->frac[ind_r - 1] += (BASE - borrow2);
- borrow2 = 1;
- }
- else {
- r->frac[ind_r - 1] -= borrow2;
- borrow2 = 0;
- }
- }
- --ind_r;
- --ind_b;
- }
-
- r->frac[ind_r] -= borrow2;
-carry:
- ind_r = ind_c;
- while (c->frac[ind_r] >= BASE) {
- c->frac[ind_r] -= BASE;
- --ind_r;
- ++c->frac[ind_r];
- }
- }
- /* End of operation, now final arrangement */
-out_side:
- c->Prec = word_c;
- c->exponent = a->exponent;
- if (!AddExponent(c, 2)) return 0;
- if (!AddExponent(c, -(b->exponent))) return 0;
-
- VpSetSign(c, VpGetSign(a) * VpGetSign(b));
- VpNmlz(c); /* normalize c */
- r->Prec = word_r;
- r->exponent = a->exponent;
- if (!AddExponent(r, 1)) return 0;
- VpSetSign(r, VpGetSign(a));
- VpNmlz(r); /* normalize r(remainder) */
- goto Exit;
-
-space_error:
-#ifdef BIGDECIMAL_DEBUG
- if (gfDebug) {
- printf(" word_a=%"PRIuSIZE"\n", word_a);
- printf(" word_b=%"PRIuSIZE"\n", word_b);
- printf(" word_c=%"PRIuSIZE"\n", word_c);
- printf(" word_r=%"PRIuSIZE"\n", word_r);
- printf(" ind_r =%"PRIuSIZE"\n", ind_r);
- }
-#endif /* BIGDECIMAL_DEBUG */
- rb_bug("ERROR(VpDivd): space for remainder too small.");
-
-Exit:
-#ifdef BIGDECIMAL_DEBUG
- if (gfDebug) {
- VPrint(stdout, " VpDivd(c=a/b), c=% \n", c);
- VPrint(stdout, " r=% \n", r);
- }
-#endif /* BIGDECIMAL_DEBUG */
- return c->Prec * BASE_FIG;
-}
-
-/*
- * Input a = 00000xxxxxxxx En(5 preceding zeros)
- * Output a = xxxxxxxx En-5
- */
-static int
-VpNmlz(Real *a)
-{
- size_t ind_a, i;
-
- if (!VpIsDef(a)) goto NoVal;
- if (VpIsZero(a)) goto NoVal;
-
- ind_a = a->Prec;
- while (ind_a--) {
- if (a->frac[ind_a]) {
- a->Prec = ind_a + 1;
- i = 0;
- while (a->frac[i] == 0) ++i; /* skip the first few zeros */
- if (i) {
- a->Prec -= i;
- if (!AddExponent(a, -(SIGNED_VALUE)i)) return 0;
- memmove(&a->frac[0], &a->frac[i], a->Prec*sizeof(DECDIG));
- }
- return 1;
- }
- }
- /* a is zero(no non-zero digit) */
- VpSetZero(a, VpGetSign(a));
- return 0;
-
-NoVal:
- a->frac[0] = 0;
- a->Prec = 1;
- return 0;
-}
-
-/*
- * VpComp = 0 ... if a=b,
- * Pos ... a>b,
- * Neg ... a<b.
- * 999 ... result undefined(NaN)
- */
-VP_EXPORT int
-VpComp(Real *a, Real *b)
-{
- int val;
- size_t mx, ind;
- int e;
- val = 0;
- if (VpIsNaN(a) || VpIsNaN(b)) return 999;
- if (!VpIsDef(a)) {
- if (!VpIsDef(b)) e = a->sign - b->sign;
- else e = a->sign;
-
- if (e > 0) return 1;
- else if (e < 0) return -1;
- else return 0;
- }
- if (!VpIsDef(b)) {
- e = -b->sign;
- if (e > 0) return 1;
- else return -1;
- }
- /* Zero check */
- if (VpIsZero(a)) {
- if (VpIsZero(b)) return 0; /* both zero */
- val = -VpGetSign(b);
- goto Exit;
- }
- if (VpIsZero(b)) {
- val = VpGetSign(a);
- goto Exit;
- }
-
- /* compare sign */
- if (VpGetSign(a) > VpGetSign(b)) {
- val = 1; /* a>b */
- goto Exit;
- }
- if (VpGetSign(a) < VpGetSign(b)) {
- val = -1; /* a<b */
- goto Exit;
- }
-
- /* a and b have same sign, && sign!=0,then compare exponent */
- if (a->exponent > b->exponent) {
- val = VpGetSign(a);
- goto Exit;
- }
- if (a->exponent < b->exponent) {
- val = -VpGetSign(b);
- goto Exit;
- }
-
- /* a and b have same exponent, then compare their significand. */
- mx = (a->Prec < b->Prec) ? a->Prec : b->Prec;
- ind = 0;
- while (ind < mx) {
- if (a->frac[ind] > b->frac[ind]) {
- val = VpGetSign(a);
- goto Exit;
- }
- if (a->frac[ind] < b->frac[ind]) {
- val = -VpGetSign(b);
- goto Exit;
- }
- ++ind;
- }
- if (a->Prec > b->Prec) {
- val = VpGetSign(a);
- }
- else if (a->Prec < b->Prec) {
- val = -VpGetSign(b);
- }
-
-Exit:
- if (val > 1) val = 1;
- else if (val < -1) val = -1;
-
-#ifdef BIGDECIMAL_DEBUG
- if (gfDebug) {
- VPrint(stdout, " VpComp a=%\n", a);
- VPrint(stdout, " b=%\n", b);
- printf(" ans=%d\n", val);
- }
-#endif /* BIGDECIMAL_DEBUG */
- return (int)val;
-}
-
-/*
- * cntl_chr ... ASCIIZ Character, print control characters
- * Available control codes:
- * % ... VP variable. To print '%', use '%%'.
- * \n ... new line
- * \b ... backspace
- * \t ... tab
- * Note: % must not appear more than once
- * a ... VP variable to be printed
- */
-static int
-VPrint(FILE *fp, const char *cntl_chr, Real *a)
-{
- size_t i, j, nc, nd, ZeroSup, sep = 10;
- DECDIG m, e, nn;
-
- j = 0;
- nd = nc = 0; /* nd : number of digits in fraction part(every 10 digits, */
- /* nd<=10). */
- /* nc : number of characters printed */
- ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */
- while (*(cntl_chr + j)) {
- if (*(cntl_chr + j) == '%' && *(cntl_chr + j + 1) != '%') {
- nc = 0;
- if (VpIsNaN(a)) {
- fprintf(fp, SZ_NaN);
- nc += 8;
- }
- else if (VpIsPosInf(a)) {
- fprintf(fp, SZ_INF);
- nc += 8;
- }
- else if (VpIsNegInf(a)) {
- fprintf(fp, SZ_NINF);
- nc += 9;
- }
- else if (!VpIsZero(a)) {
- if (BIGDECIMAL_NEGATIVE_P(a)) {
- fprintf(fp, "-");
- ++nc;
- }
- nc += fprintf(fp, "0.");
- switch (*(cntl_chr + j + 1)) {
- default:
- break;
-
- case '0': case 'z':
- ZeroSup = 0;
- ++j;
- sep = cntl_chr[j] == 'z' ? BIGDECIMAL_COMPONENT_FIGURES : 10;
- break;
- }
- for (i = 0; i < a->Prec; ++i) {
- m = BASE1;
- e = a->frac[i];
- while (m) {
- nn = e / m;
- if (!ZeroSup || nn) {
- nc += fprintf(fp, "%lu", (unsigned long)nn); /* The leading zero(s) */
- /* as 0.00xx will not */
- /* be printed. */
- ++nd;
- ZeroSup = 0; /* Set to print succeeding zeros */
- }
- if (nd >= sep) { /* print ' ' after every 10 digits */
- nd = 0;
- nc += fprintf(fp, " ");
- }
- e = e - nn * m;
- m /= 10;
- }
- }
- nc += fprintf(fp, "E%"PRIdSIZE, VpExponent10(a));
- nc += fprintf(fp, " (%"PRIdVALUE", %"PRIuSIZE", %"PRIuSIZE")", a->exponent, a->Prec, a->MaxPrec);
- }
- else {
- nc += fprintf(fp, "0.0");
- }
- }
- else {
- ++nc;
- if (*(cntl_chr + j) == '\\') {
- switch (*(cntl_chr + j + 1)) {
- case 'n':
- fprintf(fp, "\n");
- ++j;
- break;
- case 't':
- fprintf(fp, "\t");
- ++j;
- break;
- case 'b':
- fprintf(fp, "\n");
- ++j;
- break;
- default:
- fprintf(fp, "%c", *(cntl_chr + j));
- break;
- }
- }
- else {
- fprintf(fp, "%c", *(cntl_chr + j));
- if (*(cntl_chr + j) == '%') ++j;
- }
- }
- j++;
- }
-
- return (int)nc;
-}
-
-static void
-VpFormatSt(char *psz, size_t fFmt)
-{
- size_t ie, i, nf = 0;
- char ch;
-
- if (fFmt == 0) return;
-
- ie = strlen(psz);
- for (i = 0; i < ie; ++i) {
- ch = psz[i];
- if (!ch) break;
- if (ISSPACE(ch) || ch=='-' || ch=='+') continue;
- if (ch == '.') { nf = 0; continue; }
- if (ch == 'E' || ch == 'e') break;
-
- if (++nf > fFmt) {
- memmove(psz + i + 1, psz + i, ie - i + 1);
- ++ie;
- nf = 0;
- psz[i] = ' ';
- }
- }
-}
-
-VP_EXPORT ssize_t
-VpExponent10(Real *a)
-{
- ssize_t ex;
- size_t n;
-
- if (!VpHasVal(a)) return 0;
-
- ex = a->exponent * (ssize_t)BASE_FIG;
- n = BASE1;
- while ((a->frac[0] / n) == 0) {
- --ex;
- n /= 10;
- }
- return ex;
-}
-
-VP_EXPORT void
-VpSzMantissa(Real *a, char *buf, size_t buflen)
-{
- size_t i, n, ZeroSup;
- DECDIG_DBL m, e, nn;
-
- if (VpIsNaN(a)) {
- snprintf(buf, buflen, SZ_NaN);
- return;
- }
- if (VpIsPosInf(a)) {
- snprintf(buf, buflen, SZ_INF);
- return;
- }
- if (VpIsNegInf(a)) {
- snprintf(buf, buflen, SZ_NINF);
- return;
- }
-
- ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */
- if (!VpIsZero(a)) {
- if (BIGDECIMAL_NEGATIVE_P(a)) *buf++ = '-';
- n = a->Prec;
- for (i = 0; i < n; ++i) {
- m = BASE1;
- e = a->frac[i];
- while (m) {
- nn = e / m;
- if (!ZeroSup || nn) {
- snprintf(buf, buflen, "%lu", (unsigned long)nn); /* The leading zero(s) */
- buf += strlen(buf);
- /* as 0.00xx will be ignored. */
- ZeroSup = 0; /* Set to print succeeding zeros */
- }
- e = e - nn * m;
- m /= 10;
- }
- }
- *buf = 0;
- while (buf[-1] == '0') *(--buf) = 0;
- }
- else {
- if (VpIsPosZero(a)) snprintf(buf, buflen, "0");
- else snprintf(buf, buflen, "-0");
- }
-}
-
-VP_EXPORT int
-VpToSpecialString(Real *a, char *buf, size_t buflen, int fPlus)
-/* fPlus = 0: default, 1: set ' ' before digits, 2: set '+' before digits. */
-{
- if (VpIsNaN(a)) {
- snprintf(buf, buflen, SZ_NaN);
- return 1;
- }
-
- if (VpIsPosInf(a)) {
- if (fPlus == 1) {
- *buf++ = ' ';
- }
- else if (fPlus == 2) {
- *buf++ = '+';
- }
- snprintf(buf, buflen, SZ_INF);
- return 1;
- }
- if (VpIsNegInf(a)) {
- snprintf(buf, buflen, SZ_NINF);
- return 1;
- }
- if (VpIsZero(a)) {
- if (VpIsPosZero(a)) {
- if (fPlus == 1) snprintf(buf, buflen, " 0.0");
- else if (fPlus == 2) snprintf(buf, buflen, "+0.0");
- else snprintf(buf, buflen, "0.0");
- }
- else snprintf(buf, buflen, "-0.0");
- return 1;
- }
- return 0;
-}
-
-VP_EXPORT void
-VpToString(Real *a, char *buf, size_t buflen, size_t fFmt, int fPlus)
-/* fPlus = 0: default, 1: set ' ' before digits, 2: set '+' before digits. */
-{
- size_t i, n, ZeroSup;
- DECDIG shift, m, e, nn;
- char *p = buf;
- size_t plen = buflen;
- ssize_t ex;
-
- if (VpToSpecialString(a, buf, buflen, fPlus)) return;
-
- ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */
-
-#define ADVANCE(n) do { \
- if (plen < n) goto overflow; \
- p += n; \
- plen -= n; \
-} while (0)
-
- if (BIGDECIMAL_NEGATIVE_P(a)) {
- *p = '-';
- ADVANCE(1);
- }
- else if (fPlus == 1) {
- *p = ' ';
- ADVANCE(1);
- }
- else if (fPlus == 2) {
- *p = '+';
- ADVANCE(1);
- }
-
- *p = '0'; ADVANCE(1);
- *p = '.'; ADVANCE(1);
-
- n = a->Prec;
- for (i = 0; i < n; ++i) {
- m = BASE1;
- e = a->frac[i];
- while (m) {
- nn = e / m;
- if (!ZeroSup || nn) {
- /* The reading zero(s) */
- size_t n = (size_t)snprintf(p, plen, "%lu", (unsigned long)nn);
- if (n > plen) goto overflow;
- ADVANCE(n);
- /* as 0.00xx will be ignored. */
- ZeroSup = 0; /* Set to print succeeding zeros */
- }
- e = e - nn * m;
- m /= 10;
- }
- }
-
- ex = a->exponent * (ssize_t)BASE_FIG;
- shift = BASE1;
- while (a->frac[0] / shift == 0) {
- --ex;
- shift /= 10;
- }
- while (p - 1 > buf && p[-1] == '0') {
- *(--p) = '\0';
- ++plen;
- }
- snprintf(p, plen, "e%"PRIdSIZE, ex);
- if (fFmt) VpFormatSt(buf, fFmt);
-
- overflow:
- return;
-#undef ADVANCE
-}
-
-VP_EXPORT void
-VpToFString(Real *a, char *buf, size_t buflen, size_t fFmt, int fPlus)
-/* fPlus = 0: default, 1: set ' ' before digits, 2: set '+' before digits. */
-{
- size_t i, n;
- DECDIG m, e, nn;
- char *p = buf;
- size_t plen = buflen;
- ssize_t ex;
-
- if (VpToSpecialString(a, buf, buflen, fPlus)) return;
-
-#define ADVANCE(n) do { \
- if (plen < n) goto overflow; \
- p += n; \
- plen -= n; \
-} while (0)
-
-
- if (BIGDECIMAL_NEGATIVE_P(a)) {
- *p = '-';
- ADVANCE(1);
- }
- else if (fPlus == 1) {
- *p = ' ';
- ADVANCE(1);
- }
- else if (fPlus == 2) {
- *p = '+';
- ADVANCE(1);
- }
-
- n = a->Prec;
- ex = a->exponent;
- if (ex <= 0) {
- *p = '0'; ADVANCE(1);
- *p = '.'; ADVANCE(1);
- while (ex < 0) {
- for (i=0; i < BASE_FIG; ++i) {
- *p = '0'; ADVANCE(1);
- }
- ++ex;
- }
- ex = -1;
- }
-
- for (i = 0; i < n; ++i) {
- --ex;
- if (i == 0 && ex >= 0) {
- size_t n = snprintf(p, plen, "%lu", (unsigned long)a->frac[i]);
- if (n > plen) goto overflow;
- ADVANCE(n);
- }
- else {
- m = BASE1;
- e = a->frac[i];
- while (m) {
- nn = e / m;
- *p = (char)(nn + '0');
- ADVANCE(1);
- e = e - nn * m;
- m /= 10;
- }
- }
- if (ex == 0) {
- *p = '.';
- ADVANCE(1);
- }
- }
- while (--ex>=0) {
- m = BASE;
- while (m /= 10) {
- *p = '0';
- ADVANCE(1);
- }
- if (ex == 0) {
- *p = '.';
- ADVANCE(1);
- }
- }
-
- *p = '\0';
- while (p - 1 > buf && p[-1] == '0') {
- *(--p) = '\0';
- ++plen;
- }
- if (p - 1 > buf && p[-1] == '.') {
- snprintf(p, plen, "0");
- }
- if (fFmt) VpFormatSt(buf, fFmt);
-
- overflow:
- return;
-#undef ADVANCE
-}
-
-/*
- * [Output]
- * a[] ... variable to be assigned the value.
- * [Input]
- * int_chr[] ... integer part(may include '+/-').
- * ni ... number of characters in int_chr[],not including '+/-'.
- * frac[] ... fraction part.
- * nf ... number of characters in frac[].
- * exp_chr[] ... exponent part(including '+/-').
- * ne ... number of characters in exp_chr[],not including '+/-'.
- */
-VP_EXPORT int
-VpCtoV(Real *a, const char *int_chr, size_t ni, const char *frac, size_t nf, const char *exp_chr, size_t ne)
-{
- size_t i, j, ind_a, ma, mi, me;
- SIGNED_VALUE e, es, eb, ef;
- int sign, signe, exponent_overflow;
-
- /* get exponent part */
- e = 0;
- ma = a->MaxPrec;
- mi = ni;
- me = ne;
- signe = 1;
- exponent_overflow = 0;
- memset(a->frac, 0, ma * sizeof(DECDIG));
- if (ne > 0) {
- i = 0;
- if (exp_chr[0] == '-') {
- signe = -1;
- ++i;
- ++me;
- }
- else if (exp_chr[0] == '+') {
- ++i;
- ++me;
- }
- while (i < me) {
- if (MUL_OVERFLOW_SIGNED_VALUE_P(e, (SIGNED_VALUE)BASE_FIG)) {
- es = e;
- goto exp_overflow;
- }
- es = e * (SIGNED_VALUE)BASE_FIG;
- if (MUL_OVERFLOW_SIGNED_VALUE_P(e, 10) ||
- SIGNED_VALUE_MAX - (exp_chr[i] - '0') < e * 10)
- goto exp_overflow;
- e = e * 10 + exp_chr[i] - '0';
- if (MUL_OVERFLOW_SIGNED_VALUE_P(e, (SIGNED_VALUE)BASE_FIG))
- goto exp_overflow;
- if (es > (SIGNED_VALUE)(e * BASE_FIG)) {
- exp_overflow:
- exponent_overflow = 1;
- e = es; /* keep sign */
- break;
- }
- ++i;
- }
- }
-
- /* get integer part */
- i = 0;
- sign = 1;
- if (1 /*ni >= 0*/) {
- if (int_chr[0] == '-') {
- sign = -1;
- ++i;
- ++mi;
- }
- else if (int_chr[0] == '+') {
- ++i;
- ++mi;
- }
- }
-
- e = signe * e; /* e: The value of exponent part. */
- e = e + ni; /* set actual exponent size. */
-
- if (e > 0) signe = 1;
- else signe = -1;
-
- /* Adjust the exponent so that it is the multiple of BASE_FIG. */
- j = 0;
- ef = 1;
- while (ef) {
- if (e >= 0) eb = e;
- else eb = -e;
- ef = eb / (SIGNED_VALUE)BASE_FIG;
- ef = eb - ef * (SIGNED_VALUE)BASE_FIG;
- if (ef) {
- ++j; /* Means to add one more preceding zero */
- ++e;
- }
- }
-
- eb = e / (SIGNED_VALUE)BASE_FIG;
-
- if (exponent_overflow) {
- int zero = 1;
- for ( ; i < mi && zero; i++) zero = int_chr[i] == '0';
- for (i = 0; i < nf && zero; i++) zero = frac[i] == '0';
- if (!zero && signe > 0) {
- VpSetInf(a, sign);
- VpException(VP_EXCEPTION_INFINITY, "exponent overflow",0);
- }
- else VpSetZero(a, sign);
- return 1;
- }
-
- ind_a = 0;
- while (i < mi) {
- a->frac[ind_a] = 0;
- while (j < BASE_FIG && i < mi) {
- a->frac[ind_a] = a->frac[ind_a] * 10 + int_chr[i] - '0';
- ++j;
- ++i;
- }
- if (i < mi) {
- ++ind_a;
- if (ind_a >= ma) goto over_flow;
- j = 0;
- }
- }
-
- /* get fraction part */
-
- i = 0;
- while (i < nf) {
- while (j < BASE_FIG && i < nf) {
- a->frac[ind_a] = a->frac[ind_a] * 10 + frac[i] - '0';
- ++j;
- ++i;
- }
- if (i < nf) {
- ++ind_a;
- if (ind_a >= ma) goto over_flow;
- j = 0;
- }
- }
- goto Final;
-
-over_flow:
- rb_warn("Conversion from String to BigDecimal overflow (last few digits discarded).");
-
-Final:
- if (ind_a >= ma) ind_a = ma - 1;
- while (j < BASE_FIG) {
- a->frac[ind_a] = a->frac[ind_a] * 10;
- ++j;
- }
- a->Prec = ind_a + 1;
- a->exponent = eb;
- VpSetSign(a, sign);
- VpNmlz(a);
- return 1;
-}
-
-/*
- * [Input]
- * *m ... Real
- * [Output]
- * *d ... fraction part of m(d = 0.xxxxxxx). where # of 'x's is fig.
- * *e ... exponent of m.
- * BIGDECIMAL_DOUBLE_FIGURES ... Number of digits in a double variable.
- *
- * m -> d*10**e, 0<d<BASE
- * [Returns]
- * 0 ... Zero
- * 1 ... Normal
- * 2 ... Infinity
- * -1 ... NaN
- */
-VP_EXPORT int
-VpVtoD(double *d, SIGNED_VALUE *e, Real *m)
-{
- size_t ind_m, mm, fig;
- double div;
- int f = 1;
-
- if (VpIsNaN(m)) {
- *d = VpGetDoubleNaN();
- *e = 0;
- f = -1; /* NaN */
- goto Exit;
- }
- else if (VpIsPosZero(m)) {
- *d = 0.0;
- *e = 0;
- f = 0;
- goto Exit;
- }
- else if (VpIsNegZero(m)) {
- *d = VpGetDoubleNegZero();
- *e = 0;
- f = 0;
- goto Exit;
- }
- else if (VpIsPosInf(m)) {
- *d = VpGetDoublePosInf();
- *e = 0;
- f = 2;
- goto Exit;
- }
- else if (VpIsNegInf(m)) {
- *d = VpGetDoubleNegInf();
- *e = 0;
- f = 2;
- goto Exit;
- }
- /* Normal number */
- fig = roomof(BIGDECIMAL_DOUBLE_FIGURES, BASE_FIG);
- ind_m = 0;
- mm = Min(fig, m->Prec);
- *d = 0.0;
- div = 1.;
- while (ind_m < mm) {
- div /= (double)BASE;
- *d = *d + (double)m->frac[ind_m++] * div;
- }
- *e = m->exponent * (SIGNED_VALUE)BASE_FIG;
- *d *= VpGetSign(m);
-
-Exit:
-#ifdef BIGDECIMAL_DEBUG
- if (gfDebug) {
- VPrint(stdout, " VpVtoD: m=%\n", m);
- printf(" d=%e * 10 **%ld\n", *d, *e);
- printf(" BIGDECIMAL_DOUBLE_FIGURES = %d\n", BIGDECIMAL_DOUBLE_FIGURES);
- }
-#endif /*BIGDECIMAL_DEBUG */
- return f;
-}
-
-/*
- * m <- d
- */
-VP_EXPORT void
-VpDtoV(Real *m, double d)
-{
- size_t ind_m, mm;
- SIGNED_VALUE ne;
- DECDIG i;
- double val, val2;
-
- if (isnan(d)) {
- VpSetNaN(m);
- goto Exit;
- }
- if (isinf(d)) {
- if (d > 0.0) VpSetPosInf(m);
- else VpSetNegInf(m);
- goto Exit;
- }
-
- if (d == 0.0) {
- VpSetZero(m, 1);
- goto Exit;
- }
- val = (d > 0.) ? d : -d;
- ne = 0;
- if (val >= 1.0) {
- while (val >= 1.0) {
- val /= (double)BASE;
- ++ne;
- }
- }
- else {
- val2 = 1.0 / (double)BASE;
- while (val < val2) {
- val *= (double)BASE;
- --ne;
- }
- }
- /* Now val = 0.xxxxx*BASE**ne */
-
- mm = m->MaxPrec;
- memset(m->frac, 0, mm * sizeof(DECDIG));
- for (ind_m = 0; val > 0.0 && ind_m < mm; ind_m++) {
- val *= (double)BASE;
- i = (DECDIG)val;
- val -= (double)i;
- m->frac[ind_m] = i;
- }
- if (ind_m >= mm) ind_m = mm - 1;
- VpSetSign(m, (d > 0.0) ? 1 : -1);
- m->Prec = ind_m + 1;
- m->exponent = ne;
-
- VpInternalRound(m, 0, (m->Prec > 0) ? m->frac[m->Prec-1] : 0,
- (DECDIG)(val*(double)BASE));
-
-Exit:
-#ifdef BIGDECIMAL_DEBUG
- if (gfDebug) {
- printf("VpDtoV d=%30.30e\n", d);
- VPrint(stdout, " m=%\n", m);
- }
-#endif /* BIGDECIMAL_DEBUG */
- return;
-}
-
-/*
- * m <- ival
- */
-#if 0 /* unused */
-VP_EXPORT void
-VpItoV(Real *m, SIGNED_VALUE ival)
-{
- size_t mm, ind_m;
- size_t val, v1, v2, v;
- int isign;
- SIGNED_VALUE ne;
-
- if (ival == 0) {
- VpSetZero(m, 1);
- goto Exit;
- }
- isign = 1;
- val = ival;
- if (ival < 0) {
- isign = -1;
- val =(size_t)(-ival);
- }
- ne = 0;
- ind_m = 0;
- mm = m->MaxPrec;
- while (ind_m < mm) {
- m->frac[ind_m] = 0;
- ++ind_m;
- }
- ind_m = 0;
- while (val > 0) {
- if (val) {
- v1 = val;
- v2 = 1;
- while (v1 >= BASE) {
- v1 /= BASE;
- v2 *= BASE;
- }
- val = val - v2 * v1;
- v = v1;
- }
- else {
- v = 0;
- }
- m->frac[ind_m] = v;
- ++ind_m;
- ++ne;
- }
- m->Prec = ind_m - 1;
- m->exponent = ne;
- VpSetSign(m, isign);
- VpNmlz(m);
-
-Exit:
-#ifdef BIGDECIMAL_DEBUG
- if (gfDebug) {
- printf(" VpItoV i=%d\n", ival);
- VPrint(stdout, " m=%\n", m);
- }
-#endif /* BIGDECIMAL_DEBUG */
- return;
-}
-#endif
-
-/*
- * y = SQRT(x), y*y - x =>0
- */
-VP_EXPORT int
-VpSqrt(Real *y, Real *x)
-{
- Real *f = NULL;
- Real *r = NULL;
- size_t y_prec;
- SIGNED_VALUE n, e;
- ssize_t nr;
- double val;
-
- /* Zero or +Infinity ? */
- if (VpIsZero(x) || VpIsPosInf(x)) {
- VpAsgn(y,x,1);
- goto Exit;
- }
-
- /* Negative ? */
- if (BIGDECIMAL_NEGATIVE_P(x)) {
- VpSetNaN(y);
- return VpException(VP_EXCEPTION_OP, "sqrt of negative value", 0);
- }
-
- /* NaN ? */
- if (VpIsNaN(x)) {
- VpSetNaN(y);
- return VpException(VP_EXCEPTION_OP, "sqrt of 'NaN'(Not a Number)", 0);
- }
-
- /* One ? */
- if (VpIsOne(x)) {
- VpSetOne(y);
- goto Exit;
- }
-
- n = (SIGNED_VALUE)y->MaxPrec;
- if (x->MaxPrec > (size_t)n) n = (ssize_t)x->MaxPrec;
-
- /* allocate temporally variables */
- /* TODO: reconsider MaxPrec of f and r */
- f = NewOneNolimit(1, y->MaxPrec * (BASE_FIG + 2));
- r = NewOneNolimit(1, (n + n) * (BASE_FIG + 2));
-
- nr = 0;
- y_prec = y->MaxPrec;
-
- VpVtoD(&val, &e, x); /* val <- x */
- e /= (SIGNED_VALUE)BASE_FIG;
- n = e / 2;
- if (e - n * 2 != 0) {
- val /= BASE;
- n = (e + 1) / 2;
- }
- VpDtoV(y, sqrt(val)); /* y <- sqrt(val) */
- y->exponent += n;
- n = (SIGNED_VALUE)roomof(BIGDECIMAL_DOUBLE_FIGURES, BASE_FIG);
- y->MaxPrec = Min((size_t)n , y_prec);
- f->MaxPrec = y->MaxPrec + 1;
- n = (SIGNED_VALUE)(y_prec * BASE_FIG);
- if (n < (SIGNED_VALUE)maxnr) n = (SIGNED_VALUE)maxnr;
-
- /*
- * Perform: y_{n+1} = (y_n - x/y_n) / 2
- */
- do {
- y->MaxPrec *= 2;
- if (y->MaxPrec > y_prec) y->MaxPrec = y_prec;
- f->MaxPrec = y->MaxPrec;
- VpDivd(f, r, x, y); /* f = x/y */
- VpAddSub(r, f, y, -1); /* r = f - y */
- VpMult(f, VpConstPt5, r); /* f = 0.5*r */
- if (VpIsZero(f))
- goto converge;
- VpAddSub(r, f, y, 1); /* r = y + f */
- VpAsgn(y, r, 1); /* y = r */
- } while (++nr < n);
-
-#ifdef BIGDECIMAL_DEBUG
- if (gfDebug) {
- printf("ERROR(VpSqrt): did not converge within %ld iterations.\n", nr);
- }
-#endif /* BIGDECIMAL_DEBUG */
- y->MaxPrec = y_prec;
-
-converge:
- VpChangeSign(y, 1);
-#ifdef BIGDECIMAL_DEBUG
- if (gfDebug) {
- VpMult(r, y, y);
- VpAddSub(f, x, r, -1);
- printf("VpSqrt: iterations = %"PRIdSIZE"\n", nr);
- VPrint(stdout, " y =% \n", y);
- VPrint(stdout, " x =% \n", x);
- VPrint(stdout, " x-y*y = % \n", f);
- }
-#endif /* BIGDECIMAL_DEBUG */
- y->MaxPrec = y_prec;
-
-Exit:
- rbd_free_struct(f);
- rbd_free_struct(r);
- return 1;
-}
-
-/*
- * Round relatively from the decimal point.
- * f: rounding mode
- * nf: digit location to round from the decimal point.
- */
-VP_EXPORT int
-VpMidRound(Real *y, unsigned short f, ssize_t nf)
-{
- /* fracf: any positive digit under rounding position? */
- /* fracf_1further: any positive digits under one further than the rounding position? */
- /* exptoadd: number of digits needed to compensate negative nf */
- int fracf, fracf_1further;
- ssize_t n,i,ix,ioffset, exptoadd;
- DECDIG v, shifter;
- DECDIG div;
-
- nf += y->exponent * (ssize_t)BASE_FIG;
- exptoadd=0;
- if (nf < 0) {
- /* rounding position too left(large). */
- if (f != VP_ROUND_CEIL && f != VP_ROUND_FLOOR) {
- VpSetZero(y, VpGetSign(y)); /* truncate everything */
- return 0;
- }
- exptoadd = -nf;
- nf = 0;
- }
-
- ix = nf / (ssize_t)BASE_FIG;
- if ((size_t)ix >= y->Prec) return 0; /* rounding position too right(small). */
- v = y->frac[ix];
-
- ioffset = nf - ix*(ssize_t)BASE_FIG;
- n = (ssize_t)BASE_FIG - ioffset - 1;
- for (shifter = 1, i = 0; i < n; ++i) shifter *= 10;
-
- /* so the representation used (in y->frac) is an array of DECDIG, where
- each DECDIG contains a value between 0 and BASE-1, consisting of BASE_FIG
- decimal places.
-
- (that numbers of decimal places are typed as ssize_t is somewhat confusing)
-
- nf is now position (in decimal places) of the digit from the start of
- the array.
-
- ix is the position (in DECDIGs) of the DECDIG containing the decimal digit,
- from the start of the array.
-
- v is the value of this DECDIG
-
- ioffset is the number of extra decimal places along of this decimal digit
- within v.
-
- n is the number of decimal digits remaining within v after this decimal digit
- shifter is 10**n,
-
- v % shifter are the remaining digits within v
- v % (shifter * 10) are the digit together with the remaining digits within v
- v / shifter are the digit's predecessors together with the digit
- div = v / shifter / 10 is just the digit's precessors
- (v / shifter) - div*10 is just the digit, which is what v ends up being reassigned to.
- */
-
- fracf = (v % (shifter * 10) > 0);
- fracf_1further = ((v % shifter) > 0);
-
- v /= shifter;
- div = v / 10;
- v = v - div*10;
- /* now v is just the digit required.
- now fracf is whether the digit or any of the remaining digits within v are non-zero
- now fracf_1further is whether any of the remaining digits within v are non-zero
- */
-
- /* now check all the remaining DECDIGs for zero-ness a whole DECDIG at a time.
- if we spot any non-zeroness, that means that we found a positive digit under
- rounding position, and we also found a positive digit under one further than
- the rounding position, so both searches (to see if any such non-zero digit exists)
- can stop */
-
- for (i = ix + 1; (size_t)i < y->Prec; i++) {
- if (y->frac[i] % BASE) {
- fracf = fracf_1further = 1;
- break;
- }
- }
-
- /* now fracf = does any positive digit exist under the rounding position?
- now fracf_1further = does any positive digit exist under one further than the
- rounding position?
- now v = the first digit under the rounding position */
-
- /* drop digits after pointed digit */
- memset(y->frac + ix + 1, 0, (y->Prec - (ix + 1)) * sizeof(DECDIG));
-
- switch (f) {
- case VP_ROUND_DOWN: /* Truncate */
- break;
- case VP_ROUND_UP: /* Roundup */
- if (fracf) ++div;
- break;
- case VP_ROUND_HALF_UP:
- if (v>=5) ++div;
- break;
- case VP_ROUND_HALF_DOWN:
- if (v > 5 || (v == 5 && fracf_1further)) ++div;
- break;
- case VP_ROUND_CEIL:
- if (fracf && BIGDECIMAL_POSITIVE_P(y)) ++div;
- break;
- case VP_ROUND_FLOOR:
- if (fracf && BIGDECIMAL_NEGATIVE_P(y)) ++div;
- break;
- case VP_ROUND_HALF_EVEN: /* Banker's rounding */
- if (v > 5) ++div;
- else if (v == 5) {
- if (fracf_1further) {
- ++div;
- }
- else {
- if (ioffset == 0) {
- /* v is the first decimal digit of its DECDIG;
- need to grab the previous DECDIG if present
- to check for evenness of the previous decimal
- digit (which is same as that of the DECDIG since
- base 10 has a factor of 2) */
- if (ix && (y->frac[ix-1] % 2)) ++div;
- }
- else {
- if (div % 2) ++div;
- }
- }
- }
- break;
- }
- for (i = 0; i <= n; ++i) div *= 10;
- if (div >= BASE) {
- if (ix) {
- y->frac[ix] = 0;
- VpRdup(y, ix);
- }
- else {
- short s = VpGetSign(y);
- SIGNED_VALUE e = y->exponent;
- VpSetOne(y);
- VpSetSign(y, s);
- y->exponent = e + 1;
- }
- }
- else {
- y->frac[ix] = div;
- VpNmlz(y);
- }
- if (exptoadd > 0) {
- y->exponent += (SIGNED_VALUE)(exptoadd / BASE_FIG);
- exptoadd %= (ssize_t)BASE_FIG;
- for (i = 0; i < exptoadd; i++) {
- y->frac[0] *= 10;
- if (y->frac[0] >= BASE) {
- y->frac[0] /= BASE;
- y->exponent++;
- }
- }
- }
- return 1;
-}
-
-VP_EXPORT int
-VpLeftRound(Real *y, unsigned short f, ssize_t nf)
-/*
- * Round from the left hand side of the digits.
- */
-{
- DECDIG v;
- if (!VpHasVal(y)) return 0; /* Unable to round */
- v = y->frac[0];
- nf -= VpExponent(y) * (ssize_t)BASE_FIG;
- while ((v /= 10) != 0) nf--;
- nf += (ssize_t)BASE_FIG-1;
- return VpMidRound(y, f, nf);
-}
-
-VP_EXPORT int
-VpActiveRound(Real *y, Real *x, unsigned short f, ssize_t nf)
-{
- /* First,assign whole value in truncation mode */
- if (VpAsgn(y, x, 10) <= 1) return 0; /* Zero,NaN,or Infinity */
- return VpMidRound(y, f, nf);
-}
-
-static int
-VpLimitRound(Real *c, size_t ixDigit)
-{
- size_t ix = VpGetPrecLimit();
- if (!VpNmlz(c)) return -1;
- if (!ix) return 0;
- if (!ixDigit) ixDigit = c->Prec-1;
- if ((ix + BASE_FIG - 1) / BASE_FIG > ixDigit + 1) return 0;
- return VpLeftRound(c, VpGetRoundMode(), (ssize_t)ix);
-}
-
-/* If I understand correctly, this is only ever used to round off the final decimal
- digit of precision */
-static void
-VpInternalRound(Real *c, size_t ixDigit, DECDIG vPrev, DECDIG v)
-{
- int f = 0;
-
- unsigned short const rounding_mode = VpGetRoundMode();
-
- if (VpLimitRound(c, ixDigit)) return;
- if (!v) return;
-
- v /= BASE1;
- switch (rounding_mode) {
- case VP_ROUND_DOWN:
- break;
- case VP_ROUND_UP:
- if (v) f = 1;
- break;
- case VP_ROUND_HALF_UP:
- if (v >= 5) f = 1;
- break;
- case VP_ROUND_HALF_DOWN:
- /* this is ok - because this is the last digit of precision,
- the case where v == 5 and some further digits are nonzero
- will never occur */
- if (v >= 6) f = 1;
- break;
- case VP_ROUND_CEIL:
- if (v && BIGDECIMAL_POSITIVE_P(c)) f = 1;
- break;
- case VP_ROUND_FLOOR:
- if (v && BIGDECIMAL_NEGATIVE_P(c)) f = 1;
- break;
- case VP_ROUND_HALF_EVEN: /* Banker's rounding */
- /* as per VP_ROUND_HALF_DOWN, because this is the last digit of precision,
- there is no case to worry about where v == 5 and some further digits are nonzero */
- if (v > 5) f = 1;
- else if (v == 5 && vPrev % 2) f = 1;
- break;
- }
- if (f) {
- VpRdup(c, ixDigit);
- VpNmlz(c);
- }
-}
-
-/*
- * Rounds up m(plus one to final digit of m).
- */
-static int
-VpRdup(Real *m, size_t ind_m)
-{
- DECDIG carry;
-
- if (!ind_m) ind_m = m->Prec;
-
- carry = 1;
- while (carry > 0 && ind_m--) {
- m->frac[ind_m] += carry;
- if (m->frac[ind_m] >= BASE) m->frac[ind_m] -= BASE;
- else carry = 0;
- }
- if (carry > 0) { /* Overflow,count exponent and set fraction part be 1 */
- if (!AddExponent(m, 1)) return 0;
- m->Prec = m->frac[0] = 1;
- }
- else {
- VpNmlz(m);
- }
- return 1;
-}
-
-/*
- * y = x - fix(x)
- */
-VP_EXPORT void
-VpFrac(Real *y, Real *x)
-{
- size_t my, ind_y, ind_x;
-
- if (!VpHasVal(x)) {
- VpAsgn(y, x, 1);
- goto Exit;
- }
-
- if (x->exponent > 0 && (size_t)x->exponent >= x->Prec) {
- VpSetZero(y, VpGetSign(x));
- goto Exit;
- }
- else if (x->exponent <= 0) {
- VpAsgn(y, x, 1);
- goto Exit;
- }
-
- /* satisfy: x->exponent > 0 */
-
- y->Prec = x->Prec - (size_t)x->exponent;
- y->Prec = Min(y->Prec, y->MaxPrec);
- y->exponent = 0;
- VpSetSign(y, VpGetSign(x));
- ind_y = 0;
- my = y->Prec;
- ind_x = x->exponent;
- while (ind_y < my) {
- y->frac[ind_y] = x->frac[ind_x];
- ++ind_y;
- ++ind_x;
- }
- VpNmlz(y);
-
-Exit:
-#ifdef BIGDECIMAL_DEBUG
- if (gfDebug) {
- VPrint(stdout, "VpFrac y=%\n", y);
- VPrint(stdout, " x=%\n", x);
- }
-#endif /* BIGDECIMAL_DEBUG */
- return;
-}
-
-/*
- * y = x ** n
- */
-VP_EXPORT int
-VpPowerByInt(Real *y, Real *x, SIGNED_VALUE n)
-{
- size_t s, ss;
- ssize_t sign;
- Real *w1 = NULL;
- Real *w2 = NULL;
-
- if (VpIsZero(x)) {
- if (n == 0) {
- VpSetOne(y);
- goto Exit;
- }
- sign = VpGetSign(x);
- if (n < 0) {
- n = -n;
- if (sign < 0) sign = (n % 2) ? -1 : 1;
- VpSetInf(y, sign);
- }
- else {
- if (sign < 0) sign = (n % 2) ? -1 : 1;
- VpSetZero(y,sign);
- }
- goto Exit;
- }
- if (VpIsNaN(x)) {
- VpSetNaN(y);
- goto Exit;
- }
- if (VpIsInf(x)) {
- if (n == 0) {
- VpSetOne(y);
- goto Exit;
- }
- if (n > 0) {
- VpSetInf(y, (n % 2 == 0 || VpIsPosInf(x)) ? 1 : -1);
- goto Exit;
- }
- VpSetZero(y, (n % 2 == 0 || VpIsPosInf(x)) ? 1 : -1);
- goto Exit;
- }
-
- if (x->exponent == 1 && x->Prec == 1 && x->frac[0] == 1) {
- /* abs(x) = 1 */
- VpSetOne(y);
- if (BIGDECIMAL_POSITIVE_P(x)) goto Exit;
- if ((n % 2) == 0) goto Exit;
- VpSetSign(y, -1);
- goto Exit;
- }
-
- if (n > 0) sign = 1;
- else if (n < 0) {
- sign = -1;
- n = -n;
- }
- else {
- VpSetOne(y);
- goto Exit;
- }
-
- /* Allocate working variables */
- /* TODO: reconsider MaxPrec of w1 and w2 */
- w1 = NewZeroNolimit(1, (y->MaxPrec + 2) * BASE_FIG);
- w2 = NewZeroNolimit(1, (w1->MaxPrec * 2 + 1) * BASE_FIG);
-
- /* calculation start */
-
- VpAsgn(y, x, 1);
- --n;
- while (n > 0) {
- VpAsgn(w1, x, 1);
- s = 1;
- while (ss = s, (s += s) <= (size_t)n) {
- VpMult(w2, w1, w1);
- VpAsgn(w1, w2, 1);
- }
- n -= (SIGNED_VALUE)ss;
- VpMult(w2, y, w1);
- VpAsgn(y, w2, 1);
- }
- if (sign < 0) {
- VpDivd(w1, w2, VpConstOne, y);
- VpAsgn(y, w1, 1);
- }
-
-Exit:
-#ifdef BIGDECIMAL_DEBUG
- if (gfDebug) {
- VPrint(stdout, "VpPowerByInt y=%\n", y);
- VPrint(stdout, "VpPowerByInt x=%\n", x);
- printf(" n=%"PRIdVALUE"\n", n);
- }
-#endif /* BIGDECIMAL_DEBUG */
- rbd_free_struct(w2);
- rbd_free_struct(w1);
- return 1;
-}
-
-#ifdef BIGDECIMAL_DEBUG
-int
-VpVarCheck(Real * v)
-/*
- * Checks the validity of the Real variable v.
- * [Input]
- * v ... Real *, variable to be checked.
- * [Returns]
- * 0 ... correct v.
- * other ... error
- */
-{
- size_t i;
-
- if (v->MaxPrec == 0) {
- printf("ERROR(VpVarCheck): Illegal Max. Precision(=%"PRIuSIZE")\n",
- v->MaxPrec);
- return 1;
- }
- if (v->Prec == 0 || v->Prec > v->MaxPrec) {
- printf("ERROR(VpVarCheck): Illegal Precision(=%"PRIuSIZE")\n", v->Prec);
- printf(" Max. Prec.=%"PRIuSIZE"\n", v->MaxPrec);
- return 2;
- }
- for (i = 0; i < v->Prec; ++i) {
- if (v->frac[i] >= BASE) {
- printf("ERROR(VpVarCheck): Illegal fraction\n");
- printf(" Frac[%"PRIuSIZE"]=%"PRIuDECDIG"\n", i, v->frac[i]);
- printf(" Prec. =%"PRIuSIZE"\n", v->Prec);
- printf(" Exp. =%"PRIdVALUE"\n", v->exponent);
- printf(" BASE =%"PRIuDECDIG"\n", BASE);
- return 3;
- }
- }
- return 0;
-}
-#endif /* BIGDECIMAL_DEBUG */
diff --git a/ext/bigdecimal/bigdecimal.gemspec b/ext/bigdecimal/bigdecimal.gemspec
deleted file mode 100644
index f9f3b45616..0000000000
--- a/ext/bigdecimal/bigdecimal.gemspec
+++ /dev/null
@@ -1,54 +0,0 @@
-# coding: utf-8
-
-name = File.basename(__FILE__, '.*')
-source_version = ["", "ext/#{name}/"].find do |dir|
- begin
- break File.foreach(File.join(__dir__, "#{dir}#{name}.c")) {|line|
- break $1.sub("-", ".") if /^#define\s+#{name.upcase}_VERSION\s+"(.+)"/o =~ line
- }
- rescue Errno::ENOENT
- end
-end or raise "can't find #{name.upcase}_VERSION"
-
-Gem::Specification.new do |s|
- s.name = name
- s.version = source_version
- s.authors = ["Kenta Murata", "Zachary Scott", "Shigeo Kobayashi"]
- s.email = ["mrkn@mrkn.jp"]
-
- s.summary = "Arbitrary-precision decimal floating-point number library."
- s.description = "This library provides arbitrary-precision decimal floating-point number class."
- s.homepage = "https://github.com/ruby/bigdecimal"
- s.licenses = ["Ruby", "BSD-2-Clause"]
-
- s.require_paths = %w[lib]
- s.files = %w[
- bigdecimal.gemspec
- lib/bigdecimal.rb
- lib/bigdecimal/jacobian.rb
- lib/bigdecimal/ludcmp.rb
- lib/bigdecimal/math.rb
- lib/bigdecimal/newton.rb
- lib/bigdecimal/util.rb
- sample/linear.rb
- sample/nlsolve.rb
- sample/pi.rb
- ]
- if Gem::Platform === s.platform and s.platform =~ 'java' or RUBY_ENGINE == 'jruby'
- s.platform = 'java'
- else
- s.extensions = %w[ext/bigdecimal/extconf.rb]
- s.files += %w[
- ext/bigdecimal/bigdecimal.c
- ext/bigdecimal/bigdecimal.h
- ext/bigdecimal/bits.h
- ext/bigdecimal/feature.h
- ext/bigdecimal/missing.c
- ext/bigdecimal/missing.h
- ext/bigdecimal/missing/dtoa.c
- ext/bigdecimal/static_assert.h
- ]
- end
-
- s.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
-end
diff --git a/ext/bigdecimal/bigdecimal.h b/ext/bigdecimal/bigdecimal.h
deleted file mode 100644
index 54fed811fb..0000000000
--- a/ext/bigdecimal/bigdecimal.h
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- *
- * Ruby BigDecimal(Variable decimal precision) extension library.
- *
- * Copyright(C) 2002 by Shigeo Kobayashi(shigeo@tinyforest.gr.jp)
- *
- */
-
-#ifndef RUBY_BIG_DECIMAL_H
-#define RUBY_BIG_DECIMAL_H 1
-
-#define RUBY_NO_OLD_COMPATIBILITY
-#include "ruby/ruby.h"
-#include "missing.h"
-
-#ifdef HAVE_FLOAT_H
-# include <float.h>
-#endif
-
-#ifdef HAVE_INT64_T
-# define DECDIG uint32_t
-# define DECDIG_DBL uint64_t
-# define DECDIG_DBL_SIGNED int64_t
-# define SIZEOF_DECDIG 4
-# define PRI_DECDIG_PREFIX ""
-# ifdef PRI_LL_PREFIX
-# define PRI_DECDIG_DBL_PREFIX PRI_LL_PREFIX
-# else
-# define PRI_DECDIG_DBL_PREFIX "l"
-# endif
-#else
-# define DECDIG uint16_t
-# define DECDIG_DBL uint32_t
-# define DECDIG_DBL_SIGNED int32_t
-# define SIZEOF_DECDIG 2
-# define PRI_DECDIG_PREFIX "h"
-# define PRI_DECDIG_DBL_PREFIX ""
-#endif
-
-#define PRIdDECDIG PRI_DECDIG_PREFIX"d"
-#define PRIiDECDIG PRI_DECDIG_PREFIX"i"
-#define PRIoDECDIG PRI_DECDIG_PREFIX"o"
-#define PRIuDECDIG PRI_DECDIG_PREFIX"u"
-#define PRIxDECDIG PRI_DECDIG_PREFIX"x"
-#define PRIXDECDIG PRI_DECDIG_PREFIX"X"
-
-#define PRIdDECDIG_DBL PRI_DECDIG_DBL_PREFIX"d"
-#define PRIiDECDIG_DBL PRI_DECDIG_DBL_PREFIX"i"
-#define PRIoDECDIG_DBL PRI_DECDIG_DBL_PREFIX"o"
-#define PRIuDECDIG_DBL PRI_DECDIG_DBL_PREFIX"u"
-#define PRIxDECDIG_DBL PRI_DECDIG_DBL_PREFIX"x"
-#define PRIXDECDIG_DBL PRI_DECDIG_DBL_PREFIX"X"
-
-#if SIZEOF_DECDIG == 4
-# define BIGDECIMAL_BASE ((DECDIG)1000000000U)
-# define BIGDECIMAL_COMPONENT_FIGURES 9
-/*
- * The number of components required for a 64-bit integer.
- *
- * INT64_MAX: 9_223372036_854775807
- * UINT64_MAX: 18_446744073_709551615
- */
-# define BIGDECIMAL_INT64_MAX_LENGTH 3
-
-#elif SIZEOF_DECDIG == 2
-# define BIGDECIMAL_BASE ((DECDIG)10000U)
-# define BIGDECIMAL_COMPONENT_FIGURES 4
-/*
- * The number of components required for a 64-bit integer.
- *
- * INT64_MAX: 922_3372_0368_5477_5807
- * UINT64_MAX: 1844_6744_0737_0955_1615
- */
-# define BIGDECIMAL_INT64_MAX_LENGTH 5
-
-#else
-# error Unknown size of DECDIG
-#endif
-
-#define BIGDECIMAL_DOUBLE_FIGURES (1+DBL_DIG)
-
-#if defined(__cplusplus)
-extern "C" {
-#if 0
-} /* satisfy cc-mode */
-#endif
-#endif
-
-extern VALUE rb_cBigDecimal;
-
-/*
- * NaN & Infinity
- */
-#define SZ_NaN "NaN"
-#define SZ_INF "Infinity"
-#define SZ_PINF "+Infinity"
-#define SZ_NINF "-Infinity"
-
-/*
- * #define VP_EXPORT other than static to let VP_ routines
- * be called from outside of this module.
- */
-#define VP_EXPORT static
-
-/* Exception mode */
-#define VP_EXCEPTION_ALL ((unsigned short)0x00FF)
-#define VP_EXCEPTION_INFINITY ((unsigned short)0x0001)
-#define VP_EXCEPTION_NaN ((unsigned short)0x0002)
-#define VP_EXCEPTION_UNDERFLOW ((unsigned short)0x0004)
-#define VP_EXCEPTION_OVERFLOW ((unsigned short)0x0001) /* 0x0008) */
-#define VP_EXCEPTION_ZERODIVIDE ((unsigned short)0x0010)
-
-/* Following 2 exceptions can't controlled by user */
-#define VP_EXCEPTION_OP ((unsigned short)0x0020)
-
-#define BIGDECIMAL_EXCEPTION_MODE_DEFAULT 0U
-
-/* This is used in BigDecimal#mode */
-#define VP_ROUND_MODE ((unsigned short)0x0100)
-
-/* Rounding mode */
-#define VP_ROUND_UP RBD_ROUND_UP
-#define VP_ROUND_DOWN RBD_ROUND_DOWN
-#define VP_ROUND_HALF_UP RBD_ROUND_HALF_UP
-#define VP_ROUND_HALF_DOWN RBD_ROUND_HALF_DOWN
-#define VP_ROUND_CEIL RBD_ROUND_CEIL
-#define VP_ROUND_FLOOR RBD_ROUND_FLOOR
-#define VP_ROUND_HALF_EVEN RBD_ROUND_HALF_EVEN
-
-enum rbd_rounding_mode {
- RBD_ROUND_UP = 1,
- RBD_ROUND_DOWN = 2,
- RBD_ROUND_HALF_UP = 3,
- RBD_ROUND_HALF_DOWN = 4,
- RBD_ROUND_CEIL = 5,
- RBD_ROUND_FLOOR = 6,
- RBD_ROUND_HALF_EVEN = 7,
-
- RBD_ROUND_DEFAULT = RBD_ROUND_HALF_UP,
- RBD_ROUND_TRUNCATE = RBD_ROUND_DOWN,
- RBD_ROUND_BANKER = RBD_ROUND_HALF_EVEN,
- RBD_ROUND_CEILING = RBD_ROUND_CEIL
-};
-
-#define BIGDECIMAL_ROUNDING_MODE_DEFAULT VP_ROUND_HALF_UP
-
-/* Sign flag */
-#define VP_SIGN_NaN 0 /* NaN */
-#define VP_SIGN_POSITIVE_ZERO 1 /* Positive zero */
-#define VP_SIGN_NEGATIVE_ZERO -1 /* Negative zero */
-#define VP_SIGN_POSITIVE_FINITE 2 /* Positive finite number */
-#define VP_SIGN_NEGATIVE_FINITE -2 /* Negative finite number */
-#define VP_SIGN_POSITIVE_INFINITE 3 /* Positive infinite number */
-#define VP_SIGN_NEGATIVE_INFINITE -3 /* Negative infinite number */
-
-/* The size of fraction part array */
-#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
-#define FLEXIBLE_ARRAY_SIZE /* */
-#elif defined(__GNUC__) && !defined(__STRICT_ANSI__)
-#define FLEXIBLE_ARRAY_SIZE 0
-#else
-#define FLEXIBLE_ARRAY_SIZE 1
-#endif
-
-/*
- * VP representation
- * r = 0.xxxxxxxxx *BASE**exponent
- */
-typedef struct {
- VALUE obj; /* Back pointer(VALUE) for Ruby object. */
- size_t MaxPrec; /* Maximum precision size */
- /* This is the actual size of frac[] */
- /*(frac[0] to frac[MaxPrec] are available). */
- size_t Prec; /* Current precision size. */
- /* This indicates how much the */
- /* array frac[] is actually used. */
- SIGNED_VALUE exponent; /* Exponent part. */
- short sign; /* Attributes of the value. */
- /*
- * ==0 : NaN
- * 1 : Positive zero
- * -1 : Negative zero
- * 2 : Positive number
- * -2 : Negative number
- * 3 : Positive infinite number
- * -3 : Negative infinite number
- */
- short flag; /* Not used in vp_routines,space for user. */
- DECDIG frac[FLEXIBLE_ARRAY_SIZE]; /* Array of fraction part. */
-} Real;
-
-/*
- * ------------------
- * EXPORTables.
- * ------------------
- */
-
-VP_EXPORT Real *VpNewRbClass(size_t mx, char const *str, VALUE klass, bool strict_p, bool raise_exception);
-
-VP_EXPORT Real *VpCreateRbObject(size_t mx, const char *str, bool raise_exception);
-
-#define VpBaseFig() BIGDECIMAL_COMPONENT_FIGURES
-#define VpDblFig() BIGDECIMAL_DOUBLE_FIGURES
-#define VpBaseVal() BIGDECIMAL_BASE
-
-/* Zero,Inf,NaN (isinf(),isnan() used to check) */
-VP_EXPORT double VpGetDoubleNaN(void);
-VP_EXPORT double VpGetDoublePosInf(void);
-VP_EXPORT double VpGetDoubleNegInf(void);
-VP_EXPORT double VpGetDoubleNegZero(void);
-
-/* These 2 functions added at v1.1.7 */
-VP_EXPORT size_t VpGetPrecLimit(void);
-VP_EXPORT size_t VpSetPrecLimit(size_t n);
-
-/* Round mode */
-VP_EXPORT int VpIsRoundMode(unsigned short n);
-VP_EXPORT unsigned short VpGetRoundMode(void);
-VP_EXPORT unsigned short VpSetRoundMode(unsigned short n);
-
-VP_EXPORT int VpException(unsigned short f,const char *str,int always);
-#if 0 /* unused */
-VP_EXPORT int VpIsNegDoubleZero(double v);
-#endif
-VP_EXPORT size_t VpNumOfChars(Real *vp,const char *pszFmt);
-VP_EXPORT size_t VpInit(DECDIG BaseVal);
-VP_EXPORT Real *VpAlloc(size_t mx, const char *szVal, int strict_p, int exc);
-VP_EXPORT size_t VpAsgn(Real *c, Real *a, int isw);
-VP_EXPORT size_t VpAddSub(Real *c,Real *a,Real *b,int operation);
-VP_EXPORT size_t VpMult(Real *c,Real *a,Real *b);
-VP_EXPORT size_t VpDivd(Real *c,Real *r,Real *a,Real *b);
-VP_EXPORT int VpComp(Real *a,Real *b);
-VP_EXPORT ssize_t VpExponent10(Real *a);
-VP_EXPORT void VpSzMantissa(Real *a, char *buf, size_t bufsize);
-VP_EXPORT int VpToSpecialString(Real *a, char *buf, size_t bufsize, int fPlus);
-VP_EXPORT void VpToString(Real *a, char *buf, size_t bufsize, size_t fFmt, int fPlus);
-VP_EXPORT void VpToFString(Real *a, char *buf, size_t bufsize, size_t fFmt, int fPlus);
-VP_EXPORT int VpCtoV(Real *a, const char *int_chr, size_t ni, const char *frac, size_t nf, const char *exp_chr, size_t ne);
-VP_EXPORT int VpVtoD(double *d, SIGNED_VALUE *e, Real *m);
-VP_EXPORT void VpDtoV(Real *m,double d);
-#if 0 /* unused */
-VP_EXPORT void VpItoV(Real *m,S_INT ival);
-#endif
-VP_EXPORT int VpSqrt(Real *y,Real *x);
-VP_EXPORT int VpActiveRound(Real *y, Real *x, unsigned short f, ssize_t il);
-VP_EXPORT int VpMidRound(Real *y, unsigned short f, ssize_t nf);
-VP_EXPORT int VpLeftRound(Real *y, unsigned short f, ssize_t nf);
-VP_EXPORT void VpFrac(Real *y, Real *x);
-VP_EXPORT int VpPowerByInt(Real *y, Real *x, SIGNED_VALUE n);
-#define VpPower VpPowerByInt
-
-/* VP constants */
-VP_EXPORT Real *VpOne(void);
-
-/*
- * ------------------
- * MACRO definitions.
- * ------------------
- */
-#define Abs(a) (((a)>= 0)?(a):(-(a)))
-#define Max(a, b) (((a)>(b))?(a):(b))
-#define Min(a, b) (((a)>(b))?(b):(a))
-
-#define VpMaxPrec(a) ((a)->MaxPrec)
-#define VpPrec(a) ((a)->Prec)
-#define VpGetFlag(a) ((a)->flag)
-
-/* Sign */
-
-/* VpGetSign(a) returns 1,-1 if a>0,a<0 respectively */
-#define VpGetSign(a) (((a)->sign>0)?1:(-1))
-/* Change sign of a to a>0,a<0 if s = 1,-1 respectively */
-#define VpChangeSign(a,s) {if((s)>0) (a)->sign=(short)Abs((ssize_t)(a)->sign);else (a)->sign=-(short)Abs((ssize_t)(a)->sign);}
-/* Sets sign of a to a>0,a<0 if s = 1,-1 respectively */
-#define VpSetSign(a,s) {if((s)>0) (a)->sign=(short)VP_SIGN_POSITIVE_FINITE;else (a)->sign=(short)VP_SIGN_NEGATIVE_FINITE;}
-
-/* 1 */
-#define VpSetOne(a) {(a)->Prec=(a)->exponent=(a)->frac[0]=1;(a)->sign=VP_SIGN_POSITIVE_FINITE;}
-
-/* ZEROs */
-#define VpIsPosZero(a) ((a)->sign==VP_SIGN_POSITIVE_ZERO)
-#define VpIsNegZero(a) ((a)->sign==VP_SIGN_NEGATIVE_ZERO)
-#define VpIsZero(a) (VpIsPosZero(a) || VpIsNegZero(a))
-#define VpSetPosZero(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->sign=VP_SIGN_POSITIVE_ZERO)
-#define VpSetNegZero(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->sign=VP_SIGN_NEGATIVE_ZERO)
-#define VpSetZero(a,s) (void)(((s)>0)?VpSetPosZero(a):VpSetNegZero(a))
-
-/* NaN */
-#define VpIsNaN(a) ((a)->sign==VP_SIGN_NaN)
-#define VpSetNaN(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->sign=VP_SIGN_NaN)
-
-/* Infinity */
-#define VpIsPosInf(a) ((a)->sign==VP_SIGN_POSITIVE_INFINITE)
-#define VpIsNegInf(a) ((a)->sign==VP_SIGN_NEGATIVE_INFINITE)
-#define VpIsInf(a) (VpIsPosInf(a) || VpIsNegInf(a))
-#define VpIsDef(a) ( !(VpIsNaN(a)||VpIsInf(a)) )
-#define VpSetPosInf(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->sign=VP_SIGN_POSITIVE_INFINITE)
-#define VpSetNegInf(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->sign=VP_SIGN_NEGATIVE_INFINITE)
-#define VpSetInf(a,s) (void)(((s)>0)?VpSetPosInf(a):VpSetNegInf(a))
-#define VpHasVal(a) (a->frac[0])
-#define VpIsOne(a) ((a->Prec==1)&&(a->frac[0]==1)&&(a->exponent==1))
-#define VpExponent(a) (a->exponent)
-#ifdef BIGDECIMAL_DEBUG
-int VpVarCheck(Real * v);
-#endif /* BIGDECIMAL_DEBUG */
-
-#if defined(__cplusplus)
-#if 0
-{ /* satisfy cc-mode */
-#endif
-} /* extern "C" { */
-#endif
-#endif /* RUBY_BIG_DECIMAL_H */
diff --git a/ext/bigdecimal/bits.h b/ext/bigdecimal/bits.h
deleted file mode 100644
index 6e1e4776e3..0000000000
--- a/ext/bigdecimal/bits.h
+++ /dev/null
@@ -1,141 +0,0 @@
-#ifndef BIGDECIMAL_BITS_H
-#define BIGDECIMAL_BITS_H
-
-#include "feature.h"
-#include "static_assert.h"
-
-#if defined(__x86_64__) && defined(HAVE_X86INTRIN_H)
-# include <x86intrin.h> /* for _lzcnt_u64, etc. */
-#elif defined(_MSC_VER) && defined(HAVE_INTRIN_H)
-# include <intrin.h> /* for the following intrinsics */
-#endif
-
-#if defined(_MSC_VER) && defined(__AVX2__)
-# pragma intrinsic(__lzcnt)
-# pragma intrinsic(__lzcnt64)
-#endif
-
-#define numberof(array) ((int)(sizeof(array) / sizeof((array)[0])))
-#define roomof(x, y) (((x) + (y) - 1) / (y))
-#define type_roomof(x, y) roomof(sizeof(x), sizeof(y))
-
-#define MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, min, max) ( \
- (a) == 0 ? 0 : \
- (a) == -1 ? (b) < -(max) : \
- (a) > 0 ? \
- ((b) > 0 ? (max) / (a) < (b) : (min) / (a) > (b)) : \
- ((b) > 0 ? (min) / (a) < (b) : (max) / (a) > (b)))
-
-#ifdef HAVE_UINT128_T
-# define bit_length(x) \
- (unsigned int) \
- (sizeof(x) <= sizeof(int32_t) ? 32 - nlz_int32((uint32_t)(x)) : \
- sizeof(x) <= sizeof(int64_t) ? 64 - nlz_int64((uint64_t)(x)) : \
- 128 - nlz_int128((uint128_t)(x)))
-#else
-# define bit_length(x) \
- (unsigned int) \
- (sizeof(x) <= sizeof(int32_t) ? 32 - nlz_int32((uint32_t)(x)) : \
- 64 - nlz_int64((uint64_t)(x)))
-#endif
-
-static inline unsigned nlz_int32(uint32_t x);
-static inline unsigned nlz_int64(uint64_t x);
-#ifdef HAVE_UINT128_T
-static inline unsigned nlz_int128(uint128_t x);
-#endif
-
-static inline unsigned int
-nlz_int32(uint32_t x)
-{
-#if defined(_MSC_VER) && defined(__AVX2__) && defined(HAVE___LZCNT)
- /* Note: It seems there is no such thing like __LZCNT__ predefined in MSVC.
- * AMD CPUs have had this instruction for decades (since K10) but for
- * Intel, Haswell is the oldest one. We need to use __AVX2__ for maximum
- * safety. */
- return (unsigned int)__lzcnt(x);
-
-#elif defined(__x86_64__) && defined(__LZCNT__) && defined(HAVE__LZCNT_U32)
- return (unsigned int)_lzcnt_u32(x);
-
-#elif defined(_MSC_VER) && defined(HAVE__BITSCANREVERSE)
- unsigned long r;
- return _BitScanReverse(&r, x) ? (31 - (int)r) : 32;
-
-#elif __has_builtin(__builtin_clz)
- STATIC_ASSERT(sizeof_int, sizeof(int) * CHAR_BIT == 32);
- return x ? (unsigned int)__builtin_clz(x) : 32;
-
-#else
- uint32_t y;
- unsigned n = 32;
- y = x >> 16; if (y) {n -= 16; x = y;}
- y = x >> 8; if (y) {n -= 8; x = y;}
- y = x >> 4; if (y) {n -= 4; x = y;}
- y = x >> 2; if (y) {n -= 2; x = y;}
- y = x >> 1; if (y) {return n - 2;}
- return (unsigned int)(n - x);
-#endif
-}
-
-static inline unsigned int
-nlz_int64(uint64_t x)
-{
-#if defined(_MSC_VER) && defined(__AVX2__) && defined(HAVE___LZCNT64)
- return (unsigned int)__lzcnt64(x);
-
-#elif defined(__x86_64__) && defined(__LZCNT__) && defined(HAVE__LZCNT_U64)
- return (unsigned int)_lzcnt_u64(x);
-
-#elif defined(_WIN64) && defined(_MSC_VER) && defined(HAVE__BITSCANREVERSE64)
- unsigned long r;
- return _BitScanReverse64(&r, x) ? (63u - (unsigned int)r) : 64;
-
-#elif __has_builtin(__builtin_clzl) && __has_builtin(__builtin_clzll) && !(defined(__sun) && defined(__sparc))
- if (x == 0) {
- return 64;
- }
- else if (sizeof(long) * CHAR_BIT == 64) {
- return (unsigned int)__builtin_clzl((unsigned long)x);
- }
- else if (sizeof(long long) * CHAR_BIT == 64) {
- return (unsigned int)__builtin_clzll((unsigned long long)x);
- }
- else {
- /* :FIXME: Is there a way to make this branch a compile-time error? */
- __builtin_unreachable();
- }
-
-#else
- uint64_t y;
- unsigned int n = 64;
- y = x >> 32; if (y) {n -= 32; x = y;}
- y = x >> 16; if (y) {n -= 16; x = y;}
- y = x >> 8; if (y) {n -= 8; x = y;}
- y = x >> 4; if (y) {n -= 4; x = y;}
- y = x >> 2; if (y) {n -= 2; x = y;}
- y = x >> 1; if (y) {return n - 2;}
- return (unsigned int)(n - x);
-
-#endif
-}
-
-#ifdef HAVE_UINT128_T
-static inline unsigned int
-nlz_int128(uint128_t x)
-{
- uint64_t y = (uint64_t)(x >> 64);
-
- if (x == 0) {
- return 128;
- }
- else if (y == 0) {
- return (unsigned int)nlz_int64(x) + 64;
- }
- else {
- return (unsigned int)nlz_int64(y);
- }
-}
-#endif
-
-#endif /* BIGDECIMAL_BITS_H */
diff --git a/ext/bigdecimal/depend b/ext/bigdecimal/depend
deleted file mode 100644
index a2455ebbda..0000000000
--- a/ext/bigdecimal/depend
+++ /dev/null
@@ -1,327 +0,0 @@
-Makefile: $(BIGDECIMAL_RB)
-
-# AUTOGENERATED DEPENDENCIES START
-bigdecimal.o: $(RUBY_EXTCONF_H)
-bigdecimal.o: $(arch_hdrdir)/ruby/config.h
-bigdecimal.o: $(hdrdir)/ruby/assert.h
-bigdecimal.o: $(hdrdir)/ruby/backward/2/assume.h
-bigdecimal.o: $(hdrdir)/ruby/backward/2/attributes.h
-bigdecimal.o: $(hdrdir)/ruby/backward/2/bool.h
-bigdecimal.o: $(hdrdir)/ruby/backward/2/inttypes.h
-bigdecimal.o: $(hdrdir)/ruby/backward/2/limits.h
-bigdecimal.o: $(hdrdir)/ruby/backward/2/long_long.h
-bigdecimal.o: $(hdrdir)/ruby/backward/2/stdalign.h
-bigdecimal.o: $(hdrdir)/ruby/backward/2/stdarg.h
-bigdecimal.o: $(hdrdir)/ruby/defines.h
-bigdecimal.o: $(hdrdir)/ruby/intern.h
-bigdecimal.o: $(hdrdir)/ruby/internal/abi.h
-bigdecimal.o: $(hdrdir)/ruby/internal/anyargs.h
-bigdecimal.o: $(hdrdir)/ruby/internal/arithmetic.h
-bigdecimal.o: $(hdrdir)/ruby/internal/arithmetic/char.h
-bigdecimal.o: $(hdrdir)/ruby/internal/arithmetic/double.h
-bigdecimal.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h
-bigdecimal.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h
-bigdecimal.o: $(hdrdir)/ruby/internal/arithmetic/int.h
-bigdecimal.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h
-bigdecimal.o: $(hdrdir)/ruby/internal/arithmetic/long.h
-bigdecimal.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h
-bigdecimal.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h
-bigdecimal.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h
-bigdecimal.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h
-bigdecimal.o: $(hdrdir)/ruby/internal/arithmetic/short.h
-bigdecimal.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h
-bigdecimal.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h
-bigdecimal.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h
-bigdecimal.o: $(hdrdir)/ruby/internal/assume.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/alloc_size.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/artificial.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/cold.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/const.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/constexpr.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/deprecated.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/error.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/flag_enum.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/forceinline.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/format.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/noalias.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/nodiscard.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/noexcept.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/noinline.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/nonnull.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/noreturn.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/packed_struct.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/pure.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/restrict.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/warning.h
-bigdecimal.o: $(hdrdir)/ruby/internal/attr/weakref.h
-bigdecimal.o: $(hdrdir)/ruby/internal/cast.h
-bigdecimal.o: $(hdrdir)/ruby/internal/compiler_is.h
-bigdecimal.o: $(hdrdir)/ruby/internal/compiler_is/apple.h
-bigdecimal.o: $(hdrdir)/ruby/internal/compiler_is/clang.h
-bigdecimal.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h
-bigdecimal.o: $(hdrdir)/ruby/internal/compiler_is/intel.h
-bigdecimal.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h
-bigdecimal.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h
-bigdecimal.o: $(hdrdir)/ruby/internal/compiler_since.h
-bigdecimal.o: $(hdrdir)/ruby/internal/config.h
-bigdecimal.o: $(hdrdir)/ruby/internal/constant_p.h
-bigdecimal.o: $(hdrdir)/ruby/internal/core.h
-bigdecimal.o: $(hdrdir)/ruby/internal/core/rarray.h
-bigdecimal.o: $(hdrdir)/ruby/internal/core/rbasic.h
-bigdecimal.o: $(hdrdir)/ruby/internal/core/rbignum.h
-bigdecimal.o: $(hdrdir)/ruby/internal/core/rclass.h
-bigdecimal.o: $(hdrdir)/ruby/internal/core/rdata.h
-bigdecimal.o: $(hdrdir)/ruby/internal/core/rfile.h
-bigdecimal.o: $(hdrdir)/ruby/internal/core/rhash.h
-bigdecimal.o: $(hdrdir)/ruby/internal/core/robject.h
-bigdecimal.o: $(hdrdir)/ruby/internal/core/rregexp.h
-bigdecimal.o: $(hdrdir)/ruby/internal/core/rstring.h
-bigdecimal.o: $(hdrdir)/ruby/internal/core/rstruct.h
-bigdecimal.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
-bigdecimal.o: $(hdrdir)/ruby/internal/ctype.h
-bigdecimal.o: $(hdrdir)/ruby/internal/dllexport.h
-bigdecimal.o: $(hdrdir)/ruby/internal/dosish.h
-bigdecimal.o: $(hdrdir)/ruby/internal/error.h
-bigdecimal.o: $(hdrdir)/ruby/internal/eval.h
-bigdecimal.o: $(hdrdir)/ruby/internal/event.h
-bigdecimal.o: $(hdrdir)/ruby/internal/fl_type.h
-bigdecimal.o: $(hdrdir)/ruby/internal/gc.h
-bigdecimal.o: $(hdrdir)/ruby/internal/glob.h
-bigdecimal.o: $(hdrdir)/ruby/internal/globals.h
-bigdecimal.o: $(hdrdir)/ruby/internal/has/attribute.h
-bigdecimal.o: $(hdrdir)/ruby/internal/has/builtin.h
-bigdecimal.o: $(hdrdir)/ruby/internal/has/c_attribute.h
-bigdecimal.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h
-bigdecimal.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h
-bigdecimal.o: $(hdrdir)/ruby/internal/has/extension.h
-bigdecimal.o: $(hdrdir)/ruby/internal/has/feature.h
-bigdecimal.o: $(hdrdir)/ruby/internal/has/warning.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/array.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/bignum.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/class.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/compar.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/complex.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/cont.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/dir.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/enum.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/enumerator.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/error.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/eval.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/file.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/hash.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/io.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/load.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/marshal.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/numeric.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/object.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/parse.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/proc.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/process.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/random.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/range.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/rational.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/re.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/ruby.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/select.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/select/largesize.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/signal.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/sprintf.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/string.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/struct.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/thread.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/time.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/variable.h
-bigdecimal.o: $(hdrdir)/ruby/internal/intern/vm.h
-bigdecimal.o: $(hdrdir)/ruby/internal/interpreter.h
-bigdecimal.o: $(hdrdir)/ruby/internal/iterator.h
-bigdecimal.o: $(hdrdir)/ruby/internal/memory.h
-bigdecimal.o: $(hdrdir)/ruby/internal/method.h
-bigdecimal.o: $(hdrdir)/ruby/internal/module.h
-bigdecimal.o: $(hdrdir)/ruby/internal/newobj.h
-bigdecimal.o: $(hdrdir)/ruby/internal/scan_args.h
-bigdecimal.o: $(hdrdir)/ruby/internal/special_consts.h
-bigdecimal.o: $(hdrdir)/ruby/internal/static_assert.h
-bigdecimal.o: $(hdrdir)/ruby/internal/stdalign.h
-bigdecimal.o: $(hdrdir)/ruby/internal/stdbool.h
-bigdecimal.o: $(hdrdir)/ruby/internal/symbol.h
-bigdecimal.o: $(hdrdir)/ruby/internal/value.h
-bigdecimal.o: $(hdrdir)/ruby/internal/value_type.h
-bigdecimal.o: $(hdrdir)/ruby/internal/variable.h
-bigdecimal.o: $(hdrdir)/ruby/internal/warning_push.h
-bigdecimal.o: $(hdrdir)/ruby/internal/xmalloc.h
-bigdecimal.o: $(hdrdir)/ruby/missing.h
-bigdecimal.o: $(hdrdir)/ruby/ruby.h
-bigdecimal.o: $(hdrdir)/ruby/st.h
-bigdecimal.o: $(hdrdir)/ruby/subst.h
-bigdecimal.o: $(hdrdir)/ruby/util.h
-bigdecimal.o: bigdecimal.c
-bigdecimal.o: bigdecimal.h
-bigdecimal.o: bits.h
-bigdecimal.o: feature.h
-bigdecimal.o: missing.h
-bigdecimal.o: static_assert.h
-missing.o: $(RUBY_EXTCONF_H)
-missing.o: $(arch_hdrdir)/ruby/config.h
-missing.o: $(hdrdir)/ruby/assert.h
-missing.o: $(hdrdir)/ruby/atomic.h
-missing.o: $(hdrdir)/ruby/backward.h
-missing.o: $(hdrdir)/ruby/backward/2/assume.h
-missing.o: $(hdrdir)/ruby/backward/2/attributes.h
-missing.o: $(hdrdir)/ruby/backward/2/bool.h
-missing.o: $(hdrdir)/ruby/backward/2/inttypes.h
-missing.o: $(hdrdir)/ruby/backward/2/limits.h
-missing.o: $(hdrdir)/ruby/backward/2/long_long.h
-missing.o: $(hdrdir)/ruby/backward/2/stdalign.h
-missing.o: $(hdrdir)/ruby/backward/2/stdarg.h
-missing.o: $(hdrdir)/ruby/defines.h
-missing.o: $(hdrdir)/ruby/intern.h
-missing.o: $(hdrdir)/ruby/internal/abi.h
-missing.o: $(hdrdir)/ruby/internal/anyargs.h
-missing.o: $(hdrdir)/ruby/internal/arithmetic.h
-missing.o: $(hdrdir)/ruby/internal/arithmetic/char.h
-missing.o: $(hdrdir)/ruby/internal/arithmetic/double.h
-missing.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h
-missing.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h
-missing.o: $(hdrdir)/ruby/internal/arithmetic/int.h
-missing.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h
-missing.o: $(hdrdir)/ruby/internal/arithmetic/long.h
-missing.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h
-missing.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h
-missing.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h
-missing.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h
-missing.o: $(hdrdir)/ruby/internal/arithmetic/short.h
-missing.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h
-missing.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h
-missing.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h
-missing.o: $(hdrdir)/ruby/internal/assume.h
-missing.o: $(hdrdir)/ruby/internal/attr/alloc_size.h
-missing.o: $(hdrdir)/ruby/internal/attr/artificial.h
-missing.o: $(hdrdir)/ruby/internal/attr/cold.h
-missing.o: $(hdrdir)/ruby/internal/attr/const.h
-missing.o: $(hdrdir)/ruby/internal/attr/constexpr.h
-missing.o: $(hdrdir)/ruby/internal/attr/deprecated.h
-missing.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h
-missing.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h
-missing.o: $(hdrdir)/ruby/internal/attr/error.h
-missing.o: $(hdrdir)/ruby/internal/attr/flag_enum.h
-missing.o: $(hdrdir)/ruby/internal/attr/forceinline.h
-missing.o: $(hdrdir)/ruby/internal/attr/format.h
-missing.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h
-missing.o: $(hdrdir)/ruby/internal/attr/noalias.h
-missing.o: $(hdrdir)/ruby/internal/attr/nodiscard.h
-missing.o: $(hdrdir)/ruby/internal/attr/noexcept.h
-missing.o: $(hdrdir)/ruby/internal/attr/noinline.h
-missing.o: $(hdrdir)/ruby/internal/attr/nonnull.h
-missing.o: $(hdrdir)/ruby/internal/attr/noreturn.h
-missing.o: $(hdrdir)/ruby/internal/attr/packed_struct.h
-missing.o: $(hdrdir)/ruby/internal/attr/pure.h
-missing.o: $(hdrdir)/ruby/internal/attr/restrict.h
-missing.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h
-missing.o: $(hdrdir)/ruby/internal/attr/warning.h
-missing.o: $(hdrdir)/ruby/internal/attr/weakref.h
-missing.o: $(hdrdir)/ruby/internal/cast.h
-missing.o: $(hdrdir)/ruby/internal/compiler_is.h
-missing.o: $(hdrdir)/ruby/internal/compiler_is/apple.h
-missing.o: $(hdrdir)/ruby/internal/compiler_is/clang.h
-missing.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h
-missing.o: $(hdrdir)/ruby/internal/compiler_is/intel.h
-missing.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h
-missing.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h
-missing.o: $(hdrdir)/ruby/internal/compiler_since.h
-missing.o: $(hdrdir)/ruby/internal/config.h
-missing.o: $(hdrdir)/ruby/internal/constant_p.h
-missing.o: $(hdrdir)/ruby/internal/core.h
-missing.o: $(hdrdir)/ruby/internal/core/rarray.h
-missing.o: $(hdrdir)/ruby/internal/core/rbasic.h
-missing.o: $(hdrdir)/ruby/internal/core/rbignum.h
-missing.o: $(hdrdir)/ruby/internal/core/rclass.h
-missing.o: $(hdrdir)/ruby/internal/core/rdata.h
-missing.o: $(hdrdir)/ruby/internal/core/rfile.h
-missing.o: $(hdrdir)/ruby/internal/core/rhash.h
-missing.o: $(hdrdir)/ruby/internal/core/robject.h
-missing.o: $(hdrdir)/ruby/internal/core/rregexp.h
-missing.o: $(hdrdir)/ruby/internal/core/rstring.h
-missing.o: $(hdrdir)/ruby/internal/core/rstruct.h
-missing.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
-missing.o: $(hdrdir)/ruby/internal/ctype.h
-missing.o: $(hdrdir)/ruby/internal/dllexport.h
-missing.o: $(hdrdir)/ruby/internal/dosish.h
-missing.o: $(hdrdir)/ruby/internal/error.h
-missing.o: $(hdrdir)/ruby/internal/eval.h
-missing.o: $(hdrdir)/ruby/internal/event.h
-missing.o: $(hdrdir)/ruby/internal/fl_type.h
-missing.o: $(hdrdir)/ruby/internal/gc.h
-missing.o: $(hdrdir)/ruby/internal/glob.h
-missing.o: $(hdrdir)/ruby/internal/globals.h
-missing.o: $(hdrdir)/ruby/internal/has/attribute.h
-missing.o: $(hdrdir)/ruby/internal/has/builtin.h
-missing.o: $(hdrdir)/ruby/internal/has/c_attribute.h
-missing.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h
-missing.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h
-missing.o: $(hdrdir)/ruby/internal/has/extension.h
-missing.o: $(hdrdir)/ruby/internal/has/feature.h
-missing.o: $(hdrdir)/ruby/internal/has/warning.h
-missing.o: $(hdrdir)/ruby/internal/intern/array.h
-missing.o: $(hdrdir)/ruby/internal/intern/bignum.h
-missing.o: $(hdrdir)/ruby/internal/intern/class.h
-missing.o: $(hdrdir)/ruby/internal/intern/compar.h
-missing.o: $(hdrdir)/ruby/internal/intern/complex.h
-missing.o: $(hdrdir)/ruby/internal/intern/cont.h
-missing.o: $(hdrdir)/ruby/internal/intern/dir.h
-missing.o: $(hdrdir)/ruby/internal/intern/enum.h
-missing.o: $(hdrdir)/ruby/internal/intern/enumerator.h
-missing.o: $(hdrdir)/ruby/internal/intern/error.h
-missing.o: $(hdrdir)/ruby/internal/intern/eval.h
-missing.o: $(hdrdir)/ruby/internal/intern/file.h
-missing.o: $(hdrdir)/ruby/internal/intern/hash.h
-missing.o: $(hdrdir)/ruby/internal/intern/io.h
-missing.o: $(hdrdir)/ruby/internal/intern/load.h
-missing.o: $(hdrdir)/ruby/internal/intern/marshal.h
-missing.o: $(hdrdir)/ruby/internal/intern/numeric.h
-missing.o: $(hdrdir)/ruby/internal/intern/object.h
-missing.o: $(hdrdir)/ruby/internal/intern/parse.h
-missing.o: $(hdrdir)/ruby/internal/intern/proc.h
-missing.o: $(hdrdir)/ruby/internal/intern/process.h
-missing.o: $(hdrdir)/ruby/internal/intern/random.h
-missing.o: $(hdrdir)/ruby/internal/intern/range.h
-missing.o: $(hdrdir)/ruby/internal/intern/rational.h
-missing.o: $(hdrdir)/ruby/internal/intern/re.h
-missing.o: $(hdrdir)/ruby/internal/intern/ruby.h
-missing.o: $(hdrdir)/ruby/internal/intern/select.h
-missing.o: $(hdrdir)/ruby/internal/intern/select/largesize.h
-missing.o: $(hdrdir)/ruby/internal/intern/signal.h
-missing.o: $(hdrdir)/ruby/internal/intern/sprintf.h
-missing.o: $(hdrdir)/ruby/internal/intern/string.h
-missing.o: $(hdrdir)/ruby/internal/intern/struct.h
-missing.o: $(hdrdir)/ruby/internal/intern/thread.h
-missing.o: $(hdrdir)/ruby/internal/intern/time.h
-missing.o: $(hdrdir)/ruby/internal/intern/variable.h
-missing.o: $(hdrdir)/ruby/internal/intern/vm.h
-missing.o: $(hdrdir)/ruby/internal/interpreter.h
-missing.o: $(hdrdir)/ruby/internal/iterator.h
-missing.o: $(hdrdir)/ruby/internal/memory.h
-missing.o: $(hdrdir)/ruby/internal/method.h
-missing.o: $(hdrdir)/ruby/internal/module.h
-missing.o: $(hdrdir)/ruby/internal/newobj.h
-missing.o: $(hdrdir)/ruby/internal/scan_args.h
-missing.o: $(hdrdir)/ruby/internal/special_consts.h
-missing.o: $(hdrdir)/ruby/internal/static_assert.h
-missing.o: $(hdrdir)/ruby/internal/stdalign.h
-missing.o: $(hdrdir)/ruby/internal/stdbool.h
-missing.o: $(hdrdir)/ruby/internal/symbol.h
-missing.o: $(hdrdir)/ruby/internal/value.h
-missing.o: $(hdrdir)/ruby/internal/value_type.h
-missing.o: $(hdrdir)/ruby/internal/variable.h
-missing.o: $(hdrdir)/ruby/internal/warning_push.h
-missing.o: $(hdrdir)/ruby/internal/xmalloc.h
-missing.o: $(hdrdir)/ruby/missing.h
-missing.o: $(hdrdir)/ruby/ruby.h
-missing.o: $(hdrdir)/ruby/st.h
-missing.o: $(hdrdir)/ruby/subst.h
-missing.o: missing.c
-missing.o: missing/dtoa.c
-# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/bigdecimal/extconf.rb b/ext/bigdecimal/extconf.rb
deleted file mode 100644
index 23904ed60e..0000000000
--- a/ext/bigdecimal/extconf.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-# frozen_string_literal: false
-require 'mkmf'
-
-def have_builtin_func(name, check_expr, opt = "", &b)
- checking_for checking_message(name.funcall_style, nil, opt) do
- if try_compile(<<SRC, opt, &b)
-int foo;
-int main() { #{check_expr}; return 0; }
-SRC
- $defs.push(format("-DHAVE_BUILTIN_%s", name.tr_cpp))
- true
- else
- false
- end
- end
-end
-
-have_builtin_func("__builtin_clz", "__builtin_clz(0)")
-have_builtin_func("__builtin_clzl", "__builtin_clzl(0)")
-have_builtin_func("__builtin_clzll", "__builtin_clzll(0)")
-
-have_header("float.h")
-have_header("math.h")
-have_header("stdbool.h")
-have_header("stdlib.h")
-
-have_header("x86intrin.h")
-have_func("_lzcnt_u32", "x86intrin.h")
-have_func("_lzcnt_u64", "x86intrin.h")
-
-have_header("intrin.h")
-have_func("__lzcnt", "intrin.h")
-have_func("__lzcnt64", "intrin.h")
-have_func("_BitScanReverse", "intrin.h")
-have_func("_BitScanReverse64", "intrin.h")
-
-have_func("labs", "stdlib.h")
-have_func("llabs", "stdlib.h")
-have_func("finite", "math.h")
-have_func("isfinite", "math.h")
-
-have_header("ruby/atomic.h")
-have_header("ruby/internal/has/builtin.h")
-have_header("ruby/internal/static_assert.h")
-
-have_func("rb_rational_num", "ruby.h")
-have_func("rb_rational_den", "ruby.h")
-have_func("rb_complex_real", "ruby.h")
-have_func("rb_complex_imag", "ruby.h")
-have_func("rb_opts_exception_p", "ruby.h")
-have_func("rb_category_warn", "ruby.h")
-have_const("RB_WARN_CATEGORY_DEPRECATED", "ruby.h")
-
-if File.file?(File.expand_path('../lib/bigdecimal.rb', __FILE__))
- bigdecimal_rb = "$(srcdir)/lib/bigdecimal.rb"
-else
- bigdecimal_rb = "$(srcdir)/../../lib/bigdecimal.rb"
-end
-
-create_makefile('bigdecimal') {|mf|
- mf << "BIGDECIMAL_RB = #{bigdecimal_rb}\n"
-}
diff --git a/ext/bigdecimal/feature.h b/ext/bigdecimal/feature.h
deleted file mode 100644
index f628514500..0000000000
--- a/ext/bigdecimal/feature.h
+++ /dev/null
@@ -1,68 +0,0 @@
-#ifndef BIGDECIMAL_HAS_FEATURE_H
-#define BIGDECIMAL_HAS_FEATURE_H
-
-/* ======== __has_feature ======== */
-
-#ifndef __has_feature
-# define __has_feature(_) 0
-#endif
-
-/* ======== __has_extension ======== */
-
-#ifndef __has_extension
-# define __has_extension __has_feature
-#endif
-
-/* ======== __has_builtin ======== */
-
-#ifdef HAVE_RUBY_INTERNAL_HAS_BUILTIN_H
-# include <ruby/internal/has/builtin.h>
-#endif
-
-#ifdef RBIMPL_HAS_BUILTIN
-# define BIGDECIMAL_HAS_BUILTIN(...) RBIMPL_HAS_BUILTIN(__VA_ARGS__)
-
-#else
-# /* The following section is copied from CRuby's builtin.h */
-#
-# ifdef __has_builtin
-# if defined(__INTEL_COMPILER)
-# /* :TODO: Intel C Compiler has __has_builtin (since 19.1 maybe?), and is
-# * reportedly broken. We have to skip them. However the situation can
-# * change. They might improve someday. We need to revisit here later. */
-# elif defined(__GNUC__) && ! __has_builtin(__builtin_alloca)
-# /* FreeBSD's <sys/cdefs.h> defines its own *broken* version of
-# * __has_builtin. Cygwin copied that content to be a victim of the
-# * broken-ness. We don't take them into account. */
-# else
-# define HAVE___HAS_BUILTIN 1
-# endif
-# endif
-#
-# if defined(HAVE___HAS_BUILTIN)
-# define BIGDECIMAL_HAS_BUILTIN(_) __has_builtin(_)
-#
-# elif defined(__GNUC__)
-# define BIGDECIMAL_HAS_BUILTIN(_) BIGDECIMAL_HAS_BUILTIN_ ## _
-# if defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 6))
-# define BIGDECIMAL_HAS_BUILTIN___builtin_clz 1
-# define BIGDECIMAL_HAS_BUILTIN___builtin_clzl 1
-# else
-# define BIGDECIMAL_HAS_BUILTIN___builtin_clz 0
-# define BIGDECIMAL_HAS_BUILTIN___builtin_clzl 0
-# endif
-# elif defined(_MSC_VER)
-# define BIGDECIMAL_HAS_BUILTIN(_) 0
-#
-# else
-# define BIGDECIMAL_HAS_BUILTIN(_) BIGDECIMAL_HAS_BUILTIN_ ## _
-# define BIGDECIMAL_HAS_BUILTIN___builtin_clz HAVE_BUILTIN___BUILTIN_CLZ
-# define BIGDECIMAL_HAS_BUILTIN___builtin_clzl HAVE_BUILTIN___BUILTIN_CLZL
-# endif
-#endif /* RBIMPL_HAS_BUILTIN */
-
-#ifndef __has_builtin
-# define __has_builtin(...) BIGDECIMAL_HAS_BUILTIN(__VA_ARGS__)
-#endif
-
-#endif /* BIGDECIMAL_HAS_FEATURE_H */
diff --git a/ext/bigdecimal/lib/bigdecimal.rb b/ext/bigdecimal/lib/bigdecimal.rb
deleted file mode 100644
index 82b3e1b7b9..0000000000
--- a/ext/bigdecimal/lib/bigdecimal.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-if RUBY_ENGINE == 'jruby'
- JRuby::Util.load_ext("org.jruby.ext.bigdecimal.BigDecimalLibrary")
-else
- require 'bigdecimal.so'
-end
diff --git a/ext/bigdecimal/lib/bigdecimal/jacobian.rb b/ext/bigdecimal/lib/bigdecimal/jacobian.rb
deleted file mode 100644
index 4448024c74..0000000000
--- a/ext/bigdecimal/lib/bigdecimal/jacobian.rb
+++ /dev/null
@@ -1,90 +0,0 @@
-# frozen_string_literal: false
-
-require 'bigdecimal'
-
-# require 'bigdecimal/jacobian'
-#
-# Provides methods to compute the Jacobian matrix of a set of equations at a
-# point x. In the methods below:
-#
-# f is an Object which is used to compute the Jacobian matrix of the equations.
-# It must provide the following methods:
-#
-# f.values(x):: returns the values of all functions at x
-#
-# f.zero:: returns 0.0
-# f.one:: returns 1.0
-# f.two:: returns 2.0
-# f.ten:: returns 10.0
-#
-# f.eps:: returns the convergence criterion (epsilon value) used to determine whether two values are considered equal. If |a-b| < epsilon, the two values are considered equal.
-#
-# x is the point at which to compute the Jacobian.
-#
-# fx is f.values(x).
-#
-module Jacobian
- module_function
-
- # Determines the equality of two numbers by comparing to zero, or using the epsilon value
- def isEqual(a,b,zero=0.0,e=1.0e-8)
- aa = a.abs
- bb = b.abs
- if aa == zero && bb == zero then
- true
- else
- if ((a-b)/(aa+bb)).abs < e then
- true
- else
- false
- end
- end
- end
-
-
- # Computes the derivative of +f[i]+ at +x[i]+.
- # +fx+ is the value of +f+ at +x+.
- def dfdxi(f,fx,x,i)
- nRetry = 0
- n = x.size
- xSave = x[i]
- ok = 0
- ratio = f.ten*f.ten*f.ten
- dx = x[i].abs/ratio
- dx = fx[i].abs/ratio if isEqual(dx,f.zero,f.zero,f.eps)
- dx = f.one/f.ten if isEqual(dx,f.zero,f.zero,f.eps)
- until ok>0 do
- deriv = []
- nRetry += 1
- if nRetry > 100
- raise "Singular Jacobian matrix. No change at x[" + i.to_s + "]"
- end
- dx = dx*f.two
- x[i] += dx
- fxNew = f.values(x)
- for j in 0...n do
- if !isEqual(fxNew[j],fx[j],f.zero,f.eps) then
- ok += 1
- deriv <<= (fxNew[j]-fx[j])/dx
- else
- deriv <<= f.zero
- end
- end
- x[i] = xSave
- end
- deriv
- end
-
- # Computes the Jacobian of +f+ at +x+. +fx+ is the value of +f+ at +x+.
- def jacobian(f,fx,x)
- n = x.size
- dfdx = Array.new(n*n)
- for i in 0...n do
- df = dfdxi(f,fx,x,i)
- for j in 0...n do
- dfdx[j*n+i] = df[j]
- end
- end
- dfdx
- end
-end
diff --git a/ext/bigdecimal/lib/bigdecimal/ludcmp.rb b/ext/bigdecimal/lib/bigdecimal/ludcmp.rb
deleted file mode 100644
index dd265e482a..0000000000
--- a/ext/bigdecimal/lib/bigdecimal/ludcmp.rb
+++ /dev/null
@@ -1,89 +0,0 @@
-# frozen_string_literal: false
-require 'bigdecimal'
-
-#
-# Solves a*x = b for x, using LU decomposition.
-#
-module LUSolve
- module_function
-
- # Performs LU decomposition of the n by n matrix a.
- def ludecomp(a,n,zero=0,one=1)
- prec = BigDecimal.limit(nil)
- ps = []
- scales = []
- for i in 0...n do # pick up largest(abs. val.) element in each row.
- ps <<= i
- nrmrow = zero
- ixn = i*n
- for j in 0...n do
- biggst = a[ixn+j].abs
- nrmrow = biggst if biggst>nrmrow
- end
- if nrmrow>zero then
- scales <<= one.div(nrmrow,prec)
- else
- raise "Singular matrix"
- end
- end
- n1 = n - 1
- for k in 0...n1 do # Gaussian elimination with partial pivoting.
- biggst = zero;
- for i in k...n do
- size = a[ps[i]*n+k].abs*scales[ps[i]]
- if size>biggst then
- biggst = size
- pividx = i
- end
- end
- raise "Singular matrix" if biggst<=zero
- if pividx!=k then
- j = ps[k]
- ps[k] = ps[pividx]
- ps[pividx] = j
- end
- pivot = a[ps[k]*n+k]
- for i in (k+1)...n do
- psin = ps[i]*n
- a[psin+k] = mult = a[psin+k].div(pivot,prec)
- if mult!=zero then
- pskn = ps[k]*n
- for j in (k+1)...n do
- a[psin+j] -= mult.mult(a[pskn+j],prec)
- end
- end
- end
- end
- raise "Singular matrix" if a[ps[n1]*n+n1] == zero
- ps
- end
-
- # Solves a*x = b for x, using LU decomposition.
- #
- # a is a matrix, b is a constant vector, x is the solution vector.
- #
- # ps is the pivot, a vector which indicates the permutation of rows performed
- # during LU decomposition.
- def lusolve(a,b,ps,zero=0.0)
- prec = BigDecimal.limit(nil)
- n = ps.size
- x = []
- for i in 0...n do
- dot = zero
- psin = ps[i]*n
- for j in 0...i do
- dot = a[psin+j].mult(x[j],prec) + dot
- end
- x <<= b[ps[i]] - dot
- end
- (n-1).downto(0) do |i|
- dot = zero
- psin = ps[i]*n
- for j in (i+1)...n do
- dot = a[psin+j].mult(x[j],prec) + dot
- end
- x[i] = (x[i]-dot).div(a[psin+i],prec)
- end
- x
- end
-end
diff --git a/ext/bigdecimal/lib/bigdecimal/math.rb b/ext/bigdecimal/lib/bigdecimal/math.rb
deleted file mode 100644
index 0b9d0648bb..0000000000
--- a/ext/bigdecimal/lib/bigdecimal/math.rb
+++ /dev/null
@@ -1,232 +0,0 @@
-# frozen_string_literal: false
-require 'bigdecimal'
-
-#
-#--
-# Contents:
-# sqrt(x, prec)
-# sin (x, prec)
-# cos (x, prec)
-# atan(x, prec) Note: |x|<1, x=0.9999 may not converge.
-# PI (prec)
-# E (prec) == exp(1.0,prec)
-#
-# where:
-# x ... BigDecimal number to be computed.
-# |x| must be small enough to get convergence.
-# prec ... Number of digits to be obtained.
-#++
-#
-# Provides mathematical functions.
-#
-# Example:
-#
-# require "bigdecimal/math"
-#
-# include BigMath
-#
-# a = BigDecimal((PI(100)/2).to_s)
-# puts sin(a,100) # => 0.99999999999999999999......e0
-#
-module BigMath
- module_function
-
- # call-seq:
- # sqrt(decimal, numeric) -> BigDecimal
- #
- # Computes the square root of +decimal+ to the specified number of digits of
- # precision, +numeric+.
- #
- # BigMath.sqrt(BigDecimal('2'), 16).to_s
- # #=> "0.1414213562373095048801688724e1"
- #
- def sqrt(x, prec)
- x.sqrt(prec)
- end
-
- # call-seq:
- # sin(decimal, numeric) -> BigDecimal
- #
- # Computes the sine of +decimal+ to the specified number of digits of
- # precision, +numeric+.
- #
- # If +decimal+ is Infinity or NaN, returns NaN.
- #
- # BigMath.sin(BigMath.PI(5)/4, 5).to_s
- # #=> "0.70710678118654752440082036563292800375e0"
- #
- def sin(x, prec)
- raise ArgumentError, "Zero or negative precision for sin" if prec <= 0
- return BigDecimal("NaN") if x.infinite? || x.nan?
- n = prec + BigDecimal.double_fig
- one = BigDecimal("1")
- two = BigDecimal("2")
- x = -x if neg = x < 0
- if x > (twopi = two * BigMath.PI(prec))
- if x > 30
- x %= twopi
- else
- x -= twopi while x > twopi
- end
- end
- x1 = x
- x2 = x.mult(x,n)
- sign = 1
- y = x
- d = y
- i = one
- z = one
- while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
- m = BigDecimal.double_fig if m < BigDecimal.double_fig
- sign = -sign
- x1 = x2.mult(x1,n)
- i += two
- z *= (i-one) * i
- d = sign * x1.div(z,m)
- y += d
- end
- neg ? -y : y
- end
-
- # call-seq:
- # cos(decimal, numeric) -> BigDecimal
- #
- # Computes the cosine of +decimal+ to the specified number of digits of
- # precision, +numeric+.
- #
- # If +decimal+ is Infinity or NaN, returns NaN.
- #
- # BigMath.cos(BigMath.PI(4), 16).to_s
- # #=> "-0.999999999999999999999999999999856613163740061349e0"
- #
- def cos(x, prec)
- raise ArgumentError, "Zero or negative precision for cos" if prec <= 0
- return BigDecimal("NaN") if x.infinite? || x.nan?
- n = prec + BigDecimal.double_fig
- one = BigDecimal("1")
- two = BigDecimal("2")
- x = -x if x < 0
- if x > (twopi = two * BigMath.PI(prec))
- if x > 30
- x %= twopi
- else
- x -= twopi while x > twopi
- end
- end
- x1 = one
- x2 = x.mult(x,n)
- sign = 1
- y = one
- d = y
- i = BigDecimal("0")
- z = one
- while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
- m = BigDecimal.double_fig if m < BigDecimal.double_fig
- sign = -sign
- x1 = x2.mult(x1,n)
- i += two
- z *= (i-one) * i
- d = sign * x1.div(z,m)
- y += d
- end
- y
- end
-
- # call-seq:
- # atan(decimal, numeric) -> BigDecimal
- #
- # Computes the arctangent of +decimal+ to the specified number of digits of
- # precision, +numeric+.
- #
- # If +decimal+ is NaN, returns NaN.
- #
- # BigMath.atan(BigDecimal('-1'), 16).to_s
- # #=> "-0.785398163397448309615660845819878471907514682065e0"
- #
- def atan(x, prec)
- raise ArgumentError, "Zero or negative precision for atan" if prec <= 0
- return BigDecimal("NaN") if x.nan?
- pi = PI(prec)
- x = -x if neg = x < 0
- return pi.div(neg ? -2 : 2, prec) if x.infinite?
- return pi / (neg ? -4 : 4) if x.round(prec) == 1
- x = BigDecimal("1").div(x, prec) if inv = x > 1
- x = (-1 + sqrt(1 + x**2, prec))/x if dbl = x > 0.5
- n = prec + BigDecimal.double_fig
- y = x
- d = y
- t = x
- r = BigDecimal("3")
- x2 = x.mult(x,n)
- while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
- m = BigDecimal.double_fig if m < BigDecimal.double_fig
- t = -t.mult(x2,n)
- d = t.div(r,m)
- y += d
- r += 2
- end
- y *= 2 if dbl
- y = pi / 2 - y if inv
- y = -y if neg
- y
- end
-
- # call-seq:
- # PI(numeric) -> BigDecimal
- #
- # Computes the value of pi to the specified number of digits of precision,
- # +numeric+.
- #
- # BigMath.PI(10).to_s
- # #=> "0.3141592653589793238462643388813853786957412e1"
- #
- def PI(prec)
- raise ArgumentError, "Zero or negative precision for PI" if prec <= 0
- n = prec + BigDecimal.double_fig
- zero = BigDecimal("0")
- one = BigDecimal("1")
- two = BigDecimal("2")
-
- m25 = BigDecimal("-0.04")
- m57121 = BigDecimal("-57121")
-
- pi = zero
-
- d = one
- k = one
- t = BigDecimal("-80")
- while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0)
- m = BigDecimal.double_fig if m < BigDecimal.double_fig
- t = t*m25
- d = t.div(k,m)
- k = k+two
- pi = pi + d
- end
-
- d = one
- k = one
- t = BigDecimal("956")
- while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0)
- m = BigDecimal.double_fig if m < BigDecimal.double_fig
- t = t.div(m57121,n)
- d = t.div(k,m)
- pi = pi + d
- k = k+two
- end
- pi
- end
-
- # call-seq:
- # E(numeric) -> BigDecimal
- #
- # Computes e (the base of natural logarithms) to the specified number of
- # digits of precision, +numeric+.
- #
- # BigMath.E(10).to_s
- # #=> "0.271828182845904523536028752390026306410273e1"
- #
- def E(prec)
- raise ArgumentError, "Zero or negative precision for E" if prec <= 0
- BigMath.exp(1, prec)
- end
-end
diff --git a/ext/bigdecimal/lib/bigdecimal/newton.rb b/ext/bigdecimal/lib/bigdecimal/newton.rb
deleted file mode 100644
index 85bacb7f2e..0000000000
--- a/ext/bigdecimal/lib/bigdecimal/newton.rb
+++ /dev/null
@@ -1,80 +0,0 @@
-# frozen_string_literal: false
-require "bigdecimal/ludcmp"
-require "bigdecimal/jacobian"
-
-#
-# newton.rb
-#
-# Solves the nonlinear algebraic equation system f = 0 by Newton's method.
-# This program is not dependent on BigDecimal.
-#
-# To call:
-# n = nlsolve(f,x)
-# where n is the number of iterations required,
-# x is the initial value vector
-# f is an Object which is used to compute the values of the equations to be solved.
-# It must provide the following methods:
-#
-# f.values(x):: returns the values of all functions at x
-#
-# f.zero:: returns 0.0
-# f.one:: returns 1.0
-# f.two:: returns 2.0
-# f.ten:: returns 10.0
-#
-# f.eps:: returns the convergence criterion (epsilon value) used to determine whether two values are considered equal. If |a-b| < epsilon, the two values are considered equal.
-#
-# On exit, x is the solution vector.
-#
-module Newton
- include LUSolve
- include Jacobian
- module_function
-
- def norm(fv,zero=0.0) # :nodoc:
- s = zero
- n = fv.size
- for i in 0...n do
- s += fv[i]*fv[i]
- end
- s
- end
-
- # See also Newton
- def nlsolve(f,x)
- nRetry = 0
- n = x.size
-
- f0 = f.values(x)
- zero = f.zero
- one = f.one
- two = f.two
- p5 = one/two
- d = norm(f0,zero)
- minfact = f.ten*f.ten*f.ten
- minfact = one/minfact
- e = f.eps
- while d >= e do
- nRetry += 1
- # Not yet converged. => Compute Jacobian matrix
- dfdx = jacobian(f,f0,x)
- # Solve dfdx*dx = -f0 to estimate dx
- dx = lusolve(dfdx,f0,ludecomp(dfdx,n,zero,one),zero)
- fact = two
- xs = x.dup
- begin
- fact *= p5
- if fact < minfact then
- raise "Failed to reduce function values."
- end
- for i in 0...n do
- x[i] = xs[i] - dx[i]*fact
- end
- f0 = f.values(x)
- dn = norm(f0,zero)
- end while(dn>=d)
- d = dn
- end
- nRetry
- end
-end
diff --git a/ext/bigdecimal/lib/bigdecimal/util.rb b/ext/bigdecimal/lib/bigdecimal/util.rb
deleted file mode 100644
index ad92f7cfe6..0000000000
--- a/ext/bigdecimal/lib/bigdecimal/util.rb
+++ /dev/null
@@ -1,185 +0,0 @@
-# frozen_string_literal: false
-#
-#--
-# bigdecimal/util extends various native classes to provide the #to_d method,
-# and provides BigDecimal#to_d and BigDecimal#to_digits.
-#++
-
-require 'bigdecimal'
-
-class Integer < Numeric
- # call-seq:
- # int.to_d -> bigdecimal
- #
- # Returns the value of +int+ as a BigDecimal.
- #
- # require 'bigdecimal'
- # require 'bigdecimal/util'
- #
- # 42.to_d # => 0.42e2
- #
- # See also BigDecimal::new.
- #
- def to_d
- BigDecimal(self)
- end
-end
-
-
-class Float < Numeric
- # call-seq:
- # float.to_d -> bigdecimal
- # float.to_d(precision) -> bigdecimal
- #
- # Returns the value of +float+ as a BigDecimal.
- # The +precision+ parameter is used to determine the number of
- # significant digits for the result. When +precision+ is set to +0+,
- # the number of digits to represent the float being converted is determined
- # automatically.
- # The default +precision+ is +0+.
- #
- # require 'bigdecimal'
- # require 'bigdecimal/util'
- #
- # 0.5.to_d # => 0.5e0
- # 1.234.to_d # => 0.1234e1
- # 1.234.to_d(2) # => 0.12e1
- #
- # See also BigDecimal::new.
- #
- def to_d(precision=0)
- BigDecimal(self, precision)
- end
-end
-
-
-class String
- # call-seq:
- # str.to_d -> bigdecimal
- #
- # Returns the result of interpreting leading characters in +str+
- # as a BigDecimal.
- #
- # require 'bigdecimal'
- # require 'bigdecimal/util'
- #
- # "0.5".to_d # => 0.5e0
- # "123.45e1".to_d # => 0.12345e4
- # "45.67 degrees".to_d # => 0.4567e2
- #
- # See also BigDecimal::new.
- #
- def to_d
- BigDecimal.interpret_loosely(self)
- end
-end
-
-
-class BigDecimal < Numeric
- # call-seq:
- # a.to_digits -> string
- #
- # Converts a BigDecimal to a String of the form "nnnnnn.mmm".
- # This method is deprecated; use BigDecimal#to_s("F") instead.
- #
- # require 'bigdecimal/util'
- #
- # d = BigDecimal("3.14")
- # d.to_digits # => "3.14"
- #
- def to_digits
- if self.nan? || self.infinite? || self.zero?
- self.to_s
- else
- i = self.to_i.to_s
- _,f,_,z = self.frac.split
- i + "." + ("0"*(-z)) + f
- end
- end
-
- # call-seq:
- # a.to_d -> bigdecimal
- #
- # Returns self.
- #
- # require 'bigdecimal/util'
- #
- # d = BigDecimal("3.14")
- # d.to_d # => 0.314e1
- #
- def to_d
- self
- end
-end
-
-
-class Rational < Numeric
- # call-seq:
- # rat.to_d(precision) -> bigdecimal
- #
- # Returns the value as a BigDecimal.
- #
- # The required +precision+ parameter is used to determine the number of
- # significant digits for the result.
- #
- # require 'bigdecimal'
- # require 'bigdecimal/util'
- #
- # Rational(22, 7).to_d(3) # => 0.314e1
- #
- # See also BigDecimal::new.
- #
- def to_d(precision)
- BigDecimal(self, precision)
- end
-end
-
-
-class Complex < Numeric
- # call-seq:
- # cmp.to_d -> bigdecimal
- # cmp.to_d(precision) -> bigdecimal
- #
- # Returns the value as a BigDecimal.
- #
- # The +precision+ parameter is required for a rational complex number.
- # This parameter is used to determine the number of significant digits
- # for the result.
- #
- # require 'bigdecimal'
- # require 'bigdecimal/util'
- #
- # Complex(0.1234567, 0).to_d(4) # => 0.1235e0
- # Complex(Rational(22, 7), 0).to_d(3) # => 0.314e1
- #
- # See also BigDecimal::new.
- #
- def to_d(*args)
- BigDecimal(self) unless self.imag.zero? # to raise eerror
-
- if args.length == 0
- case self.real
- when Rational
- BigDecimal(self.real) # to raise error
- end
- end
- self.real.to_d(*args)
- end
-end
-
-
-class NilClass
- # call-seq:
- # nil.to_d -> bigdecimal
- #
- # Returns nil represented as a BigDecimal.
- #
- # require 'bigdecimal'
- # require 'bigdecimal/util'
- #
- # nil.to_d # => 0.0
- #
- def to_d
- BigDecimal(0)
- end
-end
diff --git a/ext/bigdecimal/missing.c b/ext/bigdecimal/missing.c
deleted file mode 100644
index 703232d92f..0000000000
--- a/ext/bigdecimal/missing.c
+++ /dev/null
@@ -1,27 +0,0 @@
-#include <ruby/ruby.h>
-
-#ifdef HAVE_RUBY_ATOMIC_H
-# include <ruby/atomic.h>
-#endif
-
-#ifdef RUBY_ATOMIC_PTR_CAS
-# define ATOMIC_PTR_CAS(var, old, new) RUBY_ATOMIC_PTR_CAS(var, old, new)
-#endif
-
-#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
-/* GCC warns about unknown sanitizer, which is annoying. */
-# undef NO_SANITIZE
-# define NO_SANITIZE(x, y) \
- _Pragma("GCC diagnostic push") \
- _Pragma("GCC diagnostic ignored \"-Wattributes\"") \
- __attribute__((__no_sanitize__(x))) y; \
- _Pragma("GCC diagnostic pop")
-#endif
-
-#undef strtod
-#define strtod BigDecimal_strtod
-#undef dtoa
-#define dtoa BigDecimal_dtoa
-#undef hdtoa
-#define hdtoa BigDecimal_hdtoa
-#include "missing/dtoa.c"
diff --git a/ext/bigdecimal/missing.h b/ext/bigdecimal/missing.h
deleted file mode 100644
index 325554b5f5..0000000000
--- a/ext/bigdecimal/missing.h
+++ /dev/null
@@ -1,196 +0,0 @@
-#ifndef MISSING_H
-#define MISSING_H 1
-
-#if defined(__cplusplus)
-extern "C" {
-#if 0
-} /* satisfy cc-mode */
-#endif
-#endif
-
-#ifdef HAVE_STDLIB_H
-# include <stdlib.h>
-#endif
-
-#ifdef HAVE_MATH_H
-# include <math.h>
-#endif
-
-#ifndef RB_UNUSED_VAR
-# if defined(_MSC_VER) && _MSC_VER >= 1911
-# define RB_UNUSED_VAR(x) x [[maybe_unused]]
-
-# elif defined(__has_cpp_attribute) && __has_cpp_attribute(maybe_unused)
-# define RB_UNUSED_VAR(x) x [[maybe_unused]]
-
-# elif defined(__has_c_attribute) && __has_c_attribute(maybe_unused)
-# define RB_UNUSED_VAR(x) x [[maybe_unused]]
-
-# elif defined(__GNUC__)
-# define RB_UNUSED_VAR(x) x __attribute__ ((unused))
-
-# else
-# define RB_UNUSED_VAR(x) x
-# endif
-#endif /* RB_UNUSED_VAR */
-
-#if defined(_MSC_VER) && _MSC_VER >= 1310
-# define HAVE___ASSUME 1
-
-#elif defined(__INTEL_COMPILER) && __INTEL_COMPILER >= 1300
-# define HAVE___ASSUME 1
-#endif
-
-#ifndef UNREACHABLE
-# if __has_builtin(__builtin_unreachable)
-# define UNREACHABLE __builtin_unreachable()
-
-# elif defined(HAVE___ASSUME)
-# define UNREACHABLE __assume(0)
-
-# else
-# define UNREACHABLE /* unreachable */
-# endif
-#endif /* UNREACHABLE */
-
-/* bool */
-
-#if defined(__bool_true_false_are_defined)
-# /* Take that. */
-
-#elif defined(HAVE_STDBOOL_H)
-# include <stdbool.h>
-
-#else
-typedef unsigned char _Bool;
-# define bool _Bool
-# define true ((_Bool)+1)
-# define false ((_Bool)-1)
-# define __bool_true_false_are_defined
-#endif
-
-/* abs */
-
-#ifndef HAVE_LABS
-static inline long
-labs(long const x)
-{
- if (x < 0) return -x;
- return x;
-}
-#endif
-
-#ifndef HAVE_LLABS
-static inline LONG_LONG
-llabs(LONG_LONG const x)
-{
- if (x < 0) return -x;
- return x;
-}
-#endif
-
-#ifdef vabs
-# undef vabs
-#endif
-#if SIZEOF_VALUE <= SIZEOF_INT
-# define vabs abs
-#elif SIZEOF_VALUE <= SIZEOF_LONG
-# define vabs labs
-#elif SIZEOF_VALUE <= SIZEOF_LONG_LONG
-# define vabs llabs
-#endif
-
-/* finite */
-
-#ifndef HAVE_FINITE
-static int
-finite(double)
-{
- return !isnan(n) && !isinf(n);
-}
-#endif
-
-#ifndef isfinite
-# ifndef HAVE_ISFINITE
-# define HAVE_ISFINITE 1
-# define isfinite(x) finite(x)
-# endif
-#endif
-
-/* dtoa */
-char *BigDecimal_dtoa(double d_, int mode, int ndigits, int *decpt, int *sign, char **rve);
-
-/* rational */
-
-#ifndef HAVE_RB_RATIONAL_NUM
-static inline VALUE
-rb_rational_num(VALUE rat)
-{
-#ifdef RRATIONAL
- return RRATIONAL(rat)->num;
-#else
- return rb_funcall(rat, rb_intern("numerator"), 0);
-#endif
-}
-#endif
-
-#ifndef HAVE_RB_RATIONAL_DEN
-static inline VALUE
-rb_rational_den(VALUE rat)
-{
-#ifdef RRATIONAL
- return RRATIONAL(rat)->den;
-#else
- return rb_funcall(rat, rb_intern("denominator"), 0);
-#endif
-}
-#endif
-
-/* complex */
-
-#ifndef HAVE_RB_COMPLEX_REAL
-static inline VALUE
-rb_complex_real(VALUE cmp)
-{
-#ifdef RCOMPLEX
- return RCOMPLEX(cmp)->real;
-#else
- return rb_funcall(cmp, rb_intern("real"), 0);
-#endif
-}
-#endif
-
-#ifndef HAVE_RB_COMPLEX_IMAG
-static inline VALUE
-rb_complex_imag(VALUE cmp)
-{
-# ifdef RCOMPLEX
- return RCOMPLEX(cmp)->imag;
-# else
- return rb_funcall(cmp, rb_intern("imag"), 0);
-# endif
-}
-#endif
-
-/* st */
-
-#ifndef ST2FIX
-# undef RB_ST2FIX
-# define RB_ST2FIX(h) LONG2FIX((long)(h))
-# define ST2FIX(h) RB_ST2FIX(h)
-#endif
-
-/* warning */
-
-#if !defined(HAVE_RB_CATEGORY_WARN) || !defined(HAVE_CONST_RB_WARN_CATEGORY_DEPRECATED)
-# define rb_category_warn(category, ...) rb_warn(__VA_ARGS__)
-#endif
-
-#if defined(__cplusplus)
-#if 0
-{ /* satisfy cc-mode */
-#endif
-} /* extern "C" { */
-#endif
-
-#endif /* MISSING_H */
diff --git a/ext/bigdecimal/missing/dtoa.c b/ext/bigdecimal/missing/dtoa.c
deleted file mode 100644
index 41b0a221d1..0000000000
--- a/ext/bigdecimal/missing/dtoa.c
+++ /dev/null
@@ -1,3462 +0,0 @@
-/****************************************************************
- *
- * The author of this software is David M. Gay.
- *
- * Copyright (c) 1991, 2000, 2001 by Lucent Technologies.
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose without fee is hereby granted, provided that this entire notice
- * is included in all copies of any software which is or includes a copy
- * or modification of this software and in all copies of the supporting
- * documentation for such software.
- *
- * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
- * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
- * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
- * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
- *
- ***************************************************************/
-
-/* Please send bug reports to David M. Gay (dmg at acm dot org,
- * with " at " changed at "@" and " dot " changed to "."). */
-
-/* On a machine with IEEE extended-precision registers, it is
- * necessary to specify double-precision (53-bit) rounding precision
- * before invoking strtod or dtoa. If the machine uses (the equivalent
- * of) Intel 80x87 arithmetic, the call
- * _control87(PC_53, MCW_PC);
- * does this with many compilers. Whether this or another call is
- * appropriate depends on the compiler; for this to work, it may be
- * necessary to #include "float.h" or another system-dependent header
- * file.
- */
-
-/* strtod for IEEE-, VAX-, and IBM-arithmetic machines.
- *
- * This strtod returns a nearest machine number to the input decimal
- * string (or sets errno to ERANGE). With IEEE arithmetic, ties are
- * broken by the IEEE round-even rule. Otherwise ties are broken by
- * biased rounding (add half and chop).
- *
- * Inspired loosely by William D. Clinger's paper "How to Read Floating
- * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101].
- *
- * Modifications:
- *
- * 1. We only require IEEE, IBM, or VAX double-precision
- * arithmetic (not IEEE double-extended).
- * 2. We get by with floating-point arithmetic in a case that
- * Clinger missed -- when we're computing d * 10^n
- * for a small integer d and the integer n is not too
- * much larger than 22 (the maximum integer k for which
- * we can represent 10^k exactly), we may be able to
- * compute (d*10^k) * 10^(e-k) with just one roundoff.
- * 3. Rather than a bit-at-a-time adjustment of the binary
- * result in the hard case, we use floating-point
- * arithmetic to determine the adjustment to within
- * one bit; only in really hard cases do we need to
- * compute a second residual.
- * 4. Because of 3., we don't need a large table of powers of 10
- * for ten-to-e (just some small tables, e.g. of 10^k
- * for 0 <= k <= 22).
- */
-
-/*
- * #define IEEE_LITTLE_ENDIAN for IEEE-arithmetic machines where the least
- * significant byte has the lowest address.
- * #define IEEE_BIG_ENDIAN for IEEE-arithmetic machines where the most
- * significant byte has the lowest address.
- * #define Long int on machines with 32-bit ints and 64-bit longs.
- * #define IBM for IBM mainframe-style floating-point arithmetic.
- * #define VAX for VAX-style floating-point arithmetic (D_floating).
- * #define No_leftright to omit left-right logic in fast floating-point
- * computation of dtoa.
- * #define Honor_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3
- * and strtod and dtoa should round accordingly.
- * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3
- * and Honor_FLT_ROUNDS is not #defined.
- * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines
- * that use extended-precision instructions to compute rounded
- * products and quotients) with IBM.
- * #define ROUND_BIASED for IEEE-format with biased rounding.
- * #define Inaccurate_Divide for IEEE-format with correctly rounded
- * products but inaccurate quotients, e.g., for Intel i860.
- * #define NO_LONG_LONG on machines that do not have a "long long"
- * integer type (of >= 64 bits). On such machines, you can
- * #define Just_16 to store 16 bits per 32-bit Long when doing
- * high-precision integer arithmetic. Whether this speeds things
- * up or slows things down depends on the machine and the number
- * being converted. If long long is available and the name is
- * something other than "long long", #define Llong to be the name,
- * and if "unsigned Llong" does not work as an unsigned version of
- * Llong, #define #ULLong to be the corresponding unsigned type.
- * #define KR_headers for old-style C function headers.
- * #define Bad_float_h if your system lacks a float.h or if it does not
- * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP,
- * FLT_RADIX, FLT_ROUNDS, and DBL_MAX.
- * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n)
- * if memory is available and otherwise does something you deem
- * appropriate. If MALLOC is undefined, malloc will be invoked
- * directly -- and assumed always to succeed.
- * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making
- * memory allocations from a private pool of memory when possible.
- * When used, the private pool is PRIVATE_MEM bytes long: 2304 bytes,
- * unless #defined to be a different length. This default length
- * suffices to get rid of MALLOC calls except for unusual cases,
- * such as decimal-to-binary conversion of a very long string of
- * digits. The longest string dtoa can return is about 751 bytes
- * long. For conversions by strtod of strings of 800 digits and
- * all dtoa conversions in single-threaded executions with 8-byte
- * pointers, PRIVATE_MEM >= 7400 appears to suffice; with 4-byte
- * pointers, PRIVATE_MEM >= 7112 appears adequate.
- * #define INFNAN_CHECK on IEEE systems to cause strtod to check for
- * Infinity and NaN (case insensitively). On some systems (e.g.,
- * some HP systems), it may be necessary to #define NAN_WORD0
- * appropriately -- to the most significant word of a quiet NaN.
- * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.)
- * When INFNAN_CHECK is #defined and No_Hex_NaN is not #defined,
- * strtod also accepts (case insensitively) strings of the form
- * NaN(x), where x is a string of hexadecimal digits and spaces;
- * if there is only one string of hexadecimal digits, it is taken
- * for the 52 fraction bits of the resulting NaN; if there are two
- * or more strings of hex digits, the first is for the high 20 bits,
- * the second and subsequent for the low 32 bits, with intervening
- * white space ignored; but if this results in none of the 52
- * fraction bits being on (an IEEE Infinity symbol), then NAN_WORD0
- * and NAN_WORD1 are used instead.
- * #define MULTIPLE_THREADS if the system offers preemptively scheduled
- * multiple threads. In this case, you must provide (or suitably
- * #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed
- * by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed
- * in pow5mult, ensures lazy evaluation of only one copy of high
- * powers of 5; omitting this lock would introduce a small
- * probability of wasting memory, but would otherwise be harmless.)
- * You must also invoke freedtoa(s) to free the value s returned by
- * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined.
- * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that
- * avoids underflows on inputs whose result does not underflow.
- * If you #define NO_IEEE_Scale on a machine that uses IEEE-format
- * floating-point numbers and flushes underflows to zero rather
- * than implementing gradual underflow, then you must also #define
- * Sudden_Underflow.
- * #define YES_ALIAS to permit aliasing certain double values with
- * arrays of ULongs. This leads to slightly better code with
- * some compilers and was always used prior to 19990916, but it
- * is not strictly legal and can cause trouble with aggressively
- * optimizing compilers (e.g., gcc 2.95.1 under -O2).
- * #define USE_LOCALE to use the current locale's decimal_point value.
- * #define SET_INEXACT if IEEE arithmetic is being used and extra
- * computation should be done to set the inexact flag when the
- * result is inexact and avoid setting inexact when the result
- * is exact. In this case, dtoa.c must be compiled in
- * an environment, perhaps provided by #include "dtoa.c" in a
- * suitable wrapper, that defines two functions,
- * int get_inexact(void);
- * void clear_inexact(void);
- * such that get_inexact() returns a nonzero value if the
- * inexact bit is already set, and clear_inexact() sets the
- * inexact bit to 0. When SET_INEXACT is #defined, strtod
- * also does extra computations to set the underflow and overflow
- * flags when appropriate (i.e., when the result is tiny and
- * inexact or when it is a numeric value rounded to +-infinity).
- * #define NO_ERRNO if strtod should not assign errno = ERANGE when
- * the result overflows to +-Infinity or underflows to 0.
- */
-
-#ifdef WORDS_BIGENDIAN
-#define IEEE_BIG_ENDIAN
-#else
-#define IEEE_LITTLE_ENDIAN
-#endif
-
-#ifdef __vax__
-#define VAX
-#undef IEEE_BIG_ENDIAN
-#undef IEEE_LITTLE_ENDIAN
-#endif
-
-#if defined(__arm__) && !defined(__VFP_FP__)
-#define IEEE_BIG_ENDIAN
-#undef IEEE_LITTLE_ENDIAN
-#endif
-
-#undef Long
-#undef ULong
-
-#include <limits.h>
-
-#if (INT_MAX >> 30) && !(INT_MAX >> 31)
-#define Long int
-#define ULong unsigned int
-#elif (LONG_MAX >> 30) && !(LONG_MAX >> 31)
-#define Long long int
-#define ULong unsigned long int
-#else
-#error No 32bit integer
-#endif
-
-#if HAVE_LONG_LONG
-#define Llong LONG_LONG
-#else
-#define NO_LONG_LONG
-#endif
-
-#ifdef DEBUG
-#include <stdio.h>
-#define Bug(x) {fprintf(stderr, "%s\n", (x)); exit(EXIT_FAILURE);}
-#endif
-
-#ifndef ISDIGIT
-#include <ctype.h>
-#define ISDIGIT(c) isdigit(c)
-#endif
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-
-#ifdef USE_LOCALE
-#include <locale.h>
-#endif
-
-#ifdef MALLOC
-extern void *MALLOC(size_t);
-#else
-#define MALLOC xmalloc
-#endif
-#ifdef FREE
-extern void FREE(void*);
-#else
-#define FREE xfree
-#endif
-#ifndef NO_SANITIZE
-#define NO_SANITIZE(x, y) y
-#endif
-
-#ifndef Omit_Private_Memory
-#ifndef PRIVATE_MEM
-#define PRIVATE_MEM 2304
-#endif
-#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double))
-static double private_mem[PRIVATE_mem], *pmem_next = private_mem;
-#endif
-
-#undef IEEE_Arith
-#undef Avoid_Underflow
-#ifdef IEEE_BIG_ENDIAN
-#define IEEE_Arith
-#endif
-#ifdef IEEE_LITTLE_ENDIAN
-#define IEEE_Arith
-#endif
-
-#ifdef Bad_float_h
-
-#ifdef IEEE_Arith
-#define DBL_DIG 15
-#define DBL_MAX_10_EXP 308
-#define DBL_MAX_EXP 1024
-#define FLT_RADIX 2
-#endif /*IEEE_Arith*/
-
-#ifdef IBM
-#define DBL_DIG 16
-#define DBL_MAX_10_EXP 75
-#define DBL_MAX_EXP 63
-#define FLT_RADIX 16
-#define DBL_MAX 7.2370055773322621e+75
-#endif
-
-#ifdef VAX
-#define DBL_DIG 16
-#define DBL_MAX_10_EXP 38
-#define DBL_MAX_EXP 127
-#define FLT_RADIX 2
-#define DBL_MAX 1.7014118346046923e+38
-#endif
-
-#ifndef LONG_MAX
-#define LONG_MAX 2147483647
-#endif
-
-#else /* ifndef Bad_float_h */
-#include <float.h>
-#endif /* Bad_float_h */
-
-#include <math.h>
-
-#ifdef __cplusplus
-extern "C" {
-#if 0
-} /* satisfy cc-mode */
-#endif
-#endif
-
-#ifndef hexdigit
-static const char hexdigit[] = "0123456789abcdef0123456789ABCDEF";
-#endif
-
-#if defined(IEEE_LITTLE_ENDIAN) + defined(IEEE_BIG_ENDIAN) + defined(VAX) + defined(IBM) != 1
-Exactly one of IEEE_LITTLE_ENDIAN, IEEE_BIG_ENDIAN, VAX, or IBM should be defined.
-#endif
-
-typedef union { double d; ULong L[2]; } U;
-
-#ifdef YES_ALIAS
-typedef double double_u;
-# define dval(x) (x)
-# ifdef IEEE_LITTLE_ENDIAN
-# define word0(x) (((ULong *)&(x))[1])
-# define word1(x) (((ULong *)&(x))[0])
-# else
-# define word0(x) (((ULong *)&(x))[0])
-# define word1(x) (((ULong *)&(x))[1])
-# endif
-#else
-typedef U double_u;
-# ifdef IEEE_LITTLE_ENDIAN
-# define word0(x) ((x).L[1])
-# define word1(x) ((x).L[0])
-# else
-# define word0(x) ((x).L[0])
-# define word1(x) ((x).L[1])
-# endif
-# define dval(x) ((x).d)
-#endif
-
-/* The following definition of Storeinc is appropriate for MIPS processors.
- * An alternative that might be better on some machines is
- * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff)
- */
-#if defined(IEEE_LITTLE_ENDIAN) + defined(VAX) + defined(__arm__)
-#define Storeinc(a,b,c) (((unsigned short *)(a))[1] = (unsigned short)(b), \
-((unsigned short *)(a))[0] = (unsigned short)(c), (a)++)
-#else
-#define Storeinc(a,b,c) (((unsigned short *)(a))[0] = (unsigned short)(b), \
-((unsigned short *)(a))[1] = (unsigned short)(c), (a)++)
-#endif
-
-/* #define P DBL_MANT_DIG */
-/* Ten_pmax = floor(P*log(2)/log(5)) */
-/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */
-/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */
-/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */
-
-#ifdef IEEE_Arith
-#define Exp_shift 20
-#define Exp_shift1 20
-#define Exp_msk1 0x100000
-#define Exp_msk11 0x100000
-#define Exp_mask 0x7ff00000
-#define P 53
-#define Bias 1023
-#define Emin (-1022)
-#define Exp_1 0x3ff00000
-#define Exp_11 0x3ff00000
-#define Ebits 11
-#define Frac_mask 0xfffff
-#define Frac_mask1 0xfffff
-#define Ten_pmax 22
-#define Bletch 0x10
-#define Bndry_mask 0xfffff
-#define Bndry_mask1 0xfffff
-#define LSB 1
-#define Sign_bit 0x80000000
-#define Log2P 1
-#define Tiny0 0
-#define Tiny1 1
-#define Quick_max 14
-#define Int_max 14
-#ifndef NO_IEEE_Scale
-#define Avoid_Underflow
-#ifdef Flush_Denorm /* debugging option */
-#undef Sudden_Underflow
-#endif
-#endif
-
-#ifndef Flt_Rounds
-#ifdef FLT_ROUNDS
-#define Flt_Rounds FLT_ROUNDS
-#else
-#define Flt_Rounds 1
-#endif
-#endif /*Flt_Rounds*/
-
-#ifdef Honor_FLT_ROUNDS
-#define Rounding rounding
-#undef Check_FLT_ROUNDS
-#define Check_FLT_ROUNDS
-#else
-#define Rounding Flt_Rounds
-#endif
-
-#else /* ifndef IEEE_Arith */
-#undef Check_FLT_ROUNDS
-#undef Honor_FLT_ROUNDS
-#undef SET_INEXACT
-#undef Sudden_Underflow
-#define Sudden_Underflow
-#ifdef IBM
-#undef Flt_Rounds
-#define Flt_Rounds 0
-#define Exp_shift 24
-#define Exp_shift1 24
-#define Exp_msk1 0x1000000
-#define Exp_msk11 0x1000000
-#define Exp_mask 0x7f000000
-#define P 14
-#define Bias 65
-#define Exp_1 0x41000000
-#define Exp_11 0x41000000
-#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */
-#define Frac_mask 0xffffff
-#define Frac_mask1 0xffffff
-#define Bletch 4
-#define Ten_pmax 22
-#define Bndry_mask 0xefffff
-#define Bndry_mask1 0xffffff
-#define LSB 1
-#define Sign_bit 0x80000000
-#define Log2P 4
-#define Tiny0 0x100000
-#define Tiny1 0
-#define Quick_max 14
-#define Int_max 15
-#else /* VAX */
-#undef Flt_Rounds
-#define Flt_Rounds 1
-#define Exp_shift 23
-#define Exp_shift1 7
-#define Exp_msk1 0x80
-#define Exp_msk11 0x800000
-#define Exp_mask 0x7f80
-#define P 56
-#define Bias 129
-#define Exp_1 0x40800000
-#define Exp_11 0x4080
-#define Ebits 8
-#define Frac_mask 0x7fffff
-#define Frac_mask1 0xffff007f
-#define Ten_pmax 24
-#define Bletch 2
-#define Bndry_mask 0xffff007f
-#define Bndry_mask1 0xffff007f
-#define LSB 0x10000
-#define Sign_bit 0x8000
-#define Log2P 1
-#define Tiny0 0x80
-#define Tiny1 0
-#define Quick_max 15
-#define Int_max 15
-#endif /* IBM, VAX */
-#endif /* IEEE_Arith */
-
-#ifndef IEEE_Arith
-#define ROUND_BIASED
-#endif
-
-#ifdef RND_PRODQUOT
-#define rounded_product(a,b) ((a) = rnd_prod((a), (b)))
-#define rounded_quotient(a,b) ((a) = rnd_quot((a), (b)))
-extern double rnd_prod(double, double), rnd_quot(double, double);
-#else
-#define rounded_product(a,b) ((a) *= (b))
-#define rounded_quotient(a,b) ((a) /= (b))
-#endif
-
-#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1))
-#define Big1 0xffffffff
-
-#ifndef Pack_32
-#define Pack_32
-#endif
-
-#define FFFFFFFF 0xffffffffUL
-
-#ifdef NO_LONG_LONG
-#undef ULLong
-#ifdef Just_16
-#undef Pack_32
-/* When Pack_32 is not defined, we store 16 bits per 32-bit Long.
- * This makes some inner loops simpler and sometimes saves work
- * during multiplications, but it often seems to make things slightly
- * slower. Hence the default is now to store 32 bits per Long.
- */
-#endif
-#else /* long long available */
-#ifndef Llong
-#define Llong long long
-#endif
-#ifndef ULLong
-#define ULLong unsigned Llong
-#endif
-#endif /* NO_LONG_LONG */
-
-#define MULTIPLE_THREADS 1
-
-#ifndef MULTIPLE_THREADS
-#define ACQUIRE_DTOA_LOCK(n) /*nothing*/
-#define FREE_DTOA_LOCK(n) /*nothing*/
-#else
-#define ACQUIRE_DTOA_LOCK(n) /*unused right now*/
-#define FREE_DTOA_LOCK(n) /*unused right now*/
-#endif
-
-#ifndef ATOMIC_PTR_CAS
-#define ATOMIC_PTR_CAS(var, old, new) ((var) = (new), (old))
-#endif
-#ifndef LIKELY
-#define LIKELY(x) (x)
-#endif
-#ifndef UNLIKELY
-#define UNLIKELY(x) (x)
-#endif
-#ifndef ASSUME
-#define ASSUME(x) (void)(x)
-#endif
-
-#define Kmax 15
-
-struct Bigint {
- struct Bigint *next;
- int k, maxwds, sign, wds;
- ULong x[1];
-};
-
-typedef struct Bigint Bigint;
-
-static Bigint *freelist[Kmax+1];
-
-static Bigint *
-Balloc(int k)
-{
- int x;
- Bigint *rv;
-#ifndef Omit_Private_Memory
- size_t len;
-#endif
-
- rv = 0;
- ACQUIRE_DTOA_LOCK(0);
- if (k <= Kmax) {
- rv = freelist[k];
- while (rv) {
- Bigint *rvn = rv;
- rv = ATOMIC_PTR_CAS(freelist[k], rv, rv->next);
- if (LIKELY(rvn == rv)) {
- ASSUME(rv);
- break;
- }
- }
- }
- if (!rv) {
- x = 1 << k;
-#ifdef Omit_Private_Memory
- rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong));
-#else
- len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1)
- /sizeof(double);
- if (k <= Kmax) {
- double *pnext = pmem_next;
- while (pnext - private_mem + len <= PRIVATE_mem) {
- double *p = pnext;
- pnext = ATOMIC_PTR_CAS(pmem_next, pnext, pnext + len);
- if (LIKELY(p == pnext)) {
- rv = (Bigint*)pnext;
- ASSUME(rv);
- break;
- }
- }
- }
- if (!rv)
- rv = (Bigint*)MALLOC(len*sizeof(double));
-#endif
- rv->k = k;
- rv->maxwds = x;
- }
- FREE_DTOA_LOCK(0);
- rv->sign = rv->wds = 0;
- return rv;
-}
-
-static void
-Bfree(Bigint *v)
-{
- Bigint *vn;
- if (v) {
- if (v->k > Kmax) {
- FREE(v);
- return;
- }
- ACQUIRE_DTOA_LOCK(0);
- do {
- vn = v->next = freelist[v->k];
- } while (UNLIKELY(ATOMIC_PTR_CAS(freelist[v->k], vn, v) != vn));
- FREE_DTOA_LOCK(0);
- }
-}
-
-#define Bcopy(x,y) memcpy((char *)&(x)->sign, (char *)&(y)->sign, \
-(y)->wds*sizeof(Long) + 2*sizeof(int))
-
-static Bigint *
-multadd(Bigint *b, int m, int a) /* multiply by m and add a */
-{
- int i, wds;
- ULong *x;
-#ifdef ULLong
- ULLong carry, y;
-#else
- ULong carry, y;
-#ifdef Pack_32
- ULong xi, z;
-#endif
-#endif
- Bigint *b1;
-
- wds = b->wds;
- x = b->x;
- i = 0;
- carry = a;
- do {
-#ifdef ULLong
- y = *x * (ULLong)m + carry;
- carry = y >> 32;
- *x++ = (ULong)(y & FFFFFFFF);
-#else
-#ifdef Pack_32
- xi = *x;
- y = (xi & 0xffff) * m + carry;
- z = (xi >> 16) * m + (y >> 16);
- carry = z >> 16;
- *x++ = (z << 16) + (y & 0xffff);
-#else
- y = *x * m + carry;
- carry = y >> 16;
- *x++ = y & 0xffff;
-#endif
-#endif
- } while (++i < wds);
- if (carry) {
- if (wds >= b->maxwds) {
- b1 = Balloc(b->k+1);
- Bcopy(b1, b);
- Bfree(b);
- b = b1;
- }
- b->x[wds++] = (ULong)carry;
- b->wds = wds;
- }
- return b;
-}
-
-static Bigint *
-s2b(const char *s, int nd0, int nd, ULong y9)
-{
- Bigint *b;
- int i, k;
- Long x, y;
-
- x = (nd + 8) / 9;
- for (k = 0, y = 1; x > y; y <<= 1, k++) ;
-#ifdef Pack_32
- b = Balloc(k);
- b->x[0] = y9;
- b->wds = 1;
-#else
- b = Balloc(k+1);
- b->x[0] = y9 & 0xffff;
- b->wds = (b->x[1] = y9 >> 16) ? 2 : 1;
-#endif
-
- i = 9;
- if (9 < nd0) {
- s += 9;
- do {
- b = multadd(b, 10, *s++ - '0');
- } while (++i < nd0);
- s++;
- }
- else
- s += 10;
- for (; i < nd; i++)
- b = multadd(b, 10, *s++ - '0');
- return b;
-}
-
-static int
-hi0bits(register ULong x)
-{
- register int k = 0;
-
- if (!(x & 0xffff0000)) {
- k = 16;
- x <<= 16;
- }
- if (!(x & 0xff000000)) {
- k += 8;
- x <<= 8;
- }
- if (!(x & 0xf0000000)) {
- k += 4;
- x <<= 4;
- }
- if (!(x & 0xc0000000)) {
- k += 2;
- x <<= 2;
- }
- if (!(x & 0x80000000)) {
- k++;
- if (!(x & 0x40000000))
- return 32;
- }
- return k;
-}
-
-static int
-lo0bits(ULong *y)
-{
- register int k;
- register ULong x = *y;
-
- if (x & 7) {
- if (x & 1)
- return 0;
- if (x & 2) {
- *y = x >> 1;
- return 1;
- }
- *y = x >> 2;
- return 2;
- }
- k = 0;
- if (!(x & 0xffff)) {
- k = 16;
- x >>= 16;
- }
- if (!(x & 0xff)) {
- k += 8;
- x >>= 8;
- }
- if (!(x & 0xf)) {
- k += 4;
- x >>= 4;
- }
- if (!(x & 0x3)) {
- k += 2;
- x >>= 2;
- }
- if (!(x & 1)) {
- k++;
- x >>= 1;
- if (!x)
- return 32;
- }
- *y = x;
- return k;
-}
-
-static Bigint *
-i2b(int i)
-{
- Bigint *b;
-
- b = Balloc(1);
- b->x[0] = i;
- b->wds = 1;
- return b;
-}
-
-static Bigint *
-mult(Bigint *a, Bigint *b)
-{
- Bigint *c;
- int k, wa, wb, wc;
- ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0;
- ULong y;
-#ifdef ULLong
- ULLong carry, z;
-#else
- ULong carry, z;
-#ifdef Pack_32
- ULong z2;
-#endif
-#endif
-
- if (a->wds < b->wds) {
- c = a;
- a = b;
- b = c;
- }
- k = a->k;
- wa = a->wds;
- wb = b->wds;
- wc = wa + wb;
- if (wc > a->maxwds)
- k++;
- c = Balloc(k);
- for (x = c->x, xa = x + wc; x < xa; x++)
- *x = 0;
- xa = a->x;
- xae = xa + wa;
- xb = b->x;
- xbe = xb + wb;
- xc0 = c->x;
-#ifdef ULLong
- for (; xb < xbe; xc0++) {
- if ((y = *xb++) != 0) {
- x = xa;
- xc = xc0;
- carry = 0;
- do {
- z = *x++ * (ULLong)y + *xc + carry;
- carry = z >> 32;
- *xc++ = (ULong)(z & FFFFFFFF);
- } while (x < xae);
- *xc = (ULong)carry;
- }
- }
-#else
-#ifdef Pack_32
- for (; xb < xbe; xb++, xc0++) {
- if ((y = *xb & 0xffff) != 0) {
- x = xa;
- xc = xc0;
- carry = 0;
- do {
- z = (*x & 0xffff) * y + (*xc & 0xffff) + carry;
- carry = z >> 16;
- z2 = (*x++ >> 16) * y + (*xc >> 16) + carry;
- carry = z2 >> 16;
- Storeinc(xc, z2, z);
- } while (x < xae);
- *xc = (ULong)carry;
- }
- if ((y = *xb >> 16) != 0) {
- x = xa;
- xc = xc0;
- carry = 0;
- z2 = *xc;
- do {
- z = (*x & 0xffff) * y + (*xc >> 16) + carry;
- carry = z >> 16;
- Storeinc(xc, z, z2);
- z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry;
- carry = z2 >> 16;
- } while (x < xae);
- *xc = z2;
- }
- }
-#else
- for (; xb < xbe; xc0++) {
- if (y = *xb++) {
- x = xa;
- xc = xc0;
- carry = 0;
- do {
- z = *x++ * y + *xc + carry;
- carry = z >> 16;
- *xc++ = z & 0xffff;
- } while (x < xae);
- *xc = (ULong)carry;
- }
- }
-#endif
-#endif
- for (xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ;
- c->wds = wc;
- return c;
-}
-
-static Bigint *p5s;
-
-static Bigint *
-pow5mult(Bigint *b, int k)
-{
- Bigint *b1, *p5, *p51;
- Bigint *p5tmp;
- int i;
- static const int p05[3] = { 5, 25, 125 };
-
- if ((i = k & 3) != 0)
- b = multadd(b, p05[i-1], 0);
-
- if (!(k >>= 2))
- return b;
- if (!(p5 = p5s)) {
- /* first time */
- ACQUIRE_DTOA_LOCK(1);
- if (!(p5 = p5s)) {
- p5 = i2b(625);
- p5->next = 0;
- p5tmp = ATOMIC_PTR_CAS(p5s, NULL, p5);
- if (UNLIKELY(p5tmp)) {
- Bfree(p5);
- p5 = p5tmp;
- }
- }
- FREE_DTOA_LOCK(1);
- }
- for (;;) {
- if (k & 1) {
- b1 = mult(b, p5);
- Bfree(b);
- b = b1;
- }
- if (!(k >>= 1))
- break;
- if (!(p51 = p5->next)) {
- ACQUIRE_DTOA_LOCK(1);
- if (!(p51 = p5->next)) {
- p51 = mult(p5,p5);
- p51->next = 0;
- p5tmp = ATOMIC_PTR_CAS(p5->next, NULL, p51);
- if (UNLIKELY(p5tmp)) {
- Bfree(p51);
- p51 = p5tmp;
- }
- }
- FREE_DTOA_LOCK(1);
- }
- p5 = p51;
- }
- return b;
-}
-
-static Bigint *
-lshift(Bigint *b, int k)
-{
- int i, k1, n, n1;
- Bigint *b1;
- ULong *x, *x1, *xe, z;
-
-#ifdef Pack_32
- n = k >> 5;
-#else
- n = k >> 4;
-#endif
- k1 = b->k;
- n1 = n + b->wds + 1;
- for (i = b->maxwds; n1 > i; i <<= 1)
- k1++;
- b1 = Balloc(k1);
- x1 = b1->x;
- for (i = 0; i < n; i++)
- *x1++ = 0;
- x = b->x;
- xe = x + b->wds;
-#ifdef Pack_32
- if (k &= 0x1f) {
- k1 = 32 - k;
- z = 0;
- do {
- *x1++ = *x << k | z;
- z = *x++ >> k1;
- } while (x < xe);
- if ((*x1 = z) != 0)
- ++n1;
- }
-#else
- if (k &= 0xf) {
- k1 = 16 - k;
- z = 0;
- do {
- *x1++ = *x << k & 0xffff | z;
- z = *x++ >> k1;
- } while (x < xe);
- if (*x1 = z)
- ++n1;
- }
-#endif
- else
- do {
- *x1++ = *x++;
- } while (x < xe);
- b1->wds = n1 - 1;
- Bfree(b);
- return b1;
-}
-
-static int
-cmp(Bigint *a, Bigint *b)
-{
- ULong *xa, *xa0, *xb, *xb0;
- int i, j;
-
- i = a->wds;
- j = b->wds;
-#ifdef DEBUG
- if (i > 1 && !a->x[i-1])
- Bug("cmp called with a->x[a->wds-1] == 0");
- if (j > 1 && !b->x[j-1])
- Bug("cmp called with b->x[b->wds-1] == 0");
-#endif
- if (i -= j)
- return i;
- xa0 = a->x;
- xa = xa0 + j;
- xb0 = b->x;
- xb = xb0 + j;
- for (;;) {
- if (*--xa != *--xb)
- return *xa < *xb ? -1 : 1;
- if (xa <= xa0)
- break;
- }
- return 0;
-}
-
-NO_SANITIZE("unsigned-integer-overflow", static Bigint * diff(Bigint *a, Bigint *b));
-static Bigint *
-diff(Bigint *a, Bigint *b)
-{
- Bigint *c;
- int i, wa, wb;
- ULong *xa, *xae, *xb, *xbe, *xc;
-#ifdef ULLong
- ULLong borrow, y;
-#else
- ULong borrow, y;
-#ifdef Pack_32
- ULong z;
-#endif
-#endif
-
- i = cmp(a,b);
- if (!i) {
- c = Balloc(0);
- c->wds = 1;
- c->x[0] = 0;
- return c;
- }
- if (i < 0) {
- c = a;
- a = b;
- b = c;
- i = 1;
- }
- else
- i = 0;
- c = Balloc(a->k);
- c->sign = i;
- wa = a->wds;
- xa = a->x;
- xae = xa + wa;
- wb = b->wds;
- xb = b->x;
- xbe = xb + wb;
- xc = c->x;
- borrow = 0;
-#ifdef ULLong
- do {
- y = (ULLong)*xa++ - *xb++ - borrow;
- borrow = y >> 32 & (ULong)1;
- *xc++ = (ULong)(y & FFFFFFFF);
- } while (xb < xbe);
- while (xa < xae) {
- y = *xa++ - borrow;
- borrow = y >> 32 & (ULong)1;
- *xc++ = (ULong)(y & FFFFFFFF);
- }
-#else
-#ifdef Pack_32
- do {
- y = (*xa & 0xffff) - (*xb & 0xffff) - borrow;
- borrow = (y & 0x10000) >> 16;
- z = (*xa++ >> 16) - (*xb++ >> 16) - borrow;
- borrow = (z & 0x10000) >> 16;
- Storeinc(xc, z, y);
- } while (xb < xbe);
- while (xa < xae) {
- y = (*xa & 0xffff) - borrow;
- borrow = (y & 0x10000) >> 16;
- z = (*xa++ >> 16) - borrow;
- borrow = (z & 0x10000) >> 16;
- Storeinc(xc, z, y);
- }
-#else
- do {
- y = *xa++ - *xb++ - borrow;
- borrow = (y & 0x10000) >> 16;
- *xc++ = y & 0xffff;
- } while (xb < xbe);
- while (xa < xae) {
- y = *xa++ - borrow;
- borrow = (y & 0x10000) >> 16;
- *xc++ = y & 0xffff;
- }
-#endif
-#endif
- while (!*--xc)
- wa--;
- c->wds = wa;
- return c;
-}
-
-static double
-ulp(double x_)
-{
- register Long L;
- double_u x, a;
- dval(x) = x_;
-
- L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1;
-#ifndef Avoid_Underflow
-#ifndef Sudden_Underflow
- if (L > 0) {
-#endif
-#endif
-#ifdef IBM
- L |= Exp_msk1 >> 4;
-#endif
- word0(a) = L;
- word1(a) = 0;
-#ifndef Avoid_Underflow
-#ifndef Sudden_Underflow
- }
- else {
- L = -L >> Exp_shift;
- if (L < Exp_shift) {
- word0(a) = 0x80000 >> L;
- word1(a) = 0;
- }
- else {
- word0(a) = 0;
- L -= Exp_shift;
- word1(a) = L >= 31 ? 1 : 1 << 31 - L;
- }
- }
-#endif
-#endif
- return dval(a);
-}
-
-static double
-b2d(Bigint *a, int *e)
-{
- ULong *xa, *xa0, w, y, z;
- int k;
- double_u d;
-#ifdef VAX
- ULong d0, d1;
-#else
-#define d0 word0(d)
-#define d1 word1(d)
-#endif
-
- xa0 = a->x;
- xa = xa0 + a->wds;
- y = *--xa;
-#ifdef DEBUG
- if (!y) Bug("zero y in b2d");
-#endif
- k = hi0bits(y);
- *e = 32 - k;
-#ifdef Pack_32
- if (k < Ebits) {
- d0 = Exp_1 | y >> (Ebits - k);
- w = xa > xa0 ? *--xa : 0;
- d1 = y << ((32-Ebits) + k) | w >> (Ebits - k);
- goto ret_d;
- }
- z = xa > xa0 ? *--xa : 0;
- if (k -= Ebits) {
- d0 = Exp_1 | y << k | z >> (32 - k);
- y = xa > xa0 ? *--xa : 0;
- d1 = z << k | y >> (32 - k);
- }
- else {
- d0 = Exp_1 | y;
- d1 = z;
- }
-#else
- if (k < Ebits + 16) {
- z = xa > xa0 ? *--xa : 0;
- d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k;
- w = xa > xa0 ? *--xa : 0;
- y = xa > xa0 ? *--xa : 0;
- d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k;
- goto ret_d;
- }
- z = xa > xa0 ? *--xa : 0;
- w = xa > xa0 ? *--xa : 0;
- k -= Ebits + 16;
- d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k;
- y = xa > xa0 ? *--xa : 0;
- d1 = w << k + 16 | y << k;
-#endif
-ret_d:
-#ifdef VAX
- word0(d) = d0 >> 16 | d0 << 16;
- word1(d) = d1 >> 16 | d1 << 16;
-#else
-#undef d0
-#undef d1
-#endif
- return dval(d);
-}
-
-static Bigint *
-d2b(double d_, int *e, int *bits)
-{
- double_u d;
- Bigint *b;
- int de, k;
- ULong *x, y, z;
-#ifndef Sudden_Underflow
- int i;
-#endif
-#ifdef VAX
- ULong d0, d1;
-#endif
- dval(d) = d_;
-#ifdef VAX
- d0 = word0(d) >> 16 | word0(d) << 16;
- d1 = word1(d) >> 16 | word1(d) << 16;
-#else
-#define d0 word0(d)
-#define d1 word1(d)
-#endif
-
-#ifdef Pack_32
- b = Balloc(1);
-#else
- b = Balloc(2);
-#endif
- x = b->x;
-
- z = d0 & Frac_mask;
- d0 &= 0x7fffffff; /* clear sign bit, which we ignore */
-#ifdef Sudden_Underflow
- de = (int)(d0 >> Exp_shift);
-#ifndef IBM
- z |= Exp_msk11;
-#endif
-#else
- if ((de = (int)(d0 >> Exp_shift)) != 0)
- z |= Exp_msk1;
-#endif
-#ifdef Pack_32
- if ((y = d1) != 0) {
- if ((k = lo0bits(&y)) != 0) {
- x[0] = y | z << (32 - k);
- z >>= k;
- }
- else
- x[0] = y;
-#ifndef Sudden_Underflow
- i =
-#endif
- b->wds = (x[1] = z) ? 2 : 1;
- }
- else {
-#ifdef DEBUG
- if (!z)
- Bug("Zero passed to d2b");
-#endif
- k = lo0bits(&z);
- x[0] = z;
-#ifndef Sudden_Underflow
- i =
-#endif
- b->wds = 1;
- k += 32;
- }
-#else
- if (y = d1) {
- if (k = lo0bits(&y))
- if (k >= 16) {
- x[0] = y | z << 32 - k & 0xffff;
- x[1] = z >> k - 16 & 0xffff;
- x[2] = z >> k;
- i = 2;
- }
- else {
- x[0] = y & 0xffff;
- x[1] = y >> 16 | z << 16 - k & 0xffff;
- x[2] = z >> k & 0xffff;
- x[3] = z >> k+16;
- i = 3;
- }
- else {
- x[0] = y & 0xffff;
- x[1] = y >> 16;
- x[2] = z & 0xffff;
- x[3] = z >> 16;
- i = 3;
- }
- }
- else {
-#ifdef DEBUG
- if (!z)
- Bug("Zero passed to d2b");
-#endif
- k = lo0bits(&z);
- if (k >= 16) {
- x[0] = z;
- i = 0;
- }
- else {
- x[0] = z & 0xffff;
- x[1] = z >> 16;
- i = 1;
- }
- k += 32;
- }
- while (!x[i])
- --i;
- b->wds = i + 1;
-#endif
-#ifndef Sudden_Underflow
- if (de) {
-#endif
-#ifdef IBM
- *e = (de - Bias - (P-1) << 2) + k;
- *bits = 4*P + 8 - k - hi0bits(word0(d) & Frac_mask);
-#else
- *e = de - Bias - (P-1) + k;
- *bits = P - k;
-#endif
-#ifndef Sudden_Underflow
- }
- else {
- *e = de - Bias - (P-1) + 1 + k;
-#ifdef Pack_32
- *bits = 32*i - hi0bits(x[i-1]);
-#else
- *bits = (i+2)*16 - hi0bits(x[i]);
-#endif
- }
-#endif
- return b;
-}
-#undef d0
-#undef d1
-
-static double
-ratio(Bigint *a, Bigint *b)
-{
- double_u da, db;
- int k, ka, kb;
-
- dval(da) = b2d(a, &ka);
- dval(db) = b2d(b, &kb);
-#ifdef Pack_32
- k = ka - kb + 32*(a->wds - b->wds);
-#else
- k = ka - kb + 16*(a->wds - b->wds);
-#endif
-#ifdef IBM
- if (k > 0) {
- word0(da) += (k >> 2)*Exp_msk1;
- if (k &= 3)
- dval(da) *= 1 << k;
- }
- else {
- k = -k;
- word0(db) += (k >> 2)*Exp_msk1;
- if (k &= 3)
- dval(db) *= 1 << k;
- }
-#else
- if (k > 0)
- word0(da) += k*Exp_msk1;
- else {
- k = -k;
- word0(db) += k*Exp_msk1;
- }
-#endif
- return dval(da) / dval(db);
-}
-
-static const double
-tens[] = {
- 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
- 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
- 1e20, 1e21, 1e22
-#ifdef VAX
- , 1e23, 1e24
-#endif
-};
-
-static const double
-#ifdef IEEE_Arith
-bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 };
-static const double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128,
-#ifdef Avoid_Underflow
- 9007199254740992.*9007199254740992.e-256
- /* = 2^106 * 1e-53 */
-#else
- 1e-256
-#endif
-};
-/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */
-/* flag unnecessarily. It leads to a song and dance at the end of strtod. */
-#define Scale_Bit 0x10
-#define n_bigtens 5
-#else
-#ifdef IBM
-bigtens[] = { 1e16, 1e32, 1e64 };
-static const double tinytens[] = { 1e-16, 1e-32, 1e-64 };
-#define n_bigtens 3
-#else
-bigtens[] = { 1e16, 1e32 };
-static const double tinytens[] = { 1e-16, 1e-32 };
-#define n_bigtens 2
-#endif
-#endif
-
-#ifndef IEEE_Arith
-#undef INFNAN_CHECK
-#endif
-
-#ifdef INFNAN_CHECK
-
-#ifndef NAN_WORD0
-#define NAN_WORD0 0x7ff80000
-#endif
-
-#ifndef NAN_WORD1
-#define NAN_WORD1 0
-#endif
-
-static int
-match(const char **sp, char *t)
-{
- int c, d;
- const char *s = *sp;
-
- while (d = *t++) {
- if ((c = *++s) >= 'A' && c <= 'Z')
- c += 'a' - 'A';
- if (c != d)
- return 0;
- }
- *sp = s + 1;
- return 1;
-}
-
-#ifndef No_Hex_NaN
-static void
-hexnan(double *rvp, const char **sp)
-{
- ULong c, x[2];
- const char *s;
- int havedig, udx0, xshift;
-
- x[0] = x[1] = 0;
- havedig = xshift = 0;
- udx0 = 1;
- s = *sp;
- while (c = *(const unsigned char*)++s) {
- if (c >= '0' && c <= '9')
- c -= '0';
- else if (c >= 'a' && c <= 'f')
- c += 10 - 'a';
- else if (c >= 'A' && c <= 'F')
- c += 10 - 'A';
- else if (c <= ' ') {
- if (udx0 && havedig) {
- udx0 = 0;
- xshift = 1;
- }
- continue;
- }
- else if (/*(*/ c == ')' && havedig) {
- *sp = s + 1;
- break;
- }
- else
- return; /* invalid form: don't change *sp */
- havedig = 1;
- if (xshift) {
- xshift = 0;
- x[0] = x[1];
- x[1] = 0;
- }
- if (udx0)
- x[0] = (x[0] << 4) | (x[1] >> 28);
- x[1] = (x[1] << 4) | c;
- }
- if ((x[0] &= 0xfffff) || x[1]) {
- word0(*rvp) = Exp_mask | x[0];
- word1(*rvp) = x[1];
- }
-}
-#endif /*No_Hex_NaN*/
-#endif /* INFNAN_CHECK */
-
-NO_SANITIZE("unsigned-integer-overflow", double strtod(const char *s00, char **se));
-double
-strtod(const char *s00, char **se)
-{
-#ifdef Avoid_Underflow
- int scale;
-#endif
- int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign,
- e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign;
- const char *s, *s0, *s1;
- double aadj, adj;
- double_u aadj1, rv, rv0;
- Long L;
- ULong y, z;
- Bigint *bb, *bb1, *bd, *bd0, *bs, *delta;
-#ifdef SET_INEXACT
- int inexact, oldinexact;
-#endif
-#ifdef Honor_FLT_ROUNDS
- int rounding;
-#endif
-#ifdef USE_LOCALE
- const char *s2;
-#endif
-
- errno = 0;
- sign = nz0 = nz = 0;
- dval(rv) = 0.;
- for (s = s00;;s++)
- switch (*s) {
- case '-':
- sign = 1;
- /* no break */
- case '+':
- if (*++s)
- goto break2;
- /* no break */
- case 0:
- goto ret0;
- case '\t':
- case '\n':
- case '\v':
- case '\f':
- case '\r':
- case ' ':
- continue;
- default:
- goto break2;
- }
-break2:
- if (*s == '0') {
- if (s[1] == 'x' || s[1] == 'X') {
- s0 = ++s;
- adj = 0;
- aadj = 1.0;
- nd0 = -4;
-
- if (!*++s || !(s1 = strchr(hexdigit, *s))) goto ret0;
- if (*s == '0') {
- while (*++s == '0');
- s1 = strchr(hexdigit, *s);
- }
- if (s1 != NULL) {
- do {
- adj += aadj * ((s1 - hexdigit) & 15);
- nd0 += 4;
- aadj /= 16;
- } while (*++s && (s1 = strchr(hexdigit, *s)));
- }
-
- if (*s == '.') {
- dsign = 1;
- if (!*++s || !(s1 = strchr(hexdigit, *s))) goto ret0;
- if (nd0 < 0) {
- while (*s == '0') {
- s++;
- nd0 -= 4;
- }
- }
- for (; *s && (s1 = strchr(hexdigit, *s)); ++s) {
- adj += aadj * ((s1 - hexdigit) & 15);
- if ((aadj /= 16) == 0.0) {
- while (strchr(hexdigit, *++s));
- break;
- }
- }
- }
- else {
- dsign = 0;
- }
-
- if (*s == 'P' || *s == 'p') {
- dsign = 0x2C - *++s; /* +: 2B, -: 2D */
- if (abs(dsign) == 1) s++;
- else dsign = 1;
-
- nd = 0;
- c = *s;
- if (c < '0' || '9' < c) goto ret0;
- do {
- nd *= 10;
- nd += c;
- nd -= '0';
- c = *++s;
- /* Float("0x0."+("0"*267)+"1fp2095") */
- if (nd + dsign * nd0 > 2095) {
- while ('0' <= c && c <= '9') c = *++s;
- break;
- }
- } while ('0' <= c && c <= '9');
- nd0 += nd * dsign;
- }
- else {
- if (dsign) goto ret0;
- }
- dval(rv) = ldexp(adj, nd0);
- goto ret;
- }
- nz0 = 1;
- while (*++s == '0') ;
- if (!*s)
- goto ret;
- }
- s0 = s;
- y = z = 0;
- for (nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++)
- if (nd < 9)
- y = 10*y + c - '0';
- else if (nd < DBL_DIG + 2)
- z = 10*z + c - '0';
- nd0 = nd;
-#ifdef USE_LOCALE
- s1 = localeconv()->decimal_point;
- if (c == *s1) {
- c = '.';
- if (*++s1) {
- s2 = s;
- for (;;) {
- if (*++s2 != *s1) {
- c = 0;
- break;
- }
- if (!*++s1) {
- s = s2;
- break;
- }
- }
- }
- }
-#endif
- if (c == '.') {
- if (!ISDIGIT(s[1]))
- goto dig_done;
- c = *++s;
- if (!nd) {
- for (; c == '0'; c = *++s)
- nz++;
- if (c > '0' && c <= '9') {
- s0 = s;
- nf += nz;
- nz = 0;
- goto have_dig;
- }
- goto dig_done;
- }
- for (; c >= '0' && c <= '9'; c = *++s) {
-have_dig:
- nz++;
- if (nd > DBL_DIG * 4) {
- continue;
- }
- if (c -= '0') {
- nf += nz;
- for (i = 1; i < nz; i++)
- if (nd++ < 9)
- y *= 10;
- else if (nd <= DBL_DIG + 2)
- z *= 10;
- if (nd++ < 9)
- y = 10*y + c;
- else if (nd <= DBL_DIG + 2)
- z = 10*z + c;
- nz = 0;
- }
- }
- }
-dig_done:
- e = 0;
- if (c == 'e' || c == 'E') {
- if (!nd && !nz && !nz0) {
- goto ret0;
- }
- s00 = s;
- esign = 0;
- switch (c = *++s) {
- case '-':
- esign = 1;
- case '+':
- c = *++s;
- }
- if (c >= '0' && c <= '9') {
- while (c == '0')
- c = *++s;
- if (c > '0' && c <= '9') {
- L = c - '0';
- s1 = s;
- while ((c = *++s) >= '0' && c <= '9')
- L = 10*L + c - '0';
- if (s - s1 > 8 || L > 19999)
- /* Avoid confusion from exponents
- * so large that e might overflow.
- */
- e = 19999; /* safe for 16 bit ints */
- else
- e = (int)L;
- if (esign)
- e = -e;
- }
- else
- e = 0;
- }
- else
- s = s00;
- }
- if (!nd) {
- if (!nz && !nz0) {
-#ifdef INFNAN_CHECK
- /* Check for Nan and Infinity */
- switch (c) {
- case 'i':
- case 'I':
- if (match(&s,"nf")) {
- --s;
- if (!match(&s,"inity"))
- ++s;
- word0(rv) = 0x7ff00000;
- word1(rv) = 0;
- goto ret;
- }
- break;
- case 'n':
- case 'N':
- if (match(&s, "an")) {
- word0(rv) = NAN_WORD0;
- word1(rv) = NAN_WORD1;
-#ifndef No_Hex_NaN
- if (*s == '(') /*)*/
- hexnan(&rv, &s);
-#endif
- goto ret;
- }
- }
-#endif /* INFNAN_CHECK */
-ret0:
- s = s00;
- sign = 0;
- }
- goto ret;
- }
- e1 = e -= nf;
-
- /* Now we have nd0 digits, starting at s0, followed by a
- * decimal point, followed by nd-nd0 digits. The number we're
- * after is the integer represented by those digits times
- * 10**e */
-
- if (!nd0)
- nd0 = nd;
- k = nd < DBL_DIG + 2 ? nd : DBL_DIG + 2;
- dval(rv) = y;
- if (k > 9) {
-#ifdef SET_INEXACT
- if (k > DBL_DIG)
- oldinexact = get_inexact();
-#endif
- dval(rv) = tens[k - 9] * dval(rv) + z;
- }
- bd0 = bb = bd = bs = delta = 0;
- if (nd <= DBL_DIG
-#ifndef RND_PRODQUOT
-#ifndef Honor_FLT_ROUNDS
- && Flt_Rounds == 1
-#endif
-#endif
- ) {
- if (!e)
- goto ret;
- if (e > 0) {
- if (e <= Ten_pmax) {
-#ifdef VAX
- goto vax_ovfl_check;
-#else
-#ifdef Honor_FLT_ROUNDS
- /* round correctly FLT_ROUNDS = 2 or 3 */
- if (sign) {
- dval(rv) = -dval(rv);
- sign = 0;
- }
-#endif
- /* rv = */ rounded_product(dval(rv), tens[e]);
- goto ret;
-#endif
- }
- i = DBL_DIG - nd;
- if (e <= Ten_pmax + i) {
- /* A fancier test would sometimes let us do
- * this for larger i values.
- */
-#ifdef Honor_FLT_ROUNDS
- /* round correctly FLT_ROUNDS = 2 or 3 */
- if (sign) {
- dval(rv) = -dval(rv);
- sign = 0;
- }
-#endif
- e -= i;
- dval(rv) *= tens[i];
-#ifdef VAX
- /* VAX exponent range is so narrow we must
- * worry about overflow here...
- */
-vax_ovfl_check:
- word0(rv) -= P*Exp_msk1;
- /* rv = */ rounded_product(dval(rv), tens[e]);
- if ((word0(rv) & Exp_mask)
- > Exp_msk1*(DBL_MAX_EXP+Bias-1-P))
- goto ovfl;
- word0(rv) += P*Exp_msk1;
-#else
- /* rv = */ rounded_product(dval(rv), tens[e]);
-#endif
- goto ret;
- }
- }
-#ifndef Inaccurate_Divide
- else if (e >= -Ten_pmax) {
-#ifdef Honor_FLT_ROUNDS
- /* round correctly FLT_ROUNDS = 2 or 3 */
- if (sign) {
- dval(rv) = -dval(rv);
- sign = 0;
- }
-#endif
- /* rv = */ rounded_quotient(dval(rv), tens[-e]);
- goto ret;
- }
-#endif
- }
- e1 += nd - k;
-
-#ifdef IEEE_Arith
-#ifdef SET_INEXACT
- inexact = 1;
- if (k <= DBL_DIG)
- oldinexact = get_inexact();
-#endif
-#ifdef Avoid_Underflow
- scale = 0;
-#endif
-#ifdef Honor_FLT_ROUNDS
- if ((rounding = Flt_Rounds) >= 2) {
- if (sign)
- rounding = rounding == 2 ? 0 : 2;
- else
- if (rounding != 2)
- rounding = 0;
- }
-#endif
-#endif /*IEEE_Arith*/
-
- /* Get starting approximation = rv * 10**e1 */
-
- if (e1 > 0) {
- if ((i = e1 & 15) != 0)
- dval(rv) *= tens[i];
- if (e1 &= ~15) {
- if (e1 > DBL_MAX_10_EXP) {
-ovfl:
-#ifndef NO_ERRNO
- errno = ERANGE;
-#endif
- /* Can't trust HUGE_VAL */
-#ifdef IEEE_Arith
-#ifdef Honor_FLT_ROUNDS
- switch (rounding) {
- case 0: /* toward 0 */
- case 3: /* toward -infinity */
- word0(rv) = Big0;
- word1(rv) = Big1;
- break;
- default:
- word0(rv) = Exp_mask;
- word1(rv) = 0;
- }
-#else /*Honor_FLT_ROUNDS*/
- word0(rv) = Exp_mask;
- word1(rv) = 0;
-#endif /*Honor_FLT_ROUNDS*/
-#ifdef SET_INEXACT
- /* set overflow bit */
- dval(rv0) = 1e300;
- dval(rv0) *= dval(rv0);
-#endif
-#else /*IEEE_Arith*/
- word0(rv) = Big0;
- word1(rv) = Big1;
-#endif /*IEEE_Arith*/
- if (bd0)
- goto retfree;
- goto ret;
- }
- e1 >>= 4;
- for (j = 0; e1 > 1; j++, e1 >>= 1)
- if (e1 & 1)
- dval(rv) *= bigtens[j];
- /* The last multiplication could overflow. */
- word0(rv) -= P*Exp_msk1;
- dval(rv) *= bigtens[j];
- if ((z = word0(rv) & Exp_mask)
- > Exp_msk1*(DBL_MAX_EXP+Bias-P))
- goto ovfl;
- if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) {
- /* set to largest number */
- /* (Can't trust DBL_MAX) */
- word0(rv) = Big0;
- word1(rv) = Big1;
- }
- else
- word0(rv) += P*Exp_msk1;
- }
- }
- else if (e1 < 0) {
- e1 = -e1;
- if ((i = e1 & 15) != 0)
- dval(rv) /= tens[i];
- if (e1 >>= 4) {
- if (e1 >= 1 << n_bigtens)
- goto undfl;
-#ifdef Avoid_Underflow
- if (e1 & Scale_Bit)
- scale = 2*P;
- for (j = 0; e1 > 0; j++, e1 >>= 1)
- if (e1 & 1)
- dval(rv) *= tinytens[j];
- if (scale && (j = 2*P + 1 - ((word0(rv) & Exp_mask)
- >> Exp_shift)) > 0) {
- /* scaled rv is denormal; zap j low bits */
- if (j >= 32) {
- word1(rv) = 0;
- if (j >= 53)
- word0(rv) = (P+2)*Exp_msk1;
- else
- word0(rv) &= 0xffffffff << (j-32);
- }
- else
- word1(rv) &= 0xffffffff << j;
- }
-#else
- for (j = 0; e1 > 1; j++, e1 >>= 1)
- if (e1 & 1)
- dval(rv) *= tinytens[j];
- /* The last multiplication could underflow. */
- dval(rv0) = dval(rv);
- dval(rv) *= tinytens[j];
- if (!dval(rv)) {
- dval(rv) = 2.*dval(rv0);
- dval(rv) *= tinytens[j];
-#endif
- if (!dval(rv)) {
-undfl:
- dval(rv) = 0.;
-#ifndef NO_ERRNO
- errno = ERANGE;
-#endif
- if (bd0)
- goto retfree;
- goto ret;
- }
-#ifndef Avoid_Underflow
- word0(rv) = Tiny0;
- word1(rv) = Tiny1;
- /* The refinement below will clean
- * this approximation up.
- */
- }
-#endif
- }
- }
-
- /* Now the hard part -- adjusting rv to the correct value.*/
-
- /* Put digits into bd: true value = bd * 10^e */
-
- bd0 = s2b(s0, nd0, nd, y);
-
- for (;;) {
- bd = Balloc(bd0->k);
- Bcopy(bd, bd0);
- bb = d2b(dval(rv), &bbe, &bbbits); /* rv = bb * 2^bbe */
- bs = i2b(1);
-
- if (e >= 0) {
- bb2 = bb5 = 0;
- bd2 = bd5 = e;
- }
- else {
- bb2 = bb5 = -e;
- bd2 = bd5 = 0;
- }
- if (bbe >= 0)
- bb2 += bbe;
- else
- bd2 -= bbe;
- bs2 = bb2;
-#ifdef Honor_FLT_ROUNDS
- if (rounding != 1)
- bs2++;
-#endif
-#ifdef Avoid_Underflow
- j = bbe - scale;
- i = j + bbbits - 1; /* logb(rv) */
- if (i < Emin) /* denormal */
- j += P - Emin;
- else
- j = P + 1 - bbbits;
-#else /*Avoid_Underflow*/
-#ifdef Sudden_Underflow
-#ifdef IBM
- j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3);
-#else
- j = P + 1 - bbbits;
-#endif
-#else /*Sudden_Underflow*/
- j = bbe;
- i = j + bbbits - 1; /* logb(rv) */
- if (i < Emin) /* denormal */
- j += P - Emin;
- else
- j = P + 1 - bbbits;
-#endif /*Sudden_Underflow*/
-#endif /*Avoid_Underflow*/
- bb2 += j;
- bd2 += j;
-#ifdef Avoid_Underflow
- bd2 += scale;
-#endif
- i = bb2 < bd2 ? bb2 : bd2;
- if (i > bs2)
- i = bs2;
- if (i > 0) {
- bb2 -= i;
- bd2 -= i;
- bs2 -= i;
- }
- if (bb5 > 0) {
- bs = pow5mult(bs, bb5);
- bb1 = mult(bs, bb);
- Bfree(bb);
- bb = bb1;
- }
- if (bb2 > 0)
- bb = lshift(bb, bb2);
- if (bd5 > 0)
- bd = pow5mult(bd, bd5);
- if (bd2 > 0)
- bd = lshift(bd, bd2);
- if (bs2 > 0)
- bs = lshift(bs, bs2);
- delta = diff(bb, bd);
- dsign = delta->sign;
- delta->sign = 0;
- i = cmp(delta, bs);
-#ifdef Honor_FLT_ROUNDS
- if (rounding != 1) {
- if (i < 0) {
- /* Error is less than an ulp */
- if (!delta->x[0] && delta->wds <= 1) {
- /* exact */
-#ifdef SET_INEXACT
- inexact = 0;
-#endif
- break;
- }
- if (rounding) {
- if (dsign) {
- adj = 1.;
- goto apply_adj;
- }
- }
- else if (!dsign) {
- adj = -1.;
- if (!word1(rv)
- && !(word0(rv) & Frac_mask)) {
- y = word0(rv) & Exp_mask;
-#ifdef Avoid_Underflow
- if (!scale || y > 2*P*Exp_msk1)
-#else
- if (y)
-#endif
- {
- delta = lshift(delta,Log2P);
- if (cmp(delta, bs) <= 0)
- adj = -0.5;
- }
- }
-apply_adj:
-#ifdef Avoid_Underflow
- if (scale && (y = word0(rv) & Exp_mask)
- <= 2*P*Exp_msk1)
- word0(adj) += (2*P+1)*Exp_msk1 - y;
-#else
-#ifdef Sudden_Underflow
- if ((word0(rv) & Exp_mask) <=
- P*Exp_msk1) {
- word0(rv) += P*Exp_msk1;
- dval(rv) += adj*ulp(dval(rv));
- word0(rv) -= P*Exp_msk1;
- }
- else
-#endif /*Sudden_Underflow*/
-#endif /*Avoid_Underflow*/
- dval(rv) += adj*ulp(dval(rv));
- }
- break;
- }
- adj = ratio(delta, bs);
- if (adj < 1.)
- adj = 1.;
- if (adj <= 0x7ffffffe) {
- /* adj = rounding ? ceil(adj) : floor(adj); */
- y = adj;
- if (y != adj) {
- if (!((rounding>>1) ^ dsign))
- y++;
- adj = y;
- }
- }
-#ifdef Avoid_Underflow
- if (scale && (y = word0(rv) & Exp_mask) <= 2*P*Exp_msk1)
- word0(adj) += (2*P+1)*Exp_msk1 - y;
-#else
-#ifdef Sudden_Underflow
- if ((word0(rv) & Exp_mask) <= P*Exp_msk1) {
- word0(rv) += P*Exp_msk1;
- adj *= ulp(dval(rv));
- if (dsign)
- dval(rv) += adj;
- else
- dval(rv) -= adj;
- word0(rv) -= P*Exp_msk1;
- goto cont;
- }
-#endif /*Sudden_Underflow*/
-#endif /*Avoid_Underflow*/
- adj *= ulp(dval(rv));
- if (dsign)
- dval(rv) += adj;
- else
- dval(rv) -= adj;
- goto cont;
- }
-#endif /*Honor_FLT_ROUNDS*/
-
- if (i < 0) {
- /* Error is less than half an ulp -- check for
- * special case of mantissa a power of two.
- */
- if (dsign || word1(rv) || word0(rv) & Bndry_mask
-#ifdef IEEE_Arith
-#ifdef Avoid_Underflow
- || (word0(rv) & Exp_mask) <= (2*P+1)*Exp_msk1
-#else
- || (word0(rv) & Exp_mask) <= Exp_msk1
-#endif
-#endif
- ) {
-#ifdef SET_INEXACT
- if (!delta->x[0] && delta->wds <= 1)
- inexact = 0;
-#endif
- break;
- }
- if (!delta->x[0] && delta->wds <= 1) {
- /* exact result */
-#ifdef SET_INEXACT
- inexact = 0;
-#endif
- break;
- }
- delta = lshift(delta,Log2P);
- if (cmp(delta, bs) > 0)
- goto drop_down;
- break;
- }
- if (i == 0) {
- /* exactly half-way between */
- if (dsign) {
- if ((word0(rv) & Bndry_mask1) == Bndry_mask1
- && word1(rv) == (
-#ifdef Avoid_Underflow
- (scale && (y = word0(rv) & Exp_mask) <= 2*P*Exp_msk1)
- ? (0xffffffff & (0xffffffff << (2*P+1-(y>>Exp_shift)))) :
-#endif
- 0xffffffff)) {
- /*boundary case -- increment exponent*/
- word0(rv) = (word0(rv) & Exp_mask)
- + Exp_msk1
-#ifdef IBM
- | Exp_msk1 >> 4
-#endif
- ;
- word1(rv) = 0;
-#ifdef Avoid_Underflow
- dsign = 0;
-#endif
- break;
- }
- }
- else if (!(word0(rv) & Bndry_mask) && !word1(rv)) {
-drop_down:
- /* boundary case -- decrement exponent */
-#ifdef Sudden_Underflow /*{{*/
- L = word0(rv) & Exp_mask;
-#ifdef IBM
- if (L < Exp_msk1)
-#else
-#ifdef Avoid_Underflow
- if (L <= (scale ? (2*P+1)*Exp_msk1 : Exp_msk1))
-#else
- if (L <= Exp_msk1)
-#endif /*Avoid_Underflow*/
-#endif /*IBM*/
- goto undfl;
- L -= Exp_msk1;
-#else /*Sudden_Underflow}{*/
-#ifdef Avoid_Underflow
- if (scale) {
- L = word0(rv) & Exp_mask;
- if (L <= (2*P+1)*Exp_msk1) {
- if (L > (P+2)*Exp_msk1)
- /* round even ==> */
- /* accept rv */
- break;
- /* rv = smallest denormal */
- goto undfl;
- }
- }
-#endif /*Avoid_Underflow*/
- L = (word0(rv) & Exp_mask) - Exp_msk1;
-#endif /*Sudden_Underflow}}*/
- word0(rv) = L | Bndry_mask1;
- word1(rv) = 0xffffffff;
-#ifdef IBM
- goto cont;
-#else
- break;
-#endif
- }
-#ifndef ROUND_BIASED
- if (!(word1(rv) & LSB))
- break;
-#endif
- if (dsign)
- dval(rv) += ulp(dval(rv));
-#ifndef ROUND_BIASED
- else {
- dval(rv) -= ulp(dval(rv));
-#ifndef Sudden_Underflow
- if (!dval(rv))
- goto undfl;
-#endif
- }
-#ifdef Avoid_Underflow
- dsign = 1 - dsign;
-#endif
-#endif
- break;
- }
- if ((aadj = ratio(delta, bs)) <= 2.) {
- if (dsign)
- aadj = dval(aadj1) = 1.;
- else if (word1(rv) || word0(rv) & Bndry_mask) {
-#ifndef Sudden_Underflow
- if (word1(rv) == Tiny1 && !word0(rv))
- goto undfl;
-#endif
- aadj = 1.;
- dval(aadj1) = -1.;
- }
- else {
- /* special case -- power of FLT_RADIX to be */
- /* rounded down... */
-
- if (aadj < 2./FLT_RADIX)
- aadj = 1./FLT_RADIX;
- else
- aadj *= 0.5;
- dval(aadj1) = -aadj;
- }
- }
- else {
- aadj *= 0.5;
- dval(aadj1) = dsign ? aadj : -aadj;
-#ifdef Check_FLT_ROUNDS
- switch (Rounding) {
- case 2: /* towards +infinity */
- dval(aadj1) -= 0.5;
- break;
- case 0: /* towards 0 */
- case 3: /* towards -infinity */
- dval(aadj1) += 0.5;
- }
-#else
- if (Flt_Rounds == 0)
- dval(aadj1) += 0.5;
-#endif /*Check_FLT_ROUNDS*/
- }
- y = word0(rv) & Exp_mask;
-
- /* Check for overflow */
-
- if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) {
- dval(rv0) = dval(rv);
- word0(rv) -= P*Exp_msk1;
- adj = dval(aadj1) * ulp(dval(rv));
- dval(rv) += adj;
- if ((word0(rv) & Exp_mask) >=
- Exp_msk1*(DBL_MAX_EXP+Bias-P)) {
- if (word0(rv0) == Big0 && word1(rv0) == Big1)
- goto ovfl;
- word0(rv) = Big0;
- word1(rv) = Big1;
- goto cont;
- }
- else
- word0(rv) += P*Exp_msk1;
- }
- else {
-#ifdef Avoid_Underflow
- if (scale && y <= 2*P*Exp_msk1) {
- if (aadj <= 0x7fffffff) {
- if ((z = (int)aadj) <= 0)
- z = 1;
- aadj = z;
- dval(aadj1) = dsign ? aadj : -aadj;
- }
- word0(aadj1) += (2*P+1)*Exp_msk1 - y;
- }
- adj = dval(aadj1) * ulp(dval(rv));
- dval(rv) += adj;
-#else
-#ifdef Sudden_Underflow
- if ((word0(rv) & Exp_mask) <= P*Exp_msk1) {
- dval(rv0) = dval(rv);
- word0(rv) += P*Exp_msk1;
- adj = dval(aadj1) * ulp(dval(rv));
- dval(rv) += adj;
-#ifdef IBM
- if ((word0(rv) & Exp_mask) < P*Exp_msk1)
-#else
- if ((word0(rv) & Exp_mask) <= P*Exp_msk1)
-#endif
- {
- if (word0(rv0) == Tiny0 && word1(rv0) == Tiny1)
- goto undfl;
- word0(rv) = Tiny0;
- word1(rv) = Tiny1;
- goto cont;
- }
- else
- word0(rv) -= P*Exp_msk1;
- }
- else {
- adj = dval(aadj1) * ulp(dval(rv));
- dval(rv) += adj;
- }
-#else /*Sudden_Underflow*/
- /* Compute adj so that the IEEE rounding rules will
- * correctly round rv + adj in some half-way cases.
- * If rv * ulp(rv) is denormalized (i.e.,
- * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid
- * trouble from bits lost to denormalization;
- * example: 1.2e-307 .
- */
- if (y <= (P-1)*Exp_msk1 && aadj > 1.) {
- dval(aadj1) = (double)(int)(aadj + 0.5);
- if (!dsign)
- dval(aadj1) = -dval(aadj1);
- }
- adj = dval(aadj1) * ulp(dval(rv));
- dval(rv) += adj;
-#endif /*Sudden_Underflow*/
-#endif /*Avoid_Underflow*/
- }
- z = word0(rv) & Exp_mask;
-#ifndef SET_INEXACT
-#ifdef Avoid_Underflow
- if (!scale)
-#endif
- if (y == z) {
- /* Can we stop now? */
- L = (Long)aadj;
- aadj -= L;
- /* The tolerances below are conservative. */
- if (dsign || word1(rv) || word0(rv) & Bndry_mask) {
- if (aadj < .4999999 || aadj > .5000001)
- break;
- }
- else if (aadj < .4999999/FLT_RADIX)
- break;
- }
-#endif
-cont:
- Bfree(bb);
- Bfree(bd);
- Bfree(bs);
- Bfree(delta);
- }
-#ifdef SET_INEXACT
- if (inexact) {
- if (!oldinexact) {
- word0(rv0) = Exp_1 + (70 << Exp_shift);
- word1(rv0) = 0;
- dval(rv0) += 1.;
- }
- }
- else if (!oldinexact)
- clear_inexact();
-#endif
-#ifdef Avoid_Underflow
- if (scale) {
- word0(rv0) = Exp_1 - 2*P*Exp_msk1;
- word1(rv0) = 0;
- dval(rv) *= dval(rv0);
-#ifndef NO_ERRNO
- /* try to avoid the bug of testing an 8087 register value */
- if (word0(rv) == 0 && word1(rv) == 0)
- errno = ERANGE;
-#endif
- }
-#endif /* Avoid_Underflow */
-#ifdef SET_INEXACT
- if (inexact && !(word0(rv) & Exp_mask)) {
- /* set underflow bit */
- dval(rv0) = 1e-300;
- dval(rv0) *= dval(rv0);
- }
-#endif
-retfree:
- Bfree(bb);
- Bfree(bd);
- Bfree(bs);
- Bfree(bd0);
- Bfree(delta);
-ret:
- if (se)
- *se = (char *)s;
- return sign ? -dval(rv) : dval(rv);
-}
-
-NO_SANITIZE("unsigned-integer-overflow", static int quorem(Bigint *b, Bigint *S));
-static int
-quorem(Bigint *b, Bigint *S)
-{
- int n;
- ULong *bx, *bxe, q, *sx, *sxe;
-#ifdef ULLong
- ULLong borrow, carry, y, ys;
-#else
- ULong borrow, carry, y, ys;
-#ifdef Pack_32
- ULong si, z, zs;
-#endif
-#endif
-
- n = S->wds;
-#ifdef DEBUG
- /*debug*/ if (b->wds > n)
- /*debug*/ Bug("oversize b in quorem");
-#endif
- if (b->wds < n)
- return 0;
- sx = S->x;
- sxe = sx + --n;
- bx = b->x;
- bxe = bx + n;
- q = *bxe / (*sxe + 1); /* ensure q <= true quotient */
-#ifdef DEBUG
- /*debug*/ if (q > 9)
- /*debug*/ Bug("oversized quotient in quorem");
-#endif
- if (q) {
- borrow = 0;
- carry = 0;
- do {
-#ifdef ULLong
- ys = *sx++ * (ULLong)q + carry;
- carry = ys >> 32;
- y = *bx - (ys & FFFFFFFF) - borrow;
- borrow = y >> 32 & (ULong)1;
- *bx++ = (ULong)(y & FFFFFFFF);
-#else
-#ifdef Pack_32
- si = *sx++;
- ys = (si & 0xffff) * q + carry;
- zs = (si >> 16) * q + (ys >> 16);
- carry = zs >> 16;
- y = (*bx & 0xffff) - (ys & 0xffff) - borrow;
- borrow = (y & 0x10000) >> 16;
- z = (*bx >> 16) - (zs & 0xffff) - borrow;
- borrow = (z & 0x10000) >> 16;
- Storeinc(bx, z, y);
-#else
- ys = *sx++ * q + carry;
- carry = ys >> 16;
- y = *bx - (ys & 0xffff) - borrow;
- borrow = (y & 0x10000) >> 16;
- *bx++ = y & 0xffff;
-#endif
-#endif
- } while (sx <= sxe);
- if (!*bxe) {
- bx = b->x;
- while (--bxe > bx && !*bxe)
- --n;
- b->wds = n;
- }
- }
- if (cmp(b, S) >= 0) {
- q++;
- borrow = 0;
- carry = 0;
- bx = b->x;
- sx = S->x;
- do {
-#ifdef ULLong
- ys = *sx++ + carry;
- carry = ys >> 32;
- y = *bx - (ys & FFFFFFFF) - borrow;
- borrow = y >> 32 & (ULong)1;
- *bx++ = (ULong)(y & FFFFFFFF);
-#else
-#ifdef Pack_32
- si = *sx++;
- ys = (si & 0xffff) + carry;
- zs = (si >> 16) + (ys >> 16);
- carry = zs >> 16;
- y = (*bx & 0xffff) - (ys & 0xffff) - borrow;
- borrow = (y & 0x10000) >> 16;
- z = (*bx >> 16) - (zs & 0xffff) - borrow;
- borrow = (z & 0x10000) >> 16;
- Storeinc(bx, z, y);
-#else
- ys = *sx++ + carry;
- carry = ys >> 16;
- y = *bx - (ys & 0xffff) - borrow;
- borrow = (y & 0x10000) >> 16;
- *bx++ = y & 0xffff;
-#endif
-#endif
- } while (sx <= sxe);
- bx = b->x;
- bxe = bx + n;
- if (!*bxe) {
- while (--bxe > bx && !*bxe)
- --n;
- b->wds = n;
- }
- }
- return q;
-}
-
-#ifndef MULTIPLE_THREADS
-static char *dtoa_result;
-#endif
-
-#ifndef MULTIPLE_THREADS
-static char *
-rv_alloc(int i)
-{
- return dtoa_result = MALLOC(i);
-}
-#else
-#define rv_alloc(i) MALLOC(i)
-#endif
-
-static char *
-nrv_alloc(const char *s, char **rve, size_t n)
-{
- char *rv, *t;
-
- t = rv = rv_alloc(n);
- while ((*t = *s++) != 0) t++;
- if (rve)
- *rve = t;
- return rv;
-}
-
-#define rv_strdup(s, rve) nrv_alloc((s), (rve), strlen(s)+1)
-
-#ifndef MULTIPLE_THREADS
-/* freedtoa(s) must be used to free values s returned by dtoa
- * when MULTIPLE_THREADS is #defined. It should be used in all cases,
- * but for consistency with earlier versions of dtoa, it is optional
- * when MULTIPLE_THREADS is not defined.
- */
-
-static void
-freedtoa(char *s)
-{
- FREE(s);
-}
-#endif
-
-static const char INFSTR[] = "Infinity";
-static const char NANSTR[] = "NaN";
-static const char ZEROSTR[] = "0";
-
-/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string.
- *
- * Inspired by "How to Print Floating-Point Numbers Accurately" by
- * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126].
- *
- * Modifications:
- * 1. Rather than iterating, we use a simple numeric overestimate
- * to determine k = floor(log10(d)). We scale relevant
- * quantities using O(log2(k)) rather than O(k) multiplications.
- * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't
- * try to generate digits strictly left to right. Instead, we
- * compute with fewer bits and propagate the carry if necessary
- * when rounding the final digit up. This is often faster.
- * 3. Under the assumption that input will be rounded nearest,
- * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22.
- * That is, we allow equality in stopping tests when the
- * round-nearest rule will give the same floating-point value
- * as would satisfaction of the stopping test with strict
- * inequality.
- * 4. We remove common factors of powers of 2 from relevant
- * quantities.
- * 5. When converting floating-point integers less than 1e16,
- * we use floating-point arithmetic rather than resorting
- * to multiple-precision integers.
- * 6. When asked to produce fewer than 15 digits, we first try
- * to get by with floating-point arithmetic; we resort to
- * multiple-precision integer arithmetic only if we cannot
- * guarantee that the floating-point calculation has given
- * the correctly rounded result. For k requested digits and
- * "uniformly" distributed input, the probability is
- * something like 10^(k-15) that we must resort to the Long
- * calculation.
- */
-
-char *
-dtoa(double d_, int mode, int ndigits, int *decpt, int *sign, char **rve)
-{
- /* Arguments ndigits, decpt, sign are similar to those
- of ecvt and fcvt; trailing zeros are suppressed from
- the returned string. If not null, *rve is set to point
- to the end of the return value. If d is +-Infinity or NaN,
- then *decpt is set to 9999.
-
- mode:
- 0 ==> shortest string that yields d when read in
- and rounded to nearest.
- 1 ==> like 0, but with Steele & White stopping rule;
- e.g. with IEEE P754 arithmetic , mode 0 gives
- 1e23 whereas mode 1 gives 9.999999999999999e22.
- 2 ==> max(1,ndigits) significant digits. This gives a
- return value similar to that of ecvt, except
- that trailing zeros are suppressed.
- 3 ==> through ndigits past the decimal point. This
- gives a return value similar to that from fcvt,
- except that trailing zeros are suppressed, and
- ndigits can be negative.
- 4,5 ==> similar to 2 and 3, respectively, but (in
- round-nearest mode) with the tests of mode 0 to
- possibly return a shorter string that rounds to d.
- With IEEE arithmetic and compilation with
- -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same
- as modes 2 and 3 when FLT_ROUNDS != 1.
- 6-9 ==> Debugging modes similar to mode - 4: don't try
- fast floating-point estimate (if applicable).
-
- Values of mode other than 0-9 are treated as mode 0.
-
- Sufficient space is allocated to the return value
- to hold the suppressed trailing zeros.
- */
-
- int bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1,
- j, j1, k, k0, k_check, leftright, m2, m5, s2, s5,
- spec_case, try_quick, half = 0;
- Long L;
-#ifndef Sudden_Underflow
- int denorm;
- ULong x;
-#endif
- Bigint *b, *b1, *delta, *mlo = 0, *mhi = 0, *S;
- double ds;
- double_u d, d2, eps;
- char *s, *s0;
-#ifdef Honor_FLT_ROUNDS
- int rounding;
-#endif
-#ifdef SET_INEXACT
- int inexact, oldinexact;
-#endif
-
- dval(d) = d_;
-
-#ifndef MULTIPLE_THREADS
- if (dtoa_result) {
- freedtoa(dtoa_result);
- dtoa_result = 0;
- }
-#endif
-
- if (word0(d) & Sign_bit) {
- /* set sign for everything, including 0's and NaNs */
- *sign = 1;
- word0(d) &= ~Sign_bit; /* clear sign bit */
- }
- else
- *sign = 0;
-
-#if defined(IEEE_Arith) + defined(VAX)
-#ifdef IEEE_Arith
- if ((word0(d) & Exp_mask) == Exp_mask)
-#else
- if (word0(d) == 0x8000)
-#endif
- {
- /* Infinity or NaN */
- *decpt = 9999;
-#ifdef IEEE_Arith
- if (!word1(d) && !(word0(d) & 0xfffff))
- return rv_strdup(INFSTR, rve);
-#endif
- return rv_strdup(NANSTR, rve);
- }
-#endif
-#ifdef IBM
- dval(d) += 0; /* normalize */
-#endif
- if (!dval(d)) {
- *decpt = 1;
- return rv_strdup(ZEROSTR, rve);
- }
-
-#ifdef SET_INEXACT
- try_quick = oldinexact = get_inexact();
- inexact = 1;
-#endif
-#ifdef Honor_FLT_ROUNDS
- if ((rounding = Flt_Rounds) >= 2) {
- if (*sign)
- rounding = rounding == 2 ? 0 : 2;
- else
- if (rounding != 2)
- rounding = 0;
- }
-#endif
-
- b = d2b(dval(d), &be, &bbits);
-#ifdef Sudden_Underflow
- i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1));
-#else
- if ((i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1))) != 0) {
-#endif
- dval(d2) = dval(d);
- word0(d2) &= Frac_mask1;
- word0(d2) |= Exp_11;
-#ifdef IBM
- if (j = 11 - hi0bits(word0(d2) & Frac_mask))
- dval(d2) /= 1 << j;
-#endif
-
- /* log(x) ~=~ log(1.5) + (x-1.5)/1.5
- * log10(x) = log(x) / log(10)
- * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10))
- * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2)
- *
- * This suggests computing an approximation k to log10(d) by
- *
- * k = (i - Bias)*0.301029995663981
- * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 );
- *
- * We want k to be too large rather than too small.
- * The error in the first-order Taylor series approximation
- * is in our favor, so we just round up the constant enough
- * to compensate for any error in the multiplication of
- * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077,
- * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14,
- * adding 1e-13 to the constant term more than suffices.
- * Hence we adjust the constant term to 0.1760912590558.
- * (We could get a more accurate k by invoking log10,
- * but this is probably not worthwhile.)
- */
-
- i -= Bias;
-#ifdef IBM
- i <<= 2;
- i += j;
-#endif
-#ifndef Sudden_Underflow
- denorm = 0;
- }
- else {
- /* d is denormalized */
-
- i = bbits + be + (Bias + (P-1) - 1);
- x = i > 32 ? word0(d) << (64 - i) | word1(d) >> (i - 32)
- : word1(d) << (32 - i);
- dval(d2) = x;
- word0(d2) -= 31*Exp_msk1; /* adjust exponent */
- i -= (Bias + (P-1) - 1) + 1;
- denorm = 1;
- }
-#endif
- ds = (dval(d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981;
- k = (int)ds;
- if (ds < 0. && ds != k)
- k--; /* want k = floor(ds) */
- k_check = 1;
- if (k >= 0 && k <= Ten_pmax) {
- if (dval(d) < tens[k])
- k--;
- k_check = 0;
- }
- j = bbits - i - 1;
- if (j >= 0) {
- b2 = 0;
- s2 = j;
- }
- else {
- b2 = -j;
- s2 = 0;
- }
- if (k >= 0) {
- b5 = 0;
- s5 = k;
- s2 += k;
- }
- else {
- b2 -= k;
- b5 = -k;
- s5 = 0;
- }
- if (mode < 0 || mode > 9)
- mode = 0;
-
-#ifndef SET_INEXACT
-#ifdef Check_FLT_ROUNDS
- try_quick = Rounding == 1;
-#else
- try_quick = 1;
-#endif
-#endif /*SET_INEXACT*/
-
- if (mode > 5) {
- mode -= 4;
- try_quick = 0;
- }
- leftright = 1;
- ilim = ilim1 = -1;
- switch (mode) {
- case 0:
- case 1:
- i = 18;
- ndigits = 0;
- break;
- case 2:
- leftright = 0;
- /* no break */
- case 4:
- if (ndigits <= 0)
- ndigits = 1;
- ilim = ilim1 = i = ndigits;
- break;
- case 3:
- leftright = 0;
- /* no break */
- case 5:
- i = ndigits + k + 1;
- ilim = i;
- ilim1 = i - 1;
- if (i <= 0)
- i = 1;
- }
- s = s0 = rv_alloc(i+1);
-
-#ifdef Honor_FLT_ROUNDS
- if (mode > 1 && rounding != 1)
- leftright = 0;
-#endif
-
- if (ilim >= 0 && ilim <= Quick_max && try_quick) {
-
- /* Try to get by with floating-point arithmetic. */
-
- i = 0;
- dval(d2) = dval(d);
- k0 = k;
- ilim0 = ilim;
- ieps = 2; /* conservative */
- if (k > 0) {
- ds = tens[k&0xf];
- j = k >> 4;
- if (j & Bletch) {
- /* prevent overflows */
- j &= Bletch - 1;
- dval(d) /= bigtens[n_bigtens-1];
- ieps++;
- }
- for (; j; j >>= 1, i++)
- if (j & 1) {
- ieps++;
- ds *= bigtens[i];
- }
- dval(d) /= ds;
- }
- else if ((j1 = -k) != 0) {
- dval(d) *= tens[j1 & 0xf];
- for (j = j1 >> 4; j; j >>= 1, i++)
- if (j & 1) {
- ieps++;
- dval(d) *= bigtens[i];
- }
- }
- if (k_check && dval(d) < 1. && ilim > 0) {
- if (ilim1 <= 0)
- goto fast_failed;
- ilim = ilim1;
- k--;
- dval(d) *= 10.;
- ieps++;
- }
- dval(eps) = ieps*dval(d) + 7.;
- word0(eps) -= (P-1)*Exp_msk1;
- if (ilim == 0) {
- S = mhi = 0;
- dval(d) -= 5.;
- if (dval(d) > dval(eps))
- goto one_digit;
- if (dval(d) < -dval(eps))
- goto no_digits;
- goto fast_failed;
- }
-#ifndef No_leftright
- if (leftright) {
- /* Use Steele & White method of only
- * generating digits needed.
- */
- dval(eps) = 0.5/tens[ilim-1] - dval(eps);
- for (i = 0;;) {
- L = (int)dval(d);
- dval(d) -= L;
- *s++ = '0' + (int)L;
- if (dval(d) < dval(eps))
- goto ret1;
- if (1. - dval(d) < dval(eps))
- goto bump_up;
- if (++i >= ilim)
- break;
- dval(eps) *= 10.;
- dval(d) *= 10.;
- }
- }
- else {
-#endif
- /* Generate ilim digits, then fix them up. */
- dval(eps) *= tens[ilim-1];
- for (i = 1;; i++, dval(d) *= 10.) {
- L = (Long)(dval(d));
- if (!(dval(d) -= L))
- ilim = i;
- *s++ = '0' + (int)L;
- if (i == ilim) {
- if (dval(d) > 0.5 + dval(eps))
- goto bump_up;
- else if (dval(d) < 0.5 - dval(eps)) {
- while (*--s == '0') ;
- s++;
- goto ret1;
- }
- half = 1;
- if ((*(s-1) - '0') & 1) {
- goto bump_up;
- }
- break;
- }
- }
-#ifndef No_leftright
- }
-#endif
-fast_failed:
- s = s0;
- dval(d) = dval(d2);
- k = k0;
- ilim = ilim0;
- }
-
- /* Do we have a "small" integer? */
-
- if (be >= 0 && k <= Int_max) {
- /* Yes. */
- ds = tens[k];
- if (ndigits < 0 && ilim <= 0) {
- S = mhi = 0;
- if (ilim < 0 || dval(d) <= 5*ds)
- goto no_digits;
- goto one_digit;
- }
- for (i = 1;; i++, dval(d) *= 10.) {
- L = (Long)(dval(d) / ds);
- dval(d) -= L*ds;
-#ifdef Check_FLT_ROUNDS
- /* If FLT_ROUNDS == 2, L will usually be high by 1 */
- if (dval(d) < 0) {
- L--;
- dval(d) += ds;
- }
-#endif
- *s++ = '0' + (int)L;
- if (!dval(d)) {
-#ifdef SET_INEXACT
- inexact = 0;
-#endif
- break;
- }
- if (i == ilim) {
-#ifdef Honor_FLT_ROUNDS
- if (mode > 1)
- switch (rounding) {
- case 0: goto ret1;
- case 2: goto bump_up;
- }
-#endif
- dval(d) += dval(d);
- if (dval(d) > ds || (dval(d) == ds && (L & 1))) {
-bump_up:
- while (*--s == '9')
- if (s == s0) {
- k++;
- *s = '0';
- break;
- }
- ++*s++;
- }
- break;
- }
- }
- goto ret1;
- }
-
- m2 = b2;
- m5 = b5;
- if (leftright) {
- i =
-#ifndef Sudden_Underflow
- denorm ? be + (Bias + (P-1) - 1 + 1) :
-#endif
-#ifdef IBM
- 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3);
-#else
- 1 + P - bbits;
-#endif
- b2 += i;
- s2 += i;
- mhi = i2b(1);
- }
- if (m2 > 0 && s2 > 0) {
- i = m2 < s2 ? m2 : s2;
- b2 -= i;
- m2 -= i;
- s2 -= i;
- }
- if (b5 > 0) {
- if (leftright) {
- if (m5 > 0) {
- mhi = pow5mult(mhi, m5);
- b1 = mult(mhi, b);
- Bfree(b);
- b = b1;
- }
- if ((j = b5 - m5) != 0)
- b = pow5mult(b, j);
- }
- else
- b = pow5mult(b, b5);
- }
- S = i2b(1);
- if (s5 > 0)
- S = pow5mult(S, s5);
-
- /* Check for special case that d is a normalized power of 2. */
-
- spec_case = 0;
- if ((mode < 2 || leftright)
-#ifdef Honor_FLT_ROUNDS
- && rounding == 1
-#endif
- ) {
- if (!word1(d) && !(word0(d) & Bndry_mask)
-#ifndef Sudden_Underflow
- && word0(d) & (Exp_mask & ~Exp_msk1)
-#endif
- ) {
- /* The special case */
- b2 += Log2P;
- s2 += Log2P;
- spec_case = 1;
- }
- }
-
- /* Arrange for convenient computation of quotients:
- * shift left if necessary so divisor has 4 leading 0 bits.
- *
- * Perhaps we should just compute leading 28 bits of S once
- * and for all and pass them and a shift to quorem, so it
- * can do shifts and ors to compute the numerator for q.
- */
-#ifdef Pack_32
- if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f) != 0)
- i = 32 - i;
-#else
- if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf) != 0)
- i = 16 - i;
-#endif
- if (i > 4) {
- i -= 4;
- b2 += i;
- m2 += i;
- s2 += i;
- }
- else if (i < 4) {
- i += 28;
- b2 += i;
- m2 += i;
- s2 += i;
- }
- if (b2 > 0)
- b = lshift(b, b2);
- if (s2 > 0)
- S = lshift(S, s2);
- if (k_check) {
- if (cmp(b,S) < 0) {
- k--;
- b = multadd(b, 10, 0); /* we botched the k estimate */
- if (leftright)
- mhi = multadd(mhi, 10, 0);
- ilim = ilim1;
- }
- }
- if (ilim <= 0 && (mode == 3 || mode == 5)) {
- if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) {
- /* no digits, fcvt style */
-no_digits:
- k = -1 - ndigits;
- goto ret;
- }
-one_digit:
- *s++ = '1';
- k++;
- goto ret;
- }
- if (leftright) {
- if (m2 > 0)
- mhi = lshift(mhi, m2);
-
- /* Compute mlo -- check for special case
- * that d is a normalized power of 2.
- */
-
- mlo = mhi;
- if (spec_case) {
- mhi = Balloc(mhi->k);
- Bcopy(mhi, mlo);
- mhi = lshift(mhi, Log2P);
- }
-
- for (i = 1;;i++) {
- dig = quorem(b,S) + '0';
- /* Do we yet have the shortest decimal string
- * that will round to d?
- */
- j = cmp(b, mlo);
- delta = diff(S, mhi);
- j1 = delta->sign ? 1 : cmp(b, delta);
- Bfree(delta);
-#ifndef ROUND_BIASED
- if (j1 == 0 && mode != 1 && !(word1(d) & 1)
-#ifdef Honor_FLT_ROUNDS
- && rounding >= 1
-#endif
- ) {
- if (dig == '9')
- goto round_9_up;
- if (j > 0)
- dig++;
-#ifdef SET_INEXACT
- else if (!b->x[0] && b->wds <= 1)
- inexact = 0;
-#endif
- *s++ = dig;
- goto ret;
- }
-#endif
- if (j < 0 || (j == 0 && mode != 1
-#ifndef ROUND_BIASED
- && !(word1(d) & 1)
-#endif
- )) {
- if (!b->x[0] && b->wds <= 1) {
-#ifdef SET_INEXACT
- inexact = 0;
-#endif
- goto accept_dig;
- }
-#ifdef Honor_FLT_ROUNDS
- if (mode > 1)
- switch (rounding) {
- case 0: goto accept_dig;
- case 2: goto keep_dig;
- }
-#endif /*Honor_FLT_ROUNDS*/
- if (j1 > 0) {
- b = lshift(b, 1);
- j1 = cmp(b, S);
- if ((j1 > 0 || (j1 == 0 && (dig & 1))) && dig++ == '9')
- goto round_9_up;
- }
-accept_dig:
- *s++ = dig;
- goto ret;
- }
- if (j1 > 0) {
-#ifdef Honor_FLT_ROUNDS
- if (!rounding)
- goto accept_dig;
-#endif
- if (dig == '9') { /* possible if i == 1 */
-round_9_up:
- *s++ = '9';
- goto roundoff;
- }
- *s++ = dig + 1;
- goto ret;
- }
-#ifdef Honor_FLT_ROUNDS
-keep_dig:
-#endif
- *s++ = dig;
- if (i == ilim)
- break;
- b = multadd(b, 10, 0);
- if (mlo == mhi)
- mlo = mhi = multadd(mhi, 10, 0);
- else {
- mlo = multadd(mlo, 10, 0);
- mhi = multadd(mhi, 10, 0);
- }
- }
- }
- else
- for (i = 1;; i++) {
- *s++ = dig = quorem(b,S) + '0';
- if (!b->x[0] && b->wds <= 1) {
-#ifdef SET_INEXACT
- inexact = 0;
-#endif
- goto ret;
- }
- if (i >= ilim)
- break;
- b = multadd(b, 10, 0);
- }
-
- /* Round off last digit */
-
-#ifdef Honor_FLT_ROUNDS
- switch (rounding) {
- case 0: goto trimzeros;
- case 2: goto roundoff;
- }
-#endif
- b = lshift(b, 1);
- j = cmp(b, S);
- if (j > 0 || (j == 0 && (dig & 1))) {
- roundoff:
- while (*--s == '9')
- if (s == s0) {
- k++;
- *s++ = '1';
- goto ret;
- }
- if (!half || (*s - '0') & 1)
- ++*s;
- }
- else {
- while (*--s == '0') ;
- }
- s++;
-ret:
- Bfree(S);
- if (mhi) {
- if (mlo && mlo != mhi)
- Bfree(mlo);
- Bfree(mhi);
- }
-ret1:
-#ifdef SET_INEXACT
- if (inexact) {
- if (!oldinexact) {
- word0(d) = Exp_1 + (70 << Exp_shift);
- word1(d) = 0;
- dval(d) += 1.;
- }
- }
- else if (!oldinexact)
- clear_inexact();
-#endif
- Bfree(b);
- *s = 0;
- *decpt = k + 1;
- if (rve)
- *rve = s;
- return s0;
-}
-
-/*-
- * Copyright (c) 2004-2008 David Schultz <das@FreeBSD.ORG>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#define DBL_MANH_SIZE 20
-#define DBL_MANL_SIZE 32
-#define DBL_ADJ (DBL_MAX_EXP - 2)
-#define SIGFIGS ((DBL_MANT_DIG + 3) / 4 + 1)
-#define dexp_get(u) ((int)(word0(u) >> Exp_shift) & ~Exp_msk1)
-#define dexp_set(u,v) (word0(u) = (((int)(word0(u)) & ~Exp_mask) | ((v) << Exp_shift)))
-#define dmanh_get(u) ((uint32_t)(word0(u) & Frac_mask))
-#define dmanl_get(u) ((uint32_t)word1(u))
-
-
-/*
- * This procedure converts a double-precision number in IEEE format
- * into a string of hexadecimal digits and an exponent of 2. Its
- * behavior is bug-for-bug compatible with dtoa() in mode 2, with the
- * following exceptions:
- *
- * - An ndigits < 0 causes it to use as many digits as necessary to
- * represent the number exactly.
- * - The additional xdigs argument should point to either the string
- * "0123456789ABCDEF" or the string "0123456789abcdef", depending on
- * which case is desired.
- * - This routine does not repeat dtoa's mistake of setting decpt
- * to 9999 in the case of an infinity or NaN. INT_MAX is used
- * for this purpose instead.
- *
- * Note that the C99 standard does not specify what the leading digit
- * should be for non-zero numbers. For instance, 0x1.3p3 is the same
- * as 0x2.6p2 is the same as 0x4.cp3. This implementation always makes
- * the leading digit a 1. This ensures that the exponent printed is the
- * actual base-2 exponent, i.e., ilogb(d).
- *
- * Inputs: d, xdigs, ndigits
- * Outputs: decpt, sign, rve
- */
-char *
-hdtoa(double d, const char *xdigs, int ndigits, int *decpt, int *sign, char **rve)
-{
- U u;
- char *s, *s0;
- int bufsize;
- uint32_t manh, manl;
-
- u.d = d;
- if (word0(u) & Sign_bit) {
- /* set sign for everything, including 0's and NaNs */
- *sign = 1;
- word0(u) &= ~Sign_bit; /* clear sign bit */
- }
- else
- *sign = 0;
-
- if (isinf(d)) { /* FP_INFINITE */
- *decpt = INT_MAX;
- return rv_strdup(INFSTR, rve);
- }
- else if (isnan(d)) { /* FP_NAN */
- *decpt = INT_MAX;
- return rv_strdup(NANSTR, rve);
- }
- else if (d == 0.0) { /* FP_ZERO */
- *decpt = 1;
- return rv_strdup(ZEROSTR, rve);
- }
- else if (dexp_get(u)) { /* FP_NORMAL */
- *decpt = dexp_get(u) - DBL_ADJ;
- }
- else { /* FP_SUBNORMAL */
- u.d *= 5.363123171977039e+154 /* 0x1p514 */;
- *decpt = dexp_get(u) - (514 + DBL_ADJ);
- }
-
- if (ndigits == 0) /* dtoa() compatibility */
- ndigits = 1;
-
- /*
- * If ndigits < 0, we are expected to auto-size, so we allocate
- * enough space for all the digits.
- */
- bufsize = (ndigits > 0) ? ndigits : SIGFIGS;
- s0 = rv_alloc(bufsize+1);
-
- /* Round to the desired number of digits. */
- if (SIGFIGS > ndigits && ndigits > 0) {
- float redux = 1.0f;
- int offset = 4 * ndigits + DBL_MAX_EXP - 4 - DBL_MANT_DIG;
- dexp_set(u, offset);
- u.d += redux;
- u.d -= redux;
- *decpt += dexp_get(u) - offset;
- }
-
- manh = dmanh_get(u);
- manl = dmanl_get(u);
- *s0 = '1';
- for (s = s0 + 1; s < s0 + bufsize; s++) {
- *s = xdigs[(manh >> (DBL_MANH_SIZE - 4)) & 0xf];
- manh = (manh << 4) | (manl >> (DBL_MANL_SIZE - 4));
- manl <<= 4;
- }
-
- /* If ndigits < 0, we are expected to auto-size the precision. */
- if (ndigits < 0) {
- for (ndigits = SIGFIGS; s0[ndigits - 1] == '0'; ndigits--)
- ;
- }
-
- s = s0 + ndigits;
- *s = '\0';
- if (rve != NULL)
- *rve = s;
- return (s0);
-}
-
-#ifdef __cplusplus
-#if 0
-{ /* satisfy cc-mode */
-#endif
-}
-#endif
diff --git a/ext/bigdecimal/sample/linear.rb b/ext/bigdecimal/sample/linear.rb
deleted file mode 100644
index 516c2473be..0000000000
--- a/ext/bigdecimal/sample/linear.rb
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/usr/local/bin/ruby
-# frozen_string_literal: false
-
-#
-# linear.rb
-#
-# Solves linear equation system(A*x = b) by LU decomposition method.
-# where A is a coefficient matrix,x is an answer vector,b is a constant vector.
-#
-# USAGE:
-# ruby linear.rb [input file solved]
-#
-
-# :stopdoc:
-require "bigdecimal"
-require "bigdecimal/ludcmp"
-
-#
-# NOTE:
-# Change following BigDecimal.limit() if needed.
-BigDecimal.limit(100)
-#
-
-include LUSolve
-def rd_order(na)
- printf("Number of equations ?") if(na <= 0)
- n = ARGF.gets().to_i
-end
-
-na = ARGV.size
-zero = BigDecimal("0.0")
-one = BigDecimal("1.0")
-
-while (n=rd_order(na))>0
- a = []
- as= []
- b = []
- if na <= 0
- # Read data from console.
- printf("\nEnter coefficient matrix element A[i,j]\n")
- for i in 0...n do
- for j in 0...n do
- printf("A[%d,%d]? ",i,j); s = ARGF.gets
- a << BigDecimal(s)
- as << BigDecimal(s)
- end
- printf("Contatant vector element b[%d] ? ",i)
- b << BigDecimal(ARGF.gets)
- end
- else
- # Read data from specified file.
- printf("Coefficient matrix and constant vector.\n")
- for i in 0...n do
- s = ARGF.gets
- printf("%d) %s",i,s)
- s = s.split
- for j in 0...n do
- a << BigDecimal(s[j])
- as << BigDecimal(s[j])
- end
- b << BigDecimal(s[n])
- end
- end
- x = lusolve(a,b,ludecomp(a,n,zero,one),zero)
- printf("Answer(x[i] & (A*x-b)[i]) follows\n")
- for i in 0...n do
- printf("x[%d]=%s ",i,x[i].to_s)
- s = zero
- for j in 0...n do
- s = s + as[i*n+j]*x[j]
- end
- printf(" & %s\n",(s-b[i]).to_s)
- end
-end
diff --git a/ext/bigdecimal/sample/nlsolve.rb b/ext/bigdecimal/sample/nlsolve.rb
deleted file mode 100644
index c2227dac73..0000000000
--- a/ext/bigdecimal/sample/nlsolve.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/usr/local/bin/ruby
-# frozen_string_literal: false
-
-#
-# nlsolve.rb
-# An example for solving nonlinear algebraic equation system.
-#
-
-require "bigdecimal"
-require "bigdecimal/newton"
-include Newton
-
-class Function # :nodoc: all
- def initialize()
- @zero = BigDecimal("0.0")
- @one = BigDecimal("1.0")
- @two = BigDecimal("2.0")
- @ten = BigDecimal("10.0")
- @eps = BigDecimal("1.0e-16")
- end
- def zero;@zero;end
- def one ;@one ;end
- def two ;@two ;end
- def ten ;@ten ;end
- def eps ;@eps ;end
- def values(x) # <= defines functions solved
- f = []
- f1 = x[0]*x[0] + x[1]*x[1] - @two # f1 = x**2 + y**2 - 2 => 0
- f2 = x[0] - x[1] # f2 = x - y => 0
- f <<= f1
- f <<= f2
- f
- end
-end
-
-f = BigDecimal.limit(100)
-f = Function.new
-x = [f.zero,f.zero] # Initial values
-n = nlsolve(f,x)
-p x
diff --git a/ext/bigdecimal/sample/pi.rb b/ext/bigdecimal/sample/pi.rb
deleted file mode 100644
index ea9663896c..0000000000
--- a/ext/bigdecimal/sample/pi.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/local/bin/ruby
-# frozen_string_literal: false
-
-#
-# pi.rb
-#
-# Calculates 3.1415.... (the number of times that a circle's diameter
-# will fit around the circle) using J. Machin's formula.
-#
-
-require "bigdecimal"
-require "bigdecimal/math.rb"
-
-include BigMath
-
-if ARGV.size == 1
- print "PI("+ARGV[0]+"):\n"
- p PI(ARGV[0].to_i)
-else
- print "TRY: ruby pi.rb 1000 \n"
-end
diff --git a/ext/bigdecimal/static_assert.h b/ext/bigdecimal/static_assert.h
deleted file mode 100644
index 9295729bf6..0000000000
--- a/ext/bigdecimal/static_assert.h
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef BIGDECIMAL_STATIC_ASSERT_H
-#define BIGDECIMAL_STATIC_ASSERT_H
-
-#include "feature.h"
-
-#ifdef HAVE_RUBY_INTERNAL_STATIC_ASSERT_H
-# include <ruby/internal/static_assert.h>
-#endif
-
-#ifdef RBIMPL_STATIC_ASSERT
-# define STATIC_ASSERT RBIMPL_STATIC_ASSERT
-#endif
-
-#ifndef STATIC_ASSERT
-# /* The following section is copied from CRuby's static_assert.h */
-
-# if defined(__cplusplus) && defined(__cpp_static_assert)
-# /* https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations */
-# define BIGDECIMAL_STATIC_ASSERT0 static_assert
-
-# elif defined(__cplusplus) && defined(_MSC_VER) && _MSC_VER >= 1600
-# define BIGDECIMAL_STATIC_ASSERT0 static_assert
-
-# elif defined(__INTEL_CXX11_MODE__)
-# define BIGDECIMAL_STATIC_ASSERT0 static_assert
-
-# elif defined(__cplusplus) && __cplusplus >= 201103L
-# define BIGDECIMAL_STATIC_ASSERT0 static_assert
-
-# elif defined(__cplusplus) && __has_extension(cxx_static_assert)
-# define BIGDECIMAL_STATIC_ASSERT0 __extension__ static_assert
-
-# elif defined(__STDC_VERSION__) && __has_extension(c_static_assert)
-# define BIGDECIMAL_STATIC_ASSERT0 __extension__ _Static_assert
-
-# elif defined(__STDC_VERSION__) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
-# define BIGDECIMAL_STATIC_ASSERT0 __extension__ _Static_assert
-#endif
-
-# if defined(__DOXYGEN__)
-# define STATIC_ASSERT static_assert
-
-# elif defined(BIGDECIMAL_STATIC_ASSERT0)
-# define STATIC_ASSERT(name, expr) \
- BIGDECIMAL_STATIC_ASSERT0(expr, #name ": " #expr)
-
-# else
-# define STATIC_ASSERT(name, expr) \
- typedef int static_assert_ ## name ## _check[1 - 2 * !(expr)]
-# endif
-#endif /* STATIC_ASSERT */
-
-
-#endif /* BIGDECIMAL_STATIC_ASSERT_H */
diff --git a/ext/cgi/escape/depend b/ext/cgi/escape/depend
index 37304a24f4..746b47246a 100644
--- a/ext/cgi/escape/depend
+++ b/ext/cgi/escape/depend
@@ -157,6 +157,7 @@ escape.o: $(hdrdir)/ruby/internal/special_consts.h
escape.o: $(hdrdir)/ruby/internal/static_assert.h
escape.o: $(hdrdir)/ruby/internal/stdalign.h
escape.o: $(hdrdir)/ruby/internal/stdbool.h
+escape.o: $(hdrdir)/ruby/internal/stdckdint.h
escape.o: $(hdrdir)/ruby/internal/symbol.h
escape.o: $(hdrdir)/ruby/internal/value.h
escape.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/cgi/escape/escape.c b/ext/cgi/escape/escape.c
index c5b76de596..495ad83aa3 100644
--- a/ext/cgi/escape/escape.c
+++ b/ext/cgi/escape/escape.c
@@ -83,7 +83,7 @@ optimized_unescape_html(VALUE str)
unsigned long charlimit = (strcasecmp(rb_enc_name(enc), "UTF-8") == 0 ? UNICODE_MAX :
strcasecmp(rb_enc_name(enc), "ISO-8859-1") == 0 ? 256 :
128);
- long i, len, beg = 0;
+ long i, j, len, beg = 0;
size_t clen, plen;
int overflow;
const char *cstr;
@@ -100,6 +100,7 @@ optimized_unescape_html(VALUE str)
plen = i - beg;
if (++i >= len) break;
c = (unsigned char)cstr[i];
+ j = i;
#define MATCH(s) (len - i >= (int)rb_strlen_lit(s) && \
memcmp(&cstr[i], s, rb_strlen_lit(s)) == 0 && \
(i += rb_strlen_lit(s) - 1, 1))
@@ -112,28 +113,40 @@ optimized_unescape_html(VALUE str)
else if (MATCH("mp;")) {
c = '&';
}
- else continue;
+ else {
+ i = j;
+ continue;
+ }
break;
case 'q':
++i;
if (MATCH("uot;")) {
c = '"';
}
- else continue;
+ else {
+ i = j;
+ continue;
+ }
break;
case 'g':
++i;
if (MATCH("t;")) {
c = '>';
}
- else continue;
+ else {
+ i = j;
+ continue;
+ }
break;
case 'l':
++i;
if (MATCH("t;")) {
c = '<';
}
- else continue;
+ else {
+ i = j;
+ continue;
+ }
break;
case '#':
if (len - ++i >= 2 && ISDIGIT(cstr[i])) {
@@ -142,9 +155,15 @@ optimized_unescape_html(VALUE str)
else if ((cstr[i] == 'x' || cstr[i] == 'X') && len - ++i >= 2 && ISXDIGIT(cstr[i])) {
cc = ruby_scan_digits(&cstr[i], len-i, 16, &clen, &overflow);
}
- else continue;
+ else {
+ i = j;
+ continue;
+ }
i += clen;
- if (overflow || cc >= charlimit || cstr[i] != ';') continue;
+ if (overflow || cc >= charlimit || cstr[i] != ';') {
+ i = j;
+ continue;
+ }
if (!dest) {
dest = rb_str_buf_new(len);
}
@@ -458,7 +477,9 @@ InitVM_escape(void)
rb_define_method(rb_mEscape, "escapeHTML", cgiesc_escape_html, 1);
rb_define_method(rb_mEscape, "unescapeHTML", cgiesc_unescape_html, 1);
rb_define_method(rb_mEscape, "escapeURIComponent", cgiesc_escape_uri_component, 1);
+ rb_define_alias(rb_mEscape, "escape_uri_component", "escapeURIComponent");
rb_define_method(rb_mEscape, "unescapeURIComponent", cgiesc_unescape_uri_component, -1);
+ rb_define_alias(rb_mEscape, "unescape_uri_component", "unescapeURIComponent");
rb_define_method(rb_mEscape, "escape", cgiesc_escape, 1);
rb_define_method(rb_mEscape, "unescape", cgiesc_unescape, -1);
rb_prepend_module(rb_mUtil, rb_mEscape);
diff --git a/ext/continuation/depend b/ext/continuation/depend
index f0333d7fe6..b40e52e29b 100644
--- a/ext/continuation/depend
+++ b/ext/continuation/depend
@@ -146,6 +146,7 @@ continuation.o: $(hdrdir)/ruby/internal/special_consts.h
continuation.o: $(hdrdir)/ruby/internal/static_assert.h
continuation.o: $(hdrdir)/ruby/internal/stdalign.h
continuation.o: $(hdrdir)/ruby/internal/stdbool.h
+continuation.o: $(hdrdir)/ruby/internal/stdckdint.h
continuation.o: $(hdrdir)/ruby/internal/symbol.h
continuation.o: $(hdrdir)/ruby/internal/value.h
continuation.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/coverage/coverage.c b/ext/coverage/coverage.c
index a3381901ee..9fc93bb58d 100644
--- a/ext/coverage/coverage.c
+++ b/ext/coverage/coverage.c
@@ -353,7 +353,8 @@ rb_coverage_peek_result(VALUE klass)
rb_raise(rb_eRuntimeError, "coverage measurement is not enabled");
}
OBJ_WB_UNPROTECT(coverages);
- st_foreach(RHASH_TBL_RAW(coverages), coverage_peek_result_i, ncoverages);
+
+ rb_hash_foreach(coverages, coverage_peek_result_i, ncoverages);
if (current_mode & COVERAGE_TARGET_METHODS) {
rb_objspace_each_objects(method_coverage_i, &ncoverages);
diff --git a/ext/coverage/depend b/ext/coverage/depend
index cd7ef43a43..1be81c5e9a 100644
--- a/ext/coverage/depend
+++ b/ext/coverage/depend
@@ -15,6 +15,7 @@ coverage.o: $(hdrdir)/ruby/backward/2/long_long.h
coverage.o: $(hdrdir)/ruby/backward/2/stdalign.h
coverage.o: $(hdrdir)/ruby/backward/2/stdarg.h
coverage.o: $(hdrdir)/ruby/defines.h
+coverage.o: $(hdrdir)/ruby/encoding.h
coverage.o: $(hdrdir)/ruby/intern.h
coverage.o: $(hdrdir)/ruby/internal/abi.h
coverage.o: $(hdrdir)/ruby/internal/anyargs.h
@@ -87,6 +88,15 @@ coverage.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
coverage.o: $(hdrdir)/ruby/internal/ctype.h
coverage.o: $(hdrdir)/ruby/internal/dllexport.h
coverage.o: $(hdrdir)/ruby/internal/dosish.h
+coverage.o: $(hdrdir)/ruby/internal/encoding/coderange.h
+coverage.o: $(hdrdir)/ruby/internal/encoding/ctype.h
+coverage.o: $(hdrdir)/ruby/internal/encoding/encoding.h
+coverage.o: $(hdrdir)/ruby/internal/encoding/pathname.h
+coverage.o: $(hdrdir)/ruby/internal/encoding/re.h
+coverage.o: $(hdrdir)/ruby/internal/encoding/sprintf.h
+coverage.o: $(hdrdir)/ruby/internal/encoding/string.h
+coverage.o: $(hdrdir)/ruby/internal/encoding/symbol.h
+coverage.o: $(hdrdir)/ruby/internal/encoding/transcode.h
coverage.o: $(hdrdir)/ruby/internal/error.h
coverage.o: $(hdrdir)/ruby/internal/eval.h
coverage.o: $(hdrdir)/ruby/internal/event.h
@@ -149,6 +159,7 @@ coverage.o: $(hdrdir)/ruby/internal/special_consts.h
coverage.o: $(hdrdir)/ruby/internal/static_assert.h
coverage.o: $(hdrdir)/ruby/internal/stdalign.h
coverage.o: $(hdrdir)/ruby/internal/stdbool.h
+coverage.o: $(hdrdir)/ruby/internal/stdckdint.h
coverage.o: $(hdrdir)/ruby/internal/symbol.h
coverage.o: $(hdrdir)/ruby/internal/value.h
coverage.o: $(hdrdir)/ruby/internal/value_type.h
@@ -156,6 +167,8 @@ coverage.o: $(hdrdir)/ruby/internal/variable.h
coverage.o: $(hdrdir)/ruby/internal/warning_push.h
coverage.o: $(hdrdir)/ruby/internal/xmalloc.h
coverage.o: $(hdrdir)/ruby/missing.h
+coverage.o: $(hdrdir)/ruby/onigmo.h
+coverage.o: $(hdrdir)/ruby/oniguruma.h
coverage.o: $(hdrdir)/ruby/ruby.h
coverage.o: $(hdrdir)/ruby/st.h
coverage.o: $(hdrdir)/ruby/subst.h
@@ -184,6 +197,7 @@ coverage.o: $(top_srcdir)/method.h
coverage.o: $(top_srcdir)/node.h
coverage.o: $(top_srcdir)/ruby_assert.h
coverage.o: $(top_srcdir)/ruby_atomic.h
+coverage.o: $(top_srcdir)/rubyparser.h
coverage.o: $(top_srcdir)/shape.h
coverage.o: $(top_srcdir)/thread_pthread.h
coverage.o: $(top_srcdir)/vm_core.h
diff --git a/ext/date/date.gemspec b/ext/date/date.gemspec
index 660353ebc5..bd42b1518a 100644
--- a/ext/date/date.gemspec
+++ b/ext/date/date.gemspec
@@ -31,4 +31,6 @@ Gem::Specification.new do |s|
s.email = [nil]
s.homepage = "https://github.com/ruby/date"
s.licenses = ["Ruby", "BSD-2-Clause"]
+
+ s.metadata["changelog_uri"] = s.homepage + "/releases"
end
diff --git a/ext/date/date_core.c b/ext/date/date_core.c
index f2baf4fcea..0fcefd671b 100644
--- a/ext/date/date_core.c
+++ b/ext/date/date_core.c
@@ -4464,12 +4464,6 @@ check_limit(VALUE str, VALUE opt)
{
size_t slen, limit;
if (NIL_P(str)) return;
- if (SYMBOL_P(str)) {
- rb_category_warn(RB_WARN_CATEGORY_DEPRECATED,
- "The ability to parse Symbol is an unintentional bug and is deprecated");
- str = rb_sym2str(str);
- }
-
StringValue(str);
slen = RSTRING_LEN(str);
limit = get_limit(opt);
@@ -8726,8 +8720,8 @@ dt_lite_to_s(VALUE self)
*
* DateTime.now.strftime # => "2022-07-01T11:03:19-05:00"
*
- * For other formats, see
- * {Formats for Dates and Times}[doc/strftime_formatting.rdoc].
+ * For other formats,
+ * see {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc]:
*
*/
static VALUE
diff --git a/ext/date/depend b/ext/date/depend
index 82f85f7bf3..d07f10a593 100644
--- a/ext/date/depend
+++ b/ext/date/depend
@@ -157,6 +157,7 @@ date_core.o: $(hdrdir)/ruby/internal/special_consts.h
date_core.o: $(hdrdir)/ruby/internal/static_assert.h
date_core.o: $(hdrdir)/ruby/internal/stdalign.h
date_core.o: $(hdrdir)/ruby/internal/stdbool.h
+date_core.o: $(hdrdir)/ruby/internal/stdckdint.h
date_core.o: $(hdrdir)/ruby/internal/symbol.h
date_core.o: $(hdrdir)/ruby/internal/value.h
date_core.o: $(hdrdir)/ruby/internal/value_type.h
@@ -331,6 +332,7 @@ date_parse.o: $(hdrdir)/ruby/internal/special_consts.h
date_parse.o: $(hdrdir)/ruby/internal/static_assert.h
date_parse.o: $(hdrdir)/ruby/internal/stdalign.h
date_parse.o: $(hdrdir)/ruby/internal/stdbool.h
+date_parse.o: $(hdrdir)/ruby/internal/stdckdint.h
date_parse.o: $(hdrdir)/ruby/internal/symbol.h
date_parse.o: $(hdrdir)/ruby/internal/value.h
date_parse.o: $(hdrdir)/ruby/internal/value_type.h
@@ -495,6 +497,7 @@ date_strftime.o: $(hdrdir)/ruby/internal/special_consts.h
date_strftime.o: $(hdrdir)/ruby/internal/static_assert.h
date_strftime.o: $(hdrdir)/ruby/internal/stdalign.h
date_strftime.o: $(hdrdir)/ruby/internal/stdbool.h
+date_strftime.o: $(hdrdir)/ruby/internal/stdckdint.h
date_strftime.o: $(hdrdir)/ruby/internal/symbol.h
date_strftime.o: $(hdrdir)/ruby/internal/value.h
date_strftime.o: $(hdrdir)/ruby/internal/value_type.h
@@ -666,6 +669,7 @@ date_strptime.o: $(hdrdir)/ruby/internal/special_consts.h
date_strptime.o: $(hdrdir)/ruby/internal/static_assert.h
date_strptime.o: $(hdrdir)/ruby/internal/stdalign.h
date_strptime.o: $(hdrdir)/ruby/internal/stdbool.h
+date_strptime.o: $(hdrdir)/ruby/internal/stdckdint.h
date_strptime.o: $(hdrdir)/ruby/internal/symbol.h
date_strptime.o: $(hdrdir)/ruby/internal/value.h
date_strptime.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/date/lib/date.rb b/ext/date/lib/date.rb
index a9fe3ce4b0..6888bd2d4e 100644
--- a/ext/date/lib/date.rb
+++ b/ext/date/lib/date.rb
@@ -4,7 +4,7 @@
require 'date_core'
class Date
- VERSION = "3.3.3" # :nodoc:
+ VERSION = "3.3.4" # :nodoc:
# call-seq:
# infinite? -> false
diff --git a/ext/digest/.document b/ext/digest/.document
new file mode 100644
index 0000000000..beab275b5a
--- /dev/null
+++ b/ext/digest/.document
@@ -0,0 +1,3 @@
+digest.c
+bubblebabble/bubblebabble.c
+*/*init.c
diff --git a/ext/digest/bubblebabble/bubblebabble.c b/ext/digest/bubblebabble/bubblebabble.c
index 358ab416b9..dac603c0d7 100644
--- a/ext/digest/bubblebabble/bubblebabble.c
+++ b/ext/digest/bubblebabble/bubblebabble.c
@@ -129,15 +129,14 @@ Init_bubblebabble(void)
rb_require("digest");
- rb_mDigest = rb_path2class("Digest");
- rb_mDigest_Instance = rb_path2class("Digest::Instance");
- rb_cDigest_Class = rb_path2class("Digest::Class");
-
#if 0
rb_mDigest = rb_define_module("Digest");
rb_mDigest_Instance = rb_define_module_under(rb_mDigest, "Instance");
rb_cDigest_Class = rb_define_class_under(rb_mDigest, "Class", rb_cObject);
#endif
+ rb_mDigest = rb_digest_namespace();
+ rb_mDigest_Instance = rb_const_get(rb_mDigest, rb_intern_const("Instance"));
+ rb_cDigest_Class = rb_const_get(rb_mDigest, rb_intern_const("Class"));
rb_define_module_function(rb_mDigest, "bubblebabble", rb_digest_s_bubblebabble, 1);
rb_define_singleton_method(rb_cDigest_Class, "bubblebabble", rb_digest_class_s_bubblebabble, -1);
diff --git a/ext/digest/bubblebabble/depend b/ext/digest/bubblebabble/depend
index 6f0003a66d..0da9c223ee 100644
--- a/ext/digest/bubblebabble/depend
+++ b/ext/digest/bubblebabble/depend
@@ -147,6 +147,7 @@ bubblebabble.o: $(hdrdir)/ruby/internal/special_consts.h
bubblebabble.o: $(hdrdir)/ruby/internal/static_assert.h
bubblebabble.o: $(hdrdir)/ruby/internal/stdalign.h
bubblebabble.o: $(hdrdir)/ruby/internal/stdbool.h
+bubblebabble.o: $(hdrdir)/ruby/internal/stdckdint.h
bubblebabble.o: $(hdrdir)/ruby/internal/symbol.h
bubblebabble.o: $(hdrdir)/ruby/internal/value.h
bubblebabble.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/digest/depend b/ext/digest/depend
index 6df940a679..cb9e8d4813 100644
--- a/ext/digest/depend
+++ b/ext/digest/depend
@@ -147,6 +147,7 @@ digest.o: $(hdrdir)/ruby/internal/special_consts.h
digest.o: $(hdrdir)/ruby/internal/static_assert.h
digest.o: $(hdrdir)/ruby/internal/stdalign.h
digest.o: $(hdrdir)/ruby/internal/stdbool.h
+digest.o: $(hdrdir)/ruby/internal/stdckdint.h
digest.o: $(hdrdir)/ruby/internal/symbol.h
digest.o: $(hdrdir)/ruby/internal/value.h
digest.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/digest/digest.h b/ext/digest/digest.h
index 8a4c5b7e4e..68a3da5dd2 100644
--- a/ext/digest/digest.h
+++ b/ext/digest/digest.h
@@ -40,7 +40,8 @@ rb_digest_##name##_update(void *ctx, unsigned char *ptr, size_t size) \
for (; size > stride; size -= stride, ptr += stride) { \
name##_Update(ctx, ptr, stride); \
} \
- if (size > 0) name##_Update(ctx, ptr, size); \
+ /* Since size <= stride, size should fit into an unsigned int */ \
+ if (size > 0) name##_Update(ctx, ptr, (unsigned int)size); \
}
#define DEFINE_FINISH_FUNC_FROM_FINAL(name) \
diff --git a/ext/digest/md5/depend b/ext/digest/md5/depend
index da1b345999..e71915e5b4 100644
--- a/ext/digest/md5/depend
+++ b/ext/digest/md5/depend
@@ -150,6 +150,7 @@ md5.o: $(hdrdir)/ruby/internal/special_consts.h
md5.o: $(hdrdir)/ruby/internal/static_assert.h
md5.o: $(hdrdir)/ruby/internal/stdalign.h
md5.o: $(hdrdir)/ruby/internal/stdbool.h
+md5.o: $(hdrdir)/ruby/internal/stdckdint.h
md5.o: $(hdrdir)/ruby/internal/symbol.h
md5.o: $(hdrdir)/ruby/internal/value.h
md5.o: $(hdrdir)/ruby/internal/value_type.h
@@ -311,6 +312,7 @@ md5init.o: $(hdrdir)/ruby/internal/special_consts.h
md5init.o: $(hdrdir)/ruby/internal/static_assert.h
md5init.o: $(hdrdir)/ruby/internal/stdalign.h
md5init.o: $(hdrdir)/ruby/internal/stdbool.h
+md5init.o: $(hdrdir)/ruby/internal/stdckdint.h
md5init.o: $(hdrdir)/ruby/internal/symbol.h
md5init.o: $(hdrdir)/ruby/internal/value.h
md5init.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/digest/md5/md5init.c b/ext/digest/md5/md5init.c
index 52cba78bf1..b81fd94864 100644
--- a/ext/digest/md5/md5init.c
+++ b/ext/digest/md5/md5init.c
@@ -53,9 +53,8 @@ Init_md5(void)
mDigest = rb_define_module("Digest"); /* let rdoc know */
#endif
mDigest = rb_digest_namespace();
- cDigest_Base = rb_path2class("Digest::Base");
+ cDigest_Base = rb_const_get(mDigest, rb_intern_const("Base"));
cDigest_MD5 = rb_define_class_under(mDigest, "MD5", cDigest_Base);
-
rb_iv_set(cDigest_MD5, "metadata", rb_digest_make_metadata(&md5));
}
diff --git a/ext/digest/rmd160/depend b/ext/digest/rmd160/depend
index abfa08b023..09558ad92b 100644
--- a/ext/digest/rmd160/depend
+++ b/ext/digest/rmd160/depend
@@ -150,6 +150,7 @@ rmd160.o: $(hdrdir)/ruby/internal/special_consts.h
rmd160.o: $(hdrdir)/ruby/internal/static_assert.h
rmd160.o: $(hdrdir)/ruby/internal/stdalign.h
rmd160.o: $(hdrdir)/ruby/internal/stdbool.h
+rmd160.o: $(hdrdir)/ruby/internal/stdckdint.h
rmd160.o: $(hdrdir)/ruby/internal/symbol.h
rmd160.o: $(hdrdir)/ruby/internal/value.h
rmd160.o: $(hdrdir)/ruby/internal/value_type.h
@@ -311,6 +312,7 @@ rmd160init.o: $(hdrdir)/ruby/internal/special_consts.h
rmd160init.o: $(hdrdir)/ruby/internal/static_assert.h
rmd160init.o: $(hdrdir)/ruby/internal/stdalign.h
rmd160init.o: $(hdrdir)/ruby/internal/stdbool.h
+rmd160init.o: $(hdrdir)/ruby/internal/stdckdint.h
rmd160init.o: $(hdrdir)/ruby/internal/symbol.h
rmd160init.o: $(hdrdir)/ruby/internal/value.h
rmd160init.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/digest/rmd160/rmd160init.c b/ext/digest/rmd160/rmd160init.c
index 2ae81ec4d6..e4b707ed9e 100644
--- a/ext/digest/rmd160/rmd160init.c
+++ b/ext/digest/rmd160/rmd160init.c
@@ -49,9 +49,8 @@ Init_rmd160(void)
mDigest = rb_define_module("Digest"); /* let rdoc know */
#endif
mDigest = rb_digest_namespace();
- cDigest_Base = rb_path2class("Digest::Base");
+ cDigest_Base = rb_const_get(mDigest, rb_intern_const("Base"));
cDigest_RMD160 = rb_define_class_under(mDigest, "RMD160", cDigest_Base);
-
rb_iv_set(cDigest_RMD160, "metadata", rb_digest_make_metadata(&rmd160));
}
diff --git a/ext/digest/sha1/depend b/ext/digest/sha1/depend
index d17338e92b..827b8a0852 100644
--- a/ext/digest/sha1/depend
+++ b/ext/digest/sha1/depend
@@ -150,6 +150,7 @@ sha1.o: $(hdrdir)/ruby/internal/special_consts.h
sha1.o: $(hdrdir)/ruby/internal/static_assert.h
sha1.o: $(hdrdir)/ruby/internal/stdalign.h
sha1.o: $(hdrdir)/ruby/internal/stdbool.h
+sha1.o: $(hdrdir)/ruby/internal/stdckdint.h
sha1.o: $(hdrdir)/ruby/internal/symbol.h
sha1.o: $(hdrdir)/ruby/internal/value.h
sha1.o: $(hdrdir)/ruby/internal/value_type.h
@@ -311,6 +312,7 @@ sha1init.o: $(hdrdir)/ruby/internal/special_consts.h
sha1init.o: $(hdrdir)/ruby/internal/static_assert.h
sha1init.o: $(hdrdir)/ruby/internal/stdalign.h
sha1init.o: $(hdrdir)/ruby/internal/stdbool.h
+sha1init.o: $(hdrdir)/ruby/internal/stdckdint.h
sha1init.o: $(hdrdir)/ruby/internal/symbol.h
sha1init.o: $(hdrdir)/ruby/internal/value.h
sha1init.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/digest/sha1/sha1init.c b/ext/digest/sha1/sha1init.c
index f7047bc6d3..c39959f428 100644
--- a/ext/digest/sha1/sha1init.c
+++ b/ext/digest/sha1/sha1init.c
@@ -55,9 +55,8 @@ Init_sha1(void)
mDigest = rb_define_module("Digest"); /* let rdoc know */
#endif
mDigest = rb_digest_namespace();
- cDigest_Base = rb_path2class("Digest::Base");
+ cDigest_Base = rb_const_get(mDigest, rb_intern_const("Base"));
cDigest_SHA1 = rb_define_class_under(mDigest, "SHA1", cDigest_Base);
-
rb_iv_set(cDigest_SHA1, "metadata", rb_digest_make_metadata(&sha1));
}
diff --git a/ext/digest/sha2/depend b/ext/digest/sha2/depend
index 7b88b6411f..af1600d346 100644
--- a/ext/digest/sha2/depend
+++ b/ext/digest/sha2/depend
@@ -150,6 +150,7 @@ sha2.o: $(hdrdir)/ruby/internal/special_consts.h
sha2.o: $(hdrdir)/ruby/internal/static_assert.h
sha2.o: $(hdrdir)/ruby/internal/stdalign.h
sha2.o: $(hdrdir)/ruby/internal/stdbool.h
+sha2.o: $(hdrdir)/ruby/internal/stdckdint.h
sha2.o: $(hdrdir)/ruby/internal/symbol.h
sha2.o: $(hdrdir)/ruby/internal/value.h
sha2.o: $(hdrdir)/ruby/internal/value_type.h
@@ -311,6 +312,7 @@ sha2init.o: $(hdrdir)/ruby/internal/special_consts.h
sha2init.o: $(hdrdir)/ruby/internal/static_assert.h
sha2init.o: $(hdrdir)/ruby/internal/stdalign.h
sha2init.o: $(hdrdir)/ruby/internal/stdbool.h
+sha2init.o: $(hdrdir)/ruby/internal/stdckdint.h
sha2init.o: $(hdrdir)/ruby/internal/symbol.h
sha2init.o: $(hdrdir)/ruby/internal/value.h
sha2init.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/digest/sha2/sha2init.c b/ext/digest/sha2/sha2init.c
index 94cccf3feb..3923e3724c 100644
--- a/ext/digest/sha2/sha2init.c
+++ b/ext/digest/sha2/sha2init.c
@@ -25,29 +25,50 @@ static const rb_digest_metadata_t sha##bitlen = { \
FOREACH_BITLEN(DEFINE_ALGO_METADATA)
/*
+ * Document-class: Digest::SHA256 < Digest::Base
+ *
* Classes for calculating message digests using the SHA-256/384/512
* Secure Hash Algorithm(s) by NIST (the US' National Institute of
* Standards and Technology), described in FIPS PUB 180-2.
+ *
+ * See SHA2.
+ */
+/*
+ * Document-class: Digest::SHA384 < Digest::Base
+ *
+ * Classes for calculating message digests using the SHA-256/384/512
+ * Secure Hash Algorithm(s) by NIST (the US' National Institute of
+ * Standards and Technology), described in FIPS PUB 180-2.
+ *
+ * See SHA2.
+ */
+/*
+ * Document-class: Digest::SHA512 < Digest::Base
+ *
+ * Classes for calculating message digests using the SHA-256/384/512
+ * Secure Hash Algorithm(s) by NIST (the US' National Institute of
+ * Standards and Technology), described in FIPS PUB 180-2.
+ *
+ * See SHA2.
*/
void
Init_sha2(void)
{
- VALUE mDigest, cDigest_Base;
+ VALUE mDigest, cDigest_Base, cDigest_SHA2;
ID id_metadata = rb_id_metadata();
-#define DECLARE_ALGO_CLASS(bitlen) \
- VALUE cDigest_SHA##bitlen;
-
- FOREACH_BITLEN(DECLARE_ALGO_CLASS)
-
+#if 0
+ mDigest = rb_define_module("Digest"); /* let rdoc know */
+#endif
mDigest = rb_digest_namespace();
- cDigest_Base = rb_path2class("Digest::Base");
+ cDigest_Base = rb_const_get(mDigest, rb_intern_const("Base"));
+
+ cDigest_SHA2 = rb_define_class_under(mDigest, "SHA256", cDigest_Base);
+ rb_ivar_set(cDigest_SHA2, id_metadata, rb_digest_make_metadata(&sha256));
-#define DEFINE_ALGO_CLASS(bitlen) \
- cDigest_SHA##bitlen = rb_define_class_under(mDigest, "SHA" #bitlen, cDigest_Base); \
-\
- rb_ivar_set(cDigest_SHA##bitlen, id_metadata, \
- rb_digest_make_metadata(&sha##bitlen));
+ cDigest_SHA2 = rb_define_class_under(mDigest, "SHA384", cDigest_Base);
+ rb_ivar_set(cDigest_SHA2, id_metadata, rb_digest_make_metadata(&sha384));
- FOREACH_BITLEN(DEFINE_ALGO_CLASS)
+ cDigest_SHA2 = rb_define_class_under(mDigest, "SHA512", cDigest_Base);
+ rb_ivar_set(cDigest_SHA2, id_metadata, rb_digest_make_metadata(&sha512));
}
diff --git a/ext/erb/escape/extconf.rb b/ext/erb/escape/extconf.rb
index c1002548ad..783e8c1f55 100644
--- a/ext/erb/escape/extconf.rb
+++ b/ext/erb/escape/extconf.rb
@@ -1,6 +1,7 @@
require 'mkmf'
-if RUBY_ENGINE == 'truffleruby'
+case RUBY_ENGINE
+when 'jruby', 'truffleruby'
File.write('Makefile', dummy_makefile($srcdir).join)
else
create_makefile 'erb/escape'
diff --git a/ext/etc/.document b/ext/etc/.document
new file mode 100644
index 0000000000..9bbea23b92
--- /dev/null
+++ b/ext/etc/.document
@@ -0,0 +1,2 @@
+etc.c
+constdefs.h
diff --git a/ext/etc/depend b/ext/etc/depend
index 00787b6aaf..675699b129 100644
--- a/ext/etc/depend
+++ b/ext/etc/depend
@@ -162,6 +162,7 @@ etc.o: $(hdrdir)/ruby/internal/special_consts.h
etc.o: $(hdrdir)/ruby/internal/static_assert.h
etc.o: $(hdrdir)/ruby/internal/stdalign.h
etc.o: $(hdrdir)/ruby/internal/stdbool.h
+etc.o: $(hdrdir)/ruby/internal/stdckdint.h
etc.o: $(hdrdir)/ruby/internal/symbol.h
etc.o: $(hdrdir)/ruby/internal/value.h
etc.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/etc/etc.c b/ext/etc/etc.c
index 0b3e5ffd5e..fcbd1af1b5 100644
--- a/ext/etc/etc.c
+++ b/ext/etc/etc.c
@@ -54,9 +54,9 @@ static VALUE sGroup;
# include <stdlib.h>
# endif
#endif
-char *getlogin();
+RUBY_EXTERN char *getlogin(void);
-#define RUBY_ETC_VERSION "1.4.2"
+#define RUBY_ETC_VERSION "1.4.3"
#ifdef HAVE_RB_DEPRECATE_CONSTANT
void rb_deprecate_constant(VALUE mod, const char *name);
@@ -68,7 +68,8 @@ void rb_deprecate_constant(VALUE mod, const char *name);
#ifndef HAVE_RB_IO_DESCRIPTOR
static int
-io_descriptor_fallback(VALUE io) {
+io_descriptor_fallback(VALUE io)
+{
rb_io_t *fptr;
GetOpenFile(io, fptr);
return fptr->fd;
@@ -202,7 +203,7 @@ setup_passwd(struct passwd *pwd)
#endif
/* call-seq:
- * getpwuid(uid) -> Passwd
+ * getpwuid(uid) -> Etc::Passwd
*
* Returns the <tt>/etc/passwd</tt> information for the user with the given
* integer +uid+.
@@ -214,7 +215,7 @@ setup_passwd(struct passwd *pwd)
*
* See the unix manpage for <code>getpwuid(3)</code> for more detail.
*
- * === Example:
+ * *Example:*
*
* Etc.getpwuid(0)
* #=> #<struct Etc::Passwd name="root", passwd="x", uid=0, gid=0, gecos="root",dir="/root", shell="/bin/bash">
@@ -242,7 +243,7 @@ etc_getpwuid(int argc, VALUE *argv, VALUE obj)
}
/* call-seq:
- * getpwnam(name) -> Passwd
+ * getpwnam(name) -> Etc::Passwd
*
* Returns the <tt>/etc/passwd</tt> information for the user with specified
* login +name+.
@@ -251,7 +252,7 @@ etc_getpwuid(int argc, VALUE *argv, VALUE obj)
*
* See the unix manpage for <code>getpwnam(3)</code> for more detail.
*
- * === Example:
+ * *Example:*
*
* Etc.getpwnam('root')
* #=> #<struct Etc::Passwd name="root", passwd="x", uid=0, gid=0, gecos="root",dir="/root", shell="/bin/bash">
@@ -306,8 +307,8 @@ each_passwd(void)
#endif
/* call-seq:
- * Etc.passwd { |struct| block } -> Passwd
- * Etc.passwd -> Passwd
+ * passwd { |struct| block }
+ * passwd -> Etc::Passwd
*
* Provides a convenient Ruby iterator which executes a block for each entry
* in the <tt>/etc/passwd</tt> file.
@@ -316,7 +317,7 @@ each_passwd(void)
*
* See ::getpwent above for details.
*
- * Example:
+ * *Example:*
*
* require 'etc'
*
@@ -342,7 +343,7 @@ etc_passwd(VALUE obj)
}
/* call-seq:
- * Etc::Passwd.each { |struct| block } -> Passwd
+ * Etc::Passwd.each { |struct| block } -> Etc::Passwd
* Etc::Passwd.each -> Enumerator
*
* Iterates for each entry in the <tt>/etc/passwd</tt> file if a block is
@@ -354,7 +355,7 @@ etc_passwd(VALUE obj)
*
* See Etc.getpwent above for details.
*
- * Example:
+ * *Example:*
*
* require 'etc'
*
@@ -376,7 +377,10 @@ etc_each_passwd(VALUE obj)
return obj;
}
-/* Resets the process of reading the <tt>/etc/passwd</tt> file, so that the
+/* call-seq:
+ * setpwent
+ *
+ * Resets the process of reading the <tt>/etc/passwd</tt> file, so that the
* next call to ::getpwent will return the first entry again.
*/
static VALUE
@@ -388,7 +392,10 @@ etc_setpwent(VALUE obj)
return Qnil;
}
-/* Ends the process of scanning through the <tt>/etc/passwd</tt> file begun
+/* call-seq:
+ * endpwent
+ *
+ * Ends the process of scanning through the <tt>/etc/passwd</tt> file begun
* with ::getpwent, and closes the file.
*/
static VALUE
@@ -400,7 +407,10 @@ etc_endpwent(VALUE obj)
return Qnil;
}
-/* Returns an entry from the <tt>/etc/passwd</tt> file.
+/* call-seq:
+ * getpwent -> Etc::Passwd
+ *
+ * Returns an entry from the <tt>/etc/passwd</tt> file.
*
* The first time it is called it opens the file and returns the first entry;
* each successive call returns the next entry, or +nil+ if the end of the file
@@ -448,7 +458,7 @@ setup_group(struct group *grp)
#endif
/* call-seq:
- * getgrgid(group_id) -> Group
+ * getgrgid(group_id) -> Etc::Group
*
* Returns information about the group with specified integer +group_id+,
* as found in <tt>/etc/group</tt>.
@@ -457,7 +467,7 @@ setup_group(struct group *grp)
*
* See the unix manpage for <code>getgrgid(3)</code> for more detail.
*
- * === Example:
+ * *Example:*
*
* Etc.getgrgid(100)
* #=> #<struct Etc::Group name="users", passwd="x", gid=100, mem=["meta", "root"]>
@@ -486,7 +496,7 @@ etc_getgrgid(int argc, VALUE *argv, VALUE obj)
}
/* call-seq:
- * getgrnam(name) -> Group
+ * getgrnam(name) -> Etc::Group
*
* Returns information about the group with specified +name+, as found in
* <tt>/etc/group</tt>.
@@ -495,7 +505,7 @@ etc_getgrgid(int argc, VALUE *argv, VALUE obj)
*
* See the unix manpage for <code>getgrnam(3)</code> for more detail.
*
- * === Example:
+ * *Example:*
*
* Etc.getgrnam('users')
* #=> #<struct Etc::Group name="users", passwd="x", gid=100, mem=["meta", "root"]>
@@ -528,7 +538,6 @@ group_ensure(VALUE _)
return Qnil;
}
-
static VALUE
group_iterate(VALUE _)
{
@@ -551,14 +560,18 @@ each_group(void)
}
#endif
-/* Provides a convenient Ruby iterator which executes a block for each entry
+/* call-seq:
+ * group { |struct| block }
+ * group -> Etc::Group
+ *
+ * Provides a convenient Ruby iterator which executes a block for each entry
* in the <tt>/etc/group</tt> file.
*
* The code block is passed an Group struct.
*
* See ::getgrent above for details.
*
- * Example:
+ * *Example:*
*
* require 'etc'
*
@@ -585,7 +598,7 @@ etc_group(VALUE obj)
#ifdef HAVE_GETGRENT
/* call-seq:
- * Etc::Group.each { |group| block } -> obj
+ * Etc::Group.each { |group| block } -> Etc::Group
* Etc::Group.each -> Enumerator
*
* Iterates for each entry in the <tt>/etc/group</tt> file if a block is
@@ -595,7 +608,7 @@ etc_group(VALUE obj)
*
* The code block is passed a Group struct.
*
- * Example:
+ * *Example:*
*
* require 'etc'
*
@@ -616,7 +629,10 @@ etc_each_group(VALUE obj)
}
#endif
-/* Resets the process of reading the <tt>/etc/group</tt> file, so that the
+/* call-seq:
+ * setgrent
+ *
+ * Resets the process of reading the <tt>/etc/group</tt> file, so that the
* next call to ::getgrent will return the first entry again.
*/
static VALUE
@@ -628,7 +644,10 @@ etc_setgrent(VALUE obj)
return Qnil;
}
-/* Ends the process of scanning through the <tt>/etc/group</tt> file begun
+/* call-seq:
+ * endgrent
+ *
+ * Ends the process of scanning through the <tt>/etc/group</tt> file begun
* by ::getgrent, and closes the file.
*/
static VALUE
@@ -640,7 +659,10 @@ etc_endgrent(VALUE obj)
return Qnil;
}
-/* Returns an entry from the <tt>/etc/group</tt> file.
+/* call-seq:
+ * getgrent -> Etc::Group
+ *
+ * Returns an entry from the <tt>/etc/group</tt> file.
*
* The first time it is called it opens the file and returns the first entry;
* each successive call returns the next entry, or +nil+ if the end of the file
@@ -671,7 +693,9 @@ UINT rb_w32_system_tmpdir(WCHAR *path, UINT len);
VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc);
#endif
-/*
+/* call-seq:
+ * sysconfdir -> String
+ *
* Returns system configuration directory.
*
* This is typically <code>"/etc"</code>, but is modified by the prefix used
@@ -691,7 +715,9 @@ etc_sysconfdir(VALUE obj)
#endif
}
-/*
+/* call-seq:
+ * systmpdir -> String
+ *
* Returns system temporary directory; typically "/tmp".
*/
static VALUE
@@ -735,13 +761,15 @@ etc_systmpdir(VALUE _)
}
#ifdef HAVE_UNAME
-/*
+/* call-seq:
+ * uname -> hash
+ *
* Returns the system information obtained by uname system call.
*
* The return value is a hash which has 5 keys at least:
* :sysname, :nodename, :release, :version, :machine
*
- * Example:
+ * *Example:*
*
* require 'etc'
* require 'pp'
@@ -851,7 +879,9 @@ etc_uname(VALUE obj)
#endif
#ifdef HAVE_SYSCONF
-/*
+/* call-seq:
+ * sysconf(name) -> Integer
+ *
* Returns system configuration variable using sysconf().
*
* _name_ should be a constant under <code>Etc</code> which begins with <code>SC_</code>.
@@ -885,7 +915,9 @@ etc_sysconf(VALUE obj, VALUE arg)
#endif
#ifdef HAVE_CONFSTR
-/*
+/* call-seq:
+ * confstr(name) -> String
+ *
* Returns system configuration variable using confstr().
*
* _name_ should be a constant under <code>Etc</code> which begins with <code>CS_</code>.
@@ -932,7 +964,9 @@ etc_confstr(VALUE obj, VALUE arg)
#endif
#ifdef HAVE_FPATHCONF
-/*
+/* call-seq:
+ * pathconf(name) -> Integer
+ *
* Returns pathname configuration variable using fpathconf().
*
* _name_ should be a constant under <code>Etc</code> which begins with <code>PC_</code>.
@@ -1024,7 +1058,9 @@ etc_nprocessors_affin(void)
}
#endif
-/*
+/* call-seq:
+ * nprocessors -> Integer
+ *
* Returns the number of online processors.
*
* The result is intended as the number of processes to
@@ -1034,7 +1070,7 @@ etc_nprocessors_affin(void)
* - sched_getaffinity(): Linux
* - sysconf(_SC_NPROCESSORS_ONLN): GNU/Linux, NetBSD, FreeBSD, OpenBSD, DragonFly BSD, OpenIndiana, Mac OS X, AIX
*
- * Example:
+ * *Example:*
*
* require 'etc'
* p Etc.nprocessors #=> 4
@@ -1043,7 +1079,7 @@ etc_nprocessors_affin(void)
* process is bound to specific cpus. This is intended for getting better
* parallel processing.
*
- * Example: (Linux)
+ * *Example:* (Linux)
*
* linux$ taskset 0x3 ./ruby -retc -e "p Etc.nprocessors" #=> 2
*
@@ -1093,7 +1129,7 @@ etc_nprocessors(VALUE obj)
* The Etc module provides a more reliable way to access information about
* the logged in user than environment variables such as +$USER+.
*
- * == Example:
+ * *Example:*
*
* require 'etc'
*
@@ -1117,6 +1153,7 @@ Init_etc(void)
RB_EXT_RACTOR_SAFE(true);
#endif
mEtc = rb_define_module("Etc");
+ /* The version */
rb_define_const(mEtc, "VERSION", rb_str_new_cstr(RUBY_ETC_VERSION));
init_constants(mEtc);
diff --git a/ext/etc/extconf.rb b/ext/etc/extconf.rb
index 7c30b38bfd..2e28d58037 100644
--- a/ext/etc/extconf.rb
+++ b/ext/etc/extconf.rb
@@ -43,8 +43,16 @@ have_struct_member('struct group', 'gr_passwd', 'grp.h')
# for https://github.com/ruby/etc
srcdir = File.expand_path("..", __FILE__)
-if !File.exist?("#{srcdir}/depend")
- %x[#{RbConfig.ruby} #{srcdir}/mkconstants.rb -o #{srcdir}/constdefs.h]
+constdefs = "#{srcdir}/constdefs.h"
+if !File.exist?(constdefs)
+ ruby = RbConfig.ruby
+ if File.file?(ruby)
+ ruby = [ruby]
+ else
+ require "shellwords"
+ ruby = Shellwords.split(ruby)
+ end
+ system(*ruby, "#{srcdir}/mkconstants.rb", "-o", constdefs)
end
# TODO: remove when dropping 2.7 support, as exported since 3.0
diff --git a/ext/etc/mkconstants.rb b/ext/etc/mkconstants.rb
index a752d64519..a766560a8a 100644
--- a/ext/etc/mkconstants.rb
+++ b/ext/etc/mkconstants.rb
@@ -35,6 +35,12 @@ opt.def_option('-H FILE', 'specify output header file') {|filename|
opt.parse!
+CONST_PREFIXES = {
+ 'SC' => 'for Etc.sysconf; See <tt>man sysconf</tt>',
+ 'CS' => 'for Etc.confstr; See <tt>man constr</tt>',
+ 'PC' => 'for IO#pathconf; See <tt>man fpathconf</tt>',
+}
+
h = {}
COMMENTS = {}
@@ -49,6 +55,13 @@ DATA.each_line {|s|
next
end
h[name] = default_value
+ if additional = CONST_PREFIXES[name[/\A_([A-Z]+)_/, 1]]
+ if comment&.match(/\w\z/)
+ comment << " " << additional
+ else
+ (comment ||= String.new) << " " << additional.sub(/\A\w/) {$&.upcase}
+ end
+ end
COMMENTS[name] = comment if comment
}
DEFS = h.to_a
@@ -66,15 +79,11 @@ def each_name(pat)
}
end
-erb_new = lambda do |src, safe, trim|
- if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
- ERB.new(src, trim_mode: trim)
- else
- ERB.new(src, safe, trim)
- end
+erb_new = lambda do |src, trim|
+ ERB.new(src, trim_mode: trim)
end
-erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_const_decls")
+erb_new.call(<<'EOS', '%').def_method(Object, "gen_const_decls")
% each_const {|name, default_value|
#if !defined(<%=name%>)
# if defined(HAVE_CONST_<%=name.upcase%>)
@@ -88,7 +97,7 @@ erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_const_decls")
% }
EOS
-erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_const_defs")
+erb_new.call(<<'EOS', '%').def_method(Object, "gen_const_defs")
% each_const {|name, default_value|
#if defined(<%=name%>)
% if comment = COMMENTS[name]
@@ -99,13 +108,13 @@ erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_const_defs")
% }
EOS
-header_result = erb_new.call(<<'EOS', nil, '%').result(binding)
+header_result = erb_new.call(<<'EOS', '%').result(binding)
/* autogenerated file */
<%= gen_const_decls %>
EOS
-result = erb_new.call(<<'EOS', nil, '%').result(binding)
+result = erb_new.call(<<'EOS', '%').result(binding)
/* autogenerated file */
#ifdef HAVE_LONG_LONG
@@ -123,6 +132,9 @@ result = erb_new.call(<<'EOS', nil, '%').result(binding)
static void
init_constants(VALUE mod)
{
+#if 0
+ mod = rb_define_module("Etc");
+#endif
<%= gen_const_defs %>
}
EOS
diff --git a/ext/extmk.rb b/ext/extmk.rb
index d6a4b80bf7..2f76e174d5 100755
--- a/ext/extmk.rb
+++ b/ext/extmk.rb
@@ -43,11 +43,7 @@ $" << "mkmf.rb"
load File.expand_path("lib/mkmf.rb", srcdir)
require 'optparse/shellwords'
-if defined?(File::NULL)
- @null = File::NULL
-elsif !File.chardev?(@null = "/dev/null")
- @null = "nul"
-end
+@null = File::NULL
def verbose?
$mflags.defined?("V") == "1"
@@ -108,7 +104,7 @@ def extract_makefile(makefile, keep = true)
end
return false
end
- srcs = Dir[File.join($srcdir, "*.{#{SRC_EXT.join(%q{,})}}")].map {|fn| File.basename(fn)}.sort
+ srcs = Dir[*SRC_EXT.map {|e| "*.#{e}"}, base: $srcdir].map {|fn| File.basename(fn)}.sort
if !srcs.empty?
old_srcs = m[/^ORIG_SRCS[ \t]*=[ \t](.*)/, 1] or return false
(old_srcs.split - srcs).empty? or return false
@@ -136,6 +132,14 @@ def extract_makefile(makefile, keep = true)
true
end
+def create_makefile(target, srcprefix = nil)
+ if $static and target.include?("/")
+ base = File.basename(target)
+ $defs << "-DInit_#{base}=Init_#{target.tr('/', '_')}"
+ end
+ super
+end
+
def extmake(target, basedir = 'ext', maybestatic = true)
FileUtils.mkpath target unless File.directory?(target)
begin
@@ -476,12 +480,13 @@ if exts = ARGV.shift
$extension = [exts] if exts
if ext_prefix.start_with?('.')
@gemname = exts
- elsif exts
- $static_ext.delete_if {|t, *| !File.fnmatch(t, exts)}
+ exts = []
+ else
+ exts &&= $static_ext.select {|t, *| File.fnmatch(t, exts)}
end
end
-ext_prefix = "#{$top_srcdir}/#{ext_prefix || 'ext'}"
-exts = $static_ext.sort_by {|t, i| i}.collect {|t, i| t}
+ext_prefix ||= 'ext'
+exts = (exts || $static_ext).sort_by {|t, i| i}.collect {|t, i| t}
default_exclude_exts =
case
when $cygwin
@@ -494,28 +499,30 @@ default_exclude_exts =
mandatory_exts = {}
withes, withouts = [["--with", nil], ["--without", default_exclude_exts]].collect {|w, d|
if !(w = %w[-extensions -ext].collect {|o|arg_config(w+o)}).any?
- d ? proc {|c1| d.any?(&c1)} : proc {true}
+ d ? proc {|&c1| d.any?(&c1)} : proc {true}
elsif (w = w.grep(String)).empty?
proc {true}
else
w = w.collect {|o| o.split(/,/)}.flatten
w.collect! {|o| o == '+' ? d : o}.flatten!
- proc {|c1| w.any?(&c1)}
+ proc {|&c1| w.any?(&c1)}
end
}
cond = proc {|ext, *|
- withes.call(proc {|n|
- !n or (mandatory_exts[ext] = true if File.fnmatch(n, ext))
- }) and
- !withouts.call(proc {|n| File.fnmatch(n, ext)})
+ withes.call {|n| !n or (mandatory_exts[ext] = true if File.fnmatch(n, ext))} and
+ !withouts.call {|n| File.fnmatch(n, ext)}
}
($extension || %w[*]).each do |e|
e = e.sub(/\A(?:\.\/)+/, '')
- incl, excl = Dir.glob("#{ext_prefix}/#{e}/**/extconf.rb").collect {|d|
- d = File.dirname(d)
- d.slice!(0, ext_prefix.length + 1)
- d
+ incl, excl = Dir.glob("#{e}/**/extconf.rb", base: "#$top_srcdir/#{ext_prefix}").collect {|d|
+ File.dirname(d)
}.partition {|ext|
+ if @gemname
+ ext = ext[%r[\A[^/]+]] # extract gem name
+ Dir.glob("*.gemspec", base: "#$top_srcdir/#{ext_prefix}/#{ext}") do |g|
+ break ext = g if ext.start_with?("#{g.chomp!(".gemspec")}-")
+ end
+ end
with_config(ext, &cond)
}
incl.sort!
@@ -526,7 +533,7 @@ cond = proc {|ext, *|
exts.delete_if {|d| File.fnmatch?("-*", d)}
end
end
-ext_prefix = ext_prefix[$top_srcdir.size+1..-2]
+ext_prefix.chomp!("/")
@ext_prefix = ext_prefix
@inplace = inplace
@@ -549,7 +556,13 @@ extend Module.new {
end
def create_makefile(*args, &block)
- return super unless @gemname
+ unless @gemname
+ if $static and (target = args.first).include?("/")
+ base = File.basename(target)
+ $defs << "-DInit_#{base}=Init_#{target.tr('/', '_')}"
+ end
+ return super
+ end
super(*args) do |conf|
conf.find do |s|
s.sub!(%r(^(srcdir *= *)\$\(top_srcdir\)/\.bundle/gems/[^/]+(?=/))) {
@@ -632,7 +645,9 @@ $hdrdir = ($top_srcdir = relative_from(srcdir, $topdir = "..")) + "/include"
extso = []
fails = []
exts.each do |d|
- $static = $force_static ? true : $static_ext[d]
+ $static = $force_static ? true : $static_ext.fetch(d) do
+ $static_ext.any? {|t, | File.fnmatch?(t, d)}
+ end
if !$nodynamic or $static
result = extmake(d, ext_prefix, !@gemname) or abort
@@ -758,7 +773,6 @@ begin
end
submakeopts << 'EXTLDFLAGS="$(EXTLDFLAGS)"'
submakeopts << 'EXTINITS="$(EXTINITS)"'
- submakeopts << 'UPDATE_LIBRARIES="$(UPDATE_LIBRARIES)"'
submakeopts << 'SHOWFLAGS='
mf.macro "SUBMAKEOPTS", submakeopts
mf.macro "NOTE_MESG", %w[$(RUBY) $(top_srcdir)/tool/lib/colorize.rb skip]
@@ -814,7 +828,7 @@ begin
end
mf.puts "#{t}:#{pd}\n\t$(Q)#{submake} $(MFLAGS) V=$(V) $(@F)"
if clean and clean.begin(1)
- mf.puts "\t$(Q)$(RM) $(ext_build_dir)/exts.mk\n\t$(Q)$(RMDIRS) -p $(@D)"
+ mf.puts "\t$(Q)$(RM) $(ext_build_dir)/exts.mk\n\t$(Q)$(RMDIRS) $(@D)"
end
end
end
diff --git a/ext/fcntl/depend b/ext/fcntl/depend
index 9ce9fa30ef..5ed652563b 100644
--- a/ext/fcntl/depend
+++ b/ext/fcntl/depend
@@ -147,6 +147,7 @@ fcntl.o: $(hdrdir)/ruby/internal/special_consts.h
fcntl.o: $(hdrdir)/ruby/internal/static_assert.h
fcntl.o: $(hdrdir)/ruby/internal/stdalign.h
fcntl.o: $(hdrdir)/ruby/internal/stdbool.h
+fcntl.o: $(hdrdir)/ruby/internal/stdckdint.h
fcntl.o: $(hdrdir)/ruby/internal/symbol.h
fcntl.o: $(hdrdir)/ruby/internal/value.h
fcntl.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/fcntl/fcntl.c b/ext/fcntl/fcntl.c
index 4ea7b29bf5..e34a3aeae3 100644
--- a/ext/fcntl/fcntl.c
+++ b/ext/fcntl/fcntl.c
@@ -28,7 +28,10 @@ pack up your own arguments to pass as args for locking functions, etc.
#include "ruby.h"
#include <fcntl.h>
-/* Fcntl loads the constants defined in the system's <fcntl.h> C header
+/*
+ * Document-module: Fcntl
+ *
+ * Fcntl loads the constants defined in the system's <fcntl.h> C header
* file, and used with both the fcntl(2) and open(2) POSIX system calls.
*
* To perform a fcntl(2) operation, use IO::fcntl.
@@ -62,18 +65,18 @@ pack up your own arguments to pass as args for locking functions, etc.
*
*/
-#define FCNTL_VERSION "1.0.2"
+#define FCNTL_VERSION "1.1.0"
void
Init_fcntl(void)
{
VALUE mFcntl = rb_define_module("Fcntl");
+ /* The version string. */
rb_define_const(mFcntl, "VERSION", rb_str_new_cstr(FCNTL_VERSION));
#ifdef F_DUPFD
- /* Document-const: F_DUPFD
- *
+ /*
* Duplicate a file descriptor to the minimum unused file descriptor
* greater than or equal to the argument.
*
@@ -84,195 +87,164 @@ Init_fcntl(void)
rb_define_const(mFcntl, "F_DUPFD", INT2NUM(F_DUPFD));
#endif
#ifdef F_GETFD
- /* Document-const: F_GETFD
- *
+ /*
* Read the close-on-exec flag of a file descriptor.
*/
rb_define_const(mFcntl, "F_GETFD", INT2NUM(F_GETFD));
#endif
#ifdef F_GETLK
- /* Document-const: F_GETLK
- *
+ /*
* Determine whether a given region of a file is locked. This uses one of
* the F_*LK flags.
*/
rb_define_const(mFcntl, "F_GETLK", INT2NUM(F_GETLK));
#endif
#ifdef F_SETFD
- /* Document-const: F_SETFD
- *
+ /*
* Set the close-on-exec flag of a file descriptor.
*/
rb_define_const(mFcntl, "F_SETFD", INT2NUM(F_SETFD));
#endif
#ifdef F_GETFL
- /* Document-const: F_GETFL
- *
+ /*
* Get the file descriptor flags. This will be one or more of the O_*
* flags.
*/
rb_define_const(mFcntl, "F_GETFL", INT2NUM(F_GETFL));
#endif
#ifdef F_SETFL
- /* Document-const: F_SETFL
- *
+ /*
* Set the file descriptor flags. This will be one or more of the O_*
* flags.
*/
rb_define_const(mFcntl, "F_SETFL", INT2NUM(F_SETFL));
#endif
#ifdef F_SETLK
- /* Document-const: F_SETLK
- *
+ /*
* Acquire a lock on a region of a file. This uses one of the F_*LCK
* flags.
*/
rb_define_const(mFcntl, "F_SETLK", INT2NUM(F_SETLK));
#endif
#ifdef F_SETLKW
- /* Document-const: F_SETLKW
- *
+ /*
* Acquire a lock on a region of a file, waiting if necessary. This uses
* one of the F_*LCK flags
*/
rb_define_const(mFcntl, "F_SETLKW", INT2NUM(F_SETLKW));
#endif
#ifdef FD_CLOEXEC
- /* Document-const: FD_CLOEXEC
- *
+ /*
* the value of the close-on-exec flag.
*/
rb_define_const(mFcntl, "FD_CLOEXEC", INT2NUM(FD_CLOEXEC));
#endif
#ifdef F_RDLCK
- /* Document-const: F_RDLCK
- *
+ /*
* Read lock for a region of a file
*/
rb_define_const(mFcntl, "F_RDLCK", INT2NUM(F_RDLCK));
#endif
#ifdef F_UNLCK
- /* Document-const: F_UNLCK
- *
+ /*
* Remove lock for a region of a file
*/
rb_define_const(mFcntl, "F_UNLCK", INT2NUM(F_UNLCK));
#endif
#ifdef F_WRLCK
- /* Document-const: F_WRLCK
- *
+ /*
* Write lock for a region of a file
*/
rb_define_const(mFcntl, "F_WRLCK", INT2NUM(F_WRLCK));
#endif
#ifdef F_SETPIPE_SZ
- /* Document-const: F_SETPIPE_SZ
- *
+ /*
* Change the capacity of the pipe referred to by fd to be at least arg bytes.
*/
rb_define_const(mFcntl, "F_SETPIPE_SZ", INT2NUM(F_SETPIPE_SZ));
#endif
#ifdef F_GETPIPE_SZ
- /* Document-const: F_GETPIPE_SZ
- *
+ /*
* Return (as the function result) the capacity of the pipe referred to by fd.
*/
rb_define_const(mFcntl, "F_GETPIPE_SZ", INT2NUM(F_GETPIPE_SZ));
#endif
#ifdef O_CREAT
- /* Document-const: O_CREAT
- *
+ /*
* Create the file if it doesn't exist
*/
rb_define_const(mFcntl, "O_CREAT", INT2NUM(O_CREAT));
#endif
#ifdef O_EXCL
- /* Document-const: O_EXCL
- *
+ /*
* Used with O_CREAT, fail if the file exists
*/
rb_define_const(mFcntl, "O_EXCL", INT2NUM(O_EXCL));
#endif
#ifdef O_NOCTTY
- /* Document-const: O_NOCTTY
- *
+ /*
* Open TTY without it becoming the controlling TTY
*/
rb_define_const(mFcntl, "O_NOCTTY", INT2NUM(O_NOCTTY));
#endif
#ifdef O_TRUNC
- /* Document-const: O_TRUNC
- *
+ /*
* Truncate the file on open
*/
rb_define_const(mFcntl, "O_TRUNC", INT2NUM(O_TRUNC));
#endif
#ifdef O_APPEND
- /* Document-const: O_APPEND
- *
+ /*
* Open the file in append mode
*/
rb_define_const(mFcntl, "O_APPEND", INT2NUM(O_APPEND));
#endif
#ifdef O_NONBLOCK
- /* Document-const: O_NONBLOCK
- *
+ /*
* Open the file in non-blocking mode
*/
rb_define_const(mFcntl, "O_NONBLOCK", INT2NUM(O_NONBLOCK));
#endif
#ifdef O_NDELAY
- /* Document-const: O_NDELAY
- *
+ /*
* Open the file in non-blocking mode
*/
rb_define_const(mFcntl, "O_NDELAY", INT2NUM(O_NDELAY));
#endif
#ifdef O_RDONLY
- /* Document-const: O_RDONLY
- *
+ /*
* Open the file in read-only mode
*/
rb_define_const(mFcntl, "O_RDONLY", INT2NUM(O_RDONLY));
#endif
#ifdef O_RDWR
- /* Document-const: O_RDWR
- *
+ /*
* Open the file in read-write mode
*/
rb_define_const(mFcntl, "O_RDWR", INT2NUM(O_RDWR));
#endif
#ifdef O_WRONLY
- /* Document-const: O_WRONLY
- *
+ /*
* Open the file in write-only mode.
*/
rb_define_const(mFcntl, "O_WRONLY", INT2NUM(O_WRONLY));
#endif
-#ifdef O_ACCMODE
- /* Document-const: O_ACCMODE
- *
+#ifndef O_ACCMODE
+ int O_ACCMODE = (O_RDONLY | O_WRONLY | O_RDWR);
+#endif
+ /*
* Mask to extract the read/write flags
*/
rb_define_const(mFcntl, "O_ACCMODE", INT2FIX(O_ACCMODE));
-#else
- /* Document-const: O_ACCMODE
- *
- * Mask to extract the read/write flags
- */
- rb_define_const(mFcntl, "O_ACCMODE", INT2FIX(O_RDONLY | O_WRONLY | O_RDWR));
-#endif
#ifdef F_DUP2FD
- /* Document-const: F_DUP2FD
- *
+ /*
* It is a FreeBSD specific constant and equivalent
* to dup2 call.
*/
rb_define_const(mFcntl, "F_DUP2FD", INT2NUM(F_DUP2FD));
#endif
#ifdef F_DUP2FD_CLOEXEC
- /* Document-const: F_DUP2FD_CLOEXEC
- *
+ /*
* It is a FreeBSD specific constant and acts
* similarly as F_DUP2FD but set the FD_CLOEXEC
* flag in addition.
diff --git a/ext/fcntl/fcntl.gemspec b/ext/fcntl/fcntl.gemspec
index 54aadb4b81..d621bc0491 100644
--- a/ext/fcntl/fcntl.gemspec
+++ b/ext/fcntl/fcntl.gemspec
@@ -23,9 +23,10 @@ Gem::Specification.new do |spec|
spec.licenses = ["Ruby", "BSD-2-Clause"]
spec.files = ["ext/fcntl/extconf.rb", "ext/fcntl/fcntl.c"]
+ spec.extra_rdoc_files = [".document", ".rdoc_options", "LICENSE.txt", "README.md"]
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
spec.extensions = "ext/fcntl/extconf.rb"
- spec.required_ruby_version = ">= 2.3.0"
+ spec.required_ruby_version = ">= 2.5.0"
end
diff --git a/ext/fiddle/closure.c b/ext/fiddle/closure.c
index 892f522a62..2b4cdb6608 100644
--- a/ext/fiddle/closure.c
+++ b/ext/fiddle/closure.c
@@ -1,4 +1,5 @@
#include <fiddle.h>
+#include <stdbool.h>
#include <ruby/thread.h>
int ruby_thread_has_gvl_p(void); /* from internal.h */
@@ -54,10 +55,13 @@ closure_memsize(const void * ptr)
}
const rb_data_type_t closure_data_type = {
- "fiddle/closure",
- {0, dealloc, closure_memsize,},
- 0, 0,
- RUBY_TYPED_FREE_IMMEDIATELY,
+ .wrap_struct_name = "fiddle/closure",
+ .function = {
+ .dmark = 0,
+ .dfree = dealloc,
+ .dsize = closure_memsize
+ },
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
struct callback_args {
@@ -136,6 +140,20 @@ with_gvl_callback(void *ptr)
rb_ary_push(params,
rb_str_new_cstr(*((const char **)(x->args[i]))));
break;
+ case TYPE_BOOL:
+ if (sizeof(bool) == sizeof(char)) {
+ rb_ary_push(params, CBOOL2RBBOOL(*(unsigned char *)x->args[i]));
+ } else if (sizeof(bool) == sizeof(short)) {
+ rb_ary_push(params, CBOOL2RBBOOL(*(unsigned short *)x->args[i]));
+ } else if (sizeof(bool) == sizeof(int)) {
+ rb_ary_push(params, CBOOL2RBBOOL(*(unsigned int *)x->args[i]));
+ } else if (sizeof(bool) == sizeof(long)) {
+ rb_ary_push(params, CBOOL2RBBOOL(*(unsigned long *)x->args[i]));
+ } else {
+ rb_raise(rb_eNotImpError, "bool isn't supported: %u",
+ (unsigned int)sizeof(bool));
+ }
+ break;
default:
rb_raise(rb_eRuntimeError, "closure args: %d", type);
}
@@ -185,6 +203,13 @@ with_gvl_callback(void *ptr)
/* Dangerous. Callback must keep reference of the String. */
*((const char **)(x->resp)) = StringValueCStr(ret);
break;
+ case TYPE_BOOL:
+ if (sizeof(bool) == sizeof(long)) {
+ *(unsigned long *)x->resp = RB_TEST(ret);
+ } else {
+ *(ffi_arg *)x->resp = RB_TEST(ret);
+ }
+ break;
default:
rb_raise(rb_eRuntimeError, "closure retval: %d", type);
}
diff --git a/ext/fiddle/conversions.c b/ext/fiddle/conversions.c
index 3b70f7de4c..796bf929c3 100644
--- a/ext/fiddle/conversions.c
+++ b/ext/fiddle/conversions.c
@@ -1,3 +1,5 @@
+#include <stdbool.h>
+
#include <fiddle.h>
VALUE
@@ -44,6 +46,7 @@ rb_fiddle_type_ensure(VALUE type)
ID ptrdiff_t_id;
ID intptr_t_id;
ID uintptr_t_id;
+ ID bool_id;
RUBY_CONST_ID(void_id, "void");
RUBY_CONST_ID(voidp_id, "voidp");
RUBY_CONST_ID(char_id, "char");
@@ -74,6 +77,7 @@ rb_fiddle_type_ensure(VALUE type)
RUBY_CONST_ID(ptrdiff_t_id, "ptrdiff_t");
RUBY_CONST_ID(intptr_t_id, "intptr_t");
RUBY_CONST_ID(uintptr_t_id, "uintptr_t");
+ RUBY_CONST_ID(bool_id, "bool");
if (type_id == void_id) {
return INT2NUM(TYPE_VOID);
}
@@ -144,6 +148,9 @@ rb_fiddle_type_ensure(VALUE type)
else if (type_id == uintptr_t_id) {
return INT2NUM(TYPE_UINTPTR_T);
}
+ else if (type_id == bool_id) {
+ return INT2NUM(TYPE_BOOL);
+ }
else {
type = original_type;
}
@@ -187,6 +194,20 @@ rb_fiddle_int_to_ffi_type(int type)
return &ffi_type_double;
case TYPE_CONST_STRING:
return &ffi_type_pointer;
+ case TYPE_BOOL:
+ signed_p = 0;
+ if (sizeof(bool) == sizeof(char)) {
+ return rb_ffi_type_of(char);
+ } else if (sizeof(bool) == sizeof(short)) {
+ return rb_ffi_type_of(short);
+ } else if (sizeof(bool) == sizeof(int)) {
+ return rb_ffi_type_of(int);
+ } else if (sizeof(bool) == sizeof(long)) {
+ return rb_ffi_type_of(long);
+ } else {
+ rb_raise(rb_eNotImpError, "bool isn't supported: %u",
+ (unsigned int)sizeof(bool));
+ }
default:
rb_raise(rb_eRuntimeError, "unknown type %d", type);
}
@@ -209,7 +230,11 @@ rb_fiddle_value_to_generic(int type, VALUE *src, fiddle_generic *dst)
dst->pointer = NUM2PTR(rb_Integer(*src));
break;
case TYPE_CHAR:
- dst->schar = (signed char)NUM2INT(*src);
+ if (RB_TYPE_P(*src, RUBY_T_STRING) && RSTRING_LEN(*src) == 1) {
+ dst->schar = RSTRING_PTR(*src)[0];
+ } else {
+ dst->schar = (signed char)NUM2INT(*src);
+ }
break;
case TYPE_UCHAR:
dst->uchar = (unsigned char)NUM2UINT(*src);
@@ -254,8 +279,23 @@ rb_fiddle_value_to_generic(int type, VALUE *src, fiddle_generic *dst)
dst->pointer = rb_string_value_cstr(src);
}
break;
+ case TYPE_BOOL:
+ if (sizeof(bool) == sizeof(char)) {
+ dst->uchar = RB_TEST(*src);
+ } else if (sizeof(bool) == sizeof(short)) {
+ dst->ushort = RB_TEST(*src);
+ } else if (sizeof(bool) == sizeof(int)) {
+ dst->uint = RB_TEST(*src);
+ } else if (sizeof(bool) == sizeof(long)) {
+ dst->ulong = RB_TEST(*src);
+ } else {
+ rb_raise(rb_eNotImpError, "bool isn't supported: %u",
+ (unsigned int)sizeof(bool));
+ }
+ break;
default:
rb_raise(rb_eRuntimeError, "unknown type %d", type);
+ break;
}
}
@@ -314,6 +354,19 @@ rb_fiddle_generic_to_value(VALUE rettype, fiddle_generic retval)
else {
return Qnil;
}
+ case TYPE_BOOL:
+ if (sizeof(bool) == sizeof(char)) {
+ return CBOOL2RBBOOL((unsigned char)retval.fffi_arg);
+ } else if (sizeof(bool) == sizeof(short)) {
+ return CBOOL2RBBOOL((unsigned short)retval.fffi_arg);
+ } else if (sizeof(bool) == sizeof(int)) {
+ return CBOOL2RBBOOL((unsigned int)retval.fffi_arg);
+ } else if (sizeof(bool) == sizeof(long)) {
+ return CBOOL2RBBOOL(retval.ulong);
+ } else {
+ rb_raise(rb_eNotImpError, "bool isn't supported: %u",
+ (unsigned int)sizeof(bool));
+ }
default:
rb_raise(rb_eRuntimeError, "unknown type %d", type);
}
diff --git a/ext/fiddle/conversions.h b/ext/fiddle/conversions.h
index c7c12a9234..7a1e928d56 100644
--- a/ext/fiddle/conversions.h
+++ b/ext/fiddle/conversions.h
@@ -50,4 +50,6 @@ VALUE generic_to_value(VALUE rettype, fiddle_generic retval);
# define NUM2PTR(x) ((void*)(NUM2ULL(x)))
#endif
+#define CBOOL2RBBOOL(cbool) ((cbool) ? RUBY_Qtrue : RUBY_Qfalse)
+
#endif
diff --git a/ext/fiddle/depend b/ext/fiddle/depend
index 561785275e..43b11ca780 100644
--- a/ext/fiddle/depend
+++ b/ext/fiddle/depend
@@ -200,6 +200,7 @@ closure.o: $(hdrdir)/ruby/internal/special_consts.h
closure.o: $(hdrdir)/ruby/internal/static_assert.h
closure.o: $(hdrdir)/ruby/internal/stdalign.h
closure.o: $(hdrdir)/ruby/internal/stdbool.h
+closure.o: $(hdrdir)/ruby/internal/stdckdint.h
closure.o: $(hdrdir)/ruby/internal/symbol.h
closure.o: $(hdrdir)/ruby/internal/value.h
closure.o: $(hdrdir)/ruby/internal/value_type.h
@@ -364,6 +365,7 @@ conversions.o: $(hdrdir)/ruby/internal/special_consts.h
conversions.o: $(hdrdir)/ruby/internal/static_assert.h
conversions.o: $(hdrdir)/ruby/internal/stdalign.h
conversions.o: $(hdrdir)/ruby/internal/stdbool.h
+conversions.o: $(hdrdir)/ruby/internal/stdckdint.h
conversions.o: $(hdrdir)/ruby/internal/symbol.h
conversions.o: $(hdrdir)/ruby/internal/value.h
conversions.o: $(hdrdir)/ruby/internal/value_type.h
@@ -527,6 +529,7 @@ fiddle.o: $(hdrdir)/ruby/internal/special_consts.h
fiddle.o: $(hdrdir)/ruby/internal/static_assert.h
fiddle.o: $(hdrdir)/ruby/internal/stdalign.h
fiddle.o: $(hdrdir)/ruby/internal/stdbool.h
+fiddle.o: $(hdrdir)/ruby/internal/stdckdint.h
fiddle.o: $(hdrdir)/ruby/internal/symbol.h
fiddle.o: $(hdrdir)/ruby/internal/value.h
fiddle.o: $(hdrdir)/ruby/internal/value_type.h
@@ -690,6 +693,7 @@ function.o: $(hdrdir)/ruby/internal/special_consts.h
function.o: $(hdrdir)/ruby/internal/static_assert.h
function.o: $(hdrdir)/ruby/internal/stdalign.h
function.o: $(hdrdir)/ruby/internal/stdbool.h
+function.o: $(hdrdir)/ruby/internal/stdckdint.h
function.o: $(hdrdir)/ruby/internal/symbol.h
function.o: $(hdrdir)/ruby/internal/value.h
function.o: $(hdrdir)/ruby/internal/value_type.h
@@ -854,6 +858,7 @@ handle.o: $(hdrdir)/ruby/internal/special_consts.h
handle.o: $(hdrdir)/ruby/internal/static_assert.h
handle.o: $(hdrdir)/ruby/internal/stdalign.h
handle.o: $(hdrdir)/ruby/internal/stdbool.h
+handle.o: $(hdrdir)/ruby/internal/stdckdint.h
handle.o: $(hdrdir)/ruby/internal/symbol.h
handle.o: $(hdrdir)/ruby/internal/value.h
handle.o: $(hdrdir)/ruby/internal/value_type.h
@@ -1027,6 +1032,7 @@ memory_view.o: $(hdrdir)/ruby/internal/special_consts.h
memory_view.o: $(hdrdir)/ruby/internal/static_assert.h
memory_view.o: $(hdrdir)/ruby/internal/stdalign.h
memory_view.o: $(hdrdir)/ruby/internal/stdbool.h
+memory_view.o: $(hdrdir)/ruby/internal/stdckdint.h
memory_view.o: $(hdrdir)/ruby/internal/symbol.h
memory_view.o: $(hdrdir)/ruby/internal/value.h
memory_view.o: $(hdrdir)/ruby/internal/value_type.h
@@ -1193,6 +1199,7 @@ pinned.o: $(hdrdir)/ruby/internal/special_consts.h
pinned.o: $(hdrdir)/ruby/internal/static_assert.h
pinned.o: $(hdrdir)/ruby/internal/stdalign.h
pinned.o: $(hdrdir)/ruby/internal/stdbool.h
+pinned.o: $(hdrdir)/ruby/internal/stdckdint.h
pinned.o: $(hdrdir)/ruby/internal/symbol.h
pinned.o: $(hdrdir)/ruby/internal/value.h
pinned.o: $(hdrdir)/ruby/internal/value_type.h
@@ -1366,6 +1373,7 @@ pointer.o: $(hdrdir)/ruby/internal/special_consts.h
pointer.o: $(hdrdir)/ruby/internal/static_assert.h
pointer.o: $(hdrdir)/ruby/internal/stdalign.h
pointer.o: $(hdrdir)/ruby/internal/stdbool.h
+pointer.o: $(hdrdir)/ruby/internal/stdckdint.h
pointer.o: $(hdrdir)/ruby/internal/symbol.h
pointer.o: $(hdrdir)/ruby/internal/value.h
pointer.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/fiddle/extconf.rb b/ext/fiddle/extconf.rb
index cf8b5223bb..2d85b3eea5 100644
--- a/ext/fiddle/extconf.rb
+++ b/ext/fiddle/extconf.rb
@@ -63,6 +63,11 @@ unless bundle
end
if have_ffi_header && (have_library('ffi') || have_library('libffi'))
have_libffi = true
+ checking_for("undefined FFI_GO_CLOSURES is used") do
+ if egrep_cpp(/warning: 'FFI_GO_CLOSURES' is not defined/, cpp_include(ffi_header), "2>&1")
+ $defs.push('-DFFI_GO_CLOSURES=0')
+ end
+ end
end
end
diff --git a/ext/fiddle/fiddle.c b/ext/fiddle/fiddle.c
index 9ab1283ba3..f420d9fa3b 100644
--- a/ext/fiddle/fiddle.c
+++ b/ext/fiddle/fiddle.c
@@ -1,3 +1,5 @@
+#include <stdbool.h>
+
#include <fiddle.h>
VALUE mFiddle;
@@ -357,6 +359,12 @@ Init_fiddle(void)
*/
rb_define_const(mFiddleTypes, "UINTPTR_T", INT2NUM(TYPE_UINTPTR_T));
+ /* Document-const: Fiddle::Types::BOOL
+ *
+ * C type - bool
+ */
+ rb_define_const(mFiddleTypes, "BOOL" , INT2NUM(TYPE_BOOL));
+
/* Document-const: ALIGN_VOIDP
*
* The alignment size of a void*
@@ -461,6 +469,12 @@ Init_fiddle(void)
*/
rb_define_const(mFiddle, "ALIGN_UINTPTR_T", INT2NUM(ALIGN_OF(uintptr_t)));
+ /* Document-const: ALIGN_BOOL
+ *
+ * The alignment size of a bool
+ */
+ rb_define_const(mFiddle, "ALIGN_BOOL", INT2NUM(ALIGN_OF(bool)));
+
/* Document-const: WINDOWS
*
* Returns a boolean regarding whether the host is WIN32
@@ -635,6 +649,12 @@ Init_fiddle(void)
*/
rb_define_const(mFiddle, "SIZEOF_CONST_STRING", INT2NUM(sizeof(const char*)));
+ /* Document-const: SIZEOF_BOOL
+ *
+ * size of a bool
+ */
+ rb_define_const(mFiddle, "SIZEOF_BOOL", INT2NUM(sizeof(bool)));
+
/* Document-const: RUBY_FREE
*
* Address of the ruby_xfree() function
diff --git a/ext/fiddle/fiddle.gemspec b/ext/fiddle/fiddle.gemspec
index 878109395b..3a1072dd49 100644
--- a/ext/fiddle/fiddle.gemspec
+++ b/ext/fiddle/fiddle.gemspec
@@ -56,4 +56,5 @@ Gem::Specification.new do |spec|
spec.required_ruby_version = ">= 2.5.0"
spec.metadata["msys2_mingw_dependencies"] = "libffi"
+ spec.metadata["changelog_uri"] = "https://github.com/ruby/fiddle/releases"
end
diff --git a/ext/fiddle/fiddle.h b/ext/fiddle/fiddle.h
index 684b943d30..348baa9ab9 100644
--- a/ext/fiddle/fiddle.h
+++ b/ext/fiddle/fiddle.h
@@ -118,7 +118,7 @@
#define TYPE_UINT -TYPE_INT
#define TYPE_LONG 5
#define TYPE_ULONG -TYPE_LONG
-#if HAVE_LONG_LONG
+#ifdef HAVE_LONG_LONG
#define TYPE_LONG_LONG 6
#define TYPE_ULONG_LONG -TYPE_LONG_LONG
#endif
@@ -126,6 +126,7 @@
#define TYPE_DOUBLE 8
#define TYPE_VARIADIC 9
#define TYPE_CONST_STRING 10
+#define TYPE_BOOL 11
#define TYPE_INT8_T TYPE_CHAR
#define TYPE_UINT8_T -TYPE_INT8_T
@@ -199,7 +200,10 @@
/* GCC releases before GCC 4.9 had a bug in _Alignof. See GCC bug 52023
<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52023>.
clang versions < 8.0.0 have the same bug. */
-#if (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112 \
+#if defined(HAVE__ALIGNOF)
+# /* Autoconf detected availability of a sane `_Alignof()`. */
+# define ALIGN_OF(type) RB_GNUC_EXTENSION(_Alignof(type))
+#elif (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112 \
|| (defined(__GNUC__) && __GNUC__ < 4 + (__GNUC_MINOR__ < 9) \
&& !defined(__clang__)) \
|| (defined(__clang__) && __clang_major__ < 8))
diff --git a/ext/fiddle/function.c b/ext/fiddle/function.c
index 274d181d17..5469e09e37 100644
--- a/ext/fiddle/function.c
+++ b/ext/fiddle/function.c
@@ -53,8 +53,13 @@ function_memsize(const void *p)
}
const rb_data_type_t function_data_type = {
- "fiddle/function",
- {0, deallocate, function_memsize,},
+ .wrap_struct_name = "fiddle/function",
+ .function = {
+ .dmark = 0,
+ .dfree = deallocate,
+ .dsize = function_memsize
+ },
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE
diff --git a/ext/fiddle/handle.c b/ext/fiddle/handle.c
index ae8cc3a581..8ba416952a 100644
--- a/ext/fiddle/handle.c
+++ b/ext/fiddle/handle.c
@@ -50,8 +50,13 @@ fiddle_handle_memsize(const void *ptr)
}
static const rb_data_type_t fiddle_handle_data_type = {
- "fiddle/handle",
- {0, fiddle_handle_free, fiddle_handle_memsize,},
+ .wrap_struct_name = "fiddle/handle",
+ .function = {
+ .dmark = 0,
+ .dfree = fiddle_handle_free,
+ .dsize = fiddle_handle_memsize
+ },
+ .flags = RUBY_TYPED_WB_PROTECTED,
};
/*
diff --git a/ext/fiddle/lib/fiddle/cparser.rb b/ext/fiddle/lib/fiddle/cparser.rb
index 9a70402953..264ca166dd 100644
--- a/ext/fiddle/lib/fiddle/cparser.rb
+++ b/ext/fiddle/lib/fiddle/cparser.rb
@@ -165,18 +165,30 @@ module Fiddle
raise(RuntimeError, "unsupported type: #{ty}")
end
return TYPE_ULONG_LONG
- when /\A(?:signed\s+)?long(?:\s+int\s+)?(?:\s+\w+)?\z/
- return TYPE_LONG
- when /\Aunsigned\s+long(?:\s+int\s+)?(?:\s+\w+)?\z/
+ when /\Aunsigned\s+long(?:\s+int\s+)?(?:\s+\w+)?\z/,
+ /\Aunsigned\s+int\s+long(?:\s+\w+)?\z/,
+ /\Along(?:\s+int)?\s+unsigned(?:\s+\w+)?\z/,
+ /\Aint\s+unsigned\s+long(?:\s+\w+)?\z/,
+ /\A(?:int\s+)?long\s+unsigned(?:\s+\w+)?\z/
return TYPE_ULONG
+ when /\A(?:signed\s+)?long(?:\s+int\s+)?(?:\s+\w+)?\z/,
+ /\A(?:signed\s+)?int\s+long(?:\s+\w+)?\z/,
+ /\Along(?:\s+int)?\s+signed(?:\s+\w+)?\z/
+ return TYPE_LONG
+ when /\Aunsigned\s+short(?:\s+int\s+)?(?:\s+\w+)?\z/,
+ /\Aunsigned\s+int\s+short(?:\s+\w+)?\z/,
+ /\Ashort(?:\s+int)?\s+unsigned(?:\s+\w+)?\z/,
+ /\Aint\s+unsigned\s+short(?:\s+\w+)?\z/,
+ /\A(?:int\s+)?short\s+unsigned(?:\s+\w+)?\z/
+ return TYPE_USHORT
+ when /\A(?:signed\s+)?short(?:\s+int\s+)?(?:\s+\w+)?\z/,
+ /\A(?:signed\s+)?int\s+short(?:\s+\w+)?\z/,
+ /\Aint\s+(?:signed\s+)?short(?:\s+\w+)?\z/
+ return TYPE_SHORT
when /\A(?:signed\s+)?int(?:\s+\w+)?\z/
return TYPE_INT
when /\A(?:unsigned\s+int|uint)(?:\s+\w+)?\z/
return TYPE_UINT
- when /\A(?:signed\s+)?short(?:\s+int\s+)?(?:\s+\w+)?\z/
- return TYPE_SHORT
- when /\Aunsigned\s+short(?:\s+int\s+)?(?:\s+\w+)?\z/
- return TYPE_USHORT
when /\A(?:signed\s+)?char(?:\s+\w+)?\z/
return TYPE_CHAR
when /\Aunsigned\s+char(?:\s+\w+)?\z/
@@ -235,6 +247,8 @@ module Fiddle
return TYPE_INTPTR_T
when /\Auintptr_t(?:\s+\w+)?\z/
return TYPE_UINTPTR_T
+ when "bool"
+ return TYPE_BOOL
when /\*/, /\[[\s\d]*\]/
return TYPE_VOIDP
when "..."
diff --git a/ext/fiddle/lib/fiddle/import.rb b/ext/fiddle/lib/fiddle/import.rb
index 09ffcef544..050708fb96 100644
--- a/ext/fiddle/lib/fiddle/import.rb
+++ b/ext/fiddle/lib/fiddle/import.rb
@@ -119,6 +119,8 @@ module Fiddle
return SIZEOF_VOIDP
when TYPE_CONST_STRING
return SIZEOF_CONST_STRING
+ when TYPE_BOOL
+ return SIZEOF_BOOL
else
if defined?(TYPE_LONG_LONG) and
ty == TYPE_LONG_LONG
diff --git a/ext/fiddle/lib/fiddle/pack.rb b/ext/fiddle/lib/fiddle/pack.rb
index 545b985d50..81088f402b 100644
--- a/ext/fiddle/lib/fiddle/pack.rb
+++ b/ext/fiddle/lib/fiddle/pack.rb
@@ -15,6 +15,7 @@ module Fiddle
TYPE_USHORT => ALIGN_SHORT,
TYPE_UINT => ALIGN_INT,
TYPE_ULONG => ALIGN_LONG,
+ TYPE_BOOL => ALIGN_BOOL,
}
PACK_MAP = {
@@ -30,6 +31,16 @@ module Fiddle
TYPE_UINT => "I!",
TYPE_ULONG => "L!",
}
+ case SIZEOF_BOOL
+ when SIZEOF_CHAR
+ PACK_MAP[TYPE_BOOL] = PACK_MAP[TYPE_UCHAR]
+ when SIZEOF_SHORT
+ PACK_MAP[TYPE_BOOL] = PACK_MAP[TYPE_USHORT]
+ when SIZEOF_INT
+ PACK_MAP[TYPE_BOOL] = PACK_MAP[TYPE_UINT]
+ when SIZEOF_LONG
+ PACK_MAP[TYPE_BOOL] = PACK_MAP[TYPE_ULONG]
+ end
SIZE_MAP = {
TYPE_VOIDP => SIZEOF_VOIDP,
@@ -43,6 +54,7 @@ module Fiddle
TYPE_USHORT => SIZEOF_SHORT,
TYPE_UINT => SIZEOF_INT,
TYPE_ULONG => SIZEOF_LONG,
+ TYPE_BOOL => SIZEOF_BOOL,
}
if defined?(TYPE_LONG_LONG)
ALIGN_MAP[TYPE_LONG_LONG] = ALIGN_MAP[TYPE_ULONG_LONG] = ALIGN_LONG_LONG
diff --git a/ext/fiddle/lib/fiddle/version.rb b/ext/fiddle/lib/fiddle/version.rb
index 706d98a7b5..6c5109dca8 100644
--- a/ext/fiddle/lib/fiddle/version.rb
+++ b/ext/fiddle/lib/fiddle/version.rb
@@ -1,3 +1,3 @@
module Fiddle
- VERSION = "1.1.2"
+ VERSION = "1.1.3"
end
diff --git a/ext/fiddle/pointer.c b/ext/fiddle/pointer.c
index 15107e3862..1b7d7a69f6 100644
--- a/ext/fiddle/pointer.c
+++ b/ext/fiddle/pointer.c
@@ -88,8 +88,13 @@ fiddle_ptr_memsize(const void *ptr)
}
static const rb_data_type_t fiddle_ptr_data_type = {
- "fiddle/pointer",
- {fiddle_ptr_mark, fiddle_ptr_free, fiddle_ptr_memsize,},
+ .wrap_struct_name = "fiddle/pointer",
+ .function = {
+ .dmark = fiddle_ptr_mark,
+ .dfree = fiddle_ptr_free,
+ .dsize = fiddle_ptr_memsize,
+ },
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
};
#ifdef HAVE_RUBY_MEMORY_VIEW_H
@@ -135,8 +140,8 @@ rb_fiddle_ptr_new2(VALUE klass, void *ptr, long size, freefunc_t func, VALUE wra
data->free = func;
data->freed = false;
data->size = size;
- data->wrap[0] = wrap0;
- data->wrap[1] = wrap1;
+ RB_OBJ_WRITE(val, &data->wrap[0], wrap0);
+ RB_OBJ_WRITE(val, &data->wrap[1], wrap1);
return val;
}
@@ -235,8 +240,8 @@ rb_fiddle_ptr_initialize(int argc, VALUE argv[], VALUE self)
/* Free previous memory. Use of inappropriate initialize may cause SEGV. */
(*(data->free))(data->ptr);
}
- data->wrap[0] = wrap;
- data->wrap[1] = funcwrap;
+ RB_OBJ_WRITE(self, &data->wrap[0], wrap);
+ RB_OBJ_WRITE(self, &data->wrap[1], funcwrap);
data->ptr = p;
data->size = s;
data->free = f;
@@ -314,7 +319,7 @@ rb_fiddle_ptr_s_malloc(int argc, VALUE argv[], VALUE klass)
}
obj = rb_fiddle_ptr_malloc(klass, s,f);
- if (wrap) RPTR_DATA(obj)->wrap[1] = wrap;
+ if (wrap) RB_OBJ_WRITE(obj, &RPTR_DATA(obj)->wrap[1], wrap);
if (rb_block_given_p()) {
if (!f) {
@@ -795,10 +800,37 @@ rb_fiddle_ptr_s_to_ptr(VALUE self, VALUE val)
if (num == val) wrap = 0;
ptr = rb_fiddle_ptr_new(NUM2PTR(num), 0, NULL);
}
- if (wrap) RPTR_DATA(ptr)->wrap[0] = wrap;
+ if (wrap) RB_OBJ_WRITE(ptr, &RPTR_DATA(ptr)->wrap[0], wrap);
return ptr;
}
+/*
+ * call-seq:
+ * Fiddle::Pointer.read(address, len) => string
+ *
+ * Or read the memory at address +address+ with length +len+ and return a
+ * string with that memory
+ */
+
+static VALUE
+rb_fiddle_ptr_read_mem(VALUE klass, VALUE address, VALUE len)
+{
+ return rb_str_new((char *)NUM2PTR(address), NUM2ULONG(len));
+}
+
+/*
+ * call-seq:
+ * Fiddle::Pointer.write(address, str)
+ *
+ * Write bytes in +str+ to the location pointed to by +address+.
+ */
+static VALUE
+rb_fiddle_ptr_write_mem(VALUE klass, VALUE addr, VALUE str)
+{
+ memcpy(NUM2PTR(addr), StringValuePtr(str), RSTRING_LEN(str));
+ return str;
+}
+
void
Init_fiddle_pointer(void)
{
@@ -815,6 +847,8 @@ Init_fiddle_pointer(void)
rb_define_singleton_method(rb_cPointer, "malloc", rb_fiddle_ptr_s_malloc, -1);
rb_define_singleton_method(rb_cPointer, "to_ptr", rb_fiddle_ptr_s_to_ptr, 1);
rb_define_singleton_method(rb_cPointer, "[]", rb_fiddle_ptr_s_to_ptr, 1);
+ rb_define_singleton_method(rb_cPointer, "read", rb_fiddle_ptr_read_mem, 2);
+ rb_define_singleton_method(rb_cPointer, "write", rb_fiddle_ptr_write_mem, 2);
rb_define_method(rb_cPointer, "initialize", rb_fiddle_ptr_initialize, -1);
rb_define_method(rb_cPointer, "free=", rb_fiddle_ptr_free_set, 1);
rb_define_method(rb_cPointer, "free", rb_fiddle_ptr_free_get, 0);
diff --git a/ext/io/console/.document b/ext/io/console/.document
new file mode 100644
index 0000000000..945a377256
--- /dev/null
+++ b/ext/io/console/.document
@@ -0,0 +1,2 @@
+console.c
+lib/size.rb
diff --git a/ext/io/console/console.c b/ext/io/console/console.c
index 6dadc8d0d6..7130c29a8b 100644
--- a/ext/io/console/console.c
+++ b/ext/io/console/console.c
@@ -2,6 +2,10 @@
/*
* console IO module
*/
+
+static const char *const
+IO_CONSOLE_VERSION = "0.7.2";
+
#include "ruby.h"
#include "ruby/io.h"
#include "ruby/thread.h"
@@ -75,10 +79,10 @@ getattr(int fd, conmode *t)
#define SET_LAST_ERROR (0)
#endif
+#define CSI "\x1b\x5b"
+
static ID id_getc, id_console, id_close;
-#if ENABLE_IO_GETPASS
-static ID id_gets, id_chomp_bang;
-#endif
+static ID id_gets, id_flush, id_chomp_bang;
#if defined HAVE_RUBY_FIBER_SCHEDULER_H
# include "ruby/fiber/scheduler.h"
@@ -898,6 +902,16 @@ console_set_winsize(VALUE io, VALUE size)
#endif
#ifdef _WIN32
+/*
+ * call-seq:
+ * io.check_winsize_changed { ... } -> io
+ *
+ * Yields while console input events are queued.
+ *
+ * This method is Windows only.
+ *
+ * You must require 'io/console' to use this method.
+ */
static VALUE
console_check_winsize_changed(VALUE io)
{
@@ -984,6 +998,14 @@ console_ioflush(VALUE io)
return io;
}
+/*
+ * call-seq:
+ * io.beep
+ *
+ * Beeps on the output console.
+ *
+ * You must require 'io/console' to use this method.
+ */
static VALUE
console_beep(VALUE io)
{
@@ -1012,67 +1034,6 @@ mode_in_range(VALUE val, int high, const char *modename)
}
#if defined _WIN32
-static VALUE
-console_goto(VALUE io, VALUE y, VALUE x)
-{
- COORD pos;
- int fd = GetWriteFD(io);
- pos.X = NUM2UINT(x);
- pos.Y = NUM2UINT(y);
- if (!SetConsoleCursorPosition((HANDLE)rb_w32_get_osfhandle(fd), pos)) {
- rb_syserr_fail(LAST_ERROR, 0);
- }
- return io;
-}
-
-static VALUE
-console_cursor_pos(VALUE io)
-{
- rb_console_size_t ws;
- int fd = GetWriteFD(io);
- if (!GetConsoleScreenBufferInfo((HANDLE)rb_w32_get_osfhandle(fd), &ws)) {
- rb_syserr_fail(LAST_ERROR, 0);
- }
- return rb_assoc_new(UINT2NUM(ws.dwCursorPosition.Y), UINT2NUM(ws.dwCursorPosition.X));
-}
-
-static VALUE
-console_move(VALUE io, int y, int x)
-{
- HANDLE h;
- rb_console_size_t ws;
- COORD *pos = &ws.dwCursorPosition;
-
- h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io));
- if (!GetConsoleScreenBufferInfo(h, &ws)) {
- rb_syserr_fail(LAST_ERROR, 0);
- }
- pos->X += x;
- pos->Y += y;
- if (!SetConsoleCursorPosition(h, *pos)) {
- rb_syserr_fail(LAST_ERROR, 0);
- }
- return io;
-}
-
-static VALUE
-console_goto_column(VALUE io, VALUE val)
-{
- HANDLE h;
- rb_console_size_t ws;
- COORD *pos = &ws.dwCursorPosition;
-
- h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io));
- if (!GetConsoleScreenBufferInfo(h, &ws)) {
- rb_syserr_fail(LAST_ERROR, 0);
- }
- pos->X = NUM2INT(val);
- if (!SetConsoleCursorPosition(h, *pos)) {
- rb_syserr_fail(LAST_ERROR, 0);
- }
- return io;
-}
-
static void
constat_clear(HANDLE handle, WORD attr, DWORD len, COORD pos)
{
@@ -1083,74 +1044,6 @@ constat_clear(HANDLE handle, WORD attr, DWORD len, COORD pos)
}
static VALUE
-console_erase_line(VALUE io, VALUE val)
-{
- HANDLE h;
- rb_console_size_t ws;
- COORD *pos = &ws.dwCursorPosition;
- DWORD w;
- int mode = mode_in_range(val, 2, "line erase");
-
- h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io));
- if (!GetConsoleScreenBufferInfo(h, &ws)) {
- rb_syserr_fail(LAST_ERROR, 0);
- }
- w = winsize_col(&ws);
- switch (mode) {
- case 0: /* after cursor */
- w -= pos->X;
- break;
- case 1: /* before *and* cursor */
- w = pos->X + 1;
- pos->X = 0;
- break;
- case 2: /* entire line */
- pos->X = 0;
- break;
- }
- constat_clear(h, ws.wAttributes, w, *pos);
- return io;
-}
-
-static VALUE
-console_erase_screen(VALUE io, VALUE val)
-{
- HANDLE h;
- rb_console_size_t ws;
- COORD *pos = &ws.dwCursorPosition;
- DWORD w;
- int mode = mode_in_range(val, 3, "screen erase");
-
- h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io));
- if (!GetConsoleScreenBufferInfo(h, &ws)) {
- rb_syserr_fail(LAST_ERROR, 0);
- }
- w = winsize_col(&ws);
- switch (mode) {
- case 0: /* erase after cursor */
- w = (w * (ws.srWindow.Bottom - pos->Y + 1) - pos->X);
- break;
- case 1: /* erase before *and* cursor */
- w = (w * (pos->Y - ws.srWindow.Top) + pos->X + 1);
- pos->X = 0;
- pos->Y = ws.srWindow.Top;
- break;
- case 2: /* erase entire screen */
- w = (w * winsize_row(&ws));
- pos->X = 0;
- pos->Y = ws.srWindow.Top;
- break;
- case 3: /* erase entire screen */
- w = (w * ws.dwSize.Y);
- pos->X = 0;
- pos->Y = 0;
- break;
- }
- constat_clear(h, ws.wAttributes, w, *pos);
- return io;
-}
-
-static VALUE
console_scroll(VALUE io, int line)
{
HANDLE h;
@@ -1180,6 +1073,17 @@ console_scroll(VALUE io, int line)
#include "win32_vk.inc"
+/*
+ * call-seq:
+ * io.pressed?(key) -> bool
+ *
+ * Returns +true+ if +key+ is pressed. +key+ may be a virtual key
+ * code or its name (String or Symbol) with out "VK_" prefix.
+ *
+ * This method is Windows only.
+ *
+ * You must require 'io/console' to use this method.
+ */
static VALUE
console_key_pressed_p(VALUE io, VALUE k)
{
@@ -1270,8 +1174,40 @@ console_vt_response(int argc, VALUE *argv, VALUE io, const struct query_args *qa
}
static VALUE
+console_scroll(VALUE io, int line)
+{
+ if (line) {
+ VALUE s = rb_sprintf(CSI "%d%c", line < 0 ? -line : line,
+ line < 0 ? 'T' : 'S');
+ rb_io_write(io, s);
+ }
+ return io;
+}
+
+# define console_key_pressed_p rb_f_notimplement
+#endif
+
+/*
+ * call-seq:
+ * io.cursor -> [row, column]
+ *
+ * Returns the current cursor position as a two-element array of integers (row, column)
+ *
+ * io.cursor # => [3, 5]
+ *
+ * You must require 'io/console' to use this method.
+ */
+static VALUE
console_cursor_pos(VALUE io)
{
+#ifdef _WIN32
+ rb_console_size_t ws;
+ int fd = GetWriteFD(io);
+ if (!GetConsoleScreenBufferInfo((HANDLE)rb_w32_get_osfhandle(fd), &ws)) {
+ rb_syserr_fail(LAST_ERROR, 0);
+ }
+ return rb_assoc_new(UINT2NUM(ws.dwCursorPosition.Y), UINT2NUM(ws.dwCursorPosition.X));
+#else
static const struct query_args query = {"\033[6n", 0};
VALUE resp = console_vt_response(0, 0, io, &query);
VALUE row, column, term;
@@ -1288,64 +1224,205 @@ console_cursor_pos(VALUE io)
RARRAY_ASET(resp, 0, INT2NUM(r));
RARRAY_ASET(resp, 1, INT2NUM(c));
return resp;
+#endif
}
+/*
+ * call-seq:
+ * io.goto(line, column) -> io
+ *
+ * Set the cursor position at +line+ and +column+.
+ *
+ * You must require 'io/console' to use this method.
+ */
static VALUE
console_goto(VALUE io, VALUE y, VALUE x)
{
- rb_io_write(io, rb_sprintf("\x1b[%d;%dH", NUM2UINT(y)+1, NUM2UINT(x)+1));
+#ifdef _WIN32
+ COORD pos;
+ int fd = GetWriteFD(io);
+ pos.X = NUM2UINT(x);
+ pos.Y = NUM2UINT(y);
+ if (!SetConsoleCursorPosition((HANDLE)rb_w32_get_osfhandle(fd), pos)) {
+ rb_syserr_fail(LAST_ERROR, 0);
+ }
+#else
+ rb_io_write(io, rb_sprintf(CSI "%d;%dH", NUM2UINT(y)+1, NUM2UINT(x)+1));
+#endif
return io;
}
static VALUE
console_move(VALUE io, int y, int x)
{
+#ifdef _WIN32
+ HANDLE h;
+ rb_console_size_t ws;
+ COORD *pos = &ws.dwCursorPosition;
+
+ h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io));
+ if (!GetConsoleScreenBufferInfo(h, &ws)) {
+ rb_syserr_fail(LAST_ERROR, 0);
+ }
+ pos->X += x;
+ pos->Y += y;
+ if (!SetConsoleCursorPosition(h, *pos)) {
+ rb_syserr_fail(LAST_ERROR, 0);
+ }
+#else
if (x || y) {
VALUE s = rb_str_new_cstr("");
- if (y) rb_str_catf(s, "\x1b[%d%c", y < 0 ? -y : y, y < 0 ? 'A' : 'B');
- if (x) rb_str_catf(s, "\x1b[%d%c", x < 0 ? -x : x, x < 0 ? 'D' : 'C');
+ if (y) rb_str_catf(s, CSI "%d%c", y < 0 ? -y : y, y < 0 ? 'A' : 'B');
+ if (x) rb_str_catf(s, CSI "%d%c", x < 0 ? -x : x, x < 0 ? 'D' : 'C');
rb_io_write(io, s);
rb_io_flush(io);
}
+#endif
return io;
}
+/*
+ * call-seq:
+ * io.goto_column(column) -> io
+ *
+ * Set the cursor position at +column+ in the same line of the current
+ * position.
+ *
+ * You must require 'io/console' to use this method.
+ */
static VALUE
console_goto_column(VALUE io, VALUE val)
{
- rb_io_write(io, rb_sprintf("\x1b[%dG", NUM2UINT(val)+1));
+#ifdef _WIN32
+ HANDLE h;
+ rb_console_size_t ws;
+ COORD *pos = &ws.dwCursorPosition;
+
+ h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io));
+ if (!GetConsoleScreenBufferInfo(h, &ws)) {
+ rb_syserr_fail(LAST_ERROR, 0);
+ }
+ pos->X = NUM2INT(val);
+ if (!SetConsoleCursorPosition(h, *pos)) {
+ rb_syserr_fail(LAST_ERROR, 0);
+ }
+#else
+ rb_io_write(io, rb_sprintf(CSI "%dG", NUM2UINT(val)+1));
+#endif
return io;
}
+/*
+ * call-seq:
+ * io.erase_line(mode) -> io
+ *
+ * Erases the line at the cursor corresponding to +mode+.
+ * +mode+ may be either:
+ * 0: after cursor
+ * 1: before and cursor
+ * 2: entire line
+ *
+ * You must require 'io/console' to use this method.
+ */
static VALUE
console_erase_line(VALUE io, VALUE val)
{
int mode = mode_in_range(val, 2, "line erase");
- rb_io_write(io, rb_sprintf("\x1b[%dK", mode));
+#ifdef _WIN32
+ HANDLE h;
+ rb_console_size_t ws;
+ COORD *pos = &ws.dwCursorPosition;
+ DWORD w;
+
+ h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io));
+ if (!GetConsoleScreenBufferInfo(h, &ws)) {
+ rb_syserr_fail(LAST_ERROR, 0);
+ }
+ w = winsize_col(&ws);
+ switch (mode) {
+ case 0: /* after cursor */
+ w -= pos->X;
+ break;
+ case 1: /* before *and* cursor */
+ w = pos->X + 1;
+ pos->X = 0;
+ break;
+ case 2: /* entire line */
+ pos->X = 0;
+ break;
+ }
+ constat_clear(h, ws.wAttributes, w, *pos);
+ return io;
+#else
+ rb_io_write(io, rb_sprintf(CSI "%dK", mode));
+#endif
return io;
}
+/*
+ * call-seq:
+ * io.erase_screen(mode) -> io
+ *
+ * Erases the screen at the cursor corresponding to +mode+.
+ * +mode+ may be either:
+ * 0: after cursor
+ * 1: before and cursor
+ * 2: entire screen
+ *
+ * You must require 'io/console' to use this method.
+ */
static VALUE
console_erase_screen(VALUE io, VALUE val)
{
int mode = mode_in_range(val, 3, "screen erase");
- rb_io_write(io, rb_sprintf("\x1b[%dJ", mode));
- return io;
-}
+#ifdef _WIN32
+ HANDLE h;
+ rb_console_size_t ws;
+ COORD *pos = &ws.dwCursorPosition;
+ DWORD w;
-static VALUE
-console_scroll(VALUE io, int line)
-{
- if (line) {
- VALUE s = rb_sprintf("\x1b[%d%c", line < 0 ? -line : line,
- line < 0 ? 'T' : 'S');
- rb_io_write(io, s);
+ h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io));
+ if (!GetConsoleScreenBufferInfo(h, &ws)) {
+ rb_syserr_fail(LAST_ERROR, 0);
}
+ w = winsize_col(&ws);
+ switch (mode) {
+ case 0: /* erase after cursor */
+ w = (w * (ws.srWindow.Bottom - pos->Y + 1) - pos->X);
+ break;
+ case 1: /* erase before *and* cursor */
+ w = (w * (pos->Y - ws.srWindow.Top) + pos->X + 1);
+ pos->X = 0;
+ pos->Y = ws.srWindow.Top;
+ break;
+ case 2: /* erase entire screen */
+ w = (w * winsize_row(&ws));
+ pos->X = 0;
+ pos->Y = ws.srWindow.Top;
+ break;
+ case 3: /* erase entire screen */
+ w = (w * ws.dwSize.Y);
+ pos->X = 0;
+ pos->Y = 0;
+ break;
+ }
+ constat_clear(h, ws.wAttributes, w, *pos);
+#else
+ rb_io_write(io, rb_sprintf(CSI "%dJ", mode));
+#endif
return io;
}
-# define console_key_pressed_p rb_f_notimplement
-#endif
+/*
+ * call-seq:
+ * io.cursor = [line, column] -> io
+ *
+ * Same as <tt>io.goto(line, column)</tt>
+ *
+ * See IO#goto.
+ *
+ * You must require 'io/console' to use this method.
+ */
static VALUE
console_cursor_set(VALUE io, VALUE cpos)
{
@@ -1354,42 +1431,98 @@ console_cursor_set(VALUE io, VALUE cpos)
return console_goto(io, RARRAY_AREF(cpos, 0), RARRAY_AREF(cpos, 1));
}
+/*
+ * call-seq:
+ * io.cursor_up(n) -> io
+ *
+ * Moves the cursor up +n+ lines.
+ *
+ * You must require 'io/console' to use this method.
+ */
static VALUE
console_cursor_up(VALUE io, VALUE val)
{
return console_move(io, -NUM2INT(val), 0);
}
+/*
+ * call-seq:
+ * io.cursor_down(n) -> io
+ *
+ * Moves the cursor down +n+ lines.
+ *
+ * You must require 'io/console' to use this method.
+ */
static VALUE
console_cursor_down(VALUE io, VALUE val)
{
return console_move(io, +NUM2INT(val), 0);
}
+/*
+ * call-seq:
+ * io.cursor_left(n) -> io
+ *
+ * Moves the cursor left +n+ columns.
+ *
+ * You must require 'io/console' to use this method.
+ */
static VALUE
console_cursor_left(VALUE io, VALUE val)
{
return console_move(io, 0, -NUM2INT(val));
}
+/*
+ * call-seq:
+ * io.cursor_right(n) -> io
+ *
+ * Moves the cursor right +n+ columns.
+ *
+ * You must require 'io/console' to use this method.
+ */
static VALUE
console_cursor_right(VALUE io, VALUE val)
{
return console_move(io, 0, +NUM2INT(val));
}
+/*
+ * call-seq:
+ * io.scroll_forward(n) -> io
+ *
+ * Scrolls the entire scrolls forward +n+ lines.
+ *
+ * You must require 'io/console' to use this method.
+ */
static VALUE
console_scroll_forward(VALUE io, VALUE val)
{
return console_scroll(io, +NUM2INT(val));
}
+/*
+ * call-seq:
+ * io.scroll_backward(n) -> io
+ *
+ * Scrolls the entire scrolls backward +n+ lines.
+ *
+ * You must require 'io/console' to use this method.
+ */
static VALUE
console_scroll_backward(VALUE io, VALUE val)
{
return console_scroll(io, -NUM2INT(val));
}
+/*
+ * call-seq:
+ * io.clear_screen -> io
+ *
+ * Clears the entire screen and moves the cursor top-left corner.
+ *
+ * You must require 'io/console' to use this method.
+ */
static VALUE
console_clear_screen(VALUE io)
{
@@ -1534,7 +1667,6 @@ io_getch(int argc, VALUE *argv, VALUE io)
return rb_funcallv(io, id_getc, argc, argv);
}
-#if ENABLE_IO_GETPASS
static VALUE
puts_call(VALUE io)
{
@@ -1542,6 +1674,12 @@ puts_call(VALUE io)
}
static VALUE
+gets_call(VALUE io)
+{
+ return rb_funcallv(io, id_gets, 0, 0);
+}
+
+static VALUE
getpass_call(VALUE io)
{
return ttymode(io, rb_io_gets, io, set_noecho, NULL);
@@ -1561,7 +1699,8 @@ static VALUE
str_chomp(VALUE str)
{
if (!NIL_P(str)) {
- rb_funcallv(str, id_chomp_bang, 0, 0);
+ const VALUE rs = rb_default_rs; /* rvalue in TruffleRuby */
+ rb_funcallv(str, id_chomp_bang, 1, &rs);
}
return str;
}
@@ -1578,6 +1717,12 @@ str_chomp(VALUE str)
* see String#chomp!.
*
* You must require 'io/console' to use this method.
+ *
+ * require 'io/console'
+ * IO::console.getpass("Enter password:")
+ * Enter password:
+ * # => "mypassword"
+ *
*/
static VALUE
console_getpass(int argc, VALUE *argv, VALUE io)
@@ -1588,6 +1733,7 @@ console_getpass(int argc, VALUE *argv, VALUE io)
wio = rb_io_get_write_io(io);
if (wio == io && io == rb_stdin) wio = rb_stderr;
prompt(argc, argv, wio);
+ rb_io_flush(wio);
str = rb_ensure(getpass_call, io, puts_call, wio);
return str_chomp(str);
}
@@ -1605,11 +1751,10 @@ io_getpass(int argc, VALUE *argv, VALUE io)
rb_check_arity(argc, 0, 1);
prompt(argc, argv, io);
- str = str_chomp(rb_funcallv(io, id_gets, 0, 0));
- puts_call(io);
- return str;
+ rb_check_funcall(io, id_flush, 0, 0);
+ str = rb_ensure(gets_call, io, puts_call, io);
+ return str_chomp(str);
}
-#endif
/*
* IO console methods
@@ -1619,10 +1764,9 @@ Init_console(void)
{
#undef rb_intern
id_getc = rb_intern("getc");
-#if ENABLE_IO_GETPASS
id_gets = rb_intern("gets");
+ id_flush = rb_intern("flush");
id_chomp_bang = rb_intern("chomp!");
-#endif
id_console = rb_intern("console");
id_close = rb_intern("close");
#define init_rawmode_opt_id(name) \
@@ -1670,20 +1814,19 @@ InitVM_console(void)
rb_define_method(rb_cIO, "clear_screen", console_clear_screen, 0);
rb_define_method(rb_cIO, "pressed?", console_key_pressed_p, 1);
rb_define_method(rb_cIO, "check_winsize_changed", console_check_winsize_changed, 0);
-#if ENABLE_IO_GETPASS
rb_define_method(rb_cIO, "getpass", console_getpass, -1);
-#endif
rb_define_singleton_method(rb_cIO, "console", console_dev, -1);
{
+ /* :stopdoc: */
VALUE mReadable = rb_define_module_under(rb_cIO, "generic_readable");
+ /* :startdoc: */
rb_define_method(mReadable, "getch", io_getch, -1);
-#if ENABLE_IO_GETPASS
rb_define_method(mReadable, "getpass", io_getpass, -1);
-#endif
}
{
/* :stopdoc: */
cConmode = rb_define_class_under(rb_cIO, "ConsoleMode", rb_cObject);
+ rb_define_const(cConmode, "VERSION", rb_str_new_cstr(IO_CONSOLE_VERSION));
rb_define_alloc_func(cConmode, conmode_alloc);
rb_undef_method(cConmode, "initialize");
rb_define_method(cConmode, "initialize_copy", conmode_init_copy, 1);
diff --git a/ext/io/console/depend b/ext/io/console/depend
index 59ca3442c2..5b91413f38 100644
--- a/ext/io/console/depend
+++ b/ext/io/console/depend
@@ -158,6 +158,7 @@ console.o: $(hdrdir)/ruby/internal/special_consts.h
console.o: $(hdrdir)/ruby/internal/static_assert.h
console.o: $(hdrdir)/ruby/internal/stdalign.h
console.o: $(hdrdir)/ruby/internal/stdbool.h
+console.o: $(hdrdir)/ruby/internal/stdckdint.h
console.o: $(hdrdir)/ruby/internal/symbol.h
console.o: $(hdrdir)/ruby/internal/value.h
console.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/io/console/extconf.rb b/ext/io/console/extconf.rb
index 3cbe8954d4..a72403c7f9 100644
--- a/ext/io/console/extconf.rb
+++ b/ext/io/console/extconf.rb
@@ -35,7 +35,6 @@ when true
elsif have_func("rb_scheduler_timeout") # 3.0
have_func("rb_io_wait")
end
- $defs << "-D""ENABLE_IO_GETPASS=1"
create_makefile("io/console") {|conf|
conf << "\n""VK_HEADER = #{vk_header}\n"
}
diff --git a/ext/io/console/io-console.gemspec b/ext/io/console/io-console.gemspec
index d26a757b01..f9c1729cb7 100644
--- a/ext/io/console/io-console.gemspec
+++ b/ext/io/console/io-console.gemspec
@@ -1,5 +1,13 @@
# -*- ruby -*-
-_VERSION = "0.6.0"
+_VERSION = ["", "ext/io/console/"].find do |dir|
+ begin
+ break File.open(File.join(__dir__, "#{dir}console.c")) {|f|
+ f.gets("\nIO_CONSOLE_VERSION ")
+ f.gets[/"(.+)"/, 1]
+ }
+ rescue Errno::ENOENT
+ end
+end
Gem::Specification.new do |s|
s.name = "io-console"
@@ -10,9 +18,11 @@ Gem::Specification.new do |s|
s.required_ruby_version = ">= 2.6.0"
s.homepage = "https://github.com/ruby/io-console"
s.metadata["source_code_url"] = s.homepage
+ s.metadata["changelog_uri"] = s.homepage + "/releases"
s.authors = ["Nobu Nakada"]
s.require_path = %[lib]
s.files = %w[
+ .document
LICENSE.txt
README.md
ext/io/console/console.c
@@ -25,15 +35,16 @@ Gem::Specification.new do |s|
if Gem::Platform === s.platform and s.platform =~ 'java'
s.files.delete_if {|f| f.start_with?("ext/")}
s.extensions.clear
+ s.require_paths.unshift('lib/ffi')
s.files.concat(%w[
- lib/io/console.rb
- lib/io/console/ffi/bsd_console.rb
- lib/io/console/ffi/common.rb
- lib/io/console/ffi/console.rb
- lib/io/console/ffi/linux_console.rb
- lib/io/console/ffi/native_console.rb
- lib/io/console/ffi/stty_console.rb
- lib/io/console/ffi/stub_console.rb
+ lib/ffi/io/console.rb
+ lib/ffi/io/console/bsd_console.rb
+ lib/ffi/io/console/common.rb
+ lib/ffi/io/console/linux_console.rb
+ lib/ffi/io/console/native_console.rb
+ lib/ffi/io/console/stty_console.rb
+ lib/ffi/io/console/stub_console.rb
+ lib/ffi/io/console/version.rb
])
end
diff --git a/ext/io/nonblock/depend b/ext/io/nonblock/depend
index 48384fca62..20a96f1252 100644
--- a/ext/io/nonblock/depend
+++ b/ext/io/nonblock/depend
@@ -157,6 +157,7 @@ nonblock.o: $(hdrdir)/ruby/internal/special_consts.h
nonblock.o: $(hdrdir)/ruby/internal/static_assert.h
nonblock.o: $(hdrdir)/ruby/internal/stdalign.h
nonblock.o: $(hdrdir)/ruby/internal/stdbool.h
+nonblock.o: $(hdrdir)/ruby/internal/stdckdint.h
nonblock.o: $(hdrdir)/ruby/internal/symbol.h
nonblock.o: $(hdrdir)/ruby/internal/value.h
nonblock.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/io/nonblock/io-nonblock.gemspec b/ext/io/nonblock/io-nonblock.gemspec
index d6df21a84d..6a16c8b03b 100644
--- a/ext/io/nonblock/io-nonblock.gemspec
+++ b/ext/io/nonblock/io-nonblock.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "io-nonblock"
- spec.version = "0.2.0"
+ spec.version = "0.3.0"
spec.authors = ["Nobu Nakada"]
spec.email = ["nobu@ruby-lang.org"]
diff --git a/ext/io/nonblock/nonblock.c b/ext/io/nonblock/nonblock.c
index 4c729a760c..d90538f735 100644
--- a/ext/io/nonblock/nonblock.c
+++ b/ext/io/nonblock/nonblock.c
@@ -66,6 +66,8 @@ set_fcntl_flags(int fd, int f)
rb_sys_fail(0);
}
+#ifndef RUBY_IO_NONBLOCK_METHODS
+
static int
io_nonblock_set(int fd, int f, int nb)
{
@@ -146,6 +148,8 @@ rb_io_nonblock_set(VALUE self, VALUE value)
return self;
}
+#endif /* RUBY_IO_NONBLOCK_METHODS */
+
static VALUE
io_nonblock_restore(VALUE arg)
{
@@ -193,7 +197,10 @@ rb_io_nonblock_block(int argc, VALUE *argv, VALUE self)
void
Init_nonblock(void)
{
+#ifndef RUBY_IO_NONBLOCK_METHODS
rb_define_method(rb_cIO, "nonblock?", rb_io_nonblock_p, 0);
rb_define_method(rb_cIO, "nonblock=", rb_io_nonblock_set, 1);
+#endif
+
rb_define_method(rb_cIO, "nonblock", rb_io_nonblock_block, -1);
}
diff --git a/ext/io/wait/depend b/ext/io/wait/depend
index 83cf8f94c8..70317b1497 100644
--- a/ext/io/wait/depend
+++ b/ext/io/wait/depend
@@ -158,6 +158,7 @@ wait.o: $(hdrdir)/ruby/internal/special_consts.h
wait.o: $(hdrdir)/ruby/internal/static_assert.h
wait.o: $(hdrdir)/ruby/internal/stdalign.h
wait.o: $(hdrdir)/ruby/internal/stdbool.h
+wait.o: $(hdrdir)/ruby/internal/stdckdint.h
wait.o: $(hdrdir)/ruby/internal/symbol.h
wait.o: $(hdrdir)/ruby/internal/value.h
wait.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/io/wait/io-wait.gemspec b/ext/io/wait/io-wait.gemspec
index ebc1f6f5c7..e850e10bf9 100644
--- a/ext/io/wait/io-wait.gemspec
+++ b/ext/io/wait/io-wait.gemspec
@@ -1,4 +1,4 @@
-_VERSION = "0.3.0"
+_VERSION = "0.3.1"
Gem::Specification.new do |spec|
spec.name = "io-wait"
diff --git a/ext/json/VERSION b/ext/json/VERSION
deleted file mode 100644
index ec1cf33c3f..0000000000
--- a/ext/json/VERSION
+++ /dev/null
@@ -1 +0,0 @@
-2.6.3
diff --git a/ext/json/generator/depend b/ext/json/generator/depend
index 42752d1a35..f47e5f3a70 100644
--- a/ext/json/generator/depend
+++ b/ext/json/generator/depend
@@ -161,6 +161,7 @@ generator.o: $(hdrdir)/ruby/internal/special_consts.h
generator.o: $(hdrdir)/ruby/internal/static_assert.h
generator.o: $(hdrdir)/ruby/internal/stdalign.h
generator.o: $(hdrdir)/ruby/internal/stdbool.h
+generator.o: $(hdrdir)/ruby/internal/stdckdint.h
generator.o: $(hdrdir)/ruby/internal/symbol.h
generator.o: $(hdrdir)/ruby/internal/value.h
generator.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c
index 8f7c57e042..6d78284bc4 100644
--- a/ext/json/generator/generator.c
+++ b/ext/json/generator/generator.c
@@ -16,7 +16,7 @@ static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before,
i_object_nl, i_array_nl, i_max_nesting, i_allow_nan, i_ascii_only,
i_pack, i_unpack, i_create_id, i_extend, i_key_p,
i_aref, i_send, i_respond_to_p, i_match, i_keys, i_depth,
- i_buffer_initial_length, i_dup, i_escape_slash;
+ i_buffer_initial_length, i_dup, i_script_safe, i_escape_slash, i_strict;
/*
* Copyright 2001-2004 Unicode, Inc.
@@ -124,7 +124,7 @@ static void unicode_escape_to_buffer(FBuffer *buffer, char buf[6], UTF16
/* Converts string to a JSON string in FBuffer buffer, where all but the ASCII
* and control characters are JSON escaped. */
-static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string, char escape_slash)
+static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string, char script_safe)
{
const UTF8 *source = (UTF8 *) RSTRING_PTR(string);
const UTF8 *sourceEnd = source + RSTRING_LEN(string);
@@ -175,7 +175,7 @@ static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string, char escap
fbuffer_append(buffer, "\\\"", 2);
break;
case '/':
- if(escape_slash) {
+ if(script_safe) {
fbuffer_append(buffer, "\\/", 2);
break;
}
@@ -228,7 +228,7 @@ static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string, char escap
* characters required by the JSON standard are JSON escaped. The remaining
* characters (should be UTF8) are just passed through and appended to the
* result. */
-static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string, char escape_slash)
+static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string, char script_safe)
{
const char *ptr = RSTRING_PTR(string), *p;
unsigned long len = RSTRING_LEN(string), start = 0, end = 0;
@@ -280,7 +280,7 @@ static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string, char escape_slas
escape_len = 2;
break;
case '/':
- if(escape_slash) {
+ if(script_safe) {
escape = "\\/";
escape_len = 2;
break;
@@ -294,6 +294,22 @@ static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string, char escape_slas
rb_raise(rb_path2class("JSON::GeneratorError"),
"partial character in source, but hit end");
}
+
+ if (script_safe && c == 0xE2) {
+ unsigned char c2 = (unsigned char) *(p+1);
+ unsigned char c3 = (unsigned char) *(p+2);
+ if (c2 == 0x80 && (c3 == 0xA8 || c3 == 0xA9)) {
+ fbuffer_append(buffer, ptr + start, end - start);
+ start = end = (end + clen);
+ if (c3 == 0xA8) {
+ fbuffer_append(buffer, "\\u2028", 6);
+ } else {
+ fbuffer_append(buffer, "\\u2029", 6);
+ }
+ continue;
+ }
+ }
+
if (!isLegalUTF8((UTF8 *) p, clen)) {
rb_raise(rb_path2class("JSON::GeneratorError"),
"source sequence is illegal/malformed utf-8");
@@ -727,8 +743,14 @@ static VALUE cState_configure(VALUE self, VALUE opts)
state->allow_nan = RTEST(tmp);
tmp = rb_hash_aref(opts, ID2SYM(i_ascii_only));
state->ascii_only = RTEST(tmp);
- tmp = rb_hash_aref(opts, ID2SYM(i_escape_slash));
- state->escape_slash = RTEST(tmp);
+ tmp = rb_hash_aref(opts, ID2SYM(i_script_safe));
+ state->script_safe = RTEST(tmp);
+ if (!state->script_safe) {
+ tmp = rb_hash_aref(opts, ID2SYM(i_escape_slash));
+ state->script_safe = RTEST(tmp);
+ }
+ tmp = rb_hash_aref(opts, ID2SYM(i_strict));
+ state->strict = RTEST(tmp);
return self;
}
@@ -763,7 +785,8 @@ static VALUE cState_to_h(VALUE self)
rb_hash_aset(result, ID2SYM(i_allow_nan), state->allow_nan ? Qtrue : Qfalse);
rb_hash_aset(result, ID2SYM(i_ascii_only), state->ascii_only ? Qtrue : Qfalse);
rb_hash_aset(result, ID2SYM(i_max_nesting), LONG2FIX(state->max_nesting));
- rb_hash_aset(result, ID2SYM(i_escape_slash), state->escape_slash ? Qtrue : Qfalse);
+ rb_hash_aset(result, ID2SYM(i_script_safe), state->script_safe ? Qtrue : Qfalse);
+ rb_hash_aset(result, ID2SYM(i_strict), state->strict ? Qtrue : Qfalse);
rb_hash_aset(result, ID2SYM(i_depth), LONG2FIX(state->depth));
rb_hash_aset(result, ID2SYM(i_buffer_initial_length), LONG2FIX(state->buffer_initial_length));
return result;
@@ -844,7 +867,7 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
if (klass == rb_cString) {
key_to_s = key;
} else if (klass == rb_cSymbol) {
- key_to_s = rb_id2str(SYM2ID(key));
+ key_to_s = rb_sym2str(key);
} else {
key_to_s = rb_funcall(key, i_to_s, 0);
}
@@ -869,7 +892,6 @@ static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_S
struct hash_foreach_arg arg;
if (max_nesting != 0 && depth > max_nesting) {
- fbuffer_free(buffer);
rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
}
fbuffer_append_char(buffer, '{');
@@ -904,7 +926,6 @@ static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_St
long depth = ++state->depth;
int i, j;
if (max_nesting != 0 && depth > max_nesting) {
- fbuffer_free(buffer);
rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
}
fbuffer_append_char(buffer, '[');
@@ -948,9 +969,9 @@ static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_S
}
#endif
if (state->ascii_only) {
- convert_UTF8_to_JSON_ASCII(buffer, obj, state->escape_slash);
+ convert_UTF8_to_JSON_ASCII(buffer, obj, state->script_safe);
} else {
- convert_UTF8_to_JSON(buffer, obj, state->escape_slash);
+ convert_UTF8_to_JSON(buffer, obj, state->script_safe);
}
fbuffer_append_char(buffer, '"');
}
@@ -997,10 +1018,8 @@ static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_St
VALUE tmp = rb_funcall(obj, i_to_s, 0);
if (!allow_nan) {
if (isinf(value)) {
- fbuffer_free(buffer);
rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(tmp));
} else if (isnan(value)) {
- fbuffer_free(buffer);
rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(tmp));
}
}
@@ -1029,6 +1048,8 @@ static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *s
generate_json_bignum(buffer, Vstate, state, obj);
} else if (klass == rb_cFloat) {
generate_json_float(buffer, Vstate, state, obj);
+ } else if (state->strict) {
+ rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(CLASS_OF(obj)));
} else if (rb_respond_to(obj, i_to_json)) {
tmp = rb_funcall(obj, i_to_json, 1, Vstate);
Check_Type(tmp, T_STRING);
@@ -1071,11 +1092,45 @@ static FBuffer *cState_prepare_buffer(VALUE self)
return buffer;
}
+struct generate_json_data {
+ FBuffer *buffer;
+ VALUE vstate;
+ JSON_Generator_State *state;
+ VALUE obj;
+};
+
+static VALUE generate_json_try(VALUE d)
+{
+ struct generate_json_data *data = (struct generate_json_data *)d;
+
+ generate_json(data->buffer, data->vstate, data->state, data->obj);
+
+ return Qnil;
+}
+
+static VALUE generate_json_rescue(VALUE d, VALUE exc)
+{
+ struct generate_json_data *data = (struct generate_json_data *)d;
+ fbuffer_free(data->buffer);
+
+ rb_exc_raise(exc);
+
+ return Qundef;
+}
+
static VALUE cState_partial_generate(VALUE self, VALUE obj)
{
FBuffer *buffer = cState_prepare_buffer(self);
GET_STATE(self);
- generate_json(buffer, self, state, obj);
+
+ struct generate_json_data data = {
+ .buffer = buffer,
+ .vstate = self,
+ .state = state,
+ .obj = obj
+ };
+ rb_rescue(generate_json_try, (VALUE)&data, generate_json_rescue, (VALUE)&data);
+
return fbuffer_to_s(buffer);
}
@@ -1391,27 +1446,58 @@ static VALUE cState_max_nesting_set(VALUE self, VALUE depth)
}
/*
- * call-seq: escape_slash
+ * call-seq: script_safe
*
* If this boolean is true, the forward slashes will be escaped in
* the json output.
*/
-static VALUE cState_escape_slash(VALUE self)
+static VALUE cState_script_safe(VALUE self)
{
GET_STATE(self);
- return state->escape_slash ? Qtrue : Qfalse;
+ return state->script_safe ? Qtrue : Qfalse;
}
/*
- * call-seq: escape_slash=(depth)
+ * call-seq: script_safe=(enable)
*
* This sets whether or not the forward slashes will be escaped in
* the json output.
*/
-static VALUE cState_escape_slash_set(VALUE self, VALUE enable)
+static VALUE cState_script_safe_set(VALUE self, VALUE enable)
+{
+ GET_STATE(self);
+ state->script_safe = RTEST(enable);
+ return Qnil;
+}
+
+/*
+ * call-seq: strict
+ *
+ * If this boolean is false, types unsupported by the JSON format will
+ * be serialized as strings.
+ * If this boolean is true, types unsupported by the JSON format will
+ * raise a JSON::GeneratorError.
+ */
+static VALUE cState_strict(VALUE self)
+{
+ GET_STATE(self);
+ return state->strict ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq: strict=(enable)
+ *
+ * This sets whether or not to serialize types unsupported by the
+ * JSON format as strings.
+ * If this boolean is false, types unsupported by the JSON format will
+ * be serialized as strings.
+ * If this boolean is true, types unsupported by the JSON format will
+ * raise a JSON::GeneratorError.
+ */
+static VALUE cState_strict_set(VALUE self, VALUE enable)
{
GET_STATE(self);
- state->escape_slash = RTEST(enable);
+ state->strict = RTEST(enable);
return Qnil;
}
@@ -1531,9 +1617,15 @@ void Init_generator(void)
rb_define_method(cState, "array_nl=", cState_array_nl_set, 1);
rb_define_method(cState, "max_nesting", cState_max_nesting, 0);
rb_define_method(cState, "max_nesting=", cState_max_nesting_set, 1);
- rb_define_method(cState, "escape_slash", cState_escape_slash, 0);
- rb_define_method(cState, "escape_slash?", cState_escape_slash, 0);
- rb_define_method(cState, "escape_slash=", cState_escape_slash_set, 1);
+ rb_define_method(cState, "script_safe", cState_script_safe, 0);
+ rb_define_method(cState, "script_safe?", cState_script_safe, 0);
+ rb_define_method(cState, "script_safe=", cState_script_safe_set, 1);
+ rb_define_alias(cState, "escape_slash", "script_safe");
+ rb_define_alias(cState, "escape_slash?", "script_safe?");
+ rb_define_alias(cState, "escape_slash=", "script_safe=");
+ rb_define_method(cState, "strict", cState_strict, 0);
+ rb_define_method(cState, "strict?", cState_strict, 0);
+ rb_define_method(cState, "strict=", cState_strict_set, 1);
rb_define_method(cState, "check_circular?", cState_check_circular_p, 0);
rb_define_method(cState, "allow_nan?", cState_allow_nan_p, 0);
rb_define_method(cState, "ascii_only?", cState_ascii_only_p, 0);
@@ -1590,7 +1682,9 @@ void Init_generator(void)
i_object_nl = rb_intern("object_nl");
i_array_nl = rb_intern("array_nl");
i_max_nesting = rb_intern("max_nesting");
+ i_script_safe = rb_intern("script_safe");
i_escape_slash = rb_intern("escape_slash");
+ i_strict = rb_intern("strict");
i_allow_nan = rb_intern("allow_nan");
i_ascii_only = rb_intern("ascii_only");
i_depth = rb_intern("depth");
diff --git a/ext/json/generator/generator.h b/ext/json/generator/generator.h
index 3ebd622554..1a736b84dd 100644
--- a/ext/json/generator/generator.h
+++ b/ext/json/generator/generator.h
@@ -49,8 +49,8 @@ static const UTF32 halfMask = 0x3FFUL;
static unsigned char isLegalUTF8(const UTF8 *source, unsigned long length);
static void unicode_escape(char *buf, UTF16 character);
static void unicode_escape_to_buffer(FBuffer *buffer, char buf[6], UTF16 character);
-static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string, char escape_slash);
-static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string, char escape_slash);
+static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string, char script_safe);
+static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string, char script_safe);
static char *fstrndup(const char *ptr, unsigned long len);
/* ruby api and some helpers */
@@ -72,7 +72,8 @@ typedef struct JSON_Generator_StateStruct {
long max_nesting;
char allow_nan;
char ascii_only;
- char escape_slash;
+ char script_safe;
+ char strict;
long depth;
long buffer_initial_length;
} JSON_Generator_State;
@@ -151,8 +152,10 @@ static VALUE cState_allow_nan_p(VALUE self);
static VALUE cState_ascii_only_p(VALUE self);
static VALUE cState_depth(VALUE self);
static VALUE cState_depth_set(VALUE self, VALUE depth);
-static VALUE cState_escape_slash(VALUE self);
-static VALUE cState_escape_slash_set(VALUE self, VALUE depth);
+static VALUE cState_script_safe(VALUE self);
+static VALUE cState_script_safe_set(VALUE self, VALUE depth);
+static VALUE cState_strict(VALUE self);
+static VALUE cState_strict_set(VALUE self, VALUE strict);
static FBuffer *cState_prepare_buffer(VALUE self);
#ifndef ZALLOC
#define ZALLOC(type) ((type *)ruby_zalloc(sizeof(type)))
diff --git a/ext/json/json.gemspec b/ext/json/json.gemspec
index f4b2ae791d..64d0c81391 100644
--- a/ext/json/json.gemspec
+++ b/ext/json/json.gemspec
@@ -1,8 +1,10 @@
-# -*- encoding: utf-8 -*-
+version = File.foreach(File.join(__dir__, "lib/json/version.rb")) do |line|
+ /^\s*VERSION\s*=\s*'(.*)'/ =~ line and break $1
+end rescue nil
Gem::Specification.new do |s|
s.name = "json"
- s.version = File.read(File.expand_path('../VERSION', __FILE__)).chomp
+ s.version = version
s.summary = "JSON Implementation for Ruby"
s.description = "This is a JSON implementation as a Ruby extension in C."
@@ -17,7 +19,6 @@ Gem::Specification.new do |s|
"CHANGES.md",
"LICENSE",
"README.md",
- "VERSION",
"ext/json/ext/fbuffer/fbuffer.h",
"ext/json/ext/generator/depend",
"ext/json/ext/generator/extconf.rb",
diff --git a/ext/json/lib/json.rb b/ext/json/lib/json.rb
index 1e64bfcb1a..807488ffef 100644
--- a/ext/json/lib/json.rb
+++ b/ext/json/lib/json.rb
@@ -285,6 +285,15 @@ require 'json/common'
# # Raises JSON::NestingError (nesting of 2 is too deep):
# JSON.generate(obj, max_nesting: 2)
#
+# ====== Escaping Options
+#
+# Options +script_safe+ (boolean) specifies wether <tt>'\u2028'</tt>, <tt>'\u2029'</tt>
+# and <tt>'/'</tt> should be escaped as to make the JSON object safe to interpolate in script
+# tags.
+#
+# Options +ascii_only+ (boolean) specifies wether all characters outside the ASCII range
+# should be escaped.
+#
# ====== Output Options
#
# The default formatting options generate the most compact
diff --git a/ext/json/lib/json/add/bigdecimal.rb b/ext/json/lib/json/add/bigdecimal.rb
index c8b4f567cb..b1d0cfa043 100644
--- a/ext/json/lib/json/add/bigdecimal.rb
+++ b/ext/json/lib/json/add/bigdecimal.rb
@@ -2,19 +2,36 @@
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
require 'json'
end
-defined?(::BigDecimal) or require 'bigdecimal'
+begin
+ require 'bigdecimal'
+rescue LoadError
+end
class BigDecimal
- # Import a JSON Marshalled object.
- #
- # method used for JSON marshalling support.
+
+ # See #as_json.
def self.json_create(object)
BigDecimal._load object['b']
end
- # Marshal the object to JSON.
+ # Methods <tt>BigDecimal#as_json</tt> and +BigDecimal.json_create+ may be used
+ # to serialize and deserialize a \BigDecimal object;
+ # see Marshal[rdoc-ref:Marshal].
+ #
+ # \Method <tt>BigDecimal#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/bigdecimal'
+ # x = BigDecimal(2).as_json # => {"json_class"=>"BigDecimal", "b"=>"27:0.2e1"}
+ # y = BigDecimal(2.0, 4).as_json # => {"json_class"=>"BigDecimal", "b"=>"36:0.2e1"}
+ # z = BigDecimal(Complex(2, 0)).as_json # => {"json_class"=>"BigDecimal", "b"=>"27:0.2e1"}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \BigDecimal object:
+ #
+ # BigDecimal.json_create(x) # => 0.2e1
+ # BigDecimal.json_create(y) # => 0.2e1
+ # BigDecimal.json_create(z) # => 0.2e1
#
- # method used for JSON marshalling support.
def as_json(*)
{
JSON.create_id => self.class.name,
@@ -22,8 +39,20 @@ class BigDecimal
}
end
- # return the JSON value
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/bigdecimal'
+ # puts BigDecimal(2).to_json
+ # puts BigDecimal(2.0, 4).to_json
+ # puts BigDecimal(Complex(2, 0)).to_json
+ #
+ # Output:
+ #
+ # {"json_class":"BigDecimal","b":"27:0.2e1"}
+ # {"json_class":"BigDecimal","b":"36:0.2e1"}
+ # {"json_class":"BigDecimal","b":"27:0.2e1"}
+ #
def to_json(*args)
as_json.to_json(*args)
end
-end
+end if defined?(::BigDecimal)
diff --git a/ext/json/lib/json/add/complex.rb b/ext/json/lib/json/add/complex.rb
index e63e29fd22..ef48c554c6 100644
--- a/ext/json/lib/json/add/complex.rb
+++ b/ext/json/lib/json/add/complex.rb
@@ -5,14 +5,27 @@ end
class Complex
- # Deserializes JSON string by converting Real value <tt>r</tt>, imaginary
- # value <tt>i</tt>, to a Complex object.
+ # See #as_json.
def self.json_create(object)
Complex(object['r'], object['i'])
end
- # Returns a hash, that will be turned into a JSON object and represent this
- # object.
+ # Methods <tt>Complex#as_json</tt> and +Complex.json_create+ may be used
+ # to serialize and deserialize a \Complex object;
+ # see Marshal[rdoc-ref:Marshal].
+ #
+ # \Method <tt>Complex#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/complex'
+ # x = Complex(2).as_json # => {"json_class"=>"Complex", "r"=>2, "i"=>0}
+ # y = Complex(2.0, 4).as_json # => {"json_class"=>"Complex", "r"=>2.0, "i"=>4}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \Complex object:
+ #
+ # Complex.json_create(x) # => (2+0i)
+ # Complex.json_create(y) # => (2.0+4i)
+ #
def as_json(*)
{
JSON.create_id => self.class.name,
@@ -21,7 +34,17 @@ class Complex
}
end
- # Stores class name (Complex) along with real value <tt>r</tt> and imaginary value <tt>i</tt> as JSON string
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/complex'
+ # puts Complex(2).to_json
+ # puts Complex(2.0, 4).to_json
+ #
+ # Output:
+ #
+ # {"json_class":"Complex","r":2,"i":0}
+ # {"json_class":"Complex","r":2.0,"i":4}
+ #
def to_json(*args)
as_json.to_json(*args)
end
diff --git a/ext/json/lib/json/add/date.rb b/ext/json/lib/json/add/date.rb
index 25523561a5..19688f751b 100644
--- a/ext/json/lib/json/add/date.rb
+++ b/ext/json/lib/json/add/date.rb
@@ -6,16 +6,29 @@ require 'date'
class Date
- # Deserializes JSON string by converting Julian year <tt>y</tt>, month
- # <tt>m</tt>, day <tt>d</tt> and Day of Calendar Reform <tt>sg</tt> to Date.
+ # See #as_json.
def self.json_create(object)
civil(*object.values_at('y', 'm', 'd', 'sg'))
end
alias start sg unless method_defined?(:start)
- # Returns a hash, that will be turned into a JSON object and represent this
- # object.
+ # Methods <tt>Date#as_json</tt> and +Date.json_create+ may be used
+ # to serialize and deserialize a \Date object;
+ # see Marshal[rdoc-ref:Marshal].
+ #
+ # \Method <tt>Date#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/date'
+ # x = Date.today.as_json
+ # # => {"json_class"=>"Date", "y"=>2023, "m"=>11, "d"=>21, "sg"=>2299161.0}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \Date object:
+ #
+ # Date.json_create(x)
+ # # => #<Date: 2023-11-21 ((2460270j,0s,0n),+0s,2299161j)>
+ #
def as_json(*)
{
JSON.create_id => self.class.name,
@@ -26,8 +39,15 @@ class Date
}
end
- # Stores class name (Date) with Julian year <tt>y</tt>, month <tt>m</tt>, day
- # <tt>d</tt> and Day of Calendar Reform <tt>sg</tt> as JSON string
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/date'
+ # puts Date.today.to_json
+ #
+ # Output:
+ #
+ # {"json_class":"Date","y":2023,"m":11,"d":21,"sg":2299161.0}
+ #
def to_json(*args)
as_json.to_json(*args)
end
diff --git a/ext/json/lib/json/add/date_time.rb b/ext/json/lib/json/add/date_time.rb
index 38b0e86ab8..d7d42591cf 100644
--- a/ext/json/lib/json/add/date_time.rb
+++ b/ext/json/lib/json/add/date_time.rb
@@ -6,9 +6,7 @@ require 'date'
class DateTime
- # Deserializes JSON string by converting year <tt>y</tt>, month <tt>m</tt>,
- # day <tt>d</tt>, hour <tt>H</tt>, minute <tt>M</tt>, second <tt>S</tt>,
- # offset <tt>of</tt> and Day of Calendar Reform <tt>sg</tt> to DateTime.
+ # See #as_json.
def self.json_create(object)
args = object.values_at('y', 'm', 'd', 'H', 'M', 'S')
of_a, of_b = object['of'].split('/')
@@ -23,8 +21,21 @@ class DateTime
alias start sg unless method_defined?(:start)
- # Returns a hash, that will be turned into a JSON object and represent this
- # object.
+ # Methods <tt>DateTime#as_json</tt> and +DateTime.json_create+ may be used
+ # to serialize and deserialize a \DateTime object;
+ # see Marshal[rdoc-ref:Marshal].
+ #
+ # \Method <tt>DateTime#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/datetime'
+ # x = DateTime.now.as_json
+ # # => {"json_class"=>"DateTime", "y"=>2023, "m"=>11, "d"=>21, "sg"=>2299161.0}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \DateTime object:
+ #
+ # DateTime.json_create(x) # BUG? Raises Date::Error "invalid date"
+ #
def as_json(*)
{
JSON.create_id => self.class.name,
@@ -39,9 +50,15 @@ class DateTime
}
end
- # Stores class name (DateTime) with Julian year <tt>y</tt>, month <tt>m</tt>,
- # day <tt>d</tt>, hour <tt>H</tt>, minute <tt>M</tt>, second <tt>S</tt>,
- # offset <tt>of</tt> and Day of Calendar Reform <tt>sg</tt> as JSON string
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/datetime'
+ # puts DateTime.now.to_json
+ #
+ # Output:
+ #
+ # {"json_class":"DateTime","y":2023,"m":11,"d":21,"sg":2299161.0}
+ #
def to_json(*args)
as_json.to_json(*args)
end
diff --git a/ext/json/lib/json/add/exception.rb b/ext/json/lib/json/add/exception.rb
index a107e5b3c4..71d8deb0ea 100644
--- a/ext/json/lib/json/add/exception.rb
+++ b/ext/json/lib/json/add/exception.rb
@@ -5,16 +5,27 @@ end
class Exception
- # Deserializes JSON string by constructing new Exception object with message
- # <tt>m</tt> and backtrace <tt>b</tt> serialized with <tt>to_json</tt>
+ # See #as_json.
def self.json_create(object)
result = new(object['m'])
result.set_backtrace object['b']
result
end
- # Returns a hash, that will be turned into a JSON object and represent this
- # object.
+ # Methods <tt>Exception#as_json</tt> and +Exception.json_create+ may be used
+ # to serialize and deserialize a \Exception object;
+ # see Marshal[rdoc-ref:Marshal].
+ #
+ # \Method <tt>Exception#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/exception'
+ # x = Exception.new('Foo').as_json # => {"json_class"=>"Exception", "m"=>"Foo", "b"=>nil}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \Exception object:
+ #
+ # Exception.json_create(x) # => #<Exception: Foo>
+ #
def as_json(*)
{
JSON.create_id => self.class.name,
@@ -23,8 +34,15 @@ class Exception
}
end
- # Stores class name (Exception) with message <tt>m</tt> and backtrace array
- # <tt>b</tt> as JSON string
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/exception'
+ # puts Exception.new('Foo').to_json
+ #
+ # Output:
+ #
+ # {"json_class":"Exception","m":"Foo","b":null}
+ #
def to_json(*args)
as_json.to_json(*args)
end
diff --git a/ext/json/lib/json/add/ostruct.rb b/ext/json/lib/json/add/ostruct.rb
index 686cf0025d..1e6f408248 100644
--- a/ext/json/lib/json/add/ostruct.rb
+++ b/ext/json/lib/json/add/ostruct.rb
@@ -2,18 +2,34 @@
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
require 'json'
end
-require 'ostruct'
+begin
+ require 'ostruct'
+rescue LoadError
+end
class OpenStruct
- # Deserializes JSON string by constructing new Struct object with values
- # <tt>t</tt> serialized by <tt>to_json</tt>.
+ # See #as_json.
def self.json_create(object)
new(object['t'] || object[:t])
end
- # Returns a hash, that will be turned into a JSON object and represent this
- # object.
+ # Methods <tt>OpenStruct#as_json</tt> and +OpenStruct.json_create+ may be used
+ # to serialize and deserialize a \OpenStruct object;
+ # see Marshal[rdoc-ref:Marshal].
+ #
+ # \Method <tt>OpenStruct#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/ostruct'
+ # x = OpenStruct.new('name' => 'Rowdy', :age => nil).as_json
+ # # => {"json_class"=>"OpenStruct", "t"=>{:name=>'Rowdy', :age=>nil}}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \OpenStruct object:
+ #
+ # OpenStruct.json_create(x)
+ # # => #<OpenStruct name='Rowdy', age=nil>
+ #
def as_json(*)
klass = self.class.name
klass.to_s.empty? and raise JSON::JSONError, "Only named structs are supported!"
@@ -23,9 +39,16 @@ class OpenStruct
}
end
- # Stores class name (OpenStruct) with this struct's values <tt>t</tt> as a
- # JSON string.
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/ostruct'
+ # puts OpenStruct.new('name' => 'Rowdy', :age => nil).to_json
+ #
+ # Output:
+ #
+ # {"json_class":"OpenStruct","t":{'name':'Rowdy',"age":null}}
+ #
def to_json(*args)
as_json.to_json(*args)
end
-end
+end if defined?(::OpenStruct)
diff --git a/ext/json/lib/json/add/range.rb b/ext/json/lib/json/add/range.rb
index 93529fb1c4..53f54ac372 100644
--- a/ext/json/lib/json/add/range.rb
+++ b/ext/json/lib/json/add/range.rb
@@ -5,14 +5,29 @@ end
class Range
- # Deserializes JSON string by constructing new Range object with arguments
- # <tt>a</tt> serialized by <tt>to_json</tt>.
+ # See #as_json.
def self.json_create(object)
new(*object['a'])
end
- # Returns a hash, that will be turned into a JSON object and represent this
- # object.
+ # Methods <tt>Range#as_json</tt> and +Range.json_create+ may be used
+ # to serialize and deserialize a \Range object;
+ # see Marshal[rdoc-ref:Marshal].
+ #
+ # \Method <tt>Range#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/range'
+ # x = (1..4).as_json # => {"json_class"=>"Range", "a"=>[1, 4, false]}
+ # y = (1...4).as_json # => {"json_class"=>"Range", "a"=>[1, 4, true]}
+ # z = ('a'..'d').as_json # => {"json_class"=>"Range", "a"=>["a", "d", false]}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \Range object:
+ #
+ # Range.json_create(x) # => 1..4
+ # Range.json_create(y) # => 1...4
+ # Range.json_create(z) # => "a".."d"
+ #
def as_json(*)
{
JSON.create_id => self.class.name,
@@ -20,9 +35,19 @@ class Range
}
end
- # Stores class name (Range) with JSON array of arguments <tt>a</tt> which
- # include <tt>first</tt> (integer), <tt>last</tt> (integer), and
- # <tt>exclude_end?</tt> (boolean) as JSON string.
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/range'
+ # puts (1..4).to_json
+ # puts (1...4).to_json
+ # puts ('a'..'d').to_json
+ #
+ # Output:
+ #
+ # {"json_class":"Range","a":[1,4,false]}
+ # {"json_class":"Range","a":[1,4,true]}
+ # {"json_class":"Range","a":["a","d",false]}
+ #
def to_json(*args)
as_json.to_json(*args)
end
diff --git a/ext/json/lib/json/add/rational.rb b/ext/json/lib/json/add/rational.rb
index f776226046..8c39a7db55 100644
--- a/ext/json/lib/json/add/rational.rb
+++ b/ext/json/lib/json/add/rational.rb
@@ -4,14 +4,28 @@ unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
end
class Rational
- # Deserializes JSON string by converting numerator value <tt>n</tt>,
- # denominator value <tt>d</tt>, to a Rational object.
+
+ # See #as_json.
def self.json_create(object)
Rational(object['n'], object['d'])
end
- # Returns a hash, that will be turned into a JSON object and represent this
- # object.
+ # Methods <tt>Rational#as_json</tt> and +Rational.json_create+ may be used
+ # to serialize and deserialize a \Rational object;
+ # see Marshal[rdoc-ref:Marshal].
+ #
+ # \Method <tt>Rational#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/rational'
+ # x = Rational(2, 3).as_json
+ # # => {"json_class"=>"Rational", "n"=>2, "d"=>3}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \Rational object:
+ #
+ # Rational.json_create(x)
+ # # => (2/3)
+ #
def as_json(*)
{
JSON.create_id => self.class.name,
@@ -20,7 +34,15 @@ class Rational
}
end
- # Stores class name (Rational) along with numerator value <tt>n</tt> and denominator value <tt>d</tt> as JSON string
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/rational'
+ # puts Rational(2, 3).to_json
+ #
+ # Output:
+ #
+ # {"json_class":"Rational","n":2,"d":3}
+ #
def to_json(*args)
as_json.to_json(*args)
end
diff --git a/ext/json/lib/json/add/regexp.rb b/ext/json/lib/json/add/regexp.rb
index 39d69fede7..b63f49608f 100644
--- a/ext/json/lib/json/add/regexp.rb
+++ b/ext/json/lib/json/add/regexp.rb
@@ -5,15 +5,26 @@ end
class Regexp
- # Deserializes JSON string by constructing new Regexp object with source
- # <tt>s</tt> (Regexp or String) and options <tt>o</tt> serialized by
- # <tt>to_json</tt>
+ # See #as_json.
def self.json_create(object)
new(object['s'], object['o'])
end
- # Returns a hash, that will be turned into a JSON object and represent this
- # object.
+ # Methods <tt>Regexp#as_json</tt> and +Regexp.json_create+ may be used
+ # to serialize and deserialize a \Regexp object;
+ # see Marshal[rdoc-ref:Marshal].
+ #
+ # \Method <tt>Regexp#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/regexp'
+ # x = /foo/.as_json
+ # # => {"json_class"=>"Regexp", "o"=>0, "s"=>"foo"}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \Regexp object:
+ #
+ # Regexp.json_create(x) # => /foo/
+ #
def as_json(*)
{
JSON.create_id => self.class.name,
@@ -22,8 +33,15 @@ class Regexp
}
end
- # Stores class name (Regexp) with options <tt>o</tt> and source <tt>s</tt>
- # (Regexp or String) as JSON string
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/regexp'
+ # puts /foo/.to_json
+ #
+ # Output:
+ #
+ # {"json_class":"Regexp","o":0,"s":"foo"}
+ #
def to_json(*args)
as_json.to_json(*args)
end
diff --git a/ext/json/lib/json/add/set.rb b/ext/json/lib/json/add/set.rb
index 71e2a0ac8b..1918353187 100644
--- a/ext/json/lib/json/add/set.rb
+++ b/ext/json/lib/json/add/set.rb
@@ -4,16 +4,27 @@ end
defined?(::Set) or require 'set'
class Set
- # Import a JSON Marshalled object.
- #
- # method used for JSON marshalling support.
+
+ # See #as_json.
def self.json_create(object)
new object['a']
end
- # Marshal the object to JSON.
+ # Methods <tt>Set#as_json</tt> and +Set.json_create+ may be used
+ # to serialize and deserialize a \Set object;
+ # see Marshal[rdoc-ref:Marshal].
+ #
+ # \Method <tt>Set#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/set'
+ # x = Set.new(%w/foo bar baz/).as_json
+ # # => {"json_class"=>"Set", "a"=>["foo", "bar", "baz"]}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \Set object:
+ #
+ # Set.json_create(x) # => #<Set: {"foo", "bar", "baz"}>
#
- # method used for JSON marshalling support.
def as_json(*)
{
JSON.create_id => self.class.name,
@@ -21,7 +32,15 @@ class Set
}
end
- # return the JSON value
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/set'
+ # puts Set.new(%w/foo bar baz/).to_json
+ #
+ # Output:
+ #
+ # {"json_class":"Set","a":["foo","bar","baz"]}
+ #
def to_json(*args)
as_json.to_json(*args)
end
diff --git a/ext/json/lib/json/add/struct.rb b/ext/json/lib/json/add/struct.rb
index e8395ed42f..86847762ac 100644
--- a/ext/json/lib/json/add/struct.rb
+++ b/ext/json/lib/json/add/struct.rb
@@ -5,14 +5,28 @@ end
class Struct
- # Deserializes JSON string by constructing new Struct object with values
- # <tt>v</tt> serialized by <tt>to_json</tt>.
+ # See #as_json.
def self.json_create(object)
new(*object['v'])
end
- # Returns a hash, that will be turned into a JSON object and represent this
- # object.
+ # Methods <tt>Struct#as_json</tt> and +Struct.json_create+ may be used
+ # to serialize and deserialize a \Struct object;
+ # see Marshal[rdoc-ref:Marshal].
+ #
+ # \Method <tt>Struct#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/struct'
+ # Customer = Struct.new('Customer', :name, :address, :zip)
+ # x = Struct::Customer.new.as_json
+ # # => {"json_class"=>"Struct::Customer", "v"=>[nil, nil, nil]}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \Struct object:
+ #
+ # Struct::Customer.json_create(x)
+ # # => #<struct Struct::Customer name=nil, address=nil, zip=nil>
+ #
def as_json(*)
klass = self.class.name
klass.to_s.empty? and raise JSON::JSONError, "Only named structs are supported!"
@@ -22,8 +36,16 @@ class Struct
}
end
- # Stores class name (Struct) with Struct values <tt>v</tt> as a JSON string.
- # Only named structs are supported.
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/struct'
+ # Customer = Struct.new('Customer', :name, :address, :zip)
+ # puts Struct::Customer.new.to_json
+ #
+ # Output:
+ #
+ # {"json_class":"Struct","t":{'name':'Rowdy',"age":null}}
+ #
def to_json(*args)
as_json.to_json(*args)
end
diff --git a/ext/json/lib/json/add/symbol.rb b/ext/json/lib/json/add/symbol.rb
index 74b13a423f..b5f3623158 100644
--- a/ext/json/lib/json/add/symbol.rb
+++ b/ext/json/lib/json/add/symbol.rb
@@ -1,11 +1,26 @@
+
#frozen_string_literal: false
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
require 'json'
end
class Symbol
- # Returns a hash, that will be turned into a JSON object and represent this
- # object.
+
+ # Methods <tt>Symbol#as_json</tt> and +Symbol.json_create+ may be used
+ # to serialize and deserialize a \Symbol object;
+ # see Marshal[rdoc-ref:Marshal].
+ #
+ # \Method <tt>Symbol#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/symbol'
+ # x = :foo.as_json
+ # # => {"json_class"=>"Symbol", "s"=>"foo"}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \Symbol object:
+ #
+ # Symbol.json_create(x) # => :foo
+ #
def as_json(*)
{
JSON.create_id => self.class.name,
@@ -13,12 +28,20 @@ class Symbol
}
end
- # Stores class name (Symbol) with String representation of Symbol as a JSON string.
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/symbol'
+ # puts :foo.to_json
+ #
+ # Output:
+ #
+ # # {"json_class":"Symbol","s":"foo"}
+ #
def to_json(*a)
as_json.to_json(*a)
end
- # Deserializes JSON string by converting the <tt>string</tt> value stored in the object to a Symbol
+ # See #as_json.
def self.json_create(o)
o['s'].to_sym
end
diff --git a/ext/json/lib/json/add/time.rb b/ext/json/lib/json/add/time.rb
index b73acc4086..599ed9e24b 100644
--- a/ext/json/lib/json/add/time.rb
+++ b/ext/json/lib/json/add/time.rb
@@ -5,7 +5,7 @@ end
class Time
- # Deserializes JSON string by converting time since epoch to Time
+ # See #as_json.
def self.json_create(object)
if usec = object.delete('u') # used to be tv_usec -> tv_nsec
object['n'] = usec * 1000
@@ -17,8 +17,22 @@ class Time
end
end
- # Returns a hash, that will be turned into a JSON object and represent this
- # object.
+ # Methods <tt>Time#as_json</tt> and +Time.json_create+ may be used
+ # to serialize and deserialize a \Time object;
+ # see Marshal[rdoc-ref:Marshal].
+ #
+ # \Method <tt>Time#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/time'
+ # x = Time.now.as_json
+ # # => {"json_class"=>"Time", "s"=>1700931656, "n"=>472846644}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \Time object:
+ #
+ # Time.json_create(x)
+ # # => 2023-11-25 11:00:56.472846644 -0600
+ #
def as_json(*)
nanoseconds = [ tv_usec * 1000 ]
respond_to?(:tv_nsec) and nanoseconds << tv_nsec
@@ -30,8 +44,15 @@ class Time
}
end
- # Stores class name (Time) with number of seconds since epoch and number of
- # microseconds for Time as JSON string
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/time'
+ # puts Time.now.to_json
+ #
+ # Output:
+ #
+ # {"json_class":"Time","s":1700931678,"n":980650786}
+ #
def to_json(*args)
as_json.to_json(*args)
end
diff --git a/ext/json/lib/json/common.rb b/ext/json/lib/json/common.rb
index ea46896fcc..95098d3bb4 100644
--- a/ext/json/lib/json/common.rb
+++ b/ext/json/lib/json/common.rb
@@ -1,8 +1,12 @@
#frozen_string_literal: false
require 'json/version'
-require 'json/generic_object'
module JSON
+ autoload :GenericObject, 'json/generic_object'
+
+ NOT_SET = Object.new.freeze
+ private_constant :NOT_SET
+
class << self
# :call-seq:
# JSON[object] -> new_array or new_string
@@ -295,19 +299,9 @@ module JSON
#
def generate(obj, opts = nil)
if State === opts
- state, opts = opts, nil
+ state = opts
else
- state = State.new
- end
- if opts
- if opts.respond_to? :to_hash
- opts = opts.to_hash
- elsif opts.respond_to? :to_h
- opts = opts.to_h
- else
- raise TypeError, "can't convert #{opts.class} into Hash"
- end
- state = state.configure(opts)
+ state = State.new(opts)
end
state.generate(obj)
end
@@ -334,19 +328,9 @@ module JSON
# JSON.fast_generate(a)
def fast_generate(obj, opts = nil)
if State === opts
- state, opts = opts, nil
+ state = opts
else
- state = JSON.create_fast_state
- end
- if opts
- if opts.respond_to? :to_hash
- opts = opts.to_hash
- elsif opts.respond_to? :to_h
- opts = opts.to_h
- else
- raise TypeError, "can't convert #{opts.class} into Hash"
- end
- state.configure(opts)
+ state = JSON.create_fast_state.configure(opts)
end
state.generate(obj)
end
@@ -592,13 +576,13 @@ module JSON
# Sets or returns the default options for the JSON.dump method.
# Initially:
# opts = JSON.dump_default_options
- # opts # => {:max_nesting=>false, :allow_nan=>true, :escape_slash=>false}
+ # opts # => {:max_nesting=>false, :allow_nan=>true, :script_safe=>false}
attr_accessor :dump_default_options
end
self.dump_default_options = {
:max_nesting => false,
:allow_nan => true,
- :escape_slash => false,
+ :script_safe => false,
}
# :call-seq:
@@ -628,16 +612,18 @@ module JSON
# puts File.read(path)
# Output:
# {"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}
- def dump(obj, anIO = nil, limit = nil)
- if anIO and limit.nil?
- anIO = anIO.to_io if anIO.respond_to?(:to_io)
- unless anIO.respond_to?(:write)
- limit = anIO
- anIO = nil
- end
+ def dump(obj, anIO = nil, limit = nil, kwargs = nil)
+ io_limit_opt = [anIO, limit, kwargs].compact
+ kwargs = io_limit_opt.pop if io_limit_opt.last.is_a?(Hash)
+ anIO, limit = io_limit_opt
+ if anIO.respond_to?(:to_io)
+ anIO = anIO.to_io
+ elsif limit.nil? && !anIO.respond_to?(:write)
+ anIO, limit = nil, anIO
end
opts = JSON.dump_default_options
opts = opts.merge(:max_nesting => limit) if limit
+ opts = merge_dump_options(opts, **kwargs) if kwargs
result = generate(obj, opts)
if anIO
anIO.write result
@@ -653,6 +639,15 @@ module JSON
def self.iconv(to, from, string)
string.encode(to, from)
end
+
+ def merge_dump_options(opts, strict: NOT_SET)
+ opts = opts.merge(strict: strict) if NOT_SET != strict
+ opts
+ end
+
+ class << self
+ private :merge_dump_options
+ end
end
module ::Kernel
diff --git a/ext/json/lib/json/generic_object.rb b/ext/json/lib/json/generic_object.rb
index 108309db26..56efda6495 100644
--- a/ext/json/lib/json/generic_object.rb
+++ b/ext/json/lib/json/generic_object.rb
@@ -1,5 +1,9 @@
#frozen_string_literal: false
-require 'ostruct'
+begin
+ require 'ostruct'
+rescue LoadError
+ warn "JSON::GenericObject requires 'ostruct'. Please install it with `gem install ostruct`."
+end
module JSON
class GenericObject < OpenStruct
@@ -67,5 +71,5 @@ module JSON
def to_json(*a)
as_json.to_json(*a)
end
- end
+ end if defined?(::OpenStruct)
end
diff --git a/ext/json/lib/json/version.rb b/ext/json/lib/json/version.rb
index 3d4326d836..836f47edf4 100644
--- a/ext/json/lib/json/version.rb
+++ b/ext/json/lib/json/version.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
module JSON
# JSON version
- VERSION = '2.6.3'
+ VERSION = '2.7.2'
VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
diff --git a/ext/json/parser/depend b/ext/json/parser/depend
index cb6e547d29..f3422b4f84 100644
--- a/ext/json/parser/depend
+++ b/ext/json/parser/depend
@@ -160,6 +160,7 @@ parser.o: $(hdrdir)/ruby/internal/special_consts.h
parser.o: $(hdrdir)/ruby/internal/static_assert.h
parser.o: $(hdrdir)/ruby/internal/stdalign.h
parser.o: $(hdrdir)/ruby/internal/stdbool.h
+parser.o: $(hdrdir)/ruby/internal/stdckdint.h
parser.o: $(hdrdir)/ruby/internal/symbol.h
parser.o: $(hdrdir)/ruby/internal/value.h
parser.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c
index 9bd7f1971e..57f87432b6 100644
--- a/ext/json/parser/parser.c
+++ b/ext/json/parser/parser.c
@@ -9,14 +9,14 @@
static void
enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...)
{
- va_list args;
- VALUE mesg;
+ va_list args;
+ VALUE mesg;
- va_start(args, fmt);
- mesg = rb_enc_vsprintf(enc, fmt, args);
- va_end(args);
+ va_start(args, fmt);
+ mesg = rb_enc_vsprintf(enc, fmt, args);
+ va_end(args);
- rb_exc_raise(rb_exc_new3(exc, mesg));
+ rb_exc_raise(rb_exc_new3(exc, mesg));
}
# define rb_enc_raise enc_raise
# endif
@@ -28,3320 +28,2184 @@ enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...)
/* unicode */
static const signed char digit_values[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, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1,
- -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1,
+ -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1
};
static UTF32 unescape_unicode(const unsigned char *p)
{
- signed char b;
- UTF32 result = 0;
- b = digit_values[p[0]];
- if (b < 0) return UNI_REPLACEMENT_CHAR;
- result = (result << 4) | (unsigned char)b;
- b = digit_values[p[1]];
- if (b < 0) return UNI_REPLACEMENT_CHAR;
- result = (result << 4) | (unsigned char)b;
- b = digit_values[p[2]];
- if (b < 0) return UNI_REPLACEMENT_CHAR;
- result = (result << 4) | (unsigned char)b;
- b = digit_values[p[3]];
- if (b < 0) return UNI_REPLACEMENT_CHAR;
- result = (result << 4) | (unsigned char)b;
- return result;
+ signed char b;
+ UTF32 result = 0;
+ b = digit_values[p[0]];
+ if (b < 0) return UNI_REPLACEMENT_CHAR;
+ result = (result << 4) | (unsigned char)b;
+ b = digit_values[p[1]];
+ if (b < 0) return UNI_REPLACEMENT_CHAR;
+ result = (result << 4) | (unsigned char)b;
+ b = digit_values[p[2]];
+ if (b < 0) return UNI_REPLACEMENT_CHAR;
+ result = (result << 4) | (unsigned char)b;
+ b = digit_values[p[3]];
+ if (b < 0) return UNI_REPLACEMENT_CHAR;
+ result = (result << 4) | (unsigned char)b;
+ return result;
}
static int convert_UTF32_to_UTF8(char *buf, UTF32 ch)
{
- int len = 1;
- if (ch <= 0x7F) {
- buf[0] = (char) ch;
- } else if (ch <= 0x07FF) {
- buf[0] = (char) ((ch >> 6) | 0xC0);
- buf[1] = (char) ((ch & 0x3F) | 0x80);
- len++;
- } else if (ch <= 0xFFFF) {
- buf[0] = (char) ((ch >> 12) | 0xE0);
- buf[1] = (char) (((ch >> 6) & 0x3F) | 0x80);
- buf[2] = (char) ((ch & 0x3F) | 0x80);
- len += 2;
- } else if (ch <= 0x1fffff) {
- buf[0] =(char) ((ch >> 18) | 0xF0);
- buf[1] =(char) (((ch >> 12) & 0x3F) | 0x80);
- buf[2] =(char) (((ch >> 6) & 0x3F) | 0x80);
- buf[3] =(char) ((ch & 0x3F) | 0x80);
- len += 3;
- } else {
- buf[0] = '?';
- }
- return len;
+ int len = 1;
+ if (ch <= 0x7F) {
+ buf[0] = (char) ch;
+ } else if (ch <= 0x07FF) {
+ buf[0] = (char) ((ch >> 6) | 0xC0);
+ buf[1] = (char) ((ch & 0x3F) | 0x80);
+ len++;
+ } else if (ch <= 0xFFFF) {
+ buf[0] = (char) ((ch >> 12) | 0xE0);
+ buf[1] = (char) (((ch >> 6) & 0x3F) | 0x80);
+ buf[2] = (char) ((ch & 0x3F) | 0x80);
+ len += 2;
+ } else if (ch <= 0x1fffff) {
+ buf[0] =(char) ((ch >> 18) | 0xF0);
+ buf[1] =(char) (((ch >> 12) & 0x3F) | 0x80);
+ buf[2] =(char) (((ch >> 6) & 0x3F) | 0x80);
+ buf[3] =(char) ((ch & 0x3F) | 0x80);
+ len += 3;
+ } else {
+ buf[0] = '?';
+ }
+ return len;
}
static VALUE mJSON, mExt, cParser, eParserError, eNestingError;
static VALUE CNaN, CInfinity, CMinusInfinity;
static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions,
-i_chr, i_max_nesting, i_allow_nan, i_symbolize_names,
-i_object_class, i_array_class, i_decimal_class, i_key_p,
-i_deep_const_get, i_match, i_match_string, i_aset, i_aref,
-i_leftshift, i_new, i_try_convert, i_freeze, i_uminus;
+ i_chr, i_max_nesting, i_allow_nan, i_symbolize_names,
+ i_object_class, i_array_class, i_decimal_class, i_key_p,
+ i_deep_const_get, i_match, i_match_string, i_aset, i_aref,
+ i_leftshift, i_new, i_try_convert, i_freeze, i_uminus;
#line 125 "parser.rl"
+#line 107 "parser.c"
enum {JSON_object_start = 1};
enum {JSON_object_first_final = 27};
enum {JSON_object_error = 0};
enum {JSON_object_en_main = 1};
-static const char MAYBE_UNUSED(_JSON_object_nfa_targs)[] = {
- 0, 0
-};
-
-static const char MAYBE_UNUSED(_JSON_object_nfa_offsets)[] = {
- 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
-};
-
-static const char MAYBE_UNUSED(_JSON_object_nfa_push_actions)[] = {
- 0, 0
-};
-
-static const char MAYBE_UNUSED(_JSON_object_nfa_pop_trans)[] = {
- 0, 0
-};
-
#line 167 "parser.rl"
static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting)
{
- int cs = EVIL;
- VALUE last_name = Qnil;
- VALUE object_class = json->object_class;
+ int cs = EVIL;
+ VALUE last_name = Qnil;
+ VALUE object_class = json->object_class;
- if (json->max_nesting && current_nesting > json->max_nesting) {
- rb_raise(eNestingError, "nesting of %d is too deep", current_nesting);
- }
+ if (json->max_nesting && current_nesting > json->max_nesting) {
+ rb_raise(eNestingError, "nesting of %d is too deep", current_nesting);
+ }
- *result = NIL_P(object_class) ? rb_hash_new() : rb_class_new_instance(0, 0, object_class);
+ *result = NIL_P(object_class) ? rb_hash_new() : rb_class_new_instance(0, 0, object_class);
+#line 131 "parser.c"
{
- cs = (int)JSON_object_start;
+ cs = JSON_object_start;
}
- #line 182 "parser.rl"
-
+#line 182 "parser.rl"
+#line 138 "parser.c"
{
- if ( p == pe )
+ if ( p == pe )
goto _test_eof;
- switch ( cs )
- {
- case 1:
- goto st_case_1;
- case 0:
- goto st_case_0;
- case 2:
- goto st_case_2;
- case 3:
- goto st_case_3;
- case 4:
- goto st_case_4;
- case 5:
- goto st_case_5;
- case 6:
- goto st_case_6;
- case 7:
- goto st_case_7;
- case 8:
- goto st_case_8;
- case 9:
- goto st_case_9;
- case 10:
- goto st_case_10;
- case 11:
- goto st_case_11;
- case 12:
- goto st_case_12;
- case 13:
- goto st_case_13;
- case 14:
- goto st_case_14;
- case 15:
- goto st_case_15;
- case 16:
- goto st_case_16;
- case 17:
- goto st_case_17;
- case 18:
- goto st_case_18;
- case 27:
- goto st_case_27;
- case 19:
- goto st_case_19;
- case 20:
- goto st_case_20;
- case 21:
- goto st_case_21;
- case 22:
- goto st_case_22;
- case 23:
- goto st_case_23;
- case 24:
- goto st_case_24;
- case 25:
- goto st_case_25;
- case 26:
- goto st_case_26;
- }
- goto st_out;
- st_case_1:
- if ( ( (*( p))) == 123 ) {
- goto st2;
- }
- {
- goto st0;
- }
- st_case_0:
- st0:
- cs = 0;
- goto _out;
- st2:
- p+= 1;
- if ( p == pe )
+ switch ( cs )
+ {
+case 1:
+ if ( (*p) == 123 )
+ goto st2;
+ goto st0;
+st0:
+cs = 0;
+ goto _out;
+st2:
+ if ( ++p == pe )
goto _test_eof2;
- st_case_2:
- switch( ( (*( p))) ) {
- case 13: {
- goto st2;
- }
- case 32: {
- goto st2;
- }
- case 34: {
- goto ctr2;
- }
- case 47: {
- goto st23;
- }
- case 125: {
- goto ctr4;
- }
- }
- if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) {
- goto st2;
- }
- {
- goto st0;
- }
- ctr2:
- {
- #line 149 "parser.rl"
-
- char *np;
- json->parsing_name = 1;
- np = JSON_parse_string(json, p, pe, &last_name);
- json->parsing_name = 0;
- if (np == NULL) { {p = p - 1; } {p+= 1; cs = 3; goto _out;} } else {p = (( np))-1;}
-
- }
-
- goto st3;
- st3:
- p+= 1;
- if ( p == pe )
+case 2:
+ switch( (*p) ) {
+ case 13: goto st2;
+ case 32: goto st2;
+ case 34: goto tr2;
+ case 47: goto st23;
+ case 125: goto tr4;
+ }
+ if ( 9 <= (*p) && (*p) <= 10 )
+ goto st2;
+ goto st0;
+tr2:
+#line 149 "parser.rl"
+ {
+ char *np;
+ json->parsing_name = 1;
+ np = JSON_parse_string(json, p, pe, &last_name);
+ json->parsing_name = 0;
+ if (np == NULL) { p--; {p++; cs = 3; goto _out;} } else {p = (( np))-1;}
+ }
+ goto st3;
+st3:
+ if ( ++p == pe )
goto _test_eof3;
- st_case_3:
- switch( ( (*( p))) ) {
- case 13: {
- goto st3;
- }
- case 32: {
- goto st3;
- }
- case 47: {
- goto st4;
- }
- case 58: {
- goto st8;
- }
- }
- if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) {
- goto st3;
- }
- {
- goto st0;
- }
- st4:
- p+= 1;
- if ( p == pe )
+case 3:
+#line 179 "parser.c"
+ switch( (*p) ) {
+ case 13: goto st3;
+ case 32: goto st3;
+ case 47: goto st4;
+ case 58: goto st8;
+ }
+ if ( 9 <= (*p) && (*p) <= 10 )
+ goto st3;
+ goto st0;
+st4:
+ if ( ++p == pe )
goto _test_eof4;
- st_case_4:
- switch( ( (*( p))) ) {
- case 42: {
- goto st5;
- }
- case 47: {
- goto st7;
- }
- }
- {
- goto st0;
- }
- st5:
- p+= 1;
- if ( p == pe )
+case 4:
+ switch( (*p) ) {
+ case 42: goto st5;
+ case 47: goto st7;
+ }
+ goto st0;
+st5:
+ if ( ++p == pe )
goto _test_eof5;
- st_case_5:
- if ( ( (*( p))) == 42 ) {
- goto st6;
- }
- {
- goto st5;
- }
- st6:
- p+= 1;
- if ( p == pe )
+case 5:
+ if ( (*p) == 42 )
+ goto st6;
+ goto st5;
+st6:
+ if ( ++p == pe )
goto _test_eof6;
- st_case_6:
- switch( ( (*( p))) ) {
- case 42: {
- goto st6;
- }
- case 47: {
- goto st3;
- }
- }
- {
- goto st5;
- }
- st7:
- p+= 1;
- if ( p == pe )
+case 6:
+ switch( (*p) ) {
+ case 42: goto st6;
+ case 47: goto st3;
+ }
+ goto st5;
+st7:
+ if ( ++p == pe )
goto _test_eof7;
- st_case_7:
- if ( ( (*( p))) == 10 ) {
- goto st3;
- }
- {
- goto st7;
- }
- st8:
- p+= 1;
- if ( p == pe )
+case 7:
+ if ( (*p) == 10 )
+ goto st3;
+ goto st7;
+st8:
+ if ( ++p == pe )
goto _test_eof8;
- st_case_8:
- switch( ( (*( p))) ) {
- case 13: {
- goto st8;
- }
- case 32: {
- goto st8;
- }
- case 34: {
- goto ctr11;
- }
- case 45: {
- goto ctr11;
- }
- case 47: {
- goto st19;
- }
- case 73: {
- goto ctr11;
- }
- case 78: {
- goto ctr11;
- }
- case 91: {
- goto ctr11;
- }
- case 102: {
- goto ctr11;
- }
- case 110: {
- goto ctr11;
- }
- case 116: {
- goto ctr11;
- }
- case 123: {
- goto ctr11;
- }
- }
- if ( ( (*( p))) > 10 ) {
- if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
- goto ctr11;
- }
- } else if ( ( (*( p))) >= 9 ) {
- goto st8;
- }
- {
- goto st0;
- }
- ctr11:
- {
- #line 133 "parser.rl"
-
- VALUE v = Qnil;
- char *np = JSON_parse_value(json, p, pe, &v, current_nesting);
- if (np == NULL) {
- {p = p - 1; } {p+= 1; cs = 9; goto _out;}
- } else {
- if (NIL_P(json->object_class)) {
- OBJ_FREEZE(last_name);
- rb_hash_aset(*result, last_name, v);
- } else {
- rb_funcall(*result, i_aset, 2, last_name, v);
- }
- {p = (( np))-1;}
-
- }
- }
-
- goto st9;
- st9:
- p+= 1;
- if ( p == pe )
+case 8:
+ switch( (*p) ) {
+ case 13: goto st8;
+ case 32: goto st8;
+ case 34: goto tr11;
+ case 45: goto tr11;
+ case 47: goto st19;
+ case 73: goto tr11;
+ case 78: goto tr11;
+ case 91: goto tr11;
+ case 102: goto tr11;
+ case 110: goto tr11;
+ case 116: goto tr11;
+ case 123: goto tr11;
+ }
+ if ( (*p) > 10 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr11;
+ } else if ( (*p) >= 9 )
+ goto st8;
+ goto st0;
+tr11:
+#line 133 "parser.rl"
+ {
+ VALUE v = Qnil;
+ char *np = JSON_parse_value(json, p, pe, &v, current_nesting);
+ if (np == NULL) {
+ p--; {p++; cs = 9; goto _out;}
+ } else {
+ if (NIL_P(json->object_class)) {
+ OBJ_FREEZE(last_name);
+ rb_hash_aset(*result, last_name, v);
+ } else {
+ rb_funcall(*result, i_aset, 2, last_name, v);
+ }
+ {p = (( np))-1;}
+ }
+ }
+ goto st9;
+st9:
+ if ( ++p == pe )
goto _test_eof9;
- st_case_9:
- switch( ( (*( p))) ) {
- case 13: {
- goto st9;
- }
- case 32: {
- goto st9;
- }
- case 44: {
- goto st10;
- }
- case 47: {
- goto st15;
- }
- case 125: {
- goto ctr4;
- }
- }
- if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) {
- goto st9;
- }
- {
- goto st0;
- }
- st10:
- p+= 1;
- if ( p == pe )
+case 9:
+#line 267 "parser.c"
+ switch( (*p) ) {
+ case 13: goto st9;
+ case 32: goto st9;
+ case 44: goto st10;
+ case 47: goto st15;
+ case 125: goto tr4;
+ }
+ if ( 9 <= (*p) && (*p) <= 10 )
+ goto st9;
+ goto st0;
+st10:
+ if ( ++p == pe )
goto _test_eof10;
- st_case_10:
- switch( ( (*( p))) ) {
- case 13: {
- goto st10;
- }
- case 32: {
- goto st10;
- }
- case 34: {
- goto ctr2;
- }
- case 47: {
- goto st11;
- }
- }
- if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) {
- goto st10;
- }
- {
- goto st0;
- }
- st11:
- p+= 1;
- if ( p == pe )
+case 10:
+ switch( (*p) ) {
+ case 13: goto st10;
+ case 32: goto st10;
+ case 34: goto tr2;
+ case 47: goto st11;
+ }
+ if ( 9 <= (*p) && (*p) <= 10 )
+ goto st10;
+ goto st0;
+st11:
+ if ( ++p == pe )
goto _test_eof11;
- st_case_11:
- switch( ( (*( p))) ) {
- case 42: {
- goto st12;
- }
- case 47: {
- goto st14;
- }
- }
- {
- goto st0;
- }
- st12:
- p+= 1;
- if ( p == pe )
+case 11:
+ switch( (*p) ) {
+ case 42: goto st12;
+ case 47: goto st14;
+ }
+ goto st0;
+st12:
+ if ( ++p == pe )
goto _test_eof12;
- st_case_12:
- if ( ( (*( p))) == 42 ) {
- goto st13;
- }
- {
- goto st12;
- }
- st13:
- p+= 1;
- if ( p == pe )
+case 12:
+ if ( (*p) == 42 )
+ goto st13;
+ goto st12;
+st13:
+ if ( ++p == pe )
goto _test_eof13;
- st_case_13:
- switch( ( (*( p))) ) {
- case 42: {
- goto st13;
- }
- case 47: {
- goto st10;
- }
- }
- {
- goto st12;
- }
- st14:
- p+= 1;
- if ( p == pe )
+case 13:
+ switch( (*p) ) {
+ case 42: goto st13;
+ case 47: goto st10;
+ }
+ goto st12;
+st14:
+ if ( ++p == pe )
goto _test_eof14;
- st_case_14:
- if ( ( (*( p))) == 10 ) {
- goto st10;
- }
- {
- goto st14;
- }
- st15:
- p+= 1;
- if ( p == pe )
+case 14:
+ if ( (*p) == 10 )
+ goto st10;
+ goto st14;
+st15:
+ if ( ++p == pe )
goto _test_eof15;
- st_case_15:
- switch( ( (*( p))) ) {
- case 42: {
- goto st16;
- }
- case 47: {
- goto st18;
- }
- }
- {
- goto st0;
- }
- st16:
- p+= 1;
- if ( p == pe )
+case 15:
+ switch( (*p) ) {
+ case 42: goto st16;
+ case 47: goto st18;
+ }
+ goto st0;
+st16:
+ if ( ++p == pe )
goto _test_eof16;
- st_case_16:
- if ( ( (*( p))) == 42 ) {
- goto st17;
- }
- {
- goto st16;
- }
- st17:
- p+= 1;
- if ( p == pe )
+case 16:
+ if ( (*p) == 42 )
+ goto st17;
+ goto st16;
+st17:
+ if ( ++p == pe )
goto _test_eof17;
- st_case_17:
- switch( ( (*( p))) ) {
- case 42: {
- goto st17;
- }
- case 47: {
- goto st9;
- }
- }
- {
- goto st16;
- }
- st18:
- p+= 1;
- if ( p == pe )
+case 17:
+ switch( (*p) ) {
+ case 42: goto st17;
+ case 47: goto st9;
+ }
+ goto st16;
+st18:
+ if ( ++p == pe )
goto _test_eof18;
- st_case_18:
- if ( ( (*( p))) == 10 ) {
- goto st9;
- }
- {
- goto st18;
- }
- ctr4:
- {
- #line 157 "parser.rl"
- {p = p - 1; } {p+= 1; cs = 27; goto _out;} }
-
- goto st27;
- st27:
- p+= 1;
- if ( p == pe )
+case 18:
+ if ( (*p) == 10 )
+ goto st9;
+ goto st18;
+tr4:
+#line 157 "parser.rl"
+ { p--; {p++; cs = 27; goto _out;} }
+ goto st27;
+st27:
+ if ( ++p == pe )
goto _test_eof27;
- st_case_27:
- {
- goto st0;
- }
- st19:
- p+= 1;
- if ( p == pe )
+case 27:
+#line 363 "parser.c"
+ goto st0;
+st19:
+ if ( ++p == pe )
goto _test_eof19;
- st_case_19:
- switch( ( (*( p))) ) {
- case 42: {
- goto st20;
- }
- case 47: {
- goto st22;
- }
- }
- {
- goto st0;
- }
- st20:
- p+= 1;
- if ( p == pe )
+case 19:
+ switch( (*p) ) {
+ case 42: goto st20;
+ case 47: goto st22;
+ }
+ goto st0;
+st20:
+ if ( ++p == pe )
goto _test_eof20;
- st_case_20:
- if ( ( (*( p))) == 42 ) {
- goto st21;
- }
- {
- goto st20;
- }
- st21:
- p+= 1;
- if ( p == pe )
+case 20:
+ if ( (*p) == 42 )
+ goto st21;
+ goto st20;
+st21:
+ if ( ++p == pe )
goto _test_eof21;
- st_case_21:
- switch( ( (*( p))) ) {
- case 42: {
- goto st21;
- }
- case 47: {
- goto st8;
- }
- }
- {
- goto st20;
- }
- st22:
- p+= 1;
- if ( p == pe )
+case 21:
+ switch( (*p) ) {
+ case 42: goto st21;
+ case 47: goto st8;
+ }
+ goto st20;
+st22:
+ if ( ++p == pe )
goto _test_eof22;
- st_case_22:
- if ( ( (*( p))) == 10 ) {
- goto st8;
- }
- {
- goto st22;
- }
- st23:
- p+= 1;
- if ( p == pe )
+case 22:
+ if ( (*p) == 10 )
+ goto st8;
+ goto st22;
+st23:
+ if ( ++p == pe )
goto _test_eof23;
- st_case_23:
- switch( ( (*( p))) ) {
- case 42: {
- goto st24;
- }
- case 47: {
- goto st26;
- }
- }
- {
- goto st0;
- }
- st24:
- p+= 1;
- if ( p == pe )
+case 23:
+ switch( (*p) ) {
+ case 42: goto st24;
+ case 47: goto st26;
+ }
+ goto st0;
+st24:
+ if ( ++p == pe )
goto _test_eof24;
- st_case_24:
- if ( ( (*( p))) == 42 ) {
- goto st25;
- }
- {
- goto st24;
- }
- st25:
- p+= 1;
- if ( p == pe )
+case 24:
+ if ( (*p) == 42 )
+ goto st25;
+ goto st24;
+st25:
+ if ( ++p == pe )
goto _test_eof25;
- st_case_25:
- switch( ( (*( p))) ) {
- case 42: {
- goto st25;
- }
- case 47: {
- goto st2;
- }
- }
- {
- goto st24;
- }
- st26:
- p+= 1;
- if ( p == pe )
+case 25:
+ switch( (*p) ) {
+ case 42: goto st25;
+ case 47: goto st2;
+ }
+ goto st24;
+st26:
+ if ( ++p == pe )
goto _test_eof26;
- st_case_26:
- if ( ( (*( p))) == 10 ) {
- goto st2;
- }
- {
- goto st26;
- }
- st_out:
- _test_eof2: cs = 2; goto _test_eof;
- _test_eof3: cs = 3; goto _test_eof;
- _test_eof4: cs = 4; goto _test_eof;
- _test_eof5: cs = 5; goto _test_eof;
- _test_eof6: cs = 6; goto _test_eof;
- _test_eof7: cs = 7; goto _test_eof;
- _test_eof8: cs = 8; goto _test_eof;
- _test_eof9: cs = 9; goto _test_eof;
- _test_eof10: cs = 10; goto _test_eof;
- _test_eof11: cs = 11; goto _test_eof;
- _test_eof12: cs = 12; goto _test_eof;
- _test_eof13: cs = 13; goto _test_eof;
- _test_eof14: cs = 14; goto _test_eof;
- _test_eof15: cs = 15; goto _test_eof;
- _test_eof16: cs = 16; goto _test_eof;
- _test_eof17: cs = 17; goto _test_eof;
- _test_eof18: cs = 18; goto _test_eof;
- _test_eof27: cs = 27; goto _test_eof;
- _test_eof19: cs = 19; goto _test_eof;
- _test_eof20: cs = 20; goto _test_eof;
- _test_eof21: cs = 21; goto _test_eof;
- _test_eof22: cs = 22; goto _test_eof;
- _test_eof23: cs = 23; goto _test_eof;
- _test_eof24: cs = 24; goto _test_eof;
- _test_eof25: cs = 25; goto _test_eof;
- _test_eof26: cs = 26; goto _test_eof;
-
- _test_eof: {}
- _out: {}
- }
-
- #line 183 "parser.rl"
-
-
- if (cs >= JSON_object_first_final) {
- if (json->create_additions) {
- VALUE klassname;
- if (NIL_P(json->object_class)) {
- klassname = rb_hash_aref(*result, json->create_id);
- } else {
- klassname = rb_funcall(*result, i_aref, 1, json->create_id);
- }
- if (!NIL_P(klassname)) {
- VALUE klass = rb_funcall(mJSON, i_deep_const_get, 1, klassname);
- if (RTEST(rb_funcall(klass, i_json_creatable_p, 0))) {
- *result = rb_funcall(klass, i_json_create, 1, *result);
- }
- }
- }
- return p + 1;
- } else {
- return NULL;
+case 26:
+ if ( (*p) == 10 )
+ goto st2;
+ goto st26;
+ }
+ _test_eof2: cs = 2; goto _test_eof;
+ _test_eof3: cs = 3; goto _test_eof;
+ _test_eof4: cs = 4; goto _test_eof;
+ _test_eof5: cs = 5; goto _test_eof;
+ _test_eof6: cs = 6; goto _test_eof;
+ _test_eof7: cs = 7; goto _test_eof;
+ _test_eof8: cs = 8; goto _test_eof;
+ _test_eof9: cs = 9; goto _test_eof;
+ _test_eof10: cs = 10; goto _test_eof;
+ _test_eof11: cs = 11; goto _test_eof;
+ _test_eof12: cs = 12; goto _test_eof;
+ _test_eof13: cs = 13; goto _test_eof;
+ _test_eof14: cs = 14; goto _test_eof;
+ _test_eof15: cs = 15; goto _test_eof;
+ _test_eof16: cs = 16; goto _test_eof;
+ _test_eof17: cs = 17; goto _test_eof;
+ _test_eof18: cs = 18; goto _test_eof;
+ _test_eof27: cs = 27; goto _test_eof;
+ _test_eof19: cs = 19; goto _test_eof;
+ _test_eof20: cs = 20; goto _test_eof;
+ _test_eof21: cs = 21; goto _test_eof;
+ _test_eof22: cs = 22; goto _test_eof;
+ _test_eof23: cs = 23; goto _test_eof;
+ _test_eof24: cs = 24; goto _test_eof;
+ _test_eof25: cs = 25; goto _test_eof;
+ _test_eof26: cs = 26; goto _test_eof;
+
+ _test_eof: {}
+ _out: {}
}
+
+#line 183 "parser.rl"
+
+ if (cs >= JSON_object_first_final) {
+ if (json->create_additions) {
+ VALUE klassname;
+ if (NIL_P(json->object_class)) {
+ klassname = rb_hash_aref(*result, json->create_id);
+ } else {
+ klassname = rb_funcall(*result, i_aref, 1, json->create_id);
+ }
+ if (!NIL_P(klassname)) {
+ VALUE klass = rb_funcall(mJSON, i_deep_const_get, 1, klassname);
+ if (RTEST(rb_funcall(klass, i_json_creatable_p, 0))) {
+ *result = rb_funcall(klass, i_json_create, 1, *result);
+ }
+ }
+ }
+ return p + 1;
+ } else {
+ return NULL;
+ }
}
+#line 486 "parser.c"
enum {JSON_value_start = 1};
enum {JSON_value_first_final = 29};
enum {JSON_value_error = 0};
enum {JSON_value_en_main = 1};
-static const char MAYBE_UNUSED(_JSON_value_nfa_targs)[] = {
- 0, 0
-};
-
-static const char MAYBE_UNUSED(_JSON_value_nfa_offsets)[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0
-};
-
-static const char MAYBE_UNUSED(_JSON_value_nfa_push_actions)[] = {
- 0, 0
-};
-
-static const char MAYBE_UNUSED(_JSON_value_nfa_pop_trans)[] = {
- 0, 0
-};
-
#line 283 "parser.rl"
static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting)
{
- int cs = EVIL;
+ int cs = EVIL;
+#line 502 "parser.c"
{
- cs = (int)JSON_value_start;
+ cs = JSON_value_start;
}
- #line 290 "parser.rl"
-
+#line 290 "parser.rl"
+#line 509 "parser.c"
{
- if ( p == pe )
+ if ( p == pe )
goto _test_eof;
- switch ( cs )
- {
- case 1:
- goto st_case_1;
- case 0:
- goto st_case_0;
- case 29:
- goto st_case_29;
- case 2:
- goto st_case_2;
- case 3:
- goto st_case_3;
- case 4:
- goto st_case_4;
- case 5:
- goto st_case_5;
- case 6:
- goto st_case_6;
- case 7:
- goto st_case_7;
- case 8:
- goto st_case_8;
- case 9:
- goto st_case_9;
- case 10:
- goto st_case_10;
- case 11:
- goto st_case_11;
- case 12:
- goto st_case_12;
- case 13:
- goto st_case_13;
- case 14:
- goto st_case_14;
- case 15:
- goto st_case_15;
- case 16:
- goto st_case_16;
- case 17:
- goto st_case_17;
- case 18:
- goto st_case_18;
- case 19:
- goto st_case_19;
- case 20:
- goto st_case_20;
- case 21:
- goto st_case_21;
- case 22:
- goto st_case_22;
- case 23:
- goto st_case_23;
- case 24:
- goto st_case_24;
- case 25:
- goto st_case_25;
- case 26:
- goto st_case_26;
- case 27:
- goto st_case_27;
- case 28:
- goto st_case_28;
- }
- goto st_out;
- st1:
- p+= 1;
- if ( p == pe )
+ switch ( cs )
+ {
+st1:
+ if ( ++p == pe )
goto _test_eof1;
- st_case_1:
- switch( ( (*( p))) ) {
- case 13: {
- goto st1;
- }
- case 32: {
- goto st1;
- }
- case 34: {
- goto ctr2;
- }
- case 45: {
- goto ctr3;
- }
- case 47: {
- goto st6;
- }
- case 73: {
- goto st10;
- }
- case 78: {
- goto st17;
- }
- case 91: {
- goto ctr7;
- }
- case 102: {
- goto st19;
- }
- case 110: {
- goto st23;
- }
- case 116: {
- goto st26;
- }
- case 123: {
- goto ctr11;
- }
- }
- if ( ( (*( p))) > 10 ) {
- if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
- goto ctr3;
- }
- } else if ( ( (*( p))) >= 9 ) {
- goto st1;
- }
- {
- goto st0;
- }
- st_case_0:
- st0:
- cs = 0;
- goto _out;
- ctr2:
- {
- #line 235 "parser.rl"
-
- char *np = JSON_parse_string(json, p, pe, result);
- if (np == NULL) { {p = p - 1; } {p+= 1; cs = 29; goto _out;} } else {p = (( np))-1;}
-
- }
-
- goto st29;
- ctr3:
- {
- #line 240 "parser.rl"
-
- char *np;
- if(pe > p + 8 && !strncmp(MinusInfinity, p, 9)) {
- if (json->allow_nan) {
- *result = CMinusInfinity;
- {p = (( p + 10))-1;}
-
- {p = p - 1; } {p+= 1; cs = 29; goto _out;}
- } else {
- rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p);
- }
- }
- np = JSON_parse_float(json, p, pe, result);
- if (np != NULL) {p = (( np))-1;}
-
- np = JSON_parse_integer(json, p, pe, result);
- if (np != NULL) {p = (( np))-1;}
-
- {p = p - 1; } {p+= 1; cs = 29; goto _out;}
- }
-
- goto st29;
- ctr7:
- {
- #line 258 "parser.rl"
-
- char *np;
- np = JSON_parse_array(json, p, pe, result, current_nesting + 1);
- if (np == NULL) { {p = p - 1; } {p+= 1; cs = 29; goto _out;} } else {p = (( np))-1;}
-
- }
-
- goto st29;
- ctr11:
- {
- #line 264 "parser.rl"
-
- char *np;
- np = JSON_parse_object(json, p, pe, result, current_nesting + 1);
- if (np == NULL) { {p = p - 1; } {p+= 1; cs = 29; goto _out;} } else {p = (( np))-1;}
-
- }
-
- goto st29;
- ctr25:
- {
- #line 228 "parser.rl"
-
- if (json->allow_nan) {
- *result = CInfinity;
- } else {
- rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 8);
- }
- }
-
- goto st29;
- ctr27:
- {
- #line 221 "parser.rl"
-
- if (json->allow_nan) {
- *result = CNaN;
- } else {
- rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 2);
- }
- }
-
- goto st29;
- ctr31:
- {
- #line 215 "parser.rl"
-
- *result = Qfalse;
- }
-
- goto st29;
- ctr34:
- {
- #line 212 "parser.rl"
-
- *result = Qnil;
- }
-
- goto st29;
- ctr37:
- {
- #line 218 "parser.rl"
-
- *result = Qtrue;
- }
-
- goto st29;
- st29:
- p+= 1;
- if ( p == pe )
+case 1:
+ switch( (*p) ) {
+ case 13: goto st1;
+ case 32: goto st1;
+ case 34: goto tr2;
+ case 45: goto tr3;
+ case 47: goto st6;
+ case 73: goto st10;
+ case 78: goto st17;
+ case 91: goto tr7;
+ case 102: goto st19;
+ case 110: goto st23;
+ case 116: goto st26;
+ case 123: goto tr11;
+ }
+ if ( (*p) > 10 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr3;
+ } else if ( (*p) >= 9 )
+ goto st1;
+ goto st0;
+st0:
+cs = 0;
+ goto _out;
+tr2:
+#line 235 "parser.rl"
+ {
+ char *np = JSON_parse_string(json, p, pe, result);
+ if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;}
+ }
+ goto st29;
+tr3:
+#line 240 "parser.rl"
+ {
+ char *np;
+ if(pe > p + 8 && !strncmp(MinusInfinity, p, 9)) {
+ if (json->allow_nan) {
+ *result = CMinusInfinity;
+ {p = (( p + 10))-1;}
+ p--; {p++; cs = 29; goto _out;}
+ } else {
+ rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p);
+ }
+ }
+ np = JSON_parse_float(json, p, pe, result);
+ if (np != NULL) {p = (( np))-1;}
+ np = JSON_parse_integer(json, p, pe, result);
+ if (np != NULL) {p = (( np))-1;}
+ p--; {p++; cs = 29; goto _out;}
+ }
+ goto st29;
+tr7:
+#line 258 "parser.rl"
+ {
+ char *np;
+ np = JSON_parse_array(json, p, pe, result, current_nesting + 1);
+ if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;}
+ }
+ goto st29;
+tr11:
+#line 264 "parser.rl"
+ {
+ char *np;
+ np = JSON_parse_object(json, p, pe, result, current_nesting + 1);
+ if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;}
+ }
+ goto st29;
+tr25:
+#line 228 "parser.rl"
+ {
+ if (json->allow_nan) {
+ *result = CInfinity;
+ } else {
+ rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 7);
+ }
+ }
+ goto st29;
+tr27:
+#line 221 "parser.rl"
+ {
+ if (json->allow_nan) {
+ *result = CNaN;
+ } else {
+ rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 2);
+ }
+ }
+ goto st29;
+tr31:
+#line 215 "parser.rl"
+ {
+ *result = Qfalse;
+ }
+ goto st29;
+tr34:
+#line 212 "parser.rl"
+ {
+ *result = Qnil;
+ }
+ goto st29;
+tr37:
+#line 218 "parser.rl"
+ {
+ *result = Qtrue;
+ }
+ goto st29;
+st29:
+ if ( ++p == pe )
goto _test_eof29;
- st_case_29:
- {
- #line 270 "parser.rl"
- {p = p - 1; } {p+= 1; cs = 29; goto _out;} }
- switch( ( (*( p))) ) {
- case 13: {
- goto st29;
- }
- case 32: {
- goto st29;
- }
- case 47: {
- goto st2;
- }
- }
- if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) {
- goto st29;
- }
- {
- goto st0;
- }
- st2:
- p+= 1;
- if ( p == pe )
+case 29:
+#line 270 "parser.rl"
+ { p--; {p++; cs = 29; goto _out;} }
+#line 629 "parser.c"
+ switch( (*p) ) {
+ case 13: goto st29;
+ case 32: goto st29;
+ case 47: goto st2;
+ }
+ if ( 9 <= (*p) && (*p) <= 10 )
+ goto st29;
+ goto st0;
+st2:
+ if ( ++p == pe )
goto _test_eof2;
- st_case_2:
- switch( ( (*( p))) ) {
- case 42: {
- goto st3;
- }
- case 47: {
- goto st5;
- }
- }
- {
- goto st0;
- }
- st3:
- p+= 1;
- if ( p == pe )
+case 2:
+ switch( (*p) ) {
+ case 42: goto st3;
+ case 47: goto st5;
+ }
+ goto st0;
+st3:
+ if ( ++p == pe )
goto _test_eof3;
- st_case_3:
- if ( ( (*( p))) == 42 ) {
- goto st4;
- }
- {
- goto st3;
- }
- st4:
- p+= 1;
- if ( p == pe )
+case 3:
+ if ( (*p) == 42 )
+ goto st4;
+ goto st3;
+st4:
+ if ( ++p == pe )
goto _test_eof4;
- st_case_4:
- switch( ( (*( p))) ) {
- case 42: {
- goto st4;
- }
- case 47: {
- goto st29;
- }
- }
- {
- goto st3;
- }
- st5:
- p+= 1;
- if ( p == pe )
+case 4:
+ switch( (*p) ) {
+ case 42: goto st4;
+ case 47: goto st29;
+ }
+ goto st3;
+st5:
+ if ( ++p == pe )
goto _test_eof5;
- st_case_5:
- if ( ( (*( p))) == 10 ) {
- goto st29;
- }
- {
- goto st5;
- }
- st6:
- p+= 1;
- if ( p == pe )
+case 5:
+ if ( (*p) == 10 )
+ goto st29;
+ goto st5;
+st6:
+ if ( ++p == pe )
goto _test_eof6;
- st_case_6:
- switch( ( (*( p))) ) {
- case 42: {
- goto st7;
- }
- case 47: {
- goto st9;
- }
- }
- {
- goto st0;
- }
- st7:
- p+= 1;
- if ( p == pe )
+case 6:
+ switch( (*p) ) {
+ case 42: goto st7;
+ case 47: goto st9;
+ }
+ goto st0;
+st7:
+ if ( ++p == pe )
goto _test_eof7;
- st_case_7:
- if ( ( (*( p))) == 42 ) {
- goto st8;
- }
- {
- goto st7;
- }
- st8:
- p+= 1;
- if ( p == pe )
+case 7:
+ if ( (*p) == 42 )
+ goto st8;
+ goto st7;
+st8:
+ if ( ++p == pe )
goto _test_eof8;
- st_case_8:
- switch( ( (*( p))) ) {
- case 42: {
- goto st8;
- }
- case 47: {
- goto st1;
- }
- }
- {
- goto st7;
- }
- st9:
- p+= 1;
- if ( p == pe )
+case 8:
+ switch( (*p) ) {
+ case 42: goto st8;
+ case 47: goto st1;
+ }
+ goto st7;
+st9:
+ if ( ++p == pe )
goto _test_eof9;
- st_case_9:
- if ( ( (*( p))) == 10 ) {
- goto st1;
- }
- {
- goto st9;
- }
- st10:
- p+= 1;
- if ( p == pe )
+case 9:
+ if ( (*p) == 10 )
+ goto st1;
+ goto st9;
+st10:
+ if ( ++p == pe )
goto _test_eof10;
- st_case_10:
- if ( ( (*( p))) == 110 ) {
- goto st11;
- }
- {
- goto st0;
- }
- st11:
- p+= 1;
- if ( p == pe )
+case 10:
+ if ( (*p) == 110 )
+ goto st11;
+ goto st0;
+st11:
+ if ( ++p == pe )
goto _test_eof11;
- st_case_11:
- if ( ( (*( p))) == 102 ) {
- goto st12;
- }
- {
- goto st0;
- }
- st12:
- p+= 1;
- if ( p == pe )
+case 11:
+ if ( (*p) == 102 )
+ goto st12;
+ goto st0;
+st12:
+ if ( ++p == pe )
goto _test_eof12;
- st_case_12:
- if ( ( (*( p))) == 105 ) {
- goto st13;
- }
- {
- goto st0;
- }
- st13:
- p+= 1;
- if ( p == pe )
+case 12:
+ if ( (*p) == 105 )
+ goto st13;
+ goto st0;
+st13:
+ if ( ++p == pe )
goto _test_eof13;
- st_case_13:
- if ( ( (*( p))) == 110 ) {
- goto st14;
- }
- {
- goto st0;
- }
- st14:
- p+= 1;
- if ( p == pe )
+case 13:
+ if ( (*p) == 110 )
+ goto st14;
+ goto st0;
+st14:
+ if ( ++p == pe )
goto _test_eof14;
- st_case_14:
- if ( ( (*( p))) == 105 ) {
- goto st15;
- }
- {
- goto st0;
- }
- st15:
- p+= 1;
- if ( p == pe )
+case 14:
+ if ( (*p) == 105 )
+ goto st15;
+ goto st0;
+st15:
+ if ( ++p == pe )
goto _test_eof15;
- st_case_15:
- if ( ( (*( p))) == 116 ) {
- goto st16;
- }
- {
- goto st0;
- }
- st16:
- p+= 1;
- if ( p == pe )
+case 15:
+ if ( (*p) == 116 )
+ goto st16;
+ goto st0;
+st16:
+ if ( ++p == pe )
goto _test_eof16;
- st_case_16:
- if ( ( (*( p))) == 121 ) {
- goto ctr25;
- }
- {
- goto st0;
- }
- st17:
- p+= 1;
- if ( p == pe )
+case 16:
+ if ( (*p) == 121 )
+ goto tr25;
+ goto st0;
+st17:
+ if ( ++p == pe )
goto _test_eof17;
- st_case_17:
- if ( ( (*( p))) == 97 ) {
- goto st18;
- }
- {
- goto st0;
- }
- st18:
- p+= 1;
- if ( p == pe )
+case 17:
+ if ( (*p) == 97 )
+ goto st18;
+ goto st0;
+st18:
+ if ( ++p == pe )
goto _test_eof18;
- st_case_18:
- if ( ( (*( p))) == 78 ) {
- goto ctr27;
- }
- {
- goto st0;
- }
- st19:
- p+= 1;
- if ( p == pe )
+case 18:
+ if ( (*p) == 78 )
+ goto tr27;
+ goto st0;
+st19:
+ if ( ++p == pe )
goto _test_eof19;
- st_case_19:
- if ( ( (*( p))) == 97 ) {
- goto st20;
- }
- {
- goto st0;
- }
- st20:
- p+= 1;
- if ( p == pe )
+case 19:
+ if ( (*p) == 97 )
+ goto st20;
+ goto st0;
+st20:
+ if ( ++p == pe )
goto _test_eof20;
- st_case_20:
- if ( ( (*( p))) == 108 ) {
- goto st21;
- }
- {
- goto st0;
- }
- st21:
- p+= 1;
- if ( p == pe )
+case 20:
+ if ( (*p) == 108 )
+ goto st21;
+ goto st0;
+st21:
+ if ( ++p == pe )
goto _test_eof21;
- st_case_21:
- if ( ( (*( p))) == 115 ) {
- goto st22;
- }
- {
- goto st0;
- }
- st22:
- p+= 1;
- if ( p == pe )
+case 21:
+ if ( (*p) == 115 )
+ goto st22;
+ goto st0;
+st22:
+ if ( ++p == pe )
goto _test_eof22;
- st_case_22:
- if ( ( (*( p))) == 101 ) {
- goto ctr31;
- }
- {
- goto st0;
- }
- st23:
- p+= 1;
- if ( p == pe )
+case 22:
+ if ( (*p) == 101 )
+ goto tr31;
+ goto st0;
+st23:
+ if ( ++p == pe )
goto _test_eof23;
- st_case_23:
- if ( ( (*( p))) == 117 ) {
- goto st24;
- }
- {
- goto st0;
- }
- st24:
- p+= 1;
- if ( p == pe )
+case 23:
+ if ( (*p) == 117 )
+ goto st24;
+ goto st0;
+st24:
+ if ( ++p == pe )
goto _test_eof24;
- st_case_24:
- if ( ( (*( p))) == 108 ) {
- goto st25;
- }
- {
- goto st0;
- }
- st25:
- p+= 1;
- if ( p == pe )
+case 24:
+ if ( (*p) == 108 )
+ goto st25;
+ goto st0;
+st25:
+ if ( ++p == pe )
goto _test_eof25;
- st_case_25:
- if ( ( (*( p))) == 108 ) {
- goto ctr34;
- }
- {
- goto st0;
- }
- st26:
- p+= 1;
- if ( p == pe )
+case 25:
+ if ( (*p) == 108 )
+ goto tr34;
+ goto st0;
+st26:
+ if ( ++p == pe )
goto _test_eof26;
- st_case_26:
- if ( ( (*( p))) == 114 ) {
- goto st27;
- }
- {
- goto st0;
- }
- st27:
- p+= 1;
- if ( p == pe )
+case 26:
+ if ( (*p) == 114 )
+ goto st27;
+ goto st0;
+st27:
+ if ( ++p == pe )
goto _test_eof27;
- st_case_27:
- if ( ( (*( p))) == 117 ) {
- goto st28;
- }
- {
- goto st0;
- }
- st28:
- p+= 1;
- if ( p == pe )
+case 27:
+ if ( (*p) == 117 )
+ goto st28;
+ goto st0;
+st28:
+ if ( ++p == pe )
goto _test_eof28;
- st_case_28:
- if ( ( (*( p))) == 101 ) {
- goto ctr37;
- }
- {
- goto st0;
- }
- st_out:
- _test_eof1: cs = 1; goto _test_eof;
- _test_eof29: cs = 29; goto _test_eof;
- _test_eof2: cs = 2; goto _test_eof;
- _test_eof3: cs = 3; goto _test_eof;
- _test_eof4: cs = 4; goto _test_eof;
- _test_eof5: cs = 5; goto _test_eof;
- _test_eof6: cs = 6; goto _test_eof;
- _test_eof7: cs = 7; goto _test_eof;
- _test_eof8: cs = 8; goto _test_eof;
- _test_eof9: cs = 9; goto _test_eof;
- _test_eof10: cs = 10; goto _test_eof;
- _test_eof11: cs = 11; goto _test_eof;
- _test_eof12: cs = 12; goto _test_eof;
- _test_eof13: cs = 13; goto _test_eof;
- _test_eof14: cs = 14; goto _test_eof;
- _test_eof15: cs = 15; goto _test_eof;
- _test_eof16: cs = 16; goto _test_eof;
- _test_eof17: cs = 17; goto _test_eof;
- _test_eof18: cs = 18; goto _test_eof;
- _test_eof19: cs = 19; goto _test_eof;
- _test_eof20: cs = 20; goto _test_eof;
- _test_eof21: cs = 21; goto _test_eof;
- _test_eof22: cs = 22; goto _test_eof;
- _test_eof23: cs = 23; goto _test_eof;
- _test_eof24: cs = 24; goto _test_eof;
- _test_eof25: cs = 25; goto _test_eof;
- _test_eof26: cs = 26; goto _test_eof;
- _test_eof27: cs = 27; goto _test_eof;
- _test_eof28: cs = 28; goto _test_eof;
-
- _test_eof: {}
- _out: {}
- }
-
- #line 291 "parser.rl"
-
-
- if (json->freeze) {
- OBJ_FREEZE(*result);
- }
-
- if (cs >= JSON_value_first_final) {
- return p;
- } else {
- return NULL;
+case 28:
+ if ( (*p) == 101 )
+ goto tr37;
+ goto st0;
+ }
+ _test_eof1: cs = 1; goto _test_eof;
+ _test_eof29: cs = 29; goto _test_eof;
+ _test_eof2: cs = 2; goto _test_eof;
+ _test_eof3: cs = 3; goto _test_eof;
+ _test_eof4: cs = 4; goto _test_eof;
+ _test_eof5: cs = 5; goto _test_eof;
+ _test_eof6: cs = 6; goto _test_eof;
+ _test_eof7: cs = 7; goto _test_eof;
+ _test_eof8: cs = 8; goto _test_eof;
+ _test_eof9: cs = 9; goto _test_eof;
+ _test_eof10: cs = 10; goto _test_eof;
+ _test_eof11: cs = 11; goto _test_eof;
+ _test_eof12: cs = 12; goto _test_eof;
+ _test_eof13: cs = 13; goto _test_eof;
+ _test_eof14: cs = 14; goto _test_eof;
+ _test_eof15: cs = 15; goto _test_eof;
+ _test_eof16: cs = 16; goto _test_eof;
+ _test_eof17: cs = 17; goto _test_eof;
+ _test_eof18: cs = 18; goto _test_eof;
+ _test_eof19: cs = 19; goto _test_eof;
+ _test_eof20: cs = 20; goto _test_eof;
+ _test_eof21: cs = 21; goto _test_eof;
+ _test_eof22: cs = 22; goto _test_eof;
+ _test_eof23: cs = 23; goto _test_eof;
+ _test_eof24: cs = 24; goto _test_eof;
+ _test_eof25: cs = 25; goto _test_eof;
+ _test_eof26: cs = 26; goto _test_eof;
+ _test_eof27: cs = 27; goto _test_eof;
+ _test_eof28: cs = 28; goto _test_eof;
+
+ _test_eof: {}
+ _out: {}
}
+
+#line 291 "parser.rl"
+
+ if (json->freeze) {
+ OBJ_FREEZE(*result);
+ }
+
+ if (cs >= JSON_value_first_final) {
+ return p;
+ } else {
+ return NULL;
+ }
}
+#line 884 "parser.c"
enum {JSON_integer_start = 1};
enum {JSON_integer_first_final = 3};
enum {JSON_integer_error = 0};
enum {JSON_integer_en_main = 1};
-static const char MAYBE_UNUSED(_JSON_integer_nfa_targs)[] = {
- 0, 0
-};
-
-static const char MAYBE_UNUSED(_JSON_integer_nfa_offsets)[] = {
- 0, 0, 0, 0, 0, 0, 0
-};
-
-static const char MAYBE_UNUSED(_JSON_integer_nfa_push_actions)[] = {
- 0, 0
-};
-
-static const char MAYBE_UNUSED(_JSON_integer_nfa_pop_trans)[] = {
- 0, 0
-};
-
#line 311 "parser.rl"
static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result)
{
- int cs = EVIL;
+ int cs = EVIL;
+#line 900 "parser.c"
{
- cs = (int)JSON_integer_start;
+ cs = JSON_integer_start;
}
- #line 318 "parser.rl"
-
- json->memo = p;
+#line 318 "parser.rl"
+ json->memo = p;
+#line 908 "parser.c"
{
- if ( p == pe )
+ if ( p == pe )
goto _test_eof;
- switch ( cs )
- {
- case 1:
- goto st_case_1;
- case 0:
- goto st_case_0;
- case 2:
- goto st_case_2;
- case 3:
- goto st_case_3;
- case 4:
- goto st_case_4;
- case 5:
- goto st_case_5;
- }
- goto st_out;
- st_case_1:
- switch( ( (*( p))) ) {
- case 45: {
- goto st2;
- }
- case 48: {
- goto st3;
- }
- }
- if ( 49 <= ( (*( p))) && ( (*( p))) <= 57 ) {
- goto st5;
- }
- {
- goto st0;
- }
- st_case_0:
- st0:
- cs = 0;
- goto _out;
- st2:
- p+= 1;
- if ( p == pe )
+ switch ( cs )
+ {
+case 1:
+ switch( (*p) ) {
+ case 45: goto st2;
+ case 48: goto st3;
+ }
+ if ( 49 <= (*p) && (*p) <= 57 )
+ goto st5;
+ goto st0;
+st0:
+cs = 0;
+ goto _out;
+st2:
+ if ( ++p == pe )
goto _test_eof2;
- st_case_2:
- if ( ( (*( p))) == 48 ) {
- goto st3;
- }
- if ( 49 <= ( (*( p))) && ( (*( p))) <= 57 ) {
- goto st5;
- }
- {
- goto st0;
- }
- st3:
- p+= 1;
- if ( p == pe )
+case 2:
+ if ( (*p) == 48 )
+ goto st3;
+ if ( 49 <= (*p) && (*p) <= 57 )
+ goto st5;
+ goto st0;
+st3:
+ if ( ++p == pe )
goto _test_eof3;
- st_case_3:
- if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
- goto st0;
- }
- {
- goto ctr4;
- }
- ctr4:
- {
- #line 308 "parser.rl"
- {p = p - 1; } {p+= 1; cs = 4; goto _out;} }
-
- goto st4;
- st4:
- p+= 1;
- if ( p == pe )
+case 3:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st0;
+ goto tr4;
+tr4:
+#line 308 "parser.rl"
+ { p--; {p++; cs = 4; goto _out;} }
+ goto st4;
+st4:
+ if ( ++p == pe )
goto _test_eof4;
- st_case_4:
- {
- goto st0;
- }
- st5:
- p+= 1;
- if ( p == pe )
+case 4:
+#line 949 "parser.c"
+ goto st0;
+st5:
+ if ( ++p == pe )
goto _test_eof5;
- st_case_5:
- if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
- goto st5;
- }
- {
- goto ctr4;
- }
- st_out:
- _test_eof2: cs = 2; goto _test_eof;
- _test_eof3: cs = 3; goto _test_eof;
- _test_eof4: cs = 4; goto _test_eof;
- _test_eof5: cs = 5; goto _test_eof;
-
- _test_eof: {}
- _out: {}
- }
-
- #line 320 "parser.rl"
-
-
- if (cs >= JSON_integer_first_final) {
- long len = p - json->memo;
- fbuffer_clear(json->fbuffer);
- fbuffer_append(json->fbuffer, json->memo, len);
- fbuffer_append_char(json->fbuffer, '\0');
- *result = rb_cstr2inum(FBUFFER_PTR(json->fbuffer), 10);
- return p + 1;
- } else {
- return NULL;
+case 5:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st5;
+ goto tr4;
+ }
+ _test_eof2: cs = 2; goto _test_eof;
+ _test_eof3: cs = 3; goto _test_eof;
+ _test_eof4: cs = 4; goto _test_eof;
+ _test_eof5: cs = 5; goto _test_eof;
+
+ _test_eof: {}
+ _out: {}
}
+
+#line 320 "parser.rl"
+
+ if (cs >= JSON_integer_first_final) {
+ long len = p - json->memo;
+ fbuffer_clear(json->fbuffer);
+ fbuffer_append(json->fbuffer, json->memo, len);
+ fbuffer_append_char(json->fbuffer, '\0');
+ *result = rb_cstr2inum(FBUFFER_PTR(json->fbuffer), 10);
+ return p + 1;
+ } else {
+ return NULL;
+ }
}
+#line 983 "parser.c"
enum {JSON_float_start = 1};
enum {JSON_float_first_final = 8};
enum {JSON_float_error = 0};
enum {JSON_float_en_main = 1};
-static const char MAYBE_UNUSED(_JSON_float_nfa_targs)[] = {
- 0, 0
-};
-
-static const char MAYBE_UNUSED(_JSON_float_nfa_offsets)[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0
-};
-
-static const char MAYBE_UNUSED(_JSON_float_nfa_push_actions)[] = {
- 0, 0
-};
-
-static const char MAYBE_UNUSED(_JSON_float_nfa_pop_trans)[] = {
- 0, 0
-};
-
#line 345 "parser.rl"
static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result)
{
- int cs = EVIL;
+ int cs = EVIL;
+#line 999 "parser.c"
{
- cs = (int)JSON_float_start;
+ cs = JSON_float_start;
}
- #line 352 "parser.rl"
-
- json->memo = p;
+#line 352 "parser.rl"
+ json->memo = p;
+#line 1007 "parser.c"
{
- if ( p == pe )
+ if ( p == pe )
goto _test_eof;
- switch ( cs )
- {
- case 1:
- goto st_case_1;
- case 0:
- goto st_case_0;
- case 2:
- goto st_case_2;
- case 3:
- goto st_case_3;
- case 4:
- goto st_case_4;
- case 8:
- goto st_case_8;
- case 9:
- goto st_case_9;
- case 5:
- goto st_case_5;
- case 6:
- goto st_case_6;
- case 10:
- goto st_case_10;
- case 7:
- goto st_case_7;
- }
- goto st_out;
- st_case_1:
- switch( ( (*( p))) ) {
- case 45: {
- goto st2;
- }
- case 48: {
- goto st3;
- }
- }
- if ( 49 <= ( (*( p))) && ( (*( p))) <= 57 ) {
- goto st7;
- }
- {
- goto st0;
- }
- st_case_0:
- st0:
- cs = 0;
- goto _out;
- st2:
- p+= 1;
- if ( p == pe )
+ switch ( cs )
+ {
+case 1:
+ switch( (*p) ) {
+ case 45: goto st2;
+ case 48: goto st3;
+ }
+ if ( 49 <= (*p) && (*p) <= 57 )
+ goto st7;
+ goto st0;
+st0:
+cs = 0;
+ goto _out;
+st2:
+ if ( ++p == pe )
goto _test_eof2;
- st_case_2:
- if ( ( (*( p))) == 48 ) {
- goto st3;
- }
- if ( 49 <= ( (*( p))) && ( (*( p))) <= 57 ) {
- goto st7;
- }
- {
- goto st0;
- }
- st3:
- p+= 1;
- if ( p == pe )
+case 2:
+ if ( (*p) == 48 )
+ goto st3;
+ if ( 49 <= (*p) && (*p) <= 57 )
+ goto st7;
+ goto st0;
+st3:
+ if ( ++p == pe )
goto _test_eof3;
- st_case_3:
- switch( ( (*( p))) ) {
- case 46: {
- goto st4;
- }
- case 69: {
- goto st5;
- }
- case 101: {
- goto st5;
- }
- }
- {
- goto st0;
- }
- st4:
- p+= 1;
- if ( p == pe )
+case 3:
+ switch( (*p) ) {
+ case 46: goto st4;
+ case 69: goto st5;
+ case 101: goto st5;
+ }
+ goto st0;
+st4:
+ if ( ++p == pe )
goto _test_eof4;
- st_case_4:
- if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
- goto st8;
- }
- {
- goto st0;
- }
- st8:
- p+= 1;
- if ( p == pe )
+case 4:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st8;
+ goto st0;
+st8:
+ if ( ++p == pe )
goto _test_eof8;
- st_case_8:
- switch( ( (*( p))) ) {
- case 69: {
- goto st5;
- }
- case 101: {
- goto st5;
- }
- }
- if ( ( (*( p))) > 46 ) {
- if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
- goto st8;
- }
- } else if ( ( (*( p))) >= 45 ) {
- goto st0;
- }
- {
- goto ctr9;
- }
- ctr9:
- {
- #line 339 "parser.rl"
- {p = p - 1; } {p+= 1; cs = 9; goto _out;} }
-
- goto st9;
- st9:
- p+= 1;
- if ( p == pe )
+case 8:
+ switch( (*p) ) {
+ case 69: goto st5;
+ case 101: goto st5;
+ }
+ if ( (*p) > 46 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st8;
+ } else if ( (*p) >= 45 )
+ goto st0;
+ goto tr9;
+tr9:
+#line 339 "parser.rl"
+ { p--; {p++; cs = 9; goto _out;} }
+ goto st9;
+st9:
+ if ( ++p == pe )
goto _test_eof9;
- st_case_9:
- {
- goto st0;
- }
- st5:
- p+= 1;
- if ( p == pe )
+case 9:
+#line 1072 "parser.c"
+ goto st0;
+st5:
+ if ( ++p == pe )
goto _test_eof5;
- st_case_5:
- switch( ( (*( p))) ) {
- case 43: {
- goto st6;
- }
- case 45: {
- goto st6;
- }
- }
- if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
- goto st10;
- }
- {
- goto st0;
- }
- st6:
- p+= 1;
- if ( p == pe )
+case 5:
+ switch( (*p) ) {
+ case 43: goto st6;
+ case 45: goto st6;
+ }
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st10;
+ goto st0;
+st6:
+ if ( ++p == pe )
goto _test_eof6;
- st_case_6:
- if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
- goto st10;
- }
- {
- goto st0;
- }
- st10:
- p+= 1;
- if ( p == pe )
+case 6:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st10;
+ goto st0;
+st10:
+ if ( ++p == pe )
goto _test_eof10;
- st_case_10:
- switch( ( (*( p))) ) {
- case 69: {
- goto st0;
- }
- case 101: {
- goto st0;
- }
- }
- if ( ( (*( p))) > 46 ) {
- if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
- goto st10;
- }
- } else if ( ( (*( p))) >= 45 ) {
- goto st0;
- }
- {
- goto ctr9;
- }
- st7:
- p+= 1;
- if ( p == pe )
+case 10:
+ switch( (*p) ) {
+ case 69: goto st0;
+ case 101: goto st0;
+ }
+ if ( (*p) > 46 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st10;
+ } else if ( (*p) >= 45 )
+ goto st0;
+ goto tr9;
+st7:
+ if ( ++p == pe )
goto _test_eof7;
- st_case_7:
- switch( ( (*( p))) ) {
- case 46: {
- goto st4;
- }
- case 69: {
- goto st5;
- }
- case 101: {
- goto st5;
- }
- }
- if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
- goto st7;
- }
- {
- goto st0;
- }
- st_out:
- _test_eof2: cs = 2; goto _test_eof;
- _test_eof3: cs = 3; goto _test_eof;
- _test_eof4: cs = 4; goto _test_eof;
- _test_eof8: cs = 8; goto _test_eof;
- _test_eof9: cs = 9; goto _test_eof;
- _test_eof5: cs = 5; goto _test_eof;
- _test_eof6: cs = 6; goto _test_eof;
- _test_eof10: cs = 10; goto _test_eof;
- _test_eof7: cs = 7; goto _test_eof;
-
- _test_eof: {}
- _out: {}
- }
-
- #line 354 "parser.rl"
-
-
- if (cs >= JSON_float_first_final) {
- VALUE mod = Qnil;
- ID method_id = 0;
- if (rb_respond_to(json->decimal_class, i_try_convert)) {
- mod = json->decimal_class;
- method_id = i_try_convert;
- } else if (rb_respond_to(json->decimal_class, i_new)) {
- mod = json->decimal_class;
- method_id = i_new;
- } else if (RB_TYPE_P(json->decimal_class, T_CLASS)) {
- VALUE name = rb_class_name(json->decimal_class);
- const char *name_cstr = RSTRING_PTR(name);
- const char *last_colon = strrchr(name_cstr, ':');
- if (last_colon) {
- const char *mod_path_end = last_colon - 1;
- VALUE mod_path = rb_str_substr(name, 0, mod_path_end - name_cstr);
- mod = rb_path_to_class(mod_path);
-
- const char *method_name_beg = last_colon + 1;
- long before_len = method_name_beg - name_cstr;
- long len = RSTRING_LEN(name) - before_len;
- VALUE method_name = rb_str_substr(name, before_len, len);
- method_id = SYM2ID(rb_str_intern(method_name));
- } else {
- mod = rb_mKernel;
- method_id = SYM2ID(rb_str_intern(name));
- }
- }
-
- long len = p - json->memo;
- fbuffer_clear(json->fbuffer);
- fbuffer_append(json->fbuffer, json->memo, len);
- fbuffer_append_char(json->fbuffer, '\0');
-
- if (method_id) {
- VALUE text = rb_str_new2(FBUFFER_PTR(json->fbuffer));
- *result = rb_funcallv(mod, method_id, 1, &text);
- } else {
- *result = DBL2NUM(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
- }
-
- return p + 1;
- } else {
- return NULL;
+case 7:
+ switch( (*p) ) {
+ case 46: goto st4;
+ case 69: goto st5;
+ case 101: goto st5;
+ }
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st7;
+ goto st0;
+ }
+ _test_eof2: cs = 2; goto _test_eof;
+ _test_eof3: cs = 3; goto _test_eof;
+ _test_eof4: cs = 4; goto _test_eof;
+ _test_eof8: cs = 8; goto _test_eof;
+ _test_eof9: cs = 9; goto _test_eof;
+ _test_eof5: cs = 5; goto _test_eof;
+ _test_eof6: cs = 6; goto _test_eof;
+ _test_eof10: cs = 10; goto _test_eof;
+ _test_eof7: cs = 7; goto _test_eof;
+
+ _test_eof: {}
+ _out: {}
}
+
+#line 354 "parser.rl"
+
+ if (cs >= JSON_float_first_final) {
+ VALUE mod = Qnil;
+ ID method_id = 0;
+ if (rb_respond_to(json->decimal_class, i_try_convert)) {
+ mod = json->decimal_class;
+ method_id = i_try_convert;
+ } else if (rb_respond_to(json->decimal_class, i_new)) {
+ mod = json->decimal_class;
+ method_id = i_new;
+ } else if (RB_TYPE_P(json->decimal_class, T_CLASS)) {
+ VALUE name = rb_class_name(json->decimal_class);
+ const char *name_cstr = RSTRING_PTR(name);
+ const char *last_colon = strrchr(name_cstr, ':');
+ if (last_colon) {
+ const char *mod_path_end = last_colon - 1;
+ VALUE mod_path = rb_str_substr(name, 0, mod_path_end - name_cstr);
+ mod = rb_path_to_class(mod_path);
+
+ const char *method_name_beg = last_colon + 1;
+ long before_len = method_name_beg - name_cstr;
+ long len = RSTRING_LEN(name) - before_len;
+ VALUE method_name = rb_str_substr(name, before_len, len);
+ method_id = SYM2ID(rb_str_intern(method_name));
+ } else {
+ mod = rb_mKernel;
+ method_id = SYM2ID(rb_str_intern(name));
+ }
+ }
+
+ long len = p - json->memo;
+ fbuffer_clear(json->fbuffer);
+ fbuffer_append(json->fbuffer, json->memo, len);
+ fbuffer_append_char(json->fbuffer, '\0');
+
+ if (method_id) {
+ VALUE text = rb_str_new2(FBUFFER_PTR(json->fbuffer));
+ *result = rb_funcallv(mod, method_id, 1, &text);
+ } else {
+ *result = DBL2NUM(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
+ }
+
+ return p + 1;
+ } else {
+ return NULL;
+ }
}
+#line 1184 "parser.c"
enum {JSON_array_start = 1};
enum {JSON_array_first_final = 17};
enum {JSON_array_error = 0};
enum {JSON_array_en_main = 1};
-static const char MAYBE_UNUSED(_JSON_array_nfa_targs)[] = {
- 0, 0
-};
-
-static const char MAYBE_UNUSED(_JSON_array_nfa_offsets)[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0
-};
-
-static const char MAYBE_UNUSED(_JSON_array_nfa_push_actions)[] = {
- 0, 0
-};
-
-static const char MAYBE_UNUSED(_JSON_array_nfa_pop_trans)[] = {
- 0, 0
-};
-
#line 432 "parser.rl"
static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting)
{
- int cs = EVIL;
- VALUE array_class = json->array_class;
+ int cs = EVIL;
+ VALUE array_class = json->array_class;
- if (json->max_nesting && current_nesting > json->max_nesting) {
- rb_raise(eNestingError, "nesting of %d is too deep", current_nesting);
- }
- *result = NIL_P(array_class) ? rb_ary_new() : rb_class_new_instance(0, 0, array_class);
+ if (json->max_nesting && current_nesting > json->max_nesting) {
+ rb_raise(eNestingError, "nesting of %d is too deep", current_nesting);
+ }
+ *result = NIL_P(array_class) ? rb_ary_new() : rb_class_new_instance(0, 0, array_class);
+#line 1206 "parser.c"
{
- cs = (int)JSON_array_start;
+ cs = JSON_array_start;
}
- #line 445 "parser.rl"
-
+#line 445 "parser.rl"
+#line 1213 "parser.c"
{
- if ( p == pe )
+ if ( p == pe )
goto _test_eof;
- switch ( cs )
- {
- case 1:
- goto st_case_1;
- case 0:
- goto st_case_0;
- case 2:
- goto st_case_2;
- case 3:
- goto st_case_3;
- case 4:
- goto st_case_4;
- case 5:
- goto st_case_5;
- case 6:
- goto st_case_6;
- case 7:
- goto st_case_7;
- case 8:
- goto st_case_8;
- case 9:
- goto st_case_9;
- case 10:
- goto st_case_10;
- case 11:
- goto st_case_11;
- case 12:
- goto st_case_12;
- case 17:
- goto st_case_17;
- case 13:
- goto st_case_13;
- case 14:
- goto st_case_14;
- case 15:
- goto st_case_15;
- case 16:
- goto st_case_16;
- }
- goto st_out;
- st_case_1:
- if ( ( (*( p))) == 91 ) {
- goto st2;
- }
- {
- goto st0;
- }
- st_case_0:
- st0:
- cs = 0;
- goto _out;
- st2:
- p+= 1;
- if ( p == pe )
+ switch ( cs )
+ {
+case 1:
+ if ( (*p) == 91 )
+ goto st2;
+ goto st0;
+st0:
+cs = 0;
+ goto _out;
+st2:
+ if ( ++p == pe )
goto _test_eof2;
- st_case_2:
- switch( ( (*( p))) ) {
- case 13: {
- goto st2;
- }
- case 32: {
- goto st2;
- }
- case 34: {
- goto ctr2;
- }
- case 45: {
- goto ctr2;
- }
- case 47: {
- goto st13;
- }
- case 73: {
- goto ctr2;
- }
- case 78: {
- goto ctr2;
- }
- case 91: {
- goto ctr2;
- }
- case 93: {
- goto ctr4;
- }
- case 102: {
- goto ctr2;
- }
- case 110: {
- goto ctr2;
- }
- case 116: {
- goto ctr2;
- }
- case 123: {
- goto ctr2;
- }
- }
- if ( ( (*( p))) > 10 ) {
- if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
- goto ctr2;
- }
- } else if ( ( (*( p))) >= 9 ) {
- goto st2;
- }
- {
- goto st0;
- }
- ctr2:
- {
- #line 409 "parser.rl"
-
- VALUE v = Qnil;
- char *np = JSON_parse_value(json, p, pe, &v, current_nesting);
- if (np == NULL) {
- {p = p - 1; } {p+= 1; cs = 3; goto _out;}
- } else {
- if (NIL_P(json->array_class)) {
- rb_ary_push(*result, v);
- } else {
- rb_funcall(*result, i_leftshift, 1, v);
- }
- {p = (( np))-1;}
-
- }
- }
-
- goto st3;
- st3:
- p+= 1;
- if ( p == pe )
+case 2:
+ switch( (*p) ) {
+ case 13: goto st2;
+ case 32: goto st2;
+ case 34: goto tr2;
+ case 45: goto tr2;
+ case 47: goto st13;
+ case 73: goto tr2;
+ case 78: goto tr2;
+ case 91: goto tr2;
+ case 93: goto tr4;
+ case 102: goto tr2;
+ case 110: goto tr2;
+ case 116: goto tr2;
+ case 123: goto tr2;
+ }
+ if ( (*p) > 10 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2;
+ } else if ( (*p) >= 9 )
+ goto st2;
+ goto st0;
+tr2:
+#line 409 "parser.rl"
+ {
+ VALUE v = Qnil;
+ char *np = JSON_parse_value(json, p, pe, &v, current_nesting);
+ if (np == NULL) {
+ p--; {p++; cs = 3; goto _out;}
+ } else {
+ if (NIL_P(json->array_class)) {
+ rb_ary_push(*result, v);
+ } else {
+ rb_funcall(*result, i_leftshift, 1, v);
+ }
+ {p = (( np))-1;}
+ }
+ }
+ goto st3;
+st3:
+ if ( ++p == pe )
goto _test_eof3;
- st_case_3:
- switch( ( (*( p))) ) {
- case 13: {
- goto st3;
- }
- case 32: {
- goto st3;
- }
- case 44: {
- goto st4;
- }
- case 47: {
- goto st9;
- }
- case 93: {
- goto ctr4;
- }
- }
- if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) {
- goto st3;
- }
- {
- goto st0;
- }
- st4:
- p+= 1;
- if ( p == pe )
+case 3:
+#line 1272 "parser.c"
+ switch( (*p) ) {
+ case 13: goto st3;
+ case 32: goto st3;
+ case 44: goto st4;
+ case 47: goto st9;
+ case 93: goto tr4;
+ }
+ if ( 9 <= (*p) && (*p) <= 10 )
+ goto st3;
+ goto st0;
+st4:
+ if ( ++p == pe )
goto _test_eof4;
- st_case_4:
- switch( ( (*( p))) ) {
- case 13: {
- goto st4;
- }
- case 32: {
- goto st4;
- }
- case 34: {
- goto ctr2;
- }
- case 45: {
- goto ctr2;
- }
- case 47: {
- goto st5;
- }
- case 73: {
- goto ctr2;
- }
- case 78: {
- goto ctr2;
- }
- case 91: {
- goto ctr2;
- }
- case 102: {
- goto ctr2;
- }
- case 110: {
- goto ctr2;
- }
- case 116: {
- goto ctr2;
- }
- case 123: {
- goto ctr2;
- }
- }
- if ( ( (*( p))) > 10 ) {
- if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
- goto ctr2;
- }
- } else if ( ( (*( p))) >= 9 ) {
- goto st4;
- }
- {
- goto st0;
- }
- st5:
- p+= 1;
- if ( p == pe )
+case 4:
+ switch( (*p) ) {
+ case 13: goto st4;
+ case 32: goto st4;
+ case 34: goto tr2;
+ case 45: goto tr2;
+ case 47: goto st5;
+ case 73: goto tr2;
+ case 78: goto tr2;
+ case 91: goto tr2;
+ case 102: goto tr2;
+ case 110: goto tr2;
+ case 116: goto tr2;
+ case 123: goto tr2;
+ }
+ if ( (*p) > 10 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2;
+ } else if ( (*p) >= 9 )
+ goto st4;
+ goto st0;
+st5:
+ if ( ++p == pe )
goto _test_eof5;
- st_case_5:
- switch( ( (*( p))) ) {
- case 42: {
- goto st6;
- }
- case 47: {
- goto st8;
- }
- }
- {
- goto st0;
- }
- st6:
- p+= 1;
- if ( p == pe )
+case 5:
+ switch( (*p) ) {
+ case 42: goto st6;
+ case 47: goto st8;
+ }
+ goto st0;
+st6:
+ if ( ++p == pe )
goto _test_eof6;
- st_case_6:
- if ( ( (*( p))) == 42 ) {
- goto st7;
- }
- {
- goto st6;
- }
- st7:
- p+= 1;
- if ( p == pe )
+case 6:
+ if ( (*p) == 42 )
+ goto st7;
+ goto st6;
+st7:
+ if ( ++p == pe )
goto _test_eof7;
- st_case_7:
- switch( ( (*( p))) ) {
- case 42: {
- goto st7;
- }
- case 47: {
- goto st4;
- }
- }
- {
- goto st6;
- }
- st8:
- p+= 1;
- if ( p == pe )
+case 7:
+ switch( (*p) ) {
+ case 42: goto st7;
+ case 47: goto st4;
+ }
+ goto st6;
+st8:
+ if ( ++p == pe )
goto _test_eof8;
- st_case_8:
- if ( ( (*( p))) == 10 ) {
- goto st4;
- }
- {
- goto st8;
- }
- st9:
- p+= 1;
- if ( p == pe )
+case 8:
+ if ( (*p) == 10 )
+ goto st4;
+ goto st8;
+st9:
+ if ( ++p == pe )
goto _test_eof9;
- st_case_9:
- switch( ( (*( p))) ) {
- case 42: {
- goto st10;
- }
- case 47: {
- goto st12;
- }
- }
- {
- goto st0;
- }
- st10:
- p+= 1;
- if ( p == pe )
+case 9:
+ switch( (*p) ) {
+ case 42: goto st10;
+ case 47: goto st12;
+ }
+ goto st0;
+st10:
+ if ( ++p == pe )
goto _test_eof10;
- st_case_10:
- if ( ( (*( p))) == 42 ) {
- goto st11;
- }
- {
- goto st10;
- }
- st11:
- p+= 1;
- if ( p == pe )
+case 10:
+ if ( (*p) == 42 )
+ goto st11;
+ goto st10;
+st11:
+ if ( ++p == pe )
goto _test_eof11;
- st_case_11:
- switch( ( (*( p))) ) {
- case 42: {
- goto st11;
- }
- case 47: {
- goto st3;
- }
- }
- {
- goto st10;
- }
- st12:
- p+= 1;
- if ( p == pe )
+case 11:
+ switch( (*p) ) {
+ case 42: goto st11;
+ case 47: goto st3;
+ }
+ goto st10;
+st12:
+ if ( ++p == pe )
goto _test_eof12;
- st_case_12:
- if ( ( (*( p))) == 10 ) {
- goto st3;
- }
- {
- goto st12;
- }
- ctr4:
- {
- #line 424 "parser.rl"
- {p = p - 1; } {p+= 1; cs = 17; goto _out;} }
-
- goto st17;
- st17:
- p+= 1;
- if ( p == pe )
+case 12:
+ if ( (*p) == 10 )
+ goto st3;
+ goto st12;
+tr4:
+#line 424 "parser.rl"
+ { p--; {p++; cs = 17; goto _out;} }
+ goto st17;
+st17:
+ if ( ++p == pe )
goto _test_eof17;
- st_case_17:
- {
- goto st0;
- }
- st13:
- p+= 1;
- if ( p == pe )
+case 17:
+#line 1379 "parser.c"
+ goto st0;
+st13:
+ if ( ++p == pe )
goto _test_eof13;
- st_case_13:
- switch( ( (*( p))) ) {
- case 42: {
- goto st14;
- }
- case 47: {
- goto st16;
- }
- }
- {
- goto st0;
- }
- st14:
- p+= 1;
- if ( p == pe )
+case 13:
+ switch( (*p) ) {
+ case 42: goto st14;
+ case 47: goto st16;
+ }
+ goto st0;
+st14:
+ if ( ++p == pe )
goto _test_eof14;
- st_case_14:
- if ( ( (*( p))) == 42 ) {
- goto st15;
- }
- {
- goto st14;
- }
- st15:
- p+= 1;
- if ( p == pe )
+case 14:
+ if ( (*p) == 42 )
+ goto st15;
+ goto st14;
+st15:
+ if ( ++p == pe )
goto _test_eof15;
- st_case_15:
- switch( ( (*( p))) ) {
- case 42: {
- goto st15;
- }
- case 47: {
- goto st2;
- }
- }
- {
- goto st14;
- }
- st16:
- p+= 1;
- if ( p == pe )
+case 15:
+ switch( (*p) ) {
+ case 42: goto st15;
+ case 47: goto st2;
+ }
+ goto st14;
+st16:
+ if ( ++p == pe )
goto _test_eof16;
- st_case_16:
- if ( ( (*( p))) == 10 ) {
- goto st2;
- }
- {
- goto st16;
- }
- st_out:
- _test_eof2: cs = 2; goto _test_eof;
- _test_eof3: cs = 3; goto _test_eof;
- _test_eof4: cs = 4; goto _test_eof;
- _test_eof5: cs = 5; goto _test_eof;
- _test_eof6: cs = 6; goto _test_eof;
- _test_eof7: cs = 7; goto _test_eof;
- _test_eof8: cs = 8; goto _test_eof;
- _test_eof9: cs = 9; goto _test_eof;
- _test_eof10: cs = 10; goto _test_eof;
- _test_eof11: cs = 11; goto _test_eof;
- _test_eof12: cs = 12; goto _test_eof;
- _test_eof17: cs = 17; goto _test_eof;
- _test_eof13: cs = 13; goto _test_eof;
- _test_eof14: cs = 14; goto _test_eof;
- _test_eof15: cs = 15; goto _test_eof;
- _test_eof16: cs = 16; goto _test_eof;
-
- _test_eof: {}
- _out: {}
- }
-
- #line 446 "parser.rl"
-
-
- if(cs >= JSON_array_first_final) {
- return p + 1;
- } else {
- rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p);
- return NULL;
+case 16:
+ if ( (*p) == 10 )
+ goto st2;
+ goto st16;
}
+ _test_eof2: cs = 2; goto _test_eof;
+ _test_eof3: cs = 3; goto _test_eof;
+ _test_eof4: cs = 4; goto _test_eof;
+ _test_eof5: cs = 5; goto _test_eof;
+ _test_eof6: cs = 6; goto _test_eof;
+ _test_eof7: cs = 7; goto _test_eof;
+ _test_eof8: cs = 8; goto _test_eof;
+ _test_eof9: cs = 9; goto _test_eof;
+ _test_eof10: cs = 10; goto _test_eof;
+ _test_eof11: cs = 11; goto _test_eof;
+ _test_eof12: cs = 12; goto _test_eof;
+ _test_eof17: cs = 17; goto _test_eof;
+ _test_eof13: cs = 13; goto _test_eof;
+ _test_eof14: cs = 14; goto _test_eof;
+ _test_eof15: cs = 15; goto _test_eof;
+ _test_eof16: cs = 16; goto _test_eof;
+
+ _test_eof: {}
+ _out: {}
+ }
+
+#line 446 "parser.rl"
+
+ if(cs >= JSON_array_first_final) {
+ return p + 1;
+ } else {
+ rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p);
+ return NULL;
+ }
}
static const size_t MAX_STACK_BUFFER_SIZE = 128;
static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int symbolize)
{
- VALUE result = Qnil;
- size_t bufferSize = stringEnd - string;
- char *p = string, *pe = string, *unescape, *bufferStart, *buffer;
- int unescape_len;
- char buf[4];
+ VALUE result = Qnil;
+ size_t bufferSize = stringEnd - string;
+ char *p = string, *pe = string, *unescape, *bufferStart, *buffer;
+ int unescape_len;
+ char buf[4];
- if (bufferSize > MAX_STACK_BUFFER_SIZE) {
+ if (bufferSize > MAX_STACK_BUFFER_SIZE) {
# ifdef HAVE_RB_ENC_INTERNED_STR
- bufferStart = buffer = ALLOC_N(char, bufferSize ? bufferSize : 1);
+ bufferStart = buffer = ALLOC_N(char, bufferSize ? bufferSize : 1);
# else
- bufferStart = buffer = ALLOC_N(char, bufferSize);
+ bufferStart = buffer = ALLOC_N(char, bufferSize);
# endif
- } else {
+ } else {
# ifdef HAVE_RB_ENC_INTERNED_STR
- bufferStart = buffer = ALLOCA_N(char, bufferSize ? bufferSize : 1);
+ bufferStart = buffer = ALLOCA_N(char, bufferSize ? bufferSize : 1);
# else
- bufferStart = buffer = ALLOCA_N(char, bufferSize);
+ bufferStart = buffer = ALLOCA_N(char, bufferSize);
# endif
- }
-
- while (pe < stringEnd) {
- if (*pe == '\\') {
- unescape = (char *) "?";
- unescape_len = 1;
- if (pe > p) {
- MEMCPY(buffer, p, char, pe - p);
- buffer += pe - p;
- }
- switch (*++pe) {
- case 'n':
- unescape = (char *) "\n";
- break;
- case 'r':
- unescape = (char *) "\r";
- break;
- case 't':
- unescape = (char *) "\t";
- break;
- case '"':
- unescape = (char *) "\"";
- break;
- case '\\':
- unescape = (char *) "\\";
- break;
- case 'b':
- unescape = (char *) "\b";
- break;
- case 'f':
- unescape = (char *) "\f";
- break;
- case 'u':
- if (pe > stringEnd - 4) {
- if (bufferSize > MAX_STACK_BUFFER_SIZE) {
- free(bufferStart);
- }
- rb_enc_raise(
- EXC_ENCODING eParserError,
- "incomplete unicode character escape sequence at '%s'", p
- );
- } else {
- UTF32 ch = unescape_unicode((unsigned char *) ++pe);
- pe += 3;
- if (UNI_SUR_HIGH_START == (ch & 0xFC00)) {
- pe++;
- if (pe > stringEnd - 6) {
- if (bufferSize > MAX_STACK_BUFFER_SIZE) {
- free(bufferStart);
- }
- rb_enc_raise(
- EXC_ENCODING eParserError,
- "incomplete surrogate pair at '%s'", p
- );
- }
- if (pe[0] == '\\' && pe[1] == 'u') {
- UTF32 sur = unescape_unicode((unsigned char *) pe + 2);
- ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16)
- | (sur & 0x3FF));
- pe += 5;
- } else {
- unescape = (char *) "?";
- break;
- }
- }
- unescape_len = convert_UTF32_to_UTF8(buf, ch);
- unescape = buf;
- }
- break;
- default:
- p = pe;
- continue;
- }
- MEMCPY(buffer, unescape, char, unescape_len);
- buffer += unescape_len;
- p = ++pe;
- } else {
- pe++;
- }
- }
-
- if (pe > p) {
- MEMCPY(buffer, p, char, pe - p);
- buffer += pe - p;
- }
-
- # ifdef HAVE_RB_ENC_INTERNED_STR
- if (intern) {
- result = rb_enc_interned_str(bufferStart, (long)(buffer - bufferStart), rb_utf8_encoding());
- } else {
- result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart));
- }
- if (bufferSize > MAX_STACK_BUFFER_SIZE) {
- free(bufferStart);
- }
- # else
- result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart));
+ }
+
+ while (pe < stringEnd) {
+ if (*pe == '\\') {
+ unescape = (char *) "?";
+ unescape_len = 1;
+ if (pe > p) {
+ MEMCPY(buffer, p, char, pe - p);
+ buffer += pe - p;
+ }
+ switch (*++pe) {
+ case 'n':
+ unescape = (char *) "\n";
+ break;
+ case 'r':
+ unescape = (char *) "\r";
+ break;
+ case 't':
+ unescape = (char *) "\t";
+ break;
+ case '"':
+ unescape = (char *) "\"";
+ break;
+ case '\\':
+ unescape = (char *) "\\";
+ break;
+ case 'b':
+ unescape = (char *) "\b";
+ break;
+ case 'f':
+ unescape = (char *) "\f";
+ break;
+ case 'u':
+ if (pe > stringEnd - 4) {
+ if (bufferSize > MAX_STACK_BUFFER_SIZE) {
+ ruby_xfree(bufferStart);
+ }
+ rb_enc_raise(
+ EXC_ENCODING eParserError,
+ "incomplete unicode character escape sequence at '%s'", p
+ );
+ } else {
+ UTF32 ch = unescape_unicode((unsigned char *) ++pe);
+ pe += 3;
+ if (UNI_SUR_HIGH_START == (ch & 0xFC00)) {
+ pe++;
+ if (pe > stringEnd - 6) {
+ if (bufferSize > MAX_STACK_BUFFER_SIZE) {
+ ruby_xfree(bufferStart);
+ }
+ rb_enc_raise(
+ EXC_ENCODING eParserError,
+ "incomplete surrogate pair at '%s'", p
+ );
+ }
+ if (pe[0] == '\\' && pe[1] == 'u') {
+ UTF32 sur = unescape_unicode((unsigned char *) pe + 2);
+ ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16)
+ | (sur & 0x3FF));
+ pe += 5;
+ } else {
+ unescape = (char *) "?";
+ break;
+ }
+ }
+ unescape_len = convert_UTF32_to_UTF8(buf, ch);
+ unescape = buf;
+ }
+ break;
+ default:
+ p = pe;
+ continue;
+ }
+ MEMCPY(buffer, unescape, char, unescape_len);
+ buffer += unescape_len;
+ p = ++pe;
+ } else {
+ pe++;
+ }
+ }
+
+ if (pe > p) {
+ MEMCPY(buffer, p, char, pe - p);
+ buffer += pe - p;
+ }
- if (bufferSize > MAX_STACK_BUFFER_SIZE) {
- free(bufferStart);
- }
-
- if (intern) {
- # if STR_UMINUS_DEDUPE_FROZEN
- // Starting from MRI 2.8 it is preferable to freeze the string
- // before deduplication so that it can be interned directly
- // otherwise it would be duplicated first which is wasteful.
- result = rb_funcall(rb_str_freeze(result), i_uminus, 0);
- # elif STR_UMINUS_DEDUPE
- // MRI 2.5 and older do not deduplicate strings that are already
- // frozen.
- result = rb_funcall(result, i_uminus, 0);
- # else
- result = rb_str_freeze(result);
- # endif
- }
- # endif
+# ifdef HAVE_RB_ENC_INTERNED_STR
+ if (intern) {
+ result = rb_enc_interned_str(bufferStart, (long)(buffer - bufferStart), rb_utf8_encoding());
+ } else {
+ result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart));
+ }
+ if (bufferSize > MAX_STACK_BUFFER_SIZE) {
+ ruby_xfree(bufferStart);
+ }
+# else
+ result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart));
+
+ if (bufferSize > MAX_STACK_BUFFER_SIZE) {
+ ruby_xfree(bufferStart);
+ }
+
+ if (intern) {
+ # if STR_UMINUS_DEDUPE_FROZEN
+ // Starting from MRI 2.8 it is preferable to freeze the string
+ // before deduplication so that it can be interned directly
+ // otherwise it would be duplicated first which is wasteful.
+ result = rb_funcall(rb_str_freeze(result), i_uminus, 0);
+ # elif STR_UMINUS_DEDUPE
+ // MRI 2.5 and older do not deduplicate strings that are already
+ // frozen.
+ result = rb_funcall(result, i_uminus, 0);
+ # else
+ result = rb_str_freeze(result);
+ # endif
+ }
+# endif
- if (symbolize) {
- result = rb_str_intern(result);
- }
+ if (symbolize) {
+ result = rb_str_intern(result);
+ }
- return result;
+ return result;
}
+#line 1592 "parser.c"
enum {JSON_string_start = 1};
enum {JSON_string_first_final = 8};
enum {JSON_string_error = 0};
enum {JSON_string_en_main = 1};
-static const char MAYBE_UNUSED(_JSON_string_nfa_targs)[] = {
- 0, 0
-};
-
-static const char MAYBE_UNUSED(_JSON_string_nfa_offsets)[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0
-};
-static const char MAYBE_UNUSED(_JSON_string_nfa_push_actions)[] = {
- 0, 0
-};
-
-static const char MAYBE_UNUSED(_JSON_string_nfa_pop_trans)[] = {
- 0, 0
-};
-
-
-#line 612 "parser.rl"
+#line 620 "parser.rl"
static int
match_i(VALUE regexp, VALUE klass, VALUE memo)
{
- if (regexp == Qundef) return ST_STOP;
- if (RTEST(rb_funcall(klass, i_json_creatable_p, 0)) &&
- RTEST(rb_funcall(regexp, i_match, 1, rb_ary_entry(memo, 0)))) {
- rb_ary_push(memo, klass);
- return ST_STOP;
- }
- return ST_CONTINUE;
+ if (regexp == Qundef) return ST_STOP;
+ if (RTEST(rb_funcall(klass, i_json_creatable_p, 0)) &&
+ RTEST(rb_funcall(regexp, i_match, 1, rb_ary_entry(memo, 0)))) {
+ rb_ary_push(memo, klass);
+ return ST_STOP;
+ }
+ return ST_CONTINUE;
}
static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result)
{
- int cs = EVIL;
- VALUE match_string;
+ int cs = EVIL;
+ VALUE match_string;
+#line 1621 "parser.c"
{
- cs = (int)JSON_string_start;
+ cs = JSON_string_start;
}
- #line 632 "parser.rl"
-
- json->memo = p;
+#line 640 "parser.rl"
+ json->memo = p;
+#line 1629 "parser.c"
{
- if ( p == pe )
+ if ( p == pe )
goto _test_eof;
- switch ( cs )
- {
- case 1:
- goto st_case_1;
- case 0:
- goto st_case_0;
- case 2:
- goto st_case_2;
- case 8:
- goto st_case_8;
- case 3:
- goto st_case_3;
- case 4:
- goto st_case_4;
- case 5:
- goto st_case_5;
- case 6:
- goto st_case_6;
- case 7:
- goto st_case_7;
- }
- goto st_out;
- st_case_1:
- if ( ( (*( p))) == 34 ) {
- goto st2;
- }
- {
- goto st0;
- }
- st_case_0:
- st0:
- cs = 0;
- goto _out;
- st2:
- p+= 1;
- if ( p == pe )
+ switch ( cs )
+ {
+case 1:
+ if ( (*p) == 34 )
+ goto st2;
+ goto st0;
+st0:
+cs = 0;
+ goto _out;
+st2:
+ if ( ++p == pe )
goto _test_eof2;
- st_case_2:
- switch( ( (*( p))) ) {
- case 34: {
- goto ctr2;
- }
- case 92: {
- goto st3;
- }
- }
- if ( 0 <= (signed char)(*(p)) && (*(p)) <= 31 ) {
- goto st0;
- }
- {
- goto st2;
- }
- ctr2:
- {
- #line 599 "parser.rl"
-
- *result = json_string_unescape(json->memo + 1, p, json->parsing_name || json-> freeze, json->parsing_name && json->symbolize_names);
- if (NIL_P(*result)) {
- {p = p - 1; }
- {p+= 1; cs = 8; goto _out;}
- } else {
- {p = (( p + 1))-1;}
-
- }
- }
- {
- #line 609 "parser.rl"
- {p = p - 1; } {p+= 1; cs = 8; goto _out;} }
-
- goto st8;
- st8:
- p+= 1;
- if ( p == pe )
+case 2:
+ switch( (*p) ) {
+ case 34: goto tr2;
+ case 92: goto st3;
+ }
+ if ( 0 <= (signed char)(*(p)) && (*(p)) <= 31 )
+ goto st0;
+ goto st2;
+tr2:
+#line 607 "parser.rl"
+ {
+ *result = json_string_unescape(json->memo + 1, p, json->parsing_name || json-> freeze, json->parsing_name && json->symbolize_names);
+ if (NIL_P(*result)) {
+ p--;
+ {p++; cs = 8; goto _out;}
+ } else {
+ {p = (( p + 1))-1;}
+ }
+ }
+#line 617 "parser.rl"
+ { p--; {p++; cs = 8; goto _out;} }
+ goto st8;
+st8:
+ if ( ++p == pe )
goto _test_eof8;
- st_case_8:
- {
- goto st0;
- }
- st3:
- p+= 1;
- if ( p == pe )
+case 8:
+#line 1671 "parser.c"
+ goto st0;
+st3:
+ if ( ++p == pe )
goto _test_eof3;
- st_case_3:
- if ( ( (*( p))) == 117 ) {
- goto st4;
- }
- if ( 0 <= (signed char)(*(p)) && (*(p)) <= 31 ) {
- goto st0;
- }
- {
- goto st2;
- }
- st4:
- p+= 1;
- if ( p == pe )
+case 3:
+ if ( (*p) == 117 )
+ goto st4;
+ if ( 0 <= (signed char)(*(p)) && (*(p)) <= 31 )
+ goto st0;
+ goto st2;
+st4:
+ if ( ++p == pe )
goto _test_eof4;
- st_case_4:
- if ( ( (*( p))) < 65 ) {
- if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
- goto st5;
- }
- } else if ( ( (*( p))) > 70 ) {
- if ( 97 <= ( (*( p))) && ( (*( p))) <= 102 ) {
- goto st5;
- }
- } else {
+case 4:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
goto st5;
- }
- {
- goto st0;
- }
- st5:
- p+= 1;
- if ( p == pe )
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st5;
+ } else
+ goto st5;
+ goto st0;
+st5:
+ if ( ++p == pe )
goto _test_eof5;
- st_case_5:
- if ( ( (*( p))) < 65 ) {
- if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
- goto st6;
- }
- } else if ( ( (*( p))) > 70 ) {
- if ( 97 <= ( (*( p))) && ( (*( p))) <= 102 ) {
- goto st6;
- }
- } else {
+case 5:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st6;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
goto st6;
- }
- {
- goto st0;
- }
- st6:
- p+= 1;
- if ( p == pe )
+ } else
+ goto st6;
+ goto st0;
+st6:
+ if ( ++p == pe )
goto _test_eof6;
- st_case_6:
- if ( ( (*( p))) < 65 ) {
- if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
- goto st7;
- }
- } else if ( ( (*( p))) > 70 ) {
- if ( 97 <= ( (*( p))) && ( (*( p))) <= 102 ) {
- goto st7;
- }
- } else {
+case 6:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
goto st7;
- }
- {
- goto st0;
- }
- st7:
- p+= 1;
- if ( p == pe )
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st7;
+ } else
+ goto st7;
+ goto st0;
+st7:
+ if ( ++p == pe )
goto _test_eof7;
- st_case_7:
- if ( ( (*( p))) < 65 ) {
- if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
- goto st2;
- }
- } else if ( ( (*( p))) > 70 ) {
- if ( 97 <= ( (*( p))) && ( (*( p))) <= 102 ) {
- goto st2;
- }
- } else {
+case 7:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
goto st2;
- }
- {
- goto st0;
- }
- st_out:
- _test_eof2: cs = 2; goto _test_eof;
- _test_eof8: cs = 8; goto _test_eof;
- _test_eof3: cs = 3; goto _test_eof;
- _test_eof4: cs = 4; goto _test_eof;
- _test_eof5: cs = 5; goto _test_eof;
- _test_eof6: cs = 6; goto _test_eof;
- _test_eof7: cs = 7; goto _test_eof;
-
- _test_eof: {}
- _out: {}
- }
-
- #line 634 "parser.rl"
-
-
- if (json->create_additions && RTEST(match_string = json->match_string)) {
- VALUE klass;
- VALUE memo = rb_ary_new2(2);
- rb_ary_push(memo, *result);
- rb_hash_foreach(match_string, match_i, memo);
- klass = rb_ary_entry(memo, 1);
- if (RTEST(klass)) {
- *result = rb_funcall(klass, i_json_create, 1, *result);
- }
- }
-
- if (cs >= JSON_string_first_final) {
- return p + 1;
- } else {
- return NULL;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st2;
+ } else
+ goto st2;
+ goto st0;
}
+ _test_eof2: cs = 2; goto _test_eof;
+ _test_eof8: cs = 8; goto _test_eof;
+ _test_eof3: cs = 3; goto _test_eof;
+ _test_eof4: cs = 4; goto _test_eof;
+ _test_eof5: cs = 5; goto _test_eof;
+ _test_eof6: cs = 6; goto _test_eof;
+ _test_eof7: cs = 7; goto _test_eof;
+
+ _test_eof: {}
+ _out: {}
+ }
+
+#line 642 "parser.rl"
+
+ if (json->create_additions && RTEST(match_string = json->match_string)) {
+ VALUE klass;
+ VALUE memo = rb_ary_new2(2);
+ rb_ary_push(memo, *result);
+ rb_hash_foreach(match_string, match_i, memo);
+ klass = rb_ary_entry(memo, 1);
+ if (RTEST(klass)) {
+ *result = rb_funcall(klass, i_json_create, 1, *result);
+ }
+ }
+
+ if (cs >= JSON_string_first_final) {
+ return p + 1;
+ } else {
+ return NULL;
+ }
}
/*
-* Document-class: JSON::Ext::Parser
-*
-* This is the JSON parser implemented as a C extension. It can be configured
-* to be used by setting
-*
-* JSON.parser = JSON::Ext::Parser
-*
-* with the method parser= in JSON.
-*
-*/
+ * Document-class: JSON::Ext::Parser
+ *
+ * This is the JSON parser implemented as a C extension. It can be configured
+ * to be used by setting
+ *
+ * JSON.parser = JSON::Ext::Parser
+ *
+ * with the method parser= in JSON.
+ *
+ */
static VALUE convert_encoding(VALUE source)
{
- #ifdef HAVE_RUBY_ENCODING_H
- rb_encoding *enc = rb_enc_get(source);
- if (enc == rb_ascii8bit_encoding()) {
- if (OBJ_FROZEN(source)) {
- source = rb_str_dup(source);
- }
- FORCE_UTF8(source);
- } else {
- source = rb_str_conv_enc(source, rb_enc_get(source), rb_utf8_encoding());
- }
- #endif
- return source;
+#ifdef HAVE_RUBY_ENCODING_H
+ rb_encoding *enc = rb_enc_get(source);
+ if (enc == rb_ascii8bit_encoding()) {
+ if (OBJ_FROZEN(source)) {
+ source = rb_str_dup(source);
+ }
+ FORCE_UTF8(source);
+ } else {
+ source = rb_str_conv_enc(source, rb_enc_get(source), rb_utf8_encoding());
+ }
+#endif
+ return source;
}
/*
-* call-seq: new(source, opts => {})
-*
-* Creates a new JSON::Ext::Parser instance for the string _source_.
-*
-* Creates a new JSON::Ext::Parser instance for the string _source_.
-*
-* It will be configured by the _opts_ hash. _opts_ can have the following
-* keys:
-*
-* _opts_ can have the following keys:
-* * *max_nesting*: The maximum depth of nesting allowed in the parsed data
-* structures. Disable depth checking with :max_nesting => false|nil|0, it
-* defaults to 100.
-* * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in
-* defiance of RFC 4627 to be parsed by the Parser. This option defaults to
-* false.
-* * *symbolize_names*: If set to true, returns symbols for the names
-* (keys) in a JSON object. Otherwise strings are returned, which is
-* also the default. It's not possible to use this option in
-* conjunction with the *create_additions* option.
-* * *create_additions*: If set to false, the Parser doesn't create
-* additions even if a matching class and create_id was found. This option
-* defaults to false.
-* * *object_class*: Defaults to Hash
-* * *array_class*: Defaults to Array
-*/
+ * call-seq: new(source, opts => {})
+ *
+ * Creates a new JSON::Ext::Parser instance for the string _source_.
+ *
+ * It will be configured by the _opts_ hash. _opts_ can have the following
+ * keys:
+ *
+ * _opts_ can have the following keys:
+ * * *max_nesting*: The maximum depth of nesting allowed in the parsed data
+ * structures. Disable depth checking with :max_nesting => false|nil|0, it
+ * defaults to 100.
+ * * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in
+ * defiance of RFC 4627 to be parsed by the Parser. This option defaults to
+ * false.
+ * * *symbolize_names*: If set to true, returns symbols for the names
+ * (keys) in a JSON object. Otherwise strings are returned, which is
+ * also the default. It's not possible to use this option in
+ * conjunction with the *create_additions* option.
+ * * *create_additions*: If set to false, the Parser doesn't create
+ * additions even if a matching class and create_id was found. This option
+ * defaults to false.
+ * * *object_class*: Defaults to Hash
+ * * *array_class*: Defaults to Array
+ */
static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
{
- VALUE source, opts;
- GET_PARSER_INIT;
-
- if (json->Vsource) {
- rb_raise(rb_eTypeError, "already initialized instance");
- }
- #ifdef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH
- rb_scan_args(argc, argv, "1:", &source, &opts);
- #else
- rb_scan_args(argc, argv, "11", &source, &opts);
- #endif
- if (!NIL_P(opts)) {
- #ifndef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH
- opts = rb_convert_type(opts, T_HASH, "Hash", "to_hash");
- if (NIL_P(opts)) {
- rb_raise(rb_eArgError, "opts needs to be like a hash");
- } else {
- #endif
- VALUE tmp = ID2SYM(i_max_nesting);
- if (option_given_p(opts, tmp)) {
- VALUE max_nesting = rb_hash_aref(opts, tmp);
- if (RTEST(max_nesting)) {
- Check_Type(max_nesting, T_FIXNUM);
- json->max_nesting = FIX2INT(max_nesting);
- } else {
- json->max_nesting = 0;
- }
- } else {
- json->max_nesting = 100;
- }
- tmp = ID2SYM(i_allow_nan);
- if (option_given_p(opts, tmp)) {
- json->allow_nan = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
- } else {
- json->allow_nan = 0;
- }
- tmp = ID2SYM(i_symbolize_names);
- if (option_given_p(opts, tmp)) {
- json->symbolize_names = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
- } else {
- json->symbolize_names = 0;
- }
- tmp = ID2SYM(i_freeze);
- if (option_given_p(opts, tmp)) {
- json->freeze = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
- } else {
- json->freeze = 0;
- }
- tmp = ID2SYM(i_create_additions);
- if (option_given_p(opts, tmp)) {
- json->create_additions = RTEST(rb_hash_aref(opts, tmp));
- } else {
- json->create_additions = 0;
- }
- if (json->symbolize_names && json->create_additions) {
- rb_raise(rb_eArgError,
- "options :symbolize_names and :create_additions cannot be "
- " used in conjunction");
- }
- tmp = ID2SYM(i_create_id);
- if (option_given_p(opts, tmp)) {
- json->create_id = rb_hash_aref(opts, tmp);
- } else {
- json->create_id = rb_funcall(mJSON, i_create_id, 0);
- }
- tmp = ID2SYM(i_object_class);
- if (option_given_p(opts, tmp)) {
- json->object_class = rb_hash_aref(opts, tmp);
- } else {
- json->object_class = Qnil;
- }
- tmp = ID2SYM(i_array_class);
- if (option_given_p(opts, tmp)) {
- json->array_class = rb_hash_aref(opts, tmp);
- } else {
- json->array_class = Qnil;
- }
- tmp = ID2SYM(i_decimal_class);
- if (option_given_p(opts, tmp)) {
- json->decimal_class = rb_hash_aref(opts, tmp);
- } else {
- json->decimal_class = Qnil;
- }
- tmp = ID2SYM(i_match_string);
- if (option_given_p(opts, tmp)) {
- VALUE match_string = rb_hash_aref(opts, tmp);
- json->match_string = RTEST(match_string) ? match_string : Qnil;
- } else {
- json->match_string = Qnil;
- }
- #ifndef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH
- }
- #endif
+ VALUE source, opts;
+ GET_PARSER_INIT;
+
+ if (json->Vsource) {
+ rb_raise(rb_eTypeError, "already initialized instance");
+ }
+ rb_scan_args(argc, argv, "1:", &source, &opts);
+ if (!NIL_P(opts)) {
+ VALUE tmp = ID2SYM(i_max_nesting);
+ if (option_given_p(opts, tmp)) {
+ VALUE max_nesting = rb_hash_aref(opts, tmp);
+ if (RTEST(max_nesting)) {
+ Check_Type(max_nesting, T_FIXNUM);
+ json->max_nesting = FIX2INT(max_nesting);
+ } else {
+ json->max_nesting = 0;
+ }
+ } else {
+ json->max_nesting = 100;
+ }
+ tmp = ID2SYM(i_allow_nan);
+ if (option_given_p(opts, tmp)) {
+ json->allow_nan = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
+ } else {
+ json->allow_nan = 0;
+ }
+ tmp = ID2SYM(i_symbolize_names);
+ if (option_given_p(opts, tmp)) {
+ json->symbolize_names = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
+ } else {
+ json->symbolize_names = 0;
+ }
+ tmp = ID2SYM(i_freeze);
+ if (option_given_p(opts, tmp)) {
+ json->freeze = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
+ } else {
+ json->freeze = 0;
+ }
+ tmp = ID2SYM(i_create_additions);
+ if (option_given_p(opts, tmp)) {
+ json->create_additions = RTEST(rb_hash_aref(opts, tmp));
+ } else {
+ json->create_additions = 0;
+ }
+ if (json->symbolize_names && json->create_additions) {
+ rb_raise(rb_eArgError,
+ "options :symbolize_names and :create_additions cannot be "
+ " used in conjunction");
+ }
+ tmp = ID2SYM(i_create_id);
+ if (option_given_p(opts, tmp)) {
+ json->create_id = rb_hash_aref(opts, tmp);
+ } else {
+ json->create_id = rb_funcall(mJSON, i_create_id, 0);
+ }
+ tmp = ID2SYM(i_object_class);
+ if (option_given_p(opts, tmp)) {
+ json->object_class = rb_hash_aref(opts, tmp);
+ } else {
+ json->object_class = Qnil;
+ }
+ tmp = ID2SYM(i_array_class);
+ if (option_given_p(opts, tmp)) {
+ json->array_class = rb_hash_aref(opts, tmp);
+ } else {
+ json->array_class = Qnil;
+ }
+ tmp = ID2SYM(i_decimal_class);
+ if (option_given_p(opts, tmp)) {
+ json->decimal_class = rb_hash_aref(opts, tmp);
} else {
- json->max_nesting = 100;
- json->allow_nan = 0;
- json->create_additions = 0;
- json->create_id = rb_funcall(mJSON, i_create_id, 0);
- json->object_class = Qnil;
- json->array_class = Qnil;
- json->decimal_class = Qnil;
- }
- source = convert_encoding(StringValue(source));
- StringValue(source);
- json->len = RSTRING_LEN(source);
- json->source = RSTRING_PTR(source);;
- json->Vsource = source;
- return self;
+ json->decimal_class = Qnil;
+ }
+ tmp = ID2SYM(i_match_string);
+ if (option_given_p(opts, tmp)) {
+ VALUE match_string = rb_hash_aref(opts, tmp);
+ json->match_string = RTEST(match_string) ? match_string : Qnil;
+ } else {
+ json->match_string = Qnil;
+ }
+ } else {
+ json->max_nesting = 100;
+ json->allow_nan = 0;
+ json->create_additions = 0;
+ json->create_id = Qnil;
+ json->object_class = Qnil;
+ json->array_class = Qnil;
+ json->decimal_class = Qnil;
+ }
+ source = convert_encoding(StringValue(source));
+ StringValue(source);
+ json->len = RSTRING_LEN(source);
+ json->source = RSTRING_PTR(source);;
+ json->Vsource = source;
+ return self;
}
+#line 1920 "parser.c"
enum {JSON_start = 1};
enum {JSON_first_final = 10};
enum {JSON_error = 0};
enum {JSON_en_main = 1};
-static const char MAYBE_UNUSED(_JSON_nfa_targs)[] = {
- 0, 0
-};
-
-static const char MAYBE_UNUSED(_JSON_nfa_offsets)[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0
-};
-
-static const char MAYBE_UNUSED(_JSON_nfa_push_actions)[] = {
- 0, 0
-};
-
-static const char MAYBE_UNUSED(_JSON_nfa_pop_trans)[] = {
- 0, 0
-};
-
-#line 835 "parser.rl"
+#line 828 "parser.rl"
/*
-* call-seq: parse()
-*
-* Parses the current JSON text _source_ and returns the complete data
-* structure as a result.
-* It raises JSON::ParseError if fail to parse.
-*/
+ * call-seq: parse()
+ *
+ * Parses the current JSON text _source_ and returns the complete data
+ * structure as a result.
+ * It raises JSON::ParserError if fail to parse.
+ */
static VALUE cParser_parse(VALUE self)
{
- char *p, *pe;
- int cs = EVIL;
- VALUE result = Qnil;
- GET_PARSER;
+ char *p, *pe;
+ int cs = EVIL;
+ VALUE result = Qnil;
+ GET_PARSER;
+#line 1946 "parser.c"
{
- cs = (int)JSON_start;
+ cs = JSON_start;
}
- #line 851 "parser.rl"
-
- p = json->source;
- pe = p + json->len;
+#line 845 "parser.rl"
+ p = json->source;
+ pe = p + json->len;
+#line 1955 "parser.c"
{
- if ( p == pe )
+ if ( p == pe )
goto _test_eof;
- switch ( cs )
- {
- case 1:
- goto st_case_1;
- case 0:
- goto st_case_0;
- case 10:
- goto st_case_10;
- case 2:
- goto st_case_2;
- case 3:
- goto st_case_3;
- case 4:
- goto st_case_4;
- case 5:
- goto st_case_5;
- case 6:
- goto st_case_6;
- case 7:
- goto st_case_7;
- case 8:
- goto st_case_8;
- case 9:
- goto st_case_9;
- }
- goto st_out;
- st1:
- p+= 1;
- if ( p == pe )
+ switch ( cs )
+ {
+st1:
+ if ( ++p == pe )
goto _test_eof1;
- st_case_1:
- switch( ( (*( p))) ) {
- case 13: {
- goto st1;
- }
- case 32: {
- goto st1;
- }
- case 34: {
- goto ctr2;
- }
- case 45: {
- goto ctr2;
- }
- case 47: {
- goto st6;
- }
- case 73: {
- goto ctr2;
- }
- case 78: {
- goto ctr2;
- }
- case 91: {
- goto ctr2;
- }
- case 102: {
- goto ctr2;
- }
- case 110: {
- goto ctr2;
- }
- case 116: {
- goto ctr2;
- }
- case 123: {
- goto ctr2;
- }
- }
- if ( ( (*( p))) > 10 ) {
- if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
- goto ctr2;
- }
- } else if ( ( (*( p))) >= 9 ) {
- goto st1;
- }
- {
- goto st0;
- }
- st_case_0:
- st0:
- cs = 0;
- goto _out;
- ctr2:
- {
- #line 827 "parser.rl"
-
- char *np = JSON_parse_value(json, p, pe, &result, 0);
- if (np == NULL) { {p = p - 1; } {p+= 1; cs = 10; goto _out;} } else {p = (( np))-1;}
-
- }
-
- goto st10;
- st10:
- p+= 1;
- if ( p == pe )
+case 1:
+ switch( (*p) ) {
+ case 13: goto st1;
+ case 32: goto st1;
+ case 34: goto tr2;
+ case 45: goto tr2;
+ case 47: goto st6;
+ case 73: goto tr2;
+ case 78: goto tr2;
+ case 91: goto tr2;
+ case 102: goto tr2;
+ case 110: goto tr2;
+ case 116: goto tr2;
+ case 123: goto tr2;
+ }
+ if ( (*p) > 10 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2;
+ } else if ( (*p) >= 9 )
+ goto st1;
+ goto st0;
+st0:
+cs = 0;
+ goto _out;
+tr2:
+#line 820 "parser.rl"
+ {
+ char *np = JSON_parse_value(json, p, pe, &result, 0);
+ if (np == NULL) { p--; {p++; cs = 10; goto _out;} } else {p = (( np))-1;}
+ }
+ goto st10;
+st10:
+ if ( ++p == pe )
goto _test_eof10;
- st_case_10:
- switch( ( (*( p))) ) {
- case 13: {
- goto st10;
- }
- case 32: {
- goto st10;
- }
- case 47: {
- goto st2;
- }
- }
- if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) {
- goto st10;
- }
- {
- goto st0;
- }
- st2:
- p+= 1;
- if ( p == pe )
+case 10:
+#line 1999 "parser.c"
+ switch( (*p) ) {
+ case 13: goto st10;
+ case 32: goto st10;
+ case 47: goto st2;
+ }
+ if ( 9 <= (*p) && (*p) <= 10 )
+ goto st10;
+ goto st0;
+st2:
+ if ( ++p == pe )
goto _test_eof2;
- st_case_2:
- switch( ( (*( p))) ) {
- case 42: {
- goto st3;
- }
- case 47: {
- goto st5;
- }
- }
- {
- goto st0;
- }
- st3:
- p+= 1;
- if ( p == pe )
+case 2:
+ switch( (*p) ) {
+ case 42: goto st3;
+ case 47: goto st5;
+ }
+ goto st0;
+st3:
+ if ( ++p == pe )
goto _test_eof3;
- st_case_3:
- if ( ( (*( p))) == 42 ) {
- goto st4;
- }
- {
- goto st3;
- }
- st4:
- p+= 1;
- if ( p == pe )
+case 3:
+ if ( (*p) == 42 )
+ goto st4;
+ goto st3;
+st4:
+ if ( ++p == pe )
goto _test_eof4;
- st_case_4:
- switch( ( (*( p))) ) {
- case 42: {
- goto st4;
- }
- case 47: {
- goto st10;
- }
- }
- {
- goto st3;
- }
- st5:
- p+= 1;
- if ( p == pe )
+case 4:
+ switch( (*p) ) {
+ case 42: goto st4;
+ case 47: goto st10;
+ }
+ goto st3;
+st5:
+ if ( ++p == pe )
goto _test_eof5;
- st_case_5:
- if ( ( (*( p))) == 10 ) {
- goto st10;
- }
- {
- goto st5;
- }
- st6:
- p+= 1;
- if ( p == pe )
+case 5:
+ if ( (*p) == 10 )
+ goto st10;
+ goto st5;
+st6:
+ if ( ++p == pe )
goto _test_eof6;
- st_case_6:
- switch( ( (*( p))) ) {
- case 42: {
- goto st7;
- }
- case 47: {
- goto st9;
- }
- }
- {
- goto st0;
- }
- st7:
- p+= 1;
- if ( p == pe )
+case 6:
+ switch( (*p) ) {
+ case 42: goto st7;
+ case 47: goto st9;
+ }
+ goto st0;
+st7:
+ if ( ++p == pe )
goto _test_eof7;
- st_case_7:
- if ( ( (*( p))) == 42 ) {
- goto st8;
- }
- {
- goto st7;
- }
- st8:
- p+= 1;
- if ( p == pe )
+case 7:
+ if ( (*p) == 42 )
+ goto st8;
+ goto st7;
+st8:
+ if ( ++p == pe )
goto _test_eof8;
- st_case_8:
- switch( ( (*( p))) ) {
- case 42: {
- goto st8;
- }
- case 47: {
- goto st1;
- }
- }
- {
- goto st7;
- }
- st9:
- p+= 1;
- if ( p == pe )
+case 8:
+ switch( (*p) ) {
+ case 42: goto st8;
+ case 47: goto st1;
+ }
+ goto st7;
+st9:
+ if ( ++p == pe )
goto _test_eof9;
- st_case_9:
- if ( ( (*( p))) == 10 ) {
- goto st1;
- }
- {
- goto st9;
- }
- st_out:
- _test_eof1: cs = 1; goto _test_eof;
- _test_eof10: cs = 10; goto _test_eof;
- _test_eof2: cs = 2; goto _test_eof;
- _test_eof3: cs = 3; goto _test_eof;
- _test_eof4: cs = 4; goto _test_eof;
- _test_eof5: cs = 5; goto _test_eof;
- _test_eof6: cs = 6; goto _test_eof;
- _test_eof7: cs = 7; goto _test_eof;
- _test_eof8: cs = 8; goto _test_eof;
- _test_eof9: cs = 9; goto _test_eof;
-
- _test_eof: {}
- _out: {}
- }
-
- #line 854 "parser.rl"
-
-
- if (cs >= JSON_first_final && p == pe) {
- return result;
- } else {
- rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p);
- return Qnil;
+case 9:
+ if ( (*p) == 10 )
+ goto st1;
+ goto st9;
}
+ _test_eof1: cs = 1; goto _test_eof;
+ _test_eof10: cs = 10; goto _test_eof;
+ _test_eof2: cs = 2; goto _test_eof;
+ _test_eof3: cs = 3; goto _test_eof;
+ _test_eof4: cs = 4; goto _test_eof;
+ _test_eof5: cs = 5; goto _test_eof;
+ _test_eof6: cs = 6; goto _test_eof;
+ _test_eof7: cs = 7; goto _test_eof;
+ _test_eof8: cs = 8; goto _test_eof;
+ _test_eof9: cs = 9; goto _test_eof;
+
+ _test_eof: {}
+ _out: {}
+ }
+
+#line 848 "parser.rl"
+
+ if (cs >= JSON_first_final && p == pe) {
+ return result;
+ } else {
+ rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p);
+ return Qnil;
+ }
}
static void JSON_mark(void *ptr)
{
- JSON_Parser *json = ptr;
- rb_gc_mark_maybe(json->Vsource);
- rb_gc_mark_maybe(json->create_id);
- rb_gc_mark_maybe(json->object_class);
- rb_gc_mark_maybe(json->array_class);
- rb_gc_mark_maybe(json->decimal_class);
- rb_gc_mark_maybe(json->match_string);
+ JSON_Parser *json = ptr;
+ rb_gc_mark_maybe(json->Vsource);
+ rb_gc_mark_maybe(json->create_id);
+ rb_gc_mark_maybe(json->object_class);
+ rb_gc_mark_maybe(json->array_class);
+ rb_gc_mark_maybe(json->decimal_class);
+ rb_gc_mark_maybe(json->match_string);
}
static void JSON_free(void *ptr)
{
- JSON_Parser *json = ptr;
- fbuffer_free(json->fbuffer);
- ruby_xfree(json);
+ JSON_Parser *json = ptr;
+ fbuffer_free(json->fbuffer);
+ ruby_xfree(json);
}
static size_t JSON_memsize(const void *ptr)
{
- const JSON_Parser *json = ptr;
- return sizeof(*json) + FBUFFER_CAPA(json->fbuffer);
+ const JSON_Parser *json = ptr;
+ return sizeof(*json) + FBUFFER_CAPA(json->fbuffer);
}
#ifdef NEW_TYPEDDATA_WRAPPER
static const rb_data_type_t JSON_Parser_type = {
- "JSON/Parser",
- {JSON_mark, JSON_free, JSON_memsize,},
- #ifdef RUBY_TYPED_FREE_IMMEDIATELY
- 0, 0,
- RUBY_TYPED_FREE_IMMEDIATELY,
- #endif
+ "JSON/Parser",
+ {JSON_mark, JSON_free, JSON_memsize,},
+#ifdef RUBY_TYPED_FREE_IMMEDIATELY
+ 0, 0,
+ RUBY_TYPED_FREE_IMMEDIATELY,
+#endif
};
#endif
static VALUE cJSON_parser_s_allocate(VALUE klass)
{
- JSON_Parser *json;
- VALUE obj = TypedData_Make_Struct(klass, JSON_Parser, &JSON_Parser_type, json);
- json->fbuffer = fbuffer_alloc(0);
- return obj;
+ JSON_Parser *json;
+ VALUE obj = TypedData_Make_Struct(klass, JSON_Parser, &JSON_Parser_type, json);
+ json->fbuffer = fbuffer_alloc(0);
+ return obj;
}
/*
-* call-seq: source()
-*
-* Returns a copy of the current _source_ string, that was used to construct
-* this Parser.
-*/
+ * call-seq: source()
+ *
+ * Returns a copy of the current _source_ string, that was used to construct
+ * this Parser.
+ */
static VALUE cParser_source(VALUE self)
{
- GET_PARSER;
- return rb_str_dup(json->Vsource);
+ GET_PARSER;
+ return rb_str_dup(json->Vsource);
}
void Init_parser(void)
{
- #ifdef HAVE_RB_EXT_RACTOR_SAFE
- rb_ext_ractor_safe(true);
- #endif
-
- #undef rb_intern
- rb_require("json/common");
- mJSON = rb_define_module("JSON");
- mExt = rb_define_module_under(mJSON, "Ext");
- cParser = rb_define_class_under(mExt, "Parser", rb_cObject);
- eParserError = rb_path2class("JSON::ParserError");
- eNestingError = rb_path2class("JSON::NestingError");
- rb_gc_register_mark_object(eParserError);
- rb_gc_register_mark_object(eNestingError);
- rb_define_alloc_func(cParser, cJSON_parser_s_allocate);
- rb_define_method(cParser, "initialize", cParser_initialize, -1);
- rb_define_method(cParser, "parse", cParser_parse, 0);
- rb_define_method(cParser, "source", cParser_source, 0);
-
- CNaN = rb_const_get(mJSON, rb_intern("NaN"));
- rb_gc_register_mark_object(CNaN);
-
- CInfinity = rb_const_get(mJSON, rb_intern("Infinity"));
- rb_gc_register_mark_object(CInfinity);
-
- CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity"));
- rb_gc_register_mark_object(CMinusInfinity);
-
- i_json_creatable_p = rb_intern("json_creatable?");
- i_json_create = rb_intern("json_create");
- i_create_id = rb_intern("create_id");
- i_create_additions = rb_intern("create_additions");
- i_chr = rb_intern("chr");
- i_max_nesting = rb_intern("max_nesting");
- i_allow_nan = rb_intern("allow_nan");
- i_symbolize_names = rb_intern("symbolize_names");
- i_object_class = rb_intern("object_class");
- i_array_class = rb_intern("array_class");
- i_decimal_class = rb_intern("decimal_class");
- i_match = rb_intern("match");
- i_match_string = rb_intern("match_string");
- i_key_p = rb_intern("key?");
- i_deep_const_get = rb_intern("deep_const_get");
- i_aset = rb_intern("[]=");
- i_aref = rb_intern("[]");
- i_leftshift = rb_intern("<<");
- i_new = rb_intern("new");
- i_try_convert = rb_intern("try_convert");
- i_freeze = rb_intern("freeze");
- i_uminus = rb_intern("-@");
+#ifdef HAVE_RB_EXT_RACTOR_SAFE
+ rb_ext_ractor_safe(true);
+#endif
+
+#undef rb_intern
+ rb_require("json/common");
+ mJSON = rb_define_module("JSON");
+ mExt = rb_define_module_under(mJSON, "Ext");
+ cParser = rb_define_class_under(mExt, "Parser", rb_cObject);
+ eParserError = rb_path2class("JSON::ParserError");
+ eNestingError = rb_path2class("JSON::NestingError");
+ rb_gc_register_mark_object(eParserError);
+ rb_gc_register_mark_object(eNestingError);
+ rb_define_alloc_func(cParser, cJSON_parser_s_allocate);
+ rb_define_method(cParser, "initialize", cParser_initialize, -1);
+ rb_define_method(cParser, "parse", cParser_parse, 0);
+ rb_define_method(cParser, "source", cParser_source, 0);
+
+ CNaN = rb_const_get(mJSON, rb_intern("NaN"));
+ rb_gc_register_mark_object(CNaN);
+
+ CInfinity = rb_const_get(mJSON, rb_intern("Infinity"));
+ rb_gc_register_mark_object(CInfinity);
+
+ CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity"));
+ rb_gc_register_mark_object(CMinusInfinity);
+
+ i_json_creatable_p = rb_intern("json_creatable?");
+ i_json_create = rb_intern("json_create");
+ i_create_id = rb_intern("create_id");
+ i_create_additions = rb_intern("create_additions");
+ i_chr = rb_intern("chr");
+ i_max_nesting = rb_intern("max_nesting");
+ i_allow_nan = rb_intern("allow_nan");
+ i_symbolize_names = rb_intern("symbolize_names");
+ i_object_class = rb_intern("object_class");
+ i_array_class = rb_intern("array_class");
+ i_decimal_class = rb_intern("decimal_class");
+ i_match = rb_intern("match");
+ i_match_string = rb_intern("match_string");
+ i_key_p = rb_intern("key?");
+ i_deep_const_get = rb_intern("deep_const_get");
+ i_aset = rb_intern("[]=");
+ i_aref = rb_intern("[]");
+ i_leftshift = rb_intern("<<");
+ i_new = rb_intern("new");
+ i_try_convert = rb_intern("try_convert");
+ i_freeze = rb_intern("freeze");
+ i_uminus = rb_intern("-@");
}
/*
-* Local variables:
-* mode: c
-* c-file-style: ruby
-* indent-tabs-mode: nil
-* End:
-*/
+ * Local variables:
+ * mode: c
+ * c-file-style: ruby
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/json/parser/parser.rl b/ext/json/parser/parser.rl
index 2dbdc7ef24..af190e7500 100644
--- a/ext/json/parser/parser.rl
+++ b/ext/json/parser/parser.rl
@@ -229,7 +229,7 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu
if (json->allow_nan) {
*result = CInfinity;
} else {
- rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 8);
+ rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 7);
}
}
action parse_string {
@@ -508,7 +508,7 @@ static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int
case 'u':
if (pe > stringEnd - 4) {
if (bufferSize > MAX_STACK_BUFFER_SIZE) {
- free(bufferStart);
+ ruby_xfree(bufferStart);
}
rb_enc_raise(
EXC_ENCODING eParserError,
@@ -521,7 +521,7 @@ static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int
pe++;
if (pe > stringEnd - 6) {
if (bufferSize > MAX_STACK_BUFFER_SIZE) {
- free(bufferStart);
+ ruby_xfree(bufferStart);
}
rb_enc_raise(
EXC_ENCODING eParserError,
@@ -566,13 +566,13 @@ static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int
result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart));
}
if (bufferSize > MAX_STACK_BUFFER_SIZE) {
- free(bufferStart);
+ ruby_xfree(bufferStart);
}
# else
result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart));
if (bufferSize > MAX_STACK_BUFFER_SIZE) {
- free(bufferStart);
+ ruby_xfree(bufferStart);
}
if (intern) {
@@ -691,8 +691,6 @@ static VALUE convert_encoding(VALUE source)
*
* Creates a new JSON::Ext::Parser instance for the string _source_.
*
- * Creates a new JSON::Ext::Parser instance for the string _source_.
- *
* It will be configured by the _opts_ hash. _opts_ can have the following
* keys:
*
@@ -721,98 +719,85 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
if (json->Vsource) {
rb_raise(rb_eTypeError, "already initialized instance");
}
-#ifdef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH
rb_scan_args(argc, argv, "1:", &source, &opts);
-#else
- rb_scan_args(argc, argv, "11", &source, &opts);
-#endif
if (!NIL_P(opts)) {
-#ifndef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH
- opts = rb_convert_type(opts, T_HASH, "Hash", "to_hash");
- if (NIL_P(opts)) {
- rb_raise(rb_eArgError, "opts needs to be like a hash");
- } else {
-#endif
- VALUE tmp = ID2SYM(i_max_nesting);
- if (option_given_p(opts, tmp)) {
- VALUE max_nesting = rb_hash_aref(opts, tmp);
- if (RTEST(max_nesting)) {
- Check_Type(max_nesting, T_FIXNUM);
- json->max_nesting = FIX2INT(max_nesting);
- } else {
- json->max_nesting = 0;
- }
- } else {
- json->max_nesting = 100;
- }
- tmp = ID2SYM(i_allow_nan);
- if (option_given_p(opts, tmp)) {
- json->allow_nan = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
- } else {
- json->allow_nan = 0;
- }
- tmp = ID2SYM(i_symbolize_names);
- if (option_given_p(opts, tmp)) {
- json->symbolize_names = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
- } else {
- json->symbolize_names = 0;
- }
- tmp = ID2SYM(i_freeze);
- if (option_given_p(opts, tmp)) {
- json->freeze = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
- } else {
- json->freeze = 0;
- }
- tmp = ID2SYM(i_create_additions);
- if (option_given_p(opts, tmp)) {
- json->create_additions = RTEST(rb_hash_aref(opts, tmp));
- } else {
- json->create_additions = 0;
- }
- if (json->symbolize_names && json->create_additions) {
- rb_raise(rb_eArgError,
- "options :symbolize_names and :create_additions cannot be "
- " used in conjunction");
- }
- tmp = ID2SYM(i_create_id);
- if (option_given_p(opts, tmp)) {
- json->create_id = rb_hash_aref(opts, tmp);
- } else {
- json->create_id = rb_funcall(mJSON, i_create_id, 0);
- }
- tmp = ID2SYM(i_object_class);
- if (option_given_p(opts, tmp)) {
- json->object_class = rb_hash_aref(opts, tmp);
- } else {
- json->object_class = Qnil;
- }
- tmp = ID2SYM(i_array_class);
- if (option_given_p(opts, tmp)) {
- json->array_class = rb_hash_aref(opts, tmp);
- } else {
- json->array_class = Qnil;
- }
- tmp = ID2SYM(i_decimal_class);
- if (option_given_p(opts, tmp)) {
- json->decimal_class = rb_hash_aref(opts, tmp);
- } else {
- json->decimal_class = Qnil;
- }
- tmp = ID2SYM(i_match_string);
- if (option_given_p(opts, tmp)) {
- VALUE match_string = rb_hash_aref(opts, tmp);
- json->match_string = RTEST(match_string) ? match_string : Qnil;
- } else {
- json->match_string = Qnil;
- }
-#ifndef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH
- }
-#endif
+ VALUE tmp = ID2SYM(i_max_nesting);
+ if (option_given_p(opts, tmp)) {
+ VALUE max_nesting = rb_hash_aref(opts, tmp);
+ if (RTEST(max_nesting)) {
+ Check_Type(max_nesting, T_FIXNUM);
+ json->max_nesting = FIX2INT(max_nesting);
+ } else {
+ json->max_nesting = 0;
+ }
+ } else {
+ json->max_nesting = 100;
+ }
+ tmp = ID2SYM(i_allow_nan);
+ if (option_given_p(opts, tmp)) {
+ json->allow_nan = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
+ } else {
+ json->allow_nan = 0;
+ }
+ tmp = ID2SYM(i_symbolize_names);
+ if (option_given_p(opts, tmp)) {
+ json->symbolize_names = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
+ } else {
+ json->symbolize_names = 0;
+ }
+ tmp = ID2SYM(i_freeze);
+ if (option_given_p(opts, tmp)) {
+ json->freeze = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
+ } else {
+ json->freeze = 0;
+ }
+ tmp = ID2SYM(i_create_additions);
+ if (option_given_p(opts, tmp)) {
+ json->create_additions = RTEST(rb_hash_aref(opts, tmp));
+ } else {
+ json->create_additions = 0;
+ }
+ if (json->symbolize_names && json->create_additions) {
+ rb_raise(rb_eArgError,
+ "options :symbolize_names and :create_additions cannot be "
+ " used in conjunction");
+ }
+ tmp = ID2SYM(i_create_id);
+ if (option_given_p(opts, tmp)) {
+ json->create_id = rb_hash_aref(opts, tmp);
+ } else {
+ json->create_id = rb_funcall(mJSON, i_create_id, 0);
+ }
+ tmp = ID2SYM(i_object_class);
+ if (option_given_p(opts, tmp)) {
+ json->object_class = rb_hash_aref(opts, tmp);
+ } else {
+ json->object_class = Qnil;
+ }
+ tmp = ID2SYM(i_array_class);
+ if (option_given_p(opts, tmp)) {
+ json->array_class = rb_hash_aref(opts, tmp);
+ } else {
+ json->array_class = Qnil;
+ }
+ tmp = ID2SYM(i_decimal_class);
+ if (option_given_p(opts, tmp)) {
+ json->decimal_class = rb_hash_aref(opts, tmp);
+ } else {
+ json->decimal_class = Qnil;
+ }
+ tmp = ID2SYM(i_match_string);
+ if (option_given_p(opts, tmp)) {
+ VALUE match_string = rb_hash_aref(opts, tmp);
+ json->match_string = RTEST(match_string) ? match_string : Qnil;
+ } else {
+ json->match_string = Qnil;
+ }
} else {
json->max_nesting = 100;
json->allow_nan = 0;
json->create_additions = 0;
- json->create_id = rb_funcall(mJSON, i_create_id, 0);
+ json->create_id = Qnil;
json->object_class = Qnil;
json->array_class = Qnil;
json->decimal_class = Qnil;
@@ -847,7 +832,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
*
* Parses the current JSON text _source_ and returns the complete data
* structure as a result.
- * It raises JSON::ParseError if fail to parse.
+ * It raises JSON::ParserError if fail to parse.
*/
static VALUE cParser_parse(VALUE self)
{
diff --git a/ext/monitor/depend b/ext/monitor/depend
index 38e21b3f66..bc7ead0d32 100644
--- a/ext/monitor/depend
+++ b/ext/monitor/depend
@@ -146,6 +146,7 @@ monitor.o: $(hdrdir)/ruby/internal/special_consts.h
monitor.o: $(hdrdir)/ruby/internal/static_assert.h
monitor.o: $(hdrdir)/ruby/internal/stdalign.h
monitor.o: $(hdrdir)/ruby/internal/stdbool.h
+monitor.o: $(hdrdir)/ruby/internal/stdckdint.h
monitor.o: $(hdrdir)/ruby/internal/symbol.h
monitor.o: $(hdrdir)/ruby/internal/value.h
monitor.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/nkf/depend b/ext/nkf/depend
deleted file mode 100644
index 98afc5f201..0000000000
--- a/ext/nkf/depend
+++ /dev/null
@@ -1,181 +0,0 @@
-# BSD make needs "nkf.o: nkf.c" dependency BEFORE "nkf.o: nkf-utf8/nkf.c".
-# It seems BSD make searches the target for implicit rule in dependencies at first.
-nkf.o: nkf.c
-
-# AUTOGENERATED DEPENDENCIES START
-nkf.o: $(RUBY_EXTCONF_H)
-nkf.o: $(arch_hdrdir)/ruby/config.h
-nkf.o: $(hdrdir)/ruby/assert.h
-nkf.o: $(hdrdir)/ruby/backward.h
-nkf.o: $(hdrdir)/ruby/backward/2/assume.h
-nkf.o: $(hdrdir)/ruby/backward/2/attributes.h
-nkf.o: $(hdrdir)/ruby/backward/2/bool.h
-nkf.o: $(hdrdir)/ruby/backward/2/inttypes.h
-nkf.o: $(hdrdir)/ruby/backward/2/limits.h
-nkf.o: $(hdrdir)/ruby/backward/2/long_long.h
-nkf.o: $(hdrdir)/ruby/backward/2/stdalign.h
-nkf.o: $(hdrdir)/ruby/backward/2/stdarg.h
-nkf.o: $(hdrdir)/ruby/defines.h
-nkf.o: $(hdrdir)/ruby/encoding.h
-nkf.o: $(hdrdir)/ruby/intern.h
-nkf.o: $(hdrdir)/ruby/internal/abi.h
-nkf.o: $(hdrdir)/ruby/internal/anyargs.h
-nkf.o: $(hdrdir)/ruby/internal/arithmetic.h
-nkf.o: $(hdrdir)/ruby/internal/arithmetic/char.h
-nkf.o: $(hdrdir)/ruby/internal/arithmetic/double.h
-nkf.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h
-nkf.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h
-nkf.o: $(hdrdir)/ruby/internal/arithmetic/int.h
-nkf.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h
-nkf.o: $(hdrdir)/ruby/internal/arithmetic/long.h
-nkf.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h
-nkf.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h
-nkf.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h
-nkf.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h
-nkf.o: $(hdrdir)/ruby/internal/arithmetic/short.h
-nkf.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h
-nkf.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h
-nkf.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h
-nkf.o: $(hdrdir)/ruby/internal/assume.h
-nkf.o: $(hdrdir)/ruby/internal/attr/alloc_size.h
-nkf.o: $(hdrdir)/ruby/internal/attr/artificial.h
-nkf.o: $(hdrdir)/ruby/internal/attr/cold.h
-nkf.o: $(hdrdir)/ruby/internal/attr/const.h
-nkf.o: $(hdrdir)/ruby/internal/attr/constexpr.h
-nkf.o: $(hdrdir)/ruby/internal/attr/deprecated.h
-nkf.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h
-nkf.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h
-nkf.o: $(hdrdir)/ruby/internal/attr/error.h
-nkf.o: $(hdrdir)/ruby/internal/attr/flag_enum.h
-nkf.o: $(hdrdir)/ruby/internal/attr/forceinline.h
-nkf.o: $(hdrdir)/ruby/internal/attr/format.h
-nkf.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h
-nkf.o: $(hdrdir)/ruby/internal/attr/noalias.h
-nkf.o: $(hdrdir)/ruby/internal/attr/nodiscard.h
-nkf.o: $(hdrdir)/ruby/internal/attr/noexcept.h
-nkf.o: $(hdrdir)/ruby/internal/attr/noinline.h
-nkf.o: $(hdrdir)/ruby/internal/attr/nonnull.h
-nkf.o: $(hdrdir)/ruby/internal/attr/noreturn.h
-nkf.o: $(hdrdir)/ruby/internal/attr/packed_struct.h
-nkf.o: $(hdrdir)/ruby/internal/attr/pure.h
-nkf.o: $(hdrdir)/ruby/internal/attr/restrict.h
-nkf.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h
-nkf.o: $(hdrdir)/ruby/internal/attr/warning.h
-nkf.o: $(hdrdir)/ruby/internal/attr/weakref.h
-nkf.o: $(hdrdir)/ruby/internal/cast.h
-nkf.o: $(hdrdir)/ruby/internal/compiler_is.h
-nkf.o: $(hdrdir)/ruby/internal/compiler_is/apple.h
-nkf.o: $(hdrdir)/ruby/internal/compiler_is/clang.h
-nkf.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h
-nkf.o: $(hdrdir)/ruby/internal/compiler_is/intel.h
-nkf.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h
-nkf.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h
-nkf.o: $(hdrdir)/ruby/internal/compiler_since.h
-nkf.o: $(hdrdir)/ruby/internal/config.h
-nkf.o: $(hdrdir)/ruby/internal/constant_p.h
-nkf.o: $(hdrdir)/ruby/internal/core.h
-nkf.o: $(hdrdir)/ruby/internal/core/rarray.h
-nkf.o: $(hdrdir)/ruby/internal/core/rbasic.h
-nkf.o: $(hdrdir)/ruby/internal/core/rbignum.h
-nkf.o: $(hdrdir)/ruby/internal/core/rclass.h
-nkf.o: $(hdrdir)/ruby/internal/core/rdata.h
-nkf.o: $(hdrdir)/ruby/internal/core/rfile.h
-nkf.o: $(hdrdir)/ruby/internal/core/rhash.h
-nkf.o: $(hdrdir)/ruby/internal/core/robject.h
-nkf.o: $(hdrdir)/ruby/internal/core/rregexp.h
-nkf.o: $(hdrdir)/ruby/internal/core/rstring.h
-nkf.o: $(hdrdir)/ruby/internal/core/rstruct.h
-nkf.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
-nkf.o: $(hdrdir)/ruby/internal/ctype.h
-nkf.o: $(hdrdir)/ruby/internal/dllexport.h
-nkf.o: $(hdrdir)/ruby/internal/dosish.h
-nkf.o: $(hdrdir)/ruby/internal/encoding/coderange.h
-nkf.o: $(hdrdir)/ruby/internal/encoding/ctype.h
-nkf.o: $(hdrdir)/ruby/internal/encoding/encoding.h
-nkf.o: $(hdrdir)/ruby/internal/encoding/pathname.h
-nkf.o: $(hdrdir)/ruby/internal/encoding/re.h
-nkf.o: $(hdrdir)/ruby/internal/encoding/sprintf.h
-nkf.o: $(hdrdir)/ruby/internal/encoding/string.h
-nkf.o: $(hdrdir)/ruby/internal/encoding/symbol.h
-nkf.o: $(hdrdir)/ruby/internal/encoding/transcode.h
-nkf.o: $(hdrdir)/ruby/internal/error.h
-nkf.o: $(hdrdir)/ruby/internal/eval.h
-nkf.o: $(hdrdir)/ruby/internal/event.h
-nkf.o: $(hdrdir)/ruby/internal/fl_type.h
-nkf.o: $(hdrdir)/ruby/internal/gc.h
-nkf.o: $(hdrdir)/ruby/internal/glob.h
-nkf.o: $(hdrdir)/ruby/internal/globals.h
-nkf.o: $(hdrdir)/ruby/internal/has/attribute.h
-nkf.o: $(hdrdir)/ruby/internal/has/builtin.h
-nkf.o: $(hdrdir)/ruby/internal/has/c_attribute.h
-nkf.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h
-nkf.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h
-nkf.o: $(hdrdir)/ruby/internal/has/extension.h
-nkf.o: $(hdrdir)/ruby/internal/has/feature.h
-nkf.o: $(hdrdir)/ruby/internal/has/warning.h
-nkf.o: $(hdrdir)/ruby/internal/intern/array.h
-nkf.o: $(hdrdir)/ruby/internal/intern/bignum.h
-nkf.o: $(hdrdir)/ruby/internal/intern/class.h
-nkf.o: $(hdrdir)/ruby/internal/intern/compar.h
-nkf.o: $(hdrdir)/ruby/internal/intern/complex.h
-nkf.o: $(hdrdir)/ruby/internal/intern/cont.h
-nkf.o: $(hdrdir)/ruby/internal/intern/dir.h
-nkf.o: $(hdrdir)/ruby/internal/intern/enum.h
-nkf.o: $(hdrdir)/ruby/internal/intern/enumerator.h
-nkf.o: $(hdrdir)/ruby/internal/intern/error.h
-nkf.o: $(hdrdir)/ruby/internal/intern/eval.h
-nkf.o: $(hdrdir)/ruby/internal/intern/file.h
-nkf.o: $(hdrdir)/ruby/internal/intern/hash.h
-nkf.o: $(hdrdir)/ruby/internal/intern/io.h
-nkf.o: $(hdrdir)/ruby/internal/intern/load.h
-nkf.o: $(hdrdir)/ruby/internal/intern/marshal.h
-nkf.o: $(hdrdir)/ruby/internal/intern/numeric.h
-nkf.o: $(hdrdir)/ruby/internal/intern/object.h
-nkf.o: $(hdrdir)/ruby/internal/intern/parse.h
-nkf.o: $(hdrdir)/ruby/internal/intern/proc.h
-nkf.o: $(hdrdir)/ruby/internal/intern/process.h
-nkf.o: $(hdrdir)/ruby/internal/intern/random.h
-nkf.o: $(hdrdir)/ruby/internal/intern/range.h
-nkf.o: $(hdrdir)/ruby/internal/intern/rational.h
-nkf.o: $(hdrdir)/ruby/internal/intern/re.h
-nkf.o: $(hdrdir)/ruby/internal/intern/ruby.h
-nkf.o: $(hdrdir)/ruby/internal/intern/select.h
-nkf.o: $(hdrdir)/ruby/internal/intern/select/largesize.h
-nkf.o: $(hdrdir)/ruby/internal/intern/signal.h
-nkf.o: $(hdrdir)/ruby/internal/intern/sprintf.h
-nkf.o: $(hdrdir)/ruby/internal/intern/string.h
-nkf.o: $(hdrdir)/ruby/internal/intern/struct.h
-nkf.o: $(hdrdir)/ruby/internal/intern/thread.h
-nkf.o: $(hdrdir)/ruby/internal/intern/time.h
-nkf.o: $(hdrdir)/ruby/internal/intern/variable.h
-nkf.o: $(hdrdir)/ruby/internal/intern/vm.h
-nkf.o: $(hdrdir)/ruby/internal/interpreter.h
-nkf.o: $(hdrdir)/ruby/internal/iterator.h
-nkf.o: $(hdrdir)/ruby/internal/memory.h
-nkf.o: $(hdrdir)/ruby/internal/method.h
-nkf.o: $(hdrdir)/ruby/internal/module.h
-nkf.o: $(hdrdir)/ruby/internal/newobj.h
-nkf.o: $(hdrdir)/ruby/internal/scan_args.h
-nkf.o: $(hdrdir)/ruby/internal/special_consts.h
-nkf.o: $(hdrdir)/ruby/internal/static_assert.h
-nkf.o: $(hdrdir)/ruby/internal/stdalign.h
-nkf.o: $(hdrdir)/ruby/internal/stdbool.h
-nkf.o: $(hdrdir)/ruby/internal/symbol.h
-nkf.o: $(hdrdir)/ruby/internal/value.h
-nkf.o: $(hdrdir)/ruby/internal/value_type.h
-nkf.o: $(hdrdir)/ruby/internal/variable.h
-nkf.o: $(hdrdir)/ruby/internal/warning_push.h
-nkf.o: $(hdrdir)/ruby/internal/xmalloc.h
-nkf.o: $(hdrdir)/ruby/missing.h
-nkf.o: $(hdrdir)/ruby/onigmo.h
-nkf.o: $(hdrdir)/ruby/oniguruma.h
-nkf.o: $(hdrdir)/ruby/ruby.h
-nkf.o: $(hdrdir)/ruby/st.h
-nkf.o: $(hdrdir)/ruby/subst.h
-nkf.o: nkf-utf8/config.h
-nkf.o: nkf-utf8/nkf.c
-nkf.o: nkf-utf8/nkf.h
-nkf.o: nkf-utf8/utf8tbl.c
-nkf.o: nkf-utf8/utf8tbl.h
-nkf.o: nkf.c
-# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/nkf/extconf.rb b/ext/nkf/extconf.rb
deleted file mode 100644
index f41f6b11dc..0000000000
--- a/ext/nkf/extconf.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-# frozen_string_literal: false
-require 'mkmf'
-create_makefile('nkf')
diff --git a/ext/nkf/lib/kconv.rb b/ext/nkf/lib/kconv.rb
deleted file mode 100644
index f52b755288..0000000000
--- a/ext/nkf/lib/kconv.rb
+++ /dev/null
@@ -1,283 +0,0 @@
-# frozen_string_literal: false
-#
-# kconv.rb - Kanji Converter.
-#
-# $Id$
-#
-# ----
-#
-# kconv.rb implements the Kconv class for Kanji Converter. Additionally,
-# some methods in String classes are added to allow easy conversion.
-#
-
-require 'nkf'
-
-#
-# Kanji Converter for Ruby.
-#
-module Kconv
- #
- # Public Constants
- #
-
- #Constant of Encoding
-
- # Auto-Detect
- AUTO = NKF::AUTO
- # ISO-2022-JP
- JIS = NKF::JIS
- # EUC-JP
- EUC = NKF::EUC
- # Shift_JIS
- SJIS = NKF::SJIS
- # BINARY
- BINARY = NKF::BINARY
- # NOCONV
- NOCONV = NKF::NOCONV
- # ASCII
- ASCII = NKF::ASCII
- # UTF-8
- UTF8 = NKF::UTF8
- # UTF-16
- UTF16 = NKF::UTF16
- # UTF-32
- UTF32 = NKF::UTF32
- # UNKNOWN
- UNKNOWN = NKF::UNKNOWN
-
- #
- # Public Methods
- #
-
- # call-seq:
- # Kconv.kconv(str, to_enc, from_enc=nil)
- #
- # Convert <code>str</code> to <code>to_enc</code>.
- # <code>to_enc</code> and <code>from_enc</code> are given as constants of Kconv or Encoding objects.
- def kconv(str, to_enc, from_enc=nil)
- opt = ''
- opt += ' --ic=' + from_enc.to_s if from_enc
- opt += ' --oc=' + to_enc.to_s if to_enc
-
- ::NKF::nkf(opt, str)
- end
- module_function :kconv
-
- #
- # Encode to
- #
-
- # call-seq:
- # Kconv.tojis(str) => string
- #
- # Convert <code>str</code> to ISO-2022-JP
- def tojis(str)
- kconv(str, JIS)
- end
- module_function :tojis
-
- # call-seq:
- # Kconv.toeuc(str) => string
- #
- # Convert <code>str</code> to EUC-JP
- def toeuc(str)
- kconv(str, EUC)
- end
- module_function :toeuc
-
- # call-seq:
- # Kconv.tosjis(str) => string
- #
- # Convert <code>str</code> to Shift_JIS
- def tosjis(str)
- kconv(str, SJIS)
- end
- module_function :tosjis
-
- # call-seq:
- # Kconv.toutf8(str) => string
- #
- # Convert <code>str</code> to UTF-8
- def toutf8(str)
- kconv(str, UTF8)
- end
- module_function :toutf8
-
- # call-seq:
- # Kconv.toutf16(str) => string
- #
- # Convert <code>str</code> to UTF-16
- def toutf16(str)
- kconv(str, UTF16)
- end
- module_function :toutf16
-
- # call-seq:
- # Kconv.toutf32(str) => string
- #
- # Convert <code>str</code> to UTF-32
- def toutf32(str)
- kconv(str, UTF32)
- end
- module_function :toutf32
-
- # call-seq:
- # Kconv.tolocale => string
- #
- # Convert <code>self</code> to locale encoding
- def tolocale(str)
- kconv(str, Encoding.locale_charmap)
- end
- module_function :tolocale
-
- #
- # guess
- #
-
- # call-seq:
- # Kconv.guess(str) => encoding
- #
- # Guess input encoding by NKF.guess
- def guess(str)
- ::NKF::guess(str)
- end
- module_function :guess
-
- #
- # isEncoding
- #
-
- # call-seq:
- # Kconv.iseuc(str) => true or false
- #
- # Returns whether input encoding is EUC-JP or not.
- #
- # *Note* don't expect this return value is MatchData.
- def iseuc(str)
- str.dup.force_encoding(EUC).valid_encoding?
- end
- module_function :iseuc
-
- # call-seq:
- # Kconv.issjis(str) => true or false
- #
- # Returns whether input encoding is Shift_JIS or not.
- def issjis(str)
- str.dup.force_encoding(SJIS).valid_encoding?
- end
- module_function :issjis
-
- # call-seq:
- # Kconv.isjis(str) => true or false
- #
- # Returns whether input encoding is ISO-2022-JP or not.
- def isjis(str)
- /\A [\t\n\r\x20-\x7E]*
- (?:
- (?:\x1b \x28 I [\x21-\x7E]*
- |\x1b \x28 J [\x21-\x7E]*
- |\x1b \x24 @ (?:[\x21-\x7E]{2})*
- |\x1b \x24 B (?:[\x21-\x7E]{2})*
- |\x1b \x24 \x28 D (?:[\x21-\x7E]{2})*
- )*
- \x1b \x28 B [\t\n\r\x20-\x7E]*
- )*
- \z/nox =~ str.dup.force_encoding('BINARY') ? true : false
- end
- module_function :isjis
-
- # call-seq:
- # Kconv.isutf8(str) => true or false
- #
- # Returns whether input encoding is UTF-8 or not.
- def isutf8(str)
- str.dup.force_encoding(UTF8).valid_encoding?
- end
- module_function :isutf8
-end
-
-class String
- # call-seq:
- # String#kconv(to_enc, from_enc)
- #
- # Convert <code>self</code> to <code>to_enc</code>.
- # <code>to_enc</code> and <code>from_enc</code> are given as constants of Kconv or Encoding objects.
- def kconv(to_enc, from_enc=nil)
- from_enc = self.encoding if !from_enc && self.encoding != Encoding.list[0]
- Kconv::kconv(self, to_enc, from_enc)
- end
-
- #
- # to Encoding
- #
-
- # call-seq:
- # String#tojis => string
- #
- # Convert <code>self</code> to ISO-2022-JP
- def tojis; Kconv.tojis(self) end
-
- # call-seq:
- # String#toeuc => string
- #
- # Convert <code>self</code> to EUC-JP
- def toeuc; Kconv.toeuc(self) end
-
- # call-seq:
- # String#tosjis => string
- #
- # Convert <code>self</code> to Shift_JIS
- def tosjis; Kconv.tosjis(self) end
-
- # call-seq:
- # String#toutf8 => string
- #
- # Convert <code>self</code> to UTF-8
- def toutf8; Kconv.toutf8(self) end
-
- # call-seq:
- # String#toutf16 => string
- #
- # Convert <code>self</code> to UTF-16
- def toutf16; Kconv.toutf16(self) end
-
- # call-seq:
- # String#toutf32 => string
- #
- # Convert <code>self</code> to UTF-32
- def toutf32; Kconv.toutf32(self) end
-
- # call-seq:
- # String#tolocale => string
- #
- # Convert <code>self</code> to locale encoding
- def tolocale; Kconv.tolocale(self) end
-
- #
- # is Encoding
- #
-
- # call-seq:
- # String#iseuc => true or false
- #
- # Returns whether <code>self</code>'s encoding is EUC-JP or not.
- def iseuc; Kconv.iseuc(self) end
-
- # call-seq:
- # String#issjis => true or false
- #
- # Returns whether <code>self</code>'s encoding is Shift_JIS or not.
- def issjis; Kconv.issjis(self) end
-
- # call-seq:
- # String#isjis => true or false
- #
- # Returns whether <code>self</code>'s encoding is ISO-2022-JP or not.
- def isjis; Kconv.isjis(self) end
-
- # call-seq:
- # String#isutf8 => true or false
- #
- # Returns whether <code>self</code>'s encoding is UTF-8 or not.
- def isutf8; Kconv.isutf8(self) end
-end
diff --git a/ext/nkf/nkf-utf8/config.h b/ext/nkf/nkf-utf8/config.h
deleted file mode 100644
index 36898c0b4b..0000000000
--- a/ext/nkf/nkf-utf8/config.h
+++ /dev/null
@@ -1,51 +0,0 @@
-#ifndef _CONFIG_H_
-#define _CONFIG_H_
-
-/* UTF8 input and output */
-#define UTF8_INPUT_ENABLE
-#define UTF8_OUTPUT_ENABLE
-
-/* invert characters invalid in Shift_JIS to CP932 */
-#define SHIFTJIS_CP932
-
-/* fix input encoding when given by option */
-#define INPUT_CODE_FIX
-
-/* --overwrite option */
-/* by Satoru Takabayashi <ccsatoru@vega.aichi-u.ac.jp> */
-#define OVERWRITE
-
-/* --cap-input, --url-input option */
-#define INPUT_OPTION
-
-/* --numchar-input option */
-#define NUMCHAR_OPTION
-
-/* --debug, --no-output option */
-#define CHECK_OPTION
-
-/* JIS X0212 */
-#define X0212_ENABLE
-
-/* --exec-in, --exec-out option
- * require pipe, fork, execvp and so on.
- * please undef this on MS-DOS, MinGW
- * this is still buggy around child process
- */
-/* #define EXEC_IO */
-
-/* Unicode Normalization */
-#define UNICODE_NORMALIZATION
-
-/*
- * Select Default Output Encoding
- *
- */
-
-/* #define DEFAULT_CODE_JIS */
-/* #define DEFAULT_CODE_SJIS */
-/* #define DEFAULT_CODE_WINDOWS_31J */
-/* #define DEFAULT_CODE_EUC */
-/* #define DEFAULT_CODE_UTF8 */
-
-#endif /* _CONFIG_H_ */
diff --git a/ext/nkf/nkf-utf8/nkf.c b/ext/nkf/nkf-utf8/nkf.c
deleted file mode 100644
index 6888a43918..0000000000
--- a/ext/nkf/nkf-utf8/nkf.c
+++ /dev/null
@@ -1,7205 +0,0 @@
-/*
- * Copyright (c) 1987, Fujitsu LTD. (Itaru ICHIKAWA).
- * Copyright (c) 1996-2018, The nkf Project.
- *
- * This software is provided 'as-is', without any express or implied
- * warranty. In no event will the authors be held liable for any damages
- * arising from the use of this software.
- *
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
- *
- * 1. The origin of this software must not be misrepresented; you must not
- * claim that you wrote the original software. If you use this software
- * in a product, an acknowledgment in the product documentation would be
- * appreciated but is not required.
- *
- * 2. Altered source versions must be plainly marked as such, and must not be
- * misrepresented as being the original software.
- *
- * 3. This notice may not be removed or altered from any source distribution.
- */
-#define NKF_VERSION "2.1.5"
-#define NKF_RELEASE_DATE "2018-12-15"
-#define COPY_RIGHT \
- "Copyright (C) 1987, FUJITSU LTD. (I.Ichikawa).\n" \
- "Copyright (C) 1996-2018, The nkf Project."
-
-#include "config.h"
-#include "nkf.h"
-#include "utf8tbl.h"
-#ifdef __WIN32__
-#include <windows.h>
-#include <locale.h>
-#endif
-#if defined(__OS2__)
-# define INCL_DOS
-# define INCL_DOSERRORS
-# include <os2.h>
-#endif
-#include <assert.h>
-
-
-/* state of output_mode and input_mode
-
- c2 0 means ASCII
- JIS_X_0201_1976_K
- ISO_8859_1
- JIS_X_0208
- EOF all termination
- c1 32bit data
-
- */
-
-/* MIME ENCODE */
-
-#define FIXED_MIME 7
-#define STRICT_MIME 8
-
-/* byte order */
-enum byte_order {
- ENDIAN_BIG = 1,
- ENDIAN_LITTLE = 2,
- ENDIAN_2143 = 3,
- ENDIAN_3412 = 4
-};
-
-/* ASCII CODE */
-
-#define BS 0x08
-#define TAB 0x09
-#define LF 0x0a
-#define CR 0x0d
-#define ESC 0x1b
-#define SP 0x20
-#define DEL 0x7f
-#define SI 0x0f
-#define SO 0x0e
-#define SS2 0x8e
-#define SS3 0x8f
-#define CRLF 0x0D0A
-
-
-/* encodings */
-
-enum nkf_encodings {
- ASCII,
- ISO_8859_1,
- ISO_2022_JP,
- CP50220,
- CP50221,
- CP50222,
- ISO_2022_JP_1,
- ISO_2022_JP_3,
- ISO_2022_JP_2004,
- SHIFT_JIS,
- WINDOWS_31J,
- CP10001,
- EUC_JP,
- EUCJP_NKF,
- CP51932,
- EUCJP_MS,
- EUCJP_ASCII,
- SHIFT_JISX0213,
- SHIFT_JIS_2004,
- EUC_JISX0213,
- EUC_JIS_2004,
- UTF_8,
- UTF_8N,
- UTF_8_BOM,
- UTF8_MAC,
- UTF_16,
- UTF_16BE,
- UTF_16BE_BOM,
- UTF_16LE,
- UTF_16LE_BOM,
- UTF_32,
- UTF_32BE,
- UTF_32BE_BOM,
- UTF_32LE,
- UTF_32LE_BOM,
- BINARY,
- NKF_ENCODING_TABLE_SIZE,
- JIS_X_0201_1976_K = 0x1013, /* I */ /* JIS C 6220-1969 */
- /* JIS_X_0201_1976_R = 0x1014, */ /* J */ /* JIS C 6220-1969 */
- /* JIS_X_0208_1978 = 0x1040, */ /* @ */ /* JIS C 6226-1978 */
- /* JIS_X_0208_1983 = 0x1087, */ /* B */ /* JIS C 6226-1983 */
- JIS_X_0208 = 0x1168, /* @B */
- JIS_X_0212 = 0x1159, /* D */
- /* JIS_X_0213_2000_1 = 0x1228, */ /* O */
- JIS_X_0213_2 = 0x1229, /* P */
- JIS_X_0213_1 = 0x1233 /* Q */
-};
-
-static nkf_char s_iconv(nkf_char c2, nkf_char c1, nkf_char c0);
-static nkf_char e_iconv(nkf_char c2, nkf_char c1, nkf_char c0);
-static nkf_char w_iconv(nkf_char c2, nkf_char c1, nkf_char c0);
-static nkf_char w_iconv16(nkf_char c2, nkf_char c1, nkf_char c0);
-static nkf_char w_iconv32(nkf_char c2, nkf_char c1, nkf_char c0);
-static void j_oconv(nkf_char c2, nkf_char c1);
-static void s_oconv(nkf_char c2, nkf_char c1);
-static void e_oconv(nkf_char c2, nkf_char c1);
-static void w_oconv(nkf_char c2, nkf_char c1);
-static void w_oconv16(nkf_char c2, nkf_char c1);
-static void w_oconv32(nkf_char c2, nkf_char c1);
-
-typedef const struct {
- const char *name;
- nkf_char (*iconv)(nkf_char c2, nkf_char c1, nkf_char c0);
- void (*oconv)(nkf_char c2, nkf_char c1);
-} nkf_native_encoding;
-
-nkf_native_encoding NkfEncodingASCII = { "ASCII", e_iconv, e_oconv };
-nkf_native_encoding NkfEncodingISO_2022_JP = { "ISO-2022-JP", e_iconv, j_oconv };
-nkf_native_encoding NkfEncodingShift_JIS = { "Shift_JIS", s_iconv, s_oconv };
-nkf_native_encoding NkfEncodingEUC_JP = { "EUC-JP", e_iconv, e_oconv };
-nkf_native_encoding NkfEncodingUTF_8 = { "UTF-8", w_iconv, w_oconv };
-nkf_native_encoding NkfEncodingUTF_16 = { "UTF-16", w_iconv16, w_oconv16 };
-nkf_native_encoding NkfEncodingUTF_32 = { "UTF-32", w_iconv32, w_oconv32 };
-
-typedef const struct {
- int id;
- const char *name;
- nkf_native_encoding *base_encoding;
-} nkf_encoding;
-
-nkf_encoding nkf_encoding_table[] = {
- {ASCII, "US-ASCII", &NkfEncodingASCII},
- {ISO_8859_1, "ISO-8859-1", &NkfEncodingASCII},
- {ISO_2022_JP, "ISO-2022-JP", &NkfEncodingISO_2022_JP},
- {CP50220, "CP50220", &NkfEncodingISO_2022_JP},
- {CP50221, "CP50221", &NkfEncodingISO_2022_JP},
- {CP50222, "CP50222", &NkfEncodingISO_2022_JP},
- {ISO_2022_JP_1, "ISO-2022-JP-1", &NkfEncodingISO_2022_JP},
- {ISO_2022_JP_3, "ISO-2022-JP-3", &NkfEncodingISO_2022_JP},
- {ISO_2022_JP_2004, "ISO-2022-JP-2004", &NkfEncodingISO_2022_JP},
- {SHIFT_JIS, "Shift_JIS", &NkfEncodingShift_JIS},
- {WINDOWS_31J, "Windows-31J", &NkfEncodingShift_JIS},
- {CP10001, "CP10001", &NkfEncodingShift_JIS},
- {EUC_JP, "EUC-JP", &NkfEncodingEUC_JP},
- {EUCJP_NKF, "eucJP-nkf", &NkfEncodingEUC_JP},
- {CP51932, "CP51932", &NkfEncodingEUC_JP},
- {EUCJP_MS, "eucJP-MS", &NkfEncodingEUC_JP},
- {EUCJP_ASCII, "eucJP-ASCII", &NkfEncodingEUC_JP},
- {SHIFT_JISX0213, "Shift_JISX0213", &NkfEncodingShift_JIS},
- {SHIFT_JIS_2004, "Shift_JIS-2004", &NkfEncodingShift_JIS},
- {EUC_JISX0213, "EUC-JISX0213", &NkfEncodingEUC_JP},
- {EUC_JIS_2004, "EUC-JIS-2004", &NkfEncodingEUC_JP},
- {UTF_8, "UTF-8", &NkfEncodingUTF_8},
- {UTF_8N, "UTF-8N", &NkfEncodingUTF_8},
- {UTF_8_BOM, "UTF-8-BOM", &NkfEncodingUTF_8},
- {UTF8_MAC, "UTF8-MAC", &NkfEncodingUTF_8},
- {UTF_16, "UTF-16", &NkfEncodingUTF_16},
- {UTF_16BE, "UTF-16BE", &NkfEncodingUTF_16},
- {UTF_16BE_BOM, "UTF-16BE-BOM", &NkfEncodingUTF_16},
- {UTF_16LE, "UTF-16LE", &NkfEncodingUTF_16},
- {UTF_16LE_BOM, "UTF-16LE-BOM", &NkfEncodingUTF_16},
- {UTF_32, "UTF-32", &NkfEncodingUTF_32},
- {UTF_32BE, "UTF-32BE", &NkfEncodingUTF_32},
- {UTF_32BE_BOM, "UTF-32BE-BOM", &NkfEncodingUTF_32},
- {UTF_32LE, "UTF-32LE", &NkfEncodingUTF_32},
- {UTF_32LE_BOM, "UTF-32LE-BOM", &NkfEncodingUTF_32},
- {BINARY, "BINARY", &NkfEncodingASCII},
- {-1, NULL, NULL}
-};
-
-static const struct {
- const char *name;
- int id;
-} encoding_name_to_id_table[] = {
- {"US-ASCII", ASCII},
- {"ASCII", ASCII},
- {"646", ASCII},
- {"ROMAN8", ASCII},
- {"ISO-2022-JP", ISO_2022_JP},
- {"ISO2022JP-CP932", CP50220},
- {"CP50220", CP50220},
- {"CP50221", CP50221},
- {"CSISO2022JP", CP50221},
- {"CP50222", CP50222},
- {"ISO-2022-JP-1", ISO_2022_JP_1},
- {"ISO-2022-JP-3", ISO_2022_JP_3},
- {"ISO-2022-JP-2004", ISO_2022_JP_2004},
- {"SHIFT_JIS", SHIFT_JIS},
- {"SJIS", SHIFT_JIS},
- {"MS_Kanji", SHIFT_JIS},
- {"PCK", SHIFT_JIS},
- {"WINDOWS-31J", WINDOWS_31J},
- {"CSWINDOWS31J", WINDOWS_31J},
- {"CP932", WINDOWS_31J},
- {"MS932", WINDOWS_31J},
- {"CP10001", CP10001},
- {"EUCJP", EUC_JP},
- {"EUC-JP", EUC_JP},
- {"EUCJP-NKF", EUCJP_NKF},
- {"CP51932", CP51932},
- {"EUC-JP-MS", EUCJP_MS},
- {"EUCJP-MS", EUCJP_MS},
- {"EUCJPMS", EUCJP_MS},
- {"EUC-JP-ASCII", EUCJP_ASCII},
- {"EUCJP-ASCII", EUCJP_ASCII},
- {"SHIFT_JISX0213", SHIFT_JISX0213},
- {"SHIFT_JIS-2004", SHIFT_JIS_2004},
- {"EUC-JISX0213", EUC_JISX0213},
- {"EUC-JIS-2004", EUC_JIS_2004},
- {"UTF-8", UTF_8},
- {"UTF-8N", UTF_8N},
- {"UTF-8-BOM", UTF_8_BOM},
- {"UTF8-MAC", UTF8_MAC},
- {"UTF-8-MAC", UTF8_MAC},
- {"UTF-16", UTF_16},
- {"UTF-16BE", UTF_16BE},
- {"UTF-16BE-BOM", UTF_16BE_BOM},
- {"UTF-16LE", UTF_16LE},
- {"UTF-16LE-BOM", UTF_16LE_BOM},
- {"UTF-32", UTF_32},
- {"UTF-32BE", UTF_32BE},
- {"UTF-32BE-BOM", UTF_32BE_BOM},
- {"UTF-32LE", UTF_32LE},
- {"UTF-32LE-BOM", UTF_32LE_BOM},
- {"BINARY", BINARY},
- {NULL, -1}
-};
-
-#if defined(DEFAULT_CODE_JIS)
-#define DEFAULT_ENCIDX ISO_2022_JP
-#elif defined(DEFAULT_CODE_SJIS)
-#define DEFAULT_ENCIDX SHIFT_JIS
-#elif defined(DEFAULT_CODE_WINDOWS_31J)
-#define DEFAULT_ENCIDX WINDOWS_31J
-#elif defined(DEFAULT_CODE_EUC)
-#define DEFAULT_ENCIDX EUC_JP
-#elif defined(DEFAULT_CODE_UTF8)
-#define DEFAULT_ENCIDX UTF_8
-#endif
-
-
-#define is_alnum(c) \
- (('a'<=c && c<='z')||('A'<= c && c<='Z')||('0'<=c && c<='9'))
-
-/* I don't trust portablity of toupper */
-#define nkf_toupper(c) (('a'<=c && c<='z')?(c-('a'-'A')):c)
-#define nkf_isoctal(c) ('0'<=c && c<='7')
-#define nkf_isdigit(c) ('0'<=c && c<='9')
-#define nkf_isxdigit(c) (nkf_isdigit(c) || ('a'<=c && c<='f') || ('A'<=c && c <= 'F'))
-#define nkf_isblank(c) (c == SP || c == TAB)
-#define nkf_isspace(c) (nkf_isblank(c) || c == CR || c == LF)
-#define nkf_isalpha(c) (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'))
-#define nkf_isalnum(c) (nkf_isdigit(c) || nkf_isalpha(c))
-#define nkf_isprint(c) (SP<=c && c<='~')
-#define nkf_isgraph(c) ('!'<=c && c<='~')
-#define hex2bin(c) (('0'<=c&&c<='9') ? (c-'0') : \
- ('A'<=c&&c<='F') ? (c-'A'+10) : \
- ('a'<=c&&c<='f') ? (c-'a'+10) : 0)
-#define bin2hex(c) ("0123456789ABCDEF"[c&15])
-#define is_eucg3(c2) (((unsigned short)c2 >> 8) == SS3)
-#define nkf_noescape_mime(c) ((c == CR) || (c == LF) || \
- ((c > SP) && (c < DEL) && (c != '?') && (c != '=') && (c != '_') \
- && (c != '(') && (c != ')') && (c != '.') && (c != 0x22)))
-
-#define is_ibmext_in_sjis(c2) (CP932_TABLE_BEGIN <= c2 && c2 <= CP932_TABLE_END)
-#define nkf_byte_jisx0201_katakana_p(c) (SP <= c && c <= 0x5F)
-
-#define HOLD_SIZE 1024
-#if defined(INT_IS_SHORT)
-#define IOBUF_SIZE 2048
-#else
-#define IOBUF_SIZE 16384
-#endif
-
-#define DEFAULT_J 'B'
-#define DEFAULT_R 'B'
-
-
-#define GETA1 0x22
-#define GETA2 0x2e
-
-
-/* MIME preprocessor */
-
-#ifdef EASYWIN /*Easy Win */
-extern POINT _BufferSize;
-#endif
-
-struct input_code{
- const char *name;
- nkf_char stat;
- nkf_char score;
- nkf_char index;
- nkf_char buf[3];
- void (*status_func)(struct input_code *, nkf_char);
- nkf_char (*iconv_func)(nkf_char c2, nkf_char c1, nkf_char c0);
- int _file_stat;
-};
-
-static const char *input_codename = NULL; /* NULL: unestablished, "": BINARY */
-static nkf_encoding *input_encoding = NULL;
-static nkf_encoding *output_encoding = NULL;
-
-#if defined(UTF8_INPUT_ENABLE) || defined(UTF8_OUTPUT_ENABLE)
-/* UCS Mapping
- * 0: Shift_JIS, eucJP-ascii
- * 1: eucJP-ms
- * 2: CP932, CP51932
- * 3: CP10001
- */
-#define UCS_MAP_ASCII 0
-#define UCS_MAP_MS 1
-#define UCS_MAP_CP932 2
-#define UCS_MAP_CP10001 3
-static int ms_ucs_map_f = UCS_MAP_ASCII;
-#endif
-#ifdef UTF8_INPUT_ENABLE
-/* no NEC special, NEC-selected IBM extended and IBM extended characters */
-static int no_cp932ext_f = FALSE;
-/* ignore ZERO WIDTH NO-BREAK SPACE */
-static int no_best_fit_chars_f = FALSE;
-static int input_endian = ENDIAN_BIG;
-static int input_bom_f = FALSE;
-static nkf_char unicode_subchar = '?'; /* the regular substitution character */
-static void (*encode_fallback)(nkf_char c) = NULL;
-static void w_status(struct input_code *, nkf_char);
-#endif
-#ifdef UTF8_OUTPUT_ENABLE
-static int output_bom_f = FALSE;
-static int output_endian = ENDIAN_BIG;
-#endif
-
-static void std_putc(nkf_char c);
-static nkf_char std_getc(FILE *f);
-static nkf_char std_ungetc(nkf_char c,FILE *f);
-
-static nkf_char broken_getc(FILE *f);
-static nkf_char broken_ungetc(nkf_char c,FILE *f);
-
-static nkf_char mime_getc(FILE *f);
-
-static void mime_putc(nkf_char c);
-
-/* buffers */
-
-#if !defined(PERL_XS) && !defined(WIN32DLL)
-static unsigned char stdibuf[IOBUF_SIZE];
-static unsigned char stdobuf[IOBUF_SIZE];
-#endif
-
-#define NKF_UNSPECIFIED (-TRUE)
-
-/* flags */
-static int unbuf_f = FALSE;
-static int estab_f = FALSE;
-static int nop_f = FALSE;
-static int binmode_f = TRUE; /* binary mode */
-static int rot_f = FALSE; /* rot14/43 mode */
-static int hira_f = FALSE; /* hira/kata henkan */
-static int alpha_f = FALSE; /* convert JIx0208 alphbet to ASCII */
-static int mime_f = MIME_DECODE_DEFAULT; /* convert MIME B base64 or Q */
-static int mime_decode_f = FALSE; /* mime decode is explicitly on */
-static int mimebuf_f = FALSE; /* MIME buffered input */
-static int broken_f = FALSE; /* convert ESC-less broken JIS */
-static int iso8859_f = FALSE; /* ISO8859 through */
-static int mimeout_f = FALSE; /* base64 mode */
-static int x0201_f = NKF_UNSPECIFIED; /* convert JIS X 0201 */
-static int iso2022jp_f = FALSE; /* replace non ISO-2022-JP with GETA */
-
-#ifdef UNICODE_NORMALIZATION
-static int nfc_f = FALSE;
-static nkf_char (*i_nfc_getc)(FILE *) = std_getc; /* input of ugetc */
-static nkf_char (*i_nfc_ungetc)(nkf_char c ,FILE *f) = std_ungetc;
-#endif
-
-#ifdef INPUT_OPTION
-static int cap_f = FALSE;
-static nkf_char (*i_cgetc)(FILE *) = std_getc; /* input of cgetc */
-static nkf_char (*i_cungetc)(nkf_char c ,FILE *f) = std_ungetc;
-
-static int url_f = FALSE;
-static nkf_char (*i_ugetc)(FILE *) = std_getc; /* input of ugetc */
-static nkf_char (*i_uungetc)(nkf_char c ,FILE *f) = std_ungetc;
-#endif
-
-#define PREFIX_EUCG3 NKF_INT32_C(0x8F00)
-#define CLASS_MASK NKF_INT32_C(0xFF000000)
-#define CLASS_UNICODE NKF_INT32_C(0x01000000)
-#define VALUE_MASK NKF_INT32_C(0x00FFFFFF)
-#define UNICODE_BMP_MAX NKF_INT32_C(0x0000FFFF)
-#define UNICODE_MAX NKF_INT32_C(0x0010FFFF)
-#define nkf_char_euc3_new(c) ((c) | PREFIX_EUCG3)
-#define nkf_char_unicode_new(c) ((c) | CLASS_UNICODE)
-#define nkf_char_unicode_p(c) ((c & CLASS_MASK) == CLASS_UNICODE)
-#define nkf_char_unicode_bmp_p(c) ((c & VALUE_MASK) <= UNICODE_BMP_MAX)
-#define nkf_char_unicode_value_p(c) ((c & VALUE_MASK) <= UNICODE_MAX)
-
-#define UTF16_TO_UTF32(lead, trail) (((lead) << 10) + (trail) - NKF_INT32_C(0x35FDC00))
-
-#ifdef NUMCHAR_OPTION
-static int numchar_f = FALSE;
-static nkf_char (*i_ngetc)(FILE *) = std_getc; /* input of ugetc */
-static nkf_char (*i_nungetc)(nkf_char c ,FILE *f) = std_ungetc;
-#endif
-
-#ifdef CHECK_OPTION
-static int noout_f = FALSE;
-static void no_putc(nkf_char c);
-static int debug_f = FALSE;
-static void debug(const char *str);
-static nkf_char (*iconv_for_check)(nkf_char c2,nkf_char c1,nkf_char c0) = 0;
-#endif
-
-static int guess_f = 0; /* 0: OFF, 1: ON, 2: VERBOSE */
-static void set_input_codename(const char *codename);
-
-#ifdef EXEC_IO
-static int exec_f = 0;
-#endif
-
-#ifdef SHIFTJIS_CP932
-/* invert IBM extended characters to others */
-static int cp51932_f = FALSE;
-
-/* invert NEC-selected IBM extended characters to IBM extended characters */
-static int cp932inv_f = TRUE;
-
-/* static nkf_char cp932_conv(nkf_char c2, nkf_char c1); */
-#endif /* SHIFTJIS_CP932 */
-
-static int x0212_f = FALSE;
-static int x0213_f = FALSE;
-
-static unsigned char prefix_table[256];
-
-static void e_status(struct input_code *, nkf_char);
-static void s_status(struct input_code *, nkf_char);
-
-struct input_code input_code_list[] = {
- {"EUC-JP", 0, 0, 0, {0, 0, 0}, e_status, e_iconv, 0},
- {"Shift_JIS", 0, 0, 0, {0, 0, 0}, s_status, s_iconv, 0},
-#ifdef UTF8_INPUT_ENABLE
- {"UTF-8", 0, 0, 0, {0, 0, 0}, w_status, w_iconv, 0},
- {"UTF-16", 0, 0, 0, {0, 0, 0}, NULL, w_iconv16, 0},
- {"UTF-32", 0, 0, 0, {0, 0, 0}, NULL, w_iconv32, 0},
-#endif
- {NULL, 0, 0, 0, {0, 0, 0}, NULL, NULL, 0}
-};
-
-static int mimeout_mode = 0; /* 0, -1, 'Q', 'B', 1, 2 */
-static int base64_count = 0;
-
-/* X0208 -> ASCII converter */
-
-/* fold parameter */
-static int f_line = 0; /* chars in line */
-static int f_prev = 0;
-static int fold_preserve_f = FALSE; /* preserve new lines */
-static int fold_f = FALSE;
-static int fold_len = 0;
-
-/* options */
-static unsigned char kanji_intro = DEFAULT_J;
-static unsigned char ascii_intro = DEFAULT_R;
-
-/* Folding */
-
-#define FOLD_MARGIN 10
-#define DEFAULT_FOLD 60
-
-static int fold_margin = FOLD_MARGIN;
-
-/* process default */
-
-static nkf_char
-no_connection2(ARG_UNUSED nkf_char c2, ARG_UNUSED nkf_char c1, ARG_UNUSED nkf_char c0)
-{
- fprintf(stderr,"nkf internal module connection failure.\n");
- exit(EXIT_FAILURE);
- return 0; /* LINT */
-}
-
-static void
-no_connection(nkf_char c2, nkf_char c1)
-{
- no_connection2(c2,c1,0);
-}
-
-static nkf_char (*iconv)(nkf_char c2,nkf_char c1,nkf_char c0) = no_connection2;
-static void (*oconv)(nkf_char c2,nkf_char c1) = no_connection;
-
-static void (*o_zconv)(nkf_char c2,nkf_char c1) = no_connection;
-static void (*o_fconv)(nkf_char c2,nkf_char c1) = no_connection;
-static void (*o_eol_conv)(nkf_char c2,nkf_char c1) = no_connection;
-static void (*o_rot_conv)(nkf_char c2,nkf_char c1) = no_connection;
-static void (*o_hira_conv)(nkf_char c2,nkf_char c1) = no_connection;
-static void (*o_base64conv)(nkf_char c2,nkf_char c1) = no_connection;
-static void (*o_iso2022jp_check_conv)(nkf_char c2,nkf_char c1) = no_connection;
-
-/* static redirections */
-
-static void (*o_putc)(nkf_char c) = std_putc;
-
-static nkf_char (*i_getc)(FILE *f) = std_getc; /* general input */
-static nkf_char (*i_ungetc)(nkf_char c,FILE *f) =std_ungetc;
-
-static nkf_char (*i_bgetc)(FILE *) = std_getc; /* input of mgetc */
-static nkf_char (*i_bungetc)(nkf_char c ,FILE *f) = std_ungetc;
-
-static void (*o_mputc)(nkf_char c) = std_putc ; /* output of mputc */
-
-static nkf_char (*i_mgetc)(FILE *) = std_getc; /* input of mgetc */
-static nkf_char (*i_mungetc)(nkf_char c ,FILE *f) = std_ungetc;
-
-/* for strict mime */
-static nkf_char (*i_mgetc_buf)(FILE *) = std_getc; /* input of mgetc_buf */
-static nkf_char (*i_mungetc_buf)(nkf_char c,FILE *f) = std_ungetc;
-
-/* Global states */
-static int output_mode = ASCII; /* output kanji mode */
-static int input_mode = ASCII; /* input kanji mode */
-static int mime_decode_mode = FALSE; /* MIME mode B base64, Q hex */
-
-/* X0201 / X0208 conversion tables */
-
-/* X0201 kana conversion table */
-/* 90-9F A0-DF */
-static const unsigned char cv[]= {
- 0x21,0x21,0x21,0x23,0x21,0x56,0x21,0x57,
- 0x21,0x22,0x21,0x26,0x25,0x72,0x25,0x21,
- 0x25,0x23,0x25,0x25,0x25,0x27,0x25,0x29,
- 0x25,0x63,0x25,0x65,0x25,0x67,0x25,0x43,
- 0x21,0x3c,0x25,0x22,0x25,0x24,0x25,0x26,
- 0x25,0x28,0x25,0x2a,0x25,0x2b,0x25,0x2d,
- 0x25,0x2f,0x25,0x31,0x25,0x33,0x25,0x35,
- 0x25,0x37,0x25,0x39,0x25,0x3b,0x25,0x3d,
- 0x25,0x3f,0x25,0x41,0x25,0x44,0x25,0x46,
- 0x25,0x48,0x25,0x4a,0x25,0x4b,0x25,0x4c,
- 0x25,0x4d,0x25,0x4e,0x25,0x4f,0x25,0x52,
- 0x25,0x55,0x25,0x58,0x25,0x5b,0x25,0x5e,
- 0x25,0x5f,0x25,0x60,0x25,0x61,0x25,0x62,
- 0x25,0x64,0x25,0x66,0x25,0x68,0x25,0x69,
- 0x25,0x6a,0x25,0x6b,0x25,0x6c,0x25,0x6d,
- 0x25,0x6f,0x25,0x73,0x21,0x2b,0x21,0x2c,
- 0x00,0x00};
-
-
-/* X0201 kana conversion table for dakuten */
-/* 90-9F A0-DF */
-static const unsigned char dv[]= {
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x25,0x74,
- 0x00,0x00,0x00,0x00,0x25,0x2c,0x25,0x2e,
- 0x25,0x30,0x25,0x32,0x25,0x34,0x25,0x36,
- 0x25,0x38,0x25,0x3a,0x25,0x3c,0x25,0x3e,
- 0x25,0x40,0x25,0x42,0x25,0x45,0x25,0x47,
- 0x25,0x49,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x25,0x50,0x25,0x53,
- 0x25,0x56,0x25,0x59,0x25,0x5c,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00};
-
-/* X0201 kana conversion table for han-dakuten */
-/* 90-9F A0-DF */
-static const unsigned char ev[]= {
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x25,0x51,0x25,0x54,
- 0x25,0x57,0x25,0x5a,0x25,0x5d,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00};
-
-/* X0201 kana to X0213 conversion table for han-dakuten */
-/* 90-9F A0-DF */
-static const unsigned char ev_x0213[]= {
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x25,0x77,0x25,0x78,
- 0x25,0x79,0x25,0x7a,0x25,0x7b,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x25,0x7c,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x25,0x7d,0x00,0x00,
- 0x25,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00};
-
-
-/* X0208 kigou conversion table */
-/* 0x8140 - 0x819e */
-static const unsigned char fv[] = {
-
- 0x00,0x00,0x00,0x00,0x2c,0x2e,0x00,0x3a,
- 0x3b,0x3f,0x21,0x00,0x00,0x27,0x60,0x00,
- 0x5e,0x00,0x5f,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x2d,0x00,0x2f,
- 0x5c,0x00,0x00,0x7c,0x00,0x00,0x60,0x27,
- 0x22,0x22,0x28,0x29,0x00,0x00,0x5b,0x5d,
- 0x7b,0x7d,0x3c,0x3e,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x2b,0x2d,0x00,0x00,
- 0x00,0x3d,0x00,0x3c,0x3e,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x24,0x00,0x00,0x25,0x23,0x26,0x2a,0x40,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
-} ;
-
-
-
-static int option_mode = 0;
-static int file_out_f = FALSE;
-#ifdef OVERWRITE
-static int overwrite_f = FALSE;
-static int preserve_time_f = FALSE;
-static int backup_f = FALSE;
-static char *backup_suffix = "";
-#endif
-
-static int eolmode_f = 0; /* CR, LF, CRLF */
-static int input_eol = 0; /* 0: unestablished, EOF: MIXED */
-static nkf_char prev_cr = 0; /* CR or 0 */
-#ifdef EASYWIN /*Easy Win */
-static int end_check;
-#endif /*Easy Win */
-
-static void *
-nkf_xmalloc(size_t size)
-{
- void *ptr;
-
- if (size == 0) size = 1;
-
- ptr = malloc(size);
- if (ptr == NULL) {
- perror("can't malloc");
- exit(EXIT_FAILURE);
- }
-
- return ptr;
-}
-
-static void *
-nkf_xrealloc(void *ptr, size_t size)
-{
- if (size == 0) size = 1;
-
- ptr = realloc(ptr, size);
- if (ptr == NULL) {
- perror("can't realloc");
- exit(EXIT_FAILURE);
- }
-
- return ptr;
-}
-
-#define nkf_xfree(ptr) free(ptr)
-
-static int
-nkf_str_caseeql(const char *src, const char *target)
-{
- int i;
- for (i = 0; src[i] && target[i]; i++) {
- if (nkf_toupper(src[i]) != nkf_toupper(target[i])) return FALSE;
- }
- if (src[i] || target[i]) return FALSE;
- else return TRUE;
-}
-
-static nkf_encoding*
-nkf_enc_from_index(int idx)
-{
- if (idx < 0 || NKF_ENCODING_TABLE_SIZE <= idx) {
- return 0;
- }
- return &nkf_encoding_table[idx];
-}
-
-static int
-nkf_enc_find_index(const char *name)
-{
- int i;
- if (name[0] == 'X' && *(name+1) == '-') name += 2;
- for (i = 0; encoding_name_to_id_table[i].id >= 0; i++) {
- if (nkf_str_caseeql(encoding_name_to_id_table[i].name, name)) {
- return encoding_name_to_id_table[i].id;
- }
- }
- return -1;
-}
-
-static nkf_encoding*
-nkf_enc_find(const char *name)
-{
- int idx = -1;
- idx = nkf_enc_find_index(name);
- if (idx < 0) return 0;
- return nkf_enc_from_index(idx);
-}
-
-#define nkf_enc_name(enc) (enc)->name
-#define nkf_enc_to_index(enc) (enc)->id
-#define nkf_enc_to_base_encoding(enc) (enc)->base_encoding
-#define nkf_enc_to_iconv(enc) nkf_enc_to_base_encoding(enc)->iconv
-#define nkf_enc_to_oconv(enc) nkf_enc_to_base_encoding(enc)->oconv
-#define nkf_enc_asciicompat(enc) (\
- nkf_enc_to_base_encoding(enc) == &NkfEncodingASCII ||\
- nkf_enc_to_base_encoding(enc) == &NkfEncodingISO_2022_JP)
-#define nkf_enc_unicode_p(enc) (\
- nkf_enc_to_base_encoding(enc) == &NkfEncodingUTF_8 ||\
- nkf_enc_to_base_encoding(enc) == &NkfEncodingUTF_16 ||\
- nkf_enc_to_base_encoding(enc) == &NkfEncodingUTF_32)
-#define nkf_enc_cp5022x_p(enc) (\
- nkf_enc_to_index(enc) == CP50220 ||\
- nkf_enc_to_index(enc) == CP50221 ||\
- nkf_enc_to_index(enc) == CP50222)
-
-#ifdef DEFAULT_CODE_LOCALE
-static const char*
-nkf_locale_charmap(void)
-{
-#ifdef HAVE_LANGINFO_H
- return nl_langinfo(CODESET);
-#elif defined(__WIN32__)
- static char buf[16];
- sprintf(buf, "CP%d", GetACP());
- return buf;
-#elif defined(__OS2__)
-# if defined(INT_IS_SHORT)
- /* OS/2 1.x */
- return NULL;
-# else
- /* OS/2 32bit */
- static char buf[16];
- ULONG ulCP[1], ulncp;
- DosQueryCp(sizeof(ulCP), ulCP, &ulncp);
- if (ulCP[0] == 932 || ulCP[0] == 943)
- strcpy(buf, "Shift_JIS");
- else
- sprintf(buf, "CP%lu", ulCP[0]);
- return buf;
-# endif
-#endif
- return NULL;
-}
-
-static nkf_encoding*
-nkf_locale_encoding(void)
-{
- nkf_encoding *enc = 0;
- const char *encname = nkf_locale_charmap();
- if (encname)
- enc = nkf_enc_find(encname);
- return enc;
-}
-#endif /* DEFAULT_CODE_LOCALE */
-
-static nkf_encoding*
-nkf_utf8_encoding(void)
-{
- return &nkf_encoding_table[UTF_8];
-}
-
-static nkf_encoding*
-nkf_default_encoding(void)
-{
- nkf_encoding *enc = 0;
-#ifdef DEFAULT_CODE_LOCALE
- enc = nkf_locale_encoding();
-#elif defined(DEFAULT_ENCIDX)
- enc = nkf_enc_from_index(DEFAULT_ENCIDX);
-#endif
- if (!enc) enc = nkf_utf8_encoding();
- return enc;
-}
-
-typedef struct {
- long capa;
- long len;
- nkf_char *ptr;
-} nkf_buf_t;
-
-static nkf_buf_t *
-nkf_buf_new(int length)
-{
- nkf_buf_t *buf = nkf_xmalloc(sizeof(nkf_buf_t));
- buf->ptr = nkf_xmalloc(sizeof(nkf_char) * length);
- buf->capa = length;
- buf->len = 0;
- return buf;
-}
-
-#if 0
-static void
-nkf_buf_dispose(nkf_buf_t *buf)
-{
- nkf_xfree(buf->ptr);
- nkf_xfree(buf);
-}
-#endif
-
-#define nkf_buf_length(buf) ((buf)->len)
-#define nkf_buf_empty_p(buf) ((buf)->len == 0)
-
-static nkf_char
-nkf_buf_at(nkf_buf_t *buf, int index)
-{
- assert(index <= buf->len);
- return buf->ptr[index];
-}
-
-static void
-nkf_buf_clear(nkf_buf_t *buf)
-{
- buf->len = 0;
-}
-
-static void
-nkf_buf_push(nkf_buf_t *buf, nkf_char c)
-{
- if (buf->capa <= buf->len) {
- exit(EXIT_FAILURE);
- }
- buf->ptr[buf->len++] = c;
-}
-
-static nkf_char
-nkf_buf_pop(nkf_buf_t *buf)
-{
- assert(!nkf_buf_empty_p(buf));
- return buf->ptr[--buf->len];
-}
-
-/* Normalization Form C */
-#ifndef PERL_XS
-#ifdef WIN32DLL
-#define fprintf dllprintf
-#endif
-
-static void
-version(void)
-{
- fprintf(HELP_OUTPUT,"Network Kanji Filter Version " NKF_VERSION " (" NKF_RELEASE_DATE ") \n" COPY_RIGHT "\n");
-}
-
-static void
-usage(void)
-{
- fprintf(HELP_OUTPUT,
- "Usage: nkf -[flags] [--] [in file] .. [out file for -O flag]\n"
-#ifdef UTF8_OUTPUT_ENABLE
- " j/s/e/w Specify output encoding ISO-2022-JP, Shift_JIS, EUC-JP\n"
- " UTF options is -w[8[0],{16,32}[{B,L}[0]]]\n"
-#else
-#endif
-#ifdef UTF8_INPUT_ENABLE
- " J/S/E/W Specify input encoding ISO-2022-JP, Shift_JIS, EUC-JP\n"
- " UTF option is -W[8,[16,32][B,L]]\n"
-#else
- " J/S/E Specify output encoding ISO-2022-JP, Shift_JIS, EUC-JP\n"
-#endif
- );
- fprintf(HELP_OUTPUT,
- " m[BQSN0] MIME decode [B:base64,Q:quoted,S:strict,N:nonstrict,0:no decode]\n"
- " M[BQ] MIME encode [B:base64 Q:quoted]\n"
- " f/F Folding: -f60 or -f or -f60-10 (fold margin 10) F preserve nl\n"
- );
- fprintf(HELP_OUTPUT,
- " Z[0-4] Default/0: Convert JISX0208 Alphabet to ASCII\n"
- " 1: Kankaku to one space 2: to two spaces 3: HTML Entity\n"
- " 4: JISX0208 Katakana to JISX0201 Katakana\n"
- " X,x Convert Halfwidth Katakana to Fullwidth or preserve it\n"
- );
- fprintf(HELP_OUTPUT,
- " O Output to File (DEFAULT 'nkf.out')\n"
- " L[uwm] Line mode u:LF w:CRLF m:CR (DEFAULT noconversion)\n"
- );
- fprintf(HELP_OUTPUT,
- " --ic=<encoding> Specify the input encoding\n"
- " --oc=<encoding> Specify the output encoding\n"
- " --hiragana --katakana Hiragana/Katakana Conversion\n"
- " --katakana-hiragana Converts each other\n"
- );
- fprintf(HELP_OUTPUT,
-#ifdef INPUT_OPTION
- " --{cap, url}-input Convert hex after ':' or '%%'\n"
-#endif
-#ifdef NUMCHAR_OPTION
- " --numchar-input Convert Unicode Character Reference\n"
-#endif
-#ifdef UTF8_INPUT_ENABLE
- " --fb-{skip, html, xml, perl, java, subchar}\n"
- " Specify unassigned character's replacement\n"
-#endif
- );
- fprintf(HELP_OUTPUT,
-#ifdef OVERWRITE
- " --in-place[=SUF] Overwrite original files\n"
- " --overwrite[=SUF] Preserve timestamp of original files\n"
-#endif
- " -g --guess Guess the input code\n"
- " -v --version Print the version\n"
- " --help/-V Print this help / configuration\n"
- );
- version();
-}
-
-static void
-show_configuration(void)
-{
- fprintf(HELP_OUTPUT,
- "Summary of my nkf " NKF_VERSION " (" NKF_RELEASE_DATE ") configuration:\n"
- " Compile-time options:\n"
- " Compiled at: " __DATE__ " " __TIME__ "\n"
- );
- fprintf(HELP_OUTPUT,
- " Default output encoding: "
-#ifdef DEFAULT_CODE_LOCALE
- "LOCALE (%s)\n", nkf_enc_name(nkf_default_encoding())
-#elif defined(DEFAULT_ENCIDX)
- "CONFIG (%s)\n", nkf_enc_name(nkf_default_encoding())
-#else
- "NONE\n"
-#endif
- );
- fprintf(HELP_OUTPUT,
- " Default output end of line: "
-#if DEFAULT_NEWLINE == CR
- "CR"
-#elif DEFAULT_NEWLINE == CRLF
- "CRLF"
-#else
- "LF"
-#endif
- "\n"
- " Decode MIME encoded string: "
-#if MIME_DECODE_DEFAULT
- "ON"
-#else
- "OFF"
-#endif
- "\n"
- " Convert JIS X 0201 Katakana: "
-#if X0201_DEFAULT
- "ON"
-#else
- "OFF"
-#endif
- "\n"
- " --help, --version output: "
-#if HELP_OUTPUT_HELP_OUTPUT
- "HELP_OUTPUT"
-#else
- "STDOUT"
-#endif
- "\n");
-}
-#endif /*PERL_XS*/
-
-#ifdef OVERWRITE
-static char*
-get_backup_filename(const char *suffix, const char *filename)
-{
- char *backup_filename;
- int asterisk_count = 0;
- int i, j;
- int filename_length = strlen(filename);
-
- for(i = 0; suffix[i]; i++){
- if(suffix[i] == '*') asterisk_count++;
- }
-
- if(asterisk_count){
- backup_filename = nkf_xmalloc(strlen(suffix) + (asterisk_count * (filename_length - 1)) + 1);
- for(i = 0, j = 0; suffix[i];){
- if(suffix[i] == '*'){
- backup_filename[j] = '\0';
- strncat(backup_filename, filename, filename_length);
- i++;
- j += filename_length;
- }else{
- backup_filename[j++] = suffix[i++];
- }
- }
- backup_filename[j] = '\0';
- }else{
- j = filename_length + strlen(suffix);
- backup_filename = nkf_xmalloc(j + 1);
- strcpy(backup_filename, filename);
- strcat(backup_filename, suffix);
- backup_filename[j] = '\0';
- }
- return backup_filename;
-}
-#endif
-
-#ifdef UTF8_INPUT_ENABLE
-static void
-nkf_each_char_to_hex(void (*f)(nkf_char c2,nkf_char c1), nkf_char c)
-{
- int shift = 20;
- c &= VALUE_MASK;
- while(shift >= 0){
- if(c >= NKF_INT32_C(1)<<shift){
- while(shift >= 0){
- (*f)(0, bin2hex(c>>shift));
- shift -= 4;
- }
- }else{
- shift -= 4;
- }
- }
- return;
-}
-
-static void
-encode_fallback_html(nkf_char c)
-{
- (*oconv)(0, '&');
- (*oconv)(0, '#');
- c &= VALUE_MASK;
- if(c >= NKF_INT32_C(1000000))
- (*oconv)(0, 0x30+(c/NKF_INT32_C(1000000))%10);
- if(c >= NKF_INT32_C(100000))
- (*oconv)(0, 0x30+(c/NKF_INT32_C(100000) )%10);
- if(c >= 10000)
- (*oconv)(0, 0x30+(c/10000 )%10);
- if(c >= 1000)
- (*oconv)(0, 0x30+(c/1000 )%10);
- if(c >= 100)
- (*oconv)(0, 0x30+(c/100 )%10);
- if(c >= 10)
- (*oconv)(0, 0x30+(c/10 )%10);
- if(c >= 0)
- (*oconv)(0, 0x30+ c %10);
- (*oconv)(0, ';');
- return;
-}
-
-static void
-encode_fallback_xml(nkf_char c)
-{
- (*oconv)(0, '&');
- (*oconv)(0, '#');
- (*oconv)(0, 'x');
- nkf_each_char_to_hex(oconv, c);
- (*oconv)(0, ';');
- return;
-}
-
-static void
-encode_fallback_java(nkf_char c)
-{
- (*oconv)(0, '\\');
- c &= VALUE_MASK;
- if(!nkf_char_unicode_bmp_p(c)){
- int high = (c >> 10) + NKF_INT32_C(0xD7C0); /* high surrogate */
- int low = (c & 0x3FF) + NKF_INT32_C(0xDC00); /* low surrogate */
- (*oconv)(0, 'u');
- (*oconv)(0, bin2hex(high>>12));
- (*oconv)(0, bin2hex(high>> 8));
- (*oconv)(0, bin2hex(high>> 4));
- (*oconv)(0, bin2hex(high ));
- (*oconv)(0, '\\');
- (*oconv)(0, 'u');
- (*oconv)(0, bin2hex(low>>12));
- (*oconv)(0, bin2hex(low>> 8));
- (*oconv)(0, bin2hex(low>> 4));
- (*oconv)(0, bin2hex(low ));
- }else{
- (*oconv)(0, 'u');
- (*oconv)(0, bin2hex(c>>12));
- (*oconv)(0, bin2hex(c>> 8));
- (*oconv)(0, bin2hex(c>> 4));
- (*oconv)(0, bin2hex(c ));
- }
- return;
-}
-
-static void
-encode_fallback_perl(nkf_char c)
-{
- (*oconv)(0, '\\');
- (*oconv)(0, 'x');
- (*oconv)(0, '{');
- nkf_each_char_to_hex(oconv, c);
- (*oconv)(0, '}');
- return;
-}
-
-static void
-encode_fallback_subchar(nkf_char c)
-{
- c = unicode_subchar;
- (*oconv)((c>>8)&0xFF, c&0xFF);
- return;
-}
-#endif
-
-static const struct {
- const char *name;
- const char *alias;
-} long_option[] = {
- {"ic=", ""},
- {"oc=", ""},
- {"base64","jMB"},
- {"euc","e"},
- {"euc-input","E"},
- {"fj","jm"},
- {"help",""},
- {"jis","j"},
- {"jis-input","J"},
- {"mac","sLm"},
- {"mime","jM"},
- {"mime-input","m"},
- {"msdos","sLw"},
- {"sjis","s"},
- {"sjis-input","S"},
- {"unix","eLu"},
- {"version","v"},
- {"windows","sLw"},
- {"hiragana","h1"},
- {"katakana","h2"},
- {"katakana-hiragana","h3"},
- {"guess=", ""},
- {"guess", "g2"},
- {"cp932", ""},
- {"no-cp932", ""},
-#ifdef X0212_ENABLE
- {"x0212", ""},
-#endif
-#ifdef UTF8_OUTPUT_ENABLE
- {"utf8", "w"},
- {"utf16", "w16"},
- {"ms-ucs-map", ""},
- {"fb-skip", ""},
- {"fb-html", ""},
- {"fb-xml", ""},
- {"fb-perl", ""},
- {"fb-java", ""},
- {"fb-subchar", ""},
- {"fb-subchar=", ""},
-#endif
-#ifdef UTF8_INPUT_ENABLE
- {"utf8-input", "W"},
- {"utf16-input", "W16"},
- {"no-cp932ext", ""},
- {"no-best-fit-chars",""},
-#endif
-#ifdef UNICODE_NORMALIZATION
- {"utf8mac-input", ""},
-#endif
-#ifdef OVERWRITE
- {"overwrite", ""},
- {"overwrite=", ""},
- {"in-place", ""},
- {"in-place=", ""},
-#endif
-#ifdef INPUT_OPTION
- {"cap-input", ""},
- {"url-input", ""},
-#endif
-#ifdef NUMCHAR_OPTION
- {"numchar-input", ""},
-#endif
-#ifdef CHECK_OPTION
- {"no-output", ""},
- {"debug", ""},
-#endif
-#ifdef SHIFTJIS_CP932
- {"cp932inv", ""},
-#endif
-#ifdef EXEC_IO
- {"exec-in", ""},
- {"exec-out", ""},
-#endif
- {"prefix=", ""},
-};
-
-static void
-set_input_encoding(nkf_encoding *enc)
-{
- switch (nkf_enc_to_index(enc)) {
- case ISO_8859_1:
- iso8859_f = TRUE;
- break;
- case CP50221:
- case CP50222:
- if (x0201_f == NKF_UNSPECIFIED) x0201_f = FALSE; /* -x specified implicitly */
- case CP50220:
-#ifdef SHIFTJIS_CP932
- cp51932_f = TRUE;
-#endif
-#ifdef UTF8_OUTPUT_ENABLE
- ms_ucs_map_f = UCS_MAP_CP932;
-#endif
- break;
- case ISO_2022_JP_1:
- x0212_f = TRUE;
- break;
- case ISO_2022_JP_3:
- x0212_f = TRUE;
- x0213_f = TRUE;
- break;
- case ISO_2022_JP_2004:
- x0212_f = TRUE;
- x0213_f = TRUE;
- break;
- case SHIFT_JIS:
- break;
- case WINDOWS_31J:
- if (x0201_f == NKF_UNSPECIFIED) x0201_f = FALSE; /* -x specified implicitly */
-#ifdef SHIFTJIS_CP932
- cp51932_f = TRUE;
-#endif
-#ifdef UTF8_OUTPUT_ENABLE
- ms_ucs_map_f = UCS_MAP_CP932;
-#endif
- break;
- break;
- case CP10001:
-#ifdef SHIFTJIS_CP932
- cp51932_f = TRUE;
-#endif
-#ifdef UTF8_OUTPUT_ENABLE
- ms_ucs_map_f = UCS_MAP_CP10001;
-#endif
- break;
- case EUC_JP:
- break;
- case EUCJP_NKF:
- break;
- case CP51932:
- if (x0201_f == NKF_UNSPECIFIED) x0201_f = FALSE; /* -x specified implicitly */
-#ifdef SHIFTJIS_CP932
- cp51932_f = TRUE;
-#endif
-#ifdef UTF8_OUTPUT_ENABLE
- ms_ucs_map_f = UCS_MAP_CP932;
-#endif
- break;
- case EUCJP_MS:
- if (x0201_f == NKF_UNSPECIFIED) x0201_f = FALSE; /* -x specified implicitly */
-#ifdef SHIFTJIS_CP932
- cp51932_f = FALSE;
-#endif
-#ifdef UTF8_OUTPUT_ENABLE
- ms_ucs_map_f = UCS_MAP_MS;
-#endif
- break;
- case EUCJP_ASCII:
- if (x0201_f == NKF_UNSPECIFIED) x0201_f = FALSE; /* -x specified implicitly */
-#ifdef SHIFTJIS_CP932
- cp51932_f = FALSE;
-#endif
-#ifdef UTF8_OUTPUT_ENABLE
- ms_ucs_map_f = UCS_MAP_ASCII;
-#endif
- break;
- case SHIFT_JISX0213:
- case SHIFT_JIS_2004:
- x0213_f = TRUE;
-#ifdef SHIFTJIS_CP932
- cp51932_f = FALSE;
- if (cp932inv_f == TRUE) cp932inv_f = FALSE;
-#endif
- break;
- case EUC_JISX0213:
- case EUC_JIS_2004:
- x0213_f = TRUE;
-#ifdef SHIFTJIS_CP932
- cp51932_f = FALSE;
-#endif
- break;
-#ifdef UTF8_INPUT_ENABLE
-#ifdef UNICODE_NORMALIZATION
- case UTF8_MAC:
- nfc_f = TRUE;
- break;
-#endif
- case UTF_16:
- case UTF_16BE:
- case UTF_16BE_BOM:
- input_endian = ENDIAN_BIG;
- break;
- case UTF_16LE:
- case UTF_16LE_BOM:
- input_endian = ENDIAN_LITTLE;
- break;
- case UTF_32:
- case UTF_32BE:
- case UTF_32BE_BOM:
- input_endian = ENDIAN_BIG;
- break;
- case UTF_32LE:
- case UTF_32LE_BOM:
- input_endian = ENDIAN_LITTLE;
- break;
-#endif
- }
-}
-
-static void
-set_output_encoding(nkf_encoding *enc)
-{
- switch (nkf_enc_to_index(enc)) {
- case CP50220:
-#ifdef SHIFTJIS_CP932
- if (cp932inv_f == TRUE) cp932inv_f = FALSE;
-#endif
-#ifdef UTF8_OUTPUT_ENABLE
- ms_ucs_map_f = UCS_MAP_CP932;
-#endif
- break;
- case CP50221:
- if (x0201_f == NKF_UNSPECIFIED) x0201_f = FALSE; /* -x specified implicitly */
-#ifdef SHIFTJIS_CP932
- if (cp932inv_f == TRUE) cp932inv_f = FALSE;
-#endif
-#ifdef UTF8_OUTPUT_ENABLE
- ms_ucs_map_f = UCS_MAP_CP932;
-#endif
- break;
- case ISO_2022_JP:
-#ifdef SHIFTJIS_CP932
- if (cp932inv_f == TRUE) cp932inv_f = FALSE;
-#endif
- break;
- case ISO_2022_JP_1:
- x0212_f = TRUE;
-#ifdef SHIFTJIS_CP932
- if (cp932inv_f == TRUE) cp932inv_f = FALSE;
-#endif
- break;
- case ISO_2022_JP_3:
- case ISO_2022_JP_2004:
- x0212_f = TRUE;
- x0213_f = TRUE;
-#ifdef SHIFTJIS_CP932
- if (cp932inv_f == TRUE) cp932inv_f = FALSE;
-#endif
- break;
- case SHIFT_JIS:
- break;
- case WINDOWS_31J:
- if (x0201_f == NKF_UNSPECIFIED) x0201_f = FALSE; /* -x specified implicitly */
-#ifdef UTF8_OUTPUT_ENABLE
- ms_ucs_map_f = UCS_MAP_CP932;
-#endif
- break;
- case CP10001:
-#ifdef UTF8_OUTPUT_ENABLE
- ms_ucs_map_f = UCS_MAP_CP10001;
-#endif
- break;
- case EUC_JP:
- x0212_f = TRUE;
-#ifdef SHIFTJIS_CP932
- if (cp932inv_f == TRUE) cp932inv_f = FALSE;
-#endif
-#ifdef UTF8_OUTPUT_ENABLE
- ms_ucs_map_f = UCS_MAP_ASCII;
-#endif
- break;
- case EUCJP_NKF:
- x0212_f = FALSE;
-#ifdef SHIFTJIS_CP932
- if (cp932inv_f == TRUE) cp932inv_f = FALSE;
-#endif
-#ifdef UTF8_OUTPUT_ENABLE
- ms_ucs_map_f = UCS_MAP_ASCII;
-#endif
- break;
- case CP51932:
- if (x0201_f == NKF_UNSPECIFIED) x0201_f = FALSE; /* -x specified implicitly */
-#ifdef SHIFTJIS_CP932
- if (cp932inv_f == TRUE) cp932inv_f = FALSE;
-#endif
-#ifdef UTF8_OUTPUT_ENABLE
- ms_ucs_map_f = UCS_MAP_CP932;
-#endif
- break;
- case EUCJP_MS:
- if (x0201_f == NKF_UNSPECIFIED) x0201_f = FALSE; /* -x specified implicitly */
- x0212_f = TRUE;
-#ifdef UTF8_OUTPUT_ENABLE
- ms_ucs_map_f = UCS_MAP_MS;
-#endif
- break;
- case EUCJP_ASCII:
- if (x0201_f == NKF_UNSPECIFIED) x0201_f = FALSE; /* -x specified implicitly */
- x0212_f = TRUE;
-#ifdef UTF8_OUTPUT_ENABLE
- ms_ucs_map_f = UCS_MAP_ASCII;
-#endif
- break;
- case SHIFT_JISX0213:
- case SHIFT_JIS_2004:
- x0213_f = TRUE;
-#ifdef SHIFTJIS_CP932
- if (cp932inv_f == TRUE) cp932inv_f = FALSE;
-#endif
- break;
- case EUC_JISX0213:
- case EUC_JIS_2004:
- x0212_f = TRUE;
- x0213_f = TRUE;
-#ifdef SHIFTJIS_CP932
- if (cp932inv_f == TRUE) cp932inv_f = FALSE;
-#endif
- break;
-#ifdef UTF8_OUTPUT_ENABLE
- case UTF_8_BOM:
- output_bom_f = TRUE;
- break;
- case UTF_16:
- case UTF_16BE_BOM:
- output_bom_f = TRUE;
- break;
- case UTF_16LE:
- output_endian = ENDIAN_LITTLE;
- output_bom_f = FALSE;
- break;
- case UTF_16LE_BOM:
- output_endian = ENDIAN_LITTLE;
- output_bom_f = TRUE;
- break;
- case UTF_32:
- case UTF_32BE_BOM:
- output_bom_f = TRUE;
- break;
- case UTF_32LE:
- output_endian = ENDIAN_LITTLE;
- output_bom_f = FALSE;
- break;
- case UTF_32LE_BOM:
- output_endian = ENDIAN_LITTLE;
- output_bom_f = TRUE;
- break;
-#endif
- }
-}
-
-static struct input_code*
-find_inputcode_byfunc(nkf_char (*iconv_func)(nkf_char c2,nkf_char c1,nkf_char c0))
-{
- if (iconv_func){
- struct input_code *p = input_code_list;
- while (p->name){
- if (iconv_func == p->iconv_func){
- return p;
- }
- p++;
- }
- }
- return 0;
-}
-
-static void
-set_iconv(nkf_char f, nkf_char (*iconv_func)(nkf_char c2,nkf_char c1,nkf_char c0))
-{
-#ifdef INPUT_CODE_FIX
- if (f || !input_encoding)
-#endif
- if (estab_f != f){
- estab_f = f;
- }
-
- if (iconv_func
-#ifdef INPUT_CODE_FIX
- && (f == -TRUE || !input_encoding) /* -TRUE means "FORCE" */
-#endif
- ){
- iconv = iconv_func;
- }
-#ifdef CHECK_OPTION
- if (estab_f && iconv_for_check != iconv){
- struct input_code *p = find_inputcode_byfunc(iconv);
- if (p){
- set_input_codename(p->name);
- debug(p->name);
- }
- iconv_for_check = iconv;
- }
-#endif
-}
-
-#ifdef X0212_ENABLE
-static nkf_char
-x0212_shift(nkf_char c)
-{
- nkf_char ret = c;
- c &= 0x7f;
- if (is_eucg3(ret)){
- if (0x75 <= c && c <= 0x7f){
- ret = c + (0x109 - 0x75);
- }
- }else{
- if (0x75 <= c && c <= 0x7f){
- ret = c + (0x113 - 0x75);
- }
- }
- return ret;
-}
-
-
-static nkf_char
-x0212_unshift(nkf_char c)
-{
- nkf_char ret = c;
- if (0x7f <= c && c <= 0x88){
- ret = c + (0x75 - 0x7f);
- }else if (0x89 <= c && c <= 0x92){
- ret = PREFIX_EUCG3 | 0x80 | (c + (0x75 - 0x89));
- }
- return ret;
-}
-#endif /* X0212_ENABLE */
-
-static int
-is_x0213_2_in_x0212(nkf_char c1)
-{
- static const char x0213_2_table[] =
- {0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1};
- int ku = c1 - 0x20;
- if (ku <= 15)
- return x0213_2_table[ku]; /* 1, 3-5, 8, 12-15 */
- if (78 <= ku && ku <= 94)
- return 1;
- return 0;
-}
-
-static nkf_char
-e2s_conv(nkf_char c2, nkf_char c1, nkf_char *p2, nkf_char *p1)
-{
- nkf_char ndx;
- if (is_eucg3(c2)){
- ndx = c2 & 0x7f;
- if (x0213_f && is_x0213_2_in_x0212(ndx)){
- if((0x21 <= ndx && ndx <= 0x2F)){
- if (p2) *p2 = ((ndx - 1) >> 1) + 0xec - ndx / 8 * 3;
- if (p1) *p1 = c1 + ((ndx & 1) ? ((c1 < 0x60) ? 0x1f : 0x20) : 0x7e);
- return 0;
- }else if(0x6E <= ndx && ndx <= 0x7E){
- if (p2) *p2 = ((ndx - 1) >> 1) + 0xbe;
- if (p1) *p1 = c1 + ((ndx & 1) ? ((c1 < 0x60) ? 0x1f : 0x20) : 0x7e);
- return 0;
- }
- return 1;
- }
-#ifdef X0212_ENABLE
- else if(nkf_isgraph(ndx)){
- nkf_char val = 0;
- const unsigned short *ptr;
- ptr = x0212_shiftjis[ndx - 0x21];
- if (ptr){
- val = ptr[(c1 & 0x7f) - 0x21];
- }
- if (val){
- c2 = val >> 8;
- c1 = val & 0xff;
- if (p2) *p2 = c2;
- if (p1) *p1 = c1;
- return 0;
- }
- c2 = x0212_shift(c2);
- }
-#endif /* X0212_ENABLE */
- }
- if(0x7F < c2) return 1;
- if (p2) *p2 = ((c2 - 1) >> 1) + ((c2 <= 0x5e) ? 0x71 : 0xb1);
- if (p1) *p1 = c1 + ((c2 & 1) ? ((c1 < 0x60) ? 0x1f : 0x20) : 0x7e);
- return 0;
-}
-
-static nkf_char
-s2e_conv(nkf_char c2, nkf_char c1, nkf_char *p2, nkf_char *p1)
-{
-#if defined(SHIFTJIS_CP932) || defined(X0212_ENABLE)
- nkf_char val;
-#endif
- static const char shift_jisx0213_s1a3_table[5][2] ={ { 1, 8}, { 3, 4}, { 5,12}, {13,14}, {15, 0} };
- if (0xFC < c1) return 1;
-#ifdef SHIFTJIS_CP932
- if (!cp932inv_f && !x0213_f && is_ibmext_in_sjis(c2)){
- val = shiftjis_cp932[c2 - CP932_TABLE_BEGIN][c1 - 0x40];
- if (val){
- c2 = val >> 8;
- c1 = val & 0xff;
- }
- }
- if (cp932inv_f
- && CP932INV_TABLE_BEGIN <= c2 && c2 <= CP932INV_TABLE_END){
- val = cp932inv[c2 - CP932INV_TABLE_BEGIN][c1 - 0x40];
- if (val){
- c2 = val >> 8;
- c1 = val & 0xff;
- }
- }
-#endif /* SHIFTJIS_CP932 */
-#ifdef X0212_ENABLE
- if (!x0213_f && is_ibmext_in_sjis(c2)){
- val = shiftjis_x0212[c2 - 0xfa][c1 - 0x40];
- if (val){
- if (val > 0x7FFF){
- c2 = PREFIX_EUCG3 | ((val >> 8) & 0x7f);
- c1 = val & 0xff;
- }else{
- c2 = val >> 8;
- c1 = val & 0xff;
- }
- if (p2) *p2 = c2;
- if (p1) *p1 = c1;
- return 0;
- }
- }
-#endif
- if(c2 >= 0x80){
- if(x0213_f && c2 >= 0xF0){
- if(c2 <= 0xF3 || (c2 == 0xF4 && c1 < 0x9F)){ /* k=1, 3<=k<=5, k=8, 12<=k<=15 */
- c2 = PREFIX_EUCG3 | 0x20 | shift_jisx0213_s1a3_table[c2 - 0xF0][0x9E < c1];
- }else{ /* 78<=k<=94 */
- c2 = PREFIX_EUCG3 | (c2 * 2 - 0x17B);
- if (0x9E < c1) c2++;
- }
- }else{
-#define SJ0162 0x00e1 /* 01 - 62 ku offset */
-#define SJ6394 0x0161 /* 63 - 94 ku offset */
- c2 = c2 + c2 - ((c2 <= 0x9F) ? SJ0162 : SJ6394);
- if (0x9E < c1) c2++;
- }
- if (c1 < 0x9F)
- c1 = c1 - ((c1 > DEL) ? SP : 0x1F);
- else {
- c1 = c1 - 0x7E;
- }
- }
-
-#ifdef X0212_ENABLE
- c2 = x0212_unshift(c2);
-#endif
- if (p2) *p2 = c2;
- if (p1) *p1 = c1;
- return 0;
-}
-
-#if defined(UTF8_INPUT_ENABLE) || defined(UTF8_OUTPUT_ENABLE)
-static void
-nkf_unicode_to_utf8(nkf_char val, nkf_char *p1, nkf_char *p2, nkf_char *p3, nkf_char *p4)
-{
- val &= VALUE_MASK;
- if (val < 0x80){
- *p1 = val;
- *p2 = 0;
- *p3 = 0;
- *p4 = 0;
- }else if (val < 0x800){
- *p1 = 0xc0 | (val >> 6);
- *p2 = 0x80 | (val & 0x3f);
- *p3 = 0;
- *p4 = 0;
- } else if (nkf_char_unicode_bmp_p(val)) {
- *p1 = 0xe0 | (val >> 12);
- *p2 = 0x80 | ((val >> 6) & 0x3f);
- *p3 = 0x80 | ( val & 0x3f);
- *p4 = 0;
- } else if (nkf_char_unicode_value_p(val)) {
- *p1 = 0xf0 | (val >> 18);
- *p2 = 0x80 | ((val >> 12) & 0x3f);
- *p3 = 0x80 | ((val >> 6) & 0x3f);
- *p4 = 0x80 | ( val & 0x3f);
- } else {
- *p1 = 0;
- *p2 = 0;
- *p3 = 0;
- *p4 = 0;
- }
-}
-
-static nkf_char
-nkf_utf8_to_unicode(nkf_char c1, nkf_char c2, nkf_char c3, nkf_char c4)
-{
- nkf_char wc;
- if (c1 <= 0x7F) {
- /* single byte */
- wc = c1;
- }
- else if (c1 <= 0xC1) {
- /* trail byte or invalid */
- return -1;
- }
- else if (c1 <= 0xDF) {
- /* 2 bytes */
- wc = (c1 & 0x1F) << 6;
- wc |= (c2 & 0x3F);
- }
- else if (c1 <= 0xEF) {
- /* 3 bytes */
- wc = (c1 & 0x0F) << 12;
- wc |= (c2 & 0x3F) << 6;
- wc |= (c3 & 0x3F);
- }
- else if (c2 <= 0xF4) {
- /* 4 bytes */
- wc = (c1 & 0x0F) << 18;
- wc |= (c2 & 0x3F) << 12;
- wc |= (c3 & 0x3F) << 6;
- wc |= (c4 & 0x3F);
- }
- else {
- return -1;
- }
- return wc;
-}
-#endif
-
-#ifdef UTF8_INPUT_ENABLE
-static int
-unicode_to_jis_common2(nkf_char c1, nkf_char c0,
- const unsigned short *const *pp, nkf_char psize,
- nkf_char *p2, nkf_char *p1)
-{
- nkf_char c2;
- const unsigned short *p;
- unsigned short val;
-
- if (pp == 0) return 1;
-
- c1 -= 0x80;
- if (c1 < 0 || psize <= c1) return 1;
- p = pp[c1];
- if (p == 0) return 1;
-
- c0 -= 0x80;
- if (c0 < 0 || sizeof_utf8_to_euc_C2 <= c0) return 1;
- val = p[c0];
- if (val == 0) return 1;
- if (no_cp932ext_f && (
- (val>>8) == 0x2D || /* NEC special characters */
- val > NKF_INT32_C(0xF300) /* IBM extended characters */
- )) return 1;
-
- c2 = val >> 8;
- if (val > 0x7FFF){
- c2 &= 0x7f;
- c2 |= PREFIX_EUCG3;
- }
- if (c2 == SO) c2 = JIS_X_0201_1976_K;
- c1 = val & 0xFF;
- if (p2) *p2 = c2;
- if (p1) *p1 = c1;
- return 0;
-}
-
-static int
-unicode_to_jis_common(nkf_char c2, nkf_char c1, nkf_char c0, nkf_char *p2, nkf_char *p1)
-{
- const unsigned short *const *pp;
- const unsigned short *const *const *ppp;
- static const char no_best_fit_chars_table_C2[] =
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 2, 1, 1, 2,
- 0, 0, 1, 1, 0, 1, 0, 1, 2, 1, 1, 1, 1, 1, 1, 1};
- static const char no_best_fit_chars_table_C2_ms[] =
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0,
- 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0};
- static const char no_best_fit_chars_table_932_C2[] =
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1,
- 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0};
- static const char no_best_fit_chars_table_932_C3[] =
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1};
- nkf_char ret = 0;
-
- if(c2 < 0x80){
- *p2 = 0;
- *p1 = c2;
- }else if(c2 < 0xe0){
- if(no_best_fit_chars_f){
- if(ms_ucs_map_f == UCS_MAP_CP932){
- switch(c2){
- case 0xC2:
- if(no_best_fit_chars_table_932_C2[c1&0x3F]) return 1;
- break;
- case 0xC3:
- if(no_best_fit_chars_table_932_C3[c1&0x3F]) return 1;
- break;
- }
- }else if(!cp932inv_f){
- switch(c2){
- case 0xC2:
- if(no_best_fit_chars_table_C2[c1&0x3F]) return 1;
- break;
- case 0xC3:
- if(no_best_fit_chars_table_932_C3[c1&0x3F]) return 1;
- break;
- }
- }else if(ms_ucs_map_f == UCS_MAP_MS){
- if(c2 == 0xC2 && no_best_fit_chars_table_C2_ms[c1&0x3F]) return 1;
- }else if(ms_ucs_map_f == UCS_MAP_CP10001){
- switch(c2){
- case 0xC2:
- switch(c1){
- case 0xA2:
- case 0xA3:
- case 0xA5:
- case 0xA6:
- case 0xAC:
- case 0xAF:
- case 0xB8:
- return 1;
- }
- break;
- }
- }
- }
- pp =
- ms_ucs_map_f == UCS_MAP_CP932 ? utf8_to_euc_2bytes_932 :
- ms_ucs_map_f == UCS_MAP_MS ? utf8_to_euc_2bytes_ms :
- ms_ucs_map_f == UCS_MAP_CP10001 ? utf8_to_euc_2bytes_mac :
- x0213_f ? utf8_to_euc_2bytes_x0213 :
- utf8_to_euc_2bytes;
- ret = unicode_to_jis_common2(c2, c1, pp, sizeof_utf8_to_euc_2bytes, p2, p1);
- }else if(c0 < 0xF0){
- if(no_best_fit_chars_f){
- if(ms_ucs_map_f == UCS_MAP_CP932){
- if(c2 == 0xE3 && c1 == 0x82 && c0 == 0x94) return 1;
- }else if(ms_ucs_map_f == UCS_MAP_MS){
- switch(c2){
- case 0xE2:
- switch(c1){
- case 0x80:
- if(c0 == 0x94 || c0 == 0x96 || c0 == 0xBE) return 1;
- break;
- case 0x88:
- if(c0 == 0x92) return 1;
- break;
- }
- break;
- case 0xE3:
- if(c1 == 0x80 || c0 == 0x9C) return 1;
- break;
- }
- }else if(ms_ucs_map_f == UCS_MAP_CP10001){
- switch(c2){
- case 0xE3:
- switch(c1){
- case 0x82:
- if(c0 == 0x94) return 1;
- break;
- case 0x83:
- if(c0 == 0xBB) return 1;
- break;
- }
- break;
- }
- }else{
- switch(c2){
- case 0xE2:
- switch(c1){
- case 0x80:
- if(c0 == 0x95) return 1;
- break;
- case 0x88:
- if(c0 == 0xA5) return 1;
- break;
- }
- break;
- case 0xEF:
- switch(c1){
- case 0xBC:
- if(c0 == 0x8D) return 1;
- break;
- case 0xBD:
- if(c0 == 0x9E && !cp932inv_f) return 1;
- break;
- case 0xBF:
- if(0xA0 <= c0 && c0 <= 0xA5) return 1;
- break;
- }
- break;
- }
- }
- }
- ppp =
- ms_ucs_map_f == UCS_MAP_CP932 ? utf8_to_euc_3bytes_932 :
- ms_ucs_map_f == UCS_MAP_MS ? utf8_to_euc_3bytes_ms :
- ms_ucs_map_f == UCS_MAP_CP10001 ? utf8_to_euc_3bytes_mac :
- x0213_f ? utf8_to_euc_3bytes_x0213 :
- utf8_to_euc_3bytes;
- ret = unicode_to_jis_common2(c1, c0, ppp[c2 - 0xE0], sizeof_utf8_to_euc_C2, p2, p1);
- }else return -1;
-#ifdef SHIFTJIS_CP932
- if (!ret&& is_eucg3(*p2)) {
- if (cp932inv_f) {
- if (encode_fallback) ret = 1;
- }
- else {
- nkf_char s2, s1;
- if (e2s_conv(*p2, *p1, &s2, &s1) == 0) {
- s2e_conv(s2, s1, p2, p1);
- }else{
- ret = 1;
- }
- }
- }
-#endif
- return ret;
-}
-
-#ifdef UTF8_OUTPUT_ENABLE
-#define X0213_SURROGATE_FIND(tbl, size, euc) do { \
- int i; \
- for (i = 0; i < size; i++) \
- if (tbl[i][0] == euc) { \
- low = tbl[i][2]; \
- break; \
- } \
- } while (0)
-
-static nkf_char
-e2w_conv(nkf_char c2, nkf_char c1)
-{
- const unsigned short *p;
-
- if (c2 == JIS_X_0201_1976_K) {
- if (ms_ucs_map_f == UCS_MAP_CP10001) {
- switch (c1) {
- case 0x20:
- return 0xA0;
- case 0x7D:
- return 0xA9;
- }
- }
- p = euc_to_utf8_1byte;
-#ifdef X0212_ENABLE
- } else if (is_eucg3(c2)){
- if(ms_ucs_map_f == UCS_MAP_ASCII&& c2 == NKF_INT32_C(0x8F22) && c1 == 0x43){
- return 0xA6;
- }
- c2 = (c2&0x7f) - 0x21;
- if (0<=c2 && c2<sizeof_euc_to_utf8_2bytes)
- p =
- x0213_f ? x0212_to_utf8_2bytes_x0213[c2] :
- x0212_to_utf8_2bytes[c2];
- else
- return 0;
-#endif
- } else {
- c2 &= 0x7f;
- c2 = (c2&0x7f) - 0x21;
- if (0<=c2 && c2<sizeof_euc_to_utf8_2bytes)
- p =
- x0213_f ? euc_to_utf8_2bytes_x0213[c2] :
- ms_ucs_map_f == UCS_MAP_ASCII ? euc_to_utf8_2bytes[c2] :
- ms_ucs_map_f == UCS_MAP_CP10001 ? euc_to_utf8_2bytes_mac[c2] :
- euc_to_utf8_2bytes_ms[c2];
- else
- return 0;
- }
- if (!p) return 0;
- c1 = (c1 & 0x7f) - 0x21;
- if (0<=c1 && c1<sizeof_euc_to_utf8_1byte) {
- nkf_char val = p[c1];
- if (x0213_f && 0xD800<=val && val<=0xDBFF) {
- nkf_char euc = (c2+0x21)<<8 | (c1+0x21);
- nkf_char low = 0;
- if (p==x0212_to_utf8_2bytes_x0213[c2]) {
- X0213_SURROGATE_FIND(x0213_2_surrogate_table, sizeof_x0213_2_surrogate_table, euc);
- } else {
- X0213_SURROGATE_FIND(x0213_1_surrogate_table, sizeof_x0213_1_surrogate_table, euc);
- }
- if (!low) return 0;
- return UTF16_TO_UTF32(val, low);
- } else {
- return val;
- }
- }
- return 0;
-}
-
-static nkf_char
-e2w_combining(nkf_char comb, nkf_char c2, nkf_char c1)
-{
- nkf_char euc;
- int i;
- for (i = 0; i < sizeof_x0213_combining_chars; i++)
- if (x0213_combining_chars[i] == comb)
- break;
- if (i >= sizeof_x0213_combining_chars)
- return 0;
- euc = (c2&0x7f)<<8 | (c1&0x7f);
- for (i = 0; i < sizeof_x0213_combining_table; i++)
- if (x0213_combining_table[i][0] == euc)
- return x0213_combining_table[i][1];
- return 0;
-}
-#endif
-
-static nkf_char
-w2e_conv(nkf_char c2, nkf_char c1, nkf_char c0, nkf_char *p2, nkf_char *p1)
-{
- nkf_char ret = 0;
-
- if (!c1){
- *p2 = 0;
- *p1 = c2;
- }else if (0xc0 <= c2 && c2 <= 0xef) {
- ret = unicode_to_jis_common(c2, c1, c0, p2, p1);
-#ifdef NUMCHAR_OPTION
- if (ret > 0){
- if (p2) *p2 = 0;
- if (p1) *p1 = nkf_char_unicode_new(nkf_utf8_to_unicode(c2, c1, c0, 0));
- ret = 0;
- }
-#endif
- }
- return ret;
-}
-
-#ifdef UTF8_INPUT_ENABLE
-static nkf_char
-w16e_conv(nkf_char val, nkf_char *p2, nkf_char *p1)
-{
- nkf_char c1, c2, c3, c4;
- nkf_char ret = 0;
- val &= VALUE_MASK;
- if (val < 0x80) {
- *p2 = 0;
- *p1 = val;
- }
- else if (nkf_char_unicode_bmp_p(val)){
- nkf_unicode_to_utf8(val, &c1, &c2, &c3, &c4);
- ret = unicode_to_jis_common(c1, c2, c3, p2, p1);
- if (ret > 0){
- *p2 = 0;
- *p1 = nkf_char_unicode_new(val);
- ret = 0;
- }
- }
- else {
- int i;
- if (x0213_f) {
- c1 = (val >> 10) + NKF_INT32_C(0xD7C0); /* high surrogate */
- c2 = (val & 0x3FF) + NKF_INT32_C(0xDC00); /* low surrogate */
- for (i = 0; i < sizeof_x0213_1_surrogate_table; i++)
- if (x0213_1_surrogate_table[i][1] == c1 && x0213_1_surrogate_table[i][2] == c2) {
- val = x0213_1_surrogate_table[i][0];
- *p2 = val >> 8;
- *p1 = val & 0xFF;
- return 0;
- }
- for (i = 0; i < sizeof_x0213_2_surrogate_table; i++)
- if (x0213_2_surrogate_table[i][1] == c1 && x0213_2_surrogate_table[i][2] == c2) {
- val = x0213_2_surrogate_table[i][0];
- *p2 = PREFIX_EUCG3 | (val >> 8);
- *p1 = val & 0xFF;
- return 0;
- }
- }
- *p2 = 0;
- *p1 = nkf_char_unicode_new(val);
- }
- return ret;
-}
-#endif
-
-static nkf_char
-e_iconv(nkf_char c2, nkf_char c1, nkf_char c0)
-{
- if (c2 == JIS_X_0201_1976_K || c2 == SS2){
- if (iso2022jp_f && !x0201_f) {
- c2 = GETA1; c1 = GETA2;
- } else {
- c2 = JIS_X_0201_1976_K;
- c1 &= 0x7f;
- }
-#ifdef X0212_ENABLE
- }else if (c2 == 0x8f){
- if (c0 == 0){
- return -1;
- }
- if (!cp51932_f && !x0213_f && 0xF5 <= c1 && c1 <= 0xFE && 0xA1 <= c0 && c0 <= 0xFE) {
- /* encoding is eucJP-ms, so invert to Unicode Private User Area */
- c1 = nkf_char_unicode_new((c1 - 0xF5) * 94 + c0 - 0xA1 + 0xE3AC);
- c2 = 0;
- } else {
- c2 = (c2 << 8) | (c1 & 0x7f);
- c1 = c0 & 0x7f;
-#ifdef SHIFTJIS_CP932
- if (cp51932_f){
- nkf_char s2, s1;
- if (e2s_conv(c2, c1, &s2, &s1) == 0){
- s2e_conv(s2, s1, &c2, &c1);
- if (c2 < 0x100){
- c1 &= 0x7f;
- c2 &= 0x7f;
- }
- }
- }
-#endif /* SHIFTJIS_CP932 */
- }
-#endif /* X0212_ENABLE */
- } else if ((c2 == EOF) || (c2 == 0) || c2 < SP || c2 == ISO_8859_1) {
- /* NOP */
- } else {
- if (!cp51932_f && ms_ucs_map_f && 0xF5 <= c2 && c2 <= 0xFE && 0xA1 <= c1 && c1 <= 0xFE) {
- /* encoding is eucJP-ms, so invert to Unicode Private User Area */
- c1 = nkf_char_unicode_new((c2 - 0xF5) * 94 + c1 - 0xA1 + 0xE000);
- c2 = 0;
- } else {
- c1 &= 0x7f;
- c2 &= 0x7f;
-#ifdef SHIFTJIS_CP932
- if (cp51932_f && 0x79 <= c2 && c2 <= 0x7c){
- nkf_char s2, s1;
- if (e2s_conv(c2, c1, &s2, &s1) == 0){
- s2e_conv(s2, s1, &c2, &c1);
- if (c2 < 0x100){
- c1 &= 0x7f;
- c2 &= 0x7f;
- }
- }
- }
-#endif /* SHIFTJIS_CP932 */
- }
- }
- (*oconv)(c2, c1);
- return 0;
-}
-
-static nkf_char
-s_iconv(ARG_UNUSED nkf_char c2, nkf_char c1, ARG_UNUSED nkf_char c0)
-{
- if (c2 == JIS_X_0201_1976_K || (0xA1 <= c2 && c2 <= 0xDF)) {
- if (iso2022jp_f && !x0201_f) {
- c2 = GETA1; c1 = GETA2;
- } else {
- c1 &= 0x7f;
- }
- } else if ((c2 == EOF) || (c2 == 0) || c2 < SP) {
- /* NOP */
- } else if (!x0213_f && 0xF0 <= c2 && c2 <= 0xF9 && 0x40 <= c1 && c1 <= 0xFC) {
- /* CP932 UDC */
- if(c1 == 0x7F) return 0;
- c1 = nkf_char_unicode_new((c2 - 0xF0) * 188 + (c1 - 0x40 - (0x7E < c1)) + 0xE000);
- c2 = 0;
- } else {
- nkf_char ret = s2e_conv(c2, c1, &c2, &c1);
- if (ret) return ret;
- }
- (*oconv)(c2, c1);
- return 0;
-}
-
-static int
-x0213_wait_combining_p(nkf_char wc)
-{
- int i;
- for (i = 0; i < sizeof_x0213_combining_table; i++) {
- if (x0213_combining_table[i][1] == wc) {
- return TRUE;
- }
- }
- return FALSE;
-}
-
-static int
-x0213_combining_p(nkf_char wc)
-{
- int i;
- for (i = 0; i < sizeof_x0213_combining_chars; i++) {
- if (x0213_combining_chars[i] == wc) {
- return TRUE;
- }
- }
- return FALSE;
-}
-
-static nkf_char
-w_iconv(nkf_char c1, nkf_char c2, nkf_char c3)
-{
- nkf_char ret = 0, c4 = 0;
- static const char w_iconv_utf8_1st_byte[] =
- { /* 0xC0 - 0xFF */
- 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
- 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
- 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 33,
- 40, 41, 41, 41, 42, 43, 43, 43, 50, 50, 50, 50, 60, 60, 70, 70};
-
- if (c3 > 0xFF) {
- c4 = c3 & 0xFF;
- c3 >>= 8;
- }
-
- if (c1 < 0 || 0xff < c1) {
- }else if (c1 == 0) { /* 0 : 1 byte*/
- c3 = 0;
- } else if ((c1 & 0xC0) == 0x80) { /* 0x80-0xbf : trail byte */
- return 0;
- } else{
- switch (w_iconv_utf8_1st_byte[c1 - 0xC0]) {
- case 21:
- if (c2 < 0x80 || 0xBF < c2) return 0;
- break;
- case 30:
- if (c3 == 0) return -1;
- if (c2 < 0xA0 || 0xBF < c2 || (c3 & 0xC0) != 0x80)
- return 0;
- break;
- case 31:
- case 33:
- if (c3 == 0) return -1;
- if ((c2 & 0xC0) != 0x80 || (c3 & 0xC0) != 0x80)
- return 0;
- break;
- case 32:
- if (c3 == 0) return -1;
- if (c2 < 0x80 || 0x9F < c2 || (c3 & 0xC0) != 0x80)
- return 0;
- break;
- case 40:
- if (c3 == 0) return -2;
- if (c2 < 0x90 || 0xBF < c2 || (c3 & 0xC0) != 0x80 || (c4 & 0xC0) != 0x80)
- return 0;
- break;
- case 41:
- if (c3 == 0) return -2;
- if (c2 < 0x80 || 0xBF < c2 || (c3 & 0xC0) != 0x80 || (c4 & 0xC0) != 0x80)
- return 0;
- break;
- case 42:
- if (c3 == 0) return -2;
- if (c2 < 0x80 || 0x8F < c2 || (c3 & 0xC0) != 0x80 || (c4 & 0xC0) != 0x80)
- return 0;
- break;
- default:
- return 0;
- break;
- }
- }
- if (c1 == 0 || c1 == EOF){
- } else if ((c1 & 0xf8) == 0xf0) { /* 4 bytes */
- c2 = nkf_char_unicode_new(nkf_utf8_to_unicode(c1, c2, c3, c4));
- c1 = 0;
- } else {
- if (x0213_f && x0213_wait_combining_p(nkf_utf8_to_unicode(c1, c2, c3, c4)))
- return -3;
- ret = w2e_conv(c1, c2, c3, &c1, &c2);
- }
- if (ret == 0){
- (*oconv)(c1, c2);
- }
- return ret;
-}
-
-static nkf_char
-w_iconv_nocombine(nkf_char c1, nkf_char c2, nkf_char c3)
-{
- /* continue from the line below 'return -3;' in w_iconv() */
- nkf_char ret = w2e_conv(c1, c2, c3, &c1, &c2);
- if (ret == 0){
- (*oconv)(c1, c2);
- }
- return ret;
-}
-
-#define NKF_ICONV_INVALID_CODE_RANGE -13
-#define NKF_ICONV_WAIT_COMBINING_CHAR -14
-#define NKF_ICONV_NOT_COMBINED -15
-static size_t
-unicode_iconv(nkf_char wc, int nocombine)
-{
- nkf_char c1, c2;
- int ret = 0;
-
- if (wc < 0x80) {
- c2 = 0;
- c1 = wc;
- }else if ((wc>>11) == 27) {
- /* unpaired surrogate */
- return NKF_ICONV_INVALID_CODE_RANGE;
- }else if (wc < 0xFFFF) {
- if (!nocombine && x0213_f && x0213_wait_combining_p(wc))
- return NKF_ICONV_WAIT_COMBINING_CHAR;
- ret = w16e_conv(wc, &c2, &c1);
- if (ret) return ret;
- }else if (wc < 0x10FFFF) {
- c2 = 0;
- c1 = nkf_char_unicode_new(wc);
- } else {
- return NKF_ICONV_INVALID_CODE_RANGE;
- }
- (*oconv)(c2, c1);
- return 0;
-}
-
-static nkf_char
-unicode_iconv_combine(nkf_char wc, nkf_char wc2)
-{
- nkf_char c1, c2;
- int i;
-
- if (wc2 < 0x80) {
- return NKF_ICONV_NOT_COMBINED;
- }else if ((wc2>>11) == 27) {
- /* unpaired surrogate */
- return NKF_ICONV_INVALID_CODE_RANGE;
- }else if (wc2 < 0xFFFF) {
- if (!x0213_combining_p(wc2))
- return NKF_ICONV_NOT_COMBINED;
- for (i = 0; i < sizeof_x0213_combining_table; i++) {
- if (x0213_combining_table[i][1] == wc &&
- x0213_combining_table[i][2] == wc2) {
- c2 = x0213_combining_table[i][0] >> 8;
- c1 = x0213_combining_table[i][0] & 0x7f;
- (*oconv)(c2, c1);
- return 0;
- }
- }
- }else if (wc2 < 0x10FFFF) {
- return NKF_ICONV_NOT_COMBINED;
- } else {
- return NKF_ICONV_INVALID_CODE_RANGE;
- }
- return NKF_ICONV_NOT_COMBINED;
-}
-
-static nkf_char
-w_iconv_combine(nkf_char c1, nkf_char c2, nkf_char c3, nkf_char c4, nkf_char c5, nkf_char c6)
-{
- nkf_char wc, wc2;
- wc = nkf_utf8_to_unicode(c1, c2, c3, 0);
- wc2 = nkf_utf8_to_unicode(c4, c5, c6, 0);
- if (wc2 < 0)
- return wc2;
- return unicode_iconv_combine(wc, wc2);
-}
-
-#define NKF_ICONV_NEED_ONE_MORE_BYTE (size_t)-1
-#define NKF_ICONV_NEED_TWO_MORE_BYTES (size_t)-2
-static size_t
-nkf_iconv_utf_16(nkf_char c1, nkf_char c2, nkf_char c3, nkf_char c4)
-{
- nkf_char wc;
-
- if (c1 == EOF) {
- (*oconv)(EOF, 0);
- return 0;
- }
-
- if (input_endian == ENDIAN_BIG) {
- if (0xD8 <= c1 && c1 <= 0xDB) {
- if (0xDC <= c3 && c3 <= 0xDF) {
- wc = UTF16_TO_UTF32(c1 << 8 | c2, c3 << 8 | c4);
- } else return NKF_ICONV_NEED_TWO_MORE_BYTES;
- } else {
- wc = c1 << 8 | c2;
- }
- } else {
- if (0xD8 <= c2 && c2 <= 0xDB) {
- if (0xDC <= c4 && c4 <= 0xDF) {
- wc = UTF16_TO_UTF32(c2 << 8 | c1, c4 << 8 | c3);
- } else return NKF_ICONV_NEED_TWO_MORE_BYTES;
- } else {
- wc = c2 << 8 | c1;
- }
- }
-
- return (*unicode_iconv)(wc, FALSE);
-}
-
-static size_t
-nkf_iconv_utf_16_combine(nkf_char c1, nkf_char c2, nkf_char c3, nkf_char c4)
-{
- nkf_char wc, wc2;
-
- if (input_endian == ENDIAN_BIG) {
- if (0xD8 <= c3 && c3 <= 0xDB) {
- return NKF_ICONV_NOT_COMBINED;
- } else {
- wc = c1 << 8 | c2;
- wc2 = c3 << 8 | c4;
- }
- } else {
- if (0xD8 <= c2 && c2 <= 0xDB) {
- return NKF_ICONV_NOT_COMBINED;
- } else {
- wc = c2 << 8 | c1;
- wc2 = c4 << 8 | c3;
- }
- }
-
- return unicode_iconv_combine(wc, wc2);
-}
-
-static size_t
-nkf_iconv_utf_16_nocombine(nkf_char c1, nkf_char c2)
-{
- nkf_char wc;
- if (input_endian == ENDIAN_BIG)
- wc = c1 << 8 | c2;
- else
- wc = c2 << 8 | c1;
- return (*unicode_iconv)(wc, TRUE);
-}
-
-static nkf_char
-w_iconv16(nkf_char c2, nkf_char c1, ARG_UNUSED nkf_char c0)
-{
- (*oconv)(c2, c1);
- return 16; /* different from w_iconv32 */
-}
-
-static nkf_char
-w_iconv32(nkf_char c2, nkf_char c1, ARG_UNUSED nkf_char c0)
-{
- (*oconv)(c2, c1);
- return 32; /* different from w_iconv16 */
-}
-
-static nkf_char
-utf32_to_nkf_char(nkf_char c1, nkf_char c2, nkf_char c3, nkf_char c4)
-{
- nkf_char wc;
-
- switch(input_endian){
- case ENDIAN_BIG:
- wc = c2 << 16 | c3 << 8 | c4;
- break;
- case ENDIAN_LITTLE:
- wc = c3 << 16 | c2 << 8 | c1;
- break;
- case ENDIAN_2143:
- wc = c1 << 16 | c4 << 8 | c3;
- break;
- case ENDIAN_3412:
- wc = c4 << 16 | c1 << 8 | c2;
- break;
- default:
- return NKF_ICONV_INVALID_CODE_RANGE;
- }
- return wc;
-}
-
-static size_t
-nkf_iconv_utf_32(nkf_char c1, nkf_char c2, nkf_char c3, nkf_char c4)
-{
- nkf_char wc;
-
- if (c1 == EOF) {
- (*oconv)(EOF, 0);
- return 0;
- }
-
- wc = utf32_to_nkf_char(c1, c2, c3, c4);
- if (wc < 0)
- return wc;
-
- return (*unicode_iconv)(wc, FALSE);
-}
-
-static nkf_char
-nkf_iconv_utf_32_combine(nkf_char c1, nkf_char c2, nkf_char c3, nkf_char c4, nkf_char c5, nkf_char c6, nkf_char c7, nkf_char c8)
-{
- nkf_char wc, wc2;
-
- wc = utf32_to_nkf_char(c1, c2, c3, c4);
- if (wc < 0)
- return wc;
- wc2 = utf32_to_nkf_char(c5, c6, c7, c8);
- if (wc2 < 0)
- return wc2;
-
- return unicode_iconv_combine(wc, wc2);
-}
-
-static size_t
-nkf_iconv_utf_32_nocombine(nkf_char c1, nkf_char c2, nkf_char c3, nkf_char c4)
-{
- nkf_char wc;
-
- wc = utf32_to_nkf_char(c1, c2, c3, c4);
- return (*unicode_iconv)(wc, TRUE);
-}
-#endif
-
-#define output_ascii_escape_sequence(mode) do { \
- if (output_mode != ASCII && output_mode != ISO_8859_1) { \
- (*o_putc)(ESC); \
- (*o_putc)('('); \
- (*o_putc)(ascii_intro); \
- output_mode = mode; \
- } \
- } while (0)
-
-static void
-output_escape_sequence(int mode)
-{
- if (output_mode == mode)
- return;
- switch(mode) {
- case ISO_8859_1:
- (*o_putc)(ESC);
- (*o_putc)('.');
- (*o_putc)('A');
- break;
- case JIS_X_0201_1976_K:
- (*o_putc)(ESC);
- (*o_putc)('(');
- (*o_putc)('I');
- break;
- case JIS_X_0208:
- (*o_putc)(ESC);
- (*o_putc)('$');
- (*o_putc)(kanji_intro);
- break;
- case JIS_X_0212:
- (*o_putc)(ESC);
- (*o_putc)('$');
- (*o_putc)('(');
- (*o_putc)('D');
- break;
- case JIS_X_0213_1:
- (*o_putc)(ESC);
- (*o_putc)('$');
- (*o_putc)('(');
- (*o_putc)('Q');
- break;
- case JIS_X_0213_2:
- (*o_putc)(ESC);
- (*o_putc)('$');
- (*o_putc)('(');
- (*o_putc)('P');
- break;
- }
- output_mode = mode;
-}
-
-static void
-j_oconv(nkf_char c2, nkf_char c1)
-{
-#ifdef NUMCHAR_OPTION
- if (c2 == 0 && nkf_char_unicode_p(c1)){
- w16e_conv(c1, &c2, &c1);
- if (c2 == 0 && nkf_char_unicode_p(c1)){
- c2 = c1 & VALUE_MASK;
- if (ms_ucs_map_f && 0xE000 <= c2 && c2 <= 0xE757) {
- /* CP5022x UDC */
- c1 &= 0xFFF;
- c2 = 0x7F + c1 / 94;
- c1 = 0x21 + c1 % 94;
- } else {
- if (encode_fallback) (*encode_fallback)(c1);
- return;
- }
- }
- }
-#endif
- if (c2 == 0) {
- output_ascii_escape_sequence(ASCII);
- (*o_putc)(c1);
- }
- else if (c2 == EOF) {
- output_ascii_escape_sequence(ASCII);
- (*o_putc)(EOF);
- }
- else if (c2 == ISO_8859_1) {
- output_ascii_escape_sequence(ISO_8859_1);
- (*o_putc)(c1|0x80);
- }
- else if (c2 == JIS_X_0201_1976_K) {
- output_escape_sequence(JIS_X_0201_1976_K);
- (*o_putc)(c1);
-#ifdef X0212_ENABLE
- } else if (is_eucg3(c2)){
- output_escape_sequence(x0213_f ? JIS_X_0213_2 : JIS_X_0212);
- (*o_putc)(c2 & 0x7f);
- (*o_putc)(c1);
-#endif
- } else {
- if(ms_ucs_map_f
- ? c2<0x20 || 0x92<c2 || c1<0x20 || 0x7e<c1
- : c2<0x20 || 0x7e<c2 || c1<0x20 || 0x7e<c1) return;
- output_escape_sequence(x0213_f ? JIS_X_0213_1 : JIS_X_0208);
- (*o_putc)(c2);
- (*o_putc)(c1);
- }
-}
-
-static void
-e_oconv(nkf_char c2, nkf_char c1)
-{
- if (c2 == 0 && nkf_char_unicode_p(c1)){
- w16e_conv(c1, &c2, &c1);
- if (c2 == 0 && nkf_char_unicode_p(c1)){
- c2 = c1 & VALUE_MASK;
- if (x0212_f && 0xE000 <= c2 && c2 <= 0xE757) {
- /* eucJP-ms UDC */
- c1 &= 0xFFF;
- c2 = c1 / 94;
- c2 += c2 < 10 ? 0x75 : 0x8FEB;
- c1 = 0x21 + c1 % 94;
- if (is_eucg3(c2)){
- (*o_putc)(0x8f);
- (*o_putc)((c2 & 0x7f) | 0x080);
- (*o_putc)(c1 | 0x080);
- }else{
- (*o_putc)((c2 & 0x7f) | 0x080);
- (*o_putc)(c1 | 0x080);
- }
- return;
- } else {
- if (encode_fallback) (*encode_fallback)(c1);
- return;
- }
- }
- }
-
- if (c2 == EOF) {
- (*o_putc)(EOF);
- } else if (c2 == 0) {
- output_mode = ASCII;
- (*o_putc)(c1);
- } else if (c2 == JIS_X_0201_1976_K) {
- output_mode = EUC_JP;
- (*o_putc)(SS2); (*o_putc)(c1|0x80);
- } else if (c2 == ISO_8859_1) {
- output_mode = ISO_8859_1;
- (*o_putc)(c1 | 0x080);
-#ifdef X0212_ENABLE
- } else if (is_eucg3(c2)){
- output_mode = EUC_JP;
-#ifdef SHIFTJIS_CP932
- if (!cp932inv_f){
- nkf_char s2, s1;
- if (e2s_conv(c2, c1, &s2, &s1) == 0){
- s2e_conv(s2, s1, &c2, &c1);
- }
- }
-#endif
- if (c2 == 0) {
- output_mode = ASCII;
- (*o_putc)(c1);
- }else if (is_eucg3(c2)){
- if (x0212_f){
- (*o_putc)(0x8f);
- (*o_putc)((c2 & 0x7f) | 0x080);
- (*o_putc)(c1 | 0x080);
- }
- }else{
- (*o_putc)((c2 & 0x7f) | 0x080);
- (*o_putc)(c1 | 0x080);
- }
-#endif
- } else {
- if (!nkf_isgraph(c1) || !nkf_isgraph(c2)) {
- set_iconv(FALSE, 0);
- return; /* too late to rescue this char */
- }
- output_mode = EUC_JP;
- (*o_putc)(c2 | 0x080);
- (*o_putc)(c1 | 0x080);
- }
-}
-
-static void
-s_oconv(nkf_char c2, nkf_char c1)
-{
-#ifdef NUMCHAR_OPTION
- if (c2 == 0 && nkf_char_unicode_p(c1)){
- w16e_conv(c1, &c2, &c1);
- if (c2 == 0 && nkf_char_unicode_p(c1)){
- c2 = c1 & VALUE_MASK;
- if (!x0213_f && 0xE000 <= c2 && c2 <= 0xE757) {
- /* CP932 UDC */
- c1 &= 0xFFF;
- c2 = c1 / 188 + (cp932inv_f ? 0xF0 : 0xEB);
- c1 = c1 % 188;
- c1 += 0x40 + (c1 > 0x3e);
- (*o_putc)(c2);
- (*o_putc)(c1);
- return;
- } else {
- if(encode_fallback)(*encode_fallback)(c1);
- return;
- }
- }
- }
-#endif
- if (c2 == EOF) {
- (*o_putc)(EOF);
- return;
- } else if (c2 == 0) {
- output_mode = ASCII;
- (*o_putc)(c1);
- } else if (c2 == JIS_X_0201_1976_K) {
- output_mode = SHIFT_JIS;
- (*o_putc)(c1|0x80);
- } else if (c2 == ISO_8859_1) {
- output_mode = ISO_8859_1;
- (*o_putc)(c1 | 0x080);
-#ifdef X0212_ENABLE
- } else if (is_eucg3(c2)){
- output_mode = SHIFT_JIS;
- if (e2s_conv(c2, c1, &c2, &c1) == 0){
- (*o_putc)(c2);
- (*o_putc)(c1);
- }
-#endif
- } else {
- if (!nkf_isprint(c1) || !nkf_isprint(c2)) {
- set_iconv(FALSE, 0);
- return; /* too late to rescue this char */
- }
- output_mode = SHIFT_JIS;
- e2s_conv(c2, c1, &c2, &c1);
-
-#ifdef SHIFTJIS_CP932
- if (cp932inv_f
- && CP932INV_TABLE_BEGIN <= c2 && c2 <= CP932INV_TABLE_END){
- nkf_char c = cp932inv[c2 - CP932INV_TABLE_BEGIN][c1 - 0x40];
- if (c){
- c2 = c >> 8;
- c1 = c & 0xff;
- }
- }
-#endif /* SHIFTJIS_CP932 */
-
- (*o_putc)(c2);
- if (prefix_table[(unsigned char)c1]){
- (*o_putc)(prefix_table[(unsigned char)c1]);
- }
- (*o_putc)(c1);
- }
-}
-
-#ifdef UTF8_OUTPUT_ENABLE
-#define OUTPUT_UTF8(val) do { \
- nkf_unicode_to_utf8(val, &c1, &c2, &c3, &c4); \
- (*o_putc)(c1); \
- if (c2) (*o_putc)(c2); \
- if (c3) (*o_putc)(c3); \
- if (c4) (*o_putc)(c4); \
- } while (0)
-
-static void
-w_oconv(nkf_char c2, nkf_char c1)
-{
- nkf_char c3, c4;
- nkf_char val, val2;
-
- if (output_bom_f) {
- output_bom_f = FALSE;
- (*o_putc)('\357');
- (*o_putc)('\273');
- (*o_putc)('\277');
- }
-
- if (c2 == EOF) {
- (*o_putc)(EOF);
- return;
- }
-
- if (c2 == 0 && nkf_char_unicode_p(c1)){
- val = c1 & VALUE_MASK;
- OUTPUT_UTF8(val);
- return;
- }
-
- if (c2 == 0) {
- (*o_putc)(c1);
- } else {
- val = e2w_conv(c2, c1);
- if (val){
- val2 = e2w_combining(val, c2, c1);
- if (val2)
- OUTPUT_UTF8(val2);
- OUTPUT_UTF8(val);
- }
- }
-}
-
-#define OUTPUT_UTF16_BYTES(c1, c2) do { \
- if (output_endian == ENDIAN_LITTLE){ \
- (*o_putc)(c1); \
- (*o_putc)(c2); \
- }else{ \
- (*o_putc)(c2); \
- (*o_putc)(c1); \
- } \
- } while (0)
-
-#define OUTPUT_UTF16(val) do { \
- if (nkf_char_unicode_bmp_p(val)) { \
- c2 = (val >> 8) & 0xff; \
- c1 = val & 0xff; \
- OUTPUT_UTF16_BYTES(c1, c2); \
- } else { \
- val &= VALUE_MASK; \
- if (val <= UNICODE_MAX) { \
- c2 = (val >> 10) + NKF_INT32_C(0xD7C0); /* high surrogate */ \
- c1 = (val & 0x3FF) + NKF_INT32_C(0xDC00); /* low surrogate */ \
- OUTPUT_UTF16_BYTES(c2 & 0xff, (c2 >> 8) & 0xff); \
- OUTPUT_UTF16_BYTES(c1 & 0xff, (c1 >> 8) & 0xff); \
- } \
- } \
- } while (0)
-
-static void
-w_oconv16(nkf_char c2, nkf_char c1)
-{
- if (output_bom_f) {
- output_bom_f = FALSE;
- OUTPUT_UTF16_BYTES(0xFF, 0xFE);
- }
-
- if (c2 == EOF) {
- (*o_putc)(EOF);
- return;
- }
-
- if (c2 == 0 && nkf_char_unicode_p(c1)) {
- OUTPUT_UTF16(c1);
- } else if (c2) {
- nkf_char val, val2;
- val = e2w_conv(c2, c1);
- if (!val) return;
- val2 = e2w_combining(val, c2, c1);
- if (val2)
- OUTPUT_UTF16(val2);
- OUTPUT_UTF16(val);
- } else {
- OUTPUT_UTF16_BYTES(c1, c2);
- }
-}
-
-#define OUTPUT_UTF32(c) do { \
- if (output_endian == ENDIAN_LITTLE){ \
- (*o_putc)( (c) & 0xFF); \
- (*o_putc)(((c) >> 8) & 0xFF); \
- (*o_putc)(((c) >> 16) & 0xFF); \
- (*o_putc)(0); \
- }else{ \
- (*o_putc)(0); \
- (*o_putc)(((c) >> 16) & 0xFF); \
- (*o_putc)(((c) >> 8) & 0xFF); \
- (*o_putc)( (c) & 0xFF); \
- } \
- } while (0)
-
-static void
-w_oconv32(nkf_char c2, nkf_char c1)
-{
- if (output_bom_f) {
- output_bom_f = FALSE;
- if (output_endian == ENDIAN_LITTLE){
- (*o_putc)(0xFF);
- (*o_putc)(0xFE);
- (*o_putc)(0);
- (*o_putc)(0);
- }else{
- (*o_putc)(0);
- (*o_putc)(0);
- (*o_putc)(0xFE);
- (*o_putc)(0xFF);
- }
- }
-
- if (c2 == EOF) {
- (*o_putc)(EOF);
- return;
- }
-
- if (c2 == ISO_8859_1) {
- c1 |= 0x80;
- } else if (c2 == 0 && nkf_char_unicode_p(c1)) {
- c1 &= VALUE_MASK;
- } else if (c2) {
- nkf_char val, val2;
- val = e2w_conv(c2, c1);
- if (!val) return;
- val2 = e2w_combining(val, c2, c1);
- if (val2)
- OUTPUT_UTF32(val2);
- c1 = val;
- }
- OUTPUT_UTF32(c1);
-}
-#endif
-
-#define SCORE_L2 (1) /* Kanji Level 2 */
-#define SCORE_KANA (SCORE_L2 << 1) /* Halfwidth Katakana */
-#define SCORE_DEPEND (SCORE_KANA << 1) /* MD Characters */
-#define SCORE_CP932 (SCORE_DEPEND << 1) /* IBM extended characters */
-#define SCORE_X0212 (SCORE_CP932 << 1) /* JIS X 0212 */
-#define SCORE_X0213 (SCORE_X0212 << 1) /* JIS X 0213 */
-#define SCORE_NO_EXIST (SCORE_X0213 << 1) /* Undefined Characters */
-#define SCORE_iMIME (SCORE_NO_EXIST << 1) /* MIME selected */
-#define SCORE_ERROR (SCORE_iMIME << 1) /* Error */
-
-#define SCORE_INIT (SCORE_iMIME)
-
-static const nkf_char score_table_A0[] = {
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, SCORE_DEPEND, SCORE_DEPEND, SCORE_DEPEND,
- SCORE_DEPEND, SCORE_DEPEND, SCORE_DEPEND, SCORE_X0213,
-};
-
-static const nkf_char score_table_F0[] = {
- SCORE_L2, SCORE_L2, SCORE_L2, SCORE_L2,
- SCORE_L2, SCORE_DEPEND, SCORE_X0213, SCORE_X0213,
- SCORE_DEPEND, SCORE_DEPEND, SCORE_CP932, SCORE_CP932,
- SCORE_CP932, SCORE_X0213, SCORE_X0213, SCORE_ERROR,
-};
-
-static const nkf_char score_table_8FA0[] = {
- 0, SCORE_X0213, SCORE_X0212, SCORE_X0213,
- SCORE_X0213, SCORE_X0213, SCORE_X0212, SCORE_X0212,
- SCORE_X0213, SCORE_X0212, SCORE_X0212, SCORE_X0212,
- SCORE_X0213, SCORE_X0213, SCORE_X0213, SCORE_X0213,
-};
-
-static const nkf_char score_table_8FE0[] = {
- SCORE_X0212, SCORE_X0212, SCORE_X0212, SCORE_X0212,
- SCORE_X0212, SCORE_X0212, SCORE_X0212, SCORE_X0212,
- SCORE_X0212, SCORE_X0212, SCORE_X0212, SCORE_X0212,
- SCORE_X0212, SCORE_X0212, SCORE_X0213, SCORE_X0213,
-};
-
-static const nkf_char score_table_8FF0[] = {
- SCORE_X0213, SCORE_X0213, SCORE_X0213, SCORE_X0212,
- SCORE_X0212, SCORE_X0213, SCORE_X0213, SCORE_X0213,
- SCORE_X0213, SCORE_X0213, SCORE_X0213, SCORE_X0213,
- SCORE_X0213, SCORE_X0213, SCORE_X0213, SCORE_X0213,
-};
-
-static void
-set_code_score(struct input_code *ptr, nkf_char score)
-{
- if (ptr){
- ptr->score |= score;
- }
-}
-
-static void
-clr_code_score(struct input_code *ptr, nkf_char score)
-{
- if (ptr){
- ptr->score &= ~score;
- }
-}
-
-static void
-code_score(struct input_code *ptr)
-{
- nkf_char c2 = ptr->buf[0];
- nkf_char c1 = ptr->buf[1];
- if (c2 < 0){
- set_code_score(ptr, SCORE_ERROR);
- }else if (c2 == SS2){
- set_code_score(ptr, SCORE_KANA);
- }else if (c2 == 0x8f){
- if ((c1 & 0x70) == 0x20){
- set_code_score(ptr, score_table_8FA0[c1 & 0x0f]);
- }else if ((c1 & 0x70) == 0x60){
- set_code_score(ptr, score_table_8FE0[c1 & 0x0f]);
- }else if ((c1 & 0x70) == 0x70){
- set_code_score(ptr, score_table_8FF0[c1 & 0x0f]);
- }else{
- set_code_score(ptr, SCORE_X0212);
- }
-#ifdef UTF8_OUTPUT_ENABLE
- }else if (!e2w_conv(c2, c1)){
- set_code_score(ptr, SCORE_NO_EXIST);
-#endif
- }else if ((c2 & 0x70) == 0x20){
- set_code_score(ptr, score_table_A0[c2 & 0x0f]);
- }else if ((c2 & 0x70) == 0x70){
- set_code_score(ptr, score_table_F0[c2 & 0x0f]);
- }else if ((c2 & 0x70) >= 0x50){
- set_code_score(ptr, SCORE_L2);
- }
-}
-
-static void
-status_disable(struct input_code *ptr)
-{
- ptr->stat = -1;
- ptr->buf[0] = -1;
- code_score(ptr);
- if (iconv == ptr->iconv_func) set_iconv(FALSE, 0);
-}
-
-static void
-status_push_ch(struct input_code *ptr, nkf_char c)
-{
- ptr->buf[ptr->index++] = c;
-}
-
-static void
-status_clear(struct input_code *ptr)
-{
- ptr->stat = 0;
- ptr->index = 0;
-}
-
-static void
-status_reset(struct input_code *ptr)
-{
- status_clear(ptr);
- ptr->score = SCORE_INIT;
-}
-
-static void
-status_reinit(struct input_code *ptr)
-{
- status_reset(ptr);
- ptr->_file_stat = 0;
-}
-
-static void
-status_check(struct input_code *ptr, nkf_char c)
-{
- if (c <= DEL && estab_f){
- status_reset(ptr);
- }
-}
-
-static void
-s_status(struct input_code *ptr, nkf_char c)
-{
- switch(ptr->stat){
- case -1:
- status_check(ptr, c);
- break;
- case 0:
- if (c <= DEL){
- break;
- }else if (nkf_char_unicode_p(c)){
- break;
- }else if (0xa1 <= c && c <= 0xdf){
- status_push_ch(ptr, SS2);
- status_push_ch(ptr, c);
- code_score(ptr);
- status_clear(ptr);
- }else if ((0x81 <= c && c < 0xa0) || (0xe0 <= c && c <= 0xea)){
- ptr->stat = 1;
- status_push_ch(ptr, c);
- }else if (0xed <= c && c <= 0xee){
- ptr->stat = 3;
- status_push_ch(ptr, c);
-#ifdef SHIFTJIS_CP932
- }else if (is_ibmext_in_sjis(c)){
- ptr->stat = 2;
- status_push_ch(ptr, c);
-#endif /* SHIFTJIS_CP932 */
-#ifdef X0212_ENABLE
- }else if (0xf0 <= c && c <= 0xfc){
- ptr->stat = 1;
- status_push_ch(ptr, c);
-#endif /* X0212_ENABLE */
- }else{
- status_disable(ptr);
- }
- break;
- case 1:
- if ((0x40 <= c && c <= 0x7e) || (0x80 <= c && c <= 0xfc)){
- status_push_ch(ptr, c);
- s2e_conv(ptr->buf[0], ptr->buf[1], &ptr->buf[0], &ptr->buf[1]);
- code_score(ptr);
- status_clear(ptr);
- }else{
- status_disable(ptr);
- }
- break;
- case 2:
-#ifdef SHIFTJIS_CP932
- if ((0x40 <= c && c <= 0x7e) || (0x80 <= c && c <= 0xfc)) {
- status_push_ch(ptr, c);
- if (s2e_conv(ptr->buf[0], ptr->buf[1], &ptr->buf[0], &ptr->buf[1]) == 0) {
- set_code_score(ptr, SCORE_CP932);
- status_clear(ptr);
- break;
- }
- }
-#endif /* SHIFTJIS_CP932 */
- status_disable(ptr);
- break;
- case 3:
- if ((0x40 <= c && c <= 0x7e) || (0x80 <= c && c <= 0xfc)){
- status_push_ch(ptr, c);
- s2e_conv(ptr->buf[0], ptr->buf[1], &ptr->buf[0], &ptr->buf[1]);
- set_code_score(ptr, SCORE_CP932);
- status_clear(ptr);
- }else{
- status_disable(ptr);
- }
- break;
- }
-}
-
-static void
-e_status(struct input_code *ptr, nkf_char c)
-{
- switch (ptr->stat){
- case -1:
- status_check(ptr, c);
- break;
- case 0:
- if (c <= DEL){
- break;
- }else if (nkf_char_unicode_p(c)){
- break;
- }else if (SS2 == c || (0xa1 <= c && c <= 0xfe)){
- ptr->stat = 1;
- status_push_ch(ptr, c);
-#ifdef X0212_ENABLE
- }else if (0x8f == c){
- ptr->stat = 2;
- status_push_ch(ptr, c);
-#endif /* X0212_ENABLE */
- }else{
- status_disable(ptr);
- }
- break;
- case 1:
- if (0xa1 <= c && c <= 0xfe){
- status_push_ch(ptr, c);
- code_score(ptr);
- status_clear(ptr);
- }else{
- status_disable(ptr);
- }
- break;
-#ifdef X0212_ENABLE
- case 2:
- if (0xa1 <= c && c <= 0xfe){
- ptr->stat = 1;
- status_push_ch(ptr, c);
- }else{
- status_disable(ptr);
- }
-#endif /* X0212_ENABLE */
- }
-}
-
-#ifdef UTF8_INPUT_ENABLE
-static void
-w_status(struct input_code *ptr, nkf_char c)
-{
- switch (ptr->stat){
- case -1:
- status_check(ptr, c);
- break;
- case 0:
- if (c <= DEL){
- break;
- }else if (nkf_char_unicode_p(c)){
- break;
- }else if (0xc0 <= c && c <= 0xdf){
- ptr->stat = 1;
- status_push_ch(ptr, c);
- }else if (0xe0 <= c && c <= 0xef){
- ptr->stat = 2;
- status_push_ch(ptr, c);
- }else if (0xf0 <= c && c <= 0xf4){
- ptr->stat = 3;
- status_push_ch(ptr, c);
- }else{
- status_disable(ptr);
- }
- break;
- case 1:
- case 2:
- if (0x80 <= c && c <= 0xbf){
- status_push_ch(ptr, c);
- if (ptr->index > ptr->stat){
- int bom = (ptr->buf[0] == 0xef && ptr->buf[1] == 0xbb
- && ptr->buf[2] == 0xbf);
- w2e_conv(ptr->buf[0], ptr->buf[1], ptr->buf[2],
- &ptr->buf[0], &ptr->buf[1]);
- if (!bom){
- code_score(ptr);
- }
- status_clear(ptr);
- }
- }else{
- status_disable(ptr);
- }
- break;
- case 3:
- if (0x80 <= c && c <= 0xbf){
- if (ptr->index < ptr->stat){
- status_push_ch(ptr, c);
- } else {
- status_clear(ptr);
- }
- }else{
- status_disable(ptr);
- }
- break;
- }
-}
-#endif
-
-static void
-code_status(nkf_char c)
-{
- int action_flag = 1;
- struct input_code *result = 0;
- struct input_code *p = input_code_list;
- while (p->name){
- if (!p->status_func) {
- ++p;
- continue;
- }
- if (!p->status_func)
- continue;
- (p->status_func)(p, c);
- if (p->stat > 0){
- action_flag = 0;
- }else if(p->stat == 0){
- if (result){
- action_flag = 0;
- }else{
- result = p;
- }
- }
- ++p;
- }
-
- if (action_flag){
- if (result && !estab_f){
- set_iconv(TRUE, result->iconv_func);
- }else if (c <= DEL){
- struct input_code *ptr = input_code_list;
- while (ptr->name){
- status_reset(ptr);
- ++ptr;
- }
- }
- }
-}
-
-typedef struct {
- nkf_buf_t *std_gc_buf;
- nkf_char broken_state;
- nkf_buf_t *broken_buf;
- nkf_char mimeout_state;
- nkf_buf_t *nfc_buf;
-} nkf_state_t;
-
-static nkf_state_t *nkf_state = NULL;
-
-#define STD_GC_BUFSIZE (256)
-
-static void
-nkf_state_init(void)
-{
- if (nkf_state) {
- nkf_buf_clear(nkf_state->std_gc_buf);
- nkf_buf_clear(nkf_state->broken_buf);
- nkf_buf_clear(nkf_state->nfc_buf);
- }
- else {
- nkf_state = nkf_xmalloc(sizeof(nkf_state_t));
- nkf_state->std_gc_buf = nkf_buf_new(STD_GC_BUFSIZE);
- nkf_state->broken_buf = nkf_buf_new(3);
- nkf_state->nfc_buf = nkf_buf_new(9);
- }
- nkf_state->broken_state = 0;
- nkf_state->mimeout_state = 0;
-}
-
-#ifndef WIN32DLL
-static nkf_char
-std_getc(FILE *f)
-{
- if (!nkf_buf_empty_p(nkf_state->std_gc_buf)){
- return nkf_buf_pop(nkf_state->std_gc_buf);
- }
- return getc(f);
-}
-#endif /*WIN32DLL*/
-
-static nkf_char
-std_ungetc(nkf_char c, ARG_UNUSED FILE *f)
-{
- nkf_buf_push(nkf_state->std_gc_buf, c);
- return c;
-}
-
-#ifndef WIN32DLL
-static void
-std_putc(nkf_char c)
-{
- if(c!=EOF)
- putchar(c);
-}
-#endif /*WIN32DLL*/
-
-static nkf_char hold_buf[HOLD_SIZE*2];
-static int hold_count = 0;
-static nkf_char
-push_hold_buf(nkf_char c2)
-{
- if (hold_count >= HOLD_SIZE*2)
- return (EOF);
- hold_buf[hold_count++] = c2;
- return ((hold_count >= HOLD_SIZE*2) ? EOF : hold_count);
-}
-
-static int
-h_conv(FILE *f, nkf_char c1, nkf_char c2)
-{
- int ret;
- int hold_index;
- int fromhold_count;
- nkf_char c3, c4;
-
- /** it must NOT be in the kanji shifte sequence */
- /** it must NOT be written in JIS7 */
- /** and it must be after 2 byte 8bit code */
-
- hold_count = 0;
- push_hold_buf(c1);
- push_hold_buf(c2);
-
- while ((c2 = (*i_getc)(f)) != EOF) {
- if (c2 == ESC){
- (*i_ungetc)(c2,f);
- break;
- }
- code_status(c2);
- if (push_hold_buf(c2) == EOF || estab_f) {
- break;
- }
- }
-
- if (!estab_f) {
- struct input_code *p = input_code_list;
- struct input_code *result = p;
- if (c2 == EOF) {
- code_status(c2);
- }
- while (p->name) {
- if (p->status_func && p->score < result->score) {
- result = p;
- }
- p++;
- }
- set_iconv(TRUE, result->iconv_func);
- }
-
-
- /** now,
- ** 1) EOF is detected, or
- ** 2) Code is established, or
- ** 3) Buffer is FULL (but last word is pushed)
- **
- ** in 1) and 3) cases, we continue to use
- ** Kanji codes by oconv and leave estab_f unchanged.
- **/
-
- ret = c2;
- hold_index = 0;
- while (hold_index < hold_count){
- c1 = hold_buf[hold_index++];
- if (nkf_char_unicode_p(c1)) {
- (*oconv)(0, c1);
- continue;
- }
- else if (c1 <= DEL){
- (*iconv)(0, c1, 0);
- continue;
- }else if (iconv == s_iconv && 0xa1 <= c1 && c1 <= 0xdf){
- (*iconv)(JIS_X_0201_1976_K, c1, 0);
- continue;
- }
- fromhold_count = 1;
- if (hold_index < hold_count){
- c2 = hold_buf[hold_index++];
- fromhold_count++;
- }else{
- c2 = (*i_getc)(f);
- if (c2 == EOF){
- c4 = EOF;
- break;
- }
- code_status(c2);
- }
- c3 = 0;
- switch ((*iconv)(c1, c2, 0)) { /* can be EUC/SJIS/UTF-8 */
- case -2:
- /* 4 bytes UTF-8 */
- if (hold_index < hold_count){
- c3 = hold_buf[hold_index++];
- } else if ((c3 = (*i_getc)(f)) == EOF) {
- ret = EOF;
- break;
- }
- code_status(c3);
- if (hold_index < hold_count){
- c4 = hold_buf[hold_index++];
- } else if ((c4 = (*i_getc)(f)) == EOF) {
- c3 = ret = EOF;
- break;
- }
- code_status(c4);
- (*iconv)(c1, c2, (c3<<8)|c4);
- break;
- case -3:
- /* 4 bytes UTF-8 (check combining character) */
- if (hold_index < hold_count){
- c3 = hold_buf[hold_index++];
- fromhold_count++;
- } else if ((c3 = (*i_getc)(f)) == EOF) {
- w_iconv_nocombine(c1, c2, 0);
- break;
- }
- if (hold_index < hold_count){
- c4 = hold_buf[hold_index++];
- fromhold_count++;
- } else if ((c4 = (*i_getc)(f)) == EOF) {
- w_iconv_nocombine(c1, c2, 0);
- if (fromhold_count <= 2)
- (*i_ungetc)(c3,f);
- else
- hold_index--;
- continue;
- }
- if (w_iconv_combine(c1, c2, 0, c3, c4, 0)) {
- w_iconv_nocombine(c1, c2, 0);
- if (fromhold_count <= 2) {
- (*i_ungetc)(c4,f);
- (*i_ungetc)(c3,f);
- } else if (fromhold_count == 3) {
- (*i_ungetc)(c4,f);
- hold_index--;
- } else {
- hold_index -= 2;
- }
- }
- break;
- case -1:
- /* 3 bytes EUC or UTF-8 */
- if (hold_index < hold_count){
- c3 = hold_buf[hold_index++];
- fromhold_count++;
- } else if ((c3 = (*i_getc)(f)) == EOF) {
- ret = EOF;
- break;
- } else {
- code_status(c3);
- }
- if ((*iconv)(c1, c2, c3) == -3) {
- /* 6 bytes UTF-8 (check combining character) */
- nkf_char c5, c6;
- if (hold_index < hold_count){
- c4 = hold_buf[hold_index++];
- fromhold_count++;
- } else if ((c4 = (*i_getc)(f)) == EOF) {
- w_iconv_nocombine(c1, c2, c3);
- continue;
- }
- if (hold_index < hold_count){
- c5 = hold_buf[hold_index++];
- fromhold_count++;
- } else if ((c5 = (*i_getc)(f)) == EOF) {
- w_iconv_nocombine(c1, c2, c3);
- if (fromhold_count == 4)
- hold_index--;
- else
- (*i_ungetc)(c4,f);
- continue;
- }
- if (hold_index < hold_count){
- c6 = hold_buf[hold_index++];
- fromhold_count++;
- } else if ((c6 = (*i_getc)(f)) == EOF) {
- w_iconv_nocombine(c1, c2, c3);
- if (fromhold_count == 5) {
- hold_index -= 2;
- } else if (fromhold_count == 4) {
- hold_index--;
- (*i_ungetc)(c5,f);
- } else {
- (*i_ungetc)(c5,f);
- (*i_ungetc)(c4,f);
- }
- continue;
- }
- if (w_iconv_combine(c1, c2, c3, c4, c5, c6)) {
- w_iconv_nocombine(c1, c2, c3);
- if (fromhold_count == 6) {
- hold_index -= 3;
- } else if (fromhold_count == 5) {
- hold_index -= 2;
- (*i_ungetc)(c6,f);
- } else if (fromhold_count == 4) {
- hold_index--;
- (*i_ungetc)(c6,f);
- (*i_ungetc)(c5,f);
- } else {
- (*i_ungetc)(c6,f);
- (*i_ungetc)(c5,f);
- (*i_ungetc)(c4,f);
- }
- }
- }
- break;
- }
- if (c3 == EOF) break;
- }
- return ret;
-}
-
-/*
- * Check and Ignore BOM
- */
-static void
-check_bom(FILE *f)
-{
- int c2;
- input_bom_f = FALSE;
- switch(c2 = (*i_getc)(f)){
- case 0x00:
- if((c2 = (*i_getc)(f)) == 0x00){
- if((c2 = (*i_getc)(f)) == 0xFE){
- if((c2 = (*i_getc)(f)) == 0xFF){
- if(!input_encoding){
- set_iconv(TRUE, w_iconv32);
- }
- if (iconv == w_iconv32) {
- input_bom_f = TRUE;
- input_endian = ENDIAN_BIG;
- return;
- }
- (*i_ungetc)(0xFF,f);
- }else (*i_ungetc)(c2,f);
- (*i_ungetc)(0xFE,f);
- }else if(c2 == 0xFF){
- if((c2 = (*i_getc)(f)) == 0xFE){
- if(!input_encoding){
- set_iconv(TRUE, w_iconv32);
- }
- if (iconv == w_iconv32) {
- input_endian = ENDIAN_2143;
- return;
- }
- (*i_ungetc)(0xFF,f);
- }else (*i_ungetc)(c2,f);
- (*i_ungetc)(0xFF,f);
- }else (*i_ungetc)(c2,f);
- (*i_ungetc)(0x00,f);
- }else (*i_ungetc)(c2,f);
- (*i_ungetc)(0x00,f);
- break;
- case 0xEF:
- if((c2 = (*i_getc)(f)) == 0xBB){
- if((c2 = (*i_getc)(f)) == 0xBF){
- if(!input_encoding){
- set_iconv(TRUE, w_iconv);
- }
- if (iconv == w_iconv) {
- input_bom_f = TRUE;
- return;
- }
- (*i_ungetc)(0xBF,f);
- }else (*i_ungetc)(c2,f);
- (*i_ungetc)(0xBB,f);
- }else (*i_ungetc)(c2,f);
- (*i_ungetc)(0xEF,f);
- break;
- case 0xFE:
- if((c2 = (*i_getc)(f)) == 0xFF){
- if((c2 = (*i_getc)(f)) == 0x00){
- if((c2 = (*i_getc)(f)) == 0x00){
- if(!input_encoding){
- set_iconv(TRUE, w_iconv32);
- }
- if (iconv == w_iconv32) {
- input_endian = ENDIAN_3412;
- return;
- }
- (*i_ungetc)(0x00,f);
- }else (*i_ungetc)(c2,f);
- (*i_ungetc)(0x00,f);
- }else (*i_ungetc)(c2,f);
- if(!input_encoding){
- set_iconv(TRUE, w_iconv16);
- }
- if (iconv == w_iconv16) {
- input_endian = ENDIAN_BIG;
- input_bom_f = TRUE;
- return;
- }
- (*i_ungetc)(0xFF,f);
- }else (*i_ungetc)(c2,f);
- (*i_ungetc)(0xFE,f);
- break;
- case 0xFF:
- if((c2 = (*i_getc)(f)) == 0xFE){
- if((c2 = (*i_getc)(f)) == 0x00){
- if((c2 = (*i_getc)(f)) == 0x00){
- if(!input_encoding){
- set_iconv(TRUE, w_iconv32);
- }
- if (iconv == w_iconv32) {
- input_endian = ENDIAN_LITTLE;
- input_bom_f = TRUE;
- return;
- }
- (*i_ungetc)(0x00,f);
- }else (*i_ungetc)(c2,f);
- (*i_ungetc)(0x00,f);
- }else (*i_ungetc)(c2,f);
- if(!input_encoding){
- set_iconv(TRUE, w_iconv16);
- }
- if (iconv == w_iconv16) {
- input_endian = ENDIAN_LITTLE;
- input_bom_f = TRUE;
- return;
- }
- (*i_ungetc)(0xFE,f);
- }else (*i_ungetc)(c2,f);
- (*i_ungetc)(0xFF,f);
- break;
- default:
- (*i_ungetc)(c2,f);
- break;
- }
-}
-
-static nkf_char
-broken_getc(FILE *f)
-{
- nkf_char c, c1;
-
- if (!nkf_buf_empty_p(nkf_state->broken_buf)) {
- return nkf_buf_pop(nkf_state->broken_buf);
- }
- c = (*i_bgetc)(f);
- if (c=='$' && nkf_state->broken_state != ESC
- && (input_mode == ASCII || input_mode == JIS_X_0201_1976_K)) {
- c1= (*i_bgetc)(f);
- nkf_state->broken_state = 0;
- if (c1=='@'|| c1=='B') {
- nkf_buf_push(nkf_state->broken_buf, c1);
- nkf_buf_push(nkf_state->broken_buf, c);
- return ESC;
- } else {
- (*i_bungetc)(c1,f);
- return c;
- }
- } else if (c=='(' && nkf_state->broken_state != ESC
- && (input_mode == JIS_X_0208 || input_mode == JIS_X_0201_1976_K)) {
- c1= (*i_bgetc)(f);
- nkf_state->broken_state = 0;
- if (c1=='J'|| c1=='B') {
- nkf_buf_push(nkf_state->broken_buf, c1);
- nkf_buf_push(nkf_state->broken_buf, c);
- return ESC;
- } else {
- (*i_bungetc)(c1,f);
- return c;
- }
- } else {
- nkf_state->broken_state = c;
- return c;
- }
-}
-
-static nkf_char
-broken_ungetc(nkf_char c, ARG_UNUSED FILE *f)
-{
- if (nkf_buf_length(nkf_state->broken_buf) < 2)
- nkf_buf_push(nkf_state->broken_buf, c);
- return c;
-}
-
-static void
-eol_conv(nkf_char c2, nkf_char c1)
-{
- if (guess_f && input_eol != EOF) {
- if (c2 == 0 && c1 == LF) {
- if (!input_eol) input_eol = prev_cr ? CRLF : LF;
- else if (input_eol != (prev_cr ? CRLF : LF)) input_eol = EOF;
- } else if (c2 == 0 && c1 == CR && input_eol == LF) input_eol = EOF;
- else if (!prev_cr);
- else if (!input_eol) input_eol = CR;
- else if (input_eol != CR) input_eol = EOF;
- }
- if (prev_cr || (c2 == 0 && c1 == LF)) {
- prev_cr = 0;
- if (eolmode_f != LF) (*o_eol_conv)(0, CR);
- if (eolmode_f != CR) (*o_eol_conv)(0, LF);
- }
- if (c2 == 0 && c1 == CR) prev_cr = CR;
- else if (c2 != 0 || c1 != LF) (*o_eol_conv)(c2, c1);
-}
-
-static void
-put_newline(void (*func)(nkf_char))
-{
- switch (eolmode_f ? eolmode_f : DEFAULT_NEWLINE) {
- case CRLF:
- (*func)(0x0D);
- (*func)(0x0A);
- break;
- case CR:
- (*func)(0x0D);
- break;
- case LF:
- (*func)(0x0A);
- break;
- }
-}
-
-static void
-oconv_newline(void (*func)(nkf_char, nkf_char))
-{
- switch (eolmode_f ? eolmode_f : DEFAULT_NEWLINE) {
- case CRLF:
- (*func)(0, 0x0D);
- (*func)(0, 0x0A);
- break;
- case CR:
- (*func)(0, 0x0D);
- break;
- case LF:
- (*func)(0, 0x0A);
- break;
- }
-}
-
-/*
- Return value of fold_conv()
-
- LF add newline and output char
- CR add newline and output nothing
- SP space
- 0 skip
- 1 (or else) normal output
-
- fold state in prev (previous character)
-
- >0x80 Japanese (X0208/X0201)
- <0x80 ASCII
- LF new line
- SP space
-
- This fold algorithm does not preserve heading space in a line.
- This is the main difference from fmt.
- */
-
-#define char_size(c2,c1) (c2?2:1)
-
-static void
-fold_conv(nkf_char c2, nkf_char c1)
-{
- nkf_char prev0;
- nkf_char fold_state;
-
- if (c1== CR && !fold_preserve_f) {
- fold_state=0; /* ignore cr */
- }else if (c1== LF&&f_prev==CR && fold_preserve_f) {
- f_prev = LF;
- fold_state=0; /* ignore cr */
- } else if (c1== BS) {
- if (f_line>0) f_line--;
- fold_state = 1;
- } else if (c2==EOF && f_line != 0) { /* close open last line */
- fold_state = LF;
- } else if ((c1==LF && !fold_preserve_f)
- || ((c1==CR||(c1==LF&&f_prev!=CR))
- && fold_preserve_f)) {
- /* new line */
- if (fold_preserve_f) {
- f_prev = c1;
- f_line = 0;
- fold_state = CR;
- } else if ((f_prev == c1)
- || (f_prev == LF)
- ) { /* duplicate newline */
- if (f_line) {
- f_line = 0;
- fold_state = LF; /* output two newline */
- } else {
- f_line = 0;
- fold_state = 1;
- }
- } else {
- if (f_prev&0x80) { /* Japanese? */
- f_prev = c1;
- fold_state = 0; /* ignore given single newline */
- } else if (f_prev==SP) {
- fold_state = 0;
- } else {
- f_prev = c1;
- if (++f_line<=fold_len)
- fold_state = SP;
- else {
- f_line = 0;
- fold_state = CR; /* fold and output nothing */
- }
- }
- }
- } else if (c1=='\f') {
- f_prev = LF;
- f_line = 0;
- fold_state = LF; /* output newline and clear */
- } else if ((c2==0 && nkf_isblank(c1)) || (c2 == '!' && c1 == '!')) {
- /* X0208 kankaku or ascii space */
- if (f_prev == SP) {
- fold_state = 0; /* remove duplicate spaces */
- } else {
- f_prev = SP;
- if (++f_line<=fold_len)
- fold_state = SP; /* output ASCII space only */
- else {
- f_prev = SP; f_line = 0;
- fold_state = CR; /* fold and output nothing */
- }
- }
- } else {
- prev0 = f_prev; /* we still need this one... , but almost done */
- f_prev = c1;
- if (c2 || c2 == JIS_X_0201_1976_K)
- f_prev |= 0x80; /* this is Japanese */
- f_line += c2 == JIS_X_0201_1976_K ? 1: char_size(c2,c1);
- if (f_line<=fold_len) { /* normal case */
- fold_state = 1;
- } else {
- if (f_line>fold_len+fold_margin) { /* too many kinsoku suspension */
- f_line = char_size(c2,c1);
- fold_state = LF; /* We can't wait, do fold now */
- } else if (c2 == JIS_X_0201_1976_K) {
- /* simple kinsoku rules return 1 means no folding */
- if (c1==(0xde&0x7f)) fold_state = 1; /* $B!+(B*/
- else if (c1==(0xdf&0x7f)) fold_state = 1; /* $B!,(B*/
- else if (c1==(0xa4&0x7f)) fold_state = 1; /* $B!#(B*/
- else if (c1==(0xa3&0x7f)) fold_state = 1; /* $B!$(B*/
- else if (c1==(0xa1&0x7f)) fold_state = 1; /* $B!W(B*/
- else if (c1==(0xb0&0x7f)) fold_state = 1; /* - */
- else if (SP<=c1 && c1<=(0xdf&0x7f)) { /* X0201 */
- f_line = 1;
- fold_state = LF;/* add one new f_line before this character */
- } else {
- f_line = 1;
- fold_state = LF;/* add one new f_line before this character */
- }
- } else if (c2==0) {
- /* kinsoku point in ASCII */
- if ( c1==')'|| /* { [ ( */
- c1==']'||
- c1=='}'||
- c1=='.'||
- c1==','||
- c1=='!'||
- c1=='?'||
- c1=='/'||
- c1==':'||
- c1==';') {
- fold_state = 1;
- /* just after special */
- } else if (!is_alnum(prev0)) {
- f_line = char_size(c2,c1);
- fold_state = LF;
- } else if ((prev0==SP) || /* ignored new f_line */
- (prev0==LF)|| /* ignored new f_line */
- (prev0&0x80)) { /* X0208 - ASCII */
- f_line = char_size(c2,c1);
- fold_state = LF;/* add one new f_line before this character */
- } else {
- fold_state = 1; /* default no fold in ASCII */
- }
- } else {
- if (c2=='!') {
- if (c1=='"') fold_state = 1; /* $B!"(B */
- else if (c1=='#') fold_state = 1; /* $B!#(B */
- else if (c1=='W') fold_state = 1; /* $B!W(B */
- else if (c1=='K') fold_state = 1; /* $B!K(B */
- else if (c1=='$') fold_state = 1; /* $B!$(B */
- else if (c1=='%') fold_state = 1; /* $B!%(B */
- else if (c1=='\'') fold_state = 1; /* $B!\(B */
- else if (c1=='(') fold_state = 1; /* $B!((B */
- else if (c1==')') fold_state = 1; /* $B!)(B */
- else if (c1=='*') fold_state = 1; /* $B!*(B */
- else if (c1=='+') fold_state = 1; /* $B!+(B */
- else if (c1==',') fold_state = 1; /* $B!,(B */
- /* default no fold in kinsoku */
- else {
- fold_state = LF;
- f_line = char_size(c2,c1);
- /* add one new f_line before this character */
- }
- } else {
- f_line = char_size(c2,c1);
- fold_state = LF;
- /* add one new f_line before this character */
- }
- }
- }
- }
- /* terminator process */
- switch(fold_state) {
- case LF:
- oconv_newline(o_fconv);
- (*o_fconv)(c2,c1);
- break;
- case 0:
- return;
- case CR:
- oconv_newline(o_fconv);
- break;
- case TAB:
- case SP:
- (*o_fconv)(0,SP);
- break;
- default:
- (*o_fconv)(c2,c1);
- }
-}
-
-static nkf_char z_prev2=0,z_prev1=0;
-
-static void
-z_conv(nkf_char c2, nkf_char c1)
-{
-
- /* if (c2) c1 &= 0x7f; assertion */
-
- if (c2 == JIS_X_0201_1976_K && (c1 == 0x20 || c1 == 0x7D || c1 == 0x7E)) {
- (*o_zconv)(c2,c1);
- return;
- }
-
- if (x0201_f) {
- if (z_prev2 == JIS_X_0201_1976_K) {
- if (c2 == JIS_X_0201_1976_K) {
- if (c1 == (0xde&0x7f)) { /* $BByE@(B */
- z_prev2 = 0;
- (*o_zconv)(dv[(z_prev1-SP)*2], dv[(z_prev1-SP)*2+1]);
- return;
- } else if (c1 == (0xdf&0x7f) && ev[(z_prev1-SP)*2]) { /* $BH>ByE@(B */
- z_prev2 = 0;
- (*o_zconv)(ev[(z_prev1-SP)*2], ev[(z_prev1-SP)*2+1]);
- return;
- } else if (x0213_f && c1 == (0xdf&0x7f) && ev_x0213[(z_prev1-SP)*2]) { /* $BH>ByE@(B */
- z_prev2 = 0;
- (*o_zconv)(ev_x0213[(z_prev1-SP)*2], ev_x0213[(z_prev1-SP)*2+1]);
- return;
- }
- }
- z_prev2 = 0;
- (*o_zconv)(cv[(z_prev1-SP)*2], cv[(z_prev1-SP)*2+1]);
- }
- if (c2 == JIS_X_0201_1976_K) {
- if (dv[(c1-SP)*2] || ev[(c1-SP)*2] || (x0213_f && ev_x0213[(c1-SP)*2])) {
- /* wait for $BByE@(B or $BH>ByE@(B */
- z_prev1 = c1;
- z_prev2 = c2;
- return;
- } else {
- (*o_zconv)(cv[(c1-SP)*2], cv[(c1-SP)*2+1]);
- return;
- }
- }
- }
-
- if (c2 == EOF) {
- (*o_zconv)(c2, c1);
- return;
- }
-
- if (alpha_f&1 && c2 == 0x23) {
- /* JISX0208 Alphabet */
- c2 = 0;
- } else if (c2 == 0x21) {
- /* JISX0208 Kigou */
- if (0x21==c1) {
- if (alpha_f&2) {
- c2 = 0;
- c1 = SP;
- } else if (alpha_f&4) {
- (*o_zconv)(0, SP);
- (*o_zconv)(0, SP);
- return;
- }
- } else if (alpha_f&1 && 0x20<c1 && c1<0x7f && fv[c1-0x20]) {
- c2 = 0;
- c1 = fv[c1-0x20];
- }
- }
-
- if (alpha_f&8 && c2 == 0) {
- /* HTML Entity */
- const char *entity = 0;
- switch (c1){
- case '>': entity = "&gt;"; break;
- case '<': entity = "&lt;"; break;
- case '\"': entity = "&quot;"; break;
- case '&': entity = "&amp;"; break;
- }
- if (entity){
- while (*entity) (*o_zconv)(0, *entity++);
- return;
- }
- }
-
- if (alpha_f & 16) {
- /* JIS X 0208 Katakana to JIS X 0201 Katakana */
- if (c2 == 0x21) {
- nkf_char c = 0;
- switch (c1) {
- case 0x23:
- /* U+3002 (0x8142) Ideographic Full Stop -> U+FF61 (0xA1) Halfwidth Ideographic Full Stop */
- c = 0xA1;
- break;
- case 0x56:
- /* U+300C (0x8175) Left Corner Bracket -> U+FF62 (0xA2) Halfwidth Left Corner Bracket */
- c = 0xA2;
- break;
- case 0x57:
- /* U+300D (0x8176) Right Corner Bracket -> U+FF63 (0xA3) Halfwidth Right Corner Bracket */
- c = 0xA3;
- break;
- case 0x22:
- /* U+3001 (0x8141) Ideographic Comma -> U+FF64 (0xA4) Halfwidth Ideographic Comma */
- c = 0xA4;
- break;
- case 0x26:
- /* U+30FB (0x8145) Katakana Middle Dot -> U+FF65 (0xA5) Halfwidth Katakana Middle Dot */
- c = 0xA5;
- break;
- case 0x3C:
- /* U+30FC (0x815B) Katakana-Hiragana Prolonged Sound Mark -> U+FF70 (0xB0) Halfwidth Katakana-Hiragana Prolonged Sound Mark */
- c = 0xB0;
- break;
- case 0x2B:
- /* U+309B (0x814A) Katakana-Hiragana Voiced Sound Mark -> U+FF9E (0xDE) Halfwidth Katakana Voiced Sound Mark */
- c = 0xDE;
- break;
- case 0x2C:
- /* U+309C (0x814B) Katakana-Hiragana Semi-Voiced Sound Mark -> U+FF9F (0xDF) Halfwidth Katakana Semi-Voiced Sound Mark */
- c = 0xDF;
- break;
- }
- if (c) {
- (*o_zconv)(JIS_X_0201_1976_K, c);
- return;
- }
- } else if (c2 == 0x25) {
- /* JISX0208 Katakana */
- static const int fullwidth_to_halfwidth[] =
- {
- 0x0000, 0x2700, 0x3100, 0x2800, 0x3200, 0x2900, 0x3300, 0x2A00,
- 0x3400, 0x2B00, 0x3500, 0x3600, 0x365E, 0x3700, 0x375E, 0x3800,
- 0x385E, 0x3900, 0x395E, 0x3A00, 0x3A5E, 0x3B00, 0x3B5E, 0x3C00,
- 0x3C5E, 0x3D00, 0x3D5E, 0x3E00, 0x3E5E, 0x3F00, 0x3F5E, 0x4000,
- 0x405E, 0x4100, 0x415E, 0x2F00, 0x4200, 0x425E, 0x4300, 0x435E,
- 0x4400, 0x445E, 0x4500, 0x4600, 0x4700, 0x4800, 0x4900, 0x4A00,
- 0x4A5E, 0x4A5F, 0x4B00, 0x4B5E, 0x4B5F, 0x4C00, 0x4C5E, 0x4C5F,
- 0x4D00, 0x4D5E, 0x4D5F, 0x4E00, 0x4E5E, 0x4E5F, 0x4F00, 0x5000,
- 0x5100, 0x5200, 0x5300, 0x2C00, 0x5400, 0x2D00, 0x5500, 0x2E00,
- 0x5600, 0x5700, 0x5800, 0x5900, 0x5A00, 0x5B00, 0x0000, 0x5C00,
- 0x0000, 0x0000, 0x2600, 0x5D00, 0x335E, 0x0000, 0x0000, 0x365F,
- 0x375F, 0x385F, 0x395F, 0x3A5F, 0x3E5F, 0x425F, 0x445F, 0x0000
- };
- if (fullwidth_to_halfwidth[c1-0x20]){
- c2 = fullwidth_to_halfwidth[c1-0x20];
- (*o_zconv)(JIS_X_0201_1976_K, c2>>8);
- if (c2 & 0xFF) {
- (*o_zconv)(JIS_X_0201_1976_K, c2&0xFF);
- }
- return;
- }
- } else if (c2 == 0 && nkf_char_unicode_p(c1) &&
- ((c1&VALUE_MASK) == 0x3099 || (c1&VALUE_MASK) == 0x309A)) { /* $B9g@.MQByE@!&H>ByE@(B */
- (*o_zconv)(JIS_X_0201_1976_K, 0x5E + (c1&VALUE_MASK) - 0x3099);
- return;
- }
- }
- (*o_zconv)(c2,c1);
-}
-
-
-#define rot13(c) ( \
- ( c < 'A') ? c: \
- (c <= 'M') ? (c + 13): \
- (c <= 'Z') ? (c - 13): \
- (c < 'a') ? (c): \
- (c <= 'm') ? (c + 13): \
- (c <= 'z') ? (c - 13): \
- (c) \
- )
-
-#define rot47(c) ( \
- ( c < '!') ? c: \
- ( c <= 'O') ? (c + 47) : \
- ( c <= '~') ? (c - 47) : \
- c \
- )
-
-static void
-rot_conv(nkf_char c2, nkf_char c1)
-{
- if (c2 == 0 || c2 == JIS_X_0201_1976_K || c2 == ISO_8859_1) {
- c1 = rot13(c1);
- } else if (c2) {
- c1 = rot47(c1);
- c2 = rot47(c2);
- }
- (*o_rot_conv)(c2,c1);
-}
-
-static void
-hira_conv(nkf_char c2, nkf_char c1)
-{
- if (hira_f & 1) {
- if (c2 == 0x25) {
- if (0x20 < c1 && c1 < 0x74) {
- c2 = 0x24;
- (*o_hira_conv)(c2,c1);
- return;
- } else if (c1 == 0x74 && nkf_enc_unicode_p(output_encoding)) {
- c2 = 0;
- c1 = nkf_char_unicode_new(0x3094);
- (*o_hira_conv)(c2,c1);
- return;
- }
- } else if (c2 == 0x21 && (c1 == 0x33 || c1 == 0x34)) {
- c1 += 2;
- (*o_hira_conv)(c2,c1);
- return;
- }
- }
- if (hira_f & 2) {
- if (c2 == 0 && c1 == nkf_char_unicode_new(0x3094)) {
- c2 = 0x25;
- c1 = 0x74;
- } else if (c2 == 0x24 && 0x20 < c1 && c1 < 0x74) {
- c2 = 0x25;
- } else if (c2 == 0x21 && (c1 == 0x35 || c1 == 0x36)) {
- c1 -= 2;
- }
- }
- (*o_hira_conv)(c2,c1);
-}
-
-
-static void
-iso2022jp_check_conv(nkf_char c2, nkf_char c1)
-{
-#define RANGE_NUM_MAX 18
- static const nkf_char range[RANGE_NUM_MAX][2] = {
- {0x222f, 0x2239,},
- {0x2242, 0x2249,},
- {0x2251, 0x225b,},
- {0x226b, 0x2271,},
- {0x227a, 0x227d,},
- {0x2321, 0x232f,},
- {0x233a, 0x2340,},
- {0x235b, 0x2360,},
- {0x237b, 0x237e,},
- {0x2474, 0x247e,},
- {0x2577, 0x257e,},
- {0x2639, 0x2640,},
- {0x2659, 0x267e,},
- {0x2742, 0x2750,},
- {0x2772, 0x277e,},
- {0x2841, 0x287e,},
- {0x4f54, 0x4f7e,},
- {0x7425, 0x747e},
- };
- nkf_char i;
- nkf_char start, end, c;
-
- if(c2 >= 0x00 && c2 <= 0x20 && c1 >= 0x7f && c1 <= 0xff) {
- c2 = GETA1;
- c1 = GETA2;
- }
- if((c2 >= 0x29 && c2 <= 0x2f) || (c2 >= 0x75 && c2 <= 0x7e)) {
- c2 = GETA1;
- c1 = GETA2;
- }
-
- for (i = 0; i < RANGE_NUM_MAX; i++) {
- start = range[i][0];
- end = range[i][1];
- c = (c2 << 8) + c1;
- if (c >= start && c <= end) {
- c2 = GETA1;
- c1 = GETA2;
- }
- }
- (*o_iso2022jp_check_conv)(c2,c1);
-}
-
-
-/* This converts =?ISO-2022-JP?B?HOGE HOGE?= */
-
-static const unsigned char *mime_pattern[] = {
- (const unsigned char *)"\075?EUC-JP?B?",
- (const unsigned char *)"\075?SHIFT_JIS?B?",
- (const unsigned char *)"\075?ISO-8859-1?Q?",
- (const unsigned char *)"\075?ISO-8859-1?B?",
- (const unsigned char *)"\075?ISO-2022-JP?B?",
- (const unsigned char *)"\075?ISO-2022-JP?B?",
- (const unsigned char *)"\075?ISO-2022-JP?Q?",
-#if defined(UTF8_INPUT_ENABLE)
- (const unsigned char *)"\075?UTF-8?B?",
- (const unsigned char *)"\075?UTF-8?Q?",
-#endif
- (const unsigned char *)"\075?US-ASCII?Q?",
- NULL
-};
-
-
-/* $B3:Ev$9$k%3!<%I$NM%@hEY$r>e$2$k$?$a$NL\0u(B */
-static nkf_char (*const mime_priority_func[])(nkf_char c2, nkf_char c1, nkf_char c0) = {
- e_iconv, s_iconv, 0, 0, 0, 0, 0,
-#if defined(UTF8_INPUT_ENABLE)
- w_iconv, w_iconv,
-#endif
- 0,
-};
-
-static const nkf_char mime_encode[] = {
- EUC_JP, SHIFT_JIS, ISO_8859_1, ISO_8859_1, JIS_X_0208, JIS_X_0201_1976_K, JIS_X_0201_1976_K,
-#if defined(UTF8_INPUT_ENABLE)
- UTF_8, UTF_8,
-#endif
- ASCII,
- 0
-};
-
-static const nkf_char mime_encode_method[] = {
- 'B', 'B','Q', 'B', 'B', 'B', 'Q',
-#if defined(UTF8_INPUT_ENABLE)
- 'B', 'Q',
-#endif
- 'Q',
- 0
-};
-
-
-/* MIME preprocessor fifo */
-
-#define MIME_BUF_SIZE (1024) /* 2^n ring buffer */
-#define MIME_BUF_MASK (MIME_BUF_SIZE-1)
-#define mime_input_buf(n) mime_input_state.buf[(n)&MIME_BUF_MASK]
-static struct {
- unsigned char buf[MIME_BUF_SIZE];
- unsigned int top;
- unsigned int last; /* decoded */
- unsigned int input; /* undecoded */
-} mime_input_state;
-static nkf_char (*mime_iconv_back)(nkf_char c2,nkf_char c1,nkf_char c0) = NULL;
-
-#define MAXRECOVER 20
-
-static void
-mime_input_buf_unshift(nkf_char c)
-{
- mime_input_buf(--mime_input_state.top) = (unsigned char)c;
-}
-
-static nkf_char
-mime_ungetc(nkf_char c, ARG_UNUSED FILE *f)
-{
- mime_input_buf_unshift(c);
- return c;
-}
-
-static nkf_char
-mime_ungetc_buf(nkf_char c, FILE *f)
-{
- if (mimebuf_f)
- (*i_mungetc_buf)(c,f);
- else
- mime_input_buf(--mime_input_state.input) = (unsigned char)c;
- return c;
-}
-
-static nkf_char
-mime_getc_buf(FILE *f)
-{
- /* we don't keep eof of mime_input_buf, because it contains ?= as
- a terminator. It was checked in mime_integrity. */
- return ((mimebuf_f)?
- (*i_mgetc_buf)(f):mime_input_buf(mime_input_state.input++));
-}
-
-static void
-switch_mime_getc(void)
-{
- if (i_getc!=mime_getc) {
- i_mgetc = i_getc; i_getc = mime_getc;
- i_mungetc = i_ungetc; i_ungetc = mime_ungetc;
- if(mime_f==STRICT_MIME) {
- i_mgetc_buf = i_mgetc; i_mgetc = mime_getc_buf;
- i_mungetc_buf = i_mungetc; i_mungetc = mime_ungetc_buf;
- }
- }
-}
-
-static void
-unswitch_mime_getc(void)
-{
- if(mime_f==STRICT_MIME) {
- i_mgetc = i_mgetc_buf;
- i_mungetc = i_mungetc_buf;
- }
- i_getc = i_mgetc;
- i_ungetc = i_mungetc;
- if(mime_iconv_back)set_iconv(FALSE, mime_iconv_back);
- mime_iconv_back = NULL;
-}
-
-static nkf_char
-mime_integrity(FILE *f, const unsigned char *p)
-{
- nkf_char c,d;
- unsigned int q;
- /* In buffered mode, read until =? or NL or buffer full
- */
- mime_input_state.input = mime_input_state.top;
- mime_input_state.last = mime_input_state.top;
-
- while(*p) mime_input_buf(mime_input_state.input++) = *p++;
- d = 0;
- q = mime_input_state.input;
- while((c=(*i_getc)(f))!=EOF) {
- if (((mime_input_state.input-mime_input_state.top)&MIME_BUF_MASK)==0) {
- break; /* buffer full */
- }
- if (c=='=' && d=='?') {
- /* checked. skip header, start decode */
- mime_input_buf(mime_input_state.input++) = (unsigned char)c;
- /* mime_last_input = mime_input_state.input; */
- mime_input_state.input = q;
- switch_mime_getc();
- return 1;
- }
- if (!( (c=='+'||c=='/'|| c=='=' || c=='?' || is_alnum(c))))
- break;
- /* Should we check length mod 4? */
- mime_input_buf(mime_input_state.input++) = (unsigned char)c;
- d=c;
- }
- /* In case of Incomplete MIME, no MIME decode */
- mime_input_buf(mime_input_state.input++) = (unsigned char)c;
- mime_input_state.last = mime_input_state.input; /* point undecoded buffer */
- mime_decode_mode = 1; /* no decode on mime_input_buf last in mime_getc */
- switch_mime_getc(); /* anyway we need buffered getc */
- return 1;
-}
-
-static nkf_char
-mime_begin_strict(FILE *f)
-{
- nkf_char c1 = 0;
- int i,j,k;
- const unsigned char *p,*q;
- nkf_char r[MAXRECOVER]; /* recovery buffer, max mime pattern length */
-
- mime_decode_mode = FALSE;
- /* =? has been checked */
- j = 0;
- p = mime_pattern[j];
- r[0]='='; r[1]='?';
-
- for(i=2;p[i]>SP;i++) { /* start at =? */
- if (((r[i] = c1 = (*i_getc)(f))==EOF) || nkf_toupper(c1) != p[i]) {
- /* pattern fails, try next one */
- q = p;
- while (mime_pattern[++j]) {
- p = mime_pattern[j];
- for(k=2;k<i;k++) /* assume length(p) > i */
- if (p[k]!=q[k]) break;
- if (k==i && nkf_toupper(c1)==p[k]) break;
- }
- p = mime_pattern[j];
- if (p) continue; /* found next one, continue */
- /* all fails, output from recovery buffer */
- (*i_ungetc)(c1,f);
- for(j=0;j<i;j++) {
- (*oconv)(0,r[j]);
- }
- return c1;
- }
- }
- mime_decode_mode = p[i-2];
-
- mime_iconv_back = iconv;
- set_iconv(FALSE, mime_priority_func[j]);
- clr_code_score(find_inputcode_byfunc(mime_priority_func[j]), SCORE_iMIME);
-
- if (mime_decode_mode=='B') {
- mimebuf_f = unbuf_f;
- if (!unbuf_f) {
- /* do MIME integrity check */
- return mime_integrity(f,mime_pattern[j]);
- }
- }
- switch_mime_getc();
- mimebuf_f = TRUE;
- return c1;
-}
-
-static nkf_char
-mime_begin(FILE *f)
-{
- nkf_char c1 = 0;
- int i,k;
-
- /* In NONSTRICT mode, only =? is checked. In case of failure, we */
- /* re-read and convert again from mime_buffer. */
-
- /* =? has been checked */
- k = mime_input_state.last;
- mime_input_buf(mime_input_state.last++)='='; mime_input_buf(mime_input_state.last++)='?';
- for(i=2;i<MAXRECOVER;i++) { /* start at =? */
- /* We accept any character type even if it is breaked by new lines */
- c1 = (*i_getc)(f); mime_input_buf(mime_input_state.last++) = (unsigned char)c1;
- if (c1==LF||c1==SP||c1==CR||
- c1=='-'||c1=='_'||is_alnum(c1)) continue;
- if (c1=='=') {
- /* Failed. But this could be another MIME preemble */
- (*i_ungetc)(c1,f);
- mime_input_state.last--;
- break;
- }
- if (c1!='?') break;
- else {
- /* c1=='?' */
- c1 = (*i_getc)(f); mime_input_buf(mime_input_state.last++) = (unsigned char)c1;
- if (!(++i<MAXRECOVER) || c1==EOF) break;
- if (c1=='b'||c1=='B') {
- mime_decode_mode = 'B';
- } else if (c1=='q'||c1=='Q') {
- mime_decode_mode = 'Q';
- } else {
- break;
- }
- c1 = (*i_getc)(f); mime_input_buf(mime_input_state.last++) = (unsigned char)c1;
- if (!(++i<MAXRECOVER) || c1==EOF) break;
- if (c1!='?') {
- mime_decode_mode = FALSE;
- }
- break;
- }
- }
- switch_mime_getc();
- if (!mime_decode_mode) {
- /* false MIME premble, restart from mime_buffer */
- mime_decode_mode = 1; /* no decode, but read from the mime_buffer */
- /* Since we are in MIME mode until buffer becomes empty, */
- /* we never go into mime_begin again for a while. */
- return c1;
- }
- /* discard mime preemble, and goto MIME mode */
- mime_input_state.last = k;
- /* do no MIME integrity check */
- return c1; /* used only for checking EOF */
-}
-
-#ifdef CHECK_OPTION
-static void
-no_putc(ARG_UNUSED nkf_char c)
-{
- ;
-}
-
-static void
-debug(const char *str)
-{
- if (debug_f){
- fprintf(stderr, "%s\n", str ? str : "NULL");
- }
-}
-#endif
-
-static void
-set_input_codename(const char *codename)
-{
- if (!input_codename) {
- input_codename = codename;
- } else if (strcmp(codename, input_codename) != 0) {
- input_codename = "";
- }
-}
-
-static const char*
-get_guessed_code(void)
-{
- if (input_codename && !*input_codename) {
- input_codename = "BINARY";
- } else {
- struct input_code *p = find_inputcode_byfunc(iconv);
- if (!input_codename) {
- input_codename = "ASCII";
- } else if (strcmp(input_codename, "Shift_JIS") == 0) {
- if (p->score & (SCORE_DEPEND|SCORE_CP932))
- input_codename = "CP932";
- } else if (strcmp(input_codename, "EUC-JP") == 0) {
- if (p->score & SCORE_X0213)
- input_codename = "EUC-JIS-2004";
- else if (p->score & (SCORE_X0212))
- input_codename = "EUCJP-MS";
- else if (p->score & (SCORE_DEPEND|SCORE_CP932))
- input_codename = "CP51932";
- } else if (strcmp(input_codename, "ISO-2022-JP") == 0) {
- if (p->score & (SCORE_KANA))
- input_codename = "CP50221";
- else if (p->score & (SCORE_DEPEND|SCORE_CP932))
- input_codename = "CP50220";
- }
- }
- return input_codename;
-}
-
-#if !defined(PERL_XS) && !defined(WIN32DLL)
-static void
-print_guessed_code(char *filename)
-{
- if (filename != NULL) printf("%s: ", filename);
- if (input_codename && !*input_codename) {
- printf("BINARY\n");
- } else {
- input_codename = get_guessed_code();
- if (guess_f == 1) {
- printf("%s\n", input_codename);
- } else {
- printf("%s%s%s%s\n",
- input_codename,
- iconv != w_iconv16 && iconv != w_iconv32 ? "" :
- input_endian == ENDIAN_LITTLE ? " LE" :
- input_endian == ENDIAN_BIG ? " BE" :
- "[BUG]",
- input_bom_f ? " (BOM)" : "",
- input_eol == CR ? " (CR)" :
- input_eol == LF ? " (LF)" :
- input_eol == CRLF ? " (CRLF)" :
- input_eol == EOF ? " (MIXED NL)" :
- "");
- }
- }
-}
-#endif /*WIN32DLL*/
-
-#ifdef INPUT_OPTION
-
-static nkf_char
-hex_getc(nkf_char ch, FILE *f, nkf_char (*g)(FILE *f), nkf_char (*u)(nkf_char c, FILE *f))
-{
- nkf_char c1, c2, c3;
- c1 = (*g)(f);
- if (c1 != ch){
- return c1;
- }
- c2 = (*g)(f);
- if (!nkf_isxdigit(c2)){
- (*u)(c2, f);
- return c1;
- }
- c3 = (*g)(f);
- if (!nkf_isxdigit(c3)){
- (*u)(c2, f);
- (*u)(c3, f);
- return c1;
- }
- return (hex2bin(c2) << 4) | hex2bin(c3);
-}
-
-static nkf_char
-cap_getc(FILE *f)
-{
- return hex_getc(':', f, i_cgetc, i_cungetc);
-}
-
-static nkf_char
-cap_ungetc(nkf_char c, FILE *f)
-{
- return (*i_cungetc)(c, f);
-}
-
-static nkf_char
-url_getc(FILE *f)
-{
- return hex_getc('%', f, i_ugetc, i_uungetc);
-}
-
-static nkf_char
-url_ungetc(nkf_char c, FILE *f)
-{
- return (*i_uungetc)(c, f);
-}
-#endif
-
-#ifdef NUMCHAR_OPTION
-static nkf_char
-numchar_getc(FILE *f)
-{
- nkf_char (*g)(FILE *) = i_ngetc;
- nkf_char (*u)(nkf_char c ,FILE *f) = i_nungetc;
- int i = 0, j;
- nkf_char buf[12];
- nkf_char c = -1;
-
- buf[i] = (*g)(f);
- if (buf[i] == '&'){
- buf[++i] = (*g)(f);
- if (buf[i] == '#'){
- c = 0;
- buf[++i] = (*g)(f);
- if (buf[i] == 'x' || buf[i] == 'X'){
- for (j = 0; j < 7; j++){
- buf[++i] = (*g)(f);
- if (!nkf_isxdigit(buf[i])){
- if (buf[i] != ';'){
- c = -1;
- }
- break;
- }
- c <<= 4;
- c |= hex2bin(buf[i]);
- }
- }else{
- for (j = 0; j < 8; j++){
- if (j){
- buf[++i] = (*g)(f);
- }
- if (!nkf_isdigit(buf[i])){
- if (buf[i] != ';'){
- c = -1;
- }
- break;
- }
- c *= 10;
- c += hex2bin(buf[i]);
- }
- }
- }
- }
- if (c != -1){
- return nkf_char_unicode_new(c);
- }
- while (i > 0){
- (*u)(buf[i], f);
- --i;
- }
- return buf[0];
-}
-
-static nkf_char
-numchar_ungetc(nkf_char c, FILE *f)
-{
- return (*i_nungetc)(c, f);
-}
-#endif
-
-#ifdef UNICODE_NORMALIZATION
-
-static nkf_char
-nfc_getc(FILE *f)
-{
- nkf_char (*g)(FILE *f) = i_nfc_getc;
- nkf_char (*u)(nkf_char c ,FILE *f) = i_nfc_ungetc;
- nkf_buf_t *buf = nkf_state->nfc_buf;
- const unsigned char *array;
- int lower=0, upper=NORMALIZATION_TABLE_LENGTH-1;
- nkf_char c = (*g)(f);
-
- if (c == EOF || c > 0xFF || (c & 0xc0) == 0x80) return c;
-
- nkf_buf_push(buf, c);
- do {
- while (lower <= upper) {
- int mid = (lower+upper) / 2;
- int len;
- array = normalization_table[mid].nfd;
- for (len=0; len < NORMALIZATION_TABLE_NFD_LENGTH && array[len]; len++) {
- if (len >= nkf_buf_length(buf)) {
- c = (*g)(f);
- if (c == EOF) {
- len = 0;
- lower = 1, upper = 0;
- break;
- }
- nkf_buf_push(buf, c);
- }
- if (array[len] != nkf_buf_at(buf, len)) {
- if (array[len] < nkf_buf_at(buf, len)) lower = mid + 1;
- else upper = mid - 1;
- len = 0;
- break;
- }
- }
- if (len > 0) {
- int i;
- array = normalization_table[mid].nfc;
- nkf_buf_clear(buf);
- for (i=0; i < NORMALIZATION_TABLE_NFC_LENGTH && array[i]; i++)
- nkf_buf_push(buf, array[i]);
- break;
- }
- }
- } while (lower <= upper);
-
- while (nkf_buf_length(buf) > 1) (*u)(nkf_buf_pop(buf), f);
- c = nkf_buf_pop(buf);
-
- return c;
-}
-
-static nkf_char
-nfc_ungetc(nkf_char c, FILE *f)
-{
- return (*i_nfc_ungetc)(c, f);
-}
-#endif /* UNICODE_NORMALIZATION */
-
-
-static nkf_char
-base64decode(nkf_char c)
-{
- int i;
- if (c > '@') {
- if (c < '[') {
- i = c - 'A'; /* A..Z 0-25 */
- } else if (c == '_') {
- i = '?' /* 63 */ ; /* _ 63 */
- } else {
- i = c - 'G' /* - 'a' + 26 */ ; /* a..z 26-51 */
- }
- } else if (c > '/') {
- i = c - '0' + '4' /* - '0' + 52 */ ; /* 0..9 52-61 */
- } else if (c == '+' || c == '-') {
- i = '>' /* 62 */ ; /* + and - 62 */
- } else {
- i = '?' /* 63 */ ; /* / 63 */
- }
- return (i);
-}
-
-static nkf_char
-mime_getc(FILE *f)
-{
- nkf_char c1, c2, c3, c4, cc;
- nkf_char t1, t2, t3, t4, mode, exit_mode;
- nkf_char lwsp_count;
- char *lwsp_buf;
- char *lwsp_buf_new;
- nkf_char lwsp_size = 128;
-
- if (mime_input_state.top != mime_input_state.last) { /* Something is in FIFO */
- return mime_input_buf(mime_input_state.top++);
- }
- if (mime_decode_mode==1 ||mime_decode_mode==FALSE) {
- mime_decode_mode=FALSE;
- unswitch_mime_getc();
- return (*i_getc)(f);
- }
-
- if (mimebuf_f == FIXED_MIME)
- exit_mode = mime_decode_mode;
- else
- exit_mode = FALSE;
- if (mime_decode_mode == 'Q') {
- if ((c1 = (*i_mgetc)(f)) == EOF) return (EOF);
- restart_mime_q:
- if (c1=='_' && mimebuf_f != FIXED_MIME) return SP;
- if (c1<=SP || DEL<=c1) {
- mime_decode_mode = exit_mode; /* prepare for quit */
- return c1;
- }
- if (c1!='=' && (c1!='?' || mimebuf_f == FIXED_MIME)) {
- return c1;
- }
-
- mime_decode_mode = exit_mode; /* prepare for quit */
- if ((c2 = (*i_mgetc)(f)) == EOF) return (EOF);
- if (c1=='?'&&c2=='=' && mimebuf_f != FIXED_MIME) {
- /* end Q encoding */
- input_mode = exit_mode;
- lwsp_count = 0;
- lwsp_buf = nkf_xmalloc((lwsp_size+5)*sizeof(char));
- while ((c1=(*i_getc)(f))!=EOF) {
- switch (c1) {
- case LF:
- case CR:
- if (c1==LF) {
- if ((c1=(*i_getc)(f))!=EOF && nkf_isblank(c1)) {
- i_ungetc(SP,f);
- continue;
- } else {
- i_ungetc(c1,f);
- }
- c1 = LF;
- } else {
- if ((c1=(*i_getc)(f))!=EOF && c1 == LF) {
- if ((c1=(*i_getc)(f))!=EOF && nkf_isblank(c1)) {
- i_ungetc(SP,f);
- continue;
- } else {
- i_ungetc(c1,f);
- }
- i_ungetc(LF,f);
- } else {
- i_ungetc(c1,f);
- }
- c1 = CR;
- }
- break;
- case SP:
- case TAB:
- lwsp_buf[lwsp_count] = (unsigned char)c1;
- if (lwsp_count++>lwsp_size){
- lwsp_size <<= 1;
- lwsp_buf_new = nkf_xrealloc(lwsp_buf, (lwsp_size+5)*sizeof(char));
- lwsp_buf = lwsp_buf_new;
- }
- continue;
- }
- break;
- }
- if (lwsp_count > 0 && (c1 != '=' || (lwsp_buf[lwsp_count-1] != SP && lwsp_buf[lwsp_count-1] != TAB))) {
- i_ungetc(c1,f);
- for(lwsp_count--;lwsp_count>0;lwsp_count--)
- i_ungetc(lwsp_buf[lwsp_count],f);
- c1 = lwsp_buf[0];
- }
- nkf_xfree(lwsp_buf);
- return c1;
- }
- if (c1=='='&&c2<SP) { /* this is soft wrap */
- while((c1 = (*i_mgetc)(f)) <=SP) {
- if (c1 == EOF) return (EOF);
- }
- mime_decode_mode = 'Q'; /* still in MIME */
- goto restart_mime_q;
- }
- if (c1=='?') {
- mime_decode_mode = 'Q'; /* still in MIME */
- (*i_mungetc)(c2,f);
- return c1;
- }
- if ((c3 = (*i_mgetc)(f)) == EOF) return (EOF);
- if (c2<=SP) return c2;
- mime_decode_mode = 'Q'; /* still in MIME */
- return ((hex2bin(c2)<<4) + hex2bin(c3));
- }
-
- if (mime_decode_mode != 'B') {
- mime_decode_mode = FALSE;
- return (*i_mgetc)(f);
- }
-
-
- /* Base64 encoding */
- /*
- MIME allows line break in the middle of
- Base64, but we are very pessimistic in decoding
- in unbuf mode because MIME encoded code may broken by
- less or editor's control sequence (such as ESC-[-K in unbuffered
- mode. ignore incomplete MIME.
- */
- mode = mime_decode_mode;
- mime_decode_mode = exit_mode; /* prepare for quit */
-
- while ((c1 = (*i_mgetc)(f))<=SP) {
- if (c1==EOF)
- return (EOF);
- }
- mime_c2_retry:
- if ((c2 = (*i_mgetc)(f))<=SP) {
- if (c2==EOF)
- return (EOF);
- if (mime_f != STRICT_MIME) goto mime_c2_retry;
- if (mimebuf_f!=FIXED_MIME) input_mode = ASCII;
- return c2;
- }
- if ((c1 == '?') && (c2 == '=')) {
- input_mode = ASCII;
- lwsp_count = 0;
- lwsp_buf = nkf_xmalloc((lwsp_size+5)*sizeof(char));
- while ((c1=(*i_getc)(f))!=EOF) {
- switch (c1) {
- case LF:
- case CR:
- if (c1==LF) {
- if ((c1=(*i_getc)(f))!=EOF && nkf_isblank(c1)) {
- i_ungetc(SP,f);
- continue;
- } else {
- i_ungetc(c1,f);
- }
- c1 = LF;
- } else {
- if ((c1=(*i_getc)(f))!=EOF) {
- if (c1==SP) {
- i_ungetc(SP,f);
- continue;
- } else if ((c1=(*i_getc)(f))!=EOF && nkf_isblank(c1)) {
- i_ungetc(SP,f);
- continue;
- } else {
- i_ungetc(c1,f);
- }
- i_ungetc(LF,f);
- } else {
- i_ungetc(c1,f);
- }
- c1 = CR;
- }
- break;
- case SP:
- case TAB:
- lwsp_buf[lwsp_count] = (unsigned char)c1;
- if (lwsp_count++>lwsp_size){
- lwsp_size <<= 1;
- lwsp_buf_new = nkf_xrealloc(lwsp_buf, (lwsp_size+5)*sizeof(char));
- lwsp_buf = lwsp_buf_new;
- }
- continue;
- }
- break;
- }
- if (lwsp_count > 0 && (c1 != '=' || (lwsp_buf[lwsp_count-1] != SP && lwsp_buf[lwsp_count-1] != TAB))) {
- i_ungetc(c1,f);
- for(lwsp_count--;lwsp_count>0;lwsp_count--)
- i_ungetc(lwsp_buf[lwsp_count],f);
- c1 = lwsp_buf[0];
- }
- nkf_xfree(lwsp_buf);
- return c1;
- }
- mime_c3_retry:
- if ((c3 = (*i_mgetc)(f))<=SP) {
- if (c3==EOF)
- return (EOF);
- if (mime_f != STRICT_MIME) goto mime_c3_retry;
- if (mimebuf_f!=FIXED_MIME) input_mode = ASCII;
- return c3;
- }
- mime_c4_retry:
- if ((c4 = (*i_mgetc)(f))<=SP) {
- if (c4==EOF)
- return (EOF);
- if (mime_f != STRICT_MIME) goto mime_c4_retry;
- if (mimebuf_f!=FIXED_MIME) input_mode = ASCII;
- return c4;
- }
-
- mime_decode_mode = mode; /* still in MIME sigh... */
-
- /* BASE 64 decoding */
-
- t1 = 0x3f & base64decode(c1);
- t2 = 0x3f & base64decode(c2);
- t3 = 0x3f & base64decode(c3);
- t4 = 0x3f & base64decode(c4);
- cc = ((t1 << 2) & 0x0fc) | ((t2 >> 4) & 0x03);
- if (c2 != '=') {
- mime_input_buf(mime_input_state.last++) = (unsigned char)cc;
- cc = ((t2 << 4) & 0x0f0) | ((t3 >> 2) & 0x0f);
- if (c3 != '=') {
- mime_input_buf(mime_input_state.last++) = (unsigned char)cc;
- cc = ((t3 << 6) & 0x0c0) | (t4 & 0x3f);
- if (c4 != '=')
- mime_input_buf(mime_input_state.last++) = (unsigned char)cc;
- }
- } else {
- return c1;
- }
- return mime_input_buf(mime_input_state.top++);
-}
-
-static const char basis_64[] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-#define MIMEOUT_BUF_LENGTH 74
-static struct {
- unsigned char buf[MIMEOUT_BUF_LENGTH+1];
- int count;
-} mimeout_state;
-
-/*nkf_char mime_lastchar2, mime_lastchar1;*/
-
-static void
-open_mime(nkf_char mode)
-{
- const unsigned char *p;
- int i;
- int j;
- p = mime_pattern[0];
- for(i=0;mime_pattern[i];i++) {
- if (mode == mime_encode[i]) {
- p = mime_pattern[i];
- break;
- }
- }
- mimeout_mode = mime_encode_method[i];
- i = 0;
- if (base64_count>45) {
- if (mimeout_state.count>0 && nkf_isblank(mimeout_state.buf[i])){
- (*o_mputc)(mimeout_state.buf[i]);
- i++;
- }
- put_newline(o_mputc);
- (*o_mputc)(SP);
- base64_count = 1;
- if (mimeout_state.count>0 && nkf_isspace(mimeout_state.buf[i])) {
- i++;
- }
- }
- for (;i<mimeout_state.count;i++) {
- if (nkf_isspace(mimeout_state.buf[i])) {
- (*o_mputc)(mimeout_state.buf[i]);
- base64_count ++;
- } else {
- break;
- }
- }
- while(*p) {
- (*o_mputc)(*p++);
- base64_count ++;
- }
- j = mimeout_state.count;
- mimeout_state.count = 0;
- for (;i<j;i++) {
- mime_putc(mimeout_state.buf[i]);
- }
-}
-
-static void
-mime_prechar(nkf_char c2, nkf_char c1)
-{
- if (mimeout_mode > 0){
- if (c2 == EOF){
- if (base64_count + mimeout_state.count/3*4> 73){
- (*o_base64conv)(EOF,0);
- oconv_newline(o_base64conv);
- (*o_base64conv)(0,SP);
- base64_count = 1;
- }
- } else {
- if ((c2 != 0 || c1 > DEL) && base64_count + mimeout_state.count/3*4> 66) {
- (*o_base64conv)(EOF,0);
- oconv_newline(o_base64conv);
- (*o_base64conv)(0,SP);
- base64_count = 1;
- mimeout_mode = -1;
- }
- }
- } else if (c2) {
- if (c2 != EOF && base64_count + mimeout_state.count/3*4> 60) {
- mimeout_mode = (output_mode==ASCII ||output_mode == ISO_8859_1) ? 'Q' : 'B';
- open_mime(output_mode);
- (*o_base64conv)(EOF,0);
- oconv_newline(o_base64conv);
- (*o_base64conv)(0,SP);
- base64_count = 1;
- mimeout_mode = -1;
- }
- }
-}
-
-static void
-close_mime(void)
-{
- (*o_mputc)('?');
- (*o_mputc)('=');
- base64_count += 2;
- mimeout_mode = 0;
-}
-
-static void
-eof_mime(void)
-{
- switch(mimeout_mode) {
- case 'Q':
- case 'B':
- break;
- case 2:
- (*o_mputc)(basis_64[((nkf_state->mimeout_state & 0x3)<< 4)]);
- (*o_mputc)('=');
- (*o_mputc)('=');
- base64_count += 3;
- break;
- case 1:
- (*o_mputc)(basis_64[((nkf_state->mimeout_state & 0xF) << 2)]);
- (*o_mputc)('=');
- base64_count += 2;
- break;
- }
- if (mimeout_mode > 0) {
- if (mimeout_f!=FIXED_MIME) {
- close_mime();
- } else if (mimeout_mode != 'Q')
- mimeout_mode = 'B';
- }
-}
-
-static void
-mimeout_addchar(nkf_char c)
-{
- switch(mimeout_mode) {
- case 'Q':
- if (c==CR||c==LF) {
- (*o_mputc)(c);
- base64_count = 0;
- } else if(!nkf_isalnum(c)) {
- (*o_mputc)('=');
- (*o_mputc)(bin2hex(((c>>4)&0xf)));
- (*o_mputc)(bin2hex((c&0xf)));
- base64_count += 3;
- } else {
- (*o_mputc)(c);
- base64_count++;
- }
- break;
- case 'B':
- nkf_state->mimeout_state=c;
- (*o_mputc)(basis_64[c>>2]);
- mimeout_mode=2;
- base64_count ++;
- break;
- case 2:
- (*o_mputc)(basis_64[((nkf_state->mimeout_state & 0x3)<< 4) | ((c & 0xF0) >> 4)]);
- nkf_state->mimeout_state=c;
- mimeout_mode=1;
- base64_count ++;
- break;
- case 1:
- (*o_mputc)(basis_64[((nkf_state->mimeout_state & 0xF) << 2) | ((c & 0xC0) >>6)]);
- (*o_mputc)(basis_64[c & 0x3F]);
- mimeout_mode='B';
- base64_count += 2;
- break;
- default:
- (*o_mputc)(c);
- base64_count++;
- break;
- }
-}
-
-static void
-mime_putc(nkf_char c)
-{
- int i, j;
- nkf_char lastchar;
-
- if (mimeout_f == FIXED_MIME){
- if (mimeout_mode == 'Q'){
- if (base64_count > 71){
- if (c!=CR && c!=LF) {
- (*o_mputc)('=');
- put_newline(o_mputc);
- }
- base64_count = 0;
- }
- }else{
- if (base64_count > 71){
- eof_mime();
- put_newline(o_mputc);
- base64_count = 0;
- }
- if (c == EOF) { /* c==EOF */
- eof_mime();
- }
- }
- if (c != EOF) { /* c==EOF */
- mimeout_addchar(c);
- }
- return;
- }
-
- /* mimeout_f != FIXED_MIME */
-
- if (c == EOF) { /* c==EOF */
- if (mimeout_mode == -1 && mimeout_state.count > 1) open_mime(output_mode);
- j = mimeout_state.count;
- mimeout_state.count = 0;
- i = 0;
- if (mimeout_mode > 0) {
- if (!nkf_isblank(mimeout_state.buf[j-1])) {
- for (;i<j;i++) {
- if (nkf_isspace(mimeout_state.buf[i]) && base64_count < 71){
- break;
- }
- mimeout_addchar(mimeout_state.buf[i]);
- }
- eof_mime();
- for (;i<j;i++) {
- mimeout_addchar(mimeout_state.buf[i]);
- }
- } else {
- for (;i<j;i++) {
- mimeout_addchar(mimeout_state.buf[i]);
- }
- eof_mime();
- }
- } else {
- for (;i<j;i++) {
- mimeout_addchar(mimeout_state.buf[i]);
- }
- }
- return;
- }
-
- if (mimeout_state.count > 0){
- lastchar = mimeout_state.buf[mimeout_state.count - 1];
- }else{
- lastchar = -1;
- }
-
- if (mimeout_mode=='Q') {
- if (c <= DEL && (output_mode==ASCII ||output_mode == ISO_8859_1)) {
- if (c == CR || c == LF) {
- close_mime();
- (*o_mputc)(c);
- base64_count = 0;
- return;
- } else if (c <= SP) {
- close_mime();
- if (base64_count > 70) {
- put_newline(o_mputc);
- base64_count = 0;
- }
- if (!nkf_isblank(c)) {
- (*o_mputc)(SP);
- base64_count++;
- }
- } else {
- if (base64_count > 70) {
- close_mime();
- put_newline(o_mputc);
- (*o_mputc)(SP);
- base64_count = 1;
- open_mime(output_mode);
- }
- if (!nkf_noescape_mime(c)) {
- mimeout_addchar(c);
- return;
- }
- }
- if (c != 0x1B) {
- (*o_mputc)(c);
- base64_count++;
- return;
- }
- }
- }
-
- if (mimeout_mode <= 0) {
- if (c <= DEL && (output_mode==ASCII || output_mode == ISO_8859_1 ||
- output_mode == UTF_8)) {
- if (nkf_isspace(c)) {
- int flag = 0;
- if (mimeout_mode == -1) {
- flag = 1;
- }
- if (c==CR || c==LF) {
- if (flag) {
- open_mime(output_mode);
- output_mode = 0;
- } else {
- base64_count = 0;
- }
- }
- for (i=0;i<mimeout_state.count;i++) {
- (*o_mputc)(mimeout_state.buf[i]);
- if (mimeout_state.buf[i] == CR || mimeout_state.buf[i] == LF){
- base64_count = 0;
- }else{
- base64_count++;
- }
- }
- if (flag) {
- eof_mime();
- base64_count = 0;
- mimeout_mode = 0;
- }
- mimeout_state.buf[0] = (char)c;
- mimeout_state.count = 1;
- }else{
- if (base64_count > 1
- && base64_count + mimeout_state.count > 76
- && mimeout_state.buf[0] != CR && mimeout_state.buf[0] != LF){
- static const char *str = "boundary=\"";
- static int len = 10;
- i = 0;
-
- for (; i < mimeout_state.count - len; ++i) {
- if (!strncmp((char *)(mimeout_state.buf+i), str, len)) {
- i += len - 2;
- break;
- }
- }
-
- if (i == 0 || i == mimeout_state.count - len) {
- put_newline(o_mputc);
- base64_count = 0;
- if (!nkf_isspace(mimeout_state.buf[0])){
- (*o_mputc)(SP);
- base64_count++;
- }
- }
- else {
- int j;
- for (j = 0; j <= i; ++j) {
- (*o_mputc)(mimeout_state.buf[j]);
- }
- put_newline(o_mputc);
- base64_count = 1;
- for (; j <= mimeout_state.count; ++j) {
- mimeout_state.buf[j - i] = mimeout_state.buf[j];
- }
- mimeout_state.count -= i;
- }
- }
- mimeout_state.buf[mimeout_state.count++] = (char)c;
- if (mimeout_state.count>MIMEOUT_BUF_LENGTH) {
- open_mime(output_mode);
- }
- }
- return;
- }else{
- if (lastchar==CR || lastchar == LF){
- for (i=0;i<mimeout_state.count;i++) {
- (*o_mputc)(mimeout_state.buf[i]);
- }
- base64_count = 0;
- mimeout_state.count = 0;
- }
- if (lastchar==SP) {
- for (i=0;i<mimeout_state.count-1;i++) {
- (*o_mputc)(mimeout_state.buf[i]);
- base64_count++;
- }
- mimeout_state.buf[0] = SP;
- mimeout_state.count = 1;
- }
- open_mime(output_mode);
- }
- }else{
- /* mimeout_mode == 'B', 1, 2 */
- if (c <= DEL && (output_mode==ASCII || output_mode == ISO_8859_1 ||
- output_mode == UTF_8)) {
- if (lastchar == CR || lastchar == LF){
- if (nkf_isblank(c)) {
- for (i=0;i<mimeout_state.count;i++) {
- mimeout_addchar(mimeout_state.buf[i]);
- }
- mimeout_state.count = 0;
- } else {
- eof_mime();
- for (i=0;i<mimeout_state.count;i++) {
- (*o_mputc)(mimeout_state.buf[i]);
- }
- base64_count = 0;
- mimeout_state.count = 0;
- }
- mimeout_state.buf[mimeout_state.count++] = (char)c;
- return;
- }
- if (nkf_isspace(c)) {
- for (i=0;i<mimeout_state.count;i++) {
- if (SP<mimeout_state.buf[i] && mimeout_state.buf[i]<DEL) {
- eof_mime();
- for (i=0;i<mimeout_state.count;i++) {
- (*o_mputc)(mimeout_state.buf[i]);
- base64_count++;
- }
- mimeout_state.count = 0;
- }
- }
- mimeout_state.buf[mimeout_state.count++] = (char)c;
- if (mimeout_state.count>MIMEOUT_BUF_LENGTH) {
- eof_mime();
- for (j=0;j<mimeout_state.count;j++) {
- (*o_mputc)(mimeout_state.buf[j]);
- base64_count++;
- }
- mimeout_state.count = 0;
- }
- return;
- }
- if (mimeout_state.count>0 && SP<c && c!='=') {
- mimeout_state.buf[mimeout_state.count++] = (char)c;
- if (mimeout_state.count>MIMEOUT_BUF_LENGTH) {
- j = mimeout_state.count;
- mimeout_state.count = 0;
- for (i=0;i<j;i++) {
- mimeout_addchar(mimeout_state.buf[i]);
- }
- }
- return;
- }
- }
- }
- if (mimeout_state.count>0) {
- j = mimeout_state.count;
- mimeout_state.count = 0;
- for (i=0;i<j;i++) {
- if (mimeout_state.buf[i]==CR || mimeout_state.buf[i]==LF)
- break;
- mimeout_addchar(mimeout_state.buf[i]);
- }
- if (i<j) {
- eof_mime();
- base64_count=0;
- for (;i<j;i++) {
- (*o_mputc)(mimeout_state.buf[i]);
- }
- open_mime(output_mode);
- }
- }
- mimeout_addchar(c);
-}
-
-static void
-base64_conv(nkf_char c2, nkf_char c1)
-{
- mime_prechar(c2, c1);
- (*o_base64conv)(c2,c1);
-}
-
-#ifdef HAVE_ICONV_H
-typedef struct nkf_iconv_t {
- iconv_t cd;
- char *input_buffer;
- size_t input_buffer_size;
- char *output_buffer;
- size_t output_buffer_size;
-};
-
-static nkf_iconv_t
-nkf_iconv_new(char *tocode, char *fromcode)
-{
- nkf_iconv_t converter;
-
- converter->input_buffer_size = IOBUF_SIZE;
- converter->input_buffer = nkf_xmalloc(converter->input_buffer_size);
- converter->output_buffer_size = IOBUF_SIZE * 2;
- converter->output_buffer = nkf_xmalloc(converter->output_buffer_size);
- converter->cd = iconv_open(tocode, fromcode);
- if (converter->cd == (iconv_t)-1)
- {
- switch (errno) {
- case EINVAL:
- perror(fprintf("iconv doesn't support %s to %s conversion.", fromcode, tocode));
- return -1;
- default:
- perror("can't iconv_open");
- }
- }
-}
-
-static size_t
-nkf_iconv_convert(nkf_iconv_t *converter, FILE *input)
-{
- size_t invalid = (size_t)0;
- char *input_buffer = converter->input_buffer;
- size_t input_length = (size_t)0;
- char *output_buffer = converter->output_buffer;
- size_t output_length = converter->output_buffer_size;
- int c;
-
- do {
- if (c != EOF) {
- while ((c = (*i_getc)(f)) != EOF) {
- input_buffer[input_length++] = c;
- if (input_length < converter->input_buffer_size) break;
- }
- }
-
- size_t ret = iconv(converter->cd, &input_buffer, &input_length, &output_buffer, &output_length);
- while (output_length-- > 0) {
- (*o_putc)(output_buffer[converter->output_buffer_size-output_length]);
- }
- if (ret == (size_t) - 1) {
- switch (errno) {
- case EINVAL:
- if (input_buffer != converter->input_buffer)
- memmove(converter->input_buffer, input_buffer, input_length);
- break;
- case E2BIG:
- converter->output_buffer_size *= 2;
- output_buffer = realloc(converter->outbuf, converter->output_buffer_size);
- if (output_buffer == NULL) {
- perror("can't realloc");
- return -1;
- }
- converter->output_buffer = output_buffer;
- break;
- default:
- perror("can't iconv");
- return -1;
- }
- } else {
- invalid += ret;
- }
- } while (1);
-
- return invalid;
-}
-
-
-static void
-nkf_iconv_close(nkf_iconv_t *convert)
-{
- nkf_xfree(converter->inbuf);
- nkf_xfree(converter->outbuf);
- iconv_close(converter->cd);
-}
-#endif
-
-
-static void
-reinit(void)
-{
- {
- struct input_code *p = input_code_list;
- while (p->name){
- status_reinit(p++);
- }
- }
- unbuf_f = FALSE;
- estab_f = FALSE;
- nop_f = FALSE;
- binmode_f = TRUE;
- rot_f = FALSE;
- hira_f = FALSE;
- alpha_f = FALSE;
- mime_f = MIME_DECODE_DEFAULT;
- mime_decode_f = FALSE;
- mimebuf_f = FALSE;
- broken_f = FALSE;
- iso8859_f = FALSE;
- mimeout_f = FALSE;
- x0201_f = NKF_UNSPECIFIED;
- iso2022jp_f = FALSE;
-#if defined(UTF8_INPUT_ENABLE) || defined(UTF8_OUTPUT_ENABLE)
- ms_ucs_map_f = UCS_MAP_ASCII;
-#endif
-#ifdef UTF8_INPUT_ENABLE
- no_cp932ext_f = FALSE;
- no_best_fit_chars_f = FALSE;
- encode_fallback = NULL;
- unicode_subchar = '?';
- input_endian = ENDIAN_BIG;
-#endif
-#ifdef UTF8_OUTPUT_ENABLE
- output_bom_f = FALSE;
- output_endian = ENDIAN_BIG;
-#endif
-#ifdef UNICODE_NORMALIZATION
- nfc_f = FALSE;
-#endif
-#ifdef INPUT_OPTION
- cap_f = FALSE;
- url_f = FALSE;
- numchar_f = FALSE;
-#endif
-#ifdef CHECK_OPTION
- noout_f = FALSE;
- debug_f = FALSE;
-#endif
- guess_f = 0;
-#ifdef EXEC_IO
- exec_f = 0;
-#endif
-#ifdef SHIFTJIS_CP932
- cp51932_f = TRUE;
- cp932inv_f = TRUE;
-#endif
-#ifdef X0212_ENABLE
- x0212_f = FALSE;
- x0213_f = FALSE;
-#endif
- {
- int i;
- for (i = 0; i < 256; i++){
- prefix_table[i] = 0;
- }
- }
- hold_count = 0;
- mimeout_state.count = 0;
- mimeout_mode = 0;
- base64_count = 0;
- f_line = 0;
- f_prev = 0;
- fold_preserve_f = FALSE;
- fold_f = FALSE;
- fold_len = 0;
- kanji_intro = DEFAULT_J;
- ascii_intro = DEFAULT_R;
- fold_margin = FOLD_MARGIN;
- o_zconv = no_connection;
- o_fconv = no_connection;
- o_eol_conv = no_connection;
- o_rot_conv = no_connection;
- o_hira_conv = no_connection;
- o_base64conv = no_connection;
- o_iso2022jp_check_conv = no_connection;
- o_putc = std_putc;
- i_getc = std_getc;
- i_ungetc = std_ungetc;
- i_bgetc = std_getc;
- i_bungetc = std_ungetc;
- o_mputc = std_putc;
- i_mgetc = std_getc;
- i_mungetc = std_ungetc;
- i_mgetc_buf = std_getc;
- i_mungetc_buf = std_ungetc;
- output_mode = ASCII;
- input_mode = ASCII;
- mime_decode_mode = FALSE;
- file_out_f = FALSE;
- eolmode_f = 0;
- input_eol = 0;
- prev_cr = 0;
- option_mode = 0;
- z_prev2=0,z_prev1=0;
-#ifdef CHECK_OPTION
- iconv_for_check = 0;
-#endif
- input_codename = NULL;
- input_encoding = NULL;
- output_encoding = NULL;
- nkf_state_init();
-#ifdef WIN32DLL
- reinitdll();
-#endif /*WIN32DLL*/
-}
-
-static int
-module_connection(void)
-{
- if (input_encoding) set_input_encoding(input_encoding);
- if (!output_encoding) {
- output_encoding = nkf_default_encoding();
- }
- if (!output_encoding) {
- if (noout_f || guess_f) output_encoding = nkf_enc_from_index(ISO_2022_JP);
- else return -1;
- }
- set_output_encoding(output_encoding);
- oconv = nkf_enc_to_oconv(output_encoding);
- o_putc = std_putc;
- if (nkf_enc_unicode_p(output_encoding))
- output_mode = UTF_8;
-
- if (x0201_f == NKF_UNSPECIFIED) {
- x0201_f = X0201_DEFAULT;
- }
-
- /* replace continuation module, from output side */
-
- /* output redirection */
-#ifdef CHECK_OPTION
- if (noout_f || guess_f){
- o_putc = no_putc;
- }
-#endif
- if (mimeout_f) {
- o_mputc = o_putc;
- o_putc = mime_putc;
- if (mimeout_f == TRUE) {
- o_base64conv = oconv; oconv = base64_conv;
- }
- /* base64_count = 0; */
- }
-
- if (eolmode_f || guess_f) {
- o_eol_conv = oconv; oconv = eol_conv;
- }
- if (rot_f) {
- o_rot_conv = oconv; oconv = rot_conv;
- }
- if (iso2022jp_f) {
- o_iso2022jp_check_conv = oconv; oconv = iso2022jp_check_conv;
- }
- if (hira_f) {
- o_hira_conv = oconv; oconv = hira_conv;
- }
- if (fold_f) {
- o_fconv = oconv; oconv = fold_conv;
- f_line = 0;
- }
- if (alpha_f || x0201_f) {
- o_zconv = oconv; oconv = z_conv;
- }
-
- i_getc = std_getc;
- i_ungetc = std_ungetc;
- /* input redirection */
-#ifdef INPUT_OPTION
- if (cap_f){
- i_cgetc = i_getc; i_getc = cap_getc;
- i_cungetc = i_ungetc; i_ungetc= cap_ungetc;
- }
- if (url_f){
- i_ugetc = i_getc; i_getc = url_getc;
- i_uungetc = i_ungetc; i_ungetc= url_ungetc;
- }
-#endif
-#ifdef NUMCHAR_OPTION
- if (numchar_f){
- i_ngetc = i_getc; i_getc = numchar_getc;
- i_nungetc = i_ungetc; i_ungetc= numchar_ungetc;
- }
-#endif
-#ifdef UNICODE_NORMALIZATION
- if (nfc_f){
- i_nfc_getc = i_getc; i_getc = nfc_getc;
- i_nfc_ungetc = i_ungetc; i_ungetc= nfc_ungetc;
- }
-#endif
- if (mime_f && mimebuf_f==FIXED_MIME) {
- i_mgetc = i_getc; i_getc = mime_getc;
- i_mungetc = i_ungetc; i_ungetc = mime_ungetc;
- }
- if (broken_f & 1) {
- i_bgetc = i_getc; i_getc = broken_getc;
- i_bungetc = i_ungetc; i_ungetc = broken_ungetc;
- }
- if (input_encoding) {
- set_iconv(-TRUE, nkf_enc_to_iconv(input_encoding));
- } else {
- set_iconv(FALSE, e_iconv);
- }
-
- {
- struct input_code *p = input_code_list;
- while (p->name){
- status_reinit(p++);
- }
- }
- return 0;
-}
-
-/*
- Conversion main loop. Code detection only.
- */
-
-#if !defined(PERL_XS) && !defined(WIN32DLL)
-static nkf_char
-noconvert(FILE *f)
-{
- nkf_char c;
-
- if (nop_f == 2)
- module_connection();
- while ((c = (*i_getc)(f)) != EOF)
- (*o_putc)(c);
- (*o_putc)(EOF);
- return 1;
-}
-#endif
-
-#define NEXT continue /* no output, get next */
-#define SKIP c2=0;continue /* no output, get next */
-#define MORE c2=c1;continue /* need one more byte */
-#define SEND (void)0 /* output c1 and c2, get next */
-#define LAST break /* end of loop, go closing */
-#define set_input_mode(mode) do { \
- input_mode = mode; \
- shift_mode = 0; \
- set_input_codename("ISO-2022-JP"); \
- debug("ISO-2022-JP"); \
-} while (0)
-
-static int
-kanji_convert(FILE *f)
-{
- nkf_char c1=0, c2=0, c3=0, c4=0;
- int shift_mode = 0; /* 0, 1, 2, 3 */
- int g2 = 0;
- int is_8bit = FALSE;
-
- if (input_encoding && !nkf_enc_asciicompat(input_encoding)) {
- is_8bit = TRUE;
- }
-
- input_mode = ASCII;
- output_mode = ASCII;
-
- if (module_connection() < 0) {
-#if !defined(PERL_XS) && !defined(WIN32DLL)
- fprintf(stderr, "no output encoding given\n");
-#endif
- return -1;
- }
- check_bom(f);
-
-#ifdef UTF8_INPUT_ENABLE
- if(iconv == w_iconv32){
- while ((c1 = (*i_getc)(f)) != EOF &&
- (c2 = (*i_getc)(f)) != EOF &&
- (c3 = (*i_getc)(f)) != EOF &&
- (c4 = (*i_getc)(f)) != EOF) {
- nkf_char c5, c6, c7, c8;
- if (nkf_iconv_utf_32(c1, c2, c3, c4) == (size_t)NKF_ICONV_WAIT_COMBINING_CHAR) {
- if ((c5 = (*i_getc)(f)) != EOF &&
- (c6 = (*i_getc)(f)) != EOF &&
- (c7 = (*i_getc)(f)) != EOF &&
- (c8 = (*i_getc)(f)) != EOF) {
- if (nkf_iconv_utf_32_combine(c1, c2, c3, c4, c5, c6, c7, c8)) {
- (*i_ungetc)(c8, f);
- (*i_ungetc)(c7, f);
- (*i_ungetc)(c6, f);
- (*i_ungetc)(c5, f);
- nkf_iconv_utf_32_nocombine(c1, c2, c3, c4);
- }
- } else {
- nkf_iconv_utf_32_nocombine(c1, c2, c3, c4);
- }
- }
- }
- goto finished;
- }
- else if (iconv == w_iconv16) {
- while ((c1 = (*i_getc)(f)) != EOF &&
- (c2 = (*i_getc)(f)) != EOF) {
- size_t ret = nkf_iconv_utf_16(c1, c2, 0, 0);
- if (ret == NKF_ICONV_NEED_TWO_MORE_BYTES &&
- (c3 = (*i_getc)(f)) != EOF &&
- (c4 = (*i_getc)(f)) != EOF) {
- nkf_iconv_utf_16(c1, c2, c3, c4);
- } else if (ret == (size_t)NKF_ICONV_WAIT_COMBINING_CHAR) {
- if ((c3 = (*i_getc)(f)) != EOF &&
- (c4 = (*i_getc)(f)) != EOF) {
- if (nkf_iconv_utf_16_combine(c1, c2, c3, c4)) {
- (*i_ungetc)(c4, f);
- (*i_ungetc)(c3, f);
- nkf_iconv_utf_16_nocombine(c1, c2);
- }
- } else {
- nkf_iconv_utf_16_nocombine(c1, c2);
- }
- }
- }
- goto finished;
- }
-#endif
-
- while ((c1 = (*i_getc)(f)) != EOF) {
-#ifdef INPUT_CODE_FIX
- if (!input_encoding)
-#endif
- code_status(c1);
- if (c2) {
- /* second byte */
- if (c2 > ((input_encoding && nkf_enc_cp5022x_p(input_encoding)) ? 0x92 : DEL)) {
- /* in case of 8th bit is on */
- if (!estab_f&&!mime_decode_mode) {
- /* in case of not established yet */
- /* It is still ambiguous */
- if (h_conv(f, c2, c1)==EOF) {
- LAST;
- }
- else {
- SKIP;
- }
- }
- else {
- /* in case of already established */
- if (c1 < 0x40) {
- /* ignore bogus code */
- SKIP;
- } else {
- SEND;
- }
- }
- }
- else {
- /* 2nd byte of 7 bit code or SJIS */
- SEND;
- }
- }
- else if (nkf_char_unicode_p(c1)) {
- (*oconv)(0, c1);
- NEXT;
- }
- else {
- /* first byte */
- if (input_mode == JIS_X_0208 && DEL <= c1 && c1 < 0x92) {
- /* CP5022x */
- MORE;
- }else if (input_codename && input_codename[0] == 'I' &&
- 0xA1 <= c1 && c1 <= 0xDF) {
- /* JIS X 0201 Katakana in 8bit JIS */
- c2 = JIS_X_0201_1976_K;
- c1 &= 0x7f;
- SEND;
- } else if (c1 > DEL) {
- /* 8 bit code */
- if (!estab_f && !iso8859_f) {
- /* not established yet */
- MORE;
- } else { /* estab_f==TRUE */
- if (iso8859_f) {
- c2 = ISO_8859_1;
- c1 &= 0x7f;
- SEND;
- }
- else if ((iconv == s_iconv && 0xA0 <= c1 && c1 <= 0xDF) ||
- (ms_ucs_map_f == UCS_MAP_CP10001 && (c1 == 0xFD || c1 == 0xFE))) {
- /* JIS X 0201 */
- c2 = JIS_X_0201_1976_K;
- c1 &= 0x7f;
- SEND;
- }
- else {
- /* already established */
- MORE;
- }
- }
- } else if (SP < c1 && c1 < DEL) {
- /* in case of Roman characters */
- if (shift_mode) {
- /* output 1 shifted byte */
- if (iso8859_f) {
- c2 = ISO_8859_1;
- SEND;
- } else if (nkf_byte_jisx0201_katakana_p(c1)){
- /* output 1 shifted byte */
- c2 = JIS_X_0201_1976_K;
- SEND;
- } else {
- /* look like bogus code */
- SKIP;
- }
- } else if (input_mode == JIS_X_0208 || input_mode == JIS_X_0212 ||
- input_mode == JIS_X_0213_1 || input_mode == JIS_X_0213_2) {
- /* in case of Kanji shifted */
- MORE;
- } else if (c1 == '=' && mime_f && !mime_decode_mode) {
- /* Check MIME code */
- if ((c1 = (*i_getc)(f)) == EOF) {
- (*oconv)(0, '=');
- LAST;
- } else if (c1 == '?') {
- /* =? is mime conversion start sequence */
- if(mime_f == STRICT_MIME) {
- /* check in real detail */
- if (mime_begin_strict(f) == EOF)
- LAST;
- SKIP;
- } else if (mime_begin(f) == EOF)
- LAST;
- SKIP;
- } else {
- (*oconv)(0, '=');
- (*i_ungetc)(c1,f);
- SKIP;
- }
- } else {
- /* normal ASCII code */
- SEND;
- }
- } else if (c1 == SI && (!is_8bit || mime_decode_mode)) {
- shift_mode = 0;
- SKIP;
- } else if (c1 == SO && (!is_8bit || mime_decode_mode)) {
- shift_mode = 1;
- SKIP;
- } else if (c1 == ESC && (!is_8bit || mime_decode_mode)) {
- if ((c1 = (*i_getc)(f)) == EOF) {
- (*oconv)(0, ESC);
- LAST;
- }
- else if (c1 == '&') {
- /* IRR */
- if ((c1 = (*i_getc)(f)) == EOF) {
- LAST;
- } else {
- SKIP;
- }
- }
- else if (c1 == '$') {
- /* GZDMx */
- if ((c1 = (*i_getc)(f)) == EOF) {
- /* don't send bogus code
- (*oconv)(0, ESC);
- (*oconv)(0, '$'); */
- LAST;
- } else if (c1 == '@' || c1 == 'B') {
- /* JIS X 0208 */
- set_input_mode(JIS_X_0208);
- SKIP;
- } else if (c1 == '(') {
- /* GZDM4 */
- if ((c1 = (*i_getc)(f)) == EOF) {
- /* don't send bogus code
- (*oconv)(0, ESC);
- (*oconv)(0, '$');
- (*oconv)(0, '(');
- */
- LAST;
- } else if (c1 == '@'|| c1 == 'B') {
- /* JIS X 0208 */
- set_input_mode(JIS_X_0208);
- SKIP;
-#ifdef X0212_ENABLE
- } else if (c1 == 'D'){
- set_input_mode(JIS_X_0212);
- SKIP;
-#endif /* X0212_ENABLE */
- } else if (c1 == 'O' || c1 == 'Q'){
- set_input_mode(JIS_X_0213_1);
- SKIP;
- } else if (c1 == 'P'){
- set_input_mode(JIS_X_0213_2);
- SKIP;
- } else {
- /* could be some special code */
- (*oconv)(0, ESC);
- (*oconv)(0, '$');
- (*oconv)(0, '(');
- (*oconv)(0, c1);
- SKIP;
- }
- } else if (broken_f&0x2) {
- /* accept any ESC-(-x as broken code ... */
- input_mode = JIS_X_0208;
- shift_mode = 0;
- SKIP;
- } else {
- (*oconv)(0, ESC);
- (*oconv)(0, '$');
- (*oconv)(0, c1);
- SKIP;
- }
- } else if (c1 == '(') {
- /* GZD4 */
- if ((c1 = (*i_getc)(f)) == EOF) {
- /* don't send bogus code
- (*oconv)(0, ESC);
- (*oconv)(0, '('); */
- LAST;
- }
- else if (c1 == 'I') {
- /* JIS X 0201 Katakana */
- set_input_mode(JIS_X_0201_1976_K);
- shift_mode = 1;
- SKIP;
- }
- else if (c1 == 'B' || c1 == 'J' || c1 == 'H') {
- /* ISO-646IRV:1983 or JIS X 0201 Roman or JUNET */
- set_input_mode(ASCII);
- SKIP;
- }
- else if (broken_f&0x2) {
- set_input_mode(ASCII);
- SKIP;
- }
- else {
- (*oconv)(0, ESC);
- (*oconv)(0, '(');
- SEND;
- }
- }
- else if (c1 == '.') {
- /* G2D6 */
- if ((c1 = (*i_getc)(f)) == EOF) {
- LAST;
- }
- else if (c1 == 'A') {
- /* ISO-8859-1 */
- g2 = ISO_8859_1;
- SKIP;
- }
- else {
- (*oconv)(0, ESC);
- (*oconv)(0, '.');
- SEND;
- }
- }
- else if (c1 == 'N') {
- /* SS2 */
- c1 = (*i_getc)(f);
- if (g2 == ISO_8859_1) {
- c2 = ISO_8859_1;
- SEND;
- }else{
- (*i_ungetc)(c1, f);
- /* lonely ESC */
- (*oconv)(0, ESC);
- SEND;
- }
- }
- else {
- i_ungetc(c1,f);
- /* lonely ESC */
- (*oconv)(0, ESC);
- SKIP;
- }
- } else if (c1 == ESC && iconv == s_iconv) {
- /* ESC in Shift_JIS */
- if ((c1 = (*i_getc)(f)) == EOF) {
- (*oconv)(0, ESC);
- LAST;
- } else if (c1 == '$') {
- /* J-PHONE emoji */
- if ((c1 = (*i_getc)(f)) == EOF) {
- LAST;
- } else if (('E' <= c1 && c1 <= 'G') ||
- ('O' <= c1 && c1 <= 'Q')) {
- /*
- NUM : 0 1 2 3 4 5
- BYTE: G E F O P Q
- C%7 : 1 6 0 2 3 4
- C%7 : 0 1 2 3 4 5 6
- NUM : 2 0 3 4 5 X 1
- */
- static const nkf_char jphone_emoji_first_table[7] =
- {0xE1E0, 0xDFE0, 0xE2E0, 0xE3E0, 0xE4E0, 0xDFE0, 0xE0E0};
- c3 = nkf_char_unicode_new(jphone_emoji_first_table[c1 % 7]);
- if ((c1 = (*i_getc)(f)) == EOF) LAST;
- while (SP <= c1 && c1 <= 'z') {
- (*oconv)(0, c1 + c3);
- if ((c1 = (*i_getc)(f)) == EOF) LAST;
- }
- SKIP;
- }
- else {
- (*oconv)(0, ESC);
- (*oconv)(0, '$');
- SEND;
- }
- }
- else {
- i_ungetc(c1,f);
- /* lonely ESC */
- (*oconv)(0, ESC);
- SKIP;
- }
- } else if (c1 == LF || c1 == CR) {
- if (broken_f&4) {
- input_mode = ASCII; set_iconv(FALSE, 0);
- SEND;
- } else if (mime_decode_f && !mime_decode_mode){
- if (c1 == LF) {
- if ((c1=(*i_getc)(f))!=EOF && c1 == SP) {
- i_ungetc(SP,f);
- continue;
- } else {
- i_ungetc(c1,f);
- }
- c1 = LF;
- SEND;
- } else { /* if (c1 == CR)*/
- if ((c1=(*i_getc)(f))!=EOF) {
- if (c1==SP) {
- i_ungetc(SP,f);
- continue;
- } else if (c1 == LF && (c1=(*i_getc)(f))!=EOF && c1 == SP) {
- i_ungetc(SP,f);
- continue;
- } else {
- i_ungetc(c1,f);
- }
- i_ungetc(LF,f);
- } else {
- i_ungetc(c1,f);
- }
- c1 = CR;
- SEND;
- }
- }
- } else
- SEND;
- }
- /* send: */
- switch(input_mode){
- case ASCII:
- switch ((*iconv)(c2, c1, 0)) { /* can be EUC / SJIS / UTF-8 */
- case -2:
- /* 4 bytes UTF-8 */
- if ((c3 = (*i_getc)(f)) != EOF) {
- code_status(c3);
- c3 <<= 8;
- if ((c4 = (*i_getc)(f)) != EOF) {
- code_status(c4);
- (*iconv)(c2, c1, c3|c4);
- }
- }
- break;
- case -3:
- /* 4 bytes UTF-8 (check combining character) */
- if ((c3 = (*i_getc)(f)) != EOF) {
- if ((c4 = (*i_getc)(f)) != EOF) {
- if (w_iconv_combine(c2, c1, 0, c3, c4, 0)) {
- (*i_ungetc)(c4, f);
- (*i_ungetc)(c3, f);
- w_iconv_nocombine(c2, c1, 0);
- }
- } else {
- (*i_ungetc)(c3, f);
- w_iconv_nocombine(c2, c1, 0);
- }
- } else {
- w_iconv_nocombine(c2, c1, 0);
- }
- break;
- case -1:
- /* 3 bytes EUC or UTF-8 */
- if ((c3 = (*i_getc)(f)) != EOF) {
- code_status(c3);
- if ((*iconv)(c2, c1, c3) == -3) {
- /* 6 bytes UTF-8 (check combining character) */
- nkf_char c5, c6;
- if ((c4 = (*i_getc)(f)) != EOF) {
- if ((c5 = (*i_getc)(f)) != EOF) {
- if ((c6 = (*i_getc)(f)) != EOF) {
- if (w_iconv_combine(c2, c1, c3, c4, c5, c6)) {
- (*i_ungetc)(c6, f);
- (*i_ungetc)(c5, f);
- (*i_ungetc)(c4, f);
- w_iconv_nocombine(c2, c1, c3);
- }
- } else {
- (*i_ungetc)(c5, f);
- (*i_ungetc)(c4, f);
- w_iconv_nocombine(c2, c1, c3);
- }
- } else {
- (*i_ungetc)(c4, f);
- w_iconv_nocombine(c2, c1, c3);
- }
- } else {
- w_iconv_nocombine(c2, c1, c3);
- }
- }
- }
- break;
- }
- break;
- case JIS_X_0208:
- case JIS_X_0213_1:
- if (ms_ucs_map_f &&
- 0x7F <= c2 && c2 <= 0x92 &&
- 0x21 <= c1 && c1 <= 0x7E) {
- /* CP932 UDC */
- c1 = nkf_char_unicode_new((c2 - 0x7F) * 94 + c1 - 0x21 + 0xE000);
- c2 = 0;
- }
- (*oconv)(c2, c1); /* this is JIS, not SJIS/EUC case */
- break;
-#ifdef X0212_ENABLE
- case JIS_X_0212:
- (*oconv)(PREFIX_EUCG3 | c2, c1);
- break;
-#endif /* X0212_ENABLE */
- case JIS_X_0213_2:
- (*oconv)(PREFIX_EUCG3 | c2, c1);
- break;
- default:
- (*oconv)(input_mode, c1); /* other special case */
- }
-
- c2 = 0;
- c3 = 0;
- continue;
- /* goto next_word */
- }
-
-finished:
- /* epilogue */
- (*iconv)(EOF, 0, 0);
- if (!input_codename)
- {
- if (is_8bit) {
- struct input_code *p = input_code_list;
- struct input_code *result = p;
- while (p->name){
- if (p->score < result->score) result = p;
- ++p;
- }
- set_input_codename(result->name);
-#ifdef CHECK_OPTION
- debug(result->name);
-#endif
- }
- }
- return 0;
-}
-
-/*
- * int options(unsigned char *cp)
- *
- * return values:
- * 0: success
- * -1: ArgumentError
- */
-static int
-options(unsigned char *cp)
-{
- nkf_char i, j;
- unsigned char *p;
- unsigned char *cp_back = NULL;
- nkf_encoding *enc;
-
- if (option_mode==1)
- return 0;
- while(*cp && *cp++!='-');
- while (*cp || cp_back) {
- if(!*cp){
- cp = cp_back;
- cp_back = NULL;
- continue;
- }
- p = 0;
- switch (*cp++) {
- case '-': /* literal options */
- if (!*cp || *cp == SP) { /* ignore the rest of arguments */
- option_mode = 1;
- return 0;
- }
- for (i=0;i<(int)(sizeof(long_option)/sizeof(long_option[0]));i++) {
- p = (unsigned char *)long_option[i].name;
- for (j=0;*p && *p != '=' && *p == cp[j];p++, j++);
- if (*p == cp[j] || cp[j] == SP){
- p = &cp[j] + 1;
- break;
- }
- p = 0;
- }
- if (p == 0) {
-#if !defined(PERL_XS) && !defined(WIN32DLL)
- fprintf(stderr, "unknown long option: --%s\n", cp);
-#endif
- return -1;
- }
- while(*cp && *cp != SP && cp++);
- if (long_option[i].alias[0]){
- cp_back = cp;
- cp = (unsigned char *)long_option[i].alias;
- }else{
-#ifndef PERL_XS
- if (strcmp(long_option[i].name, "help") == 0){
- usage();
- exit(EXIT_SUCCESS);
- }
-#endif
- if (strcmp(long_option[i].name, "ic=") == 0){
- enc = nkf_enc_find((char *)p);
- if (!enc) continue;
- input_encoding = enc;
- continue;
- }
- if (strcmp(long_option[i].name, "oc=") == 0){
- enc = nkf_enc_find((char *)p);
- /* if (enc <= 0) continue; */
- if (!enc) continue;
- output_encoding = enc;
- continue;
- }
- if (strcmp(long_option[i].name, "guess=") == 0){
- if (p[0] == '0' || p[0] == '1') {
- guess_f = 1;
- } else {
- guess_f = 2;
- }
- continue;
- }
-#ifdef OVERWRITE
- if (strcmp(long_option[i].name, "overwrite") == 0){
- file_out_f = TRUE;
- overwrite_f = TRUE;
- preserve_time_f = TRUE;
- continue;
- }
- if (strcmp(long_option[i].name, "overwrite=") == 0){
- file_out_f = TRUE;
- overwrite_f = TRUE;
- preserve_time_f = TRUE;
- backup_f = TRUE;
- backup_suffix = (char *)p;
- continue;
- }
- if (strcmp(long_option[i].name, "in-place") == 0){
- file_out_f = TRUE;
- overwrite_f = TRUE;
- preserve_time_f = FALSE;
- continue;
- }
- if (strcmp(long_option[i].name, "in-place=") == 0){
- file_out_f = TRUE;
- overwrite_f = TRUE;
- preserve_time_f = FALSE;
- backup_f = TRUE;
- backup_suffix = (char *)p;
- continue;
- }
-#endif
-#ifdef INPUT_OPTION
- if (strcmp(long_option[i].name, "cap-input") == 0){
- cap_f = TRUE;
- continue;
- }
- if (strcmp(long_option[i].name, "url-input") == 0){
- url_f = TRUE;
- continue;
- }
-#endif
-#ifdef NUMCHAR_OPTION
- if (strcmp(long_option[i].name, "numchar-input") == 0){
- numchar_f = TRUE;
- continue;
- }
-#endif
-#ifdef CHECK_OPTION
- if (strcmp(long_option[i].name, "no-output") == 0){
- noout_f = TRUE;
- continue;
- }
- if (strcmp(long_option[i].name, "debug") == 0){
- debug_f = TRUE;
- continue;
- }
-#endif
- if (strcmp(long_option[i].name, "cp932") == 0){
-#ifdef SHIFTJIS_CP932
- cp51932_f = TRUE;
- cp932inv_f = -TRUE;
-#endif
-#ifdef UTF8_OUTPUT_ENABLE
- ms_ucs_map_f = UCS_MAP_CP932;
-#endif
- continue;
- }
- if (strcmp(long_option[i].name, "no-cp932") == 0){
-#ifdef SHIFTJIS_CP932
- cp51932_f = FALSE;
- cp932inv_f = FALSE;
-#endif
-#ifdef UTF8_OUTPUT_ENABLE
- ms_ucs_map_f = UCS_MAP_ASCII;
-#endif
- continue;
- }
-#ifdef SHIFTJIS_CP932
- if (strcmp(long_option[i].name, "cp932inv") == 0){
- cp932inv_f = -TRUE;
- continue;
- }
-#endif
-
-#ifdef X0212_ENABLE
- if (strcmp(long_option[i].name, "x0212") == 0){
- x0212_f = TRUE;
- continue;
- }
-#endif
-
-#ifdef EXEC_IO
- if (strcmp(long_option[i].name, "exec-in") == 0){
- exec_f = 1;
- return 0;
- }
- if (strcmp(long_option[i].name, "exec-out") == 0){
- exec_f = -1;
- return 0;
- }
-#endif
-#if defined(UTF8_OUTPUT_ENABLE) && defined(UTF8_INPUT_ENABLE)
- if (strcmp(long_option[i].name, "no-cp932ext") == 0){
- no_cp932ext_f = TRUE;
- continue;
- }
- if (strcmp(long_option[i].name, "no-best-fit-chars") == 0){
- no_best_fit_chars_f = TRUE;
- continue;
- }
- if (strcmp(long_option[i].name, "fb-skip") == 0){
- encode_fallback = NULL;
- continue;
- }
- if (strcmp(long_option[i].name, "fb-html") == 0){
- encode_fallback = encode_fallback_html;
- continue;
- }
- if (strcmp(long_option[i].name, "fb-xml") == 0){
- encode_fallback = encode_fallback_xml;
- continue;
- }
- if (strcmp(long_option[i].name, "fb-java") == 0){
- encode_fallback = encode_fallback_java;
- continue;
- }
- if (strcmp(long_option[i].name, "fb-perl") == 0){
- encode_fallback = encode_fallback_perl;
- continue;
- }
- if (strcmp(long_option[i].name, "fb-subchar") == 0){
- encode_fallback = encode_fallback_subchar;
- continue;
- }
- if (strcmp(long_option[i].name, "fb-subchar=") == 0){
- encode_fallback = encode_fallback_subchar;
- unicode_subchar = 0;
- if (p[0] != '0'){
- /* decimal number */
- for (i = 0; i < 7 && nkf_isdigit(p[i]); i++){
- unicode_subchar *= 10;
- unicode_subchar += hex2bin(p[i]);
- }
- }else if(p[1] == 'x' || p[1] == 'X'){
- /* hexadecimal number */
- for (i = 2; i < 8 && nkf_isxdigit(p[i]); i++){
- unicode_subchar <<= 4;
- unicode_subchar |= hex2bin(p[i]);
- }
- }else{
- /* octal number */
- for (i = 1; i < 8 && nkf_isoctal(p[i]); i++){
- unicode_subchar *= 8;
- unicode_subchar += hex2bin(p[i]);
- }
- }
- w16e_conv(unicode_subchar, &i, &j);
- unicode_subchar = i<<8 | j;
- continue;
- }
-#endif
-#ifdef UTF8_OUTPUT_ENABLE
- if (strcmp(long_option[i].name, "ms-ucs-map") == 0){
- ms_ucs_map_f = UCS_MAP_MS;
- continue;
- }
-#endif
-#ifdef UNICODE_NORMALIZATION
- if (strcmp(long_option[i].name, "utf8mac-input") == 0){
- nfc_f = TRUE;
- continue;
- }
-#endif
- if (strcmp(long_option[i].name, "prefix=") == 0){
- if (nkf_isgraph(p[0])){
- for (i = 1; nkf_isgraph(p[i]); i++){
- prefix_table[p[i]] = p[0];
- }
- }
- continue;
- }
-#if !defined(PERL_XS) && !defined(WIN32DLL)
- fprintf(stderr, "unsupported long option: --%s\n", long_option[i].name);
-#endif
- return -1;
- }
- continue;
- case 'b': /* buffered mode */
- unbuf_f = FALSE;
- continue;
- case 'u': /* non bufferd mode */
- unbuf_f = TRUE;
- continue;
- case 't': /* transparent mode */
- if (*cp=='1') {
- /* alias of -t */
- cp++;
- nop_f = TRUE;
- } else if (*cp=='2') {
- /*
- * -t with put/get
- *
- * nkf -t2MB hoge.bin | nkf -t2mB | diff -s - hoge.bin
- *
- */
- cp++;
- nop_f = 2;
- } else
- nop_f = TRUE;
- continue;
- case 'j': /* JIS output */
- case 'n':
- output_encoding = nkf_enc_from_index(ISO_2022_JP);
- continue;
- case 'e': /* AT&T EUC output */
- output_encoding = nkf_enc_from_index(EUCJP_NKF);
- continue;
- case 's': /* SJIS output */
- output_encoding = nkf_enc_from_index(SHIFT_JIS);
- continue;
- case 'l': /* ISO8859 Latin-1 support, no conversion */
- iso8859_f = TRUE; /* Only compatible with ISO-2022-JP */
- input_encoding = nkf_enc_from_index(ISO_8859_1);
- continue;
- case 'i': /* Kanji IN ESC-$-@/B */
- if (*cp=='@'||*cp=='B')
- kanji_intro = *cp++;
- continue;
- case 'o': /* ASCII IN ESC-(-J/B/H */
- /* ESC ( H was used in initial JUNET messages */
- if (*cp=='J'||*cp=='B'||*cp=='H')
- ascii_intro = *cp++;
- continue;
- case 'h':
- /*
- bit:1 katakana->hiragana
- bit:2 hiragana->katakana
- */
- if ('9'>= *cp && *cp>='0')
- hira_f |= (*cp++ -'0');
- else
- hira_f |= 1;
- continue;
- case 'r':
- rot_f = TRUE;
- continue;
-#if defined(MSDOS) || defined(__OS2__)
- case 'T':
- binmode_f = FALSE;
- continue;
-#endif
-#ifndef PERL_XS
- case 'V':
- show_configuration();
- exit(EXIT_SUCCESS);
- break;
- case 'v':
- version();
- exit(EXIT_SUCCESS);
- break;
-#endif
-#ifdef UTF8_OUTPUT_ENABLE
- case 'w': /* UTF-{8,16,32} output */
- if (cp[0] == '8') {
- cp++;
- if (cp[0] == '0'){
- cp++;
- output_encoding = nkf_enc_from_index(UTF_8N);
- } else {
- output_bom_f = TRUE;
- output_encoding = nkf_enc_from_index(UTF_8_BOM);
- }
- } else {
- int enc_idx;
- if ('1'== cp[0] && '6'==cp[1]) {
- cp += 2;
- enc_idx = UTF_16;
- } else if ('3'== cp[0] && '2'==cp[1]) {
- cp += 2;
- enc_idx = UTF_32;
- } else {
- output_encoding = nkf_enc_from_index(UTF_8);
- continue;
- }
- if (cp[0]=='L') {
- cp++;
- output_endian = ENDIAN_LITTLE;
- output_bom_f = TRUE;
- } else if (cp[0] == 'B') {
- cp++;
- output_bom_f = TRUE;
- }
- if (cp[0] == '0'){
- output_bom_f = FALSE;
- cp++;
- enc_idx = enc_idx == UTF_16
- ? (output_endian == ENDIAN_LITTLE ? UTF_16LE : UTF_16BE)
- : (output_endian == ENDIAN_LITTLE ? UTF_32LE : UTF_32BE);
- } else {
- enc_idx = enc_idx == UTF_16
- ? (output_endian == ENDIAN_LITTLE ? UTF_16LE_BOM : UTF_16BE_BOM)
- : (output_endian == ENDIAN_LITTLE ? UTF_32LE_BOM : UTF_32BE_BOM);
- }
- output_encoding = nkf_enc_from_index(enc_idx);
- }
- continue;
-#endif
-#ifdef UTF8_INPUT_ENABLE
- case 'W': /* UTF input */
- if (cp[0] == '8') {
- cp++;
- input_encoding = nkf_enc_from_index(UTF_8);
- }else{
- int enc_idx;
- if ('1'== cp[0] && '6'==cp[1]) {
- cp += 2;
- input_endian = ENDIAN_BIG;
- enc_idx = UTF_16;
- } else if ('3'== cp[0] && '2'==cp[1]) {
- cp += 2;
- input_endian = ENDIAN_BIG;
- enc_idx = UTF_32;
- } else {
- input_encoding = nkf_enc_from_index(UTF_8);
- continue;
- }
- if (cp[0]=='L') {
- cp++;
- input_endian = ENDIAN_LITTLE;
- } else if (cp[0] == 'B') {
- cp++;
- input_endian = ENDIAN_BIG;
- }
- enc_idx = (enc_idx == UTF_16
- ? (input_endian == ENDIAN_LITTLE ? UTF_16LE : UTF_16BE)
- : (input_endian == ENDIAN_LITTLE ? UTF_32LE : UTF_32BE));
- input_encoding = nkf_enc_from_index(enc_idx);
- }
- continue;
-#endif
- /* Input code assumption */
- case 'J': /* ISO-2022-JP input */
- input_encoding = nkf_enc_from_index(ISO_2022_JP);
- continue;
- case 'E': /* EUC-JP input */
- input_encoding = nkf_enc_from_index(EUCJP_NKF);
- continue;
- case 'S': /* Shift_JIS input */
- input_encoding = nkf_enc_from_index(SHIFT_JIS);
- continue;
- case 'Z': /* Convert X0208 alphabet to ascii */
- /* alpha_f
- bit:0 Convert JIS X 0208 Alphabet to ASCII
- bit:1 Convert Kankaku to one space
- bit:2 Convert Kankaku to two spaces
- bit:3 Convert HTML Entity
- bit:4 Convert JIS X 0208 Katakana to JIS X 0201 Katakana
- */
- while ('0'<= *cp && *cp <='4') {
- alpha_f |= 1 << (*cp++ - '0');
- }
- alpha_f |= 1;
- continue;
- case 'x': /* Convert X0201 kana to X0208 or X0201 Conversion */
- x0201_f = FALSE; /* No X0201->X0208 conversion */
- /* accept X0201
- ESC-(-I in JIS, EUC, MS Kanji
- SI/SO in JIS, EUC, MS Kanji
- SS2 in EUC, JIS, not in MS Kanji
- MS Kanji (0xa0-0xdf)
- output X0201
- ESC-(-I in JIS (0x20-0x5f)
- SS2 in EUC (0xa0-0xdf)
- 0xa0-0xd in MS Kanji (0xa0-0xdf)
- */
- continue;
- case 'X': /* Convert X0201 kana to X0208 */
- x0201_f = TRUE;
- continue;
- case 'F': /* prserve new lines */
- fold_preserve_f = TRUE;
- case 'f': /* folding -f60 or -f */
- fold_f = TRUE;
- fold_len = 0;
- while('0'<= *cp && *cp <='9') { /* we don't use atoi here */
- fold_len *= 10;
- fold_len += *cp++ - '0';
- }
- if (!(0<fold_len && fold_len<BUFSIZ))
- fold_len = DEFAULT_FOLD;
- if (*cp=='-') {
- fold_margin = 0;
- cp++;
- while('0'<= *cp && *cp <='9') { /* we don't use atoi here */
- fold_margin *= 10;
- fold_margin += *cp++ - '0';
- }
- }
- continue;
- case 'm': /* MIME support */
- /* mime_decode_f = TRUE; */ /* this has too large side effects... */
- if (*cp=='B'||*cp=='Q') {
- mime_decode_mode = *cp++;
- mimebuf_f = FIXED_MIME;
- } else if (*cp=='N') {
- mime_f = TRUE; cp++;
- } else if (*cp=='S') {
- mime_f = STRICT_MIME; cp++;
- } else if (*cp=='0') {
- mime_decode_f = FALSE;
- mime_f = FALSE; cp++;
- } else {
- mime_f = STRICT_MIME;
- }
- continue;
- case 'M': /* MIME output */
- if (*cp=='B') {
- mimeout_mode = 'B';
- mimeout_f = FIXED_MIME; cp++;
- } else if (*cp=='Q') {
- mimeout_mode = 'Q';
- mimeout_f = FIXED_MIME; cp++;
- } else {
- mimeout_f = TRUE;
- }
- continue;
- case 'B': /* Broken JIS support */
- /* bit:0 no ESC JIS
- bit:1 allow any x on ESC-(-x or ESC-$-x
- bit:2 reset to ascii on NL
- */
- if ('9'>= *cp && *cp>='0')
- broken_f |= 1<<(*cp++ -'0');
- else
- broken_f |= TRUE;
- continue;
-#ifndef PERL_XS
- case 'O':/* for Output file */
- file_out_f = TRUE;
- continue;
-#endif
- case 'c':/* add cr code */
- eolmode_f = CRLF;
- continue;
- case 'd':/* delete cr code */
- eolmode_f = LF;
- continue;
- case 'I': /* ISO-2022-JP output */
- iso2022jp_f = TRUE;
- continue;
- case 'L': /* line mode */
- if (*cp=='u') { /* unix */
- eolmode_f = LF; cp++;
- } else if (*cp=='m') { /* mac */
- eolmode_f = CR; cp++;
- } else if (*cp=='w') { /* windows */
- eolmode_f = CRLF; cp++;
- } else if (*cp=='0') { /* no conversion */
- eolmode_f = 0; cp++;
- }
- continue;
-#ifndef PERL_XS
- case 'g':
- if ('2' <= *cp && *cp <= '9') {
- guess_f = 2;
- cp++;
- } else if (*cp == '0' || *cp == '1') {
- guess_f = 1;
- cp++;
- } else {
- guess_f = 1;
- }
- continue;
-#endif
- case SP:
- /* module multiple options in a string are allowed for Perl module */
- while(*cp && *cp++!='-');
- continue;
- default:
-#if !defined(PERL_XS) && !defined(WIN32DLL)
- fprintf(stderr, "unknown option: -%c\n", *(cp-1));
-#endif
- /* bogus option but ignored */
- return -1;
- }
- }
- return 0;
-}
-
-#ifdef WIN32DLL
-#include "nkf32dll.c"
-#elif defined(PERL_XS)
-#else /* WIN32DLL */
-int
-main(int argc, char **argv)
-{
- FILE *fin;
- unsigned char *cp;
-
- char *outfname = NULL;
- char *origfname;
-
-#ifdef EASYWIN /*Easy Win */
- _BufferSize.y = 400;/*Set Scroll Buffer Size*/
-#endif
-#ifdef DEFAULT_CODE_LOCALE
- setlocale(LC_CTYPE, "");
-#endif
- nkf_state_init();
-
- for (argc--,argv++; (argc > 0) && **argv == '-'; argc--, argv++) {
- cp = (unsigned char *)*argv;
- options(cp);
-#ifdef EXEC_IO
- if (exec_f){
- int fds[2], pid;
- if (pipe(fds) < 0 || (pid = fork()) < 0){
- abort();
- }
- if (pid == 0){
- if (exec_f > 0){
- close(fds[0]);
- dup2(fds[1], 1);
- }else{
- close(fds[1]);
- dup2(fds[0], 0);
- }
- execvp(argv[1], &argv[1]);
- }
- if (exec_f > 0){
- close(fds[1]);
- dup2(fds[0], 0);
- }else{
- close(fds[0]);
- dup2(fds[1], 1);
- }
- argc = 0;
- break;
- }
-#endif
- }
-
- if (guess_f) {
-#ifdef CHECK_OPTION
- int debug_f_back = debug_f;
-#endif
-#ifdef EXEC_IO
- int exec_f_back = exec_f;
-#endif
-#ifdef X0212_ENABLE
- int x0212_f_back = x0212_f;
-#endif
- int x0213_f_back = x0213_f;
- int guess_f_back = guess_f;
- reinit();
- guess_f = guess_f_back;
- mime_f = FALSE;
-#ifdef CHECK_OPTION
- debug_f = debug_f_back;
-#endif
-#ifdef EXEC_IO
- exec_f = exec_f_back;
-#endif
- x0212_f = x0212_f_back;
- x0213_f = x0213_f_back;
- }
-
- if (binmode_f == TRUE)
-#if defined(__OS2__) && (defined(__IBMC__) || defined(__IBMCPP__))
- if (freopen("","wb",stdout) == NULL)
- return (-1);
-#else
- setbinmode(stdout);
-#endif
-
- if (unbuf_f)
- setbuf(stdout, (char *) NULL);
- else
- setvbuffer(stdout, (char *) stdobuf, IOBUF_SIZE);
-
- if (argc == 0) {
- if (binmode_f == TRUE)
-#if defined(__OS2__) && (defined(__IBMC__) || defined(__IBMCPP__))
- if (freopen("","rb",stdin) == NULL) return (-1);
-#else
- setbinmode(stdin);
-#endif
- setvbuffer(stdin, (char *) stdibuf, IOBUF_SIZE);
- if (nop_f)
- noconvert(stdin);
- else {
- kanji_convert(stdin);
- if (guess_f) print_guessed_code(NULL);
- }
- } else {
- int nfiles = argc;
- int is_argument_error = FALSE;
- while (argc--) {
- input_codename = NULL;
- input_eol = 0;
-#ifdef CHECK_OPTION
- iconv_for_check = 0;
-#endif
- if ((fin = fopen((origfname = *argv++), "r")) == NULL) {
- perror(*(argv-1));
- is_argument_error = TRUE;
- continue;
- } else {
-#ifdef OVERWRITE
- int fd = 0;
- int fd_backup = 0;
-#endif
-
- /* reopen file for stdout */
- if (file_out_f == TRUE) {
-#ifdef OVERWRITE
- if (overwrite_f){
- outfname = nkf_xmalloc(strlen(origfname)
- + strlen(".nkftmpXXXXXX")
- + 1);
- strcpy(outfname, origfname);
-#ifdef MSDOS
- {
- int i;
- for (i = strlen(outfname); i; --i){
- if (outfname[i - 1] == '/'
- || outfname[i - 1] == '\\'){
- break;
- }
- }
- outfname[i] = '\0';
- }
- strcat(outfname, "ntXXXXXX");
- mktemp(outfname);
- fd = open(outfname, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL,
- S_IREAD | S_IWRITE);
-#else
- strcat(outfname, ".nkftmpXXXXXX");
- fd = mkstemp(outfname);
-#endif
- if (fd < 0
- || (fd_backup = dup(fileno(stdout))) < 0
- || dup2(fd, fileno(stdout)) < 0
- ){
- perror(origfname);
- return -1;
- }
- }else
-#endif
- if(argc == 1) {
- outfname = *argv++;
- argc--;
- } else {
- outfname = "nkf.out";
- }
-
- if(freopen(outfname, "w", stdout) == NULL) {
- perror (outfname);
- return (-1);
- }
- if (binmode_f == TRUE) {
-#if defined(__OS2__) && (defined(__IBMC__) || defined(__IBMCPP__))
- if (freopen("","wb",stdout) == NULL)
- return (-1);
-#else
- setbinmode(stdout);
-#endif
- }
- }
- if (binmode_f == TRUE)
-#if defined(__OS2__) && (defined(__IBMC__) || defined(__IBMCPP__))
- if (freopen("","rb",fin) == NULL)
- return (-1);
-#else
- setbinmode(fin);
-#endif
- setvbuffer(fin, (char *) stdibuf, IOBUF_SIZE);
- if (nop_f)
- noconvert(fin);
- else {
- char *filename = NULL;
- kanji_convert(fin);
- if (nfiles > 1) filename = origfname;
- if (guess_f) print_guessed_code(filename);
- }
- fclose(fin);
-#ifdef OVERWRITE
- if (overwrite_f) {
- struct stat sb;
-#if defined(MSDOS) && !defined(__MINGW32__) && !defined(__WIN32__) && !defined(__WATCOMC__) && !defined(__EMX__) && !defined(__OS2__) && !defined(__DJGPP__)
- time_t tb[2];
-#else
- struct utimbuf tb;
-#endif
-
- fflush(stdout);
- close(fd);
- if (dup2(fd_backup, fileno(stdout)) < 0){
- perror("dup2");
- }
- if (stat(origfname, &sb)) {
- fprintf(stderr, "Can't stat %s\n", origfname);
- }
- /* $B%Q!<%_%C%7%g%s$rI|85(B */
- if (chmod(outfname, sb.st_mode)) {
- fprintf(stderr, "Can't set permission %s\n", outfname);
- }
-
- /* $B%?%$%`%9%?%s%W$rI|85(B */
- if(preserve_time_f){
-#if defined(MSDOS) && !defined(__MINGW32__) && !defined(__WIN32__) && !defined(__WATCOMC__) && !defined(__EMX__) && !defined(__OS2__) && !defined(__DJGPP__)
- tb[0] = tb[1] = sb.st_mtime;
- if (utime(outfname, tb)) {
- fprintf(stderr, "Can't set timestamp %s\n", outfname);
- }
-#else
- tb.actime = sb.st_atime;
- tb.modtime = sb.st_mtime;
- if (utime(outfname, &tb)) {
- fprintf(stderr, "Can't set timestamp %s\n", outfname);
- }
-#endif
- }
- if(backup_f){
- char *backup_filename = get_backup_filename(backup_suffix, origfname);
-#ifdef MSDOS
- unlink(backup_filename);
-#endif
- if (rename(origfname, backup_filename)) {
- perror(backup_filename);
- fprintf(stderr, "Can't rename %s to %s\n",
- origfname, backup_filename);
- }
- nkf_xfree(backup_filename);
- }else{
-#ifdef MSDOS
- if (unlink(origfname)){
- perror(origfname);
- }
-#endif
- }
- if (rename(outfname, origfname)) {
- perror(origfname);
- fprintf(stderr, "Can't rename %s to %s\n",
- outfname, origfname);
- }
- nkf_xfree(outfname);
- }
-#endif
- }
- }
- if (is_argument_error)
- return(-1);
- }
-#ifdef EASYWIN /*Easy Win */
- if (file_out_f == FALSE)
- scanf("%d",&end_check);
- else
- fclose(stdout);
-#else /* for Other OS */
- if (file_out_f == TRUE)
- fclose(stdout);
-#endif /*Easy Win */
- return (0);
-}
-#endif /* WIN32DLL */
diff --git a/ext/nkf/nkf-utf8/nkf.h b/ext/nkf/nkf-utf8/nkf.h
deleted file mode 100644
index b3a520da54..0000000000
--- a/ext/nkf/nkf-utf8/nkf.h
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- *
- * nkf.h - Header file for nkf
- *
- */
-
-#ifndef NKF_H
-#define NKF_H
-
-/* Wrapper of configurations */
-
-#ifndef MIME_DECODE_DEFAULT
-#define MIME_DECODE_DEFAULT STRICT_MIME
-#endif
-#ifndef X0201_DEFAULT
-#define X0201_DEFAULT TRUE
-#endif
-
-#if defined(DEFAULT_NEWLINE) && DEFAULT_NEWLINE == 0x0D0A
-#elif defined(DEFAULT_NEWLINE) && DEFAULT_NEWLINE == 0x0D
-#else
-#define DEFAULT_NEWLINE 0x0A
-#endif
-#ifdef HELP_OUTPUT_STDERR
-#define HELP_OUTPUT stderr
-#else
-#define HELP_OUTPUT stdout
-#endif
-
-
-/* Compatibility definitions */
-
-#ifdef nkf_char
-#elif defined(INT_IS_SHORT)
-typedef long nkf_char;
-#define NKF_INT32_C(n) (n##L)
-#else
-typedef int nkf_char;
-#define NKF_INT32_C(n) (n)
-#endif
-
-#if (defined(__TURBOC__) || defined(_MSC_VER) || defined(LSI_C) || (defined(__WATCOMC__) && defined(__386__) && !defined(__LINUX__)) || defined(__MINGW32__) || defined(__EMX__) || defined(__MSDOS__) || defined(__WINDOWS__) || defined(__DOS__) || defined(__OS2__)) && !defined(MSDOS)
-#define MSDOS
-#if (defined(__Win32__) || defined(_WIN32)) && !defined(__WIN32__)
-#define __WIN32__
-#endif
-#endif
-
-#ifdef PERL_XS
-#undef OVERWRITE
-#endif
-
-#ifndef PERL_XS
-#include <stdio.h>
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-
-#if defined(MSDOS) || defined(__OS2__)
-#include <fcntl.h>
-#include <io.h>
-#if defined(_MSC_VER) || defined(__WATCOMC__)
-#define mktemp _mktemp
-#endif
-#endif
-
-#ifdef MSDOS
-#ifdef LSI_C
-#define setbinmode(fp) fsetbin(fp)
-#elif defined(__DJGPP__)
-#include <libc/dosio.h>
-void setbinmode(FILE *fp)
-{
- /* we do not use libc's setmode(), which changes COOKED/RAW mode in device. */
- int fd, m;
- fd = fileno(fp);
- m = (__file_handle_modes[fd] & (~O_TEXT)) | O_BINARY;
- __file_handle_set(fd, m);
-}
-#else /* Microsoft C, Turbo C */
-#define setbinmode(fp) setmode(fileno(fp), O_BINARY)
-#endif
-#else /* UNIX */
-#define setbinmode(fp) (void)(fp)
-#endif
-
-#ifdef _IOFBF /* SysV and MSDOS, Windows */
-#define setvbuffer(fp, buf, size) setvbuf(fp, buf, _IOFBF, size)
-#else /* BSD */
-#define setvbuffer(fp, buf, size) setbuffer(fp, buf, size)
-#endif
-
-/*Borland C++ 4.5 EasyWin*/
-#if defined(__TURBOC__) && defined(_Windows) && !defined(__WIN32__) /*Easy Win */
-#define EASYWIN
-#ifndef __WIN16__
-#define __WIN16__
-#endif
-#include <windows.h>
-#endif
-
-#ifdef OVERWRITE
-/* added by satoru@isoternet.org */
-#if defined(__EMX__)
-#include <sys/types.h>
-#endif
-#include <sys/stat.h>
-#if !defined(MSDOS) || defined(__DJGPP__) /* UNIX, djgpp */
-#include <unistd.h>
-#if defined(__WATCOMC__)
-#include <sys/utime.h>
-#else
-#include <utime.h>
-#endif
-#else /* defined(MSDOS) */
-#ifdef __WIN32__
-#ifdef __BORLANDC__ /* BCC32 */
-#include <utime.h>
-#else /* !defined(__BORLANDC__) */
-#include <sys/utime.h>
-#endif /* (__BORLANDC__) */
-#else /* !defined(__WIN32__) */
-#if defined(_MSC_VER) || defined(__MINGW32__) || defined(__WATCOMC__) || defined(__OS2__) || defined(__EMX__) || defined(__IBMC__) || defined(__IBMCPP__) /* VC++, MinGW, Watcom, emx+gcc, IBM VAC++ */
-#include <sys/utime.h>
-#elif defined(__TURBOC__) /* BCC */
-#include <utime.h>
-#elif defined(LSI_C) /* LSI C */
-#endif /* (__WIN32__) */
-#endif
-#endif
-#endif
-
-#if !defined(DEFAULT_CODE_JIS) && !defined(DEFAULT_CODE_SJIS) && \
- !defined(DEFAULT_CODE_WINDOWS_31J) && !defined(DEFAULT_CODE_EUC) && \
- !defined(DEFAULT_CODE_UTF8) && !defined(DEFAULT_CODE_LOCALE)
-#define DEFAULT_CODE_LOCALE
-#endif
-
-#ifdef DEFAULT_CODE_LOCALE
-
-#if defined(__WIN32__) /* not win32 should be posix */
-# ifndef HAVE_LOCALE_H
-# define HAVE_LOCALE_H
-# endif
-#elif defined(__OS2__)
-# undef HAVE_LANGINFO_H /* We do not use kLIBC's langinfo. */
-# ifndef HAVE_LOCALE_H
-# define HAVE_LOCALE_H
-# endif
-#elif defined(MSDOS)
-# ifndef HAVE_LOCALE_H
-# define HAVE_LOCALE_H
-# endif
-#elif defined(__BIONIC__) /* bionic doesn't have locale */
-#else
-# ifndef HAVE_LANGINFO_H
-# define HAVE_LANGINFO_H
-# endif
-# ifndef HAVE_LOCALE_H
-# define HAVE_LOCALE_H
-# endif
-#endif
-
-#ifdef HAVE_LANGINFO_H
-#include <langinfo.h>
-#endif
-#ifdef HAVE_LOCALE_H
-#include <locale.h>
-#endif
-
-#endif /* DEFAULT_CODE_LOCALE */
-
-#define FALSE 0
-#define TRUE 1
-
-#ifndef ARG_UNUSED
-#if defined(__GNUC__)
-# define ARG_UNUSED __attribute__ ((unused))
-#else
-# define ARG_UNUSED
-#endif
-#endif
-
-#ifdef WIN32DLL
-#include "nkf32.h"
-#endif
-
-#endif /* NKF_H */
diff --git a/ext/nkf/nkf-utf8/utf8tbl.c b/ext/nkf/nkf-utf8/utf8tbl.c
deleted file mode 100644
index a31e4e7805..0000000000
--- a/ext/nkf/nkf-utf8/utf8tbl.c
+++ /dev/null
@@ -1,14638 +0,0 @@
-/*
- * utf8tbl.c - Conversion Table for nkf
- *
- */
-
-#include "config.h"
-#include "utf8tbl.h"
-
-#ifdef UTF8_OUTPUT_ENABLE
-static const unsigned short euc_to_utf8_A1[] = {
- 0x3000, 0x3001, 0x3002, 0xFF0C, 0xFF0E, 0x30FB, 0xFF1A,
- 0xFF1B, 0xFF1F, 0xFF01, 0x309B, 0x309C, 0x00B4, 0xFF40, 0x00A8,
- 0xFF3E, 0x203E, 0xFF3F, 0x30FD, 0x30FE, 0x309D, 0x309E, 0x3003,
- 0x4EDD, 0x3005, 0x3006, 0x3007, 0x30FC, 0x2014, 0x2010, 0xFF0F,
- 0xFF3C, 0x301C, 0x2016, 0xFF5C, 0x2026, 0x2025, 0x2018, 0x2019,
- 0x201C, 0x201D, 0xFF08, 0xFF09, 0x3014, 0x3015, 0xFF3B, 0xFF3D,
- 0xFF5B, 0xFF5D, 0x3008, 0x3009, 0x300A, 0x300B, 0x300C, 0x300D,
- 0x300E, 0x300F, 0x3010, 0x3011, 0xFF0B, 0x2212, 0x00B1, 0x00D7,
- 0x00F7, 0xFF1D, 0x2260, 0xFF1C, 0xFF1E, 0x2266, 0x2267, 0x221E,
- 0x2234, 0x2642, 0x2640, 0x00B0, 0x2032, 0x2033, 0x2103, 0x00A5,
- 0xFF04, 0x00A2, 0x00A3, 0xFF05, 0xFF03, 0xFF06, 0xFF0A, 0xFF20,
- 0x00A7, 0x2606, 0x2605, 0x25CB, 0x25CF, 0x25CE, 0x25C7,
-};
-
-/* Microsoft UCS Mapping Compatible */
-static const unsigned short euc_to_utf8_A1_ms[] = {
- 0x3000, 0x3001, 0x3002, 0xFF0C, 0xFF0E, 0x30FB, 0xFF1A,
- 0xFF1B, 0xFF1F, 0xFF01, 0x309B, 0x309C, 0x00B4, 0xFF40, 0x00A8,
- 0xFF3E, 0xFFE3, 0xFF3F, 0x30FD, 0x30FE, 0x309D, 0x309E, 0x3003,
- 0x4EDD, 0x3005, 0x3006, 0x3007, 0x30FC, 0x2015, 0x2010, 0xFF0F,
- 0xFF3C, 0xFF5E, 0x2225, 0xFF5C, 0x2026, 0x2025, 0x2018, 0x2019,
- 0x201C, 0x201D, 0xFF08, 0xFF09, 0x3014, 0x3015, 0xFF3B, 0xFF3D,
- 0xFF5B, 0xFF5D, 0x3008, 0x3009, 0x300A, 0x300B, 0x300C, 0x300D,
- 0x300E, 0x300F, 0x3010, 0x3011, 0xFF0B, 0xFF0D, 0x00B1, 0x00D7,
- 0x00F7, 0xFF1D, 0x2260, 0xFF1C, 0xFF1E, 0x2266, 0x2267, 0x221E,
- 0x2234, 0x2642, 0x2640, 0x00B0, 0x2032, 0x2033, 0x2103, 0xFFE5,
- 0xFF04, 0xFFE0, 0xFFE1, 0xFF05, 0xFF03, 0xFF06, 0xFF0A, 0xFF20,
- 0x00A7, 0x2606, 0x2605, 0x25CB, 0x25CF, 0x25CE, 0x25C7,
-};
-static const unsigned short euc_to_utf8_A2[] = {
- 0x25C6, 0x25A1, 0x25A0, 0x25B3, 0x25B2, 0x25BD, 0x25BC,
- 0x203B, 0x3012, 0x2192, 0x2190, 0x2191, 0x2193, 0x3013, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x2208, 0x220B, 0x2286, 0x2287, 0x2282, 0x2283,
- 0x222A, 0x2229, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x2227, 0x2228, 0x00AC, 0x21D2, 0x21D4, 0x2200,
- 0x2203, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0x2220, 0x22A5, 0x2312, 0x2202,
- 0x2207, 0x2261, 0x2252, 0x226A, 0x226B, 0x221A, 0x223D, 0x221D,
- 0x2235, 0x222B, 0x222C, 0, 0, 0, 0, 0,
- 0, 0, 0x212B, 0x2030, 0x266F, 0x266D, 0x266A, 0x2020,
- 0x2021, 0x00B6, 0, 0, 0, 0, 0x25EF,
-};
-
-/* Microsoft UCS Mapping Compatible */
-static const unsigned short euc_to_utf8_A2_ms[] = {
- 0x25C6, 0x25A1, 0x25A0, 0x25B3, 0x25B2, 0x25BD, 0x25BC,
- 0x203B, 0x3012, 0x2192, 0x2190, 0x2191, 0x2193, 0x3013, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x2208, 0x220B, 0x2286, 0x2287, 0x2282, 0x2283,
- 0x222A, 0x2229, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x2227, 0x2228, 0xFFE2, 0x21D2, 0x21D4, 0x2200,
- 0x2203, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0x2220, 0x22A5, 0x2312, 0x2202,
- 0x2207, 0x2261, 0x2252, 0x226A, 0x226B, 0x221A, 0x223D, 0x221D,
- 0x2235, 0x222B, 0x222C, 0, 0, 0, 0, 0,
- 0, 0, 0x212B, 0x2030, 0x266F, 0x266D, 0x266A, 0x2020,
- 0x2021, 0x00B6, 0, 0, 0, 0, 0x25EF,
-};
-static const unsigned short euc_to_utf8_A2_x0213[] = {
- 0x25C6, 0x25A1, 0x25A0, 0x25B3, 0x25B2, 0x25BD, 0x25BC,
- 0x203B, 0x3012, 0x2192, 0x2190, 0x2191, 0x2193, 0x3013, 0xFF07,
- 0xFF02, 0xFF0D, 0xFF5E, 0x3033, 0x3034, 0x3035, 0x303B, 0x303C,
- 0x30FF, 0x309F, 0x2208, 0x220B, 0x2286, 0x2287, 0x2282, 0x2283,
- 0x222A, 0x2229, 0x2284, 0x2285, 0x228A, 0x228B, 0x2209, 0x2205,
- 0x2305, 0x2306, 0x2227, 0x2228, 0x00AC, 0x21D2, 0x21D4, 0x2200,
- 0x2203, 0x2295, 0x2296, 0x2297, 0x2225, 0x2226, 0xFF5F, 0xFF60,
- 0x3018, 0x3019, 0x3016, 0x3017, 0x2220, 0x22A5, 0x2312, 0x2202,
- 0x2207, 0x2261, 0x2252, 0x226A, 0x226B, 0x221A, 0x223D, 0x221D,
- 0x2235, 0x222B, 0x222C, 0x2262, 0x2243, 0x2245, 0x2248, 0x2276,
- 0x2277, 0x2194, 0x212B, 0x2030, 0x266F, 0x266D, 0x266A, 0x2020,
- 0x2021, 0x00B6, 0x266E, 0x266B, 0x266C, 0x2669, 0x25EF,
-};
-static const unsigned short euc_to_utf8_A3[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xFF10, 0xFF11, 0xFF12, 0xFF13, 0xFF14, 0xFF15, 0xFF16, 0xFF17,
- 0xFF18, 0xFF19, 0, 0, 0, 0, 0, 0,
- 0, 0xFF21, 0xFF22, 0xFF23, 0xFF24, 0xFF25, 0xFF26, 0xFF27,
- 0xFF28, 0xFF29, 0xFF2A, 0xFF2B, 0xFF2C, 0xFF2D, 0xFF2E, 0xFF2F,
- 0xFF30, 0xFF31, 0xFF32, 0xFF33, 0xFF34, 0xFF35, 0xFF36, 0xFF37,
- 0xFF38, 0xFF39, 0xFF3A, 0, 0, 0, 0, 0,
- 0, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47,
- 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F,
- 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57,
- 0xFF58, 0xFF59, 0xFF5A, 0, 0, 0, 0,
-};
-static const unsigned short euc_to_utf8_A3_x0213[] = {
- 0x25B7, 0x25B6, 0x25C1, 0x25C0, 0x2197, 0x2198, 0x2196,
- 0x2199, 0x21C4, 0x21E8, 0x21E6, 0x21E7, 0x21E9, 0x2934, 0x2935,
- 0xFF10, 0xFF11, 0xFF12, 0xFF13, 0xFF14, 0xFF15, 0xFF16, 0xFF17,
- 0xFF18, 0xFF19, 0x29BF, 0x25C9, 0x303D, 0xFE46, 0xFE45, 0x25E6,
- 0x2022, 0xFF21, 0xFF22, 0xFF23, 0xFF24, 0xFF25, 0xFF26, 0xFF27,
- 0xFF28, 0xFF29, 0xFF2A, 0xFF2B, 0xFF2C, 0xFF2D, 0xFF2E, 0xFF2F,
- 0xFF30, 0xFF31, 0xFF32, 0xFF33, 0xFF34, 0xFF35, 0xFF36, 0xFF37,
- 0xFF38, 0xFF39, 0xFF3A, 0x2213, 0x2135, 0x210F, 0x33CB, 0x2113,
- 0x2127, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47,
- 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F,
- 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57,
- 0xFF58, 0xFF59, 0xFF5A, 0x30A0, 0x2013, 0x29FA, 0x29FB,
-};
-static const unsigned short euc_to_utf8_A4[] = {
- 0x3041, 0x3042, 0x3043, 0x3044, 0x3045, 0x3046, 0x3047,
- 0x3048, 0x3049, 0x304A, 0x304B, 0x304C, 0x304D, 0x304E, 0x304F,
- 0x3050, 0x3051, 0x3052, 0x3053, 0x3054, 0x3055, 0x3056, 0x3057,
- 0x3058, 0x3059, 0x305A, 0x305B, 0x305C, 0x305D, 0x305E, 0x305F,
- 0x3060, 0x3061, 0x3062, 0x3063, 0x3064, 0x3065, 0x3066, 0x3067,
- 0x3068, 0x3069, 0x306A, 0x306B, 0x306C, 0x306D, 0x306E, 0x306F,
- 0x3070, 0x3071, 0x3072, 0x3073, 0x3074, 0x3075, 0x3076, 0x3077,
- 0x3078, 0x3079, 0x307A, 0x307B, 0x307C, 0x307D, 0x307E, 0x307F,
- 0x3080, 0x3081, 0x3082, 0x3083, 0x3084, 0x3085, 0x3086, 0x3087,
- 0x3088, 0x3089, 0x308A, 0x308B, 0x308C, 0x308D, 0x308E, 0x308F,
- 0x3090, 0x3091, 0x3092, 0x3093, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short euc_to_utf8_A4_x0213[] = {
- 0x3041, 0x3042, 0x3043, 0x3044, 0x3045, 0x3046, 0x3047,
- 0x3048, 0x3049, 0x304A, 0x304B, 0x304C, 0x304D, 0x304E, 0x304F,
- 0x3050, 0x3051, 0x3052, 0x3053, 0x3054, 0x3055, 0x3056, 0x3057,
- 0x3058, 0x3059, 0x305A, 0x305B, 0x305C, 0x305D, 0x305E, 0x305F,
- 0x3060, 0x3061, 0x3062, 0x3063, 0x3064, 0x3065, 0x3066, 0x3067,
- 0x3068, 0x3069, 0x306A, 0x306B, 0x306C, 0x306D, 0x306E, 0x306F,
- 0x3070, 0x3071, 0x3072, 0x3073, 0x3074, 0x3075, 0x3076, 0x3077,
- 0x3078, 0x3079, 0x307A, 0x307B, 0x307C, 0x307D, 0x307E, 0x307F,
- 0x3080, 0x3081, 0x3082, 0x3083, 0x3084, 0x3085, 0x3086, 0x3087,
- 0x3088, 0x3089, 0x308A, 0x308B, 0x308C, 0x308D, 0x308E, 0x308F,
- 0x3090, 0x3091, 0x3092, 0x3093, 0x3094, 0x3095, 0x3096, /*0x304B*/ 0x309A,
- /*0x304D*/ 0x309A, /*0x304F*/ 0x309A, /*0x3051*/ 0x309A, /*0x3053*/ 0x309A, 0, 0, 0,
-};
-static const unsigned short euc_to_utf8_A5[] = {
- 0x30A1, 0x30A2, 0x30A3, 0x30A4, 0x30A5, 0x30A6, 0x30A7,
- 0x30A8, 0x30A9, 0x30AA, 0x30AB, 0x30AC, 0x30AD, 0x30AE, 0x30AF,
- 0x30B0, 0x30B1, 0x30B2, 0x30B3, 0x30B4, 0x30B5, 0x30B6, 0x30B7,
- 0x30B8, 0x30B9, 0x30BA, 0x30BB, 0x30BC, 0x30BD, 0x30BE, 0x30BF,
- 0x30C0, 0x30C1, 0x30C2, 0x30C3, 0x30C4, 0x30C5, 0x30C6, 0x30C7,
- 0x30C8, 0x30C9, 0x30CA, 0x30CB, 0x30CC, 0x30CD, 0x30CE, 0x30CF,
- 0x30D0, 0x30D1, 0x30D2, 0x30D3, 0x30D4, 0x30D5, 0x30D6, 0x30D7,
- 0x30D8, 0x30D9, 0x30DA, 0x30DB, 0x30DC, 0x30DD, 0x30DE, 0x30DF,
- 0x30E0, 0x30E1, 0x30E2, 0x30E3, 0x30E4, 0x30E5, 0x30E6, 0x30E7,
- 0x30E8, 0x30E9, 0x30EA, 0x30EB, 0x30EC, 0x30ED, 0x30EE, 0x30EF,
- 0x30F0, 0x30F1, 0x30F2, 0x30F3, 0x30F4, 0x30F5, 0x30F6, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short euc_to_utf8_A5_x0213[] = {
- 0x30A1, 0x30A2, 0x30A3, 0x30A4, 0x30A5, 0x30A6, 0x30A7,
- 0x30A8, 0x30A9, 0x30AA, 0x30AB, 0x30AC, 0x30AD, 0x30AE, 0x30AF,
- 0x30B0, 0x30B1, 0x30B2, 0x30B3, 0x30B4, 0x30B5, 0x30B6, 0x30B7,
- 0x30B8, 0x30B9, 0x30BA, 0x30BB, 0x30BC, 0x30BD, 0x30BE, 0x30BF,
- 0x30C0, 0x30C1, 0x30C2, 0x30C3, 0x30C4, 0x30C5, 0x30C6, 0x30C7,
- 0x30C8, 0x30C9, 0x30CA, 0x30CB, 0x30CC, 0x30CD, 0x30CE, 0x30CF,
- 0x30D0, 0x30D1, 0x30D2, 0x30D3, 0x30D4, 0x30D5, 0x30D6, 0x30D7,
- 0x30D8, 0x30D9, 0x30DA, 0x30DB, 0x30DC, 0x30DD, 0x30DE, 0x30DF,
- 0x30E0, 0x30E1, 0x30E2, 0x30E3, 0x30E4, 0x30E5, 0x30E6, 0x30E7,
- 0x30E8, 0x30E9, 0x30EA, 0x30EB, 0x30EC, 0x30ED, 0x30EE, 0x30EF,
- 0x30F0, 0x30F1, 0x30F2, 0x30F3, 0x30F4, 0x30F5, 0x30F6, /*0x30AB*/ 0x309A,
- /*0x30AD*/ 0x309A, /*0x30AF*/ 0x309A, /*0x30B1*/ 0x309A, /*0x30B3*/ 0x309A, /*0x30BB*/ 0x309A, /*0x30C4*/ 0x309A, /*0x30C8*/ 0x309A,
-};
-static const unsigned short euc_to_utf8_A6[] = {
- 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
- 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F,
- 0x03A0, 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8,
- 0x03A9, 0, 0, 0, 0, 0, 0, 0,
- 0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,
- 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
- 0x03C0, 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8,
- 0x03C9, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short euc_to_utf8_A6_x0213[] = {
- 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
- 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F,
- 0x03A0, 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8,
- 0x03A9, 0x2664, 0x2660, 0x2662, 0x2666, 0x2661, 0x2665, 0x2667,
- 0x2663, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,
- 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
- 0x03C0, 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8,
- 0x03C9, 0x03C2, 0x24F5, 0x24F6, 0x24F7, 0x24F8, 0x24F9, 0x24FA,
- 0x24FB, 0x24FC, 0x24FD, 0x24FE, 0x2616, 0x2617, 0x3020, 0x260E,
- 0x2600, 0x2601, 0x2602, 0x2603, 0x2668, 0x25B1, 0x31F0, 0x31F1,
- 0x31F2, 0x31F3, 0x31F4, 0x31F5, 0x31F6, 0x31F7, 0x31F8, 0x31F9,
- /*0x31F7*/ 0x309A, 0x31FA, 0x31FB, 0x31FC, 0x31FD, 0x31FE, 0x31FF,
-};
-static const unsigned short euc_to_utf8_A7[] = {
- 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0401,
- 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D,
- 0x041E, 0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425,
- 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D,
- 0x042E, 0x042F, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0451,
- 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D,
- 0x043E, 0x043F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445,
- 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D,
- 0x044E, 0x044F, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short euc_to_utf8_A7_x0213[] = {
- 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0401,
- 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D,
- 0x041E, 0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425,
- 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D,
- 0x042E, 0x042F, 0x23BE, 0x23BF, 0x23C0, 0x23C1, 0x23C2, 0x23C3,
- 0x23C4, 0x23C5, 0x23C6, 0x23C7, 0x23C8, 0x23C9, 0x23CA, 0x23CB,
- 0x23CC, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0451,
- 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D,
- 0x043E, 0x043F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445,
- 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D,
- 0x044E, 0x044F, 0x30F7, 0x30F8, 0x30F9, 0x30FA, 0x22DA, 0x22DB,
- 0x2153, 0x2154, 0x2155, 0x2713, 0x2318, 0x2423, 0x23CE,
-};
-static const unsigned short euc_to_utf8_A8[] = {
- 0x2500, 0x2502, 0x250C, 0x2510, 0x2518, 0x2514, 0x251C,
- 0x252C, 0x2524, 0x2534, 0x253C, 0x2501, 0x2503, 0x250F, 0x2513,
- 0x251B, 0x2517, 0x2523, 0x2533, 0x252B, 0x253B, 0x254B, 0x2520,
- 0x252F, 0x2528, 0x2537, 0x253F, 0x251D, 0x2530, 0x2525, 0x2538,
- 0x2542, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short euc_to_utf8_A8_x0213[] = {
- 0x2500, 0x2502, 0x250C, 0x2510, 0x2518, 0x2514, 0x251C,
- 0x252C, 0x2524, 0x2534, 0x253C, 0x2501, 0x2503, 0x250F, 0x2513,
- 0x251B, 0x2517, 0x2523, 0x2533, 0x252B, 0x253B, 0x254B, 0x2520,
- 0x252F, 0x2528, 0x2537, 0x253F, 0x251D, 0x2530, 0x2525, 0x2538,
- 0x2542, 0x3251, 0x3252, 0x3253, 0x3254, 0x3255, 0x3256, 0x3257,
- 0x3258, 0x3259, 0x325A, 0x325B, 0x325C, 0x325D, 0x325E, 0x325F,
- 0x32B1, 0x32B2, 0x32B3, 0x32B4, 0x32B5, 0x32B6, 0x32B7, 0x32B8,
- 0x32B9, 0x32BA, 0x32BB, 0x32BC, 0x32BD, 0x32BE, 0x32BF, 0,
- 0, 0, 0, 0, 0, 0, 0, 0x25D0,
- 0x25D1, 0x25D2, 0x25D3, 0x203C, 0x2047, 0x2048, 0x2049, 0x01CD,
- 0x01CE, 0x01D0, 0x1E3E, 0x1E3F, 0x01F8, 0x01F9, 0x01D1, 0x01D2,
- 0x01D4, 0x01D6, 0x01D8, 0x01DA, 0x01DC, 0, 0,
-};
-static const unsigned short euc_to_utf8_A9[] = {
- 0x2460, 0x2461, 0x2462, 0x2463, 0x2464, 0x2465, 0x2466,
- 0x2467, 0x2468, 0x2469, 0x246A, 0x246B, 0x246C, 0x246D, 0x246E,
- 0x246F, 0x2470, 0x2471, 0x2472, 0x2473, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0x2474,
- 0x2475, 0x2476, 0x2477, 0x2478, 0x2479, 0x247A, 0x247B, 0x247C,
- 0x247D, 0x247E, 0x247F, 0x2480, 0x2481, 0x2482, 0x2483, 0x2484,
- 0x2485, 0x2486, 0x2487, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x2776, 0x2777, 0x2778,
- 0x2779, 0x277A, 0x277B, 0x277C, 0x277D, 0x277E, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x2488, 0x2489, 0x248A, 0x248B, 0x248C, 0x248D,
- 0x248E, 0x248F, 0x2490, 0, 0, 0, 0,
-};
-static const unsigned short euc_to_utf8_A9_x0213[] = {
- 0x20AC, 0x00A0, 0x00A1, 0x00A4, 0x00A6, 0x00A9, 0x00AA,
- 0x00AB, 0x00AD, 0x00AE, 0x00AF, 0x00B2, 0x00B3, 0x00B7, 0x00B8,
- 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x00C0,
- 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8,
- 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0,
- 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D8, 0x00D9,
- 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0, 0x00E1,
- 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9,
- 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F0, 0x00F1,
- 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F8, 0x00F9, 0x00FA,
- 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF, 0x0100, 0x012A, 0x016A,
- 0x0112, 0x014C, 0x0101, 0x012B, 0x016B, 0x0113, 0x014D,
-};
-static const unsigned short euc_to_utf8_AA[] = {
- 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166,
- 0x2167, 0x2168, 0x2169, 0x216A, 0x216B, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x2170, 0x2171, 0x2172,
- 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217A,
- 0x217B, 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, 0x249C, 0x249D, 0x249E,
- 0x249F, 0x24A0, 0x24A1, 0x24A2, 0x24A3, 0x24A4, 0x24A5, 0x24A6,
- 0x24A7, 0x24A8, 0x24A9, 0x24AA, 0x24AB, 0x24AC, 0x24AD, 0x24AE,
- 0x24AF, 0x24B0, 0x24B1, 0x24B2, 0x24B3, 0x24B4, 0x24B5, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short euc_to_utf8_AA_x0213[] = {
- 0x0104, 0x02D8, 0x0141, 0x013D, 0x015A, 0x0160, 0x015E,
- 0x0164, 0x0179, 0x017D, 0x017B, 0x0105, 0x02DB, 0x0142, 0x013E,
- 0x015B, 0x02C7, 0x0161, 0x015F, 0x0165, 0x017A, 0x02DD, 0x017E,
- 0x017C, 0x0154, 0x0102, 0x0139, 0x0106, 0x010C, 0x0118, 0x011A,
- 0x010E, 0x0143, 0x0147, 0x0150, 0x0158, 0x016E, 0x0170, 0x0162,
- 0x0155, 0x0103, 0x013A, 0x0107, 0x010D, 0x0119, 0x011B, 0x010F,
- 0x0111, 0x0144, 0x0148, 0x0151, 0x0159, 0x016F, 0x0171, 0x0163,
- 0x02D9, 0x0108, 0x011C, 0x0124, 0x0134, 0x015C, 0x016C, 0x0109,
- 0x011D, 0x0125, 0x0135, 0x015D, 0x016D, 0x0271, 0x028B, 0x027E,
- 0x0283, 0x0292, 0x026C, 0x026E, 0x0279, 0x0288, 0x0256, 0x0273,
- 0x027D, 0x0282, 0x0290, 0x027B, 0x026D, 0x025F, 0x0272, 0x029D,
- 0x028E, 0x0261, 0x014B, 0x0270, 0x0281, 0x0127, 0x0295,
-};
-static const unsigned short euc_to_utf8_AB[] = {
- 0x339C, 0x339F, 0x339D, 0x33A0, 0x33A4, 0, 0x33A1,
- 0x33A5, 0x339E, 0x33A2, 0x338E, 0, 0x338F, 0x33C4, 0x3396,
- 0x3397, 0x2113, 0x3398, 0x33B3, 0x33B2, 0x33B1, 0x33B0, 0x2109,
- 0x33D4, 0x33CB, 0x3390, 0x3385, 0x3386, 0x3387, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x2116, 0x33CD, 0x2121, 0,
-};
-static const unsigned short euc_to_utf8_AB_x0213[] = {
- 0x0294, 0x0266, 0x0298, 0x01C2, 0x0253, 0x0257, 0x0284,
- 0x0260, 0x0193, 0x0153, 0x0152, 0x0268, 0x0289, 0x0258, 0x0275,
- 0x0259, 0x025C, 0x025E, 0x0250, 0x026F, 0x028A, 0x0264, 0x028C,
- 0x0254, 0x0251, 0x0252, 0x028D, 0x0265, 0x02A2, 0x02A1, 0x0255,
- 0x0291, 0x027A, 0x0267, 0x025A, /*0x00E6*/ 0x0300, 0x01FD, 0x1F70, 0x1F71,
- /*0x0254*/ 0x0300, /*0x0254*/ 0x0301, /*0x028C*/ 0x0300, /*0x028C*/ 0x0301, /*0x0259*/ 0x0300, /*0x0259*/ 0x0301, /*0x025A*/ 0x0300, /*0x025A*/ 0x0301,
- 0x1F72, 0x1F73, 0x0361, 0x02C8, 0x02CC, 0x02D0, 0x02D1, 0x0306,
- 0x203F, 0x030B, /*0*/ 0x0301, 0x0304, /*0*/ 0x0300, 0x030F, 0x030C, 0x0302,
- /*0*/ 0x02E5, 0x02E6, 0x02E7, 0x02E8, /*0*/ 0x02E9, /*0x02E9*/ 0x02E5, /*0x02E5*/ 0x02E9, 0x0325,
- 0x032C, 0x0339, 0x031C, 0x031F, 0x0320, 0x0308, 0x033D, 0x0329,
- 0x032F, 0x02DE, 0x0324, 0x0330, 0x033C, 0x0334, 0x031D, 0x031E,
- 0x0318, 0x0319, 0x032A, 0x033A, 0x033B, 0x0303, 0x031A,
-};
-static const unsigned short euc_to_utf8_AC[] = {
- 0x2664, 0x2667, 0x2661, 0x2662, 0x2660, 0x2663, 0x2665,
- 0x2666, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x3020, 0x260E, 0x3004,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0x261E, 0x261C, 0x261D, 0x261F, 0x21C6, 0x21C4, 0x21C5,
- 0, 0x21E8, 0x21E6, 0x21E7, 0x21E9, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short euc_to_utf8_AC_mac[] = {
- 0x2664, 0x2667, 0x2661, 0x2662, 0x2660, 0x2663, 0x2665,
- 0x2666, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x3020, 0x260E, 0x3004,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0x261E, 0x261C, 0x261D, 0x261F, 0x21C6, 0x21C4, 0x21C5,
- 0, 0x21E8, 0x21E6, 0x21E7, 0x21E9, 0x2192, 0x2190, 0x2191,
- 0x2193, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short euc_to_utf8_AC_x0213[] = {
- 0x2776, 0x2777, 0x2778, 0x2779, 0x277A, 0x277B, 0x277C,
- 0x277D, 0x277E, 0x277F, 0x24EB, 0x24EC, 0x24ED, 0x24EE, 0x24EF,
- 0x24F0, 0x24F1, 0x24F2, 0x24F3, 0x24F4, 0x2170, 0x2171, 0x2172,
- 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217A,
- 0x217B, 0x24D0, 0x24D1, 0x24D2, 0x24D3, 0x24D4, 0x24D5, 0x24D6,
- 0x24D7, 0x24D8, 0x24D9, 0x24DA, 0x24DB, 0x24DC, 0x24DD, 0x24DE,
- 0x24DF, 0x24E0, 0x24E1, 0x24E2, 0x24E3, 0x24E4, 0x24E5, 0x24E6,
- 0x24E7, 0x24E8, 0x24E9, 0x32D0, 0x32D1, 0x32D2, 0x32D3, 0x32D4,
- 0x32D5, 0x32D6, 0x32D7, 0x32D8, 0x32D9, 0x32DA, 0x32DB, 0x32DC,
- 0x32DD, 0x32DE, 0x32DF, 0x32E0, 0x32E1, 0x32E2, 0x32E3, 0x32FA,
- 0x32E9, 0x32E5, 0x32ED, 0x32EC, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x2051, 0x2042,
-};
-static const unsigned short euc_to_utf8_AD[] = {
- 0x2460, 0x2461, 0x2462, 0x2463, 0x2464, 0x2465, 0x2466,
- 0x2467, 0x2468, 0x2469, 0x246A, 0x246B, 0x246C, 0x246D, 0x246E,
- 0x246F, 0x2470, 0x2471, 0x2472, 0x2473, 0x2160, 0x2161, 0x2162,
- 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0,
- 0x3349, 0x3314, 0x3322, 0x334D, 0x3318, 0x3327, 0x3303, 0x3336,
- 0x3351, 0x3357, 0x330D, 0x3326, 0x3323, 0x332B, 0x334A, 0x333B,
- 0x339C, 0x339D, 0x339E, 0x338E, 0x338F, 0x33C4, 0x33A1, 0,
- 0, 0, 0, 0, 0, 0, 0, 0x337B,
- 0x301D, 0x301F, 0x2116, 0x33CD, 0x2121, 0x32A4, 0x32A5, 0x32A6,
- 0x32A7, 0x32A8, 0x3231, 0x3232, 0x3239, 0x337E, 0x337D, 0x337C,
- 0x2252, 0x2261, 0x222B, 0x222E, 0x2211, 0x221A, 0x22A5, 0x2220,
- 0x221F, 0x22BF, 0x2235, 0x2229, 0x222A, 0, 0x3299,
-};
-static const unsigned short euc_to_utf8_AD_mac[] = {
- 0x65E5, 0x6708, 0x706B, 0x6C34, 0x6728, 0x91D1, 0x571F,
- 0x796D, 0x795D, 0x81EA, 0x81F3, 0x3239, 0x547C, 0x3231, 0x8CC7,
- 0x540D, 0x3232, 0x5B66, 0x8CA1, 0x793E, 0x7279, 0x76E3, 0x4F01,
- 0x5354, 0x52B4, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0,
- 0x3349, 0x3314, 0x3322, 0x334D, 0x3318, 0x3327, 0x3303, 0x3336,
- 0x3351, 0x3357, 0x330D, 0x3326, 0x3323, 0x332B, 0x334A, 0x333B,
- 0x339C, 0x339D, 0x339E, 0x338E, 0x338F, 0x33C4, 0x33A1, 0,
- 0, 0, 0, 0, 0, 0, 0, 0x337B,
- 0x301D, 0x301F, 0x2116, 0x33CD, 0x2121, 0x32A4, 0x32A5, 0x32A6,
- 0x32A7, 0x32A8, 0x3231, 0x3232, 0x3239, 0x337E, 0x337D, 0x337C,
- 0x2252, 0x5927, 0x5C0F, 0x32A4, 0x32A5, 0x32A6, 0x32A7, 0x32A8,
- 0x533B, 0x8CA1, 0x512A, 0x52B4, 0x5370, 0x63A7, 0x79D8,
-};
-static const unsigned short euc_to_utf8_AD_x0213[] = {
- 0x2460, 0x2461, 0x2462, 0x2463, 0x2464, 0x2465, 0x2466,
- 0x2467, 0x2468, 0x2469, 0x246A, 0x246B, 0x246C, 0x246D, 0x246E,
- 0x246F, 0x2470, 0x2471, 0x2472, 0x2473, 0x2160, 0x2161, 0x2162,
- 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216A,
- 0x3349, 0x3314, 0x3322, 0x334D, 0x3318, 0x3327, 0x3303, 0x3336,
- 0x3351, 0x3357, 0x330D, 0x3326, 0x3323, 0x332B, 0x334A, 0x333B,
- 0x339C, 0x339D, 0x339E, 0x338E, 0x338F, 0x33C4, 0x33A1, 0x216B,
- 0, 0, 0, 0, 0, 0, 0, 0x337B,
- 0x301D, 0x301F, 0x2116, 0x33CD, 0x2121, 0x32A4, 0x32A5, 0x32A6,
- 0x32A7, 0x32A8, 0x3231, 0x3232, 0x3239, 0x337E, 0x337D, 0x337C,
- 0x2252, 0x2261, 0x222B, 0x222E, 0x2211, 0x221A, 0x22A5, 0x2220,
- 0x221F, 0x22BF, 0x2235, 0x2229, 0x222A, 0x2756, 0x261E,
-};
-static const unsigned short euc_to_utf8_AE[] = {
- 0x3349, 0x3322, 0x334D, 0x3314, 0x3316, 0x3305, 0x3333,
- 0x334E, 0x3303, 0x3336, 0x3318, 0x3315, 0x3327, 0x3351, 0x334A,
- 0x3339, 0x3357, 0x330D, 0x3342, 0x3323, 0x3326, 0x333B, 0x332B,
- 0, 0, 0, 0, 0, 0, 0, 0x3300,
- 0x331E, 0x332A, 0x3331, 0x3347, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0x337E,
- 0x337D, 0x337C, 0x337B, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0x337F, 0, 0,
-};
-static const unsigned short euc_to_utf8_AE_x0213[] = {
- 0x4FF1, 0xD840 /*0xDC0B*/, 0x3402, 0x4E28, 0x4E2F, 0x4E30, 0x4E8D,
- 0x4EE1, 0x4EFD, 0x4EFF, 0x4F03, 0x4F0B, 0x4F60, 0x4F48, 0x4F49,
- 0x4F56, 0x4F5F, 0x4F6A, 0x4F6C, 0x4F7E, 0x4F8A, 0x4F94, 0x4F97,
- 0xFA30, 0x4FC9, 0x4FE0, 0x5001, 0x5002, 0x500E, 0x5018, 0x5027,
- 0x502E, 0x5040, 0x503B, 0x5041, 0x5094, 0x50CC, 0x50F2, 0x50D0,
- 0x50E6, 0xFA31, 0x5106, 0x5103, 0x510B, 0x511E, 0x5135, 0x514A,
- 0xFA32, 0x5155, 0x5157, 0x34B5, 0x519D, 0x51C3, 0x51CA, 0x51DE,
- 0x51E2, 0x51EE, 0x5201, 0x34DB, 0x5213, 0x5215, 0x5249, 0x5257,
- 0x5261, 0x5293, 0x52C8, 0xFA33, 0x52CC, 0x52D0, 0x52D6, 0x52DB,
- 0xFA34, 0x52F0, 0x52FB, 0x5300, 0x5307, 0x531C, 0xFA35, 0x5361,
- 0x5363, 0x537D, 0x5393, 0x539D, 0x53B2, 0x5412, 0x5427, 0x544D,
- 0x549C, 0x546B, 0x5474, 0x547F, 0x5488, 0x5496, 0x54A1,
-};
-static const unsigned short euc_to_utf8_AF[] = {
- 0x222E, 0x221F, 0x22BF, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x301D, 0x301F, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0x3094, 0, 0x30F7, 0x30F8, 0x30F9, 0x30FA, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short euc_to_utf8_AF_x0213[] = {
- 0x54A9, 0x54C6, 0x54FF, 0x550E, 0x552B, 0x5535, 0x5550,
- 0x555E, 0x5581, 0x5586, 0x558E, 0xFA36, 0x55AD, 0x55CE, 0xFA37,
- 0x5608, 0x560E, 0x563B, 0x5649, 0x5676, 0x5666, 0xFA38, 0x566F,
- 0x5671, 0x5672, 0x5699, 0x569E, 0x56A9, 0x56AC, 0x56B3, 0x56C9,
- 0x56CA, 0x570A, 0xD844 /*0xDE3D*/, 0x5721, 0x572F, 0x5733, 0x5734, 0x5770,
- 0x5777, 0x577C, 0x579C, 0xFA0F, 0xD844 /*0xDF1B*/, 0x57B8, 0x57C7, 0x57C8,
- 0x57CF, 0x57E4, 0x57ED, 0x57F5, 0x57F6, 0x57FF, 0x5809, 0xFA10,
- 0x5861, 0x5864, 0xFA39, 0x587C, 0x5889, 0x589E, 0xFA3A, 0x58A9,
- 0xD845 /*0xDC6E*/, 0x58D2, 0x58CE, 0x58D4, 0x58DA, 0x58E0, 0x58E9, 0x590C,
- 0x8641, 0x595D, 0x596D, 0x598B, 0x5992, 0x59A4, 0x59C3, 0x59D2,
- 0x59DD, 0x5A13, 0x5A23, 0x5A67, 0x5A6D, 0x5A77, 0x5A7E, 0x5A84,
- 0x5A9E, 0x5AA7, 0x5AC4, 0xD846 /*0xDCBD*/, 0x5B19, 0x5B25, 0x525D,
-};
-static const unsigned short euc_to_utf8_B0[] = {
- 0x4E9C, 0x5516, 0x5A03, 0x963F, 0x54C0, 0x611B, 0x6328,
- 0x59F6, 0x9022, 0x8475, 0x831C, 0x7A50, 0x60AA, 0x63E1, 0x6E25,
- 0x65ED, 0x8466, 0x82A6, 0x9BF5, 0x6893, 0x5727, 0x65A1, 0x6271,
- 0x5B9B, 0x59D0, 0x867B, 0x98F4, 0x7D62, 0x7DBE, 0x9B8E, 0x6216,
- 0x7C9F, 0x88B7, 0x5B89, 0x5EB5, 0x6309, 0x6697, 0x6848, 0x95C7,
- 0x978D, 0x674F, 0x4EE5, 0x4F0A, 0x4F4D, 0x4F9D, 0x5049, 0x56F2,
- 0x5937, 0x59D4, 0x5A01, 0x5C09, 0x60DF, 0x610F, 0x6170, 0x6613,
- 0x6905, 0x70BA, 0x754F, 0x7570, 0x79FB, 0x7DAD, 0x7DEF, 0x80C3,
- 0x840E, 0x8863, 0x8B02, 0x9055, 0x907A, 0x533B, 0x4E95, 0x4EA5,
- 0x57DF, 0x80B2, 0x90C1, 0x78EF, 0x4E00, 0x58F1, 0x6EA2, 0x9038,
- 0x7A32, 0x8328, 0x828B, 0x9C2F, 0x5141, 0x5370, 0x54BD, 0x54E1,
- 0x56E0, 0x59FB, 0x5F15, 0x98F2, 0x6DEB, 0x80E4, 0x852D,
-};
-static const unsigned short euc_to_utf8_B1[] = {
- 0x9662, 0x9670, 0x96A0, 0x97FB, 0x540B, 0x53F3, 0x5B87,
- 0x70CF, 0x7FBD, 0x8FC2, 0x96E8, 0x536F, 0x9D5C, 0x7ABA, 0x4E11,
- 0x7893, 0x81FC, 0x6E26, 0x5618, 0x5504, 0x6B1D, 0x851A, 0x9C3B,
- 0x59E5, 0x53A9, 0x6D66, 0x74DC, 0x958F, 0x5642, 0x4E91, 0x904B,
- 0x96F2, 0x834F, 0x990C, 0x53E1, 0x55B6, 0x5B30, 0x5F71, 0x6620,
- 0x66F3, 0x6804, 0x6C38, 0x6CF3, 0x6D29, 0x745B, 0x76C8, 0x7A4E,
- 0x9834, 0x82F1, 0x885B, 0x8A60, 0x92ED, 0x6DB2, 0x75AB, 0x76CA,
- 0x99C5, 0x60A6, 0x8B01, 0x8D8A, 0x95B2, 0x698E, 0x53AD, 0x5186,
- 0x5712, 0x5830, 0x5944, 0x5BB4, 0x5EF6, 0x6028, 0x63A9, 0x63F4,
- 0x6CBF, 0x6F14, 0x708E, 0x7114, 0x7159, 0x71D5, 0x733F, 0x7E01,
- 0x8276, 0x82D1, 0x8597, 0x9060, 0x925B, 0x9D1B, 0x5869, 0x65BC,
- 0x6C5A, 0x7525, 0x51F9, 0x592E, 0x5965, 0x5F80, 0x5FDC,
-};
-static const unsigned short euc_to_utf8_B2[] = {
- 0x62BC, 0x65FA, 0x6A2A, 0x6B27, 0x6BB4, 0x738B, 0x7FC1,
- 0x8956, 0x9D2C, 0x9D0E, 0x9EC4, 0x5CA1, 0x6C96, 0x837B, 0x5104,
- 0x5C4B, 0x61B6, 0x81C6, 0x6876, 0x7261, 0x4E59, 0x4FFA, 0x5378,
- 0x6069, 0x6E29, 0x7A4F, 0x97F3, 0x4E0B, 0x5316, 0x4EEE, 0x4F55,
- 0x4F3D, 0x4FA1, 0x4F73, 0x52A0, 0x53EF, 0x5609, 0x590F, 0x5AC1,
- 0x5BB6, 0x5BE1, 0x79D1, 0x6687, 0x679C, 0x67B6, 0x6B4C, 0x6CB3,
- 0x706B, 0x73C2, 0x798D, 0x79BE, 0x7A3C, 0x7B87, 0x82B1, 0x82DB,
- 0x8304, 0x8377, 0x83EF, 0x83D3, 0x8766, 0x8AB2, 0x5629, 0x8CA8,
- 0x8FE6, 0x904E, 0x971E, 0x868A, 0x4FC4, 0x5CE8, 0x6211, 0x7259,
- 0x753B, 0x81E5, 0x82BD, 0x86FE, 0x8CC0, 0x96C5, 0x9913, 0x99D5,
- 0x4ECB, 0x4F1A, 0x89E3, 0x56DE, 0x584A, 0x58CA, 0x5EFB, 0x5FEB,
- 0x602A, 0x6094, 0x6062, 0x61D0, 0x6212, 0x62D0, 0x6539,
-};
-static const unsigned short euc_to_utf8_B3[] = {
- 0x9B41, 0x6666, 0x68B0, 0x6D77, 0x7070, 0x754C, 0x7686,
- 0x7D75, 0x82A5, 0x87F9, 0x958B, 0x968E, 0x8C9D, 0x51F1, 0x52BE,
- 0x5916, 0x54B3, 0x5BB3, 0x5D16, 0x6168, 0x6982, 0x6DAF, 0x788D,
- 0x84CB, 0x8857, 0x8A72, 0x93A7, 0x9AB8, 0x6D6C, 0x99A8, 0x86D9,
- 0x57A3, 0x67FF, 0x86CE, 0x920E, 0x5283, 0x5687, 0x5404, 0x5ED3,
- 0x62E1, 0x64B9, 0x683C, 0x6838, 0x6BBB, 0x7372, 0x78BA, 0x7A6B,
- 0x899A, 0x89D2, 0x8D6B, 0x8F03, 0x90ED, 0x95A3, 0x9694, 0x9769,
- 0x5B66, 0x5CB3, 0x697D, 0x984D, 0x984E, 0x639B, 0x7B20, 0x6A2B,
- 0x6A7F, 0x68B6, 0x9C0D, 0x6F5F, 0x5272, 0x559D, 0x6070, 0x62EC,
- 0x6D3B, 0x6E07, 0x6ED1, 0x845B, 0x8910, 0x8F44, 0x4E14, 0x9C39,
- 0x53F6, 0x691B, 0x6A3A, 0x9784, 0x682A, 0x515C, 0x7AC3, 0x84B2,
- 0x91DC, 0x938C, 0x565B, 0x9D28, 0x6822, 0x8305, 0x8431,
-};
-static const unsigned short euc_to_utf8_B4[] = {
- 0x7CA5, 0x5208, 0x82C5, 0x74E6, 0x4E7E, 0x4F83, 0x51A0,
- 0x5BD2, 0x520A, 0x52D8, 0x52E7, 0x5DFB, 0x559A, 0x582A, 0x59E6,
- 0x5B8C, 0x5B98, 0x5BDB, 0x5E72, 0x5E79, 0x60A3, 0x611F, 0x6163,
- 0x61BE, 0x63DB, 0x6562, 0x67D1, 0x6853, 0x68FA, 0x6B3E, 0x6B53,
- 0x6C57, 0x6F22, 0x6F97, 0x6F45, 0x74B0, 0x7518, 0x76E3, 0x770B,
- 0x7AFF, 0x7BA1, 0x7C21, 0x7DE9, 0x7F36, 0x7FF0, 0x809D, 0x8266,
- 0x839E, 0x89B3, 0x8ACC, 0x8CAB, 0x9084, 0x9451, 0x9593, 0x9591,
- 0x95A2, 0x9665, 0x97D3, 0x9928, 0x8218, 0x4E38, 0x542B, 0x5CB8,
- 0x5DCC, 0x73A9, 0x764C, 0x773C, 0x5CA9, 0x7FEB, 0x8D0B, 0x96C1,
- 0x9811, 0x9854, 0x9858, 0x4F01, 0x4F0E, 0x5371, 0x559C, 0x5668,
- 0x57FA, 0x5947, 0x5B09, 0x5BC4, 0x5C90, 0x5E0C, 0x5E7E, 0x5FCC,
- 0x63EE, 0x673A, 0x65D7, 0x65E2, 0x671F, 0x68CB, 0x68C4,
-};
-static const unsigned short euc_to_utf8_B5[] = {
- 0x6A5F, 0x5E30, 0x6BC5, 0x6C17, 0x6C7D, 0x757F, 0x7948,
- 0x5B63, 0x7A00, 0x7D00, 0x5FBD, 0x898F, 0x8A18, 0x8CB4, 0x8D77,
- 0x8ECC, 0x8F1D, 0x98E2, 0x9A0E, 0x9B3C, 0x4E80, 0x507D, 0x5100,
- 0x5993, 0x5B9C, 0x622F, 0x6280, 0x64EC, 0x6B3A, 0x72A0, 0x7591,
- 0x7947, 0x7FA9, 0x87FB, 0x8ABC, 0x8B70, 0x63AC, 0x83CA, 0x97A0,
- 0x5409, 0x5403, 0x55AB, 0x6854, 0x6A58, 0x8A70, 0x7827, 0x6775,
- 0x9ECD, 0x5374, 0x5BA2, 0x811A, 0x8650, 0x9006, 0x4E18, 0x4E45,
- 0x4EC7, 0x4F11, 0x53CA, 0x5438, 0x5BAE, 0x5F13, 0x6025, 0x6551,
- 0x673D, 0x6C42, 0x6C72, 0x6CE3, 0x7078, 0x7403, 0x7A76, 0x7AAE,
- 0x7B08, 0x7D1A, 0x7CFE, 0x7D66, 0x65E7, 0x725B, 0x53BB, 0x5C45,
- 0x5DE8, 0x62D2, 0x62E0, 0x6319, 0x6E20, 0x865A, 0x8A31, 0x8DDD,
- 0x92F8, 0x6F01, 0x79A6, 0x9B5A, 0x4EA8, 0x4EAB, 0x4EAC,
-};
-static const unsigned short euc_to_utf8_B6[] = {
- 0x4F9B, 0x4FA0, 0x50D1, 0x5147, 0x7AF6, 0x5171, 0x51F6,
- 0x5354, 0x5321, 0x537F, 0x53EB, 0x55AC, 0x5883, 0x5CE1, 0x5F37,
- 0x5F4A, 0x602F, 0x6050, 0x606D, 0x631F, 0x6559, 0x6A4B, 0x6CC1,
- 0x72C2, 0x72ED, 0x77EF, 0x80F8, 0x8105, 0x8208, 0x854E, 0x90F7,
- 0x93E1, 0x97FF, 0x9957, 0x9A5A, 0x4EF0, 0x51DD, 0x5C2D, 0x6681,
- 0x696D, 0x5C40, 0x66F2, 0x6975, 0x7389, 0x6850, 0x7C81, 0x50C5,
- 0x52E4, 0x5747, 0x5DFE, 0x9326, 0x65A4, 0x6B23, 0x6B3D, 0x7434,
- 0x7981, 0x79BD, 0x7B4B, 0x7DCA, 0x82B9, 0x83CC, 0x887F, 0x895F,
- 0x8B39, 0x8FD1, 0x91D1, 0x541F, 0x9280, 0x4E5D, 0x5036, 0x53E5,
- 0x533A, 0x72D7, 0x7396, 0x77E9, 0x82E6, 0x8EAF, 0x99C6, 0x99C8,
- 0x99D2, 0x5177, 0x611A, 0x865E, 0x55B0, 0x7A7A, 0x5076, 0x5BD3,
- 0x9047, 0x9685, 0x4E32, 0x6ADB, 0x91E7, 0x5C51, 0x5C48,
-};
-static const unsigned short euc_to_utf8_B7[] = {
- 0x6398, 0x7A9F, 0x6C93, 0x9774, 0x8F61, 0x7AAA, 0x718A,
- 0x9688, 0x7C82, 0x6817, 0x7E70, 0x6851, 0x936C, 0x52F2, 0x541B,
- 0x85AB, 0x8A13, 0x7FA4, 0x8ECD, 0x90E1, 0x5366, 0x8888, 0x7941,
- 0x4FC2, 0x50BE, 0x5211, 0x5144, 0x5553, 0x572D, 0x73EA, 0x578B,
- 0x5951, 0x5F62, 0x5F84, 0x6075, 0x6176, 0x6167, 0x61A9, 0x63B2,
- 0x643A, 0x656C, 0x666F, 0x6842, 0x6E13, 0x7566, 0x7A3D, 0x7CFB,
- 0x7D4C, 0x7D99, 0x7E4B, 0x7F6B, 0x830E, 0x834A, 0x86CD, 0x8A08,
- 0x8A63, 0x8B66, 0x8EFD, 0x981A, 0x9D8F, 0x82B8, 0x8FCE, 0x9BE8,
- 0x5287, 0x621F, 0x6483, 0x6FC0, 0x9699, 0x6841, 0x5091, 0x6B20,
- 0x6C7A, 0x6F54, 0x7A74, 0x7D50, 0x8840, 0x8A23, 0x6708, 0x4EF6,
- 0x5039, 0x5026, 0x5065, 0x517C, 0x5238, 0x5263, 0x55A7, 0x570F,
- 0x5805, 0x5ACC, 0x5EFA, 0x61B2, 0x61F8, 0x62F3, 0x6372,
-};
-static const unsigned short euc_to_utf8_B8[] = {
- 0x691C, 0x6A29, 0x727D, 0x72AC, 0x732E, 0x7814, 0x786F,
- 0x7D79, 0x770C, 0x80A9, 0x898B, 0x8B19, 0x8CE2, 0x8ED2, 0x9063,
- 0x9375, 0x967A, 0x9855, 0x9A13, 0x9E78, 0x5143, 0x539F, 0x53B3,
- 0x5E7B, 0x5F26, 0x6E1B, 0x6E90, 0x7384, 0x73FE, 0x7D43, 0x8237,
- 0x8A00, 0x8AFA, 0x9650, 0x4E4E, 0x500B, 0x53E4, 0x547C, 0x56FA,
- 0x59D1, 0x5B64, 0x5DF1, 0x5EAB, 0x5F27, 0x6238, 0x6545, 0x67AF,
- 0x6E56, 0x72D0, 0x7CCA, 0x88B4, 0x80A1, 0x80E1, 0x83F0, 0x864E,
- 0x8A87, 0x8DE8, 0x9237, 0x96C7, 0x9867, 0x9F13, 0x4E94, 0x4E92,
- 0x4F0D, 0x5348, 0x5449, 0x543E, 0x5A2F, 0x5F8C, 0x5FA1, 0x609F,
- 0x68A7, 0x6A8E, 0x745A, 0x7881, 0x8A9E, 0x8AA4, 0x8B77, 0x9190,
- 0x4E5E, 0x9BC9, 0x4EA4, 0x4F7C, 0x4FAF, 0x5019, 0x5016, 0x5149,
- 0x516C, 0x529F, 0x52B9, 0x52FE, 0x539A, 0x53E3, 0x5411,
-};
-static const unsigned short euc_to_utf8_B9[] = {
- 0x540E, 0x5589, 0x5751, 0x57A2, 0x597D, 0x5B54, 0x5B5D,
- 0x5B8F, 0x5DE5, 0x5DE7, 0x5DF7, 0x5E78, 0x5E83, 0x5E9A, 0x5EB7,
- 0x5F18, 0x6052, 0x614C, 0x6297, 0x62D8, 0x63A7, 0x653B, 0x6602,
- 0x6643, 0x66F4, 0x676D, 0x6821, 0x6897, 0x69CB, 0x6C5F, 0x6D2A,
- 0x6D69, 0x6E2F, 0x6E9D, 0x7532, 0x7687, 0x786C, 0x7A3F, 0x7CE0,
- 0x7D05, 0x7D18, 0x7D5E, 0x7DB1, 0x8015, 0x8003, 0x80AF, 0x80B1,
- 0x8154, 0x818F, 0x822A, 0x8352, 0x884C, 0x8861, 0x8B1B, 0x8CA2,
- 0x8CFC, 0x90CA, 0x9175, 0x9271, 0x783F, 0x92FC, 0x95A4, 0x964D,
- 0x9805, 0x9999, 0x9AD8, 0x9D3B, 0x525B, 0x52AB, 0x53F7, 0x5408,
- 0x58D5, 0x62F7, 0x6FE0, 0x8C6A, 0x8F5F, 0x9EB9, 0x514B, 0x523B,
- 0x544A, 0x56FD, 0x7A40, 0x9177, 0x9D60, 0x9ED2, 0x7344, 0x6F09,
- 0x8170, 0x7511, 0x5FFD, 0x60DA, 0x9AA8, 0x72DB, 0x8FBC,
-};
-static const unsigned short euc_to_utf8_BA[] = {
- 0x6B64, 0x9803, 0x4ECA, 0x56F0, 0x5764, 0x58BE, 0x5A5A,
- 0x6068, 0x61C7, 0x660F, 0x6606, 0x6839, 0x68B1, 0x6DF7, 0x75D5,
- 0x7D3A, 0x826E, 0x9B42, 0x4E9B, 0x4F50, 0x53C9, 0x5506, 0x5D6F,
- 0x5DE6, 0x5DEE, 0x67FB, 0x6C99, 0x7473, 0x7802, 0x8A50, 0x9396,
- 0x88DF, 0x5750, 0x5EA7, 0x632B, 0x50B5, 0x50AC, 0x518D, 0x6700,
- 0x54C9, 0x585E, 0x59BB, 0x5BB0, 0x5F69, 0x624D, 0x63A1, 0x683D,
- 0x6B73, 0x6E08, 0x707D, 0x91C7, 0x7280, 0x7815, 0x7826, 0x796D,
- 0x658E, 0x7D30, 0x83DC, 0x88C1, 0x8F09, 0x969B, 0x5264, 0x5728,
- 0x6750, 0x7F6A, 0x8CA1, 0x51B4, 0x5742, 0x962A, 0x583A, 0x698A,
- 0x80B4, 0x54B2, 0x5D0E, 0x57FC, 0x7895, 0x9DFA, 0x4F5C, 0x524A,
- 0x548B, 0x643E, 0x6628, 0x6714, 0x67F5, 0x7A84, 0x7B56, 0x7D22,
- 0x932F, 0x685C, 0x9BAD, 0x7B39, 0x5319, 0x518A, 0x5237,
-};
-static const unsigned short euc_to_utf8_BB[] = {
- 0x5BDF, 0x62F6, 0x64AE, 0x64E6, 0x672D, 0x6BBA, 0x85A9,
- 0x96D1, 0x7690, 0x9BD6, 0x634C, 0x9306, 0x9BAB, 0x76BF, 0x6652,
- 0x4E09, 0x5098, 0x53C2, 0x5C71, 0x60E8, 0x6492, 0x6563, 0x685F,
- 0x71E6, 0x73CA, 0x7523, 0x7B97, 0x7E82, 0x8695, 0x8B83, 0x8CDB,
- 0x9178, 0x9910, 0x65AC, 0x66AB, 0x6B8B, 0x4ED5, 0x4ED4, 0x4F3A,
- 0x4F7F, 0x523A, 0x53F8, 0x53F2, 0x55E3, 0x56DB, 0x58EB, 0x59CB,
- 0x59C9, 0x59FF, 0x5B50, 0x5C4D, 0x5E02, 0x5E2B, 0x5FD7, 0x601D,
- 0x6307, 0x652F, 0x5B5C, 0x65AF, 0x65BD, 0x65E8, 0x679D, 0x6B62,
- 0x6B7B, 0x6C0F, 0x7345, 0x7949, 0x79C1, 0x7CF8, 0x7D19, 0x7D2B,
- 0x80A2, 0x8102, 0x81F3, 0x8996, 0x8A5E, 0x8A69, 0x8A66, 0x8A8C,
- 0x8AEE, 0x8CC7, 0x8CDC, 0x96CC, 0x98FC, 0x6B6F, 0x4E8B, 0x4F3C,
- 0x4F8D, 0x5150, 0x5B57, 0x5BFA, 0x6148, 0x6301, 0x6642,
-};
-static const unsigned short euc_to_utf8_BC[] = {
- 0x6B21, 0x6ECB, 0x6CBB, 0x723E, 0x74BD, 0x75D4, 0x78C1,
- 0x793A, 0x800C, 0x8033, 0x81EA, 0x8494, 0x8F9E, 0x6C50, 0x9E7F,
- 0x5F0F, 0x8B58, 0x9D2B, 0x7AFA, 0x8EF8, 0x5B8D, 0x96EB, 0x4E03,
- 0x53F1, 0x57F7, 0x5931, 0x5AC9, 0x5BA4, 0x6089, 0x6E7F, 0x6F06,
- 0x75BE, 0x8CEA, 0x5B9F, 0x8500, 0x7BE0, 0x5072, 0x67F4, 0x829D,
- 0x5C61, 0x854A, 0x7E1E, 0x820E, 0x5199, 0x5C04, 0x6368, 0x8D66,
- 0x659C, 0x716E, 0x793E, 0x7D17, 0x8005, 0x8B1D, 0x8ECA, 0x906E,
- 0x86C7, 0x90AA, 0x501F, 0x52FA, 0x5C3A, 0x6753, 0x707C, 0x7235,
- 0x914C, 0x91C8, 0x932B, 0x82E5, 0x5BC2, 0x5F31, 0x60F9, 0x4E3B,
- 0x53D6, 0x5B88, 0x624B, 0x6731, 0x6B8A, 0x72E9, 0x73E0, 0x7A2E,
- 0x816B, 0x8DA3, 0x9152, 0x9996, 0x5112, 0x53D7, 0x546A, 0x5BFF,
- 0x6388, 0x6A39, 0x7DAC, 0x9700, 0x56DA, 0x53CE, 0x5468,
-};
-static const unsigned short euc_to_utf8_BD[] = {
- 0x5B97, 0x5C31, 0x5DDE, 0x4FEE, 0x6101, 0x62FE, 0x6D32,
- 0x79C0, 0x79CB, 0x7D42, 0x7E4D, 0x7FD2, 0x81ED, 0x821F, 0x8490,
- 0x8846, 0x8972, 0x8B90, 0x8E74, 0x8F2F, 0x9031, 0x914B, 0x916C,
- 0x96C6, 0x919C, 0x4EC0, 0x4F4F, 0x5145, 0x5341, 0x5F93, 0x620E,
- 0x67D4, 0x6C41, 0x6E0B, 0x7363, 0x7E26, 0x91CD, 0x9283, 0x53D4,
- 0x5919, 0x5BBF, 0x6DD1, 0x795D, 0x7E2E, 0x7C9B, 0x587E, 0x719F,
- 0x51FA, 0x8853, 0x8FF0, 0x4FCA, 0x5CFB, 0x6625, 0x77AC, 0x7AE3,
- 0x821C, 0x99FF, 0x51C6, 0x5FAA, 0x65EC, 0x696F, 0x6B89, 0x6DF3,
- 0x6E96, 0x6F64, 0x76FE, 0x7D14, 0x5DE1, 0x9075, 0x9187, 0x9806,
- 0x51E6, 0x521D, 0x6240, 0x6691, 0x66D9, 0x6E1A, 0x5EB6, 0x7DD2,
- 0x7F72, 0x66F8, 0x85AF, 0x85F7, 0x8AF8, 0x52A9, 0x53D9, 0x5973,
- 0x5E8F, 0x5F90, 0x6055, 0x92E4, 0x9664, 0x50B7, 0x511F,
-};
-static const unsigned short euc_to_utf8_BE[] = {
- 0x52DD, 0x5320, 0x5347, 0x53EC, 0x54E8, 0x5546, 0x5531,
- 0x5617, 0x5968, 0x59BE, 0x5A3C, 0x5BB5, 0x5C06, 0x5C0F, 0x5C11,
- 0x5C1A, 0x5E84, 0x5E8A, 0x5EE0, 0x5F70, 0x627F, 0x6284, 0x62DB,
- 0x638C, 0x6377, 0x6607, 0x660C, 0x662D, 0x6676, 0x677E, 0x68A2,
- 0x6A1F, 0x6A35, 0x6CBC, 0x6D88, 0x6E09, 0x6E58, 0x713C, 0x7126,
- 0x7167, 0x75C7, 0x7701, 0x785D, 0x7901, 0x7965, 0x79F0, 0x7AE0,
- 0x7B11, 0x7CA7, 0x7D39, 0x8096, 0x83D6, 0x848B, 0x8549, 0x885D,
- 0x88F3, 0x8A1F, 0x8A3C, 0x8A54, 0x8A73, 0x8C61, 0x8CDE, 0x91A4,
- 0x9266, 0x937E, 0x9418, 0x969C, 0x9798, 0x4E0A, 0x4E08, 0x4E1E,
- 0x4E57, 0x5197, 0x5270, 0x57CE, 0x5834, 0x58CC, 0x5B22, 0x5E38,
- 0x60C5, 0x64FE, 0x6761, 0x6756, 0x6D44, 0x72B6, 0x7573, 0x7A63,
- 0x84B8, 0x8B72, 0x91B8, 0x9320, 0x5631, 0x57F4, 0x98FE,
-};
-static const unsigned short euc_to_utf8_BF[] = {
- 0x62ED, 0x690D, 0x6B96, 0x71ED, 0x7E54, 0x8077, 0x8272,
- 0x89E6, 0x98DF, 0x8755, 0x8FB1, 0x5C3B, 0x4F38, 0x4FE1, 0x4FB5,
- 0x5507, 0x5A20, 0x5BDD, 0x5BE9, 0x5FC3, 0x614E, 0x632F, 0x65B0,
- 0x664B, 0x68EE, 0x699B, 0x6D78, 0x6DF1, 0x7533, 0x75B9, 0x771F,
- 0x795E, 0x79E6, 0x7D33, 0x81E3, 0x82AF, 0x85AA, 0x89AA, 0x8A3A,
- 0x8EAB, 0x8F9B, 0x9032, 0x91DD, 0x9707, 0x4EBA, 0x4EC1, 0x5203,
- 0x5875, 0x58EC, 0x5C0B, 0x751A, 0x5C3D, 0x814E, 0x8A0A, 0x8FC5,
- 0x9663, 0x976D, 0x7B25, 0x8ACF, 0x9808, 0x9162, 0x56F3, 0x53A8,
- 0x9017, 0x5439, 0x5782, 0x5E25, 0x63A8, 0x6C34, 0x708A, 0x7761,
- 0x7C8B, 0x7FE0, 0x8870, 0x9042, 0x9154, 0x9310, 0x9318, 0x968F,
- 0x745E, 0x9AC4, 0x5D07, 0x5D69, 0x6570, 0x67A2, 0x8DA8, 0x96DB,
- 0x636E, 0x6749, 0x6919, 0x83C5, 0x9817, 0x96C0, 0x88FE,
-};
-static const unsigned short euc_to_utf8_C0[] = {
- 0x6F84, 0x647A, 0x5BF8, 0x4E16, 0x702C, 0x755D, 0x662F,
- 0x51C4, 0x5236, 0x52E2, 0x59D3, 0x5F81, 0x6027, 0x6210, 0x653F,
- 0x6574, 0x661F, 0x6674, 0x68F2, 0x6816, 0x6B63, 0x6E05, 0x7272,
- 0x751F, 0x76DB, 0x7CBE, 0x8056, 0x58F0, 0x88FD, 0x897F, 0x8AA0,
- 0x8A93, 0x8ACB, 0x901D, 0x9192, 0x9752, 0x9759, 0x6589, 0x7A0E,
- 0x8106, 0x96BB, 0x5E2D, 0x60DC, 0x621A, 0x65A5, 0x6614, 0x6790,
- 0x77F3, 0x7A4D, 0x7C4D, 0x7E3E, 0x810A, 0x8CAC, 0x8D64, 0x8DE1,
- 0x8E5F, 0x78A9, 0x5207, 0x62D9, 0x63A5, 0x6442, 0x6298, 0x8A2D,
- 0x7A83, 0x7BC0, 0x8AAC, 0x96EA, 0x7D76, 0x820C, 0x8749, 0x4ED9,
- 0x5148, 0x5343, 0x5360, 0x5BA3, 0x5C02, 0x5C16, 0x5DDD, 0x6226,
- 0x6247, 0x64B0, 0x6813, 0x6834, 0x6CC9, 0x6D45, 0x6D17, 0x67D3,
- 0x6F5C, 0x714E, 0x717D, 0x65CB, 0x7A7F, 0x7BAD, 0x7DDA,
-};
-static const unsigned short euc_to_utf8_C1[] = {
- 0x7E4A, 0x7FA8, 0x817A, 0x821B, 0x8239, 0x85A6, 0x8A6E,
- 0x8CCE, 0x8DF5, 0x9078, 0x9077, 0x92AD, 0x9291, 0x9583, 0x9BAE,
- 0x524D, 0x5584, 0x6F38, 0x7136, 0x5168, 0x7985, 0x7E55, 0x81B3,
- 0x7CCE, 0x564C, 0x5851, 0x5CA8, 0x63AA, 0x66FE, 0x66FD, 0x695A,
- 0x72D9, 0x758F, 0x758E, 0x790E, 0x7956, 0x79DF, 0x7C97, 0x7D20,
- 0x7D44, 0x8607, 0x8A34, 0x963B, 0x9061, 0x9F20, 0x50E7, 0x5275,
- 0x53CC, 0x53E2, 0x5009, 0x55AA, 0x58EE, 0x594F, 0x723D, 0x5B8B,
- 0x5C64, 0x531D, 0x60E3, 0x60F3, 0x635C, 0x6383, 0x633F, 0x63BB,
- 0x64CD, 0x65E9, 0x66F9, 0x5DE3, 0x69CD, 0x69FD, 0x6F15, 0x71E5,
- 0x4E89, 0x75E9, 0x76F8, 0x7A93, 0x7CDF, 0x7DCF, 0x7D9C, 0x8061,
- 0x8349, 0x8358, 0x846C, 0x84BC, 0x85FB, 0x88C5, 0x8D70, 0x9001,
- 0x906D, 0x9397, 0x971C, 0x9A12, 0x50CF, 0x5897, 0x618E,
-};
-static const unsigned short euc_to_utf8_C2[] = {
- 0x81D3, 0x8535, 0x8D08, 0x9020, 0x4FC3, 0x5074, 0x5247,
- 0x5373, 0x606F, 0x6349, 0x675F, 0x6E2C, 0x8DB3, 0x901F, 0x4FD7,
- 0x5C5E, 0x8CCA, 0x65CF, 0x7D9A, 0x5352, 0x8896, 0x5176, 0x63C3,
- 0x5B58, 0x5B6B, 0x5C0A, 0x640D, 0x6751, 0x905C, 0x4ED6, 0x591A,
- 0x592A, 0x6C70, 0x8A51, 0x553E, 0x5815, 0x59A5, 0x60F0, 0x6253,
- 0x67C1, 0x8235, 0x6955, 0x9640, 0x99C4, 0x9A28, 0x4F53, 0x5806,
- 0x5BFE, 0x8010, 0x5CB1, 0x5E2F, 0x5F85, 0x6020, 0x614B, 0x6234,
- 0x66FF, 0x6CF0, 0x6EDE, 0x80CE, 0x817F, 0x82D4, 0x888B, 0x8CB8,
- 0x9000, 0x902E, 0x968A, 0x9EDB, 0x9BDB, 0x4EE3, 0x53F0, 0x5927,
- 0x7B2C, 0x918D, 0x984C, 0x9DF9, 0x6EDD, 0x7027, 0x5353, 0x5544,
- 0x5B85, 0x6258, 0x629E, 0x62D3, 0x6CA2, 0x6FEF, 0x7422, 0x8A17,
- 0x9438, 0x6FC1, 0x8AFE, 0x8338, 0x51E7, 0x86F8, 0x53EA,
-};
-static const unsigned short euc_to_utf8_C3[] = {
- 0x53E9, 0x4F46, 0x9054, 0x8FB0, 0x596A, 0x8131, 0x5DFD,
- 0x7AEA, 0x8FBF, 0x68DA, 0x8C37, 0x72F8, 0x9C48, 0x6A3D, 0x8AB0,
- 0x4E39, 0x5358, 0x5606, 0x5766, 0x62C5, 0x63A2, 0x65E6, 0x6B4E,
- 0x6DE1, 0x6E5B, 0x70AD, 0x77ED, 0x7AEF, 0x7BAA, 0x7DBB, 0x803D,
- 0x80C6, 0x86CB, 0x8A95, 0x935B, 0x56E3, 0x58C7, 0x5F3E, 0x65AD,
- 0x6696, 0x6A80, 0x6BB5, 0x7537, 0x8AC7, 0x5024, 0x77E5, 0x5730,
- 0x5F1B, 0x6065, 0x667A, 0x6C60, 0x75F4, 0x7A1A, 0x7F6E, 0x81F4,
- 0x8718, 0x9045, 0x99B3, 0x7BC9, 0x755C, 0x7AF9, 0x7B51, 0x84C4,
- 0x9010, 0x79E9, 0x7A92, 0x8336, 0x5AE1, 0x7740, 0x4E2D, 0x4EF2,
- 0x5B99, 0x5FE0, 0x62BD, 0x663C, 0x67F1, 0x6CE8, 0x866B, 0x8877,
- 0x8A3B, 0x914E, 0x92F3, 0x99D0, 0x6A17, 0x7026, 0x732A, 0x82E7,
- 0x8457, 0x8CAF, 0x4E01, 0x5146, 0x51CB, 0x558B, 0x5BF5,
-};
-static const unsigned short euc_to_utf8_C4[] = {
- 0x5E16, 0x5E33, 0x5E81, 0x5F14, 0x5F35, 0x5F6B, 0x5FB4,
- 0x61F2, 0x6311, 0x66A2, 0x671D, 0x6F6E, 0x7252, 0x753A, 0x773A,
- 0x8074, 0x8139, 0x8178, 0x8776, 0x8ABF, 0x8ADC, 0x8D85, 0x8DF3,
- 0x929A, 0x9577, 0x9802, 0x9CE5, 0x52C5, 0x6357, 0x76F4, 0x6715,
- 0x6C88, 0x73CD, 0x8CC3, 0x93AE, 0x9673, 0x6D25, 0x589C, 0x690E,
- 0x69CC, 0x8FFD, 0x939A, 0x75DB, 0x901A, 0x585A, 0x6802, 0x63B4,
- 0x69FB, 0x4F43, 0x6F2C, 0x67D8, 0x8FBB, 0x8526, 0x7DB4, 0x9354,
- 0x693F, 0x6F70, 0x576A, 0x58F7, 0x5B2C, 0x7D2C, 0x722A, 0x540A,
- 0x91E3, 0x9DB4, 0x4EAD, 0x4F4E, 0x505C, 0x5075, 0x5243, 0x8C9E,
- 0x5448, 0x5824, 0x5B9A, 0x5E1D, 0x5E95, 0x5EAD, 0x5EF7, 0x5F1F,
- 0x608C, 0x62B5, 0x633A, 0x63D0, 0x68AF, 0x6C40, 0x7887, 0x798E,
- 0x7A0B, 0x7DE0, 0x8247, 0x8A02, 0x8AE6, 0x8E44, 0x9013,
-};
-static const unsigned short euc_to_utf8_C5[] = {
- 0x90B8, 0x912D, 0x91D8, 0x9F0E, 0x6CE5, 0x6458, 0x64E2,
- 0x6575, 0x6EF4, 0x7684, 0x7B1B, 0x9069, 0x93D1, 0x6EBA, 0x54F2,
- 0x5FB9, 0x64A4, 0x8F4D, 0x8FED, 0x9244, 0x5178, 0x586B, 0x5929,
- 0x5C55, 0x5E97, 0x6DFB, 0x7E8F, 0x751C, 0x8CBC, 0x8EE2, 0x985B,
- 0x70B9, 0x4F1D, 0x6BBF, 0x6FB1, 0x7530, 0x96FB, 0x514E, 0x5410,
- 0x5835, 0x5857, 0x59AC, 0x5C60, 0x5F92, 0x6597, 0x675C, 0x6E21,
- 0x767B, 0x83DF, 0x8CED, 0x9014, 0x90FD, 0x934D, 0x7825, 0x783A,
- 0x52AA, 0x5EA6, 0x571F, 0x5974, 0x6012, 0x5012, 0x515A, 0x51AC,
- 0x51CD, 0x5200, 0x5510, 0x5854, 0x5858, 0x5957, 0x5B95, 0x5CF6,
- 0x5D8B, 0x60BC, 0x6295, 0x642D, 0x6771, 0x6843, 0x68BC, 0x68DF,
- 0x76D7, 0x6DD8, 0x6E6F, 0x6D9B, 0x706F, 0x71C8, 0x5F53, 0x75D8,
- 0x7977, 0x7B49, 0x7B54, 0x7B52, 0x7CD6, 0x7D71, 0x5230,
-};
-static const unsigned short euc_to_utf8_C6[] = {
- 0x8463, 0x8569, 0x85E4, 0x8A0E, 0x8B04, 0x8C46, 0x8E0F,
- 0x9003, 0x900F, 0x9419, 0x9676, 0x982D, 0x9A30, 0x95D8, 0x50CD,
- 0x52D5, 0x540C, 0x5802, 0x5C0E, 0x61A7, 0x649E, 0x6D1E, 0x77B3,
- 0x7AE5, 0x80F4, 0x8404, 0x9053, 0x9285, 0x5CE0, 0x9D07, 0x533F,
- 0x5F97, 0x5FB3, 0x6D9C, 0x7279, 0x7763, 0x79BF, 0x7BE4, 0x6BD2,
- 0x72EC, 0x8AAD, 0x6803, 0x6A61, 0x51F8, 0x7A81, 0x6934, 0x5C4A,
- 0x9CF6, 0x82EB, 0x5BC5, 0x9149, 0x701E, 0x5678, 0x5C6F, 0x60C7,
- 0x6566, 0x6C8C, 0x8C5A, 0x9041, 0x9813, 0x5451, 0x66C7, 0x920D,
- 0x5948, 0x90A3, 0x5185, 0x4E4D, 0x51EA, 0x8599, 0x8B0E, 0x7058,
- 0x637A, 0x934B, 0x6962, 0x99B4, 0x7E04, 0x7577, 0x5357, 0x6960,
- 0x8EDF, 0x96E3, 0x6C5D, 0x4E8C, 0x5C3C, 0x5F10, 0x8FE9, 0x5302,
- 0x8CD1, 0x8089, 0x8679, 0x5EFF, 0x65E5, 0x4E73, 0x5165,
-};
-static const unsigned short euc_to_utf8_C7[] = {
- 0x5982, 0x5C3F, 0x97EE, 0x4EFB, 0x598A, 0x5FCD, 0x8A8D,
- 0x6FE1, 0x79B0, 0x7962, 0x5BE7, 0x8471, 0x732B, 0x71B1, 0x5E74,
- 0x5FF5, 0x637B, 0x649A, 0x71C3, 0x7C98, 0x4E43, 0x5EFC, 0x4E4B,
- 0x57DC, 0x56A2, 0x60A9, 0x6FC3, 0x7D0D, 0x80FD, 0x8133, 0x81BF,
- 0x8FB2, 0x8997, 0x86A4, 0x5DF4, 0x628A, 0x64AD, 0x8987, 0x6777,
- 0x6CE2, 0x6D3E, 0x7436, 0x7834, 0x5A46, 0x7F75, 0x82AD, 0x99AC,
- 0x4FF3, 0x5EC3, 0x62DD, 0x6392, 0x6557, 0x676F, 0x76C3, 0x724C,
- 0x80CC, 0x80BA, 0x8F29, 0x914D, 0x500D, 0x57F9, 0x5A92, 0x6885,
- 0x6973, 0x7164, 0x72FD, 0x8CB7, 0x58F2, 0x8CE0, 0x966A, 0x9019,
- 0x877F, 0x79E4, 0x77E7, 0x8429, 0x4F2F, 0x5265, 0x535A, 0x62CD,
- 0x67CF, 0x6CCA, 0x767D, 0x7B94, 0x7C95, 0x8236, 0x8584, 0x8FEB,
- 0x66DD, 0x6F20, 0x7206, 0x7E1B, 0x83AB, 0x99C1, 0x9EA6,
-};
-static const unsigned short euc_to_utf8_C8[] = {
- 0x51FD, 0x7BB1, 0x7872, 0x7BB8, 0x8087, 0x7B48, 0x6AE8,
- 0x5E61, 0x808C, 0x7551, 0x7560, 0x516B, 0x9262, 0x6E8C, 0x767A,
- 0x9197, 0x9AEA, 0x4F10, 0x7F70, 0x629C, 0x7B4F, 0x95A5, 0x9CE9,
- 0x567A, 0x5859, 0x86E4, 0x96BC, 0x4F34, 0x5224, 0x534A, 0x53CD,
- 0x53DB, 0x5E06, 0x642C, 0x6591, 0x677F, 0x6C3E, 0x6C4E, 0x7248,
- 0x72AF, 0x73ED, 0x7554, 0x7E41, 0x822C, 0x85E9, 0x8CA9, 0x7BC4,
- 0x91C6, 0x7169, 0x9812, 0x98EF, 0x633D, 0x6669, 0x756A, 0x76E4,
- 0x78D0, 0x8543, 0x86EE, 0x532A, 0x5351, 0x5426, 0x5983, 0x5E87,
- 0x5F7C, 0x60B2, 0x6249, 0x6279, 0x62AB, 0x6590, 0x6BD4, 0x6CCC,
- 0x75B2, 0x76AE, 0x7891, 0x79D8, 0x7DCB, 0x7F77, 0x80A5, 0x88AB,
- 0x8AB9, 0x8CBB, 0x907F, 0x975E, 0x98DB, 0x6A0B, 0x7C38, 0x5099,
- 0x5C3E, 0x5FAE, 0x6787, 0x6BD8, 0x7435, 0x7709, 0x7F8E,
-};
-static const unsigned short euc_to_utf8_C9[] = {
- 0x9F3B, 0x67CA, 0x7A17, 0x5339, 0x758B, 0x9AED, 0x5F66,
- 0x819D, 0x83F1, 0x8098, 0x5F3C, 0x5FC5, 0x7562, 0x7B46, 0x903C,
- 0x6867, 0x59EB, 0x5A9B, 0x7D10, 0x767E, 0x8B2C, 0x4FF5, 0x5F6A,
- 0x6A19, 0x6C37, 0x6F02, 0x74E2, 0x7968, 0x8868, 0x8A55, 0x8C79,
- 0x5EDF, 0x63CF, 0x75C5, 0x79D2, 0x82D7, 0x9328, 0x92F2, 0x849C,
- 0x86ED, 0x9C2D, 0x54C1, 0x5F6C, 0x658C, 0x6D5C, 0x7015, 0x8CA7,
- 0x8CD3, 0x983B, 0x654F, 0x74F6, 0x4E0D, 0x4ED8, 0x57E0, 0x592B,
- 0x5A66, 0x5BCC, 0x51A8, 0x5E03, 0x5E9C, 0x6016, 0x6276, 0x6577,
- 0x65A7, 0x666E, 0x6D6E, 0x7236, 0x7B26, 0x8150, 0x819A, 0x8299,
- 0x8B5C, 0x8CA0, 0x8CE6, 0x8D74, 0x961C, 0x9644, 0x4FAE, 0x64AB,
- 0x6B66, 0x821E, 0x8461, 0x856A, 0x90E8, 0x5C01, 0x6953, 0x98A8,
- 0x847A, 0x8557, 0x4F0F, 0x526F, 0x5FA9, 0x5E45, 0x670D,
-};
-static const unsigned short euc_to_utf8_CA[] = {
- 0x798F, 0x8179, 0x8907, 0x8986, 0x6DF5, 0x5F17, 0x6255,
- 0x6CB8, 0x4ECF, 0x7269, 0x9B92, 0x5206, 0x543B, 0x5674, 0x58B3,
- 0x61A4, 0x626E, 0x711A, 0x596E, 0x7C89, 0x7CDE, 0x7D1B, 0x96F0,
- 0x6587, 0x805E, 0x4E19, 0x4F75, 0x5175, 0x5840, 0x5E63, 0x5E73,
- 0x5F0A, 0x67C4, 0x4E26, 0x853D, 0x9589, 0x965B, 0x7C73, 0x9801,
- 0x50FB, 0x58C1, 0x7656, 0x78A7, 0x5225, 0x77A5, 0x8511, 0x7B86,
- 0x504F, 0x5909, 0x7247, 0x7BC7, 0x7DE8, 0x8FBA, 0x8FD4, 0x904D,
- 0x4FBF, 0x52C9, 0x5A29, 0x5F01, 0x97AD, 0x4FDD, 0x8217, 0x92EA,
- 0x5703, 0x6355, 0x6B69, 0x752B, 0x88DC, 0x8F14, 0x7A42, 0x52DF,
- 0x5893, 0x6155, 0x620A, 0x66AE, 0x6BCD, 0x7C3F, 0x83E9, 0x5023,
- 0x4FF8, 0x5305, 0x5446, 0x5831, 0x5949, 0x5B9D, 0x5CF0, 0x5CEF,
- 0x5D29, 0x5E96, 0x62B1, 0x6367, 0x653E, 0x65B9, 0x670B,
-};
-static const unsigned short euc_to_utf8_CB[] = {
- 0x6CD5, 0x6CE1, 0x70F9, 0x7832, 0x7E2B, 0x80DE, 0x82B3,
- 0x840C, 0x84EC, 0x8702, 0x8912, 0x8A2A, 0x8C4A, 0x90A6, 0x92D2,
- 0x98FD, 0x9CF3, 0x9D6C, 0x4E4F, 0x4EA1, 0x508D, 0x5256, 0x574A,
- 0x59A8, 0x5E3D, 0x5FD8, 0x5FD9, 0x623F, 0x66B4, 0x671B, 0x67D0,
- 0x68D2, 0x5192, 0x7D21, 0x80AA, 0x81A8, 0x8B00, 0x8C8C, 0x8CBF,
- 0x927E, 0x9632, 0x5420, 0x982C, 0x5317, 0x50D5, 0x535C, 0x58A8,
- 0x64B2, 0x6734, 0x7267, 0x7766, 0x7A46, 0x91E6, 0x52C3, 0x6CA1,
- 0x6B86, 0x5800, 0x5E4C, 0x5954, 0x672C, 0x7FFB, 0x51E1, 0x76C6,
- 0x6469, 0x78E8, 0x9B54, 0x9EBB, 0x57CB, 0x59B9, 0x6627, 0x679A,
- 0x6BCE, 0x54E9, 0x69D9, 0x5E55, 0x819C, 0x6795, 0x9BAA, 0x67FE,
- 0x9C52, 0x685D, 0x4EA6, 0x4FE3, 0x53C8, 0x62B9, 0x672B, 0x6CAB,
- 0x8FC4, 0x4FAD, 0x7E6D, 0x9EBF, 0x4E07, 0x6162, 0x6E80,
-};
-static const unsigned short euc_to_utf8_CC[] = {
- 0x6F2B, 0x8513, 0x5473, 0x672A, 0x9B45, 0x5DF3, 0x7B95,
- 0x5CAC, 0x5BC6, 0x871C, 0x6E4A, 0x84D1, 0x7A14, 0x8108, 0x5999,
- 0x7C8D, 0x6C11, 0x7720, 0x52D9, 0x5922, 0x7121, 0x725F, 0x77DB,
- 0x9727, 0x9D61, 0x690B, 0x5A7F, 0x5A18, 0x51A5, 0x540D, 0x547D,
- 0x660E, 0x76DF, 0x8FF7, 0x9298, 0x9CF4, 0x59EA, 0x725D, 0x6EC5,
- 0x514D, 0x68C9, 0x7DBF, 0x7DEC, 0x9762, 0x9EBA, 0x6478, 0x6A21,
- 0x8302, 0x5984, 0x5B5F, 0x6BDB, 0x731B, 0x76F2, 0x7DB2, 0x8017,
- 0x8499, 0x5132, 0x6728, 0x9ED9, 0x76EE, 0x6762, 0x52FF, 0x9905,
- 0x5C24, 0x623B, 0x7C7E, 0x8CB0, 0x554F, 0x60B6, 0x7D0B, 0x9580,
- 0x5301, 0x4E5F, 0x51B6, 0x591C, 0x723A, 0x8036, 0x91CE, 0x5F25,
- 0x77E2, 0x5384, 0x5F79, 0x7D04, 0x85AC, 0x8A33, 0x8E8D, 0x9756,
- 0x67F3, 0x85AE, 0x9453, 0x6109, 0x6108, 0x6CB9, 0x7652,
-};
-static const unsigned short euc_to_utf8_CD[] = {
- 0x8AED, 0x8F38, 0x552F, 0x4F51, 0x512A, 0x52C7, 0x53CB,
- 0x5BA5, 0x5E7D, 0x60A0, 0x6182, 0x63D6, 0x6709, 0x67DA, 0x6E67,
- 0x6D8C, 0x7336, 0x7337, 0x7531, 0x7950, 0x88D5, 0x8A98, 0x904A,
- 0x9091, 0x90F5, 0x96C4, 0x878D, 0x5915, 0x4E88, 0x4F59, 0x4E0E,
- 0x8A89, 0x8F3F, 0x9810, 0x50AD, 0x5E7C, 0x5996, 0x5BB9, 0x5EB8,
- 0x63DA, 0x63FA, 0x64C1, 0x66DC, 0x694A, 0x69D8, 0x6D0B, 0x6EB6,
- 0x7194, 0x7528, 0x7AAF, 0x7F8A, 0x8000, 0x8449, 0x84C9, 0x8981,
- 0x8B21, 0x8E0A, 0x9065, 0x967D, 0x990A, 0x617E, 0x6291, 0x6B32,
- 0x6C83, 0x6D74, 0x7FCC, 0x7FFC, 0x6DC0, 0x7F85, 0x87BA, 0x88F8,
- 0x6765, 0x83B1, 0x983C, 0x96F7, 0x6D1B, 0x7D61, 0x843D, 0x916A,
- 0x4E71, 0x5375, 0x5D50, 0x6B04, 0x6FEB, 0x85CD, 0x862D, 0x89A7,
- 0x5229, 0x540F, 0x5C65, 0x674E, 0x68A8, 0x7406, 0x7483,
-};
-static const unsigned short euc_to_utf8_CE[] = {
- 0x75E2, 0x88CF, 0x88E1, 0x91CC, 0x96E2, 0x9678, 0x5F8B,
- 0x7387, 0x7ACB, 0x844E, 0x63A0, 0x7565, 0x5289, 0x6D41, 0x6E9C,
- 0x7409, 0x7559, 0x786B, 0x7C92, 0x9686, 0x7ADC, 0x9F8D, 0x4FB6,
- 0x616E, 0x65C5, 0x865C, 0x4E86, 0x4EAE, 0x50DA, 0x4E21, 0x51CC,
- 0x5BEE, 0x6599, 0x6881, 0x6DBC, 0x731F, 0x7642, 0x77AD, 0x7A1C,
- 0x7CE7, 0x826F, 0x8AD2, 0x907C, 0x91CF, 0x9675, 0x9818, 0x529B,
- 0x7DD1, 0x502B, 0x5398, 0x6797, 0x6DCB, 0x71D0, 0x7433, 0x81E8,
- 0x8F2A, 0x96A3, 0x9C57, 0x9E9F, 0x7460, 0x5841, 0x6D99, 0x7D2F,
- 0x985E, 0x4EE4, 0x4F36, 0x4F8B, 0x51B7, 0x52B1, 0x5DBA, 0x601C,
- 0x73B2, 0x793C, 0x82D3, 0x9234, 0x96B7, 0x96F6, 0x970A, 0x9E97,
- 0x9F62, 0x66A6, 0x6B74, 0x5217, 0x52A3, 0x70C8, 0x88C2, 0x5EC9,
- 0x604B, 0x6190, 0x6F23, 0x7149, 0x7C3E, 0x7DF4, 0x806F,
-};
-static const unsigned short euc_to_utf8_CF[] = {
- 0x84EE, 0x9023, 0x932C, 0x5442, 0x9B6F, 0x6AD3, 0x7089,
- 0x8CC2, 0x8DEF, 0x9732, 0x52B4, 0x5A41, 0x5ECA, 0x5F04, 0x6717,
- 0x697C, 0x6994, 0x6D6A, 0x6F0F, 0x7262, 0x72FC, 0x7BED, 0x8001,
- 0x807E, 0x874B, 0x90CE, 0x516D, 0x9E93, 0x7984, 0x808B, 0x9332,
- 0x8AD6, 0x502D, 0x548C, 0x8A71, 0x6B6A, 0x8CC4, 0x8107, 0x60D1,
- 0x67A0, 0x9DF2, 0x4E99, 0x4E98, 0x9C10, 0x8A6B, 0x85C1, 0x8568,
- 0x6900, 0x6E7E, 0x7897, 0x8155, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short euc_to_utf8_CF_x0213[] = {
- 0x84EE, 0x9023, 0x932C, 0x5442, 0x9B6F, 0x6AD3, 0x7089,
- 0x8CC2, 0x8DEF, 0x9732, 0x52B4, 0x5A41, 0x5ECA, 0x5F04, 0x6717,
- 0x697C, 0x6994, 0x6D6A, 0x6F0F, 0x7262, 0x72FC, 0x7BED, 0x8001,
- 0x807E, 0x874B, 0x90CE, 0x516D, 0x9E93, 0x7984, 0x808B, 0x9332,
- 0x8AD6, 0x502D, 0x548C, 0x8A71, 0x6B6A, 0x8CC4, 0x8107, 0x60D1,
- 0x67A0, 0x9DF2, 0x4E99, 0x4E98, 0x9C10, 0x8A6B, 0x85C1, 0x8568,
- 0x6900, 0x6E7E, 0x7897, 0x8155, 0xD842 /*0xDF9F*/, 0x5B41, 0x5B56, 0x5B7D,
- 0x5B93, 0x5BD8, 0x5BEC, 0x5C12, 0x5C1E, 0x5C23, 0x5C2B, 0x378D,
- 0x5C62, 0xFA3B, 0xFA3C, 0xD845 /*0xDEB4*/, 0x5C7A, 0x5C8F, 0x5C9F, 0x5CA3,
- 0x5CAA, 0x5CBA, 0x5CCB, 0x5CD0, 0x5CD2, 0x5CF4, 0xD847 /*0xDE34*/, 0x37E2,
- 0x5D0D, 0x5D27, 0xFA11, 0x5D46, 0x5D47, 0x5D53, 0x5D4A, 0x5D6D,
- 0x5D81, 0x5DA0, 0x5DA4, 0x5DA7, 0x5DB8, 0x5DCB, 0x541E,
-};
-static const unsigned short euc_to_utf8_D0[] = {
- 0x5F0C, 0x4E10, 0x4E15, 0x4E2A, 0x4E31, 0x4E36, 0x4E3C,
- 0x4E3F, 0x4E42, 0x4E56, 0x4E58, 0x4E82, 0x4E85, 0x8C6B, 0x4E8A,
- 0x8212, 0x5F0D, 0x4E8E, 0x4E9E, 0x4E9F, 0x4EA0, 0x4EA2, 0x4EB0,
- 0x4EB3, 0x4EB6, 0x4ECE, 0x4ECD, 0x4EC4, 0x4EC6, 0x4EC2, 0x4ED7,
- 0x4EDE, 0x4EED, 0x4EDF, 0x4EF7, 0x4F09, 0x4F5A, 0x4F30, 0x4F5B,
- 0x4F5D, 0x4F57, 0x4F47, 0x4F76, 0x4F88, 0x4F8F, 0x4F98, 0x4F7B,
- 0x4F69, 0x4F70, 0x4F91, 0x4F6F, 0x4F86, 0x4F96, 0x5118, 0x4FD4,
- 0x4FDF, 0x4FCE, 0x4FD8, 0x4FDB, 0x4FD1, 0x4FDA, 0x4FD0, 0x4FE4,
- 0x4FE5, 0x501A, 0x5028, 0x5014, 0x502A, 0x5025, 0x5005, 0x4F1C,
- 0x4FF6, 0x5021, 0x5029, 0x502C, 0x4FFE, 0x4FEF, 0x5011, 0x5006,
- 0x5043, 0x5047, 0x6703, 0x5055, 0x5050, 0x5048, 0x505A, 0x5056,
- 0x506C, 0x5078, 0x5080, 0x509A, 0x5085, 0x50B4, 0x50B2,
-};
-static const unsigned short euc_to_utf8_D1[] = {
- 0x50C9, 0x50CA, 0x50B3, 0x50C2, 0x50D6, 0x50DE, 0x50E5,
- 0x50ED, 0x50E3, 0x50EE, 0x50F9, 0x50F5, 0x5109, 0x5101, 0x5102,
- 0x5116, 0x5115, 0x5114, 0x511A, 0x5121, 0x513A, 0x5137, 0x513C,
- 0x513B, 0x513F, 0x5140, 0x5152, 0x514C, 0x5154, 0x5162, 0x7AF8,
- 0x5169, 0x516A, 0x516E, 0x5180, 0x5182, 0x56D8, 0x518C, 0x5189,
- 0x518F, 0x5191, 0x5193, 0x5195, 0x5196, 0x51A4, 0x51A6, 0x51A2,
- 0x51A9, 0x51AA, 0x51AB, 0x51B3, 0x51B1, 0x51B2, 0x51B0, 0x51B5,
- 0x51BD, 0x51C5, 0x51C9, 0x51DB, 0x51E0, 0x8655, 0x51E9, 0x51ED,
- 0x51F0, 0x51F5, 0x51FE, 0x5204, 0x520B, 0x5214, 0x520E, 0x5227,
- 0x522A, 0x522E, 0x5233, 0x5239, 0x524F, 0x5244, 0x524B, 0x524C,
- 0x525E, 0x5254, 0x526A, 0x5274, 0x5269, 0x5273, 0x527F, 0x527D,
- 0x528D, 0x5294, 0x5292, 0x5271, 0x5288, 0x5291, 0x8FA8,
-};
-static const unsigned short euc_to_utf8_D2[] = {
- 0x8FA7, 0x52AC, 0x52AD, 0x52BC, 0x52B5, 0x52C1, 0x52CD,
- 0x52D7, 0x52DE, 0x52E3, 0x52E6, 0x98ED, 0x52E0, 0x52F3, 0x52F5,
- 0x52F8, 0x52F9, 0x5306, 0x5308, 0x7538, 0x530D, 0x5310, 0x530F,
- 0x5315, 0x531A, 0x5323, 0x532F, 0x5331, 0x5333, 0x5338, 0x5340,
- 0x5346, 0x5345, 0x4E17, 0x5349, 0x534D, 0x51D6, 0x535E, 0x5369,
- 0x536E, 0x5918, 0x537B, 0x5377, 0x5382, 0x5396, 0x53A0, 0x53A6,
- 0x53A5, 0x53AE, 0x53B0, 0x53B6, 0x53C3, 0x7C12, 0x96D9, 0x53DF,
- 0x66FC, 0x71EE, 0x53EE, 0x53E8, 0x53ED, 0x53FA, 0x5401, 0x543D,
- 0x5440, 0x542C, 0x542D, 0x543C, 0x542E, 0x5436, 0x5429, 0x541D,
- 0x544E, 0x548F, 0x5475, 0x548E, 0x545F, 0x5471, 0x5477, 0x5470,
- 0x5492, 0x547B, 0x5480, 0x5476, 0x5484, 0x5490, 0x5486, 0x54C7,
- 0x54A2, 0x54B8, 0x54A5, 0x54AC, 0x54C4, 0x54C8, 0x54A8,
-};
-static const unsigned short euc_to_utf8_D3[] = {
- 0x54AB, 0x54C2, 0x54A4, 0x54BE, 0x54BC, 0x54D8, 0x54E5,
- 0x54E6, 0x550F, 0x5514, 0x54FD, 0x54EE, 0x54ED, 0x54FA, 0x54E2,
- 0x5539, 0x5540, 0x5563, 0x554C, 0x552E, 0x555C, 0x5545, 0x5556,
- 0x5557, 0x5538, 0x5533, 0x555D, 0x5599, 0x5580, 0x54AF, 0x558A,
- 0x559F, 0x557B, 0x557E, 0x5598, 0x559E, 0x55AE, 0x557C, 0x5583,
- 0x55A9, 0x5587, 0x55A8, 0x55DA, 0x55C5, 0x55DF, 0x55C4, 0x55DC,
- 0x55E4, 0x55D4, 0x5614, 0x55F7, 0x5616, 0x55FE, 0x55FD, 0x561B,
- 0x55F9, 0x564E, 0x5650, 0x71DF, 0x5634, 0x5636, 0x5632, 0x5638,
- 0x566B, 0x5664, 0x562F, 0x566C, 0x566A, 0x5686, 0x5680, 0x568A,
- 0x56A0, 0x5694, 0x568F, 0x56A5, 0x56AE, 0x56B6, 0x56B4, 0x56C2,
- 0x56BC, 0x56C1, 0x56C3, 0x56C0, 0x56C8, 0x56CE, 0x56D1, 0x56D3,
- 0x56D7, 0x56EE, 0x56F9, 0x5700, 0x56FF, 0x5704, 0x5709,
-};
-static const unsigned short euc_to_utf8_D4[] = {
- 0x5708, 0x570B, 0x570D, 0x5713, 0x5718, 0x5716, 0x55C7,
- 0x571C, 0x5726, 0x5737, 0x5738, 0x574E, 0x573B, 0x5740, 0x574F,
- 0x5769, 0x57C0, 0x5788, 0x5761, 0x577F, 0x5789, 0x5793, 0x57A0,
- 0x57B3, 0x57A4, 0x57AA, 0x57B0, 0x57C3, 0x57C6, 0x57D4, 0x57D2,
- 0x57D3, 0x580A, 0x57D6, 0x57E3, 0x580B, 0x5819, 0x581D, 0x5872,
- 0x5821, 0x5862, 0x584B, 0x5870, 0x6BC0, 0x5852, 0x583D, 0x5879,
- 0x5885, 0x58B9, 0x589F, 0x58AB, 0x58BA, 0x58DE, 0x58BB, 0x58B8,
- 0x58AE, 0x58C5, 0x58D3, 0x58D1, 0x58D7, 0x58D9, 0x58D8, 0x58E5,
- 0x58DC, 0x58E4, 0x58DF, 0x58EF, 0x58FA, 0x58F9, 0x58FB, 0x58FC,
- 0x58FD, 0x5902, 0x590A, 0x5910, 0x591B, 0x68A6, 0x5925, 0x592C,
- 0x592D, 0x5932, 0x5938, 0x593E, 0x7AD2, 0x5955, 0x5950, 0x594E,
- 0x595A, 0x5958, 0x5962, 0x5960, 0x5967, 0x596C, 0x5969,
-};
-static const unsigned short euc_to_utf8_D5[] = {
- 0x5978, 0x5981, 0x599D, 0x4F5E, 0x4FAB, 0x59A3, 0x59B2,
- 0x59C6, 0x59E8, 0x59DC, 0x598D, 0x59D9, 0x59DA, 0x5A25, 0x5A1F,
- 0x5A11, 0x5A1C, 0x5A09, 0x5A1A, 0x5A40, 0x5A6C, 0x5A49, 0x5A35,
- 0x5A36, 0x5A62, 0x5A6A, 0x5A9A, 0x5ABC, 0x5ABE, 0x5ACB, 0x5AC2,
- 0x5ABD, 0x5AE3, 0x5AD7, 0x5AE6, 0x5AE9, 0x5AD6, 0x5AFA, 0x5AFB,
- 0x5B0C, 0x5B0B, 0x5B16, 0x5B32, 0x5AD0, 0x5B2A, 0x5B36, 0x5B3E,
- 0x5B43, 0x5B45, 0x5B40, 0x5B51, 0x5B55, 0x5B5A, 0x5B5B, 0x5B65,
- 0x5B69, 0x5B70, 0x5B73, 0x5B75, 0x5B78, 0x6588, 0x5B7A, 0x5B80,
- 0x5B83, 0x5BA6, 0x5BB8, 0x5BC3, 0x5BC7, 0x5BC9, 0x5BD4, 0x5BD0,
- 0x5BE4, 0x5BE6, 0x5BE2, 0x5BDE, 0x5BE5, 0x5BEB, 0x5BF0, 0x5BF6,
- 0x5BF3, 0x5C05, 0x5C07, 0x5C08, 0x5C0D, 0x5C13, 0x5C20, 0x5C22,
- 0x5C28, 0x5C38, 0x5C39, 0x5C41, 0x5C46, 0x5C4E, 0x5C53,
-};
-static const unsigned short euc_to_utf8_D6[] = {
- 0x5C50, 0x5C4F, 0x5B71, 0x5C6C, 0x5C6E, 0x4E62, 0x5C76,
- 0x5C79, 0x5C8C, 0x5C91, 0x5C94, 0x599B, 0x5CAB, 0x5CBB, 0x5CB6,
- 0x5CBC, 0x5CB7, 0x5CC5, 0x5CBE, 0x5CC7, 0x5CD9, 0x5CE9, 0x5CFD,
- 0x5CFA, 0x5CED, 0x5D8C, 0x5CEA, 0x5D0B, 0x5D15, 0x5D17, 0x5D5C,
- 0x5D1F, 0x5D1B, 0x5D11, 0x5D14, 0x5D22, 0x5D1A, 0x5D19, 0x5D18,
- 0x5D4C, 0x5D52, 0x5D4E, 0x5D4B, 0x5D6C, 0x5D73, 0x5D76, 0x5D87,
- 0x5D84, 0x5D82, 0x5DA2, 0x5D9D, 0x5DAC, 0x5DAE, 0x5DBD, 0x5D90,
- 0x5DB7, 0x5DBC, 0x5DC9, 0x5DCD, 0x5DD3, 0x5DD2, 0x5DD6, 0x5DDB,
- 0x5DEB, 0x5DF2, 0x5DF5, 0x5E0B, 0x5E1A, 0x5E19, 0x5E11, 0x5E1B,
- 0x5E36, 0x5E37, 0x5E44, 0x5E43, 0x5E40, 0x5E4E, 0x5E57, 0x5E54,
- 0x5E5F, 0x5E62, 0x5E64, 0x5E47, 0x5E75, 0x5E76, 0x5E7A, 0x9EBC,
- 0x5E7F, 0x5EA0, 0x5EC1, 0x5EC2, 0x5EC8, 0x5ED0, 0x5ECF,
-};
-static const unsigned short euc_to_utf8_D7[] = {
- 0x5ED6, 0x5EE3, 0x5EDD, 0x5EDA, 0x5EDB, 0x5EE2, 0x5EE1,
- 0x5EE8, 0x5EE9, 0x5EEC, 0x5EF1, 0x5EF3, 0x5EF0, 0x5EF4, 0x5EF8,
- 0x5EFE, 0x5F03, 0x5F09, 0x5F5D, 0x5F5C, 0x5F0B, 0x5F11, 0x5F16,
- 0x5F29, 0x5F2D, 0x5F38, 0x5F41, 0x5F48, 0x5F4C, 0x5F4E, 0x5F2F,
- 0x5F51, 0x5F56, 0x5F57, 0x5F59, 0x5F61, 0x5F6D, 0x5F73, 0x5F77,
- 0x5F83, 0x5F82, 0x5F7F, 0x5F8A, 0x5F88, 0x5F91, 0x5F87, 0x5F9E,
- 0x5F99, 0x5F98, 0x5FA0, 0x5FA8, 0x5FAD, 0x5FBC, 0x5FD6, 0x5FFB,
- 0x5FE4, 0x5FF8, 0x5FF1, 0x5FDD, 0x60B3, 0x5FFF, 0x6021, 0x6060,
- 0x6019, 0x6010, 0x6029, 0x600E, 0x6031, 0x601B, 0x6015, 0x602B,
- 0x6026, 0x600F, 0x603A, 0x605A, 0x6041, 0x606A, 0x6077, 0x605F,
- 0x604A, 0x6046, 0x604D, 0x6063, 0x6043, 0x6064, 0x6042, 0x606C,
- 0x606B, 0x6059, 0x6081, 0x608D, 0x60E7, 0x6083, 0x609A,
-};
-static const unsigned short euc_to_utf8_D8[] = {
- 0x6084, 0x609B, 0x6096, 0x6097, 0x6092, 0x60A7, 0x608B,
- 0x60E1, 0x60B8, 0x60E0, 0x60D3, 0x60B4, 0x5FF0, 0x60BD, 0x60C6,
- 0x60B5, 0x60D8, 0x614D, 0x6115, 0x6106, 0x60F6, 0x60F7, 0x6100,
- 0x60F4, 0x60FA, 0x6103, 0x6121, 0x60FB, 0x60F1, 0x610D, 0x610E,
- 0x6147, 0x613E, 0x6128, 0x6127, 0x614A, 0x613F, 0x613C, 0x612C,
- 0x6134, 0x613D, 0x6142, 0x6144, 0x6173, 0x6177, 0x6158, 0x6159,
- 0x615A, 0x616B, 0x6174, 0x616F, 0x6165, 0x6171, 0x615F, 0x615D,
- 0x6153, 0x6175, 0x6199, 0x6196, 0x6187, 0x61AC, 0x6194, 0x619A,
- 0x618A, 0x6191, 0x61AB, 0x61AE, 0x61CC, 0x61CA, 0x61C9, 0x61F7,
- 0x61C8, 0x61C3, 0x61C6, 0x61BA, 0x61CB, 0x7F79, 0x61CD, 0x61E6,
- 0x61E3, 0x61F6, 0x61FA, 0x61F4, 0x61FF, 0x61FD, 0x61FC, 0x61FE,
- 0x6200, 0x6208, 0x6209, 0x620D, 0x620C, 0x6214, 0x621B,
-};
-static const unsigned short euc_to_utf8_D9[] = {
- 0x621E, 0x6221, 0x622A, 0x622E, 0x6230, 0x6232, 0x6233,
- 0x6241, 0x624E, 0x625E, 0x6263, 0x625B, 0x6260, 0x6268, 0x627C,
- 0x6282, 0x6289, 0x627E, 0x6292, 0x6293, 0x6296, 0x62D4, 0x6283,
- 0x6294, 0x62D7, 0x62D1, 0x62BB, 0x62CF, 0x62FF, 0x62C6, 0x64D4,
- 0x62C8, 0x62DC, 0x62CC, 0x62CA, 0x62C2, 0x62C7, 0x629B, 0x62C9,
- 0x630C, 0x62EE, 0x62F1, 0x6327, 0x6302, 0x6308, 0x62EF, 0x62F5,
- 0x6350, 0x633E, 0x634D, 0x641C, 0x634F, 0x6396, 0x638E, 0x6380,
- 0x63AB, 0x6376, 0x63A3, 0x638F, 0x6389, 0x639F, 0x63B5, 0x636B,
- 0x6369, 0x63BE, 0x63E9, 0x63C0, 0x63C6, 0x63E3, 0x63C9, 0x63D2,
- 0x63F6, 0x63C4, 0x6416, 0x6434, 0x6406, 0x6413, 0x6426, 0x6436,
- 0x651D, 0x6417, 0x6428, 0x640F, 0x6467, 0x646F, 0x6476, 0x644E,
- 0x652A, 0x6495, 0x6493, 0x64A5, 0x64A9, 0x6488, 0x64BC,
-};
-static const unsigned short euc_to_utf8_DA[] = {
- 0x64DA, 0x64D2, 0x64C5, 0x64C7, 0x64BB, 0x64D8, 0x64C2,
- 0x64F1, 0x64E7, 0x8209, 0x64E0, 0x64E1, 0x62AC, 0x64E3, 0x64EF,
- 0x652C, 0x64F6, 0x64F4, 0x64F2, 0x64FA, 0x6500, 0x64FD, 0x6518,
- 0x651C, 0x6505, 0x6524, 0x6523, 0x652B, 0x6534, 0x6535, 0x6537,
- 0x6536, 0x6538, 0x754B, 0x6548, 0x6556, 0x6555, 0x654D, 0x6558,
- 0x655E, 0x655D, 0x6572, 0x6578, 0x6582, 0x6583, 0x8B8A, 0x659B,
- 0x659F, 0x65AB, 0x65B7, 0x65C3, 0x65C6, 0x65C1, 0x65C4, 0x65CC,
- 0x65D2, 0x65DB, 0x65D9, 0x65E0, 0x65E1, 0x65F1, 0x6772, 0x660A,
- 0x6603, 0x65FB, 0x6773, 0x6635, 0x6636, 0x6634, 0x661C, 0x664F,
- 0x6644, 0x6649, 0x6641, 0x665E, 0x665D, 0x6664, 0x6667, 0x6668,
- 0x665F, 0x6662, 0x6670, 0x6683, 0x6688, 0x668E, 0x6689, 0x6684,
- 0x6698, 0x669D, 0x66C1, 0x66B9, 0x66C9, 0x66BE, 0x66BC,
-};
-static const unsigned short euc_to_utf8_DB[] = {
- 0x66C4, 0x66B8, 0x66D6, 0x66DA, 0x66E0, 0x663F, 0x66E6,
- 0x66E9, 0x66F0, 0x66F5, 0x66F7, 0x670F, 0x6716, 0x671E, 0x6726,
- 0x6727, 0x9738, 0x672E, 0x673F, 0x6736, 0x6741, 0x6738, 0x6737,
- 0x6746, 0x675E, 0x6760, 0x6759, 0x6763, 0x6764, 0x6789, 0x6770,
- 0x67A9, 0x677C, 0x676A, 0x678C, 0x678B, 0x67A6, 0x67A1, 0x6785,
- 0x67B7, 0x67EF, 0x67B4, 0x67EC, 0x67B3, 0x67E9, 0x67B8, 0x67E4,
- 0x67DE, 0x67DD, 0x67E2, 0x67EE, 0x67B9, 0x67CE, 0x67C6, 0x67E7,
- 0x6A9C, 0x681E, 0x6846, 0x6829, 0x6840, 0x684D, 0x6832, 0x684E,
- 0x68B3, 0x682B, 0x6859, 0x6863, 0x6877, 0x687F, 0x689F, 0x688F,
- 0x68AD, 0x6894, 0x689D, 0x689B, 0x6883, 0x6AAE, 0x68B9, 0x6874,
- 0x68B5, 0x68A0, 0x68BA, 0x690F, 0x688D, 0x687E, 0x6901, 0x68CA,
- 0x6908, 0x68D8, 0x6922, 0x6926, 0x68E1, 0x690C, 0x68CD,
-};
-static const unsigned short euc_to_utf8_DC[] = {
- 0x68D4, 0x68E7, 0x68D5, 0x6936, 0x6912, 0x6904, 0x68D7,
- 0x68E3, 0x6925, 0x68F9, 0x68E0, 0x68EF, 0x6928, 0x692A, 0x691A,
- 0x6923, 0x6921, 0x68C6, 0x6979, 0x6977, 0x695C, 0x6978, 0x696B,
- 0x6954, 0x697E, 0x696E, 0x6939, 0x6974, 0x693D, 0x6959, 0x6930,
- 0x6961, 0x695E, 0x695D, 0x6981, 0x696A, 0x69B2, 0x69AE, 0x69D0,
- 0x69BF, 0x69C1, 0x69D3, 0x69BE, 0x69CE, 0x5BE8, 0x69CA, 0x69DD,
- 0x69BB, 0x69C3, 0x69A7, 0x6A2E, 0x6991, 0x69A0, 0x699C, 0x6995,
- 0x69B4, 0x69DE, 0x69E8, 0x6A02, 0x6A1B, 0x69FF, 0x6B0A, 0x69F9,
- 0x69F2, 0x69E7, 0x6A05, 0x69B1, 0x6A1E, 0x69ED, 0x6A14, 0x69EB,
- 0x6A0A, 0x6A12, 0x6AC1, 0x6A23, 0x6A13, 0x6A44, 0x6A0C, 0x6A72,
- 0x6A36, 0x6A78, 0x6A47, 0x6A62, 0x6A59, 0x6A66, 0x6A48, 0x6A38,
- 0x6A22, 0x6A90, 0x6A8D, 0x6AA0, 0x6A84, 0x6AA2, 0x6AA3,
-};
-static const unsigned short euc_to_utf8_DD[] = {
- 0x6A97, 0x8617, 0x6ABB, 0x6AC3, 0x6AC2, 0x6AB8, 0x6AB3,
- 0x6AAC, 0x6ADE, 0x6AD1, 0x6ADF, 0x6AAA, 0x6ADA, 0x6AEA, 0x6AFB,
- 0x6B05, 0x8616, 0x6AFA, 0x6B12, 0x6B16, 0x9B31, 0x6B1F, 0x6B38,
- 0x6B37, 0x76DC, 0x6B39, 0x98EE, 0x6B47, 0x6B43, 0x6B49, 0x6B50,
- 0x6B59, 0x6B54, 0x6B5B, 0x6B5F, 0x6B61, 0x6B78, 0x6B79, 0x6B7F,
- 0x6B80, 0x6B84, 0x6B83, 0x6B8D, 0x6B98, 0x6B95, 0x6B9E, 0x6BA4,
- 0x6BAA, 0x6BAB, 0x6BAF, 0x6BB2, 0x6BB1, 0x6BB3, 0x6BB7, 0x6BBC,
- 0x6BC6, 0x6BCB, 0x6BD3, 0x6BDF, 0x6BEC, 0x6BEB, 0x6BF3, 0x6BEF,
- 0x9EBE, 0x6C08, 0x6C13, 0x6C14, 0x6C1B, 0x6C24, 0x6C23, 0x6C5E,
- 0x6C55, 0x6C62, 0x6C6A, 0x6C82, 0x6C8D, 0x6C9A, 0x6C81, 0x6C9B,
- 0x6C7E, 0x6C68, 0x6C73, 0x6C92, 0x6C90, 0x6CC4, 0x6CF1, 0x6CD3,
- 0x6CBD, 0x6CD7, 0x6CC5, 0x6CDD, 0x6CAE, 0x6CB1, 0x6CBE,
-};
-static const unsigned short euc_to_utf8_DE[] = {
- 0x6CBA, 0x6CDB, 0x6CEF, 0x6CD9, 0x6CEA, 0x6D1F, 0x884D,
- 0x6D36, 0x6D2B, 0x6D3D, 0x6D38, 0x6D19, 0x6D35, 0x6D33, 0x6D12,
- 0x6D0C, 0x6D63, 0x6D93, 0x6D64, 0x6D5A, 0x6D79, 0x6D59, 0x6D8E,
- 0x6D95, 0x6FE4, 0x6D85, 0x6DF9, 0x6E15, 0x6E0A, 0x6DB5, 0x6DC7,
- 0x6DE6, 0x6DB8, 0x6DC6, 0x6DEC, 0x6DDE, 0x6DCC, 0x6DE8, 0x6DD2,
- 0x6DC5, 0x6DFA, 0x6DD9, 0x6DE4, 0x6DD5, 0x6DEA, 0x6DEE, 0x6E2D,
- 0x6E6E, 0x6E2E, 0x6E19, 0x6E72, 0x6E5F, 0x6E3E, 0x6E23, 0x6E6B,
- 0x6E2B, 0x6E76, 0x6E4D, 0x6E1F, 0x6E43, 0x6E3A, 0x6E4E, 0x6E24,
- 0x6EFF, 0x6E1D, 0x6E38, 0x6E82, 0x6EAA, 0x6E98, 0x6EC9, 0x6EB7,
- 0x6ED3, 0x6EBD, 0x6EAF, 0x6EC4, 0x6EB2, 0x6ED4, 0x6ED5, 0x6E8F,
- 0x6EA5, 0x6EC2, 0x6E9F, 0x6F41, 0x6F11, 0x704C, 0x6EEC, 0x6EF8,
- 0x6EFE, 0x6F3F, 0x6EF2, 0x6F31, 0x6EEF, 0x6F32, 0x6ECC,
-};
-static const unsigned short euc_to_utf8_DF[] = {
- 0x6F3E, 0x6F13, 0x6EF7, 0x6F86, 0x6F7A, 0x6F78, 0x6F81,
- 0x6F80, 0x6F6F, 0x6F5B, 0x6FF3, 0x6F6D, 0x6F82, 0x6F7C, 0x6F58,
- 0x6F8E, 0x6F91, 0x6FC2, 0x6F66, 0x6FB3, 0x6FA3, 0x6FA1, 0x6FA4,
- 0x6FB9, 0x6FC6, 0x6FAA, 0x6FDF, 0x6FD5, 0x6FEC, 0x6FD4, 0x6FD8,
- 0x6FF1, 0x6FEE, 0x6FDB, 0x7009, 0x700B, 0x6FFA, 0x7011, 0x7001,
- 0x700F, 0x6FFE, 0x701B, 0x701A, 0x6F74, 0x701D, 0x7018, 0x701F,
- 0x7030, 0x703E, 0x7032, 0x7051, 0x7063, 0x7099, 0x7092, 0x70AF,
- 0x70F1, 0x70AC, 0x70B8, 0x70B3, 0x70AE, 0x70DF, 0x70CB, 0x70DD,
- 0x70D9, 0x7109, 0x70FD, 0x711C, 0x7119, 0x7165, 0x7155, 0x7188,
- 0x7166, 0x7162, 0x714C, 0x7156, 0x716C, 0x718F, 0x71FB, 0x7184,
- 0x7195, 0x71A8, 0x71AC, 0x71D7, 0x71B9, 0x71BE, 0x71D2, 0x71C9,
- 0x71D4, 0x71CE, 0x71E0, 0x71EC, 0x71E7, 0x71F5, 0x71FC,
-};
-static const unsigned short euc_to_utf8_E0[] = {
- 0x71F9, 0x71FF, 0x720D, 0x7210, 0x721B, 0x7228, 0x722D,
- 0x722C, 0x7230, 0x7232, 0x723B, 0x723C, 0x723F, 0x7240, 0x7246,
- 0x724B, 0x7258, 0x7274, 0x727E, 0x7282, 0x7281, 0x7287, 0x7292,
- 0x7296, 0x72A2, 0x72A7, 0x72B9, 0x72B2, 0x72C3, 0x72C6, 0x72C4,
- 0x72CE, 0x72D2, 0x72E2, 0x72E0, 0x72E1, 0x72F9, 0x72F7, 0x500F,
- 0x7317, 0x730A, 0x731C, 0x7316, 0x731D, 0x7334, 0x732F, 0x7329,
- 0x7325, 0x733E, 0x734E, 0x734F, 0x9ED8, 0x7357, 0x736A, 0x7368,
- 0x7370, 0x7378, 0x7375, 0x737B, 0x737A, 0x73C8, 0x73B3, 0x73CE,
- 0x73BB, 0x73C0, 0x73E5, 0x73EE, 0x73DE, 0x74A2, 0x7405, 0x746F,
- 0x7425, 0x73F8, 0x7432, 0x743A, 0x7455, 0x743F, 0x745F, 0x7459,
- 0x7441, 0x745C, 0x7469, 0x7470, 0x7463, 0x746A, 0x7476, 0x747E,
- 0x748B, 0x749E, 0x74A7, 0x74CA, 0x74CF, 0x74D4, 0x73F1,
-};
-static const unsigned short euc_to_utf8_E1[] = {
- 0x74E0, 0x74E3, 0x74E7, 0x74E9, 0x74EE, 0x74F2, 0x74F0,
- 0x74F1, 0x74F8, 0x74F7, 0x7504, 0x7503, 0x7505, 0x750C, 0x750E,
- 0x750D, 0x7515, 0x7513, 0x751E, 0x7526, 0x752C, 0x753C, 0x7544,
- 0x754D, 0x754A, 0x7549, 0x755B, 0x7546, 0x755A, 0x7569, 0x7564,
- 0x7567, 0x756B, 0x756D, 0x7578, 0x7576, 0x7586, 0x7587, 0x7574,
- 0x758A, 0x7589, 0x7582, 0x7594, 0x759A, 0x759D, 0x75A5, 0x75A3,
- 0x75C2, 0x75B3, 0x75C3, 0x75B5, 0x75BD, 0x75B8, 0x75BC, 0x75B1,
- 0x75CD, 0x75CA, 0x75D2, 0x75D9, 0x75E3, 0x75DE, 0x75FE, 0x75FF,
- 0x75FC, 0x7601, 0x75F0, 0x75FA, 0x75F2, 0x75F3, 0x760B, 0x760D,
- 0x7609, 0x761F, 0x7627, 0x7620, 0x7621, 0x7622, 0x7624, 0x7634,
- 0x7630, 0x763B, 0x7647, 0x7648, 0x7646, 0x765C, 0x7658, 0x7661,
- 0x7662, 0x7668, 0x7669, 0x766A, 0x7667, 0x766C, 0x7670,
-};
-static const unsigned short euc_to_utf8_E2[] = {
- 0x7672, 0x7676, 0x7678, 0x767C, 0x7680, 0x7683, 0x7688,
- 0x768B, 0x768E, 0x7696, 0x7693, 0x7699, 0x769A, 0x76B0, 0x76B4,
- 0x76B8, 0x76B9, 0x76BA, 0x76C2, 0x76CD, 0x76D6, 0x76D2, 0x76DE,
- 0x76E1, 0x76E5, 0x76E7, 0x76EA, 0x862F, 0x76FB, 0x7708, 0x7707,
- 0x7704, 0x7729, 0x7724, 0x771E, 0x7725, 0x7726, 0x771B, 0x7737,
- 0x7738, 0x7747, 0x775A, 0x7768, 0x776B, 0x775B, 0x7765, 0x777F,
- 0x777E, 0x7779, 0x778E, 0x778B, 0x7791, 0x77A0, 0x779E, 0x77B0,
- 0x77B6, 0x77B9, 0x77BF, 0x77BC, 0x77BD, 0x77BB, 0x77C7, 0x77CD,
- 0x77D7, 0x77DA, 0x77DC, 0x77E3, 0x77EE, 0x77FC, 0x780C, 0x7812,
- 0x7926, 0x7820, 0x792A, 0x7845, 0x788E, 0x7874, 0x7886, 0x787C,
- 0x789A, 0x788C, 0x78A3, 0x78B5, 0x78AA, 0x78AF, 0x78D1, 0x78C6,
- 0x78CB, 0x78D4, 0x78BE, 0x78BC, 0x78C5, 0x78CA, 0x78EC,
-};
-static const unsigned short euc_to_utf8_E3[] = {
- 0x78E7, 0x78DA, 0x78FD, 0x78F4, 0x7907, 0x7912, 0x7911,
- 0x7919, 0x792C, 0x792B, 0x7940, 0x7960, 0x7957, 0x795F, 0x795A,
- 0x7955, 0x7953, 0x797A, 0x797F, 0x798A, 0x799D, 0x79A7, 0x9F4B,
- 0x79AA, 0x79AE, 0x79B3, 0x79B9, 0x79BA, 0x79C9, 0x79D5, 0x79E7,
- 0x79EC, 0x79E1, 0x79E3, 0x7A08, 0x7A0D, 0x7A18, 0x7A19, 0x7A20,
- 0x7A1F, 0x7980, 0x7A31, 0x7A3B, 0x7A3E, 0x7A37, 0x7A43, 0x7A57,
- 0x7A49, 0x7A61, 0x7A62, 0x7A69, 0x9F9D, 0x7A70, 0x7A79, 0x7A7D,
- 0x7A88, 0x7A97, 0x7A95, 0x7A98, 0x7A96, 0x7AA9, 0x7AC8, 0x7AB0,
- 0x7AB6, 0x7AC5, 0x7AC4, 0x7ABF, 0x9083, 0x7AC7, 0x7ACA, 0x7ACD,
- 0x7ACF, 0x7AD5, 0x7AD3, 0x7AD9, 0x7ADA, 0x7ADD, 0x7AE1, 0x7AE2,
- 0x7AE6, 0x7AED, 0x7AF0, 0x7B02, 0x7B0F, 0x7B0A, 0x7B06, 0x7B33,
- 0x7B18, 0x7B19, 0x7B1E, 0x7B35, 0x7B28, 0x7B36, 0x7B50,
-};
-static const unsigned short euc_to_utf8_E4[] = {
- 0x7B7A, 0x7B04, 0x7B4D, 0x7B0B, 0x7B4C, 0x7B45, 0x7B75,
- 0x7B65, 0x7B74, 0x7B67, 0x7B70, 0x7B71, 0x7B6C, 0x7B6E, 0x7B9D,
- 0x7B98, 0x7B9F, 0x7B8D, 0x7B9C, 0x7B9A, 0x7B8B, 0x7B92, 0x7B8F,
- 0x7B5D, 0x7B99, 0x7BCB, 0x7BC1, 0x7BCC, 0x7BCF, 0x7BB4, 0x7BC6,
- 0x7BDD, 0x7BE9, 0x7C11, 0x7C14, 0x7BE6, 0x7BE5, 0x7C60, 0x7C00,
- 0x7C07, 0x7C13, 0x7BF3, 0x7BF7, 0x7C17, 0x7C0D, 0x7BF6, 0x7C23,
- 0x7C27, 0x7C2A, 0x7C1F, 0x7C37, 0x7C2B, 0x7C3D, 0x7C4C, 0x7C43,
- 0x7C54, 0x7C4F, 0x7C40, 0x7C50, 0x7C58, 0x7C5F, 0x7C64, 0x7C56,
- 0x7C65, 0x7C6C, 0x7C75, 0x7C83, 0x7C90, 0x7CA4, 0x7CAD, 0x7CA2,
- 0x7CAB, 0x7CA1, 0x7CA8, 0x7CB3, 0x7CB2, 0x7CB1, 0x7CAE, 0x7CB9,
- 0x7CBD, 0x7CC0, 0x7CC5, 0x7CC2, 0x7CD8, 0x7CD2, 0x7CDC, 0x7CE2,
- 0x9B3B, 0x7CEF, 0x7CF2, 0x7CF4, 0x7CF6, 0x7CFA, 0x7D06,
-};
-static const unsigned short euc_to_utf8_E5[] = {
- 0x7D02, 0x7D1C, 0x7D15, 0x7D0A, 0x7D45, 0x7D4B, 0x7D2E,
- 0x7D32, 0x7D3F, 0x7D35, 0x7D46, 0x7D73, 0x7D56, 0x7D4E, 0x7D72,
- 0x7D68, 0x7D6E, 0x7D4F, 0x7D63, 0x7D93, 0x7D89, 0x7D5B, 0x7D8F,
- 0x7D7D, 0x7D9B, 0x7DBA, 0x7DAE, 0x7DA3, 0x7DB5, 0x7DC7, 0x7DBD,
- 0x7DAB, 0x7E3D, 0x7DA2, 0x7DAF, 0x7DDC, 0x7DB8, 0x7D9F, 0x7DB0,
- 0x7DD8, 0x7DDD, 0x7DE4, 0x7DDE, 0x7DFB, 0x7DF2, 0x7DE1, 0x7E05,
- 0x7E0A, 0x7E23, 0x7E21, 0x7E12, 0x7E31, 0x7E1F, 0x7E09, 0x7E0B,
- 0x7E22, 0x7E46, 0x7E66, 0x7E3B, 0x7E35, 0x7E39, 0x7E43, 0x7E37,
- 0x7E32, 0x7E3A, 0x7E67, 0x7E5D, 0x7E56, 0x7E5E, 0x7E59, 0x7E5A,
- 0x7E79, 0x7E6A, 0x7E69, 0x7E7C, 0x7E7B, 0x7E83, 0x7DD5, 0x7E7D,
- 0x8FAE, 0x7E7F, 0x7E88, 0x7E89, 0x7E8C, 0x7E92, 0x7E90, 0x7E93,
- 0x7E94, 0x7E96, 0x7E8E, 0x7E9B, 0x7E9C, 0x7F38, 0x7F3A,
-};
-static const unsigned short euc_to_utf8_E6[] = {
- 0x7F45, 0x7F4C, 0x7F4D, 0x7F4E, 0x7F50, 0x7F51, 0x7F55,
- 0x7F54, 0x7F58, 0x7F5F, 0x7F60, 0x7F68, 0x7F69, 0x7F67, 0x7F78,
- 0x7F82, 0x7F86, 0x7F83, 0x7F88, 0x7F87, 0x7F8C, 0x7F94, 0x7F9E,
- 0x7F9D, 0x7F9A, 0x7FA3, 0x7FAF, 0x7FB2, 0x7FB9, 0x7FAE, 0x7FB6,
- 0x7FB8, 0x8B71, 0x7FC5, 0x7FC6, 0x7FCA, 0x7FD5, 0x7FD4, 0x7FE1,
- 0x7FE6, 0x7FE9, 0x7FF3, 0x7FF9, 0x98DC, 0x8006, 0x8004, 0x800B,
- 0x8012, 0x8018, 0x8019, 0x801C, 0x8021, 0x8028, 0x803F, 0x803B,
- 0x804A, 0x8046, 0x8052, 0x8058, 0x805A, 0x805F, 0x8062, 0x8068,
- 0x8073, 0x8072, 0x8070, 0x8076, 0x8079, 0x807D, 0x807F, 0x8084,
- 0x8086, 0x8085, 0x809B, 0x8093, 0x809A, 0x80AD, 0x5190, 0x80AC,
- 0x80DB, 0x80E5, 0x80D9, 0x80DD, 0x80C4, 0x80DA, 0x80D6, 0x8109,
- 0x80EF, 0x80F1, 0x811B, 0x8129, 0x8123, 0x812F, 0x814B,
-};
-static const unsigned short euc_to_utf8_E7[] = {
- 0x968B, 0x8146, 0x813E, 0x8153, 0x8151, 0x80FC, 0x8171,
- 0x816E, 0x8165, 0x8166, 0x8174, 0x8183, 0x8188, 0x818A, 0x8180,
- 0x8182, 0x81A0, 0x8195, 0x81A4, 0x81A3, 0x815F, 0x8193, 0x81A9,
- 0x81B0, 0x81B5, 0x81BE, 0x81B8, 0x81BD, 0x81C0, 0x81C2, 0x81BA,
- 0x81C9, 0x81CD, 0x81D1, 0x81D9, 0x81D8, 0x81C8, 0x81DA, 0x81DF,
- 0x81E0, 0x81E7, 0x81FA, 0x81FB, 0x81FE, 0x8201, 0x8202, 0x8205,
- 0x8207, 0x820A, 0x820D, 0x8210, 0x8216, 0x8229, 0x822B, 0x8238,
- 0x8233, 0x8240, 0x8259, 0x8258, 0x825D, 0x825A, 0x825F, 0x8264,
- 0x8262, 0x8268, 0x826A, 0x826B, 0x822E, 0x8271, 0x8277, 0x8278,
- 0x827E, 0x828D, 0x8292, 0x82AB, 0x829F, 0x82BB, 0x82AC, 0x82E1,
- 0x82E3, 0x82DF, 0x82D2, 0x82F4, 0x82F3, 0x82FA, 0x8393, 0x8303,
- 0x82FB, 0x82F9, 0x82DE, 0x8306, 0x82DC, 0x8309, 0x82D9,
-};
-static const unsigned short euc_to_utf8_E8[] = {
- 0x8335, 0x8334, 0x8316, 0x8332, 0x8331, 0x8340, 0x8339,
- 0x8350, 0x8345, 0x832F, 0x832B, 0x8317, 0x8318, 0x8385, 0x839A,
- 0x83AA, 0x839F, 0x83A2, 0x8396, 0x8323, 0x838E, 0x8387, 0x838A,
- 0x837C, 0x83B5, 0x8373, 0x8375, 0x83A0, 0x8389, 0x83A8, 0x83F4,
- 0x8413, 0x83EB, 0x83CE, 0x83FD, 0x8403, 0x83D8, 0x840B, 0x83C1,
- 0x83F7, 0x8407, 0x83E0, 0x83F2, 0x840D, 0x8422, 0x8420, 0x83BD,
- 0x8438, 0x8506, 0x83FB, 0x846D, 0x842A, 0x843C, 0x855A, 0x8484,
- 0x8477, 0x846B, 0x84AD, 0x846E, 0x8482, 0x8469, 0x8446, 0x842C,
- 0x846F, 0x8479, 0x8435, 0x84CA, 0x8462, 0x84B9, 0x84BF, 0x849F,
- 0x84D9, 0x84CD, 0x84BB, 0x84DA, 0x84D0, 0x84C1, 0x84C6, 0x84D6,
- 0x84A1, 0x8521, 0x84FF, 0x84F4, 0x8517, 0x8518, 0x852C, 0x851F,
- 0x8515, 0x8514, 0x84FC, 0x8540, 0x8563, 0x8558, 0x8548,
-};
-static const unsigned short euc_to_utf8_E9[] = {
- 0x8541, 0x8602, 0x854B, 0x8555, 0x8580, 0x85A4, 0x8588,
- 0x8591, 0x858A, 0x85A8, 0x856D, 0x8594, 0x859B, 0x85EA, 0x8587,
- 0x859C, 0x8577, 0x857E, 0x8590, 0x85C9, 0x85BA, 0x85CF, 0x85B9,
- 0x85D0, 0x85D5, 0x85DD, 0x85E5, 0x85DC, 0x85F9, 0x860A, 0x8613,
- 0x860B, 0x85FE, 0x85FA, 0x8606, 0x8622, 0x861A, 0x8630, 0x863F,
- 0x864D, 0x4E55, 0x8654, 0x865F, 0x8667, 0x8671, 0x8693, 0x86A3,
- 0x86A9, 0x86AA, 0x868B, 0x868C, 0x86B6, 0x86AF, 0x86C4, 0x86C6,
- 0x86B0, 0x86C9, 0x8823, 0x86AB, 0x86D4, 0x86DE, 0x86E9, 0x86EC,
- 0x86DF, 0x86DB, 0x86EF, 0x8712, 0x8706, 0x8708, 0x8700, 0x8703,
- 0x86FB, 0x8711, 0x8709, 0x870D, 0x86F9, 0x870A, 0x8734, 0x873F,
- 0x8737, 0x873B, 0x8725, 0x8729, 0x871A, 0x8760, 0x875F, 0x8778,
- 0x874C, 0x874E, 0x8774, 0x8757, 0x8768, 0x876E, 0x8759,
-};
-static const unsigned short euc_to_utf8_EA[] = {
- 0x8753, 0x8763, 0x876A, 0x8805, 0x87A2, 0x879F, 0x8782,
- 0x87AF, 0x87CB, 0x87BD, 0x87C0, 0x87D0, 0x96D6, 0x87AB, 0x87C4,
- 0x87B3, 0x87C7, 0x87C6, 0x87BB, 0x87EF, 0x87F2, 0x87E0, 0x880F,
- 0x880D, 0x87FE, 0x87F6, 0x87F7, 0x880E, 0x87D2, 0x8811, 0x8816,
- 0x8815, 0x8822, 0x8821, 0x8831, 0x8836, 0x8839, 0x8827, 0x883B,
- 0x8844, 0x8842, 0x8852, 0x8859, 0x885E, 0x8862, 0x886B, 0x8881,
- 0x887E, 0x889E, 0x8875, 0x887D, 0x88B5, 0x8872, 0x8882, 0x8897,
- 0x8892, 0x88AE, 0x8899, 0x88A2, 0x888D, 0x88A4, 0x88B0, 0x88BF,
- 0x88B1, 0x88C3, 0x88C4, 0x88D4, 0x88D8, 0x88D9, 0x88DD, 0x88F9,
- 0x8902, 0x88FC, 0x88F4, 0x88E8, 0x88F2, 0x8904, 0x890C, 0x890A,
- 0x8913, 0x8943, 0x891E, 0x8925, 0x892A, 0x892B, 0x8941, 0x8944,
- 0x893B, 0x8936, 0x8938, 0x894C, 0x891D, 0x8960, 0x895E,
-};
-static const unsigned short euc_to_utf8_EB[] = {
- 0x8966, 0x8964, 0x896D, 0x896A, 0x896F, 0x8974, 0x8977,
- 0x897E, 0x8983, 0x8988, 0x898A, 0x8993, 0x8998, 0x89A1, 0x89A9,
- 0x89A6, 0x89AC, 0x89AF, 0x89B2, 0x89BA, 0x89BD, 0x89BF, 0x89C0,
- 0x89DA, 0x89DC, 0x89DD, 0x89E7, 0x89F4, 0x89F8, 0x8A03, 0x8A16,
- 0x8A10, 0x8A0C, 0x8A1B, 0x8A1D, 0x8A25, 0x8A36, 0x8A41, 0x8A5B,
- 0x8A52, 0x8A46, 0x8A48, 0x8A7C, 0x8A6D, 0x8A6C, 0x8A62, 0x8A85,
- 0x8A82, 0x8A84, 0x8AA8, 0x8AA1, 0x8A91, 0x8AA5, 0x8AA6, 0x8A9A,
- 0x8AA3, 0x8AC4, 0x8ACD, 0x8AC2, 0x8ADA, 0x8AEB, 0x8AF3, 0x8AE7,
- 0x8AE4, 0x8AF1, 0x8B14, 0x8AE0, 0x8AE2, 0x8AF7, 0x8ADE, 0x8ADB,
- 0x8B0C, 0x8B07, 0x8B1A, 0x8AE1, 0x8B16, 0x8B10, 0x8B17, 0x8B20,
- 0x8B33, 0x97AB, 0x8B26, 0x8B2B, 0x8B3E, 0x8B28, 0x8B41, 0x8B4C,
- 0x8B4F, 0x8B4E, 0x8B49, 0x8B56, 0x8B5B, 0x8B5A, 0x8B6B,
-};
-static const unsigned short euc_to_utf8_EC[] = {
- 0x8B5F, 0x8B6C, 0x8B6F, 0x8B74, 0x8B7D, 0x8B80, 0x8B8C,
- 0x8B8E, 0x8B92, 0x8B93, 0x8B96, 0x8B99, 0x8B9A, 0x8C3A, 0x8C41,
- 0x8C3F, 0x8C48, 0x8C4C, 0x8C4E, 0x8C50, 0x8C55, 0x8C62, 0x8C6C,
- 0x8C78, 0x8C7A, 0x8C82, 0x8C89, 0x8C85, 0x8C8A, 0x8C8D, 0x8C8E,
- 0x8C94, 0x8C7C, 0x8C98, 0x621D, 0x8CAD, 0x8CAA, 0x8CBD, 0x8CB2,
- 0x8CB3, 0x8CAE, 0x8CB6, 0x8CC8, 0x8CC1, 0x8CE4, 0x8CE3, 0x8CDA,
- 0x8CFD, 0x8CFA, 0x8CFB, 0x8D04, 0x8D05, 0x8D0A, 0x8D07, 0x8D0F,
- 0x8D0D, 0x8D10, 0x9F4E, 0x8D13, 0x8CCD, 0x8D14, 0x8D16, 0x8D67,
- 0x8D6D, 0x8D71, 0x8D73, 0x8D81, 0x8D99, 0x8DC2, 0x8DBE, 0x8DBA,
- 0x8DCF, 0x8DDA, 0x8DD6, 0x8DCC, 0x8DDB, 0x8DCB, 0x8DEA, 0x8DEB,
- 0x8DDF, 0x8DE3, 0x8DFC, 0x8E08, 0x8E09, 0x8DFF, 0x8E1D, 0x8E1E,
- 0x8E10, 0x8E1F, 0x8E42, 0x8E35, 0x8E30, 0x8E34, 0x8E4A,
-};
-static const unsigned short euc_to_utf8_ED[] = {
- 0x8E47, 0x8E49, 0x8E4C, 0x8E50, 0x8E48, 0x8E59, 0x8E64,
- 0x8E60, 0x8E2A, 0x8E63, 0x8E55, 0x8E76, 0x8E72, 0x8E7C, 0x8E81,
- 0x8E87, 0x8E85, 0x8E84, 0x8E8B, 0x8E8A, 0x8E93, 0x8E91, 0x8E94,
- 0x8E99, 0x8EAA, 0x8EA1, 0x8EAC, 0x8EB0, 0x8EC6, 0x8EB1, 0x8EBE,
- 0x8EC5, 0x8EC8, 0x8ECB, 0x8EDB, 0x8EE3, 0x8EFC, 0x8EFB, 0x8EEB,
- 0x8EFE, 0x8F0A, 0x8F05, 0x8F15, 0x8F12, 0x8F19, 0x8F13, 0x8F1C,
- 0x8F1F, 0x8F1B, 0x8F0C, 0x8F26, 0x8F33, 0x8F3B, 0x8F39, 0x8F45,
- 0x8F42, 0x8F3E, 0x8F4C, 0x8F49, 0x8F46, 0x8F4E, 0x8F57, 0x8F5C,
- 0x8F62, 0x8F63, 0x8F64, 0x8F9C, 0x8F9F, 0x8FA3, 0x8FAD, 0x8FAF,
- 0x8FB7, 0x8FDA, 0x8FE5, 0x8FE2, 0x8FEA, 0x8FEF, 0x9087, 0x8FF4,
- 0x9005, 0x8FF9, 0x8FFA, 0x9011, 0x9015, 0x9021, 0x900D, 0x901E,
- 0x9016, 0x900B, 0x9027, 0x9036, 0x9035, 0x9039, 0x8FF8,
-};
-static const unsigned short euc_to_utf8_EE[] = {
- 0x904F, 0x9050, 0x9051, 0x9052, 0x900E, 0x9049, 0x903E,
- 0x9056, 0x9058, 0x905E, 0x9068, 0x906F, 0x9076, 0x96A8, 0x9072,
- 0x9082, 0x907D, 0x9081, 0x9080, 0x908A, 0x9089, 0x908F, 0x90A8,
- 0x90AF, 0x90B1, 0x90B5, 0x90E2, 0x90E4, 0x6248, 0x90DB, 0x9102,
- 0x9112, 0x9119, 0x9132, 0x9130, 0x914A, 0x9156, 0x9158, 0x9163,
- 0x9165, 0x9169, 0x9173, 0x9172, 0x918B, 0x9189, 0x9182, 0x91A2,
- 0x91AB, 0x91AF, 0x91AA, 0x91B5, 0x91B4, 0x91BA, 0x91C0, 0x91C1,
- 0x91C9, 0x91CB, 0x91D0, 0x91D6, 0x91DF, 0x91E1, 0x91DB, 0x91FC,
- 0x91F5, 0x91F6, 0x921E, 0x91FF, 0x9214, 0x922C, 0x9215, 0x9211,
- 0x925E, 0x9257, 0x9245, 0x9249, 0x9264, 0x9248, 0x9295, 0x923F,
- 0x924B, 0x9250, 0x929C, 0x9296, 0x9293, 0x929B, 0x925A, 0x92CF,
- 0x92B9, 0x92B7, 0x92E9, 0x930F, 0x92FA, 0x9344, 0x932E,
-};
-static const unsigned short euc_to_utf8_EF[] = {
- 0x9319, 0x9322, 0x931A, 0x9323, 0x933A, 0x9335, 0x933B,
- 0x935C, 0x9360, 0x937C, 0x936E, 0x9356, 0x93B0, 0x93AC, 0x93AD,
- 0x9394, 0x93B9, 0x93D6, 0x93D7, 0x93E8, 0x93E5, 0x93D8, 0x93C3,
- 0x93DD, 0x93D0, 0x93C8, 0x93E4, 0x941A, 0x9414, 0x9413, 0x9403,
- 0x9407, 0x9410, 0x9436, 0x942B, 0x9435, 0x9421, 0x943A, 0x9441,
- 0x9452, 0x9444, 0x945B, 0x9460, 0x9462, 0x945E, 0x946A, 0x9229,
- 0x9470, 0x9475, 0x9477, 0x947D, 0x945A, 0x947C, 0x947E, 0x9481,
- 0x947F, 0x9582, 0x9587, 0x958A, 0x9594, 0x9596, 0x9598, 0x9599,
- 0x95A0, 0x95A8, 0x95A7, 0x95AD, 0x95BC, 0x95BB, 0x95B9, 0x95BE,
- 0x95CA, 0x6FF6, 0x95C3, 0x95CD, 0x95CC, 0x95D5, 0x95D4, 0x95D6,
- 0x95DC, 0x95E1, 0x95E5, 0x95E2, 0x9621, 0x9628, 0x962E, 0x962F,
- 0x9642, 0x964C, 0x964F, 0x964B, 0x9677, 0x965C, 0x965E,
-};
-static const unsigned short euc_to_utf8_F0[] = {
- 0x965D, 0x965F, 0x9666, 0x9672, 0x966C, 0x968D, 0x9698,
- 0x9695, 0x9697, 0x96AA, 0x96A7, 0x96B1, 0x96B2, 0x96B0, 0x96B4,
- 0x96B6, 0x96B8, 0x96B9, 0x96CE, 0x96CB, 0x96C9, 0x96CD, 0x894D,
- 0x96DC, 0x970D, 0x96D5, 0x96F9, 0x9704, 0x9706, 0x9708, 0x9713,
- 0x970E, 0x9711, 0x970F, 0x9716, 0x9719, 0x9724, 0x972A, 0x9730,
- 0x9739, 0x973D, 0x973E, 0x9744, 0x9746, 0x9748, 0x9742, 0x9749,
- 0x975C, 0x9760, 0x9764, 0x9766, 0x9768, 0x52D2, 0x976B, 0x9771,
- 0x9779, 0x9785, 0x977C, 0x9781, 0x977A, 0x9786, 0x978B, 0x978F,
- 0x9790, 0x979C, 0x97A8, 0x97A6, 0x97A3, 0x97B3, 0x97B4, 0x97C3,
- 0x97C6, 0x97C8, 0x97CB, 0x97DC, 0x97ED, 0x9F4F, 0x97F2, 0x7ADF,
- 0x97F6, 0x97F5, 0x980F, 0x980C, 0x9838, 0x9824, 0x9821, 0x9837,
- 0x983D, 0x9846, 0x984F, 0x984B, 0x986B, 0x986F, 0x9870,
-};
-static const unsigned short euc_to_utf8_F1[] = {
- 0x9871, 0x9874, 0x9873, 0x98AA, 0x98AF, 0x98B1, 0x98B6,
- 0x98C4, 0x98C3, 0x98C6, 0x98E9, 0x98EB, 0x9903, 0x9909, 0x9912,
- 0x9914, 0x9918, 0x9921, 0x991D, 0x991E, 0x9924, 0x9920, 0x992C,
- 0x992E, 0x993D, 0x993E, 0x9942, 0x9949, 0x9945, 0x9950, 0x994B,
- 0x9951, 0x9952, 0x994C, 0x9955, 0x9997, 0x9998, 0x99A5, 0x99AD,
- 0x99AE, 0x99BC, 0x99DF, 0x99DB, 0x99DD, 0x99D8, 0x99D1, 0x99ED,
- 0x99EE, 0x99F1, 0x99F2, 0x99FB, 0x99F8, 0x9A01, 0x9A0F, 0x9A05,
- 0x99E2, 0x9A19, 0x9A2B, 0x9A37, 0x9A45, 0x9A42, 0x9A40, 0x9A43,
- 0x9A3E, 0x9A55, 0x9A4D, 0x9A5B, 0x9A57, 0x9A5F, 0x9A62, 0x9A65,
- 0x9A64, 0x9A69, 0x9A6B, 0x9A6A, 0x9AAD, 0x9AB0, 0x9ABC, 0x9AC0,
- 0x9ACF, 0x9AD1, 0x9AD3, 0x9AD4, 0x9ADE, 0x9ADF, 0x9AE2, 0x9AE3,
- 0x9AE6, 0x9AEF, 0x9AEB, 0x9AEE, 0x9AF4, 0x9AF1, 0x9AF7,
-};
-static const unsigned short euc_to_utf8_F2[] = {
- 0x9AFB, 0x9B06, 0x9B18, 0x9B1A, 0x9B1F, 0x9B22, 0x9B23,
- 0x9B25, 0x9B27, 0x9B28, 0x9B29, 0x9B2A, 0x9B2E, 0x9B2F, 0x9B32,
- 0x9B44, 0x9B43, 0x9B4F, 0x9B4D, 0x9B4E, 0x9B51, 0x9B58, 0x9B74,
- 0x9B93, 0x9B83, 0x9B91, 0x9B96, 0x9B97, 0x9B9F, 0x9BA0, 0x9BA8,
- 0x9BB4, 0x9BC0, 0x9BCA, 0x9BB9, 0x9BC6, 0x9BCF, 0x9BD1, 0x9BD2,
- 0x9BE3, 0x9BE2, 0x9BE4, 0x9BD4, 0x9BE1, 0x9C3A, 0x9BF2, 0x9BF1,
- 0x9BF0, 0x9C15, 0x9C14, 0x9C09, 0x9C13, 0x9C0C, 0x9C06, 0x9C08,
- 0x9C12, 0x9C0A, 0x9C04, 0x9C2E, 0x9C1B, 0x9C25, 0x9C24, 0x9C21,
- 0x9C30, 0x9C47, 0x9C32, 0x9C46, 0x9C3E, 0x9C5A, 0x9C60, 0x9C67,
- 0x9C76, 0x9C78, 0x9CE7, 0x9CEC, 0x9CF0, 0x9D09, 0x9D08, 0x9CEB,
- 0x9D03, 0x9D06, 0x9D2A, 0x9D26, 0x9DAF, 0x9D23, 0x9D1F, 0x9D44,
- 0x9D15, 0x9D12, 0x9D41, 0x9D3F, 0x9D3E, 0x9D46, 0x9D48,
-};
-static const unsigned short euc_to_utf8_F3[] = {
- 0x9D5D, 0x9D5E, 0x9D64, 0x9D51, 0x9D50, 0x9D59, 0x9D72,
- 0x9D89, 0x9D87, 0x9DAB, 0x9D6F, 0x9D7A, 0x9D9A, 0x9DA4, 0x9DA9,
- 0x9DB2, 0x9DC4, 0x9DC1, 0x9DBB, 0x9DB8, 0x9DBA, 0x9DC6, 0x9DCF,
- 0x9DC2, 0x9DD9, 0x9DD3, 0x9DF8, 0x9DE6, 0x9DED, 0x9DEF, 0x9DFD,
- 0x9E1A, 0x9E1B, 0x9E1E, 0x9E75, 0x9E79, 0x9E7D, 0x9E81, 0x9E88,
- 0x9E8B, 0x9E8C, 0x9E92, 0x9E95, 0x9E91, 0x9E9D, 0x9EA5, 0x9EA9,
- 0x9EB8, 0x9EAA, 0x9EAD, 0x9761, 0x9ECC, 0x9ECE, 0x9ECF, 0x9ED0,
- 0x9ED4, 0x9EDC, 0x9EDE, 0x9EDD, 0x9EE0, 0x9EE5, 0x9EE8, 0x9EEF,
- 0x9EF4, 0x9EF6, 0x9EF7, 0x9EF9, 0x9EFB, 0x9EFC, 0x9EFD, 0x9F07,
- 0x9F08, 0x76B7, 0x9F15, 0x9F21, 0x9F2C, 0x9F3E, 0x9F4A, 0x9F52,
- 0x9F54, 0x9F63, 0x9F5F, 0x9F60, 0x9F61, 0x9F66, 0x9F67, 0x9F6C,
- 0x9F6A, 0x9F77, 0x9F72, 0x9F76, 0x9F95, 0x9F9C, 0x9FA0,
-};
-static const unsigned short euc_to_utf8_F4[] = {
- 0x582F, 0x69C7, 0x9059, 0x7464, 0x51DC, 0x7199, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short euc_to_utf8_F4_x0213[] = {
- 0x582F, 0x69C7, 0x9059, 0x7464, 0x51DC, 0x7199, 0x5653,
- 0x5DE2, 0x5E14, 0x5E18, 0x5E58, 0x5E5E, 0x5EBE, 0xF928, 0x5ECB,
- 0x5EF9, 0x5F00, 0x5F02, 0x5F07, 0x5F1D, 0x5F23, 0x5F34, 0x5F36,
- 0x5F3D, 0x5F40, 0x5F45, 0x5F54, 0x5F58, 0x5F64, 0x5F67, 0x5F7D,
- 0x5F89, 0x5F9C, 0x5FA7, 0x5FAF, 0x5FB5, 0x5FB7, 0x5FC9, 0x5FDE,
- 0x5FE1, 0x5FE9, 0x600D, 0x6014, 0x6018, 0x6033, 0x6035, 0x6047,
- 0xFA3D, 0x609D, 0x609E, 0x60CB, 0x60D4, 0x60D5, 0x60DD, 0x60F8,
- 0x611C, 0x612B, 0x6130, 0x6137, 0xFA3E, 0x618D, 0xFA3F, 0x61BC,
- 0x61B9, 0xFA40, 0x6222, 0x623E, 0x6243, 0x6256, 0x625A, 0x626F,
- 0x6285, 0x62C4, 0x62D6, 0x62FC, 0x630A, 0x6318, 0x6339, 0x6343,
- 0x6365, 0x637C, 0x63E5, 0x63ED, 0x63F5, 0x6410, 0x6414, 0x6422,
- 0x6479, 0x6451, 0x6460, 0x646D, 0x64CE, 0x64BE, 0x64BF,
-};
-static const unsigned short euc_to_utf8_F5[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xFE33, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xFE31, 0, 0,
- 0, 0, 0, 0, 0, 0xFE30, 0, 0,
- 0, 0, 0xFE35, 0xFE36, 0xFE39, 0xFE3A, 0, 0,
- 0xFE37, 0xFE38, 0xFE3F, 0xFE40, 0xFE3D, 0xFE3E, 0xFE41, 0xFE42,
- 0xFE43, 0xFE44, 0xFE3B, 0xFE3C, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short euc_to_utf8_F5_x0213[] = {
- 0x64C4, 0x64CA, 0x64D0, 0x64F7, 0x64FB, 0x6522, 0x6529,
- 0xFA41, 0x6567, 0x659D, 0xFA42, 0x6600, 0x6609, 0x6615, 0x661E,
- 0x663A, 0x6622, 0x6624, 0x662B, 0x6630, 0x6631, 0x6633, 0x66FB,
- 0x6648, 0x664C, 0xD84C /*0xDDC4*/, 0x6659, 0x665A, 0x6661, 0x6665, 0x6673,
- 0x6677, 0x6678, 0x668D, 0xFA43, 0x66A0, 0x66B2, 0x66BB, 0x66C6,
- 0x66C8, 0x3B22, 0x66DB, 0x66E8, 0x66FA, 0x6713, 0xF929, 0x6733,
- 0x6766, 0x6747, 0x6748, 0x677B, 0x6781, 0x6793, 0x6798, 0x679B,
- 0x67BB, 0x67F9, 0x67C0, 0x67D7, 0x67FC, 0x6801, 0x6852, 0x681D,
- 0x682C, 0x6831, 0x685B, 0x6872, 0x6875, 0xFA44, 0x68A3, 0x68A5,
- 0x68B2, 0x68C8, 0x68D0, 0x68E8, 0x68ED, 0x68F0, 0x68F1, 0x68FC,
- 0x690A, 0x6949, 0xD84D /*0xDDC4*/, 0x6935, 0x6942, 0x6957, 0x6963, 0x6964,
- 0x6968, 0x6980, 0xFA14, 0x69A5, 0x69AD, 0x69CF, 0x3BB6,
-};
-static const unsigned short euc_to_utf8_F6_x0213[] = {
- 0x3BC3, 0x69E2, 0x69E9, 0x69EA, 0x69F5, 0x69F6, 0x6A0F,
- 0x6A15, 0xD84D /*0xDF3F*/, 0x6A3B, 0x6A3E, 0x6A45, 0x6A50, 0x6A56, 0x6A5B,
- 0x6A6B, 0x6A73, 0xD84D /*0xDF63*/, 0x6A89, 0x6A94, 0x6A9D, 0x6A9E, 0x6AA5,
- 0x6AE4, 0x6AE7, 0x3C0F, 0xF91D, 0x6B1B, 0x6B1E, 0x6B2C, 0x6B35,
- 0x6B46, 0x6B56, 0x6B60, 0x6B65, 0x6B67, 0x6B77, 0x6B82, 0x6BA9,
- 0x6BAD, 0xF970, 0x6BCF, 0x6BD6, 0x6BD7, 0x6BFF, 0x6C05, 0x6C10,
- 0x6C33, 0x6C59, 0x6C5C, 0x6CAA, 0x6C74, 0x6C76, 0x6C85, 0x6C86,
- 0x6C98, 0x6C9C, 0x6CFB, 0x6CC6, 0x6CD4, 0x6CE0, 0x6CEB, 0x6CEE,
- 0xD84F /*0xDCFE*/, 0x6D04, 0x6D0E, 0x6D2E, 0x6D31, 0x6D39, 0x6D3F, 0x6D58,
- 0x6D65, 0xFA45, 0x6D82, 0x6D87, 0x6D89, 0x6D94, 0x6DAA, 0x6DAC,
- 0x6DBF, 0x6DC4, 0x6DD6, 0x6DDA, 0x6DDB, 0x6DDD, 0x6DFC, 0xFA46,
- 0x6E34, 0x6E44, 0x6E5C, 0x6E5E, 0x6EAB, 0x6EB1, 0x6EC1,
-};
-static const unsigned short euc_to_utf8_F7_x0213[] = {
- 0x6EC7, 0x6ECE, 0x6F10, 0x6F1A, 0xFA47, 0x6F2A, 0x6F2F,
- 0x6F33, 0x6F51, 0x6F59, 0x6F5E, 0x6F61, 0x6F62, 0x6F7E, 0x6F88,
- 0x6F8C, 0x6F8D, 0x6F94, 0x6FA0, 0x6FA7, 0x6FB6, 0x6FBC, 0x6FC7,
- 0x6FCA, 0x6FF9, 0x6FF0, 0x6FF5, 0x7005, 0x7006, 0x7028, 0x704A,
- 0x705D, 0x705E, 0x704E, 0x7064, 0x7075, 0x7085, 0x70A4, 0x70AB,
- 0x70B7, 0x70D4, 0x70D8, 0x70E4, 0x710F, 0x712B, 0x711E, 0x7120,
- 0x712E, 0x7130, 0x7146, 0x7147, 0x7151, 0xFA48, 0x7152, 0x715C,
- 0x7160, 0x7168, 0xFA15, 0x7185, 0x7187, 0x7192, 0x71C1, 0x71BA,
- 0x71C4, 0x71FE, 0x7200, 0x7215, 0x7255, 0x7256, 0x3E3F, 0x728D,
- 0x729B, 0x72BE, 0x72C0, 0x72FB, 0xD851 /*0xDFF1*/, 0x7327, 0x7328, 0xFA16,
- 0x7350, 0x7366, 0x737C, 0x7395, 0x739F, 0x73A0, 0x73A2, 0x73A6,
- 0x73AB, 0x73C9, 0x73CF, 0x73D6, 0x73D9, 0x73E3, 0x73E9,
-};
-static const unsigned short euc_to_utf8_F8_x0213[] = {
- 0x7407, 0x740A, 0x741A, 0x741B, 0xFA4A, 0x7426, 0x7428,
- 0x742A, 0x742B, 0x742C, 0x742E, 0x742F, 0x7430, 0x7444, 0x7446,
- 0x7447, 0x744B, 0x7457, 0x7462, 0x746B, 0x746D, 0x7486, 0x7487,
- 0x7489, 0x7498, 0x749C, 0x749F, 0x74A3, 0x7490, 0x74A6, 0x74A8,
- 0x74A9, 0x74B5, 0x74BF, 0x74C8, 0x74C9, 0x74DA, 0x74FF, 0x7501,
- 0x7517, 0x752F, 0x756F, 0x7579, 0x7592, 0x3F72, 0x75CE, 0x75E4,
- 0x7600, 0x7602, 0x7608, 0x7615, 0x7616, 0x7619, 0x761E, 0x762D,
- 0x7635, 0x7643, 0x764B, 0x7664, 0x7665, 0x766D, 0x766F, 0x7671,
- 0x7681, 0x769B, 0x769D, 0x769E, 0x76A6, 0x76AA, 0x76B6, 0x76C5,
- 0x76CC, 0x76CE, 0x76D4, 0x76E6, 0x76F1, 0x76FC, 0x770A, 0x7719,
- 0x7734, 0x7736, 0x7746, 0x774D, 0x774E, 0x775C, 0x775F, 0x7762,
- 0x777A, 0x7780, 0x7794, 0x77AA, 0x77E0, 0x782D, 0xD855 /*0xDC8E*/,
-};
-static const unsigned short euc_to_utf8_F9[] = {
- 0x7E8A, 0x891C, 0x9348, 0x9288, 0x84DC, 0x4FC9, 0x70BB,
- 0x6631, 0x68C8, 0x92F9, 0x66FB, 0x5F45, 0x4E28, 0x4EE1, 0x4EFC,
- 0x4F00, 0x4F03, 0x4F39, 0x4F56, 0x4F92, 0x4F8A, 0x4F9A, 0x4F94,
- 0x4FCD, 0x5040, 0x5022, 0x4FFF, 0x501E, 0x5046, 0x5070, 0x5042,
- 0x5094, 0x50F4, 0x50D8, 0x514A, 0x5164, 0x519D, 0x51BE, 0x51EC,
- 0x5215, 0x529C, 0x52A6, 0x52C0, 0x52DB, 0x5300, 0x5307, 0x5324,
- 0x5372, 0x5393, 0x53B2, 0x53DD, 0xFA0E, 0x549C, 0x548A, 0x54A9,
- 0x54FF, 0x5586, 0x5759, 0x5765, 0x57AC, 0x57C8, 0x57C7, 0xFA0F,
- 0xFA10, 0x589E, 0x58B2, 0x590B, 0x5953, 0x595B, 0x595D, 0x5963,
- 0x59A4, 0x59BA, 0x5B56, 0x5BC0, 0x752F, 0x5BD8, 0x5BEC, 0x5C1E,
- 0x5CA6, 0x5CBA, 0x5CF5, 0x5D27, 0x5D53, 0xFA11, 0x5D42, 0x5D6D,
- 0x5DB8, 0x5DB9, 0x5DD0, 0x5F21, 0x5F34, 0x5F67, 0x5FB7,
-};
-static const unsigned short euc_to_utf8_F9_x0213[] = {
- 0x7843, 0x784E, 0x784F, 0x7851, 0x7868, 0x786E, 0xFA4B,
- 0x78B0, 0xD855 /*0xDD0E*/, 0x78AD, 0x78E4, 0x78F2, 0x7900, 0x78F7, 0x791C,
- 0x792E, 0x7931, 0x7934, 0xFA4C, 0xFA4D, 0x7945, 0x7946, 0xFA4E,
- 0xFA4F, 0xFA50, 0x795C, 0xFA51, 0xFA19, 0xFA1A, 0x7979, 0xFA52,
- 0xFA53, 0xFA1B, 0x7998, 0x79B1, 0x79B8, 0x79C8, 0x79CA, 0xD855 /*0xDF71*/,
- 0x79D4, 0x79DE, 0x79EB, 0x79ED, 0x7A03, 0xFA54, 0x7A39, 0x7A5D,
- 0x7A6D, 0xFA55, 0x7A85, 0x7AA0, 0xD856 /*0xDDC4*/, 0x7AB3, 0x7ABB, 0x7ACE,
- 0x7AEB, 0x7AFD, 0x7B12, 0x7B2D, 0x7B3B, 0x7B47, 0x7B4E, 0x7B60,
- 0x7B6D, 0x7B6F, 0x7B72, 0x7B9E, 0xFA56, 0x7BD7, 0x7BD9, 0x7C01,
- 0x7C31, 0x7C1E, 0x7C20, 0x7C33, 0x7C36, 0x4264, 0xD857 /*0xDDA1*/, 0x7C59,
- 0x7C6D, 0x7C79, 0x7C8F, 0x7C94, 0x7CA0, 0x7CBC, 0x7CD5, 0x7CD9,
- 0x7CDD, 0x7D07, 0x7D08, 0x7D13, 0x7D1D, 0x7D23, 0x7D31,
-};
-static const unsigned short euc_to_utf8_FA[] = {
- 0x5FDE, 0x605D, 0x6085, 0x608A, 0x60DE, 0x60D5, 0x6120,
- 0x60F2, 0x6111, 0x6137, 0x6130, 0x6198, 0x6213, 0x62A6, 0x63F5,
- 0x6460, 0x649D, 0x64CE, 0x654E, 0x6600, 0x6615, 0x663B, 0x6609,
- 0x662E, 0x661E, 0x6624, 0x6665, 0x6657, 0x6659, 0xFA12, 0x6673,
- 0x6699, 0x66A0, 0x66B2, 0x66BF, 0x66FA, 0x670E, 0xF929, 0x6766,
- 0x67BB, 0x6852, 0x67C0, 0x6801, 0x6844, 0x68CF, 0xFA13, 0x6968,
- 0xFA14, 0x6998, 0x69E2, 0x6A30, 0x6A6B, 0x6A46, 0x6A73, 0x6A7E,
- 0x6AE2, 0x6AE4, 0x6BD6, 0x6C3F, 0x6C5C, 0x6C86, 0x6C6F, 0x6CDA,
- 0x6D04, 0x6D87, 0x6D6F, 0x6D96, 0x6DAC, 0x6DCF, 0x6DF8, 0x6DF2,
- 0x6DFC, 0x6E39, 0x6E5C, 0x6E27, 0x6E3C, 0x6EBF, 0x6F88, 0x6FB5,
- 0x6FF5, 0x7005, 0x7007, 0x7028, 0x7085, 0x70AB, 0x710F, 0x7104,
- 0x715C, 0x7146, 0x7147, 0xFA15, 0x71C1, 0x71FE, 0x72B1,
-};
-static const unsigned short euc_to_utf8_FA_x0213[] = {
- 0x7D41, 0x7D48, 0x7D53, 0x7D5C, 0x7D7A, 0x7D83, 0x7D8B,
- 0x7DA0, 0x7DA6, 0x7DC2, 0x7DCC, 0x7DD6, 0x7DE3, 0xFA57, 0x7E28,
- 0x7E08, 0x7E11, 0x7E15, 0xFA59, 0x7E47, 0x7E52, 0x7E61, 0x7E8A,
- 0x7E8D, 0x7F47, 0xFA5A, 0x7F91, 0x7F97, 0x7FBF, 0x7FCE, 0x7FDB,
- 0x7FDF, 0x7FEC, 0x7FEE, 0x7FFA, 0xFA5B, 0x8014, 0x8026, 0x8035,
- 0x8037, 0x803C, 0x80CA, 0x80D7, 0x80E0, 0x80F3, 0x8118, 0x814A,
- 0x8160, 0x8167, 0x8168, 0x816D, 0x81BB, 0x81CA, 0x81CF, 0x81D7,
- 0xFA5C, 0x4453, 0x445B, 0x8260, 0x8274, 0xD85A /*0xDEFF*/, 0x828E, 0x82A1,
- 0x82A3, 0x82A4, 0x82A9, 0x82AE, 0x82B7, 0x82BE, 0x82BF, 0x82C6,
- 0x82D5, 0x82FD, 0x82FE, 0x8300, 0x8301, 0x8362, 0x8322, 0x832D,
- 0x833A, 0x8343, 0x8347, 0x8351, 0x8355, 0x837D, 0x8386, 0x8392,
- 0x8398, 0x83A7, 0x83A9, 0x83BF, 0x83C0, 0x83C7, 0x83CF,
-};
-static const unsigned short euc_to_utf8_FB[] = {
- 0x72BE, 0x7324, 0xFA16, 0x7377, 0x73BD, 0x73C9, 0x73D6,
- 0x73E3, 0x73D2, 0x7407, 0x73F5, 0x7426, 0x742A, 0x7429, 0x742E,
- 0x7462, 0x7489, 0x749F, 0x7501, 0x756F, 0x7682, 0x769C, 0x769E,
- 0x769B, 0x76A6, 0xFA17, 0x7746, 0x52AF, 0x7821, 0x784E, 0x7864,
- 0x787A, 0x7930, 0xFA18, 0xFA19, 0xFA1A, 0x7994, 0xFA1B, 0x799B,
- 0x7AD1, 0x7AE7, 0xFA1C, 0x7AEB, 0x7B9E, 0xFA1D, 0x7D48, 0x7D5C,
- 0x7DB7, 0x7DA0, 0x7DD6, 0x7E52, 0x7F47, 0x7FA1, 0xFA1E, 0x8301,
- 0x8362, 0x837F, 0x83C7, 0x83F6, 0x8448, 0x84B4, 0x8553, 0x8559,
- 0x856B, 0xFA1F, 0x85B0, 0xFA20, 0xFA21, 0x8807, 0x88F5, 0x8A12,
- 0x8A37, 0x8A79, 0x8AA7, 0x8ABE, 0x8ADF, 0xFA22, 0x8AF6, 0x8B53,
- 0x8B7F, 0x8CF0, 0x8CF4, 0x8D12, 0x8D76, 0xFA23, 0x8ECF, 0xFA24,
- 0xFA25, 0x9067, 0x90DE, 0xFA26, 0x9115, 0x9127, 0x91DA,
-};
-static const unsigned short euc_to_utf8_FB_x0213[] = {
- 0x83D1, 0x83E1, 0x83EA, 0x8401, 0x8406, 0x840A, 0xFA5F,
- 0x8448, 0x845F, 0x8470, 0x8473, 0x8485, 0x849E, 0x84AF, 0x84B4,
- 0x84BA, 0x84C0, 0x84C2, 0xD85B /*0xDE40*/, 0x8532, 0x851E, 0x8523, 0x852F,
- 0x8559, 0x8564, 0xFA1F, 0x85AD, 0x857A, 0x858C, 0x858F, 0x85A2,
- 0x85B0, 0x85CB, 0x85CE, 0x85ED, 0x8612, 0x85FF, 0x8604, 0x8605,
- 0x8610, 0xD85C /*0xDCF4*/, 0x8618, 0x8629, 0x8638, 0x8657, 0x865B, 0xF936,
- 0x8662, 0x459D, 0x866C, 0x8675, 0x8698, 0x86B8, 0x86FA, 0x86FC,
- 0x86FD, 0x870B, 0x8771, 0x8787, 0x8788, 0x87AC, 0x87AD, 0x87B5,
- 0x45EA, 0x87D6, 0x87EC, 0x8806, 0x880A, 0x8810, 0x8814, 0x881F,
- 0x8898, 0x88AA, 0x88CA, 0x88CE, 0xD85D /*0xDE84*/, 0x88F5, 0x891C, 0xFA60,
- 0x8918, 0x8919, 0x891A, 0x8927, 0x8930, 0x8932, 0x8939, 0x8940,
- 0x8994, 0xFA61, 0x89D4, 0x89E5, 0x89F6, 0x8A12, 0x8A15,
-};
-static const unsigned short euc_to_utf8_FC[] = {
- 0x91D7, 0x91DE, 0x91ED, 0x91EE, 0x91E4, 0x91E5, 0x9206,
- 0x9210, 0x920A, 0x923A, 0x9240, 0x923C, 0x924E, 0x9259, 0x9251,
- 0x9239, 0x9267, 0x92A7, 0x9277, 0x9278, 0x92E7, 0x92D7, 0x92D9,
- 0x92D0, 0xFA27, 0x92D5, 0x92E0, 0x92D3, 0x9325, 0x9321, 0x92FB,
- 0xFA28, 0x931E, 0x92FF, 0x931D, 0x9302, 0x9370, 0x9357, 0x93A4,
- 0x93C6, 0x93DE, 0x93F8, 0x9431, 0x9445, 0x9448, 0x9592, 0xF9DC,
- 0xFA29, 0x969D, 0x96AF, 0x9733, 0x973B, 0x9743, 0x974D, 0x974F,
- 0x9751, 0x9755, 0x9857, 0x9865, 0xFA2A, 0xFA2B, 0x9927, 0xFA2C,
- 0x999E, 0x9A4E, 0x9AD9, 0x9ADC, 0x9B75, 0x9B72, 0x9B8F, 0x9BB1,
- 0x9BBB, 0x9C00, 0x9D70, 0x9D6B, 0xFA2D, 0x9E19, 0x9ED1, 0,
- 0, 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176,
- 0x2177, 0x2178, 0x2179, 0xFFE2, 0x00A6, 0xFF07, 0xFF02,
-};
-
-/* Microsoft UCS Mapping Compatible */
-static const unsigned short euc_to_utf8_FC_ms[] = {
- 0x91D7, 0x91DE, 0x91ED, 0x91EE, 0x91E4, 0x91E5, 0x9206,
- 0x9210, 0x920A, 0x923A, 0x9240, 0x923C, 0x924E, 0x9259, 0x9251,
- 0x9239, 0x9267, 0x92A7, 0x9277, 0x9278, 0x92E7, 0x92D7, 0x92D9,
- 0x92D0, 0xFA27, 0x92D5, 0x92E0, 0x92D3, 0x9325, 0x9321, 0x92FB,
- 0xFA28, 0x931E, 0x92FF, 0x931D, 0x9302, 0x9370, 0x9357, 0x93A4,
- 0x93C6, 0x93DE, 0x93F8, 0x9431, 0x9445, 0x9448, 0x9592, 0xF9DC,
- 0xFA29, 0x969D, 0x96AF, 0x9733, 0x973B, 0x9743, 0x974D, 0x974F,
- 0x9751, 0x9755, 0x9857, 0x9865, 0xFA2A, 0xFA2B, 0x9927, 0xFA2C,
- 0x999E, 0x9A4E, 0x9AD9, 0x9ADC, 0x9B75, 0x9B72, 0x9B8F, 0x9BB1,
- 0x9BBB, 0x9C00, 0x9D70, 0x9D6B, 0xFA2D, 0x9E19, 0x9ED1, 0,
- 0, 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176,
- 0x2177, 0x2178, 0x2179, 0xFFE2, 0xFFE4, 0xFF07, 0xFF02,
-};
-static const unsigned short euc_to_utf8_FC_x0213[] = {
- 0x8A22, 0x8A37, 0x8A47, 0x8A4E, 0x8A5D, 0x8A61, 0x8A75,
- 0x8A79, 0x8AA7, 0x8AD0, 0x8ADF, 0x8AF4, 0x8AF6, 0xFA22, 0xFA62,
- 0xFA63, 0x8B46, 0x8B54, 0x8B59, 0x8B69, 0x8B9D, 0x8C49, 0x8C68,
- 0xFA64, 0x8CE1, 0x8CF4, 0x8CF8, 0x8CFE, 0xFA65, 0x8D12, 0x8D1B,
- 0x8DAF, 0x8DCE, 0x8DD1, 0x8DD7, 0x8E20, 0x8E23, 0x8E3D, 0x8E70,
- 0x8E7B, 0xD860 /*0xDE77*/, 0x8EC0, 0x4844, 0x8EFA, 0x8F1E, 0x8F2D, 0x8F36,
- 0x8F54, 0xD860 /*0xDFCD*/, 0x8FA6, 0x8FB5, 0x8FE4, 0x8FE8, 0x8FEE, 0x9008,
- 0x902D, 0xFA67, 0x9088, 0x9095, 0x9097, 0x9099, 0x909B, 0x90A2,
- 0x90B3, 0x90BE, 0x90C4, 0x90C5, 0x90C7, 0x90D7, 0x90DD, 0x90DE,
- 0x90EF, 0x90F4, 0xFA26, 0x9114, 0x9115, 0x9116, 0x9122, 0x9123,
- 0x9127, 0x912F, 0x9131, 0x9134, 0x913D, 0x9148, 0x915B, 0x9183,
- 0x919E, 0x91AC, 0x91B1, 0x91BC, 0x91D7, 0x91FB, 0x91E4,
-};
-static const unsigned short euc_to_utf8_FD_x0213[] = {
- 0x91E5, 0x91ED, 0x91F1, 0x9207, 0x9210, 0x9238, 0x9239,
- 0x923A, 0x923C, 0x9240, 0x9243, 0x924F, 0x9278, 0x9288, 0x92C2,
- 0x92CB, 0x92CC, 0x92D3, 0x92E0, 0x92FF, 0x9304, 0x931F, 0x9321,
- 0x9325, 0x9348, 0x9349, 0x934A, 0x9364, 0x9365, 0x936A, 0x9370,
- 0x939B, 0x93A3, 0x93BA, 0x93C6, 0x93DE, 0x93DF, 0x9404, 0x93FD,
- 0x9433, 0x944A, 0x9463, 0x946B, 0x9471, 0x9472, 0x958E, 0x959F,
- 0x95A6, 0x95A9, 0x95AC, 0x95B6, 0x95BD, 0x95CB, 0x95D0, 0x95D3,
- 0x49B0, 0x95DA, 0x95DE, 0x9658, 0x9684, 0xF9DC, 0x969D, 0x96A4,
- 0x96A5, 0x96D2, 0x96DE, 0xFA68, 0x96E9, 0x96EF, 0x9733, 0x973B,
- 0x974D, 0x974E, 0x974F, 0x975A, 0x976E, 0x9773, 0x9795, 0x97AE,
- 0x97BA, 0x97C1, 0x97C9, 0x97DE, 0x97DB, 0x97F4, 0xFA69, 0x980A,
- 0x981E, 0x982B, 0x9830, 0xFA6A, 0x9852, 0x9853, 0x9856,
-};
-static const unsigned short euc_to_utf8_FE_x0213[] = {
- 0x9857, 0x9859, 0x985A, 0xF9D0, 0x9865, 0x986C, 0x98BA,
- 0x98C8, 0x98E7, 0x9958, 0x999E, 0x9A02, 0x9A03, 0x9A24, 0x9A2D,
- 0x9A2E, 0x9A38, 0x9A4A, 0x9A4E, 0x9A52, 0x9AB6, 0x9AC1, 0x9AC3,
- 0x9ACE, 0x9AD6, 0x9AF9, 0x9B02, 0x9B08, 0x9B20, 0x4C17, 0x9B2D,
- 0x9B5E, 0x9B79, 0x9B66, 0x9B72, 0x9B75, 0x9B84, 0x9B8A, 0x9B8F,
- 0x9B9E, 0x9BA7, 0x9BC1, 0x9BCE, 0x9BE5, 0x9BF8, 0x9BFD, 0x9C00,
- 0x9C23, 0x9C41, 0x9C4F, 0x9C50, 0x9C53, 0x9C63, 0x9C65, 0x9C77,
- 0x9D1D, 0x9D1E, 0x9D43, 0x9D47, 0x9D52, 0x9D63, 0x9D70, 0x9D7C,
- 0x9D8A, 0x9D96, 0x9DC0, 0x9DAC, 0x9DBC, 0x9DD7, 0xD868 /*0xDD90*/, 0x9DE7,
- 0x9E07, 0x9E15, 0x9E7C, 0x9E9E, 0x9EA4, 0x9EAC, 0x9EAF, 0x9EB4,
- 0x9EB5, 0x9EC3, 0x9ED1, 0x9F10, 0x9F39, 0x9F57, 0x9F90, 0x9F94,
- 0x9F97, 0x9FA2, 0x59F8, 0x5C5B, 0x5E77, 0x7626, 0x7E6B,
-};
-
-static const unsigned short euc_to_utf8_8FA1_x0213[] = {
- 0xD840 /*0xDC89*/, 0x4E02, 0x4E0F, 0x4E12, 0x4E29, 0x4E2B, 0x4E2E,
- 0x4E40, 0x4E47, 0x4E48, 0xD840 /*0xDCA2*/, 0x4E51, 0x3406, 0xD840 /*0xDCA4*/, 0x4E5A,
- 0x4E69, 0x4E9D, 0x342C, 0x342E, 0x4EB9, 0x4EBB, 0xD840 /*0xDDA2*/, 0x4EBC,
- 0x4EC3, 0x4EC8, 0x4ED0, 0x4EEB, 0x4EDA, 0x4EF1, 0x4EF5, 0x4F00,
- 0x4F16, 0x4F64, 0x4F37, 0x4F3E, 0x4F54, 0x4F58, 0xD840 /*0xDE13*/, 0x4F77,
- 0x4F78, 0x4F7A, 0x4F7D, 0x4F82, 0x4F85, 0x4F92, 0x4F9A, 0x4FE6,
- 0x4FB2, 0x4FBE, 0x4FC5, 0x4FCB, 0x4FCF, 0x4FD2, 0x346A, 0x4FF2,
- 0x5000, 0x5010, 0x5013, 0x501C, 0x501E, 0x5022, 0x3468, 0x5042,
- 0x5046, 0x504E, 0x5053, 0x5057, 0x5063, 0x5066, 0x506A, 0x5070,
- 0x50A3, 0x5088, 0x5092, 0x5093, 0x5095, 0x5096, 0x509C, 0x50AA,
- 0xD840 /*0xDF2B*/, 0x50B1, 0x50BA, 0x50BB, 0x50C4, 0x50C7, 0x50F3, 0xD840 /*0xDF81*/,
- 0x50CE, 0xD840 /*0xDF71*/, 0x50D4, 0x50D9, 0x50E1, 0x50E9, 0x3492,
-};
-static const unsigned short euc_to_utf8_8FA3_x0213[] = {
- 0x5108, 0xD840 /*0xDFF9*/, 0x5117, 0x511B, 0xD841 /*0xDC4A*/, 0x5160, 0xD841 /*0xDD09*/,
- 0x5173, 0x5183, 0x518B, 0x34BC, 0x5198, 0x51A3, 0x51AD, 0x34C7,
- 0x51BC, 0xD841 /*0xDDD6*/, 0xD841 /*0xDE28*/, 0x51F3, 0x51F4, 0x5202, 0x5212, 0x5216,
- 0xD841 /*0xDF4F*/, 0x5255, 0x525C, 0x526C, 0x5277, 0x5284, 0x5282, 0xD842 /*0xDC07*/,
- 0x5298, 0xD842 /*0xDC3A*/, 0x52A4, 0x52A6, 0x52AF, 0x52BA, 0x52BB, 0x52CA,
- 0x351F, 0x52D1, 0xD842 /*0xDCB9*/, 0x52F7, 0x530A, 0x530B, 0x5324, 0x5335,
- 0x533E, 0x5342, 0xD842 /*0xDD7C*/, 0xD842 /*0xDD9D*/, 0x5367, 0x536C, 0x537A, 0x53A4,
- 0x53B4, 0xD842 /*0xDED3*/, 0x53B7, 0x53C0, 0xD842 /*0xDF1D*/, 0x355D, 0x355E, 0x53D5,
- 0x53DA, 0x3563, 0x53F4, 0x53F5, 0x5455, 0x5424, 0x5428, 0x356E,
- 0x5443, 0x5462, 0x5466, 0x546C, 0x548A, 0x548D, 0x5495, 0x54A0,
- 0x54A6, 0x54AD, 0x54AE, 0x54B7, 0x54BA, 0x54BF, 0x54C3, 0xD843 /*0xDD45*/,
- 0x54EC, 0x54EF, 0x54F1, 0x54F3, 0x5500, 0x5501, 0x5509,
-};
-static const unsigned short euc_to_utf8_8FA4_x0213[] = {
- 0x553C, 0x5541, 0x35A6, 0x5547, 0x554A, 0x35A8, 0x5560,
- 0x5561, 0x5564, 0xD843 /*0xDDE1*/, 0x557D, 0x5582, 0x5588, 0x5591, 0x35C5,
- 0x55D2, 0xD843 /*0xDE95*/, 0xD843 /*0xDE6D*/, 0x55BF, 0x55C9, 0x55CC, 0x55D1, 0x55DD,
- 0x35DA, 0x55E2, 0xD843 /*0xDE64*/, 0x55E9, 0x5628, 0xD843 /*0xDF5F*/, 0x5607, 0x5610,
- 0x5630, 0x5637, 0x35F4, 0x563D, 0x563F, 0x5640, 0x5647, 0x565E,
- 0x5660, 0x566D, 0x3605, 0x5688, 0x568C, 0x5695, 0x569A, 0x569D,
- 0x56A8, 0x56AD, 0x56B2, 0x56C5, 0x56CD, 0x56DF, 0x56E8, 0x56F6,
- 0x56F7, 0xD844 /*0xDE01*/, 0x5715, 0x5723, 0xD844 /*0xDE55*/, 0x5729, 0xD844 /*0xDE7B*/, 0x5745,
- 0x5746, 0x574C, 0x574D, 0xD844 /*0xDE74*/, 0x5768, 0x576F, 0x5773, 0x5774,
- 0x5775, 0x577B, 0xD844 /*0xDEE4*/, 0xD844 /*0xDED7*/, 0x57AC, 0x579A, 0x579D, 0x579E,
- 0x57A8, 0x57D7, 0xD844 /*0xDEFD*/, 0x57CC, 0xD844 /*0xDF36*/, 0xD844 /*0xDF44*/, 0x57DE, 0x57E6,
- 0x57F0, 0x364A, 0x57F8, 0x57FB, 0x57FD, 0x5804, 0x581E,
-};
-static const unsigned short euc_to_utf8_8FA5_x0213[] = {
- 0x5820, 0x5827, 0x5832, 0x5839, 0xD844 /*0xDFC4*/, 0x5849, 0x584C,
- 0x5867, 0x588A, 0x588B, 0x588D, 0x588F, 0x5890, 0x5894, 0x589D,
- 0x58AA, 0x58B1, 0xD845 /*0xDC6D*/, 0x58C3, 0x58CD, 0x58E2, 0x58F3, 0x58F4,
- 0x5905, 0x5906, 0x590B, 0x590D, 0x5914, 0x5924, 0xD845 /*0xDDD7*/, 0x3691,
- 0x593D, 0x3699, 0x5946, 0x3696, 0xD85B /*0xDC29*/, 0x595B, 0x595F, 0xD845 /*0xDE47*/,
- 0x5975, 0x5976, 0x597C, 0x599F, 0x59AE, 0x59BC, 0x59C8, 0x59CD,
- 0x59DE, 0x59E3, 0x59E4, 0x59E7, 0x59EE, 0xD845 /*0xDF06*/, 0xD845 /*0xDF42*/, 0x36CF,
- 0x5A0C, 0x5A0D, 0x5A17, 0x5A27, 0x5A2D, 0x5A55, 0x5A65, 0x5A7A,
- 0x5A8B, 0x5A9C, 0x5A9F, 0x5AA0, 0x5AA2, 0x5AB1, 0x5AB3, 0x5AB5,
- 0x5ABA, 0x5ABF, 0x5ADA, 0x5ADC, 0x5AE0, 0x5AE5, 0x5AF0, 0x5AEE,
- 0x5AF5, 0x5B00, 0x5B08, 0x5B17, 0x5B34, 0x5B2D, 0x5B4C, 0x5B52,
- 0x5B68, 0x5B6F, 0x5B7C, 0x5B7F, 0x5B81, 0x5B84, 0xD846 /*0xDDC3*/,
-};
-static const unsigned short euc_to_utf8_8FA8_x0213[] = {
- 0x5B96, 0x5BAC, 0x3761, 0x5BC0, 0x3762, 0x5BCE, 0x5BD6,
- 0x376C, 0x376B, 0x5BF1, 0x5BFD, 0x3775, 0x5C03, 0x5C29, 0x5C30,
- 0xD847 /*0xDC56*/, 0x5C5F, 0x5C63, 0x5C67, 0x5C68, 0x5C69, 0x5C70, 0xD847 /*0xDD2D*/,
- 0xD847 /*0xDD45*/, 0x5C7C, 0xD847 /*0xDD78*/, 0xD847 /*0xDD62*/, 0x5C88, 0x5C8A, 0x37C1, 0xD847 /*0xDDA1*/,
- 0xD847 /*0xDD9C*/, 0x5CA0, 0x5CA2, 0x5CA6, 0x5CA7, 0xD847 /*0xDD92*/, 0x5CAD, 0x5CB5,
- 0xD847 /*0xDDB7*/, 0x5CC9, 0xD847 /*0xDDE0*/, 0xD847 /*0xDE33*/, 0x5D06, 0x5D10, 0x5D2B, 0x5D1D,
- 0x5D20, 0x5D24, 0x5D26, 0x5D31, 0x5D39, 0x5D42, 0x37E8, 0x5D61,
- 0x5D6A, 0x37F4, 0x5D70, 0xD847 /*0xDF1E*/, 0x37FD, 0x5D88, 0x3800, 0x5D92,
- 0x5D94, 0x5D97, 0x5D99, 0x5DB0, 0x5DB2, 0x5DB4, 0xD847 /*0xDF76*/, 0x5DB9,
- 0x5DD1, 0x5DD7, 0x5DD8, 0x5DE0, 0xD847 /*0xDFFA*/, 0x5DE4, 0x5DE9, 0x382F,
- 0x5E00, 0x3836, 0x5E12, 0x5E15, 0x3840, 0x5E1F, 0x5E2E, 0x5E3E,
- 0x5E49, 0x385C, 0x5E56, 0x3861, 0x5E6B, 0x5E6C, 0x5E6D,
-};
-static const unsigned short euc_to_utf8_8FAC_x0213[] = {
- 0x5E6E, 0xD848 /*0xDD7B*/, 0x5EA5, 0x5EAA, 0x5EAC, 0x5EB9, 0x5EBF,
- 0x5EC6, 0x5ED2, 0x5ED9, 0xD848 /*0xDF1E*/, 0x5EFD, 0x5F08, 0x5F0E, 0x5F1C,
- 0xD848 /*0xDFAD*/, 0x5F1E, 0x5F47, 0x5F63, 0x5F72, 0x5F7E, 0x5F8F, 0x5FA2,
- 0x5FA4, 0x5FB8, 0x5FC4, 0x38FA, 0x5FC7, 0x5FCB, 0x5FD2, 0x5FD3,
- 0x5FD4, 0x5FE2, 0x5FEE, 0x5FEF, 0x5FF3, 0x5FFC, 0x3917, 0x6017,
- 0x6022, 0x6024, 0x391A, 0x604C, 0x607F, 0x608A, 0x6095, 0x60A8,
- 0xD849 /*0xDEF3*/, 0x60B0, 0x60B1, 0x60BE, 0x60C8, 0x60D9, 0x60DB, 0x60EE,
- 0x60F2, 0x60F5, 0x6110, 0x6112, 0x6113, 0x6119, 0x611E, 0x613A,
- 0x396F, 0x6141, 0x6146, 0x6160, 0x617C, 0xD84A /*0xDC5B*/, 0x6192, 0x6193,
- 0x6197, 0x6198, 0x61A5, 0x61A8, 0x61AD, 0xD84A /*0xDCAB*/, 0x61D5, 0x61DD,
- 0x61DF, 0x61F5, 0xD84A /*0xDD8F*/, 0x6215, 0x6223, 0x6229, 0x6246, 0x624C,
- 0x6251, 0x6252, 0x6261, 0x6264, 0x627B, 0x626D, 0x6273,
-};
-static const unsigned short euc_to_utf8_8FAD_x0213[] = {
- 0x6299, 0x62A6, 0x62D5, 0xD84A /*0xDEB8*/, 0x62FD, 0x6303, 0x630D,
- 0x6310, 0xD84A /*0xDF4F*/, 0xD84A /*0xDF50*/, 0x6332, 0x6335, 0x633B, 0x633C, 0x6341,
- 0x6344, 0x634E, 0xD84A /*0xDF46*/, 0x6359, 0xD84B /*0xDC1D*/, 0xD84A /*0xDFA6*/, 0x636C, 0x6384,
- 0x6399, 0xD84B /*0xDC24*/, 0x6394, 0x63BD, 0x63F7, 0x63D4, 0x63D5, 0x63DC,
- 0x63E0, 0x63EB, 0x63EC, 0x63F2, 0x6409, 0x641E, 0x6425, 0x6429,
- 0x642F, 0x645A, 0x645B, 0x645D, 0x6473, 0x647D, 0x6487, 0x6491,
- 0x649D, 0x649F, 0x64CB, 0x64CC, 0x64D5, 0x64D7, 0xD84B /*0xDDE1*/, 0x64E4,
- 0x64E5, 0x64FF, 0x6504, 0x3A6E, 0x650F, 0x6514, 0x6516, 0x3A73,
- 0x651E, 0x6532, 0x6544, 0x6554, 0x656B, 0x657A, 0x6581, 0x6584,
- 0x6585, 0x658A, 0x65B2, 0x65B5, 0x65B8, 0x65BF, 0x65C2, 0x65C9,
- 0x65D4, 0x3AD6, 0x65F2, 0x65F9, 0x65FC, 0x6604, 0x6608, 0x6621,
- 0x662A, 0x6645, 0x6651, 0x664E, 0x3AEA, 0xD84C /*0xDDC3*/, 0x6657,
-};
-static const unsigned short euc_to_utf8_8FAE_x0213[] = {
- 0x665B, 0x6663, 0xD84C /*0xDDF5*/, 0xD84C /*0xDDB6*/, 0x666A, 0x666B, 0x666C,
- 0x666D, 0x667B, 0x6680, 0x6690, 0x6692, 0x6699, 0x3B0E, 0x66AD,
- 0x66B1, 0x66B5, 0x3B1A, 0x66BF, 0x3B1C, 0x66EC, 0x3AD7, 0x6701,
- 0x6705, 0x6712, 0xD84C /*0xDF72*/, 0x6719, 0xD84C /*0xDFD3*/, 0xD84C /*0xDFD2*/, 0x674C, 0x674D,
- 0x6754, 0x675D, 0xD84C /*0xDFD0*/, 0xD84C /*0xDFE4*/, 0xD84C /*0xDFD5*/, 0x6774, 0x6776, 0xD84C /*0xDFDA*/,
- 0x6792, 0xD84C /*0xDFDF*/, 0x8363, 0x6810, 0x67B0, 0x67B2, 0x67C3, 0x67C8,
- 0x67D2, 0x67D9, 0x67DB, 0x67F0, 0x67F7, 0xD84D /*0xDC4A*/, 0xD84D /*0xDC51*/, 0xD84D /*0xDC4B*/,
- 0x6818, 0x681F, 0x682D, 0xD84D /*0xDC65*/, 0x6833, 0x683B, 0x683E, 0x6844,
- 0x6845, 0x6849, 0x684C, 0x6855, 0x6857, 0x3B77, 0x686B, 0x686E,
- 0x687A, 0x687C, 0x6882, 0x6890, 0x6896, 0x3B6D, 0x6898, 0x6899,
- 0x689A, 0x689C, 0x68AA, 0x68AB, 0x68B4, 0x68BB, 0x68FB, 0xD84D /*0xDCE4*/,
- 0xD84D /*0xDD5A*/, 0xFA13, 0x68C3, 0x68C5, 0x68CC, 0x68CF, 0x68D6,
-};
-static const unsigned short euc_to_utf8_8FAF_x0213[] = {
- 0x68D9, 0x68E4, 0x68E5, 0x68EC, 0x68F7, 0x6903, 0x6907,
- 0x3B87, 0x3B88, 0xD84D /*0xDD94*/, 0x693B, 0x3B8D, 0x6946, 0x6969, 0x696C,
- 0x6972, 0x697A, 0x697F, 0x6992, 0x3BA4, 0x6996, 0x6998, 0x69A6,
- 0x69B0, 0x69B7, 0x69BA, 0x69BC, 0x69C0, 0x69D1, 0x69D6, 0xD84D /*0xDE39*/,
- 0xD84D /*0xDE47*/, 0x6A30, 0xD84D /*0xDE38*/, 0xD84D /*0xDE3A*/, 0x69E3, 0x69EE, 0x69EF, 0x69F3,
- 0x3BCD, 0x69F4, 0x69FE, 0x6A11, 0x6A1A, 0x6A1D, 0xD84D /*0xDF1C*/, 0x6A32,
- 0x6A33, 0x6A34, 0x6A3F, 0x6A46, 0x6A49, 0x6A7A, 0x6A4E, 0x6A52,
- 0x6A64, 0xD84D /*0xDF0C*/, 0x6A7E, 0x6A83, 0x6A8B, 0x3BF0, 0x6A91, 0x6A9F,
- 0x6AA1, 0xD84D /*0xDF64*/, 0x6AAB, 0x6ABD, 0x6AC6, 0x6AD4, 0x6AD0, 0x6ADC,
- 0x6ADD, 0xD84D /*0xDFFF*/, 0xD84D /*0xDFE7*/, 0x6AEC, 0x6AF1, 0x6AF2, 0x6AF3, 0x6AFD,
- 0xD84E /*0xDC24*/, 0x6B0B, 0x6B0F, 0x6B10, 0x6B11, 0xD84E /*0xDC3D*/, 0x6B17, 0x3C26,
- 0x6B2F, 0x6B4A, 0x6B58, 0x6B6C, 0x6B75, 0x6B7A, 0x6B81,
-};
-static const unsigned short euc_to_utf8_8FEE_x0213[] = {
- 0x6B9B, 0x6BAE, 0xD84E /*0xDE98*/, 0x6BBD, 0x6BBE, 0x6BC7, 0x6BC8,
- 0x6BC9, 0x6BDA, 0x6BE6, 0x6BE7, 0x6BEE, 0x6BF1, 0x6C02, 0x6C0A,
- 0x6C0E, 0x6C35, 0x6C36, 0x6C3A, 0xD84F /*0xDC7F*/, 0x6C3F, 0x6C4D, 0x6C5B,
- 0x6C6D, 0x6C84, 0x6C89, 0x3CC3, 0x6C94, 0x6C95, 0x6C97, 0x6CAD,
- 0x6CC2, 0x6CD0, 0x3CD2, 0x6CD6, 0x6CDA, 0x6CDC, 0x6CE9, 0x6CEC,
- 0x6CED, 0xD84F /*0xDD00*/, 0x6D00, 0x6D0A, 0x6D24, 0x6D26, 0x6D27, 0x6C67,
- 0x6D2F, 0x6D3C, 0x6D5B, 0x6D5E, 0x6D60, 0x6D70, 0x6D80, 0x6D81,
- 0x6D8A, 0x6D8D, 0x6D91, 0x6D98, 0xD84F /*0xDD40*/, 0x6E17, 0xD84F /*0xDDFA*/, 0xD84F /*0xDDF9*/,
- 0xD84F /*0xDDD3*/, 0x6DAB, 0x6DAE, 0x6DB4, 0x6DC2, 0x6D34, 0x6DC8, 0x6DCE,
- 0x6DCF, 0x6DD0, 0x6DDF, 0x6DE9, 0x6DF6, 0x6E36, 0x6E1E, 0x6E22,
- 0x6E27, 0x3D11, 0x6E32, 0x6E3C, 0x6E48, 0x6E49, 0x6E4B, 0x6E4C,
- 0x6E4F, 0x6E51, 0x6E53, 0x6E54, 0x6E57, 0x6E63, 0x3D1E,
-};
-static const unsigned short euc_to_utf8_8FEF_x0213[] = {
- 0x6E93, 0x6EA7, 0x6EB4, 0x6EBF, 0x6EC3, 0x6ECA, 0x6ED9,
- 0x6F35, 0x6EEB, 0x6EF9, 0x6EFB, 0x6F0A, 0x6F0C, 0x6F18, 0x6F25,
- 0x6F36, 0x6F3C, 0xD84F /*0xDF7E*/, 0x6F52, 0x6F57, 0x6F5A, 0x6F60, 0x6F68,
- 0x6F98, 0x6F7D, 0x6F90, 0x6F96, 0x6FBE, 0x6F9F, 0x6FA5, 0x6FAF,
- 0x3D64, 0x6FB5, 0x6FC8, 0x6FC9, 0x6FDA, 0x6FDE, 0x6FE9, 0xD850 /*0xDC96*/,
- 0x6FFC, 0x7000, 0x7007, 0x700A, 0x7023, 0xD850 /*0xDD03*/, 0x7039, 0x703A,
- 0x703C, 0x7043, 0x7047, 0x704B, 0x3D9A, 0x7054, 0x7065, 0x7069,
- 0x706C, 0x706E, 0x7076, 0x707E, 0x7081, 0x7086, 0x7095, 0x7097,
- 0x70BB, 0xD850 /*0xDDC6*/, 0x709F, 0x70B1, 0xD850 /*0xDDFE*/, 0x70EC, 0x70CA, 0x70D1,
- 0x70D3, 0x70DC, 0x7103, 0x7104, 0x7106, 0x7107, 0x7108, 0x710C,
- 0x3DC0, 0x712F, 0x7131, 0x7150, 0x714A, 0x7153, 0x715E, 0x3DD4,
- 0x7196, 0x7180, 0x719B, 0x71A0, 0x71A2, 0x71AE, 0x71AF,
-};
-static const unsigned short euc_to_utf8_8FF0_x0213[] = {
- 0x71B3, 0xD850 /*0xDFBC*/, 0x71CB, 0x71D3, 0x71D9, 0x71DC, 0x7207,
- 0x3E05, 0xFA49, 0x722B, 0x7234, 0x7238, 0x7239, 0x4E2C, 0x7242,
- 0x7253, 0x7257, 0x7263, 0xD851 /*0xDE29*/, 0x726E, 0x726F, 0x7278, 0x727F,
- 0x728E, 0xD851 /*0xDEA5*/, 0x72AD, 0x72AE, 0x72B0, 0x72B1, 0x72C1, 0x3E60,
- 0x72CC, 0x3E66, 0x3E68, 0x72F3, 0x72FA, 0x7307, 0x7312, 0x7318,
- 0x7319, 0x3E83, 0x7339, 0x732C, 0x7331, 0x7333, 0x733D, 0x7352,
- 0x3E94, 0x736B, 0x736C, 0xD852 /*0xDC96*/, 0x736E, 0x736F, 0x7371, 0x7377,
- 0x7381, 0x7385, 0x738A, 0x7394, 0x7398, 0x739C, 0x739E, 0x73A5,
- 0x73A8, 0x73B5, 0x73B7, 0x73B9, 0x73BC, 0x73BF, 0x73C5, 0x73CB,
- 0x73E1, 0x73E7, 0x73F9, 0x7413, 0x73FA, 0x7401, 0x7424, 0x7431,
- 0x7439, 0x7453, 0x7440, 0x7443, 0x744D, 0x7452, 0x745D, 0x7471,
- 0x7481, 0x7485, 0x7488, 0xD852 /*0xDE4D*/, 0x7492, 0x7497, 0x7499,
-};
-static const unsigned short euc_to_utf8_8FF1_x0213[] = {
- 0x74A0, 0x74A1, 0x74A5, 0x74AA, 0x74AB, 0x74B9, 0x74BB,
- 0x74BA, 0x74D6, 0x74D8, 0x74DE, 0x74EF, 0x74EB, 0xD852 /*0xDF56*/, 0x74FA,
- 0xD852 /*0xDF6F*/, 0x7520, 0x7524, 0x752A, 0x3F57, 0xD853 /*0xDC16*/, 0x753D, 0x753E,
- 0x7540, 0x7548, 0x754E, 0x7550, 0x7552, 0x756C, 0x7572, 0x7571,
- 0x757A, 0x757D, 0x757E, 0x7581, 0xD853 /*0xDD14*/, 0x758C, 0x3F75, 0x75A2,
- 0x3F77, 0x75B0, 0x75B7, 0x75BF, 0x75C0, 0x75C6, 0x75CF, 0x75D3,
- 0x75DD, 0x75DF, 0x75E0, 0x75E7, 0x75EC, 0x75EE, 0x75F1, 0x75F9,
- 0x7603, 0x7618, 0x7607, 0x760F, 0x3FAE, 0xD853 /*0xDE0E*/, 0x7613, 0x761B,
- 0x761C, 0xD853 /*0xDE37*/, 0x7625, 0x7628, 0x763C, 0x7633, 0xD853 /*0xDE6A*/, 0x3FC9,
- 0x7641, 0xD853 /*0xDE8B*/, 0x7649, 0x7655, 0x3FD7, 0x766E, 0x7695, 0x769C,
- 0x76A1, 0x76A0, 0x76A7, 0x76A8, 0x76AF, 0xD854 /*0xDC4A*/, 0x76C9, 0xD854 /*0xDC55*/,
- 0x76E8, 0x76EC, 0xD854 /*0xDD22*/, 0x7717, 0x771A, 0x772D, 0x7735,
-};
-static const unsigned short euc_to_utf8_8FF2_x0213[] = {
- 0xD854 /*0xDDA9*/, 0x4039, 0xD854 /*0xDDE5*/, 0xD854 /*0xDDCD*/, 0x7758, 0x7760, 0x776A,
- 0xD854 /*0xDE1E*/, 0x7772, 0x777C, 0x777D, 0xD854 /*0xDE4C*/, 0x4058, 0x779A, 0x779F,
- 0x77A2, 0x77A4, 0x77A9, 0x77DE, 0x77DF, 0x77E4, 0x77E6, 0x77EA,
- 0x77EC, 0x4093, 0x77F0, 0x77F4, 0x77FB, 0xD855 /*0xDC2E*/, 0x7805, 0x7806,
- 0x7809, 0x780D, 0x7819, 0x7821, 0x782C, 0x7847, 0x7864, 0x786A,
- 0xD855 /*0xDCD9*/, 0x788A, 0x7894, 0x78A4, 0x789D, 0x789E, 0x789F, 0x78BB,
- 0x78C8, 0x78CC, 0x78CE, 0x78D5, 0x78E0, 0x78E1, 0x78E6, 0x78F9,
- 0x78FA, 0x78FB, 0x78FE, 0xD855 /*0xDDA7*/, 0x7910, 0x791B, 0x7930, 0x7925,
- 0x793B, 0x794A, 0x7958, 0x795B, 0x4105, 0x7967, 0x7972, 0x7994,
- 0x7995, 0x7996, 0x799B, 0x79A1, 0x79A9, 0x79B4, 0x79BB, 0x79C2,
- 0x79C7, 0x79CC, 0x79CD, 0x79D6, 0x4148, 0xD855 /*0xDFA9*/, 0xD855 /*0xDFB4*/, 0x414F,
- 0x7A0A, 0x7A11, 0x7A15, 0x7A1B, 0x7A1E, 0x4163, 0x7A2D,
-};
-static const unsigned short euc_to_utf8_8FF3_x0213[] = {
- 0x7A38, 0x7A47, 0x7A4C, 0x7A56, 0x7A59, 0x7A5C, 0x7A5F,
- 0x7A60, 0x7A67, 0x7A6A, 0x7A75, 0x7A78, 0x7A82, 0x7A8A, 0x7A90,
- 0x7AA3, 0x7AAC, 0xD856 /*0xDDD4*/, 0x41B4, 0x7AB9, 0x7ABC, 0x7ABE, 0x41BF,
- 0x7ACC, 0x7AD1, 0x7AE7, 0x7AE8, 0x7AF4, 0xD856 /*0xDEE4*/, 0xD856 /*0xDEE3*/, 0x7B07,
- 0xD856 /*0xDEF1*/, 0x7B3D, 0x7B27, 0x7B2A, 0x7B2E, 0x7B2F, 0x7B31, 0x41E6,
- 0x41F3, 0x7B7F, 0x7B41, 0x41EE, 0x7B55, 0x7B79, 0x7B64, 0x7B66,
- 0x7B69, 0x7B73, 0xD856 /*0xDFB2*/, 0x4207, 0x7B90, 0x7B91, 0x7B9B, 0x420E,
- 0x7BAF, 0x7BB5, 0x7BBC, 0x7BC5, 0x7BCA, 0xD857 /*0xDC4B*/, 0xD857 /*0xDC64*/, 0x7BD4,
- 0x7BD6, 0x7BDA, 0x7BEA, 0x7BF0, 0x7C03, 0x7C0B, 0x7C0E, 0x7C0F,
- 0x7C26, 0x7C45, 0x7C4A, 0x7C51, 0x7C57, 0x7C5E, 0x7C61, 0x7C69,
- 0x7C6E, 0x7C6F, 0x7C70, 0xD857 /*0xDE2E*/, 0xD857 /*0xDE56*/, 0xD857 /*0xDE65*/, 0x7CA6, 0xD857 /*0xDE62*/,
- 0x7CB6, 0x7CB7, 0x7CBF, 0xD857 /*0xDED8*/, 0x7CC4, 0xD857 /*0xDEC2*/, 0x7CC8,
-};
-static const unsigned short euc_to_utf8_8FF4_x0213[] = {
- 0x7CCD, 0xD857 /*0xDEE8*/, 0x7CD7, 0xD857 /*0xDF23*/, 0x7CE6, 0x7CEB, 0xD857 /*0xDF5C*/,
- 0x7CF5, 0x7D03, 0x7D09, 0x42C6, 0x7D12, 0x7D1E, 0xD857 /*0xDFE0*/, 0xD857 /*0xDFD4*/,
- 0x7D3D, 0x7D3E, 0x7D40, 0x7D47, 0xD858 /*0xDC0C*/, 0xD857 /*0xDFFB*/, 0x42D6, 0x7D59,
- 0x7D5A, 0x7D6A, 0x7D70, 0x42DD, 0x7D7F, 0xD858 /*0xDC17*/, 0x7D86, 0x7D88,
- 0x7D8C, 0x7D97, 0xD858 /*0xDC60*/, 0x7D9D, 0x7DA7, 0x7DAA, 0x7DB6, 0x7DB7,
- 0x7DC0, 0x7DD7, 0x7DD9, 0x7DE6, 0x7DF1, 0x7DF9, 0x4302, 0xD858 /*0xDCED*/,
- 0xFA58, 0x7E10, 0x7E17, 0x7E1D, 0x7E20, 0x7E27, 0x7E2C, 0x7E45,
- 0x7E73, 0x7E75, 0x7E7E, 0x7E86, 0x7E87, 0x432B, 0x7E91, 0x7E98,
- 0x7E9A, 0x4343, 0x7F3C, 0x7F3B, 0x7F3E, 0x7F43, 0x7F44, 0x7F4F,
- 0x34C1, 0xD858 /*0xDE70*/, 0x7F52, 0xD858 /*0xDE86*/, 0x7F61, 0x7F63, 0x7F64, 0x7F6D,
- 0x7F7D, 0x7F7E, 0xD858 /*0xDF4C*/, 0x7F90, 0x517B, 0xD84F /*0xDD0E*/, 0x7F96, 0x7F9C,
- 0x7FAD, 0xD859 /*0xDC02*/, 0x7FC3, 0x7FCF, 0x7FE3, 0x7FE5, 0x7FEF,
-};
-static const unsigned short euc_to_utf8_8FF5_x0213[] = {
- 0x7FF2, 0x8002, 0x800A, 0x8008, 0x800E, 0x8011, 0x8016,
- 0x8024, 0x802C, 0x8030, 0x8043, 0x8066, 0x8071, 0x8075, 0x807B,
- 0x8099, 0x809C, 0x80A4, 0x80A7, 0x80B8, 0xD859 /*0xDE7E*/, 0x80C5, 0x80D5,
- 0x80D8, 0x80E6, 0xD859 /*0xDEB0*/, 0x810D, 0x80F5, 0x80FB, 0x43EE, 0x8135,
- 0x8116, 0x811E, 0x43F0, 0x8124, 0x8127, 0x812C, 0xD859 /*0xDF1D*/, 0x813D,
- 0x4408, 0x8169, 0x4417, 0x8181, 0x441C, 0x8184, 0x8185, 0x4422,
- 0x8198, 0x81B2, 0x81C1, 0x81C3, 0x81D6, 0x81DB, 0xD85A /*0xDCDD*/, 0x81E4,
- 0xD85A /*0xDCEA*/, 0x81EC, 0xD85A /*0xDD51*/, 0x81FD, 0x81FF, 0xD85A /*0xDD6F*/, 0x8204, 0xD85A /*0xDDDD*/,
- 0x8219, 0x8221, 0x8222, 0xD85A /*0xDE1E*/, 0x8232, 0x8234, 0x823C, 0x8246,
- 0x8249, 0x8245, 0xD85A /*0xDE58*/, 0x824B, 0x4476, 0x824F, 0x447A, 0x8257,
- 0xD85A /*0xDE8C*/, 0x825C, 0x8263, 0xD85A /*0xDEB7*/, 0xFA5D, 0xFA5E, 0x8279, 0x4491,
- 0x827D, 0x827F, 0x8283, 0x828A, 0x8293, 0x82A7, 0x82A8,
-};
-static const unsigned short euc_to_utf8_8FF6_x0213[] = {
- 0x82B2, 0x82B4, 0x82BA, 0x82BC, 0x82E2, 0x82E8, 0x82F7,
- 0x8307, 0x8308, 0x830C, 0x8354, 0x831B, 0x831D, 0x8330, 0x833C,
- 0x8344, 0x8357, 0x44BE, 0x837F, 0x44D4, 0x44B3, 0x838D, 0x8394,
- 0x8395, 0x839B, 0x839D, 0x83C9, 0x83D0, 0x83D4, 0x83DD, 0x83E5,
- 0x83F9, 0x840F, 0x8411, 0x8415, 0xD85B /*0xDC73*/, 0x8417, 0x8439, 0x844A,
- 0x844F, 0x8451, 0x8452, 0x8459, 0x845A, 0x845C, 0xD85B /*0xDCDD*/, 0x8465,
- 0x8476, 0x8478, 0x847C, 0x8481, 0x450D, 0x84DC, 0x8497, 0x84A6,
- 0x84BE, 0x4508, 0x84CE, 0x84CF, 0x84D3, 0xD85B /*0xDE65*/, 0x84E7, 0x84EA,
- 0x84EF, 0x84F0, 0x84F1, 0x84FA, 0x84FD, 0x850C, 0x851B, 0x8524,
- 0x8525, 0x852B, 0x8534, 0x854F, 0x856F, 0x4525, 0x4543, 0x853E,
- 0x8551, 0x8553, 0x855E, 0x8561, 0x8562, 0xD85B /*0xDF94*/, 0x857B, 0x857D,
- 0x857F, 0x8581, 0x8586, 0x8593, 0x859D, 0x859F, 0xD85B /*0xDFF8*/,
-};
-static const unsigned short euc_to_utf8_8FF7_x0213[] = {
- 0xD85B /*0xDFF6*/, 0xD85B /*0xDFF7*/, 0x85B7, 0x85BC, 0x85C7, 0x85CA, 0x85D8,
- 0x85D9, 0x85DF, 0x85E1, 0x85E6, 0x85F6, 0x8600, 0x8611, 0x861E,
- 0x8621, 0x8624, 0x8627, 0xD85C /*0xDD0D*/, 0x8639, 0x863C, 0xD85C /*0xDD39*/, 0x8640,
- 0xFA20, 0x8653, 0x8656, 0x866F, 0x8677, 0x867A, 0x8687, 0x8689,
- 0x868D, 0x8691, 0x869C, 0x869D, 0x86A8, 0xFA21, 0x86B1, 0x86B3,
- 0x86C1, 0x86C3, 0x86D1, 0x86D5, 0x86D7, 0x86E3, 0x86E6, 0x45B8,
- 0x8705, 0x8707, 0x870E, 0x8710, 0x8713, 0x8719, 0x871F, 0x8721,
- 0x8723, 0x8731, 0x873A, 0x873E, 0x8740, 0x8743, 0x8751, 0x8758,
- 0x8764, 0x8765, 0x8772, 0x877C, 0xD85C /*0xDFDB*/, 0xD85C /*0xDFDA*/, 0x87A7, 0x8789,
- 0x878B, 0x8793, 0x87A0, 0xD85C /*0xDFFE*/, 0x45E5, 0x87BE, 0xD85D /*0xDC10*/, 0x87C1,
- 0x87CE, 0x87F5, 0x87DF, 0xD85D /*0xDC49*/, 0x87E3, 0x87E5, 0x87E6, 0x87EA,
- 0x87EB, 0x87ED, 0x8801, 0x8803, 0x880B, 0x8813, 0x8828,
-};
-static const unsigned short euc_to_utf8_8FF8_x0213[] = {
- 0x882E, 0x8832, 0x883C, 0x460F, 0x884A, 0x8858, 0x885F,
- 0x8864, 0xD85D /*0xDE15*/, 0xD85D /*0xDE14*/, 0x8869, 0xD85D /*0xDE31*/, 0x886F, 0x88A0, 0x88BC,
- 0x88BD, 0x88BE, 0x88C0, 0x88D2, 0xD85D /*0xDE93*/, 0x88D1, 0x88D3, 0x88DB,
- 0x88F0, 0x88F1, 0x4641, 0x8901, 0xD85D /*0xDF0E*/, 0x8937, 0xD85D /*0xDF23*/, 0x8942,
- 0x8945, 0x8949, 0xD85D /*0xDF52*/, 0x4665, 0x8962, 0x8980, 0x8989, 0x8990,
- 0x899F, 0x89B0, 0x89B7, 0x89D6, 0x89D8, 0x89EB, 0x46A1, 0x89F1,
- 0x89F3, 0x89FD, 0x89FF, 0x46AF, 0x8A11, 0x8A14, 0xD85E /*0xDD85*/, 0x8A21,
- 0x8A35, 0x8A3E, 0x8A45, 0x8A4D, 0x8A58, 0x8AAE, 0x8A90, 0x8AB7,
- 0x8ABE, 0x8AD7, 0x8AFC, 0xD85E /*0xDE84*/, 0x8B0A, 0x8B05, 0x8B0D, 0x8B1C,
- 0x8B1F, 0x8B2D, 0x8B43, 0x470C, 0x8B51, 0x8B5E, 0x8B76, 0x8B7F,
- 0x8B81, 0x8B8B, 0x8B94, 0x8B95, 0x8B9C, 0x8B9E, 0x8C39, 0xD85E /*0xDFB3*/,
- 0x8C3D, 0xD85E /*0xDFBE*/, 0xD85E /*0xDFC7*/, 0x8C45, 0x8C47, 0x8C4F, 0x8C54,
-};
-static const unsigned short euc_to_utf8_8FF9_x0213[] = {
- 0x8C57, 0x8C69, 0x8C6D, 0x8C73, 0xD85F /*0xDCB8*/, 0x8C93, 0x8C92,
- 0x8C99, 0x4764, 0x8C9B, 0x8CA4, 0x8CD6, 0x8CD5, 0x8CD9, 0xD85F /*0xDDA0*/,
- 0x8CF0, 0x8CF1, 0xD85F /*0xDE10*/, 0x8D09, 0x8D0E, 0x8D6C, 0x8D84, 0x8D95,
- 0x8DA6, 0xD85F /*0xDFB7*/, 0x8DC6, 0x8DC8, 0x8DD9, 0x8DEC, 0x8E0C, 0x47FD,
- 0x8DFD, 0x8E06, 0xD860 /*0xDC8A*/, 0x8E14, 0x8E16, 0x8E21, 0x8E22, 0x8E27,
- 0xD860 /*0xDCBB*/, 0x4816, 0x8E36, 0x8E39, 0x8E4B, 0x8E54, 0x8E62, 0x8E6C,
- 0x8E6D, 0x8E6F, 0x8E98, 0x8E9E, 0x8EAE, 0x8EB3, 0x8EB5, 0x8EB6,
- 0x8EBB, 0xD860 /*0xDE82*/, 0x8ED1, 0x8ED4, 0x484E, 0x8EF9, 0xD860 /*0xDEF3*/, 0x8F00,
- 0x8F08, 0x8F17, 0x8F2B, 0x8F40, 0x8F4A, 0x8F58, 0xD861 /*0xDC0C*/, 0x8FA4,
- 0x8FB4, 0xFA66, 0x8FB6, 0xD861 /*0xDC55*/, 0x8FC1, 0x8FC6, 0xFA24, 0x8FCA,
- 0x8FCD, 0x8FD3, 0x8FD5, 0x8FE0, 0x8FF1, 0x8FF5, 0x8FFB, 0x9002,
- 0x900C, 0x9037, 0xD861 /*0xDD6B*/, 0x9043, 0x9044, 0x905D, 0xD861 /*0xDDC8*/,
-};
-static const unsigned short euc_to_utf8_8FFA_x0213[] = {
- 0xD861 /*0xDDC9*/, 0x9085, 0x908C, 0x9090, 0x961D, 0x90A1, 0x48B5,
- 0x90B0, 0x90B6, 0x90C3, 0x90C8, 0xD861 /*0xDED7*/, 0x90DC, 0x90DF, 0xD861 /*0xDEFA*/,
- 0x90F6, 0x90F2, 0x9100, 0x90EB, 0x90FE, 0x90FF, 0x9104, 0x9106,
- 0x9118, 0x911C, 0x911E, 0x9137, 0x9139, 0x913A, 0x9146, 0x9147,
- 0x9157, 0x9159, 0x9161, 0x9164, 0x9174, 0x9179, 0x9185, 0x918E,
- 0x91A8, 0x91AE, 0x91B3, 0x91B6, 0x91C3, 0x91C4, 0x91DA, 0xD862 /*0xDD49*/,
- 0xD862 /*0xDD46*/, 0x91EC, 0x91EE, 0x9201, 0x920A, 0x9216, 0x9217, 0xD862 /*0xDD6B*/,
- 0x9233, 0x9242, 0x9247, 0x924A, 0x924E, 0x9251, 0x9256, 0x9259,
- 0x9260, 0x9261, 0x9265, 0x9267, 0x9268, 0xD862 /*0xDD87*/, 0xD862 /*0xDD88*/, 0x927C,
- 0x927D, 0x927F, 0x9289, 0x928D, 0x9297, 0x9299, 0x929F, 0x92A7,
- 0x92AB, 0xD862 /*0xDDBA*/, 0xD862 /*0xDDBB*/, 0x92B2, 0x92BF, 0x92C0, 0x92C6, 0x92CE,
- 0x92D0, 0x92D7, 0x92D9, 0x92E5, 0x92E7, 0x9311, 0xD862 /*0xDE1E*/,
-};
-static const unsigned short euc_to_utf8_8FFB_x0213[] = {
- 0xD862 /*0xDE29*/, 0x92F7, 0x92F9, 0x92FB, 0x9302, 0x930D, 0x9315,
- 0x931D, 0x931E, 0x9327, 0x9329, 0xD862 /*0xDE71*/, 0xD862 /*0xDE43*/, 0x9347, 0x9351,
- 0x9357, 0x935A, 0x936B, 0x9371, 0x9373, 0x93A1, 0xD862 /*0xDE99*/, 0xD862 /*0xDECD*/,
- 0x9388, 0x938B, 0x938F, 0x939E, 0x93F5, 0xD862 /*0xDEE4*/, 0xD862 /*0xDEDD*/, 0x93F1,
- 0x93C1, 0x93C7, 0x93DC, 0x93E2, 0x93E7, 0x9409, 0x940F, 0x9416,
- 0x9417, 0x93FB, 0x9432, 0x9434, 0x943B, 0x9445, 0xD862 /*0xDFC1*/, 0xD862 /*0xDFEF*/,
- 0x946D, 0x946F, 0x9578, 0x9579, 0x9586, 0x958C, 0x958D, 0xD863 /*0xDD10*/,
- 0x95AB, 0x95B4, 0xD863 /*0xDD71*/, 0x95C8, 0xD863 /*0xDDFB*/, 0xD863 /*0xDE1F*/, 0x962C, 0x9633,
- 0x9634, 0xD863 /*0xDE36*/, 0x963C, 0x9641, 0x9661, 0xD863 /*0xDE89*/, 0x9682, 0xD863 /*0xDEEB*/,
- 0x969A, 0xD863 /*0xDF32*/, 0x49E7, 0x96A9, 0x96AF, 0x96B3, 0x96BA, 0x96BD,
- 0x49FA, 0xD863 /*0xDFF8*/, 0x96D8, 0x96DA, 0x96DD, 0x4A04, 0x9714, 0x9723,
- 0x4A29, 0x9736, 0x9741, 0x9747, 0x9755, 0x9757, 0x975B,
-};
-static const unsigned short euc_to_utf8_8FFC_x0213[] = {
- 0x976A, 0xD864 /*0xDEA0*/, 0xD864 /*0xDEB1*/, 0x9796, 0x979A, 0x979E, 0x97A2,
- 0x97B1, 0x97B2, 0x97BE, 0x97CC, 0x97D1, 0x97D4, 0x97D8, 0x97D9,
- 0x97E1, 0x97F1, 0x9804, 0x980D, 0x980E, 0x9814, 0x9816, 0x4ABC,
- 0xD865 /*0xDC90*/, 0x9823, 0x9832, 0x9833, 0x9825, 0x9847, 0x9866, 0x98AB,
- 0x98AD, 0x98B0, 0xD865 /*0xDDCF*/, 0x98B7, 0x98B8, 0x98BB, 0x98BC, 0x98BF,
- 0x98C2, 0x98C7, 0x98CB, 0x98E0, 0xD865 /*0xDE7F*/, 0x98E1, 0x98E3, 0x98E5,
- 0x98EA, 0x98F0, 0x98F1, 0x98F3, 0x9908, 0x4B3B, 0xD865 /*0xDEF0*/, 0x9916,
- 0x9917, 0xD865 /*0xDF19*/, 0x991A, 0x991B, 0x991C, 0xD865 /*0xDF50*/, 0x9931, 0x9932,
- 0x9933, 0x993A, 0x993B, 0x993C, 0x9940, 0x9941, 0x9946, 0x994D,
- 0x994E, 0x995C, 0x995F, 0x9960, 0x99A3, 0x99A6, 0x99B9, 0x99BD,
- 0x99BF, 0x99C3, 0x99C9, 0x99D4, 0x99D9, 0x99DE, 0xD866 /*0xDCC6*/, 0x99F0,
- 0x99F9, 0x99FC, 0x9A0A, 0x9A11, 0x9A16, 0x9A1A, 0x9A20,
-};
-static const unsigned short euc_to_utf8_8FFD_x0213[] = {
- 0x9A31, 0x9A36, 0x9A44, 0x9A4C, 0x9A58, 0x4BC2, 0x9AAF,
- 0x4BCA, 0x9AB7, 0x4BD2, 0x9AB9, 0xD866 /*0xDE72*/, 0x9AC6, 0x9AD0, 0x9AD2,
- 0x9AD5, 0x4BE8, 0x9ADC, 0x9AE0, 0x9AE5, 0x9AE9, 0x9B03, 0x9B0C,
- 0x9B10, 0x9B12, 0x9B16, 0x9B1C, 0x9B2B, 0x9B33, 0x9B3D, 0x4C20,
- 0x9B4B, 0x9B63, 0x9B65, 0x9B6B, 0x9B6C, 0x9B73, 0x9B76, 0x9B77,
- 0x9BA6, 0x9BAC, 0x9BB1, 0xD867 /*0xDDDB*/, 0xD867 /*0xDE3D*/, 0x9BB2, 0x9BB8, 0x9BBE,
- 0x9BC7, 0x9BF3, 0x9BD8, 0x9BDD, 0x9BE7, 0x9BEA, 0x9BEB, 0x9BEF,
- 0x9BEE, 0xD867 /*0xDE15*/, 0x9BFA, 0xD867 /*0xDE8A*/, 0x9BF7, 0xD867 /*0xDE49*/, 0x9C16, 0x9C18,
- 0x9C19, 0x9C1A, 0x9C1D, 0x9C22, 0x9C27, 0x9C29, 0x9C2A, 0xD867 /*0xDEC4*/,
- 0x9C31, 0x9C36, 0x9C37, 0x9C45, 0x9C5C, 0xD867 /*0xDEE9*/, 0x9C49, 0x9C4A,
- 0xD867 /*0xDEDB*/, 0x9C54, 0x9C58, 0x9C5B, 0x9C5D, 0x9C5F, 0x9C69, 0x9C6A,
- 0x9C6B, 0x9C6D, 0x9C6E, 0x9C70, 0x9C72, 0x9C75, 0x9C7A,
-};
-static const unsigned short euc_to_utf8_8FFE_x0213[] = {
- 0x9CE6, 0x9CF2, 0x9D0B, 0x9D02, 0xD867 /*0xDFCE*/, 0x9D11, 0x9D17,
- 0x9D18, 0xD868 /*0xDC2F*/, 0x4CC4, 0xD868 /*0xDC1A*/, 0x9D32, 0x4CD1, 0x9D42, 0x9D4A,
- 0x9D5F, 0x9D62, 0xD868 /*0xDCF9*/, 0x9D69, 0x9D6B, 0xD868 /*0xDC82*/, 0x9D73, 0x9D76,
- 0x9D77, 0x9D7E, 0x9D84, 0x9D8D, 0x9D99, 0x9DA1, 0x9DBF, 0x9DB5,
- 0x9DB9, 0x9DBD, 0x9DC3, 0x9DC7, 0x9DC9, 0x9DD6, 0x9DDA, 0x9DDF,
- 0x9DE0, 0x9DE3, 0x9DF4, 0x4D07, 0x9E0A, 0x9E02, 0x9E0D, 0x9E19,
- 0x9E1C, 0x9E1D, 0x9E7B, 0xD848 /*0xDE18*/, 0x9E80, 0x9E85, 0x9E9B, 0x9EA8,
- 0xD868 /*0xDF8C*/, 0x9EBD, 0xD869 /*0xDC37*/, 0x9EDF, 0x9EE7, 0x9EEE, 0x9EFF, 0x9F02,
- 0x4D77, 0x9F03, 0x9F17, 0x9F19, 0x9F2F, 0x9F37, 0x9F3A, 0x9F3D,
- 0x9F41, 0x9F45, 0x9F46, 0x9F53, 0x9F55, 0x9F58, 0xD869 /*0xDDF1*/, 0x9F5D,
- 0xD869 /*0xDE02*/, 0x9F69, 0xD869 /*0xDE1A*/, 0x9F6D, 0x9F70, 0x9F75, 0xD869 /*0xDEB2*/, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-
-#ifdef X0212_ENABLE
-static const unsigned short euc_to_utf8_8FA2[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0x02D8,
- 0x02C7, 0x00B8, 0x02D9, 0x02DD, 0x00AF, 0x02DB, 0x02DA, 0xFF5E,
- 0x0384, 0x0385, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x00A1, 0xFFE4, 0x00BF, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x00BA, 0x00AA, 0x00A9, 0x00AE, 0x2122,
- 0x00A4, 0x2116, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short euc_to_utf8_8FA6[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0x0386, 0x0388, 0x0389, 0x038A, 0x03AA, 0, 0x038C,
- 0, 0x038E, 0x03AB, 0, 0x038F, 0, 0, 0,
- 0, 0x03AC, 0x03AD, 0x03AE, 0x03AF, 0x03CA, 0x0390, 0x03CC,
- 0x03C2, 0x03CD, 0x03CB, 0x03B0, 0x03CE, 0, 0,
-};
-static const unsigned short euc_to_utf8_8FA7[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407,
- 0x0408, 0x0409, 0x040A, 0x040B, 0x040C, 0x040E, 0x040F, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457,
- 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x045E, 0x045F,
-};
-static const unsigned short euc_to_utf8_8FA9[] = {
- 0x00C6, 0x0110, 0, 0x0126, 0, 0x0132, 0,
- 0x0141, 0x013F, 0, 0x014A, 0x00D8, 0x0152, 0, 0x0166,
- 0x00DE, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0x00E6, 0x0111, 0x00F0, 0x0127, 0x0131, 0x0133, 0x0138,
- 0x0142, 0x0140, 0x0149, 0x014B, 0x00F8, 0x0153, 0x00DF, 0x0167,
- 0x00FE, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short euc_to_utf8_8FAA[] = {
- 0x00C1, 0x00C0, 0x00C4, 0x00C2, 0x0102, 0x01CD, 0x0100,
- 0x0104, 0x00C5, 0x00C3, 0x0106, 0x0108, 0x010C, 0x00C7, 0x010A,
- 0x010E, 0x00C9, 0x00C8, 0x00CB, 0x00CA, 0x011A, 0x0116, 0x0112,
- 0x0118, 0, 0x011C, 0x011E, 0x0122, 0x0120, 0x0124, 0x00CD,
- 0x00CC, 0x00CF, 0x00CE, 0x01CF, 0x0130, 0x012A, 0x012E, 0x0128,
- 0x0134, 0x0136, 0x0139, 0x013D, 0x013B, 0x0143, 0x0147, 0x0145,
- 0x00D1, 0x00D3, 0x00D2, 0x00D6, 0x00D4, 0x01D1, 0x0150, 0x014C,
- 0x00D5, 0x0154, 0x0158, 0x0156, 0x015A, 0x015C, 0x0160, 0x015E,
- 0x0164, 0x0162, 0x00DA, 0x00D9, 0x00DC, 0x00DB, 0x016C, 0x01D3,
- 0x0170, 0x016A, 0x0172, 0x016E, 0x0168, 0x01D7, 0x01DB, 0x01D9,
- 0x01D5, 0x0174, 0x00DD, 0x0178, 0x0176, 0x0179, 0x017D, 0x017B,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short euc_to_utf8_8FAB[] = {
- 0x00E1, 0x00E0, 0x00E4, 0x00E2, 0x0103, 0x01CE, 0x0101,
- 0x0105, 0x00E5, 0x00E3, 0x0107, 0x0109, 0x010D, 0x00E7, 0x010B,
- 0x010F, 0x00E9, 0x00E8, 0x00EB, 0x00EA, 0x011B, 0x0117, 0x0113,
- 0x0119, 0x01F5, 0x011D, 0x011F, 0, 0x0121, 0x0125, 0x00ED,
- 0x00EC, 0x00EF, 0x00EE, 0x01D0, 0, 0x012B, 0x012F, 0x0129,
- 0x0135, 0x0137, 0x013A, 0x013E, 0x013C, 0x0144, 0x0148, 0x0146,
- 0x00F1, 0x00F3, 0x00F2, 0x00F6, 0x00F4, 0x01D2, 0x0151, 0x014D,
- 0x00F5, 0x0155, 0x0159, 0x0157, 0x015B, 0x015D, 0x0161, 0x015F,
- 0x0165, 0x0163, 0x00FA, 0x00F9, 0x00FC, 0x00FB, 0x016D, 0x01D4,
- 0x0171, 0x016B, 0x0173, 0x016F, 0x0169, 0x01D8, 0x01DC, 0x01DA,
- 0x01D6, 0x0175, 0x00FD, 0x00FF, 0x0177, 0x017A, 0x017E, 0x017C,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short euc_to_utf8_8FB0[] = {
- 0x4E02, 0x4E04, 0x4E05, 0x4E0C, 0x4E12, 0x4E1F, 0x4E23,
- 0x4E24, 0x4E28, 0x4E2B, 0x4E2E, 0x4E2F, 0x4E30, 0x4E35, 0x4E40,
- 0x4E41, 0x4E44, 0x4E47, 0x4E51, 0x4E5A, 0x4E5C, 0x4E63, 0x4E68,
- 0x4E69, 0x4E74, 0x4E75, 0x4E79, 0x4E7F, 0x4E8D, 0x4E96, 0x4E97,
- 0x4E9D, 0x4EAF, 0x4EB9, 0x4EC3, 0x4ED0, 0x4EDA, 0x4EDB, 0x4EE0,
- 0x4EE1, 0x4EE2, 0x4EE8, 0x4EEF, 0x4EF1, 0x4EF3, 0x4EF5, 0x4EFD,
- 0x4EFE, 0x4EFF, 0x4F00, 0x4F02, 0x4F03, 0x4F08, 0x4F0B, 0x4F0C,
- 0x4F12, 0x4F15, 0x4F16, 0x4F17, 0x4F19, 0x4F2E, 0x4F31, 0x4F60,
- 0x4F33, 0x4F35, 0x4F37, 0x4F39, 0x4F3B, 0x4F3E, 0x4F40, 0x4F42,
- 0x4F48, 0x4F49, 0x4F4B, 0x4F4C, 0x4F52, 0x4F54, 0x4F56, 0x4F58,
- 0x4F5F, 0x4F63, 0x4F6A, 0x4F6C, 0x4F6E, 0x4F71, 0x4F77, 0x4F78,
- 0x4F79, 0x4F7A, 0x4F7D, 0x4F7E, 0x4F81, 0x4F82, 0x4F84,
-};
-static const unsigned short euc_to_utf8_8FB1[] = {
- 0x4F85, 0x4F89, 0x4F8A, 0x4F8C, 0x4F8E, 0x4F90, 0x4F92,
- 0x4F93, 0x4F94, 0x4F97, 0x4F99, 0x4F9A, 0x4F9E, 0x4F9F, 0x4FB2,
- 0x4FB7, 0x4FB9, 0x4FBB, 0x4FBC, 0x4FBD, 0x4FBE, 0x4FC0, 0x4FC1,
- 0x4FC5, 0x4FC6, 0x4FC8, 0x4FC9, 0x4FCB, 0x4FCC, 0x4FCD, 0x4FCF,
- 0x4FD2, 0x4FDC, 0x4FE0, 0x4FE2, 0x4FF0, 0x4FF2, 0x4FFC, 0x4FFD,
- 0x4FFF, 0x5000, 0x5001, 0x5004, 0x5007, 0x500A, 0x500C, 0x500E,
- 0x5010, 0x5013, 0x5017, 0x5018, 0x501B, 0x501C, 0x501D, 0x501E,
- 0x5022, 0x5027, 0x502E, 0x5030, 0x5032, 0x5033, 0x5035, 0x5040,
- 0x5041, 0x5042, 0x5045, 0x5046, 0x504A, 0x504C, 0x504E, 0x5051,
- 0x5052, 0x5053, 0x5057, 0x5059, 0x505F, 0x5060, 0x5062, 0x5063,
- 0x5066, 0x5067, 0x506A, 0x506D, 0x5070, 0x5071, 0x503B, 0x5081,
- 0x5083, 0x5084, 0x5086, 0x508A, 0x508E, 0x508F, 0x5090,
-};
-static const unsigned short euc_to_utf8_8FB2[] = {
- 0x5092, 0x5093, 0x5094, 0x5096, 0x509B, 0x509C, 0x509E,
- 0x509F, 0x50A0, 0x50A1, 0x50A2, 0x50AA, 0x50AF, 0x50B0, 0x50B9,
- 0x50BA, 0x50BD, 0x50C0, 0x50C3, 0x50C4, 0x50C7, 0x50CC, 0x50CE,
- 0x50D0, 0x50D3, 0x50D4, 0x50D8, 0x50DC, 0x50DD, 0x50DF, 0x50E2,
- 0x50E4, 0x50E6, 0x50E8, 0x50E9, 0x50EF, 0x50F1, 0x50F6, 0x50FA,
- 0x50FE, 0x5103, 0x5106, 0x5107, 0x5108, 0x510B, 0x510C, 0x510D,
- 0x510E, 0x50F2, 0x5110, 0x5117, 0x5119, 0x511B, 0x511C, 0x511D,
- 0x511E, 0x5123, 0x5127, 0x5128, 0x512C, 0x512D, 0x512F, 0x5131,
- 0x5133, 0x5134, 0x5135, 0x5138, 0x5139, 0x5142, 0x514A, 0x514F,
- 0x5153, 0x5155, 0x5157, 0x5158, 0x515F, 0x5164, 0x5166, 0x517E,
- 0x5183, 0x5184, 0x518B, 0x518E, 0x5198, 0x519D, 0x51A1, 0x51A3,
- 0x51AD, 0x51B8, 0x51BA, 0x51BC, 0x51BE, 0x51BF, 0x51C2,
-};
-static const unsigned short euc_to_utf8_8FB3[] = {
- 0x51C8, 0x51CF, 0x51D1, 0x51D2, 0x51D3, 0x51D5, 0x51D8,
- 0x51DE, 0x51E2, 0x51E5, 0x51EE, 0x51F2, 0x51F3, 0x51F4, 0x51F7,
- 0x5201, 0x5202, 0x5205, 0x5212, 0x5213, 0x5215, 0x5216, 0x5218,
- 0x5222, 0x5228, 0x5231, 0x5232, 0x5235, 0x523C, 0x5245, 0x5249,
- 0x5255, 0x5257, 0x5258, 0x525A, 0x525C, 0x525F, 0x5260, 0x5261,
- 0x5266, 0x526E, 0x5277, 0x5278, 0x5279, 0x5280, 0x5282, 0x5285,
- 0x528A, 0x528C, 0x5293, 0x5295, 0x5296, 0x5297, 0x5298, 0x529A,
- 0x529C, 0x52A4, 0x52A5, 0x52A6, 0x52A7, 0x52AF, 0x52B0, 0x52B6,
- 0x52B7, 0x52B8, 0x52BA, 0x52BB, 0x52BD, 0x52C0, 0x52C4, 0x52C6,
- 0x52C8, 0x52CC, 0x52CF, 0x52D1, 0x52D4, 0x52D6, 0x52DB, 0x52DC,
- 0x52E1, 0x52E5, 0x52E8, 0x52E9, 0x52EA, 0x52EC, 0x52F0, 0x52F1,
- 0x52F4, 0x52F6, 0x52F7, 0x5300, 0x5303, 0x530A, 0x530B,
-};
-static const unsigned short euc_to_utf8_8FB4[] = {
- 0x530C, 0x5311, 0x5313, 0x5318, 0x531B, 0x531C, 0x531E,
- 0x531F, 0x5325, 0x5327, 0x5328, 0x5329, 0x532B, 0x532C, 0x532D,
- 0x5330, 0x5332, 0x5335, 0x533C, 0x533D, 0x533E, 0x5342, 0x534C,
- 0x534B, 0x5359, 0x535B, 0x5361, 0x5363, 0x5365, 0x536C, 0x536D,
- 0x5372, 0x5379, 0x537E, 0x5383, 0x5387, 0x5388, 0x538E, 0x5393,
- 0x5394, 0x5399, 0x539D, 0x53A1, 0x53A4, 0x53AA, 0x53AB, 0x53AF,
- 0x53B2, 0x53B4, 0x53B5, 0x53B7, 0x53B8, 0x53BA, 0x53BD, 0x53C0,
- 0x53C5, 0x53CF, 0x53D2, 0x53D3, 0x53D5, 0x53DA, 0x53DD, 0x53DE,
- 0x53E0, 0x53E6, 0x53E7, 0x53F5, 0x5402, 0x5413, 0x541A, 0x5421,
- 0x5427, 0x5428, 0x542A, 0x542F, 0x5431, 0x5434, 0x5435, 0x5443,
- 0x5444, 0x5447, 0x544D, 0x544F, 0x545E, 0x5462, 0x5464, 0x5466,
- 0x5467, 0x5469, 0x546B, 0x546D, 0x546E, 0x5474, 0x547F,
-};
-static const unsigned short euc_to_utf8_8FB5[] = {
- 0x5481, 0x5483, 0x5485, 0x5488, 0x5489, 0x548D, 0x5491,
- 0x5495, 0x5496, 0x549C, 0x549F, 0x54A1, 0x54A6, 0x54A7, 0x54A9,
- 0x54AA, 0x54AD, 0x54AE, 0x54B1, 0x54B7, 0x54B9, 0x54BA, 0x54BB,
- 0x54BF, 0x54C6, 0x54CA, 0x54CD, 0x54CE, 0x54E0, 0x54EA, 0x54EC,
- 0x54EF, 0x54F6, 0x54FC, 0x54FE, 0x54FF, 0x5500, 0x5501, 0x5505,
- 0x5508, 0x5509, 0x550C, 0x550D, 0x550E, 0x5515, 0x552A, 0x552B,
- 0x5532, 0x5535, 0x5536, 0x553B, 0x553C, 0x553D, 0x5541, 0x5547,
- 0x5549, 0x554A, 0x554D, 0x5550, 0x5551, 0x5558, 0x555A, 0x555B,
- 0x555E, 0x5560, 0x5561, 0x5564, 0x5566, 0x557F, 0x5581, 0x5582,
- 0x5586, 0x5588, 0x558E, 0x558F, 0x5591, 0x5592, 0x5593, 0x5594,
- 0x5597, 0x55A3, 0x55A4, 0x55AD, 0x55B2, 0x55BF, 0x55C1, 0x55C3,
- 0x55C6, 0x55C9, 0x55CB, 0x55CC, 0x55CE, 0x55D1, 0x55D2,
-};
-static const unsigned short euc_to_utf8_8FB6[] = {
- 0x55D3, 0x55D7, 0x55D8, 0x55DB, 0x55DE, 0x55E2, 0x55E9,
- 0x55F6, 0x55FF, 0x5605, 0x5608, 0x560A, 0x560D, 0x560E, 0x560F,
- 0x5610, 0x5611, 0x5612, 0x5619, 0x562C, 0x5630, 0x5633, 0x5635,
- 0x5637, 0x5639, 0x563B, 0x563C, 0x563D, 0x563F, 0x5640, 0x5641,
- 0x5643, 0x5644, 0x5646, 0x5649, 0x564B, 0x564D, 0x564F, 0x5654,
- 0x565E, 0x5660, 0x5661, 0x5662, 0x5663, 0x5666, 0x5669, 0x566D,
- 0x566F, 0x5671, 0x5672, 0x5675, 0x5684, 0x5685, 0x5688, 0x568B,
- 0x568C, 0x5695, 0x5699, 0x569A, 0x569D, 0x569E, 0x569F, 0x56A6,
- 0x56A7, 0x56A8, 0x56A9, 0x56AB, 0x56AC, 0x56AD, 0x56B1, 0x56B3,
- 0x56B7, 0x56BE, 0x56C5, 0x56C9, 0x56CA, 0x56CB, 0x56CF, 0x56D0,
- 0x56CC, 0x56CD, 0x56D9, 0x56DC, 0x56DD, 0x56DF, 0x56E1, 0x56E4,
- 0x56E5, 0x56E6, 0x56E7, 0x56E8, 0x56F1, 0x56EB, 0x56ED,
-};
-static const unsigned short euc_to_utf8_8FB7[] = {
- 0x56F6, 0x56F7, 0x5701, 0x5702, 0x5707, 0x570A, 0x570C,
- 0x5711, 0x5715, 0x571A, 0x571B, 0x571D, 0x5720, 0x5722, 0x5723,
- 0x5724, 0x5725, 0x5729, 0x572A, 0x572C, 0x572E, 0x572F, 0x5733,
- 0x5734, 0x573D, 0x573E, 0x573F, 0x5745, 0x5746, 0x574C, 0x574D,
- 0x5752, 0x5762, 0x5765, 0x5767, 0x5768, 0x576B, 0x576D, 0x576E,
- 0x576F, 0x5770, 0x5771, 0x5773, 0x5774, 0x5775, 0x5777, 0x5779,
- 0x577A, 0x577B, 0x577C, 0x577E, 0x5781, 0x5783, 0x578C, 0x5794,
- 0x5797, 0x5799, 0x579A, 0x579C, 0x579D, 0x579E, 0x579F, 0x57A1,
- 0x5795, 0x57A7, 0x57A8, 0x57A9, 0x57AC, 0x57B8, 0x57BD, 0x57C7,
- 0x57C8, 0x57CC, 0x57CF, 0x57D5, 0x57DD, 0x57DE, 0x57E4, 0x57E6,
- 0x57E7, 0x57E9, 0x57ED, 0x57F0, 0x57F5, 0x57F6, 0x57F8, 0x57FD,
- 0x57FE, 0x57FF, 0x5803, 0x5804, 0x5808, 0x5809, 0x57E1,
-};
-static const unsigned short euc_to_utf8_8FB8[] = {
- 0x580C, 0x580D, 0x581B, 0x581E, 0x581F, 0x5820, 0x5826,
- 0x5827, 0x582D, 0x5832, 0x5839, 0x583F, 0x5849, 0x584C, 0x584D,
- 0x584F, 0x5850, 0x5855, 0x585F, 0x5861, 0x5864, 0x5867, 0x5868,
- 0x5878, 0x587C, 0x587F, 0x5880, 0x5881, 0x5887, 0x5888, 0x5889,
- 0x588A, 0x588C, 0x588D, 0x588F, 0x5890, 0x5894, 0x5896, 0x589D,
- 0x58A0, 0x58A1, 0x58A2, 0x58A6, 0x58A9, 0x58B1, 0x58B2, 0x58C4,
- 0x58BC, 0x58C2, 0x58C8, 0x58CD, 0x58CE, 0x58D0, 0x58D2, 0x58D4,
- 0x58D6, 0x58DA, 0x58DD, 0x58E1, 0x58E2, 0x58E9, 0x58F3, 0x5905,
- 0x5906, 0x590B, 0x590C, 0x5912, 0x5913, 0x5914, 0x8641, 0x591D,
- 0x5921, 0x5923, 0x5924, 0x5928, 0x592F, 0x5930, 0x5933, 0x5935,
- 0x5936, 0x593F, 0x5943, 0x5946, 0x5952, 0x5953, 0x5959, 0x595B,
- 0x595D, 0x595E, 0x595F, 0x5961, 0x5963, 0x596B, 0x596D,
-};
-static const unsigned short euc_to_utf8_8FB9[] = {
- 0x596F, 0x5972, 0x5975, 0x5976, 0x5979, 0x597B, 0x597C,
- 0x598B, 0x598C, 0x598E, 0x5992, 0x5995, 0x5997, 0x599F, 0x59A4,
- 0x59A7, 0x59AD, 0x59AE, 0x59AF, 0x59B0, 0x59B3, 0x59B7, 0x59BA,
- 0x59BC, 0x59C1, 0x59C3, 0x59C4, 0x59C8, 0x59CA, 0x59CD, 0x59D2,
- 0x59DD, 0x59DE, 0x59DF, 0x59E3, 0x59E4, 0x59E7, 0x59EE, 0x59EF,
- 0x59F1, 0x59F2, 0x59F4, 0x59F7, 0x5A00, 0x5A04, 0x5A0C, 0x5A0D,
- 0x5A0E, 0x5A12, 0x5A13, 0x5A1E, 0x5A23, 0x5A24, 0x5A27, 0x5A28,
- 0x5A2A, 0x5A2D, 0x5A30, 0x5A44, 0x5A45, 0x5A47, 0x5A48, 0x5A4C,
- 0x5A50, 0x5A55, 0x5A5E, 0x5A63, 0x5A65, 0x5A67, 0x5A6D, 0x5A77,
- 0x5A7A, 0x5A7B, 0x5A7E, 0x5A8B, 0x5A90, 0x5A93, 0x5A96, 0x5A99,
- 0x5A9C, 0x5A9E, 0x5A9F, 0x5AA0, 0x5AA2, 0x5AA7, 0x5AAC, 0x5AB1,
- 0x5AB2, 0x5AB3, 0x5AB5, 0x5AB8, 0x5ABA, 0x5ABB, 0x5ABF,
-};
-static const unsigned short euc_to_utf8_8FBA[] = {
- 0x5AC4, 0x5AC6, 0x5AC8, 0x5ACF, 0x5ADA, 0x5ADC, 0x5AE0,
- 0x5AE5, 0x5AEA, 0x5AEE, 0x5AF5, 0x5AF6, 0x5AFD, 0x5B00, 0x5B01,
- 0x5B08, 0x5B17, 0x5B34, 0x5B19, 0x5B1B, 0x5B1D, 0x5B21, 0x5B25,
- 0x5B2D, 0x5B38, 0x5B41, 0x5B4B, 0x5B4C, 0x5B52, 0x5B56, 0x5B5E,
- 0x5B68, 0x5B6E, 0x5B6F, 0x5B7C, 0x5B7D, 0x5B7E, 0x5B7F, 0x5B81,
- 0x5B84, 0x5B86, 0x5B8A, 0x5B8E, 0x5B90, 0x5B91, 0x5B93, 0x5B94,
- 0x5B96, 0x5BA8, 0x5BA9, 0x5BAC, 0x5BAD, 0x5BAF, 0x5BB1, 0x5BB2,
- 0x5BB7, 0x5BBA, 0x5BBC, 0x5BC0, 0x5BC1, 0x5BCD, 0x5BCF, 0x5BD6,
- 0x5BD7, 0x5BD8, 0x5BD9, 0x5BDA, 0x5BE0, 0x5BEF, 0x5BF1, 0x5BF4,
- 0x5BFD, 0x5C0C, 0x5C17, 0x5C1E, 0x5C1F, 0x5C23, 0x5C26, 0x5C29,
- 0x5C2B, 0x5C2C, 0x5C2E, 0x5C30, 0x5C32, 0x5C35, 0x5C36, 0x5C59,
- 0x5C5A, 0x5C5C, 0x5C62, 0x5C63, 0x5C67, 0x5C68, 0x5C69,
-};
-static const unsigned short euc_to_utf8_8FBB[] = {
- 0x5C6D, 0x5C70, 0x5C74, 0x5C75, 0x5C7A, 0x5C7B, 0x5C7C,
- 0x5C7D, 0x5C87, 0x5C88, 0x5C8A, 0x5C8F, 0x5C92, 0x5C9D, 0x5C9F,
- 0x5CA0, 0x5CA2, 0x5CA3, 0x5CA6, 0x5CAA, 0x5CB2, 0x5CB4, 0x5CB5,
- 0x5CBA, 0x5CC9, 0x5CCB, 0x5CD2, 0x5CDD, 0x5CD7, 0x5CEE, 0x5CF1,
- 0x5CF2, 0x5CF4, 0x5D01, 0x5D06, 0x5D0D, 0x5D12, 0x5D2B, 0x5D23,
- 0x5D24, 0x5D26, 0x5D27, 0x5D31, 0x5D34, 0x5D39, 0x5D3D, 0x5D3F,
- 0x5D42, 0x5D43, 0x5D46, 0x5D48, 0x5D55, 0x5D51, 0x5D59, 0x5D4A,
- 0x5D5F, 0x5D60, 0x5D61, 0x5D62, 0x5D64, 0x5D6A, 0x5D6D, 0x5D70,
- 0x5D79, 0x5D7A, 0x5D7E, 0x5D7F, 0x5D81, 0x5D83, 0x5D88, 0x5D8A,
- 0x5D92, 0x5D93, 0x5D94, 0x5D95, 0x5D99, 0x5D9B, 0x5D9F, 0x5DA0,
- 0x5DA7, 0x5DAB, 0x5DB0, 0x5DB4, 0x5DB8, 0x5DB9, 0x5DC3, 0x5DC7,
- 0x5DCB, 0x5DD0, 0x5DCE, 0x5DD8, 0x5DD9, 0x5DE0, 0x5DE4,
-};
-static const unsigned short euc_to_utf8_8FBC[] = {
- 0x5DE9, 0x5DF8, 0x5DF9, 0x5E00, 0x5E07, 0x5E0D, 0x5E12,
- 0x5E14, 0x5E15, 0x5E18, 0x5E1F, 0x5E20, 0x5E2E, 0x5E28, 0x5E32,
- 0x5E35, 0x5E3E, 0x5E4B, 0x5E50, 0x5E49, 0x5E51, 0x5E56, 0x5E58,
- 0x5E5B, 0x5E5C, 0x5E5E, 0x5E68, 0x5E6A, 0x5E6B, 0x5E6C, 0x5E6D,
- 0x5E6E, 0x5E70, 0x5E80, 0x5E8B, 0x5E8E, 0x5EA2, 0x5EA4, 0x5EA5,
- 0x5EA8, 0x5EAA, 0x5EAC, 0x5EB1, 0x5EB3, 0x5EBD, 0x5EBE, 0x5EBF,
- 0x5EC6, 0x5ECC, 0x5ECB, 0x5ECE, 0x5ED1, 0x5ED2, 0x5ED4, 0x5ED5,
- 0x5EDC, 0x5EDE, 0x5EE5, 0x5EEB, 0x5F02, 0x5F06, 0x5F07, 0x5F08,
- 0x5F0E, 0x5F19, 0x5F1C, 0x5F1D, 0x5F21, 0x5F22, 0x5F23, 0x5F24,
- 0x5F28, 0x5F2B, 0x5F2C, 0x5F2E, 0x5F30, 0x5F34, 0x5F36, 0x5F3B,
- 0x5F3D, 0x5F3F, 0x5F40, 0x5F44, 0x5F45, 0x5F47, 0x5F4D, 0x5F50,
- 0x5F54, 0x5F58, 0x5F5B, 0x5F60, 0x5F63, 0x5F64, 0x5F67,
-};
-static const unsigned short euc_to_utf8_8FBD[] = {
- 0x5F6F, 0x5F72, 0x5F74, 0x5F75, 0x5F78, 0x5F7A, 0x5F7D,
- 0x5F7E, 0x5F89, 0x5F8D, 0x5F8F, 0x5F96, 0x5F9C, 0x5F9D, 0x5FA2,
- 0x5FA7, 0x5FAB, 0x5FA4, 0x5FAC, 0x5FAF, 0x5FB0, 0x5FB1, 0x5FB8,
- 0x5FC4, 0x5FC7, 0x5FC8, 0x5FC9, 0x5FCB, 0x5FD0, 0x5FD1, 0x5FD2,
- 0x5FD3, 0x5FD4, 0x5FDE, 0x5FE1, 0x5FE2, 0x5FE8, 0x5FE9, 0x5FEA,
- 0x5FEC, 0x5FED, 0x5FEE, 0x5FEF, 0x5FF2, 0x5FF3, 0x5FF6, 0x5FFA,
- 0x5FFC, 0x6007, 0x600A, 0x600D, 0x6013, 0x6014, 0x6017, 0x6018,
- 0x601A, 0x601F, 0x6024, 0x602D, 0x6033, 0x6035, 0x6040, 0x6047,
- 0x6048, 0x6049, 0x604C, 0x6051, 0x6054, 0x6056, 0x6057, 0x605D,
- 0x6061, 0x6067, 0x6071, 0x607E, 0x607F, 0x6082, 0x6086, 0x6088,
- 0x608A, 0x608E, 0x6091, 0x6093, 0x6095, 0x6098, 0x609D, 0x609E,
- 0x60A2, 0x60A4, 0x60A5, 0x60A8, 0x60B0, 0x60B1, 0x60B7,
-};
-static const unsigned short euc_to_utf8_8FBE[] = {
- 0x60BB, 0x60BE, 0x60C2, 0x60C4, 0x60C8, 0x60C9, 0x60CA,
- 0x60CB, 0x60CE, 0x60CF, 0x60D4, 0x60D5, 0x60D9, 0x60DB, 0x60DD,
- 0x60DE, 0x60E2, 0x60E5, 0x60F2, 0x60F5, 0x60F8, 0x60FC, 0x60FD,
- 0x6102, 0x6107, 0x610A, 0x610C, 0x6110, 0x6111, 0x6112, 0x6113,
- 0x6114, 0x6116, 0x6117, 0x6119, 0x611C, 0x611E, 0x6122, 0x612A,
- 0x612B, 0x6130, 0x6131, 0x6135, 0x6136, 0x6137, 0x6139, 0x6141,
- 0x6145, 0x6146, 0x6149, 0x615E, 0x6160, 0x616C, 0x6172, 0x6178,
- 0x617B, 0x617C, 0x617F, 0x6180, 0x6181, 0x6183, 0x6184, 0x618B,
- 0x618D, 0x6192, 0x6193, 0x6197, 0x6198, 0x619C, 0x619D, 0x619F,
- 0x61A0, 0x61A5, 0x61A8, 0x61AA, 0x61AD, 0x61B8, 0x61B9, 0x61BC,
- 0x61C0, 0x61C1, 0x61C2, 0x61CE, 0x61CF, 0x61D5, 0x61DC, 0x61DD,
- 0x61DE, 0x61DF, 0x61E1, 0x61E2, 0x61E7, 0x61E9, 0x61E5,
-};
-static const unsigned short euc_to_utf8_8FBF[] = {
- 0x61EC, 0x61ED, 0x61EF, 0x6201, 0x6203, 0x6204, 0x6207,
- 0x6213, 0x6215, 0x621C, 0x6220, 0x6222, 0x6223, 0x6227, 0x6229,
- 0x622B, 0x6239, 0x623D, 0x6242, 0x6243, 0x6244, 0x6246, 0x624C,
- 0x6250, 0x6251, 0x6252, 0x6254, 0x6256, 0x625A, 0x625C, 0x6264,
- 0x626D, 0x626F, 0x6273, 0x627A, 0x627D, 0x628D, 0x628E, 0x628F,
- 0x6290, 0x62A6, 0x62A8, 0x62B3, 0x62B6, 0x62B7, 0x62BA, 0x62BE,
- 0x62BF, 0x62C4, 0x62CE, 0x62D5, 0x62D6, 0x62DA, 0x62EA, 0x62F2,
- 0x62F4, 0x62FC, 0x62FD, 0x6303, 0x6304, 0x630A, 0x630B, 0x630D,
- 0x6310, 0x6313, 0x6316, 0x6318, 0x6329, 0x632A, 0x632D, 0x6335,
- 0x6336, 0x6339, 0x633C, 0x6341, 0x6342, 0x6343, 0x6344, 0x6346,
- 0x634A, 0x634B, 0x634E, 0x6352, 0x6353, 0x6354, 0x6358, 0x635B,
- 0x6365, 0x6366, 0x636C, 0x636D, 0x6371, 0x6374, 0x6375,
-};
-static const unsigned short euc_to_utf8_8FC0[] = {
- 0x6378, 0x637C, 0x637D, 0x637F, 0x6382, 0x6384, 0x6387,
- 0x638A, 0x6390, 0x6394, 0x6395, 0x6399, 0x639A, 0x639E, 0x63A4,
- 0x63A6, 0x63AD, 0x63AE, 0x63AF, 0x63BD, 0x63C1, 0x63C5, 0x63C8,
- 0x63CE, 0x63D1, 0x63D3, 0x63D4, 0x63D5, 0x63DC, 0x63E0, 0x63E5,
- 0x63EA, 0x63EC, 0x63F2, 0x63F3, 0x63F5, 0x63F8, 0x63F9, 0x6409,
- 0x640A, 0x6410, 0x6412, 0x6414, 0x6418, 0x641E, 0x6420, 0x6422,
- 0x6424, 0x6425, 0x6429, 0x642A, 0x642F, 0x6430, 0x6435, 0x643D,
- 0x643F, 0x644B, 0x644F, 0x6451, 0x6452, 0x6453, 0x6454, 0x645A,
- 0x645B, 0x645C, 0x645D, 0x645F, 0x6460, 0x6461, 0x6463, 0x646D,
- 0x6473, 0x6474, 0x647B, 0x647D, 0x6485, 0x6487, 0x648F, 0x6490,
- 0x6491, 0x6498, 0x6499, 0x649B, 0x649D, 0x649F, 0x64A1, 0x64A3,
- 0x64A6, 0x64A8, 0x64AC, 0x64B3, 0x64BD, 0x64BE, 0x64BF,
-};
-static const unsigned short euc_to_utf8_8FC1[] = {
- 0x64C4, 0x64C9, 0x64CA, 0x64CB, 0x64CC, 0x64CE, 0x64D0,
- 0x64D1, 0x64D5, 0x64D7, 0x64E4, 0x64E5, 0x64E9, 0x64EA, 0x64ED,
- 0x64F0, 0x64F5, 0x64F7, 0x64FB, 0x64FF, 0x6501, 0x6504, 0x6508,
- 0x6509, 0x650A, 0x650F, 0x6513, 0x6514, 0x6516, 0x6519, 0x651B,
- 0x651E, 0x651F, 0x6522, 0x6526, 0x6529, 0x652E, 0x6531, 0x653A,
- 0x653C, 0x653D, 0x6543, 0x6547, 0x6549, 0x6550, 0x6552, 0x6554,
- 0x655F, 0x6560, 0x6567, 0x656B, 0x657A, 0x657D, 0x6581, 0x6585,
- 0x658A, 0x6592, 0x6595, 0x6598, 0x659D, 0x65A0, 0x65A3, 0x65A6,
- 0x65AE, 0x65B2, 0x65B3, 0x65B4, 0x65BF, 0x65C2, 0x65C8, 0x65C9,
- 0x65CE, 0x65D0, 0x65D4, 0x65D6, 0x65D8, 0x65DF, 0x65F0, 0x65F2,
- 0x65F4, 0x65F5, 0x65F9, 0x65FE, 0x65FF, 0x6600, 0x6604, 0x6608,
- 0x6609, 0x660D, 0x6611, 0x6612, 0x6615, 0x6616, 0x661D,
-};
-static const unsigned short euc_to_utf8_8FC2[] = {
- 0x661E, 0x6621, 0x6622, 0x6623, 0x6624, 0x6626, 0x6629,
- 0x662A, 0x662B, 0x662C, 0x662E, 0x6630, 0x6631, 0x6633, 0x6639,
- 0x6637, 0x6640, 0x6645, 0x6646, 0x664A, 0x664C, 0x6651, 0x664E,
- 0x6657, 0x6658, 0x6659, 0x665B, 0x665C, 0x6660, 0x6661, 0x66FB,
- 0x666A, 0x666B, 0x666C, 0x667E, 0x6673, 0x6675, 0x667F, 0x6677,
- 0x6678, 0x6679, 0x667B, 0x6680, 0x667C, 0x668B, 0x668C, 0x668D,
- 0x6690, 0x6692, 0x6699, 0x669A, 0x669B, 0x669C, 0x669F, 0x66A0,
- 0x66A4, 0x66AD, 0x66B1, 0x66B2, 0x66B5, 0x66BB, 0x66BF, 0x66C0,
- 0x66C2, 0x66C3, 0x66C8, 0x66CC, 0x66CE, 0x66CF, 0x66D4, 0x66DB,
- 0x66DF, 0x66E8, 0x66EB, 0x66EC, 0x66EE, 0x66FA, 0x6705, 0x6707,
- 0x670E, 0x6713, 0x6719, 0x671C, 0x6720, 0x6722, 0x6733, 0x673E,
- 0x6745, 0x6747, 0x6748, 0x674C, 0x6754, 0x6755, 0x675D,
-};
-static const unsigned short euc_to_utf8_8FC3[] = {
- 0x6766, 0x676C, 0x676E, 0x6774, 0x6776, 0x677B, 0x6781,
- 0x6784, 0x678E, 0x678F, 0x6791, 0x6793, 0x6796, 0x6798, 0x6799,
- 0x679B, 0x67B0, 0x67B1, 0x67B2, 0x67B5, 0x67BB, 0x67BC, 0x67BD,
- 0x67F9, 0x67C0, 0x67C2, 0x67C3, 0x67C5, 0x67C8, 0x67C9, 0x67D2,
- 0x67D7, 0x67D9, 0x67DC, 0x67E1, 0x67E6, 0x67F0, 0x67F2, 0x67F6,
- 0x67F7, 0x6852, 0x6814, 0x6819, 0x681D, 0x681F, 0x6828, 0x6827,
- 0x682C, 0x682D, 0x682F, 0x6830, 0x6831, 0x6833, 0x683B, 0x683F,
- 0x6844, 0x6845, 0x684A, 0x684C, 0x6855, 0x6857, 0x6858, 0x685B,
- 0x686B, 0x686E, 0x686F, 0x6870, 0x6871, 0x6872, 0x6875, 0x6879,
- 0x687A, 0x687B, 0x687C, 0x6882, 0x6884, 0x6886, 0x6888, 0x6896,
- 0x6898, 0x689A, 0x689C, 0x68A1, 0x68A3, 0x68A5, 0x68A9, 0x68AA,
- 0x68AE, 0x68B2, 0x68BB, 0x68C5, 0x68C8, 0x68CC, 0x68CF,
-};
-static const unsigned short euc_to_utf8_8FC4[] = {
- 0x68D0, 0x68D1, 0x68D3, 0x68D6, 0x68D9, 0x68DC, 0x68DD,
- 0x68E5, 0x68E8, 0x68EA, 0x68EB, 0x68EC, 0x68ED, 0x68F0, 0x68F1,
- 0x68F5, 0x68F6, 0x68FB, 0x68FC, 0x68FD, 0x6906, 0x6909, 0x690A,
- 0x6910, 0x6911, 0x6913, 0x6916, 0x6917, 0x6931, 0x6933, 0x6935,
- 0x6938, 0x693B, 0x6942, 0x6945, 0x6949, 0x694E, 0x6957, 0x695B,
- 0x6963, 0x6964, 0x6965, 0x6966, 0x6968, 0x6969, 0x696C, 0x6970,
- 0x6971, 0x6972, 0x697A, 0x697B, 0x697F, 0x6980, 0x698D, 0x6992,
- 0x6996, 0x6998, 0x69A1, 0x69A5, 0x69A6, 0x69A8, 0x69AB, 0x69AD,
- 0x69AF, 0x69B7, 0x69B8, 0x69BA, 0x69BC, 0x69C5, 0x69C8, 0x69D1,
- 0x69D6, 0x69D7, 0x69E2, 0x69E5, 0x69EE, 0x69EF, 0x69F1, 0x69F3,
- 0x69F5, 0x69FE, 0x6A00, 0x6A01, 0x6A03, 0x6A0F, 0x6A11, 0x6A15,
- 0x6A1A, 0x6A1D, 0x6A20, 0x6A24, 0x6A28, 0x6A30, 0x6A32,
-};
-static const unsigned short euc_to_utf8_8FC5[] = {
- 0x6A34, 0x6A37, 0x6A3B, 0x6A3E, 0x6A3F, 0x6A45, 0x6A46,
- 0x6A49, 0x6A4A, 0x6A4E, 0x6A50, 0x6A51, 0x6A52, 0x6A55, 0x6A56,
- 0x6A5B, 0x6A64, 0x6A67, 0x6A6A, 0x6A71, 0x6A73, 0x6A7E, 0x6A81,
- 0x6A83, 0x6A86, 0x6A87, 0x6A89, 0x6A8B, 0x6A91, 0x6A9B, 0x6A9D,
- 0x6A9E, 0x6A9F, 0x6AA5, 0x6AAB, 0x6AAF, 0x6AB0, 0x6AB1, 0x6AB4,
- 0x6ABD, 0x6ABE, 0x6ABF, 0x6AC6, 0x6AC9, 0x6AC8, 0x6ACC, 0x6AD0,
- 0x6AD4, 0x6AD5, 0x6AD6, 0x6ADC, 0x6ADD, 0x6AE4, 0x6AE7, 0x6AEC,
- 0x6AF0, 0x6AF1, 0x6AF2, 0x6AFC, 0x6AFD, 0x6B02, 0x6B03, 0x6B06,
- 0x6B07, 0x6B09, 0x6B0F, 0x6B10, 0x6B11, 0x6B17, 0x6B1B, 0x6B1E,
- 0x6B24, 0x6B28, 0x6B2B, 0x6B2C, 0x6B2F, 0x6B35, 0x6B36, 0x6B3B,
- 0x6B3F, 0x6B46, 0x6B4A, 0x6B4D, 0x6B52, 0x6B56, 0x6B58, 0x6B5D,
- 0x6B60, 0x6B67, 0x6B6B, 0x6B6E, 0x6B70, 0x6B75, 0x6B7D,
-};
-static const unsigned short euc_to_utf8_8FC6[] = {
- 0x6B7E, 0x6B82, 0x6B85, 0x6B97, 0x6B9B, 0x6B9F, 0x6BA0,
- 0x6BA2, 0x6BA3, 0x6BA8, 0x6BA9, 0x6BAC, 0x6BAD, 0x6BAE, 0x6BB0,
- 0x6BB8, 0x6BB9, 0x6BBD, 0x6BBE, 0x6BC3, 0x6BC4, 0x6BC9, 0x6BCC,
- 0x6BD6, 0x6BDA, 0x6BE1, 0x6BE3, 0x6BE6, 0x6BE7, 0x6BEE, 0x6BF1,
- 0x6BF7, 0x6BF9, 0x6BFF, 0x6C02, 0x6C04, 0x6C05, 0x6C09, 0x6C0D,
- 0x6C0E, 0x6C10, 0x6C12, 0x6C19, 0x6C1F, 0x6C26, 0x6C27, 0x6C28,
- 0x6C2C, 0x6C2E, 0x6C33, 0x6C35, 0x6C36, 0x6C3A, 0x6C3B, 0x6C3F,
- 0x6C4A, 0x6C4B, 0x6C4D, 0x6C4F, 0x6C52, 0x6C54, 0x6C59, 0x6C5B,
- 0x6C5C, 0x6C6B, 0x6C6D, 0x6C6F, 0x6C74, 0x6C76, 0x6C78, 0x6C79,
- 0x6C7B, 0x6C85, 0x6C86, 0x6C87, 0x6C89, 0x6C94, 0x6C95, 0x6C97,
- 0x6C98, 0x6C9C, 0x6C9F, 0x6CB0, 0x6CB2, 0x6CB4, 0x6CC2, 0x6CC6,
- 0x6CCD, 0x6CCF, 0x6CD0, 0x6CD1, 0x6CD2, 0x6CD4, 0x6CD6,
-};
-static const unsigned short euc_to_utf8_8FC7[] = {
- 0x6CDA, 0x6CDC, 0x6CE0, 0x6CE7, 0x6CE9, 0x6CEB, 0x6CEC,
- 0x6CEE, 0x6CF2, 0x6CF4, 0x6D04, 0x6D07, 0x6D0A, 0x6D0E, 0x6D0F,
- 0x6D11, 0x6D13, 0x6D1A, 0x6D26, 0x6D27, 0x6D28, 0x6C67, 0x6D2E,
- 0x6D2F, 0x6D31, 0x6D39, 0x6D3C, 0x6D3F, 0x6D57, 0x6D5E, 0x6D5F,
- 0x6D61, 0x6D65, 0x6D67, 0x6D6F, 0x6D70, 0x6D7C, 0x6D82, 0x6D87,
- 0x6D91, 0x6D92, 0x6D94, 0x6D96, 0x6D97, 0x6D98, 0x6DAA, 0x6DAC,
- 0x6DB4, 0x6DB7, 0x6DB9, 0x6DBD, 0x6DBF, 0x6DC4, 0x6DC8, 0x6DCA,
- 0x6DCE, 0x6DCF, 0x6DD6, 0x6DDB, 0x6DDD, 0x6DDF, 0x6DE0, 0x6DE2,
- 0x6DE5, 0x6DE9, 0x6DEF, 0x6DF0, 0x6DF4, 0x6DF6, 0x6DFC, 0x6E00,
- 0x6E04, 0x6E1E, 0x6E22, 0x6E27, 0x6E32, 0x6E36, 0x6E39, 0x6E3B,
- 0x6E3C, 0x6E44, 0x6E45, 0x6E48, 0x6E49, 0x6E4B, 0x6E4F, 0x6E51,
- 0x6E52, 0x6E53, 0x6E54, 0x6E57, 0x6E5C, 0x6E5D, 0x6E5E,
-};
-static const unsigned short euc_to_utf8_8FC8[] = {
- 0x6E62, 0x6E63, 0x6E68, 0x6E73, 0x6E7B, 0x6E7D, 0x6E8D,
- 0x6E93, 0x6E99, 0x6EA0, 0x6EA7, 0x6EAD, 0x6EAE, 0x6EB1, 0x6EB3,
- 0x6EBB, 0x6EBF, 0x6EC0, 0x6EC1, 0x6EC3, 0x6EC7, 0x6EC8, 0x6ECA,
- 0x6ECD, 0x6ECE, 0x6ECF, 0x6EEB, 0x6EED, 0x6EEE, 0x6EF9, 0x6EFB,
- 0x6EFD, 0x6F04, 0x6F08, 0x6F0A, 0x6F0C, 0x6F0D, 0x6F16, 0x6F18,
- 0x6F1A, 0x6F1B, 0x6F26, 0x6F29, 0x6F2A, 0x6F2F, 0x6F30, 0x6F33,
- 0x6F36, 0x6F3B, 0x6F3C, 0x6F2D, 0x6F4F, 0x6F51, 0x6F52, 0x6F53,
- 0x6F57, 0x6F59, 0x6F5A, 0x6F5D, 0x6F5E, 0x6F61, 0x6F62, 0x6F68,
- 0x6F6C, 0x6F7D, 0x6F7E, 0x6F83, 0x6F87, 0x6F88, 0x6F8B, 0x6F8C,
- 0x6F8D, 0x6F90, 0x6F92, 0x6F93, 0x6F94, 0x6F96, 0x6F9A, 0x6F9F,
- 0x6FA0, 0x6FA5, 0x6FA6, 0x6FA7, 0x6FA8, 0x6FAE, 0x6FAF, 0x6FB0,
- 0x6FB5, 0x6FB6, 0x6FBC, 0x6FC5, 0x6FC7, 0x6FC8, 0x6FCA,
-};
-static const unsigned short euc_to_utf8_8FC9[] = {
- 0x6FDA, 0x6FDE, 0x6FE8, 0x6FE9, 0x6FF0, 0x6FF5, 0x6FF9,
- 0x6FFC, 0x6FFD, 0x7000, 0x7005, 0x7006, 0x7007, 0x700D, 0x7017,
- 0x7020, 0x7023, 0x702F, 0x7034, 0x7037, 0x7039, 0x703C, 0x7043,
- 0x7044, 0x7048, 0x7049, 0x704A, 0x704B, 0x7054, 0x7055, 0x705D,
- 0x705E, 0x704E, 0x7064, 0x7065, 0x706C, 0x706E, 0x7075, 0x7076,
- 0x707E, 0x7081, 0x7085, 0x7086, 0x7094, 0x7095, 0x7096, 0x7097,
- 0x7098, 0x709B, 0x70A4, 0x70AB, 0x70B0, 0x70B1, 0x70B4, 0x70B7,
- 0x70CA, 0x70D1, 0x70D3, 0x70D4, 0x70D5, 0x70D6, 0x70D8, 0x70DC,
- 0x70E4, 0x70FA, 0x7103, 0x7104, 0x7105, 0x7106, 0x7107, 0x710B,
- 0x710C, 0x710F, 0x711E, 0x7120, 0x712B, 0x712D, 0x712F, 0x7130,
- 0x7131, 0x7138, 0x7141, 0x7145, 0x7146, 0x7147, 0x714A, 0x714B,
- 0x7150, 0x7152, 0x7157, 0x715A, 0x715C, 0x715E, 0x7160,
-};
-static const unsigned short euc_to_utf8_8FCA[] = {
- 0x7168, 0x7179, 0x7180, 0x7185, 0x7187, 0x718C, 0x7192,
- 0x719A, 0x719B, 0x71A0, 0x71A2, 0x71AF, 0x71B0, 0x71B2, 0x71B3,
- 0x71BA, 0x71BF, 0x71C0, 0x71C1, 0x71C4, 0x71CB, 0x71CC, 0x71D3,
- 0x71D6, 0x71D9, 0x71DA, 0x71DC, 0x71F8, 0x71FE, 0x7200, 0x7207,
- 0x7208, 0x7209, 0x7213, 0x7217, 0x721A, 0x721D, 0x721F, 0x7224,
- 0x722B, 0x722F, 0x7234, 0x7238, 0x7239, 0x7241, 0x7242, 0x7243,
- 0x7245, 0x724E, 0x724F, 0x7250, 0x7253, 0x7255, 0x7256, 0x725A,
- 0x725C, 0x725E, 0x7260, 0x7263, 0x7268, 0x726B, 0x726E, 0x726F,
- 0x7271, 0x7277, 0x7278, 0x727B, 0x727C, 0x727F, 0x7284, 0x7289,
- 0x728D, 0x728E, 0x7293, 0x729B, 0x72A8, 0x72AD, 0x72AE, 0x72B1,
- 0x72B4, 0x72BE, 0x72C1, 0x72C7, 0x72C9, 0x72CC, 0x72D5, 0x72D6,
- 0x72D8, 0x72DF, 0x72E5, 0x72F3, 0x72F4, 0x72FA, 0x72FB,
-};
-static const unsigned short euc_to_utf8_8FCB[] = {
- 0x72FE, 0x7302, 0x7304, 0x7305, 0x7307, 0x730B, 0x730D,
- 0x7312, 0x7313, 0x7318, 0x7319, 0x731E, 0x7322, 0x7324, 0x7327,
- 0x7328, 0x732C, 0x7331, 0x7332, 0x7335, 0x733A, 0x733B, 0x733D,
- 0x7343, 0x734D, 0x7350, 0x7352, 0x7356, 0x7358, 0x735D, 0x735E,
- 0x735F, 0x7360, 0x7366, 0x7367, 0x7369, 0x736B, 0x736C, 0x736E,
- 0x736F, 0x7371, 0x7377, 0x7379, 0x737C, 0x7380, 0x7381, 0x7383,
- 0x7385, 0x7386, 0x738E, 0x7390, 0x7393, 0x7395, 0x7397, 0x7398,
- 0x739C, 0x739E, 0x739F, 0x73A0, 0x73A2, 0x73A5, 0x73A6, 0x73AA,
- 0x73AB, 0x73AD, 0x73B5, 0x73B7, 0x73B9, 0x73BC, 0x73BD, 0x73BF,
- 0x73C5, 0x73C6, 0x73C9, 0x73CB, 0x73CC, 0x73CF, 0x73D2, 0x73D3,
- 0x73D6, 0x73D9, 0x73DD, 0x73E1, 0x73E3, 0x73E6, 0x73E7, 0x73E9,
- 0x73F4, 0x73F5, 0x73F7, 0x73F9, 0x73FA, 0x73FB, 0x73FD,
-};
-static const unsigned short euc_to_utf8_8FCC[] = {
- 0x73FF, 0x7400, 0x7401, 0x7404, 0x7407, 0x740A, 0x7411,
- 0x741A, 0x741B, 0x7424, 0x7426, 0x7428, 0x7429, 0x742A, 0x742B,
- 0x742C, 0x742D, 0x742E, 0x742F, 0x7430, 0x7431, 0x7439, 0x7440,
- 0x7443, 0x7444, 0x7446, 0x7447, 0x744B, 0x744D, 0x7451, 0x7452,
- 0x7457, 0x745D, 0x7462, 0x7466, 0x7467, 0x7468, 0x746B, 0x746D,
- 0x746E, 0x7471, 0x7472, 0x7480, 0x7481, 0x7485, 0x7486, 0x7487,
- 0x7489, 0x748F, 0x7490, 0x7491, 0x7492, 0x7498, 0x7499, 0x749A,
- 0x749C, 0x749F, 0x74A0, 0x74A1, 0x74A3, 0x74A6, 0x74A8, 0x74A9,
- 0x74AA, 0x74AB, 0x74AE, 0x74AF, 0x74B1, 0x74B2, 0x74B5, 0x74B9,
- 0x74BB, 0x74BF, 0x74C8, 0x74C9, 0x74CC, 0x74D0, 0x74D3, 0x74D8,
- 0x74DA, 0x74DB, 0x74DE, 0x74DF, 0x74E4, 0x74E8, 0x74EA, 0x74EB,
- 0x74EF, 0x74F4, 0x74FA, 0x74FB, 0x74FC, 0x74FF, 0x7506,
-};
-static const unsigned short euc_to_utf8_8FCD[] = {
- 0x7512, 0x7516, 0x7517, 0x7520, 0x7521, 0x7524, 0x7527,
- 0x7529, 0x752A, 0x752F, 0x7536, 0x7539, 0x753D, 0x753E, 0x753F,
- 0x7540, 0x7543, 0x7547, 0x7548, 0x754E, 0x7550, 0x7552, 0x7557,
- 0x755E, 0x755F, 0x7561, 0x756F, 0x7571, 0x7579, 0x757A, 0x757B,
- 0x757C, 0x757D, 0x757E, 0x7581, 0x7585, 0x7590, 0x7592, 0x7593,
- 0x7595, 0x7599, 0x759C, 0x75A2, 0x75A4, 0x75B4, 0x75BA, 0x75BF,
- 0x75C0, 0x75C1, 0x75C4, 0x75C6, 0x75CC, 0x75CE, 0x75CF, 0x75D7,
- 0x75DC, 0x75DF, 0x75E0, 0x75E1, 0x75E4, 0x75E7, 0x75EC, 0x75EE,
- 0x75EF, 0x75F1, 0x75F9, 0x7600, 0x7602, 0x7603, 0x7604, 0x7607,
- 0x7608, 0x760A, 0x760C, 0x760F, 0x7612, 0x7613, 0x7615, 0x7616,
- 0x7619, 0x761B, 0x761C, 0x761D, 0x761E, 0x7623, 0x7625, 0x7626,
- 0x7629, 0x762D, 0x7632, 0x7633, 0x7635, 0x7638, 0x7639,
-};
-static const unsigned short euc_to_utf8_8FCE[] = {
- 0x763A, 0x763C, 0x764A, 0x7640, 0x7641, 0x7643, 0x7644,
- 0x7645, 0x7649, 0x764B, 0x7655, 0x7659, 0x765F, 0x7664, 0x7665,
- 0x766D, 0x766E, 0x766F, 0x7671, 0x7674, 0x7681, 0x7685, 0x768C,
- 0x768D, 0x7695, 0x769B, 0x769C, 0x769D, 0x769F, 0x76A0, 0x76A2,
- 0x76A3, 0x76A4, 0x76A5, 0x76A6, 0x76A7, 0x76A8, 0x76AA, 0x76AD,
- 0x76BD, 0x76C1, 0x76C5, 0x76C9, 0x76CB, 0x76CC, 0x76CE, 0x76D4,
- 0x76D9, 0x76E0, 0x76E6, 0x76E8, 0x76EC, 0x76F0, 0x76F1, 0x76F6,
- 0x76F9, 0x76FC, 0x7700, 0x7706, 0x770A, 0x770E, 0x7712, 0x7714,
- 0x7715, 0x7717, 0x7719, 0x771A, 0x771C, 0x7722, 0x7728, 0x772D,
- 0x772E, 0x772F, 0x7734, 0x7735, 0x7736, 0x7739, 0x773D, 0x773E,
- 0x7742, 0x7745, 0x7746, 0x774A, 0x774D, 0x774E, 0x774F, 0x7752,
- 0x7756, 0x7757, 0x775C, 0x775E, 0x775F, 0x7760, 0x7762,
-};
-static const unsigned short euc_to_utf8_8FCF[] = {
- 0x7764, 0x7767, 0x776A, 0x776C, 0x7770, 0x7772, 0x7773,
- 0x7774, 0x777A, 0x777D, 0x7780, 0x7784, 0x778C, 0x778D, 0x7794,
- 0x7795, 0x7796, 0x779A, 0x779F, 0x77A2, 0x77A7, 0x77AA, 0x77AE,
- 0x77AF, 0x77B1, 0x77B5, 0x77BE, 0x77C3, 0x77C9, 0x77D1, 0x77D2,
- 0x77D5, 0x77D9, 0x77DE, 0x77DF, 0x77E0, 0x77E4, 0x77E6, 0x77EA,
- 0x77EC, 0x77F0, 0x77F1, 0x77F4, 0x77F8, 0x77FB, 0x7805, 0x7806,
- 0x7809, 0x780D, 0x780E, 0x7811, 0x781D, 0x7821, 0x7822, 0x7823,
- 0x782D, 0x782E, 0x7830, 0x7835, 0x7837, 0x7843, 0x7844, 0x7847,
- 0x7848, 0x784C, 0x784E, 0x7852, 0x785C, 0x785E, 0x7860, 0x7861,
- 0x7863, 0x7864, 0x7868, 0x786A, 0x786E, 0x787A, 0x787E, 0x788A,
- 0x788F, 0x7894, 0x7898, 0x78A1, 0x789D, 0x789E, 0x789F, 0x78A4,
- 0x78A8, 0x78AC, 0x78AD, 0x78B0, 0x78B1, 0x78B2, 0x78B3,
-};
-static const unsigned short euc_to_utf8_8FD0[] = {
- 0x78BB, 0x78BD, 0x78BF, 0x78C7, 0x78C8, 0x78C9, 0x78CC,
- 0x78CE, 0x78D2, 0x78D3, 0x78D5, 0x78D6, 0x78E4, 0x78DB, 0x78DF,
- 0x78E0, 0x78E1, 0x78E6, 0x78EA, 0x78F2, 0x78F3, 0x7900, 0x78F6,
- 0x78F7, 0x78FA, 0x78FB, 0x78FF, 0x7906, 0x790C, 0x7910, 0x791A,
- 0x791C, 0x791E, 0x791F, 0x7920, 0x7925, 0x7927, 0x7929, 0x792D,
- 0x7931, 0x7934, 0x7935, 0x793B, 0x793D, 0x793F, 0x7944, 0x7945,
- 0x7946, 0x794A, 0x794B, 0x794F, 0x7951, 0x7954, 0x7958, 0x795B,
- 0x795C, 0x7967, 0x7969, 0x796B, 0x7972, 0x7979, 0x797B, 0x797C,
- 0x797E, 0x798B, 0x798C, 0x7991, 0x7993, 0x7994, 0x7995, 0x7996,
- 0x7998, 0x799B, 0x799C, 0x79A1, 0x79A8, 0x79A9, 0x79AB, 0x79AF,
- 0x79B1, 0x79B4, 0x79B8, 0x79BB, 0x79C2, 0x79C4, 0x79C7, 0x79C8,
- 0x79CA, 0x79CF, 0x79D4, 0x79D6, 0x79DA, 0x79DD, 0x79DE,
-};
-static const unsigned short euc_to_utf8_8FD1[] = {
- 0x79E0, 0x79E2, 0x79E5, 0x79EA, 0x79EB, 0x79ED, 0x79F1,
- 0x79F8, 0x79FC, 0x7A02, 0x7A03, 0x7A07, 0x7A09, 0x7A0A, 0x7A0C,
- 0x7A11, 0x7A15, 0x7A1B, 0x7A1E, 0x7A21, 0x7A27, 0x7A2B, 0x7A2D,
- 0x7A2F, 0x7A30, 0x7A34, 0x7A35, 0x7A38, 0x7A39, 0x7A3A, 0x7A44,
- 0x7A45, 0x7A47, 0x7A48, 0x7A4C, 0x7A55, 0x7A56, 0x7A59, 0x7A5C,
- 0x7A5D, 0x7A5F, 0x7A60, 0x7A65, 0x7A67, 0x7A6A, 0x7A6D, 0x7A75,
- 0x7A78, 0x7A7E, 0x7A80, 0x7A82, 0x7A85, 0x7A86, 0x7A8A, 0x7A8B,
- 0x7A90, 0x7A91, 0x7A94, 0x7A9E, 0x7AA0, 0x7AA3, 0x7AAC, 0x7AB3,
- 0x7AB5, 0x7AB9, 0x7ABB, 0x7ABC, 0x7AC6, 0x7AC9, 0x7ACC, 0x7ACE,
- 0x7AD1, 0x7ADB, 0x7AE8, 0x7AE9, 0x7AEB, 0x7AEC, 0x7AF1, 0x7AF4,
- 0x7AFB, 0x7AFD, 0x7AFE, 0x7B07, 0x7B14, 0x7B1F, 0x7B23, 0x7B27,
- 0x7B29, 0x7B2A, 0x7B2B, 0x7B2D, 0x7B2E, 0x7B2F, 0x7B30,
-};
-static const unsigned short euc_to_utf8_8FD2[] = {
- 0x7B31, 0x7B34, 0x7B3D, 0x7B3F, 0x7B40, 0x7B41, 0x7B47,
- 0x7B4E, 0x7B55, 0x7B60, 0x7B64, 0x7B66, 0x7B69, 0x7B6A, 0x7B6D,
- 0x7B6F, 0x7B72, 0x7B73, 0x7B77, 0x7B84, 0x7B89, 0x7B8E, 0x7B90,
- 0x7B91, 0x7B96, 0x7B9B, 0x7B9E, 0x7BA0, 0x7BA5, 0x7BAC, 0x7BAF,
- 0x7BB0, 0x7BB2, 0x7BB5, 0x7BB6, 0x7BBA, 0x7BBB, 0x7BBC, 0x7BBD,
- 0x7BC2, 0x7BC5, 0x7BC8, 0x7BCA, 0x7BD4, 0x7BD6, 0x7BD7, 0x7BD9,
- 0x7BDA, 0x7BDB, 0x7BE8, 0x7BEA, 0x7BF2, 0x7BF4, 0x7BF5, 0x7BF8,
- 0x7BF9, 0x7BFA, 0x7BFC, 0x7BFE, 0x7C01, 0x7C02, 0x7C03, 0x7C04,
- 0x7C06, 0x7C09, 0x7C0B, 0x7C0C, 0x7C0E, 0x7C0F, 0x7C19, 0x7C1B,
- 0x7C20, 0x7C25, 0x7C26, 0x7C28, 0x7C2C, 0x7C31, 0x7C33, 0x7C34,
- 0x7C36, 0x7C39, 0x7C3A, 0x7C46, 0x7C4A, 0x7C55, 0x7C51, 0x7C52,
- 0x7C53, 0x7C59, 0x7C5A, 0x7C5B, 0x7C5C, 0x7C5D, 0x7C5E,
-};
-static const unsigned short euc_to_utf8_8FD3[] = {
- 0x7C61, 0x7C63, 0x7C67, 0x7C69, 0x7C6D, 0x7C6E, 0x7C70,
- 0x7C72, 0x7C79, 0x7C7C, 0x7C7D, 0x7C86, 0x7C87, 0x7C8F, 0x7C94,
- 0x7C9E, 0x7CA0, 0x7CA6, 0x7CB0, 0x7CB6, 0x7CB7, 0x7CBA, 0x7CBB,
- 0x7CBC, 0x7CBF, 0x7CC4, 0x7CC7, 0x7CC8, 0x7CC9, 0x7CCD, 0x7CCF,
- 0x7CD3, 0x7CD4, 0x7CD5, 0x7CD7, 0x7CD9, 0x7CDA, 0x7CDD, 0x7CE6,
- 0x7CE9, 0x7CEB, 0x7CF5, 0x7D03, 0x7D07, 0x7D08, 0x7D09, 0x7D0F,
- 0x7D11, 0x7D12, 0x7D13, 0x7D16, 0x7D1D, 0x7D1E, 0x7D23, 0x7D26,
- 0x7D2A, 0x7D2D, 0x7D31, 0x7D3C, 0x7D3D, 0x7D3E, 0x7D40, 0x7D41,
- 0x7D47, 0x7D48, 0x7D4D, 0x7D51, 0x7D53, 0x7D57, 0x7D59, 0x7D5A,
- 0x7D5C, 0x7D5D, 0x7D65, 0x7D67, 0x7D6A, 0x7D70, 0x7D78, 0x7D7A,
- 0x7D7B, 0x7D7F, 0x7D81, 0x7D82, 0x7D83, 0x7D85, 0x7D86, 0x7D88,
- 0x7D8B, 0x7D8C, 0x7D8D, 0x7D91, 0x7D96, 0x7D97, 0x7D9D,
-};
-static const unsigned short euc_to_utf8_8FD4[] = {
- 0x7D9E, 0x7DA6, 0x7DA7, 0x7DAA, 0x7DB3, 0x7DB6, 0x7DB7,
- 0x7DB9, 0x7DC2, 0x7DC3, 0x7DC4, 0x7DC5, 0x7DC6, 0x7DCC, 0x7DCD,
- 0x7DCE, 0x7DD7, 0x7DD9, 0x7E00, 0x7DE2, 0x7DE5, 0x7DE6, 0x7DEA,
- 0x7DEB, 0x7DED, 0x7DF1, 0x7DF5, 0x7DF6, 0x7DF9, 0x7DFA, 0x7E08,
- 0x7E10, 0x7E11, 0x7E15, 0x7E17, 0x7E1C, 0x7E1D, 0x7E20, 0x7E27,
- 0x7E28, 0x7E2C, 0x7E2D, 0x7E2F, 0x7E33, 0x7E36, 0x7E3F, 0x7E44,
- 0x7E45, 0x7E47, 0x7E4E, 0x7E50, 0x7E52, 0x7E58, 0x7E5F, 0x7E61,
- 0x7E62, 0x7E65, 0x7E6B, 0x7E6E, 0x7E6F, 0x7E73, 0x7E78, 0x7E7E,
- 0x7E81, 0x7E86, 0x7E87, 0x7E8A, 0x7E8D, 0x7E91, 0x7E95, 0x7E98,
- 0x7E9A, 0x7E9D, 0x7E9E, 0x7F3C, 0x7F3B, 0x7F3D, 0x7F3E, 0x7F3F,
- 0x7F43, 0x7F44, 0x7F47, 0x7F4F, 0x7F52, 0x7F53, 0x7F5B, 0x7F5C,
- 0x7F5D, 0x7F61, 0x7F63, 0x7F64, 0x7F65, 0x7F66, 0x7F6D,
-};
-static const unsigned short euc_to_utf8_8FD5[] = {
- 0x7F71, 0x7F7D, 0x7F7E, 0x7F7F, 0x7F80, 0x7F8B, 0x7F8D,
- 0x7F8F, 0x7F90, 0x7F91, 0x7F96, 0x7F97, 0x7F9C, 0x7FA1, 0x7FA2,
- 0x7FA6, 0x7FAA, 0x7FAD, 0x7FB4, 0x7FBC, 0x7FBF, 0x7FC0, 0x7FC3,
- 0x7FC8, 0x7FCE, 0x7FCF, 0x7FDB, 0x7FDF, 0x7FE3, 0x7FE5, 0x7FE8,
- 0x7FEC, 0x7FEE, 0x7FEF, 0x7FF2, 0x7FFA, 0x7FFD, 0x7FFE, 0x7FFF,
- 0x8007, 0x8008, 0x800A, 0x800D, 0x800E, 0x800F, 0x8011, 0x8013,
- 0x8014, 0x8016, 0x801D, 0x801E, 0x801F, 0x8020, 0x8024, 0x8026,
- 0x802C, 0x802E, 0x8030, 0x8034, 0x8035, 0x8037, 0x8039, 0x803A,
- 0x803C, 0x803E, 0x8040, 0x8044, 0x8060, 0x8064, 0x8066, 0x806D,
- 0x8071, 0x8075, 0x8081, 0x8088, 0x808E, 0x809C, 0x809E, 0x80A6,
- 0x80A7, 0x80AB, 0x80B8, 0x80B9, 0x80C8, 0x80CD, 0x80CF, 0x80D2,
- 0x80D4, 0x80D5, 0x80D7, 0x80D8, 0x80E0, 0x80ED, 0x80EE,
-};
-static const unsigned short euc_to_utf8_8FD6[] = {
- 0x80F0, 0x80F2, 0x80F3, 0x80F6, 0x80F9, 0x80FA, 0x80FE,
- 0x8103, 0x810B, 0x8116, 0x8117, 0x8118, 0x811C, 0x811E, 0x8120,
- 0x8124, 0x8127, 0x812C, 0x8130, 0x8135, 0x813A, 0x813C, 0x8145,
- 0x8147, 0x814A, 0x814C, 0x8152, 0x8157, 0x8160, 0x8161, 0x8167,
- 0x8168, 0x8169, 0x816D, 0x816F, 0x8177, 0x8181, 0x8190, 0x8184,
- 0x8185, 0x8186, 0x818B, 0x818E, 0x8196, 0x8198, 0x819B, 0x819E,
- 0x81A2, 0x81AE, 0x81B2, 0x81B4, 0x81BB, 0x81CB, 0x81C3, 0x81C5,
- 0x81CA, 0x81CE, 0x81CF, 0x81D5, 0x81D7, 0x81DB, 0x81DD, 0x81DE,
- 0x81E1, 0x81E4, 0x81EB, 0x81EC, 0x81F0, 0x81F1, 0x81F2, 0x81F5,
- 0x81F6, 0x81F8, 0x81F9, 0x81FD, 0x81FF, 0x8200, 0x8203, 0x820F,
- 0x8213, 0x8214, 0x8219, 0x821A, 0x821D, 0x8221, 0x8222, 0x8228,
- 0x8232, 0x8234, 0x823A, 0x8243, 0x8244, 0x8245, 0x8246,
-};
-static const unsigned short euc_to_utf8_8FD7[] = {
- 0x824B, 0x824E, 0x824F, 0x8251, 0x8256, 0x825C, 0x8260,
- 0x8263, 0x8267, 0x826D, 0x8274, 0x827B, 0x827D, 0x827F, 0x8280,
- 0x8281, 0x8283, 0x8284, 0x8287, 0x8289, 0x828A, 0x828E, 0x8291,
- 0x8294, 0x8296, 0x8298, 0x829A, 0x829B, 0x82A0, 0x82A1, 0x82A3,
- 0x82A4, 0x82A7, 0x82A8, 0x82A9, 0x82AA, 0x82AE, 0x82B0, 0x82B2,
- 0x82B4, 0x82B7, 0x82BA, 0x82BC, 0x82BE, 0x82BF, 0x82C6, 0x82D0,
- 0x82D5, 0x82DA, 0x82E0, 0x82E2, 0x82E4, 0x82E8, 0x82EA, 0x82ED,
- 0x82EF, 0x82F6, 0x82F7, 0x82FD, 0x82FE, 0x8300, 0x8301, 0x8307,
- 0x8308, 0x830A, 0x830B, 0x8354, 0x831B, 0x831D, 0x831E, 0x831F,
- 0x8321, 0x8322, 0x832C, 0x832D, 0x832E, 0x8330, 0x8333, 0x8337,
- 0x833A, 0x833C, 0x833D, 0x8342, 0x8343, 0x8344, 0x8347, 0x834D,
- 0x834E, 0x8351, 0x8355, 0x8356, 0x8357, 0x8370, 0x8378,
-};
-static const unsigned short euc_to_utf8_8FD8[] = {
- 0x837D, 0x837F, 0x8380, 0x8382, 0x8384, 0x8386, 0x838D,
- 0x8392, 0x8394, 0x8395, 0x8398, 0x8399, 0x839B, 0x839C, 0x839D,
- 0x83A6, 0x83A7, 0x83A9, 0x83AC, 0x83BE, 0x83BF, 0x83C0, 0x83C7,
- 0x83C9, 0x83CF, 0x83D0, 0x83D1, 0x83D4, 0x83DD, 0x8353, 0x83E8,
- 0x83EA, 0x83F6, 0x83F8, 0x83F9, 0x83FC, 0x8401, 0x8406, 0x840A,
- 0x840F, 0x8411, 0x8415, 0x8419, 0x83AD, 0x842F, 0x8439, 0x8445,
- 0x8447, 0x8448, 0x844A, 0x844D, 0x844F, 0x8451, 0x8452, 0x8456,
- 0x8458, 0x8459, 0x845A, 0x845C, 0x8460, 0x8464, 0x8465, 0x8467,
- 0x846A, 0x8470, 0x8473, 0x8474, 0x8476, 0x8478, 0x847C, 0x847D,
- 0x8481, 0x8485, 0x8492, 0x8493, 0x8495, 0x849E, 0x84A6, 0x84A8,
- 0x84A9, 0x84AA, 0x84AF, 0x84B1, 0x84B4, 0x84BA, 0x84BD, 0x84BE,
- 0x84C0, 0x84C2, 0x84C7, 0x84C8, 0x84CC, 0x84CF, 0x84D3,
-};
-static const unsigned short euc_to_utf8_8FD9[] = {
- 0x84DC, 0x84E7, 0x84EA, 0x84EF, 0x84F0, 0x84F1, 0x84F2,
- 0x84F7, 0x8532, 0x84FA, 0x84FB, 0x84FD, 0x8502, 0x8503, 0x8507,
- 0x850C, 0x850E, 0x8510, 0x851C, 0x851E, 0x8522, 0x8523, 0x8524,
- 0x8525, 0x8527, 0x852A, 0x852B, 0x852F, 0x8533, 0x8534, 0x8536,
- 0x853F, 0x8546, 0x854F, 0x8550, 0x8551, 0x8552, 0x8553, 0x8556,
- 0x8559, 0x855C, 0x855D, 0x855E, 0x855F, 0x8560, 0x8561, 0x8562,
- 0x8564, 0x856B, 0x856F, 0x8579, 0x857A, 0x857B, 0x857D, 0x857F,
- 0x8581, 0x8585, 0x8586, 0x8589, 0x858B, 0x858C, 0x858F, 0x8593,
- 0x8598, 0x859D, 0x859F, 0x85A0, 0x85A2, 0x85A5, 0x85A7, 0x85B4,
- 0x85B6, 0x85B7, 0x85B8, 0x85BC, 0x85BD, 0x85BE, 0x85BF, 0x85C2,
- 0x85C7, 0x85CA, 0x85CB, 0x85CE, 0x85AD, 0x85D8, 0x85DA, 0x85DF,
- 0x85E0, 0x85E6, 0x85E8, 0x85ED, 0x85F3, 0x85F6, 0x85FC,
-};
-static const unsigned short euc_to_utf8_8FDA[] = {
- 0x85FF, 0x8600, 0x8604, 0x8605, 0x860D, 0x860E, 0x8610,
- 0x8611, 0x8612, 0x8618, 0x8619, 0x861B, 0x861E, 0x8621, 0x8627,
- 0x8629, 0x8636, 0x8638, 0x863A, 0x863C, 0x863D, 0x8640, 0x8642,
- 0x8646, 0x8652, 0x8653, 0x8656, 0x8657, 0x8658, 0x8659, 0x865D,
- 0x8660, 0x8661, 0x8662, 0x8663, 0x8664, 0x8669, 0x866C, 0x866F,
- 0x8675, 0x8676, 0x8677, 0x867A, 0x868D, 0x8691, 0x8696, 0x8698,
- 0x869A, 0x869C, 0x86A1, 0x86A6, 0x86A7, 0x86A8, 0x86AD, 0x86B1,
- 0x86B3, 0x86B4, 0x86B5, 0x86B7, 0x86B8, 0x86B9, 0x86BF, 0x86C0,
- 0x86C1, 0x86C3, 0x86C5, 0x86D1, 0x86D2, 0x86D5, 0x86D7, 0x86DA,
- 0x86DC, 0x86E0, 0x86E3, 0x86E5, 0x86E7, 0x8688, 0x86FA, 0x86FC,
- 0x86FD, 0x8704, 0x8705, 0x8707, 0x870B, 0x870E, 0x870F, 0x8710,
- 0x8713, 0x8714, 0x8719, 0x871E, 0x871F, 0x8721, 0x8723,
-};
-static const unsigned short euc_to_utf8_8FDB[] = {
- 0x8728, 0x872E, 0x872F, 0x8731, 0x8732, 0x8739, 0x873A,
- 0x873C, 0x873D, 0x873E, 0x8740, 0x8743, 0x8745, 0x874D, 0x8758,
- 0x875D, 0x8761, 0x8764, 0x8765, 0x876F, 0x8771, 0x8772, 0x877B,
- 0x8783, 0x8784, 0x8785, 0x8786, 0x8787, 0x8788, 0x8789, 0x878B,
- 0x878C, 0x8790, 0x8793, 0x8795, 0x8797, 0x8798, 0x8799, 0x879E,
- 0x87A0, 0x87A3, 0x87A7, 0x87AC, 0x87AD, 0x87AE, 0x87B1, 0x87B5,
- 0x87BE, 0x87BF, 0x87C1, 0x87C8, 0x87C9, 0x87CA, 0x87CE, 0x87D5,
- 0x87D6, 0x87D9, 0x87DA, 0x87DC, 0x87DF, 0x87E2, 0x87E3, 0x87E4,
- 0x87EA, 0x87EB, 0x87ED, 0x87F1, 0x87F3, 0x87F8, 0x87FA, 0x87FF,
- 0x8801, 0x8803, 0x8806, 0x8809, 0x880A, 0x880B, 0x8810, 0x8819,
- 0x8812, 0x8813, 0x8814, 0x8818, 0x881A, 0x881B, 0x881C, 0x881E,
- 0x881F, 0x8828, 0x882D, 0x882E, 0x8830, 0x8832, 0x8835,
-};
-static const unsigned short euc_to_utf8_8FDC[] = {
- 0x883A, 0x883C, 0x8841, 0x8843, 0x8845, 0x8848, 0x8849,
- 0x884A, 0x884B, 0x884E, 0x8851, 0x8855, 0x8856, 0x8858, 0x885A,
- 0x885C, 0x885F, 0x8860, 0x8864, 0x8869, 0x8871, 0x8879, 0x887B,
- 0x8880, 0x8898, 0x889A, 0x889B, 0x889C, 0x889F, 0x88A0, 0x88A8,
- 0x88AA, 0x88BA, 0x88BD, 0x88BE, 0x88C0, 0x88CA, 0x88CB, 0x88CC,
- 0x88CD, 0x88CE, 0x88D1, 0x88D2, 0x88D3, 0x88DB, 0x88DE, 0x88E7,
- 0x88EF, 0x88F0, 0x88F1, 0x88F5, 0x88F7, 0x8901, 0x8906, 0x890D,
- 0x890E, 0x890F, 0x8915, 0x8916, 0x8918, 0x8919, 0x891A, 0x891C,
- 0x8920, 0x8926, 0x8927, 0x8928, 0x8930, 0x8931, 0x8932, 0x8935,
- 0x8939, 0x893A, 0x893E, 0x8940, 0x8942, 0x8945, 0x8946, 0x8949,
- 0x894F, 0x8952, 0x8957, 0x895A, 0x895B, 0x895C, 0x8961, 0x8962,
- 0x8963, 0x896B, 0x896E, 0x8970, 0x8973, 0x8975, 0x897A,
-};
-static const unsigned short euc_to_utf8_8FDD[] = {
- 0x897B, 0x897C, 0x897D, 0x8989, 0x898D, 0x8990, 0x8994,
- 0x8995, 0x899B, 0x899C, 0x899F, 0x89A0, 0x89A5, 0x89B0, 0x89B4,
- 0x89B5, 0x89B6, 0x89B7, 0x89BC, 0x89D4, 0x89D5, 0x89D6, 0x89D7,
- 0x89D8, 0x89E5, 0x89E9, 0x89EB, 0x89ED, 0x89F1, 0x89F3, 0x89F6,
- 0x89F9, 0x89FD, 0x89FF, 0x8A04, 0x8A05, 0x8A07, 0x8A0F, 0x8A11,
- 0x8A12, 0x8A14, 0x8A15, 0x8A1E, 0x8A20, 0x8A22, 0x8A24, 0x8A26,
- 0x8A2B, 0x8A2C, 0x8A2F, 0x8A35, 0x8A37, 0x8A3D, 0x8A3E, 0x8A40,
- 0x8A43, 0x8A45, 0x8A47, 0x8A49, 0x8A4D, 0x8A4E, 0x8A53, 0x8A56,
- 0x8A57, 0x8A58, 0x8A5C, 0x8A5D, 0x8A61, 0x8A65, 0x8A67, 0x8A75,
- 0x8A76, 0x8A77, 0x8A79, 0x8A7A, 0x8A7B, 0x8A7E, 0x8A7F, 0x8A80,
- 0x8A83, 0x8A86, 0x8A8B, 0x8A8F, 0x8A90, 0x8A92, 0x8A96, 0x8A97,
- 0x8A99, 0x8A9F, 0x8AA7, 0x8AA9, 0x8AAE, 0x8AAF, 0x8AB3,
-};
-static const unsigned short euc_to_utf8_8FDE[] = {
- 0x8AB6, 0x8AB7, 0x8ABB, 0x8ABE, 0x8AC3, 0x8AC6, 0x8AC8,
- 0x8AC9, 0x8ACA, 0x8AD1, 0x8AD3, 0x8AD4, 0x8AD5, 0x8AD7, 0x8ADD,
- 0x8ADF, 0x8AEC, 0x8AF0, 0x8AF4, 0x8AF5, 0x8AF6, 0x8AFC, 0x8AFF,
- 0x8B05, 0x8B06, 0x8B0B, 0x8B11, 0x8B1C, 0x8B1E, 0x8B1F, 0x8B0A,
- 0x8B2D, 0x8B30, 0x8B37, 0x8B3C, 0x8B42, 0x8B43, 0x8B44, 0x8B45,
- 0x8B46, 0x8B48, 0x8B52, 0x8B53, 0x8B54, 0x8B59, 0x8B4D, 0x8B5E,
- 0x8B63, 0x8B6D, 0x8B76, 0x8B78, 0x8B79, 0x8B7C, 0x8B7E, 0x8B81,
- 0x8B84, 0x8B85, 0x8B8B, 0x8B8D, 0x8B8F, 0x8B94, 0x8B95, 0x8B9C,
- 0x8B9E, 0x8B9F, 0x8C38, 0x8C39, 0x8C3D, 0x8C3E, 0x8C45, 0x8C47,
- 0x8C49, 0x8C4B, 0x8C4F, 0x8C51, 0x8C53, 0x8C54, 0x8C57, 0x8C58,
- 0x8C5B, 0x8C5D, 0x8C59, 0x8C63, 0x8C64, 0x8C66, 0x8C68, 0x8C69,
- 0x8C6D, 0x8C73, 0x8C75, 0x8C76, 0x8C7B, 0x8C7E, 0x8C86,
-};
-static const unsigned short euc_to_utf8_8FDF[] = {
- 0x8C87, 0x8C8B, 0x8C90, 0x8C92, 0x8C93, 0x8C99, 0x8C9B,
- 0x8C9C, 0x8CA4, 0x8CB9, 0x8CBA, 0x8CC5, 0x8CC6, 0x8CC9, 0x8CCB,
- 0x8CCF, 0x8CD6, 0x8CD5, 0x8CD9, 0x8CDD, 0x8CE1, 0x8CE8, 0x8CEC,
- 0x8CEF, 0x8CF0, 0x8CF2, 0x8CF5, 0x8CF7, 0x8CF8, 0x8CFE, 0x8CFF,
- 0x8D01, 0x8D03, 0x8D09, 0x8D12, 0x8D17, 0x8D1B, 0x8D65, 0x8D69,
- 0x8D6C, 0x8D6E, 0x8D7F, 0x8D82, 0x8D84, 0x8D88, 0x8D8D, 0x8D90,
- 0x8D91, 0x8D95, 0x8D9E, 0x8D9F, 0x8DA0, 0x8DA6, 0x8DAB, 0x8DAC,
- 0x8DAF, 0x8DB2, 0x8DB5, 0x8DB7, 0x8DB9, 0x8DBB, 0x8DC0, 0x8DC5,
- 0x8DC6, 0x8DC7, 0x8DC8, 0x8DCA, 0x8DCE, 0x8DD1, 0x8DD4, 0x8DD5,
- 0x8DD7, 0x8DD9, 0x8DE4, 0x8DE5, 0x8DE7, 0x8DEC, 0x8DF0, 0x8DBC,
- 0x8DF1, 0x8DF2, 0x8DF4, 0x8DFD, 0x8E01, 0x8E04, 0x8E05, 0x8E06,
- 0x8E0B, 0x8E11, 0x8E14, 0x8E16, 0x8E20, 0x8E21, 0x8E22,
-};
-static const unsigned short euc_to_utf8_8FE0[] = {
- 0x8E23, 0x8E26, 0x8E27, 0x8E31, 0x8E33, 0x8E36, 0x8E37,
- 0x8E38, 0x8E39, 0x8E3D, 0x8E40, 0x8E41, 0x8E4B, 0x8E4D, 0x8E4E,
- 0x8E4F, 0x8E54, 0x8E5B, 0x8E5C, 0x8E5D, 0x8E5E, 0x8E61, 0x8E62,
- 0x8E69, 0x8E6C, 0x8E6D, 0x8E6F, 0x8E70, 0x8E71, 0x8E79, 0x8E7A,
- 0x8E7B, 0x8E82, 0x8E83, 0x8E89, 0x8E90, 0x8E92, 0x8E95, 0x8E9A,
- 0x8E9B, 0x8E9D, 0x8E9E, 0x8EA2, 0x8EA7, 0x8EA9, 0x8EAD, 0x8EAE,
- 0x8EB3, 0x8EB5, 0x8EBA, 0x8EBB, 0x8EC0, 0x8EC1, 0x8EC3, 0x8EC4,
- 0x8EC7, 0x8ECF, 0x8ED1, 0x8ED4, 0x8EDC, 0x8EE8, 0x8EEE, 0x8EF0,
- 0x8EF1, 0x8EF7, 0x8EF9, 0x8EFA, 0x8EED, 0x8F00, 0x8F02, 0x8F07,
- 0x8F08, 0x8F0F, 0x8F10, 0x8F16, 0x8F17, 0x8F18, 0x8F1E, 0x8F20,
- 0x8F21, 0x8F23, 0x8F25, 0x8F27, 0x8F28, 0x8F2C, 0x8F2D, 0x8F2E,
- 0x8F34, 0x8F35, 0x8F36, 0x8F37, 0x8F3A, 0x8F40, 0x8F41,
-};
-static const unsigned short euc_to_utf8_8FE1[] = {
- 0x8F43, 0x8F47, 0x8F4F, 0x8F51, 0x8F52, 0x8F53, 0x8F54,
- 0x8F55, 0x8F58, 0x8F5D, 0x8F5E, 0x8F65, 0x8F9D, 0x8FA0, 0x8FA1,
- 0x8FA4, 0x8FA5, 0x8FA6, 0x8FB5, 0x8FB6, 0x8FB8, 0x8FBE, 0x8FC0,
- 0x8FC1, 0x8FC6, 0x8FCA, 0x8FCB, 0x8FCD, 0x8FD0, 0x8FD2, 0x8FD3,
- 0x8FD5, 0x8FE0, 0x8FE3, 0x8FE4, 0x8FE8, 0x8FEE, 0x8FF1, 0x8FF5,
- 0x8FF6, 0x8FFB, 0x8FFE, 0x9002, 0x9004, 0x9008, 0x900C, 0x9018,
- 0x901B, 0x9028, 0x9029, 0x902F, 0x902A, 0x902C, 0x902D, 0x9033,
- 0x9034, 0x9037, 0x903F, 0x9043, 0x9044, 0x904C, 0x905B, 0x905D,
- 0x9062, 0x9066, 0x9067, 0x906C, 0x9070, 0x9074, 0x9079, 0x9085,
- 0x9088, 0x908B, 0x908C, 0x908E, 0x9090, 0x9095, 0x9097, 0x9098,
- 0x9099, 0x909B, 0x90A0, 0x90A1, 0x90A2, 0x90A5, 0x90B0, 0x90B2,
- 0x90B3, 0x90B4, 0x90B6, 0x90BD, 0x90CC, 0x90BE, 0x90C3,
-};
-static const unsigned short euc_to_utf8_8FE2[] = {
- 0x90C4, 0x90C5, 0x90C7, 0x90C8, 0x90D5, 0x90D7, 0x90D8,
- 0x90D9, 0x90DC, 0x90DD, 0x90DF, 0x90E5, 0x90D2, 0x90F6, 0x90EB,
- 0x90EF, 0x90F0, 0x90F4, 0x90FE, 0x90FF, 0x9100, 0x9104, 0x9105,
- 0x9106, 0x9108, 0x910D, 0x9110, 0x9114, 0x9116, 0x9117, 0x9118,
- 0x911A, 0x911C, 0x911E, 0x9120, 0x9125, 0x9122, 0x9123, 0x9127,
- 0x9129, 0x912E, 0x912F, 0x9131, 0x9134, 0x9136, 0x9137, 0x9139,
- 0x913A, 0x913C, 0x913D, 0x9143, 0x9147, 0x9148, 0x914F, 0x9153,
- 0x9157, 0x9159, 0x915A, 0x915B, 0x9161, 0x9164, 0x9167, 0x916D,
- 0x9174, 0x9179, 0x917A, 0x917B, 0x9181, 0x9183, 0x9185, 0x9186,
- 0x918A, 0x918E, 0x9191, 0x9193, 0x9194, 0x9195, 0x9198, 0x919E,
- 0x91A1, 0x91A6, 0x91A8, 0x91AC, 0x91AD, 0x91AE, 0x91B0, 0x91B1,
- 0x91B2, 0x91B3, 0x91B6, 0x91BB, 0x91BC, 0x91BD, 0x91BF,
-};
-static const unsigned short euc_to_utf8_8FE3[] = {
- 0x91C2, 0x91C3, 0x91C5, 0x91D3, 0x91D4, 0x91D7, 0x91D9,
- 0x91DA, 0x91DE, 0x91E4, 0x91E5, 0x91E9, 0x91EA, 0x91EC, 0x91ED,
- 0x91EE, 0x91EF, 0x91F0, 0x91F1, 0x91F7, 0x91F9, 0x91FB, 0x91FD,
- 0x9200, 0x9201, 0x9204, 0x9205, 0x9206, 0x9207, 0x9209, 0x920A,
- 0x920C, 0x9210, 0x9212, 0x9213, 0x9216, 0x9218, 0x921C, 0x921D,
- 0x9223, 0x9224, 0x9225, 0x9226, 0x9228, 0x922E, 0x922F, 0x9230,
- 0x9233, 0x9235, 0x9236, 0x9238, 0x9239, 0x923A, 0x923C, 0x923E,
- 0x9240, 0x9242, 0x9243, 0x9246, 0x9247, 0x924A, 0x924D, 0x924E,
- 0x924F, 0x9251, 0x9258, 0x9259, 0x925C, 0x925D, 0x9260, 0x9261,
- 0x9265, 0x9267, 0x9268, 0x9269, 0x926E, 0x926F, 0x9270, 0x9275,
- 0x9276, 0x9277, 0x9278, 0x9279, 0x927B, 0x927C, 0x927D, 0x927F,
- 0x9288, 0x9289, 0x928A, 0x928D, 0x928E, 0x9292, 0x9297,
-};
-static const unsigned short euc_to_utf8_8FE4[] = {
- 0x9299, 0x929F, 0x92A0, 0x92A4, 0x92A5, 0x92A7, 0x92A8,
- 0x92AB, 0x92AF, 0x92B2, 0x92B6, 0x92B8, 0x92BA, 0x92BB, 0x92BC,
- 0x92BD, 0x92BF, 0x92C0, 0x92C1, 0x92C2, 0x92C3, 0x92C5, 0x92C6,
- 0x92C7, 0x92C8, 0x92CB, 0x92CC, 0x92CD, 0x92CE, 0x92D0, 0x92D3,
- 0x92D5, 0x92D7, 0x92D8, 0x92D9, 0x92DC, 0x92DD, 0x92DF, 0x92E0,
- 0x92E1, 0x92E3, 0x92E5, 0x92E7, 0x92E8, 0x92EC, 0x92EE, 0x92F0,
- 0x92F9, 0x92FB, 0x92FF, 0x9300, 0x9302, 0x9308, 0x930D, 0x9311,
- 0x9314, 0x9315, 0x931C, 0x931D, 0x931E, 0x931F, 0x9321, 0x9324,
- 0x9325, 0x9327, 0x9329, 0x932A, 0x9333, 0x9334, 0x9336, 0x9337,
- 0x9347, 0x9348, 0x9349, 0x9350, 0x9351, 0x9352, 0x9355, 0x9357,
- 0x9358, 0x935A, 0x935E, 0x9364, 0x9365, 0x9367, 0x9369, 0x936A,
- 0x936D, 0x936F, 0x9370, 0x9371, 0x9373, 0x9374, 0x9376,
-};
-static const unsigned short euc_to_utf8_8FE5[] = {
- 0x937A, 0x937D, 0x937F, 0x9380, 0x9381, 0x9382, 0x9388,
- 0x938A, 0x938B, 0x938D, 0x938F, 0x9392, 0x9395, 0x9398, 0x939B,
- 0x939E, 0x93A1, 0x93A3, 0x93A4, 0x93A6, 0x93A8, 0x93AB, 0x93B4,
- 0x93B5, 0x93B6, 0x93BA, 0x93A9, 0x93C1, 0x93C4, 0x93C5, 0x93C6,
- 0x93C7, 0x93C9, 0x93CA, 0x93CB, 0x93CC, 0x93CD, 0x93D3, 0x93D9,
- 0x93DC, 0x93DE, 0x93DF, 0x93E2, 0x93E6, 0x93E7, 0x93F9, 0x93F7,
- 0x93F8, 0x93FA, 0x93FB, 0x93FD, 0x9401, 0x9402, 0x9404, 0x9408,
- 0x9409, 0x940D, 0x940E, 0x940F, 0x9415, 0x9416, 0x9417, 0x941F,
- 0x942E, 0x942F, 0x9431, 0x9432, 0x9433, 0x9434, 0x943B, 0x943F,
- 0x943D, 0x9443, 0x9445, 0x9448, 0x944A, 0x944C, 0x9455, 0x9459,
- 0x945C, 0x945F, 0x9461, 0x9463, 0x9468, 0x946B, 0x946D, 0x946E,
- 0x946F, 0x9471, 0x9472, 0x9484, 0x9483, 0x9578, 0x9579,
-};
-static const unsigned short euc_to_utf8_8FE6[] = {
- 0x957E, 0x9584, 0x9588, 0x958C, 0x958D, 0x958E, 0x959D,
- 0x959E, 0x959F, 0x95A1, 0x95A6, 0x95A9, 0x95AB, 0x95AC, 0x95B4,
- 0x95B6, 0x95BA, 0x95BD, 0x95BF, 0x95C6, 0x95C8, 0x95C9, 0x95CB,
- 0x95D0, 0x95D1, 0x95D2, 0x95D3, 0x95D9, 0x95DA, 0x95DD, 0x95DE,
- 0x95DF, 0x95E0, 0x95E4, 0x95E6, 0x961D, 0x961E, 0x9622, 0x9624,
- 0x9625, 0x9626, 0x962C, 0x9631, 0x9633, 0x9637, 0x9638, 0x9639,
- 0x963A, 0x963C, 0x963D, 0x9641, 0x9652, 0x9654, 0x9656, 0x9657,
- 0x9658, 0x9661, 0x966E, 0x9674, 0x967B, 0x967C, 0x967E, 0x967F,
- 0x9681, 0x9682, 0x9683, 0x9684, 0x9689, 0x9691, 0x9696, 0x969A,
- 0x969D, 0x969F, 0x96A4, 0x96A5, 0x96A6, 0x96A9, 0x96AE, 0x96AF,
- 0x96B3, 0x96BA, 0x96CA, 0x96D2, 0x5DB2, 0x96D8, 0x96DA, 0x96DD,
- 0x96DE, 0x96DF, 0x96E9, 0x96EF, 0x96F1, 0x96FA, 0x9702,
-};
-static const unsigned short euc_to_utf8_8FE7[] = {
- 0x9703, 0x9705, 0x9709, 0x971A, 0x971B, 0x971D, 0x9721,
- 0x9722, 0x9723, 0x9728, 0x9731, 0x9733, 0x9741, 0x9743, 0x974A,
- 0x974E, 0x974F, 0x9755, 0x9757, 0x9758, 0x975A, 0x975B, 0x9763,
- 0x9767, 0x976A, 0x976E, 0x9773, 0x9776, 0x9777, 0x9778, 0x977B,
- 0x977D, 0x977F, 0x9780, 0x9789, 0x9795, 0x9796, 0x9797, 0x9799,
- 0x979A, 0x979E, 0x979F, 0x97A2, 0x97AC, 0x97AE, 0x97B1, 0x97B2,
- 0x97B5, 0x97B6, 0x97B8, 0x97B9, 0x97BA, 0x97BC, 0x97BE, 0x97BF,
- 0x97C1, 0x97C4, 0x97C5, 0x97C7, 0x97C9, 0x97CA, 0x97CC, 0x97CD,
- 0x97CE, 0x97D0, 0x97D1, 0x97D4, 0x97D7, 0x97D8, 0x97D9, 0x97DD,
- 0x97DE, 0x97E0, 0x97DB, 0x97E1, 0x97E4, 0x97EF, 0x97F1, 0x97F4,
- 0x97F7, 0x97F8, 0x97FA, 0x9807, 0x980A, 0x9819, 0x980D, 0x980E,
- 0x9814, 0x9816, 0x981C, 0x981E, 0x9820, 0x9823, 0x9826,
-};
-static const unsigned short euc_to_utf8_8FE8[] = {
- 0x982B, 0x982E, 0x982F, 0x9830, 0x9832, 0x9833, 0x9835,
- 0x9825, 0x983E, 0x9844, 0x9847, 0x984A, 0x9851, 0x9852, 0x9853,
- 0x9856, 0x9857, 0x9859, 0x985A, 0x9862, 0x9863, 0x9865, 0x9866,
- 0x986A, 0x986C, 0x98AB, 0x98AD, 0x98AE, 0x98B0, 0x98B4, 0x98B7,
- 0x98B8, 0x98BA, 0x98BB, 0x98BF, 0x98C2, 0x98C5, 0x98C8, 0x98CC,
- 0x98E1, 0x98E3, 0x98E5, 0x98E6, 0x98E7, 0x98EA, 0x98F3, 0x98F6,
- 0x9902, 0x9907, 0x9908, 0x9911, 0x9915, 0x9916, 0x9917, 0x991A,
- 0x991B, 0x991C, 0x991F, 0x9922, 0x9926, 0x9927, 0x992B, 0x9931,
- 0x9932, 0x9933, 0x9934, 0x9935, 0x9939, 0x993A, 0x993B, 0x993C,
- 0x9940, 0x9941, 0x9946, 0x9947, 0x9948, 0x994D, 0x994E, 0x9954,
- 0x9958, 0x9959, 0x995B, 0x995C, 0x995E, 0x995F, 0x9960, 0x999B,
- 0x999D, 0x999F, 0x99A6, 0x99B0, 0x99B1, 0x99B2, 0x99B5,
-};
-static const unsigned short euc_to_utf8_8FE9[] = {
- 0x99B9, 0x99BA, 0x99BD, 0x99BF, 0x99C3, 0x99C9, 0x99D3,
- 0x99D4, 0x99D9, 0x99DA, 0x99DC, 0x99DE, 0x99E7, 0x99EA, 0x99EB,
- 0x99EC, 0x99F0, 0x99F4, 0x99F5, 0x99F9, 0x99FD, 0x99FE, 0x9A02,
- 0x9A03, 0x9A04, 0x9A0B, 0x9A0C, 0x9A10, 0x9A11, 0x9A16, 0x9A1E,
- 0x9A20, 0x9A22, 0x9A23, 0x9A24, 0x9A27, 0x9A2D, 0x9A2E, 0x9A33,
- 0x9A35, 0x9A36, 0x9A38, 0x9A47, 0x9A41, 0x9A44, 0x9A4A, 0x9A4B,
- 0x9A4C, 0x9A4E, 0x9A51, 0x9A54, 0x9A56, 0x9A5D, 0x9AAA, 0x9AAC,
- 0x9AAE, 0x9AAF, 0x9AB2, 0x9AB4, 0x9AB5, 0x9AB6, 0x9AB9, 0x9ABB,
- 0x9ABE, 0x9ABF, 0x9AC1, 0x9AC3, 0x9AC6, 0x9AC8, 0x9ACE, 0x9AD0,
- 0x9AD2, 0x9AD5, 0x9AD6, 0x9AD7, 0x9ADB, 0x9ADC, 0x9AE0, 0x9AE4,
- 0x9AE5, 0x9AE7, 0x9AE9, 0x9AEC, 0x9AF2, 0x9AF3, 0x9AF5, 0x9AF9,
- 0x9AFA, 0x9AFD, 0x9AFF, 0x9B00, 0x9B01, 0x9B02, 0x9B03,
-};
-static const unsigned short euc_to_utf8_8FEA[] = {
- 0x9B04, 0x9B05, 0x9B08, 0x9B09, 0x9B0B, 0x9B0C, 0x9B0D,
- 0x9B0E, 0x9B10, 0x9B12, 0x9B16, 0x9B19, 0x9B1B, 0x9B1C, 0x9B20,
- 0x9B26, 0x9B2B, 0x9B2D, 0x9B33, 0x9B34, 0x9B35, 0x9B37, 0x9B39,
- 0x9B3A, 0x9B3D, 0x9B48, 0x9B4B, 0x9B4C, 0x9B55, 0x9B56, 0x9B57,
- 0x9B5B, 0x9B5E, 0x9B61, 0x9B63, 0x9B65, 0x9B66, 0x9B68, 0x9B6A,
- 0x9B6B, 0x9B6C, 0x9B6D, 0x9B6E, 0x9B73, 0x9B75, 0x9B77, 0x9B78,
- 0x9B79, 0x9B7F, 0x9B80, 0x9B84, 0x9B85, 0x9B86, 0x9B87, 0x9B89,
- 0x9B8A, 0x9B8B, 0x9B8D, 0x9B8F, 0x9B90, 0x9B94, 0x9B9A, 0x9B9D,
- 0x9B9E, 0x9BA6, 0x9BA7, 0x9BA9, 0x9BAC, 0x9BB0, 0x9BB1, 0x9BB2,
- 0x9BB7, 0x9BB8, 0x9BBB, 0x9BBC, 0x9BBE, 0x9BBF, 0x9BC1, 0x9BC7,
- 0x9BC8, 0x9BCE, 0x9BD0, 0x9BD7, 0x9BD8, 0x9BDD, 0x9BDF, 0x9BE5,
- 0x9BE7, 0x9BEA, 0x9BEB, 0x9BEF, 0x9BF3, 0x9BF7, 0x9BF8,
-};
-static const unsigned short euc_to_utf8_8FEB[] = {
- 0x9BF9, 0x9BFA, 0x9BFD, 0x9BFF, 0x9C00, 0x9C02, 0x9C0B,
- 0x9C0F, 0x9C11, 0x9C16, 0x9C18, 0x9C19, 0x9C1A, 0x9C1C, 0x9C1E,
- 0x9C22, 0x9C23, 0x9C26, 0x9C27, 0x9C28, 0x9C29, 0x9C2A, 0x9C31,
- 0x9C35, 0x9C36, 0x9C37, 0x9C3D, 0x9C41, 0x9C43, 0x9C44, 0x9C45,
- 0x9C49, 0x9C4A, 0x9C4E, 0x9C4F, 0x9C50, 0x9C53, 0x9C54, 0x9C56,
- 0x9C58, 0x9C5B, 0x9C5D, 0x9C5E, 0x9C5F, 0x9C63, 0x9C69, 0x9C6A,
- 0x9C5C, 0x9C6B, 0x9C68, 0x9C6E, 0x9C70, 0x9C72, 0x9C75, 0x9C77,
- 0x9C7B, 0x9CE6, 0x9CF2, 0x9CF7, 0x9CF9, 0x9D0B, 0x9D02, 0x9D11,
- 0x9D17, 0x9D18, 0x9D1C, 0x9D1D, 0x9D1E, 0x9D2F, 0x9D30, 0x9D32,
- 0x9D33, 0x9D34, 0x9D3A, 0x9D3C, 0x9D45, 0x9D3D, 0x9D42, 0x9D43,
- 0x9D47, 0x9D4A, 0x9D53, 0x9D54, 0x9D5F, 0x9D63, 0x9D62, 0x9D65,
- 0x9D69, 0x9D6A, 0x9D6B, 0x9D70, 0x9D76, 0x9D77, 0x9D7B,
-};
-static const unsigned short euc_to_utf8_8FEC[] = {
- 0x9D7C, 0x9D7E, 0x9D83, 0x9D84, 0x9D86, 0x9D8A, 0x9D8D,
- 0x9D8E, 0x9D92, 0x9D93, 0x9D95, 0x9D96, 0x9D97, 0x9D98, 0x9DA1,
- 0x9DAA, 0x9DAC, 0x9DAE, 0x9DB1, 0x9DB5, 0x9DB9, 0x9DBC, 0x9DBF,
- 0x9DC3, 0x9DC7, 0x9DC9, 0x9DCA, 0x9DD4, 0x9DD5, 0x9DD6, 0x9DD7,
- 0x9DDA, 0x9DDE, 0x9DDF, 0x9DE0, 0x9DE5, 0x9DE7, 0x9DE9, 0x9DEB,
- 0x9DEE, 0x9DF0, 0x9DF3, 0x9DF4, 0x9DFE, 0x9E0A, 0x9E02, 0x9E07,
- 0x9E0E, 0x9E10, 0x9E11, 0x9E12, 0x9E15, 0x9E16, 0x9E19, 0x9E1C,
- 0x9E1D, 0x9E7A, 0x9E7B, 0x9E7C, 0x9E80, 0x9E82, 0x9E83, 0x9E84,
- 0x9E85, 0x9E87, 0x9E8E, 0x9E8F, 0x9E96, 0x9E98, 0x9E9B, 0x9E9E,
- 0x9EA4, 0x9EA8, 0x9EAC, 0x9EAE, 0x9EAF, 0x9EB0, 0x9EB3, 0x9EB4,
- 0x9EB5, 0x9EC6, 0x9EC8, 0x9ECB, 0x9ED5, 0x9EDF, 0x9EE4, 0x9EE7,
- 0x9EEC, 0x9EED, 0x9EEE, 0x9EF0, 0x9EF1, 0x9EF2, 0x9EF5,
-};
-static const unsigned short euc_to_utf8_8FED[] = {
- 0x9EF8, 0x9EFF, 0x9F02, 0x9F03, 0x9F09, 0x9F0F, 0x9F10,
- 0x9F11, 0x9F12, 0x9F14, 0x9F16, 0x9F17, 0x9F19, 0x9F1A, 0x9F1B,
- 0x9F1F, 0x9F22, 0x9F26, 0x9F2A, 0x9F2B, 0x9F2F, 0x9F31, 0x9F32,
- 0x9F34, 0x9F37, 0x9F39, 0x9F3A, 0x9F3C, 0x9F3D, 0x9F3F, 0x9F41,
- 0x9F43, 0x9F44, 0x9F45, 0x9F46, 0x9F47, 0x9F53, 0x9F55, 0x9F56,
- 0x9F57, 0x9F58, 0x9F5A, 0x9F5D, 0x9F5E, 0x9F68, 0x9F69, 0x9F6D,
- 0x9F6E, 0x9F6F, 0x9F70, 0x9F71, 0x9F73, 0x9F75, 0x9F7A, 0x9F7D,
- 0x9F8F, 0x9F90, 0x9F91, 0x9F92, 0x9F94, 0x9F96, 0x9F97, 0x9F9E,
- 0x9FA1, 0x9FA2, 0x9FA3, 0x9FA5, 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,
-};
-static const unsigned short euc_to_utf8_8FF3[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x2170, 0x2171, 0x2172, 0x2173, 0x2174,
- 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x2160, 0x2161,
-};
-static const unsigned short euc_to_utf8_8FF4[] = {
- 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168,
- 0x2169, 0xff07, 0xff02, 0x3231, 0x2116, 0x2121, 0x70bb, 0x4efc,
- 0x50f4, 0x51ec, 0x5307, 0x5324, 0xfa0e, 0x548a, 0x5759, 0xfa0f,
- 0xfa10, 0x589e, 0x5bec, 0x5cf5, 0x5d53, 0xfa11, 0x5fb7, 0x6085,
- 0x6120, 0x654e, 0x663b, 0x6665, 0xfa12, 0xf929, 0x6801, 0xfa13,
- 0xfa14, 0x6a6b, 0x6ae2, 0x6df8, 0x6df2, 0x7028, 0xfa15, 0xfa16,
- 0x7501, 0x7682, 0x769e, 0xfa17, 0x7930, 0xfa18, 0xfa19, 0xfa1a,
- 0xfa1b, 0x7ae7, 0xfa1c, 0xfa1d, 0x7da0, 0x7dd6, 0xfa1e, 0x8362,
- 0xfa1f, 0x85b0, 0xfa20, 0xfa21, 0x8807, 0xfa22, 0x8b7f, 0x8cf4,
- 0x8d76, 0xfa23, 0xfa24, 0xfa25, 0x90de, 0xfa26, 0x9115, 0xfa27,
- 0xfa28, 0x9592, 0xf9dc, 0xfa29, 0x973b, 0x974d, 0x9751, 0xfa2a,
- 0xfa2b, 0xfa2c, 0x999e, 0x9ad9, 0x9b72, 0xfa2d, 0x9ed1,
-};
-#endif /* X0212_ENABLE */
-
-const unsigned short euc_to_utf8_1byte[] = {
- 0xFF61, 0xFF62, 0xFF63, 0xFF64, 0xFF65, 0xFF66, 0xFF67,
- 0xFF68, 0xFF69, 0xFF6A, 0xFF6B, 0xFF6C, 0xFF6D, 0xFF6E, 0xFF6F,
- 0xFF70, 0xFF71, 0xFF72, 0xFF73, 0xFF74, 0xFF75, 0xFF76, 0xFF77,
- 0xFF78, 0xFF79, 0xFF7A, 0xFF7B, 0xFF7C, 0xFF7D, 0xFF7E, 0xFF7F,
- 0xFF80, 0xFF81, 0xFF82, 0xFF83, 0xFF84, 0xFF85, 0xFF86, 0xFF87,
- 0xFF88, 0xFF89, 0xFF8A, 0xFF8B, 0xFF8C, 0xFF8D, 0xFF8E, 0xFF8F,
- 0xFF90, 0xFF91, 0xFF92, 0xFF93, 0xFF94, 0xFF95, 0xFF96, 0xFF97,
- 0xFF98, 0xFF99, 0xFF9A, 0xFF9B, 0xFF9C, 0xFF9D, 0xFF9E, 0xFF9F,
- 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, 0x00A9, 0x2122,
-};
-const unsigned short *const euc_to_utf8_2bytes[] = {
- euc_to_utf8_A1, euc_to_utf8_A2, euc_to_utf8_A3,
- euc_to_utf8_A4, euc_to_utf8_A5, euc_to_utf8_A6, euc_to_utf8_A7,
- euc_to_utf8_A8, euc_to_utf8_A9, euc_to_utf8_AA, euc_to_utf8_AB,
- euc_to_utf8_AC, euc_to_utf8_AD, euc_to_utf8_AE, euc_to_utf8_AF,
- euc_to_utf8_B0, euc_to_utf8_B1, euc_to_utf8_B2, euc_to_utf8_B3,
- euc_to_utf8_B4, euc_to_utf8_B5, euc_to_utf8_B6, euc_to_utf8_B7,
- euc_to_utf8_B8, euc_to_utf8_B9, euc_to_utf8_BA, euc_to_utf8_BB,
- euc_to_utf8_BC, euc_to_utf8_BD, euc_to_utf8_BE, euc_to_utf8_BF,
- euc_to_utf8_C0, euc_to_utf8_C1, euc_to_utf8_C2, euc_to_utf8_C3,
- euc_to_utf8_C4, euc_to_utf8_C5, euc_to_utf8_C6, euc_to_utf8_C7,
- euc_to_utf8_C8, euc_to_utf8_C9, euc_to_utf8_CA, euc_to_utf8_CB,
- euc_to_utf8_CC, euc_to_utf8_CD, euc_to_utf8_CE, euc_to_utf8_CF,
- euc_to_utf8_D0, euc_to_utf8_D1, euc_to_utf8_D2, euc_to_utf8_D3,
- euc_to_utf8_D4, euc_to_utf8_D5, euc_to_utf8_D6, euc_to_utf8_D7,
- euc_to_utf8_D8, euc_to_utf8_D9, euc_to_utf8_DA, euc_to_utf8_DB,
- euc_to_utf8_DC, euc_to_utf8_DD, euc_to_utf8_DE, euc_to_utf8_DF,
- euc_to_utf8_E0, euc_to_utf8_E1, euc_to_utf8_E2, euc_to_utf8_E3,
- euc_to_utf8_E4, euc_to_utf8_E5, euc_to_utf8_E6, euc_to_utf8_E7,
- euc_to_utf8_E8, euc_to_utf8_E9, euc_to_utf8_EA, euc_to_utf8_EB,
- euc_to_utf8_EC, euc_to_utf8_ED, euc_to_utf8_EE, euc_to_utf8_EF,
- euc_to_utf8_F0, euc_to_utf8_F1, euc_to_utf8_F2, euc_to_utf8_F3,
- euc_to_utf8_F4, euc_to_utf8_F5, 0, 0,
- 0, euc_to_utf8_F9, euc_to_utf8_FA, euc_to_utf8_FB,
- euc_to_utf8_FC, 0, 0,
-};
-/* Microsoft UCS Mapping Compatible */
-const unsigned short *const euc_to_utf8_2bytes_ms[] = {
- euc_to_utf8_A1_ms, euc_to_utf8_A2_ms, euc_to_utf8_A3,
- euc_to_utf8_A4, euc_to_utf8_A5, euc_to_utf8_A6, euc_to_utf8_A7,
- euc_to_utf8_A8, euc_to_utf8_A9, euc_to_utf8_AA, euc_to_utf8_AB,
- euc_to_utf8_AC, euc_to_utf8_AD, euc_to_utf8_AE, euc_to_utf8_AF,
- euc_to_utf8_B0, euc_to_utf8_B1, euc_to_utf8_B2, euc_to_utf8_B3,
- euc_to_utf8_B4, euc_to_utf8_B5, euc_to_utf8_B6, euc_to_utf8_B7,
- euc_to_utf8_B8, euc_to_utf8_B9, euc_to_utf8_BA, euc_to_utf8_BB,
- euc_to_utf8_BC, euc_to_utf8_BD, euc_to_utf8_BE, euc_to_utf8_BF,
- euc_to_utf8_C0, euc_to_utf8_C1, euc_to_utf8_C2, euc_to_utf8_C3,
- euc_to_utf8_C4, euc_to_utf8_C5, euc_to_utf8_C6, euc_to_utf8_C7,
- euc_to_utf8_C8, euc_to_utf8_C9, euc_to_utf8_CA, euc_to_utf8_CB,
- euc_to_utf8_CC, euc_to_utf8_CD, euc_to_utf8_CE, euc_to_utf8_CF,
- euc_to_utf8_D0, euc_to_utf8_D1, euc_to_utf8_D2, euc_to_utf8_D3,
- euc_to_utf8_D4, euc_to_utf8_D5, euc_to_utf8_D6, euc_to_utf8_D7,
- euc_to_utf8_D8, euc_to_utf8_D9, euc_to_utf8_DA, euc_to_utf8_DB,
- euc_to_utf8_DC, euc_to_utf8_DD, euc_to_utf8_DE, euc_to_utf8_DF,
- euc_to_utf8_E0, euc_to_utf8_E1, euc_to_utf8_E2, euc_to_utf8_E3,
- euc_to_utf8_E4, euc_to_utf8_E5, euc_to_utf8_E6, euc_to_utf8_E7,
- euc_to_utf8_E8, euc_to_utf8_E9, euc_to_utf8_EA, euc_to_utf8_EB,
- euc_to_utf8_EC, euc_to_utf8_ED, euc_to_utf8_EE, euc_to_utf8_EF,
- euc_to_utf8_F0, euc_to_utf8_F1, euc_to_utf8_F2, euc_to_utf8_F3,
- euc_to_utf8_F4, euc_to_utf8_F5, 0, 0,
- 0, euc_to_utf8_F9, euc_to_utf8_FA, euc_to_utf8_FB,
- euc_to_utf8_FC_ms, 0, 0,
-};
-/* CP10001 */
-const unsigned short *const euc_to_utf8_2bytes_mac[] = {
- euc_to_utf8_A1_ms, euc_to_utf8_A2_ms, euc_to_utf8_A3,
- euc_to_utf8_A4, euc_to_utf8_A5, euc_to_utf8_A6, euc_to_utf8_A7,
- euc_to_utf8_A8, euc_to_utf8_A9, euc_to_utf8_AA, euc_to_utf8_AB,
- euc_to_utf8_AC_mac, euc_to_utf8_AD_mac, euc_to_utf8_AE, euc_to_utf8_AF,
- euc_to_utf8_B0, euc_to_utf8_B1, euc_to_utf8_B2, euc_to_utf8_B3,
- euc_to_utf8_B4, euc_to_utf8_B5, euc_to_utf8_B6, euc_to_utf8_B7,
- euc_to_utf8_B8, euc_to_utf8_B9, euc_to_utf8_BA, euc_to_utf8_BB,
- euc_to_utf8_BC, euc_to_utf8_BD, euc_to_utf8_BE, euc_to_utf8_BF,
- euc_to_utf8_C0, euc_to_utf8_C1, euc_to_utf8_C2, euc_to_utf8_C3,
- euc_to_utf8_C4, euc_to_utf8_C5, euc_to_utf8_C6, euc_to_utf8_C7,
- euc_to_utf8_C8, euc_to_utf8_C9, euc_to_utf8_CA, euc_to_utf8_CB,
- euc_to_utf8_CC, euc_to_utf8_CD, euc_to_utf8_CE, euc_to_utf8_CF,
- euc_to_utf8_D0, euc_to_utf8_D1, euc_to_utf8_D2, euc_to_utf8_D3,
- euc_to_utf8_D4, euc_to_utf8_D5, euc_to_utf8_D6, euc_to_utf8_D7,
- euc_to_utf8_D8, euc_to_utf8_D9, euc_to_utf8_DA, euc_to_utf8_DB,
- euc_to_utf8_DC, euc_to_utf8_DD, euc_to_utf8_DE, euc_to_utf8_DF,
- euc_to_utf8_E0, euc_to_utf8_E1, euc_to_utf8_E2, euc_to_utf8_E3,
- euc_to_utf8_E4, euc_to_utf8_E5, euc_to_utf8_E6, euc_to_utf8_E7,
- euc_to_utf8_E8, euc_to_utf8_E9, euc_to_utf8_EA, euc_to_utf8_EB,
- euc_to_utf8_EC, euc_to_utf8_ED, euc_to_utf8_EE, euc_to_utf8_EF,
- euc_to_utf8_F0, euc_to_utf8_F1, euc_to_utf8_F2, euc_to_utf8_F3,
- euc_to_utf8_F4, euc_to_utf8_F5, 0, 0,
- 0, euc_to_utf8_F9, euc_to_utf8_FA, euc_to_utf8_FB,
- euc_to_utf8_FC_ms, 0, 0,
-};
-const unsigned short *const euc_to_utf8_2bytes_x0213[] = {
- euc_to_utf8_A1, euc_to_utf8_A2_x0213, euc_to_utf8_A3_x0213,
- euc_to_utf8_A4_x0213, euc_to_utf8_A5_x0213, euc_to_utf8_A6_x0213, euc_to_utf8_A7_x0213,
- euc_to_utf8_A8_x0213, euc_to_utf8_A9_x0213, euc_to_utf8_AA_x0213, euc_to_utf8_AB_x0213,
- euc_to_utf8_AC_x0213, euc_to_utf8_AD_x0213, euc_to_utf8_AE_x0213, euc_to_utf8_AF_x0213,
- euc_to_utf8_B0, euc_to_utf8_B1, euc_to_utf8_B2, euc_to_utf8_B3,
- euc_to_utf8_B4, euc_to_utf8_B5, euc_to_utf8_B6, euc_to_utf8_B7,
- euc_to_utf8_B8, euc_to_utf8_B9, euc_to_utf8_BA, euc_to_utf8_BB,
- euc_to_utf8_BC, euc_to_utf8_BD, euc_to_utf8_BE, euc_to_utf8_BF,
- euc_to_utf8_C0, euc_to_utf8_C1, euc_to_utf8_C2, euc_to_utf8_C3,
- euc_to_utf8_C4, euc_to_utf8_C5, euc_to_utf8_C6, euc_to_utf8_C7,
- euc_to_utf8_C8, euc_to_utf8_C9, euc_to_utf8_CA, euc_to_utf8_CB,
- euc_to_utf8_CC, euc_to_utf8_CD, euc_to_utf8_CE, euc_to_utf8_CF_x0213,
- euc_to_utf8_D0, euc_to_utf8_D1, euc_to_utf8_D2, euc_to_utf8_D3,
- euc_to_utf8_D4, euc_to_utf8_D5, euc_to_utf8_D6, euc_to_utf8_D7,
- euc_to_utf8_D8, euc_to_utf8_D9, euc_to_utf8_DA, euc_to_utf8_DB,
- euc_to_utf8_DC, euc_to_utf8_DD, euc_to_utf8_DE, euc_to_utf8_DF,
- euc_to_utf8_E0, euc_to_utf8_E1, euc_to_utf8_E2, euc_to_utf8_E3,
- euc_to_utf8_E4, euc_to_utf8_E5, euc_to_utf8_E6, euc_to_utf8_E7,
- euc_to_utf8_E8, euc_to_utf8_E9, euc_to_utf8_EA, euc_to_utf8_EB,
- euc_to_utf8_EC, euc_to_utf8_ED, euc_to_utf8_EE, euc_to_utf8_EF,
- euc_to_utf8_F0, euc_to_utf8_F1, euc_to_utf8_F2, euc_to_utf8_F3,
- euc_to_utf8_F4_x0213, euc_to_utf8_F5_x0213, euc_to_utf8_F6_x0213, euc_to_utf8_F7_x0213,
- euc_to_utf8_F8_x0213, euc_to_utf8_F9_x0213, euc_to_utf8_FA_x0213, euc_to_utf8_FB_x0213,
- euc_to_utf8_FC_x0213, euc_to_utf8_FD_x0213, euc_to_utf8_FE_x0213,
-};
-
-#ifdef X0212_ENABLE
-const unsigned short *const x0212_to_utf8_2bytes[] = {
- 0, euc_to_utf8_8FA2, 0,
- 0, 0, euc_to_utf8_8FA6, euc_to_utf8_8FA7,
- 0, euc_to_utf8_8FA9, euc_to_utf8_8FAA, euc_to_utf8_8FAB,
- 0, 0, 0, 0,
- euc_to_utf8_8FB0, euc_to_utf8_8FB1, euc_to_utf8_8FB2, euc_to_utf8_8FB3,
- euc_to_utf8_8FB4, euc_to_utf8_8FB5, euc_to_utf8_8FB6, euc_to_utf8_8FB7,
- euc_to_utf8_8FB8, euc_to_utf8_8FB9, euc_to_utf8_8FBA, euc_to_utf8_8FBB,
- euc_to_utf8_8FBC, euc_to_utf8_8FBD, euc_to_utf8_8FBE, euc_to_utf8_8FBF,
- euc_to_utf8_8FC0, euc_to_utf8_8FC1, euc_to_utf8_8FC2, euc_to_utf8_8FC3,
- euc_to_utf8_8FC4, euc_to_utf8_8FC5, euc_to_utf8_8FC6, euc_to_utf8_8FC7,
- euc_to_utf8_8FC8, euc_to_utf8_8FC9, euc_to_utf8_8FCA, euc_to_utf8_8FCB,
- euc_to_utf8_8FCC, euc_to_utf8_8FCD, euc_to_utf8_8FCE, euc_to_utf8_8FCF,
- euc_to_utf8_8FD0, euc_to_utf8_8FD1, euc_to_utf8_8FD2, euc_to_utf8_8FD3,
- euc_to_utf8_8FD4, euc_to_utf8_8FD5, euc_to_utf8_8FD6, euc_to_utf8_8FD7,
- euc_to_utf8_8FD8, euc_to_utf8_8FD9, euc_to_utf8_8FDA, euc_to_utf8_8FDB,
- euc_to_utf8_8FDC, euc_to_utf8_8FDD, euc_to_utf8_8FDE, euc_to_utf8_8FDF,
- euc_to_utf8_8FE0, euc_to_utf8_8FE1, euc_to_utf8_8FE2, euc_to_utf8_8FE3,
- euc_to_utf8_8FE4, euc_to_utf8_8FE5, euc_to_utf8_8FE6, euc_to_utf8_8FE7,
- euc_to_utf8_8FE8, euc_to_utf8_8FE9, euc_to_utf8_8FEA, euc_to_utf8_8FEB,
- euc_to_utf8_8FEC, euc_to_utf8_8FED, 0, 0,
- 0, 0, 0, euc_to_utf8_8FF3,
- euc_to_utf8_8FF4, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0,};
-
-const unsigned short *const x0212_to_utf8_2bytes_x0213[] = {
- euc_to_utf8_8FA1_x0213, euc_to_utf8_8FA2, euc_to_utf8_8FA3_x0213,
- euc_to_utf8_8FA4_x0213, euc_to_utf8_8FA5_x0213, euc_to_utf8_8FA6, euc_to_utf8_8FA7,
- euc_to_utf8_8FA8_x0213, euc_to_utf8_8FA9, euc_to_utf8_8FAA, euc_to_utf8_8FAB,
- euc_to_utf8_8FAC_x0213, euc_to_utf8_8FAD_x0213, euc_to_utf8_8FAE_x0213, euc_to_utf8_8FAF_x0213,
- euc_to_utf8_8FB0, euc_to_utf8_8FB1, euc_to_utf8_8FB2, euc_to_utf8_8FB3,
- euc_to_utf8_8FB4, euc_to_utf8_8FB5, euc_to_utf8_8FB6, euc_to_utf8_8FB7,
- euc_to_utf8_8FB8, euc_to_utf8_8FB9, euc_to_utf8_8FBA, euc_to_utf8_8FBB,
- euc_to_utf8_8FBC, euc_to_utf8_8FBD, euc_to_utf8_8FBE, euc_to_utf8_8FBF,
- euc_to_utf8_8FC0, euc_to_utf8_8FC1, euc_to_utf8_8FC2, euc_to_utf8_8FC3,
- euc_to_utf8_8FC4, euc_to_utf8_8FC5, euc_to_utf8_8FC6, euc_to_utf8_8FC7,
- euc_to_utf8_8FC8, euc_to_utf8_8FC9, euc_to_utf8_8FCA, euc_to_utf8_8FCB,
- euc_to_utf8_8FCC, euc_to_utf8_8FCD, euc_to_utf8_8FCE, euc_to_utf8_8FCF,
- euc_to_utf8_8FD0, euc_to_utf8_8FD1, euc_to_utf8_8FD2, euc_to_utf8_8FD3,
- euc_to_utf8_8FD4, euc_to_utf8_8FD5, euc_to_utf8_8FD6, euc_to_utf8_8FD7,
- euc_to_utf8_8FD8, euc_to_utf8_8FD9, euc_to_utf8_8FDA, euc_to_utf8_8FDB,
- euc_to_utf8_8FDC, euc_to_utf8_8FDD, euc_to_utf8_8FDE, euc_to_utf8_8FDF,
- euc_to_utf8_8FE0, euc_to_utf8_8FE1, euc_to_utf8_8FE2, euc_to_utf8_8FE3,
- euc_to_utf8_8FE4, euc_to_utf8_8FE5, euc_to_utf8_8FE6, euc_to_utf8_8FE7,
- euc_to_utf8_8FE8, euc_to_utf8_8FE9, euc_to_utf8_8FEA, euc_to_utf8_8FEB,
- euc_to_utf8_8FEC, euc_to_utf8_8FED, euc_to_utf8_8FEE_x0213, euc_to_utf8_8FEF_x0213,
- euc_to_utf8_8FF0_x0213, euc_to_utf8_8FF1_x0213, euc_to_utf8_8FF2_x0213, euc_to_utf8_8FF3_x0213,
- euc_to_utf8_8FF4_x0213, euc_to_utf8_8FF5_x0213, euc_to_utf8_8FF6_x0213, euc_to_utf8_8FF7_x0213,
- euc_to_utf8_8FF8_x0213, euc_to_utf8_8FF9_x0213, euc_to_utf8_8FFA_x0213, euc_to_utf8_8FFB_x0213,
- euc_to_utf8_8FFC_x0213, euc_to_utf8_8FFD_x0213, euc_to_utf8_8FFE_x0213,};
-#endif /* X0212_ENABLE */
-
-const unsigned short x0213_combining_chars[sizeof_x0213_combining_chars] = {
- 0x309A, 0x0300, 0x0301, 0x02E5, 0x02E9,
-};
-const unsigned short x0213_combining_table[sizeof_x0213_combining_table][3] = {
- {0x2477, 0x304B, 0x309A},
- {0x2478, 0x304D, 0x309A},
- {0x2479, 0x304F, 0x309A},
- {0x247A, 0x3051, 0x309A},
- {0x247B, 0x3053, 0x309A},
- {0x2577, 0x30AB, 0x309A},
- {0x2578, 0x30AD, 0x309A},
- {0x2579, 0x30AF, 0x309A},
- {0x257A, 0x30B1, 0x309A},
- {0x257B, 0x30B3, 0x309A},
- {0x257C, 0x30BB, 0x309A},
- {0x257D, 0x30C4, 0x309A},
- {0x257E, 0x30C8, 0x309A},
- {0x2678, 0x31F7, 0x309A},
- {0x2B44, 0x00E6, 0x0300},
- {0x2B48, 0x0254, 0x0300},
- {0x2B49, 0x0254, 0x0301},
- {0x2B4A, 0x028C, 0x0300},
- {0x2B4B, 0x028C, 0x0301},
- {0x2B4C, 0x0259, 0x0300},
- {0x2B4D, 0x0259, 0x0301},
- {0x2B4E, 0x025A, 0x0300},
- {0x2B4F, 0x025A, 0x0301},
- {0x2B65, 0x02E9, 0x02E5},
- {0x2B66, 0x02E5, 0x02E9},
-};
-const unsigned short x0213_1_surrogate_table[sizeof_x0213_1_surrogate_table][3] = {
- {0x2E22, 0xD840, 0xDC0B},
- {0x2F42, 0xD844, 0xDE3D},
- {0x2F4C, 0xD844, 0xDF1B},
- {0x2F60, 0xD845, 0xDC6E},
- {0x2F7B, 0xD846, 0xDCBD},
- {0x4F54, 0xD842, 0xDF9F},
- {0x4F63, 0xD845, 0xDEB4},
- {0x4F6E, 0xD847, 0xDE34},
- {0x753A, 0xD84C, 0xDDC4},
- {0x7572, 0xD84D, 0xDDC4},
- {0x7629, 0xD84D, 0xDF3F},
- {0x7632, 0xD84D, 0xDF63},
- {0x7660, 0xD84F, 0xDCFE},
- {0x776C, 0xD851, 0xDFF1},
- {0x787E, 0xD855, 0xDC8E},
- {0x7929, 0xD855, 0xDD0E},
- {0x7947, 0xD855, 0xDF71},
- {0x7954, 0xD856, 0xDDC4},
- {0x796E, 0xD857, 0xDDA1},
- {0x7A5D, 0xD85A, 0xDEFF},
- {0x7B33, 0xD85B, 0xDE40},
- {0x7B49, 0xD85C, 0xDCF4},
- {0x7B6C, 0xD85D, 0xDE84},
- {0x7C49, 0xD860, 0xDE77},
- {0x7C51, 0xD860, 0xDFCD},
- {0x7E66, 0xD868, 0xDD90},
-};
-const unsigned short x0213_2_surrogate_table[sizeof_x0213_2_surrogate_table][3] = {
- {0x2121, 0xD840, 0xDC89},
- {0x212B, 0xD840, 0xDCA2},
- {0x212E, 0xD840, 0xDCA4},
- {0x2136, 0xD840, 0xDDA2},
- {0x2146, 0xD840, 0xDE13},
- {0x2170, 0xD840, 0xDF2B},
- {0x2177, 0xD840, 0xDF81},
- {0x2179, 0xD840, 0xDF71},
- {0x2322, 0xD840, 0xDFF9},
- {0x2325, 0xD841, 0xDC4A},
- {0x2327, 0xD841, 0xDD09},
- {0x2331, 0xD841, 0xDDD6},
- {0x2332, 0xD841, 0xDE28},
- {0x2338, 0xD841, 0xDF4F},
- {0x233F, 0xD842, 0xDC07},
- {0x2341, 0xD842, 0xDC3A},
- {0x234A, 0xD842, 0xDCB9},
- {0x2352, 0xD842, 0xDD7C},
- {0x2353, 0xD842, 0xDD9D},
- {0x2359, 0xD842, 0xDED3},
- {0x235C, 0xD842, 0xDF1D},
- {0x2377, 0xD843, 0xDD45},
- {0x242A, 0xD843, 0xDDE1},
- {0x2431, 0xD843, 0xDE95},
- {0x2432, 0xD843, 0xDE6D},
- {0x243A, 0xD843, 0xDE64},
- {0x243D, 0xD843, 0xDF5F},
- {0x2459, 0xD844, 0xDE01},
- {0x245C, 0xD844, 0xDE55},
- {0x245E, 0xD844, 0xDE7B},
- {0x2463, 0xD844, 0xDE74},
- {0x246A, 0xD844, 0xDEE4},
- {0x246B, 0xD844, 0xDED7},
- {0x2472, 0xD844, 0xDEFD},
- {0x2474, 0xD844, 0xDF36},
- {0x2475, 0xD844, 0xDF44},
- {0x2525, 0xD844, 0xDFC4},
- {0x2532, 0xD845, 0xDC6D},
- {0x253E, 0xD845, 0xDDD7},
- {0x2544, 0xD85B, 0xDC29},
- {0x2547, 0xD845, 0xDE47},
- {0x2555, 0xD845, 0xDF06},
- {0x2556, 0xD845, 0xDF42},
- {0x257E, 0xD846, 0xDDC3},
- {0x2830, 0xD847, 0xDC56},
- {0x2837, 0xD847, 0xDD2D},
- {0x2838, 0xD847, 0xDD45},
- {0x283A, 0xD847, 0xDD78},
- {0x283B, 0xD847, 0xDD62},
- {0x283F, 0xD847, 0xDDA1},
- {0x2840, 0xD847, 0xDD9C},
- {0x2845, 0xD847, 0xDD92},
- {0x2848, 0xD847, 0xDDB7},
- {0x284A, 0xD847, 0xDDE0},
- {0x284B, 0xD847, 0xDE33},
- {0x285B, 0xD847, 0xDF1E},
- {0x2866, 0xD847, 0xDF76},
- {0x286C, 0xD847, 0xDFFA},
- {0x2C22, 0xD848, 0xDD7B},
- {0x2C2B, 0xD848, 0xDF1E},
- {0x2C30, 0xD848, 0xDFAD},
- {0x2C50, 0xD849, 0xDEF3},
- {0x2C65, 0xD84A, 0xDC5B},
- {0x2C6D, 0xD84A, 0xDCAB},
- {0x2C72, 0xD84A, 0xDD8F},
- {0x2D24, 0xD84A, 0xDEB8},
- {0x2D29, 0xD84A, 0xDF4F},
- {0x2D2A, 0xD84A, 0xDF50},
- {0x2D32, 0xD84A, 0xDF46},
- {0x2D34, 0xD84B, 0xDC1D},
- {0x2D35, 0xD84A, 0xDFA6},
- {0x2D39, 0xD84B, 0xDC24},
- {0x2D56, 0xD84B, 0xDDE1},
- {0x2D7D, 0xD84C, 0xDDC3},
- {0x2E23, 0xD84C, 0xDDF5},
- {0x2E24, 0xD84C, 0xDDB6},
- {0x2E3A, 0xD84C, 0xDF72},
- {0x2E3C, 0xD84C, 0xDFD3},
- {0x2E3D, 0xD84C, 0xDFD2},
- {0x2E42, 0xD84C, 0xDFD0},
- {0x2E43, 0xD84C, 0xDFE4},
- {0x2E44, 0xD84C, 0xDFD5},
- {0x2E47, 0xD84C, 0xDFDA},
- {0x2E49, 0xD84C, 0xDFDF},
- {0x2E55, 0xD84D, 0xDC4A},
- {0x2E56, 0xD84D, 0xDC51},
- {0x2E57, 0xD84D, 0xDC4B},
- {0x2E5B, 0xD84D, 0xDC65},
- {0x2E77, 0xD84D, 0xDCE4},
- {0x2E78, 0xD84D, 0xDD5A},
- {0x2F2A, 0xD84D, 0xDD94},
- {0x2F3F, 0xD84D, 0xDE39},
- {0x2F40, 0xD84D, 0xDE47},
- {0x2F42, 0xD84D, 0xDE38},
- {0x2F43, 0xD84D, 0xDE3A},
- {0x2F4E, 0xD84D, 0xDF1C},
- {0x2F59, 0xD84D, 0xDF0C},
- {0x2F61, 0xD84D, 0xDF64},
- {0x2F69, 0xD84D, 0xDFFF},
- {0x2F6A, 0xD84D, 0xDFE7},
- {0x2F70, 0xD84E, 0xDC24},
- {0x2F75, 0xD84E, 0xDC3D},
- {0x6E23, 0xD84E, 0xDE98},
- {0x6E34, 0xD84F, 0xDC7F},
- {0x6E49, 0xD84F, 0xDD00},
- {0x6E5C, 0xD84F, 0xDD40},
- {0x6E5E, 0xD84F, 0xDDFA},
- {0x6E5F, 0xD84F, 0xDDF9},
- {0x6E60, 0xD84F, 0xDDD3},
- {0x6F32, 0xD84F, 0xDF7E},
- {0x6F47, 0xD850, 0xDC96},
- {0x6F4D, 0xD850, 0xDD03},
- {0x6F61, 0xD850, 0xDDC6},
- {0x6F64, 0xD850, 0xDDFE},
- {0x7022, 0xD850, 0xDFBC},
- {0x7033, 0xD851, 0xDE29},
- {0x7039, 0xD851, 0xDEA5},
- {0x7053, 0xD852, 0xDC96},
- {0x707B, 0xD852, 0xDE4D},
- {0x712E, 0xD852, 0xDF56},
- {0x7130, 0xD852, 0xDF6F},
- {0x7135, 0xD853, 0xDC16},
- {0x7144, 0xD853, 0xDD14},
- {0x715D, 0xD853, 0xDE0E},
- {0x7161, 0xD853, 0xDE37},
- {0x7166, 0xD853, 0xDE6A},
- {0x7169, 0xD853, 0xDE8B},
- {0x7175, 0xD854, 0xDC4A},
- {0x7177, 0xD854, 0xDC55},
- {0x717A, 0xD854, 0xDD22},
- {0x7221, 0xD854, 0xDDA9},
- {0x7223, 0xD854, 0xDDE5},
- {0x7224, 0xD854, 0xDDCD},
- {0x7228, 0xD854, 0xDE1E},
- {0x722C, 0xD854, 0xDE4C},
- {0x723D, 0xD855, 0xDC2E},
- {0x7248, 0xD855, 0xDCD9},
- {0x725B, 0xD855, 0xDDA7},
- {0x7275, 0xD855, 0xDFA9},
- {0x7276, 0xD855, 0xDFB4},
- {0x7332, 0xD856, 0xDDD4},
- {0x733D, 0xD856, 0xDEE4},
- {0x733E, 0xD856, 0xDEE3},
- {0x7340, 0xD856, 0xDEF1},
- {0x7352, 0xD856, 0xDFB2},
- {0x735D, 0xD857, 0xDC4B},
- {0x735E, 0xD857, 0xDC64},
- {0x7373, 0xD857, 0xDE2E},
- {0x7374, 0xD857, 0xDE56},
- {0x7375, 0xD857, 0xDE65},
- {0x7377, 0xD857, 0xDE62},
- {0x737B, 0xD857, 0xDED8},
- {0x737D, 0xD857, 0xDEC2},
- {0x7422, 0xD857, 0xDEE8},
- {0x7424, 0xD857, 0xDF23},
- {0x7427, 0xD857, 0xDF5C},
- {0x742E, 0xD857, 0xDFE0},
- {0x742F, 0xD857, 0xDFD4},
- {0x7434, 0xD858, 0xDC0C},
- {0x7435, 0xD857, 0xDFFB},
- {0x743D, 0xD858, 0xDC17},
- {0x7442, 0xD858, 0xDC60},
- {0x744F, 0xD858, 0xDCED},
- {0x7469, 0xD858, 0xDE70},
- {0x746B, 0xD858, 0xDE86},
- {0x7472, 0xD858, 0xDF4C},
- {0x7475, 0xD84F, 0xDD0E},
- {0x7479, 0xD859, 0xDC02},
- {0x7535, 0xD859, 0xDE7E},
- {0x753A, 0xD859, 0xDEB0},
- {0x7546, 0xD859, 0xDF1D},
- {0x7556, 0xD85A, 0xDCDD},
- {0x7558, 0xD85A, 0xDCEA},
- {0x755A, 0xD85A, 0xDD51},
- {0x755D, 0xD85A, 0xDD6F},
- {0x755F, 0xD85A, 0xDDDD},
- {0x7563, 0xD85A, 0xDE1E},
- {0x756A, 0xD85A, 0xDE58},
- {0x7570, 0xD85A, 0xDE8C},
- {0x7573, 0xD85A, 0xDEB7},
- {0x7644, 0xD85B, 0xDC73},
- {0x764E, 0xD85B, 0xDCDD},
- {0x765D, 0xD85B, 0xDE65},
- {0x7675, 0xD85B, 0xDF94},
- {0x767E, 0xD85B, 0xDFF8},
- {0x7721, 0xD85B, 0xDFF6},
- {0x7722, 0xD85B, 0xDFF7},
- {0x7733, 0xD85C, 0xDD0D},
- {0x7736, 0xD85C, 0xDD39},
- {0x7764, 0xD85C, 0xDFDB},
- {0x7765, 0xD85C, 0xDFDA},
- {0x776B, 0xD85C, 0xDFFE},
- {0x776E, 0xD85D, 0xDC10},
- {0x7773, 0xD85D, 0xDC49},
- {0x7829, 0xD85D, 0xDE15},
- {0x782A, 0xD85D, 0xDE14},
- {0x782C, 0xD85D, 0xDE31},
- {0x7834, 0xD85D, 0xDE93},
- {0x783C, 0xD85D, 0xDF0E},
- {0x783E, 0xD85D, 0xDF23},
- {0x7842, 0xD85D, 0xDF52},
- {0x7856, 0xD85E, 0xDD85},
- {0x7863, 0xD85E, 0xDE84},
- {0x7877, 0xD85E, 0xDFB3},
- {0x7879, 0xD85E, 0xDFBE},
- {0x787A, 0xD85E, 0xDFC7},
- {0x7925, 0xD85F, 0xDCB8},
- {0x792F, 0xD85F, 0xDDA0},
- {0x7932, 0xD85F, 0xDE10},
- {0x7939, 0xD85F, 0xDFB7},
- {0x7942, 0xD860, 0xDC8A},
- {0x7948, 0xD860, 0xDCBB},
- {0x7959, 0xD860, 0xDE82},
- {0x795E, 0xD860, 0xDEF3},
- {0x7966, 0xD861, 0xDC0C},
- {0x796B, 0xD861, 0xDC55},
- {0x797A, 0xD861, 0xDD6B},
- {0x797E, 0xD861, 0xDDC8},
- {0x7A21, 0xD861, 0xDDC9},
- {0x7A2C, 0xD861, 0xDED7},
- {0x7A2F, 0xD861, 0xDEFA},
- {0x7A4F, 0xD862, 0xDD49},
- {0x7A50, 0xD862, 0xDD46},
- {0x7A57, 0xD862, 0xDD6B},
- {0x7A65, 0xD862, 0xDD87},
- {0x7A66, 0xD862, 0xDD88},
- {0x7A71, 0xD862, 0xDDBA},
- {0x7A72, 0xD862, 0xDDBB},
- {0x7A7E, 0xD862, 0xDE1E},
- {0x7B21, 0xD862, 0xDE29},
- {0x7B2C, 0xD862, 0xDE71},
- {0x7B2D, 0xD862, 0xDE43},
- {0x7B36, 0xD862, 0xDE99},
- {0x7B37, 0xD862, 0xDECD},
- {0x7B3D, 0xD862, 0xDEE4},
- {0x7B3E, 0xD862, 0xDEDD},
- {0x7B4E, 0xD862, 0xDFC1},
- {0x7B4F, 0xD862, 0xDFEF},
- {0x7B57, 0xD863, 0xDD10},
- {0x7B5A, 0xD863, 0xDD71},
- {0x7B5C, 0xD863, 0xDDFB},
- {0x7B5D, 0xD863, 0xDE1F},
- {0x7B61, 0xD863, 0xDE36},
- {0x7B65, 0xD863, 0xDE89},
- {0x7B67, 0xD863, 0xDEEB},
- {0x7B69, 0xD863, 0xDF32},
- {0x7B71, 0xD863, 0xDFF8},
- {0x7C22, 0xD864, 0xDEA0},
- {0x7C23, 0xD864, 0xDEB1},
- {0x7C38, 0xD865, 0xDC90},
- {0x7C42, 0xD865, 0xDDCF},
- {0x7C4C, 0xD865, 0xDE7F},
- {0x7C56, 0xD865, 0xDEF0},
- {0x7C59, 0xD865, 0xDF19},
- {0x7C5D, 0xD865, 0xDF50},
- {0x7C76, 0xD866, 0xDCC6},
- {0x7D2C, 0xD866, 0xDE72},
- {0x7D4B, 0xD867, 0xDDDB},
- {0x7D4C, 0xD867, 0xDE3D},
- {0x7D59, 0xD867, 0xDE15},
- {0x7D5B, 0xD867, 0xDE8A},
- {0x7D5D, 0xD867, 0xDE49},
- {0x7D67, 0xD867, 0xDEC4},
- {0x7D6D, 0xD867, 0xDEE9},
- {0x7D70, 0xD867, 0xDEDB},
- {0x7E25, 0xD867, 0xDFCE},
- {0x7E29, 0xD868, 0xDC2F},
- {0x7E2B, 0xD868, 0xDC1A},
- {0x7E32, 0xD868, 0xDCF9},
- {0x7E35, 0xD868, 0xDC82},
- {0x7E53, 0xD848, 0xDE18},
- {0x7E58, 0xD868, 0xDF8C},
- {0x7E5A, 0xD869, 0xDC37},
- {0x7E6E, 0xD869, 0xDDF1},
- {0x7E70, 0xD869, 0xDE02},
- {0x7E72, 0xD869, 0xDE1A},
- {0x7E76, 0xD869, 0xDEB2},
-};
-#endif /* UTF8_OUTPUT_ENABLE */
-
-#ifdef UTF8_INPUT_ENABLE
-static const unsigned short utf8_to_euc_C2[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xA242, 0x2171, 0x2172, 0xA270, 0x216F, 0xA243, 0x2178,
- 0x212F, 0xA26D, 0xA26C, 0, 0x224C, 0, 0xA26E, 0xA234,
- 0x216B, 0x215E, 0, 0, 0x212D, 0, 0x2279, 0,
- 0xA231, 0, 0xA26B, 0, 0, 0, 0, 0xA244,
-};
-static const unsigned short utf8_to_euc_C2_ms[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xA242, 0x2171, 0x2172, 0xA270, 0x5C, 0xA243, 0x2178,
- 0x212F, 0xA26D, 0xA26C, 0, 0x224C, 0, 0xA26E, 0xA234,
- 0x216B, 0x215E, 0, 0, 0x212D, 0, 0x2279, 0,
- 0xA231, 0, 0xA26B, 0, 0, 0, 0, 0xA244,
-};
-static const unsigned short utf8_to_euc_C2_mac[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x00A0, 0xA242, 0x2171, 0x2172, 0xA270, 0x5C, 0xA243, 0x2178,
- 0x212F, 0x00FD, 0xA26C, 0, 0x224C, 0, 0xA26E, 0xA234,
- 0x216B, 0x215E, 0, 0, 0x212D, 0, 0x2279, 0,
- 0xA231, 0, 0xA26B, 0, 0, 0, 0, 0xA244,
-};
-static const unsigned short utf8_to_euc_C2_932[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0x21, 0x2171, 0x2172, 0, 0x5C, 0x7C, 0x2178,
- 0x212F, 0x63, 0x61, 0x2263, 0x224C, 0x2D, 0x52, 0x2131,
- 0x216B, 0x215E, 0x32, 0x33, 0x212D, 0x264C, 0x2279, 0x2126,
- 0x2124, 0x31, 0x6F, 0x2264, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_C2_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x2922, 0x2923, 0x2171, 0x2172, 0x2924, 0x216F, 0x2925, 0x2178,
- 0x212F, 0x2926, 0x2927, 0x2928, 0x224C, 0x2929, 0x292A, 0x292B,
- 0x216B, 0x215E, 0x292C, 0x292D, 0x212D, 0, 0x2279, 0x292E,
- 0x292F, 0x2930, 0x2931, 0x2932, 0x2933, 0x2934, 0x2935, 0x2936,
-};
-static const unsigned short utf8_to_euc_C3[] = {
- 0xAA22, 0xAA21, 0xAA24, 0xAA2A, 0xAA23, 0xAA29, 0xA921, 0xAA2E,
- 0xAA32, 0xAA31, 0xAA34, 0xAA33, 0xAA40, 0xAA3F, 0xAA42, 0xAA41,
- 0, 0xAA50, 0xAA52, 0xAA51, 0xAA54, 0xAA58, 0xAA53, 0x215F,
- 0xA92C, 0xAA63, 0xAA62, 0xAA65, 0xAA64, 0xAA72, 0xA930, 0xA94E,
- 0xAB22, 0xAB21, 0xAB24, 0xAB2A, 0xAB23, 0xAB29, 0xA941, 0xAB2E,
- 0xAB32, 0xAB31, 0xAB34, 0xAB33, 0xAB40, 0xAB3F, 0xAB42, 0xAB41,
- 0xA943, 0xAB50, 0xAB52, 0xAB51, 0xAB54, 0xAB58, 0xAB53, 0x2160,
- 0xA94C, 0xAB63, 0xAB62, 0xAB65, 0xAB64, 0xAB72, 0xA950, 0xAB73,
-};
-static const unsigned short utf8_to_euc_C3_932[] = {
- 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x43,
- 0x45, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
- 0x44, 0x4E, 0x4F, 0x4F, 0x4F, 0x4F, 0x4F, 0x215F,
- 0x4F, 0x55, 0x55, 0x55, 0x55, 0x59, 0x54, 0x73,
- 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x63,
- 0x65, 0x65, 0x65, 0x65, 0x69, 0x69, 0x69, 0x69,
- 0x64, 0x6E, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x2160,
- 0x6F, 0x75, 0x75, 0x75, 0x75, 0x79, 0x74, 0x79,
-};
-static const unsigned short utf8_to_euc_C3_x0213[] = {
- 0x2937, 0x2938, 0x2939, 0x293A, 0x293B, 0x293C, 0x293D, 0x293E,
- 0x293F, 0x2940, 0x2941, 0x2942, 0x2943, 0x2944, 0x2945, 0x2946,
- 0x2947, 0x2948, 0x2949, 0x294A, 0x294B, 0x294C, 0x294D, 0x215F,
- 0x294E, 0x294F, 0x2950, 0x2951, 0x2952, 0x2953, 0x2954, 0x2955,
- 0x2956, 0x2957, 0x2958, 0x2959, 0x295A, 0x295B, 0x295C, 0x295D,
- 0x295E, 0x295F, 0x2960, 0x2961, 0x2962, 0x2963, 0x2964, 0x2965,
- 0x2966, 0x2967, 0x2968, 0x2969, 0x296A, 0x296B, 0x296C, 0x2160,
- 0x296D, 0x296E, 0x296F, 0x2970, 0x2971, 0x2972, 0x2973, 0x2974,
-};
-static const unsigned short utf8_to_euc_C4[] = {
- 0xAA27, 0xAB27, 0xAA25, 0xAB25, 0xAA28, 0xAB28, 0xAA2B, 0xAB2B,
- 0xAA2C, 0xAB2C, 0xAA2F, 0xAB2F, 0xAA2D, 0xAB2D, 0xAA30, 0xAB30,
- 0xA922, 0xA942, 0xAA37, 0xAB37, 0, 0, 0xAA36, 0xAB36,
- 0xAA38, 0xAB38, 0xAA35, 0xAB35, 0xAA3A, 0xAB3A, 0xAA3B, 0xAB3B,
- 0xAA3D, 0xAB3D, 0xAA3C, 0, 0xAA3E, 0xAB3E, 0xA924, 0xA944,
- 0xAA47, 0xAB47, 0xAA45, 0xAB45, 0, 0, 0xAA46, 0xAB46,
- 0xAA44, 0xA945, 0xA926, 0xA946, 0xAA48, 0xAB48, 0xAA49, 0xAB49,
- 0xA947, 0xAA4A, 0xAB4A, 0xAA4C, 0xAB4C, 0xAA4B, 0xAB4B, 0xA929,
-};
-static const unsigned short utf8_to_euc_C4_x0213[] = {
- 0x2975, 0x297A, 0x2A3A, 0x2A49, 0x2A21, 0x2A2C, 0x2A3C, 0x2A4B,
- 0x2A59, 0x2A5F, 0xAA2F, 0xAB2F, 0x2A3D, 0x2A4C, 0x2A40, 0x2A4F,
- 0xA922, 0x2A50, 0x2978, 0x297D, 0, 0, 0xAA36, 0xAB36,
- 0x2A3E, 0x2A4D, 0x2A3F, 0x2A4E, 0x2A5A, 0x2A60, 0xAA3B, 0xAB3B,
- 0xAA3D, 0xAB3D, 0xAA3C, 0, 0x2A5B, 0x2A61, 0xA924, 0x2A7D,
- 0xAA47, 0xAB47, 0x2976, 0x297B, 0, 0, 0xAA46, 0xAB46,
- 0xAA44, 0xA945, 0xA926, 0xA946, 0x2A5C, 0x2A62, 0xAA49, 0xAB49,
- 0xA947, 0x2A3B, 0x2A4A, 0xAA4C, 0xAB4C, 0x2A24, 0x2A2F, 0xA929,
-};
-static const unsigned short utf8_to_euc_C5[] = {
- 0xA949, 0xA928, 0xA948, 0xAA4D, 0xAB4D, 0xAA4F, 0xAB4F, 0xAA4E,
- 0xAB4E, 0xA94A, 0xA92B, 0xA94B, 0xAA57, 0xAB57, 0, 0,
- 0xAA56, 0xAB56, 0xA92D, 0xA94D, 0xAA59, 0xAB59, 0xAA5B, 0xAB5B,
- 0xAA5A, 0xAB5A, 0xAA5C, 0xAB5C, 0xAA5D, 0xAB5D, 0xAA5F, 0xAB5F,
- 0xAA5E, 0xAB5E, 0xAA61, 0xAB61, 0xAA60, 0xAB60, 0xA92F, 0xA94F,
- 0xAA6C, 0xAB6C, 0xAA69, 0xAB69, 0xAA66, 0xAB66, 0xAA6B, 0xAB6B,
- 0xAA68, 0xAB68, 0xAA6A, 0xAB6A, 0xAA71, 0xAB71, 0xAA74, 0xAB74,
- 0xAA73, 0xAA75, 0xAB75, 0xAA77, 0xAB77, 0xAA76, 0xAB76, 0,
-};
-static const unsigned short utf8_to_euc_C5_x0213[] = {
- 0xA949, 0x2A23, 0x2A2E, 0x2A41, 0x2A51, 0xAA4F, 0xAB4F, 0x2A42,
- 0x2A52, 0xA94A, 0xA92B, 0x2A7A, 0x2979, 0x297E, 0, 0,
- 0x2A43, 0x2A53, 0x2B2B, 0x2B2A, 0x2A39, 0x2A48, 0xAA5B, 0xAB5B,
- 0x2A44, 0x2A54, 0x2A25, 0x2A30, 0x2A5D, 0x2A63, 0x2A27, 0x2A33,
- 0x2A26, 0x2A32, 0x2A47, 0x2A57, 0x2A28, 0x2A34, 0xA92F, 0xA94F,
- 0xAA6C, 0xAB6C, 0x2977, 0x297C, 0x2A5E, 0x2A64, 0x2A45, 0x2A55,
- 0x2A46, 0x2A56, 0xAA6A, 0xAB6A, 0xAA71, 0xAB71, 0xAA74, 0xAB74,
- 0xAA73, 0x2A29, 0x2A35, 0x2A2B, 0x2A38, 0x2A2A, 0x2A37, 0,
-};
-static const unsigned short utf8_to_euc_C6_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x2B29, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_C7[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xAA26, 0xAB26, 0xAA43,
- 0xAB43, 0xAA55, 0xAB55, 0xAA67, 0xAB67, 0xAA70, 0xAB70, 0xAA6D,
- 0xAB6D, 0xAA6F, 0xAB6F, 0xAA6E, 0xAB6E, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xAB39, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_C7_x0213[] = {
- 0, 0, 0x2B24, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x286F, 0x2870, 0xAA43,
- 0x2871, 0x2876, 0x2877, 0xAA67, 0x2878, 0xAA70, 0x2879, 0xAA6D,
- 0x287A, 0xAA6F, 0x287B, 0xAA6E, 0x287C, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xAB39, 0, 0,
- 0x2874, 0x2875, 0, 0, 0, 0x2B45, 0, 0,
-};
-static const unsigned short utf8_to_euc_C9_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x2B33, 0x2B39, 0x2B3A, 0x2B25, 0x2B38, 0x2B3F, 0x2A6E, 0x2B26,
- 0x2B2E, 0x2B30, 0x2B43, 0, 0x2B31, 0, 0x2B32, 0x2A75,
- 0x2B28, 0x2A79, 0, 0, 0x2B36, 0x2B3C, 0x2B22, 0x2B42,
- 0x2B2C, 0, 0, 0, 0x2A6A, 0x2A74, 0x2A6B, 0x2B34,
- 0x2A7B, 0x2A65, 0x2A76, 0x2A6F, 0, 0x2B2F, 0, 0,
- 0, 0x2A6C, 0x2B41, 0x2A73, 0, 0x2A70, 0x2A67, 0,
-};
-static const unsigned short utf8_to_euc_CA_x0213[] = {
- 0, 0x2A7C, 0x2A71, 0x2A68, 0x2B27, 0, 0, 0,
- 0x2A6D, 0x2B2D, 0x2B35, 0x2A66, 0x2B37, 0x2B3B, 0x2A78, 0,
- 0x2A72, 0x2B40, 0x2A69, 0, 0x2B21, 0x2A7E, 0, 0,
- 0x2B23, 0, 0, 0, 0, 0x2A77, 0, 0,
- 0, 0x2B3E, 0x2B3D, 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,
-};
-static const unsigned short utf8_to_euc_CB[] = {
- 0, 0, 0, 0, 0, 0, 0, 0xA230,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xA22F, 0xA232, 0xA236, 0xA235, 0, 0xA233, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_CB_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0x2A31,
- 0x2B53, 0, 0, 0, 0x2B54, 0, 0, 0,
- 0x2B55, 0x2B56, 0, 0, 0, 0, 0, 0,
- 0x2A22, 0x2A58, 0xA236, 0x2A2D, 0, 0x2A36, 0x2B71, 0,
- 0, 0, 0, 0, 0, 0x2B60, 0x2B61, 0x2B62,
- 0x2B63, 0x2B64, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_CC_x0213[] = {
- 0x2B5C, 0x2B5A, 0x2B5F, 0x2B7D, 0x2B5B, 0, 0x2B57, 0,
- 0x2B6D, 0, 0, 0x2B59, 0x2B5E, 0, 0, 0x2B5D,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x2B78, 0x2B79, 0x2B7E, 0, 0x2B6A, 0x2B76, 0x2B77, 0x2B6B,
- 0x2B6C, 0, 0, 0, 0x2B72, 0x2B67, 0, 0,
- 0, 0x2B6F, 0x2B7A, 0, 0x2B68, 0, 0, 0x2B70,
- 0x2B73, 0, 0, 0, 0x2B75, 0, 0, 0,
- 0, 0x2B69, 0x2B7B, 0x2B7C, 0x2B74, 0x2B6E, 0, 0,
-};
-static const unsigned short utf8_to_euc_CD_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0x2B52, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_CE[] = {
- 0, 0, 0, 0, 0xA238, 0xA239, 0xA661, 0,
- 0xA662, 0xA663, 0xA664, 0, 0xA667, 0, 0xA669, 0xA66C,
- 0xA676, 0x2621, 0x2622, 0x2623, 0x2624, 0x2625, 0x2626, 0x2627,
- 0x2628, 0x2629, 0x262A, 0x262B, 0x262C, 0x262D, 0x262E, 0x262F,
- 0x2630, 0x2631, 0, 0x2632, 0x2633, 0x2634, 0x2635, 0x2636,
- 0x2637, 0x2638, 0xA665, 0xA66A, 0xA671, 0xA672, 0xA673, 0xA674,
- 0xA67B, 0x2641, 0x2642, 0x2643, 0x2644, 0x2645, 0x2646, 0x2647,
- 0x2648, 0x2649, 0x264A, 0x264B, 0x264C, 0x264D, 0x264E, 0x264F,
-};
-static const unsigned short utf8_to_euc_CF[] = {
- 0x2650, 0x2651, 0xA678, 0x2652, 0x2653, 0x2654, 0x2655, 0x2656,
- 0x2657, 0x2658, 0xA675, 0xA67A, 0xA677, 0xA679, 0xA67C, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_CF_x0213[] = {
- 0x2650, 0x2651, 0x2659, 0x2652, 0x2653, 0x2654, 0x2655, 0x2656,
- 0x2657, 0x2658, 0xA675, 0xA67A, 0xA677, 0xA679, 0xA67C, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_D0[] = {
- 0, 0x2727, 0xA742, 0xA743, 0xA744, 0xA745, 0xA746, 0xA747,
- 0xA748, 0xA749, 0xA74A, 0xA74B, 0xA74C, 0, 0xA74D, 0xA74E,
- 0x2721, 0x2722, 0x2723, 0x2724, 0x2725, 0x2726, 0x2728, 0x2729,
- 0x272A, 0x272B, 0x272C, 0x272D, 0x272E, 0x272F, 0x2730, 0x2731,
- 0x2732, 0x2733, 0x2734, 0x2735, 0x2736, 0x2737, 0x2738, 0x2739,
- 0x273A, 0x273B, 0x273C, 0x273D, 0x273E, 0x273F, 0x2740, 0x2741,
- 0x2751, 0x2752, 0x2753, 0x2754, 0x2755, 0x2756, 0x2758, 0x2759,
- 0x275A, 0x275B, 0x275C, 0x275D, 0x275E, 0x275F, 0x2760, 0x2761,
-};
-static const unsigned short utf8_to_euc_D1[] = {
- 0x2762, 0x2763, 0x2764, 0x2765, 0x2766, 0x2767, 0x2768, 0x2769,
- 0x276A, 0x276B, 0x276C, 0x276D, 0x276E, 0x276F, 0x2770, 0x2771,
- 0, 0x2757, 0xA772, 0xA773, 0xA774, 0xA775, 0xA776, 0xA777,
- 0xA778, 0xA779, 0xA77A, 0xA77B, 0xA77C, 0, 0xA77D, 0xA77E,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E1B8_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0x2872, 0x2873,
-};
-static const unsigned short utf8_to_euc_E1BD_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x2B46, 0x2B47, 0x2B50, 0x2B51, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E280[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x213E, 0, 0, 0, 0x213D, 0x213D, 0x2142, 0,
- 0x2146, 0x2147, 0, 0, 0x2148, 0x2149, 0, 0,
- 0x2277, 0x2278, 0, 0, 0, 0x2145, 0x2144, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x2273, 0, 0x216C, 0x216D, 0, 0, 0, 0,
- 0, 0, 0, 0x2228, 0, 0, 0x2131, 0,
-};
-static const unsigned short utf8_to_euc_E280_ms[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x213E, 0, 0, 0, 0x213D, 0x213D, 0x2142, 0,
- 0x2146, 0x2147, 0, 0, 0x2148, 0x2149, 0, 0,
- 0x2277, 0x2278, 0, 0, 0, 0x2145, 0x2144, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x2273, 0, 0x216C, 0x216D, 0, 0, 0, 0,
- 0, 0, 0, 0x2228, 0, 0, 0x7E, 0,
-};
-static const unsigned short utf8_to_euc_E280_932[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x213E, 0, 0, 0, 0, 0x213D, 0, 0,
- 0x2146, 0x2147, 0, 0, 0x2148, 0x2149, 0, 0,
- 0x2277, 0x2278, 0, 0, 0, 0x2145, 0x2144, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x2273, 0, 0x216C, 0x216D, 0, 0, 0, 0,
- 0, 0, 0, 0x2228, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E280_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x213E, 0, 0, 0x237C, 0x213D, 0x213D, 0x2142, 0,
- 0x2146, 0x2147, 0, 0, 0x2148, 0x2149, 0, 0,
- 0x2277, 0x2278, 0x2340, 0, 0, 0x2145, 0x2144, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x2273, 0, 0x216C, 0x216D, 0, 0, 0, 0,
- 0, 0, 0, 0x2228, 0x286B, 0, 0x2131, 0x2B58,
-};
-static const unsigned short utf8_to_euc_E281_x0213[] = {
- 0, 0, 0x2C7E, 0, 0, 0, 0, 0x286C,
- 0x286D, 0x286E, 0, 0, 0, 0, 0, 0,
- 0, 0x2C7D, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E282_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0x2921, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E284[] = {
- 0, 0, 0, 0x216E, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0x2D62, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0x2D64, 0xA26F, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x2272, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E284_mac[] = {
- 0, 0, 0, 0x216E, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0x2B7B, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0x2B7D, 0x00FE, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x2272, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E284_x0213[] = {
- 0, 0, 0, 0x216E, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0x235D,
- 0, 0, 0, 0x235F, 0, 0, 0x2D62, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0x2D64, 0xA26F, 0, 0, 0, 0, 0x2360,
- 0, 0, 0, 0x2272, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x235C, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E285[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x2D35, 0x2D36, 0x2D37, 0x2D38, 0x2D39, 0x2D3A, 0x2D3B, 0x2D3C,
- 0x2D3D, 0x2D3E, 0, 0, 0, 0, 0, 0,
- 0xF373, 0xF374, 0xF375, 0xF376, 0xF377, 0xF378, 0xF379, 0xF37A,
- 0xF37B, 0xF37C, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E285_mac[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x2A21, 0x2A22, 0x2A23, 0x2A24, 0x2A25, 0x2A26, 0x2A27, 0x2A28,
- 0x2A29, 0x2A2A, 0, 0, 0, 0, 0, 0,
- 0x2A35, 0x2A36, 0x2A37, 0x2A38, 0x2A39, 0x2A3A, 0x2A3B, 0x2A3C,
- 0x2A3D, 0x2A3E, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E285_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x2778, 0x2779, 0x277A, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x2D35, 0x2D36, 0x2D37, 0x2D38, 0x2D39, 0x2D3A, 0x2D3B, 0x2D3C,
- 0x2D3D, 0x2D3E, 0x2D3F, 0x2D57, 0, 0, 0, 0,
- 0x2C35, 0x2C36, 0x2C37, 0x2C38, 0x2C39, 0x2C3A, 0x2C3B, 0x2C3C,
- 0x2C3D, 0x2C3E, 0x2C3F, 0x2C40, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E286[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x222B, 0x222C, 0x222A, 0x222D, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E286_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x222B, 0x222C, 0x222A, 0x222D, 0x2271, 0, 0x2327, 0x2325,
- 0x2326, 0x2328, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E287[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x224D, 0, 0x224E, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E287_x0213[] = {
- 0, 0, 0, 0, 0x2329, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x224D, 0, 0x224E, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0x232B, 0x232C,
- 0x232A, 0x232D, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E288[] = {
- 0x224F, 0, 0x225F, 0x2250, 0, 0, 0, 0x2260,
- 0x223A, 0, 0, 0x223B, 0, 0, 0, 0,
- 0, 0x2D74, 0x215D, 0, 0, 0, 0, 0,
- 0, 0, 0x2265, 0, 0, 0x2267, 0x2167, 0x2D78,
- 0x225C, 0, 0, 0, 0, 0x2142, 0, 0x224A,
- 0x224B, 0x2241, 0x2240, 0x2269, 0x226A, 0, 0x2D73, 0,
- 0, 0, 0, 0, 0x2168, 0x2268, 0, 0,
- 0, 0, 0, 0, 0, 0x2266, 0, 0,
-};
-static const unsigned short utf8_to_euc_E288_932[] = {
- 0x224F, 0, 0x225F, 0x2250, 0, 0, 0, 0x2260,
- 0x223A, 0, 0, 0x223B, 0, 0, 0, 0,
- 0, 0x2D74, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x2265, 0, 0, 0x2267, 0x2167, 0x2D78,
- 0x225C, 0, 0, 0, 0, 0x2142, 0, 0x224A,
- 0x224B, 0x2241, 0x2240, 0x2269, 0x226A, 0, 0x2D73, 0,
- 0, 0, 0, 0, 0x2168, 0x2268, 0, 0,
- 0, 0, 0, 0, 0, 0x2266, 0, 0,
-};
-static const unsigned short utf8_to_euc_E288_mac[] = {
- 0x224F, 0, 0x225F, 0x2250, 0, 0, 0, 0x2260,
- 0x223A, 0, 0, 0x223B, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x2265, 0, 0, 0x2267, 0x2167, 0x2F22,
- 0x225C, 0, 0, 0, 0, 0x2142, 0, 0x224A,
- 0x224B, 0x2241, 0x2240, 0x2269, 0x226A, 0, 0x2F21, 0,
- 0, 0, 0, 0, 0x2168, 0x2268, 0, 0,
- 0, 0, 0, 0, 0, 0x2266, 0, 0,
-};
-static const unsigned short utf8_to_euc_E288_x0213[] = {
- 0x224F, 0, 0x225F, 0x2250, 0, 0x2247, 0, 0x2260,
- 0x223A, 0x2246, 0, 0x223B, 0, 0, 0, 0,
- 0, 0x2D74, 0x215D, 0x235B, 0, 0, 0, 0,
- 0, 0, 0x2265, 0, 0, 0x2267, 0x2167, 0x2D78,
- 0x225C, 0, 0, 0, 0, 0x2254, 0x2255, 0x224A,
- 0x224B, 0x2241, 0x2240, 0x2269, 0x226A, 0, 0x2D73, 0,
- 0, 0, 0, 0, 0x2168, 0x2268, 0, 0,
- 0, 0, 0, 0, 0, 0x2266, 0, 0,
-};
-static const unsigned short utf8_to_euc_E289[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x2262, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x2162, 0x2261, 0, 0, 0, 0, 0x2165, 0x2166,
- 0, 0, 0x2263, 0x2264, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E289_x0213[] = {
- 0, 0, 0, 0x226C, 0, 0x226D, 0, 0,
- 0x226E, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x2262, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x2162, 0x2261, 0x226B, 0, 0, 0, 0x2165, 0x2166,
- 0, 0, 0x2263, 0x2264, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0x226F, 0x2270,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E28A[] = {
- 0, 0, 0x223E, 0x223F, 0, 0, 0x223C, 0x223D,
- 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, 0x225D, 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, 0x2D79,
-};
-static const unsigned short utf8_to_euc_E28A_mac[] = {
- 0, 0, 0x223E, 0x223F, 0, 0, 0x223C, 0x223D,
- 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, 0x225D, 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, 0x2F23,
-};
-static const unsigned short utf8_to_euc_E28A_x0213[] = {
- 0, 0, 0x223E, 0x223F, 0x2242, 0x2243, 0x223C, 0x223D,
- 0, 0, 0x2244, 0x2245, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x2251, 0x2252, 0x2253,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x225D, 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, 0x2D79,
-};
-static const unsigned short utf8_to_euc_E28B_x0213[] = {
- 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, 0x2776, 0x2777, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E28C[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x225E, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E28C_x0213[] = {
- 0, 0, 0, 0, 0, 0x2248, 0x2249, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x225E, 0, 0, 0, 0, 0,
- 0x277C, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E28E_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0x2742, 0x2743,
-};
-static const unsigned short utf8_to_euc_E28F_x0213[] = {
- 0x2744, 0x2745, 0x2746, 0x2747, 0x2748, 0x2749, 0x274A, 0x274B,
- 0x274C, 0x274D, 0x274E, 0x274F, 0x2750, 0, 0x277E, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E290_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x277D, 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,
-};
-static const unsigned short utf8_to_euc_E291[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x2D21, 0x2D22, 0x2D23, 0x2D24, 0x2D25, 0x2D26, 0x2D27, 0x2D28,
- 0x2D29, 0x2D2A, 0x2D2B, 0x2D2C, 0x2D2D, 0x2D2E, 0x2D2F, 0x2D30,
- 0x2D31, 0x2D32, 0x2D33, 0x2D34, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E291_mac[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x2921, 0x2922, 0x2923, 0x2924, 0x2925, 0x2926, 0x2927, 0x2928,
- 0x2929, 0x292A, 0x292B, 0x292C, 0x292D, 0x292E, 0x292F, 0x2930,
- 0x2931, 0x2932, 0x2933, 0x2934, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E293_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x2C41, 0x2C42, 0x2C43, 0x2C44, 0x2C45, 0x2C46, 0x2C47, 0x2C48,
- 0x2C49, 0x2C4A, 0x2C4B, 0x2C4C, 0x2C4D, 0x2C4E, 0x2C4F, 0x2C50,
- 0x2C51, 0x2C52, 0x2C53, 0x2C54, 0x2C55, 0x2C56, 0x2C57, 0x2C58,
- 0x2C59, 0x2C5A, 0, 0x2C2B, 0x2C2C, 0x2C2D, 0x2C2E, 0x2C2F,
- 0x2C30, 0x2C31, 0x2C32, 0x2C33, 0x2C34, 0x265A, 0x265B, 0x265C,
- 0x265D, 0x265E, 0x265F, 0x2660, 0x2661, 0x2662, 0x2663, 0,
-};
-static const unsigned short utf8_to_euc_E294[] = {
- 0x2821, 0x282C, 0x2822, 0x282D, 0, 0, 0, 0,
- 0, 0, 0, 0, 0x2823, 0, 0, 0x282E,
- 0x2824, 0, 0, 0x282F, 0x2826, 0, 0, 0x2831,
- 0x2825, 0, 0, 0x2830, 0x2827, 0x283C, 0, 0,
- 0x2837, 0, 0, 0x2832, 0x2829, 0x283E, 0, 0,
- 0x2839, 0, 0, 0x2834, 0x2828, 0, 0, 0x2838,
- 0x283D, 0, 0, 0x2833, 0x282A, 0, 0, 0x283A,
- 0x283F, 0, 0, 0x2835, 0x282B, 0, 0, 0x283B,
-};
-static const unsigned short utf8_to_euc_E295[] = {
- 0, 0, 0x2840, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x2836, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E296[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x2223, 0x2222, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x2225, 0x2224, 0, 0, 0, 0,
- 0, 0, 0, 0, 0x2227, 0x2226, 0, 0,
-};
-static const unsigned short utf8_to_euc_E296_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x2223, 0x2222, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0x266D, 0x2225, 0x2224, 0, 0, 0x2322, 0x2321,
- 0, 0, 0, 0, 0x2227, 0x2226, 0, 0,
-};
-static const unsigned short utf8_to_euc_E297[] = {
- 0, 0, 0, 0, 0, 0, 0x2221, 0x217E,
- 0, 0, 0, 0x217B, 0, 0, 0x217D, 0x217C,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0x227E,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E297_x0213[] = {
- 0x2324, 0x2323, 0, 0, 0, 0, 0x2221, 0x217E,
- 0, 0x233B, 0, 0x217B, 0, 0, 0x217D, 0x217C,
- 0x2867, 0x2868, 0x2869, 0x286A, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0x233F, 0,
- 0, 0, 0, 0, 0, 0, 0, 0x227E,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E298[] = {
- 0, 0, 0, 0, 0, 0x217A, 0x2179, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E298_x0213[] = {
- 0x2668, 0x2669, 0x266A, 0x266B, 0, 0x217A, 0x2179, 0,
- 0, 0, 0, 0, 0, 0, 0x2667, 0,
- 0, 0, 0, 0, 0, 0, 0x2664, 0x2665,
- 0, 0, 0, 0, 0, 0, 0x2D7E, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E299[] = {
- 0x216A, 0, 0x2169, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x2276, 0, 0, 0x2275, 0, 0x2274,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E299_x0213[] = {
- 0x216A, 0, 0x2169, 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,
- 0x263A, 0x263D, 0x263B, 0x2640, 0x2639, 0x263E, 0x263C, 0x263F,
- 0x266C, 0x227D, 0x2276, 0x227B, 0x227C, 0x2275, 0x227A, 0x2274,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E29C_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x277B, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E29D_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0x2D7D, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0x2C21, 0x2C22,
- 0x2C23, 0x2C24, 0x2C25, 0x2C26, 0x2C27, 0x2C28, 0x2C29, 0x2C2A,
-};
-static const unsigned short utf8_to_euc_E2A4_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0x232E, 0x232F, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E2A6_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0x233A,
-};
-static const unsigned short utf8_to_euc_E2A7_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x237D, 0x237E, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E380[] = {
- 0x2121, 0x2122, 0x2123, 0x2137, 0, 0x2139, 0x213A, 0x213B,
- 0x2152, 0x2153, 0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159,
- 0x215A, 0x215B, 0x2229, 0x222E, 0x214C, 0x214D, 0, 0,
- 0, 0, 0, 0, 0x2141, 0x2D60, 0, 0x2D61,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E380_932[] = {
- 0x2121, 0x2122, 0x2123, 0x2137, 0, 0x2139, 0x213A, 0x213B,
- 0x2152, 0x2153, 0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159,
- 0x215A, 0x215B, 0x2229, 0x222E, 0x214C, 0x214D, 0, 0,
- 0, 0, 0, 0, 0, 0x2D60, 0, 0x2D61,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E380_x0213[] = {
- 0x2121, 0x2122, 0x2123, 0x2137, 0, 0x2139, 0x213A, 0x213B,
- 0x2152, 0x2153, 0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159,
- 0x215A, 0x215B, 0x2229, 0x222E, 0x214C, 0x214D, 0x225A, 0x225B,
- 0x2258, 0x2259, 0, 0, 0x2141, 0x2D60, 0, 0x2D61,
- 0x2666, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x2233, 0x2234, 0x2235, 0, 0,
- 0, 0, 0, 0x2236, 0x2237, 0x233C, 0, 0,
-};
-static const unsigned short utf8_to_euc_E381[] = {
- 0, 0x2421, 0x2422, 0x2423, 0x2424, 0x2425, 0x2426, 0x2427,
- 0x2428, 0x2429, 0x242A, 0x242B, 0x242C, 0x242D, 0x242E, 0x242F,
- 0x2430, 0x2431, 0x2432, 0x2433, 0x2434, 0x2435, 0x2436, 0x2437,
- 0x2438, 0x2439, 0x243A, 0x243B, 0x243C, 0x243D, 0x243E, 0x243F,
- 0x2440, 0x2441, 0x2442, 0x2443, 0x2444, 0x2445, 0x2446, 0x2447,
- 0x2448, 0x2449, 0x244A, 0x244B, 0x244C, 0x244D, 0x244E, 0x244F,
- 0x2450, 0x2451, 0x2452, 0x2453, 0x2454, 0x2455, 0x2456, 0x2457,
- 0x2458, 0x2459, 0x245A, 0x245B, 0x245C, 0x245D, 0x245E, 0x245F,
-};
-static const unsigned short utf8_to_euc_E382[] = {
- 0x2460, 0x2461, 0x2462, 0x2463, 0x2464, 0x2465, 0x2466, 0x2467,
- 0x2468, 0x2469, 0x246A, 0x246B, 0x246C, 0x246D, 0x246E, 0x246F,
- 0x2470, 0x2471, 0x2472, 0x2473, 0, 0, 0, 0,
- 0, 0, 0, 0x212B, 0x212C, 0x2135, 0x2136, 0,
- 0, 0x2521, 0x2522, 0x2523, 0x2524, 0x2525, 0x2526, 0x2527,
- 0x2528, 0x2529, 0x252A, 0x252B, 0x252C, 0x252D, 0x252E, 0x252F,
- 0x2530, 0x2531, 0x2532, 0x2533, 0x2534, 0x2535, 0x2536, 0x2537,
- 0x2538, 0x2539, 0x253A, 0x253B, 0x253C, 0x253D, 0x253E, 0x253F,
-};
-static const unsigned short utf8_to_euc_E382_932[] = {
- 0x2460, 0x2461, 0x2462, 0x2463, 0x2464, 0x2465, 0x2466, 0x2467,
- 0x2468, 0x2469, 0x246A, 0x246B, 0x246C, 0x246D, 0x246E, 0x246F,
- 0x2470, 0x2471, 0x2472, 0x2473, 0x2574, 0, 0, 0,
- 0, 0, 0, 0x212B, 0x212C, 0x2135, 0x2136, 0,
- 0, 0x2521, 0x2522, 0x2523, 0x2524, 0x2525, 0x2526, 0x2527,
- 0x2528, 0x2529, 0x252A, 0x252B, 0x252C, 0x252D, 0x252E, 0x252F,
- 0x2530, 0x2531, 0x2532, 0x2533, 0x2534, 0x2535, 0x2536, 0x2537,
- 0x2538, 0x2539, 0x253A, 0x253B, 0x253C, 0x253D, 0x253E, 0x253F,
-};
-static const unsigned short utf8_to_euc_E382_x0213[] = {
- 0x2460, 0x2461, 0x2462, 0x2463, 0x2464, 0x2465, 0x2466, 0x2467,
- 0x2468, 0x2469, 0x246A, 0x246B, 0x246C, 0x246D, 0x246E, 0x246F,
- 0x2470, 0x2471, 0x2472, 0x2473, 0x2474, 0x2475, 0x2476, 0,
- 0, 0, 0, 0x212B, 0x212C, 0x2135, 0x2136, 0x2239,
- 0x237B, 0x2521, 0x2522, 0x2523, 0x2524, 0x2525, 0x2526, 0x2527,
- 0x2528, 0x2529, 0x252A, 0x252B, 0x252C, 0x252D, 0x252E, 0x252F,
- 0x2530, 0x2531, 0x2532, 0x2533, 0x2534, 0x2535, 0x2536, 0x2537,
- 0x2538, 0x2539, 0x253A, 0x253B, 0x253C, 0x253D, 0x253E, 0x253F,
-};
-static const unsigned short utf8_to_euc_E383[] = {
- 0x2540, 0x2541, 0x2542, 0x2543, 0x2544, 0x2545, 0x2546, 0x2547,
- 0x2548, 0x2549, 0x254A, 0x254B, 0x254C, 0x254D, 0x254E, 0x254F,
- 0x2550, 0x2551, 0x2552, 0x2553, 0x2554, 0x2555, 0x2556, 0x2557,
- 0x2558, 0x2559, 0x255A, 0x255B, 0x255C, 0x255D, 0x255E, 0x255F,
- 0x2560, 0x2561, 0x2562, 0x2563, 0x2564, 0x2565, 0x2566, 0x2567,
- 0x2568, 0x2569, 0x256A, 0x256B, 0x256C, 0x256D, 0x256E, 0x256F,
- 0x2570, 0x2571, 0x2572, 0x2573, 0x2574, 0x2575, 0x2576, 0,
- 0, 0, 0, 0x2126, 0x213C, 0x2133, 0x2134, 0,
-};
-static const unsigned short utf8_to_euc_E383_x0213[] = {
- 0x2540, 0x2541, 0x2542, 0x2543, 0x2544, 0x2545, 0x2546, 0x2547,
- 0x2548, 0x2549, 0x254A, 0x254B, 0x254C, 0x254D, 0x254E, 0x254F,
- 0x2550, 0x2551, 0x2552, 0x2553, 0x2554, 0x2555, 0x2556, 0x2557,
- 0x2558, 0x2559, 0x255A, 0x255B, 0x255C, 0x255D, 0x255E, 0x255F,
- 0x2560, 0x2561, 0x2562, 0x2563, 0x2564, 0x2565, 0x2566, 0x2567,
- 0x2568, 0x2569, 0x256A, 0x256B, 0x256C, 0x256D, 0x256E, 0x256F,
- 0x2570, 0x2571, 0x2572, 0x2573, 0x2574, 0x2575, 0x2576, 0x2772,
- 0x2773, 0x2774, 0x2775, 0x2126, 0x213C, 0x2133, 0x2134, 0x2238,
-};
-static const unsigned short utf8_to_euc_E387_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x266E, 0x266F, 0x2670, 0x2671, 0x2672, 0x2673, 0x2674, 0x2675,
- 0x2676, 0x2677, 0x2679, 0x267A, 0x267B, 0x267C, 0x267D, 0x267E,
-};
-static const unsigned short utf8_to_euc_E388[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0x2D6A, 0x2D6B, 0, 0, 0, 0, 0,
- 0, 0x2D6C, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E388_mac[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0x2D2E, 0x2D31, 0, 0, 0, 0, 0,
- 0, 0x2D2C, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E389_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0x2841, 0x2842, 0x2843, 0x2844, 0x2845, 0x2846, 0x2847,
- 0x2848, 0x2849, 0x284A, 0x284B, 0x284C, 0x284D, 0x284E, 0x284F,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E38A[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0x2D65, 0x2D66, 0x2D67, 0x2D68,
- 0x2D69, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E38A_mac[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0x2D73, 0x2D74, 0x2D75, 0x2D76,
- 0x2D77, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E38A_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0x2D65, 0x2D66, 0x2D67, 0x2D68,
- 0x2D69, 0, 0, 0, 0, 0, 0, 0,
- 0, 0x2850, 0x2851, 0x2852, 0x2853, 0x2854, 0x2855, 0x2856,
- 0x2857, 0x2858, 0x2859, 0x285A, 0x285B, 0x285C, 0x285D, 0x285E,
-};
-static const unsigned short utf8_to_euc_E38B_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x2C5B, 0x2C5C, 0x2C5D, 0x2C5E, 0x2C5F, 0x2C60, 0x2C61, 0x2C62,
- 0x2C63, 0x2C64, 0x2C65, 0x2C66, 0x2C67, 0x2C68, 0x2C69, 0x2C6A,
- 0x2C6B, 0x2C6C, 0x2C6D, 0x2C6E, 0, 0x2C71, 0, 0,
- 0, 0x2C70, 0, 0, 0x2C73, 0x2C72, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x2C6F, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E38C[] = {
- 0, 0, 0, 0x2D46, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x2D4A, 0, 0,
- 0, 0, 0, 0, 0x2D41, 0, 0, 0,
- 0x2D44, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x2D42, 0x2D4C, 0, 0, 0x2D4B, 0x2D45,
- 0, 0, 0, 0x2D4D, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0x2D47, 0,
- 0, 0, 0, 0x2D4F, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E38C_mac[] = {
- 0, 0, 0, 0x2E29, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x2E32, 0, 0,
- 0, 0, 0, 0, 0x2E24, 0, 0, 0,
- 0x2E2B, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x2E22, 0x2E34, 0, 0, 0x2E35, 0x2E2D,
- 0, 0, 0, 0x2E37, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0x2E2A, 0,
- 0, 0, 0, 0x2E36, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E38D[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0x2D40, 0x2D4E, 0, 0, 0x2D43, 0, 0,
- 0, 0x2D48, 0, 0, 0, 0, 0, 0x2D49,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x2D5F, 0x2D6F, 0x2D6E, 0x2D6D, 0,
-};
-static const unsigned short utf8_to_euc_E38D_mac[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0x2E21, 0x2E2F, 0, 0, 0x2E23, 0, 0,
- 0, 0x2E2E, 0, 0, 0, 0, 0, 0x2E31,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x2E6A, 0x2E69, 0x2E68, 0x2E67, 0,
-};
-static const unsigned short utf8_to_euc_E38E[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0x2D53, 0x2D54,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0x2D50, 0x2D51, 0x2D52, 0,
- 0, 0x2D56, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E38E_mac[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0x2B2B, 0x2B2D,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0x2B21, 0x2B23, 0x2B29, 0,
- 0, 0x2B27, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E38F[] = {
- 0, 0, 0, 0, 0x2D55, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x2D63, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E38F_mac[] = {
- 0, 0, 0, 0, 0x2B2E, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x2B7C, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E38F_x0213[] = {
- 0, 0, 0, 0, 0x2D55, 0, 0, 0,
- 0, 0, 0, 0x235E, 0, 0x2D63, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E390_x0213[] = {
- 0, 0, 0x2E23, 0, 0, 0, 0xA12D, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xA132, 0, 0xA133, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E391_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xA15E, 0, 0xA156, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E392_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xA17E, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x2E53, 0, 0,
- 0, 0, 0, 0, 0xA32B, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E393_x0213[] = {
- 0, 0xF468, 0, 0, 0, 0, 0, 0xA32F,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x2E5B, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E394_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xA348,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E395_x0213[] = {
- 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, 0xA35D, 0xA35E, 0,
- 0, 0, 0, 0xA361, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xA367, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E396_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xA423, 0,
- 0xA426, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E397_x0213[] = {
- 0, 0, 0, 0, 0, 0xA42F, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xA438, 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, 0xA442, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E398_x0213[] = {
- 0, 0, 0, 0, 0, 0xA44A, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E399_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xA479, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E39A_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xA53F, 0, 0, 0, 0, 0xA543, 0,
- 0, 0xA541, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E39B_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xA557,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E39D_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xA823, 0xA825, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xA829, 0xA828, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xA82C, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E39E_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x4F5F, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E39F_x0213[] = {
- 0, 0xA83E, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x4F6F, 0, 0, 0, 0, 0,
- 0xA856, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xA859, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xA85C, 0, 0,
-};
-static const unsigned short utf8_to_euc_E3A0_x0213[] = {
- 0xA85E, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xA86F,
- 0, 0, 0, 0, 0, 0, 0xA871, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E3A1_x0213[] = {
- 0xA874, 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, 0xA879, 0, 0, 0,
- 0, 0xA87B, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E3A3_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xAC3B, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E3A4_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xAC46,
- 0, 0, 0xAC4A, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E3A5_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xAC60,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E3A9_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xAD5B, 0,
- 0, 0, 0, 0xAD5F, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E3AB_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xAD71, 0xAE36,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xAD7C, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E3AC_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xAE2E, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xAE32, 0, 0xAE34, 0, 0, 0,
- 0, 0, 0x7549, 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,
-};
-static const unsigned short utf8_to_euc_E3AD_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xAE6D, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xAE65,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E3AE_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0xAF28,
- 0xAF29, 0, 0, 0, 0, 0xAF2C, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xAF34, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0x757E, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E3AF_x0213[] = {
- 0, 0, 0, 0x7621, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xAF48, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xAF5D, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E3B0_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0x763A,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xAF77, 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,
-};
-static const unsigned short utf8_to_euc_E3B3_x0213[] = {
- 0, 0, 0, 0xEE3B, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xEE42, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E3B4_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xEE71, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xEE7E, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E3B5_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xEF40, 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,
-};
-static const unsigned short utf8_to_euc_E3B6_x0213[] = {
- 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, 0xEF54, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E3B7_x0213[] = {
- 0xEF70, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xEF77, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E3B8_x0213[] = {
- 0, 0, 0, 0, 0, 0xF028, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0x7766,
-};
-static const unsigned short utf8_to_euc_E3B9_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xF03F, 0, 0, 0, 0, 0, 0xF041, 0,
- 0xF042, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E3BA_x0213[] = {
- 0, 0, 0, 0xF049, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xF050, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E3BD_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xF134,
- 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, 0x784D, 0, 0, 0xF146, 0, 0xF148,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E3BE_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xF15C, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E3BF_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xF167, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xF16C,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E480_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xF222, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E481_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xF22D, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E482_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xF239, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E484_x0213[] = {
- 0, 0, 0, 0, 0, 0xF264, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E485_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xF274, 0, 0, 0, 0, 0, 0, 0xF277,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xF27D, 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,
-};
-static const unsigned short utf8_to_euc_E486_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xF333, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xF337,
-};
-static const unsigned short utf8_to_euc_E487_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xF347, 0,
- 0, 0, 0, 0, 0, 0, 0xF34B, 0,
- 0, 0, 0, 0xF348, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E488_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0xF353,
- 0, 0, 0, 0, 0, 0, 0xF357, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E489_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0x796D, 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,
-};
-static const unsigned short utf8_to_euc_E48B_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0xF42B, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xF436, 0,
- 0, 0, 0, 0, 0, 0xF43B, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E48C_x0213[] = {
- 0, 0, 0xF44E, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xF45D, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E48D_x0213[] = {
- 0, 0, 0, 0xF461, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E48F_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xF53E, 0,
- 0xF542, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E490_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xF548, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xF54A,
- 0, 0, 0, 0, 0xF54C, 0, 0, 0,
- 0, 0, 0xF54F, 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,
-};
-static const unsigned short utf8_to_euc_E491_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x7A59, 0, 0, 0, 0,
- 0, 0, 0, 0x7A5A, 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, 0xF56C, 0,
- 0, 0, 0xF56E, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E492_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xF577, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xF635, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xF632, 0,
-};
-static const unsigned short utf8_to_euc_E493_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xF634, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E494_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xF659, 0, 0, 0, 0, 0xF654, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xF66D, 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,
-};
-static const unsigned short utf8_to_euc_E495_x0213[] = {
- 0, 0, 0, 0xF66E, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E496_x0213[] = {
- 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, 0x7B51, 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,
- 0xF74F, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E497_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xF76C, 0, 0,
- 0, 0, 0x7B60, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E498_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xF824,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E499_x0213[] = {
- 0, 0xF83A, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xF843, 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,
-};
-static const unsigned short utf8_to_euc_E49A_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xF84E, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xF853,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E49C_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xF86B, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E49D_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xF929, 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,
-};
-static const unsigned short utf8_to_euc_E49F_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xF93F, 0, 0,
-};
-static const unsigned short utf8_to_euc_E4A0_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xF949, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E4A1_x0213[] = {
- 0, 0, 0, 0, 0x7C4B, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xF95C, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E4A2_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xFA27, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E4A6_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x7D58, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E4A7_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xFB6A,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xFB70, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E4A8_x0213[] = {
- 0, 0, 0, 0, 0xFB75, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xFB78, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E4AA_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xFC37, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E4AC_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xFC55, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E4AF_x0213[] = {
- 0, 0, 0xFD26, 0, 0, 0, 0, 0,
- 0, 0, 0xFD28, 0, 0, 0, 0, 0,
- 0, 0, 0xFD2A, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xFD31, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E4B0_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0x7E3E,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xFD3F, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E4B3_x0213[] = {
- 0, 0, 0, 0, 0xFE2A, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xFE2D, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E4B4_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0xFE4B,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E4B5_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xFE60,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E4B8[] = {
- 0x306C, 0x437A, 0xB021, 0x3C37, 0xB022, 0xB023, 0, 0x4B7C,
- 0x3E66, 0x3B30, 0x3E65, 0x323C, 0xB024, 0x4954, 0x4D3F, 0,
- 0x5022, 0x312F, 0xB025, 0, 0x336E, 0x5023, 0x4024, 0x5242,
- 0x3556, 0x4A3A, 0, 0, 0, 0, 0x3E67, 0xB026,
- 0, 0x4E3E, 0, 0xB027, 0xB028, 0, 0x4A42, 0,
- 0xB029, 0, 0x5024, 0xB02A, 0, 0x4366, 0xB02B, 0xB02C,
- 0xB02D, 0x5025, 0x367A, 0, 0, 0xB02E, 0x5026, 0,
- 0x345D, 0x4330, 0, 0x3C67, 0x5027, 0, 0, 0x5028,
-};
-static const unsigned short utf8_to_euc_E4B8_x0213[] = {
- 0x306C, 0x437A, 0xA122, 0x3C37, 0xB022, 0xB023, 0, 0x4B7C,
- 0x3E66, 0x3B30, 0x3E65, 0x323C, 0xB024, 0x4954, 0x4D3F, 0xA123,
- 0x5022, 0x312F, 0xA124, 0, 0x336E, 0x5023, 0x4024, 0x5242,
- 0x3556, 0x4A3A, 0, 0, 0, 0, 0x3E67, 0xB026,
- 0, 0x4E3E, 0, 0xB027, 0xB028, 0, 0x4A42, 0,
- 0x2E24, 0xA125, 0x5024, 0xA126, 0xF02E, 0x4366, 0xA127, 0x2E25,
- 0x2E26, 0x5025, 0x367A, 0, 0, 0xB02E, 0x5026, 0,
- 0x345D, 0x4330, 0, 0x3C67, 0x5027, 0, 0, 0x5028,
-};
-static const unsigned short utf8_to_euc_E4B9[] = {
- 0xB02F, 0xB030, 0x5029, 0x4735, 0xB031, 0x3557, 0, 0xB032,
- 0, 0, 0, 0x4737, 0, 0x4663, 0x3843, 0x4B33,
- 0, 0xB033, 0, 0, 0, 0x6949, 0x502A, 0x3E68,
- 0x502B, 0x3235, 0xB034, 0, 0xB035, 0x3665, 0x3870, 0x4C69,
- 0, 0, 0x5626, 0xB036, 0, 0, 0, 0,
- 0xB037, 0xB038, 0, 0, 0, 0, 0, 0,
- 0, 0x4D70, 0, 0x467D, 0xB039, 0xB03A, 0, 0,
- 0, 0xB03B, 0, 0, 0, 0, 0x3425, 0xB03C,
-};
-static const unsigned short utf8_to_euc_E4B9_x0213[] = {
- 0xA128, 0xB030, 0x5029, 0x4735, 0xB031, 0x3557, 0, 0xA129,
- 0xA12A, 0, 0, 0x4737, 0, 0x4663, 0x3843, 0x4B33,
- 0, 0xA12C, 0, 0, 0, 0x6949, 0x502A, 0x3E68,
- 0x502B, 0x3235, 0xA12F, 0, 0xB035, 0x3665, 0x3870, 0x4C69,
- 0, 0, 0x5626, 0xB036, 0, 0, 0, 0,
- 0xB037, 0xA130, 0, 0, 0, 0, 0, 0,
- 0, 0x4D70, 0, 0x467D, 0xB039, 0xB03A, 0, 0,
- 0, 0xB03B, 0, 0, 0, 0, 0x3425, 0xB03C,
-};
-static const unsigned short utf8_to_euc_E4BA[] = {
- 0x3535, 0, 0x502C, 0, 0, 0x502D, 0x4E3B, 0,
- 0x4D3D, 0x4168, 0x502F, 0x3B76, 0x4673, 0xB03D, 0x5032, 0,
- 0, 0x313E, 0x385F, 0, 0x385E, 0x3066, 0xB03E, 0xB03F,
- 0x4F4B, 0x4F4A, 0, 0x3A33, 0x3021, 0xB040, 0x5033, 0x5034,
- 0x5035, 0x4B34, 0x5036, 0, 0x3872, 0x3067, 0x4B72, 0,
- 0x357C, 0, 0, 0x357D, 0x357E, 0x4462, 0x4E3C, 0xB041,
- 0x5037, 0, 0, 0x5038, 0, 0, 0x5039, 0,
- 0, 0xB042, 0x3F4D, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E4BA_x0213[] = {
- 0x3535, 0, 0x502C, 0, 0, 0x502D, 0x4E3B, 0,
- 0x4D3D, 0x4168, 0x502F, 0x3B76, 0x4673, 0x2E27, 0x5032, 0,
- 0, 0x313E, 0x385F, 0, 0x385E, 0x3066, 0xB03E, 0xB03F,
- 0x4F4B, 0x4F4A, 0, 0x3A33, 0x3021, 0xA131, 0x5033, 0x5034,
- 0x5035, 0x4B34, 0x5036, 0, 0x3872, 0x3067, 0x4B72, 0,
- 0x357C, 0, 0, 0x357D, 0x357E, 0x4462, 0x4E3C, 0xB041,
- 0x5037, 0, 0, 0x5038, 0, 0, 0x5039, 0,
- 0, 0xA134, 0x3F4D, 0xA135, 0xA137, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E4BB[] = {
- 0x3D3A, 0x3F4E, 0x503E, 0xB043, 0x503C, 0, 0x503D, 0x3558,
- 0, 0, 0x3A23, 0x3270, 0, 0x503B, 0x503A, 0x4A29,
- 0xB044, 0, 0, 0, 0x3B46, 0x3B45, 0x423E, 0x503F,
- 0x4955, 0x4067, 0xB045, 0xB046, 0, 0x2138, 0x5040, 0x5042,
- 0xB047, 0xB048, 0xB049, 0x4265, 0x4E61, 0x304A, 0, 0,
- 0xB04A, 0, 0, 0, 0, 0x5041, 0x323E, 0xB04B,
- 0x3644, 0xB04C, 0x4367, 0xB04D, 0, 0xB04E, 0x376F, 0x5043,
- 0, 0, 0, 0x4724, 0xF42F, 0xB04F, 0xB050, 0xB051,
-};
-static const unsigned short utf8_to_euc_E4BB_x0213[] = {
- 0x3D3A, 0x3F4E, 0x503E, 0xA138, 0x503C, 0, 0x503D, 0x3558,
- 0xA139, 0, 0x3A23, 0x3270, 0, 0x503B, 0x503A, 0x4A29,
- 0xA13A, 0, 0, 0, 0x3B46, 0x3B45, 0x423E, 0x503F,
- 0x4955, 0x4067, 0xA13C, 0xB046, 0, 0x2138, 0x5040, 0x5042,
- 0xB047, 0x2E28, 0xB049, 0x4265, 0x4E61, 0x304A, 0, 0,
- 0xB04A, 0, 0, 0xA13B, 0, 0x5041, 0x323E, 0xB04B,
- 0x3644, 0xA13D, 0x4367, 0xB04D, 0, 0xA13E, 0x376F, 0x5043,
- 0, 0, 0, 0x4724, 0, 0x2E29, 0xB050, 0x2E2A,
-};
-static const unsigned short utf8_to_euc_E4BC[] = {
- 0xB052, 0x346B, 0xB053, 0xB054, 0, 0, 0, 0,
- 0xB055, 0x5044, 0x304B, 0xB056, 0xB057, 0x3860, 0x346C, 0x497A,
- 0x4832, 0x3559, 0xB058, 0, 0, 0xB059, 0xB05A, 0xB05B,
- 0, 0xB05C, 0x3271, 0, 0x5067, 0x4541, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xB05D, 0x476C,
- 0x5046, 0xB05E, 0, 0xB060, 0x483C, 0xB061, 0x4E62, 0xB062,
- 0x3F2D, 0xB063, 0x3B47, 0xB064, 0x3B77, 0x3240, 0xB065, 0,
-};
-static const unsigned short utf8_to_euc_E4BC_x0213[] = {
- 0xA13F, 0x346B, 0xB053, 0x2E2B, 0, 0, 0, 0,
- 0xB055, 0x5044, 0x304B, 0x2E2C, 0xB057, 0x3860, 0x346C, 0x497A,
- 0x4832, 0x3559, 0xB058, 0, 0, 0xB059, 0xA140, 0xB05B,
- 0, 0xB05C, 0x3271, 0, 0x5067, 0x4541, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xB05D, 0x476C,
- 0x5046, 0xB05E, 0, 0xB060, 0x483C, 0xB061, 0x4E62, 0xA142,
- 0x3F2D, 0, 0x3B47, 0xB064, 0x3B77, 0x3240, 0xA143, 0,
-};
-static const unsigned short utf8_to_euc_E4BD[] = {
- 0xB066, 0, 0xB067, 0x4451, 0, 0, 0x4322, 0x504A,
- 0xB068, 0xB069, 0, 0xB06A, 0xB06B, 0x304C, 0x4463, 0x3D3B,
- 0x3A34, 0x4D24, 0xB06C, 0x424E, 0xB06D, 0x323F, 0xB06E, 0x5049,
- 0xB06F, 0x4D3E, 0x5045, 0x5047, 0x3A6E, 0x5048, 0x5524, 0xB070,
- 0xB05F, 0, 0, 0xB071, 0, 0, 0, 0,
- 0, 0x5050, 0xB072, 0, 0xB073, 0, 0xB074, 0x5053,
- 0x5051, 0xB075, 0, 0x3242, 0, 0x4A3B, 0x504B, 0xB076,
- 0xB077, 0xB078, 0xB079, 0x504F, 0x3873, 0xB07A, 0xB07B, 0x3B48,
-};
-static const unsigned short utf8_to_euc_E4BD_x0213[] = {
- 0xB066, 0, 0xB067, 0x4451, 0, 0, 0x4322, 0x504A,
- 0x2E2E, 0x2E2F, 0, 0xB06A, 0xB06B, 0x304C, 0x4463, 0x3D3B,
- 0x3A34, 0x4D24, 0xB06C, 0x424E, 0xA144, 0x323F, 0x2E30, 0x5049,
- 0xA145, 0x4D3E, 0x5045, 0x5047, 0x3A6E, 0x5048, 0x5524, 0x2E31,
- 0x2E2D, 0, 0, 0xB071, 0xA141, 0, 0, 0,
- 0, 0x5050, 0x2E32, 0, 0x2E33, 0, 0xB074, 0x5053,
- 0x5051, 0xB075, 0, 0x3242, 0, 0x4A3B, 0x504B, 0xA147,
- 0xA148, 0xB078, 0xA149, 0x504F, 0x3873, 0xA14A, 0x2E34, 0x3B48,
-};
-static const unsigned short utf8_to_euc_E4BE[] = {
- 0, 0xB07C, 0xB07D, 0x3426, 0xB07E, 0xB121, 0x5054, 0,
- 0x504C, 0xB122, 0xB123, 0x4E63, 0xB124, 0x3B78, 0xB125, 0x504D,
- 0xB126, 0x5052, 0xB127, 0xB128, 0xB129, 0, 0x5055, 0xB12A,
- 0x504E, 0xB12B, 0xB12C, 0x3621, 0, 0x304D, 0xB12D, 0xB12E,
- 0x3622, 0x3241, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x5525, 0, 0x4B79, 0x496E, 0x3874,
- 0, 0, 0xB12F, 0, 0, 0x3F2F, 0x4E37, 0xB130,
- 0, 0xB131, 0, 0xB132, 0xB133, 0xB134, 0xB135, 0x4A58,
-};
-static const unsigned short utf8_to_euc_E4BE_x0213[] = {
- 0, 0xB07C, 0xA14B, 0x3426, 0xB07E, 0xA14C, 0x5054, 0,
- 0x504C, 0xB122, 0x2E35, 0x4E63, 0xB124, 0x3B78, 0xB125, 0x504D,
- 0xB126, 0x5052, 0xA14D, 0xB128, 0x2E36, 0, 0x5055, 0x2E37,
- 0x504E, 0xB12B, 0xA14E, 0x3621, 0, 0x304D, 0xB12D, 0xB12E,
- 0x3622, 0x3241, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x5525, 0, 0x4B79, 0x496E, 0x3874,
- 0, 0, 0xA150, 0, 0, 0x3F2F, 0x4E37, 0xB130,
- 0, 0xB131, 0, 0xB132, 0xB133, 0xB134, 0xA151, 0x4A58,
-};
-static const unsigned short utf8_to_euc_E4BF[] = {
- 0xB136, 0xB137, 0x3738, 0x4225, 0x3264, 0xB138, 0xB139, 0,
- 0xB13A, 0xB13B, 0x3D53, 0xB13C, 0xB13D, 0xB13E, 0x5059, 0xB13F,
- 0x505E, 0x505C, 0xB140, 0, 0x5057, 0, 0, 0x422F,
- 0x505A, 0, 0x505D, 0x505B, 0xB141, 0x4A5D, 0, 0x5058,
- 0xB142, 0x3F2E, 0xB143, 0x4B73, 0x505F, 0x5060, 0, 0,
- 0, 0, 0, 0, 0, 0, 0x3D24, 0x506D,
- 0xB144, 0, 0xB145, 0x4750, 0, 0x4936, 0x5068, 0,
- 0x4A70, 0, 0x3236, 0, 0xB146, 0xB147, 0x506C, 0xB148,
-};
-static const unsigned short utf8_to_euc_E4BF_x0213[] = {
- 0xB136, 0xB137, 0x3738, 0x4225, 0x3264, 0xA152, 0xB139, 0,
- 0xB13A, 0x2E39, 0x3D53, 0xA153, 0xB13D, 0, 0x5059, 0xA154,
- 0x505E, 0x505C, 0xA155, 0, 0x5057, 0, 0, 0x422F,
- 0x505A, 0, 0x505D, 0x505B, 0xB141, 0x4A5D, 0, 0x5058,
- 0x2E3A, 0x3F2E, 0xB143, 0x4B73, 0x505F, 0x5060, 0xA14F, 0,
- 0, 0, 0, 0, 0, 0, 0x3D24, 0x506D,
- 0xB144, 0x2E21, 0xA157, 0x4750, 0, 0x4936, 0x5068, 0,
- 0x4A70, 0, 0x3236, 0, 0xB146, 0xB147, 0x506C, 0,
-};
-static const unsigned short utf8_to_euc_E580[] = {
- 0xB149, 0xB14A, 0, 0, 0xB14B, 0x5066, 0x506F, 0xB14C,
- 0, 0x4152, 0xB14D, 0x3844, 0xB14E, 0x475C, 0xB14F, 0x6047,
- 0xB150, 0x506E, 0x455D, 0xB151, 0x5063, 0, 0x3876, 0xB152,
- 0xB153, 0x3875, 0x5061, 0xB154, 0xB155, 0xB156, 0xB157, 0x3C5A,
- 0, 0x5069, 0xB158, 0x4A6F, 0x434D, 0x5065, 0x3771, 0xB159,
- 0x5062, 0x506A, 0x5064, 0x4E51, 0x506B, 0x4F41, 0xB15A, 0,
- 0xB15B, 0, 0xB15C, 0xB15D, 0, 0xB15E, 0x3666, 0,
- 0, 0x3770, 0, 0xB176, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E580_x0213[] = {
- 0xA158, 0x2E3B, 0x2E3C, 0, 0xB14B, 0x5066, 0x506F, 0xB14C,
- 0, 0x4152, 0xB14D, 0x3844, 0xB14E, 0x475C, 0x2E3D, 0x6047,
- 0xA159, 0x506E, 0x455D, 0xA15A, 0x5063, 0, 0x3876, 0xB152,
- 0x2E3E, 0x3875, 0x5061, 0xB154, 0xA15B, 0xB156, 0xA15C, 0x3C5A,
- 0, 0x5069, 0xA15D, 0x4A6F, 0x434D, 0x5065, 0x3771, 0x2E3F,
- 0x5062, 0x506A, 0x5064, 0x4E51, 0x506B, 0x4F41, 0x2E40, 0,
- 0xB15B, 0, 0xB15C, 0xB15D, 0, 0xB15E, 0x3666, 0,
- 0, 0x3770, 0, 0x2E42, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E581[] = {
- 0xB15F, 0xB160, 0xB161, 0x5070, 0, 0xB162, 0xB163, 0x5071,
- 0x5075, 0x304E, 0xB164, 0, 0xB165, 0, 0xB166, 0x4A50,
- 0x5074, 0xB167, 0xB168, 0xB169, 0, 0x5073, 0x5077, 0xB16A,
- 0, 0xB16B, 0x5076, 0, 0x4464, 0, 0, 0xB16C,
- 0xB16D, 0, 0xB16E, 0xB16F, 0, 0x3772, 0xB170, 0xB171,
- 0, 0, 0xB172, 0, 0x5078, 0xB173, 0, 0,
- 0xB174, 0xB175, 0x3C45, 0, 0x4226, 0x4465, 0x3676, 0,
- 0x5079, 0, 0, 0, 0, 0x3536, 0, 0,
-};
-static const unsigned short utf8_to_euc_E581_x0213[] = {
- 0x2E41, 0x2E43, 0xA15F, 0x5070, 0, 0xB162, 0xA160, 0x5071,
- 0x5075, 0x304E, 0xB164, 0, 0xB165, 0, 0xA161, 0x4A50,
- 0x5074, 0xB167, 0xB168, 0xA162, 0, 0x5073, 0x5077, 0xA163,
- 0, 0xB16B, 0x5076, 0, 0x4464, 0, 0, 0xB16C,
- 0xB16D, 0, 0xB16E, 0xA164, 0, 0x3772, 0xA165, 0xB171,
- 0, 0, 0xA166, 0, 0x5078, 0xB173, 0, 0,
- 0xA167, 0xB175, 0x3C45, 0, 0x4226, 0x4465, 0x3676, 0,
- 0x5079, 0, 0, 0, 0, 0x3536, 0, 0,
-};
-static const unsigned short utf8_to_euc_E582[] = {
- 0x507A, 0xB177, 0, 0xB178, 0xB179, 0x507C, 0xB17A, 0,
- 0, 0, 0xB17B, 0, 0, 0x4B35, 0xB17C, 0xB17D,
- 0xB17E, 0x3766, 0xB221, 0xB222, 0xB223, 0, 0xB224, 0,
- 0x3B31, 0x4877, 0x507B, 0xB225, 0xB226, 0, 0xB227, 0xB228,
- 0xB229, 0xB22A, 0xB22B, 0, 0, 0, 0, 0,
- 0, 0, 0xB22C, 0, 0x3A45, 0x4D43, 0, 0xB22D,
- 0xB22E, 0, 0x507E, 0x5123, 0x507D, 0x3A44, 0, 0x3D7D,
- 0, 0xB22F, 0xB230, 0, 0, 0xB231, 0x3739, 0,
-};
-static const unsigned short utf8_to_euc_E582_x0213[] = {
- 0x507A, 0xB177, 0, 0xB178, 0xB179, 0x507C, 0xB17A, 0,
- 0xA169, 0, 0xB17B, 0, 0, 0x4B35, 0xB17C, 0xB17D,
- 0xB17E, 0x3766, 0xA16A, 0xA16B, 0x2E44, 0xA16C, 0xA16D, 0,
- 0x3B31, 0x4877, 0x507B, 0xB225, 0xA16E, 0, 0xB227, 0xB228,
- 0xB229, 0xB22A, 0xB22B, 0xA168, 0, 0, 0, 0,
- 0, 0, 0xA16F, 0, 0x3A45, 0x4D43, 0, 0xB22D,
- 0xB22E, 0xA171, 0x507E, 0x5123, 0x507D, 0x3A44, 0, 0x3D7D,
- 0, 0xB22F, 0xA172, 0xA173, 0, 0xB231, 0x3739, 0,
-};
-static const unsigned short utf8_to_euc_E583[] = {
- 0xB232, 0, 0x5124, 0xB233, 0xB234, 0x364F, 0, 0xB235,
- 0, 0x5121, 0x5122, 0, 0xB236, 0x462F, 0xB237, 0x417C,
- 0xB238, 0x3623, 0, 0xB239, 0xB23A, 0x4B4D, 0x5125, 0,
- 0xB23B, 0, 0x4E3D, 0, 0xB23C, 0xB23D, 0x5126, 0xB23E,
- 0, 0, 0xB23F, 0x5129, 0xB240, 0x5127, 0xB241, 0x414E,
- 0xB242, 0xB243, 0, 0, 0, 0x5128, 0x512A, 0xB244,
- 0, 0xB245, 0xB251, 0, 0xF430, 0x512C, 0xB246, 0,
- 0, 0x512B, 0xB247, 0x4A48, 0, 0, 0xB248, 0,
-};
-static const unsigned short utf8_to_euc_E583_x0213[] = {
- 0xB232, 0, 0x5124, 0xB233, 0xA174, 0x364F, 0, 0xA175,
- 0, 0x5121, 0x5122, 0, 0x2E45, 0x462F, 0xA178, 0x417C,
- 0x2E47, 0x3623, 0, 0xB239, 0xA17A, 0x4B4D, 0x5125, 0,
- 0, 0xA17B, 0x4E3D, 0, 0xB23C, 0xB23D, 0x5126, 0xB23E,
- 0, 0xA17C, 0xB23F, 0x5129, 0xB240, 0x5127, 0x2E48, 0x414E,
- 0xB242, 0xA17D, 0, 0, 0, 0x5128, 0x512A, 0xB244,
- 0, 0xB245, 0x2E46, 0xA176, 0, 0x512C, 0xB246, 0,
- 0, 0x512B, 0xB247, 0x4A48, 0, 0, 0xB248, 0,
-};
-static const unsigned short utf8_to_euc_E584[] = {
- 0x3537, 0x512E, 0x512F, 0xB249, 0x322F, 0, 0xB24A, 0xB24B,
- 0xB24C, 0x512D, 0, 0xB24D, 0xB24E, 0xB24F, 0xB250, 0,
- 0xB252, 0, 0x3C74, 0, 0x5132, 0x5131, 0x5130, 0xB253,
- 0x5056, 0xB254, 0x5133, 0xB255, 0xB256, 0xB257, 0xB258, 0x3D7E,
- 0, 0x5134, 0, 0xB259, 0, 0, 0, 0xB25A,
- 0xB25B, 0, 0x4D25, 0, 0xB25C, 0xB25D, 0, 0xB25E,
- 0, 0xB25F, 0x4C59, 0xB260, 0xB261, 0xB262, 0, 0x5136,
- 0xB263, 0xB264, 0x5135, 0x5138, 0x5137, 0, 0, 0x5139,
-};
-static const unsigned short utf8_to_euc_E584_x0213[] = {
- 0x3537, 0x512E, 0x512F, 0x2E4B, 0x322F, 0, 0x2E4A, 0xB24B,
- 0xA321, 0x512D, 0, 0x2E4C, 0xB24E, 0xB24F, 0xB250, 0,
- 0xB252, 0, 0x3C74, 0, 0x5132, 0x5131, 0x5130, 0xA323,
- 0x5056, 0xB254, 0x5133, 0xA324, 0xB256, 0xB257, 0x2E4D, 0x3D7E,
- 0, 0x5134, 0, 0xB259, 0, 0, 0, 0xB25A,
- 0xB25B, 0, 0x4D25, 0, 0xB25C, 0xB25D, 0, 0xB25E,
- 0, 0xB25F, 0x4C59, 0xB260, 0xB261, 0x2E4E, 0, 0x5136,
- 0xB263, 0xB264, 0x5135, 0x5138, 0x5137, 0, 0, 0x5139,
-};
-static const unsigned short utf8_to_euc_E585[] = {
- 0x513A, 0x3074, 0xB265, 0x3835, 0x373B, 0x3D3C, 0x437B, 0x3624,
- 0x4068, 0x3877, 0xB266, 0x396E, 0x513C, 0x4C48, 0x4546, 0xB267,
- 0x3B79, 0, 0x513B, 0xB268, 0x513D, 0xB269, 0, 0xB26A,
- 0xB26B, 0, 0x455E, 0, 0x3375, 0, 0, 0xB26C,
- 0, 0, 0x513E, 0, 0xB26D, 0x467E, 0xB26E, 0,
- 0x4134, 0x5140, 0x5141, 0x482C, 0x3878, 0x4F3B, 0x5142, 0,
- 0, 0x3626, 0, 0, 0, 0x4A3C, 0x4236, 0x3671,
- 0x4535, 0, 0, 0, 0x3773, 0, 0xB26F, 0,
-};
-static const unsigned short utf8_to_euc_E585_x0213[] = {
- 0x513A, 0x3074, 0xB265, 0x3835, 0x373B, 0x3D3C, 0x437B, 0x3624,
- 0x4068, 0x3877, 0x2E4F, 0x396E, 0x513C, 0x4C48, 0x4546, 0xB267,
- 0x3B79, 0, 0x513B, 0xB268, 0x513D, 0x2E51, 0, 0x2E52,
- 0xB26B, 0, 0x455E, 0, 0x3375, 0, 0, 0xB26C,
- 0xA326, 0, 0x513E, 0, 0, 0x467E, 0xB26E, 0,
- 0x4134, 0x5140, 0x5141, 0x482C, 0x3878, 0x4F3B, 0x5142, 0,
- 0, 0x3626, 0, 0xA328, 0, 0x4A3C, 0x4236, 0x3671,
- 0x4535, 0, 0, 0xF474, 0x3773, 0, 0xB26F, 0,
-};
-static const unsigned short utf8_to_euc_E586[] = {
- 0x5143, 0, 0x5144, 0xB270, 0xB271, 0x4662, 0x315F, 0,
- 0, 0x5147, 0x3A7D, 0xB272, 0x5146, 0x3A46, 0xB273, 0x5148,
- 0x666E, 0x5149, 0x4B41, 0x514A, 0, 0x514B, 0x514C, 0x3E69,
- 0xB274, 0x3C4C, 0, 0, 0, 0xB275, 0, 0,
- 0x3427, 0xB276, 0x514F, 0xB277, 0x514D, 0x4C3D, 0x514E, 0,
- 0x495A, 0x5150, 0x5151, 0x5152, 0x455F, 0xB278, 0, 0,
- 0x5156, 0x5154, 0x5155, 0x5153, 0x3A63, 0x5157, 0x4C6A, 0x4E64,
- 0xB279, 0, 0xB27A, 0, 0xB27B, 0x5158, 0xB27C, 0xB27D,
-};
-static const unsigned short utf8_to_euc_E586_x0213[] = {
- 0x5143, 0, 0x5144, 0xA329, 0xB271, 0x4662, 0x315F, 0,
- 0, 0x5147, 0x3A7D, 0xA32A, 0x5146, 0x3A46, 0xB273, 0x5148,
- 0x666E, 0x5149, 0x4B41, 0x514A, 0, 0x514B, 0x514C, 0x3E69,
- 0xA32C, 0x3C4C, 0, 0, 0, 0x2E54, 0, 0,
- 0x3427, 0xB276, 0x514F, 0xA32D, 0x514D, 0x4C3D, 0x514E, 0,
- 0x495A, 0x5150, 0x5151, 0x5152, 0x455F, 0xA32E, 0, 0,
- 0x5156, 0x5154, 0x5155, 0x5153, 0x3A63, 0x5157, 0x4C6A, 0x4E64,
- 0xB279, 0, 0xB27A, 0, 0xA330, 0x5158, 0, 0xB27D,
-};
-static const unsigned short utf8_to_euc_E587[] = {
- 0, 0, 0xB27E, 0, 0x4028, 0x5159, 0x3D5A, 0,
- 0xB321, 0x515A, 0, 0x437C, 0x4E3F, 0x4560, 0, 0xB322,
- 0, 0xB323, 0xB324, 0xB325, 0, 0xB326, 0x5245, 0,
- 0xB327, 0, 0, 0x515B, 0x7425, 0x3645, 0xB328, 0,
- 0x515C, 0x4B5E, 0xB329, 0, 0, 0xB32A, 0x3D68, 0x427C,
- 0, 0x515E, 0x4664, 0, 0xF431, 0x515F, 0xB32B, 0,
- 0x5160, 0x332E, 0xB32C, 0xB32D, 0xB32E, 0x5161, 0x3627, 0xB32F,
- 0x464C, 0x317A, 0x3D50, 0, 0, 0x4821, 0x5162, 0,
-};
-static const unsigned short utf8_to_euc_E587_x0213[] = {
- 0, 0, 0xB27E, 0x2E55, 0x4028, 0x5159, 0x3D5A, 0,
- 0xB321, 0x515A, 0x2E56, 0x437C, 0x4E3F, 0x4560, 0, 0xB322,
- 0, 0xB323, 0xB324, 0xB325, 0, 0xB326, 0x5245, 0,
- 0xB327, 0, 0, 0x515B, 0x7425, 0x3645, 0x2E57, 0,
- 0x515C, 0x4B5E, 0x2E58, 0, 0, 0xB32A, 0x3D68, 0x427C,
- 0, 0x515E, 0x4664, 0, 0, 0x515F, 0x2E59, 0,
- 0x5160, 0x332E, 0xB32C, 0xA333, 0xA334, 0x5161, 0x3627, 0xB32F,
- 0x464C, 0x317A, 0x3D50, 0, 0, 0x4821, 0x5162, 0,
-};
-static const unsigned short utf8_to_euc_E588[] = {
- 0x4561, 0xB330, 0xB331, 0x3F4F, 0x5163, 0xB332, 0x4A2C, 0x405A,
- 0x3422, 0, 0x3429, 0x5164, 0, 0, 0x5166, 0,
- 0, 0x373A, 0xB333, 0xB334, 0x5165, 0xB335, 0xB336, 0x4E73,
- 0xB337, 0, 0, 0, 0, 0x3D69, 0, 0,
- 0, 0, 0xB338, 0, 0x483D, 0x4A4C, 0, 0x5167,
- 0xB339, 0x4D78, 0x5168, 0, 0, 0, 0x5169, 0,
- 0x457E, 0xB33A, 0xB33B, 0x516A, 0, 0xB33C, 0x4029, 0x3A7E,
- 0x3774, 0x516B, 0x3B49, 0x396F, 0xB33D, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E588_x0213[] = {
- 0x4561, 0x2E5A, 0xA335, 0x3F4F, 0x5163, 0xB332, 0x4A2C, 0x405A,
- 0x3422, 0, 0x3429, 0x5164, 0, 0, 0x5166, 0,
- 0, 0x373A, 0xA336, 0x2E5C, 0x5165, 0x2E5D, 0xA337, 0x4E73,
- 0xB337, 0, 0, 0, 0, 0x3D69, 0, 0,
- 0, 0, 0xB338, 0, 0x483D, 0x4A4C, 0, 0x5167,
- 0xB339, 0x4D78, 0x5168, 0, 0, 0, 0x5169, 0,
- 0x457E, 0xB33A, 0xB33B, 0x516A, 0, 0xB33C, 0x4029, 0x3A7E,
- 0x3774, 0x516B, 0x3B49, 0x396F, 0xB33D, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E589[] = {
- 0, 0, 0, 0x4466, 0x516D, 0xB33E, 0, 0x4227,
- 0, 0xB33F, 0x3A6F, 0x516E, 0x516F, 0x4130, 0, 0x516C,
- 0, 0, 0, 0, 0x5171, 0xB340, 0x4B36, 0xB341,
- 0xB342, 0, 0xB343, 0x3964, 0xB344, 0, 0x5170, 0xB345,
- 0xB346, 0xB347, 0, 0x3775, 0x3A5E, 0x476D, 0xB348, 0,
- 0, 0x5174, 0x5172, 0, 0, 0, 0xB349, 0x497B,
- 0x3E6A, 0x517B, 0x3364, 0x5175, 0x5173, 0x414F, 0, 0xB34A,
- 0xB34B, 0xB34C, 0, 0, 0, 0x5177, 0, 0x5176,
-};
-static const unsigned short utf8_to_euc_E589_x0213[] = {
- 0, 0, 0, 0x4466, 0x516D, 0xB33E, 0, 0x4227,
- 0, 0x2E5E, 0x3A6F, 0x516E, 0x516F, 0x4130, 0, 0x516C,
- 0, 0, 0, 0, 0x5171, 0xA339, 0x4B36, 0x2E5F,
- 0xB342, 0, 0xB343, 0x3964, 0xA33A, 0x2F7E, 0x5170, 0xB345,
- 0xB346, 0x2E60, 0, 0x3775, 0x3A5E, 0x476D, 0xB348, 0,
- 0, 0x5174, 0x5172, 0, 0xA33B, 0, 0xB349, 0x497B,
- 0x3E6A, 0x517B, 0x3364, 0x5175, 0x5173, 0x414F, 0, 0xA33C,
- 0xB34B, 0xB34C, 0, 0, 0, 0x5177, 0, 0x5176,
-};
-static const unsigned short utf8_to_euc_E58A[] = {
- 0xB34D, 0, 0xB34E, 0x3344, 0, 0xB34F, 0, 0x3760,
- 0x517C, 0x4E2D, 0xB350, 0, 0xB351, 0x5178, 0, 0,
- 0, 0x517D, 0x517A, 0xB352, 0x5179, 0xB353, 0xB354, 0xB355,
- 0xB356, 0, 0xB357, 0x4E4F, 0xB358, 0, 0, 0x3879,
- 0x3243, 0, 0, 0x4E74, 0xB359, 0xB35A, 0xB35B, 0xB35C,
- 0, 0x3D75, 0x4558, 0x3965, 0x5222, 0x5223, 0, 0xB35D,
- 0xB35E, 0x4E65, 0, 0, 0x4F2B, 0x5225, 0xB35F, 0xB360,
- 0xB361, 0x387A, 0xB362, 0xB363, 0x5224, 0xB364, 0x332F, 0,
-};
-static const unsigned short utf8_to_euc_E58A_x0213[] = {
- 0xB34D, 0, 0xA33E, 0x3344, 0xA33D, 0xB34F, 0, 0x3760,
- 0x517C, 0x4E2D, 0xB350, 0, 0xB351, 0x5178, 0, 0,
- 0, 0x517D, 0x517A, 0x2E61, 0x5179, 0xB353, 0xB354, 0xB355,
- 0xA340, 0, 0xB357, 0x4E4F, 0, 0, 0, 0x3879,
- 0x3243, 0, 0, 0x4E74, 0xA342, 0xB35A, 0xA343, 0xB35C,
- 0, 0x3D75, 0x4558, 0x3965, 0x5222, 0x5223, 0, 0xA344,
- 0xB35E, 0x4E65, 0, 0, 0x4F2B, 0x5225, 0xB35F, 0xB360,
- 0xB361, 0x387A, 0xA345, 0xA346, 0x5224, 0xB364, 0x332F, 0,
-};
-static const unsigned short utf8_to_euc_E58B[] = {
- 0xB365, 0x5226, 0, 0x4B56, 0xB366, 0x443C, 0xB367, 0x4D26,
- 0xB368, 0x4A59, 0, 0, 0xB369, 0x5227, 0, 0xB36A,
- 0, 0xB36B, 0x7055, 0, 0xB36C, 0x4630, 0xB36D, 0x5228,
- 0x342A, 0x4C33, 0, 0xB36E, 0xB36F, 0x3E21, 0x5229, 0x4A67,
- 0x522D, 0xB370, 0x402A, 0x522A, 0x3650, 0xB371, 0x522B, 0x342B,
- 0xB372, 0xB373, 0xB374, 0, 0xB375, 0, 0, 0,
- 0xB376, 0xB377, 0x372E, 0x522E, 0xB378, 0x522F, 0xB379, 0xB37A,
- 0x5230, 0x5231, 0x3C5B, 0, 0, 0, 0x387B, 0x4C5E,
-};
-static const unsigned short utf8_to_euc_E58B_x0213[] = {
- 0, 0x5226, 0, 0x4B56, 0xB366, 0x443C, 0xB367, 0x4D26,
- 0x2E62, 0x4A59, 0xA347, 0, 0x2E64, 0x5227, 0, 0xB36A,
- 0x2E65, 0xA349, 0x7055, 0, 0xB36C, 0x4630, 0x2E66, 0x5228,
- 0x342A, 0x4C33, 0, 0x2E67, 0xB36F, 0x3E21, 0x5229, 0x4A67,
- 0x522D, 0xB370, 0x402A, 0x522A, 0x3650, 0xB371, 0x522B, 0x342B,
- 0xB372, 0xB373, 0xB374, 0, 0xB375, 0, 0, 0,
- 0x2E69, 0xB377, 0x372E, 0x522E, 0xB378, 0x522F, 0xB379, 0xA34B,
- 0x5230, 0x5231, 0x3C5B, 0x2E6A, 0, 0, 0x387B, 0x4C5E,
-};
-static const unsigned short utf8_to_euc_E58C[] = {
- 0xB37B, 0x4C68, 0x4677, 0xB37C, 0, 0x4A71, 0x5232, 0xF432,
- 0x5233, 0, 0xB37D, 0xB37E, 0xB421, 0x5235, 0, 0x5237,
- 0x5236, 0xB422, 0, 0xB423, 0, 0x5238, 0x323D, 0x4B4C,
- 0xB424, 0x3A7C, 0x5239, 0xB425, 0xB426, 0x4159, 0xB427, 0xB428,
- 0x3E22, 0x3629, 0, 0x523A, 0xF433, 0xB429, 0, 0xB42A,
- 0xB42B, 0xB42C, 0x485B, 0xB42D, 0xB42E, 0xB42F, 0, 0x523B,
- 0xB430, 0x523C, 0xB431, 0x523D, 0, 0xB432, 0, 0,
- 0x523E, 0x4924, 0x3668, 0x3065, 0xB433, 0xB434, 0xB435, 0x463F,
-};
-static const unsigned short utf8_to_euc_E58C_x0213[] = {
- 0x2E6B, 0x4C68, 0x4677, 0xB37C, 0, 0x4A71, 0x5232, 0x2E6C,
- 0x5233, 0, 0xA34C, 0xA34D, 0xB421, 0x5235, 0, 0x5237,
- 0x5236, 0xB422, 0, 0xB423, 0, 0x5238, 0x323D, 0x4B4C,
- 0xB424, 0x3A7C, 0x5239, 0xB425, 0x2E6D, 0x4159, 0xB427, 0xB428,
- 0x3E22, 0x3629, 0, 0x523A, 0xA34E, 0xB429, 0, 0xB42A,
- 0xB42B, 0xB42C, 0x485B, 0xB42D, 0xB42E, 0xB42F, 0, 0x523B,
- 0xB430, 0x523C, 0xB431, 0x523D, 0, 0xA34F, 0, 0,
- 0x523E, 0x4924, 0x3668, 0x3065, 0xB433, 0xB434, 0xA350, 0x463F,
-};
-static const unsigned short utf8_to_euc_E58D[] = {
- 0x523F, 0x3D3D, 0xB436, 0x4069, 0, 0x5241, 0x5240, 0x3E23,
- 0x3861, 0x5243, 0x483E, 0xB438, 0xB437, 0x5244, 0, 0,
- 0, 0x485C, 0x4234, 0x426E, 0x3628, 0, 0, 0x466E,
- 0x4331, 0xB439, 0x476E, 0xB43A, 0x4B4E, 0, 0x5246, 0,
- 0x406A, 0xB43B, 0, 0xB43C, 0, 0xB43D, 0x3735, 0,
- 0, 0x5247, 0, 0, 0xB43E, 0xB43F, 0x5248, 0x312C,
- 0x3075, 0x346D, 0xB440, 0x4228, 0x3551, 0x4D71, 0, 0x524B,
- 0x3237, 0xB441, 0, 0x524A, 0, 0, 0xB442, 0x362A,
-};
-static const unsigned short utf8_to_euc_E58D_x0213[] = {
- 0x523F, 0x3D3D, 0xA351, 0x4069, 0, 0x5241, 0x5240, 0x3E23,
- 0x3861, 0x5243, 0x483E, 0xB438, 0xB437, 0x5244, 0, 0,
- 0, 0x485C, 0x4234, 0x426E, 0x3628, 0, 0, 0x466E,
- 0x4331, 0xB439, 0x476E, 0xB43A, 0x4B4E, 0, 0x5246, 0,
- 0x406A, 0x2E6F, 0, 0x2E70, 0, 0xB43D, 0x3735, 0xA354,
- 0, 0x5247, 0, 0, 0xA355, 0xB43F, 0x5248, 0x312C,
- 0x3075, 0x346D, 0, 0x4228, 0x3551, 0x4D71, 0, 0x524B,
- 0x3237, 0xB441, 0xA356, 0x524A, 0, 0x2E71, 0xB442, 0x362A,
-};
-static const unsigned short utf8_to_euc_E58E[] = {
- 0, 0, 0x524C, 0xB443, 0x4C71, 0, 0, 0xB444,
- 0xB445, 0, 0, 0, 0, 0, 0xB446, 0,
- 0, 0, 0, 0xB447, 0xB448, 0, 0x524D, 0,
- 0x4E52, 0xB449, 0x387C, 0, 0, 0xB44A, 0, 0x3836,
- 0x524E, 0xB44B, 0, 0, 0xB44C, 0x5250, 0x524F, 0,
- 0x3F5F, 0x3139, 0xB44D, 0xB44E, 0, 0x315E, 0x5251, 0xB44F,
- 0x5252, 0, 0xB450, 0x3837, 0xB451, 0xB452, 0x5253, 0xB453,
- 0xB454, 0, 0xB455, 0x356E, 0, 0xB456, 0, 0,
-};
-static const unsigned short utf8_to_euc_E58E_x0213[] = {
- 0, 0, 0x524C, 0xB443, 0x4C71, 0, 0, 0xB444,
- 0xB445, 0, 0, 0, 0, 0, 0xB446, 0,
- 0, 0, 0, 0x2E72, 0xB448, 0, 0x524D, 0,
- 0x4E52, 0xB449, 0x387C, 0, 0, 0x2E73, 0, 0x3836,
- 0x524E, 0xB44B, 0, 0, 0xA357, 0x5250, 0x524F, 0,
- 0x3F5F, 0x3139, 0xB44D, 0xB44E, 0, 0x315E, 0x5251, 0xB44F,
- 0x5252, 0, 0x2E74, 0x3837, 0xA358, 0xB452, 0x5253, 0xA35A,
- 0xB454, 0, 0xB455, 0x356E, 0, 0xB456, 0, 0,
-};
-static const unsigned short utf8_to_euc_E58F[] = {
- 0xB457, 0, 0x3B32, 0x5254, 0, 0xB458, 0, 0,
- 0x4B74, 0x3A35, 0x355A, 0x4D27, 0x4150, 0x483F, 0x3C7D, 0xB459,
- 0, 0, 0xB45A, 0xB45B, 0x3D47, 0xB45C, 0x3C68, 0x3C75,
- 0, 0x3D76, 0xB45D, 0x4840, 0, 0xB45E, 0xB45F, 0x5257,
- 0xB460, 0x3143, 0x4151, 0x387D, 0x3845, 0x3667, 0xB461, 0xB462,
- 0x525B, 0x4321, 0x427E, 0x362B, 0x3E24, 0x525C, 0x525A, 0x3244,
- 0x4266, 0x3C38, 0x3B4B, 0x3126, 0, 0xB463, 0x3370, 0x3966,
- 0x3B4A, 0, 0x525D, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E58F_x0213[] = {
- 0xA35B, 0, 0x3B32, 0x5254, 0, 0xB458, 0, 0,
- 0x4B74, 0x3A35, 0x355A, 0x4D27, 0x4150, 0x483F, 0x3C7D, 0xB459,
- 0, 0, 0xB45A, 0xB45B, 0x3D47, 0xA35F, 0x3C68, 0x3C75,
- 0, 0x3D76, 0xA360, 0x4840, 0, 0, 0xB45F, 0x5257,
- 0xB460, 0x3143, 0x4151, 0x387D, 0x3845, 0x3667, 0xB461, 0xB462,
- 0x525B, 0x4321, 0x427E, 0x362B, 0x3E24, 0x525C, 0x525A, 0x3244,
- 0x4266, 0x3C38, 0x3B4B, 0x3126, 0xA362, 0xA363, 0x3370, 0x3966,
- 0x3B4A, 0, 0x525D, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E590[] = {
- 0, 0x525E, 0xB464, 0x3549, 0x3346, 0, 0, 0,
- 0x3967, 0x3548, 0x445F, 0x3125, 0x4631, 0x4C3E, 0x3921, 0x4D79,
- 0x4547, 0x387E, 0, 0xB465, 0, 0, 0, 0,
- 0, 0, 0xB466, 0x372F, 0, 0x5267, 0, 0x3663,
- 0x4B4A, 0xB467, 0, 0, 0, 0, 0x485D, 0xB468,
- 0xB469, 0x5266, 0xB46A, 0x345E, 0x5261, 0x5262, 0x5264, 0xB46B,
- 0, 0xB46C, 0, 0, 0xB46D, 0xB46E, 0x5265, 0,
- 0x355B, 0x3F61, 0, 0x4A2D, 0x5263, 0x525F, 0x3863, 0,
-};
-static const unsigned short utf8_to_euc_E590_x0213[] = {
- 0, 0x525E, 0xB464, 0x3549, 0x3346, 0, 0, 0,
- 0x3967, 0x3548, 0x445F, 0x3125, 0x4631, 0x4C3E, 0x3921, 0x4D79,
- 0x4547, 0x387E, 0x2E75, 0xB465, 0, 0, 0, 0,
- 0, 0, 0xB466, 0x372F, 0, 0x5267, 0x4F7E, 0x3663,
- 0x4B4A, 0xB467, 0, 0, 0xA365, 0, 0x485D, 0x2E76,
- 0xA366, 0x5266, 0xB46A, 0x345E, 0x5261, 0x5262, 0x5264, 0xB46B,
- 0, 0xB46C, 0, 0, 0xB46D, 0xB46E, 0x5265, 0,
- 0x355B, 0x3F61, 0, 0x4A2D, 0x5263, 0x525F, 0x3863, 0,
-};
-static const unsigned short utf8_to_euc_E591[] = {
- 0x5260, 0, 0x4F24, 0xB46F, 0xB470, 0, 0x4A72, 0xB471,
- 0x4468, 0x3862, 0x3970, 0, 0, 0xB472, 0x5268, 0xB473,
- 0, 0x465D, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xB474, 0x526C,
- 0, 0, 0xB475, 0, 0xB476, 0, 0xB477, 0xB478,
- 0x3C7E, 0xB479, 0x3C76, 0xB47A, 0, 0xB47B, 0xB47C, 0,
- 0x526F, 0x526D, 0, 0x4C23, 0xB47D, 0x526A, 0x5273, 0x526E,
- 0, 0, 0, 0x5271, 0x3846, 0x4C3F, 0, 0xB47E,
-};
-static const unsigned short utf8_to_euc_E591_x0213[] = {
- 0x5260, 0, 0x4F24, 0xA368, 0xB470, 0, 0x4A72, 0xB471,
- 0x4468, 0x3862, 0x3970, 0, 0, 0x2E77, 0x5268, 0xB473,
- 0, 0x465D, 0, 0, 0, 0xA364, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xB474, 0x526C,
- 0, 0, 0xA369, 0, 0xB476, 0, 0xA36A, 0xB478,
- 0x3C7E, 0xB479, 0x3C76, 0x2E79, 0xA36B, 0xB47B, 0xB47C, 0,
- 0x526F, 0x526D, 0, 0x4C23, 0x2E7A, 0x526A, 0x5273, 0x526E,
- 0, 0, 0, 0x5271, 0x3846, 0x4C3F, 0, 0x2E7B,
-};
-static const unsigned short utf8_to_euc_E592[] = {
- 0x5272, 0xB521, 0, 0xB522, 0x5274, 0xB523, 0x5276, 0,
- 0xB524, 0xB525, 0xF435, 0x3A70, 0x4F42, 0xB526, 0x526B, 0x5269,
- 0x5275, 0xB527, 0x5270, 0, 0, 0xB528, 0xB529, 0,
- 0, 0, 0, 0, 0xB52A, 0, 0, 0xB52B,
- 0, 0xB52C, 0x5278, 0, 0x5323, 0x527A, 0xB52D, 0xB52E,
- 0x527E, 0xB52F, 0xB530, 0x5321, 0x527B, 0xB531, 0xB532, 0x533E,
- 0, 0xB533, 0x3A69, 0x3331, 0, 0, 0, 0xB534,
- 0x5279, 0xB535, 0xB536, 0xB537, 0x5325, 0x3076, 0x5324, 0xB538,
-};
-static const unsigned short utf8_to_euc_E592_x0213[] = {
- 0x5272, 0xB521, 0, 0xB522, 0x5274, 0xB523, 0x5276, 0,
- 0x2E7C, 0xB525, 0xA36C, 0x3A70, 0x4F42, 0xA36D, 0x526B, 0x5269,
- 0x5275, 0xB527, 0x5270, 0, 0, 0xA36E, 0x2E7D, 0,
- 0, 0, 0, 0, 0x2E78, 0, 0, 0xB52B,
- 0xA36F, 0x2E7E, 0x5278, 0, 0x5323, 0x527A, 0xA370, 0xB52E,
- 0x527E, 0x2F21, 0xB530, 0x5321, 0x527B, 0xA371, 0xA372, 0x533E,
- 0, 0xB533, 0x3A69, 0x3331, 0, 0, 0, 0xA373,
- 0x5279, 0xB535, 0xA374, 0xB537, 0x5325, 0x3076, 0x5324, 0xA375,
-};
-static const unsigned short utf8_to_euc_E593[] = {
- 0x3025, 0x494A, 0x5322, 0, 0x527C, 0, 0xB539, 0x5277,
- 0x527D, 0x3A48, 0xB53A, 0, 0, 0xB53B, 0xB53C, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x5326, 0, 0, 0, 0, 0, 0, 0,
- 0xB53D, 0x3077, 0x532F, 0, 0, 0x5327, 0x5328, 0,
- 0x3E25, 0x4B69, 0xB53E, 0, 0xB53F, 0x532D, 0x532C, 0xB540,
- 0, 0, 0x452F, 0, 0, 0, 0xB541, 0,
- 0, 0, 0x532E, 0, 0xB542, 0x532B, 0xB543, 0xB544,
-};
-static const unsigned short utf8_to_euc_E593_x0213[] = {
- 0x3025, 0x494A, 0x5322, 0xA376, 0x527C, 0, 0x2F22, 0x5277,
- 0x527D, 0x3A48, 0xB53A, 0, 0, 0xB53B, 0xB53C, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x5326, 0, 0, 0, 0, 0, 0, 0,
- 0xB53D, 0x3077, 0x532F, 0, 0, 0x5327, 0x5328, 0,
- 0x3E25, 0x4B69, 0xB53E, 0, 0xA378, 0x532D, 0x532C, 0xA379,
- 0, 0xA37A, 0x452F, 0xA37B, 0, 0, 0xB541, 0,
- 0, 0, 0x532E, 0, 0xB542, 0x532B, 0xB543, 0x2F23,
-};
-static const unsigned short utf8_to_euc_E594[] = {
- 0xB545, 0xB546, 0, 0, 0x3134, 0xB547, 0x3A36, 0x3F30,
- 0xB548, 0xB549, 0, 0, 0xB54A, 0xB54B, 0xB54C, 0x5329,
- 0x4562, 0, 0, 0, 0x532A, 0xB54D, 0x3022, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xB54E, 0xB54F, 0, 0, 0x5334, 0x4D23,
- 0, 0x3E27, 0xB550, 0x533A, 0, 0xB551, 0xB552, 0,
- 0x5339, 0x5330, 0, 0xB553, 0xB554, 0xB555, 0x4243, 0,
-};
-static const unsigned short utf8_to_euc_E594_x0213[] = {
- 0xA37C, 0xA37D, 0, 0, 0x3134, 0xB547, 0x3A36, 0x3F30,
- 0xB548, 0xA37E, 0, 0, 0xB54A, 0xB54B, 0x2F24, 0x5329,
- 0x4562, 0, 0, 0, 0x532A, 0xB54D, 0x3022, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xB54E, 0x2F25, 0, 0, 0x5334, 0x4D23,
- 0, 0x3E27, 0xB550, 0x533A, 0, 0x2F26, 0xB552, 0,
- 0x5339, 0x5330, 0, 0xB553, 0xA421, 0xB555, 0x4243, 0,
-};
-static const unsigned short utf8_to_euc_E595[] = {
- 0x5331, 0xB556, 0, 0, 0x426F, 0x5336, 0x3E26, 0xB557,
- 0, 0xB558, 0xB559, 0, 0x5333, 0xB55A, 0, 0x4C64,
- 0xB55B, 0xB55C, 0, 0x373C, 0, 0, 0x5337, 0x5338,
- 0xB55D, 0, 0xB55E, 0xB55F, 0x5335, 0x533B, 0xB560, 0,
- 0xB561, 0xB562, 0, 0x5332, 0xB563, 0, 0xB564, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x5341, 0x5346, 0, 0x5342, 0xB565,
-};
-static const unsigned short utf8_to_euc_E595_x0213[] = {
- 0x5331, 0xA422, 0, 0, 0x426F, 0x5336, 0x3E26, 0xA424,
- 0, 0xB558, 0xA425, 0, 0x5333, 0xB55A, 0, 0x4C64,
- 0x2F27, 0xB55C, 0, 0x373C, 0, 0, 0x5337, 0x5338,
- 0xB55D, 0, 0xB55E, 0xB55F, 0x5335, 0x533B, 0x2F28, 0,
- 0xA427, 0xA428, 0, 0x5332, 0xA429, 0, 0xB564, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x5341, 0x5346, 0xA42B, 0x5342, 0xB565,
-};
-static const unsigned short utf8_to_euc_E596[] = {
- 0x533D, 0xB566, 0xB567, 0x5347, 0x4131, 0, 0xB568, 0x5349,
- 0xB569, 0x3922, 0x533F, 0x437D, 0, 0, 0xB56A, 0xB56B,
- 0, 0xB56C, 0xB56D, 0xB56E, 0xB56F, 0, 0, 0xB570,
- 0x5343, 0x533C, 0x342D, 0, 0x346E, 0x3365, 0x5344, 0x5340,
- 0, 0, 0, 0xB571, 0xB572, 0, 0, 0x3776,
- 0x534A, 0x5348, 0x4153, 0x354A, 0x362C, 0xB573, 0x5345, 0,
- 0x3674, 0, 0xB574, 0, 0, 0, 0x3144, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xB575,
-};
-static const unsigned short utf8_to_euc_E596_x0213[] = {
- 0x533D, 0x2F29, 0xA42C, 0x5347, 0x4131, 0, 0x2F2A, 0x5349,
- 0xA42D, 0x3922, 0x533F, 0x437D, 0, 0, 0x2F2B, 0xB56B,
- 0, 0xA42E, 0xB56D, 0xB56E, 0xB56F, 0, 0, 0xB570,
- 0x5343, 0x533C, 0x342D, 0, 0x346E, 0x3365, 0x5344, 0x5340,
- 0, 0, 0, 0xB571, 0xB572, 0, 0, 0x3776,
- 0x534A, 0x5348, 0x4153, 0x354A, 0x362C, 0x2F2D, 0x5345, 0,
- 0x3674, 0, 0xB574, 0, 0, 0, 0x3144, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xA433,
-};
-static const unsigned short utf8_to_euc_E597[] = {
- 0, 0xB576, 0, 0xB577, 0x534E, 0x534C, 0xB578, 0x5427,
- 0, 0xB579, 0, 0xB57A, 0xB57B, 0, 0xB57C, 0,
- 0, 0xB57D, 0xB57E, 0xB621, 0x5351, 0, 0, 0xB622,
- 0xB623, 0, 0x534B, 0xB624, 0x534F, 0, 0xB625, 0x534D,
- 0, 0, 0xB626, 0x3B4C, 0x5350, 0, 0, 0,
- 0, 0xB627, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xB628, 0x5353,
- 0, 0x5358, 0, 0, 0, 0x5356, 0x5355, 0xB629,
-};
-static const unsigned short utf8_to_euc_E597_x0213[] = {
- 0, 0xB576, 0, 0xB577, 0x534E, 0x534C, 0xB578, 0x5427,
- 0, 0xA434, 0, 0xB57A, 0xA435, 0, 0x2F2E, 0,
- 0, 0xA436, 0xA430, 0xB621, 0x5351, 0, 0, 0xB622,
- 0xB623, 0, 0x534B, 0xB624, 0x534F, 0xA437, 0xB625, 0x534D,
- 0, 0, 0xA439, 0x3B4C, 0x5350, 0, 0, 0,
- 0, 0xA43B, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xB628, 0x5353,
- 0, 0x5358, 0, 0, 0, 0x5356, 0x5355, 0xB629,
-};
-static const unsigned short utf8_to_euc_E598[] = {
- 0, 0, 0, 0, 0, 0xB62A, 0x4332, 0,
- 0xB62B, 0x3245, 0xB62C, 0, 0, 0xB62D, 0xB62E, 0xB62F,
- 0xB630, 0xB631, 0xB632, 0, 0x5352, 0, 0x5354, 0x3E28,
- 0x3133, 0xB633, 0, 0x5357, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0x325E, 0, 0, 0xB634, 0, 0, 0x5362,
- 0xB635, 0x3E7C, 0x535E, 0xB636, 0x535C, 0xB637, 0x535D, 0xB638,
- 0x535F, 0xB639, 0, 0xB63A, 0xB63B, 0xB63C, 0, 0xB63D,
-};
-static const unsigned short utf8_to_euc_E598_x0213[] = {
- 0, 0, 0, 0, 0, 0xB62A, 0x4332, 0xA43E,
- 0x2F30, 0x3245, 0xB62C, 0, 0, 0xB62D, 0x2F31, 0xB62F,
- 0xA43F, 0xB631, 0xB632, 0, 0x5352, 0, 0x5354, 0x3E28,
- 0x3133, 0xB633, 0, 0x5357, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xA43C, 0x325E, 0, 0, 0xB634, 0, 0, 0x5362,
- 0xA440, 0x3E7C, 0x535E, 0xB636, 0x535C, 0xB637, 0x535D, 0xA441,
- 0x535F, 0xB639, 0, 0x2F32, 0xB63B, 0xA443, 0, 0xA444,
-};
-static const unsigned short utf8_to_euc_E599[] = {
- 0xB63E, 0xB63F, 0x313D, 0xB640, 0xB641, 0, 0xB642, 0,
- 0, 0xB643, 0, 0xB644, 0x4139, 0xB645, 0x5359, 0xB646,
- 0x535A, 0, 0, 0, 0xB647, 0, 0, 0,
- 0, 0, 0, 0x337A, 0, 0, 0xB648, 0,
- 0xB649, 0xB64A, 0xB64B, 0xB64C, 0x5361, 0, 0xB64D, 0,
- 0x346F, 0xB64E, 0x5364, 0x5360, 0x5363, 0xB64F, 0, 0xB650,
- 0, 0xB651, 0xB652, 0, 0x4A2E, 0xB653, 0, 0,
- 0x4655, 0, 0x4838, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E599_x0213[] = {
- 0xA445, 0xB63F, 0x313D, 0xB640, 0xB641, 0, 0xB642, 0xA446,
- 0, 0x2F33, 0, 0xB644, 0x4139, 0xB645, 0x5359, 0xB646,
- 0x535A, 0, 0, 0x7427, 0xB647, 0, 0, 0,
- 0, 0, 0, 0x337A, 0, 0, 0xA447, 0,
- 0xA448, 0xB64A, 0xB64B, 0xB64C, 0x5361, 0, 0x2F35, 0,
- 0x346F, 0xB64E, 0x5364, 0x5360, 0x5363, 0xA449, 0, 0x2F37,
- 0, 0x2F38, 0x2F39, 0, 0x4A2E, 0xB653, 0x2F34, 0,
- 0x4655, 0, 0x4838, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E59A[] = {
- 0x5366, 0, 0, 0, 0xB654, 0xB655, 0x5365, 0x3345,
- 0xB656, 0, 0x5367, 0xB657, 0xB658, 0, 0, 0x536A,
- 0, 0, 0, 0, 0x5369, 0xB659, 0, 0,
- 0, 0xB65A, 0xB65B, 0, 0, 0xB65C, 0xB65D, 0xB65E,
- 0x5368, 0, 0x4739, 0, 0, 0x536B, 0xB65F, 0xB660,
- 0xB661, 0xB662, 0, 0xB663, 0xB664, 0xB665, 0x536C, 0,
- 0, 0xB666, 0, 0xB667, 0x536E, 0, 0x536D, 0xB668,
- 0, 0, 0, 0, 0x5370, 0, 0xB669, 0,
-};
-static const unsigned short utf8_to_euc_E59A_x0213[] = {
- 0x5366, 0, 0, 0, 0xB654, 0xB655, 0x5365, 0x3345,
- 0xA44B, 0, 0x5367, 0xB657, 0xA44C, 0, 0, 0x536A,
- 0, 0, 0, 0, 0x5369, 0xA44D, 0, 0,
- 0, 0x2F3A, 0xA44E, 0, 0, 0xA44F, 0x2F3B, 0xB65E,
- 0x5368, 0, 0x4739, 0, 0, 0x536B, 0xB65F, 0xB660,
- 0xA450, 0x2F3C, 0, 0xB663, 0x2F3D, 0xA451, 0x536C, 0,
- 0, 0xB666, 0xA452, 0x2F3E, 0x536E, 0, 0x536D, 0xB668,
- 0, 0, 0, 0, 0x5370, 0, 0xB669, 0,
-};
-static const unsigned short utf8_to_euc_E59B[] = {
- 0x5373, 0x5371, 0x536F, 0x5372, 0, 0xB66A, 0, 0,
- 0x5374, 0xB66B, 0xB66C, 0xB66D, 0xB670, 0xB671, 0x5375, 0xB66E,
- 0xB66F, 0x5376, 0, 0x5377, 0, 0, 0, 0x5378,
- 0x5145, 0xB672, 0x3C7C, 0x3B4D, 0xB673, 0xB674, 0x3273, 0xB675,
- 0x3078, 0xB676, 0, 0x4344, 0xB677, 0xB678, 0xB679, 0xB67A,
- 0xB67B, 0, 0, 0xB67D, 0, 0xB67E, 0x5379, 0,
- 0x3A24, 0xB67C, 0x304F, 0x3F5E, 0, 0, 0xB721, 0xB722,
- 0, 0x537A, 0x3847, 0, 0, 0x3971, 0, 0x537C,
-};
-static const unsigned short utf8_to_euc_E59B_x0213[] = {
- 0x5373, 0x5371, 0x536F, 0x5372, 0, 0xA453, 0, 0,
- 0x5374, 0x2F3F, 0x2F40, 0xB66D, 0xB670, 0xA454, 0x5375, 0xB66E,
- 0xB66F, 0x5376, 0, 0x5377, 0, 0, 0, 0x5378,
- 0x5145, 0xB672, 0x3C7C, 0x3B4D, 0xB673, 0xB674, 0x3273, 0xA455,
- 0x3078, 0xB676, 0, 0x4344, 0xB677, 0xB678, 0xB679, 0xB67A,
- 0xA456, 0, 0, 0xB67D, 0, 0xB67E, 0x5379, 0,
- 0x3A24, 0xB67C, 0x304F, 0x3F5E, 0, 0, 0xA457, 0xA458,
- 0, 0x537A, 0x3847, 0, 0, 0x3971, 0, 0x537C,
-};
-static const unsigned short utf8_to_euc_E59C[] = {
- 0x537B, 0xB723, 0xB724, 0x4A60, 0x537D, 0, 0, 0xB725,
- 0x5421, 0x537E, 0xB726, 0x5422, 0xB727, 0x5423, 0, 0x3777,
- 0, 0xB728, 0x3160, 0x5424, 0, 0xB729, 0x5426, 0,
- 0x5425, 0, 0xB72A, 0xB72B, 0x5428, 0xB72C, 0, 0x455A,
- 0xB72D, 0, 0xB72E, 0xB72F, 0xB730, 0xB731, 0x5429, 0x3035,
- 0x3A5F, 0xB732, 0xB733, 0, 0xB734, 0x373D, 0xB735, 0xB736,
- 0x434F, 0, 0, 0xB737, 0xB738, 0, 0, 0x542A,
- 0x542B, 0, 0, 0x542D, 0, 0xB739, 0xB73A, 0xB73B,
-};
-static const unsigned short utf8_to_euc_E59C_x0213[] = {
- 0x537B, 0xB723, 0xB724, 0x4A60, 0x537D, 0, 0, 0xB725,
- 0x5421, 0x537E, 0x2F41, 0x5422, 0xB727, 0x5423, 0, 0x3777,
- 0, 0xB728, 0x3160, 0x5424, 0, 0xA45A, 0x5426, 0,
- 0x5425, 0, 0xB72A, 0xB72B, 0x5428, 0xB72C, 0, 0x455A,
- 0xB72D, 0x2F43, 0xB72E, 0xA45B, 0xB730, 0xB731, 0x5429, 0x3035,
- 0x3A5F, 0xA45D, 0xB733, 0, 0xB734, 0x373D, 0xB735, 0x2F44,
- 0x434F, 0, 0, 0x2F45, 0x2F46, 0, 0, 0x542A,
- 0x542B, 0, 0, 0x542D, 0, 0xB739, 0xB73A, 0xB73B,
-};
-static const unsigned short utf8_to_euc_E59D[] = {
- 0x542E, 0, 0x3A64, 0, 0, 0xB73C, 0xB73D, 0x3651,
- 0, 0, 0x4B37, 0, 0xB73E, 0xB73F, 0x542C, 0x542F,
- 0x3A41, 0x3923, 0xB740, 0, 0, 0, 0, 0,
- 0, 0xF436, 0, 0, 0, 0, 0, 0,
- 0, 0x5433, 0xB741, 0, 0x3A25, 0xB742, 0x4333, 0xB743,
- 0xB744, 0x5430, 0x445A, 0xB745, 0, 0xB746, 0xB747, 0xB748,
- 0xB749, 0xB74A, 0, 0xB74B, 0xB74C, 0xB74D, 0, 0xB74E,
- 0, 0xB74F, 0xB750, 0xB751, 0xB752, 0, 0xB753, 0x5434,
-};
-static const unsigned short utf8_to_euc_E59D_x0213[] = {
- 0x542E, 0, 0x3A64, 0, 0, 0xA45F, 0xA460, 0x3651,
- 0, 0, 0x4B37, 0, 0xA461, 0xA462, 0x542C, 0x542F,
- 0x3A41, 0x3923, 0xB740, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0x5433, 0xB741, 0, 0x3A25, 0, 0x4333, 0xB743,
- 0xA464, 0x5430, 0x445A, 0xB745, 0, 0xB746, 0xB747, 0xA465,
- 0x2F47, 0xB74A, 0, 0xA466, 0xA467, 0xA468, 0, 0x2F48,
- 0, 0xB74F, 0xB750, 0xA469, 0x2F49, 0, 0xB753, 0x5434,
-};
-static const unsigned short utf8_to_euc_E59E[] = {
- 0, 0xB754, 0x3F62, 0xB755, 0, 0, 0, 0,
- 0x5432, 0x5435, 0, 0x373F, 0xB756, 0, 0, 0,
- 0, 0, 0, 0x5436, 0xB757, 0xB760, 0, 0xB758,
- 0, 0xB759, 0xB75A, 0, 0xB75B, 0xB75C, 0xB75D, 0xB75E,
- 0x5437, 0xB75F, 0x3924, 0x3340, 0x5439, 0, 0, 0xB761,
- 0xB762, 0xB763, 0x543A, 0, 0xB764, 0, 0, 0,
- 0x543B, 0, 0, 0x5438, 0, 0, 0, 0,
- 0xB765, 0, 0, 0, 0, 0xB766, 0, 0,
-};
-static const unsigned short utf8_to_euc_E59E_x0213[] = {
- 0, 0xB754, 0x3F62, 0xB755, 0, 0, 0, 0,
- 0x5432, 0x5435, 0, 0x373F, 0xB756, 0, 0, 0,
- 0, 0, 0, 0x5436, 0xB757, 0xB760, 0, 0xB758,
- 0, 0xB759, 0xA46D, 0, 0x2F4A, 0xA46E, 0xA46F, 0xB75E,
- 0x5437, 0xB75F, 0x3924, 0x3340, 0x5439, 0, 0, 0xB761,
- 0xA470, 0xB763, 0x543A, 0, 0xA46C, 0, 0, 0,
- 0x543B, 0, 0, 0x5438, 0, 0, 0, 0,
- 0x2F4D, 0, 0, 0, 0, 0xB766, 0, 0,
-};
-static const unsigned short utf8_to_euc_E59F[] = {
- 0x5431, 0, 0, 0x543C, 0, 0, 0x543D, 0xB767,
- 0xB768, 0, 0, 0x4B64, 0xB769, 0, 0x3E6B, 0xB76A,
- 0, 0, 0x543F, 0x5440, 0x543E, 0xB76B, 0x5442, 0,
- 0, 0, 0, 0, 0x4738, 0xB76C, 0xB76D, 0x3068,
- 0x4956, 0xB77E, 0, 0x5443, 0xB76E, 0, 0xB76F, 0xB770,
- 0, 0xB771, 0, 0, 0, 0xB772, 0, 0,
- 0xB773, 0, 0, 0, 0x3E7D, 0xB774, 0xB775, 0x3C39,
- 0xB776, 0x475D, 0x3470, 0, 0x3A6B, 0xB777, 0xB778, 0xB779,
-};
-static const unsigned short utf8_to_euc_E59F_x0213[] = {
- 0x5431, 0, 0, 0x543C, 0, 0, 0x543D, 0x2F4E,
- 0x2F4F, 0, 0, 0x4B64, 0xA473, 0, 0x3E6B, 0x2F50,
- 0, 0, 0x543F, 0x5440, 0x543E, 0xB76B, 0x5442, 0xA471,
- 0, 0, 0, 0, 0x4738, 0xB76C, 0xA476, 0x3068,
- 0x4956, 0xB77E, 0, 0x5443, 0x2F51, 0, 0xA477, 0xB770,
- 0, 0xB771, 0, 0, 0, 0x2F52, 0, 0,
- 0xA478, 0, 0, 0, 0x3E7D, 0x2F53, 0x2F54, 0x3C39,
- 0xA47A, 0x475D, 0x3470, 0xA47B, 0x3A6B, 0xA47C, 0xB778, 0x2F55,
-};
-static const unsigned short utf8_to_euc_E5A0[] = {
- 0x4B59, 0, 0x4632, 0xB77A, 0xB77B, 0x3778, 0x424F, 0,
- 0xB77C, 0xB77D, 0x5441, 0x5444, 0xB821, 0xB822, 0, 0,
- 0, 0, 0, 0, 0, 0x4244, 0, 0,
- 0, 0x5445, 0, 0xB823, 0, 0x5446, 0xB824, 0xB825,
- 0xB826, 0x5448, 0, 0, 0x4469, 0, 0xB827, 0xB828,
- 0, 0, 0x342E, 0, 0, 0xB829, 0, 0x7421,
- 0x3161, 0x4A73, 0xB82A, 0, 0x3E6C, 0x4548, 0, 0,
- 0, 0xB82B, 0x3A66, 0, 0, 0x544E, 0, 0xB82C,
-};
-static const unsigned short utf8_to_euc_E5A0_x0213[] = {
- 0x4B59, 0, 0x4632, 0xB77A, 0xA47D, 0x3778, 0x424F, 0,
- 0xB77C, 0x2F56, 0x5441, 0x5444, 0xB821, 0xB822, 0, 0,
- 0, 0, 0, 0, 0, 0x4244, 0, 0,
- 0, 0x5445, 0, 0xB823, 0, 0x5446, 0xA47E, 0xB825,
- 0xA521, 0x5448, 0, 0, 0x4469, 0, 0xB827, 0xA522,
- 0, 0, 0x342E, 0, 0, 0xB829, 0, 0x7421,
- 0x3161, 0x4A73, 0xA523, 0, 0x3E6C, 0x4548, 0, 0,
- 0, 0xA524, 0x3A66, 0, 0, 0x544E, 0, 0xB82C,
-};
-static const unsigned short utf8_to_euc_E5A1[] = {
- 0x4A3D, 0x4E5D, 0, 0, 0, 0, 0, 0,
- 0, 0xB82D, 0x3274, 0x544A, 0xB82E, 0xB82F, 0, 0xB830,
- 0xB831, 0x413A, 0x544D, 0, 0x4563, 0xB832, 0, 0x4549,
- 0x4564, 0x4839, 0x444D, 0, 0, 0, 0x3A49, 0xB833,
- 0, 0xB834, 0x5449, 0, 0xB835, 0, 0, 0xB836,
- 0xB837, 0x3176, 0, 0x4536, 0, 0, 0, 0,
- 0x544B, 0, 0x5447, 0, 0, 0x3F50, 0, 0,
- 0xB838, 0x544F, 0, 0, 0xB839, 0, 0x3D4E, 0xB83A,
-};
-static const unsigned short utf8_to_euc_E5A1_x0213[] = {
- 0x4A3D, 0x4E5D, 0, 0, 0, 0, 0, 0,
- 0, 0xA526, 0x3274, 0x544A, 0xA527, 0xB82F, 0, 0xB830,
- 0xB831, 0x413A, 0x544D, 0, 0x4563, 0xB832, 0, 0x4549,
- 0x4564, 0x4839, 0x444D, 0, 0, 0, 0x3A49, 0xB833,
- 0, 0x2F58, 0x5449, 0, 0x2F59, 0, 0, 0xA528,
- 0xB837, 0x3176, 0, 0x4536, 0, 0, 0, 0,
- 0x544B, 0, 0x5447, 0, 0, 0x3F50, 0, 0,
- 0xB838, 0x544F, 0, 0, 0x2F5B, 0, 0x3D4E, 0xB83A,
-};
-static const unsigned short utf8_to_euc_E5A2[] = {
- 0xB83B, 0xB83C, 0, 0x362D, 0, 0x5450, 0, 0xB83D,
- 0xB83E, 0xB83F, 0xB840, 0, 0xB841, 0xB842, 0, 0xB843,
- 0xB844, 0, 0, 0x4A68, 0xB845, 0, 0xB846, 0x417D,
- 0, 0, 0, 0, 0x4446, 0xB847, 0xF439, 0x5452,
- 0xB848, 0xB849, 0xB84A, 0, 0, 0, 0xB84B, 0,
- 0x4B4F, 0xB84C, 0, 0x5453, 0, 0, 0x5458, 0,
- 0, 0xB84D, 0xB84E, 0x4A2F, 0, 0, 0, 0,
- 0x5457, 0x5451, 0x5454, 0x5456, 0xB850, 0, 0x3A26, 0,
-};
-static const unsigned short utf8_to_euc_E5A2_x0213[] = {
- 0xB83B, 0xB83C, 0, 0x362D, 0, 0x5450, 0, 0xB83D,
- 0xB83E, 0x2F5C, 0xA529, 0xA52A, 0xB841, 0xA52B, 0, 0xA52C,
- 0xA52D, 0, 0, 0x4A68, 0xA52E, 0, 0xB846, 0x417D,
- 0, 0, 0, 0, 0x4446, 0xA52F, 0x2F5D, 0x5452,
- 0xB848, 0xB849, 0xB84A, 0, 0, 0, 0xB84B, 0,
- 0x4B4F, 0x2F5F, 0xA530, 0x5453, 0, 0, 0x5458, 0,
- 0, 0xA531, 0, 0x4A2F, 0, 0, 0, 0,
- 0x5457, 0x5451, 0x5454, 0x5456, 0xB850, 0, 0x3A26, 0,
-};
-static const unsigned short utf8_to_euc_E5A3[] = {
- 0, 0x4A49, 0xB851, 0, 0xB84F, 0x5459, 0, 0x4345,
- 0xB852, 0, 0x3275, 0, 0x3E6D, 0xB853, 0xB854, 0,
- 0xB855, 0x545B, 0xB856, 0x545A, 0xB857, 0x3968, 0xB858, 0x545C,
- 0x545E, 0x545D, 0xB859, 0, 0x5460, 0xB85A, 0x5455, 0x5462,
- 0, 0xB85B, 0xB85C, 0, 0x5461, 0x545F, 0, 0,
- 0, 0xB85D, 0, 0x3B4E, 0x3F51, 0, 0x4154, 0x5463,
- 0x403C, 0x306D, 0x4764, 0xB85E, 0, 0, 0, 0x445B,
- 0, 0x5465, 0x5464, 0x5466, 0x5467, 0x5468, 0, 0,
-};
-static const unsigned short utf8_to_euc_E5A3_x0213[] = {
- 0, 0x4A49, 0xB851, 0xA533, 0xB84F, 0x5459, 0, 0x4345,
- 0xB852, 0, 0x3275, 0, 0x3E6D, 0xA534, 0x2F62, 0,
- 0xB855, 0x545B, 0x2F61, 0x545A, 0x2F63, 0x3968, 0xB858, 0x545C,
- 0x545E, 0x545D, 0x2F64, 0, 0x5460, 0xB85A, 0x5455, 0x5462,
- 0x2F65, 0xB85B, 0xA535, 0, 0x5461, 0x545F, 0, 0,
- 0, 0x2F66, 0, 0x3B4E, 0x3F51, 0, 0x4154, 0x5463,
- 0x403C, 0x306D, 0x4764, 0xA536, 0xA537, 0, 0, 0x445B,
- 0, 0x5465, 0x5464, 0x5466, 0x5467, 0x5468, 0, 0,
-};
-static const unsigned short utf8_to_euc_E5A4[] = {
- 0, 0, 0x5469, 0, 0, 0xB85F, 0xB860, 0,
- 0, 0x4A51, 0x546A, 0xB861, 0xB862, 0, 0, 0x3246,
- 0x546B, 0, 0xB863, 0xB864, 0xB865, 0x4D3C, 0x3330, 0,
- 0x5249, 0x3D48, 0x423F, 0x546C, 0x4C6B, 0xB867, 0, 0,
- 0, 0xB868, 0x4C34, 0xB869, 0xB86A, 0x546E, 0, 0x4267,
- 0xB86B, 0x4537, 0x4240, 0x4957, 0x546F, 0x5470, 0x317B, 0xB86C,
- 0xB86D, 0x3C3A, 0x5471, 0xB86E, 0, 0xB86F, 0xB870, 0x3050,
- 0x5472, 0, 0, 0, 0, 0, 0x5473, 0xB871,
-};
-static const unsigned short utf8_to_euc_E5A4_x0213[] = {
- 0, 0, 0x5469, 0, 0, 0xA538, 0xA539, 0,
- 0, 0x4A51, 0x546A, 0xA53A, 0x2F67, 0xA53B, 0, 0x3246,
- 0x546B, 0, 0xB863, 0xB864, 0xA53C, 0x4D3C, 0x3330, 0,
- 0x5249, 0x3D48, 0x423F, 0x546C, 0x4C6B, 0xB867, 0, 0,
- 0, 0xB868, 0x4C34, 0xB869, 0xA53D, 0x546E, 0, 0x4267,
- 0xB86B, 0x4537, 0x4240, 0x4957, 0x546F, 0x5470, 0x317B, 0xB86C,
- 0xB86D, 0x3C3A, 0x5471, 0xB86E, 0, 0xB86F, 0xB870, 0x3050,
- 0x5472, 0, 0, 0, 0, 0xA540, 0x5473, 0xB871,
-};
-static const unsigned short utf8_to_euc_E5A5[] = {
- 0, 0, 0, 0xB872, 0x3162, 0, 0xB873, 0x3471,
- 0x4660, 0x4A74, 0, 0, 0, 0, 0x5477, 0x4155,
- 0x5476, 0x3740, 0xB874, 0xB875, 0x4B5B, 0x5475, 0, 0x4565,
- 0x5479, 0xB876, 0x5478, 0xB877, 0, 0xB878, 0xB879, 0xB87A,
- 0x547B, 0xB87B, 0x547A, 0xB87C, 0, 0x317C, 0, 0x547C,
- 0x3E29, 0x547E, 0x4325, 0xB87D, 0x547D, 0xB87E, 0x4A33, 0xB921,
- 0, 0, 0xB922, 0x3D77, 0x455B, 0xB923, 0xB924, 0,
- 0x5521, 0xB925, 0, 0xB926, 0xB927, 0x3925, 0, 0,
-};
-static const unsigned short utf8_to_euc_E5A5_x0213[] = {
- 0, 0, 0, 0xB872, 0x3162, 0, 0xA542, 0x3471,
- 0x4660, 0x4A74, 0, 0, 0, 0, 0x5477, 0x4155,
- 0x5476, 0x3740, 0xB874, 0, 0x4B5B, 0x5475, 0, 0x4565,
- 0x5479, 0xB876, 0x5478, 0xA545, 0, 0x2F69, 0xB879, 0xA546,
- 0x547B, 0xB87B, 0x547A, 0, 0, 0x317C, 0, 0x547C,
- 0x3E29, 0x547E, 0x4325, 0xB87D, 0x547D, 0x2F6A, 0x4A33, 0xB921,
- 0, 0, 0xB922, 0x3D77, 0x455B, 0xA548, 0xA549, 0,
- 0x5521, 0xB925, 0, 0xB926, 0xA54A, 0x3925, 0, 0,
-};
-static const unsigned short utf8_to_euc_E5A6[] = {
- 0, 0x5522, 0x4721, 0x485E, 0x4C51, 0, 0, 0,
- 0, 0, 0x4725, 0xB928, 0xB929, 0x552B, 0xB92A, 0,
- 0, 0, 0xB92B, 0x3538, 0, 0xB92C, 0x4D45, 0xB92D,
- 0, 0x4C2F, 0, 0x562C, 0, 0x5523, 0, 0xB92E,
- 0, 0, 0, 0x5526, 0xB92F, 0x4245, 0, 0xB930,
- 0x4B38, 0, 0, 0, 0x454A, 0xB931, 0xB932, 0xB933,
- 0xB934, 0, 0x5527, 0xB935, 0, 0, 0, 0xB936,
- 0, 0x4B65, 0xB937, 0x3A4A, 0xB938, 0, 0x3E2A, 0,
-};
-static const unsigned short utf8_to_euc_E5A6_x0213[] = {
- 0, 0x5522, 0x4721, 0x485E, 0x4C51, 0, 0, 0,
- 0, 0, 0x4725, 0x2F6B, 0xB929, 0x552B, 0xB92A, 0,
- 0, 0, 0x2F6C, 0x3538, 0, 0xB92C, 0x4D45, 0xB92D,
- 0, 0x4C2F, 0, 0x562C, 0, 0x5523, 0, 0xA54B,
- 0, 0, 0, 0x5526, 0x2F6D, 0x4245, 0, 0xB930,
- 0x4B38, 0, 0, 0, 0x454A, 0xB931, 0xA54C, 0xB933,
- 0xB934, 0, 0x5527, 0xB935, 0, 0, 0, 0xB936,
- 0, 0x4B65, 0, 0x3A4A, 0xA54D, 0, 0x3E2A, 0,
-};
-static const unsigned short utf8_to_euc_E5A7[] = {
- 0, 0xB939, 0, 0xB93A, 0xB93B, 0, 0x5528, 0,
- 0xB93C, 0x3B50, 0xB93D, 0x3B4F, 0, 0xB93E, 0, 0,
- 0x3039, 0x3848, 0xB93F, 0x402B, 0x3051, 0, 0, 0,
- 0, 0x552C, 0x552D, 0, 0x552A, 0xB940, 0xB941, 0xB942,
- 0, 0, 0, 0xB943, 0xB944, 0x3138, 0x342F, 0xB945,
- 0x5529, 0, 0x4C45, 0x4931, 0, 0, 0xB946, 0xB947,
- 0, 0xB948, 0xB949, 0, 0xB94A, 0, 0x3028, 0xB94B,
- 0, 0, 0, 0x3079, 0, 0, 0, 0x3B51,
-};
-static const unsigned short utf8_to_euc_E5A7_x0213[] = {
- 0, 0xB939, 0, 0x2F6E, 0xB93B, 0, 0x5528, 0,
- 0xA54E, 0x3B50, 0xB93D, 0x3B4F, 0, 0xA54F, 0, 0,
- 0x3039, 0x3848, 0x2F6F, 0x402B, 0x3051, 0, 0, 0,
- 0, 0x552C, 0x552D, 0, 0x552A, 0x2F70, 0xA550, 0xB942,
- 0, 0, 0, 0xA551, 0xA552, 0x3138, 0x342F, 0xA553,
- 0x5529, 0, 0x4C45, 0x4931, 0, 0, 0xA554, 0xB947,
- 0, 0xB948, 0xB949, 0, 0xB94A, 0, 0x3028, 0xB94B,
- 0x7E7A, 0, 0, 0x3079, 0, 0, 0, 0x3B51,
-};
-static const unsigned short utf8_to_euc_E5A8[] = {
- 0xB94C, 0x3052, 0, 0x3023, 0xB94D, 0, 0, 0,
- 0, 0x5532, 0, 0, 0xB94E, 0xB94F, 0xB950, 0,
- 0, 0x5530, 0xB951, 0xB952, 0, 0, 0, 0,
- 0x4C3C, 0, 0x5533, 0, 0x5531, 0, 0xB953, 0x552F,
- 0x3F31, 0, 0, 0xB954, 0xB955, 0x552E, 0, 0xB956,
- 0xB957, 0x4A5A, 0xB958, 0, 0, 0xB959, 0, 0x3864,
- 0xB95A, 0, 0, 0, 0, 0x5537, 0x5538, 0,
- 0, 0, 0, 0, 0x3E2B, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E5A8_x0213[] = {
- 0xB94C, 0x3052, 0, 0x3023, 0xB94D, 0, 0, 0,
- 0, 0x5532, 0, 0, 0xA558, 0xA559, 0xB950, 0,
- 0, 0x5530, 0xB951, 0x2F71, 0, 0, 0, 0xA55A,
- 0x4C3C, 0, 0x5533, 0, 0x5531, 0, 0xB953, 0x552F,
- 0x3F31, 0, 0, 0x2F72, 0xB955, 0x552E, 0, 0xA55B,
- 0xB957, 0x4A5A, 0xB958, 0, 0, 0xA55C, 0, 0x3864,
- 0xB95A, 0, 0, 0, 0, 0x5537, 0x5538, 0,
- 0, 0, 0, 0, 0x3E2B, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E5A9[] = {
- 0x5534, 0x4F2C, 0, 0, 0xB95B, 0xB95C, 0x474C, 0xB95D,
- 0xB95E, 0x5536, 0, 0, 0xB95F, 0, 0, 0,
- 0xB960, 0, 0, 0, 0, 0xB961, 0, 0,
- 0, 0, 0x3A27, 0, 0, 0, 0xB962, 0,
- 0, 0, 0x5539, 0xB963, 0, 0xB964, 0x4958, 0xB965,
- 0, 0, 0x553A, 0, 0x5535, 0xB966, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xB967,
- 0, 0, 0xB968, 0xB969, 0, 0, 0xB96A, 0x4C3B,
-};
-static const unsigned short utf8_to_euc_E5A9_x0213[] = {
- 0x5534, 0x4F2C, 0, 0, 0xB95B, 0xB95C, 0x474C, 0xB95D,
- 0xB95E, 0x5536, 0, 0, 0xB95F, 0, 0, 0,
- 0xB960, 0, 0, 0, 0, 0xA55D, 0, 0,
- 0, 0, 0x3A27, 0, 0, 0, 0xB962, 0,
- 0, 0, 0x5539, 0xB963, 0, 0xA55E, 0x4958, 0x2F73,
- 0, 0, 0x553A, 0, 0x5535, 0x2F74, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0x2F75,
- 0, 0, 0xA55F, 0xB969, 0, 0, 0x2F76, 0x4C3B,
-};
-static const unsigned short utf8_to_euc_E5AA[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xB96B, 0, 0, 0, 0,
- 0xB96C, 0, 0x475E, 0xB96D, 0, 0, 0xB96E, 0,
- 0, 0xB96F, 0x553B, 0x4932, 0xB970, 0, 0xB971, 0xB972,
- 0xB973, 0, 0xB974, 0, 0, 0, 0, 0xB975,
- 0, 0, 0, 0, 0xB976, 0, 0, 0,
- 0, 0xB977, 0xB978, 0xB979, 0, 0xB97A, 0, 0,
- 0xB97B, 0, 0xB97C, 0xB97D, 0x553C, 0x5540, 0x553D, 0xB97E,
-};
-static const unsigned short utf8_to_euc_E5AA_x0213[] = {
- 0, 0, 0, 0, 0x2F77, 0, 0, 0,
- 0, 0, 0, 0xA560, 0, 0, 0, 0,
- 0xB96C, 0, 0x475E, 0xB96D, 0, 0, 0xB96E, 0,
- 0, 0xB96F, 0x553B, 0x4932, 0xA561, 0, 0x2F78, 0xA562,
- 0xA563, 0, 0xA564, 0, 0, 0, 0, 0x2F79,
- 0, 0, 0, 0, 0xB976, 0, 0, 0,
- 0, 0xA565, 0xB978, 0xA566, 0, 0xA567, 0, 0,
- 0xB97B, 0, 0xA568, 0xB97D, 0x553C, 0x5540, 0x553D, 0xA569,
-};
-static const unsigned short utf8_to_euc_E5AB[] = {
- 0, 0x3247, 0x553F, 0, 0xBA21, 0, 0xBA22, 0,
- 0xBA23, 0x3C3B, 0, 0x553E, 0x3779, 0, 0, 0xBA24,
- 0x554C, 0, 0, 0, 0, 0, 0x5545, 0x5542,
- 0, 0, 0xBA25, 0, 0xBA26, 0, 0, 0,
- 0xBA27, 0x4364, 0, 0x5541, 0, 0xBA28, 0x5543, 0,
- 0, 0x5544, 0xBA29, 0, 0, 0, 0xBA2A, 0,
- 0, 0, 0, 0, 0, 0xBA2B, 0xBA2C, 0,
- 0, 0, 0x5546, 0x5547, 0, 0xBA2D, 0, 0,
-};
-static const unsigned short utf8_to_euc_E5AB_x0213[] = {
- 0, 0x3247, 0x553F, 0, 0x2F7A, 0, 0xBA22, 0,
- 0xBA23, 0x3C3B, 0, 0x553E, 0x3779, 0, 0, 0xBA24,
- 0x554C, 0, 0, 0, 0, 0, 0x5545, 0x5542,
- 0, 0, 0xA56A, 0, 0xA56B, 0, 0, 0,
- 0xA56C, 0x4364, 0, 0x5541, 0, 0xA56D, 0x5543, 0,
- 0, 0x5544, 0xBA29, 0, 0, 0, 0xA56F, 0,
- 0xA56E, 0, 0, 0, 0, 0xA570, 0xBA2C, 0,
- 0, 0, 0x5546, 0x5547, 0, 0xBA2D, 0, 0,
-};
-static const unsigned short utf8_to_euc_E5AC[] = {
- 0xBA2E, 0xBA2F, 0, 0, 0, 0, 0, 0,
- 0xBA30, 0x3472, 0, 0x5549, 0x5548, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0x554A, 0xBA31,
- 0, 0xBA33, 0, 0xBA34, 0, 0xBA35, 0, 0,
- 0, 0xBA36, 0x3E6E, 0, 0, 0xBA37, 0, 0,
- 0, 0, 0x554D, 0, 0x445C, 0xBA38, 0, 0,
- 0x3145, 0, 0x554B, 0, 0xBA32, 0, 0x554E, 0,
- 0xBA39, 0, 0, 0, 0, 0, 0x554F, 0,
-};
-static const unsigned short utf8_to_euc_E5AC_x0213[] = {
- 0xA571, 0xBA2F, 0, 0, 0, 0, 0, 0,
- 0xA572, 0x3472, 0, 0x5549, 0x5548, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0x554A, 0xA573,
- 0, 0x2F7C, 0, 0xBA34, 0, 0xBA35, 0, 0,
- 0, 0xBA36, 0x3E6E, 0, 0, 0x2F7D, 0, 0,
- 0, 0, 0x554D, 0, 0x445C, 0xA575, 0, 0,
- 0x3145, 0, 0x554B, 0, 0xA574, 0, 0x554E, 0,
- 0xBA39, 0, 0, 0, 0, 0, 0x554F, 0,
-};
-static const unsigned short utf8_to_euc_E5AD[] = {
- 0x5552, 0xBA3A, 0, 0x5550, 0, 0x5551, 0, 0,
- 0, 0, 0, 0xBA3B, 0xBA3C, 0, 0, 0,
- 0x3B52, 0x5553, 0xBA3D, 0, 0x3926, 0x5554, 0xBA3E, 0x3B7A,
- 0x4238, 0, 0x5555, 0x5556, 0x3B5A, 0x3927, 0xBA3F, 0x4C52,
- 0, 0, 0, 0x3528, 0x3849, 0x5557, 0x3358, 0,
- 0xBA40, 0x5558, 0, 0x4239, 0, 0, 0xBA41, 0xBA42,
- 0x5559, 0x5623, 0, 0x555A, 0, 0x555B, 0, 0,
- 0x555C, 0, 0x555E, 0, 0xBA43, 0xBA44, 0xBA45, 0xBA46,
-};
-static const unsigned short utf8_to_euc_E5AD_x0213[] = {
- 0x5552, 0x4F55, 0, 0x5550, 0, 0x5551, 0, 0,
- 0, 0, 0, 0xBA3B, 0xA576, 0, 0, 0,
- 0x3B52, 0x5553, 0xA577, 0, 0x3926, 0x5554, 0x4F56, 0x3B7A,
- 0x4238, 0, 0x5555, 0x5556, 0x3B5A, 0x3927, 0xBA3F, 0x4C52,
- 0, 0, 0, 0x3528, 0x3849, 0x5557, 0x3358, 0,
- 0xA578, 0x5558, 0, 0x4239, 0, 0, 0xBA41, 0xA579,
- 0x5559, 0x5623, 0, 0x555A, 0, 0x555B, 0, 0,
- 0x555C, 0, 0x555E, 0, 0xA57A, 0x4F57, 0xBA45, 0xA57B,
-};
-static const unsigned short utf8_to_euc_E5AE[] = {
- 0x555F, 0xBA47, 0, 0x5560, 0xBA48, 0x4270, 0xBA49, 0x3127,
- 0x3C69, 0x3042, 0xBA4A, 0x4157, 0x3430, 0x3C35, 0xBA4B, 0x3928,
- 0xBA4C, 0xBA4D, 0, 0xBA4E, 0xBA4F, 0x4566, 0xBA50, 0x3D21,
- 0x3431, 0x4368, 0x446A, 0x3038, 0x3539, 0x4A75, 0, 0x3C42,
- 0, 0, 0x3552, 0x406B, 0x3C3C, 0x4D28, 0x5561, 0,
- 0xBA51, 0xBA52, 0, 0, 0xBA53, 0xBA54, 0x355C, 0xBA55,
- 0x3A4B, 0xBA56, 0xBA57, 0x3332, 0x3163, 0x3E2C, 0x3248, 0xBA58,
- 0x5562, 0x4D46, 0xBA59, 0, 0xBA5A, 0, 0, 0x3D49,
-};
-static const unsigned short utf8_to_euc_E5AE_x0213[] = {
- 0x555F, 0xA57C, 0, 0x5560, 0xA57D, 0x4270, 0xBA49, 0x3127,
- 0x3C69, 0x3042, 0xBA4A, 0x4157, 0x3430, 0x3C35, 0xBA4B, 0x3928,
- 0xBA4C, 0xBA4D, 0, 0x4F58, 0xBA4F, 0x4566, 0xA821, 0x3D21,
- 0x3431, 0x4368, 0x446A, 0x3038, 0x3539, 0x4A75, 0, 0x3C42,
- 0, 0, 0x3552, 0x406B, 0x3C3C, 0x4D28, 0x5561, 0,
- 0xBA51, 0xBA52, 0, 0, 0xA822, 0xBA54, 0x355C, 0xBA55,
- 0x3A4B, 0xBA56, 0xBA57, 0x3332, 0x3163, 0x3E2C, 0x3248, 0xBA58,
- 0x5562, 0x4D46, 0xBA59, 0, 0xBA5A, 0, 0, 0x3D49,
-};
-static const unsigned short utf8_to_euc_E5AF[] = {
- 0xBA5B, 0xBA5C, 0x3C64, 0x5563, 0x3473, 0x4652, 0x4C29, 0x5564,
- 0, 0x5565, 0, 0, 0x4959, 0xBA5D, 0, 0xBA5E,
- 0x5567, 0, 0x3428, 0x3677, 0x5566, 0, 0xBA5F, 0xBA60,
- 0xBA61, 0xBA62, 0xBA63, 0x3432, 0, 0x3F32, 0x556B, 0x3B21,
- 0xBA64, 0x3249, 0x556A, 0, 0x5568, 0x556C, 0x5569, 0x472B,
- 0x5C4D, 0x3F33, 0, 0x556D, 0xF43A, 0, 0x4E40, 0xBA65,
- 0x556E, 0xBA66, 0, 0x5570, 0xBA67, 0x437E, 0x556F, 0,
- 0x4023, 0, 0x3B7B, 0, 0, 0xBA68, 0x4250, 0x3C77,
-};
-static const unsigned short utf8_to_euc_E5AF_x0213[] = {
- 0xA824, 0xBA5C, 0x3C64, 0x5563, 0x3473, 0x4652, 0x4C29, 0x5564,
- 0, 0x5565, 0, 0, 0x4959, 0xBA5D, 0xA826, 0xBA5E,
- 0x5567, 0, 0x3428, 0x3677, 0x5566, 0, 0xA827, 0xBA60,
- 0x4F59, 0xBA62, 0xBA63, 0x3432, 0, 0x3F32, 0x556B, 0x3B21,
- 0xBA64, 0x3249, 0x556A, 0, 0x5568, 0x556C, 0x5569, 0x472B,
- 0x5C4D, 0x3F33, 0, 0x556D, 0x4F5A, 0, 0x4E40, 0xBA65,
- 0x556E, 0xA82A, 0, 0x5570, 0xBA67, 0x437E, 0x556F, 0,
- 0x4023, 0, 0x3B7B, 0, 0, 0xA82B, 0x4250, 0x3C77,
-};
-static const unsigned short utf8_to_euc_E5B0[] = {
- 0, 0x4975, 0x406C, 0, 0x3C4D, 0x5571, 0x3E2D, 0x5572,
- 0x5573, 0x3053, 0x423A, 0x3F52, 0xBA69, 0x5574, 0x4633, 0x3E2E,
- 0, 0x3E2F, 0, 0x5575, 0, 0, 0x406D, 0xBA6A,
- 0, 0, 0x3E30, 0, 0, 0, 0xBA6B, 0xBA6C,
- 0x5576, 0, 0x5577, 0xBA6D, 0x4C60, 0, 0xBA6E, 0,
- 0x5578, 0xBA6F, 0, 0xBA70, 0xBA71, 0x3646, 0xBA72, 0,
- 0xBA73, 0x3D22, 0xBA74, 0, 0, 0xBA75, 0xBA76, 0,
- 0x5579, 0x557A, 0x3C5C, 0x3F2C, 0x4674, 0x3F54, 0x4878, 0x4722,
-};
-static const unsigned short utf8_to_euc_E5B0_x0213[] = {
- 0, 0x4975, 0x406C, 0xA82D, 0x3C4D, 0x5571, 0x3E2D, 0x5572,
- 0x5573, 0x3053, 0x423A, 0x3F52, 0xBA69, 0x5574, 0x4633, 0x3E2E,
- 0, 0x3E2F, 0x4F5B, 0x5575, 0, 0, 0x406D, 0xBA6A,
- 0, 0, 0x3E30, 0, 0, 0, 0x4F5C, 0xBA6C,
- 0x5576, 0, 0x5577, 0x4F5D, 0x4C60, 0, 0xBA6E, 0,
- 0x5578, 0xA82E, 0, 0x4F5E, 0xBA71, 0x3646, 0xBA72, 0,
- 0xA82F, 0x3D22, 0xBA74, 0, 0, 0xBA75, 0xBA76, 0,
- 0x5579, 0x557A, 0x3C5C, 0x3F2C, 0x4674, 0x3F54, 0x4878, 0x4722,
-};
-static const unsigned short utf8_to_euc_E5B1[] = {
- 0x3649, 0x557B, 0, 0, 0, 0x356F, 0x557C, 0,
- 0x367E, 0, 0x464F, 0x3230, 0, 0x3B53, 0x557D, 0x5622,
- 0x5621, 0x367D, 0, 0x557E, 0, 0x4538, 0, 0,
- 0, 0xBA77, 0xBA78, 0, 0xBA79, 0, 0x4230, 0,
- 0x454B, 0x3C48, 0xBA7A, 0xBA7B, 0x4158, 0x4D7A, 0, 0xBA7C,
- 0xBA7D, 0xBA7E, 0, 0, 0x5624, 0xBB21, 0x5625, 0x4656,
- 0xBB22, 0x3B33, 0, 0, 0xBB23, 0xBB24, 0x5627, 0,
- 0, 0x5628, 0xBB25, 0xBB26, 0xBB27, 0xBB28, 0, 0,
-};
-static const unsigned short utf8_to_euc_E5B1_x0213[] = {
- 0x3649, 0x557B, 0, 0, 0, 0x356F, 0x557C, 0,
- 0x367E, 0, 0x464F, 0x3230, 0, 0x3B53, 0x557D, 0x5622,
- 0x5621, 0x367D, 0, 0x557E, 0, 0x4538, 0, 0,
- 0, 0xBA77, 0xBA78, 0x7E7B, 0xBA79, 0, 0x4230, 0xA831,
- 0x454B, 0x3C48, 0x4F60, 0xA832, 0x4158, 0x4D7A, 0, 0xA833,
- 0xA834, 0xA835, 0, 0, 0x5624, 0xBB21, 0x5625, 0x4656,
- 0xA836, 0x3B33, 0, 0, 0xBB23, 0xBB24, 0x5627, 0,
- 0, 0x5628, 0x4F64, 0xBB26, 0xA839, 0xBB28, 0, 0,
-};
-static const unsigned short utf8_to_euc_E5B2[] = {
- 0, 0, 0, 0, 0, 0, 0, 0xBB29,
- 0xBB2A, 0, 0xBB2B, 0, 0x5629, 0, 0, 0xBB2C,
- 0x3474, 0x562A, 0xBB2D, 0, 0x562B, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xBB2E, 0, 0xBB2F,
- 0xBB30, 0x322C, 0xBB31, 0xBB32, 0, 0, 0xBB33, 0,
- 0x413B, 0x3464, 0xBB34, 0x562D, 0x4C28, 0, 0, 0,
- 0, 0x4252, 0xBB35, 0x3359, 0xBB36, 0xBB37, 0x562F, 0x5631,
- 0x345F, 0, 0xBB38, 0x562E, 0x5630, 0, 0x5633, 0,
-};
-static const unsigned short utf8_to_euc_E5B2_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0xBB29,
- 0xA83C, 0, 0xA83D, 0, 0x5629, 0, 0, 0x4F65,
- 0x3474, 0x562A, 0xBB2D, 0, 0x562B, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xBB2E, 0, 0x4F66,
- 0xA841, 0x322C, 0xA842, 0x4F67, 0, 0, 0xA843, 0xA844,
- 0x413B, 0x3464, 0x4F68, 0x562D, 0x4C28, 0xA846, 0, 0,
- 0, 0x4252, 0xBB35, 0x3359, 0xBB36, 0xA847, 0x562F, 0x5631,
- 0x345F, 0, 0x4F69, 0x562E, 0x5630, 0, 0x5633, 0,
-};
-static const unsigned short utf8_to_euc_E5B3[] = {
- 0, 0, 0, 0, 0, 0x5632, 0, 0x5634,
- 0, 0xBB39, 0, 0xBB3A, 0, 0, 0, 0,
- 0, 0, 0xBB3B, 0, 0, 0, 0, 0xBB3D,
- 0, 0x5635, 0, 0, 0, 0xBB3C, 0, 0,
- 0x463D, 0x362E, 0, 0, 0, 0, 0, 0,
- 0x3265, 0x5636, 0x563B, 0, 0, 0x5639, 0xBB3E, 0x4A77,
- 0x4A76, 0xBB3F, 0xBB40, 0, 0xBB41, 0xF43B, 0x4567, 0,
- 0, 0, 0x5638, 0x3D54, 0, 0x5637, 0, 0,
-};
-static const unsigned short utf8_to_euc_E5B3_x0213[] = {
- 0, 0, 0, 0, 0, 0x5632, 0, 0x5634,
- 0, 0xA849, 0, 0x4F6A, 0, 0, 0, 0,
- 0x4F6B, 0, 0x4F6C, 0, 0, 0, 0, 0xBB3D,
- 0, 0x5635, 0, 0, 0, 0xBB3C, 0, 0,
- 0x463D, 0x362E, 0, 0, 0, 0, 0, 0,
- 0x3265, 0x5636, 0x563B, 0, 0, 0x5639, 0xBB3E, 0x4A77,
- 0x4A76, 0xBB3F, 0xBB40, 0, 0x4F6D, 0, 0x4567, 0,
- 0, 0, 0x5638, 0x3D54, 0, 0x5637, 0, 0,
-};
-static const unsigned short utf8_to_euc_E5B4[] = {
- 0, 0xBB42, 0, 0, 0, 0, 0xBB43, 0x3F72,
- 0, 0, 0, 0x563C, 0, 0xBB44, 0x3A6A, 0,
- 0, 0x5642, 0xBB45, 0, 0x5643, 0x563D, 0x3333, 0x563E,
- 0x5647, 0x5646, 0x5645, 0x5641, 0, 0, 0, 0x5640,
- 0, 0, 0x5644, 0xBB47, 0xBB48, 0, 0xBB49, 0xBB4A,
- 0, 0x4A78, 0, 0xBB46, 0, 0, 0, 0,
- 0, 0xBB4B, 0, 0, 0xBB4C, 0, 0, 0,
- 0, 0xBB4D, 0, 0, 0, 0xBB4E, 0, 0xBB4F,
-};
-static const unsigned short utf8_to_euc_E5B4_x0213[] = {
- 0, 0xBB42, 0, 0, 0, 0, 0xA84C, 0x3F72,
- 0, 0, 0, 0x563C, 0, 0x4F70, 0x3A6A, 0,
- 0xA84D, 0x5642, 0xBB45, 0, 0x5643, 0x563D, 0x3333, 0x563E,
- 0x5647, 0x5646, 0x5645, 0x5641, 0, 0xA84F, 0, 0x5640,
- 0xA850, 0, 0x5644, 0xBB47, 0xA851, 0, 0xA852, 0x4F71,
- 0, 0x4A78, 0, 0xA84E, 0, 0, 0, 0,
- 0, 0xA853, 0, 0, 0xBB4C, 0, 0, 0,
- 0, 0xA854, 0, 0, 0, 0xBB4E, 0, 0xBB4F,
-};
-static const unsigned short utf8_to_euc_E5B5[] = {
- 0, 0, 0xBB50, 0xBB51, 0, 0, 0xBB52, 0,
- 0xBB53, 0, 0xBB57, 0x564B, 0x5648, 0, 0x564A, 0,
- 0x4D72, 0xBB55, 0x5649, 0xF43C, 0, 0xBB54, 0, 0,
- 0, 0xBB56, 0, 0, 0x563F, 0, 0, 0xBB58,
- 0xBB59, 0xBB5A, 0xBB5B, 0, 0xBB5C, 0, 0, 0,
- 0, 0x3F73, 0xBB5D, 0, 0x564C, 0xBB5E, 0, 0x3A37,
- 0xBB5F, 0, 0, 0x564D, 0, 0, 0x564E, 0,
- 0, 0xBB60, 0xBB61, 0, 0, 0, 0xBB62, 0xBB63,
-};
-static const unsigned short utf8_to_euc_E5B5_x0213[] = {
- 0, 0, 0xA855, 0xBB51, 0, 0, 0x4F73, 0x4F74,
- 0xBB53, 0, 0x4F76, 0x564B, 0x5648, 0, 0x564A, 0,
- 0x4D72, 0xBB55, 0x5649, 0x4F75, 0, 0xBB54, 0, 0,
- 0, 0xBB56, 0, 0, 0x563F, 0, 0, 0xBB58,
- 0xBB59, 0xA857, 0xBB5B, 0, 0xBB5C, 0, 0, 0,
- 0, 0x3F73, 0xA858, 0, 0x564C, 0x4F77, 0, 0x3A37,
- 0xA85A, 0, 0, 0x564D, 0, 0, 0x564E, 0,
- 0, 0xBB60, 0xBB61, 0, 0, 0, 0xBB62, 0xBB63,
-};
-static const unsigned short utf8_to_euc_E5B6[] = {
- 0, 0xBB64, 0x5651, 0xBB65, 0x5650, 0, 0, 0x564F,
- 0xBB66, 0, 0xBB67, 0x4568, 0x563A, 0, 0, 0,
- 0x5657, 0, 0xBB68, 0xBB69, 0xBB6A, 0xBB6B, 0, 0,
- 0, 0xBB6C, 0, 0xBB6D, 0, 0x5653, 0, 0xBB6E,
- 0xBB6F, 0, 0x5652, 0, 0, 0, 0, 0xBB70,
- 0, 0, 0, 0xBB71, 0x5654, 0, 0x5655, 0,
- 0xBB72, 0, 0xE674, 0, 0xBB73, 0, 0, 0x5658,
- 0xBB74, 0xBB75, 0x4E66, 0, 0x5659, 0x5656, 0, 0,
-};
-static const unsigned short utf8_to_euc_E5B6_x0213[] = {
- 0, 0x4F78, 0x5651, 0xBB65, 0x5650, 0, 0, 0x564F,
- 0xA85D, 0, 0xBB67, 0x4568, 0x563A, 0, 0, 0,
- 0x5657, 0, 0xA85F, 0xBB69, 0xA860, 0xBB6B, 0, 0xA861,
- 0, 0xA862, 0, 0xBB6D, 0, 0x5653, 0, 0xBB6E,
- 0x4F79, 0, 0x5652, 0, 0x4F7A, 0, 0, 0x4F7B,
- 0, 0, 0, 0xBB71, 0x5654, 0, 0x5655, 0,
- 0xA863, 0, 0xA864, 0, 0xA865, 0, 0, 0x5658,
- 0x4F7C, 0xA867, 0x4E66, 0, 0x5659, 0x5656, 0, 0,
-};
-static const unsigned short utf8_to_euc_E5B7[] = {
- 0, 0, 0, 0xBB76, 0, 0, 0, 0xBB77,
- 0, 0x565A, 0, 0xBB78, 0x3460, 0x565B, 0xBB7A, 0,
- 0xBB79, 0, 0x565D, 0x565C, 0, 0, 0x565E, 0,
- 0xBB7B, 0xBB7C, 0, 0x565F, 0, 0x406E, 0x3D23, 0,
- 0xBB7D, 0x3D64, 0, 0x4163, 0xBB7E, 0x3929, 0x3A38, 0x392A,
- 0x3570, 0xBC21, 0, 0x5660, 0, 0, 0x3A39, 0,
- 0, 0x384A, 0x5661, 0x4C26, 0x4743, 0x5662, 0, 0x392B,
- 0xBC22, 0xBC23, 0, 0x342C, 0, 0x4327, 0x3652, 0,
-};
-static const unsigned short utf8_to_euc_E5B7_x0213[] = {
- 0, 0, 0, 0xBB76, 0, 0, 0, 0xBB77,
- 0, 0x565A, 0, 0x4F7D, 0x3460, 0x565B, 0xBB7A, 0,
- 0, 0xA868, 0x565D, 0x565C, 0, 0, 0x565E, 0xA869,
- 0xA86A, 0xBB7C, 0, 0x565F, 0, 0x406E, 0x3D23, 0,
- 0xA86B, 0x3D64, 0x7428, 0x4163, 0xA86D, 0x3929, 0x3A38, 0x392A,
- 0x3570, 0xA86E, 0, 0x5660, 0, 0, 0x3A39, 0,
- 0, 0x384A, 0x5661, 0x4C26, 0x4743, 0x5662, 0, 0x392B,
- 0xBC22, 0xBC23, 0, 0x342C, 0, 0x4327, 0x3652, 0,
-};
-static const unsigned short utf8_to_euc_E5B8[] = {
- 0xBC24, 0, 0x3B54, 0x495B, 0, 0, 0x4841, 0xBC25,
- 0, 0, 0, 0x5663, 0x3475, 0xBC26, 0, 0,
- 0, 0x5666, 0xBC27, 0, 0xBC28, 0xBC29, 0x4421, 0,
- 0xBC2A, 0x5665, 0x5664, 0x5667, 0, 0x446B, 0, 0xBC2B,
- 0xBC2C, 0, 0, 0, 0, 0x3F63, 0, 0,
- 0xBC2E, 0, 0, 0x3B55, 0, 0x404A, 0xBC2D, 0x4253,
- 0x3522, 0, 0xBC2F, 0x4422, 0, 0xBC30, 0x5668, 0x5669,
- 0x3E6F, 0, 0, 0, 0, 0x4B39, 0xBC31, 0,
-};
-static const unsigned short utf8_to_euc_E5B8_x0213[] = {
- 0xA870, 0, 0x3B54, 0x495B, 0, 0, 0x4841, 0xBC25,
- 0, 0, 0, 0x5663, 0x3475, 0xBC26, 0, 0,
- 0, 0x5666, 0xA872, 0, 0x7429, 0xA873, 0x4421, 0,
- 0x742A, 0x5665, 0x5664, 0x5667, 0, 0x446B, 0, 0xA875,
- 0xBC2C, 0, 0, 0, 0, 0x3F63, 0, 0,
- 0xBC2E, 0, 0, 0x3B55, 0, 0x404A, 0xA876, 0x4253,
- 0x3522, 0, 0xBC2F, 0x4422, 0, 0xBC30, 0x5668, 0x5669,
- 0x3E6F, 0, 0, 0, 0, 0x4B39, 0xA877, 0,
-};
-static const unsigned short utf8_to_euc_E5B9[] = {
- 0x566C, 0, 0, 0x566B, 0x566A, 0x497D, 0, 0x5673,
- 0, 0xBC34, 0, 0xBC32, 0x4B5A, 0, 0x566D, 0,
- 0xBC33, 0xBC35, 0, 0, 0x566F, 0x4B6B, 0xBC36, 0x566E,
- 0xBC37, 0, 0, 0xBC38, 0xBC39, 0, 0xBC3A, 0x5670,
- 0, 0x4828, 0x5671, 0x4A3E, 0x5672, 0, 0, 0,
- 0xBC3B, 0, 0xBC3C, 0xBC3D, 0xBC3E, 0xBC3F, 0xBC40, 0,
- 0xBC41, 0, 0x3433, 0x4A3F, 0x472F, 0x5674, 0x5675, 0,
- 0x392C, 0x3434, 0x5676, 0x3838, 0x4D44, 0x4D29, 0x3476, 0x5678,
-};
-static const unsigned short utf8_to_euc_E5B9_x0213[] = {
- 0x566C, 0, 0, 0x566B, 0x566A, 0x497D, 0, 0x5673,
- 0, 0xA878, 0, 0xBC32, 0x4B5A, 0, 0x566D, 0,
- 0xBC33, 0xBC35, 0, 0, 0x566F, 0x4B6B, 0xA87A, 0x566E,
- 0x742B, 0, 0, 0xBC38, 0xBC39, 0, 0x742C, 0x5670,
- 0, 0x4828, 0x5671, 0x4A3E, 0x5672, 0, 0, 0,
- 0xBC3B, 0, 0xBC3C, 0xA87C, 0xA87D, 0xA87E, 0xAC21, 0,
- 0xBC41, 0, 0x3433, 0x4A3F, 0x472F, 0x5674, 0x5675, 0x7E7C,
- 0x392C, 0x3434, 0x5676, 0x3838, 0x4D44, 0x4D29, 0x3476, 0x5678,
-};
-static const unsigned short utf8_to_euc_E5BA[] = {
- 0xBC42, 0x4423, 0, 0x392D, 0x3E31, 0, 0, 0x485F,
- 0, 0, 0x3E32, 0xBC43, 0, 0, 0xBC44, 0x3D78,
- 0, 0, 0, 0, 0, 0x446C, 0x4A79, 0x4539,
- 0, 0, 0x392E, 0, 0x495C, 0, 0, 0,
- 0x5679, 0, 0xBC45, 0, 0xBC46, 0xBC47, 0x4559, 0x3A42,
- 0xBC48, 0, 0xBC49, 0x384B, 0xBC4A, 0x446D, 0, 0,
- 0, 0xBC4B, 0, 0xBC4C, 0, 0x3043, 0x3D6E, 0x392F,
- 0x4D47, 0, 0, 0, 0, 0xBC4D, 0xBC4E, 0xBC4F,
-};
-static const unsigned short utf8_to_euc_E5BA_x0213[] = {
- 0xBC42, 0x4423, 0, 0x392D, 0x3E31, 0, 0, 0x485F,
- 0, 0, 0x3E32, 0xBC43, 0, 0, 0xBC44, 0x3D78,
- 0, 0, 0, 0, 0, 0x446C, 0x4A79, 0x4539,
- 0, 0, 0x392E, 0, 0x495C, 0, 0, 0,
- 0x5679, 0, 0xBC45, 0, 0xBC46, 0xAC23, 0x4559, 0x3A42,
- 0xBC48, 0, 0xAC24, 0x384B, 0xAC25, 0x446D, 0, 0,
- 0, 0xBC4B, 0, 0xBC4C, 0, 0x3043, 0x3D6E, 0x392F,
- 0x4D47, 0xAC26, 0, 0, 0, 0xBC4D, 0x742D, 0xAC27,
-};
-static const unsigned short utf8_to_euc_E5BB[] = {
- 0, 0x567A, 0x567B, 0x4751, 0, 0, 0xBC50, 0,
- 0x567C, 0x4E77, 0x4F2D, 0xBC52, 0xBC51, 0, 0xBC53, 0x567E,
- 0x567D, 0xBC54, 0xBC55, 0x3347, 0xBC56, 0xBC57, 0x5721, 0,
- 0, 0, 0x5724, 0x5725, 0xBC58, 0x5723, 0xBC59, 0x4940,
- 0x3E33, 0x5727, 0x5726, 0x5722, 0, 0xBC5A, 0, 0,
- 0x5728, 0x5729, 0, 0xBC5B, 0x572A, 0, 0, 0,
- 0x572D, 0x572B, 0, 0x572C, 0x572E, 0, 0x3164, 0x446E,
- 0x572F, 0, 0x377A, 0x3276, 0x4736, 0, 0x5730, 0x467B,
-};
-static const unsigned short utf8_to_euc_E5BB_x0213[] = {
- 0, 0x567A, 0x567B, 0x4751, 0, 0, 0xAC28, 0,
- 0x567C, 0x4E77, 0x4F2D, 0x742F, 0xBC51, 0, 0xBC53, 0x567E,
- 0x567D, 0xBC54, 0xAC29, 0x3347, 0xBC56, 0xBC57, 0x5721, 0,
- 0, 0xAC2A, 0x5724, 0x5725, 0xBC58, 0x5723, 0xBC59, 0x4940,
- 0x3E33, 0x5727, 0x5726, 0x5722, 0, 0xBC5A, 0, 0,
- 0x5728, 0x5729, 0, 0xBC5B, 0x572A, 0, 0, 0,
- 0x572D, 0x572B, 0, 0x572C, 0x572E, 0, 0x3164, 0x446E,
- 0x572F, 0x7430, 0x377A, 0x3276, 0x4736, 0xAC2C, 0x5730, 0x467B,
-};
-static const unsigned short utf8_to_euc_E5BC[] = {
- 0, 0x4A5B, 0xBC5C, 0x5731, 0x4F2E, 0, 0xBC5D, 0xBC5E,
- 0xBC5F, 0x5732, 0x4A40, 0x5735, 0x5021, 0x5031, 0xBC60, 0x3C30,
- 0x4675, 0x5736, 0, 0x355D, 0x4424, 0x307A, 0x5737, 0x4A26,
- 0x3930, 0xBC61, 0, 0x4350, 0xBC62, 0xBC63, 0, 0x446F,
- 0, 0xBC64, 0xBC65, 0xBC66, 0xBC67, 0x4C6F, 0x3839, 0x384C,
- 0xBC68, 0x5738, 0, 0xBC69, 0xBC6A, 0x5739, 0xBC6B, 0x573F,
- 0xBC6C, 0x3C65, 0, 0, 0xBC6D, 0x4425, 0xBC6E, 0x362F,
- 0x573A, 0, 0, 0xBC6F, 0x492B, 0xBC70, 0x4346, 0xBC71,
-};
-static const unsigned short utf8_to_euc_E5BC_x0213[] = {
- 0x7431, 0x4A5B, 0x7432, 0x5731, 0x4F2E, 0, 0xBC5D, 0x7433,
- 0xAC2D, 0x5732, 0x4A40, 0x5735, 0x5021, 0x5031, 0xAC2E, 0x3C30,
- 0x4675, 0x5736, 0, 0x355D, 0x4424, 0x307A, 0x5737, 0x4A26,
- 0x3930, 0xBC61, 0, 0x4350, 0xAC2F, 0x7434, 0xAC31, 0x446F,
- 0, 0, 0xBC65, 0x7435, 0xBC67, 0x4C6F, 0x3839, 0x384C,
- 0xBC68, 0x5738, 0, 0xBC69, 0xBC6A, 0x5739, 0xBC6B, 0x573F,
- 0xBC6C, 0x3C65, 0, 0, 0x7436, 0x4425, 0x7437, 0x362F,
- 0x573A, 0, 0, 0xBC6F, 0x492B, 0x7438, 0x4346, 0xBC71,
-};
-static const unsigned short utf8_to_euc_E5BD[] = {
- 0xBC72, 0x573B, 0, 0, 0xBC73, 0xBC74, 0, 0xBC75,
- 0x573C, 0, 0x3630, 0, 0x573D, 0xBC76, 0x573E, 0,
- 0xBC77, 0x5740, 0, 0x4576, 0xBC78, 0, 0x5741, 0x5742,
- 0xBC79, 0x5743, 0, 0xBC7A, 0x5734, 0x5733, 0, 0,
- 0xBC7B, 0x5744, 0x3741, 0xBC7C, 0xBC7D, 0, 0x4927, 0xBC7E,
- 0, 0x3A4C, 0x4937, 0x4426, 0x494B, 0x5745, 0, 0xBD21,
- 0x3E34, 0x3146, 0xBD22, 0x5746, 0xBD23, 0xBD24, 0, 0x5747,
- 0xBD25, 0x4C72, 0xBD26, 0, 0x4860, 0xBD27, 0xBD28, 0x574A,
-};
-static const unsigned short utf8_to_euc_E5BD_x0213[] = {
- 0x7439, 0x573B, 0, 0, 0xBC73, 0x743A, 0, 0xAC32,
- 0x573C, 0, 0x3630, 0, 0x573D, 0xBC76, 0x573E, 0,
- 0xBC77, 0x5740, 0, 0x4576, 0x743B, 0, 0x5741, 0x5742,
- 0x743C, 0x5743, 0, 0xBC7A, 0x5734, 0x5733, 0, 0,
- 0xBC7B, 0x5744, 0x3741, 0xAC33, 0x743D, 0, 0x4927, 0x743E,
- 0, 0x3A4C, 0x4937, 0x4426, 0x494B, 0x5745, 0, 0xBD21,
- 0x3E34, 0x3146, 0xAC34, 0x5746, 0xBD23, 0xBD24, 0, 0x5747,
- 0xBD25, 0x4C72, 0xBD26, 0, 0x4860, 0x743F, 0xAC35, 0x574A,
-};
-static const unsigned short utf8_to_euc_E5BE[] = {
- 0x317D, 0x402C, 0x5749, 0x5748, 0x3742, 0x4254, 0, 0x574E,
- 0x574C, 0xBD29, 0x574B, 0x4E27, 0x3865, 0xBD2A, 0, 0xBD2B,
- 0x3D79, 0x574D, 0x454C, 0x3D3E, 0, 0, 0xBD2C, 0x4640,
- 0x5751, 0x5750, 0, 0, 0xBD2D, 0xBD2E, 0x574F, 0,
- 0x5752, 0x3866, 0xBD2F, 0, 0xBD32, 0, 0, 0xBD30,
- 0x5753, 0x497C, 0x3D5B, 0xBD31, 0xBD33, 0x5754, 0x4879, 0xBD34,
- 0xBD35, 0xBD36, 0, 0x4641, 0x4427, 0, 0, 0xF43E,
- 0xBD37, 0x4530, 0, 0, 0x5755, 0x352B, 0, 0,
-};
-static const unsigned short utf8_to_euc_E5BE_x0213[] = {
- 0x317D, 0x402C, 0x5749, 0x5748, 0x3742, 0x4254, 0, 0x574E,
- 0x574C, 0x7440, 0x574B, 0x4E27, 0x3865, 0xBD2A, 0, 0xAC36,
- 0x3D79, 0x574D, 0x454C, 0x3D3E, 0, 0, 0xBD2C, 0x4640,
- 0x5751, 0x5750, 0, 0, 0x7441, 0xBD2E, 0x574F, 0,
- 0x5752, 0x3866, 0xAC37, 0, 0xAC38, 0, 0, 0x7442,
- 0x5753, 0x497C, 0x3D5B, 0xBD31, 0xBD33, 0x5754, 0x4879, 0x7443,
- 0xBD35, 0xBD36, 0, 0x4641, 0x4427, 0x7444, 0, 0x7445,
- 0xAC39, 0x4530, 0, 0, 0x5755, 0x352B, 0, 0,
-};
-static const unsigned short utf8_to_euc_E5BF[] = {
- 0, 0, 0, 0x3F34, 0xBD38, 0x492C, 0, 0xBD39,
- 0xBD3A, 0xBD3B, 0, 0xBD3C, 0x3477, 0x4726, 0, 0,
- 0xBD3D, 0xBD3E, 0xBD3F, 0xBD40, 0xBD41, 0, 0x5756, 0x3B56,
- 0x4B3A, 0x4B3B, 0, 0, 0x317E, 0x575B, 0xBD42, 0,
- 0x4369, 0xBD43, 0xBD44, 0, 0x5758, 0, 0, 0,
- 0xBD45, 0xBD46, 0xBD47, 0x3277, 0xBD48, 0xBD49, 0xBD4A, 0xBD4B,
- 0x582D, 0x575A, 0xBD4C, 0xBD4D, 0, 0x4730, 0xBD4E, 0,
- 0x5759, 0, 0xBD4F, 0x5757, 0xBD50, 0x397A, 0, 0x575D,
-};
-static const unsigned short utf8_to_euc_E5BF_x0213[] = {
- 0, 0, 0, 0x3F34, 0xAC3A, 0x492C, 0, 0xAC3C,
- 0xBD3A, 0x7446, 0, 0xAC3D, 0x3477, 0x4726, 0, 0,
- 0xBD3D, 0xBD3E, 0xAC3E, 0xAC3F, 0xAC40, 0, 0x5756, 0x3B56,
- 0x4B3A, 0x4B3B, 0, 0, 0x317E, 0x575B, 0x7447, 0,
- 0x4369, 0x7448, 0xAC41, 0, 0x5758, 0, 0, 0,
- 0xBD45, 0x7449, 0xBD47, 0x3277, 0xBD48, 0xBD49, 0xAC42, 0xAC43,
- 0x582D, 0x575A, 0xBD4C, 0xAC44, 0, 0x4730, 0xBD4E, 0,
- 0x5759, 0, 0xBD4F, 0x5757, 0xAC45, 0x397A, 0, 0x575D,
-};
-static const unsigned short utf8_to_euc_E680[] = {
- 0, 0, 0, 0, 0, 0, 0, 0xBD51,
- 0, 0, 0xBD52, 0, 0, 0xBD53, 0x5763, 0x5769,
- 0x5761, 0, 0x455C, 0xBD54, 0xBD55, 0x5766, 0x495D, 0xBD56,
- 0xBD57, 0x5760, 0xBD58, 0x5765, 0x4E67, 0x3B57, 0, 0xBD59,
- 0x4255, 0x575E, 0, 0, 0xBD5A, 0x355E, 0x5768, 0x402D,
- 0x3165, 0x5762, 0x3278, 0x5767, 0, 0xBD5B, 0, 0x3631,
- 0, 0x5764, 0, 0xBD5C, 0, 0xBD5D, 0, 0,
- 0, 0, 0x576A, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E680_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0xBD51,
- 0, 0, 0xBD52, 0, 0, 0x744A, 0x5763, 0x5769,
- 0x5761, 0, 0x455C, 0xBD54, 0x744B, 0x5766, 0x495D, 0xAC47,
- 0x744C, 0x5760, 0xBD58, 0x5765, 0x4E67, 0x3B57, 0, 0xBD59,
- 0x4255, 0x575E, 0xAC48, 0, 0xAC49, 0x355E, 0x5768, 0x402D,
- 0x3165, 0x5762, 0x3278, 0x5767, 0, 0xBD5B, 0, 0x3631,
- 0, 0x5764, 0, 0x744D, 0, 0x744E, 0, 0,
- 0, 0, 0x576A, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E681[] = {
- 0xBD5E, 0x576C, 0x5776, 0x5774, 0, 0, 0x5771, 0xBD5F,
- 0xBD60, 0xBD61, 0x5770, 0x4E78, 0xBD62, 0x5772, 0, 0,
- 0x3632, 0xBD63, 0x3931, 0, 0xBD64, 0x3D7A, 0xBD65, 0xBD66,
- 0, 0x5779, 0x576B, 0, 0, 0xBD67, 0, 0x576F,
- 0x575F, 0xBD68, 0x327A, 0x5773, 0x5775, 0x4351, 0, 0xBD69,
- 0x3A28, 0x3238, 0x576D, 0x5778, 0x5777, 0x3633, 0, 0x4229,
- 0x3366, 0xBD6A, 0, 0, 0, 0x3743, 0, 0x576E,
- 0, 0, 0, 0, 0, 0, 0xBD6B, 0xBD6C,
-};
-static const unsigned short utf8_to_euc_E681_x0213[] = {
- 0xBD5E, 0x576C, 0x5776, 0x5774, 0, 0, 0x5771, 0x744F,
- 0xBD60, 0xBD61, 0x5770, 0x4E78, 0xAC4B, 0x5772, 0, 0,
- 0x3632, 0xBD63, 0x3931, 0, 0xBD64, 0x3D7A, 0xBD65, 0xBD66,
- 0, 0x5779, 0x576B, 0, 0, 0, 0, 0x576F,
- 0x575F, 0xBD68, 0x327A, 0x5773, 0x5775, 0x4351, 0, 0xBD69,
- 0x3A28, 0x3238, 0x576D, 0x5778, 0x5777, 0x3633, 0, 0x4229,
- 0x3366, 0xBD6A, 0, 0, 0, 0x3743, 0, 0x576E,
- 0, 0, 0, 0, 0, 0, 0xBD6B, 0xAC4C,
-};
-static const unsigned short utf8_to_euc_E682[] = {
- 0, 0x577A, 0xBD6D, 0x577D, 0x5821, 0xF43F, 0xBD6E, 0,
- 0xBD6F, 0x3C3D, 0xBD70, 0x5827, 0x4470, 0x577B, 0xBD71, 0,
- 0, 0xBD72, 0x5825, 0xBD73, 0x3279, 0xBD74, 0x5823, 0x5824,
- 0xBD75, 0, 0x577E, 0x5822, 0, 0xBD76, 0xBD77, 0x3867,
- 0x4D2A, 0, 0xBD78, 0x3435, 0xBD79, 0xBD7A, 0x3159, 0x5826,
- 0xBD7B, 0x473A, 0x302D, 0, 0, 0, 0, 0,
- 0xBD7C, 0xBD7D, 0x4861, 0x575C, 0x582C, 0x5830, 0x4C65, 0xBD7E,
- 0x5829, 0, 0, 0xBE21, 0x4569, 0x582E, 0xBE22, 0,
-};
-static const unsigned short utf8_to_euc_E682_x0213[] = {
- 0, 0x577A, 0xBD6D, 0x577D, 0x5821, 0, 0xBD6E, 0,
- 0xBD6F, 0x3C3D, 0xAC4D, 0x5827, 0x4470, 0x577B, 0xBD71, 0,
- 0, 0xBD72, 0x5825, 0xBD73, 0x3279, 0xAC4E, 0x5823, 0x5824,
- 0xBD75, 0, 0x577E, 0x5822, 0, 0x7451, 0x7452, 0x3867,
- 0x4D2A, 0, 0xBD78, 0x3435, 0xBD79, 0xBD7A, 0x3159, 0x5826,
- 0xAC4F, 0x473A, 0x302D, 0, 0, 0, 0, 0,
- 0xAC51, 0xAC52, 0x4861, 0x575C, 0x582C, 0x5830, 0x4C65, 0xBD7E,
- 0x5829, 0, 0, 0xBE21, 0x4569, 0x582E, 0xAC53, 0,
-};
-static const unsigned short utf8_to_euc_E683[] = {
- 0, 0, 0xBE23, 0, 0xBE24, 0x3E70, 0x582F, 0x4657,
- 0xBE25, 0xBE26, 0xBE27, 0xBE28, 0, 0, 0xBE29, 0xBE2A,
- 0, 0x4F47, 0, 0x582B, 0xBE2B, 0xBE2C, 0, 0,
- 0x5831, 0xBE2D, 0x397B, 0xBE2E, 0x404B, 0xBE2F, 0xBE30, 0x3054,
- 0x582A, 0x5828, 0xBE31, 0x415A, 0, 0xBE32, 0, 0x577C,
- 0x3B34, 0, 0, 0, 0, 0, 0, 0,
- 0x4246, 0x583D, 0xBE33, 0x415B, 0x5838, 0xBE34, 0x5835, 0x5836,
- 0xBE35, 0x3C66, 0x5839, 0x583C, 0xBE36, 0xBE37, 0, 0,
-};
-static const unsigned short utf8_to_euc_E683_x0213[] = {
- 0, 0, 0xBE23, 0, 0xBE24, 0x3E70, 0x582F, 0x4657,
- 0xAC54, 0xBE26, 0xBE27, 0x7453, 0, 0, 0xBE29, 0xBE2A,
- 0, 0x4F47, 0, 0x582B, 0x7454, 0x7455, 0, 0,
- 0x5831, 0xAC55, 0x397B, 0xAC56, 0x404B, 0x7456, 0, 0x3054,
- 0x582A, 0x5828, 0xBE31, 0x415A, 0, 0xBE32, 0, 0x577C,
- 0x3B34, 0, 0, 0, 0, 0, 0xAC57, 0,
- 0x4246, 0x583D, 0xAC58, 0x415B, 0x5838, 0xAC59, 0x5835, 0x5836,
- 0x7457, 0x3C66, 0x5839, 0x583C, 0xBE36, 0xBE37, 0, 0,
-};
-static const unsigned short utf8_to_euc_E684[] = {
- 0x5837, 0x3D25, 0xBE38, 0x583A, 0, 0, 0x5834, 0xBE39,
- 0x4C7C, 0x4C7B, 0xBE3A, 0, 0xBE3B, 0x583E, 0x583F, 0x3055,
- 0xBE3C, 0xBE3D, 0xBE3E, 0xBE3F, 0xBE40, 0x5833, 0xBE41, 0xBE42,
- 0, 0xBE43, 0x3672, 0x3026, 0xBE44, 0, 0xBE45, 0x3436,
- 0xF440, 0x583B, 0xBE46, 0, 0, 0, 0, 0x5843,
- 0x5842, 0, 0xBE47, 0xBE48, 0x5847, 0, 0, 0,
- 0xBE49, 0xBE4A, 0, 0, 0x5848, 0xBE4B, 0xBE4C, 0xBE4D,
- 0, 0xBE4E, 0, 0, 0x5846, 0x5849, 0x5841, 0x5845,
-};
-static const unsigned short utf8_to_euc_E684_x0213[] = {
- 0x5837, 0x3D25, 0xBE38, 0x583A, 0, 0, 0x5834, 0xBE39,
- 0x4C7C, 0x4C7B, 0xBE3A, 0, 0xBE3B, 0x583E, 0x583F, 0x3055,
- 0xAC5A, 0, 0xAC5B, 0xAC5C, 0xBE40, 0x5833, 0xBE41, 0xBE42,
- 0, 0xAC5D, 0x3672, 0x3026, 0x7458, 0, 0xAC5E, 0x3436,
- 0, 0x583B, 0xBE46, 0, 0, 0, 0, 0x5843,
- 0x5842, 0, 0xBE47, 0x7459, 0x5847, 0, 0, 0,
- 0x745A, 0xBE4A, 0, 0, 0x5848, 0xBE4B, 0xBE4C, 0x745B,
- 0, 0xBE4E, 0xAC5F, 0, 0x5846, 0x5849, 0x5841, 0x5845,
-};
-static const unsigned short utf8_to_euc_E685[] = {
- 0, 0xBE4F, 0x584A, 0, 0x584B, 0xBE50, 0xBE51, 0x5840,
- 0x3B7C, 0xBE52, 0x5844, 0x4256, 0x3932, 0x5832, 0x3F35, 0,
- 0, 0, 0, 0x5858, 0, 0x4A69, 0, 0,
- 0x584E, 0x584F, 0x5850, 0, 0, 0x5857, 0xBE53, 0x5856,
- 0xBE54, 0, 0x4B7D, 0x3437, 0, 0x5854, 0, 0x3745,
- 0x3334, 0, 0, 0x5851, 0xBE55, 0, 0x4E38, 0x5853,
- 0x3056, 0x5855, 0xBE56, 0x584C, 0x5852, 0x5859, 0x3744, 0x584D,
- 0xBE57, 0, 0, 0xBE58, 0xBE59, 0, 0x4D5D, 0xBE5A,
-};
-static const unsigned short utf8_to_euc_E685_x0213[] = {
- 0, 0xAC61, 0x584A, 0, 0x584B, 0xBE50, 0xAC62, 0x5840,
- 0x3B7C, 0xBE52, 0x5844, 0x4256, 0x3932, 0x5832, 0x3F35, 0,
- 0, 0, 0, 0x5858, 0, 0x4A69, 0, 0,
- 0x584E, 0x584F, 0x5850, 0, 0, 0x5857, 0xBE53, 0x5856,
- 0xAC63, 0, 0x4B7D, 0x3437, 0, 0x5854, 0, 0x3745,
- 0x3334, 0, 0, 0x5851, 0xBE55, 0, 0x4E38, 0x5853,
- 0x3056, 0x5855, 0xBE56, 0x584C, 0x5852, 0x5859, 0x3744, 0x584D,
- 0xBE57, 0, 0, 0xBE58, 0xAC64, 0, 0x4D5D, 0xBE5A,
-};
-static const unsigned short utf8_to_euc_E686[] = {
- 0xBE5B, 0xBE5C, 0x4D2B, 0xBE5D, 0xBE5E, 0, 0, 0x585C,
- 0, 0, 0x5860, 0xBE5F, 0, 0xBE60, 0x417E, 0,
- 0x4E79, 0x5861, 0xBE61, 0xBE62, 0x585E, 0, 0x585B, 0xBE63,
- 0xBE64, 0x585A, 0x585F, 0, 0xBE65, 0xBE66, 0, 0xBE67,
- 0xBE68, 0, 0, 0, 0x4A30, 0xBE69, 0, 0x4634,
- 0xBE6A, 0x3746, 0xBE6B, 0x5862, 0x585D, 0xBE6C, 0x5863, 0,
- 0, 0, 0x377B, 0, 0, 0, 0x3231, 0,
- 0xBE6D, 0xBE6E, 0x586B, 0, 0xBE6F, 0, 0x3438, 0,
-};
-static const unsigned short utf8_to_euc_E686_x0213[] = {
- 0xBE5B, 0xBE5C, 0x4D2B, 0xBE5D, 0xBE5E, 0, 0, 0x585C,
- 0, 0, 0x5860, 0xBE5F, 0, 0x745D, 0x417E, 0,
- 0x4E79, 0x5861, 0xAC66, 0xAC67, 0x585E, 0, 0x585B, 0xAC68,
- 0xAC69, 0x585A, 0x585F, 0, 0xBE65, 0xBE66, 0, 0xBE67,
- 0xBE68, 0, 0, 0, 0x4A30, 0xAC6A, 0, 0x4634,
- 0xAC6B, 0x3746, 0xBE6B, 0x5862, 0x585D, 0xAC6C, 0x5863, 0,
- 0, 0, 0x377B, 0, 0, 0, 0x3231, 0,
- 0xBE6D, 0x7460, 0x586B, 0, 0x745F, 0, 0x3438, 0,
-};
-static const unsigned short utf8_to_euc_E687[] = {
- 0xBE70, 0xBE71, 0xBE72, 0x5869, 0, 0, 0x586A, 0x3A29,
- 0x5868, 0x5866, 0x5865, 0x586C, 0x5864, 0x586E, 0xBE73, 0xBE74,
- 0x327B, 0, 0, 0, 0, 0xBE75, 0, 0,
- 0, 0, 0, 0, 0xBE76, 0xBE77, 0xBE78, 0xBE79,
- 0, 0xBE7A, 0xBE7B, 0x5870, 0, 0xBE7E, 0x586F, 0xBE7C,
- 0, 0xBE7D, 0, 0, 0xBF21, 0xBF22, 0, 0xBF23,
- 0, 0, 0x4428, 0, 0x5873, 0, 0x5871, 0x5867,
- 0x377C, 0, 0x5872, 0, 0x5876, 0x5875, 0x5877, 0x5874,
-};
-static const unsigned short utf8_to_euc_E687_x0213[] = {
- 0xBE70, 0xBE71, 0xBE72, 0x5869, 0, 0, 0x586A, 0x3A29,
- 0x5868, 0x5866, 0x5865, 0x586C, 0x5864, 0x586E, 0xBE73, 0xBE74,
- 0x327B, 0, 0, 0, 0, 0xAC6E, 0, 0,
- 0, 0, 0, 0, 0xBE76, 0xAC6F, 0xBE78, 0xAC70,
- 0, 0xBE7A, 0xBE7B, 0x5870, 0, 0xBE7E, 0x586F, 0xBE7C,
- 0, 0xBE7D, 0, 0, 0xBF21, 0xBF22, 0, 0xBF23,
- 0, 0, 0x4428, 0, 0x5873, 0xAC71, 0x5871, 0x5867,
- 0x377C, 0, 0x5872, 0, 0x5876, 0x5875, 0x5877, 0x5874,
-};
-static const unsigned short utf8_to_euc_E688[] = {
- 0x5878, 0xBF24, 0, 0xBF25, 0xBF26, 0, 0, 0xBF27,
- 0x5879, 0x587A, 0x4A6A, 0, 0x587C, 0x587B, 0x3D3F, 0,
- 0x402E, 0x3266, 0x327C, 0xBF28, 0x587D, 0xBF29, 0x303F, 0,
- 0, 0, 0x404C, 0x587E, 0xBF2A, 0x6C43, 0x5921, 0x3761,
- 0xBF2B, 0x5922, 0xBF2C, 0xBF2D, 0, 0, 0x406F, 0xBF2E,
- 0, 0xBF2F, 0x5923, 0xBF30, 0, 0, 0x5924, 0x353A,
- 0x5925, 0, 0x5926, 0x5927, 0x4257, 0, 0, 0,
- 0x384D, 0xBF31, 0, 0x4C61, 0, 0xBF32, 0, 0x4B3C,
-};
-static const unsigned short utf8_to_euc_E688_x0213[] = {
- 0x5878, 0xBF24, 0, 0xBF25, 0xBF26, 0, 0, 0xBF27,
- 0x5879, 0x587A, 0x4A6A, 0, 0x587C, 0x587B, 0x3D3F, 0,
- 0x402E, 0x3266, 0x327C, 0, 0x587D, 0xAC73, 0x303F, 0,
- 0, 0, 0x404C, 0x587E, 0xBF2A, 0x6C43, 0x5921, 0x3761,
- 0xBF2B, 0x5922, 0x7462, 0xAC74, 0, 0, 0x406F, 0xBF2E,
- 0, 0xAC75, 0x5923, 0xBF30, 0, 0, 0x5924, 0x353A,
- 0x5925, 0, 0x5926, 0x5927, 0x4257, 0, 0, 0,
- 0x384D, 0xBF31, 0, 0x4C61, 0, 0xBF32, 0x7463, 0x4B3C,
-};
-static const unsigned short utf8_to_euc_E689[] = {
- 0x3D6A, 0x5928, 0xBF33, 0xBF34, 0xBF35, 0, 0xBF36, 0x4070,
- 0x6E3D, 0x4862, 0, 0x3C6A, 0xBF37, 0x3A4D, 0x5929, 0,
- 0xBF38, 0xBF39, 0xBF3A, 0x4247, 0xBF3B, 0x4A27, 0xBF3C, 0,
- 0x4271, 0, 0xBF3D, 0x592C, 0xBF3E, 0, 0x592A, 0,
- 0x592D, 0, 0, 0x592B, 0xBF3F, 0, 0, 0,
- 0x592E, 0, 0, 0, 0, 0xBF40, 0x4A31, 0xBF41,
- 0, 0x3037, 0, 0xBF42, 0, 0, 0x495E, 0,
- 0, 0x4863, 0xBF43, 0, 0x592F, 0xBF44, 0x5932, 0x3E35,
-};
-static const unsigned short utf8_to_euc_E689_x0213[] = {
- 0x3D6A, 0x5928, 0xBF33, 0x7464, 0xBF35, 0, 0xAC76, 0x4070,
- 0x6E3D, 0x4862, 0, 0x3C6A, 0xAC77, 0x3A4D, 0x5929, 0,
- 0xBF38, 0xAC78, 0xAC79, 0x4247, 0xBF3B, 0x4A27, 0x7465, 0,
- 0x4271, 0, 0x7466, 0x592C, 0xBF3E, 0, 0x592A, 0,
- 0x592D, 0xAC7A, 0, 0x592B, 0xAC7B, 0, 0, 0,
- 0x592E, 0, 0, 0, 0, 0xAC7D, 0x4A31, 0x7467,
- 0, 0x3037, 0, 0xAC7E, 0, 0, 0x495E, 0,
- 0, 0x4863, 0xBF43, 0xAC7C, 0x592F, 0xBF44, 0x5932, 0x3E35,
-};
-static const unsigned short utf8_to_euc_E68A[] = {
- 0x353B, 0, 0x5930, 0x5937, 0x3E36, 0, 0, 0,
- 0, 0x5931, 0x4744, 0, 0, 0xBF45, 0xBF46, 0xBF47,
- 0xBF48, 0x4D5E, 0x5933, 0x5934, 0x5938, 0x456A, 0x5935, 0x3933,
- 0x405E, 0, 0, 0x5946, 0x4834, 0, 0x4272, 0,
- 0, 0, 0, 0, 0, 0, 0xBF49, 0,
- 0xBF4A, 0, 0, 0x4864, 0x5A2D, 0, 0, 0,
- 0, 0x4A7A, 0, 0xBF4B, 0, 0x4471, 0xBF4C, 0xBF4D,
- 0, 0x4B75, 0xBF4E, 0x593B, 0x3221, 0x436A, 0xBF4F, 0xBF50,
-};
-static const unsigned short utf8_to_euc_E68A_x0213[] = {
- 0x353B, 0, 0x5930, 0x5937, 0x3E36, 0x7468, 0, 0,
- 0, 0x5931, 0x4744, 0, 0, 0xBF45, 0xBF46, 0xBF47,
- 0xBF48, 0x4D5E, 0x5933, 0x5934, 0x5938, 0x456A, 0x5935, 0x3933,
- 0x405E, 0xAD21, 0, 0x5946, 0x4834, 0, 0x4272, 0,
- 0, 0, 0, 0, 0, 0, 0xAD22, 0,
- 0xBF4A, 0, 0, 0x4864, 0x5A2D, 0, 0, 0,
- 0, 0x4A7A, 0, 0xBF4B, 0, 0x4471, 0xBF4C, 0xBF4D,
- 0, 0x4B75, 0xBF4E, 0x593B, 0x3221, 0x436A, 0xBF4F, 0xBF50,
-};
-static const unsigned short utf8_to_euc_E68B[] = {
- 0, 0, 0x5944, 0, 0xBF51, 0x4334, 0x593E, 0x5945,
- 0x5940, 0x5947, 0x5943, 0, 0x5942, 0x476F, 0xBF52, 0x593C,
- 0x327D, 0x593A, 0x3571, 0x4273, 0x5936, 0xBF53, 0xBF54, 0x5939,
- 0x3934, 0x405B, 0xBF55, 0x3E37, 0x5941, 0x4752, 0, 0,
- 0x3572, 0x3348, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xBF56, 0, 0x3367, 0x3F21, 0x5949, 0x594E,
- 0, 0x594A, 0xBF57, 0x377D, 0xBF58, 0x594F, 0x3B22, 0x3969,
- 0, 0, 0, 0, 0xBF59, 0xBF5A, 0x3D26, 0x593D,
-};
-static const unsigned short utf8_to_euc_E68B_x0213[] = {
- 0, 0, 0x5944, 0, 0x7469, 0x4334, 0x593E, 0x5945,
- 0x5940, 0x5947, 0x5943, 0, 0x5942, 0x476F, 0xBF52, 0x593C,
- 0x327D, 0x593A, 0x3571, 0x4273, 0x5936, 0xAD23, 0x746A, 0x5939,
- 0x3934, 0x405B, 0xBF55, 0x3E37, 0x5941, 0x4752, 0, 0,
- 0x3572, 0x3348, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xBF56, 0, 0x3367, 0x3F21, 0x5949, 0x594E,
- 0, 0x594A, 0xBF57, 0x377D, 0xBF58, 0x594F, 0x3B22, 0x3969,
- 0, 0, 0, 0, 0x746B, 0xAD25, 0x3D26, 0x593D,
-};
-static const unsigned short utf8_to_euc_E68C[] = {
- 0, 0x3B7D, 0x594C, 0xBF5B, 0xBF5C, 0, 0, 0x3B58,
- 0x594D, 0x3044, 0xBF5D, 0xBF5E, 0x5948, 0xBF5F, 0, 0,
- 0xBF60, 0x4429, 0, 0xBF61, 0, 0, 0xBF62, 0,
- 0xBF63, 0x3573, 0, 0, 0, 0, 0, 0x3634,
- 0, 0, 0, 0, 0, 0, 0, 0x594B,
- 0x3027, 0xBF64, 0xBF65, 0x3A43, 0, 0xBF66, 0, 0x3F36,
- 0, 0, 0, 0, 0, 0xBF67, 0xBF68, 0,
- 0, 0xBF69, 0x4472, 0, 0xBF6A, 0x4854, 0x5951, 0x415E,
-};
-static const unsigned short utf8_to_euc_E68C_x0213[] = {
- 0, 0x3B7D, 0x594C, 0xAD26, 0xBF5C, 0, 0, 0x3B58,
- 0x594D, 0x3044, 0x746C, 0xBF5E, 0x5948, 0xAD27, 0, 0,
- 0xAD28, 0x4429, 0, 0xBF61, 0, 0, 0xBF62, 0,
- 0x746D, 0x3573, 0, 0, 0, 0, 0, 0x3634,
- 0, 0, 0, 0, 0, 0, 0, 0x594B,
- 0x3027, 0xBF64, 0xBF65, 0x3A43, 0, 0xBF66, 0, 0x3F36,
- 0, 0, 0xAD2B, 0, 0, 0xAD2C, 0xBF68, 0,
- 0, 0x746E, 0x4472, 0xAD2D, 0xAD2E, 0x4854, 0x5951, 0x415E,
-};
-static const unsigned short utf8_to_euc_E68D[] = {
- 0, 0xBF6B, 0xBF6C, 0xBF6D, 0xBF6E, 0, 0xBF6F, 0,
- 0, 0x422A, 0xBF70, 0xBF71, 0x3B2B, 0x5952, 0xBF72, 0x5954,
- 0x5950, 0, 0xBF73, 0xBF74, 0xBF75, 0x4A61, 0, 0x443D,
- 0xBF76, 0, 0, 0xBF77, 0x415C, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xBF78, 0xBF79, 0x4A7B,
- 0x3C4E, 0x5960, 0, 0x595F, 0xBF7A, 0xBF7B, 0x3F78, 0,
- 0, 0xBF7C, 0x377E, 0, 0xBF7D, 0xBF7E, 0x5959, 0x3E39,
- 0xC021, 0, 0x4668, 0x4731, 0xC022, 0xC023, 0, 0xC024,
-};
-static const unsigned short utf8_to_euc_E68D_x0213[] = {
- 0, 0xAD2F, 0xBF6C, 0x746F, 0xAD30, 0, 0xBF6F, 0,
- 0, 0x422A, 0xBF70, 0xBF71, 0x3B2B, 0x5952, 0xAD31, 0x5954,
- 0x5950, 0, 0xBF73, 0xBF74, 0xBF75, 0x4A61, 0, 0x443D,
- 0xBF76, 0xAD33, 0, 0xBF77, 0x415C, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x7470, 0xBF79, 0x4A7B,
- 0x3C4E, 0x5960, 0, 0x595F, 0xAD36, 0xBF7B, 0x3F78, 0,
- 0, 0xBF7C, 0x377E, 0, 0xBF7D, 0xBF7E, 0x5959, 0x3E39,
- 0xC021, 0, 0x4668, 0x4731, 0x7471, 0xC023, 0, 0xC024,
-};
-static const unsigned short utf8_to_euc_E68E[] = {
- 0x5957, 0, 0xC025, 0x415D, 0xC026, 0, 0, 0xC027,
- 0x3C78, 0x595C, 0xC028, 0, 0x3E38, 0, 0x5956, 0x595B,
- 0xC029, 0, 0x4753, 0, 0xC02A, 0xC02B, 0x5955, 0,
- 0x3721, 0xC02C, 0xC02D, 0x335D, 0, 0, 0xC02E, 0x595D,
- 0x4E2B, 0x3A4E, 0x4335, 0x595A, 0xC02F, 0x405C, 0xC030, 0x3935,
- 0x3F64, 0x3166, 0x413C, 0x5958, 0x3545, 0xC031, 0xC032, 0xC033,
- 0, 0, 0x3747, 0, 0x444F, 0x595E, 0, 0,
- 0, 0, 0, 0x415F, 0, 0xC034, 0x5961, 0,
-};
-static const unsigned short utf8_to_euc_E68E_x0213[] = {
- 0x5957, 0, 0xC025, 0x415D, 0xAD37, 0, 0, 0xC027,
- 0x3C78, 0x595C, 0xC028, 0, 0x3E38, 0, 0x5956, 0x595B,
- 0xC029, 0, 0x4753, 0, 0xAD3A, 0xC02B, 0x5955, 0,
- 0x3721, 0xAD38, 0xC02D, 0x335D, 0, 0, 0xC02E, 0x595D,
- 0x4E2B, 0x3A4E, 0x4335, 0x595A, 0xC02F, 0x405C, 0xC030, 0x3935,
- 0x3F64, 0x3166, 0x413C, 0x5958, 0x3545, 0xC031, 0xC032, 0xC033,
- 0, 0, 0x3747, 0, 0x444F, 0x595E, 0, 0,
- 0, 0, 0, 0x415F, 0, 0xAD3B, 0x5961, 0,
-};
-static const unsigned short utf8_to_euc_E68F[] = {
- 0x5963, 0xC035, 0, 0x4237, 0x5969, 0xC036, 0x5964, 0,
- 0xC037, 0x5966, 0, 0, 0, 0, 0xC038, 0x4941,
- 0x4473, 0xC039, 0x5967, 0xC03A, 0xC03B, 0xC03C, 0x4D2C, 0,
- 0, 0, 0x4D48, 0x3439, 0xC03D, 0, 0, 0,
- 0xC03E, 0x302E, 0, 0x5965, 0, 0xC03F, 0, 0,
- 0, 0x5962, 0xC040, 0, 0xC041, 0, 0x3478, 0,
- 0, 0, 0xC042, 0xC043, 0x3167, 0xC044, 0x5968, 0,
- 0xC045, 0xC046, 0x4D49, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E68F_x0213[] = {
- 0x5963, 0xC035, 0, 0x4237, 0x5969, 0xC036, 0x5964, 0,
- 0xC037, 0x5966, 0, 0, 0, 0, 0xC038, 0x4941,
- 0x4473, 0xC039, 0x5967, 0xC03A, 0xAD3D, 0xAD3E, 0x4D2C, 0,
- 0, 0, 0x4D48, 0x3439, 0xAD3F, 0, 0, 0,
- 0xAD40, 0x302E, 0, 0x5965, 0, 0x7472, 0, 0,
- 0, 0x5962, 0xC040, 0xAD41, 0xAD42, 0x7473, 0x3478, 0,
- 0, 0, 0xAD43, 0xC043, 0x3167, 0x7474, 0x5968, 0xAD3C,
- 0xC045, 0xC046, 0x4D49, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E690[] = {
- 0, 0, 0, 0, 0, 0, 0x596C, 0,
- 0, 0xC047, 0xC048, 0, 0, 0x423B, 0, 0x5973,
- 0xC049, 0, 0xC04A, 0x596D, 0xC04B, 0, 0x596A, 0x5971,
- 0xC04C, 0, 0, 0, 0x5953, 0, 0xC04D, 0,
- 0xC04E, 0, 0xC04F, 0, 0xC050, 0xC051, 0x596E, 0,
- 0x5972, 0xC052, 0xC053, 0, 0x4842, 0x456B, 0, 0xC054,
- 0xC055, 0, 0, 0, 0x596B, 0xC056, 0x596F, 0,
- 0, 0, 0x3748, 0, 0, 0xC057, 0x3A71, 0xC058,
-};
-static const unsigned short utf8_to_euc_E690_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0x596C, 0,
- 0, 0xAD44, 0xC048, 0, 0, 0x423B, 0, 0x5973,
- 0x7475, 0, 0xC04A, 0x596D, 0x7476, 0, 0x596A, 0x5971,
- 0xC04C, 0, 0, 0, 0x5953, 0, 0xAD45, 0,
- 0xC04E, 0, 0x7477, 0, 0xC050, 0xAD46, 0x596E, 0,
- 0x5972, 0xAD47, 0xC053, 0, 0x4842, 0x456B, 0, 0xAD48,
- 0xC055, 0, 0, 0, 0x596B, 0xC056, 0x596F, 0,
- 0, 0, 0x3748, 0, 0, 0xC057, 0x3A71, 0xC058,
-};
-static const unsigned short utf8_to_euc_E691[] = {
- 0, 0, 0x405D, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xC059, 0, 0, 0x5977, 0xC05A,
- 0, 0xC05B, 0xC05C, 0xC05D, 0xC05E, 0, 0, 0,
- 0x4526, 0, 0xC05F, 0xC060, 0xC061, 0xC062, 0, 0xC063,
- 0xC064, 0xC065, 0, 0xC066, 0, 0, 0, 0x5974,
- 0, 0x4B60, 0, 0, 0, 0xC067, 0, 0x5975,
- 0, 0, 0, 0xC068, 0xC069, 0, 0x5976, 0,
- 0x4C4E, 0, 0x4022, 0xC06A, 0, 0xC06B, 0, 0,
-};
-static const unsigned short utf8_to_euc_E691_x0213[] = {
- 0, 0, 0x405D, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xC059, 0, 0, 0x5977, 0xC05A,
- 0, 0x7479, 0xC05C, 0xC05D, 0xC05E, 0, 0, 0,
- 0x4526, 0, 0xAD49, 0xAD4A, 0xC061, 0xAD4B, 0, 0xC063,
- 0x747A, 0xC065, 0, 0xC066, 0, 0, 0, 0x5974,
- 0, 0x4B60, 0, 0, 0, 0x747B, 0, 0x5975,
- 0, 0, 0, 0xAD4C, 0xC069, 0, 0x5976, 0,
- 0x4C4E, 0x7478, 0x4022, 0xC06A, 0, 0xAD4D, 0, 0,
-};
-static const unsigned short utf8_to_euc_E692[] = {
- 0, 0, 0, 0x3762, 0, 0xC06C, 0, 0xC06D,
- 0x597D, 0, 0, 0, 0, 0, 0, 0xC06E,
- 0xC06F, 0xC070, 0x3B35, 0x597A, 0, 0x5979, 0, 0,
- 0xC071, 0xC072, 0x4732, 0xC073, 0, 0xC074, 0x4635, 0xC075,
- 0, 0xC076, 0, 0xC077, 0x4531, 0x597B, 0xC078, 0,
- 0xC079, 0x597C, 0, 0x496F, 0xC07A, 0x4745, 0x3B23, 0,
- 0x4071, 0, 0x4B50, 0xC07B, 0, 0, 0, 0,
- 0, 0x3349, 0, 0x5A25, 0x597E, 0xC07C, 0xC07D, 0xC07E,
-};
-static const unsigned short utf8_to_euc_E692_x0213[] = {
- 0, 0, 0, 0x3762, 0, 0xC06C, 0, 0xAD4E,
- 0x597D, 0, 0, 0, 0, 0, 0, 0xC06E,
- 0xC06F, 0xAD4F, 0x3B35, 0x597A, 0, 0x5979, 0, 0,
- 0xC071, 0xC072, 0x4732, 0xC073, 0, 0xAD50, 0x4635, 0xAD51,
- 0, 0xC076, 0, 0xC077, 0x4531, 0x597B, 0xC078, 0,
- 0xC079, 0x597C, 0, 0x496F, 0xC07A, 0x4745, 0x3B23, 0,
- 0x4071, 0, 0x4B50, 0xC07B, 0, 0, 0, 0,
- 0, 0x3349, 0, 0x5A25, 0x597E, 0xC07C, 0x747D, 0x747E,
-};
-static const unsigned short utf8_to_euc_E693[] = {
- 0, 0x4D4A, 0x5A27, 0, 0xC121, 0x5A23, 0, 0x5A24,
- 0, 0xC122, 0xC123, 0xC124, 0xC125, 0x4160, 0xC126, 0,
- 0xC127, 0xC128, 0x5A22, 0, 0x593F, 0xC129, 0, 0xC12A,
- 0x5A26, 0, 0x5A21, 0, 0, 0, 0, 0,
- 0x5A2B, 0x5A2C, 0x4527, 0x5A2E, 0xC12B, 0xC12C, 0x3B24, 0x5A29,
- 0, 0xC12D, 0xC12E, 0, 0x353C, 0xC12F, 0, 0x5A2F,
- 0xC130, 0x5A28, 0x5A33, 0, 0x5A32, 0xC131, 0x5A31, 0xC132,
- 0, 0, 0x5A34, 0xC133, 0, 0x5A36, 0x3E71, 0xC134,
-};
-static const unsigned short utf8_to_euc_E693_x0213[] = {
- 0, 0x4D4A, 0x5A27, 0, 0x7521, 0x5A23, 0, 0x5A24,
- 0, 0xC122, 0x7522, 0xAD52, 0xAD53, 0x4160, 0x747C, 0,
- 0x7523, 0xC128, 0x5A22, 0, 0x593F, 0xAD54, 0, 0xAD55,
- 0x5A26, 0, 0x5A21, 0, 0, 0, 0, 0,
- 0x5A2B, 0x5A2C, 0x4527, 0x5A2E, 0xAD57, 0xAD58, 0x3B24, 0x5A29,
- 0, 0xC12D, 0xC12E, 0, 0x353C, 0xC12F, 0, 0x5A2F,
- 0xC130, 0x5A28, 0x5A33, 0, 0x5A32, 0xC131, 0x5A31, 0x7524,
- 0, 0, 0x5A34, 0x7525, 0, 0x5A36, 0x3E71, 0xAD59,
-};
-static const unsigned short utf8_to_euc_E694[] = {
- 0x5A35, 0xC135, 0, 0, 0xC136, 0x5A39, 0, 0,
- 0xC137, 0xC138, 0xC139, 0, 0, 0, 0, 0xC13A,
- 0, 0, 0, 0xC13B, 0xC13C, 0, 0xC13D, 0,
- 0x5A37, 0xC13E, 0, 0xC13F, 0x5A38, 0x5970, 0xC140, 0xC141,
- 0, 0, 0xC142, 0x5A3B, 0x5A3A, 0, 0xC143, 0,
- 0, 0xC144, 0x5978, 0x5A3C, 0x5A30, 0, 0xC145, 0x3B59,
- 0, 0xC146, 0, 0, 0x5A3D, 0x5A3E, 0x5A40, 0x5A3F,
- 0x5A41, 0x327E, 0xC147, 0x3936, 0xC148, 0xC149, 0x4A7C, 0x402F,
-};
-static const unsigned short utf8_to_euc_E694_x0213[] = {
- 0x5A35, 0xC135, 0, 0, 0xAD5A, 0x5A39, 0, 0,
- 0xC137, 0xC138, 0xC139, 0, 0, 0, 0, 0xAD5C,
- 0, 0, 0, 0xC13B, 0xAD5D, 0, 0xAD5E, 0,
- 0x5A37, 0xC13E, 0, 0xC13F, 0x5A38, 0x5970, 0xAD60, 0xC141,
- 0, 0, 0x7526, 0x5A3B, 0x5A3A, 0, 0xC143, 0,
- 0, 0x7527, 0x5978, 0x5A3C, 0x5A30, 0, 0xC145, 0x3B59,
- 0, 0xC146, 0xAD61, 0, 0x5A3D, 0x5A3E, 0x5A40, 0x5A3F,
- 0x5A41, 0x327E, 0xC147, 0x3936, 0xC148, 0xC149, 0x4A7C, 0x402F,
-};
-static const unsigned short utf8_to_euc_E695[] = {
- 0, 0, 0, 0xC14A, 0, 0x384E, 0, 0xC14B,
- 0x5A43, 0xC14C, 0, 0, 0, 0x5A46, 0xF441, 0x4952,
- 0xC14D, 0x355F, 0xC14E, 0, 0xC14F, 0x5A45, 0x5A44, 0x4754,
- 0x5A47, 0x3635, 0, 0, 0, 0x5A49, 0x5A48, 0xC150,
- 0xC151, 0, 0x343A, 0x3B36, 0, 0, 0x4658, 0xC152,
- 0, 0, 0, 0xC153, 0x3749, 0, 0, 0,
- 0x3F74, 0, 0x5A4A, 0, 0x4030, 0x4528, 0, 0x495F,
- 0x5A4B, 0, 0xC154, 0, 0, 0xC155, 0, 0,
-};
-static const unsigned short utf8_to_euc_E695_x0213[] = {
- 0, 0, 0, 0xC14A, 0xAD62, 0x384E, 0, 0xC14B,
- 0x5A43, 0xC14C, 0, 0, 0, 0x5A46, 0, 0x4952,
- 0xC14D, 0x355F, 0xC14E, 0, 0xAD63, 0x5A45, 0x5A44, 0x4754,
- 0x5A47, 0x3635, 0, 0, 0, 0x5A49, 0x5A48, 0xC150,
- 0xC151, 0, 0x343A, 0x3B36, 0, 0, 0x4658, 0x7529,
- 0, 0, 0, 0xAD64, 0x3749, 0, 0, 0,
- 0x3F74, 0, 0x5A4A, 0, 0x4030, 0x4528, 0, 0x495F,
- 0x5A4B, 0, 0xAD65, 0, 0, 0xC155, 0, 0,
-};
-static const unsigned short utf8_to_euc_E696[] = {
- 0, 0xC156, 0x5A4C, 0x5A4D, 0, 0xC157, 0, 0x4A38,
- 0x555D, 0x4046, 0xC158, 0, 0x494C, 0, 0x3A58, 0,
- 0x4865, 0x4843, 0xC159, 0, 0, 0xC15A, 0, 0x454D,
- 0xC15B, 0x4E41, 0, 0x5A4F, 0x3C50, 0xC15C, 0, 0x5A50,
- 0xC15D, 0x3036, 0, 0xC15E, 0x3654, 0x404D, 0xC15F, 0x4960,
- 0, 0, 0, 0x5A51, 0x3B42, 0x4347, 0xC160, 0x3B5B,
- 0x3F37, 0, 0xC161, 0xC162, 0xC163, 0, 0, 0x5A52,
- 0, 0x4A7D, 0, 0, 0x3177, 0x3B5C, 0, 0xC164,
-};
-static const unsigned short utf8_to_euc_E696_x0213[] = {
- 0, 0xAD66, 0x5A4C, 0x5A4D, 0xAD67, 0xAD68, 0, 0x4A38,
- 0x555D, 0x4046, 0xAD69, 0, 0x494C, 0, 0x3A58, 0,
- 0x4865, 0x4843, 0xC159, 0, 0, 0xC15A, 0, 0x454D,
- 0xC15B, 0x4E41, 0, 0x5A4F, 0x3C50, 0x752A, 0, 0x5A50,
- 0xC15D, 0x3036, 0, 0xC15E, 0x3654, 0x404D, 0xC15F, 0x4960,
- 0, 0, 0, 0x5A51, 0x3B42, 0x4347, 0xC160, 0x3B5B,
- 0x3F37, 0, 0xAD6A, 0xC162, 0xC163, 0xAD6B, 0, 0x5A52,
- 0xAD6C, 0x4A7D, 0, 0, 0x3177, 0x3B5C, 0, 0xAD6D,
-};
-static const unsigned short utf8_to_euc_E697[] = {
- 0, 0x5A55, 0xC165, 0x5A53, 0x5A56, 0x4E39, 0x5A54, 0,
- 0xC166, 0xC167, 0, 0x407B, 0x5A57, 0, 0xC168, 0x4232,
- 0xC169, 0, 0x5A58, 0, 0xC16A, 0, 0xC16B, 0x347A,
- 0xC16C, 0x5A5A, 0, 0x5A59, 0, 0, 0, 0xC16D,
- 0x5A5B, 0x5A5C, 0x347B, 0, 0, 0x467C, 0x4336, 0x356C,
- 0x3B5D, 0x4161, 0, 0, 0x3D5C, 0x3030, 0, 0,
- 0xC16E, 0x5A5D, 0xC16F, 0, 0xC170, 0xC171, 0, 0,
- 0, 0xC172, 0x3222, 0x5A61, 0, 0, 0xC173, 0xC174,
-};
-static const unsigned short utf8_to_euc_E697_x0213[] = {
- 0, 0x5A55, 0xAD6E, 0x5A53, 0x5A56, 0x4E39, 0x5A54, 0,
- 0xC166, 0xAD6F, 0, 0x407B, 0x5A57, 0, 0xC168, 0x4232,
- 0xC169, 0, 0x5A58, 0, 0xAD70, 0, 0xC16B, 0x347A,
- 0xC16C, 0x5A5A, 0, 0x5A59, 0, 0, 0, 0xC16D,
- 0x5A5B, 0x5A5C, 0x347B, 0, 0, 0x467C, 0x4336, 0x356C,
- 0x3B5D, 0x4161, 0, 0, 0x3D5C, 0x3030, 0, 0,
- 0xC16E, 0x5A5D, 0xAD72, 0, 0xC170, 0xC171, 0, 0,
- 0, 0xAD73, 0x3222, 0x5A61, 0xAD74, 0, 0xC173, 0xC174,
-};
-static const unsigned short utf8_to_euc_E698[] = {
- 0xC175, 0, 0x3937, 0x5A60, 0xC176, 0, 0x3A2B, 0x3E3A,
- 0xC177, 0xC178, 0x5A5F, 0, 0x3E3B, 0xC179, 0x4C40, 0x3A2A,
- 0, 0xC17A, 0xC17B, 0x3057, 0x404E, 0xC17C, 0xC17D, 0,
- 0, 0, 0, 0, 0x5A66, 0xC17E, 0xC221, 0x4031,
- 0x3147, 0xC222, 0xC223, 0xC224, 0xC225, 0x3D55, 0xC226, 0x4B66,
- 0x3A72, 0xC227, 0xC228, 0xC229, 0xC22A, 0x3E3C, 0xC22B, 0x4027,
- 0xC22C, 0xC22D, 0, 0xC22E, 0x5A65, 0x5A63, 0x5A64, 0xC230,
- 0, 0xC22F, 0, 0xF442, 0x436B, 0, 0, 0x5B26,
-};
-static const unsigned short utf8_to_euc_E698_x0213[] = {
- 0x752C, 0, 0x3937, 0x5A60, 0xAD75, 0, 0x3A2B, 0x3E3A,
- 0xAD76, 0x752D, 0x5A5F, 0, 0x3E3B, 0xC179, 0x4C40, 0x3A2A,
- 0, 0xC17A, 0xC17B, 0x3057, 0x404E, 0x752E, 0xC17D, 0,
- 0, 0, 0, 0, 0x5A66, 0xC17E, 0x752F, 0x4031,
- 0x3147, 0xAD77, 0x7531, 0xC224, 0x7532, 0x3D55, 0xC226, 0x4B66,
- 0x3A72, 0xC227, 0xAD78, 0x7533, 0xC22A, 0x3E3C, 0, 0x4027,
- 0x7534, 0x7535, 0, 0x7536, 0x5A65, 0x5A63, 0x5A64, 0xC230,
- 0, 0xC22F, 0x7530, 0, 0x436B, 0, 0, 0x5B26,
-};
-static const unsigned short utf8_to_euc_E699[] = {
- 0xC231, 0x5A6A, 0x3B7E, 0x3938, 0x5A68, 0xC232, 0xC233, 0,
- 0, 0x5A69, 0xC234, 0x3F38, 0xC235, 0, 0xC237, 0x5A67,
- 0, 0xC236, 0x3B2F, 0, 0, 0, 0, 0xC238,
- 0xC239, 0xC23A, 0, 0xC23B, 0xC23C, 0x5A6C, 0x5A6B, 0x5A70,
- 0xC23D, 0xC23E, 0x5A71, 0, 0x5A6D, 0xF443, 0x3322, 0x5A6E,
- 0x5A6F, 0x4855, 0xC240, 0xC241, 0xC242, 0, 0x4961, 0x374A,
- 0x5A72, 0, 0, 0xC244, 0x4032, 0xC245, 0x3E3D, 0xC247,
- 0xC248, 0xC249, 0x4352, 0xC24A, 0xC24C, 0, 0xC243, 0xC246,
-};
-static const unsigned short utf8_to_euc_E699_x0213[] = {
- 0xC231, 0x5A6A, 0x3B7E, 0x3938, 0x5A68, 0xAD79, 0xC233, 0,
- 0x7538, 0x5A69, 0xC234, 0x3F38, 0x7539, 0, 0xAD7B, 0x5A67,
- 0, 0xAD7A, 0x3B2F, 0, 0, 0, 0, 0xAD7E,
- 0xC239, 0x753B, 0x753C, 0xAE21, 0xC23C, 0x5A6C, 0x5A6B, 0x5A70,
- 0xC23D, 0x753D, 0x5A71, 0xAE22, 0x5A6D, 0x753E, 0x3322, 0x5A6E,
- 0x5A6F, 0x4855, 0xAE25, 0xAE26, 0xAE27, 0xAE28, 0x4961, 0x374A,
- 0x5A72, 0, 0, 0x753F, 0x4032, 0xC245, 0x3E3D, 0x7540,
- 0x7541, 0xC249, 0x4352, 0xAE29, 0xC24C, 0, 0xC243, 0xC246,
-};
-static const unsigned short utf8_to_euc_E69A[] = {
- 0xC24B, 0x3647, 0, 0x5A73, 0x5A77, 0, 0, 0x324B,
- 0x5A74, 0x5A76, 0, 0xC24D, 0xC24E, 0xC24F, 0x5A75, 0,
- 0xC250, 0x3D6B, 0xC251, 0, 0, 0, 0x4348, 0x3045,
- 0x5A78, 0xC252, 0xC253, 0xC254, 0xC255, 0x5A79, 0, 0xC256,
- 0xC257, 0, 0x442A, 0, 0xC258, 0, 0x4E71, 0,
- 0, 0, 0, 0x3B43, 0, 0xC259, 0x4A6B, 0,
- 0, 0xC25A, 0xC25B, 0, 0x4B3D, 0xC25C, 0, 0,
- 0x5B22, 0x5A7B, 0, 0xC25D, 0x5A7E, 0, 0x5A7D, 0xC25E,
-};
-static const unsigned short utf8_to_euc_E69A_x0213[] = {
- 0xAE2A, 0x3647, 0, 0x5A73, 0x5A77, 0, 0, 0x324B,
- 0x5A74, 0x5A76, 0, 0xC24D, 0xC24E, 0x7542, 0x5A75, 0,
- 0xAE2B, 0x3D6B, 0xAE2C, 0, 0, 0, 0x4348, 0x3045,
- 0x5A78, 0xAE2D, 0xC253, 0xC254, 0xC255, 0x5A79, 0, 0xC256,
- 0x7544, 0, 0x442A, 0, 0xC258, 0, 0x4E71, 0,
- 0, 0, 0, 0x3B43, 0, 0xAE2F, 0x4A6B, 0,
- 0, 0xAE30, 0x7545, 0, 0x4B3D, 0xAE31, 0, 0,
- 0x5B22, 0x5A7B, 0, 0x7546, 0x5A7E, 0, 0x5A7D, 0xAE33,
-};
-static const unsigned short utf8_to_euc_E69B[] = {
- 0xC25F, 0x5A7A, 0xC260, 0xC261, 0x5B21, 0, 0, 0x465E,
- 0xC262, 0x5A7C, 0, 0, 0xC263, 0, 0xC264, 0xC265,
- 0, 0, 0, 0, 0xC266, 0, 0x5B23, 0,
- 0, 0x3D6C, 0x5B24, 0xC267, 0x4D4B, 0x4778, 0, 0xC268,
- 0x5B25, 0, 0, 0, 0, 0, 0x5B27, 0,
- 0xC269, 0x5B28, 0, 0xC26A, 0xC26B, 0, 0xC26C, 0,
- 0x5B29, 0, 0x364A, 0x3148, 0x3939, 0x5B2A, 0, 0x5B2B,
- 0x3D71, 0x4162, 0xC26D, 0xC23F, 0x5258, 0x413E, 0x413D, 0x4258,
-};
-static const unsigned short utf8_to_euc_E69B_x0213[] = {
- 0xC25F, 0x5A7A, 0xC260, 0xC261, 0x5B21, 0, 0x7547, 0x465E,
- 0x7548, 0x5A7C, 0, 0, 0xC263, 0, 0xC264, 0xC265,
- 0, 0, 0, 0, 0xC266, 0, 0x5B23, 0,
- 0, 0x3D6C, 0x5B24, 0x754A, 0x4D4B, 0x4778, 0, 0xC268,
- 0x5B25, 0, 0, 0, 0, 0, 0x5B27, 0,
- 0x754B, 0x5B28, 0, 0xC26A, 0xAE35, 0, 0xC26C, 0,
- 0x5B29, 0, 0x364A, 0x3148, 0x3939, 0x5B2A, 0, 0x5B2B,
- 0x3D71, 0x4162, 0x754C, 0x7537, 0x5258, 0x413E, 0x413D, 0x4258,
-};
-static const unsigned short utf8_to_euc_E69C[] = {
- 0x3A47, 0, 0, 0x5072, 0, 0xC26E, 0, 0xC26F,
- 0x376E, 0x4D2D, 0, 0x4A7E, 0, 0x497E, 0xC270, 0x5B2C,
- 0, 0, 0, 0xC271, 0x3A73, 0x443F, 0x5B2D, 0x4F2F,
- 0, 0xC272, 0, 0x4B3E, 0xC273, 0x442B, 0x5B2E, 0x347C,
- 0xC274, 0, 0xC275, 0, 0, 0, 0x5B2F, 0x5B30,
- 0x4C5A, 0, 0x4C24, 0x4B76, 0x4B5C, 0x3B25, 0x5B32, 0,
- 0, 0x3C6B, 0, 0xC276, 0x4B51, 0, 0x5B34, 0x5B37,
- 0x5B36, 0, 0x3479, 0, 0, 0x3560, 0xC277, 0x5B33,
-};
-static const unsigned short utf8_to_euc_E69C_x0213[] = {
- 0x3A47, 0xAE37, 0, 0x5072, 0, 0xAE38, 0, 0xC26F,
- 0x376E, 0x4D2D, 0, 0x4A7E, 0, 0x497E, 0, 0x5B2C,
- 0, 0, 0xAE39, 0x754D, 0x3A73, 0x443F, 0x5B2D, 0x4F2F,
- 0, 0xAE3B, 0, 0x4B3E, 0xC273, 0x442B, 0x5B2E, 0x347C,
- 0xC274, 0, 0xC275, 0, 0, 0, 0x5B2F, 0x5B30,
- 0x4C5A, 0, 0x4C24, 0x4B76, 0x4B5C, 0x3B25, 0x5B32, 0,
- 0, 0x3C6B, 0, 0x754F, 0x4B51, 0, 0x5B34, 0x5B37,
- 0x5B36, 0, 0x3479, 0, 0, 0x3560, 0xC277, 0x5B33,
-};
-static const unsigned short utf8_to_euc_E69D[] = {
- 0, 0x5B35, 0, 0, 0, 0xC278, 0x5B38, 0xC279,
- 0xC27A, 0x3F79, 0, 0, 0xC27B, 0, 0x4D7B, 0x3049,
- 0x3A60, 0x423C, 0, 0x3C5D, 0xC27C, 0xC27D, 0x3E73, 0,
- 0, 0x5B3B, 0, 0, 0x454E, 0xC27E, 0x5B39, 0x422B,
- 0x5B3A, 0x3E72, 0x4C5D, 0x5B3C, 0x5B3D, 0x4D68, 0xC321, 0,
- 0, 0, 0x5B42, 0, 0xC322, 0x393A, 0xC323, 0x4755,
- 0x5B3F, 0x456C, 0x5A5E, 0x5A62, 0xC324, 0x354F, 0xC325, 0x4747,
- 0, 0, 0, 0xC326, 0x5B41, 0, 0x3E3E, 0x4844,
-};
-static const unsigned short utf8_to_euc_E69D_x0213[] = {
- 0, 0x5B35, 0, 0, 0, 0xC278, 0x5B38, 0x7551,
- 0x7552, 0x3F79, 0, 0, 0xAE3E, 0xAE3F, 0x4D7B, 0x3049,
- 0x3A60, 0x423C, 0, 0x3C5D, 0xAE40, 0xC27D, 0x3E73, 0,
- 0, 0x5B3B, 0, 0, 0x454E, 0xAE41, 0x5B39, 0x422B,
- 0x5B3A, 0x3E72, 0x4C5D, 0x5B3C, 0x5B3D, 0x4D68, 0x7550, 0,
- 0, 0, 0x5B42, 0, 0xC322, 0x393A, 0xC323, 0x4755,
- 0x5B3F, 0x456C, 0x5A5E, 0x5A62, 0xAE45, 0x354F, 0xAE46, 0x4747,
- 0, 0, 0, 0x7553, 0x5B41, 0, 0x3E3E, 0x4844,
-};
-static const unsigned short utf8_to_euc_E69E[] = {
- 0, 0xC327, 0, 0, 0xC328, 0x5B47, 0, 0x487A,
- 0, 0x5B3E, 0, 0x5B44, 0x5B43, 0, 0xC329, 0xC32A,
- 0x404F, 0xC32B, 0, 0xC32C, 0, 0x4B6D, 0xC32D, 0x4E53,
- 0xC32E, 0xC32F, 0x4B67, 0xC330, 0x324C, 0x3B5E, 0, 0,
- 0x4F48, 0x5B46, 0x3F75, 0, 0, 0, 0x5B45, 0,
- 0, 0x5B40, 0, 0, 0, 0, 0, 0x384F,
- 0xC331, 0xC332, 0xC333, 0x5B4C, 0x5B4A, 0xC334, 0x324D, 0x5B48,
- 0x5B4E, 0x5B54, 0, 0xC335, 0xC336, 0xC337, 0, 0,
-};
-static const unsigned short utf8_to_euc_E69E_x0213[] = {
- 0, 0x7554, 0, 0, 0xC328, 0x5B47, 0, 0x487A,
- 0, 0x5B3E, 0, 0x5B44, 0x5B43, 0, 0xC329, 0xC32A,
- 0x404F, 0xC32B, 0xAE48, 0x7555, 0, 0x4B6D, 0xC32D, 0x4E53,
- 0x7556, 0xC32F, 0x4B67, 0x7557, 0x324C, 0x3B5E, 0, 0,
- 0x4F48, 0x5B46, 0x3F75, 0, 0, 0, 0x5B45, 0,
- 0, 0x5B40, 0, 0, 0, 0, 0, 0x384F,
- 0xAE4C, 0xC332, 0xAE4D, 0x5B4C, 0x5B4A, 0xC334, 0x324D, 0x5B48,
- 0x5B4E, 0x5B54, 0, 0x7558, 0xC336, 0xC337, 0, 0,
-};
-static const unsigned short utf8_to_euc_E69F[] = {
- 0xC339, 0x4248, 0xC33A, 0xC33B, 0x4A41, 0xC33C, 0x5B56, 0,
- 0xC33D, 0xC33E, 0x4922, 0, 0, 0, 0x5B55, 0x4770,
- 0x4B3F, 0x343B, 0xC33F, 0x4077, 0x3D40, 0, 0, 0xC340,
- 0x4453, 0xC341, 0x4D2E, 0, 0xC342, 0x5B51, 0x5B50, 0,
- 0, 0xC343, 0x5B52, 0, 0x5B4F, 0, 0xC344, 0x5B57,
- 0, 0x5B4D, 0, 0, 0x5B4B, 0, 0x5B53, 0x5B49,
- 0xC345, 0x436C, 0xC346, 0x4C78, 0x3C46, 0x3A74, 0xC347, 0xC348,
- 0, 0xC338, 0, 0x3A3A, 0, 0, 0x4B6F, 0x3341,
-};
-static const unsigned short utf8_to_euc_E69F_x0213[] = {
- 0x755A, 0x4248, 0xC33A, 0xAE4E, 0x4A41, 0xC33C, 0x5B56, 0,
- 0xAE4F, 0xC33E, 0x4922, 0, 0, 0, 0x5B55, 0x4770,
- 0x4B3F, 0x343B, 0xAE50, 0x4077, 0x3D40, 0, 0, 0x755B,
- 0x4453, 0xAE51, 0x4D2E, 0xAE52, 0xC342, 0x5B51, 0x5B50, 0,
- 0, 0xC343, 0x5B52, 0, 0x5B4F, 0, 0xC344, 0x5B57,
- 0, 0x5B4D, 0, 0, 0x5B4B, 0, 0x5B53, 0x5B49,
- 0xAE53, 0x436C, 0xC346, 0x4C78, 0x3C46, 0x3A74, 0xC347, 0xAE54,
- 0, 0x7559, 0, 0x3A3A, 0x755C, 0, 0x4B6F, 0x3341,
-};
-static const unsigned short utf8_to_euc_E6A0[] = {
- 0, 0xF446, 0x444E, 0x464A, 0x3149, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x4072, 0xC34A, 0, 0x4034, 0x372A,
- 0, 0xC34B, 0, 0, 0, 0xC34C, 0x5B59, 0xC34D,
- 0, 0x393B, 0x337C, 0, 0, 0, 0, 0xC34F,
- 0xC34E, 0x5B5B, 0x3374, 0x5B61, 0xC350, 0xC351, 0, 0xC352,
- 0xC353, 0xC354, 0x5B5E, 0xC355, 0x4073, 0, 0, 0,
- 0x334B, 0x3A2C, 0, 0xC356, 0x334A, 0x3A4F, 0, 0xC357,
-};
-static const unsigned short utf8_to_euc_E6A0_x0213[] = {
- 0, 0x755D, 0x444E, 0x464A, 0x3149, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xAE4B, 0, 0, 0x4072, 0xC34A, 0, 0x4034, 0x372A,
- 0xAE58, 0xC34B, 0, 0, 0, 0x755F, 0x5B59, 0xAE59,
- 0, 0x393B, 0x337C, 0, 0, 0, 0, 0xC34F,
- 0xC34E, 0x5B5B, 0x3374, 0x5B61, 0x7560, 0xAE5A, 0, 0xC352,
- 0xC353, 0x7561, 0x5B5E, 0xAE5C, 0x4073, 0, 0, 0,
- 0x334B, 0x3A2C, 0, 0xAE5D, 0x334A, 0x3A4F, 0xAE5E, 0xC357,
-};
-static const unsigned short utf8_to_euc_E6A1[] = {
- 0x5B5C, 0x3765, 0x374B, 0x456D, 0xC358, 0xC359, 0x5B5A, 0,
- 0x3046, 0, 0xC35A, 0, 0xC35B, 0x5B5D, 0x5B5F, 0,
- 0x364D, 0x372C, 0xC349, 0x343C, 0x354B, 0xC35C, 0, 0xC35D,
- 0xC35E, 0x5B62, 0, 0xC35F, 0x3A79, 0x4B71, 0, 0x3B37,
- 0, 0, 0, 0x5B63, 0, 0, 0, 0x4930,
- 0, 0, 0, 0xC360, 0, 0, 0xC361, 0xC362,
- 0xC363, 0xC364, 0xC365, 0, 0x5B6F, 0xC366, 0x3233, 0x5B64,
- 0, 0xC367, 0xC368, 0xC369, 0xC36A, 0, 0x5B75, 0x5B65,
-};
-static const unsigned short utf8_to_euc_E6A1_x0213[] = {
- 0x5B5C, 0x3765, 0x374B, 0x456D, 0xAE5F, 0xAE60, 0x5B5A, 0,
- 0x3046, 0xAE61, 0xC35A, 0, 0xAE62, 0x5B5D, 0x5B5F, 0,
- 0x364D, 0x372C, 0x755E, 0x343C, 0x354B, 0xAE63, 0, 0xAE64,
- 0xC35E, 0x5B62, 0, 0x7562, 0x3A79, 0x4B71, 0, 0x3B37,
- 0, 0, 0, 0x5B63, 0, 0, 0, 0x4930,
- 0, 0, 0, 0xAE66, 0, 0, 0xAE67, 0xC362,
- 0xC363, 0xC364, 0x7563, 0, 0x5B6F, 0x7564, 0x3233, 0x5B64,
- 0, 0xC367, 0xAE68, 0xC369, 0xAE69, 0, 0x5B75, 0x5B65,
-};
-static const unsigned short utf8_to_euc_E6A2[] = {
- 0, 0x4E42, 0xC36B, 0x5B6C, 0xC36C, 0x475F, 0xC36D, 0,
- 0xC36E, 0, 0, 0, 0, 0x5B74, 0, 0x5B67,
- 0, 0, 0, 0x3034, 0x5B69, 0, 0xC36F, 0x393C,
- 0xC370, 0, 0xC371, 0x5B6B, 0xC372, 0x5B6A, 0, 0x5B66,
- 0x5B71, 0xC373, 0x3E3F, 0xC374, 0, 0xC375, 0x546D, 0x3868,
- 0x4D7C, 0xC376, 0xC377, 0, 0, 0x5B68, 0xC378, 0x4474,
- 0x3323, 0x3A2D, 0xC379, 0x5B60, 0, 0x5B70, 0x3361, 0,
- 0, 0x5B6E, 0x5B72, 0xC37A, 0x456E, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E6A2_x0213[] = {
- 0, 0x4E42, 0xAE6A, 0x5B6C, 0xC36C, 0x475F, 0xC36D, 0,
- 0xC36E, 0, 0, 0, 0, 0x5B74, 0, 0x5B67,
- 0xAE6B, 0, 0, 0x3034, 0x5B69, 0, 0xAE6C, 0x393C,
- 0xAE6E, 0xAE6F, 0xAE70, 0x5B6B, 0xAE71, 0x5B6A, 0, 0x5B66,
- 0x5B71, 0xC373, 0x3E3F, 0x7566, 0, 0x7567, 0x546D, 0x3868,
- 0x4D7C, 0xC376, 0xAE72, 0xAE73, 0, 0x5B68, 0xC378, 0x4474,
- 0x3323, 0x3A2D, 0x7568, 0x5B60, 0xAE74, 0x5B70, 0x3361, 0,
- 0, 0x5B6E, 0x5B72, 0xAE75, 0x456E, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E6A3[] = {
- 0, 0, 0, 0, 0x347E, 0xC37B, 0x5C32, 0,
- 0xC37C, 0x4C49, 0x5B77, 0x347D, 0xC37D, 0x5B7E, 0, 0xC37E,
- 0xC421, 0xC422, 0x4B40, 0xC423, 0x5C21, 0x5C23, 0xC424, 0x5C27,
- 0x5B79, 0xC425, 0x432A, 0, 0xC426, 0xC427, 0, 0x456F,
- 0x5C2B, 0x5B7C, 0, 0x5C28, 0, 0xC428, 0, 0x5C22,
- 0xC429, 0, 0xC42A, 0xC42B, 0xC42C, 0xC42D, 0x3F39, 0x5C2C,
- 0xC42E, 0xC42F, 0x4033, 0, 0, 0xC430, 0xC431, 0,
- 0, 0x5C2A, 0x343D, 0xC432, 0xC433, 0xC434, 0, 0,
-};
-static const unsigned short utf8_to_euc_E6A3_x0213[] = {
- 0, 0, 0, 0xAE7A, 0x347E, 0xAE7B, 0x5C32, 0,
- 0x7569, 0x4C49, 0x5B77, 0x347D, 0xAE7C, 0x5B7E, 0, 0xAE7D,
- 0x756A, 0xC422, 0x4B40, 0xC423, 0x5C21, 0x5C23, 0xAE7E, 0x5C27,
- 0x5B79, 0xAF21, 0x432A, 0, 0xC426, 0xC427, 0, 0x456F,
- 0x5C2B, 0x5B7C, 0, 0x5C28, 0xAF22, 0xAF23, 0, 0x5C22,
- 0x756B, 0, 0xC42A, 0xC42B, 0xAF24, 0x756C, 0x3F39, 0x5C2C,
- 0x756D, 0x756E, 0x4033, 0, 0, 0xC430, 0xC431, 0xAF25,
- 0, 0x5C2A, 0x343D, 0xAE76, 0x756F, 0xC434, 0, 0,
-};
-static const unsigned short utf8_to_euc_E6A4[] = {
- 0x4F50, 0x5B76, 0, 0, 0x5C26, 0x3058, 0xC435, 0,
- 0x5B78, 0xC436, 0xC437, 0x4C3A, 0x5B7D, 0x3F22, 0x4447, 0x5B73,
- 0xC438, 0xC439, 0x5C25, 0xC43A, 0, 0, 0xC43B, 0xC43C,
- 0, 0x3F7A, 0x5C2F, 0x3371, 0x3821, 0, 0, 0,
- 0, 0x5C31, 0x5B7A, 0x5C30, 0, 0x5C29, 0x5B7B, 0,
- 0x5C2D, 0, 0x5C2E, 0, 0, 0, 0, 0,
- 0x5C3F, 0xC43D, 0, 0xC43E, 0x464E, 0xC43F, 0x5C24, 0,
- 0xC440, 0x5C3B, 0, 0xC441, 0, 0x5C3D, 0, 0x4458,
-};
-static const unsigned short utf8_to_euc_E6A4_x0213[] = {
- 0x4F50, 0x5B76, 0, 0xAF26, 0x5C26, 0x3058, 0xC435, 0xAF27,
- 0x5B78, 0xC436, 0x7570, 0x4C3A, 0x5B7D, 0x3F22, 0x4447, 0x5B73,
- 0xC438, 0xC439, 0x5C25, 0xC43A, 0, 0, 0xC43B, 0xC43C,
- 0, 0x3F7A, 0x5C2F, 0x3371, 0x3821, 0, 0, 0,
- 0, 0x5C31, 0x5B7A, 0x5C30, 0, 0x5C29, 0x5B7B, 0,
- 0x5C2D, 0, 0x5C2E, 0, 0, 0, 0, 0,
- 0x5C3F, 0xC43D, 0, 0xC43E, 0x464E, 0x7573, 0x5C24, 0,
- 0xC440, 0x5C3B, 0, 0xAF2B, 0, 0x5C3D, 0, 0x4458,
-};
-static const unsigned short utf8_to_euc_E6A5[] = {
- 0, 0, 0xC442, 0, 0, 0xC443, 0, 0,
- 0, 0xC444, 0x4D4C, 0, 0, 0, 0xC445, 0,
- 0, 0, 0, 0x4976, 0x5C38, 0x424A, 0, 0xC446,
- 0, 0x5C3E, 0x413F, 0xC447, 0x5C35, 0x5C42, 0x5C41, 0,
- 0x466F, 0x5C40, 0x466A, 0xC448, 0xC449, 0xC44A, 0xC44B, 0,
- 0xC44C, 0xC44D, 0x5C44, 0x5C37, 0xC44E, 0x3648, 0x5C3A, 0x3D5D,
- 0xC44F, 0xC450, 0xC451, 0x4760, 0x5C3C, 0x364B, 0, 0x5C34,
- 0x5C36, 0x5C33, 0xC452, 0xC453, 0x4F30, 0x335A, 0x5C39, 0xC454,
-};
-static const unsigned short utf8_to_euc_E6A5_x0213[] = {
- 0, 0, 0x7574, 0, 0, 0xC443, 0xAF2D, 0,
- 0, 0x7571, 0x4D4C, 0, 0, 0, 0xC445, 0,
- 0, 0, 0, 0x4976, 0x5C38, 0x424A, 0, 0x7575,
- 0, 0x5C3E, 0x413F, 0xC447, 0x5C35, 0x5C42, 0x5C41, 0,
- 0x466F, 0x5C40, 0x466A, 0x7576, 0x7577, 0xC44A, 0xC44B, 0,
- 0x7578, 0xAF2E, 0x5C44, 0x5C37, 0xAF2F, 0x3648, 0x5C3A, 0x3D5D,
- 0xC44F, 0xC450, 0xAF30, 0x4760, 0x5C3C, 0x364B, 0, 0x5C34,
- 0x5C36, 0x5C33, 0xAF31, 0xC453, 0x4F30, 0x335A, 0x5C39, 0xAF32,
-};
-static const unsigned short utf8_to_euc_E6A6[] = {
- 0xC455, 0x5C43, 0x3335, 0, 0, 0, 0, 0,
- 0, 0, 0x3A67, 0, 0, 0xC456, 0x315D, 0,
- 0, 0x5C54, 0xC457, 0, 0x4F31, 0x5C57, 0xC458, 0,
- 0xC459, 0, 0, 0x3F3A, 0x5C56, 0, 0, 0,
- 0x5C55, 0xC45A, 0, 0, 0, 0xC45B, 0xC45C, 0x5C52,
- 0xC45D, 0, 0, 0xC45E, 0, 0xC45F, 0x5C46, 0xC460,
- 0, 0x5C63, 0x5C45, 0, 0x5C58, 0, 0, 0xC461,
- 0xC462, 0, 0xC463, 0x5C50, 0xC464, 0, 0x5C4B, 0x5C48,
-};
-static const unsigned short utf8_to_euc_E6A6_x0213[] = {
- 0x7579, 0x5C43, 0x3335, 0, 0, 0, 0, 0,
- 0, 0, 0x3A67, 0, 0, 0xC456, 0x315D, 0,
- 0, 0x5C54, 0xAF33, 0, 0x4F31, 0x5C57, 0xAF35, 0,
- 0xAF36, 0, 0, 0x3F3A, 0x5C56, 0, 0, 0,
- 0x5C55, 0xC45A, 0, 0, 0, 0x757B, 0xAF37, 0x5C52,
- 0xC45D, 0, 0, 0xC45E, 0, 0x757C, 0x5C46, 0xC460,
- 0xAF38, 0x5C63, 0x5C45, 0, 0x5C58, 0, 0, 0xAF39,
- 0xC462, 0, 0xAF3A, 0x5C50, 0xAF3B, 0, 0x5C4B, 0x5C48,
-};
-static const unsigned short utf8_to_euc_E6A7[] = {
- 0, 0x5C49, 0, 0x5C51, 0, 0xC465, 0, 0x7422,
- 0xC466, 0, 0x5C4E, 0x393D, 0x4448, 0x4164, 0x5C4C, 0,
- 0x5C47, 0xC467, 0, 0x5C4A, 0, 0, 0xC468, 0xC469,
- 0x4D4D, 0x4B6A, 0, 0, 0, 0x5C4F, 0x5C59, 0,
- 0, 0, 0xC46A, 0, 0, 0xC46B, 0, 0x5C61,
- 0x5C5A, 0, 0, 0x5C67, 0, 0x5C65, 0xC46C, 0xC46D,
- 0, 0xC46E, 0x5C60, 0xC46F, 0, 0xC470, 0, 0,
- 0, 0x5C5F, 0, 0x4450, 0, 0x4165, 0xC471, 0x5C5D,
-};
-static const unsigned short utf8_to_euc_E6A7_x0213[] = {
- 0xAF3C, 0x5C49, 0, 0x5C51, 0, 0xC465, 0, 0x7422,
- 0xC466, 0, 0x5C4E, 0x393D, 0x4448, 0x4164, 0x5C4C, 0x757D,
- 0x5C47, 0xAF3D, 0, 0x5C4A, 0, 0, 0xAF3E, 0xC469,
- 0x4D4D, 0x4B6A, 0, 0, 0, 0x5C4F, 0x5C59, 0,
- 0, 0, 0x7622, 0xAF44, 0, 0xC46B, 0, 0x5C61,
- 0x5C5A, 0x7623, 0x7624, 0x5C67, 0, 0x5C65, 0xAF45, 0xAF46,
- 0, 0xC46E, 0x5C60, 0xAF47, 0xAF49, 0x7625, 0x7626, 0,
- 0, 0x5C5F, 0, 0x4450, 0, 0x4165, 0xAF4A, 0x5C5D,
-};
-static const unsigned short utf8_to_euc_E6A8[] = {
- 0xC472, 0xC473, 0x5C5B, 0xC474, 0, 0x5C62, 0, 0,
- 0, 0, 0x5C68, 0x4875, 0x5C6E, 0, 0, 0xC475,
- 0, 0xC476, 0x5C69, 0x5C6C, 0x5C66, 0xC477, 0, 0x4374,
- 0, 0x4938, 0xC478, 0x5C5C, 0, 0xC479, 0x5C64, 0x3E40,
- 0xC47A, 0x4C4F, 0x5C78, 0x5C6B, 0xC47B, 0, 0, 0,
- 0xC47C, 0x3822, 0x3223, 0x335F, 0, 0, 0x5C53, 0,
- 0xC47D, 0, 0xC47E, 0, 0xC521, 0x3E41, 0x5C70, 0xC522,
- 0x5C77, 0x3C79, 0x3372, 0xC523, 0, 0x432E, 0xC524, 0xC525,
-};
-static const unsigned short utf8_to_euc_E6A8_x0213[] = {
- 0xC472, 0xC473, 0x5C5B, 0xC474, 0, 0x5C62, 0, 0,
- 0, 0, 0x5C68, 0x4875, 0x5C6E, 0, 0, 0x7627,
- 0, 0xAF4B, 0x5C69, 0x5C6C, 0x5C66, 0x7628, 0, 0x4374,
- 0, 0x4938, 0xAF4C, 0x5C5C, 0, 0xAF4D, 0x5C64, 0x3E40,
- 0xC47A, 0x4C4F, 0x5C78, 0x5C6B, 0xC47B, 0, 0, 0,
- 0xC47C, 0x3822, 0x3223, 0x335F, 0, 0, 0x5C53, 0,
- 0xAF41, 0, 0xAF4F, 0xAF50, 0xAF51, 0x3E41, 0x5C70, 0xC522,
- 0x5C77, 0x3C79, 0x3372, 0x762A, 0, 0x432E, 0x762B, 0xAF52,
-};
-static const unsigned short utf8_to_euc_E6A9[] = {
- 0, 0, 0, 0, 0x5C6D, 0xC526, 0xC527, 0x5C72,
- 0x5C76, 0xC528, 0xC529, 0x3636, 0, 0, 0xC52A, 0,
- 0xC52B, 0xC52C, 0xC52D, 0, 0, 0xC52E, 0xC52F, 0,
- 0x354C, 0x5C74, 0, 0xC530, 0, 0, 0, 0x3521,
- 0, 0x464B, 0x5C73, 0, 0xC531, 0, 0x5C75, 0xC532,
- 0, 0, 0xC533, 0xF449, 0, 0, 0, 0,
- 0, 0xC534, 0x5C6F, 0xC535, 0, 0, 0, 0,
- 0x5C71, 0, 0, 0, 0, 0, 0xC536, 0x3360,
-};
-static const unsigned short utf8_to_euc_E6A9_x0213[] = {
- 0, 0, 0, 0, 0x5C6D, 0x762C, 0xAF53, 0x5C72,
- 0x5C76, 0xAF54, 0xC529, 0x3636, 0, 0, 0xAF56, 0,
- 0x762D, 0xC52C, 0xAF57, 0, 0, 0xC52E, 0x762E, 0,
- 0x354C, 0x5C74, 0, 0x762F, 0, 0, 0, 0x3521,
- 0, 0x464B, 0x5C73, 0, 0xAF58, 0, 0x5C75, 0xC532,
- 0, 0, 0xC533, 0x7630, 0, 0, 0, 0,
- 0, 0xC534, 0x5C6F, 0x7631, 0, 0, 0, 0,
- 0x5C71, 0, 0xAF55, 0, 0, 0, 0xAF5A, 0x3360,
-};
-static const unsigned short utf8_to_euc_E6AA[] = {
- 0x4349, 0xC537, 0, 0xC538, 0x5C7C, 0, 0xC539, 0xC53A,
- 0, 0xC53B, 0, 0xC53C, 0, 0x5C7A, 0x3869, 0,
- 0x5C79, 0xC53D, 0, 0, 0, 0, 0, 0x5D21,
- 0, 0, 0, 0xC53E, 0x5B58, 0xC53F, 0xC540, 0xC541,
- 0x5C7B, 0, 0x5C7D, 0x5C7E, 0, 0xC542, 0, 0,
- 0, 0, 0x5D2C, 0xC543, 0x5D28, 0, 0x5B6D, 0xC544,
- 0xC545, 0xC546, 0, 0x5D27, 0xC547, 0, 0, 0,
- 0x5D26, 0, 0, 0x5D23, 0, 0xC548, 0xC549, 0xC54A,
-};
-static const unsigned short utf8_to_euc_E6AA_x0213[] = {
- 0x4349, 0xC537, 0, 0xAF5B, 0x5C7C, 0, 0xC539, 0xC53A,
- 0, 0x7633, 0, 0xAF5C, 0, 0x5C7A, 0x3869, 0,
- 0x5C79, 0xAF5E, 0, 0, 0x7634, 0, 0, 0x5D21,
- 0, 0, 0, 0xC53E, 0x5B58, 0x7635, 0x7636, 0xAF5F,
- 0x5C7B, 0xAF60, 0x5C7D, 0x5C7E, 0, 0x7637, 0, 0,
- 0, 0, 0x5D2C, 0xAF62, 0x5D28, 0, 0x5B6D, 0xC544,
- 0xC545, 0xC546, 0, 0x5D27, 0xC547, 0, 0, 0,
- 0x5D26, 0, 0, 0x5D23, 0, 0xAF63, 0xC549, 0xC54A,
-};
-static const unsigned short utf8_to_euc_E6AB[] = {
- 0, 0x5C6A, 0x5D25, 0x5D24, 0, 0, 0xC54B, 0,
- 0xC54D, 0xC54C, 0, 0, 0xC54E, 0, 0, 0,
- 0xC54F, 0x5D2A, 0, 0x4F26, 0xC550, 0xC551, 0xC552, 0,
- 0, 0, 0x5D2D, 0x367B, 0xC553, 0xC554, 0x5D29, 0x5D2B,
- 0, 0, 0xF44A, 0, 0xC555, 0, 0, 0xC556,
- 0x4827, 0, 0x5D2E, 0, 0xC557, 0, 0, 0,
- 0xC558, 0xC559, 0xC55A, 0, 0, 0, 0, 0,
- 0, 0, 0x5D32, 0x5D2F, 0xC55B, 0xC55C, 0, 0,
-};
-static const unsigned short utf8_to_euc_E6AB_x0213[] = {
- 0, 0x5C6A, 0x5D25, 0x5D24, 0, 0, 0xAF64, 0,
- 0xC54D, 0xC54C, 0, 0, 0xC54E, 0, 0, 0,
- 0xAF66, 0x5D2A, 0, 0x4F26, 0xAF65, 0xC551, 0xC552, 0,
- 0, 0, 0x5D2D, 0x367B, 0xAF67, 0xAF68, 0x5D29, 0x5D2B,
- 0, 0, 0, 0, 0x7638, 0, 0, 0x7639,
- 0x4827, 0, 0x5D2E, 0, 0xAF6B, 0, 0, 0,
- 0xC558, 0xAF6C, 0xAF6D, 0xAF6E, 0, 0, 0, 0,
- 0, 0, 0x5D32, 0x5D2F, 0xC55B, 0xAF6F, 0, 0,
-};
-static const unsigned short utf8_to_euc_E6AC[] = {
- 0, 0, 0xC55D, 0xC55E, 0x4D73, 0x5D30, 0xC55F, 0xC560,
- 0, 0xC561, 0x5C5E, 0, 0, 0, 0, 0xC562,
- 0xC563, 0xC564, 0x5D33, 0, 0, 0, 0x5D34, 0xC565,
- 0, 0, 0, 0xC566, 0, 0x3135, 0xC567, 0x5D36,
- 0x3767, 0x3C21, 0, 0x3655, 0xC568, 0, 0, 0x3224,
- 0xC569, 0, 0, 0xC56A, 0xC56B, 0, 0, 0xC56C,
- 0, 0, 0x4D5F, 0, 0, 0xC56D, 0xC56E, 0x5D38,
- 0x5D37, 0x5D3A, 0x353D, 0xC56F, 0, 0x3656, 0x343E, 0xC570,
-};
-static const unsigned short utf8_to_euc_E6AC_x0213[] = {
- 0, 0, 0xC55D, 0xC55E, 0x4D73, 0x5D30, 0xC55F, 0xC560,
- 0, 0xC561, 0x5C5E, 0xAF71, 0, 0, 0, 0xAF72,
- 0xAF73, 0xAF74, 0x5D33, 0, 0, 0, 0x5D34, 0xAF76,
- 0, 0, 0, 0x763C, 0, 0x3135, 0x763D, 0x5D36,
- 0x3767, 0x3C21, 0, 0x3655, 0xC568, 0, 0, 0x3224,
- 0xC569, 0, 0, 0xC56A, 0x763E, 0, 0, 0xAF78,
- 0, 0, 0x4D5F, 0, 0, 0x763F, 0xC56E, 0x5D38,
- 0x5D37, 0x5D3A, 0x353D, 0xC56F, 0, 0x3656, 0x343E, 0xC570,
-};
-static const unsigned short utf8_to_euc_E6AD[] = {
- 0, 0, 0, 0x5D3D, 0, 0, 0xC571, 0x5D3C,
- 0, 0x5D3E, 0xC572, 0, 0x324E, 0xC573, 0x4337, 0,
- 0x5D3F, 0, 0xC574, 0x343F, 0x5D41, 0, 0xC575, 0,
- 0xC576, 0x5D40, 0, 0x5D42, 0, 0xC577, 0, 0x5D43,
- 0xC578, 0x5D44, 0x3B5F, 0x4035, 0x3A21, 0, 0x4970, 0xC579,
- 0, 0x4A62, 0x4F44, 0xC57A, 0, 0, 0xC57B, 0x3B75,
- 0xC57C, 0, 0, 0x3A50, 0x4E72, 0xC57D, 0, 0,
- 0x5D45, 0x5D46, 0, 0x3B60, 0, 0xC57E, 0xC621, 0x5D47,
-};
-static const unsigned short utf8_to_euc_E6AD_x0213[] = {
- 0, 0, 0, 0x5D3D, 0, 0, 0x7640, 0x5D3C,
- 0, 0x5D3E, 0xAF79, 0, 0x324E, 0xC573, 0x4337, 0,
- 0x5D3F, 0, 0xC574, 0x343F, 0x5D41, 0, 0x7641, 0,
- 0xAF7A, 0x5D40, 0, 0x5D42, 0, 0xC577, 0, 0x5D43,
- 0x7642, 0x5D44, 0x3B5F, 0x4035, 0x3A21, 0x7643, 0x4970, 0x7644,
- 0, 0x4A62, 0x4F44, 0xC57A, 0xAF7B, 0, 0xC57B, 0x3B75,
- 0xC57C, 0, 0, 0x3A50, 0x4E72, 0xAF7C, 0, 0x7645,
- 0x5D45, 0x5D46, 0xAF7D, 0x3B60, 0, 0xC57E, 0xC621, 0x5D47,
-};
-static const unsigned short utf8_to_euc_E6AE[] = {
- 0x5D48, 0, 0xC622, 0x5D4A, 0x5D49, 0xC623, 0x4B58, 0,
- 0, 0x3D5E, 0x3C6C, 0x3B44, 0, 0x5D4B, 0, 0,
- 0, 0, 0, 0, 0, 0x5D4D, 0x3F23, 0xC624,
- 0x5D4C, 0, 0, 0xC625, 0, 0, 0x5D4E, 0xC626,
- 0xC627, 0, 0xC628, 0xC629, 0x5D4F, 0, 0, 0,
- 0xC62A, 0xC62B, 0x5D50, 0x5D51, 0xC62C, 0xC62D, 0xC62E, 0x5D52,
- 0xC62F, 0x5D54, 0x5D53, 0x5D55, 0x3225, 0x434A, 0, 0x5D56,
- 0xC630, 0xC631, 0x3B26, 0x334C, 0x5D57, 0xC632, 0xC633, 0x4542,
-};
-static const unsigned short utf8_to_euc_E6AE_x0213[] = {
- 0x5D48, 0xAF7E, 0x7646, 0x5D4A, 0x5D49, 0xC623, 0x4B58, 0,
- 0, 0x3D5E, 0x3C6C, 0x3B44, 0, 0x5D4B, 0, 0,
- 0, 0, 0, 0, 0, 0x5D4D, 0x3F23, 0xC624,
- 0x5D4C, 0, 0, 0xEE21, 0, 0, 0x5D4E, 0xC626,
- 0xC627, 0, 0xC628, 0xC629, 0x5D4F, 0, 0, 0,
- 0xC62A, 0x7647, 0x5D50, 0x5D51, 0xC62C, 0x7648, 0xEE22, 0x5D52,
- 0xC62F, 0x5D54, 0x5D53, 0x5D55, 0x3225, 0x434A, 0, 0x5D56,
- 0xC630, 0xC631, 0x3B26, 0x334C, 0x5D57, 0xEE24, 0xEE25, 0x4542,
-};
-static const unsigned short utf8_to_euc_E6AF[] = {
- 0x544C, 0, 0, 0xC634, 0xC635, 0x3523, 0x5D58, 0,
- 0, 0xC636, 0, 0x5D59, 0xC637, 0x4A6C, 0x4B68, 0,
- 0, 0, 0x4647, 0x5D5A, 0x4866, 0, 0xC638, 0,
- 0x487B, 0, 0xC639, 0x4C53, 0, 0, 0, 0x5D5B,
- 0, 0xC63A, 0, 0xC63B, 0, 0, 0xC63C, 0xC63D,
- 0, 0, 0, 0x5D5D, 0x5D5C, 0, 0xC63E, 0x5D5F,
- 0, 0xC63F, 0, 0x5D5E, 0, 0, 0, 0xC640,
- 0, 0xC641, 0, 0, 0, 0, 0, 0xC642,
-};
-static const unsigned short utf8_to_euc_E6AF_x0213[] = {
- 0x544C, 0, 0, 0xC634, 0xC635, 0x3523, 0x5D58, 0xEE26,
- 0xEE27, 0xEE28, 0, 0x5D59, 0xC637, 0x4A6C, 0x4B68, 0x764A,
- 0, 0, 0x4647, 0x5D5A, 0x4866, 0, 0x764B, 0x764C,
- 0x487B, 0, 0xEE29, 0x4C53, 0, 0, 0, 0x5D5B,
- 0, 0xC63A, 0, 0xC63B, 0, 0, 0xEE2A, 0xEE2B,
- 0, 0, 0, 0x5D5D, 0x5D5C, 0, 0xEE2C, 0x5D5F,
- 0, 0xEE2D, 0, 0x5D5E, 0, 0, 0, 0xC640,
- 0, 0xC641, 0, 0, 0, 0, 0, 0x764D,
-};
-static const unsigned short utf8_to_euc_E6B0[] = {
- 0, 0, 0xC643, 0, 0xC644, 0xC645, 0, 0,
- 0x5D61, 0xC646, 0, 0, 0, 0xC647, 0xC648, 0x3B61,
- 0xC649, 0x4C31, 0xC64A, 0x5D62, 0x5D63, 0, 0, 0x3524,
- 0, 0xC64B, 0, 0x5D64, 0, 0, 0, 0xC64C,
- 0, 0, 0, 0x5D66, 0x5D65, 0, 0xC64D, 0xC64E,
- 0xC64F, 0, 0, 0, 0xC650, 0, 0xC651, 0,
- 0, 0, 0, 0xC652, 0x3F65, 0xC653, 0xC654, 0x4939,
- 0x314A, 0, 0xC655, 0xC656, 0, 0, 0x4845, 0xC657,
-};
-static const unsigned short utf8_to_euc_E6B0_x0213[] = {
- 0, 0, 0xEE2E, 0, 0xC644, 0x764E, 0, 0,
- 0x5D61, 0xC646, 0xEE2F, 0, 0, 0xC647, 0xEE30, 0x3B61,
- 0x764F, 0x4C31, 0xC64A, 0x5D62, 0x5D63, 0, 0, 0x3524,
- 0, 0xC64B, 0, 0x5D64, 0, 0, 0, 0xC64C,
- 0, 0, 0, 0x5D66, 0x5D65, 0, 0xC64D, 0xC64E,
- 0xC64F, 0, 0, 0, 0xC650, 0, 0xC651, 0,
- 0, 0, 0, 0x7650, 0x3F65, 0xEE31, 0xEE32, 0x4939,
- 0x314A, 0, 0xEE33, 0xC656, 0, 0, 0x4845, 0xEE35,
-};
-static const unsigned short utf8_to_euc_E6B1[] = {
- 0x4475, 0x3D41, 0x3561, 0, 0, 0, 0, 0,
- 0, 0, 0xC658, 0xC659, 0, 0xC65A, 0x4846, 0xC65B,
- 0x3C2E, 0, 0xC65C, 0, 0xC65D, 0x5D68, 0, 0x3440,
- 0, 0xC65E, 0x3178, 0xC65F, 0xC660, 0x4672, 0x5D67, 0x393E,
- 0x4353, 0, 0x5D69, 0, 0, 0, 0, 0xC736,
- 0x5D71, 0, 0x5D6A, 0xC661, 0, 0xC662, 0, 0xC663,
- 0x4241, 0, 0x3562, 0x5D72, 0xC664, 0, 0xC665, 0,
- 0xC666, 0xC667, 0x3768, 0xC668, 0, 0x3525, 0x5D70, 0,
-};
-static const unsigned short utf8_to_euc_E6B1_x0213[] = {
- 0x4475, 0x3D41, 0x3561, 0, 0, 0, 0, 0,
- 0, 0, 0xC658, 0xC659, 0, 0xEE36, 0x4846, 0xC65B,
- 0x3C2E, 0, 0xC65C, 0, 0xC65D, 0x5D68, 0, 0x3440,
- 0, 0x7651, 0x3178, 0xEE37, 0x7652, 0x4672, 0x5D67, 0x393E,
- 0x4353, 0, 0x5D69, 0, 0, 0, 0, 0xEE4F,
- 0x5D71, 0, 0x5D6A, 0xC661, 0, 0xEE38, 0, 0,
- 0x4241, 0, 0x3562, 0x5D72, 0x7654, 0, 0x7655, 0,
- 0xC666, 0xC667, 0x3768, 0xC668, 0, 0x3525, 0x5D70, 0,
-};
-static const unsigned short utf8_to_euc_E6B2[] = {
- 0, 0x5D6E, 0x5D6B, 0x4D60, 0, 0xC669, 0xC66A, 0xC66B,
- 0x4440, 0xC66C, 0, 0, 0x4659, 0x5D6C, 0, 0,
- 0x5D74, 0, 0x5D73, 0x3723, 0xC66D, 0xC66E, 0x322D, 0xC66F,
- 0xC670, 0x3A3B, 0x5D6D, 0x5D6F, 0xC671, 0, 0, 0xC672,
- 0, 0x4B57, 0x4274, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x4B77, 0, 0, 0x5D7C, 0,
- 0xC673, 0x5D7D, 0xC674, 0x324F, 0xC675, 0, 0, 0,
- 0x4A28, 0x4C7D, 0x5E21, 0x3C23, 0x3E42, 0x5D78, 0x5D7E, 0x3168,
-};
-static const unsigned short utf8_to_euc_E6B2_x0213[] = {
- 0, 0x5D6E, 0x5D6B, 0x4D60, 0xEE39, 0x7656, 0x7657, 0xC66B,
- 0x4440, 0xEE3A, 0, 0, 0x4659, 0x5D6C, 0, 0,
- 0x5D74, 0, 0x5D73, 0x3723, 0xEE3C, 0xEE3D, 0x322D, 0xEE3E,
- 0x7658, 0x3A3B, 0x5D6D, 0x5D6F, 0x7659, 0, 0, 0xC672,
- 0, 0x4B57, 0x4274, 0, 0, 0, 0, 0,
- 0, 0, 0x7653, 0x4B77, 0, 0xEE3F, 0x5D7C, 0,
- 0xC673, 0x5D7D, 0xC674, 0x324F, 0xC675, 0, 0, 0,
- 0x4A28, 0x4C7D, 0x5E21, 0x3C23, 0x3E42, 0x5D78, 0x5D7E, 0x3168,
-};
-static const unsigned short utf8_to_euc_E6B3[] = {
- 0, 0x3637, 0xC676, 0, 0x5D75, 0x5D7A, 0xC677, 0,
- 0, 0x4074, 0x4771, 0, 0x4867, 0xC678, 0, 0xC679,
- 0xC67A, 0xC67B, 0xC67C, 0x5D77, 0xC67D, 0x4B21, 0xC67E, 0x5D79,
- 0, 0x5E24, 0xC721, 0x5E22, 0xC722, 0x5D7B, 0, 0,
- 0xC723, 0x4B22, 0x4748, 0x3563, 0, 0x4525, 0, 0xC724,
- 0x436D, 0xC725, 0x5E25, 0xC726, 0xC727, 0, 0xC728, 0x5E23,
- 0x4259, 0x5D76, 0xC729, 0x314B, 0xC72A, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E6B3_x0213[] = {
- 0, 0x3637, 0xEE40, 0, 0x5D75, 0x5D7A, 0x765B, 0,
- 0, 0x4074, 0x4771, 0, 0x4867, 0xC678, 0, 0xC679,
- 0xEE41, 0xC67B, 0xC67C, 0x5D77, 0x765C, 0x4B21, 0xEE43, 0x5D79,
- 0, 0x5E24, 0xEE44, 0x5E22, 0xEE45, 0x5D7B, 0, 0,
- 0x765D, 0x4B22, 0x4748, 0x3563, 0, 0x4525, 0, 0xC724,
- 0x436D, 0xEE46, 0x5E25, 0x765E, 0xEE47, 0xEE48, 0x765F, 0x5E23,
- 0x4259, 0x5D76, 0xC729, 0x314B, 0xC72A, 0, 0, 0,
- 0, 0, 0, 0x765A, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E6B4[] = {
- 0, 0, 0, 0, 0xC72B, 0, 0, 0xC72C,
- 0, 0, 0xC72D, 0x4D4E, 0x5E30, 0, 0xC72E, 0xC72F,
- 0, 0xC730, 0x5E2F, 0xC731, 0, 0, 0, 0x4076,
- 0, 0x5E2C, 0xC732, 0x4D6C, 0, 0, 0x4636, 0x5E26,
- 0, 0, 0, 0, 0, 0x4445, 0xC733, 0xC734,
- 0xC735, 0x314C, 0x393F, 0x5E29, 0, 0, 0xC737, 0xC738,
- 0, 0xC739, 0x3D27, 0x5E2E, 0, 0x5E2D, 0x5E28, 0,
- 0x5E2B, 0xC73A, 0, 0x3368, 0xC73B, 0x5E2A, 0x4749, 0xC73C,
-};
-static const unsigned short utf8_to_euc_E6B4_x0213[] = {
- 0xEE4A, 0, 0, 0, 0x7661, 0, 0, 0xC72C,
- 0, 0, 0xEE4B, 0x4D4E, 0x5E30, 0, 0x7662, 0xC72F,
- 0, 0xC730, 0x5E2F, 0xC731, 0, 0, 0, 0x4076,
- 0, 0x5E2C, 0xC732, 0x4D6C, 0, 0, 0x4636, 0x5E26,
- 0, 0, 0, 0, 0xEE4C, 0x4445, 0xEE4D, 0xEE4E,
- 0xC735, 0x314C, 0x393F, 0x5E29, 0, 0, 0x7663, 0xEE50,
- 0, 0x7664, 0x3D27, 0x5E2E, 0xEE65, 0x5E2D, 0x5E28, 0,
- 0x5E2B, 0x7665, 0, 0x3368, 0xEE51, 0x5E2A, 0x4749, 0x7666,
-};
-static const unsigned short utf8_to_euc_E6B5[] = {
- 0, 0x4E2E, 0, 0, 0x3E74, 0x4075, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xC73D,
- 0, 0x5E36, 0x5E34, 0, 0x494D, 0, 0xC73E, 0xC73F,
- 0, 0xC740, 0, 0x5E31, 0x5E33, 0xC741, 0x313A, 0xC742,
- 0, 0x3940, 0x4F32, 0, 0x333D, 0, 0x4962, 0xC743,
- 0xC744, 0, 0, 0, 0x4D61, 0, 0, 0x3324,
- 0x3F3B, 0x5E35, 0, 0, 0xC745, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E6B5_x0213[] = {
- 0, 0x4E2E, 0, 0, 0x3E74, 0x4075, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xC73D,
- 0x7667, 0x5E36, 0x5E34, 0xEE52, 0x494D, 0, 0xEE53, 0xC73F,
- 0xEE54, 0xC740, 0, 0x5E31, 0x5E33, 0x7668, 0x313A, 0xC742,
- 0, 0x3940, 0x4F32, 0, 0x333D, 0, 0x4962, 0,
- 0xEE55, 0, 0, 0, 0x4D61, 0, 0, 0x3324,
- 0x3F3B, 0x5E35, 0, 0, 0xC745, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E6B6[] = {
- 0, 0, 0xC746, 0, 0, 0x5E3A, 0, 0xC747,
- 0x3E43, 0, 0, 0, 0x4D30, 0, 0x5E37, 0,
- 0, 0xC748, 0xC749, 0x5E32, 0xC74A, 0x5E38, 0xC74B, 0xC74C,
- 0xC74D, 0x4E5E, 0, 0x4573, 0x4642, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xC74E, 0, 0xC74F, 0, 0, 0x3336,
- 0, 0, 0x3155, 0, 0xC750, 0x5E3E, 0, 0xC751,
- 0x5E41, 0xC752, 0, 0, 0x4E43, 0xC753, 0, 0xC754,
-};
-static const unsigned short utf8_to_euc_E6B6_x0213[] = {
- 0xEE56, 0xEE57, 0x766A, 0, 0, 0x5E3A, 0, 0x766B,
- 0x3E43, 0x766C, 0xEE58, 0, 0x4D30, 0xEE59, 0x5E37, 0,
- 0, 0xEE5A, 0xC749, 0x5E32, 0x766D, 0x5E38, 0, 0xC74C,
- 0xEE5B, 0x4E5E, 0, 0x4573, 0x4642, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x766E, 0xEE61, 0x766F, 0, 0xEE62, 0x3336,
- 0, 0, 0x3155, 0, 0xEE63, 0x5E3E, 0, 0xC751,
- 0x5E41, 0xC752, 0, 0, 0x4E43, 0xC753, 0, 0x7670,
-};
-static const unsigned short utf8_to_euc_E6B7[] = {
- 0x4D64, 0, 0, 0, 0xC755, 0x5E48, 0x5E42, 0x5E3F,
- 0xC756, 0, 0xC757, 0x4E54, 0x5E45, 0, 0xC758, 0xC759,
- 0, 0x3D4A, 0x5E47, 0, 0, 0x5E4C, 0xC75A, 0,
- 0x4571, 0x5E4A, 0, 0xC75B, 0, 0xC75C, 0x5E44, 0xC75D,
- 0xC75E, 0x4338, 0xC75F, 0, 0x5E4B, 0xC760, 0x5E40, 0,
- 0x5E46, 0xC761, 0x5E4D, 0x307C, 0x5E43, 0, 0x5E4E, 0xC762,
- 0xC763, 0x3F3C, 0xF44C, 0x3D5F, 0xC764, 0x4A25, 0xC765, 0x3A2E,
- 0xF44B, 0x5E3B, 0x5E49, 0x453A, 0xC766, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E6B7_x0213[] = {
- 0x4D64, 0, 0xEE64, 0, 0x7671, 0x5E48, 0x5E42, 0x5E3F,
- 0xEE66, 0, 0xC757, 0x4E54, 0x5E45, 0, 0xEE67, 0xEE68,
- 0xEE69, 0x3D4A, 0x5E47, 0, 0, 0x5E4C, 0x7672, 0,
- 0x4571, 0x5E4A, 0x7673, 0x7674, 0, 0x7675, 0x5E44, 0xEE6A,
- 0xC75E, 0x4338, 0xC75F, 0, 0x5E4B, 0xC760, 0x5E40, 0,
- 0x5E46, 0xEE6B, 0x5E4D, 0x307C, 0x5E43, 0, 0x5E4E, 0xC762,
- 0xC763, 0x3F3C, 0, 0x3D5F, 0xC764, 0x4A25, 0xEE6C, 0x3A2E,
- 0, 0x5E3B, 0x5E49, 0x453A, 0x7676, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E6B8[] = {
- 0xC767, 0, 0, 0, 0xC768, 0x4036, 0, 0x3369,
- 0x3A51, 0x3E44, 0x5E3D, 0x3D42, 0, 0, 0, 0,
- 0, 0, 0, 0x374C, 0, 0x5E3C, 0, 0,
- 0, 0x5E52, 0x3D6D, 0x383A, 0, 0x5E61, 0xC769, 0x5E5B,
- 0x3574, 0x454F, 0xC76A, 0x5E56, 0x5E5F, 0x302F, 0x3132, 0xC76B,
- 0, 0x3239, 0, 0x5E58, 0x422C, 0x5E4F, 0x5E51, 0x3941,
- 0, 0, 0xC76C, 0, 0, 0, 0xC76D, 0,
- 0x5E62, 0xC76E, 0x5E5D, 0xC76F, 0xC770, 0, 0x5E55, 0,
-};
-static const unsigned short utf8_to_euc_E6B8_x0213[] = {
- 0xC767, 0, 0, 0, 0xC768, 0x4036, 0, 0x3369,
- 0x3A51, 0x3E44, 0x5E3D, 0x3D42, 0, 0, 0, 0,
- 0, 0, 0, 0x374C, 0, 0x5E3C, 0, 0xEE5D,
- 0, 0x5E52, 0x3D6D, 0x383A, 0, 0x5E61, 0xEE6E, 0x5E5B,
- 0x3574, 0x454F, 0xEE6F, 0x5E56, 0x5E5F, 0x302F, 0x3132, 0xEE70,
- 0, 0x3239, 0, 0x5E58, 0x422C, 0x5E4F, 0x5E51, 0x3941,
- 0, 0, 0xEE72, 0, 0x7678, 0, 0xEE6D, 0,
- 0x5E62, 0, 0x5E5D, 0xC76F, 0xEE73, 0, 0x5E55, 0,
-};
-static const unsigned short utf8_to_euc_E6B9[] = {
- 0, 0, 0, 0x5E5C, 0xC771, 0xC772, 0, 0,
- 0xC773, 0xC774, 0x4C2B, 0xC775, 0, 0x5E5A, 0x5E5E, 0xC776,
- 0, 0xC777, 0xC778, 0xC779, 0xC77A, 0, 0x3850, 0xC77B,
- 0x3E45, 0, 0, 0x4339, 0xC77C, 0xC77D, 0xC77E, 0x5E54,
- 0, 0, 0xC821, 0xC822, 0, 0, 0, 0x4D2F,
- 0xC823, 0, 0, 0x5E57, 0, 0, 0x5E50, 0x4572,
- 0, 0, 0x5E53, 0xC824, 0, 0, 0x5E59, 0,
- 0, 0, 0, 0xC825, 0, 0xC826, 0x4F51, 0x3C3E,
-};
-static const unsigned short utf8_to_euc_E6B9_x0213[] = {
- 0, 0, 0, 0x5E5C, 0x7679, 0xC772, 0, 0,
- 0xEE74, 0xEE75, 0x4C2B, 0xEE76, 0xEE77, 0x5E5A, 0x5E5E, 0xEE78,
- 0, 0xEE79, 0xC778, 0xEE7A, 0xEE7B, 0, 0x3850, 0xEE7C,
- 0x3E45, 0, 0, 0x4339, 0x767A, 0xC77D, 0x767B, 0x5E54,
- 0, 0, 0xC821, 0xEE7D, 0, 0, 0, 0x4D2F,
- 0xC823, 0, 0, 0x5E57, 0, 0, 0x5E50, 0x4572,
- 0, 0, 0x5E53, 0xC824, 0, 0, 0x5E59, 0,
- 0, 0, 0, 0xC825, 0, 0xC826, 0x4F51, 0x3C3E,
-};
-static const unsigned short utf8_to_euc_E6BA[] = {
- 0x4B7E, 0, 0x5E63, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0x482E, 0xC827, 0, 0x5E6F,
- 0x383B, 0, 0, 0xC828, 0, 0, 0x3D60, 0,
- 0x5E65, 0xC829, 0, 0, 0x4E2F, 0x3942, 0, 0x5E72,
- 0xC82A, 0, 0x306E, 0, 0, 0x5E70, 0, 0xC82B,
- 0, 0, 0x5E64, 0, 0, 0xC82C, 0xC82D, 0x5E6A,
- 0, 0xC82E, 0x5E6C, 0xC82F, 0, 0, 0x4D4F, 0x5E67,
- 0, 0, 0x452E, 0xC830, 0, 0x5E69, 0, 0xC831,
-};
-static const unsigned short utf8_to_euc_E6BA_x0213[] = {
- 0x4B7E, 0, 0x5E63, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0x482E, 0xC827, 0, 0x5E6F,
- 0x383B, 0, 0, 0xEF21, 0, 0, 0x3D60, 0,
- 0x5E65, 0xC829, 0, 0, 0x4E2F, 0x3942, 0, 0x5E72,
- 0xC82A, 0, 0x306E, 0, 0, 0x5E70, 0, 0xEF22,
- 0, 0, 0x5E64, 0x767C, 0, 0xC82C, 0xC82D, 0x5E6A,
- 0, 0x767D, 0x5E6C, 0xC82F, 0xEF23, 0, 0x4D4F, 0x5E67,
- 0, 0, 0x452E, 0xC830, 0, 0x5E69, 0, 0xEF24,
-};
-static const unsigned short utf8_to_euc_E6BB[] = {
- 0xC832, 0xC833, 0x5E71, 0xC834, 0x5E6B, 0x4C47, 0, 0xC835,
- 0xC836, 0x5E66, 0xC837, 0x3C22, 0x5E7E, 0xC838, 0xC839, 0xC83A,
- 0, 0x336A, 0, 0x5E68, 0x5E6D, 0x5E6E, 0, 0,
- 0, 0, 0, 0, 0, 0x426C, 0x425A, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xC83B, 0x5E76, 0xC83C, 0xC83D, 0x5E7C,
- 0, 0, 0x5E7A, 0, 0x4529, 0, 0, 0x5F23,
- 0x5E77, 0xC83E, 0, 0xC83F, 0, 0xC840, 0x5E78, 0x5E60,
-};
-static const unsigned short utf8_to_euc_E6BB_x0213[] = {
- 0xC832, 0x767E, 0x5E71, 0xEF25, 0x5E6B, 0x4C47, 0, 0x7721,
- 0xC836, 0x5E66, 0xEF26, 0x3C22, 0x5E7E, 0xC838, 0x7722, 0xC83A,
- 0, 0x336A, 0, 0x5E68, 0x5E6D, 0x5E6E, 0, 0,
- 0, 0xEF27, 0, 0, 0, 0x426C, 0x425A, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xEF29, 0x5E76, 0xC83C, 0xC83D, 0x5E7C,
- 0, 0, 0x5E7A, 0, 0x4529, 0, 0, 0x5F23,
- 0x5E77, 0xEF2A, 0, 0xEF2B, 0, 0xC840, 0x5E78, 0x5E60,
-};
-static const unsigned short utf8_to_euc_E6BC[] = {
- 0, 0x3579, 0x493A, 0, 0xC841, 0, 0x3C3F, 0,
- 0xC842, 0x3977, 0xC843, 0, 0xC844, 0xC845, 0, 0x4F33,
- 0, 0x5E74, 0, 0x5F22, 0x3169, 0x4166, 0xC846, 0,
- 0xC847, 0, 0xC848, 0xC849, 0, 0, 0, 0,
- 0x4779, 0, 0x3441, 0x4E7A, 0, 0, 0xC84A, 0,
- 0, 0xC84B, 0xC84C, 0x4C21, 0x4452, 0xC853, 0, 0xC84D,
- 0xC84E, 0x5E7B, 0x5E7D, 0xC84F, 0, 0, 0xC850, 0,
- 0x4132, 0, 0, 0xC851, 0xC852, 0, 0x5F21, 0x5E79,
-};
-static const unsigned short utf8_to_euc_E6BC_x0213[] = {
- 0, 0x3579, 0x493A, 0, 0xC841, 0, 0x3C3F, 0,
- 0xC842, 0x3977, 0xEF2C, 0, 0xEF2D, 0xC845, 0, 0x4F33,
- 0x7723, 0x5E74, 0, 0x5F22, 0x3169, 0x4166, 0xC846, 0,
- 0xEF2E, 0, 0x7724, 0xC849, 0, 0, 0, 0,
- 0x4779, 0, 0x3441, 0x4E7A, 0, 0xEF2F, 0xC84A, 0,
- 0, 0xC84B, 0x7726, 0x4C21, 0x4452, 0xC853, 0, 0x7727,
- 0xC84E, 0x5E7B, 0x5E7D, 0x7728, 0, 0xEF28, 0xEF30, 0,
- 0x4132, 0, 0, 0xC851, 0xEF31, 0, 0x5F21, 0x5E79,
-};
-static const unsigned short utf8_to_euc_E6BD[] = {
- 0, 0x5E73, 0, 0, 0, 0x3443, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xC854,
- 0, 0xC855, 0xC856, 0xC857, 0x3769, 0, 0, 0xC858,
- 0x5F2F, 0xC859, 0xC85A, 0x5F2A, 0x4078, 0xC85B, 0xC85C, 0x3363,
- 0, 0xC85D, 0xC85E, 0, 0x3D61, 0, 0x5F33, 0,
- 0xC85F, 0, 0, 0, 0xC860, 0x5F2C, 0x442C, 0x5F29,
- 0x4459, 0, 0, 0, 0x5F4C, 0, 0, 0,
- 0x5F26, 0, 0x5F25, 0, 0x5F2E, 0xC861, 0xC862, 0,
-};
-static const unsigned short utf8_to_euc_E6BD_x0213[] = {
- 0, 0x5E73, 0, 0, 0, 0x3443, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xC854,
- 0, 0x7729, 0xEF33, 0xC857, 0x3769, 0, 0, 0xEF34,
- 0x5F2F, 0x772A, 0xEF35, 0x5F2A, 0x4078, 0xC85B, 0x772B, 0x3363,
- 0xEF36, 0x772C, 0x772D, 0, 0x3D61, 0, 0x5F33, 0,
- 0xEF37, 0, 0, 0, 0xC860, 0x5F2C, 0x442C, 0x5F29,
- 0x4459, 0, 0, 0, 0x5F4C, 0, 0, 0,
- 0x5F26, 0, 0x5F25, 0, 0x5F2E, 0xEF39, 0x772E, 0,
-};
-static const unsigned short utf8_to_euc_E6BE[] = {
- 0x5F28, 0x5F27, 0x5F2D, 0xC863, 0x4021, 0, 0x5F24, 0xC864,
- 0xC865, 0, 0, 0xC866, 0xC867, 0xC868, 0x5F30, 0,
- 0xC869, 0x5F31, 0xC86A, 0xC86B, 0xC86C, 0, 0xC86D, 0x3442,
- 0, 0, 0xC86E, 0, 0, 0, 0, 0xC86F,
- 0xC870, 0x5F36, 0, 0x5F35, 0x5F37, 0xC871, 0xC872, 0xC873,
- 0xC874, 0, 0x5F3A, 0, 0, 0, 0xC875, 0xC876,
- 0xC877, 0x4543, 0, 0x5F34, 0, 0xC878, 0xC879, 0,
- 0, 0x5F38, 0, 0, 0xC87A, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E6BE_x0213[] = {
- 0x5F28, 0x5F27, 0x5F2D, 0xC863, 0x4021, 0, 0x5F24, 0xC864,
- 0x772F, 0, 0, 0xC866, 0x7730, 0x7731, 0x5F30, 0,
- 0xEF3A, 0x5F31, 0xC86A, 0xC86B, 0x7732, 0, 0xEF3B, 0x3442,
- 0xEF38, 0, 0xC86E, 0, 0, 0, 0, 0xEF3D,
- 0x7733, 0x5F36, 0, 0x5F35, 0x5F37, 0xEF3E, 0xC872, 0x7734,
- 0xC874, 0, 0x5F3A, 0, 0, 0, 0xC875, 0xEF3F,
- 0xC877, 0x4543, 0, 0x5F34, 0, 0xEF41, 0x7735, 0,
- 0, 0x5F38, 0, 0, 0x7736, 0, 0xEF3C, 0,
-};
-static const unsigned short utf8_to_euc_E6BF[] = {
- 0x3763, 0x4279, 0x5F32, 0x473B, 0, 0xC87B, 0x5F39, 0xC87C,
- 0xC87D, 0, 0xC87E, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0x5F3E, 0x5F3C, 0, 0,
- 0x5F3F, 0, 0xC921, 0x5F42, 0, 0, 0xC922, 0x5F3B,
- 0x396A, 0x4728, 0, 0, 0x5E39, 0, 0, 0,
- 0xC923, 0xC924, 0, 0x4D74, 0x5F3D, 0, 0x5F41, 0x4275,
- 0xC925, 0x5F40, 0, 0x5F2B, 0, 0xC926, 0x6F69, 0,
- 0, 0xC927, 0x5F45, 0, 0xC928, 0xC929, 0x5F49, 0,
-};
-static const unsigned short utf8_to_euc_E6BF_x0213[] = {
- 0x3763, 0x4279, 0x5F32, 0x473B, 0, 0xC87B, 0x5F39, 0x7737,
- 0xEF42, 0xEF43, 0x7738, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0x5F3E, 0x5F3C, 0, 0,
- 0x5F3F, 0, 0xEF44, 0x5F42, 0, 0, 0xEF45, 0x5F3B,
- 0x396A, 0x4728, 0, 0, 0x5E39, 0, 0, 0,
- 0xC923, 0xEF46, 0, 0x4D74, 0x5F3D, 0, 0x5F41, 0x4275,
- 0x773A, 0x5F40, 0, 0x5F2B, 0, 0x773B, 0x6F69, 0,
- 0, 0x7739, 0x5F45, 0, 0xEF48, 0xC929, 0x5F49, 0,
-};
-static const unsigned short utf8_to_euc_E780[] = {
- 0xC92A, 0x5F47, 0, 0, 0, 0xC92B, 0xC92C, 0xC92D,
- 0, 0x5F43, 0, 0x5F44, 0, 0xC92E, 0, 0x5F48,
- 0, 0x5F46, 0, 0, 0, 0x494E, 0, 0xC92F,
- 0x5F4E, 0, 0x5F4B, 0x5F4A, 0, 0x5F4D, 0x4654, 0x5F4F,
- 0xC930, 0, 0, 0xC931, 0, 0, 0x4375, 0x426D,
- 0xF44D, 0, 0, 0, 0x4025, 0, 0, 0xC932,
- 0x5F50, 0, 0x5F52, 0, 0xC933, 0, 0, 0xC934,
- 0, 0xC935, 0, 0, 0xC936, 0, 0x5F51, 0,
-};
-static const unsigned short utf8_to_euc_E780_x0213[] = {
- 0xEF49, 0x5F47, 0, 0, 0, 0x773C, 0x773D, 0xEF4A,
- 0, 0x5F43, 0xEF4B, 0x5F44, 0, 0xC92E, 0, 0x5F48,
- 0, 0x5F46, 0, 0, 0, 0x494E, 0, 0xC92F,
- 0x5F4E, 0, 0x5F4B, 0x5F4A, 0, 0x5F4D, 0x4654, 0x5F4F,
- 0xC930, 0, 0, 0xEF4C, 0, 0, 0x4375, 0x426D,
- 0x773E, 0, 0, 0, 0x4025, 0, 0, 0xC932,
- 0x5F50, 0, 0x5F52, 0, 0xC933, 0, 0, 0xC934,
- 0, 0xEF4E, 0xEF4F, 0, 0xEF50, 0, 0x5F51, 0,
-};
-static const unsigned short utf8_to_euc_E781[] = {
- 0, 0, 0, 0xC937, 0xC938, 0, 0, 0,
- 0xC939, 0xC93A, 0xC93B, 0xC93C, 0x5E75, 0, 0xC941, 0,
- 0, 0x5F53, 0, 0, 0xC93D, 0xC93E, 0, 0,
- 0x4667, 0, 0, 0, 0, 0xC93F, 0xC940, 0,
- 0, 0, 0, 0x5F54, 0xC942, 0xC943, 0, 0,
- 0, 0, 0, 0x3250, 0xC944, 0, 0xC945, 0x4574,
- 0x3325, 0, 0, 0, 0, 0xC946, 0xC947, 0,
- 0x3564, 0, 0, 0, 0x3C5E, 0x3A52, 0xC948, 0,
-};
-static const unsigned short utf8_to_euc_E781_x0213[] = {
- 0, 0, 0, 0xEF51, 0xC938, 0, 0, 0xEF52,
- 0xC939, 0xC93A, 0x773F, 0xEF53, 0x5E75, 0, 0x7742, 0,
- 0, 0x5F53, 0, 0, 0xEF55, 0xC93E, 0, 0,
- 0x4667, 0, 0, 0, 0, 0x7740, 0x7741, 0,
- 0, 0, 0, 0x5F54, 0x7743, 0xEF56, 0, 0,
- 0, 0xEF57, 0, 0x3250, 0xEF58, 0, 0xEF59, 0x4574,
- 0x3325, 0, 0, 0, 0, 0x7744, 0xEF5A, 0,
- 0x3564, 0, 0, 0, 0x3C5E, 0x3A52, 0xEF5B, 0,
-};
-static const unsigned short utf8_to_euc_E782[] = {
- 0, 0xC949, 0, 0, 0, 0xC94A, 0xC94B, 0,
- 0, 0x4F27, 0x3F66, 0, 0, 0, 0x316A, 0,
- 0, 0, 0x5F56, 0, 0xC94C, 0xC94D, 0xC94E, 0xC94F,
- 0xC950, 0x5F55, 0, 0xC951, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xC952, 0, 0, 0,
- 0, 0, 0, 0xC953, 0x5F59, 0x433A, 0x5F5C, 0x5F57,
- 0xC954, 0xC955, 0, 0x5F5B, 0xC956, 0, 0, 0xC957,
- 0x5F5A, 0x4540, 0x3059, 0xF42E, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E782_x0213[] = {
- 0, 0xEF5C, 0, 0, 0, 0x7745, 0xEF5D, 0,
- 0, 0x4F27, 0x3F66, 0, 0, 0, 0x316A, 0,
- 0, 0, 0x5F56, 0, 0xC94C, 0xEF5E, 0xC94E, 0xEF5F,
- 0xC950, 0x5F55, 0, 0xC951, 0, 0, 0, 0xEF62,
- 0, 0, 0, 0, 0x7746, 0, 0, 0,
- 0, 0, 0, 0x7747, 0x5F59, 0x433A, 0x5F5C, 0x5F57,
- 0xC954, 0xEF63, 0, 0x5F5B, 0xC956, 0, 0, 0x7748,
- 0x5F5A, 0x4540, 0x3059, 0xEF60, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E783[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x4E75, 0, 0xC958, 0x5F5E, 0, 0, 0, 0x3128,
- 0, 0xC959, 0, 0xC95A, 0xC95B, 0xC95C, 0xC95D, 0,
- 0xC95E, 0x5F60, 0, 0, 0xC95F, 0x5F5F, 0, 0x5F5D,
- 0, 0, 0, 0, 0xC960, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0x5F58, 0, 0, 0, 0, 0, 0,
- 0, 0x4B23, 0xC961, 0, 0, 0x5F62, 0, 0,
-};
-static const unsigned short utf8_to_euc_E783_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x4E75, 0, 0xEF66, 0x5F5E, 0, 0, 0, 0x3128,
- 0, 0xEF67, 0, 0xEF68, 0x7749, 0xC95C, 0xC95D, 0,
- 0x774A, 0x5F60, 0, 0, 0xEF69, 0x5F5F, 0, 0x5F5D,
- 0, 0, 0, 0, 0x774B, 0, 0, 0,
- 0, 0, 0, 0, 0xEF65, 0, 0, 0,
- 0, 0x5F58, 0, 0, 0, 0, 0, 0,
- 0, 0x4B23, 0xC961, 0, 0, 0x5F62, 0, 0,
-};
-static const unsigned short utf8_to_euc_E784[] = {
- 0, 0, 0, 0xC962, 0xC963, 0xC964, 0xC965, 0xC966,
- 0, 0x5F61, 0, 0xC967, 0xC968, 0, 0, 0xC969,
- 0, 0, 0, 0, 0x316B, 0, 0, 0,
- 0, 0x5F64, 0x4A32, 0, 0x5F63, 0, 0xC96A, 0,
- 0xC96B, 0x4C35, 0, 0, 0, 0, 0x3E47, 0,
- 0, 0, 0, 0xC96C, 0, 0xC96D, 0, 0xC96E,
- 0xC96F, 0xC970, 0, 0, 0, 0, 0x4133, 0,
- 0xC971, 0, 0, 0, 0x3E46, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E784_x0213[] = {
- 0, 0, 0, 0xEF6A, 0xEF6B, 0xC964, 0xEF6C, 0xEF6D,
- 0xEF6E, 0x5F61, 0, 0xC967, 0xEF6F, 0, 0, 0x774C,
- 0, 0, 0, 0, 0x316B, 0, 0, 0,
- 0, 0x5F64, 0x4A32, 0, 0x5F63, 0, 0x774E, 0,
- 0x774F, 0x4C35, 0, 0, 0, 0, 0x3E47, 0,
- 0, 0, 0, 0x774D, 0, 0xC96D, 0x7750, 0xEF71,
- 0x7751, 0xEF72, 0, 0, 0, 0, 0x4133, 0,
- 0xC971, 0, 0, 0, 0x3E46, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E785[] = {
- 0, 0xC972, 0, 0, 0, 0xC973, 0xC974, 0xC975,
- 0, 0x4E7B, 0xC976, 0xC977, 0x5F6A, 0, 0x4079, 0,
- 0xC978, 0, 0xC979, 0, 0, 0x5F66, 0x5F6B, 0xC97A,
- 0, 0x316C, 0xC97B, 0, 0xC97C, 0, 0xC97D, 0,
- 0xC97E, 0, 0x5F69, 0, 0x4761, 0x5F65, 0x5F68, 0x3E48,
- 0xCA21, 0x4851, 0, 0, 0x5F6C, 0, 0x3C51, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xCA22, 0, 0, 0, 0x407A, 0, 0,
-};
-static const unsigned short utf8_to_euc_E785_x0213[] = {
- 0, 0xC972, 0, 0, 0, 0xC973, 0x7752, 0x7753,
- 0, 0x4E7B, 0xEF74, 0xC977, 0x5F6A, 0, 0x4079, 0,
- 0xEF73, 0x7754, 0x7756, 0xEF75, 0, 0x5F66, 0x5F6B, 0xC97A,
- 0, 0x316C, 0xC97B, 0, 0x7757, 0, 0xEF76, 0,
- 0x7758, 0, 0x5F69, 0, 0x4761, 0x5F65, 0x5F68, 0x3E48,
- 0x7759, 0x4851, 0, 0, 0x5F6C, 0, 0x3C51, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xCA22, 0, 0, 0, 0x407A, 0, 0,
-};
-static const unsigned short utf8_to_euc_E786[] = {
- 0xCA23, 0, 0, 0, 0x5F6F, 0xCA24, 0, 0xCA25,
- 0x5F67, 0, 0x3727, 0, 0xCA26, 0, 0, 0x5F6D,
- 0, 0, 0xCA27, 0, 0x4D50, 0x5F70, 0, 0,
- 0, 0x7426, 0xCA28, 0xCA29, 0, 0, 0, 0x3D4F,
- 0xCA2A, 0, 0xCA2B, 0, 0, 0, 0, 0,
- 0x5F71, 0, 0, 0, 0x5F72, 0, 0, 0xCA2C,
- 0xCA2D, 0x472E, 0xCA2E, 0xCA2F, 0, 0, 0, 0,
- 0, 0x5F74, 0xCA30, 0, 0, 0, 0x5F75, 0xCA31,
-};
-static const unsigned short utf8_to_euc_E786_x0213[] = {
- 0xEF79, 0, 0, 0, 0x5F6F, 0x775B, 0, 0x775C,
- 0x5F67, 0, 0x3727, 0, 0xCA26, 0, 0, 0x5F6D,
- 0, 0, 0x775D, 0, 0x4D50, 0x5F70, 0xEF78, 0,
- 0, 0x7426, 0xCA28, 0xEF7A, 0, 0, 0, 0x3D4F,
- 0xEF7B, 0, 0xEF7C, 0, 0, 0, 0, 0,
- 0x5F71, 0, 0, 0, 0x5F72, 0, 0xEF7D, 0xEF7E,
- 0xCA2D, 0x472E, 0xCA2E, 0xF021, 0, 0, 0, 0,
- 0, 0x5F74, 0x775F, 0, 0, 0, 0x5F75, 0xCA31,
-};
-static const unsigned short utf8_to_euc_E787[] = {
- 0xCA32, 0xCA33, 0, 0x4733, 0xCA34, 0, 0, 0,
- 0x4575, 0x5F77, 0, 0xCA35, 0xCA36, 0, 0x5F79, 0,
- 0x4E55, 0, 0x5F76, 0xCA37, 0x5F78, 0x316D, 0xCA38, 0x5F73,
- 0, 0xCA39, 0xCA3A, 0, 0xCA3B, 0, 0, 0x535B,
- 0x5F7A, 0, 0, 0, 0, 0x4167, 0x3B38, 0x5F7C,
- 0, 0, 0, 0, 0x5F7B, 0x3F24, 0x5259, 0,
- 0, 0, 0, 0, 0, 0x5F7D, 0, 0,
- 0xCA3C, 0x6021, 0, 0x5F6E, 0x5F7E, 0, 0xCA3D, 0x6022,
-};
-static const unsigned short utf8_to_euc_E787_x0213[] = {
- 0xCA32, 0x775E, 0, 0x4733, 0x7760, 0, 0, 0,
- 0x4575, 0x5F77, 0, 0xF023, 0xCA36, 0, 0x5F79, 0,
- 0x4E55, 0, 0x5F76, 0xF024, 0x5F78, 0x316D, 0xCA38, 0x5F73,
- 0, 0xF025, 0xCA3A, 0, 0xF026, 0, 0, 0x535B,
- 0x5F7A, 0, 0, 0, 0, 0x4167, 0x3B38, 0x5F7C,
- 0, 0, 0, 0, 0x5F7B, 0x3F24, 0x5259, 0,
- 0, 0, 0, 0, 0, 0x5F7D, 0, 0,
- 0xCA3C, 0x6021, 0, 0x5F6E, 0x5F7E, 0, 0x7761, 0x6022,
-};
-static const unsigned short utf8_to_euc_E788[] = {
- 0xCA3E, 0, 0, 0, 0, 0, 0x477A, 0xCA3F,
- 0xCA40, 0xCA41, 0, 0, 0, 0x6023, 0, 0,
- 0x6024, 0, 0, 0xCA42, 0, 0, 0, 0xCA43,
- 0, 0, 0xCA44, 0x6025, 0, 0xCA45, 0, 0xCA46,
- 0, 0, 0, 0, 0xCA47, 0, 0, 0,
- 0x6026, 0, 0x445E, 0xCA48, 0x6028, 0x6027, 0, 0xCA49,
- 0x6029, 0, 0x602A, 0, 0xCA4A, 0x3C5F, 0x4963, 0,
- 0xCA4B, 0xCA4C, 0x4C6C, 0x602B, 0x602C, 0x4156, 0x3C24, 0x602D,
-};
-static const unsigned short utf8_to_euc_E788_x0213[] = {
- 0x7762, 0, 0, 0, 0, 0, 0x477A, 0xF027,
- 0xCA40, 0xCA41, 0, 0, 0, 0x6023, 0, 0,
- 0x6024, 0, 0, 0xCA42, 0, 0x7763, 0, 0xCA43,
- 0, 0, 0xCA44, 0x6025, 0, 0xCA45, 0, 0xCA46,
- 0, 0, 0, 0, 0xCA47, 0, 0, 0,
- 0x6026, 0, 0x445E, 0xF02A, 0x6028, 0x6027, 0, 0xCA49,
- 0x6029, 0, 0x602A, 0, 0xF02B, 0x3C5F, 0x4963, 0,
- 0xF02C, 0xF02D, 0x4C6C, 0x602B, 0x602C, 0x4156, 0x3C24, 0x602D,
-};
-static const unsigned short utf8_to_euc_E789[] = {
- 0x602E, 0xCA4D, 0xCA4E, 0xCA4F, 0, 0xCA50, 0x602F, 0x4A52,
- 0x4847, 0, 0, 0x6030, 0x4757, 0, 0xCA51, 0xCA52,
- 0xCA53, 0, 0x442D, 0xCA54, 0, 0xCA55, 0xCA56, 0,
- 0x6031, 0x3267, 0xCA57, 0x356D, 0xCA58, 0x4C46, 0xCA59, 0x4C36,
- 0xCA5A, 0x3234, 0x4F34, 0xCA5B, 0, 0, 0, 0x4B52,
- 0xCA5C, 0x4A2A, 0, 0xCA5D, 0, 0, 0xCA5E, 0xCA5F,
- 0, 0xCA60, 0x4037, 0, 0x6032, 0, 0, 0xCA61,
- 0xCA62, 0x4643, 0, 0xCA63, 0xCA64, 0x3823, 0x6033, 0xCA65,
-};
-static const unsigned short utf8_to_euc_E789_x0213[] = {
- 0x602E, 0xCA4D, 0xF02F, 0xCA4F, 0, 0xCA50, 0x602F, 0x4A52,
- 0x4847, 0, 0, 0x6030, 0x4757, 0, 0xCA51, 0xCA52,
- 0xCA53, 0, 0x442D, 0xF030, 0, 0x7764, 0x7765, 0xF031,
- 0x6031, 0x3267, 0xCA57, 0x356D, 0xCA58, 0x4C46, 0xCA59, 0x4C36,
- 0xCA5A, 0x3234, 0x4F34, 0xF032, 0, 0, 0, 0x4B52,
- 0xCA5C, 0x4A2A, 0, 0xCA5D, 0, 0, 0xF034, 0xF035,
- 0, 0xCA60, 0x4037, 0, 0x6032, 0, 0, 0xCA61,
- 0xF036, 0x4643, 0, 0xCA63, 0xCA64, 0x3823, 0x6033, 0xF037,
-};
-static const unsigned short utf8_to_euc_E78A[] = {
- 0x3A54, 0x6035, 0x6034, 0, 0xCA66, 0, 0, 0x6036,
- 0, 0xCA67, 0, 0, 0, 0xCA68, 0xCA69, 0,
- 0, 0, 0x6037, 0xCA6A, 0, 0, 0x6038, 0,
- 0, 0, 0, 0xCA6B, 0, 0, 0, 0,
- 0x353E, 0, 0x6039, 0, 0, 0, 0, 0x603A,
- 0xCA6C, 0, 0, 0, 0x3824, 0xCA6D, 0xCA6E, 0x4848,
- 0, 0xCA6F, 0x603C, 0, 0xCA70, 0, 0x3E75, 0,
- 0, 0x603B, 0, 0, 0, 0, 0xCA71, 0,
-};
-static const unsigned short utf8_to_euc_E78A_x0213[] = {
- 0x3A54, 0x6035, 0x6034, 0, 0xCA66, 0, 0, 0x6036,
- 0, 0xCA67, 0, 0, 0, 0x7767, 0xF038, 0,
- 0, 0, 0x6037, 0xCA6A, 0, 0, 0x6038, 0,
- 0, 0, 0, 0x7768, 0, 0, 0, 0,
- 0x353E, 0, 0x6039, 0, 0, 0, 0, 0x603A,
- 0xCA6C, 0, 0, 0, 0x3824, 0xF03A, 0xF03B, 0x4848,
- 0xF03C, 0xF03D, 0x603C, 0, 0xCA70, 0, 0x3E75, 0,
- 0, 0x603B, 0, 0, 0, 0, 0x7769, 0,
-};
-static const unsigned short utf8_to_euc_E78B[] = {
- 0, 0xCA72, 0x3638, 0x603D, 0x603F, 0, 0x603E, 0xCA73,
- 0, 0xCA74, 0, 0, 0xCA75, 0, 0x6040, 0,
- 0x3851, 0, 0x6041, 0, 0, 0xCA76, 0xCA77, 0x3669,
- 0xCA78, 0x4140, 0, 0x397D, 0, 0, 0, 0xCA79,
- 0x6043, 0x6044, 0x6042, 0, 0, 0xCA7A, 0, 0,
- 0, 0x3C6D, 0, 0, 0x4648, 0x3639, 0, 0,
- 0, 0, 0, 0xCA7B, 0xCA7C, 0, 0, 0x6046,
- 0x432C, 0x6045, 0xCA7D, 0xCA7E, 0x4F35, 0x4762, 0xCB21, 0,
-};
-static const unsigned short utf8_to_euc_E78B_x0213[] = {
- 0x776A, 0xF03E, 0x3638, 0x603D, 0x603F, 0, 0x603E, 0xCA73,
- 0, 0xCA74, 0, 0, 0xF040, 0, 0x6040, 0,
- 0x3851, 0, 0x6041, 0, 0, 0xCA76, 0xCA77, 0x3669,
- 0xCA78, 0x4140, 0, 0x397D, 0, 0, 0, 0xCA79,
- 0x6043, 0x6044, 0x6042, 0, 0, 0xCA7A, 0, 0,
- 0, 0x3C6D, 0, 0, 0x4648, 0x3639, 0, 0,
- 0, 0, 0, 0xF043, 0xCA7C, 0, 0, 0x6046,
- 0x432C, 0x6045, 0xF044, 0x776B, 0x4F35, 0x4762, 0xCB21, 0,
-};
-static const unsigned short utf8_to_euc_E78C[] = {
- 0, 0, 0xCB22, 0, 0xCB23, 0xCB24, 0, 0xCB25,
- 0, 0, 0x6049, 0xCB26, 0, 0xCB27, 0, 0,
- 0, 0, 0xCB28, 0xCB29, 0, 0, 0x604B, 0x6048,
- 0xCB2A, 0xCB2B, 0, 0x4C54, 0x604A, 0x604C, 0xCB2C, 0x4E44,
- 0, 0, 0xCB2D, 0, 0xCB2E, 0x6050, 0, 0xCB2F,
- 0xCB30, 0x604F, 0x4376, 0x472D, 0xCB31, 0, 0x3825, 0x604E,
- 0, 0xCB32, 0xCB33, 0, 0x604D, 0xCB34, 0x4D31, 0x4D32,
- 0, 0, 0xCB35, 0xCB36, 0, 0xCB37, 0x6051, 0x316E,
-};
-static const unsigned short utf8_to_euc_E78C_x0213[] = {
- 0, 0, 0xCB22, 0, 0xCB23, 0xCB24, 0, 0xF045,
- 0, 0, 0x6049, 0xCB26, 0, 0xCB27, 0, 0,
- 0, 0, 0xF046, 0xCB29, 0, 0, 0x604B, 0x6048,
- 0xF047, 0xF048, 0, 0x4C54, 0x604A, 0x604C, 0xCB2C, 0x4E44,
- 0, 0, 0xCB2D, 0, 0, 0x6050, 0, 0x776D,
- 0x776E, 0x604F, 0x4376, 0x472D, 0xF04B, 0, 0x3825, 0x604E,
- 0, 0xF04C, 0xCB33, 0xF04D, 0x604D, 0xCB34, 0x4D31, 0x4D32,
- 0, 0xF04A, 0xCB35, 0xCB36, 0, 0xF04E, 0x6051, 0x316E,
-};
-static const unsigned short utf8_to_euc_E78D[] = {
- 0, 0, 0, 0xCB38, 0x3976, 0x3B62, 0, 0,
- 0, 0, 0, 0, 0, 0xCB39, 0x6052, 0x6053,
- 0xCB3A, 0, 0xCB3B, 0, 0, 0, 0xCB3C, 0x6055,
- 0xCB3D, 0, 0, 0, 0, 0xCB3E, 0xCB3F, 0xCB40,
- 0xCB41, 0, 0, 0x3D43, 0, 0, 0xCB42, 0xCB43,
- 0x6057, 0xCB44, 0x6056, 0xCB45, 0xCB46, 0, 0xCB47, 0xCB48,
- 0x6058, 0xCB49, 0x334D, 0, 0, 0x605A, 0, 0xCB4A,
- 0x6059, 0xCB4B, 0x605C, 0x605B, 0xCB4C, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E78D_x0213[] = {
- 0, 0, 0, 0xCB38, 0x3976, 0x3B62, 0, 0,
- 0, 0, 0, 0, 0, 0xCB39, 0x6052, 0x6053,
- 0x7770, 0, 0xF04F, 0, 0, 0, 0xCB3C, 0x6055,
- 0xCB3D, 0, 0, 0, 0, 0xCB3E, 0xCB3F, 0xCB40,
- 0xCB41, 0, 0, 0x3D43, 0, 0, 0x7771, 0xCB43,
- 0x6057, 0xCB44, 0x6056, 0xF051, 0xF052, 0, 0xF054, 0xF055,
- 0x6058, 0xF056, 0x334D, 0, 0, 0x605A, 0, 0xF057,
- 0x6059, 0xCB4B, 0x605C, 0x605B, 0x7772, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E78E[] = {
- 0xCB4D, 0xCB4E, 0, 0xCB4F, 0x383C, 0xCB50, 0xCB51, 0x4E28,
- 0, 0x364C, 0, 0x3226, 0, 0, 0xCB52, 0,
- 0xCB53, 0, 0, 0xCB54, 0, 0xCB55, 0x366A, 0xCB56,
- 0xCB57, 0, 0, 0, 0xCB58, 0, 0xCB59, 0xCB5A,
- 0xCB5B, 0, 0xCB5C, 0, 0, 0xCB5D, 0xCB5E, 0,
- 0, 0x3461, 0xCB5F, 0xCB60, 0, 0xCB61, 0, 0,
- 0, 0, 0x4E68, 0x605E, 0, 0xCB62, 0, 0xCB63,
- 0, 0xCB64, 0, 0x6060, 0xCB65, 0xCB66, 0, 0xCB67,
-};
-static const unsigned short utf8_to_euc_E78E_x0213[] = {
- 0xCB4D, 0xF058, 0, 0xCB4F, 0x383C, 0xF059, 0xCB51, 0x4E28,
- 0, 0x364C, 0xF05A, 0x3226, 0, 0, 0xCB52, 0,
- 0xCB53, 0, 0, 0xCB54, 0xF05B, 0x7773, 0x366A, 0xCB56,
- 0xF05C, 0, 0, 0, 0xF05D, 0, 0xF05E, 0x7774,
- 0x7775, 0, 0x7776, 0, 0, 0xF05F, 0x7777, 0,
- 0xF060, 0x3461, 0xCB5F, 0x7778, 0, 0xCB61, 0, 0,
- 0, 0, 0x4E68, 0x605E, 0, 0xF061, 0, 0xF062,
- 0, 0xF063, 0, 0x6060, 0xF064, 0, 0, 0xF065,
-};
-static const unsigned short utf8_to_euc_E78F[] = {
- 0x6061, 0, 0x3251, 0, 0, 0xCB68, 0xCB69, 0,
- 0x605D, 0xCB6A, 0x3B39, 0xCB6B, 0xCB6C, 0x4441, 0x605F, 0xCB6D,
- 0, 0, 0xCB6E, 0xCB6F, 0, 0, 0xCB70, 0,
- 0, 0xCB71, 0, 0, 0, 0xCB72, 0x6064, 0,
- 0x3C6E, 0xCB73, 0, 0xCB74, 0, 0x6062, 0xCB75, 0xCB76,
- 0, 0xCB77, 0x373E, 0, 0, 0x4849, 0x6063, 0,
- 0, 0x607E, 0, 0, 0xCB78, 0xCB79, 0, 0xCB7A,
- 0x6069, 0xCB7B, 0xCB7C, 0xCB7D, 0, 0xCB7E, 0x383D, 0xCC21,
-};
-static const unsigned short utf8_to_euc_E78F_x0213[] = {
- 0x6061, 0, 0x3251, 0, 0, 0xF066, 0xCB69, 0,
- 0x605D, 0x7779, 0x3B39, 0xF067, 0xCB6C, 0x4441, 0x605F, 0x777A,
- 0, 0, 0, 0xCB6F, 0, 0, 0x777B, 0,
- 0, 0x777C, 0, 0, 0, 0xCB72, 0x6064, 0,
- 0x3C6E, 0xF068, 0, 0x777D, 0, 0x6062, 0xCB75, 0xF069,
- 0, 0x777E, 0x373E, 0, 0, 0x4849, 0x6063, 0,
- 0, 0x607E, 0, 0, 0xCB78, 0, 0, 0xCB7A,
- 0x6069, 0xF06A, 0xF06C, 0xCB7D, 0, 0xCB7E, 0x383D, 0xCC21,
-};
-static const unsigned short utf8_to_euc_E790[] = {
- 0xCC22, 0xCC23, 0, 0x3565, 0xCC24, 0x6066, 0x4D7D, 0xCC25,
- 0, 0x4E30, 0xCC26, 0, 0, 0, 0, 0,
- 0, 0xCC27, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xCC28, 0xCC29, 0, 0, 0, 0,
- 0, 0, 0x4276, 0, 0xCC2A, 0x6068, 0xCC2B, 0,
- 0xCC2C, 0xCC2D, 0xCC2E, 0xCC2F, 0xCC30, 0xCC31, 0xCC32, 0xCC33,
- 0xCC34, 0xCC35, 0x606A, 0x4E56, 0x3657, 0x487C, 0x474A, 0,
- 0, 0xCC36, 0x606B, 0, 0, 0, 0, 0x606D,
-};
-static const unsigned short utf8_to_euc_E790_x0213[] = {
- 0xCC22, 0xF06D, 0, 0x3565, 0xCC24, 0x6066, 0x4D7D, 0x7821,
- 0, 0x4E30, 0x7822, 0, 0, 0, 0, 0,
- 0, 0xCC27, 0, 0xF06B, 0, 0, 0, 0,
- 0, 0, 0x7823, 0x7824, 0, 0, 0, 0,
- 0, 0, 0x4276, 0, 0xF06E, 0x6068, 0x7826, 0,
- 0x7827, 0, 0x7828, 0x7829, 0x782A, 0xCC31, 0x782B, 0x782C,
- 0x782D, 0xF06F, 0x606A, 0x4E56, 0x3657, 0x487C, 0x474A, 0,
- 0, 0xF070, 0x606B, 0, 0, 0, 0, 0x606D,
-};
-static const unsigned short utf8_to_euc_E791[] = {
- 0xCC37, 0x6070, 0, 0xCC38, 0xCC39, 0, 0xCC3A, 0xCC3B,
- 0, 0, 0, 0xCC3C, 0, 0xCC3D, 0, 0,
- 0, 0xCC3E, 0xCC3F, 0, 0, 0x606C, 0, 0xCC40,
- 0, 0x606F, 0x386A, 0x314D, 0x6071, 0xCC41, 0x3F70, 0x606E,
- 0x4E5C, 0, 0xCC42, 0x6074, 0x7424, 0, 0xCC43, 0xCC44,
- 0xCC45, 0x6072, 0x6075, 0xCC46, 0, 0xCC47, 0xCC48, 0x6067,
- 0x6073, 0xCC49, 0xCC4A, 0x3A3C, 0, 0, 0x6076, 0,
- 0, 0, 0, 0, 0, 0, 0x6077, 0,
-};
-static const unsigned short utf8_to_euc_E791_x0213[] = {
- 0xF072, 0x6070, 0, 0xF073, 0x782E, 0, 0x782F, 0x7830,
- 0, 0, 0, 0x7831, 0, 0xF074, 0, 0,
- 0, 0xCC3E, 0xF075, 0xF071, 0, 0x606C, 0, 0x7832,
- 0, 0x606F, 0x386A, 0x314D, 0x6071, 0xF076, 0x3F70, 0x606E,
- 0x4E5C, 0, 0x7833, 0x6074, 0x7424, 0, 0xCC43, 0xCC44,
- 0xCC45, 0x6072, 0x6075, 0x7834, 0, 0x7835, 0xCC48, 0x6067,
- 0x6073, 0xF077, 0xCC4A, 0x3A3C, 0, 0, 0x6076, 0,
- 0, 0, 0, 0, 0, 0, 0x6077, 0,
-};
-static const unsigned short utf8_to_euc_E792[] = {
- 0xCC4B, 0xCC4C, 0, 0x4D7E, 0, 0xCC4D, 0xCC4E, 0xCC4F,
- 0, 0xCC50, 0, 0x6078, 0, 0, 0, 0xCC51,
- 0xCC52, 0xCC53, 0xCC54, 0, 0, 0, 0, 0,
- 0xCC55, 0xCC56, 0xCC57, 0, 0xCC58, 0, 0x6079, 0xCC59,
- 0xCC5A, 0xCC5B, 0x6065, 0xCC5C, 0, 0, 0xCC5D, 0x607A,
- 0xCC5E, 0xCC5F, 0xCC60, 0xCC61, 0, 0, 0xCC62, 0xCC63,
- 0x3444, 0xCC64, 0xCC65, 0, 0, 0xCC66, 0, 0,
- 0, 0xCC67, 0, 0xCC68, 0, 0x3C25, 0, 0xCC69,
-};
-static const unsigned short utf8_to_euc_E792_x0213[] = {
- 0xCC4B, 0xF078, 0, 0x4D7E, 0, 0xF079, 0x7836, 0x7837,
- 0xF07A, 0x7838, 0, 0x6078, 0, 0, 0, 0xCC51,
- 0x783D, 0xCC53, 0xF07C, 0, 0, 0, 0, 0xF07D,
- 0x7839, 0xF07E, 0xCC57, 0, 0x783A, 0, 0x6079, 0x783B,
- 0xF121, 0xF122, 0x6065, 0x783C, 0, 0xF123, 0x783E, 0x607A,
- 0x783F, 0x7840, 0xF124, 0xF125, 0, 0, 0xCC62, 0xCC63,
- 0x3444, 0xCC64, 0xCC65, 0, 0, 0x7841, 0, 0,
- 0, 0xF126, 0xF128, 0xF127, 0, 0x3C25, 0, 0x7842,
-};
-static const unsigned short utf8_to_euc_E793[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xCC6A, 0xCC6B, 0x607B, 0, 0xCC6C, 0, 0, 0x607C,
- 0xCC6D, 0, 0, 0xCC6E, 0x607D, 0, 0, 0,
- 0xCC6F, 0, 0xCC70, 0xCC71, 0x313B, 0, 0xCC72, 0xCC73,
- 0x6121, 0, 0x493B, 0x6122, 0xCC74, 0, 0x3424, 0x6123,
- 0xCC75, 0x6124, 0xCC76, 0xCC77, 0, 0, 0x6125, 0xCC78,
- 0x6127, 0x6128, 0x6126, 0, 0xCC79, 0, 0x4953, 0x612A,
- 0x6129, 0, 0xCC7A, 0xCC7B, 0xCC7C, 0, 0, 0xCC7D,
-};
-static const unsigned short utf8_to_euc_E793_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x7843, 0x7844, 0x607B, 0, 0xCC6C, 0, 0, 0x607C,
- 0xCC6D, 0, 0, 0xCC6E, 0x607D, 0, 0xF129, 0,
- 0xF12A, 0, 0x7845, 0xCC71, 0x313B, 0, 0xF12B, 0xCC73,
- 0x6121, 0, 0x493B, 0x6122, 0xCC74, 0, 0x3424, 0x6123,
- 0xCC75, 0x6124, 0xCC76, 0xF12D, 0, 0, 0x6125, 0xF12C,
- 0x6127, 0x6128, 0x6126, 0, 0xCC79, 0, 0x4953, 0x612A,
- 0x6129, 0, 0xF12F, 0xCC7B, 0xCC7C, 0, 0, 0x7846,
-};
-static const unsigned short utf8_to_euc_E794[] = {
- 0, 0xF450, 0, 0x612C, 0x612B, 0x612D, 0xCC7E, 0,
- 0, 0, 0, 0, 0x612E, 0x6130, 0x612F, 0,
- 0, 0x3979, 0xCD21, 0x6132, 0, 0x6131, 0xCD22, 0xCD23,
- 0x3445, 0, 0x3F53, 0, 0x453C, 0, 0x6133, 0x4038,
- 0xCD24, 0xCD25, 0, 0x3B3A, 0xCD26, 0x3179, 0x6134, 0xCD27,
- 0x4D51, 0xCD28, 0xCD29, 0x4A63, 0x6135, 0, 0, 0xCD2A,
- 0x4544, 0x4D33, 0x3943, 0x3F3D, 0, 0, 0xCD2B, 0x434B,
- 0x5234, 0xCD2C, 0x442E, 0x3268, 0x6136, 0xCD2D, 0xCD2E, 0xCD2F,
-};
-static const unsigned short utf8_to_euc_E794_x0213[] = {
- 0, 0x7847, 0, 0x612C, 0x612B, 0x612D, 0xCC7E, 0,
- 0, 0, 0, 0, 0x612E, 0x6130, 0x612F, 0,
- 0, 0x3979, 0xCD21, 0x6132, 0, 0x6131, 0xCD22, 0x7848,
- 0x3445, 0, 0x3F53, 0, 0x453C, 0, 0x6133, 0x4038,
- 0xF131, 0xCD25, 0, 0x3B3A, 0xF132, 0x3179, 0x6134, 0xCD27,
- 0x4D51, 0xCD28, 0xF133, 0x4A63, 0x6135, 0, 0, 0x7849,
- 0x4544, 0x4D33, 0x3943, 0x3F3D, 0, 0, 0xCD2B, 0x434B,
- 0x5234, 0xCD2C, 0x442E, 0x3268, 0x6136, 0xF136, 0xF137, 0xCD2F,
-};
-static const unsigned short utf8_to_euc_E795[] = {
- 0xCD30, 0, 0, 0xCD31, 0x6137, 0, 0x613C, 0xCD32,
- 0xCD33, 0x613A, 0x6139, 0x5A42, 0x3326, 0x6138, 0xCD34, 0x305A,
- 0xCD35, 0x482A, 0xCD36, 0, 0x484A, 0, 0, 0xCD37,
- 0, 0x4E31, 0x613D, 0x613B, 0x435C, 0x4026, 0xCD38, 0xCD39,
- 0x482B, 0xCD3A, 0x492D, 0, 0x613F, 0x4E2C, 0x374D, 0x6140,
- 0, 0x613E, 0x4856, 0x6141, 0, 0x6142, 0, 0xCD3B,
- 0x305B, 0xCD3C, 0, 0x3E76, 0x6147, 0, 0x6144, 0x466D,
- 0x6143, 0xCD3D, 0xCD3E, 0xCD3F, 0xCD40, 0xCD41, 0xCD42, 0x3526,
-};
-static const unsigned short utf8_to_euc_E795_x0213[] = {
- 0xF138, 0, 0, 0xCD31, 0x6137, 0, 0x613C, 0xCD32,
- 0xF139, 0x613A, 0x6139, 0x5A42, 0x3326, 0x6138, 0xF13A, 0x305A,
- 0xF13B, 0x482A, 0xF13C, 0, 0x484A, 0, 0, 0xCD37,
- 0, 0x4E31, 0x613D, 0x613B, 0x435C, 0x4026, 0xCD38, 0xCD39,
- 0x482B, 0xCD3A, 0x492D, 0, 0x613F, 0x4E2C, 0x374D, 0x6140,
- 0, 0x613E, 0x4856, 0x6141, 0xF13D, 0x6142, 0, 0x784A,
- 0x305B, 0xF13F, 0xF13E, 0x3E76, 0x6147, 0, 0x6144, 0x466D,
- 0x6143, 0x784B, 0xF140, 0xCD3F, 0xCD40, 0xF141, 0xF142, 0x3526,
-};
-static const unsigned short utf8_to_euc_E796[] = {
- 0, 0xCD43, 0x614A, 0, 0, 0xCD44, 0x6145, 0x6146,
- 0, 0x6149, 0x6148, 0x4925, 0, 0, 0x4142, 0x4141,
- 0xCD45, 0x353F, 0xCD46, 0xCD47, 0x614B, 0xCD48, 0, 0,
- 0, 0xCD49, 0x614C, 0, 0xCD4A, 0x614D, 0, 0,
- 0, 0, 0xCD4B, 0x614F, 0xCD4C, 0x614E, 0, 0,
- 0, 0, 0, 0x3156, 0, 0, 0, 0,
- 0, 0x6157, 0x4868, 0x6151, 0xCD4D, 0x6153, 0, 0,
- 0x6155, 0x3F3E, 0xCD4E, 0, 0x6156, 0x6154, 0x3C40, 0xCD4F,
-};
-static const unsigned short utf8_to_euc_E796_x0213[] = {
- 0, 0xF143, 0x614A, 0, 0, 0xCD44, 0x6145, 0x6146,
- 0, 0x6149, 0x6148, 0x4925, 0xF145, 0, 0x4142, 0x4141,
- 0xCD45, 0x353F, 0x784C, 0xCD47, 0x614B, 0xCD48, 0, 0,
- 0, 0xCD49, 0x614C, 0, 0xCD4A, 0x614D, 0, 0,
- 0, 0, 0xF147, 0x614F, 0xCD4C, 0x614E, 0, 0,
- 0, 0, 0, 0x3156, 0, 0, 0, 0,
- 0xF149, 0x6157, 0x4868, 0x6151, 0xCD4D, 0x6153, 0, 0xF14A,
- 0x6155, 0x3F3E, 0xCD4E, 0, 0x6156, 0x6154, 0x3C40, 0xF14B,
-};
-static const unsigned short utf8_to_euc_E797[] = {
- 0xCD50, 0xCD51, 0x6150, 0x6152, 0xCD52, 0x4942, 0xCD53, 0x3E49,
- 0, 0, 0x6159, 0, 0xCD54, 0x6158, 0xCD55, 0xCD56,
- 0, 0, 0x615A, 0, 0x3C26, 0x3A2F, 0, 0xCD57,
- 0x4577, 0x615B, 0, 0x444B, 0xCD58, 0, 0x615D, 0xCD59,
- 0xCD5A, 0xCD5B, 0x4E21, 0x615C, 0xCD5C, 0, 0, 0xCD5D,
- 0, 0x4169, 0, 0, 0xCD5E, 0, 0xCD5F, 0xCD60,
- 0x6162, 0xCD61, 0x6164, 0x6165, 0x4354, 0, 0, 0,
- 0, 0xCD62, 0x6163, 0, 0x6160, 0, 0x615E, 0x615F,
-};
-static const unsigned short utf8_to_euc_E797_x0213[] = {
- 0xF14C, 0xCD51, 0x6150, 0x6152, 0xCD52, 0x4942, 0xF14D, 0x3E49,
- 0, 0, 0x6159, 0, 0xCD54, 0x6158, 0x784E, 0xF14E,
- 0, 0, 0x615A, 0xF14F, 0x3C26, 0x3A2F, 0, 0xCD57,
- 0x4577, 0x615B, 0, 0x444B, 0xCD58, 0xF150, 0x615D, 0xF151,
- 0xF152, 0xCD5B, 0x4E21, 0x615C, 0x784F, 0, 0, 0xF153,
- 0, 0x4169, 0, 0, 0xF154, 0, 0xF155, 0xCD60,
- 0x6162, 0xF156, 0x6164, 0x6165, 0x4354, 0, 0, 0,
- 0, 0xF157, 0x6163, 0, 0x6160, 0, 0x615E, 0x615F,
-};
-static const unsigned short utf8_to_euc_E798[] = {
- 0xCD63, 0x6161, 0xCD64, 0xCD65, 0xCD66, 0, 0, 0xCD67,
- 0xCD68, 0x6168, 0xCD69, 0x6166, 0xCD6A, 0x6167, 0, 0xCD6B,
- 0, 0, 0xCD6C, 0xCD6D, 0, 0xCD6E, 0xCD6F, 0,
- 0, 0xCD70, 0, 0xCD71, 0xCD72, 0xCD73, 0xCD74, 0x6169,
- 0x616B, 0x616C, 0x616D, 0xCD75, 0x616E, 0xCD76, 0xCD77, 0x616A,
- 0, 0xCD78, 0, 0, 0, 0xCD79, 0, 0,
- 0x6170, 0, 0xCD7A, 0xCD7B, 0x616F, 0xCD7C, 0, 0,
- 0xCD7D, 0xCD7E, 0xCE21, 0x6171, 0xCE22, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E798_x0213[] = {
- 0x7850, 0x6161, 0x7851, 0xF158, 0xCD66, 0, 0, 0xF15A,
- 0x7852, 0x6168, 0xCD69, 0x6166, 0xCD6A, 0x6167, 0, 0xF15B,
- 0, 0, 0xCD6C, 0xF15E, 0, 0x7853, 0x7854, 0,
- 0xF159, 0x7855, 0, 0xF15F, 0xF160, 0xCD73, 0x7856, 0x6169,
- 0x616B, 0x616C, 0x616D, 0xCD75, 0x616E, 0xF162, 0x7E7D, 0x616A,
- 0xF163, 0xCD78, 0, 0, 0, 0x7857, 0, 0,
- 0x6170, 0, 0xCD7A, 0xF165, 0x616F, 0x7858, 0, 0,
- 0xCD7D, 0xCD7E, 0xCE21, 0x6171, 0xF164, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E799[] = {
- 0xCE24, 0xCE25, 0x4E45, 0xCE26, 0xCE27, 0xCE28, 0x6174, 0x6172,
- 0x6173, 0xCE29, 0xCE23, 0xCE2A, 0x3462, 0, 0, 0,
- 0, 0, 0x4C7E, 0, 0, 0xCE2B, 0x4A4A, 0,
- 0x6176, 0xCE2C, 0, 0, 0x6175, 0, 0, 0xCE2D,
- 0, 0x6177, 0x6178, 0, 0xCE2E, 0xCE2F, 0, 0x617C,
- 0x6179, 0x617A, 0x617B, 0, 0x617D, 0xCE30, 0xCE31, 0xCE32,
- 0x617E, 0xCE33, 0x6221, 0, 0xCE34, 0, 0x6222, 0,
- 0x6223, 0, 0x482F, 0x4550, 0x6224, 0x4772, 0x4934, 0,
-};
-static const unsigned short utf8_to_euc_E799_x0213[] = {
- 0xCE24, 0xF168, 0x4E45, 0x7859, 0xCE27, 0xCE28, 0x6174, 0x6172,
- 0x6173, 0xF16A, 0xCE23, 0x785A, 0x3462, 0, 0, 0,
- 0, 0, 0x4C7E, 0, 0, 0xF16B, 0x4A4A, 0,
- 0x6176, 0xCE2C, 0, 0, 0x6175, 0, 0, 0xCE2D,
- 0, 0x6177, 0x6178, 0, 0x785B, 0x785C, 0, 0x617C,
- 0x6179, 0x617A, 0x617B, 0, 0x617D, 0x785D, 0xF16D, 0x785E,
- 0x617E, 0x785F, 0x6221, 0, 0xCE34, 0, 0x6222, 0,
- 0x6223, 0, 0x482F, 0x4550, 0x6224, 0x4772, 0x4934, 0,
-};
-static const unsigned short utf8_to_euc_E79A[] = {
- 0x6225, 0xCE35, 0xF451, 0x6226, 0x452A, 0xCE36, 0x3327, 0x3944,
- 0x6227, 0, 0, 0x6228, 0xCE37, 0xCE38, 0x6229, 0,
- 0x3B29, 0, 0, 0x622B, 0, 0xCE39, 0x622A, 0,
- 0, 0x622C, 0x622D, 0xCE3A, 0xCE3B, 0xCE3C, 0xF452, 0xCE3D,
- 0xCE3E, 0, 0xCE3F, 0xCE40, 0xCE41, 0xCE42, 0xCE43, 0xCE44,
- 0xCE45, 0, 0xCE46, 0, 0, 0xCE47, 0x4869, 0,
- 0x622E, 0, 0, 0, 0x622F, 0, 0, 0x7369,
- 0x6230, 0x6231, 0x6232, 0, 0, 0xCE48, 0, 0x3B2E,
-};
-static const unsigned short utf8_to_euc_E79A_x0213[] = {
- 0x6225, 0x7860, 0, 0x6226, 0x452A, 0xCE36, 0x3327, 0x3944,
- 0x6227, 0, 0, 0x6228, 0xCE37, 0xCE38, 0x6229, 0,
- 0x3B29, 0, 0, 0x622B, 0, 0xF16E, 0x622A, 0,
- 0, 0x622C, 0x622D, 0x7861, 0xF16F, 0x7862, 0x7863, 0xCE3D,
- 0xF171, 0xF170, 0xCE3F, 0xCE40, 0xCE41, 0xCE42, 0x7864, 0xF172,
- 0xF173, 0, 0x7865, 0, 0, 0xCE47, 0x4869, 0xF174,
- 0x622E, 0, 0, 0, 0x622F, 0, 0x7866, 0x7369,
- 0x6230, 0x6231, 0x6232, 0, 0, 0xCE48, 0, 0x3B2E,
-};
-static const unsigned short utf8_to_euc_E79B[] = {
- 0, 0xCE49, 0x6233, 0x4756, 0, 0xCE4A, 0x4B5F, 0,
- 0x314E, 0xCE4B, 0x3157, 0xCE4C, 0xCE4D, 0x6234, 0xCE4E, 0,
- 0, 0, 0x6236, 0, 0xCE4F, 0, 0x6235, 0x4570,
- 0, 0xCE50, 0, 0x4039, 0x5D39, 0, 0x6237, 0x4C41,
- 0xCE51, 0x6238, 0, 0x3446, 0x4857, 0x6239, 0xCE52, 0x623A,
- 0xCE53, 0, 0x623B, 0, 0xCE54, 0, 0x4C5C, 0,
- 0xCE55, 0xCE56, 0x4C55, 0, 0x443E, 0, 0xCE57, 0,
- 0x416A, 0xCE58, 0, 0x623D, 0xCE59, 0, 0x3D62, 0,
-};
-static const unsigned short utf8_to_euc_E79B_x0213[] = {
- 0, 0xCE49, 0x6233, 0x4756, 0, 0x7867, 0x4B5F, 0,
- 0x314E, 0xF176, 0x3157, 0xCE4C, 0x7868, 0x6234, 0x7869, 0,
- 0, 0, 0x6236, 0, 0x786A, 0, 0x6235, 0x4570,
- 0, 0xCE50, 0, 0x4039, 0x5D39, 0, 0x6237, 0x4C41,
- 0xCE51, 0x6238, 0, 0x3446, 0x4857, 0x6239, 0x786B, 0x623A,
- 0xF178, 0, 0x623B, 0, 0xF179, 0, 0x4C5C, 0,
- 0xCE55, 0x786C, 0x4C55, 0, 0x443E, 0, 0xCE57, 0,
- 0x416A, 0xCE58, 0, 0x623D, 0x786D, 0, 0x3D62, 0,
-};
-static const unsigned short utf8_to_euc_E79C[] = {
- 0xCE5A, 0x3E4A, 0, 0, 0x6240, 0, 0xCE5B, 0x623F,
- 0x623E, 0x487D, 0xCE5C, 0x3447, 0x3829, 0, 0xCE5D, 0,
- 0, 0, 0xCE5E, 0, 0xCE5F, 0xCE60, 0, 0xCE61,
- 0, 0xCE62, 0xCE63, 0x6246, 0xCE64, 0, 0x6243, 0x3F3F,
- 0x4C32, 0, 0xCE65, 0, 0x6242, 0x6244, 0x6245, 0,
- 0xCE66, 0x6241, 0, 0, 0, 0xCE67, 0xCE68, 0xCE69,
- 0, 0, 0, 0, 0xCE6A, 0xCE6B, 0xCE6C, 0x6247,
- 0x6248, 0xCE6D, 0x442F, 0, 0x3463, 0xCE6E, 0xCE6F, 0,
-};
-static const unsigned short utf8_to_euc_E79C_x0213[] = {
- 0xCE5A, 0x3E4A, 0, 0, 0x6240, 0, 0xCE5B, 0x623F,
- 0x623E, 0x487D, 0x786E, 0x3447, 0x3829, 0, 0xCE5D, 0,
- 0, 0, 0xCE5E, 0, 0xCE5F, 0xCE60, 0, 0xF17B,
- 0, 0x786F, 0xF17C, 0x6246, 0xCE64, 0, 0x6243, 0x3F3F,
- 0x4C32, 0, 0xCE65, 0, 0x6242, 0x6244, 0x6245, 0,
- 0xCE66, 0x6241, 0, 0, 0, 0xF17D, 0xCE68, 0xCE69,
- 0, 0, 0, 0, 0x7870, 0xF17E, 0x7871, 0x6247,
- 0x6248, 0xCE6D, 0x442F, 0, 0x3463, 0xCE6E, 0xCE6F, 0,
-};
-static const unsigned short utf8_to_euc_E79D[] = {
- 0x4365, 0, 0xCE70, 0, 0, 0xCE71, 0xCE72, 0x6249,
- 0, 0, 0xCE73, 0, 0, 0xCE74, 0xCE75, 0xCE76,
- 0, 0, 0xCE77, 0, 0, 0, 0xCE78, 0xCE79,
- 0, 0, 0x624A, 0x624D, 0xCE7A, 0, 0xCE7B, 0xCE7C,
- 0xCE7D, 0x3F67, 0xCE7E, 0x4644, 0xCF21, 0x624E, 0x4B53, 0xCF22,
- 0x624B, 0, 0xCF23, 0x624C, 0xCF24, 0, 0, 0,
- 0xCF25, 0, 0xCF26, 0xCF27, 0xCF28, 0, 0, 0,
- 0, 0x6251, 0xCF29, 0, 0, 0xCF2A, 0x6250, 0x624F,
-};
-static const unsigned short utf8_to_euc_E79D_x0213[] = {
- 0x4365, 0, 0xCE70, 0, 0, 0xCE71, 0x7872, 0x6249,
- 0, 0, 0xCE73, 0, 0, 0x7873, 0x7874, 0xCE76,
- 0, 0, 0xCE77, 0, 0, 0, 0xCE78, 0xCE79,
- 0xF225, 0, 0x624A, 0x624D, 0x7875, 0, 0xCE7B, 0x7876,
- 0xF226, 0x3F67, 0x7877, 0x4644, 0xCF21, 0x624E, 0x4B53, 0xCF22,
- 0x624B, 0, 0xF227, 0x624C, 0xCF24, 0, 0, 0,
- 0xCF25, 0, 0xF229, 0xCF27, 0xCF28, 0, 0, 0,
- 0, 0x6251, 0x7878, 0, 0xF22A, 0xF22B, 0x6250, 0x624F,
-};
-static const unsigned short utf8_to_euc_E79E[] = {
- 0xCF2B, 0, 0, 0, 0xCF2C, 0, 0, 0,
- 0, 0, 0, 0x6253, 0xCF2D, 0xCF2E, 0x6252, 0,
- 0, 0x6254, 0, 0, 0xCF2F, 0xCF30, 0xCF31, 0,
- 0, 0, 0xCF32, 0, 0, 0, 0x6256, 0xCF33,
- 0x6255, 0, 0xCF34, 0, 0, 0x4A4D, 0, 0xCF35,
- 0, 0, 0xCF36, 0, 0x3D56, 0x4E46, 0xCF37, 0xCF38,
- 0x6257, 0xCF39, 0, 0x4637, 0, 0xCF3A, 0x6258, 0,
- 0, 0x6259, 0, 0x625D, 0x625B, 0x625C, 0xCF3B, 0x625A,
-};
-static const unsigned short utf8_to_euc_E79E_x0213[] = {
- 0x7879, 0, 0, 0, 0xCF2C, 0, 0, 0,
- 0, 0, 0, 0x6253, 0xCF2D, 0xCF2E, 0x6252, 0,
- 0, 0x6254, 0, 0, 0x787A, 0xCF30, 0xCF31, 0,
- 0, 0, 0xF22E, 0, 0, 0, 0x6256, 0xF22F,
- 0x6255, 0, 0xF230, 0, 0xF231, 0x4A4D, 0, 0xCF35,
- 0, 0xF232, 0x787B, 0, 0x3D56, 0x4E46, 0xCF37, 0xCF38,
- 0x6257, 0xCF39, 0, 0x4637, 0, 0xCF3A, 0x6258, 0,
- 0, 0x6259, 0, 0x625D, 0x625B, 0x625C, 0xCF3B, 0x625A,
-};
-static const unsigned short utf8_to_euc_E79F[] = {
- 0, 0, 0, 0xCF3C, 0, 0, 0, 0x625E,
- 0, 0xCF3D, 0, 0, 0, 0x625F, 0, 0,
- 0, 0xCF3E, 0xCF3F, 0, 0, 0xCF40, 0, 0x6260,
- 0, 0xCF41, 0x6261, 0x4C37, 0x6262, 0, 0xCF42, 0xCF43,
- 0xCF44, 0, 0x4C70, 0x6263, 0xCF45, 0x434E, 0xCF46, 0x476A,
- 0, 0x366B, 0xCF47, 0, 0xCF48, 0x433B, 0x6264, 0x363A,
- 0xCF49, 0xCF4A, 0, 0x4050, 0xCF4B, 0, 0, 0,
- 0xCF4C, 0, 0, 0xCF4D, 0x6265, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E79F_x0213[] = {
- 0, 0, 0, 0xCF3C, 0, 0, 0, 0x625E,
- 0, 0xCF3D, 0, 0, 0, 0x625F, 0, 0,
- 0, 0xCF3E, 0xCF3F, 0, 0, 0xCF40, 0, 0x6260,
- 0, 0xCF41, 0x6261, 0x4C37, 0x6262, 0, 0xF233, 0xF234,
- 0x787C, 0, 0x4C70, 0x6263, 0xF235, 0x434E, 0xF236, 0x476A,
- 0, 0x366B, 0xF237, 0, 0xF238, 0x433B, 0x6264, 0x363A,
- 0xF23A, 0xCF4A, 0, 0x4050, 0xF23B, 0, 0, 0,
- 0xCF4C, 0, 0, 0xF23C, 0x6265, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E7A0[] = {
- 0, 0, 0x3A3D, 0, 0, 0xCF4E, 0xCF4F, 0,
- 0, 0xCF50, 0, 0, 0x6266, 0xCF51, 0xCF52, 0,
- 0, 0xCF53, 0x6267, 0, 0x3826, 0x3A55, 0, 0,
- 0, 0, 0, 0, 0, 0xCF54, 0, 0,
- 0x6269, 0xCF55, 0xCF56, 0xCF57, 0, 0x4556, 0x3A56, 0x354E,
- 0, 0, 0, 0, 0, 0xCF58, 0xCF59, 0,
- 0xCF5A, 0, 0x4B24, 0, 0x474B, 0xCF5B, 0, 0xCF5C,
- 0, 0, 0x4557, 0, 0, 0, 0, 0x395C,
-};
-static const unsigned short utf8_to_euc_E7A0_x0213[] = {
- 0, 0, 0x3A3D, 0, 0, 0xF23E, 0xF23F, 0,
- 0, 0xF240, 0, 0, 0x6266, 0xF241, 0xCF52, 0,
- 0, 0xCF53, 0x6267, 0, 0x3826, 0x3A55, 0, 0,
- 0, 0xF242, 0, 0, 0, 0xCF54, 0, 0,
- 0x6269, 0xF243, 0xCF56, 0xCF57, 0, 0x4556, 0x3A56, 0x354E,
- 0, 0, 0, 0, 0xF244, 0x787D, 0xCF59, 0,
- 0xCF5A, 0, 0x4B24, 0, 0x474B, 0xCF5B, 0, 0xCF5C,
- 0, 0, 0x4557, 0, 0, 0, 0, 0x395C,
-};
-static const unsigned short utf8_to_euc_E7A1[] = {
- 0, 0, 0, 0xCF5D, 0xCF5E, 0x626B, 0, 0xCF5F,
- 0xCF60, 0, 0, 0, 0xCF61, 0, 0xCF62, 0,
- 0, 0, 0xCF63, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xCF64, 0x3E4B, 0xCF65, 0,
- 0xCF66, 0xCF67, 0, 0xCF68, 0xCF69, 0, 0, 0,
- 0xCF6A, 0, 0xCF6B, 0x4E32, 0x3945, 0, 0xCF6C, 0x3827,
- 0, 0, 0x4823, 0, 0x626D, 0, 0, 0,
- 0, 0, 0xCF6D, 0, 0x626F, 0, 0xCF6E, 0,
-};
-static const unsigned short utf8_to_euc_E7A1_x0213[] = {
- 0, 0, 0, 0x7921, 0xCF5E, 0x626B, 0, 0xF245,
- 0xCF60, 0, 0, 0, 0xCF61, 0, 0x7922, 0x7923,
- 0, 0x7924, 0xCF63, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xCF64, 0x3E4B, 0xCF65, 0,
- 0xCF66, 0xCF67, 0, 0xCF68, 0xF246, 0, 0, 0,
- 0x7925, 0, 0xF247, 0x4E32, 0x3945, 0, 0x7926, 0x3827,
- 0, 0, 0x4823, 0, 0x626D, 0, 0, 0,
- 0, 0, 0, 0, 0x626F, 0, 0xCF6E, 0,
-};
-static const unsigned short utf8_to_euc_E7A2[] = {
- 0, 0x386B, 0, 0, 0, 0, 0x626E, 0x4476,
- 0, 0, 0xCF6F, 0, 0x6271, 0x3337, 0x626C, 0xCF70,
- 0, 0x486A, 0, 0x3130, 0xCF71, 0x3A6C, 0, 0x4F52,
- 0xCF72, 0, 0x6270, 0, 0, 0xCF74, 0xCF75, 0xCF76,
- 0, 0xCF73, 0, 0x6272, 0xCF77, 0, 0, 0x4A4B,
- 0xCF78, 0x4059, 0x6274, 0, 0xCF79, 0xCF7A, 0, 0x6275,
- 0xCF7B, 0xCF7C, 0xCF7D, 0xCF7E, 0, 0x6273, 0, 0,
- 0, 0, 0x334E, 0xD021, 0x627B, 0xD022, 0x627A, 0xD023,
-};
-static const unsigned short utf8_to_euc_E7A2_x0213[] = {
- 0, 0x386B, 0, 0, 0, 0, 0x626E, 0x4476,
- 0, 0, 0xF249, 0, 0x6271, 0x3337, 0x626C, 0xCF70,
- 0, 0x486A, 0, 0x3130, 0xF24A, 0x3A6C, 0, 0x4F52,
- 0xCF72, 0, 0x6270, 0, 0, 0xF24C, 0xF24D, 0xF24E,
- 0, 0xCF73, 0, 0x6272, 0xF24B, 0, 0, 0x4A4B,
- 0xCF78, 0x4059, 0x6274, 0, 0xCF79, 0x792A, 0, 0x6275,
- 0x7928, 0xCF7C, 0xCF7D, 0xCF7E, 0, 0x6273, 0, 0,
- 0, 0, 0x334E, 0xF24F, 0x627B, 0xD022, 0x627A, 0xD023,
-};
-static const unsigned short utf8_to_euc_E7A3[] = {
- 0, 0x3C27, 0, 0, 0, 0x627C, 0x6277, 0xD024,
- 0xD025, 0xD026, 0x627D, 0x6278, 0xD027, 0, 0xD028, 0,
- 0x4858, 0x6276, 0xD029, 0xD02A, 0x6279, 0xD02B, 0xD02C, 0,
- 0, 0, 0x6322, 0xD02E, 0, 0, 0, 0xD02F,
- 0xD030, 0xD031, 0, 0, 0xD02D, 0, 0xD032, 0x6321,
- 0x4B61, 0, 0xD033, 0, 0x627E, 0, 0, 0x306B,
- 0, 0, 0xD034, 0xD035, 0x6324, 0, 0xD037, 0xD038,
- 0, 0, 0xD039, 0xD03A, 0, 0x6323, 0, 0xD03B,
-};
-static const unsigned short utf8_to_euc_E7A3_x0213[] = {
- 0, 0x3C27, 0, 0, 0, 0x627C, 0x6277, 0xD024,
- 0xF250, 0xD026, 0x627D, 0x6278, 0xF251, 0, 0xF252, 0,
- 0x4858, 0x6276, 0xD029, 0xD02A, 0x6279, 0xF253, 0xD02C, 0,
- 0, 0, 0x6322, 0xD02E, 0, 0, 0, 0xD02F,
- 0xF254, 0xF255, 0, 0, 0x792B, 0, 0xF256, 0x6321,
- 0x4B61, 0, 0xD033, 0, 0x627E, 0, 0, 0x306B,
- 0, 0, 0x792C, 0xD035, 0x6324, 0, 0xD037, 0x792E,
- 0, 0xF257, 0xF258, 0xF259, 0, 0x6323, 0xF25A, 0xD03B,
-};
-static const unsigned short utf8_to_euc_E7A4[] = {
- 0xD036, 0x3E4C, 0, 0, 0, 0, 0xD03C, 0x6325,
- 0, 0, 0, 0, 0xD03D, 0, 0x4143, 0,
- 0xD03E, 0x6327, 0x6326, 0, 0, 0, 0, 0,
- 0, 0x6328, 0xD03F, 0, 0xD040, 0, 0xD041, 0xD042,
- 0xD043, 0, 0, 0, 0, 0xD044, 0x6268, 0xD045,
- 0, 0xD046, 0x626A, 0x632A, 0x6329, 0xD047, 0, 0,
- 0xF454, 0xD048, 0, 0, 0xD049, 0xD04A, 0, 0,
- 0, 0, 0x3C28, 0xD04B, 0x4E69, 0xD04C, 0x3C52, 0xD04D,
-};
-static const unsigned short utf8_to_euc_E7A4_x0213[] = {
- 0x792D, 0x3E4C, 0, 0, 0, 0, 0xD03C, 0x6325,
- 0, 0, 0, 0, 0xD03D, 0, 0x4143, 0,
- 0xF25C, 0x6327, 0x6326, 0, 0, 0, 0, 0,
- 0, 0x6328, 0xD03F, 0xF25D, 0x792F, 0, 0xD041, 0xD042,
- 0xD043, 0, 0, 0, 0, 0xF25F, 0x6268, 0xD045,
- 0, 0xD046, 0x626A, 0x632A, 0x6329, 0xD047, 0x7930, 0,
- 0xF25E, 0x7931, 0, 0, 0x7932, 0xD04A, 0, 0,
- 0, 0, 0x3C28, 0xF260, 0x4E69, 0xD04C, 0x3C52, 0xD04D,
-};
-static const unsigned short utf8_to_euc_E7A5[] = {
- 0x632B, 0x3737, 0, 0, 0xD04E, 0xD04F, 0xD050, 0x3540,
- 0x3527, 0x3B63, 0xD051, 0xD052, 0, 0, 0, 0xD053,
- 0x4D34, 0xD054, 0, 0x6331, 0xD055, 0x6330, 0x4144, 0x632D,
- 0xD056, 0, 0x632F, 0xD057, 0xD058, 0x3D4B, 0x3F40, 0x632E,
- 0x632C, 0, 0x472A, 0, 0, 0x3E4D, 0, 0xD059,
- 0x493C, 0xD05A, 0, 0xD05B, 0, 0x3A57, 0, 0,
- 0, 0, 0xD05C, 0, 0, 0, 0, 0x4578,
- 0, 0xD05D, 0x6332, 0xD05E, 0xD05F, 0, 0xD060, 0x6333,
-};
-static const unsigned short utf8_to_euc_E7A5_x0213[] = {
- 0x632B, 0x3737, 0, 0, 0xD04E, 0x7935, 0x7936, 0x3540,
- 0x3527, 0x3B63, 0xF261, 0xD052, 0, 0, 0, 0xD053,
- 0x4D34, 0xD054, 0, 0x6331, 0xD055, 0x6330, 0x4144, 0x632D,
- 0xF262, 0, 0x632F, 0xF263, 0x793A, 0x3D4B, 0x3F40, 0x632E,
- 0x632C, 0, 0x472A, 0, 0, 0x3E4D, 0, 0xF265,
- 0x493C, 0xD05A, 0, 0xD05B, 0, 0x3A57, 0, 0,
- 0, 0, 0xF266, 0, 0, 0, 0, 0x4578,
- 0, 0x793E, 0x6332, 0xD05E, 0xD05F, 0, 0xD060, 0x6333,
-};
-static const unsigned short utf8_to_euc_E7A6[] = {
- 0x6349, 0x3658, 0, 0, 0x4F3D, 0x4135, 0, 0,
- 0, 0, 0x6334, 0xD061, 0xD062, 0x3252, 0x4477, 0x4A21,
- 0, 0xD063, 0, 0xD064, 0xD065, 0xD066, 0xD067, 0,
- 0xD068, 0, 0, 0xD069, 0xD06A, 0x6335, 0, 0,
- 0, 0xD06B, 0, 0, 0, 0, 0x357A, 0x6336,
- 0xD06C, 0xD06D, 0x6338, 0xD06E, 0, 0, 0x6339, 0xD06F,
- 0x4729, 0xD070, 0, 0x633A, 0xD071, 0, 0, 0,
- 0xD072, 0x633B, 0x633C, 0xD073, 0, 0x3659, 0x3253, 0x4645,
-};
-static const unsigned short utf8_to_euc_E7A6_x0213[] = {
- 0x6349, 0x3658, 0, 0, 0x4F3D, 0x4135, 0, 0,
- 0, 0, 0x6334, 0xD061, 0xD062, 0x3252, 0x4477, 0x4A21,
- 0, 0xD063, 0, 0xD064, 0xF267, 0xF268, 0xF269, 0,
- 0x7942, 0, 0, 0xF26A, 0xD06A, 0x6335, 0, 0,
- 0, 0xF26B, 0, 0, 0, 0, 0x357A, 0x6336,
- 0xD06C, 0xF26C, 0x6338, 0xD06E, 0, 0, 0x6339, 0xD06F,
- 0x4729, 0x7943, 0, 0x633A, 0xF26D, 0, 0, 0,
- 0x7944, 0x633B, 0x633C, 0xF26E, 0, 0x3659, 0x3253, 0x4645,
-};
-static const unsigned short utf8_to_euc_E7A7[] = {
- 0x3D28, 0x3B64, 0xD074, 0, 0xD075, 0, 0, 0xD076,
- 0xD077, 0x633D, 0xD078, 0x3D29, 0, 0, 0, 0xD079,
- 0, 0x324A, 0x4943, 0, 0xD07A, 0x633E, 0xD07B, 0,
- 0x486B, 0, 0xD07C, 0, 0, 0xD07D, 0xD07E, 0x4145,
- 0xD121, 0x6341, 0xD122, 0x6342, 0x4769, 0xD123, 0x3F41, 0x633F,
- 0, 0x4361, 0xD124, 0xD125, 0x6340, 0xD126, 0, 0,
- 0x3E4E, 0xD127, 0, 0, 0, 0, 0, 0,
- 0xD128, 0, 0, 0x305C, 0xD129, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E7A7_x0213[] = {
- 0x3D28, 0x3B64, 0xF26F, 0, 0xD075, 0, 0, 0xF270,
- 0x7945, 0x633D, 0x7946, 0x3D29, 0xF271, 0xF272, 0, 0xD079,
- 0, 0x324A, 0x4943, 0, 0x7948, 0x633E, 0xF273, 0,
- 0x486B, 0, 0xD07C, 0, 0, 0xD07D, 0x7949, 0x4145,
- 0xD121, 0x6341, 0xD122, 0x6342, 0x4769, 0xD123, 0x3F41, 0x633F,
- 0, 0x4361, 0xD124, 0x794A, 0x6340, 0x794B, 0, 0,
- 0x3E4E, 0xD127, 0, 0, 0, 0, 0, 0,
- 0xD128, 0, 0, 0x305C, 0xD129, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E7A8[] = {
- 0x3529, 0, 0xD12A, 0xD12B, 0, 0, 0, 0xD12C,
- 0x6343, 0xD12D, 0xD12E, 0x4478, 0xD12F, 0x6344, 0x4047, 0,
- 0, 0xD130, 0, 0, 0x4C2D, 0xD131, 0, 0x4923,
- 0x6345, 0x6346, 0x4355, 0xD132, 0x4E47, 0, 0xD133, 0x6348,
- 0x6347, 0xD134, 0, 0, 0, 0, 0, 0xD135,
- 0, 0, 0, 0xD136, 0, 0xD137, 0x3C6F, 0xD138,
- 0xD139, 0x634A, 0x3070, 0, 0xD13A, 0xD13B, 0, 0x634D,
- 0xD13C, 0xD13D, 0xD13E, 0x634B, 0x3254, 0x374E, 0x634C, 0x3946,
-};
-static const unsigned short utf8_to_euc_E7A8_x0213[] = {
- 0x3529, 0, 0xD12A, 0x794C, 0, 0, 0, 0xD12C,
- 0x6343, 0xD12D, 0xF278, 0x4478, 0xD12F, 0x6344, 0x4047, 0,
- 0, 0xF279, 0, 0, 0x4C2D, 0xF27A, 0, 0x4923,
- 0x6345, 0x6346, 0x4355, 0xF27B, 0x4E47, 0, 0xF27C, 0x6348,
- 0x6347, 0xD134, 0, 0, 0, 0, 0, 0xD135,
- 0, 0, 0, 0xD136, 0, 0xF27E, 0x3C6F, 0xD138,
- 0xD139, 0x634A, 0x3070, 0, 0xD13A, 0xD13B, 0, 0x634D,
- 0xF321, 0x794E, 0xD13E, 0x634B, 0x3254, 0x374E, 0x634C, 0x3946,
-};
-static const unsigned short utf8_to_euc_E7A9[] = {
- 0x3972, 0, 0x4A66, 0x634E, 0xD13F, 0xD140, 0x4B54, 0xD141,
- 0xD142, 0x6350, 0, 0, 0xD143, 0x4051, 0x314F, 0x323A,
- 0x302C, 0, 0, 0, 0, 0xD144, 0xD145, 0x634F,
- 0, 0xD146, 0, 0, 0xD147, 0xD148, 0, 0xD149,
- 0xD14A, 0x6351, 0x6352, 0x3E77, 0, 0xD14B, 0, 0xD14C,
- 0, 0x6353, 0xD14D, 0x334F, 0, 0xD14E, 0, 0,
- 0x6355, 0, 0, 0, 0x376A, 0xD14F, 0x3566, 0,
- 0xD150, 0x6356, 0x3675, 0, 0, 0x6357, 0xD151, 0x407C,
-};
-static const unsigned short utf8_to_euc_E7A9_x0213[] = {
- 0x3972, 0, 0x4A66, 0x634E, 0xD13F, 0xD140, 0x4B54, 0xF322,
- 0xD142, 0x6350, 0, 0, 0xF323, 0x4051, 0x314F, 0x323A,
- 0x302C, 0, 0, 0, 0, 0xD144, 0xF324, 0x634F,
- 0, 0xF325, 0, 0, 0xF326, 0x794F, 0, 0xF327,
- 0xF328, 0x6351, 0x6352, 0x3E77, 0, 0xD14B, 0, 0xF329,
- 0, 0x6353, 0xF32A, 0x334F, 0, 0x7950, 0, 0,
- 0x6355, 0, 0, 0, 0x376A, 0xF32B, 0x3566, 0,
- 0xF32C, 0x6356, 0x3675, 0, 0, 0x6357, 0xD151, 0x407C,
-};
-static const unsigned short utf8_to_euc_E7AA[] = {
- 0xD152, 0x464D, 0xD153, 0x4060, 0x3A75, 0xD154, 0xD155, 0,
- 0x6358, 0, 0xD156, 0xD157, 0, 0, 0, 0,
- 0xD158, 0xD159, 0x4362, 0x416B, 0xD15A, 0x635A, 0x635C, 0x6359,
- 0x635B, 0, 0, 0, 0, 0, 0xD15B, 0x3722,
- 0xD15C, 0, 0, 0xD15D, 0, 0, 0, 0,
- 0, 0x635D, 0x3726, 0, 0xD15E, 0, 0x3567, 0x4D52,
- 0x635F, 0, 0, 0xD15F, 0, 0xD160, 0x6360, 0,
- 0, 0xD161, 0x312E, 0xD162, 0xD163, 0, 0, 0x6363,
-};
-static const unsigned short utf8_to_euc_E7AA_x0213[] = {
- 0xD152, 0x464D, 0xF32D, 0x4060, 0x3A75, 0x7952, 0xD155, 0,
- 0x6358, 0, 0xF32E, 0xD157, 0, 0, 0, 0,
- 0xF32F, 0xD159, 0x4362, 0x416B, 0xD15A, 0x635A, 0x635C, 0x6359,
- 0x635B, 0, 0, 0, 0, 0, 0xD15B, 0x3722,
- 0x7953, 0, 0, 0xF330, 0, 0, 0, 0,
- 0, 0x635D, 0x3726, 0, 0xF331, 0, 0x3567, 0x4D52,
- 0x635F, 0, 0, 0x7955, 0, 0xD160, 0x6360, 0,
- 0, 0xF334, 0x312E, 0x7956, 0xF335, 0, 0xF336, 0x6363,
-};
-static const unsigned short utf8_to_euc_E7AB[] = {
- 0, 0, 0, 0x3376, 0x6362, 0x6361, 0xD164, 0x6365,
- 0x635E, 0xD165, 0x6366, 0x4E29, 0xD166, 0x6367, 0xD167, 0x6368,
- 0, 0xD168, 0x5474, 0x636A, 0, 0x6369, 0, 0,
- 0, 0x636B, 0x636C, 0xD169, 0x4E35, 0x636D, 0, 0x706F,
- 0x3E4F, 0x636E, 0x636F, 0x3D57, 0, 0x4638, 0x6370, 0xF459,
- 0xD16A, 0xD16B, 0x4328, 0xD16C, 0xD16D, 0x6371, 0, 0x433C,
- 0x6372, 0xD16E, 0, 0, 0xD16F, 0, 0x3625, 0,
- 0x513F, 0x435D, 0x3C33, 0xD170, 0, 0xD171, 0xD172, 0x3448,
-};
-static const unsigned short utf8_to_euc_E7AB_x0213[] = {
- 0, 0, 0, 0x3376, 0x6362, 0x6361, 0xD164, 0x6365,
- 0x635E, 0xD165, 0x6366, 0x4E29, 0xF338, 0x6367, 0x7957, 0x6368,
- 0, 0xF339, 0x5474, 0x636A, 0, 0x6369, 0, 0,
- 0, 0x636B, 0x636C, 0xD169, 0x4E35, 0x636D, 0, 0x706F,
- 0x3E4F, 0x636E, 0x636F, 0x3D57, 0, 0x4638, 0x6370, 0xF33A,
- 0xF33B, 0xD16B, 0x4328, 0x7958, 0xD16D, 0x6371, 0, 0x433C,
- 0x6372, 0xD16E, 0, 0, 0xF33C, 0, 0x3625, 0,
- 0x513F, 0x435D, 0x3C33, 0xD170, 0, 0x7959, 0xD172, 0x3448,
-};
-static const unsigned short utf8_to_euc_E7AC[] = {
- 0, 0, 0x6373, 0, 0x6422, 0, 0x6376, 0xD173,
- 0x3568, 0, 0x6375, 0x6424, 0, 0, 0, 0x6374,
- 0, 0x3E50, 0, 0, 0xD174, 0, 0, 0,
- 0x6378, 0x6379, 0, 0x452B, 0, 0, 0x637A, 0xD175,
- 0x335E, 0, 0, 0xD176, 0, 0x3F5A, 0x4964, 0xD177,
- 0x637C, 0xD178, 0xD179, 0xD17A, 0x4268, 0xD17B, 0xD17C, 0xD17D,
- 0xD17E, 0xD221, 0, 0x6377, 0xD222, 0x637B, 0x637D, 0,
- 0, 0x3A7B, 0, 0, 0, 0xD223, 0, 0xD224,
-};
-static const unsigned short utf8_to_euc_E7AC_x0213[] = {
- 0, 0, 0x6373, 0, 0x6422, 0, 0x6376, 0xF33F,
- 0x3568, 0, 0x6375, 0x6424, 0, 0, 0, 0x6374,
- 0, 0x3E50, 0x795A, 0, 0xD174, 0, 0, 0,
- 0x6378, 0x6379, 0, 0x452B, 0, 0, 0x637A, 0xD175,
- 0x335E, 0, 0, 0xD176, 0, 0x3F5A, 0x4964, 0xF342,
- 0x637C, 0xD178, 0xF343, 0xD17A, 0x4268, 0x795B, 0xF344, 0xF345,
- 0xD17E, 0xF346, 0, 0x6377, 0xD222, 0x637B, 0x637D, 0,
- 0, 0x3A7B, 0, 0x795C, 0, 0xF341, 0, 0xD224,
-};
-static const unsigned short utf8_to_euc_E7AD[] = {
- 0xD225, 0xD226, 0, 0, 0, 0x6426, 0x492E, 0xD227,
- 0x4826, 0x4579, 0, 0x365A, 0x6425, 0x6423, 0xD228, 0x4835,
- 0x637E, 0x435E, 0x457B, 0, 0x457A, 0xD229, 0x3A76, 0,
- 0, 0, 0, 0, 0, 0x6438, 0, 0,
- 0xD22A, 0, 0, 0, 0xD22B, 0x6428, 0xD22C, 0x642A,
- 0, 0xD22D, 0xD22E, 0, 0x642D, 0xD22F, 0x642E, 0xD230,
- 0x642B, 0x642C, 0xD231, 0xD232, 0x6429, 0x6427, 0, 0xD233,
- 0, 0, 0x6421, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E7AD_x0213[] = {
- 0xD225, 0xF34A, 0, 0, 0, 0x6426, 0x492E, 0x795D,
- 0x4826, 0x4579, 0, 0x365A, 0x6425, 0x6423, 0x795E, 0x4835,
- 0x637E, 0x435E, 0x457B, 0, 0x457A, 0xF34C, 0x3A76, 0,
- 0, 0, 0, 0, 0, 0x6438, 0, 0,
- 0x795F, 0, 0, 0, 0xF34E, 0x6428, 0xF34F, 0x642A,
- 0, 0xF350, 0xD22E, 0, 0x642D, 0x7960, 0x642E, 0x7961,
- 0x642B, 0x642C, 0x7962, 0xF351, 0x6429, 0x6427, 0, 0xD233,
- 0, 0xF34D, 0x6421, 0, 0, 0, 0, 0xF349,
-};
-static const unsigned short utf8_to_euc_E7AE[] = {
- 0, 0, 0, 0, 0xD234, 0, 0x4A4F, 0x3255,
- 0, 0xD235, 0, 0x6435, 0, 0x6432, 0xD236, 0x6437,
- 0xD237, 0xD238, 0x6436, 0, 0x4773, 0x4C27, 0xD239, 0x3B3B,
- 0x6430, 0x6439, 0x6434, 0xD23A, 0x6433, 0x642F, 0xD23B, 0x6431,
- 0xD23C, 0x3449, 0, 0, 0, 0xD23D, 0, 0,
- 0, 0, 0x433D, 0, 0xD23E, 0x407D, 0, 0xD23F,
- 0xD240, 0x4822, 0xD241, 0, 0x643E, 0xD242, 0xD243, 0,
- 0x4824, 0, 0xD244, 0xD245, 0xD246, 0xD247, 0, 0,
-};
-static const unsigned short utf8_to_euc_E7AE_x0213[] = {
- 0, 0, 0, 0, 0xD234, 0, 0x4A4F, 0x3255,
- 0, 0xD235, 0, 0x6435, 0, 0x6432, 0xD236, 0x6437,
- 0xF354, 0xF355, 0x6436, 0, 0x4773, 0x4C27, 0xD239, 0x3B3B,
- 0x6430, 0x6439, 0x6434, 0xF356, 0x6433, 0x642F, 0x7963, 0x6431,
- 0xD23C, 0x3449, 0, 0, 0, 0xD23D, 0, 0,
- 0, 0, 0x433D, 0, 0xD23E, 0x407D, 0, 0xF358,
- 0xD240, 0x4822, 0xD241, 0, 0x643E, 0xF359, 0xD243, 0,
- 0x4824, 0, 0xD244, 0xD245, 0xF35A, 0xD247, 0, 0,
-};
-static const unsigned short utf8_to_euc_E7AF[] = {
- 0x4061, 0x643B, 0xD248, 0, 0x484F, 0xD249, 0x643F, 0x4A53,
- 0xD24A, 0x435B, 0xD24B, 0x643A, 0x643C, 0, 0, 0x643D,
- 0, 0, 0, 0, 0xD24C, 0, 0xD24D, 0xD24E,
- 0, 0xD24F, 0xD250, 0xD251, 0, 0x6440, 0, 0,
- 0x3C44, 0, 0, 0, 0x4646, 0x6445, 0x6444, 0,
- 0xD252, 0x6441, 0xD253, 0, 0, 0x4F36, 0, 0,
- 0, 0, 0xD254, 0x644A, 0xD255, 0xD256, 0x644E, 0x644B,
- 0xD257, 0xD258, 0xD259, 0, 0xD25A, 0, 0xD25B, 0,
-};
-static const unsigned short utf8_to_euc_E7AF_x0213[] = {
- 0x4061, 0x643B, 0xD248, 0, 0x484F, 0xF35B, 0x643F, 0x4A53,
- 0xD24A, 0x435B, 0xF35C, 0x643A, 0x643C, 0, 0, 0x643D,
- 0, 0, 0, 0, 0xF35F, 0, 0xF360, 0x7965,
- 0, 0x7966, 0xF361, 0xD251, 0, 0x6440, 0, 0,
- 0x3C44, 0, 0, 0, 0x4646, 0x6445, 0x6444, 0,
- 0xD252, 0x6441, 0xF362, 0, 0, 0x4F36, 0, 0,
- 0xF363, 0, 0xD254, 0x644A, 0xD255, 0xD256, 0x644E, 0x644B,
- 0xD257, 0xD258, 0xD259, 0, 0xD25A, 0, 0xD25B, 0,
-};
-static const unsigned short utf8_to_euc_E7B0[] = {
- 0x6447, 0xD25C, 0xD25D, 0xD25E, 0xD25F, 0, 0xD260, 0x6448,
- 0, 0xD261, 0, 0xD262, 0xD263, 0x644D, 0xD264, 0xD265,
- 0, 0x6442, 0x5255, 0x6449, 0x6443, 0, 0, 0x644C,
- 0, 0xD266, 0, 0xD267, 0, 0, 0, 0x6452,
- 0xD268, 0x344A, 0, 0x644F, 0, 0xD269, 0xD26A, 0x6450,
- 0xD26B, 0, 0x6451, 0x6454, 0xD26C, 0, 0, 0,
- 0, 0xD26D, 0, 0xD26E, 0xD26F, 0, 0xD270, 0x6453,
- 0x4876, 0xD271, 0xD272, 0, 0, 0x6455, 0x4E7C, 0x4A6D,
-};
-static const unsigned short utf8_to_euc_E7B0_x0213[] = {
- 0x6447, 0x7967, 0xD25D, 0xF364, 0xD25F, 0, 0xD260, 0x6448,
- 0, 0xD261, 0, 0xF365, 0xD263, 0x644D, 0xF366, 0xF367,
- 0, 0x6442, 0x5255, 0x6449, 0x6443, 0, 0, 0x644C,
- 0, 0xD266, 0, 0xD267, 0, 0, 0x7969, 0x6452,
- 0x796A, 0x344A, 0, 0x644F, 0, 0xD269, 0xF368, 0x6450,
- 0xD26B, 0, 0x6451, 0x6454, 0xD26C, 0, 0, 0,
- 0, 0x7968, 0, 0x796B, 0xD26F, 0, 0x796C, 0x6453,
- 0x4876, 0xD271, 0xD272, 0, 0, 0x6455, 0x4E7C, 0x4A6D,
-};
-static const unsigned short utf8_to_euc_E7B1[] = {
- 0x645A, 0, 0, 0x6457, 0, 0, 0xD273, 0,
- 0, 0, 0xD274, 0, 0x6456, 0x4052, 0, 0x6459,
- 0x645B, 0xD276, 0xD277, 0xD278, 0x6458, 0xD275, 0x645F, 0,
- 0x645C, 0xD279, 0xD27A, 0xD27B, 0xD27C, 0xD27D, 0xD27E, 0x645D,
- 0x6446, 0xD321, 0, 0xD322, 0x645E, 0x6460, 0, 0xD323,
- 0, 0xD324, 0, 0, 0x6461, 0xD325, 0xD326, 0,
- 0xD327, 0, 0xD328, 0x4A46, 0, 0x6462, 0, 0,
- 0, 0xD329, 0, 0, 0xD32A, 0xD32B, 0x4C62, 0,
-};
-static const unsigned short utf8_to_euc_E7B1_x0213[] = {
- 0x645A, 0, 0, 0x6457, 0, 0xF369, 0xD273, 0,
- 0, 0, 0xF36A, 0, 0x6456, 0x4052, 0, 0x6459,
- 0x645B, 0xF36B, 0xD277, 0xD278, 0x6458, 0xD275, 0x645F, 0xF36C,
- 0x645C, 0x796F, 0xD27A, 0xD27B, 0xD27C, 0xD27D, 0xF36D, 0x645D,
- 0x6446, 0xF36E, 0, 0xD322, 0x645E, 0x6460, 0, 0xD323,
- 0, 0xF36F, 0, 0, 0x6461, 0x7970, 0xF370, 0xF371,
- 0xF372, 0, 0xD328, 0x4A46, 0, 0x6462, 0, 0,
- 0, 0x7971, 0, 0, 0xD32A, 0xD32B, 0x4C62, 0,
-};
-static const unsigned short utf8_to_euc_E7B2[] = {
- 0, 0x364E, 0x3729, 0x6463, 0, 0, 0xD32C, 0xD32D,
- 0, 0x4A34, 0, 0x3F68, 0, 0x4C30, 0, 0xD32E,
- 0x6464, 0, 0x4E33, 0, 0xD32F, 0x4774, 0, 0x4146,
- 0x4734, 0, 0, 0x3D4D, 0, 0, 0xD330, 0x3040,
- 0xD331, 0x6469, 0x6467, 0, 0x6465, 0x3421, 0xD332, 0x3E51,
- 0x646A, 0, 0, 0x6468, 0, 0x6466, 0x646E, 0,
- 0xD333, 0x646D, 0x646C, 0x646B, 0, 0, 0xD334, 0xD335,
- 0, 0x646F, 0xD336, 0xD337, 0xD338, 0x6470, 0x403A, 0xD339,
-};
-static const unsigned short utf8_to_euc_E7B2_x0213[] = {
- 0, 0x364E, 0x3729, 0x6463, 0, 0, 0xD32C, 0xD32D,
- 0, 0x4A34, 0, 0x3F68, 0, 0x4C30, 0, 0x7972,
- 0x6464, 0, 0x4E33, 0, 0x7973, 0x4774, 0, 0x4146,
- 0x4734, 0, 0, 0x3D4D, 0, 0, 0xD330, 0x3040,
- 0x7974, 0x6469, 0x6467, 0, 0x6465, 0x3421, 0xF376, 0x3E51,
- 0x646A, 0, 0, 0x6468, 0, 0x6466, 0x646E, 0,
- 0xD333, 0x646D, 0x646C, 0x646B, 0, 0, 0xF378, 0xF379,
- 0, 0x646F, 0xD336, 0xD337, 0x7975, 0x6470, 0x403A, 0xF37A,
-};
-static const unsigned short utf8_to_euc_E7B3[] = {
- 0x6471, 0, 0x6473, 0, 0xD33A, 0x6472, 0, 0xD33B,
- 0xD33C, 0xD33D, 0x3852, 0, 0, 0xD33E, 0x4138, 0xD33F,
- 0, 0, 0x6475, 0xD340, 0xD341, 0xD342, 0x457C, 0xD343,
- 0x6474, 0xD344, 0xD345, 0, 0x6476, 0xD346, 0x4A35, 0x416C,
- 0x3947, 0, 0x6477, 0, 0, 0, 0xD347, 0x4E48,
- 0, 0xD348, 0, 0xD349, 0, 0, 0, 0x6479,
- 0, 0, 0x647A, 0, 0x647B, 0xD34A, 0x647C, 0,
- 0x3B65, 0, 0x647D, 0x374F, 0, 0, 0x356A, 0,
-};
-static const unsigned short utf8_to_euc_E7B3_x0213[] = {
- 0x6471, 0, 0x6473, 0, 0xF37C, 0x6472, 0, 0xD33B,
- 0xF37E, 0xD33D, 0x3852, 0, 0, 0xF421, 0x4138, 0xD33F,
- 0, 0, 0x6475, 0xD340, 0xD341, 0x7976, 0x457C, 0xF423,
- 0x6474, 0x7977, 0xD345, 0, 0x6476, 0x7978, 0x4A35, 0x416C,
- 0x3947, 0, 0x6477, 0, 0, 0, 0xF425, 0x4E48,
- 0, 0xD348, 0, 0xF426, 0, 0, 0, 0x6479,
- 0, 0, 0x647A, 0, 0x647B, 0xF428, 0x647C, 0,
- 0x3B65, 0, 0x647D, 0x374F, 0, 0, 0x356A, 0,
-};
-static const unsigned short utf8_to_euc_E7B4[] = {
- 0x352A, 0, 0x6521, 0xD34B, 0x4C73, 0x3948, 0x647E, 0xD34C,
- 0xD34D, 0xD34E, 0x6524, 0x4C66, 0, 0x473C, 0, 0xD34F,
- 0x4933, 0xD350, 0xD351, 0xD352, 0x3D63, 0x6523, 0xD353, 0x3C53,
- 0x3949, 0x3B66, 0x3569, 0x4A36, 0x6522, 0xD354, 0xD355, 0,
- 0x4147, 0x4B42, 0x3A77, 0xD356, 0, 0, 0xD357, 0,
- 0, 0, 0xD358, 0x3B67, 0x445D, 0xD359, 0x6527, 0x4E5F,
- 0x3A59, 0xD35A, 0x6528, 0x3F42, 0, 0x652A, 0, 0,
- 0, 0x3E52, 0x3A30, 0, 0xD35B, 0xD35C, 0xD35D, 0x6529,
-};
-static const unsigned short utf8_to_euc_E7B4_x0213[] = {
- 0x352A, 0, 0x6521, 0xF429, 0x4C73, 0x3948, 0x647E, 0x7979,
- 0x797A, 0xF42A, 0x6524, 0x4C66, 0, 0x473C, 0, 0xD34F,
- 0x4933, 0xD350, 0xF42C, 0x797B, 0x3D63, 0x6523, 0xD353, 0x3C53,
- 0x3949, 0x3B66, 0x3569, 0x4A36, 0x6522, 0x797C, 0xF42D, 0,
- 0x4147, 0x4B42, 0x3A77, 0x797D, 0, 0, 0xD357, 0,
- 0, 0, 0xD358, 0x3B67, 0x445D, 0xD359, 0x6527, 0x4E5F,
- 0x3A59, 0x797E, 0x6528, 0x3F42, 0, 0x652A, 0, 0,
- 0, 0x3E52, 0x3A30, 0, 0xD35B, 0xF430, 0xF431, 0x6529,
-};
-static const unsigned short utf8_to_euc_E7B5[] = {
- 0xD35E, 0xD35F, 0x3D2A, 0x383E, 0x4148, 0x6525, 0x652B, 0xD360,
- 0xD361, 0, 0, 0x6526, 0x3750, 0xD362, 0x652E, 0x6532,
- 0x376B, 0xD363, 0, 0xD364, 0, 0, 0x652D, 0xD365,
- 0, 0xD366, 0xD367, 0x6536, 0xD368, 0xD369, 0x394A, 0,
- 0, 0x4D6D, 0x303C, 0x6533, 0, 0xD36A, 0x356B, 0xD36B,
- 0x6530, 0, 0xD36C, 0, 0, 0, 0x6531, 0,
- 0xD36D, 0x457D, 0x652F, 0x652C, 0, 0x3328, 0x4064, 0,
- 0xD36E, 0x3828, 0xD36F, 0xD370, 0, 0x6538, 0, 0xD371,
-};
-static const unsigned short utf8_to_euc_E7B5_x0213[] = {
- 0xF432, 0x7A21, 0x3D2A, 0x383E, 0x4148, 0x6525, 0x652B, 0xF433,
- 0x7A22, 0, 0, 0x6526, 0x3750, 0xD362, 0x652E, 0x6532,
- 0x376B, 0xD363, 0, 0x7A23, 0, 0, 0x652D, 0xD365,
- 0, 0xF437, 0xF438, 0x6536, 0x7A24, 0xD369, 0x394A, 0,
- 0, 0x4D6D, 0x303C, 0x6533, 0, 0xD36A, 0x356B, 0xD36B,
- 0x6530, 0, 0xF439, 0, 0, 0, 0x6531, 0,
- 0xF43A, 0x457D, 0x652F, 0x652C, 0, 0x3328, 0x4064, 0,
- 0xD36E, 0x3828, 0x7A25, 0xD370, 0, 0x6538, 0, 0xF43C,
-};
-static const unsigned short utf8_to_euc_E7B6[] = {
- 0, 0xD372, 0xD373, 0xD374, 0, 0xD375, 0xD376, 0,
- 0xD377, 0x6535, 0, 0xD378, 0xD379, 0xD37A, 0, 0x6537,
- 0, 0xD37B, 0, 0x6534, 0, 0, 0xD37C, 0xD37D,
- 0, 0x3751, 0x4233, 0x6539, 0x416E, 0xD37E, 0xD421, 0x6546,
- 0xF45C, 0, 0x6542, 0x653C, 0, 0, 0xD422, 0xD423,
- 0, 0, 0xD424, 0x6540, 0x3C7A, 0x305D, 0x653B, 0x6543,
- 0x6547, 0x394B, 0x4C56, 0xD425, 0x4456, 0x653D, 0xD426, 0xD427,
- 0x6545, 0xD428, 0x653A, 0x433E, 0, 0x653F, 0x303D, 0x4C4A,
-};
-static const unsigned short utf8_to_euc_E7B6_x0213[] = {
- 0, 0xD372, 0xD373, 0x7A26, 0, 0xD375, 0xF43E, 0,
- 0xF43F, 0x6535, 0, 0x7A27, 0xF440, 0xD37A, 0, 0x6537,
- 0, 0xD37B, 0, 0x6534, 0, 0, 0xD37C, 0xF441,
- 0, 0x3751, 0x4233, 0x6539, 0x416E, 0xF443, 0xD421, 0x6546,
- 0x7A28, 0, 0x6542, 0x653C, 0, 0, 0x7A29, 0xF444,
- 0, 0, 0xF445, 0x6540, 0x3C7A, 0x305D, 0x653B, 0x6543,
- 0x6547, 0x394B, 0x4C56, 0xD425, 0x4456, 0x653D, 0xF446, 0xF447,
- 0x6545, 0xD428, 0x653A, 0x433E, 0, 0x653F, 0x303D, 0x4C4A,
-};
-static const unsigned short utf8_to_euc_E7B7[] = {
- 0, 0, 0xD429, 0xD42A, 0xD42B, 0xD42C, 0xD42D, 0x653E,
- 0, 0, 0x365B, 0x486C, 0xD42E, 0xD42F, 0xD430, 0x416D,
- 0, 0x4E50, 0x3D6F, 0, 0, 0x656E, 0xF45D, 0xD431,
- 0x6548, 0xD432, 0x407E, 0, 0x6544, 0x6549, 0x654B, 0,
- 0x4479, 0x654E, 0xD434, 0, 0x654A, 0xD435, 0xD436, 0,
- 0x4A54, 0x344B, 0xD437, 0xD438, 0x4C4B, 0xD439, 0, 0x305E,
- 0, 0xD43A, 0x654D, 0, 0x4E7D, 0xD43B, 0xD43C, 0,
- 0, 0xD43D, 0xD43E, 0x654C, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E7B7_x0213[] = {
- 0xF448, 0, 0x7A2A, 0xD42A, 0xD42B, 0xD42C, 0xD42D, 0x653E,
- 0, 0, 0x365B, 0x486C, 0x7A2B, 0xD42F, 0xD430, 0x416D,
- 0, 0x4E50, 0x3D6F, 0, 0, 0x656E, 0x7A2C, 0xF449,
- 0x6548, 0xF44A, 0x407E, 0, 0x6544, 0x6549, 0x654B, 0,
- 0x4479, 0x654E, 0xD434, 0x7A2D, 0x654A, 0xD435, 0xF44B, 0,
- 0x4A54, 0x344B, 0xD437, 0xD438, 0x4C4B, 0xD439, 0, 0x305E,
- 0, 0xF44C, 0x654D, 0, 0x4E7D, 0xD43B, 0xD43C, 0,
- 0, 0xF44D, 0xD43E, 0x654C, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E7B8[] = {
- 0xD433, 0x316F, 0, 0, 0x466C, 0x654F, 0, 0,
- 0xD43F, 0x6556, 0x6550, 0x6557, 0, 0, 0, 0,
- 0xD440, 0xD441, 0x6553, 0, 0, 0xD442, 0, 0xD443,
- 0, 0, 0, 0x477B, 0xD444, 0xD445, 0x3C4A, 0x6555,
- 0xD446, 0x6552, 0x6558, 0x6551, 0, 0, 0x3D44, 0xD447,
- 0xD448, 0, 0, 0x4B25, 0xD449, 0xD44A, 0x3D4C, 0xD44B,
- 0, 0x6554, 0x6560, 0xD44C, 0, 0x655C, 0xD44D, 0x655F,
- 0, 0x655D, 0x6561, 0x655B, 0, 0x6541, 0x4053, 0xD44E,
-};
-static const unsigned short utf8_to_euc_E7B8_x0213[] = {
- 0xD433, 0x316F, 0, 0, 0x466C, 0x654F, 0, 0,
- 0x7A30, 0x6556, 0x6550, 0x6557, 0, 0, 0, 0,
- 0xF451, 0x7A31, 0x6553, 0, 0, 0x7A32, 0, 0xF452,
- 0, 0, 0, 0x477B, 0xD444, 0xF453, 0x3C4A, 0x6555,
- 0xF454, 0x6552, 0x6558, 0x6551, 0, 0, 0x3D44, 0xF455,
- 0x7A2F, 0, 0, 0x4B25, 0xF456, 0xD44A, 0x3D4C, 0xD44B,
- 0, 0x6554, 0x6560, 0xD44C, 0, 0x655C, 0xD44D, 0x655F,
- 0, 0x655D, 0x6561, 0x655B, 0, 0x6541, 0x4053, 0xD44E,
-};
-static const unsigned short utf8_to_euc_E7B9[] = {
- 0, 0x484B, 0, 0x655E, 0xD44F, 0xD450, 0x6559, 0xD451,
- 0, 0, 0x4121, 0x3752, 0, 0x3D2B, 0xD452, 0,
- 0xD453, 0, 0xD454, 0, 0x3F25, 0x4136, 0x6564, 0,
- 0xD455, 0x6566, 0x6567, 0, 0, 0x6563, 0x6565, 0xD456,
- 0, 0xD457, 0xD458, 0, 0, 0xD459, 0x655A, 0x6562,
- 0, 0x656A, 0x6569, 0xD45A, 0, 0x4B7A, 0xD45B, 0xD45C,
- 0x372B, 0, 0, 0xD45D, 0, 0, 0, 0,
- 0xD45E, 0x6568, 0, 0x656C, 0x656B, 0x656F, 0xD45F, 0x6571,
-};
-static const unsigned short utf8_to_euc_E7B9_x0213[] = {
- 0, 0x484B, 0, 0x655E, 0xD44F, 0xF457, 0x6559, 0x7A34,
- 0, 0, 0x4121, 0x3752, 0, 0x3D2B, 0xD452, 0,
- 0xD453, 0, 0x7A35, 0, 0x3F25, 0x4136, 0x6564, 0,
- 0xD455, 0x6566, 0x6567, 0, 0, 0x6563, 0x6565, 0xD456,
- 0, 0x7A36, 0xD458, 0, 0, 0xD459, 0x655A, 0x6562,
- 0, 0x656A, 0x6569, 0x7E7E, 0, 0x4B7A, 0xD45B, 0xD45C,
- 0x372B, 0, 0, 0xF458, 0, 0xF459, 0, 0,
- 0xD45E, 0x6568, 0, 0x656C, 0x656B, 0x656F, 0xF45A, 0x6571,
-};
-static const unsigned short utf8_to_euc_E7BA[] = {
- 0, 0xD460, 0x3B3C, 0x656D, 0, 0, 0xD461, 0xD462,
- 0x6572, 0x6573, 0xD463, 0, 0x6574, 0xD464, 0x657A, 0x453B,
- 0x6576, 0xD465, 0x6575, 0x6577, 0x6578, 0xD466, 0x6579, 0,
- 0xD467, 0, 0xD468, 0x657B, 0x657C, 0xD469, 0xD46A, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E7BA_x0213[] = {
- 0, 0xD460, 0x3B3C, 0x656D, 0, 0, 0xF45B, 0xF45C,
- 0x6572, 0x6573, 0x7A37, 0, 0x6574, 0x7A38, 0x657A, 0x453B,
- 0x6576, 0xF45E, 0x6575, 0x6577, 0x6578, 0xD466, 0x6579, 0,
- 0xF45F, 0, 0xF460, 0x657B, 0x657C, 0xD469, 0xD46A, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E7BC[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0x344C, 0,
- 0x657D, 0, 0x657E, 0xD46C, 0xD46B, 0xD46D, 0xD46E, 0xD46F,
-};
-static const unsigned short utf8_to_euc_E7BC_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0x344C, 0,
- 0x657D, 0, 0x657E, 0xF463, 0xF462, 0xD46D, 0xF464, 0xD46F,
-};
-static const unsigned short utf8_to_euc_E7BD[] = {
- 0, 0, 0, 0xD470, 0xD471, 0x6621, 0, 0xD472,
- 0, 0, 0, 0, 0x6622, 0x6623, 0x6624, 0xD473,
- 0x6625, 0x6626, 0xD474, 0xD475, 0x6628, 0x6627, 0, 0,
- 0x6629, 0, 0, 0xD476, 0xD477, 0xD478, 0, 0x662A,
- 0x662B, 0xD479, 0, 0xD47A, 0xD47B, 0xD47C, 0xD47D, 0x662E,
- 0x662C, 0x662D, 0x3A61, 0x3753, 0, 0xD47E, 0x4356, 0,
- 0x4833, 0xD521, 0x3D70, 0, 0, 0x474D, 0, 0x486D,
- 0x662F, 0x586D, 0, 0, 0, 0xD522, 0xD523, 0xD524,
-};
-static const unsigned short utf8_to_euc_E7BD_x0213[] = {
- 0, 0, 0, 0xF465, 0xF466, 0x6621, 0, 0x7A39,
- 0, 0, 0, 0, 0x6622, 0x6623, 0x6624, 0xF467,
- 0x6625, 0x6626, 0xF46A, 0xD475, 0x6628, 0x6627, 0, 0,
- 0x6629, 0, 0, 0xD476, 0xD477, 0xD478, 0, 0x662A,
- 0x662B, 0xF46C, 0, 0xF46D, 0xF46E, 0xD47C, 0xD47D, 0x662E,
- 0x662C, 0x662D, 0x3A61, 0x3753, 0, 0xF46F, 0x4356, 0,
- 0x4833, 0xD521, 0x3D70, 0, 0, 0x474D, 0, 0x486D,
- 0x662F, 0x586D, 0, 0, 0, 0xF470, 0xF471, 0xD524,
-};
-static const unsigned short utf8_to_euc_E7BE[] = {
- 0xD525, 0, 0x6630, 0x6632, 0, 0x4D65, 0x6631, 0x6634,
- 0x6633, 0, 0x4D53, 0xD526, 0x6635, 0xD527, 0x487E, 0xD528,
- 0xD529, 0xD52A, 0, 0, 0x6636, 0, 0xD52B, 0xD52C,
- 0, 0, 0x6639, 0, 0xD52D, 0x6638, 0x6637, 0,
- 0, 0xD52E, 0xD52F, 0x663A, 0x3732, 0, 0xD530, 0,
- 0x4122, 0x3541, 0xD531, 0, 0, 0xD532, 0x663E, 0x663B,
- 0, 0, 0x663C, 0, 0xD533, 0, 0x663F, 0,
- 0x6640, 0x663D, 0, 0, 0xD534, 0x3129, 0, 0xD535,
-};
-static const unsigned short utf8_to_euc_E7BE_x0213[] = {
- 0xD525, 0, 0x6630, 0x6632, 0, 0x4D65, 0x6631, 0x6634,
- 0x6633, 0, 0x4D53, 0xD526, 0x6635, 0xD527, 0x487E, 0xD528,
- 0xF473, 0x7A3B, 0, 0, 0x6636, 0, 0xF476, 0x7A3C,
- 0, 0, 0x6639, 0, 0xF477, 0x6638, 0x6637, 0,
- 0, 0, 0xD52F, 0x663A, 0x3732, 0, 0xD530, 0,
- 0x4122, 0x3541, 0xD531, 0, 0, 0xF478, 0x663E, 0x663B,
- 0, 0, 0x663C, 0, 0xD533, 0, 0x663F, 0,
- 0x6640, 0x663D, 0, 0, 0xD534, 0x3129, 0, 0x7A3D,
-};
-static const unsigned short utf8_to_euc_E7BF[] = {
- 0xD536, 0x3227, 0, 0xD537, 0, 0x6642, 0x6643, 0,
- 0xD538, 0, 0x6644, 0, 0x4D62, 0, 0xD539, 0xD53A,
- 0, 0, 0x3D2C, 0, 0x6646, 0x6645, 0, 0,
- 0, 0, 0, 0xD53B, 0, 0, 0, 0xD53C,
- 0x3F69, 0x6647, 0, 0xD53D, 0, 0xD53E, 0x6648, 0,
- 0xD53F, 0x6649, 0, 0x3465, 0xD540, 0, 0xD541, 0xD542,
- 0x344D, 0, 0xD543, 0x664A, 0, 0, 0, 0,
- 0, 0x664B, 0xD544, 0x4B5D, 0x4D63, 0xD545, 0xD546, 0xD547,
-};
-static const unsigned short utf8_to_euc_E7BF_x0213[] = {
- 0xD536, 0x3227, 0, 0xF47A, 0, 0x6642, 0x6643, 0,
- 0xD538, 0, 0x6644, 0, 0x4D62, 0, 0x7A3E, 0xF47B,
- 0, 0, 0x3D2C, 0, 0x6646, 0x6645, 0, 0,
- 0, 0, 0, 0x7A3F, 0, 0, 0, 0x7A40,
- 0x3F69, 0x6647, 0, 0xF47C, 0, 0xF47D, 0x6648, 0,
- 0xD53F, 0x6649, 0, 0x3465, 0x7A41, 0, 0x7A42, 0xF47E,
- 0x344D, 0, 0xF521, 0x664A, 0, 0, 0, 0,
- 0, 0x664B, 0x7A43, 0x4B5D, 0x4D63, 0xD545, 0xD546, 0xD547,
-};
-static const unsigned short utf8_to_euc_E880[] = {
- 0x4D54, 0x4F37, 0, 0x394D, 0x664E, 0x3C54, 0x664D, 0xD548,
- 0xD549, 0, 0xD54A, 0x664F, 0x3C29, 0xD54B, 0xD54C, 0xD54D,
- 0x4251, 0xD54E, 0x6650, 0xD54F, 0xD550, 0x394C, 0xD551, 0x4C57,
- 0x6651, 0x6652, 0, 0, 0x6653, 0xD552, 0xD553, 0xD554,
- 0xD555, 0x6654, 0, 0, 0xD556, 0, 0xD557, 0,
- 0x6655, 0, 0, 0, 0xD558, 0, 0xD559, 0,
- 0xD55A, 0, 0, 0x3C2A, 0xD55B, 0xD55C, 0x4C6D, 0xD55D,
- 0, 0xD55E, 0xD55F, 0x6657, 0xD560, 0x433F, 0xD561, 0x6656,
-};
-static const unsigned short utf8_to_euc_E880_x0213[] = {
- 0x4D54, 0x4F37, 0xF522, 0x394D, 0x664E, 0x3C54, 0x664D, 0xD548,
- 0xF524, 0, 0xF523, 0x664F, 0x3C29, 0xD54B, 0xF525, 0xD54D,
- 0x4251, 0xF526, 0x6650, 0xD54F, 0x7A45, 0x394C, 0xF527, 0x4C57,
- 0x6651, 0x6652, 0, 0, 0x6653, 0xD552, 0xD553, 0xD554,
- 0xD555, 0x6654, 0, 0, 0xF528, 0, 0x7A46, 0,
- 0x6655, 0, 0, 0, 0xF529, 0, 0xD559, 0,
- 0xF52A, 0, 0, 0x3C2A, 0xD55B, 0x7A47, 0x4C6D, 0x7A48,
- 0, 0xD55E, 0xD55F, 0x6657, 0x7A49, 0x433F, 0xD561, 0x6656,
-};
-static const unsigned short utf8_to_euc_E881[] = {
- 0xD562, 0, 0, 0, 0xD563, 0, 0x6659, 0,
- 0, 0, 0x6658, 0, 0, 0, 0, 0,
- 0, 0, 0x665A, 0, 0, 0, 0x403B, 0,
- 0x665B, 0, 0x665C, 0, 0, 0, 0x4A39, 0x665D,
- 0xD564, 0x416F, 0x665E, 0, 0xD565, 0, 0xD566, 0,
- 0x665F, 0, 0, 0, 0, 0xD567, 0, 0x4E7E,
- 0x6662, 0xD568, 0x6661, 0x6660, 0x4430, 0xD569, 0x6663, 0x3F26,
- 0, 0x6664, 0, 0, 0, 0x6665, 0x4F38, 0x6666,
-};
-static const unsigned short utf8_to_euc_E881_x0213[] = {
- 0xD562, 0, 0, 0xF52B, 0xD563, 0, 0x6659, 0,
- 0, 0, 0x6658, 0, 0, 0, 0, 0,
- 0, 0, 0x665A, 0, 0, 0, 0x403B, 0,
- 0x665B, 0, 0x665C, 0, 0, 0, 0x4A39, 0x665D,
- 0xD564, 0x416F, 0x665E, 0, 0xD565, 0, 0xF52C, 0,
- 0x665F, 0, 0, 0, 0, 0xD567, 0, 0x4E7E,
- 0x6662, 0xF52D, 0x6661, 0x6660, 0x4430, 0xF52E, 0x6663, 0x3F26,
- 0, 0x6664, 0, 0xF52F, 0, 0x6665, 0x4F38, 0x6666,
-};
-static const unsigned short utf8_to_euc_E882[] = {
- 0, 0xD56A, 0, 0, 0x6667, 0x6669, 0x6668, 0x4825,
- 0xD56B, 0x4679, 0, 0x4F3E, 0x4829, 0, 0xD56C, 0,
- 0, 0, 0, 0x666B, 0, 0, 0x3E53, 0,
- 0x492A, 0, 0x666C, 0x666A, 0xD56D, 0x344E, 0xD56E, 0,
- 0, 0x3854, 0x3B68, 0, 0, 0x486E, 0xD56F, 0xD570,
- 0, 0x382A, 0x4B43, 0xD571, 0x666F, 0x666D, 0, 0x394E,
- 0, 0x394F, 0x3069, 0, 0x3A68, 0, 0, 0,
- 0xD572, 0xD573, 0x4759, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E882_x0213[] = {
- 0, 0xD56A, 0, 0, 0x6667, 0x6669, 0x6668, 0x4825,
- 0xD56B, 0x4679, 0, 0x4F3E, 0x4829, 0, 0xD56C, 0,
- 0, 0, 0, 0x666B, 0, 0, 0x3E53, 0,
- 0x492A, 0xF530, 0x666C, 0x666A, 0xF531, 0x344E, 0xD56E, 0,
- 0, 0x3854, 0x3B68, 0, 0xF532, 0x486E, 0xD56F, 0xF533,
- 0, 0x382A, 0x4B43, 0xD571, 0x666F, 0x666D, 0, 0x394E,
- 0, 0x394F, 0x3069, 0, 0x3A68, 0, 0, 0,
- 0xF534, 0xD573, 0x4759, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E883[] = {
- 0, 0, 0, 0x305F, 0x6674, 0, 0x4340, 0,
- 0xD574, 0, 0, 0, 0x4758, 0xD575, 0x425B, 0xD576,
- 0, 0, 0xD577, 0, 0xD578, 0xD579, 0x6676, 0xD57A,
- 0xD57B, 0x6672, 0x6675, 0x6670, 0, 0x6673, 0x4B26, 0,
- 0xD57C, 0x3855, 0, 0, 0x307D, 0x6671, 0, 0,
- 0, 0, 0, 0, 0, 0xD57D, 0xD57E, 0x6678,
- 0xD621, 0x6679, 0xD622, 0xD623, 0x4639, 0, 0xD624, 0,
- 0x363B, 0xD625, 0xD626, 0, 0x6726, 0x473D, 0xD627, 0,
-};
-static const unsigned short utf8_to_euc_E883_x0213[] = {
- 0, 0, 0, 0x305F, 0x6674, 0xF536, 0x4340, 0,
- 0xD574, 0, 0x7A4A, 0, 0x4758, 0xD575, 0x425B, 0xD576,
- 0, 0, 0xD577, 0, 0xD578, 0xF537, 0x6676, 0x7A4B,
- 0xF538, 0x6672, 0x6675, 0x6670, 0, 0x6673, 0x4B26, 0,
- 0x7A4C, 0x3855, 0, 0, 0x307D, 0x6671, 0xF539, 0,
- 0, 0, 0, 0, 0, 0xD57D, 0xD57E, 0x6678,
- 0xD621, 0x6679, 0xD622, 0x7A4D, 0x4639, 0xF53C, 0xD624, 0,
- 0x363B, 0xD625, 0xD626, 0xF53D, 0x6726, 0x473D, 0xD627, 0,
-};
-static const unsigned short utf8_to_euc_E884[] = {
- 0, 0, 0x3B69, 0xD628, 0, 0x363C, 0x4048, 0x4F46,
- 0x4C2E, 0x6677, 0x4054, 0xD629, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xD62A, 0xD62B,
- 0xD62C, 0, 0x3553, 0x667A, 0xD62D, 0, 0xD62E, 0,
- 0xD62F, 0, 0, 0x667C, 0xD630, 0, 0, 0xD631,
- 0, 0x667B, 0, 0, 0xD632, 0, 0, 0x667D,
- 0xD633, 0x4326, 0, 0x473E, 0, 0xD634, 0, 0,
- 0, 0x4431, 0xD635, 0, 0xD636, 0, 0x6723, 0,
-};
-static const unsigned short utf8_to_euc_E884_x0213[] = {
- 0, 0, 0x3B69, 0xD628, 0, 0x363C, 0x4048, 0x4F46,
- 0x4C2E, 0x6677, 0x4054, 0xD629, 0, 0xF53B, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xF540, 0xD62B,
- 0x7A4E, 0, 0x3553, 0x667A, 0xD62D, 0, 0xF541, 0,
- 0xD62F, 0, 0, 0x667C, 0xF543, 0, 0, 0xF544,
- 0, 0x667B, 0, 0, 0xF545, 0, 0, 0x667D,
- 0xD633, 0x4326, 0, 0x473E, 0, 0xF53F, 0, 0,
- 0, 0x4431, 0xD635, 0, 0xD636, 0xF547, 0x6723, 0,
-};
-static const unsigned short utf8_to_euc_E885[] = {
- 0, 0, 0, 0, 0, 0xD637, 0x6722, 0xD638,
- 0, 0, 0xD639, 0x667E, 0xD63A, 0, 0x3F55, 0,
- 0x4965, 0x6725, 0xD63B, 0x6724, 0x3950, 0x4F53, 0, 0xD63C,
- 0, 0, 0, 0, 0, 0, 0, 0x6735,
- 0xD63D, 0xD63E, 0, 0, 0, 0x6729, 0x672A, 0xD63F,
- 0xD640, 0xD641, 0, 0x3C70, 0, 0xD642, 0x6728, 0xD643,
- 0x3978, 0x6727, 0, 0, 0x672B, 0, 0, 0xD644,
- 0x4432, 0x4A22, 0x4123, 0, 0, 0, 0, 0x425C,
-};
-static const unsigned short utf8_to_euc_E885_x0213[] = {
- 0, 0, 0, 0, 0, 0xD637, 0x6722, 0xD638,
- 0, 0, 0x7A4F, 0x667E, 0xD63A, 0, 0x3F55, 0,
- 0x4965, 0x6725, 0xD63B, 0x6724, 0x3950, 0x4F53, 0, 0xD63C,
- 0, 0, 0, 0, 0, 0, 0, 0x6735,
- 0x7A50, 0xD63E, 0, 0, 0, 0x6729, 0x672A, 0x7A51,
- 0x7A52, 0xF549, 0, 0x3C70, 0, 0x7A53, 0x6728, 0xD643,
- 0x3978, 0x6727, 0, 0, 0x672B, 0, 0, 0xD644,
- 0x4432, 0x4A22, 0x4123, 0, 0, 0, 0, 0x425C,
-};
-static const unsigned short utf8_to_euc_E886[] = {
- 0x672F, 0xD645, 0x6730, 0x672C, 0xD647, 0xD648, 0xD649, 0,
- 0x672D, 0, 0x672E, 0xD64A, 0, 0, 0xD64B, 0x3951,
- 0xD646, 0, 0, 0x6736, 0, 0x6732, 0xD64C, 0,
- 0xD64D, 0, 0x4966, 0xD64E, 0x4B6C, 0x4928, 0xD64F, 0,
- 0x6731, 0, 0xD650, 0x6734, 0x6733, 0, 0, 0,
- 0x4B44, 0x6737, 0, 0, 0, 0, 0xD651, 0,
- 0x6738, 0, 0xD652, 0x4137, 0xD653, 0x6739, 0, 0,
- 0x673B, 0, 0x673F, 0xD654, 0, 0x673C, 0x673A, 0x473F,
-};
-static const unsigned short utf8_to_euc_E886_x0213[] = {
- 0x672F, 0xF54B, 0x6730, 0x672C, 0xF54D, 0xF54E, 0xD649, 0,
- 0x672D, 0, 0x672E, 0xD64A, 0, 0, 0xD64B, 0x3951,
- 0xD646, 0, 0, 0x6736, 0, 0x6732, 0xD64C, 0,
- 0xF550, 0, 0x4966, 0xD64E, 0x4B6C, 0x4928, 0xD64F, 0,
- 0x6731, 0, 0xD650, 0x6734, 0x6733, 0, 0, 0,
- 0x4B44, 0x6737, 0, 0, 0, 0, 0xD651, 0,
- 0x6738, 0, 0xF551, 0x4137, 0xD653, 0x6739, 0, 0,
- 0x673B, 0, 0x673F, 0x7A54, 0, 0x673C, 0x673A, 0x473F,
-};
-static const unsigned short utf8_to_euc_E887[] = {
- 0x673D, 0, 0x673E, 0xD656, 0, 0xD657, 0x3232, 0,
- 0x6745, 0x6740, 0xD658, 0xD655, 0, 0x6741, 0xD659, 0xD65A,
- 0, 0x6742, 0, 0x4221, 0, 0xD65B, 0, 0xD65C,
- 0x6744, 0x6743, 0x6746, 0xD65D, 0, 0xD65E, 0xD65F, 0x6747,
- 0x6748, 0xD660, 0, 0x3F43, 0xD661, 0x3269, 0, 0x6749,
- 0x4E57, 0, 0x3C2B, 0xD662, 0xD663, 0x3D2D, 0, 0,
- 0xD664, 0xD665, 0xD666, 0x3B6A, 0x4357, 0xD667, 0xD668, 0,
- 0xD669, 0xD66A, 0x674A, 0x674B, 0x3131, 0xD66B, 0x674C, 0xD66C,
-};
-static const unsigned short utf8_to_euc_E887_x0213[] = {
- 0x673D, 0xF552, 0x673E, 0xF553, 0, 0xD657, 0x3232, 0,
- 0x6745, 0x6740, 0x7A55, 0xD655, 0, 0x6741, 0xD659, 0x7A56,
- 0, 0x6742, 0, 0x4221, 0, 0xD65B, 0xF554, 0x7A57,
- 0x6744, 0x6743, 0x6746, 0xF555, 0, 0xD65E, 0xD65F, 0x6747,
- 0x6748, 0xD660, 0, 0x3F43, 0xF557, 0x3269, 0, 0x6749,
- 0x4E57, 0, 0x3C2B, 0xD662, 0xF559, 0x3D2D, 0, 0,
- 0xD664, 0xD665, 0xD666, 0x3B6A, 0x4357, 0xD667, 0xD668, 0,
- 0xD669, 0xD66A, 0x674A, 0x674B, 0x3131, 0xF55B, 0x674C, 0xF55C,
-};
-static const unsigned short utf8_to_euc_E888[] = {
- 0xD66D, 0x674D, 0x674E, 0xD66E, 0, 0x674F, 0, 0x6750,
- 0x363D, 0x5A2A, 0x6751, 0, 0x4065, 0x6752, 0x3C4B, 0xD66F,
- 0x6753, 0, 0x5030, 0xD670, 0xD671, 0, 0x6754, 0x4A5E,
- 0x345C, 0xD672, 0xD673, 0x4124, 0x3D58, 0xD674, 0x4971, 0x3D2E,
- 0, 0xD675, 0xD676, 0, 0, 0, 0, 0,
- 0xD677, 0x6755, 0x3952, 0x6756, 0x484C, 0, 0x6764, 0,
- 0, 0, 0xD678, 0x6758, 0xD679, 0x4249, 0x4775, 0x383F,
- 0x6757, 0x4125, 0xD67A, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E888_x0213[] = {
- 0xD66D, 0x674D, 0x674E, 0xD66E, 0xF55E, 0x674F, 0, 0x6750,
- 0x363D, 0x5A2A, 0x6751, 0, 0x4065, 0x6752, 0x3C4B, 0xD66F,
- 0x6753, 0, 0x5030, 0xD670, 0xD671, 0, 0x6754, 0x4A5E,
- 0x345C, 0xF560, 0xD673, 0x4124, 0x3D58, 0xD674, 0x4971, 0x3D2E,
- 0, 0xF561, 0xF562, 0, 0, 0, 0, 0,
- 0xD677, 0x6755, 0x3952, 0x6756, 0x484C, 0, 0x6764, 0,
- 0, 0, 0xF564, 0x6758, 0xF565, 0x4249, 0x4775, 0x383F,
- 0x6757, 0x4125, 0xD67A, 0, 0xF566, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E889[] = {
- 0x6759, 0, 0, 0xD67B, 0xD67C, 0xD67D, 0xD67E, 0x447A,
- 0, 0, 0, 0xD721, 0, 0, 0xD722, 0xD723,
- 0, 0xD724, 0, 0, 0, 0, 0xD725, 0,
- 0x675B, 0x675A, 0x675D, 0, 0xD726, 0x675C, 0, 0x675E,
- 0xD727, 0, 0x6760, 0xD728, 0x675F, 0, 0x344F, 0xD729,
- 0x6761, 0, 0x6762, 0x6763, 0, 0xD72A, 0x3A31, 0x4E49,
- 0, 0x6765, 0x3F27, 0, 0xD72B, 0, 0x3170, 0x6766,
- 0x6767, 0, 0, 0xD72C, 0, 0xD72D, 0x6768, 0xD72E,
-};
-static const unsigned short utf8_to_euc_E889_x0213[] = {
- 0x6759, 0, 0, 0xD67B, 0xD67C, 0xF569, 0xF567, 0x447A,
- 0, 0xF568, 0, 0xF56B, 0, 0, 0xD722, 0xF56D,
- 0, 0xD724, 0, 0, 0, 0, 0xD725, 0xF56F,
- 0x675B, 0x675A, 0x675D, 0, 0xF571, 0x675C, 0, 0x675E,
- 0x7A5B, 0, 0x6760, 0xF572, 0x675F, 0, 0x344F, 0xD729,
- 0x6761, 0, 0x6762, 0x6763, 0, 0xD72A, 0x3A31, 0x4E49,
- 0, 0x6765, 0x3F27, 0, 0x7A5C, 0, 0x3170, 0x6766,
- 0x6767, 0xF576, 0, 0xD72C, 0, 0xF578, 0x6768, 0xF579,
-};
-static const unsigned short utf8_to_euc_E88A[] = {
- 0xD72F, 0xD730, 0, 0xD731, 0xD732, 0, 0, 0xD733,
- 0, 0xD734, 0xD735, 0x3072, 0, 0x6769, 0xD736, 0,
- 0, 0xD737, 0x676A, 0, 0xD738, 0, 0xD739, 0,
- 0xD73A, 0x4967, 0xD73B, 0xD73C, 0, 0x3C47, 0, 0x676C,
- 0xD73D, 0xD73E, 0, 0xD73F, 0xD740, 0x3329, 0x3032, 0xD741,
- 0xD742, 0xD743, 0xD744, 0x676B, 0x676E, 0x474E, 0xD745, 0x3F44,
- 0xD746, 0x3256, 0xD747, 0x4B27, 0xD748, 0, 0, 0xD749,
- 0x375D, 0x365C, 0xD74A, 0x676D, 0xD74B, 0x326A, 0xD74C, 0xD74D,
-};
-static const unsigned short utf8_to_euc_E88A_x0213[] = {
- 0xD72F, 0xD730, 0, 0xF57A, 0xD732, 0, 0, 0xD733,
- 0, 0xD734, 0xF57B, 0x3072, 0, 0x6769, 0x7A5E, 0,
- 0, 0xD737, 0x676A, 0xF57C, 0xD738, 0, 0xD739, 0,
- 0xD73A, 0x4967, 0xD73B, 0xD73C, 0, 0x3C47, 0, 0x676C,
- 0xD73D, 0x7A5F, 0, 0x7A60, 0x7A61, 0x3329, 0x3032, 0xF57D,
- 0xF57E, 0x7A62, 0xD744, 0x676B, 0x676E, 0x474E, 0x7A63, 0x3F44,
- 0xD746, 0x3256, 0xF621, 0x4B27, 0xF622, 0, 0, 0x7A64,
- 0x375D, 0x365C, 0xF623, 0x676D, 0xF624, 0x326A, 0x7A65, 0x7A66,
-};
-static const unsigned short utf8_to_euc_E88B[] = {
- 0, 0, 0, 0, 0, 0x3423, 0xD74E, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xD74F, 0x3171, 0x6772, 0x4E6A, 0x425D, 0xD750, 0, 0x4944,
- 0, 0x677E, 0xD751, 0x3257, 0x677C, 0, 0x677A, 0x6771,
- 0xD752, 0x676F, 0xD753, 0x6770, 0xD754, 0x3C63, 0x366C, 0x4377,
- 0xD755, 0, 0xD756, 0x4651, 0, 0xD757, 0, 0xD758,
- 0, 0x3151, 0, 0x6774, 0x6773, 0, 0xD759, 0xD75A,
- 0, 0x6779, 0x6775, 0x6778, 0, 0xD75B, 0xD75C, 0,
-};
-static const unsigned short utf8_to_euc_E88B_x0213[] = {
- 0, 0, 0, 0, 0, 0x3423, 0x7A67, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xD74F, 0x3171, 0x6772, 0x4E6A, 0x425D, 0x7A68, 0, 0x4944,
- 0, 0x677E, 0xD751, 0x3257, 0x677C, 0, 0x677A, 0x6771,
- 0xD752, 0x676F, 0xF625, 0x6770, 0xD754, 0x3C63, 0x366C, 0x4377,
- 0xF626, 0, 0xD756, 0x4651, 0, 0xD757, 0, 0xD758,
- 0, 0x3151, 0, 0x6774, 0x6773, 0, 0xD759, 0xF627,
- 0, 0x6779, 0x6775, 0x6778, 0, 0x7A69, 0x7A6A, 0,
-};
-static const unsigned short utf8_to_euc_E88C[] = {
- 0xD75D, 0xD75E, 0x4C50, 0x6777, 0x3258, 0x337D, 0x677B, 0xD75F,
- 0xD760, 0x677D, 0xD761, 0xD762, 0, 0, 0x3754, 0,
- 0, 0, 0, 0, 0, 0, 0x6823, 0x682C,
- 0x682D, 0, 0, 0xD764, 0x302B, 0xD765, 0xD766, 0xD767,
- 0, 0xD768, 0xD769, 0x6834, 0, 0, 0, 0,
- 0x3071, 0, 0, 0x682B, 0xD76A, 0xD76B, 0xD76C, 0x682A,
- 0xD76D, 0x6825, 0x6824, 0xD76E, 0x6822, 0x6821, 0x4363, 0xD76F,
- 0x427B, 0x6827, 0xD770, 0, 0xD771, 0xD772, 0, 0,
-};
-static const unsigned short utf8_to_euc_E88C_x0213[] = {
- 0x7A6B, 0x7A6C, 0x4C50, 0x6777, 0x3258, 0x337D, 0x677B, 0xF628,
- 0xF629, 0x677D, 0xD761, 0xD762, 0xF62A, 0, 0x3754, 0,
- 0, 0, 0, 0, 0, 0, 0x6823, 0x682C,
- 0x682D, 0, 0, 0xF62C, 0x302B, 0xF62D, 0xD766, 0xD767,
- 0, 0xD768, 0x7A6E, 0x6834, 0, 0, 0, 0,
- 0x3071, 0, 0, 0x682B, 0xD76A, 0x7A6F, 0xD76C, 0x682A,
- 0xF62E, 0x6825, 0x6824, 0xD76E, 0x6822, 0x6821, 0x4363, 0xD76F,
- 0x427B, 0x6827, 0x7A70, 0, 0xF62F, 0xD772, 0, 0,
-};
-static const unsigned short utf8_to_euc_E88D[] = {
- 0x6826, 0, 0xD773, 0xD774, 0xD775, 0x6829, 0, 0xD776,
- 0, 0x4170, 0x3755, 0, 0, 0xD777, 0xD778, 0x3141,
- 0x6828, 0xD779, 0x3953, 0xD83E, 0xD763, 0xD77A, 0xD77B, 0xD77C,
- 0x4171, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xF45F, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xD77D, 0, 0, 0x683A, 0, 0x683B, 0, 0x3259,
- 0xD77E, 0, 0, 0x322E, 0x6838, 0xD821, 0, 0xD822,
-};
-static const unsigned short utf8_to_euc_E88D_x0213[] = {
- 0x6826, 0, 0xD773, 0x7A71, 0xF630, 0x6829, 0, 0x7A72,
- 0, 0x4170, 0x3755, 0, 0, 0xD777, 0xD778, 0x3141,
- 0x6828, 0x7A73, 0x3953, 0xD83E, 0xF62B, 0x7A74, 0xD77B, 0xF631,
- 0x4171, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x7A6D, 0xAE4A, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xD77D, 0, 0, 0x683A, 0, 0x683B, 0, 0x3259,
- 0xD77E, 0, 0, 0x322E, 0x6838, 0x7A75, 0, 0xF633,
-};
-static const unsigned short utf8_to_euc_E88E[] = {
- 0xD823, 0, 0xD824, 0, 0xD825, 0x682E, 0xD826, 0x6836,
- 0, 0x683D, 0x6837, 0, 0, 0xD827, 0x6835, 0,
- 0, 0, 0xD828, 0x6776, 0xD829, 0xD82A, 0x6833, 0,
- 0xD82B, 0xD82C, 0x682F, 0xD82D, 0xD82E, 0xD82F, 0x3450, 0x6831,
- 0x683C, 0, 0x6832, 0, 0, 0, 0xD830, 0xD831,
- 0x683E, 0xD832, 0x6830, 0x477C, 0xD833, 0xD84C, 0, 0,
- 0, 0x4D69, 0, 0, 0, 0x6839, 0, 0,
- 0, 0, 0, 0, 0, 0x684F, 0xD834, 0xD835,
-};
-static const unsigned short utf8_to_euc_E88E_x0213[] = {
- 0xD823, 0, 0xD824, 0, 0xD825, 0x682E, 0x7A76, 0x6836,
- 0, 0x683D, 0x6837, 0, 0, 0xF636, 0x6835, 0,
- 0, 0, 0x7A77, 0x6776, 0xF637, 0xF638, 0x6833, 0,
- 0x7A78, 0xD82C, 0x682F, 0xF639, 0xD82E, 0xF63A, 0x3450, 0x6831,
- 0x683C, 0, 0x6832, 0, 0, 0, 0xD830, 0x7A79,
- 0x683E, 0x7A7A, 0x6830, 0x477C, 0xD833, 0xD84C, 0, 0,
- 0, 0x4D69, 0, 0, 0, 0x6839, 0, 0,
- 0, 0, 0, 0, 0, 0x684F, 0xD834, 0x7A7B,
-};
-static const unsigned short utf8_to_euc_E88F[] = {
- 0xD836, 0x6847, 0, 0, 0, 0x3F7B, 0, 0xD837,
- 0, 0xD838, 0x3546, 0, 0x365D, 0, 0x6842, 0xD839,
- 0xD83A, 0xD83B, 0, 0x325B, 0xD83C, 0, 0x3E54, 0,
- 0x6845, 0, 0, 0, 0x3A5A, 0xD83D, 0, 0x4551,
- 0x684A, 0, 0, 0, 0, 0, 0, 0,
- 0xD83F, 0x4A6E, 0xD840, 0x6841, 0, 0, 0, 0x325A,
- 0x3856, 0x4929, 0x684B, 0, 0x683F, 0, 0xD841, 0x6848,
- 0xD842, 0xD843, 0, 0x6852, 0xD844, 0x6843, 0, 0,
-};
-static const unsigned short utf8_to_euc_E88F_x0213[] = {
- 0x7A7C, 0x6847, 0, 0, 0, 0x3F7B, 0, 0x7A7D,
- 0, 0xF63B, 0x3546, 0, 0x365D, 0, 0x6842, 0x7A7E,
- 0xF63C, 0x7B21, 0, 0x325B, 0xF63D, 0, 0x3E54, 0,
- 0x6845, 0, 0, 0, 0x3A5A, 0xF63E, 0, 0x4551,
- 0x684A, 0x7B22, 0, 0, 0, 0xF63F, 0, 0,
- 0xD83F, 0x4A6E, 0x7B23, 0x6841, 0, 0, 0, 0x325A,
- 0x3856, 0x4929, 0x684B, 0, 0x683F, 0, 0, 0x6848,
- 0xD842, 0xF640, 0, 0x6852, 0xD844, 0x6843, 0, 0,
-};
-static const unsigned short utf8_to_euc_E890[] = {
- 0, 0xD845, 0, 0x6844, 0x463A, 0, 0xD846, 0x6849,
- 0, 0, 0xD847, 0x6846, 0x4B28, 0x684C, 0x3060, 0xD848,
- 0, 0xD849, 0, 0x6840, 0, 0xD84A, 0, 0,
- 0, 0xD84B, 0, 0, 0, 0, 0, 0,
- 0x684E, 0, 0x684D, 0, 0, 0, 0, 0,
- 0, 0x476B, 0x6854, 0, 0x685F, 0, 0, 0xD84D,
- 0, 0x337E, 0, 0, 0, 0x6862, 0, 0,
- 0x6850, 0xD84E, 0, 0, 0x6855, 0x4D6E, 0, 0,
-};
-static const unsigned short utf8_to_euc_E890_x0213[] = {
- 0, 0x7B24, 0, 0x6844, 0x463A, 0, 0x7B25, 0x6849,
- 0, 0, 0x7B26, 0x6846, 0x4B28, 0x684C, 0x3060, 0xF641,
- 0, 0xF642, 0, 0x6840, 0, 0xF643, 0, 0xF645,
- 0, 0xD84B, 0, 0, 0, 0, 0, 0,
- 0x684E, 0, 0x684D, 0, 0, 0, 0, 0,
- 0, 0x476B, 0x6854, 0, 0x685F, 0, 0, 0xD84D,
- 0, 0x337E, 0, 0, 0, 0x6862, 0, 0,
- 0x6850, 0xF646, 0, 0, 0x6855, 0x4D6E, 0, 0,
-};
-static const unsigned short utf8_to_euc_E891[] = {
- 0, 0, 0, 0, 0, 0xD84F, 0x685E, 0xD850,
- 0xD851, 0x4D55, 0xD852, 0, 0, 0xD853, 0x4E2A, 0xD854,
- 0, 0xD855, 0xD856, 0, 0, 0, 0xD857, 0x4378,
- 0xD858, 0xD859, 0xD85A, 0x336B, 0xD85B, 0, 0, 0,
- 0xD85C, 0x4972, 0x6864, 0x4621, 0xD85D, 0xD85E, 0x3031, 0xD85F,
- 0, 0x685D, 0xD860, 0x6859, 0x4172, 0x6853, 0x685B, 0x6860,
- 0xD861, 0x472C, 0, 0xD862, 0xD863, 0x302A, 0xD864, 0x6858,
- 0xD865, 0x6861, 0x4978, 0, 0xD866, 0xD867, 0, 0,
-};
-static const unsigned short utf8_to_euc_E891_x0213[] = {
- 0, 0, 0, 0, 0, 0xD84F, 0x685E, 0xD850,
- 0x7B28, 0x4D55, 0xF647, 0, 0, 0xD853, 0x4E2A, 0xF648,
- 0, 0xF649, 0xF64A, 0, 0, 0, 0xD857, 0x4378,
- 0xD858, 0xF64B, 0xF64C, 0x336B, 0xF64D, 0, 0, 0x7B29,
- 0xD85C, 0x4972, 0x6864, 0x4621, 0xD85D, 0xF64F, 0x3031, 0xD85F,
- 0, 0x685D, 0xD860, 0x6859, 0x4172, 0x6853, 0x685B, 0x6860,
- 0x7B2A, 0x472C, 0, 0x7B2B, 0xD863, 0x302A, 0xF650, 0x6858,
- 0xF651, 0x6861, 0x4978, 0, 0xF652, 0xD867, 0, 0,
-};
-static const unsigned short utf8_to_euc_E892[] = {
- 0, 0xD868, 0x685C, 0, 0x6857, 0xD869, 0, 0,
- 0, 0, 0, 0x3E55, 0, 0, 0, 0,
- 0x3D2F, 0, 0xD86A, 0xD86B, 0x3C2C, 0xD86C, 0, 0,
- 0, 0x4C58, 0, 0, 0x4947, 0, 0xD86D, 0x6867,
- 0, 0x6870, 0, 0, 0, 0, 0xD86E, 0,
- 0xD86F, 0xD870, 0xD871, 0, 0, 0x685A, 0, 0xD872,
- 0, 0xD873, 0x3377, 0, 0xD874, 0, 0, 0,
- 0x3E78, 0x6865, 0xD875, 0x686A, 0x4173, 0xD876, 0xD877, 0x6866,
-};
-static const unsigned short utf8_to_euc_E892_x0213[] = {
- 0, 0xF653, 0x685C, 0, 0x6857, 0x7B2C, 0, 0,
- 0, 0, 0, 0x3E55, 0, 0, 0, 0,
- 0x3D2F, 0, 0xD86A, 0xD86B, 0x3C2C, 0xD86C, 0, 0xF656,
- 0, 0x4C58, 0, 0, 0x4947, 0, 0x7B2D, 0x6867,
- 0, 0x6870, 0, 0, 0, 0, 0xF657, 0,
- 0xD86F, 0xD870, 0xD871, 0, 0, 0x685A, 0, 0x7B2E,
- 0, 0xD873, 0x3377, 0, 0x7B2F, 0, 0, 0,
- 0x3E78, 0x6865, 0x7B30, 0x686A, 0x4173, 0xD876, 0xF658, 0x6866,
-};
-static const unsigned short utf8_to_euc_E893[] = {
- 0xD878, 0x686D, 0xD879, 0, 0x435F, 0, 0x686E, 0xD87A,
- 0xD87B, 0x4D56, 0x6863, 0x3338, 0xD87C, 0x6869, 0, 0xD87D,
- 0x686C, 0x4C2C, 0, 0xD87E, 0, 0, 0x686F, 0,
- 0, 0x6868, 0x686B, 0, 0xD921, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xD922,
- 0, 0, 0xD923, 0, 0x4B29, 0, 0x4F21, 0xD924,
- 0xD925, 0xD926, 0xD927, 0, 0x6873, 0, 0, 0xD928,
- 0, 0, 0xD92A, 0xD92B, 0x687A, 0xD92C, 0, 0x6872,
-};
-static const unsigned short utf8_to_euc_E893_x0213[] = {
- 0x7B31, 0x686D, 0x7B32, 0, 0x435F, 0, 0x686E, 0xD87A,
- 0xD87B, 0x4D56, 0x6863, 0x3338, 0xD87C, 0x6869, 0xF65A, 0xF65B,
- 0x686C, 0x4C2C, 0, 0xF65C, 0, 0, 0x686F, 0,
- 0, 0x6868, 0x686B, 0, 0xF655, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xF65E,
- 0, 0, 0xF65F, 0, 0x4B29, 0, 0x4F21, 0xF660,
- 0xF661, 0xF662, 0xD927, 0, 0x6873, 0, 0, 0xD928,
- 0, 0, 0xF663, 0xD92B, 0x687A, 0xF664, 0, 0x6872,
-};
-static const unsigned short utf8_to_euc_E894[] = {
- 0x3C43, 0, 0xD92D, 0xD92E, 0, 0, 0x6851, 0xD92F,
- 0, 0, 0, 0, 0xD930, 0, 0xD931, 0,
- 0xD932, 0x4A4E, 0, 0x4C22, 0x6879, 0x6878, 0, 0x6874,
- 0x6875, 0, 0x3136, 0, 0xD933, 0, 0xD934, 0x6877,
- 0, 0x6871, 0xD935, 0xD936, 0xD937, 0xD938, 0x4455, 0xD939,
- 0, 0, 0xD93A, 0xD93B, 0x6876, 0x307E, 0, 0xD93C,
- 0, 0, 0xD929, 0xD93D, 0xD93E, 0x4222, 0xD93F, 0,
- 0, 0, 0, 0, 0, 0x4A43, 0, 0xD940,
-};
-static const unsigned short utf8_to_euc_E894_x0213[] = {
- 0x3C43, 0, 0xD92D, 0xD92E, 0, 0, 0x6851, 0xD92F,
- 0, 0, 0, 0, 0xF665, 0, 0xD931, 0,
- 0xD932, 0x4A4E, 0, 0x4C22, 0x6879, 0x6878, 0, 0x6874,
- 0x6875, 0, 0x3136, 0xF666, 0xD933, 0, 0x7B35, 0x6877,
- 0, 0x6871, 0xD935, 0x7B36, 0xF667, 0xF668, 0x4455, 0xD939,
- 0, 0, 0xD93A, 0xF669, 0x6876, 0x307E, 0, 0x7B37,
- 0, 0, 0x7B34, 0xD93D, 0xF66A, 0x4222, 0xD93F, 0,
- 0, 0, 0, 0, 0, 0x4A43, 0xF66F, 0xD940,
-};
-static const unsigned short utf8_to_euc_E895[] = {
- 0x687B, 0x6921, 0, 0x4859, 0, 0, 0xD941, 0,
- 0x687E, 0x3E56, 0x3C49, 0x6923, 0, 0, 0x363E, 0xD942,
- 0xD943, 0xD944, 0xD945, 0xD946, 0, 0x6924, 0xD947, 0x4979,
- 0x687D, 0xD948, 0x6856, 0, 0xD949, 0xD94A, 0xD94B, 0xD94C,
- 0xD94D, 0xD94E, 0xD94F, 0x687C, 0xD950, 0, 0, 0,
- 0x4F4F, 0x4622, 0x4973, 0xD951, 0, 0x692B, 0, 0xD952,
- 0, 0, 0, 0, 0, 0, 0, 0x6931,
- 0, 0xD953, 0xD954, 0xD955, 0, 0xD956, 0x6932, 0xD957,
-};
-static const unsigned short utf8_to_euc_E895_x0213[] = {
- 0x687B, 0x6921, 0, 0x4859, 0, 0, 0xD941, 0,
- 0x687E, 0x3E56, 0x3C49, 0x6923, 0, 0, 0x363E, 0xF66B,
- 0xD943, 0xF670, 0xD945, 0xF671, 0, 0x6924, 0xD947, 0x4979,
- 0x687D, 0x7B38, 0x6856, 0, 0xD949, 0xD94A, 0xF672, 0xD94C,
- 0xD94D, 0xF673, 0xF674, 0x687C, 0x7B39, 0, 0, 0,
- 0x4F4F, 0x4622, 0x4973, 0, 0, 0x692B, 0, 0xF66C,
- 0, 0, 0, 0, 0, 0, 0, 0x6931,
- 0, 0xD953, 0x7B3C, 0xF676, 0, 0xF677, 0x6932, 0xF678,
-};
-static const unsigned short utf8_to_euc_E896[] = {
- 0x6925, 0xD958, 0, 0, 0x4776, 0xD959, 0xD95A, 0x692F,
- 0x6927, 0xD95B, 0x6929, 0xD95C, 0xD95D, 0, 0, 0xD95E,
- 0x6933, 0x6928, 0, 0xD95F, 0x692C, 0, 0, 0x3172,
- 0xD960, 0x4665, 0, 0x692D, 0x6930, 0xD961, 0, 0xD962,
- 0xD963, 0, 0xD964, 0, 0x6926, 0xD965, 0x4126, 0xD966,
- 0x692A, 0x3B27, 0x3F45, 0x3730, 0x4C74, 0xD974, 0x4C79, 0x3D72,
- 0xF461, 0, 0, 0, 0xD967, 0, 0xD968, 0xD969,
- 0xD96A, 0x6937, 0x6935, 0, 0xD96B, 0xD96C, 0xD96D, 0xD96E,
-};
-static const unsigned short utf8_to_euc_E896_x0213[] = {
- 0x6925, 0xF679, 0, 0, 0x4776, 0xD959, 0xF67A, 0x692F,
- 0x6927, 0xD95B, 0x6929, 0xD95C, 0x7B3D, 0, 0, 0x7B3E,
- 0x6933, 0x6928, 0, 0xF67B, 0x692C, 0, 0, 0x3172,
- 0xD960, 0x4665, 0, 0x692D, 0x6930, 0xF67C, 0, 0xF67D,
- 0xD963, 0, 0x7B3F, 0, 0x6926, 0xD965, 0x4126, 0xD966,
- 0x692A, 0x3B27, 0x3F45, 0x3730, 0x4C74, 0x7B3B, 0x4C79, 0x3D72,
- 0x7B40, 0, 0, 0, 0xD967, 0, 0xD968, 0xF723,
- 0xD96A, 0x6937, 0x6935, 0, 0xF724, 0xD96C, 0xD96D, 0xD96E,
-};
-static const unsigned short utf8_to_euc_E897[] = {
- 0, 0x4F4E, 0xD96F, 0, 0, 0, 0, 0xD970,
- 0, 0x6934, 0xD971, 0xD972, 0, 0x4D75, 0xD973, 0x6936,
- 0x6938, 0, 0, 0, 0, 0x6939, 0, 0,
- 0xD975, 0, 0xD976, 0, 0x693C, 0x693A, 0, 0xD977,
- 0xD978, 0, 0, 0, 0x4623, 0x693B, 0xD979, 0,
- 0xD97A, 0x484D, 0x692E, 0, 0, 0xD97B, 0, 0,
- 0, 0, 0, 0xD97C, 0, 0, 0xD97D, 0x3D73,
- 0, 0x693D, 0x6942, 0x4174, 0xD97E, 0, 0x6941, 0xDA21,
-};
-static const unsigned short utf8_to_euc_E897_x0213[] = {
- 0, 0x4F4E, 0xD96F, 0, 0, 0, 0, 0xF725,
- 0, 0x6934, 0xF726, 0x7B41, 0, 0x4D75, 0x7B42, 0x6936,
- 0x6938, 0, 0, 0, 0, 0x6939, 0, 0,
- 0xF727, 0xF728, 0xD976, 0, 0x693C, 0x693A, 0, 0xF729,
- 0xD978, 0xF72A, 0, 0, 0x4623, 0x693B, 0xF72B, 0,
- 0xD97A, 0x484D, 0x692E, 0, 0, 0x7B43, 0, 0,
- 0, 0, 0, 0xD97C, 0, 0, 0xF72C, 0x3D73,
- 0, 0x693D, 0x6942, 0x4174, 0xD97E, 0, 0x6941, 0x7B45,
-};
-static const unsigned short utf8_to_euc_E898[] = {
- 0xDA22, 0, 0x6922, 0, 0xDA23, 0xDA24, 0x6943, 0x4149,
- 0, 0, 0x693E, 0x6940, 0, 0xDA25, 0xDA26, 0,
- 0xDA27, 0xDA28, 0xDA29, 0x693F, 0, 0, 0x5D31, 0x5D22,
- 0xDA2A, 0xDA2B, 0x6945, 0xDA2C, 0, 0, 0xDA2D, 0,
- 0, 0xDA2E, 0x6944, 0, 0, 0, 0, 0xDA2F,
- 0, 0xDA30, 0, 0, 0, 0x4D76, 0, 0x623C,
- 0x6946, 0, 0, 0, 0, 0, 0xDA31, 0,
- 0xDA32, 0, 0xDA33, 0, 0xDA34, 0xDA35, 0, 0x6947,
-};
-static const unsigned short utf8_to_euc_E898_x0213[] = {
- 0xF72D, 0, 0x6922, 0, 0x7B46, 0x7B47, 0x6943, 0x4149,
- 0, 0, 0x693E, 0x6940, 0, 0xDA25, 0xDA26, 0,
- 0x7B48, 0xF72E, 0x7B44, 0x693F, 0, 0, 0x5D31, 0x5D22,
- 0x7B4A, 0xDA2B, 0x6945, 0xDA2C, 0, 0, 0xF72F, 0,
- 0, 0xF730, 0x6944, 0, 0xF731, 0, 0, 0xF732,
- 0, 0x7B4B, 0, 0, 0, 0x4D76, 0, 0x623C,
- 0x6946, 0, 0, 0, 0, 0, 0xDA31, 0,
- 0x7B4C, 0xF734, 0xDA33, 0, 0xF735, 0xDA35, 0, 0x6947,
-};
-static const unsigned short utf8_to_euc_E899[] = {
- 0xDA36, 0xB866, 0xDA37, 0, 0, 0, 0xDA38, 0,
- 0, 0, 0, 0, 0, 0x6948, 0x3857, 0,
- 0x3554, 0, 0xDA39, 0xDA3A, 0x694A, 0x515D, 0xDA3B, 0xDA3C,
- 0xDA3D, 0xDA3E, 0x3575, 0, 0x4E3A, 0xDA3F, 0x3673, 0x694B,
- 0xDA40, 0xDA41, 0xDA42, 0xDA43, 0xDA44, 0, 0, 0x694C,
- 0, 0xDA45, 0, 0x436E, 0xDA46, 0, 0, 0xDA47,
- 0, 0x694D, 0, 0, 0, 0xDA48, 0xDA49, 0xDA4A,
- 0, 0x467A, 0xDA4B, 0x303A, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E899_x0213[] = {
- 0xF737, 0x2F68, 0xDA37, 0, 0, 0, 0xDA38, 0,
- 0, 0, 0, 0, 0, 0x6948, 0x3857, 0,
- 0x3554, 0, 0xDA39, 0xF739, 0x694A, 0x515D, 0xF73A, 0x7B4D,
- 0xDA3D, 0xDA3E, 0x3575, 0x7B4E, 0x4E3A, 0xDA3F, 0x3673, 0x694B,
- 0xDA40, 0xDA41, 0x7B50, 0xDA43, 0xDA44, 0, 0, 0x694C,
- 0, 0xDA45, 0, 0x436E, 0x7B52, 0, 0, 0xF73B,
- 0, 0x694D, 0, 0, 0, 0x7B53, 0xDA49, 0xF73C,
- 0, 0x467A, 0xF73D, 0x303A, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E89A[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xDA6D, 0, 0x3263, 0x6952, 0x6953, 0xDA4C, 0, 0,
- 0, 0xDA4D, 0, 0x694E, 0, 0x3B3D, 0xDA4E, 0,
- 0xDA4F, 0, 0xDA50, 0, 0xDA51, 0, 0, 0,
- 0, 0xDA52, 0, 0x694F, 0x4742, 0, 0xDA53, 0xDA54,
- 0xDA55, 0x6950, 0x6951, 0x695B, 0, 0xDA56, 0, 0x6955,
- 0x6958, 0xDA57, 0, 0xDA58, 0xDA59, 0xDA5A, 0x6954, 0xDA5B,
- 0xDA5C, 0xDA5D, 0, 0, 0, 0, 0, 0xDA5E,
-};
-static const unsigned short utf8_to_euc_E89A_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0xF73E,
- 0xDA6D, 0xF73F, 0x3263, 0x6952, 0x6953, 0xF740, 0, 0,
- 0, 0xF741, 0, 0x694E, 0, 0x3B3D, 0xDA4E, 0,
- 0x7B54, 0, 0xDA50, 0, 0xF742, 0xF743, 0, 0,
- 0, 0xDA52, 0, 0x694F, 0x4742, 0, 0xDA53, 0xDA54,
- 0xF744, 0x6950, 0x6951, 0x695B, 0, 0xDA56, 0, 0x6955,
- 0x6958, 0xF746, 0, 0xF747, 0xDA59, 0xDA5A, 0x6954, 0xDA5B,
- 0x7B55, 0xDA5D, 0, 0, 0, 0, 0, 0xDA5E,
-};
-static const unsigned short utf8_to_euc_E89B[] = {
- 0xDA5F, 0xDA60, 0, 0xDA61, 0x6956, 0xDA62, 0x6957, 0x3C58,
- 0, 0x6959, 0, 0x4341, 0, 0x3756, 0x3342, 0,
- 0, 0xDA63, 0xDA64, 0, 0x695C, 0xDA65, 0, 0xDA66,
- 0, 0x333F, 0xDA67, 0x6961, 0xDA68, 0, 0x695D, 0x6960,
- 0xDA69, 0, 0, 0xDA6A, 0x483A, 0xDA6B, 0, 0xDA6C,
- 0, 0x695E, 0, 0, 0x695F, 0x4948, 0x485A, 0x6962,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x427D, 0x696C, 0xDA6E, 0x6968, 0xDA6F, 0xDA70, 0x326B, 0,
-};
-static const unsigned short utf8_to_euc_E89B_x0213[] = {
- 0xDA5F, 0xF748, 0, 0xF749, 0x6956, 0xDA62, 0x6957, 0x3C58,
- 0, 0x6959, 0, 0x4341, 0, 0x3756, 0x3342, 0,
- 0, 0xF74A, 0xDA64, 0, 0x695C, 0xF74B, 0, 0xF74C,
- 0, 0x333F, 0xDA67, 0x6961, 0xDA68, 0, 0x695D, 0x6960,
- 0xDA69, 0, 0, 0xF74D, 0x483A, 0xDA6B, 0xF74E, 0xDA6C,
- 0, 0x695E, 0, 0, 0x695F, 0x4948, 0x485A, 0x6962,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x427D, 0x696C, 0x7B56, 0x6968, 0x7B57, 0x7B58, 0x326B, 0,
-};
-static const unsigned short utf8_to_euc_E89C[] = {
- 0x6966, 0, 0x4B2A, 0x6967, 0xDA71, 0xDA72, 0x6964, 0xDA73,
- 0x6965, 0x696A, 0x696D, 0xDA74, 0, 0x696B, 0xDA75, 0xDA76,
- 0xDA77, 0x6969, 0x6963, 0xDA78, 0xDA79, 0, 0, 0,
- 0x4358, 0xDA7A, 0x6974, 0, 0x4C2A, 0, 0xDA7B, 0xDA7C,
- 0, 0xDA7D, 0, 0xDA7E, 0, 0x6972, 0, 0,
- 0xDB21, 0x6973, 0, 0, 0, 0, 0xDB22, 0xDB23,
- 0, 0xDB24, 0xDB25, 0, 0x696E, 0, 0, 0x6970,
- 0, 0xDB26, 0xDB27, 0x6971, 0xDB28, 0xDB29, 0xDB2A, 0x696F,
-};
-static const unsigned short utf8_to_euc_E89C_x0213[] = {
- 0x6966, 0, 0x4B2A, 0x6967, 0xDA71, 0xF750, 0x6964, 0xF751,
- 0x6965, 0x696A, 0x696D, 0x7B59, 0, 0x696B, 0xF752, 0xDA76,
- 0xF753, 0x6969, 0x6963, 0xF754, 0xDA79, 0, 0, 0,
- 0x4358, 0xF755, 0x6974, 0, 0x4C2A, 0, 0xDA7B, 0xF756,
- 0, 0xF757, 0, 0xF758, 0, 0x6972, 0, 0,
- 0xDB21, 0x6973, 0, 0, 0, 0, 0xDB22, 0xDB23,
- 0, 0xF759, 0xDB25, 0, 0x696E, 0, 0, 0x6970,
- 0, 0xDB26, 0xF75A, 0x6971, 0xDB28, 0xDB29, 0xF75B, 0x696F,
-};
-static const unsigned short utf8_to_euc_E89D[] = {
- 0xDB2B, 0, 0, 0xDB2C, 0, 0xDB2D, 0, 0,
- 0, 0x4066, 0, 0x4F39, 0x6978, 0xDB2E, 0x6979, 0,
- 0, 0, 0, 0x6A21, 0, 0x3F2A, 0, 0x697B,
- 0xDB2F, 0x697E, 0, 0, 0, 0xDB30, 0, 0x6976,
- 0x6975, 0xDB31, 0, 0x6A22, 0xDB32, 0xDB33, 0x325C, 0,
- 0x697C, 0, 0x6A23, 0, 0, 0, 0x697D, 0xDB34,
- 0, 0xDB35, 0xDB36, 0, 0x697A, 0, 0x4433, 0,
- 0x6977, 0, 0, 0xDB37, 0, 0, 0, 0x4768,
-};
-static const unsigned short utf8_to_euc_E89D_x0213[] = {
- 0xF75C, 0, 0, 0xF75D, 0, 0xDB2D, 0, 0,
- 0, 0x4066, 0, 0x4F39, 0x6978, 0xDB2E, 0x6979, 0,
- 0, 0xF75E, 0, 0x6A21, 0, 0x3F2A, 0, 0x697B,
- 0xF75F, 0x697E, 0, 0, 0, 0xDB30, 0, 0x6976,
- 0x6975, 0xDB31, 0, 0x6A22, 0xF760, 0xF761, 0x325C, 0,
- 0x697C, 0, 0x6A23, 0, 0, 0, 0x697D, 0xDB34,
- 0, 0x7B5A, 0xF762, 0, 0x697A, 0, 0x4433, 0,
- 0x6977, 0, 0, 0xDB37, 0xF763, 0, 0, 0x4768,
-};
-static const unsigned short utf8_to_euc_E89E[] = {
- 0, 0, 0x6A27, 0xDB38, 0xDB39, 0xDB3A, 0xDB3B, 0xDB3C,
- 0xDB3D, 0xDB3E, 0, 0xDB3F, 0xDB40, 0x4D3B, 0, 0,
- 0xDB41, 0, 0, 0xDB42, 0, 0xDB43, 0, 0xDB44,
- 0xDB45, 0xDB46, 0, 0, 0, 0, 0xDB47, 0x6A26,
- 0xDB48, 0, 0x6A25, 0xDB49, 0, 0, 0, 0xDB4A,
- 0, 0, 0, 0x6A2E, 0xDB4B, 0xDB4C, 0xDB4D, 0x6A28,
- 0, 0xDB4E, 0, 0x6A30, 0, 0xDB4F, 0, 0,
- 0, 0, 0x4D66, 0x6A33, 0, 0x6A2A, 0xDB50, 0xDB51,
-};
-static const unsigned short utf8_to_euc_E89E_x0213[] = {
- 0, 0, 0x6A27, 0xDB38, 0xDB39, 0xDB3A, 0xDB3B, 0x7B5B,
- 0x7B5C, 0xF767, 0, 0xF768, 0xDB40, 0x4D3B, 0, 0,
- 0xDB41, 0, 0, 0xF769, 0, 0xDB43, 0, 0xDB44,
- 0xDB45, 0xDB46, 0, 0, 0, 0, 0xDB47, 0x6A26,
- 0xF76A, 0, 0x6A25, 0xDB49, 0, 0, 0, 0xF766,
- 0, 0, 0, 0x6A2E, 0x7B5D, 0x7B5E, 0xDB4D, 0x6A28,
- 0, 0xDB4E, 0, 0x6A30, 0, 0x7B5F, 0, 0,
- 0, 0, 0x4D66, 0x6A33, 0, 0x6A2A, 0xF76D, 0xDB51,
-};
-static const unsigned short utf8_to_euc_E89F[] = {
- 0x6A2B, 0xDB52, 0, 0, 0x6A2F, 0, 0x6A32, 0x6A31,
- 0xDB53, 0xDB54, 0xDB55, 0x6A29, 0, 0, 0xDB56, 0,
- 0x6A2C, 0, 0x6A3D, 0, 0, 0xDB57, 0xDB58, 0,
- 0, 0xDB59, 0xDB5A, 0, 0xDB5B, 0, 0, 0xDB5C,
- 0x6A36, 0, 0xDB5D, 0xDB5E, 0xDB5F, 0, 0, 0,
- 0, 0, 0xDB60, 0xDB61, 0, 0xDB62, 0, 0x6A34,
- 0, 0xDB63, 0x6A35, 0xDB64, 0, 0, 0x6A3A, 0x6A3B,
- 0xDB65, 0x332A, 0xDB66, 0x3542, 0, 0, 0x6A39, 0xDB67,
-};
-static const unsigned short utf8_to_euc_E89F_x0213[] = {
- 0x6A2B, 0xF76F, 0, 0, 0x6A2F, 0, 0x6A32, 0x6A31,
- 0xDB53, 0xDB54, 0xDB55, 0x6A29, 0, 0, 0xF770, 0,
- 0x6A2C, 0, 0x6A3D, 0, 0, 0xDB57, 0x7B61, 0,
- 0, 0xDB59, 0xDB5A, 0, 0xDB5B, 0, 0, 0xF772,
- 0x6A36, 0, 0xDB5D, 0xF774, 0xDB5F, 0xF775, 0xF776, 0,
- 0, 0, 0xF777, 0xF778, 0x7B62, 0xF779, 0, 0x6A34,
- 0, 0xDB63, 0x6A35, 0xDB64, 0, 0xF771, 0x6A3A, 0x6A3B,
- 0xDB65, 0x332A, 0xDB66, 0x3542, 0, 0, 0x6A39, 0xDB67,
-};
-static const unsigned short utf8_to_euc_E8A0[] = {
- 0, 0xDB68, 0, 0xDB69, 0, 0x6A24, 0xDB6A, 0xF464,
- 0, 0xDB6B, 0xDB6C, 0xDB6D, 0, 0x6A38, 0x6A3C, 0x6A37,
- 0xDB6E, 0x6A3E, 0xDB70, 0xDB71, 0xDB72, 0x6A40, 0x6A3F, 0,
- 0xDB73, 0xDB6F, 0xDB74, 0xDB75, 0xDB76, 0, 0xDB77, 0xDB78,
- 0, 0x6A42, 0x6A41, 0x695A, 0, 0, 0, 0x6A46,
- 0xDB79, 0, 0, 0, 0, 0xDB7A, 0xDB7B, 0,
- 0xDB7C, 0x6A43, 0xDB7D, 0, 0, 0xDB7E, 0x6A44, 0,
- 0, 0x6A45, 0xDC21, 0x6A47, 0xDC22, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E8A0_x0213[] = {
- 0, 0xF77A, 0, 0xF77B, 0, 0x6A24, 0x7B63, 0,
- 0, 0xDB6B, 0x7B64, 0xF77C, 0, 0x6A38, 0x6A3C, 0x6A37,
- 0x7B65, 0x6A3E, 0xDB70, 0xF77D, 0x7B66, 0x6A40, 0x6A3F, 0,
- 0xDB73, 0xDB6F, 0xDB74, 0xDB75, 0xDB76, 0, 0xDB77, 0x7B67,
- 0, 0x6A42, 0x6A41, 0x695A, 0, 0, 0, 0x6A46,
- 0xF77E, 0, 0, 0, 0, 0xDB7A, 0xF821, 0,
- 0xDB7C, 0x6A43, 0xF822, 0, 0, 0xDB7E, 0x6A44, 0,
- 0, 0x6A45, 0xDC21, 0x6A47, 0xF823, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E8A1[] = {
- 0x376C, 0xDC23, 0x6A49, 0xDC24, 0x6A48, 0xDC25, 0x3D30, 0,
- 0xDC26, 0xDC27, 0xDC28, 0xDC29, 0x3954, 0x5E27, 0xDC2A, 0,
- 0, 0xDC2B, 0x6A4A, 0x3D51, 0, 0xDC2C, 0xDC2D, 0x3339,
- 0xDC2E, 0x6A4B, 0xDC2F, 0x3152, 0xDC30, 0x3E57, 0x6A4C, 0xDC31,
- 0xDC32, 0x3955, 0x6A4D, 0x3061, 0xDC33, 0, 0, 0,
- 0x493D, 0xDC34, 0, 0x6A4E, 0, 0, 0, 0,
- 0x3F6A, 0xDC35, 0x6A55, 0, 0, 0x6A52, 0, 0x436F,
- 0, 0xDC36, 0, 0xDC37, 0, 0x6A53, 0x6A50, 0x365E,
-};
-static const unsigned short utf8_to_euc_E8A1_x0213[] = {
- 0x376C, 0xDC23, 0x6A49, 0xDC24, 0x6A48, 0xDC25, 0x3D30, 0,
- 0xDC26, 0xDC27, 0xF825, 0xDC29, 0x3954, 0x5E27, 0xDC2A, 0,
- 0, 0xDC2B, 0x6A4A, 0x3D51, 0, 0xDC2C, 0xDC2D, 0x3339,
- 0xF826, 0x6A4B, 0xDC2F, 0x3152, 0xDC30, 0x3E57, 0x6A4C, 0xF827,
- 0xDC32, 0x3955, 0x6A4D, 0x3061, 0xF828, 0, 0, 0,
- 0x493D, 0xF82B, 0, 0x6A4E, 0, 0, 0, 0xF82D,
- 0x3F6A, 0xDC35, 0x6A55, 0, 0, 0x6A52, 0, 0x436F,
- 0, 0xDC36, 0, 0xDC37, 0, 0x6A53, 0x6A50, 0x365E,
-};
-static const unsigned short utf8_to_euc_E8A2[] = {
- 0xDC38, 0x6A4F, 0x6A56, 0, 0, 0, 0, 0,
- 0x3736, 0, 0, 0x425E, 0, 0x6A5C, 0, 0,
- 0, 0, 0x6A58, 0, 0, 0, 0x4235, 0x6A57,
- 0xDC39, 0x6A5A, 0xDC3A, 0xDC3B, 0xDC3C, 0, 0x6A51, 0xDC3D,
- 0xDC3E, 0, 0x6A5B, 0, 0x6A5D, 0, 0, 0,
- 0xDC3F, 0, 0xDC40, 0x486F, 0, 0, 0x6A59, 0,
- 0x6A5E, 0x6A60, 0, 0, 0x3853, 0x6A54, 0, 0x3041,
- 0, 0, 0xDC41, 0, 0, 0xDC42, 0xDC43, 0x6A5F,
-};
-static const unsigned short utf8_to_euc_E8A2_x0213[] = {
- 0xDC38, 0x6A4F, 0x6A56, 0, 0, 0, 0, 0,
- 0x3736, 0, 0, 0x425E, 0, 0x6A5C, 0, 0,
- 0, 0, 0x6A58, 0, 0, 0, 0x4235, 0x6A57,
- 0x7B68, 0x6A5A, 0xDC3A, 0xDC3B, 0xDC3C, 0, 0x6A51, 0xDC3D,
- 0xF82E, 0, 0x6A5B, 0, 0x6A5D, 0, 0, 0,
- 0xDC3F, 0, 0x7B69, 0x486F, 0, 0, 0x6A59, 0,
- 0x6A5E, 0x6A60, 0, 0, 0x3853, 0x6A54, 0, 0x3041,
- 0, 0, 0xDC41, 0, 0xF82F, 0xF830, 0xF831, 0x6A5F,
-};
-static const unsigned short utf8_to_euc_E8A3[] = {
- 0xDC44, 0x3A5B, 0x4E76, 0x6A61, 0x6A62, 0x4175, 0, 0,
- 0, 0, 0xDC45, 0xDC46, 0xDC47, 0xDC48, 0xDC49, 0x4E22,
- 0, 0xDC4A, 0xDC4B, 0xDC4C, 0x6A63, 0x4D35, 0, 0,
- 0x6A64, 0x6A65, 0, 0xDC4D, 0x4A64, 0x6A66, 0xDC4E, 0x3A40,
- 0, 0x4E23, 0, 0, 0, 0, 0, 0xDC4F,
- 0x6A6B, 0, 0, 0, 0, 0, 0, 0xDC50,
- 0xDC51, 0xDC52, 0x6A6C, 0x3E58, 0x6A6A, 0xDC53, 0, 0xDC54,
- 0x4D67, 0x6A67, 0, 0, 0x6A69, 0x403D, 0x3F7E, 0,
-};
-static const unsigned short utf8_to_euc_E8A3_x0213[] = {
- 0xF832, 0x3A5B, 0x4E76, 0x6A61, 0x6A62, 0x4175, 0, 0,
- 0, 0, 0x7B6A, 0xDC46, 0xDC47, 0xDC48, 0x7B6B, 0x4E22,
- 0, 0xF835, 0xF833, 0xF836, 0x6A63, 0x4D35, 0, 0,
- 0x6A64, 0x6A65, 0, 0xF837, 0x4A64, 0x6A66, 0xDC4E, 0x3A40,
- 0, 0x4E23, 0, 0, 0, 0, 0, 0xDC4F,
- 0x6A6B, 0, 0, 0, 0, 0, 0, 0xDC50,
- 0xF838, 0xF839, 0x6A6C, 0x3E58, 0x6A6A, 0x7B6D, 0, 0xDC54,
- 0x4D67, 0x6A67, 0, 0, 0x6A69, 0x403D, 0x3F7E, 0,
-};
-static const unsigned short utf8_to_euc_E8A4[] = {
- 0, 0xDC55, 0x6A68, 0, 0x6A6D, 0, 0xDC56, 0x4A23,
- 0, 0, 0x6A6F, 0, 0x6A6E, 0xDC57, 0xDC58, 0xDC59,
- 0x336C, 0, 0x4B2B, 0x6A70, 0, 0xDC5A, 0xDC5B, 0,
- 0xDC5C, 0xDC5D, 0xDC5E, 0, 0xDC5F, 0x6A7C, 0x6A72, 0,
- 0xDC60, 0, 0, 0, 0, 0x6A73, 0xDC61, 0xDC62,
- 0xDC63, 0, 0x6A74, 0x6A75, 0, 0, 0, 0,
- 0xDC64, 0xDC65, 0xDC66, 0, 0, 0xDC67, 0x6A79, 0,
- 0x6A7A, 0xDC68, 0xDC69, 0x6A78, 0, 0, 0xDC6A, 0,
-};
-static const unsigned short utf8_to_euc_E8A4_x0213[] = {
- 0, 0xF83B, 0x6A68, 0, 0x6A6D, 0, 0xDC56, 0x4A23,
- 0, 0, 0x6A6F, 0, 0x6A6E, 0xDC57, 0xDC58, 0xDC59,
- 0x336C, 0, 0x4B2B, 0x6A70, 0, 0xDC5A, 0xDC5B, 0,
- 0x7B70, 0x7B71, 0x7B72, 0, 0x7B6E, 0x6A7C, 0x6A72, 0,
- 0xDC60, 0, 0, 0, 0, 0x6A73, 0xDC61, 0x7B73,
- 0xDC63, 0, 0x6A74, 0x6A75, 0, 0, 0, 0,
- 0x7B74, 0xDC65, 0x7B75, 0, 0, 0xDC67, 0x6A79, 0xF83D,
- 0x6A7A, 0x7B76, 0xDC69, 0x6A78, 0, 0, 0xDC6A, 0,
-};
-static const unsigned short utf8_to_euc_E8A5[] = {
- 0xDC6B, 0x6A76, 0xDC6C, 0x6A71, 0x6A77, 0xDC6D, 0xDC6E, 0,
- 0, 0xDC6F, 0, 0, 0x6A7B, 0x7037, 0, 0xDC70,
- 0, 0, 0xDC71, 0, 0, 0, 0x3228, 0xDC72,
- 0, 0, 0xDC73, 0xDC74, 0xDC75, 0, 0x6A7E, 0x365F,
- 0x6A7D, 0xDC76, 0xDC77, 0xDC78, 0x6B22, 0, 0x6B21, 0,
- 0, 0, 0x6B24, 0xDC79, 0, 0x6B23, 0xDC7A, 0x6B25,
- 0xDC7B, 0, 0x3D31, 0xDC7C, 0x6B26, 0xDC7D, 0, 0x6B27,
- 0, 0, 0xDC7E, 0xDD21, 0xDD22, 0xDD23, 0x6B28, 0x403E,
-};
-static const unsigned short utf8_to_euc_E8A5_x0213[] = {
- 0x7B77, 0x6A76, 0xF83F, 0x6A71, 0x6A77, 0xF840, 0xDC6E, 0,
- 0, 0xF841, 0, 0, 0x6A7B, 0x7037, 0, 0xDC70,
- 0, 0, 0xDC71, 0, 0, 0, 0x3228, 0xDC72,
- 0, 0, 0xDC73, 0xDC74, 0xDC75, 0, 0x6A7E, 0x365F,
- 0x6A7D, 0xDC76, 0xF844, 0xDC78, 0x6B22, 0, 0x6B21, 0,
- 0, 0, 0x6B24, 0xDC79, 0, 0x6B23, 0xDC7A, 0x6B25,
- 0xDC7B, 0, 0x3D31, 0xDC7C, 0x6B26, 0xDC7D, 0, 0x6B27,
- 0, 0, 0xDC7E, 0xDD21, 0xDD22, 0xDD23, 0x6B28, 0x403E,
-};
-static const unsigned short utf8_to_euc_E8A6[] = {
- 0, 0x4D57, 0, 0x6B29, 0, 0, 0x4A24, 0x4746,
- 0x6B2A, 0xDD24, 0x6B2B, 0x382B, 0, 0xDD25, 0, 0x352C,
- 0xDD26, 0, 0, 0x6B2C, 0xDD27, 0xDD28, 0x3B6B, 0x4741,
- 0x6B2D, 0, 0x3350, 0xDD29, 0xDD2A, 0, 0, 0xDD2B,
- 0xDD2C, 0x6B2E, 0, 0, 0, 0xDD2D, 0x6B30, 0x4D77,
- 0, 0x6B2F, 0x3F46, 0, 0x6B31, 0, 0, 0x6B32,
- 0xDD2E, 0, 0x6B33, 0x3451, 0xDD2F, 0xDD30, 0xDD31, 0xDD32,
- 0, 0, 0x6B34, 0, 0xDD33, 0x6B35, 0, 0x6B36,
-};
-static const unsigned short utf8_to_euc_E8A6_x0213[] = {
- 0xF845, 0x4D57, 0, 0x6B29, 0, 0, 0x4A24, 0x4746,
- 0x6B2A, 0xF846, 0x6B2B, 0x382B, 0, 0xDD25, 0, 0x352C,
- 0xF847, 0, 0, 0x6B2C, 0x7B78, 0xDD28, 0x3B6B, 0x4741,
- 0x6B2D, 0, 0x3350, 0xDD29, 0xDD2A, 0, 0, 0xF848,
- 0xDD2C, 0x6B2E, 0, 0, 0, 0xDD2D, 0x6B30, 0x4D77,
- 0, 0x6B2F, 0x3F46, 0, 0x6B31, 0, 0, 0x6B32,
- 0xF849, 0, 0x6B33, 0x3451, 0xDD2F, 0xDD30, 0xDD31, 0xF84A,
- 0, 0, 0x6B34, 0, 0xDD33, 0x6B35, 0, 0x6B36,
-};
-static const unsigned short utf8_to_euc_E8A7[] = {
- 0x6B37, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x3351, 0, 0xDD34, 0xDD35, 0xDD36, 0xDD37,
- 0xDD38, 0, 0x6B38, 0, 0x6B39, 0x6B3A, 0, 0,
- 0, 0, 0, 0x3272, 0, 0xDD39, 0x3F28, 0x6B3B,
- 0, 0xDD3A, 0, 0xDD3B, 0, 0xDD3C, 0, 0,
- 0, 0xDD3D, 0, 0xDD3E, 0x6B3C, 0, 0xDD3F, 0,
- 0x6B3D, 0xDD40, 0, 0, 0, 0xDD41, 0, 0xDD42,
-};
-static const unsigned short utf8_to_euc_E8A7_x0213[] = {
- 0x6B37, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x3351, 0, 0x7B7A, 0xDD35, 0xF84B, 0xDD37,
- 0xF84C, 0, 0x6B38, 0, 0x6B39, 0x6B3A, 0, 0,
- 0, 0, 0, 0x3272, 0, 0x7B7B, 0x3F28, 0x6B3B,
- 0, 0xDD3A, 0, 0xF84D, 0, 0xDD3C, 0, 0,
- 0, 0xF84F, 0, 0xF850, 0x6B3C, 0, 0x7B7C, 0,
- 0x6B3D, 0xDD40, 0, 0, 0, 0xF851, 0, 0xF852,
-};
-static const unsigned short utf8_to_euc_E8A8[] = {
- 0x3840, 0, 0x447B, 0x6B3E, 0xDD43, 0xDD44, 0, 0xDD45,
- 0x3757, 0, 0x3F56, 0, 0x6B41, 0, 0x4624, 0xDD46,
- 0x6B40, 0xDD47, 0xDD48, 0x3731, 0xDD49, 0xDD4A, 0x6B3F, 0x4277,
- 0x352D, 0, 0, 0x6B42, 0, 0x6B43, 0xDD4B, 0x3E59,
- 0xDD4C, 0, 0xDD4D, 0x376D, 0xDD4E, 0x6B44, 0xDD4F, 0,
- 0, 0, 0x4B2C, 0xDD50, 0xDD51, 0x405F, 0, 0xDD52,
- 0, 0x3576, 0, 0x4C75, 0x414A, 0xDD53, 0x6B45, 0xDD54,
- 0, 0, 0x3F47, 0x4370, 0x3E5A, 0xDD55, 0xDD56, 0,
-};
-static const unsigned short utf8_to_euc_E8A8_x0213[] = {
- 0x3840, 0, 0x447B, 0x6B3E, 0xDD43, 0xDD44, 0, 0xDD45,
- 0x3757, 0, 0x3F56, 0, 0x6B41, 0, 0x4624, 0xDD46,
- 0x6B40, 0xF854, 0x7B7D, 0x3731, 0xF855, 0x7B7E, 0x6B3F, 0x4277,
- 0x352D, 0, 0, 0x6B42, 0, 0x6B43, 0xDD4B, 0x3E59,
- 0xDD4C, 0xF857, 0x7C21, 0x376D, 0xDD4E, 0x6B44, 0xDD4F, 0,
- 0, 0, 0x4B2C, 0xDD50, 0xDD51, 0x405F, 0, 0xDD52,
- 0, 0x3576, 0, 0x4C75, 0x414A, 0xF858, 0x6B45, 0x7C22,
- 0, 0, 0x3F47, 0x4370, 0x3E5A, 0xDD55, 0xF859, 0,
-};
-static const unsigned short utf8_to_euc_E8A9[] = {
- 0xDD57, 0x6B46, 0, 0xDD58, 0, 0xDD59, 0x6B49, 0xDD5A,
- 0x6B4A, 0xDD5B, 0, 0, 0, 0xDD5C, 0xDD5D, 0,
- 0x3A3E, 0x4242, 0x6B48, 0xDD5E, 0x3E5B, 0x493E, 0xDD5F, 0xDD60,
- 0xDD61, 0, 0, 0x6B47, 0xDD62, 0xDD63, 0x3B6C, 0,
- 0x3153, 0xDD64, 0x6B4E, 0x3758, 0, 0xDD65, 0x3B6E, 0xDD66,
- 0, 0x3B6D, 0, 0x4F4D, 0x6B4D, 0x6B4C, 0x4127, 0,
- 0x354D, 0x4F43, 0x333A, 0x3E5C, 0, 0xDD67, 0xDD68, 0xDD69,
- 0, 0xDD6A, 0xDD6B, 0xDD6C, 0x6B4B, 0, 0xDD6D, 0xDD6E,
-};
-static const unsigned short utf8_to_euc_E8A9_x0213[] = {
- 0xDD57, 0x6B46, 0, 0xDD58, 0, 0xF85A, 0x6B49, 0x7C23,
- 0x6B4A, 0xDD5B, 0, 0, 0, 0xF85B, 0x7C24, 0,
- 0x3A3E, 0x4242, 0x6B48, 0xDD5E, 0x3E5B, 0x493E, 0xDD5F, 0xDD60,
- 0xF85C, 0, 0, 0x6B47, 0xDD62, 0x7C25, 0x3B6C, 0,
- 0x3153, 0x7C26, 0x6B4E, 0x3758, 0, 0xDD65, 0x3B6E, 0xDD66,
- 0, 0x3B6D, 0, 0x4F4D, 0x6B4D, 0x6B4C, 0x4127, 0,
- 0x354D, 0x4F43, 0x333A, 0x3E5C, 0, 0x7C27, 0xDD68, 0xDD69,
- 0, 0x7C28, 0xDD6B, 0xDD6C, 0x6B4B, 0, 0xDD6D, 0xDD6E,
-};
-static const unsigned short utf8_to_euc_E8AA[] = {
- 0xDD6F, 0, 0x6B50, 0xDD70, 0x6B51, 0x6B4F, 0xDD71, 0x3858,
- 0, 0x4D40, 0, 0xDD72, 0x3B6F, 0x4727, 0, 0xDD73,
- 0xDD74, 0x6B54, 0xDD75, 0x4040, 0, 0x4342, 0xDD76, 0xDD77,
- 0x4D36, 0xDD78, 0x6B57, 0, 0, 0, 0x386C, 0xDD79,
- 0x403F, 0x6B53, 0, 0x6B58, 0x386D, 0x6B55, 0x6B56, 0xDD7A,
- 0x6B52, 0xDD7B, 0, 0, 0x4062, 0x4649, 0xDD7C, 0xDD7D,
- 0x432F, 0, 0x325D, 0xDD7E, 0, 0, 0xDE21, 0xDE22,
- 0, 0x4870, 0, 0xDE23, 0x3543, 0, 0xDE24, 0x4434,
-};
-static const unsigned short utf8_to_euc_E8AA_x0213[] = {
- 0xDD6F, 0, 0x6B50, 0xDD70, 0x6B51, 0x6B4F, 0xDD71, 0x3858,
- 0, 0x4D40, 0, 0xDD72, 0x3B6F, 0x4727, 0, 0xDD73,
- 0xF85E, 0x6B54, 0xDD75, 0x4040, 0, 0x4342, 0xDD76, 0xDD77,
- 0x4D36, 0xDD78, 0x6B57, 0, 0, 0, 0x386C, 0xDD79,
- 0x403F, 0x6B53, 0, 0x6B58, 0x386D, 0x6B55, 0x6B56, 0x7C29,
- 0x6B52, 0xDD7B, 0, 0, 0x4062, 0x4649, 0xF85D, 0xDD7D,
- 0x432F, 0, 0x325D, 0xDD7E, 0, 0, 0xDE21, 0xF85F,
- 0, 0x4870, 0, 0xDE23, 0x3543, 0, 0xF860, 0x4434,
-};
-static const unsigned short utf8_to_euc_E8AB[] = {
- 0, 0, 0x6B5B, 0xDE25, 0x6B59, 0, 0xDE26, 0x434C,
- 0xDE27, 0xDE28, 0xDE29, 0x4041, 0x3452, 0x6B5A, 0, 0x3F5B,
- 0, 0xDE2A, 0x4E4A, 0xDE2B, 0xDE2C, 0xDE2D, 0x4F40, 0xDE2E,
- 0, 0, 0x6B5C, 0x6B67, 0x4435, 0xDE2F, 0x6B66, 0xDE30,
- 0x6B63, 0x6B6B, 0x6B64, 0, 0x6B60, 0, 0x447C, 0x6B5F,
- 0, 0, 0, 0x6B5D, 0xDE31, 0x4D21, 0x3B70, 0,
- 0xDE32, 0x6B61, 0, 0x6B5E, 0xDE33, 0xDE34, 0xDE35, 0x6B65,
- 0x3D74, 0, 0x3841, 0, 0xDE36, 0, 0x427A, 0xDE37,
-};
-static const unsigned short utf8_to_euc_E8AB_x0213[] = {
- 0, 0, 0x6B5B, 0xDE25, 0x6B59, 0, 0xDE26, 0x434C,
- 0xDE27, 0xDE28, 0xDE29, 0x4041, 0x3452, 0x6B5A, 0, 0x3F5B,
- 0x7C2A, 0xDE2A, 0x4E4A, 0xDE2B, 0xDE2C, 0xDE2D, 0x4F40, 0xF861,
- 0, 0, 0x6B5C, 0x6B67, 0x4435, 0xDE2F, 0x6B66, 0x7C2B,
- 0x6B63, 0x6B6B, 0x6B64, 0, 0x6B60, 0, 0x447C, 0x6B5F,
- 0, 0, 0, 0x6B5D, 0xDE31, 0x4D21, 0x3B70, 0,
- 0xDE32, 0x6B61, 0, 0x6B5E, 0x7C2C, 0xDE34, 0x7C2D, 0x6B65,
- 0x3D74, 0, 0x3841, 0, 0xF862, 0, 0x427A, 0xDE37,
-};
-static const unsigned short utf8_to_euc_E8AC[] = {
- 0x4B45, 0x315A, 0x3062, 0, 0x4625, 0xDE38, 0xDE39, 0x6B69,
- 0, 0, 0xDE3F, 0xDE3A, 0x6B68, 0, 0x4666, 0,
- 0x6B6D, 0xDE3B, 0, 0, 0x6B62, 0, 0x6B6C, 0x6B6E,
- 0, 0x382C, 0x6B6A, 0x3956, 0xDE3C, 0x3C55, 0xDE3D, 0xDE3E,
- 0x6B6F, 0x4D58, 0, 0, 0, 0, 0x6B72, 0,
- 0x6B75, 0, 0, 0x6B73, 0x4935, 0xDE40, 0, 0,
- 0xDE41, 0, 0, 0x6B70, 0, 0, 0, 0xDE42,
- 0, 0x3660, 0, 0, 0xDE43, 0, 0x6B74, 0,
-};
-static const unsigned short utf8_to_euc_E8AC_x0213[] = {
- 0x4B45, 0x315A, 0x3062, 0, 0x4625, 0xF865, 0xDE39, 0x6B69,
- 0, 0, 0xF864, 0xDE3A, 0x6B68, 0xF866, 0x4666, 0,
- 0x6B6D, 0xDE3B, 0, 0, 0x6B62, 0, 0x6B6C, 0x6B6E,
- 0, 0x382C, 0x6B6A, 0x3956, 0xF867, 0x3C55, 0xDE3D, 0xF868,
- 0x6B6F, 0x4D58, 0, 0, 0, 0, 0x6B72, 0,
- 0x6B75, 0, 0, 0x6B73, 0x4935, 0xF869, 0, 0,
- 0xDE41, 0, 0, 0x6B70, 0, 0, 0, 0xDE42,
- 0, 0x3660, 0, 0, 0xDE43, 0, 0x6B74, 0,
-};
-static const unsigned short utf8_to_euc_E8AD[] = {
- 0, 0x6B76, 0xDE44, 0xDE45, 0xDE46, 0xDE47, 0xDE48, 0,
- 0xDE49, 0x6B7A, 0, 0, 0x6B77, 0xDE4E, 0x6B79, 0x6B78,
- 0, 0, 0xDE4A, 0xDE4B, 0xDE4C, 0, 0x6B7B, 0,
- 0x3C31, 0xDE4D, 0x6B7D, 0x6B7C, 0x4968, 0, 0xDE4F, 0x6C21,
- 0, 0, 0, 0xDE50, 0, 0, 0x3759, 0,
- 0, 0, 0, 0x6B7E, 0x6C22, 0xDE51, 0, 0x6C23,
- 0x3544, 0x6641, 0x3E79, 0, 0x6C24, 0, 0xDE52, 0x386E,
- 0xDE53, 0xDE54, 0, 0, 0xDE55, 0x6C25, 0xDE56, 0xF466,
-};
-static const unsigned short utf8_to_euc_E8AD_x0213[] = {
- 0, 0x6B76, 0xDE44, 0xF86A, 0xDE46, 0xDE47, 0x7C31, 0,
- 0xDE49, 0x6B7A, 0, 0, 0x6B77, 0xDE4E, 0x6B79, 0x6B78,
- 0, 0xF86C, 0xDE4A, 0, 0x7C32, 0, 0x6B7B, 0,
- 0x3C31, 0x7C33, 0x6B7D, 0x6B7C, 0x4968, 0, 0xF86D, 0x6C21,
- 0, 0, 0, 0xDE50, 0, 0, 0x3759, 0,
- 0, 0x7C34, 0, 0x6B7E, 0x6C22, 0xDE51, 0, 0x6C23,
- 0x3544, 0x6641, 0x3E79, 0, 0x6C24, 0, 0xF86E, 0x386E,
- 0xDE53, 0xDE54, 0, 0, 0xDE55, 0x6C25, 0xDE56, 0xF86F,
-};
-static const unsigned short utf8_to_euc_E8AE[] = {
- 0x6C26, 0xDE57, 0, 0x3B3E, 0xDE58, 0xDE59, 0, 0,
- 0, 0, 0x5A4E, 0xDE5A, 0x6C27, 0xDE5B, 0x6C28, 0xDE5C,
- 0x3D32, 0, 0x6C29, 0x6C2A, 0xDE5D, 0xDE5E, 0x6C2B, 0,
- 0, 0x6C2C, 0x6C2D, 0, 0xDE5F, 0, 0xDE60, 0xDE61,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E8AE_x0213[] = {
- 0x6C26, 0xF870, 0, 0x3B3E, 0xDE58, 0xDE59, 0, 0,
- 0, 0, 0x5A4E, 0xF871, 0x6C27, 0xDE5B, 0x6C28, 0xDE5C,
- 0x3D32, 0, 0x6C29, 0x6C2A, 0xF872, 0xF873, 0x6C2B, 0,
- 0, 0x6C2C, 0x6C2D, 0, 0xF874, 0x7C35, 0xF875, 0xDE61,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E8B0[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0x432B,
- 0xDE62, 0xDE63, 0x6C2E, 0, 0, 0xDE64, 0xDE65, 0x6C30,
-};
-static const unsigned short utf8_to_euc_E8B0_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0x432B,
- 0xDE62, 0xF876, 0x6C2E, 0, 0, 0xF878, 0xDE65, 0x6C30,
-};
-static const unsigned short utf8_to_euc_E8B1[] = {
- 0, 0x6C2F, 0, 0, 0, 0xDE66, 0x4626, 0xDE67,
- 0x6C31, 0xDE68, 0x4B2D, 0xDE69, 0x6C32, 0, 0x6C33, 0xDE6A,
- 0x6C34, 0xDE6B, 0, 0xDE6C, 0xDE6D, 0x6C35, 0, 0xDE6E,
- 0xDE6F, 0xDE72, 0x465A, 0xDE70, 0, 0xDE71, 0, 0,
- 0, 0x3E5D, 0x6C36, 0xDE73, 0xDE74, 0, 0xDE75, 0,
- 0xDE76, 0xDE77, 0x396B, 0x502E, 0x6C37, 0xDE78, 0, 0,
- 0, 0, 0, 0xDE79, 0, 0xDE7A, 0xDE7B, 0,
- 0x6C38, 0x493F, 0x6C39, 0xDE7C, 0x6C41, 0, 0xDE7D, 0,
-};
-static const unsigned short utf8_to_euc_E8B1_x0213[] = {
- 0, 0x6C2F, 0, 0, 0, 0xF87B, 0x4626, 0xF87C,
- 0x6C31, 0x7C36, 0x4B2D, 0xDE69, 0x6C32, 0, 0x6C33, 0xF87D,
- 0x6C34, 0xDE6B, 0, 0xDE6C, 0xF87E, 0x6C35, 0, 0xF921,
- 0xDE6F, 0xDE72, 0x465A, 0xDE70, 0, 0xDE71, 0, 0,
- 0, 0x3E5D, 0x6C36, 0xDE73, 0xDE74, 0, 0xDE75, 0,
- 0x7C37, 0xF922, 0x396B, 0x502E, 0x6C37, 0xF923, 0, 0,
- 0, 0, 0, 0xF924, 0, 0xDE7A, 0xDE7B, 0,
- 0x6C38, 0x493F, 0x6C39, 0xDE7C, 0x6C41, 0, 0xDE7D, 0,
-};
-static const unsigned short utf8_to_euc_E8B2[] = {
- 0, 0, 0x6C3A, 0, 0, 0x6C3C, 0xDE7E, 0xDF21,
- 0, 0x6C3B, 0x6C3D, 0xDF22, 0x4B46, 0x6C3E, 0x6C3F, 0,
- 0xDF23, 0, 0xDF24, 0xDF25, 0x6C40, 0, 0, 0,
- 0x6C42, 0xDF26, 0, 0xDF27, 0xDF28, 0x332D, 0x4467, 0,
- 0x4969, 0x3A62, 0x3957, 0, 0xDF29, 0, 0, 0x494F,
- 0x325F, 0x484E, 0x6C45, 0x3453, 0x4055, 0x6C44, 0x6C49, 0x4379,
- 0x4C63, 0, 0x6C47, 0x6C48, 0x352E, 0, 0x6C4A, 0x4763,
- 0x425F, 0xDF2A, 0xDF2B, 0x4871, 0x453D, 0x6C46, 0, 0x4B47,
-};
-static const unsigned short utf8_to_euc_E8B2_x0213[] = {
- 0, 0, 0x6C3A, 0, 0, 0x6C3C, 0xDE7E, 0xDF21,
- 0, 0x6C3B, 0x6C3D, 0xDF22, 0x4B46, 0x6C3E, 0x6C3F, 0,
- 0xDF23, 0, 0xF927, 0xF926, 0x6C40, 0, 0, 0,
- 0x6C42, 0xF928, 0, 0xF92A, 0xDF28, 0x332D, 0x4467, 0,
- 0x4969, 0x3A62, 0x3957, 0, 0xF92B, 0, 0, 0x494F,
- 0x325F, 0x484E, 0x6C45, 0x3453, 0x4055, 0x6C44, 0x6C49, 0x4379,
- 0x4C63, 0, 0x6C47, 0x6C48, 0x352E, 0, 0x6C4A, 0x4763,
- 0x425F, 0xDF2A, 0xDF2B, 0x4871, 0x453D, 0x6C46, 0, 0x4B47,
-};
-static const unsigned short utf8_to_euc_E8B3[] = {
- 0x326C, 0x6C4C, 0x4F28, 0x4442, 0x4F45, 0xDF2C, 0xDF2D, 0x3B71,
- 0x6C4B, 0xDF2E, 0x4231, 0xDF2F, 0, 0x6C5C, 0x4128, 0xDF30,
- 0, 0x4678, 0, 0x4950, 0, 0xDF32, 0xDF31, 0,
- 0, 0xDF33, 0x6C4F, 0x3B3F, 0x3B72, 0xDF34, 0x3E5E, 0,
- 0x4765, 0xDF35, 0x382D, 0x6C4E, 0x6C4D, 0, 0x496A, 0,
- 0xDF36, 0, 0x3C41, 0, 0xDF37, 0x4552, 0, 0xDF38,
- 0xDF39, 0, 0xDF3A, 0, 0xF467, 0xDF3B, 0, 0xDF3C,
- 0xDF3D, 0, 0x6C51, 0x6C52, 0x3958, 0x6C50, 0xDF3E, 0xDF3F,
-};
-static const unsigned short utf8_to_euc_E8B3_x0213[] = {
- 0x326C, 0x6C4C, 0x4F28, 0x4442, 0x4F45, 0xDF2C, 0xDF2D, 0x3B71,
- 0x6C4B, 0xDF2E, 0x4231, 0xDF2F, 0, 0x6C5C, 0x4128, 0xDF30,
- 0, 0x4678, 0, 0x4950, 0, 0xF92D, 0xF92C, 0,
- 0, 0xF92E, 0x6C4F, 0x3B3F, 0x3B72, 0xDF34, 0x3E5E, 0,
- 0x4765, 0x7C39, 0x382D, 0x6C4E, 0x6C4D, 0, 0x496A, 0,
- 0xDF36, 0, 0x3C41, 0, 0xDF37, 0x4552, 0, 0xDF38,
- 0xF930, 0xF931, 0xDF3A, 0, 0x7C3A, 0xDF3B, 0, 0xDF3C,
- 0x7C3B, 0, 0x6C51, 0x6C52, 0x3958, 0x6C50, 0x7C3C, 0xDF3F,
-};
-static const unsigned short utf8_to_euc_E8B4[] = {
- 0, 0xDF40, 0, 0xDF41, 0x6C53, 0x6C54, 0, 0x6C56,
- 0x4223, 0xDF42, 0x6C55, 0x3466, 0, 0x6C58, 0, 0x6C57,
- 0x6C59, 0, 0xDF43, 0x6C5B, 0x6C5D, 0, 0x6C5E, 0xDF44,
- 0, 0, 0, 0xDF45, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E8B4_x0213[] = {
- 0, 0xDF40, 0, 0xDF41, 0x6C53, 0x6C54, 0, 0x6C56,
- 0x4223, 0xF933, 0x6C55, 0x3466, 0, 0x6C58, 0xF934, 0x6C57,
- 0x6C59, 0, 0x7C3E, 0x6C5B, 0x6C5D, 0, 0x6C5E, 0xDF44,
- 0, 0, 0, 0x7C3F, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E8B5[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0x4056, 0xDF46, 0x3C4F, 0x6C5F,
- 0, 0xDF47, 0, 0x3352, 0xDF48, 0x6C60, 0xDF49, 0,
- 0x4176, 0x6C61, 0, 0x6C62, 0x496B, 0, 0xF468, 0x352F,
- 0, 0, 0, 0, 0, 0, 0, 0xDF4A,
-};
-static const unsigned short utf8_to_euc_E8B5_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0x4056, 0xDF46, 0x3C4F, 0x6C5F,
- 0, 0xDF47, 0, 0x3352, 0xF935, 0x6C60, 0xDF49, 0,
- 0x4176, 0x6C61, 0, 0x6C62, 0x496B, 0, 0, 0x352F,
- 0, 0, 0, 0, 0, 0, 0, 0xDF4A,
-};
-static const unsigned short utf8_to_euc_E8B6[] = {
- 0, 0x6C63, 0xDF4B, 0, 0xDF4C, 0x4436, 0, 0,
- 0xDF4D, 0, 0x315B, 0, 0, 0xDF4E, 0, 0,
- 0xDF4F, 0xDF50, 0, 0, 0, 0xDF51, 0, 0,
- 0, 0x6C64, 0, 0, 0, 0, 0xDF52, 0xDF53,
- 0xDF54, 0, 0, 0x3C71, 0, 0, 0xDF55, 0,
- 0x3F76, 0, 0, 0xDF56, 0xDF57, 0, 0, 0xDF58,
- 0, 0, 0xDF59, 0x422D, 0, 0xDF5A, 0, 0xDF5B,
- 0, 0xDF5C, 0x6C67, 0xDF5D, 0xDF6F, 0, 0x6C66, 0,
-};
-static const unsigned short utf8_to_euc_E8B6_x0213[] = {
- 0, 0x6C63, 0xDF4B, 0, 0xF936, 0x4436, 0, 0,
- 0xDF4D, 0, 0x315B, 0, 0, 0xDF4E, 0, 0,
- 0xDF4F, 0xDF50, 0, 0, 0, 0xF937, 0, 0,
- 0, 0x6C64, 0, 0, 0, 0, 0xDF52, 0xDF53,
- 0xDF54, 0, 0, 0x3C71, 0, 0, 0xF938, 0,
- 0x3F76, 0, 0, 0xDF56, 0xDF57, 0, 0, 0x7C40,
- 0, 0, 0xDF59, 0x422D, 0, 0xDF5A, 0, 0xDF5B,
- 0, 0xDF5C, 0x6C67, 0xDF5D, 0xDF6F, 0, 0x6C66, 0,
-};
-static const unsigned short utf8_to_euc_E8B7[] = {
- 0xDF5E, 0, 0x6C65, 0, 0, 0xDF5F, 0xDF60, 0xDF61,
- 0xDF62, 0, 0xDF63, 0x6C6D, 0x6C6B, 0, 0xDF64, 0x6C68,
- 0, 0xDF65, 0, 0, 0xDF66, 0xDF67, 0x6C6A, 0xDF68,
- 0, 0xDF69, 0x6C69, 0x6C6C, 0, 0x3577, 0, 0x6C70,
- 0, 0x4057, 0, 0x6C71, 0xDF6A, 0xDF6B, 0, 0xDF6C,
- 0x3859, 0, 0x6C6E, 0x6C6F, 0xDF6D, 0, 0, 0x4F29,
- 0xDF6E, 0xDF70, 0xDF71, 0x4437, 0xDF72, 0x4129, 0, 0,
- 0, 0, 0, 0, 0x6C72, 0xDF73, 0, 0x6C75,
-};
-static const unsigned short utf8_to_euc_E8B7_x0213[] = {
- 0xDF5E, 0, 0x6C65, 0, 0, 0xDF5F, 0xF93A, 0xDF61,
- 0xF93B, 0, 0xDF63, 0x6C6D, 0x6C6B, 0, 0x7C41, 0x6C68,
- 0, 0x7C42, 0, 0, 0xDF66, 0xDF67, 0x6C6A, 0x7C43,
- 0, 0xF93C, 0x6C69, 0x6C6C, 0, 0x3577, 0, 0x6C70,
- 0, 0x4057, 0, 0x6C71, 0xDF6A, 0xDF6B, 0, 0xDF6C,
- 0x3859, 0, 0x6C6E, 0x6C6F, 0xF93D, 0, 0, 0x4F29,
- 0xDF6E, 0xDF70, 0xDF71, 0x4437, 0xDF72, 0x4129, 0, 0,
- 0, 0, 0, 0, 0x6C72, 0xF940, 0, 0x6C75,
-};
-static const unsigned short utf8_to_euc_E8B8[] = {
- 0, 0xDF74, 0, 0, 0xDF75, 0xDF76, 0xDF77, 0,
- 0x6C73, 0x6C74, 0x4D59, 0xDF78, 0, 0, 0, 0x4627,
- 0x6C78, 0xDF79, 0, 0, 0xDF7A, 0, 0xDF7B, 0,
- 0, 0, 0, 0, 0, 0x6C76, 0x6C77, 0x6C79,
- 0xDF7C, 0xDF7D, 0xDF7E, 0xE021, 0, 0, 0xE022, 0xE023,
- 0, 0, 0x6D29, 0, 0, 0, 0, 0,
- 0x6C7C, 0xE024, 0, 0xE025, 0x6C7D, 0x6C7B, 0xE026, 0xE027,
- 0xE028, 0xE029, 0, 0, 0, 0xE02A, 0, 0,
-};
-static const unsigned short utf8_to_euc_E8B8_x0213[] = {
- 0, 0xDF74, 0, 0, 0xDF75, 0xDF76, 0xF941, 0,
- 0x6C73, 0x6C74, 0x4D59, 0xDF78, 0xF93E, 0, 0, 0x4627,
- 0x6C78, 0xDF79, 0, 0, 0xF943, 0, 0xF944, 0,
- 0, 0, 0, 0, 0, 0x6C76, 0x6C77, 0x6C79,
- 0x7C44, 0xF945, 0xF946, 0x7C45, 0, 0, 0xE022, 0xF947,
- 0, 0, 0x6D29, 0, 0, 0, 0, 0,
- 0x6C7C, 0xE024, 0, 0xE025, 0x6C7D, 0x6C7B, 0xF94A, 0xE027,
- 0xE028, 0xF94B, 0, 0, 0, 0x7C46, 0, 0,
-};
-static const unsigned short utf8_to_euc_E8B9[] = {
- 0xE02B, 0xE02C, 0x6C7A, 0, 0x447D, 0, 0, 0x6D21,
- 0x6D25, 0x6D22, 0x6C7E, 0xE02D, 0x6D23, 0xE02E, 0xE02F, 0xE030,
- 0x6D24, 0, 0, 0, 0xE031, 0x6D2B, 0, 0,
- 0, 0x6D26, 0, 0xE032, 0xE033, 0xE034, 0xE035, 0x4058,
- 0x6D28, 0xE036, 0xE037, 0x6D2A, 0x6D27, 0, 0, 0,
- 0, 0xE038, 0, 0, 0xE039, 0xE03A, 0, 0xE03B,
- 0xE03C, 0xE03D, 0x6D2D, 0, 0x3D33, 0, 0x6D2C, 0,
- 0, 0xE03E, 0xE03F, 0xE040, 0x6D2E, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E8B9_x0213[] = {
- 0xE02B, 0xE02C, 0x6C7A, 0, 0x447D, 0, 0, 0x6D21,
- 0x6D25, 0x6D22, 0x6C7E, 0xF94C, 0x6D23, 0xE02E, 0xE02F, 0xE030,
- 0x6D24, 0, 0, 0, 0xF94D, 0x6D2B, 0, 0,
- 0, 0x6D26, 0, 0xE032, 0xE033, 0xE034, 0xE035, 0x4058,
- 0x6D28, 0xE036, 0xF94E, 0x6D2A, 0x6D27, 0, 0, 0,
- 0, 0xE038, 0, 0, 0xF94F, 0xF950, 0, 0xF951,
- 0x7C47, 0xE03D, 0x6D2D, 0, 0x3D33, 0, 0x6D2C, 0,
- 0, 0xE03E, 0xE03F, 0x7C48, 0x6D2E, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E8BA[] = {
- 0, 0x6D2F, 0xE041, 0xE042, 0x6D32, 0x6D31, 0, 0x6D30,
- 0, 0xE043, 0x6D34, 0x6D33, 0, 0x4C76, 0, 0,
- 0xE044, 0x6D36, 0xE045, 0x6D35, 0x6D37, 0xE046, 0, 0,
- 0, 0x6D38, 0xE047, 0xE048, 0, 0xE049, 0xE04A, 0,
- 0, 0x6D3A, 0xE04B, 0, 0, 0, 0, 0xE04C,
- 0, 0xE04D, 0x6D39, 0x3F48, 0x6D3B, 0xE04E, 0xE04F, 0x366D,
- 0x6D3C, 0x6D3E, 0, 0xE050, 0, 0xE051, 0, 0,
- 0, 0, 0xE052, 0xE053, 0, 0, 0x6D3F, 0,
-};
-static const unsigned short utf8_to_euc_E8BA_x0213[] = {
- 0, 0x6D2F, 0xE041, 0xE042, 0x6D32, 0x6D31, 0, 0x6D30,
- 0, 0xE043, 0x6D34, 0x6D33, 0, 0x4C76, 0, 0,
- 0xE044, 0x6D36, 0xE045, 0x6D35, 0x6D37, 0xE046, 0, 0,
- 0xF952, 0x6D38, 0xE047, 0xE048, 0, 0xE049, 0xF953, 0,
- 0, 0x6D3A, 0xE04B, 0, 0, 0, 0, 0xE04C,
- 0, 0xE04D, 0x6D39, 0x3F48, 0x6D3B, 0xE04E, 0xF954, 0x366D,
- 0x6D3C, 0x6D3E, 0, 0xF955, 0, 0xF956, 0xF957, 0,
- 0, 0, 0xE052, 0xF958, 0, 0, 0x6D3F, 0,
-};
-static const unsigned short utf8_to_euc_E8BB[] = {
- 0xE054, 0xE055, 0, 0xE056, 0xE057, 0x6D40, 0x6D3D, 0xE058,
- 0x6D41, 0, 0x3C56, 0x6D42, 0x3530, 0x3733, 0, 0xE059,
- 0, 0xE05A, 0x382E, 0, 0xE05B, 0, 0, 0,
- 0, 0, 0, 0x6D43, 0xE05C, 0, 0, 0x4670,
- 0, 0, 0x453E, 0x6D44, 0, 0, 0, 0,
- 0xE05D, 0, 0, 0x6D47, 0, 0xE064, 0xE05E, 0,
- 0xE05F, 0xE060, 0, 0, 0, 0, 0, 0xE061,
- 0x3C34, 0xE062, 0xE063, 0x6D46, 0x6D45, 0x375A, 0x6D48, 0,
-};
-static const unsigned short utf8_to_euc_E8BB_x0213[] = {
- 0x7C4A, 0xE055, 0, 0xE056, 0xE057, 0x6D40, 0x6D3D, 0xE058,
- 0x6D41, 0, 0x3C56, 0x6D42, 0x3530, 0x3733, 0, 0,
- 0, 0xF95A, 0x382E, 0, 0xF95B, 0, 0, 0,
- 0, 0, 0, 0x6D43, 0xE05C, 0, 0, 0x4670,
- 0, 0, 0x453E, 0x6D44, 0, 0, 0, 0,
- 0xE05D, 0, 0, 0x6D47, 0, 0xE064, 0xE05E, 0,
- 0xE05F, 0xE060, 0, 0, 0, 0, 0, 0xE061,
- 0x3C34, 0xF95D, 0x7C4C, 0x6D46, 0x6D45, 0x375A, 0x6D48, 0,
-};
-static const unsigned short utf8_to_euc_E8BC[] = {
- 0xE065, 0, 0xE066, 0x3353, 0, 0x6D4A, 0, 0xE067,
- 0xE068, 0x3A5C, 0x6D49, 0, 0x6D52, 0, 0, 0xE069,
- 0xE06A, 0, 0x6D4C, 0x6D4E, 0x4A65, 0x6D4B, 0xE06B, 0xE06C,
- 0xE06D, 0x6D4D, 0, 0x6D51, 0x6D4F, 0x3531, 0xE06E, 0x6D50,
- 0xE06F, 0xE070, 0, 0xE071, 0, 0xE072, 0x6D53, 0xE073,
- 0xE074, 0x475A, 0x4E58, 0, 0xE075, 0xE076, 0xE077, 0x3D34,
- 0, 0, 0, 0x6D54, 0xE078, 0xE079, 0xE07A, 0xE07B,
- 0x4D22, 0x6D56, 0xE07C, 0x6D55, 0, 0, 0x6D59, 0x4D41,
-};
-static const unsigned short utf8_to_euc_E8BC_x0213[] = {
- 0xF95F, 0, 0xE066, 0x3353, 0, 0x6D4A, 0, 0xE067,
- 0xF960, 0x3A5C, 0x6D49, 0, 0x6D52, 0, 0, 0xE069,
- 0xE06A, 0, 0x6D4C, 0x6D4E, 0x4A65, 0x6D4B, 0xE06B, 0xF961,
- 0xE06D, 0x6D4D, 0, 0x6D51, 0x6D4F, 0x3531, 0x7C4D, 0x6D50,
- 0xE06F, 0xE070, 0, 0xE071, 0, 0xE072, 0x6D53, 0xE073,
- 0xE074, 0x475A, 0x4E58, 0xF962, 0xE075, 0x7C4E, 0xE077, 0x3D34,
- 0, 0, 0, 0x6D54, 0xE078, 0xE079, 0x7C4F, 0xE07B,
- 0x4D22, 0x6D56, 0xE07C, 0x6D55, 0, 0, 0x6D59, 0x4D41,
-};
-static const unsigned short utf8_to_euc_E8BD[] = {
- 0xE07D, 0xE07E, 0x6D58, 0xE121, 0x336D, 0x6D57, 0x6D5C, 0xE122,
- 0, 0x6D5B, 0, 0, 0x6D5A, 0x4532, 0x6D5D, 0xE123,
- 0, 0xE124, 0xE125, 0xE126, 0xE127, 0xE128, 0, 0x6D5E,
- 0xE129, 0, 0, 0, 0x6D5F, 0xE12A, 0xE12B, 0x396C,
- 0, 0x3725, 0x6D60, 0x6D61, 0x6D62, 0xE12C, 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,
-};
-static const unsigned short utf8_to_euc_E8BD_x0213[] = {
- 0xF963, 0xE07E, 0x6D58, 0xE121, 0x336D, 0x6D57, 0x6D5C, 0xE122,
- 0, 0x6D5B, 0xF964, 0, 0x6D5A, 0x4532, 0x6D5D, 0xE123,
- 0, 0xE124, 0xE125, 0xE126, 0x7C50, 0xE128, 0, 0x6D5E,
- 0xF965, 0, 0, 0, 0x6D5F, 0xE12A, 0xE12B, 0x396C,
- 0, 0x3725, 0x6D60, 0x6D61, 0x6D62, 0xE12C, 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,
-};
-static const unsigned short utf8_to_euc_E8BE[] = {
- 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, 0x3F49, 0x6D63, 0xE12D, 0x3C2D, 0x6D64,
- 0xE12E, 0xE12F, 0, 0x6D65, 0xE130, 0xE131, 0xE132, 0x5221,
- 0x517E, 0, 0, 0, 0, 0x6D66, 0x6570, 0x6D67,
- 0x4324, 0x3F2B, 0x4740, 0, 0, 0xE133, 0xE134, 0x6D68,
- 0xE135, 0, 0x4A55, 0x4454, 0x397E, 0, 0xE136, 0x4329,
-};
-static const unsigned short utf8_to_euc_E8BE_x0213[] = {
- 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, 0x3F49, 0x6D63, 0xE12D, 0x3C2D, 0x6D64,
- 0xE12E, 0xE12F, 0, 0x6D65, 0xF967, 0xE131, 0x7C52, 0x5221,
- 0x517E, 0, 0, 0, 0, 0x6D66, 0x6570, 0x6D67,
- 0x4324, 0x3F2B, 0x4740, 0, 0xF968, 0x7C53, 0xF96A, 0x6D68,
- 0xE135, 0, 0x4A55, 0x4454, 0x397E, 0, 0xE136, 0x4329,
-};
-static const unsigned short utf8_to_euc_E8BF[] = {
- 0xE137, 0xE138, 0x312A, 0, 0x4B78, 0x3F57, 0xE139, 0,
- 0, 0, 0xE13A, 0xE13B, 0, 0xE13C, 0x375E, 0,
- 0xE13D, 0x3661, 0xE13E, 0xE13F, 0x4A56, 0xE140, 0, 0,
- 0, 0, 0x6D69, 0, 0, 0, 0, 0,
- 0xE141, 0, 0x6D6B, 0xE142, 0xE143, 0x6D6A, 0x3260, 0,
- 0xE144, 0x4676, 0x6D6C, 0x4777, 0, 0x4533, 0xE145, 0x6D6D,
- 0x3D52, 0xE146, 0, 0, 0x6D6F, 0xE147, 0xE148, 0x4C42,
- 0x6D7E, 0x6D71, 0x6D72, 0xE149, 0, 0x4449, 0xE14A, 0,
-};
-static const unsigned short utf8_to_euc_E8BF_x0213[] = {
- 0xE137, 0xF96C, 0x312A, 0, 0x4B78, 0x3F57, 0xF96D, 0,
- 0, 0, 0xF96F, 0xE13B, 0, 0xF970, 0x375E, 0,
- 0xE13D, 0x3661, 0xE13E, 0xF971, 0x4A56, 0xF972, 0, 0,
- 0, 0, 0x6D69, 0, 0, 0, 0, 0,
- 0xF973, 0, 0x6D6B, 0xE142, 0x7C54, 0x6D6A, 0x3260, 0,
- 0x7C55, 0x4676, 0x6D6C, 0x4777, 0, 0x4533, 0x7C56, 0x6D6D,
- 0x3D52, 0xF974, 0, 0, 0x6D6F, 0xF975, 0xE148, 0x4C42,
- 0x6D7E, 0x6D71, 0x6D72, 0xF976, 0, 0x4449, 0xE14A, 0,
-};
-static const unsigned short utf8_to_euc_E980[] = {
- 0x4260, 0x4177, 0xE14B, 0x4628, 0xE14C, 0x6D70, 0x3555, 0,
- 0xE14D, 0, 0, 0x6D79, 0xE14E, 0x6D76, 0x6E25, 0x4629,
- 0x4360, 0x6D73, 0, 0x447E, 0x4553, 0x6D74, 0x6D78, 0x3F60,
- 0xE14F, 0x4767, 0x444C, 0xE150, 0, 0x4042, 0x6D77, 0x422E,
- 0x4224, 0x6D75, 0x3029, 0x4F22, 0, 0, 0, 0x6D7A,
- 0xE151, 0xE152, 0xE154, 0, 0xE155, 0xE156, 0x4261, 0xE153,
- 0, 0x3D35, 0x3F4A, 0xE157, 0xE158, 0x6D7C, 0x6D7B, 0xE159,
- 0x306F, 0x6D7D, 0, 0, 0x492F, 0, 0x6E27, 0xE15A,
-};
-static const unsigned short utf8_to_euc_E980_x0213[] = {
- 0x4260, 0x4177, 0xF977, 0x4628, 0xE14C, 0x6D70, 0x3555, 0,
- 0x7C57, 0, 0, 0x6D79, 0xF978, 0x6D76, 0x6E25, 0x4629,
- 0x4360, 0x6D73, 0, 0x447E, 0x4553, 0x6D74, 0x6D78, 0x3F60,
- 0xE14F, 0x4767, 0x444C, 0xE150, 0, 0x4042, 0x6D77, 0x422E,
- 0x4224, 0x6D75, 0x3029, 0x4F22, 0, 0, 0, 0x6D7A,
- 0xE151, 0xE152, 0xE154, 0, 0xE155, 0x7C58, 0x4261, 0xE153,
- 0, 0x3D35, 0x3F4A, 0xE157, 0xE158, 0x6D7C, 0x6D7B, 0xF979,
- 0x306F, 0x6D7D, 0, 0, 0x492F, 0, 0x6E27, 0xE15A,
-};
-static const unsigned short utf8_to_euc_E981[] = {
- 0, 0x465B, 0x3F6B, 0xE15B, 0xE15C, 0x4359, 0, 0x3678,
- 0, 0x6E26, 0x4D37, 0x313F, 0xE15D, 0x4A57, 0x3261, 0x6E21,
- 0x6E22, 0x6E23, 0x6E24, 0x463B, 0x4323, 0x3063, 0x6E28, 0,
- 0x6E29, 0x7423, 0, 0xE15E, 0x423D, 0xE15F, 0x6E2A, 0,
- 0x3173, 0x414C, 0xE160, 0x382F, 0, 0x4D5A, 0xE161, 0xE162,
- 0x6E2B, 0x452C, 0, 0, 0xE163, 0x4178, 0x3C57, 0x6E2C,
- 0xE164, 0, 0x6E2F, 0, 0xE165, 0x3D65, 0x6E2D, 0x412B,
- 0x412A, 0xE166, 0x3064, 0, 0x4E4B, 0x6E31, 0, 0x4872,
-};
-static const unsigned short utf8_to_euc_E981_x0213[] = {
- 0, 0x465B, 0x3F6B, 0xF97B, 0xF97C, 0x4359, 0, 0x3678,
- 0, 0x6E26, 0x4D37, 0x313F, 0xE15D, 0x4A57, 0x3261, 0x6E21,
- 0x6E22, 0x6E23, 0x6E24, 0x463B, 0x4323, 0x3063, 0x6E28, 0,
- 0x6E29, 0x7423, 0, 0xE15E, 0x423D, 0xF97D, 0x6E2A, 0,
- 0x3173, 0x414C, 0xE160, 0x382F, 0, 0x4D5A, 0xE161, 0,
- 0x6E2B, 0x452C, 0, 0, 0xE163, 0x4178, 0x3C57, 0x6E2C,
- 0xE164, 0, 0x6E2F, 0, 0xE165, 0x3D65, 0x6E2D, 0x412B,
- 0x412A, 0xE166, 0x3064, 0, 0x4E4B, 0x6E31, 0, 0x4872,
-};
-static const unsigned short utf8_to_euc_E982[] = {
- 0x6E33, 0x6E32, 0x6E30, 0x6364, 0x3454, 0xE167, 0, 0x6D6E,
- 0xE168, 0x6E35, 0x6E34, 0xE169, 0xE16A, 0, 0xE16B, 0x6E36,
- 0xE16C, 0x4D38, 0, 0, 0, 0xE16D, 0, 0xE16E,
- 0xE16F, 0xE170, 0, 0xE171, 0, 0, 0, 0,
- 0xE172, 0xE173, 0xE174, 0x4661, 0, 0xE175, 0x4B2E, 0,
- 0x6E37, 0, 0x3C59, 0, 0, 0, 0, 0x6E38,
- 0xE176, 0x6E39, 0xE177, 0xE178, 0xE179, 0x6E3A, 0xE17A, 0,
- 0x4521, 0, 0, 0, 0, 0xE17B, 0xE17D, 0,
-};
-static const unsigned short utf8_to_euc_E982_x0213[] = {
- 0x6E33, 0x6E32, 0x6E30, 0x6364, 0x3454, 0xFA22, 0, 0x6D6E,
- 0x7C5A, 0x6E35, 0x6E34, 0xE169, 0xFA23, 0, 0xE16B, 0x6E36,
- 0xFA24, 0x4D38, 0, 0, 0, 0x7C5B, 0, 0x7C5C,
- 0xE16F, 0x7C5D, 0, 0x7C5E, 0, 0, 0, 0,
- 0xE172, 0xFA26, 0x7C5F, 0x4661, 0, 0xE175, 0x4B2E, 0,
- 0x6E37, 0, 0x3C59, 0, 0, 0, 0, 0x6E38,
- 0xFA28, 0x6E39, 0xE177, 0x7C60, 0xE179, 0x6E3A, 0xFA29, 0,
- 0x4521, 0, 0, 0, 0, 0xE17B, 0x7C61, 0,
-};
-static const unsigned short utf8_to_euc_E983[] = {
- 0, 0x306A, 0, 0xE17E, 0xE221, 0xE222, 0, 0xE223,
- 0xE224, 0, 0x3959, 0, 0xE17C, 0, 0x4F3A, 0,
- 0, 0, 0xE22D, 0, 0, 0xE225, 0, 0xE226,
- 0xE227, 0xE228, 0, 0x6E3E, 0xE229, 0xE22A, 0xF46C, 0xE22B,
- 0, 0x3734, 0x6E3B, 0, 0x6E3C, 0xE22C, 0, 0,
- 0x4974, 0, 0, 0xE22F, 0, 0x3354, 0, 0xE230,
- 0xE231, 0, 0, 0, 0xE232, 0x4D39, 0xE22E, 0x363F,
- 0, 0, 0, 0, 0, 0x4554, 0xE233, 0xE234,
-};
-static const unsigned short utf8_to_euc_E983_x0213[] = {
- 0, 0x306A, 0, 0xFA2A, 0x7C62, 0x7C63, 0, 0x7C64,
- 0xFA2B, 0, 0x3959, 0, 0xE17C, 0, 0x4F3A, 0,
- 0, 0, 0xE22D, 0, 0, 0xE225, 0, 0x7C65,
- 0xE227, 0xE228, 0, 0x6E3E, 0xFA2D, 0x7C66, 0x7C67, 0xFA2E,
- 0, 0x3734, 0x6E3B, 0, 0x6E3C, 0xE22C, 0, 0,
- 0x4974, 0, 0, 0xFA33, 0, 0x3354, 0, 0x7C68,
- 0xE231, 0, 0xFA31, 0, 0x7C69, 0x4D39, 0xFA30, 0x363F,
- 0, 0, 0, 0, 0, 0x4554, 0xFA34, 0xFA35,
-};
-static const unsigned short utf8_to_euc_E984[] = {
- 0xE235, 0, 0x6E3F, 0, 0xE236, 0xE237, 0xE238, 0,
- 0xE239, 0, 0, 0, 0, 0xE23A, 0, 0,
- 0xE23B, 0, 0x6E40, 0, 0xE23C, 0xF46E, 0xE23D, 0xE23E,
- 0xE23F, 0x6E41, 0xE240, 0, 0xE241, 0, 0xE242, 0,
- 0xE243, 0, 0xE245, 0xE246, 0, 0xE244, 0, 0xE247,
- 0, 0xE248, 0, 0, 0, 0x4522, 0xE249, 0xE24A,
- 0x6E43, 0xE24B, 0x6E42, 0, 0xE24C, 0, 0xE24D, 0xE24E,
- 0, 0xE24F, 0xE250, 0, 0xE251, 0xE252, 0, 0,
-};
-static const unsigned short utf8_to_euc_E984_x0213[] = {
- 0xFA32, 0, 0x6E3F, 0, 0xFA36, 0xE237, 0xFA37, 0,
- 0xE239, 0, 0, 0, 0, 0xE23A, 0, 0,
- 0xE23B, 0, 0x6E40, 0, 0x7C6B, 0x7C6C, 0x7C6D, 0xE23E,
- 0xFA38, 0x6E41, 0xE240, 0, 0xFA39, 0, 0xFA3A, 0,
- 0xE243, 0, 0x7C6E, 0x7C6F, 0, 0xE244, 0, 0x7C70,
- 0, 0xE248, 0, 0, 0, 0x4522, 0xE249, 0x7C71,
- 0x6E43, 0x7C72, 0x6E42, 0, 0x7C73, 0, 0xE24D, 0xFA3B,
- 0, 0xFA3C, 0xFA3D, 0, 0xE251, 0x7C74, 0, 0,
-};
-static const unsigned short utf8_to_euc_E985[] = {
- 0, 0, 0, 0xE253, 0, 0, 0, 0xE254,
- 0xE255, 0x4653, 0x6E44, 0x3D36, 0x3C60, 0x475B, 0x4371, 0xE256,
- 0, 0, 0x3C72, 0xE257, 0x3F6C, 0, 0x6E45, 0xE258,
- 0x6E46, 0xE259, 0xE25A, 0xE25B, 0, 0, 0, 0,
- 0, 0xE25C, 0x3F5D, 0x6E47, 0xE25D, 0x6E48, 0, 0xE25E,
- 0, 0x6E49, 0x4D6F, 0, 0x3D37, 0xE25F, 0, 0,
- 0, 0, 0x6E4B, 0x6E4A, 0xE260, 0x395A, 0, 0x3973,
- 0x3B40, 0xE261, 0xE262, 0xE263, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E985_x0213[] = {
- 0, 0, 0, 0xE253, 0, 0, 0xFA3E, 0xFA3F,
- 0x7C75, 0x4653, 0x6E44, 0x3D36, 0x3C60, 0x475B, 0x4371, 0xE256,
- 0, 0, 0x3C72, 0xE257, 0x3F6C, 0, 0x6E45, 0xFA40,
- 0x6E46, 0xFA41, 0xE25A, 0x7C76, 0, 0, 0, 0,
- 0, 0xFA42, 0x3F5D, 0x6E47, 0xFA43, 0x6E48, 0, 0xE25E,
- 0, 0x6E49, 0x4D6F, 0, 0x3D37, 0xE25F, 0, 0,
- 0, 0, 0x6E4B, 0x6E4A, 0xFA44, 0x395A, 0, 0x3973,
- 0x3B40, 0xFA45, 0xE262, 0xE263, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E986[] = {
- 0, 0xE264, 0x6E4E, 0xE265, 0, 0xE266, 0xE267, 0x3D66,
- 0, 0x6E4D, 0xE268, 0x6E4C, 0, 0x4269, 0xE269, 0,
- 0x386F, 0xE26A, 0x4043, 0xE26B, 0xE26C, 0xE26D, 0, 0x4830,
- 0xE26E, 0, 0, 0, 0x3D39, 0, 0xE26F, 0,
- 0, 0xE270, 0x6E4F, 0, 0x3E5F, 0, 0xE271, 0,
- 0xE272, 0, 0x6E52, 0x6E50, 0xE273, 0xE274, 0xE275, 0x6E51,
- 0xE276, 0xE277, 0xE278, 0xE279, 0x6E54, 0x6E53, 0xE27A, 0,
- 0x3E7A, 0, 0x6E55, 0xE27B, 0xE27C, 0xE27D, 0, 0xE27E,
-};
-static const unsigned short utf8_to_euc_E986_x0213[] = {
- 0, 0xE264, 0x6E4E, 0x7C77, 0, 0xFA46, 0xE267, 0x3D66,
- 0, 0x6E4D, 0xE268, 0x6E4C, 0, 0x4269, 0xFA47, 0,
- 0x386F, 0xE26A, 0x4043, 0xE26B, 0xE26C, 0xE26D, 0, 0x4830,
- 0xE26E, 0, 0, 0, 0x3D39, 0, 0x7C78, 0,
- 0, 0xE270, 0x6E4F, 0, 0x3E5F, 0, 0xE271, 0,
- 0xFA48, 0, 0x6E52, 0x6E50, 0x7C79, 0xE274, 0xFA49, 0x6E51,
- 0xE276, 0x7C7A, 0xE278, 0xFA4A, 0x6E54, 0x6E53, 0xFA4B, 0,
- 0x3E7A, 0, 0x6E55, 0xE27B, 0x7C7B, 0xE27D, 0, 0xE27E,
-};
-static const unsigned short utf8_to_euc_E987[] = {
- 0x6E56, 0x6E57, 0xE321, 0xE322, 0, 0xE323, 0x4850, 0x3A53,
- 0x3C61, 0x6E58, 0, 0x6E59, 0x4E24, 0x3D45, 0x4C6E, 0x4E4C,
- 0x6E5A, 0x3662, 0, 0xE324, 0xE325, 0, 0x6E5B, 0xE326,
- 0x4523, 0xE327, 0xE328, 0x6E5E, 0x3378, 0x3F4B, 0xE329, 0x6E5C,
- 0, 0x6E5D, 0, 0x4460, 0xE32A, 0xE32B, 0x4B55, 0x367C,
- 0, 0xE32C, 0xE32D, 0, 0xE32E, 0xE32F, 0xE330, 0xE331,
- 0xE332, 0xE333, 0, 0, 0, 0x6E60, 0x6E61, 0xE334,
- 0, 0xE335, 0, 0xE336, 0x6E5F, 0xE337, 0, 0x6E63,
-};
-static const unsigned short utf8_to_euc_E987_x0213[] = {
- 0x6E56, 0x6E57, 0xE321, 0xFA4C, 0xFA4D, 0xE323, 0x4850, 0x3A53,
- 0x3C61, 0x6E58, 0, 0x6E59, 0x4E24, 0x3D45, 0x4C6E, 0x4E4C,
- 0x6E5A, 0x3662, 0, 0xE324, 0xE325, 0, 0x6E5B, 0x7C7C,
- 0x4523, 0xE327, 0xFA4E, 0x6E5E, 0x3378, 0x3F4B, 0, 0x6E5C,
- 0, 0x6E5D, 0, 0x4460, 0x7C7E, 0x7D21, 0x4B55, 0x367C,
- 0, 0xE32C, 0xE32D, 0, 0xFA51, 0x7D22, 0xFA52, 0xE331,
- 0xE332, 0x7D23, 0, 0, 0, 0x6E60, 0x6E61, 0xE334,
- 0, 0xE335, 0, 0x7C7D, 0x6E5F, 0xE337, 0, 0x6E63,
-};
-static const unsigned short utf8_to_euc_E988[] = {
- 0xE338, 0xE339, 0, 0, 0xE33A, 0xE33B, 0xE33C, 0xE33D,
- 0, 0xE33E, 0xE33F, 0, 0xE340, 0x465F, 0x3343, 0,
- 0xE341, 0x6E67, 0xE342, 0xE343, 0x6E64, 0x6E66, 0xE344, 0,
- 0xE345, 0, 0, 0, 0xE346, 0xE347, 0x6E62, 0,
- 0, 0, 0, 0xE348, 0xE349, 0xE34A, 0xE34B, 0,
- 0xE34C, 0x6F4F, 0, 0, 0x6E65, 0, 0xE34D, 0xE34E,
- 0xE34F, 0, 0, 0xE350, 0x4E6B, 0xE351, 0xE352, 0x385A,
- 0xE353, 0xE354, 0xE355, 0, 0xE356, 0, 0xE357, 0x6E6F,
-};
-static const unsigned short utf8_to_euc_E988_x0213[] = {
- 0xE338, 0xFA53, 0, 0, 0xE33A, 0xE33B, 0, 0x7D24,
- 0, 0xE33E, 0xFA54, 0, 0xE340, 0x465F, 0x3343, 0,
- 0x7D25, 0x6E67, 0xE342, 0xE343, 0x6E64, 0x6E66, 0xFA55, 0xFA56,
- 0xE345, 0, 0, 0, 0xE346, 0xE347, 0x6E62, 0,
- 0, 0, 0, 0xE348, 0xE349, 0xE34A, 0xE34B, 0,
- 0xE34C, 0x6F4F, 0, 0, 0x6E65, 0, 0xE34D, 0xE34E,
- 0xE34F, 0, 0, 0xFA58, 0x4E6B, 0xE351, 0xE352, 0x385A,
- 0x7D26, 0x7D27, 0x7D28, 0, 0x7D29, 0, 0xE357, 0x6E6F,
-};
-static const unsigned short utf8_to_euc_E989[] = {
- 0xE358, 0, 0xE359, 0xE35A, 0x4534, 0x6E6A, 0xE35B, 0xE35C,
- 0x6E6D, 0x6E6B, 0xE35D, 0x6E70, 0, 0xE35E, 0xE35F, 0xE360,
- 0x6E71, 0xE361, 0, 0, 0, 0, 0, 0x6E69,
- 0xE362, 0xE363, 0x6E76, 0x3174, 0xE364, 0xE365, 0x6E68, 0,
- 0xE366, 0xE367, 0x482D, 0, 0x6E6C, 0xE368, 0x3E60, 0xE369,
- 0xE36A, 0xE36B, 0, 0, 0, 0, 0xE36C, 0xE36D,
- 0xE36E, 0x395B, 0, 0, 0, 0xE36F, 0xE370, 0xE371,
- 0xE372, 0xE373, 0, 0xE374, 0xE375, 0xE376, 0x4B48, 0xE377,
-};
-static const unsigned short utf8_to_euc_E989_x0213[] = {
- 0x7D2A, 0, 0xFA59, 0x7D2B, 0x4534, 0x6E6A, 0xE35B, 0xFA5A,
- 0x6E6D, 0x6E6B, 0xFA5B, 0x6E70, 0, 0xE35E, 0xFA5C, 0x7D2C,
- 0x6E71, 0xFA5D, 0, 0, 0, 0, 0xFA5E, 0x6E69,
- 0xE362, 0xFA5F, 0x6E76, 0x3174, 0xE364, 0xE365, 0x6E68, 0,
- 0xFA60, 0xFA61, 0x482D, 0, 0x6E6C, 0xFA62, 0x3E60, 0xFA63,
- 0xFA64, 0xE36B, 0, 0, 0, 0, 0xE36C, 0xE36D,
- 0xE36E, 0x395B, 0, 0, 0, 0xE36F, 0xE370, 0,
- 0x7D2D, 0xE373, 0, 0xE374, 0xFA67, 0xFA68, 0x4B48, 0xFA69,
-};
-static const unsigned short utf8_to_euc_E98A[] = {
- 0x3664, 0, 0, 0x3D46, 0, 0x463C, 0, 0,
- 0xE378, 0xE379, 0xE37A, 0, 0, 0xE37B, 0xE37C, 0,
- 0, 0x412D, 0xE37D, 0x6E74, 0, 0x6E6E, 0x6E73, 0xE37E,
- 0x4C43, 0xE421, 0x4438, 0x6E75, 0x6E72, 0, 0, 0xE422,
- 0xE423, 0, 0, 0, 0xE424, 0xE425, 0, 0xE426,
- 0xE427, 0, 0, 0xE428, 0, 0x412C, 0, 0xE429,
- 0, 0, 0xE42A, 0, 0, 0, 0xE42B, 0x6E79,
- 0xE42C, 0x6E78, 0xE42D, 0xE42E, 0xE42F, 0xE430, 0, 0xE431,
-};
-static const unsigned short utf8_to_euc_E98A_x0213[] = {
- 0x3664, 0, 0, 0x3D46, 0, 0x463C, 0, 0,
- 0x7D2E, 0xFA6A, 0xE37A, 0, 0, 0xFA6B, 0xE37C, 0,
- 0, 0x412D, 0xE37D, 0x6E74, 0, 0x6E6E, 0x6E73, 0xFA6C,
- 0x4C43, 0xFA6D, 0x4438, 0x6E75, 0x6E72, 0, 0, 0xFA6E,
- 0xE423, 0, 0, 0, 0xE424, 0xE425, 0, 0xFA6F,
- 0xE427, 0, 0, 0xFA70, 0, 0x412C, 0, 0xE429,
- 0, 0, 0xFA73, 0, 0, 0, 0xE42B, 0x6E79,
- 0xE42C, 0x6E78, 0xE42D, 0xE42E, 0xE42F, 0xE430, 0, 0xFA74,
-};
-static const unsigned short utf8_to_euc_E98B[] = {
- 0xE432, 0xE433, 0xE434, 0xE435, 0, 0xE436, 0xE437, 0xE438,
- 0xE439, 0, 0, 0xE43A, 0xE43B, 0xE43C, 0xE43D, 0x6E77,
- 0xE43E, 0, 0x4B2F, 0xE43F, 0, 0xE440, 0, 0xE441,
- 0xE442, 0xE443, 0, 0, 0xE444, 0xE445, 0, 0xE446,
- 0xE447, 0xE448, 0, 0xE449, 0x3D7B, 0xE44A, 0, 0xE44B,
- 0xE44C, 0x6E7A, 0x4A5F, 0, 0xE44D, 0x3154, 0xE44E, 0,
- 0xE44F, 0, 0x4946, 0x4372, 0, 0, 0, 0,
- 0x3578, 0xE450, 0x6E7C, 0xE451, 0x395D, 0, 0, 0xE452,
-};
-static const unsigned short utf8_to_euc_E98B_x0213[] = {
- 0xFA75, 0xE433, 0x7D2F, 0xE435, 0, 0xE436, 0xFA76, 0xE438,
- 0xE439, 0, 0, 0x7D30, 0x7D31, 0xE43C, 0xFA77, 0x6E77,
- 0xFA78, 0, 0x4B2F, 0x7D32, 0, 0, 0, 0xFA79,
- 0xE442, 0xFA7A, 0, 0, 0xE444, 0xE445, 0, 0xE446,
- 0x7D33, 0xE448, 0, 0xE449, 0x3D7B, 0xFA7B, 0, 0xFA7C,
- 0xE44C, 0x6E7A, 0x4A5F, 0, 0xE44D, 0x3154, 0xE44E, 0,
- 0xE44F, 0, 0x4946, 0x4372, 0, 0, 0, 0xFB22,
- 0x3578, 0xFB23, 0x6E7C, 0xFB24, 0x395D, 0, 0, 0x7D34,
-};
-static const unsigned short utf8_to_euc_E98C[] = {
- 0xE453, 0, 0xE454, 0, 0, 0, 0x3B2C, 0,
- 0xE455, 0, 0, 0, 0, 0xE456, 0, 0x6E7B,
- 0x3F6D, 0xE457, 0, 0, 0xE458, 0xE459, 0, 0,
- 0x3F6E, 0x6F21, 0x6F23, 0, 0xE45A, 0xE45B, 0xE45C, 0xE45D,
- 0x3E7B, 0xE45E, 0x6F22, 0x6F24, 0xE45F, 0xE460, 0x3653, 0xE461,
- 0x4945, 0xE462, 0xE463, 0x3C62, 0x4F23, 0, 0x6E7E, 0x3A78,
- 0, 0, 0x4F3F, 0xE464, 0xE465, 0x6F26, 0xE466, 0xE467,
- 0, 0, 0x6F25, 0x6F27, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E98C_x0213[] = {
- 0xE453, 0, 0xFB25, 0, 0x7D35, 0, 0x3B2C, 0,
- 0xE455, 0, 0, 0, 0, 0xFB26, 0, 0x6E7B,
- 0x3F6D, 0xFA7D, 0, 0, 0xE458, 0xFB27, 0, 0,
- 0x3F6E, 0x6F21, 0x6F23, 0, 0xE45A, 0xFB28, 0xFB29, 0x7D36,
- 0x3E7B, 0x7D37, 0x6F22, 0x6F24, 0xE45F, 0x7D38, 0x3653, 0xFB2A,
- 0x4945, 0xFB2B, 0xE463, 0x3C62, 0x4F23, 0, 0x6E7E, 0x3A78,
- 0, 0, 0x4F3F, 0xE464, 0xE465, 0x6F26, 0xE466, 0xE467,
- 0, 0, 0x6F25, 0x6F27, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E98D[] = {
- 0, 0, 0, 0, 0x6E7D, 0, 0, 0xE468,
- 0xE469, 0xE46A, 0, 0x4669, 0, 0x4555, 0, 0,
- 0xE46B, 0xE46C, 0xE46D, 0, 0x4457, 0xE46E, 0x6F2C, 0xE46F,
- 0xE470, 0, 0xE471, 0x4343, 0x6F28, 0, 0xE472, 0,
- 0x6F29, 0, 0, 0, 0xE473, 0xE474, 0, 0xE475,
- 0, 0xE476, 0xE477, 0, 0x372D, 0xE478, 0x6F2B, 0xE479,
- 0xE47A, 0xE47B, 0, 0xE47C, 0xE47D, 0x3830, 0xE47E, 0,
- 0, 0, 0xE521, 0, 0x6F2A, 0xE522, 0x3E61, 0xE523,
-};
-static const unsigned short utf8_to_euc_E98D_x0213[] = {
- 0, 0, 0, 0, 0x6E7D, 0, 0, 0xFB2E,
- 0x7D39, 0x7D3A, 0x7D3B, 0x4669, 0, 0x4555, 0, 0,
- 0xE46B, 0xFB2F, 0xE46D, 0, 0x4457, 0xE46E, 0x6F2C, 0xFB30,
- 0xE470, 0, 0xFB31, 0x4343, 0x6F28, 0, 0xE472, 0,
- 0x6F29, 0, 0, 0, 0x7D3C, 0x7D3D, 0, 0xE475,
- 0, 0xE476, 0x7D3E, 0xFB32, 0x372D, 0xE478, 0x6F2B, 0xE479,
- 0x7D3F, 0xFB33, 0, 0xFB34, 0xE47D, 0x3830, 0xE47E, 0,
- 0, 0, 0xE521, 0, 0x6F2A, 0xE522, 0x3E61, 0xE523,
-};
-static const unsigned short utf8_to_euc_E98E[] = {
- 0xE524, 0xE525, 0xE526, 0, 0, 0, 0, 0,
- 0xE527, 0, 0xE528, 0xE529, 0x3379, 0xE52A, 0, 0xE52B,
- 0, 0, 0xE52C, 0, 0x6F30, 0xE52D, 0x3A3F, 0x4179,
- 0xE52E, 0, 0x444A, 0xE52F, 0, 0, 0xE530, 0,
- 0, 0xE531, 0, 0xE532, 0xE533, 0, 0xE534, 0x333B,
- 0xE535, 0xE53B, 0, 0xE536, 0x6F2E, 0x6F2F, 0x4443, 0,
- 0x6F2D, 0, 0, 0, 0xE537, 0xE538, 0xE539, 0,
- 0, 0x6F31, 0xE53A, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E98E_x0213[] = {
- 0xE524, 0xE525, 0xE526, 0, 0, 0, 0, 0,
- 0xFB38, 0, 0xE528, 0xFB39, 0x3379, 0xE52A, 0, 0xFB3A,
- 0, 0, 0xE52C, 0, 0x6F30, 0xE52D, 0x3A3F, 0x4179,
- 0xE52E, 0, 0x444A, 0x7D40, 0, 0, 0xFB3B, 0,
- 0, 0xFB35, 0, 0x7D41, 0, 0, 0xE534, 0x333B,
- 0xE535, 0xE53B, 0, 0xE536, 0x6F2E, 0x6F2F, 0x4443, 0,
- 0x6F2D, 0, 0, 0, 0xE537, 0xE538, 0xE539, 0,
- 0, 0x6F31, 0x7D42, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E98F[] = {
- 0, 0xE53C, 0, 0x6F37, 0xE53D, 0xE53E, 0xE53F, 0xE540,
- 0x6F3A, 0xE541, 0xE542, 0xE543, 0xE544, 0xE545, 0, 0,
- 0x6F39, 0x452D, 0, 0xE546, 0, 0, 0x6F32, 0x6F33,
- 0x6F36, 0xE547, 0, 0, 0xE548, 0x6F38, 0xE549, 0xE54A,
- 0, 0x3640, 0xE54B, 0, 0x6F3B, 0x6F35, 0xE54C, 0xE54D,
- 0x6F34, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xE54F,
- 0xE550, 0xE54E, 0xE551, 0xE552, 0, 0xE553, 0, 0,
-};
-static const unsigned short utf8_to_euc_E98F_x0213[] = {
- 0, 0xFB40, 0, 0x6F37, 0xE53D, 0xE53E, 0x7D43, 0xFB41,
- 0x6F3A, 0xE541, 0xE542, 0xE543, 0xE544, 0xE545, 0, 0,
- 0x6F39, 0x452D, 0, 0xE546, 0, 0, 0x6F32, 0x6F33,
- 0x6F36, 0xE547, 0, 0, 0xFB42, 0x6F38, 0x7D44, 0x7D45,
- 0, 0x3640, 0xFB43, 0, 0x6F3B, 0x6F35, 0xE54C, 0xFB44,
- 0x6F34, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xFB3F, 0, 0, 0, 0xFB3C, 0, 0xE54F,
- 0, 0xE54E, 0xE551, 0xFB49, 0, 0x7D47, 0, 0,
-};
-static const unsigned short utf8_to_euc_E990[] = {
- 0, 0xE554, 0xE555, 0x6F3F, 0xE556, 0, 0, 0x6F40,
- 0xE557, 0xE558, 0, 0, 0, 0xE559, 0xE55A, 0xE55B,
- 0x6F41, 0, 0, 0x6F3E, 0x6F3D, 0xE55C, 0xE55D, 0xE55E,
- 0x3E62, 0x462A, 0x6F3C, 0, 0, 0, 0, 0xE55F,
- 0, 0x6F45, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x6F43, 0, 0, 0xE560, 0xE561,
- 0, 0xE562, 0xE563, 0xE564, 0xE565, 0x6F44, 0x6F42, 0,
- 0x4278, 0, 0x6F46, 0xE566, 0, 0xE568, 0, 0xE567,
-};
-static const unsigned short utf8_to_euc_E990_x0213[] = {
- 0, 0xE554, 0xE555, 0x6F3F, 0x7D46, 0, 0, 0x6F40,
- 0xE557, 0xFB45, 0, 0, 0, 0xE559, 0xE55A, 0xFB46,
- 0x6F41, 0, 0, 0x6F3E, 0x6F3D, 0xE55C, 0xFB47, 0xFB48,
- 0x3E62, 0x462A, 0x6F3C, 0, 0, 0, 0, 0xE55F,
- 0, 0x6F45, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x6F43, 0, 0, 0xE560, 0xE561,
- 0, 0, 0xFB4A, 0x7D48, 0xFB4B, 0x6F44, 0x6F42, 0,
- 0x4278, 0, 0x6F46, 0xFB4C, 0, 0xE568, 0, 0xE567,
-};
-static const unsigned short utf8_to_euc_E991[] = {
- 0, 0x6F47, 0, 0xE569, 0x6F49, 0xE56A, 0, 0,
- 0xE56B, 0, 0xE56C, 0, 0xE56D, 0, 0, 0,
- 0, 0x3455, 0x6F48, 0x4C7A, 0, 0xE56E, 0, 0,
- 0, 0xE56F, 0x6F54, 0x6F4A, 0xE570, 0, 0x6F4D, 0xE571,
- 0x6F4B, 0xE572, 0x6F4C, 0xE573, 0, 0, 0, 0,
- 0xE574, 0, 0x6F4E, 0xE575, 0, 0xE576, 0xE577, 0xE578,
- 0x6F50, 0xE579, 0xE57A, 0, 0, 0x6F51, 0, 0x6F52,
- 0, 0, 0, 0, 0x6F55, 0x6F53, 0x6F56, 0x6F58,
-};
-static const unsigned short utf8_to_euc_E991_x0213[] = {
- 0, 0x6F47, 0, 0xE569, 0x6F49, 0xFB4D, 0, 0,
- 0, 0, 0x7D49, 0, 0xE56D, 0, 0, 0,
- 0, 0x3455, 0x6F48, 0x4C7A, 0, 0xE56E, 0, 0,
- 0, 0xE56F, 0x6F54, 0x6F4A, 0xE570, 0, 0x6F4D, 0xE571,
- 0x6F4B, 0xE572, 0x6F4C, 0x7D4A, 0, 0, 0, 0,
- 0xE574, 0, 0x6F4E, 0x7D4B, 0, 0xFB50, 0xE577, 0xFB51,
- 0x6F50, 0x7D4C, 0x7D4D, 0, 0, 0x6F51, 0, 0x6F52,
- 0, 0, 0, 0, 0x6F55, 0x6F53, 0x6F56, 0x6F58,
-};
-static const unsigned short utf8_to_euc_E992[] = {
- 0, 0x6F57, 0, 0xE57C, 0xE57B, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E995[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0x4439,
- 0xE57D, 0xE57E, 0, 0, 0, 0, 0xE621, 0,
-};
-static const unsigned short utf8_to_euc_E995_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0x4439,
- 0xFB52, 0xFB53, 0, 0, 0, 0, 0xE621, 0,
-};
-static const unsigned short utf8_to_euc_E996[] = {
- 0x4C67, 0, 0x6F59, 0x412E, 0xE622, 0, 0, 0x6F5A,
- 0xE623, 0x4A44, 0x6F5B, 0x332B, 0xE624, 0xE625, 0xE626, 0x313C,
- 0, 0x3457, 0xF471, 0x3456, 0x6F5C, 0, 0x6F5D, 0,
- 0x6F5E, 0x6F5F, 0, 0, 0, 0xE627, 0xE628, 0xE629,
- 0x6F60, 0xE62A, 0x3458, 0x3355, 0x395E, 0x4836, 0xE62B, 0x6F62,
- 0x6F61, 0xE62C, 0, 0xE62D, 0xE62E, 0x6F63, 0, 0,
- 0, 0, 0x315C, 0, 0xE62F, 0, 0xE630, 0,
- 0, 0x6F66, 0xE631, 0x6F65, 0x6F64, 0xE632, 0x6F67, 0xE633,
-};
-static const unsigned short utf8_to_euc_E996_x0213[] = {
- 0x4C67, 0, 0x6F59, 0x412E, 0xE622, 0, 0xFB54, 0x6F5A,
- 0xE623, 0x4A44, 0x6F5B, 0x332B, 0xFB55, 0xFB56, 0x7D4E, 0x313C,
- 0, 0x3457, 0, 0x3456, 0x6F5C, 0, 0x6F5D, 0,
- 0x6F5E, 0x6F5F, 0, 0, 0, 0xE627, 0xE628, 0x7D4F,
- 0x6F60, 0xE62A, 0x3458, 0x3355, 0x395E, 0x4836, 0x7D50, 0x6F62,
- 0x6F61, 0x7D51, 0, 0xFB58, 0x7D52, 0x6F63, 0, 0,
- 0, 0, 0x315C, 0, 0xFB59, 0, 0x7D53, 0,
- 0, 0x6F66, 0xE631, 0x6F65, 0x6F64, 0x7D54, 0x6F67, 0xE633,
-};
-static const unsigned short utf8_to_euc_E997[] = {
- 0, 0, 0, 0x6F6A, 0, 0, 0xE634, 0x3047,
- 0xE635, 0xE636, 0x6F68, 0xE637, 0x6F6C, 0x6F6B, 0, 0,
- 0xE638, 0xE639, 0xE63A, 0xE63B, 0x6F6E, 0x6F6D, 0x6F6F, 0,
- 0x462E, 0xE63C, 0xE63D, 0, 0x6F70, 0xE63E, 0xE63F, 0xE640,
- 0xE641, 0x6F71, 0x6F73, 0, 0xE642, 0x6F72, 0xE643, 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,
-};
-static const unsigned short utf8_to_euc_E997_x0213[] = {
- 0, 0, 0, 0x6F6A, 0, 0, 0xE634, 0x3047,
- 0xFB5B, 0xE636, 0x6F68, 0x7D55, 0x6F6C, 0x6F6B, 0, 0,
- 0x7D56, 0xE639, 0xE63A, 0x7D57, 0x6F6E, 0x6F6D, 0x6F6F, 0,
- 0x462E, 0xE63C, 0x7D59, 0, 0x6F70, 0xE63E, 0x7D5A, 0xE640,
- 0xE641, 0x6F71, 0x6F73, 0, 0xE642, 0x6F72, 0xE643, 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,
-};
-static const unsigned short utf8_to_euc_E998[] = {
- 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, 0x496C, 0xE644, 0xE645, 0,
- 0, 0x6F74, 0xE646, 0, 0xE647, 0xE648, 0xE649, 0,
- 0x6F75, 0, 0x3A65, 0, 0xE64A, 0, 0x6F76, 0x6F77,
- 0, 0xE64B, 0x4B49, 0xE64C, 0, 0, 0, 0xE64D,
- 0xE64E, 0xE64F, 0xE650, 0x414B, 0xE651, 0xE652, 0, 0x3024,
-};
-static const unsigned short utf8_to_euc_E998_x0213[] = {
- 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, 0x496C, 0xFA25, 0xE645, 0,
- 0, 0x6F74, 0xE646, 0, 0xE647, 0xE648, 0xE649, 0,
- 0x6F75, 0, 0x3A65, 0, 0xFB5E, 0, 0x6F76, 0x6F77,
- 0, 0xE64B, 0x4B49, 0xFB5F, 0xFB60, 0, 0, 0xE64D,
- 0xE64E, 0xE64F, 0xE650, 0x414B, 0xFB62, 0xE652, 0, 0x3024,
-};
-static const unsigned short utf8_to_euc_E999[] = {
- 0x424B, 0xE653, 0x6F78, 0, 0x496D, 0, 0, 0,
- 0, 0, 0, 0x6F7B, 0x6F79, 0x395F, 0, 0x6F7A,
- 0x3842, 0, 0xE654, 0, 0xE655, 0, 0xE656, 0xE657,
- 0xE658, 0, 0, 0x4A45, 0x6F7D, 0x7021, 0x6F7E, 0x7022,
- 0, 0xE659, 0x3121, 0x3F58, 0x3D7C, 0x3459, 0x7023, 0,
- 0, 0, 0x4766, 0, 0x7025, 0, 0xE65A, 0,
- 0x3122, 0, 0x7024, 0x4444, 0xE65B, 0x4E4D, 0x462B, 0x6F7C,
- 0x4E26, 0, 0x3831, 0xE65C, 0xE65D, 0x4D5B, 0xE65E, 0xE65F,
-};
-static const unsigned short utf8_to_euc_E999_x0213[] = {
- 0x424B, 0xFB63, 0x6F78, 0, 0x496D, 0, 0, 0,
- 0, 0, 0, 0x6F7B, 0x6F79, 0x395F, 0, 0x6F7A,
- 0x3842, 0, 0xE654, 0, 0xE655, 0, 0xE656, 0xE657,
- 0x7D5B, 0, 0, 0x4A45, 0x6F7D, 0x7021, 0x6F7E, 0x7022,
- 0, 0xFB64, 0x3121, 0x3F58, 0x3D7C, 0x3459, 0x7023, 0,
- 0, 0, 0x4766, 0, 0x7025, 0, 0xE65A, 0,
- 0x3122, 0, 0x7024, 0x4444, 0xE65B, 0x4E4D, 0x462B, 0x6F7C,
- 0x4E26, 0, 0x3831, 0xE65C, 0xE65D, 0x4D5B, 0xE65E, 0xE65F,
-};
-static const unsigned short utf8_to_euc_E99A[] = {
- 0, 0xE660, 0xE661, 0xE662, 0xE663, 0x3679, 0x4E34, 0,
- 0x3728, 0xE664, 0x4262, 0x6721, 0, 0x7026, 0x332C, 0x3F6F,
- 0, 0xE665, 0, 0, 0x3356, 0x7028, 0xE666, 0x7029,
- 0x7027, 0x3764, 0xE667, 0x3A5D, 0x3E63, 0xE668, 0, 0xE669,
- 0x3123, 0, 0, 0x4E59, 0xE66A, 0xE66B, 0xE66C, 0x702B,
- 0x6E2E, 0xE66D, 0x702A, 0, 0, 0, 0xE66E, 0xE66F,
- 0x702E, 0x702C, 0x702D, 0xE670, 0x702F, 0, 0x7030, 0x4E6C,
- 0x7031, 0x7032, 0xE671, 0x4049, 0x483B, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E99A_x0213[] = {
- 0, 0xE660, 0xFB66, 0xE662, 0x7D5C, 0x3679, 0x4E34, 0,
- 0x3728, 0xE664, 0x4262, 0x6721, 0, 0x7026, 0x332C, 0x3F6F,
- 0, 0xE665, 0, 0, 0x3356, 0x7028, 0xE666, 0x7029,
- 0x7027, 0x3764, 0xFB68, 0x3A5D, 0x3E63, 0x7D5E, 0, 0xE669,
- 0x3123, 0, 0, 0x4E59, 0x7D5F, 0x7D60, 0xE66C, 0x702B,
- 0x6E2E, 0xFB6B, 0x702A, 0, 0, 0, 0xE66E, 0xFB6C,
- 0x702E, 0x702C, 0x702D, 0xFB6D, 0x702F, 0, 0x7030, 0x4E6C,
- 0x7031, 0x7032, 0xFB6E, 0x4049, 0x483B, 0xFB6F, 0, 0,
-};
-static const unsigned short utf8_to_euc_E99B[] = {
- 0x3F7D, 0x3467, 0, 0, 0x4D3A, 0x326D, 0x3D38, 0x385B,
- 0, 0x7035, 0xE672, 0x7034, 0x3B73, 0x7036, 0x7033, 0,
- 0, 0x3B28, 0xE673, 0, 0, 0x703A, 0x6A2D, 0,
- 0xE675, 0x5256, 0xE676, 0x3F77, 0x7038, 0xE677, 0xE678, 0xE679,
- 0, 0, 0x4E25, 0x4671, 0, 0, 0, 0,
- 0x312B, 0xE67A, 0x4063, 0x3C36, 0, 0, 0, 0xE67B,
- 0x4A37, 0xE67C, 0x3140, 0, 0, 0, 0x4E6D, 0x4D6B,
- 0, 0x703B, 0xE67D, 0x4545, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E99B_x0213[] = {
- 0x3F7D, 0x3467, 0, 0, 0x4D3A, 0x326D, 0x3D38, 0x385B,
- 0, 0x7035, 0xE672, 0x7034, 0x3B73, 0x7036, 0x7033, 0,
- 0, 0x3B28, 0x7D61, 0, 0, 0x703A, 0x6A2D, 0,
- 0xFB72, 0x5256, 0xFB73, 0x3F77, 0x7038, 0xFB74, 0x7D62, 0xE679,
- 0, 0, 0x4E25, 0x4671, 0, 0, 0, 0,
- 0x312B, 0x7D64, 0x4063, 0x3C36, 0, 0, 0, 0x7D65,
- 0x4A37, 0xE67C, 0x3140, 0, 0, 0, 0x4E6D, 0x4D6B,
- 0, 0x703B, 0xE67D, 0x4545, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E99C[] = {
- 0x3C7B, 0, 0xE67E, 0xE721, 0x703C, 0xE722, 0x703D, 0x3F4C,
- 0x703E, 0xE723, 0x4E6E, 0, 0, 0x7039, 0x7040, 0x7042,
- 0, 0x7041, 0, 0x703F, 0, 0, 0x7043, 0,
- 0, 0x7044, 0xE724, 0xE725, 0x417A, 0xE726, 0x3262, 0,
- 0, 0xE727, 0xE728, 0xE729, 0x7045, 0, 0, 0x4C38,
- 0xE72A, 0, 0x7046, 0, 0, 0, 0, 0,
- 0x7047, 0xE72B, 0x4F2A, 0xE72C, 0, 0, 0, 0,
- 0x5B31, 0x7048, 0, 0xF474, 0, 0x7049, 0x704A, 0,
-};
-static const unsigned short utf8_to_euc_E99C_x0213[] = {
- 0x3C7B, 0, 0xE67E, 0xE721, 0x703C, 0xE722, 0x703D, 0x3F4C,
- 0x703E, 0xE723, 0x4E6E, 0, 0, 0x7039, 0x7040, 0x7042,
- 0, 0x7041, 0, 0x703F, 0xFB76, 0, 0x7043, 0,
- 0, 0x7044, 0xE724, 0xE725, 0x417A, 0xE726, 0x3262, 0,
- 0, 0xE727, 0xE728, 0xFB77, 0x7045, 0, 0, 0x4C38,
- 0xE72A, 0, 0x7046, 0, 0, 0, 0, 0,
- 0x7047, 0xE72B, 0x4F2A, 0x7D66, 0, 0, 0xFB79, 0,
- 0x5B31, 0x7048, 0, 0x7D67, 0, 0x7049, 0x704A, 0,
-};
-static const unsigned short utf8_to_euc_E99D[] = {
- 0, 0xE72D, 0x704E, 0xE72E, 0x704B, 0, 0x704C, 0,
- 0x704D, 0x704F, 0xE72F, 0, 0, 0xF475, 0xE730, 0xE731,
- 0, 0xF476, 0x4044, 0, 0, 0xE732, 0x4C77, 0xE733,
- 0xE734, 0x4045, 0xE735, 0xE736, 0x7050, 0, 0x4873, 0,
- 0x7051, 0x7353, 0x4C4C, 0xE737, 0x7052, 0, 0x7053, 0xE738,
- 0x7054, 0x3357, 0xE739, 0x7056, 0, 0x3F59, 0xE73A, 0,
- 0, 0x7057, 0, 0xE73B, 0x3724, 0, 0xE73C, 0xE73D,
- 0xE73E, 0x7058, 0x705C, 0xE73F, 0x705A, 0xE740, 0, 0xE741,
-};
-static const unsigned short utf8_to_euc_E99D_x0213[] = {
- 0, 0xFB7A, 0x704E, 0, 0x704B, 0, 0x704C, 0xFB7B,
- 0x704D, 0x704F, 0xE72F, 0, 0, 0x7D68, 0x7D69, 0x7D6A,
- 0, 0, 0x4044, 0, 0, 0xFB7C, 0x4C77, 0xFB7D,
- 0xE734, 0x4045, 0x7D6B, 0xFB7E, 0x7050, 0, 0x4873, 0,
- 0x7051, 0x7353, 0x4C4C, 0xE737, 0x7052, 0, 0x7053, 0xE738,
- 0x7054, 0x3357, 0xFC21, 0x7056, 0, 0x3F59, 0x7D6C, 0,
- 0, 0x7057, 0, 0x7D6D, 0x3724, 0, 0xE73C, 0xE73D,
- 0xE73E, 0x7058, 0x705C, 0xE73F, 0x705A, 0xE740, 0, 0xE741,
-};
-static const unsigned short utf8_to_euc_E99E[] = {
- 0xE742, 0x705B, 0, 0, 0x3373, 0x7059, 0x705D, 0,
- 0, 0xE743, 0, 0x705E, 0, 0x3048, 0, 0x705F,
- 0x7060, 0, 0, 0, 0, 0xE744, 0xE745, 0xE746,
- 0x3E64, 0xE747, 0xE748, 0, 0x7061, 0, 0xE749, 0xE74A,
- 0x3547, 0, 0xE74B, 0x7064, 0, 0, 0x7063, 0,
- 0x7062, 0, 0, 0x6B71, 0xE74C, 0x4A5C, 0xE74D, 0,
- 0, 0xE74E, 0xE74F, 0x7065, 0x7066, 0xE750, 0xE751, 0,
- 0xE752, 0xE753, 0xE754, 0, 0xE755, 0, 0xE756, 0xE757,
-};
-static const unsigned short utf8_to_euc_E99E_x0213[] = {
- 0xE742, 0x705B, 0, 0, 0x3373, 0x7059, 0x705D, 0,
- 0, 0xE743, 0, 0x705E, 0, 0x3048, 0, 0x705F,
- 0x7060, 0, 0, 0, 0, 0x7D6E, 0xFC24, 0xE746,
- 0x3E64, 0xE747, 0xFC25, 0, 0x7061, 0, 0xFC26, 0xE74A,
- 0x3547, 0, 0xFC27, 0x7064, 0, 0, 0x7063, 0,
- 0x7062, 0, 0, 0x6B71, 0xE74C, 0x4A5C, 0x7D6F, 0,
- 0, 0xFC28, 0xFC29, 0x7065, 0x7066, 0xE750, 0xE751, 0,
- 0xE752, 0xE753, 0x7D70, 0, 0xE755, 0, 0xFC2A, 0xE757,
-};
-static const unsigned short utf8_to_euc_E99F[] = {
- 0, 0xE758, 0, 0x7067, 0xE759, 0xE75A, 0x7068, 0xE75B,
- 0x7069, 0xE75C, 0xE75D, 0x706A, 0xE75E, 0xE75F, 0xE760, 0,
- 0xE761, 0xE762, 0, 0x345A, 0xE763, 0, 0, 0xE764,
- 0xE765, 0xE766, 0, 0xE76A, 0x706B, 0xE767, 0xE768, 0,
- 0xE769, 0xE76B, 0, 0, 0xE76C, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x706C, 0x4723, 0xE76D,
- 0, 0xE76E, 0x706E, 0x323B, 0xE76F, 0x7071, 0x7070, 0xE770,
- 0xE771, 0, 0xE772, 0x3124, 0, 0, 0, 0x3641,
-};
-static const unsigned short utf8_to_euc_E99F_x0213[] = {
- 0, 0x7D71, 0, 0x7067, 0xE759, 0xE75A, 0x7068, 0xE75B,
- 0x7069, 0x7D72, 0xE75D, 0x706A, 0xFC2B, 0xE75F, 0xE760, 0,
- 0xE761, 0xFC2C, 0, 0x345A, 0xFC2D, 0, 0, 0xE764,
- 0xFC2E, 0xFC2F, 0, 0x7D74, 0x706B, 0xE767, 0x7D73, 0,
- 0xE769, 0xFC30, 0, 0, 0xE76C, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x706C, 0x4723, 0xE76D,
- 0, 0xFC31, 0x706E, 0x323B, 0x7D75, 0x7071, 0x7070, 0xE770,
- 0xE771, 0, 0xE772, 0x3124, 0, 0, 0, 0x3641,
-};
-static const unsigned short utf8_to_euc_E9A0[] = {
- 0, 0x4A47, 0x443A, 0x3A22, 0, 0x3960, 0x3D67, 0xE773,
- 0x3F5C, 0, 0xE774, 0, 0x7073, 0xE776, 0xE777, 0x7072,
- 0x4D42, 0x3468, 0x4852, 0x465C, 0xE778, 0, 0xE779, 0x3F7C,
- 0x4E4E, 0xE775, 0x375B, 0, 0xE77A, 0, 0xE77B, 0,
- 0xE77C, 0x7076, 0, 0xE77D, 0x7075, 0xE828, 0xE77E, 0,
- 0, 0, 0, 0xE821, 0x4B4B, 0x462C, 0xE822, 0xE823,
- 0xE824, 0, 0xE825, 0xE826, 0x3150, 0xE827, 0, 0x7077,
- 0x7074, 0, 0, 0x4951, 0x4D6A, 0x7078, 0xE829, 0,
-};
-static const unsigned short utf8_to_euc_E9A0_x0213[] = {
- 0, 0x4A47, 0x443A, 0x3A22, 0xFC32, 0x3960, 0x3D67, 0xE773,
- 0x3F5C, 0, 0x7D77, 0, 0x7073, 0xFC33, 0xFC34, 0x7072,
- 0x4D42, 0x3468, 0x4852, 0x465C, 0xFC35, 0, 0xFC36, 0x3F7C,
- 0x4E4E, 0xE775, 0x375B, 0, 0xE77A, 0, 0x7D78, 0,
- 0xE77C, 0x7076, 0, 0xFC39, 0x7075, 0xFC3C, 0xE77E, 0,
- 0, 0, 0, 0x7D79, 0x4B4B, 0x462C, 0xE822, 0xE823,
- 0x7D7A, 0, 0xFC3A, 0xFC3B, 0x3150, 0xE827, 0, 0x7077,
- 0x7074, 0, 0, 0x4951, 0x4D6A, 0x7078, 0xE829, 0,
-};
-static const unsigned short utf8_to_euc_E9A1[] = {
- 0, 0, 0, 0, 0xE82A, 0, 0x7079, 0xE82B,
- 0, 0, 0xE82C, 0x707B, 0x426A, 0x335B, 0x335C, 0x707A,
- 0, 0xE82D, 0xE82E, 0xE82F, 0x3469, 0x3832, 0xE830, 0xE831,
- 0x346A, 0xE832, 0xE833, 0x453F, 0, 0, 0x4E60, 0,
- 0, 0, 0xE834, 0xE835, 0, 0xE836, 0xE837, 0x385C,
- 0, 0, 0xE838, 0x707C, 0xE839, 0, 0, 0x707D,
- 0x707E, 0x7121, 0, 0x7123, 0x7122, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E9A1_x0213[] = {
- 0, 0, 0, 0, 0xE82A, 0, 0x7079, 0xFC3D,
- 0, 0, 0xE82C, 0x707B, 0x426A, 0x335B, 0x335C, 0x707A,
- 0, 0xE82D, 0x7D7C, 0x7D7D, 0x3469, 0x3832, 0x7D7E, 0x7E21,
- 0x346A, 0x7E22, 0x7E23, 0x453F, 0, 0, 0x4E60, 0,
- 0, 0, 0xE834, 0xE835, 0, 0x7E25, 0xFC3E, 0x385C,
- 0, 0, 0xE838, 0x707C, 0x7E26, 0, 0, 0x707D,
- 0x707E, 0x7121, 0, 0x7123, 0x7122, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E9A2[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x4977, 0, 0x7124, 0xE83A, 0, 0xE83B, 0xE83C, 0x7125,
- 0xE83D, 0x7126, 0, 0, 0xE83E, 0, 0x7127, 0xE83F,
- 0xE840, 0, 0xE841, 0xE842, 0, 0, 0, 0xE843,
-};
-static const unsigned short utf8_to_euc_E9A2_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x4977, 0, 0x7124, 0xFC3F, 0, 0xFC40, 0xE83C, 0x7125,
- 0xFC41, 0x7126, 0, 0, 0xE83E, 0, 0x7127, 0xFC43,
- 0xFC44, 0, 0x7E27, 0xFC45, 0xFC46, 0, 0, 0xFC47,
-};
-static const unsigned short utf8_to_euc_E9A3[] = {
- 0, 0, 0xE844, 0x7129, 0x7128, 0xE845, 0x712A, 0,
- 0xE846, 0, 0, 0, 0xE847, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x4874, 0x664C, 0, 0, 0x3F29,
- 0, 0xE848, 0x3532, 0xE849, 0, 0xE84A, 0xE84B, 0xE84C,
- 0, 0x712B, 0xE84D, 0x712C, 0, 0x522C, 0x5D3B, 0x4853,
- 0, 0, 0x307B, 0xE84E, 0x303B, 0, 0xE84F, 0,
- 0, 0, 0, 0, 0x3B74, 0x4B30, 0x3E7E, 0,
-};
-static const unsigned short utf8_to_euc_E9A3_x0213[] = {
- 0, 0, 0xFC48, 0x7129, 0x7128, 0xE845, 0x712A, 0xFC49,
- 0x7E28, 0, 0, 0xFC4A, 0xE847, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x4874, 0x664C, 0, 0, 0x3F29,
- 0xFC4B, 0xFC4D, 0x3532, 0xFC4E, 0, 0xFC4F, 0xE84B, 0x7E29,
- 0, 0x712B, 0xFC50, 0x712C, 0, 0x522C, 0x5D3B, 0x4853,
- 0xFC51, 0xFC52, 0x307B, 0xFC53, 0x303B, 0, 0xE84F, 0,
- 0, 0, 0, 0, 0x3B74, 0x4B30, 0x3E7E, 0,
-};
-static const unsigned short utf8_to_euc_E9A4[] = {
- 0, 0, 0xE850, 0x712D, 0, 0x4C5F, 0, 0xE851,
- 0xE852, 0x712E, 0x4D5C, 0, 0x3142, 0, 0, 0,
- 0x3B41, 0xE853, 0x712F, 0x326E, 0x7130, 0xE854, 0xE855, 0xE856,
- 0x7131, 0, 0xE857, 0xE858, 0xE859, 0x7133, 0x7134, 0xE85A,
- 0x7136, 0x7132, 0xE85B, 0, 0x7135, 0, 0xE85C, 0xE85D,
- 0x345B, 0, 0, 0xE85E, 0x7137, 0, 0x7138, 0,
- 0, 0xE85F, 0xE860, 0xE861, 0xE862, 0xE863, 0, 0,
- 0, 0xE864, 0xE865, 0xE866, 0xE867, 0x7139, 0x713A, 0,
-};
-static const unsigned short utf8_to_euc_E9A4_x0213[] = {
- 0, 0, 0xE850, 0x712D, 0, 0x4C5F, 0, 0xE851,
- 0xFC54, 0x712E, 0x4D5C, 0, 0x3142, 0, 0, 0,
- 0x3B41, 0xE853, 0x712F, 0x326E, 0x7130, 0xE854, 0xFC57, 0xFC58,
- 0x7131, 0, 0xFC5A, 0xFC5B, 0xFC5C, 0x7133, 0x7134, 0xE85A,
- 0x7136, 0x7132, 0xE85B, 0, 0x7135, 0, 0xE85C, 0,
- 0x345B, 0, 0, 0xE85E, 0x7137, 0, 0x7138, 0,
- 0, 0xFC5E, 0xFC5F, 0xFC60, 0xE862, 0xE863, 0, 0,
- 0, 0xE864, 0xFC61, 0xFC62, 0xFC63, 0x7139, 0x713A, 0,
-};
-static const unsigned short utf8_to_euc_E9A5[] = {
- 0xE868, 0xE869, 0x713B, 0, 0, 0x713D, 0xE86A, 0xE86B,
- 0xE86C, 0x713C, 0, 0x713F, 0x7142, 0xE86D, 0xE86E, 0,
- 0x713E, 0x7140, 0x7141, 0, 0xE86F, 0x7143, 0, 0x3642,
- 0xE870, 0xE871, 0, 0xE872, 0xE873, 0, 0xE874, 0xE875,
- 0xE876, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E9A5_x0213[] = {
- 0xFC64, 0xFC65, 0x713B, 0, 0, 0x713D, 0xFC66, 0xE86B,
- 0xE86C, 0x713C, 0, 0x713F, 0x7142, 0xFC67, 0xFC68, 0,
- 0x713E, 0x7140, 0x7141, 0, 0xE86F, 0x7143, 0, 0x3642,
- 0x7E2A, 0xE871, 0, 0xE872, 0xFC69, 0, 0xE874, 0xFC6A,
- 0xFC6B, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E9A6[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0x3C73, 0x7144,
- 0x7145, 0x3961, 0, 0xE877, 0, 0xE878, 0xF47A, 0xE879,
- 0, 0, 0, 0, 0, 0x7146, 0xE87A, 0,
- 0x333E, 0, 0, 0, 0x474F, 0x7147, 0x7148, 0,
- 0xE87B, 0xE87C, 0xE87D, 0x435A, 0x466B, 0xE87E, 0, 0,
- 0, 0xE921, 0xE922, 0, 0x7149, 0xE923, 0, 0xE924,
-};
-static const unsigned short utf8_to_euc_E9A6_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0x3C73, 0x7144,
- 0x7145, 0x3961, 0, 0xE877, 0, 0xE878, 0x7E2B, 0xE879,
- 0, 0, 0, 0xFC6C, 0, 0x7146, 0xFC6D, 0,
- 0x333E, 0, 0, 0, 0x474F, 0x7147, 0x7148, 0,
- 0xE87B, 0xE87C, 0xE87D, 0x435A, 0x466B, 0xE87E, 0, 0,
- 0, 0xFC6E, 0xE922, 0, 0x7149, 0xFC6F, 0, 0xFC70,
-};
-static const unsigned short utf8_to_euc_E9A7[] = {
- 0, 0x477D, 0, 0xE925, 0x424C, 0x3158, 0x366E, 0,
- 0x366F, 0xE926, 0, 0, 0, 0, 0, 0,
- 0x4373, 0x714E, 0x3670, 0xE927, 0xE928, 0x326F, 0, 0,
- 0x714D, 0xE929, 0xE92A, 0x714B, 0xE92B, 0x714C, 0xE92C, 0x714A,
- 0, 0, 0x7158, 0, 0, 0, 0, 0xE92D,
- 0, 0, 0xE92E, 0xE92F, 0xE930, 0x714F, 0x7150, 0,
- 0xE931, 0x7151, 0x7152, 0, 0xE932, 0xE933, 0, 0,
- 0x7154, 0xE934, 0, 0x7153, 0, 0xE935, 0xE936, 0x3D59,
-};
-static const unsigned short utf8_to_euc_E9A7_x0213[] = {
- 0, 0x477D, 0, 0xFC71, 0x424C, 0x3158, 0x366E, 0,
- 0x366F, 0xFC72, 0, 0, 0, 0, 0, 0,
- 0x4373, 0x714E, 0x3670, 0xE927, 0xFC73, 0x326F, 0, 0,
- 0x714D, 0xFC74, 0xE92A, 0x714B, 0xE92B, 0x714C, 0xFC75, 0x714A,
- 0, 0, 0x7158, 0, 0, 0, 0, 0xE92D,
- 0, 0, 0xE92E, 0xE92F, 0xE930, 0x714F, 0x7150, 0,
- 0xFC77, 0x7151, 0x7152, 0, 0xE932, 0xE933, 0, 0,
- 0x7154, 0xFC78, 0, 0x7153, 0xFC79, 0xE935, 0xE936, 0x3D59,
-};
-static const unsigned short utf8_to_euc_E9A8[] = {
- 0, 0x7155, 0xE937, 0xE938, 0xE939, 0x7157, 0, 0,
- 0, 0, 0, 0xE93A, 0xE93B, 0, 0x3533, 0x7156,
- 0xE93C, 0xE93D, 0x417B, 0x3833, 0, 0, 0xE93E, 0,
- 0, 0x7159, 0, 0, 0, 0, 0xE93F, 0,
- 0xE940, 0, 0xE941, 0xE942, 0xE943, 0, 0, 0xE944,
- 0x424D, 0, 0, 0x715A, 0, 0xE945, 0xE946, 0,
- 0x462D, 0, 0, 0xE947, 0, 0xE948, 0xE949, 0x715B,
- 0xE94A, 0, 0, 0, 0, 0, 0x7160, 0,
-};
-static const unsigned short utf8_to_euc_E9A8_x0213[] = {
- 0, 0x7155, 0x7E2C, 0x7E2D, 0xE939, 0x7157, 0, 0,
- 0, 0, 0xFC7A, 0xE93A, 0xE93B, 0, 0x3533, 0x7156,
- 0xE93C, 0xFC7B, 0x417B, 0x3833, 0, 0, 0xFC7C, 0,
- 0, 0x7159, 0xFC7D, 0, 0, 0, 0xE93F, 0,
- 0xFC7E, 0, 0xE941, 0xE942, 0x7E2E, 0, 0, 0xE944,
- 0x424D, 0, 0, 0x715A, 0, 0x7E2F, 0x7E30, 0,
- 0x462D, 0xFD21, 0, 0xE947, 0, 0xE948, 0xFD22, 0x715B,
- 0x7E31, 0, 0, 0, 0, 0, 0x7160, 0,
-};
-static const unsigned short utf8_to_euc_E9A9[] = {
- 0x715E, 0xE94C, 0x715D, 0x715F, 0xE94D, 0x715C, 0, 0xE94B,
- 0, 0, 0xE94E, 0xE94F, 0xE950, 0x7162, 0xE951, 0,
- 0, 0xE952, 0, 0, 0xE953, 0x7161, 0xE954, 0x7164,
- 0, 0, 0x3643, 0x7163, 0, 0xE955, 0, 0x7165,
- 0, 0, 0x7166, 0, 0x7168, 0x7167, 0, 0,
- 0, 0x7169, 0x716B, 0x716A, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E9A9_x0213[] = {
- 0x715E, 0xE94C, 0x715D, 0x715F, 0xFD23, 0x715C, 0, 0xE94B,
- 0, 0, 0x7E32, 0xE94F, 0xFD24, 0x7162, 0x7E33, 0,
- 0, 0xE952, 0x7E34, 0, 0xE953, 0x7161, 0xE954, 0x7164,
- 0xFD25, 0, 0x3643, 0x7163, 0, 0xE955, 0, 0x7165,
- 0, 0, 0x7166, 0, 0x7168, 0x7167, 0, 0,
- 0, 0x7169, 0x716B, 0x716A, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E9AA[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x397C, 0, 0xE956, 0, 0xE957, 0x716C, 0xE958, 0xE959,
- 0x716D, 0, 0xE95A, 0, 0xE95B, 0xE95C, 0xE95D, 0,
- 0x333C, 0xE95E, 0, 0xE95F, 0x716E, 0, 0xE960, 0xE961,
-};
-static const unsigned short utf8_to_euc_E9AA_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x397C, 0, 0xE956, 0, 0xE957, 0x716C, 0xE958, 0xFD27,
- 0x716D, 0, 0xE95A, 0, 0xE95B, 0xE95C, 0x7E35, 0xFD29,
- 0x333C, 0xFD2B, 0, 0xE95F, 0x716E, 0, 0xE960, 0xE961,
-};
-static const unsigned short utf8_to_euc_E9AB[] = {
- 0x716F, 0xE962, 0, 0xE963, 0x3F71, 0, 0xE964, 0,
- 0xE965, 0, 0, 0, 0, 0, 0xE966, 0x7170,
- 0xE967, 0x7171, 0xE968, 0x7172, 0x7173, 0xE969, 0xE96A, 0xE96B,
- 0x3962, 0xF47B, 0, 0xE96C, 0xE96D, 0, 0x7174, 0x7175,
- 0xE96E, 0, 0x7176, 0x7177, 0xE96F, 0xE970, 0x7178, 0xE971,
- 0, 0xE972, 0x4831, 0x717A, 0xE973, 0x4926, 0x717B, 0x7179,
- 0, 0x717D, 0xE974, 0xE975, 0x717C, 0xE976, 0, 0x717E,
- 0, 0xE977, 0xE978, 0x7221, 0, 0xE979, 0, 0xE97A,
-};
-static const unsigned short utf8_to_euc_E9AB_x0213[] = {
- 0x716F, 0x7E36, 0, 0x7E37, 0x3F71, 0, 0xFD2D, 0,
- 0xE965, 0, 0, 0, 0, 0, 0x7E38, 0x7170,
- 0xFD2E, 0x7171, 0xFD2F, 0x7172, 0x7173, 0xFD30, 0x7E39, 0xE96B,
- 0x3962, 0, 0, 0xE96C, 0xFD32, 0, 0x7174, 0x7175,
- 0xFD33, 0, 0x7176, 0x7177, 0xE96F, 0xFD34, 0x7178, 0xE971,
- 0, 0xFD35, 0x4831, 0x717A, 0xE973, 0x4926, 0x717B, 0x7179,
- 0, 0x717D, 0xE974, 0xE975, 0x717C, 0xE976, 0, 0x717E,
- 0, 0x7E3A, 0xE978, 0x7221, 0, 0xE979, 0, 0xE97A,
-};
-static const unsigned short utf8_to_euc_E9AC[] = {
- 0xE97B, 0xE97C, 0xE97D, 0xE97E, 0xEA21, 0xEA22, 0x7222, 0,
- 0xEA23, 0xEA24, 0, 0xEA25, 0xEA26, 0xEA27, 0xEA28, 0,
- 0xEA29, 0, 0xEA2A, 0, 0, 0, 0xEA2B, 0,
- 0x7223, 0xEA2C, 0x7224, 0xEA2D, 0xEA2E, 0, 0, 0x7225,
- 0xEA2F, 0, 0x7226, 0x7227, 0, 0x7228, 0xEA30, 0x7229,
- 0x722A, 0x722B, 0x722C, 0xEA31, 0, 0xEA32, 0x722D, 0x722E,
- 0, 0x5D35, 0x722F, 0xEA33, 0xEA34, 0xEA35, 0, 0xEA36,
- 0, 0xEA37, 0xEA38, 0x6478, 0x3534, 0xEA39, 0, 0,
-};
-static const unsigned short utf8_to_euc_E9AC_x0213[] = {
- 0xE97B, 0xE97C, 0x7E3B, 0xFD36, 0xEA21, 0xEA22, 0x7222, 0,
- 0x7E3C, 0xEA24, 0, 0xEA25, 0xFD37, 0xEA27, 0xEA28, 0,
- 0xFD38, 0, 0xFD39, 0, 0, 0, 0xFD3A, 0,
- 0x7223, 0xEA2C, 0x7224, 0xEA2D, 0xFD3B, 0, 0, 0x7225,
- 0x7E3D, 0, 0x7226, 0x7227, 0, 0x7228, 0xEA30, 0x7229,
- 0x722A, 0x722B, 0x722C, 0xFD3C, 0, 0x7E3F, 0x722D, 0x722E,
- 0, 0x5D35, 0x722F, 0xFD3D, 0xEA34, 0xEA35, 0, 0xEA36,
- 0, 0xEA37, 0xEA38, 0x6478, 0x3534, 0xFD3E, 0, 0,
-};
-static const unsigned short utf8_to_euc_E9AD[] = {
- 0, 0x3321, 0x3A32, 0x7231, 0x7230, 0x4C25, 0, 0,
- 0xEA3A, 0, 0, 0xEA3B, 0xEA3C, 0x7233, 0x7234, 0x7232,
- 0, 0x7235, 0, 0, 0x4B62, 0xEA3D, 0xEA3E, 0xEA3F,
- 0x7236, 0, 0x357B, 0xEA40, 0, 0, 0xEA41, 0,
- 0, 0xEA42, 0, 0xEA43, 0, 0xEA44, 0xEA45, 0,
- 0xEA46, 0, 0xEA47, 0xEA48, 0xEA49, 0xEA4A, 0xEA4B, 0x4F25,
- 0, 0, 0xF47C, 0xEA4C, 0x7237, 0xEA4D, 0, 0xEA4E,
- 0xEA4F, 0xEA50, 0, 0, 0, 0, 0, 0xEA51,
-};
-static const unsigned short utf8_to_euc_E9AD_x0213[] = {
- 0, 0x3321, 0x3A32, 0x7231, 0x7230, 0x4C25, 0, 0,
- 0xEA3A, 0, 0, 0xFD40, 0xEA3C, 0x7233, 0x7234, 0x7232,
- 0, 0x7235, 0, 0, 0x4B62, 0xEA3D, 0xEA3E, 0xEA3F,
- 0x7236, 0, 0x357B, 0xEA40, 0, 0, 0x7E40, 0,
- 0, 0xEA42, 0, 0xFD41, 0, 0xFD42, 0x7E42, 0,
- 0xEA46, 0, 0xEA47, 0xFD43, 0xFD44, 0xEA4A, 0xEA4B, 0x4F25,
- 0, 0, 0x7E43, 0xFD45, 0x7237, 0x7E44, 0xFD46, 0xFD47,
- 0xEA4F, 0x7E41, 0, 0, 0, 0, 0, 0xEA51,
-};
-static const unsigned short utf8_to_euc_E9AE[] = {
- 0xEA52, 0, 0, 0x7239, 0xEA53, 0xEA54, 0xEA55, 0xEA56,
- 0, 0xEA57, 0xEA58, 0xEA59, 0, 0xEA5A, 0x303E, 0xEA5B,
- 0xEA5C, 0x723A, 0x4A2B, 0x7238, 0xEA5D, 0, 0x723B, 0x723C,
- 0, 0, 0xEA5E, 0, 0, 0xEA5F, 0xEA60, 0x723D,
- 0x723E, 0, 0, 0, 0, 0, 0xEA61, 0xEA62,
- 0x723F, 0xEA63, 0x4B6E, 0x3B2D, 0xEA64, 0x3A7A, 0x412F, 0,
- 0xEA65, 0xEA66, 0xEA67, 0, 0x7240, 0, 0, 0xEA68,
- 0xEA69, 0x7243, 0, 0xEA6A, 0xEA6B, 0, 0xEA6C, 0xEA6D,
-};
-static const unsigned short utf8_to_euc_E9AE_x0213[] = {
- 0xEA52, 0, 0, 0x7239, 0x7E45, 0xEA54, 0xEA55, 0xEA56,
- 0, 0xEA57, 0x7E46, 0xEA59, 0, 0xEA5A, 0x303E, 0x7E47,
- 0xEA5C, 0x723A, 0x4A2B, 0x7238, 0xEA5D, 0, 0x723B, 0x723C,
- 0, 0, 0xEA5E, 0, 0, 0xEA5F, 0x7E48, 0x723D,
- 0x723E, 0, 0, 0, 0, 0, 0xFD48, 0x7E49,
- 0x723F, 0xEA63, 0x4B6E, 0x3B2D, 0xFD49, 0x3A7A, 0x412F, 0,
- 0xEA65, 0xFD4A, 0xFD4D, 0, 0x7240, 0, 0, 0xEA68,
- 0xFD4E, 0x7243, 0, 0, 0xEA6B, 0, 0xFD4F, 0xEA6D,
-};
-static const unsigned short utf8_to_euc_E9AF[] = {
- 0x7241, 0xEA6E, 0, 0, 0, 0, 0x7244, 0xEA6F,
- 0xEA70, 0x3871, 0x7242, 0, 0, 0, 0xEA71, 0x7245,
- 0xEA72, 0x7246, 0x7247, 0, 0x724B, 0, 0x3B2A, 0xEA73,
- 0xEA74, 0, 0, 0x4264, 0, 0xEA75, 0, 0xEA76,
- 0, 0x724C, 0x7249, 0x7248, 0x724A, 0xEA77, 0, 0xEA78,
- 0x375F, 0, 0xEA79, 0xEA7A, 0, 0, 0, 0xEA7B,
- 0x7250, 0x724F, 0x724E, 0xEA7C, 0, 0x3033, 0, 0xEA7D,
- 0xEA7E, 0xEB21, 0xEB22, 0, 0, 0xEB23, 0, 0xEB24,
-};
-static const unsigned short utf8_to_euc_E9AF_x0213[] = {
- 0x7241, 0x7E4A, 0, 0, 0, 0, 0x7244, 0xFD50,
- 0xEA70, 0x3871, 0x7242, 0, 0, 0, 0x7E4B, 0x7245,
- 0xEA72, 0x7246, 0x7247, 0, 0x724B, 0, 0x3B2A, 0xEA73,
- 0xFD52, 0, 0, 0x4264, 0, 0xFD53, 0, 0xEA76,
- 0, 0x724C, 0x7249, 0x7248, 0x724A, 0x7E4C, 0, 0xFD54,
- 0x375F, 0, 0xFD55, 0xFD56, 0, 0, 0xFD58, 0xFD57,
- 0x7250, 0x724F, 0x724E, 0xFD51, 0, 0x3033, 0, 0xFD5C,
- 0x7E4D, 0xEB21, 0xFD5A, 0, 0, 0x7E4E, 0, 0xEB24,
-};
-static const unsigned short utf8_to_euc_E9B0[] = {
- 0xEB25, 0, 0xEB26, 0, 0x725A, 0, 0x7256, 0,
- 0x7257, 0x7253, 0x7259, 0xEB27, 0x7255, 0x3362, 0, 0xEB28,
- 0x4F4C, 0xEB29, 0x7258, 0x7254, 0x7252, 0x7251, 0xEB2A, 0,
- 0xEB2B, 0xEB2C, 0xEB2D, 0x725C, 0xEB2E, 0, 0xEB2F, 0,
- 0, 0x725F, 0xEB30, 0xEB31, 0x725E, 0x725D, 0xEB32, 0xEB33,
- 0xEB34, 0xEB35, 0xEB36, 0, 0, 0x4949, 0x725B, 0x3073,
- 0x7260, 0xEB37, 0x7262, 0, 0, 0xEB38, 0xEB39, 0xEB3A,
- 0, 0x336F, 0x724D, 0x3137, 0, 0xEB3B, 0x7264, 0,
-};
-static const unsigned short utf8_to_euc_E9B0_x0213[] = {
- 0x7E4F, 0, 0xEB26, 0, 0x725A, 0, 0x7256, 0,
- 0x7257, 0x7253, 0x7259, 0xEB27, 0x7255, 0x3362, 0, 0xEB28,
- 0x4F4C, 0xEB29, 0x7258, 0x7254, 0x7252, 0x7251, 0xFD5E, 0,
- 0xFD5F, 0xFD60, 0xFD61, 0x725C, 0xEB2E, 0xFD62, 0xEB2F, 0,
- 0, 0x725F, 0xFD63, 0x7E50, 0x725E, 0x725D, 0xEB32, 0xFD64,
- 0xEB34, 0xFD65, 0xFD66, 0, 0, 0x4949, 0x725B, 0x3073,
- 0x7260, 0xFD68, 0x7262, 0, 0, 0xEB38, 0xFD69, 0xFD6A,
- 0, 0x336F, 0x724D, 0x3137, 0, 0xEB3B, 0x7264, 0,
-};
-static const unsigned short utf8_to_euc_E9B1[] = {
- 0, 0xEB3C, 0, 0xEB3D, 0xEB3E, 0xEB3F, 0x7263, 0x7261,
- 0x432D, 0xEB40, 0xEB41, 0, 0, 0, 0xEB42, 0xEB43,
- 0xEB44, 0, 0x4B70, 0xEB45, 0xEB46, 0, 0xEB47, 0x4E5A,
- 0xEB48, 0, 0x7265, 0xEB49, 0xEB50, 0xEB4A, 0xEB4B, 0xEB4C,
- 0x7266, 0, 0, 0xEB4D, 0, 0, 0, 0x7267,
- 0xEB52, 0xEB4E, 0xEB4F, 0xEB51, 0, 0, 0xEB53, 0,
- 0xEB54, 0, 0xEB55, 0, 0, 0xEB56, 0x7268, 0xEB57,
- 0x7269, 0, 0, 0xEB58, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E9B1_x0213[] = {
- 0, 0x7E51, 0, 0xEB3D, 0xEB3E, 0xFD6B, 0x7263, 0x7261,
- 0x432D, 0xFD6E, 0xFD6F, 0, 0, 0, 0xEB42, 0x7E52,
- 0x7E53, 0, 0x4B70, 0x7E54, 0xFD71, 0, 0xEB47, 0x4E5A,
- 0xFD72, 0, 0x7265, 0xFD73, 0xFD6C, 0xFD74, 0xEB4B, 0xFD75,
- 0x7266, 0, 0, 0x7E55, 0, 0x7E56, 0, 0x7267,
- 0xEB52, 0xFD76, 0xFD77, 0xFD78, 0, 0xFD79, 0xFD7A, 0,
- 0xFD7B, 0, 0xFD7C, 0, 0, 0xFD7D, 0x7268, 0x7E57,
- 0x7269, 0, 0xFD7E, 0xEB58, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E9B3[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x443B, 0xEB59, 0x726A,
- 0, 0x4837, 0, 0x726F, 0x726B, 0, 0, 0,
- 0x726C, 0, 0xEB5A, 0x4B31, 0x4C44, 0, 0x4650, 0xEB5B,
- 0, 0xEB5C, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E9B3_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x443B, 0xFE21, 0x726A,
- 0, 0x4837, 0, 0x726F, 0x726B, 0, 0, 0,
- 0x726C, 0, 0xFE22, 0x4B31, 0x4C44, 0, 0x4650, 0xEB5B,
- 0, 0xEB5C, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E9B4[] = {
- 0, 0, 0xEB5E, 0x7270, 0, 0, 0x7271, 0x463E,
- 0x726E, 0x726D, 0, 0xEB5D, 0, 0, 0x322A, 0,
- 0, 0xEB5F, 0x7279, 0, 0, 0x7278, 0, 0xEB60,
- 0xEB61, 0, 0, 0x3175, 0xEB62, 0xEB63, 0xEB64, 0x7276,
- 0, 0, 0, 0x7275, 0, 0, 0x7273, 0,
- 0x337B, 0, 0x7272, 0x3C32, 0x3229, 0, 0, 0xEB65,
- 0xEB66, 0, 0xEB67, 0xEB68, 0xEB69, 0, 0, 0,
- 0, 0, 0xEB6A, 0x3963, 0xEB6B, 0xEB6D, 0x727C, 0x727B,
-};
-static const unsigned short utf8_to_euc_E9B4_x0213[] = {
- 0, 0, 0xFE24, 0x7270, 0, 0, 0x7271, 0x463E,
- 0x726E, 0x726D, 0, 0xFE23, 0, 0, 0x322A, 0,
- 0, 0xFE26, 0x7279, 0, 0, 0x7278, 0, 0xFE27,
- 0xFE28, 0, 0, 0x3175, 0xEB62, 0x7E58, 0x7E59, 0x7276,
- 0, 0, 0, 0x7275, 0, 0, 0x7273, 0,
- 0x337B, 0, 0x7272, 0x3C32, 0x3229, 0, 0, 0xEB65,
- 0xEB66, 0, 0xFE2C, 0xEB68, 0xEB69, 0, 0, 0,
- 0, 0, 0xEB6A, 0x3963, 0xEB6B, 0xEB6D, 0x727C, 0x727B,
-};
-static const unsigned short utf8_to_euc_E9B5[] = {
- 0, 0x727A, 0xEB6E, 0xEB6F, 0x7277, 0xEB6C, 0x727D, 0xEB70,
- 0x727E, 0, 0xEB71, 0, 0, 0, 0, 0,
- 0x7325, 0x7324, 0, 0xEB72, 0xEB73, 0, 0, 0,
- 0, 0x7326, 0, 0, 0x312D, 0x7321, 0x7322, 0xEB74,
- 0x3974, 0x4C39, 0xEB76, 0xEB75, 0x7323, 0xEB77, 0, 0,
- 0, 0xEB78, 0xEB79, 0xEB7A, 0x4B32, 0, 0, 0x732B,
- 0xEB7B, 0, 0x7327, 0, 0, 0, 0xEB7C, 0xEB7D,
- 0, 0, 0x732C, 0xEB7E, 0xEC21, 0, 0xEC22, 0,
-};
-static const unsigned short utf8_to_euc_E9B5_x0213[] = {
- 0, 0x727A, 0xFE2E, 0x7E5A, 0x7277, 0xEB6C, 0x727D, 0x7E5B,
- 0x727E, 0, 0xFE2F, 0, 0, 0, 0, 0,
- 0x7325, 0x7324, 0x7E5C, 0xEB72, 0xEB73, 0, 0, 0,
- 0, 0x7326, 0, 0, 0x312D, 0x7321, 0x7322, 0xFE30,
- 0x3974, 0x4C39, 0xFE31, 0x7E5D, 0x7323, 0xEB77, 0, 0,
- 0, 0xFE33, 0xEB79, 0xFE34, 0x4B32, 0, 0, 0x732B,
- 0x7E5E, 0, 0x7327, 0xFE36, 0, 0, 0xFE37, 0xFE38,
- 0, 0, 0x732C, 0xEB7E, 0x7E5F, 0, 0xFE39, 0,
-};
-static const unsigned short utf8_to_euc_E9B6[] = {
- 0, 0, 0, 0xEC23, 0xEC24, 0, 0xEC25, 0x7329,
- 0, 0x7328, 0xEC26, 0, 0, 0xEC27, 0xEC28, 0x375C,
- 0, 0, 0xEC29, 0xEC2A, 0, 0xEC2B, 0xEC2C, 0xEC2D,
- 0xEC2E, 0, 0x732D, 0, 0, 0, 0, 0,
- 0, 0xEC2F, 0, 0, 0x732E, 0, 0, 0,
- 0, 0x732F, 0xEC30, 0x732A, 0xEC31, 0, 0xEC32, 0x7274,
- 0, 0xEC33, 0x7330, 0, 0x4461, 0xEC34, 0, 0,
- 0x7334, 0xEC35, 0x7335, 0x7333, 0xEC36, 0, 0, 0xEC37,
-};
-static const unsigned short utf8_to_euc_E9B6_x0213[] = {
- 0, 0, 0, 0xEC23, 0xFE3A, 0, 0xEC25, 0x7329,
- 0, 0x7328, 0x7E60, 0, 0, 0xFE3B, 0xEC28, 0x375C,
- 0, 0, 0xEC29, 0xEC2A, 0, 0xEC2B, 0x7E61, 0xEC2D,
- 0xEC2E, 0xFE3C, 0x732D, 0, 0, 0, 0, 0,
- 0, 0xFE3D, 0, 0, 0x732E, 0, 0, 0,
- 0, 0x732F, 0xEC30, 0x732A, 0x7E63, 0, 0xEC32, 0x7274,
- 0, 0xEC33, 0x7330, 0, 0x4461, 0xFE3F, 0, 0,
- 0x7334, 0xFE40, 0x7335, 0x7333, 0x7E64, 0xFE41, 0, 0xFE3E,
-};
-static const unsigned short utf8_to_euc_E9B7[] = {
- 0, 0x7332, 0x7338, 0xEC38, 0x7331, 0, 0x7336, 0xEC39,
- 0, 0xEC3A, 0xEC3B, 0, 0, 0, 0, 0x7337,
- 0, 0, 0, 0x733A, 0xEC3C, 0xEC3D, 0xEC3E, 0xEC3F,
- 0, 0x7339, 0xEC40, 0, 0, 0, 0xEC41, 0xEC42,
- 0xEC43, 0, 0, 0, 0, 0xEC44, 0x733C, 0xEC45,
- 0, 0xEC46, 0, 0xEC47, 0, 0x733D, 0xEC48, 0x733E,
- 0xEC49, 0, 0x4F49, 0xEC4A, 0xEC4B, 0, 0, 0,
- 0x733B, 0x426B, 0x3A6D, 0, 0, 0x733F, 0xEC4C, 0,
-};
-static const unsigned short utf8_to_euc_E9B7_x0213[] = {
- 0x7E62, 0x7332, 0x7338, 0xFE42, 0x7331, 0, 0x7336, 0xFE43,
- 0, 0xFE44, 0xEC3B, 0, 0, 0, 0, 0x7337,
- 0, 0, 0, 0x733A, 0xEC3C, 0xEC3D, 0xFE45, 0x7E65,
- 0, 0x7339, 0xFE46, 0, 0, 0, 0xEC41, 0xFE47,
- 0xFE48, 0, 0, 0xFE49, 0, 0xEC44, 0x733C, 0x7E67,
- 0, 0xEC46, 0, 0xEC47, 0, 0x733D, 0xEC48, 0x733E,
- 0xEC49, 0, 0x4F49, 0xEC4A, 0xFE4A, 0, 0, 0,
- 0x733B, 0x426B, 0x3A6D, 0, 0, 0x733F, 0xEC4C, 0,
-};
-static const unsigned short utf8_to_euc_E9B8[] = {
- 0, 0, 0xEC4E, 0, 0, 0, 0, 0xEC4F,
- 0, 0, 0xEC4D, 0, 0, 0, 0xEC50, 0,
- 0xEC51, 0xEC52, 0xEC53, 0, 0, 0xEC54, 0xEC55, 0,
- 0, 0xEC56, 0x7340, 0x7341, 0xEC57, 0xEC58, 0x7342, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E9B8_x0213[] = {
- 0, 0, 0xFE4D, 0, 0, 0, 0, 0x7E68,
- 0, 0, 0xFE4C, 0, 0, 0xFE4E, 0xEC50, 0,
- 0xEC51, 0xEC52, 0xEC53, 0, 0, 0x7E69, 0xEC55, 0,
- 0, 0xFE4F, 0x7340, 0x7341, 0xFE50, 0xFE51, 0x7342, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_E9B9[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x7343, 0, 0,
- 0x3834, 0x7344, 0xEC59, 0xEC5A, 0xEC5B, 0x7345, 0, 0x3C2F,
-};
-static const unsigned short utf8_to_euc_E9B9_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x7343, 0, 0,
- 0x3834, 0x7344, 0xEC59, 0xFE52, 0x7E6A, 0x7345, 0, 0x3C2F,
-};
-static const unsigned short utf8_to_euc_E9BA[] = {
- 0xEC5C, 0x7346, 0xEC5D, 0xEC5E, 0xEC5F, 0xEC60, 0, 0xEC61,
- 0x7347, 0, 0, 0x7348, 0x7349, 0, 0xEC62, 0xEC63,
- 0, 0x734C, 0x734A, 0x4F3C, 0, 0x734B, 0xEC64, 0x4E6F,
- 0xEC65, 0, 0, 0xEC66, 0, 0x734D, 0xEC67, 0x4E5B,
- 0, 0, 0, 0, 0xEC68, 0x734E, 0x477E, 0,
- 0xEC69, 0x734F, 0x7351, 0, 0xEC6A, 0x7352, 0xEC6B, 0xEC6C,
- 0xEC6D, 0, 0, 0xEC6E, 0xEC6F, 0xEC70, 0, 0,
- 0x7350, 0x396D, 0x4C4D, 0x4B63, 0x5677, 0, 0x5D60, 0x4B7B,
-};
-static const unsigned short utf8_to_euc_E9BA_x0213[] = {
- 0xFE54, 0x7346, 0xEC5D, 0xEC5E, 0xEC5F, 0xFE55, 0, 0xEC61,
- 0x7347, 0, 0, 0x7348, 0x7349, 0, 0xEC62, 0xEC63,
- 0, 0x734C, 0x734A, 0x4F3C, 0, 0x734B, 0xEC64, 0x4E6F,
- 0xEC65, 0, 0, 0xFE56, 0, 0x734D, 0x7E6B, 0x4E5B,
- 0, 0, 0, 0, 0x7E6C, 0x734E, 0x477E, 0,
- 0xFE57, 0x734F, 0x7351, 0, 0x7E6D, 0x7352, 0xEC6B, 0x7E6E,
- 0xEC6D, 0, 0, 0xEC6E, 0x7E6F, 0x7E70, 0, 0,
- 0x7350, 0x396D, 0x4C4D, 0x4B63, 0x5677, 0xFE59, 0x5D60, 0x4B7B,
-};
-static const unsigned short utf8_to_euc_E9BB[] = {
- 0, 0, 0, 0, 0x322B, 0, 0xEC71, 0,
- 0xEC72, 0, 0, 0xEC73, 0x7354, 0x3550, 0x7355, 0x7356,
- 0x7357, 0xF47E, 0x3975, 0, 0x7358, 0xEC74, 0, 0,
- 0x6054, 0x4C5B, 0, 0x4263, 0x7359, 0x735B, 0x735A, 0xEC75,
- 0x735C, 0, 0, 0, 0xEC76, 0x735D, 0, 0xEC77,
- 0x735E, 0, 0, 0, 0xEC78, 0xEC79, 0xEC7A, 0x735F,
- 0xEC7B, 0xEC7C, 0xEC7D, 0, 0x7360, 0xEC7E, 0x7361, 0x7362,
- 0xED21, 0x7363, 0, 0x7364, 0x7365, 0x7366, 0, 0xED22,
-};
-static const unsigned short utf8_to_euc_E9BB_x0213[] = {
- 0, 0, 0, 0x7E71, 0x322B, 0, 0xEC71, 0,
- 0xEC72, 0, 0, 0xEC73, 0x7354, 0x3550, 0x7355, 0x7356,
- 0x7357, 0x7E72, 0x3975, 0, 0x7358, 0xEC74, 0, 0,
- 0x6054, 0x4C5B, 0, 0x4263, 0x7359, 0x735B, 0x735A, 0xFE5B,
- 0x735C, 0, 0, 0, 0xEC76, 0x735D, 0, 0xFE5C,
- 0x735E, 0, 0, 0, 0xEC78, 0xEC79, 0xFE5D, 0x735F,
- 0xEC7B, 0xEC7C, 0xEC7D, 0, 0x7360, 0xEC7E, 0x7361, 0x7362,
- 0xED21, 0x7363, 0, 0x7364, 0x7365, 0x7366, 0, 0xFE5E,
-};
-static const unsigned short utf8_to_euc_E9BC[] = {
- 0, 0, 0xED23, 0xED24, 0, 0, 0, 0x7367,
- 0x7368, 0xED25, 0, 0, 0, 0, 0x4524, 0xED26,
- 0xED27, 0xED28, 0xED29, 0x385D, 0xED2A, 0x736A, 0xED2B, 0xED2C,
- 0, 0xED2D, 0xED2E, 0xED2F, 0, 0, 0, 0xED30,
- 0x414D, 0x736B, 0xED31, 0, 0, 0, 0xED32, 0,
- 0, 0, 0xED33, 0xED34, 0x736C, 0, 0, 0xED35,
- 0, 0xED36, 0xED37, 0, 0xED38, 0, 0, 0xED39,
- 0, 0xED3A, 0xED3B, 0x4921, 0xED3C, 0xED3D, 0x736D, 0xED3E,
-};
-static const unsigned short utf8_to_euc_E9BC_x0213[] = {
- 0, 0, 0xFE5F, 0xFE61, 0, 0, 0, 0x7367,
- 0x7368, 0xED25, 0, 0, 0, 0, 0x4524, 0xED26,
- 0x7E73, 0xED28, 0xED29, 0x385D, 0xED2A, 0x736A, 0xED2B, 0xFE62,
- 0, 0xFE63, 0xED2E, 0xED2F, 0, 0, 0, 0xED30,
- 0x414D, 0x736B, 0xED31, 0, 0, 0, 0xED32, 0,
- 0, 0, 0xED33, 0xED34, 0x736C, 0, 0, 0xFE64,
- 0, 0xED36, 0xED37, 0, 0xED38, 0, 0, 0xFE65,
- 0, 0x7E74, 0xFE66, 0x4921, 0xED3C, 0xFE67, 0x736D, 0xED3E,
-};
-static const unsigned short utf8_to_euc_E9BD[] = {
- 0, 0xED3F, 0, 0xED40, 0xED41, 0xED42, 0xED43, 0xED44,
- 0, 0, 0x736E, 0x6337, 0, 0, 0x6C5A, 0x706D,
- 0, 0, 0x736F, 0xED45, 0x7370, 0xED46, 0xED47, 0xED48,
- 0xED49, 0, 0xED4A, 0, 0, 0xED4B, 0xED4C, 0x7372,
- 0x7373, 0x7374, 0x4E70, 0x7371, 0, 0, 0x7375, 0x7376,
- 0xED4D, 0xED4E, 0x7378, 0, 0x7377, 0xED4F, 0xED50, 0xED51,
- 0xED52, 0xED53, 0x737A, 0xED54, 0, 0xED55, 0x737B, 0x7379,
- 0, 0, 0xED56, 0, 0, 0xED57, 0, 0,
-};
-static const unsigned short utf8_to_euc_E9BD_x0213[] = {
- 0, 0xFE68, 0, 0xED40, 0xED41, 0xFE69, 0xFE6A, 0xED44,
- 0, 0, 0x736E, 0x6337, 0, 0, 0x6C5A, 0x706D,
- 0, 0, 0x736F, 0xFE6B, 0x7370, 0xFE6C, 0xED47, 0x7E75,
- 0xFE6D, 0, 0xED4A, 0, 0, 0xFE6F, 0xED4C, 0x7372,
- 0x7373, 0x7374, 0x4E70, 0x7371, 0, 0, 0x7375, 0x7376,
- 0xED4D, 0xFE71, 0x7378, 0, 0x7377, 0xFE73, 0xED50, 0xED51,
- 0xFE74, 0xED53, 0x737A, 0xED54, 0, 0xFE75, 0x737B, 0x7379,
- 0, 0, 0xED56, 0, 0, 0xED57, 0, 0,
-};
-static const unsigned short utf8_to_euc_E9BE[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x4E36, 0, 0xED58,
- 0xED59, 0xED5A, 0xED5B, 0, 0xED5C, 0x737C, 0xED5D, 0xED5E,
- 0, 0, 0, 0, 0x737D, 0x6354, 0xED5F, 0,
- 0x737E, 0xED60, 0xED61, 0xED62, 0, 0xED63, 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,
-};
-static const unsigned short utf8_to_euc_E9BE_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x4E36, 0, 0xED58,
- 0x7E76, 0xED5A, 0xED5B, 0, 0x7E77, 0x737C, 0xED5D, 0x7E78,
- 0, 0, 0, 0, 0x737D, 0x6354, 0xED5F, 0,
- 0x737E, 0xED60, 0x7E79, 0xED62, 0, 0xED63, 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,
-};
-static const unsigned short utf8_to_euc_EFA4[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xF445, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_EFA4_x0213[] = {
- 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, 0x763B, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x742E, 0x754E, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0x7B4F, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_EFA5_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x7649, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_EFA7[] = {
- 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, 0xF472, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_EFA7_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x7E24, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0x7D5D, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_EFA8[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xF434, 0xF437,
- 0xF438, 0xF43D, 0xF444, 0xF447, 0xF448, 0xF44E, 0xF44F, 0xF453,
- 0xF455, 0xF456, 0xF457, 0xF458, 0xF45A, 0xF45B, 0xF45E, 0xF460,
- 0xF462, 0xF463, 0xF465, 0xF469, 0xF46A, 0xF46B, 0xF46D, 0xF46F,
- 0xF470, 0xF473, 0xF477, 0xF478, 0xF479, 0xF47D, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_EFA8_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0x2F4B,
- 0x2F57, 0x4F72, 0, 0xAE79, 0x757A, 0x775A, 0x776F, 0,
- 0, 0x793C, 0x793D, 0x7941, 0, 0, 0, 0x7B3A,
- 0xF738, 0xF745, 0x7C2E, 0, 0xF96E, 0, 0x7C6A, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x2E38, 0x2E49, 0x2E50, 0x2E63, 0x2E68, 0x2E6E, 0x2F2C, 0x2F2F,
- 0x2F36, 0x2F5A, 0x2F5E, 0x4F61, 0x4F62, 0x7450, 0x745C, 0x745E,
-};
-static const unsigned short utf8_to_euc_EFA9_x0213[] = {
- 0x7461, 0x7528, 0x752B, 0x7543, 0x7565, 0x7669, 0x7677, 0x7725,
- 0x7755, 0xF029, 0x7825, 0x7927, 0x7933, 0x7934, 0x7937, 0x7938,
- 0x7939, 0x793B, 0x793F, 0x7940, 0x794D, 0x7951, 0x7964, 0x7A2E,
- 0xF450, 0x7A33, 0x7A3A, 0x7A44, 0x7A58, 0xF574, 0xF575, 0x7B27,
- 0x7B6F, 0x7B79, 0x7C2F, 0x7C30, 0x7C38, 0x7C3D, 0xF969, 0x7C59,
- 0x7D63, 0x7D76, 0x7D7B, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_EFB9_x0213[] = {
- 0, 0, 0, 0, 0, 0x233E, 0x233D, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_EFBC[] = {
- 0, 0x212A, 0xF42A, 0x2174, 0x2170, 0x2173, 0x2175, 0xF429,
- 0x214A, 0x214B, 0x2176, 0x215C, 0x2124, 0x215D, 0x2125, 0x213F,
- 0x2330, 0x2331, 0x2332, 0x2333, 0x2334, 0x2335, 0x2336, 0x2337,
- 0x2338, 0x2339, 0x2127, 0x2128, 0x2163, 0x2161, 0x2164, 0x2129,
- 0x2177, 0x2341, 0x2342, 0x2343, 0x2344, 0x2345, 0x2346, 0x2347,
- 0x2348, 0x2349, 0x234A, 0x234B, 0x234C, 0x234D, 0x234E, 0x234F,
- 0x2350, 0x2351, 0x2352, 0x2353, 0x2354, 0x2355, 0x2356, 0x2357,
- 0x2358, 0x2359, 0x235A, 0x214E, 0x2140, 0x214F, 0x2130, 0x2132,
-};
-static const unsigned short utf8_to_euc_EFBC_x0213[] = {
- 0, 0x212A, 0x2230, 0x2174, 0x2170, 0x2173, 0x2175, 0x222F,
- 0x214A, 0x214B, 0x2176, 0x215C, 0x2124, 0x2231, 0x2125, 0x213F,
- 0x2330, 0x2331, 0x2332, 0x2333, 0x2334, 0x2335, 0x2336, 0x2337,
- 0x2338, 0x2339, 0x2127, 0x2128, 0x2163, 0x2161, 0x2164, 0x2129,
- 0x2177, 0x2341, 0x2342, 0x2343, 0x2344, 0x2345, 0x2346, 0x2347,
- 0x2348, 0x2349, 0x234A, 0x234B, 0x234C, 0x234D, 0x234E, 0x234F,
- 0x2350, 0x2351, 0x2352, 0x2353, 0x2354, 0x2355, 0x2356, 0x2357,
- 0x2358, 0x2359, 0x235A, 0x214E, 0x2140, 0x214F, 0x2130, 0x2132,
-};
-static const unsigned short utf8_to_euc_EFBD[] = {
- 0x212E, 0x2361, 0x2362, 0x2363, 0x2364, 0x2365, 0x2366, 0x2367,
- 0x2368, 0x2369, 0x236A, 0x236B, 0x236C, 0x236D, 0x236E, 0x236F,
- 0x2370, 0x2371, 0x2372, 0x2373, 0x2374, 0x2375, 0x2376, 0x2377,
- 0x2378, 0x2379, 0x237A, 0x2150, 0x2143, 0x2151, 0xA237, 0,
- 0, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27,
- 0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F,
- 0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37,
- 0x0E38, 0x0E39, 0x0E3A, 0x0E3B, 0x0E3C, 0x0E3D, 0x0E3E, 0x0E3F,
-};
-static const unsigned short utf8_to_euc_EFBD_ms[] = {
- 0x212E, 0x2361, 0x2362, 0x2363, 0x2364, 0x2365, 0x2366, 0x2367,
- 0x2368, 0x2369, 0x236A, 0x236B, 0x236C, 0x236D, 0x236E, 0x236F,
- 0x2370, 0x2371, 0x2372, 0x2373, 0x2374, 0x2375, 0x2376, 0x2377,
- 0x2378, 0x2379, 0x237A, 0x2150, 0x2143, 0x2151, 0x2141, 0,
- 0, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27,
- 0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F,
- 0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37,
- 0x0E38, 0x0E39, 0x0E3A, 0x0E3B, 0x0E3C, 0x0E3D, 0x0E3E, 0x0E3F,
-};
-static const unsigned short utf8_to_euc_EFBD_x0213[] = {
- 0x212E, 0x2361, 0x2362, 0x2363, 0x2364, 0x2365, 0x2366, 0x2367,
- 0x2368, 0x2369, 0x236A, 0x236B, 0x236C, 0x236D, 0x236E, 0x236F,
- 0x2370, 0x2371, 0x2372, 0x2373, 0x2374, 0x2375, 0x2376, 0x2377,
- 0x2378, 0x2379, 0x237A, 0x2150, 0x2143, 0x2151, 0x2232, 0x2256,
- 0x2257, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27,
- 0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F,
- 0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37,
- 0x0E38, 0x0E39, 0x0E3A, 0x0E3B, 0x0E3C, 0x0E3D, 0x0E3E, 0x0E3F,
-};
-static const unsigned short utf8_to_euc_EFBE[] = {
- 0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47,
- 0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F,
- 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57,
- 0x0E58, 0x0E59, 0x0E5A, 0x0E5B, 0x0E5C, 0x0E5D, 0x0E5E, 0x0E5F,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short utf8_to_euc_EFBF[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0x2171, 0x2172, 0x224C, 0x2131, 0xA243, 0x216F, 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,
-};
-static const unsigned short utf8_to_euc_EFBF_x0213[] = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x2131, 0, 0x216F, 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,
-};
-static const unsigned short *const utf8_to_euc_E1_x0213[] = {
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- utf8_to_euc_E1B8_x0213, 0, 0, 0,
- 0, utf8_to_euc_E1BD_x0213, 0, 0,
-};
-static const unsigned short *const utf8_to_euc_E2[] = {
- utf8_to_euc_E280, 0, 0, 0,
- utf8_to_euc_E284, utf8_to_euc_E285, utf8_to_euc_E286, utf8_to_euc_E287,
- utf8_to_euc_E288, utf8_to_euc_E289, utf8_to_euc_E28A, 0,
- utf8_to_euc_E28C, 0, 0, 0,
- 0, utf8_to_euc_E291, 0, 0,
- utf8_to_euc_E294, utf8_to_euc_E295, utf8_to_euc_E296, utf8_to_euc_E297,
- utf8_to_euc_E298, utf8_to_euc_E299, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
-};
-static const unsigned short *const utf8_to_euc_E2_ms[] = {
- utf8_to_euc_E280_ms, 0, 0, 0,
- utf8_to_euc_E284, utf8_to_euc_E285, utf8_to_euc_E286, utf8_to_euc_E287,
- utf8_to_euc_E288, utf8_to_euc_E289, utf8_to_euc_E28A, 0,
- utf8_to_euc_E28C, 0, 0, 0,
- 0, utf8_to_euc_E291, 0, 0,
- utf8_to_euc_E294, utf8_to_euc_E295, utf8_to_euc_E296, utf8_to_euc_E297,
- utf8_to_euc_E298, utf8_to_euc_E299, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
-};
-static const unsigned short *const utf8_to_euc_E2_932[] = {
- utf8_to_euc_E280_932, 0, 0, 0,
- utf8_to_euc_E284, utf8_to_euc_E285, utf8_to_euc_E286, utf8_to_euc_E287,
- utf8_to_euc_E288_932, utf8_to_euc_E289, utf8_to_euc_E28A, 0,
- utf8_to_euc_E28C, 0, 0, 0,
- 0, utf8_to_euc_E291, 0, 0,
- utf8_to_euc_E294, utf8_to_euc_E295, utf8_to_euc_E296, utf8_to_euc_E297,
- utf8_to_euc_E298, utf8_to_euc_E299, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
-};
-static const unsigned short *const utf8_to_euc_E2_mac[] = {
- utf8_to_euc_E280_932, 0, 0, 0,
- utf8_to_euc_E284_mac, utf8_to_euc_E285_mac, utf8_to_euc_E286, utf8_to_euc_E287,
- utf8_to_euc_E288_mac, utf8_to_euc_E289, utf8_to_euc_E28A_mac, 0,
- utf8_to_euc_E28C, 0, 0, 0,
- 0, utf8_to_euc_E291_mac, 0, 0,
- utf8_to_euc_E294, utf8_to_euc_E295, utf8_to_euc_E296, utf8_to_euc_E297,
- utf8_to_euc_E298, utf8_to_euc_E299, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
-};
-static const unsigned short *const utf8_to_euc_E2_x0213[] = {
- utf8_to_euc_E280_x0213, utf8_to_euc_E281_x0213, utf8_to_euc_E282_x0213, 0,
- utf8_to_euc_E284_x0213, utf8_to_euc_E285_x0213, utf8_to_euc_E286_x0213, utf8_to_euc_E287_x0213,
- utf8_to_euc_E288_x0213, utf8_to_euc_E289_x0213, utf8_to_euc_E28A_x0213, utf8_to_euc_E28B_x0213,
- utf8_to_euc_E28C_x0213, 0, utf8_to_euc_E28E_x0213, utf8_to_euc_E28F_x0213,
- utf8_to_euc_E290_x0213, utf8_to_euc_E291, 0, utf8_to_euc_E293_x0213,
- utf8_to_euc_E294, utf8_to_euc_E295, utf8_to_euc_E296_x0213, utf8_to_euc_E297_x0213,
- utf8_to_euc_E298_x0213, utf8_to_euc_E299_x0213, 0, 0,
- utf8_to_euc_E29C_x0213, utf8_to_euc_E29D_x0213, 0, 0,
- 0, 0, 0, 0,
- utf8_to_euc_E2A4_x0213, 0, utf8_to_euc_E2A6_x0213, utf8_to_euc_E2A7_x0213,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
-};
-static const unsigned short *const utf8_to_euc_E3[] = {
- utf8_to_euc_E380, utf8_to_euc_E381, utf8_to_euc_E382, utf8_to_euc_E383,
- 0, 0, 0, 0,
- utf8_to_euc_E388, 0, utf8_to_euc_E38A, 0,
- utf8_to_euc_E38C, utf8_to_euc_E38D, utf8_to_euc_E38E, utf8_to_euc_E38F,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
-};
-static const unsigned short *const utf8_to_euc_E3_932[] = {
- utf8_to_euc_E380_932, utf8_to_euc_E381, utf8_to_euc_E382_932, utf8_to_euc_E383,
- 0, 0, 0, 0,
- utf8_to_euc_E388, 0, utf8_to_euc_E38A, 0,
- utf8_to_euc_E38C, utf8_to_euc_E38D, utf8_to_euc_E38E, utf8_to_euc_E38F,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
-};
-static const unsigned short *const utf8_to_euc_E3_mac[] = {
- utf8_to_euc_E380_932, utf8_to_euc_E381, utf8_to_euc_E382_932, utf8_to_euc_E383,
- 0, 0, 0, 0,
- utf8_to_euc_E388_mac, 0, utf8_to_euc_E38A_mac, 0,
- utf8_to_euc_E38C_mac, utf8_to_euc_E38D_mac, utf8_to_euc_E38E_mac, utf8_to_euc_E38F_mac,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
-};
-static const unsigned short *const utf8_to_euc_E3_x0213[] = {
- utf8_to_euc_E380_x0213, utf8_to_euc_E381, utf8_to_euc_E382_x0213, utf8_to_euc_E383_x0213,
- 0, 0, 0, utf8_to_euc_E387_x0213,
- utf8_to_euc_E388, utf8_to_euc_E389_x0213, utf8_to_euc_E38A_x0213, utf8_to_euc_E38B_x0213,
- utf8_to_euc_E38C, utf8_to_euc_E38D, utf8_to_euc_E38E, utf8_to_euc_E38F_x0213,
- utf8_to_euc_E390_x0213, utf8_to_euc_E391_x0213, utf8_to_euc_E392_x0213, utf8_to_euc_E393_x0213,
- utf8_to_euc_E394_x0213, utf8_to_euc_E395_x0213, utf8_to_euc_E396_x0213, utf8_to_euc_E397_x0213,
- utf8_to_euc_E398_x0213, utf8_to_euc_E399_x0213, utf8_to_euc_E39A_x0213, utf8_to_euc_E39B_x0213,
- 0, utf8_to_euc_E39D_x0213, utf8_to_euc_E39E_x0213, utf8_to_euc_E39F_x0213,
- utf8_to_euc_E3A0_x0213, utf8_to_euc_E3A1_x0213, 0, utf8_to_euc_E3A3_x0213,
- utf8_to_euc_E3A4_x0213, utf8_to_euc_E3A5_x0213, 0, 0,
- 0, utf8_to_euc_E3A9_x0213, 0, utf8_to_euc_E3AB_x0213,
- utf8_to_euc_E3AC_x0213, utf8_to_euc_E3AD_x0213, utf8_to_euc_E3AE_x0213, utf8_to_euc_E3AF_x0213,
- utf8_to_euc_E3B0_x0213, 0, 0, utf8_to_euc_E3B3_x0213,
- utf8_to_euc_E3B4_x0213, utf8_to_euc_E3B5_x0213, utf8_to_euc_E3B6_x0213, utf8_to_euc_E3B7_x0213,
- utf8_to_euc_E3B8_x0213, utf8_to_euc_E3B9_x0213, utf8_to_euc_E3BA_x0213, 0,
- 0, utf8_to_euc_E3BD_x0213, utf8_to_euc_E3BE_x0213, utf8_to_euc_E3BF_x0213,
-};
-static const unsigned short *const utf8_to_euc_E4[] = {
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- utf8_to_euc_E4B8, utf8_to_euc_E4B9, utf8_to_euc_E4BA, utf8_to_euc_E4BB,
- utf8_to_euc_E4BC, utf8_to_euc_E4BD, utf8_to_euc_E4BE, utf8_to_euc_E4BF,
-};
-static const unsigned short *const utf8_to_euc_E4_x0213[] = {
- utf8_to_euc_E480_x0213, utf8_to_euc_E481_x0213, utf8_to_euc_E482_x0213, 0,
- utf8_to_euc_E484_x0213, utf8_to_euc_E485_x0213, utf8_to_euc_E486_x0213, utf8_to_euc_E487_x0213,
- utf8_to_euc_E488_x0213, utf8_to_euc_E489_x0213, 0, utf8_to_euc_E48B_x0213,
- utf8_to_euc_E48C_x0213, utf8_to_euc_E48D_x0213, 0, utf8_to_euc_E48F_x0213,
- utf8_to_euc_E490_x0213, utf8_to_euc_E491_x0213, utf8_to_euc_E492_x0213, utf8_to_euc_E493_x0213,
- utf8_to_euc_E494_x0213, utf8_to_euc_E495_x0213, utf8_to_euc_E496_x0213, utf8_to_euc_E497_x0213,
- utf8_to_euc_E498_x0213, utf8_to_euc_E499_x0213, utf8_to_euc_E49A_x0213, 0,
- utf8_to_euc_E49C_x0213, utf8_to_euc_E49D_x0213, 0, utf8_to_euc_E49F_x0213,
- utf8_to_euc_E4A0_x0213, utf8_to_euc_E4A1_x0213, utf8_to_euc_E4A2_x0213, 0,
- 0, 0, utf8_to_euc_E4A6_x0213, utf8_to_euc_E4A7_x0213,
- utf8_to_euc_E4A8_x0213, 0, utf8_to_euc_E4AA_x0213, 0,
- utf8_to_euc_E4AC_x0213, 0, 0, utf8_to_euc_E4AF_x0213,
- utf8_to_euc_E4B0_x0213, 0, 0, utf8_to_euc_E4B3_x0213,
- utf8_to_euc_E4B4_x0213, utf8_to_euc_E4B5_x0213, 0, 0,
- utf8_to_euc_E4B8_x0213, utf8_to_euc_E4B9_x0213, utf8_to_euc_E4BA_x0213, utf8_to_euc_E4BB_x0213,
- utf8_to_euc_E4BC_x0213, utf8_to_euc_E4BD_x0213, utf8_to_euc_E4BE_x0213, utf8_to_euc_E4BF_x0213,
-};
-static const unsigned short *const utf8_to_euc_E5[] = {
- utf8_to_euc_E580, utf8_to_euc_E581, utf8_to_euc_E582, utf8_to_euc_E583,
- utf8_to_euc_E584, utf8_to_euc_E585, utf8_to_euc_E586, utf8_to_euc_E587,
- utf8_to_euc_E588, utf8_to_euc_E589, utf8_to_euc_E58A, utf8_to_euc_E58B,
- utf8_to_euc_E58C, utf8_to_euc_E58D, utf8_to_euc_E58E, utf8_to_euc_E58F,
- utf8_to_euc_E590, utf8_to_euc_E591, utf8_to_euc_E592, utf8_to_euc_E593,
- utf8_to_euc_E594, utf8_to_euc_E595, utf8_to_euc_E596, utf8_to_euc_E597,
- utf8_to_euc_E598, utf8_to_euc_E599, utf8_to_euc_E59A, utf8_to_euc_E59B,
- utf8_to_euc_E59C, utf8_to_euc_E59D, utf8_to_euc_E59E, utf8_to_euc_E59F,
- utf8_to_euc_E5A0, utf8_to_euc_E5A1, utf8_to_euc_E5A2, utf8_to_euc_E5A3,
- utf8_to_euc_E5A4, utf8_to_euc_E5A5, utf8_to_euc_E5A6, utf8_to_euc_E5A7,
- utf8_to_euc_E5A8, utf8_to_euc_E5A9, utf8_to_euc_E5AA, utf8_to_euc_E5AB,
- utf8_to_euc_E5AC, utf8_to_euc_E5AD, utf8_to_euc_E5AE, utf8_to_euc_E5AF,
- utf8_to_euc_E5B0, utf8_to_euc_E5B1, utf8_to_euc_E5B2, utf8_to_euc_E5B3,
- utf8_to_euc_E5B4, utf8_to_euc_E5B5, utf8_to_euc_E5B6, utf8_to_euc_E5B7,
- utf8_to_euc_E5B8, utf8_to_euc_E5B9, utf8_to_euc_E5BA, utf8_to_euc_E5BB,
- utf8_to_euc_E5BC, utf8_to_euc_E5BD, utf8_to_euc_E5BE, utf8_to_euc_E5BF,
-};
-static const unsigned short *const utf8_to_euc_E5_x0213[] = {
- utf8_to_euc_E580_x0213, utf8_to_euc_E581_x0213, utf8_to_euc_E582_x0213, utf8_to_euc_E583_x0213,
- utf8_to_euc_E584_x0213, utf8_to_euc_E585_x0213, utf8_to_euc_E586_x0213, utf8_to_euc_E587_x0213,
- utf8_to_euc_E588_x0213, utf8_to_euc_E589_x0213, utf8_to_euc_E58A_x0213, utf8_to_euc_E58B_x0213,
- utf8_to_euc_E58C_x0213, utf8_to_euc_E58D_x0213, utf8_to_euc_E58E_x0213, utf8_to_euc_E58F_x0213,
- utf8_to_euc_E590_x0213, utf8_to_euc_E591_x0213, utf8_to_euc_E592_x0213, utf8_to_euc_E593_x0213,
- utf8_to_euc_E594_x0213, utf8_to_euc_E595_x0213, utf8_to_euc_E596_x0213, utf8_to_euc_E597_x0213,
- utf8_to_euc_E598_x0213, utf8_to_euc_E599_x0213, utf8_to_euc_E59A_x0213, utf8_to_euc_E59B_x0213,
- utf8_to_euc_E59C_x0213, utf8_to_euc_E59D_x0213, utf8_to_euc_E59E_x0213, utf8_to_euc_E59F_x0213,
- utf8_to_euc_E5A0_x0213, utf8_to_euc_E5A1_x0213, utf8_to_euc_E5A2_x0213, utf8_to_euc_E5A3_x0213,
- utf8_to_euc_E5A4_x0213, utf8_to_euc_E5A5_x0213, utf8_to_euc_E5A6_x0213, utf8_to_euc_E5A7_x0213,
- utf8_to_euc_E5A8_x0213, utf8_to_euc_E5A9_x0213, utf8_to_euc_E5AA_x0213, utf8_to_euc_E5AB_x0213,
- utf8_to_euc_E5AC_x0213, utf8_to_euc_E5AD_x0213, utf8_to_euc_E5AE_x0213, utf8_to_euc_E5AF_x0213,
- utf8_to_euc_E5B0_x0213, utf8_to_euc_E5B1_x0213, utf8_to_euc_E5B2_x0213, utf8_to_euc_E5B3_x0213,
- utf8_to_euc_E5B4_x0213, utf8_to_euc_E5B5_x0213, utf8_to_euc_E5B6_x0213, utf8_to_euc_E5B7_x0213,
- utf8_to_euc_E5B8_x0213, utf8_to_euc_E5B9_x0213, utf8_to_euc_E5BA_x0213, utf8_to_euc_E5BB_x0213,
- utf8_to_euc_E5BC_x0213, utf8_to_euc_E5BD_x0213, utf8_to_euc_E5BE_x0213, utf8_to_euc_E5BF_x0213,
-};
-static const unsigned short *const utf8_to_euc_E6[] = {
- utf8_to_euc_E680, utf8_to_euc_E681, utf8_to_euc_E682, utf8_to_euc_E683,
- utf8_to_euc_E684, utf8_to_euc_E685, utf8_to_euc_E686, utf8_to_euc_E687,
- utf8_to_euc_E688, utf8_to_euc_E689, utf8_to_euc_E68A, utf8_to_euc_E68B,
- utf8_to_euc_E68C, utf8_to_euc_E68D, utf8_to_euc_E68E, utf8_to_euc_E68F,
- utf8_to_euc_E690, utf8_to_euc_E691, utf8_to_euc_E692, utf8_to_euc_E693,
- utf8_to_euc_E694, utf8_to_euc_E695, utf8_to_euc_E696, utf8_to_euc_E697,
- utf8_to_euc_E698, utf8_to_euc_E699, utf8_to_euc_E69A, utf8_to_euc_E69B,
- utf8_to_euc_E69C, utf8_to_euc_E69D, utf8_to_euc_E69E, utf8_to_euc_E69F,
- utf8_to_euc_E6A0, utf8_to_euc_E6A1, utf8_to_euc_E6A2, utf8_to_euc_E6A3,
- utf8_to_euc_E6A4, utf8_to_euc_E6A5, utf8_to_euc_E6A6, utf8_to_euc_E6A7,
- utf8_to_euc_E6A8, utf8_to_euc_E6A9, utf8_to_euc_E6AA, utf8_to_euc_E6AB,
- utf8_to_euc_E6AC, utf8_to_euc_E6AD, utf8_to_euc_E6AE, utf8_to_euc_E6AF,
- utf8_to_euc_E6B0, utf8_to_euc_E6B1, utf8_to_euc_E6B2, utf8_to_euc_E6B3,
- utf8_to_euc_E6B4, utf8_to_euc_E6B5, utf8_to_euc_E6B6, utf8_to_euc_E6B7,
- utf8_to_euc_E6B8, utf8_to_euc_E6B9, utf8_to_euc_E6BA, utf8_to_euc_E6BB,
- utf8_to_euc_E6BC, utf8_to_euc_E6BD, utf8_to_euc_E6BE, utf8_to_euc_E6BF,
-};
-static const unsigned short *const utf8_to_euc_E6_x0213[] = {
- utf8_to_euc_E680_x0213, utf8_to_euc_E681_x0213, utf8_to_euc_E682_x0213, utf8_to_euc_E683_x0213,
- utf8_to_euc_E684_x0213, utf8_to_euc_E685_x0213, utf8_to_euc_E686_x0213, utf8_to_euc_E687_x0213,
- utf8_to_euc_E688_x0213, utf8_to_euc_E689_x0213, utf8_to_euc_E68A_x0213, utf8_to_euc_E68B_x0213,
- utf8_to_euc_E68C_x0213, utf8_to_euc_E68D_x0213, utf8_to_euc_E68E_x0213, utf8_to_euc_E68F_x0213,
- utf8_to_euc_E690_x0213, utf8_to_euc_E691_x0213, utf8_to_euc_E692_x0213, utf8_to_euc_E693_x0213,
- utf8_to_euc_E694_x0213, utf8_to_euc_E695_x0213, utf8_to_euc_E696_x0213, utf8_to_euc_E697_x0213,
- utf8_to_euc_E698_x0213, utf8_to_euc_E699_x0213, utf8_to_euc_E69A_x0213, utf8_to_euc_E69B_x0213,
- utf8_to_euc_E69C_x0213, utf8_to_euc_E69D_x0213, utf8_to_euc_E69E_x0213, utf8_to_euc_E69F_x0213,
- utf8_to_euc_E6A0_x0213, utf8_to_euc_E6A1_x0213, utf8_to_euc_E6A2_x0213, utf8_to_euc_E6A3_x0213,
- utf8_to_euc_E6A4_x0213, utf8_to_euc_E6A5_x0213, utf8_to_euc_E6A6_x0213, utf8_to_euc_E6A7_x0213,
- utf8_to_euc_E6A8_x0213, utf8_to_euc_E6A9_x0213, utf8_to_euc_E6AA_x0213, utf8_to_euc_E6AB_x0213,
- utf8_to_euc_E6AC_x0213, utf8_to_euc_E6AD_x0213, utf8_to_euc_E6AE_x0213, utf8_to_euc_E6AF_x0213,
- utf8_to_euc_E6B0_x0213, utf8_to_euc_E6B1_x0213, utf8_to_euc_E6B2_x0213, utf8_to_euc_E6B3_x0213,
- utf8_to_euc_E6B4_x0213, utf8_to_euc_E6B5_x0213, utf8_to_euc_E6B6_x0213, utf8_to_euc_E6B7_x0213,
- utf8_to_euc_E6B8_x0213, utf8_to_euc_E6B9_x0213, utf8_to_euc_E6BA_x0213, utf8_to_euc_E6BB_x0213,
- utf8_to_euc_E6BC_x0213, utf8_to_euc_E6BD_x0213, utf8_to_euc_E6BE_x0213, utf8_to_euc_E6BF_x0213,
-};
-static const unsigned short *const utf8_to_euc_E7[] = {
- utf8_to_euc_E780, utf8_to_euc_E781, utf8_to_euc_E782, utf8_to_euc_E783,
- utf8_to_euc_E784, utf8_to_euc_E785, utf8_to_euc_E786, utf8_to_euc_E787,
- utf8_to_euc_E788, utf8_to_euc_E789, utf8_to_euc_E78A, utf8_to_euc_E78B,
- utf8_to_euc_E78C, utf8_to_euc_E78D, utf8_to_euc_E78E, utf8_to_euc_E78F,
- utf8_to_euc_E790, utf8_to_euc_E791, utf8_to_euc_E792, utf8_to_euc_E793,
- utf8_to_euc_E794, utf8_to_euc_E795, utf8_to_euc_E796, utf8_to_euc_E797,
- utf8_to_euc_E798, utf8_to_euc_E799, utf8_to_euc_E79A, utf8_to_euc_E79B,
- utf8_to_euc_E79C, utf8_to_euc_E79D, utf8_to_euc_E79E, utf8_to_euc_E79F,
- utf8_to_euc_E7A0, utf8_to_euc_E7A1, utf8_to_euc_E7A2, utf8_to_euc_E7A3,
- utf8_to_euc_E7A4, utf8_to_euc_E7A5, utf8_to_euc_E7A6, utf8_to_euc_E7A7,
- utf8_to_euc_E7A8, utf8_to_euc_E7A9, utf8_to_euc_E7AA, utf8_to_euc_E7AB,
- utf8_to_euc_E7AC, utf8_to_euc_E7AD, utf8_to_euc_E7AE, utf8_to_euc_E7AF,
- utf8_to_euc_E7B0, utf8_to_euc_E7B1, utf8_to_euc_E7B2, utf8_to_euc_E7B3,
- utf8_to_euc_E7B4, utf8_to_euc_E7B5, utf8_to_euc_E7B6, utf8_to_euc_E7B7,
- utf8_to_euc_E7B8, utf8_to_euc_E7B9, utf8_to_euc_E7BA, 0,
- utf8_to_euc_E7BC, utf8_to_euc_E7BD, utf8_to_euc_E7BE, utf8_to_euc_E7BF,
-};
-static const unsigned short *const utf8_to_euc_E7_x0213[] = {
- utf8_to_euc_E780_x0213, utf8_to_euc_E781_x0213, utf8_to_euc_E782_x0213, utf8_to_euc_E783_x0213,
- utf8_to_euc_E784_x0213, utf8_to_euc_E785_x0213, utf8_to_euc_E786_x0213, utf8_to_euc_E787_x0213,
- utf8_to_euc_E788_x0213, utf8_to_euc_E789_x0213, utf8_to_euc_E78A_x0213, utf8_to_euc_E78B_x0213,
- utf8_to_euc_E78C_x0213, utf8_to_euc_E78D_x0213, utf8_to_euc_E78E_x0213, utf8_to_euc_E78F_x0213,
- utf8_to_euc_E790_x0213, utf8_to_euc_E791_x0213, utf8_to_euc_E792_x0213, utf8_to_euc_E793_x0213,
- utf8_to_euc_E794_x0213, utf8_to_euc_E795_x0213, utf8_to_euc_E796_x0213, utf8_to_euc_E797_x0213,
- utf8_to_euc_E798_x0213, utf8_to_euc_E799_x0213, utf8_to_euc_E79A_x0213, utf8_to_euc_E79B_x0213,
- utf8_to_euc_E79C_x0213, utf8_to_euc_E79D_x0213, utf8_to_euc_E79E_x0213, utf8_to_euc_E79F_x0213,
- utf8_to_euc_E7A0_x0213, utf8_to_euc_E7A1_x0213, utf8_to_euc_E7A2_x0213, utf8_to_euc_E7A3_x0213,
- utf8_to_euc_E7A4_x0213, utf8_to_euc_E7A5_x0213, utf8_to_euc_E7A6_x0213, utf8_to_euc_E7A7_x0213,
- utf8_to_euc_E7A8_x0213, utf8_to_euc_E7A9_x0213, utf8_to_euc_E7AA_x0213, utf8_to_euc_E7AB_x0213,
- utf8_to_euc_E7AC_x0213, utf8_to_euc_E7AD_x0213, utf8_to_euc_E7AE_x0213, utf8_to_euc_E7AF_x0213,
- utf8_to_euc_E7B0_x0213, utf8_to_euc_E7B1_x0213, utf8_to_euc_E7B2_x0213, utf8_to_euc_E7B3_x0213,
- utf8_to_euc_E7B4_x0213, utf8_to_euc_E7B5_x0213, utf8_to_euc_E7B6_x0213, utf8_to_euc_E7B7_x0213,
- utf8_to_euc_E7B8_x0213, utf8_to_euc_E7B9_x0213, utf8_to_euc_E7BA_x0213, 0,
- utf8_to_euc_E7BC_x0213, utf8_to_euc_E7BD_x0213, utf8_to_euc_E7BE_x0213, utf8_to_euc_E7BF_x0213,
-};
-static const unsigned short *const utf8_to_euc_E8[] = {
- utf8_to_euc_E880, utf8_to_euc_E881, utf8_to_euc_E882, utf8_to_euc_E883,
- utf8_to_euc_E884, utf8_to_euc_E885, utf8_to_euc_E886, utf8_to_euc_E887,
- utf8_to_euc_E888, utf8_to_euc_E889, utf8_to_euc_E88A, utf8_to_euc_E88B,
- utf8_to_euc_E88C, utf8_to_euc_E88D, utf8_to_euc_E88E, utf8_to_euc_E88F,
- utf8_to_euc_E890, utf8_to_euc_E891, utf8_to_euc_E892, utf8_to_euc_E893,
- utf8_to_euc_E894, utf8_to_euc_E895, utf8_to_euc_E896, utf8_to_euc_E897,
- utf8_to_euc_E898, utf8_to_euc_E899, utf8_to_euc_E89A, utf8_to_euc_E89B,
- utf8_to_euc_E89C, utf8_to_euc_E89D, utf8_to_euc_E89E, utf8_to_euc_E89F,
- utf8_to_euc_E8A0, utf8_to_euc_E8A1, utf8_to_euc_E8A2, utf8_to_euc_E8A3,
- utf8_to_euc_E8A4, utf8_to_euc_E8A5, utf8_to_euc_E8A6, utf8_to_euc_E8A7,
- utf8_to_euc_E8A8, utf8_to_euc_E8A9, utf8_to_euc_E8AA, utf8_to_euc_E8AB,
- utf8_to_euc_E8AC, utf8_to_euc_E8AD, utf8_to_euc_E8AE, 0,
- utf8_to_euc_E8B0, utf8_to_euc_E8B1, utf8_to_euc_E8B2, utf8_to_euc_E8B3,
- utf8_to_euc_E8B4, utf8_to_euc_E8B5, utf8_to_euc_E8B6, utf8_to_euc_E8B7,
- utf8_to_euc_E8B8, utf8_to_euc_E8B9, utf8_to_euc_E8BA, utf8_to_euc_E8BB,
- utf8_to_euc_E8BC, utf8_to_euc_E8BD, utf8_to_euc_E8BE, utf8_to_euc_E8BF,
-};
-static const unsigned short *const utf8_to_euc_E8_x0213[] = {
- utf8_to_euc_E880_x0213, utf8_to_euc_E881_x0213, utf8_to_euc_E882_x0213, utf8_to_euc_E883_x0213,
- utf8_to_euc_E884_x0213, utf8_to_euc_E885_x0213, utf8_to_euc_E886_x0213, utf8_to_euc_E887_x0213,
- utf8_to_euc_E888_x0213, utf8_to_euc_E889_x0213, utf8_to_euc_E88A_x0213, utf8_to_euc_E88B_x0213,
- utf8_to_euc_E88C_x0213, utf8_to_euc_E88D_x0213, utf8_to_euc_E88E_x0213, utf8_to_euc_E88F_x0213,
- utf8_to_euc_E890_x0213, utf8_to_euc_E891_x0213, utf8_to_euc_E892_x0213, utf8_to_euc_E893_x0213,
- utf8_to_euc_E894_x0213, utf8_to_euc_E895_x0213, utf8_to_euc_E896_x0213, utf8_to_euc_E897_x0213,
- utf8_to_euc_E898_x0213, utf8_to_euc_E899_x0213, utf8_to_euc_E89A_x0213, utf8_to_euc_E89B_x0213,
- utf8_to_euc_E89C_x0213, utf8_to_euc_E89D_x0213, utf8_to_euc_E89E_x0213, utf8_to_euc_E89F_x0213,
- utf8_to_euc_E8A0_x0213, utf8_to_euc_E8A1_x0213, utf8_to_euc_E8A2_x0213, utf8_to_euc_E8A3_x0213,
- utf8_to_euc_E8A4_x0213, utf8_to_euc_E8A5_x0213, utf8_to_euc_E8A6_x0213, utf8_to_euc_E8A7_x0213,
- utf8_to_euc_E8A8_x0213, utf8_to_euc_E8A9_x0213, utf8_to_euc_E8AA_x0213, utf8_to_euc_E8AB_x0213,
- utf8_to_euc_E8AC_x0213, utf8_to_euc_E8AD_x0213, utf8_to_euc_E8AE_x0213, 0,
- utf8_to_euc_E8B0_x0213, utf8_to_euc_E8B1_x0213, utf8_to_euc_E8B2_x0213, utf8_to_euc_E8B3_x0213,
- utf8_to_euc_E8B4_x0213, utf8_to_euc_E8B5_x0213, utf8_to_euc_E8B6_x0213, utf8_to_euc_E8B7_x0213,
- utf8_to_euc_E8B8_x0213, utf8_to_euc_E8B9_x0213, utf8_to_euc_E8BA_x0213, utf8_to_euc_E8BB_x0213,
- utf8_to_euc_E8BC_x0213, utf8_to_euc_E8BD_x0213, utf8_to_euc_E8BE_x0213, utf8_to_euc_E8BF_x0213,
-};
-static const unsigned short *const utf8_to_euc_E9[] = {
- utf8_to_euc_E980, utf8_to_euc_E981, utf8_to_euc_E982, utf8_to_euc_E983,
- utf8_to_euc_E984, utf8_to_euc_E985, utf8_to_euc_E986, utf8_to_euc_E987,
- utf8_to_euc_E988, utf8_to_euc_E989, utf8_to_euc_E98A, utf8_to_euc_E98B,
- utf8_to_euc_E98C, utf8_to_euc_E98D, utf8_to_euc_E98E, utf8_to_euc_E98F,
- utf8_to_euc_E990, utf8_to_euc_E991, utf8_to_euc_E992, 0,
- 0, utf8_to_euc_E995, utf8_to_euc_E996, utf8_to_euc_E997,
- utf8_to_euc_E998, utf8_to_euc_E999, utf8_to_euc_E99A, utf8_to_euc_E99B,
- utf8_to_euc_E99C, utf8_to_euc_E99D, utf8_to_euc_E99E, utf8_to_euc_E99F,
- utf8_to_euc_E9A0, utf8_to_euc_E9A1, utf8_to_euc_E9A2, utf8_to_euc_E9A3,
- utf8_to_euc_E9A4, utf8_to_euc_E9A5, utf8_to_euc_E9A6, utf8_to_euc_E9A7,
- utf8_to_euc_E9A8, utf8_to_euc_E9A9, utf8_to_euc_E9AA, utf8_to_euc_E9AB,
- utf8_to_euc_E9AC, utf8_to_euc_E9AD, utf8_to_euc_E9AE, utf8_to_euc_E9AF,
- utf8_to_euc_E9B0, utf8_to_euc_E9B1, 0, utf8_to_euc_E9B3,
- utf8_to_euc_E9B4, utf8_to_euc_E9B5, utf8_to_euc_E9B6, utf8_to_euc_E9B7,
- utf8_to_euc_E9B8, utf8_to_euc_E9B9, utf8_to_euc_E9BA, utf8_to_euc_E9BB,
- utf8_to_euc_E9BC, utf8_to_euc_E9BD, utf8_to_euc_E9BE, 0,
-};
-static const unsigned short *const utf8_to_euc_E9_x0213[] = {
- utf8_to_euc_E980_x0213, utf8_to_euc_E981_x0213, utf8_to_euc_E982_x0213, utf8_to_euc_E983_x0213,
- utf8_to_euc_E984_x0213, utf8_to_euc_E985_x0213, utf8_to_euc_E986_x0213, utf8_to_euc_E987_x0213,
- utf8_to_euc_E988_x0213, utf8_to_euc_E989_x0213, utf8_to_euc_E98A_x0213, utf8_to_euc_E98B_x0213,
- utf8_to_euc_E98C_x0213, utf8_to_euc_E98D_x0213, utf8_to_euc_E98E_x0213, utf8_to_euc_E98F_x0213,
- utf8_to_euc_E990_x0213, utf8_to_euc_E991_x0213, utf8_to_euc_E992, 0,
- 0, utf8_to_euc_E995_x0213, utf8_to_euc_E996_x0213, utf8_to_euc_E997_x0213,
- utf8_to_euc_E998_x0213, utf8_to_euc_E999_x0213, utf8_to_euc_E99A_x0213, utf8_to_euc_E99B_x0213,
- utf8_to_euc_E99C_x0213, utf8_to_euc_E99D_x0213, utf8_to_euc_E99E_x0213, utf8_to_euc_E99F_x0213,
- utf8_to_euc_E9A0_x0213, utf8_to_euc_E9A1_x0213, utf8_to_euc_E9A2_x0213, utf8_to_euc_E9A3_x0213,
- utf8_to_euc_E9A4_x0213, utf8_to_euc_E9A5_x0213, utf8_to_euc_E9A6_x0213, utf8_to_euc_E9A7_x0213,
- utf8_to_euc_E9A8_x0213, utf8_to_euc_E9A9_x0213, utf8_to_euc_E9AA_x0213, utf8_to_euc_E9AB_x0213,
- utf8_to_euc_E9AC_x0213, utf8_to_euc_E9AD_x0213, utf8_to_euc_E9AE_x0213, utf8_to_euc_E9AF_x0213,
- utf8_to_euc_E9B0_x0213, utf8_to_euc_E9B1_x0213, 0, utf8_to_euc_E9B3_x0213,
- utf8_to_euc_E9B4_x0213, utf8_to_euc_E9B5_x0213, utf8_to_euc_E9B6_x0213, utf8_to_euc_E9B7_x0213,
- utf8_to_euc_E9B8_x0213, utf8_to_euc_E9B9_x0213, utf8_to_euc_E9BA_x0213, utf8_to_euc_E9BB_x0213,
- utf8_to_euc_E9BC_x0213, utf8_to_euc_E9BD_x0213, utf8_to_euc_E9BE_x0213, 0,
-};
-static const unsigned short *const utf8_to_euc_EF[] = {
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- utf8_to_euc_EFA4, 0, 0, utf8_to_euc_EFA7,
- utf8_to_euc_EFA8, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- utf8_to_euc_EFBC, utf8_to_euc_EFBD, utf8_to_euc_EFBE, utf8_to_euc_EFBF,
-};
-static const unsigned short *const utf8_to_euc_EF_ms[] = {
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- utf8_to_euc_EFA4, 0, 0, utf8_to_euc_EFA7,
- utf8_to_euc_EFA8, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- utf8_to_euc_EFBC, utf8_to_euc_EFBD_ms, utf8_to_euc_EFBE, utf8_to_euc_EFBF,
-};
-static const unsigned short *const utf8_to_euc_EF_x0213[] = {
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- utf8_to_euc_EFA4_x0213, utf8_to_euc_EFA5_x0213, 0, utf8_to_euc_EFA7_x0213,
- utf8_to_euc_EFA8_x0213, utf8_to_euc_EFA9_x0213, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, utf8_to_euc_EFB9_x0213, 0, 0,
- utf8_to_euc_EFBC_x0213, utf8_to_euc_EFBD_x0213, utf8_to_euc_EFBE, utf8_to_euc_EFBF_x0213,
-};
-const unsigned short *const utf8_to_euc_2bytes[] = {
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, utf8_to_euc_C2, utf8_to_euc_C3,
- utf8_to_euc_C4, utf8_to_euc_C5, 0, utf8_to_euc_C7,
- 0, 0, 0, utf8_to_euc_CB,
- 0, 0, utf8_to_euc_CE, utf8_to_euc_CF,
- utf8_to_euc_D0, utf8_to_euc_D1, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
-};
-const unsigned short *const utf8_to_euc_2bytes_ms[] = {
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, utf8_to_euc_C2_ms, utf8_to_euc_C3,
- utf8_to_euc_C4, utf8_to_euc_C5, 0, utf8_to_euc_C7,
- 0, 0, 0, utf8_to_euc_CB,
- 0, 0, utf8_to_euc_CE, utf8_to_euc_CF,
- utf8_to_euc_D0, utf8_to_euc_D1, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
-};
-const unsigned short *const utf8_to_euc_2bytes_932[] = {
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, utf8_to_euc_C2_932, utf8_to_euc_C3_932,
- utf8_to_euc_C4, utf8_to_euc_C5, 0, utf8_to_euc_C7,
- 0, 0, 0, utf8_to_euc_CB,
- 0, 0, utf8_to_euc_CE, utf8_to_euc_CF,
- utf8_to_euc_D0, utf8_to_euc_D1, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
-};
-const unsigned short *const utf8_to_euc_2bytes_mac[] = {
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, utf8_to_euc_C2_mac, utf8_to_euc_C3,
- utf8_to_euc_C4, utf8_to_euc_C5, 0, utf8_to_euc_C7,
- 0, 0, 0, utf8_to_euc_CB,
- 0, 0, utf8_to_euc_CE, utf8_to_euc_CF,
- utf8_to_euc_D0, utf8_to_euc_D1, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
-};
-const unsigned short *const utf8_to_euc_2bytes_x0213[] = {
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, utf8_to_euc_C2_x0213, utf8_to_euc_C3_x0213,
- utf8_to_euc_C4_x0213, utf8_to_euc_C5_x0213, utf8_to_euc_C6_x0213, utf8_to_euc_C7_x0213,
- 0, utf8_to_euc_C9_x0213, utf8_to_euc_CA_x0213, utf8_to_euc_CB_x0213,
- utf8_to_euc_CC_x0213, utf8_to_euc_CD_x0213, utf8_to_euc_CE, utf8_to_euc_CF_x0213,
- utf8_to_euc_D0, utf8_to_euc_D1, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
-};
-const unsigned short *const *const utf8_to_euc_3bytes[] = {
- 0, 0, utf8_to_euc_E2, utf8_to_euc_E3,
- utf8_to_euc_E4, utf8_to_euc_E5, utf8_to_euc_E6, utf8_to_euc_E7,
- utf8_to_euc_E8, utf8_to_euc_E9, 0, 0,
- 0, 0, 0, utf8_to_euc_EF,
-};
-const unsigned short *const *const utf8_to_euc_3bytes_ms[] = {
- 0, 0, utf8_to_euc_E2_ms, utf8_to_euc_E3,
- utf8_to_euc_E4, utf8_to_euc_E5, utf8_to_euc_E6, utf8_to_euc_E7,
- utf8_to_euc_E8, utf8_to_euc_E9, 0, 0,
- 0, 0, 0, utf8_to_euc_EF_ms,
-};
-const unsigned short *const *const utf8_to_euc_3bytes_932[] = {
- 0, 0, utf8_to_euc_E2_932, utf8_to_euc_E3_932,
- utf8_to_euc_E4, utf8_to_euc_E5, utf8_to_euc_E6, utf8_to_euc_E7,
- utf8_to_euc_E8, utf8_to_euc_E9, 0, 0,
- 0, 0, 0, utf8_to_euc_EF_ms,
-};
-const unsigned short *const *const utf8_to_euc_3bytes_mac[] = {
- 0, 0, utf8_to_euc_E2_mac, utf8_to_euc_E3_mac,
- utf8_to_euc_E4, utf8_to_euc_E5, utf8_to_euc_E6, utf8_to_euc_E7,
- utf8_to_euc_E8, utf8_to_euc_E9, 0, 0,
- 0, 0, 0, utf8_to_euc_EF_ms,
-};
-const unsigned short *const *const utf8_to_euc_3bytes_x0213[] = {
- 0, utf8_to_euc_E1_x0213, utf8_to_euc_E2_x0213, utf8_to_euc_E3_x0213,
- utf8_to_euc_E4_x0213, utf8_to_euc_E5_x0213, utf8_to_euc_E6_x0213, utf8_to_euc_E7_x0213,
- utf8_to_euc_E8_x0213, utf8_to_euc_E9_x0213, 0, 0,
- 0, 0, 0, utf8_to_euc_EF_x0213,
-};
-
-#ifdef UNICODE_NORMALIZATION
-
-/* Normalization Table by Apple */
-/* http://developer.apple.com/technotes/tn/tn1150table.html */
-
-const struct normalization_pair normalization_table[] = {
- {{0xcd,0xbe}, {0x3b}},
- {{0xc3,0x80}, {0x41,0xcc,0x80,0x00}},
- {{0xc3,0x81}, {0x41,0xcc,0x81}},
- {{0xc3,0x82}, {0x41,0xcc,0x82}},
- {{0xe1,0xba,0xa6}, {0x41,0xcc,0x82,0xcc,0x80}},
- {{0xe1,0xba,0xa4}, {0x41,0xcc,0x82,0xcc,0x81}},
- {{0xe1,0xba,0xaa}, {0x41,0xcc,0x82,0xcc,0x83}},
- {{0xe1,0xba,0xa8}, {0x41,0xcc,0x82,0xcc,0x89}},
- {{0xc3,0x83}, {0x41,0xcc,0x83}},
- {{0xc4,0x80}, {0x41,0xcc,0x84}},
- {{0xc4,0x82}, {0x41,0xcc,0x86}},
- {{0xe1,0xba,0xb0}, {0x41,0xcc,0x86,0xcc,0x80}},
- {{0xe1,0xba,0xae}, {0x41,0xcc,0x86,0xcc,0x81}},
- {{0xe1,0xba,0xb4}, {0x41,0xcc,0x86,0xcc,0x83}},
- {{0xe1,0xba,0xb2}, {0x41,0xcc,0x86,0xcc,0x89}},
- {{0xc7,0xa0}, {0x41,0xcc,0x87,0xcc,0x84}},
- {{0xc3,0x84}, {0x41,0xcc,0x88}},
- {{0xc7,0x9e}, {0x41,0xcc,0x88,0xcc,0x84}},
- {{0xe1,0xba,0xa2}, {0x41,0xcc,0x89}},
- {{0xc3,0x85}, {0x41,0xcc,0x8a}},
- {{0xc7,0xba}, {0x41,0xcc,0x8a,0xcc,0x81}},
- {{0xc7,0x8d}, {0x41,0xcc,0x8c}},
- {{0xc8,0x80}, {0x41,0xcc,0x8f}},
- {{0xc8,0x82}, {0x41,0xcc,0x91}},
- {{0xe1,0xba,0xa0}, {0x41,0xcc,0xa3}},
- {{0xe1,0xba,0xac}, {0x41,0xcc,0xa3,0xcc,0x82}},
- {{0xe1,0xba,0xb6}, {0x41,0xcc,0xa3,0xcc,0x86}},
- {{0xe1,0xb8,0x80}, {0x41,0xcc,0xa5}},
- {{0xc4,0x84}, {0x41,0xcc,0xa8}},
- {{0xe1,0xb8,0x82}, {0x42,0xcc,0x87}},
- {{0xe1,0xb8,0x84}, {0x42,0xcc,0xa3}},
- {{0xe1,0xb8,0x86}, {0x42,0xcc,0xb1}},
- {{0xc4,0x86}, {0x43,0xcc,0x81}},
- {{0xc4,0x88}, {0x43,0xcc,0x82}},
- {{0xc4,0x8a}, {0x43,0xcc,0x87}},
- {{0xc4,0x8c}, {0x43,0xcc,0x8c}},
- {{0xc3,0x87}, {0x43,0xcc,0xa7}},
- {{0xe1,0xb8,0x88}, {0x43,0xcc,0xa7,0xcc,0x81}},
- {{0xe1,0xb8,0x8a}, {0x44,0xcc,0x87}},
- {{0xc4,0x8e}, {0x44,0xcc,0x8c}},
- {{0xe1,0xb8,0x8c}, {0x44,0xcc,0xa3}},
- {{0xe1,0xb8,0x90}, {0x44,0xcc,0xa7}},
- {{0xe1,0xb8,0x92}, {0x44,0xcc,0xad}},
- {{0xe1,0xb8,0x8e}, {0x44,0xcc,0xb1}},
- {{0xc3,0x88}, {0x45,0xcc,0x80}},
- {{0xc3,0x89}, {0x45,0xcc,0x81}},
- {{0xc3,0x8a}, {0x45,0xcc,0x82}},
- {{0xe1,0xbb,0x80}, {0x45,0xcc,0x82,0xcc,0x80}},
- {{0xe1,0xba,0xbe}, {0x45,0xcc,0x82,0xcc,0x81}},
- {{0xe1,0xbb,0x84}, {0x45,0xcc,0x82,0xcc,0x83}},
- {{0xe1,0xbb,0x82}, {0x45,0xcc,0x82,0xcc,0x89}},
- {{0xe1,0xba,0xbc}, {0x45,0xcc,0x83}},
- {{0xc4,0x92}, {0x45,0xcc,0x84}},
- {{0xe1,0xb8,0x94}, {0x45,0xcc,0x84,0xcc,0x80}},
- {{0xe1,0xb8,0x96}, {0x45,0xcc,0x84,0xcc,0x81}},
- {{0xc4,0x94}, {0x45,0xcc,0x86}},
- {{0xc4,0x96}, {0x45,0xcc,0x87}},
- {{0xc3,0x8b}, {0x45,0xcc,0x88}},
- {{0xe1,0xba,0xba}, {0x45,0xcc,0x89}},
- {{0xc4,0x9a}, {0x45,0xcc,0x8c}},
- {{0xc8,0x84}, {0x45,0xcc,0x8f}},
- {{0xc8,0x86}, {0x45,0xcc,0x91}},
- {{0xe1,0xba,0xb8}, {0x45,0xcc,0xa3}},
- {{0xe1,0xbb,0x86}, {0x45,0xcc,0xa3,0xcc,0x82}},
- {{0xe1,0xb8,0x9c}, {0x45,0xcc,0xa7,0xcc,0x86}},
- {{0xc4,0x98}, {0x45,0xcc,0xa8}},
- {{0xe1,0xb8,0x98}, {0x45,0xcc,0xad}},
- {{0xe1,0xb8,0x9a}, {0x45,0xcc,0xb0}},
- {{0xe1,0xb8,0x9e}, {0x46,0xcc,0x87}},
- {{0xc7,0xb4}, {0x47,0xcc,0x81}},
- {{0xc4,0x9c}, {0x47,0xcc,0x82}},
- {{0xe1,0xb8,0xa0}, {0x47,0xcc,0x84}},
- {{0xc4,0x9e}, {0x47,0xcc,0x86}},
- {{0xc4,0xa0}, {0x47,0xcc,0x87}},
- {{0xc7,0xa6}, {0x47,0xcc,0x8c}},
- {{0xc4,0xa2}, {0x47,0xcc,0xa7}},
- {{0xc4,0xa4}, {0x48,0xcc,0x82}},
- {{0xe1,0xb8,0xa2}, {0x48,0xcc,0x87}},
- {{0xe1,0xb8,0xa6}, {0x48,0xcc,0x88}},
- {{0xe1,0xb8,0xa4}, {0x48,0xcc,0xa3}},
- {{0xe1,0xb8,0xa8}, {0x48,0xcc,0xa7}},
- {{0xe1,0xb8,0xaa}, {0x48,0xcc,0xae}},
- {{0xc3,0x8c}, {0x49,0xcc,0x80}},
- {{0xc3,0x8d}, {0x49,0xcc,0x81}},
- {{0xc3,0x8e}, {0x49,0xcc,0x82}},
- {{0xc4,0xa8}, {0x49,0xcc,0x83}},
- {{0xc4,0xaa}, {0x49,0xcc,0x84}},
- {{0xc4,0xac}, {0x49,0xcc,0x86}},
- {{0xc4,0xb0}, {0x49,0xcc,0x87}},
- {{0xc3,0x8f}, {0x49,0xcc,0x88}},
- {{0xe1,0xb8,0xae}, {0x49,0xcc,0x88,0xcc,0x81}},
- {{0xe1,0xbb,0x88}, {0x49,0xcc,0x89}},
- {{0xc7,0x8f}, {0x49,0xcc,0x8c}},
- {{0xc8,0x88}, {0x49,0xcc,0x8f}},
- {{0xc8,0x8a}, {0x49,0xcc,0x91}},
- {{0xe1,0xbb,0x8a}, {0x49,0xcc,0xa3}},
- {{0xc4,0xae}, {0x49,0xcc,0xa8}},
- {{0xe1,0xb8,0xac}, {0x49,0xcc,0xb0}},
- {{0xc4,0xb4}, {0x4a,0xcc,0x82}},
- {{0xe1,0xb8,0xb0}, {0x4b,0xcc,0x81}},
- {{0xc7,0xa8}, {0x4b,0xcc,0x8c}},
- {{0xe1,0xb8,0xb2}, {0x4b,0xcc,0xa3}},
- {{0xc4,0xb6}, {0x4b,0xcc,0xa7}},
- {{0xe1,0xb8,0xb4}, {0x4b,0xcc,0xb1}},
- {{0xc4,0xb9}, {0x4c,0xcc,0x81}},
- {{0xc4,0xbd}, {0x4c,0xcc,0x8c}},
- {{0xe1,0xb8,0xb6}, {0x4c,0xcc,0xa3}},
- {{0xe1,0xb8,0xb8}, {0x4c,0xcc,0xa3,0xcc,0x84}},
- {{0xc4,0xbb}, {0x4c,0xcc,0xa7}},
- {{0xe1,0xb8,0xbc}, {0x4c,0xcc,0xad}},
- {{0xe1,0xb8,0xba}, {0x4c,0xcc,0xb1}},
- {{0xe1,0xb8,0xbe}, {0x4d,0xcc,0x81}},
- {{0xe1,0xb9,0x80}, {0x4d,0xcc,0x87}},
- {{0xe1,0xb9,0x82}, {0x4d,0xcc,0xa3}},
- {{0xc5,0x83}, {0x4e,0xcc,0x81}},
- {{0xc3,0x91}, {0x4e,0xcc,0x83}},
- {{0xe1,0xb9,0x84}, {0x4e,0xcc,0x87}},
- {{0xc5,0x87}, {0x4e,0xcc,0x8c}},
- {{0xe1,0xb9,0x86}, {0x4e,0xcc,0xa3}},
- {{0xc5,0x85}, {0x4e,0xcc,0xa7}},
- {{0xe1,0xb9,0x8a}, {0x4e,0xcc,0xad}},
- {{0xe1,0xb9,0x88}, {0x4e,0xcc,0xb1}},
- {{0xc3,0x92}, {0x4f,0xcc,0x80}},
- {{0xc3,0x93}, {0x4f,0xcc,0x81}},
- {{0xc3,0x94}, {0x4f,0xcc,0x82}},
- {{0xe1,0xbb,0x92}, {0x4f,0xcc,0x82,0xcc,0x80}},
- {{0xe1,0xbb,0x90}, {0x4f,0xcc,0x82,0xcc,0x81}},
- {{0xe1,0xbb,0x96}, {0x4f,0xcc,0x82,0xcc,0x83}},
- {{0xe1,0xbb,0x94}, {0x4f,0xcc,0x82,0xcc,0x89}},
- {{0xc3,0x95}, {0x4f,0xcc,0x83}},
- {{0xe1,0xb9,0x8c}, {0x4f,0xcc,0x83,0xcc,0x81}},
- {{0xe1,0xb9,0x8e}, {0x4f,0xcc,0x83,0xcc,0x88}},
- {{0xc5,0x8c}, {0x4f,0xcc,0x84}},
- {{0xe1,0xb9,0x90}, {0x4f,0xcc,0x84,0xcc,0x80}},
- {{0xe1,0xb9,0x92}, {0x4f,0xcc,0x84,0xcc,0x81}},
- {{0xc5,0x8e}, {0x4f,0xcc,0x86}},
- {{0xc3,0x96}, {0x4f,0xcc,0x88}},
- {{0xe1,0xbb,0x8e}, {0x4f,0xcc,0x89}},
- {{0xc5,0x90}, {0x4f,0xcc,0x8b}},
- {{0xc7,0x91}, {0x4f,0xcc,0x8c}},
- {{0xc8,0x8c}, {0x4f,0xcc,0x8f}},
- {{0xc8,0x8e}, {0x4f,0xcc,0x91}},
- {{0xc6,0xa0}, {0x4f,0xcc,0x9b}},
- {{0xe1,0xbb,0x9c}, {0x4f,0xcc,0x9b,0xcc,0x80}},
- {{0xe1,0xbb,0x9a}, {0x4f,0xcc,0x9b,0xcc,0x81}},
- {{0xe1,0xbb,0xa0}, {0x4f,0xcc,0x9b,0xcc,0x83}},
- {{0xe1,0xbb,0x9e}, {0x4f,0xcc,0x9b,0xcc,0x89}},
- {{0xe1,0xbb,0xa2}, {0x4f,0xcc,0x9b,0xcc,0xa3}},
- {{0xe1,0xbb,0x8c}, {0x4f,0xcc,0xa3}},
- {{0xe1,0xbb,0x98}, {0x4f,0xcc,0xa3,0xcc,0x82}},
- {{0xc7,0xaa}, {0x4f,0xcc,0xa8}},
- {{0xc7,0xac}, {0x4f,0xcc,0xa8,0xcc,0x84}},
- {{0xe1,0xb9,0x94}, {0x50,0xcc,0x81}},
- {{0xe1,0xb9,0x96}, {0x50,0xcc,0x87}},
- {{0xc5,0x94}, {0x52,0xcc,0x81}},
- {{0xe1,0xb9,0x98}, {0x52,0xcc,0x87}},
- {{0xc5,0x98}, {0x52,0xcc,0x8c}},
- {{0xc8,0x90}, {0x52,0xcc,0x8f}},
- {{0xc8,0x92}, {0x52,0xcc,0x91}},
- {{0xe1,0xb9,0x9a}, {0x52,0xcc,0xa3}},
- {{0xe1,0xb9,0x9c}, {0x52,0xcc,0xa3,0xcc,0x84}},
- {{0xc5,0x96}, {0x52,0xcc,0xa7}},
- {{0xe1,0xb9,0x9e}, {0x52,0xcc,0xb1}},
- {{0xc5,0x9a}, {0x53,0xcc,0x81}},
- {{0xe1,0xb9,0xa4}, {0x53,0xcc,0x81,0xcc,0x87}},
- {{0xc5,0x9c}, {0x53,0xcc,0x82}},
- {{0xe1,0xb9,0xa0}, {0x53,0xcc,0x87}},
- {{0xc5,0xa0}, {0x53,0xcc,0x8c}},
- {{0xe1,0xb9,0xa6}, {0x53,0xcc,0x8c,0xcc,0x87}},
- {{0xe1,0xb9,0xa2}, {0x53,0xcc,0xa3}},
- {{0xe1,0xb9,0xa8}, {0x53,0xcc,0xa3,0xcc,0x87}},
- {{0xc5,0x9e}, {0x53,0xcc,0xa7}},
- {{0xe1,0xb9,0xaa}, {0x54,0xcc,0x87}},
- {{0xc5,0xa4}, {0x54,0xcc,0x8c}},
- {{0xe1,0xb9,0xac}, {0x54,0xcc,0xa3}},
- {{0xc5,0xa2}, {0x54,0xcc,0xa7}},
- {{0xe1,0xb9,0xb0}, {0x54,0xcc,0xad}},
- {{0xe1,0xb9,0xae}, {0x54,0xcc,0xb1}},
- {{0xc3,0x99}, {0x55,0xcc,0x80}},
- {{0xc3,0x9a}, {0x55,0xcc,0x81}},
- {{0xc3,0x9b}, {0x55,0xcc,0x82}},
- {{0xc5,0xa8}, {0x55,0xcc,0x83}},
- {{0xe1,0xb9,0xb8}, {0x55,0xcc,0x83,0xcc,0x81}},
- {{0xc5,0xaa}, {0x55,0xcc,0x84}},
- {{0xe1,0xb9,0xba}, {0x55,0xcc,0x84,0xcc,0x88}},
- {{0xc5,0xac}, {0x55,0xcc,0x86}},
- {{0xc3,0x9c}, {0x55,0xcc,0x88}},
- {{0xc7,0x9b}, {0x55,0xcc,0x88,0xcc,0x80}},
- {{0xc7,0x97}, {0x55,0xcc,0x88,0xcc,0x81}},
- {{0xc7,0x95}, {0x55,0xcc,0x88,0xcc,0x84}},
- {{0xc7,0x99}, {0x55,0xcc,0x88,0xcc,0x8c}},
- {{0xe1,0xbb,0xa6}, {0x55,0xcc,0x89}},
- {{0xc5,0xae}, {0x55,0xcc,0x8a}},
- {{0xc5,0xb0}, {0x55,0xcc,0x8b}},
- {{0xc7,0x93}, {0x55,0xcc,0x8c}},
- {{0xc8,0x94}, {0x55,0xcc,0x8f}},
- {{0xc8,0x96}, {0x55,0xcc,0x91}},
- {{0xc6,0xaf}, {0x55,0xcc,0x9b}},
- {{0xe1,0xbb,0xaa}, {0x55,0xcc,0x9b,0xcc,0x80}},
- {{0xe1,0xbb,0xa8}, {0x55,0xcc,0x9b,0xcc,0x81}},
- {{0xe1,0xbb,0xae}, {0x55,0xcc,0x9b,0xcc,0x83}},
- {{0xe1,0xbb,0xac}, {0x55,0xcc,0x9b,0xcc,0x89}},
- {{0xe1,0xbb,0xb0}, {0x55,0xcc,0x9b,0xcc,0xa3}},
- {{0xe1,0xbb,0xa4}, {0x55,0xcc,0xa3}},
- {{0xe1,0xb9,0xb2}, {0x55,0xcc,0xa4}},
- {{0xc5,0xb2}, {0x55,0xcc,0xa8}},
- {{0xe1,0xb9,0xb6}, {0x55,0xcc,0xad}},
- {{0xe1,0xb9,0xb4}, {0x55,0xcc,0xb0}},
- {{0xe1,0xb9,0xbc}, {0x56,0xcc,0x83}},
- {{0xe1,0xb9,0xbe}, {0x56,0xcc,0xa3}},
- {{0xe1,0xba,0x80}, {0x57,0xcc,0x80}},
- {{0xe1,0xba,0x82}, {0x57,0xcc,0x81}},
- {{0xc5,0xb4}, {0x57,0xcc,0x82}},
- {{0xe1,0xba,0x86}, {0x57,0xcc,0x87}},
- {{0xe1,0xba,0x84}, {0x57,0xcc,0x88}},
- {{0xe1,0xba,0x88}, {0x57,0xcc,0xa3}},
- {{0xe1,0xba,0x8a}, {0x58,0xcc,0x87}},
- {{0xe1,0xba,0x8c}, {0x58,0xcc,0x88}},
- {{0xe1,0xbb,0xb2}, {0x59,0xcc,0x80}},
- {{0xc3,0x9d}, {0x59,0xcc,0x81}},
- {{0xc5,0xb6}, {0x59,0xcc,0x82}},
- {{0xe1,0xbb,0xb8}, {0x59,0xcc,0x83}},
- {{0xe1,0xba,0x8e}, {0x59,0xcc,0x87}},
- {{0xc5,0xb8}, {0x59,0xcc,0x88}},
- {{0xe1,0xbb,0xb6}, {0x59,0xcc,0x89}},
- {{0xe1,0xbb,0xb4}, {0x59,0xcc,0xa3}},
- {{0xc5,0xb9}, {0x5a,0xcc,0x81}},
- {{0xe1,0xba,0x90}, {0x5a,0xcc,0x82}},
- {{0xc5,0xbb}, {0x5a,0xcc,0x87}},
- {{0xc5,0xbd}, {0x5a,0xcc,0x8c}},
- {{0xe1,0xba,0x92}, {0x5a,0xcc,0xa3}},
- {{0xe1,0xba,0x94}, {0x5a,0xcc,0xb1}},
- {{0xe1,0xbf,0xaf}, {0x60}},
- {{0xc3,0xa0}, {0x61,0xcc,0x80}},
- {{0xc3,0xa1}, {0x61,0xcc,0x81}},
- {{0xc3,0xa2}, {0x61,0xcc,0x82}},
- {{0xe1,0xba,0xa7}, {0x61,0xcc,0x82,0xcc,0x80}},
- {{0xe1,0xba,0xa5}, {0x61,0xcc,0x82,0xcc,0x81}},
- {{0xe1,0xba,0xab}, {0x61,0xcc,0x82,0xcc,0x83}},
- {{0xe1,0xba,0xa9}, {0x61,0xcc,0x82,0xcc,0x89}},
- {{0xc3,0xa3}, {0x61,0xcc,0x83}},
- {{0xc4,0x81}, {0x61,0xcc,0x84}},
- {{0xc4,0x83}, {0x61,0xcc,0x86}},
- {{0xe1,0xba,0xb1}, {0x61,0xcc,0x86,0xcc,0x80}},
- {{0xe1,0xba,0xaf}, {0x61,0xcc,0x86,0xcc,0x81}},
- {{0xe1,0xba,0xb5}, {0x61,0xcc,0x86,0xcc,0x83}},
- {{0xe1,0xba,0xb3}, {0x61,0xcc,0x86,0xcc,0x89}},
- {{0xc7,0xa1}, {0x61,0xcc,0x87,0xcc,0x84}},
- {{0xc3,0xa4}, {0x61,0xcc,0x88}},
- {{0xc7,0x9f}, {0x61,0xcc,0x88,0xcc,0x84}},
- {{0xe1,0xba,0xa3}, {0x61,0xcc,0x89}},
- {{0xc3,0xa5}, {0x61,0xcc,0x8a}},
- {{0xc7,0xbb}, {0x61,0xcc,0x8a,0xcc,0x81}},
- {{0xc7,0x8e}, {0x61,0xcc,0x8c}},
- {{0xc8,0x81}, {0x61,0xcc,0x8f}},
- {{0xc8,0x83}, {0x61,0xcc,0x91}},
- {{0xe1,0xba,0xa1}, {0x61,0xcc,0xa3}},
- {{0xe1,0xba,0xad}, {0x61,0xcc,0xa3,0xcc,0x82}},
- {{0xe1,0xba,0xb7}, {0x61,0xcc,0xa3,0xcc,0x86}},
- {{0xe1,0xb8,0x81}, {0x61,0xcc,0xa5}},
- {{0xc4,0x85}, {0x61,0xcc,0xa8}},
- {{0xe1,0xb8,0x83}, {0x62,0xcc,0x87}},
- {{0xe1,0xb8,0x85}, {0x62,0xcc,0xa3}},
- {{0xe1,0xb8,0x87}, {0x62,0xcc,0xb1}},
- {{0xc4,0x87}, {0x63,0xcc,0x81}},
- {{0xc4,0x89}, {0x63,0xcc,0x82}},
- {{0xc4,0x8b}, {0x63,0xcc,0x87}},
- {{0xc4,0x8d}, {0x63,0xcc,0x8c}},
- {{0xc3,0xa7}, {0x63,0xcc,0xa7}},
- {{0xe1,0xb8,0x89}, {0x63,0xcc,0xa7,0xcc,0x81}},
- {{0xe1,0xb8,0x8b}, {0x64,0xcc,0x87}},
- {{0xc4,0x8f}, {0x64,0xcc,0x8c}},
- {{0xe1,0xb8,0x8d}, {0x64,0xcc,0xa3}},
- {{0xe1,0xb8,0x91}, {0x64,0xcc,0xa7}},
- {{0xe1,0xb8,0x93}, {0x64,0xcc,0xad}},
- {{0xe1,0xb8,0x8f}, {0x64,0xcc,0xb1}},
- {{0xc3,0xa8}, {0x65,0xcc,0x80}},
- {{0xc3,0xa9}, {0x65,0xcc,0x81}},
- {{0xc3,0xaa}, {0x65,0xcc,0x82}},
- {{0xe1,0xbb,0x81}, {0x65,0xcc,0x82,0xcc,0x80}},
- {{0xe1,0xba,0xbf}, {0x65,0xcc,0x82,0xcc,0x81}},
- {{0xe1,0xbb,0x85}, {0x65,0xcc,0x82,0xcc,0x83}},
- {{0xe1,0xbb,0x83}, {0x65,0xcc,0x82,0xcc,0x89}},
- {{0xe1,0xba,0xbd}, {0x65,0xcc,0x83}},
- {{0xc4,0x93}, {0x65,0xcc,0x84}},
- {{0xe1,0xb8,0x95}, {0x65,0xcc,0x84,0xcc,0x80}},
- {{0xe1,0xb8,0x97}, {0x65,0xcc,0x84,0xcc,0x81}},
- {{0xc4,0x95}, {0x65,0xcc,0x86}},
- {{0xc4,0x97}, {0x65,0xcc,0x87}},
- {{0xc3,0xab}, {0x65,0xcc,0x88}},
- {{0xe1,0xba,0xbb}, {0x65,0xcc,0x89}},
- {{0xc4,0x9b}, {0x65,0xcc,0x8c}},
- {{0xc8,0x85}, {0x65,0xcc,0x8f}},
- {{0xc8,0x87}, {0x65,0xcc,0x91}},
- {{0xe1,0xba,0xb9}, {0x65,0xcc,0xa3}},
- {{0xe1,0xbb,0x87}, {0x65,0xcc,0xa3,0xcc,0x82}},
- {{0xe1,0xb8,0x9d}, {0x65,0xcc,0xa7,0xcc,0x86}},
- {{0xc4,0x99}, {0x65,0xcc,0xa8}},
- {{0xe1,0xb8,0x99}, {0x65,0xcc,0xad}},
- {{0xe1,0xb8,0x9b}, {0x65,0xcc,0xb0}},
- {{0xe1,0xb8,0x9f}, {0x66,0xcc,0x87}},
- {{0xc7,0xb5}, {0x67,0xcc,0x81}},
- {{0xc4,0x9d}, {0x67,0xcc,0x82}},
- {{0xe1,0xb8,0xa1}, {0x67,0xcc,0x84}},
- {{0xc4,0x9f}, {0x67,0xcc,0x86}},
- {{0xc4,0xa1}, {0x67,0xcc,0x87}},
- {{0xc7,0xa7}, {0x67,0xcc,0x8c}},
- {{0xc4,0xa3}, {0x67,0xcc,0xa7}},
- {{0xc4,0xa5}, {0x68,0xcc,0x82}},
- {{0xe1,0xb8,0xa3}, {0x68,0xcc,0x87}},
- {{0xe1,0xb8,0xa7}, {0x68,0xcc,0x88}},
- {{0xe1,0xb8,0xa5}, {0x68,0xcc,0xa3}},
- {{0xe1,0xb8,0xa9}, {0x68,0xcc,0xa7}},
- {{0xe1,0xb8,0xab}, {0x68,0xcc,0xae}},
- {{0xe1,0xba,0x96}, {0x68,0xcc,0xb1}},
- {{0xc3,0xac}, {0x69,0xcc,0x80}},
- {{0xc3,0xad}, {0x69,0xcc,0x81}},
- {{0xc3,0xae}, {0x69,0xcc,0x82}},
- {{0xc4,0xa9}, {0x69,0xcc,0x83}},
- {{0xc4,0xab}, {0x69,0xcc,0x84}},
- {{0xc4,0xad}, {0x69,0xcc,0x86}},
- {{0xc3,0xaf}, {0x69,0xcc,0x88}},
- {{0xe1,0xb8,0xaf}, {0x69,0xcc,0x88,0xcc,0x81}},
- {{0xe1,0xbb,0x89}, {0x69,0xcc,0x89}},
- {{0xc7,0x90}, {0x69,0xcc,0x8c}},
- {{0xc8,0x89}, {0x69,0xcc,0x8f}},
- {{0xc8,0x8b}, {0x69,0xcc,0x91}},
- {{0xe1,0xbb,0x8b}, {0x69,0xcc,0xa3}},
- {{0xc4,0xaf}, {0x69,0xcc,0xa8}},
- {{0xe1,0xb8,0xad}, {0x69,0xcc,0xb0}},
- {{0xc4,0xb5}, {0x6a,0xcc,0x82}},
- {{0xc7,0xb0}, {0x6a,0xcc,0x8c}},
- {{0xe1,0xb8,0xb1}, {0x6b,0xcc,0x81}},
- {{0xc7,0xa9}, {0x6b,0xcc,0x8c}},
- {{0xe1,0xb8,0xb3}, {0x6b,0xcc,0xa3}},
- {{0xc4,0xb7}, {0x6b,0xcc,0xa7}},
- {{0xe1,0xb8,0xb5}, {0x6b,0xcc,0xb1}},
- {{0xc4,0xba}, {0x6c,0xcc,0x81}},
- {{0xc4,0xbe}, {0x6c,0xcc,0x8c}},
- {{0xe1,0xb8,0xb7}, {0x6c,0xcc,0xa3}},
- {{0xe1,0xb8,0xb9}, {0x6c,0xcc,0xa3,0xcc,0x84}},
- {{0xc4,0xbc}, {0x6c,0xcc,0xa7}},
- {{0xe1,0xb8,0xbd}, {0x6c,0xcc,0xad}},
- {{0xe1,0xb8,0xbb}, {0x6c,0xcc,0xb1}},
- {{0xe1,0xb8,0xbf}, {0x6d,0xcc,0x81}},
- {{0xe1,0xb9,0x81}, {0x6d,0xcc,0x87}},
- {{0xe1,0xb9,0x83}, {0x6d,0xcc,0xa3}},
- {{0xc5,0x84}, {0x6e,0xcc,0x81}},
- {{0xc3,0xb1}, {0x6e,0xcc,0x83}},
- {{0xe1,0xb9,0x85}, {0x6e,0xcc,0x87}},
- {{0xc5,0x88}, {0x6e,0xcc,0x8c}},
- {{0xe1,0xb9,0x87}, {0x6e,0xcc,0xa3}},
- {{0xc5,0x86}, {0x6e,0xcc,0xa7}},
- {{0xe1,0xb9,0x8b}, {0x6e,0xcc,0xad}},
- {{0xe1,0xb9,0x89}, {0x6e,0xcc,0xb1}},
- {{0xc3,0xb2}, {0x6f,0xcc,0x80}},
- {{0xc3,0xb3}, {0x6f,0xcc,0x81}},
- {{0xc3,0xb4}, {0x6f,0xcc,0x82}},
- {{0xe1,0xbb,0x93}, {0x6f,0xcc,0x82,0xcc,0x80}},
- {{0xe1,0xbb,0x91}, {0x6f,0xcc,0x82,0xcc,0x81}},
- {{0xe1,0xbb,0x97}, {0x6f,0xcc,0x82,0xcc,0x83}},
- {{0xe1,0xbb,0x95}, {0x6f,0xcc,0x82,0xcc,0x89}},
- {{0xc3,0xb5}, {0x6f,0xcc,0x83}},
- {{0xe1,0xb9,0x8d}, {0x6f,0xcc,0x83,0xcc,0x81}},
- {{0xe1,0xb9,0x8f}, {0x6f,0xcc,0x83,0xcc,0x88}},
- {{0xc5,0x8d}, {0x6f,0xcc,0x84}},
- {{0xe1,0xb9,0x91}, {0x6f,0xcc,0x84,0xcc,0x80}},
- {{0xe1,0xb9,0x93}, {0x6f,0xcc,0x84,0xcc,0x81}},
- {{0xc5,0x8f}, {0x6f,0xcc,0x86}},
- {{0xc3,0xb6}, {0x6f,0xcc,0x88}},
- {{0xe1,0xbb,0x8f}, {0x6f,0xcc,0x89}},
- {{0xc5,0x91}, {0x6f,0xcc,0x8b}},
- {{0xc7,0x92}, {0x6f,0xcc,0x8c}},
- {{0xc8,0x8d}, {0x6f,0xcc,0x8f}},
- {{0xc8,0x8f}, {0x6f,0xcc,0x91}},
- {{0xc6,0xa1}, {0x6f,0xcc,0x9b}},
- {{0xe1,0xbb,0x9d}, {0x6f,0xcc,0x9b,0xcc,0x80}},
- {{0xe1,0xbb,0x9b}, {0x6f,0xcc,0x9b,0xcc,0x81}},
- {{0xe1,0xbb,0xa1}, {0x6f,0xcc,0x9b,0xcc,0x83}},
- {{0xe1,0xbb,0x9f}, {0x6f,0xcc,0x9b,0xcc,0x89}},
- {{0xe1,0xbb,0xa3}, {0x6f,0xcc,0x9b,0xcc,0xa3}},
- {{0xe1,0xbb,0x8d}, {0x6f,0xcc,0xa3}},
- {{0xe1,0xbb,0x99}, {0x6f,0xcc,0xa3,0xcc,0x82}},
- {{0xc7,0xab}, {0x6f,0xcc,0xa8}},
- {{0xc7,0xad}, {0x6f,0xcc,0xa8,0xcc,0x84}},
- {{0xe1,0xb9,0x95}, {0x70,0xcc,0x81}},
- {{0xe1,0xb9,0x97}, {0x70,0xcc,0x87}},
- {{0xc5,0x95}, {0x72,0xcc,0x81}},
- {{0xe1,0xb9,0x99}, {0x72,0xcc,0x87}},
- {{0xc5,0x99}, {0x72,0xcc,0x8c}},
- {{0xc8,0x91}, {0x72,0xcc,0x8f}},
- {{0xc8,0x93}, {0x72,0xcc,0x91}},
- {{0xe1,0xb9,0x9b}, {0x72,0xcc,0xa3}},
- {{0xe1,0xb9,0x9d}, {0x72,0xcc,0xa3,0xcc,0x84}},
- {{0xc5,0x97}, {0x72,0xcc,0xa7}},
- {{0xe1,0xb9,0x9f}, {0x72,0xcc,0xb1}},
- {{0xc5,0x9b}, {0x73,0xcc,0x81}},
- {{0xe1,0xb9,0xa5}, {0x73,0xcc,0x81,0xcc,0x87}},
- {{0xc5,0x9d}, {0x73,0xcc,0x82}},
- {{0xe1,0xb9,0xa1}, {0x73,0xcc,0x87}},
- {{0xc5,0xa1}, {0x73,0xcc,0x8c}},
- {{0xe1,0xb9,0xa7}, {0x73,0xcc,0x8c,0xcc,0x87}},
- {{0xe1,0xb9,0xa3}, {0x73,0xcc,0xa3}},
- {{0xe1,0xb9,0xa9}, {0x73,0xcc,0xa3,0xcc,0x87}},
- {{0xc5,0x9f}, {0x73,0xcc,0xa7}},
- {{0xe1,0xb9,0xab}, {0x74,0xcc,0x87}},
- {{0xe1,0xba,0x97}, {0x74,0xcc,0x88}},
- {{0xc5,0xa5}, {0x74,0xcc,0x8c}},
- {{0xe1,0xb9,0xad}, {0x74,0xcc,0xa3}},
- {{0xc5,0xa3}, {0x74,0xcc,0xa7}},
- {{0xe1,0xb9,0xb1}, {0x74,0xcc,0xad}},
- {{0xe1,0xb9,0xaf}, {0x74,0xcc,0xb1}},
- {{0xc3,0xb9}, {0x75,0xcc,0x80}},
- {{0xc3,0xba}, {0x75,0xcc,0x81}},
- {{0xc3,0xbb}, {0x75,0xcc,0x82}},
- {{0xc5,0xa9}, {0x75,0xcc,0x83}},
- {{0xe1,0xb9,0xb9}, {0x75,0xcc,0x83,0xcc,0x81}},
- {{0xc5,0xab}, {0x75,0xcc,0x84}},
- {{0xe1,0xb9,0xbb}, {0x75,0xcc,0x84,0xcc,0x88}},
- {{0xc5,0xad}, {0x75,0xcc,0x86}},
- {{0xc3,0xbc}, {0x75,0xcc,0x88}},
- {{0xc7,0x9c}, {0x75,0xcc,0x88,0xcc,0x80}},
- {{0xc7,0x98}, {0x75,0xcc,0x88,0xcc,0x81}},
- {{0xc7,0x96}, {0x75,0xcc,0x88,0xcc,0x84}},
- {{0xc7,0x9a}, {0x75,0xcc,0x88,0xcc,0x8c}},
- {{0xe1,0xbb,0xa7}, {0x75,0xcc,0x89}},
- {{0xc5,0xaf}, {0x75,0xcc,0x8a}},
- {{0xc5,0xb1}, {0x75,0xcc,0x8b}},
- {{0xc7,0x94}, {0x75,0xcc,0x8c}},
- {{0xc8,0x95}, {0x75,0xcc,0x8f}},
- {{0xc8,0x97}, {0x75,0xcc,0x91}},
- {{0xc6,0xb0}, {0x75,0xcc,0x9b}},
- {{0xe1,0xbb,0xab}, {0x75,0xcc,0x9b,0xcc,0x80}},
- {{0xe1,0xbb,0xa9}, {0x75,0xcc,0x9b,0xcc,0x81}},
- {{0xe1,0xbb,0xaf}, {0x75,0xcc,0x9b,0xcc,0x83}},
- {{0xe1,0xbb,0xad}, {0x75,0xcc,0x9b,0xcc,0x89}},
- {{0xe1,0xbb,0xb1}, {0x75,0xcc,0x9b,0xcc,0xa3}},
- {{0xe1,0xbb,0xa5}, {0x75,0xcc,0xa3}},
- {{0xe1,0xb9,0xb3}, {0x75,0xcc,0xa4}},
- {{0xc5,0xb3}, {0x75,0xcc,0xa8}},
- {{0xe1,0xb9,0xb7}, {0x75,0xcc,0xad}},
- {{0xe1,0xb9,0xb5}, {0x75,0xcc,0xb0}},
- {{0xe1,0xb9,0xbd}, {0x76,0xcc,0x83}},
- {{0xe1,0xb9,0xbf}, {0x76,0xcc,0xa3}},
- {{0xe1,0xba,0x81}, {0x77,0xcc,0x80}},
- {{0xe1,0xba,0x83}, {0x77,0xcc,0x81}},
- {{0xc5,0xb5}, {0x77,0xcc,0x82}},
- {{0xe1,0xba,0x87}, {0x77,0xcc,0x87}},
- {{0xe1,0xba,0x85}, {0x77,0xcc,0x88}},
- {{0xe1,0xba,0x98}, {0x77,0xcc,0x8a}},
- {{0xe1,0xba,0x89}, {0x77,0xcc,0xa3}},
- {{0xe1,0xba,0x8b}, {0x78,0xcc,0x87}},
- {{0xe1,0xba,0x8d}, {0x78,0xcc,0x88}},
- {{0xe1,0xbb,0xb3}, {0x79,0xcc,0x80}},
- {{0xc3,0xbd}, {0x79,0xcc,0x81}},
- {{0xc5,0xb7}, {0x79,0xcc,0x82}},
- {{0xe1,0xbb,0xb9}, {0x79,0xcc,0x83}},
- {{0xe1,0xba,0x8f}, {0x79,0xcc,0x87}},
- {{0xc3,0xbf}, {0x79,0xcc,0x88}},
- {{0xe1,0xbb,0xb7}, {0x79,0xcc,0x89}},
- {{0xe1,0xba,0x99}, {0x79,0xcc,0x8a}},
- {{0xe1,0xbb,0xb5}, {0x79,0xcc,0xa3}},
- {{0xc5,0xba}, {0x7a,0xcc,0x81}},
- {{0xe1,0xba,0x91}, {0x7a,0xcc,0x82}},
- {{0xc5,0xbc}, {0x7a,0xcc,0x87}},
- {{0xc5,0xbe}, {0x7a,0xcc,0x8c}},
- {{0xe1,0xba,0x93}, {0x7a,0xcc,0xa3}},
- {{0xe1,0xba,0x95}, {0x7a,0xcc,0xb1}},
- {{0xe1,0xbf,0xad}, {0xc2,0xa8,0xcc,0x80}},
- {{0xe1,0xbf,0xae}, {0xc2,0xa8,0xcc,0x81}},
- {{0xce,0x85}, {0xc2,0xa8,0xcc,0x8d}},
- {{0xe1,0xbf,0x81}, {0xc2,0xa8,0xcd,0x82}},
- {{0xe1,0xbf,0xbd}, {0xc2,0xb4}},
- {{0xce,0x87}, {0xc2,0xb7}},
- {{0xd3,0x94}, {0xc3,0x86}},
- {{0xc7,0xbc}, {0xc3,0x86,0xcc,0x81}},
- {{0xc7,0xa2}, {0xc3,0x86,0xcc,0x84}},
- {{0xc7,0xbe}, {0xc3,0x98,0xcc,0x81}},
- {{0xd3,0x95}, {0xc3,0xa6}},
- {{0xc7,0xbd}, {0xc3,0xa6,0xcc,0x81}},
- {{0xc7,0xa3}, {0xc3,0xa6,0xcc,0x84}},
- {{0xc7,0xbf}, {0xc3,0xb8,0xcc,0x81}},
- {{0xe1,0xba,0x9b}, {0xc5,0xbf,0xcc,0x87}},
- {{0xd3,0x98}, {0xc6,0x8f}},
- {{0xd3,0x9a}, {0xc6,0x8f,0xcc,0x88}},
- {{0xd3,0xa8}, {0xc6,0x9f}},
- {{0xd3,0xaa}, {0xc6,0x9f,0xcc,0x88}},
- {{0xd3,0xa0}, {0xc6,0xb7}},
- {{0xc7,0xae}, {0xc6,0xb7,0xcc,0x8c}},
- {{0xd3,0x99}, {0xc9,0x99}},
- {{0xd3,0x9b}, {0xc9,0x99,0xcc,0x88}},
- {{0xd3,0xa9}, {0xc9,0xb5}},
- {{0xd3,0xab}, {0xc9,0xb5,0xcc,0x88}},
- {{0xd3,0xa1}, {0xca,0x92}},
- {{0xc7,0xaf}, {0xca,0x92,0xcc,0x8c}},
- {{0xcd,0xb4}, {0xca,0xb9}},
- {{0xcd,0x80}, {0xcc,0x80}},
- {{0xcd,0x81}, {0xcc,0x81}},
- {{0xcc,0x90}, {0xcc,0x86,0xcc,0x87}},
- {{0xcd,0x84}, {0xcc,0x88,0xcc,0x8d}},
- {{0xcd,0x83}, {0xcc,0x93}},
- {{0xe1,0xbe,0xba}, {0xce,0x91,0xcc,0x80}},
- {{0xe1,0xbe,0xbb}, {0xce,0x91,0xcc,0x81}},
- {{0xe1,0xbe,0xb9}, {0xce,0x91,0xcc,0x84}},
- {{0xe1,0xbe,0xb8}, {0xce,0x91,0xcc,0x86}},
- {{0xce,0x86}, {0xce,0x91,0xcc,0x8d}},
- {{0xe1,0xbc,0x88}, {0xce,0x91,0xcc,0x93}},
- {{0xe1,0xbc,0x8a}, {0xce,0x91,0xcc,0x93,0xcc,0x80}},
- {{0xe1,0xbc,0x8c}, {0xce,0x91,0xcc,0x93,0xcc,0x81}},
- {{0xe1,0xbc,0x8e}, {0xce,0x91,0xcc,0x93,0xcd,0x82}},
- {{0xe1,0xbc,0x89}, {0xce,0x91,0xcc,0x94}},
- {{0xe1,0xbc,0x8b}, {0xce,0x91,0xcc,0x94,0xcc,0x80}},
- {{0xe1,0xbc,0x8d}, {0xce,0x91,0xcc,0x94,0xcc,0x81}},
- {{0xe1,0xbc,0x8f}, {0xce,0x91,0xcc,0x94,0xcd,0x82}},
- {{0xe1,0xbe,0xbc}, {0xce,0x91,0xcd,0x85}},
- {{0xe1,0xbe,0x88}, {0xce,0x91,0xcd,0x85,0xcc,0x93}},
- {{0xe1,0xbe,0x8a}, {0xce,0x91,0xcd,0x85,0xcc,0x93,0xcc,0x80}},
- {{0xe1,0xbe,0x8c}, {0xce,0x91,0xcd,0x85,0xcc,0x93,0xcc,0x81}},
- {{0xe1,0xbe,0x8e}, {0xce,0x91,0xcd,0x85,0xcc,0x93,0xcd,0x82}},
- {{0xe1,0xbe,0x89}, {0xce,0x91,0xcd,0x85,0xcc,0x94}},
- {{0xe1,0xbe,0x8b}, {0xce,0x91,0xcd,0x85,0xcc,0x94,0xcc,0x80}},
- {{0xe1,0xbe,0x8d}, {0xce,0x91,0xcd,0x85,0xcc,0x94,0xcc,0x81}},
- {{0xe1,0xbe,0x8f}, {0xce,0x91,0xcd,0x85,0xcc,0x94,0xcd,0x82}},
- {{0xe1,0xbf,0x88}, {0xce,0x95,0xcc,0x80}},
- {{0xe1,0xbf,0x89}, {0xce,0x95,0xcc,0x81}},
- {{0xce,0x88}, {0xce,0x95,0xcc,0x8d}},
- {{0xe1,0xbc,0x98}, {0xce,0x95,0xcc,0x93}},
- {{0xe1,0xbc,0x9a}, {0xce,0x95,0xcc,0x93,0xcc,0x80}},
- {{0xe1,0xbc,0x9c}, {0xce,0x95,0xcc,0x93,0xcc,0x81}},
- {{0xe1,0xbc,0x99}, {0xce,0x95,0xcc,0x94}},
- {{0xe1,0xbc,0x9b}, {0xce,0x95,0xcc,0x94,0xcc,0x80}},
- {{0xe1,0xbc,0x9d}, {0xce,0x95,0xcc,0x94,0xcc,0x81}},
- {{0xe1,0xbf,0x8a}, {0xce,0x97,0xcc,0x80}},
- {{0xe1,0xbf,0x8b}, {0xce,0x97,0xcc,0x81}},
- {{0xce,0x89}, {0xce,0x97,0xcc,0x8d}},
- {{0xe1,0xbc,0xa8}, {0xce,0x97,0xcc,0x93}},
- {{0xe1,0xbc,0xaa}, {0xce,0x97,0xcc,0x93,0xcc,0x80}},
- {{0xe1,0xbc,0xac}, {0xce,0x97,0xcc,0x93,0xcc,0x81}},
- {{0xe1,0xbc,0xae}, {0xce,0x97,0xcc,0x93,0xcd,0x82}},
- {{0xe1,0xbc,0xa9}, {0xce,0x97,0xcc,0x94}},
- {{0xe1,0xbc,0xab}, {0xce,0x97,0xcc,0x94,0xcc,0x80}},
- {{0xe1,0xbc,0xad}, {0xce,0x97,0xcc,0x94,0xcc,0x81}},
- {{0xe1,0xbc,0xaf}, {0xce,0x97,0xcc,0x94,0xcd,0x82}},
- {{0xe1,0xbf,0x8c}, {0xce,0x97,0xcd,0x85}},
- {{0xe1,0xbe,0x98}, {0xce,0x97,0xcd,0x85,0xcc,0x93}},
- {{0xe1,0xbe,0x9a}, {0xce,0x97,0xcd,0x85,0xcc,0x93,0xcc,0x80}},
- {{0xe1,0xbe,0x9c}, {0xce,0x97,0xcd,0x85,0xcc,0x93,0xcc,0x81}},
- {{0xe1,0xbe,0x9e}, {0xce,0x97,0xcd,0x85,0xcc,0x93,0xcd,0x82}},
- {{0xe1,0xbe,0x99}, {0xce,0x97,0xcd,0x85,0xcc,0x94}},
- {{0xe1,0xbe,0x9b}, {0xce,0x97,0xcd,0x85,0xcc,0x94,0xcc,0x80}},
- {{0xe1,0xbe,0x9d}, {0xce,0x97,0xcd,0x85,0xcc,0x94,0xcc,0x81}},
- {{0xe1,0xbe,0x9f}, {0xce,0x97,0xcd,0x85,0xcc,0x94,0xcd,0x82}},
- {{0xe1,0xbf,0x9a}, {0xce,0x99,0xcc,0x80}},
- {{0xe1,0xbf,0x9b}, {0xce,0x99,0xcc,0x81}},
- {{0xe1,0xbf,0x99}, {0xce,0x99,0xcc,0x84}},
- {{0xe1,0xbf,0x98}, {0xce,0x99,0xcc,0x86}},
- {{0xce,0xaa}, {0xce,0x99,0xcc,0x88}},
- {{0xce,0x8a}, {0xce,0x99,0xcc,0x8d}},
- {{0xe1,0xbc,0xb8}, {0xce,0x99,0xcc,0x93}},
- {{0xe1,0xbc,0xba}, {0xce,0x99,0xcc,0x93,0xcc,0x80}},
- {{0xe1,0xbc,0xbc}, {0xce,0x99,0xcc,0x93,0xcc,0x81}},
- {{0xe1,0xbc,0xbe}, {0xce,0x99,0xcc,0x93,0xcd,0x82}},
- {{0xe1,0xbc,0xb9}, {0xce,0x99,0xcc,0x94}},
- {{0xe1,0xbc,0xbb}, {0xce,0x99,0xcc,0x94,0xcc,0x80}},
- {{0xe1,0xbc,0xbd}, {0xce,0x99,0xcc,0x94,0xcc,0x81}},
- {{0xe1,0xbc,0xbf}, {0xce,0x99,0xcc,0x94,0xcd,0x82}},
- {{0xe1,0xbf,0xb8}, {0xce,0x9f,0xcc,0x80}},
- {{0xe1,0xbf,0xb9}, {0xce,0x9f,0xcc,0x81}},
- {{0xce,0x8c}, {0xce,0x9f,0xcc,0x8d}},
- {{0xe1,0xbd,0x88}, {0xce,0x9f,0xcc,0x93}},
- {{0xe1,0xbd,0x8a}, {0xce,0x9f,0xcc,0x93,0xcc,0x80}},
- {{0xe1,0xbd,0x8c}, {0xce,0x9f,0xcc,0x93,0xcc,0x81}},
- {{0xe1,0xbd,0x89}, {0xce,0x9f,0xcc,0x94}},
- {{0xe1,0xbd,0x8b}, {0xce,0x9f,0xcc,0x94,0xcc,0x80}},
- {{0xe1,0xbd,0x8d}, {0xce,0x9f,0xcc,0x94,0xcc,0x81}},
- {{0xe1,0xbf,0xac}, {0xce,0xa1,0xcc,0x94}},
- {{0xe1,0xbf,0xaa}, {0xce,0xa5,0xcc,0x80}},
- {{0xe1,0xbf,0xab}, {0xce,0xa5,0xcc,0x81}},
- {{0xe1,0xbf,0xa9}, {0xce,0xa5,0xcc,0x84}},
- {{0xe1,0xbf,0xa8}, {0xce,0xa5,0xcc,0x86}},
- {{0xce,0xab}, {0xce,0xa5,0xcc,0x88}},
- {{0xce,0x8e}, {0xce,0xa5,0xcc,0x8d}},
- {{0xe1,0xbd,0x99}, {0xce,0xa5,0xcc,0x94}},
- {{0xe1,0xbd,0x9b}, {0xce,0xa5,0xcc,0x94,0xcc,0x80}},
- {{0xe1,0xbd,0x9d}, {0xce,0xa5,0xcc,0x94,0xcc,0x81}},
- {{0xe1,0xbd,0x9f}, {0xce,0xa5,0xcc,0x94,0xcd,0x82}},
- {{0xe1,0xbf,0xba}, {0xce,0xa9,0xcc,0x80}},
- {{0xe1,0xbf,0xbb}, {0xce,0xa9,0xcc,0x81}},
- {{0xce,0x8f}, {0xce,0xa9,0xcc,0x8d}},
- {{0xe1,0xbd,0xa8}, {0xce,0xa9,0xcc,0x93}},
- {{0xe1,0xbd,0xaa}, {0xce,0xa9,0xcc,0x93,0xcc,0x80}},
- {{0xe1,0xbd,0xac}, {0xce,0xa9,0xcc,0x93,0xcc,0x81}},
- {{0xe1,0xbd,0xae}, {0xce,0xa9,0xcc,0x93,0xcd,0x82}},
- {{0xe1,0xbd,0xa9}, {0xce,0xa9,0xcc,0x94}},
- {{0xe1,0xbd,0xab}, {0xce,0xa9,0xcc,0x94,0xcc,0x80}},
- {{0xe1,0xbd,0xad}, {0xce,0xa9,0xcc,0x94,0xcc,0x81}},
- {{0xe1,0xbd,0xaf}, {0xce,0xa9,0xcc,0x94,0xcd,0x82}},
- {{0xe1,0xbf,0xbc}, {0xce,0xa9,0xcd,0x85}},
- {{0xe1,0xbe,0xa8}, {0xce,0xa9,0xcd,0x85,0xcc,0x93}},
- {{0xe1,0xbe,0xaa}, {0xce,0xa9,0xcd,0x85,0xcc,0x93,0xcc,0x80}},
- {{0xe1,0xbe,0xac}, {0xce,0xa9,0xcd,0x85,0xcc,0x93,0xcc,0x81}},
- {{0xe1,0xbe,0xae}, {0xce,0xa9,0xcd,0x85,0xcc,0x93,0xcd,0x82}},
- {{0xe1,0xbe,0xa9}, {0xce,0xa9,0xcd,0x85,0xcc,0x94}},
- {{0xe1,0xbe,0xab}, {0xce,0xa9,0xcd,0x85,0xcc,0x94,0xcc,0x80}},
- {{0xe1,0xbe,0xad}, {0xce,0xa9,0xcd,0x85,0xcc,0x94,0xcc,0x81}},
- {{0xe1,0xbe,0xaf}, {0xce,0xa9,0xcd,0x85,0xcc,0x94,0xcd,0x82}},
- {{0xe1,0xbd,0xb0}, {0xce,0xb1,0xcc,0x80}},
- {{0xe1,0xbd,0xb1}, {0xce,0xb1,0xcc,0x81}},
- {{0xe1,0xbe,0xb1}, {0xce,0xb1,0xcc,0x84}},
- {{0xe1,0xbe,0xb0}, {0xce,0xb1,0xcc,0x86}},
- {{0xce,0xac}, {0xce,0xb1,0xcc,0x8d}},
- {{0xe1,0xbc,0x80}, {0xce,0xb1,0xcc,0x93}},
- {{0xe1,0xbc,0x82}, {0xce,0xb1,0xcc,0x93,0xcc,0x80}},
- {{0xe1,0xbc,0x84}, {0xce,0xb1,0xcc,0x93,0xcc,0x81}},
- {{0xe1,0xbc,0x86}, {0xce,0xb1,0xcc,0x93,0xcd,0x82}},
- {{0xe1,0xbc,0x81}, {0xce,0xb1,0xcc,0x94}},
- {{0xe1,0xbc,0x83}, {0xce,0xb1,0xcc,0x94,0xcc,0x80}},
- {{0xe1,0xbc,0x85}, {0xce,0xb1,0xcc,0x94,0xcc,0x81}},
- {{0xe1,0xbc,0x87}, {0xce,0xb1,0xcc,0x94,0xcd,0x82}},
- {{0xe1,0xbe,0xb6}, {0xce,0xb1,0xcd,0x82}},
- {{0xe1,0xbe,0xb3}, {0xce,0xb1,0xcd,0x85}},
- {{0xe1,0xbe,0xb2}, {0xce,0xb1,0xcd,0x85,0xcc,0x80}},
- {{0xe1,0xbe,0xb4}, {0xce,0xb1,0xcd,0x85,0xcc,0x81}},
- {{0xe1,0xbe,0x80}, {0xce,0xb1,0xcd,0x85,0xcc,0x93}},
- {{0xe1,0xbe,0x82}, {0xce,0xb1,0xcd,0x85,0xcc,0x93,0xcc,0x80}},
- {{0xe1,0xbe,0x84}, {0xce,0xb1,0xcd,0x85,0xcc,0x93,0xcc,0x81}},
- {{0xe1,0xbe,0x86}, {0xce,0xb1,0xcd,0x85,0xcc,0x93,0xcd,0x82}},
- {{0xe1,0xbe,0x81}, {0xce,0xb1,0xcd,0x85,0xcc,0x94}},
- {{0xe1,0xbe,0x83}, {0xce,0xb1,0xcd,0x85,0xcc,0x94,0xcc,0x80}},
- {{0xe1,0xbe,0x85}, {0xce,0xb1,0xcd,0x85,0xcc,0x94,0xcc,0x81}},
- {{0xe1,0xbe,0x87}, {0xce,0xb1,0xcd,0x85,0xcc,0x94,0xcd,0x82}},
- {{0xe1,0xbe,0xb7}, {0xce,0xb1,0xcd,0x85,0xcd,0x82}},
- {{0xe1,0xbd,0xb2}, {0xce,0xb5,0xcc,0x80}},
- {{0xe1,0xbd,0xb3}, {0xce,0xb5,0xcc,0x81}},
- {{0xce,0xad}, {0xce,0xb5,0xcc,0x8d}},
- {{0xe1,0xbc,0x90}, {0xce,0xb5,0xcc,0x93}},
- {{0xe1,0xbc,0x92}, {0xce,0xb5,0xcc,0x93,0xcc,0x80}},
- {{0xe1,0xbc,0x94}, {0xce,0xb5,0xcc,0x93,0xcc,0x81}},
- {{0xe1,0xbc,0x91}, {0xce,0xb5,0xcc,0x94}},
- {{0xe1,0xbc,0x93}, {0xce,0xb5,0xcc,0x94,0xcc,0x80}},
- {{0xe1,0xbc,0x95}, {0xce,0xb5,0xcc,0x94,0xcc,0x81}},
- {{0xe1,0xbd,0xb4}, {0xce,0xb7,0xcc,0x80}},
- {{0xe1,0xbd,0xb5}, {0xce,0xb7,0xcc,0x81}},
- {{0xce,0xae}, {0xce,0xb7,0xcc,0x8d}},
- {{0xe1,0xbc,0xa0}, {0xce,0xb7,0xcc,0x93}},
- {{0xe1,0xbc,0xa2}, {0xce,0xb7,0xcc,0x93,0xcc,0x80}},
- {{0xe1,0xbc,0xa4}, {0xce,0xb7,0xcc,0x93,0xcc,0x81}},
- {{0xe1,0xbc,0xa6}, {0xce,0xb7,0xcc,0x93,0xcd,0x82}},
- {{0xe1,0xbc,0xa1}, {0xce,0xb7,0xcc,0x94}},
- {{0xe1,0xbc,0xa3}, {0xce,0xb7,0xcc,0x94,0xcc,0x80}},
- {{0xe1,0xbc,0xa5}, {0xce,0xb7,0xcc,0x94,0xcc,0x81}},
- {{0xe1,0xbc,0xa7}, {0xce,0xb7,0xcc,0x94,0xcd,0x82}},
- {{0xe1,0xbf,0x86}, {0xce,0xb7,0xcd,0x82}},
- {{0xe1,0xbf,0x83}, {0xce,0xb7,0xcd,0x85}},
- {{0xe1,0xbf,0x82}, {0xce,0xb7,0xcd,0x85,0xcc,0x80}},
- {{0xe1,0xbf,0x84}, {0xce,0xb7,0xcd,0x85,0xcc,0x81}},
- {{0xe1,0xbe,0x90}, {0xce,0xb7,0xcd,0x85,0xcc,0x93}},
- {{0xe1,0xbe,0x92}, {0xce,0xb7,0xcd,0x85,0xcc,0x93,0xcc,0x80}},
- {{0xe1,0xbe,0x94}, {0xce,0xb7,0xcd,0x85,0xcc,0x93,0xcc,0x81}},
- {{0xe1,0xbe,0x96}, {0xce,0xb7,0xcd,0x85,0xcc,0x93,0xcd,0x82}},
- {{0xe1,0xbe,0x91}, {0xce,0xb7,0xcd,0x85,0xcc,0x94}},
- {{0xe1,0xbe,0x93}, {0xce,0xb7,0xcd,0x85,0xcc,0x94,0xcc,0x80}},
- {{0xe1,0xbe,0x95}, {0xce,0xb7,0xcd,0x85,0xcc,0x94,0xcc,0x81}},
- {{0xe1,0xbe,0x97}, {0xce,0xb7,0xcd,0x85,0xcc,0x94,0xcd,0x82}},
- {{0xe1,0xbf,0x87}, {0xce,0xb7,0xcd,0x85,0xcd,0x82}},
- {{0xe1,0xbe,0xbe}, {0xce,0xb9}},
- {{0xe1,0xbd,0xb6}, {0xce,0xb9,0xcc,0x80}},
- {{0xe1,0xbd,0xb7}, {0xce,0xb9,0xcc,0x81}},
- {{0xe1,0xbf,0x91}, {0xce,0xb9,0xcc,0x84}},
- {{0xe1,0xbf,0x90}, {0xce,0xb9,0xcc,0x86}},
- {{0xcf,0x8a}, {0xce,0xb9,0xcc,0x88}},
- {{0xe1,0xbf,0x92}, {0xce,0xb9,0xcc,0x88,0xcc,0x80}},
- {{0xe1,0xbf,0x93}, {0xce,0xb9,0xcc,0x88,0xcc,0x81}},
- {{0xce,0x90}, {0xce,0xb9,0xcc,0x88,0xcc,0x8d}},
- {{0xe1,0xbf,0x97}, {0xce,0xb9,0xcc,0x88,0xcd,0x82}},
- {{0xce,0xaf}, {0xce,0xb9,0xcc,0x8d}},
- {{0xe1,0xbc,0xb0}, {0xce,0xb9,0xcc,0x93}},
- {{0xe1,0xbc,0xb2}, {0xce,0xb9,0xcc,0x93,0xcc,0x80}},
- {{0xe1,0xbc,0xb4}, {0xce,0xb9,0xcc,0x93,0xcc,0x81}},
- {{0xe1,0xbc,0xb6}, {0xce,0xb9,0xcc,0x93,0xcd,0x82}},
- {{0xe1,0xbc,0xb1}, {0xce,0xb9,0xcc,0x94}},
- {{0xe1,0xbc,0xb3}, {0xce,0xb9,0xcc,0x94,0xcc,0x80}},
- {{0xe1,0xbc,0xb5}, {0xce,0xb9,0xcc,0x94,0xcc,0x81}},
- {{0xe1,0xbc,0xb7}, {0xce,0xb9,0xcc,0x94,0xcd,0x82}},
- {{0xe1,0xbf,0x96}, {0xce,0xb9,0xcd,0x82}},
- {{0xe1,0xbd,0xb8}, {0xce,0xbf,0xcc,0x80}},
- {{0xe1,0xbd,0xb9}, {0xce,0xbf,0xcc,0x81}},
- {{0xcf,0x8c}, {0xce,0xbf,0xcc,0x8d}},
- {{0xe1,0xbd,0x80}, {0xce,0xbf,0xcc,0x93}},
- {{0xe1,0xbd,0x82}, {0xce,0xbf,0xcc,0x93,0xcc,0x80}},
- {{0xe1,0xbd,0x84}, {0xce,0xbf,0xcc,0x93,0xcc,0x81}},
- {{0xe1,0xbd,0x81}, {0xce,0xbf,0xcc,0x94}},
- {{0xe1,0xbd,0x83}, {0xce,0xbf,0xcc,0x94,0xcc,0x80}},
- {{0xe1,0xbd,0x85}, {0xce,0xbf,0xcc,0x94,0xcc,0x81}},
- {{0xe1,0xbf,0xb4}, {0xce,0xbf,0xcd,0x85,0xcc,0x81}},
- {{0xe1,0xbf,0xa4}, {0xcf,0x81,0xcc,0x93}},
- {{0xe1,0xbf,0xa5}, {0xcf,0x81,0xcc,0x94}},
- {{0xe1,0xbd,0xba}, {0xcf,0x85,0xcc,0x80}},
- {{0xe1,0xbd,0xbb}, {0xcf,0x85,0xcc,0x81}},
- {{0xe1,0xbf,0xa1}, {0xcf,0x85,0xcc,0x84}},
- {{0xe1,0xbf,0xa0}, {0xcf,0x85,0xcc,0x86}},
- {{0xcf,0x8b}, {0xcf,0x85,0xcc,0x88}},
- {{0xe1,0xbf,0xa2}, {0xcf,0x85,0xcc,0x88,0xcc,0x80}},
- {{0xe1,0xbf,0xa3}, {0xcf,0x85,0xcc,0x88,0xcc,0x81}},
- {{0xce,0xb0}, {0xcf,0x85,0xcc,0x88,0xcc,0x8d}},
- {{0xe1,0xbf,0xa7}, {0xcf,0x85,0xcc,0x88,0xcd,0x82}},
- {{0xcf,0x8d}, {0xcf,0x85,0xcc,0x8d}},
- {{0xe1,0xbd,0x90}, {0xcf,0x85,0xcc,0x93}},
- {{0xe1,0xbd,0x92}, {0xcf,0x85,0xcc,0x93,0xcc,0x80}},
- {{0xe1,0xbd,0x94}, {0xcf,0x85,0xcc,0x93,0xcc,0x81}},
- {{0xe1,0xbd,0x96}, {0xcf,0x85,0xcc,0x93,0xcd,0x82}},
- {{0xe1,0xbd,0x91}, {0xcf,0x85,0xcc,0x94}},
- {{0xe1,0xbd,0x93}, {0xcf,0x85,0xcc,0x94,0xcc,0x80}},
- {{0xe1,0xbd,0x95}, {0xcf,0x85,0xcc,0x94,0xcc,0x81}},
- {{0xe1,0xbd,0x97}, {0xcf,0x85,0xcc,0x94,0xcd,0x82}},
- {{0xe1,0xbf,0xa6}, {0xcf,0x85,0xcd,0x82}},
- {{0xe1,0xbd,0xbc}, {0xcf,0x89,0xcc,0x80}},
- {{0xe1,0xbd,0xbd}, {0xcf,0x89,0xcc,0x81}},
- {{0xcf,0x8e}, {0xcf,0x89,0xcc,0x8d}},
- {{0xe1,0xbd,0xa0}, {0xcf,0x89,0xcc,0x93}},
- {{0xe1,0xbd,0xa2}, {0xcf,0x89,0xcc,0x93,0xcc,0x80}},
- {{0xe1,0xbd,0xa4}, {0xcf,0x89,0xcc,0x93,0xcc,0x81}},
- {{0xe1,0xbd,0xa6}, {0xcf,0x89,0xcc,0x93,0xcd,0x82}},
- {{0xe1,0xbd,0xa1}, {0xcf,0x89,0xcc,0x94}},
- {{0xe1,0xbd,0xa3}, {0xcf,0x89,0xcc,0x94,0xcc,0x80}},
- {{0xe1,0xbd,0xa5}, {0xcf,0x89,0xcc,0x94,0xcc,0x81}},
- {{0xe1,0xbd,0xa7}, {0xcf,0x89,0xcc,0x94,0xcd,0x82}},
- {{0xe1,0xbf,0xb6}, {0xcf,0x89,0xcd,0x82}},
- {{0xe1,0xbf,0xb3}, {0xcf,0x89,0xcd,0x85}},
- {{0xe1,0xbf,0xb2}, {0xcf,0x89,0xcd,0x85,0xcc,0x80}},
- {{0xe1,0xbe,0xa0}, {0xcf,0x89,0xcd,0x85,0xcc,0x93}},
- {{0xe1,0xbe,0xa2}, {0xcf,0x89,0xcd,0x85,0xcc,0x93,0xcc,0x80}},
- {{0xe1,0xbe,0xa4}, {0xcf,0x89,0xcd,0x85,0xcc,0x93,0xcc,0x81}},
- {{0xe1,0xbe,0xa6}, {0xcf,0x89,0xcd,0x85,0xcc,0x93,0xcd,0x82}},
- {{0xe1,0xbe,0xa1}, {0xcf,0x89,0xcd,0x85,0xcc,0x94}},
- {{0xe1,0xbe,0xa3}, {0xcf,0x89,0xcd,0x85,0xcc,0x94,0xcc,0x80}},
- {{0xe1,0xbe,0xa5}, {0xcf,0x89,0xcd,0x85,0xcc,0x94,0xcc,0x81}},
- {{0xe1,0xbe,0xa7}, {0xcf,0x89,0xcd,0x85,0xcc,0x94,0xcd,0x82}},
- {{0xe1,0xbf,0xb7}, {0xcf,0x89,0xcd,0x85,0xcd,0x82}},
- {{0xcf,0x94}, {0xcf,0x92,0xcc,0x88}},
- {{0xcf,0x93}, {0xcf,0x92,0xcc,0x8d}},
- {{0xd0,0x87}, {0xd0,0x86,0xcc,0x88}},
- {{0xd3,0x90}, {0xd0,0x90,0xcc,0x86}},
- {{0xd3,0x92}, {0xd0,0x90,0xcc,0x88}},
- {{0xd0,0x83}, {0xd0,0x93,0xcc,0x81}},
- {{0xd3,0x96}, {0xd0,0x95,0xcc,0x86}},
- {{0xd0,0x81}, {0xd0,0x95,0xcc,0x88}},
- {{0xd3,0x81}, {0xd0,0x96,0xcc,0x86}},
- {{0xd3,0x9c}, {0xd0,0x96,0xcc,0x88}},
- {{0xd3,0x9e}, {0xd0,0x97,0xcc,0x88}},
- {{0xd3,0xa2}, {0xd0,0x98,0xcc,0x84}},
- {{0xd0,0x99}, {0xd0,0x98,0xcc,0x86}},
- {{0xd3,0xa4}, {0xd0,0x98,0xcc,0x88}},
- {{0xd0,0x8c}, {0xd0,0x9a,0xcc,0x81}},
- {{0xd3,0xa6}, {0xd0,0x9e,0xcc,0x88}},
- {{0xd3,0xae}, {0xd0,0xa3,0xcc,0x84}},
- {{0xd0,0x8e}, {0xd0,0xa3,0xcc,0x86}},
- {{0xd3,0xb0}, {0xd0,0xa3,0xcc,0x88}},
- {{0xd3,0xb2}, {0xd0,0xa3,0xcc,0x8b}},
- {{0xd3,0xb4}, {0xd0,0xa7,0xcc,0x88}},
- {{0xd3,0xb8}, {0xd0,0xab,0xcc,0x88}},
- {{0xd3,0x91}, {0xd0,0xb0,0xcc,0x86}},
- {{0xd3,0x93}, {0xd0,0xb0,0xcc,0x88}},
- {{0xd1,0x93}, {0xd0,0xb3,0xcc,0x81}},
- {{0xd3,0x97}, {0xd0,0xb5,0xcc,0x86}},
- {{0xd1,0x91}, {0xd0,0xb5,0xcc,0x88}},
- {{0xd3,0x82}, {0xd0,0xb6,0xcc,0x86}},
- {{0xd3,0x9d}, {0xd0,0xb6,0xcc,0x88}},
- {{0xd3,0x9f}, {0xd0,0xb7,0xcc,0x88}},
- {{0xd3,0xa3}, {0xd0,0xb8,0xcc,0x84}},
- {{0xd0,0xb9}, {0xd0,0xb8,0xcc,0x86}},
- {{0xd3,0xa5}, {0xd0,0xb8,0xcc,0x88}},
- {{0xd1,0x9c}, {0xd0,0xba,0xcc,0x81}},
- {{0xd3,0xa7}, {0xd0,0xbe,0xcc,0x88}},
- {{0xd3,0xaf}, {0xd1,0x83,0xcc,0x84}},
- {{0xd1,0x9e}, {0xd1,0x83,0xcc,0x86}},
- {{0xd3,0xb1}, {0xd1,0x83,0xcc,0x88}},
- {{0xd3,0xb3}, {0xd1,0x83,0xcc,0x8b}},
- {{0xd3,0xb5}, {0xd1,0x87,0xcc,0x88}},
- {{0xd3,0xb9}, {0xd1,0x8b,0xcc,0x88}},
- {{0xd1,0x97}, {0xd1,0x96,0xcc,0x88}},
- {{0xd1,0xb6}, {0xd1,0xb4,0xcc,0x8f}},
- {{0xd1,0xb7}, {0xd1,0xb5,0xcc,0x8f}},
- {{0xef,0xac,0xae}, {0xd7,0x90,0xd6,0xb7}},
- {{0xef,0xac,0xaf}, {0xd7,0x90,0xd6,0xb8}},
- {{0xef,0xac,0xb0}, {0xd7,0x90,0xd6,0xbc}},
- {{0xef,0xac,0xb1}, {0xd7,0x91,0xd6,0xbc}},
- {{0xef,0xad,0x8c}, {0xd7,0x91,0xd6,0xbf}},
- {{0xef,0xac,0xb2}, {0xd7,0x92,0xd6,0xbc}},
- {{0xef,0xac,0xb3}, {0xd7,0x93,0xd6,0xbc}},
- {{0xef,0xac,0xb4}, {0xd7,0x94,0xd6,0xbc}},
- {{0xef,0xad,0x8b}, {0xd7,0x95,0xd6,0xb9}},
- {{0xef,0xac,0xb5}, {0xd7,0x95,0xd6,0xbc}},
- {{0xef,0xac,0xb6}, {0xd7,0x96,0xd6,0xbc}},
- {{0xef,0xac,0xb8}, {0xd7,0x98,0xd6,0xbc}},
- {{0xef,0xac,0xb9}, {0xd7,0x99,0xd6,0xbc}},
- {{0xef,0xac,0xba}, {0xd7,0x9a,0xd6,0xbc}},
- {{0xef,0xac,0xbb}, {0xd7,0x9b,0xd6,0xbc}},
- {{0xef,0xad,0x8d}, {0xd7,0x9b,0xd6,0xbf}},
- {{0xef,0xac,0xbc}, {0xd7,0x9c,0xd6,0xbc}},
- {{0xef,0xac,0xbe}, {0xd7,0x9e,0xd6,0xbc}},
- {{0xef,0xad,0x80}, {0xd7,0xa0,0xd6,0xbc}},
- {{0xef,0xad,0x81}, {0xd7,0xa1,0xd6,0xbc}},
- {{0xef,0xad,0x83}, {0xd7,0xa3,0xd6,0xbc}},
- {{0xef,0xad,0x84}, {0xd7,0xa4,0xd6,0xbc}},
- {{0xef,0xad,0x8e}, {0xd7,0xa4,0xd6,0xbf}},
- {{0xef,0xad,0x86}, {0xd7,0xa6,0xd6,0xbc}},
- {{0xef,0xad,0x87}, {0xd7,0xa7,0xd6,0xbc}},
- {{0xef,0xad,0x88}, {0xd7,0xa8,0xd6,0xbc}},
- {{0xef,0xad,0x89}, {0xd7,0xa9,0xd6,0xbc}},
- {{0xef,0xac,0xac}, {0xd7,0xa9,0xd6,0xbc,0xd7,0x81}},
- {{0xef,0xac,0xad}, {0xd7,0xa9,0xd6,0xbc,0xd7,0x82}},
- {{0xef,0xac,0xaa}, {0xd7,0xa9,0xd7,0x81}},
- {{0xef,0xac,0xab}, {0xd7,0xa9,0xd7,0x82}},
- {{0xef,0xad,0x8a}, {0xd7,0xaa,0xd6,0xbc}},
- {{0xef,0xac,0x9f}, {0xd7,0xb2,0xd6,0xb7}},
- {{0xe0,0xa5,0x98}, {0xe0,0xa4,0x95,0xe0,0xa4,0xbc}},
- {{0xe0,0xa5,0x99}, {0xe0,0xa4,0x96,0xe0,0xa4,0xbc}},
- {{0xe0,0xa5,0x9a}, {0xe0,0xa4,0x97,0xe0,0xa4,0xbc}},
- {{0xe0,0xa5,0x9b}, {0xe0,0xa4,0x9c,0xe0,0xa4,0xbc}},
- {{0xe0,0xa5,0x9c}, {0xe0,0xa4,0xa1,0xe0,0xa4,0xbc}},
- {{0xe0,0xa5,0x9d}, {0xe0,0xa4,0xa2,0xe0,0xa4,0xbc}},
- {{0xe0,0xa4,0xa9}, {0xe0,0xa4,0xa8,0xe0,0xa4,0xbc}},
- {{0xe0,0xa5,0x9e}, {0xe0,0xa4,0xab,0xe0,0xa4,0xbc}},
- {{0xe0,0xa5,0x9f}, {0xe0,0xa4,0xaf,0xe0,0xa4,0xbc}},
- {{0xe0,0xa4,0xb1}, {0xe0,0xa4,0xb0,0xe0,0xa4,0xbc}},
- {{0xe0,0xa4,0xb4}, {0xe0,0xa4,0xb3,0xe0,0xa4,0xbc}},
- {{0xe0,0xa7,0x9c}, {0xe0,0xa6,0xa1,0xe0,0xa6,0xbc}},
- {{0xe0,0xa7,0x9d}, {0xe0,0xa6,0xa2,0xe0,0xa6,0xbc}},
- {{0xe0,0xa6,0xb0}, {0xe0,0xa6,0xac,0xe0,0xa6,0xbc}},
- {{0xe0,0xa7,0x9f}, {0xe0,0xa6,0xaf,0xe0,0xa6,0xbc}},
- {{0xe0,0xa7,0x8b}, {0xe0,0xa7,0x87,0xe0,0xa6,0xbe}},
- {{0xe0,0xa7,0x8c}, {0xe0,0xa7,0x87,0xe0,0xa7,0x97}},
- {{0xe0,0xa9,0x99}, {0xe0,0xa8,0x96,0xe0,0xa8,0xbc}},
- {{0xe0,0xa9,0x9a}, {0xe0,0xa8,0x97,0xe0,0xa8,0xbc}},
- {{0xe0,0xa9,0x9b}, {0xe0,0xa8,0x9c,0xe0,0xa8,0xbc}},
- {{0xe0,0xa9,0x9c}, {0xe0,0xa8,0xa1,0xe0,0xa8,0xbc}},
- {{0xe0,0xa9,0x9e}, {0xe0,0xa8,0xab,0xe0,0xa8,0xbc}},
- {{0xe0,0xad,0x9c}, {0xe0,0xac,0xa1,0xe0,0xac,0xbc}},
- {{0xe0,0xad,0x9d}, {0xe0,0xac,0xa2,0xe0,0xac,0xbc}},
- {{0xe0,0xad,0x9f}, {0xe0,0xac,0xaf,0xe0,0xac,0xbc}},
- {{0xe0,0xad,0x8b}, {0xe0,0xad,0x87,0xe0,0xac,0xbe}},
- {{0xe0,0xad,0x88}, {0xe0,0xad,0x87,0xe0,0xad,0x96}},
- {{0xe0,0xad,0x8c}, {0xe0,0xad,0x87,0xe0,0xad,0x97}},
- {{0xe0,0xae,0x94}, {0xe0,0xae,0x92,0xe0,0xaf,0x97}},
- {{0xe0,0xaf,0x8a}, {0xe0,0xaf,0x86,0xe0,0xae,0xbe}},
- {{0xe0,0xaf,0x8c}, {0xe0,0xaf,0x86,0xe0,0xaf,0x97}},
- {{0xe0,0xaf,0x8b}, {0xe0,0xaf,0x87,0xe0,0xae,0xbe}},
- {{0xe0,0xb1,0x88}, {0xe0,0xb1,0x86,0xe0,0xb1,0x96}},
- {{0xe0,0xb3,0x80}, {0xe0,0xb2,0xbf,0xe0,0xb3,0x95}},
- {{0xe0,0xb3,0x8a}, {0xe0,0xb3,0x86,0xe0,0xb3,0x82}},
- {{0xe0,0xb3,0x8b}, {0xe0,0xb3,0x86,0xe0,0xb3,0x82,0xe0,0xb3,0x95}},
- {{0xe0,0xb3,0x87}, {0xe0,0xb3,0x86,0xe0,0xb3,0x95}},
- {{0xe0,0xb3,0x88}, {0xe0,0xb3,0x86,0xe0,0xb3,0x96}},
- {{0xe0,0xb5,0x8a}, {0xe0,0xb5,0x86,0xe0,0xb4,0xbe}},
- {{0xe0,0xb5,0x8c}, {0xe0,0xb5,0x86,0xe0,0xb5,0x97}},
- {{0xe0,0xb5,0x8b}, {0xe0,0xb5,0x87,0xe0,0xb4,0xbe}},
- {{0xe0,0xb8,0xb3}, {0xe0,0xb9,0x8d,0xe0,0xb8,0xb2}},
- {{0xe0,0xba,0xb3}, {0xe0,0xbb,0x8d,0xe0,0xba,0xb2}},
- {{0xe0,0xbd,0xa9}, {0xe0,0xbd,0x80,0xe0,0xbe,0xb5}},
- {{0xe0,0xbd,0x83}, {0xe0,0xbd,0x82,0xe0,0xbe,0xb7}},
- {{0xe0,0xbd,0x8d}, {0xe0,0xbd,0x8c,0xe0,0xbe,0xb7}},
- {{0xe0,0xbd,0x92}, {0xe0,0xbd,0x91,0xe0,0xbe,0xb7}},
- {{0xe0,0xbd,0x97}, {0xe0,0xbd,0x96,0xe0,0xbe,0xb7}},
- {{0xe0,0xbd,0x9c}, {0xe0,0xbd,0x9b,0xe0,0xbe,0xb7}},
- {{0xe0,0xbd,0xb3}, {0xe0,0xbd,0xb2,0xe0,0xbd,0xb1}},
- {{0xe0,0xbd,0xb5}, {0xe0,0xbd,0xb4,0xe0,0xbd,0xb1}},
- {{0xe0,0xbe,0x81}, {0xe0,0xbe,0x80,0xe0,0xbd,0xb1}},
- {{0xe0,0xbe,0xb9}, {0xe0,0xbe,0x90,0xe0,0xbe,0xb5}},
- {{0xe0,0xbe,0x93}, {0xe0,0xbe,0x92,0xe0,0xbe,0xb7}},
- {{0xe0,0xbe,0x9d}, {0xe0,0xbe,0x9c,0xe0,0xbe,0xb7}},
- {{0xe0,0xbe,0xa2}, {0xe0,0xbe,0xa1,0xe0,0xbe,0xb7}},
- {{0xe0,0xbe,0xa7}, {0xe0,0xbe,0xa6,0xe0,0xbe,0xb7}},
- {{0xe0,0xbe,0xac}, {0xe0,0xbe,0xab,0xe0,0xbe,0xb7}},
- {{0xe0,0xbd,0xb6}, {0xe0,0xbe,0xb2,0xe0,0xbe,0x80}},
- {{0xe0,0xbd,0xb7}, {0xe0,0xbe,0xb2,0xe0,0xbe,0x80,0xe0,0xbd,0xb1}},
- {{0xe0,0xbd,0xb8}, {0xe0,0xbe,0xb3,0xe0,0xbe,0x80}},
- {{0xe0,0xbd,0xb9}, {0xe0,0xbe,0xb3,0xe0,0xbe,0x80,0xe0,0xbd,0xb1}},
- {{0xe1,0xbf,0x8d}, {0xe1,0xbe,0xbf,0xcc,0x80}},
- {{0xe1,0xbf,0x8e}, {0xe1,0xbe,0xbf,0xcc,0x81}},
- {{0xe1,0xbf,0x8f}, {0xe1,0xbe,0xbf,0xcd,0x82}},
- {{0xe1,0xbf,0x9d}, {0xe1,0xbf,0xbe,0xcc,0x80}},
- {{0xe1,0xbf,0x9e}, {0xe1,0xbf,0xbe,0xcc,0x81}},
- {{0xe1,0xbf,0x9f}, {0xe1,0xbf,0xbe,0xcd,0x82}},
- {{0xe3,0x82,0x94}, {0xe3,0x81,0x86,0xe3,0x82,0x99}},
- {{0xe3,0x81,0x8c}, {0xe3,0x81,0x8b,0xe3,0x82,0x99}},
- {{0xe3,0x81,0x8e}, {0xe3,0x81,0x8d,0xe3,0x82,0x99}},
- {{0xe3,0x81,0x90}, {0xe3,0x81,0x8f,0xe3,0x82,0x99}},
- {{0xe3,0x81,0x92}, {0xe3,0x81,0x91,0xe3,0x82,0x99}},
- {{0xe3,0x81,0x94}, {0xe3,0x81,0x93,0xe3,0x82,0x99}},
- {{0xe3,0x81,0x96}, {0xe3,0x81,0x95,0xe3,0x82,0x99}},
- {{0xe3,0x81,0x98}, {0xe3,0x81,0x97,0xe3,0x82,0x99}},
- {{0xe3,0x81,0x9a}, {0xe3,0x81,0x99,0xe3,0x82,0x99}},
- {{0xe3,0x81,0x9c}, {0xe3,0x81,0x9b,0xe3,0x82,0x99}},
- {{0xe3,0x81,0x9e}, {0xe3,0x81,0x9d,0xe3,0x82,0x99}},
- {{0xe3,0x81,0xa0}, {0xe3,0x81,0x9f,0xe3,0x82,0x99}},
- {{0xe3,0x81,0xa2}, {0xe3,0x81,0xa1,0xe3,0x82,0x99}},
- {{0xe3,0x81,0xa5}, {0xe3,0x81,0xa4,0xe3,0x82,0x99}},
- {{0xe3,0x81,0xa7}, {0xe3,0x81,0xa6,0xe3,0x82,0x99}},
- {{0xe3,0x81,0xa9}, {0xe3,0x81,0xa8,0xe3,0x82,0x99}},
- {{0xe3,0x81,0xb0}, {0xe3,0x81,0xaf,0xe3,0x82,0x99}},
- {{0xe3,0x81,0xb1}, {0xe3,0x81,0xaf,0xe3,0x82,0x9a}},
- {{0xe3,0x81,0xb3}, {0xe3,0x81,0xb2,0xe3,0x82,0x99}},
- {{0xe3,0x81,0xb4}, {0xe3,0x81,0xb2,0xe3,0x82,0x9a}},
- {{0xe3,0x81,0xb6}, {0xe3,0x81,0xb5,0xe3,0x82,0x99}},
- {{0xe3,0x81,0xb7}, {0xe3,0x81,0xb5,0xe3,0x82,0x9a}},
- {{0xe3,0x81,0xb9}, {0xe3,0x81,0xb8,0xe3,0x82,0x99}},
- {{0xe3,0x81,0xba}, {0xe3,0x81,0xb8,0xe3,0x82,0x9a}},
- {{0xe3,0x81,0xbc}, {0xe3,0x81,0xbb,0xe3,0x82,0x99}},
- {{0xe3,0x81,0xbd}, {0xe3,0x81,0xbb,0xe3,0x82,0x9a}},
- {{0xe3,0x82,0x9e}, {0xe3,0x82,0x9d,0xe3,0x82,0x99}},
- {{0xe3,0x83,0xb4}, {0xe3,0x82,0xa6,0xe3,0x82,0x99}},
- {{0xe3,0x82,0xac}, {0xe3,0x82,0xab,0xe3,0x82,0x99}},
- {{0xe3,0x82,0xae}, {0xe3,0x82,0xad,0xe3,0x82,0x99}},
- {{0xe3,0x82,0xb0}, {0xe3,0x82,0xaf,0xe3,0x82,0x99}},
- {{0xe3,0x82,0xb2}, {0xe3,0x82,0xb1,0xe3,0x82,0x99}},
- {{0xe3,0x82,0xb4}, {0xe3,0x82,0xb3,0xe3,0x82,0x99}},
- {{0xe3,0x82,0xb6}, {0xe3,0x82,0xb5,0xe3,0x82,0x99}},
- {{0xe3,0x82,0xb8}, {0xe3,0x82,0xb7,0xe3,0x82,0x99}},
- {{0xe3,0x82,0xba}, {0xe3,0x82,0xb9,0xe3,0x82,0x99}},
- {{0xe3,0x82,0xbc}, {0xe3,0x82,0xbb,0xe3,0x82,0x99}},
- {{0xe3,0x82,0xbe}, {0xe3,0x82,0xbd,0xe3,0x82,0x99}},
- {{0xe3,0x83,0x80}, {0xe3,0x82,0xbf,0xe3,0x82,0x99}},
- {{0xe3,0x83,0x82}, {0xe3,0x83,0x81,0xe3,0x82,0x99}},
- {{0xe3,0x83,0x85}, {0xe3,0x83,0x84,0xe3,0x82,0x99}},
- {{0xe3,0x83,0x87}, {0xe3,0x83,0x86,0xe3,0x82,0x99}},
- {{0xe3,0x83,0x89}, {0xe3,0x83,0x88,0xe3,0x82,0x99}},
- {{0xe3,0x83,0x90}, {0xe3,0x83,0x8f,0xe3,0x82,0x99}},
- {{0xe3,0x83,0x91}, {0xe3,0x83,0x8f,0xe3,0x82,0x9a}},
- {{0xe3,0x83,0x93}, {0xe3,0x83,0x92,0xe3,0x82,0x99}},
- {{0xe3,0x83,0x94}, {0xe3,0x83,0x92,0xe3,0x82,0x9a}},
- {{0xe3,0x83,0x96}, {0xe3,0x83,0x95,0xe3,0x82,0x99}},
- {{0xe3,0x83,0x97}, {0xe3,0x83,0x95,0xe3,0x82,0x9a}},
- {{0xe3,0x83,0x99}, {0xe3,0x83,0x98,0xe3,0x82,0x99}},
- {{0xe3,0x83,0x9a}, {0xe3,0x83,0x98,0xe3,0x82,0x9a}},
- {{0xe3,0x83,0x9c}, {0xe3,0x83,0x9b,0xe3,0x82,0x99}},
- {{0xe3,0x83,0x9d}, {0xe3,0x83,0x9b,0xe3,0x82,0x9a}},
- {{0xe3,0x83,0xb7}, {0xe3,0x83,0xaf,0xe3,0x82,0x99}},
- {{0xe3,0x83,0xb8}, {0xe3,0x83,0xb0,0xe3,0x82,0x99}},
- {{0xe3,0x83,0xb9}, {0xe3,0x83,0xb1,0xe3,0x82,0x99}},
- {{0xe3,0x83,0xba}, {0xe3,0x83,0xb2,0xe3,0x82,0x99}},
- {{0xe3,0x83,0xbe}, {0xe3,0x83,0xbd,0xe3,0x82,0x99}},
-};
-#endif /* UNICODE_NORMALIZATION */
-#endif /* UTF8_INPUT_ENABLE */
-
-#ifdef SHIFTJIS_CP932
-const unsigned short shiftjis_cp932[3][189] = {
- {
- 0xEEEF, 0xEEF0, 0xEEF1, 0xEEF2, 0xEEF3, 0xEEF4, 0xEEF5, 0xEEF6,
- 0xEEF7, 0xEEF8, 0x8754, 0x8755, 0x8756, 0x8757, 0x8758, 0x8759,
- 0x875A, 0x875B, 0x875C, 0x875D, 0x81CA, 0xEEFA, 0xEEFB, 0xEEFC,
- 0x878A, 0x8782, 0x8784, 0x81E6, 0xED40, 0xED41, 0xED42, 0xED43,
- 0xED44, 0xED45, 0xED46, 0xED47, 0xED48, 0xED49, 0xED4A, 0xED4B,
- 0xED4C, 0xED4D, 0xED4E, 0xED4F, 0xED50, 0xED51, 0xED52, 0xED53,
- 0xED54, 0xED55, 0xED56, 0xED57, 0xED58, 0xED59, 0xED5A, 0xED5B,
- 0xED5C, 0xED5D, 0xED5E, 0xED5F, 0xED60, 0xED61, 0xED62, 0,
- 0xED63, 0xED64, 0xED65, 0xED66, 0xED67, 0xED68, 0xED69, 0xED6A,
- 0xED6B, 0xED6C, 0xED6D, 0xED6E, 0xED6F, 0xED70, 0xED71, 0xED72,
- 0xED73, 0xED74, 0xED75, 0xED76, 0xED77, 0xED78, 0xED79, 0xED7A,
- 0xED7B, 0xED7C, 0xED7D, 0xED7E, 0xED80, 0xED81, 0xED82, 0xED83,
- 0xED84, 0xED85, 0xED86, 0xED87, 0xED88, 0xED89, 0xED8A, 0xED8B,
- 0xED8C, 0xED8D, 0xED8E, 0xED8F, 0xED90, 0xED91, 0xED92, 0xED93,
- 0xED94, 0xED95, 0xED96, 0xED97, 0xED98, 0xED99, 0xED9A, 0xED9B,
- 0xED9C, 0xED9D, 0xED9E, 0xED9F, 0xEDA0, 0xEDA1, 0xEDA2, 0xEDA3,
- 0xEDA4, 0xEDA5, 0xEDA6, 0xEDA7, 0xEDA8, 0xEDA9, 0xEDAA, 0xEDAB,
- 0xEDAC, 0xEDAD, 0xEDAE, 0xEDAF, 0xEDB0, 0xEDB1, 0xEDB2, 0xEDB3,
- 0xEDB4, 0xEDB5, 0xEDB6, 0xEDB7, 0xEDB8, 0xEDB9, 0xEDBA, 0xEDBB,
- 0xEDBC, 0xEDBD, 0xEDBE, 0xEDBF, 0xEDC0, 0xEDC1, 0xEDC2, 0xEDC3,
- 0xEDC4, 0xEDC5, 0xEDC6, 0xEDC7, 0xEDC8, 0xEDC9, 0xEDCA, 0xEDCB,
- 0xEDCC, 0xEDCD, 0xEDCE, 0xEDCF, 0xEDD0, 0xEDD1, 0xEDD2, 0xEDD3,
- 0xEDD4, 0xEDD5, 0xEDD6, 0xEDD7, 0xEDD8, 0xEDD9, 0xEDDA, 0xEDDB,
- 0xEDDC, 0xEDDD, 0xEDDE, 0xEDDF, 0xEDE0,
- },
- {
- 0xEDE1, 0xEDE2, 0xEDE3, 0xEDE4, 0xEDE5, 0xEDE6, 0xEDE7, 0xEDE8,
- 0xEDE9, 0xEDEA, 0xEDEB, 0xEDEC, 0xEDED, 0xEDEE, 0xEDEF, 0xEDF0,
- 0xEDF1, 0xEDF2, 0xEDF3, 0xEDF4, 0xEDF5, 0xEDF6, 0xEDF7, 0xEDF8,
- 0xEDF9, 0xEDFA, 0xEDFB, 0xEDFC, 0xEE40, 0xEE41, 0xEE42, 0xEE43,
- 0xEE44, 0xEE45, 0xEE46, 0xEE47, 0xEE48, 0xEE49, 0xEE4A, 0xEE4B,
- 0xEE4C, 0xEE4D, 0xEE4E, 0xEE4F, 0xEE50, 0xEE51, 0xEE52, 0xEE53,
- 0xEE54, 0xEE55, 0xEE56, 0xEE57, 0xEE58, 0xEE59, 0xEE5A, 0xEE5B,
- 0xEE5C, 0xEE5D, 0xEE5E, 0xEE5F, 0xEE60, 0xEE61, 0xEE62, 0,
- 0xEE63, 0xEE64, 0xEE65, 0xEE66, 0xEE67, 0xEE68, 0xEE69, 0xEE6A,
- 0xEE6B, 0xEE6C, 0xEE6D, 0xEE6E, 0xEE6F, 0xEE70, 0xEE71, 0xEE72,
- 0xEE73, 0xEE74, 0xEE75, 0xEE76, 0xEE77, 0xEE78, 0xEE79, 0xEE7A,
- 0xEE7B, 0xEE7C, 0xEE7D, 0xEE7E, 0xEE80, 0xEE81, 0xEE82, 0xEE83,
- 0xEE84, 0xEE85, 0xEE86, 0xEE87, 0xEE88, 0xEE89, 0xEE8A, 0xEE8B,
- 0xEE8C, 0xEE8D, 0xEE8E, 0xEE8F, 0xEE90, 0xEE91, 0xEE92, 0xEE93,
- 0xEE94, 0xEE95, 0xEE96, 0xEE97, 0xEE98, 0xEE99, 0xEE9A, 0xEE9B,
- 0xEE9C, 0xEE9D, 0xEE9E, 0xEE9F, 0xEEA0, 0xEEA1, 0xEEA2, 0xEEA3,
- 0xEEA4, 0xEEA5, 0xEEA6, 0xEEA7, 0xEEA8, 0xEEA9, 0xEEAA, 0xEEAB,
- 0xEEAC, 0xEEAD, 0xEEAE, 0xEEAF, 0xEEB0, 0xEEB1, 0xEEB2, 0xEEB3,
- 0xEEB4, 0xEEB5, 0xEEB6, 0xEEB7, 0xEEB8, 0xEEB9, 0xEEBA, 0xEEBB,
- 0xEEBC, 0xEEBD, 0xEEBE, 0xEEBF, 0xEEC0, 0xEEC1, 0xEEC2, 0xEEC3,
- 0xEEC4, 0xEEC5, 0xEEC6, 0xEEC7, 0xEEC8, 0xEEC9, 0xEECA, 0xEECB,
- 0xEECC, 0xEECD, 0xEECE, 0xEECF, 0xEED0, 0xEED1, 0xEED2, 0xEED3,
- 0xEED4, 0xEED5, 0xEED6, 0xEED7, 0xEED8, 0xEED9, 0xEEDA, 0xEEDB,
- 0xEEDC, 0xEEDD, 0xEEDE, 0xEEDF, 0xEEE0,
- },
- {
- 0xEEE1, 0xEEE2, 0xEEE3, 0xEEE4, 0xEEE5, 0xEEE6, 0xEEE7, 0xEEE8,
- 0xEEE9, 0xEEEA, 0xEEEB, 0xEEEC, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0,
- },
-};
-const unsigned short cp932inv[2][189] = {
- {
- 0xFA5C, 0xFA5D, 0xFA5E, 0xFA5F, 0xFA60, 0xFA61, 0xFA62, 0xFA63,
- 0xFA64, 0xFA65, 0xFA66, 0xFA67, 0xFA68, 0xFA69, 0xFA6A, 0xFA6B,
- 0xFA6C, 0xFA6D, 0xFA6E, 0xFA6F, 0xFA70, 0xFA71, 0xFA72, 0xFA73,
- 0xFA74, 0xFA75, 0xFA76, 0xFA77, 0xFA78, 0xFA79, 0xFA7A, 0xFA7B,
- 0xFA7C, 0xFA7D, 0xFA7E, 0xFA80, 0xFA81, 0xFA82, 0xFA83, 0xFA84,
- 0xFA85, 0xFA86, 0xFA87, 0xFA88, 0xFA89, 0xFA8A, 0xFA8B, 0xFA8C,
- 0xFA8D, 0xFA8E, 0xFA8F, 0xFA90, 0xFA91, 0xFA92, 0xFA93, 0xFA94,
- 0xFA95, 0xFA96, 0xFA97, 0xFA98, 0xFA99, 0xFA9A, 0xFA9B, 0,
- 0xFA9C, 0xFA9D, 0xFA9E, 0xFA9F, 0xFAA0, 0xFAA1, 0xFAA2, 0xFAA3,
- 0xFAA4, 0xFAA5, 0xFAA6, 0xFAA7, 0xFAA8, 0xFAA9, 0xFAAA, 0xFAAB,
- 0xFAAC, 0xFAAD, 0xFAAE, 0xFAAF, 0xFAB0, 0xFAB1, 0xFAB2, 0xFAB3,
- 0xFAB4, 0xFAB5, 0xFAB6, 0xFAB7, 0xFAB8, 0xFAB9, 0xFABA, 0xFABB,
- 0xFABC, 0xFABD, 0xFABE, 0xFABF, 0xFAC0, 0xFAC1, 0xFAC2, 0xFAC3,
- 0xFAC4, 0xFAC5, 0xFAC6, 0xFAC7, 0xFAC8, 0xFAC9, 0xFACA, 0xFACB,
- 0xFACC, 0xFACD, 0xFACE, 0xFACF, 0xFAD0, 0xFAD1, 0xFAD2, 0xFAD3,
- 0xFAD4, 0xFAD5, 0xFAD6, 0xFAD7, 0xFAD8, 0xFAD9, 0xFADA, 0xFADB,
- 0xFADC, 0xFADD, 0xFADE, 0xFADF, 0xFAE0, 0xFAE1, 0xFAE2, 0xFAE3,
- 0xFAE4, 0xFAE5, 0xFAE6, 0xFAE7, 0xFAE8, 0xFAE9, 0xFAEA, 0xFAEB,
- 0xFAEC, 0xFAED, 0xFAEE, 0xFAEF, 0xFAF0, 0xFAF1, 0xFAF2, 0xFAF3,
- 0xFAF4, 0xFAF5, 0xFAF6, 0xFAF7, 0xFAF8, 0xFAF9, 0xFAFA, 0xFAFB,
- 0xFAFC, 0xFB40, 0xFB41, 0xFB42, 0xFB43, 0xFB44, 0xFB45, 0xFB46,
- 0xFB47, 0xFB48, 0xFB49, 0xFB4A, 0xFB4B, 0xFB4C, 0xFB4D, 0xFB4E,
- 0xFB4F, 0xFB50, 0xFB51, 0xFB52, 0xFB53, 0xFB54, 0xFB55, 0xFB56,
- 0xFB57, 0xFB58, 0xFB59, 0xFB5A, 0xFB5B,
- },
- {
- 0xFB5C, 0xFB5D, 0xFB5E, 0xFB5F, 0xFB60, 0xFB61, 0xFB62, 0xFB63,
- 0xFB64, 0xFB65, 0xFB66, 0xFB67, 0xFB68, 0xFB69, 0xFB6A, 0xFB6B,
- 0xFB6C, 0xFB6D, 0xFB6E, 0xFB6F, 0xFB70, 0xFB71, 0xFB72, 0xFB73,
- 0xFB74, 0xFB75, 0xFB76, 0xFB77, 0xFB78, 0xFB79, 0xFB7A, 0xFB7B,
- 0xFB7C, 0xFB7D, 0xFB7E, 0xFB80, 0xFB81, 0xFB82, 0xFB83, 0xFB84,
- 0xFB85, 0xFB86, 0xFB87, 0xFB88, 0xFB89, 0xFB8A, 0xFB8B, 0xFB8C,
- 0xFB8D, 0xFB8E, 0xFB8F, 0xFB90, 0xFB91, 0xFB92, 0xFB93, 0xFB94,
- 0xFB95, 0xFB96, 0xFB97, 0xFB98, 0xFB99, 0xFB9A, 0xFB9B, 0,
- 0xFB9C, 0xFB9D, 0xFB9E, 0xFB9F, 0xFBA0, 0xFBA1, 0xFBA2, 0xFBA3,
- 0xFBA4, 0xFBA5, 0xFBA6, 0xFBA7, 0xFBA8, 0xFBA9, 0xFBAA, 0xFBAB,
- 0xFBAC, 0xFBAD, 0xFBAE, 0xFBAF, 0xFBB0, 0xFBB1, 0xFBB2, 0xFBB3,
- 0xFBB4, 0xFBB5, 0xFBB6, 0xFBB7, 0xFBB8, 0xFBB9, 0xFBBA, 0xFBBB,
- 0xFBBC, 0xFBBD, 0xFBBE, 0xFBBF, 0xFBC0, 0xFBC1, 0xFBC2, 0xFBC3,
- 0xFBC4, 0xFBC5, 0xFBC6, 0xFBC7, 0xFBC8, 0xFBC9, 0xFBCA, 0xFBCB,
- 0xFBCC, 0xFBCD, 0xFBCE, 0xFBCF, 0xFBD0, 0xFBD1, 0xFBD2, 0xFBD3,
- 0xFBD4, 0xFBD5, 0xFBD6, 0xFBD7, 0xFBD8, 0xFBD9, 0xFBDA, 0xFBDB,
- 0xFBDC, 0xFBDD, 0xFBDE, 0xFBDF, 0xFBE0, 0xFBE1, 0xFBE2, 0xFBE3,
- 0xFBE4, 0xFBE5, 0xFBE6, 0xFBE7, 0xFBE8, 0xFBE9, 0xFBEA, 0xFBEB,
- 0xFBEC, 0xFBED, 0xFBEE, 0xFBEF, 0xFBF0, 0xFBF1, 0xFBF2, 0xFBF3,
- 0xFBF4, 0xFBF5, 0xFBF6, 0xFBF7, 0xFBF8, 0xFBF9, 0xFBFA, 0xFBFB,
- 0xFBFC, 0xFC40, 0xFC41, 0xFC42, 0xFC43, 0xFC44, 0xFC45, 0xFC46,
- 0xFC47, 0xFC48, 0xFC49, 0xFC4A, 0xFC4B, 0, 0, 0xFA40,
- 0xFA41, 0xFA42, 0xFA43, 0xFA44, 0xFA45, 0xFA46, 0xFA47, 0xFA48,
- 0xFA49, 0x81CA, 0xFA55, 0xFA56, 0xFA57,
- },
-};
-#endif /* SHIFTJIS_CP932 */
-
-#ifdef X0212_ENABLE
-const unsigned short shiftjis_x0212[3][189] = {
- {
- 0xF373, 0xF374, 0xF375, 0xF376, 0xF377, 0xF378, 0xF379, 0xF37A,
- 0xF37B, 0xF37C, 0xF37D, 0xF37E, 0xF421, 0xF422, 0xF423, 0xF424,
- 0xF425, 0xF426, 0xF427, 0xF428, 0x224C, 0xA243, 0xF429, 0xF42A,
- 0xF42B, 0xF42C, 0xF42D, 0x2268, 0xD463, 0xDC5F, 0xE469, 0xE378,
- 0xD921, 0xB13B, 0xF42E, 0xC22D, 0xC37C, 0xE450, 0xC23F, 0xBC74,
- 0xB029, 0xB048, 0xF42F, 0xB052, 0xB054, 0xB063, 0xB06E, 0xB127,
- 0xB123, 0xB12C, 0xB129, 0xB13E, 0xB15F, 0xB158, 0xB148, 0xB157,
- 0xB163, 0xB174, 0xB161, 0xB223, 0xF430, 0xB23B, 0xB266, 0,
- 0xB26D, 0xB275, 0xB27C, 0xF431, 0xB335, 0xB358, 0xB35B, 0xB365,
- 0xB36E, 0xB37B, 0xF432, 0xF433, 0xB440, 0xB447, 0xB450, 0xB45E,
- 0xF434, 0xB52A, 0xF435, 0xB52F, 0xB544, 0xB568, 0xF436, 0xB742,
- 0xB764, 0xB768, 0xB767, 0xF437, 0xF438, 0xF439, 0xB84E, 0xB861,
- 0xB875, 0xB877, 0xB878, 0xB87C, 0xB92F, 0xB937, 0xBA3E, 0xBA5B,
- 0xCD2A, 0xBA61, 0xF43A, 0xBA6B, 0xBB33, 0xBB38, 0xF43B, 0xBB4A,
- 0xF43C, 0xF43D, 0xBB50, 0xBB5E, 0xBB74, 0xBB75, 0xBB79, 0xBC64,
- 0xBC6D, 0xBC7E, 0xF43E, 0xBD42, 0xBD67, 0xF43F, 0xBD70, 0xBE30,
- 0xBE2C, 0xF440, 0xBE33, 0xBE3D, 0xBE4D, 0xBE49, 0xBE64, 0xBF28,
- 0xBF49, 0xC044, 0xC064, 0xC074, 0xC126, 0xF441, 0xC175, 0xC17C,
- 0xF442, 0xC178, 0xC22B, 0xC221, 0xC225, 0xF443, 0xC238, 0xC23A,
- 0xF444, 0xC244, 0xC252, 0xC257, 0xC25B, 0xC25E, 0xC26D, 0xC270,
- 0xF445, 0xC321, 0xC335, 0xC349, 0xC339, 0xF446, 0xC358, 0xC37E,
- 0xF447, 0xC44C, 0xF448, 0xC459, 0xC46A, 0xC47D, 0xF449, 0xC527,
- 0xC535, 0xC536, 0xF44A, 0xC555, 0xC638, 0xC657, 0xC660, 0xC66A,
- 0xC663, 0xC721, 0xC72B, 0xC747, 0xC743,
- },
- {
- 0xC74B, 0xC74F, 0xC759, 0xF44B, 0xF44C, 0xC766, 0xC76E, 0xC77C,
- 0xC76B, 0xC770, 0xC831, 0xC865, 0xC878, 0xC926, 0xC92B, 0xC92D,
- 0xF44D, 0xC94A, 0xC953, 0xC969, 0xC963, 0xC97C, 0xC974, 0xC975,
- 0xF44E, 0xCA33, 0xCA3D, 0xCA6F, 0xCA71, 0xCB2E, 0xF44F, 0xCB4A,
- 0xCB66, 0xCB6A, 0xCB70, 0xCB74, 0xCB6E, 0xCC25, 0xCB79, 0xCC2B,
- 0xCC2E, 0xCC2D, 0xCC32, 0xCC42, 0xCC50, 0xCC59, 0xF450, 0xCD3B,
- 0xF451, 0xCE3B, 0xF452, 0xCE3A, 0xCE43, 0xF453, 0xCE72, 0xB35D,
- 0xCF55, 0xCF62, 0xCF69, 0xCF6D, 0xF454, 0xF455, 0xF456, 0,
- 0xF457, 0xD065, 0xF458, 0xD069, 0xD168, 0xF459, 0xF45A, 0xD16C,
- 0xD23B, 0xF45B, 0xD361, 0xD368, 0xD427, 0xF45C, 0xF45D, 0xD454,
- 0xD472, 0xD52E, 0xF45E, 0xD75E, 0xF45F, 0xD822, 0xD837, 0xD841,
- 0xD851, 0xD874, 0xD946, 0xD948, 0xD951, 0xF460, 0xF461, 0xF462,
- 0xF463, 0xF464, 0xDC53, 0xDD48, 0xDD54, 0xDD6A, 0xDD7A, 0xDE24,
- 0xDE30, 0xF465, 0xDE35, 0xDE4B, 0xF466, 0xDF39, 0xF467, 0xDF43,
- 0xF468, 0xF469, 0xE059, 0xF46A, 0xF46B, 0xE162, 0xF46C, 0xF46D,
- 0xF46E, 0xE247, 0xE328, 0xE326, 0xE329, 0xE32F, 0xE330, 0xE32A,
- 0xE32B, 0xE33C, 0xE341, 0xE33F, 0xE355, 0xE358, 0xE356, 0xE35F,
- 0xE363, 0xE361, 0xE354, 0xE369, 0xE426, 0xE371, 0xE372, 0xE44B,
- 0xE441, 0xE443, 0xE43E, 0xF46F, 0xE440, 0xE447, 0xE43F, 0xE460,
- 0xE45E, 0xE451, 0xF470, 0xE45C, 0xE452, 0xE45B, 0xE454, 0xE47A,
- 0xE46F, 0xE533, 0xE53F, 0xE549, 0xE550, 0xE562, 0xE56A, 0xE56B,
- 0xF471, 0xF472, 0xF473, 0xE668, 0xE66F, 0xE72C, 0xF474, 0xE72E,
- 0xF475, 0xE731, 0xF476, 0xE732, 0xE831, 0xE836, 0xF477, 0xF478,
- 0xE85D, 0xF479, 0xF47A, 0xE951, 0xF47B,
- },
- {
- 0xE96D, 0xEA4D, 0xF47C, 0xEA5B, 0xEA66, 0xEA6A, 0xEB25, 0xEB7B,
- 0xEB7A, 0xF47D, 0xEC56, 0xF47E, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0,
- },
-};
-
-static const unsigned short x0212_shiftjis_A2[] = {
- 0x819F, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0x8143, 0, 0, 0x8150, 0, 0, 0x8160,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xFA55, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_B0[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0xFA68, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xFA69, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xFA6B, 0, 0xFA6C, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xFA6D, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xFA6E, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_B1[] = {
- 0, 0, 0xFA70, 0, 0, 0, 0xFA6F,
- 0, 0xFA72, 0, 0, 0xFA71, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xFA61, 0, 0, 0xFA73, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xFA76, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xFA77,
- 0xFA75, 0, 0, 0, 0, 0, 0, 0xFA74,
- 0, 0xFA7A, 0, 0xFA78, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xFA79, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_B2[] = {
- 0, 0, 0xFA7B, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xFA7D, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xFA7E, 0,
- 0, 0, 0, 0, 0, 0xFA80, 0, 0,
- 0, 0, 0, 0, 0, 0xFA81, 0, 0,
- 0, 0, 0, 0, 0xFA82, 0, 0,
-};
-static const unsigned short x0212_shiftjis_B3[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xFA84, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xFA85, 0, 0, 0xFA86, 0, 0xFB77, 0, 0,
- 0, 0, 0, 0, 0, 0xFA87, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xFA88, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xFA89, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_B4[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xFA8C, 0, 0, 0, 0, 0, 0, 0xFA8D,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xFA8E, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xFA8F, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_B5[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xFA91, 0, 0, 0, 0, 0xFA93,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xFA94, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xFA95, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_B7[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xFA97, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xFA98, 0, 0, 0xFA9A,
- 0xFA99, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_B8[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xFA9E, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xFA9F, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xFAA0, 0, 0xFAA1,
- 0xFAA2, 0, 0, 0, 0xFAA3, 0, 0,
-};
-static const unsigned short x0212_shiftjis_B9[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xFAA4,
- 0, 0, 0, 0, 0, 0, 0, 0xFAA5,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_BA[] = {
- 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, 0xFAA6, 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, 0xFAA7, 0, 0, 0, 0,
- 0, 0xFAA9, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xFAAB, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_BB[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xFAAC, 0, 0, 0, 0,
- 0xFAAD, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xFAAF, 0, 0, 0, 0, 0,
- 0xFAB2, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xFAB3, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xFAB4, 0xFAB5, 0, 0,
- 0, 0xFAB6, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_BC[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xFAB7, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xFAB8, 0, 0,
- 0, 0, 0, 0, 0xFA67, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xFAB9,
-};
-static const unsigned short x0212_shiftjis_BD[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xFABB, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xFABC,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xFABE, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_BE[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xFAC0, 0, 0, 0,
- 0xFABF, 0, 0, 0xFAC2, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xFAC3, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xFAC5, 0, 0, 0, 0xFAC4, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xFAC6, 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,
-};
-static const unsigned short x0212_shiftjis_BF[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0xFAC7, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xFAC8, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_C0[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xFAC9, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xFACA, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xFACB, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_C1[] = {
- 0, 0, 0, 0, 0, 0xFACC, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xFACE, 0, 0,
- 0xFAD1, 0, 0, 0, 0xFACF, 0, 0,
-};
-static const unsigned short x0212_shiftjis_C2[] = {
- 0xFAD3, 0, 0, 0, 0xFAD4, 0, 0,
- 0, 0, 0, 0xFAD2, 0, 0xFA63, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xFAD6, 0, 0xFAD7, 0, 0, 0, 0, 0xFA66,
- 0, 0, 0, 0, 0xFAD9, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xFADA, 0, 0, 0, 0, 0xFADB,
- 0, 0, 0, 0xFADC, 0, 0, 0xFADD, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xFADE, 0, 0,
- 0xFADF, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_C3[] = {
- 0xFAE1, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xFAE2, 0, 0,
- 0, 0xFAE4, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xFAE3, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xFAE6, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xFA64, 0, 0xFAE7,
-};
-static const unsigned short x0212_shiftjis_C4[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xFAE9, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xFAEB, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xFAEC, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xFAED, 0,
-};
-static const unsigned short x0212_shiftjis_C5[] = {
- 0, 0, 0, 0, 0, 0, 0xFAEF,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xFAF0, 0xFAF1, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xFAF3, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_C6[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xFAF4, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xFAF5,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xFAF6, 0, 0, 0xFAF8, 0, 0, 0, 0,
- 0, 0, 0xFAF7, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_C7[] = {
- 0xFAF9, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xFAFA, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xFAFC, 0, 0, 0, 0xFAFB,
- 0, 0, 0, 0xFB40, 0, 0, 0, 0xFB41,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xFB42, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xFB45, 0,
- 0, 0, 0, 0xFB48, 0, 0, 0xFB46, 0,
- 0xFB49, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xFB47, 0, 0,
-};
-static const unsigned short x0212_shiftjis_C8[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xFB4A, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xFB4B, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xFB4C, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_C9[] = {
- 0, 0, 0, 0, 0, 0xFB4D, 0,
- 0, 0, 0, 0xFB4E, 0, 0xFB4F, 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, 0xFB51, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xFB52, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xFB54, 0, 0, 0, 0,
- 0, 0xFB53, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xFB56, 0xFB57, 0, 0,
- 0, 0, 0, 0, 0xFB55, 0, 0,
-};
-static const unsigned short x0212_shiftjis_CA[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xFB59, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xFB5A, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xFB5B,
- 0, 0xFB5C, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_CB[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xFB5D, 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, 0xFB5F, 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, 0xFB60, 0,
- 0, 0, 0xFB61, 0, 0, 0, 0xFB64, 0,
- 0xFB62, 0, 0, 0, 0xFB63, 0, 0, 0,
- 0, 0xFB66, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_CC[] = {
- 0, 0, 0, 0, 0xFB65, 0, 0,
- 0, 0, 0, 0xFB67, 0, 0xFB69, 0xFB68, 0,
- 0, 0, 0xFB6A, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xFB6B, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xFB6C, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xFB6D, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_CD[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xFAA8, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xFB6F, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_CE[] = {
- 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, 0xFB73, 0xFB71, 0, 0, 0, 0,
- 0, 0, 0, 0xFB74, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xFB76, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_CF[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xFB78, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xFB79, 0, 0, 0, 0, 0,
- 0, 0xFB7A, 0, 0, 0, 0xFB7B, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_D0[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xFB81, 0, 0,
- 0, 0xFB83, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_D1[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xFB84, 0, 0, 0, 0xFB87, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_D2[] = {
- 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, 0xFB88, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_D3[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xFB8A, 0, 0, 0, 0, 0, 0,
- 0xFB8B, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_D4[] = {
- 0, 0, 0, 0, 0, 0, 0xFB8C,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xFB8F, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xFA5C, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xFB90, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_D5[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xFB91, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_D7[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xFB93, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_D8[] = {
- 0, 0xFB95, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xFB96,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xFB97, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xFB98, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xFB99, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_D9[] = {
- 0xFA60, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xFB9A, 0,
- 0xFB9B, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xFB9C, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_DC[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xFBA2, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xFA5D,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_DD[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xFBA3, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xFBA4, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xFBA5, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xFBA6, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_DE[] = {
- 0, 0, 0, 0xFBA7, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xFBA8, 0, 0, 0, 0, 0xFBAA, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xFBAB, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_DF[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xFBAD, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xFBAF, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_E0[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xFBB2, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_E1[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xFBB5, 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,
-};
-static const unsigned short x0212_shiftjis_E2[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xFBB9,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_E3[] = {
- 0, 0, 0, 0, 0, 0xFBBB, 0,
- 0xFBBA, 0xFBBC, 0xFBBF, 0xFBC0, 0, 0, 0, 0xFBBD,
- 0xFBBE, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xFBC1, 0, 0, 0xFBC3,
- 0, 0xFBC2, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xFBCA, 0xFBC4, 0xFBC6, 0,
- 0xFBC5, 0, 0, 0, 0, 0, 0, 0xFBC7,
- 0, 0xFBC9, 0, 0xFBC8, 0, 0, 0, 0,
- 0, 0xFBCB, 0, 0, 0, 0, 0, 0,
- 0, 0xFBCD, 0xFBCE, 0, 0, 0, 0, 0,
- 0xFA5F, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_E4[] = {
- 0, 0, 0, 0, 0, 0xFBCC, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xFBD2, 0xFBD6,
- 0xFBD4, 0xFBD0, 0, 0xFBD1, 0, 0, 0, 0xFBD5,
- 0, 0, 0, 0xFBCF, 0, 0, 0, 0,
- 0xFA65, 0xFBD9, 0xFBDC, 0, 0xFBDE, 0, 0, 0,
- 0, 0, 0, 0xFBDD, 0xFBDB, 0, 0xFBD8, 0,
- 0xFBD7, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xFA5E, 0, 0, 0, 0, 0, 0xFBE0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xFBDF, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_E5[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xFBE1, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0xFBE2,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xFBE3, 0, 0, 0, 0, 0, 0,
- 0xFBE4, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xFBE5, 0, 0, 0, 0, 0,
- 0, 0, 0xFBE6, 0xFBE7, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_E6[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0xFBEB, 0, 0, 0, 0, 0, 0, 0xFBEC,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_E7[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0xFBED, 0, 0xFBEF, 0,
- 0, 0xFBF1, 0xFBF3, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_E8[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xFBF4, 0, 0, 0, 0, 0xFBF5, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xFBF8, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_E9[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0xFBFB, 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, 0xFC40, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_EA[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0xFC41, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xFC43, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xFC44, 0,
- 0, 0, 0xFC45, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_EB[] = {
- 0, 0, 0, 0, 0xFC46, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0xFC48, 0xFC47, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_EC[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xFC4A, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-};
-static const unsigned short x0212_shiftjis_F3[] = {
- 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0xFA40, 0xFA41, 0xFA42, 0xFA43, 0xFA44,
- 0xFA45, 0xFA46, 0xFA47, 0xFA48, 0xFA49, 0xFA4A, 0xFA4B,
-};
-static const unsigned short x0212_shiftjis_F4[] = {
- 0xFA4C, 0xFA4D, 0xFA4E, 0xFA4F, 0xFA50, 0xFA51, 0xFA52,
- 0xFA53, 0xFA56, 0xFA57, 0xFA58, 0xFA59, 0xFA5A, 0xFA62, 0xFA6A,
- 0xFA7C, 0xFA83, 0xFA8A, 0xFA8B, 0xFA90, 0xFA92, 0xFA96, 0xFA9B,
- 0xFA9C, 0xFA9D, 0xFAAA, 0xFAAE, 0xFAB0, 0xFAB1, 0xFABA, 0xFABD,
- 0xFAC1, 0xFACD, 0xFAD0, 0xFAD5, 0xFAD8, 0xFAE0, 0xFAE5, 0xFAE8,
- 0xFAEA, 0xFAEE, 0xFAF2, 0xFB43, 0xFB44, 0xFB50, 0xFB58, 0xFB5E,
- 0xFB6E, 0xFB70, 0xFB72, 0xFB75, 0xFB7C, 0xFB7D, 0xFB7E, 0xFB80,
- 0xFB82, 0xFB85, 0xFB86, 0xFB89, 0xFB8D, 0xFB8E, 0xFB92, 0xFB94,
- 0xFB9D, 0xFB9E, 0xFB9F, 0xFBA0, 0xFBA1, 0xFBA9, 0xFBAC, 0xFBAE,
- 0xFBB0, 0xFBB1, 0xFBB3, 0xFBB4, 0xFBB6, 0xFBB7, 0xFBB8, 0xFBD3,
- 0xFBDA, 0xFBE8, 0xFBE9, 0xFBEA, 0xFBEE, 0xFBF0, 0xFBF2, 0xFBF6,
- 0xFBF7, 0xFBF9, 0xFBFA, 0xFBFC, 0xFC42, 0xFC49, 0xFC4B,
-};
-const unsigned short *const x0212_shiftjis[] = {
- 0, x0212_shiftjis_A2, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- x0212_shiftjis_B0, x0212_shiftjis_B1, x0212_shiftjis_B2, x0212_shiftjis_B3,
- x0212_shiftjis_B4, x0212_shiftjis_B5, 0, x0212_shiftjis_B7,
- x0212_shiftjis_B8, x0212_shiftjis_B9, x0212_shiftjis_BA, x0212_shiftjis_BB,
- x0212_shiftjis_BC, x0212_shiftjis_BD, x0212_shiftjis_BE, x0212_shiftjis_BF,
- x0212_shiftjis_C0, x0212_shiftjis_C1, x0212_shiftjis_C2, x0212_shiftjis_C3,
- x0212_shiftjis_C4, x0212_shiftjis_C5, x0212_shiftjis_C6, x0212_shiftjis_C7,
- x0212_shiftjis_C8, x0212_shiftjis_C9, x0212_shiftjis_CA, x0212_shiftjis_CB,
- x0212_shiftjis_CC, x0212_shiftjis_CD, x0212_shiftjis_CE, x0212_shiftjis_CF,
- x0212_shiftjis_D0, x0212_shiftjis_D1, x0212_shiftjis_D2, x0212_shiftjis_D3,
- x0212_shiftjis_D4, x0212_shiftjis_D5, 0, x0212_shiftjis_D7,
- x0212_shiftjis_D8, x0212_shiftjis_D9, 0, 0,
- x0212_shiftjis_DC, x0212_shiftjis_DD, x0212_shiftjis_DE, x0212_shiftjis_DF,
- x0212_shiftjis_E0, x0212_shiftjis_E1, x0212_shiftjis_E2, x0212_shiftjis_E3,
- x0212_shiftjis_E4, x0212_shiftjis_E5, x0212_shiftjis_E6, x0212_shiftjis_E7,
- x0212_shiftjis_E8, x0212_shiftjis_E9, x0212_shiftjis_EA, x0212_shiftjis_EB,
- x0212_shiftjis_EC, 0, 0, 0,
- 0, 0, 0, x0212_shiftjis_F3,
- x0212_shiftjis_F4, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0,
-};
-#endif /* X0212_ENABLE */
diff --git a/ext/nkf/nkf-utf8/utf8tbl.h b/ext/nkf/nkf-utf8/utf8tbl.h
deleted file mode 100644
index 54a34271dd..0000000000
--- a/ext/nkf/nkf-utf8/utf8tbl.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * utf8tbl.h - Header file for Conversion Table
- *
- */
-
-#ifndef _UTF8TBL_H_
-#define _UTF8TBL_H_
-
-#ifdef UTF8_OUTPUT_ENABLE
-#define sizeof_euc_to_utf8_1byte 94
-#define sizeof_euc_to_utf8_2bytes 94
-extern const unsigned short euc_to_utf8_1byte[];
-extern const unsigned short *const euc_to_utf8_2bytes[];
-extern const unsigned short *const euc_to_utf8_2bytes_ms[];
-extern const unsigned short *const euc_to_utf8_2bytes_mac[];
-extern const unsigned short *const euc_to_utf8_2bytes_x0213[];
-extern const unsigned short *const x0212_to_utf8_2bytes[];
-extern const unsigned short *const x0212_to_utf8_2bytes_x0213[];
-#define sizeof_x0213_combining_chars 5
-#define sizeof_x0213_combining_table 25
-#define sizeof_x0213_1_surrogate_table 26
-#define sizeof_x0213_2_surrogate_table 277
-extern const unsigned short x0213_combining_chars[sizeof_x0213_combining_chars];
-extern const unsigned short x0213_combining_table[sizeof_x0213_combining_table][3];
-extern const unsigned short x0213_1_surrogate_table[sizeof_x0213_1_surrogate_table][3];
-extern const unsigned short x0213_2_surrogate_table[sizeof_x0213_2_surrogate_table][3];
-#endif /* UTF8_OUTPUT_ENABLE */
-
-#ifdef UTF8_INPUT_ENABLE
-#define sizeof_utf8_to_euc_C2 64
-#define sizeof_utf8_to_euc_E5B8 64
-#define sizeof_utf8_to_euc_2bytes 112
-#define sizeof_utf8_to_euc_3bytes 16
-extern const unsigned short *const utf8_to_euc_2bytes[];
-extern const unsigned short *const utf8_to_euc_2bytes_ms[];
-extern const unsigned short *const utf8_to_euc_2bytes_932[];
-extern const unsigned short *const utf8_to_euc_2bytes_mac[];
-extern const unsigned short *const utf8_to_euc_2bytes_x0213[];
-extern const unsigned short *const *const utf8_to_euc_3bytes[];
-extern const unsigned short *const *const utf8_to_euc_3bytes_ms[];
-extern const unsigned short *const *const utf8_to_euc_3bytes_932[];
-extern const unsigned short *const *const utf8_to_euc_3bytes_mac[];
-extern const unsigned short *const *const utf8_to_euc_3bytes_x0213[];
-#endif /* UTF8_INPUT_ENABLE */
-
-#ifdef UNICODE_NORMALIZATION
-
-#define NORMALIZATION_TABLE_LENGTH 942
-#define NORMALIZATION_TABLE_NFC_LENGTH 3
-#define NORMALIZATION_TABLE_NFD_LENGTH 9
-struct normalization_pair {
- const unsigned char nfc[NORMALIZATION_TABLE_NFC_LENGTH];
- const unsigned char nfd[NORMALIZATION_TABLE_NFD_LENGTH];
-};
-extern const struct normalization_pair normalization_table[];
-#endif
-
-#ifdef SHIFTJIS_CP932
-#define CP932_TABLE_BEGIN 0xFA
-#define CP932_TABLE_END 0xFC
-extern const unsigned short shiftjis_cp932[3][189];
-#define CP932INV_TABLE_BEGIN 0xED
-#define CP932INV_TABLE_END 0xEE
-extern const unsigned short cp932inv[2][189];
-#endif /* SHIFTJIS_CP932 */
-
-#ifdef X0212_ENABLE
-extern const unsigned short shiftjis_x0212[3][189];
-extern const unsigned short *const x0212_shiftjis[];
-#endif /* X0212_ENABLE */
-
-#endif
diff --git a/ext/nkf/nkf.c b/ext/nkf/nkf.c
deleted file mode 100644
index 73b616a54f..0000000000
--- a/ext/nkf/nkf.c
+++ /dev/null
@@ -1,506 +0,0 @@
-/*
- * NKF - Ruby extension for Network Kanji Filter
- *
- * original nkf2.x is maintained at http://sourceforge.jp/projects/nkf/
- *
- * $Id$
- *
- */
-
-#define RUBY_NKF_REVISION "$Revision$"
-#define RUBY_NKF_VERSION NKF_VERSION " (" NKF_RELEASE_DATE ")"
-#define NKF_GEM_VERSION "0.1.3"
-
-#include "ruby/ruby.h"
-#include "ruby/encoding.h"
-
-/* Replace nkf's getchar/putchar for variable modification */
-/* we never use getc, ungetc */
-
-#undef getc
-#undef ungetc
-#define getc(f) (input_ctr>=i_len?-1:input[input_ctr++])
-#define ungetc(c,f) input_ctr--
-
-#define INCSIZE 32
-#undef putchar
-#undef TRUE
-#undef FALSE
-#define putchar(c) rb_nkf_putchar(c)
-
-/* Input/Output pointers */
-
-static unsigned char *output;
-static unsigned char *input;
-static int input_ctr;
-static int i_len;
-static int output_ctr;
-static int o_len;
-static int incsize;
-
-static VALUE result;
-
-static int
-rb_nkf_putchar(unsigned int c)
-{
- if (output_ctr >= o_len) {
- o_len += incsize;
- rb_str_resize(result, o_len);
- incsize *= 2;
- output = (unsigned char *)RSTRING_PTR(result);
- }
- output[output_ctr++] = c;
-
- return c;
-}
-
-/* Include kanji filter main part */
-/* getchar and putchar will be replaced during inclusion */
-
-#define PERL_XS 1
-#include "nkf-utf8/config.h"
-#include "nkf-utf8/utf8tbl.c"
-#include "nkf-utf8/nkf.c"
-
-rb_encoding* rb_nkf_enc_get(const char *name)
-{
- int idx = rb_enc_find_index(name);
- if (idx < 0) {
- nkf_encoding *nkf_enc = nkf_enc_find(name);
- idx = rb_enc_find_index(nkf_enc_name(nkf_enc_to_base_encoding(nkf_enc)));
- if (idx < 0) {
- idx = rb_define_dummy_encoding(name);
- }
- }
- return rb_enc_from_index(idx);
-}
-
-int nkf_split_options(const char *arg)
-{
- int count = 0;
- unsigned char option[256];
- int i = 0, j = 0;
- int is_escaped = FALSE;
- int is_single_quoted = FALSE;
- int is_double_quoted = FALSE;
- for(i = 0; arg[i]; i++){
- if(j == 255){
- return -1;
- }else if(is_single_quoted){
- if(arg[i] == '\''){
- is_single_quoted = FALSE;
- }else{
- option[j++] = arg[i];
- }
- }else if(is_escaped){
- is_escaped = FALSE;
- option[j++] = arg[i];
- }else if(arg[i] == '\\'){
- is_escaped = TRUE;
- }else if(is_double_quoted){
- if(arg[i] == '"'){
- is_double_quoted = FALSE;
- }else{
- option[j++] = arg[i];
- }
- }else if(arg[i] == '\''){
- is_single_quoted = TRUE;
- }else if(arg[i] == '"'){
- is_double_quoted = TRUE;
- }else if(arg[i] == ' '){
- option[j] = '\0';
- options(option);
- j = 0;
- }else{
- option[j++] = arg[i];
- }
- }
- if(j){
- option[j] = '\0';
- options(option);
- }
- return count;
-}
-
-/*
- * call-seq:
- * NKF.nkf(opt, str) => string
- *
- * Convert _str_ and return converted result.
- * Conversion details are specified by _opt_ as String.
- *
- * require 'nkf'
- * output = NKF.nkf("-s", input)
- */
-
-static VALUE
-rb_nkf_convert(VALUE obj, VALUE opt, VALUE src)
-{
- VALUE tmp;
- reinit();
- nkf_split_options(StringValueCStr(opt));
- if (!output_encoding) rb_raise(rb_eArgError, "no output encoding given");
-
- switch (nkf_enc_to_index(output_encoding)) {
- case UTF_8_BOM: output_encoding = nkf_enc_from_index(UTF_8); break;
- case UTF_16BE_BOM: output_encoding = nkf_enc_from_index(UTF_16BE); break;
- case UTF_16LE_BOM: output_encoding = nkf_enc_from_index(UTF_16LE); break;
- case UTF_32BE_BOM: output_encoding = nkf_enc_from_index(UTF_32BE); break;
- case UTF_32LE_BOM: output_encoding = nkf_enc_from_index(UTF_32LE); break;
- }
- output_bom_f = FALSE;
-
- incsize = INCSIZE;
-
- input_ctr = 0;
- input = (unsigned char *)StringValuePtr(src);
- i_len = RSTRING_LENINT(src);
- tmp = rb_str_new(0, i_len*3 + 10);
-
- output_ctr = 0;
- output = (unsigned char *)RSTRING_PTR(tmp);
- o_len = RSTRING_LENINT(tmp);
- *output = '\0';
-
- /* use _result_ begin*/
- result = tmp;
- kanji_convert(NULL);
- result = Qnil;
- /* use _result_ end */
-
- rb_str_set_len(tmp, output_ctr);
-
- if (mimeout_f)
- rb_enc_associate(tmp, rb_usascii_encoding());
- else
- rb_enc_associate(tmp, rb_nkf_enc_get(nkf_enc_name(output_encoding)));
-
- return tmp;
-}
-
-
-/*
- * call-seq:
- * NKF.guess(str) => encoding
- *
- * Returns guessed encoding of _str_ by nkf routine.
- *
- */
-
-static VALUE
-rb_nkf_guess(VALUE obj, VALUE src)
-{
- reinit();
-
- input_ctr = 0;
- input = (unsigned char *)StringValuePtr(src);
- i_len = RSTRING_LENINT(src);
-
- guess_f = TRUE;
- kanji_convert( NULL );
- guess_f = FALSE;
-
- return rb_enc_from_encoding(rb_nkf_enc_get(get_guessed_code()));
-}
-
-
-/*
- * NKF - Ruby extension for Network Kanji Filter
- *
- * == Description
- *
- * This is a Ruby Extension version of nkf (Network Kanji Filter).
- * It converts the first argument and returns converted result. Conversion
- * details are specified by flags as the first argument.
- *
- * *Nkf* is a yet another kanji code converter among networks, hosts and terminals.
- * It converts input kanji code to designated kanji code
- * such as ISO-2022-JP, Shift_JIS, EUC-JP, UTF-8 or UTF-16.
- *
- * One of the most unique faculty of *nkf* is the guess of the input kanji encodings.
- * It currently recognizes ISO-2022-JP, Shift_JIS, EUC-JP, UTF-8 and UTF-16.
- * So users needn't set the input kanji code explicitly.
- *
- * By default, X0201 kana is converted into X0208 kana.
- * For X0201 kana, SO/SI, SSO and ESC-(-I methods are supported.
- * For automatic code detection, nkf assumes no X0201 kana in Shift_JIS.
- * To accept X0201 in Shift_JIS, use <b>-X</b>, <b>-x</b> or <b>-S</b>.
- *
- * == Flags
- *
- * === -b -u
- *
- * Output is buffered (DEFAULT), Output is unbuffered.
- *
- * === -j -s -e -w -w16 -w32
- *
- * Output code is ISO-2022-JP (7bit JIS), Shift_JIS, EUC-JP,
- * UTF-8N, UTF-16BE, UTF-32BE.
- * Without this option and compile option, ISO-2022-JP is assumed.
- *
- * === -J -S -E -W -W16 -W32
- *
- * Input assumption is JIS 7 bit, Shift_JIS, EUC-JP,
- * UTF-8, UTF-16, UTF-32.
- *
- * ==== -J
- *
- * Assume JIS input. It also accepts EUC-JP.
- * This is the default. This flag does not exclude Shift_JIS.
- *
- * ==== -S
- *
- * Assume Shift_JIS and X0201 kana input. It also accepts JIS.
- * EUC-JP is recognized as X0201 kana. Without <b>-x</b> flag,
- * X0201 kana (halfwidth kana) is converted into X0208.
- *
- * ==== -E
- *
- * Assume EUC-JP input. It also accepts JIS.
- * Same as -J.
- *
- * === -t
- *
- * No conversion.
- *
- * === -i_
- *
- * Output sequence to designate JIS-kanji. (DEFAULT B)
- *
- * === -o_
- *
- * Output sequence to designate ASCII. (DEFAULT B)
- *
- * === -r
- *
- * {de/en}crypt ROT13/47
- *
- * === \-h[123] --hiragana --katakana --katakana-hiragana
- *
- * [-h1 --hiragana] Katakana to Hiragana conversion.
- *
- * [-h2 --katakana] Hiragana to Katakana conversion.
- *
- * [-h3 --katakana-hiragana] Katakana to Hiragana and Hiragana to Katakana conversion.
- *
- * === -T
- *
- * Text mode output (MS-DOS)
- *
- * === -l
- *
- * ISO8859-1 (Latin-1) support
- *
- * === -f[<code>m</code> [- <code>n</code>]]
- *
- * Folding on <code>m</code> length with <code>n</code> margin in a line.
- * Without this option, fold length is 60 and fold margin is 10.
- *
- * === -F
- *
- * New line preserving line folding.
- *
- * === \-Z[0-3]
- *
- * Convert X0208 alphabet (Fullwidth Alphabets) to ASCII.
- *
- * [-Z -Z0] Convert X0208 alphabet to ASCII.
- *
- * [-Z1] Converts X0208 kankaku to single ASCII space.
- *
- * [-Z2] Converts X0208 kankaku to double ASCII spaces.
- *
- * [-Z3] Replacing Fullwidth >, <, ", & into '&gt;', '&lt;', '&quot;', '&amp;' as in HTML.
- *
- * === -X -x
- *
- * Assume X0201 kana in MS-Kanji.
- * With <b>-X</b> or without this option, X0201 is converted into X0208 Kana.
- * With <b>-x</b>, try to preserve X0208 kana and do not convert X0201 kana to X0208.
- * In JIS output, ESC-(-I is used. In EUC output, SSO is used.
- *
- * === \-B[0-2]
- *
- * Assume broken JIS-Kanji input, which lost ESC.
- * Useful when your site is using old B-News Nihongo patch.
- *
- * [-B1] allows any char after ESC-( or ESC-$.
- *
- * [-B2] forces ASCII after NL.
- *
- * === -I
- *
- * Replacing non iso-2022-jp char into a geta character
- * (substitute character in Japanese).
- *
- * === -d -c
- *
- * Delete \r in line feed, Add \r in line feed.
- *
- * === \-m[BQN0]
- *
- * MIME ISO-2022-JP/ISO8859-1 decode. (DEFAULT)
- * To see ISO8859-1 (Latin-1) -l is necessary.
- *
- * [-mB] Decode MIME base64 encoded stream. Remove header or other part before
- * conversion.
- *
- * [-mQ] Decode MIME quoted stream. '_' in quoted stream is converted to space.
- *
- * [-mN] Non-strict decoding.
- * It allows line break in the middle of the base64 encoding.
- *
- * [-m0] No MIME decode.
- *
- * === -M
- *
- * MIME encode. Header style. All ASCII code and control characters are intact.
- * Kanji conversion is performed before encoding, so this cannot be used as a picture encoder.
- *
- * [-MB] MIME encode Base64 stream.
- *
- * [-MQ] Perform quoted encoding.
- *
- * === -l
- *
- * Input and output code is ISO8859-1 (Latin-1) and ISO-2022-JP.
- * <b>-s</b>, <b>-e</b> and <b>-x</b> are not compatible with this option.
- *
- * === \-L[uwm]
- *
- * new line mode
- * Without this option, nkf doesn't convert line breaks.
- *
- * [-Lu] unix (LF)
- *
- * [-Lw] windows (CRLF)
- *
- * [-Lm] mac (CR)
- *
- * === --fj --unix --mac --msdos --windows
- *
- * convert for these system
- *
- * === --jis --euc --sjis --mime --base64
- *
- * convert for named code
- *
- * === --jis-input --euc-input --sjis-input --mime-input --base64-input
- *
- * assume input system
- *
- * === --ic=<code>input codeset</code> --oc=<code>output codeset</code>
- *
- * Set the input or output codeset.
- * NKF supports following codesets and those codeset name are case insensitive.
- *
- * [ISO-2022-JP] a.k.a. RFC1468, 7bit JIS, JUNET
- *
- * [EUC-JP (eucJP-nkf)] a.k.a. AT&T JIS, Japanese EUC, UJIS
- *
- * [eucJP-ascii] a.k.a. x-eucjp-open-19970715-ascii
- *
- * [eucJP-ms] a.k.a. x-eucjp-open-19970715-ms
- *
- * [CP51932] Microsoft Version of EUC-JP.
- *
- * [Shift_JIS] SJIS, MS-Kanji
- *
- * [Windows-31J] a.k.a. CP932
- *
- * [UTF-8] same as UTF-8N
- *
- * [UTF-8N] UTF-8 without BOM
- *
- * [UTF-8-BOM] UTF-8 with BOM
- *
- * [UTF-16] same as UTF-16BE
- *
- * [UTF-16BE] UTF-16 Big Endian without BOM
- *
- * [UTF-16BE-BOM] UTF-16 Big Endian with BOM
- *
- * [UTF-16LE] UTF-16 Little Endian without BOM
- *
- * [UTF-16LE-BOM] UTF-16 Little Endian with BOM
- *
- * [UTF-32] same as UTF-32BE
- *
- * [UTF-32BE] UTF-32 Big Endian without BOM
- *
- * [UTF-32BE-BOM] UTF-32 Big Endian with BOM
- *
- * [UTF-32LE] UTF-32 Little Endian without BOM
- *
- * [UTF-32LE-BOM] UTF-32 Little Endian with BOM
- *
- * [UTF8-MAC] NKDed UTF-8, a.k.a. UTF8-NFD (input only)
- *
- * === --fb-{skip, html, xml, perl, java, subchar}
- *
- * Specify the way that nkf handles unassigned characters.
- * Without this option, --fb-skip is assumed.
- *
- * === --prefix= <code>escape character</code> <code>target character</code> ..
- *
- * When nkf converts to Shift_JIS,
- * nkf adds a specified escape character to specified 2nd byte of Shift_JIS characters.
- * 1st byte of argument is the escape character and following bytes are target characters.
- *
- * === --no-cp932ext
- *
- * Handle the characters extended in CP932 as unassigned characters.
- *
- * == --no-best-fit-chars
- *
- * When Unicode to Encoded byte conversion,
- * don't convert characters which is not round trip safe.
- * When Unicode to Unicode conversion,
- * with this and -x option, nkf can be used as UTF converter.
- * (In other words, without this and -x option, nkf doesn't save some characters)
- *
- * When nkf convert string which related to path, you should use this option.
- *
- * === --cap-input
- *
- * Decode hex encoded characters.
- *
- * === --url-input
- *
- * Unescape percent escaped characters.
- *
- * === --
- *
- * Ignore rest of -option.
- */
-
-void
-Init_nkf(void)
-{
- VALUE mNKF = rb_define_module("NKF");
-
- rb_define_module_function(mNKF, "nkf", rb_nkf_convert, 2);
- rb_define_module_function(mNKF, "guess", rb_nkf_guess, 1);
- rb_define_alias(rb_singleton_class(mNKF), "guess", "guess");
-
- rb_define_const(mNKF, "AUTO", Qnil);
- rb_define_const(mNKF, "NOCONV", Qnil);
- rb_define_const(mNKF, "UNKNOWN", Qnil);
- rb_define_const(mNKF, "BINARY", rb_enc_from_encoding(rb_nkf_enc_get("BINARY")));
- rb_define_const(mNKF, "ASCII", rb_enc_from_encoding(rb_nkf_enc_get("US-ASCII")));
- rb_define_const(mNKF, "JIS", rb_enc_from_encoding(rb_nkf_enc_get("ISO-2022-JP")));
- rb_define_const(mNKF, "EUC", rb_enc_from_encoding(rb_nkf_enc_get("EUC-JP")));
- rb_define_const(mNKF, "SJIS", rb_enc_from_encoding(rb_nkf_enc_get("Shift_JIS")));
- rb_define_const(mNKF, "UTF8", rb_enc_from_encoding(rb_utf8_encoding()));
- rb_define_const(mNKF, "UTF16", rb_enc_from_encoding(rb_nkf_enc_get("UTF-16BE")));
- rb_define_const(mNKF, "UTF32", rb_enc_from_encoding(rb_nkf_enc_get("UTF-32BE")));
-
- /* Full version string of nkf */
- rb_define_const(mNKF, "VERSION", rb_str_new2(RUBY_NKF_VERSION));
- /* Version of nkf */
- rb_define_const(mNKF, "NKF_VERSION", rb_str_new2(NKF_VERSION));
- /* Release date of nkf */
- rb_define_const(mNKF, "NKF_RELEASE_DATE", rb_str_new2(NKF_RELEASE_DATE));
- /* Version of nkf library */
- rb_define_const(mNKF, "GEM_VERSION", rb_str_new_cstr(NKF_GEM_VERSION));
-}
diff --git a/ext/nkf/nkf.gemspec b/ext/nkf/nkf.gemspec
deleted file mode 100644
index 097a9485ed..0000000000
--- a/ext/nkf/nkf.gemspec
+++ /dev/null
@@ -1,35 +0,0 @@
-source_version = ["", "ext/nkf/"].find do |dir|
- begin
- break File.open(File.join(__dir__, "#{dir}nkf.c")) {|f|
- f.gets("\n#define NKF_GEM_VERSION ")
- f.gets[/\s*"(.+)"/, 1]
- }
- rescue Errno::ENOENT
- end
-end
-
-Gem::Specification.new do |spec|
- spec.name = "nkf"
- spec.version = source_version
- spec.authors = ["NARUSE Yui"]
- spec.email = ["naruse@airemix.jp"]
-
- spec.summary = %q{Ruby extension for Network Kanji Filter}
- spec.description = %q{Ruby extension for Network Kanji Filter}
- spec.homepage = "https://github.com/ruby/nkf"
- spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
- spec.licenses = ["Ruby", "BSD-2-Clause"]
-
- spec.metadata["homepage_uri"] = spec.homepage
- spec.metadata["source_code_uri"] = spec.homepage
-
- # Specify which files should be added to the gem when it is released.
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
- end
- spec.bindir = "exe"
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
- spec.require_paths = ["lib"]
- spec.extensions = ["ext/nkf/extconf.rb"]
-end
diff --git a/ext/objspace/depend b/ext/objspace/depend
index d1e8236eb8..a02168b06a 100644
--- a/ext/objspace/depend
+++ b/ext/objspace/depend
@@ -15,6 +15,7 @@ object_tracing.o: $(hdrdir)/ruby/backward/2/stdalign.h
object_tracing.o: $(hdrdir)/ruby/backward/2/stdarg.h
object_tracing.o: $(hdrdir)/ruby/debug.h
object_tracing.o: $(hdrdir)/ruby/defines.h
+object_tracing.o: $(hdrdir)/ruby/encoding.h
object_tracing.o: $(hdrdir)/ruby/intern.h
object_tracing.o: $(hdrdir)/ruby/internal/abi.h
object_tracing.o: $(hdrdir)/ruby/internal/anyargs.h
@@ -87,6 +88,15 @@ object_tracing.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
object_tracing.o: $(hdrdir)/ruby/internal/ctype.h
object_tracing.o: $(hdrdir)/ruby/internal/dllexport.h
object_tracing.o: $(hdrdir)/ruby/internal/dosish.h
+object_tracing.o: $(hdrdir)/ruby/internal/encoding/coderange.h
+object_tracing.o: $(hdrdir)/ruby/internal/encoding/ctype.h
+object_tracing.o: $(hdrdir)/ruby/internal/encoding/encoding.h
+object_tracing.o: $(hdrdir)/ruby/internal/encoding/pathname.h
+object_tracing.o: $(hdrdir)/ruby/internal/encoding/re.h
+object_tracing.o: $(hdrdir)/ruby/internal/encoding/sprintf.h
+object_tracing.o: $(hdrdir)/ruby/internal/encoding/string.h
+object_tracing.o: $(hdrdir)/ruby/internal/encoding/symbol.h
+object_tracing.o: $(hdrdir)/ruby/internal/encoding/transcode.h
object_tracing.o: $(hdrdir)/ruby/internal/error.h
object_tracing.o: $(hdrdir)/ruby/internal/eval.h
object_tracing.o: $(hdrdir)/ruby/internal/event.h
@@ -149,6 +159,7 @@ object_tracing.o: $(hdrdir)/ruby/internal/special_consts.h
object_tracing.o: $(hdrdir)/ruby/internal/static_assert.h
object_tracing.o: $(hdrdir)/ruby/internal/stdalign.h
object_tracing.o: $(hdrdir)/ruby/internal/stdbool.h
+object_tracing.o: $(hdrdir)/ruby/internal/stdckdint.h
object_tracing.o: $(hdrdir)/ruby/internal/symbol.h
object_tracing.o: $(hdrdir)/ruby/internal/value.h
object_tracing.o: $(hdrdir)/ruby/internal/value_type.h
@@ -156,6 +167,8 @@ object_tracing.o: $(hdrdir)/ruby/internal/variable.h
object_tracing.o: $(hdrdir)/ruby/internal/warning_push.h
object_tracing.o: $(hdrdir)/ruby/internal/xmalloc.h
object_tracing.o: $(hdrdir)/ruby/missing.h
+object_tracing.o: $(hdrdir)/ruby/onigmo.h
+object_tracing.o: $(hdrdir)/ruby/oniguruma.h
object_tracing.o: $(hdrdir)/ruby/ruby.h
object_tracing.o: $(hdrdir)/ruby/st.h
object_tracing.o: $(hdrdir)/ruby/subst.h
@@ -170,6 +183,7 @@ object_tracing.o: $(top_srcdir)/internal/basic_operators.h
object_tracing.o: $(top_srcdir)/internal/compilers.h
object_tracing.o: $(top_srcdir)/internal/gc.h
object_tracing.o: $(top_srcdir)/internal/imemo.h
+object_tracing.o: $(top_srcdir)/internal/sanitizers.h
object_tracing.o: $(top_srcdir)/internal/serial.h
object_tracing.o: $(top_srcdir)/internal/static_assert.h
object_tracing.o: $(top_srcdir)/internal/vm.h
@@ -178,6 +192,7 @@ object_tracing.o: $(top_srcdir)/method.h
object_tracing.o: $(top_srcdir)/node.h
object_tracing.o: $(top_srcdir)/ruby_assert.h
object_tracing.o: $(top_srcdir)/ruby_atomic.h
+object_tracing.o: $(top_srcdir)/rubyparser.h
object_tracing.o: $(top_srcdir)/thread_pthread.h
object_tracing.o: $(top_srcdir)/vm_core.h
object_tracing.o: $(top_srcdir)/vm_opts.h
@@ -344,6 +359,7 @@ objspace.o: $(hdrdir)/ruby/internal/special_consts.h
objspace.o: $(hdrdir)/ruby/internal/static_assert.h
objspace.o: $(hdrdir)/ruby/internal/stdalign.h
objspace.o: $(hdrdir)/ruby/internal/stdbool.h
+objspace.o: $(hdrdir)/ruby/internal/stdckdint.h
objspace.o: $(hdrdir)/ruby/internal/symbol.h
objspace.o: $(hdrdir)/ruby/internal/value.h
objspace.o: $(hdrdir)/ruby/internal/value_type.h
@@ -365,11 +381,7 @@ objspace.o: $(top_srcdir)/ccan/container_of/container_of.h
objspace.o: $(top_srcdir)/ccan/list/list.h
objspace.o: $(top_srcdir)/ccan/str/str.h
objspace.o: $(top_srcdir)/constant.h
-objspace.o: $(hdrdir)/ruby/thread_native.h
-objspace.o: $(top_srcdir)/ccan/check_type/check_type.h
-objspace.o: $(top_srcdir)/ccan/container_of/container_of.h
-objspace.o: $(top_srcdir)/ccan/list/list.h
-objspace.o: $(top_srcdir)/ccan/str/str.h
+objspace.o: $(top_srcdir)/debug_counter.h
objspace.o: $(top_srcdir)/id_table.h
objspace.o: $(top_srcdir)/internal.h
objspace.o: $(top_srcdir)/internal/array.h
@@ -389,11 +401,14 @@ objspace.o: $(top_srcdir)/method.h
objspace.o: $(top_srcdir)/node.h
objspace.o: $(top_srcdir)/ruby_assert.h
objspace.o: $(top_srcdir)/ruby_atomic.h
+objspace.o: $(top_srcdir)/rubyparser.h
objspace.o: $(top_srcdir)/shape.h
objspace.o: $(top_srcdir)/symbol.h
objspace.o: $(top_srcdir)/thread_pthread.h
objspace.o: $(top_srcdir)/vm_core.h
+objspace.o: $(top_srcdir)/vm_debug.h
objspace.o: $(top_srcdir)/vm_opts.h
+objspace.o: $(top_srcdir)/vm_sync.h
objspace.o: objspace.c
objspace.o: {$(VPATH)}id.h
objspace_dump.o: $(RUBY_EXTCONF_H)
@@ -556,6 +571,7 @@ objspace_dump.o: $(hdrdir)/ruby/internal/special_consts.h
objspace_dump.o: $(hdrdir)/ruby/internal/static_assert.h
objspace_dump.o: $(hdrdir)/ruby/internal/stdalign.h
objspace_dump.o: $(hdrdir)/ruby/internal/stdbool.h
+objspace_dump.o: $(hdrdir)/ruby/internal/stdckdint.h
objspace_dump.o: $(hdrdir)/ruby/internal/symbol.h
objspace_dump.o: $(hdrdir)/ruby/internal/value.h
objspace_dump.o: $(hdrdir)/ruby/internal/value_type.h
@@ -576,6 +592,7 @@ objspace_dump.o: $(top_srcdir)/ccan/container_of/container_of.h
objspace_dump.o: $(top_srcdir)/ccan/list/list.h
objspace_dump.o: $(top_srcdir)/ccan/str/str.h
objspace_dump.o: $(top_srcdir)/constant.h
+objspace_dump.o: $(top_srcdir)/debug_counter.h
objspace_dump.o: $(top_srcdir)/id_table.h
objspace_dump.o: $(top_srcdir)/internal.h
objspace_dump.o: $(top_srcdir)/internal/array.h
@@ -585,6 +602,7 @@ objspace_dump.o: $(top_srcdir)/internal/compilers.h
objspace_dump.o: $(top_srcdir)/internal/gc.h
objspace_dump.o: $(top_srcdir)/internal/hash.h
objspace_dump.o: $(top_srcdir)/internal/imemo.h
+objspace_dump.o: $(top_srcdir)/internal/io.h
objspace_dump.o: $(top_srcdir)/internal/sanitizers.h
objspace_dump.o: $(top_srcdir)/internal/serial.h
objspace_dump.o: $(top_srcdir)/internal/static_assert.h
@@ -596,11 +614,15 @@ objspace_dump.o: $(top_srcdir)/method.h
objspace_dump.o: $(top_srcdir)/node.h
objspace_dump.o: $(top_srcdir)/ruby_assert.h
objspace_dump.o: $(top_srcdir)/ruby_atomic.h
+objspace_dump.o: $(top_srcdir)/rubyparser.h
objspace_dump.o: $(top_srcdir)/shape.h
objspace_dump.o: $(top_srcdir)/symbol.h
objspace_dump.o: $(top_srcdir)/thread_pthread.h
+objspace_dump.o: $(top_srcdir)/vm_callinfo.h
objspace_dump.o: $(top_srcdir)/vm_core.h
+objspace_dump.o: $(top_srcdir)/vm_debug.h
objspace_dump.o: $(top_srcdir)/vm_opts.h
+objspace_dump.o: $(top_srcdir)/vm_sync.h
objspace_dump.o: objspace.h
objspace_dump.o: objspace_dump.c
objspace_dump.o: {$(VPATH)}id.h
diff --git a/ext/objspace/objspace.c b/ext/objspace/objspace.c
index e8762d1be6..24d7bd419f 100644
--- a/ext/objspace/objspace.c
+++ b/ext/objspace/objspace.c
@@ -19,7 +19,6 @@
#include "internal/hash.h"
#include "internal/imemo.h"
#include "internal/sanitizers.h"
-#include "node.h"
#include "ruby/io.h"
#include "ruby/re.h"
#include "ruby/st.h"
@@ -170,8 +169,7 @@ setup_hash(int argc, VALUE *argv)
hash = rb_hash_new();
}
else if (!RHASH_EMPTY_P(hash)) {
- /* WB: no new reference */
- st_foreach(RHASH_TBL_RAW(hash), set_zero_i, hash);
+ rb_hash_foreach(hash, set_zero_i, (st_data_t)hash);
}
return hash;
@@ -337,17 +335,6 @@ count_symbols(int argc, VALUE *argv, VALUE os)
return hash;
}
-static void
-cn_i(VALUE v, void *n)
-{
- size_t *nodes = (size_t *)n;
-
- if (BUILTIN_TYPE(v) == T_NODE) {
- size_t s = nd_type((NODE *)v);
- nodes[s]++;
- }
-}
-
/*
* call-seq:
* ObjectSpace.count_nodes([result_hash]) -> hash
@@ -374,135 +361,7 @@ cn_i(VALUE v, void *n)
static VALUE
count_nodes(int argc, VALUE *argv, VALUE os)
{
- size_t nodes[NODE_LAST+1];
- enum node_type i;
- VALUE hash = setup_hash(argc, argv);
-
- for (i = 0; i <= NODE_LAST; i++) {
- nodes[i] = 0;
- }
-
- each_object_with_flags(cn_i, &nodes[0]);
-
- for (i=0; i<NODE_LAST; i++) {
- if (nodes[i] != 0) {
- VALUE node;
- switch (i) {
-#define COUNT_NODE(n) case n: node = ID2SYM(rb_intern(#n)); goto set
- COUNT_NODE(NODE_SCOPE);
- COUNT_NODE(NODE_BLOCK);
- COUNT_NODE(NODE_IF);
- COUNT_NODE(NODE_UNLESS);
- COUNT_NODE(NODE_CASE);
- COUNT_NODE(NODE_CASE2);
- COUNT_NODE(NODE_CASE3);
- COUNT_NODE(NODE_WHEN);
- COUNT_NODE(NODE_IN);
- COUNT_NODE(NODE_WHILE);
- COUNT_NODE(NODE_UNTIL);
- COUNT_NODE(NODE_ITER);
- COUNT_NODE(NODE_FOR);
- COUNT_NODE(NODE_FOR_MASGN);
- COUNT_NODE(NODE_BREAK);
- COUNT_NODE(NODE_NEXT);
- COUNT_NODE(NODE_REDO);
- COUNT_NODE(NODE_RETRY);
- COUNT_NODE(NODE_BEGIN);
- COUNT_NODE(NODE_RESCUE);
- COUNT_NODE(NODE_RESBODY);
- COUNT_NODE(NODE_ENSURE);
- COUNT_NODE(NODE_AND);
- COUNT_NODE(NODE_OR);
- COUNT_NODE(NODE_MASGN);
- COUNT_NODE(NODE_LASGN);
- COUNT_NODE(NODE_DASGN);
- COUNT_NODE(NODE_GASGN);
- COUNT_NODE(NODE_IASGN);
- COUNT_NODE(NODE_CDECL);
- COUNT_NODE(NODE_CVASGN);
- COUNT_NODE(NODE_OP_ASGN1);
- COUNT_NODE(NODE_OP_ASGN2);
- COUNT_NODE(NODE_OP_ASGN_AND);
- COUNT_NODE(NODE_OP_ASGN_OR);
- COUNT_NODE(NODE_OP_CDECL);
- COUNT_NODE(NODE_CALL);
- COUNT_NODE(NODE_OPCALL);
- COUNT_NODE(NODE_FCALL);
- COUNT_NODE(NODE_VCALL);
- COUNT_NODE(NODE_QCALL);
- COUNT_NODE(NODE_SUPER);
- COUNT_NODE(NODE_ZSUPER);
- COUNT_NODE(NODE_LIST);
- COUNT_NODE(NODE_ZLIST);
- COUNT_NODE(NODE_VALUES);
- COUNT_NODE(NODE_HASH);
- COUNT_NODE(NODE_RETURN);
- COUNT_NODE(NODE_YIELD);
- COUNT_NODE(NODE_LVAR);
- COUNT_NODE(NODE_DVAR);
- COUNT_NODE(NODE_GVAR);
- COUNT_NODE(NODE_IVAR);
- COUNT_NODE(NODE_CONST);
- COUNT_NODE(NODE_CVAR);
- COUNT_NODE(NODE_NTH_REF);
- COUNT_NODE(NODE_BACK_REF);
- COUNT_NODE(NODE_MATCH);
- COUNT_NODE(NODE_MATCH2);
- COUNT_NODE(NODE_MATCH3);
- COUNT_NODE(NODE_LIT);
- COUNT_NODE(NODE_STR);
- COUNT_NODE(NODE_DSTR);
- COUNT_NODE(NODE_XSTR);
- COUNT_NODE(NODE_DXSTR);
- COUNT_NODE(NODE_EVSTR);
- COUNT_NODE(NODE_DREGX);
- COUNT_NODE(NODE_ONCE);
- COUNT_NODE(NODE_ARGS);
- COUNT_NODE(NODE_ARGS_AUX);
- COUNT_NODE(NODE_OPT_ARG);
- COUNT_NODE(NODE_KW_ARG);
- COUNT_NODE(NODE_POSTARG);
- COUNT_NODE(NODE_ARGSCAT);
- COUNT_NODE(NODE_ARGSPUSH);
- COUNT_NODE(NODE_SPLAT);
- COUNT_NODE(NODE_BLOCK_PASS);
- COUNT_NODE(NODE_DEFN);
- COUNT_NODE(NODE_DEFS);
- COUNT_NODE(NODE_ALIAS);
- COUNT_NODE(NODE_VALIAS);
- COUNT_NODE(NODE_UNDEF);
- COUNT_NODE(NODE_CLASS);
- COUNT_NODE(NODE_MODULE);
- COUNT_NODE(NODE_SCLASS);
- COUNT_NODE(NODE_COLON2);
- COUNT_NODE(NODE_COLON3);
- COUNT_NODE(NODE_DOT2);
- COUNT_NODE(NODE_DOT3);
- COUNT_NODE(NODE_FLIP2);
- COUNT_NODE(NODE_FLIP3);
- COUNT_NODE(NODE_SELF);
- COUNT_NODE(NODE_NIL);
- COUNT_NODE(NODE_TRUE);
- COUNT_NODE(NODE_FALSE);
- COUNT_NODE(NODE_ERRINFO);
- COUNT_NODE(NODE_DEFINED);
- COUNT_NODE(NODE_POSTEXE);
- COUNT_NODE(NODE_DSYM);
- COUNT_NODE(NODE_ATTRASGN);
- COUNT_NODE(NODE_LAMBDA);
- COUNT_NODE(NODE_ARYPTN);
- COUNT_NODE(NODE_FNDPTN);
- COUNT_NODE(NODE_HSHPTN);
- COUNT_NODE(NODE_ERROR);
-#undef COUNT_NODE
- case NODE_LAST: break;
- }
- UNREACHABLE;
- set:
- rb_hash_aset(hash, node, SIZET2NUM(nodes[i]));
- }
- }
- return hash;
+ return setup_hash(argc, argv);
}
static void
@@ -764,7 +623,7 @@ collect_values(st_data_t key, st_data_t value, st_data_t data)
*
* With this method, you can find memory leaks.
*
- * This method is only expected to work except with C Ruby.
+ * This method is only expected to work with C Ruby.
*
* Example:
* ObjectSpace.reachable_objects_from(['a', 'b', 'c'])
diff --git a/ext/objspace/objspace_dump.c b/ext/objspace/objspace_dump.c
index d8a11083d6..bb479b91c5 100644
--- a/ext/objspace/objspace_dump.c
+++ b/ext/objspace/objspace_dump.c
@@ -18,6 +18,7 @@
#include "internal/class.h"
#include "internal/gc.h"
#include "internal/hash.h"
+#include "internal/io.h"
#include "internal/string.h"
#include "internal/sanitizers.h"
#include "symbol.h"
@@ -27,6 +28,7 @@
#include "ruby/debug.h"
#include "ruby/util.h"
#include "ruby/io.h"
+#include "vm_callinfo.h"
#include "vm_core.h"
RUBY_EXTERN const char ruby_hexdigits[];
@@ -166,10 +168,8 @@ dump_append_c(struct dump_config *dc, unsigned char c)
}
static void
-dump_append_ref(struct dump_config *dc, VALUE ref)
+dump_append_ptr(struct dump_config *dc, VALUE ref)
{
- RUBY_ASSERT(ref > 0);
-
char buffer[roomof(sizeof(VALUE) * CHAR_BIT, 4) + rb_strlen_lit("\"0x\"")];
char *buffer_start, *buffer_end;
@@ -186,6 +186,14 @@ dump_append_ref(struct dump_config *dc, VALUE ref)
}
static void
+dump_append_ref(struct dump_config *dc, VALUE ref)
+{
+ RUBY_ASSERT(ref > 0);
+ dump_append_ptr(dc, ref);
+}
+
+
+static void
dump_append_string_value(struct dump_config *dc, VALUE obj)
{
long i;
@@ -358,8 +366,9 @@ dump_append_string_content(struct dump_config *dc, VALUE obj)
static inline void
dump_append_id(struct dump_config *dc, ID id)
{
- if (is_instance_id(id)) {
- dump_append_string_value(dc, rb_sym2str(ID2SYM(id)));
+ VALUE str = rb_sym2str(ID2SYM(id));
+ if (RTEST(str)) {
+ dump_append_string_value(dc, str);
}
else {
dump_append(dc, "\"ID_INTERNAL(");
@@ -377,6 +386,7 @@ dump_object(VALUE obj, struct dump_config *dc)
rb_io_t *fptr;
ID flags[RB_OBJ_GC_FLAGS_MAX];
size_t n, i;
+ ID mid;
if (SPECIAL_CONST_P(obj)) {
dump_append_special_const(dc, obj);
@@ -428,6 +438,33 @@ dump_object(VALUE obj, struct dump_config *dc)
dump_append(dc, ", \"imemo_type\":\"");
dump_append(dc, rb_imemo_name(imemo_type(obj)));
dump_append(dc, "\"");
+
+ switch (imemo_type(obj)) {
+ case imemo_callinfo:
+ mid = vm_ci_mid((const struct rb_callinfo *)obj);
+ if (mid != 0) {
+ dump_append(dc, ", \"mid\":");
+ dump_append_id(dc, mid);
+ }
+ break;
+
+ case imemo_callcache:
+ mid = vm_cc_cme((const struct rb_callcache *)obj)->called_id;
+ if (mid != 0) {
+ dump_append(dc, ", \"called_id\":");
+ dump_append_id(dc, mid);
+
+ VALUE klass = ((const struct rb_callcache *)obj)->klass;
+ if (klass != 0) {
+ dump_append(dc, ", \"receiver_class\":");
+ dump_append_ref(dc, klass);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
break;
case T_SYMBOL:
@@ -439,6 +476,8 @@ dump_object(VALUE obj, struct dump_config *dc)
dump_append(dc, ", \"embedded\":true");
if (FL_TEST(obj, RSTRING_FSTR))
dump_append(dc, ", \"fstring\":true");
+ if (CHILLED_STRING_P(obj))
+ dump_append(dc, ", \"chilled\":true");
if (STR_SHARED_P(obj))
dump_append(dc, ", \"shared\":true");
else
@@ -523,7 +562,7 @@ dump_object(VALUE obj, struct dump_config *dc)
}
}
- if (FL_TEST(obj, FL_SINGLETON)) {
+ if (RCLASS_SINGLETON_P(obj)) {
dump_append(dc, ", \"singleton\":true");
}
}
@@ -755,16 +794,6 @@ shape_i(rb_shape_t *shape, void *data)
case SHAPE_FROZEN:
dump_append(dc, "\"FROZEN\"");
break;
- case SHAPE_CAPACITY_CHANGE:
- dump_append(dc, "\"CAPACITY_CHANGE\"");
- dump_append(dc, ", \"capacity\":");
- dump_append_sizet(dc, shape->capacity);
- break;
- case SHAPE_INITIAL_CAPACITY:
- dump_append(dc, "\"INITIAL_CAPACITY\"");
- dump_append(dc, ", \"capacity\":");
- dump_append_sizet(dc, shape->capacity);
- break;
case SHAPE_T_OBJECT:
dump_append(dc, "\"T_OBJECT\"");
break;
diff --git a/ext/openssl/History.md b/ext/openssl/History.md
index 1e0df7dd87..3249f6617a 100644
--- a/ext/openssl/History.md
+++ b/ext/openssl/History.md
@@ -1,3 +1,43 @@
+Version 3.2.0
+=============
+
+Compatibility
+-------------
+
+* Ruby >= 2.7
+ - Support for Ruby 2.6 has been removed. Note that Ruby 2.6 reached the
+ end-of-life in 2022-04.
+ [[GitHub #639]](https://github.com/ruby/openssl/pull/639)
+* OpenSSL >= 1.0.2 or LibreSSL >= 3.1
+
+Notable changes
+---------------
+
+* Add a stub gemspec for JRuby, which depends on the `jruby-openssl` gem.
+ [[GitHub #598]](https://github.com/ruby/openssl/pull/598)
+* Add support for the FIPS module in OpenSSL 3.0/3.1.
+ [[GitHub #608]](https://github.com/ruby/openssl/pull/608)
+* Rework `OpenSSL::PKey` routines for loading DER or PEM encoded keys for better
+ compatibility with OpenSSL 3.0/3.1 with the FIPS module.
+ [[GitHub #615]](https://github.com/ruby/openssl/pull/615)
+ [[GitHub #669]](https://github.com/ruby/openssl/pull/669)
+* Add `OpenSSL::Provider` module for loading and unloading OpenSSL 3 providers.
+ [[GitHub #635]](https://github.com/ruby/openssl/pull/635)
+* Add `OpenSSL::PKey.new_raw_private_key`, `.new_raw_public_key`,
+ `OpenSSL::PKey::PKey#raw_private_key`, and `#raw_public_key` for public key
+ algorithms that use "raw private/public key", such as X25519 and Ed25519.
+ [[GitHub #646]](https://github.com/ruby/openssl/pull/646)
+* Improve OpenSSL error messages to include additional information when
+ it is available in OpenSSL's error queue.
+ [[GitHub #648]](https://github.com/ruby/openssl/pull/648)
+* Change `OpenSSL::SSL::SSLContext#ca_file=` and `#ca_path=` to raise
+ `OpenSSL::SSL::SSLError` instead of printing a warning message.
+ [[GitHub #659]](https://github.com/ruby/openssl/pull/659)
+* Allow `OpenSSL::X509::ExtensionFactory#create_extension` to take OIDs in the
+ dotted-decimal notation.
+ [[GitHub #141]](https://github.com/ruby/openssl/pull/141)
+
+
Version 3.1.0
=============
@@ -417,7 +457,7 @@ Security fixes
Bug fixes
---------
-* Fixed OpenSSL::PKey::*.{new,generate} immediately aborting if the thread is
+* Fixed OpenSSL::PKey::\*.{new,generate} immediately aborting if the thread is
interrupted.
[[Bug #14882]](https://bugs.ruby-lang.org/issues/14882)
[[GitHub #205]](https://github.com/ruby/openssl/pull/205)
diff --git a/ext/openssl/depend b/ext/openssl/depend
index e156edf38a..12c6793939 100644
--- a/ext/openssl/depend
+++ b/ext/openssl/depend
@@ -161,6 +161,7 @@ ossl.o: $(hdrdir)/ruby/internal/special_consts.h
ossl.o: $(hdrdir)/ruby/internal/static_assert.h
ossl.o: $(hdrdir)/ruby/internal/stdalign.h
ossl.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl.o: $(hdrdir)/ruby/internal/symbol.h
ossl.o: $(hdrdir)/ruby/internal/value.h
ossl.o: $(hdrdir)/ruby/internal/value_type.h
@@ -192,6 +193,7 @@ ossl.o: ossl_ocsp.h
ossl.o: ossl_pkcs12.h
ossl.o: ossl_pkcs7.h
ossl.o: ossl_pkey.h
+ossl.o: ossl_provider.h
ossl.o: ossl_rand.h
ossl.o: ossl_ssl.h
ossl.o: ossl_ts.h
@@ -354,6 +356,7 @@ ossl_asn1.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_asn1.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_asn1.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_asn1.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_asn1.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_asn1.o: $(hdrdir)/ruby/internal/symbol.h
ossl_asn1.o: $(hdrdir)/ruby/internal/value.h
ossl_asn1.o: $(hdrdir)/ruby/internal/value_type.h
@@ -385,6 +388,7 @@ ossl_asn1.o: ossl_ocsp.h
ossl_asn1.o: ossl_pkcs12.h
ossl_asn1.o: ossl_pkcs7.h
ossl_asn1.o: ossl_pkey.h
+ossl_asn1.o: ossl_provider.h
ossl_asn1.o: ossl_rand.h
ossl_asn1.o: ossl_ssl.h
ossl_asn1.o: ossl_ts.h
@@ -547,6 +551,7 @@ ossl_bio.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_bio.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_bio.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_bio.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_bio.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_bio.o: $(hdrdir)/ruby/internal/symbol.h
ossl_bio.o: $(hdrdir)/ruby/internal/value.h
ossl_bio.o: $(hdrdir)/ruby/internal/value_type.h
@@ -578,6 +583,7 @@ ossl_bio.o: ossl_ocsp.h
ossl_bio.o: ossl_pkcs12.h
ossl_bio.o: ossl_pkcs7.h
ossl_bio.o: ossl_pkey.h
+ossl_bio.o: ossl_provider.h
ossl_bio.o: ossl_rand.h
ossl_bio.o: ossl_ssl.h
ossl_bio.o: ossl_ts.h
@@ -740,6 +746,7 @@ ossl_bn.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_bn.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_bn.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_bn.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_bn.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_bn.o: $(hdrdir)/ruby/internal/symbol.h
ossl_bn.o: $(hdrdir)/ruby/internal/value.h
ossl_bn.o: $(hdrdir)/ruby/internal/value_type.h
@@ -772,6 +779,7 @@ ossl_bn.o: ossl_ocsp.h
ossl_bn.o: ossl_pkcs12.h
ossl_bn.o: ossl_pkcs7.h
ossl_bn.o: ossl_pkey.h
+ossl_bn.o: ossl_provider.h
ossl_bn.o: ossl_rand.h
ossl_bn.o: ossl_ssl.h
ossl_bn.o: ossl_ts.h
@@ -934,6 +942,7 @@ ossl_cipher.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_cipher.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_cipher.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_cipher.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_cipher.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_cipher.o: $(hdrdir)/ruby/internal/symbol.h
ossl_cipher.o: $(hdrdir)/ruby/internal/value.h
ossl_cipher.o: $(hdrdir)/ruby/internal/value_type.h
@@ -965,6 +974,7 @@ ossl_cipher.o: ossl_ocsp.h
ossl_cipher.o: ossl_pkcs12.h
ossl_cipher.o: ossl_pkcs7.h
ossl_cipher.o: ossl_pkey.h
+ossl_cipher.o: ossl_provider.h
ossl_cipher.o: ossl_rand.h
ossl_cipher.o: ossl_ssl.h
ossl_cipher.o: ossl_ts.h
@@ -1127,6 +1137,7 @@ ossl_config.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_config.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_config.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_config.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_config.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_config.o: $(hdrdir)/ruby/internal/symbol.h
ossl_config.o: $(hdrdir)/ruby/internal/value.h
ossl_config.o: $(hdrdir)/ruby/internal/value_type.h
@@ -1158,6 +1169,7 @@ ossl_config.o: ossl_ocsp.h
ossl_config.o: ossl_pkcs12.h
ossl_config.o: ossl_pkcs7.h
ossl_config.o: ossl_pkey.h
+ossl_config.o: ossl_provider.h
ossl_config.o: ossl_rand.h
ossl_config.o: ossl_ssl.h
ossl_config.o: ossl_ts.h
@@ -1320,6 +1332,7 @@ ossl_digest.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_digest.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_digest.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_digest.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_digest.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_digest.o: $(hdrdir)/ruby/internal/symbol.h
ossl_digest.o: $(hdrdir)/ruby/internal/value.h
ossl_digest.o: $(hdrdir)/ruby/internal/value_type.h
@@ -1351,6 +1364,7 @@ ossl_digest.o: ossl_ocsp.h
ossl_digest.o: ossl_pkcs12.h
ossl_digest.o: ossl_pkcs7.h
ossl_digest.o: ossl_pkey.h
+ossl_digest.o: ossl_provider.h
ossl_digest.o: ossl_rand.h
ossl_digest.o: ossl_ssl.h
ossl_digest.o: ossl_ts.h
@@ -1513,6 +1527,7 @@ ossl_engine.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_engine.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_engine.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_engine.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_engine.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_engine.o: $(hdrdir)/ruby/internal/symbol.h
ossl_engine.o: $(hdrdir)/ruby/internal/value.h
ossl_engine.o: $(hdrdir)/ruby/internal/value_type.h
@@ -1544,6 +1559,7 @@ ossl_engine.o: ossl_ocsp.h
ossl_engine.o: ossl_pkcs12.h
ossl_engine.o: ossl_pkcs7.h
ossl_engine.o: ossl_pkey.h
+ossl_engine.o: ossl_provider.h
ossl_engine.o: ossl_rand.h
ossl_engine.o: ossl_ssl.h
ossl_engine.o: ossl_ts.h
@@ -1706,6 +1722,7 @@ ossl_hmac.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_hmac.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_hmac.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_hmac.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_hmac.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_hmac.o: $(hdrdir)/ruby/internal/symbol.h
ossl_hmac.o: $(hdrdir)/ruby/internal/value.h
ossl_hmac.o: $(hdrdir)/ruby/internal/value_type.h
@@ -1737,6 +1754,7 @@ ossl_hmac.o: ossl_ocsp.h
ossl_hmac.o: ossl_pkcs12.h
ossl_hmac.o: ossl_pkcs7.h
ossl_hmac.o: ossl_pkey.h
+ossl_hmac.o: ossl_provider.h
ossl_hmac.o: ossl_rand.h
ossl_hmac.o: ossl_ssl.h
ossl_hmac.o: ossl_ts.h
@@ -1899,6 +1917,7 @@ ossl_kdf.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_kdf.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_kdf.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_kdf.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_kdf.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_kdf.o: $(hdrdir)/ruby/internal/symbol.h
ossl_kdf.o: $(hdrdir)/ruby/internal/value.h
ossl_kdf.o: $(hdrdir)/ruby/internal/value_type.h
@@ -1930,6 +1949,7 @@ ossl_kdf.o: ossl_ocsp.h
ossl_kdf.o: ossl_pkcs12.h
ossl_kdf.o: ossl_pkcs7.h
ossl_kdf.o: ossl_pkey.h
+ossl_kdf.o: ossl_provider.h
ossl_kdf.o: ossl_rand.h
ossl_kdf.o: ossl_ssl.h
ossl_kdf.o: ossl_ts.h
@@ -2092,6 +2112,7 @@ ossl_ns_spki.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_ns_spki.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_ns_spki.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_ns_spki.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_ns_spki.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_ns_spki.o: $(hdrdir)/ruby/internal/symbol.h
ossl_ns_spki.o: $(hdrdir)/ruby/internal/value.h
ossl_ns_spki.o: $(hdrdir)/ruby/internal/value_type.h
@@ -2123,6 +2144,7 @@ ossl_ns_spki.o: ossl_ocsp.h
ossl_ns_spki.o: ossl_pkcs12.h
ossl_ns_spki.o: ossl_pkcs7.h
ossl_ns_spki.o: ossl_pkey.h
+ossl_ns_spki.o: ossl_provider.h
ossl_ns_spki.o: ossl_rand.h
ossl_ns_spki.o: ossl_ssl.h
ossl_ns_spki.o: ossl_ts.h
@@ -2285,6 +2307,7 @@ ossl_ocsp.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_ocsp.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_ocsp.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_ocsp.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_ocsp.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_ocsp.o: $(hdrdir)/ruby/internal/symbol.h
ossl_ocsp.o: $(hdrdir)/ruby/internal/value.h
ossl_ocsp.o: $(hdrdir)/ruby/internal/value_type.h
@@ -2316,6 +2339,7 @@ ossl_ocsp.o: ossl_ocsp.h
ossl_ocsp.o: ossl_pkcs12.h
ossl_ocsp.o: ossl_pkcs7.h
ossl_ocsp.o: ossl_pkey.h
+ossl_ocsp.o: ossl_provider.h
ossl_ocsp.o: ossl_rand.h
ossl_ocsp.o: ossl_ssl.h
ossl_ocsp.o: ossl_ts.h
@@ -2478,6 +2502,7 @@ ossl_pkcs12.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_pkcs12.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_pkcs12.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_pkcs12.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_pkcs12.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_pkcs12.o: $(hdrdir)/ruby/internal/symbol.h
ossl_pkcs12.o: $(hdrdir)/ruby/internal/value.h
ossl_pkcs12.o: $(hdrdir)/ruby/internal/value_type.h
@@ -2509,6 +2534,7 @@ ossl_pkcs12.o: ossl_pkcs12.c
ossl_pkcs12.o: ossl_pkcs12.h
ossl_pkcs12.o: ossl_pkcs7.h
ossl_pkcs12.o: ossl_pkey.h
+ossl_pkcs12.o: ossl_provider.h
ossl_pkcs12.o: ossl_rand.h
ossl_pkcs12.o: ossl_ssl.h
ossl_pkcs12.o: ossl_ts.h
@@ -2671,6 +2697,7 @@ ossl_pkcs7.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_pkcs7.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_pkcs7.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_pkcs7.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_pkcs7.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_pkcs7.o: $(hdrdir)/ruby/internal/symbol.h
ossl_pkcs7.o: $(hdrdir)/ruby/internal/value.h
ossl_pkcs7.o: $(hdrdir)/ruby/internal/value_type.h
@@ -2702,6 +2729,7 @@ ossl_pkcs7.o: ossl_pkcs12.h
ossl_pkcs7.o: ossl_pkcs7.c
ossl_pkcs7.o: ossl_pkcs7.h
ossl_pkcs7.o: ossl_pkey.h
+ossl_pkcs7.o: ossl_provider.h
ossl_pkcs7.o: ossl_rand.h
ossl_pkcs7.o: ossl_ssl.h
ossl_pkcs7.o: ossl_ts.h
@@ -2864,6 +2892,7 @@ ossl_pkey.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_pkey.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_pkey.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_pkey.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_pkey.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_pkey.o: $(hdrdir)/ruby/internal/symbol.h
ossl_pkey.o: $(hdrdir)/ruby/internal/value.h
ossl_pkey.o: $(hdrdir)/ruby/internal/value_type.h
@@ -2895,6 +2924,7 @@ ossl_pkey.o: ossl_pkcs12.h
ossl_pkey.o: ossl_pkcs7.h
ossl_pkey.o: ossl_pkey.c
ossl_pkey.o: ossl_pkey.h
+ossl_pkey.o: ossl_provider.h
ossl_pkey.o: ossl_rand.h
ossl_pkey.o: ossl_ssl.h
ossl_pkey.o: ossl_ts.h
@@ -3057,6 +3087,7 @@ ossl_pkey_dh.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_pkey_dh.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_pkey_dh.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_pkey_dh.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_pkey_dh.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_pkey_dh.o: $(hdrdir)/ruby/internal/symbol.h
ossl_pkey_dh.o: $(hdrdir)/ruby/internal/value.h
ossl_pkey_dh.o: $(hdrdir)/ruby/internal/value_type.h
@@ -3088,6 +3119,7 @@ ossl_pkey_dh.o: ossl_pkcs12.h
ossl_pkey_dh.o: ossl_pkcs7.h
ossl_pkey_dh.o: ossl_pkey.h
ossl_pkey_dh.o: ossl_pkey_dh.c
+ossl_pkey_dh.o: ossl_provider.h
ossl_pkey_dh.o: ossl_rand.h
ossl_pkey_dh.o: ossl_ssl.h
ossl_pkey_dh.o: ossl_ts.h
@@ -3250,6 +3282,7 @@ ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/symbol.h
ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/value.h
ossl_pkey_dsa.o: $(hdrdir)/ruby/internal/value_type.h
@@ -3281,6 +3314,7 @@ ossl_pkey_dsa.o: ossl_pkcs12.h
ossl_pkey_dsa.o: ossl_pkcs7.h
ossl_pkey_dsa.o: ossl_pkey.h
ossl_pkey_dsa.o: ossl_pkey_dsa.c
+ossl_pkey_dsa.o: ossl_provider.h
ossl_pkey_dsa.o: ossl_rand.h
ossl_pkey_dsa.o: ossl_ssl.h
ossl_pkey_dsa.o: ossl_ts.h
@@ -3443,6 +3477,7 @@ ossl_pkey_ec.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_pkey_ec.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_pkey_ec.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_pkey_ec.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_pkey_ec.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_pkey_ec.o: $(hdrdir)/ruby/internal/symbol.h
ossl_pkey_ec.o: $(hdrdir)/ruby/internal/value.h
ossl_pkey_ec.o: $(hdrdir)/ruby/internal/value_type.h
@@ -3474,6 +3509,7 @@ ossl_pkey_ec.o: ossl_pkcs12.h
ossl_pkey_ec.o: ossl_pkcs7.h
ossl_pkey_ec.o: ossl_pkey.h
ossl_pkey_ec.o: ossl_pkey_ec.c
+ossl_pkey_ec.o: ossl_provider.h
ossl_pkey_ec.o: ossl_rand.h
ossl_pkey_ec.o: ossl_ssl.h
ossl_pkey_ec.o: ossl_ts.h
@@ -3636,6 +3672,7 @@ ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/symbol.h
ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/value.h
ossl_pkey_rsa.o: $(hdrdir)/ruby/internal/value_type.h
@@ -3667,10 +3704,206 @@ ossl_pkey_rsa.o: ossl_pkcs12.h
ossl_pkey_rsa.o: ossl_pkcs7.h
ossl_pkey_rsa.o: ossl_pkey.h
ossl_pkey_rsa.o: ossl_pkey_rsa.c
+ossl_pkey_rsa.o: ossl_provider.h
ossl_pkey_rsa.o: ossl_rand.h
ossl_pkey_rsa.o: ossl_ssl.h
ossl_pkey_rsa.o: ossl_ts.h
ossl_pkey_rsa.o: ossl_x509.h
+ossl_provider.o: $(RUBY_EXTCONF_H)
+ossl_provider.o: $(arch_hdrdir)/ruby/config.h
+ossl_provider.o: $(hdrdir)/ruby.h
+ossl_provider.o: $(hdrdir)/ruby/assert.h
+ossl_provider.o: $(hdrdir)/ruby/backward.h
+ossl_provider.o: $(hdrdir)/ruby/backward/2/assume.h
+ossl_provider.o: $(hdrdir)/ruby/backward/2/attributes.h
+ossl_provider.o: $(hdrdir)/ruby/backward/2/bool.h
+ossl_provider.o: $(hdrdir)/ruby/backward/2/inttypes.h
+ossl_provider.o: $(hdrdir)/ruby/backward/2/limits.h
+ossl_provider.o: $(hdrdir)/ruby/backward/2/long_long.h
+ossl_provider.o: $(hdrdir)/ruby/backward/2/stdalign.h
+ossl_provider.o: $(hdrdir)/ruby/backward/2/stdarg.h
+ossl_provider.o: $(hdrdir)/ruby/defines.h
+ossl_provider.o: $(hdrdir)/ruby/encoding.h
+ossl_provider.o: $(hdrdir)/ruby/intern.h
+ossl_provider.o: $(hdrdir)/ruby/internal/abi.h
+ossl_provider.o: $(hdrdir)/ruby/internal/anyargs.h
+ossl_provider.o: $(hdrdir)/ruby/internal/arithmetic.h
+ossl_provider.o: $(hdrdir)/ruby/internal/arithmetic/char.h
+ossl_provider.o: $(hdrdir)/ruby/internal/arithmetic/double.h
+ossl_provider.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h
+ossl_provider.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h
+ossl_provider.o: $(hdrdir)/ruby/internal/arithmetic/int.h
+ossl_provider.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h
+ossl_provider.o: $(hdrdir)/ruby/internal/arithmetic/long.h
+ossl_provider.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h
+ossl_provider.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h
+ossl_provider.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h
+ossl_provider.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h
+ossl_provider.o: $(hdrdir)/ruby/internal/arithmetic/short.h
+ossl_provider.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h
+ossl_provider.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h
+ossl_provider.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h
+ossl_provider.o: $(hdrdir)/ruby/internal/assume.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/alloc_size.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/artificial.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/cold.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/const.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/constexpr.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/deprecated.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/error.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/flag_enum.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/forceinline.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/format.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/noalias.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/nodiscard.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/noexcept.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/noinline.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/nonnull.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/noreturn.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/packed_struct.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/pure.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/restrict.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/warning.h
+ossl_provider.o: $(hdrdir)/ruby/internal/attr/weakref.h
+ossl_provider.o: $(hdrdir)/ruby/internal/cast.h
+ossl_provider.o: $(hdrdir)/ruby/internal/compiler_is.h
+ossl_provider.o: $(hdrdir)/ruby/internal/compiler_is/apple.h
+ossl_provider.o: $(hdrdir)/ruby/internal/compiler_is/clang.h
+ossl_provider.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h
+ossl_provider.o: $(hdrdir)/ruby/internal/compiler_is/intel.h
+ossl_provider.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h
+ossl_provider.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h
+ossl_provider.o: $(hdrdir)/ruby/internal/compiler_since.h
+ossl_provider.o: $(hdrdir)/ruby/internal/config.h
+ossl_provider.o: $(hdrdir)/ruby/internal/constant_p.h
+ossl_provider.o: $(hdrdir)/ruby/internal/core.h
+ossl_provider.o: $(hdrdir)/ruby/internal/core/rarray.h
+ossl_provider.o: $(hdrdir)/ruby/internal/core/rbasic.h
+ossl_provider.o: $(hdrdir)/ruby/internal/core/rbignum.h
+ossl_provider.o: $(hdrdir)/ruby/internal/core/rclass.h
+ossl_provider.o: $(hdrdir)/ruby/internal/core/rdata.h
+ossl_provider.o: $(hdrdir)/ruby/internal/core/rfile.h
+ossl_provider.o: $(hdrdir)/ruby/internal/core/rhash.h
+ossl_provider.o: $(hdrdir)/ruby/internal/core/robject.h
+ossl_provider.o: $(hdrdir)/ruby/internal/core/rregexp.h
+ossl_provider.o: $(hdrdir)/ruby/internal/core/rstring.h
+ossl_provider.o: $(hdrdir)/ruby/internal/core/rstruct.h
+ossl_provider.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
+ossl_provider.o: $(hdrdir)/ruby/internal/ctype.h
+ossl_provider.o: $(hdrdir)/ruby/internal/dllexport.h
+ossl_provider.o: $(hdrdir)/ruby/internal/dosish.h
+ossl_provider.o: $(hdrdir)/ruby/internal/encoding/coderange.h
+ossl_provider.o: $(hdrdir)/ruby/internal/encoding/ctype.h
+ossl_provider.o: $(hdrdir)/ruby/internal/encoding/encoding.h
+ossl_provider.o: $(hdrdir)/ruby/internal/encoding/pathname.h
+ossl_provider.o: $(hdrdir)/ruby/internal/encoding/re.h
+ossl_provider.o: $(hdrdir)/ruby/internal/encoding/sprintf.h
+ossl_provider.o: $(hdrdir)/ruby/internal/encoding/string.h
+ossl_provider.o: $(hdrdir)/ruby/internal/encoding/symbol.h
+ossl_provider.o: $(hdrdir)/ruby/internal/encoding/transcode.h
+ossl_provider.o: $(hdrdir)/ruby/internal/error.h
+ossl_provider.o: $(hdrdir)/ruby/internal/eval.h
+ossl_provider.o: $(hdrdir)/ruby/internal/event.h
+ossl_provider.o: $(hdrdir)/ruby/internal/fl_type.h
+ossl_provider.o: $(hdrdir)/ruby/internal/gc.h
+ossl_provider.o: $(hdrdir)/ruby/internal/glob.h
+ossl_provider.o: $(hdrdir)/ruby/internal/globals.h
+ossl_provider.o: $(hdrdir)/ruby/internal/has/attribute.h
+ossl_provider.o: $(hdrdir)/ruby/internal/has/builtin.h
+ossl_provider.o: $(hdrdir)/ruby/internal/has/c_attribute.h
+ossl_provider.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h
+ossl_provider.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h
+ossl_provider.o: $(hdrdir)/ruby/internal/has/extension.h
+ossl_provider.o: $(hdrdir)/ruby/internal/has/feature.h
+ossl_provider.o: $(hdrdir)/ruby/internal/has/warning.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/array.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/bignum.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/class.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/compar.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/complex.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/cont.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/dir.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/enum.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/enumerator.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/error.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/eval.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/file.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/hash.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/io.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/load.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/marshal.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/numeric.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/object.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/parse.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/proc.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/process.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/random.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/range.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/rational.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/re.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/ruby.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/select.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/select/largesize.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/signal.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/sprintf.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/string.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/struct.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/thread.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/time.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/variable.h
+ossl_provider.o: $(hdrdir)/ruby/internal/intern/vm.h
+ossl_provider.o: $(hdrdir)/ruby/internal/interpreter.h
+ossl_provider.o: $(hdrdir)/ruby/internal/iterator.h
+ossl_provider.o: $(hdrdir)/ruby/internal/memory.h
+ossl_provider.o: $(hdrdir)/ruby/internal/method.h
+ossl_provider.o: $(hdrdir)/ruby/internal/module.h
+ossl_provider.o: $(hdrdir)/ruby/internal/newobj.h
+ossl_provider.o: $(hdrdir)/ruby/internal/scan_args.h
+ossl_provider.o: $(hdrdir)/ruby/internal/special_consts.h
+ossl_provider.o: $(hdrdir)/ruby/internal/static_assert.h
+ossl_provider.o: $(hdrdir)/ruby/internal/stdalign.h
+ossl_provider.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_provider.o: $(hdrdir)/ruby/internal/stdckdint.h
+ossl_provider.o: $(hdrdir)/ruby/internal/symbol.h
+ossl_provider.o: $(hdrdir)/ruby/internal/value.h
+ossl_provider.o: $(hdrdir)/ruby/internal/value_type.h
+ossl_provider.o: $(hdrdir)/ruby/internal/variable.h
+ossl_provider.o: $(hdrdir)/ruby/internal/warning_push.h
+ossl_provider.o: $(hdrdir)/ruby/internal/xmalloc.h
+ossl_provider.o: $(hdrdir)/ruby/io.h
+ossl_provider.o: $(hdrdir)/ruby/missing.h
+ossl_provider.o: $(hdrdir)/ruby/onigmo.h
+ossl_provider.o: $(hdrdir)/ruby/oniguruma.h
+ossl_provider.o: $(hdrdir)/ruby/ruby.h
+ossl_provider.o: $(hdrdir)/ruby/st.h
+ossl_provider.o: $(hdrdir)/ruby/subst.h
+ossl_provider.o: $(hdrdir)/ruby/thread.h
+ossl_provider.o: openssl_missing.h
+ossl_provider.o: ossl.h
+ossl_provider.o: ossl_asn1.h
+ossl_provider.o: ossl_bio.h
+ossl_provider.o: ossl_bn.h
+ossl_provider.o: ossl_cipher.h
+ossl_provider.o: ossl_config.h
+ossl_provider.o: ossl_digest.h
+ossl_provider.o: ossl_engine.h
+ossl_provider.o: ossl_hmac.h
+ossl_provider.o: ossl_kdf.h
+ossl_provider.o: ossl_ns_spki.h
+ossl_provider.o: ossl_ocsp.h
+ossl_provider.o: ossl_pkcs12.h
+ossl_provider.o: ossl_pkcs7.h
+ossl_provider.o: ossl_pkey.h
+ossl_provider.o: ossl_provider.c
+ossl_provider.o: ossl_provider.h
+ossl_provider.o: ossl_rand.h
+ossl_provider.o: ossl_ssl.h
+ossl_provider.o: ossl_ts.h
+ossl_provider.o: ossl_x509.h
ossl_rand.o: $(RUBY_EXTCONF_H)
ossl_rand.o: $(arch_hdrdir)/ruby/config.h
ossl_rand.o: $(hdrdir)/ruby.h
@@ -3829,6 +4062,7 @@ ossl_rand.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_rand.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_rand.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_rand.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_rand.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_rand.o: $(hdrdir)/ruby/internal/symbol.h
ossl_rand.o: $(hdrdir)/ruby/internal/value.h
ossl_rand.o: $(hdrdir)/ruby/internal/value_type.h
@@ -3859,6 +4093,7 @@ ossl_rand.o: ossl_ocsp.h
ossl_rand.o: ossl_pkcs12.h
ossl_rand.o: ossl_pkcs7.h
ossl_rand.o: ossl_pkey.h
+ossl_rand.o: ossl_provider.h
ossl_rand.o: ossl_rand.c
ossl_rand.o: ossl_rand.h
ossl_rand.o: ossl_ssl.h
@@ -4022,6 +4257,7 @@ ossl_ssl.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_ssl.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_ssl.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_ssl.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_ssl.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_ssl.o: $(hdrdir)/ruby/internal/symbol.h
ossl_ssl.o: $(hdrdir)/ruby/internal/value.h
ossl_ssl.o: $(hdrdir)/ruby/internal/value_type.h
@@ -4052,6 +4288,7 @@ ossl_ssl.o: ossl_ocsp.h
ossl_ssl.o: ossl_pkcs12.h
ossl_ssl.o: ossl_pkcs7.h
ossl_ssl.o: ossl_pkey.h
+ossl_ssl.o: ossl_provider.h
ossl_ssl.o: ossl_rand.h
ossl_ssl.o: ossl_ssl.c
ossl_ssl.o: ossl_ssl.h
@@ -4215,6 +4452,7 @@ ossl_ssl_session.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_ssl_session.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_ssl_session.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_ssl_session.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_ssl_session.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_ssl_session.o: $(hdrdir)/ruby/internal/symbol.h
ossl_ssl_session.o: $(hdrdir)/ruby/internal/value.h
ossl_ssl_session.o: $(hdrdir)/ruby/internal/value_type.h
@@ -4245,6 +4483,7 @@ ossl_ssl_session.o: ossl_ocsp.h
ossl_ssl_session.o: ossl_pkcs12.h
ossl_ssl_session.o: ossl_pkcs7.h
ossl_ssl_session.o: ossl_pkey.h
+ossl_ssl_session.o: ossl_provider.h
ossl_ssl_session.o: ossl_rand.h
ossl_ssl_session.o: ossl_ssl.h
ossl_ssl_session.o: ossl_ssl_session.c
@@ -4408,6 +4647,7 @@ ossl_ts.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_ts.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_ts.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_ts.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_ts.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_ts.o: $(hdrdir)/ruby/internal/symbol.h
ossl_ts.o: $(hdrdir)/ruby/internal/value.h
ossl_ts.o: $(hdrdir)/ruby/internal/value_type.h
@@ -4438,6 +4678,7 @@ ossl_ts.o: ossl_ocsp.h
ossl_ts.o: ossl_pkcs12.h
ossl_ts.o: ossl_pkcs7.h
ossl_ts.o: ossl_pkey.h
+ossl_ts.o: ossl_provider.h
ossl_ts.o: ossl_rand.h
ossl_ts.o: ossl_ssl.h
ossl_ts.o: ossl_ts.c
@@ -4601,6 +4842,7 @@ ossl_x509.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_x509.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_x509.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_x509.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_x509.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_x509.o: $(hdrdir)/ruby/internal/symbol.h
ossl_x509.o: $(hdrdir)/ruby/internal/value.h
ossl_x509.o: $(hdrdir)/ruby/internal/value_type.h
@@ -4631,6 +4873,7 @@ ossl_x509.o: ossl_ocsp.h
ossl_x509.o: ossl_pkcs12.h
ossl_x509.o: ossl_pkcs7.h
ossl_x509.o: ossl_pkey.h
+ossl_x509.o: ossl_provider.h
ossl_x509.o: ossl_rand.h
ossl_x509.o: ossl_ssl.h
ossl_x509.o: ossl_ts.h
@@ -4794,6 +5037,7 @@ ossl_x509attr.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_x509attr.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_x509attr.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_x509attr.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_x509attr.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_x509attr.o: $(hdrdir)/ruby/internal/symbol.h
ossl_x509attr.o: $(hdrdir)/ruby/internal/value.h
ossl_x509attr.o: $(hdrdir)/ruby/internal/value_type.h
@@ -4824,6 +5068,7 @@ ossl_x509attr.o: ossl_ocsp.h
ossl_x509attr.o: ossl_pkcs12.h
ossl_x509attr.o: ossl_pkcs7.h
ossl_x509attr.o: ossl_pkey.h
+ossl_x509attr.o: ossl_provider.h
ossl_x509attr.o: ossl_rand.h
ossl_x509attr.o: ossl_ssl.h
ossl_x509attr.o: ossl_ts.h
@@ -4987,6 +5232,7 @@ ossl_x509cert.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_x509cert.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_x509cert.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_x509cert.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_x509cert.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_x509cert.o: $(hdrdir)/ruby/internal/symbol.h
ossl_x509cert.o: $(hdrdir)/ruby/internal/value.h
ossl_x509cert.o: $(hdrdir)/ruby/internal/value_type.h
@@ -5017,6 +5263,7 @@ ossl_x509cert.o: ossl_ocsp.h
ossl_x509cert.o: ossl_pkcs12.h
ossl_x509cert.o: ossl_pkcs7.h
ossl_x509cert.o: ossl_pkey.h
+ossl_x509cert.o: ossl_provider.h
ossl_x509cert.o: ossl_rand.h
ossl_x509cert.o: ossl_ssl.h
ossl_x509cert.o: ossl_ts.h
@@ -5180,6 +5427,7 @@ ossl_x509crl.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_x509crl.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_x509crl.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_x509crl.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_x509crl.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_x509crl.o: $(hdrdir)/ruby/internal/symbol.h
ossl_x509crl.o: $(hdrdir)/ruby/internal/value.h
ossl_x509crl.o: $(hdrdir)/ruby/internal/value_type.h
@@ -5210,6 +5458,7 @@ ossl_x509crl.o: ossl_ocsp.h
ossl_x509crl.o: ossl_pkcs12.h
ossl_x509crl.o: ossl_pkcs7.h
ossl_x509crl.o: ossl_pkey.h
+ossl_x509crl.o: ossl_provider.h
ossl_x509crl.o: ossl_rand.h
ossl_x509crl.o: ossl_ssl.h
ossl_x509crl.o: ossl_ts.h
@@ -5373,6 +5622,7 @@ ossl_x509ext.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_x509ext.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_x509ext.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_x509ext.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_x509ext.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_x509ext.o: $(hdrdir)/ruby/internal/symbol.h
ossl_x509ext.o: $(hdrdir)/ruby/internal/value.h
ossl_x509ext.o: $(hdrdir)/ruby/internal/value_type.h
@@ -5403,6 +5653,7 @@ ossl_x509ext.o: ossl_ocsp.h
ossl_x509ext.o: ossl_pkcs12.h
ossl_x509ext.o: ossl_pkcs7.h
ossl_x509ext.o: ossl_pkey.h
+ossl_x509ext.o: ossl_provider.h
ossl_x509ext.o: ossl_rand.h
ossl_x509ext.o: ossl_ssl.h
ossl_x509ext.o: ossl_ts.h
@@ -5566,6 +5817,7 @@ ossl_x509name.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_x509name.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_x509name.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_x509name.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_x509name.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_x509name.o: $(hdrdir)/ruby/internal/symbol.h
ossl_x509name.o: $(hdrdir)/ruby/internal/value.h
ossl_x509name.o: $(hdrdir)/ruby/internal/value_type.h
@@ -5596,6 +5848,7 @@ ossl_x509name.o: ossl_ocsp.h
ossl_x509name.o: ossl_pkcs12.h
ossl_x509name.o: ossl_pkcs7.h
ossl_x509name.o: ossl_pkey.h
+ossl_x509name.o: ossl_provider.h
ossl_x509name.o: ossl_rand.h
ossl_x509name.o: ossl_ssl.h
ossl_x509name.o: ossl_ts.h
@@ -5759,6 +6012,7 @@ ossl_x509req.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_x509req.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_x509req.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_x509req.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_x509req.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_x509req.o: $(hdrdir)/ruby/internal/symbol.h
ossl_x509req.o: $(hdrdir)/ruby/internal/value.h
ossl_x509req.o: $(hdrdir)/ruby/internal/value_type.h
@@ -5789,6 +6043,7 @@ ossl_x509req.o: ossl_ocsp.h
ossl_x509req.o: ossl_pkcs12.h
ossl_x509req.o: ossl_pkcs7.h
ossl_x509req.o: ossl_pkey.h
+ossl_x509req.o: ossl_provider.h
ossl_x509req.o: ossl_rand.h
ossl_x509req.o: ossl_ssl.h
ossl_x509req.o: ossl_ts.h
@@ -5952,6 +6207,7 @@ ossl_x509revoked.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_x509revoked.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_x509revoked.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_x509revoked.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_x509revoked.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_x509revoked.o: $(hdrdir)/ruby/internal/symbol.h
ossl_x509revoked.o: $(hdrdir)/ruby/internal/value.h
ossl_x509revoked.o: $(hdrdir)/ruby/internal/value_type.h
@@ -5982,6 +6238,7 @@ ossl_x509revoked.o: ossl_ocsp.h
ossl_x509revoked.o: ossl_pkcs12.h
ossl_x509revoked.o: ossl_pkcs7.h
ossl_x509revoked.o: ossl_pkey.h
+ossl_x509revoked.o: ossl_provider.h
ossl_x509revoked.o: ossl_rand.h
ossl_x509revoked.o: ossl_ssl.h
ossl_x509revoked.o: ossl_ts.h
@@ -6145,6 +6402,7 @@ ossl_x509store.o: $(hdrdir)/ruby/internal/special_consts.h
ossl_x509store.o: $(hdrdir)/ruby/internal/static_assert.h
ossl_x509store.o: $(hdrdir)/ruby/internal/stdalign.h
ossl_x509store.o: $(hdrdir)/ruby/internal/stdbool.h
+ossl_x509store.o: $(hdrdir)/ruby/internal/stdckdint.h
ossl_x509store.o: $(hdrdir)/ruby/internal/symbol.h
ossl_x509store.o: $(hdrdir)/ruby/internal/value.h
ossl_x509store.o: $(hdrdir)/ruby/internal/value_type.h
@@ -6175,6 +6433,7 @@ ossl_x509store.o: ossl_ocsp.h
ossl_x509store.o: ossl_pkcs12.h
ossl_x509store.o: ossl_pkcs7.h
ossl_x509store.o: ossl_pkey.h
+ossl_x509store.o: ossl_provider.h
ossl_x509store.o: ossl_rand.h
ossl_x509store.o: ossl_ssl.h
ossl_x509store.o: ossl_ts.h
diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb
index ef21bd5f43..dd3732d0a8 100644
--- a/ext/openssl/extconf.rb
+++ b/ext/openssl/extconf.rb
@@ -13,22 +13,36 @@
require "mkmf"
-dir_config_given = dir_config("openssl").any?
+ssl_dirs = dir_config("openssl")
+dir_config_given = ssl_dirs.any?
+
+_, ssl_ldir = ssl_dirs
+if ssl_ldir&.split(File::PATH_SEPARATOR)&.none? { |dir| File.directory?(dir) }
+ # According to the `mkmf.rb#dir_config`, the `--with-openssl-dir=<dir>` uses
+ # the value of the `File.basename(RbConfig::MAKEFILE_CONFIG["libdir"])` as a
+ # loaded library directory name.
+ ruby_ldir_name = File.basename(RbConfig::MAKEFILE_CONFIG["libdir"])
+
+ raise "OpenSSL library directory could not be found in '#{ssl_ldir}'. " \
+ "You might want to fix this error in one of the following ways.\n" \
+ " * Recompile OpenSSL by configuring it with --libdir=#{ruby_ldir_name} " \
+ " to specify the OpenSSL library directory.\n" \
+ " * Recompile Ruby by configuring it with --libdir=<dir> to specify the " \
+ "Ruby library directory.\n" \
+ " * Compile this openssl gem with --with-openssl-include=<dir> and " \
+ "--with-openssl-lib=<dir> options to specify the OpenSSL include and " \
+ "library directories."
+end
+
dir_config("kerberos")
Logging::message "=== OpenSSL for Ruby configurator ===\n"
-##
-# Adds -DOSSL_DEBUG for compilation and some more targets when GCC is used
-# To turn it on, use: --with-debug or --enable-debug
-#
-if with_config("debug") or enable_config("debug")
- $defs.push("-DOSSL_DEBUG")
-end
$defs.push("-D""OPENSSL_SUPPRESS_DEPRECATED")
have_func("rb_io_descriptor")
have_func("rb_io_maybe_wait(0, Qnil, Qnil, Qnil)", "ruby/io.h") # Ruby 3.1
+have_func("rb_io_timeout", "ruby/io.h")
Logging::message "=== Checking for system dependent stuff... ===\n"
have_library("nsl", "t_open")
@@ -192,6 +206,12 @@ have_func("EVP_PKEY_dup(NULL)", evp_h)
Logging::message "=== Checking done. ===\n"
+# Append flags from environment variables.
+extcflags = ENV["RUBY_OPENSSL_EXTCFLAGS"]
+append_cflags(extcflags.split) if extcflags
+extldflags = ENV["RUBY_OPENSSL_EXTLDFLAGS"]
+append_ldflags(extldflags.split) if extldflags
+
create_header
create_makefile("openssl")
Logging::message "Done.\n"
diff --git a/ext/openssl/lib/openssl/buffering.rb b/ext/openssl/lib/openssl/buffering.rb
index d47e1082ef..68aa7bc970 100644
--- a/ext/openssl/lib/openssl/buffering.rb
+++ b/ext/openssl/lib/openssl/buffering.rb
@@ -93,9 +93,7 @@ module OpenSSL::Buffering
nil
else
size = @rbuffer.size unless size
- ret = @rbuffer[0, size]
- @rbuffer[0, size] = ""
- ret
+ @rbuffer.slice!(0, size)
end
end
@@ -106,8 +104,7 @@ module OpenSSL::Buffering
#
# Get the next 8bit byte from `ssl`. Returns `nil` on EOF
def getbyte
- byte = read(1)
- byte && byte.unpack1("C")
+ read(1)&.ord
end
##
@@ -232,7 +229,7 @@ module OpenSSL::Buffering
#
# Unlike IO#gets the separator must be provided if a limit is provided.
- def gets(eol=$/, limit=nil)
+ def gets(eol=$/, limit=nil, chomp: false)
idx = @rbuffer.index(eol)
until @eof
break if idx
@@ -247,7 +244,11 @@ module OpenSSL::Buffering
if size && limit && limit >= 0
size = [size, limit].min
end
- consume_rbuff(size)
+ line = consume_rbuff(size)
+ if chomp && line
+ line.chomp!(eol)
+ end
+ line
end
##
diff --git a/ext/openssl/lib/openssl/ssl.rb b/ext/openssl/lib/openssl/ssl.rb
index ea8bb2a18e..d28bf1a374 100644
--- a/ext/openssl/lib/openssl/ssl.rb
+++ b/ext/openssl/lib/openssl/ssl.rb
@@ -22,7 +22,6 @@ module OpenSSL
module SSL
class SSLContext
DEFAULT_PARAMS = { # :nodoc:
- :min_version => OpenSSL::SSL::TLS1_VERSION,
:verify_mode => OpenSSL::SSL::VERIFY_PEER,
:verify_hostname => true,
:options => -> {
@@ -34,27 +33,28 @@ module OpenSSL
}
if defined?(OpenSSL::PKey::DH)
- DEFAULT_2048 = OpenSSL::PKey::DH.new <<-_end_of_pem_
+ DH_ffdhe2048 = OpenSSL::PKey::DH.new <<-_end_of_pem_
-----BEGIN DH PARAMETERS-----
-MIIBCAKCAQEA7E6kBrYiyvmKAMzQ7i8WvwVk9Y/+f8S7sCTN712KkK3cqd1jhJDY
-JbrYeNV3kUIKhPxWHhObHKpD1R84UpL+s2b55+iMd6GmL7OYmNIT/FccKhTcveab
-VBmZT86BZKYyf45hUF9FOuUM9xPzuK3Vd8oJQvfYMCd7LPC0taAEljQLR4Edf8E6
-YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
-1bNveX5wInh5GDx1FGhKBZ+s1H+aedudCm7sCgRwv8lKWYGiHzObSma8A86KG+MD
-7Lo5JquQ3DlBodj3IDyPrxIv96lvRPFtAwIBAg==
+MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
++8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
+87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
+YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
+7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
+ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
-----END DH PARAMETERS-----
_end_of_pem_
- private_constant :DEFAULT_2048
+ private_constant :DH_ffdhe2048
DEFAULT_TMP_DH_CALLBACK = lambda { |ctx, is_export, keylen| # :nodoc:
warn "using default DH parameters." if $VERBOSE
- DEFAULT_2048
+ DH_ffdhe2048
}
end
if !(OpenSSL::OPENSSL_VERSION.start_with?("OpenSSL") &&
OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10100000)
DEFAULT_PARAMS.merge!(
+ min_version: OpenSSL::SSL::TLS1_VERSION,
ciphers: %w{
ECDHE-ECDSA-AES128-GCM-SHA256
ECDHE-RSA-AES128-GCM-SHA256
@@ -252,6 +252,14 @@ YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
to_io.peeraddr
end
+ def local_address
+ to_io.local_address
+ end
+
+ def remote_address
+ to_io.remote_address
+ end
+
def setsockopt(level, optname, optval)
to_io.setsockopt(level, optname, optval)
end
@@ -271,6 +279,36 @@ YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
def do_not_reverse_lookup=(flag)
to_io.do_not_reverse_lookup = flag
end
+
+ def close_on_exec=(value)
+ to_io.close_on_exec = value
+ end
+
+ def close_on_exec?
+ to_io.close_on_exec?
+ end
+
+ def wait(*args)
+ to_io.wait(*args)
+ end
+
+ def wait_readable(*args)
+ to_io.wait_readable(*args)
+ end
+
+ def wait_writable(*args)
+ to_io.wait_writable(*args)
+ end
+
+ if IO.method_defined?(:timeout)
+ def timeout
+ to_io.timeout
+ end
+
+ def timeout=(value)
+ to_io.timeout=(value)
+ end
+ end
end
def verify_certificate_identity(cert, hostname)
@@ -421,6 +459,32 @@ YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
nil
end
+ # Close the stream for reading.
+ # This method is ignored by OpenSSL as there is no reasonable way to
+ # implement it, but exists for compatibility with IO.
+ def close_read
+ # Unsupported and ignored.
+ # Just don't read any more.
+ end
+
+ # Closes the stream for writing. The behavior of this method depends on
+ # the version of OpenSSL and the TLS protocol in use.
+ #
+ # - Sends a 'close_notify' alert to the peer.
+ # - Does not wait for the peer's 'close_notify' alert in response.
+ #
+ # In TLS 1.2 and earlier:
+ # - On receipt of a 'close_notify' alert, responds with a 'close_notify'
+ # alert of its own and close down the connection immediately,
+ # discarding any pending writes.
+ #
+ # Therefore, on TLS 1.2, this method will cause the connection to be
+ # completely shut down. On TLS 1.3, the connection will remain open for
+ # reading only.
+ def close_write
+ stop
+ end
+
private
def using_anon_cipher?
@@ -494,7 +558,7 @@ YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
unless ctx.session_id_context
# see #6137 - session id may not exceed 32 bytes
prng = ::Random.new($0.hash)
- session_id = prng.bytes(16).unpack('H*')[0]
+ session_id = prng.bytes(16).unpack1('H*')
@ctx.session_id_context = session_id
end
@start_immediately = true
diff --git a/ext/openssl/lib/openssl/version.rb b/ext/openssl/lib/openssl/version.rb
index 4163f55064..9315a79381 100644
--- a/ext/openssl/lib/openssl/version.rb
+++ b/ext/openssl/lib/openssl/version.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
module OpenSSL
- VERSION = "3.1.0"
+ VERSION = "3.2.0"
end
diff --git a/ext/openssl/openssl.gemspec b/ext/openssl/openssl.gemspec
index 3a2f64f676..2765f55401 100644
--- a/ext/openssl/openssl.gemspec
+++ b/ext/openssl/openssl.gemspec
@@ -1,10 +1,10 @@
Gem::Specification.new do |spec|
spec.name = "openssl"
- spec.version = "3.1.0"
+ spec.version = "3.2.0"
spec.authors = ["Martin Bosslet", "SHIBATA Hiroshi", "Zachary Scott", "Kazuki Yamaguchi"]
spec.email = ["ruby-core@ruby-lang.org"]
- spec.summary = %q{OpenSSL provides SSL, TLS and general purpose cryptography.}
- spec.description = %q{It wraps the OpenSSL library.}
+ spec.summary = %q{SSL/TLS and general-purpose cryptography for Ruby}
+ spec.description = %q{OpenSSL for Ruby provides access to SSL/TLS and general-purpose cryptography based on the OpenSSL library.}
spec.homepage = "https://github.com/ruby/openssl"
spec.license = "Ruby"
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
spec.extra_rdoc_files = Dir["*.md"]
spec.rdoc_options = ["--main", "README.md"]
- spec.required_ruby_version = ">= 2.6.0"
+ spec.required_ruby_version = ">= 2.7.0"
spec.metadata["msys2_mingw_dependencies"] = "openssl"
end
diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c
index 81069c8b93..00eded55cb 100644
--- a/ext/openssl/ossl.c
+++ b/ext/openssl/ossl.c
@@ -207,7 +207,7 @@ ossl_pem_passwd_cb(char *buf, int max_len, int flag, void *pwd_)
while (1) {
/*
- * when the flag is nonzero, this passphrase
+ * when the flag is nonzero, this password
* will be used to perform encryption; otherwise it will
* be used to perform decryption.
*/
@@ -272,23 +272,28 @@ VALUE
ossl_make_error(VALUE exc, VALUE str)
{
unsigned long e;
+ const char *data;
+ int flags;
- e = ERR_peek_last_error();
+ if (NIL_P(str))
+ str = rb_str_new(NULL, 0);
+
+#ifdef HAVE_ERR_GET_ERROR_ALL
+ e = ERR_peek_last_error_all(NULL, NULL, NULL, &data, &flags);
+#else
+ e = ERR_peek_last_error_line_data(NULL, NULL, &data, &flags);
+#endif
if (e) {
- const char *msg = ERR_reason_error_string(e);
+ const char *msg = ERR_reason_error_string(e);
- if (NIL_P(str)) {
- if (msg) str = rb_str_new_cstr(msg);
- }
- else {
- if (RSTRING_LEN(str)) rb_str_cat2(str, ": ");
- rb_str_cat2(str, msg ? msg : "(null)");
- }
- ossl_clear_error();
+ if (RSTRING_LEN(str)) rb_str_cat_cstr(str, ": ");
+ rb_str_cat_cstr(str, msg ? msg : "(null)");
+ if (flags & ERR_TXT_STRING && data)
+ rb_str_catf(str, " (%s)", data);
+ ossl_clear_error();
}
- if (NIL_P(str)) str = rb_str_new(0, 0);
- return rb_exc_new3(exc, str);
+ return rb_exc_new_str(exc, str);
}
void
@@ -369,22 +374,6 @@ ossl_get_errors(VALUE _)
*/
VALUE dOSSL;
-#if !defined(HAVE_VA_ARGS_MACRO)
-void
-ossl_debug(const char *fmt, ...)
-{
- va_list args;
-
- if (dOSSL == Qtrue) {
- fprintf(stderr, "OSSL_DEBUG: ");
- va_start(args, fmt);
- vfprintf(stderr, fmt, args);
- va_end(args);
- fprintf(stderr, " [CONTEXT N/A]\n");
- }
-}
-#endif
-
/*
* call-seq:
* OpenSSL.debug -> true | false
@@ -474,75 +463,6 @@ ossl_fips_mode_set(VALUE self, VALUE enabled)
#endif
}
-#if defined(OSSL_DEBUG)
-#if !defined(LIBRESSL_VERSION_NUMBER) && \
- (OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(OPENSSL_NO_CRYPTO_MDEBUG) || \
- defined(CRYPTO_malloc_debug_init))
-/*
- * call-seq:
- * OpenSSL.mem_check_start -> nil
- *
- * Calls CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON). Starts tracking memory
- * allocations. See also OpenSSL.print_mem_leaks.
- *
- * This is available only when built with a capable OpenSSL and --enable-debug
- * configure option.
- */
-static VALUE
-mem_check_start(VALUE self)
-{
- CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
- return Qnil;
-}
-
-/*
- * call-seq:
- * OpenSSL.print_mem_leaks -> true | false
- *
- * For debugging the Ruby/OpenSSL library. Calls CRYPTO_mem_leaks_fp(stderr).
- * Prints detected memory leaks to standard error. This cleans the global state
- * up thus you cannot use any methods of the library after calling this.
- *
- * Returns +true+ if leaks detected, +false+ otherwise.
- *
- * This is available only when built with a capable OpenSSL and --enable-debug
- * configure option.
- *
- * === Example
- * OpenSSL.mem_check_start
- * NOT_GCED = OpenSSL::PKey::RSA.new(256)
- *
- * END {
- * GC.start
- * OpenSSL.print_mem_leaks # will print the leakage
- * }
- */
-static VALUE
-print_mem_leaks(VALUE self)
-{
-#if OPENSSL_VERSION_NUMBER >= 0x10100000
- int ret;
-#endif
-
-#ifndef HAVE_RB_EXT_RACTOR_SAFE
- // for Ruby 2.x
- void ossl_bn_ctx_free(void); // ossl_bn.c
- ossl_bn_ctx_free();
-#endif
-
-#if OPENSSL_VERSION_NUMBER >= 0x10100000
- ret = CRYPTO_mem_leaks_fp(stderr);
- if (ret < 0)
- ossl_raise(eOSSLError, "CRYPTO_mem_leaks_fp");
- return ret ? Qfalse : Qtrue;
-#else
- CRYPTO_mem_leaks_fp(stderr);
- return Qnil;
-#endif
-}
-#endif
-#endif
-
#if !defined(HAVE_OPENSSL_110_THREADING_API)
/**
* Stores locks needed for OpenSSL thread safety
@@ -685,23 +605,21 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2)
*
* key = OpenSSL::PKey::RSA.new 2048
*
- * open 'private_key.pem', 'w' do |io| io.write key.to_pem end
- * open 'public_key.pem', 'w' do |io| io.write key.public_key.to_pem end
+ * File.write 'private_key.pem', key.private_to_pem
+ * File.write 'public_key.pem', key.public_to_pem
*
* === Exporting a Key
*
* Keys saved to disk without encryption are not secure as anyone who gets
* ahold of the key may use it unless it is encrypted. In order to securely
- * export a key you may export it with a pass phrase.
+ * export a key you may export it with a password.
*
* cipher = OpenSSL::Cipher.new 'aes-256-cbc'
- * pass_phrase = 'my secure pass phrase goes here'
+ * password = 'my secure password goes here'
*
- * key_secure = key.export cipher, pass_phrase
+ * key_secure = key.private_to_pem cipher, password
*
- * open 'private.secure.pem', 'w' do |io|
- * io.write key_secure
- * end
+ * File.write 'private.secure.pem', key_secure
*
* OpenSSL::Cipher.ciphers returns a list of available ciphers.
*
@@ -721,13 +639,13 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2)
*
* === Loading an Encrypted Key
*
- * OpenSSL will prompt you for your pass phrase when loading an encrypted key.
- * If you will not be able to type in the pass phrase you may provide it when
+ * OpenSSL will prompt you for your password when loading an encrypted key.
+ * If you will not be able to type in the password you may provide it when
* loading the key:
*
* key4_pem = File.read 'private.secure.pem'
- * pass_phrase = 'my secure pass phrase goes here'
- * key4 = OpenSSL::PKey.read key4_pem, pass_phrase
+ * password = 'my secure password goes here'
+ * key4 = OpenSSL::PKey.read key4_pem, password
*
* == RSA Encryption
*
@@ -843,45 +761,6 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2)
* decrypted = cipher.update encrypted
* decrypted << cipher.final
*
- * == PKCS #5 Password-based Encryption
- *
- * PKCS #5 is a password-based encryption standard documented at
- * RFC2898[http://www.ietf.org/rfc/rfc2898.txt]. It allows a short password or
- * passphrase to be used to create a secure encryption key. If possible, PBKDF2
- * as described above should be used if the circumstances allow it.
- *
- * PKCS #5 uses a Cipher, a pass phrase and a salt to generate an encryption
- * key.
- *
- * pass_phrase = 'my secure pass phrase goes here'
- * salt = '8 octets'
- *
- * === Encryption
- *
- * First set up the cipher for encryption
- *
- * encryptor = OpenSSL::Cipher.new 'aes-256-cbc'
- * encryptor.encrypt
- * encryptor.pkcs5_keyivgen pass_phrase, salt
- *
- * Then pass the data you want to encrypt through
- *
- * encrypted = encryptor.update 'top secret document'
- * encrypted << encryptor.final
- *
- * === Decryption
- *
- * Use a new Cipher instance set up for decryption
- *
- * decryptor = OpenSSL::Cipher.new 'aes-256-cbc'
- * decryptor.decrypt
- * decryptor.pkcs5_keyivgen pass_phrase, salt
- *
- * Then pass the data you want to decrypt through
- *
- * plain = decryptor.update encrypted
- * plain << decryptor.final
- *
* == X509 Certificates
*
* === Creating a Certificate
@@ -959,12 +838,12 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2)
* not readable by other users.
*
* ca_key = OpenSSL::PKey::RSA.new 2048
- * pass_phrase = 'my secure pass phrase goes here'
+ * password = 'my secure password goes here'
*
- * cipher = OpenSSL::Cipher.new 'aes-256-cbc'
+ * cipher = 'aes-256-cbc'
*
* open 'ca_key.pem', 'w', 0400 do |io|
- * io.write ca_key.export(cipher, pass_phrase)
+ * io.write ca_key.private_to_pem(cipher, password)
* end
*
* === CA Certificate
@@ -1204,10 +1083,27 @@ Init_openssl(void)
/*
* Version number of OpenSSL the ruby OpenSSL extension was built with
- * (base 16)
+ * (base 16). The formats are below.
+ *
+ * [OpenSSL 3] <tt>0xMNN00PP0 (major minor 00 patch 0)</tt>
+ * [OpenSSL before 3] <tt>0xMNNFFPPS (major minor fix patch status)</tt>
+ * [LibreSSL] <tt>0x20000000 (fixed value)</tt>
+ *
+ * See also the man page OPENSSL_VERSION_NUMBER(3).
*/
rb_define_const(mOSSL, "OPENSSL_VERSION_NUMBER", INT2NUM(OPENSSL_VERSION_NUMBER));
+#if defined(LIBRESSL_VERSION_NUMBER)
+ /*
+ * Version number of LibreSSL the ruby OpenSSL extension was built with
+ * (base 16). The format is <tt>0xMNNFF00f (major minor fix 00
+ * status)</tt>. This constant is only defined in LibreSSL cases.
+ *
+ * See also the man page LIBRESSL_VERSION_NUMBER(3).
+ */
+ rb_define_const(mOSSL, "LIBRESSL_VERSION_NUMBER", INT2NUM(LIBRESSL_VERSION_NUMBER));
+#endif
+
/*
* Boolean indicating whether OpenSSL is FIPS-capable or not
*/
@@ -1271,42 +1167,7 @@ Init_openssl(void)
Init_ossl_x509();
Init_ossl_ocsp();
Init_ossl_engine();
+ Init_ossl_provider();
Init_ossl_asn1();
Init_ossl_kdf();
-
-#if defined(OSSL_DEBUG)
- /*
- * For debugging Ruby/OpenSSL. Enable only when built with --enable-debug
- */
-#if !defined(LIBRESSL_VERSION_NUMBER) && \
- (OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(OPENSSL_NO_CRYPTO_MDEBUG) || \
- defined(CRYPTO_malloc_debug_init))
- rb_define_module_function(mOSSL, "mem_check_start", mem_check_start, 0);
- rb_define_module_function(mOSSL, "print_mem_leaks", print_mem_leaks, 0);
-
-#if defined(CRYPTO_malloc_debug_init) /* <= 1.0.2 */
- CRYPTO_malloc_debug_init();
-#endif
-
-#if defined(V_CRYPTO_MDEBUG_ALL) /* <= 1.0.2 */
- CRYPTO_set_mem_debug_options(V_CRYPTO_MDEBUG_ALL);
-#endif
-
-#if OPENSSL_VERSION_NUMBER < 0x10100000 /* <= 1.0.2 */
- {
- int i;
- /*
- * See crypto/ex_data.c; call def_get_class() immediately to avoid
- * allocations. 15 is the maximum number that is used as the class index
- * in OpenSSL 1.0.2.
- */
- for (i = 0; i <= 15; i++) {
- if (CRYPTO_get_ex_new_index(i, 0, (void *)"ossl-mdebug-dummy", 0, 0, 0) < 0)
- rb_raise(rb_eRuntimeError, "CRYPTO_get_ex_new_index for "
- "class index %d failed", i);
- }
- }
-#endif
-#endif
-#endif
}
diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h
index facb80aa73..68d42b71e2 100644
--- a/ext/openssl/ossl.h
+++ b/ext/openssl/ossl.h
@@ -62,6 +62,10 @@
# define OSSL_USE_ENGINE
#endif
+#if OSSL_OPENSSL_PREREQ(3, 0, 0)
+# define OSSL_USE_PROVIDER
+#endif
+
/*
* Common Module
*/
@@ -157,7 +161,6 @@ VALUE ossl_to_der_if_possible(VALUE);
*/
extern VALUE dOSSL;
-#if defined(HAVE_VA_ARGS_MACRO)
#define OSSL_Debug(...) do { \
if (dOSSL == Qtrue) { \
fprintf(stderr, "OSSL_DEBUG: "); \
@@ -166,11 +169,6 @@ extern VALUE dOSSL;
} \
} while (0)
-#else
-void ossl_debug(const char *, ...);
-#define OSSL_Debug ossl_debug
-#endif
-
/*
* Include all parts
*/
@@ -194,6 +192,7 @@ void ossl_debug(const char *, ...);
#endif
#include "ossl_x509.h"
#include "ossl_engine.h"
+#include "ossl_provider.h"
#include "ossl_kdf.h"
void Init_openssl(void);
diff --git a/ext/openssl/ossl_bn.c b/ext/openssl/ossl_bn.c
index bf2bac3679..ce0d3ec7ee 100644
--- a/ext/openssl/ossl_bn.c
+++ b/ext/openssl/ossl_bn.c
@@ -41,7 +41,7 @@ static const rb_data_type_t ossl_bn_type = {
{
0, ossl_bn_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c
index cb8fbc3ca2..1910a5cdee 100644
--- a/ext/openssl/ossl_cipher.c
+++ b/ext/openssl/ossl_cipher.c
@@ -42,7 +42,7 @@ static const rb_data_type_t ossl_cipher_type = {
{
0, ossl_cipher_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
@@ -442,8 +442,8 @@ ossl_cipher_final(VALUE self)
* call-seq:
* cipher.name -> string
*
- * Returns the name of the cipher which may differ slightly from the original
- * name provided.
+ * Returns the short name of the cipher which may differ slightly from the
+ * original name provided.
*/
static VALUE
ossl_cipher_name(VALUE self)
diff --git a/ext/openssl/ossl_config.c b/ext/openssl/ossl_config.c
index 0bac027487..0e598b4d51 100644
--- a/ext/openssl/ossl_config.c
+++ b/ext/openssl/ossl_config.c
@@ -22,7 +22,7 @@ static const rb_data_type_t ossl_config_type = {
{
0, nconf_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
CONF *
diff --git a/ext/openssl/ossl_digest.c b/ext/openssl/ossl_digest.c
index fc326ec14a..1ae26a2355 100644
--- a/ext/openssl/ossl_digest.c
+++ b/ext/openssl/ossl_digest.c
@@ -35,7 +35,7 @@ static const rb_data_type_t ossl_digest_type = {
{
0, ossl_digest_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
@@ -103,7 +103,8 @@ VALUE ossl_digest_update(VALUE, VALUE);
* Digest.new(string [, data]) -> Digest
*
* Creates a Digest instance based on _string_, which is either the ln
- * (long name) or sn (short name) of a supported digest algorithm.
+ * (long name) or sn (short name) of a supported digest algorithm. A list of
+ * supported algorithms can be obtained by calling OpenSSL::Digest.digests.
*
* If _data_ (a String) is given, it is used as the initial input to the
* Digest instance, i.e.
@@ -162,6 +163,32 @@ ossl_digest_copy(VALUE self, VALUE other)
return self;
}
+static void
+add_digest_name_to_ary(const OBJ_NAME *name, void *arg)
+{
+ VALUE ary = (VALUE)arg;
+ rb_ary_push(ary, rb_str_new2(name->name));
+}
+
+/*
+ * call-seq:
+ * OpenSSL::Digest.digests -> array[string...]
+ *
+ * Returns the names of all available digests in an array.
+ */
+static VALUE
+ossl_s_digests(VALUE self)
+{
+ VALUE ary;
+
+ ary = rb_ary_new();
+ OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_MD_METH,
+ add_digest_name_to_ary,
+ (void*)ary);
+
+ return ary;
+}
+
/*
* call-seq:
* digest.reset -> self
@@ -245,7 +272,8 @@ ossl_digest_finish(int argc, VALUE *argv, VALUE self)
* call-seq:
* digest.name -> string
*
- * Returns the sn of this Digest algorithm.
+ * Returns the short name of this Digest algorithm which may differ slightly
+ * from the original name provided.
*
* === Example
* digest = OpenSSL::Digest.new('SHA512')
@@ -412,6 +440,7 @@ Init_ossl_digest(void)
rb_define_alloc_func(cDigest, ossl_digest_alloc);
+ rb_define_module_function(cDigest, "digests", ossl_s_digests, 0);
rb_define_method(cDigest, "initialize", ossl_digest_initialize, -1);
rb_define_method(cDigest, "initialize_copy", ossl_digest_copy, 1);
rb_define_method(cDigest, "reset", ossl_digest_reset, 0);
diff --git a/ext/openssl/ossl_engine.c b/ext/openssl/ossl_engine.c
index 1abde7f766..9e86321d06 100644
--- a/ext/openssl/ossl_engine.c
+++ b/ext/openssl/ossl_engine.c
@@ -78,7 +78,7 @@ static const rb_data_type_t ossl_engine_type = {
{
0, ossl_engine_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
diff --git a/ext/openssl/ossl_hmac.c b/ext/openssl/ossl_hmac.c
index 1a5f471a27..c485ba7e67 100644
--- a/ext/openssl/ossl_hmac.c
+++ b/ext/openssl/ossl_hmac.c
@@ -42,7 +42,7 @@ static const rb_data_type_t ossl_hmac_type = {
{
0, ossl_hmac_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE
diff --git a/ext/openssl/ossl_kdf.c b/ext/openssl/ossl_kdf.c
index 0d25a7304b..ba197a659e 100644
--- a/ext/openssl/ossl_kdf.c
+++ b/ext/openssl/ossl_kdf.c
@@ -18,10 +18,10 @@ static VALUE mKDF, eKDF;
* of _length_ bytes.
*
* For more information about PBKDF2, see RFC 2898 Section 5.2
- * (https://tools.ietf.org/html/rfc2898#section-5.2).
+ * (https://www.rfc-editor.org/rfc/rfc2898#section-5.2).
*
* === Parameters
- * pass :: The passphrase.
+ * pass :: The password.
* salt :: The salt. Salts prevent attacks based on dictionaries of common
* passwords and attacks based on rainbow tables. It is a public
* value that can be safely stored along with the password (e.g.
@@ -81,10 +81,10 @@ kdf_pbkdf2_hmac(int argc, VALUE *argv, VALUE self)
* bcrypt.
*
* The keyword arguments _N_, _r_ and _p_ can be used to tune scrypt. RFC 7914
- * (published on 2016-08, https://tools.ietf.org/html/rfc7914#section-2) states
+ * (published on 2016-08, https://www.rfc-editor.org/rfc/rfc7914#section-2) states
* that using values r=8 and p=1 appears to yield good results.
*
- * See RFC 7914 (https://tools.ietf.org/html/rfc7914) for more information.
+ * See RFC 7914 (https://www.rfc-editor.org/rfc/rfc7914) for more information.
*
* === Parameters
* pass :: Passphrase.
@@ -147,7 +147,7 @@ kdf_scrypt(int argc, VALUE *argv, VALUE self)
* KDF.hkdf(ikm, salt:, info:, length:, hash:) -> String
*
* HMAC-based Extract-and-Expand Key Derivation Function (HKDF) as specified in
- * {RFC 5869}[https://tools.ietf.org/html/rfc5869].
+ * {RFC 5869}[https://www.rfc-editor.org/rfc/rfc5869].
*
* New in OpenSSL 1.1.0.
*
@@ -165,7 +165,7 @@ kdf_scrypt(int argc, VALUE *argv, VALUE self)
* The hash function.
*
* === Example
- * # The values from https://datatracker.ietf.org/doc/html/rfc5869#appendix-A.1
+ * # The values from https://www.rfc-editor.org/rfc/rfc5869#appendix-A.1
* ikm = ["0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"].pack("H*")
* salt = ["000102030405060708090a0b0c"].pack("H*")
* info = ["f0f1f2f3f4f5f6f7f8f9"].pack("H*")
diff --git a/ext/openssl/ossl_ns_spki.c b/ext/openssl/ossl_ns_spki.c
index 9b1147367a..9d70b5d87a 100644
--- a/ext/openssl/ossl_ns_spki.c
+++ b/ext/openssl/ossl_ns_spki.c
@@ -50,7 +50,7 @@ static const rb_data_type_t ossl_netscape_spki_type = {
{
0, ossl_netscape_spki_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE
@@ -365,8 +365,8 @@ ossl_spki_verify(VALUE self, VALUE key)
*
* OpenSSL::Netscape is a namespace for SPKI (Simple Public Key
* Infrastructure) which implements Signed Public Key and Challenge.
- * See {RFC 2692}[http://tools.ietf.org/html/rfc2692] and {RFC
- * 2693}[http://tools.ietf.org/html/rfc2692] for details.
+ * See {RFC 2692}[https://www.rfc-editor.org/rfc/rfc2692] and {RFC
+ * 2693}[https://www.rfc-editor.org/rfc/rfc2692] for details.
*/
/* Document-class: OpenSSL::Netscape::SPKIError
diff --git a/ext/openssl/ossl_ocsp.c b/ext/openssl/ossl_ocsp.c
index 9c8d768d87..df986bb3ee 100644
--- a/ext/openssl/ossl_ocsp.c
+++ b/ext/openssl/ossl_ocsp.c
@@ -86,7 +86,7 @@ static const rb_data_type_t ossl_ocsp_request_type = {
{
0, ossl_ocsp_request_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static void
@@ -100,7 +100,7 @@ static const rb_data_type_t ossl_ocsp_response_type = {
{
0, ossl_ocsp_response_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static void
@@ -114,7 +114,7 @@ static const rb_data_type_t ossl_ocsp_basicresp_type = {
{
0, ossl_ocsp_basicresp_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static void
@@ -128,7 +128,7 @@ static const rb_data_type_t ossl_ocsp_singleresp_type = {
{
0, ossl_ocsp_singleresp_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static void
@@ -142,7 +142,7 @@ static const rb_data_type_t ossl_ocsp_certid_type = {
{
0, ossl_ocsp_certid_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
@@ -1701,7 +1701,7 @@ Init_ossl_ocsp(void)
* require 'net/http'
*
* http_response =
- * Net::HTTP.start ocsp_uri.hostname, ocsp.port do |http|
+ * Net::HTTP.start ocsp_uri.hostname, ocsp_uri.port do |http|
* http.post ocsp_uri.path, request.to_der,
* 'content-type' => 'application/ocsp-request'
* end
diff --git a/ext/openssl/ossl_pkcs12.c b/ext/openssl/ossl_pkcs12.c
index fb947df1d0..164b2da465 100644
--- a/ext/openssl/ossl_pkcs12.c
+++ b/ext/openssl/ossl_pkcs12.c
@@ -44,7 +44,7 @@ static const rb_data_type_t ossl_pkcs12_type = {
{
0, ossl_pkcs12_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE
diff --git a/ext/openssl/ossl_pkcs7.c b/ext/openssl/ossl_pkcs7.c
index dbe5347639..78dcbd667a 100644
--- a/ext/openssl/ossl_pkcs7.c
+++ b/ext/openssl/ossl_pkcs7.c
@@ -65,7 +65,7 @@ const rb_data_type_t ossl_pkcs7_type = {
{
0, ossl_pkcs7_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static void
@@ -79,7 +79,7 @@ static const rb_data_type_t ossl_pkcs7_signer_info_type = {
{
0, ossl_pkcs7_signer_info_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static void
@@ -93,7 +93,7 @@ static const rb_data_type_t ossl_pkcs7_recip_info_type = {
{
0, ossl_pkcs7_recip_info_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c
index 476256679b..013412c27f 100644
--- a/ext/openssl/ossl_pkey.c
+++ b/ext/openssl/ossl_pkey.c
@@ -35,7 +35,7 @@ const rb_data_type_t ossl_evp_pkey_type = {
{
0, ossl_evp_pkey_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE
@@ -82,31 +82,62 @@ ossl_pkey_new(EVP_PKEY *pkey)
#if OSSL_OPENSSL_PREREQ(3, 0, 0)
# include <openssl/decoder.h>
-EVP_PKEY *
-ossl_pkey_read_generic(BIO *bio, VALUE pass)
+static EVP_PKEY *
+ossl_pkey_read(BIO *bio, const char *input_type, int selection, VALUE pass)
{
void *ppass = (void *)pass;
OSSL_DECODER_CTX *dctx;
EVP_PKEY *pkey = NULL;
int pos = 0, pos2;
- dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, "DER", NULL, NULL, 0, NULL, NULL);
+ dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, input_type, NULL, NULL,
+ selection, NULL, NULL);
if (!dctx)
goto out;
- if (OSSL_DECODER_CTX_set_pem_password_cb(dctx, ossl_pem_passwd_cb, ppass) != 1)
- goto out;
-
- /* First check DER */
- if (OSSL_DECODER_from_bio(dctx, bio) == 1)
+ if (OSSL_DECODER_CTX_set_pem_password_cb(dctx, ossl_pem_passwd_cb,
+ ppass) != 1)
goto out;
+ while (1) {
+ if (OSSL_DECODER_from_bio(dctx, bio) == 1)
+ goto out;
+ if (BIO_eof(bio))
+ break;
+ pos2 = BIO_tell(bio);
+ if (pos2 < 0 || pos2 <= pos)
+ break;
+ ossl_clear_error();
+ pos = pos2;
+ }
+ out:
OSSL_BIO_reset(bio);
+ OSSL_DECODER_CTX_free(dctx);
+ return pkey;
+}
- /* Then check PEM; multiple OSSL_DECODER_from_bio() calls may be needed */
- if (OSSL_DECODER_CTX_set_input_type(dctx, "PEM") != 1)
- goto out;
+EVP_PKEY *
+ossl_pkey_read_generic(BIO *bio, VALUE pass)
+{
+ EVP_PKEY *pkey = NULL;
+ /* First check DER, then check PEM. */
+ const char *input_types[] = {"DER", "PEM"};
+ int input_type_num = (int)(sizeof(input_types) / sizeof(char *));
/*
- * First check for private key formats. This is to keep compatibility with
- * ruby/openssl < 3.0 which decoded the following as a private key.
+ * Non-zero selections to try to decode.
+ *
+ * See EVP_PKEY_fromdata(3) - Selections to see all the selections.
+ *
+ * This is a workaround for the decoder failing to decode or returning
+ * bogus keys with selection 0, if a key management provider is different
+ * from a decoder provider. The workaround is to avoid using selection 0.
+ *
+ * Affected OpenSSL versions: >= 3.1.0, <= 3.1.2, or >= 3.0.0, <= 3.0.10
+ * Fixed OpenSSL versions: 3.2, next release of the 3.1.z and 3.0.z
+ *
+ * See https://github.com/openssl/openssl/pull/21519 for details.
+ *
+ * First check for private key formats (EVP_PKEY_KEYPAIR). This is to keep
+ * compatibility with ruby/openssl < 3.0 which decoded the following as a
+ * private key.
*
* $ openssl ecparam -name prime256v1 -genkey -outform PEM
* -----BEGIN EC PARAMETERS-----
@@ -124,36 +155,28 @@ ossl_pkey_read_generic(BIO *bio, VALUE pass)
*
* Note that normally, the input is supposed to contain a single decodable
* PEM block only, so this special handling should not create a new problem.
+ *
+ * Note that we need to create the OSSL_DECODER_CTX variable each time when
+ * we use the different selection as a workaround.
+ * See https://github.com/openssl/openssl/issues/20657 for details.
*/
- OSSL_DECODER_CTX_set_selection(dctx, EVP_PKEY_KEYPAIR);
- while (1) {
- if (OSSL_DECODER_from_bio(dctx, bio) == 1)
- goto out;
- if (BIO_eof(bio))
- break;
- pos2 = BIO_tell(bio);
- if (pos2 < 0 || pos2 <= pos)
- break;
- ossl_clear_error();
- pos = pos2;
- }
-
- OSSL_BIO_reset(bio);
- OSSL_DECODER_CTX_set_selection(dctx, 0);
- while (1) {
- if (OSSL_DECODER_from_bio(dctx, bio) == 1)
- goto out;
- if (BIO_eof(bio))
- break;
- pos2 = BIO_tell(bio);
- if (pos2 < 0 || pos2 <= pos)
- break;
- ossl_clear_error();
- pos = pos2;
+ int selections[] = {
+ EVP_PKEY_KEYPAIR,
+ EVP_PKEY_KEY_PARAMETERS,
+ EVP_PKEY_PUBLIC_KEY
+ };
+ int selection_num = (int)(sizeof(selections) / sizeof(int));
+ int i, j;
+
+ for (i = 0; i < input_type_num; i++) {
+ for (j = 0; j < selection_num; j++) {
+ pkey = ossl_pkey_read(bio, input_types[i], selections[j], pass);
+ if (pkey) {
+ goto out;
+ }
+ }
}
-
out:
- OSSL_DECODER_CTX_free(dctx);
return pkey;
}
#else
@@ -260,9 +283,9 @@ struct pkey_blocking_generate_arg {
EVP_PKEY_CTX *ctx;
EVP_PKEY *pkey;
int state;
- int yield: 1;
- int genparam: 1;
- int interrupted: 1;
+ unsigned int yield: 1;
+ unsigned int genparam: 1;
+ unsigned int interrupted: 1;
};
static VALUE
@@ -612,6 +635,72 @@ ossl_pkey_initialize_copy(VALUE self, VALUE other)
}
#endif
+#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
+/*
+ * call-seq:
+ * OpenSSL::PKey.new_raw_private_key(algo, string) -> PKey
+ *
+ * See the OpenSSL documentation for EVP_PKEY_new_raw_private_key()
+ */
+
+static VALUE
+ossl_pkey_new_raw_private_key(VALUE self, VALUE type, VALUE key)
+{
+ EVP_PKEY *pkey;
+ const EVP_PKEY_ASN1_METHOD *ameth;
+ int pkey_id;
+ size_t keylen;
+
+ StringValue(type);
+ StringValue(key);
+ ameth = EVP_PKEY_asn1_find_str(NULL, RSTRING_PTR(type), RSTRING_LENINT(type));
+ if (!ameth)
+ ossl_raise(ePKeyError, "algorithm %"PRIsVALUE" not found", type);
+ EVP_PKEY_asn1_get0_info(&pkey_id, NULL, NULL, NULL, NULL, ameth);
+
+ keylen = RSTRING_LEN(key);
+
+ pkey = EVP_PKEY_new_raw_private_key(pkey_id, NULL, (unsigned char *)RSTRING_PTR(key), keylen);
+ if (!pkey)
+ ossl_raise(ePKeyError, "EVP_PKEY_new_raw_private_key");
+
+ return ossl_pkey_new(pkey);
+}
+#endif
+
+#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
+/*
+ * call-seq:
+ * OpenSSL::PKey.new_raw_public_key(algo, string) -> PKey
+ *
+ * See the OpenSSL documentation for EVP_PKEY_new_raw_public_key()
+ */
+
+static VALUE
+ossl_pkey_new_raw_public_key(VALUE self, VALUE type, VALUE key)
+{
+ EVP_PKEY *pkey;
+ const EVP_PKEY_ASN1_METHOD *ameth;
+ int pkey_id;
+ size_t keylen;
+
+ StringValue(type);
+ StringValue(key);
+ ameth = EVP_PKEY_asn1_find_str(NULL, RSTRING_PTR(type), RSTRING_LENINT(type));
+ if (!ameth)
+ ossl_raise(ePKeyError, "algorithm %"PRIsVALUE" not found", type);
+ EVP_PKEY_asn1_get0_info(&pkey_id, NULL, NULL, NULL, NULL, ameth);
+
+ keylen = RSTRING_LEN(key);
+
+ pkey = EVP_PKEY_new_raw_public_key(pkey_id, NULL, (unsigned char *)RSTRING_PTR(key), keylen);
+ if (!pkey)
+ ossl_raise(ePKeyError, "EVP_PKEY_new_raw_public_key");
+
+ return ossl_pkey_new(pkey);
+}
+#endif
+
/*
* call-seq:
* pkey.oid -> string
@@ -793,6 +882,18 @@ ossl_pkey_private_to_der(int argc, VALUE *argv, VALUE self)
*
* Serializes the private key to PEM-encoded PKCS #8 format. See #private_to_der
* for more details.
+ *
+ * An unencrypted PEM-encoded key will look like:
+ *
+ * -----BEGIN PRIVATE KEY-----
+ * [...]
+ * -----END PRIVATE KEY-----
+ *
+ * An encrypted PEM-encoded key will look like:
+ *
+ * -----BEGIN ENCRYPTED PRIVATE KEY-----
+ * [...]
+ * -----END ENCRYPTED PRIVATE KEY-----
*/
static VALUE
ossl_pkey_private_to_pem(int argc, VALUE *argv, VALUE self)
@@ -800,6 +901,35 @@ ossl_pkey_private_to_pem(int argc, VALUE *argv, VALUE self)
return do_pkcs8_export(argc, argv, self, 0);
}
+#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
+/*
+ * call-seq:
+ * pkey.raw_private_key => string
+ *
+ * See the OpenSSL documentation for EVP_PKEY_get_raw_private_key()
+ */
+
+static VALUE
+ossl_pkey_raw_private_key(VALUE self)
+{
+ EVP_PKEY *pkey;
+ VALUE str;
+ size_t len;
+
+ GetPKey(self, pkey);
+ if (EVP_PKEY_get_raw_private_key(pkey, NULL, &len) != 1)
+ ossl_raise(ePKeyError, "EVP_PKEY_get_raw_private_key");
+ str = rb_str_new(NULL, len);
+
+ if (EVP_PKEY_get_raw_private_key(pkey, (unsigned char *)RSTRING_PTR(str), &len) != 1)
+ ossl_raise(ePKeyError, "EVP_PKEY_get_raw_private_key");
+
+ rb_str_set_len(str, len);
+
+ return str;
+}
+#endif
+
VALUE
ossl_pkey_export_spki(VALUE self, int to_der)
{
@@ -842,6 +972,12 @@ ossl_pkey_public_to_der(VALUE self)
* pkey.public_to_pem -> string
*
* Serializes the public key to PEM-encoded X.509 SubjectPublicKeyInfo format.
+ *
+ * A PEM-encoded key will look like:
+ *
+ * -----BEGIN PUBLIC KEY-----
+ * [...]
+ * -----END PUBLIC KEY-----
*/
static VALUE
ossl_pkey_public_to_pem(VALUE self)
@@ -849,6 +985,35 @@ ossl_pkey_public_to_pem(VALUE self)
return ossl_pkey_export_spki(self, 0);
}
+#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
+/*
+ * call-seq:
+ * pkey.raw_public_key => string
+ *
+ * See the OpenSSL documentation for EVP_PKEY_get_raw_public_key()
+ */
+
+static VALUE
+ossl_pkey_raw_public_key(VALUE self)
+{
+ EVP_PKEY *pkey;
+ VALUE str;
+ size_t len;
+
+ GetPKey(self, pkey);
+ if (EVP_PKEY_get_raw_public_key(pkey, NULL, &len) != 1)
+ ossl_raise(ePKeyError, "EVP_PKEY_get_raw_public_key");
+ str = rb_str_new(NULL, len);
+
+ if (EVP_PKEY_get_raw_public_key(pkey, (unsigned char *)RSTRING_PTR(str), &len) != 1)
+ ossl_raise(ePKeyError, "EVP_PKEY_get_raw_public_key");
+
+ rb_str_set_len(str, len);
+
+ return str;
+}
+#endif
+
/*
* call-seq:
* pkey.compare?(another_pkey) -> true | false
@@ -1586,6 +1751,10 @@ Init_ossl_pkey(void)
rb_define_module_function(mPKey, "read", ossl_pkey_new_from_data, -1);
rb_define_module_function(mPKey, "generate_parameters", ossl_pkey_s_generate_parameters, -1);
rb_define_module_function(mPKey, "generate_key", ossl_pkey_s_generate_key, -1);
+#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
+ rb_define_module_function(mPKey, "new_raw_private_key", ossl_pkey_new_raw_private_key, 2);
+ rb_define_module_function(mPKey, "new_raw_public_key", ossl_pkey_new_raw_public_key, 2);
+#endif
rb_define_alloc_func(cPKey, ossl_pkey_alloc);
rb_define_method(cPKey, "initialize", ossl_pkey_initialize, 0);
@@ -1601,6 +1770,10 @@ Init_ossl_pkey(void)
rb_define_method(cPKey, "private_to_pem", ossl_pkey_private_to_pem, -1);
rb_define_method(cPKey, "public_to_der", ossl_pkey_public_to_der, 0);
rb_define_method(cPKey, "public_to_pem", ossl_pkey_public_to_pem, 0);
+#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
+ rb_define_method(cPKey, "raw_private_key", ossl_pkey_raw_private_key, 0);
+ rb_define_method(cPKey, "raw_public_key", ossl_pkey_raw_public_key, 0);
+#endif
rb_define_method(cPKey, "compare?", ossl_pkey_compare, 1);
rb_define_method(cPKey, "sign", ossl_pkey_sign, -1);
diff --git a/ext/openssl/ossl_pkey_dh.c b/ext/openssl/ossl_pkey_dh.c
index 83c41378fe..a231814a99 100644
--- a/ext/openssl/ossl_pkey_dh.c
+++ b/ext/openssl/ossl_pkey_dh.c
@@ -216,9 +216,20 @@ ossl_dh_is_private(VALUE self)
* dh.to_pem -> aString
* dh.to_s -> aString
*
- * Encodes this DH to its PEM encoding. Note that any existing per-session
- * public/private keys will *not* get encoded, just the Diffie-Hellman
- * parameters will be encoded.
+ * Serializes the DH parameters to a PEM-encoding.
+ *
+ * Note that any existing per-session public/private keys will *not* get
+ * encoded, just the Diffie-Hellman parameters will be encoded.
+ *
+ * PEM-encoded parameters will look like:
+ *
+ * -----BEGIN DH PARAMETERS-----
+ * [...]
+ * -----END DH PARAMETERS-----
+ *
+ * See also #public_to_pem (X.509 SubjectPublicKeyInfo) and
+ * #private_to_pem (PKCS #8 PrivateKeyInfo or EncryptedPrivateKeyInfo) for
+ * serialization with the private or public key components.
*/
static VALUE
ossl_dh_export(VALUE self)
@@ -244,10 +255,14 @@ ossl_dh_export(VALUE self)
* call-seq:
* dh.to_der -> aString
*
- * Encodes this DH to its DER encoding. Note that any existing per-session
- * public/private keys will *not* get encoded, just the Diffie-Hellman
- * parameters will be encoded.
-
+ * Serializes the DH parameters to a DER-encoding
+ *
+ * Note that any existing per-session public/private keys will *not* get
+ * encoded, just the Diffie-Hellman parameters will be encoded.
+ *
+ * See also #public_to_der (X.509 SubjectPublicKeyInfo) and
+ * #private_to_der (PKCS #8 PrivateKeyInfo or EncryptedPrivateKeyInfo) for
+ * serialization with the private or public key components.
*/
static VALUE
ossl_dh_to_der(VALUE self)
diff --git a/ext/openssl/ossl_pkey_dsa.c b/ext/openssl/ossl_pkey_dsa.c
index b097f8c9d2..058ce73888 100644
--- a/ext/openssl/ossl_pkey_dsa.c
+++ b/ext/openssl/ossl_pkey_dsa.c
@@ -211,16 +211,58 @@ ossl_dsa_is_private(VALUE self)
* dsa.to_pem([cipher, password]) -> aString
* dsa.to_s([cipher, password]) -> aString
*
- * Encodes this DSA to its PEM encoding.
+ * Serializes a private or public key to a PEM-encoding.
*
- * === Parameters
- * * _cipher_ is an OpenSSL::Cipher.
- * * _password_ is a string containing your password.
+ * [When the key contains public components only]
*
- * === Examples
- * DSA.to_pem -> aString
- * DSA.to_pem(cipher, 'mypassword') -> aString
+ * Serializes it into an X.509 SubjectPublicKeyInfo.
+ * The parameters _cipher_ and _password_ are ignored.
*
+ * A PEM-encoded key will look like:
+ *
+ * -----BEGIN PUBLIC KEY-----
+ * [...]
+ * -----END PUBLIC KEY-----
+ *
+ * Consider using #public_to_pem instead. This serializes the key into an
+ * X.509 SubjectPublicKeyInfo regardless of whether it is a public key
+ * or a private key.
+ *
+ * [When the key contains private components, and no parameters are given]
+ *
+ * Serializes it into a traditional \OpenSSL DSAPrivateKey.
+ *
+ * A PEM-encoded key will look like:
+ *
+ * -----BEGIN DSA PRIVATE KEY-----
+ * [...]
+ * -----END DSA PRIVATE KEY-----
+ *
+ * [When the key contains private components, and _cipher_ and _password_ are given]
+ *
+ * Serializes it into a traditional \OpenSSL DSAPrivateKey and encrypts it in
+ * OpenSSL's traditional PEM encryption format.
+ * _cipher_ must be a cipher name understood by OpenSSL::Cipher.new or an
+ * instance of OpenSSL::Cipher.
+ *
+ * An encrypted PEM-encoded key will look like:
+ *
+ * -----BEGIN DSA PRIVATE KEY-----
+ * Proc-Type: 4,ENCRYPTED
+ * DEK-Info: AES-128-CBC,733F5302505B34701FC41F5C0746E4C0
+ *
+ * [...]
+ * -----END DSA PRIVATE KEY-----
+ *
+ * Note that this format uses MD5 to derive the encryption key, and hence
+ * will not be available on FIPS-compliant systems.
+ *
+ * <b>This method is kept for compatibility.</b>
+ * This should only be used when the traditional, non-standard \OpenSSL format
+ * is required.
+ *
+ * Consider using #public_to_pem (X.509 SubjectPublicKeyInfo) or #private_to_pem
+ * (PKCS #8 PrivateKeyInfo or EncryptedPrivateKeyInfo) instead.
*/
static VALUE
ossl_dsa_export(int argc, VALUE *argv, VALUE self)
@@ -238,8 +280,15 @@ ossl_dsa_export(int argc, VALUE *argv, VALUE self)
* call-seq:
* dsa.to_der -> aString
*
- * Encodes this DSA to its DER encoding.
+ * Serializes a private or public key to a DER-encoding.
+ *
+ * See #to_pem for details.
+ *
+ * <b>This method is kept for compatibility.</b>
+ * This should only be used when the traditional, non-standard \OpenSSL format
+ * is required.
*
+ * Consider using #public_to_der or #private_to_der instead.
*/
static VALUE
ossl_dsa_to_der(VALUE self)
diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c
index 92842f95ac..4b3a1fd0fe 100644
--- a/ext/openssl/ossl_pkey_ec.c
+++ b/ext/openssl/ossl_pkey_ec.c
@@ -400,13 +400,61 @@ static VALUE ossl_ec_key_is_private(VALUE self)
/*
* call-seq:
- * key.export([cipher, pass_phrase]) => String
- * key.to_pem([cipher, pass_phrase]) => String
+ * key.export([cipher, password]) => String
+ * key.to_pem([cipher, password]) => String
*
- * Outputs the EC key in PEM encoding. If _cipher_ and _pass_phrase_ are given
- * they will be used to encrypt the key. _cipher_ must be an OpenSSL::Cipher
- * instance. Note that encryption will only be effective for a private key,
- * public keys will always be encoded in plain text.
+ * Serializes a private or public key to a PEM-encoding.
+ *
+ * [When the key contains public components only]
+ *
+ * Serializes it into an X.509 SubjectPublicKeyInfo.
+ * The parameters _cipher_ and _password_ are ignored.
+ *
+ * A PEM-encoded key will look like:
+ *
+ * -----BEGIN PUBLIC KEY-----
+ * [...]
+ * -----END PUBLIC KEY-----
+ *
+ * Consider using #public_to_pem instead. This serializes the key into an
+ * X.509 SubjectPublicKeyInfo regardless of whether it is a public key
+ * or a private key.
+ *
+ * [When the key contains private components, and no parameters are given]
+ *
+ * Serializes it into a SEC 1/RFC 5915 ECPrivateKey.
+ *
+ * A PEM-encoded key will look like:
+ *
+ * -----BEGIN EC PRIVATE KEY-----
+ * [...]
+ * -----END EC PRIVATE KEY-----
+ *
+ * [When the key contains private components, and _cipher_ and _password_ are given]
+ *
+ * Serializes it into a SEC 1/RFC 5915 ECPrivateKey
+ * and encrypts it in OpenSSL's traditional PEM encryption format.
+ * _cipher_ must be a cipher name understood by OpenSSL::Cipher.new or an
+ * instance of OpenSSL::Cipher.
+ *
+ * An encrypted PEM-encoded key will look like:
+ *
+ * -----BEGIN EC PRIVATE KEY-----
+ * Proc-Type: 4,ENCRYPTED
+ * DEK-Info: AES-128-CBC,733F5302505B34701FC41F5C0746E4C0
+ *
+ * [...]
+ * -----END EC PRIVATE KEY-----
+ *
+ * Note that this format uses MD5 to derive the encryption key, and hence
+ * will not be available on FIPS-compliant systems.
+ *
+ * <b>This method is kept for compatibility.</b>
+ * This should only be used when the SEC 1/RFC 5915 ECPrivateKey format is
+ * required.
+ *
+ * Consider using #public_to_pem (X.509 SubjectPublicKeyInfo) or #private_to_pem
+ * (PKCS #8 PrivateKeyInfo or EncryptedPrivateKeyInfo) instead.
*/
static VALUE
ossl_ec_key_export(int argc, VALUE *argv, VALUE self)
@@ -426,7 +474,15 @@ ossl_ec_key_export(int argc, VALUE *argv, VALUE self)
* call-seq:
* key.to_der => String
*
- * See the OpenSSL documentation for i2d_ECPrivateKey_bio()
+ * Serializes a private or public key to a DER-encoding.
+ *
+ * See #to_pem for details.
+ *
+ * <b>This method is kept for compatibility.</b>
+ * This should only be used when the SEC 1/RFC 5915 ECPrivateKey format is
+ * required.
+ *
+ * Consider using #public_to_der or #private_to_der instead.
*/
static VALUE
ossl_ec_key_to_der(VALUE self)
@@ -530,7 +586,7 @@ static const rb_data_type_t ossl_ec_group_type = {
{
0, ossl_ec_group_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE
@@ -1115,7 +1171,7 @@ static const rb_data_type_t ossl_ec_point_type = {
{
0, ossl_ec_point_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE
diff --git a/ext/openssl/ossl_pkey_rsa.c b/ext/openssl/ossl_pkey_rsa.c
index 072adabe62..389f76f309 100644
--- a/ext/openssl/ossl_pkey_rsa.c
+++ b/ext/openssl/ossl_pkey_rsa.c
@@ -50,8 +50,8 @@ VALUE eRSAError;
/*
* call-seq:
* RSA.new -> rsa
- * RSA.new(encoded_key [, passphrase]) -> rsa
- * RSA.new(encoded_key) { passphrase } -> rsa
+ * RSA.new(encoded_key [, password ]) -> rsa
+ * RSA.new(encoded_key) { password } -> rsa
* RSA.new(size [, exponent]) -> rsa
*
* Generates or loads an \RSA keypair.
@@ -61,9 +61,9 @@ VALUE eRSAError;
* #set_crt_params.
*
* If called with a String, tries to parse as DER or PEM encoding of an \RSA key.
- * Note that, if _passphrase_ is not specified but the key is encrypted with a
- * passphrase, \OpenSSL will prompt for it.
- * See also OpenSSL::PKey.read which can parse keys of any kinds.
+ * Note that if _password_ is not specified, but the key is encrypted with a
+ * password, \OpenSSL will prompt for it.
+ * See also OpenSSL::PKey.read which can parse keys of any kind.
*
* If called with a number, generates a new key pair. This form works as an
* alias of RSA.generate.
@@ -71,7 +71,7 @@ VALUE eRSAError;
* Examples:
* OpenSSL::PKey::RSA.new 2048
* OpenSSL::PKey::RSA.new File.read 'rsa.pem'
- * OpenSSL::PKey::RSA.new File.read('rsa.pem'), 'my pass phrase'
+ * OpenSSL::PKey::RSA.new File.read('rsa.pem'), 'my password'
*/
static VALUE
ossl_rsa_initialize(int argc, VALUE *argv, VALUE self)
@@ -217,13 +217,61 @@ can_export_rsaprivatekey(VALUE self)
/*
* call-seq:
- * rsa.export([cipher, pass_phrase]) => PEM-format String
- * rsa.to_pem([cipher, pass_phrase]) => PEM-format String
- * rsa.to_s([cipher, pass_phrase]) => PEM-format String
+ * rsa.export([cipher, password]) => PEM-format String
+ * rsa.to_pem([cipher, password]) => PEM-format String
+ * rsa.to_s([cipher, password]) => PEM-format String
*
- * Outputs this keypair in PEM encoding. If _cipher_ and _pass_phrase_ are
- * given they will be used to encrypt the key. _cipher_ must be an
- * OpenSSL::Cipher instance.
+ * Serializes a private or public key to a PEM-encoding.
+ *
+ * [When the key contains public components only]
+ *
+ * Serializes it into an X.509 SubjectPublicKeyInfo.
+ * The parameters _cipher_ and _password_ are ignored.
+ *
+ * A PEM-encoded key will look like:
+ *
+ * -----BEGIN PUBLIC KEY-----
+ * [...]
+ * -----END PUBLIC KEY-----
+ *
+ * Consider using #public_to_pem instead. This serializes the key into an
+ * X.509 SubjectPublicKeyInfo regardless of whether the key is a public key
+ * or a private key.
+ *
+ * [When the key contains private components, and no parameters are given]
+ *
+ * Serializes it into a PKCS #1 RSAPrivateKey.
+ *
+ * A PEM-encoded key will look like:
+ *
+ * -----BEGIN RSA PRIVATE KEY-----
+ * [...]
+ * -----END RSA PRIVATE KEY-----
+ *
+ * [When the key contains private components, and _cipher_ and _password_ are given]
+ *
+ * Serializes it into a PKCS #1 RSAPrivateKey
+ * and encrypts it in OpenSSL's traditional PEM encryption format.
+ * _cipher_ must be a cipher name understood by OpenSSL::Cipher.new or an
+ * instance of OpenSSL::Cipher.
+ *
+ * An encrypted PEM-encoded key will look like:
+ *
+ * -----BEGIN RSA PRIVATE KEY-----
+ * Proc-Type: 4,ENCRYPTED
+ * DEK-Info: AES-128-CBC,733F5302505B34701FC41F5C0746E4C0
+ *
+ * [...]
+ * -----END RSA PRIVATE KEY-----
+ *
+ * Note that this format uses MD5 to derive the encryption key, and hence
+ * will not be available on FIPS-compliant systems.
+ *
+ * <b>This method is kept for compatibility.</b>
+ * This should only be used when the PKCS #1 RSAPrivateKey format is required.
+ *
+ * Consider using #public_to_pem (X.509 SubjectPublicKeyInfo) or #private_to_pem
+ * (PKCS #8 PrivateKeyInfo or EncryptedPrivateKeyInfo) instead.
*/
static VALUE
ossl_rsa_export(int argc, VALUE *argv, VALUE self)
@@ -238,7 +286,14 @@ ossl_rsa_export(int argc, VALUE *argv, VALUE self)
* call-seq:
* rsa.to_der => DER-format String
*
- * Outputs this keypair in DER encoding.
+ * Serializes a private or public key to a DER-encoding.
+ *
+ * See #to_pem for details.
+ *
+ * <b>This method is kept for compatibility.</b>
+ * This should only be used when the PKCS #1 RSAPrivateKey format is required.
+ *
+ * Consider using #public_to_der or #private_to_der instead.
*/
static VALUE
ossl_rsa_to_der(VALUE self)
diff --git a/ext/openssl/ossl_provider.c b/ext/openssl/ossl_provider.c
new file mode 100644
index 0000000000..981c6ccdc7
--- /dev/null
+++ b/ext/openssl/ossl_provider.c
@@ -0,0 +1,211 @@
+/*
+ * This program is licensed under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#include "ossl.h"
+
+#ifdef OSSL_USE_PROVIDER
+# include <openssl/provider.h>
+
+#define NewProvider(klass) \
+ TypedData_Wrap_Struct((klass), &ossl_provider_type, 0)
+#define SetProvider(obj, provider) do { \
+ if (!(provider)) { \
+ ossl_raise(rb_eRuntimeError, "Provider wasn't initialized."); \
+ } \
+ RTYPEDDATA_DATA(obj) = (provider); \
+} while(0)
+#define GetProvider(obj, provider) do { \
+ TypedData_Get_Struct((obj), OSSL_PROVIDER, &ossl_provider_type, (provider)); \
+ if (!(provider)) { \
+ ossl_raise(rb_eRuntimeError, "PROVIDER wasn't initialized."); \
+ } \
+} while (0)
+
+static const rb_data_type_t ossl_provider_type = {
+ "OpenSSL/Provider",
+ {
+ 0,
+ },
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
+};
+
+/*
+ * Classes
+ */
+/* Document-class: OpenSSL::Provider
+ *
+ * This class is the access to openssl's Provider
+ * See also, https://www.openssl.org/docs/manmaster/man7/provider.html
+ */
+static VALUE cProvider;
+/* Document-class: OpenSSL::Provider::ProviderError
+ *
+ * This is the generic exception for OpenSSL::Provider related errors
+ */
+static VALUE eProviderError;
+
+/*
+ * call-seq:
+ * OpenSSL::Provider.load(name) -> provider
+ *
+ * This method loads and initializes a provider
+ */
+static VALUE
+ossl_provider_s_load(VALUE klass, VALUE name)
+{
+ OSSL_PROVIDER *provider = NULL;
+ VALUE obj;
+
+ const char *provider_name_ptr = StringValueCStr(name);
+
+ provider = OSSL_PROVIDER_load(NULL, provider_name_ptr);
+ if (provider == NULL) {
+ ossl_raise(eProviderError, "Failed to load %s provider", provider_name_ptr);
+ }
+ obj = NewProvider(klass);
+ SetProvider(obj, provider);
+
+ return obj;
+}
+
+struct ary_with_state { VALUE ary; int state; };
+struct rb_push_provider_name_args { OSSL_PROVIDER *prov; VALUE ary; };
+
+static VALUE
+rb_push_provider_name(VALUE rb_push_provider_name_args)
+{
+ struct rb_push_provider_name_args *args = (struct rb_push_provider_name_args *)rb_push_provider_name_args;
+
+ VALUE name = rb_str_new2(OSSL_PROVIDER_get0_name(args->prov));
+ return rb_ary_push(args->ary, name);
+}
+
+static int
+push_provider(OSSL_PROVIDER *prov, void *cbdata)
+{
+ struct ary_with_state *ary_with_state = (struct ary_with_state *)cbdata;
+ struct rb_push_provider_name_args args = { prov, ary_with_state->ary };
+
+ rb_protect(rb_push_provider_name, (VALUE)&args, &ary_with_state->state);
+ if (ary_with_state->state) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+/*
+ * call-seq:
+ * OpenSSL::Provider.provider_names -> [provider_name, ...]
+ *
+ * Returns an array of currently loaded provider names.
+ */
+static VALUE
+ossl_provider_s_provider_names(VALUE klass)
+{
+ VALUE ary = rb_ary_new();
+ struct ary_with_state cbdata = { ary, 0 };
+
+ int result = OSSL_PROVIDER_do_all(NULL, &push_provider, (void*)&cbdata);
+ if (result != 1 ) {
+ if (cbdata.state) {
+ rb_jump_tag(cbdata.state);
+ } else {
+ ossl_raise(eProviderError, "Failed to load provider names");
+ }
+ }
+
+ return ary;
+}
+
+/*
+ * call-seq:
+ * provider.unload -> true
+ *
+ * This method unloads this provider.
+ *
+ * if provider unload fails or already unloaded, it raises OpenSSL::Provider::ProviderError
+ */
+static VALUE
+ossl_provider_unload(VALUE self)
+{
+ OSSL_PROVIDER *prov;
+ if (RTYPEDDATA_DATA(self) == NULL) {
+ ossl_raise(eProviderError, "Provider already unloaded.");
+ }
+ GetProvider(self, prov);
+
+ int result = OSSL_PROVIDER_unload(prov);
+
+ if (result != 1) {
+ ossl_raise(eProviderError, "Failed to unload provider");
+ }
+ RTYPEDDATA_DATA(self) = NULL;
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * provider.name -> string
+ *
+ * Get the name of this provider.
+ *
+ * if this provider is already unloaded, it raises OpenSSL::Provider::ProviderError
+ */
+static VALUE
+ossl_provider_get_name(VALUE self)
+{
+ OSSL_PROVIDER *prov;
+ if (RTYPEDDATA_DATA(self) == NULL) {
+ ossl_raise(eProviderError, "Provider already unloaded.");
+ }
+ GetProvider(self, prov);
+
+ return rb_str_new2(OSSL_PROVIDER_get0_name(prov));
+}
+
+/*
+ * call-seq:
+ * provider.inspect -> string
+ *
+ * Pretty prints this provider.
+ */
+static VALUE
+ossl_provider_inspect(VALUE self)
+{
+ OSSL_PROVIDER *prov;
+ if (RTYPEDDATA_DATA(self) == NULL ) {
+ return rb_sprintf("#<%"PRIsVALUE" unloaded provider>", rb_obj_class(self));
+ }
+ GetProvider(self, prov);
+
+ return rb_sprintf("#<%"PRIsVALUE" name=\"%s\">",
+ rb_obj_class(self), OSSL_PROVIDER_get0_name(prov));
+}
+
+void
+Init_ossl_provider(void)
+{
+#if 0
+ mOSSL = rb_define_module("OpenSSL");
+ eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
+#endif
+
+ cProvider = rb_define_class_under(mOSSL, "Provider", rb_cObject);
+ eProviderError = rb_define_class_under(cProvider, "ProviderError", eOSSLError);
+
+ rb_undef_alloc_func(cProvider);
+ rb_define_singleton_method(cProvider, "load", ossl_provider_s_load, 1);
+ rb_define_singleton_method(cProvider, "provider_names", ossl_provider_s_provider_names, 0);
+
+ rb_define_method(cProvider, "unload", ossl_provider_unload, 0);
+ rb_define_method(cProvider, "name", ossl_provider_get_name, 0);
+ rb_define_method(cProvider, "inspect", ossl_provider_inspect, 0);
+}
+#else
+void
+Init_ossl_provider(void)
+{
+}
+#endif
diff --git a/ext/openssl/ossl_provider.h b/ext/openssl/ossl_provider.h
new file mode 100644
index 0000000000..1d69cb1e44
--- /dev/null
+++ b/ext/openssl/ossl_provider.h
@@ -0,0 +1,5 @@
+#if !defined(OSSL_PROVIDER_H)
+#define OSSL_PROVIDER_H
+
+void Init_ossl_provider(void);
+#endif
diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c
index b5bd508c7c..9f374b65ff 100644
--- a/ext/openssl/ossl_ssl.c
+++ b/ext/openssl/ossl_ssl.c
@@ -77,7 +77,7 @@ static const rb_data_type_t ossl_sslctx_type = {
{
ossl_sslctx_mark, ossl_sslctx_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE
@@ -885,9 +885,9 @@ ossl_sslctx_setup(VALUE self)
if (ca_path && !SSL_CTX_load_verify_dir(ctx, ca_path))
ossl_raise(eSSLError, "SSL_CTX_load_verify_dir");
#else
- if(ca_file || ca_path){
- if (!SSL_CTX_load_verify_locations(ctx, ca_file, ca_path))
- rb_warning("can't set verify locations");
+ if (ca_file || ca_path) {
+ if (!SSL_CTX_load_verify_locations(ctx, ca_file, ca_path))
+ ossl_raise(eSSLError, "SSL_CTX_load_verify_locations");
}
#endif
@@ -1553,6 +1553,10 @@ ossl_ssl_mark(void *ptr)
{
SSL *ssl = ptr;
rb_gc_mark((VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx));
+
+ // Note: this reference is stored as @verify_callback so we don't need to mark it.
+ // However we do need to ensure GC compaction won't move it, hence why
+ // we call rb_gc_mark here.
rb_gc_mark((VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_vcb_idx));
}
@@ -1567,7 +1571,7 @@ const rb_data_type_t ossl_ssl_type = {
{
ossl_ssl_mark, ossl_ssl_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE
@@ -1646,6 +1650,8 @@ ossl_ssl_initialize(int argc, VALUE *argv, VALUE self)
SSL_set_ex_data(ssl, ossl_ssl_ex_ptr_idx, (void *)self);
SSL_set_info_callback(ssl, ssl_info_cb);
verify_cb = rb_attr_get(v_ctx, id_i_verify_callback);
+ // We don't need to trigger a write barrier because it's already
+ // an instance variable of this object.
SSL_set_ex_data(ssl, ossl_ssl_ex_vcb_idx, (void *)verify_cb);
rb_call_super(0, NULL);
@@ -1719,11 +1725,20 @@ no_exception_p(VALUE opts)
#define RUBY_IO_TIMEOUT_DEFAULT Qnil
#endif
+#ifdef HAVE_RB_IO_TIMEOUT
+#define IO_TIMEOUT_ERROR rb_eIOTimeoutError
+#else
+#define IO_TIMEOUT_ERROR rb_eIOError
+#endif
+
+
static void
io_wait_writable(VALUE io)
{
#ifdef HAVE_RB_IO_MAYBE_WAIT
- rb_io_maybe_wait_writable(errno, io, RUBY_IO_TIMEOUT_DEFAULT);
+ if (!rb_io_maybe_wait_writable(errno, io, RUBY_IO_TIMEOUT_DEFAULT)) {
+ rb_raise(IO_TIMEOUT_ERROR, "Timed out while waiting to become writable!");
+ }
#else
rb_io_t *fptr;
GetOpenFile(io, fptr);
@@ -1735,7 +1750,9 @@ static void
io_wait_readable(VALUE io)
{
#ifdef HAVE_RB_IO_MAYBE_WAIT
- rb_io_maybe_wait_readable(errno, io, RUBY_IO_TIMEOUT_DEFAULT);
+ if (!rb_io_maybe_wait_readable(errno, io, RUBY_IO_TIMEOUT_DEFAULT)) {
+ rb_raise(IO_TIMEOUT_ERROR, "Timed out while waiting to become readable!");
+ }
#else
rb_io_t *fptr;
GetOpenFile(io, fptr);
@@ -1750,71 +1767,71 @@ ossl_start_ssl(VALUE self, int (*func)(SSL *), const char *funcname, VALUE opts)
int ret, ret2;
VALUE cb_state;
int nonblock = opts != Qfalse;
-#if defined(SSL_R_CERTIFICATE_VERIFY_FAILED)
- unsigned long err;
-#endif
rb_ivar_set(self, ID_callback_state, Qnil);
GetSSL(self, ssl);
VALUE io = rb_attr_get(self, id_i_io);
- for(;;){
- ret = func(ssl);
+ for (;;) {
+ ret = func(ssl);
- cb_state = rb_attr_get(self, ID_callback_state);
+ cb_state = rb_attr_get(self, ID_callback_state);
if (!NIL_P(cb_state)) {
- /* must cleanup OpenSSL error stack before re-raising */
- ossl_clear_error();
- rb_jump_tag(NUM2INT(cb_state));
- }
+ /* must cleanup OpenSSL error stack before re-raising */
+ ossl_clear_error();
+ rb_jump_tag(NUM2INT(cb_state));
+ }
- if (ret > 0)
- break;
+ if (ret > 0)
+ break;
- switch((ret2 = ssl_get_error(ssl, ret))){
- case SSL_ERROR_WANT_WRITE:
+ switch ((ret2 = ssl_get_error(ssl, ret))) {
+ case SSL_ERROR_WANT_WRITE:
if (no_exception_p(opts)) { return sym_wait_writable; }
write_would_block(nonblock);
io_wait_writable(io);
continue;
- case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_READ:
if (no_exception_p(opts)) { return sym_wait_readable; }
read_would_block(nonblock);
io_wait_readable(io);
continue;
- case SSL_ERROR_SYSCALL:
+ case SSL_ERROR_SYSCALL:
#ifdef __APPLE__
/* See ossl_ssl_write_internal() */
if (errno == EPROTOTYPE)
continue;
#endif
- if (errno) rb_sys_fail(funcname);
- ossl_raise(eSSLError, "%s SYSCALL returned=%d errno=%d peeraddr=%"PRIsVALUE" state=%s",
- funcname, ret2, errno, peeraddr_ip_str(self), SSL_state_string_long(ssl));
-
+ if (errno) rb_sys_fail(funcname);
+ /* fallthrough */
+ default: {
+ VALUE error_append = Qnil;
#if defined(SSL_R_CERTIFICATE_VERIFY_FAILED)
- case SSL_ERROR_SSL:
- err = ERR_peek_last_error();
- if (ERR_GET_LIB(err) == ERR_LIB_SSL &&
- ERR_GET_REASON(err) == SSL_R_CERTIFICATE_VERIFY_FAILED) {
- const char *err_msg = ERR_reason_error_string(err),
- *verify_msg = X509_verify_cert_error_string(SSL_get_verify_result(ssl));
- if (!err_msg)
- err_msg = "(null)";
- if (!verify_msg)
- verify_msg = "(null)";
- ossl_clear_error(); /* let ossl_raise() not append message */
- ossl_raise(eSSLError, "%s returned=%d errno=%d peeraddr=%"PRIsVALUE" state=%s: %s (%s)",
- funcname, ret2, errno, peeraddr_ip_str(self), SSL_state_string_long(ssl),
- err_msg, verify_msg);
- }
+ unsigned long err = ERR_peek_last_error();
+ if (ERR_GET_LIB(err) == ERR_LIB_SSL &&
+ ERR_GET_REASON(err) == SSL_R_CERTIFICATE_VERIFY_FAILED) {
+ const char *err_msg = ERR_reason_error_string(err),
+ *verify_msg = X509_verify_cert_error_string(SSL_get_verify_result(ssl));
+ if (!err_msg)
+ err_msg = "(null)";
+ if (!verify_msg)
+ verify_msg = "(null)";
+ ossl_clear_error(); /* let ossl_raise() not append message */
+ error_append = rb_sprintf(": %s (%s)", err_msg, verify_msg);
+ }
#endif
- /* fallthrough */
- default:
- ossl_raise(eSSLError, "%s returned=%d errno=%d peeraddr=%"PRIsVALUE" state=%s",
- funcname, ret2, errno, peeraddr_ip_str(self), SSL_state_string_long(ssl));
- }
+ ossl_raise(eSSLError,
+ "%s%s returned=%d errno=%d peeraddr=%"PRIsVALUE" state=%s%"PRIsVALUE,
+ funcname,
+ ret2 == SSL_ERROR_SYSCALL ? " SYSCALL" : "",
+ ret2,
+ errno,
+ peeraddr_ip_str(self),
+ SSL_state_string_long(ssl),
+ error_append);
+ }
+ }
}
return self;
diff --git a/ext/openssl/ossl_ssl_session.c b/ext/openssl/ossl_ssl_session.c
index 139a474b04..c5df902c60 100644
--- a/ext/openssl/ossl_ssl_session.c
+++ b/ext/openssl/ossl_ssl_session.c
@@ -19,7 +19,7 @@ const rb_data_type_t ossl_ssl_session_type = {
{
0, ossl_ssl_session_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE ossl_ssl_session_alloc(VALUE klass)
diff --git a/ext/openssl/ossl_ts.c b/ext/openssl/ossl_ts.c
index b33ff10c10..f698bdc7ff 100644
--- a/ext/openssl/ossl_ts.c
+++ b/ext/openssl/ossl_ts.c
@@ -83,7 +83,7 @@ static const rb_data_type_t ossl_ts_req_type = {
{
0, ossl_ts_req_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static void
@@ -97,7 +97,7 @@ static const rb_data_type_t ossl_ts_resp_type = {
{
0, ossl_ts_resp_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static void
@@ -111,7 +111,7 @@ static const rb_data_type_t ossl_ts_token_info_type = {
{
0, ossl_ts_token_info_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE
diff --git a/ext/openssl/ossl_x509attr.c b/ext/openssl/ossl_x509attr.c
index 60846cfe9d..d1d8bb5e95 100644
--- a/ext/openssl/ossl_x509attr.c
+++ b/ext/openssl/ossl_x509attr.c
@@ -41,7 +41,7 @@ static const rb_data_type_t ossl_x509attr_type = {
{
0, ossl_x509attr_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
diff --git a/ext/openssl/ossl_x509cert.c b/ext/openssl/ossl_x509cert.c
index 9443541645..aa6b9bb7ce 100644
--- a/ext/openssl/ossl_x509cert.c
+++ b/ext/openssl/ossl_x509cert.c
@@ -41,7 +41,7 @@ static const rb_data_type_t ossl_x509_type = {
{
0, ossl_x509_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
diff --git a/ext/openssl/ossl_x509crl.c b/ext/openssl/ossl_x509crl.c
index 6c1d915370..80e29f9df2 100644
--- a/ext/openssl/ossl_x509crl.c
+++ b/ext/openssl/ossl_x509crl.c
@@ -41,7 +41,7 @@ static const rb_data_type_t ossl_x509crl_type = {
{
0, ossl_x509crl_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
diff --git a/ext/openssl/ossl_x509ext.c b/ext/openssl/ossl_x509ext.c
index e54102c771..192d09bd3f 100644
--- a/ext/openssl/ossl_x509ext.c
+++ b/ext/openssl/ossl_x509ext.c
@@ -55,7 +55,7 @@ static const rb_data_type_t ossl_x509ext_type = {
{
0, ossl_x509ext_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
@@ -108,7 +108,7 @@ static const rb_data_type_t ossl_x509extfactory_type = {
{
0, ossl_x509extfactory_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE
@@ -209,15 +209,16 @@ ossl_x509extfactory_create_ext(int argc, VALUE *argv, VALUE self)
int nid;
VALUE rconf;
CONF *conf;
+ const char *oid_cstr = NULL;
rb_scan_args(argc, argv, "21", &oid, &value, &critical);
- StringValueCStr(oid);
StringValue(value);
if(NIL_P(critical)) critical = Qfalse;
- nid = OBJ_ln2nid(RSTRING_PTR(oid));
- if(!nid) nid = OBJ_sn2nid(RSTRING_PTR(oid));
- if(!nid) ossl_raise(eX509ExtError, "unknown OID `%"PRIsVALUE"'", oid);
+ oid_cstr = StringValueCStr(oid);
+ nid = OBJ_ln2nid(oid_cstr);
+ if (nid != NID_undef)
+ oid_cstr = OBJ_nid2sn(nid);
valstr = rb_str_new2(RTEST(critical) ? "critical," : "");
rb_str_append(valstr, value);
@@ -228,7 +229,12 @@ ossl_x509extfactory_create_ext(int argc, VALUE *argv, VALUE self)
rconf = rb_iv_get(self, "@config");
conf = NIL_P(rconf) ? NULL : GetConfig(rconf);
X509V3_set_nconf(ctx, conf);
- ext = X509V3_EXT_nconf_nid(conf, ctx, nid, RSTRING_PTR(valstr));
+
+#if OSSL_OPENSSL_PREREQ(1, 1, 0) || OSSL_IS_LIBRESSL
+ ext = X509V3_EXT_nconf(conf, ctx, oid_cstr, RSTRING_PTR(valstr));
+#else
+ ext = X509V3_EXT_nconf(conf, ctx, (char *)oid_cstr, RSTRING_PTR(valstr));
+#endif
X509V3_set_ctx_nodb(ctx);
if (!ext){
ossl_raise(eX509ExtError, "%"PRIsVALUE" = %"PRIsVALUE, oid, valstr);
diff --git a/ext/openssl/ossl_x509name.c b/ext/openssl/ossl_x509name.c
index 13a2b2c030..9591912f70 100644
--- a/ext/openssl/ossl_x509name.c
+++ b/ext/openssl/ossl_x509name.c
@@ -46,7 +46,7 @@ static const rb_data_type_t ossl_x509name_type = {
{
0, ossl_x509name_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
diff --git a/ext/openssl/ossl_x509req.c b/ext/openssl/ossl_x509req.c
index 77a7d3f2ff..f058185151 100644
--- a/ext/openssl/ossl_x509req.c
+++ b/ext/openssl/ossl_x509req.c
@@ -41,7 +41,7 @@ static const rb_data_type_t ossl_x509req_type = {
{
0, ossl_x509req_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
diff --git a/ext/openssl/ossl_x509revoked.c b/ext/openssl/ossl_x509revoked.c
index 10b8aa4ad6..108447c868 100644
--- a/ext/openssl/ossl_x509revoked.c
+++ b/ext/openssl/ossl_x509revoked.c
@@ -41,7 +41,7 @@ static const rb_data_type_t ossl_x509rev_type = {
{
0, ossl_x509rev_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
diff --git a/ext/openssl/ossl_x509store.c b/ext/openssl/ossl_x509store.c
index 7c546187c3..f27381ca90 100644
--- a/ext/openssl/ossl_x509store.c
+++ b/ext/openssl/ossl_x509store.c
@@ -116,6 +116,9 @@ static void
ossl_x509store_mark(void *ptr)
{
X509_STORE *store = ptr;
+ // Note: this reference is stored as @verify_callback so we don't need to mark it.
+ // However we do need to ensure GC compaction won't move it, hence why
+ // we call rb_gc_mark here.
rb_gc_mark((VALUE)X509_STORE_get_ex_data(store, store_ex_verify_cb_idx));
}
@@ -130,7 +133,7 @@ static const rb_data_type_t ossl_x509store_type = {
{
ossl_x509store_mark, ossl_x509store_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
@@ -187,8 +190,9 @@ ossl_x509store_set_vfy_cb(VALUE self, VALUE cb)
X509_STORE *store;
GetX509Store(self, store);
- X509_STORE_set_ex_data(store, store_ex_verify_cb_idx, (void *)cb);
rb_iv_set(self, "@verify_callback", cb);
+ // We don't need to trigger a write barrier because `rb_iv_set` did it.
+ X509_STORE_set_ex_data(store, store_ex_verify_cb_idx, (void *)cb);
return cb;
}
@@ -507,6 +511,9 @@ static void
ossl_x509stctx_mark(void *ptr)
{
X509_STORE_CTX *ctx = ptr;
+ // Note: this reference is stored as @verify_callback so we don't need to mark it.
+ // However we do need to ensure GC compaction won't move it, hence why
+ // we call rb_gc_mark here.
rb_gc_mark((VALUE)X509_STORE_CTX_get_ex_data(ctx, stctx_ex_verify_cb_idx));
}
@@ -526,7 +533,7 @@ static const rb_data_type_t ossl_x509stctx_type = {
{
ossl_x509stctx_mark, ossl_x509stctx_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE
@@ -614,8 +621,8 @@ ossl_x509stctx_verify(VALUE self)
X509_STORE_CTX *ctx;
GetX509StCtx(self, ctx);
- X509_STORE_CTX_set_ex_data(ctx, stctx_ex_verify_cb_idx,
- (void *)rb_iv_get(self, "@verify_callback"));
+ VALUE cb = rb_iv_get(self, "@verify_callback");
+ X509_STORE_CTX_set_ex_data(ctx, stctx_ex_verify_cb_idx, (void *)cb);
switch (X509_verify_cert(ctx)) {
case 1:
diff --git a/ext/pathname/depend b/ext/pathname/depend
index 5dd8b042de..cca7877dad 100644
--- a/ext/pathname/depend
+++ b/ext/pathname/depend
@@ -157,6 +157,7 @@ pathname.o: $(hdrdir)/ruby/internal/special_consts.h
pathname.o: $(hdrdir)/ruby/internal/static_assert.h
pathname.o: $(hdrdir)/ruby/internal/stdalign.h
pathname.o: $(hdrdir)/ruby/internal/stdbool.h
+pathname.o: $(hdrdir)/ruby/internal/stdckdint.h
pathname.o: $(hdrdir)/ruby/internal/symbol.h
pathname.o: $(hdrdir)/ruby/internal/value.h
pathname.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/pathname/extconf.rb b/ext/pathname/extconf.rb
index 84e68277aa..b4e1617b9e 100644
--- a/ext/pathname/extconf.rb
+++ b/ext/pathname/extconf.rb
@@ -1,4 +1,3 @@
# frozen_string_literal: false
require 'mkmf'
-have_func("rb_file_s_birthtime")
create_makefile('pathname')
diff --git a/ext/pathname/lib/pathname.rb b/ext/pathname/lib/pathname.rb
index e0f38b2b37..dc639174d5 100644
--- a/ext/pathname/lib/pathname.rb
+++ b/ext/pathname/lib/pathname.rb
@@ -14,7 +14,7 @@ require 'pathname.so'
class Pathname
- VERSION = "0.2.1"
+ VERSION = "0.3.0"
# :stopdoc:
@@ -580,14 +580,13 @@ class Pathname # * Find *
end
-autoload(:FileUtils, 'fileutils')
-
class Pathname # * FileUtils *
# Creates a full path, including any intermediate directories that don't yet
# exist.
#
# See FileUtils.mkpath and FileUtils.mkdir_p
def mkpath(mode: nil)
+ require 'fileutils'
FileUtils.mkpath(@path, mode: mode)
nil
end
diff --git a/ext/pathname/pathname.c b/ext/pathname/pathname.c
index 878f216fb5..cdecb3f897 100644
--- a/ext/pathname/pathname.c
+++ b/ext/pathname/pathname.c
@@ -479,7 +479,6 @@ path_atime(VALUE self)
return rb_funcall(rb_cFile, id_atime, 1, get_strpath(self));
}
-#if defined(HAVE_RB_FILE_S_BIRTHTIME)
/*
* call-seq:
* pathname.birthtime -> time
@@ -494,10 +493,6 @@ path_birthtime(VALUE self)
{
return rb_funcall(rb_cFile, id_birthtime, 1, get_strpath(self));
}
-#else
-/* check at compilation time for `respond_to?` */
-# define path_birthtime rb_f_notimplement
-#endif
/*
* call-seq:
diff --git a/ext/psych/depend b/ext/psych/depend
index 13fbe3fb19..a4d9ca9a6a 100644
--- a/ext/psych/depend
+++ b/ext/psych/depend
@@ -171,6 +171,7 @@ psych.o: $(hdrdir)/ruby/internal/special_consts.h
psych.o: $(hdrdir)/ruby/internal/static_assert.h
psych.o: $(hdrdir)/ruby/internal/stdalign.h
psych.o: $(hdrdir)/ruby/internal/stdbool.h
+psych.o: $(hdrdir)/ruby/internal/stdckdint.h
psych.o: $(hdrdir)/ruby/internal/symbol.h
psych.o: $(hdrdir)/ruby/internal/value.h
psych.o: $(hdrdir)/ruby/internal/value_type.h
@@ -347,6 +348,7 @@ psych_emitter.o: $(hdrdir)/ruby/internal/special_consts.h
psych_emitter.o: $(hdrdir)/ruby/internal/static_assert.h
psych_emitter.o: $(hdrdir)/ruby/internal/stdalign.h
psych_emitter.o: $(hdrdir)/ruby/internal/stdbool.h
+psych_emitter.o: $(hdrdir)/ruby/internal/stdckdint.h
psych_emitter.o: $(hdrdir)/ruby/internal/symbol.h
psych_emitter.o: $(hdrdir)/ruby/internal/value.h
psych_emitter.o: $(hdrdir)/ruby/internal/value_type.h
@@ -523,6 +525,7 @@ psych_parser.o: $(hdrdir)/ruby/internal/special_consts.h
psych_parser.o: $(hdrdir)/ruby/internal/static_assert.h
psych_parser.o: $(hdrdir)/ruby/internal/stdalign.h
psych_parser.o: $(hdrdir)/ruby/internal/stdbool.h
+psych_parser.o: $(hdrdir)/ruby/internal/stdckdint.h
psych_parser.o: $(hdrdir)/ruby/internal/symbol.h
psych_parser.o: $(hdrdir)/ruby/internal/value.h
psych_parser.o: $(hdrdir)/ruby/internal/value_type.h
@@ -699,6 +702,7 @@ psych_to_ruby.o: $(hdrdir)/ruby/internal/special_consts.h
psych_to_ruby.o: $(hdrdir)/ruby/internal/static_assert.h
psych_to_ruby.o: $(hdrdir)/ruby/internal/stdalign.h
psych_to_ruby.o: $(hdrdir)/ruby/internal/stdbool.h
+psych_to_ruby.o: $(hdrdir)/ruby/internal/stdckdint.h
psych_to_ruby.o: $(hdrdir)/ruby/internal/symbol.h
psych_to_ruby.o: $(hdrdir)/ruby/internal/value.h
psych_to_ruby.o: $(hdrdir)/ruby/internal/value_type.h
@@ -875,6 +879,7 @@ psych_yaml_tree.o: $(hdrdir)/ruby/internal/special_consts.h
psych_yaml_tree.o: $(hdrdir)/ruby/internal/static_assert.h
psych_yaml_tree.o: $(hdrdir)/ruby/internal/stdalign.h
psych_yaml_tree.o: $(hdrdir)/ruby/internal/stdbool.h
+psych_yaml_tree.o: $(hdrdir)/ruby/internal/stdckdint.h
psych_yaml_tree.o: $(hdrdir)/ruby/internal/symbol.h
psych_yaml_tree.o: $(hdrdir)/ruby/internal/value.h
psych_yaml_tree.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/psych/lib/psych.rb b/ext/psych/lib/psych.rb
index 4a2ab58514..d87bd9040a 100644
--- a/ext/psych/lib/psych.rb
+++ b/ext/psych/lib/psych.rb
@@ -489,6 +489,10 @@ module Psych
#
# Default: <tt>false</tt>.
#
+ # [<tt>:stringify_names</tt>] Dump symbol keys in Hash objects as string.
+ #
+ # Default: <tt>false</tt>.
+ #
# Example:
#
# # Dump an array, get back a YAML string
@@ -502,6 +506,9 @@ module Psych
#
# # Dump an array to an IO with indentation set
# Psych.dump(['a', ['b']], StringIO.new, indentation: 3)
+ #
+ # # Dump hash with symbol keys as string
+ # Psych.dump({a: "b"}, stringify_names: true) # => "---\na: b\n"
def self.dump o, io = nil, options = {}
if Hash === io
options = io
@@ -562,6 +569,10 @@ module Psych
#
# Default: <tt>false</tt>.
#
+ # [<tt>:stringify_names</tt>] Dump symbol keys in Hash objects as string.
+ #
+ # Default: <tt>false</tt>.
+ #
# Example:
#
# # Dump an array, get back a YAML string
@@ -575,6 +586,9 @@ module Psych
#
# # Dump an array to an IO with indentation set
# Psych.safe_dump(['a', ['b']], StringIO.new, indentation: 3)
+ #
+ # # Dump hash with symbol keys as string
+ # Psych.dump({a: "b"}, stringify_names: true) # => "---\na: b\n"
def self.safe_dump o, io = nil, options = {}
if Hash === io
options = io
@@ -694,26 +708,8 @@ module Psych
dump_tags[klass] = tag
end
- # Workaround for emulating `warn '...', uplevel: 1` in Ruby 2.4 or lower.
- def self.warn_with_uplevel(message, uplevel: 1)
- at = parse_caller(caller[uplevel]).join(':')
- warn "#{at}: #{message}"
- end
-
- def self.parse_caller(at)
- if /^(.+?):(\d+)(?::in `.*')?/ =~ at
- file = $1
- line = $2.to_i
- [file, line]
- end
- end
- private_class_method :warn_with_uplevel, :parse_caller
-
class << self
if defined?(Ractor)
- require 'forwardable'
- extend Forwardable
-
class Config
attr_accessor :load_tags, :dump_tags, :domain_types
def initialize
@@ -727,7 +723,29 @@ module Psych
Ractor.current[:PsychConfig] ||= Config.new
end
- def_delegators :config, :load_tags, :dump_tags, :domain_types, :load_tags=, :dump_tags=, :domain_types=
+ def load_tags
+ config.load_tags
+ end
+
+ def dump_tags
+ config.dump_tags
+ end
+
+ def domain_types
+ config.domain_types
+ end
+
+ def load_tags=(value)
+ config.load_tags = value
+ end
+
+ def dump_tags=(value)
+ config.dump_tags = value
+ end
+
+ def domain_types=(value)
+ config.domain_types = value
+ end
else
attr_accessor :load_tags
attr_accessor :dump_tags
diff --git a/ext/psych/lib/psych/versions.rb b/ext/psych/lib/psych/versions.rb
index 57b7659b57..b9e8d9ef11 100644
--- a/ext/psych/lib/psych/versions.rb
+++ b/ext/psych/lib/psych/versions.rb
@@ -2,9 +2,9 @@
module Psych
# The version of Psych you are using
- VERSION = '5.1.0'
+ VERSION = '5.1.2'
if RUBY_ENGINE == 'jruby'
- DEFAULT_SNAKEYAML_VERSION = '2.6'.freeze
+ DEFAULT_SNAKEYAML_VERSION = '2.7'.freeze
end
end
diff --git a/ext/psych/lib/psych/visitors/to_ruby.rb b/ext/psych/lib/psych/visitors/to_ruby.rb
index 8614251ca9..f0fda9bdbc 100644
--- a/ext/psych/lib/psych/visitors/to_ruby.rb
+++ b/ext/psych/lib/psych/visitors/to_ruby.rb
@@ -101,7 +101,7 @@ module Psych
source = $1
options = 0
lang = nil
- ($2 || '').split('').each do |option|
+ $2&.each_char do |option|
case option
when 'x' then options |= Regexp::EXTENDED
when 'i' then options |= Regexp::IGNORECASE
diff --git a/ext/psych/lib/psych/visitors/yaml_tree.rb b/ext/psych/lib/psych/visitors/yaml_tree.rb
index 51491783c3..a2ebc4d781 100644
--- a/ext/psych/lib/psych/visitors/yaml_tree.rb
+++ b/ext/psych/lib/psych/visitors/yaml_tree.rb
@@ -15,30 +15,25 @@ module Psych
class YAMLTree < Psych::Visitors::Visitor
class Registrar # :nodoc:
def initialize
- @obj_to_id = {}
- @obj_to_node = {}
- @targets = []
+ @obj_to_id = {}.compare_by_identity
+ @obj_to_node = {}.compare_by_identity
@counter = 0
end
def register target, node
- return unless target.respond_to? :object_id
- @targets << target
- @obj_to_node[target.object_id] = node
+ @obj_to_node[target] = node
end
def key? target
- @obj_to_node.key? target.object_id
- rescue NoMethodError
- false
+ @obj_to_node.key? target
end
def id_for target
- @obj_to_id[target.object_id] ||= (@counter += 1)
+ @obj_to_id[target] ||= (@counter += 1)
end
def node_for target
- @obj_to_node[target.object_id]
+ @obj_to_node[target]
end
end
@@ -70,6 +65,7 @@ module Psych
fail(ArgumentError, "Invalid line_width #{@line_width}, must be non-negative or -1 for unlimited.")
end
end
+ @stringify_names = options[:stringify_names]
@coders = []
@dispatch_cache = Hash.new do |h,klass|
@@ -272,7 +268,7 @@ module Psych
tag = 'tag:yaml.org,2002:str'
plain = false
quote = false
- elsif o == 'y' || o == 'n'
+ elsif o == 'y' || o == 'Y' || o == 'n' || o == 'N'
style = Nodes::Scalar::DOUBLE_QUOTED
elsif @line_width && o.length > @line_width
style = Nodes::Scalar::FOLDED
@@ -328,7 +324,7 @@ module Psych
if o.class == ::Hash
register(o, @emitter.start_mapping(nil, nil, true, Psych::Nodes::Mapping::BLOCK))
o.each do |k,v|
- accept k
+ accept(@stringify_names && Symbol === k ? k.to_s : k)
accept v
end
@emitter.end_mapping
@@ -341,7 +337,7 @@ module Psych
register(o, @emitter.start_mapping(nil, '!set', false, Psych::Nodes::Mapping::BLOCK))
o.each do |k,v|
- accept k
+ accept(@stringify_names && Symbol === k ? k.to_s : k)
accept v
end
diff --git a/ext/psych/psych.gemspec b/ext/psych/psych.gemspec
index a8ee5da028..a3fc53a8b9 100644
--- a/ext/psych/psych.gemspec
+++ b/ext/psych/psych.gemspec
@@ -21,28 +21,41 @@ DESCRIPTION
s.licenses = ["MIT"]
s.require_paths = ["lib"]
- # for ruby core repository. It was generated by `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
+ # for ruby core repository.
+ # It was generated by
+ # `git ls-files -z`.split("\x0").reject { |f|
+ # f.match(%r{^\.git|^(test|spec|features|bin|tool)/|^[A-Z]\w+file$|/extlibs$|\.(gemspec|java)$|jar})
+ # }
s.files = [
- ".gitignore", "Gemfile", "LICENSE", "Mavenfile", "README.md", "Rakefile", "bin/console",
- "bin/setup", "ext/psych/depend", "ext/psych/extconf.rb", "ext/psych/psych.c", "ext/psych/psych.h",
- "ext/psych/psych_emitter.c", "ext/psych/psych_emitter.h", "ext/psych/psych_parser.c", "ext/psych/psych_parser.h",
- "ext/psych/psych_to_ruby.c", "ext/psych/psych_to_ruby.h", "ext/psych/psych_yaml_tree.c", "ext/psych/psych_yaml_tree.h",
- "lib/psych.rb", "lib/psych/class_loader.rb", "lib/psych/coder.rb", "lib/psych/core_ext.rb", "lib/psych/exception.rb",
- "lib/psych/handler.rb", "lib/psych/handlers/document_stream.rb", "lib/psych/handlers/recorder.rb",
- "lib/psych/json/ruby_events.rb", "lib/psych/json/stream.rb", "lib/psych/json/tree_builder.rb",
- "lib/psych/json/yaml_events.rb", "lib/psych/nodes.rb", "lib/psych/nodes/alias.rb", "lib/psych/nodes/document.rb",
- "lib/psych/nodes/mapping.rb", "lib/psych/nodes/node.rb", "lib/psych/nodes/scalar.rb", "lib/psych/nodes/sequence.rb",
- "lib/psych/nodes/stream.rb", "lib/psych/omap.rb", "lib/psych/parser.rb", "lib/psych/scalar_scanner.rb",
- "lib/psych/set.rb", "lib/psych/stream.rb", "lib/psych/streaming.rb", "lib/psych/syntax_error.rb",
- "lib/psych/tree_builder.rb", "lib/psych/versions.rb", "lib/psych/visitors.rb","lib/psych/visitors/depth_first.rb",
- "lib/psych/visitors/emitter.rb", "lib/psych/visitors/json_tree.rb", "lib/psych/visitors/to_ruby.rb",
- "lib/psych/visitors/visitor.rb", "lib/psych/visitors/yaml_tree.rb", "lib/psych/y.rb", "psych.gemspec"
+ "CONTRIBUTING.md", "LICENSE", "README.md", "ext/psych/depend",
+ "ext/psych/extconf.rb", "ext/psych/psych.c", "ext/psych/psych.h",
+ "ext/psych/psych_emitter.c", "ext/psych/psych_emitter.h",
+ "ext/psych/psych_parser.c", "ext/psych/psych_parser.h",
+ "ext/psych/psych_to_ruby.c", "ext/psych/psych_to_ruby.h",
+ "ext/psych/psych_yaml_tree.c", "ext/psych/psych_yaml_tree.h",
+ "lib/psych.rb", "lib/psych/class_loader.rb", "lib/psych/coder.rb",
+ "lib/psych/core_ext.rb", "lib/psych/exception.rb", "lib/psych/handler.rb",
+ "lib/psych/handlers/document_stream.rb", "lib/psych/handlers/recorder.rb",
+ "lib/psych/json/ruby_events.rb", "lib/psych/json/stream.rb",
+ "lib/psych/json/tree_builder.rb", "lib/psych/json/yaml_events.rb",
+ "lib/psych/nodes.rb", "lib/psych/nodes/alias.rb",
+ "lib/psych/nodes/document.rb", "lib/psych/nodes/mapping.rb",
+ "lib/psych/nodes/node.rb", "lib/psych/nodes/scalar.rb",
+ "lib/psych/nodes/sequence.rb", "lib/psych/nodes/stream.rb",
+ "lib/psych/omap.rb", "lib/psych/parser.rb", "lib/psych/scalar_scanner.rb",
+ "lib/psych/set.rb", "lib/psych/stream.rb", "lib/psych/streaming.rb",
+ "lib/psych/syntax_error.rb", "lib/psych/tree_builder.rb",
+ "lib/psych/versions.rb", "lib/psych/visitors.rb",
+ "lib/psych/visitors/depth_first.rb", "lib/psych/visitors/emitter.rb",
+ "lib/psych/visitors/json_tree.rb", "lib/psych/visitors/to_ruby.rb",
+ "lib/psych/visitors/visitor.rb", "lib/psych/visitors/yaml_tree.rb",
+ "lib/psych/y.rb"
]
s.rdoc_options = ["--main", "README.md"]
s.extra_rdoc_files = ["README.md"]
- s.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
+ s.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
s.required_rubygems_version = Gem::Requirement.new(">= 0")
if RUBY_ENGINE == 'jruby'
@@ -63,5 +76,5 @@ DESCRIPTION
end
s.metadata['msys2_mingw_dependencies'] = 'libyaml'
-
+ s.metadata['changelog_uri'] = s.homepage + '/releases'
end
diff --git a/ext/pty/depend b/ext/pty/depend
index d4d0d558ef..adecfff862 100644
--- a/ext/pty/depend
+++ b/ext/pty/depend
@@ -157,6 +157,7 @@ pty.o: $(hdrdir)/ruby/internal/special_consts.h
pty.o: $(hdrdir)/ruby/internal/static_assert.h
pty.o: $(hdrdir)/ruby/internal/stdalign.h
pty.o: $(hdrdir)/ruby/internal/stdbool.h
+pty.o: $(hdrdir)/ruby/internal/stdckdint.h
pty.o: $(hdrdir)/ruby/internal/symbol.h
pty.o: $(hdrdir)/ruby/internal/value.h
pty.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/pty/extconf.rb b/ext/pty/extconf.rb
index ba0c4286fd..da3655cf4d 100644
--- a/ext/pty/extconf.rb
+++ b/ext/pty/extconf.rb
@@ -13,10 +13,13 @@ if /mswin|mingw|bccwin/ !~ RUBY_PLATFORM
have_header("util.h") # OpenBSD openpty
util = have_library("util", "openpty")
end
- if have_func("posix_openpt") or
+ openpt = have_func("posix_openpt")
+ if openpt
+ have_func("ptsname_r") or have_func("ptsname")
+ end
+ if openpt or
(util or have_func("openpty")) or
have_func("_getpty") or
- have_func("ptsname") or
have_func("ioctl")
create_makefile('pty')
end
diff --git a/ext/pty/pty.c b/ext/pty/pty.c
index 0aca10bfa0..5ca55e41d6 100644
--- a/ext/pty/pty.c
+++ b/ext/pty/pty.c
@@ -92,9 +92,13 @@ struct pty_info {
static void getDevice(int*, int*, char [DEVICELEN], int);
+static int start_new_session(char *errbuf, size_t errbuf_len);
+static int obtain_ctty(int master, int slave, const char *slavename, char *errbuf, size_t errbuf_len);
+static int drop_privilige(char *errbuf, size_t errbuf_len);
+
struct child_info {
int master, slave;
- char *slavename;
+ const char *slavename;
VALUE execarg_obj;
struct rb_execarg *eargp;
};
@@ -102,26 +106,42 @@ struct child_info {
static int
chfunc(void *data, char *errbuf, size_t errbuf_len)
{
- struct child_info *carg = data;
+ const struct child_info *carg = data;
int master = carg->master;
int slave = carg->slave;
+ const char *slavename = carg->slavename;
+
+ if (start_new_session(errbuf, errbuf_len))
+ return -1;
+
+ if (obtain_ctty(master, slave, slavename, errbuf, errbuf_len))
+ return -1;
+
+ if (drop_privilige(errbuf, errbuf_len))
+ return -1;
+
+ return rb_exec_async_signal_safe(carg->eargp, errbuf, errbuf_len);
+}
#define ERROR_EXIT(str) do { \
strlcpy(errbuf, (str), errbuf_len); \
return -1; \
} while (0)
- /*
- * Set free from process group and controlling terminal
- */
+/*
+ * Set free from process group and controlling terminal
+ */
+static int
+start_new_session(char *errbuf, size_t errbuf_len)
+{
#ifdef HAVE_SETSID
(void) setsid();
#else /* HAS_SETSID */
# ifdef HAVE_SETPGRP
-# ifdef SETGRP_VOID
+# ifdef SETPGRP_VOID
if (setpgrp() == -1)
ERROR_EXIT("setpgrp()");
-# else /* SETGRP_VOID */
+# else /* SETPGRP_VOID */
if (setpgrp(0, getpid()) == -1)
ERROR_EXIT("setpgrp()");
{
@@ -132,20 +152,25 @@ chfunc(void *data, char *errbuf, size_t errbuf_len)
ERROR_EXIT("ioctl(TIOCNOTTY)");
close(i);
}
-# endif /* SETGRP_VOID */
+# endif /* SETPGRP_VOID */
# endif /* HAVE_SETPGRP */
#endif /* HAS_SETSID */
+ return 0;
+}
- /*
- * obtain new controlling terminal
- */
+/*
+ * obtain new controlling terminal
+ */
+static int
+obtain_ctty(int master, int slave, const char *slavename, char *errbuf, size_t errbuf_len)
+{
#if defined(TIOCSCTTY)
close(master);
(void) ioctl(slave, TIOCSCTTY, (char *)0);
/* errors ignored for sun */
#else
close(slave);
- slave = rb_cloexec_open(carg->slavename, O_RDWR, 0);
+ slave = rb_cloexec_open(slavename, O_RDWR, 0);
if (slave < 0) {
ERROR_EXIT("open: pty slave");
}
@@ -156,13 +181,19 @@ chfunc(void *data, char *errbuf, size_t errbuf_len)
dup2(slave,1);
dup2(slave,2);
if (slave < 0 || slave > 2) (void)!close(slave);
+ return 0;
+}
+
+static int
+drop_privilige(char *errbuf, size_t errbuf_len)
+{
#if defined(HAVE_SETEUID) || defined(HAVE_SETREUID) || defined(HAVE_SETRESUID)
if (seteuid(getuid())) ERROR_EXIT("seteuid()");
#endif
+ return 0;
+}
- return rb_exec_async_signal_safe(carg->eargp, errbuf, sizeof(errbuf_len));
#undef ERROR_EXIT
-}
static void
establishShell(int argc, VALUE *argv, struct pty_info *info,
@@ -225,7 +256,21 @@ establishShell(int argc, VALUE *argv, struct pty_info *info,
RB_GC_GUARD(carg.execarg_obj);
}
-#if defined(HAVE_POSIX_OPENPT) || defined(HAVE_OPENPTY) || defined(HAVE_PTSNAME)
+#if (defined(HAVE_POSIX_OPENPT) || defined(HAVE_PTSNAME)) && !defined(HAVE_PTSNAME_R)
+/* glibc only, not obsolete interface on Tru64 or HP-UX */
+static int
+ptsname_r(int fd, char *buf, size_t buflen)
+{
+ extern char *ptsname(int);
+ char *name = ptsname(fd);
+ if (!name) return -1;
+ strlcpy(buf, name, buflen);
+ return 0;
+}
+# define HAVE_PTSNAME_R 1
+#endif
+
+#if defined(HAVE_POSIX_OPENPT) || defined(HAVE_OPENPTY) || defined(HAVE_PTSNAME_R)
static int
no_mesg(char *slavedevice, int nomesg)
{
@@ -258,13 +303,19 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg,
/* Unix98 PTY */
int masterfd = -1, slavefd = -1;
char *slavedevice;
+ struct sigaction dfl, old;
+
+ dfl.sa_handler = SIG_DFL;
+ dfl.sa_flags = 0;
+ sigemptyset(&dfl.sa_mask);
#if defined(__sun) || defined(__OpenBSD__) || (defined(__FreeBSD__) && __FreeBSD_version < 902000)
/* workaround for Solaris 10: grantpt() doesn't work if FD_CLOEXEC is set. [ruby-dev:44688] */
/* FreeBSD 9.2 or later supports O_CLOEXEC
* http://www.freebsd.org/cgi/query-pr.cgi?pr=162374 */
if ((masterfd = posix_openpt(O_RDWR|O_NOCTTY)) == -1) goto error;
- if (rb_grantpt(masterfd) == -1) goto error;
+ if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error;
+ if (grantpt(masterfd) == -1) goto grantpt_error;
rb_fd_fix_cloexec(masterfd);
#else
{
@@ -278,10 +329,13 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg,
if ((masterfd = posix_openpt(flags)) == -1) goto error;
}
rb_fd_fix_cloexec(masterfd);
- if (rb_grantpt(masterfd) == -1) goto error;
+ if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error;
+ if (grantpt(masterfd) == -1) goto grantpt_error;
#endif
+ if (sigaction(SIGCHLD, &old, NULL) == -1) goto error;
if (unlockpt(masterfd) == -1) goto error;
- if ((slavedevice = ptsname(masterfd)) == NULL) goto error;
+ if (ptsname_r(masterfd, SlaveName, DEVICELEN) != 0) goto error;
+ slavedevice = SlaveName;
if (no_mesg(slavedevice, nomesg) == -1) goto error;
if ((slavefd = rb_cloexec_open(slavedevice, O_RDWR|O_NOCTTY, 0)) == -1) goto error;
rb_update_max_fd(slavefd);
@@ -294,9 +348,10 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg,
*master = masterfd;
*slave = slavefd;
- strlcpy(SlaveName, slavedevice, DEVICELEN);
return 0;
+ grantpt_error:
+ sigaction(SIGCHLD, &old, NULL);
error:
if (slavefd != -1) close(slavefd);
if (masterfd != -1) close(masterfd);
@@ -346,21 +401,25 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg,
char *slavedevice;
void (*s)();
- extern char *ptsname(int);
extern int unlockpt(int);
+ extern int grantpt(int);
#if defined(__sun)
/* workaround for Solaris 10: grantpt() doesn't work if FD_CLOEXEC is set. [ruby-dev:44688] */
if((masterfd = open("/dev/ptmx", O_RDWR, 0)) == -1) goto error;
- if(rb_grantpt(masterfd) == -1) goto error;
+ s = signal(SIGCHLD, SIG_DFL);
+ if(grantpt(masterfd) == -1) goto error;
rb_fd_fix_cloexec(masterfd);
#else
if((masterfd = rb_cloexec_open("/dev/ptmx", O_RDWR, 0)) == -1) goto error;
rb_update_max_fd(masterfd);
- if(rb_grantpt(masterfd) == -1) goto error;
+ s = signal(SIGCHLD, SIG_DFL);
+ if(grantpt(masterfd) == -1) goto error;
#endif
+ signal(SIGCHLD, s);
if(unlockpt(masterfd) == -1) goto error;
- if((slavedevice = ptsname(masterfd)) == NULL) goto error;
+ if (ptsname_r(masterfd, SlaveName, DEVICELEN) != 0) goto error;
+ slavedevice = SlaveName;
if (no_mesg(slavedevice, nomesg) == -1) goto error;
if((slavefd = rb_cloexec_open(slavedevice, O_RDWR, 0)) == -1) goto error;
rb_update_max_fd(slavefd);
@@ -371,7 +430,6 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg,
#endif
*master = masterfd;
*slave = slavefd;
- strlcpy(SlaveName, slavedevice, DEVICELEN);
return 0;
error:
@@ -745,8 +803,13 @@ void
Init_pty(void)
{
cPTY = rb_define_module("PTY");
- /* :nodoc: */
- rb_define_module_function(cPTY,"getpty",pty_getpty,-1);
+#if 1
+ rb_define_module_function(cPTY,"get""pty",pty_getpty,-1);
+#else /* for RDoc */
+ /* show getpty as an alias of spawn */
+ VALUE sPTY = rb_singleton_class(cPTY);
+ rb_define_alias(sPTY, "getpty", "spawn");
+#endif
rb_define_module_function(cPTY,"spawn",pty_getpty,-1);
rb_define_singleton_method(cPTY,"check",pty_check,-1);
rb_define_singleton_method(cPTY,"open",pty_open,0);
diff --git a/ext/racc/cparse/README b/ext/racc/cparse/README
deleted file mode 100644
index 550e8d49fe..0000000000
--- a/ext/racc/cparse/README
+++ /dev/null
@@ -1,11 +0,0 @@
-Racc Runtime README
-===================
-
-This directory contains a runtime library of
-Racc parser generator. If you want to generate
-your own parser, you must get Racc full package.
-Get it from:
-
- - http://i.loveruby.net/en/projects/racc
- - https://github.com/ruby/racc
-
diff --git a/ext/racc/cparse/cparse.c b/ext/racc/cparse/cparse.c
deleted file mode 100644
index f752eb7749..0000000000
--- a/ext/racc/cparse/cparse.c
+++ /dev/null
@@ -1,861 +0,0 @@
-/*
-
- cparse.c -- Racc Runtime Core
-
- Copyright (c) 1999-2006 Minero Aoki
-
- This library is free software.
- You can distribute/modify this program under the same terms of ruby.
-
-*/
-
-#include <ruby.h>
-
-#ifndef FALSE
-#define FALSE 0
-#endif
-#ifndef TRUE
-#define TRUE 1
-#endif
-
-/* -----------------------------------------------------------------------
- Important Constants
------------------------------------------------------------------------ */
-
-#define RACC_VERSION "1.6.2"
-
-#define DEFAULT_TOKEN -1
-#define ERROR_TOKEN 1
-#define FINAL_TOKEN 0
-
-#define vDEFAULT_TOKEN INT2FIX(DEFAULT_TOKEN)
-#define vERROR_TOKEN INT2FIX(ERROR_TOKEN)
-#define vFINAL_TOKEN INT2FIX(FINAL_TOKEN)
-
-/* -----------------------------------------------------------------------
- File Local Variables
------------------------------------------------------------------------ */
-
-static VALUE RaccBug;
-static VALUE CparseParams;
-
-static ID id_yydebug;
-static ID id_nexttoken;
-static ID id_onerror;
-static ID id_noreduce;
-static ID id_errstatus;
-
-static ID id_d_shift;
-static ID id_d_reduce;
-static ID id_d_accept;
-static ID id_d_read_token;
-static ID id_d_next_state;
-static ID id_d_e_pop;
-
-/* -----------------------------------------------------------------------
- Utils
------------------------------------------------------------------------ */
-
-/* For backward compatibility */
-#ifndef ID2SYM
-# define ID2SYM(i) ULONG2NUM(i)
-#endif
-#ifndef SYM2ID
-# define SYM2ID(v) ((ID)NUM2ULONG(v))
-#endif
-#ifndef SYMBOL_P
-# define SYMBOL_P(v) FIXNUM_P(v)
-#endif
-#ifndef LONG2NUM
-# define LONG2NUM(i) INT2NUM(i)
-#endif
-
-#ifndef HAVE_RB_ARY_SUBSEQ
-# define rb_ary_subseq(ary, beg, len) rb_ary_new4(len, RARRAY_PTR(ary) + beg)
-#endif
-
-static ID value_to_id _((VALUE v));
-static inline long num_to_long _((VALUE n));
-
-static ID
-value_to_id(VALUE v)
-{
- if (! SYMBOL_P(v)) {
- rb_raise(rb_eTypeError, "not symbol");
- }
- return SYM2ID(v);
-}
-
-static inline long
-num_to_long(VALUE n)
-{
- return NUM2LONG(n);
-}
-
-#define AREF(s, idx) \
- ((0 <= idx && idx < RARRAY_LEN(s)) ? rb_ary_entry(s, idx) : Qnil)
-
-/* -----------------------------------------------------------------------
- Parser Stack Interfaces
------------------------------------------------------------------------ */
-
-static VALUE get_stack_tail _((VALUE stack, long len));
-static void cut_stack_tail _((VALUE stack, long len));
-
-static VALUE
-get_stack_tail(VALUE stack, long len)
-{
- if (len < 0) return Qnil; /* system error */
- if (len > RARRAY_LEN(stack)) len = RARRAY_LEN(stack);
- return rb_ary_subseq(stack, RARRAY_LEN(stack) - len, len);
-}
-
-static void
-cut_stack_tail(VALUE stack, long len)
-{
- while (len > 0) {
- rb_ary_pop(stack);
- len--;
- }
-}
-
-#define STACK_INIT_LEN 64
-#define NEW_STACK() rb_ary_new2(STACK_INIT_LEN)
-#define PUSH(s, i) rb_ary_store(s, RARRAY_LEN(s), i)
-#define POP(s) rb_ary_pop(s)
-#define LAST_I(s) \
- ((RARRAY_LEN(s) > 0) ? rb_ary_entry(s, RARRAY_LEN(s) - 1) : Qnil)
-#define GET_TAIL(s, len) get_stack_tail(s, len)
-#define CUT_TAIL(s, len) cut_stack_tail(s, len)
-
-/* -----------------------------------------------------------------------
- struct cparse_params
------------------------------------------------------------------------ */
-
-struct cparse_params {
- VALUE value_v; /* VALUE version of this struct */
-
- VALUE parser; /* parser object */
-
- int lex_is_iterator;
- VALUE lexer; /* scanner object */
- ID lexmid; /* name of scanner method (must be an iterator) */
-
- /* State transition tables (immutable)
- Data structure is from Dragon Book 4.9 */
- /* action table */
- VALUE action_table;
- VALUE action_check;
- VALUE action_default;
- VALUE action_pointer;
- /* goto table */
- VALUE goto_table;
- VALUE goto_check;
- VALUE goto_default;
- VALUE goto_pointer;
-
- long nt_base; /* NonTerminal BASE index */
- VALUE reduce_table; /* reduce data table */
- VALUE token_table; /* token conversion table */
-
- /* parser stacks and parameters */
- VALUE state;
- long curstate;
- VALUE vstack;
- VALUE tstack;
- VALUE t;
- long shift_n;
- long reduce_n;
- long ruleno;
-
- long errstatus; /* nonzero in error recovering mode */
- long nerr; /* number of error */
-
- int use_result_var;
-
- VALUE retval; /* return value of parser routine */
- long fin; /* parse result status */
-#define CP_FIN_ACCEPT 1
-#define CP_FIN_EOT 2
-#define CP_FIN_CANTPOP 3
-
- int debug; /* user level debug */
- int sys_debug; /* system level debug */
-
- long i; /* table index */
-};
-
-/* -----------------------------------------------------------------------
- Parser Main Routines
------------------------------------------------------------------------ */
-
-static VALUE racc_cparse _((VALUE parser, VALUE arg, VALUE sysdebug));
-static VALUE racc_yyparse _((VALUE parser, VALUE lexer, VALUE lexmid,
- VALUE arg, VALUE sysdebug));
-
-static void call_lexer _((struct cparse_params *v));
-static VALUE lexer_i _((RB_BLOCK_CALL_FUNC_ARGLIST(block_args, data)));
-
-static VALUE assert_array _((VALUE a));
-static long assert_integer _((VALUE n));
-static VALUE assert_hash _((VALUE h));
-static VALUE initialize_params _((VALUE vparams, VALUE parser, VALUE arg,
- VALUE lexer, VALUE lexmid));
-static void cparse_params_mark _((void *ptr));
-static size_t cparse_params_memsize _((const void *ptr));
-
-static void parse_main _((struct cparse_params *v,
- VALUE tok, VALUE val, int resume));
-static void extract_user_token _((struct cparse_params *v,
- VALUE block_args, VALUE *tok, VALUE *val));
-static void shift _((struct cparse_params* v, long act, VALUE tok, VALUE val));
-static int reduce _((struct cparse_params* v, long act));
-static rb_block_call_func reduce0;
-
-#ifdef DEBUG
-# define D_puts(msg) if (v->sys_debug) puts(msg)
-# define D_printf(fmt,arg) if (v->sys_debug) printf(fmt,arg)
-#else
-# define D_puts(msg)
-# define D_printf(fmt,arg)
-#endif
-
-#undef RUBY_UNTYPED_DATA_WARNING
-#define RUBY_UNTYPED_DATA_WARNING 1
-
-static const rb_data_type_t cparse_params_type = {
- "racc/cparse",
- {
- cparse_params_mark,
- RUBY_TYPED_DEFAULT_FREE,
- cparse_params_memsize,
- },
-#ifdef RUBY_TYPED_FREE_IMMEDIATELY
- 0, 0,
- RUBY_TYPED_FREE_IMMEDIATELY,
-#endif
-};
-
-static VALUE
-racc_cparse(VALUE parser, VALUE arg, VALUE sysdebug)
-{
- VALUE vparams;
- struct cparse_params *v;
-
- vparams = TypedData_Make_Struct(CparseParams, struct cparse_params,
- &cparse_params_type, v);
- D_puts("starting cparse");
- v->sys_debug = RTEST(sysdebug);
- vparams = initialize_params(vparams, parser, arg, Qnil, Qnil);
- v->lex_is_iterator = FALSE;
- parse_main(v, Qnil, Qnil, 0);
-
- RB_GC_GUARD(vparams);
- return v->retval;
-}
-
-static VALUE
-racc_yyparse(VALUE parser, VALUE lexer, VALUE lexmid, VALUE arg, VALUE sysdebug)
-{
- VALUE vparams;
- struct cparse_params *v;
-
- vparams = TypedData_Make_Struct(CparseParams, struct cparse_params,
- &cparse_params_type, v);
- v->sys_debug = RTEST(sysdebug);
- D_puts("start C yyparse");
- vparams = initialize_params(vparams, parser, arg, lexer, lexmid);
- v->lex_is_iterator = TRUE;
- D_puts("params initialized");
- parse_main(v, Qnil, Qnil, 0);
- call_lexer(v);
- if (!v->fin) {
- rb_raise(rb_eArgError, "%s() is finished before EndOfToken",
- rb_id2name(v->lexmid));
- }
-
- RB_GC_GUARD(vparams);
- return v->retval;
-}
-
-#ifdef HAVE_RB_BLOCK_CALL
-static void
-call_lexer(struct cparse_params *v)
-{
- rb_block_call(v->lexer, v->lexmid, 0, NULL, lexer_i, v->value_v);
-}
-#else
-static VALUE
-lexer_iter(VALUE data)
-{
- struct cparse_params *v = rb_check_typeddata(data, &cparse_params_type);
-
- rb_funcall(v->lexer, v->lexmid, 0);
- return Qnil;
-}
-
-static void
-call_lexer(struct cparse_params *v)
-{
- rb_iterate(lexer_iter, v->value_v, lexer_i, v->value_v);
-}
-#endif
-
-static VALUE
-lexer_i(RB_BLOCK_CALL_FUNC_ARGLIST(block_args, data))
-{
- struct cparse_params *v = rb_check_typeddata(data, &cparse_params_type);
- VALUE tok, val;
-
- if (v->fin)
- rb_raise(rb_eArgError, "extra token after EndOfToken");
- extract_user_token(v, block_args, &tok, &val);
- parse_main(v, tok, val, 1);
- if (v->fin && v->fin != CP_FIN_ACCEPT)
- rb_iter_break();
- return Qnil;
-}
-
-static VALUE
-assert_array(VALUE a)
-{
- Check_Type(a, T_ARRAY);
- return a;
-}
-
-static VALUE
-assert_hash(VALUE h)
-{
- Check_Type(h, T_HASH);
- return h;
-}
-
-static long
-assert_integer(VALUE n)
-{
- return NUM2LONG(n);
-}
-
-static VALUE
-initialize_params(VALUE vparams, VALUE parser, VALUE arg, VALUE lexer, VALUE lexmid)
-{
- struct cparse_params *v = rb_check_typeddata(vparams, &cparse_params_type);
-
- v->value_v = vparams;
- v->parser = parser;
- v->lexer = lexer;
- if (! NIL_P(lexmid))
- v->lexmid = value_to_id(lexmid);
-
- v->debug = RTEST(rb_ivar_get(parser, id_yydebug));
-
- Check_Type(arg, T_ARRAY);
- if (!(13 <= RARRAY_LEN(arg) && RARRAY_LEN(arg) <= 14))
- rb_raise(RaccBug, "[Racc Bug] wrong arg.size %ld", RARRAY_LEN(arg));
- v->action_table = assert_array (rb_ary_entry(arg, 0));
- v->action_check = assert_array (rb_ary_entry(arg, 1));
- v->action_default = assert_array (rb_ary_entry(arg, 2));
- v->action_pointer = assert_array (rb_ary_entry(arg, 3));
- v->goto_table = assert_array (rb_ary_entry(arg, 4));
- v->goto_check = assert_array (rb_ary_entry(arg, 5));
- v->goto_default = assert_array (rb_ary_entry(arg, 6));
- v->goto_pointer = assert_array (rb_ary_entry(arg, 7));
- v->nt_base = assert_integer(rb_ary_entry(arg, 8));
- v->reduce_table = assert_array (rb_ary_entry(arg, 9));
- v->token_table = assert_hash (rb_ary_entry(arg, 10));
- v->shift_n = assert_integer(rb_ary_entry(arg, 11));
- v->reduce_n = assert_integer(rb_ary_entry(arg, 12));
- if (RARRAY_LEN(arg) > 13) {
- v->use_result_var = RTEST(rb_ary_entry(arg, 13));
- }
- else {
- v->use_result_var = TRUE;
- }
-
- v->tstack = v->debug ? NEW_STACK() : Qnil;
- v->vstack = NEW_STACK();
- v->state = NEW_STACK();
- v->curstate = 0;
- PUSH(v->state, INT2FIX(0));
- v->t = INT2FIX(FINAL_TOKEN + 1); /* must not init to FINAL_TOKEN */
- v->nerr = 0;
- v->errstatus = 0;
- rb_ivar_set(parser, id_errstatus, LONG2NUM(v->errstatus));
-
- v->retval = Qnil;
- v->fin = 0;
-
- v->lex_is_iterator = FALSE;
-
- rb_iv_set(parser, "@vstack", v->vstack);
- if (v->debug) {
- rb_iv_set(parser, "@tstack", v->tstack);
- }
- else {
- rb_iv_set(parser, "@tstack", Qnil);
- }
-
- return vparams;
-}
-
-static void
-cparse_params_mark(void *ptr)
-{
- struct cparse_params *v = (struct cparse_params*)ptr;
-
- rb_gc_mark(v->value_v);
- rb_gc_mark(v->parser);
- rb_gc_mark(v->lexer);
- rb_gc_mark(v->action_table);
- rb_gc_mark(v->action_check);
- rb_gc_mark(v->action_default);
- rb_gc_mark(v->action_pointer);
- rb_gc_mark(v->goto_table);
- rb_gc_mark(v->goto_check);
- rb_gc_mark(v->goto_default);
- rb_gc_mark(v->goto_pointer);
- rb_gc_mark(v->reduce_table);
- rb_gc_mark(v->token_table);
- rb_gc_mark(v->state);
- rb_gc_mark(v->vstack);
- rb_gc_mark(v->tstack);
- rb_gc_mark(v->t);
- rb_gc_mark(v->retval);
-}
-
-static size_t
-cparse_params_memsize(const void *ptr)
-{
- return sizeof(struct cparse_params);
-}
-
-static void
-extract_user_token(struct cparse_params *v, VALUE block_args,
- VALUE *tok, VALUE *val)
-{
- if (NIL_P(block_args)) {
- /* EOF */
- *tok = Qfalse;
- *val = rb_str_new("$", 1);
- return;
- }
-
- if (!RB_TYPE_P(block_args, T_ARRAY)) {
- rb_raise(rb_eTypeError,
- "%s() %s %"PRIsVALUE" (must be Array[2])",
- v->lex_is_iterator ? rb_id2name(v->lexmid) : "next_token",
- v->lex_is_iterator ? "yielded" : "returned",
- rb_obj_class(block_args));
- }
- if (RARRAY_LEN(block_args) != 2) {
- rb_raise(rb_eArgError,
- "%s() %s wrong size of array (%ld for 2)",
- v->lex_is_iterator ? rb_id2name(v->lexmid) : "next_token",
- v->lex_is_iterator ? "yielded" : "returned",
- RARRAY_LEN(block_args));
- }
- *tok = AREF(block_args, 0);
- *val = AREF(block_args, 1);
-}
-
-#define SHIFT(v,act,tok,val) shift(v,act,tok,val)
-#define REDUCE(v,act) do {\
- switch (reduce(v,act)) { \
- case 0: /* normal */ \
- break; \
- case 1: /* yyerror */ \
- goto user_yyerror; \
- case 2: /* yyaccept */ \
- D_puts("u accept"); \
- goto accept; \
- default: \
- break; \
- } \
-} while (0)
-
-static void
-parse_main(struct cparse_params *v, VALUE tok, VALUE val, int resume)
-{
- long i; /* table index */
- long act; /* action type */
- VALUE act_value; /* action type, VALUE version */
- int read_next = 1; /* true if we need to read next token */
- VALUE tmp;
-
- if (resume)
- goto resume;
-
- while (1) {
- D_puts("");
- D_puts("---- enter new loop ----");
- D_puts("");
-
- D_printf("(act) k1=%ld\n", v->curstate);
- tmp = AREF(v->action_pointer, v->curstate);
- if (NIL_P(tmp)) goto notfound;
- D_puts("(act) pointer[k1] ok");
- i = NUM2LONG(tmp);
-
- D_printf("read_next=%d\n", read_next);
- if (read_next && (v->t != vFINAL_TOKEN)) {
- if (v->lex_is_iterator) {
- D_puts("resuming...");
- if (v->fin) rb_raise(rb_eArgError, "token given after EOF");
- v->i = i; /* save i */
- return;
- resume:
- D_puts("resumed");
- i = v->i; /* load i */
- }
- else {
- D_puts("next_token");
- tmp = rb_funcall(v->parser, id_nexttoken, 0);
- extract_user_token(v, tmp, &tok, &val);
- }
- /* convert token */
- v->t = rb_hash_aref(v->token_table, tok);
- if (NIL_P(v->t)) {
- v->t = vERROR_TOKEN;
- }
- D_printf("(act) t(k2)=%ld\n", NUM2LONG(v->t));
- if (v->debug) {
- rb_funcall(v->parser, id_d_read_token,
- 3, v->t, tok, val);
- }
- }
- read_next = 0;
-
- i += NUM2LONG(v->t);
- D_printf("(act) i=%ld\n", i);
- if (i < 0) goto notfound;
-
- act_value = AREF(v->action_table, i);
- if (NIL_P(act_value)) goto notfound;
- act = NUM2LONG(act_value);
- D_printf("(act) table[i]=%ld\n", act);
-
- tmp = AREF(v->action_check, i);
- if (NIL_P(tmp)) goto notfound;
- if (NUM2LONG(tmp) != v->curstate) goto notfound;
- D_printf("(act) check[i]=%ld\n", NUM2LONG(tmp));
-
- D_puts("(act) found");
- act_fixed:
- D_printf("act=%ld\n", act);
- goto handle_act;
-
- notfound:
- D_puts("(act) not found: use default");
- act_value = AREF(v->action_default, v->curstate);
- act = NUM2LONG(act_value);
- goto act_fixed;
-
-
- handle_act:
- if (act > 0 && act < v->shift_n) {
- D_puts("shift");
- if (v->errstatus > 0) {
- v->errstatus--;
- rb_ivar_set(v->parser, id_errstatus, LONG2NUM(v->errstatus));
- }
- SHIFT(v, act, v->t, val);
- read_next = 1;
- }
- else if (act < 0 && act > -(v->reduce_n)) {
- D_puts("reduce");
- REDUCE(v, act);
- }
- else if (act == -(v->reduce_n)) {
- goto error;
- error_recovered:
- ; /* goto label requires stmt */
- }
- else if (act == v->shift_n) {
- D_puts("accept");
- goto accept;
- }
- else {
- rb_raise(RaccBug, "[Racc Bug] unknown act value %ld", act);
- }
-
- if (v->debug) {
- rb_funcall(v->parser, id_d_next_state,
- 2, LONG2NUM(v->curstate), v->state);
- }
- }
- /* not reach */
-
-
- accept:
- if (v->debug) rb_funcall(v->parser, id_d_accept, 0);
- v->retval = rb_ary_entry(v->vstack, 0);
- v->fin = CP_FIN_ACCEPT;
- return;
-
-
- error:
- D_printf("error detected, status=%ld\n", v->errstatus);
- if (v->errstatus == 0) {
- v->nerr++;
- rb_funcall(v->parser, id_onerror,
- 3, v->t, val, v->vstack);
- }
- user_yyerror:
- if (v->errstatus == 3) {
- if (v->t == vFINAL_TOKEN) {
- v->retval = Qnil;
- v->fin = CP_FIN_EOT;
- return;
- }
- read_next = 1;
- }
- v->errstatus = 3;
- rb_ivar_set(v->parser, id_errstatus, LONG2NUM(v->errstatus));
-
- /* check if we can shift/reduce error token */
- D_printf("(err) k1=%ld\n", v->curstate);
- D_printf("(err) k2=%d (error)\n", ERROR_TOKEN);
- while (1) {
- tmp = AREF(v->action_pointer, v->curstate);
- if (NIL_P(tmp)) goto error_pop;
- D_puts("(err) pointer[k1] ok");
-
- i = NUM2LONG(tmp) + ERROR_TOKEN;
- D_printf("(err) i=%ld\n", i);
- if (i < 0) goto error_pop;
-
- act_value = AREF(v->action_table, i);
- if (NIL_P(act_value)) {
- D_puts("(err) table[i] == nil");
- goto error_pop;
- }
- act = NUM2LONG(act_value);
- D_printf("(err) table[i]=%ld\n", act);
-
- tmp = AREF(v->action_check, i);
- if (NIL_P(tmp)) {
- D_puts("(err) check[i] == nil");
- goto error_pop;
- }
- if (NUM2LONG(tmp) != v->curstate) {
- D_puts("(err) check[i] != k1");
- goto error_pop;
- }
-
- D_puts("(err) found: can handle error token");
- break;
-
- error_pop:
- D_puts("(err) act not found: can't handle error token; pop");
-
- if (RARRAY_LEN(v->state) <= 1) {
- v->retval = Qnil;
- v->fin = CP_FIN_CANTPOP;
- return;
- }
- POP(v->state);
- POP(v->vstack);
- v->curstate = num_to_long(LAST_I(v->state));
- if (v->debug) {
- POP(v->tstack);
- rb_funcall(v->parser, id_d_e_pop,
- 3, v->state, v->tstack, v->vstack);
- }
- }
-
- /* shift/reduce error token */
- if (act > 0 && act < v->shift_n) {
- D_puts("e shift");
- SHIFT(v, act, ERROR_TOKEN, val);
- }
- else if (act < 0 && act > -(v->reduce_n)) {
- D_puts("e reduce");
- REDUCE(v, act);
- }
- else if (act == v->shift_n) {
- D_puts("e accept");
- goto accept;
- }
- else {
- rb_raise(RaccBug, "[Racc Bug] unknown act value %ld", act);
- }
- goto error_recovered;
-}
-
-static void
-shift(struct cparse_params *v, long act, VALUE tok, VALUE val)
-{
- PUSH(v->vstack, val);
- if (v->debug) {
- PUSH(v->tstack, tok);
- rb_funcall(v->parser, id_d_shift,
- 3, tok, v->tstack, v->vstack);
- }
- v->curstate = act;
- PUSH(v->state, LONG2NUM(v->curstate));
-}
-
-static int
-reduce(struct cparse_params *v, long act)
-{
- VALUE code;
- v->ruleno = -act * 3;
- code = rb_catch("racc_jump", reduce0, v->value_v);
- v->errstatus = num_to_long(rb_ivar_get(v->parser, id_errstatus));
- return NUM2INT(code);
-}
-
-static VALUE
-reduce0(RB_BLOCK_CALL_FUNC_ARGLIST(_, data))
-{
- struct cparse_params *v = rb_check_typeddata(data, &cparse_params_type);
- VALUE reduce_to, reduce_len, method_id;
- long len;
- ID mid;
- VALUE tmp, tmp_t = Qundef, tmp_v = Qundef;
- long i, k1, k2;
- VALUE goto_state;
-
- reduce_len = rb_ary_entry(v->reduce_table, v->ruleno);
- reduce_to = rb_ary_entry(v->reduce_table, v->ruleno+1);
- method_id = rb_ary_entry(v->reduce_table, v->ruleno+2);
- len = NUM2LONG(reduce_len);
- mid = value_to_id(method_id);
-
- /* call action */
- if (len == 0) {
- tmp = Qnil;
- if (mid != id_noreduce)
- tmp_v = rb_ary_new();
- if (v->debug)
- tmp_t = rb_ary_new();
- }
- else {
- if (mid != id_noreduce) {
- tmp_v = GET_TAIL(v->vstack, len);
- tmp = rb_ary_entry(tmp_v, 0);
- }
- else {
- tmp = rb_ary_entry(v->vstack, RARRAY_LEN(v->vstack) - len);
- }
- CUT_TAIL(v->vstack, len);
- if (v->debug) {
- tmp_t = GET_TAIL(v->tstack, len);
- CUT_TAIL(v->tstack, len);
- }
- CUT_TAIL(v->state, len);
- }
- if (mid != id_noreduce) {
- if (v->use_result_var) {
- tmp = rb_funcall(v->parser, mid,
- 3, tmp_v, v->vstack, tmp);
- }
- else {
- tmp = rb_funcall(v->parser, mid,
- 2, tmp_v, v->vstack);
- }
- }
-
- /* then push result */
- PUSH(v->vstack, tmp);
- if (v->debug) {
- PUSH(v->tstack, reduce_to);
- rb_funcall(v->parser, id_d_reduce,
- 4, tmp_t, reduce_to, v->tstack, v->vstack);
- }
-
- /* calculate transition state */
- if (RARRAY_LEN(v->state) == 0)
- rb_raise(RaccBug, "state stack unexpectedly empty");
- k2 = num_to_long(LAST_I(v->state));
- k1 = num_to_long(reduce_to) - v->nt_base;
- D_printf("(goto) k1=%ld\n", k1);
- D_printf("(goto) k2=%ld\n", k2);
-
- tmp = AREF(v->goto_pointer, k1);
- if (NIL_P(tmp)) goto notfound;
-
- i = NUM2LONG(tmp) + k2;
- D_printf("(goto) i=%ld\n", i);
- if (i < 0) goto notfound;
-
- goto_state = AREF(v->goto_table, i);
- if (NIL_P(goto_state)) {
- D_puts("(goto) table[i] == nil");
- goto notfound;
- }
- D_printf("(goto) table[i]=%ld (goto_state)\n", NUM2LONG(goto_state));
-
- tmp = AREF(v->goto_check, i);
- if (NIL_P(tmp)) {
- D_puts("(goto) check[i] == nil");
- goto notfound;
- }
- if (tmp != LONG2NUM(k1)) {
- D_puts("(goto) check[i] != table[i]");
- goto notfound;
- }
- D_printf("(goto) check[i]=%ld\n", NUM2LONG(tmp));
-
- D_puts("(goto) found");
- transit:
- PUSH(v->state, goto_state);
- v->curstate = NUM2LONG(goto_state);
- return INT2FIX(0);
-
- notfound:
- D_puts("(goto) not found: use default");
- /* overwrite `goto-state' by default value */
- goto_state = AREF(v->goto_default, k1);
- goto transit;
-}
-
-/* -----------------------------------------------------------------------
- Ruby Interface
------------------------------------------------------------------------ */
-
-void
-Init_cparse(void)
-{
-#ifdef HAVE_RB_EXT_RACTOR_SAFE
- rb_ext_ractor_safe(true);
-#endif
-
- VALUE Racc, Parser;
- ID id_racc = rb_intern_const("Racc");
-
- if (rb_const_defined(rb_cObject, id_racc)) {
- Racc = rb_const_get(rb_cObject, id_racc);
- Parser = rb_const_get_at(Racc, rb_intern_const("Parser"));
- }
- else {
- Racc = rb_define_module("Racc");
- Parser = rb_define_class_under(Racc, "Parser", rb_cObject);
- }
- rb_define_private_method(Parser, "_racc_do_parse_c", racc_cparse, 2);
- rb_define_private_method(Parser, "_racc_yyparse_c", racc_yyparse, 4);
- rb_define_const(Parser, "Racc_Runtime_Core_Version_C",
- rb_str_new2(RACC_VERSION));
- rb_define_const(Parser, "Racc_Runtime_Core_Id_C",
- rb_str_new2("$originalId: cparse.c,v 1.8 2006/07/06 11:39:46 aamine Exp $"));
-
- CparseParams = rb_define_class_under(Racc, "CparseParams", rb_cObject);
- rb_undef_alloc_func(CparseParams);
- rb_undef_method(CparseParams, "initialize");
- rb_undef_method(CparseParams, "initialize_copy");
-
- RaccBug = rb_eRuntimeError;
-
- id_yydebug = rb_intern_const("@yydebug");
- id_nexttoken = rb_intern_const("next_token");
- id_onerror = rb_intern_const("on_error");
- id_noreduce = rb_intern_const("_reduce_none");
- id_errstatus = rb_intern_const("@racc_error_status");
-
- id_d_shift = rb_intern_const("racc_shift");
- id_d_reduce = rb_intern_const("racc_reduce");
- id_d_accept = rb_intern_const("racc_accept");
- id_d_read_token = rb_intern_const("racc_read_token");
- id_d_next_state = rb_intern_const("racc_next_state");
- id_d_e_pop = rb_intern_const("racc_e_pop");
-}
diff --git a/ext/racc/cparse/depend b/ext/racc/cparse/depend
deleted file mode 100644
index 80dc7c456b..0000000000
--- a/ext/racc/cparse/depend
+++ /dev/null
@@ -1,161 +0,0 @@
-# AUTOGENERATED DEPENDENCIES START
-cparse.o: $(RUBY_EXTCONF_H)
-cparse.o: $(arch_hdrdir)/ruby/config.h
-cparse.o: $(hdrdir)/ruby.h
-cparse.o: $(hdrdir)/ruby/assert.h
-cparse.o: $(hdrdir)/ruby/backward.h
-cparse.o: $(hdrdir)/ruby/backward/2/assume.h
-cparse.o: $(hdrdir)/ruby/backward/2/attributes.h
-cparse.o: $(hdrdir)/ruby/backward/2/bool.h
-cparse.o: $(hdrdir)/ruby/backward/2/inttypes.h
-cparse.o: $(hdrdir)/ruby/backward/2/limits.h
-cparse.o: $(hdrdir)/ruby/backward/2/long_long.h
-cparse.o: $(hdrdir)/ruby/backward/2/stdalign.h
-cparse.o: $(hdrdir)/ruby/backward/2/stdarg.h
-cparse.o: $(hdrdir)/ruby/defines.h
-cparse.o: $(hdrdir)/ruby/intern.h
-cparse.o: $(hdrdir)/ruby/internal/abi.h
-cparse.o: $(hdrdir)/ruby/internal/anyargs.h
-cparse.o: $(hdrdir)/ruby/internal/arithmetic.h
-cparse.o: $(hdrdir)/ruby/internal/arithmetic/char.h
-cparse.o: $(hdrdir)/ruby/internal/arithmetic/double.h
-cparse.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h
-cparse.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h
-cparse.o: $(hdrdir)/ruby/internal/arithmetic/int.h
-cparse.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h
-cparse.o: $(hdrdir)/ruby/internal/arithmetic/long.h
-cparse.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h
-cparse.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h
-cparse.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h
-cparse.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h
-cparse.o: $(hdrdir)/ruby/internal/arithmetic/short.h
-cparse.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h
-cparse.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h
-cparse.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h
-cparse.o: $(hdrdir)/ruby/internal/assume.h
-cparse.o: $(hdrdir)/ruby/internal/attr/alloc_size.h
-cparse.o: $(hdrdir)/ruby/internal/attr/artificial.h
-cparse.o: $(hdrdir)/ruby/internal/attr/cold.h
-cparse.o: $(hdrdir)/ruby/internal/attr/const.h
-cparse.o: $(hdrdir)/ruby/internal/attr/constexpr.h
-cparse.o: $(hdrdir)/ruby/internal/attr/deprecated.h
-cparse.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h
-cparse.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h
-cparse.o: $(hdrdir)/ruby/internal/attr/error.h
-cparse.o: $(hdrdir)/ruby/internal/attr/flag_enum.h
-cparse.o: $(hdrdir)/ruby/internal/attr/forceinline.h
-cparse.o: $(hdrdir)/ruby/internal/attr/format.h
-cparse.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h
-cparse.o: $(hdrdir)/ruby/internal/attr/noalias.h
-cparse.o: $(hdrdir)/ruby/internal/attr/nodiscard.h
-cparse.o: $(hdrdir)/ruby/internal/attr/noexcept.h
-cparse.o: $(hdrdir)/ruby/internal/attr/noinline.h
-cparse.o: $(hdrdir)/ruby/internal/attr/nonnull.h
-cparse.o: $(hdrdir)/ruby/internal/attr/noreturn.h
-cparse.o: $(hdrdir)/ruby/internal/attr/packed_struct.h
-cparse.o: $(hdrdir)/ruby/internal/attr/pure.h
-cparse.o: $(hdrdir)/ruby/internal/attr/restrict.h
-cparse.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h
-cparse.o: $(hdrdir)/ruby/internal/attr/warning.h
-cparse.o: $(hdrdir)/ruby/internal/attr/weakref.h
-cparse.o: $(hdrdir)/ruby/internal/cast.h
-cparse.o: $(hdrdir)/ruby/internal/compiler_is.h
-cparse.o: $(hdrdir)/ruby/internal/compiler_is/apple.h
-cparse.o: $(hdrdir)/ruby/internal/compiler_is/clang.h
-cparse.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h
-cparse.o: $(hdrdir)/ruby/internal/compiler_is/intel.h
-cparse.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h
-cparse.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h
-cparse.o: $(hdrdir)/ruby/internal/compiler_since.h
-cparse.o: $(hdrdir)/ruby/internal/config.h
-cparse.o: $(hdrdir)/ruby/internal/constant_p.h
-cparse.o: $(hdrdir)/ruby/internal/core.h
-cparse.o: $(hdrdir)/ruby/internal/core/rarray.h
-cparse.o: $(hdrdir)/ruby/internal/core/rbasic.h
-cparse.o: $(hdrdir)/ruby/internal/core/rbignum.h
-cparse.o: $(hdrdir)/ruby/internal/core/rclass.h
-cparse.o: $(hdrdir)/ruby/internal/core/rdata.h
-cparse.o: $(hdrdir)/ruby/internal/core/rfile.h
-cparse.o: $(hdrdir)/ruby/internal/core/rhash.h
-cparse.o: $(hdrdir)/ruby/internal/core/robject.h
-cparse.o: $(hdrdir)/ruby/internal/core/rregexp.h
-cparse.o: $(hdrdir)/ruby/internal/core/rstring.h
-cparse.o: $(hdrdir)/ruby/internal/core/rstruct.h
-cparse.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
-cparse.o: $(hdrdir)/ruby/internal/ctype.h
-cparse.o: $(hdrdir)/ruby/internal/dllexport.h
-cparse.o: $(hdrdir)/ruby/internal/dosish.h
-cparse.o: $(hdrdir)/ruby/internal/error.h
-cparse.o: $(hdrdir)/ruby/internal/eval.h
-cparse.o: $(hdrdir)/ruby/internal/event.h
-cparse.o: $(hdrdir)/ruby/internal/fl_type.h
-cparse.o: $(hdrdir)/ruby/internal/gc.h
-cparse.o: $(hdrdir)/ruby/internal/glob.h
-cparse.o: $(hdrdir)/ruby/internal/globals.h
-cparse.o: $(hdrdir)/ruby/internal/has/attribute.h
-cparse.o: $(hdrdir)/ruby/internal/has/builtin.h
-cparse.o: $(hdrdir)/ruby/internal/has/c_attribute.h
-cparse.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h
-cparse.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h
-cparse.o: $(hdrdir)/ruby/internal/has/extension.h
-cparse.o: $(hdrdir)/ruby/internal/has/feature.h
-cparse.o: $(hdrdir)/ruby/internal/has/warning.h
-cparse.o: $(hdrdir)/ruby/internal/intern/array.h
-cparse.o: $(hdrdir)/ruby/internal/intern/bignum.h
-cparse.o: $(hdrdir)/ruby/internal/intern/class.h
-cparse.o: $(hdrdir)/ruby/internal/intern/compar.h
-cparse.o: $(hdrdir)/ruby/internal/intern/complex.h
-cparse.o: $(hdrdir)/ruby/internal/intern/cont.h
-cparse.o: $(hdrdir)/ruby/internal/intern/dir.h
-cparse.o: $(hdrdir)/ruby/internal/intern/enum.h
-cparse.o: $(hdrdir)/ruby/internal/intern/enumerator.h
-cparse.o: $(hdrdir)/ruby/internal/intern/error.h
-cparse.o: $(hdrdir)/ruby/internal/intern/eval.h
-cparse.o: $(hdrdir)/ruby/internal/intern/file.h
-cparse.o: $(hdrdir)/ruby/internal/intern/hash.h
-cparse.o: $(hdrdir)/ruby/internal/intern/io.h
-cparse.o: $(hdrdir)/ruby/internal/intern/load.h
-cparse.o: $(hdrdir)/ruby/internal/intern/marshal.h
-cparse.o: $(hdrdir)/ruby/internal/intern/numeric.h
-cparse.o: $(hdrdir)/ruby/internal/intern/object.h
-cparse.o: $(hdrdir)/ruby/internal/intern/parse.h
-cparse.o: $(hdrdir)/ruby/internal/intern/proc.h
-cparse.o: $(hdrdir)/ruby/internal/intern/process.h
-cparse.o: $(hdrdir)/ruby/internal/intern/random.h
-cparse.o: $(hdrdir)/ruby/internal/intern/range.h
-cparse.o: $(hdrdir)/ruby/internal/intern/rational.h
-cparse.o: $(hdrdir)/ruby/internal/intern/re.h
-cparse.o: $(hdrdir)/ruby/internal/intern/ruby.h
-cparse.o: $(hdrdir)/ruby/internal/intern/select.h
-cparse.o: $(hdrdir)/ruby/internal/intern/select/largesize.h
-cparse.o: $(hdrdir)/ruby/internal/intern/signal.h
-cparse.o: $(hdrdir)/ruby/internal/intern/sprintf.h
-cparse.o: $(hdrdir)/ruby/internal/intern/string.h
-cparse.o: $(hdrdir)/ruby/internal/intern/struct.h
-cparse.o: $(hdrdir)/ruby/internal/intern/thread.h
-cparse.o: $(hdrdir)/ruby/internal/intern/time.h
-cparse.o: $(hdrdir)/ruby/internal/intern/variable.h
-cparse.o: $(hdrdir)/ruby/internal/intern/vm.h
-cparse.o: $(hdrdir)/ruby/internal/interpreter.h
-cparse.o: $(hdrdir)/ruby/internal/iterator.h
-cparse.o: $(hdrdir)/ruby/internal/memory.h
-cparse.o: $(hdrdir)/ruby/internal/method.h
-cparse.o: $(hdrdir)/ruby/internal/module.h
-cparse.o: $(hdrdir)/ruby/internal/newobj.h
-cparse.o: $(hdrdir)/ruby/internal/scan_args.h
-cparse.o: $(hdrdir)/ruby/internal/special_consts.h
-cparse.o: $(hdrdir)/ruby/internal/static_assert.h
-cparse.o: $(hdrdir)/ruby/internal/stdalign.h
-cparse.o: $(hdrdir)/ruby/internal/stdbool.h
-cparse.o: $(hdrdir)/ruby/internal/symbol.h
-cparse.o: $(hdrdir)/ruby/internal/value.h
-cparse.o: $(hdrdir)/ruby/internal/value_type.h
-cparse.o: $(hdrdir)/ruby/internal/variable.h
-cparse.o: $(hdrdir)/ruby/internal/warning_push.h
-cparse.o: $(hdrdir)/ruby/internal/xmalloc.h
-cparse.o: $(hdrdir)/ruby/missing.h
-cparse.o: $(hdrdir)/ruby/ruby.h
-cparse.o: $(hdrdir)/ruby/st.h
-cparse.o: $(hdrdir)/ruby/subst.h
-cparse.o: cparse.c
-# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/racc/cparse/extconf.rb b/ext/racc/cparse/extconf.rb
deleted file mode 100644
index 18c5689ad8..0000000000
--- a/ext/racc/cparse/extconf.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: false
-#
-
-require 'mkmf'
-
-have_func('rb_block_call')
-have_func('rb_ary_subseq')
-
-create_makefile 'racc/cparse'
diff --git a/ext/rbconfig/sizeof/depend b/ext/rbconfig/sizeof/depend
index 4e4ebd4ae5..5f75fa8c76 100644
--- a/ext/rbconfig/sizeof/depend
+++ b/ext/rbconfig/sizeof/depend
@@ -161,6 +161,7 @@ limits.o: $(hdrdir)/ruby/internal/special_consts.h
limits.o: $(hdrdir)/ruby/internal/static_assert.h
limits.o: $(hdrdir)/ruby/internal/stdalign.h
limits.o: $(hdrdir)/ruby/internal/stdbool.h
+limits.o: $(hdrdir)/ruby/internal/stdckdint.h
limits.o: $(hdrdir)/ruby/internal/symbol.h
limits.o: $(hdrdir)/ruby/internal/value.h
limits.o: $(hdrdir)/ruby/internal/value_type.h
@@ -319,6 +320,7 @@ sizes.o: $(hdrdir)/ruby/internal/special_consts.h
sizes.o: $(hdrdir)/ruby/internal/static_assert.h
sizes.o: $(hdrdir)/ruby/internal/stdalign.h
sizes.o: $(hdrdir)/ruby/internal/stdbool.h
+sizes.o: $(hdrdir)/ruby/internal/stdckdint.h
sizes.o: $(hdrdir)/ruby/internal/symbol.h
sizes.o: $(hdrdir)/ruby/internal/value.h
sizes.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/ripper/depend b/ext/ripper/depend
index da1bf58da7..fe6bd872bd 100644
--- a/ext/ripper/depend
+++ b/ext/ripper/depend
@@ -5,13 +5,14 @@ BISON = $(BASERUBY) $(top_srcdir)/tool/lrama/exe/lrama
.SUFFIXES: .y
-src: ripper.c eventids1.c eventids2table.c
+src: ripper.c ripper_init.c eventids1.c eventids1.h eventids2table.c
+ripper.c ripper_init.c eventids1.c eventids1.h eventids2table.c: depend
ripper.o: ripper.c
.y.c:
$(ECHO) compiling compiler $<
- $(Q) $(BISON) -t -v -o$@ -h$*.h - parse.y < $<
+ $(Q) $(BISON) -t -v -o$@ - $< < $<
all: check
static: check
@@ -28,6 +29,10 @@ check: .eventids2-check
$(Q) $(RUBY) $(GEN) --mode=check --ids1src=$(SRC1) --ids2src=$(SRC2)
@exit > $@
+eventids1.h: $(GEN) $(srcdir)/tools/dsl.rb $(SRC1)
+ $(ECHO) generating $@ from $(SRC1)
+ $(Q) $(RUBY) $(GEN) --mode=eventids1_h --ids1src=$(SRC1) --output=$@
+
eventids1.c: $(GEN) $(srcdir)/tools/dsl.rb $(SRC1)
$(ECHO) generating $@ from $(SRC1)
$(Q) $(RUBY) $(GEN) --mode=eventids1 --ids1src=$(SRC1) --output=$@
@@ -36,6 +41,10 @@ eventids2table.c: $(GEN) $(srcdir)/tools/dsl.rb $(SRC2)
$(ECHO) generating $@ from $(SRC2)
$(Q) $(RUBY) $(GEN) --mode=eventids2table --ids2src=$(SRC2) --output=$@
+ripper_init.c: $(srcdir)/ripper_init.c.tmpl ripper.y $(srcdir)/tools/preproc.rb $(top_srcdir)/internal/ruby_parser.h
+ $(ECHO) generating $@ from $(srcdir)/ripper_init.c.tmpl
+ $(Q) $(RUBY) $(srcdir)/tools/preproc.rb --output=$@ --template=$(srcdir)/ripper_init.c.tmpl $(top_srcdir)/internal/ruby_parser.h
+
# Entries for Ripper maintainer
preproc: ripper.E
@@ -44,6 +53,344 @@ ripper.E: ripper.c
$(Q) $(CC) -E $(INCFLAGS) $(CPPFLAGS) $< | $(RUBY) $(srcdir)/tools/strip.rb > $@
# AUTOGENERATED DEPENDENCIES START
+eventids1.o: $(RUBY_EXTCONF_H)
+eventids1.o: $(arch_hdrdir)/ruby/config.h
+eventids1.o: $(hdrdir)/ruby/assert.h
+eventids1.o: $(hdrdir)/ruby/backward.h
+eventids1.o: $(hdrdir)/ruby/backward/2/assume.h
+eventids1.o: $(hdrdir)/ruby/backward/2/attributes.h
+eventids1.o: $(hdrdir)/ruby/backward/2/bool.h
+eventids1.o: $(hdrdir)/ruby/backward/2/inttypes.h
+eventids1.o: $(hdrdir)/ruby/backward/2/limits.h
+eventids1.o: $(hdrdir)/ruby/backward/2/long_long.h
+eventids1.o: $(hdrdir)/ruby/backward/2/stdalign.h
+eventids1.o: $(hdrdir)/ruby/backward/2/stdarg.h
+eventids1.o: $(hdrdir)/ruby/defines.h
+eventids1.o: $(hdrdir)/ruby/intern.h
+eventids1.o: $(hdrdir)/ruby/internal/abi.h
+eventids1.o: $(hdrdir)/ruby/internal/anyargs.h
+eventids1.o: $(hdrdir)/ruby/internal/arithmetic.h
+eventids1.o: $(hdrdir)/ruby/internal/arithmetic/char.h
+eventids1.o: $(hdrdir)/ruby/internal/arithmetic/double.h
+eventids1.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h
+eventids1.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h
+eventids1.o: $(hdrdir)/ruby/internal/arithmetic/int.h
+eventids1.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h
+eventids1.o: $(hdrdir)/ruby/internal/arithmetic/long.h
+eventids1.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h
+eventids1.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h
+eventids1.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h
+eventids1.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h
+eventids1.o: $(hdrdir)/ruby/internal/arithmetic/short.h
+eventids1.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h
+eventids1.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h
+eventids1.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h
+eventids1.o: $(hdrdir)/ruby/internal/assume.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/alloc_size.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/artificial.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/cold.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/const.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/constexpr.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/deprecated.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/error.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/flag_enum.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/forceinline.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/format.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/noalias.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/nodiscard.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/noexcept.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/noinline.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/nonnull.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/noreturn.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/packed_struct.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/pure.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/restrict.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/warning.h
+eventids1.o: $(hdrdir)/ruby/internal/attr/weakref.h
+eventids1.o: $(hdrdir)/ruby/internal/cast.h
+eventids1.o: $(hdrdir)/ruby/internal/compiler_is.h
+eventids1.o: $(hdrdir)/ruby/internal/compiler_is/apple.h
+eventids1.o: $(hdrdir)/ruby/internal/compiler_is/clang.h
+eventids1.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h
+eventids1.o: $(hdrdir)/ruby/internal/compiler_is/intel.h
+eventids1.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h
+eventids1.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h
+eventids1.o: $(hdrdir)/ruby/internal/compiler_since.h
+eventids1.o: $(hdrdir)/ruby/internal/config.h
+eventids1.o: $(hdrdir)/ruby/internal/constant_p.h
+eventids1.o: $(hdrdir)/ruby/internal/core.h
+eventids1.o: $(hdrdir)/ruby/internal/core/rarray.h
+eventids1.o: $(hdrdir)/ruby/internal/core/rbasic.h
+eventids1.o: $(hdrdir)/ruby/internal/core/rbignum.h
+eventids1.o: $(hdrdir)/ruby/internal/core/rclass.h
+eventids1.o: $(hdrdir)/ruby/internal/core/rdata.h
+eventids1.o: $(hdrdir)/ruby/internal/core/rfile.h
+eventids1.o: $(hdrdir)/ruby/internal/core/rhash.h
+eventids1.o: $(hdrdir)/ruby/internal/core/robject.h
+eventids1.o: $(hdrdir)/ruby/internal/core/rregexp.h
+eventids1.o: $(hdrdir)/ruby/internal/core/rstring.h
+eventids1.o: $(hdrdir)/ruby/internal/core/rstruct.h
+eventids1.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
+eventids1.o: $(hdrdir)/ruby/internal/ctype.h
+eventids1.o: $(hdrdir)/ruby/internal/dllexport.h
+eventids1.o: $(hdrdir)/ruby/internal/dosish.h
+eventids1.o: $(hdrdir)/ruby/internal/error.h
+eventids1.o: $(hdrdir)/ruby/internal/eval.h
+eventids1.o: $(hdrdir)/ruby/internal/event.h
+eventids1.o: $(hdrdir)/ruby/internal/fl_type.h
+eventids1.o: $(hdrdir)/ruby/internal/gc.h
+eventids1.o: $(hdrdir)/ruby/internal/glob.h
+eventids1.o: $(hdrdir)/ruby/internal/globals.h
+eventids1.o: $(hdrdir)/ruby/internal/has/attribute.h
+eventids1.o: $(hdrdir)/ruby/internal/has/builtin.h
+eventids1.o: $(hdrdir)/ruby/internal/has/c_attribute.h
+eventids1.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h
+eventids1.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h
+eventids1.o: $(hdrdir)/ruby/internal/has/extension.h
+eventids1.o: $(hdrdir)/ruby/internal/has/feature.h
+eventids1.o: $(hdrdir)/ruby/internal/has/warning.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/array.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/bignum.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/class.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/compar.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/complex.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/cont.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/dir.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/enum.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/enumerator.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/error.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/eval.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/file.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/hash.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/io.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/load.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/marshal.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/numeric.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/object.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/parse.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/proc.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/process.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/random.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/range.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/rational.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/re.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/ruby.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/select.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/select/largesize.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/signal.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/sprintf.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/string.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/struct.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/thread.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/time.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/variable.h
+eventids1.o: $(hdrdir)/ruby/internal/intern/vm.h
+eventids1.o: $(hdrdir)/ruby/internal/interpreter.h
+eventids1.o: $(hdrdir)/ruby/internal/iterator.h
+eventids1.o: $(hdrdir)/ruby/internal/memory.h
+eventids1.o: $(hdrdir)/ruby/internal/method.h
+eventids1.o: $(hdrdir)/ruby/internal/module.h
+eventids1.o: $(hdrdir)/ruby/internal/newobj.h
+eventids1.o: $(hdrdir)/ruby/internal/scan_args.h
+eventids1.o: $(hdrdir)/ruby/internal/special_consts.h
+eventids1.o: $(hdrdir)/ruby/internal/static_assert.h
+eventids1.o: $(hdrdir)/ruby/internal/stdalign.h
+eventids1.o: $(hdrdir)/ruby/internal/stdbool.h
+eventids1.o: $(hdrdir)/ruby/internal/stdckdint.h
+eventids1.o: $(hdrdir)/ruby/internal/symbol.h
+eventids1.o: $(hdrdir)/ruby/internal/value.h
+eventids1.o: $(hdrdir)/ruby/internal/value_type.h
+eventids1.o: $(hdrdir)/ruby/internal/variable.h
+eventids1.o: $(hdrdir)/ruby/internal/warning_push.h
+eventids1.o: $(hdrdir)/ruby/internal/xmalloc.h
+eventids1.o: $(hdrdir)/ruby/missing.h
+eventids1.o: $(hdrdir)/ruby/ruby.h
+eventids1.o: $(hdrdir)/ruby/st.h
+eventids1.o: $(hdrdir)/ruby/subst.h
+eventids1.o: eventids1.h
+eventids1.o: {$(VPATH)}eventids1.c
+eventids1.o: {$(VPATH)}eventids1.h
+eventids2.o: $(RUBY_EXTCONF_H)
+eventids2.o: $(arch_hdrdir)/ruby/config.h
+eventids2.o: $(hdrdir)/ruby/assert.h
+eventids2.o: $(hdrdir)/ruby/backward.h
+eventids2.o: $(hdrdir)/ruby/backward/2/assume.h
+eventids2.o: $(hdrdir)/ruby/backward/2/attributes.h
+eventids2.o: $(hdrdir)/ruby/backward/2/bool.h
+eventids2.o: $(hdrdir)/ruby/backward/2/inttypes.h
+eventids2.o: $(hdrdir)/ruby/backward/2/limits.h
+eventids2.o: $(hdrdir)/ruby/backward/2/long_long.h
+eventids2.o: $(hdrdir)/ruby/backward/2/stdalign.h
+eventids2.o: $(hdrdir)/ruby/backward/2/stdarg.h
+eventids2.o: $(hdrdir)/ruby/defines.h
+eventids2.o: $(hdrdir)/ruby/encoding.h
+eventids2.o: $(hdrdir)/ruby/intern.h
+eventids2.o: $(hdrdir)/ruby/internal/abi.h
+eventids2.o: $(hdrdir)/ruby/internal/anyargs.h
+eventids2.o: $(hdrdir)/ruby/internal/arithmetic.h
+eventids2.o: $(hdrdir)/ruby/internal/arithmetic/char.h
+eventids2.o: $(hdrdir)/ruby/internal/arithmetic/double.h
+eventids2.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h
+eventids2.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h
+eventids2.o: $(hdrdir)/ruby/internal/arithmetic/int.h
+eventids2.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h
+eventids2.o: $(hdrdir)/ruby/internal/arithmetic/long.h
+eventids2.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h
+eventids2.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h
+eventids2.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h
+eventids2.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h
+eventids2.o: $(hdrdir)/ruby/internal/arithmetic/short.h
+eventids2.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h
+eventids2.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h
+eventids2.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h
+eventids2.o: $(hdrdir)/ruby/internal/assume.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/alloc_size.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/artificial.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/cold.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/const.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/constexpr.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/deprecated.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/error.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/flag_enum.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/forceinline.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/format.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/noalias.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/nodiscard.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/noexcept.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/noinline.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/nonnull.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/noreturn.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/packed_struct.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/pure.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/restrict.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/warning.h
+eventids2.o: $(hdrdir)/ruby/internal/attr/weakref.h
+eventids2.o: $(hdrdir)/ruby/internal/cast.h
+eventids2.o: $(hdrdir)/ruby/internal/compiler_is.h
+eventids2.o: $(hdrdir)/ruby/internal/compiler_is/apple.h
+eventids2.o: $(hdrdir)/ruby/internal/compiler_is/clang.h
+eventids2.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h
+eventids2.o: $(hdrdir)/ruby/internal/compiler_is/intel.h
+eventids2.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h
+eventids2.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h
+eventids2.o: $(hdrdir)/ruby/internal/compiler_since.h
+eventids2.o: $(hdrdir)/ruby/internal/config.h
+eventids2.o: $(hdrdir)/ruby/internal/constant_p.h
+eventids2.o: $(hdrdir)/ruby/internal/core.h
+eventids2.o: $(hdrdir)/ruby/internal/core/rarray.h
+eventids2.o: $(hdrdir)/ruby/internal/core/rbasic.h
+eventids2.o: $(hdrdir)/ruby/internal/core/rbignum.h
+eventids2.o: $(hdrdir)/ruby/internal/core/rclass.h
+eventids2.o: $(hdrdir)/ruby/internal/core/rdata.h
+eventids2.o: $(hdrdir)/ruby/internal/core/rfile.h
+eventids2.o: $(hdrdir)/ruby/internal/core/rhash.h
+eventids2.o: $(hdrdir)/ruby/internal/core/robject.h
+eventids2.o: $(hdrdir)/ruby/internal/core/rregexp.h
+eventids2.o: $(hdrdir)/ruby/internal/core/rstring.h
+eventids2.o: $(hdrdir)/ruby/internal/core/rstruct.h
+eventids2.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
+eventids2.o: $(hdrdir)/ruby/internal/ctype.h
+eventids2.o: $(hdrdir)/ruby/internal/dllexport.h
+eventids2.o: $(hdrdir)/ruby/internal/dosish.h
+eventids2.o: $(hdrdir)/ruby/internal/encoding/coderange.h
+eventids2.o: $(hdrdir)/ruby/internal/encoding/ctype.h
+eventids2.o: $(hdrdir)/ruby/internal/encoding/encoding.h
+eventids2.o: $(hdrdir)/ruby/internal/encoding/pathname.h
+eventids2.o: $(hdrdir)/ruby/internal/encoding/re.h
+eventids2.o: $(hdrdir)/ruby/internal/encoding/sprintf.h
+eventids2.o: $(hdrdir)/ruby/internal/encoding/string.h
+eventids2.o: $(hdrdir)/ruby/internal/encoding/symbol.h
+eventids2.o: $(hdrdir)/ruby/internal/encoding/transcode.h
+eventids2.o: $(hdrdir)/ruby/internal/error.h
+eventids2.o: $(hdrdir)/ruby/internal/eval.h
+eventids2.o: $(hdrdir)/ruby/internal/event.h
+eventids2.o: $(hdrdir)/ruby/internal/fl_type.h
+eventids2.o: $(hdrdir)/ruby/internal/gc.h
+eventids2.o: $(hdrdir)/ruby/internal/glob.h
+eventids2.o: $(hdrdir)/ruby/internal/globals.h
+eventids2.o: $(hdrdir)/ruby/internal/has/attribute.h
+eventids2.o: $(hdrdir)/ruby/internal/has/builtin.h
+eventids2.o: $(hdrdir)/ruby/internal/has/c_attribute.h
+eventids2.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h
+eventids2.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h
+eventids2.o: $(hdrdir)/ruby/internal/has/extension.h
+eventids2.o: $(hdrdir)/ruby/internal/has/feature.h
+eventids2.o: $(hdrdir)/ruby/internal/has/warning.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/array.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/bignum.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/class.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/compar.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/complex.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/cont.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/dir.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/enum.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/enumerator.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/error.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/eval.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/file.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/hash.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/io.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/load.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/marshal.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/numeric.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/object.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/parse.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/proc.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/process.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/random.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/range.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/rational.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/re.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/ruby.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/select.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/select/largesize.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/signal.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/sprintf.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/string.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/struct.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/thread.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/time.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/variable.h
+eventids2.o: $(hdrdir)/ruby/internal/intern/vm.h
+eventids2.o: $(hdrdir)/ruby/internal/interpreter.h
+eventids2.o: $(hdrdir)/ruby/internal/iterator.h
+eventids2.o: $(hdrdir)/ruby/internal/memory.h
+eventids2.o: $(hdrdir)/ruby/internal/method.h
+eventids2.o: $(hdrdir)/ruby/internal/module.h
+eventids2.o: $(hdrdir)/ruby/internal/newobj.h
+eventids2.o: $(hdrdir)/ruby/internal/scan_args.h
+eventids2.o: $(hdrdir)/ruby/internal/special_consts.h
+eventids2.o: $(hdrdir)/ruby/internal/static_assert.h
+eventids2.o: $(hdrdir)/ruby/internal/stdalign.h
+eventids2.o: $(hdrdir)/ruby/internal/stdbool.h
+eventids2.o: $(hdrdir)/ruby/internal/stdckdint.h
+eventids2.o: $(hdrdir)/ruby/internal/symbol.h
+eventids2.o: $(hdrdir)/ruby/internal/value.h
+eventids2.o: $(hdrdir)/ruby/internal/value_type.h
+eventids2.o: $(hdrdir)/ruby/internal/variable.h
+eventids2.o: $(hdrdir)/ruby/internal/warning_push.h
+eventids2.o: $(hdrdir)/ruby/internal/xmalloc.h
+eventids2.o: $(hdrdir)/ruby/missing.h
+eventids2.o: $(hdrdir)/ruby/onigmo.h
+eventids2.o: $(hdrdir)/ruby/oniguruma.h
+eventids2.o: $(hdrdir)/ruby/ruby.h
+eventids2.o: $(hdrdir)/ruby/st.h
+eventids2.o: $(hdrdir)/ruby/subst.h
+eventids2.o: $(top_srcdir)/internal.h
+eventids2.o: $(top_srcdir)/internal/static_assert.h
+eventids2.o: $(top_srcdir)/rubyparser.h
+eventids2.o: eventids2.c
+eventids2.o: eventids2.h
+eventids2.o: {$(VPATH)}eventids2table.c
+eventids2.o: {$(VPATH)}parse.h
ripper.o: $(RUBY_EXTCONF_H)
ripper.o: $(arch_hdrdir)/ruby/config.h
ripper.o: $(hdrdir)/ruby.h
@@ -204,6 +551,7 @@ ripper.o: $(hdrdir)/ruby/internal/special_consts.h
ripper.o: $(hdrdir)/ruby/internal/static_assert.h
ripper.o: $(hdrdir)/ruby/internal/stdalign.h
ripper.o: $(hdrdir)/ruby/internal/stdbool.h
+ripper.o: $(hdrdir)/ruby/internal/stdckdint.h
ripper.o: $(hdrdir)/ruby/internal/symbol.h
ripper.o: $(hdrdir)/ruby/internal/value.h
ripper.o: $(hdrdir)/ruby/internal/value_type.h
@@ -221,6 +569,7 @@ ripper.o: $(hdrdir)/ruby/st.h
ripper.o: $(hdrdir)/ruby/subst.h
ripper.o: $(hdrdir)/ruby/thread_native.h
ripper.o: $(hdrdir)/ruby/util.h
+ripper.o: $(hdrdir)/ruby/version.h
ripper.o: $(top_srcdir)/ccan/check_type/check_type.h
ripper.o: $(top_srcdir)/ccan/container_of/container_of.h
ripper.o: $(top_srcdir)/ccan/list/list.h
@@ -246,6 +595,8 @@ ripper.o: $(top_srcdir)/internal/numeric.h
ripper.o: $(top_srcdir)/internal/parse.h
ripper.o: $(top_srcdir)/internal/rational.h
ripper.o: $(top_srcdir)/internal/re.h
+ripper.o: $(top_srcdir)/internal/ruby_parser.h
+ripper.o: $(top_srcdir)/internal/sanitizers.h
ripper.o: $(top_srcdir)/internal/serial.h
ripper.o: $(top_srcdir)/internal/static_assert.h
ripper.o: $(top_srcdir)/internal/string.h
@@ -256,17 +607,23 @@ ripper.o: $(top_srcdir)/internal/vm.h
ripper.o: $(top_srcdir)/internal/warnings.h
ripper.o: $(top_srcdir)/method.h
ripper.o: $(top_srcdir)/node.h
+ripper.o: $(top_srcdir)/parser_node.h
+ripper.o: $(top_srcdir)/parser_st.h
ripper.o: $(top_srcdir)/regenc.h
ripper.o: $(top_srcdir)/ruby_assert.h
ripper.o: $(top_srcdir)/ruby_atomic.h
+ripper.o: $(top_srcdir)/rubyparser.h
ripper.o: $(top_srcdir)/shape.h
ripper.o: $(top_srcdir)/symbol.h
ripper.o: $(top_srcdir)/thread_pthread.h
ripper.o: $(top_srcdir)/vm_core.h
ripper.o: $(top_srcdir)/vm_opts.h
ripper.o: ../../probes.h
+ripper.o: eventids1.h
ripper.o: eventids2.c
+ripper.o: eventids2.h
ripper.o: ripper.y
+ripper.o: ripper_init.h
ripper.o: {$(VPATH)}eventids1.c
ripper.o: {$(VPATH)}eventids2table.c
ripper.o: {$(VPATH)}id.h
@@ -274,4 +631,198 @@ ripper.o: {$(VPATH)}lex.c
ripper.o: {$(VPATH)}parse.h
ripper.o: {$(VPATH)}probes.dmyh
ripper.o: {$(VPATH)}ripper.c
+ripper_init.o: $(RUBY_EXTCONF_H)
+ripper_init.o: $(arch_hdrdir)/ruby/config.h
+ripper_init.o: $(hdrdir)/ruby/assert.h
+ripper_init.o: $(hdrdir)/ruby/backward.h
+ripper_init.o: $(hdrdir)/ruby/backward/2/assume.h
+ripper_init.o: $(hdrdir)/ruby/backward/2/attributes.h
+ripper_init.o: $(hdrdir)/ruby/backward/2/bool.h
+ripper_init.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h
+ripper_init.o: $(hdrdir)/ruby/backward/2/inttypes.h
+ripper_init.o: $(hdrdir)/ruby/backward/2/limits.h
+ripper_init.o: $(hdrdir)/ruby/backward/2/long_long.h
+ripper_init.o: $(hdrdir)/ruby/backward/2/stdalign.h
+ripper_init.o: $(hdrdir)/ruby/backward/2/stdarg.h
+ripper_init.o: $(hdrdir)/ruby/defines.h
+ripper_init.o: $(hdrdir)/ruby/encoding.h
+ripper_init.o: $(hdrdir)/ruby/intern.h
+ripper_init.o: $(hdrdir)/ruby/internal/abi.h
+ripper_init.o: $(hdrdir)/ruby/internal/anyargs.h
+ripper_init.o: $(hdrdir)/ruby/internal/arithmetic.h
+ripper_init.o: $(hdrdir)/ruby/internal/arithmetic/char.h
+ripper_init.o: $(hdrdir)/ruby/internal/arithmetic/double.h
+ripper_init.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h
+ripper_init.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h
+ripper_init.o: $(hdrdir)/ruby/internal/arithmetic/int.h
+ripper_init.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h
+ripper_init.o: $(hdrdir)/ruby/internal/arithmetic/long.h
+ripper_init.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h
+ripper_init.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h
+ripper_init.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h
+ripper_init.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h
+ripper_init.o: $(hdrdir)/ruby/internal/arithmetic/short.h
+ripper_init.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h
+ripper_init.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h
+ripper_init.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h
+ripper_init.o: $(hdrdir)/ruby/internal/assume.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/alloc_size.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/artificial.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/cold.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/const.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/constexpr.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/deprecated.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/error.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/flag_enum.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/forceinline.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/format.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/noalias.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/nodiscard.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/noexcept.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/noinline.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/nonnull.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/noreturn.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/packed_struct.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/pure.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/restrict.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/warning.h
+ripper_init.o: $(hdrdir)/ruby/internal/attr/weakref.h
+ripper_init.o: $(hdrdir)/ruby/internal/cast.h
+ripper_init.o: $(hdrdir)/ruby/internal/compiler_is.h
+ripper_init.o: $(hdrdir)/ruby/internal/compiler_is/apple.h
+ripper_init.o: $(hdrdir)/ruby/internal/compiler_is/clang.h
+ripper_init.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h
+ripper_init.o: $(hdrdir)/ruby/internal/compiler_is/intel.h
+ripper_init.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h
+ripper_init.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h
+ripper_init.o: $(hdrdir)/ruby/internal/compiler_since.h
+ripper_init.o: $(hdrdir)/ruby/internal/config.h
+ripper_init.o: $(hdrdir)/ruby/internal/constant_p.h
+ripper_init.o: $(hdrdir)/ruby/internal/core.h
+ripper_init.o: $(hdrdir)/ruby/internal/core/rarray.h
+ripper_init.o: $(hdrdir)/ruby/internal/core/rbasic.h
+ripper_init.o: $(hdrdir)/ruby/internal/core/rbignum.h
+ripper_init.o: $(hdrdir)/ruby/internal/core/rclass.h
+ripper_init.o: $(hdrdir)/ruby/internal/core/rdata.h
+ripper_init.o: $(hdrdir)/ruby/internal/core/rfile.h
+ripper_init.o: $(hdrdir)/ruby/internal/core/rhash.h
+ripper_init.o: $(hdrdir)/ruby/internal/core/robject.h
+ripper_init.o: $(hdrdir)/ruby/internal/core/rregexp.h
+ripper_init.o: $(hdrdir)/ruby/internal/core/rstring.h
+ripper_init.o: $(hdrdir)/ruby/internal/core/rstruct.h
+ripper_init.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
+ripper_init.o: $(hdrdir)/ruby/internal/ctype.h
+ripper_init.o: $(hdrdir)/ruby/internal/dllexport.h
+ripper_init.o: $(hdrdir)/ruby/internal/dosish.h
+ripper_init.o: $(hdrdir)/ruby/internal/encoding/coderange.h
+ripper_init.o: $(hdrdir)/ruby/internal/encoding/ctype.h
+ripper_init.o: $(hdrdir)/ruby/internal/encoding/encoding.h
+ripper_init.o: $(hdrdir)/ruby/internal/encoding/pathname.h
+ripper_init.o: $(hdrdir)/ruby/internal/encoding/re.h
+ripper_init.o: $(hdrdir)/ruby/internal/encoding/sprintf.h
+ripper_init.o: $(hdrdir)/ruby/internal/encoding/string.h
+ripper_init.o: $(hdrdir)/ruby/internal/encoding/symbol.h
+ripper_init.o: $(hdrdir)/ruby/internal/encoding/transcode.h
+ripper_init.o: $(hdrdir)/ruby/internal/error.h
+ripper_init.o: $(hdrdir)/ruby/internal/eval.h
+ripper_init.o: $(hdrdir)/ruby/internal/event.h
+ripper_init.o: $(hdrdir)/ruby/internal/fl_type.h
+ripper_init.o: $(hdrdir)/ruby/internal/gc.h
+ripper_init.o: $(hdrdir)/ruby/internal/glob.h
+ripper_init.o: $(hdrdir)/ruby/internal/globals.h
+ripper_init.o: $(hdrdir)/ruby/internal/has/attribute.h
+ripper_init.o: $(hdrdir)/ruby/internal/has/builtin.h
+ripper_init.o: $(hdrdir)/ruby/internal/has/c_attribute.h
+ripper_init.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h
+ripper_init.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h
+ripper_init.o: $(hdrdir)/ruby/internal/has/extension.h
+ripper_init.o: $(hdrdir)/ruby/internal/has/feature.h
+ripper_init.o: $(hdrdir)/ruby/internal/has/warning.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/array.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/bignum.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/class.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/compar.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/complex.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/cont.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/dir.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/enum.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/enumerator.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/error.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/eval.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/file.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/hash.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/io.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/load.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/marshal.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/numeric.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/object.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/parse.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/proc.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/process.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/random.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/range.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/rational.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/re.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/ruby.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/select.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/signal.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/sprintf.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/string.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/struct.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/thread.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/time.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/variable.h
+ripper_init.o: $(hdrdir)/ruby/internal/intern/vm.h
+ripper_init.o: $(hdrdir)/ruby/internal/interpreter.h
+ripper_init.o: $(hdrdir)/ruby/internal/iterator.h
+ripper_init.o: $(hdrdir)/ruby/internal/memory.h
+ripper_init.o: $(hdrdir)/ruby/internal/method.h
+ripper_init.o: $(hdrdir)/ruby/internal/module.h
+ripper_init.o: $(hdrdir)/ruby/internal/newobj.h
+ripper_init.o: $(hdrdir)/ruby/internal/scan_args.h
+ripper_init.o: $(hdrdir)/ruby/internal/special_consts.h
+ripper_init.o: $(hdrdir)/ruby/internal/static_assert.h
+ripper_init.o: $(hdrdir)/ruby/internal/stdalign.h
+ripper_init.o: $(hdrdir)/ruby/internal/stdbool.h
+ripper_init.o: $(hdrdir)/ruby/internal/stdckdint.h
+ripper_init.o: $(hdrdir)/ruby/internal/symbol.h
+ripper_init.o: $(hdrdir)/ruby/internal/value.h
+ripper_init.o: $(hdrdir)/ruby/internal/value_type.h
+ripper_init.o: $(hdrdir)/ruby/internal/variable.h
+ripper_init.o: $(hdrdir)/ruby/internal/warning_push.h
+ripper_init.o: $(hdrdir)/ruby/internal/xmalloc.h
+ripper_init.o: $(hdrdir)/ruby/missing.h
+ripper_init.o: $(hdrdir)/ruby/onigmo.h
+ripper_init.o: $(hdrdir)/ruby/oniguruma.h
+ripper_init.o: $(hdrdir)/ruby/ruby.h
+ripper_init.o: $(hdrdir)/ruby/st.h
+ripper_init.o: $(hdrdir)/ruby/subst.h
+ripper_init.o: $(top_srcdir)/internal.h
+ripper_init.o: $(top_srcdir)/internal/array.h
+ripper_init.o: $(top_srcdir)/internal/bignum.h
+ripper_init.o: $(top_srcdir)/internal/bits.h
+ripper_init.o: $(top_srcdir)/internal/compilers.h
+ripper_init.o: $(top_srcdir)/internal/complex.h
+ripper_init.o: $(top_srcdir)/internal/fixnum.h
+ripper_init.o: $(top_srcdir)/internal/imemo.h
+ripper_init.o: $(top_srcdir)/internal/numeric.h
+ripper_init.o: $(top_srcdir)/internal/parse.h
+ripper_init.o: $(top_srcdir)/internal/rational.h
+ripper_init.o: $(top_srcdir)/internal/ruby_parser.h
+ripper_init.o: $(top_srcdir)/internal/serial.h
+ripper_init.o: $(top_srcdir)/internal/static_assert.h
+ripper_init.o: $(top_srcdir)/internal/vm.h
+ripper_init.o: $(top_srcdir)/node.h
+ripper_init.o: $(top_srcdir)/ruby_assert.h
+ripper_init.o: $(top_srcdir)/rubyparser.h
+ripper_init.o: eventids1.h
+ripper_init.o: eventids2.h
+ripper_init.o: ripper_init.h
+ripper_init.o: {$(VPATH)}parse.h
+ripper_init.o: {$(VPATH)}ripper_init.c
# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/ripper/eventids2.c b/ext/ripper/eventids2.c
index 05687497ac..439663f0fd 100644
--- a/ext/ripper/eventids2.c
+++ b/ext/ripper/eventids2.c
@@ -1,3 +1,11 @@
+#include "ruby/ruby.h"
+#include "rubyparser.h"
+#define YYSTYPE_IS_DECLARED
+#include "parse.h"
+#include "eventids2.h"
+#include "internal.h"
+#include "internal/static_assert.h"
+
typedef struct {
ID ripper_id_backref;
ID ripper_id_backtick;
@@ -57,7 +65,9 @@ static ripper_scanner_ids_t ripper_scanner_ids;
#include "eventids2table.c"
-static void
+STATIC_ASSERT(eventids2_table_size, RIPPER_EVENTIDS2_TABLE_SIZE == sizeof(ripper_scanner_ids)/sizeof(ID));
+
+void
ripper_init_eventids2(void)
{
#define set_id2(name) ripper_scanner_ids.ripper_id_##name = rb_intern_const("on_"#name)
@@ -118,7 +128,7 @@ ripper_init_eventids2(void)
STATIC_ASSERT(k__END___range, k__END__ < SHRT_MAX);
STATIC_ASSERT(ripper_scanner_ids_size, sizeof(ripper_scanner_ids) < SHRT_MAX);
-static ID
+ID
ripper_token2eventid(enum yytokentype tok)
{
#define O(member) (int)offsetof(ripper_scanner_ids_t, ripper_id_##member)+1
diff --git a/ext/ripper/eventids2.h b/ext/ripper/eventids2.h
new file mode 100644
index 0000000000..49e46432b9
--- /dev/null
+++ b/ext/ripper/eventids2.h
@@ -0,0 +1,8 @@
+#ifndef RIPPER_EVENTIDS2
+#define RIPPER_EVENTIDS2
+
+void ripper_init_eventids2(void);
+void ripper_init_eventids2_table(VALUE self);
+ID ripper_token2eventid(enum yytokentype tok);
+
+#endif /* RIPPER_EVENTIDS2 */
diff --git a/ext/ripper/extconf.rb b/ext/ripper/extconf.rb
index 24bf8c063f..c3c56c27c5 100644
--- a/ext/ripper/extconf.rb
+++ b/ext/ripper/extconf.rb
@@ -5,8 +5,8 @@ require 'mkmf'
require 'rbconfig'
def main
- $objs = %w(ripper.o)
- $distcleanfiles.concat %w(ripper.y ripper.c eventids1.c eventids2table.c)
+ $objs = %w(eventids1.o eventids2.o ripper.o ripper_init.o)
+ $distcleanfiles.concat %w(ripper.y ripper.c eventids1.c eventids1.h eventids2table.c ripper_init.c)
$cleanfiles.concat %w(ripper.E ripper.output y.output .eventids2-check)
$defs << '-DRIPPER'
$defs << '-DRIPPER_DEBUG' if $debug
diff --git a/ext/ripper/ripper_init.c.tmpl b/ext/ripper/ripper_init.c.tmpl
new file mode 100644
index 0000000000..08a9c4860b
--- /dev/null
+++ b/ext/ripper/ripper_init.c.tmpl
@@ -0,0 +1,669 @@
+%# -*- c -*-
+#include "ruby/ruby.h"
+#include "ruby/encoding.h"
+#include "internal.h"
+#include "rubyparser.h"
+#define YYSTYPE_IS_DECLARED
+#include "parse.h"
+#include "internal/parse.h"
+#include "internal/ruby_parser.h"
+#include "node.h"
+#include "eventids1.h"
+#include "eventids2.h"
+#include "ripper_init.h"
+
+#define STR_NEW2(ptr) rb_enc_str_new((ptr),strlen(ptr),rb_ruby_parser_enc(p))
+#define RIPPER_VERSION "0.1.0"
+
+ID id_warn, id_warning, id_gets, id_assoc;
+
+enum lex_type {
+ lex_type_str,
+ lex_type_io,
+ lex_type_generic,
+};
+
+struct ripper {
+ rb_parser_t *p;
+ enum lex_type type;
+ union {
+ struct lex_pointer_string ptr_str;
+ VALUE val;
+ } data;
+};
+
+static void
+ripper_parser_mark2(void *ptr)
+{
+ struct ripper *r = (struct ripper*)ptr;
+ if (r->p) {
+ ripper_parser_mark(r->p);
+
+ switch (r->type) {
+ case lex_type_str:
+ rb_gc_mark(r->data.ptr_str.str);
+ break;
+ case lex_type_io:
+ rb_gc_mark(r->data.val);
+ break;
+ case lex_type_generic:
+ rb_gc_mark(r->data.val);
+ break;
+ }
+ }
+}
+
+static void
+ripper_parser_free2(void *ptr)
+{
+ struct ripper *r = (struct ripper*)ptr;
+ if (r->p) ripper_parser_free(r->p);
+ xfree(r);
+}
+
+static size_t
+ripper_parser_memsize2(const void *ptr)
+{
+ struct ripper *r = (struct ripper*)ptr;
+ return (r->p) ? ripper_parser_memsize(r->p) : 0;
+}
+
+static const rb_data_type_t parser_data_type = {
+ "ripper",
+ {
+ ripper_parser_mark2,
+ ripper_parser_free2,
+ ripper_parser_memsize2,
+ },
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+};
+
+static VALUE
+ripper_lex_get_generic(struct parser_params *p, rb_parser_input_data input, int line_count)
+{
+ VALUE src = (VALUE)input;
+ VALUE line = rb_funcallv_public(src, id_gets, 0, 0);
+ if (!NIL_P(line) && !RB_TYPE_P(line, T_STRING)) {
+ rb_raise(rb_eTypeError,
+ "gets returned %"PRIsVALUE" (expected String or nil)",
+ rb_obj_class(line));
+ }
+ return line;
+}
+
+void
+ripper_compile_error(struct parser_params *p, const char *fmt, ...)
+{
+ VALUE str;
+ va_list args;
+
+ va_start(args, fmt);
+ str = rb_vsprintf(fmt, args);
+ va_end(args);
+ rb_funcall(ripper_value(p), rb_intern("compile_error"), 1, str);
+ ripper_error(p);
+}
+
+static VALUE
+ripper_lex_io_get(struct parser_params *p, rb_parser_input_data input, int line_count)
+{
+ VALUE src = (VALUE)input;
+ return rb_io_gets(src);
+}
+
+static VALUE
+ripper_lex_get_str(struct parser_params *p, rb_parser_input_data input, int line_count)
+{
+ return rb_parser_lex_get_str((struct lex_pointer_string *)input);
+}
+
+static VALUE
+ripper_s_allocate(VALUE klass)
+{
+ struct ripper *r;
+
+ VALUE self = TypedData_Make_Struct(klass, struct ripper,
+ &parser_data_type, r);
+
+#ifdef UNIVERSAL_PARSER
+ const rb_parser_config_t *config = rb_ruby_parser_config();
+ r->p = rb_ripper_parser_params_allocate(config);
+#else
+ r->p = rb_ruby_ripper_parser_allocate();
+#endif
+ rb_ruby_parser_set_value(r->p, self);
+ return self;
+}
+
+static struct parser_params *
+ripper_parser_params(VALUE self, bool initialized)
+{
+ struct ripper *r;
+ struct parser_params *p;
+
+ TypedData_Get_Struct(self, struct ripper, &parser_data_type, r);
+ p = r->p;
+ if (initialized && !rb_ruby_ripper_initialized_p(p)) {
+ rb_raise(rb_eArgError, "method called for uninitialized object");
+ }
+ return p;
+}
+
+/*
+ * call-seq:
+ * ripper.error? -> Boolean
+ *
+ * Return true if parsed source has errors.
+ */
+static VALUE
+ripper_error_p(VALUE vparser)
+{
+ struct parser_params *p = ripper_parser_params(vparser, false);
+
+ return RBOOL(rb_ruby_parser_error_p(p));
+}
+
+/*
+ * call-seq:
+ * ripper.end_seen? -> Boolean
+ *
+ * Return true if parsed source ended by +\_\_END\_\_+.
+ */
+static VALUE
+ripper_parser_end_seen_p(VALUE vparser)
+{
+ struct parser_params *p = ripper_parser_params(vparser, false);
+
+ return RBOOL(rb_ruby_parser_end_seen_p(p));
+}
+
+/*
+ * call-seq:
+ * ripper.encoding -> encoding
+ *
+ * Return encoding of the source.
+ */
+static VALUE
+ripper_parser_encoding(VALUE vparser)
+{
+ struct parser_params *p = ripper_parser_params(vparser, false);
+
+ return rb_enc_from_encoding(rb_ruby_parser_encoding(p));
+}
+
+/*
+ * call-seq:
+ * ripper.yydebug -> true or false
+ *
+ * Get yydebug.
+ */
+static VALUE
+ripper_parser_get_yydebug(VALUE self)
+{
+ struct parser_params *p = ripper_parser_params(self, false);
+
+ return RBOOL(rb_ruby_parser_get_yydebug(p));
+}
+
+/*
+ * call-seq:
+ * ripper.yydebug = flag
+ *
+ * Set yydebug.
+ */
+static VALUE
+ripper_parser_set_yydebug(VALUE self, VALUE flag)
+{
+ struct parser_params *p = ripper_parser_params(self, false);
+
+ rb_ruby_parser_set_yydebug(p, RTEST(flag));
+ return flag;
+}
+
+/*
+ * call-seq:
+ * ripper.debug_output -> obj
+ *
+ * Get debug output.
+ */
+static VALUE
+ripper_parser_get_debug_output(VALUE self)
+{
+ struct parser_params *p = ripper_parser_params(self, false);
+
+ return rb_ruby_parser_debug_output(p);
+}
+
+/*
+ * call-seq:
+ * ripper.debug_output = obj
+ *
+ * Set debug output.
+ */
+static VALUE
+ripper_parser_set_debug_output(VALUE self, VALUE output)
+{
+ struct parser_params *p = ripper_parser_params(self, false);
+
+ rb_ruby_parser_set_debug_output(p, output);
+ return output;
+}
+
+#ifdef UNIVERSAL_PARSER
+struct dedent_string_arg {
+ struct parser_params *p;
+ VALUE input;
+ VALUE width;
+};
+
+static VALUE
+parser_dedent_string0(VALUE a)
+{
+ struct dedent_string_arg *arg = (void *)a;
+ int wid, col;
+
+ StringValue(arg->input);
+ wid = NUM2UINT(arg->width);
+ col = rb_ruby_ripper_dedent_string(arg->p, arg->input, wid);
+ return INT2NUM(col);
+}
+
+static VALUE
+parser_free(VALUE a)
+{
+ struct parser_params *p = (void *)a;
+
+ rb_ruby_parser_free(p);
+ return Qnil;
+}
+#endif
+
+/*
+ * call-seq:
+ * Ripper.dedent_string(input, width) -> Integer
+ *
+ * USE OF RIPPER LIBRARY ONLY.
+ *
+ * Strips up to +width+ leading whitespaces from +input+,
+ * and returns the stripped column width.
+ */
+#ifdef UNIVERSAL_PARSER
+static VALUE
+parser_dedent_string(VALUE self, VALUE input, VALUE width)
+{
+ struct parser_params *p;
+ struct dedent_string_arg args;
+
+ p = rb_parser_params_new();
+
+ args.p = p;
+ args.input = input;
+ args.width = width;
+ return rb_ensure(parser_dedent_string0, (VALUE)&args, parser_free, (VALUE)p);
+}
+#else
+static VALUE
+parser_dedent_string(VALUE self, VALUE input, VALUE width)
+{
+ int wid, col;
+
+ StringValue(input);
+ wid = NUM2UINT(width);
+ col = rb_ruby_ripper_dedent_string(0, input, wid);
+ return INT2NUM(col);
+}
+#endif
+
+/*
+ * call-seq:
+ * Ripper.new(src, filename="(ripper)", lineno=1) -> ripper
+ *
+ * Create a new Ripper object.
+ * _src_ must be a String, an IO, or an Object which has #gets method.
+ *
+ * This method does not starts parsing.
+ * See also Ripper#parse and Ripper.parse.
+ */
+static VALUE
+ripper_initialize(int argc, VALUE *argv, VALUE self)
+{
+ struct ripper *r;
+ struct parser_params *p;
+ VALUE src, fname, lineno;
+ rb_parser_lex_gets_func *gets;
+ VALUE sourcefile_string;
+ const char *sourcefile;
+ int sourceline;
+ rb_parser_input_data input;
+
+ p = ripper_parser_params(self, false);
+ TypedData_Get_Struct(self, struct ripper, &parser_data_type, r);
+ rb_scan_args(argc, argv, "12", &src, &fname, &lineno);
+ if (RB_TYPE_P(src, T_FILE)) {
+ gets = ripper_lex_io_get;
+ r->type = lex_type_io;
+ r->data.val = src;
+ input = (rb_parser_input_data)src;
+ }
+ else if (rb_respond_to(src, id_gets)) {
+ gets = ripper_lex_get_generic;
+ r->type = lex_type_generic;
+ r->data.val = src;
+ input = (rb_parser_input_data)src;
+ }
+ else {
+ StringValue(src);
+ gets = ripper_lex_get_str;
+ r->type = lex_type_str;
+ r->data.ptr_str.str = src;
+ r->data.ptr_str.ptr = 0;
+ input = (rb_parser_input_data)&r->data.ptr_str;
+ }
+ if (NIL_P(fname)) {
+ fname = STR_NEW2("(ripper)");
+ OBJ_FREEZE(fname);
+ }
+ else {
+ StringValueCStr(fname);
+ fname = rb_str_new_frozen(fname);
+ }
+ rb_ruby_ripper_parser_initialize(p);
+
+ sourcefile_string = fname;
+ sourcefile = RSTRING_PTR(fname);
+ sourceline = NIL_P(lineno) ? 0 : NUM2INT(lineno) - 1;
+
+ rb_ruby_parser_ripper_initialize(p, gets, input, sourcefile_string, sourcefile, sourceline);
+
+ return Qnil;
+}
+
+static VALUE
+ripper_parse0(VALUE vparser)
+{
+ struct parser_params *p = ripper_parser_params(vparser, false);
+
+ rb_ruby_ripper_parse0(p);
+ return rb_ruby_parser_result(p);
+}
+
+static VALUE
+ripper_ensure(VALUE vparser)
+{
+ struct parser_params *p = ripper_parser_params(vparser, false);
+
+ rb_ruby_parser_set_parsing_thread(p, Qnil);
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * ripper.parse
+ *
+ * Start parsing and returns the value of the root action.
+ */
+static VALUE
+ripper_parse(VALUE self)
+{
+ struct parser_params *p = ripper_parser_params(self, true);
+ VALUE result;
+
+ if (!NIL_P(rb_ruby_parser_parsing_thread(p))) {
+ if (rb_ruby_parser_parsing_thread(p) == rb_thread_current())
+ rb_raise(rb_eArgError, "Ripper#parse is not reentrant");
+ else
+ rb_raise(rb_eArgError, "Ripper#parse is not multithread-safe");
+ }
+ rb_ruby_parser_set_parsing_thread(p, rb_thread_current());
+ result = rb_ensure(ripper_parse0, self, ripper_ensure, self);
+ RB_GC_GUARD(self);
+
+ return result;
+}
+
+/*
+ * call-seq:
+ * ripper.column -> Integer
+ *
+ * Return column number of current parsing line.
+ * This number starts from 0.
+ */
+static VALUE
+ripper_column(VALUE self)
+{
+ struct parser_params *p = ripper_parser_params(self, true);
+ long col;
+
+ if (NIL_P(rb_ruby_parser_parsing_thread(p))) return Qnil;
+ col = rb_ruby_ripper_column(p);
+ return LONG2NUM(col);
+}
+
+/*
+ * call-seq:
+ * ripper.filename -> String
+ *
+ * Return current parsing filename.
+ */
+static VALUE
+ripper_filename(VALUE self)
+{
+ struct parser_params *p = ripper_parser_params(self, true);
+
+ return rb_ruby_parser_ruby_sourcefile_string(p);
+}
+
+/*
+ * call-seq:
+ * ripper.lineno -> Integer
+ *
+ * Return line number of current parsing line.
+ * This number starts from 1.
+ */
+static VALUE
+ripper_lineno(VALUE self)
+{
+ struct parser_params *p = ripper_parser_params(self, true);
+
+ if (NIL_P(rb_ruby_parser_parsing_thread(p))) return Qnil;
+ return INT2NUM(rb_ruby_parser_ruby_sourceline(p));
+}
+
+/*
+ * call-seq:
+ * ripper.state -> Integer
+ *
+ * Return scanner state of current token.
+ */
+static VALUE
+ripper_state(VALUE self)
+{
+ struct parser_params *p = ripper_parser_params(self, true);
+
+ if (NIL_P(rb_ruby_parser_parsing_thread(p))) return Qnil;
+ return INT2NUM(rb_ruby_parser_lex_state(p));
+}
+
+/*
+ * call-seq:
+ * ripper.token -> String
+ *
+ * Return the current token string.
+ */
+static VALUE
+ripper_token(VALUE self)
+{
+ struct parser_params *p = ripper_parser_params(self, true);
+ long pos, len;
+ VALUE str;
+
+ if (NIL_P(rb_ruby_parser_parsing_thread(p))) return Qnil;
+ pos = rb_ruby_ripper_column(p);
+ len = rb_ruby_ripper_token_len(p);
+ str = rb_str_new_parser_string(rb_ruby_ripper_lex_lastline(p));
+ return rb_str_subseq(str, pos, len);
+}
+
+#ifdef RIPPER_DEBUG
+/* :nodoc: */
+static VALUE
+ripper_assert_Qundef(VALUE self, VALUE obj, VALUE msg)
+{
+ StringValue(msg);
+ if (UNDEF_P(obj)) {
+ rb_raise(rb_eArgError, "%"PRIsVALUE, msg);
+ }
+ return Qnil;
+}
+
+/* :nodoc: */
+static VALUE
+ripper_raw_value(VALUE self, VALUE obj)
+{
+ return ULONG2NUM(obj);
+}
+
+/* :nodoc: */
+static VALUE
+ripper_validate_object(VALUE self, VALUE x)
+{
+ if (x == Qfalse) return x;
+ if (x == Qtrue) return x;
+ if (NIL_P(x)) return x;
+ if (UNDEF_P(x))
+ rb_raise(rb_eArgError, "Qundef given");
+ if (FIXNUM_P(x)) return x;
+ if (SYMBOL_P(x)) return x;
+ switch (BUILTIN_TYPE(x)) {
+ case T_STRING:
+ case T_OBJECT:
+ case T_ARRAY:
+ case T_BIGNUM:
+ case T_FLOAT:
+ case T_COMPLEX:
+ case T_RATIONAL:
+ break;
+ default:
+ rb_raise(rb_eArgError, "wrong type of ruby object: %p (%s)",
+ (void *)x, rb_obj_classname(x));
+ }
+ if (!RBASIC_CLASS(x)) {
+ rb_raise(rb_eArgError, "hidden ruby object: %p (%s)",
+ (void *)x, rb_builtin_type_name(TYPE(x)));
+ }
+ return x;
+}
+#endif
+
+#ifdef UNIVERSAL_PARSER
+struct lex_state_name_arg {
+ struct parser_params *p;
+ VALUE state;
+};
+
+static VALUE
+lex_state_name0(VALUE a)
+{
+ struct lex_state_name_arg *arg = (void *)a;
+
+ return rb_ruby_ripper_lex_state_name(arg->p, NUM2INT(arg->state));
+}
+#endif
+
+/*
+ * call-seq:
+ * Ripper.lex_state_name(integer) -> string
+ *
+ * Returns a string representation of lex_state.
+ */
+#ifdef UNIVERSAL_PARSER
+static VALUE
+ripper_lex_state_name(VALUE self, VALUE state)
+{
+ struct parser_params *p;
+ struct lex_state_name_arg args;
+
+ p = rb_parser_params_new();
+
+ args.p = p;
+ args.state = state;
+
+ return rb_ensure(lex_state_name0, (VALUE)&args, parser_free, (VALUE)p);
+}
+#else
+static VALUE
+ripper_lex_state_name(VALUE self, VALUE state)
+{
+ return rb_ruby_ripper_lex_state_name(0, NUM2INT(state));
+}
+#endif
+
+void
+Init_ripper(void)
+{
+ ripper_init_eventids1();
+ ripper_init_eventids2();
+ id_warn = rb_intern_const("warn");
+ id_warning = rb_intern_const("warning");
+ id_gets = rb_intern_const("gets");
+ id_assoc = rb_intern_const("=>");
+
+ InitVM(ripper);
+}
+
+void
+InitVM_ripper(void)
+{
+ VALUE Ripper;
+
+ Ripper = rb_define_class("Ripper", rb_cObject);
+ /* version of Ripper */
+ rb_define_const(Ripper, "Version", rb_usascii_str_new2(RIPPER_VERSION));
+ rb_define_alloc_func(Ripper, ripper_s_allocate);
+ rb_define_method(Ripper, "initialize", ripper_initialize, -1);
+ rb_define_method(Ripper, "parse", ripper_parse, 0);
+ rb_define_method(Ripper, "column", ripper_column, 0);
+ rb_define_method(Ripper, "filename", ripper_filename, 0);
+ rb_define_method(Ripper, "lineno", ripper_lineno, 0);
+ rb_define_method(Ripper, "state", ripper_state, 0);
+ rb_define_method(Ripper, "token", ripper_token, 0);
+ rb_define_method(Ripper, "end_seen?", ripper_parser_end_seen_p, 0);
+ rb_define_method(Ripper, "encoding", ripper_parser_encoding, 0);
+ rb_define_method(Ripper, "yydebug", ripper_parser_get_yydebug, 0);
+ rb_define_method(Ripper, "yydebug=", ripper_parser_set_yydebug, 1);
+ rb_define_method(Ripper, "debug_output", ripper_parser_get_debug_output, 0);
+ rb_define_method(Ripper, "debug_output=", ripper_parser_set_debug_output, 1);
+ rb_define_method(Ripper, "error?", ripper_error_p, 0);
+#ifdef RIPPER_DEBUG
+ rb_define_method(Ripper, "assert_Qundef", ripper_assert_Qundef, 2);
+ rb_define_method(Ripper, "rawVALUE", ripper_raw_value, 1);
+ rb_define_method(Ripper, "validate_object", ripper_validate_object, 1);
+#endif
+
+ rb_define_singleton_method(Ripper, "dedent_string", parser_dedent_string, 2);
+ rb_define_private_method(Ripper, "dedent_string", parser_dedent_string, 2);
+
+ rb_define_singleton_method(Ripper, "lex_state_name", ripper_lex_state_name, 1);
+
+<% @exprs.each do |expr, desc| -%>
+ /* <%=desc%> */
+ rb_define_const(Ripper, "<%=expr%>", INT2NUM(<%=expr%>));
+<% end %>
+ ripper_init_eventids1_table(Ripper);
+ ripper_init_eventids2_table(Ripper);
+
+# if 0
+ /* Hack to let RDoc document SCRIPT_LINES__ */
+
+ /*
+ * When a Hash is assigned to +SCRIPT_LINES__+ the contents of files loaded
+ * after the assignment will be added as an Array of lines with the file
+ * name as the key.
+ */
+ rb_define_global_const("SCRIPT_LINES__", Qnil);
+#endif
+ rb_ripper_none = rb_obj_alloc(rb_cObject);
+ rb_obj_freeze(rb_ripper_none);
+ rb_gc_register_mark_object(rb_ripper_none);
+
+}
diff --git a/ext/ripper/ripper_init.h b/ext/ripper/ripper_init.h
new file mode 100644
index 0000000000..664bb7bce3
--- /dev/null
+++ b/ext/ripper/ripper_init.h
@@ -0,0 +1,7 @@
+#ifndef RIPPER_INIT_H
+#define RIPPER_INIT_H
+
+extern VALUE rb_ripper_none;
+PRINTF_ARGS(void ripper_compile_error(struct parser_params*, const char *fmt, ...), 2, 3);
+
+#endif /* RIPPER_INIT_H */
diff --git a/ext/ripper/tools/dsl.rb b/ext/ripper/tools/dsl.rb
index 49ff51711f..d0002d1ec3 100644
--- a/ext/ripper/tools/dsl.rb
+++ b/ext/ripper/tools/dsl.rb
@@ -1,37 +1,52 @@
+# frozen_string_literal: true
+
# Simple DSL implementation for Ripper code generation
#
-# input: /*% ripper: stmts_add(stmts_new, void_stmt) %*/
+# input: /*% ripper: stmts_add!(stmts_new!, void_stmt!) %*/
# output:
# VALUE v1, v2;
# v1 = dispatch0(stmts_new);
# v2 = dispatch0(void_stmt);
# $$ = dispatch2(stmts_add, v1, v2);
-
-$dollar = "$$"
-alias $$ $dollar
+#
+# - The code must be a single line.
+#
+# - The code is basically Ruby code, even if it appears like in C and
+# the result will be processed as C. e.g., comments need to be in
+# Ruby style.
class DSL
- def initialize(code, options)
+ TAG_PATTERN = /(?><[a-zA-Z0-9_]+>)/.source
+ NAME_PATTERN = /(?>\$|\d+|[a-zA-Z_][a-zA-Z0-9_]*|\[[a-zA-Z_.][-a-zA-Z0-9_.]*\])(?>(?:\.|->)[a-zA-Z_][a-zA-Z0-9_]*)*/.source
+ NOT_REF_PATTERN = /(?>\#.*|[^\"$@]*|"(?>\\.|[^\"])*")/.source
+
+ def self.line?(line, lineno = nil)
+ if %r</\*% *ripper(?:\[(.*?)\])?: *(.*?) *%\*/> =~ line
+ new($2, $1&.split(",") || [], lineno)
+ end
+ end
+
+ def initialize(code, options, lineno = nil)
+ @lineno = lineno
@events = {}
@error = options.include?("error")
@brace = options.include?("brace")
if options.include?("final")
@final = "p->result"
else
- @final = (options.grep(/\A\$(?:\$|\d+)\z/)[0] || "$$")
+ @final = (options.grep(/\A\$#{NAME_PATTERN}\z/o)[0] || "p->s_lvalue")
end
@vars = 0
- # create $1 == "$1", $2 == "$2", ...
- s = (1..20).map {|n| "$#{n}"}
- re = Array.new(s.size, "([^\0]+)")
- /#{re.join("\0")}/ =~ s.join("\0")
-
# struct parser_params *p
p = p = "p"
- @code = ""
+ @code = +""
+ code = code.gsub(%r[\G#{NOT_REF_PATTERN}\K(\$|\$:|@)#{TAG_PATTERN}?#{NAME_PATTERN}]o, '"\&"')
@last_value = eval(code)
+ rescue SyntaxError
+ $stderr.puts "error on line #{@lineno}" if @lineno
+ raise
end
attr_reader :events
@@ -62,11 +77,15 @@ class DSL
vars = []
args.each do |arg|
vars << v = new_var
- @code << "#{ v }=#{ arg };"
+ if arg =~ /\A\$:#{NAME_PATTERN}\z/
+ @code << "#{ v }=get_value(#{arg});"
+ else
+ @code << "#{ v }=#{ arg };"
+ end
end
v = new_var
d = "dispatch#{ args.size }(#{ [event, *vars].join(",") })"
- d = "#{ vars.last }==Qundef ? #{ vars.first } : #{ d }" if qundef_check
+ d = "#{ vars.last }==rb_ripper_none ? #{ vars.first } : #{ d }" if qundef_check
@code << "#{ v }=#{ d };"
v
end
@@ -85,4 +104,3 @@ class DSL
name
end
end
-
diff --git a/ext/ripper/tools/generate.rb b/ext/ripper/tools/generate.rb
index 883e6ef2df..92ced37f04 100644
--- a/ext/ripper/tools/generate.rb
+++ b/ext/ripper/tools/generate.rb
@@ -11,7 +11,7 @@ def main
parser = @parser = OptionParser.new
parser.banner = "Usage: #{File.basename($0)} --mode=MODE [--ids1src=PATH] [--ids2src=PATH] [--output=PATH]"
- parser.on('--mode=MODE', 'check, eventids1, or eventids2table.') {|m|
+ parser.on('--mode=MODE', 'check, eventids1_h, eventids1, or eventids2table.') {|m|
mode = m
}
parser.on('--ids1src=PATH', 'A source file of event-IDs 1 (parse.y).') {|path|
@@ -45,6 +45,9 @@ def main
abort "event crash: #{common.join(' ')}"
end
exit 0
+ when 'eventids1_h'
+ usage 'no --ids1src' unless ids1src
+ result = generate_eventids1_h(read_ids1(ids1src))
when 'eventids1'
usage 'no --ids1src' unless ids1src
result = generate_eventids1(read_ids1(ids1src))
@@ -67,19 +70,35 @@ def usage(msg)
exit false
end
-def generate_eventids1(ids)
+def generate_eventids1_h(ids)
buf = "".dup
- buf << %Q[static struct {\n]
+ buf << %Q[#ifndef RIPPER_EVENTIDS1\n]
+ buf << %Q[#define RIPPER_EVENTIDS1\n]
+ buf << %Q[\n]
+ buf << %Q[void ripper_init_eventids1(void);\n]
+ buf << %Q[void ripper_init_eventids1_table(VALUE self);\n]
+ buf << %Q[\n]
+ buf << %Q[struct ripper_parser_ids {\n]
ids.each do |id, arity|
buf << %Q[ ID id_#{id};\n]
end
- buf << %Q[} ripper_parser_ids;\n]
+ buf << %Q[};\n]
buf << %Q[\n]
ids.each do |id, arity|
buf << %Q[#define ripper_id_#{id} ripper_parser_ids.id_#{id}\n]
end
+ buf << %Q[#endif /* RIPPER_EVENTIDS1 */\n]
+ buf << %Q[\n]
+end
+
+def generate_eventids1(ids)
+ buf = "".dup
+ buf << %Q[#include "ruby/ruby.h"\n]
+ buf << %Q[#include "eventids1.h"\n]
buf << %Q[\n]
- buf << %Q[static void\n]
+ buf << %Q[struct ripper_parser_ids ripper_parser_ids;\n]
+ buf << %Q[\n]
+ buf << %Q[void\n]
buf << %Q[ripper_init_eventids1(void)\n]
buf << %Q[{\n]
buf << %Q[#define set_id1(name) ripper_id_##name = rb_intern_const("on_"#name)\n]
@@ -88,7 +107,9 @@ def generate_eventids1(ids)
end
buf << %Q[}\n]
buf << %Q[\n]
- buf << %Q[static void\n]
+ buf << %Q[#define intern_sym(name) ID2SYM(rb_intern_const(name))\n]
+ buf << %Q[\n]
+ buf << %Q[void\n]
buf << %Q[ripper_init_eventids1_table(VALUE self)\n]
buf << %Q[{\n]
buf << %Q[ VALUE h = rb_hash_new();\n]
@@ -102,7 +123,11 @@ end
def generate_eventids2_table(ids)
buf = "".dup
- buf << %Q[static void\n]
+ buf << %Q[#include "ruby/ruby.h"\n]
+ buf << %Q[\n]
+ buf << %Q[#define intern_sym(name) ID2SYM(rb_intern_const(name))\n]
+ buf << %Q[\n]
+ buf << %Q[void\n]
buf << %Q[ripper_init_eventids2_table(VALUE self)\n]
buf << %Q[{\n]
buf << %Q[ VALUE h = rb_hash_new();\n]
@@ -111,6 +136,8 @@ def generate_eventids2_table(ids)
buf << %Q[ rb_hash_aset(h, intern_sym("#{id}"), INT2FIX(1));\n]
end
buf << %Q[}\n]
+ buf << %Q[\n]
+ buf << %Q[#define RIPPER_EVENTIDS2_TABLE_SIZE #{ids.size}\n]
buf
end
@@ -146,9 +173,7 @@ def read_ids1_with_locations(path)
line.scan(/\bdispatch(\d)\((\w+)/) do |arity, event|
(h[event] ||= []).push [f.lineno, arity.to_i]
end
- if line =~ %r</\*% *ripper(?:\[(.*?)\])?: *(.*?) *%\*/>
- gen = DSL.new($2, ($1 || "").split(","))
- gen.generate
+ if gen = DSL.line?(line, f.lineno)
gen.events.each do |event, arity|
(h[event] ||= []).push [f.lineno, arity.to_i]
end
diff --git a/ext/ripper/tools/preproc.rb b/ext/ripper/tools/preproc.rb
index f419fc3dbe..a92be93d5b 100644
--- a/ext/ripper/tools/preproc.rb
+++ b/ext/ripper/tools/preproc.rb
@@ -5,11 +5,15 @@ require 'optparse'
def main
output = nil
+ template = nil
parser = OptionParser.new
- parser.banner = "Usage: #{File.basename($0)} [--output=PATH] <parse.y>"
+ parser.banner = "Usage: #{File.basename($0)} [--output=PATH] [--template=PATH] <parse.y>"
parser.on('--output=PATH', 'An output file.') {|path|
output = path
}
+ parser.on('--template=PATH', 'An template file.') {|path|
+ template = path
+ }
parser.on('--help', 'Prints this message and quit.') {
puts parser.help
exit true
@@ -25,13 +29,13 @@ def main
unless ARGV.size == 2
abort "wrong number of arguments (#{ARGV.size} for 2)"
end
- process STDIN, out, ARGV[1]
+ process STDIN, out, ARGV[1], template
else
unless ARGV.size == 1
abort "wrong number of arguments (#{ARGV.size} for 1)"
end
File.open(ARGV[0]) {|f|
- process f, out, ARGV[0]
+ process f, out, ARGV[0], template
}
end
if output
@@ -41,34 +45,32 @@ def main
end
end
-def process(f, out, path)
+def process(f, out, path, template)
prelude f, out
grammar f, out
- usercode f, out, path
+ usercode f, out, path, template
end
-def prelude(f, out)
- @exprs = {}
- lex_state_def = false
+require_relative 'dsl'
+
+def generate_line(f, out)
while line = f.gets
- case line
- when /\A%%/
+ case
+ when gen = DSL.line?(line)
+ out << gen.generate << "\n"
+ when line.start_with?("%%")
out << "%%\n"
- return
- when /\A%token/, /\A} <node>/
- out << line.sub(/<\w+>/, '<val>')
- when /\A%type/
- out << line.sub(/<\w+>/, '<val>')
- when /^enum lex_state_(?:bits|e) \{/
- lex_state_def = true
- out << line
- when /^\}/
- lex_state_def = false
- out << line
+ break
else
- out << line
+ out << yield(line)
end
- if lex_state_def
+ end
+end
+
+def prelude(f, out)
+ @exprs = {}
+ generate_line(f, out) do |line|
+ if (/^enum lex_state_(?:bits|e) \{/ =~ line)..(/^\}/ =~ line)
case line
when /^\s*(EXPR_\w+),\s+\/\*(.+)\*\//
@exprs[$1.chomp("_bit")] = $2.strip
@@ -78,37 +80,44 @@ def prelude(f, out)
@exprs[name] = "equals to " + (val.start_with?("(") ? "<tt>#{val}</tt>" : "+#{val}+")
end
end
+ line
end
end
-require_relative "dsl"
-
def grammar(f, out)
- while line = f.gets
+ generate_line(f, out) do |line|
case line
- when %r</\*% *ripper(?:\[(.*?)\])?: *(.*?) *%\*/>
- out << DSL.new($2, ($1 || "").split(",")).generate << "\n"
when %r</\*%%%\*/>
- out << "#if 0\n"
+ "#if 0\n"
when %r</\*%>
- out << "#endif\n"
+ "#endif\n"
when %r<%\*/>
- out << "\n"
- when /\A%%/
- out << "%%\n"
- return
+ "\n"
else
- out << line
+ line
end
end
end
-def usercode(f, out, path)
+def usercode(f, out, path, template)
require 'erb'
+ lineno = nil
+ src = nil
compiler = ERB::Compiler.new('%-')
compiler.put_cmd = compiler.insert_cmd = "out.<<"
- lineno = f.lineno
- src, = compiler.compile(f.read)
+
+ if template
+ File.open(template) do |f|
+ out.clear
+ lineno = f.lineno
+ src, = compiler.compile(f.read)
+ path = template
+ end
+ else
+ lineno = f.lineno
+ src, = compiler.compile(f.read)
+ end
+
eval(src, binding, path, lineno)
end
diff --git a/ext/socket/ancdata.c b/ext/socket/ancdata.c
index 7406177de2..6ef040b692 100644
--- a/ext/socket/ancdata.c
+++ b/ext/socket/ancdata.c
@@ -1555,6 +1555,10 @@ bsock_recvmsg_internal(VALUE sock,
ss = rb_recvmsg(fptr->fd, &mh, flags);
+ if (ss == 0 && !rsock_is_dgram(fptr)) {
+ return Qnil;
+ }
+
if (ss == -1) {
int e;
if (!nonblock && rb_io_maybe_wait_readable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT)) {
diff --git a/ext/socket/depend b/ext/socket/depend
index 016b6cfc90..750bb0734f 100644
--- a/ext/socket/depend
+++ b/ext/socket/depend
@@ -170,6 +170,7 @@ ancdata.o: $(hdrdir)/ruby/internal/special_consts.h
ancdata.o: $(hdrdir)/ruby/internal/static_assert.h
ancdata.o: $(hdrdir)/ruby/internal/stdalign.h
ancdata.o: $(hdrdir)/ruby/internal/stdbool.h
+ancdata.o: $(hdrdir)/ruby/internal/stdckdint.h
ancdata.o: $(hdrdir)/ruby/internal/symbol.h
ancdata.o: $(hdrdir)/ruby/internal/value.h
ancdata.o: $(hdrdir)/ruby/internal/value_type.h
@@ -186,6 +187,7 @@ ancdata.o: $(hdrdir)/ruby/subst.h
ancdata.o: $(hdrdir)/ruby/thread.h
ancdata.o: $(hdrdir)/ruby/thread_native.h
ancdata.o: $(hdrdir)/ruby/util.h
+ancdata.o: $(hdrdir)/ruby/version.h
ancdata.o: $(top_srcdir)/ccan/check_type/check_type.h
ancdata.o: $(top_srcdir)/ccan/container_of/container_of.h
ancdata.o: $(top_srcdir)/ccan/list/list.h
@@ -198,6 +200,7 @@ ancdata.o: $(top_srcdir)/internal/error.h
ancdata.o: $(top_srcdir)/internal/gc.h
ancdata.o: $(top_srcdir)/internal/imemo.h
ancdata.o: $(top_srcdir)/internal/io.h
+ancdata.o: $(top_srcdir)/internal/sanitizers.h
ancdata.o: $(top_srcdir)/internal/serial.h
ancdata.o: $(top_srcdir)/internal/static_assert.h
ancdata.o: $(top_srcdir)/internal/string.h
@@ -208,6 +211,7 @@ ancdata.o: $(top_srcdir)/method.h
ancdata.o: $(top_srcdir)/node.h
ancdata.o: $(top_srcdir)/ruby_assert.h
ancdata.o: $(top_srcdir)/ruby_atomic.h
+ancdata.o: $(top_srcdir)/rubyparser.h
ancdata.o: $(top_srcdir)/shape.h
ancdata.o: $(top_srcdir)/thread_pthread.h
ancdata.o: $(top_srcdir)/vm_core.h
@@ -377,6 +381,7 @@ basicsocket.o: $(hdrdir)/ruby/internal/special_consts.h
basicsocket.o: $(hdrdir)/ruby/internal/static_assert.h
basicsocket.o: $(hdrdir)/ruby/internal/stdalign.h
basicsocket.o: $(hdrdir)/ruby/internal/stdbool.h
+basicsocket.o: $(hdrdir)/ruby/internal/stdckdint.h
basicsocket.o: $(hdrdir)/ruby/internal/symbol.h
basicsocket.o: $(hdrdir)/ruby/internal/value.h
basicsocket.o: $(hdrdir)/ruby/internal/value_type.h
@@ -393,6 +398,7 @@ basicsocket.o: $(hdrdir)/ruby/subst.h
basicsocket.o: $(hdrdir)/ruby/thread.h
basicsocket.o: $(hdrdir)/ruby/thread_native.h
basicsocket.o: $(hdrdir)/ruby/util.h
+basicsocket.o: $(hdrdir)/ruby/version.h
basicsocket.o: $(top_srcdir)/ccan/check_type/check_type.h
basicsocket.o: $(top_srcdir)/ccan/container_of/container_of.h
basicsocket.o: $(top_srcdir)/ccan/list/list.h
@@ -405,6 +411,7 @@ basicsocket.o: $(top_srcdir)/internal/error.h
basicsocket.o: $(top_srcdir)/internal/gc.h
basicsocket.o: $(top_srcdir)/internal/imemo.h
basicsocket.o: $(top_srcdir)/internal/io.h
+basicsocket.o: $(top_srcdir)/internal/sanitizers.h
basicsocket.o: $(top_srcdir)/internal/serial.h
basicsocket.o: $(top_srcdir)/internal/static_assert.h
basicsocket.o: $(top_srcdir)/internal/string.h
@@ -415,6 +422,7 @@ basicsocket.o: $(top_srcdir)/method.h
basicsocket.o: $(top_srcdir)/node.h
basicsocket.o: $(top_srcdir)/ruby_assert.h
basicsocket.o: $(top_srcdir)/ruby_atomic.h
+basicsocket.o: $(top_srcdir)/rubyparser.h
basicsocket.o: $(top_srcdir)/shape.h
basicsocket.o: $(top_srcdir)/thread_pthread.h
basicsocket.o: $(top_srcdir)/vm_core.h
@@ -584,6 +592,7 @@ constants.o: $(hdrdir)/ruby/internal/special_consts.h
constants.o: $(hdrdir)/ruby/internal/static_assert.h
constants.o: $(hdrdir)/ruby/internal/stdalign.h
constants.o: $(hdrdir)/ruby/internal/stdbool.h
+constants.o: $(hdrdir)/ruby/internal/stdckdint.h
constants.o: $(hdrdir)/ruby/internal/symbol.h
constants.o: $(hdrdir)/ruby/internal/value.h
constants.o: $(hdrdir)/ruby/internal/value_type.h
@@ -600,6 +609,7 @@ constants.o: $(hdrdir)/ruby/subst.h
constants.o: $(hdrdir)/ruby/thread.h
constants.o: $(hdrdir)/ruby/thread_native.h
constants.o: $(hdrdir)/ruby/util.h
+constants.o: $(hdrdir)/ruby/version.h
constants.o: $(top_srcdir)/ccan/check_type/check_type.h
constants.o: $(top_srcdir)/ccan/container_of/container_of.h
constants.o: $(top_srcdir)/ccan/list/list.h
@@ -612,6 +622,7 @@ constants.o: $(top_srcdir)/internal/error.h
constants.o: $(top_srcdir)/internal/gc.h
constants.o: $(top_srcdir)/internal/imemo.h
constants.o: $(top_srcdir)/internal/io.h
+constants.o: $(top_srcdir)/internal/sanitizers.h
constants.o: $(top_srcdir)/internal/serial.h
constants.o: $(top_srcdir)/internal/static_assert.h
constants.o: $(top_srcdir)/internal/string.h
@@ -622,6 +633,7 @@ constants.o: $(top_srcdir)/method.h
constants.o: $(top_srcdir)/node.h
constants.o: $(top_srcdir)/ruby_assert.h
constants.o: $(top_srcdir)/ruby_atomic.h
+constants.o: $(top_srcdir)/rubyparser.h
constants.o: $(top_srcdir)/shape.h
constants.o: $(top_srcdir)/thread_pthread.h
constants.o: $(top_srcdir)/vm_core.h
@@ -792,6 +804,7 @@ ifaddr.o: $(hdrdir)/ruby/internal/special_consts.h
ifaddr.o: $(hdrdir)/ruby/internal/static_assert.h
ifaddr.o: $(hdrdir)/ruby/internal/stdalign.h
ifaddr.o: $(hdrdir)/ruby/internal/stdbool.h
+ifaddr.o: $(hdrdir)/ruby/internal/stdckdint.h
ifaddr.o: $(hdrdir)/ruby/internal/symbol.h
ifaddr.o: $(hdrdir)/ruby/internal/value.h
ifaddr.o: $(hdrdir)/ruby/internal/value_type.h
@@ -808,6 +821,7 @@ ifaddr.o: $(hdrdir)/ruby/subst.h
ifaddr.o: $(hdrdir)/ruby/thread.h
ifaddr.o: $(hdrdir)/ruby/thread_native.h
ifaddr.o: $(hdrdir)/ruby/util.h
+ifaddr.o: $(hdrdir)/ruby/version.h
ifaddr.o: $(top_srcdir)/ccan/check_type/check_type.h
ifaddr.o: $(top_srcdir)/ccan/container_of/container_of.h
ifaddr.o: $(top_srcdir)/ccan/list/list.h
@@ -820,6 +834,7 @@ ifaddr.o: $(top_srcdir)/internal/error.h
ifaddr.o: $(top_srcdir)/internal/gc.h
ifaddr.o: $(top_srcdir)/internal/imemo.h
ifaddr.o: $(top_srcdir)/internal/io.h
+ifaddr.o: $(top_srcdir)/internal/sanitizers.h
ifaddr.o: $(top_srcdir)/internal/serial.h
ifaddr.o: $(top_srcdir)/internal/static_assert.h
ifaddr.o: $(top_srcdir)/internal/string.h
@@ -830,6 +845,7 @@ ifaddr.o: $(top_srcdir)/method.h
ifaddr.o: $(top_srcdir)/node.h
ifaddr.o: $(top_srcdir)/ruby_assert.h
ifaddr.o: $(top_srcdir)/ruby_atomic.h
+ifaddr.o: $(top_srcdir)/rubyparser.h
ifaddr.o: $(top_srcdir)/shape.h
ifaddr.o: $(top_srcdir)/thread_pthread.h
ifaddr.o: $(top_srcdir)/vm_core.h
@@ -999,6 +1015,7 @@ init.o: $(hdrdir)/ruby/internal/special_consts.h
init.o: $(hdrdir)/ruby/internal/static_assert.h
init.o: $(hdrdir)/ruby/internal/stdalign.h
init.o: $(hdrdir)/ruby/internal/stdbool.h
+init.o: $(hdrdir)/ruby/internal/stdckdint.h
init.o: $(hdrdir)/ruby/internal/symbol.h
init.o: $(hdrdir)/ruby/internal/value.h
init.o: $(hdrdir)/ruby/internal/value_type.h
@@ -1015,6 +1032,7 @@ init.o: $(hdrdir)/ruby/subst.h
init.o: $(hdrdir)/ruby/thread.h
init.o: $(hdrdir)/ruby/thread_native.h
init.o: $(hdrdir)/ruby/util.h
+init.o: $(hdrdir)/ruby/version.h
init.o: $(top_srcdir)/ccan/check_type/check_type.h
init.o: $(top_srcdir)/ccan/container_of/container_of.h
init.o: $(top_srcdir)/ccan/list/list.h
@@ -1027,6 +1045,7 @@ init.o: $(top_srcdir)/internal/error.h
init.o: $(top_srcdir)/internal/gc.h
init.o: $(top_srcdir)/internal/imemo.h
init.o: $(top_srcdir)/internal/io.h
+init.o: $(top_srcdir)/internal/sanitizers.h
init.o: $(top_srcdir)/internal/serial.h
init.o: $(top_srcdir)/internal/static_assert.h
init.o: $(top_srcdir)/internal/string.h
@@ -1037,6 +1056,7 @@ init.o: $(top_srcdir)/method.h
init.o: $(top_srcdir)/node.h
init.o: $(top_srcdir)/ruby_assert.h
init.o: $(top_srcdir)/ruby_atomic.h
+init.o: $(top_srcdir)/rubyparser.h
init.o: $(top_srcdir)/shape.h
init.o: $(top_srcdir)/thread_pthread.h
init.o: $(top_srcdir)/vm_core.h
@@ -1206,6 +1226,7 @@ ipsocket.o: $(hdrdir)/ruby/internal/special_consts.h
ipsocket.o: $(hdrdir)/ruby/internal/static_assert.h
ipsocket.o: $(hdrdir)/ruby/internal/stdalign.h
ipsocket.o: $(hdrdir)/ruby/internal/stdbool.h
+ipsocket.o: $(hdrdir)/ruby/internal/stdckdint.h
ipsocket.o: $(hdrdir)/ruby/internal/symbol.h
ipsocket.o: $(hdrdir)/ruby/internal/value.h
ipsocket.o: $(hdrdir)/ruby/internal/value_type.h
@@ -1222,6 +1243,7 @@ ipsocket.o: $(hdrdir)/ruby/subst.h
ipsocket.o: $(hdrdir)/ruby/thread.h
ipsocket.o: $(hdrdir)/ruby/thread_native.h
ipsocket.o: $(hdrdir)/ruby/util.h
+ipsocket.o: $(hdrdir)/ruby/version.h
ipsocket.o: $(top_srcdir)/ccan/check_type/check_type.h
ipsocket.o: $(top_srcdir)/ccan/container_of/container_of.h
ipsocket.o: $(top_srcdir)/ccan/list/list.h
@@ -1234,6 +1256,7 @@ ipsocket.o: $(top_srcdir)/internal/error.h
ipsocket.o: $(top_srcdir)/internal/gc.h
ipsocket.o: $(top_srcdir)/internal/imemo.h
ipsocket.o: $(top_srcdir)/internal/io.h
+ipsocket.o: $(top_srcdir)/internal/sanitizers.h
ipsocket.o: $(top_srcdir)/internal/serial.h
ipsocket.o: $(top_srcdir)/internal/static_assert.h
ipsocket.o: $(top_srcdir)/internal/string.h
@@ -1244,6 +1267,7 @@ ipsocket.o: $(top_srcdir)/method.h
ipsocket.o: $(top_srcdir)/node.h
ipsocket.o: $(top_srcdir)/ruby_assert.h
ipsocket.o: $(top_srcdir)/ruby_atomic.h
+ipsocket.o: $(top_srcdir)/rubyparser.h
ipsocket.o: $(top_srcdir)/shape.h
ipsocket.o: $(top_srcdir)/thread_pthread.h
ipsocket.o: $(top_srcdir)/vm_core.h
@@ -1413,6 +1437,7 @@ option.o: $(hdrdir)/ruby/internal/special_consts.h
option.o: $(hdrdir)/ruby/internal/static_assert.h
option.o: $(hdrdir)/ruby/internal/stdalign.h
option.o: $(hdrdir)/ruby/internal/stdbool.h
+option.o: $(hdrdir)/ruby/internal/stdckdint.h
option.o: $(hdrdir)/ruby/internal/symbol.h
option.o: $(hdrdir)/ruby/internal/value.h
option.o: $(hdrdir)/ruby/internal/value_type.h
@@ -1429,6 +1454,7 @@ option.o: $(hdrdir)/ruby/subst.h
option.o: $(hdrdir)/ruby/thread.h
option.o: $(hdrdir)/ruby/thread_native.h
option.o: $(hdrdir)/ruby/util.h
+option.o: $(hdrdir)/ruby/version.h
option.o: $(top_srcdir)/ccan/check_type/check_type.h
option.o: $(top_srcdir)/ccan/container_of/container_of.h
option.o: $(top_srcdir)/ccan/list/list.h
@@ -1441,6 +1467,7 @@ option.o: $(top_srcdir)/internal/error.h
option.o: $(top_srcdir)/internal/gc.h
option.o: $(top_srcdir)/internal/imemo.h
option.o: $(top_srcdir)/internal/io.h
+option.o: $(top_srcdir)/internal/sanitizers.h
option.o: $(top_srcdir)/internal/serial.h
option.o: $(top_srcdir)/internal/static_assert.h
option.o: $(top_srcdir)/internal/string.h
@@ -1451,6 +1478,7 @@ option.o: $(top_srcdir)/method.h
option.o: $(top_srcdir)/node.h
option.o: $(top_srcdir)/ruby_assert.h
option.o: $(top_srcdir)/ruby_atomic.h
+option.o: $(top_srcdir)/rubyparser.h
option.o: $(top_srcdir)/shape.h
option.o: $(top_srcdir)/thread_pthread.h
option.o: $(top_srcdir)/vm_core.h
@@ -1620,6 +1648,7 @@ raddrinfo.o: $(hdrdir)/ruby/internal/special_consts.h
raddrinfo.o: $(hdrdir)/ruby/internal/static_assert.h
raddrinfo.o: $(hdrdir)/ruby/internal/stdalign.h
raddrinfo.o: $(hdrdir)/ruby/internal/stdbool.h
+raddrinfo.o: $(hdrdir)/ruby/internal/stdckdint.h
raddrinfo.o: $(hdrdir)/ruby/internal/symbol.h
raddrinfo.o: $(hdrdir)/ruby/internal/value.h
raddrinfo.o: $(hdrdir)/ruby/internal/value_type.h
@@ -1636,6 +1665,7 @@ raddrinfo.o: $(hdrdir)/ruby/subst.h
raddrinfo.o: $(hdrdir)/ruby/thread.h
raddrinfo.o: $(hdrdir)/ruby/thread_native.h
raddrinfo.o: $(hdrdir)/ruby/util.h
+raddrinfo.o: $(hdrdir)/ruby/version.h
raddrinfo.o: $(top_srcdir)/ccan/check_type/check_type.h
raddrinfo.o: $(top_srcdir)/ccan/container_of/container_of.h
raddrinfo.o: $(top_srcdir)/ccan/list/list.h
@@ -1648,6 +1678,7 @@ raddrinfo.o: $(top_srcdir)/internal/error.h
raddrinfo.o: $(top_srcdir)/internal/gc.h
raddrinfo.o: $(top_srcdir)/internal/imemo.h
raddrinfo.o: $(top_srcdir)/internal/io.h
+raddrinfo.o: $(top_srcdir)/internal/sanitizers.h
raddrinfo.o: $(top_srcdir)/internal/serial.h
raddrinfo.o: $(top_srcdir)/internal/static_assert.h
raddrinfo.o: $(top_srcdir)/internal/string.h
@@ -1658,6 +1689,7 @@ raddrinfo.o: $(top_srcdir)/method.h
raddrinfo.o: $(top_srcdir)/node.h
raddrinfo.o: $(top_srcdir)/ruby_assert.h
raddrinfo.o: $(top_srcdir)/ruby_atomic.h
+raddrinfo.o: $(top_srcdir)/rubyparser.h
raddrinfo.o: $(top_srcdir)/shape.h
raddrinfo.o: $(top_srcdir)/thread_pthread.h
raddrinfo.o: $(top_srcdir)/vm_core.h
@@ -1827,6 +1859,7 @@ socket.o: $(hdrdir)/ruby/internal/special_consts.h
socket.o: $(hdrdir)/ruby/internal/static_assert.h
socket.o: $(hdrdir)/ruby/internal/stdalign.h
socket.o: $(hdrdir)/ruby/internal/stdbool.h
+socket.o: $(hdrdir)/ruby/internal/stdckdint.h
socket.o: $(hdrdir)/ruby/internal/symbol.h
socket.o: $(hdrdir)/ruby/internal/value.h
socket.o: $(hdrdir)/ruby/internal/value_type.h
@@ -1843,6 +1876,7 @@ socket.o: $(hdrdir)/ruby/subst.h
socket.o: $(hdrdir)/ruby/thread.h
socket.o: $(hdrdir)/ruby/thread_native.h
socket.o: $(hdrdir)/ruby/util.h
+socket.o: $(hdrdir)/ruby/version.h
socket.o: $(top_srcdir)/ccan/check_type/check_type.h
socket.o: $(top_srcdir)/ccan/container_of/container_of.h
socket.o: $(top_srcdir)/ccan/list/list.h
@@ -1855,6 +1889,7 @@ socket.o: $(top_srcdir)/internal/error.h
socket.o: $(top_srcdir)/internal/gc.h
socket.o: $(top_srcdir)/internal/imemo.h
socket.o: $(top_srcdir)/internal/io.h
+socket.o: $(top_srcdir)/internal/sanitizers.h
socket.o: $(top_srcdir)/internal/serial.h
socket.o: $(top_srcdir)/internal/static_assert.h
socket.o: $(top_srcdir)/internal/string.h
@@ -1865,6 +1900,7 @@ socket.o: $(top_srcdir)/method.h
socket.o: $(top_srcdir)/node.h
socket.o: $(top_srcdir)/ruby_assert.h
socket.o: $(top_srcdir)/ruby_atomic.h
+socket.o: $(top_srcdir)/rubyparser.h
socket.o: $(top_srcdir)/shape.h
socket.o: $(top_srcdir)/thread_pthread.h
socket.o: $(top_srcdir)/vm_core.h
@@ -2034,6 +2070,7 @@ sockssocket.o: $(hdrdir)/ruby/internal/special_consts.h
sockssocket.o: $(hdrdir)/ruby/internal/static_assert.h
sockssocket.o: $(hdrdir)/ruby/internal/stdalign.h
sockssocket.o: $(hdrdir)/ruby/internal/stdbool.h
+sockssocket.o: $(hdrdir)/ruby/internal/stdckdint.h
sockssocket.o: $(hdrdir)/ruby/internal/symbol.h
sockssocket.o: $(hdrdir)/ruby/internal/value.h
sockssocket.o: $(hdrdir)/ruby/internal/value_type.h
@@ -2050,6 +2087,7 @@ sockssocket.o: $(hdrdir)/ruby/subst.h
sockssocket.o: $(hdrdir)/ruby/thread.h
sockssocket.o: $(hdrdir)/ruby/thread_native.h
sockssocket.o: $(hdrdir)/ruby/util.h
+sockssocket.o: $(hdrdir)/ruby/version.h
sockssocket.o: $(top_srcdir)/ccan/check_type/check_type.h
sockssocket.o: $(top_srcdir)/ccan/container_of/container_of.h
sockssocket.o: $(top_srcdir)/ccan/list/list.h
@@ -2062,6 +2100,7 @@ sockssocket.o: $(top_srcdir)/internal/error.h
sockssocket.o: $(top_srcdir)/internal/gc.h
sockssocket.o: $(top_srcdir)/internal/imemo.h
sockssocket.o: $(top_srcdir)/internal/io.h
+sockssocket.o: $(top_srcdir)/internal/sanitizers.h
sockssocket.o: $(top_srcdir)/internal/serial.h
sockssocket.o: $(top_srcdir)/internal/static_assert.h
sockssocket.o: $(top_srcdir)/internal/string.h
@@ -2072,6 +2111,7 @@ sockssocket.o: $(top_srcdir)/method.h
sockssocket.o: $(top_srcdir)/node.h
sockssocket.o: $(top_srcdir)/ruby_assert.h
sockssocket.o: $(top_srcdir)/ruby_atomic.h
+sockssocket.o: $(top_srcdir)/rubyparser.h
sockssocket.o: $(top_srcdir)/shape.h
sockssocket.o: $(top_srcdir)/thread_pthread.h
sockssocket.o: $(top_srcdir)/vm_core.h
@@ -2241,6 +2281,7 @@ tcpserver.o: $(hdrdir)/ruby/internal/special_consts.h
tcpserver.o: $(hdrdir)/ruby/internal/static_assert.h
tcpserver.o: $(hdrdir)/ruby/internal/stdalign.h
tcpserver.o: $(hdrdir)/ruby/internal/stdbool.h
+tcpserver.o: $(hdrdir)/ruby/internal/stdckdint.h
tcpserver.o: $(hdrdir)/ruby/internal/symbol.h
tcpserver.o: $(hdrdir)/ruby/internal/value.h
tcpserver.o: $(hdrdir)/ruby/internal/value_type.h
@@ -2257,6 +2298,7 @@ tcpserver.o: $(hdrdir)/ruby/subst.h
tcpserver.o: $(hdrdir)/ruby/thread.h
tcpserver.o: $(hdrdir)/ruby/thread_native.h
tcpserver.o: $(hdrdir)/ruby/util.h
+tcpserver.o: $(hdrdir)/ruby/version.h
tcpserver.o: $(top_srcdir)/ccan/check_type/check_type.h
tcpserver.o: $(top_srcdir)/ccan/container_of/container_of.h
tcpserver.o: $(top_srcdir)/ccan/list/list.h
@@ -2269,6 +2311,7 @@ tcpserver.o: $(top_srcdir)/internal/error.h
tcpserver.o: $(top_srcdir)/internal/gc.h
tcpserver.o: $(top_srcdir)/internal/imemo.h
tcpserver.o: $(top_srcdir)/internal/io.h
+tcpserver.o: $(top_srcdir)/internal/sanitizers.h
tcpserver.o: $(top_srcdir)/internal/serial.h
tcpserver.o: $(top_srcdir)/internal/static_assert.h
tcpserver.o: $(top_srcdir)/internal/string.h
@@ -2279,6 +2322,7 @@ tcpserver.o: $(top_srcdir)/method.h
tcpserver.o: $(top_srcdir)/node.h
tcpserver.o: $(top_srcdir)/ruby_assert.h
tcpserver.o: $(top_srcdir)/ruby_atomic.h
+tcpserver.o: $(top_srcdir)/rubyparser.h
tcpserver.o: $(top_srcdir)/shape.h
tcpserver.o: $(top_srcdir)/thread_pthread.h
tcpserver.o: $(top_srcdir)/vm_core.h
@@ -2448,6 +2492,7 @@ tcpsocket.o: $(hdrdir)/ruby/internal/special_consts.h
tcpsocket.o: $(hdrdir)/ruby/internal/static_assert.h
tcpsocket.o: $(hdrdir)/ruby/internal/stdalign.h
tcpsocket.o: $(hdrdir)/ruby/internal/stdbool.h
+tcpsocket.o: $(hdrdir)/ruby/internal/stdckdint.h
tcpsocket.o: $(hdrdir)/ruby/internal/symbol.h
tcpsocket.o: $(hdrdir)/ruby/internal/value.h
tcpsocket.o: $(hdrdir)/ruby/internal/value_type.h
@@ -2464,6 +2509,7 @@ tcpsocket.o: $(hdrdir)/ruby/subst.h
tcpsocket.o: $(hdrdir)/ruby/thread.h
tcpsocket.o: $(hdrdir)/ruby/thread_native.h
tcpsocket.o: $(hdrdir)/ruby/util.h
+tcpsocket.o: $(hdrdir)/ruby/version.h
tcpsocket.o: $(top_srcdir)/ccan/check_type/check_type.h
tcpsocket.o: $(top_srcdir)/ccan/container_of/container_of.h
tcpsocket.o: $(top_srcdir)/ccan/list/list.h
@@ -2476,6 +2522,7 @@ tcpsocket.o: $(top_srcdir)/internal/error.h
tcpsocket.o: $(top_srcdir)/internal/gc.h
tcpsocket.o: $(top_srcdir)/internal/imemo.h
tcpsocket.o: $(top_srcdir)/internal/io.h
+tcpsocket.o: $(top_srcdir)/internal/sanitizers.h
tcpsocket.o: $(top_srcdir)/internal/serial.h
tcpsocket.o: $(top_srcdir)/internal/static_assert.h
tcpsocket.o: $(top_srcdir)/internal/string.h
@@ -2486,6 +2533,7 @@ tcpsocket.o: $(top_srcdir)/method.h
tcpsocket.o: $(top_srcdir)/node.h
tcpsocket.o: $(top_srcdir)/ruby_assert.h
tcpsocket.o: $(top_srcdir)/ruby_atomic.h
+tcpsocket.o: $(top_srcdir)/rubyparser.h
tcpsocket.o: $(top_srcdir)/shape.h
tcpsocket.o: $(top_srcdir)/thread_pthread.h
tcpsocket.o: $(top_srcdir)/vm_core.h
@@ -2655,6 +2703,7 @@ udpsocket.o: $(hdrdir)/ruby/internal/special_consts.h
udpsocket.o: $(hdrdir)/ruby/internal/static_assert.h
udpsocket.o: $(hdrdir)/ruby/internal/stdalign.h
udpsocket.o: $(hdrdir)/ruby/internal/stdbool.h
+udpsocket.o: $(hdrdir)/ruby/internal/stdckdint.h
udpsocket.o: $(hdrdir)/ruby/internal/symbol.h
udpsocket.o: $(hdrdir)/ruby/internal/value.h
udpsocket.o: $(hdrdir)/ruby/internal/value_type.h
@@ -2671,6 +2720,7 @@ udpsocket.o: $(hdrdir)/ruby/subst.h
udpsocket.o: $(hdrdir)/ruby/thread.h
udpsocket.o: $(hdrdir)/ruby/thread_native.h
udpsocket.o: $(hdrdir)/ruby/util.h
+udpsocket.o: $(hdrdir)/ruby/version.h
udpsocket.o: $(top_srcdir)/ccan/check_type/check_type.h
udpsocket.o: $(top_srcdir)/ccan/container_of/container_of.h
udpsocket.o: $(top_srcdir)/ccan/list/list.h
@@ -2683,6 +2733,7 @@ udpsocket.o: $(top_srcdir)/internal/error.h
udpsocket.o: $(top_srcdir)/internal/gc.h
udpsocket.o: $(top_srcdir)/internal/imemo.h
udpsocket.o: $(top_srcdir)/internal/io.h
+udpsocket.o: $(top_srcdir)/internal/sanitizers.h
udpsocket.o: $(top_srcdir)/internal/serial.h
udpsocket.o: $(top_srcdir)/internal/static_assert.h
udpsocket.o: $(top_srcdir)/internal/string.h
@@ -2693,6 +2744,7 @@ udpsocket.o: $(top_srcdir)/method.h
udpsocket.o: $(top_srcdir)/node.h
udpsocket.o: $(top_srcdir)/ruby_assert.h
udpsocket.o: $(top_srcdir)/ruby_atomic.h
+udpsocket.o: $(top_srcdir)/rubyparser.h
udpsocket.o: $(top_srcdir)/shape.h
udpsocket.o: $(top_srcdir)/thread_pthread.h
udpsocket.o: $(top_srcdir)/vm_core.h
@@ -2862,6 +2914,7 @@ unixserver.o: $(hdrdir)/ruby/internal/special_consts.h
unixserver.o: $(hdrdir)/ruby/internal/static_assert.h
unixserver.o: $(hdrdir)/ruby/internal/stdalign.h
unixserver.o: $(hdrdir)/ruby/internal/stdbool.h
+unixserver.o: $(hdrdir)/ruby/internal/stdckdint.h
unixserver.o: $(hdrdir)/ruby/internal/symbol.h
unixserver.o: $(hdrdir)/ruby/internal/value.h
unixserver.o: $(hdrdir)/ruby/internal/value_type.h
@@ -2878,6 +2931,7 @@ unixserver.o: $(hdrdir)/ruby/subst.h
unixserver.o: $(hdrdir)/ruby/thread.h
unixserver.o: $(hdrdir)/ruby/thread_native.h
unixserver.o: $(hdrdir)/ruby/util.h
+unixserver.o: $(hdrdir)/ruby/version.h
unixserver.o: $(top_srcdir)/ccan/check_type/check_type.h
unixserver.o: $(top_srcdir)/ccan/container_of/container_of.h
unixserver.o: $(top_srcdir)/ccan/list/list.h
@@ -2890,6 +2944,7 @@ unixserver.o: $(top_srcdir)/internal/error.h
unixserver.o: $(top_srcdir)/internal/gc.h
unixserver.o: $(top_srcdir)/internal/imemo.h
unixserver.o: $(top_srcdir)/internal/io.h
+unixserver.o: $(top_srcdir)/internal/sanitizers.h
unixserver.o: $(top_srcdir)/internal/serial.h
unixserver.o: $(top_srcdir)/internal/static_assert.h
unixserver.o: $(top_srcdir)/internal/string.h
@@ -2900,6 +2955,7 @@ unixserver.o: $(top_srcdir)/method.h
unixserver.o: $(top_srcdir)/node.h
unixserver.o: $(top_srcdir)/ruby_assert.h
unixserver.o: $(top_srcdir)/ruby_atomic.h
+unixserver.o: $(top_srcdir)/rubyparser.h
unixserver.o: $(top_srcdir)/shape.h
unixserver.o: $(top_srcdir)/thread_pthread.h
unixserver.o: $(top_srcdir)/vm_core.h
@@ -3069,6 +3125,7 @@ unixsocket.o: $(hdrdir)/ruby/internal/special_consts.h
unixsocket.o: $(hdrdir)/ruby/internal/static_assert.h
unixsocket.o: $(hdrdir)/ruby/internal/stdalign.h
unixsocket.o: $(hdrdir)/ruby/internal/stdbool.h
+unixsocket.o: $(hdrdir)/ruby/internal/stdckdint.h
unixsocket.o: $(hdrdir)/ruby/internal/symbol.h
unixsocket.o: $(hdrdir)/ruby/internal/value.h
unixsocket.o: $(hdrdir)/ruby/internal/value_type.h
@@ -3085,6 +3142,7 @@ unixsocket.o: $(hdrdir)/ruby/subst.h
unixsocket.o: $(hdrdir)/ruby/thread.h
unixsocket.o: $(hdrdir)/ruby/thread_native.h
unixsocket.o: $(hdrdir)/ruby/util.h
+unixsocket.o: $(hdrdir)/ruby/version.h
unixsocket.o: $(top_srcdir)/ccan/check_type/check_type.h
unixsocket.o: $(top_srcdir)/ccan/container_of/container_of.h
unixsocket.o: $(top_srcdir)/ccan/list/list.h
@@ -3097,6 +3155,7 @@ unixsocket.o: $(top_srcdir)/internal/error.h
unixsocket.o: $(top_srcdir)/internal/gc.h
unixsocket.o: $(top_srcdir)/internal/imemo.h
unixsocket.o: $(top_srcdir)/internal/io.h
+unixsocket.o: $(top_srcdir)/internal/sanitizers.h
unixsocket.o: $(top_srcdir)/internal/serial.h
unixsocket.o: $(top_srcdir)/internal/static_assert.h
unixsocket.o: $(top_srcdir)/internal/string.h
@@ -3107,6 +3166,7 @@ unixsocket.o: $(top_srcdir)/method.h
unixsocket.o: $(top_srcdir)/node.h
unixsocket.o: $(top_srcdir)/ruby_assert.h
unixsocket.o: $(top_srcdir)/ruby_atomic.h
+unixsocket.o: $(top_srcdir)/rubyparser.h
unixsocket.o: $(top_srcdir)/shape.h
unixsocket.o: $(top_srcdir)/thread_pthread.h
unixsocket.o: $(top_srcdir)/vm_core.h
diff --git a/ext/socket/extconf.rb b/ext/socket/extconf.rb
index 93bd850c34..d44ce31b0a 100644
--- a/ext/socket/extconf.rb
+++ b/ext/socket/extconf.rb
@@ -327,6 +327,8 @@ end
net/if_dl.h
arpa/nameser.h
resolv.h
+ pthread.h
+ sched.h
].each {|h|
if have_header(h, headers)
headers << h
@@ -700,6 +702,9 @@ SRC
end
end
+ have_func("pthread_create")
+ have_func("pthread_detach")
+
$VPATH << '$(topdir)' << '$(top_srcdir)'
create_makefile("socket")
end
diff --git a/ext/socket/getaddrinfo.c b/ext/socket/getaddrinfo.c
index 95a2feb3be..bf0d90129f 100644
--- a/ext/socket/getaddrinfo.c
+++ b/ext/socket/getaddrinfo.c
@@ -219,8 +219,7 @@ freeaddrinfo(struct addrinfo *ai)
do {
next = ai->ai_next;
- if (ai->ai_canonname)
- free(ai->ai_canonname);
+ free(ai->ai_canonname);
/* no need to free(ai->ai_addr) */
free(ai);
} while ((ai = next) != NULL);
diff --git a/ext/socket/init.c b/ext/socket/init.c
index 557d4374a5..0e312b540e 100644
--- a/ext/socket/init.c
+++ b/ext/socket/init.c
@@ -27,6 +27,7 @@ VALUE rb_cSocket;
VALUE rb_cAddrinfo;
VALUE rb_eSocket;
+VALUE rb_eResolution;
#ifdef SOCKS
VALUE rb_cSOCKSSocket;
@@ -34,9 +35,10 @@ VALUE rb_cSOCKSSocket;
int rsock_do_not_reverse_lookup = 1;
static VALUE sym_wait_readable;
+static ID id_error_code;
void
-rsock_raise_socket_error(const char *reason, int error)
+rsock_raise_resolution_error(const char *reason, int error)
{
#ifdef EAI_SYSTEM
int e;
@@ -48,10 +50,14 @@ rsock_raise_socket_error(const char *reason, int error)
VALUE msg = rb_sprintf("%s: ", reason);
if (!enc) enc = rb_default_internal_encoding();
rb_str_concat(msg, rb_w32_conv_from_wchar(gai_strerrorW(error), enc));
- rb_exc_raise(rb_exc_new_str(rb_eSocket, msg));
#else
- rb_raise(rb_eSocket, "%s: %s", reason, gai_strerror(error));
+ VALUE msg = rb_sprintf("%s: %s", reason, gai_strerror(error));
#endif
+
+ StringValue(msg);
+ VALUE self = rb_class_new_instance(1, &msg, rb_eResolution);
+ rb_ivar_set(self, id_error_code, INT2NUM(error));
+ rb_exc_raise(self);
}
#if defined __APPLE__
@@ -116,6 +122,7 @@ recvfrom_blocking(void *data)
ssize_t ret;
ret = recvfrom(arg->fd, RSTRING_PTR(arg->str), arg->length,
arg->flags, &arg->buf.addr, &arg->alen);
+
if (ret != -1 && len0 < arg->alen)
arg->alen = len0;
@@ -147,6 +154,18 @@ recvfrom_locktmp(VALUE v)
return rb_thread_io_blocking_region(recvfrom_blocking, arg, arg->fd);
}
+int
+rsock_is_dgram(rb_io_t *fptr)
+{
+ int socktype;
+ socklen_t optlen = (socklen_t)sizeof(socktype);
+ int ret = getsockopt(fptr->fd, SOL_SOCKET, SO_TYPE, (void*)&socktype, &optlen);
+ if (ret == -1) {
+ rb_sys_fail("getsockopt(SO_TYPE)");
+ }
+ return socktype == SOCK_DGRAM;
+}
+
VALUE
rsock_s_recvfrom(VALUE socket, int argc, VALUE *argv, enum sock_recv_type from)
{
@@ -185,8 +204,12 @@ rsock_s_recvfrom(VALUE socket, int argc, VALUE *argv, enum sock_recv_type from)
rb_io_wait(fptr->self, RB_INT2NUM(RUBY_IO_READABLE), Qnil);
#endif
- slen = (long)rb_str_locktmp_ensure(str, recvfrom_locktmp, (VALUE)&arg);
+ rb_str_locktmp(str);
+ slen = (long)rb_ensure(recvfrom_locktmp, (VALUE)&arg, rb_str_unlocktmp, str);
+ if (slen == 0 && !rsock_is_dgram(fptr)) {
+ return Qnil;
+ }
if (slen >= 0) break;
if (!rb_io_maybe_wait_readable(errno, socket, RUBY_IO_TIMEOUT_DEFAULT))
@@ -259,6 +282,10 @@ rsock_s_recvfrom_nonblock(VALUE sock, VALUE len, VALUE flg, VALUE str,
if (slen != -1 && len0 < alen)
alen = len0;
+ if (slen == 0 && !rsock_is_dgram(fptr)) {
+ return Qnil;
+ }
+
if (slen < 0) {
int e = errno;
switch (e) {
@@ -392,7 +419,7 @@ rsock_write_nonblock(VALUE sock, VALUE str, VALUE ex)
if (e == EWOULDBLOCK || e == EAGAIN) {
if (ex == Qfalse) return sym_wait_writable;
rb_readwrite_syserr_fail(RB_IO_WAIT_WRITABLE, e,
- "write would block");
+ "write would block");
}
rb_syserr_fail_path(e, fptr->pathv);
}
@@ -433,9 +460,9 @@ rsock_socket(int domain, int type, int proto)
fd = rsock_socket0(domain, type, proto);
if (fd < 0) {
- if (rb_gc_for_fd(errno)) {
- fd = rsock_socket0(domain, type, proto);
- }
+ if (rb_gc_for_fd(errno)) {
+ fd = rsock_socket0(domain, type, proto);
+ }
}
if (0 <= fd)
rb_update_max_fd(fd);
@@ -492,10 +519,10 @@ wait_connectable(int fd, struct timeval *timeout)
switch (sockerr) {
case 0:
- /*
- * be defensive in case some platforms set SO_ERROR on the original,
- * interrupted connect()
- */
+ /*
+ * be defensive in case some platforms set SO_ERROR on the original,
+ * interrupted connect()
+ */
/* when the connection timed out, no errno is set and revents is 0. */
if (timeout && revents == 0) {
@@ -681,9 +708,9 @@ rsock_s_accept(VALUE klass, VALUE io, struct sockaddr *sockaddr, socklen_t *len)
RB_IO_POINTER(io, fptr);
struct accept_arg accept_arg = {
- .fd = fptr->fd,
- .sockaddr = sockaddr,
- .len = len
+ .fd = fptr->fd,
+ .sockaddr = sockaddr,
+ .len = len
};
int retry = 0, peer;
@@ -730,10 +757,10 @@ rsock_getfamily(rb_io_t *fptr)
if (cached) {
switch (cached) {
#ifdef AF_UNIX
- case FMODE_UNIX: return AF_UNIX;
+ case FMODE_UNIX: return AF_UNIX;
#endif
- case FMODE_INET: return AF_INET;
- case FMODE_INET6: return AF_INET6;
+ case FMODE_INET: return AF_INET;
+ case FMODE_INET6: return AF_INET6;
}
}
@@ -752,6 +779,18 @@ rsock_getfamily(rb_io_t *fptr)
return ss.addr.sa_family;
}
+/*
+ * call-seq:
+ * error_code -> integer
+ *
+ * Returns the raw error code occurred at name resolution.
+ */
+static VALUE
+sock_resolv_error_code(VALUE self)
+{
+ return rb_attr_get(self, id_error_code);
+}
+
void
rsock_init_socket_init(void)
{
@@ -759,6 +798,11 @@ rsock_init_socket_init(void)
* SocketError is the error class for socket.
*/
rb_eSocket = rb_define_class("SocketError", rb_eStandardError);
+ /*
+ * ResolutionError is the error class for socket name resolution.
+ */
+ rb_eResolution = rb_define_class_under(rb_cSocket, "ResolutionError", rb_eSocket);
+ rb_define_method(rb_eResolution, "error_code", sock_resolv_error_code, 0);
rsock_init_ipsocket();
rsock_init_tcpsocket();
rsock_init_tcpserver();
@@ -772,6 +816,8 @@ rsock_init_socket_init(void)
rsock_init_sockifaddr();
rsock_init_socket_constants();
+ id_error_code = rb_intern_const("error_code");
+
#undef rb_intern
sym_wait_readable = ID2SYM(rb_intern("wait_readable"));
diff --git a/ext/socket/lib/socket.rb b/ext/socket/lib/socket.rb
index eecdc7d4b8..e953077fe6 100644
--- a/ext/socket/lib/socket.rb
+++ b/ext/socket/lib/socket.rb
@@ -333,9 +333,10 @@ class BasicSocket < IO
# _flags_ is zero or more of the +MSG_+ options.
# The result, _mesg_, is the data received.
#
- # When recvfrom(2) returns 0, Socket#recv_nonblock returns
- # an empty string as data.
- # The meaning depends on the socket: EOF on TCP, empty packet on UDP, etc.
+ # When recvfrom(2) returns 0, Socket#recv_nonblock returns nil.
+ # In most cases it means the connection was closed, but for UDP connections
+ # it may mean an empty packet was received, as the underlying API makes
+ # it impossible to distinguish these two cases.
#
# === Parameters
# * +maxlen+ - the number of bytes to receive from the socket
@@ -480,9 +481,10 @@ class Socket < BasicSocket
# The second element, _sender_addrinfo_, contains protocol-specific address
# information of the sender.
#
- # When recvfrom(2) returns 0, Socket#recvfrom_nonblock returns
- # an empty string as data.
- # The meaning depends on the socket: EOF on TCP, empty packet on UDP, etc.
+ # When recvfrom(2) returns 0, Socket#recv_nonblock returns nil.
+ # In most cases it means the connection was closed, but for UDP connections
+ # it may mean an empty packet was received, as the underlying API makes
+ # it impossible to distinguish these two cases.
#
# === Parameters
# * +maxlen+ - the maximum number of bytes to receive from the socket
@@ -597,6 +599,30 @@ class Socket < BasicSocket
__accept_nonblock(exception)
end
+ RESOLUTION_DELAY = 0.05
+ private_constant :RESOLUTION_DELAY
+
+ CONNECTION_ATTEMPT_DELAY = 0.25
+ private_constant :CONNECTION_ATTEMPT_DELAY
+
+ ADDRESS_FAMILIES = {
+ ipv6: Socket::AF_INET6,
+ ipv4: Socket::AF_INET
+ }.freeze
+ private_constant :ADDRESS_FAMILIES
+
+ HOSTNAME_RESOLUTION_QUEUE_UPDATED = 0
+ private_constant :HOSTNAME_RESOLUTION_QUEUE_UPDATED
+
+ IPV6_ADRESS_FORMAT = /(?i)(?:(?:[0-9A-F]{1,4}:){7}(?:[0-9A-F]{1,4}|:)|(?:[0-9A-F]{1,4}:){6}(?:[0-9A-F]{1,4}::[0-9A-F]{1,4}|:(?:[0-9A-F]{1,4}:){1,5}[0-9A-F]{1,4}|:)|(?:[0-9A-F]{1,4}:){5}(?::[0-9A-F]{1,4}::[0-9A-F]{1,4}|:(?:[0-9A-F]{1,4}:){1,4}[0-9A-F]{1,4}|:)|(?:[0-9A-F]{1,4}:){4}(?::[0-9A-F]{1,4}::[0-9A-F]{1,4}|:(?:[0-9A-F]{1,4}:){1,3}[0-9A-F]{1,4}|:)|(?:[0-9A-F]{1,4}:){3}(?::[0-9A-F]{1,4}::[0-9A-F]{1,4}|:(?:[0-9A-F]{1,4}:){1,2}[0-9A-F]{1,4}|:)|(?:[0-9A-F]{1,4}:){2}(?::[0-9A-F]{1,4}::[0-9A-F]{1,4}|:(?:[0-9A-F]{1,4}:)[0-9A-F]{1,4}|:)|(?:[0-9A-F]{1,4}:){1}(?::[0-9A-F]{1,4}::[0-9A-F]{1,4}|::(?:[0-9A-F]{1,4}:){1,5}[0-9A-F]{1,4}|:)|::(?:[0-9A-F]{1,4}::[0-9A-F]{1,4}|:(?:[0-9A-F]{1,4}:){1,6}[0-9A-F]{1,4}|:))(?:%.+)?/
+ private_constant :IPV6_ADRESS_FORMAT
+
+ @tcp_fast_fallback = true
+
+ class << self
+ attr_accessor :tcp_fast_fallback
+ end
+
# :call-seq:
# Socket.tcp(host, port, local_host=nil, local_port=nil, [opts]) {|socket| ... }
# Socket.tcp(host, port, local_host=nil, local_port=nil, [opts])
@@ -622,8 +648,491 @@ class Socket < BasicSocket
# sock.close_write
# puts sock.read
# }
- #
- def self.tcp(host, port, local_host = nil, local_port = nil, connect_timeout: nil, resolv_timeout: nil) # :yield: socket
+ def self.tcp(host, port, local_host = nil, local_port = nil, connect_timeout: nil, resolv_timeout: nil, fast_fallback: tcp_fast_fallback, &block) # :yield: socket
+ unless fast_fallback
+ return tcp_without_fast_fallback(host, port, local_host, local_port, connect_timeout:, resolv_timeout:, &block)
+ end
+
+ # Happy Eyeballs' states
+ # - :start
+ # - :v6c
+ # - :v4w
+ # - :v4c
+ # - :v46c
+ # - :v46w
+ # - :success
+ # - :failure
+ # - :timeout
+
+ specified_family_name = nil
+ hostname_resolution_threads = []
+ hostname_resolution_queue = nil
+ hostname_resolution_waiting = nil
+ hostname_resolution_expires_at = nil
+ selectable_addrinfos = SelectableAddrinfos.new
+ connecting_sockets = ConnectingSockets.new
+ local_addrinfos = []
+ connection_attempt_delay_expires_at = nil
+ connection_attempt_started_at = nil
+ state = :start
+ connected_socket = nil
+ last_error = nil
+ is_windows_environment ||= (RUBY_PLATFORM =~ /mswin|mingw|cygwin/)
+
+ ret = loop do
+ case state
+ when :start
+ specified_family_name, next_state = host && specified_family_name_and_next_state(host)
+
+ if local_host && local_port
+ specified_family_name, next_state = specified_family_name_and_next_state(local_host) unless specified_family_name
+ local_addrinfos = Addrinfo.getaddrinfo(local_host, local_port, ADDRESS_FAMILIES[specified_family_name], :STREAM, timeout: resolv_timeout)
+ end
+
+ if specified_family_name
+ addrinfos = Addrinfo.getaddrinfo(host, port, ADDRESS_FAMILIES[specified_family_name], :STREAM, timeout: resolv_timeout)
+ selectable_addrinfos.add(specified_family_name, addrinfos)
+ hostname_resolution_queue = NoHostnameResolutionQueue.new
+ state = next_state
+ next
+ end
+
+ resolving_family_names = ADDRESS_FAMILIES.keys
+ hostname_resolution_queue = HostnameResolutionQueue.new(resolving_family_names.size)
+ hostname_resolution_waiting = hostname_resolution_queue.waiting_pipe
+ hostname_resolution_started_at = current_clocktime if resolv_timeout
+ hostname_resolution_args = [host, port, hostname_resolution_queue]
+
+ hostname_resolution_threads.concat(
+ resolving_family_names.map { |family|
+ thread_args = [family, *hostname_resolution_args]
+ thread = Thread.new(*thread_args) { |*thread_args| hostname_resolution(*thread_args) }
+ Thread.pass
+ thread
+ }
+ )
+
+ hostname_resolution_retry_count = resolving_family_names.size - 1
+ hostname_resolution_expires_at = hostname_resolution_started_at + resolv_timeout if resolv_timeout
+
+ while hostname_resolution_retry_count >= 0
+ remaining = resolv_timeout ? second_to_timeout(hostname_resolution_started_at + resolv_timeout) : nil
+ hostname_resolved, _, = IO.select(hostname_resolution_waiting, nil, nil, remaining)
+
+ unless hostname_resolved
+ state = :timeout
+ break
+ end
+
+ family_name, res = hostname_resolution_queue.get
+
+ if res.is_a? Exception
+ unless (Socket.const_defined?(:EAI_ADDRFAMILY)) &&
+ (res.is_a?(Socket::ResolutionError)) &&
+ (res.error_code == Socket::EAI_ADDRFAMILY)
+ last_error = res
+ end
+
+ if hostname_resolution_retry_count.zero?
+ state = :failure
+ break
+ end
+ hostname_resolution_retry_count -= 1
+ else
+ state = case family_name
+ when :ipv6 then :v6c
+ when :ipv4 then hostname_resolution_queue.closed? ? :v4c : :v4w
+ end
+ selectable_addrinfos.add(family_name, res)
+ last_error = nil
+ break
+ end
+ end
+
+ next
+ when :v4w
+ ipv6_resolved, _, = IO.select(hostname_resolution_waiting, nil, nil, RESOLUTION_DELAY)
+
+ if ipv6_resolved
+ family_name, res = hostname_resolution_queue.get
+ selectable_addrinfos.add(family_name, res) unless res.is_a? Exception
+ state = :v46c
+ else
+ state = :v4c
+ end
+
+ next
+ when :v4c, :v6c, :v46c
+ connection_attempt_started_at = current_clocktime unless connection_attempt_started_at
+ addrinfo = selectable_addrinfos.get
+
+ if local_addrinfos.any?
+ local_addrinfo = local_addrinfos.find { |lai| lai.afamily == addrinfo.afamily }
+
+ if local_addrinfo.nil?
+ if selectable_addrinfos.empty? && connecting_sockets.empty? && hostname_resolution_queue.closed?
+ last_error = SocketError.new 'no appropriate local address'
+ state = :failure
+ elsif selectable_addrinfos.any?
+ # Try other Addrinfo in next loop
+ else
+ if resolv_timeout && hostname_resolution_queue.opened? &&
+ (current_clocktime >= hostname_resolution_expires_at)
+ state = :timeout
+ else
+ # Wait for connection to be established or hostname resolution in next loop
+ connection_attempt_delay_expires_at = nil
+ state = :v46w
+ end
+ end
+ next
+ end
+ end
+
+ connection_attempt_delay_expires_at = current_clocktime + CONNECTION_ATTEMPT_DELAY
+
+ begin
+ result = if specified_family_name && selectable_addrinfos.empty? &&
+ connecting_sockets.empty? && hostname_resolution_queue.closed?
+ local_addrinfo ?
+ addrinfo.connect_from(local_addrinfo, timeout: connect_timeout) :
+ addrinfo.connect(timeout: connect_timeout)
+ else
+ socket = Socket.new(addrinfo.pfamily, addrinfo.socktype, addrinfo.protocol)
+ socket.bind(local_addrinfo) if local_addrinfo
+ socket.connect_nonblock(addrinfo, exception: false)
+ end
+
+ case result
+ when 0
+ connected_socket = socket
+ state = :success
+ when Socket
+ connected_socket = result
+ state = :success
+ when :wait_writable
+ connecting_sockets.add(socket, addrinfo)
+ state = :v46w
+ end
+ rescue SystemCallError => e
+ last_error = e
+ socket.close if socket && !socket.closed?
+
+ if selectable_addrinfos.empty? && connecting_sockets.empty? && hostname_resolution_queue.closed?
+ state = :failure
+ elsif selectable_addrinfos.any?
+ # Try other Addrinfo in next loop
+ else
+ if resolv_timeout && hostname_resolution_queue.opened? &&
+ (current_clocktime >= hostname_resolution_expires_at)
+ state = :timeout
+ else
+ # Wait for connection to be established or hostname resolution in next loop
+ connection_attempt_delay_expires_at = nil
+ state = :v46w
+ end
+ end
+ end
+
+ next
+ when :v46w
+ if connect_timeout && second_to_timeout(connection_attempt_started_at + connect_timeout).zero?
+ state = :timeout
+ next
+ end
+
+ remaining = second_to_timeout(connection_attempt_delay_expires_at)
+ hostname_resolution_waiting = hostname_resolution_waiting && hostname_resolution_queue.closed? ? nil : hostname_resolution_waiting
+ hostname_resolved, connectable_sockets, = IO.select(hostname_resolution_waiting, connecting_sockets.all, nil, remaining)
+
+ if connectable_sockets&.any?
+ while (connectable_socket = connectable_sockets.pop)
+ is_connected =
+ if is_windows_environment
+ sockopt = connectable_socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_CONNECT_TIME)
+ sockopt.unpack('i').first >= 0
+ else
+ sockopt = connectable_socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR)
+ sockopt.int.zero?
+ end
+
+ if is_connected
+ connected_socket = connectable_socket
+ connecting_sockets.delete connectable_socket
+ connectable_sockets.each do |other_connectable_socket|
+ other_connectable_socket.close unless other_connectable_socket.closed?
+ end
+ state = :success
+ break
+ else
+ failed_ai = connecting_sockets.delete connectable_socket
+ inspected_ip_address = failed_ai.ipv6? ? "[#{failed_ai.ip_address}]" : failed_ai.ip_address
+ last_error = SystemCallError.new("connect(2) for #{inspected_ip_address}:#{failed_ai.ip_port}", sockopt.int)
+ connectable_socket.close unless connectable_socket.closed?
+
+ next if connectable_sockets.any?
+
+ if selectable_addrinfos.empty? && connecting_sockets.empty? && hostname_resolution_queue.closed?
+ state = :failure
+ elsif selectable_addrinfos.any?
+ # Wait for connection attempt delay timeout in next loop
+ else
+ if resolv_timeout && hostname_resolution_queue.opened? &&
+ (current_clocktime >= hostname_resolution_expires_at)
+ state = :timeout
+ else
+ # Wait for connection to be established or hostname resolution in next loop
+ connection_attempt_delay_expires_at = nil
+ end
+ end
+ end
+ end
+ elsif hostname_resolved&.any?
+ family_name, res = hostname_resolution_queue.get
+ selectable_addrinfos.add(family_name, res) unless res.is_a? Exception
+ else
+ if selectable_addrinfos.empty? && connecting_sockets.empty? && hostname_resolution_queue.closed?
+ state = :failure
+ elsif selectable_addrinfos.any?
+ # Try other Addrinfo in next loop
+ state = :v46c
+ else
+ if resolv_timeout && hostname_resolution_queue.opened? &&
+ (current_clocktime >= hostname_resolution_expires_at)
+ state = :timeout
+ else
+ # Wait for connection to be established or hostname resolution in next loop
+ connection_attempt_delay_expires_at = nil
+ end
+ end
+ end
+
+ next
+ when :success
+ break connected_socket
+ when :failure
+ raise last_error
+ when :timeout
+ raise Errno::ETIMEDOUT, 'user specified timeout'
+ end
+ end
+
+ if block_given?
+ begin
+ yield ret
+ ensure
+ ret.close
+ end
+ else
+ ret
+ end
+ ensure
+ if fast_fallback
+ hostname_resolution_threads.each do |thread|
+ thread&.exit
+ end
+
+ hostname_resolution_queue&.close_all
+
+ connecting_sockets.each do |connecting_socket|
+ connecting_socket.close unless connecting_socket.closed?
+ end
+ end
+ end
+
+ def self.specified_family_name_and_next_state(hostname)
+ if hostname.match?(IPV6_ADRESS_FORMAT) then [:ipv6, :v6c]
+ elsif hostname.match?(/^([0-9]{1,3}\.){3}[0-9]{1,3}$/) then [:ipv4, :v4c]
+ end
+ end
+ private_class_method :specified_family_name_and_next_state
+
+ def self.hostname_resolution(family, host, port, hostname_resolution_queue)
+ begin
+ resolved_addrinfos = Addrinfo.getaddrinfo(host, port, ADDRESS_FAMILIES[family], :STREAM)
+ hostname_resolution_queue.add_resolved(family, resolved_addrinfos)
+ rescue => e
+ hostname_resolution_queue.add_error(family, e)
+ end
+ end
+ private_class_method :hostname_resolution
+
+ def self.second_to_timeout(ends_at)
+ return 0 unless ends_at
+
+ remaining = (ends_at - current_clocktime)
+ remaining.negative? ? 0 : remaining
+ end
+ private_class_method :second_to_timeout
+
+ def self.current_clocktime
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ end
+ private_class_method :current_clocktime
+
+ class SelectableAddrinfos
+ PRIORITY_ON_V6 = [:ipv6, :ipv4]
+ PRIORITY_ON_V4 = [:ipv4, :ipv6]
+
+ def initialize
+ @addrinfo_dict = {}
+ @last_family = nil
+ end
+
+ def add(family_name, addrinfos)
+ @addrinfo_dict[family_name] = addrinfos
+ end
+
+ def get
+ return nil if empty?
+
+ if @addrinfo_dict.size == 1
+ @addrinfo_dict.each { |_, addrinfos| return addrinfos.shift }
+ end
+
+ precedences = case @last_family
+ when :ipv4, nil then PRIORITY_ON_V6
+ when :ipv6 then PRIORITY_ON_V4
+ end
+
+ precedences.each do |family_name|
+ addrinfo = @addrinfo_dict[family_name]&.shift
+ next unless addrinfo
+
+ @last_family = family_name
+ return addrinfo
+ end
+ end
+
+ def empty?
+ @addrinfo_dict.all? { |_, addrinfos| addrinfos.empty? }
+ end
+
+ def any?
+ !empty?
+ end
+ end
+ private_constant :SelectableAddrinfos
+
+ class NoHostnameResolutionQueue
+ def waiting_pipe
+ nil
+ end
+
+ def add_resolved(_, _)
+ raise StandardError, "This #{self.class} cannot respond to:"
+ end
+
+ def add_error(_, _)
+ raise StandardError, "This #{self.class} cannot respond to:"
+ end
+
+ def get
+ nil
+ end
+
+ def opened?
+ false
+ end
+
+ def closed?
+ true
+ end
+
+ def close_all
+ # Do nothing
+ end
+ end
+ private_constant :NoHostnameResolutionQueue
+
+ class HostnameResolutionQueue
+ def initialize(size)
+ @size = size
+ @taken_count = 0
+ @rpipe, @wpipe = IO.pipe
+ @queue = Queue.new
+ @mutex = Mutex.new
+ end
+
+ def waiting_pipe
+ [@rpipe]
+ end
+
+ def add_resolved(family, resolved_addrinfos)
+ @mutex.synchronize do
+ @queue.push [family, resolved_addrinfos]
+ @wpipe.putc HOSTNAME_RESOLUTION_QUEUE_UPDATED
+ end
+ end
+
+ def add_error(family, error)
+ @mutex.synchronize do
+ @queue.push [family, error]
+ @wpipe.putc HOSTNAME_RESOLUTION_QUEUE_UPDATED
+ end
+ end
+
+ def get
+ return nil if @queue.empty?
+
+ res = nil
+
+ @mutex.synchronize do
+ @rpipe.getbyte
+ res = @queue.pop
+ end
+
+ @taken_count += 1
+ close_all if @taken_count == @size
+ res
+ end
+
+ def closed?
+ @rpipe.closed?
+ end
+
+ def opened?
+ !closed?
+ end
+
+ def close_all
+ @queue.close unless @queue.closed?
+ @rpipe.close unless @rpipe.closed?
+ @wpipe.close unless @wpipe.closed?
+ end
+ end
+ private_constant :HostnameResolutionQueue
+
+ class ConnectingSockets
+ def initialize
+ @socket_dict = {}
+ end
+
+ def all
+ @socket_dict.keys
+ end
+
+ def add(socket, addrinfo)
+ @socket_dict[socket] = addrinfo
+ end
+
+ def delete(socket)
+ @socket_dict.delete socket
+ end
+
+ def empty?
+ @socket_dict.empty?
+ end
+
+ def each
+ @socket_dict.keys.each do |socket|
+ yield socket
+ end
+ end
+ end
+ private_constant :ConnectingSockets
+
+ def self.tcp_without_fast_fallback(host, port, local_host, local_port, connect_timeout:, resolv_timeout:, &block)
last_error = nil
ret = nil
@@ -667,6 +1176,7 @@ class Socket < BasicSocket
ret
end
end
+ private_class_method :tcp_without_fast_fallback
# :stopdoc:
def self.ip_sockets_port0(ai_list, reuseaddr)
@@ -1229,9 +1739,10 @@ class UDPSocket < IPSocket
# The first element of the results, _mesg_, is the data received.
# The second element, _sender_inet_addr_, is an array to represent the sender address.
#
- # When recvfrom(2) returns 0,
- # Socket#recvfrom_nonblock returns an empty string as data.
- # It means an empty packet.
+ # When recvfrom(2) returns 0, Socket#recv_nonblock returns nil.
+ # In most cases it means the connection was closed, but it may also mean
+ # an empty packet was received, as the underlying API makes
+ # it impossible to distinguish these two cases.
#
# === Parameters
# * +maxlen+ - the number of bytes to receive from the socket
diff --git a/ext/socket/mkconstants.rb b/ext/socket/mkconstants.rb
index 5e6c0668f6..4271e40cd8 100644
--- a/ext/socket/mkconstants.rb
+++ b/ext/socket/mkconstants.rb
@@ -51,7 +51,10 @@ DATA.each_line {|s|
next
end
h[name] = default_value
- COMMENTS[name] = comment
+ if comment
+ # Stop unintentional references
+ COMMENTS[name] = comment.gsub(/\b(Data|Kernel|Process|Set|Socket|Time)\b/, '\\\\\\&')
+ end
}
DEFS = h.to_a
@@ -73,15 +76,11 @@ def each_name(pat)
}
end
-erb_new = lambda do |src, safe, trim|
- if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
- ERB.new(src, trim_mode: trim)
- else
- ERB.new(src, safe, trim)
- end
+erb_new = lambda do |src, trim|
+ ERB.new(src, trim_mode: trim)
end
-erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_const_decls")
+erb_new.call(<<'EOS', '%').def_method(Object, "gen_const_decls")
% each_const {|guard, name, default_value|
#if !defined(<%=name%>)
# if defined(HAVE_CONST_<%=name.upcase%>)
@@ -95,7 +94,7 @@ erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_const_decls")
% }
EOS
-erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_const_defs_in_guard(name, default_value)")
+erb_new.call(<<'EOS', '%').def_method(Object, "gen_const_defs_in_guard(name, default_value)")
#if defined(<%=name%>)
/* <%= COMMENTS[name] %> */
rb_define_const(rb_cSocket, <%=c_str name%>, INTEGER2NUM(<%=name%>));
@@ -104,7 +103,7 @@ erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_const_defs_in_guard(name
#endif
EOS
-erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_const_defs")
+erb_new.call(<<'EOS', '%').def_method(Object, "gen_const_defs")
% each_const {|guard, name, default_value|
% if guard
#if <%=guard%>
@@ -154,7 +153,7 @@ def each_names_with_len(pat, prefix_optional=nil)
}
end
-erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_name_to_int_decl(funcname, pat, prefix_optional, guard=nil)")
+erb_new.call(<<'EOS', '%').def_method(Object, "gen_name_to_int_decl(funcname, pat, prefix_optional, guard=nil)")
%if guard
#ifdef <%=guard%>
int <%=funcname%>(const char *str, long len, int *valp);
@@ -164,7 +163,7 @@ int <%=funcname%>(const char *str, long len, int *valp);
%end
EOS
-erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_name_to_int_func_in_guard(funcname, pat, prefix_optional, guard=nil)")
+erb_new.call(<<'EOS', '%').def_method(Object, "gen_name_to_int_func_in_guard(funcname, pat, prefix_optional, guard=nil)")
int
<%=funcname%>(const char *str, long len, int *valp)
{
@@ -186,7 +185,7 @@ int
}
EOS
-erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_name_to_int_func(funcname, pat, prefix_optional, guard=nil)")
+erb_new.call(<<'EOS', '%').def_method(Object, "gen_name_to_int_func(funcname, pat, prefix_optional, guard=nil)")
%if guard
#ifdef <%=guard%>
<%=gen_name_to_int_func_in_guard(funcname, pat, prefix_optional, guard)%>
@@ -215,7 +214,7 @@ def reverse_each_name_with_prefix_optional(pat, prefix_pat)
end
end
-erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_int_to_name_hash(hash_var, pat, prefix_pat)")
+erb_new.call(<<'EOS', '%').def_method(Object, "gen_int_to_name_hash(hash_var, pat, prefix_pat)")
<%=hash_var%> = st_init_numtable();
% reverse_each_name_with_prefix_optional(pat, prefix_pat) {|n,s|
#ifdef <%=n%>
@@ -224,7 +223,7 @@ erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_int_to_name_hash(hash_va
% }
EOS
-erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_int_to_name_func(func_name, hash_var)")
+erb_new.call(<<'EOS', '%').def_method(Object, "gen_int_to_name_func(func_name, hash_var)")
ID
<%=func_name%>(int val)
{
@@ -235,7 +234,7 @@ ID
}
EOS
-erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_int_to_name_decl(func_name, hash_var)")
+erb_new.call(<<'EOS', '%').def_method(Object, "gen_int_to_name_decl(func_name, hash_var)")
ID <%=func_name%>(int val);
EOS
@@ -284,7 +283,7 @@ def_intern('rsock_intern_udp_optname', /\AUDP_/, "UDP_")
def_intern('rsock_intern_scm_optname', /\ASCM_/, "SCM_")
def_intern('rsock_intern_local_optname', /\ALOCAL_/, "LOCAL_")
-result = erb_new.call(<<'EOS', nil, '%').result(binding)
+result = erb_new.call(<<'EOS', '%').result(binding)
/* autogenerated file */
<%= INTERN_DEFS.map {|vardef, gen_hash, decl, func| vardef }.join("\n") %>
@@ -327,7 +326,7 @@ init_constants(void)
EOS
-header_result = erb_new.call(<<'EOS', nil, '%').result(binding)
+header_result = erb_new.call(<<'EOS', '%').result(binding)
/* autogenerated file */
<%= gen_const_decls %>
<%= NAME_TO_INT_DEFS.map {|decl, func| decl }.join("\n") %>
@@ -423,8 +422,8 @@ AF_ISDN nil Integrated Services Digital Network
PF_ISDN nil Integrated Services Digital Network
AF_NATM nil Native ATM access
PF_NATM nil Native ATM access
-AF_SYSTEM
-PF_SYSTEM
+AF_SYSTEM nil Kernel event messages
+PF_SYSTEM nil Kernel event messages
AF_NETBIOS nil NetBIOS
PF_NETBIOS nil NetBIOS
AF_PPP nil Point-to-Point Protocol
@@ -440,8 +439,8 @@ PF_PACKET nil Direct link-layer access
AF_E164 nil CCITT (ITU-T) E.164 recommendation
PF_XTP nil eXpress Transfer Protocol
-PF_RTIP
-PF_PIP
+PF_RTIP nil Help Identify RTIP packets
+PF_PIP nil Help Identify PIP packets
AF_KEY nil Key management protocol, originally developed for usage with IPsec
PF_KEY nil Key management protocol, originally developed for usage with IPsec
AF_NETLINK nil Kernel user interface device
@@ -666,6 +665,7 @@ SO_SETFIB nil Set the associated routing table for the socket (FreeBSD
SO_RTABLE nil Set the routing table for this socket (OpenBSD)
SO_INCOMING_CPU nil Receive the cpu attached to the socket (Linux 3.19)
SO_INCOMING_NAPI_ID nil Receive the napi ID attached to a RX queue (Linux 4.12)
+SO_CONNECT_TIME nil Returns the number of seconds a socket has been connected. This option is only valid for connection-oriented protocols (Windows)
SOPRI_INTERACTIVE nil Interactive socket priority
SOPRI_NORMAL nil Normal socket priority
@@ -745,6 +745,7 @@ SHUT_RDWR 2 Shut down the both sides of the socket
IPV6_JOIN_GROUP nil Join a group membership
IPV6_LEAVE_GROUP nil Leave a group membership
+IPV6_MTU_DISCOVER nil Path MTU discovery
IPV6_MULTICAST_HOPS nil IP6 multicast hops
IPV6_MULTICAST_IF nil IP6 multicast interface
IPV6_MULTICAST_LOOP nil IP6 multicast loopback
@@ -759,6 +760,7 @@ IPV6_NEXTHOP nil Next hop address
IPV6_PATHMTU nil Retrieve current path MTU
IPV6_PKTINFO nil Receive packet information with datagram
IPV6_RECVDSTOPTS nil Receive all IP6 options for response
+IPV6_RECVERR nil Enable extended reliable error message passing
IPV6_RECVHOPLIMIT nil Receive hop limit with datagram
IPV6_RECVHOPOPTS nil Receive hop-by-hop options
IPV6_RECVPKTINFO nil Receive destination IP address and incoming interface
diff --git a/ext/socket/raddrinfo.c b/ext/socket/raddrinfo.c
index 269edc4dad..e79bcfa332 100644
--- a/ext/socket/raddrinfo.c
+++ b/ext/socket/raddrinfo.c
@@ -10,6 +10,22 @@
#include "rubysocket.h"
+// GETADDRINFO_IMPL == 0 : call getaddrinfo/getnameinfo directly
+// GETADDRINFO_IMPL == 1 : call getaddrinfo/getnameinfo without gvl (but uncancellable)
+// GETADDRINFO_IMPL == 2 : call getaddrinfo/getnameinfo in a dedicated pthread
+// (and if the call is interrupted, the pthread is detached)
+
+#ifndef GETADDRINFO_IMPL
+# ifdef GETADDRINFO_EMU
+# define GETADDRINFO_IMPL 0
+# elif !defined(HAVE_PTHREAD_CREATE) || !defined(HAVE_PTHREAD_DETACH) || defined(__MINGW32__) || defined(__MINGW64__)
+# define GETADDRINFO_IMPL 1
+# else
+# define GETADDRINFO_IMPL 2
+# include "ruby/thread_native.h"
+# endif
+#endif
+
#if defined(INET6) && (defined(LOOKUP_ORDER_HACK_INET) || defined(LOOKUP_ORDER_HACK_INET6))
#define LOOKUP_ORDERS (sizeof(lookup_order_table) / sizeof(lookup_order_table[0]))
static const int lookup_order_table[] = {
@@ -173,32 +189,6 @@ parse_numeric_port(const char *service, int *portp)
}
#endif
-#ifndef GETADDRINFO_EMU
-struct getaddrinfo_arg
-{
- const char *node;
- const char *service;
- const struct addrinfo *hints;
- struct addrinfo **res;
-};
-
-static void *
-nogvl_getaddrinfo(void *arg)
-{
- int ret;
- struct getaddrinfo_arg *ptr = arg;
- ret = getaddrinfo(ptr->node, ptr->service, ptr->hints, ptr->res);
-#ifdef __linux__
- /* On Linux (mainly Ubuntu 13.04) /etc/nsswitch.conf has mdns4 and
- * it cause getaddrinfo to return EAI_SYSTEM/ENOENT. [ruby-list:49420]
- */
- if (ret == EAI_SYSTEM && errno == ENOENT)
- ret = EAI_NONAME;
-#endif
- return (void *)(VALUE)ret;
-}
-#endif
-
static int
numeric_getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints,
@@ -302,7 +292,263 @@ rb_freeaddrinfo(struct rb_addrinfo *ai)
xfree(ai);
}
-#ifndef GETADDRINFO_EMU
+#if GETADDRINFO_IMPL == 0
+
+static int
+rb_getaddrinfo(const char *hostp, const char *portp, const struct addrinfo *hints, struct addrinfo **ai)
+{
+ return getaddrinfo(hostp, portp, hints, ai);
+}
+
+#elif GETADDRINFO_IMPL == 1
+
+struct getaddrinfo_arg
+{
+ const char *node;
+ const char *service;
+ const struct addrinfo *hints;
+ struct addrinfo **res;
+};
+
+static void *
+nogvl_getaddrinfo(void *arg)
+{
+ int ret;
+ struct getaddrinfo_arg *ptr = arg;
+ ret = getaddrinfo(ptr->node, ptr->service, ptr->hints, ptr->res);
+#ifdef __linux__
+ /* On Linux (mainly Ubuntu 13.04) /etc/nsswitch.conf has mdns4 and
+ * it cause getaddrinfo to return EAI_SYSTEM/ENOENT. [ruby-list:49420]
+ */
+ if (ret == EAI_SYSTEM && errno == ENOENT)
+ ret = EAI_NONAME;
+#endif
+ return (void *)(VALUE)ret;
+}
+
+static int
+rb_getaddrinfo(const char *hostp, const char *portp, const struct addrinfo *hints, struct addrinfo **ai)
+{
+ struct getaddrinfo_arg arg;
+ MEMZERO(&arg, struct getaddrinfo_arg, 1);
+ arg.node = hostp;
+ arg.service = portp;
+ arg.hints = hints;
+ arg.res = ai;
+ return (int)(VALUE)rb_thread_call_without_gvl(nogvl_getaddrinfo, &arg, RUBY_UBF_IO, 0);
+}
+
+#elif GETADDRINFO_IMPL == 2
+
+struct getaddrinfo_arg
+{
+ char *node, *service;
+ struct addrinfo hints;
+ struct addrinfo *ai;
+ int err, gai_errno, refcount, done, cancelled;
+ rb_nativethread_lock_t lock;
+ rb_nativethread_cond_t cond;
+};
+
+static struct getaddrinfo_arg *
+allocate_getaddrinfo_arg(const char *hostp, const char *portp, const struct addrinfo *hints)
+{
+ size_t hostp_offset = sizeof(struct getaddrinfo_arg);
+ size_t portp_offset = hostp_offset + (hostp ? strlen(hostp) + 1 : 0);
+ size_t bufsize = portp_offset + (portp ? strlen(portp) + 1 : 0);
+
+ char *buf = malloc(bufsize);
+ if (!buf) {
+ rb_gc();
+ buf = malloc(bufsize);
+ if (!buf) return NULL;
+ }
+ struct getaddrinfo_arg *arg = (struct getaddrinfo_arg *)buf;
+
+ if (hostp) {
+ arg->node = buf + hostp_offset;
+ strcpy(arg->node, hostp);
+ }
+ else {
+ arg->node = NULL;
+ }
+
+ if (portp) {
+ arg->service = buf + portp_offset;
+ strcpy(arg->service, portp);
+ }
+ else {
+ arg->service = NULL;
+ }
+
+ arg->hints = *hints;
+ arg->ai = NULL;
+
+ arg->refcount = 2;
+ arg->done = arg->cancelled = 0;
+
+ rb_nativethread_lock_initialize(&arg->lock);
+ rb_native_cond_initialize(&arg->cond);
+
+ return arg;
+}
+
+static void
+free_getaddrinfo_arg(struct getaddrinfo_arg *arg)
+{
+ rb_native_cond_destroy(&arg->cond);
+ rb_nativethread_lock_destroy(&arg->lock);
+ free(arg);
+}
+
+static void *
+do_getaddrinfo(void *ptr)
+{
+ struct getaddrinfo_arg *arg = (struct getaddrinfo_arg *)ptr;
+
+ int err, gai_errno;
+ err = getaddrinfo(arg->node, arg->service, &arg->hints, &arg->ai);
+ gai_errno = errno;
+#ifdef __linux__
+ /* On Linux (mainly Ubuntu 13.04) /etc/nsswitch.conf has mdns4 and
+ * it cause getaddrinfo to return EAI_SYSTEM/ENOENT. [ruby-list:49420]
+ */
+ if (err == EAI_SYSTEM && errno == ENOENT)
+ err = EAI_NONAME;
+#endif
+
+ int need_free = 0;
+ rb_nativethread_lock_lock(&arg->lock);
+ {
+ arg->err = err;
+ arg->gai_errno = gai_errno;
+ if (arg->cancelled) {
+ freeaddrinfo(arg->ai);
+ }
+ else {
+ arg->done = 1;
+ rb_native_cond_signal(&arg->cond);
+ }
+ if (--arg->refcount == 0) need_free = 1;
+ }
+ rb_nativethread_lock_unlock(&arg->lock);
+
+ if (need_free) free_getaddrinfo_arg(arg);
+
+ return 0;
+}
+
+static void *
+wait_getaddrinfo(void *ptr)
+{
+ struct getaddrinfo_arg *arg = (struct getaddrinfo_arg *)ptr;
+ rb_nativethread_lock_lock(&arg->lock);
+ while (!arg->done && !arg->cancelled) {
+ rb_native_cond_wait(&arg->cond, &arg->lock);
+ }
+ rb_nativethread_lock_unlock(&arg->lock);
+ return 0;
+}
+
+static void
+cancel_getaddrinfo(void *ptr)
+{
+ struct getaddrinfo_arg *arg = (struct getaddrinfo_arg *)ptr;
+ rb_nativethread_lock_lock(&arg->lock);
+ {
+ arg->cancelled = 1;
+ rb_native_cond_signal(&arg->cond);
+ }
+ rb_nativethread_lock_unlock(&arg->lock);
+}
+
+static int
+do_pthread_create(pthread_t *th, void *(*start_routine) (void *), void *arg)
+{
+ int limit = 3, ret;
+ do {
+ // It is said that pthread_create may fail spuriously, so we follow the JDK and retry several times.
+ //
+ // https://bugs.openjdk.org/browse/JDK-8268605
+ // https://github.com/openjdk/jdk/commit/e35005d5ce383ddd108096a3079b17cb0bcf76f1
+ ret = pthread_create(th, 0, start_routine, arg);
+ } while (ret == EAGAIN && limit-- > 0);
+ return ret;
+}
+
+static int
+rb_getaddrinfo(const char *hostp, const char *portp, const struct addrinfo *hints, struct addrinfo **ai)
+{
+ int retry;
+ struct getaddrinfo_arg *arg;
+ int err, gai_errno = 0;
+
+start:
+ retry = 0;
+
+ arg = allocate_getaddrinfo_arg(hostp, portp, hints);
+ if (!arg) {
+ return EAI_MEMORY;
+ }
+
+ pthread_t th;
+ if (do_pthread_create(&th, do_getaddrinfo, arg) != 0) {
+ free_getaddrinfo_arg(arg);
+ return EAI_AGAIN;
+ }
+ pthread_detach(th);
+
+ rb_thread_call_without_gvl2(wait_getaddrinfo, arg, cancel_getaddrinfo, arg);
+
+ int need_free = 0;
+ rb_nativethread_lock_lock(&arg->lock);
+ {
+ if (arg->done) {
+ err = arg->err;
+ gai_errno = arg->gai_errno;
+ if (err == 0) *ai = arg->ai;
+ }
+ else if (arg->cancelled) {
+ err = EAI_AGAIN;
+ }
+ else {
+ // If already interrupted, rb_thread_call_without_gvl2 may return without calling wait_getaddrinfo.
+ // In this case, it could be !arg->done && !arg->cancelled.
+ arg->cancelled = 1; // to make do_getaddrinfo call freeaddrinfo
+ retry = 1;
+ }
+ if (--arg->refcount == 0) need_free = 1;
+ }
+ rb_nativethread_lock_unlock(&arg->lock);
+
+ if (need_free) free_getaddrinfo_arg(arg);
+
+ // If the current thread is interrupted by asynchronous exception, the following raises the exception.
+ // But if the current thread is interrupted by timer thread, the following returns; we need to manually retry.
+ rb_thread_check_ints();
+ if (retry) goto start;
+
+ /* Because errno is threadlocal, the errno value we got from the call to getaddrinfo() in the thread
+ * (in case of EAI_SYSTEM return value) is not propagated to the caller of _this_ function. Set errno
+ * explicitly, as round-tripped through struct getaddrinfo_arg, to deal with that */
+ if (gai_errno) errno = gai_errno;
+ return err;
+}
+
+#endif
+
+#if GETADDRINFO_IMPL == 0
+
+int
+rb_getnameinfo(const struct sockaddr *sa, socklen_t salen,
+ char *host, size_t hostlen,
+ char *serv, size_t servlen, int flags)
+{
+ return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
+}
+
+#elif GETADDRINFO_IMPL == 1
+
struct getnameinfo_arg
{
const struct sockaddr *sa;
@@ -323,16 +569,11 @@ nogvl_getnameinfo(void *arg)
ptr->serv, (socklen_t)ptr->servlen,
ptr->flags);
}
-#endif
-
int
rb_getnameinfo(const struct sockaddr *sa, socklen_t salen,
- char *host, size_t hostlen,
- char *serv, size_t servlen, int flags)
+ char *host, size_t hostlen,
+ char *serv, size_t servlen, int flags)
{
-#ifdef GETADDRINFO_EMU
- return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
-#else
struct getnameinfo_arg arg;
int ret;
arg.sa = sa;
@@ -344,17 +585,185 @@ rb_getnameinfo(const struct sockaddr *sa, socklen_t salen,
arg.flags = flags;
ret = (int)(VALUE)rb_thread_call_without_gvl(nogvl_getnameinfo, &arg, RUBY_UBF_IO, 0);
return ret;
-#endif
+}
+
+#elif GETADDRINFO_IMPL == 2
+
+struct getnameinfo_arg
+{
+ struct sockaddr *sa;
+ socklen_t salen;
+ int flags;
+ char *host;
+ size_t hostlen;
+ char *serv;
+ size_t servlen;
+ int err, gni_errno, refcount, done, cancelled;
+ rb_nativethread_lock_t lock;
+ rb_nativethread_cond_t cond;
+};
+
+static struct getnameinfo_arg *
+allocate_getnameinfo_arg(const struct sockaddr *sa, socklen_t salen, size_t hostlen, size_t servlen, int flags)
+{
+ size_t sa_offset = sizeof(struct getnameinfo_arg);
+ size_t host_offset = sa_offset + salen;
+ size_t serv_offset = host_offset + hostlen;
+ size_t bufsize = serv_offset + servlen;
+
+ char *buf = malloc(bufsize);
+ if (!buf) {
+ rb_gc();
+ buf = malloc(bufsize);
+ if (!buf) return NULL;
+ }
+ struct getnameinfo_arg *arg = (struct getnameinfo_arg *)buf;
+
+ arg->sa = (struct sockaddr *)(buf + sa_offset);
+ memcpy(arg->sa, sa, salen);
+ arg->salen = salen;
+ arg->host = buf + host_offset;
+ arg->hostlen = hostlen;
+ arg->serv = buf + serv_offset;
+ arg->servlen = servlen;
+ arg->flags = flags;
+
+ arg->refcount = 2;
+ arg->done = arg->cancelled = 0;
+
+ rb_nativethread_lock_initialize(&arg->lock);
+ rb_native_cond_initialize(&arg->cond);
+
+ return arg;
+}
+
+static void
+free_getnameinfo_arg(struct getnameinfo_arg *arg)
+{
+ rb_native_cond_destroy(&arg->cond);
+ rb_nativethread_lock_destroy(&arg->lock);
+
+ free(arg);
+}
+
+static void *
+do_getnameinfo(void *ptr)
+{
+ struct getnameinfo_arg *arg = (struct getnameinfo_arg *)ptr;
+
+ int err, gni_errno;
+ err = getnameinfo(arg->sa, arg->salen, arg->host, (socklen_t)arg->hostlen, arg->serv, (socklen_t)arg->servlen, arg->flags);
+ gni_errno = errno;
+
+ int need_free = 0;
+ rb_nativethread_lock_lock(&arg->lock);
+ arg->err = err;
+ arg->gni_errno = gni_errno;
+ if (!arg->cancelled) {
+ arg->done = 1;
+ rb_native_cond_signal(&arg->cond);
+ }
+ if (--arg->refcount == 0) need_free = 1;
+ rb_nativethread_lock_unlock(&arg->lock);
+
+ if (need_free) free_getnameinfo_arg(arg);
+
+ return 0;
+}
+
+static void *
+wait_getnameinfo(void *ptr)
+{
+ struct getnameinfo_arg *arg = (struct getnameinfo_arg *)ptr;
+ rb_nativethread_lock_lock(&arg->lock);
+ while (!arg->done && !arg->cancelled) {
+ rb_native_cond_wait(&arg->cond, &arg->lock);
+ }
+ rb_nativethread_lock_unlock(&arg->lock);
+ return 0;
}
static void
+cancel_getnameinfo(void *ptr)
+{
+ struct getnameinfo_arg *arg = (struct getnameinfo_arg *)ptr;
+ rb_nativethread_lock_lock(&arg->lock);
+ arg->cancelled = 1;
+ rb_native_cond_signal(&arg->cond);
+ rb_nativethread_lock_unlock(&arg->lock);
+}
+
+int
+rb_getnameinfo(const struct sockaddr *sa, socklen_t salen,
+ char *host, size_t hostlen,
+ char *serv, size_t servlen, int flags)
+{
+ int retry;
+ struct getnameinfo_arg *arg;
+ int err, gni_errno = 0;
+
+start:
+ retry = 0;
+
+ arg = allocate_getnameinfo_arg(sa, salen, hostlen, servlen, flags);
+ if (!arg) {
+ return EAI_MEMORY;
+ }
+
+ pthread_t th;
+ if (do_pthread_create(&th, do_getnameinfo, arg) != 0) {
+ free_getnameinfo_arg(arg);
+ return EAI_AGAIN;
+ }
+ pthread_detach(th);
+
+ rb_thread_call_without_gvl2(wait_getnameinfo, arg, cancel_getnameinfo, arg);
+
+ int need_free = 0;
+ rb_nativethread_lock_lock(&arg->lock);
+ if (arg->done) {
+ err = arg->err;
+ gni_errno = arg->gni_errno;
+ if (err == 0) {
+ if (host) memcpy(host, arg->host, hostlen);
+ if (serv) memcpy(serv, arg->serv, servlen);
+ }
+ }
+ else if (arg->cancelled) {
+ err = EAI_AGAIN;
+ }
+ else {
+ // If already interrupted, rb_thread_call_without_gvl2 may return without calling wait_getnameinfo.
+ // In this case, it could be !arg->done && !arg->cancelled.
+ arg->cancelled = 1;
+ retry = 1;
+ }
+ if (--arg->refcount == 0) need_free = 1;
+ rb_nativethread_lock_unlock(&arg->lock);
+
+ if (need_free) free_getnameinfo_arg(arg);
+
+ // If the current thread is interrupted by asynchronous exception, the following raises the exception.
+ // But if the current thread is interrupted by timer thread, the following returns; we need to manually retry.
+ rb_thread_check_ints();
+ if (retry) goto start;
+
+ /* Make sure we copy the thread-local errno value from the getnameinfo thread back to this thread, so
+ * calling code sees the correct errno */
+ if (gni_errno) errno = gni_errno;
+ return err;
+}
+
+#endif
+
+static void
make_ipaddr0(struct sockaddr *addr, socklen_t addrlen, char *buf, size_t buflen)
{
int error;
error = rb_getnameinfo(addr, addrlen, buf, buflen, NULL, 0, NI_NUMERICHOST);
if (error) {
- rsock_raise_socket_error("getnameinfo", error);
+ rsock_raise_resolution_error("getnameinfo", error);
}
}
@@ -550,17 +959,7 @@ rsock_getaddrinfo(VALUE host, VALUE port, struct addrinfo *hints, int socktype_h
}
if (!resolved) {
-#ifdef GETADDRINFO_EMU
- error = getaddrinfo(hostp, portp, hints, &ai);
-#else
- struct getaddrinfo_arg arg;
- MEMZERO(&arg, struct getaddrinfo_arg, 1);
- arg.node = hostp;
- arg.service = portp;
- arg.hints = hints;
- arg.res = &ai;
- error = (int)(VALUE)rb_thread_call_without_gvl(nogvl_getaddrinfo, &arg, RUBY_UBF_IO, 0);
-#endif
+ error = rb_getaddrinfo(hostp, portp, hints, &ai);
if (error == 0) {
res = (struct rb_addrinfo *)xmalloc(sizeof(struct rb_addrinfo));
res->allocated_by_malloc = 0;
@@ -573,7 +972,7 @@ rsock_getaddrinfo(VALUE host, VALUE port, struct addrinfo *hints, int socktype_h
if (hostp && hostp[strlen(hostp)-1] == '\n') {
rb_raise(rb_eSocket, "newline at the end of hostname");
}
- rsock_raise_socket_error("getaddrinfo", error);
+ rsock_raise_resolution_error("getaddrinfo", error);
}
return res;
@@ -632,7 +1031,7 @@ rsock_ipaddr(struct sockaddr *sockaddr, socklen_t sockaddrlen, int norevlookup)
error = rb_getnameinfo(sockaddr, sockaddrlen, hbuf, sizeof(hbuf),
pbuf, sizeof(pbuf), NI_NUMERICHOST | NI_NUMERICSERV);
if (error) {
- rsock_raise_socket_error("getnameinfo", error);
+ rsock_raise_resolution_error("getnameinfo", error);
}
addr2 = rb_str_new2(hbuf);
if (addr1 == Qnil) {
@@ -1266,11 +1665,11 @@ rsock_inspect_sockaddr(struct sockaddr *sockaddr_arg, socklen_t socklen, VALUE r
* RFC 4007: IPv6 Scoped Address Architecture
* draft-ietf-ipv6-scope-api-00.txt: Scoped Address Extensions to the IPv6 Basic Socket API
*/
- error = getnameinfo(&sockaddr->addr, socklen,
- hbuf, (socklen_t)sizeof(hbuf), NULL, 0,
- NI_NUMERICHOST|NI_NUMERICSERV);
+ error = rb_getnameinfo(&sockaddr->addr, socklen,
+ hbuf, (socklen_t)sizeof(hbuf), NULL, 0,
+ NI_NUMERICHOST|NI_NUMERICSERV);
if (error) {
- rsock_raise_socket_error("getnameinfo", error);
+ rsock_raise_resolution_error("getnameinfo", error);
}
if (addr->sin6_port == 0) {
rb_str_cat2(ret, hbuf);
@@ -1634,11 +2033,11 @@ addrinfo_mdump(VALUE self)
{
char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
int error;
- error = getnameinfo(&rai->addr.addr, rai->sockaddr_len,
- hbuf, (socklen_t)sizeof(hbuf), pbuf, (socklen_t)sizeof(pbuf),
- NI_NUMERICHOST|NI_NUMERICSERV);
+ error = rb_getnameinfo(&rai->addr.addr, rai->sockaddr_len,
+ hbuf, (socklen_t)sizeof(hbuf), pbuf, (socklen_t)sizeof(pbuf),
+ NI_NUMERICHOST|NI_NUMERICSERV);
if (error) {
- rsock_raise_socket_error("getnameinfo", error);
+ rsock_raise_resolution_error("getnameinfo", error);
}
sockaddr = rb_assoc_new(rb_str_new_cstr(hbuf), rb_str_new_cstr(pbuf));
break;
@@ -1980,11 +2379,11 @@ addrinfo_getnameinfo(int argc, VALUE *argv, VALUE self)
if (rai->socktype == SOCK_DGRAM)
flags |= NI_DGRAM;
- error = getnameinfo(&rai->addr.addr, rai->sockaddr_len,
- hbuf, (socklen_t)sizeof(hbuf), pbuf, (socklen_t)sizeof(pbuf),
- flags);
+ error = rb_getnameinfo(&rai->addr.addr, rai->sockaddr_len,
+ hbuf, (socklen_t)sizeof(hbuf), pbuf, (socklen_t)sizeof(pbuf),
+ flags);
if (error) {
- rsock_raise_socket_error("getnameinfo", error);
+ rsock_raise_resolution_error("getnameinfo", error);
}
return rb_assoc_new(rb_str_new2(hbuf), rb_str_new2(pbuf));
@@ -2614,12 +3013,12 @@ rsock_io_socket_addrinfo(VALUE io, struct sockaddr *addr, socklen_t len)
void
rsock_init_addrinfo(void)
{
+ id_timeout = rb_intern("timeout");
+
/*
* The Addrinfo class maps <tt>struct addrinfo</tt> to ruby. This
* structure identifies an Internet host and a service.
*/
- id_timeout = rb_intern("timeout");
-
rb_cAddrinfo = rb_define_class("Addrinfo", rb_cObject);
rb_define_alloc_func(rb_cAddrinfo, addrinfo_s_allocate);
rb_define_method(rb_cAddrinfo, "initialize", addrinfo_initialize, -1);
diff --git a/ext/socket/rubysocket.h b/ext/socket/rubysocket.h
index 5f803ba0da..f486db4262 100644
--- a/ext/socket/rubysocket.h
+++ b/ext/socket/rubysocket.h
@@ -35,6 +35,7 @@
#ifdef _WIN32
# include <winsock2.h>
# include <ws2tcpip.h>
+# include <mswsock.h>
# include <iphlpapi.h>
# if defined(_MSC_VER)
# undef HAVE_TYPE_STRUCT_SOCKADDR_DL
@@ -285,6 +286,7 @@ extern VALUE rb_cAddrinfo;
extern VALUE rb_cSockOpt;
extern VALUE rb_eSocket;
+extern VALUE rb_eResolution;
#ifdef SOCKS
extern VALUE rb_cSOCKSSocket;
@@ -307,7 +309,7 @@ VALUE rsock_sockaddr_string_value_with_addrinfo(volatile VALUE *v, VALUE *ai_ret
VALUE rb_check_sockaddr_string_type(VALUE);
-NORETURN(void rsock_raise_socket_error(const char *, int));
+NORETURN(void rsock_raise_resolution_error(const char *, int));
int rsock_family_arg(VALUE domain);
int rsock_socktype_arg(VALUE type);
@@ -459,6 +461,8 @@ VALUE rsock_write_nonblock(VALUE sock, VALUE buf, VALUE ex);
void rsock_make_fd_nonblock(int fd);
+int rsock_is_dgram(rb_io_t *fptr);
+
#if !defined HAVE_INET_NTOP && ! defined _WIN32
const char *inet_ntop(int, const void *, char *, size_t);
#elif defined __MINGW32__
diff --git a/ext/socket/socket.c b/ext/socket/socket.c
index eb74f7a936..c780d77cf6 100644
--- a/ext/socket/socket.c
+++ b/ext/socket/socket.c
@@ -1313,7 +1313,7 @@ sock_s_getnameinfo(int argc, VALUE *argv, VALUE _)
saved_errno = errno;
if (res) rb_freeaddrinfo(res);
errno = saved_errno;
- rsock_raise_socket_error("getnameinfo", error);
+ rsock_raise_resolution_error("getnameinfo", error);
UNREACHABLE_RETURN(Qnil);
}
@@ -1654,8 +1654,7 @@ socket_s_ip_address_list(VALUE self)
finish:
save_errno = errno;
- if (lc.lifc_buf != NULL)
- xfree(lc.lifc_req);
+ xfree(lc.lifc_req);
if (fd != -1)
close(fd);
errno = save_errno;
diff --git a/ext/stringio/.document b/ext/stringio/.document
new file mode 100644
index 0000000000..decba0135a
--- /dev/null
+++ b/ext/stringio/.document
@@ -0,0 +1 @@
+*.[ch]
diff --git a/ext/stringio/depend b/ext/stringio/depend
index ba2b812041..b9fba7e9fa 100644
--- a/ext/stringio/depend
+++ b/ext/stringio/depend
@@ -157,6 +157,7 @@ stringio.o: $(hdrdir)/ruby/internal/special_consts.h
stringio.o: $(hdrdir)/ruby/internal/static_assert.h
stringio.o: $(hdrdir)/ruby/internal/stdalign.h
stringio.o: $(hdrdir)/ruby/internal/stdbool.h
+stringio.o: $(hdrdir)/ruby/internal/stdckdint.h
stringio.o: $(hdrdir)/ruby/internal/symbol.h
stringio.o: $(hdrdir)/ruby/internal/value.h
stringio.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/stringio/extconf.rb b/ext/stringio/extconf.rb
index ad8650dce2..553732f79c 100644
--- a/ext/stringio/extconf.rb
+++ b/ext/stringio/extconf.rb
@@ -1,3 +1,7 @@
# frozen_string_literal: false
require 'mkmf'
-create_makefile('stringio')
+if RUBY_ENGINE == 'ruby'
+ create_makefile('stringio')
+else
+ File.write('Makefile', dummy_makefile("").join)
+end
diff --git a/ext/stringio/stringio.c b/ext/stringio/stringio.c
index 68306cac4b..820b8228a3 100644
--- a/ext/stringio/stringio.c
+++ b/ext/stringio/stringio.c
@@ -12,7 +12,10 @@
**********************************************************************/
-#define STRINGIO_VERSION "3.0.8"
+static const char *const
+STRINGIO_VERSION = "3.1.1";
+
+#include <stdbool.h>
#include "ruby.h"
#include "ruby/io.h"
@@ -47,7 +50,20 @@ static long strio_write(VALUE self, VALUE str);
#define IS_STRIO(obj) (rb_typeddata_is_kind_of((obj), &strio_data_type))
#define error_inval(msg) (rb_syserr_fail(EINVAL, msg))
-#define get_enc(ptr) ((ptr)->enc ? (ptr)->enc : rb_enc_get((ptr)->string))
+#define get_enc(ptr) ((ptr)->enc ? (ptr)->enc : !NIL_P((ptr)->string) ? rb_enc_get((ptr)->string) : NULL)
+#ifndef HAVE_RB_STR_CHILLED_P
+static bool
+rb_str_chilled_p(VALUE str)
+{
+ return false;
+}
+#endif
+
+static bool
+readonly_string_p(VALUE string)
+{
+ return OBJ_FROZEN_RAW(string) && !rb_str_chilled_p(string);
+}
static struct StringIO *
strio_alloc(void)
@@ -165,7 +181,13 @@ writable(VALUE strio)
static void
check_modifiable(struct StringIO *ptr)
{
- if (OBJ_FROZEN(ptr->string)) {
+ if (NIL_P(ptr->string)) {
+ /* Null device StringIO */
+ }
+ else if (rb_str_chilled_p(ptr->string)) {
+ rb_str_modify(ptr->string);
+ }
+ else if (OBJ_FROZEN_RAW(ptr->string)) {
rb_raise(rb_eIOError, "not modifiable string");
}
}
@@ -280,13 +302,14 @@ strio_init(int argc, VALUE *argv, struct StringIO *ptr, VALUE self)
argc = rb_scan_args(argc, argv, "02:", &string, &vmode, &opt);
rb_io_extract_modeenc(&vmode, 0, opt, &oflags, &ptr->flags, &convconfig);
- if (argc) {
+ if (!NIL_P(string)) {
StringValue(string);
}
- else {
+ else if (!argc) {
string = rb_enc_str_new("", 0, rb_default_external_encoding());
}
- if (OBJ_FROZEN_RAW(string)) {
+
+ if (!NIL_P(string) && readonly_string_p(string)) {
if (ptr->flags & FMODE_WRITABLE) {
rb_syserr_fail(EACCES, 0);
}
@@ -296,11 +319,11 @@ strio_init(int argc, VALUE *argv, struct StringIO *ptr, VALUE self)
ptr->flags |= FMODE_WRITABLE;
}
}
- if (ptr->flags & FMODE_TRUNC) {
+ if (!NIL_P(string) && (ptr->flags & FMODE_TRUNC)) {
rb_str_resize(string, 0);
}
RB_OBJ_WRITE(self, &ptr->string, string);
- if (argc == 1) {
+ if (argc == 1 && !NIL_P(string)) {
ptr->enc = rb_enc_get(string);
}
else {
@@ -480,7 +503,7 @@ strio_set_string(VALUE self, VALUE string)
rb_io_taint_check(self);
ptr->flags &= ~FMODE_READWRITE;
StringValue(string);
- ptr->flags = OBJ_FROZEN(string) ? FMODE_READABLE : FMODE_READWRITE;
+ ptr->flags = readonly_string_p(string) ? FMODE_READABLE : FMODE_READWRITE;
ptr->pos = 0;
ptr->lineno = 0;
RB_OBJ_WRITE(self, &ptr->string, string);
@@ -594,6 +617,7 @@ static struct StringIO *
strio_to_read(VALUE self)
{
struct StringIO *ptr = readable(self);
+ if (NIL_P(ptr->string)) return NULL;
if (ptr->pos < RSTRING_LEN(ptr->string)) return ptr;
return NULL;
}
@@ -603,7 +627,7 @@ strio_to_read(VALUE self)
* eof? -> true or false
*
* Returns +true+ if positioned at end-of-stream, +false+ otherwise;
- * see {Position}[rdoc-ref:File@Position].
+ * see {Position}[rdoc-ref:IO@Position].
*
* Raises IOError if the stream is not opened for reading.
*/
@@ -871,7 +895,7 @@ strio_getc(VALUE self)
int len;
char *p;
- if (pos >= RSTRING_LEN(str)) {
+ if (NIL_P(str) || pos >= RSTRING_LEN(str)) {
return Qnil;
}
p = RSTRING_PTR(str)+pos;
@@ -892,7 +916,7 @@ strio_getbyte(VALUE self)
{
struct StringIO *ptr = readable(self);
int c;
- if (ptr->pos >= RSTRING_LEN(ptr->string)) {
+ if (NIL_P(ptr->string) || ptr->pos >= RSTRING_LEN(ptr->string)) {
return Qnil;
}
c = RSTRING_PTR(ptr->string)[ptr->pos++];
@@ -914,9 +938,6 @@ strio_extend(struct StringIO *ptr, long pos, long len)
if (pos > olen)
MEMZERO(RSTRING_PTR(ptr->string) + olen, char, pos - olen);
}
- else {
- rb_str_modify(ptr->string);
- }
}
/*
@@ -933,6 +954,7 @@ strio_ungetc(VALUE self, VALUE c)
rb_encoding *enc, *enc2;
check_modifiable(ptr);
+ if (NIL_P(ptr->string)) return Qnil;
if (NIL_P(c)) return Qnil;
if (RB_INTEGER_TYPE_P(c)) {
int len, cc = NUM2INT(c);
@@ -970,12 +992,13 @@ strio_ungetbyte(VALUE self, VALUE c)
struct StringIO *ptr = readable(self);
check_modifiable(ptr);
+ if (NIL_P(ptr->string)) return Qnil;
if (NIL_P(c)) return Qnil;
if (RB_INTEGER_TYPE_P(c)) {
- /* rb_int_and() not visible from exts */
- VALUE v = rb_funcall(c, '&', 1, INT2FIX(0xff));
- const char cc = NUM2INT(v) & 0xFF;
- strio_unget_bytes(ptr, &cc, 1);
+ /* rb_int_and() not visible from exts */
+ VALUE v = rb_funcall(c, '&', 1, INT2FIX(0xff));
+ const char cc = NUM2INT(v) & 0xFF;
+ strio_unget_bytes(ptr, &cc, 1);
}
else {
long cl;
@@ -1142,38 +1165,57 @@ struct getline_arg {
};
static struct getline_arg *
-prepare_getline_args(struct getline_arg *arg, int argc, VALUE *argv)
+prepare_getline_args(struct StringIO *ptr, struct getline_arg *arg, int argc, VALUE *argv)
{
- VALUE str, lim, opts;
+ VALUE rs, lim, opts;
long limit = -1;
int respect_chomp;
- argc = rb_scan_args(argc, argv, "02:", &str, &lim, &opts);
- respect_chomp = argc == 0 || !NIL_P(str);
+ argc = rb_scan_args(argc, argv, "02:", &rs, &lim, &opts);
+ respect_chomp = argc == 0 || !NIL_P(rs);
switch (argc) {
case 0:
- str = rb_rs;
+ rs = rb_rs;
break;
case 1:
- if (!NIL_P(str) && !RB_TYPE_P(str, T_STRING)) {
- VALUE tmp = rb_check_string_type(str);
+ if (!NIL_P(rs) && !RB_TYPE_P(rs, T_STRING)) {
+ VALUE tmp = rb_check_string_type(rs);
if (NIL_P(tmp)) {
- limit = NUM2LONG(str);
- str = rb_rs;
+ limit = NUM2LONG(rs);
+ rs = rb_rs;
}
else {
- str = tmp;
+ rs = tmp;
}
}
break;
case 2:
- if (!NIL_P(str)) StringValue(str);
+ if (!NIL_P(rs)) StringValue(rs);
if (!NIL_P(lim)) limit = NUM2LONG(lim);
break;
}
- arg->rs = str;
+ if (!NIL_P(ptr->string) && !NIL_P(rs)) {
+ rb_encoding *enc_rs, *enc_io;
+ enc_rs = rb_enc_get(rs);
+ enc_io = get_enc(ptr);
+ if (enc_rs != enc_io &&
+ (rb_enc_str_coderange(rs) != ENC_CODERANGE_7BIT ||
+ (RSTRING_LEN(rs) > 0 && !rb_enc_asciicompat(enc_io)))) {
+ if (rs == rb_rs) {
+ rs = rb_enc_str_new(0, 0, enc_io);
+ rb_str_buf_cat_ascii(rs, "\n");
+ rs = rs;
+ }
+ else {
+ rb_raise(rb_eArgError, "encoding mismatch: %s IO with %s RS",
+ rb_enc_name(enc_io),
+ rb_enc_name(enc_rs));
+ }
+ }
+ }
+ arg->rs = rs;
arg->limit = limit;
arg->chomp = 0;
if (!NIL_P(opts)) {
@@ -1183,9 +1225,9 @@ prepare_getline_args(struct getline_arg *arg, int argc, VALUE *argv)
keywords[0] = rb_intern_const("chomp");
}
rb_get_kwargs(opts, keywords, 0, 1, &vchomp);
- if (respect_chomp) {
+ if (respect_chomp) {
arg->chomp = (vchomp != Qundef) && RTEST(vchomp);
- }
+ }
}
return arg;
}
@@ -1209,7 +1251,7 @@ strio_getline(struct getline_arg *arg, struct StringIO *ptr)
long w = 0;
rb_encoding *enc = get_enc(ptr);
- if (ptr->pos >= (n = RSTRING_LEN(ptr->string))) {
+ if (NIL_P(ptr->string) || ptr->pos >= (n = RSTRING_LEN(ptr->string))) {
return Qnil;
}
s = RSTRING_PTR(ptr->string);
@@ -1225,7 +1267,7 @@ strio_getline(struct getline_arg *arg, struct StringIO *ptr)
str = strio_substr(ptr, ptr->pos, e - s - w, enc);
}
else if ((n = RSTRING_LEN(str)) == 0) {
- const char *paragraph_end = NULL;
+ const char *paragraph_end = NULL;
p = s;
while (p[(p + 1 < e) && (*p == '\r') && 0] == '\n') {
p += *p == '\r';
@@ -1235,18 +1277,18 @@ strio_getline(struct getline_arg *arg, struct StringIO *ptr)
}
s = p;
while ((p = memchr(p, '\n', e - p)) && (p != e)) {
- p++;
- if (!((p < e && *p == '\n') ||
- (p + 1 < e && *p == '\r' && *(p+1) == '\n'))) {
- continue;
- }
- paragraph_end = p - ((*(p-2) == '\r') ? 2 : 1);
- while ((p < e && *p == '\n') ||
- (p + 1 < e && *p == '\r' && *(p+1) == '\n')) {
- p += (*p == '\r') ? 2 : 1;
- }
- e = p;
- break;
+ p++;
+ if (!((p < e && *p == '\n') ||
+ (p + 1 < e && *p == '\r' && *(p+1) == '\n'))) {
+ continue;
+ }
+ paragraph_end = p - ((*(p-2) == '\r') ? 2 : 1);
+ while ((p < e && *p == '\n') ||
+ (p + 1 < e && *p == '\r' && *(p+1) == '\n')) {
+ p += (*p == '\r') ? 2 : 1;
+ }
+ e = p;
+ break;
}
if (arg->chomp && paragraph_end) {
w = e - paragraph_end;
@@ -1301,15 +1343,16 @@ strio_getline(struct getline_arg *arg, struct StringIO *ptr)
static VALUE
strio_gets(int argc, VALUE *argv, VALUE self)
{
+ struct StringIO *ptr = readable(self);
struct getline_arg arg;
VALUE str;
- if (prepare_getline_args(&arg, argc, argv)->limit == 0) {
- struct StringIO *ptr = readable(self);
+ if (prepare_getline_args(ptr, &arg, argc, argv)->limit == 0) {
+ if (NIL_P(ptr->string)) return Qnil;
return rb_enc_str_new(0, 0, get_enc(ptr));
}
- str = strio_getline(&arg, readable(self));
+ str = strio_getline(&arg, ptr);
rb_lastline_set(str);
return str;
}
@@ -1346,16 +1389,16 @@ static VALUE
strio_each(int argc, VALUE *argv, VALUE self)
{
VALUE line;
+ struct StringIO *ptr = readable(self);
struct getline_arg arg;
- StringIO(self);
RETURN_ENUMERATOR(self, argc, argv);
- if (prepare_getline_args(&arg, argc, argv)->limit == 0) {
+ if (prepare_getline_args(ptr, &arg, argc, argv)->limit == 0) {
rb_raise(rb_eArgError, "invalid limit: 0 for each_line");
}
- while (!NIL_P(line = strio_getline(&arg, readable(self)))) {
+ while (!NIL_P(line = strio_getline(&arg, ptr))) {
rb_yield(line);
}
return self;
@@ -1373,15 +1416,15 @@ static VALUE
strio_readlines(int argc, VALUE *argv, VALUE self)
{
VALUE ary, line;
+ struct StringIO *ptr = readable(self);
struct getline_arg arg;
- StringIO(self);
- ary = rb_ary_new();
- if (prepare_getline_args(&arg, argc, argv)->limit == 0) {
+ if (prepare_getline_args(ptr, &arg, argc, argv)->limit == 0) {
rb_raise(rb_eArgError, "invalid limit: 0 for readlines");
}
- while (!NIL_P(line = strio_getline(&arg, readable(self)))) {
+ ary = rb_ary_new();
+ while (!NIL_P(line = strio_getline(&arg, ptr))) {
rb_ary_push(ary, line);
}
return ary;
@@ -1420,6 +1463,7 @@ strio_write(VALUE self, VALUE str)
if (!RB_TYPE_P(str, T_STRING))
str = rb_obj_as_string(str);
enc = get_enc(ptr);
+ if (!enc) return 0;
enc2 = rb_enc_get(str);
if (enc != enc2 && enc != ascii8bit && enc != (usascii = rb_usascii_encoding())) {
VALUE converted = rb_str_conv_enc(str, enc2, enc);
@@ -1445,6 +1489,7 @@ strio_write(VALUE self, VALUE str)
}
else {
strio_extend(ptr, ptr->pos, len);
+ rb_str_modify(ptr->string);
memmove(RSTRING_PTR(ptr->string)+ptr->pos, RSTRING_PTR(str), len);
}
RB_GC_GUARD(str);
@@ -1491,10 +1536,12 @@ strio_putc(VALUE self, VALUE ch)
check_modifiable(ptr);
if (RB_TYPE_P(ch, T_STRING)) {
+ if (NIL_P(ptr->string)) return ch;
str = rb_str_substr(ch, 0, 1);
}
else {
char c = NUM2CHR(ch);
+ if (NIL_P(ptr->string)) return ch;
str = rb_str_new(&c, 1);
}
strio_write(self, str);
@@ -1537,7 +1584,8 @@ strio_read(int argc, VALUE *argv, VALUE self)
if (len < 0) {
rb_raise(rb_eArgError, "negative length %ld given", len);
}
- if (len > 0 && ptr->pos >= RSTRING_LEN(ptr->string)) {
+ if (len > 0 &&
+ (NIL_P(ptr->string) || ptr->pos >= RSTRING_LEN(ptr->string))) {
if (!NIL_P(str)) rb_str_resize(str, 0);
return Qnil;
}
@@ -1546,6 +1594,7 @@ strio_read(int argc, VALUE *argv, VALUE self)
}
/* fall through */
case 0:
+ if (NIL_P(ptr->string)) return Qnil;
len = RSTRING_LEN(ptr->string);
if (len <= ptr->pos) {
rb_encoding *enc = get_enc(ptr);
@@ -1563,7 +1612,7 @@ strio_read(int argc, VALUE *argv, VALUE self)
}
break;
default:
- rb_error_arity(argc, 0, 2);
+ rb_error_arity(argc, 0, 2);
}
if (NIL_P(str)) {
rb_encoding *enc = binary ? rb_ascii8bit_encoding() : get_enc(ptr);
@@ -1574,16 +1623,64 @@ strio_read(int argc, VALUE *argv, VALUE self)
if (len > rest) len = rest;
rb_str_resize(str, len);
MEMCPY(RSTRING_PTR(str), RSTRING_PTR(ptr->string) + ptr->pos, char, len);
- if (binary)
- rb_enc_associate(str, rb_ascii8bit_encoding());
- else
+ if (!binary) {
rb_enc_copy(str, ptr->string);
+ }
}
ptr->pos += RSTRING_LEN(str);
return str;
}
/*
+ * call-seq:
+ * pread(maxlen, offset) -> string
+ * pread(maxlen, offset, out_string) -> string
+ *
+ * See IO#pread.
+ */
+static VALUE
+strio_pread(int argc, VALUE *argv, VALUE self)
+{
+ VALUE rb_len, rb_offset, rb_buf;
+ rb_scan_args(argc, argv, "21", &rb_len, &rb_offset, &rb_buf);
+ long len = NUM2LONG(rb_len);
+ long offset = NUM2LONG(rb_offset);
+
+ if (len < 0) {
+ rb_raise(rb_eArgError, "negative string size (or size too big): %" PRIsVALUE, rb_len);
+ }
+
+ if (len == 0) {
+ if (NIL_P(rb_buf)) {
+ return rb_str_new("", 0);
+ }
+ return rb_buf;
+ }
+
+ if (offset < 0) {
+ rb_syserr_fail_str(EINVAL, rb_sprintf("pread: Invalid offset argument: %" PRIsVALUE, rb_offset));
+ }
+
+ struct StringIO *ptr = readable(self);
+
+ if (offset >= RSTRING_LEN(ptr->string)) {
+ rb_eof_error();
+ }
+
+ if (NIL_P(rb_buf)) {
+ return strio_substr(ptr, offset, len, rb_ascii8bit_encoding());
+ }
+
+ long rest = RSTRING_LEN(ptr->string) - offset;
+ if (len > rest) len = rest;
+ rb_str_resize(rb_buf, len);
+ rb_enc_associate(rb_buf, rb_ascii8bit_encoding());
+ MEMCPY(RSTRING_PTR(rb_buf), RSTRING_PTR(ptr->string) + offset, char, len);
+ return rb_buf;
+}
+
+
+/*
* call-seq:
* strio.sysread(integer[, outbuf]) -> string
* strio.readpartial(integer[, outbuf]) -> string
@@ -1631,8 +1728,14 @@ strio_read_nonblock(int argc, VALUE *argv, VALUE self)
return val;
}
+/*
+ * See IO#write
+ */
#define strio_syswrite rb_io_write
+/*
+ * See IO#write_nonblock
+ */
static VALUE
strio_syswrite_nonblock(int argc, VALUE *argv, VALUE self)
{
@@ -1660,7 +1763,7 @@ strio_size(VALUE self)
{
VALUE string = StringIO(self)->string;
if (NIL_P(string)) {
- rb_raise(rb_eIOError, "not opened");
+ return INT2FIX(0);
}
return ULONG2NUM(RSTRING_LEN(string));
}
@@ -1677,10 +1780,12 @@ strio_truncate(VALUE self, VALUE len)
{
VALUE string = writable(self)->string;
long l = NUM2LONG(len);
- long plen = RSTRING_LEN(string);
+ long plen;
if (l < 0) {
error_inval("negative length");
}
+ if (NIL_P(string)) return 0;
+ plen = RSTRING_LEN(string);
rb_str_resize(string, l);
if (plen < l) {
MEMZERO(RSTRING_PTR(string) + plen, char, l - plen);
@@ -1751,13 +1856,22 @@ strio_set_encoding(int argc, VALUE *argv, VALUE self)
}
}
ptr->enc = enc;
- if (WRITABLE(self)) {
+ if (!NIL_P(ptr->string) && WRITABLE(self)) {
rb_enc_associate(ptr->string, enc);
}
return self;
}
+/*
+ * call-seq:
+ * strio.set_encoding_by_bom => strio or nil
+ *
+ * Sets the encoding according to the BOM (Byte Order Mark) in the
+ * string.
+ *
+ * Returns +self+ if the BOM is found, otherwise +nil.
+ */
static VALUE
strio_set_encoding_by_bom(VALUE self)
{
@@ -1790,10 +1904,15 @@ Init_stringio(void)
VALUE StringIO = rb_define_class("StringIO", rb_cObject);
+ /* The version string */
rb_define_const(StringIO, "VERSION", rb_str_new_cstr(STRINGIO_VERSION));
rb_include_module(StringIO, rb_mEnumerable);
rb_define_alloc_func(StringIO, strio_s_allocate);
+
+ /* Maximum length that a StringIO instance can hold */
+ rb_define_const(StringIO, "MAX_LENGTH", LONG2NUM(LONG_MAX));
+
rb_define_singleton_method(StringIO, "new", strio_s_new, -1);
rb_define_singleton_method(StringIO, "open", strio_s_open, -1);
rb_define_method(StringIO, "initialize", strio_initialize, -1);
@@ -1843,6 +1962,7 @@ Init_stringio(void)
rb_define_method(StringIO, "gets", strio_gets, -1);
rb_define_method(StringIO, "readlines", strio_readlines, -1);
rb_define_method(StringIO, "read", strio_read, -1);
+ rb_define_method(StringIO, "pread", strio_pread, -1);
rb_define_method(StringIO, "write", strio_write_m, -1);
rb_define_method(StringIO, "putc", strio_putc, 1);
diff --git a/ext/stringio/stringio.gemspec b/ext/stringio/stringio.gemspec
index 7d320b3e78..b40b7fc824 100644
--- a/ext/stringio/stringio.gemspec
+++ b/ext/stringio/stringio.gemspec
@@ -4,7 +4,7 @@
source_version = ["", "ext/stringio/"].find do |dir|
begin
break File.open(File.join(__dir__, "#{dir}stringio.c")) {|f|
- f.gets("\n#define STRINGIO_VERSION ")
+ f.gets("\nSTRINGIO_VERSION ")
f.gets[/\s*"(.+)"/, 1]
}
rescue Errno::ENOENT
@@ -21,12 +21,19 @@ Gem::Specification.new do |s|
s.files = ["README.md"]
jruby = true if Gem::Platform.new('java') =~ s.platform or RUBY_ENGINE == 'jruby'
if jruby
- s.files += ["lib/stringio.rb", "lib/stringio.jar"]
+ s.require_paths = "lib/java"
+ s.files += ["lib/java/stringio.rb", "lib/java/stringio.jar"]
s.platform = "java"
else
s.extensions = ["ext/stringio/extconf.rb"]
s.files += ["ext/stringio/extconf.rb", "ext/stringio/stringio.c"]
end
+
+ s.extra_rdoc_files = [
+ ".document", ".rdoc_options", "COPYING", "LICENSE.txt",
+ "NEWS.md", "README.md", "docs/io.rb", "ext/stringio/.document",
+ ]
+
s.homepage = "https://github.com/ruby/stringio"
s.licenses = ["Ruby", "BSD-2-Clause"]
s.required_ruby_version = ">= 2.7"
diff --git a/ext/strscan/depend b/ext/strscan/depend
index 8d985b59e8..8dbae206d4 100644
--- a/ext/strscan/depend
+++ b/ext/strscan/depend
@@ -157,6 +157,7 @@ strscan.o: $(hdrdir)/ruby/internal/special_consts.h
strscan.o: $(hdrdir)/ruby/internal/static_assert.h
strscan.o: $(hdrdir)/ruby/internal/stdalign.h
strscan.o: $(hdrdir)/ruby/internal/stdbool.h
+strscan.o: $(hdrdir)/ruby/internal/stdckdint.h
strscan.o: $(hdrdir)/ruby/internal/symbol.h
strscan.o: $(hdrdir)/ruby/internal/value.h
strscan.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/strscan/extconf.rb b/ext/strscan/extconf.rb
index b53b63e455..bd65606a4e 100644
--- a/ext/strscan/extconf.rb
+++ b/ext/strscan/extconf.rb
@@ -3,6 +3,7 @@ require 'mkmf'
if RUBY_ENGINE == 'ruby'
$INCFLAGS << " -I$(top_srcdir)" if $extmk
have_func("onig_region_memsize", "ruby.h")
+ have_func("rb_reg_onig_match", "ruby.h")
create_makefile 'strscan'
else
File.write('Makefile', dummy_makefile("").join)
diff --git a/ext/strscan/strscan.c b/ext/strscan/strscan.c
index f7888e2cf1..70a3ce5260 100644
--- a/ext/strscan/strscan.c
+++ b/ext/strscan/strscan.c
@@ -22,7 +22,7 @@ extern size_t onig_region_memsize(const struct re_registers *regs);
#include <stdbool.h>
-#define STRSCAN_VERSION "3.0.7"
+#define STRSCAN_VERSION "3.1.1"
/* =======================================================================
Data Type Definitions
@@ -539,6 +539,68 @@ adjust_register_position(struct strscanner *p, long position)
}
}
+/* rb_reg_onig_match is available in Ruby 3.3 and later. */
+#ifndef HAVE_RB_REG_ONIG_MATCH
+static OnigPosition
+rb_reg_onig_match(VALUE re, VALUE str,
+ OnigPosition (*match)(regex_t *reg, VALUE str, struct re_registers *regs, void *args),
+ void *args, struct re_registers *regs)
+{
+ regex_t *reg = rb_reg_prepare_re(re, str);
+
+ bool tmpreg = reg != RREGEXP_PTR(re);
+ if (!tmpreg) RREGEXP(re)->usecnt++;
+
+ OnigPosition result = match(reg, str, regs, args);
+
+ if (!tmpreg) RREGEXP(re)->usecnt--;
+ if (tmpreg) {
+ if (RREGEXP(re)->usecnt) {
+ onig_free(reg);
+ }
+ else {
+ onig_free(RREGEXP_PTR(re));
+ RREGEXP_PTR(re) = reg;
+ }
+ }
+
+ if (result < 0) {
+ if (result != ONIG_MISMATCH) {
+ rb_raise(ScanError, "regexp buffer overflow");
+ }
+ }
+
+ return result;
+}
+#endif
+
+static OnigPosition
+strscan_match(regex_t *reg, VALUE str, struct re_registers *regs, void *args_ptr)
+{
+ struct strscanner *p = (struct strscanner *)args_ptr;
+
+ return onig_match(reg,
+ match_target(p),
+ (UChar* )(CURPTR(p) + S_RESTLEN(p)),
+ (UChar* )CURPTR(p),
+ regs,
+ ONIG_OPTION_NONE);
+}
+
+static OnigPosition
+strscan_search(regex_t *reg, VALUE str, struct re_registers *regs, void *args_ptr)
+{
+ struct strscanner *p = (struct strscanner *)args_ptr;
+
+ return onig_search(reg,
+ match_target(p),
+ (UChar *)(CURPTR(p) + S_RESTLEN(p)),
+ (UChar *)CURPTR(p),
+ (UChar *)(CURPTR(p) + S_RESTLEN(p)),
+ regs,
+ ONIG_OPTION_NONE);
+}
+
static VALUE
strscan_do_scan(VALUE self, VALUE pattern, int succptr, int getstr, int headonly)
{
@@ -560,47 +622,14 @@ strscan_do_scan(VALUE self, VALUE pattern, int succptr, int getstr, int headonly
}
if (RB_TYPE_P(pattern, T_REGEXP)) {
- regex_t *rb_reg_prepare_re(VALUE re, VALUE str);
- regex_t *re;
- long ret;
- int tmpreg;
-
p->regex = pattern;
- re = rb_reg_prepare_re(pattern, p->str);
- tmpreg = re != RREGEXP_PTR(pattern);
- if (!tmpreg) RREGEXP(pattern)->usecnt++;
-
- if (headonly) {
- ret = onig_match(re,
- match_target(p),
- (UChar* )(CURPTR(p) + S_RESTLEN(p)),
- (UChar* )CURPTR(p),
- &(p->regs),
- ONIG_OPTION_NONE);
- }
- else {
- ret = onig_search(re,
- match_target(p),
- (UChar* )(CURPTR(p) + S_RESTLEN(p)),
- (UChar* )CURPTR(p),
- (UChar* )(CURPTR(p) + S_RESTLEN(p)),
- &(p->regs),
- ONIG_OPTION_NONE);
- }
- if (!tmpreg) RREGEXP(pattern)->usecnt--;
- if (tmpreg) {
- if (RREGEXP(pattern)->usecnt) {
- onig_free(re);
- }
- else {
- onig_free(RREGEXP_PTR(pattern));
- RREGEXP_PTR(pattern) = re;
- }
- }
+ OnigPosition ret = rb_reg_onig_match(pattern,
+ p->str,
+ headonly ? strscan_match : strscan_search,
+ (void *)p,
+ &(p->regs));
- if (ret == -2) rb_raise(ScanError, "regexp buffer overflow");
- if (ret < 0) {
- /* not matched */
+ if (ret == ONIG_MISMATCH) {
return Qnil;
}
}
@@ -874,6 +903,57 @@ strscan_getch(VALUE self)
}
/*
+ * Scans one byte and returns it as an integer.
+ * This method is not multibyte character sensitive.
+ * See also: #getch.
+ *
+ * s = StringScanner.new('ab')
+ * s.scan_byte # => 97
+ * s.scan_byte # => 98
+ * s.scan_byte # => nil
+ *
+ * s = StringScanner.new("\244\242".force_encoding("euc-jp"))
+ * s.scan_byte # => 0xA4
+ * s.scan_byte # => 0xA2
+ * s.scan_byte # => nil
+ */
+static VALUE
+strscan_scan_byte(VALUE self)
+{
+ struct strscanner *p;
+
+ GET_SCANNER(self, p);
+ CLEAR_MATCH_STATUS(p);
+ if (EOS_P(p))
+ return Qnil;
+
+ VALUE byte = INT2FIX((unsigned char)*CURPTR(p));
+ p->prev = p->curr;
+ p->curr++;
+ MATCHED(p);
+ adjust_registers_to_matched(p);
+ return byte;
+}
+
+/*
+ * Peeks at the current byte and returns it as an integer.
+ *
+ * s = StringScanner.new('ab')
+ * s.peek_byte # => 97
+ */
+static VALUE
+strscan_peek_byte(VALUE self)
+{
+ struct strscanner *p;
+
+ GET_SCANNER(self, p);
+ if (EOS_P(p))
+ return Qnil;
+
+ return INT2FIX((unsigned char)*CURPTR(p));
+}
+
+/*
* Scans one byte and returns it.
* This method is not multibyte character sensitive.
* See also: #getch.
@@ -1214,10 +1294,10 @@ strscan_size(VALUE self)
* If nothing was priorly matched, it returns nil.
*
* s = StringScanner.new("Fri Dec 12 1975 14:39")
- * s.scan(/(\w+) (\w+) (\d+) /) # -> "Fri Dec 12 "
- * s.captures # -> ["Fri", "Dec", "12"]
- * s.scan(/(\w+) (\w+) (\d+) /) # -> nil
- * s.captures # -> nil
+ * s.scan(/(\w+) (\w+) (\d+) (1980)?/) # -> "Fri Dec 12 "
+ * s.captures # -> ["Fri", "Dec", "12", nil]
+ * s.scan(/(\w+) (\w+) (\d+) (1980)?/) # -> nil
+ * s.captures # -> nil
*/
static VALUE
strscan_captures(VALUE self)
@@ -1233,9 +1313,13 @@ strscan_captures(VALUE self)
new_ary = rb_ary_new2(num_regs);
for (i = 1; i < num_regs; i++) {
- VALUE str = extract_range(p,
- adjust_register_position(p, p->regs.beg[i]),
- adjust_register_position(p, p->regs.end[i]));
+ VALUE str;
+ if (p->regs.beg[i] == -1)
+ str = Qnil;
+ else
+ str = extract_range(p,
+ adjust_register_position(p, p->regs.beg[i]),
+ adjust_register_position(p, p->regs.end[i]));
rb_ary_push(new_ary, str);
}
@@ -1502,7 +1586,9 @@ strscan_named_captures(VALUE self)
named_captures_data data;
data.self = self;
data.captures = rb_hash_new();
- onig_foreach_name(RREGEXP_PTR(p->regex), named_captures_iter, &data);
+ if (!RB_NIL_P(p->regex)) {
+ onig_foreach_name(RREGEXP_PTR(p->regex), named_captures_iter, &data);
+ }
return data.captures;
}
@@ -1570,6 +1656,7 @@ strscan_named_captures(VALUE self)
*
* - #getch
* - #get_byte
+ * - #scan_byte
* - #scan
* - #scan_until
* - #skip
@@ -1582,6 +1669,7 @@ strscan_named_captures(VALUE self)
* - #exist?
* - #match?
* - #peek
+ * - #peek_byte
*
* === Finding Where we Are
*
@@ -1673,7 +1761,9 @@ Init_strscan(void)
rb_define_method(StringScanner, "getch", strscan_getch, 0);
rb_define_method(StringScanner, "get_byte", strscan_get_byte, 0);
rb_define_method(StringScanner, "getbyte", strscan_getbyte, 0);
+ rb_define_method(StringScanner, "scan_byte", strscan_scan_byte, 0);
rb_define_method(StringScanner, "peek", strscan_peek, 1);
+ rb_define_method(StringScanner, "peek_byte", strscan_peek_byte, 0);
rb_define_method(StringScanner, "peep", strscan_peep, 1);
rb_define_method(StringScanner, "unscan", strscan_unscan, 0);
diff --git a/ext/syslog/depend b/ext/syslog/depend
deleted file mode 100644
index ee4ac5f47d..0000000000
--- a/ext/syslog/depend
+++ /dev/null
@@ -1,161 +0,0 @@
-# AUTOGENERATED DEPENDENCIES START
-syslog.o: $(RUBY_EXTCONF_H)
-syslog.o: $(arch_hdrdir)/ruby/config.h
-syslog.o: $(hdrdir)/ruby/assert.h
-syslog.o: $(hdrdir)/ruby/backward.h
-syslog.o: $(hdrdir)/ruby/backward/2/assume.h
-syslog.o: $(hdrdir)/ruby/backward/2/attributes.h
-syslog.o: $(hdrdir)/ruby/backward/2/bool.h
-syslog.o: $(hdrdir)/ruby/backward/2/inttypes.h
-syslog.o: $(hdrdir)/ruby/backward/2/limits.h
-syslog.o: $(hdrdir)/ruby/backward/2/long_long.h
-syslog.o: $(hdrdir)/ruby/backward/2/stdalign.h
-syslog.o: $(hdrdir)/ruby/backward/2/stdarg.h
-syslog.o: $(hdrdir)/ruby/defines.h
-syslog.o: $(hdrdir)/ruby/intern.h
-syslog.o: $(hdrdir)/ruby/internal/abi.h
-syslog.o: $(hdrdir)/ruby/internal/anyargs.h
-syslog.o: $(hdrdir)/ruby/internal/arithmetic.h
-syslog.o: $(hdrdir)/ruby/internal/arithmetic/char.h
-syslog.o: $(hdrdir)/ruby/internal/arithmetic/double.h
-syslog.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h
-syslog.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h
-syslog.o: $(hdrdir)/ruby/internal/arithmetic/int.h
-syslog.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h
-syslog.o: $(hdrdir)/ruby/internal/arithmetic/long.h
-syslog.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h
-syslog.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h
-syslog.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h
-syslog.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h
-syslog.o: $(hdrdir)/ruby/internal/arithmetic/short.h
-syslog.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h
-syslog.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h
-syslog.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h
-syslog.o: $(hdrdir)/ruby/internal/assume.h
-syslog.o: $(hdrdir)/ruby/internal/attr/alloc_size.h
-syslog.o: $(hdrdir)/ruby/internal/attr/artificial.h
-syslog.o: $(hdrdir)/ruby/internal/attr/cold.h
-syslog.o: $(hdrdir)/ruby/internal/attr/const.h
-syslog.o: $(hdrdir)/ruby/internal/attr/constexpr.h
-syslog.o: $(hdrdir)/ruby/internal/attr/deprecated.h
-syslog.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h
-syslog.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h
-syslog.o: $(hdrdir)/ruby/internal/attr/error.h
-syslog.o: $(hdrdir)/ruby/internal/attr/flag_enum.h
-syslog.o: $(hdrdir)/ruby/internal/attr/forceinline.h
-syslog.o: $(hdrdir)/ruby/internal/attr/format.h
-syslog.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h
-syslog.o: $(hdrdir)/ruby/internal/attr/noalias.h
-syslog.o: $(hdrdir)/ruby/internal/attr/nodiscard.h
-syslog.o: $(hdrdir)/ruby/internal/attr/noexcept.h
-syslog.o: $(hdrdir)/ruby/internal/attr/noinline.h
-syslog.o: $(hdrdir)/ruby/internal/attr/nonnull.h
-syslog.o: $(hdrdir)/ruby/internal/attr/noreturn.h
-syslog.o: $(hdrdir)/ruby/internal/attr/packed_struct.h
-syslog.o: $(hdrdir)/ruby/internal/attr/pure.h
-syslog.o: $(hdrdir)/ruby/internal/attr/restrict.h
-syslog.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h
-syslog.o: $(hdrdir)/ruby/internal/attr/warning.h
-syslog.o: $(hdrdir)/ruby/internal/attr/weakref.h
-syslog.o: $(hdrdir)/ruby/internal/cast.h
-syslog.o: $(hdrdir)/ruby/internal/compiler_is.h
-syslog.o: $(hdrdir)/ruby/internal/compiler_is/apple.h
-syslog.o: $(hdrdir)/ruby/internal/compiler_is/clang.h
-syslog.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h
-syslog.o: $(hdrdir)/ruby/internal/compiler_is/intel.h
-syslog.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h
-syslog.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h
-syslog.o: $(hdrdir)/ruby/internal/compiler_since.h
-syslog.o: $(hdrdir)/ruby/internal/config.h
-syslog.o: $(hdrdir)/ruby/internal/constant_p.h
-syslog.o: $(hdrdir)/ruby/internal/core.h
-syslog.o: $(hdrdir)/ruby/internal/core/rarray.h
-syslog.o: $(hdrdir)/ruby/internal/core/rbasic.h
-syslog.o: $(hdrdir)/ruby/internal/core/rbignum.h
-syslog.o: $(hdrdir)/ruby/internal/core/rclass.h
-syslog.o: $(hdrdir)/ruby/internal/core/rdata.h
-syslog.o: $(hdrdir)/ruby/internal/core/rfile.h
-syslog.o: $(hdrdir)/ruby/internal/core/rhash.h
-syslog.o: $(hdrdir)/ruby/internal/core/robject.h
-syslog.o: $(hdrdir)/ruby/internal/core/rregexp.h
-syslog.o: $(hdrdir)/ruby/internal/core/rstring.h
-syslog.o: $(hdrdir)/ruby/internal/core/rstruct.h
-syslog.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
-syslog.o: $(hdrdir)/ruby/internal/ctype.h
-syslog.o: $(hdrdir)/ruby/internal/dllexport.h
-syslog.o: $(hdrdir)/ruby/internal/dosish.h
-syslog.o: $(hdrdir)/ruby/internal/error.h
-syslog.o: $(hdrdir)/ruby/internal/eval.h
-syslog.o: $(hdrdir)/ruby/internal/event.h
-syslog.o: $(hdrdir)/ruby/internal/fl_type.h
-syslog.o: $(hdrdir)/ruby/internal/gc.h
-syslog.o: $(hdrdir)/ruby/internal/glob.h
-syslog.o: $(hdrdir)/ruby/internal/globals.h
-syslog.o: $(hdrdir)/ruby/internal/has/attribute.h
-syslog.o: $(hdrdir)/ruby/internal/has/builtin.h
-syslog.o: $(hdrdir)/ruby/internal/has/c_attribute.h
-syslog.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h
-syslog.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h
-syslog.o: $(hdrdir)/ruby/internal/has/extension.h
-syslog.o: $(hdrdir)/ruby/internal/has/feature.h
-syslog.o: $(hdrdir)/ruby/internal/has/warning.h
-syslog.o: $(hdrdir)/ruby/internal/intern/array.h
-syslog.o: $(hdrdir)/ruby/internal/intern/bignum.h
-syslog.o: $(hdrdir)/ruby/internal/intern/class.h
-syslog.o: $(hdrdir)/ruby/internal/intern/compar.h
-syslog.o: $(hdrdir)/ruby/internal/intern/complex.h
-syslog.o: $(hdrdir)/ruby/internal/intern/cont.h
-syslog.o: $(hdrdir)/ruby/internal/intern/dir.h
-syslog.o: $(hdrdir)/ruby/internal/intern/enum.h
-syslog.o: $(hdrdir)/ruby/internal/intern/enumerator.h
-syslog.o: $(hdrdir)/ruby/internal/intern/error.h
-syslog.o: $(hdrdir)/ruby/internal/intern/eval.h
-syslog.o: $(hdrdir)/ruby/internal/intern/file.h
-syslog.o: $(hdrdir)/ruby/internal/intern/hash.h
-syslog.o: $(hdrdir)/ruby/internal/intern/io.h
-syslog.o: $(hdrdir)/ruby/internal/intern/load.h
-syslog.o: $(hdrdir)/ruby/internal/intern/marshal.h
-syslog.o: $(hdrdir)/ruby/internal/intern/numeric.h
-syslog.o: $(hdrdir)/ruby/internal/intern/object.h
-syslog.o: $(hdrdir)/ruby/internal/intern/parse.h
-syslog.o: $(hdrdir)/ruby/internal/intern/proc.h
-syslog.o: $(hdrdir)/ruby/internal/intern/process.h
-syslog.o: $(hdrdir)/ruby/internal/intern/random.h
-syslog.o: $(hdrdir)/ruby/internal/intern/range.h
-syslog.o: $(hdrdir)/ruby/internal/intern/rational.h
-syslog.o: $(hdrdir)/ruby/internal/intern/re.h
-syslog.o: $(hdrdir)/ruby/internal/intern/ruby.h
-syslog.o: $(hdrdir)/ruby/internal/intern/select.h
-syslog.o: $(hdrdir)/ruby/internal/intern/select/largesize.h
-syslog.o: $(hdrdir)/ruby/internal/intern/signal.h
-syslog.o: $(hdrdir)/ruby/internal/intern/sprintf.h
-syslog.o: $(hdrdir)/ruby/internal/intern/string.h
-syslog.o: $(hdrdir)/ruby/internal/intern/struct.h
-syslog.o: $(hdrdir)/ruby/internal/intern/thread.h
-syslog.o: $(hdrdir)/ruby/internal/intern/time.h
-syslog.o: $(hdrdir)/ruby/internal/intern/variable.h
-syslog.o: $(hdrdir)/ruby/internal/intern/vm.h
-syslog.o: $(hdrdir)/ruby/internal/interpreter.h
-syslog.o: $(hdrdir)/ruby/internal/iterator.h
-syslog.o: $(hdrdir)/ruby/internal/memory.h
-syslog.o: $(hdrdir)/ruby/internal/method.h
-syslog.o: $(hdrdir)/ruby/internal/module.h
-syslog.o: $(hdrdir)/ruby/internal/newobj.h
-syslog.o: $(hdrdir)/ruby/internal/scan_args.h
-syslog.o: $(hdrdir)/ruby/internal/special_consts.h
-syslog.o: $(hdrdir)/ruby/internal/static_assert.h
-syslog.o: $(hdrdir)/ruby/internal/stdalign.h
-syslog.o: $(hdrdir)/ruby/internal/stdbool.h
-syslog.o: $(hdrdir)/ruby/internal/symbol.h
-syslog.o: $(hdrdir)/ruby/internal/value.h
-syslog.o: $(hdrdir)/ruby/internal/value_type.h
-syslog.o: $(hdrdir)/ruby/internal/variable.h
-syslog.o: $(hdrdir)/ruby/internal/warning_push.h
-syslog.o: $(hdrdir)/ruby/internal/xmalloc.h
-syslog.o: $(hdrdir)/ruby/missing.h
-syslog.o: $(hdrdir)/ruby/ruby.h
-syslog.o: $(hdrdir)/ruby/st.h
-syslog.o: $(hdrdir)/ruby/subst.h
-syslog.o: $(hdrdir)/ruby/util.h
-syslog.o: syslog.c
-# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/syslog/extconf.rb b/ext/syslog/extconf.rb
deleted file mode 100644
index 1230a4d52e..0000000000
--- a/ext/syslog/extconf.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-# frozen_string_literal: false
-# $RoughId: extconf.rb,v 1.3 2001/11/24 17:49:26 knu Exp $
-# $Id$
-
-require 'mkmf'
-
-have_library("log") # for Android
-
-have_header("syslog.h") &&
- have_func("openlog") &&
- have_func("setlogmask") &&
- create_makefile("syslog")
-
diff --git a/ext/syslog/lib/syslog/logger.rb b/ext/syslog/lib/syslog/logger.rb
deleted file mode 100644
index 453ca2785c..0000000000
--- a/ext/syslog/lib/syslog/logger.rb
+++ /dev/null
@@ -1,209 +0,0 @@
-# frozen_string_literal: false
-require 'syslog'
-require 'logger'
-
-##
-# Syslog::Logger is a Logger work-alike that logs via syslog instead of to a
-# file. You can use Syslog::Logger to aggregate logs between multiple
-# machines.
-#
-# By default, Syslog::Logger uses the program name 'ruby', but this can be
-# changed via the first argument to Syslog::Logger.new.
-#
-# NOTE! You can only set the Syslog::Logger program name when you initialize
-# Syslog::Logger for the first time. This is a limitation of the way
-# Syslog::Logger uses syslog (and in some ways, a limitation of the way
-# syslog(3) works). Attempts to change Syslog::Logger's program name after
-# the first initialization will be ignored.
-#
-# === Example
-#
-# The following will log to syslogd on your local machine:
-#
-# require 'syslog/logger'
-#
-# log = Syslog::Logger.new 'my_program'
-# log.info 'this line will be logged via syslog(3)'
-#
-# Also the facility may be set to specify the facility level which will be used:
-#
-# log.info 'this line will be logged using Syslog default facility level'
-#
-# log_local1 = Syslog::Logger.new 'my_program', Syslog::LOG_LOCAL1
-# log_local1.info 'this line will be logged using local1 facility level'
-#
-#
-# You may need to perform some syslog.conf setup first. For a BSD machine add
-# the following lines to /etc/syslog.conf:
-#
-# !my_program
-# *.* /var/log/my_program.log
-#
-# Then touch /var/log/my_program.log and signal syslogd with a HUP
-# (killall -HUP syslogd, on FreeBSD).
-#
-# If you wish to have logs automatically roll over and archive, see the
-# newsyslog.conf(5) and newsyslog(8) man pages.
-
-class Syslog::Logger
- # Default formatter for log messages.
- class Formatter
- def call severity, time, progname, msg
- clean msg
- end
-
- private
-
- ##
- # Clean up messages so they're nice and pretty.
-
- def clean message
- message = message.to_s.strip
- message.gsub!(/\e\[[0-9;]*m/, '') # remove useless ansi color codes
- return message
- end
- end
-
- ##
- # The version of Syslog::Logger you are using.
-
- VERSION = '2.1.0'
-
- ##
- # Maps Logger warning types to syslog(3) warning types.
- #
- # Messages from Ruby applications are not considered as critical as messages
- # from other system daemons using syslog(3), so most messages are reduced by
- # one level. For example, a fatal message for Ruby's Logger is considered
- # an error for syslog(3).
-
- LEVEL_MAP = {
- ::Logger::UNKNOWN => Syslog::LOG_ALERT,
- ::Logger::FATAL => Syslog::LOG_ERR,
- ::Logger::ERROR => Syslog::LOG_WARNING,
- ::Logger::WARN => Syslog::LOG_NOTICE,
- ::Logger::INFO => Syslog::LOG_INFO,
- ::Logger::DEBUG => Syslog::LOG_DEBUG,
- }
-
- ##
- # Returns the internal Syslog object that is initialized when the
- # first instance is created.
-
- def self.syslog
- @@syslog
- end
-
- ##
- # Specifies the internal Syslog object to be used.
-
- def self.syslog= syslog
- @@syslog = syslog
- end
-
- ##
- # Builds a methods for level +meth+.
-
- def self.make_methods meth
- level = ::Logger.const_get(meth.upcase)
- eval <<-EOM, nil, __FILE__, __LINE__ + 1
- def #{meth}(message = nil, &block)
- add(#{level}, message, &block)
- end
-
- def #{meth}?
- level <= #{level}
- end
- EOM
- end
-
- ##
- # :method: unknown
- #
- # Logs a +message+ at the unknown (syslog alert) log level, or logs the
- # message returned from the block.
-
- ##
- # :method: fatal
- #
- # Logs a +message+ at the fatal (syslog err) log level, or logs the message
- # returned from the block.
-
- ##
- # :method: error
- #
- # Logs a +message+ at the error (syslog warning) log level, or logs the
- # message returned from the block.
-
- ##
- # :method: warn
- #
- # Logs a +message+ at the warn (syslog notice) log level, or logs the
- # message returned from the block.
-
- ##
- # :method: info
- #
- # Logs a +message+ at the info (syslog info) log level, or logs the message
- # returned from the block.
-
- ##
- # :method: debug
- #
- # Logs a +message+ at the debug (syslog debug) log level, or logs the
- # message returned from the block.
-
- Logger::Severity::constants.each do |severity|
- make_methods severity.downcase
- end
-
- ##
- # Log level for Logger compatibility.
-
- attr_accessor :level
-
- # Logging formatter, as a +Proc+ that will take four arguments and
- # return the formatted message. The arguments are:
- #
- # +severity+:: The Severity of the log message.
- # +time+:: A Time instance representing when the message was logged.
- # +progname+:: The #progname configured, or passed to the logger method.
- # +msg+:: The _Object_ the user passed to the log message; not necessarily a
- # String.
- #
- # The block should return an Object that can be written to the logging
- # device via +write+. The default formatter is used when no formatter is
- # set.
- attr_accessor :formatter
-
- ##
- # The facility argument is used to specify what type of program is logging the message.
-
- attr_accessor :facility
-
- ##
- # Fills in variables for Logger compatibility. If this is the first
- # instance of Syslog::Logger, +program_name+ may be set to change the logged
- # program name. The +facility+ may be set to specify the facility level which will be used.
- #
- # Due to the way syslog works, only one program name may be chosen.
-
- def initialize program_name = 'ruby', facility = nil
- @level = ::Logger::DEBUG
- @formatter = Formatter.new
-
- @@syslog ||= Syslog.open(program_name)
-
- @facility = (facility || @@syslog.facility)
- end
-
- ##
- # Almost duplicates Logger#add. +progname+ is ignored.
-
- def add severity, message = nil, progname = nil, &block
- severity ||= ::Logger::UNKNOWN
- level <= severity and
- @@syslog.log( (LEVEL_MAP[severity] | @facility), '%s', formatter.call(severity, Time.now, progname, (message || block.call)) )
- true
- end
-end
diff --git a/ext/syslog/syslog.c b/ext/syslog/syslog.c
deleted file mode 100644
index 6a97c15811..0000000000
--- a/ext/syslog/syslog.c
+++ /dev/null
@@ -1,592 +0,0 @@
-/*
- * UNIX Syslog extension for Ruby
- * Amos Gouaux, University of Texas at Dallas
- * <amos+ruby@utdallas.edu>
- * Documented by mathew <meta@pobox.com>
- *
- * $RoughId: syslog.c,v 1.21 2002/02/25 12:21:17 knu Exp $
- * $Id$
- */
-
-#include "ruby/ruby.h"
-#include "ruby/util.h"
-#include <syslog.h>
-
-#define SYSLOG_VERSION "0.1.1"
-
-/* Syslog class */
-static VALUE mSyslog;
-/*
- * Module holding all Syslog constants. See Syslog::log and
- * Syslog::open for constant descriptions.
- */
-static VALUE mSyslogConstants;
-/* Module holding Syslog option constants */
-static VALUE mSyslogOption;
-/* Module holding Syslog facility constants */
-static VALUE mSyslogFacility;
-/* Module holding Syslog level constants */
-static VALUE mSyslogLevel;
-/* Module holding Syslog utility macros */
-static VALUE mSyslogMacros;
-
-static const char *syslog_ident = NULL;
-static int syslog_options = -1, syslog_facility = -1, syslog_mask = -1;
-static int syslog_opened = 0;
-
-/* Package helper routines */
-static void syslog_write(int pri, int argc, VALUE *argv)
-{
- VALUE str;
-
- if (argc < 1) {
- rb_raise(rb_eArgError, "no log message supplied");
- }
-
- if (!syslog_opened) {
- rb_raise(rb_eRuntimeError, "must open syslog before write");
- }
-
- str = rb_f_sprintf(argc, argv);
-
- syslog(pri, "%s", RSTRING_PTR(str));
-}
-
-/* Closes the syslog facility.
- * Raises a runtime exception if it is not open.
- */
-static VALUE mSyslog_close(VALUE self)
-{
- if (!syslog_opened) {
- rb_raise(rb_eRuntimeError, "syslog not opened");
- }
-
- closelog();
-
- xfree((void *)syslog_ident);
- syslog_ident = NULL;
- syslog_options = syslog_facility = syslog_mask = -1;
- syslog_opened = 0;
-
- return Qnil;
-}
-
-/* call-seq:
- * open(ident, options, facility) => syslog
- *
- * :yields: syslog
- *
- * Open the syslog facility.
- * Raises a runtime exception if it is already open.
- *
- * Can be called with or without a code block. If called with a block, the
- * Syslog object created is passed to the block.
- *
- * If the syslog is already open, raises a RuntimeError.
- *
- * +ident+ is a String which identifies the calling program.
- *
- * +options+ is the logical OR of any of the following:
- *
- * LOG_CONS:: If there is an error while sending to the system logger,
- * write directly to the console instead.
- *
- * LOG_NDELAY:: Open the connection now, rather than waiting for the first
- * message to be written.
- *
- * LOG_NOWAIT:: Don't wait for any child processes created while logging
- * messages. (Has no effect on Linux.)
- *
- * LOG_ODELAY:: Opposite of LOG_NDELAY; wait until a message is sent before
- * opening the connection. (This is the default.)
- *
- * LOG_PERROR:: Print the message to stderr as well as sending it to syslog.
- * (Not in POSIX.1-2001.)
- *
- * LOG_PID:: Include the current process ID with each message.
- *
- * +facility+ describes the type of program opening the syslog, and is
- * the logical OR of any of the following which are defined for the host OS:
- *
- * LOG_AUTH:: Security or authorization. Deprecated, use LOG_AUTHPRIV
- * instead.
- *
- * LOG_AUTHPRIV:: Security or authorization messages which should be kept
- * private.
- *
- * LOG_CONSOLE:: System console message.
- *
- * LOG_CRON:: System task scheduler (cron or at).
- *
- * LOG_DAEMON:: A system daemon which has no facility value of its own.
- *
- * LOG_FTP:: An FTP server.
- *
- * LOG_KERN:: A kernel message (not sendable by user processes, so not of
- * much use to Ruby, but listed here for completeness).
- *
- * LOG_LPR:: Line printer subsystem.
- *
- * LOG_MAIL:: Mail delivery or transport subsystem.
- *
- * LOG_NEWS:: Usenet news system.
- *
- * LOG_NTP:: Network Time Protocol server.
- *
- * LOG_SECURITY:: General security message.
- *
- * LOG_SYSLOG:: Messages generated internally by syslog.
- *
- * LOG_USER:: Generic user-level message.
- *
- * LOG_UUCP:: UUCP subsystem.
- *
- * LOG_LOCAL0 to LOG_LOCAL7:: Locally-defined facilities.
- *
- * Example:
- *
- * Syslog.open("webrick", Syslog::LOG_PID,
- * Syslog::LOG_DAEMON | Syslog::LOG_LOCAL3)
- *
- */
-static VALUE mSyslog_open(int argc, VALUE *argv, VALUE self)
-{
- VALUE ident, opt, fac;
- const char *ident_ptr;
-
- if (syslog_opened) {
- rb_raise(rb_eRuntimeError, "syslog already open");
- }
-
- rb_scan_args(argc, argv, "03", &ident, &opt, &fac);
-
- if (NIL_P(ident)) {
- ident = rb_gv_get("$0");
- }
- ident_ptr = StringValueCStr(ident);
- syslog_ident = strdup(ident_ptr);
-
- if (NIL_P(opt)) {
- syslog_options = LOG_PID | LOG_CONS;
- } else {
- syslog_options = NUM2INT(opt);
- }
-
- if (NIL_P(fac)) {
- syslog_facility = LOG_USER;
- } else {
- syslog_facility = NUM2INT(fac);
- }
-
- openlog(syslog_ident, syslog_options, syslog_facility);
-
- syslog_opened = 1;
-
- setlogmask(syslog_mask = setlogmask(0));
-
- /* be like File.new.open {...} */
- if (rb_block_given_p()) {
- rb_ensure(rb_yield, self, mSyslog_close, self);
- }
-
- return self;
-}
-
-/* call-seq:
- * reopen(ident, options, facility) => syslog
- *
- * :yields: syslog
- *
- * Closes and then reopens the syslog.
- *
- * Arguments are the same as for open().
- */
-static VALUE mSyslog_reopen(int argc, VALUE *argv, VALUE self)
-{
- mSyslog_close(self);
-
- return mSyslog_open(argc, argv, self);
-}
-
-/* call-seq:
- * opened?
- *
- * Returns true if the syslog is open.
- */
-static VALUE mSyslog_isopen(VALUE self)
-{
- return syslog_opened ? Qtrue : Qfalse;
-}
-
-/* Returns the identity string used in the last call to open()
- */
-static VALUE mSyslog_ident(VALUE self)
-{
- return syslog_opened ? rb_str_new2(syslog_ident) : Qnil;
-}
-
-/* Returns the options bitmask used in the last call to open()
- */
-static VALUE mSyslog_options(VALUE self)
-{
- return syslog_opened ? INT2NUM(syslog_options) : Qnil;
-}
-
-/* Returns the facility number used in the last call to open()
- */
-static VALUE mSyslog_facility(VALUE self)
-{
- return syslog_opened ? INT2NUM(syslog_facility) : Qnil;
-}
-
-/* Returns the log priority mask in effect. The mask is not reset by opening
- * or closing syslog.
- */
-static VALUE mSyslog_get_mask(VALUE self)
-{
- return syslog_opened ? INT2NUM(syslog_mask) : Qnil;
-}
-
-/* call-seq:
- * mask=(priority_mask)
- *
- * Sets the log priority mask. A method LOG_UPTO is defined to make it easier
- * to set mask values. Example:
- *
- * Syslog.mask = Syslog::LOG_UPTO(Syslog::LOG_ERR)
- *
- * Alternatively, specific priorities can be selected and added together using
- * binary OR. Example:
- *
- * Syslog.mask = Syslog::LOG_MASK(Syslog::LOG_ERR) | Syslog::LOG_MASK(Syslog::LOG_CRIT)
- *
- * The priority mask persists through calls to open() and close().
- */
-static VALUE mSyslog_set_mask(VALUE self, VALUE mask)
-{
- if (!syslog_opened) {
- rb_raise(rb_eRuntimeError, "must open syslog before setting log mask");
- }
-
- setlogmask(syslog_mask = NUM2INT(mask));
-
- return mask;
-}
-
-/* call-seq:
- * log(priority, format_string, *format_args)
- *
- * Log a message with the specified priority. Example:
- *
- * Syslog.log(Syslog::LOG_CRIT, "Out of disk space")
- * Syslog.log(Syslog::LOG_CRIT, "User %s logged in", ENV['USER'])
- *
- * The priority levels, in descending order, are:
- *
- * LOG_EMERG:: System is unusable
- * LOG_ALERT:: Action needs to be taken immediately
- * LOG_CRIT:: A critical condition has occurred
- * LOG_ERR:: An error occurred
- * LOG_WARNING:: Warning of a possible problem
- * LOG_NOTICE:: A normal but significant condition occurred
- * LOG_INFO:: Informational message
- * LOG_DEBUG:: Debugging information
- *
- * Each priority level also has a shortcut method that logs with it's named priority.
- * As an example, the two following statements would produce the same result:
- *
- * Syslog.log(Syslog::LOG_ALERT, "Out of memory")
- * Syslog.alert("Out of memory")
- *
- */
-static VALUE mSyslog_log(int argc, VALUE *argv, VALUE self)
-{
- VALUE pri;
-
- rb_check_arity(argc, 2, UNLIMITED_ARGUMENTS);
-
- argc--;
- pri = *argv++;
-
- if (!FIXNUM_P(pri)) {
- rb_raise(rb_eTypeError, "type mismatch: %"PRIsVALUE" given", rb_obj_class(pri));
- }
-
- syslog_write(FIX2INT(pri), argc, argv);
-
- return self;
-}
-
-/* Returns an inspect() string summarizing the object state.
- */
-static VALUE mSyslog_inspect(VALUE self)
-{
- Check_Type(self, T_MODULE);
-
- if (!syslog_opened)
- return rb_sprintf("<#%"PRIsVALUE": opened=false>", self);
-
- return rb_sprintf("<#%"PRIsVALUE": opened=true, ident=\"%s\", options=%d, facility=%d, mask=%d>",
- self,
- syslog_ident,
- syslog_options,
- syslog_facility,
- syslog_mask);
-}
-
-/* Returns self, for backward compatibility.
- */
-static VALUE mSyslog_instance(VALUE self)
-{
- return self;
-}
-
-#define define_syslog_shortcut_method(pri, name) \
-static VALUE mSyslog_##name(int argc, VALUE *argv, VALUE self) \
-{ \
- syslog_write((pri), argc, argv); \
-\
- return self; \
-}
-
-#ifdef LOG_EMERG
-define_syslog_shortcut_method(LOG_EMERG, emerg)
-#endif
-#ifdef LOG_ALERT
-define_syslog_shortcut_method(LOG_ALERT, alert)
-#endif
-#ifdef LOG_CRIT
-define_syslog_shortcut_method(LOG_CRIT, crit)
-#endif
-#ifdef LOG_ERR
-define_syslog_shortcut_method(LOG_ERR, err)
-#endif
-#ifdef LOG_WARNING
-define_syslog_shortcut_method(LOG_WARNING, warning)
-#endif
-#ifdef LOG_NOTICE
-define_syslog_shortcut_method(LOG_NOTICE, notice)
-#endif
-#ifdef LOG_INFO
-define_syslog_shortcut_method(LOG_INFO, info)
-#endif
-#ifdef LOG_DEBUG
-define_syslog_shortcut_method(LOG_DEBUG, debug)
-#endif
-
-/* call-seq:
- * LOG_MASK(priority_level) => priority_mask
- *
- * Generates a mask bit for a priority level. See #mask=
- */
-static VALUE mSyslogMacros_LOG_MASK(VALUE mod, VALUE pri)
-{
- return INT2FIX(LOG_MASK(NUM2INT(pri)));
-}
-
-/* call-seq:
- * LOG_UPTO(priority_level) => priority_mask
- *
- * Generates a mask value for priority levels at or below the level specified.
- * See #mask=
- */
-static VALUE mSyslogMacros_LOG_UPTO(VALUE mod, VALUE pri)
-{
- return INT2FIX(LOG_UPTO(NUM2INT(pri)));
-}
-
-static VALUE mSyslogMacros_included(VALUE mod, VALUE target)
-{
- rb_extend_object(target, mSyslogMacros);
- return mod;
-}
-
-/* The syslog package provides a Ruby interface to the POSIX system logging
- * facility.
- *
- * Syslog messages are typically passed to a central logging daemon.
- * The daemon may filter them; route them into different files (usually
- * found under /var/log); place them in SQL databases; forward
- * them to centralized logging servers via TCP or UDP; or even alert the
- * system administrator via email, pager or text message.
- *
- * Unlike application-level logging via Logger or Log4r, syslog is designed
- * to allow secure tamper-proof logging.
- *
- * The syslog protocol is standardized in RFC 5424.
- */
-void Init_syslog(void)
-{
-#undef rb_intern
- mSyslog = rb_define_module("Syslog");
-
- mSyslogConstants = rb_define_module_under(mSyslog, "Constants");
-
- mSyslogOption = rb_define_module_under(mSyslog, "Option");
- mSyslogFacility = rb_define_module_under(mSyslog, "Facility");
- mSyslogLevel = rb_define_module_under(mSyslog, "Level");
- mSyslogMacros = rb_define_module_under(mSyslog, "Macros");
-
- rb_define_module_function(mSyslog, "open", mSyslog_open, -1);
- rb_define_module_function(mSyslog, "reopen", mSyslog_reopen, -1);
- rb_define_module_function(mSyslog, "open!", mSyslog_reopen, -1);
- rb_define_module_function(mSyslog, "opened?", mSyslog_isopen, 0);
-
- rb_define_module_function(mSyslog, "ident", mSyslog_ident, 0);
- rb_define_module_function(mSyslog, "options", mSyslog_options, 0);
- rb_define_module_function(mSyslog, "facility", mSyslog_facility, 0);
-
- rb_define_module_function(mSyslog, "log", mSyslog_log, -1);
- rb_define_module_function(mSyslog, "close", mSyslog_close, 0);
- rb_define_module_function(mSyslog, "mask", mSyslog_get_mask, 0);
- rb_define_module_function(mSyslog, "mask=", mSyslog_set_mask, 1);
-
- rb_define_singleton_method(mSyslog, "inspect", mSyslog_inspect, 0);
- rb_define_module_function(mSyslog, "instance", mSyslog_instance, 0);
-
- /* Syslog options */
-
-#define rb_define_syslog_option(c) \
- rb_define_const(mSyslogOption, #c, INT2NUM(c))
-
-#ifdef LOG_PID
- rb_define_syslog_option(LOG_PID);
-#endif
-#ifdef LOG_CONS
- rb_define_syslog_option(LOG_CONS);
-#endif
-#ifdef LOG_ODELAY
- rb_define_syslog_option(LOG_ODELAY); /* deprecated */
-#endif
-#ifdef LOG_NDELAY
- rb_define_syslog_option(LOG_NDELAY);
-#endif
-#ifdef LOG_NOWAIT
- rb_define_syslog_option(LOG_NOWAIT); /* deprecated */
-#endif
-#ifdef LOG_PERROR
- rb_define_syslog_option(LOG_PERROR);
-#endif
-
- /* Syslog facilities */
-
-#define rb_define_syslog_facility(c) \
- rb_define_const(mSyslogFacility, #c, INT2NUM(c))
-
-#ifdef LOG_AUTH
- rb_define_syslog_facility(LOG_AUTH);
-#endif
-#ifdef LOG_AUTHPRIV
- rb_define_syslog_facility(LOG_AUTHPRIV);
-#endif
-#ifdef LOG_CONSOLE
- rb_define_syslog_facility(LOG_CONSOLE);
-#endif
-#ifdef LOG_CRON
- rb_define_syslog_facility(LOG_CRON);
-#endif
-#ifdef LOG_DAEMON
- rb_define_syslog_facility(LOG_DAEMON);
-#endif
-#ifdef LOG_FTP
- rb_define_syslog_facility(LOG_FTP);
-#endif
-#ifdef LOG_KERN
- rb_define_syslog_facility(LOG_KERN);
-#endif
-#ifdef LOG_LPR
- rb_define_syslog_facility(LOG_LPR);
-#endif
-#ifdef LOG_MAIL
- rb_define_syslog_facility(LOG_MAIL);
-#endif
-#ifdef LOG_NEWS
- rb_define_syslog_facility(LOG_NEWS);
-#endif
-#ifdef LOG_NTP
- rb_define_syslog_facility(LOG_NTP);
-#endif
-#ifdef LOG_SECURITY
- rb_define_syslog_facility(LOG_SECURITY);
-#endif
-#ifdef LOG_SYSLOG
- rb_define_syslog_facility(LOG_SYSLOG);
-#endif
-#ifdef LOG_USER
- rb_define_syslog_facility(LOG_USER);
-#endif
-#ifdef LOG_UUCP
- rb_define_syslog_facility(LOG_UUCP);
-#endif
-#ifdef LOG_LOCAL0
- rb_define_syslog_facility(LOG_LOCAL0);
-#endif
-#ifdef LOG_LOCAL1
- rb_define_syslog_facility(LOG_LOCAL1);
-#endif
-#ifdef LOG_LOCAL2
- rb_define_syslog_facility(LOG_LOCAL2);
-#endif
-#ifdef LOG_LOCAL3
- rb_define_syslog_facility(LOG_LOCAL3);
-#endif
-#ifdef LOG_LOCAL4
- rb_define_syslog_facility(LOG_LOCAL4);
-#endif
-#ifdef LOG_LOCAL5
- rb_define_syslog_facility(LOG_LOCAL5);
-#endif
-#ifdef LOG_LOCAL6
- rb_define_syslog_facility(LOG_LOCAL6);
-#endif
-#ifdef LOG_LOCAL7
- rb_define_syslog_facility(LOG_LOCAL7);
-#endif
-
- /* Syslog levels and the shortcut methods */
-
-#define rb_define_syslog_level(c, m) \
- rb_define_const(mSyslogLevel, #c, INT2NUM(c)); \
- rb_define_module_function(mSyslog, #m, mSyslog_##m, -1)
-
-#ifdef LOG_EMERG
- rb_define_syslog_level(LOG_EMERG, emerg);
-#endif
-#ifdef LOG_ALERT
- rb_define_syslog_level(LOG_ALERT, alert);
-#endif
-#ifdef LOG_CRIT
- rb_define_syslog_level(LOG_CRIT, crit);
-#endif
-#ifdef LOG_ERR
- rb_define_syslog_level(LOG_ERR, err);
-#endif
-#ifdef LOG_WARNING
- rb_define_syslog_level(LOG_WARNING, warning);
-#endif
-#ifdef LOG_NOTICE
- rb_define_syslog_level(LOG_NOTICE, notice);
-#endif
-#ifdef LOG_INFO
- rb_define_syslog_level(LOG_INFO, info);
-#endif
-#ifdef LOG_DEBUG
- rb_define_syslog_level(LOG_DEBUG, debug);
-#endif
-
- /* Syslog macros */
-
- rb_define_const(mSyslog, "VERSION", rb_str_new_cstr(SYSLOG_VERSION));
-
- rb_define_method(mSyslogMacros, "LOG_MASK", mSyslogMacros_LOG_MASK, 1);
- rb_define_method(mSyslogMacros, "LOG_UPTO", mSyslogMacros_LOG_UPTO, 1);
- rb_define_singleton_method(mSyslogMacros, "included", mSyslogMacros_included, 1);
-
- rb_include_module(mSyslogConstants, mSyslogOption);
- rb_include_module(mSyslogConstants, mSyslogFacility);
- rb_include_module(mSyslogConstants, mSyslogLevel);
- rb_funcall(mSyslogConstants, rb_intern("include"), 1, mSyslogMacros);
-
- rb_define_singleton_method(mSyslogConstants, "included", mSyslogMacros_included, 1);
- rb_funcall(mSyslog, rb_intern("include"), 1, mSyslogConstants);
-}
diff --git a/ext/syslog/syslog.gemspec b/ext/syslog/syslog.gemspec
deleted file mode 100644
index 10a6d1f25c..0000000000
--- a/ext/syslog/syslog.gemspec
+++ /dev/null
@@ -1,28 +0,0 @@
-source_version = %w[. ext/syslog].find do |dir|
- break $1 if File.foreach(File.join(__dir__, dir, "syslog.c")).any?(/^#define\s+SYSLOG_VERSION\s+"(.+)"/)
-rescue Errno::ENOENT
-end
-
-Gem::Specification.new do |spec|
- spec.name = "syslog"
- spec.version = source_version
- spec.authors = ["Akinori MUSHA"]
- spec.email = ["knu@idaemons.org"]
-
- spec.summary = %q{Ruby interface for the POSIX system logging facility.}
- spec.description = %q{Ruby interface for the POSIX system logging facility.}
- spec.homepage = "https://github.com/ruby/syslog"
- spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
- spec.licenses = ["Ruby", "BSD-2-Clause"]
-
- spec.metadata["homepage_uri"] = spec.homepage
- spec.metadata["source_code_uri"] = spec.homepage
-
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
- end
- spec.extensions = ["ext/syslog/extconf.rb"]
- spec.bindir = "exe"
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
- spec.require_paths = ["lib"]
-end
diff --git a/ext/syslog/syslog.txt b/ext/syslog/syslog.txt
deleted file mode 100644
index 1507a87924..0000000000
--- a/ext/syslog/syslog.txt
+++ /dev/null
@@ -1,124 +0,0 @@
-.\" syslog.txt - -*- Indented-Text -*-
-$RoughId: syslog.txt,v 1.18 2002/02/25 08:20:14 knu Exp $
-$Id$
-
-UNIX Syslog extension for Ruby
-Amos Gouaux, University of Texas at Dallas
-<amos+ruby@utdallas.edu>
-&
-Akinori MUSHA
-<knu@iDaemons.org>
-
-Contact:
- - Akinori MUSHA <knu@iDaemons.org> (current maintainer)
-
-** Syslog(Module)
-
-Included Modules: Syslog::Constants
-
-require 'syslog'
-
-A Simple wrapper for the UNIX syslog system calls that might be handy
-if you're writing a server in Ruby. For the details of the syslog(8)
-architecture and constants, see the syslog(3) manual page of your
-platform.
-
-Module Methods:
-
- open(ident = $0, logopt = Syslog::LOG_PID | Syslog::LOG_CONS,
- facility = Syslog::LOG_USER) [{ |syslog| ... }]
-
- Opens syslog with the given options and returns the module
- itself. If a block is given, calls it with an argument of
- itself. If syslog is already opened, raises RuntimeError.
-
- Example:
- Syslog.open('ftpd', Syslog::LOG_PID | Syslog::LOG_NDELAY,
- Syslog::LOG_FTP)
-
- open!(ident = $0, logopt = Syslog::LOG_PID | Syslog::LOG_CONS,
- facility = Syslog::LOG_USER)
- reopen(ident = $0, logopt = Syslog::LOG_PID | Syslog::LOG_CONS,
- facility = Syslog::LOG_USER)
-
- Same as open, but does a close first.
-
- opened?
-
- Returns true if syslog opened, otherwise false.
-
- ident
- options
- facility
-
- Returns the parameters given in the last open, respectively.
- Every call of Syslog::open resets these values.
-
- log(pri, message, ...)
-
- Writes message to syslog.
-
- Example:
- Syslog.log(Syslog::LOG_CRIT, "the sky is falling in %d seconds!", 10)
-
- crit(message, ...)
- emerg(message, ...)
- alert(message, ...)
- err(message, ...)
- warning(message, ...)
- notice(message, ...)
- info(message, ...)
- debug(message, ...)
-
- These are shortcut methods of Syslog::log(). The lineup may
- vary depending on what priorities are defined on your system.
-
- Example:
- Syslog.crit("the sky is falling in %d seconds!", 5)
-
- mask
- mask=(mask)
-
- Returns or sets the log priority mask. The value of the mask
- is persistent and will not be reset by Syslog::open or
- Syslog::close.
-
- Example:
- Syslog.mask = Syslog::LOG_UPTO(Syslog::LOG_ERR)
-
- close
-
- Closes syslog.
-
- inspect
-
- Returns the "inspect" string of the Syslog module.
-
- instance
-
- Returns the module itself. (Just for backward compatibility)
-
- LOG_MASK(pri)
-
- Creates a mask for one priority.
-
- LOG_UPTO(pri)
-
- Creates a mask for all priorities up to pri.
-
-** Syslog::Constants(Module)
-
-require 'syslog'
-include Syslog::Constants
-
-This module includes the LOG_* constants available on the system.
-
-Module Methods:
-
- LOG_MASK(pri)
-
- Creates a mask for one priority.
-
- LOG_UPTO(pri)
-
- Creates a mask for all priorities up to pri.
diff --git a/ext/win32/lib/win32/registry.rb b/ext/win32/lib/win32/registry.rb
index b5b99ff684..4fed03f6a9 100644
--- a/ext/win32/lib/win32/registry.rb
+++ b/ext/win32/lib/win32/registry.rb
@@ -318,7 +318,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr
size = packdw(0)
name = make_wstr(name)
check RegQueryValueExW.call(hkey, name, 0, type, 0, size)
- data = "\0".force_encoding('ASCII-8BIT') * unpackdw(size)
+ data = "\0".b * unpackdw(size)
check RegQueryValueExW.call(hkey, name, 0, type, data, size)
[ unpackdw(type), data[0, unpackdw(size)] ]
end
@@ -740,14 +740,11 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr
# method returns.
#
def write(name, type, data)
- termsize = 0
case type
when REG_SZ, REG_EXPAND_SZ
- data = data.encode(WCHAR)
- termsize = WCHAR_SIZE
+ data = data.encode(WCHAR) << WCHAR_NUL
when REG_MULTI_SZ
data = data.to_a.map {|s| s.encode(WCHAR)}.join(WCHAR_NUL) << WCHAR_NUL
- termsize = WCHAR_SIZE
when REG_BINARY, REG_NONE
data = data.to_s
when REG_DWORD
@@ -759,7 +756,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr
else
raise TypeError, "Unsupported type #{Registry.type2name(type)}"
end
- API.SetValue(@hkey, name, type, data, data.bytesize + termsize)
+ API.SetValue(@hkey, name, type, data, data.bytesize)
end
#
diff --git a/ext/win32ole/.document b/ext/win32ole/.document
new file mode 100644
index 0000000000..decba0135a
--- /dev/null
+++ b/ext/win32ole/.document
@@ -0,0 +1 @@
+*.[ch]
diff --git a/ext/win32ole/lib/win32ole.rb b/ext/win32ole/lib/win32ole.rb
index d7034f7845..f5c8a52c4a 100644
--- a/ext/win32ole/lib/win32ole.rb
+++ b/ext/win32ole/lib/win32ole.rb
@@ -5,7 +5,6 @@ rescue LoadError
end
if defined?(WIN32OLE)
- # WIN32OLE
class WIN32OLE
#
@@ -26,7 +25,7 @@ if defined?(WIN32OLE)
def ole_methods_safely
ole_methods
- rescue WIN32OLEQueryInterfaceError
+ rescue WIN32OLE::QueryInterfaceError
[]
end
end
diff --git a/ext/win32ole/lib/win32ole/property.rb b/ext/win32ole/lib/win32ole/property.rb
index fea047cd19..558056b32b 100644
--- a/ext/win32ole/lib/win32ole/property.rb
+++ b/ext/win32ole/lib/win32ole/property.rb
@@ -1,7 +1,12 @@
# frozen_string_literal: false
-# OLEProperty
-# helper class of Property with arguments.
-class OLEProperty
+
+class WIN32OLE
+end
+
+# OLEProperty is a helper class of Property with arguments, used by
+# `olegen.rb`-generated files.
+class WIN32OLE::Property
+ # :stopdoc:
def initialize(obj, dispid, gettypes, settypes)
@obj = obj
@dispid = dispid
@@ -14,4 +19,11 @@ class OLEProperty
def []=(*args)
@obj._setproperty(@dispid, args, @settypes)
end
+ # :stopdoc:
+end
+
+module WIN32OLE::VariantType
+ # Alias for `olegen.rb`-generated files, that should include
+ # WIN32OLE::VARIANT.
+ OLEProperty = WIN32OLE::Property
end
diff --git a/ext/win32ole/sample/olegen.rb b/ext/win32ole/sample/olegen.rb
deleted file mode 100644
index 4b088a774f..0000000000
--- a/ext/win32ole/sample/olegen.rb
+++ /dev/null
@@ -1,348 +0,0 @@
-# frozen_string_literal: false
-#-----------------------------
-# olegen.rb
-# $Revision$
-#-----------------------------
-
-require 'win32ole'
-
-class WIN32COMGen
- def initialize(typelib)
- @typelib = typelib
- @receiver = ""
- end
- attr_reader :typelib
-
- def ole_classes(typelib)
- begin
- @ole = WIN32OLE.new(typelib)
- [@ole.ole_obj_help]
- rescue
- WIN32OLE_TYPE.ole_classes(typelib)
- end
- end
-
- def generate_args(method)
- args = []
- if method.size_opt_params >= 0
- size_required_params = method.size_params - method.size_opt_params
- else
- size_required_params = method.size_params - 1
- end
- size_required_params.times do |i|
- if method.params[i] && method.params[i].optional?
- args.push "arg#{i}=nil"
- else
- args.push "arg#{i}"
- end
- end
- if method.size_opt_params >= 0
- method.size_opt_params.times do |i|
- args.push "arg#{i + size_required_params}=nil"
- end
- else
- args.push "*arg"
- end
- args.join(", ")
- end
-
- def generate_argtype(typedetails)
- ts = ''
- typedetails.each do |t|
- case t
- when 'CARRAY', 'VOID', 'UINT', 'RESULT', 'DECIMAL', 'I8', 'UI8'
-# raise "Sorry type\"" + t + "\" not supported"
- ts << "\"??? NOT SUPPORTED TYPE:`#{t}'\""
- when 'USERDEFINED', 'Unknown Type 9'
- ts << 'VT_DISPATCH'
- break;
- when 'SAFEARRAY'
- ts << 'VT_ARRAY|'
- when 'PTR'
- ts << 'VT_BYREF|'
- when 'INT'
- ts << 'VT_I4'
- else
- if String === t
- ts << 'VT_' + t
- end
- end
- end
- if ts.empty?
- ts = 'VT_VARIANT'
- elsif ts[-1] == ?|
- ts += 'VT_VARIANT'
- end
- ts
- end
-
- def generate_argtypes(method, proptypes)
- types = method.params.collect{|param|
- generate_argtype(param.ole_type_detail)
- }.join(", ")
- if proptypes
- types += ", " if types.size > 0
- types += generate_argtype(proptypes)
- end
- types
- end
-
- def generate_method_body(method, disptype, types=nil)
- " ret = #{@receiver}#{disptype}(#{method.dispid}, [" +
- generate_args(method).gsub("=nil", "") +
- "], [" +
- generate_argtypes(method, types) +
- "])\n" +
- " @lastargs = WIN32OLE::ARGV\n" +
- " ret"
- end
-
- def generate_method_help(method, type = nil)
- str = " # "
- if type
- str += type
- else
- str += method.return_type
- end
- str += " #{method.name}"
- if method.event?
- str += " EVENT"
- str += " in #{method.event_interface}"
- end
- if method.helpstring && method.helpstring != ""
- str += "\n # "
- str += method.helpstring
- end
- args_help = generate_method_args_help(method)
- if args_help
- str += "\n"
- str += args_help
- end
- str
- end
-
- def generate_method_args_help(method)
- args = []
- method.params.each_with_index {|param, i|
- h = " # #{param.ole_type} arg#{i} --- #{param.name}"
- inout = []
- inout.push "IN" if param.input?
- inout.push "OUT" if param.output?
- h += " [#{inout.join('/')}]"
- h += " ( = #{param.default})" if param.default
- args.push h
- }
- if args.size > 0
- args.join("\n")
- else
- nil
- end
- end
-
- def generate_method(method, disptype, io = STDOUT, types = nil)
- io.puts "\n"
- io.puts generate_method_help(method)
- if method.invoke_kind == 'PROPERTYPUT'
- io.print " def #{method.name}=("
- else
- io.print " def #{method.name}("
- end
- io.print generate_args(method)
- io.puts ")"
- io.puts generate_method_body(method, disptype, types)
- io.puts " end"
- end
-
- def generate_propputref_methods(klass, io = STDOUT)
- klass.ole_methods.select {|method|
- method.invoke_kind == 'PROPERTYPUTREF' && method.visible?
- }.each do |method|
- generate_method(method, io)
- end
- end
-
- def generate_properties_with_args(klass, io = STDOUT)
- klass.ole_methods.select {|method|
- method.invoke_kind == 'PROPERTYGET' &&
- method.visible? &&
- method.size_params > 0
- }.each do |method|
- types = method.return_type_detail
- io.puts "\n"
- io.puts generate_method_help(method, types[0])
- io.puts " def #{method.name}"
- if klass.ole_type == "Class"
- io.print " OLEProperty.new(@dispatch, #{method.dispid}, ["
- else
- io.print " OLEProperty.new(self, #{method.dispid}, ["
- end
- io.print generate_argtypes(method, nil)
- io.print "], ["
- io.print generate_argtypes(method, types)
- io.puts "])"
- io.puts " end"
- end
- end
-
- def generate_propput_methods(klass, io = STDOUT)
- klass.ole_methods.select {|method|
- method.invoke_kind == 'PROPERTYPUT' && method.visible? &&
- method.size_params == 1
- }.each do |method|
- ms = klass.ole_methods.select {|m|
- m.invoke_kind == 'PROPERTYGET' &&
- m.dispid == method.dispid
- }
- types = []
- if ms.size == 1
- types = ms[0].return_type_detail
- end
- generate_method(method, '_setproperty', io, types)
- end
- end
-
- def generate_propget_methods(klass, io = STDOUT)
- klass.ole_methods.select {|method|
- method.invoke_kind == 'PROPERTYGET' && method.visible? &&
- method.size_params == 0
- }.each do |method|
- generate_method(method, '_getproperty', io)
- end
- end
-
- def generate_func_methods(klass, io = STDOUT)
- klass.ole_methods.select {|method|
- method.invoke_kind == "FUNC" && method.visible?
- }.each do |method|
- generate_method(method, '_invoke', io)
- end
- end
-
- def generate_methods(klass, io = STDOUT)
- generate_propget_methods(klass, io)
- generate_propput_methods(klass, io)
- generate_properties_with_args(klass, io)
- generate_func_methods(klass, io)
-# generate_propputref_methods(klass, io)
- end
-
- def generate_constants(klass, io = STDOUT)
- klass.variables.select {|v|
- v.visible? && v.variable_kind == 'CONSTANT'
- }.each do |v|
- io.print " "
- io.print v.name.sub(/^./){$&.upcase}
- io.print " = "
- io.puts v.value
- end
- end
-
- def class_name(klass)
- klass_name = klass.name
- if klass.ole_type == "Class" &&
- klass.guid &&
- klass.progid
- klass_name = klass.progid.gsub(/\./, '_')
- end
- if /^[A-Z]/ !~ klass_name || Module.constants.include?(klass_name)
- klass_name = 'OLE' + klass_name
- end
- klass_name
- end
-
- def define_initialize(klass)
- <<STR
-
- def initialize(obj = nil)
- @clsid = "#{klass.guid}"
- @progid = "#{klass.progid}"
- if obj.nil?
- @dispatch = WIN32OLE.new @progid
- else
- @dispatch = obj
- end
- end
-STR
- end
-
- def define_include
- " include WIN32OLE::VARIANT"
- end
-
- def define_instance_variables
- " attr_reader :lastargs"
- end
-
- def define_method_missing
- <<STR
-
- def method_missing(cmd, *arg)
- @dispatch.method_missing(cmd, *arg)
- end
-STR
- end
-
- def define_class(klass, io = STDOUT)
- io.puts "class #{class_name(klass)} # #{klass.name}"
- io.puts define_include
- io.puts define_instance_variables
- io.puts " attr_reader :dispatch"
- io.puts " attr_reader :clsid"
- io.puts " attr_reader :progid"
- io.puts define_initialize(klass)
- io.puts define_method_missing
- end
-
- def define_module(klass, io = STDOUT)
- io.puts "module #{class_name(klass)}"
- io.puts define_include
- io.puts define_instance_variables
- end
-
- def generate_class(klass, io = STDOUT)
- io.puts "\n# #{klass.helpstring}"
- if klass.ole_type == "Class" &&
- klass.guid &&
- klass.progid
- @receiver = "@dispatch."
- define_class(klass, io)
- else
- @receiver = ""
- define_module(klass, io)
- end
- generate_constants(klass, io)
- generate_methods(klass, io)
- io.puts "end"
- end
-
- def generate(io = STDOUT)
- io.puts "require 'win32ole'"
- io.puts "require 'win32ole/property'"
-
- ole_classes(typelib).select{|klass|
- klass.visible? &&
- (klass.ole_type == "Class" ||
- klass.ole_type == "Interface" ||
- klass.ole_type == "Dispatch" ||
- klass.ole_type == "Enum")
- }.each do |klass|
- generate_class(klass, io)
- end
- begin
- @ole.quit if @ole
- rescue
- end
- end
-end
-
-require 'win32ole'
-if __FILE__ == $0
- if ARGV.size == 0
- $stderr.puts "usage: #{$0} Type Library [...]"
- exit 1
- end
- ARGV.each do |typelib|
- comgen = WIN32COMGen.new(typelib)
- comgen.generate
- end
-end
diff --git a/ext/win32ole/win32ole.c b/ext/win32ole/win32ole.c
index f630e657cd..e0342d1e9d 100644
--- a/ext/win32ole/win32ole.c
+++ b/ext/win32ole/win32ole.c
@@ -27,7 +27,7 @@
const IID IID_IMultiLanguage2 = {0xDCCFC164, 0x2B38, 0x11d2, {0xB7, 0xEC, 0x00, 0xC0, 0x4F, 0x8F, 0x5D, 0x9A}};
#endif
-#define WIN32OLE_VERSION "1.8.9"
+#define WIN32OLE_VERSION "1.8.10"
typedef HRESULT (STDAPICALLTYPE FNCOCREATEINSTANCEEX)
(REFCLSID, IUnknown*, DWORD, COSERVERINFO*, DWORD, MULTI_QI*);
@@ -1962,7 +1962,7 @@ ole_bind_obj(VALUE moniker, int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * WIN32OLE.connect( ole ) --> aWIN32OLE
+ * connect(ole) --> aWIN32OLE
*
* Returns running OLE Automation object or WIN32OLE object from moniker.
* 1st argument should be OLE program id or class id or moniker.
@@ -2019,7 +2019,7 @@ fole_s_connect(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * WIN32OLE.const_load( ole, mod = WIN32OLE)
+ * const_load(ole, mod = WIN32OLE)
*
* Defines the constants of OLE Automation server as mod's constants.
* The first argument is WIN32OLE object or type library name.
@@ -2124,7 +2124,7 @@ reference_count(struct oledata * pole)
/*
* call-seq:
- * WIN32OLE.ole_reference_count(aWIN32OLE) --> number
+ * ole_reference_count(aWIN32OLE) --> number
*
* Returns reference counter of Dispatch interface of WIN32OLE object.
* You should not use this method because this method
@@ -2140,7 +2140,7 @@ fole_s_reference_count(VALUE self, VALUE obj)
/*
* call-seq:
- * WIN32OLE.ole_free(aWIN32OLE) --> number
+ * ole_free(aWIN32OLE) --> number
*
* Invokes Release method of Dispatch interface of WIN32OLE object.
* You should not use this method because this method
@@ -2184,10 +2184,10 @@ ole_show_help(VALUE helpfile, VALUE helpcontext)
/*
* call-seq:
- * WIN32OLE.ole_show_help(obj [,helpcontext])
+ * ole_show_help(obj [,helpcontext])
*
- * Displays helpfile. The 1st argument specifies WIN32OLE_TYPE
- * object or WIN32OLE_METHOD object or helpfile.
+ * Displays helpfile. The 1st argument specifies WIN32OLE::Type
+ * object or WIN32OLE::Method object or helpfile.
*
* excel = WIN32OLE.new('Excel.Application')
* typeobj = excel.ole_type
@@ -2215,7 +2215,7 @@ fole_s_show_help(int argc, VALUE *argv, VALUE self)
helpfile = target;
}
if (!RB_TYPE_P(helpfile, T_STRING)) {
- rb_raise(rb_eTypeError, "1st parameter must be (String|WIN32OLE_TYPE|WIN32OLE_METHOD)");
+ rb_raise(rb_eTypeError, "1st parameter must be (String|WIN32OLE::Type|WIN32OLE::Method)");
}
hwnd = ole_show_help(helpfile, helpcontext);
if(hwnd == 0) {
@@ -2227,7 +2227,7 @@ fole_s_show_help(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * WIN32OLE.codepage
+ * codepage
*
* Returns current codepage.
* WIN32OLE.codepage # => WIN32OLE::CP_ACP
@@ -2258,7 +2258,7 @@ code_page_installed(UINT cp)
/*
* call-seq:
- * WIN32OLE.codepage = CP
+ * codepage = CP
*
* Sets current codepage.
* The WIN32OLE.codepage is initialized according to
@@ -2282,7 +2282,7 @@ fole_s_set_code_page(VALUE self, VALUE vcp)
/*
* call-seq:
- * WIN32OLE.locale -> locale id.
+ * locale -> locale id.
*
* Returns current locale id (lcid). The default locale is
* WIN32OLE::LOCALE_SYSTEM_DEFAULT.
@@ -2316,12 +2316,12 @@ lcid_installed(LCID lcid)
/*
* call-seq:
- * WIN32OLE.locale = lcid
+ * locale = lcid
*
* Sets current locale id (lcid).
*
* WIN32OLE.locale = 1033 # set locale English(U.S)
- * obj = WIN32OLE_VARIANT.new("$100,000", WIN32OLE::VARIANT::VT_CY)
+ * obj = WIN32OLE::Variant.new("$100,000", WIN32OLE::VARIANT::VT_CY)
*
*/
static VALUE
@@ -2345,7 +2345,7 @@ fole_s_set_locale(VALUE self, VALUE vlcid)
/*
* call-seq:
- * WIN32OLE.create_guid
+ * create_guid
*
* Creates GUID.
* WIN32OLE.create_guid # => {1CB530F1-F6B1-404D-BCE6-1959BF91F4A8}
@@ -2393,9 +2393,9 @@ fole_s_ole_uninitialize(VALUE self)
/*
* Document-class: WIN32OLE
*
- * <code>WIN32OLE</code> objects represent OLE Automation object in Ruby.
+ * +WIN32OLE+ objects represent OLE Automation object in Ruby.
*
- * By using WIN32OLE, you can access OLE server like VBScript.
+ * By using +WIN32OLE+, you can access OLE server like VBScript.
*
* Here is sample script.
*
@@ -2419,18 +2419,18 @@ fole_s_ole_uninitialize(VALUE self)
* excel.ActiveWorkbook.Close(0);
* excel.Quit();
*
- * Unfortunately, Win32OLE doesn't support the argument passed by
+ * Unfortunately, +WIN32OLE+ doesn't support the argument passed by
* reference directly.
- * Instead, Win32OLE provides WIN32OLE::ARGV or WIN32OLE_VARIANT object.
+ * Instead, +WIN32OLE+ provides WIN32OLE::ARGV or WIN32OLE::Variant object.
* If you want to get the result value of argument passed by reference,
- * you can use WIN32OLE::ARGV or WIN32OLE_VARIANT.
+ * you can use WIN32OLE::ARGV or WIN32OLE::Variant.
*
* oleobj.method(arg1, arg2, refargv3)
* puts WIN32OLE::ARGV[2] # the value of refargv3 after called oleobj.method
*
* or
*
- * refargv3 = WIN32OLE_VARIANT.new(XXX,
+ * refargv3 = WIN32OLE::Variant.new(XXX,
* WIN32OLE::VARIANT::VT_BYREF|WIN32OLE::VARIANT::VT_XXX)
* oleobj.method(arg1, arg2, refargv3)
* p refargv3.value # the value of refargv3 after called oleobj.method.
@@ -2439,7 +2439,7 @@ fole_s_ole_uninitialize(VALUE self)
/*
* call-seq:
- * WIN32OLE.new(server, [host]) -> WIN32OLE object
+ * new(server, [host]) -> WIN32OLE object
* WIN32OLE.new(server, license: 'key') -> WIN32OLE object
*
* Returns a new WIN32OLE object(OLE Automation object).
@@ -2826,7 +2826,7 @@ ole_invoke(int argc, VALUE *argv, VALUE self, USHORT wFlags, BOOL is_bracket)
/*
* call-seq:
- * WIN32OLE#invoke(method, [arg1,...]) => return value of method.
+ * invoke(method, [arg1,...]) => return value of method.
*
* Runs OLE method.
* The first argument specifies the method name of OLE Automation object.
@@ -3038,7 +3038,7 @@ ole_invoke2(VALUE self, VALUE dispid, VALUE args, VALUE types, USHORT dispkind)
/*
* call-seq:
- * WIN32OLE#_invoke(dispid, args, types)
+ * _invoke(dispid, args, types)
*
* Runs the early binding method.
* The 1st argument specifies dispatch ID,
@@ -3056,7 +3056,7 @@ fole_invoke2(VALUE self, VALUE dispid, VALUE args, VALUE types)
/*
* call-seq:
- * WIN32OLE#_getproperty(dispid, args, types)
+ * _getproperty(dispid, args, types)
*
* Runs the early binding method to get property.
* The 1st argument specifies dispatch ID,
@@ -3074,7 +3074,7 @@ fole_getproperty2(VALUE self, VALUE dispid, VALUE args, VALUE types)
/*
* call-seq:
- * WIN32OLE#_setproperty(dispid, args, types)
+ * _setproperty(dispid, args, types)
*
* Runs the early binding method to set property.
* The 1st argument specifies dispatch ID,
@@ -3120,7 +3120,7 @@ fole_setproperty_with_bracket(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * WIN32OLE.setproperty('property', [arg1, arg2,...] val)
+ * setproperty('property', [arg1, arg2,...] val)
*
* Sets property of OLE object.
* When you want to set property with argument, you can use this method.
@@ -3226,7 +3226,7 @@ ole_propertyput(VALUE self, VALUE property, VALUE value)
/*
* call-seq:
- * WIN32OLE#ole_free
+ * ole_free
*
* invokes Release method of Dispatch interface of WIN32OLE object.
* Usually, you do not need to call this method because Release method
@@ -3269,7 +3269,7 @@ ole_ienum_free(VALUE pEnumV)
/*
* call-seq:
- * WIN32OLE#each {|i|...}
+ * each {|i|...}
*
* Iterates over each item of OLE collection which has IEnumVARIANT interface.
*
@@ -3340,7 +3340,7 @@ fole_each(VALUE self)
/*
* call-seq:
- * WIN32OLE#method_missing(id [,arg1, arg2, ...])
+ * method_missing(id [,arg1, arg2, ...])
*
* Calls WIN32OLE#invoke method.
*/
@@ -3438,9 +3438,9 @@ ole_methods(VALUE self, int mask)
/*
* call-seq:
- * WIN32OLE#ole_methods
+ * ole_methods
*
- * Returns the array of WIN32OLE_METHOD object.
+ * Returns the array of WIN32OLE::Method object.
* The element is OLE method of WIN32OLE object.
*
* excel = WIN32OLE.new('Excel.Application')
@@ -3455,9 +3455,9 @@ fole_methods(VALUE self)
/*
* call-seq:
- * WIN32OLE#ole_get_methods
+ * ole_get_methods
*
- * Returns the array of WIN32OLE_METHOD object .
+ * Returns the array of WIN32OLE::Method object .
* The element of the array is property (gettable) of WIN32OLE object.
*
* excel = WIN32OLE.new('Excel.Application')
@@ -3471,9 +3471,9 @@ fole_get_methods(VALUE self)
/*
* call-seq:
- * WIN32OLE#ole_put_methods
+ * ole_put_methods
*
- * Returns the array of WIN32OLE_METHOD object .
+ * Returns the array of WIN32OLE::Method object .
* The element of the array is property (settable) of WIN32OLE object.
*
* excel = WIN32OLE.new('Excel.Application')
@@ -3487,9 +3487,9 @@ fole_put_methods(VALUE self)
/*
* call-seq:
- * WIN32OLE#ole_func_methods
+ * ole_func_methods
*
- * Returns the array of WIN32OLE_METHOD object .
+ * Returns the array of WIN32OLE::Method object .
* The element of the array is property (settable) of WIN32OLE object.
*
* excel = WIN32OLE.new('Excel.Application')
@@ -3504,9 +3504,9 @@ fole_func_methods(VALUE self)
/*
* call-seq:
- * WIN32OLE#ole_type
+ * ole_type
*
- * Returns WIN32OLE_TYPE object.
+ * Returns WIN32OLE::Type object.
*
* excel = WIN32OLE.new('Excel.Application')
* tobj = excel.ole_type
@@ -3529,16 +3529,16 @@ fole_type(VALUE self)
type = ole_type_from_itypeinfo(pTypeInfo);
OLE_RELEASE(pTypeInfo);
if (type == Qnil) {
- rb_raise(rb_eRuntimeError, "failed to create WIN32OLE_TYPE obj from ITypeInfo");
+ rb_raise(rb_eRuntimeError, "failed to create WIN32OLE::Type obj from ITypeInfo");
}
return type;
}
/*
* call-seq:
- * WIN32OLE#ole_typelib -> The WIN32OLE_TYPELIB object
+ * ole_typelib -> The WIN32OLE_TYPELIB object
*
- * Returns the WIN32OLE_TYPELIB object. The object represents the
+ * Returns the WIN32OLE::TypeLib object. The object represents the
* type library which contains the WIN32OLE object.
*
* excel = WIN32OLE.new('Excel.Application')
@@ -3570,7 +3570,7 @@ fole_typelib(VALUE self)
/*
* call-seq:
- * WIN32OLE#ole_query_interface(iid) -> WIN32OLE object
+ * ole_query_interface(iid) -> WIN32OLE object
*
* Returns WIN32OLE object for a specific dispatch or dual
* interface specified by iid.
@@ -3616,7 +3616,7 @@ fole_query_interface(VALUE self, VALUE str_iid)
/*
* call-seq:
- * WIN32OLE#ole_respond_to?(method) -> true or false
+ * ole_respond_to?(method) -> true or false
*
* Returns true when OLE object has OLE method, otherwise returns false.
*
@@ -3825,9 +3825,9 @@ ole_typedesc2val(ITypeInfo *pTypeInfo, TYPEDESC *pTypeDesc, VALUE typedetails)
/*
* call-seq:
- * WIN32OLE#ole_method_help(method)
+ * ole_method_help(method)
*
- * Returns WIN32OLE_METHOD object corresponding with method
+ * Returns WIN32OLE::Method object corresponding with method
* specified by 1st argument.
*
* excel = WIN32OLE.new('Excel.Application')
@@ -3859,7 +3859,7 @@ fole_method_help(VALUE self, VALUE cmdname)
/*
* call-seq:
- * WIN32OLE#ole_activex_initialize() -> Qnil
+ * ole_activex_initialize() -> Qnil
*
* Initialize WIN32OLE object(ActiveX Control) by calling
* IPersistMemory::InitNew.
@@ -4073,7 +4073,7 @@ Init_win32ole(void)
* p c # => 0
* p WIN32OLE::ARGV # => [10, 20, 30]
*
- * You can use WIN32OLE_VARIANT object to retrieve the value of reference
+ * You can use WIN32OLE::Variant object to retrieve the value of reference
* arguments instead of referring WIN32OLE::ARGV.
*
*/
diff --git a/ext/win32ole/win32ole.gemspec b/ext/win32ole/win32ole.gemspec
index 9c137a5d70..625916ae17 100644
--- a/ext/win32ole/win32ole.gemspec
+++ b/ext/win32ole/win32ole.gemspec
@@ -23,9 +23,11 @@ Gem::Specification.new do |spec|
spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = spec.homepage
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
- end
+ pathspecs = %W[
+ :(exclude,literal)#{File.basename(__FILE__)}
+ :^/bin/ :^/test/ :^/rakelib/ :^/.git* :^/Gemfile* :^/Rakefile*
+ ]
+ spec.files = IO.popen(%w[git ls-files -z --] + pathspecs, chdir: __dir__, err: IO::NULL, exception: false, &:read).split("\x0")
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
diff --git a/ext/win32ole/win32ole_error.c b/ext/win32ole/win32ole_error.c
index 2bb5156263..66b5136dee 100644
--- a/ext/win32ole/win32ole_error.c
+++ b/ext/win32ole/win32ole_error.c
@@ -67,7 +67,7 @@ void
Init_win32ole_error(void)
{
/*
- * Document-class: WIN32OLERuntimeError
+ * Document-class: WIN32OLE::RuntimeError
*
* Raised when OLE processing failed.
*
@@ -77,11 +77,20 @@ Init_win32ole_error(void)
*
* raises the exception:
*
- * WIN32OLERuntimeError: unknown OLE server: `NonExistProgID'
+ * WIN32OLE::RuntimeError: unknown OLE server: `NonExistProgID'
* HRESULT error code:0x800401f3
* Invalid class string
*
*/
- eWIN32OLERuntimeError = rb_define_class("WIN32OLERuntimeError", rb_eRuntimeError);
- eWIN32OLEQueryInterfaceError = rb_define_class("WIN32OLEQueryInterfaceError", eWIN32OLERuntimeError);
+ eWIN32OLERuntimeError = rb_define_class_under(cWIN32OLE, "RuntimeError", rb_eRuntimeError);
+ /* Alias of WIN32OLE::RuntimeError, for the backward compatibility */
+ rb_define_const(rb_cObject, "WIN32OLERuntimeError", eWIN32OLERuntimeError);
+ /*
+ * Document-class: WIN32OLE::QueryInterfaceError
+ *
+ * Raised when OLE query failed.
+ */
+ eWIN32OLEQueryInterfaceError = rb_define_class_under(cWIN32OLE, "QueryInterfaceError", eWIN32OLERuntimeError);
+ /* Alias of WIN32OLE::QueryInterfaceError, for the backward compatibility */
+ rb_define_const(rb_cObject, "WIN32OLEQueryInterfaceError", eWIN32OLEQueryInterfaceError);
}
diff --git a/ext/win32ole/win32ole_event.c b/ext/win32ole/win32ole_event.c
index 45ebf13433..ff6835e3e4 100644
--- a/ext/win32ole/win32ole_event.c
+++ b/ext/win32ole/win32ole_event.c
@@ -1,9 +1,9 @@
#include "win32ole.h"
/*
- * Document-class: WIN32OLE_EVENT
+ * Document-class: WIN32OLE::Event
*
- * <code>WIN32OLE_EVENT</code> objects controls OLE event.
+ * +WIN32OLE::Event+ objects controls OLE event.
*/
RUBY_EXTERN void rb_write_error_str(VALUE mesg);
@@ -974,13 +974,13 @@ ev_advise(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * WIN32OLE_EVENT.new(ole, event) #=> WIN32OLE_EVENT object.
+ * new(ole, event) #=> WIN32OLE::Event object.
*
* Returns OLE event object.
* The first argument specifies WIN32OLE object.
* The second argument specifies OLE event name.
* ie = WIN32OLE.new('InternetExplorer.Application')
- * ev = WIN32OLE_EVENT.new(ie, 'DWebBrowserEvents')
+ * ev = WIN32OLE::Event.new(ie, 'DWebBrowserEvents')
*/
static VALUE
fev_initialize(int argc, VALUE *argv, VALUE self)
@@ -1004,7 +1004,7 @@ ole_msg_loop(void)
/*
* call-seq:
- * WIN32OLE_EVENT.message_loop
+ * message_loop
*
* Translates and dispatches Windows message.
*/
@@ -1052,7 +1052,7 @@ ev_on_event(int argc, VALUE *argv, VALUE self, VALUE is_ary_arg)
/*
* call-seq:
- * WIN32OLE_EVENT#on_event([event]){...}
+ * on_event([event]){...}
*
* Defines the callback event.
* If argument is omitted, this method defines the callback of all events.
@@ -1061,12 +1061,12 @@ ev_on_event(int argc, VALUE *argv, VALUE self, VALUE is_ary_arg)
* use `return' or :return.
*
* ie = WIN32OLE.new('InternetExplorer.Application')
- * ev = WIN32OLE_EVENT.new(ie)
+ * ev = WIN32OLE::Event.new(ie)
* ev.on_event("NavigateComplete") {|url| puts url}
* ev.on_event() {|ev, *args| puts "#{ev} fired"}
*
* ev.on_event("BeforeNavigate2") {|*args|
- * ...
+ * # ...
* # set true to BeforeNavigate reference argument `Cancel'.
* # Cancel is 7-th argument of BeforeNavigate,
* # so you can use 6 as key of hash instead of 'Cancel'.
@@ -1075,7 +1075,7 @@ ev_on_event(int argc, VALUE *argv, VALUE self, VALUE is_ary_arg)
* {:Cancel => true} # or {'Cancel' => true} or {6 => true}
* }
*
- * ev.on_event(...) {|*args|
+ * ev.on_event(event_name) {|*args|
* {:return => 1, :xxx => yyy}
* }
*/
@@ -1087,14 +1087,14 @@ fev_on_event(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * WIN32OLE_EVENT#on_event_with_outargs([event]){...}
+ * on_event_with_outargs([event]){...}
*
* Defines the callback of event.
* If you want modify argument in callback,
- * you could use this method instead of WIN32OLE_EVENT#on_event.
+ * you could use this method instead of WIN32OLE::Event#on_event.
*
* ie = WIN32OLE.new('InternetExplorer.Application')
- * ev = WIN32OLE_EVENT.new(ie)
+ * ev = WIN32OLE::Event.new(ie)
* ev.on_event_with_outargs('BeforeNavigate2') {|*args|
* args.last[6] = true
* }
@@ -1107,18 +1107,18 @@ fev_on_event_with_outargs(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * WIN32OLE_EVENT#off_event([event])
+ * off_event([event])
*
* removes the callback of event.
*
* ie = WIN32OLE.new('InternetExplorer.Application')
- * ev = WIN32OLE_EVENT.new(ie)
+ * ev = WIN32OLE::Event.new(ie)
* ev.on_event('BeforeNavigate2') {|*args|
* args.last[6] = true
* }
- * ...
+ * # ...
* ev.off_event('BeforeNavigate2')
- * ...
+ * # ...
*/
static VALUE
fev_off_event(int argc, VALUE *argv, VALUE self)
@@ -1145,16 +1145,16 @@ fev_off_event(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * WIN32OLE_EVENT#unadvise -> nil
+ * unadvise -> nil
*
- * disconnects OLE server. If this method called, then the WIN32OLE_EVENT object
+ * disconnects OLE server. If this method called, then the WIN32OLE::Event object
* does not receive the OLE server event any more.
* This method is trial implementation.
*
* ie = WIN32OLE.new('InternetExplorer.Application')
- * ev = WIN32OLE_EVENT.new(ie)
- * ev.on_event() {...}
- * ...
+ * ev = WIN32OLE::Event.new(ie)
+ * ev.on_event() { something }
+ * # ...
* ev.unadvise
*
*/
@@ -1201,7 +1201,7 @@ evs_length(void)
/*
* call-seq:
- * WIN32OLE_EVENT#handler=
+ * handler=
*
* sets event handler object. If handler object has onXXX
* method according to XXX event, then onXXX method is called
@@ -1212,7 +1212,7 @@ evs_length(void)
* called and 1-st argument is event name.
*
* If handler object has onXXX method and there is block
- * defined by WIN32OLE_EVENT#on_event('XXX'){},
+ * defined by <code>on_event('XXX'){}</code>,
* then block is executed but handler object method is not called
* when XXX event occurs.
*
@@ -1230,7 +1230,7 @@ evs_length(void)
*
* handler = Handler.new
* ie = WIN32OLE.new('InternetExplorer.Application')
- * ev = WIN32OLE_EVENT.new(ie)
+ * ev = WIN32OLE::Event.new(ie)
* ev.on_event("StatusTextChange") {|*args|
* puts "this block executed."
* puts "handler.onStatusTextChange method is not called."
@@ -1246,7 +1246,7 @@ fev_set_handler(VALUE self, VALUE val)
/*
* call-seq:
- * WIN32OLE_EVENT#handler
+ * handler
*
* returns handler object.
*
@@ -1265,6 +1265,7 @@ Init_win32ole_event(void)
rb_gc_register_mark_object(ary_ole_event);
id_events = rb_intern("events");
cWIN32OLE_EVENT = rb_define_class_under(cWIN32OLE, "Event", rb_cObject);
+ /* Alias of WIN32OLE::Event, for the backward compatibility */
rb_define_const(rb_cObject, "WIN32OLE_EVENT", cWIN32OLE_EVENT);
rb_define_singleton_method(cWIN32OLE_EVENT, "message_loop", fev_s_msg_loop, 0);
rb_define_alloc_func(cWIN32OLE_EVENT, fev_s_allocate);
diff --git a/ext/win32ole/win32ole_method.c b/ext/win32ole/win32ole_method.c
index 646fdaf60c..a0278729f0 100644
--- a/ext/win32ole/win32ole_method.c
+++ b/ext/win32ole/win32ole_method.c
@@ -216,9 +216,9 @@ create_win32ole_method(ITypeInfo *pTypeInfo, VALUE name)
}
/*
- * Document-class: WIN32OLE_METHOD
+ * Document-class: WIN32OLE::Method
*
- * <code>WIN32OLE_METHOD</code> objects represent OLE method information.
+ * +WIN32OLE::Method+ objects represent OLE method information.
*/
static VALUE
@@ -251,16 +251,16 @@ folemethod_s_allocate(VALUE klass)
/*
* call-seq:
- * WIN32OLE_METHOD.new(ole_type, method) -> WIN32OLE_METHOD object
+ * WIN32OLE::Method.new(ole_type, method) -> WIN32OLE::Method object
*
- * Returns a new WIN32OLE_METHOD object which represents the information
+ * Returns a new WIN32OLE::Method object which represents the information
* about OLE method.
- * The first argument <i>ole_type</i> specifies WIN32OLE_TYPE object.
+ * The first argument <i>ole_type</i> specifies WIN32OLE::Type object.
* The second argument <i>method</i> specifies OLE method name defined OLE class
- * which represents WIN32OLE_TYPE object.
+ * which represents WIN32OLE::Type object.
*
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbook')
- * method = WIN32OLE_METHOD.new(tobj, 'SaveAs')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Workbook')
+ * method = WIN32OLE::Method.new(tobj, 'SaveAs')
*/
static VALUE
folemethod_initialize(VALUE self, VALUE oletype, VALUE method)
@@ -277,19 +277,19 @@ folemethod_initialize(VALUE self, VALUE oletype, VALUE method)
}
}
else {
- rb_raise(rb_eTypeError, "1st argument should be WIN32OLE_TYPE object");
+ rb_raise(rb_eTypeError, "1st argument should be WIN32OLE::Type object");
}
return obj;
}
/*
* call-seq:
- * WIN32OLE_METHOD#name
+ * name
*
* Returns the name of the method.
*
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbook')
- * method = WIN32OLE_METHOD.new(tobj, 'SaveAs')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Workbook')
+ * method = WIN32OLE::Method.new(tobj, 'SaveAs')
* puts method.name # => SaveAs
*
*/
@@ -317,11 +317,11 @@ ole_method_return_type(ITypeInfo *pTypeInfo, UINT method_index)
/*
* call-seq:
- * WIN32OLE_METHOD#return_type
+ * return_type
*
* Returns string of return value type of method.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbooks')
- * method = WIN32OLE_METHOD.new(tobj, 'Add')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Workbooks')
+ * method = WIN32OLE::Method.new(tobj, 'Add')
* puts method.return_type # => Workbook
*
*/
@@ -351,11 +351,11 @@ ole_method_return_vtype(ITypeInfo *pTypeInfo, UINT method_index)
/*
* call-seq:
- * WIN32OLE_METHOD#return_vtype
+ * return_vtype
*
* Returns number of return value type of method.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbooks')
- * method = WIN32OLE_METHOD.new(tobj, 'Add')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Workbooks')
+ * method = WIN32OLE::Method.new(tobj, 'Add')
* puts method.return_vtype # => 26
*
*/
@@ -385,12 +385,12 @@ ole_method_return_type_detail(ITypeInfo *pTypeInfo, UINT method_index)
/*
* call-seq:
- * WIN32OLE_METHOD#return_type_detail
+ * return_type_detail
*
* Returns detail information of return value type of method.
* The information is array.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbooks')
- * method = WIN32OLE_METHOD.new(tobj, 'Add')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Workbooks')
+ * method = WIN32OLE::Method.new(tobj, 'Add')
* p method.return_type_detail # => ["PTR", "USERDEFINED", "Workbook"]
*/
static VALUE
@@ -437,11 +437,11 @@ ole_method_invoke_kind(ITypeInfo *pTypeInfo, UINT method_index)
/*
* call-seq:
- * WIN32OLE_METHOD#invkind
+ * invkind
*
* Returns the method invoke kind.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbooks')
- * method = WIN32OLE_METHOD.new(tobj, 'Add')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Workbooks')
+ * method = WIN32OLE::Method.new(tobj, 'Add')
* puts method.invkind # => 1
*
*/
@@ -455,13 +455,13 @@ folemethod_invkind(VALUE self)
/*
* call-seq:
- * WIN32OLE_METHOD#invoke_kind
+ * invoke_kind
*
* Returns the method kind string. The string is "UNKNOWN" or "PROPERTY"
* or "PROPERTY" or "PROPERTYGET" or "PROPERTYPUT" or "PROPERTYPPUTREF"
* or "FUNC".
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbooks')
- * method = WIN32OLE_METHOD.new(tobj, 'Add')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Workbooks')
+ * method = WIN32OLE::Method.new(tobj, 'Add')
* puts method.invoke_kind # => "FUNC"
*/
static VALUE
@@ -494,11 +494,11 @@ ole_method_visible(ITypeInfo *pTypeInfo, UINT method_index)
/*
* call-seq:
- * WIN32OLE_METHOD#visible?
+ * visible?
*
* Returns true if the method is public.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbooks')
- * method = WIN32OLE_METHOD.new(tobj, 'Add')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Workbooks')
+ * method = WIN32OLE::Method.new(tobj, 'Add')
* puts method.visible? # => true
*/
static VALUE
@@ -575,11 +575,11 @@ ole_method_event(ITypeInfo *pTypeInfo, UINT method_index, VALUE method_name)
/*
* call-seq:
- * WIN32OLE_METHOD#event?
+ * event?
*
* Returns true if the method is event.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbook')
- * method = WIN32OLE_METHOD.new(tobj, 'SheetActivate')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Workbook')
+ * method = WIN32OLE::Method.new(tobj, 'SheetActivate')
* puts method.event? # => true
*
*/
@@ -597,11 +597,11 @@ folemethod_event(VALUE self)
/*
* call-seq:
- * WIN32OLE_METHOD#event_interface
+ * event_interface
*
* Returns event interface name if the method is event.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbook')
- * method = WIN32OLE_METHOD.new(tobj, 'SheetActivate')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Workbook')
+ * method = WIN32OLE::Method.new(tobj, 'SheetActivate')
* puts method.event_interface # => WorkbookEvents
*/
static VALUE
@@ -655,12 +655,12 @@ ole_method_helpstring(ITypeInfo *pTypeInfo, UINT method_index)
/*
* call-seq:
- * WIN32OLE_METHOD#helpstring
+ * helpstring
*
* Returns help string of OLE method. If the help string is not found,
* then the method returns nil.
- * tobj = WIN32OLE_TYPE.new('Microsoft Internet Controls', 'IWebBrowser')
- * method = WIN32OLE_METHOD.new(tobj, 'Navigate')
+ * tobj = WIN32OLE::Type.new('Microsoft Internet Controls', 'IWebBrowser')
+ * method = WIN32OLE::Method.new(tobj, 'Navigate')
* puts method.helpstring # => Navigates to a URL or file.
*
*/
@@ -686,12 +686,12 @@ ole_method_helpfile(ITypeInfo *pTypeInfo, UINT method_index)
/*
* call-seq:
- * WIN32OLE_METHOD#helpfile
+ * helpfile
*
* Returns help file. If help file is not found, then
* the method returns nil.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbooks')
- * method = WIN32OLE_METHOD.new(tobj, 'Add')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Workbooks')
+ * method = WIN32OLE::Method.new(tobj, 'Add')
* puts method.helpfile # => C:\...\VBAXL9.CHM
*/
static VALUE
@@ -717,11 +717,11 @@ ole_method_helpcontext(ITypeInfo *pTypeInfo, UINT method_index)
/*
* call-seq:
- * WIN32OLE_METHOD#helpcontext
+ * helpcontext
*
* Returns help context.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbooks')
- * method = WIN32OLE_METHOD.new(tobj, 'Add')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Workbooks')
+ * method = WIN32OLE::Method.new(tobj, 'Add')
* puts method.helpcontext # => 65717
*/
static VALUE
@@ -748,11 +748,11 @@ ole_method_dispid(ITypeInfo *pTypeInfo, UINT method_index)
/*
* call-seq:
- * WIN32OLE_METHOD#dispid
+ * dispid
*
* Returns dispatch ID.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbooks')
- * method = WIN32OLE_METHOD.new(tobj, 'Add')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Workbooks')
+ * method = WIN32OLE::Method.new(tobj, 'Add')
* puts method.dispid # => 181
*/
static VALUE
@@ -779,11 +779,11 @@ ole_method_offset_vtbl(ITypeInfo *pTypeInfo, UINT method_index)
/*
* call-seq:
- * WIN32OLE_METHOD#offset_vtbl
+ * offset_vtbl
*
* Returns the offset ov VTBL.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbooks')
- * method = WIN32OLE_METHOD.new(tobj, 'Add')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Workbooks')
+ * method = WIN32OLE::Method.new(tobj, 'Add')
* puts method.offset_vtbl # => 40
*/
static VALUE
@@ -810,11 +810,11 @@ ole_method_size_params(ITypeInfo *pTypeInfo, UINT method_index)
/*
* call-seq:
- * WIN32OLE_METHOD#size_params
+ * size_params
*
* Returns the size of arguments of the method.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbook')
- * method = WIN32OLE_METHOD.new(tobj, 'SaveAs')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Workbook')
+ * method = WIN32OLE::Method.new(tobj, 'SaveAs')
* puts method.size_params # => 11
*
*/
@@ -842,11 +842,11 @@ ole_method_size_opt_params(ITypeInfo *pTypeInfo, UINT method_index)
/*
* call-seq:
- * WIN32OLE_METHOD#size_opt_params
+ * size_opt_params
*
* Returns the size of optional parameters.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbook')
- * method = WIN32OLE_METHOD.new(tobj, 'SaveAs')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Workbook')
+ * method = WIN32OLE::Method.new(tobj, 'SaveAs')
* puts method.size_opt_params # => 4
*/
static VALUE
@@ -892,11 +892,11 @@ ole_method_params(ITypeInfo *pTypeInfo, UINT method_index)
/*
* call-seq:
- * WIN32OLE_METHOD#params
+ * params
*
- * returns array of WIN32OLE_PARAM object corresponding with method parameters.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbook')
- * method = WIN32OLE_METHOD.new(tobj, 'SaveAs')
+ * returns array of WIN32OLE::Param object corresponding with method parameters.
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Workbook')
+ * method = WIN32OLE::Method.new(tobj, 'SaveAs')
* p method.params # => [Filename, FileFormat, Password, WriteResPassword,
* ReadOnlyRecommended, CreateBackup, AccessMode,
* ConflictResolution, AddToMru, TextCodepage,
@@ -912,7 +912,7 @@ folemethod_params(VALUE self)
/*
* call-seq:
- * WIN32OLE_METHOD#inspect -> String
+ * inspect -> String
*
* Returns the method name with class name.
*
@@ -920,7 +920,7 @@ folemethod_params(VALUE self)
static VALUE
folemethod_inspect(VALUE self)
{
- return default_inspect(self, "WIN32OLE_METHOD");
+ return default_inspect(self, "WIN32OLE::Method");
}
VALUE cWIN32OLE_METHOD;
@@ -928,6 +928,7 @@ VALUE cWIN32OLE_METHOD;
void Init_win32ole_method(void)
{
cWIN32OLE_METHOD = rb_define_class_under(cWIN32OLE, "Method", rb_cObject);
+ /* Alias of WIN32OLE::Method, for the backward compatibility */
rb_define_const(rb_cObject, "WIN32OLE_METHOD", cWIN32OLE_METHOD);
rb_define_alloc_func(cWIN32OLE_METHOD, folemethod_s_allocate);
rb_define_method(cWIN32OLE_METHOD, "initialize", folemethod_initialize, 2);
diff --git a/ext/win32ole/win32ole_param.c b/ext/win32ole/win32ole_param.c
index b654aaa845..0c4b185921 100644
--- a/ext/win32ole/win32ole_param.c
+++ b/ext/win32ole/win32ole_param.c
@@ -64,9 +64,9 @@ create_win32ole_param(ITypeInfo *pTypeInfo, UINT method_index, UINT index, VALUE
}
/*
- * Document-class: WIN32OLE_PARAM
+ * Document-class: WIN32OLE::Param
*
- * <code>WIN32OLE_PARAM</code> objects represent param information of
+ * +WIN32OLE::Param+ objects represent param information of
* the OLE method.
*/
static VALUE
@@ -131,15 +131,15 @@ oleparam_ole_param(VALUE self, VALUE olemethod, int n)
/*
* call-seq:
- * WIN32OLE_PARAM.new(method, n) -> WIN32OLE_PARAM object
+ * new(method, n) -> WIN32OLE::Param object
*
- * Returns WIN32OLE_PARAM object which represents OLE parameter information.
- * 1st argument should be WIN32OLE_METHOD object.
+ * Returns WIN32OLE::Param object which represents OLE parameter information.
+ * 1st argument should be WIN32OLE::Method object.
* 2nd argument `n' is n-th parameter of the method specified by 1st argument.
*
- * tobj = WIN32OLE_TYPE.new('Microsoft Scripting Runtime', 'IFileSystem')
- * method = WIN32OLE_METHOD.new(tobj, 'CreateTextFile')
- * param = WIN32OLE_PARAM.new(method, 2) # => #<WIN32OLE_PARAM:Overwrite=true>
+ * tobj = WIN32OLE::Type.new('Microsoft Scripting Runtime', 'IFileSystem')
+ * method = WIN32OLE::Method.new(tobj, 'CreateTextFile')
+ * param = WIN32OLE::Param.new(method, 2) # => #<WIN32OLE::Param:Overwrite=true>
*
*/
static VALUE
@@ -147,7 +147,7 @@ foleparam_initialize(VALUE self, VALUE olemethod, VALUE n)
{
int idx;
if (!rb_obj_is_kind_of(olemethod, cWIN32OLE_METHOD)) {
- rb_raise(rb_eTypeError, "1st parameter must be WIN32OLE_METHOD object");
+ rb_raise(rb_eTypeError, "1st parameter must be WIN32OLE::Method object");
}
idx = RB_FIX2INT(n);
return oleparam_ole_param(self, olemethod, idx);
@@ -155,11 +155,11 @@ foleparam_initialize(VALUE self, VALUE olemethod, VALUE n)
/*
* call-seq:
- * WIN32OLE_PARAM#name
+ * name
*
* Returns name.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbook')
- * method = WIN32OLE_METHOD.new(tobj, 'SaveAs')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Workbook')
+ * method = WIN32OLE::Method.new(tobj, 'SaveAs')
* param1 = method.params[0]
* puts param1.name # => Filename
*/
@@ -186,11 +186,11 @@ ole_param_ole_type(ITypeInfo *pTypeInfo, UINT method_index, UINT index)
/*
* call-seq:
- * WIN32OLE_PARAM#ole_type
+ * ole_type
*
- * Returns OLE type of WIN32OLE_PARAM object(parameter of OLE method).
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbook')
- * method = WIN32OLE_METHOD.new(tobj, 'SaveAs')
+ * Returns OLE type of WIN32OLE::Param object(parameter of OLE method).
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Workbook')
+ * method = WIN32OLE::Method.new(tobj, 'SaveAs')
* param1 = method.params[0]
* puts param1.ole_type # => VARIANT
*/
@@ -220,11 +220,11 @@ ole_param_ole_type_detail(ITypeInfo *pTypeInfo, UINT method_index, UINT index)
/*
* call-seq:
- * WIN32OLE_PARAM#ole_type_detail
+ * ole_type_detail
*
* Returns detail information of type of argument.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'IWorksheetFunction')
- * method = WIN32OLE_METHOD.new(tobj, 'SumIf')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'IWorksheetFunction')
+ * method = WIN32OLE::Method.new(tobj, 'SumIf')
* param1 = method.params[0]
* p param1.ole_type_detail # => ["PTR", "USERDEFINED", "Range"]
*/
@@ -254,11 +254,11 @@ ole_param_flag_mask(ITypeInfo *pTypeInfo, UINT method_index, UINT index, USHORT
/*
* call-seq:
- * WIN32OLE_PARAM#input?
+ * input?
*
* Returns true if the parameter is input.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbook')
- * method = WIN32OLE_METHOD.new(tobj, 'SaveAs')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Workbook')
+ * method = WIN32OLE::Method.new(tobj, 'SaveAs')
* param1 = method.params[0]
* puts param1.input? # => true
*/
@@ -273,22 +273,22 @@ foleparam_input(VALUE self)
/*
* call-seq:
- * WIN32OLE#output?
+ * output?
*
* Returns true if argument is output.
- * tobj = WIN32OLE_TYPE.new('Microsoft Internet Controls', 'DWebBrowserEvents')
- * method = WIN32OLE_METHOD.new(tobj, 'NewWindow')
+ * tobj = WIN32OLE::Type.new('Microsoft Internet Controls', 'DWebBrowserEvents')
+ * method = WIN32OLE::Method.new(tobj, 'NewWindow')
* method.params.each do |param|
* puts "#{param.name} #{param.output?}"
* end
*
- * The result of above script is following:
- * URL false
- * Flags false
- * TargetFrameName false
- * PostData false
- * Headers false
- * Processed true
+ * The result of above script is following:
+ * URL false
+ * Flags false
+ * TargetFrameName false
+ * PostData false
+ * Headers false
+ * Processed true
*/
static VALUE
foleparam_output(VALUE self)
@@ -301,11 +301,11 @@ foleparam_output(VALUE self)
/*
* call-seq:
- * WIN32OLE_PARAM#optional?
+ * optional?
*
* Returns true if argument is optional.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbook')
- * method = WIN32OLE_METHOD.new(tobj, 'SaveAs')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Workbook')
+ * method = WIN32OLE::Method.new(tobj, 'SaveAs')
* param1 = method.params[0]
* puts "#{param1.name} #{param1.optional?}" # => Filename true
*/
@@ -320,12 +320,12 @@ foleparam_optional(VALUE self)
/*
* call-seq:
- * WIN32OLE_PARAM#retval?
+ * retval?
*
* Returns true if argument is return value.
- * tobj = WIN32OLE_TYPE.new('DirectX 7 for Visual Basic Type Library',
- * 'DirectPlayLobbyConnection')
- * method = WIN32OLE_METHOD.new(tobj, 'GetPlayerShortName')
+ * tobj = WIN32OLE::Type.new('DirectX 7 for Visual Basic Type Library',
+ * 'DirectPlayLobbyConnection')
+ * method = WIN32OLE::Method.new(tobj, 'GetPlayerShortName')
* param = method.params[0]
* puts "#{param.name} #{param.retval?}" # => name true
*/
@@ -363,12 +363,12 @@ ole_param_default(ITypeInfo *pTypeInfo, UINT method_index, UINT index)
/*
* call-seq:
- * WIN32OLE_PARAM#default
+ * default
*
* Returns default value. If the default value does not exist,
* this method returns nil.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Workbook')
- * method = WIN32OLE_METHOD.new(tobj, 'SaveAs')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Workbook')
+ * method = WIN32OLE::Method.new(tobj, 'SaveAs')
* method.params.each do |param|
* if param.default
* puts "#{param.name} (= #{param.default})"
@@ -377,18 +377,18 @@ ole_param_default(ITypeInfo *pTypeInfo, UINT method_index, UINT index)
* end
* end
*
- * The above script result is following:
- * Filename
- * FileFormat
- * Password
- * WriteResPassword
- * ReadOnlyRecommended
- * CreateBackup
- * AccessMode (= 1)
- * ConflictResolution
- * AddToMru
- * TextCodepage
- * TextVisualLayout
+ * The above script result is following:
+ * Filename
+ * FileFormat
+ * Password
+ * WriteResPassword
+ * ReadOnlyRecommended
+ * CreateBackup
+ * AccessMode (= 1)
+ * ConflictResolution
+ * AddToMru
+ * TextCodepage
+ * TextVisualLayout
*/
static VALUE
foleparam_default(VALUE self)
@@ -401,7 +401,7 @@ foleparam_default(VALUE self)
/*
* call-seq:
- * WIN32OLE_PARAM#inspect -> String
+ * inspect -> String
*
* Returns the parameter name with class name. If the parameter has default value,
* then returns name=value string with class name.
@@ -416,13 +416,14 @@ foleparam_inspect(VALUE self)
rb_str_cat2(detail, "=");
rb_str_concat(detail, rb_inspect(defval));
}
- return make_inspect("WIN32OLE_PARAM", detail);
+ return make_inspect("WIN32OLE::Param", detail);
}
void
Init_win32ole_param(void)
{
cWIN32OLE_PARAM = rb_define_class_under(cWIN32OLE, "Param", rb_cObject);
+ /* Alias of WIN32OLE::Param, for the backward compatibility */
rb_define_const(rb_cObject, "WIN32OLE_PARAM", cWIN32OLE_PARAM);
rb_define_alloc_func(cWIN32OLE_PARAM, foleparam_s_allocate);
rb_define_method(cWIN32OLE_PARAM, "initialize", foleparam_initialize, 2);
diff --git a/ext/win32ole/win32ole_record.c b/ext/win32ole/win32ole_record.c
index 9e18653db9..02f05a3fa7 100644
--- a/ext/win32ole/win32ole_record.c
+++ b/ext/win32ole/win32ole_record.c
@@ -177,10 +177,10 @@ create_win32ole_record(IRecordInfo *pri, void *prec)
}
/*
- * Document-class: WIN32OLE_RECORD
+ * Document-class: WIN32OLE::Record
*
- * <code>WIN32OLE_RECORD</code> objects represents VT_RECORD OLE variant.
- * Win32OLE returns WIN32OLE_RECORD object if the result value of invoking
+ * +WIN32OLE::Record+ objects represents VT_RECORD OLE variant.
+ * Win32OLE returns WIN32OLE::Record object if the result value of invoking
* OLE methods.
*
* If COM server in VB.NET ComServer project is the following:
@@ -206,7 +206,7 @@ create_win32ole_record(IRecordInfo *pri, void *prec)
* require 'win32ole'
* obj = WIN32OLE.new('ComServer.ComClass')
* book = obj.getBook
- * book.class # => WIN32OLE_RECORD
+ * book.class # => WIN32OLE::Record
* book.title # => "The Ruby Book"
* book.cost # => 20
*
@@ -253,11 +253,11 @@ folerecord_s_allocate(VALUE klass) {
/*
* call-seq:
- * WIN32OLE_RECORD.new(typename, obj) -> WIN32OLE_RECORD object
+ * new(typename, obj) -> WIN32OLE::Record object
*
- * Returns WIN32OLE_RECORD object. The first argument is struct name (String
+ * Returns WIN32OLE::Record object. The first argument is struct name (String
* or Symbol).
- * The second parameter obj should be WIN32OLE object or WIN32OLE_TYPELIB object.
+ * The second parameter obj should be WIN32OLE object or WIN32OLE::TypeLib object.
* If COM server in VB.NET ComServer project is the following:
*
* Imports System.Runtime.InteropServices
@@ -269,13 +269,13 @@ folerecord_s_allocate(VALUE klass) {
* End Structure
* End Class
*
- * then, you can create WIN32OLE_RECORD object is as following:
+ * then, you can create WIN32OLE::Record object is as following:
*
* require 'win32ole'
* obj = WIN32OLE.new('ComServer.ComClass')
- * book1 = WIN32OLE_RECORD.new('Book', obj) # => WIN32OLE_RECORD object
+ * book1 = WIN32OLE::Record.new('Book', obj) # => WIN32OLE::Record object
* tlib = obj.ole_typelib
- * book2 = WIN32OLE_RECORD.new('Book', tlib) # => WIN32OLE_RECORD object
+ * book2 = WIN32OLE::Record.new('Book', tlib) # => WIN32OLE::Record object
*
*/
static VALUE
@@ -303,7 +303,7 @@ folerecord_initialize(VALUE self, VALUE typename, VALUE oleobj) {
hr = E_FAIL;
}
} else {
- rb_raise(rb_eArgError, "2nd argument should be WIN32OLE object or WIN32OLE_TYPELIB object");
+ rb_raise(rb_eArgError, "2nd argument should be WIN32OLE object or WIN32OLE::TypeLib object");
}
if (FAILED(hr)) {
@@ -323,7 +323,7 @@ folerecord_initialize(VALUE self, VALUE typename, VALUE oleobj) {
/*
* call-seq:
- * WIN32OLE_RECORD#to_h #=> Ruby Hash object.
+ * WIN32OLE::Record#to_h #=> Ruby Hash object.
*
* Returns Ruby Hash object which represents VT_RECORD variable.
* The keys of Hash object are member names of VT_RECORD OLE variable and
@@ -346,7 +346,7 @@ folerecord_initialize(VALUE self, VALUE typename, VALUE oleobj) {
* End Function
* End Class
*
- * then, the result of WIN32OLE_RECORD#to_h is the following:
+ * then, the result of WIN32OLE::Record#to_h is the following:
*
* require 'win32ole'
* obj = WIN32OLE.new('ComServer.ComClass')
@@ -362,7 +362,7 @@ folerecord_to_h(VALUE self)
/*
* call-seq:
- * WIN32OLE_RECORD#typename #=> String object
+ * typename #=> String object
*
* Returns the type name of VT_RECORD OLE variable.
*
@@ -383,7 +383,7 @@ folerecord_to_h(VALUE self)
* End Function
* End Class
*
- * then, the result of WIN32OLE_RECORD#typename is the following:
+ * then, the result of WIN32OLE::Record#typename is the following:
*
* require 'win32ole'
* obj = WIN32OLE.new('ComServer.ComClass')
@@ -423,7 +423,7 @@ olerecord_ivar_set(VALUE self, VALUE name, VALUE val)
/*
* call-seq:
- * WIN32OLE_RECORD#method_missing(name)
+ * method_missing(name)
*
* Returns value specified by the member name of VT_RECORD OLE variable.
* Or sets value specified by the member name of VT_RECORD OLE variable.
@@ -443,7 +443,7 @@ olerecord_ivar_set(VALUE self, VALUE name, VALUE val)
* Then getting/setting value from Ruby is as the following:
*
* obj = WIN32OLE.new('ComServer.ComClass')
- * book = WIN32OLE_RECORD.new('Book', obj)
+ * book = WIN32OLE::Record.new('Book', obj)
* book.title # => nil ( book.method_missing(:title) is invoked. )
* book.title = "Ruby" # ( book.method_missing(:title=, "Ruby") is invoked. )
*/
@@ -473,7 +473,7 @@ folerecord_method_missing(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * WIN32OLE_RECORD#ole_instance_variable_get(name)
+ * ole_instance_variable_get(name)
*
* Returns value specified by the member name of VT_RECORD OLE object.
* If the member name is not correct, KeyError exception is raised.
@@ -494,7 +494,7 @@ folerecord_method_missing(int argc, VALUE *argv, VALUE self)
* then accessing object_id of ComObject from Ruby is as the following:
*
* srver = WIN32OLE.new('ComServer.ComClass')
- * obj = WIN32OLE_RECORD.new('ComObject', server)
+ * obj = WIN32OLE::Record.new('ComObject', server)
* # obj.object_id returns Ruby Object#object_id
* obj.ole_instance_variable_get(:object_id) # => nil
*
@@ -515,7 +515,7 @@ folerecord_ole_instance_variable_get(VALUE self, VALUE name)
/*
* call-seq:
- * WIN32OLE_RECORD#ole_instance_variable_set(name, val)
+ * ole_instance_variable_set(name, val)
*
* Sets value specified by the member name of VT_RECORD OLE object.
* If the member name is not correct, KeyError exception is raised.
@@ -534,7 +534,7 @@ folerecord_ole_instance_variable_get(VALUE self, VALUE name)
* then setting value of the `title' member is as following:
*
* srver = WIN32OLE.new('ComServer.ComClass')
- * obj = WIN32OLE_RECORD.new('Book', server)
+ * obj = WIN32OLE::Record.new('Book', server)
* obj.ole_instance_variable_set(:title, "The Ruby Book")
*
*/
@@ -554,7 +554,7 @@ folerecord_ole_instance_variable_set(VALUE self, VALUE name, VALUE val)
/*
* call-seq:
- * WIN32OLE_RECORD#inspect -> String
+ * inspect -> String
*
* Returns the OLE struct name and member name and the value of member
*
@@ -570,8 +570,8 @@ folerecord_ole_instance_variable_set(VALUE self, VALUE name, VALUE val)
* then
*
* srver = WIN32OLE.new('ComServer.ComClass')
- * obj = WIN32OLE_RECORD.new('Book', server)
- * obj.inspect # => <WIN32OLE_RECORD(ComClass) {"title" => nil, "cost" => nil}>
+ * obj = WIN32OLE::Record.new('Book', server)
+ * obj.inspect # => <WIN32OLE::Record(ComClass) {"title" => nil, "cost" => nil}>
*
*/
static VALUE
@@ -584,7 +584,7 @@ folerecord_inspect(VALUE self)
tname = rb_inspect(tname);
}
field = rb_inspect(folerecord_to_h(self));
- return rb_sprintf("#<WIN32OLE_RECORD(%"PRIsVALUE") %"PRIsVALUE">",
+ return rb_sprintf("#<WIN32OLE::Record(%"PRIsVALUE") %"PRIsVALUE">",
tname,
field);
}
@@ -595,6 +595,7 @@ void
Init_win32ole_record(void)
{
cWIN32OLE_RECORD = rb_define_class_under(cWIN32OLE, "Record", rb_cObject);
+ /* Alias of WIN32OLE::Record, for the backward compatibility */
rb_define_const(rb_cObject, "WIN32OLE_RECORD", cWIN32OLE_RECORD);
rb_define_alloc_func(cWIN32OLE_RECORD, folerecord_s_allocate);
rb_define_method(cWIN32OLE_RECORD, "initialize", folerecord_initialize, 2);
diff --git a/ext/win32ole/win32ole_type.c b/ext/win32ole/win32ole_type.c
index 1b96aea858..45b16d6722 100644
--- a/ext/win32ole/win32ole_type.c
+++ b/ext/win32ole/win32ole_type.c
@@ -54,9 +54,9 @@ static const rb_data_type_t oletype_datatype = {
};
/*
- * Document-class: WIN32OLE_TYPE
+ * Document-class: WIN32OLE::Type
*
- * <code>WIN32OLE_TYPE</code> objects represent OLE type library information.
+ * +WIN32OLE::Type+ objects represent OLE type library information.
*/
static void
@@ -106,10 +106,12 @@ ole_type_from_itypeinfo(ITypeInfo *pTypeInfo)
/*
* call-seq:
- * WIN32OLE_TYPE.ole_classes(typelib)
+ * ole_classes(typelib)
*
- * Returns array of WIN32OLE_TYPE objects defined by the <i>typelib</i> type library.
- * This method will be OBSOLETE. Use WIN32OLE_TYPELIB.new(typelib).ole_classes instead.
+ * Returns array of WIN32OLE::Type objects defined by the <i>typelib</i> type library.
+ *
+ * This method will be OBSOLETE.
+ * Use <code>WIN32OLE::TypeLib.new(typelib).ole_classes</code> instead.
*/
static VALUE
foletype_s_ole_classes(VALUE self, VALUE typelib)
@@ -118,8 +120,8 @@ foletype_s_ole_classes(VALUE self, VALUE typelib)
/*
rb_warn("%s is obsolete; use %s instead.",
- "WIN32OLE_TYPE.ole_classes",
- "WIN32OLE_TYPELIB.new(typelib).ole_types");
+ "WIN32OLE::Type.ole_classes",
+ "WIN32OLE::TypeLib.new(typelib).ole_types");
*/
obj = rb_funcall(cWIN32OLE_TYPELIB, rb_intern("new"), 1, typelib);
return rb_funcall(obj, rb_intern("ole_types"), 0);
@@ -127,10 +129,12 @@ foletype_s_ole_classes(VALUE self, VALUE typelib)
/*
* call-seq:
- * WIN32OLE_TYPE.typelibs
+ * typelibs
*
* Returns array of type libraries.
- * This method will be OBSOLETE. Use WIN32OLE_TYPELIB.typelibs.collect{|t| t.name} instead.
+ *
+ * This method will be OBSOLETE.
+ * Use <code>WIN32OLE::TypeLib.typelibs.collect{|t| t.name}</code> instead.
*
*/
static VALUE
@@ -138,15 +142,15 @@ foletype_s_typelibs(VALUE self)
{
/*
rb_warn("%s is obsolete. use %s instead.",
- "WIN32OLE_TYPE.typelibs",
- "WIN32OLE_TYPELIB.typelibs.collect{t|t.name}");
+ "WIN32OLE::Type.typelibs",
+ "WIN32OLE::TypeLib.typelibs.collect{t|t.name}");
*/
- return rb_eval_string("WIN32OLE_TYPELIB.typelibs.collect{|t|t.name}");
+ return rb_eval_string("WIN32OLE::TypeLib.typelibs.collect{|t|t.name}");
}
/*
* call-seq:
- * WIN32OLE_TYPE.progids
+ * progids
*
* Returns array of ProgID.
*/
@@ -214,7 +218,6 @@ create_win32ole_type(ITypeInfo *pTypeInfo, VALUE name)
static VALUE
oleclass_from_typelib(VALUE self, ITypeLib *pTypeLib, VALUE oleclass)
{
-
long count;
int i;
HRESULT hr;
@@ -245,14 +248,14 @@ oleclass_from_typelib(VALUE self, ITypeLib *pTypeLib, VALUE oleclass)
/*
* call-seq:
- * WIN32OLE_TYPE.new(typelib, ole_class) -> WIN32OLE_TYPE object
+ * new(typelib, ole_class) -> WIN32OLE::Type object
*
- * Returns a new WIN32OLE_TYPE object.
+ * Returns a new WIN32OLE::Type object.
* The first argument <i>typelib</i> specifies OLE type library name.
* The second argument specifies OLE class name.
*
- * WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Application')
- * # => WIN32OLE_TYPE object of Application class of Excel.
+ * WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Application')
+ * # => WIN32OLE::Type object of Application class of Excel.
*/
static VALUE
foletype_initialize(VALUE self, VALUE typelib, VALUE oleclass)
@@ -284,10 +287,10 @@ foletype_initialize(VALUE self, VALUE typelib, VALUE oleclass)
/*
* call-seq:
- * WIN32OLE_TYPE#name #=> OLE type name
+ * name #=> OLE type name
*
* Returns OLE type name.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Application')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Application')
* puts tobj.name # => Application
*/
static VALUE
@@ -344,10 +347,10 @@ ole_ole_type(ITypeInfo *pTypeInfo)
/*
* call-seq:
- * WIN32OLE_TYPE#ole_type #=> OLE type string.
+ * ole_type #=> OLE type string.
*
* returns type of OLE class.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Application')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Application')
* puts tobj.ole_type # => Class
*/
static VALUE
@@ -378,10 +381,10 @@ ole_type_guid(ITypeInfo *pTypeInfo)
/*
* call-seq:
- * WIN32OLE_TYPE#guid #=> GUID
+ * guid #=> GUID
*
* Returns GUID.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Application')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Application')
* puts tobj.guid # => {00024500-0000-0000-C000-000000000046}
*/
static VALUE
@@ -412,10 +415,10 @@ ole_type_progid(ITypeInfo *pTypeInfo)
/*
* call-seq:
- * WIN32OLE_TYPE#progid #=> ProgID
+ * progid #=> ProgID
*
* Returns ProgID if it exists. If not found, then returns nil.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Application')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Application')
* puts tobj.progid # => Excel.Application.9
*/
static VALUE
@@ -446,10 +449,10 @@ ole_type_visible(ITypeInfo *pTypeInfo)
/*
* call-seq:
- * WIN32OLE_TYPE#visible? #=> true or false
+ * visible? #=> true or false
*
* Returns true if the OLE class is public.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Application')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Application')
* puts tobj.visible # => true
*/
static VALUE
@@ -475,10 +478,10 @@ ole_type_major_version(ITypeInfo *pTypeInfo)
/*
* call-seq:
- * WIN32OLE_TYPE#major_version
+ * major_version
*
* Returns major version.
- * tobj = WIN32OLE_TYPE.new('Microsoft Word 10.0 Object Library', 'Documents')
+ * tobj = WIN32OLE::Type.new('Microsoft Word 10.0 Object Library', 'Documents')
* puts tobj.major_version # => 8
*/
static VALUE
@@ -504,10 +507,10 @@ ole_type_minor_version(ITypeInfo *pTypeInfo)
/*
* call-seq:
- * WIN32OLE_TYPE#minor_version #=> OLE minor version
+ * minor_version #=> OLE minor version
*
* Returns minor version.
- * tobj = WIN32OLE_TYPE.new('Microsoft Word 10.0 Object Library', 'Documents')
+ * tobj = WIN32OLE::Type.new('Microsoft Word 10.0 Object Library', 'Documents')
* puts tobj.minor_version # => 2
*/
static VALUE
@@ -533,10 +536,10 @@ ole_type_typekind(ITypeInfo *pTypeInfo)
/*
* call-seq:
- * WIN32OLE_TYPE#typekind #=> number of type.
+ * typekind #=> number of type.
*
* Returns number which represents type.
- * tobj = WIN32OLE_TYPE.new('Microsoft Word 10.0 Object Library', 'Documents')
+ * tobj = WIN32OLE::Type.new('Microsoft Word 10.0 Object Library', 'Documents')
* puts tobj.typekind # => 4
*
*/
@@ -561,10 +564,10 @@ ole_type_helpstring(ITypeInfo *pTypeInfo)
/*
* call-seq:
- * WIN32OLE_TYPE#helpstring #=> help string.
+ * helpstring #=> help string.
*
* Returns help string.
- * tobj = WIN32OLE_TYPE.new('Microsoft Internet Controls', 'IWebBrowser')
+ * tobj = WIN32OLE::Type.new('Microsoft Internet Controls', 'IWebBrowser')
* puts tobj.helpstring # => Web Browser interface
*/
static VALUE
@@ -594,10 +597,10 @@ ole_type_src_type(ITypeInfo *pTypeInfo)
/*
* call-seq:
- * WIN32OLE_TYPE#src_type #=> OLE source class
+ * src_type #=> OLE source class
*
* Returns source class when the OLE class is 'Alias'.
- * tobj = WIN32OLE_TYPE.new('Microsoft Office 9.0 Object Library', 'MsoRGBType')
+ * tobj = WIN32OLE::Type.new('Microsoft Office 9.0 Object Library', 'MsoRGBType')
* puts tobj.src_type # => I4
*
*/
@@ -622,10 +625,10 @@ ole_type_helpfile(ITypeInfo *pTypeInfo)
/*
* call-seq:
- * WIN32OLE_TYPE#helpfile
+ * helpfile
*
* Returns helpfile path. If helpfile is not found, then returns nil.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Worksheet')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Worksheet')
* puts tobj.helpfile # => C:\...\VBAXL9.CHM
*
*/
@@ -650,10 +653,10 @@ ole_type_helpcontext(ITypeInfo *pTypeInfo)
/*
* call-seq:
- * WIN32OLE_TYPE#helpcontext
+ * helpcontext
*
* Returns helpcontext. If helpcontext is not found, then returns nil.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Worksheet')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Worksheet')
* puts tobj.helpfile # => 131185
*/
static VALUE
@@ -701,11 +704,11 @@ ole_variables(ITypeInfo *pTypeInfo)
/*
* call-seq:
- * WIN32OLE_TYPE#variables
+ * variables
*
- * Returns array of WIN32OLE_VARIABLE objects which represent variables
+ * Returns array of WIN32OLE::Variable objects which represent variables
* defined in OLE class.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'XlSheetType')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'XlSheetType')
* vars = tobj.variables
* vars.each do |v|
* puts "#{v.name} = #{v.value}"
@@ -728,11 +731,11 @@ foletype_variables(VALUE self)
/*
* call-seq:
- * WIN32OLE_TYPE#ole_methods # the array of WIN32OLE_METHOD objects.
+ * ole_methods # the array of WIN32OLE::Method objects.
*
- * Returns array of WIN32OLE_METHOD objects which represent OLE method defined in
+ * Returns array of WIN32OLE::Method objects which represent OLE method defined in
* OLE type library.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Worksheet')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Worksheet')
* methods = tobj.ole_methods.collect{|m|
* m.name
* }
@@ -747,11 +750,11 @@ foletype_methods(VALUE self)
/*
* call-seq:
- * WIN32OLE_TYPE#ole_typelib
+ * ole_typelib
*
- * Returns the WIN32OLE_TYPELIB object which is including the WIN32OLE_TYPE
+ * Returns the WIN32OLE::TypeLib object which is including the WIN32OLE::Type
* object. If it is not found, then returns nil.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Worksheet')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Worksheet')
* puts tobj.ole_typelib # => 'Microsoft Excel 9.0 Object Library'
*/
static VALUE
@@ -804,11 +807,11 @@ ole_type_impl_ole_types(ITypeInfo *pTypeInfo, int implflags)
/*
* call-seq:
- * WIN32OLE_TYPE#implemented_ole_types
+ * implemented_ole_types
*
- * Returns the array of WIN32OLE_TYPE object which is implemented by the WIN32OLE_TYPE
+ * Returns the array of WIN32OLE::Type object which is implemented by the WIN32OLE::Type
* object.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'Worksheet')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'Worksheet')
* p tobj.implemented_ole_types # => [_Worksheet, DocEvents]
*/
static VALUE
@@ -820,13 +823,13 @@ foletype_impl_ole_types(VALUE self)
/*
* call-seq:
- * WIN32OLE_TYPE#source_ole_types
+ * source_ole_types
*
- * Returns the array of WIN32OLE_TYPE object which is implemented by the WIN32OLE_TYPE
+ * Returns the array of WIN32OLE::Type object which is implemented by the WIN32OLE::Type
* object and having IMPLTYPEFLAG_FSOURCE.
- * tobj = WIN32OLE_TYPE.new('Microsoft Internet Controls', "InternetExplorer")
+ * tobj = WIN32OLE::Type.new('Microsoft Internet Controls', "InternetExplorer")
* p tobj.source_ole_types
- * # => [#<WIN32OLE_TYPE:DWebBrowserEvents2>, #<WIN32OLE_TYPE:DWebBrowserEvents>]
+ * # => [#<WIN32OLE::Type:DWebBrowserEvents2>, #<WIN32OLE::Type:DWebBrowserEvents>]
*/
static VALUE
foletype_source_ole_types(VALUE self)
@@ -837,12 +840,12 @@ foletype_source_ole_types(VALUE self)
/*
* call-seq:
- * WIN32OLE_TYPE#default_event_sources
+ * default_event_sources
*
- * Returns the array of WIN32OLE_TYPE object which is implemented by the WIN32OLE_TYPE
+ * Returns the array of WIN32OLE::Type object which is implemented by the WIN32OLE::Type
* object and having IMPLTYPEFLAG_FSOURCE and IMPLTYPEFLAG_FDEFAULT.
- * tobj = WIN32OLE_TYPE.new('Microsoft Internet Controls', "InternetExplorer")
- * p tobj.default_event_sources # => [#<WIN32OLE_TYPE:DWebBrowserEvents2>]
+ * tobj = WIN32OLE::Type.new('Microsoft Internet Controls', "InternetExplorer")
+ * p tobj.default_event_sources # => [#<WIN32OLE::Type:DWebBrowserEvents2>]
*/
static VALUE
foletype_default_event_sources(VALUE self)
@@ -853,13 +856,13 @@ foletype_default_event_sources(VALUE self)
/*
* call-seq:
- * WIN32OLE_TYPE#default_ole_types
+ * default_ole_types
*
- * Returns the array of WIN32OLE_TYPE object which is implemented by the WIN32OLE_TYPE
+ * Returns the array of WIN32OLE::Type object which is implemented by the WIN32OLE::Type
* object and having IMPLTYPEFLAG_FDEFAULT.
- * tobj = WIN32OLE_TYPE.new('Microsoft Internet Controls', "InternetExplorer")
+ * tobj = WIN32OLE::Type.new('Microsoft Internet Controls', "InternetExplorer")
* p tobj.default_ole_types
- * # => [#<WIN32OLE_TYPE:IWebBrowser2>, #<WIN32OLE_TYPE:DWebBrowserEvents2>]
+ * # => [#<WIN32OLE::Type:IWebBrowser2>, #<WIN32OLE::Type:DWebBrowserEvents2>]
*/
static VALUE
foletype_default_ole_types(VALUE self)
@@ -870,17 +873,17 @@ foletype_default_ole_types(VALUE self)
/*
* call-seq:
- * WIN32OLE_TYPE#inspect -> String
+ * inspect -> String
*
* Returns the type name with class name.
*
* ie = WIN32OLE.new('InternetExplorer.Application')
- * ie.ole_type.inspect => #<WIN32OLE_TYPE:IWebBrowser2>
+ * ie.ole_type.inspect => #<WIN32OLE::Type:IWebBrowser2>
*/
static VALUE
foletype_inspect(VALUE self)
{
- return default_inspect(self, "WIN32OLE_TYPE");
+ return default_inspect(self, "WIN32OLE::Type");
}
VALUE cWIN32OLE_TYPE;
@@ -888,6 +891,7 @@ VALUE cWIN32OLE_TYPE;
void Init_win32ole_type(void)
{
cWIN32OLE_TYPE = rb_define_class_under(cWIN32OLE, "Type", rb_cObject);
+ /* Alias of WIN32OLE::Type, for the backward compatibility */
rb_define_const(rb_cObject, "WIN32OLE_TYPE", cWIN32OLE_TYPE);
rb_define_singleton_method(cWIN32OLE_TYPE, "ole_classes", foletype_s_ole_classes, 1);
rb_define_singleton_method(cWIN32OLE_TYPE, "typelibs", foletype_s_typelibs, 0);
diff --git a/ext/win32ole/win32ole_typelib.c b/ext/win32ole/win32ole_typelib.c
index fb68bebda8..e5eda07e76 100644
--- a/ext/win32ole/win32ole_typelib.c
+++ b/ext/win32ole/win32ole_typelib.c
@@ -127,19 +127,19 @@ ole_typelib_from_itypeinfo(ITypeInfo *pTypeInfo)
}
/*
- * Document-class: WIN32OLE_TYPELIB
+ * Document-class: WIN32OLE::TypeLib
*
- * <code>WIN32OLE_TYPELIB</code> objects represent OLE tyblib information.
+ * +WIN32OLE::TypeLib+ objects represent OLE tyblib information.
*/
/*
* call-seq:
*
- * WIN32OLE_TYPELIB.typelibs
+ * typelibs
*
- * Returns the array of WIN32OLE_TYPELIB object.
+ * Returns the array of WIN32OLE::TypeLib object.
*
- * tlibs = WIN32OLE_TYPELIB.typelibs
+ * tlibs = WIN32OLE::TypeLib.typelibs
*
*/
static VALUE
@@ -364,9 +364,9 @@ oletypelib_search_registry2(VALUE self, VALUE args)
/*
* call-seq:
- * WIN32OLE_TYPELIB.new(typelib [, version1, version2]) -> WIN32OLE_TYPELIB object
+ * new(typelib [, version1, version2]) -> WIN32OLE::TypeLib object
*
- * Returns a new WIN32OLE_TYPELIB object.
+ * Returns a new WIN32OLE::TypeLib object.
*
* The first argument <i>typelib</i> specifies OLE type library name or GUID or
* OLE library file.
@@ -376,11 +376,11 @@ oletypelib_search_registry2(VALUE self, VALUE args)
* If the first argument is type library name, then the second and third argument
* are ignored.
*
- * tlib1 = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library')
- * tlib2 = WIN32OLE_TYPELIB.new('{00020813-0000-0000-C000-000000000046}')
- * tlib3 = WIN32OLE_TYPELIB.new('{00020813-0000-0000-C000-000000000046}', 1.3)
- * tlib4 = WIN32OLE_TYPELIB.new('{00020813-0000-0000-C000-000000000046}', 1, 3)
- * tlib5 = WIN32OLE_TYPELIB.new("C:\\WINNT\\SYSTEM32\\SHELL32.DLL")
+ * tlib1 = WIN32OLE::TypeLib.new('Microsoft Excel 9.0 Object Library')
+ * tlib2 = WIN32OLE::TypeLib.new('{00020813-0000-0000-C000-000000000046}')
+ * tlib3 = WIN32OLE::TypeLib.new('{00020813-0000-0000-C000-000000000046}', 1.3)
+ * tlib4 = WIN32OLE::TypeLib.new('{00020813-0000-0000-C000-000000000046}', 1, 3)
+ * tlib5 = WIN32OLE::TypeLib.new("C:\\WINNT\\SYSTEM32\\SHELL32.DLL")
* puts tlib1.name # -> 'Microsoft Excel 9.0 Object Library'
* puts tlib2.name # -> 'Microsoft Excel 9.0 Object Library'
* puts tlib3.name # -> 'Microsoft Excel 9.0 Object Library'
@@ -428,11 +428,11 @@ foletypelib_initialize(VALUE self, VALUE args)
/*
* call-seq:
- * WIN32OLE_TYPELIB#guid -> The guid string.
+ * guid -> The guid string.
*
* Returns guid string which specifies type library.
*
- * tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library')
+ * tlib = WIN32OLE::TypeLib.new('Microsoft Excel 9.0 Object Library')
* guid = tlib.guid # -> '{00020813-0000-0000-C000-000000000046}'
*/
static VALUE
@@ -456,11 +456,11 @@ foletypelib_guid(VALUE self)
/*
* call-seq:
- * WIN32OLE_TYPELIB#name -> The type library name
+ * name -> The type library name
*
* Returns the type library name.
*
- * tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library')
+ * tlib = WIN32OLE::TypeLib.new('Microsoft Excel 9.0 Object Library')
* name = tlib.name # -> 'Microsoft Excel 9.0 Object Library'
*/
static VALUE
@@ -500,11 +500,11 @@ make_version_str(VALUE major, VALUE minor)
/*
* call-seq:
- * WIN32OLE_TYPELIB#version -> The type library version String object.
+ * version -> The type library version String object.
*
* Returns the type library version.
*
- * tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library')
+ * tlib = WIN32OLE::TypeLib.new('Microsoft Excel 9.0 Object Library')
* puts tlib.version #-> "1.3"
*/
static VALUE
@@ -523,11 +523,11 @@ foletypelib_version(VALUE self)
/*
* call-seq:
- * WIN32OLE_TYPELIB#major_version -> The type library major version.
+ * major_version -> The type library major version.
*
* Returns the type library major version.
*
- * tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library')
+ * tlib = WIN32OLE::TypeLib.new('Microsoft Excel 9.0 Object Library')
* puts tlib.major_version # -> 1
*/
static VALUE
@@ -546,11 +546,11 @@ foletypelib_major_version(VALUE self)
/*
* call-seq:
- * WIN32OLE_TYPELIB#minor_version -> The type library minor version.
+ * minor_version -> The type library minor version.
*
* Returns the type library minor version.
*
- * tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library')
+ * tlib = WIN32OLE::TypeLib.new('Microsoft Excel 9.0 Object Library')
* puts tlib.minor_version # -> 3
*/
static VALUE
@@ -568,11 +568,11 @@ foletypelib_minor_version(VALUE self)
/*
* call-seq:
- * WIN32OLE_TYPELIB#path -> The type library file path.
+ * path -> The type library file path.
*
* Returns the type library file path.
*
- * tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library')
+ * tlib = WIN32OLE::TypeLib.new('Microsoft Excel 9.0 Object Library')
* puts tlib.path #-> 'C:\...\EXCEL9.OLB'
*/
static VALUE
@@ -604,15 +604,15 @@ foletypelib_path(VALUE self)
/*
* call-seq:
- * WIN32OLE_TYPELIB#visible?
+ * visible?
*
* Returns true if the type library information is not hidden.
* If wLibFlags of TLIBATTR is 0 or LIBFLAG_FRESTRICTED or LIBFLAG_FHIDDEN,
* the method returns false, otherwise, returns true.
* If the method fails to access the TLIBATTR information, then
- * WIN32OLERuntimeError is raised.
+ * WIN32OLE::RuntimeError is raised.
*
- * tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library')
+ * tlib = WIN32OLE::TypeLib.new('Microsoft Excel 9.0 Object Library')
* tlib.visible? # => true
*/
static VALUE
@@ -636,12 +636,12 @@ foletypelib_visible(VALUE self)
/*
* call-seq:
- * WIN32OLE_TYPELIB#library_name
+ * library_name
*
* Returns library name.
- * If the method fails to access library name, WIN32OLERuntimeError is raised.
+ * If the method fails to access library name, WIN32OLE::RuntimeError is raised.
*
- * tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library')
+ * tlib = WIN32OLE::TypeLib.new('Microsoft Excel 9.0 Object Library')
* tlib.library_name # => Excel
*/
static VALUE
@@ -790,11 +790,11 @@ typelib_file(VALUE ole)
/*
* call-seq:
- * WIN32OLE_TYPELIB#ole_types -> The array of WIN32OLE_TYPE object included the type library.
+ * ole_types -> The array of WIN32OLE::Type object included the type library.
*
* Returns the type library file path.
*
- * tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library')
+ * tlib = WIN32OLE::TypeLib.new('Microsoft Excel 9.0 Object Library')
* classes = tlib.ole_types.collect{|k| k.name} # -> ['AddIn', 'AddIns' ...]
*/
static VALUE
@@ -809,17 +809,17 @@ foletypelib_ole_types(VALUE self)
/*
* call-seq:
- * WIN32OLE_TYPELIB#inspect -> String
+ * inspect -> String
*
* Returns the type library name with class name.
*
- * tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library')
- * tlib.inspect # => "<#WIN32OLE_TYPELIB:Microsoft Excel 9.0 Object Library>"
+ * tlib = WIN32OLE::TypeLib.new('Microsoft Excel 9.0 Object Library')
+ * tlib.inspect # => "<#WIN32OLE::TypeLib:Microsoft Excel 9.0 Object Library>"
*/
static VALUE
foletypelib_inspect(VALUE self)
{
- return default_inspect(self, "WIN32OLE_TYPELIB");
+ return default_inspect(self, "WIN32OLE::TypeLib");
}
VALUE cWIN32OLE_TYPELIB;
@@ -827,7 +827,8 @@ VALUE cWIN32OLE_TYPELIB;
void
Init_win32ole_typelib(void)
{
- cWIN32OLE_TYPELIB = rb_define_class_under(cWIN32OLE, "Typelib", rb_cObject);
+ cWIN32OLE_TYPELIB = rb_define_class_under(cWIN32OLE, "TypeLib", rb_cObject);
+ /* Alias of WIN32OLE::TypeLib, for the backward compatibility */
rb_define_const(rb_cObject, "WIN32OLE_TYPELIB", cWIN32OLE_TYPELIB);
rb_define_singleton_method(cWIN32OLE_TYPELIB, "typelibs", foletypelib_s_typelibs, 0);
rb_define_alloc_func(cWIN32OLE_TYPELIB, foletypelib_s_allocate);
diff --git a/ext/win32ole/win32ole_variable.c b/ext/win32ole/win32ole_variable.c
index e7f58c891e..34435301d4 100644
--- a/ext/win32ole/win32ole_variable.c
+++ b/ext/win32ole/win32ole_variable.c
@@ -43,9 +43,9 @@ olevariable_size(const void *ptr)
}
/*
- * Document-class: WIN32OLE_VARIABLE
+ * Document-class: WIN32OLE::Variable
*
- * <code>WIN32OLE_VARIABLE</code> objects represent OLE variable information.
+ * +WIN32OLE::Variable+ objects represent OLE variable information.
*/
VALUE
@@ -63,11 +63,11 @@ create_win32ole_variable(ITypeInfo *pTypeInfo, UINT index, VALUE name)
/*
* call-seq:
- * WIN32OLE_VARIABLE#name
+ * name
*
* Returns the name of variable.
*
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'XlSheetType')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'XlSheetType')
* variables = tobj.variables
* variables.each do |variable|
* puts "#{variable.name}"
@@ -103,11 +103,11 @@ ole_variable_ole_type(ITypeInfo *pTypeInfo, UINT var_index)
/*
* call-seq:
- * WIN32OLE_VARIABLE#ole_type
+ * ole_type
*
* Returns OLE type string.
*
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'XlSheetType')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'XlSheetType')
* variables = tobj.variables
* variables.each do |variable|
* puts "#{variable.ole_type} #{variable.name}"
@@ -145,11 +145,11 @@ ole_variable_ole_type_detail(ITypeInfo *pTypeInfo, UINT var_index)
/*
* call-seq:
- * WIN32OLE_VARIABLE#ole_type_detail
+ * ole_type_detail
*
* Returns detail information of type. The information is array of type.
*
- * tobj = WIN32OLE_TYPE.new('DirectX 7 for Visual Basic Type Library', 'D3DCLIPSTATUS')
+ * tobj = WIN32OLE::Type.new('DirectX 7 for Visual Basic Type Library', 'D3DCLIPSTATUS')
* variable = tobj.variables.find {|variable| variable.name == 'lFlags'}
* tdetail = variable.ole_type_detail
* p tdetail # => ["USERDEFINED", "CONST_D3DCLIPSTATUSFLAGS"]
@@ -180,12 +180,12 @@ ole_variable_value(ITypeInfo *pTypeInfo, UINT var_index)
/*
* call-seq:
- * WIN32OLE_VARIABLE#value
+ * value
*
* Returns value if value is exists. If the value does not exist,
* this method returns nil.
*
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'XlSheetType')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'XlSheetType')
* variables = tobj.variables
* variables.each do |variable|
* puts "#{variable.name} #{variable.value}"
@@ -227,11 +227,11 @@ ole_variable_visible(ITypeInfo *pTypeInfo, UINT var_index)
/*
* call-seq:
- * WIN32OLE_VARIABLE#visible?
+ * visible?
*
* Returns true if the variable is public.
*
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'XlSheetType')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'XlSheetType')
* variables = tobj.variables
* variables.each do |variable|
* puts "#{variable.name} #{variable.visible?}"
@@ -284,11 +284,11 @@ ole_variable_kind(ITypeInfo *pTypeInfo, UINT var_index)
/*
* call-seq:
- * WIN32OLE_VARIABLE#variable_kind
+ * variable_kind
*
* Returns variable kind string.
*
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'XlSheetType')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'XlSheetType')
* variables = tobj.variables
* variables.each do |variable|
* puts "#{variable.name} #{variable.variable_kind}"
@@ -325,10 +325,10 @@ ole_variable_varkind(ITypeInfo *pTypeInfo, UINT var_index)
/*
* call-seq:
- * WIN32OLE_VARIABLE#varkind
+ * varkind
*
* Returns the number which represents variable kind.
- * tobj = WIN32OLE_TYPE.new('Microsoft Excel 9.0 Object Library', 'XlSheetType')
+ * tobj = WIN32OLE::Type.new('Microsoft Excel 9.0 Object Library', 'XlSheetType')
* variables = tobj.variables
* variables.each do |variable|
* puts "#{variable.name} #{variable.varkind}"
@@ -351,7 +351,7 @@ folevariable_varkind(VALUE self)
/*
* call-seq:
- * WIN32OLE_VARIABLE#inspect -> String
+ * inspect -> String
*
* Returns the OLE variable name and the value with class name.
*
@@ -362,7 +362,7 @@ folevariable_inspect(VALUE self)
VALUE v = rb_inspect(folevariable_value(self));
VALUE n = folevariable_name(self);
VALUE detail = rb_sprintf("%"PRIsVALUE"=%"PRIsVALUE, n, v);
- return make_inspect("WIN32OLE_VARIABLE", detail);
+ return make_inspect("WIN32OLE::Variable", detail);
}
VALUE cWIN32OLE_VARIABLE;
@@ -370,6 +370,7 @@ VALUE cWIN32OLE_VARIABLE;
void Init_win32ole_variable(void)
{
cWIN32OLE_VARIABLE = rb_define_class_under(cWIN32OLE, "Variable", rb_cObject);
+ /* Alias of WIN32OLE::Variable, for the backward compatibility */
rb_define_const(rb_cObject, "WIN32OLE_VARIABLE", cWIN32OLE_VARIABLE);
rb_undef_alloc_func(cWIN32OLE_VARIABLE);
rb_define_method(cWIN32OLE_VARIABLE, "name", folevariable_name, 0);
diff --git a/ext/win32ole/win32ole_variant.c b/ext/win32ole/win32ole_variant.c
index f1d83ed2e1..45c70b1dc3 100644
--- a/ext/win32ole/win32ole_variant.c
+++ b/ext/win32ole/win32ole_variant.c
@@ -267,7 +267,7 @@ folevariant_s_allocate(VALUE klass)
/*
* call-seq:
- * WIN32OLE_VARIANT.array(ary, vt)
+ * array(ary, vt)
*
* Returns Ruby object wrapping OLE variant whose variant type is VT_ARRAY.
* The first argument should be Array object which specifies dimensions
@@ -277,7 +277,7 @@ folevariant_s_allocate(VALUE klass)
* The following create 2 dimensions OLE array. The first dimensions size
* is 3, and the second is 4.
*
- * ole_ary = WIN32OLE_VARIANT.array([3,4], VT_I4)
+ * ole_ary = WIN32OLE::Variant.array([3,4], VT_I4)
* ruby_ary = ole_ary.value # => [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
*
*/
@@ -357,44 +357,44 @@ check_type_val2variant(VALUE val)
case T_NIL:
break;
default:
- rb_raise(rb_eTypeError, "can not convert WIN32OLE_VARIANT from type %s",
+ rb_raise(rb_eTypeError, "can not convert WIN32OLE::Variant from type %s",
rb_obj_classname(val));
}
}
}
/*
- * Document-class: WIN32OLE_VARIANT
+ * Document-class: WIN32OLE::Variant
*
- * <code>WIN32OLE_VARIANT</code> objects represents OLE variant.
+ * +WIN32OLE::Variant+ objects represents OLE variant.
*
* Win32OLE converts Ruby object into OLE variant automatically when
* invoking OLE methods. If OLE method requires the argument which is
* different from the variant by automatic conversion of Win32OLE, you
- * can convert the specified variant type by using WIN32OLE_VARIANT class.
+ * can convert the specified variant type by using WIN32OLE::Variant class.
*
- * param = WIN32OLE_VARIANT.new(10, WIN32OLE::VARIANT::VT_R4)
+ * param = WIN32OLE::Variant.new(10, WIN32OLE::VARIANT::VT_R4)
* oleobj.method(param)
*
- * WIN32OLE_VARIANT does not support VT_RECORD variant. Use WIN32OLE_RECORD
- * class instead of WIN32OLE_VARIANT if the VT_RECORD variant is needed.
+ * WIN32OLE::Variant does not support VT_RECORD variant. Use WIN32OLE::Record
+ * class instead of WIN32OLE::Variant if the VT_RECORD variant is needed.
*/
/*
* call-seq:
- * WIN32OLE_VARIANT.new(val, vartype) #=> WIN32OLE_VARIANT object.
+ * new(val, vartype) #=> WIN32OLE::Variant object.
*
* Returns Ruby object wrapping OLE variant.
* The first argument specifies Ruby object to convert OLE variant variable.
* The second argument specifies VARIANT type.
- * In some situation, you need the WIN32OLE_VARIANT object to pass OLE method
+ * In some situation, you need the WIN32OLE::Variant object to pass OLE method
*
* shell = WIN32OLE.new("Shell.Application")
* folder = shell.NameSpace("C:\\Windows")
* item = folder.ParseName("tmp.txt")
* # You can't use Ruby String object to call FolderItem.InvokeVerb.
- * # Instead, you have to use WIN32OLE_VARIANT object to call the method.
- * shortcut = WIN32OLE_VARIANT.new("Create Shortcut(\&S)")
+ * # Instead, you have to use WIN32OLE::Variant object to call the method.
+ * shortcut = WIN32OLE::Variant.new("Create Shortcut(\&S)")
* item.invokeVerb(shortcut)
*
*/
@@ -422,7 +422,7 @@ folevariant_initialize(VALUE self, VALUE args)
vvt = rb_ary_entry(args, 1);
vt = RB_NUM2INT(vvt);
if ((vt & VT_TYPEMASK) == VT_RECORD) {
- rb_raise(rb_eArgError, "not supported VT_RECORD WIN32OLE_VARIANT object");
+ rb_raise(rb_eArgError, "not supported VT_RECORD WIN32OLE::Variant object");
}
ole_val2olevariantdata(val, vt, pvar);
}
@@ -482,22 +482,22 @@ unlock_safe_array(SAFEARRAY *psa)
/*
* call-seq:
- * WIN32OLE_VARIANT[i,j,...] #=> element of OLE array.
+ * variant[i,j,...] #=> element of OLE array.
*
- * Returns the element of WIN32OLE_VARIANT object(OLE array).
+ * Returns the element of WIN32OLE::Variant object(OLE array).
* This method is available only when the variant type of
- * WIN32OLE_VARIANT object is VT_ARRAY.
+ * WIN32OLE::Variant object is VT_ARRAY.
*
* REMARK:
* The all indices should be 0 or natural number and
* lower than or equal to max indices.
* (This point is different with Ruby Array indices.)
*
- * obj = WIN32OLE_VARIANT.new([[1,2,3],[4,5,6]])
+ * obj = WIN32OLE::Variant.new([[1,2,3],[4,5,6]])
* p obj[0,0] # => 1
* p obj[1,0] # => 4
- * p obj[2,0] # => WIN32OLERuntimeError
- * p obj[0, -1] # => WIN32OLERuntimeError
+ * p obj[2,0] # => WIN32OLE::RuntimeError
+ * p obj[0, -1] # => WIN32OLE::RuntimeError
*
*/
static VALUE
@@ -537,23 +537,23 @@ folevariant_ary_aref(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * WIN32OLE_VARIANT[i,j,...] = val #=> set the element of OLE array
+ * variant[i,j,...] = val #=> set the element of OLE array
*
- * Set the element of WIN32OLE_VARIANT object(OLE array) to val.
+ * Set the element of WIN32OLE::Variant object(OLE array) to val.
* This method is available only when the variant type of
- * WIN32OLE_VARIANT object is VT_ARRAY.
+ * WIN32OLE::Variant object is VT_ARRAY.
*
* REMARK:
* The all indices should be 0 or natural number and
* lower than or equal to max indices.
* (This point is different with Ruby Array indices.)
*
- * obj = WIN32OLE_VARIANT.new([[1,2,3],[4,5,6]])
+ * obj = WIN32OLE::Variant.new([[1,2,3],[4,5,6]])
* obj[0,0] = 7
* obj[1,0] = 8
* p obj.value # => [[7,2,3], [8,5,6]]
- * obj[2,0] = 9 # => WIN32OLERuntimeError
- * obj[0, -1] = 9 # => WIN32OLERuntimeError
+ * obj[2,0] = 9 # => WIN32OLE::RuntimeError
+ * obj[0, -1] = 9 # => WIN32OLE::RuntimeError
*
*/
static VALUE
@@ -598,10 +598,10 @@ folevariant_ary_aset(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * WIN32OLE_VARIANT.value #=> Ruby object.
+ * value #=> Ruby object.
*
* Returns Ruby object value from OLE variant.
- * obj = WIN32OLE_VARIANT.new(1, WIN32OLE::VARIANT::VT_BSTR)
+ * obj = WIN32OLE::Variant.new(1, WIN32OLE::VARIANT::VT_BSTR)
* obj.value # => "1" (not Integer object, but String object "1")
*
*/
@@ -637,10 +637,10 @@ folevariant_value(VALUE self)
/*
* call-seq:
- * WIN32OLE_VARIANT.vartype #=> OLE variant type.
+ * vartype #=> OLE variant type.
*
* Returns OLE variant type.
- * obj = WIN32OLE_VARIANT.new("string")
+ * obj = WIN32OLE::Variant.new("string")
* obj.vartype # => WIN32OLE::VARIANT::VT_BSTR
*
*/
@@ -654,7 +654,7 @@ folevariant_vartype(VALUE self)
/*
* call-seq:
- * WIN32OLE_VARIANT.value = val #=> set WIN32OLE_VARIANT value to val.
+ * variant.value = val #=> set WIN32OLE::Variant value to val.
*
* Sets variant value to val. If the val type does not match variant value
* type(vartype), then val is changed to match variant value type(vartype)
@@ -662,7 +662,7 @@ folevariant_vartype(VALUE self)
* This method is not available when vartype is VT_ARRAY(except VT_UI1|VT_ARRAY).
* If the vartype is VT_UI1|VT_ARRAY, the val should be String object.
*
- * obj = WIN32OLE_VARIANT.new(1) # obj.vartype is WIN32OLE::VARIANT::VT_I4
+ * obj = WIN32OLE::Variant.new(1) # obj.vartype is WIN32OLE::VARIANT::VT_I4
* obj.value = 3.2 # 3.2 is changed to 3 when setting value.
* p obj.value # => 3
*/
@@ -696,6 +696,7 @@ Init_win32ole_variant(void)
{
#undef rb_intern
cWIN32OLE_VARIANT = rb_define_class_under(cWIN32OLE, "Variant", rb_cObject);
+ /* Alias of WIN32OLE::Variant, for the backward compatibility */
rb_define_const(rb_cObject, "WIN32OLE_VARIANT", cWIN32OLE_VARIANT);
rb_define_alloc_func(cWIN32OLE_VARIANT, folevariant_s_allocate);
rb_define_singleton_method(cWIN32OLE_VARIANT, "array", folevariant_s_array, 2);
@@ -729,7 +730,7 @@ Init_win32ole_variant(void)
* This constants is used for not specified parameter.
*
* fso = WIN32OLE.new("Scripting.FileSystemObject")
- * fso.openTextFile(filename, WIN32OLE_VARIANT::NoParam, false)
+ * fso.openTextFile(filename, WIN32OLE::Variant::NoParam, false)
*/
rb_define_const(cWIN32OLE_VARIANT, "NoParam",
rb_funcall(cWIN32OLE_VARIANT, rb_intern("new"), 2, INT2NUM(DISP_E_PARAMNOTFOUND), RB_INT2FIX(VT_ERROR)));
diff --git a/ext/win32ole/win32ole_variant_m.c b/ext/win32ole/win32ole_variant_m.c
index c285a00177..d879506be3 100644
--- a/ext/win32ole/win32ole_variant_m.c
+++ b/ext/win32ole/win32ole_variant_m.c
@@ -5,16 +5,18 @@ VALUE mWIN32OLE_VARIANT;
void Init_win32ole_variant_m(void)
{
/*
- * Document-module: WIN32OLE::VARIANT
+ * Document-module: WIN32OLE::VariantType
*
- * The WIN32OLE::VARIANT module includes constants of VARIANT type constants.
- * The constants is used when creating WIN32OLE_VARIANT object.
+ * The +WIN32OLE::VariantType+ module includes constants of VARIANT type constants.
+ * The constants is used when creating WIN32OLE::Variant object.
*
- * obj = WIN32OLE_VARIANT.new("2e3", WIN32OLE::VARIANT::VT_R4)
+ * obj = WIN32OLE::Variant.new("2e3", WIN32OLE::VARIANT::VT_R4)
* obj.value # => 2000.0
*
*/
- mWIN32OLE_VARIANT = rb_define_module_under(cWIN32OLE, "VARIANT");
+ mWIN32OLE_VARIANT = rb_define_module_under(cWIN32OLE, "VariantType");
+ /* Alias of WIN32OLE::VariantType, for the backward compatibility */
+ rb_define_const(cWIN32OLE, "VARIANT", mWIN32OLE_VARIANT);
/*
* represents VT_EMPTY type constant.
diff --git a/ext/zlib/depend b/ext/zlib/depend
index bdcf6a93e8..bdce420264 100644
--- a/ext/zlib/depend
+++ b/ext/zlib/depend
@@ -157,6 +157,7 @@ zlib.o: $(hdrdir)/ruby/internal/special_consts.h
zlib.o: $(hdrdir)/ruby/internal/static_assert.h
zlib.o: $(hdrdir)/ruby/internal/stdalign.h
zlib.o: $(hdrdir)/ruby/internal/stdbool.h
+zlib.o: $(hdrdir)/ruby/internal/stdckdint.h
zlib.o: $(hdrdir)/ruby/internal/symbol.h
zlib.o: $(hdrdir)/ruby/internal/value.h
zlib.o: $(hdrdir)/ruby/internal/value_type.h
diff --git a/ext/zlib/extconf.rb b/ext/zlib/extconf.rb
index 1254c62a17..2b2dbb1a5b 100644
--- a/ext/zlib/extconf.rb
+++ b/ext/zlib/extconf.rb
@@ -120,12 +120,18 @@ if have_zlib
$defs << "-DHAVE_CRC32_COMBINE"
$defs << "-DHAVE_ADLER32_COMBINE"
$defs << "-DHAVE_TYPE_Z_CRC_T"
- $defs << "-DHAVE_TYPE_Z_SIZE_T"
+ $defs << "-DHAVE_CRC32_Z"
+ $defs << "-DHAVE_ADLER32_Z"
+ $defs << "-DHAVE_ZLIB_SIZE_T_FUNCS"
else
have_func('crc32_combine', 'zlib.h')
have_func('adler32_combine', 'zlib.h')
have_type('z_crc_t', 'zlib.h')
- have_type('z_size_t', 'zlib.h')
+ if (have_type('z_size_t', 'zlib.h') &&
+ have_func('crc32_z', 'zlib.h') &&
+ have_func('adler32_z', 'zlib.h'))
+ $defs << "-DHAVE_ZLIB_SIZE_T_FUNCS"
+ end
end
create_makefile('zlib') {|conf|
diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c
index aefdba0ebd..fe03072576 100644
--- a/ext/zlib/zlib.c
+++ b/ext/zlib/zlib.c
@@ -25,7 +25,7 @@
# define VALGRIND_MAKE_MEM_UNDEFINED(p, n) 0
#endif
-#define RUBY_ZLIB_VERSION "3.0.0"
+#define RUBY_ZLIB_VERSION "3.1.0"
#ifndef RB_PASS_CALLED_KEYWORDS
# define rb_class_new_instance_kw(argc, argv, klass, kw_splat) rb_class_new_instance(argc, argv, klass)
@@ -44,7 +44,7 @@
#endif
#endif
-#if defined(HAVE_TYPE_Z_SIZE_T)
+#if defined(HAVE_ZLIB_SIZE_T_FUNCS)
typedef uLong (*checksum_func)(uLong, const Bytef*, z_size_t);
# define crc32 crc32_z
# define adler32 adler32_z
@@ -388,7 +388,7 @@ rb_zlib_version(VALUE klass)
# define mask32(x) (x)
#endif
-#if SIZEOF_LONG > SIZEOF_INT && !defined(HAVE_TYPE_Z_SIZE_T)
+#if SIZEOF_LONG > SIZEOF_INT && !defined(HAVE_ZLIB_SIZE_T_FUNCS)
static uLong
checksum_long(uLong (*func)(uLong, const Bytef*, uInt), uLong sum, const Bytef *ptr, long len)
{
@@ -923,7 +923,7 @@ zstream_discard_input(struct zstream *z, long len)
z->input = Qnil;
}
else {
- z->input = rb_str_substr(z->input, len,
+ z->input = rb_str_subseq(z->input, len,
RSTRING_LEN(z->input) - len);
}
}
@@ -3500,6 +3500,9 @@ static VALUE
rb_gzfile_eof_p(VALUE obj)
{
struct gzfile *gz = get_gzfile(obj);
+ while (!ZSTREAM_IS_FINISHED(&gz->z) && ZSTREAM_BUF_FILLED(&gz->z) == 0) {
+ gzfile_read_more(gz, Qnil);
+ }
return GZFILE_IS_FINISHED(gz) ? Qtrue : Qfalse;
}
diff --git a/ext/zlib/zlib.gemspec b/ext/zlib/zlib.gemspec
index 4a5f8f2ee8..bb67ea156c 100644
--- a/ext/zlib/zlib.gemspec
+++ b/ext/zlib/zlib.gemspec
@@ -27,5 +27,5 @@ Gem::Specification.new do |spec|
spec.executables = []
spec.require_paths = ["lib"]
spec.extensions = "ext/zlib/extconf.rb"
- spec.required_ruby_version = ">= 2.3.0"
+ spec.required_ruby_version = ">= 2.5.0"
end
diff --git a/file.c b/file.c
index 5f59373a45..835f19e541 100644
--- a/file.c
+++ b/file.c
@@ -468,7 +468,7 @@ apply2files(int (*func)(const char *, void *), int argc, VALUE *argv, void *arg)
aa->fn[aa->i].path = path;
}
- rb_thread_call_without_gvl(no_gvl_apply2files, aa, RUBY_UBF_IO, 0);
+ IO_WITHOUT_GVL(no_gvl_apply2files, aa);
if (aa->errnum) {
#ifdef UTIME_EINVAL
if (func == utime_internal) {
@@ -483,28 +483,29 @@ apply2files(int (*func)(const char *, void *), int argc, VALUE *argv, void *arg)
return LONG2FIX(argc);
}
-static size_t
-stat_memsize(const void *p)
-{
- return sizeof(struct stat);
-}
-
static const rb_data_type_t stat_data_type = {
"stat",
- {NULL, RUBY_TYPED_DEFAULT_FREE, stat_memsize,},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
+ {
+ NULL,
+ RUBY_TYPED_DEFAULT_FREE,
+ NULL, // No external memory to report
+ },
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
+};
+
+struct rb_stat {
+ struct stat stat;
+ bool initialized;
};
static VALUE
stat_new_0(VALUE klass, const struct stat *st)
{
- struct stat *nst = 0;
- VALUE obj = TypedData_Wrap_Struct(klass, &stat_data_type, 0);
-
+ struct rb_stat *rb_st;
+ VALUE obj = TypedData_Make_Struct(klass, struct rb_stat, &stat_data_type, rb_st);
if (st) {
- nst = ALLOC(struct stat);
- *nst = *st;
- RTYPEDDATA_DATA(obj) = nst;
+ rb_st->stat = *st;
+ rb_st->initialized = true;
}
return obj;
}
@@ -518,10 +519,10 @@ rb_stat_new(const struct stat *st)
static struct stat*
get_stat(VALUE self)
{
- struct stat* st;
- TypedData_Get_Struct(self, struct stat, &stat_data_type, st);
- if (!st) rb_raise(rb_eTypeError, "uninitialized File::Stat");
- return st;
+ struct rb_stat* rb_st;
+ TypedData_Get_Struct(self, struct rb_stat, &stat_data_type, rb_st);
+ if (!rb_st->initialized) rb_raise(rb_eTypeError, "uninitialized File::Stat");
+ return &rb_st->stat;
}
static struct timespec stat_mtimespec(const struct stat *st);
@@ -1092,9 +1093,9 @@ rb_stat_inspect(VALUE self)
#endif
};
- struct stat* st;
- TypedData_Get_Struct(self, struct stat, &stat_data_type, st);
- if (!st) {
+ struct rb_stat* rb_st;
+ TypedData_Get_Struct(self, struct rb_stat, &stat_data_type, rb_st);
+ if (!rb_st->initialized) {
return rb_sprintf("#<%s: uninitialized>", rb_obj_classname(self));
}
@@ -1167,8 +1168,7 @@ stat_without_gvl(const char *path, struct stat *st)
data.file.path = path;
data.st = st;
- return (int)(VALUE)rb_thread_call_without_gvl(no_gvl_stat, &data,
- RUBY_UBF_IO, NULL);
+ return IO_WITHOUT_GVL_INT(no_gvl_stat, &data);
}
#if !defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC) && \
@@ -1218,8 +1218,7 @@ statx_without_gvl(const char *path, struct statx *stx, unsigned int mask)
no_gvl_statx_data data = {stx, AT_FDCWD, path, 0, mask};
/* call statx(2) with pathname */
- return (int)(VALUE)rb_thread_call_without_gvl(no_gvl_statx, &data,
- RUBY_UBF_IO, NULL);
+ return IO_WITHOUT_GVL_INT(no_gvl_statx, &data);
}
static int
@@ -1381,8 +1380,7 @@ lstat_without_gvl(const char *path, struct stat *st)
data.file.path = path;
data.st = st;
- return (int)(VALUE)rb_thread_call_without_gvl(no_gvl_lstat, &data,
- RUBY_UBF_IO, NULL);
+ return IO_WITHOUT_GVL_INT(no_gvl_lstat, &data);
}
#endif /* HAVE_LSTAT */
@@ -1556,8 +1554,7 @@ rb_eaccess(VALUE fname, int mode)
aa.path = StringValueCStr(fname);
aa.mode = mode;
- return (int)(VALUE)rb_thread_call_without_gvl(nogvl_eaccess, &aa,
- RUBY_UBF_IO, 0);
+ return IO_WITHOUT_GVL_INT(nogvl_eaccess, &aa);
}
static void *
@@ -1578,8 +1575,7 @@ rb_access(VALUE fname, int mode)
aa.path = StringValueCStr(fname);
aa.mode = mode;
- return (int)(VALUE)rb_thread_call_without_gvl(nogvl_access, &aa,
- RUBY_UBF_IO, 0);
+ return IO_WITHOUT_GVL_INT(nogvl_access, &aa);
}
/*
@@ -1593,8 +1589,6 @@ rb_access(VALUE fname, int mode)
*/
/*
- * Document-method: directory?
- *
* call-seq:
* File.directory?(path) -> true or false
*
@@ -1773,7 +1767,7 @@ rb_file_blockdev_p(VALUE obj, VALUE fname)
*
* Returns +true+ if +filepath+ points to a character device, +false+ otherwise.
*
- * File.chardev?($stdin) # => true
+ * File.chardev?($stdin) # => true
* File.chardev?('t.txt') # => false
*
*/
@@ -2454,6 +2448,7 @@ rb_file_ctime(VALUE obj)
return stat_ctime(&st);
}
+#if defined(HAVE_STAT_BIRTHTIME)
/*
* call-seq:
* File.birthtime(file_name) -> time
@@ -2468,8 +2463,7 @@ rb_file_ctime(VALUE obj)
*
*/
-#if defined(HAVE_STAT_BIRTHTIME)
-RUBY_FUNC_EXPORTED VALUE
+VALUE
rb_file_s_birthtime(VALUE klass, VALUE fname)
{
statx_data st;
@@ -2514,16 +2508,6 @@ rb_file_birthtime(VALUE obj)
# define rb_file_birthtime rb_f_notimplement
#endif
-/*
- * call-seq:
- * file.size -> integer
- *
- * Returns the size of <i>file</i> in bytes.
- *
- * File.new("testfile").size #=> 66
- *
- */
-
rb_off_t
rb_file_size(VALUE file)
{
@@ -2547,12 +2531,45 @@ rb_file_size(VALUE file)
}
}
+/*
+ * call-seq:
+ * file.size -> integer
+ *
+ * Returns the size of <i>file</i> in bytes.
+ *
+ * File.new("testfile").size #=> 66
+ *
+ */
+
static VALUE
file_size(VALUE self)
{
return OFFT2NUM(rb_file_size(self));
}
+struct nogvl_chmod_data {
+ const char *path;
+ mode_t mode;
+};
+
+static void *
+nogvl_chmod(void *ptr)
+{
+ struct nogvl_chmod_data *data = ptr;
+ int ret = chmod(data->path, data->mode);
+ return (void *)(VALUE)ret;
+}
+
+static int
+rb_chmod(const char *path, mode_t mode)
+{
+ struct nogvl_chmod_data data = {
+ .path = path,
+ .mode = mode,
+ };
+ return IO_WITHOUT_GVL_INT(nogvl_chmod, &data);
+}
+
static int
chmod_internal(const char *path, void *mode)
{
@@ -2583,6 +2600,29 @@ rb_file_s_chmod(int argc, VALUE *argv, VALUE _)
return apply2files(chmod_internal, argc, argv, &mode);
}
+#ifdef HAVE_FCHMOD
+struct nogvl_fchmod_data {
+ int fd;
+ mode_t mode;
+};
+
+static VALUE
+io_blocking_fchmod(void *ptr)
+{
+ struct nogvl_fchmod_data *data = ptr;
+ int ret = fchmod(data->fd, data->mode);
+ return (VALUE)ret;
+}
+
+static int
+rb_fchmod(int fd, mode_t mode)
+{
+ (void)rb_chmod; /* suppress unused-function warning when HAVE_FCHMOD */
+ struct nogvl_fchmod_data data = {.fd = fd, .mode = mode};
+ return (int)rb_thread_io_blocking_region(io_blocking_fchmod, &data, fd);
+}
+#endif
+
/*
* call-seq:
* file.chmod(mode_int) -> 0
@@ -2609,7 +2649,7 @@ rb_file_chmod(VALUE obj, VALUE vmode)
GetOpenFile(obj, fptr);
#ifdef HAVE_FCHMOD
- if (fchmod(fptr->fd, mode) == -1) {
+ if (rb_fchmod(fptr->fd, mode) == -1) {
if (HAVE_FCHMOD || errno != ENOSYS)
rb_sys_fail_path(fptr->pathv);
}
@@ -2620,7 +2660,7 @@ rb_file_chmod(VALUE obj, VALUE vmode)
#if !defined HAVE_FCHMOD || !HAVE_FCHMOD
if (NIL_P(fptr->pathv)) return Qnil;
path = rb_str_encode_ospath(fptr->pathv);
- if (chmod(RSTRING_PTR(path), mode) == -1)
+ if (rb_chmod(RSTRING_PTR(path), mode) == -1)
rb_sys_fail_path(fptr->pathv);
#endif
@@ -2715,6 +2755,51 @@ rb_file_s_chown(int argc, VALUE *argv, VALUE _)
return apply2files(chown_internal, argc, argv, &arg);
}
+struct nogvl_chown_data {
+ union {
+ const char *path;
+ int fd;
+ } as;
+ struct chown_args new;
+};
+
+static void *
+nogvl_chown(void *ptr)
+{
+ struct nogvl_chown_data *data = ptr;
+ return (void *)(VALUE)chown(data->as.path, data->new.owner, data->new.group);
+}
+
+static int
+rb_chown(const char *path, rb_uid_t owner, rb_gid_t group)
+{
+ struct nogvl_chown_data data = {
+ .as = {.path = path},
+ .new = {.owner = owner, .group = group},
+ };
+ return IO_WITHOUT_GVL_INT(nogvl_chown, &data);
+}
+
+#ifdef HAVE_FCHOWN
+static void *
+nogvl_fchown(void *ptr)
+{
+ struct nogvl_chown_data *data = ptr;
+ return (void *)(VALUE)fchown(data->as.fd, data->new.owner, data->new.group);
+}
+
+static int
+rb_fchown(int fd, rb_uid_t owner, rb_gid_t group)
+{
+ (void)rb_chown; /* suppress unused-function warning when HAVE_FCHMOD */
+ struct nogvl_chown_data data = {
+ .as = {.fd = fd},
+ .new = {.owner = owner, .group = group},
+ };
+ return IO_WITHOUT_GVL_INT(nogvl_fchown, &data);
+}
+#endif
+
/*
* call-seq:
* file.chown(owner_int, group_int ) -> 0
@@ -2746,10 +2831,10 @@ rb_file_chown(VALUE obj, VALUE owner, VALUE group)
#ifndef HAVE_FCHOWN
if (NIL_P(fptr->pathv)) return Qnil;
path = rb_str_encode_ospath(fptr->pathv);
- if (chown(RSTRING_PTR(path), o, g) == -1)
+ if (rb_chown(RSTRING_PTR(path), o, g) == -1)
rb_sys_fail_path(fptr->pathv);
#else
- if (fchown(fptr->fd, o, g) == -1)
+ if (rb_fchown(fptr->fd, o, g) == -1)
rb_sys_fail_path(fptr->pathv);
#endif
@@ -3141,8 +3226,7 @@ readlink_without_gvl(VALUE path, VALUE buf, size_t size)
ra.buf = RSTRING_PTR(buf);
ra.size = size;
- return (ssize_t)rb_thread_call_without_gvl(nogvl_readlink, &ra,
- RUBY_UBF_IO, 0);
+ return (ssize_t)IO_WITHOUT_GVL(nogvl_readlink, &ra);
}
VALUE
@@ -3243,8 +3327,7 @@ rb_file_s_rename(VALUE klass, VALUE from, VALUE to)
#if defined __CYGWIN__
errno = 0;
#endif
- if ((int)(VALUE)rb_thread_call_without_gvl(no_gvl_rename, &ra,
- RUBY_UBF_IO, 0) < 0) {
+ if (IO_WITHOUT_GVL_INT(no_gvl_rename, &ra) < 0) {
int e = errno;
#if defined DOSISH
switch (e) {
@@ -3674,7 +3757,7 @@ rb_default_home_dir(VALUE result)
* lookup by getuid() has a chance of succeeding.
*/
if (NIL_P(login_name)) {
- rb_raise(rb_eArgError, "couldn't find login name -- expanding `~'");
+ rb_raise(rb_eArgError, "couldn't find login name -- expanding '~'");
}
# endif /* !defined(HAVE_GETPWUID_R) && !defined(HAVE_GETPWUID) */
@@ -3682,7 +3765,7 @@ rb_default_home_dir(VALUE result)
if (NIL_P(pw_dir)) {
pw_dir = rb_getpwdiruid();
if (NIL_P(pw_dir)) {
- rb_raise(rb_eArgError, "couldn't find home for uid `%ld'", (long)getuid());
+ rb_raise(rb_eArgError, "couldn't find home for uid '%ld'", (long)getuid());
}
}
@@ -3693,7 +3776,7 @@ rb_default_home_dir(VALUE result)
}
#endif /* defined HAVE_PWD_H */
if (!dir) {
- rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'");
+ rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding '~'");
}
return copy_home_path(result, dir);
}
@@ -3718,7 +3801,15 @@ append_fspath(VALUE result, VALUE fname, char *dir, rb_encoding **enc, rb_encodi
size_t dirlen = strlen(dir), buflen = rb_str_capacity(result);
if (NORMALIZE_UTF8PATH || *enc != fsenc) {
- rb_encoding *direnc = fs_enc_check(fname, dirname = ospath_new(dir, dirlen, fsenc));
+ dirname = ospath_new(dir, dirlen, fsenc);
+ if (!rb_enc_compatible(fname, dirname)) {
+ xfree(dir);
+ /* rb_enc_check must raise because the two encodings are not
+ * compatible. */
+ rb_enc_check(fname, dirname);
+ rb_bug("unreachable");
+ }
+ rb_encoding *direnc = fs_enc_check(fname, dirname);
if (direnc != fsenc) {
dirname = rb_str_conv_enc(dirname, fsenc, direnc);
RSTRING_GETMEM(dirname, cwdp, dirlen);
@@ -4610,7 +4701,7 @@ rmext(const char *p, long l0, long l1, const char *e, long l2, rb_encoding *enc)
if (l1 < l2) return l1;
s = p+l1-l2;
- if (rb_enc_left_char_head(p, s, p+l1, enc) != s) return 0;
+ if (!at_char_boundary(p, s, p+l1, enc)) return 0;
#if CASEFOLD_FILESYSTEM
#define fncomp strncasecmp
#else
@@ -4971,7 +5062,7 @@ rb_file_s_extname(VALUE klass, VALUE fname)
*
* Returns the string representation of the path
*
- * File.path("/dev/null") #=> "/dev/null"
+ * File.path(File::NULL) #=> "/dev/null"
* File.path(Pathname.new("/tmp")) #=> "/tmp"
*
*/
@@ -5129,8 +5220,7 @@ rb_file_s_truncate(VALUE klass, VALUE path, VALUE len)
path = rb_str_encode_ospath(path);
ta.path = StringValueCStr(path);
- r = (int)(VALUE)rb_thread_call_without_gvl(nogvl_truncate, &ta,
- RUBY_UBF_IO, NULL);
+ r = IO_WITHOUT_GVL_INT(nogvl_truncate, &ta);
if (r < 0)
rb_sys_fail_path(path);
return INT2FIX(0);
@@ -5223,47 +5313,50 @@ rb_thread_flock(void *data)
return (VALUE)ret;
}
-/*
+/* :markup: markdown
+ *
* call-seq:
- * file.flock(locking_constant) -> 0 or false
- *
- * Locks or unlocks a file according to <i>locking_constant</i> (a
- * logical <em>or</em> of the values in the table below).
- * Returns <code>false</code> if File::LOCK_NB is specified and the
- * operation would otherwise have blocked. Not available on all
- * platforms.
- *
- * Locking constants (in class File):
- *
- * LOCK_EX | Exclusive lock. Only one process may hold an
- * | exclusive lock for a given file at a time.
- * ----------+------------------------------------------------
- * LOCK_NB | Don't block when locking. May be combined
- * | with other lock options using logical or.
- * ----------+------------------------------------------------
- * LOCK_SH | Shared lock. Multiple processes may each hold a
- * | shared lock for a given file at the same time.
- * ----------+------------------------------------------------
- * LOCK_UN | Unlock.
+ * flock(locking_constant) -> 0 or false
+ *
+ * Locks or unlocks file +self+ according to the given `locking_constant`,
+ * a bitwise OR of the values in the table below.
+ *
+ * Not available on all platforms.
+ *
+ * Returns `false` if `File::LOCK_NB` is specified and the operation would have blocked;
+ * otherwise returns `0`.
+ *
+ * <br>
+ *
+ * | Constant | Lock | Effect
+ * |-----------------|--------------|-------------------------------------------------------------------
+ * | +File::LOCK_EX+ | Exclusive | Only one process may hold an exclusive lock for +self+ at a time.
+ * | +File::LOCK_NB+ | Non-blocking | No blocking; may be combined with +File::LOCK_SH+ or +File::LOCK_EX+ using the bitwise OR operator <tt>\|</tt>.
+ * | +File::LOCK_SH+ | Shared | Multiple processes may each hold a shared lock for +self+ at the same time.
+ * | +File::LOCK_UN+ | Unlock | Remove an existing lock held by this process.
+ *
+ * <br>
*
* Example:
*
- * # update a counter using write lock
- * # don't use "w" because it truncates the file before lock.
- * File.open("counter", File::RDWR|File::CREAT, 0644) {|f|
- * f.flock(File::LOCK_EX)
- * value = f.read.to_i + 1
- * f.rewind
- * f.write("#{value}\n")
- * f.flush
- * f.truncate(f.pos)
- * }
- *
- * # read the counter using read lock
- * File.open("counter", "r") {|f|
- * f.flock(File::LOCK_SH)
- * p f.read
- * }
+ * ```ruby
+ * # Update a counter using an exclusive lock.
+ * # Don't use File::WRONLY because it truncates the file.
+ * File.open('counter', File::RDWR | File::CREAT, 0644) do |f|
+ * f.flock(File::LOCK_EX)
+ * value = f.read.to_i + 1
+ * f.rewind
+ * f.write("#{value}\n")
+ * f.flush
+ * f.truncate(f.pos)
+ * end
+ *
+ * # Read the counter using a shared lock.
+ * File.open('counter', 'r') do |f|
+ * f.flock(File::LOCK_SH)
+ * f.read
+ * end
+ * ```
*
*/
@@ -5327,60 +5420,83 @@ test_check(int n, int argc, VALUE *argv)
#define CHECK(n) test_check((n), argc, argv)
/*
+ * :markup: markdown
+ *
* call-seq:
- * test(cmd, file1 [, file2] ) -> obj
- *
- * Uses the character +cmd+ to perform various tests on +file1+ (first
- * table below) or on +file1+ and +file2+ (second table).
- *
- * File tests on a single file:
- *
- * Cmd Returns Meaning
- * "A" | Time | Last access time for file1
- * "b" | boolean | True if file1 is a block device
- * "c" | boolean | True if file1 is a character device
- * "C" | Time | Last change time for file1
- * "d" | boolean | True if file1 exists and is a directory
- * "e" | boolean | True if file1 exists
- * "f" | boolean | True if file1 exists and is a regular file
- * "g" | boolean | True if file1 has the setgid bit set
- * "G" | boolean | True if file1 exists and has a group
- * | | ownership equal to the caller's group
- * "k" | boolean | True if file1 exists and has the sticky bit set
- * "l" | boolean | True if file1 exists and is a symbolic link
- * "M" | Time | Last modification time for file1
- * "o" | boolean | True if file1 exists and is owned by
- * | | the caller's effective uid
- * "O" | boolean | True if file1 exists and is owned by
- * | | the caller's real uid
- * "p" | boolean | True if file1 exists and is a fifo
- * "r" | boolean | True if file1 is readable by the effective
- * | | uid/gid of the caller
- * "R" | boolean | True if file is readable by the real
- * | | uid/gid of the caller
- * "s" | int/nil | If file1 has nonzero size, return the size,
- * | | otherwise return nil
- * "S" | boolean | True if file1 exists and is a socket
- * "u" | boolean | True if file1 has the setuid bit set
- * "w" | boolean | True if file1 exists and is writable by
- * | | the effective uid/gid
- * "W" | boolean | True if file1 exists and is writable by
- * | | the real uid/gid
- * "x" | boolean | True if file1 exists and is executable by
- * | | the effective uid/gid
- * "X" | boolean | True if file1 exists and is executable by
- * | | the real uid/gid
- * "z" | boolean | True if file1 exists and has a zero length
- *
- * Tests that take two files:
- *
- * "-" | boolean | True if file1 and file2 are identical
- * "=" | boolean | True if the modification times of file1
- * | | and file2 are equal
- * "<" | boolean | True if the modification time of file1
- * | | is prior to that of file2
- * ">" | boolean | True if the modification time of file1
- * | | is after that of file2
+ * test(char, path0, path1 = nil) -> object
+ *
+ * Performs a test on one or both of the <i>filesystem entities</i> at the given paths
+ * `path0` and `path1`:
+ *
+ * - Each path `path0` or `path1` points to a file, directory, device, pipe, etc.
+ * - Character `char` selects a specific test.
+ *
+ * The tests:
+ *
+ * - Each of these tests operates only on the entity at `path0`,
+ * and returns `true` or `false`;
+ * for a non-existent entity, returns `false` (does not raise exception):
+ *
+ * | Character | Test |
+ * |:------------:|:--------------------------------------------------------------------------|
+ * | <tt>'b'</tt> | Whether the entity is a block device. |
+ * | <tt>'c'</tt> | Whether the entity is a character device. |
+ * | <tt>'d'</tt> | Whether the entity is a directory. |
+ * | <tt>'e'</tt> | Whether the entity is an existing entity. |
+ * | <tt>'f'</tt> | Whether the entity is an existing regular file. |
+ * | <tt>'g'</tt> | Whether the entity's setgid bit is set. |
+ * | <tt>'G'</tt> | Whether the entity's group ownership is equal to the caller's. |
+ * | <tt>'k'</tt> | Whether the entity's sticky bit is set. |
+ * | <tt>'l'</tt> | Whether the entity is a symbolic link. |
+ * | <tt>'o'</tt> | Whether the entity is owned by the caller's effective uid. |
+ * | <tt>'O'</tt> | Like <tt>'o'</tt>, but uses the real uid (not the effective uid). |
+ * | <tt>'p'</tt> | Whether the entity is a FIFO device (named pipe). |
+ * | <tt>'r'</tt> | Whether the entity is readable by the caller's effecive uid/gid. |
+ * | <tt>'R'</tt> | Like <tt>'r'</tt>, but uses the real uid/gid (not the effective uid/gid). |
+ * | <tt>'S'</tt> | Whether the entity is a socket. |
+ * | <tt>'u'</tt> | Whether the entity's setuid bit is set. |
+ * | <tt>'w'</tt> | Whether the entity is writable by the caller's effective uid/gid. |
+ * | <tt>'W'</tt> | Like <tt>'w'</tt>, but uses the real uid/gid (not the effective uid/gid). |
+ * | <tt>'x'</tt> | Whether the entity is executable by the caller's effective uid/gid. |
+ * | <tt>'X'</tt> | Like <tt>'x'</tt>, but uses the real uid/gid (not the effecive uid/git). |
+ * | <tt>'z'</tt> | Whether the entity exists and is of length zero. |
+ *
+ * - This test operates only on the entity at `path0`,
+ * and returns an integer size or +nil+:
+ *
+ * | Character | Test |
+ * |:------------:|:---------------------------------------------------------------------------------------------|
+ * | <tt>'s'</tt> | Returns positive integer size if the entity exists and has non-zero length, +nil+ otherwise. |
+ *
+ * - Each of these tests operates only on the entity at `path0`,
+ * and returns a Time object;
+ * raises an exception if the entity does not exist:
+ *
+ * | Character | Test |
+ * |:------------:|:---------------------------------------|
+ * | <tt>'A'</tt> | Last access time for the entity. |
+ * | <tt>'C'</tt> | Last change time for the entity. |
+ * | <tt>'M'</tt> | Last modification time for the entity. |
+ *
+ * - Each of these tests operates on the modification time (`mtime`)
+ * of each of the entities at `path0` and `path1`,
+ * and returns a `true` or `false`;
+ * returns `false` if either entity does not exist:
+ *
+ * | Character | Test |
+ * |:------------:|:----------------------------------------------------------------|
+ * | <tt>'<'</tt> | Whether the `mtime` at `path0` is less than that at `path1`. |
+ * | <tt>'='</tt> | Whether the `mtime` at `path0` is equal to that at `path1`. |
+ * | <tt>'>'</tt> | Whether the `mtime` at `path0` is greater than that at `path1`. |
+ *
+ * - This test operates on the content of each of the entities at `path0` and `path1`,
+ * and returns a `true` or `false`;
+ * returns `false` if either entity does not exist:
+ *
+ * | Character | Test |
+ * |:------------:|:----------------------------------------------|
+ * | <tt>'-'</tt> | Whether the entities exist and are identical. |
+ *
*/
static VALUE
@@ -5559,7 +5675,7 @@ rb_stat_s_alloc(VALUE klass)
static VALUE
rb_stat_init(VALUE obj, VALUE fname)
{
- struct stat st, *nst;
+ struct stat st;
FilePathValue(fname);
fname = rb_str_encode_ospath(fname);
@@ -5567,13 +5683,11 @@ rb_stat_init(VALUE obj, VALUE fname)
rb_sys_fail_path(fname);
}
- if (DATA_PTR(obj)) {
- xfree(DATA_PTR(obj));
- DATA_PTR(obj) = NULL;
- }
- nst = ALLOC(struct stat);
- *nst = st;
- DATA_PTR(obj) = nst;
+ struct rb_stat *rb_st;
+ TypedData_Get_Struct(obj, struct rb_stat, &stat_data_type, rb_st);
+
+ rb_st->stat = st;
+ rb_st->initialized = true;
return Qnil;
}
@@ -5582,19 +5696,15 @@ rb_stat_init(VALUE obj, VALUE fname)
static VALUE
rb_stat_init_copy(VALUE copy, VALUE orig)
{
- struct stat *nst;
-
if (!OBJ_INIT_COPY(copy, orig)) return copy;
- if (DATA_PTR(copy)) {
- xfree(DATA_PTR(copy));
- DATA_PTR(copy) = 0;
- }
- if (DATA_PTR(orig)) {
- nst = ALLOC(struct stat);
- *nst = *(struct stat*)DATA_PTR(orig);
- DATA_PTR(copy) = nst;
- }
+ struct rb_stat *orig_rb_st;
+ TypedData_Get_Struct(orig, struct rb_stat, &stat_data_type, orig_rb_st);
+
+ struct rb_stat *copy_rb_st;
+ TypedData_Get_Struct(copy, struct rb_stat, &stat_data_type, copy_rb_st);
+
+ *copy_rb_st = *orig_rb_st;
return copy;
}
@@ -6086,7 +6196,7 @@ rb_stat_z(VALUE obj)
* the file otherwise.
*
* File.stat("testfile").size? #=> 66
- * File.stat("/dev/null").size? #=> nil
+ * File.stat(File::NULL).size? #=> nil
*
*/
@@ -6205,7 +6315,7 @@ rb_file_s_mkfifo(int argc, VALUE *argv, VALUE _)
FilePathValue(path);
path = rb_str_encode_ospath(path);
ma.path = RSTRING_PTR(path);
- if (rb_thread_call_without_gvl(nogvl_mkfifo, &ma, RUBY_UBF_IO, 0)) {
+ if (IO_WITHOUT_GVL(nogvl_mkfifo, &ma)) {
rb_sys_fail_path(path);
}
return INT2FIX(0);
@@ -6531,7 +6641,7 @@ const char ruby_null_device[] =
* \Class \File extends module FileTest, supporting such singleton methods
* as <tt>File.exist?</tt>.
*
- * === About the Examples
+ * == About the Examples
*
* Many examples here use these variables:
*
@@ -6539,11 +6649,11 @@ const char ruby_null_device[] =
*
* == Access Modes
*
- * \Methods File.new and File.open each create a \File object for a given file path.
+ * Methods File.new and File.open each create a \File object for a given file path.
*
* === \String Access Modes
*
- * \Methods File.new and File.open each may take string argument +mode+, which:
+ * Methods File.new and File.open each may take string argument +mode+, which:
*
* - Begins with a 1- or 2-character
* {read/write mode}[rdoc-ref:File@Read-2FWrite+Mode].
@@ -6612,7 +6722,7 @@ const char ruby_null_device[] =
*
* - <tt>'r'</tt>:
*
- * - File is not initially truncated:
+ * - \File is not initially truncated:
*
* f = File.new('t.txt') # => #<File:t.txt>
* f.size == 0 # => false
@@ -6621,7 +6731,7 @@ const char ruby_null_device[] =
*
* f.pos # => 0
*
- * - File may be read anywhere; see IO#rewind, IO#pos=, IO#seek:
+ * - \File may be read anywhere; see IO#rewind, IO#pos=, IO#seek:
*
* f.readline # => "First line\n"
* f.readline # => "Second line\n"
@@ -6641,7 +6751,7 @@ const char ruby_null_device[] =
*
* - <tt>'w'</tt>:
*
- * - File is initially truncated:
+ * - \File is initially truncated:
*
* path = 't.tmp'
* File.write(path, text)
@@ -6652,7 +6762,7 @@ const char ruby_null_device[] =
*
* f.pos # => 0
*
- * - File may be written anywhere (even past end-of-file);
+ * - \File may be written anywhere (even past end-of-file);
* see IO#rewind, IO#pos=, IO#seek:
*
* f.write('foo')
@@ -6695,7 +6805,7 @@ const char ruby_null_device[] =
*
* - <tt>'a'</tt>:
*
- * - File is not initially truncated:
+ * - \File is not initially truncated:
*
* path = 't.tmp'
* File.write(path, 'foo')
@@ -6706,7 +6816,7 @@ const char ruby_null_device[] =
*
* f.pos # => 0
*
- * - File may be written only at end-of-file;
+ * - \File may be written only at end-of-file;
* IO#rewind, IO#pos=, IO#seek do not affect writing:
*
* f.write('bar')
@@ -6727,7 +6837,7 @@ const char ruby_null_device[] =
*
* - <tt>'r+'</tt>:
*
- * - File is not initially truncated:
+ * - \File is not initially truncated:
*
* path = 't.tmp'
* File.write(path, text)
@@ -6738,7 +6848,7 @@ const char ruby_null_device[] =
*
* f.pos # => 0
*
- * - File may be read or written anywhere (even past end-of-file);
+ * - \File may be read or written anywhere (even past end-of-file);
* see IO#rewind, IO#pos=, IO#seek:
*
* f.readline # => "First line\n"
@@ -6783,7 +6893,7 @@ const char ruby_null_device[] =
*
* - <tt>'a+'</tt>:
*
- * - File is not initially truncated:
+ * - \File is not initially truncated:
*
* path = 't.tmp'
* File.write(path, 'foo')
@@ -6794,7 +6904,7 @@ const char ruby_null_device[] =
*
* f.pos # => 0
*
- * - File may be written only at end-of-file;
+ * - \File may be written only at end-of-file;
* IO#rewind, IO#pos=, IO#seek do not affect writing:
*
* f.write('bar')
@@ -6809,7 +6919,7 @@ const char ruby_null_device[] =
* f.flush
* File.read(path) # => "foobarbazbat"
*
- * - File may be read anywhere; see IO#rewind, IO#pos=, IO#seek:
+ * - \File may be read anywhere; see IO#rewind, IO#pos=, IO#seek:
*
* f.rewind
* f.read # => "foobarbazbat"
@@ -6834,7 +6944,7 @@ const char ruby_null_device[] =
* f = File.new(path, 'w')
* f.pos # => 0
*
- * - File may be written anywhere (even past end-of-file);
+ * - \File may be written anywhere (even past end-of-file);
* see IO#rewind, IO#pos=, IO#seek:
*
* f.write('foo')
@@ -6911,7 +7021,7 @@ const char ruby_null_device[] =
* f = File.new(path, 'w+')
* f.pos # => 0
*
- * - File may be written anywhere (even past end-of-file);
+ * - \File may be written anywhere (even past end-of-file);
* see IO#rewind, IO#pos=, IO#seek:
*
* f.write('foo')
@@ -6948,7 +7058,7 @@ const char ruby_null_device[] =
* File.read(path) # => "bazbam\u0000\u0000bah"
* f.pos # => 11
*
- * - File may be read anywhere (even past end-of-file);
+ * - \File may be read anywhere (even past end-of-file);
* see IO#rewind, IO#pos=, IO#seek:
*
* f.rewind
@@ -6989,7 +7099,7 @@ const char ruby_null_device[] =
* f.flush
* File.read(path) # => "foobarbaz"
*
- * - File may be read anywhere (even past end-of-file);
+ * - \File may be read anywhere (even past end-of-file);
* see IO#rewind, IO#pos=, IO#seek:
*
* f.rewind
@@ -7134,7 +7244,6 @@ const char ruby_null_device[] =
*
* Note that file permissions are quite different from the _mode_
* of a file stream (\File object).
- * See IO@Modes.
*
* In a \File object, the permissions are available thus,
* where method +mode+, despite its name, returns permissions:
@@ -7180,7 +7289,7 @@ const char ruby_null_device[] =
*
* == \File \Constants
*
- * Various constants for use in \File and \IO methods
+ * Various constants for use in \File and IO methods
* may be found in module File::Constants;
* an array of their names is returned by <tt>File::Constants.constants</tt>.
*
@@ -7190,7 +7299,7 @@ const char ruby_null_device[] =
*
* - Inherits from {class IO}[rdoc-ref:IO@What-27s+Here],
* in particular, methods for creating, reading, and writing files
- * - Includes {module FileTest}[rdoc-ref:FileTest@What-27s+Here].
+ * - Includes module FileTest,
* which provides dozens of additional methods.
*
* Here, class \File provides methods that are useful for:
@@ -7235,15 +7344,15 @@ const char ruby_null_device[] =
*
* _Times_
*
- * - ::atime: Returns a \Time for the most recent access to the given file.
- * - ::birthtime: Returns a \Time for the creation of the given file.
- * - ::ctime: Returns a \Time for the metadata change of the given file.
- * - ::mtime: Returns a \Time for the most recent data modification to
+ * - ::atime: Returns a Time for the most recent access to the given file.
+ * - ::birthtime: Returns a Time for the creation of the given file.
+ * - ::ctime: Returns a Time for the metadata change of the given file.
+ * - ::mtime: Returns a Time for the most recent data modification to
* the content of the given file.
- * - #atime: Returns a \Time for the most recent access to +self+.
- * - #birthtime: Returns a \Time the creation for +self+.
- * - #ctime: Returns a \Time for the metadata change of +self+.
- * - #mtime: Returns a \Time for the most recent data modification
+ * - #atime: Returns a Time for the most recent access to +self+.
+ * - #birthtime: Returns a Time the creation for +self+.
+ * - #ctime: Returns a Time for the metadata change of +self+.
+ * - #mtime: Returns a Time for the most recent data modification
* to the content of +self+.
*
* _Types_
@@ -7432,93 +7541,400 @@ Init_File(void)
/*
* Document-module: File::Constants
*
- * File::Constants provides file-related constants. All possible
- * file constants are listed in the documentation but they may not all
- * be present on your platform.
+ * \Module +File::Constants+ defines file-related constants.
+ *
+ * There are two families of constants here:
+ *
+ * - Those having to do with {file access}[rdoc-ref:File::Constants@File+Access].
+ * - Those having to do with {filename globbing}[rdoc-ref:File::Constants@Filename+Globbing+Constants+-28File-3A-3AFNM_-2A-29].
*
- * If the underlying platform doesn't define a constant the corresponding
- * Ruby constant is not defined.
+ * \File constants defined for the local process may be retrieved
+ * with method File::Constants.constants:
+ *
+ * File::Constants.constants.take(5)
+ * # => [:RDONLY, :WRONLY, :RDWR, :APPEND, :CREAT]
+ *
+ * == \File Access
+ *
+ * \File-access constants may be used with optional argument +mode+ in calls
+ * to the following methods:
+ *
+ * - File.new.
+ * - File.open.
+ * - IO.for_fd.
+ * - IO.new.
+ * - IO.open.
+ * - IO.popen.
+ * - IO.reopen.
+ * - IO.sysopen.
+ * - StringIO.new.
+ * - StringIO.open.
+ * - StringIO#reopen.
+ *
+ * === Read/Write Access
+ *
+ * Read-write access for a stream
+ * may be specified by a file-access constant.
+ *
+ * The constant may be specified as part of a bitwise OR of other such constants.
+ *
+ * Any combination of the constants in this section may be specified.
+ *
+ * ==== File::RDONLY
+ *
+ * Flag File::RDONLY specifies the stream should be opened for reading only:
+ *
+ * filepath = '/tmp/t.tmp'
+ * f = File.new(filepath, File::RDONLY)
+ * f.write('Foo') # Raises IOError (not opened for writing).
+ *
+ * ==== File::WRONLY
+ *
+ * Flag File::WRONLY specifies that the stream should be opened for writing only:
+ *
+ * f = File.new(filepath, File::WRONLY)
+ * f.read # Raises IOError (not opened for reading).
+ *
+ * ==== File::RDWR
+ *
+ * Flag File::RDWR specifies that the stream should be opened
+ * for both reading and writing:
+ *
+ * f = File.new(filepath, File::RDWR)
+ * f.write('Foo') # => 3
+ * f.rewind # => 0
+ * f.read # => "Foo"
+ *
+ * === \File Positioning
+ *
+ * ==== File::APPEND
+ *
+ * Flag File::APPEND specifies that the stream should be opened
+ * in append mode.
+ *
+ * Before each write operation, the position is set to end-of-stream.
+ * The modification of the position and the following write operation
+ * are performed as a single atomic step.
+ *
+ * ==== File::TRUNC
+ *
+ * Flag File::TRUNC specifies that the stream should be truncated
+ * at its beginning.
+ * If the file exists and is successfully opened for writing,
+ * it is to be truncated to position zero;
+ * its ctime and mtime are updated.
+ *
+ * There is no effect on a FIFO special file or a terminal device.
+ * The effect on other file types is implementation-defined.
+ * The result of using File::TRUNC with File::RDONLY is undefined.
+ *
+ * === Creating and Preserving
+ *
+ * ==== File::CREAT
+ *
+ * Flag File::CREAT specifies that the stream should be created
+ * if it does not already exist.
+ *
+ * If the file exists:
+ *
+ * - Raise an exception if File::EXCL is also specified.
+ * - Otherwise, do nothing.
+ *
+ * If the file does not exist, then it is created.
+ * Upon successful completion, the atime, ctime, and mtime of the file are updated,
+ * and the ctime and mtime of the parent directory are updated.
+ *
+ * ==== File::EXCL
+ *
+ * Flag File::EXCL specifies that the stream should not already exist;
+ * If flags File::CREAT and File::EXCL are both specified
+ * and the stream already exists, an exception is raised.
+ *
+ * The check for the existence and creation of the file is performed as an
+ * atomic operation.
+ *
+ * If both File::EXCL and File::CREAT are specified and the path names a symbolic link,
+ * an exception is raised regardless of the contents of the symbolic link.
+ *
+ * If File::EXCL is specified and File::CREAT is not specified,
+ * the result is undefined.
+ *
+ * === POSIX \File \Constants
+ *
+ * Some file-access constants are defined only on POSIX-compliant systems;
+ * those are:
+ *
+ * - File::SYNC.
+ * - File::DSYNC.
+ * - File::RSYNC.
+ * - File::DIRECT.
+ * - File::NOATIME.
+ * - File::NOCTTY.
+ * - File::NOFOLLOW.
+ * - File::TMPFILE.
+ *
+ * ==== File::SYNC, File::RSYNC, and File::DSYNC
+ *
+ * Flag File::SYNC, File::RSYNC, or File::DSYNC
+ * specifies synchronization of I/O operations with the underlying file system.
+ *
+ * These flags are valid only for POSIX-compliant systems.
+ *
+ * - File::SYNC specifies that all write operations (both data and metadata)
+ * are immediately to be flushed to the underlying storage device.
+ * This means that the data is written to the storage device,
+ * and the file's metadata (e.g., file size, timestamps, permissions)
+ * are also synchronized.
+ * This guarantees that data is safely stored on the storage medium
+ * before returning control to the calling program.
+ * This flag can have a significant impact on performance
+ * since it requires synchronous writes, which can be slower
+ * compared to asynchronous writes.
+ *
+ * - File::RSYNC specifies that any read operations on the file will not return
+ * until all outstanding write operations
+ * (those that have been issued but not completed) are also synchronized.
+ * This is useful when you want to read the most up-to-date data,
+ * which may still be in the process of being written.
+ *
+ * - File::DSYNC specifies that all _data_ write operations
+ * are immediately to be flushed to the underlying storage device;
+ * this differs from File::SYNC, which requires that _metadata_
+ * also be synchronized.
+ *
+ * Note that the behavior of these flags may vary slightly
+ * depending on the operating system and filesystem being used.
+ * Additionally, using these flags can have an impact on performance
+ * due to the synchronous nature of the I/O operations,
+ * so they should be used judiciously,
+ * especially in performance-critical applications.
+ *
+ * ==== File::NOCTTY
+ *
+ * Flag File::NOCTTY specifies that if the stream is a terminal device,
+ * that device does not become the controlling terminal for the process.
+ *
+ * Defined only for POSIX-compliant systems.
+ *
+ * ==== File::DIRECT
+ *
+ * Flag File::DIRECT requests that cache effects of the I/O to and from the stream
+ * be minimized.
+ *
+ * Defined only for POSIX-compliant systems.
+ *
+ * ==== File::NOATIME
+ *
+ * Flag File::NOATIME specifies that act of opening the stream
+ * should not modify its access time (atime).
+ *
+ * Defined only for POSIX-compliant systems.
+ *
+ * ==== File::NOFOLLOW
+ *
+ * Flag File::NOFOLLOW specifies that if path is a symbolic link,
+ * it should not be followed.
+ *
+ * Defined only for POSIX-compliant systems.
+ *
+ * ==== File::TMPFILE
+ *
+ * Flag File::TMPFILE specifies that the opened stream
+ * should be a new temporary file.
+ *
+ * Defined only for POSIX-compliant systems.
+ *
+ * === Other File-Access \Constants
+ *
+ * ==== File::NONBLOCK
+ *
+ * When possible, the file is opened in nonblocking mode.
+ * Neither the open operation nor any subsequent I/O operations on
+ * the file will cause the calling process to wait.
+ *
+ * ==== File::BINARY
+ *
+ * Flag File::BINARY specifies that the stream is to be accessed in binary mode.
+ *
+ * ==== File::SHARE_DELETE
+ *
+ * Flag File::SHARE_DELETE enables other processes to open the stream
+ * with delete access.
+ *
+ * Windows only.
+ *
+ * If the stream is opened for (local) delete access without File::SHARE_DELETE,
+ * and another process attempts to open it with delete access,
+ * the attempt fails and the stream is not opened for that process.
+ *
+ * == Locking
+ *
+ * Four file constants relate to stream locking;
+ * see File#flock:
+ *
+ * ==== File::LOCK_EX
+ *
+ * Flag File::LOCK_EX specifies an exclusive lock;
+ * only one process a a time may lock the stream.
+ *
+ * ==== File::LOCK_NB
+ *
+ * Flag File::LOCK_NB specifies non-blocking locking for the stream;
+ * may be combined with File::LOCK_EX or File::LOCK_SH.
+ *
+ * ==== File::LOCK_SH
+ *
+ * Flag File::LOCK_SH specifies that multiple processes may lock
+ * the stream at the same time.
+ *
+ * ==== File::LOCK_UN
+ *
+ * Flag File::LOCK_UN specifies that the stream is not to be locked.
+ *
+ * == Filename Globbing \Constants (File::FNM_*)
+ *
+ * Filename-globbing constants may be used with optional argument +flags+
+ * in calls to the following methods:
+ *
+ * - Dir.glob.
+ * - File.fnmatch.
+ * - Pathname#fnmatch.
+ * - Pathname.glob.
+ * - Pathname#glob.
+ *
+ * The constants are:
+ *
+ * ==== File::FNM_CASEFOLD
+ *
+ * Flag File::FNM_CASEFOLD makes patterns case insensitive
+ * for File.fnmatch (but not Dir.glob).
+ *
+ * ==== File::FNM_DOTMATCH
+ *
+ * Flag File::FNM_DOTMATCH makes the <tt>'*'</tt> pattern
+ * match a filename starting with <tt>'.'</tt>.
+ *
+ * ==== File::FNM_EXTGLOB
+ *
+ * Flag File::FNM_EXTGLOB enables pattern <tt>'{_a_,_b_}'</tt>,
+ * which matches pattern '_a_' and pattern '_b_';
+ * behaves like
+ * a {regexp union}[rdoc-ref:Regexp.union]
+ * (e.g., <tt>'(?:_a_|_b_)'</tt>):
+ *
+ * pattern = '{LEGAL,BSDL}'
+ * Dir.glob(pattern) # => ["LEGAL", "BSDL"]
+ * Pathname.glob(pattern) # => [#<Pathname:LEGAL>, #<Pathname:BSDL>]
+ * pathname.glob(pattern) # => [#<Pathname:LEGAL>, #<Pathname:BSDL>]
+ *
+ * ==== File::FNM_NOESCAPE
+ *
+ * Flag File::FNM_NOESCAPE disables <tt>'\'</tt> escaping.
+ *
+ * ==== File::FNM_PATHNAME
+ *
+ * Flag File::FNM_PATHNAME specifies that patterns <tt>'*'</tt> and <tt>'?'</tt>
+ * do not match the directory separator
+ * (the value of constant File::SEPARATOR).
+ *
+ * ==== File::FNM_SHORTNAME
+ *
+ * Flag File::FNM_SHORTNAME allows patterns to match short names if they exist.
+ *
+ * Windows only.
+ *
+ * ==== File::FNM_SYSCASE
+ *
+ * Flag File::FNM_SYSCASE specifies that case sensitivity
+ * is the same as in the underlying operating system;
+ * effective for File.fnmatch, but not Dir.glob.
+ *
+ * == Other \Constants
+ *
+ * ==== File::NULL
+ *
+ * Flag File::NULL contains the string value of the null device:
+ *
+ * - On a Unix-like OS, <tt>'/dev/null'</tt>.
+ * - On Windows, <tt>'NUL'</tt>.
*
- * Your platform documentations (e.g. man open(2)) may describe more
- * detailed information.
*/
rb_mFConst = rb_define_module_under(rb_cFile, "Constants");
rb_include_module(rb_cIO, rb_mFConst);
-
- /* open for reading only */
+ /* {File::RDONLY}[rdoc-ref:File::Constants@File-3A-3ARDONLY] */
rb_define_const(rb_mFConst, "RDONLY", INT2FIX(O_RDONLY));
- /* open for writing only */
+ /* {File::WRONLY}[rdoc-ref:File::Constants@File-3A-3AWRONLY] */
rb_define_const(rb_mFConst, "WRONLY", INT2FIX(O_WRONLY));
- /* open for reading and writing */
+ /* {File::RDWR}[rdoc-ref:File::Constants@File-3A-3ARDWR] */
rb_define_const(rb_mFConst, "RDWR", INT2FIX(O_RDWR));
- /* append on each write */
+ /* {File::APPEND}[rdoc-ref:File::Constants@File-3A-3AAPPEND] */
rb_define_const(rb_mFConst, "APPEND", INT2FIX(O_APPEND));
- /* create file if it does not exist */
+ /* {File::CREAT}[rdoc-ref:File::Constants@File-3A-3ACREAT] */
rb_define_const(rb_mFConst, "CREAT", INT2FIX(O_CREAT));
- /* error if CREAT and the file exists */
+ /* {File::EXCL}[rdoc-ref:File::Constants@File-3A-3AEXCL] */
rb_define_const(rb_mFConst, "EXCL", INT2FIX(O_EXCL));
#if defined(O_NDELAY) || defined(O_NONBLOCK)
# ifndef O_NONBLOCK
# define O_NONBLOCK O_NDELAY
# endif
- /* do not block on open or for data to become available */
+ /* {File::NONBLOCK}[rdoc-ref:File::Constants@File-3A-3ANONBLOCK] */
rb_define_const(rb_mFConst, "NONBLOCK", INT2FIX(O_NONBLOCK));
#endif
- /* truncate size to 0 */
+ /* {File::TRUNC}[rdoc-ref:File::Constants@File-3A-3ATRUNC] */
rb_define_const(rb_mFConst, "TRUNC", INT2FIX(O_TRUNC));
#ifdef O_NOCTTY
- /* not to make opened IO the controlling terminal device */
+ /* {File::NOCTTY}[rdoc-ref:File::Constants@File-3A-3ANOCTTY] */
rb_define_const(rb_mFConst, "NOCTTY", INT2FIX(O_NOCTTY));
#endif
#ifndef O_BINARY
# define O_BINARY 0
#endif
- /* disable line code conversion */
+ /* {File::BINARY}[rdoc-ref:File::Constants@File-3A-3ABINARY] */
rb_define_const(rb_mFConst, "BINARY", INT2FIX(O_BINARY));
#ifndef O_SHARE_DELETE
# define O_SHARE_DELETE 0
#endif
- /* can delete opened file */
+ /* {File::SHARE_DELETE}[rdoc-ref:File::Constants@File-3A-3ASHARE_DELETE] */
rb_define_const(rb_mFConst, "SHARE_DELETE", INT2FIX(O_SHARE_DELETE));
#ifdef O_SYNC
- /* any write operation perform synchronously */
+ /* {File::SYNC}[rdoc-ref:File::Constants@File-3A-3ASYNC-2C+File-3A-3ARSYNC-2C+and+File-3A-3ADSYNC] */
rb_define_const(rb_mFConst, "SYNC", INT2FIX(O_SYNC));
#endif
#ifdef O_DSYNC
- /* any write operation perform synchronously except some meta data */
+ /* {File::DSYNC}[rdoc-ref:File::Constants@File-3A-3ASYNC-2C+File-3A-3ARSYNC-2C+and+File-3A-3ADSYNC] */
rb_define_const(rb_mFConst, "DSYNC", INT2FIX(O_DSYNC));
#endif
#ifdef O_RSYNC
- /* any read operation perform synchronously. used with SYNC or DSYNC. */
+ /* {File::RSYNC}[rdoc-ref:File::Constants@File-3A-3ASYNC-2C+File-3A-3ARSYNC-2C+and+File-3A-3ADSYNC] */
rb_define_const(rb_mFConst, "RSYNC", INT2FIX(O_RSYNC));
#endif
#ifdef O_NOFOLLOW
- /* do not follow symlinks */
+ /* {File::NOFOLLOW}[rdoc-ref:File::Constants@File-3A-3ANOFOLLOW] */
rb_define_const(rb_mFConst, "NOFOLLOW", INT2FIX(O_NOFOLLOW)); /* FreeBSD, Linux */
#endif
#ifdef O_NOATIME
- /* do not change atime */
+ /* {File::NOATIME}[rdoc-ref:File::Constants@File-3A-3ANOATIME] */
rb_define_const(rb_mFConst, "NOATIME", INT2FIX(O_NOATIME)); /* Linux */
#endif
#ifdef O_DIRECT
- /* Try to minimize cache effects of the I/O to and from this file. */
+ /* {File::DIRECT}[rdoc-ref:File::Constants@File-3A-3ADIRECT] */
rb_define_const(rb_mFConst, "DIRECT", INT2FIX(O_DIRECT));
#endif
#ifdef O_TMPFILE
- /* Create an unnamed temporary file */
+ /* {File::TMPFILE}[rdoc-ref:File::Constants@File-3A-3ATMPFILE] */
rb_define_const(rb_mFConst, "TMPFILE", INT2FIX(O_TMPFILE));
#endif
- /* shared lock. see File#flock */
+ /* {File::LOCK_SH}[rdoc-ref:File::Constants@File-3A-3ALOCK_SH] */
rb_define_const(rb_mFConst, "LOCK_SH", INT2FIX(LOCK_SH));
- /* exclusive lock. see File#flock */
+ /* {File::LOCK_EX}[rdoc-ref:File::Constants@File-3A-3ALOCK_EX] */
rb_define_const(rb_mFConst, "LOCK_EX", INT2FIX(LOCK_EX));
- /* unlock. see File#flock */
+ /* {File::LOCK_UN}[rdoc-ref:File::Constants@File-3A-3ALOCK_UN] */
rb_define_const(rb_mFConst, "LOCK_UN", INT2FIX(LOCK_UN));
- /* non-blocking lock. used with LOCK_SH or LOCK_EX. see File#flock */
+ /* {File::LOCK_NB}[rdoc-ref:File::Constants@File-3A-3ALOCK_NB] */
rb_define_const(rb_mFConst, "LOCK_NB", INT2FIX(LOCK_NB));
- /* Name of the null device */
+ /* {File::NULL}[rdoc-ref:File::Constants@File-3A-3ANULL] */
rb_define_const(rb_mFConst, "NULL", rb_fstring_cstr(ruby_null_device));
rb_define_global_function("test", rb_f_test, -1);
diff --git a/gc.c b/gc.c
index 707754a9e5..fc03774a8d 100644
--- a/gc.c
+++ b/gc.c
@@ -21,8 +21,6 @@
#include <signal.h>
-#define sighandler_t ruby_sighandler_t
-
#ifndef _WIN32
#include <unistd.h>
#include <sys/mman.h>
@@ -60,6 +58,15 @@
# endif
#endif
+#ifdef HAVE_MALLOC_TRIM
+# include <malloc.h>
+
+# ifdef __EMSCRIPTEN__
+/* malloc_trim is defined in emscripten/emmalloc.h on emscripten. */
+# include <emscripten/emmalloc.h>
+# endif
+#endif
+
#if !defined(PAGE_SIZE) && defined(HAVE_SYS_USER_H)
/* LIST_HEAD conflicts with sys/queue.h on macOS */
# include <sys/user.h>
@@ -95,6 +102,7 @@
#undef LIST_HEAD /* ccan/list conflicts with BSD-origin sys/queue.h. */
#include "constant.h"
+#include "darray.h"
#include "debug_counter.h"
#include "eval_intern.h"
#include "id_table.h"
@@ -131,7 +139,6 @@
#include "ruby_assert.h"
#include "ruby_atomic.h"
#include "symbol.h"
-#include "transient_heap.h"
#include "vm_core.h"
#include "vm_sync.h"
#include "vm_callinfo.h"
@@ -148,6 +155,68 @@
#define MAP_ANONYMOUS MAP_ANON
#endif
+
+static size_t malloc_offset = 0;
+#if defined(HAVE_MALLOC_USABLE_SIZE)
+static size_t
+gc_compute_malloc_offset(void)
+{
+ // Different allocators use different metadata storage strategies which result in different
+ // ideal sizes.
+ // For instance malloc(64) will waste 8B with glibc, but waste 0B with jemalloc.
+ // But malloc(56) will waste 0B with glibc, but waste 8B with jemalloc.
+ // So we try allocating 64, 56 and 48 bytes and select the first offset that doesn't
+ // waste memory.
+ // This was tested on Linux with glibc 2.35 and jemalloc 5, and for both it result in
+ // no wasted memory.
+ size_t offset = 0;
+ for (offset = 0; offset <= 16; offset += 8) {
+ size_t allocated = (64 - offset);
+ void *test_ptr = malloc(allocated);
+ size_t wasted = malloc_usable_size(test_ptr) - allocated;
+ free(test_ptr);
+
+ if (wasted == 0) {
+ return offset;
+ }
+ }
+ return 0;
+}
+#else
+static size_t
+gc_compute_malloc_offset(void)
+{
+ // If we don't have malloc_usable_size, we use powers of 2.
+ return 0;
+}
+#endif
+
+size_t
+rb_malloc_grow_capa(size_t current, size_t type_size)
+{
+ size_t current_capacity = current;
+ if (current_capacity < 4) {
+ current_capacity = 4;
+ }
+ current_capacity *= type_size;
+
+ // We double the current capacity.
+ size_t new_capacity = (current_capacity * 2);
+
+ // And round up to the next power of 2 if it's not already one.
+ if (rb_popcount64(new_capacity) != 1) {
+ new_capacity = (size_t)(1 << (64 - nlz_int64(new_capacity)));
+ }
+
+ new_capacity -= malloc_offset;
+ new_capacity /= type_size;
+ if (current > new_capacity) {
+ rb_bug("rb_malloc_grow_capa: current_capacity=%zu, new_capacity=%zu, malloc_offset=%zu", current, new_capacity, malloc_offset);
+ }
+ RUBY_ASSERT(new_capacity > current);
+ return new_capacity;
+}
+
static inline struct rbimpl_size_mul_overflow_tag
size_add_overflow(size_t x, size_t y)
{
@@ -155,6 +224,9 @@ size_add_overflow(size_t x, size_t y)
bool p;
#if 0
+#elif defined(ckd_add)
+ p = ckd_add(&z, x, y);
+
#elif __has_builtin(__builtin_add_overflow)
p = __builtin_add_overflow(x, y, &z);
@@ -328,6 +400,14 @@ rb_gc_guarded_ptr_val(volatile VALUE *ptr, VALUE val)
#define GC_OLDMALLOC_LIMIT_MAX (128 * 1024 * 1024 /* 128MB */)
#endif
+#ifndef GC_CAN_COMPILE_COMPACTION
+#if defined(__wasi__) /* WebAssembly doesn't support signals */
+# define GC_CAN_COMPILE_COMPACTION 0
+#else
+# define GC_CAN_COMPILE_COMPACTION 1
+#endif
+#endif
+
#ifndef PRINT_MEASURE_LINE
#define PRINT_MEASURE_LINE 0
#endif
@@ -339,10 +419,9 @@ rb_gc_guarded_ptr_val(volatile VALUE *ptr, VALUE val)
#endif
#define USE_TICK_T (PRINT_ENTER_EXIT_TICK || PRINT_MEASURE_LINE || PRINT_ROOT_TICKS)
-#define TICK_TYPE 1
typedef struct {
- size_t heap_init_slots;
+ size_t size_pool_init_slots[SIZE_POOL_COUNT];
size_t heap_free_slots;
double growth_factor;
size_t growth_max_slots;
@@ -360,12 +439,10 @@ typedef struct {
size_t oldmalloc_limit_min;
size_t oldmalloc_limit_max;
double oldmalloc_limit_growth_factor;
-
- VALUE gc_stress;
} ruby_gc_params_t;
static ruby_gc_params_t gc_params = {
- GC_HEAP_INIT_SLOTS,
+ { 0 },
GC_HEAP_FREE_SLOTS,
GC_HEAP_GROWTH_FACTOR,
GC_HEAP_GROWTH_MAX_SLOTS,
@@ -383,8 +460,6 @@ static ruby_gc_params_t gc_params = {
GC_OLDMALLOC_LIMIT_MIN,
GC_OLDMALLOC_LIMIT_MAX,
GC_OLDMALLOC_LIMIT_GROWTH_FACTOR,
-
- FALSE,
};
/* GC_DEBUG:
@@ -432,16 +507,6 @@ int ruby_rgengc_debug;
// Note: using RUBY_ASSERT_WHEN() extend a macro in expr (info by nobu).
#define GC_ASSERT(expr) RUBY_ASSERT_MESG_WHEN(RGENGC_CHECK_MODE > 0, expr, #expr)
-/* RGENGC_OLD_NEWOBJ_CHECK
- * 0: disable all assertions
- * >0: make a OLD object when new object creation.
- *
- * Make one OLD object per RGENGC_OLD_NEWOBJ_CHECK WB protected objects creation.
- */
-#ifndef RGENGC_OLD_NEWOBJ_CHECK
-#define RGENGC_OLD_NEWOBJ_CHECK 0
-#endif
-
/* RGENGC_PROFILE
* 0: disable RGenGC profiling
* 1: enable profiling for basic information
@@ -492,7 +557,7 @@ int ruby_rgengc_debug;
#endif
#ifndef GC_DEBUG_STRESS_TO_CLASS
-#define GC_DEBUG_STRESS_TO_CLASS 0
+#define GC_DEBUG_STRESS_TO_CLASS RUBY_DEBUG
#endif
#ifndef RGENGC_OBJ_INFO
@@ -654,7 +719,6 @@ enum {
BITS_SIZE = sizeof(bits_t),
BITS_BITLENGTH = ( BITS_SIZE * CHAR_BIT )
};
-#define popcount_bits rb_popcount_intptr
struct heap_page_header {
struct heap_page *page;
@@ -666,11 +730,6 @@ struct heap_page_body {
/* RVALUE values[]; */
};
-struct gc_list {
- VALUE *varptr;
- struct gc_list *next;
-};
-
#define STACK_CHUNK_SIZE 500
typedef struct stack_chunk {
@@ -690,6 +749,8 @@ typedef struct mark_stack {
#define SIZE_POOL_EDEN_HEAP(size_pool) (&(size_pool)->eden_heap)
#define SIZE_POOL_TOMB_HEAP(size_pool) (&(size_pool)->tomb_heap)
+typedef int (*gc_compact_compare_func)(const void *l, const void *r, void *d);
+
typedef struct rb_heap_struct {
struct heap_page *free_pages;
struct ccan_list_head pages;
@@ -710,6 +771,9 @@ typedef struct rb_size_pool_struct {
size_t total_allocated_pages;
size_t total_freed_pages;
size_t force_major_gc_count;
+ size_t force_incremental_marking_finish_count;
+ size_t total_allocated_objects;
+ size_t total_freed_objects;
/* Sweeping statistics */
size_t freed_slots;
@@ -746,15 +810,14 @@ typedef struct rb_objspace {
unsigned int during_compacting : 1;
unsigned int during_reference_updating : 1;
unsigned int gc_stressful: 1;
- unsigned int has_hook: 1;
+ unsigned int has_newobj_hook: 1;
unsigned int during_minor_gc : 1;
unsigned int during_incremental_marking : 1;
unsigned int measure_gc : 1;
} flags;
rb_event_flag_t hook_events;
- size_t total_allocated_objects;
- VALUE next_object_id;
+ unsigned long long next_object_id;
rb_size_pool_t size_pools[SIZE_POOL_COUNT];
@@ -822,13 +885,15 @@ typedef struct rb_objspace {
/* basic statistics */
size_t count;
- size_t total_freed_objects;
uint64_t marking_time_ns;
struct timespec marking_start_time;
uint64_t sweeping_time_ns;
struct timespec sweeping_start_time;
+
+ /* Weak references */
+ size_t weak_references_count;
+ size_t retained_weak_references_count;
} profile;
- struct gc_list *global_list;
VALUE gc_stress_mode;
@@ -858,6 +923,9 @@ typedef struct rb_objspace {
size_t moved_up_count_table[T_MASK];
size_t moved_down_count_table[T_MASK];
size_t total_moved;
+
+ /* This function will be used, if set, to sort the heap prior to compaction */
+ gc_compact_compare_func compare_func;
} rcompactor;
struct {
@@ -871,6 +939,14 @@ typedef struct rb_objspace {
#if GC_DEBUG_STRESS_TO_CLASS
VALUE stress_to_class;
#endif
+
+ rb_darray(VALUE *) weak_references;
+ rb_postponed_job_handle_t finalize_deferred_pjob;
+
+#ifdef RUBY_ASAN_ENABLED
+ const rb_execution_context_t *marking_machine_context_ec;
+#endif
+
} rb_objspace_t;
@@ -939,15 +1015,19 @@ static const bool HEAP_PAGE_ALLOC_USE_MMAP = false;
static bool heap_page_alloc_use_mmap;
#endif
+#define RVALUE_AGE_BIT_COUNT 2
+#define RVALUE_AGE_BIT_MASK (((bits_t)1 << RVALUE_AGE_BIT_COUNT) - 1)
+
struct heap_page {
short slot_size;
short total_slots;
short free_slots;
short final_slots;
+ short pinned_slots;
struct {
unsigned int before_sweep : 1;
unsigned int has_remembered_objects : 1;
- unsigned int has_uncollectible_shady_objects : 1;
+ unsigned int has_uncollectible_wb_unprotected_objects : 1;
unsigned int in_tomb : 1;
} flags;
@@ -968,6 +1048,7 @@ struct heap_page {
/* If set, the object is not movable */
bits_t pinned_bits[HEAP_PAGE_BITMAP_LIMIT];
+ bits_t age_bits[HEAP_PAGE_BITMAP_LIMIT * RVALUE_AGE_BIT_COUNT];
};
/*
@@ -1011,6 +1092,35 @@ asan_unlock_freelist(struct heap_page *page)
#define GC_SWEEP_PAGES_FREEABLE_PER_STEP 3
+#define RVALUE_AGE_BITMAP_INDEX(n) (NUM_IN_PAGE(n) / (BITS_BITLENGTH / RVALUE_AGE_BIT_COUNT))
+#define RVALUE_AGE_BITMAP_OFFSET(n) ((NUM_IN_PAGE(n) % (BITS_BITLENGTH / RVALUE_AGE_BIT_COUNT)) * RVALUE_AGE_BIT_COUNT)
+
+#define RVALUE_OLD_AGE 3
+
+static int
+RVALUE_AGE_GET(VALUE obj)
+{
+ bits_t *age_bits = GET_HEAP_PAGE(obj)->age_bits;
+ return (int)(age_bits[RVALUE_AGE_BITMAP_INDEX(obj)] >> RVALUE_AGE_BITMAP_OFFSET(obj)) & RVALUE_AGE_BIT_MASK;
+}
+
+static void
+RVALUE_AGE_SET(VALUE obj, int age)
+{
+ RUBY_ASSERT(age <= RVALUE_OLD_AGE);
+ bits_t *age_bits = GET_HEAP_PAGE(obj)->age_bits;
+ // clear the bits
+ age_bits[RVALUE_AGE_BITMAP_INDEX(obj)] &= ~(RVALUE_AGE_BIT_MASK << (RVALUE_AGE_BITMAP_OFFSET(obj)));
+ // shift the correct value in
+ age_bits[RVALUE_AGE_BITMAP_INDEX(obj)] |= ((bits_t)age << RVALUE_AGE_BITMAP_OFFSET(obj));
+ if (age == RVALUE_OLD_AGE) {
+ RB_FL_SET_RAW(obj, RUBY_FL_PROMOTED);
+ }
+ else {
+ RB_FL_UNSET_RAW(obj, RUBY_FL_PROMOTED);
+ }
+}
+
/* Aliases */
#define rb_objspace (*rb_objspace_of(GET_VM()))
#define rb_objspace_of(vm) ((vm)->objspace)
@@ -1020,10 +1130,6 @@ asan_unlock_freelist(struct heap_page *page)
if (unless_objspace_vm) objspace = unless_objspace_vm->objspace; \
else /* return; or objspace will be warned uninitialized */
-#define ruby_initial_gc_stress gc_params.gc_stress
-
-VALUE *ruby_initial_gc_stress_ptr = &ruby_initial_gc_stress;
-
#define malloc_limit objspace->malloc_params.limit
#define malloc_increase objspace->malloc_params.increase
#define malloc_allocated_size objspace->malloc_params.allocated_size
@@ -1039,13 +1145,14 @@ VALUE *ruby_initial_gc_stress_ptr = &ruby_initial_gc_stress;
#define during_gc objspace->flags.during_gc
#define finalizing objspace->atomic_flags.finalizing
#define finalizer_table objspace->finalizer_table
-#define global_list objspace->global_list
#define ruby_gc_stressful objspace->flags.gc_stressful
#define ruby_gc_stress_mode objspace->gc_stress_mode
#if GC_DEBUG_STRESS_TO_CLASS
#define stress_to_class objspace->stress_to_class
+#define set_stress_to_class(c) (stress_to_class = (c))
#else
-#define stress_to_class 0
+#define stress_to_class (objspace, 0)
+#define set_stress_to_class(c) (objspace, (c))
#endif
#if 0
@@ -1162,8 +1269,31 @@ total_freed_pages(rb_objspace_t *objspace)
return count;
}
+static inline size_t
+total_allocated_objects(rb_objspace_t *objspace)
+{
+ size_t count = 0;
+ for (int i = 0; i < SIZE_POOL_COUNT; i++) {
+ rb_size_pool_t *size_pool = &size_pools[i];
+ count += size_pool->total_allocated_objects;
+ }
+ return count;
+}
+
+static inline size_t
+total_freed_objects(rb_objspace_t *objspace)
+{
+ size_t count = 0;
+ for (int i = 0; i < SIZE_POOL_COUNT; i++) {
+ rb_size_pool_t *size_pool = &size_pools[i];
+ count += size_pool->total_freed_objects;
+ }
+ return count;
+}
+
#define gc_mode(objspace) gc_mode_verify((enum gc_mode)(objspace)->flags.mode)
-#define gc_mode_set(objspace, mode) ((objspace)->flags.mode = (unsigned int)gc_mode_verify(mode))
+#define gc_mode_set(objspace, m) ((objspace)->flags.mode = (unsigned int)gc_mode_verify(m))
+#define gc_needs_major_flags objspace->rgengc.need_major_gc
#define is_marking(objspace) (gc_mode(objspace) == gc_mode_marking)
#define is_sweeping(objspace) (gc_mode(objspace) == gc_mode_sweeping)
@@ -1171,6 +1301,7 @@ total_freed_pages(rb_objspace_t *objspace)
#define is_incremental_marking(objspace) ((objspace)->flags.during_incremental_marking != FALSE)
#define will_be_incremental_marking(objspace) ((objspace)->rgengc.need_major_gc != GPR_FLAG_NONE)
#define GC_INCREMENTAL_SWEEP_SLOT_COUNT 2048
+#define GC_INCREMENTAL_SWEEP_POOL_SLOT_COUNT 1024
#define is_lazy_sweeping(objspace) (GC_ENABLE_LAZY_SWEEP && has_sweeping_pages(objspace))
#if SIZEOF_LONG == SIZEOF_VOIDP
@@ -1201,10 +1332,10 @@ int ruby_gc_debug_indent = 0;
VALUE rb_mGC;
int ruby_disable_gc = 0;
int ruby_enable_autocompact = 0;
+#if RGENGC_CHECK_MODE
+gc_compact_compare_func ruby_autocompact_compare_func;
+#endif
-void rb_iseq_mark_and_move(rb_iseq_t *iseq, bool referece_updating);
-void rb_iseq_free(const rb_iseq_t *iseq);
-size_t rb_iseq_memsize(const rb_iseq_t *iseq);
void rb_vm_update_references(void *ptr);
void rb_gcdebug_print_obj_condition(VALUE obj);
@@ -1214,9 +1345,6 @@ NORETURN(static void gc_raise(VALUE exc, const char *fmt, ...));
NORETURN(static void negative_size_allocation_error(const char *));
static void init_mark_stack(mark_stack_t *stack);
-
-static int ready_to_gc(rb_objspace_t *objspace);
-
static int garbage_collect(rb_objspace_t *, unsigned int reason);
static int gc_start(rb_objspace_t *objspace, unsigned int reason);
@@ -1236,44 +1364,23 @@ static void gc_marking_enter(rb_objspace_t *objspace);
static void gc_marking_exit(rb_objspace_t *objspace);
static void gc_sweeping_enter(rb_objspace_t *objspace);
static void gc_sweeping_exit(rb_objspace_t *objspace);
-
-static void gc_marks_start(rb_objspace_t *objspace, int full);
-static void gc_marks_finish(rb_objspace_t *objspace);
static bool gc_marks_continue(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap);
static void gc_sweep(rb_objspace_t *objspace);
-static void gc_sweep_start(rb_objspace_t *objspace);
static void gc_sweep_finish_size_pool(rb_objspace_t *objspace, rb_size_pool_t *size_pool);
-static void gc_sweep_finish(rb_objspace_t *objspace);
-static int gc_sweep_step(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap);
-static void gc_sweep_rest(rb_objspace_t *objspace);
static void gc_sweep_continue(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap);
static inline void gc_mark(rb_objspace_t *objspace, VALUE ptr);
static inline void gc_pin(rb_objspace_t *objspace, VALUE ptr);
static inline void gc_mark_and_pin(rb_objspace_t *objspace, VALUE ptr);
-static void gc_mark_ptr(rb_objspace_t *objspace, VALUE ptr);
NO_SANITIZE("memory", static void gc_mark_maybe(rb_objspace_t *objspace, VALUE ptr));
-static void gc_mark_children(rb_objspace_t *objspace, VALUE ptr);
static int gc_mark_stacked_objects_incremental(rb_objspace_t *, size_t count);
-static int gc_mark_stacked_objects_all(rb_objspace_t *);
-static void gc_grey(rb_objspace_t *objspace, VALUE ptr);
-
-static inline int gc_mark_set(rb_objspace_t *objspace, VALUE obj);
-NO_SANITIZE("memory", static inline int is_pointer_to_heap(rb_objspace_t *objspace, void *ptr));
-
-static void push_mark_stack(mark_stack_t *, VALUE);
-static int pop_mark_stack(mark_stack_t *, VALUE *);
-static size_t mark_stack_size(mark_stack_t *stack);
-static void shrink_stack_chunk_cache(mark_stack_t *stack);
+NO_SANITIZE("memory", static inline int is_pointer_to_heap(rb_objspace_t *objspace, const void *ptr));
static size_t obj_memsize_of(VALUE obj, int use_all_types);
static void gc_verify_internal_consistency(rb_objspace_t *objspace);
-static int gc_verify_heap_page(rb_objspace_t *objspace, struct heap_page *page, VALUE obj);
-static int gc_verify_heap_pages(rb_objspace_t *objspace);
-static void gc_stress_set(rb_objspace_t *objspace, VALUE flag);
static VALUE gc_disable_no_rest(rb_objspace_t *);
static double getrusage_time(void);
@@ -1306,19 +1413,12 @@ static inline void gc_prof_set_heap_info(rb_objspace_t *);
#endif
PRINTF_ARGS(static void gc_report_body(int level, rb_objspace_t *objspace, const char *fmt, ...), 3, 4);
static const char *obj_info(VALUE obj);
+static const char *obj_info_basic(VALUE obj);
static const char *obj_type_name(VALUE obj);
-/*
- * 1 - TSC (H/W Time Stamp Counter)
- * 2 - getrusage
- */
-#ifndef TICK_TYPE
-#define TICK_TYPE 1
-#endif
+static void gc_finalize_deferred(void *dmy);
#if USE_TICK_T
-
-#if TICK_TYPE == 1
/* the following code is only for internal tuning. */
/* Source code to use RDTSC is quoted and modified from
@@ -1415,49 +1515,10 @@ tick(void)
return clock();
}
#endif /* TSC */
-
-#elif TICK_TYPE == 2
-typedef double tick_t;
-#define PRItick "4.9f"
-
-static inline tick_t
-tick(void)
-{
- return getrusage_time();
-}
-#else /* TICK_TYPE */
-#error "choose tick type"
-#endif /* TICK_TYPE */
-
-#define MEASURE_LINE(expr) do { \
- volatile tick_t start_time = tick(); \
- volatile tick_t end_time; \
- expr; \
- end_time = tick(); \
- fprintf(stderr, "0\t%"PRItick"\t%s\n", end_time - start_time, #expr); \
-} while (0)
-
#else /* USE_TICK_T */
#define MEASURE_LINE(expr) expr
#endif /* USE_TICK_T */
-static inline void *
-asan_unpoison_object_temporary(VALUE obj)
-{
- void *ptr = asan_poisoned_object_p(obj);
- asan_unpoison_object(obj, false);
- return ptr;
-}
-
-static inline void *
-asan_poison_object_restore(VALUE obj, void *ptr)
-{
- if (ptr) {
- asan_poison_object(obj);
- }
- return NULL;
-}
-
#define asan_unpoisoning_object(obj) \
for (void *poisoned = asan_unpoison_object_temporary(obj), \
*unpoisoning = &poisoned; /* flag to loop just once */ \
@@ -1483,21 +1544,10 @@ asan_poison_object_restore(VALUE obj, void *ptr)
#define RVALUE_PAGE_UNCOLLECTIBLE(page, obj) MARKED_IN_BITMAP((page)->uncollectible_bits, (obj))
#define RVALUE_PAGE_MARKING(page, obj) MARKED_IN_BITMAP((page)->marking_bits, (obj))
-#define RVALUE_OLD_AGE 3
-#define RVALUE_AGE_SHIFT 5 /* FL_PROMOTED0 bit */
-
-static int rgengc_remembered(rb_objspace_t *objspace, VALUE obj);
-static int rgengc_remembered_sweep(rb_objspace_t *objspace, VALUE obj);
static int rgengc_remember(rb_objspace_t *objspace, VALUE obj);
static void rgengc_mark_and_rememberset_clear(rb_objspace_t *objspace, rb_heap_t *heap);
static void rgengc_rememberset_mark(rb_objspace_t *objspace, rb_heap_t *heap);
-static inline int
-RVALUE_FLAGS_AGE(VALUE flags)
-{
- return (int)((flags & (FL_PROMOTED0 | FL_PROMOTED1)) >> RVALUE_AGE_SHIFT);
-}
-
static int
check_rvalue_consistency_force(const VALUE obj, int terminate)
{
@@ -1537,7 +1587,7 @@ check_rvalue_consistency_force(const VALUE obj, int terminate)
const int mark_bit = RVALUE_MARK_BITMAP(obj) != 0;
const int marking_bit = RVALUE_MARKING_BITMAP(obj) != 0;
const int remembered_bit = MARKED_IN_BITMAP(GET_HEAP_PAGE(obj)->remembered_bits, obj) != 0;
- const int age = RVALUE_FLAGS_AGE(RBASIC(obj)->flags);
+ const int age = RVALUE_AGE_GET((VALUE)obj);
if (GET_HEAP_PAGE(obj)->flags.in_tomb) {
fprintf(stderr, "check_rvalue_consistency: %s is in tomb page.\n", obj_info(obj));
@@ -1681,34 +1731,20 @@ RVALUE_UNCOLLECTIBLE(VALUE obj)
}
static inline int
-RVALUE_OLD_P_RAW(VALUE obj)
-{
- const VALUE promoted = FL_PROMOTED0 | FL_PROMOTED1;
- return (RBASIC(obj)->flags & promoted) == promoted;
-}
-
-static inline int
RVALUE_OLD_P(VALUE obj)
{
+ GC_ASSERT(!RB_SPECIAL_CONST_P(obj));
check_rvalue_consistency(obj);
- return RVALUE_OLD_P_RAW(obj);
+ // Because this will only ever be called on GC controlled objects,
+ // we can use the faster _RAW function here
+ return RB_OBJ_PROMOTED_RAW(obj);
}
-#if RGENGC_CHECK_MODE || GC_DEBUG
-static inline int
-RVALUE_AGE(VALUE obj)
-{
- check_rvalue_consistency(obj);
- return RVALUE_FLAGS_AGE(RBASIC(obj)->flags);
-}
-#endif
-
static inline void
RVALUE_PAGE_OLD_UNCOLLECTIBLE_SET(rb_objspace_t *objspace, struct heap_page *page, VALUE obj)
{
MARK_IN_BITMAP(&page->uncollectible_bits[0], obj);
objspace->rgengc.old_objects++;
- rb_transient_heap_promote(obj);
#if RGENGC_PROFILE >= 2
objspace->profile.total_promoted_count++;
@@ -1723,39 +1759,39 @@ RVALUE_OLD_UNCOLLECTIBLE_SET(rb_objspace_t *objspace, VALUE obj)
RVALUE_PAGE_OLD_UNCOLLECTIBLE_SET(objspace, GET_HEAP_PAGE(obj), obj);
}
-static inline VALUE
-RVALUE_FLAGS_AGE_SET(VALUE flags, int age)
-{
- flags &= ~(FL_PROMOTED0 | FL_PROMOTED1);
- flags |= (age << RVALUE_AGE_SHIFT);
- return flags;
-}
-
/* set age to age+1 */
static inline void
RVALUE_AGE_INC(rb_objspace_t *objspace, VALUE obj)
{
- VALUE flags = RBASIC(obj)->flags;
- int age = RVALUE_FLAGS_AGE(flags);
+ int age = RVALUE_AGE_GET((VALUE)obj);
if (RGENGC_CHECK_MODE && age == RVALUE_OLD_AGE) {
rb_bug("RVALUE_AGE_INC: can not increment age of OLD object %s.", obj_info(obj));
}
age++;
- RBASIC(obj)->flags = RVALUE_FLAGS_AGE_SET(flags, age);
+ RVALUE_AGE_SET(obj, age);
if (age == RVALUE_OLD_AGE) {
RVALUE_OLD_UNCOLLECTIBLE_SET(objspace, obj);
}
+
check_rvalue_consistency(obj);
}
static inline void
-RVALUE_DEMOTE_RAW(rb_objspace_t *objspace, VALUE obj)
+RVALUE_AGE_SET_CANDIDATE(rb_objspace_t *objspace, VALUE obj)
{
- RBASIC(obj)->flags = RVALUE_FLAGS_AGE_SET(RBASIC(obj)->flags, 0);
- CLEAR_IN_BITMAP(GET_HEAP_UNCOLLECTIBLE_BITS(obj), obj);
+ check_rvalue_consistency(obj);
+ GC_ASSERT(!RVALUE_OLD_P(obj));
+ RVALUE_AGE_SET(obj, RVALUE_OLD_AGE - 1);
+ check_rvalue_consistency(obj);
+}
+
+static inline void
+RVALUE_AGE_RESET(VALUE obj)
+{
+ RVALUE_AGE_SET(obj, 0);
}
static inline void
@@ -1768,7 +1804,8 @@ RVALUE_DEMOTE(rb_objspace_t *objspace, VALUE obj)
CLEAR_IN_BITMAP(GET_HEAP_PAGE(obj)->remembered_bits, obj);
}
- RVALUE_DEMOTE_RAW(objspace, obj);
+ CLEAR_IN_BITMAP(GET_HEAP_UNCOLLECTIBLE_BITS(obj), obj);
+ RVALUE_AGE_RESET(obj);
if (RVALUE_MARKED(obj)) {
objspace->rgengc.old_objects--;
@@ -1777,22 +1814,6 @@ RVALUE_DEMOTE(rb_objspace_t *objspace, VALUE obj)
check_rvalue_consistency(obj);
}
-static inline void
-RVALUE_AGE_RESET_RAW(VALUE obj)
-{
- RBASIC(obj)->flags = RVALUE_FLAGS_AGE_SET(RBASIC(obj)->flags, 0);
-}
-
-static inline void
-RVALUE_AGE_RESET(VALUE obj)
-{
- check_rvalue_consistency(obj);
- GC_ASSERT(!RVALUE_OLD_P(obj));
-
- RVALUE_AGE_RESET_RAW(obj);
- check_rvalue_consistency(obj);
-}
-
static inline int
RVALUE_BLACK_P(VALUE obj)
{
@@ -1823,27 +1844,68 @@ calloc1(size_t n)
return calloc(1, n);
}
-rb_objspace_t *
-rb_objspace_alloc(void)
+static VALUE initial_stress = Qfalse;
+
+void
+rb_gc_initial_stress_set(VALUE flag)
{
- rb_objspace_t *objspace = calloc1(sizeof(rb_objspace_t));
- objspace->flags.measure_gc = 1;
- malloc_limit = gc_params.malloc_limit_min;
+ initial_stress = flag;
+}
- for (int i = 0; i < SIZE_POOL_COUNT; i++) {
- rb_size_pool_t *size_pool = &size_pools[i];
+static void *rb_gc_impl_objspace_alloc(void);
- size_pool->slot_size = (1 << i) * BASE_SLOT_SIZE;
+#if USE_SHARED_GC
+# include "dln.h"
- ccan_list_head_init(&SIZE_POOL_EDEN_HEAP(size_pool)->pages);
- ccan_list_head_init(&SIZE_POOL_TOMB_HEAP(size_pool)->pages);
+# define RUBY_GC_LIBRARY_PATH "RUBY_GC_LIBRARY_PATH"
+
+void
+ruby_external_gc_init(void)
+{
+ char *gc_so_path = getenv(RUBY_GC_LIBRARY_PATH);
+ void *handle = NULL;
+ if (gc_so_path && dln_supported_p()) {
+ char error[1024];
+ handle = dln_open(gc_so_path, error, sizeof(error));
+ if (!handle) {
+ fprintf(stderr, "%s", error);
+ rb_bug("ruby_external_gc_init: Shared library %s cannot be opened", gc_so_path);
+ }
}
- dont_gc_on();
+# define load_external_gc_func(name) do { \
+ if (handle) { \
+ rb_gc_functions->name = dln_symbol(handle, "rb_gc_impl_" #name); \
+ if (!rb_gc_functions->name) { \
+ rb_bug("ruby_external_gc_init: " #name " func not exported by library %s", gc_so_path); \
+ } \
+ } \
+ else { \
+ rb_gc_functions->name = rb_gc_impl_##name; \
+ } \
+} while (0)
- return objspace;
+ load_external_gc_func(objspace_alloc);
+
+# undef load_external_gc_func
}
+# define rb_gc_impl_objspace_alloc rb_gc_functions->objspace_alloc
+#endif
+
+rb_objspace_t *
+rb_objspace_alloc(void)
+{
+#if USE_SHARED_GC
+ ruby_external_gc_init();
+#endif
+ return (rb_objspace_t *)rb_gc_impl_objspace_alloc();
+}
+
+#if USE_SHARED_GC
+# undef rb_gc_impl_objspace_alloc
+#endif
+
static void free_stack_chunks(mark_stack_t *);
static void mark_stack_free_cache(mark_stack_t *);
static void heap_page_free(rb_objspace_t *objspace, struct heap_page *page);
@@ -1854,21 +1916,13 @@ rb_objspace_free(rb_objspace_t *objspace)
if (is_lazy_sweeping(objspace))
rb_bug("lazy sweeping underway when freeing object space");
- if (objspace->profile.records) {
- free(objspace->profile.records);
- objspace->profile.records = 0;
- }
+ free(objspace->profile.records);
+ objspace->profile.records = NULL;
- if (global_list) {
- struct gc_list *list, *next;
- for (list = global_list; list; list = next) {
- next = list->next;
- xfree(list);
- }
- }
if (heap_pages_sorted) {
size_t i;
- for (i = 0; i < heap_allocated_pages; ++i) {
+ size_t total_heap_pages = heap_allocated_pages;
+ for (i = 0; i < total_heap_pages; ++i) {
heap_page_free(objspace, heap_pages_sorted[i]);
}
free(heap_pages_sorted);
@@ -1889,6 +1943,8 @@ rb_objspace_free(rb_objspace_t *objspace)
free_stack_chunks(&objspace->mark_stack);
mark_stack_free_cache(&objspace->mark_stack);
+ rb_darray_free(objspace->weak_references);
+
free(objspace);
}
@@ -1962,6 +2018,8 @@ heap_page_add_freeobj(rb_objspace_t *objspace, struct heap_page *page, VALUE obj
page->freelist = p;
asan_lock_freelist(page);
+ RVALUE_AGE_RESET(obj);
+
if (RGENGC_CHECK_MODE &&
/* obj should belong to page */
!(page->start <= (uintptr_t)obj &&
@@ -2040,6 +2098,40 @@ heap_page_free(rb_objspace_t *objspace, struct heap_page *page)
free(page);
}
+static void *
+rb_aligned_malloc(size_t alignment, size_t size)
+{
+ /* alignment must be a power of 2 */
+ GC_ASSERT(((alignment - 1) & alignment) == 0);
+ GC_ASSERT(alignment % sizeof(void*) == 0);
+
+ void *res;
+
+#if defined __MINGW32__
+ res = __mingw_aligned_malloc(size, alignment);
+#elif defined _WIN32
+ void *_aligned_malloc(size_t, size_t);
+ res = _aligned_malloc(size, alignment);
+#elif defined(HAVE_POSIX_MEMALIGN)
+ if (posix_memalign(&res, alignment, size) != 0) {
+ return NULL;
+ }
+#elif defined(HAVE_MEMALIGN)
+ res = memalign(alignment, size);
+#else
+ char* aligned;
+ res = malloc(alignment + size + sizeof(void*));
+ aligned = (char*)res + alignment + sizeof(void*);
+ aligned -= ((VALUE)aligned & (alignment - 1));
+ ((void**)aligned)[-1] = res;
+ res = (void*)aligned;
+#endif
+
+ GC_ASSERT((uintptr_t)res % alignment == 0);
+
+ return res;
+}
+
static void
heap_pages_free_unused_pages(rb_objspace_t *objspace)
{
@@ -2054,7 +2146,7 @@ heap_pages_free_unused_pages(rb_objspace_t *objspace)
}
if (has_pages_in_tomb_heap) {
- for (i = j = 1; j < heap_allocated_pages; i++) {
+ for (i = j = 0; j < heap_allocated_pages; i++) {
struct heap_page *page = heap_pages_sorted[i];
if (page->flags.in_tomb && page->free_slots == page->total_slots) {
@@ -2074,6 +2166,11 @@ heap_pages_free_unused_pages(rb_objspace_t *objspace)
GC_ASSERT(himem <= heap_pages_himem);
heap_pages_himem = himem;
+ struct heap_page *lopage = heap_pages_sorted[0];
+ uintptr_t lomem = (uintptr_t)lopage->start;
+ GC_ASSERT(lomem >= heap_pages_lomem);
+ heap_pages_lomem = lomem;
+
GC_ASSERT(j == heap_allocated_pages);
}
}
@@ -2285,6 +2382,7 @@ heap_assign_page(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *
heap_add_freepage(heap, page);
}
+#if GC_CAN_COMPILE_COMPACTION
static void
heap_add_pages(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap, size_t add)
{
@@ -2298,6 +2396,26 @@ heap_add_pages(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *he
GC_ASSERT(size_pool->allocatable_pages == 0);
}
+#endif
+
+static size_t
+slots_to_pages_for_size_pool(rb_objspace_t *objspace, rb_size_pool_t *size_pool, size_t slots)
+{
+ size_t multiple = size_pool->slot_size / BASE_SLOT_SIZE;
+ /* Due to alignment, heap pages may have one less slot. We should
+ * ensure there is enough pages to guarantee that we will have at
+ * least the required number of slots after allocating all the pages. */
+ size_t slots_per_page = (HEAP_PAGE_OBJ_LIMIT / multiple) - 1;
+ return CEILDIV(slots, slots_per_page);
+}
+
+static size_t
+minimum_pages_for_size_pool(rb_objspace_t *objspace, rb_size_pool_t *size_pool)
+{
+ size_t size_pool_idx = size_pool - size_pools;
+ size_t init_slots = gc_params.size_pool_init_slots[size_pool_idx];
+ return slots_to_pages_for_size_pool(objspace, size_pool, init_slots);
+}
static size_t
heap_extend_pages(rb_objspace_t *objspace, rb_size_pool_t *size_pool, size_t free_slots, size_t total_slots, size_t used)
@@ -2309,8 +2427,7 @@ heap_extend_pages(rb_objspace_t *objspace, rb_size_pool_t *size_pool, size_t fre
next_used = (size_t)(used * gc_params.growth_factor);
}
else if (total_slots == 0) {
- int multiple = size_pool->slot_size / BASE_SLOT_SIZE;
- next_used = (gc_params.heap_init_slots * multiple) / HEAP_PAGE_OBJ_LIMIT;
+ next_used = minimum_pages_for_size_pool(objspace, size_pool);
}
else {
/* Find `f' where free_slots = f * total_slots * goal_ratio
@@ -2369,7 +2486,7 @@ gc_continue(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap)
gc_enter(objspace, gc_enter_event_continue, &lock_lev);
/* Continue marking if in incremental marking. */
- if (heap->free_pages == NULL && is_incremental_marking(objspace)) {
+ if (is_incremental_marking(objspace)) {
if (gc_marks_continue(objspace, size_pool, heap)) {
gc_sweep(objspace);
}
@@ -2408,7 +2525,7 @@ heap_prepare(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap
* sweeping and still don't have a free page, then
* gc_sweep_finish_size_pool should allow us to create a new page. */
if (heap->free_pages == NULL && !heap_increment(objspace, size_pool, heap)) {
- if (objspace->rgengc.need_major_gc == GPR_FLAG_NONE) {
+ if (gc_needs_major_flags == GPR_FLAG_NONE) {
rb_bug("cannot create a new page after GC");
}
else { // Major GC is required, which will allow us to create new page
@@ -2437,52 +2554,17 @@ rb_objspace_set_event_hook(const rb_event_flag_t event)
{
rb_objspace_t *objspace = &rb_objspace;
objspace->hook_events = event & RUBY_INTERNAL_EVENT_OBJSPACE_MASK;
- objspace->flags.has_hook = (objspace->hook_events != 0);
+ objspace->flags.has_newobj_hook = !!(objspace->hook_events & RUBY_INTERNAL_EVENT_NEWOBJ);
}
static void
gc_event_hook_body(rb_execution_context_t *ec, rb_objspace_t *objspace, const rb_event_flag_t event, VALUE data)
{
if (UNLIKELY(!ec->cfp)) return;
- const VALUE *pc = ec->cfp->pc;
- if (pc && VM_FRAME_RUBYFRAME_P(ec->cfp)) {
- int prev_opcode = rb_vm_insn_addr2opcode((void *)*ec->cfp->iseq->body->iseq_encoded);
- for (const VALUE *insn = ec->cfp->iseq->body->iseq_encoded; insn < pc; insn += rb_insn_len(prev_opcode)) {
- prev_opcode = rb_vm_insn_addr2opcode((void *)*insn);
- }
-
- /* If the previous instruction is a leaf instruction, then the PC is
- * the currently executing instruction. We should increment the PC
- * because the source line is calculated with PC-1 in calc_pos.
- *
- * If the previous instruction is not a leaf instruction and the
- * current instruction is not a leaf instruction, then the PC was
- * incremented before the instruction was ran (meaning the currently
- * executing instruction is actually the previous instruction), so we
- * should not increment the PC otherwise we will calculate the source
- * line for the next instruction.
- *
- * However, this implementation still has a bug. Consider the
- * following situation:
- *
- * non-leaf
- * leaf <-
- *
- * Where the PC currently points to a leaf instruction. We don't know
- * which instruction we really are at since we could be at the non-leaf
- * instruction (since it incremented the PC before executing the
- * instruction). We could also be at the leaf instruction since the PC
- * doesn't get incremented until the instruction finishes.
- */
- if (rb_insns_leaf_p(prev_opcode)) {
- ec->cfp->pc++;
- }
- }
EXEC_EVENT_HOOK(ec, event, ec->cfp->self, 0, 0, 0, data);
- ec->cfp->pc = pc;
}
-#define gc_event_hook_available_p(objspace) ((objspace)->flags.has_hook)
+#define gc_event_newobj_hook_needed_p(objspace) ((objspace)->flags.has_newobj_hook)
#define gc_event_hook_needed_p(objspace, event) ((objspace)->hook_events & (event))
#define gc_event_hook_prep(objspace, event, data, prep) do { \
@@ -2505,6 +2587,11 @@ newobj_init(VALUE klass, VALUE flags, int wb_protected, rb_objspace_t *objspace,
p->as.basic.flags = flags;
*((VALUE *)&p->as.basic.klass) = klass;
+ int t = flags & RUBY_T_MASK;
+ if (t == T_CLASS || t == T_MODULE || t == T_ICLASS) {
+ RVALUE_AGE_SET_CANDIDATE(objspace, obj);
+ }
+
#if RACTOR_CHECK_MODE
rb_ractor_setup_belonging(obj);
#endif
@@ -2521,13 +2608,7 @@ newobj_init(VALUE klass, VALUE flags, int wb_protected, rb_objspace_t *objspace,
GC_ASSERT(RVALUE_OLD_P(obj) == FALSE);
GC_ASSERT(RVALUE_WB_UNPROTECTED(obj) == FALSE);
- if (flags & FL_PROMOTED1) {
- if (RVALUE_AGE(obj) != 2) rb_bug("newobj: %s of age (%d) != 2.", obj_info(obj), RVALUE_AGE(obj));
- }
- else {
- if (RVALUE_AGE(obj) > 0) rb_bug("newobj: %s of age (%d) > 0.", obj_info(obj), RVALUE_AGE(obj));
- }
- if (rgengc_remembered(objspace, (VALUE)obj)) rb_bug("newobj: %s is remembered.", obj_info(obj));
+ if (RVALUE_REMEMBERED((VALUE)obj)) rb_bug("newobj: %s is remembered.", obj_info(obj));
}
RB_VM_LOCK_LEAVE_NO_BARRIER();
#endif
@@ -2537,9 +2618,6 @@ newobj_init(VALUE klass, VALUE flags, int wb_protected, rb_objspace_t *objspace,
MARK_IN_BITMAP(GET_HEAP_WB_UNPROTECTED_BITS(obj), obj);
}
- // TODO: make it atomic, or ractor local
- objspace->total_allocated_objects++;
-
#if RGENGC_PROFILE
if (wb_protected) {
objspace->profile.total_generated_normal_object_count++;
@@ -2560,26 +2638,8 @@ newobj_init(VALUE klass, VALUE flags, int wb_protected, rb_objspace_t *objspace,
GC_ASSERT(!SPECIAL_CONST_P(obj)); /* check alignment */
#endif
- gc_report(5, objspace, "newobj: %s\n", obj_info(obj));
-
-#if RGENGC_OLD_NEWOBJ_CHECK > 0
- {
- static int newobj_cnt = RGENGC_OLD_NEWOBJ_CHECK;
-
- if (!is_incremental_marking(objspace) &&
- flags & FL_WB_PROTECTED && /* do not promote WB unprotected objects */
- ! RB_TYPE_P(obj, T_ARRAY)) { /* array.c assumes that allocated objects are new */
- if (--newobj_cnt == 0) {
- newobj_cnt = RGENGC_OLD_NEWOBJ_CHECK;
-
- gc_mark_set(objspace, obj);
- RVALUE_AGE_SET_OLD(objspace, obj);
+ gc_report(5, objspace, "newobj: %s\n", obj_info_basic(obj));
- rb_gc_writebarrier_remember(obj);
- }
- }
- }
-#endif
// RUBY_DEBUG_LOG("obj:%p (%s)", (void *)obj, obj_type_name(obj));
return obj;
}
@@ -2607,18 +2667,50 @@ size_pool_slot_size(unsigned char pool_id)
return slot_size;
}
-size_t
-rb_size_pool_slot_size(unsigned char pool_id)
-{
- return size_pool_slot_size(pool_id);
-}
-
bool
rb_gc_size_allocatable_p(size_t size)
{
return size <= size_pool_slot_size(SIZE_POOL_COUNT - 1);
}
+static size_t size_pool_sizes[SIZE_POOL_COUNT + 1] = { 0 };
+
+size_t *
+rb_gc_size_pool_sizes(void)
+{
+ if (size_pool_sizes[0] == 0) {
+ for (unsigned char i = 0; i < SIZE_POOL_COUNT; i++) {
+ size_pool_sizes[i] = size_pool_slot_size(i);
+ }
+ }
+
+ return size_pool_sizes;
+}
+
+size_t
+rb_gc_size_pool_id_for_size(size_t size)
+{
+ size += RVALUE_OVERHEAD;
+
+ size_t slot_count = CEILDIV(size, BASE_SLOT_SIZE);
+
+ /* size_pool_idx is ceil(log2(slot_count)) */
+ size_t size_pool_idx = 64 - nlz_int64(slot_count - 1);
+
+ if (size_pool_idx >= SIZE_POOL_COUNT) {
+ rb_bug("rb_gc_size_pool_id_for_size: allocation size too large "
+ "(size=%"PRIuSIZE"u, size_pool_idx=%"PRIuSIZE"u)", size, size_pool_idx);
+ }
+
+#if RGENGC_CHECK_MODE
+ rb_objspace_t *objspace = &rb_objspace;
+ GC_ASSERT(size <= (size_t)size_pools[size_pool_idx].slot_size);
+ if (size_pool_idx > 0) GC_ASSERT(size > (size_t)size_pools[size_pool_idx - 1].slot_size);
+#endif
+
+ return size_pool_idx;
+}
+
static inline VALUE
ractor_cache_allocate_slot(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache,
size_t size_pool_idx)
@@ -2708,35 +2800,11 @@ newobj_fill(VALUE obj, VALUE v1, VALUE v2, VALUE v3)
return obj;
}
-static inline size_t
-size_pool_idx_for_size(size_t size)
-{
- size += RVALUE_OVERHEAD;
-
- size_t slot_count = CEILDIV(size, BASE_SLOT_SIZE);
-
- /* size_pool_idx is ceil(log2(slot_count)) */
- size_t size_pool_idx = 64 - nlz_int64(slot_count - 1);
-
- if (size_pool_idx >= SIZE_POOL_COUNT) {
- rb_bug("size_pool_idx_for_size: allocation size too large");
- }
-
-#if RGENGC_CHECK_MODE
- rb_objspace_t *objspace = &rb_objspace;
- GC_ASSERT(size <= (size_t)size_pools[size_pool_idx].slot_size);
- if (size_pool_idx > 0) GC_ASSERT(size > (size_t)size_pools[size_pool_idx - 1].slot_size);
-#endif
-
- return size_pool_idx;
-}
-
static VALUE
-newobj_alloc(rb_objspace_t *objspace, rb_ractor_t *cr, size_t size_pool_idx, bool vm_locked)
+newobj_alloc(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t size_pool_idx, bool vm_locked)
{
rb_size_pool_t *size_pool = &size_pools[size_pool_idx];
rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool);
- rb_ractor_newobj_cache_t *cache = &cr->newobj_cache;
VALUE obj = ractor_cache_allocate_slot(objspace, cache, size_pool_idx);
@@ -2745,7 +2813,7 @@ newobj_alloc(rb_objspace_t *objspace, rb_ractor_t *cr, size_t size_pool_idx, boo
bool unlock_vm = false;
if (!vm_locked) {
- RB_VM_LOCK_ENTER_CR_LEV(cr, &lev);
+ RB_VM_LOCK_ENTER_CR_LEV(GET_RACTOR(), &lev);
vm_locked = true;
unlock_vm = true;
}
@@ -2774,10 +2842,12 @@ newobj_alloc(rb_objspace_t *objspace, rb_ractor_t *cr, size_t size_pool_idx, boo
}
if (unlock_vm) {
- RB_VM_LOCK_LEAVE_CR_LEV(cr, &lev);
+ RB_VM_LOCK_LEAVE_CR_LEV(GET_RACTOR(), &lev);
}
}
+ size_pool->total_allocated_objects++;
+
return obj;
}
@@ -2787,15 +2857,15 @@ newobj_zero_slot(VALUE obj)
memset((char *)obj + sizeof(struct RBasic), 0, rb_gc_obj_slot_size(obj) - sizeof(struct RBasic));
}
-ALWAYS_INLINE(static VALUE newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_t *cr, int wb_protected, size_t size_pool_idx));
+ALWAYS_INLINE(static VALUE newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, int wb_protected, size_t size_pool_idx));
static inline VALUE
-newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_t *cr, int wb_protected, size_t size_pool_idx)
+newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, int wb_protected, size_t size_pool_idx)
{
VALUE obj;
unsigned int lev;
- RB_VM_LOCK_ENTER_CR_LEV(cr, &lev);
+ RB_VM_LOCK_ENTER_CR_LEV(GET_RACTOR(), &lev);
{
if (UNLIKELY(during_gc || ruby_gc_stressful)) {
if (during_gc) {
@@ -2811,35 +2881,35 @@ newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_t *
}
}
- obj = newobj_alloc(objspace, cr, size_pool_idx, true);
+ obj = newobj_alloc(objspace, cache, size_pool_idx, true);
newobj_init(klass, flags, wb_protected, objspace, obj);
gc_event_hook_prep(objspace, RUBY_INTERNAL_EVENT_NEWOBJ, obj, newobj_zero_slot(obj));
}
- RB_VM_LOCK_LEAVE_CR_LEV(cr, &lev);
+ RB_VM_LOCK_LEAVE_CR_LEV(GET_RACTOR(), &lev);
return obj;
}
NOINLINE(static VALUE newobj_slowpath_wb_protected(VALUE klass, VALUE flags,
- rb_objspace_t *objspace, rb_ractor_t *cr, size_t size_pool_idx));
+ rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t size_pool_idx));
NOINLINE(static VALUE newobj_slowpath_wb_unprotected(VALUE klass, VALUE flags,
- rb_objspace_t *objspace, rb_ractor_t *cr, size_t size_pool_idx));
+ rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t size_pool_idx));
static VALUE
-newobj_slowpath_wb_protected(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_t *cr, size_t size_pool_idx)
+newobj_slowpath_wb_protected(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t size_pool_idx)
{
- return newobj_slowpath(klass, flags, objspace, cr, TRUE, size_pool_idx);
+ return newobj_slowpath(klass, flags, objspace, cache, TRUE, size_pool_idx);
}
static VALUE
-newobj_slowpath_wb_unprotected(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_t *cr, size_t size_pool_idx)
+newobj_slowpath_wb_unprotected(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t size_pool_idx)
{
- return newobj_slowpath(klass, flags, objspace, cr, FALSE, size_pool_idx);
+ return newobj_slowpath(klass, flags, objspace, cache, FALSE, size_pool_idx);
}
static inline VALUE
-newobj_of0(VALUE klass, VALUE flags, int wb_protected, rb_ractor_t *cr, size_t alloc_size)
+newobj_of(rb_ractor_t *cr, VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3, int wb_protected, size_t alloc_size)
{
VALUE obj;
rb_objspace_t *objspace = &rb_objspace;
@@ -2847,43 +2917,32 @@ newobj_of0(VALUE klass, VALUE flags, int wb_protected, rb_ractor_t *cr, size_t a
RB_DEBUG_COUNTER_INC(obj_newobj);
(void)RB_DEBUG_COUNTER_INC_IF(obj_newobj_wb_unprotected, !wb_protected);
-#if GC_DEBUG_STRESS_TO_CLASS
if (UNLIKELY(stress_to_class)) {
long i, cnt = RARRAY_LEN(stress_to_class);
for (i = 0; i < cnt; ++i) {
if (klass == RARRAY_AREF(stress_to_class, i)) rb_memerror();
}
}
-#endif
- size_t size_pool_idx = size_pool_idx_for_size(alloc_size);
+ size_t size_pool_idx = rb_gc_size_pool_id_for_size(alloc_size);
- if (SHAPE_IN_BASIC_FLAGS || (flags & RUBY_T_MASK) == T_OBJECT) {
- flags |= (VALUE)size_pool_idx << SHAPE_FLAG_SHIFT;
- }
+ rb_ractor_newobj_cache_t *cache = &cr->newobj_cache;
if (!UNLIKELY(during_gc ||
ruby_gc_stressful ||
- gc_event_hook_available_p(objspace)) &&
+ gc_event_newobj_hook_needed_p(objspace)) &&
wb_protected) {
- obj = newobj_alloc(objspace, cr, size_pool_idx, false);
+ obj = newobj_alloc(objspace, cache, size_pool_idx, false);
newobj_init(klass, flags, wb_protected, objspace, obj);
}
else {
RB_DEBUG_COUNTER_INC(obj_newobj_slowpath);
obj = wb_protected ?
- newobj_slowpath_wb_protected(klass, flags, objspace, cr, size_pool_idx) :
- newobj_slowpath_wb_unprotected(klass, flags, objspace, cr, size_pool_idx);
+ newobj_slowpath_wb_protected(klass, flags, objspace, cache, size_pool_idx) :
+ newobj_slowpath_wb_unprotected(klass, flags, objspace, cache, size_pool_idx);
}
- return obj;
-}
-
-static inline VALUE
-newobj_of(rb_ractor_t *cr, VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3, int wb_protected, size_t alloc_size)
-{
- VALUE obj = newobj_of0(klass, flags, wb_protected, cr, alloc_size);
return newobj_fill(obj, v1, v2, v3);
}
@@ -2901,173 +2960,10 @@ rb_wb_protected_newobj_of(rb_execution_context_t *ec, VALUE klass, VALUE flags,
return newobj_of(rb_ec_ractor_ptr(ec), klass, flags, 0, 0, 0, TRUE, size);
}
-/* for compatibility */
-
-VALUE
-rb_newobj(void)
-{
- return newobj_of(GET_RACTOR(), 0, T_NONE, 0, 0, 0, FALSE, RVALUE_SIZE);
-}
-
-static size_t
-rb_obj_embedded_size(uint32_t numiv)
-{
- return offsetof(struct RObject, as.ary) + (sizeof(VALUE) * numiv);
-}
-
-static VALUE
-rb_class_instance_allocate_internal(VALUE klass, VALUE flags, bool wb_protected)
-{
- GC_ASSERT((flags & RUBY_T_MASK) == T_OBJECT);
- GC_ASSERT(flags & ROBJECT_EMBED);
-
- size_t size;
- uint32_t index_tbl_num_entries = RCLASS_EXT(klass)->max_iv_count;
-
- size = rb_obj_embedded_size(index_tbl_num_entries);
- if (!rb_gc_size_allocatable_p(size)) {
- size = sizeof(struct RObject);
- }
-
- VALUE obj = newobj_of(GET_RACTOR(), klass, flags, 0, 0, 0, wb_protected, size);
- RUBY_ASSERT(rb_shape_get_shape(obj)->type == SHAPE_ROOT ||
- rb_shape_get_shape(obj)->type == SHAPE_INITIAL_CAPACITY);
-
- // Set the shape to the specific T_OBJECT shape which is always
- // SIZE_POOL_COUNT away from the root shape.
- ROBJECT_SET_SHAPE_ID(obj, ROBJECT_SHAPE_ID(obj) + SIZE_POOL_COUNT);
-
-#if RUBY_DEBUG
- RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
- VALUE *ptr = ROBJECT_IVPTR(obj);
- for (size_t i = 0; i < ROBJECT_IV_CAPACITY(obj); i++) {
- ptr[i] = Qundef;
- }
-#endif
-
- return obj;
-}
-
-VALUE
-rb_newobj_of(VALUE klass, VALUE flags)
-{
- if ((flags & RUBY_T_MASK) == T_OBJECT) {
- return rb_class_instance_allocate_internal(klass, (flags | ROBJECT_EMBED) & ~FL_WB_PROTECTED, flags & FL_WB_PROTECTED);
- }
- else {
- return newobj_of(GET_RACTOR(), klass, flags & ~FL_WB_PROTECTED, 0, 0, 0, flags & FL_WB_PROTECTED, RVALUE_SIZE);
- }
-}
-
#define UNEXPECTED_NODE(func) \
rb_bug(#func"(): GC does not handle T_NODE 0x%x(%p) 0x%"PRIxVALUE, \
BUILTIN_TYPE(obj), (void*)(obj), RBASIC(obj)->flags)
-const char *
-rb_imemo_name(enum imemo_type type)
-{
- // put no default case to get a warning if an imemo type is missing
- switch (type) {
-#define IMEMO_NAME(x) case imemo_##x: return #x;
- IMEMO_NAME(env);
- IMEMO_NAME(cref);
- IMEMO_NAME(svar);
- IMEMO_NAME(throw_data);
- IMEMO_NAME(ifunc);
- IMEMO_NAME(memo);
- IMEMO_NAME(ment);
- IMEMO_NAME(iseq);
- IMEMO_NAME(tmpbuf);
- IMEMO_NAME(ast);
- IMEMO_NAME(parser_strterm);
- IMEMO_NAME(callinfo);
- IMEMO_NAME(callcache);
- IMEMO_NAME(constcache);
-#undef IMEMO_NAME
- }
- return "unknown";
-}
-
-#undef rb_imemo_new
-
-VALUE
-rb_imemo_new(enum imemo_type type, VALUE v1, VALUE v2, VALUE v3, VALUE v0)
-{
- size_t size = RVALUE_SIZE;
- VALUE flags = T_IMEMO | (type << FL_USHIFT);
- return newobj_of(GET_RACTOR(), v0, flags, v1, v2, v3, TRUE, size);
-}
-
-static VALUE
-rb_imemo_tmpbuf_new(VALUE v1, VALUE v2, VALUE v3, VALUE v0)
-{
- size_t size = sizeof(struct rb_imemo_tmpbuf_struct);
- VALUE flags = T_IMEMO | (imemo_tmpbuf << FL_USHIFT);
- return newobj_of(GET_RACTOR(), v0, flags, v1, v2, v3, FALSE, size);
-}
-
-static VALUE
-rb_imemo_tmpbuf_auto_free_maybe_mark_buffer(void *buf, size_t cnt)
-{
- return rb_imemo_tmpbuf_new((VALUE)buf, 0, (VALUE)cnt, 0);
-}
-
-rb_imemo_tmpbuf_t *
-rb_imemo_tmpbuf_parser_heap(void *buf, rb_imemo_tmpbuf_t *old_heap, size_t cnt)
-{
- return (rb_imemo_tmpbuf_t *)rb_imemo_tmpbuf_new((VALUE)buf, (VALUE)old_heap, (VALUE)cnt, 0);
-}
-
-static size_t
-imemo_memsize(VALUE obj)
-{
- size_t size = 0;
- switch (imemo_type(obj)) {
- case imemo_ment:
- size += sizeof(RANY(obj)->as.imemo.ment.def);
- break;
- case imemo_iseq:
- size += rb_iseq_memsize((rb_iseq_t *)obj);
- break;
- case imemo_env:
- size += RANY(obj)->as.imemo.env.env_size * sizeof(VALUE);
- break;
- case imemo_tmpbuf:
- size += RANY(obj)->as.imemo.alloc.cnt * sizeof(VALUE);
- break;
- case imemo_ast:
- size += rb_ast_memsize(&RANY(obj)->as.imemo.ast);
- break;
- case imemo_cref:
- case imemo_svar:
- case imemo_throw_data:
- case imemo_ifunc:
- case imemo_memo:
- case imemo_parser_strterm:
- break;
- default:
- /* unreachable */
- break;
- }
- return size;
-}
-
-#if IMEMO_DEBUG
-VALUE
-rb_imemo_new_debug(enum imemo_type type, VALUE v1, VALUE v2, VALUE v3, VALUE v0, const char *file, int line)
-{
- VALUE memo = rb_imemo_new(type, v1, v2, v3, v0);
- fprintf(stderr, "memo %p (type: %d) @ %s:%d\n", (void *)memo, imemo_type(memo), file, line);
- return memo;
-}
-#endif
-
-VALUE
-rb_class_allocate_instance(VALUE klass)
-{
- return rb_class_instance_allocate_internal(klass, T_OBJECT | ROBJECT_EMBED, RGENGC_WB_PROTECTED_OBJECT);
-}
-
static inline void
rb_data_object_check(VALUE klass)
{
@@ -3093,34 +2989,66 @@ rb_data_object_zalloc(VALUE klass, size_t size, RUBY_DATA_FUNC dmark, RUBY_DATA_
return obj;
}
-VALUE
-rb_data_typed_object_wrap(VALUE klass, void *datap, const rb_data_type_t *type)
+static VALUE
+typed_data_alloc(VALUE klass, VALUE typed_flag, void *datap, const rb_data_type_t *type, size_t size)
{
RBIMPL_NONNULL_ARG(type);
if (klass) rb_data_object_check(klass);
bool wb_protected = (type->flags & RUBY_FL_WB_PROTECTED) || !type->function.dmark;
- return newobj_of(GET_RACTOR(), klass, T_DATA, (VALUE)type, (VALUE)1, (VALUE)datap, wb_protected, sizeof(struct RTypedData));
+ return newobj_of(GET_RACTOR(), klass, T_DATA, (VALUE)type, 1 | typed_flag, (VALUE)datap, wb_protected, size);
+}
+
+VALUE
+rb_data_typed_object_wrap(VALUE klass, void *datap, const rb_data_type_t *type)
+{
+ if (UNLIKELY(type->flags & RUBY_TYPED_EMBEDDABLE)) {
+ rb_raise(rb_eTypeError, "Cannot wrap an embeddable TypedData");
+ }
+
+ return typed_data_alloc(klass, 0, datap, type, sizeof(struct RTypedData));
}
VALUE
rb_data_typed_object_zalloc(VALUE klass, size_t size, const rb_data_type_t *type)
{
- VALUE obj = rb_data_typed_object_wrap(klass, 0, type);
+ if (type->flags & RUBY_TYPED_EMBEDDABLE) {
+ if (!(type->flags & RUBY_TYPED_FREE_IMMEDIATELY)) {
+ rb_raise(rb_eTypeError, "Embeddable TypedData must be freed immediately");
+ }
+
+ size_t embed_size = offsetof(struct RTypedData, data) + size;
+ if (rb_gc_size_allocatable_p(embed_size)) {
+ VALUE obj = typed_data_alloc(klass, TYPED_DATA_EMBEDDED, 0, type, embed_size);
+ memset((char *)obj + offsetof(struct RTypedData, data), 0, size);
+ return obj;
+ }
+ }
+
+ VALUE obj = typed_data_alloc(klass, 0, NULL, type, sizeof(struct RTypedData));
DATA_PTR(obj) = xcalloc(1, size);
return obj;
}
-size_t
+static size_t
rb_objspace_data_type_memsize(VALUE obj)
{
+ size_t size = 0;
if (RTYPEDDATA_P(obj)) {
const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
- const void *ptr = RTYPEDDATA_DATA(obj);
+ const void *ptr = RTYPEDDATA_GET_DATA(obj);
+
+ if (RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_EMBEDDABLE && !RTYPEDDATA_EMBEDDED_P(obj)) {
+#ifdef HAVE_MALLOC_USABLE_SIZE
+ size += malloc_usable_size((void *)ptr);
+#endif
+ }
+
if (ptr && type->function.dsize) {
- return type->function.dsize(ptr);
+ size += type->function.dsize(ptr);
}
}
- return 0;
+
+ return size;
}
const char *
@@ -3171,9 +3099,9 @@ heap_page_for_ptr(rb_objspace_t *objspace, uintptr_t ptr)
}
}
-PUREFUNC(static inline int is_pointer_to_heap(rb_objspace_t *objspace, void *ptr);)
+PUREFUNC(static inline int is_pointer_to_heap(rb_objspace_t *objspace, const void *ptr);)
static inline int
-is_pointer_to_heap(rb_objspace_t *objspace, void *ptr)
+is_pointer_to_heap(rb_objspace_t *objspace, const void *ptr)
{
register uintptr_t p = (uintptr_t)ptr;
register struct heap_page *page;
@@ -3204,149 +3132,19 @@ is_pointer_to_heap(rb_objspace_t *objspace, void *ptr)
}
static enum rb_id_table_iterator_result
-free_const_entry_i(VALUE value, void *data)
+cvar_table_free_i(VALUE value, void *ctx)
{
- rb_const_entry_t *ce = (rb_const_entry_t *)value;
- xfree(ce);
+ xfree((void *)value);
return ID_TABLE_CONTINUE;
}
-void
-rb_free_const_table(struct rb_id_table *tbl)
-{
- rb_id_table_foreach_values(tbl, free_const_entry_i, 0);
- rb_id_table_free(tbl);
-}
-
-// alive: if false, target pointers can be freed already.
-// To check it, we need objspace parameter.
-static void
-vm_ccs_free(struct rb_class_cc_entries *ccs, int alive, rb_objspace_t *objspace, VALUE klass)
-{
- if (ccs->entries) {
- for (int i=0; i<ccs->len; i++) {
- const struct rb_callcache *cc = ccs->entries[i].cc;
- if (!alive) {
- void *ptr = asan_unpoison_object_temporary((VALUE)cc);
- // ccs can be free'ed.
- if (is_pointer_to_heap(objspace, (void *)cc) &&
- IMEMO_TYPE_P(cc, imemo_callcache) &&
- cc->klass == klass) {
- // OK. maybe target cc.
- }
- else {
- if (ptr) {
- asan_poison_object((VALUE)cc);
- }
- continue;
- }
- if (ptr) {
- asan_poison_object((VALUE)cc);
- }
- }
- vm_cc_invalidate(cc);
- }
- ruby_xfree(ccs->entries);
- }
- ruby_xfree(ccs);
-}
-
-void
-rb_vm_ccs_free(struct rb_class_cc_entries *ccs)
-{
- RB_DEBUG_COUNTER_INC(ccs_free);
- vm_ccs_free(ccs, TRUE, NULL, Qundef);
-}
-
-struct cc_tbl_i_data {
- rb_objspace_t *objspace;
- VALUE klass;
- bool alive;
-};
-
-static enum rb_id_table_iterator_result
-cc_table_mark_i(ID id, VALUE ccs_ptr, void *data_ptr)
-{
- struct cc_tbl_i_data *data = data_ptr;
- struct rb_class_cc_entries *ccs = (struct rb_class_cc_entries *)ccs_ptr;
- VM_ASSERT(vm_ccs_p(ccs));
- VM_ASSERT(id == ccs->cme->called_id);
-
- if (METHOD_ENTRY_INVALIDATED(ccs->cme)) {
- rb_vm_ccs_free(ccs);
- return ID_TABLE_DELETE;
- }
- else {
- gc_mark(data->objspace, (VALUE)ccs->cme);
-
- for (int i=0; i<ccs->len; i++) {
- VM_ASSERT(data->klass == ccs->entries[i].cc->klass);
- VM_ASSERT(vm_cc_check_cme(ccs->entries[i].cc, ccs->cme));
-
- gc_mark(data->objspace, (VALUE)ccs->entries[i].ci);
- gc_mark(data->objspace, (VALUE)ccs->entries[i].cc);
- }
- return ID_TABLE_CONTINUE;
- }
-}
-
-static void
-cc_table_mark(rb_objspace_t *objspace, VALUE klass)
-{
- struct rb_id_table *cc_tbl = RCLASS_CC_TBL(klass);
- if (cc_tbl) {
- struct cc_tbl_i_data data = {
- .objspace = objspace,
- .klass = klass,
- };
- rb_id_table_foreach(cc_tbl, cc_table_mark_i, &data);
- }
-}
-
-static enum rb_id_table_iterator_result
-cc_table_free_i(VALUE ccs_ptr, void *data_ptr)
-{
- struct cc_tbl_i_data *data = data_ptr;
- struct rb_class_cc_entries *ccs = (struct rb_class_cc_entries *)ccs_ptr;
- VM_ASSERT(vm_ccs_p(ccs));
- vm_ccs_free(ccs, data->alive, data->objspace, data->klass);
- return ID_TABLE_CONTINUE;
-}
-
-static void
-cc_table_free(rb_objspace_t *objspace, VALUE klass, bool alive)
-{
- struct rb_id_table *cc_tbl = RCLASS_CC_TBL(klass);
-
- if (cc_tbl) {
- struct cc_tbl_i_data data = {
- .objspace = objspace,
- .klass = klass,
- .alive = alive,
- };
- rb_id_table_foreach_values(cc_tbl, cc_table_free_i, &data);
- rb_id_table_free(cc_tbl);
- }
-}
-
-static enum rb_id_table_iterator_result
-cvar_table_free_i(VALUE value, void * ctx)
-{
- xfree((void *) value);
- return ID_TABLE_CONTINUE;
-}
-
-void
-rb_cc_table_free(VALUE klass)
-{
- cc_table_free(&rb_objspace, klass, TRUE);
-}
+#define ZOMBIE_OBJ_KEPT_FLAGS (FL_SEEN_OBJ_ID | FL_FINALIZE)
static inline void
make_zombie(rb_objspace_t *objspace, VALUE obj, void (*dfree)(void *), void *data)
{
struct RZombie *zombie = RZOMBIE(obj);
- zombie->basic.flags = T_ZOMBIE | (zombie->basic.flags & FL_SEEN_OBJ_ID);
+ zombie->basic.flags = T_ZOMBIE | (zombie->basic.flags & ZOMBIE_OBJ_KEPT_FLAGS);
zombie->dfree = dfree;
zombie->data = data;
VALUE prev, next = heap_pages_deferred_final;
@@ -3388,10 +3186,10 @@ obj_free_object_id(rb_objspace_t *objspace, VALUE obj)
static bool
rb_data_free(rb_objspace_t *objspace, VALUE obj)
{
- if (DATA_PTR(obj)) {
+ void *data = RTYPEDDATA_P(obj) ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj);
+ if (data) {
int free_immediately = false;
void (*dfree)(void *);
- void *data = DATA_PTR(obj);
if (RTYPEDDATA_P(obj)) {
free_immediately = (RANY(obj)->as.typeddata.type->flags & RUBY_TYPED_FREE_IMMEDIATELY) != 0;
@@ -3403,17 +3201,23 @@ rb_data_free(rb_objspace_t *objspace, VALUE obj)
if (dfree) {
if (dfree == RUBY_DEFAULT_FREE) {
- xfree(data);
- RB_DEBUG_COUNTER_INC(obj_data_xfree);
+ if (!RTYPEDDATA_EMBEDDED_P(obj)) {
+ xfree(data);
+ RB_DEBUG_COUNTER_INC(obj_data_xfree);
+ }
}
else if (free_immediately) {
(*dfree)(data);
+ if (RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_EMBEDDABLE && !RTYPEDDATA_EMBEDDED_P(obj)) {
+ xfree(data);
+ }
+
RB_DEBUG_COUNTER_INC(obj_data_imm_free);
}
else {
- RB_DEBUG_COUNTER_INC(obj_data_zombie);
make_zombie(objspace, obj, dfree, data);
- return false;
+ RB_DEBUG_COUNTER_INC(obj_data_zombie);
+ return FALSE;
}
}
else {
@@ -3472,9 +3276,6 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
else if (RANY(obj)->as.basic.flags & ROBJECT_EMBED) {
RB_DEBUG_COUNTER_INC(obj_obj_embed);
}
- else if (ROBJ_TRANSIENT_P(obj)) {
- RB_DEBUG_COUNTER_INC(obj_obj_transient);
- }
else {
xfree(RANY(obj)->as.object.as.heap.ivptr);
RB_DEBUG_COUNTER_INC(obj_obj_ptr);
@@ -3483,10 +3284,14 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
case T_MODULE:
case T_CLASS:
rb_id_table_free(RCLASS_M_TBL(obj));
- cc_table_free(objspace, obj, FALSE);
- if (RCLASS_IVPTR(obj)) {
+ rb_cc_table_free(obj);
+ if (rb_shape_obj_too_complex(obj)) {
+ st_free_table((st_table *)RCLASS_IVPTR(obj));
+ }
+ else {
xfree(RCLASS_IVPTR(obj));
}
+
if (RCLASS_CONST_TBL(obj)) {
rb_free_const_table(RCLASS_CONST_TBL(obj));
}
@@ -3552,12 +3357,7 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
}
#endif
- if (RHASH_ST_TABLE_P(obj)) {
- st_table *tab = RHASH_ST_TABLE(obj);
-
- if (tab->bins != NULL) free(tab->bins);
- free(tab->entries);
- }
+ rb_hash_free(obj);
break;
case T_REGEXP:
if (RANY(obj)->as.regexp.ptr) {
@@ -3569,8 +3369,8 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
if (!rb_data_free(objspace, obj)) return false;
break;
case T_MATCH:
- if (RANY(obj)->as.match.rmatch) {
- struct rmatch *rm = RANY(obj)->as.match.rmatch;
+ {
+ rb_matchext_t *rm = RMATCH_EXT(obj);
#if USE_DEBUG_COUNTER
if (rm->regs.num_regs >= 8) {
RB_DEBUG_COUNTER_INC(obj_match_ge8);
@@ -3583,9 +3383,7 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
}
#endif
onig_region_free(&rm->regs, 0);
- if (rm->char_offset)
- xfree(rm->char_offset);
- xfree(rm);
+ xfree(rm->char_offset);
RB_DEBUG_COUNTER_INC(obj_match_ptr);
}
@@ -3615,7 +3413,7 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
rb_id_table_free(RCLASS_CALLABLE_M_TBL(obj));
}
rb_class_remove_subclass_head(obj);
- cc_table_free(objspace, obj, FALSE);
+ rb_cc_table_free(obj);
rb_class_remove_from_module_subclasses(obj);
rb_class_remove_from_super_subclasses(obj);
@@ -3645,9 +3443,6 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
RANY(obj)->as.rstruct.as.heap.ptr == NULL) {
RB_DEBUG_COUNTER_INC(obj_struct_embed);
}
- else if (RSTRUCT_TRANSIENT_P(obj)) {
- RB_DEBUG_COUNTER_INC(obj_struct_transient);
- }
else {
xfree((void *)RANY(obj)->as.rstruct.as.heap.ptr);
RB_DEBUG_COUNTER_INC(obj_struct_ptr);
@@ -3662,57 +3457,8 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
break;
case T_IMEMO:
- switch (imemo_type(obj)) {
- case imemo_ment:
- rb_free_method_entry(&RANY(obj)->as.imemo.ment);
- RB_DEBUG_COUNTER_INC(obj_imemo_ment);
- break;
- case imemo_iseq:
- rb_iseq_free(&RANY(obj)->as.imemo.iseq);
- RB_DEBUG_COUNTER_INC(obj_imemo_iseq);
- break;
- case imemo_env:
- GC_ASSERT(VM_ENV_ESCAPED_P(RANY(obj)->as.imemo.env.ep));
- xfree((VALUE *)RANY(obj)->as.imemo.env.env);
- RB_DEBUG_COUNTER_INC(obj_imemo_env);
- break;
- case imemo_tmpbuf:
- xfree(RANY(obj)->as.imemo.alloc.ptr);
- RB_DEBUG_COUNTER_INC(obj_imemo_tmpbuf);
- break;
- case imemo_ast:
- rb_ast_free(&RANY(obj)->as.imemo.ast);
- RB_DEBUG_COUNTER_INC(obj_imemo_ast);
- break;
- case imemo_cref:
- RB_DEBUG_COUNTER_INC(obj_imemo_cref);
- break;
- case imemo_svar:
- RB_DEBUG_COUNTER_INC(obj_imemo_svar);
- break;
- case imemo_throw_data:
- RB_DEBUG_COUNTER_INC(obj_imemo_throw_data);
- break;
- case imemo_ifunc:
- RB_DEBUG_COUNTER_INC(obj_imemo_ifunc);
- break;
- case imemo_memo:
- RB_DEBUG_COUNTER_INC(obj_imemo_memo);
- break;
- case imemo_parser_strterm:
- RB_DEBUG_COUNTER_INC(obj_imemo_parser_strterm);
- break;
- case imemo_callinfo:
- RB_DEBUG_COUNTER_INC(obj_imemo_callinfo);
- break;
- case imemo_callcache:
- RB_DEBUG_COUNTER_INC(obj_imemo_callcache);
- break;
- case imemo_constcache:
- RB_DEBUG_COUNTER_INC(obj_imemo_constcache);
- break;
- }
- return TRUE;
+ rb_imemo_free((VALUE)obj);
+ break;
default:
rb_bug("gc_sweep(): unknown data type 0x%x(%p) 0x%"PRIxVALUE,
@@ -3724,13 +3470,14 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
return FALSE;
}
else {
+ RBASIC(obj)->flags = 0;
return TRUE;
}
}
-#define OBJ_ID_INCREMENT (sizeof(RVALUE) / 2)
-#define OBJ_ID_INITIAL (OBJ_ID_INCREMENT * 2)
+#define OBJ_ID_INCREMENT (sizeof(RVALUE))
+#define OBJ_ID_INITIAL (OBJ_ID_INCREMENT)
static int
object_id_cmp(st_data_t x, st_data_t y)
@@ -3758,17 +3505,44 @@ static const struct st_hash_type object_id_hash_type = {
object_id_hash,
};
-void
-Init_heap(void)
+static void *
+rb_gc_impl_objspace_alloc(void)
{
- rb_objspace_t *objspace = &rb_objspace;
+ rb_objspace_t *objspace = calloc1(sizeof(rb_objspace_t));
+ ruby_current_vm_ptr->objspace = objspace;
+
+ objspace->flags.gc_stressful = RTEST(initial_stress);
+ objspace->gc_stress_mode = initial_stress;
+
+ objspace->flags.measure_gc = 1;
+ malloc_limit = gc_params.malloc_limit_min;
+ objspace->finalize_deferred_pjob = rb_postponed_job_preregister(0, gc_finalize_deferred, objspace);
+ if (objspace->finalize_deferred_pjob == POSTPONED_JOB_HANDLE_INVALID) {
+ rb_bug("Could not preregister postponed job for GC");
+ }
+
+ for (int i = 0; i < SIZE_POOL_COUNT; i++) {
+ rb_size_pool_t *size_pool = &size_pools[i];
+
+ size_pool->slot_size = (1 << i) * BASE_SLOT_SIZE;
+
+ ccan_list_head_init(&SIZE_POOL_EDEN_HEAP(size_pool)->pages);
+ ccan_list_head_init(&SIZE_POOL_TOMB_HEAP(size_pool)->pages);
+ }
+
+ rb_darray_make(&objspace->weak_references, 0);
+
+ // TODO: debug why on Windows Ruby crashes on boot when GC is on.
+#ifdef _WIN32
+ dont_gc_on();
+#endif
#if defined(INIT_HEAP_PAGE_ALLOC_USE_MMAP)
/* Need to determine if we can use mmap at runtime. */
heap_page_alloc_use_mmap = INIT_HEAP_PAGE_ALLOC_USE_MMAP;
#endif
- objspace->next_object_id = INT2FIX(OBJ_ID_INITIAL);
+ objspace->next_object_id = OBJ_ID_INITIAL;
objspace->id_to_obj_tbl = st_init_table(&object_id_hash_type);
objspace->obj_to_id_tbl = st_init_numtable();
@@ -3776,13 +3550,14 @@ Init_heap(void)
objspace->rgengc.oldmalloc_increase_limit = gc_params.oldmalloc_limit_min;
#endif
- heap_add_pages(objspace, &size_pools[0], SIZE_POOL_EDEN_HEAP(&size_pools[0]), gc_params.heap_init_slots / HEAP_PAGE_OBJ_LIMIT);
-
- /* Give other size pools allocatable pages. */
- for (int i = 1; i < SIZE_POOL_COUNT; i++) {
+ /* Set size pools allocatable pages. */
+ for (int i = 0; i < SIZE_POOL_COUNT; i++) {
rb_size_pool_t *size_pool = &size_pools[i];
- int multiple = size_pool->slot_size / BASE_SLOT_SIZE;
- size_pool->allocatable_pages = gc_params.heap_init_slots * multiple / HEAP_PAGE_OBJ_LIMIT;
+
+ /* Set the default value of size_pool_init_slots. */
+ gc_params.size_pool_init_slots[i] = GC_HEAP_INIT_SLOTS;
+
+ size_pool->allocatable_pages = minimum_pages_for_size_pool(objspace, size_pool);
}
heap_pages_expand_sorted(objspace);
@@ -3790,17 +3565,11 @@ Init_heap(void)
objspace->profile.invoke_time = getrusage_time();
finalizer_table = st_init_numtable();
-}
-
-void
-Init_gc_stress(void)
-{
- rb_objspace_t *objspace = &rb_objspace;
-
- gc_stress_set(objspace, ruby_initial_gc_stress);
+ return objspace;
}
typedef int each_obj_callback(void *, void *, size_t, void *);
+typedef int each_page_callback(struct heap_page *, void *);
static void objspace_each_objects(rb_objspace_t *objspace, each_obj_callback *callback, void *data, bool protected);
static void objspace_reachable_objects_from_root(rb_objspace_t *, void (func)(const char *, VALUE, void *), void *);
@@ -3809,7 +3578,8 @@ struct each_obj_data {
rb_objspace_t *objspace;
bool reenable_incremental;
- each_obj_callback *callback;
+ each_obj_callback *each_obj_callback;
+ each_page_callback *each_page_callback;
void *data;
struct heap_page **pages[SIZE_POOL_COUNT];
@@ -3829,11 +3599,7 @@ objspace_each_objects_ensure(VALUE arg)
for (int i = 0; i < SIZE_POOL_COUNT; i++) {
struct heap_page **pages = data->pages[i];
- /* pages could be NULL if an error was raised during setup (e.g.
- * malloc failed due to out of memory). */
- if (pages) {
- free(pages);
- }
+ free(pages);
}
return Qnil;
@@ -3887,8 +3653,12 @@ objspace_each_objects_try(VALUE arg)
uintptr_t pstart = (uintptr_t)page->start;
uintptr_t pend = pstart + (page->total_slots * size_pool->slot_size);
- if (!__asan_region_is_poisoned((void *)pstart, pend - pstart) &&
- (*data->callback)((void *)pstart, (void *)pend, size_pool->slot_size, data->data)) {
+ if (data->each_obj_callback &&
+ (*data->each_obj_callback)((void *)pstart, (void *)pend, size_pool->slot_size, data->data)) {
+ break;
+ }
+ if (data->each_page_callback &&
+ (*data->each_page_callback)(page, data->data)) {
break;
}
@@ -3944,9 +3714,10 @@ rb_objspace_each_objects(each_obj_callback *callback, void *data)
}
static void
-objspace_each_objects(rb_objspace_t *objspace, each_obj_callback *callback, void *data, bool protected)
+objspace_each_exec(bool protected, struct each_obj_data *each_obj_data)
{
/* Disable incremental GC */
+ rb_objspace_t *objspace = each_obj_data->objspace;
bool reenable_incremental = FALSE;
if (protected) {
reenable_incremental = !objspace->flags.dont_incremental;
@@ -3955,25 +3726,38 @@ objspace_each_objects(rb_objspace_t *objspace, each_obj_callback *callback, void
objspace->flags.dont_incremental = TRUE;
}
+ each_obj_data->reenable_incremental = reenable_incremental;
+ memset(&each_obj_data->pages, 0, sizeof(each_obj_data->pages));
+ memset(&each_obj_data->pages_counts, 0, sizeof(each_obj_data->pages_counts));
+ rb_ensure(objspace_each_objects_try, (VALUE)each_obj_data,
+ objspace_each_objects_ensure, (VALUE)each_obj_data);
+}
+
+static void
+objspace_each_objects(rb_objspace_t *objspace, each_obj_callback *callback, void *data, bool protected)
+{
struct each_obj_data each_obj_data = {
.objspace = objspace,
- .reenable_incremental = reenable_incremental,
-
- .callback = callback,
+ .each_obj_callback = callback,
+ .each_page_callback = NULL,
.data = data,
-
- .pages = {NULL},
- .pages_counts = {0},
};
- rb_ensure(objspace_each_objects_try, (VALUE)&each_obj_data,
- objspace_each_objects_ensure, (VALUE)&each_obj_data);
+ objspace_each_exec(protected, &each_obj_data);
}
-void
-rb_objspace_each_objects_without_setup(each_obj_callback *callback, void *data)
+#if GC_CAN_COMPILE_COMPACTION
+static void
+objspace_each_pages(rb_objspace_t *objspace, each_page_callback *callback, void *data, bool protected)
{
- objspace_each_objects(&rb_objspace, callback, data, FALSE);
+ struct each_obj_data each_obj_data = {
+ .objspace = objspace,
+ .each_obj_callback = NULL,
+ .each_page_callback = callback,
+ .data = data,
+ };
+ objspace_each_exec(protected, &each_obj_data);
}
+#endif
struct os_each_struct {
size_t num;
@@ -4000,7 +3784,7 @@ internal_object_p(VALUE obj)
break;
case T_CLASS:
if (!p->as.basic.klass) break;
- if (FL_TEST(obj, FL_SINGLETON)) {
+ if (RCLASS_SINGLETON_P(obj)) {
return rb_singleton_class_internal_p(obj);
}
return 0;
@@ -4142,7 +3926,7 @@ should_be_finalizable(VALUE obj)
rb_check_frozen(obj);
}
-VALUE
+static VALUE
rb_define_finalizer_no_check(VALUE obj, VALUE block)
{
rb_objspace_t *objspace = &rb_objspace;
@@ -4280,11 +4064,15 @@ rb_gc_copy_finalizer(VALUE dest, VALUE obj)
st_data_t data;
if (!FL_TEST(obj, FL_FINALIZE)) return;
- if (st_lookup(finalizer_table, obj, &data)) {
+
+ if (RB_LIKELY(st_lookup(finalizer_table, obj, &data))) {
table = (VALUE)data;
st_insert(finalizer_table, dest, table);
+ FL_SET(dest, FL_FINALIZE);
+ }
+ else {
+ rb_bug("rb_gc_copy_finalizer: FL_FINALIZE set but not found in finalizer_table: %s", obj_info(obj));
}
- FL_SET(dest, FL_FINALIZE);
}
static VALUE
@@ -4313,16 +4101,20 @@ run_finalizer(rb_objspace_t *objspace, VALUE obj, VALUE table)
VALUE objid;
VALUE final;
rb_control_frame_t *cfp;
+ VALUE *sp;
long finished;
} saved;
+
rb_execution_context_t * volatile ec = GET_EC();
#define RESTORE_FINALIZER() (\
ec->cfp = saved.cfp, \
+ ec->cfp->sp = saved.sp, \
ec->errinfo = saved.errinfo)
saved.errinfo = ec->errinfo;
saved.objid = rb_obj_id(obj);
saved.cfp = ec->cfp;
+ saved.sp = ec->cfp->sp;
saved.finished = 0;
saved.final = Qundef;
@@ -4344,15 +4136,23 @@ run_finalizer(rb_objspace_t *objspace, VALUE obj, VALUE table)
static void
run_final(rb_objspace_t *objspace, VALUE zombie)
{
- st_data_t key, table;
-
if (RZOMBIE(zombie)->dfree) {
RZOMBIE(zombie)->dfree(RZOMBIE(zombie)->data);
}
- key = (st_data_t)zombie;
- if (st_delete(finalizer_table, &key, &table)) {
- run_finalizer(objspace, zombie, (VALUE)table);
+ st_data_t key = (st_data_t)zombie;
+ if (FL_TEST_RAW(zombie, FL_FINALIZE)) {
+ FL_UNSET(zombie, FL_FINALIZE);
+ st_data_t table;
+ if (st_delete(finalizer_table, &key, &table)) {
+ run_finalizer(objspace, zombie, (VALUE)table);
+ }
+ else {
+ rb_bug("FL_FINALIZE flag is set, but finalizers are not found");
+ }
+ }
+ else {
+ GC_ASSERT(!st_lookup(finalizer_table, key, NULL));
}
}
@@ -4382,7 +4182,7 @@ finalize_list(rb_objspace_t *objspace, VALUE zombie)
page->final_slots--;
page->free_slots++;
heap_page_add_freeobj(objspace, page, zombie);
- objspace->profile.total_freed_objects++;
+ page->size_pool->total_freed_objects++;
}
RB_VM_LOCK_LEAVE();
@@ -4421,9 +4221,44 @@ gc_finalize_deferred(void *dmy)
static void
gc_finalize_deferred_register(rb_objspace_t *objspace)
{
- if (rb_postponed_job_register_one(0, gc_finalize_deferred, objspace) == 0) {
- rb_bug("gc_finalize_deferred_register: can't register finalizer.");
+ /* will enqueue a call to gc_finalize_deferred */
+ rb_postponed_job_trigger(objspace->finalize_deferred_pjob);
+}
+
+static int pop_mark_stack(mark_stack_t *stack, VALUE *data);
+
+static void
+gc_abort(rb_objspace_t *objspace)
+{
+ if (is_incremental_marking(objspace)) {
+ /* Remove all objects from the mark stack. */
+ VALUE obj;
+ while (pop_mark_stack(&objspace->mark_stack, &obj));
+
+ objspace->flags.during_incremental_marking = FALSE;
+ }
+
+ if (is_lazy_sweeping(objspace)) {
+ for (int i = 0; i < SIZE_POOL_COUNT; i++) {
+ rb_size_pool_t *size_pool = &size_pools[i];
+ rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool);
+
+ heap->sweeping_page = NULL;
+ struct heap_page *page = NULL;
+
+ ccan_list_for_each(&heap->pages, page, page_node) {
+ page->flags.before_sweep = false;
+ }
+ }
}
+
+ for (int i = 0; i < SIZE_POOL_COUNT; i++) {
+ rb_size_pool_t *size_pool = &size_pools[i];
+ rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool);
+ rgengc_mark_and_rememberset_clear(objspace, heap);
+ }
+
+ gc_mode_set(objspace, gc_mode_none);
}
struct force_finalize_list {
@@ -4444,25 +4279,95 @@ force_chain_object(st_data_t key, st_data_t val, st_data_t arg)
return ST_CONTINUE;
}
+static void
+gc_each_object(rb_objspace_t *objspace, void (*func)(VALUE obj, void *data), void *data)
+{
+ for (size_t i = 0; i < heap_allocated_pages; i++) {
+ struct heap_page *page = heap_pages_sorted[i];
+ short stride = page->slot_size;
+
+ uintptr_t p = (uintptr_t)page->start;
+ uintptr_t pend = p + page->total_slots * stride;
+ for (; p < pend; p += stride) {
+ VALUE obj = (VALUE)p;
+
+ void *poisoned = asan_unpoison_object_temporary(obj);
+
+ func(obj, data);
+
+ if (poisoned) {
+ GC_ASSERT(BUILTIN_TYPE(obj) == T_NONE);
+ asan_poison_object(obj);
+ }
+ }
+ }
+}
+
bool rb_obj_is_main_ractor(VALUE gv);
+static void
+rb_objspace_free_objects_i(VALUE obj, void *data)
+{
+ rb_objspace_t *objspace = (rb_objspace_t *)data;
+
+ switch (BUILTIN_TYPE(obj)) {
+ case T_NONE:
+ case T_SYMBOL:
+ break;
+ default:
+ obj_free(objspace, obj);
+ break;
+ }
+}
+
void
-rb_objspace_call_finalizer(rb_objspace_t *objspace)
+rb_objspace_free_objects(rb_objspace_t *objspace)
{
- size_t i;
+ gc_each_object(objspace, rb_objspace_free_objects_i, objspace);
+}
+static void
+rb_objspace_call_finalizer_i(VALUE obj, void *data)
+{
+ rb_objspace_t *objspace = (rb_objspace_t *)data;
+
+ switch (BUILTIN_TYPE(obj)) {
+ case T_DATA:
+ if (!rb_free_at_exit && (!DATA_PTR(obj) || !RANY(obj)->as.data.dfree)) break;
+ if (rb_obj_is_thread(obj)) break;
+ if (rb_obj_is_mutex(obj)) break;
+ if (rb_obj_is_fiber(obj)) break;
+ if (rb_obj_is_main_ractor(obj)) break;
+
+ obj_free(objspace, obj);
+ break;
+ case T_FILE:
+ obj_free(objspace, obj);
+ break;
+ case T_SYMBOL:
+ case T_ARRAY:
+ case T_NONE:
+ break;
+ default:
+ if (rb_free_at_exit) {
+ obj_free(objspace, obj);
+ }
+ break;
+ }
+}
+
+void
+rb_objspace_call_finalizer(rb_objspace_t *objspace)
+{
#if RGENGC_CHECK_MODE >= 2
gc_verify_internal_consistency(objspace);
#endif
- gc_rest(objspace);
-
if (ATOMIC_EXCHANGE(finalizing, 1)) return;
/* run finalizers */
finalize_deferred(objspace);
GC_ASSERT(heap_pages_deferred_final == 0);
- gc_rest(objspace);
/* prohibit incremental GC */
objspace->flags.dont_incremental = 1;
@@ -4472,14 +4377,21 @@ rb_objspace_call_finalizer(rb_objspace_t *objspace)
st_foreach(finalizer_table, force_chain_object, (st_data_t)&list);
while (list) {
struct force_finalize_list *curr = list;
+
st_data_t obj = (st_data_t)curr->obj;
- run_finalizer(objspace, curr->obj, curr->table);
st_delete(finalizer_table, &obj, 0);
+ FL_UNSET(curr->obj, FL_FINALIZE);
+
+ run_finalizer(objspace, curr->obj, curr->table);
+
list = curr->next;
xfree(curr);
}
}
+ /* Abort incremental marking and lazy sweeping to speed up shutdown. */
+ gc_abort(objspace);
+
/* prohibit GC because force T_DATA finalizers can break an object graph consistency */
dont_gc_on();
@@ -4487,40 +4399,7 @@ rb_objspace_call_finalizer(rb_objspace_t *objspace)
unsigned int lock_lev;
gc_enter(objspace, gc_enter_event_finalizer, &lock_lev);
- /* run data/file object's finalizers */
- for (i = 0; i < heap_allocated_pages; i++) {
- struct heap_page *page = heap_pages_sorted[i];
- short stride = page->slot_size;
-
- uintptr_t p = (uintptr_t)page->start;
- uintptr_t pend = p + page->total_slots * stride;
- for (; p < pend; p += stride) {
- VALUE vp = (VALUE)p;
- void *poisoned = asan_unpoison_object_temporary(vp);
- switch (BUILTIN_TYPE(vp)) {
- case T_DATA:
- if (!DATA_PTR(p) || !RANY(p)->as.data.dfree) break;
- if (rb_obj_is_thread(vp)) break;
- if (rb_obj_is_mutex(vp)) break;
- if (rb_obj_is_fiber(vp)) break;
- if (rb_obj_is_main_ractor(vp)) break;
-
- rb_data_free(objspace, vp);
- break;
- case T_FILE:
- if (RANY(p)->as.file.fptr) {
- make_io_zombie(objspace, vp);
- }
- break;
- default:
- break;
- }
- if (poisoned) {
- GC_ASSERT(BUILTIN_TYPE(vp) == T_NONE);
- asan_poison_object(vp);
- }
- }
- }
+ gc_each_object(objspace, rb_objspace_call_finalizer_i, objspace);
gc_exit(objspace, gc_enter_event_finalizer, &lock_lev);
@@ -4531,29 +4410,15 @@ rb_objspace_call_finalizer(rb_objspace_t *objspace)
ATOMIC_SET(finalizing, 0);
}
-static inline int
-is_swept_object(VALUE ptr)
-{
- struct heap_page *page = GET_HEAP_PAGE(ptr);
- return page->flags.before_sweep ? FALSE : TRUE;
-}
-
/* garbage objects will be collected soon. */
-static inline int
+static inline bool
is_garbage_object(rb_objspace_t *objspace, VALUE ptr)
{
- if (!is_lazy_sweeping(objspace) ||
- is_swept_object(ptr) ||
- MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(ptr), ptr)) {
-
- return FALSE;
- }
- else {
- return TRUE;
- }
+ return is_lazy_sweeping(objspace) && GET_HEAP_PAGE(ptr)->flags.before_sweep &&
+ !MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(ptr), ptr);
}
-static inline int
+static inline bool
is_live_object(rb_objspace_t *objspace, VALUE ptr)
{
switch (BUILTIN_TYPE(ptr)) {
@@ -4565,20 +4430,13 @@ is_live_object(rb_objspace_t *objspace, VALUE ptr)
break;
}
- if (!is_garbage_object(objspace, ptr)) {
- return TRUE;
- }
- else {
- return FALSE;
- }
+ return !is_garbage_object(objspace, ptr);
}
static inline int
is_markable_object(VALUE obj)
{
- if (rb_special_const_p(obj)) return FALSE; /* special const is not markable */
- check_rvalue_consistency(obj);
- return TRUE;
+ return !RB_SPECIAL_CONST_P(obj);
}
int
@@ -4596,26 +4454,12 @@ rb_objspace_garbage_object_p(VALUE obj)
}
bool
-rb_gc_is_ptr_to_obj(void *ptr)
+rb_gc_is_ptr_to_obj(const void *ptr)
{
rb_objspace_t *objspace = &rb_objspace;
return is_pointer_to_heap(objspace, ptr);
}
-VALUE
-rb_gc_id2ref_obj_tbl(VALUE objid)
-{
- rb_objspace_t *objspace = &rb_objspace;
-
- VALUE orig;
- if (st_lookup(objspace->id_to_obj_tbl, objid, &orig)) {
- return orig;
- }
- else {
- return Qundef;
- }
-}
-
/*
* call-seq:
* ObjectSpace._id2ref(object_id) -> an_object
@@ -4640,32 +4484,34 @@ id2ref(VALUE objid)
#define NUM2PTR(x) NUM2ULL(x)
#endif
rb_objspace_t *objspace = &rb_objspace;
- VALUE ptr;
- VALUE orig;
- void *p0;
objid = rb_to_int(objid);
if (FIXNUM_P(objid) || rb_big_size(objid) <= SIZEOF_VOIDP) {
- ptr = NUM2PTR(objid);
- if (ptr == Qtrue) return Qtrue;
- if (ptr == Qfalse) return Qfalse;
- if (NIL_P(ptr)) return Qnil;
- if (FIXNUM_P(ptr)) return (VALUE)ptr;
- if (FLONUM_P(ptr)) return (VALUE)ptr;
+ VALUE ptr = NUM2PTR(objid);
+ if (SPECIAL_CONST_P(ptr)) {
+ if (ptr == Qtrue) return Qtrue;
+ if (ptr == Qfalse) return Qfalse;
+ if (NIL_P(ptr)) return Qnil;
+ if (FIXNUM_P(ptr)) return ptr;
+ if (FLONUM_P(ptr)) return ptr;
+
+ if (SYMBOL_P(ptr)) {
+ // Check that the symbol is valid
+ if (rb_static_id_valid_p(SYM2ID(ptr))) {
+ return ptr;
+ }
+ else {
+ rb_raise(rb_eRangeError, "%p is not symbol id value", (void *)ptr);
+ }
+ }
- ptr = obj_id_to_ref(objid);
- if ((ptr % sizeof(RVALUE)) == (4 << 2)) {
- ID symid = ptr / sizeof(RVALUE);
- p0 = (void *)ptr;
- if (!rb_static_id_valid_p(symid))
- rb_raise(rb_eRangeError, "%p is not symbol id value", p0);
- return ID2SYM(symid);
+ rb_raise(rb_eRangeError, "%+"PRIsVALUE" is not id value", rb_int2str(objid, 10));
}
}
- if (!UNDEF_P(orig = rb_gc_id2ref_obj_tbl(objid)) &&
- is_live_object(objspace, orig)) {
-
+ VALUE orig;
+ if (st_lookup(objspace->id_to_obj_tbl, objid, &orig) &&
+ is_live_object(objspace, orig)) {
if (!rb_multi_ractor_p() || rb_ractor_shareable_p(orig)) {
return orig;
}
@@ -4674,7 +4520,7 @@ id2ref(VALUE objid)
}
}
- if (rb_int_ge(objid, objspace->next_object_id)) {
+ if (rb_int_ge(objid, ULL2NUM(objspace->next_object_id))) {
rb_raise(rb_eRangeError, "%+"PRIsVALUE" is not id value", rb_int2str(objid, 10));
}
else {
@@ -4692,19 +4538,13 @@ os_id2ref(VALUE os, VALUE objid)
static VALUE
rb_find_object_id(VALUE obj, VALUE (*get_heap_object_id)(VALUE))
{
- if (STATIC_SYM_P(obj)) {
- return (SYM2ID(obj) * sizeof(RVALUE) + (4 << 2)) | FIXNUM_FLAG;
- }
- else if (FLONUM_P(obj)) {
+ if (SPECIAL_CONST_P(obj)) {
#if SIZEOF_LONG == SIZEOF_VOIDP
return LONG2NUM((SIGNED_VALUE)obj);
#else
return LL2NUM((SIGNED_VALUE)obj);
#endif
}
- else if (SPECIAL_CONST_P(obj)) {
- return LONG2NUM((SIGNED_VALUE)obj);
- }
return get_heap_object_id(obj);
}
@@ -4722,8 +4562,8 @@ cached_object_id(VALUE obj)
else {
GC_ASSERT(!FL_TEST(obj, FL_SEEN_OBJ_ID));
- id = objspace->next_object_id;
- objspace->next_object_id = rb_int_plus(id, INT2FIX(OBJ_ID_INCREMENT));
+ id = ULL2NUM(objspace->next_object_id);
+ objspace->next_object_id += OBJ_ID_INCREMENT;
VALUE already_disabled = rb_gc_disable_no_rest();
st_insert(objspace->obj_to_id_tbl, (st_data_t)obj, (st_data_t)id);
@@ -4858,24 +4698,22 @@ obj_memsize_of(VALUE obj, int use_all_types)
break;
case T_MODULE:
case T_CLASS:
- if (RCLASS_EXT(obj)) {
- if (RCLASS_M_TBL(obj)) {
- size += rb_id_table_memsize(RCLASS_M_TBL(obj));
- }
- // class IV sizes are allocated as powers of two
- size += SIZEOF_VALUE << bit_length(RCLASS_IV_COUNT(obj));
- if (RCLASS_CVC_TBL(obj)) {
- size += rb_id_table_memsize(RCLASS_CVC_TBL(obj));
- }
- if (RCLASS_EXT(obj)->const_tbl) {
- size += rb_id_table_memsize(RCLASS_EXT(obj)->const_tbl);
- }
- if (RCLASS_CC_TBL(obj)) {
- size += cc_table_memsize(RCLASS_CC_TBL(obj));
- }
- if (FL_TEST_RAW(obj, RCLASS_SUPERCLASSES_INCLUDE_SELF)) {
- size += (RCLASS_SUPERCLASS_DEPTH(obj) + 1) * sizeof(VALUE);
- }
+ if (RCLASS_M_TBL(obj)) {
+ size += rb_id_table_memsize(RCLASS_M_TBL(obj));
+ }
+ // class IV sizes are allocated as powers of two
+ size += SIZEOF_VALUE << bit_length(RCLASS_IV_COUNT(obj));
+ if (RCLASS_CVC_TBL(obj)) {
+ size += rb_id_table_memsize(RCLASS_CVC_TBL(obj));
+ }
+ if (RCLASS_EXT(obj)->const_tbl) {
+ size += rb_id_table_memsize(RCLASS_EXT(obj)->const_tbl);
+ }
+ if (RCLASS_CC_TBL(obj)) {
+ size += cc_table_memsize(RCLASS_CC_TBL(obj));
+ }
+ if (FL_TEST_RAW(obj, RCLASS_SUPERCLASSES_INCLUDE_SELF)) {
+ size += (RCLASS_SUPERCLASS_DEPTH(obj) + 1) * sizeof(VALUE);
}
break;
case T_ICLASS:
@@ -4884,7 +4722,7 @@ obj_memsize_of(VALUE obj, int use_all_types)
size += rb_id_table_memsize(RCLASS_M_TBL(obj));
}
}
- if (RCLASS_EXT(obj) && RCLASS_CC_TBL(obj)) {
+ if (RCLASS_CC_TBL(obj)) {
size += cc_table_memsize(RCLASS_CC_TBL(obj));
}
break;
@@ -4910,11 +4748,10 @@ obj_memsize_of(VALUE obj, int use_all_types)
if (use_all_types) size += rb_objspace_data_type_memsize(obj);
break;
case T_MATCH:
- if (RMATCH(obj)->rmatch) {
- struct rmatch *rm = RMATCH(obj)->rmatch;
+ {
+ rb_matchext_t *rm = RMATCH_EXT(obj);
size += onig_region_memsize(&rm->regs);
size += sizeof(struct rmatch_offset) * rm->char_offset_num_allocated;
- size += sizeof(struct rmatch);
}
break;
case T_FILE:
@@ -4926,7 +4763,7 @@ obj_memsize_of(VALUE obj, int use_all_types)
case T_COMPLEX:
break;
case T_IMEMO:
- size += imemo_memsize(obj);
+ size += rb_imemo_memsize(obj);
break;
case T_FLOAT:
@@ -5014,6 +4851,27 @@ type_sym(size_t type)
}
}
+struct count_objects_data {
+ size_t counts[T_MASK+1];
+ size_t freed;
+ size_t total;
+};
+
+static void
+count_objects_i(VALUE obj, void *d)
+{
+ struct count_objects_data *data = (struct count_objects_data *)d;
+
+ if (RANY(obj)->as.basic.flags) {
+ data->counts[BUILTIN_TYPE(obj)]++;
+ }
+ else {
+ data->freed++;
+ }
+
+ data->total++;
+}
+
/*
* call-seq:
* ObjectSpace.count_objects([result_hash]) -> hash
@@ -5053,10 +4911,7 @@ static VALUE
count_objects(int argc, VALUE *argv, VALUE os)
{
rb_objspace_t *objspace = &rb_objspace;
- size_t counts[T_MASK+1];
- size_t freed = 0;
- size_t total = 0;
- size_t i;
+ struct count_objects_data data = { 0 };
VALUE hash = Qnil;
if (rb_check_arity(argc, 0, 1) == 1) {
@@ -5065,34 +4920,7 @@ count_objects(int argc, VALUE *argv, VALUE os)
rb_raise(rb_eTypeError, "non-hash given");
}
- for (i = 0; i <= T_MASK; i++) {
- counts[i] = 0;
- }
-
- for (i = 0; i < heap_allocated_pages; i++) {
- struct heap_page *page = heap_pages_sorted[i];
- short stride = page->slot_size;
-
- uintptr_t p = (uintptr_t)page->start;
- uintptr_t pend = p + page->total_slots * stride;
- for (;p < pend; p += stride) {
- VALUE vp = (VALUE)p;
- GC_ASSERT((NUM_IN_PAGE(vp) * BASE_SLOT_SIZE) % page->slot_size == 0);
-
- void *poisoned = asan_unpoison_object_temporary(vp);
- if (RANY(p)->as.basic.flags) {
- counts[BUILTIN_TYPE(vp)]++;
- }
- else {
- freed++;
- }
- if (poisoned) {
- GC_ASSERT(BUILTIN_TYPE(vp) == T_NONE);
- asan_poison_object(vp);
- }
- }
- total += page->total_slots;
- }
+ gc_each_object(objspace, count_objects_i, &data);
if (NIL_P(hash)) {
hash = rb_hash_new();
@@ -5100,13 +4928,13 @@ count_objects(int argc, VALUE *argv, VALUE os)
else if (!RHASH_EMPTY_P(hash)) {
rb_hash_stlike_foreach(hash, set_zero, hash);
}
- rb_hash_aset(hash, ID2SYM(rb_intern("TOTAL")), SIZET2NUM(total));
- rb_hash_aset(hash, ID2SYM(rb_intern("FREE")), SIZET2NUM(freed));
+ rb_hash_aset(hash, ID2SYM(rb_intern("TOTAL")), SIZET2NUM(data.total));
+ rb_hash_aset(hash, ID2SYM(rb_intern("FREE")), SIZET2NUM(data.freed));
- for (i = 0; i <= T_MASK; i++) {
+ for (size_t i = 0; i <= T_MASK; i++) {
VALUE type = type_sym(i);
- if (counts[i])
- rb_hash_aset(hash, type, SIZET2NUM(counts[i]));
+ if (data.counts[i])
+ rb_hash_aset(hash, type, SIZET2NUM(data.counts[i]));
}
return hash;
@@ -5133,7 +4961,7 @@ objspace_available_slots(rb_objspace_t *objspace)
static size_t
objspace_live_slots(rb_objspace_t *objspace)
{
- return (objspace->total_allocated_objects - objspace->profile.total_freed_objects) - heap_pages_final_slots;
+ return total_allocated_objects(objspace) - total_freed_objects(objspace) - heap_pages_final_slots;
}
static size_t
@@ -5212,7 +5040,9 @@ try_move(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *free_page,
* full */
return false;
}
+ asan_unlock_freelist(free_page);
free_page->freelist = RANY(dest)->as.free.next;
+ asan_lock_freelist(free_page);
GC_ASSERT(RB_BUILTIN_TYPE(dest) == T_NONE);
@@ -5244,14 +5074,8 @@ gc_unprotect_pages(rb_objspace_t *objspace, rb_heap_t *heap)
}
static void gc_update_references(rb_objspace_t * objspace);
+#if GC_CAN_COMPILE_COMPACTION
static void invalidate_moved_page(rb_objspace_t *objspace, struct heap_page *page);
-
-#ifndef GC_CAN_COMPILE_COMPACTION
-#if defined(__wasi__) /* WebAssembly doesn't support signals */
-# define GC_CAN_COMPILE_COMPACTION 0
-#else
-# define GC_CAN_COMPILE_COMPACTION 1
-#endif
#endif
#if defined(__MINGW32__) || defined(_WIN32)
@@ -5437,45 +5261,6 @@ install_handlers(void)
#endif
static void
-revert_stack_objects(VALUE stack_obj, void *ctx)
-{
- rb_objspace_t * objspace = (rb_objspace_t*)ctx;
-
- if (BUILTIN_TYPE(stack_obj) == T_MOVED) {
- /* For now we'll revert the whole page if the object made it to the
- * stack. I think we can change this to move just the one object
- * back though */
- invalidate_moved_page(objspace, GET_HEAP_PAGE(stack_obj));
- }
-}
-
-static void
-revert_machine_stack_references(rb_objspace_t *objspace, VALUE v)
-{
- if (is_pointer_to_heap(objspace, (void *)v)) {
- if (BUILTIN_TYPE(v) == T_MOVED) {
- /* For now we'll revert the whole page if the object made it to the
- * stack. I think we can change this to move just the one object
- * back though */
- invalidate_moved_page(objspace, GET_HEAP_PAGE(v));
- }
- }
-}
-
-static void each_machine_stack_value(const rb_execution_context_t *ec, void (*cb)(rb_objspace_t *, VALUE));
-
-static void
-check_stack_for_moved(rb_objspace_t *objspace)
-{
- rb_execution_context_t *ec = GET_EC();
- rb_vm_t *vm = rb_ec_vm_ptr(ec);
- rb_vm_each_stack_value(vm, revert_stack_objects, (void*)objspace);
- each_machine_stack_value(ec, revert_machine_stack_references);
-}
-
-static void gc_mode_transition(rb_objspace_t *objspace, enum gc_mode mode);
-
-static void
gc_compact_finish(rb_objspace_t *objspace)
{
for (int i = 0; i < SIZE_POOL_COUNT; i++) {
@@ -5486,13 +5271,6 @@ gc_compact_finish(rb_objspace_t *objspace)
uninstall_handlers();
- /* The mutator is allowed to run during incremental sweeping. T_MOVED
- * objects can get pushed on the stack and when the compaction process
- * finishes up, it may remove the read barrier before anything has a
- * chance to read from the T_MOVED address. To fix this, we scan the stack
- * then revert any moved objects that made it to the stack. */
- check_stack_for_moved(objspace);
-
gc_update_references(objspace);
objspace->profile.compact_count++;
@@ -5533,46 +5311,46 @@ gc_sweep_plane(rb_objspace_t *objspace, rb_heap_t *heap, uintptr_t p, bits_t bit
asan_unpoison_object(vp, false);
if (bitset & 1) {
switch (BUILTIN_TYPE(vp)) {
- default: /* majority case */
- gc_report(2, objspace, "page_sweep: free %p\n", (void *)p);
+ default: /* majority case */
+ gc_report(2, objspace, "page_sweep: free %p\n", (void *)p);
#if RGENGC_CHECK_MODE
- if (!is_full_marking(objspace)) {
- if (RVALUE_OLD_P(vp)) rb_bug("page_sweep: %p - old while minor GC.", (void *)p);
- if (rgengc_remembered_sweep(objspace, vp)) rb_bug("page_sweep: %p - remembered.", (void *)p);
- }
+ if (!is_full_marking(objspace)) {
+ if (RVALUE_OLD_P(vp)) rb_bug("page_sweep: %p - old while minor GC.", (void *)p);
+ if (RVALUE_REMEMBERED(vp)) rb_bug("page_sweep: %p - remembered.", (void *)p);
+ }
#endif
- if (obj_free(objspace, vp)) {
- // always add free slots back to the swept pages freelist,
- // so that if we're comapacting, we can re-use the slots
- (void)VALGRIND_MAKE_MEM_UNDEFINED((void*)p, BASE_SLOT_SIZE);
- heap_page_add_freeobj(objspace, sweep_page, vp);
- gc_report(3, objspace, "page_sweep: %s is added to freelist\n", obj_info(vp));
- ctx->freed_slots++;
- }
- else {
- ctx->final_slots++;
- }
- break;
-
- case T_MOVED:
- if (objspace->flags.during_compacting) {
- /* The sweep cursor shouldn't have made it to any
- * T_MOVED slots while the compact flag is enabled.
- * The sweep cursor and compact cursor move in
- * opposite directions, and when they meet references will
- * get updated and "during_compacting" should get disabled */
- rb_bug("T_MOVED shouldn't be seen until compaction is finished");
- }
- gc_report(3, objspace, "page_sweep: %s is added to freelist\n", obj_info(vp));
- ctx->empty_slots++;
+ if (obj_free(objspace, vp)) {
+ // always add free slots back to the swept pages freelist,
+ // so that if we're comapacting, we can re-use the slots
+ (void)VALGRIND_MAKE_MEM_UNDEFINED((void*)p, BASE_SLOT_SIZE);
heap_page_add_freeobj(objspace, sweep_page, vp);
- break;
- case T_ZOMBIE:
- /* already counted */
- break;
- case T_NONE:
- ctx->empty_slots++; /* already freed */
- break;
+ gc_report(3, objspace, "page_sweep: %s is added to freelist\n", obj_info(vp));
+ ctx->freed_slots++;
+ }
+ else {
+ ctx->final_slots++;
+ }
+ break;
+
+ case T_MOVED:
+ if (objspace->flags.during_compacting) {
+ /* The sweep cursor shouldn't have made it to any
+ * T_MOVED slots while the compact flag is enabled.
+ * The sweep cursor and compact cursor move in
+ * opposite directions, and when they meet references will
+ * get updated and "during_compacting" should get disabled */
+ rb_bug("T_MOVED shouldn't be seen until compaction is finished");
+ }
+ gc_report(3, objspace, "page_sweep: %s is added to freelist\n", obj_info(vp));
+ ctx->empty_slots++;
+ heap_page_add_freeobj(objspace, sweep_page, vp);
+ break;
+ case T_ZOMBIE:
+ /* already counted */
+ break;
+ case T_NONE:
+ ctx->empty_slots++; /* already freed */
+ break;
}
}
p += slot_size;
@@ -5648,13 +5426,10 @@ gc_sweep_page(rb_objspace_t *objspace, rb_heap_t *heap, struct gc_sweep_context
ctx->freed_slots, ctx->empty_slots, ctx->final_slots);
sweep_page->free_slots += ctx->freed_slots + ctx->empty_slots;
- objspace->profile.total_freed_objects += ctx->freed_slots;
+ sweep_page->size_pool->total_freed_objects += ctx->freed_slots;
if (heap_pages_deferred_final && !finalizing) {
- rb_thread_t *th = GET_THREAD();
- if (th) {
- gc_finalize_deferred_register(objspace);
- }
+ gc_finalize_deferred_register(objspace);
}
#if RGENGC_CHECK_MODE
@@ -5744,12 +5519,27 @@ gc_sweep_start_heap(rb_objspace_t *objspace, rb_heap_t *heap)
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 4
__attribute__((noinline))
#endif
+
+#if GC_CAN_COMPILE_COMPACTION
+static void gc_sort_heap_by_compare_func(rb_objspace_t *objspace, gc_compact_compare_func compare_func);
+static int compare_pinned_slots(const void *left, const void *right, void *d);
+#endif
+
static void
gc_sweep_start(rb_objspace_t *objspace)
{
gc_mode_transition(objspace, gc_mode_sweeping);
objspace->rincgc.pooled_slots = 0;
+#if GC_CAN_COMPILE_COMPACTION
+ if (objspace->flags.during_compacting) {
+ gc_sort_heap_by_compare_func(
+ objspace,
+ objspace->rcompactor.compare_func ? objspace->rcompactor.compare_func : compare_pinned_slots
+ );
+ }
+#endif
+
for (int i = 0; i < SIZE_POOL_COUNT; i++) {
rb_size_pool_t *size_pool = &size_pools[i];
rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool);
@@ -5778,14 +5568,15 @@ gc_sweep_finish_size_pool(rb_objspace_t *objspace, rb_size_pool_t *size_pool)
size_t total_pages = heap->total_pages + SIZE_POOL_TOMB_HEAP(size_pool)->total_pages;
size_t swept_slots = size_pool->freed_slots + size_pool->empty_slots;
- size_t min_free_slots = (size_t)(total_slots * gc_params.heap_free_slots_min_ratio);
+ size_t init_slots = gc_params.size_pool_init_slots[size_pool - size_pools];
+ size_t min_free_slots = (size_t)(MAX(total_slots, init_slots) * gc_params.heap_free_slots_min_ratio);
/* If we don't have enough slots and we have pages on the tomb heap, move
* pages from the tomb heap to the eden heap. This may prevent page
* creation thrashing (frequently allocating and deallocting pages) and
* GC thrashing (running GC more frequently than required). */
struct heap_page *resurrected_page;
- while ((swept_slots < min_free_slots || swept_slots < gc_params.heap_init_slots) &&
+ while (swept_slots < min_free_slots &&
(resurrected_page = heap_page_resurrect(objspace, size_pool))) {
swept_slots += resurrected_page->free_slots;
@@ -5793,32 +5584,24 @@ gc_sweep_finish_size_pool(rb_objspace_t *objspace, rb_size_pool_t *size_pool)
heap_add_freepage(heap, resurrected_page);
}
- /* Some size pools may have very few pages (or even no pages). These size pools
- * should still have allocatable pages. */
- if (min_free_slots < gc_params.heap_init_slots && swept_slots < gc_params.heap_init_slots) {
- int multiple = size_pool->slot_size / BASE_SLOT_SIZE;
- size_t extra_slots = gc_params.heap_init_slots - swept_slots;
- size_t extend_page_count = CEILDIV(extra_slots * multiple, HEAP_PAGE_OBJ_LIMIT);
- if (extend_page_count > size_pool->allocatable_pages) {
- size_pool_allocatable_pages_set(objspace, size_pool, extend_page_count);
- }
- }
-
if (swept_slots < min_free_slots) {
bool grow_heap = is_full_marking(objspace);
- if (!is_full_marking(objspace)) {
- /* The heap is a growth heap if it freed more slots than had empty
- * slots and used up all of its allocatable pages. */
- bool is_growth_heap = (size_pool->empty_slots == 0 ||
- size_pool->freed_slots > size_pool->empty_slots) &&
- size_pool->allocatable_pages == 0;
-
- if (objspace->profile.count - objspace->rgengc.last_major_gc < RVALUE_OLD_AGE) {
+ /* Consider growing or starting a major GC if we are not currently in a
+ * major GC and we can't allocate any more pages. */
+ if (!is_full_marking(objspace) && size_pool->allocatable_pages == 0) {
+ /* The heap is a growth heap if it freed more slots than had empty slots. */
+ bool is_growth_heap = size_pool->empty_slots == 0 || size_pool->freed_slots > size_pool->empty_slots;
+
+ /* Grow this heap if we haven't run at least RVALUE_OLD_AGE minor
+ * GC since the last major GC or if this heap is smaller than the
+ * the configured initial size. */
+ if (objspace->profile.count - objspace->rgengc.last_major_gc < RVALUE_OLD_AGE ||
+ total_slots < init_slots) {
grow_heap = TRUE;
}
else if (is_growth_heap) { /* Only growth heaps are allowed to start a major GC. */
- objspace->rgengc.need_major_gc |= GPR_FLAG_MAJOR_BY_NOFREE;
+ gc_needs_major_flags |= GPR_FLAG_MAJOR_BY_NOFREE;
size_pool->force_major_gc_count++;
}
}
@@ -5883,9 +5666,7 @@ gc_sweep_step(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *hea
struct heap_page *sweep_page = heap->sweeping_page;
int unlink_limit = GC_SWEEP_PAGES_FREEABLE_PER_STEP;
int swept_slots = 0;
- bool need_pool = TRUE;
-
- gc_report(2, objspace, "gc_sweep_step (need_pool: %d)\n", need_pool);
+ int pooled_slots = 0;
if (sweep_page == NULL) return FALSE;
@@ -5920,9 +5701,9 @@ gc_sweep_step(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *hea
size_pool->freed_slots += ctx.freed_slots;
size_pool->empty_slots += ctx.empty_slots;
- if (need_pool) {
+ if (pooled_slots < GC_INCREMENTAL_SWEEP_POOL_SLOT_COUNT) {
heap_add_poolpage(objspace, heap, sweep_page);
- need_pool = FALSE;
+ pooled_slots += free_slots;
}
else {
heap_add_freepage(heap, sweep_page);
@@ -5992,6 +5773,7 @@ gc_sweep_continue(rb_objspace_t *objspace, rb_size_pool_t *sweep_size_pool, rb_h
gc_sweeping_exit(objspace);
}
+#if GC_CAN_COMPILE_COMPACTION
static void
invalidate_moved_plane(rb_objspace_t *objspace, struct heap_page *page, uintptr_t p, bits_t bitset)
{
@@ -6064,6 +5846,7 @@ invalidate_moved_page(rb_objspace_t *objspace, struct heap_page *page)
p += BITS_BITLENGTH * BASE_SLOT_SIZE;
}
}
+#endif
static void
gc_compact_start(rb_objspace_t *objspace)
@@ -6248,9 +6031,8 @@ mark_stack_free_cache(mark_stack_t *stack)
}
static void
-push_mark_stack(mark_stack_t *stack, VALUE data)
+push_mark_stack(mark_stack_t *stack, VALUE obj)
{
- VALUE obj = data;
switch (BUILTIN_TYPE(obj)) {
case T_OBJECT:
case T_CLASS:
@@ -6275,7 +6057,7 @@ push_mark_stack(mark_stack_t *stack, VALUE data)
if (stack->index == stack->limit) {
push_mark_stack_chunk(stack);
}
- stack->chunk->data[stack->index++] = data;
+ stack->chunk->data[stack->index++] = obj;
return;
case T_NONE:
@@ -6294,8 +6076,8 @@ push_mark_stack(mark_stack_t *stack, VALUE data)
}
rb_bug("rb_gc_mark(): unknown data type 0x%x(%p) %s",
- BUILTIN_TYPE(obj), (void *)data,
- is_pointer_to_heap(&rb_objspace, (void *)data) ? "corrupted object" : "non object");
+ BUILTIN_TYPE(obj), (void *)obj,
+ is_pointer_to_heap(&rb_objspace, (void *)obj) ? "corrupted object" : "non object");
}
static int
@@ -6431,16 +6213,6 @@ rb_gc_mark_locations(const VALUE *start, const VALUE *end)
gc_mark_locations(&rb_objspace, start, end, gc_mark_maybe);
}
-static void
-gc_mark_values(rb_objspace_t *objspace, long n, const VALUE *values)
-{
- long i;
-
- for (i=0; i<n; i++) {
- gc_mark(objspace, values[i]);
- }
-}
-
void
rb_gc_mark_values(long n, const VALUE *values)
{
@@ -6448,19 +6220,7 @@ rb_gc_mark_values(long n, const VALUE *values)
rb_objspace_t *objspace = &rb_objspace;
for (i=0; i<n; i++) {
- gc_mark_and_pin(objspace, values[i]);
- }
-}
-
-static void
-gc_mark_stack_values(rb_objspace_t *objspace, long n, const VALUE *values)
-{
- long i;
-
- for (i=0; i<n; i++) {
- if (is_markable_object(values[i])) {
- gc_mark_and_pin(objspace, values[i]);
- }
+ gc_mark(objspace, values[i]);
}
}
@@ -6468,7 +6228,10 @@ void
rb_gc_mark_vm_stack_values(long n, const VALUE *values)
{
rb_objspace_t *objspace = &rb_objspace;
- gc_mark_stack_values(objspace, n, values);
+
+ for (long i = 0; i < n; i++) {
+ gc_mark_and_pin(objspace, values[i]);
+ }
}
static int
@@ -6593,52 +6356,6 @@ rb_mark_hash(st_table *tbl)
mark_st(&rb_objspace, tbl);
}
-static void
-mark_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me)
-{
- const rb_method_definition_t *def = me->def;
-
- gc_mark(objspace, me->owner);
- gc_mark(objspace, me->defined_class);
-
- if (def) {
- switch (def->type) {
- case VM_METHOD_TYPE_ISEQ:
- if (def->body.iseq.iseqptr) gc_mark(objspace, (VALUE)def->body.iseq.iseqptr);
- gc_mark(objspace, (VALUE)def->body.iseq.cref);
-
- if (def->iseq_overload && me->defined_class) {
- // it can be a key of "overloaded_cme" table
- // so it should be pinned.
- gc_mark_and_pin(objspace, (VALUE)me);
- }
- break;
- case VM_METHOD_TYPE_ATTRSET:
- case VM_METHOD_TYPE_IVAR:
- gc_mark(objspace, def->body.attr.location);
- break;
- case VM_METHOD_TYPE_BMETHOD:
- gc_mark(objspace, def->body.bmethod.proc);
- if (def->body.bmethod.hooks) rb_hook_list_mark(def->body.bmethod.hooks);
- break;
- case VM_METHOD_TYPE_ALIAS:
- gc_mark(objspace, (VALUE)def->body.alias.original_me);
- return;
- case VM_METHOD_TYPE_REFINED:
- gc_mark(objspace, (VALUE)def->body.refined.orig_me);
- gc_mark(objspace, (VALUE)def->body.refined.owner);
- break;
- case VM_METHOD_TYPE_CFUNC:
- case VM_METHOD_TYPE_ZSUPER:
- case VM_METHOD_TYPE_MISSING:
- case VM_METHOD_TYPE_OPTIMIZED:
- case VM_METHOD_TYPE_UNDEF:
- case VM_METHOD_TYPE_NOTIMPLEMENTED:
- break;
- }
- }
-}
-
static enum rb_id_table_iterator_result
mark_method_entry_i(VALUE me, void *data)
{
@@ -6687,6 +6404,26 @@ mark_const_tbl(rb_objspace_t *objspace, struct rb_id_table *tbl)
static void each_stack_location(rb_objspace_t *objspace, const rb_execution_context_t *ec,
const VALUE *stack_start, const VALUE *stack_end, void (*cb)(rb_objspace_t *, VALUE));
+static void
+gc_mark_machine_stack_location_maybe(rb_objspace_t *objspace, VALUE obj)
+{
+ gc_mark_maybe(objspace, obj);
+
+#ifdef RUBY_ASAN_ENABLED
+ const rb_execution_context_t *ec = objspace->marking_machine_context_ec;
+ void *fake_frame_start;
+ void *fake_frame_end;
+ bool is_fake_frame = asan_get_fake_stack_extents(
+ ec->machine.asan_fake_stack_handle, obj,
+ ec->machine.stack_start, ec->machine.stack_end,
+ &fake_frame_start, &fake_frame_end
+ );
+ if (is_fake_frame) {
+ each_stack_location(objspace, ec, fake_frame_start, fake_frame_end, gc_mark_maybe);
+ }
+#endif
+}
+
#if defined(__wasm__)
@@ -6748,27 +6485,39 @@ mark_current_machine_context(rb_objspace_t *objspace, rb_execution_context_t *ec
SET_STACK_END;
GET_STACK_BOUNDS(stack_start, stack_end, 1);
- each_location(objspace, save_regs_gc_mark.v, numberof(save_regs_gc_mark.v), gc_mark_maybe);
+#ifdef RUBY_ASAN_ENABLED
+ objspace->marking_machine_context_ec = ec;
+#endif
+
+ each_location(objspace, save_regs_gc_mark.v, numberof(save_regs_gc_mark.v), gc_mark_machine_stack_location_maybe);
+ each_stack_location(objspace, ec, stack_start, stack_end, gc_mark_machine_stack_location_maybe);
- each_stack_location(objspace, ec, stack_start, stack_end, gc_mark_maybe);
+#ifdef RUBY_ASAN_ENABLED
+ objspace->marking_machine_context_ec = NULL;
+#endif
}
#endif
-static void
-each_machine_stack_value(const rb_execution_context_t *ec, void (*cb)(rb_objspace_t *, VALUE))
+void
+rb_gc_mark_machine_context(const rb_execution_context_t *ec)
{
rb_objspace_t *objspace = &rb_objspace;
+#ifdef RUBY_ASAN_ENABLED
+ objspace->marking_machine_context_ec = ec;
+#endif
+
VALUE *stack_start, *stack_end;
GET_STACK_BOUNDS(stack_start, stack_end, 0);
RUBY_DEBUG_LOG("ec->th:%u stack_start:%p stack_end:%p", rb_ec_thread_ptr(ec)->serial, stack_start, stack_end);
- each_stack_location(objspace, ec, stack_start, stack_end, cb);
-}
-void
-rb_gc_mark_machine_stack(const rb_execution_context_t *ec)
-{
- each_machine_stack_value(ec, gc_mark_maybe);
+ each_stack_location(objspace, ec, stack_start, stack_end, gc_mark_machine_stack_location_maybe);
+ int num_regs = sizeof(ec->machine.regs)/(sizeof(VALUE));
+ each_location(objspace, (VALUE*)&ec->machine.regs, num_regs, gc_mark_machine_stack_location_maybe);
+
+#ifdef RUBY_ASAN_ENABLED
+ objspace->marking_machine_context_ec = NULL;
+#endif
}
static void
@@ -6844,7 +6593,7 @@ gc_remember_unprotected(rb_objspace_t *objspace, VALUE obj)
bits_t *uncollectible_bits = &page->uncollectible_bits[0];
if (!MARKED_IN_BITMAP(uncollectible_bits, obj)) {
- page->flags.has_uncollectible_shady_objects = TRUE;
+ page->flags.has_uncollectible_wb_unprotected_objects = TRUE;
MARK_IN_BITMAP(uncollectible_bits, obj);
objspace->rgengc.uncollectible_wb_unprotected_objects++;
@@ -6952,7 +6701,11 @@ gc_pin(rb_objspace_t *objspace, VALUE obj)
GC_ASSERT(is_markable_object(obj));
if (UNLIKELY(objspace->flags.during_compacting)) {
if (LIKELY(during_gc)) {
- MARK_IN_BITMAP(GET_HEAP_PINNED_BITS(obj), obj);
+ if (!MARKED_IN_BITMAP(GET_HEAP_PINNED_BITS(obj), obj)) {
+ GC_ASSERT(GET_HEAP_PAGE(obj)->pinned_slots <= GET_HEAP_PAGE(obj)->total_slots);
+ GET_HEAP_PAGE(obj)->pinned_slots++;
+ MARK_IN_BITMAP(GET_HEAP_PINNED_BITS(obj), obj);
+ }
}
}
}
@@ -7001,14 +6754,63 @@ rb_gc_mark_and_move(VALUE *ptr)
}
}
-/* CAUTION: THIS FUNCTION ENABLE *ONLY BEFORE* SWEEPING.
- * This function is only for GC_END_MARK timing.
- */
+void
+rb_gc_mark_weak(VALUE *ptr)
+{
+ rb_objspace_t *objspace = &rb_objspace;
-int
-rb_objspace_marked_object_p(VALUE obj)
+ if (UNLIKELY(!during_gc)) return;
+
+ VALUE obj = *ptr;
+ if (RB_SPECIAL_CONST_P(obj)) return;
+
+ GC_ASSERT(objspace->rgengc.parent_object == 0 || FL_TEST(objspace->rgengc.parent_object, FL_WB_PROTECTED));
+
+ if (UNLIKELY(RB_TYPE_P(obj, T_NONE))) {
+ rp(obj);
+ rb_bug("try to mark T_NONE object");
+ }
+
+ /* If we are in a minor GC and the other object is old, then obj should
+ * already be marked and cannot be reclaimed in this GC cycle so we don't
+ * need to add it to the weak refences list. */
+ if (!is_full_marking(objspace) && RVALUE_OLD_P(obj)) {
+ GC_ASSERT(RVALUE_MARKED(obj));
+ GC_ASSERT(!objspace->flags.during_compacting);
+
+ return;
+ }
+
+ rgengc_check_relation(objspace, obj);
+
+ DURING_GC_COULD_MALLOC_REGION_START();
+ {
+ rb_darray_append(&objspace->weak_references, ptr);
+ }
+ DURING_GC_COULD_MALLOC_REGION_END();
+
+ objspace->profile.weak_references_count++;
+}
+
+void
+rb_gc_remove_weak(VALUE parent_obj, VALUE *ptr)
{
- return RVALUE_MARKED(obj) ? TRUE : FALSE;
+ rb_objspace_t *objspace = &rb_objspace;
+
+ /* If we're not incremental marking, then the state of the objects can't
+ * change so we don't need to do anything. */
+ if (!is_incremental_marking(objspace)) return;
+ /* If parent_obj has not been marked, then ptr has not yet been marked
+ * weak, so we don't need to do anything. */
+ if (!RVALUE_MARKED(parent_obj)) return;
+
+ VALUE **ptr_ptr;
+ rb_darray_foreach(objspace->weak_references, i, ptr_ptr) {
+ if (*ptr_ptr == ptr) {
+ *ptr_ptr = NULL;
+ break;
+ }
+ }
}
static inline void
@@ -7022,122 +6824,12 @@ gc_mark_set_parent(rb_objspace_t *objspace, VALUE obj)
}
}
-static void
-gc_mark_imemo(rb_objspace_t *objspace, VALUE obj)
-{
- switch (imemo_type(obj)) {
- case imemo_env:
- {
- const rb_env_t *env = (const rb_env_t *)obj;
-
- if (LIKELY(env->ep)) {
- // just after newobj() can be NULL here.
- GC_ASSERT(env->ep[VM_ENV_DATA_INDEX_ENV] == obj);
- GC_ASSERT(VM_ENV_ESCAPED_P(env->ep));
- gc_mark_values(objspace, (long)env->env_size, env->env);
- VM_ENV_FLAGS_SET(env->ep, VM_ENV_FLAG_WB_REQUIRED);
- gc_mark(objspace, (VALUE)rb_vm_env_prev_env(env));
- gc_mark(objspace, (VALUE)env->iseq);
- }
- }
- return;
- case imemo_cref:
- gc_mark(objspace, RANY(obj)->as.imemo.cref.klass_or_self);
- gc_mark(objspace, (VALUE)RANY(obj)->as.imemo.cref.next);
- gc_mark(objspace, RANY(obj)->as.imemo.cref.refinements);
- return;
- case imemo_svar:
- gc_mark(objspace, RANY(obj)->as.imemo.svar.cref_or_me);
- gc_mark(objspace, RANY(obj)->as.imemo.svar.lastline);
- gc_mark(objspace, RANY(obj)->as.imemo.svar.backref);
- gc_mark(objspace, RANY(obj)->as.imemo.svar.others);
- return;
- case imemo_throw_data:
- gc_mark(objspace, RANY(obj)->as.imemo.throw_data.throw_obj);
- return;
- case imemo_ifunc:
- gc_mark_maybe(objspace, (VALUE)RANY(obj)->as.imemo.ifunc.data);
- return;
- case imemo_memo:
- gc_mark(objspace, RANY(obj)->as.imemo.memo.v1);
- gc_mark(objspace, RANY(obj)->as.imemo.memo.v2);
- gc_mark_maybe(objspace, RANY(obj)->as.imemo.memo.u3.value);
- return;
- case imemo_ment:
- mark_method_entry(objspace, &RANY(obj)->as.imemo.ment);
- return;
- case imemo_iseq:
- rb_iseq_mark_and_move((rb_iseq_t *)obj, false);
- return;
- case imemo_tmpbuf:
- {
- const rb_imemo_tmpbuf_t *m = &RANY(obj)->as.imemo.alloc;
- do {
- rb_gc_mark_locations(m->ptr, m->ptr + m->cnt);
- } while ((m = m->next) != NULL);
- }
- return;
- case imemo_ast:
- rb_ast_mark(&RANY(obj)->as.imemo.ast);
- return;
- case imemo_parser_strterm:
- rb_strterm_mark(obj);
- return;
- case imemo_callinfo:
- return;
- case imemo_callcache:
- {
- const struct rb_callcache *cc = (const struct rb_callcache *)obj;
- // should not mark klass here
- gc_mark(objspace, (VALUE)vm_cc_cme(cc));
- }
- return;
- case imemo_constcache:
- {
- const struct iseq_inline_constant_cache_entry *ice = (struct iseq_inline_constant_cache_entry *)obj;
- gc_mark(objspace, ice->value);
- }
- return;
-#if VM_CHECK_MODE > 0
- default:
- VM_UNREACHABLE(gc_mark_imemo);
-#endif
- }
-}
-
static bool
gc_declarative_marking_p(const rb_data_type_t *type)
{
return (type->flags & RUBY_TYPED_DECL_MARKING) != 0;
}
-#define EDGE (VALUE *)((char *)data_struct + offset)
-
-static inline void
-gc_mark_from_offset(rb_objspace_t *objspace, VALUE obj)
-{
- // we are overloading the dmark callback to contain a list of offsets
- size_t *offset_list = (size_t *)RANY(obj)->as.typeddata.type->function.dmark;
- void *data_struct = RANY(obj)->as.typeddata.data;
-
- for (size_t offset = *offset_list; *offset_list != RUBY_REF_END; offset = *offset_list++) {
- rb_gc_mark_movable(*EDGE);
- }
-}
-
-static inline void
-gc_ref_update_from_offset(rb_objspace_t *objspace, VALUE obj)
-{
- // we are overloading the dmark callback to contain a list of offsets
- size_t *offset_list = (size_t *)RANY(obj)->as.typeddata.type->function.dmark;
- void *data_struct = RANY(obj)->as.typeddata.data;
-
- for (size_t offset = *offset_list; *offset_list != RUBY_REF_END; offset = *offset_list++) {
- if (SPECIAL_CONST_P(*EDGE)) continue;
- *EDGE = rb_gc_location(*EDGE);
- }
-}
-
static void mark_cvc_tbl(rb_objspace_t *objspace, VALUE klass);
static void
@@ -7147,15 +6839,17 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
gc_mark_set_parent(objspace, obj);
if (FL_TEST(obj, FL_EXIVAR)) {
- rb_mark_and_update_generic_ivar(obj);
+ rb_mark_generic_ivar(obj);
}
switch (BUILTIN_TYPE(obj)) {
case T_FLOAT:
case T_BIGNUM:
case T_SYMBOL:
- /* Not immediates, but does not have references and singleton
- * class */
+ /* Not immediates, but does not have references and singleton class.
+ *
+ * RSYMBOL(obj)->fstr intentionally not marked. See log for 96815f1e
+ * ("symbol.c: remove rb_gc_mark_symbols()") */
return;
case T_NIL:
@@ -7168,7 +6862,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
break;
case T_IMEMO:
- gc_mark_imemo(objspace, obj);
+ rb_imemo_mark_and_move(obj, false);
return;
default:
@@ -7187,13 +6881,17 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
if (RCLASS_SUPER(obj)) {
gc_mark(objspace, RCLASS_SUPER(obj));
}
- if (!RCLASS_EXT(obj)) break;
mark_m_tbl(objspace, RCLASS_M_TBL(obj));
mark_cvc_tbl(objspace, obj);
- cc_table_mark(objspace, obj);
- for (attr_index_t i = 0; i < RCLASS_IV_COUNT(obj); i++) {
- gc_mark(objspace, RCLASS_IVPTR(obj)[i]);
+ rb_cc_table_mark(obj);
+ if (rb_shape_obj_too_complex(obj)) {
+ mark_tbl_no_pin(objspace, (st_table *)RCLASS_IVPTR(obj));
+ }
+ else {
+ for (attr_index_t i = 0; i < RCLASS_IV_COUNT(obj); i++) {
+ gc_mark(objspace, RCLASS_IVPTR(obj)[i]);
+ }
}
mark_const_tbl(objspace, RCLASS_CONST_TBL(obj));
@@ -7207,13 +6905,12 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
if (RCLASS_SUPER(obj)) {
gc_mark(objspace, RCLASS_SUPER(obj));
}
- if (!RCLASS_EXT(obj)) break;
if (RCLASS_INCLUDER(obj)) {
gc_mark(objspace, RCLASS_INCLUDER(obj));
}
mark_m_tbl(objspace, RCLASS_CALLABLE_M_TBL(obj));
- cc_table_mark(objspace, obj);
+ rb_cc_table_mark(obj);
break;
case T_ARRAY:
@@ -7223,16 +6920,10 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
}
else {
long i, len = RARRAY_LEN(obj);
- const VALUE *ptr = RARRAY_CONST_PTR_TRANSIENT(obj);
+ const VALUE *ptr = RARRAY_CONST_PTR(obj);
for (i=0; i < len; i++) {
gc_mark(objspace, ptr[i]);
}
-
- if (LIKELY(during_gc)) {
- if (!ARY_EMBED_P(obj) && RARRAY_TRANSIENT_P(obj)) {
- rb_transient_heap_mark(obj, ptr);
- }
- }
}
break;
@@ -7242,16 +6933,30 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
case T_STRING:
if (STR_SHARED_P(obj)) {
- gc_mark(objspace, any->as.string.as.heap.aux.shared);
+ if (STR_EMBED_P(any->as.string.as.heap.aux.shared)) {
+ /* Embedded shared strings cannot be moved because this string
+ * points into the slot of the shared string. There may be code
+ * using the RSTRING_PTR on the stack, which would pin this
+ * string but not pin the shared string, causing it to move. */
+ gc_mark_and_pin(objspace, any->as.string.as.heap.aux.shared);
+ }
+ else {
+ gc_mark(objspace, any->as.string.as.heap.aux.shared);
+ }
}
break;
case T_DATA:
{
- void *const ptr = DATA_PTR(obj);
+ void *const ptr = RTYPEDDATA_P(obj) ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj);
+
if (ptr) {
if (RTYPEDDATA_P(obj) && gc_declarative_marking_p(any->as.typeddata.type)) {
- gc_mark_from_offset(objspace, obj);
+ size_t *offset_list = (size_t *)RANY(obj)->as.typeddata.type->function.dmark;
+
+ for (size_t offset = *offset_list; offset != RUBY_REF_END; offset = *offset_list++) {
+ rb_gc_mark_movable(*(VALUE *)((char *)ptr + offset));
+ }
}
else {
RUBY_DATA_FUNC mark_func = RTYPEDDATA_P(obj) ?
@@ -7276,11 +6981,6 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
for (i = 0; i < len; i++) {
gc_mark(objspace, ptr[i]);
}
-
- if (LIKELY(during_gc) &&
- ROBJ_TRANSIENT_P(obj)) {
- rb_transient_heap_mark(obj, ptr);
- }
}
if (shape) {
VALUE klass = RBASIC_CLASS(obj);
@@ -7337,11 +7037,6 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
for (i=0; i<len; i++) {
gc_mark(objspace, ptr[i]);
}
-
- if (LIKELY(during_gc) &&
- RSTRUCT_TRANSIENT_P(obj)) {
- rb_transient_heap_mark(obj, ptr);
- }
}
break;
@@ -7443,7 +7138,6 @@ show_mark_ticks(void)
static void
gc_mark_roots(rb_objspace_t *objspace, const char **categoryp)
{
- struct gc_list *list;
rb_execution_context_t *ec = GET_EC();
rb_vm_t *vm = rb_ec_vm_ptr(ec);
@@ -7493,10 +7187,6 @@ gc_mark_roots(rb_objspace_t *objspace, const char **categoryp)
mark_current_machine_context(objspace, ec);
/* mark protected global variables */
- MARK_CHECKPOINT("global_list");
- for (list = global_list; list; list = list->next) {
- gc_mark_maybe(objspace, *list->varptr);
- }
MARK_CHECKPOINT("end_proc");
rb_mark_end_proc();
@@ -7505,7 +7195,6 @@ gc_mark_roots(rb_objspace_t *objspace, const char **categoryp)
rb_gc_mark_global_tbl();
MARK_CHECKPOINT("object_id");
- rb_gc_mark(objspace->next_object_id);
mark_tbl_no_pin(objspace, objspace->obj_to_id_tbl); /* Only mark ids */
if (stress_to_class) rb_gc_mark(stress_to_class);
@@ -7817,7 +7506,7 @@ check_children_i(const VALUE child, void *ptr)
if (check_rvalue_consistency_force(child, FALSE) != 0) {
fprintf(stderr, "check_children_i: %s has error (referenced from %s)",
obj_info(child), obj_info(data->parent));
- rb_print_backtrace(); /* C backtrace will help to debug */
+ rb_print_backtrace(stderr); /* C backtrace will help to debug */
data->err_count++;
}
@@ -7865,8 +7554,20 @@ verify_internal_consistency_i(void *page_start, void *page_end, size_t stride,
}
else {
if (BUILTIN_TYPE(obj) == T_ZOMBIE) {
- GC_ASSERT((RBASIC(obj)->flags & ~FL_SEEN_OBJ_ID) == T_ZOMBIE);
data->zombie_object_count++;
+
+ if ((RBASIC(obj)->flags & ~ZOMBIE_OBJ_KEPT_FLAGS) != T_ZOMBIE) {
+ fprintf(stderr, "verify_internal_consistency_i: T_ZOMBIE has extra flags set: %s\n",
+ obj_info(obj));
+ data->err_count++;
+ }
+
+ if (!!FL_TEST(obj, FL_FINALIZE) != !!st_is_member(finalizer_table, obj)) {
+ fprintf(stderr, "verify_internal_consistency_i: FL_FINALIZE %s but %s finalizer_table: %s\n",
+ FL_TEST(obj, FL_FINALIZE) ? "set" : "not set", st_is_member(finalizer_table, obj) ? "in" : "not in",
+ obj_info(obj));
+ data->err_count++;
+ }
}
}
if (poisoned) {
@@ -7925,7 +7626,7 @@ gc_verify_heap_page(rb_objspace_t *objspace, struct heap_page *page, VALUE obj)
(void *)page, remembered_old_objects, obj ? obj_info(obj) : "");
}
- if (page->flags.has_uncollectible_shady_objects == FALSE && has_remembered_shady == TRUE) {
+ if (page->flags.has_uncollectible_wb_unprotected_objects == FALSE && has_remembered_shady == TRUE) {
rb_bug("page %p's has_remembered_shady should be false, but there are remembered shady objects. %s",
(void *)page, obj ? obj_info(obj) : "");
}
@@ -8037,9 +7738,8 @@ gc_verify_internal_consistency_(rb_objspace_t *objspace)
!finalizing &&
ruby_single_main_ractor != NULL) {
if (objspace_live_slots(objspace) != data.live_object_count) {
- fprintf(stderr, "heap_pages_final_slots: %"PRIdSIZE", "
- "objspace->profile.total_freed_objects: %"PRIdSIZE"\n",
- heap_pages_final_slots, objspace->profile.total_freed_objects);
+ fprintf(stderr, "heap_pages_final_slots: %"PRIdSIZE", total_freed_objects: %"PRIdSIZE"\n",
+ heap_pages_final_slots, total_freed_objects(objspace));
rb_bug("inconsistent live slot number: expect %"PRIuSIZE", but %"PRIuSIZE".",
objspace_live_slots(objspace), data.live_object_count);
}
@@ -8106,13 +7806,6 @@ rb_gc_verify_internal_consistency(void)
gc_verify_internal_consistency(&rb_objspace);
}
-static VALUE
-gc_verify_transient_heap_internal_consistency(VALUE dmy)
-{
- rb_transient_heap_verify();
- return Qnil;
-}
-
static void
heap_move_pooled_pages_to_free_pages(rb_heap_t *heap)
{
@@ -8164,6 +7857,14 @@ gc_marks_start(rb_objspace_t *objspace, int full_mark)
rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool);
rgengc_mark_and_rememberset_clear(objspace, heap);
heap_move_pooled_pages_to_free_pages(heap);
+
+ if (objspace->flags.during_compacting) {
+ struct heap_page *page = NULL;
+
+ ccan_list_for_each(&heap->pages, page, page_node) {
+ page->pinned_slots = 0;
+ }
+ }
}
}
else {
@@ -8228,6 +7929,37 @@ gc_marks_wb_unprotected_objects(rb_objspace_t *objspace, rb_heap_t *heap)
}
static void
+gc_update_weak_references(rb_objspace_t *objspace)
+{
+ size_t retained_weak_references_count = 0;
+ VALUE **ptr_ptr;
+ rb_darray_foreach(objspace->weak_references, i, ptr_ptr) {
+ if (!*ptr_ptr) continue;
+
+ VALUE obj = **ptr_ptr;
+
+ if (RB_SPECIAL_CONST_P(obj)) continue;
+
+ if (!RVALUE_MARKED(obj)) {
+ **ptr_ptr = Qundef;
+ }
+ else {
+ retained_weak_references_count++;
+ }
+ }
+
+ objspace->profile.retained_weak_references_count = retained_weak_references_count;
+
+ rb_darray_clear(objspace->weak_references);
+
+ DURING_GC_COULD_MALLOC_REGION_START();
+ {
+ rb_darray_resize_capa(&objspace->weak_references, retained_weak_references_count);
+ }
+ DURING_GC_COULD_MALLOC_REGION_END();
+}
+
+static void
gc_marks_finish(rb_objspace_t *objspace)
{
/* finish incremental GC */
@@ -8253,6 +7985,8 @@ gc_marks_finish(rb_objspace_t *objspace)
}
}
+ gc_update_weak_references(objspace);
+
#if RGENGC_CHECK_MODE >= 2
gc_verify_internal_consistency(objspace);
#endif
@@ -8275,9 +8009,14 @@ gc_marks_finish(rb_objspace_t *objspace)
GC_ASSERT(heap_eden_total_slots(objspace) >= objspace->marked_slots);
- /* setup free-able page counts */
- if (max_free_slots < gc_params.heap_init_slots * r_mul) {
- max_free_slots = gc_params.heap_init_slots * r_mul;
+ /* Setup freeable slots. */
+ size_t total_init_slots = 0;
+ for (int i = 0; i < SIZE_POOL_COUNT; i++) {
+ total_init_slots += gc_params.size_pool_init_slots[i] * r_mul;
+ }
+
+ if (max_free_slots < total_init_slots) {
+ max_free_slots = total_init_slots;
}
if (sweep_slots > max_free_slots) {
@@ -8301,7 +8040,7 @@ gc_marks_finish(rb_objspace_t *objspace)
}
else {
gc_report(1, objspace, "gc_marks_finish: next is full GC!!)\n");
- objspace->rgengc.need_major_gc |= GPR_FLAG_MAJOR_BY_NOFREE;
+ gc_needs_major_flags |= GPR_FLAG_MAJOR_BY_NOFREE;
}
}
}
@@ -8317,23 +8056,22 @@ gc_marks_finish(rb_objspace_t *objspace)
}
if (objspace->rgengc.uncollectible_wb_unprotected_objects > objspace->rgengc.uncollectible_wb_unprotected_objects_limit) {
- objspace->rgengc.need_major_gc |= GPR_FLAG_MAJOR_BY_SHADY;
+ gc_needs_major_flags |= GPR_FLAG_MAJOR_BY_SHADY;
}
if (objspace->rgengc.old_objects > objspace->rgengc.old_objects_limit) {
- objspace->rgengc.need_major_gc |= GPR_FLAG_MAJOR_BY_OLDGEN;
+ gc_needs_major_flags |= GPR_FLAG_MAJOR_BY_OLDGEN;
}
if (RGENGC_FORCE_MAJOR_GC) {
- objspace->rgengc.need_major_gc = GPR_FLAG_MAJOR_BY_FORCE;
+ gc_needs_major_flags = GPR_FLAG_MAJOR_BY_FORCE;
}
gc_report(1, objspace, "gc_marks_finish (marks %"PRIdSIZE" objects, "
"old %"PRIdSIZE" objects, total %"PRIdSIZE" slots, "
"sweep %"PRIdSIZE" slots, increment: %"PRIdSIZE", next GC: %s)\n",
objspace->marked_slots, objspace->rgengc.old_objects, heap_eden_total_slots(objspace), sweep_slots, heap_allocatable_pages(objspace),
- objspace->rgengc.need_major_gc ? "major" : "minor");
+ gc_needs_major_flags ? "major" : "minor");
}
- rb_transient_heap_finish_marking();
rb_ractor_finish_marking();
gc_event_hook(objspace, RUBY_INTERNAL_EVENT_GC_END_MARK, 0);
@@ -8378,7 +8116,7 @@ gc_compact_destination_pool(rb_objspace_t *objspace, rb_size_pool_t *src_pool, V
}
if (rb_gc_size_allocatable_p(obj_size)){
- idx = size_pool_idx_for_size(obj_size);
+ idx = rb_gc_size_pool_id_for_size(obj_size);
}
return &size_pools[idx];
}
@@ -8401,7 +8139,7 @@ gc_compact_move(rb_objspace_t *objspace, rb_heap_t *heap, rb_size_pool_t *size_p
if (RB_TYPE_P(src, T_OBJECT)) {
orig_shape = rb_shape_get_shape(src);
if (dheap != heap && !rb_shape_obj_too_complex(src)) {
- rb_shape_t *initial_shape = rb_shape_get_shape_by_id((shape_id_t)((dest_pool - size_pools) + SIZE_POOL_COUNT));
+ rb_shape_t *initial_shape = rb_shape_get_shape_by_id((shape_id_t)((dest_pool - size_pools) + FIRST_T_OBJECT_SHAPE_ID));
new_shape = rb_shape_traverse_from_new_root(initial_shape, orig_shape);
if (!new_shape) {
@@ -8614,6 +8352,7 @@ gc_marks_continue(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t
else {
gc_report(2, objspace, "gc_marks_continue: no more pooled pages (stack depth: %"PRIdSIZE").\n",
mark_stack_size(&objspace->mark_stack));
+ size_pool->force_incremental_marking_finish_count++;
gc_marks_rest(objspace);
}
@@ -8686,12 +8425,6 @@ gc_report_body(int level, rb_objspace_t *objspace, const char *fmt, ...)
/* bit operations */
static int
-rgengc_remembersetbits_get(rb_objspace_t *objspace, VALUE obj)
-{
- return RVALUE_REMEMBERED(obj);
-}
-
-static int
rgengc_remembersetbits_set(rb_objspace_t *objspace, VALUE obj)
{
struct heap_page *page = GET_HEAP_PAGE(obj);
@@ -8714,7 +8447,7 @@ static int
rgengc_remember(rb_objspace_t *objspace, VALUE obj)
{
gc_report(6, objspace, "rgengc_remember: %s %s\n", obj_info(obj),
- rgengc_remembersetbits_get(objspace, obj) ? "was already remembered" : "is remembered now");
+ RVALUE_REMEMBERED(obj) ? "was already remembered" : "is remembered now");
check_rvalue_consistency(obj);
@@ -8723,7 +8456,7 @@ rgengc_remember(rb_objspace_t *objspace, VALUE obj)
}
#if RGENGC_PROFILE > 0
- if (!rgengc_remembered(objspace, obj)) {
+ if (!RVALUE_REMEMBERED(obj)) {
if (RVALUE_WB_UNPROTECTED(obj) == 0) {
objspace->profile.total_remembered_normal_object_count++;
#if RGENGC_PROFILE >= 2
@@ -8736,21 +8469,6 @@ rgengc_remember(rb_objspace_t *objspace, VALUE obj)
return rgengc_remembersetbits_set(objspace, obj);
}
-static int
-rgengc_remembered_sweep(rb_objspace_t *objspace, VALUE obj)
-{
- int result = rgengc_remembersetbits_get(objspace, obj);
- check_rvalue_consistency(obj);
- return result;
-}
-
-static int
-rgengc_remembered(rb_objspace_t *objspace, VALUE obj)
-{
- gc_report(6, objspace, "rgengc_remembered: %s\n", obj_info(obj));
- return rgengc_remembered_sweep(objspace, obj);
-}
-
#ifndef PROFILE_REMEMBERSET_MARK
#define PROFILE_REMEMBERSET_MARK 0
#endif
@@ -8785,16 +8503,16 @@ rgengc_rememberset_mark(rb_objspace_t *objspace, rb_heap_t *heap)
gc_report(1, objspace, "rgengc_rememberset_mark: start\n");
ccan_list_for_each(&heap->pages, page, page_node) {
- if (page->flags.has_remembered_objects | page->flags.has_uncollectible_shady_objects) {
+ if (page->flags.has_remembered_objects | page->flags.has_uncollectible_wb_unprotected_objects) {
uintptr_t p = page->start;
bits_t bitset, bits[HEAP_PAGE_BITMAP_LIMIT];
bits_t *remembered_bits = page->remembered_bits;
bits_t *uncollectible_bits = page->uncollectible_bits;
bits_t *wb_unprotected_bits = page->wb_unprotected_bits;
#if PROFILE_REMEMBERSET_MARK
- if (page->flags.has_remembered_objects && page->flags.has_uncollectible_shady_objects) has_both++;
+ if (page->flags.has_remembered_objects && page->flags.has_uncollectible_wb_unprotected_objects) has_both++;
else if (page->flags.has_remembered_objects) has_old++;
- else if (page->flags.has_uncollectible_shady_objects) has_shady++;
+ else if (page->flags.has_uncollectible_wb_unprotected_objects) has_shady++;
#endif
for (j=0; j<HEAP_PAGE_BITMAP_LIMIT; j++) {
bits[j] = remembered_bits[j] | (uncollectible_bits[j] & wb_unprotected_bits[j]);
@@ -8837,7 +8555,7 @@ rgengc_mark_and_rememberset_clear(rb_objspace_t *objspace, rb_heap_t *heap)
memset(&page->marking_bits[0], 0, HEAP_PAGE_BITMAP_SIZE);
memset(&page->remembered_bits[0], 0, HEAP_PAGE_BITMAP_SIZE);
memset(&page->pinned_bits[0], 0, HEAP_PAGE_BITMAP_SIZE);
- page->flags.has_uncollectible_shady_objects = FALSE;
+ page->flags.has_uncollectible_wb_unprotected_objects = FALSE;
page->flags.has_remembered_objects = FALSE;
}
}
@@ -8855,9 +8573,8 @@ gc_writebarrier_generational(VALUE a, VALUE b, rb_objspace_t *objspace)
if (is_incremental_marking(objspace)) rb_bug("gc_writebarrier_generational: called while incremental marking: %s -> %s", obj_info(a), obj_info(b));
}
-#if 1
/* mark `a' and remember (default behavior) */
- if (!rgengc_remembered(objspace, a)) {
+ if (!RVALUE_REMEMBERED(a)) {
RB_VM_LOCK_ENTER_NO_BARRIER();
{
rgengc_remember(objspace, a);
@@ -8865,19 +8582,6 @@ gc_writebarrier_generational(VALUE a, VALUE b, rb_objspace_t *objspace)
RB_VM_LOCK_LEAVE_NO_BARRIER();
gc_report(1, objspace, "gc_writebarrier_generational: %s (remembered) -> %s\n", obj_info(a), obj_info(b));
}
-#else
- /* mark `b' and remember */
- MARK_IN_BITMAP(GET_HEAP_MARK_BITS(b), b);
- if (RVALUE_WB_UNPROTECTED(b)) {
- gc_remember_unprotected(objspace, b);
- }
- else {
- RVALUE_AGE_SET_OLD(objspace, b);
- rgengc_remember(objspace, b);
- }
-
- gc_report(1, objspace, "gc_writebarrier_generational: %s -> %s (remembered)\n", obj_info(a), obj_info(b));
-#endif
check_rvalue_consistency(a);
check_rvalue_consistency(b);
@@ -8965,7 +8669,7 @@ rb_gc_writebarrier_unprotect(VALUE obj)
rb_objspace_t *objspace = &rb_objspace;
gc_report(2, objspace, "rb_gc_writebarrier_unprotect: %s %s\n", obj_info(obj),
- rgengc_remembered(objspace, obj) ? " (already remembered)" : "");
+ RVALUE_REMEMBERED(obj) ? " (already remembered)" : "");
RB_VM_LOCK_ENTER_NO_BARRIER();
{
@@ -9016,35 +8720,12 @@ rb_gc_writebarrier_remember(VALUE obj)
}
void
-rb_copy_wb_protected_attribute(VALUE dest, VALUE obj)
+rb_gc_copy_attributes(VALUE dest, VALUE obj)
{
- rb_objspace_t *objspace = &rb_objspace;
-
- if (RVALUE_WB_UNPROTECTED(obj) && !RVALUE_WB_UNPROTECTED(dest)) {
- if (!RVALUE_OLD_P(dest)) {
- MARK_IN_BITMAP(GET_HEAP_WB_UNPROTECTED_BITS(dest), dest);
- RVALUE_AGE_RESET_RAW(dest);
- }
- else {
- RVALUE_DEMOTE(objspace, dest);
- }
+ if (RVALUE_WB_UNPROTECTED(obj)) {
+ rb_gc_writebarrier_unprotect(dest);
}
-
- check_rvalue_consistency(dest);
-}
-
-/* RGENGC analysis information */
-
-VALUE
-rb_obj_rgengc_writebarrier_protected_p(VALUE obj)
-{
- return RBOOL(!RVALUE_WB_UNPROTECTED(obj));
-}
-
-VALUE
-rb_obj_rgengc_promoted_p(VALUE obj)
-{
- return RBOOL(OBJ_PROMOTED(obj));
+ rb_gc_copy_finalizer(dest, obj);
}
size_t
@@ -9096,48 +8777,25 @@ rb_gc_ractor_newobj_cache_clear(rb_ractor_newobj_cache_t *newobj_cache)
}
void
-rb_gc_force_recycle(VALUE obj)
-{
- /* no-op */
-}
-
-#ifndef MARK_OBJECT_ARY_BUCKET_SIZE
-#define MARK_OBJECT_ARY_BUCKET_SIZE 1024
-#endif
-
-void
rb_gc_register_mark_object(VALUE obj)
{
if (!is_pointer_to_heap(&rb_objspace, (void *)obj))
return;
- RB_VM_LOCK_ENTER();
- {
- VALUE ary_ary = GET_VM()->mark_object_ary;
- VALUE ary = rb_ary_last(0, 0, ary_ary);
-
- if (NIL_P(ary) || RARRAY_LEN(ary) >= MARK_OBJECT_ARY_BUCKET_SIZE) {
- ary = rb_ary_hidden_new(MARK_OBJECT_ARY_BUCKET_SIZE);
- rb_ary_push(ary_ary, ary);
- }
-
- rb_ary_push(ary, obj);
- }
- RB_VM_LOCK_LEAVE();
+ rb_vm_register_global_object(obj);
}
void
rb_gc_register_address(VALUE *addr)
{
- rb_objspace_t *objspace = &rb_objspace;
- struct gc_list *tmp;
+ rb_vm_t *vm = GET_VM();
VALUE obj = *addr;
- tmp = ALLOC(struct gc_list);
- tmp->next = global_list;
+ struct global_object_list *tmp = ALLOC(struct global_object_list);
+ tmp->next = vm->global_object_list;
tmp->varptr = addr;
- global_list = tmp;
+ vm->global_object_list = tmp;
/*
* Because some C extensions have assignment-then-register bugs,
@@ -9147,24 +8805,24 @@ rb_gc_register_address(VALUE *addr)
if (0 && !SPECIAL_CONST_P(obj)) {
rb_warn("Object is assigned to registering address already: %"PRIsVALUE,
rb_obj_class(obj));
- rb_print_backtrace();
+ rb_print_backtrace(stderr);
}
}
void
rb_gc_unregister_address(VALUE *addr)
{
- rb_objspace_t *objspace = &rb_objspace;
- struct gc_list *tmp = global_list;
+ rb_vm_t *vm = GET_VM();
+ struct global_object_list *tmp = vm->global_object_list;
if (tmp->varptr == addr) {
- global_list = tmp->next;
+ vm->global_object_list = tmp->next;
xfree(tmp);
return;
}
while (tmp->next) {
if (tmp->next->varptr == addr) {
- struct gc_list *t = tmp->next;
+ struct global_object_list *t = tmp->next;
tmp->next = tmp->next->next;
xfree(t);
@@ -9180,8 +8838,6 @@ rb_global_variable(VALUE *var)
rb_gc_register_address(var);
}
-#define GC_NOTIFY 0
-
enum {
gc_stress_no_major,
gc_stress_no_immediate_sweep,
@@ -9255,7 +8911,7 @@ gc_reset_malloc_info(rb_objspace_t *objspace, bool full_mark)
#if RGENGC_ESTIMATE_OLDMALLOC
if (!full_mark) {
if (objspace->rgengc.oldmalloc_increase > objspace->rgengc.oldmalloc_increase_limit) {
- objspace->rgengc.need_major_gc |= GPR_FLAG_MAJOR_BY_OLDMALLOC;
+ gc_needs_major_flags |= GPR_FLAG_MAJOR_BY_OLDMALLOC;
objspace->rgengc.oldmalloc_increase_limit =
(size_t)(objspace->rgengc.oldmalloc_increase_limit * gc_params.oldmalloc_limit_growth_factor);
@@ -9266,7 +8922,7 @@ gc_reset_malloc_info(rb_objspace_t *objspace, bool full_mark)
if (0) fprintf(stderr, "%"PRIdSIZE"\t%d\t%"PRIuSIZE"\t%"PRIuSIZE"\t%"PRIdSIZE"\n",
rb_gc_count(),
- objspace->rgengc.need_major_gc,
+ gc_needs_major_flags,
objspace->rgengc.oldmalloc_increase,
objspace->rgengc.oldmalloc_increase_limit,
gc_params.oldmalloc_limit_max);
@@ -9314,20 +8970,11 @@ static int
gc_start(rb_objspace_t *objspace, unsigned int reason)
{
unsigned int do_full_mark = !!(reason & GPR_FLAG_FULL_MARK);
- unsigned int immediate_mark = reason & GPR_FLAG_IMMEDIATE_MARK;
/* reason may be clobbered, later, so keep set immediate_sweep here */
objspace->flags.immediate_sweep = !!(reason & GPR_FLAG_IMMEDIATE_SWEEP);
- /* Explicitly enable compaction (GC.compact) */
- if (do_full_mark && ruby_enable_autocompact) {
- objspace->flags.during_compacting = TRUE;
- }
- else {
- objspace->flags.during_compacting = !!(reason & GPR_FLAG_COMPACT);
- }
-
- if (!heap_allocated_pages) return FALSE; /* heap is not ready */
+ if (!heap_allocated_pages) return TRUE; /* heap is not ready */
if (!(reason & GPR_FLAG_METHOD) && !ready_to_gc(objspace)) return TRUE; /* GC is not allowed */
GC_ASSERT(gc_mode(objspace) == gc_mode_none);
@@ -9350,30 +8997,42 @@ gc_start(rb_objspace_t *objspace, unsigned int reason)
objspace->flags.immediate_sweep = !(flag & (1<<gc_stress_no_immediate_sweep));
}
- else {
- if (objspace->rgengc.need_major_gc) {
- reason |= objspace->rgengc.need_major_gc;
- do_full_mark = TRUE;
- }
- else if (RGENGC_FORCE_MAJOR_GC) {
- reason = GPR_FLAG_MAJOR_BY_FORCE;
- do_full_mark = TRUE;
- }
- objspace->rgengc.need_major_gc = GPR_FLAG_NONE;
+ if (gc_needs_major_flags) {
+ reason |= gc_needs_major_flags;
+ do_full_mark = TRUE;
+ }
+ else if (RGENGC_FORCE_MAJOR_GC) {
+ reason = GPR_FLAG_MAJOR_BY_FORCE;
+ do_full_mark = TRUE;
}
+ gc_needs_major_flags = GPR_FLAG_NONE;
+
if (do_full_mark && (reason & GPR_FLAG_MAJOR_MASK) == 0) {
reason |= GPR_FLAG_MAJOR_BY_FORCE; /* GC by CAPI, METHOD, and so on. */
}
- if (objspace->flags.dont_incremental || immediate_mark) {
+ if (objspace->flags.dont_incremental ||
+ reason & GPR_FLAG_IMMEDIATE_MARK ||
+ ruby_gc_stressful) {
objspace->flags.during_incremental_marking = FALSE;
}
else {
objspace->flags.during_incremental_marking = do_full_mark;
}
+ /* Explicitly enable compaction (GC.compact) */
+ if (do_full_mark && ruby_enable_autocompact) {
+ objspace->flags.during_compacting = TRUE;
+#if RGENGC_CHECK_MODE
+ objspace->rcompactor.compare_func = ruby_autocompact_compare_func;
+#endif
+ }
+ else {
+ objspace->flags.during_compacting = !!(reason & GPR_FLAG_COMPACT);
+ }
+
if (!GC_ENABLE_LAZY_SWEEP || objspace->flags.dont_incremental) {
objspace->flags.immediate_sweep = TRUE;
}
@@ -9407,11 +9066,12 @@ gc_start(rb_objspace_t *objspace, unsigned int reason)
objspace->profile.count++;
objspace->profile.latest_gc_info = reason;
- objspace->profile.total_allocated_objects_at_gc_start = objspace->total_allocated_objects;
+ objspace->profile.total_allocated_objects_at_gc_start = total_allocated_objects(objspace);
objspace->profile.heap_used_at_gc_start = heap_allocated_pages;
+ objspace->profile.weak_references_count = 0;
+ objspace->profile.retained_weak_references_count = 0;
gc_prof_setup_new_record(objspace, reason);
gc_reset_malloc_info(objspace, do_full_mark);
- rb_transient_heap_start_marking(do_full_mark);
gc_event_hook(objspace, RUBY_INTERNAL_EVENT_GC_START, 0 /* TODO: pass minor/immediate flag? */);
GC_ASSERT(during_gc);
@@ -9431,10 +9091,7 @@ gc_start(rb_objspace_t *objspace, unsigned int reason)
static void
gc_rest(rb_objspace_t *objspace)
{
- int marking = is_incremental_marking(objspace);
- int sweeping = is_lazy_sweeping(objspace);
-
- if (marking || sweeping) {
+ if (is_incremental_marking(objspace) || is_lazy_sweeping(objspace)) {
unsigned int lock_lev;
gc_enter(objspace, gc_enter_event_rest, &lock_lev);
@@ -9562,10 +9219,6 @@ gc_enter_count(enum gc_enter_event event)
}
}
-#ifndef MEASURE_GC
-#define MEASURE_GC (objspace->flags.measure_gc)
-#endif
-
static bool current_process_time(struct timespec *ts);
static void
@@ -9612,7 +9265,7 @@ gc_enter(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_
gc_enter_count(event);
if (UNLIKELY(during_gc != 0)) rb_bug("during_gc != 0");
- if (RGENGC_CHECK_MODE >= 3) gc_verify_internal_consistency(objspace);
+ if (RGENGC_CHECK_MODE >= 3 && (dont_gc_val() == 0)) gc_verify_internal_consistency(objspace);
during_gc = TRUE;
RUBY_DEBUG_LOG("%s (%s)",gc_enter_event_cstr(event), gc_current_status(objspace));
@@ -9635,12 +9288,18 @@ gc_exit(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_l
RB_VM_LOCK_LEAVE_LEV(lock_lev);
}
+#ifndef MEASURE_GC
+#define MEASURE_GC (objspace->flags.measure_gc)
+#endif
+
static void
gc_marking_enter(rb_objspace_t *objspace)
{
GC_ASSERT(during_gc != 0);
- gc_clock_start(&objspace->profile.marking_start_time);
+ if (MEASURE_GC) {
+ gc_clock_start(&objspace->profile.marking_start_time);
+ }
}
static void
@@ -9648,7 +9307,9 @@ gc_marking_exit(rb_objspace_t *objspace)
{
GC_ASSERT(during_gc != 0);
- objspace->profile.marking_time_ns += gc_clock_end(&objspace->profile.marking_start_time);
+ if (MEASURE_GC) {
+ objspace->profile.marking_time_ns += gc_clock_end(&objspace->profile.marking_start_time);
+ }
}
static void
@@ -9656,7 +9317,9 @@ gc_sweeping_enter(rb_objspace_t *objspace)
{
GC_ASSERT(during_gc != 0);
- gc_clock_start(&objspace->profile.sweeping_start_time);
+ if (MEASURE_GC) {
+ gc_clock_start(&objspace->profile.sweeping_start_time);
+ }
}
static void
@@ -9664,7 +9327,9 @@ gc_sweeping_exit(rb_objspace_t *objspace)
{
GC_ASSERT(during_gc != 0);
- objspace->profile.sweeping_time_ns += gc_clock_end(&objspace->profile.sweeping_start_time);
+ if (MEASURE_GC) {
+ objspace->profile.sweeping_time_ns += gc_clock_end(&objspace->profile.sweeping_start_time);
+ }
}
static void *
@@ -9696,6 +9361,33 @@ garbage_collect_with_gvl(rb_objspace_t *objspace, unsigned int reason)
}
}
+static int
+gc_set_candidate_object_i(void *vstart, void *vend, size_t stride, void *data)
+{
+ rb_objspace_t *objspace = &rb_objspace;
+ VALUE v = (VALUE)vstart;
+ for (; v != (VALUE)vend; v += stride) {
+ asan_unpoisoning_object(v) {
+ switch (BUILTIN_TYPE(v)) {
+ case T_NONE:
+ case T_ZOMBIE:
+ break;
+ case T_STRING:
+ // precompute the string coderange. This both save time for when it will be
+ // eventually needed, and avoid mutating heap pages after a potential fork.
+ rb_enc_str_coderange(v);
+ // fall through
+ default:
+ if (!RVALUE_OLD_P(v) && !RVALUE_WB_UNPROTECTED(v)) {
+ RVALUE_AGE_SET_CANDIDATE(objspace, v);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
static VALUE
gc_start_internal(rb_execution_context_t *ec, VALUE self, VALUE full_mark, VALUE immediate_mark, VALUE immediate_sweep, VALUE compact)
{
@@ -9723,6 +9415,61 @@ gc_start_internal(rb_execution_context_t *ec, VALUE self, VALUE full_mark, VALUE
return Qnil;
}
+static void
+free_empty_pages(void)
+{
+ rb_objspace_t *objspace = &rb_objspace;
+
+ for (int i = 0; i < SIZE_POOL_COUNT; i++) {
+ /* Move all empty pages to the tomb heap for freeing. */
+ rb_size_pool_t *size_pool = &size_pools[i];
+ rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool);
+ rb_heap_t *tomb_heap = SIZE_POOL_TOMB_HEAP(size_pool);
+
+ size_t freed_pages = 0;
+
+ struct heap_page **next_page_ptr = &heap->free_pages;
+ struct heap_page *page = heap->free_pages;
+ while (page) {
+ /* All finalizers should have been ran in gc_start_internal, so there
+ * should be no objects that require finalization. */
+ GC_ASSERT(page->final_slots == 0);
+
+ struct heap_page *next_page = page->free_next;
+
+ if (page->free_slots == page->total_slots) {
+ heap_unlink_page(objspace, heap, page);
+ heap_add_page(objspace, size_pool, tomb_heap, page);
+ freed_pages++;
+ }
+ else {
+ *next_page_ptr = page;
+ next_page_ptr = &page->free_next;
+ }
+
+ page = next_page;
+ }
+
+ *next_page_ptr = NULL;
+
+ size_pool_allocatable_pages_set(objspace, size_pool, size_pool->allocatable_pages + freed_pages);
+ }
+
+ heap_pages_free_unused_pages(objspace);
+}
+
+void
+rb_gc_prepare_heap(void)
+{
+ rb_objspace_each_objects(gc_set_candidate_object_i, NULL);
+ gc_start_internal(NULL, Qtrue, Qtrue, Qtrue, Qtrue, Qtrue);
+ free_empty_pages();
+
+#if defined(HAVE_MALLOC_TRIM) && !defined(RUBY_ALTERNATIVE_MALLOC_HEADER)
+ malloc_trim(0);
+#endif
+}
+
static int
gc_is_moveable_obj(rb_objspace_t *objspace, VALUE obj)
{
@@ -9730,12 +9477,11 @@ gc_is_moveable_obj(rb_objspace_t *objspace, VALUE obj)
switch (BUILTIN_TYPE(obj)) {
case T_NONE:
- case T_NIL:
case T_MOVED:
case T_ZOMBIE:
return FALSE;
case T_SYMBOL:
- if (DYNAMIC_SYM_P(obj) && (RSYMBOL(obj)->id & ~ID_SCOPE_MASK)) {
+ if (RSYMBOL(obj)->id & ~ID_SCOPE_MASK) {
return FALSE;
}
/* fall through */
@@ -9780,21 +9526,13 @@ gc_is_moveable_obj(rb_objspace_t *objspace, VALUE obj)
return FALSE;
}
-/* Used in places that could malloc, which can cause the GC to run. We need to
- * temporarily disable the GC to allow the malloc to happen. */
-#define COULD_MALLOC_REGION_START() \
- GC_ASSERT(during_gc); \
- VALUE _already_disabled = rb_gc_disable_no_rest(); \
-
-#define COULD_MALLOC_REGION_END() \
- if (_already_disabled == Qfalse) rb_objspace_gc_enable(objspace);
-
static VALUE
gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t src_slot_size, size_t slot_size)
{
int marked;
int wb_unprotected;
int uncollectible;
+ int age;
RVALUE *dest = (RVALUE *)free;
RVALUE *src = (RVALUE *)scan;
@@ -9806,10 +9544,11 @@ gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t src_slot_size, s
GC_ASSERT(!RVALUE_MARKING((VALUE)src));
/* Save off bits for current object. */
- marked = rb_objspace_marked_object_p((VALUE)src);
+ marked = RVALUE_MARKED((VALUE)src);
wb_unprotected = RVALUE_WB_UNPROTECTED((VALUE)src);
uncollectible = RVALUE_UNCOLLECTIBLE((VALUE)src);
bool remembered = RVALUE_REMEMBERED((VALUE)src);
+ age = RVALUE_AGE_GET((VALUE)src);
/* Clear bits for eventual T_MOVED */
CLEAR_IN_BITMAP(GET_HEAP_MARK_BITS((VALUE)src), (VALUE)src);
@@ -9819,26 +9558,32 @@ gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t src_slot_size, s
if (FL_TEST((VALUE)src, FL_EXIVAR)) {
/* Resizing the st table could cause a malloc */
- COULD_MALLOC_REGION_START();
+ DURING_GC_COULD_MALLOC_REGION_START();
{
rb_mv_generic_ivar((VALUE)src, (VALUE)dest);
}
- COULD_MALLOC_REGION_END();
+ DURING_GC_COULD_MALLOC_REGION_END();
}
- st_data_t srcid = (st_data_t)src, id;
+ if (FL_TEST((VALUE)src, FL_SEEN_OBJ_ID)) {
+ /* If the source object's object_id has been seen, we need to update
+ * the object to object id mapping. */
+ st_data_t srcid = (st_data_t)src, id;
- /* If the source object's object_id has been seen, we need to update
- * the object to object id mapping. */
- if (st_lookup(objspace->obj_to_id_tbl, srcid, &id)) {
gc_report(4, objspace, "Moving object with seen id: %p -> %p\n", (void *)src, (void *)dest);
/* Resizing the st table could cause a malloc */
- COULD_MALLOC_REGION_START();
+ DURING_GC_COULD_MALLOC_REGION_START();
{
- st_delete(objspace->obj_to_id_tbl, &srcid, 0);
+ if (!st_delete(objspace->obj_to_id_tbl, &srcid, &id)) {
+ rb_bug("gc_move: object ID seen, but not in mapping table: %s", obj_info((VALUE)src));
+ }
+
st_insert(objspace->obj_to_id_tbl, (st_data_t)dest, id);
}
- COULD_MALLOC_REGION_END();
+ DURING_GC_COULD_MALLOC_REGION_END();
+ }
+ else {
+ GC_ASSERT(!st_lookup(objspace->obj_to_id_tbl, (st_data_t)src, NULL));
}
/* Move the object */
@@ -9852,6 +9597,7 @@ gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t src_slot_size, s
}
memset(src, 0, src_slot_size);
+ RVALUE_AGE_RESET((VALUE)src);
/* Set bits for object in new location */
if (remembered) {
@@ -9882,6 +9628,7 @@ gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t src_slot_size, s
CLEAR_IN_BITMAP(GET_HEAP_UNCOLLECTIBLE_BITS((VALUE)dest), (VALUE)dest);
}
+ RVALUE_AGE_SET((VALUE)dest, age);
/* Assign forwarding address */
src->as.moved.flags = T_MOVED;
src->as.moved.dummy = Qundef;
@@ -9893,6 +9640,18 @@ gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t src_slot_size, s
#if GC_CAN_COMPILE_COMPACTION
static int
+compare_pinned_slots(const void *left, const void *right, void *dummy)
+{
+ struct heap_page *left_page;
+ struct heap_page *right_page;
+
+ left_page = *(struct heap_page * const *)left;
+ right_page = *(struct heap_page * const *)right;
+
+ return left_page->pinned_slots - right_page->pinned_slots;
+}
+
+static int
compare_free_slots(const void *left, const void *right, void *dummy)
{
struct heap_page *left_page;
@@ -9905,7 +9664,7 @@ compare_free_slots(const void *left, const void *right, void *dummy)
}
static void
-gc_sort_heap_by_empty_slots(rb_objspace_t *objspace)
+gc_sort_heap_by_compare_func(rb_objspace_t *objspace, gc_compact_compare_func compare_func)
{
for (int j = 0; j < SIZE_POOL_COUNT; j++) {
rb_size_pool_t *size_pool = &size_pools[j];
@@ -9925,7 +9684,7 @@ gc_sort_heap_by_empty_slots(rb_objspace_t *objspace)
/* Sort the heap so "filled pages" are first. `heap_add_page` adds to the
* head of the list, so empty pages will end up at the start of the heap */
- ruby_qsort(page_list, total_pages, sizeof(struct heap_page *), compare_free_slots, NULL);
+ ruby_qsort(page_list, total_pages, sizeof(struct heap_page *), compare_func, NULL);
/* Reset the eden heap */
ccan_list_head_init(&SIZE_POOL_EDEN_HEAP(size_pool)->pages);
@@ -9962,7 +9721,7 @@ gc_ref_update_array(rb_objspace_t * objspace, VALUE v)
long len = RARRAY_LEN(v);
if (len > 0) {
- VALUE *ptr = (VALUE *)RARRAY_CONST_PTR_TRANSIENT(v);
+ VALUE *ptr = (VALUE *)RARRAY_CONST_PTR(v);
for (long i = 0; i < len; i++) {
UPDATE_IF_MOVED(objspace, ptr[i]);
}
@@ -9976,13 +9735,15 @@ gc_ref_update_array(rb_objspace_t * objspace, VALUE v)
}
}
+static void gc_ref_update_table_values_only(rb_objspace_t *objspace, st_table *tbl);
+
static void
gc_ref_update_object(rb_objspace_t *objspace, VALUE v)
{
VALUE *ptr = ROBJECT_IVPTR(v);
if (rb_shape_obj_too_complex(v)) {
- rb_gc_update_tbl_refs(ROBJECT_IV_HASH(v));
+ gc_ref_update_table_values_only(objspace, ROBJECT_IV_HASH(v));
return;
}
@@ -9992,12 +9753,7 @@ gc_ref_update_object(rb_objspace_t *objspace, VALUE v)
// Object can be re-embedded
memcpy(ROBJECT(v)->as.ary, ptr, sizeof(VALUE) * ROBJECT_IV_COUNT(v));
RB_FL_SET_RAW(v, ROBJECT_EMBED);
- if (ROBJ_TRANSIENT_P(v)) {
- ROBJ_TRANSIENT_UNSET(v);
- }
- else {
- xfree(ptr);
- }
+ xfree(ptr);
ptr = ROBJECT(v)->as.ary;
}
@@ -10065,7 +9821,7 @@ hash_foreach_replace_value(st_data_t key, st_data_t value, st_data_t argp, int e
}
static void
-gc_update_tbl_refs(rb_objspace_t * objspace, st_table *tbl)
+gc_ref_update_table_values_only(rb_objspace_t *objspace, st_table *tbl)
{
if (!tbl || tbl->num_entries == 0) return;
@@ -10074,6 +9830,12 @@ gc_update_tbl_refs(rb_objspace_t * objspace, st_table *tbl)
}
}
+void
+rb_gc_ref_update_table_values_only(st_table *tbl)
+{
+ gc_ref_update_table_values_only(&rb_objspace, tbl);
+}
+
static void
gc_update_table_refs(rb_objspace_t * objspace, st_table *tbl)
{
@@ -10084,7 +9846,7 @@ gc_update_table_refs(rb_objspace_t * objspace, st_table *tbl)
}
}
-/* Update MOVED references in an st_table */
+/* Update MOVED references in a VALUE=>VALUE st_table */
void
rb_gc_update_tbl_refs(st_table *ptr)
{
@@ -10099,47 +9861,6 @@ gc_ref_update_hash(rb_objspace_t * objspace, VALUE v)
}
static void
-gc_ref_update_method_entry(rb_objspace_t *objspace, rb_method_entry_t *me)
-{
- rb_method_definition_t *def = me->def;
-
- UPDATE_IF_MOVED(objspace, me->owner);
- UPDATE_IF_MOVED(objspace, me->defined_class);
-
- if (def) {
- switch (def->type) {
- case VM_METHOD_TYPE_ISEQ:
- if (def->body.iseq.iseqptr) {
- TYPED_UPDATE_IF_MOVED(objspace, rb_iseq_t *, def->body.iseq.iseqptr);
- }
- TYPED_UPDATE_IF_MOVED(objspace, rb_cref_t *, def->body.iseq.cref);
- break;
- case VM_METHOD_TYPE_ATTRSET:
- case VM_METHOD_TYPE_IVAR:
- UPDATE_IF_MOVED(objspace, def->body.attr.location);
- break;
- case VM_METHOD_TYPE_BMETHOD:
- UPDATE_IF_MOVED(objspace, def->body.bmethod.proc);
- break;
- case VM_METHOD_TYPE_ALIAS:
- TYPED_UPDATE_IF_MOVED(objspace, struct rb_method_entry_struct *, def->body.alias.original_me);
- return;
- case VM_METHOD_TYPE_REFINED:
- TYPED_UPDATE_IF_MOVED(objspace, struct rb_method_entry_struct *, def->body.refined.orig_me);
- UPDATE_IF_MOVED(objspace, def->body.refined.owner);
- break;
- case VM_METHOD_TYPE_CFUNC:
- case VM_METHOD_TYPE_ZSUPER:
- case VM_METHOD_TYPE_MISSING:
- case VM_METHOD_TYPE_OPTIMIZED:
- case VM_METHOD_TYPE_UNDEF:
- case VM_METHOD_TYPE_NOTIMPLEMENTED:
- break;
- }
- }
-}
-
-static void
gc_update_values(rb_objspace_t *objspace, long n, VALUE *values)
{
long i;
@@ -10149,82 +9870,10 @@ gc_update_values(rb_objspace_t *objspace, long n, VALUE *values)
}
}
-static void
-gc_ref_update_imemo(rb_objspace_t *objspace, VALUE obj)
+void
+rb_gc_update_values(long n, VALUE *values)
{
- switch (imemo_type(obj)) {
- case imemo_env:
- {
- rb_env_t *env = (rb_env_t *)obj;
- if (LIKELY(env->ep)) {
- // just after newobj() can be NULL here.
- TYPED_UPDATE_IF_MOVED(objspace, rb_iseq_t *, env->iseq);
- UPDATE_IF_MOVED(objspace, env->ep[VM_ENV_DATA_INDEX_ENV]);
- gc_update_values(objspace, (long)env->env_size, (VALUE *)env->env);
- }
- }
- break;
- case imemo_cref:
- UPDATE_IF_MOVED(objspace, RANY(obj)->as.imemo.cref.klass_or_self);
- TYPED_UPDATE_IF_MOVED(objspace, struct rb_cref_struct *, RANY(obj)->as.imemo.cref.next);
- UPDATE_IF_MOVED(objspace, RANY(obj)->as.imemo.cref.refinements);
- break;
- case imemo_svar:
- UPDATE_IF_MOVED(objspace, RANY(obj)->as.imemo.svar.cref_or_me);
- UPDATE_IF_MOVED(objspace, RANY(obj)->as.imemo.svar.lastline);
- UPDATE_IF_MOVED(objspace, RANY(obj)->as.imemo.svar.backref);
- UPDATE_IF_MOVED(objspace, RANY(obj)->as.imemo.svar.others);
- break;
- case imemo_throw_data:
- UPDATE_IF_MOVED(objspace, RANY(obj)->as.imemo.throw_data.throw_obj);
- break;
- case imemo_ifunc:
- break;
- case imemo_memo:
- UPDATE_IF_MOVED(objspace, RANY(obj)->as.imemo.memo.v1);
- UPDATE_IF_MOVED(objspace, RANY(obj)->as.imemo.memo.v2);
- break;
- case imemo_ment:
- gc_ref_update_method_entry(objspace, &RANY(obj)->as.imemo.ment);
- break;
- case imemo_iseq:
- rb_iseq_mark_and_move((rb_iseq_t *)obj, true);
- break;
- case imemo_ast:
- rb_ast_update_references((rb_ast_t *)obj);
- break;
- case imemo_callcache:
- {
- const struct rb_callcache *cc = (const struct rb_callcache *)obj;
- if (cc->klass) {
- UPDATE_IF_MOVED(objspace, cc->klass);
- if (!is_live_object(objspace, cc->klass)) {
- *((VALUE *)(&cc->klass)) = (VALUE)0;
- }
- }
-
- if (cc->cme_) {
- TYPED_UPDATE_IF_MOVED(objspace, struct rb_callable_method_entry_struct *, cc->cme_);
- if (!is_live_object(objspace, (VALUE)cc->cme_)) {
- *((struct rb_callable_method_entry_struct **)(&cc->cme_)) = (struct rb_callable_method_entry_struct *)0;
- }
- }
- }
- break;
- case imemo_constcache:
- {
- const struct iseq_inline_constant_cache_entry *ice = (struct iseq_inline_constant_cache_entry *)obj;
- UPDATE_IF_MOVED(objspace, ice->value);
- }
- break;
- case imemo_parser_strterm:
- case imemo_tmpbuf:
- case imemo_callinfo:
- break;
- default:
- rb_bug("not reachable %d", imemo_type(obj));
- break;
- }
+ gc_update_values(&rb_objspace, n, values);
}
static enum rb_id_table_iterator_result
@@ -10303,9 +9952,6 @@ update_cc_tbl_i(VALUE ccs_ptr, void *data)
}
for (int i=0; i<ccs->len; i++) {
- if (gc_object_moved_p(objspace, (VALUE)ccs->entries[i].ci)) {
- ccs->entries[i].ci = (struct rb_callinfo *)rb_gc_location((VALUE)ccs->entries[i].ci);
- }
if (gc_object_moved_p(objspace, (VALUE)ccs->entries[i].cc)) {
ccs->entries[i].cc = (struct rb_callcache *)rb_gc_location((VALUE)ccs->entries[i].cc);
}
@@ -10353,12 +9999,13 @@ update_cvc_tbl(rb_objspace_t *objspace, VALUE klass)
static enum rb_id_table_iterator_result
mark_cvc_tbl_i(VALUE cvc_entry, void *data)
{
+ rb_objspace_t *objspace = (rb_objspace_t *)data;
struct rb_cvar_class_tbl_entry *entry;
entry = (struct rb_cvar_class_tbl_entry *)cvc_entry;
RUBY_ASSERT(entry->cref == 0 || (BUILTIN_TYPE((VALUE)entry->cref) == T_IMEMO && IMEMO_TYPE_P(entry->cref, imemo_cref)));
- rb_gc_mark((VALUE) entry->cref);
+ gc_mark(objspace, (VALUE) entry->cref);
return ID_TABLE_CONTINUE;
}
@@ -10432,7 +10079,7 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
gc_report(4, objspace, "update-refs: %p ->\n", (void *)obj);
if (FL_TEST(obj, FL_EXIVAR)) {
- rb_mark_and_update_generic_ivar(obj);
+ rb_ref_update_generic_ivar(obj);
}
switch (BUILTIN_TYPE(obj)) {
@@ -10445,14 +10092,18 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
if (RCLASS_SUPER((VALUE)obj)) {
UPDATE_IF_MOVED(objspace, RCLASS(obj)->super);
}
- if (!RCLASS_EXT(obj)) break;
update_m_tbl(objspace, RCLASS_M_TBL(obj));
update_cc_tbl(objspace, obj);
update_cvc_tbl(objspace, obj);
update_superclasses(objspace, obj);
- for (attr_index_t i = 0; i < RCLASS_IV_COUNT(obj); i++) {
- UPDATE_IF_MOVED(objspace, RCLASS_IVPTR(obj)[i]);
+ if (rb_shape_obj_too_complex(obj)) {
+ gc_ref_update_table_values_only(objspace, RCLASS_IV_HASH(obj));
+ }
+ else {
+ for (attr_index_t i = 0; i < RCLASS_IV_COUNT(obj); i++) {
+ UPDATE_IF_MOVED(objspace, RCLASS_IVPTR(obj)[i]);
+ }
}
update_class_ext(objspace, RCLASS_EXT(obj));
@@ -10462,21 +10113,19 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
break;
case T_ICLASS:
- if (FL_TEST(obj, RICLASS_IS_ORIGIN) &&
- !FL_TEST(obj, RICLASS_ORIGIN_SHARED_MTBL)) {
+ if (RICLASS_OWNS_M_TBL_P(obj)) {
update_m_tbl(objspace, RCLASS_M_TBL(obj));
}
if (RCLASS_SUPER((VALUE)obj)) {
UPDATE_IF_MOVED(objspace, RCLASS(obj)->super);
}
- if (!RCLASS_EXT(obj)) break;
update_class_ext(objspace, RCLASS_EXT(obj));
update_m_tbl(objspace, RCLASS_CALLABLE_M_TBL(obj));
update_cc_tbl(objspace, obj);
break;
case T_IMEMO:
- gc_ref_update_imemo(objspace, obj);
+ rb_imemo_mark_and_move(obj, true);
return;
case T_NIL:
@@ -10499,10 +10148,7 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
case T_STRING:
{
if (STR_SHARED_P(obj)) {
- VALUE old_root = any->as.string.as.heap.aux.shared;
UPDATE_IF_MOVED(objspace, any->as.string.as.heap.aux.shared);
- VALUE new_root = any->as.string.as.heap.aux.shared;
- rb_str_update_shared_ary(obj, old_root, new_root);
}
/* If, after move the string is not embedded, and can fit in the
@@ -10518,10 +10164,16 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
case T_DATA:
/* Call the compaction callback, if it exists */
{
- void *const ptr = DATA_PTR(obj);
+ void *const ptr = RTYPEDDATA_P(obj) ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj);
if (ptr) {
if (RTYPEDDATA_P(obj) && gc_declarative_marking_p(any->as.typeddata.type)) {
- gc_ref_update_from_offset(objspace, obj);
+ size_t *offset_list = (size_t *)RANY(obj)->as.typeddata.type->function.dmark;
+
+ for (size_t offset = *offset_list; offset != RUBY_REF_END; offset = *offset_list++) {
+ VALUE *ref = (VALUE *)((char *)ptr + offset);
+ if (SPECIAL_CONST_P(*ref)) continue;
+ *ref = rb_gc_location(*ref);
+ }
}
else if (RTYPEDDATA_P(obj)) {
RUBY_DATA_FUNC compact_func = any->as.typeddata.type->function.dcompact;
@@ -10551,9 +10203,7 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
break;
case T_SYMBOL:
- if (DYNAMIC_SYM_P((VALUE)any)) {
- UPDATE_IF_MOVED(objspace, RSYMBOL(any)->fstr);
- }
+ UPDATE_IF_MOVED(objspace, RSYMBOL(any)->fstr);
break;
case T_FLOAT:
@@ -10610,7 +10260,7 @@ gc_ref_update(void *vstart, void *vend, size_t stride, rb_objspace_t * objspace,
VALUE v = (VALUE)vstart;
asan_unlock_freelist(page);
asan_lock_freelist(page);
- page->flags.has_uncollectible_shady_objects = FALSE;
+ page->flags.has_uncollectible_wb_unprotected_objects = FALSE;
page->flags.has_remembered_objects = FALSE;
/* For each object on the page */
@@ -10624,7 +10274,7 @@ gc_ref_update(void *vstart, void *vend, size_t stride, rb_objspace_t * objspace,
break;
default:
if (RVALUE_WB_UNPROTECTED(v)) {
- page->flags.has_uncollectible_shady_objects = TRUE;
+ page->flags.has_uncollectible_wb_unprotected_objects = TRUE;
}
if (RVALUE_REMEMBERED(v)) {
page->flags.has_remembered_objects = TRUE;
@@ -10679,11 +10329,10 @@ gc_update_references(rb_objspace_t *objspace)
}
}
rb_vm_update_references(vm);
- rb_transient_heap_update_references();
rb_gc_update_global_tbl();
global_symbols.ids = rb_gc_location(global_symbols.ids);
global_symbols.dsymbol_fstr_hash = rb_gc_location(global_symbols.dsymbol_fstr_hash);
- gc_update_tbl_refs(objspace, objspace->obj_to_id_tbl);
+ gc_ref_update_table_values_only(objspace, objspace->obj_to_id_tbl);
gc_update_table_refs(objspace, objspace->id_to_obj_tbl);
gc_update_table_refs(objspace, global_symbols.str_sym);
gc_update_table_refs(objspace, finalizer_table);
@@ -10698,11 +10347,23 @@ gc_update_references(rb_objspace_t *objspace)
*
* Returns information about object moved in the most recent \GC compaction.
*
- * The returned hash has two keys :considered and :moved. The hash for
- * :considered lists the number of objects that were considered for movement
- * by the compactor, and the :moved hash lists the number of objects that
- * were actually moved. Some objects can't be moved (maybe they were pinned)
- * so these numbers can be used to calculate compaction efficiency.
+ * The returned +hash+ contains the following keys:
+ *
+ * [considered]
+ * Hash containing the type of the object as the key and the number of
+ * objects of that type that were considered for movement.
+ * [moved]
+ * Hash containing the type of the object as the key and the number of
+ * objects of that type that were actually moved.
+ * [moved_up]
+ * Hash containing the type of the object as the key and the number of
+ * objects of that type that were increased in size.
+ * [moved_down]
+ * Hash containing the type of the object as the key and the number of
+ * objects of that type that were decreased in size.
+ *
+ * Some objects can't be moved (due to pinning) so these numbers can be used to
+ * calculate compaction efficiency.
*/
static VALUE
gc_compact_stats(VALUE self)
@@ -10748,7 +10409,9 @@ gc_compact_stats(VALUE self)
static void
root_obj_check_moved_i(const char *category, VALUE obj, void *data)
{
- if (gc_object_moved_p(&rb_objspace, obj)) {
+ rb_objspace_t *objspace = data;
+
+ if (gc_object_moved_p(objspace, obj)) {
rb_bug("ROOT %s points to MOVED: %p -> %s", category, (void *)obj, obj_info(rb_gc_location(obj)));
}
}
@@ -10765,9 +10428,11 @@ reachable_object_check_moved_i(VALUE ref, void *data)
static int
heap_check_moved_i(void *vstart, void *vend, size_t stride, void *data)
{
+ rb_objspace_t *objspace = data;
+
VALUE v = (VALUE)vstart;
for (; v != (VALUE)vend; v += stride) {
- if (gc_object_moved_p(&rb_objspace, v)) {
+ if (gc_object_moved_p(objspace, v)) {
/* Moved object still on the heap, something may have a reference. */
}
else {
@@ -10795,16 +10460,16 @@ heap_check_moved_i(void *vstart, void *vend, size_t stride, void *data)
/*
* call-seq:
- * GC.compact
+ * GC.compact -> hash
*
- * This function compacts objects together in Ruby's heap. It eliminates
+ * This function compacts objects together in Ruby's heap. It eliminates
* unused space (or fragmentation) in the heap by moving objects in to that
- * unused space. This function returns a hash which contains statistics about
- * which objects were moved. See <tt>GC.latest_gc_info</tt> for details about
- * compaction statistics.
+ * unused space.
+ *
+ * The returned +hash+ contains statistics about the objects that were moved;
+ * see GC.latest_compact_info.
*
- * This method is implementation specific and not expected to be implemented
- * in any implementation besides MRI.
+ * This method is only expected to work on CRuby.
*
* To test whether \GC compaction is supported, use the idiom:
*
@@ -10823,6 +10488,39 @@ gc_compact(VALUE self)
#endif
#if GC_CAN_COMPILE_COMPACTION
+
+struct desired_compaction_pages_i_data {
+ rb_objspace_t *objspace;
+ size_t required_slots[SIZE_POOL_COUNT];
+};
+
+static int
+desired_compaction_pages_i(struct heap_page *page, void *data)
+{
+ struct desired_compaction_pages_i_data *tdata = data;
+ rb_objspace_t *objspace = tdata->objspace;
+ VALUE vstart = (VALUE)page->start;
+ VALUE vend = vstart + (VALUE)(page->total_slots * page->size_pool->slot_size);
+
+
+ for (VALUE v = vstart; v != vend; v += page->size_pool->slot_size) {
+ /* skip T_NONEs; they won't be moved */
+ void *poisoned = asan_unpoison_object_temporary(v);
+ if (BUILTIN_TYPE(v) == T_NONE) {
+ if (poisoned) {
+ asan_poison_object(v);
+ }
+ continue;
+ }
+
+ rb_size_pool_t *dest_pool = gc_compact_destination_pool(objspace, page->size_pool, v);
+ size_t dest_pool_idx = dest_pool - size_pools;
+ tdata->required_slots[dest_pool_idx]++;
+ }
+
+ return 0;
+}
+
static VALUE
gc_verify_compaction_references(rb_execution_context_t *ec, VALUE self, VALUE double_heap, VALUE expand_heap, VALUE toward_empty)
{
@@ -10830,7 +10528,6 @@ gc_verify_compaction_references(rb_execution_context_t *ec, VALUE self, VALUE do
/* Clear the heap. */
gc_start_internal(NULL, self, Qtrue, Qtrue, Qtrue, Qfalse);
- size_t growth_slots = gc_params.heap_init_slots;
if (RTEST(double_heap)) {
rb_warn("double_heap is deprecated, please use expand_heap instead");
@@ -10841,32 +10538,69 @@ gc_verify_compaction_references(rb_execution_context_t *ec, VALUE self, VALUE do
gc_rest(objspace);
/* if both double_heap and expand_heap are set, expand_heap takes precedence */
- if (RTEST(double_heap) || RTEST(expand_heap)) {
+ if (RTEST(expand_heap)) {
+ struct desired_compaction_pages_i_data desired_compaction = {
+ .objspace = objspace,
+ .required_slots = {0},
+ };
+ /* Work out how many objects want to be in each size pool, taking account of moves */
+ objspace_each_pages(objspace, desired_compaction_pages_i, &desired_compaction, TRUE);
+
+ /* Find out which pool has the most pages */
+ size_t max_existing_pages = 0;
+ for (int i = 0; i < SIZE_POOL_COUNT; i++) {
+ rb_size_pool_t *size_pool = &size_pools[i];
+ rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool);
+ max_existing_pages = MAX(max_existing_pages, heap->total_pages);
+ }
+ /* Add pages to each size pool so that compaction is guaranteed to move every object */
for (int i = 0; i < SIZE_POOL_COUNT; i++) {
rb_size_pool_t *size_pool = &size_pools[i];
rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool);
- if (RTEST(expand_heap)) {
- size_t required_pages = growth_slots / size_pool->slot_size;
- heap_add_pages(objspace, size_pool, heap, MAX(required_pages, heap->total_pages));
- }
- else {
- heap_add_pages(objspace, size_pool, heap, heap->total_pages);
- }
+ size_t pages_to_add = 0;
+ /*
+ * Step 1: Make sure every pool has the same number of pages, by adding empty pages
+ * to smaller pools. This is required to make sure the compact cursor can advance
+ * through all of the pools in `gc_sweep_compact` without hitting the "sweep &
+ * compact cursors met" condition on some pools before fully compacting others
+ */
+ pages_to_add += max_existing_pages - heap->total_pages;
+ /*
+ * Step 2: Now add additional free pages to each size pool sufficient to hold all objects
+ * that want to be in that size pool, whether moved into it or moved within it
+ */
+ pages_to_add += slots_to_pages_for_size_pool(objspace, size_pool, desired_compaction.required_slots[i]);
+ /*
+ * Step 3: Add two more pages so that the compact & sweep cursors will meet _after_ all objects
+ * have been moved, and not on the last iteration of the `gc_sweep_compact` loop
+ */
+ pages_to_add += 2;
+
+ heap_add_pages(objspace, size_pool, heap, pages_to_add);
+ }
+ }
+ else if (RTEST(double_heap)) {
+ for (int i = 0; i < SIZE_POOL_COUNT; i++) {
+ rb_size_pool_t *size_pool = &size_pools[i];
+ rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool);
+ heap_add_pages(objspace, size_pool, heap, heap->total_pages);
}
+
}
if (RTEST(toward_empty)) {
- gc_sort_heap_by_empty_slots(objspace);
+ objspace->rcompactor.compare_func = compare_free_slots;
}
}
RB_VM_LOCK_LEAVE();
gc_start_internal(NULL, self, Qtrue, Qtrue, Qtrue, Qtrue);
- objspace_reachable_objects_from_root(objspace, root_obj_check_moved_i, NULL);
- objspace_each_objects(objspace, heap_check_moved_i, NULL, TRUE);
+ objspace_reachable_objects_from_root(objspace, root_obj_check_moved_i, objspace);
+ objspace_each_objects(objspace, heap_check_moved_i, objspace, TRUE);
+ objspace->rcompactor.compare_func = NULL;
return gc_compact_stats(self);
}
#else
@@ -10934,6 +10668,7 @@ gc_info_decode(rb_objspace_t *objspace, const VALUE hash_or_key, const unsigned
#endif
static VALUE sym_newobj, sym_malloc, sym_method, sym_capi;
static VALUE sym_none, sym_marking, sym_sweeping;
+ static VALUE sym_weak_references_count, sym_retained_weak_references_count;
VALUE hash = Qnil, key = Qnil;
VALUE major_by, need_major_by;
unsigned int flags = orig_flags ? orig_flags : objspace->profile.latest_gc_info;
@@ -10973,6 +10708,9 @@ gc_info_decode(rb_objspace_t *objspace, const VALUE hash_or_key, const unsigned
S(none);
S(marking);
S(sweeping);
+
+ S(weak_references_count);
+ S(retained_weak_references_count);
#undef S
}
@@ -10994,7 +10732,7 @@ gc_info_decode(rb_objspace_t *objspace, const VALUE hash_or_key, const unsigned
SET(major_by, major_by);
if (orig_flags == 0) { /* set need_major_by only if flags not set explicitly */
- unsigned int need_major_flags = objspace->rgengc.need_major_gc;
+ unsigned int need_major_flags = gc_needs_major_flags;
need_major_by =
(need_major_flags & GPR_FLAG_MAJOR_BY_NOFREE) ? sym_nofree :
(need_major_flags & GPR_FLAG_MAJOR_BY_OLDGEN) ? sym_oldgen :
@@ -11023,6 +10761,9 @@ gc_info_decode(rb_objspace_t *objspace, const VALUE hash_or_key, const unsigned
SET(state, gc_mode(objspace) == gc_mode_none ? sym_none :
gc_mode(objspace) == gc_mode_marking ? sym_marking : sym_sweeping);
}
+
+ SET(weak_references_count, LONG2FIX(objspace->profile.weak_references_count));
+ SET(retained_weak_references_count, LONG2FIX(objspace->profile.retained_weak_references_count));
#undef SET
if (!NIL_P(key)) {/* matched key should return above */
@@ -11088,6 +10829,7 @@ enum gc_stat_sym {
gc_stat_sym_oldmalloc_increase_bytes,
gc_stat_sym_oldmalloc_increase_bytes_limit,
#endif
+ gc_stat_sym_weak_references_count,
#if RGENGC_PROFILE
gc_stat_sym_total_generated_normal_object_count,
gc_stat_sym_total_generated_shady_object_count,
@@ -11139,6 +10881,7 @@ setup_gc_stat_symbols(void)
S(oldmalloc_increase_bytes);
S(oldmalloc_increase_bytes_limit);
#endif
+ S(weak_references_count);
#if RGENGC_PROFILE
S(total_generated_normal_object_count);
S(total_generated_shady_object_count);
@@ -11199,8 +10942,8 @@ gc_stat_internal(VALUE hash_or_sym)
SET(heap_tomb_pages, heap_tomb_total_pages(objspace));
SET(total_allocated_pages, total_allocated_pages(objspace));
SET(total_freed_pages, total_freed_pages(objspace));
- SET(total_allocated_objects, objspace->total_allocated_objects);
- SET(total_freed_objects, objspace->profile.total_freed_objects);
+ SET(total_allocated_objects, total_allocated_objects(objspace));
+ SET(total_freed_objects, total_freed_objects(objspace));
SET(malloc_increase_bytes, malloc_increase);
SET(malloc_increase_bytes_limit, malloc_limit);
SET(minor_gc_count, objspace->profile.minor_gc_count);
@@ -11290,6 +11033,9 @@ enum gc_stat_heap_sym {
gc_stat_heap_sym_total_allocated_pages,
gc_stat_heap_sym_total_freed_pages,
gc_stat_heap_sym_force_major_gc_count,
+ gc_stat_heap_sym_force_incremental_marking_finish_count,
+ gc_stat_heap_sym_total_allocated_objects,
+ gc_stat_heap_sym_total_freed_objects,
gc_stat_heap_sym_last
};
@@ -11309,6 +11055,9 @@ setup_gc_stat_heap_symbols(void)
S(total_allocated_pages);
S(total_freed_pages);
S(force_major_gc_count);
+ S(force_incremental_marking_finish_count);
+ S(total_allocated_objects);
+ S(total_freed_objects);
#undef S
}
}
@@ -11352,6 +11101,9 @@ gc_stat_heap_internal(int size_pool_idx, VALUE hash_or_sym)
SET(total_allocated_pages, size_pool->total_allocated_pages);
SET(total_freed_pages, size_pool->total_freed_pages);
SET(force_major_gc_count, size_pool->force_major_gc_count);
+ SET(force_incremental_marking_finish_count, size_pool->force_incremental_marking_finish_count);
+ SET(total_allocated_objects, size_pool->total_allocated_objects);
+ SET(total_freed_objects, size_pool->total_freed_objects);
#undef SET
if (!NIL_P(key)) { /* matched key should return above */
@@ -11417,18 +11169,14 @@ gc_stress_get(rb_execution_context_t *ec, VALUE self)
return ruby_gc_stress_mode;
}
-static void
-gc_stress_set(rb_objspace_t *objspace, VALUE flag)
-{
- objspace->flags.gc_stressful = RTEST(flag);
- objspace->gc_stress_mode = flag;
-}
-
static VALUE
gc_stress_set_m(rb_execution_context_t *ec, VALUE self, VALUE flag)
{
rb_objspace_t *objspace = &rb_objspace;
- gc_stress_set(objspace, flag);
+
+ objspace->flags.gc_stressful = RTEST(flag);
+ objspace->gc_stress_mode = flag;
+
return flag;
}
@@ -11506,6 +11254,18 @@ gc_set_auto_compact(VALUE _, VALUE v)
GC_ASSERT(GC_COMPACTION_SUPPORTED);
ruby_enable_autocompact = RTEST(v);
+
+#if RGENGC_CHECK_MODE
+ ruby_autocompact_compare_func = NULL;
+
+ if (SYMBOL_P(v)) {
+ ID id = RB_SYM2ID(v);
+ if (id == rb_intern("empty")) {
+ ruby_autocompact_compare_func = compare_free_slots;
+ }
+ }
+#endif
+
return v;
}
#else
@@ -11629,33 +11389,26 @@ get_envparam_double(const char *name, double *default_value, double lower_bound,
}
static void
-gc_set_initial_pages(rb_objspace_t *objspace, int global_heap_init_slots)
+gc_set_initial_pages(rb_objspace_t *objspace)
{
gc_rest(objspace);
for (int i = 0; i < SIZE_POOL_COUNT; i++) {
rb_size_pool_t *size_pool = &size_pools[i];
- char env_key[sizeof("RUBY_GC_HEAP_INIT_SIZE_" "_SLOTS") + DECIMAL_SIZE_OF_BITS(sizeof(size_pool->slot_size) * CHAR_BIT)];
- snprintf(env_key, sizeof(env_key), "RUBY_GC_HEAP_INIT_SIZE_%d_SLOTS", size_pool->slot_size);
+ char env_key[sizeof("RUBY_GC_HEAP_" "_INIT_SLOTS") + DECIMAL_SIZE_OF_BITS(sizeof(int) * CHAR_BIT)];
+ snprintf(env_key, sizeof(env_key), "RUBY_GC_HEAP_%d_INIT_SLOTS", i);
- size_t pool_init_slots = 0;
- if (!get_envparam_size(env_key, &pool_init_slots, 0)) {
- if (global_heap_init_slots) {
- // If we use the global init slot we ponderate it by slot size
- pool_init_slots = gc_params.heap_init_slots / (size_pool->slot_size / BASE_SLOT_SIZE);
- }
- else {
- continue;
- }
+ size_t size_pool_init_slots = gc_params.size_pool_init_slots[i];
+ if (get_envparam_size(env_key, &size_pool_init_slots, 0)) {
+ gc_params.size_pool_init_slots[i] = size_pool_init_slots;
}
- if (pool_init_slots > size_pool->eden_heap.total_slots) {
- size_t slots = pool_init_slots - size_pool->eden_heap.total_slots;
- int multiple = size_pool->slot_size / BASE_SLOT_SIZE;
- size_pool->allocatable_pages = slots * multiple / HEAP_PAGE_OBJ_LIMIT;
+ if (size_pool_init_slots > size_pool->eden_heap.total_slots) {
+ size_t slots = size_pool_init_slots - size_pool->eden_heap.total_slots;
+ size_pool->allocatable_pages = slots_to_pages_for_size_pool(objspace, size_pool, slots);
}
else {
- /* We already have more slots than heap_init_slots allows, so
+ /* We already have more slots than size_pool_init_slots allows, so
* prevent creating more pages. */
size_pool->allocatable_pages = 0;
}
@@ -11666,8 +11419,6 @@ gc_set_initial_pages(rb_objspace_t *objspace, int global_heap_init_slots)
/*
* GC tuning environment variables
*
- * * RUBY_GC_HEAP_INIT_SLOTS
- * - Initial allocation slots.
* * RUBY_GC_HEAP_FREE_SLOTS
* - Prepare at least this amount of slots after GC.
* - Allocate slots if there are not enough slots.
@@ -11714,13 +11465,7 @@ ruby_gc_set_params(void)
/* ok */
}
- /* RUBY_GC_HEAP_INIT_SLOTS */
- if (get_envparam_size("RUBY_GC_HEAP_INIT_SLOTS", &gc_params.heap_init_slots, 0)) {
- gc_set_initial_pages(objspace, TRUE);
- }
- else {
- gc_set_initial_pages(objspace, FALSE);
- }
+ gc_set_initial_pages(objspace);
get_envparam_double("RUBY_GC_HEAP_GROWTH_FACTOR", &gc_params.growth_factor, 1.0, 0.0, FALSE);
get_envparam_size ("RUBY_GC_HEAP_GROWTH_MAX_SLOTS", &gc_params.growth_max_slots, 0);
@@ -11937,40 +11682,6 @@ rb_memerror(void)
EC_JUMP_TAG(ec, TAG_RAISE);
}
-void *
-rb_aligned_malloc(size_t alignment, size_t size)
-{
- /* alignment must be a power of 2 */
- GC_ASSERT(((alignment - 1) & alignment) == 0);
- GC_ASSERT(alignment % sizeof(void*) == 0);
-
- void *res;
-
-#if defined __MINGW32__
- res = __mingw_aligned_malloc(size, alignment);
-#elif defined _WIN32
- void *_aligned_malloc(size_t, size_t);
- res = _aligned_malloc(size, alignment);
-#elif defined(HAVE_POSIX_MEMALIGN)
- if (posix_memalign(&res, alignment, size) != 0) {
- return NULL;
- }
-#elif defined(HAVE_MEMALIGN)
- res = memalign(alignment, size);
-#else
- char* aligned;
- res = malloc(alignment + size + sizeof(void*));
- aligned = (char*)res + alignment + sizeof(void*);
- aligned -= ((VALUE)aligned & (alignment - 1));
- ((void**)aligned)[-1] = res;
- res = (void*)aligned;
-#endif
-
- GC_ASSERT((uintptr_t)res % alignment == 0);
-
- return res;
-}
-
static void
rb_aligned_free(void *ptr, size_t size)
{
@@ -12624,55 +12335,42 @@ ruby_mimmalloc(size_t size)
return mem;
}
-void
-ruby_mimfree(void *ptr)
+void *
+ruby_mimcalloc(size_t num, size_t size)
{
+ void *mem;
#if CALC_EXACT_MALLOC_SIZE
- struct malloc_obj_info *info = (struct malloc_obj_info *)ptr - 1;
- ptr = info;
+ size += sizeof(struct malloc_obj_info);
#endif
- free(ptr);
-}
-
-void *
-rb_alloc_tmp_buffer_with_count(volatile VALUE *store, size_t size, size_t cnt)
-{
- void *ptr;
- VALUE imemo;
- rb_imemo_tmpbuf_t *tmpbuf;
-
- /* Keep the order; allocate an empty imemo first then xmalloc, to
- * get rid of potential memory leak */
- imemo = rb_imemo_tmpbuf_auto_free_maybe_mark_buffer(NULL, 0);
- *store = imemo;
- ptr = ruby_xmalloc0(size);
- tmpbuf = (rb_imemo_tmpbuf_t *)imemo;
- tmpbuf->ptr = ptr;
- tmpbuf->cnt = cnt;
- return ptr;
-}
-
-void *
-rb_alloc_tmp_buffer(volatile VALUE *store, long len)
-{
- long cnt;
-
- if (len < 0 || (cnt = (long)roomof(len, sizeof(VALUE))) < 0) {
- rb_raise(rb_eArgError, "negative buffer size (or size too big)");
+ mem = calloc(num, size);
+#if CALC_EXACT_MALLOC_SIZE
+ if (!mem) {
+ return NULL;
}
-
- return rb_alloc_tmp_buffer_with_count(store, len, cnt);
+ else
+ /* set 0 for consistency of allocated_size/allocations */
+ {
+ struct malloc_obj_info *info = mem;
+ info->size = 0;
+#if USE_GC_MALLOC_OBJ_INFO_DETAILS
+ info->gen = 0;
+ info->file = NULL;
+ info->line = 0;
+#endif
+ mem = info + 1;
+ }
+#endif
+ return mem;
}
void
-rb_free_tmp_buffer(volatile VALUE *store)
+ruby_mimfree(void *ptr)
{
- rb_imemo_tmpbuf_t *s = (rb_imemo_tmpbuf_t*)ATOMIC_VALUE_EXCHANGE(*store, 0);
- if (s) {
- void *ptr = ATOMIC_PTR_EXCHANGE(s->ptr, 0);
- s->cnt = 0;
- ruby_xfree(ptr);
- }
+#if CALC_EXACT_MALLOC_SIZE
+ struct malloc_obj_info *info = (struct malloc_obj_info *)ptr - 1;
+ ptr = info;
+#endif
+ free(ptr);
}
#if MALLOC_ALLOCATED_SIZE
@@ -12949,7 +12647,7 @@ gc_prof_set_heap_info(rb_objspace_t *objspace)
{
if (gc_prof_enabled(objspace)) {
gc_profile_record *record = gc_prof_record(objspace);
- size_t live = objspace->profile.total_allocated_objects_at_gc_start - objspace->profile.total_freed_objects;
+ size_t live = objspace->profile.total_allocated_objects_at_gc_start - total_freed_objects(objspace);
size_t total = objspace->profile.heap_used_at_gc_start * HEAP_PAGE_OBJ_LIMIT;
#if GC_PROFILE_MORE_DETAIL
@@ -12981,9 +12679,7 @@ gc_profile_clear(VALUE _)
objspace->profile.size = 0;
objspace->profile.next_index = 0;
objspace->profile.current_record = 0;
- if (p) {
- free(p);
- }
+ free(p);
return Qnil;
}
@@ -13053,7 +12749,7 @@ gc_profile_record_get(VALUE _)
gc_profile_record *record = &objspace->profile.records[i];
prof = rb_hash_new();
- rb_hash_aset(prof, ID2SYM(rb_intern("GC_FLAGS")), gc_info_decode(0, rb_hash_new(), record->flags));
+ rb_hash_aset(prof, ID2SYM(rb_intern("GC_FLAGS")), gc_info_decode(objspace, rb_hash_new(), record->flags));
rb_hash_aset(prof, ID2SYM(rb_intern("GC_TIME")), DBL2NUM(record->gc_time));
rb_hash_aset(prof, ID2SYM(rb_intern("GC_INVOKE_TIME")), DBL2NUM(record->gc_invoke_time));
rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_USE_SIZE")), SIZET2NUM(record->heap_use_size));
@@ -13428,7 +13124,6 @@ str_len_no_raise(VALUE str)
memcpy(buff + pos, (s), rb_strlen_lit(s) + 1); \
} \
} while (0)
-#define TF(c) ((c) != 0 ? "true" : "false")
#define C(c, s) ((c) != 0 ? (s) : " ")
static size_t
@@ -13447,7 +13142,7 @@ rb_raw_obj_info_common(char *const buff, const size_t buff_size, const VALUE obj
}
}
else {
- const int age = RVALUE_FLAGS_AGE(RBASIC(obj)->flags);
+ const int age = RVALUE_AGE_GET(obj);
if (is_pointer_to_heap(&rb_objspace, (void *)obj)) {
APPEND_F("%p [%d%s%s%s%s%s%s] %s ",
@@ -13511,13 +13206,12 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU
RARRAY_LEN(obj));
}
else {
- APPEND_F("[%s%s%s] len: %ld, capa:%ld ptr:%p",
+ APPEND_F("[%s%s] len: %ld, capa:%ld ptr:%p",
C(ARY_EMBED_P(obj), "E"),
C(ARY_SHARED_P(obj), "S"),
- C(RARRAY_TRANSIENT_P(obj), "T"),
RARRAY_LEN(obj),
ARY_EMBED_P(obj) ? -1L : RARRAY(obj)->as.heap.aux.capa,
- (void *)RARRAY_CONST_PTR_TRANSIENT(obj));
+ (void *)RARRAY_CONST_PTR(obj));
}
break;
case T_STRING: {
@@ -13561,7 +13255,7 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU
APPEND_F("%s", RSTRING_PTR(class_path));
}
else {
- APPEND_S("(annon)");
+ APPEND_S("(anon)");
}
break;
}
@@ -13575,14 +13269,20 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU
}
case T_OBJECT:
{
- uint32_t len = ROBJECT_IV_CAPACITY(obj);
-
- if (RANY(obj)->as.basic.flags & ROBJECT_EMBED) {
- APPEND_F("(embed) len:%d", len);
+ if (rb_shape_obj_too_complex(obj)) {
+ size_t hash_len = rb_st_table_size(ROBJECT_IV_HASH(obj));
+ APPEND_F("(too_complex) len:%zu", hash_len);
}
else {
- VALUE *ptr = ROBJECT_IVPTR(obj);
- APPEND_F("len:%d ptr:%p", len, (void *)ptr);
+ uint32_t len = ROBJECT_IV_CAPACITY(obj);
+
+ if (RANY(obj)->as.basic.flags & ROBJECT_EMBED) {
+ APPEND_F("(embed) len:%d", len);
+ }
+ else {
+ VALUE *ptr = ROBJECT_IVPTR(obj);
+ APPEND_F("len:%d ptr:%p", len, (void *)ptr);
+ }
}
}
break;
@@ -13617,7 +13317,7 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU
{
const rb_method_entry_t *me = &RANY(obj)->as.imemo.ment;
- APPEND_F(":%s (%s%s%s%s) type:%s alias:%d owner:%p defined_class:%p",
+ APPEND_F(":%s (%s%s%s%s) type:%s aliased:%d owner:%p defined_class:%p",
rb_id2name(me->called_id),
METHOD_ENTRY_VISI(me) == METHOD_VISI_PUBLIC ? "pub" :
METHOD_ENTRY_VISI(me) == METHOD_VISI_PRIVATE ? "pri" : "pro",
@@ -13625,7 +13325,7 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU
METHOD_ENTRY_CACHED(me) ? ",cc" : "",
METHOD_ENTRY_INVALIDATED(me) ? ",inv" : "",
me->def ? rb_method_type_name(me->def->type) : "NULL",
- me->def ? me->def->alias_count : -1,
+ me->def ? me->def->aliased : -1,
(void *)me->owner, // obj_info(me->owner),
(void *)me->defined_class); //obj_info(me->defined_class)));
@@ -13685,7 +13385,6 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU
return pos;
}
-#undef TF
#undef C
const char *
@@ -13731,12 +13430,32 @@ obj_info(VALUE obj)
char *const buff = obj_info_buffers[index];
return rb_raw_obj_info(buff, OBJ_INFO_BUFFERS_SIZE, obj);
}
+
+static const char *
+obj_info_basic(VALUE obj)
+{
+ rb_atomic_t index = atomic_inc_wraparound(&obj_info_buffers_index, OBJ_INFO_BUFFERS_NUM);
+ char *const buff = obj_info_buffers[index];
+
+ asan_unpoisoning_object(obj) {
+ rb_raw_obj_info_common(buff, OBJ_INFO_BUFFERS_SIZE, obj);
+ }
+
+ return buff;
+}
#else
static const char *
obj_info(VALUE obj)
{
return obj_type_name(obj);
}
+
+static const char *
+obj_info_basic(VALUE obj)
+{
+ return obj_type_name(obj);
+}
+
#endif
const char *
@@ -13784,14 +13503,14 @@ rb_gcdebug_print_obj_condition(VALUE obj)
fprintf(stderr, "marked? : %s\n", MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(obj), obj) ? "true" : "false");
fprintf(stderr, "pinned? : %s\n", MARKED_IN_BITMAP(GET_HEAP_PINNED_BITS(obj), obj) ? "true" : "false");
- fprintf(stderr, "age? : %d\n", RVALUE_AGE(obj));
+ fprintf(stderr, "age? : %d\n", RVALUE_AGE_GET(obj));
fprintf(stderr, "old? : %s\n", RVALUE_OLD_P(obj) ? "true" : "false");
fprintf(stderr, "WB-protected?: %s\n", RVALUE_WB_UNPROTECTED(obj) ? "false" : "true");
fprintf(stderr, "remembered? : %s\n", RVALUE_REMEMBERED(obj) ? "true" : "false");
if (is_lazy_sweeping(objspace)) {
fprintf(stderr, "lazy sweeping?: true\n");
- fprintf(stderr, "swept?: %s\n", is_swept_object(obj) ? "done" : "not yet");
+ fprintf(stderr, "page swept?: %s\n", GET_HEAP_PAGE(ptr)->flags.before_sweep ? "false" : "true");
}
else {
fprintf(stderr, "lazy sweeping?: false\n");
@@ -13813,8 +13532,8 @@ rb_gcdebug_sentinel(VALUE obj, const char *name)
#endif /* GC_DEBUG */
-#if GC_DEBUG_STRESS_TO_CLASS
-/*
+/* :nodoc:
+ *
* call-seq:
* GC.add_stress_to_class(class[, ...])
*
@@ -13827,13 +13546,14 @@ rb_gcdebug_add_stress_to_class(int argc, VALUE *argv, VALUE self)
rb_objspace_t *objspace = &rb_objspace;
if (!stress_to_class) {
- stress_to_class = rb_ary_hidden_new(argc);
+ set_stress_to_class(rb_ary_hidden_new(argc));
}
rb_ary_cat(stress_to_class, argv, argc);
return self;
}
-/*
+/* :nodoc:
+ *
* call-seq:
* GC.remove_stress_to_class(class[, ...])
*
@@ -13852,12 +13572,11 @@ rb_gcdebug_remove_stress_to_class(int argc, VALUE *argv, VALUE self)
rb_ary_delete_same(stress_to_class, argv[i]);
}
if (RARRAY_LEN(stress_to_class) == 0) {
- stress_to_class = 0;
+ set_stress_to_class(0);
}
}
return Qnil;
}
-#endif
/*
* Document-module: ObjectSpace
@@ -13910,7 +13629,15 @@ rb_gcdebug_remove_stress_to_class(int argc, VALUE *argv, VALUE self)
void
Init_GC(void)
{
+#if USE_SHARED_GC
+ if (getenv(RUBY_GC_LIBRARY_PATH) != NULL && !dln_supported_p()) {
+ rb_warn(RUBY_GC_LIBRARY_PATH " is ignored because this executable file can't load extension libraries");
+ }
+#endif
+
#undef rb_intern
+ malloc_offset = gc_compute_malloc_offset();
+
VALUE rb_mObjSpace;
VALUE rb_mProfiler;
VALUE gc_constants;
@@ -13927,11 +13654,12 @@ Init_GC(void)
rb_hash_aset(gc_constants, ID2SYM(rb_intern("HEAP_PAGE_SIZE")), SIZET2NUM(HEAP_PAGE_SIZE));
rb_hash_aset(gc_constants, ID2SYM(rb_intern("SIZE_POOL_COUNT")), LONG2FIX(SIZE_POOL_COUNT));
rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVARGC_MAX_ALLOCATE_SIZE")), LONG2FIX(size_pool_slot_size(SIZE_POOL_COUNT - 1)));
+ rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_OLD_AGE")), LONG2FIX(RVALUE_OLD_AGE));
if (RB_BUG_INSTEAD_OF_RB_MEMERROR+0) {
rb_hash_aset(gc_constants, ID2SYM(rb_intern("RB_BUG_INSTEAD_OF_RB_MEMERROR")), Qtrue);
}
OBJ_FREEZE(gc_constants);
- /* internal constants */
+ /* Internal constants in the garbage collector. */
rb_define_const(rb_mGC, "INTERNAL_CONSTANTS", gc_constants);
rb_mProfiler = rb_define_module_under(rb_mGC, "Profiler");
@@ -13962,7 +13690,6 @@ Init_GC(void)
/* internal methods */
rb_define_singleton_method(rb_mGC, "verify_internal_consistency", gc_verify_internal_consistency_m, 0);
- rb_define_singleton_method(rb_mGC, "verify_transient_heap_internal_consistency", gc_verify_transient_heap_internal_consistency, 0);
#if MALLOC_ALLOCATED_SIZE
rb_define_singleton_method(rb_mGC, "malloc_allocated_size", gc_malloc_allocated_size, 0);
rb_define_singleton_method(rb_mGC, "malloc_allocations", gc_malloc_allocations, 0);
@@ -13983,10 +13710,10 @@ Init_GC(void)
rb_define_singleton_method(rb_mGC, "verify_compaction_references", rb_f_notimplement, -1);
}
-#if GC_DEBUG_STRESS_TO_CLASS
- rb_define_singleton_method(rb_mGC, "add_stress_to_class", rb_gcdebug_add_stress_to_class, -1);
- rb_define_singleton_method(rb_mGC, "remove_stress_to_class", rb_gcdebug_remove_stress_to_class, -1);
-#endif
+ if (GC_DEBUG_STRESS_TO_CLASS) {
+ rb_define_singleton_method(rb_mGC, "add_stress_to_class", rb_gcdebug_add_stress_to_class, -1);
+ rb_define_singleton_method(rb_mGC, "remove_stress_to_class", rb_gcdebug_remove_stress_to_class, -1);
+ }
{
VALUE opts;
diff --git a/gc.rb b/gc.rb
index dd4fb6c274..880aa46475 100644
--- a/gc.rb
+++ b/gc.rb
@@ -1,6 +1,6 @@
# for gc.c
-# The GC module provides an interface to Ruby's mark and
+# The \GC module provides an interface to Ruby's mark and
# sweep garbage collection mechanism.
#
# Some of the underlying methods are also available via the ObjectSpace
@@ -10,30 +10,36 @@
# GC::Profiler.
module GC
- # call-seq:
- # GC.start -> nil
- # ObjectSpace.garbage_collect -> nil
- # include GC; garbage_collect -> nil
- # GC.start(full_mark: true, immediate_sweep: true) -> nil
- # ObjectSpace.garbage_collect(full_mark: true, immediate_sweep: true) -> nil
- # include GC; garbage_collect(full_mark: true, immediate_sweep: true) -> nil
- #
- # Initiates garbage collection, even if manually disabled.
- #
- # This method is defined with keyword arguments that default to true:
- #
- # def GC.start(full_mark: true, immediate_sweep: true); end
- #
- # Use full_mark: false to perform a minor \GC.
- # Use immediate_sweep: false to defer sweeping (use lazy sweep).
- #
- # Note: These keyword arguments are implementation and version dependent. They
- # are not guaranteed to be future-compatible, and may be ignored if the
- # underlying implementation does not support them.
+ # Initiates garbage collection, even if manually disabled.
+ #
+ # The +full_mark+ keyword argument determines whether or not to perform a
+ # major garbage collection cycle. When set to +true+, a major garbage
+ # collection cycle is ran, meaning all objects are marked. When set to
+ # +false+, a minor garbage collection cycle is ran, meaning only young
+ # objects are marked.
+ #
+ # The +immediate_mark+ keyword argument determines whether or not to perform
+ # incremental marking. When set to +true+, marking is completed during the
+ # call to this method. When set to +false+, marking is performed in steps
+ # that is interleaved with future Ruby code execution, so marking might not
+ # be completed during this method call. Note that if +full_mark+ is +false+
+ # then marking will always be immediate, regardless of the value of
+ # +immediate_mark+.
+ #
+ # The +immediate_sweep+ keyword argument determines whether or not to defer
+ # sweeping (using lazy sweep). When set to +false+, sweeping is performed in
+ # steps that is interleaved with future Ruby code execution, so sweeping might
+ # not be completed during this method call. When set to +true+, sweeping is
+ # completed during the call to this method.
+ #
+ # Note: These keyword arguments are implementation and version dependent. They
+ # are not guaranteed to be future-compatible, and may be ignored if the
+ # underlying implementation does not support them.
def self.start full_mark: true, immediate_mark: true, immediate_sweep: true
Primitive.gc_start_internal full_mark, immediate_mark, immediate_sweep, false
end
+ # Alias of GC.start
def garbage_collect full_mark: true, immediate_mark: true, immediate_sweep: true
Primitive.gc_start_internal full_mark, immediate_mark, immediate_sweep, false
end
@@ -191,19 +197,18 @@ module GC
# GC.stat_heap(heap_name, hash) -> Hash
# GC.stat_heap(heap_name, :key) -> Numeric
#
- # Returns information for memory pools in the \GC.
+ # Returns information for heaps in the \GC.
#
# If the first optional argument, +heap_name+, is passed in and not +nil+, it
- # returns a +Hash+ containing information about the particular memory pool.
- # Otherwise, it will return a +Hash+ with memory pool names as keys and
- # a +Hash+ containing information about the memory pool as values.
+ # returns a +Hash+ containing information about the particular heap.
+ # Otherwise, it will return a +Hash+ with heap names as keys and
+ # a +Hash+ containing information about the heap as values.
#
# If the second optional argument, +hash_or_key+, is given as +Hash+, it will
# be overwritten and returned. This is intended to avoid the probe effect.
#
# If both optional arguments are passed in and the second optional argument is
- # a symbol, it will return a +Numeric+ of the value for the particular memory
- # pool.
+ # a symbol, it will return a +Numeric+ of the value for the particular heap.
#
# On CRuby, +heap_name+ is of the type +Integer+ but may be of type +String+
# on other implementations.
@@ -214,6 +219,36 @@ module GC
# If the optional argument, hash, is given, it is overwritten and returned.
#
# This method is only expected to work on CRuby.
+ #
+ # The hash includes the following keys about the internal information in
+ # the \GC:
+ #
+ # [slot_size]
+ # The slot size of the heap in bytes.
+ # [heap_allocatable_pages]
+ # The number of pages that can be allocated without triggering a new
+ # garbage collection cycle.
+ # [heap_eden_pages]
+ # The number of pages in the eden heap.
+ # [heap_eden_slots]
+ # The total number of slots in all of the pages in the eden heap.
+ # [heap_tomb_pages]
+ # The number of pages in the tomb heap. The tomb heap only contains pages
+ # that do not have any live objects.
+ # [heap_tomb_slots]
+ # The total number of slots in all of the pages in the tomb heap.
+ # [total_allocated_pages]
+ # The total number of pages that have been allocated in the heap.
+ # [total_freed_pages]
+ # The total number of pages that have been freed and released back to the
+ # system in the heap.
+ # [force_major_gc_count]
+ # The number of times major garbage collection cycles this heap has forced
+ # to start due to running out of free slots.
+ # [force_incremental_marking_finish_count]
+ # The number of times this heap has forced incremental marking to complete
+ # due to running out of pooled slots.
+ #
def self.stat_heap heap_name = nil, hash_or_key = nil
Primitive.gc_stat_heap heap_name, hash_or_key
end
@@ -288,6 +323,7 @@ module GC
end
module ObjectSpace
+ # Alias of GC.start
def garbage_collect full_mark: true, immediate_mark: true, immediate_sweep: true
Primitive.gc_start_internal full_mark, immediate_mark, immediate_sweep, false
end
diff --git a/gem_prelude.rb b/gem_prelude.rb
index f382021ca3..bcd2560fab 100644
--- a/gem_prelude.rb
+++ b/gem_prelude.rb
@@ -4,6 +4,8 @@ rescue LoadError => e
raise unless e.path == 'rubygems'
warn "`RubyGems' were not loaded."
+else
+ require 'bundled_gems'
end if defined?(Gem)
begin
diff --git a/gems/bundled_gems b/gems/bundled_gems
index 6607c656f7..ad979c1c5c 100644
--- a/gems/bundled_gems
+++ b/gems/bundled_gems
@@ -5,18 +5,32 @@
# - repository-url: URL from where clone for test
# - revision: revision in repository-url to test
# if `revision` is not given, "v"+`version` or `version` will be used.
-minitest 5.18.0 https://github.com/minitest/minitest
-power_assert 2.0.3 https://github.com/ruby/power_assert
-rake 13.0.6 https://github.com/ruby/rake
-test-unit 3.5.9 https://github.com/test-unit/test-unit
-rexml 3.2.5 https://github.com/ruby/rexml
-rss 0.2.9 https://github.com/ruby/rss
-net-ftp 0.2.0 https://github.com/ruby/net-ftp
-net-imap 0.3.4 https://github.com/ruby/net-imap
+
+minitest 5.22.3 https://github.com/minitest/minitest ea9caafc0754b1d6236a490d59e624b53209734a
+power_assert 2.0.3 https://github.com/ruby/power_assert 84e85124c5014a139af39161d484156cfe87a9ed
+rake 13.2.1 https://github.com/ruby/rake
+test-unit 3.6.2 https://github.com/test-unit/test-unit
+rexml 3.2.6 https://github.com/ruby/rexml
+rss 0.3.0 https://github.com/ruby/rss
+net-ftp 0.3.4 https://github.com/ruby/net-ftp
+net-imap 0.4.10 https://github.com/ruby/net-imap
net-pop 0.1.2 https://github.com/ruby/net-pop
-net-smtp 0.3.3 https://github.com/ruby/net-smtp
+net-smtp 0.5.0 https://github.com/ruby/net-smtp
matrix 0.4.2 https://github.com/ruby/matrix
prime 0.1.2 https://github.com/ruby/prime
-rbs 3.1.0 https://github.com/ruby/rbs
-typeprof 0.21.7 https://github.com/ruby/typeprof aabc019684d8b4a1ed66c2a1ca48da7bbb18dcc0
-debug 1.8.0 https://github.com/ruby/debug
+rbs 3.4.4 https://github.com/ruby/rbs ba7872795d5de04adb8ff500c0e6afdc81a041dd
+typeprof 0.21.11 https://github.com/ruby/typeprof b19a6416da3a05d57fadd6ffdadb382b6d236ca5
+debug 1.9.2 https://github.com/ruby/debug
+racc 1.7.3 https://github.com/ruby/racc
+mutex_m 0.2.0 https://github.com/ruby/mutex_m
+getoptlong 0.2.1 https://github.com/ruby/getoptlong
+base64 0.2.0 https://github.com/ruby/base64
+bigdecimal 3.1.7 https://github.com/ruby/bigdecimal
+observer 0.1.2 https://github.com/ruby/observer
+abbrev 0.1.2 https://github.com/ruby/abbrev
+resolv-replace 0.1.1 https://github.com/ruby/resolv-replace
+rinda 0.2.0 https://github.com/ruby/rinda
+drb 2.2.1 https://github.com/ruby/drb
+nkf 0.2.0 https://github.com/ruby/nkf
+syslog 0.1.2 https://github.com/ruby/syslog
+csv 3.3.0 https://github.com/ruby/csv
diff --git a/gems/lib/core_assertions.rb b/gems/lib/core_assertions.rb
new file mode 100644
index 0000000000..7334063885
--- /dev/null
+++ b/gems/lib/core_assertions.rb
@@ -0,0 +1 @@
+require_relative "../../tool/lib/core_assertions.rb"
diff --git a/gems/lib/envutil.rb b/gems/lib/envutil.rb
new file mode 100644
index 0000000000..d684c22cf2
--- /dev/null
+++ b/gems/lib/envutil.rb
@@ -0,0 +1 @@
+require_relative "../../tool/lib/envutil.rb"
diff --git a/gems/lib/rake/extensiontask.rb b/gems/lib/rake/extensiontask.rb
new file mode 100644
index 0000000000..fdbe8d8874
--- /dev/null
+++ b/gems/lib/rake/extensiontask.rb
@@ -0,0 +1,12 @@
+require "rake/tasklib" unless defined?(Rake::TaskLib)
+
+module Rake
+ class ExtensionTask < TaskLib
+ def initialize(...)
+ task :compile do |args|
+ puts "Dummy `compile` task defined in #{__FILE__}"
+ puts "#{args.name} => #{args.prereqs.join(' ')}"
+ end
+ end
+ end
+end
diff --git a/hash.c b/hash.c
index 0aab4443df..f34f64065b 100644
--- a/hash.c
+++ b/hash.c
@@ -35,6 +35,7 @@
#include "internal/hash.h"
#include "internal/object.h"
#include "internal/proc.h"
+#include "internal/st.h"
#include "internal/symbol.h"
#include "internal/thread.h"
#include "internal/time.h"
@@ -44,11 +45,27 @@
#include "ruby/util.h"
#include "ruby_assert.h"
#include "symbol.h"
-#include "transient_heap.h"
#include "ruby/thread_native.h"
#include "ruby/ractor.h"
#include "vm_sync.h"
+/* Flags of RHash
+ *
+ * 1: RHASH_PASS_AS_KEYWORDS
+ * The hash is flagged as Ruby 2 keywords hash.
+ * 2: RHASH_PROC_DEFAULT
+ * The hash has a default proc (rather than a default value).
+ * 3: RHASH_ST_TABLE_FLAG
+ * The hash uses a ST table (rather than an AR table).
+ * 4-7: RHASH_AR_TABLE_SIZE_MASK
+ * The size of the AR table.
+ * 8-11: RHASH_AR_TABLE_BOUND_MASK
+ * The bounds of the AR table.
+ * 13-19: RHASH_LEV_MASK
+ * The iterational level of the hash. Used to prevent modifications
+ * to the hash during interation.
+ */
+
#ifndef HASH_DEBUG
#define HASH_DEBUG 0
#endif
@@ -193,10 +210,26 @@ any_hash(VALUE a, st_index_t (*other_func)(VALUE))
return (long)hnum;
}
+VALUE rb_obj_hash(VALUE obj);
+VALUE rb_vm_call0(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE *argv, const rb_callable_method_entry_t *cme, int kw_splat);
+
static st_index_t
obj_any_hash(VALUE obj)
{
- VALUE hval = rb_check_funcall_basic_kw(obj, id_hash, rb_mKernel, 0, 0, 0);
+ VALUE hval = Qundef;
+ VALUE klass = CLASS_OF(obj);
+ if (klass) {
+ const rb_callable_method_entry_t *cme = rb_callable_method_entry(klass, id_hash);
+ if (cme && METHOD_ENTRY_BASIC(cme)) {
+ // Optimize away the frame push overhead if it's the default Kernel#hash
+ if (cme->def->type == VM_METHOD_TYPE_CFUNC && cme->def->body.cfunc.func == (rb_cfunc_t)rb_obj_hash) {
+ hval = rb_obj_hash(obj);
+ }
+ else if (RBASIC_CLASS(cme->defined_class) == rb_mKernel) {
+ hval = rb_vm_call0(GET_EC(), obj, id_hash, 0, 0, cme, 0);
+ }
+ }
+ }
if (UNDEF_P(hval)) {
hval = rb_exec_recursive_outer_mid(hash_recursive, obj, 0, id_hash);
@@ -361,16 +394,17 @@ const struct st_hash_type rb_hashtype_ident = {
rb_ident_hash,
};
+#define RHASH_IDENTHASH_P(hash) (RHASH_TYPE(hash) == &identhash)
+#define RHASH_STRING_KEY_P(hash, key) (!RHASH_IDENTHASH_P(hash) && (rb_obj_class(key) == rb_cString))
+
typedef st_index_t st_hash_t;
/*
* RHASH_AR_TABLE_P(h):
- * * as.ar == NULL or
- * as.ar points ar_table.
- * * as.ar is allocated by transient heap or xmalloc.
+ * RHASH_AR_TABLE points to ar_table.
*
* !RHASH_AR_TABLE_P(h):
- * * as.st points st_table.
+ * RHASH_ST_TABLE points st_table.
*/
#define RHASH_AR_TABLE_MAX_BOUND RHASH_AR_TABLE_MAX_SIZE
@@ -447,14 +481,20 @@ ar_set_entry(VALUE hash, unsigned int index, st_data_t key, st_data_t val, st_ha
((unsigned int)((RBASIC(h)->flags >> RHASH_AR_TABLE_BOUND_SHIFT) & \
(RHASH_AR_TABLE_BOUND_MASK >> RHASH_AR_TABLE_BOUND_SHIFT)))
-#define RHASH_AR_TABLE_BOUND(h) (HASH_ASSERT(RHASH_AR_TABLE_P(h)), \
- RHASH_AR_TABLE_BOUND_RAW(h))
-
#define RHASH_ST_TABLE_SET(h, s) rb_hash_st_table_set(h, s)
#define RHASH_TYPE(hash) (RHASH_AR_TABLE_P(hash) ? &objhash : RHASH_ST_TABLE(hash)->type)
#define HASH_ASSERT(expr) RUBY_ASSERT_MESG_WHEN(HASH_DEBUG, expr, #expr)
+static inline unsigned int
+RHASH_AR_TABLE_BOUND(VALUE h)
+{
+ HASH_ASSERT(RHASH_AR_TABLE_P(h));
+ const unsigned int bound = RHASH_AR_TABLE_BOUND_RAW(h);
+ HASH_ASSERT(bound <= RHASH_AR_TABLE_MAX_SIZE);
+ return bound;
+}
+
#if HASH_DEBUG
#define hash_verify(hash) hash_verify_(hash, __FILE__, __LINE__)
@@ -464,10 +504,10 @@ rb_hash_dump(VALUE hash)
rb_obj_info_dump(hash);
if (RHASH_AR_TABLE_P(hash)) {
- unsigned i, n = 0, bound = RHASH_AR_TABLE_BOUND(hash);
+ unsigned i, bound = RHASH_AR_TABLE_BOUND(hash);
fprintf(stderr, " size:%u bound:%u\n",
- RHASH_AR_TABLE_SIZE(hash), RHASH_AR_TABLE_BOUND(hash));
+ RHASH_AR_TABLE_SIZE(hash), bound);
for (i=0; i<bound; i++) {
st_data_t k, v;
@@ -481,7 +521,6 @@ rb_hash_dump(VALUE hash)
rb_raw_obj_info(b1, 0x100, k),
rb_raw_obj_info(b2, 0x100, v),
ar_hint(hash, i));
- n++;
}
else {
fprintf(stderr, " %d empty\n", i);
@@ -527,18 +566,6 @@ hash_verify_(VALUE hash, const char *file, int line)
#endif
static inline int
-RHASH_TABLE_NULL_P(VALUE hash)
-{
- if (RHASH_AR_TABLE(hash) == NULL) {
- HASH_ASSERT(RHASH_AR_TABLE_P(hash));
- return TRUE;
- }
- else {
- return FALSE;
- }
-}
-
-static inline int
RHASH_TABLE_EMPTY_P(VALUE hash)
{
return RHASH_SIZE(hash) == 0;
@@ -620,18 +647,6 @@ RHASH_AR_TABLE_CLEAR(VALUE h)
memset(RHASH_AR_TABLE(h), 0, sizeof(ar_table));
}
-static ar_table*
-ar_alloc_table(VALUE hash)
-{
- ar_table *tab = RHASH_AR_TABLE(hash);
- memset(tab, 0, sizeof(ar_table));
-
- RHASH_AR_TABLE_SIZE_SET(hash, 0);
- RHASH_AR_TABLE_BOUND_SET(hash, 0);
-
- return tab;
-}
-
NOINLINE(static int ar_equal(VALUE x, VALUE y));
static int
@@ -690,9 +705,8 @@ ar_find_entry(VALUE hash, st_hash_t hash_value, st_data_t key)
return ar_find_entry_hint(hash, hint, key);
}
-//old one
static inline void
-ar_free_and_clear_table(VALUE hash)
+hash_ar_free_and_clear_table(VALUE hash)
{
RHASH_AR_TABLE_CLEAR(hash);
@@ -700,72 +714,75 @@ ar_free_and_clear_table(VALUE hash)
HASH_ASSERT(RHASH_AR_TABLE_BOUND(hash) == 0);
}
-static void
-ar_try_convert_table(VALUE hash)
-{
- if (!RHASH_AR_TABLE_P(hash)) return;
-
- const unsigned size = RHASH_AR_TABLE_SIZE(hash);
+void rb_st_add_direct_with_hash(st_table *tab, st_data_t key, st_data_t value, st_hash_t hash); // st.c
- if (size < RHASH_AR_TABLE_MAX_SIZE) {
- return;
- }
-
- st_table tab;
- st_table *new_tab = &tab;
- rb_st_init_existing_table_with_size(new_tab, &objhash, size * 2);
+enum ar_each_key_type {
+ ar_each_key_copy,
+ ar_each_key_cmp,
+ ar_each_key_insert,
+};
- for (st_index_t i = 0; i < RHASH_AR_TABLE_MAX_BOUND; i++) {
- ar_table_pair *pair = RHASH_AR_TABLE_REF(hash, i);
- st_add_direct(new_tab, pair->key, pair->val);
+static inline int
+ar_each_key(ar_table *ar, int max, enum ar_each_key_type type, st_data_t *dst_keys, st_table *new_tab, st_hash_t *hashes)
+{
+ for (int i = 0; i < max; i++) {
+ ar_table_pair *pair = &ar->pairs[i];
+
+ switch (type) {
+ case ar_each_key_copy:
+ dst_keys[i] = pair->key;
+ break;
+ case ar_each_key_cmp:
+ if (dst_keys[i] != pair->key) return 1;
+ break;
+ case ar_each_key_insert:
+ if (UNDEF_P(pair->key)) continue; // deleted entry
+ rb_st_add_direct_with_hash(new_tab, pair->key, pair->val, hashes[i]);
+ break;
+ }
}
- ar_free_and_clear_table(hash);
- RHASH_ST_TABLE_SET(hash, new_tab);
+ return 0;
}
static st_table *
ar_force_convert_table(VALUE hash, const char *file, int line)
{
- st_table *new_tab;
- st_table tab;
- new_tab = &tab;
-
if (RHASH_ST_TABLE_P(hash)) {
return RHASH_ST_TABLE(hash);
}
-
- if (RHASH_AR_TABLE(hash)) {
- unsigned i, bound = RHASH_AR_TABLE_BOUND(hash);
-
- rb_st_init_existing_table_with_size(new_tab, &objhash, RHASH_AR_TABLE_SIZE(hash));
-
- for (i = 0; i < bound; i++) {
- if (ar_cleared_entry(hash, i)) continue;
-
- ar_table_pair *pair = RHASH_AR_TABLE_REF(hash, i);
- st_add_direct(new_tab, pair->key, pair->val);
- }
- ar_free_and_clear_table(hash);
- }
else {
- rb_st_init_existing_table_with_size(new_tab, &objhash, 0);
- }
-
- RHASH_ST_TABLE_SET(hash, new_tab);
-
- new_tab = RHASH_ST_TABLE(hash);
-
- return new_tab;
-}
+ ar_table *ar = RHASH_AR_TABLE(hash);
+ st_hash_t hashes[RHASH_AR_TABLE_MAX_SIZE];
+ unsigned int bound, size;
+
+ // prepare hash values
+ do {
+ st_data_t keys[RHASH_AR_TABLE_MAX_SIZE];
+ bound = RHASH_AR_TABLE_BOUND(hash);
+ size = RHASH_AR_TABLE_SIZE(hash);
+ ar_each_key(ar, bound, ar_each_key_copy, keys, NULL, NULL);
+
+ for (unsigned int i = 0; i < bound; i++) {
+ // do_hash calls #hash method and it can modify hash object
+ hashes[i] = UNDEF_P(keys[i]) ? 0 : ar_do_hash(keys[i]);
+ }
-static ar_table *
-hash_ar_table(VALUE hash)
-{
- if (RHASH_TABLE_NULL_P(hash)) {
- ar_alloc_table(hash);
+ // check if modified
+ if (UNLIKELY(!RHASH_AR_TABLE_P(hash))) return RHASH_ST_TABLE(hash);
+ if (UNLIKELY(RHASH_AR_TABLE_BOUND(hash) != bound)) continue;
+ if (UNLIKELY(ar_each_key(ar, bound, ar_each_key_cmp, keys, NULL, NULL))) continue;
+ } while (0);
+
+ // make st
+ st_table tab;
+ st_table *new_tab = &tab;
+ st_init_existing_table_with_size(new_tab, &objhash, size);
+ ar_each_key(ar, bound, ar_each_key_insert, NULL, new_tab, hashes);
+ hash_ar_free_and_clear_table(hash);
+ RHASH_ST_TABLE_SET(hash, new_tab);
+ return RHASH_ST_TABLE(hash);
}
- return RHASH_AR_TABLE(hash);
}
static int
@@ -818,7 +835,6 @@ ar_add_direct_with_hash(VALUE hash, st_data_t key, st_data_t val, st_hash_t hash
else {
if (UNLIKELY(bin >= RHASH_AR_TABLE_MAX_BOUND)) {
bin = ar_compact_table(hash);
- hash_ar_table(hash);
}
HASH_ASSERT(bin < RHASH_AR_TABLE_MAX_BOUND);
@@ -829,6 +845,14 @@ ar_add_direct_with_hash(VALUE hash, st_data_t key, st_data_t val, st_hash_t hash
}
}
+static void
+ensure_ar_table(VALUE hash)
+{
+ if (!RHASH_AR_TABLE_P(hash)) {
+ rb_raise(rb_eRuntimeError, "hash representation was changed during iteration");
+ }
+}
+
static int
ar_general_foreach(VALUE hash, st_foreach_check_callback_func *func, st_update_callback_func *replace, st_data_t arg)
{
@@ -839,7 +863,10 @@ ar_general_foreach(VALUE hash, st_foreach_check_callback_func *func, st_update_c
if (ar_cleared_entry(hash, i)) continue;
ar_table_pair *pair = RHASH_AR_TABLE_REF(hash, i);
- enum st_retval retval = (*func)(pair->key, pair->val, arg, 0);
+ st_data_t key = (st_data_t)pair->key;
+ st_data_t val = (st_data_t)pair->val;
+ enum st_retval retval = (*func)(key, val, arg, 0);
+ ensure_ar_table(hash);
/* pair may be not valid here because of theap */
switch (retval) {
@@ -850,14 +877,12 @@ ar_general_foreach(VALUE hash, st_foreach_check_callback_func *func, st_update_c
return 0;
case ST_REPLACE:
if (replace) {
- VALUE key = pair->key;
- VALUE val = pair->val;
retval = (*replace)(&key, &val, arg, TRUE);
// TODO: pair should be same as pair before.
- ar_table_pair *pair = RHASH_AR_TABLE_REF(hash, i);
- pair->key = key;
- pair->val = val;
+ pair = RHASH_AR_TABLE_REF(hash, i);
+ pair->key = (VALUE)key;
+ pair->val = (VALUE)val;
}
break;
case ST_DELETE:
@@ -914,6 +939,7 @@ ar_foreach_check(VALUE hash, st_foreach_check_callback_func *func, st_data_t arg
hint = ar_hint(hash, i);
retval = (*func)(key, pair->val, arg, 0);
+ ensure_ar_table(hash);
hash_verify(hash);
switch (retval) {
@@ -963,7 +989,6 @@ ar_update(VALUE hash, st_data_t key,
existing = (bin != RHASH_AR_TABLE_MAX_BOUND) ? TRUE : FALSE;
}
else {
- hash_ar_table(hash); /* allocate ltbl if needed */
existing = FALSE;
}
@@ -975,6 +1000,7 @@ ar_update(VALUE hash, st_data_t key,
old_key = key;
retval = (*func)(&key, &value, arg, existing);
/* pair can be invalid here because of theap */
+ ensure_ar_table(hash);
switch (retval) {
case ST_CONTINUE:
@@ -1012,9 +1038,6 @@ ar_insert(VALUE hash, st_data_t key, st_data_t value)
return -1;
}
- HASH_ASSERT(RHASH_AR_TABLE(hash));
- hash_ar_table(hash); /* prepare ltbl */
-
bin = ar_find_entry(hash, hash_value, key);
if (bin == RHASH_AR_TABLE_MAX_BOUND) {
if (RHASH_AR_TABLE_SIZE(hash) >= RHASH_AR_TABLE_MAX_SIZE) {
@@ -1022,8 +1045,6 @@ ar_insert(VALUE hash, st_data_t key, st_data_t value)
}
else if (bin >= RHASH_AR_TABLE_MAX_BOUND) {
bin = ar_compact_table(hash);
- HASH_ASSERT(RHASH_AR_TABLE(hash));
- hash_ar_table(hash);
}
HASH_ASSERT(bin < RHASH_AR_TABLE_MAX_BOUND);
@@ -1183,6 +1204,33 @@ ar_clear(VALUE hash)
}
}
+static void
+hash_st_free(VALUE hash)
+{
+ HASH_ASSERT(RHASH_ST_TABLE_P(hash));
+
+ st_table *tab = RHASH_ST_TABLE(hash);
+
+ xfree(tab->bins);
+ xfree(tab->entries);
+}
+
+static void
+hash_st_free_and_clear_table(VALUE hash)
+{
+ hash_st_free(hash);
+
+ RHASH_ST_CLEAR(hash);
+}
+
+void
+rb_hash_free(VALUE hash)
+{
+ if (RHASH_ST_TABLE_P(hash)) {
+ hash_st_free(hash);
+ }
+}
+
typedef int st_foreach_func(st_data_t, st_data_t, st_data_t);
struct foreach_safe_arg {
@@ -1271,78 +1319,79 @@ hash_foreach_iter(st_data_t key, st_data_t value, st_data_t argp, int error)
return hash_iter_status_check(status);
}
-static int
+static unsigned long
iter_lev_in_ivar(VALUE hash)
{
VALUE levval = rb_ivar_get(hash, id_hash_iter_lev);
HASH_ASSERT(FIXNUM_P(levval));
- return FIX2INT(levval);
+ long lev = FIX2LONG(levval);
+ HASH_ASSERT(lev >= 0);
+ return (unsigned long)lev;
}
void rb_ivar_set_internal(VALUE obj, ID id, VALUE val);
static void
-iter_lev_in_ivar_set(VALUE hash, int lev)
+iter_lev_in_ivar_set(VALUE hash, unsigned long lev)
{
- rb_ivar_set_internal(hash, id_hash_iter_lev, INT2FIX(lev));
+ HASH_ASSERT(lev >= RHASH_LEV_MAX);
+ HASH_ASSERT(POSFIXABLE(lev)); /* POSFIXABLE means fitting to long */
+ rb_ivar_set_internal(hash, id_hash_iter_lev, LONG2FIX((long)lev));
}
-static inline int
+static inline unsigned long
iter_lev_in_flags(VALUE hash)
{
- unsigned int u = (unsigned int)((RBASIC(hash)->flags >> RHASH_LEV_SHIFT) & RHASH_LEV_MAX);
- return (int)u;
+ return (unsigned long)((RBASIC(hash)->flags >> RHASH_LEV_SHIFT) & RHASH_LEV_MAX);
}
static inline void
-iter_lev_in_flags_set(VALUE hash, int lev)
+iter_lev_in_flags_set(VALUE hash, unsigned long lev)
{
+ HASH_ASSERT(lev <= RHASH_LEV_MAX);
RBASIC(hash)->flags = ((RBASIC(hash)->flags & ~RHASH_LEV_MASK) | ((VALUE)lev << RHASH_LEV_SHIFT));
}
-static int
-RHASH_ITER_LEV(VALUE hash)
+static inline bool
+hash_iterating_p(VALUE hash)
{
- int lev = iter_lev_in_flags(hash);
-
- if (lev == RHASH_LEV_MAX) {
- return iter_lev_in_ivar(hash);
- }
- else {
- return lev;
- }
+ return iter_lev_in_flags(hash) > 0;
}
static void
hash_iter_lev_inc(VALUE hash)
{
- int lev = iter_lev_in_flags(hash);
+ unsigned long lev = iter_lev_in_flags(hash);
if (lev == RHASH_LEV_MAX) {
- lev = iter_lev_in_ivar(hash);
- iter_lev_in_ivar_set(hash, lev+1);
+ lev = iter_lev_in_ivar(hash) + 1;
+ if (!POSFIXABLE(lev)) { /* paranoiac check */
+ rb_raise(rb_eRuntimeError, "too much nested iterations");
+ }
}
else {
lev += 1;
iter_lev_in_flags_set(hash, lev);
- if (lev == RHASH_LEV_MAX) {
- iter_lev_in_ivar_set(hash, lev);
- }
+ if (lev < RHASH_LEV_MAX) return;
}
+ iter_lev_in_ivar_set(hash, lev);
}
static void
hash_iter_lev_dec(VALUE hash)
{
- int lev = iter_lev_in_flags(hash);
+ unsigned long lev = iter_lev_in_flags(hash);
if (lev == RHASH_LEV_MAX) {
lev = iter_lev_in_ivar(hash);
- HASH_ASSERT(lev > 0);
- iter_lev_in_ivar_set(hash, lev-1);
+ if (lev > RHASH_LEV_MAX) {
+ iter_lev_in_ivar_set(hash, lev-1);
+ return;
+ }
+ rb_attr_delete(hash, id_hash_iter_lev);
}
- else {
- HASH_ASSERT(lev > 0);
- iter_lev_in_flags_set(hash, lev - 1);
+ else if (lev == 0) {
+ rb_raise(rb_eRuntimeError, "iteration level underflow");
}
+ iter_lev_in_flags_set(hash, lev - 1);
}
static VALUE
@@ -1420,6 +1469,16 @@ rb_hash_foreach(VALUE hash, rb_foreach_func *func, VALUE farg)
hash_verify(hash);
}
+void rb_st_compact_table(st_table *tab);
+
+static void
+compact_after_delete(VALUE hash)
+{
+ if (!hash_iterating_p(hash) && RHASH_ST_TABLE_P(hash)) {
+ rb_st_compact_table(RHASH_ST_TABLE(hash));
+ }
+}
+
static VALUE
hash_alloc_flags(VALUE klass, VALUE flags, VALUE ifnone, bool st)
{
@@ -1485,21 +1544,48 @@ rb_hash_new_capa(long capa)
static VALUE
hash_copy(VALUE ret, VALUE hash)
{
- if (!RHASH_EMPTY_P(hash)) {
- if (RHASH_AR_TABLE_P(hash)) {
+ if (RHASH_AR_TABLE_P(hash)) {
+ if (RHASH_AR_TABLE_P(ret)) {
ar_copy(ret, hash);
}
else {
- RHASH_ST_TABLE_SET(ret, st_copy(RHASH_ST_TABLE(hash)));
+ st_table *tab = RHASH_ST_TABLE(ret);
+ st_init_existing_table_with_size(tab, &objhash, RHASH_AR_TABLE_SIZE(hash));
+
+ int bound = RHASH_AR_TABLE_BOUND(hash);
+ for (int i = 0; i < bound; i++) {
+ if (ar_cleared_entry(hash, i)) continue;
+
+ ar_table_pair *pair = RHASH_AR_TABLE_REF(hash, i);
+ st_add_direct(tab, pair->key, pair->val);
+ RB_OBJ_WRITTEN(ret, Qundef, pair->key);
+ RB_OBJ_WRITTEN(ret, Qundef, pair->val);
+ }
}
}
+ else {
+ HASH_ASSERT(sizeof(st_table) <= sizeof(ar_table));
+
+ RHASH_SET_ST_FLAG(ret);
+ st_replace(RHASH_ST_TABLE(ret), RHASH_ST_TABLE(hash));
+
+ rb_gc_writebarrier_remember(ret);
+ }
return ret;
}
static VALUE
hash_dup_with_compare_by_id(VALUE hash)
{
- return hash_copy(copy_compare_by_id(rb_hash_new(), hash), hash);
+ VALUE dup = hash_alloc_flags(rb_cHash, 0, Qnil, RHASH_ST_TABLE_P(hash));
+ if (RHASH_ST_TABLE_P(hash)) {
+ RHASH_SET_ST_FLAG(dup);
+ }
+ else {
+ RHASH_UNSET_ST_FLAG(dup);
+ }
+
+ return hash_copy(dup, hash);
}
static VALUE
@@ -1533,7 +1619,7 @@ rb_hash_modify_check(VALUE hash)
rb_check_frozen(hash);
}
-RUBY_FUNC_EXPORTED struct st_table *
+struct st_table *
rb_hash_tbl_raw(VALUE hash, const char *file, int line)
{
return ar_force_convert_table(hash, file, line);
@@ -1594,7 +1680,7 @@ rb_hash_stlike_update(VALUE hash, st_data_t key, st_update_callback_func *func,
if (RHASH_AR_TABLE_P(hash)) {
int result = ar_update(hash, key, func, arg);
if (result == -1) {
- ar_try_convert_table(hash);
+ ar_force_convert_table(hash, __FILE__, __LINE__);
}
else {
return result;
@@ -1651,14 +1737,14 @@ tbl_update(VALUE hash, VALUE key, tbl_update_func func, st_data_t optional_arg)
return ret;
}
-#define UPDATE_CALLBACK(iter_lev, func) ((iter_lev) > 0 ? func##_noinsert : func##_insert)
+#define UPDATE_CALLBACK(iter_p, func) ((iter_p) ? func##_noinsert : func##_insert)
-#define RHASH_UPDATE_ITER(h, iter_lev, key, func, a) do { \
- tbl_update((h), (key), UPDATE_CALLBACK((iter_lev), func), (st_data_t)(a)); \
+#define RHASH_UPDATE_ITER(h, iter_p, key, func, a) do { \
+ tbl_update((h), (key), UPDATE_CALLBACK(iter_p, func), (st_data_t)(a)); \
} while (0)
#define RHASH_UPDATE(hash, key, func, arg) \
- RHASH_UPDATE_ITER(hash, RHASH_ITER_LEV(hash), key, func, arg)
+ RHASH_UPDATE_ITER(hash, hash_iterating_p(hash), key, func, arg)
static void
set_proc_default(VALUE hash, VALUE proc)
@@ -1681,7 +1767,7 @@ set_proc_default(VALUE hash, VALUE proc)
* Hash.new(default_value = nil) -> new_hash
* Hash.new {|hash, key| ... } -> new_hash
*
- * Returns a new empty \Hash object.
+ * Returns a new empty +Hash+ object.
*
* The initial default value and initial default proc for the new hash
* depend on which form above was used. See {Default Values}[rdoc-ref:Hash@Default+Values].
@@ -1740,26 +1826,26 @@ static VALUE rb_hash_to_a(VALUE hash);
* Hash[ [*2_element_arrays] ] -> new_hash
* Hash[*objects] -> new_hash
*
- * Returns a new \Hash object populated with the given objects, if any.
+ * Returns a new +Hash+ object populated with the given objects, if any.
* See Hash::new.
*
- * With no argument, returns a new empty \Hash.
+ * With no argument, returns a new empty +Hash+.
*
- * When the single given argument is a \Hash, returns a new \Hash
- * populated with the entries from the given \Hash, excluding the
+ * When the single given argument is a +Hash+, returns a new +Hash+
+ * populated with the entries from the given +Hash+, excluding the
* default value or proc.
*
* h = {foo: 0, bar: 1, baz: 2}
* Hash[h] # => {:foo=>0, :bar=>1, :baz=>2}
*
- * When the single given argument is an \Array of 2-element Arrays,
- * returns a new \Hash object wherein each 2-element array forms a
+ * When the single given argument is an Array of 2-element Arrays,
+ * returns a new +Hash+ object wherein each 2-element array forms a
* key-value entry:
*
* Hash[ [ [:foo, 0], [:bar, 1] ] ] # => {:foo=>0, :bar=>1}
*
* When the argument count is an even number;
- * returns a new \Hash object wherein each successive pair of arguments
+ * returns a new +Hash+ object wherein each successive pair of arguments
* has become a key-value entry:
*
* Hash[:foo, 0, :bar, 1] # => {:foo=>0, :bar=>1}
@@ -1784,7 +1870,8 @@ rb_hash_s_create(int argc, VALUE *argv, VALUE klass)
}
else {
hash = hash_alloc(klass);
- hash_copy(hash, tmp);
+ if (!RHASH_EMPTY_P(tmp))
+ hash_copy(hash, tmp);
return hash;
}
}
@@ -1846,14 +1933,14 @@ rb_check_hash_type(VALUE hash)
* call-seq:
* Hash.try_convert(obj) -> obj, new_hash, or nil
*
- * If +obj+ is a \Hash object, returns +obj+.
+ * If +obj+ is a +Hash+ object, returns +obj+.
*
* Otherwise if +obj+ responds to <tt>:to_hash</tt>,
* calls <tt>obj.to_hash</tt> and returns the result.
*
* Returns +nil+ if +obj+ does not respond to <tt>:to_hash</tt>
*
- * Raises an exception unless <tt>obj.to_hash</tt> returns a \Hash object.
+ * Raises an exception unless <tt>obj.to_hash</tt> returns a +Hash+ object.
*/
static VALUE
rb_hash_s_try_convert(VALUE dummy, VALUE hash)
@@ -1945,14 +2032,15 @@ rb_hash_rehash(VALUE hash)
VALUE tmp;
st_table *tbl;
- if (RHASH_ITER_LEV(hash) > 0) {
+ if (hash_iterating_p(hash)) {
rb_raise(rb_eRuntimeError, "rehash during iteration");
}
rb_hash_modify_check(hash);
if (RHASH_AR_TABLE_P(hash)) {
tmp = hash_alloc(0);
rb_hash_foreach(hash, rb_hash_rehash_i, (VALUE)tmp);
- ar_free_and_clear_table(hash);
+
+ hash_ar_free_and_clear_table(hash);
ar_copy(hash, tmp);
}
else if (RHASH_ST_TABLE_P(hash)) {
@@ -1964,6 +2052,7 @@ rb_hash_rehash(VALUE hash)
rb_hash_foreach(hash, rb_hash_rehash_i, (VALUE)tmp);
+ hash_st_free(hash);
RHASH_ST_TABLE_SET(hash, tbl);
RHASH_ST_CLEAR(tmp);
}
@@ -2220,7 +2309,7 @@ rb_hash_default_proc(VALUE hash)
* call-seq:
* hash.default_proc = proc -> proc
*
- * Sets the default proc for +self+ to +proc+:
+ * Sets the default proc for +self+ to +proc+
* (see {Default Values}[rdoc-ref:Hash@Default+Values]):
* h = {}
* h.default_proc # => nil
@@ -2273,7 +2362,7 @@ key_i(VALUE key, VALUE value, VALUE arg)
* h.key(0) # => :foo
* h.key(2) # => :bar
*
- * Returns +nil+ if so such value is found.
+ * Returns +nil+ if no such value is found.
*/
static VALUE
@@ -2372,6 +2461,7 @@ rb_hash_delete_m(VALUE hash, VALUE key)
val = rb_hash_delete_entry(hash, key);
if (!UNDEF_P(val)) {
+ compact_after_delete(hash);
return val;
}
else {
@@ -2405,7 +2495,7 @@ shift_i_safe(VALUE key, VALUE value, VALUE arg)
*
* Removes the first hash entry
* (see {Entry Order}[rdoc-ref:Hash@Entry+Order]);
- * returns a 2-element \Array containing the removed key and value:
+ * returns a 2-element Array containing the removed key and value:
* h = {foo: 0, bar: 1, baz: 2}
* h.shift # => [:foo, 0]
* h # => {:bar=>1, :baz=>2}
@@ -2421,7 +2511,7 @@ rb_hash_shift(VALUE hash)
rb_hash_modify_check(hash);
if (RHASH_AR_TABLE_P(hash)) {
var.key = Qundef;
- if (RHASH_ITER_LEV(hash) == 0) {
+ if (!hash_iterating_p(hash)) {
if (ar_shift(hash, &var.key, &var.val)) {
return rb_assoc_new(var.key, var.val);
}
@@ -2436,7 +2526,7 @@ rb_hash_shift(VALUE hash)
}
if (RHASH_ST_TABLE_P(hash)) {
var.key = Qundef;
- if (RHASH_ITER_LEV(hash) == 0) {
+ if (!hash_iterating_p(hash)) {
if (st_shift(RHASH_ST_TABLE(hash), &var.key, &var.val)) {
return rb_assoc_new(var.key, var.val);
}
@@ -2479,7 +2569,7 @@ hash_enum_size(VALUE hash, VALUE args, VALUE eobj)
* h = {foo: 0, bar: 1, baz: 2}
* h.delete_if {|key, value| value > 0 } # => {:foo=>0}
*
- * If no block given, returns a new \Enumerator:
+ * If no block given, returns a new Enumerator:
* h = {foo: 0, bar: 1, baz: 2}
* e = h.delete_if # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:delete_if>
* e.each { |key, value| value > 0 } # => {:foo=>0}
@@ -2492,6 +2582,7 @@ rb_hash_delete_if(VALUE hash)
rb_hash_modify_check(hash);
if (!RHASH_TABLE_EMPTY_P(hash)) {
rb_hash_foreach(hash, delete_if_i, hash);
+ compact_after_delete(hash);
}
return hash;
}
@@ -2508,7 +2599,7 @@ rb_hash_delete_if(VALUE hash)
*
* Returns +nil+ if no entries are removed.
*
- * Returns a new \Enumerator if no block given:
+ * Returns a new Enumerator if no block given:
* h = {foo: 0, bar: 1, baz: 2}
* e = h.reject! # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:reject!>
* e.each {|key, value| key.start_with?('b') } # => {:foo=>0}
@@ -2533,13 +2624,13 @@ rb_hash_reject_bang(VALUE hash)
* hash.reject {|key, value| ... } -> new_hash
* hash.reject -> new_enumerator
*
- * Returns a new \Hash object whose entries are all those
+ * Returns a new +Hash+ object whose entries are all those
* from +self+ for which the block returns +false+ or +nil+:
* h = {foo: 0, bar: 1, baz: 2}
* h1 = h.reject {|key, value| key.start_with?('b') }
* h1 # => {:foo=>0}
*
- * Returns a new \Enumerator if no block given:
+ * Returns a new Enumerator if no block given:
* h = {foo: 0, bar: 1, baz: 2}
* e = h.reject # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:reject>
* h1 = e.each {|key, value| key.start_with?('b') }
@@ -2555,6 +2646,7 @@ rb_hash_reject(VALUE hash)
result = hash_dup_with_compare_by_id(hash);
if (!RHASH_EMPTY_P(hash)) {
rb_hash_foreach(result, delete_if_i, result);
+ compact_after_delete(result);
}
return result;
}
@@ -2563,7 +2655,7 @@ rb_hash_reject(VALUE hash)
* call-seq:
* hash.slice(*keys) -> new_hash
*
- * Returns a new \Hash object containing the entries for the given +keys+:
+ * Returns a new +Hash+ object containing the entries for the given +keys+:
* h = {foo: 0, bar: 1, baz: 2}
* h.slice(:baz, :foo) # => {:baz=>2, :foo=>0}
*
@@ -2595,7 +2687,7 @@ rb_hash_slice(int argc, VALUE *argv, VALUE hash)
* call-seq:
* hsh.except(*keys) -> a_hash
*
- * Returns a new \Hash excluding entries for the given +keys+:
+ * Returns a new +Hash+ excluding entries for the given +keys+:
* h = { a: 100, b: 200, c: 300 }
* h.except(:a) #=> {:b=>200, :c=>300}
*
@@ -2614,6 +2706,7 @@ rb_hash_except(int argc, VALUE *argv, VALUE hash)
key = argv[i];
rb_hash_delete(result, key);
}
+ compact_after_delete(result);
return result;
}
@@ -2622,7 +2715,7 @@ rb_hash_except(int argc, VALUE *argv, VALUE hash)
* call-seq:
* hash.values_at(*keys) -> new_array
*
- * Returns a new \Array containing values for the given +keys+:
+ * Returns a new Array containing values for the given +keys+:
* h = {foo: 0, bar: 1, baz: 2}
* h.values_at(:baz, :foo) # => [2, 0]
*
@@ -2648,11 +2741,11 @@ rb_hash_values_at(int argc, VALUE *argv, VALUE hash)
* hash.fetch_values(*keys) -> new_array
* hash.fetch_values(*keys) {|key| ... } -> new_array
*
- * Returns a new \Array containing the values associated with the given keys *keys:
+ * Returns a new Array containing the values associated with the given keys *keys:
* h = {foo: 0, bar: 1, baz: 2}
* h.fetch_values(:baz, :foo) # => [2, 0]
*
- * Returns a new empty \Array if no arguments given.
+ * Returns a new empty Array if no arguments given.
*
* When a block is given, calls the block with each missing key,
* treating the block's return value as the value for that key:
@@ -2690,11 +2783,11 @@ keep_if_i(VALUE key, VALUE value, VALUE hash)
* hash.select {|key, value| ... } -> new_hash
* hash.select -> new_enumerator
*
- * Returns a new \Hash object whose entries are those for which the block returns a truthy value:
+ * Returns a new +Hash+ object whose entries are those for which the block returns a truthy value:
* h = {foo: 0, bar: 1, baz: 2}
* h.select {|key, value| value < 2 } # => {:foo=>0, :bar=>1}
*
- * Returns a new \Enumerator if no block given:
+ * Returns a new Enumerator if no block given:
* h = {foo: 0, bar: 1, baz: 2}
* e = h.select # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:select>
* e.each {|key, value| value < 2 } # => {:foo=>0, :bar=>1}
@@ -2709,6 +2802,7 @@ rb_hash_select(VALUE hash)
result = hash_dup_with_compare_by_id(hash);
if (!RHASH_EMPTY_P(hash)) {
rb_hash_foreach(result, keep_if_i, result);
+ compact_after_delete(result);
}
return result;
}
@@ -2724,7 +2818,7 @@ rb_hash_select(VALUE hash)
*
* Returns +nil+ if no entries were removed.
*
- * Returns a new \Enumerator if no block given:
+ * Returns a new Enumerator if no block given:
* h = {foo: 0, bar: 1, baz: 2}
* e = h.select! # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:select!>
* e.each { |key, value| value < 2 } # => {:foo=>0, :bar=>1}
@@ -2755,7 +2849,7 @@ rb_hash_select_bang(VALUE hash)
* h = {foo: 0, bar: 1, baz: 2}
* h.keep_if { |key, value| key.start_with?('b') } # => {:bar=>1, :baz=>2}
*
- * Returns a new \Enumerator if no block given:
+ * Returns a new Enumerator if no block given:
* h = {foo: 0, bar: 1, baz: 2}
* e = h.keep_if # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:keep_if>
* e.each { |key, value| key.start_with?('b') } # => {:bar=>1, :baz=>2}
@@ -2790,7 +2884,7 @@ rb_hash_clear(VALUE hash)
{
rb_hash_modify_check(hash);
- if (RHASH_ITER_LEV(hash) > 0) {
+ if (hash_iterating_p(hash)) {
rb_hash_foreach(hash, clear_i, 0);
}
else if (RHASH_AR_TABLE_P(hash)) {
@@ -2798,6 +2892,7 @@ rb_hash_clear(VALUE hash)
}
else {
st_clear(RHASH_ST_TABLE(hash));
+ compact_after_delete(hash);
}
return hash;
@@ -2860,20 +2955,15 @@ NOINSERT_UPDATE_CALLBACK(hash_aset_str)
VALUE
rb_hash_aset(VALUE hash, VALUE key, VALUE val)
{
- int iter_lev = RHASH_ITER_LEV(hash);
+ bool iter_p = hash_iterating_p(hash);
rb_hash_modify(hash);
- if (RHASH_TABLE_NULL_P(hash)) {
- if (iter_lev > 0) no_new_key();
- ar_alloc_table(hash);
- }
-
- if (RHASH_TYPE(hash) == &identhash || rb_obj_class(key) != rb_cString) {
- RHASH_UPDATE_ITER(hash, iter_lev, key, hash_aset, val);
+ if (!RHASH_STRING_KEY_P(hash, key)) {
+ RHASH_UPDATE_ITER(hash, iter_p, key, hash_aset, val);
}
else {
- RHASH_UPDATE_ITER(hash, iter_lev, key, hash_aset_str, val);
+ RHASH_UPDATE_ITER(hash, iter_p, key, hash_aset_str, val);
}
return val;
}
@@ -2893,7 +2983,7 @@ rb_hash_replace(VALUE hash, VALUE hash2)
{
rb_hash_modify_check(hash);
if (hash == hash2) return hash;
- if (RHASH_ITER_LEV(hash) > 0) {
+ if (hash_iterating_p(hash)) {
rb_raise(rb_eRuntimeError, "can't replace hash during iteration");
}
hash2 = to_hash(hash2);
@@ -2901,37 +2991,13 @@ rb_hash_replace(VALUE hash, VALUE hash2)
COPY_DEFAULT(hash, hash2);
if (RHASH_AR_TABLE_P(hash)) {
- ar_free_and_clear_table(hash);
+ hash_ar_free_and_clear_table(hash);
}
else {
- RHASH_ST_CLEAR(hash);
- }
-
- if (RHASH_AR_TABLE_P(hash2)) {
- if (RHASH_AR_TABLE_P(hash)) {
- ar_copy(hash, hash2);
- }
- else {
- st_table *tab = RHASH_ST_TABLE(hash);
- rb_st_init_existing_table_with_size(tab, &objhash, RHASH_AR_TABLE_SIZE(hash2));
-
- int bound = RHASH_AR_TABLE_BOUND(hash2);
- for (int i = 0; i < bound; i++) {
- if (ar_cleared_entry(hash2, i)) continue;
-
- ar_table_pair *pair = RHASH_AR_TABLE_REF(hash2, i);
- st_add_direct(tab, pair->key, pair->val);
- RB_OBJ_WRITTEN(hash, Qundef, pair->key);
- RB_OBJ_WRITTEN(hash, Qundef, pair->val);
- }
- }
+ hash_st_free_and_clear_table(hash);
}
- else {
- HASH_ASSERT(sizeof(st_table) <= sizeof(ar_table));
- RHASH_ST_TABLE_SET(hash, st_copy(RHASH_ST_TABLE(hash2)));
- rb_gc_writebarrier_remember(hash);
- }
+ hash_copy(hash, hash2);
return hash;
}
@@ -2968,7 +3034,7 @@ rb_hash_size_num(VALUE hash)
* {foo: 0, bar: 1, baz: 2}.empty? # => false
*/
-static VALUE
+VALUE
rb_hash_empty_p(VALUE hash)
{
return RBOOL(RHASH_EMPTY_P(hash));
@@ -2994,7 +3060,7 @@ each_value_i(VALUE key, VALUE value, VALUE _)
* 1
* 2
*
- * Returns a new \Enumerator if no block given:
+ * Returns a new Enumerator if no block given:
* h = {foo: 0, bar: 1, baz: 2}
* e = h.each_value # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:each_value>
* h1 = e.each {|value| puts value }
@@ -3033,7 +3099,7 @@ each_key_i(VALUE key, VALUE value, VALUE _)
* bar
* baz
*
- * Returns a new \Enumerator if no block given:
+ * Returns a new Enumerator if no block given:
* h = {foo: 0, bar: 1, baz: 2}
* e = h.each_key # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:each_key>
* h1 = e.each {|key| puts key }
@@ -3083,7 +3149,7 @@ each_pair_i_fast(VALUE key, VALUE value, VALUE _)
* bar: 1
* baz: 2
*
- * Returns a new \Enumerator if no block given:
+ * Returns a new Enumerator if no block given:
* h = {foo: 0, bar: 1, baz: 2}
* e = h.each_pair # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:each_pair>
* h1 = e.each {|key, value| puts "#{key}: #{value}"}
@@ -3142,7 +3208,7 @@ transform_keys_i(VALUE key, VALUE value, VALUE result)
* hash.transform_keys(hash2) {|other_key| ...} -> new_hash
* hash.transform_keys -> new_enumerator
*
- * Returns a new \Hash object; each entry has:
+ * Returns a new +Hash+ object; each entry has:
* * A key provided by the block.
* * The value from +self+.
*
@@ -3166,7 +3232,7 @@ transform_keys_i(VALUE key, VALUE value, VALUE result)
* h1 = h.transform_keys {|key| :bat }
* h1 # => {:bat=>2}
*
- * Returns a new \Enumerator if no block given:
+ * Returns a new Enumerator if no block given:
* h = {foo: 0, bar: 1, baz: 2}
* e = h.transform_keys # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:transform_keys>
* h1 = e.each { |key| key.to_s }
@@ -3257,6 +3323,7 @@ rb_hash_transform_keys_bang(int argc, VALUE *argv, VALUE hash)
rb_ary_clear(pairs);
rb_hash_clear(new_keys);
}
+ compact_after_delete(hash);
return hash;
}
@@ -3281,7 +3348,7 @@ transform_values_foreach_replace(st_data_t *key, st_data_t *value, st_data_t arg
* hash.transform_values {|value| ... } -> new_hash
* hash.transform_values -> new_enumerator
*
- * Returns a new \Hash object; each entry has:
+ * Returns a new +Hash+ object; each entry has:
* * A key from +self+.
* * A value provided by the block.
*
@@ -3290,7 +3357,7 @@ transform_values_foreach_replace(st_data_t *key, st_data_t *value, st_data_t arg
* h1 = h.transform_values {|value| value * 100}
* h1 # => {:foo=>0, :bar=>100, :baz=>200}
*
- * Returns a new \Enumerator if no block given:
+ * Returns a new Enumerator if no block given:
* h = {foo: 0, bar: 1, baz: 2}
* e = h.transform_values # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:transform_values>
* h1 = e.each { |value| value * 100}
@@ -3307,6 +3374,7 @@ rb_hash_transform_values(VALUE hash)
if (!RHASH_EMPTY_P(hash)) {
rb_hash_stlike_foreach_with_replace(result, transform_values_foreach_func, transform_values_foreach_replace, result);
+ compact_after_delete(result);
}
return result;
@@ -3321,7 +3389,7 @@ rb_hash_transform_values(VALUE hash)
* h = {foo: 0, bar: 1, baz: 2}
* h.transform_values! {|value| value * 100} # => {:foo=>0, :bar=>100, :baz=>200}
*
- * Returns a new \Enumerator if no block given:
+ * Returns a new Enumerator if no block given:
* h = {foo: 0, bar: 1, baz: 2}
* e = h.transform_values! # => #<Enumerator: {:foo=>0, :bar=>100, :baz=>200}:transform_values!>
* h1 = e.each {|value| value * 100}
@@ -3351,8 +3419,8 @@ to_a_i(VALUE key, VALUE value, VALUE ary)
* call-seq:
* hash.to_a -> new_array
*
- * Returns a new \Array of 2-element \Array objects;
- * each nested \Array contains a key-value pair from +self+:
+ * Returns a new Array of 2-element Array objects;
+ * each nested Array contains a key-value pair from +self+:
* h = {foo: 0, bar: 1, baz: 2}
* h.to_a # => [[:foo, 0], [:bar, 1], [:baz, 2]]
*/
@@ -3405,7 +3473,7 @@ inspect_hash(VALUE hash, VALUE dummy, int recur)
* call-seq:
* hash.inspect -> new_string
*
- * Returns a new \String containing the hash entries:
+ * Returns a new String containing the hash entries:
* h = {foo: 0, bar: 1, baz: 2}
* h.inspect # => "{:foo=>0, :bar=>1, :baz=>2}"
@@ -3470,15 +3538,15 @@ rb_hash_to_h_block(VALUE hash)
* hash.to_h -> self or new_hash
* hash.to_h {|key, value| ... } -> new_hash
*
- * For an instance of \Hash, returns +self+.
+ * For an instance of +Hash+, returns +self+.
*
- * For a subclass of \Hash, returns a new \Hash
+ * For a subclass of +Hash+, returns a new +Hash+
* containing the content of +self+.
*
- * When a block is given, returns a new \Hash object
+ * When a block is given, returns a new +Hash+ object
* whose content is based on the block;
- * the block should return a 2-element \Array object
- * specifying the key-value pair to be included in the returned \Array:
+ * the block should return a 2-element Array object
+ * specifying the key-value pair to be included in the returned Array:
* h = {foo: 0, bar: 1, baz: 2}
* h1 = h.to_h {|key, value| [value, key] }
* h1 # => {0=>:foo, 1=>:bar, 2=>:baz}
@@ -3508,7 +3576,7 @@ keys_i(VALUE key, VALUE value, VALUE ary)
* call-seq:
* hash.keys -> new_array
*
- * Returns a new \Array containing all keys in +self+:
+ * Returns a new Array containing all keys in +self+:
* h = {foo: 0, bar: 1, baz: 2}
* h.keys # => [:foo, :bar, :baz]
*/
@@ -3522,7 +3590,7 @@ rb_hash_keys(VALUE hash)
if (size == 0) return keys;
if (ST_DATA_COMPATIBLE_P(VALUE)) {
- RARRAY_PTR_USE_TRANSIENT(keys, ptr, {
+ RARRAY_PTR_USE(keys, ptr, {
if (RHASH_AR_TABLE_P(hash)) {
size = ar_keys(hash, ptr, size);
}
@@ -3552,7 +3620,7 @@ values_i(VALUE key, VALUE value, VALUE ary)
* call-seq:
* hash.values -> new_array
*
- * Returns a new \Array containing all values in +self+:
+ * Returns a new Array containing all values in +self+:
* h = {foo: 0, bar: 1, baz: 2}
* h.values # => [0, 1, 2]
*/
@@ -3569,14 +3637,14 @@ rb_hash_values(VALUE hash)
if (ST_DATA_COMPATIBLE_P(VALUE)) {
if (RHASH_AR_TABLE_P(hash)) {
rb_gc_writebarrier_remember(values);
- RARRAY_PTR_USE_TRANSIENT(values, ptr, {
+ RARRAY_PTR_USE(values, ptr, {
size = ar_values(hash, ptr, size);
});
}
else if (RHASH_ST_TABLE_P(hash)) {
st_table *table = RHASH_ST_TABLE(hash);
rb_gc_writebarrier_remember(values);
- RARRAY_PTR_USE_TRANSIENT(values, ptr, {
+ RARRAY_PTR_USE(values, ptr, {
size = st_values(table, ptr, size);
});
}
@@ -3723,7 +3791,7 @@ hash_equal(VALUE hash1, VALUE hash2, int eql)
* hash == object -> true or false
*
* Returns +true+ if all of the following are true:
- * * +object+ is a \Hash object.
+ * * +object+ is a +Hash+ object.
* * +hash+ and +object+ have the same keys (regardless of order).
* * For each key +key+, <tt>hash[key] == object[key]</tt>.
*
@@ -3745,16 +3813,15 @@ rb_hash_equal(VALUE hash1, VALUE hash2)
/*
* call-seq:
- * hash.eql? object -> true or false
+ * hash.eql?(object) -> true or false
*
* Returns +true+ if all of the following are true:
- * * +object+ is a \Hash object.
+ * * +object+ is a +Hash+ object.
* * +hash+ and +object+ have the same keys (regardless of order).
- * * For each key +key+, <tt>h[key] eql? object[key]</tt>.
+ * * For each key +key+, <tt>h[key].eql?(object[key])</tt>.
*
* Otherwise, returns +false+.
*
- * Equal:
* h1 = {foo: 0, bar: 1, baz: 2}
* h2 = {foo: 0, bar: 1, baz: 2}
* h1.eql? h2 # => true
@@ -3784,10 +3851,10 @@ hash_i(VALUE key, VALUE val, VALUE arg)
* call-seq:
* hash.hash -> an_integer
*
- * Returns the \Integer hash-code for the hash.
+ * Returns the Integer hash-code for the hash.
*
- * Two \Hash objects have the same hash-code if their content is the same
- * (regardless or order):
+ * Two +Hash+ objects have the same hash-code if their content is the same
+ * (regardless of order):
* h1 = {foo: 0, bar: 1, baz: 2}
* h2 = {baz: 2, bar: 1, foo: 0}
* h2.hash == h1.hash # => true
@@ -3818,7 +3885,7 @@ rb_hash_invert_i(VALUE key, VALUE value, VALUE hash)
* call-seq:
* hash.invert -> new_hash
*
- * Returns a new \Hash object with the each key-value pair inverted:
+ * Returns a new +Hash+ object with the each key-value pair inverted:
* h = {foo: 0, bar: 1, baz: 2}
* h1 = h.invert
* h1 # => {0=>:foo, 1=>:bar, 2=>:baz}
@@ -3839,18 +3906,9 @@ rb_hash_invert(VALUE hash)
}
static int
-rb_hash_update_callback(st_data_t *key, st_data_t *value, struct update_arg *arg, int existing)
-{
- *value = arg->arg;
- return ST_CONTINUE;
-}
-
-NOINSERT_UPDATE_CALLBACK(rb_hash_update_callback)
-
-static int
rb_hash_update_i(VALUE key, VALUE value, VALUE hash)
{
- RHASH_UPDATE(hash, key, rb_hash_update_callback, value);
+ rb_hash_aset(hash, key, value);
return ST_CONTINUE;
}
@@ -3862,6 +3920,9 @@ rb_hash_update_block_callback(st_data_t *key, st_data_t *value, struct update_ar
if (existing) {
newvalue = (st_data_t)rb_yield_values(3, (VALUE)*key, (VALUE)*value, (VALUE)newvalue);
}
+ else if (RHASH_STRING_KEY_P(arg->hash, *key) && !RB_OBJ_FROZEN(*key)) {
+ *key = rb_hash_key_str(*key);
+ }
*value = newvalue;
return ST_CONTINUE;
}
@@ -3883,7 +3944,7 @@ rb_hash_update_block_i(VALUE key, VALUE value, VALUE hash)
*
* Merges each of +other_hashes+ into +self+; returns +self+.
*
- * Each argument in +other_hashes+ must be a \Hash.
+ * Each argument in +other_hashes+ must be a +Hash+.
*
* With arguments and no block:
* * Returns +self+, after the given hashes are merged into it.
@@ -3997,16 +4058,16 @@ rb_hash_update_by(VALUE hash1, VALUE hash2, rb_hash_update_func *func)
* hash.merge(*other_hashes) -> new_hash
* hash.merge(*other_hashes) { |key, old_value, new_value| ... } -> new_hash
*
- * Returns the new \Hash formed by merging each of +other_hashes+
+ * Returns the new +Hash+ formed by merging each of +other_hashes+
* into a copy of +self+.
*
- * Each argument in +other_hashes+ must be a \Hash.
+ * Each argument in +other_hashes+ must be a +Hash+.
*
* ---
*
* With arguments and no block:
- * * Returns the new \Hash object formed by merging each successive
- * \Hash in +other_hashes+ into +self+.
+ * * Returns the new +Hash+ object formed by merging each successive
+ * +Hash+ in +other_hashes+ into +self+.
* * Each new-key entry is added at the end.
* * Each duplicate-key entry's value overwrites the previous value.
*
@@ -4017,7 +4078,7 @@ rb_hash_update_by(VALUE hash1, VALUE hash2, rb_hash_update_func *func)
* h.merge(h1, h2) # => {:foo=>0, :bar=>4, :baz=>2, :bat=>6, :bam=>5}
*
* With arguments and a block:
- * * Returns a new \Hash object that is the merge of +self+ and each given hash.
+ * * Returns a new +Hash+ object that is the merge of +self+ and each given hash.
* * The given hashes are merged left to right.
* * Each new-key entry is added at the end.
* * For each duplicate key:
@@ -4054,24 +4115,17 @@ assoc_cmp(VALUE a, VALUE b)
return !RTEST(rb_equal(a, b));
}
-static VALUE
-lookup2_call(VALUE arg)
-{
- VALUE *args = (VALUE *)arg;
- return rb_hash_lookup2(args[0], args[1], Qundef);
-}
-
-struct reset_hash_type_arg {
- VALUE hash;
- const struct st_hash_type *orighash;
+struct assoc_arg {
+ st_table *tbl;
+ st_data_t key;
};
static VALUE
-reset_hash_type(VALUE arg)
+assoc_lookup(VALUE arg)
{
- struct reset_hash_type_arg *p = (struct reset_hash_type_arg *)arg;
- HASH_ASSERT(RHASH_ST_TABLE_P(p->hash));
- RHASH_ST_TABLE(p->hash)->type = p->orighash;
+ struct assoc_arg *p = (struct assoc_arg*)arg;
+ st_data_t data;
+ if (st_lookup(p->tbl, p->key, &data)) return (VALUE)data;
return Qundef;
}
@@ -4091,7 +4145,7 @@ assoc_i(VALUE key, VALUE val, VALUE arg)
* call-seq:
* hash.assoc(key) -> new_array or nil
*
- * If the given +key+ is found, returns a 2-element \Array containing that key and its value:
+ * If the given +key+ is found, returns a 2-element Array containing that key and its value:
* h = {foo: 0, bar: 1, baz: 2}
* h.assoc(:bar) # => [:bar, 1]
*
@@ -4101,30 +4155,30 @@ assoc_i(VALUE key, VALUE val, VALUE arg)
static VALUE
rb_hash_assoc(VALUE hash, VALUE key)
{
- st_table *table;
- const struct st_hash_type *orighash;
VALUE args[2];
if (RHASH_EMPTY_P(hash)) return Qnil;
- ar_force_convert_table(hash, __FILE__, __LINE__);
- HASH_ASSERT(RHASH_ST_TABLE_P(hash));
- table = RHASH_ST_TABLE(hash);
- orighash = table->type;
-
- if (orighash != &identhash) {
- VALUE value;
- struct reset_hash_type_arg ensure_arg;
- struct st_hash_type assochash;
-
- assochash.compare = assoc_cmp;
- assochash.hash = orighash->hash;
- table->type = &assochash;
- args[0] = hash;
- args[1] = key;
- ensure_arg.hash = hash;
- ensure_arg.orighash = orighash;
- value = rb_ensure(lookup2_call, (VALUE)&args, reset_hash_type, (VALUE)&ensure_arg);
+ if (RHASH_ST_TABLE_P(hash) && !RHASH_IDENTHASH_P(hash)) {
+ VALUE value = Qundef;
+ st_table assoctable = *RHASH_ST_TABLE(hash);
+ assoctable.type = &(struct st_hash_type){
+ .compare = assoc_cmp,
+ .hash = assoctable.type->hash,
+ };
+ VALUE arg = (VALUE)&(struct assoc_arg){
+ .tbl = &assoctable,
+ .key = (st_data_t)key,
+ };
+
+ if (RB_OBJ_FROZEN(hash)) {
+ value = assoc_lookup(arg);
+ }
+ else {
+ hash_iter_lev_inc(hash);
+ value = rb_ensure(assoc_lookup, arg, hash_foreach_ensure, hash);
+ }
+ hash_verify(hash);
if (!UNDEF_P(value)) return rb_assoc_new(key, value);
}
@@ -4150,7 +4204,7 @@ rassoc_i(VALUE key, VALUE val, VALUE arg)
* call-seq:
* hash.rassoc(value) -> new_array or nil
*
- * Returns a new 2-element \Array consisting of the key and value
+ * Returns a new 2-element Array consisting of the key and value
* of the first-found entry whose value is <tt>==</tt> to value
* (see {Entry Order}[rdoc-ref:Hash@Entry+Order]):
* h = {foo: 0, bar: 1, baz: 1}
@@ -4187,7 +4241,7 @@ flatten_i(VALUE key, VALUE val, VALUE ary)
* hash.flatten -> new_array
* hash.flatten(level) -> new_array
*
- * Returns a new \Array object that is a 1-dimensional flattening of +self+.
+ * Returns a new Array object that is a 1-dimensional flattening of +self+.
*
* ---
*
@@ -4195,7 +4249,7 @@ flatten_i(VALUE key, VALUE val, VALUE ary)
* h = {foo: 0, bar: [:bat, 3], baz: 2}
* h.flatten # => [:foo, 0, :bar, [:bat, 3], :baz, 2]
*
- * Takes the depth of recursive flattening from \Integer argument +level+:
+ * Takes the depth of recursive flattening from Integer argument +level+:
* h = {foo: 0, bar: [:bat, [:baz, [:bat, ]]]}
* h.flatten(1) # => [:foo, 0, :bar, [:bat, [:baz, [:bat]]]]
* h.flatten(2) # => [:foo, 0, :bar, :bat, [:baz, [:bat]]]
@@ -4271,6 +4325,7 @@ rb_hash_compact(VALUE hash)
VALUE result = rb_hash_dup(hash);
if (!RHASH_EMPTY_P(hash)) {
rb_hash_foreach(result, delete_if_nil, result);
+ compact_after_delete(result);
}
else if (rb_hash_compare_by_id_p(hash)) {
result = rb_hash_compare_by_id(result);
@@ -4340,17 +4395,33 @@ rb_hash_compare_by_id(VALUE hash)
if (rb_hash_compare_by_id_p(hash)) return hash;
rb_hash_modify_check(hash);
- ar_force_convert_table(hash, __FILE__, __LINE__);
- HASH_ASSERT(RHASH_ST_TABLE_P(hash));
+ if (hash_iterating_p(hash)) {
+ rb_raise(rb_eRuntimeError, "compare_by_identity during iteration");
+ }
+
+ if (RHASH_TABLE_EMPTY_P(hash)) {
+ // Fast path: There's nothing to rehash, so we don't need a `tmp` table.
+ // We're most likely an AR table, so this will need an allocation.
+ ar_force_convert_table(hash, __FILE__, __LINE__);
+ HASH_ASSERT(RHASH_ST_TABLE_P(hash));
- tmp = hash_alloc(0);
- hash_st_table_init(tmp, &identhash, RHASH_SIZE(hash));
- identtable = RHASH_ST_TABLE(tmp);
+ RHASH_ST_TABLE(hash)->type = &identhash;
+ }
+ else {
+ // Slow path: Need to rehash the members of `self` into a new
+ // `tmp` table using the new `identhash` compare/hash functions.
+ tmp = hash_alloc(0);
+ hash_st_table_init(tmp, &identhash, RHASH_SIZE(hash));
+ identtable = RHASH_ST_TABLE(tmp);
- rb_hash_foreach(hash, rb_hash_rehash_i, (VALUE)tmp);
+ rb_hash_foreach(hash, rb_hash_rehash_i, (VALUE)tmp);
+ rb_hash_free(hash);
- RHASH_ST_TABLE_SET(hash, identtable);
- RHASH_ST_CLEAR(tmp);
+ // We know for sure `identtable` is an st table,
+ // so we can skip `ar_force_convert_table` here.
+ RHASH_ST_TABLE_SET(hash, identtable);
+ RHASH_ST_CLEAR(tmp);
+ }
return hash;
}
@@ -4365,7 +4436,7 @@ rb_hash_compare_by_id(VALUE hash)
VALUE
rb_hash_compare_by_id_p(VALUE hash)
{
- return RBOOL(RHASH_ST_TABLE_P(hash) && RHASH_ST_TABLE(hash)->type == &identhash);
+ return RBOOL(RHASH_IDENTHASH_P(hash));
}
VALUE
@@ -4632,7 +4703,7 @@ hash_proc_call(RB_BLOCK_CALL_FUNC_ARGLIST(key, hash))
* call-seq:
* hash.to_proc -> proc
*
- * Returns a \Proc object that maps a key to its value:
+ * Returns a Proc object that maps a key to its value:
* h = {foo: 0, bar: 1, baz: 2}
* proc = h.to_proc
* proc.class # => Proc
@@ -4646,6 +4717,7 @@ rb_hash_to_proc(VALUE hash)
return rb_func_lambda_new(hash_proc_call, hash, 1, 1);
}
+/* :nodoc: */
static VALUE
rb_hash_deconstruct_keys(VALUE hash, VALUE keys)
{
@@ -4676,14 +4748,13 @@ rb_hash_add_new_element(VALUE hash, VALUE key, VALUE val)
args[1] = val;
if (RHASH_AR_TABLE_P(hash)) {
- hash_ar_table(hash);
-
ret = ar_update(hash, (st_data_t)key, add_new_i, (st_data_t)args);
if (ret != -1) {
return ret;
}
- ar_try_convert_table(hash);
+ ar_force_convert_table(hash, __FILE__, __LINE__);
}
+
tbl = RHASH_TBL_RAW(hash);
return st_update(tbl, (st_data_t)key, add_new_i, (st_data_t)args);
@@ -4716,15 +4787,6 @@ rb_hash_bulk_insert(long argc, const VALUE *argv, VALUE hash)
if (argc > 0) {
st_index_t size = argc / 2;
- if (RHASH_TABLE_NULL_P(hash)) {
- if (size <= RHASH_AR_TABLE_MAX_SIZE) {
- hash_ar_table(hash);
- }
- else {
- RHASH_TBL_RAW(hash);
- }
- }
-
if (RHASH_AR_TABLE_P(hash) &&
(RHASH_AR_TABLE_SIZE(hash) + size <= RHASH_AR_TABLE_MAX_SIZE)) {
ar_bulk_insert(hash, argc, argv);
@@ -4849,7 +4911,7 @@ static inline const char *
env_name(volatile VALUE *s)
{
const char *name;
- SafeStringValue(*s);
+ StringValue(*s);
get_env_ptr(name, *s);
return name;
}
@@ -5326,8 +5388,8 @@ env_aset(VALUE nm, VALUE val)
env_delete(nm);
return Qnil;
}
- SafeStringValue(nm);
- SafeStringValue(val);
+ StringValue(nm);
+ StringValue(val);
/* nm can be modified in `val.to_str`, don't get `name` before
* check for `val` */
get_env_ptr(name, nm);
@@ -5514,7 +5576,7 @@ env_each_value(VALUE ehash)
* ENV.each_pair { |name, value| block } -> ENV
* ENV.each_pair -> an_enumerator
*
- * Yields each environment variable name and its value as a 2-element \Array:
+ * Yields each environment variable name and its value as a 2-element Array:
* h = {}
* ENV.each_pair { |name, value| h[name] = value } # => ENV
* h # => {"bar"=>"1", "foo"=>"0"}
@@ -5650,7 +5712,7 @@ env_delete_if(VALUE ehash)
* Returns +nil+ in the Array for each name that is not an ENV name:
* ENV.values_at('foo', 'bat', 'bar', 'bam') # => ["0", nil, "1", nil]
*
- * Returns an empty \Array if no names given.
+ * Returns an empty Array if no names given.
*
* Raises an exception if any name is invalid.
* See {Invalid Names and Values}[rdoc-ref:ENV@Invalid+Names+and+Values].
@@ -6190,7 +6252,7 @@ env_rassoc(VALUE dmy, VALUE obj)
static VALUE
env_key(VALUE dmy, VALUE value)
{
- SafeStringValue(value);
+ StringValue(value);
VALUE str = Qnil;
ENV_LOCK();
@@ -6597,27 +6659,27 @@ static const rb_data_type_t env_data_type = {
};
/*
- * A \Hash maps each of its unique keys to a specific value.
+ * A +Hash+ maps each of its unique keys to a specific value.
*
- * A \Hash has certain similarities to an \Array, but:
- * - An \Array index is always an \Integer.
- * - A \Hash key can be (almost) any object.
+ * A +Hash+ has certain similarities to an Array, but:
+ * - An Array index is always an Integer.
+ * - A +Hash+ key can be (almost) any object.
*
- * === \Hash \Data Syntax
+ * === +Hash+ \Data Syntax
*
- * The older syntax for \Hash data uses the "hash rocket," <tt>=></tt>:
+ * The older syntax for +Hash+ data uses the "hash rocket," <tt>=></tt>:
*
* h = {:foo => 0, :bar => 1, :baz => 2}
* h # => {:foo=>0, :bar=>1, :baz=>2}
*
- * Alternatively, but only for a \Hash key that's a \Symbol,
+ * Alternatively, but only for a +Hash+ key that's a Symbol,
* you can use a newer JSON-style syntax,
- * where each bareword becomes a \Symbol:
+ * where each bareword becomes a Symbol:
*
* h = {foo: 0, bar: 1, baz: 2}
* h # => {:foo=>0, :bar=>1, :baz=>2}
*
- * You can also use a \String in place of a bareword:
+ * You can also use a String in place of a bareword:
*
* h = {'foo': 0, 'bar': 1, 'baz': 2}
* h # => {:foo=>0, :bar=>1, :baz=>2}
@@ -6633,7 +6695,7 @@ static const rb_data_type_t env_data_type = {
* # Raises SyntaxError (syntax error, unexpected ':', expecting =>):
* h = {0: 'zero'}
*
- * Hash value can be omitted, meaning that value will be fetched from the context
+ * +Hash+ value can be omitted, meaning that value will be fetched from the context
* by the name of the key:
*
* x = 0
@@ -6643,24 +6705,24 @@ static const rb_data_type_t env_data_type = {
*
* === Common Uses
*
- * You can use a \Hash to give names to objects:
+ * You can use a +Hash+ to give names to objects:
*
* person = {name: 'Matz', language: 'Ruby'}
* person # => {:name=>"Matz", :language=>"Ruby"}
*
- * You can use a \Hash to give names to method arguments:
+ * You can use a +Hash+ to give names to method arguments:
*
* def some_method(hash)
* p hash
* end
* some_method({foo: 0, bar: 1, baz: 2}) # => {:foo=>0, :bar=>1, :baz=>2}
*
- * Note: when the last argument in a method call is a \Hash,
+ * Note: when the last argument in a method call is a +Hash+,
* the curly braces may be omitted:
*
* some_method(foo: 0, bar: 1, baz: 2) # => {:foo=>0, :bar=>1, :baz=>2}
*
- * You can use a \Hash to initialize an object:
+ * You can use a +Hash+ to initialize an object:
*
* class Dev
* attr_accessor :name, :language
@@ -6672,9 +6734,9 @@ static const rb_data_type_t env_data_type = {
* matz = Dev.new(name: 'Matz', language: 'Ruby')
* matz # => #<Dev: @name="Matz", @language="Ruby">
*
- * === Creating a \Hash
+ * === Creating a +Hash+
*
- * You can create a \Hash object explicitly with:
+ * You can create a +Hash+ object explicitly with:
*
* - A {hash literal}[rdoc-ref:syntax/literals.rdoc@Hash+Literals].
*
@@ -6682,47 +6744,47 @@ static const rb_data_type_t env_data_type = {
*
* - \Method #Hash.
*
- * You can create a \Hash by calling method Hash.new.
+ * You can create a +Hash+ by calling method Hash.new.
*
- * Create an empty Hash:
+ * Create an empty +Hash+:
*
* h = Hash.new
* h # => {}
* h.class # => Hash
*
- * You can create a \Hash by calling method Hash.[].
+ * You can create a +Hash+ by calling method Hash.[].
*
- * Create an empty Hash:
+ * Create an empty +Hash+:
*
* h = Hash[]
* h # => {}
*
- * Create a \Hash with initial entries:
+ * Create a +Hash+ with initial entries:
*
* h = Hash[foo: 0, bar: 1, baz: 2]
* h # => {:foo=>0, :bar=>1, :baz=>2}
*
- * You can create a \Hash by using its literal form (curly braces).
+ * You can create a +Hash+ by using its literal form (curly braces).
*
- * Create an empty \Hash:
+ * Create an empty +Hash+:
*
* h = {}
* h # => {}
*
- * Create a \Hash with initial entries:
+ * Create a +Hash+ with initial entries:
*
* h = {foo: 0, bar: 1, baz: 2}
* h # => {:foo=>0, :bar=>1, :baz=>2}
*
*
- * === \Hash Value Basics
+ * === +Hash+ Value Basics
*
- * The simplest way to retrieve a \Hash value (instance method #[]):
+ * The simplest way to retrieve a +Hash+ value (instance method #[]):
*
* h = {foo: 0, bar: 1, baz: 2}
* h[:foo] # => 0
*
- * The simplest way to create or update a \Hash value (instance method #[]=):
+ * The simplest way to create or update a +Hash+ value (instance method #[]=):
*
* h = {foo: 0, bar: 1, baz: 2}
* h[:bat] = 3 # => 3
@@ -6730,7 +6792,7 @@ static const rb_data_type_t env_data_type = {
* h[:foo] = 4 # => 4
* h # => {:foo=>4, :bar=>1, :baz=>2, :bat=>3}
*
- * The simplest way to delete a \Hash entry (instance method #delete):
+ * The simplest way to delete a +Hash+ entry (instance method #delete):
*
* h = {foo: 0, bar: 1, baz: 2}
* h.delete(:bar) # => 1
@@ -6738,13 +6800,13 @@ static const rb_data_type_t env_data_type = {
*
* === Entry Order
*
- * A \Hash object presents its entries in the order of their creation. This is seen in:
+ * A +Hash+ object presents its entries in the order of their creation. This is seen in:
*
* - Iterative methods such as <tt>each</tt>, <tt>each_key</tt>, <tt>each_pair</tt>, <tt>each_value</tt>.
* - Other order-sensitive methods such as <tt>shift</tt>, <tt>keys</tt>, <tt>values</tt>.
- * - The \String returned by method <tt>inspect</tt>.
+ * - The String returned by method <tt>inspect</tt>.
*
- * A new \Hash has its initial ordering per the given entries:
+ * A new +Hash+ has its initial ordering per the given entries:
*
* h = Hash[foo: 0, bar: 1]
* h # => {:foo=>0, :bar=>1}
@@ -6765,18 +6827,18 @@ static const rb_data_type_t env_data_type = {
* h[:foo] = 5
* h # => {:bar=>1, :baz=>3, :foo=>5}
*
- * === \Hash Keys
+ * === +Hash+ Keys
*
- * ==== \Hash Key Equivalence
+ * ==== +Hash+ Key Equivalence
*
* Two objects are treated as the same \hash key when their <code>hash</code> value
* is identical and the two objects are <code>eql?</code> to each other.
*
- * ==== Modifying an Active \Hash Key
+ * ==== Modifying an Active +Hash+ Key
*
- * Modifying a \Hash key while it is in use damages the hash's index.
+ * Modifying a +Hash+ key while it is in use damages the hash's index.
*
- * This \Hash has keys that are Arrays:
+ * This +Hash+ has keys that are Arrays:
*
* a0 = [ :foo, :bar ]
* a1 = [ :baz, :bat ]
@@ -6790,7 +6852,7 @@ static const rb_data_type_t env_data_type = {
* a0[0] = :bam
* a0.hash # => 1069447059
*
- * And damages the \Hash index:
+ * And damages the +Hash+ index:
*
* h.include?(a0) # => false
* h[a0] # => nil
@@ -6801,9 +6863,9 @@ static const rb_data_type_t env_data_type = {
* h.include?(a0) # => true
* h[a0] # => 0
*
- * A \String key is always safe.
- * That's because an unfrozen \String
- * passed as a key will be replaced by a duplicated and frozen \String:
+ * A String key is always safe.
+ * That's because an unfrozen String
+ * passed as a key will be replaced by a duplicated and frozen String:
*
* s = 'foo'
* s.frozen? # => false
@@ -6811,15 +6873,15 @@ static const rb_data_type_t env_data_type = {
* first_key = h.keys.first
* first_key.frozen? # => true
*
- * ==== User-Defined \Hash Keys
+ * ==== User-Defined +Hash+ Keys
*
- * To be useable as a \Hash key, objects must implement the methods <code>hash</code> and <code>eql?</code>.
- * Note: this requirement does not apply if the \Hash uses #compare_by_identity since comparison will then
+ * To be useable as a +Hash+ key, objects must implement the methods <code>hash</code> and <code>eql?</code>.
+ * Note: this requirement does not apply if the +Hash+ uses #compare_by_identity since comparison will then
* rely on the keys' object id instead of <code>hash</code> and <code>eql?</code>.
*
- * \Object defines basic implementation for <code>hash</code> and <code>eq?</code> that makes each object
+ * Object defines basic implementation for <code>hash</code> and <code>eq?</code> that makes each object
* a distinct key. Typically, user-defined classes will want to override these methods to provide meaningful
- * behavior, or for example inherit \Struct that has useful definitions for these.
+ * behavior, or for example inherit Struct that has useful definitions for these.
*
* A typical implementation of <code>hash</code> is based on the
* object's data while <code>eql?</code> is usually aliased to the overridden
@@ -6842,7 +6904,7 @@ static const rb_data_type_t env_data_type = {
* alias eql? ==
*
* def hash
- * @author.hash ^ @title.hash # XOR
+ * [self.class, @author, @title].hash
* end
* end
*
@@ -6900,9 +6962,9 @@ static const rb_data_type_t env_data_type = {
*
* To use a mutable object as default, it is recommended to use a default proc
*
- * ==== Default \Proc
+ * ==== Default Proc
*
- * When the default proc for a \Hash is set (i.e., not +nil+),
+ * When the default proc for a +Hash+ is set (i.e., not +nil+),
* the default value returned by method #[] is determined by the default proc alone.
*
* You can retrieve the default proc with method #default_proc:
@@ -6920,7 +6982,7 @@ static const rb_data_type_t env_data_type = {
*
* When the default proc is set (i.e., not +nil+)
* and method #[] is called with with a non-existent key,
- * #[] calls the default proc with both the \Hash object itself and the missing key,
+ * #[] calls the default proc with both the +Hash+ object itself and the missing key,
* then returns the proc's return value:
*
* h = Hash.new { |hash, key| "Default value for #{key}" }
@@ -6946,13 +7008,13 @@ static const rb_data_type_t env_data_type = {
*
* === What's Here
*
- * First, what's elsewhere. \Class \Hash:
+ * First, what's elsewhere. \Class +Hash+:
*
* - Inherits from {class Object}[rdoc-ref:Object@What-27s+Here].
* - Includes {module Enumerable}[rdoc-ref:Enumerable@What-27s+Here],
* which provides dozens of additional methods.
*
- * Here, class \Hash provides methods that are useful for:
+ * Here, class +Hash+ provides methods that are useful for:
*
* - {Creating a Hash}[rdoc-ref:Hash@Methods+for+Creating+a+Hash]
* - {Setting Hash State}[rdoc-ref:Hash@Methods+for+Setting+Hash+State]
@@ -6966,15 +7028,15 @@ static const rb_data_type_t env_data_type = {
* - {Transforming Keys and Values}[rdoc-ref:Hash@Methods+for+Transforming+Keys+and+Values]
* - {And more....}[rdoc-ref:Hash@Other+Methods]
*
- * \Class \Hash also includes methods from module Enumerable.
+ * \Class +Hash+ also includes methods from module Enumerable.
*
- * ==== Methods for Creating a \Hash
+ * ==== Methods for Creating a +Hash+
*
* - ::[]: Returns a new hash populated with given objects.
* - ::new: Returns a new empty hash.
* - ::try_convert: Returns a new hash created from a given object.
*
- * ==== Methods for Setting \Hash State
+ * ==== Methods for Setting +Hash+ State
*
* - #compare_by_identity: Sets +self+ to consider only identity in comparing keys.
* - #default=: Sets the default to a given value.
@@ -7014,7 +7076,7 @@ static const rb_data_type_t env_data_type = {
* - #key: Returns the key for the first-found entry with a given value.
* - #keys: Returns an array containing all keys in +self+.
* - #rassoc: Returns a 2-element array consisting of the key and value
- of the first-found entry having a given value.
+ * of the first-found entry having a given value.
* - #values: Returns an array containing all values in +self+/
* - #values_at: Returns an array containing values for given keys.
*
@@ -7056,8 +7118,8 @@ static const rb_data_type_t env_data_type = {
* - #inspect, #to_s: Returns a new String containing the hash entries.
* - #to_a: Returns a new array of 2-element arrays;
* each nested array contains a key-value pair from +self+.
- * - #to_h: Returns +self+ if a \Hash;
- * if a subclass of \Hash, returns a \Hash containing the entries from +self+.
+ * - #to_h: Returns +self+ if a +Hash+;
+ * if a subclass of +Hash+, returns a +Hash+ containing the entries from +self+.
* - #to_hash: Returns +self+.
* - #to_proc: Returns a proc that maps a given key to its value.
*
@@ -7179,15 +7241,15 @@ Init_Hash(void)
/* Document-class: ENV
*
- * ENV is a hash-like accessor for environment variables.
+ * +ENV+ is a hash-like accessor for environment variables.
*
* === Interaction with the Operating System
*
- * The ENV object interacts with the operating system's environment variables:
+ * The +ENV+ object interacts with the operating system's environment variables:
*
- * - When you get the value for a name in ENV, the value is retrieved from among the current environment variables.
- * - When you create or set a name-value pair in ENV, the name and value are immediately set in the environment variables.
- * - When you delete a name-value pair in ENV, it is immediately deleted from the environment variables.
+ * - When you get the value for a name in +ENV+, the value is retrieved from among the current environment variables.
+ * - When you create or set a name-value pair in +ENV+, the name and value are immediately set in the environment variables.
+ * - When you delete a name-value pair in +ENV+, it is immediately deleted from the environment variables.
*
* === Names and Values
*
@@ -7237,33 +7299,33 @@ Init_Hash(void)
*
* === About Ordering
*
- * ENV enumerates its name/value pairs in the order found
+ * +ENV+ enumerates its name/value pairs in the order found
* in the operating system's environment variables.
- * Therefore the ordering of ENV content is OS-dependent, and may be indeterminate.
+ * Therefore the ordering of +ENV+ content is OS-dependent, and may be indeterminate.
*
* This will be seen in:
- * - A Hash returned by an ENV method.
- * - An Enumerator returned by an ENV method.
+ * - A Hash returned by an +ENV+ method.
+ * - An Enumerator returned by an +ENV+ method.
* - An Array returned by ENV.keys, ENV.values, or ENV.to_a.
* - The String returned by ENV.inspect.
* - The Array returned by ENV.shift.
* - The name returned by ENV.key.
*
* === About the Examples
- * Some methods in ENV return ENV itself. Typically, there are many environment variables.
- * It's not useful to display a large ENV in the examples here,
- * so most example snippets begin by resetting the contents of ENV:
- * - ENV.replace replaces ENV with a new collection of entries.
- * - ENV.clear empties ENV.
+ * Some methods in +ENV+ return +ENV+ itself. Typically, there are many environment variables.
+ * It's not useful to display a large +ENV+ in the examples here,
+ * so most example snippets begin by resetting the contents of +ENV+:
+ * - ENV.replace replaces +ENV+ with a new collection of entries.
+ * - ENV.clear empties +ENV+.
*
- * == What's Here
+ * === What's Here
*
- * First, what's elsewhere. \Class \ENV:
+ * First, what's elsewhere. \Class +ENV+:
*
* - Inherits from {class Object}[rdoc-ref:Object@What-27s+Here].
* - Extends {module Enumerable}[rdoc-ref:Enumerable@What-27s+Here],
*
- * Here, class \ENV provides methods that are useful for:
+ * Here, class +ENV+ provides methods that are useful for:
*
* - {Querying}[rdoc-ref:ENV@Methods+for+Querying]
* - {Assigning}[rdoc-ref:ENV@Methods+for+Assigning]
@@ -7272,26 +7334,26 @@ Init_Hash(void)
* - {Converting}[rdoc-ref:ENV@Methods+for+Converting]
* - {And more ....}[rdoc-ref:ENV@More+Methods]
*
- * === Methods for Querying
+ * ==== Methods for Querying
*
* - ::[]: Returns the value for the given environment variable name if it exists:
- * - ::empty?: Returns whether \ENV is empty.
- * - ::has_value?, ::value?: Returns whether the given value is in \ENV.
+ * - ::empty?: Returns whether +ENV+ is empty.
+ * - ::has_value?, ::value?: Returns whether the given value is in +ENV+.
* - ::include?, ::has_key?, ::key?, ::member?: Returns whether the given name
- is in \ENV.
+ is in +ENV+.
* - ::key: Returns the name of the first entry with the given value.
* - ::size, ::length: Returns the number of entries.
* - ::value?: Returns whether any entry has the given value.
*
- * === Methods for Assigning
+ * ==== Methods for Assigning
*
* - ::[]=, ::store: Creates, updates, or deletes the named environment variable.
- * - ::clear: Removes every environment variable; returns \ENV:
- * - ::update, ::merge!: Adds to \ENV each key/value pair in the given hash.
- * - ::replace: Replaces the entire content of the \ENV
+ * - ::clear: Removes every environment variable; returns +ENV+:
+ * - ::update, ::merge!: Adds to +ENV+ each key/value pair in the given hash.
+ * - ::replace: Replaces the entire content of the +ENV+
* with the name/value pairs in the given hash.
*
- * === Methods for Deleting
+ * ==== Methods for Deleting
*
* - ::delete: Deletes the named environment variable name if it exists.
* - ::delete_if: Deletes entries selected by the block.
@@ -7300,22 +7362,22 @@ Init_Hash(void)
* - ::select!, ::filter!: Deletes entries selected by the block.
* - ::shift: Removes and returns the first entry.
*
- * === Methods for Iterating
+ * ==== Methods for Iterating
*
* - ::each, ::each_pair: Calls the block with each name/value pair.
* - ::each_key: Calls the block with each name.
* - ::each_value: Calls the block with each value.
*
- * === Methods for Converting
+ * ==== Methods for Converting
*
* - ::assoc: Returns a 2-element array containing the name and value
* of the named environment variable if it exists:
- * - ::clone: Returns \ENV (and issues a warning).
+ * - ::clone: Returns +ENV+ (and issues a warning).
* - ::except: Returns a hash of all name/value pairs except those given.
* - ::fetch: Returns the value for the given name.
- * - ::inspect: Returns the contents of \ENV as a string.
- * - ::invert: Returns a hash whose keys are the ENV values,
- and whose values are the corresponding ENV names.
+ * - ::inspect: Returns the contents of +ENV+ as a string.
+ * - ::invert: Returns a hash whose keys are the +ENV+ values,
+ and whose values are the corresponding +ENV+ names.
* - ::keys: Returns an array of all names.
* - ::rassoc: Returns the name and value of the first found entry
* that has the given value.
@@ -7329,11 +7391,11 @@ Init_Hash(void)
* - ::values: Returns all values as an array.
* - ::values_at: Returns an array of the values for the given name.
*
- * === More Methods
+ * ==== More Methods
*
* - ::dup: Raises an exception.
* - ::freeze: Raises an exception.
- * - ::rehash: Returns +nil+, without modifying \ENV.
+ * - ::rehash: Returns +nil+, without modifying +ENV+.
*
*/
@@ -7404,7 +7466,7 @@ Init_Hash(void)
rb_undef_method(envtbl_class, "initialize_dup");
/*
- * ENV is a Hash-like accessor for environment variables.
+ * +ENV+ is a Hash-like accessor for environment variables.
*
* See ENV (the class) for more details.
*/
diff --git a/hrtime.h b/hrtime.h
index 7ed4e6b04c..2ca7b0c088 100644
--- a/hrtime.h
+++ b/hrtime.h
@@ -6,6 +6,8 @@
# include <sys/time.h>
#endif
+#include "internal/compilers.h"
+
/*
* Hi-res monotonic clock. It is currently nsec resolution, which has over
* 500 years of range (with an unsigned 64-bit integer). Developers
@@ -61,7 +63,11 @@ rb_hrtime_mul(rb_hrtime_t a, rb_hrtime_t b)
{
rb_hrtime_t c;
-#ifdef HAVE_BUILTIN___BUILTIN_MUL_OVERFLOW
+#ifdef ckd_mul
+ if (ckd_mul(&c, a, b))
+ return RB_HRTIME_MAX;
+
+#elif __has_builtin(__builtin_mul_overflow)
if (__builtin_mul_overflow(a, b, &c))
return RB_HRTIME_MAX;
#else
@@ -81,7 +87,11 @@ rb_hrtime_add(rb_hrtime_t a, rb_hrtime_t b)
{
rb_hrtime_t c;
-#ifdef HAVE_BUILTIN___BUILTIN_ADD_OVERFLOW
+#ifdef ckd_add
+ if (ckd_add(&c, a, b))
+ return RB_HRTIME_MAX;
+
+#elif __has_builtin(__builtin_add_overflow)
if (__builtin_add_overflow(a, b, &c))
return RB_HRTIME_MAX;
#else
diff --git a/id_table.c b/id_table.c
index 650721c670..6bb067d09a 100644
--- a/id_table.c
+++ b/id_table.c
@@ -150,7 +150,7 @@ hash_table_raw_insert(struct rb_id_table *tbl, id_key_t key, VALUE val)
int mask = tbl->capa - 1;
int ix = key & mask;
int d = 1;
- assert(key != 0);
+ RUBY_ASSERT(key != 0);
while (ITEM_KEY_ISSET(tbl, ix)) {
ITEM_SET_COLLIDED(tbl, ix);
ix = (ix + d) & mask;
@@ -276,7 +276,7 @@ rb_id_table_foreach(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, v
if (ITEM_KEY_ISSET(tbl, i)) {
const id_key_t key = ITEM_GET_KEY(tbl, i);
enum rb_id_table_iterator_result ret = (*func)(key2id(key), tbl->items[i].val, data);
- assert(key != 0);
+ RUBY_ASSERT(key != 0);
if (ret == ID_TABLE_DELETE)
hash_delete_index(tbl, i);
diff --git a/imemo.c b/imemo.c
new file mode 100644
index 0000000000..1face1ce4e
--- /dev/null
+++ b/imemo.c
@@ -0,0 +1,588 @@
+
+#include "constant.h"
+#include "id_table.h"
+#include "internal.h"
+#include "internal/imemo.h"
+#include "vm_callinfo.h"
+
+size_t rb_iseq_memsize(const rb_iseq_t *iseq);
+void rb_iseq_mark_and_move(rb_iseq_t *iseq, bool reference_updating);
+void rb_iseq_free(const rb_iseq_t *iseq);
+
+const char *
+rb_imemo_name(enum imemo_type type)
+{
+ // put no default case to get a warning if an imemo type is missing
+ switch (type) {
+#define IMEMO_NAME(x) case imemo_##x: return #x;
+ IMEMO_NAME(ast);
+ IMEMO_NAME(callcache);
+ IMEMO_NAME(callinfo);
+ IMEMO_NAME(constcache);
+ IMEMO_NAME(cref);
+ IMEMO_NAME(env);
+ IMEMO_NAME(ifunc);
+ IMEMO_NAME(iseq);
+ IMEMO_NAME(memo);
+ IMEMO_NAME(ment);
+ IMEMO_NAME(parser_strterm);
+ IMEMO_NAME(svar);
+ IMEMO_NAME(throw_data);
+ IMEMO_NAME(tmpbuf);
+#undef IMEMO_NAME
+ default:
+ rb_bug("unreachable");
+ }
+}
+
+/* =========================================================================
+ * allocation
+ * ========================================================================= */
+
+VALUE
+rb_imemo_new(enum imemo_type type, VALUE v0)
+{
+ size_t size = RVALUE_SIZE;
+ VALUE flags = T_IMEMO | FL_WB_PROTECTED | (type << FL_USHIFT);
+ NEWOBJ_OF(obj, void, v0, flags, size, 0);
+
+ return (VALUE)obj;
+}
+
+static rb_imemo_tmpbuf_t *
+rb_imemo_tmpbuf_new(void)
+{
+ size_t size = sizeof(struct rb_imemo_tmpbuf_struct);
+ VALUE flags = T_IMEMO | (imemo_tmpbuf << FL_USHIFT);
+ NEWOBJ_OF(obj, struct rb_imemo_tmpbuf_struct, 0, flags, size, 0);
+
+ return obj;
+}
+
+void *
+rb_alloc_tmp_buffer_with_count(volatile VALUE *store, size_t size, size_t cnt)
+{
+ void *ptr;
+ rb_imemo_tmpbuf_t *tmpbuf;
+
+ /* Keep the order; allocate an empty imemo first then xmalloc, to
+ * get rid of potential memory leak */
+ tmpbuf = rb_imemo_tmpbuf_new();
+ *store = (VALUE)tmpbuf;
+ ptr = ruby_xmalloc(size);
+ tmpbuf->ptr = ptr;
+ tmpbuf->cnt = cnt;
+
+ return ptr;
+}
+
+void *
+rb_alloc_tmp_buffer(volatile VALUE *store, long len)
+{
+ long cnt;
+
+ if (len < 0 || (cnt = (long)roomof(len, sizeof(VALUE))) < 0) {
+ rb_raise(rb_eArgError, "negative buffer size (or size too big)");
+ }
+
+ return rb_alloc_tmp_buffer_with_count(store, len, cnt);
+}
+
+void
+rb_free_tmp_buffer(volatile VALUE *store)
+{
+ rb_imemo_tmpbuf_t *s = (rb_imemo_tmpbuf_t*)ATOMIC_VALUE_EXCHANGE(*store, 0);
+ if (s) {
+ void *ptr = ATOMIC_PTR_EXCHANGE(s->ptr, 0);
+ s->cnt = 0;
+ ruby_xfree(ptr);
+ }
+}
+
+rb_imemo_tmpbuf_t *
+rb_imemo_tmpbuf_parser_heap(void *buf, rb_imemo_tmpbuf_t *old_heap, size_t cnt)
+{
+ rb_imemo_tmpbuf_t *tmpbuf = rb_imemo_tmpbuf_new();
+ tmpbuf->ptr = buf;
+ tmpbuf->next = old_heap;
+ tmpbuf->cnt = cnt;
+
+ return tmpbuf;
+}
+
+#if IMEMO_DEBUG
+VALUE
+rb_imemo_new_debug(enum imemo_type type, VALUE v0, const char *file, int line)
+{
+ VALUE memo = rb_imemo_new(type, v0);
+ fprintf(stderr, "memo %p (type: %d) @ %s:%d\n", (void *)memo, imemo_type(memo), file, line);
+ return memo;
+}
+#endif
+
+/* =========================================================================
+ * memsize
+ * ========================================================================= */
+
+size_t
+rb_imemo_memsize(VALUE obj)
+{
+ size_t size = 0;
+ switch (imemo_type(obj)) {
+ case imemo_ast:
+ rb_bug("imemo_ast is obsolete");
+
+ break;
+ case imemo_callcache:
+ break;
+ case imemo_callinfo:
+ break;
+ case imemo_constcache:
+ break;
+ case imemo_cref:
+ break;
+ case imemo_env:
+ size += ((rb_env_t *)obj)->env_size * sizeof(VALUE);
+
+ break;
+ case imemo_ifunc:
+ break;
+ case imemo_iseq:
+ size += rb_iseq_memsize((rb_iseq_t *)obj);
+
+ break;
+ case imemo_memo:
+ break;
+ case imemo_ment:
+ size += sizeof(((rb_method_entry_t *)obj)->def);
+
+ break;
+ case imemo_parser_strterm:
+ break;
+ case imemo_svar:
+ break;
+ case imemo_throw_data:
+ break;
+ case imemo_tmpbuf:
+ size += ((rb_imemo_tmpbuf_t *)obj)->cnt * sizeof(VALUE);
+
+ break;
+ default:
+ rb_bug("unreachable");
+ }
+
+ return size;
+}
+
+/* =========================================================================
+ * mark
+ * ========================================================================= */
+
+static enum rb_id_table_iterator_result
+cc_table_mark_i(ID id, VALUE ccs_ptr, void *data)
+{
+ struct rb_class_cc_entries *ccs = (struct rb_class_cc_entries *)ccs_ptr;
+ VM_ASSERT(vm_ccs_p(ccs));
+ VM_ASSERT(id == ccs->cme->called_id);
+
+ if (METHOD_ENTRY_INVALIDATED(ccs->cme)) {
+ rb_vm_ccs_free(ccs);
+ return ID_TABLE_DELETE;
+ }
+ else {
+ rb_gc_mark_movable((VALUE)ccs->cme);
+
+ for (int i=0; i<ccs->len; i++) {
+ VM_ASSERT((VALUE)data == ccs->entries[i].cc->klass);
+ VM_ASSERT(vm_cc_check_cme(ccs->entries[i].cc, ccs->cme));
+
+ rb_gc_mark_movable((VALUE)ccs->entries[i].cc);
+ }
+ return ID_TABLE_CONTINUE;
+ }
+}
+
+void
+rb_cc_table_mark(VALUE klass)
+{
+ struct rb_id_table *cc_tbl = RCLASS_CC_TBL(klass);
+ if (cc_tbl) {
+ rb_id_table_foreach(cc_tbl, cc_table_mark_i, (void *)klass);
+ }
+}
+
+static bool
+moved_or_living_object_strictly_p(VALUE obj)
+{
+ return obj && (rb_objspace_markable_object_p(obj) || BUILTIN_TYPE(obj) == T_MOVED);
+}
+
+static void
+mark_and_move_method_entry(rb_method_entry_t *ment, bool reference_updating)
+{
+ rb_method_definition_t *def = ment->def;
+
+ rb_gc_mark_and_move(&ment->owner);
+ rb_gc_mark_and_move(&ment->defined_class);
+
+ if (def) {
+ switch (def->type) {
+ case VM_METHOD_TYPE_ISEQ:
+ if (def->body.iseq.iseqptr) {
+ rb_gc_mark_and_move_ptr(&def->body.iseq.iseqptr);
+ }
+ rb_gc_mark_and_move_ptr(&def->body.iseq.cref);
+
+ if (!reference_updating) {
+ if (def->iseq_overload && ment->defined_class) {
+ // it can be a key of "overloaded_cme" table
+ // so it should be pinned.
+ rb_gc_mark((VALUE)ment);
+ }
+ }
+ break;
+ case VM_METHOD_TYPE_ATTRSET:
+ case VM_METHOD_TYPE_IVAR:
+ rb_gc_mark_and_move(&def->body.attr.location);
+ break;
+ case VM_METHOD_TYPE_BMETHOD:
+ rb_gc_mark_and_move(&def->body.bmethod.proc);
+ if (!reference_updating) {
+ if (def->body.bmethod.hooks) rb_hook_list_mark(def->body.bmethod.hooks);
+ }
+ break;
+ case VM_METHOD_TYPE_ALIAS:
+ rb_gc_mark_and_move_ptr(&def->body.alias.original_me);
+ return;
+ case VM_METHOD_TYPE_REFINED:
+ rb_gc_mark_and_move_ptr(&def->body.refined.orig_me);
+ break;
+ case VM_METHOD_TYPE_CFUNC:
+ case VM_METHOD_TYPE_ZSUPER:
+ case VM_METHOD_TYPE_MISSING:
+ case VM_METHOD_TYPE_OPTIMIZED:
+ case VM_METHOD_TYPE_UNDEF:
+ case VM_METHOD_TYPE_NOTIMPLEMENTED:
+ break;
+ }
+ }
+}
+
+void
+rb_imemo_mark_and_move(VALUE obj, bool reference_updating)
+{
+ switch (imemo_type(obj)) {
+ case imemo_ast:
+ rb_bug("imemo_ast is obsolete");
+
+ break;
+ case imemo_callcache: {
+ /* cc is callcache.
+ *
+ * cc->klass (klass) should not be marked because if the klass is
+ * free'ed, the cc->klass will be cleared by `vm_cc_invalidate()`.
+ *
+ * cc->cme (cme) should not be marked because if cc is invalidated
+ * when cme is free'ed.
+ * - klass marks cme if klass uses cme.
+ * - caller classe's ccs->cme marks cc->cme.
+ * - if cc is invalidated (klass doesn't refer the cc),
+ * cc is invalidated by `vm_cc_invalidate()` and cc->cme is
+ * not be accessed.
+ * - On the multi-Ractors, cme will be collected with global GC
+ * so that it is safe if GC is not interleaving while accessing
+ * cc and cme.
+ * - However, cc_type_super and cc_type_refinement are not chained
+ * from ccs so cc->cme should be marked; the cme might be
+ * reachable only through cc in these cases.
+ */
+ struct rb_callcache *cc = (struct rb_callcache *)obj;
+ if (reference_updating) {
+ if (!cc->klass) {
+ // already invalidated
+ }
+ else {
+ if (moved_or_living_object_strictly_p(cc->klass) &&
+ moved_or_living_object_strictly_p((VALUE)cc->cme_)) {
+ *((VALUE *)&cc->klass) = rb_gc_location(cc->klass);
+ *((struct rb_callable_method_entry_struct **)&cc->cme_) =
+ (struct rb_callable_method_entry_struct *)rb_gc_location((VALUE)cc->cme_);
+ }
+ else {
+ vm_cc_invalidate(cc);
+ }
+ }
+ }
+ else {
+ if (vm_cc_super_p(cc) || vm_cc_refinement_p(cc)) {
+ rb_gc_mark_movable((VALUE)cc->cme_);
+ }
+ }
+
+ break;
+ }
+ case imemo_callinfo:
+ break;
+ case imemo_constcache: {
+ struct iseq_inline_constant_cache_entry *ice = (struct iseq_inline_constant_cache_entry *)obj;
+
+ rb_gc_mark_and_move(&ice->value);
+
+ break;
+ }
+ case imemo_cref: {
+ rb_cref_t *cref = (rb_cref_t *)obj;
+
+ rb_gc_mark_and_move(&cref->klass_or_self);
+ rb_gc_mark_and_move_ptr(&cref->next);
+ rb_gc_mark_and_move(&cref->refinements);
+
+ break;
+ }
+ case imemo_env: {
+ rb_env_t *env = (rb_env_t *)obj;
+
+ if (LIKELY(env->ep)) {
+ // just after newobj() can be NULL here.
+ RUBY_ASSERT(rb_gc_location(env->ep[VM_ENV_DATA_INDEX_ENV]) == rb_gc_location(obj));
+ RUBY_ASSERT(reference_updating || VM_ENV_ESCAPED_P(env->ep));
+
+ for (unsigned int i = 0; i < env->env_size; i++) {
+ rb_gc_mark_and_move((VALUE *)&env->env[i]);
+ }
+
+ rb_gc_mark_and_move_ptr(&env->iseq);
+
+ if (reference_updating) {
+ ((VALUE *)env->ep)[VM_ENV_DATA_INDEX_ENV] = rb_gc_location(env->ep[VM_ENV_DATA_INDEX_ENV]);
+ }
+ else {
+ VM_ENV_FLAGS_SET(env->ep, VM_ENV_FLAG_WB_REQUIRED);
+ rb_gc_mark_movable( (VALUE)rb_vm_env_prev_env(env));
+ }
+ }
+
+ break;
+ }
+ case imemo_ifunc: {
+ struct vm_ifunc *ifunc = (struct vm_ifunc *)obj;
+
+ if (!reference_updating) {
+ rb_gc_mark_maybe((VALUE)ifunc->data);
+ }
+
+ break;
+ }
+ case imemo_iseq:
+ rb_iseq_mark_and_move((rb_iseq_t *)obj, reference_updating);
+ break;
+ case imemo_memo: {
+ struct MEMO *memo = (struct MEMO *)obj;
+
+ rb_gc_mark_and_move((VALUE *)&memo->v1);
+ rb_gc_mark_and_move((VALUE *)&memo->v2);
+ if (!reference_updating) {
+ rb_gc_mark_maybe(memo->u3.value);
+ }
+
+ break;
+ }
+ case imemo_ment:
+ mark_and_move_method_entry((rb_method_entry_t *)obj, reference_updating);
+ break;
+ case imemo_parser_strterm:
+ break;
+ case imemo_svar: {
+ struct vm_svar *svar = (struct vm_svar *)obj;
+
+ rb_gc_mark_and_move((VALUE *)&svar->cref_or_me);
+ rb_gc_mark_and_move((VALUE *)&svar->lastline);
+ rb_gc_mark_and_move((VALUE *)&svar->backref);
+ rb_gc_mark_and_move((VALUE *)&svar->others);
+
+ break;
+ }
+ case imemo_throw_data: {
+ struct vm_throw_data *throw_data = (struct vm_throw_data *)obj;
+
+ rb_gc_mark_and_move((VALUE *)&throw_data->throw_obj);
+
+ break;
+ }
+ case imemo_tmpbuf: {
+ const rb_imemo_tmpbuf_t *m = (const rb_imemo_tmpbuf_t *)obj;
+
+ if (!reference_updating) {
+ do {
+ rb_gc_mark_locations(m->ptr, m->ptr + m->cnt);
+ } while ((m = m->next) != NULL);
+ }
+
+ break;
+ }
+ default:
+ rb_bug("unreachable");
+ }
+}
+
+/* =========================================================================
+ * free
+ * ========================================================================= */
+
+static enum rb_id_table_iterator_result
+free_const_entry_i(VALUE value, void *data)
+{
+ rb_const_entry_t *ce = (rb_const_entry_t *)value;
+ xfree(ce);
+ return ID_TABLE_CONTINUE;
+}
+
+void
+rb_free_const_table(struct rb_id_table *tbl)
+{
+ rb_id_table_foreach_values(tbl, free_const_entry_i, 0);
+ rb_id_table_free(tbl);
+}
+
+// alive: if false, target pointers can be freed already.
+static void
+vm_ccs_free(struct rb_class_cc_entries *ccs, int alive, VALUE klass)
+{
+ if (ccs->entries) {
+ for (int i=0; i<ccs->len; i++) {
+ const struct rb_callcache *cc = ccs->entries[i].cc;
+ if (!alive) {
+ void *ptr = asan_unpoison_object_temporary((VALUE)cc);
+ // ccs can be free'ed.
+ if (rb_objspace_markable_object_p((VALUE)cc) &&
+ IMEMO_TYPE_P(cc, imemo_callcache) &&
+ cc->klass == klass) {
+ // OK. maybe target cc.
+ }
+ else {
+ if (ptr) {
+ asan_poison_object((VALUE)cc);
+ }
+ continue;
+ }
+ if (ptr) {
+ asan_poison_object((VALUE)cc);
+ }
+ }
+
+ VM_ASSERT(!vm_cc_super_p(cc) && !vm_cc_refinement_p(cc));
+ vm_cc_invalidate(cc);
+ }
+ ruby_xfree(ccs->entries);
+ }
+ ruby_xfree(ccs);
+}
+
+void
+rb_vm_ccs_free(struct rb_class_cc_entries *ccs)
+{
+ RB_DEBUG_COUNTER_INC(ccs_free);
+ vm_ccs_free(ccs, true, Qundef);
+}
+
+static enum rb_id_table_iterator_result
+cc_table_free_i(VALUE ccs_ptr, void *data)
+{
+ struct rb_class_cc_entries *ccs = (struct rb_class_cc_entries *)ccs_ptr;
+ VALUE klass = (VALUE)data;
+ VM_ASSERT(vm_ccs_p(ccs));
+
+ vm_ccs_free(ccs, false, klass);
+
+ return ID_TABLE_CONTINUE;
+}
+
+void
+rb_cc_table_free(VALUE klass)
+{
+ struct rb_id_table *cc_tbl = RCLASS_CC_TBL(klass);
+
+ if (cc_tbl) {
+ rb_id_table_foreach_values(cc_tbl, cc_table_free_i, (void *)klass);
+ rb_id_table_free(cc_tbl);
+ }
+}
+
+void
+rb_imemo_free(VALUE obj)
+{
+ switch (imemo_type(obj)) {
+ case imemo_ast:
+ rb_bug("imemo_ast is obsolete");
+
+ break;
+ case imemo_callcache:
+ RB_DEBUG_COUNTER_INC(obj_imemo_callcache);
+
+ break;
+ case imemo_callinfo:{
+ const struct rb_callinfo *ci = ((const struct rb_callinfo *)obj);
+
+ rb_vm_ci_free(ci);
+ if (ci->kwarg) {
+ ((struct rb_callinfo_kwarg *)ci->kwarg)->references--;
+ if (ci->kwarg->references == 0) xfree((void *)ci->kwarg);
+ }
+ RB_DEBUG_COUNTER_INC(obj_imemo_callinfo);
+
+ break;
+ }
+ case imemo_constcache:
+ RB_DEBUG_COUNTER_INC(obj_imemo_constcache);
+
+ break;
+ case imemo_cref:
+ RB_DEBUG_COUNTER_INC(obj_imemo_cref);
+
+ break;
+ case imemo_env: {
+ rb_env_t *env = (rb_env_t *)obj;
+
+ RUBY_ASSERT(VM_ENV_ESCAPED_P(env->ep));
+ xfree((VALUE *)env->env);
+ RB_DEBUG_COUNTER_INC(obj_imemo_env);
+
+ break;
+ }
+ case imemo_ifunc:
+ RB_DEBUG_COUNTER_INC(obj_imemo_ifunc);
+ break;
+ case imemo_iseq:
+ rb_iseq_free((rb_iseq_t *)obj);
+ RB_DEBUG_COUNTER_INC(obj_imemo_iseq);
+
+ break;
+ case imemo_memo:
+ RB_DEBUG_COUNTER_INC(obj_imemo_memo);
+
+ break;
+ case imemo_ment:
+ rb_free_method_entry((rb_method_entry_t *)obj);
+ RB_DEBUG_COUNTER_INC(obj_imemo_ment);
+
+ break;
+ case imemo_parser_strterm:
+ RB_DEBUG_COUNTER_INC(obj_imemo_parser_strterm);
+
+ break;
+ case imemo_svar:
+ RB_DEBUG_COUNTER_INC(obj_imemo_svar);
+ break;
+ case imemo_throw_data:
+ RB_DEBUG_COUNTER_INC(obj_imemo_throw_data);
+
+ break;
+ case imemo_tmpbuf:
+ xfree(((rb_imemo_tmpbuf_t *)obj)->ptr);
+ RB_DEBUG_COUNTER_INC(obj_imemo_tmpbuf);
+
+ break;
+ default:
+ rb_bug("unreachable");
+ }
+}
diff --git a/include/ruby/assert.h b/include/ruby/assert.h
index 0c052363bc..e9edd9e640 100644
--- a/include/ruby/assert.h
+++ b/include/ruby/assert.h
@@ -22,6 +22,7 @@
*/
#include "ruby/internal/assume.h"
#include "ruby/internal/attr/cold.h"
+#include "ruby/internal/attr/format.h"
#include "ruby/internal/attr/noreturn.h"
#include "ruby/internal/cast.h"
#include "ruby/internal/dllexport.h"
@@ -132,6 +133,11 @@ RBIMPL_SYMBOL_EXPORT_BEGIN()
RBIMPL_ATTR_NORETURN()
RBIMPL_ATTR_COLD()
void rb_assert_failure(const char *file, int line, const char *name, const char *expr);
+
+RBIMPL_ATTR_NORETURN()
+RBIMPL_ATTR_COLD()
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 5, 6)
+void rb_assert_failure_detail(const char *file, int line, const char *name, const char *expr, const char *fmt, ...);
RBIMPL_SYMBOL_EXPORT_END()
#ifdef RUBY_FUNCTION_NAME_STRING
@@ -147,8 +153,28 @@ RBIMPL_SYMBOL_EXPORT_END()
*
* @param mesg The message to display.
*/
-#define RUBY_ASSERT_FAIL(mesg) \
+#if defined(HAVE___VA_OPT__)
+# if RBIMPL_HAS_WARNING("-Wgnu-zero-variadic-macro-arguments")
+/* __VA_OPT__ is to be used for the zero variadic macro arguments
+ * cases. */
+RBIMPL_WARNING_IGNORED(-Wgnu-zero-variadic-macro-arguments)
+# endif
+# define RBIMPL_VA_OPT_ARGS(...) __VA_OPT__(,) __VA_ARGS__
+
+# define RUBY_ASSERT_FAIL(mesg, ...) \
+ rb_assert_failure##__VA_OPT__(_detail)( \
+ __FILE__, __LINE__, RBIMPL_ASSERT_FUNC, mesg RBIMPL_VA_OPT_ARGS(__VA_ARGS__))
+#elif !defined(__cplusplus)
+# define RBIMPL_VA_OPT_ARGS(...)
+
+# define RUBY_ASSERT_FAIL(mesg, ...) \
+ rb_assert_failure(__FILE__, __LINE__, RBIMPL_ASSERT_FUNC, mesg)
+#else
+# undef RBIMPL_VA_OPT_ARGS
+
+# define RUBY_ASSERT_FAIL(mesg) \
rb_assert_failure(__FILE__, __LINE__, RBIMPL_ASSERT_FUNC, mesg)
+#endif
/**
* Asserts that the expression is truthy. If not aborts with the message.
@@ -156,15 +182,25 @@ RBIMPL_SYMBOL_EXPORT_END()
* @param expr What supposedly evaluates to true.
* @param mesg The message to display on failure.
*/
-#define RUBY_ASSERT_MESG(expr, mesg) \
+#if defined(RBIMPL_VA_OPT_ARGS)
+# define RUBY_ASSERT_MESG(expr, ...) \
+ (RB_LIKELY(expr) ? RBIMPL_ASSERT_NOTHING : RUBY_ASSERT_FAIL(__VA_ARGS__))
+#else
+# define RUBY_ASSERT_MESG(expr, mesg) \
(RB_LIKELY(expr) ? RBIMPL_ASSERT_NOTHING : RUBY_ASSERT_FAIL(mesg))
+#endif
/**
* A variant of #RUBY_ASSERT that does not interface with #RUBY_DEBUG.
*
* @copydetails #RUBY_ASSERT
*/
-#define RUBY_ASSERT_ALWAYS(expr) RUBY_ASSERT_MESG((expr), #expr)
+#if defined(RBIMPL_VA_OPT_ARGS)
+# define RUBY_ASSERT_ALWAYS(expr, ...) \
+ RUBY_ASSERT_MESG(expr, #expr RBIMPL_VA_OPT_ARGS(__VA_ARGS__))
+#else
+# define RUBY_ASSERT_ALWAYS(expr) RUBY_ASSERT_MESG((expr), #expr)
+#endif
/**
* Asserts that the given expression is truthy if and only if #RUBY_DEBUG is truthy.
@@ -172,9 +208,18 @@ RBIMPL_SYMBOL_EXPORT_END()
* @param expr What supposedly evaluates to true.
*/
#if RUBY_DEBUG
-# define RUBY_ASSERT(expr) RUBY_ASSERT_MESG((expr), #expr)
+# if defined(RBIMPL_VA_OPT_ARGS)
+# define RUBY_ASSERT(expr, ...) \
+ RUBY_ASSERT_MESG((expr), #expr RBIMPL_VA_OPT_ARGS(__VA_ARGS__))
+# else
+# define RUBY_ASSERT(expr) RUBY_ASSERT_MESG((expr), #expr)
+# endif
#else
-# define RUBY_ASSERT(expr) RBIMPL_ASSERT_NOTHING
+# if defined(RBIMPL_VA_OPT_ARGS)
+# define RUBY_ASSERT(/* expr, */...) RBIMPL_ASSERT_NOTHING
+# else
+# define RUBY_ASSERT(expr) RBIMPL_ASSERT_NOTHING
+# endif
#endif
/**
@@ -187,9 +232,18 @@ RBIMPL_SYMBOL_EXPORT_END()
/* Currently `RUBY_DEBUG == ! defined(NDEBUG)` is always true. There is no
* difference any longer between this one and `RUBY_ASSERT`. */
#if defined(NDEBUG)
-# define RUBY_ASSERT_NDEBUG(expr) RBIMPL_ASSERT_NOTHING
+# if defined(RBIMPL_VA_OPT_ARGS)
+# define RUBY_ASSERT_NDEBUG(/* expr, */...) RBIMPL_ASSERT_NOTHING
+# else
+# define RUBY_ASSERT_NDEBUG(expr) RBIMPL_ASSERT_NOTHING
+# endif
#else
-# define RUBY_ASSERT_NDEBUG(expr) RUBY_ASSERT_MESG((expr), #expr)
+# if defined(RBIMPL_VA_OPT_ARGS)
+# define RUBY_ASSERT_NDEBUG(expr, ...) \
+ RUBY_ASSERT_MESG((expr), #expr RBIMPL_VA_OPT_ARGS(__VA_ARGS__))
+# else
+# define RUBY_ASSERT_NDEBUG(expr) RUBY_ASSERT_MESG((expr), #expr)
+# endif
#endif
/**
@@ -197,10 +251,20 @@ RBIMPL_SYMBOL_EXPORT_END()
* @param mesg The message to display on failure.
*/
#if RUBY_DEBUG
-# define RUBY_ASSERT_MESG_WHEN(cond, expr, mesg) RUBY_ASSERT_MESG((expr), (mesg))
+# if defined(RBIMPL_VA_OPT_ARGS)
+# define RUBY_ASSERT_MESG_WHEN(cond, /* expr, */...) \
+ RUBY_ASSERT_MESG(__VA_ARGS__)
+# else
+# define RUBY_ASSERT_MESG_WHEN(cond, expr, mesg) RUBY_ASSERT_MESG((expr), (mesg))
+# endif
#else
-# define RUBY_ASSERT_MESG_WHEN(cond, expr, mesg) \
+# if defined(RBIMPL_VA_OPT_ARGS)
+# define RUBY_ASSERT_MESG_WHEN(cond, expr, ...) \
+ ((cond) ? RUBY_ASSERT_MESG((expr), __VA_ARGS__) : RBIMPL_ASSERT_NOTHING)
+# else
+# define RUBY_ASSERT_MESG_WHEN(cond, expr, mesg) \
((cond) ? RUBY_ASSERT_MESG((expr), (mesg)) : RBIMPL_ASSERT_NOTHING)
+# endif
#endif
/**
@@ -210,7 +274,23 @@ RBIMPL_SYMBOL_EXPORT_END()
* @param cond Extra condition that shall hold for assertion to take effect.
* @param expr What supposedly evaluates to true.
*/
-#define RUBY_ASSERT_WHEN(cond, expr) RUBY_ASSERT_MESG_WHEN((cond), (expr), #expr)
+#if defined(RBIMPL_VA_OPT_ARGS)
+# define RUBY_ASSERT_WHEN(cond, expr, ...) \
+ RUBY_ASSERT_MESG_WHEN(cond, expr, #expr RBIMPL_VA_OPT_ARGS(__VA_ARGS__))
+#else
+# define RUBY_ASSERT_WHEN(cond, expr) RUBY_ASSERT_MESG_WHEN((cond), (expr), #expr)
+#endif
+
+/**
+ * A variant of #RUBY_ASSERT that asserts when either #RUBY_DEBUG or built-in
+ * type of `obj` is `type`.
+ *
+ * @param obj Object to check its built-in typue.
+ * @param type Built-in type constant, T_ARRAY, T_STRING, etc.
+ */
+#define RUBY_ASSERT_BUILTIN_TYPE(obj, type) \
+ RUBY_ASSERT(RB_TYPE_P(obj, type), \
+ "Actual type is %s", rb_builtin_type_name(BUILTIN_TYPE(obj)))
/**
* This is either #RUBY_ASSERT or #RBIMPL_ASSUME, depending on #RUBY_DEBUG.
diff --git a/include/ruby/atomic.h b/include/ruby/atomic.h
index 3eb80fbf7d..043a6a9945 100644
--- a/include/ruby/atomic.h
+++ b/include/ruby/atomic.h
@@ -139,6 +139,15 @@ typedef unsigned int rb_atomic_t;
rbimpl_atomic_cas(&(var), (oldval), (newval))
/**
+ * Atomic load. This loads `var` with an atomic intrinsic and returns
+ * its value.
+ *
+ * @param var A variable of ::rb_atomic_t
+ * @return What was stored in `var`j
+ */
+#define RUBY_ATOMIC_LOAD(var) rbimpl_atomic_load(&(var))
+
+/**
* Identical to #RUBY_ATOMIC_EXCHANGE, except for the return type.
*
* @param var A variable of ::rb_atomic_t.
@@ -280,6 +289,17 @@ typedef unsigned int rb_atomic_t;
RBIMPL_CAST(rbimpl_atomic_ptr_exchange((void **)&(var), (void *)val))
/**
+ * Identical to #RUBY_ATOMIC_LOAD, except it expects its arguments are `void*`.
+ * There are cases where ::rb_atomic_t is 32bit while `void*` is 64bit. This
+ * should be used for size related operations to support such platforms.
+ *
+ * @param var A variable of `void*`
+ * @return The value of `var` (without tearing)
+ */
+#define RUBY_ATOMIC_PTR_LOAD(var) \
+ RBIMPL_CAST(rbimpl_atomic_ptr_load((void **)&var))
+
+/**
* Identical to #RUBY_ATOMIC_CAS, except it expects its arguments are `void*`.
* There are cases where ::rb_atomic_t is 32bit while `void*` is 64bit. This
* should be used for size related operations to support such platforms.
@@ -404,7 +424,7 @@ rbimpl_atomic_size_add(volatile size_t *ptr, size_t val)
#elif defined(HAVE_GCC_SYNC_BUILTINS)
__sync_add_and_fetch(ptr, val);
-#elif defined(_WIN32) && defined(_M_AMD64)
+#elif defined(_WIN64)
/* Ditto for `InterlockeExchangedAdd`. */
InterlockedExchangeAdd64(ptr, val);
@@ -456,13 +476,15 @@ rbimpl_atomic_size_inc(volatile size_t *ptr)
#elif defined(HAVE_GCC_ATOMIC_BUILTINS) || defined(HAVE_GCC_SYNC_BUILTINS)
rbimpl_atomic_size_add(ptr, 1);
-#elif defined(_WIN32) && defined(_M_AMD64)
+#elif defined(_WIN64)
InterlockedIncrement64(ptr);
#elif defined(__sun) && defined(HAVE_ATOMIC_H) && (defined(_LP64) || defined(_I32LPx))
atomic_inc_ulong(ptr);
#else
+ RBIMPL_STATIC_ASSERT(size_of_size_t, sizeof *ptr == sizeof(rb_atomic_t));
+
rbimpl_atomic_size_add(ptr, 1);
#endif
@@ -538,7 +560,7 @@ rbimpl_atomic_size_sub(volatile size_t *ptr, size_t val)
#elif defined(HAVE_GCC_SYNC_BUILTINS)
__sync_sub_and_fetch(ptr, val);
-#elif defined(_WIN32) && defined(_M_AMD64)
+#elif defined(_WIN64)
const ssize_t neg = -1;
InterlockedExchangeAdd64(ptr, neg * val);
@@ -590,13 +612,15 @@ rbimpl_atomic_size_dec(volatile size_t *ptr)
#elif defined(HAVE_GCC_ATOMIC_BUILTINS) || defined(HAVE_GCC_SYNC_BUILTINS)
rbimpl_atomic_size_sub(ptr, 1);
-#elif defined(_WIN32) && defined(_M_AMD64)
+#elif defined(_WIN64)
InterlockedDecrement64(ptr);
#elif defined(__sun) && defined(HAVE_ATOMIC_H) && (defined(_LP64) || defined(_I32LPx))
atomic_dec_ulong(ptr);
#else
+ RBIMPL_STATIC_ASSERT(size_of_size_t, sizeof *ptr == sizeof(rb_atomic_t));
+
rbimpl_atomic_size_sub(ptr, 1);
#endif
@@ -688,7 +712,7 @@ rbimpl_atomic_size_exchange(volatile size_t *ptr, size_t val)
#elif defined(HAVE_GCC_SYNC_BUILTINS)
return __sync_lock_test_and_set(ptr, val);
-#elif defined(_WIN32) && defined(_M_AMD64)
+#elif defined(_WIN64)
return InterlockedExchange64(ptr, val);
#elif defined(__sun) && defined(HAVE_ATOMIC_H) && (defined(_LP64) || defined(_I32LPx))
@@ -749,6 +773,21 @@ rbimpl_atomic_value_exchange(volatile VALUE *ptr, VALUE val)
RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
+static inline rb_atomic_t
+rbimpl_atomic_load(volatile rb_atomic_t *ptr)
+{
+#if 0
+
+#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
+ return __atomic_load_n(ptr, __ATOMIC_SEQ_CST);
+#else
+ return rbimpl_atomic_fetch_add(ptr, 0);
+#endif
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
static inline void
rbimpl_atomic_set(volatile rb_atomic_t *ptr, rb_atomic_t val)
{
@@ -823,7 +862,7 @@ rbimpl_atomic_size_cas(volatile size_t *ptr, size_t oldval, size_t newval)
#elif defined(HAVE_GCC_SYNC_BUILTINS)
return __sync_val_compare_and_swap(ptr, oldval, newval);
-#elif defined(_WIN32) && defined(_M_AMD64)
+#elif defined(_WIN64)
return InterlockedCompareExchange64(ptr, newval, oldval);
#elif defined(__sun) && defined(HAVE_ATOMIC_H) && (defined(_LP64) || defined(_I32LPx))
@@ -875,6 +914,22 @@ rbimpl_atomic_ptr_cas(void **ptr, const void *oldval, const void *newval)
RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
+static inline void *
+rbimpl_atomic_ptr_load(void **ptr)
+{
+#if 0
+
+#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
+ return __atomic_load_n(ptr, __ATOMIC_SEQ_CST);
+#else
+ void *val = *ptr;
+ return rbimpl_atomic_ptr_cas(ptr, val, val);
+#endif
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
static inline VALUE
rbimpl_atomic_value_cas(volatile VALUE *ptr, VALUE oldval, VALUE newval)
{
diff --git a/include/ruby/debug.h b/include/ruby/debug.h
index f95acdb17e..f7c8e6ca8d 100644
--- a/include/ruby/debug.h
+++ b/include/ruby/debug.h
@@ -10,6 +10,7 @@
* modify this file, provided that the conditions mentioned in the
* file COPYING are met. Consult the file for details.
*/
+#include "ruby/internal/attr/deprecated.h"
#include "ruby/internal/attr/nonnull.h"
#include "ruby/internal/attr/returns_nonnull.h"
#include "ruby/internal/dllexport.h"
@@ -51,6 +52,25 @@ RBIMPL_ATTR_NONNULL((3))
int rb_profile_frames(int start, int limit, VALUE *buff, int *lines);
/**
+ * Queries mysterious "frame"s of the given range.
+ *
+ * A per-thread version of rb_profile_frames().
+ * Arguments and return values are the same with rb_profile_frames() with the
+ * exception of the first argument _thread_, which accepts the Thread to be
+ * profiled/queried.
+ *
+ * @param[in] thread The Ruby Thread to be profiled.
+ * @param[in] start Start position (0 means the topmost).
+ * @param[in] limit Number objects of `buff`.
+ * @param[out] buff Return buffer.
+ * @param[out] lines Return buffer.
+ * @return Number of objects filled into `buff`.
+ * @post `buff` is filled with backtrace pointers.
+ * @post `lines` is filled with `__LINE__` of each backtraces.
+ */
+int rb_profile_thread_frames(VALUE thread, int start, int limit, VALUE *buff, int *lines);
+
+/**
* Queries the path of the passed backtrace.
*
* @param[in] frame What rb_profile_frames() returned.
@@ -596,48 +616,157 @@ VALUE rb_tracearg_object(rb_trace_arg_t *trace_arg);
/*
* Postponed Job API
- * rb_postponed_job_register and rb_postponed_job_register_one are
- * async-signal-safe and used via SIGPROF by the "stackprof" RubyGem
+ *
+ * This API is designed to be called from contexts where it is not safe to run Ruby
+ * code (e.g. because they do not hold the GVL or because GC is in progress), and
+ * defer a callback to run in a context where it _is_ safe. The primary intended
+ * users of this API is for sampling profilers like the "stackprof" gem; these work
+ * by scheduling the periodic delivery of a SIGPROF signal, and inside the C-level
+ * signal handler, deferring a job to collect a Ruby backtrace when it is next safe
+ * to do so.
+ *
+ * Ruby maintains a small, fixed-size postponed job table. An extension using this
+ * API should first call `rb_postponed_job_preregister` to register a callback
+ * function in this table and obtain a handle of type `rb_postponed_job_handle_t`
+ * to it. Subsequently, the callback can be triggered by calling
+ * `rb_postponed_job_trigger` with that handle, or the `data` associated with the
+ * callback function can be changed by calling `rb_postponed_job_preregister` again.
+ *
+ * Because the postponed job table is quite small (it only has 32 entries on most
+ * common systems), extensions should generally only preregister one or two `func`
+ * values.
+ *
+ * Historically, this API provided two functions `rb_postponed_job_register` and
+ * `rb_postponed_job_register_one`, which claimed to be fully async-signal-safe and
+ * would call back the provided `func` and `data` at an appropriate time. However,
+ * these functions were subject to race conditions which could cause crashes when
+ * racing with Ruby's internal use of them. These two functions are still present,
+ * but are marked as deprecated and have slightly changed semantics:
+ *
+ * * rb_postponed_job_register now works like rb_postponed_job_register_one i.e.
+ * `func` will only be executed at most one time each time Ruby checks for
+ * interrupts, no matter how many times it is registered
+ * * They are also called with the last `data` to be registered, not the first
+ * (which is how rb_postponed_job_register_one previously worked)
*/
+
/**
* Type of postponed jobs.
*
- * @param[in,out] arg What was passed to rb_postponed_job_register().
+ * @param[in,out] arg What was passed to `rb_postponed_job_preregister`
*/
typedef void (*rb_postponed_job_func_t)(void *arg);
/**
- * Registers a postponed job.
+ * The type of a handle returned from `rb_postponed_job_preregister` and
+ * passed to `rb_postponed_job_trigger`
+ */
+typedef unsigned int rb_postponed_job_handle_t;
+#define POSTPONED_JOB_HANDLE_INVALID ((rb_postponed_job_handle_t)UINT_MAX)
+
+/**
+ * Pre-registers a func in Ruby's postponed job preregistration table,
+ * returning an opaque handle which can be used to trigger the job later. Generally,
+ * this function will be called during the initialization routine of an extension.
*
- * There are situations when running a ruby program is not possible. For
- * instance when a program is in a signal handler; for another instance when
- * the GC is busy. On such situations however, there might be needs to do
- * something. We cannot but defer such operations until we are 100% sure it is
- * safe to execute them. This mechanism is called postponed jobs. This
- * function registers a new one. The registered job would eventually gets
- * executed.
+ * The returned handle can be used later to call `rb_postponed_job_trigger`. This will
+ * cause Ruby to call back into the registered `func` with `data` at a later time, in
+ * a context where the GVL is held and it is safe to perform Ruby allocations.
*
- * @param[in] flags (Unused) reserved for future extensions.
+ * If the given `func` was already pre-registered, this function will overwrite the
+ * stored data with the newly passed data, and return the same handle instance as
+ * was previously returned.
+ *
+ * If this function is called concurrently with the same `func`, then the stored data
+ * could be the value from either call (but will definitely be one of them).
+ *
+ * If this function is called to update the data concurrently with a call to
+ * `rb_postponed_job_trigger` on the same handle, it's undefined whether `func` will
+ * be called with the old data or the new data.
+ *
+ * Although the current implementation of this function is in fact async-signal-safe and
+ * has defined semantics when called concurrently on the same `func`, a future Ruby
+ * version might require that this method be called under the GVL; thus, programs which
+ * aim to be forward-compatible should call this method whilst holding the GVL.
+ *
+ * @param[in] flags Unused and ignored
+ * @param[in] func The function to be pre-registered
+ * @param[in] data The data to be pre-registered
+ * @retval POSTPONED_JOB_HANDLE_INVALID The job table is full; this registration
+ * did not succeed and no further registration will do so for
+ * the lifetime of the program.
+ * @retval otherwise A handle which can be passed to `rb_postponed_job_trigger`
+ */
+rb_postponed_job_handle_t rb_postponed_job_preregister(unsigned int flags, rb_postponed_job_func_t func, void *data);
+
+/**
+ * Triggers a pre-registered job registered with rb_postponed_job_preregister,
+ * scheduling it for execution the next time the Ruby VM checks for interrupts.
+ * The context in which the job is called in holds the GVL and is safe to perform
+ * Ruby allocations within (i.e. it is not during GC).
+ *
+ * This method is async-signal-safe and can be called from any thread, at any
+ * time, including in signal handlers.
+ *
+ * If this method is called multiple times, Ruby will coalesce this into only
+ * one call to the job the next time it checks for interrupts.
+ *
+ * @params[in] h A handle returned from rb_postponed_job_preregister
+ */
+void rb_postponed_job_trigger(rb_postponed_job_handle_t h);
+
+/**
+ * Schedules the given `func` to be called with `data` when Ruby next checks for
+ * interrupts. If this function is called multiple times in between Ruby checking
+ * for interrupts, then `func` will be called only once with the `data` value from
+ * the first call to this function.
+ *
+ * Like `rb_postponed_job_trigger`, the context in which the job is called
+ * holds the GVL and can allocate Ruby objects.
+ *
+ * This method essentially has the same semantics as:
+ *
+ * ```
+ * rb_postponed_job_trigger(rb_postponed_job_preregister(func, data));
+ * ```
+ *
+ * @note Previous versions of Ruby promised that the (`func`, `data`) pairs would
+ * be executed as many times as they were registered with this function; in
+ * reality this was always subject to race conditions and this function no
+ * longer provides this guarantee. Instead, multiple calls to this function
+ * can be coalesced into a single execution of the passed `func`, with the
+ * most recent `data` registered at that time passed in.
+ *
+ * @deprecated This interface implies that arbitrarily many `func`'s can be enqueued
+ * over the lifetime of the program, whilst in reality the registration
+ * slots for postponed jobs are a finite resource. This is made clearer
+ * by the `rb_postponed_job_preregister` and `rb_postponed_job_trigger`
+ * functions, and a future version of Ruby might delete this function.
+ *
+ * @param[in] flags Unused and ignored.
* @param[in] func Job body.
* @param[in,out] data Passed as-is to `func`.
- * @retval 0 Postponed job buffer is full. Failed.
- * @retval otherwise Opaque return value.
- * @post The passed job is postponed.
+ * @retval 0 Postponed job registration table is full. Failed.
+ * @retval 1 Registration succeeded.
+ * @post The passed job will run on the next interrupt check.
*/
+ RBIMPL_ATTR_DEPRECATED(("use rb_postponed_job_preregister and rb_postponed_job_trigger"))
int rb_postponed_job_register(unsigned int flags, rb_postponed_job_func_t func, void *data);
/**
- * Identical to rb_postponed_job_register_one(), except it additionally checks
- * for duplicated registration. In case the passed job is already in the
- * postponed job buffer this function does nothing.
+ * Identical to `rb_postponed_job_register`
+ *
+ * @deprecated This is deprecated for the same reason as `rb_postponed_job_register`
*
- * @param[in] flags (Unused) reserved for future extensions.
+ * @param[in] flags Unused and ignored.
* @param[in] func Job body.
* @param[in,out] data Passed as-is to `func`.
- * @retval 0 Postponed job buffer is full. Failed.
- * @retval otherwise Opaque return value.
+ * @retval 0 Postponed job registration table is full. Failed.
+ * @retval 1 Registration succeeded.
+ * @post The passed job will run on the next interrupt check.
*/
+ RBIMPL_ATTR_DEPRECATED(("use rb_postponed_job_preregister and rb_postponed_job_trigger"))
int rb_postponed_job_register_one(unsigned int flags, rb_postponed_job_func_t func, void *data);
/** @} */
diff --git a/include/ruby/fiber/scheduler.h b/include/ruby/fiber/scheduler.h
index ad3d2d7483..8f3d383330 100644
--- a/include/ruby/fiber/scheduler.h
+++ b/include/ruby/fiber/scheduler.h
@@ -97,7 +97,7 @@ VALUE rb_fiber_scheduler_get(void);
* current thread will call scheduler's `#close` method on finalisation
* (allowing the scheduler to properly manage all non-finished fibers).
* `scheduler` can be an object of any class corresponding to
- * `Fiber::SchedulerInterface`. Its implementation is up to the user.
+ * `Fiber::Scheduler` interface. Its implementation is up to the user.
*
* @param[in] scheduler The scheduler to set.
* @exception rb_eArgError `scheduler` does not conform the interface.
diff --git a/include/ruby/internal/abi.h b/include/ruby/internal/abi.h
index 8e1bbf3951..e735a67564 100644
--- a/include/ruby/internal/abi.h
+++ b/include/ruby/internal/abi.h
@@ -33,7 +33,7 @@
#endif
#endif /* RUBY_ABI_VERSION */
-#ifdef RUBY_DLN_CHECK_ABI
+#if defined(RUBY_DLN_CHECK_ABI) && !defined(RUBY_EXPORT)
# ifdef __cplusplus
extern "C" {
diff --git a/include/ruby/internal/attr/noexcept.h b/include/ruby/internal/attr/noexcept.h
index ea3001df2a..7c3f92f1e7 100644
--- a/include/ruby/internal/attr/noexcept.h
+++ b/include/ruby/internal/attr/noexcept.h
@@ -54,7 +54,7 @@
* get smarter and smarter. Today they can infer if it actually throws
* or not without any annotations by humans (correct me if I'm wrong).
*
- * - When an inline function attributed `noexcepr` actually _does_ throw an
+ * - When an inline function attributed `noexcept` actually _does_ throw an
* exception: they have to call `std::terminate` then (C++ standard
* mandates so). This means exception handling routines are actually
* enforced, not omitted. This doesn't impact runtime performance (The
diff --git a/include/ruby/internal/core/rarray.h b/include/ruby/internal/core/rarray.h
index 9670f93d0c..90690fe794 100644
--- a/include/ruby/internal/core/rarray.h
+++ b/include/ruby/internal/core/rarray.h
@@ -36,18 +36,6 @@
#include "ruby/assert.h"
/**
- * @private
- * @warning Do not touch this macro.
- * @warning It is an implementation detail.
- * @warning The value of this macro must match for ruby itself and all
- * extension libraries, otherwise serious memory corruption shall
- * occur.
- */
-#ifndef USE_TRANSIENT_HEAP
-# define USE_TRANSIENT_HEAP 1
-#endif
-
-/**
* Convenient casting macro.
*
* @param obj An object, which is in fact an ::RArray.
@@ -59,15 +47,9 @@
#define RARRAY_EMBED_LEN_MASK RARRAY_EMBED_LEN_MASK
#define RARRAY_EMBED_LEN_MAX RARRAY_EMBED_LEN_MAX
#define RARRAY_EMBED_LEN_SHIFT RARRAY_EMBED_LEN_SHIFT
-#if USE_TRANSIENT_HEAP
-# define RARRAY_TRANSIENT_FLAG RARRAY_TRANSIENT_FLAG
-#else
-# define RARRAY_TRANSIENT_FLAG 0
-#endif
/** @endcond */
#define RARRAY_LEN rb_array_len /**< @alias{rb_array_len} */
#define RARRAY_CONST_PTR rb_array_const_ptr /**< @alias{rb_array_const_ptr} */
-#define RARRAY_CONST_PTR_TRANSIENT rb_array_const_ptr_transient /**< @alias{rb_array_const_ptr_transient} */
/** @cond INTERNAL_MACRO */
#if defined(__fcc__) || defined(__fcc_version) || \
@@ -80,7 +62,6 @@
#define RARRAY_EMBED_LEN RARRAY_EMBED_LEN
#define RARRAY_LENINT RARRAY_LENINT
-#define RARRAY_TRANSIENT_P RARRAY_TRANSIENT_P
#define RARRAY_ASET RARRAY_ASET
#define RARRAY_PTR RARRAY_PTR
/** @endcond */
@@ -132,24 +113,6 @@ enum ruby_rarray_flags {
*/
RARRAY_EMBED_LEN_MASK = RUBY_FL_USER9 | RUBY_FL_USER8 | RUBY_FL_USER7 | RUBY_FL_USER6 |
RUBY_FL_USER5 | RUBY_FL_USER4 | RUBY_FL_USER3
-
-#if USE_TRANSIENT_HEAP
- ,
-
- /**
- * This flag has something to do with an array's "transiency". A transient
- * array is an array of young generation (of generational GC), who stores
- * its elements inside of dedicated memory pages called a transient heap.
- * Not every young generation share that storage scheme, but elder
- * generations must no join.
- *
- * @internal
- *
- * 3rd parties must not be aware that there even is more than one way to
- * store array elements. It was a bad idea to expose this to them.
- */
- RARRAY_TRANSIENT_FLAG = RUBY_FL_USER13
-#endif
};
/**
@@ -249,16 +212,6 @@ VALUE *rb_ary_ptr_use_start(VALUE ary);
*/
void rb_ary_ptr_use_end(VALUE a);
-#if USE_TRANSIENT_HEAP
-/**
- * Destructively converts an array of transient backend into ordinal one.
- *
- * @param[out] a An object of ::RArray.
- * @pre `a` must be a transient array.
- * @post `a` gets out of transient heap, destructively.
- */
-void rb_ary_detransient(VALUE a);
-#endif
RBIMPL_SYMBOL_EXPORT_END()
RBIMPL_ATTR_PURE_UNLESS_DEBUG()
@@ -331,33 +284,6 @@ RARRAY_LENINT(VALUE ary)
}
RBIMPL_ATTR_PURE_UNLESS_DEBUG()
-RBIMPL_ATTR_ARTIFICIAL()
-/**
- * Queries if the array is a transient array.
- *
- * @param[in] ary Array in question.
- * @retval true Yes it is.
- * @retval false No it isn't.
- * @pre `ary` must be an instance of ::RArray.
- *
- * @internal
- *
- * @shyouhei doesn't understand the benefit of this function called from
- * extension libraries.
- */
-static inline bool
-RARRAY_TRANSIENT_P(VALUE ary)
-{
- RBIMPL_ASSERT_TYPE(ary, RUBY_T_ARRAY);
-
-#if USE_TRANSIENT_HEAP
- return RB_FL_ANY_RAW(ary, RARRAY_TRANSIENT_FLAG);
-#else
- return false;
-#endif
-}
-
-RBIMPL_ATTR_PURE_UNLESS_DEBUG()
/**
* @private
*
@@ -368,7 +294,7 @@ RBIMPL_ATTR_PURE_UNLESS_DEBUG()
* @return Its backend storage.
*/
static inline const VALUE *
-rb_array_const_ptr_transient(VALUE a)
+rb_array_const_ptr(VALUE a)
{
RBIMPL_ASSERT_TYPE(a, RUBY_T_ARRAY);
@@ -380,102 +306,21 @@ rb_array_const_ptr_transient(VALUE a)
}
}
-#if ! USE_TRANSIENT_HEAP
-RBIMPL_ATTR_PURE_UNLESS_DEBUG()
-#endif
-/**
- * @private
- *
- * This is an implementation detail of RARRAY_PTR(). People do not use it
- * directly.
- *
- * @param[in] a An object of ::RArray.
- * @return Its backend storage.
- * @post `a` is not a transient array.
- */
-static inline const VALUE *
-rb_array_const_ptr(VALUE a)
-{
- RBIMPL_ASSERT_TYPE(a, RUBY_T_ARRAY);
-
-#if USE_TRANSIENT_HEAP
- if (RARRAY_TRANSIENT_P(a)) {
- rb_ary_detransient(a);
- }
-#endif
- return rb_array_const_ptr_transient(a);
-}
-
-/**
- * @private
- *
- * This is an implementation detail of #RARRAY_PTR_USE. People do not use it
- * directly.
- *
- * @param[in] a An object of ::RArray.
- * @param[in] allow_transient Whether `a` can be transient or not.
- * @return Its backend storage.
- * @post `a` is not a transient array unless `allow_transient`.
- */
-static inline VALUE *
-rb_array_ptr_use_start(VALUE a,
- RBIMPL_ATTR_MAYBE_UNUSED()
- int allow_transient)
-{
- RBIMPL_ASSERT_TYPE(a, RUBY_T_ARRAY);
-
-#if USE_TRANSIENT_HEAP
- if (!allow_transient) {
- if (RARRAY_TRANSIENT_P(a)) {
- rb_ary_detransient(a);
- }
- }
-#endif
-
- return rb_ary_ptr_use_start(a);
-}
-
-/**
- * @private
- *
- * This is an implementation detail of #RARRAY_PTR_USE. People do not use it
- * directly.
- *
- * @param[in] a An object of ::RArray.
- * @param[in] allow_transient Whether `a` can be transient or not.
- */
-static inline void
-rb_array_ptr_use_end(VALUE a,
- RBIMPL_ATTR_MAYBE_UNUSED()
- int allow_transient)
-{
- RBIMPL_ASSERT_TYPE(a, RUBY_T_ARRAY);
- rb_ary_ptr_use_end(a);
-}
-
/**
* @private
*
* This is an implementation detail of #RARRAY_PTR_USE. People do not use it
* directly.
*/
-#define RBIMPL_RARRAY_STMT(flag, ary, var, expr) do { \
+#define RBIMPL_RARRAY_STMT(ary, var, expr) do { \
RBIMPL_ASSERT_TYPE((ary), RUBY_T_ARRAY); \
const VALUE rbimpl_ary = (ary); \
- VALUE *var = rb_array_ptr_use_start(rbimpl_ary, (flag)); \
+ VALUE *var = rb_ary_ptr_use_start(rbimpl_ary); \
expr; \
- rb_array_ptr_use_end(rbimpl_ary, (flag)); \
+ rb_ary_ptr_use_end(rbimpl_ary); \
} while (0)
/**
- * @private
- *
- * This is an implementation detail of #RARRAY_PTR_USE. People do not use it
- * directly.
- */
-#define RARRAY_PTR_USE_END(a) rb_array_ptr_use_end(a, 0)
-
-/**
* Declares a section of code where raw pointers are used. In case you need to
* touch the raw C array instead of polite CAPIs, then that operation shall be
* wrapped using this macro.
@@ -501,21 +346,11 @@ rb_array_ptr_use_end(VALUE a,
* them use it... Maybe some transition path can be implemented later.
*/
#define RARRAY_PTR_USE(ary, ptr_name, expr) \
- RBIMPL_RARRAY_STMT(0, ary, ptr_name, expr)
-
-/**
- * Identical to #RARRAY_PTR_USE, except the pointer can be a transient one.
- *
- * @param ary An object of ::RArray.
- * @param ptr_name A variable name which points the C array in `expr`.
- * @param expr The expression that touches `ptr_name`.
- */
-#define RARRAY_PTR_USE_TRANSIENT(ary, ptr_name, expr) \
- RBIMPL_RARRAY_STMT(1, ary, ptr_name, expr)
+ RBIMPL_RARRAY_STMT(ary, ptr_name, expr)
/**
* Wild use of a C pointer. This function accesses the backend storage
- * directly. This is slower than #RARRAY_PTR_USE_TRANSIENT. It exercises
+ * directly. This is slower than #RARRAY_PTR_USE. It exercises
* extra manoeuvres to protect our generational GC. Use of this function is
* considered archaic. Use a modern way instead.
*
@@ -550,7 +385,7 @@ RARRAY_PTR(VALUE ary)
static inline void
RARRAY_ASET(VALUE ary, long i, VALUE v)
{
- RARRAY_PTR_USE_TRANSIENT(ary, ptr,
+ RARRAY_PTR_USE(ary, ptr,
RB_OBJ_WRITE(ary, &ptr[i], v));
}
@@ -565,6 +400,6 @@ RARRAY_ASET(VALUE ary, long i, VALUE v)
* remains as it is due to that. If we could warn such usages we can set a
* transition path, but currently no way is found to do so.
*/
-#define RARRAY_AREF(a, i) RARRAY_CONST_PTR_TRANSIENT(a)[i]
+#define RARRAY_AREF(a, i) RARRAY_CONST_PTR(a)[i]
#endif /* RBIMPL_RARRAY_H */
diff --git a/include/ruby/internal/core/rbasic.h b/include/ruby/internal/core/rbasic.h
index 4617f743a7..a1477e2600 100644
--- a/include/ruby/internal/core/rbasic.h
+++ b/include/ruby/internal/core/rbasic.h
@@ -56,22 +56,20 @@ enum ruby_rvalue_flags {
};
/**
- * Ruby's object's, base components. Every single ruby objects have them in
- * common.
+ * Ruby object's base components. All Ruby objects have them in common.
*/
struct
RUBY_ALIGNAS(SIZEOF_VALUE)
RBasic {
/**
- * Per-object flags. Each ruby objects have their own characteristics
- * apart from their classes. For instance whether an object is frozen or
- * not is not controlled by its class. This is where such properties are
- * stored.
+ * Per-object flags. Each Ruby object has its own characteristics apart
+ * from its class. For instance, whether an object is frozen or not is not
+ * controlled by its class. This is where such properties are stored.
*
* @see enum ::ruby_fl_type
*
- * @note This is ::VALUE rather than an enum for alignment purpose. Back
+ * @note This is ::VALUE rather than an enum for alignment purposes. Back
* in the 1990s there were no such thing like `_Alignas` in C.
*/
VALUE flags;
@@ -79,10 +77,10 @@ RBasic {
/**
* Class of an object. Every object has its class. Also, everything is an
* object in Ruby. This means classes are also objects. Classes have
- * their own classes, classes of classes have their classes, too ... and
- * it recursively continues forever.
+ * their own classes, classes of classes have their classes too, and it
+ * recursively continues forever.
*
- * Also note the `const` qualifier. In ruby an object cannot "change" its
+ * Also note the `const` qualifier. In Ruby, an object cannot "change" its
* class.
*/
const VALUE klass;
diff --git a/include/ruby/internal/core/rmatch.h b/include/ruby/internal/core/rmatch.h
index 2d2fd897f5..a528c2999e 100644
--- a/include/ruby/internal/core/rmatch.h
+++ b/include/ruby/internal/core/rmatch.h
@@ -68,7 +68,7 @@ struct rmatch_offset {
};
/** Represents a match. */
-struct rmatch {
+struct rb_matchext_struct {
/**
* "Registers" of a match. This is a quasi-opaque struct that holds
* execution result of a match. Roughly resembles `&~`.
@@ -82,6 +82,8 @@ struct rmatch {
int char_offset_num_allocated;
};
+typedef struct rb_matchext_struct rb_matchext_t;
+
/**
* Regular expression execution context. When a regular expression "matches"
* to a string, it generates capture groups etc. This struct holds that info.
@@ -102,16 +104,13 @@ struct RMatch {
VALUE str;
/**
- * The result of this match.
- */
- struct rmatch *rmatch;
-
- /**
* The expression of this match.
*/
VALUE regexp; /* RRegexp */
};
+#define RMATCH_EXT(m) ((rb_matchext_t *)((char *)(m) + sizeof(struct RMatch)))
+
RBIMPL_ATTR_PURE_UNLESS_DEBUG()
RBIMPL_ATTR_ARTIFICIAL()
/**
@@ -139,8 +138,7 @@ static inline struct re_registers *
RMATCH_REGS(VALUE match)
{
RBIMPL_ASSERT_TYPE(match, RUBY_T_MATCH);
- RBIMPL_ASSERT_OR_ASSUME(RMATCH(match)->rmatch != NULL);
- return &RMATCH(match)->rmatch->regs;
+ return &RMATCH_EXT(match)->regs;
}
#endif /* RBIMPL_RMATCH_H */
diff --git a/include/ruby/internal/core/robject.h b/include/ruby/internal/core/robject.h
index dc1faad89f..c2bcae6306 100644
--- a/include/ruby/internal/core/robject.h
+++ b/include/ruby/internal/core/robject.h
@@ -119,13 +119,6 @@ struct RObject {
} as;
};
-/* Offsets for YJIT */
-#ifndef __cplusplus
-static const int32_t ROBJECT_OFFSET_AS_HEAP_IVPTR = offsetof(struct RObject, as.heap.ivptr);
-static const int32_t ROBJECT_OFFSET_AS_HEAP_IV_INDEX_TBL = offsetof(struct RObject, as.heap.iv_index_tbl);
-static const int32_t ROBJECT_OFFSET_AS_ARY = offsetof(struct RObject, as.ary);
-#endif
-
RBIMPL_ATTR_PURE_UNLESS_DEBUG()
RBIMPL_ATTR_ARTIFICIAL()
/**
diff --git a/include/ruby/internal/core/rstring.h b/include/ruby/internal/core/rstring.h
index 777fe6dab1..0bca74e688 100644
--- a/include/ruby/internal/core/rstring.h
+++ b/include/ruby/internal/core/rstring.h
@@ -43,7 +43,6 @@
/** @cond INTERNAL_MACRO */
#define RSTRING_NOEMBED RSTRING_NOEMBED
#define RSTRING_FSTR RSTRING_FSTR
-#define RSTRING_EMBED_LEN RSTRING_EMBED_LEN
#define RSTRING_LEN RSTRING_LEN
#define RSTRING_LENINT RSTRING_LENINT
#define RSTRING_PTR RSTRING_PTR
@@ -199,6 +198,13 @@ struct RString {
/** Basic part, including flags and class. */
struct RBasic basic;
+ /**
+ * Length of the string, not including terminating NUL character.
+ *
+ * @note This is in bytes.
+ */
+ long len;
+
/** String's specific fields. */
union {
@@ -207,14 +213,6 @@ struct RString {
* pattern.
*/
struct {
-
- /**
- * Length of the string, not including terminating NUL character.
- *
- * @note This is in bytes.
- */
- long len;
-
/**
* Pointer to the contents of the string. In the old days each
* string had dedicated memory regions. That is no longer true
@@ -245,7 +243,6 @@ struct RString {
/** Embedded contents. */
struct {
- long len;
/* This is a length 1 array because:
* 1. GCC has a bug that does not optimize C flexible array members
* (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102452)
@@ -364,24 +361,12 @@ RBIMPL_ATTR_ARTIFICIAL()
*
* @param[in] str String in question.
* @return Its length, in bytes.
- * @pre `str` must be an instance of ::RString, and must has its
- * ::RSTRING_NOEMBED flag off.
- *
- * @internal
- *
- * This was a macro before. It was inevitable to be public, since macros are
- * global constructs. But should it be forever? Now that it is a function,
- * @shyouhei thinks it could just be eliminated, hidden into implementation
- * details.
+ * @pre `str` must be an instance of ::RString.
*/
static inline long
-RSTRING_EMBED_LEN(VALUE str)
+RSTRING_LEN(VALUE str)
{
- RBIMPL_ASSERT_TYPE(str, RUBY_T_STRING);
- RBIMPL_ASSERT_OR_ASSUME(! RB_FL_ANY_RAW(str, RSTRING_NOEMBED));
-
- long f = RSTRING(str)->as.embed.len;
- return f;
+ return RSTRING(str)->len;
}
RBIMPL_WARNING_PUSH()
@@ -411,7 +396,7 @@ rbimpl_rstring_getmem(VALUE str)
else {
/* Expecting compilers to optimize this on-stack struct away. */
struct RString retval;
- retval.as.heap.len = RSTRING_EMBED_LEN(str);
+ retval.len = RSTRING_LEN(str);
retval.as.heap.ptr = RSTRING(str)->as.embed.ary;
return retval;
}
@@ -419,21 +404,6 @@ rbimpl_rstring_getmem(VALUE str)
RBIMPL_WARNING_POP()
-RBIMPL_ATTR_PURE_UNLESS_DEBUG()
-RBIMPL_ATTR_ARTIFICIAL()
-/**
- * Queries the length of the string.
- *
- * @param[in] str String in question.
- * @return Its length, in bytes.
- * @pre `str` must be an instance of ::RString.
- */
-static inline long
-RSTRING_LEN(VALUE str)
-{
- return rbimpl_rstring_getmem(str).as.heap.len;
-}
-
RBIMPL_ATTR_ARTIFICIAL()
/**
* Queries the contents pointer of the string.
@@ -447,13 +417,9 @@ RSTRING_PTR(VALUE str)
{
char *ptr = rbimpl_rstring_getmem(str).as.heap.ptr;
- if (RB_UNLIKELY(! ptr)) {
+ if (RUBY_DEBUG && RB_UNLIKELY(! ptr)) {
/* :BEWARE: @shyouhei thinks that currently, there are rooms for this
- * function to return NULL. In the 20th century that was a pointless
- * concern. However struct RString can hold fake strings nowadays. It
- * seems no check against NULL are exercised around handling of them
- * (one of such usages is located in marshal.c, which scares
- * @shyouhei). Better check here for maximum safety.
+ * function to return NULL. Better check here for maximum safety.
*
* Also, this is not rb_warn() because RSTRING_PTR() can be called
* during GC (see what obj_info() does). rb_warn() needs to allocate
@@ -477,12 +443,12 @@ RSTRING_END(VALUE str)
{
struct RString buf = rbimpl_rstring_getmem(str);
- if (RB_UNLIKELY(! buf.as.heap.ptr)) {
+ if (RUBY_DEBUG && RB_UNLIKELY(! buf.as.heap.ptr)) {
/* Ditto. */
rb_debug_rstring_null_ptr("RSTRING_END");
}
- return &buf.as.heap.ptr[buf.as.heap.len];
+ return &buf.as.heap.ptr[buf.len];
}
RBIMPL_ATTR_ARTIFICIAL()
@@ -516,7 +482,7 @@ RSTRING_LENINT(VALUE str)
__extension__ ({ \
struct RString rbimpl_str = rbimpl_rstring_getmem(str); \
(ptrvar) = rbimpl_str.as.heap.ptr; \
- (lenvar) = rbimpl_str.as.heap.len; \
+ (lenvar) = rbimpl_str.len; \
})
#else
# define RSTRING_GETMEM(str, ptrvar, lenvar) \
diff --git a/include/ruby/internal/core/rtypeddata.h b/include/ruby/internal/core/rtypeddata.h
index 1abf965305..6c19576c20 100644
--- a/include/ruby/internal/core/rtypeddata.h
+++ b/include/ruby/internal/core/rtypeddata.h
@@ -114,6 +114,8 @@
#define RUBY_TYPED_PROMOTED1 RUBY_TYPED_PROMOTED1
/** @endcond */
+#define TYPED_DATA_EMBEDDED 2
+
/**
* @private
*
@@ -137,6 +139,8 @@ rbimpl_typeddata_flags {
*/
RUBY_TYPED_FREE_IMMEDIATELY = 1,
+ RUBY_TYPED_EMBEDDABLE = 2,
+
/**
* This flag has something to do with Ractor. Multiple Ractors run without
* protecting each other. Sharing an object among Ractors is basically
@@ -173,10 +177,9 @@ rbimpl_typeddata_flags {
RUBY_TYPED_WB_PROTECTED = RUBY_FL_WB_PROTECTED, /* THIS FLAG DEPENDS ON Ruby version */
/**
- * This flag is mysterious. It seems nobody is currently using it. The
- * intention of this flag is also unclear. We need further investigations.
+ * This flag no longer in use
*/
- RUBY_TYPED_PROMOTED1 = RUBY_FL_PROMOTED1, /* THIS FLAG DEPENDS ON Ruby version */
+ RUBY_TYPED_UNUSED = RUBY_FL_UNUSED6,
/**
* This flag determines whether marking and compaction should be carried out
@@ -461,7 +464,7 @@ RBIMPL_SYMBOL_EXPORT_END()
*/
#define TypedData_Make_Struct0(result, klass, type, size, data_type, sval) \
VALUE result = rb_data_typed_object_zalloc(klass, size, data_type); \
- (sval) = RBIMPL_CAST((type *)RTYPEDDATA_DATA(result)); \
+ (sval) = (type *)RTYPEDDATA_GET_DATA(result); \
RBIMPL_CAST(/*suppress unused variable warnings*/(void)(sval))
/**
@@ -512,6 +515,36 @@ RBIMPL_SYMBOL_EXPORT_END()
#define TypedData_Get_Struct(obj,type,data_type,sval) \
((sval) = RBIMPL_CAST((type *)rb_check_typeddata((obj), (data_type))))
+static inline bool
+RTYPEDDATA_EMBEDDED_P(VALUE obj)
+{
+#if RUBY_DEBUG
+ if (RB_UNLIKELY(!RB_TYPE_P(obj, RUBY_T_DATA))) {
+ Check_Type(obj, RUBY_T_DATA);
+ RBIMPL_UNREACHABLE_RETURN(false);
+ }
+#endif
+
+ return RTYPEDDATA(obj)->typed_flag & TYPED_DATA_EMBEDDED;
+}
+
+static inline void *
+RTYPEDDATA_GET_DATA(VALUE obj)
+{
+#if RUBY_DEBUG
+ if (RB_UNLIKELY(!RB_TYPE_P(obj, RUBY_T_DATA))) {
+ Check_Type(obj, RUBY_T_DATA);
+ RBIMPL_UNREACHABLE_RETURN(false);
+ }
+#endif
+
+ /* We reuse the data pointer in embedded TypedData. We can't use offsetof
+ * since RTypedData a non-POD type in C++. */
+ const size_t embedded_typed_data_size = sizeof(struct RTypedData) - sizeof(void *);
+
+ return RTYPEDDATA_EMBEDDED_P(obj) ? (char *)obj + embedded_typed_data_size : RTYPEDDATA(obj)->data;
+}
+
RBIMPL_ATTR_PURE()
RBIMPL_ATTR_ARTIFICIAL()
/**
@@ -528,7 +561,8 @@ RBIMPL_ATTR_ARTIFICIAL()
static inline bool
rbimpl_rtypeddata_p(VALUE obj)
{
- return RTYPEDDATA(obj)->typed_flag == 1;
+ VALUE typed_flag = RTYPEDDATA(obj)->typed_flag;
+ return typed_flag != 0 && typed_flag <= 3;
}
RBIMPL_ATTR_PURE_UNLESS_DEBUG()
diff --git a/include/ruby/internal/encoding/encoding.h b/include/ruby/internal/encoding/encoding.h
index dc3e0151f0..a680651a81 100644
--- a/include/ruby/internal/encoding/encoding.h
+++ b/include/ruby/internal/encoding/encoding.h
@@ -28,6 +28,7 @@
#include "ruby/internal/attr/pure.h"
#include "ruby/internal/attr/returns_nonnull.h"
#include "ruby/internal/dllexport.h"
+#include "ruby/internal/encoding/coderange.h"
#include "ruby/internal/value.h"
#include "ruby/internal/core/rbasic.h"
#include "ruby/internal/fl_type.h"
diff --git a/include/ruby/internal/encoding/string.h b/include/ruby/internal/encoding/string.h
index 6ed7ca1c90..2b9dfe4f31 100644
--- a/include/ruby/internal/encoding/string.h
+++ b/include/ruby/internal/encoding/string.h
@@ -30,7 +30,7 @@
RBIMPL_SYMBOL_EXPORT_BEGIN()
/**
- * Identical to rb_enc_str_new(), except it additionally takes an encoding.
+ * Identical to rb_str_new(), except it additionally takes an encoding.
*
* @param[in] ptr A memory region of `len` bytes length.
* @param[in] len Length of `ptr`, in bytes, not including the
diff --git a/include/ruby/internal/error.h b/include/ruby/internal/error.h
index 1ce9a30e6f..cd37f4461a 100644
--- a/include/ruby/internal/error.h
+++ b/include/ruby/internal/error.h
@@ -481,7 +481,7 @@ VALUE *rb_ruby_debug_ptr(void);
*/
#define ruby_debug (*rb_ruby_debug_ptr())
-/* reports if `-W' specified */
+/* reports if $VERBOSE is true */
RBIMPL_ATTR_NONNULL((1))
RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 2)
/**
@@ -496,7 +496,8 @@ RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 2)
* default, the method just emits its passed contents to ::rb_stderr using
* rb_io_write().
*
- * @note This function is affected by the `-W` flag.
+ * @note This function is affected by the value of $VERBOSE, it does
+ * nothing unless $VERBOSE is true.
* @param[in] fmt Format specifier string compatible with rb_sprintf().
*
* @internal
@@ -521,7 +522,7 @@ RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 3, 4)
* Issues a compile-time warning that happens at `__file__:__line__`. Purpose
* of this function being exposed to CAPI is unclear.
*
- * @note This function is affected by the `-W` flag.
+ * @note This function is affected by the value of $VERBOSE.
* @param[in] file The path corresponding to Ruby level `__FILE__`.
* @param[in] line The number corresponding to Ruby level `__LINE__`.
* @param[in] fmt Format specifier string compatible with rb_sprintf().
@@ -534,19 +535,20 @@ RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 2)
* Identical to rb_sys_fail(), except it does not raise an exception to render
* a warning instead.
*
- * @note This function is affected by the `-W` flag.
+ * @note This function is affected by the value of $VERBOSE.
* @param[in] fmt Format specifier string compatible with rb_sprintf().
*/
void rb_sys_warning(const char *fmt, ...);
-/* reports always */
+/* reports if $VERBOSE is not nil (so if it is true or false) */
RBIMPL_ATTR_COLD()
RBIMPL_ATTR_NONNULL((1))
RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 2)
/**
- * Identical to rb_warning(), except it reports always regardless of runtime
- * `-W` flag.
+ * Identical to rb_warning(), except it reports unless $VERBOSE is nil.
*
+ * @note This function is affected by the value of $VERBOSE, it does
+ * nothing if $VERBOSE is nil.
* @param[in] fmt Format specifier string compatible with rb_sprintf().
*/
void rb_warn(const char *fmt, ...);
@@ -555,8 +557,7 @@ RBIMPL_ATTR_COLD()
RBIMPL_ATTR_NONNULL((2))
RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3)
/**
- * Identical to rb_category_warning(), except it reports always regardless of
- * runtime `-W` flag.
+ * Identical to rb_category_warning(), except it reports unless $VERBOSE is nil.
*
* @param[in] cat Category e.g. deprecated.
* @param[in] fmt Format specifier string compatible with rb_sprintf().
@@ -566,8 +567,7 @@ void rb_category_warn(rb_warning_category_t cat, const char *fmt, ...);
RBIMPL_ATTR_NONNULL((1, 3))
RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 3, 4)
/**
- * Identical to rb_compile_warning(), except it reports always regardless of
- * runtime `-W` flag.
+ * Identical to rb_compile_warning(), except it reports unless $VERBOSE is nil.
*
* @param[in] file The path corresponding to Ruby level `__FILE__`.
* @param[in] line The number corresponding to Ruby level `__LINE__`.
diff --git a/include/ruby/internal/event.h b/include/ruby/internal/event.h
index 04b137a193..1d194ed618 100644
--- a/include/ruby/internal/event.h
+++ b/include/ruby/internal/event.h
@@ -23,6 +23,10 @@
#include "ruby/internal/dllexport.h"
#include "ruby/internal/value.h"
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
/* These macros are not enums because they are wider than int.*/
/**
@@ -54,6 +58,7 @@
#define RUBY_EVENT_THREAD_END 0x0800 /**< Encountered an end of a thread. */
#define RUBY_EVENT_FIBER_SWITCH 0x1000 /**< Encountered a `Fiber#yield`. */
#define RUBY_EVENT_SCRIPT_COMPILED 0x2000 /**< Encountered an `eval`. */
+#define RUBY_EVENT_RESCUE 0x4000 /**< Encountered a `rescue` statement. */
#define RUBY_EVENT_TRACEPOINT_ALL 0xffff /**< Bitmask of extended events. */
/** @} */
diff --git a/include/ruby/internal/fl_type.h b/include/ruby/internal/fl_type.h
index 555f23a5a5..0a05166784 100644
--- a/include/ruby/internal/fl_type.h
+++ b/include/ruby/internal/fl_type.h
@@ -57,8 +57,7 @@
#define FL_SINGLETON RBIMPL_CAST((VALUE)RUBY_FL_SINGLETON) /**< @old{RUBY_FL_SINGLETON} */
#define FL_WB_PROTECTED RBIMPL_CAST((VALUE)RUBY_FL_WB_PROTECTED) /**< @old{RUBY_FL_WB_PROTECTED} */
-#define FL_PROMOTED0 RBIMPL_CAST((VALUE)RUBY_FL_PROMOTED0) /**< @old{RUBY_FL_PROMOTED0} */
-#define FL_PROMOTED1 RBIMPL_CAST((VALUE)RUBY_FL_PROMOTED1) /**< @old{RUBY_FL_PROMOTED1} */
+#define FL_PROMOTED RBIMPL_CAST((VALUE)RUBY_FL_PROMOTED) /**< @old{RUBY_FL_PROMOTED} */
#define FL_FINALIZE RBIMPL_CAST((VALUE)RUBY_FL_FINALIZE) /**< @old{RUBY_FL_FINALIZE} */
#define FL_TAINT RBIMPL_CAST((VALUE)RUBY_FL_TAINT) /**< @old{RUBY_FL_TAINT} */
#define FL_SHAREABLE RBIMPL_CAST((VALUE)RUBY_FL_SHAREABLE) /**< @old{RUBY_FL_SHAREABLE} */
@@ -111,13 +110,6 @@
#define RB_OBJ_FREEZE_RAW RB_OBJ_FREEZE_RAW
#define RB_OBJ_FROZEN RB_OBJ_FROZEN
#define RB_OBJ_FROZEN_RAW RB_OBJ_FROZEN_RAW
-#define RB_OBJ_INFECT RB_OBJ_INFECT
-#define RB_OBJ_INFECT_RAW RB_OBJ_INFECT_RAW
-#define RB_OBJ_TAINT RB_OBJ_TAINT
-#define RB_OBJ_TAINTABLE RB_OBJ_TAINTABLE
-#define RB_OBJ_TAINTED RB_OBJ_TAINTED
-#define RB_OBJ_TAINTED_RAW RB_OBJ_TAINTED_RAW
-#define RB_OBJ_TAINT_RAW RB_OBJ_TAINT_RAW
#define RB_OBJ_UNTRUST RB_OBJ_TAINT
#define RB_OBJ_UNTRUSTED RB_OBJ_TAINTED
/** @endcond */
@@ -207,12 +199,15 @@ ruby_fl_type {
RUBY_FL_WB_PROTECTED = (1<<5),
/**
- * This flag has something to do with our garbage collector. These days
- * ruby objects are "generational". There are those who are young and
- * those who are old. Young objects are prone to die; monitored relatively
- * extensively by the garbage collector. OTOH old objects tend to live
- * longer. They are relatively rarely considered. This flag is set when a
- * object experienced promotion i.e. survived a garbage collection.
+ * Ruby objects are "generational". There are young objects & old objects.
+ * Young objects are prone to die & monitored relatively extensively by the
+ * garbage collector. Old objects tend to live longer & are monitored less
+ * frequently. When an object survives a GC, its age is incremented. When
+ * age is equal to RVALUE_OLD_AGE, the object becomes Old. This flag is set
+ * when an object becomes old, and is used by the write barrier to check if
+ * an old object should be considered for marking more frequently - as old
+ * objects that have references added between major GCs need to be remarked
+ * to prevent the referred object being mistakenly swept.
*
* @internal
*
@@ -220,41 +215,14 @@ ruby_fl_type {
* 3rd parties. It must be an implementation detail that they should never
* know. Might better be hidden.
*/
- RUBY_FL_PROMOTED0 = (1<<5),
+ RUBY_FL_PROMOTED = (1<<5),
/**
- * This flag has something to do with our garbage collector. These days
- * ruby objects are "generational". There are those who are young and
- * those who are old. Young objects are prone to die; monitored relatively
- * extensively by the garbage collector. OTOH old objects tend to live
- * longer. They are relatively rarely considered. This flag is set when a
- * object experienced two promotions i.e. survived garbage collections
- * twice.
+ * This flag is no longer in use
*
* @internal
- *
- * But honestly, @shyouhei doesn't think this flag should be visible from
- * 3rd parties. It must be an implementation detail that they should never
- * know. Might better be hidden.
*/
- RUBY_FL_PROMOTED1 = (1<<6),
-
- /**
- * This flag has something to do with our garbage collector. These days
- * ruby objects are "generational". There are those who are young and
- * those who are old. Young objects are prone to die; monitored relatively
- * extensively by the garbage collector. OTOH old objects tend to live
- * longer. They are relatively rarely considered. This flag is set when a
- * object experienced promotions i.e. survived more than one garbage
- * collections.
- *
- * @internal
- *
- * But honestly, @shyouhei doesn't think this flag should be visible from
- * 3rd parties. It must be an implementation detail that they should never
- * know. Might better be hidden.
- */
- RUBY_FL_PROMOTED = RUBY_FL_PROMOTED0 | RUBY_FL_PROMOTED1,
+ RUBY_FL_UNUSED6 = (1<<6),
/**
* This flag has something to do with finalisers. A ruby object can have
@@ -283,7 +251,7 @@ ruby_fl_type {
# pragma deprecated(RUBY_FL_TAINT)
#endif
- = (1<<8),
+ = 0,
/**
* This flag has something to do with Ractor. Multiple Ractors run without
@@ -310,7 +278,7 @@ ruby_fl_type {
# pragma deprecated(RUBY_FL_UNTRUSTED)
#endif
- = (1<<8),
+ = 0,
/**
* This flag has something to do with object IDs. Unlike in the old days,
@@ -427,7 +395,7 @@ ruby_fl_type {
* 3rd parties. It must be an implementation detail that they should never
* know. Might better be hidden.
*/
- RUBY_FL_SINGLETON = RUBY_FL_USER0,
+ RUBY_FL_SINGLETON = RUBY_FL_USER1,
};
enum {
@@ -489,7 +457,7 @@ RB_FL_ABLE(VALUE obj)
RBIMPL_ATTR_PURE_UNLESS_DEBUG()
RBIMPL_ATTR_ARTIFICIAL()
/**
- * This is an implenentation detail of RB_FL_TEST(). 3rd parties need not use
+ * This is an implementation detail of RB_FL_TEST(). 3rd parties need not use
* this. Just always use RB_FL_TEST().
*
* @param[in] obj Object in question.
@@ -537,7 +505,7 @@ RB_FL_TEST(VALUE obj, VALUE flags)
RBIMPL_ATTR_PURE_UNLESS_DEBUG()
RBIMPL_ATTR_ARTIFICIAL()
/**
- * This is an implenentation detail of RB_FL_ANY(). 3rd parties need not use
+ * This is an implementation detail of RB_FL_ANY(). 3rd parties need not use
* this. Just always use RB_FL_ANY().
*
* @param[in] obj Object in question.
@@ -571,7 +539,7 @@ RB_FL_ANY(VALUE obj, VALUE flags)
RBIMPL_ATTR_PURE_UNLESS_DEBUG()
RBIMPL_ATTR_ARTIFICIAL()
/**
- * This is an implenentation detail of RB_FL_ALL(). 3rd parties need not use
+ * This is an implementation detail of RB_FL_ALL(). 3rd parties need not use
* this. Just always use RB_FL_ALL().
*
* @param[in] obj Object in question.
@@ -607,7 +575,7 @@ RBIMPL_ATTR_ARTIFICIAL()
/**
* @private
*
- * This is an implenentation detail of RB_FL_SET(). 3rd parties need not use
+ * This is an implementation detail of RB_FL_SET(). 3rd parties need not use
* this. Just always use RB_FL_SET().
*
* @param[out] obj Object in question.
@@ -627,7 +595,7 @@ rbimpl_fl_set_raw_raw(struct RBasic *obj, VALUE flags)
RBIMPL_ATTR_ARTIFICIAL()
/**
- * This is an implenentation detail of RB_FL_SET(). 3rd parties need not use
+ * This is an implementation detail of RB_FL_SET(). 3rd parties need not use
* this. Just always use RB_FL_SET().
*
* @param[out] obj Object in question.
@@ -667,7 +635,7 @@ RBIMPL_ATTR_ARTIFICIAL()
/**
* @private
*
- * This is an implenentation detail of RB_FL_UNSET(). 3rd parties need not use
+ * This is an implementation detail of RB_FL_UNSET(). 3rd parties need not use
* this. Just always use RB_FL_UNSET().
*
* @param[out] obj Object in question.
@@ -687,7 +655,7 @@ rbimpl_fl_unset_raw_raw(struct RBasic *obj, VALUE flags)
RBIMPL_ATTR_ARTIFICIAL()
/**
- * This is an implenentation detail of RB_FL_UNSET(). 3rd parties need not use
+ * This is an implementation detail of RB_FL_UNSET(). 3rd parties need not use
* this. Just always use RB_FL_UNSET().
*
* @param[out] obj Object in question.
@@ -722,7 +690,7 @@ RBIMPL_ATTR_ARTIFICIAL()
/**
* @private
*
- * This is an implenentation detail of RB_FL_REVERSE(). 3rd parties need not
+ * This is an implementation detail of RB_FL_REVERSE(). 3rd parties need not
* use this. Just always use RB_FL_REVERSE().
*
* @param[out] obj Object in question.
@@ -742,7 +710,7 @@ rbimpl_fl_reverse_raw_raw(struct RBasic *obj, VALUE flags)
RBIMPL_ATTR_ARTIFICIAL()
/**
- * This is an implenentation detail of RB_FL_REVERSE(). 3rd parties need not
+ * This is an implementation detail of RB_FL_REVERSE(). 3rd parties need not
* use this. Just always use RB_FL_REVERSE().
*
* @param[out] obj Object in question.
@@ -898,7 +866,7 @@ RB_OBJ_INFECT(VALUE dst, VALUE src)
RBIMPL_ATTR_PURE_UNLESS_DEBUG()
RBIMPL_ATTR_ARTIFICIAL()
/**
- * This is an implenentation detail of RB_OBJ_FROZEN(). 3rd parties need not
+ * This is an implementation detail of RB_OBJ_FROZEN(). 3rd parties need not
* use this. Just always use RB_OBJ_FROZEN().
*
* @param[in] obj Object in question.
@@ -937,9 +905,13 @@ RB_OBJ_FROZEN(VALUE obj)
}
}
+RUBY_SYMBOL_EXPORT_BEGIN
+void rb_obj_freeze_inline(VALUE obj);
+RUBY_SYMBOL_EXPORT_END
+
RBIMPL_ATTR_ARTIFICIAL()
/**
- * This is an implenentation detail of RB_OBJ_FREEZE(). 3rd parties need not
+ * This is an implementation detail of RB_OBJ_FREEZE(). 3rd parties need not
* use this. Just always use RB_OBJ_FREEZE().
*
* @param[out] obj Object in question.
@@ -947,11 +919,7 @@ RBIMPL_ATTR_ARTIFICIAL()
static inline void
RB_OBJ_FREEZE_RAW(VALUE obj)
{
- RB_FL_SET_RAW(obj, RUBY_FL_FREEZE);
+ rb_obj_freeze_inline(obj);
}
-RUBY_SYMBOL_EXPORT_BEGIN
-void rb_obj_freeze_inline(VALUE obj);
-RUBY_SYMBOL_EXPORT_END
-
#endif /* RBIMPL_FL_TYPE_H */
diff --git a/include/ruby/internal/gc.h b/include/ruby/internal/gc.h
index 205830954e..462f416af2 100644
--- a/include/ruby/internal/gc.h
+++ b/include/ruby/internal/gc.h
@@ -44,10 +44,11 @@
RBIMPL_SYMBOL_EXPORT_BEGIN()
-#define REF_EDGE(s, p) (offsetof(struct s, p))
-#define REFS_LIST_PTR(l) ((RUBY_DATA_FUNC)l)
+#define RUBY_REF_EDGE(s, p) offsetof(s, p)
+#define RUBY_REFS_LIST_PTR(l) (RUBY_DATA_FUNC)(l)
#define RUBY_REF_END SIZE_MAX
-#define RUBY_REFERENCES_START(t) static size_t t[] = {
+#define RUBY_REFERENCES(t) static const size_t t[]
+#define RUBY_REFERENCES_START(t) RUBY_REFERENCES(t) = {
#define RUBY_REFERENCES_END RUBY_REF_END, };
/* gc.c */
@@ -209,22 +210,6 @@ void rb_gc_mark_movable(VALUE obj);
VALUE rb_gc_location(VALUE obj);
/**
- * Asserts that the passed object is no longer needed. Such objects are
- * reclaimed sooner or later so this function is not mandatory. But sometimes
- * you can know from your application knowledge that an object is surely dead
- * at some point. Calling this as a hint can be a polite way.
- *
- * @param[out] obj Object, dead.
- * @pre `obj` have never been passed to this function before.
- * @post `obj` could be invalidated.
- * @warning It is a failure to pass an object multiple times to this
- * function.
- * @deprecated This is now a no-op function.
- */
-RBIMPL_ATTR_DEPRECATED(("this is now a no-op function"))
-void rb_gc_force_recycle(VALUE obj);
-
-/**
* Triggers a GC process. This was the only GC entry point that we had at the
* beginning. Over time our GC evolved. Now what this function does is just a
* very simplified variation of the entire GC algorithms. A series of
@@ -443,18 +428,6 @@ RBIMPL_SYMBOL_EXPORT_END()
#define USE_RGENGC 1
/**
- * @private
- *
- * This is a compile-time flag to enable/disable incremental GC feature. It
- * has to be set at the time ruby itself compiles. Makes no sense for 3rd
- * parties. It is safe for them to set this though; that just doesn't change
- * anything.
- */
-#ifndef USE_RINCGC
-# define USE_RINCGC 1
-#endif
-
-/**
* @deprecated This macro seems broken. Setting this to anything other than
* zero just doesn't compile. We need to KonMari.
*/
@@ -850,4 +823,7 @@ rb_obj_write(
return a;
}
+RBIMPL_ATTR_DEPRECATED(("Will be removed soon"))
+static inline void rb_gc_force_recycle(VALUE obj){}
+
#endif /* RBIMPL_GC_H */
diff --git a/include/ruby/internal/globals.h b/include/ruby/internal/globals.h
index 5a414fc472..60d8e5309a 100644
--- a/include/ruby/internal/globals.h
+++ b/include/ruby/internal/globals.h
@@ -94,7 +94,7 @@ RUBY_EXTERN VALUE rb_cRegexp; /**< `Regexp` class. */
RUBY_EXTERN VALUE rb_cStat; /**< `File::Stat` class. */
RUBY_EXTERN VALUE rb_cString; /**< `String` class. */
RUBY_EXTERN VALUE rb_cStruct; /**< `Struct` class. */
-RUBY_EXTERN VALUE rb_cSymbol; /**< `Sumbol` class. */
+RUBY_EXTERN VALUE rb_cSymbol; /**< `Symbol` class. */
RUBY_EXTERN VALUE rb_cThread; /**< `Thread` class. */
RUBY_EXTERN VALUE rb_cTime; /**< `Time` class. */
RUBY_EXTERN VALUE rb_cTrueClass; /**< `TrueClass` class. */
diff --git a/include/ruby/internal/has/c_attribute.h b/include/ruby/internal/has/c_attribute.h
index c5c48867bf..69b0f402cd 100644
--- a/include/ruby/internal/has/c_attribute.h
+++ b/include/ruby/internal/has/c_attribute.h
@@ -21,11 +21,23 @@
* @brief Defines #RBIMPL_HAS_C_ATTRIBUTE.
*/
+#include "ruby/internal/has/extension.h"
+#include "ruby/internal/has/warning.h"
+
/** Wraps (or simulates) `__has_c_attribute`. */
#if defined(__cplusplus)
# /* Makes no sense. */
# define RBIMPL_HAS_C_ATTRIBUTE(_) 0
+#elif RBIMPL_HAS_EXTENSION(c_attributes)
+# /* Hmm. It seems Clang 17 has this macro defined even when -std=c99 mode,
+# * _and_ fails to compile complaining that attributes are C2X feature. We
+# * need to work around this nonsense. */
+# define RBIMPL_HAS_C_ATTRIBUTE(_) __has_c_attribute(_)
+
+#elif RBIMPL_HAS_WARNING("-Wc2x-extensions")
+# define RBIMPL_HAS_C_ATTRIBUTE(_) 0
+
#elif defined(__has_c_attribute)
# define RBIMPL_HAS_C_ATTRIBUTE(_) __has_c_attribute(_)
diff --git a/include/ruby/internal/intern/array.h b/include/ruby/internal/intern/array.h
index 2262c6f0c6..1909fdf17b 100644
--- a/include/ruby/internal/intern/array.h
+++ b/include/ruby/internal/intern/array.h
@@ -187,7 +187,7 @@ VALUE rb_ary_shared_with_p(VALUE lhs, VALUE rhs);
* : (int i) -> T?
* | (int beg, int len) -> ::Array[T]?
* | (Range[int] r) -> ::Array[T]?
- * | (ArithmeticSequence as) -> ::Array[T]? # This also raises RagneError.
+ * | (ArithmeticSequence as) -> ::Array[T]? # This also raises RangeError.
* end
* ```
*/
diff --git a/include/ruby/internal/intern/bignum.h b/include/ruby/internal/intern/bignum.h
index 43d68018de..c27f77a1fb 100644
--- a/include/ruby/internal/intern/bignum.h
+++ b/include/ruby/internal/intern/bignum.h
@@ -51,7 +51,7 @@ RBIMPL_SYMBOL_EXPORT_BEGIN()
VALUE rb_big_new(size_t len, int sign);
/**
- * Queries if the passed bignum instance is a "bigzro". What is a bigzero?
+ * Queries if the passed bignum instance is a "bigzero". What is a bigzero?
* Well, bignums are for very big integers, but can also represent tiny ones
* like -1, 0, 1. Bigzero are instances of bignums whose values are zero.
* Knowing if a bignum is bigzero can be handy on occasions, like for instance
@@ -793,7 +793,7 @@ size_t rb_absint_size(VALUE val, int *nlz_bits_ret);
* @exception rb_eTypeError `val` doesn't respond to `#to_int`.
* @retval (size_t)-1 Overflowed.
* @retval otherwise
- `((val_numbits * CHAR_BIT + word_numbits - 1) / word_numbits)`,
+ * `((val_numbits * CHAR_BIT + word_numbits - 1) / word_numbits)`,
* where val_numbits is the number of bits of `abs(val)`.
* @post If `nlz_bits_ret` is not `NULL` and there is no overflow,
* `(return_value * word_numbits - val_numbits)` is stored in
diff --git a/include/ruby/internal/intern/class.h b/include/ruby/internal/intern/class.h
index 0fb2d001bc..357af5d176 100644
--- a/include/ruby/internal/intern/class.h
+++ b/include/ruby/internal/intern/class.h
@@ -88,8 +88,8 @@ VALUE rb_define_class_id(ID id, VALUE super);
* @post `outer::id` refers the returned class.
* @note If a class named `id` is already defined and its superclass is
* `super`, the function just returns the defined class.
- * @note The compaction GC does not move classes returned by this
- * function.
+ * @note The GC does not collect nor move classes returned by this
+ * function. They are immortal.
*/
VALUE rb_define_class_id_under(VALUE outer, ID id, VALUE super);
@@ -127,8 +127,8 @@ VALUE rb_define_module_id(ID id);
* constant is not a module.
* @return The created module.
* @post `outer::id` refers the returned module.
- * @note The compaction GC does not move classes returned by this
- * function.
+ * @note The GC does not collect nor move classes returned by this
+ * function. They are immortal.
*/
VALUE rb_define_module_id_under(VALUE outer, ID id);
diff --git a/include/ruby/internal/intern/error.h b/include/ruby/internal/intern/error.h
index bf8daadd3e..11e147a121 100644
--- a/include/ruby/internal/intern/error.h
+++ b/include/ruby/internal/intern/error.h
@@ -190,7 +190,6 @@ RBIMPL_ATTR_NONNULL(())
*/
void rb_error_frozen(const char *what);
-RBIMPL_ATTR_NORETURN()
/**
* Identical to rb_error_frozen(), except it takes arbitrary Ruby object
* instead of C's string.
diff --git a/include/ruby/internal/intern/load.h b/include/ruby/internal/intern/load.h
index 288a16c2ec..9ceb98c2e4 100644
--- a/include/ruby/internal/intern/load.h
+++ b/include/ruby/internal/intern/load.h
@@ -177,6 +177,43 @@ VALUE rb_f_require(VALUE self, VALUE feature);
VALUE rb_require_string(VALUE feature);
/**
+ * Resolves and returns a symbol of a function in the native extension
+ * specified by the feature and symbol names. Extensions will use this function
+ * to access the symbols provided by other native extensions.
+ *
+ * @param[in] feature Name of a feature, e.g. `"json"`.
+ * @param[in] symbol Name of a symbol defined by the feature.
+ * @return The resolved symbol of a function, defined and externed by the
+ * specified feature. It may be NULL if the feature is not loaded,
+ * the feature is not extension, or the symbol is not found.
+ */
+void *rb_ext_resolve_symbol(const char *feature, const char *symbol);
+
+/**
+ * This macro is to provide backwards compatibility. It provides a way to
+ * define function prototypes and resolving function symbols in a safe way.
+ *
+ * ```CXX
+ * // prototypes
+ * #ifdef HAVE_RB_EXT_RESOLVE_SYMBOL
+ * VALUE *(*other_extension_func)(VALUE,VALUE);
+ * #else
+ * VALUE other_extension_func(VALUE);
+ * #endif
+ *
+ * // in Init_xxx()
+ * #ifdef HAVE_RB_EXT_RESOLVE_SYMBOL
+ * other_extension_func = \
+ * (VALUE(*)(VALUE,VALUE))rb_ext_resolve_symbol(fname, sym_name);
+ * if (other_extension_func == NULL) {
+ * // raise your own error
+ * }
+ * #endif
+ * ```
+ */
+#define HAVE_RB_EXT_RESOLVE_SYMBOL 1
+
+/**
* @name extension configuration
* @{
*/
diff --git a/include/ruby/internal/intern/object.h b/include/ruby/internal/intern/object.h
index b9ffa57c06..9daad7d046 100644
--- a/include/ruby/internal/intern/object.h
+++ b/include/ruby/internal/intern/object.h
@@ -151,13 +151,12 @@ VALUE rb_obj_is_kind_of(VALUE obj, VALUE klass);
* @return An allocated, not yet initialised instance of `klass`.
* @note It calls the allocator defined by rb_define_alloc_func(). You
* cannot use this function to define an allocator. Use
- * rb_newobj_of(), #TypedData_Make_Struct or others, instead.
+ * TypedData_Make_Struct or others, instead.
* @note Usually prefer rb_class_new_instance() to rb_obj_alloc() and
* rb_obj_call_init().
* @see rb_class_new_instance()
* @see rb_obj_call_init()
* @see rb_define_alloc_func()
- * @see rb_newobj_of()
* @see #TypedData_Make_Struct
*/
VALUE rb_obj_alloc(VALUE klass);
diff --git a/include/ruby/internal/intern/process.h b/include/ruby/internal/intern/process.h
index 7a7b24ed4b..cfa5e13162 100644
--- a/include/ruby/internal/intern/process.h
+++ b/include/ruby/internal/intern/process.h
@@ -31,6 +31,15 @@ RBIMPL_SYMBOL_EXPORT_BEGIN()
/* process.c */
/**
+ * Wait for the specified process to terminate, reap it, and return its status.
+ *
+ * @param[in] pid The process ID to wait for.
+ * @param[in] flags The flags to pass to waitpid(2).
+ * @return VALUE An instance of Process::Status.
+ */
+VALUE rb_process_status_wait(rb_pid_t pid, int flags);
+
+/**
* Sets the "last status", or the `$?`.
*
* @param[in] status The termination status, as defined in `waitpid(3posix)`.
@@ -247,7 +256,7 @@ rb_pid_t rb_spawn_err(int argc, const VALUE *argv, char *errbuf, size_t buflen);
*
* @internal
*
- * This function might or might not exist depending on `./confiugre` result.
+ * This function might or might not exist depending on `./configure` result.
* It must be a portability hell. Better not use.
*/
VALUE rb_proc_times(VALUE _);
diff --git a/include/ruby/internal/intern/re.h b/include/ruby/internal/intern/re.h
index 31f5593275..4dd58b469b 100644
--- a/include/ruby/internal/intern/re.h
+++ b/include/ruby/internal/intern/re.h
@@ -87,11 +87,6 @@ void rb_match_busy(VALUE md);
* @retval RUBY_Qfalse There is a `n`-th capture and is empty.
* @retval RUBY_Qtrue There is a `n`-th capture that has something.
*
- * @internal
- *
- * @shyouhei wonders: why there are both rb_reg_match_defined() and
- * rb_match_nth_defined, which are largely the same things, but do not share
- * their implementations at all?
*/
VALUE rb_reg_nth_defined(int n, VALUE md);
diff --git a/include/ruby/internal/intern/select.h b/include/ruby/internal/intern/select.h
index fabc287cd1..6ba84c6e63 100644
--- a/include/ruby/internal/intern/select.h
+++ b/include/ruby/internal/intern/select.h
@@ -76,7 +76,7 @@ struct timeval;
*
* Although any file descriptors are possible here, it makes completely no
* sense to pass a descriptor that is not `O_NONBLOCK`. If you want to know
- * the reason for this limitatuon in detail, you might find this thread super
+ * the reason for this limitation in detail, you might find this thread super
* interesting: https://lkml.org/lkml/2004/10/6/117
*/
int rb_thread_fd_select(int nfds, rb_fdset_t *rfds, rb_fdset_t *wfds, rb_fdset_t *efds, struct timeval *timeout);
diff --git a/include/ruby/internal/intern/signal.h b/include/ruby/internal/intern/signal.h
index 84f7558404..4773788651 100644
--- a/include/ruby/internal/intern/signal.h
+++ b/include/ruby/internal/intern/signal.h
@@ -97,7 +97,7 @@ RBIMPL_ATTR_NONNULL(())
* - Case #11: When signo and PID are both negative, the behaviour of this
* function depends on how `killpg(3)` works. On Linux, it seems such
* attempt is strictly prohibited and `Errno::EINVAL` is raised. But on
- * macOS, it seems it tries to to send the signal actually to the process
+ * macOS, it seems it tries to send the signal actually to the process
* group.
*
* @note Above description is in fact different from how `kill(2)` works.
@@ -113,12 +113,6 @@ RBIMPL_ATTR_NONNULL(())
*/
VALUE rb_f_kill(int argc, const VALUE *argv);
-/* This must be private, @shyouhei guesses. */
-#ifdef POSIX_SIGNAL
-#define posix_signal ruby_posix_signal
-void (*posix_signal(int, void (*)(int)))(int);
-#endif
-
RBIMPL_ATTR_PURE()
/**
* Queries the name of the signal. It returns for instance `"KILL"` for
diff --git a/include/ruby/internal/intern/string.h b/include/ruby/internal/intern/string.h
index 3083125e56..6827563e8d 100644
--- a/include/ruby/internal/intern/string.h
+++ b/include/ruby/internal/intern/string.h
@@ -412,7 +412,7 @@ VALUE rb_utf8_str_new_static(const char *ptr, long len);
/**
* Identical to rb_interned_str(), except it takes a Ruby's string instead of
- * C's. It can also be seen as a routine identical to to rb_str_new_shared(),
+ * C's. It can also be seen as a routine identical to rb_str_new_shared(),
* except it returns an infamous "f"string.
*
* @param[in] str An object of ::RString.
@@ -454,7 +454,7 @@ VALUE rb_interned_str(const char *ptr, long len);
RBIMPL_ATTR_NONNULL(())
/**
* Identical to rb_interned_str(), except it assumes the passed pointer is a
- * pointer to a C's string. It can also be seen as a routine identical to to
+ * pointer to a C's string. It can also be seen as a routine identical to
* rb_str_to_interned_str(), except it takes a C's string instead of Ruby's.
* Or it can also be seen as a routine identical to rb_str_new_cstr(), except
* it returns an infamous "f"string.
@@ -602,6 +602,21 @@ VALUE rb_str_dup(VALUE str);
VALUE rb_str_resurrect(VALUE str);
/**
+ * Returns whether a string is chilled or not.
+ *
+ * This function is temporary and users must check for its presence using
+ * #ifdef HAVE_RB_STR_CHILLED_P. If HAVE_RB_STR_CHILLED_P is not defined, then
+ * strings can't be chilled.
+ *
+ * @param[in] str A string.
+ * @retval 1 The string is chilled.
+ * @retval 0 Otherwise.
+ */
+bool rb_str_chilled_p(VALUE str);
+
+#define HAVE_RB_STR_CHILLED_P 1
+
+/**
* Obtains a "temporary lock" of the string. This advisory locking mechanism
* prevents other cooperating threads from tampering the receiver. The same
* thing could be done via freeze mechanism, but this one can also be unlocked
diff --git a/include/ruby/internal/intern/struct.h b/include/ruby/internal/intern/struct.h
index 312cf444e2..16b3fad4e0 100644
--- a/include/ruby/internal/intern/struct.h
+++ b/include/ruby/internal/intern/struct.h
@@ -46,14 +46,16 @@ VALUE rb_struct_new(VALUE klass, ...);
*
* @param[in] name Name of the class.
* @param[in] ... Arbitrary number of `const char*`, terminated by
- * zero. Each of which are the name of fields.
+ * NULL. Each of which are the name of fields.
* @exception rb_eNameError `name` is not a constant name.
* @exception rb_eTypeError `name` is already taken.
- * @exception rb_eArgError Duplicated field name.
+ * @exception rb_eArgError Duplicated field name.
* @return The defined class.
* @post Global toplevel constant `name` is defined.
* @note `name` is allowed to be a null pointer. This function creates
* an anonymous struct class then.
+ * @note The GC does not collect nor move classes returned by this
+ * function. They are immortal.
*
* @internal
*
@@ -70,14 +72,16 @@ RBIMPL_ATTR_NONNULL((2))
* @param[out] space Namespace that the defining class shall reside.
* @param[in] name Name of the class.
* @param[in] ... Arbitrary number of `const char*`, terminated by
- * zero. Each of which are the name of fields.
+ * NULL. Each of which are the name of fields.
* @exception rb_eNameError `name` is not a constant name.
* @exception rb_eTypeError `name` is already taken.
- * @exception rb_eArgError Duplicated field name.
+ * @exception rb_eArgError Duplicated field name.
* @return The defined class.
* @post `name` is a constant under `space`.
* @note In contrast to rb_struct_define(), it doesn't make any sense to
* pass a null pointer to this function.
+ * @note The GC does not collect nor move classes returned by this
+ * function. They are immortal.
*/
VALUE rb_struct_define_under(VALUE space, const char *name, ...);
@@ -164,10 +168,10 @@ VALUE rb_struct_alloc_noinit(VALUE klass);
* @param[in] super Superclass of the defining class.
* @param[in] func Must be 0 for extension libraries.
* @param[in] ... Arbitrary number of `const char*`, terminated by
- * zero. Each of which are the name of fields.
+ * NULL. Each of which are the name of fields.
* @exception rb_eNameError `name` is not a constant name.
* @exception rb_eTypeError `name` is already taken.
- * @exception rb_eArgError Duplicated field name.
+ * @exception rb_eArgError Duplicated field name.
* @return The defined class.
* @post Global toplevel constant `name` is defined.
* @note `name` is allowed to be a null pointer. This function creates
@@ -187,17 +191,35 @@ RBIMPL_ATTR_NONNULL((2))
* @param[in] super Superclass of the defining class.
* @param[in] alloc Must be 0 for extension libraries.
* @param[in] ... Arbitrary number of `const char*`, terminated by
- * zero. Each of which are the name of fields.
+ * NULL. Each of which are the name of fields.
* @exception rb_eNameError `class_name` is not a constant name.
* @exception rb_eTypeError `class_name` is already taken.
- * @exception rb_eArgError Duplicated field name.
+ * @exception rb_eArgError Duplicated field name.
* @return The defined class.
* @post `class_name` is a constant under `outer`.
* @note In contrast to rb_struct_define_without_accessor(), it doesn't
* make any sense to pass a null name.
+ * @note The GC does not collect nor move classes returned by this
+ * function. They are immortal.
*/
VALUE rb_struct_define_without_accessor_under(VALUE outer, const char *class_name, VALUE super, rb_alloc_func_t alloc, ...);
+/**
+ * Defines an anonymous data class.
+ *
+ * @endinternal
+ *
+ * @param[in] super Superclass of the defining class. Must be a
+ * descendant of ::rb_cData, or 0 as ::rb_cData.
+ * @param[in] ... Arbitrary number of `const char*`, terminated by
+ * NULL. Each of which are the name of fields.
+ * @exception rb_eArgError Duplicated field name.
+ * @return The defined class.
+ * @note The GC does not collect nor move classes returned by this
+ * function. They are immortal.
+ */
+VALUE rb_data_define(VALUE super, ...);
+
RBIMPL_SYMBOL_EXPORT_END()
#endif /* RBIMPL_INTERN_STRUCT_H */
diff --git a/include/ruby/internal/intern/vm.h b/include/ruby/internal/intern/vm.h
index 76af796b54..29e0c7f534 100644
--- a/include/ruby/internal/intern/vm.h
+++ b/include/ruby/internal/intern/vm.h
@@ -229,8 +229,7 @@ void rb_define_alloc_func(VALUE klass, rb_alloc_func_t func);
* restrict creation of an instance of a class. For example it rarely makes
* sense for a DB adaptor class to allow programmers creating DB row objects
* without querying the DB itself. You can kill sporadic creation of such
- * objects then, by nullifying the allocator function using this API. Your
- * object shall be allocated using #RB_NEWOBJ_OF() directly.
+ * objects then, by nullifying the allocator function using this API.
*
* @param[out] klass The class to modify.
* @pre `klass` must be an instance of Class.
diff --git a/include/ruby/internal/interpreter.h b/include/ruby/internal/interpreter.h
index 662d39c0ec..a10e7ad2d8 100644
--- a/include/ruby/internal/interpreter.h
+++ b/include/ruby/internal/interpreter.h
@@ -141,7 +141,7 @@ void ruby_show_copyright(void);
*
* @param[in] addr A pointer somewhere on the stack, near its bottom.
*/
-void ruby_init_stack(volatile VALUE *addr);
+void ruby_init_stack(void *addr);
/**
* Initializes the VM and builtin libraries.
diff --git a/include/ruby/internal/memory.h b/include/ruby/internal/memory.h
index 6884db195d..270cc1ac8b 100644
--- a/include/ruby/internal/memory.h
+++ b/include/ruby/internal/memory.h
@@ -38,7 +38,7 @@
# include <alloca.h>
#endif
-#if defined(_MSC_VER) && defined(_WIN64)
+#if defined(_MSC_VER) && defined(_M_AMD64)
# include <intrin.h>
# pragma intrinsic(_umul128)
#endif
@@ -56,13 +56,14 @@
#include "ruby/internal/has/builtin.h"
#include "ruby/internal/stdalign.h"
#include "ruby/internal/stdbool.h"
+#include "ruby/internal/stdckdint.h"
#include "ruby/internal/xmalloc.h"
#include "ruby/backward/2/limits.h"
#include "ruby/backward/2/long_long.h"
#include "ruby/backward/2/assume.h"
#include "ruby/defines.h"
-/** @cond INTENAL_MACRO */
+/** @cond INTERNAL_MACRO */
/* Make alloca work the best possible way. */
#if defined(alloca)
@@ -567,7 +568,10 @@ rbimpl_size_mul_overflow(size_t x, size_t y)
{
struct rbimpl_size_mul_overflow_tag ret = { false, 0, };
-#if RBIMPL_HAS_BUILTIN(__builtin_mul_overflow)
+#if defined(ckd_mul)
+ ret.left = ckd_mul(&ret.right, x, y);
+
+#elif RBIMPL_HAS_BUILTIN(__builtin_mul_overflow)
ret.left = __builtin_mul_overflow(x, y, &ret.right);
#elif defined(DSIZE_T)
diff --git a/include/ruby/internal/module.h b/include/ruby/internal/module.h
index d678dd2102..97b0b2b8b0 100644
--- a/include/ruby/internal/module.h
+++ b/include/ruby/internal/module.h
@@ -56,8 +56,8 @@ RBIMPL_ATTR_NONNULL(())
* @post Top-level constant named `name` refers the returned class.
* @note If a class named `name` is already defined and its superclass is
* `super`, the function just returns the defined class.
- * @note The compaction GC does not move classes returned by this
- * function.
+ * @note The GC does not collect nor move classes returned by this
+ * function. They are immortal.
*
* @internal
*
@@ -75,8 +75,8 @@ RBIMPL_ATTR_NONNULL(())
* constant is not a module.
* @return The created module.
* @post Top-level constant named `name` refers the returned module.
- * @note The compaction GC does not move classes returned by this
- * function.
+ * @note The GC does not collect nor move modules returned by this
+ * function. They are immortal.
*
* @internal
*
@@ -103,8 +103,8 @@ RBIMPL_ATTR_NONNULL(())
* @post `outer::name` refers the returned class.
* @note If a class named `name` is already defined and its superclass
* is `super`, the function just returns the defined class.
- * @note The compaction GC does not move classes returned by this
- * function.
+ * @note The GC does not collect nor move classes returned by this
+ * function. They are immortal.
*/
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super);
@@ -118,8 +118,8 @@ RBIMPL_ATTR_NONNULL(())
* the constant is not a class.
* @return The created module.
* @post `outer::name` refers the returned module.
- * @note The compaction GC does not move classes returned by this
- * function.
+ * @note The GC does not collect nor move modules returned by this
+ * function. They are immortal.
*/
VALUE rb_define_module_under(VALUE outer, const char *name);
diff --git a/include/ruby/internal/newobj.h b/include/ruby/internal/newobj.h
index ba1d7cbe59..6eee2fa5fa 100644
--- a/include/ruby/internal/newobj.h
+++ b/include/ruby/internal/newobj.h
@@ -29,63 +29,14 @@
#include "ruby/internal/value.h"
#include "ruby/assert.h"
-/**
- * Declares, allocates, then assigns a new object to the given variable.
- *
- * @param obj Variable name.
- * @param type Variable type.
- * @exception rb_eNoMemError No space left.
- * @return An allocated object, not initialised.
- * @note Modern programs tend to use #NEWOBJ_OF instead.
- *
- * @internal
- *
- * :FIXME: Should we deprecate it?
- */
-#define RB_NEWOBJ(obj,type) type *(obj) = RBIMPL_CAST((type *)rb_newobj())
-
-/**
- * Identical to #RB_NEWOBJ, except it also accepts the allocating object's
- * class and flags.
- *
- * @param obj Variable name.
- * @param type Variable type.
- * @param klass Object's class.
- * @param flags Object's flags.
- * @exception rb_eNoMemError No space left.
- * @return An allocated object, filled with the arguments.
- */
-#define RB_NEWOBJ_OF(obj,type,klass,flags) type *(obj) = RBIMPL_CAST((type *)rb_newobj_of(klass, flags))
-
-#define NEWOBJ RB_NEWOBJ /**< @old{RB_NEWOBJ} */
-#define NEWOBJ_OF RB_NEWOBJ_OF /**< @old{RB_NEWOBJ_OF} */
#define OBJSETUP rb_obj_setup /**< @old{rb_obj_setup} */
#define CLONESETUP rb_clone_setup /**< @old{rb_clone_setup} */
#define DUPSETUP rb_dup_setup /**< @old{rb_dup_setup} */
RBIMPL_SYMBOL_EXPORT_BEGIN()
/**
- * This is the implementation detail of #RB_NEWOBJ.
- *
- * @exception rb_eNoMemError No space left.
- * @return An allocated object, not initialised.
- */
-VALUE rb_newobj(void);
-
-/**
- * This is the implementation detail of #RB_NEWOBJ_OF.
- *
- * @param klass Object's class.
- * @param flags Object's flags.
- * @exception rb_eNoMemError No space left.
- * @return An allocated object, filled with the arguments.
- */
-VALUE rb_newobj_of(VALUE klass, VALUE flags);
-
-/**
* Fills common fields in the object.
*
- * @note Prefer rb_newobj_of() to this function.
* @param[in,out] obj A Ruby object to be set up.
* @param[in] klass `obj` will belong to this class.
* @param[in] type One of ::ruby_value_type.
diff --git a/include/ruby/internal/static_assert.h b/include/ruby/internal/static_assert.h
index 594c2b2917..b9ff6646e7 100644
--- a/include/ruby/internal/static_assert.h
+++ b/include/ruby/internal/static_assert.h
@@ -71,7 +71,7 @@
#else
# define RBIMPL_STATIC_ASSERT(name, expr) \
- typedef int static_assert_ ## name ## _check[1 - 2 * !(expr)]
+ MAYBE_UNUSED(typedef int static_assert_ ## name ## _check[1 - 2 * !(expr)])
#endif
#endif /* RBIMPL_STATIC_ASSERT_H */
diff --git a/include/ruby/internal/stdckdint.h b/include/ruby/internal/stdckdint.h
new file mode 100644
index 0000000000..e5b5b8b751
--- /dev/null
+++ b/include/ruby/internal/stdckdint.h
@@ -0,0 +1,68 @@
+#ifndef RBIMPL_STDCKDINT_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_STDCKDINT_H
+/**
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief C23 shim for <stdckdint.h>
+ */
+#include "ruby/internal/config.h"
+#include "ruby/internal/cast.h"
+#include "ruby/internal/has/builtin.h"
+#include "ruby/internal/stdbool.h"
+
+#ifdef __has_include
+# if __has_include(<stdckdint.h>)
+# /* Conforming C23 situation; e.g. recent clang */
+# define RBIMPL_HAVE_STDCKDINT_H
+# endif
+#endif
+
+#ifdef HAVE_STDCKDINT_H
+# /* Some OSes (most notably FreeBSD) have this file. */
+# define RBIMPL_HAVE_STDCKDINT_H
+#endif
+
+#ifdef __cplusplus
+# /* It seems OS/Compiler provided stdckdint.h tend not support C++ yet.
+# * Situations could improve someday but in a meantime let us work around.
+# */
+# undef RBIMPL_HAVE_STDCKDINT_H
+#endif
+
+#ifdef RBIMPL_HAVE_STDCKDINT_H
+# /* Take that. */
+# include <stdckdint.h>
+
+#elif RBIMPL_HAS_BUILTIN(__builtin_add_overflow)
+# define ckd_add(x, y, z) RBIMPL_CAST((bool)__builtin_add_overflow((y), (z), (x)))
+# define ckd_sub(x, y, z) RBIMPL_CAST((bool)__builtin_sub_overflow((y), (z), (x)))
+# define ckd_mul(x, y, z) RBIMPL_CAST((bool)__builtin_mul_overflow((y), (z), (x)))
+# define __STDC_VERSION_STDCKDINT_H__ 202311L
+
+#/* elif defined(__cplusplus) */
+#/* :TODO: if we assume C++11 we can use `<type_traits>` to implement them. */
+
+#else
+# /* intentionally leave them undefined */
+# /* to make `#ifdef ckd_add` etc. work as intended. */
+# undef ckd_add
+# undef ckd_sub
+# undef ckd_mul
+# undef __STDC_VERSION_STDCKDINT_H__
+#endif
+
+#endif /* RBIMPL_STDCKDINT_H */
diff --git a/include/ruby/io.h b/include/ruby/io.h
index 9b381fc9bb..e9dfeda5b1 100644
--- a/include/ruby/io.h
+++ b/include/ruby/io.h
@@ -137,40 +137,51 @@ struct rb_io_encoding {
VALUE ecopts;
};
+#ifndef HAVE_RB_IO_T
+#define HAVE_RB_IO_T 1
/** Ruby's IO, metadata and buffers. */
-typedef struct rb_io {
-
+struct rb_io {
/** The IO's Ruby level counterpart. */
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
VALUE self;
/** stdio ptr for read/write, if available. */
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
FILE *stdio_file;
/** file descriptor. */
+ RBIMPL_ATTR_DEPRECATED(("rb_io_descriptor"))
int fd;
/** mode flags: FMODE_XXXs */
+ RBIMPL_ATTR_DEPRECATED(("rb_io_mode"))
int mode;
/** child's pid (for pipes) */
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
rb_pid_t pid;
/** number of lines read */
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
int lineno;
/** pathname for file */
+ RBIMPL_ATTR_DEPRECATED(("rb_io_path"))
VALUE pathv;
/** finalize proc */
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
void (*finalize)(struct rb_io*,int);
/** Write buffer. */
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
rb_io_buffer_t wbuf;
/**
* (Byte) read buffer. Note also that there is a field called
* ::rb_io_t::cbuf, which also concerns read IO.
*/
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
rb_io_buffer_t rbuf;
/**
@@ -178,20 +189,25 @@ typedef struct rb_io {
*
* @see rb_io_set_write_io()
*/
+ RBIMPL_ATTR_DEPRECATED(("rb_io_get_write_io"))
VALUE tied_io_for_writing;
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
struct rb_io_encoding encs; /**< Decomposed encoding flags. */
/** Encoding converter used when reading from this IO. */
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
rb_econv_t *readconv;
/**
* rb_io_ungetc() destination. This buffer is read before checking
* ::rb_io_t::rbuf
*/
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
rb_io_buffer_t cbuf;
/** Encoding converter used when writing to this IO. */
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
rb_econv_t *writeconv;
/**
@@ -200,21 +216,25 @@ typedef struct rb_io {
* conversion from encoding X to encoding Y does not exist, Ruby finds an
* encoding Z that bridges the two, so that X to Z to Y conversion happens.
*/
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
VALUE writeconv_asciicompat;
/** Whether ::rb_io_t::writeconv is already set up. */
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
int writeconv_initialized;
/**
* Value of ::rb_io_t::rb_io_enc_t::ecflags stored right before
* initialising ::rb_io_t::writeconv.
*/
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
int writeconv_pre_ecflags;
/**
* Value of ::rb_io_t::rb_io_enc_t::ecopts stored right before initialising
* ::rb_io_t::writeconv.
*/
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
VALUE writeconv_pre_ecopts;
/**
@@ -224,27 +244,23 @@ typedef struct rb_io {
*
* This of course doesn't help inter-process IO interleaves, though.
*/
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
VALUE write_lock;
/**
* The timeout associated with this IO when performing blocking operations.
*/
+ RBIMPL_ATTR_DEPRECATED(("rb_io_timeout/rb_io_set_timeout"))
VALUE timeout;
-} rb_io_t;
+};
+#endif
+
+typedef struct rb_io rb_io_t;
/** @alias{rb_io_enc_t} */
typedef struct rb_io_encoding rb_io_enc_t;
/**
- * @private
- *
- * @deprecated This macro once was a thing in the old days, but makes no sense
- * any longer today. Exists here for backwards compatibility
- * only. You can safely forget about it.
- */
-#define HAVE_RB_IO_T 1
-
-/**
* @name Possible flags for ::rb_io_t::mode
*
* @{
diff --git a/include/ruby/io/buffer.h b/include/ruby/io/buffer.h
index 737fafe518..e4d98bf051 100644
--- a/include/ruby/io/buffer.h
+++ b/include/ruby/io/buffer.h
@@ -23,10 +23,18 @@ RBIMPL_SYMBOL_EXPORT_BEGIN()
#define RUBY_IO_BUFFER_VERSION 2
+// The `IO::Buffer` class.
RUBY_EXTERN VALUE rb_cIOBuffer;
+
+// The operating system page size.
RUBY_EXTERN size_t RUBY_IO_BUFFER_PAGE_SIZE;
+
+// The default buffer size, usually a (small) multiple of the page size.
+// Can be overridden by the RUBY_IO_BUFFER_DEFAULT_SIZE environment variable.
RUBY_EXTERN size_t RUBY_IO_BUFFER_DEFAULT_SIZE;
+// Represents the internal state of the buffer.
+// More than one flag can be set at a time.
enum rb_io_buffer_flags {
// The memory in the buffer is owned by someone else.
// More specifically, it means that someone else owns the buffer and we shouldn't try to resize it.
@@ -49,21 +57,22 @@ enum rb_io_buffer_flags {
RB_IO_BUFFER_PRIVATE = 64,
// The buffer is read-only and cannot be modified.
- RB_IO_BUFFER_READONLY = 128
+ RB_IO_BUFFER_READONLY = 128,
+
+ // The buffer is backed by a file.
+ RB_IO_BUFFER_FILE = 256,
};
+// Represents the endian of the data types.
enum rb_io_buffer_endian {
+ // The least significant units are put first.
RB_IO_BUFFER_LITTLE_ENDIAN = 4,
RB_IO_BUFFER_BIG_ENDIAN = 8,
-#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
- RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_LITTLE_ENDIAN,
-#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#if defined(WORDS_BIGENDIAN)
RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_BIG_ENDIAN,
-#elif REG_DWORD == REG_DWORD_LITTLE_ENDIAN
+#else
RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_LITTLE_ENDIAN,
-#elif REG_DWORD == REG_DWORD_BIG_ENDIAN
- RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_BIG_ENDIAN,
#endif
RB_IO_BUFFER_NETWORK_ENDIAN = RB_IO_BUFFER_BIG_ENDIAN
@@ -79,7 +88,10 @@ int rb_io_buffer_try_unlock(VALUE self);
VALUE rb_io_buffer_free(VALUE self);
VALUE rb_io_buffer_free_locked(VALUE self);
-int rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size);
+// Access the internal buffer and flags. Validates the pointers.
+// The points may not remain valid if the source buffer is manipulated.
+// Consider using rb_io_buffer_lock if needed.
+enum rb_io_buffer_flags rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size);
void rb_io_buffer_get_bytes_for_reading(VALUE self, const void **base, size_t *size);
void rb_io_buffer_get_bytes_for_writing(VALUE self, void **base, size_t *size);
diff --git a/include/ruby/memory_view.h b/include/ruby/memory_view.h
index 1ddca2d46f..42309d5afc 100644
--- a/include/ruby/memory_view.h
+++ b/include/ruby/memory_view.h
@@ -47,10 +47,10 @@ typedef struct {
char format;
/** :FIXME: what is a "native" size is unclear. */
- unsigned native_size_p: 1;
+ bool native_size_p;
/** Endian of the component */
- unsigned little_endian_p: 1;
+ bool little_endian_p;
/** The component's offset. */
size_t offset;
diff --git a/include/ruby/onigmo.h b/include/ruby/onigmo.h
index 0a5400c3a5..d233336316 100644
--- a/include/ruby/onigmo.h
+++ b/include/ruby/onigmo.h
@@ -844,6 +844,8 @@ void onig_free(OnigRegex);
ONIG_EXTERN
void onig_free_body(OnigRegex);
ONIG_EXTERN
+int onig_reg_copy(OnigRegex* reg, OnigRegex orig_reg);
+ONIG_EXTERN
OnigPosition onig_scan(OnigRegex reg, const OnigUChar* str, const OnigUChar* end, OnigRegion* region, OnigOptionType option, int (*scan_callback)(OnigPosition, OnigPosition, OnigRegion*, void*), void* callback_arg);
ONIG_EXTERN
OnigPosition onig_search(OnigRegex, const OnigUChar* str, const OnigUChar* end, const OnigUChar* start, const OnigUChar* range, OnigRegion* region, OnigOptionType option);
diff --git a/include/ruby/random.h b/include/ruby/random.h
index 39bdb6f3e3..f3df0d96fb 100644
--- a/include/ruby/random.h
+++ b/include/ruby/random.h
@@ -11,7 +11,7 @@
*
* This is a set of APIs to roll your own subclass of ::rb_cRandom. An
* illustrative example of such PRNG can be found at
- * `ext/-test-/ramdom/loop.c`.
+ * `ext/-test-/random/loop.c`.
*/
#include "ruby/ruby.h"
diff --git a/include/ruby/re.h b/include/ruby/re.h
index 3892d6e7f2..f86d6f26cf 100644
--- a/include/ruby/re.h
+++ b/include/ruby/re.h
@@ -18,6 +18,7 @@
#include <stdio.h>
+#include "ruby/onigmo.h"
#include "ruby/regex.h"
#include "ruby/internal/core/rmatch.h"
#include "ruby/internal/dllexport.h"
@@ -126,6 +127,30 @@ VALUE rb_reg_quote(VALUE str);
regex_t *rb_reg_prepare_re(VALUE re, VALUE str);
/**
+ * Runs a regular expression match using function `match`. Performs preparation,
+ * error handling, and memory cleanup.
+ *
+ * @param[in] re Target regular expression.
+ * @param[in] str What `re` is about to run on.
+ * @param[in] match The function to run to match `str` against `re`.
+ * @param[in] args Pointer to arguments to pass into `match`.
+ * @param[out] regs Registers on a successful match.
+ * @exception rb_eArgError `re` does not fit for `str`.
+ * @exception rb_eEncCompatError `re` and `str` are incompatible.
+ * @exception rb_eRegexpError `re` is malformed.
+ * @return Match position on a successful match, `ONIG_MISMATCH` otherwise.
+ *
+ * @internal
+ *
+ * The type `regex_t *` is defined in `<ruby/onigmo.h>`, _and_
+ * _conflicts_ with POSIX's `<regex.h>`. We can no longer save the situation
+ * at this point. Just don't mix the two.
+ */
+OnigPosition rb_reg_onig_match(VALUE re, VALUE str,
+ OnigPosition (*match)(regex_t *reg, VALUE str, struct re_registers *regs, void *args),
+ void *args, struct re_registers *regs);
+
+/**
* Duplicates a match data. This is roughly the same as `onig_region_copy()`,
* except it tries to GC when there is not enough memory.
*
diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h
index eb9a7e4d0f..035f02c70b 100644
--- a/include/ruby/ruby.h
+++ b/include/ruby/ruby.h
@@ -97,8 +97,10 @@ VALUE rb_get_path(VALUE obj);
VALUE rb_get_path_no_checksafe(VALUE);
/**
- * @deprecated This macro is an alias of #FilePathValue now. The part that did
- * "String" was deleted. It remains here because of no harm.
+ * This macro actually does the same thing as #FilePathValue now. The "String"
+ * part indicates that this is for when a string is treated like a pathname,
+ * rather than the actual pathname on the file systems. For examples:
+ * `Dir.fnmatch?`, `File.join`, `File.basename`, etc.
*/
#define FilePathStringValue(v) ((v) = rb_get_path(v))
@@ -270,6 +272,124 @@ RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 3, 0)
*/
int ruby_vsnprintf(char *str, size_t n, char const *fmt, va_list ap);
+#include <errno.h>
+
+/**
+ * @name Errno handling routines for userland threads
+ * @note POSIX chapter 2 section 3 states that for each thread of a process,
+ * the value of `errno` shall not be affected by function calls or
+ * assignments to `errno` by other threads.
+ *
+ * Soooo this `#define errno` below seems like a noob mistake at first sight.
+ * If you look at its actual implementation, the functions are just adding one
+ * level of indirection. It doesn't make any sense sorry? But yes! @ko1 told
+ * @shyouhei that this is inevitable.
+ *
+ * The ultimate reason is because Ruby now has N:M threads implemented.
+ * Threads of that sort change their context in user land. A function can be
+ * "transferred" between threads in middle of their executions. Let us for
+ * instance consider:
+ *
+ * ```cxx
+ * void foo()
+ * {
+ * auto i = errno;
+ * close(0);
+ * errno = i;
+ * }
+ * ```
+ *
+ * This function (if ran under our Ractor) could change its running thread at
+ * the `close` function. But the two `errno` invocations are different! Look
+ * how the source code above is compiled by clang 17 with `-O3` flag @ Linux:
+ *
+ * ```
+ * foo(int): # @foo(int)
+ * push rbp
+ * push r14
+ * push rbx
+ * mov ebx, edi
+ * call __errno_location@PLT
+ * mov r14, rax
+ * mov ebp, dword ptr [rax]
+ * mov edi, ebx
+ * call close@PLT
+ * mov dword ptr [r14], ebp
+ * pop rbx
+ * pop r14
+ * pop rbp
+ * ret
+ * ```
+ *
+ * Notice how `__errno_location@PLT` is `call`-ed only once. The compiler
+ * assumes that the location of `errno` does not change during a function call.
+ * Sadly this is no longer true for us. The `close@PLT` now changes threads,
+ * which should also change where `errno` is stored.
+ *
+ * With the `#define errno` below the compilation result changes to this:
+ *
+ * ```
+ * foo(int): # @foo(int)
+ * push rbp
+ * push rbx
+ * push rax
+ * mov ebx, edi
+ * call rb_errno_ptr()@PLT
+ * mov ebp, dword ptr [rax]
+ * mov edi, ebx
+ * call close@PLT
+ * call rb_errno_ptr()@PLT
+ * mov dword ptr [rax], ebp
+ * add rsp, 8
+ * pop rbx
+ * pop rbp
+ * ret
+ * ```
+ *
+ * Which fixes the problem.
+ */
+
+/**
+ * Identical to system `errno`.
+ *
+ * @return The last set `errno` number.
+ */
+int rb_errno(void);
+
+/**
+ * Set the errno.
+ *
+ * @param err New `errno`.
+ * @post `errno` is now set to `err`.
+ */
+void rb_errno_set(int err);
+
+/**
+ * The location of `errno`
+ *
+ * @return The (thread-specific) location of `errno`.
+ */
+int *rb_errno_ptr(void);
+
+/**
+ * Not sure if it is necessary for extension libraries but this is where the
+ * "bare" errno is located.
+ *
+ * @return The location of `errno`.
+ */
+static inline int *
+rb_orig_errno_ptr(void)
+{
+ return &errno;
+}
+
+#define rb_orig_errno errno /**< System-provided original `errno`. */
+#undef errno
+#define errno (*rb_errno_ptr()) /**< Ractor-aware version of `errno`. */
+
+/** @} */
+
+
/** @cond INTERNAL_MACRO */
#if RBIMPL_HAS_WARNING("-Wgnu-zero-variadic-macro-arguments")
# /* Skip it; clang -pedantic doesn't like the following */
diff --git a/include/ruby/st.h b/include/ruby/st.h
index 0d42283364..f35ab43603 100644
--- a/include/ruby/st.h
+++ b/include/ruby/st.h
@@ -104,8 +104,6 @@ st_table *rb_st_init_table(const struct st_hash_type *);
#define st_init_table rb_st_init_table
st_table *rb_st_init_table_with_size(const struct st_hash_type *, st_index_t);
#define st_init_table_with_size rb_st_init_table_with_size
-st_table *rb_st_init_existing_table_with_size(st_table *tab, const struct st_hash_type *type, st_index_t size);
-#define st_init_existing_table_with_size rb_st_init_existing_table_with_size
st_table *rb_st_init_numtable(void);
#define st_init_numtable rb_st_init_numtable
st_table *rb_st_init_numtable_with_size(st_index_t);
diff --git a/include/ruby/thread.h b/include/ruby/thread.h
index 0b5b1ca0f3..337f477fd0 100644
--- a/include/ruby/thread.h
+++ b/include/ruby/thread.h
@@ -190,14 +190,59 @@ void *rb_nogvl(void *(*func)(void *), void *data1,
*/
#define RUBY_CALL_WO_GVL_FLAG_SKIP_CHECK_INTS_
-#define RUBY_INTERNAL_THREAD_EVENT_STARTED 1 << 0 /** thread started */
+/**
+ * Declare the current Ruby thread should acquire a dedicated
+ * native thread on M:N thread scheduler.
+ *
+ * If a C extension (or a library which the extension relies on) should
+ * keep to run on a native thread (e.g. using thread-local-storage),
+ * this function allocates a dedicated native thread for the thread.
+ *
+ * @return `false` if the thread already running on a dedicated native
+ * thread. Otherwise `true`.
+ */
+bool rb_thread_lock_native_thread(void);
+
+/**
+ * Triggered when a new thread is started.
+ *
+ * @note The callback will be called *without* the GVL held.
+ */
+#define RUBY_INTERNAL_THREAD_EVENT_STARTED 1 << 0
+
+/**
+* Triggered when a thread attempt to acquire the GVL.
+*
+* @note The callback will be called *without* the GVL held.
+*/
#define RUBY_INTERNAL_THREAD_EVENT_READY 1 << 1 /** acquiring GVL */
+
+/**
+ * Triggered when a thread successfully acquired the GVL.
+ *
+ * @note The callback will be called *with* the GVL held.
+ */
#define RUBY_INTERNAL_THREAD_EVENT_RESUMED 1 << 2 /** acquired GVL */
+
+/**
+ * Triggered when a thread released the GVL.
+ *
+ * @note The callback will be called *without* the GVL held.
+ */
#define RUBY_INTERNAL_THREAD_EVENT_SUSPENDED 1 << 3 /** released GVL */
+
+/**
+ * Triggered when a thread exits.
+ *
+ * @note The callback will be called *without* the GVL held.
+ */
#define RUBY_INTERNAL_THREAD_EVENT_EXITED 1 << 4 /** thread terminated */
+
#define RUBY_INTERNAL_THREAD_EVENT_MASK 0xff /** All Thread events */
-typedef void rb_internal_thread_event_data_t; // for future extension.
+typedef struct rb_internal_thread_event_data {
+ VALUE thread;
+} rb_internal_thread_event_data_t;
typedef void (*rb_internal_thread_event_callback)(rb_event_flag_t event,
const rb_internal_thread_event_data_t *event_data,
@@ -211,7 +256,12 @@ typedef struct rb_internal_thread_event_hook rb_internal_thread_event_hook_t;
* @param[in] events A set of events that `func` should run.
* @param[in] data Passed as-is to `func`.
* @return An opaque pointer to the hook, to unregister it later.
- * @note This functionality is a noop on Windows.
+ * @note This functionality is a noop on Windows and WebAssembly.
+ * @note The callback will be called without the GVL held, except for the
+ * RESUMED event.
+ * @note Callbacks are not guaranteed to be executed on the native threads
+ * that corresponds to the Ruby thread. To identify which Ruby thread
+ * the event refers to, you must use `event_data->thread`.
* @warning This function MUST not be called from a thread event callback.
*/
rb_internal_thread_event_hook_t *rb_internal_thread_add_event_hook(
@@ -223,13 +273,53 @@ rb_internal_thread_event_hook_t *rb_internal_thread_add_event_hook(
* Unregister the passed hook.
*
* @param[in] hook. The hook to unregister.
- * @return Wether the hook was found and unregistered.
- * @note This functionality is a noop on Windows.
+ * @return Whether the hook was found and unregistered.
+ * @note This functionality is a noop on Windows and WebAssembly.
* @warning This function MUST not be called from a thread event callback.
*/
bool rb_internal_thread_remove_event_hook(
rb_internal_thread_event_hook_t * hook);
+
+typedef int rb_internal_thread_specific_key_t;
+#define RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX 8
+/**
+ * Create a key to store thread specific data.
+ *
+ * These APIs are designed for tools using
+ * rb_internal_thread_event_hook APIs.
+ *
+ * Note that only `RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX` keys
+ * can be created. raises `ThreadError` if exceeded.
+ *
+ * Usage:
+ * // at initialize time:
+ * int tool_key; // gvar
+ * Init_tool() {
+ * tool_key = rb_internal_thread_specific_key_create();
+ * }
+ *
+ * // at any timing:
+ * rb_internal_thread_specific_set(thread, tool_key, per_thread_data);
+ * ...
+ * per_thread_data = rb_internal_thread_specific_get(thread, tool_key);
+ */
+rb_internal_thread_specific_key_t rb_internal_thread_specific_key_create(void);
+
+/**
+ * Get thread and tool specific data.
+ *
+ * This function is async signal safe and thread safe.
+ */
+void *rb_internal_thread_specific_get(VALUE thread_val, rb_internal_thread_specific_key_t key);
+
+/**
+ * Set thread and tool specific data.
+ *
+ * This function is async signal safe and thread safe.
+ */
+void rb_internal_thread_specific_set(VALUE thread_val, rb_internal_thread_specific_key_t key, void *data);
+
RBIMPL_SYMBOL_EXPORT_END()
#endif /* RUBY_THREAD_H */
diff --git a/include/ruby/util.h b/include/ruby/util.h
index ee11bc940a..12e69c4b80 100644
--- a/include/ruby/util.h
+++ b/include/ruby/util.h
@@ -33,7 +33,9 @@
RBIMPL_SYMBOL_EXPORT_BEGIN()
-/** an approximation of ceil(n * log10(2)), up to 65536 at least */
+/** an approximation of ceil(n * log10(2)), up to 1,048,576 (1<<20)
+ * without overflow within 32-bit calculation
+ */
#define DECIMAL_SIZE_OF_BITS(n) (((n) * 3010 + 9998) / 9999)
/** an approximation of decimal representation size for n-bytes */
diff --git a/include/ruby/version.h b/include/ruby/version.h
index 08c0aadb07..e9113177de 100644
--- a/include/ruby/version.h
+++ b/include/ruby/version.h
@@ -67,7 +67,7 @@
* Minor version. As of writing this version changes annually. Greater
* version doesn't mean "better"; they just mean years passed.
*/
-#define RUBY_API_VERSION_MINOR 3
+#define RUBY_API_VERSION_MINOR 4
/**
* Teeny version. This digit is kind of reserved these days. Kept 0 for the
diff --git a/include/ruby/vm.h b/include/ruby/vm.h
index 3458c28be7..8779780952 100644
--- a/include/ruby/vm.h
+++ b/include/ruby/vm.h
@@ -49,6 +49,13 @@ int ruby_vm_destruct(ruby_vm_t *vm);
*/
void ruby_vm_at_exit(void(*func)(ruby_vm_t *));
+/**
+ * Returns whether the Ruby VM will free all memory at shutdown.
+ *
+ * @return true if free-at-exit is enabled, false otherwise.
+ */
+bool ruby_free_at_exit_p(void);
+
RBIMPL_SYMBOL_EXPORT_END()
#endif /* RUBY_VM_H */
diff --git a/include/ruby/win32.h b/include/ruby/win32.h
index dfb56f4182..27a3467606 100644
--- a/include/ruby/win32.h
+++ b/include/ruby/win32.h
@@ -35,6 +35,7 @@ extern "C++" { /* template without extern "C++" */
#endif
#include <winsock2.h>
#include <ws2tcpip.h>
+#include <mswsock.h>
#if !defined(_MSC_VER) || _MSC_VER >= 1400
#include <iphlpapi.h>
#endif
diff --git a/inits.c b/inits.c
index 8b66334a4d..677a384f9a 100644
--- a/inits.c
+++ b/inits.c
@@ -22,11 +22,6 @@ rb_call_inits(void)
{
CALL(default_shapes);
CALL(Thread_Mutex);
-#if USE_TRANSIENT_HEAP
- CALL(TransientHeap);
-#endif
- CALL(vm_postponed_job);
- CALL(Method);
CALL(RandomSeedCore);
CALL(encodings);
CALL(sym);
@@ -56,7 +51,6 @@ rb_call_inits(void)
CALL(Dir);
CALL(Time);
CALL(Random);
- CALL(signal);
CALL(load);
CALL(Proc);
CALL(Binding);
@@ -68,6 +62,7 @@ rb_call_inits(void)
CALL(VM);
CALL(ISeq);
CALL(Thread);
+ CALL(signal);
CALL(Fiber_Scheduler);
CALL(process);
CALL(Cont);
@@ -78,8 +73,8 @@ rb_call_inits(void)
CALL(vm_trace);
CALL(vm_stack_canary);
CALL(ast);
- CALL(gc_stress);
CALL(shape);
+ CALL(Prism);
// enable builtin loading
CALL(builtin);
diff --git a/insns.def b/insns.def
index bbf946ac67..9c649904b8 100644
--- a/insns.def
+++ b/insns.def
@@ -260,20 +260,7 @@ opt_getconstant_path
(VALUE val)
// attr bool leaf = false; /* may autoload or raise */
{
- const ID *segments = ic->segments;
- struct iseq_inline_constant_cache_entry *ice = ic->entry;
- if (ice && vm_ic_hit_p(ice, GET_EP())) {
- val = ice->value;
-
- VM_ASSERT(val == vm_get_ev_const_chain(ec, segments));
- } else {
- ruby_vm_constant_cache_misses++;
- val = vm_get_ev_const_chain(ec, segments);
- vm_ic_track_const_chain(GET_CFP(), ic, segments);
- // Because leaf=false, we need to undo the PC increment to get the address to this instruction
- // INSN_ATTR(width) == 2
- vm_ic_update(GET_ISEQ(), ic, val, GET_EP(), GET_PC() - 2);
- }
+ val = rb_vm_opt_getconstant_path(ec, GET_CFP(), ic);
}
/* Get constant variable id. If klass is Qnil and allow_nil is Qtrue, constants
@@ -388,7 +375,17 @@ putstring
()
(VALUE val)
{
- val = rb_ec_str_resurrect(ec, str);
+ val = rb_ec_str_resurrect(ec, str, false);
+}
+
+/* put chilled string val. string will be copied but frozen in the future. */
+DEFINE_INSN
+putchilledstring
+(VALUE str)
+()
+(VALUE val)
+{
+ val = rb_ec_str_resurrect(ec, str, true);
}
/* put concatenate strings */
@@ -475,6 +472,20 @@ newarraykwsplat
}
}
+/* push hash onto array unless the hash is empty (as empty keyword
+ splats should be ignored).
+ */
+DEFINE_INSN
+pushtoarraykwsplat
+()
+(VALUE ary, VALUE hash)
+(VALUE ary)
+{
+ if (!RHASH_EMPTY_P(hash)) {
+ rb_ary_push(ary, hash);
+ }
+}
+
/* dup array */
DEFINE_INSN
duparray
@@ -511,13 +522,16 @@ expandarray
(rb_num_t num, rb_num_t flag)
(..., VALUE ary)
(...)
+// attr bool handles_sp = true;
// attr bool leaf = false; /* has rb_check_array_type() */
// attr rb_snum_t sp_inc = (rb_snum_t)num - 1 + (flag & 1 ? 1 : 0);
{
- vm_expandarray(GET_SP(), ary, num, (int)flag);
+ vm_expandarray(GET_CFP(), ary, num, (int)flag);
}
-/* concat two arrays */
+/* concat two arrays, without modifying first array.
+ * attempts to convert both objects to arrays using to_a.
+ */
DEFINE_INSN
concatarray
()
@@ -528,6 +542,32 @@ concatarray
ary = vm_concat_array(ary1, ary2);
}
+/* concat second array to first array.
+ * first argument must already be an array.
+ * attempts to convert second object to array using to_a.
+ */
+DEFINE_INSN
+concattoarray
+()
+(VALUE ary1, VALUE ary2)
+(VALUE ary)
+// attr bool leaf = false; /* has rb_check_array_type() */
+{
+ ary = vm_concat_to_array(ary1, ary2);
+}
+
+/* push given number of objects to array directly before. */
+DEFINE_INSN
+pushtoarray
+(rb_num_t num)
+(...)
+(VALUE val)
+// attr rb_snum_t sp_inc = -(rb_snum_t)num;
+{
+ const VALUE *objp = STACK_ADDR_FROM_TOP(num);
+ val = rb_ary_cat(*(objp-1), objp, num);
+}
+
/* call to_a on array ary to splat */
DEFINE_INSN
splatarray
@@ -539,6 +579,22 @@ splatarray
obj = vm_splat_array(flag, ary);
}
+/* call to_hash on hash to keyword splat before converting block */
+DEFINE_INSN
+splatkw
+()
+(VALUE hash, VALUE block)
+(VALUE obj, VALUE block)
+// attr bool leaf = false; /* has rb_to_hash_type() */
+{
+ if (NIL_P(hash)) {
+ obj = Qnil;
+ }
+ else {
+ obj = rb_to_hash_type(hash);
+ }
+}
+
/* put new Hash from n elements. n must be an even number. */
DEFINE_INSN
newhash
@@ -710,7 +766,7 @@ definedivar
// attr bool leaf = false;
{
val = Qnil;
- if (vm_getivar(GET_SELF(), id, GET_ISEQ(), ic, NULL, FALSE, Qundef) != Qundef) {
+ if (!UNDEF_P(vm_getivar(GET_SELF(), id, GET_ISEQ(), ic, NULL, FALSE, Qundef))) {
val = pushval;
}
}
@@ -815,7 +871,7 @@ send
val = vm_sendish(ec, GET_CFP(), cd, bh, mexp_search_method);
JIT_EXEC(ec, val);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
RESTORE_REGS();
NEXT_INSN();
}
@@ -835,7 +891,7 @@ opt_send_without_block
val = vm_sendish(ec, GET_CFP(), cd, bh, mexp_search_method);
JIT_EXEC(ec, val);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
RESTORE_REGS();
NEXT_INSN();
}
@@ -851,7 +907,7 @@ objtostring
{
val = vm_objtostring(GET_ISEQ(), recv, cd);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
CALL_SIMPLE_METHOD();
}
}
@@ -864,7 +920,7 @@ opt_str_freeze
{
val = vm_opt_str_freeze(str, BOP_FREEZE, idFreeze);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
PUSH(rb_str_resurrect(str));
CALL_SIMPLE_METHOD();
}
@@ -879,7 +935,7 @@ opt_nil_p
{
val = vm_opt_nil_p(GET_ISEQ(), cd, recv);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
CALL_SIMPLE_METHOD();
}
}
@@ -892,7 +948,7 @@ opt_str_uminus
{
val = vm_opt_str_freeze(str, BOP_UMINUS, idUMinus);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
PUSH(rb_str_resurrect(str));
CALL_SIMPLE_METHOD();
}
@@ -939,7 +995,7 @@ invokesuper
val = vm_sendish(ec, GET_CFP(), cd, bh, mexp_search_super);
JIT_EXEC(ec, val);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
RESTORE_REGS();
NEXT_INSN();
}
@@ -959,7 +1015,7 @@ invokeblock
val = vm_sendish(ec, GET_CFP(), cd, bh, mexp_search_invokeblock);
JIT_EXEC(ec, val);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
RESTORE_REGS();
NEXT_INSN();
}
@@ -1117,7 +1173,7 @@ opt_plus
{
val = vm_opt_plus(recv, obj);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
CALL_SIMPLE_METHOD();
}
}
@@ -1131,7 +1187,7 @@ opt_minus
{
val = vm_opt_minus(recv, obj);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
CALL_SIMPLE_METHOD();
}
}
@@ -1145,7 +1201,7 @@ opt_mult
{
val = vm_opt_mult(recv, obj);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
CALL_SIMPLE_METHOD();
}
}
@@ -1162,7 +1218,7 @@ opt_div
{
val = vm_opt_div(recv, obj);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
CALL_SIMPLE_METHOD();
}
}
@@ -1178,7 +1234,7 @@ opt_mod
{
val = vm_opt_mod(recv, obj);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
CALL_SIMPLE_METHOD();
}
}
@@ -1192,7 +1248,7 @@ opt_eq
{
val = opt_equality(GET_ISEQ(), recv, obj, cd);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
CALL_SIMPLE_METHOD();
}
}
@@ -1206,7 +1262,7 @@ opt_neq
{
val = vm_opt_neq(GET_ISEQ(), cd, cd_eq, recv, obj);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
CALL_SIMPLE_METHOD();
}
}
@@ -1220,7 +1276,7 @@ opt_lt
{
val = vm_opt_lt(recv, obj);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
CALL_SIMPLE_METHOD();
}
}
@@ -1234,7 +1290,7 @@ opt_le
{
val = vm_opt_le(recv, obj);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
CALL_SIMPLE_METHOD();
}
}
@@ -1248,7 +1304,7 @@ opt_gt
{
val = vm_opt_gt(recv, obj);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
CALL_SIMPLE_METHOD();
}
}
@@ -1262,7 +1318,7 @@ opt_ge
{
val = vm_opt_ge(recv, obj);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
CALL_SIMPLE_METHOD();
}
}
@@ -1280,7 +1336,7 @@ opt_ltlt
{
val = vm_opt_ltlt(recv, obj);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
CALL_SIMPLE_METHOD();
}
}
@@ -1294,7 +1350,7 @@ opt_and
{
val = vm_opt_and(recv, obj);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
CALL_SIMPLE_METHOD();
}
}
@@ -1308,7 +1364,7 @@ opt_or
{
val = vm_opt_or(recv, obj);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
CALL_SIMPLE_METHOD();
}
}
@@ -1327,7 +1383,7 @@ opt_aref
{
val = vm_opt_aref(recv, obj);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
CALL_SIMPLE_METHOD();
}
}
@@ -1344,7 +1400,7 @@ opt_aset
{
val = vm_opt_aset(recv, obj, set);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
CALL_SIMPLE_METHOD();
}
}
@@ -1360,7 +1416,7 @@ opt_aset_with
{
VALUE tmp = vm_opt_aset_with(recv, key, val);
- if (tmp != Qundef) {
+ if (!UNDEF_P(tmp)) {
val = tmp;
}
else {
@@ -1381,7 +1437,7 @@ opt_aref_with
{
val = vm_opt_aref_with(recv, key);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
PUSH(rb_str_resurrect(key));
CALL_SIMPLE_METHOD();
}
@@ -1396,7 +1452,7 @@ opt_length
{
val = vm_opt_length(recv, BOP_LENGTH);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
CALL_SIMPLE_METHOD();
}
}
@@ -1410,7 +1466,7 @@ opt_size
{
val = vm_opt_length(recv, BOP_SIZE);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
CALL_SIMPLE_METHOD();
}
}
@@ -1424,7 +1480,7 @@ opt_empty_p
{
val = vm_opt_empty_p(recv);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
CALL_SIMPLE_METHOD();
}
}
@@ -1438,7 +1494,7 @@ opt_succ
{
val = vm_opt_succ(recv);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
CALL_SIMPLE_METHOD();
}
}
@@ -1452,7 +1508,7 @@ opt_not
{
val = vm_opt_not(GET_ISEQ(), cd, recv);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
CALL_SIMPLE_METHOD();
}
}
@@ -1467,7 +1523,7 @@ opt_regexpmatch2
{
val = vm_opt_regexpmatch2(obj2, obj1);
- if (val == Qundef) {
+ if (UNDEF_P(val)) {
CALL_SIMPLE_METHOD();
}
}
diff --git a/internal.h b/internal.h
index c66e057f60..4fb99d1c08 100644
--- a/internal.h
+++ b/internal.h
@@ -40,10 +40,6 @@
#undef RClass
#undef RCLASS_SUPER
-/* internal/gc.h */
-#undef NEWOBJ_OF
-#undef RB_NEWOBJ_OF
-
/* internal/hash.h */
#undef RHASH_IFNONE
#undef RHASH_SIZE
diff --git a/internal/array.h b/internal/array.h
index 7e55b66a87..39f6fcbea6 100644
--- a/internal/array.h
+++ b/internal/array.h
@@ -40,8 +40,6 @@ VALUE rb_ary_diff(VALUE ary1, VALUE ary2);
static inline VALUE rb_ary_entry_internal(VALUE ary, long offset);
static inline bool ARY_PTR_USING_P(VALUE ary);
-static inline void RARY_TRANSIENT_SET(VALUE ary);
-static inline void RARY_TRANSIENT_UNSET(VALUE ary);
VALUE rb_ary_tmp_new_from_values(VALUE, long, const VALUE *);
VALUE rb_check_to_array(VALUE ary);
@@ -56,7 +54,7 @@ static inline VALUE
rb_ary_entry_internal(VALUE ary, long offset)
{
long len = RARRAY_LEN(ary);
- const VALUE *ptr = RARRAY_CONST_PTR_TRANSIENT(ary);
+ const VALUE *ptr = RARRAY_CONST_PTR(ary);
if (len == 0) return Qnil;
if (offset < 0) {
offset += len;
@@ -118,22 +116,6 @@ ARY_SHARED_ROOT_REFCNT(VALUE ary)
return RARRAY(ary)->as.heap.aux.capa;
}
-static inline void
-RARY_TRANSIENT_SET(VALUE ary)
-{
-#if USE_TRANSIENT_HEAP
- FL_SET_RAW(ary, RARRAY_TRANSIENT_FLAG);
-#endif
-}
-
-static inline void
-RARY_TRANSIENT_UNSET(VALUE ary)
-{
-#if USE_TRANSIENT_HEAP
- FL_UNSET_RAW(ary, RARRAY_TRANSIENT_FLAG);
-#endif
-}
-
#undef rb_ary_new_from_args
#if RBIMPL_HAS_WARNING("-Wgnu-zero-variadic-macro-arguments")
# /* Skip it; clang -pedantic doesn't like the following */
@@ -154,9 +136,16 @@ RBIMPL_ATTR_ARTIFICIAL()
static inline VALUE
RARRAY_AREF(VALUE ary, long i)
{
+ VALUE val;
RBIMPL_ASSERT_TYPE(ary, RUBY_T_ARRAY);
- return RARRAY_CONST_PTR_TRANSIENT(ary)[i];
+ RBIMPL_WARNING_PUSH();
+#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ == 13
+ RBIMPL_WARNING_IGNORED(-Warray-bounds);
+#endif
+ val = RARRAY_CONST_PTR(ary)[i];
+ RBIMPL_WARNING_POP();
+ return val;
}
#endif /* INTERNAL_ARRAY_H */
diff --git a/internal/bits.h b/internal/bits.h
index 6248e4cfa9..1fe98fa430 100644
--- a/internal/bits.h
+++ b/internal/bits.h
@@ -118,12 +118,16 @@
MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, FIXNUM_MIN, FIXNUM_MAX)
#endif
-#ifdef MUL_OVERFLOW_P
+#if defined(MUL_OVERFLOW_P) && defined(USE___BUILTIN_MUL_OVERFLOW_LONG_LONG)
# define MUL_OVERFLOW_LONG_LONG_P(a, b) MUL_OVERFLOW_P(a, b)
+#else
+# define MUL_OVERFLOW_LONG_LONG_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, LLONG_MIN, LLONG_MAX)
+#endif
+
+#ifdef MUL_OVERFLOW_P
# define MUL_OVERFLOW_LONG_P(a, b) MUL_OVERFLOW_P(a, b)
# define MUL_OVERFLOW_INT_P(a, b) MUL_OVERFLOW_P(a, b)
#else
-# define MUL_OVERFLOW_LONG_LONG_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, LLONG_MIN, LLONG_MAX)
# define MUL_OVERFLOW_LONG_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, LONG_MIN, LONG_MAX)
# define MUL_OVERFLOW_INT_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, INT_MIN, INT_MAX)
#endif
@@ -394,9 +398,9 @@ rb_popcount32(uint32_t x)
#else
x = (x & 0x55555555) + (x >> 1 & 0x55555555);
x = (x & 0x33333333) + (x >> 2 & 0x33333333);
- x = (x & 0x0f0f0f0f) + (x >> 4 & 0x0f0f0f0f);
- x = (x & 0x001f001f) + (x >> 8 & 0x001f001f);
- x = (x & 0x0000003f) + (x >>16 & 0x0000003f);
+ x = (x & 0x07070707) + (x >> 4 & 0x07070707);
+ x = (x & 0x000f000f) + (x >> 8 & 0x000f000f);
+ x = (x & 0x0000001f) + (x >>16 & 0x0000001f);
return (unsigned int)x;
#endif
@@ -424,9 +428,9 @@ rb_popcount64(uint64_t x)
x = (x & 0x5555555555555555) + (x >> 1 & 0x5555555555555555);
x = (x & 0x3333333333333333) + (x >> 2 & 0x3333333333333333);
x = (x & 0x0707070707070707) + (x >> 4 & 0x0707070707070707);
- x = (x & 0x001f001f001f001f) + (x >> 8 & 0x001f001f001f001f);
- x = (x & 0x0000003f0000003f) + (x >>16 & 0x0000003f0000003f);
- x = (x & 0x000000000000007f) + (x >>32 & 0x000000000000007f);
+ x = (x & 0x000f000f000f000f) + (x >> 8 & 0x000f000f000f000f);
+ x = (x & 0x0000001f0000001f) + (x >>16 & 0x0000001f0000001f);
+ x = (x & 0x000000000000003f) + (x >>32 & 0x000000000000003f);
return (unsigned int)x;
#endif
diff --git a/internal/class.h b/internal/class.h
index d76da84bd1..8a6c956233 100644
--- a/internal/class.h
+++ b/internal/class.h
@@ -19,6 +19,7 @@
#include "shape.h"
#include "ruby_assert.h"
#include "vm_core.h"
+#include "vm_sync.h"
#include "method.h" /* for rb_cref_t */
#ifdef RCLASS_SUPER
@@ -43,7 +44,7 @@ struct rb_classext_struct {
VALUE *iv_ptr;
struct rb_id_table *const_tbl;
struct rb_id_table *callable_m_tbl;
- struct rb_id_table *cc_tbl; /* ID -> [[ci, cc1], cc2, ...] */
+ struct rb_id_table *cc_tbl; /* ID -> [[ci1, cc1], [ci2, cc2] ...] */
struct rb_id_table *cvc_tbl;
size_t superclass_depth;
VALUE *superclasses;
@@ -85,7 +86,12 @@ struct RClass {
// Assert that classes can be embedded in size_pools[2] (which has 160B slot size)
STATIC_ASSERT(sizeof_rb_classext_t, sizeof(struct RClass) + sizeof(rb_classext_t) <= 4 * RVALUE_SIZE);
-#define RCLASS_EXT(c) ((rb_classext_t *)((char *)(c) + sizeof(struct RClass)))
+struct RClass_and_rb_classext_t {
+ struct RClass rclass;
+ rb_classext_t classext;
+};
+
+#define RCLASS_EXT(c) (&((struct RClass_and_rb_classext_t*)(c))->classext)
#define RCLASS_CONST_TBL(c) (RCLASS_EXT(c)->const_tbl)
#define RCLASS_M_TBL(c) (RCLASS(c)->m_tbl)
#define RCLASS_IVPTR(c) (RCLASS_EXT(c)->iv_ptr)
@@ -102,10 +108,56 @@ STATIC_ASSERT(sizeof_rb_classext_t, sizeof(struct RClass) + sizeof(rb_classext_t
#define RCLASS_SUPERCLASSES(c) (RCLASS_EXT(c)->superclasses)
#define RCLASS_ATTACHED_OBJECT(c) (RCLASS_EXT(c)->as.singleton_class.attached_object)
+#define RCLASS_IS_ROOT FL_USER0
#define RICLASS_IS_ORIGIN FL_USER0
#define RCLASS_SUPERCLASSES_INCLUDE_SELF FL_USER2
#define RICLASS_ORIGIN_SHARED_MTBL FL_USER3
+static inline st_table *
+RCLASS_IV_HASH(VALUE obj)
+{
+ RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
+ RUBY_ASSERT(rb_shape_obj_too_complex(obj));
+ return (st_table *)RCLASS_IVPTR(obj);
+}
+
+static inline void
+RCLASS_SET_IV_HASH(VALUE obj, const st_table *tbl)
+{
+ RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
+ RUBY_ASSERT(rb_shape_obj_too_complex(obj));
+ RCLASS_IVPTR(obj) = (VALUE *)tbl;
+}
+
+static inline uint32_t
+RCLASS_IV_COUNT(VALUE obj)
+{
+ RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
+ if (rb_shape_obj_too_complex(obj)) {
+ uint32_t count;
+
+ // "Too complex" classes could have their IV hash mutated in
+ // parallel, so lets lock around getting the hash size.
+ RB_VM_LOCK_ENTER();
+ {
+ count = (uint32_t)rb_st_table_size(RCLASS_IV_HASH(obj));
+ }
+ RB_VM_LOCK_LEAVE();
+
+ return count;
+ }
+ else {
+ return rb_shape_get_shape_by_id(RCLASS_SHAPE_ID(obj))->next_iv_index;
+ }
+}
+
+static inline void
+RCLASS_SET_M_TBL(VALUE klass, struct rb_id_table *table)
+{
+ RUBY_ASSERT(!RB_OBJ_PROMOTED(klass));
+ RCLASS_M_TBL(klass) = table;
+}
+
/* class.c */
void rb_class_subclass_add(VALUE super, VALUE klass);
void rb_class_remove_from_super_subclasses(VALUE);
@@ -124,6 +176,7 @@ void rb_class_foreach_subclass(VALUE klass, void (*f)(VALUE, VALUE), VALUE);
void rb_class_detach_subclasses(VALUE);
void rb_class_detach_module_subclasses(VALUE);
void rb_class_remove_from_module_subclasses(VALUE);
+VALUE rb_define_class_id_under_no_pin(VALUE outer, ID id, VALUE super);
VALUE rb_obj_methods(int argc, const VALUE *argv, VALUE obj);
VALUE rb_obj_protected_methods(int argc, const VALUE *argv, VALUE obj);
VALUE rb_obj_private_methods(int argc, const VALUE *argv, VALUE obj);
@@ -143,11 +196,17 @@ static inline void RCLASS_SET_INCLUDER(VALUE iclass, VALUE klass);
VALUE rb_class_inherited(VALUE, VALUE);
VALUE rb_keyword_error_new(const char *, VALUE);
+static inline bool
+RCLASS_SINGLETON_P(VALUE klass)
+{
+ return RB_TYPE_P(klass, T_CLASS) && FL_TEST_RAW(klass, FL_SINGLETON);
+}
+
static inline rb_alloc_func_t
RCLASS_ALLOCATOR(VALUE klass)
{
- if (FL_TEST_RAW(klass, FL_SINGLETON)) {
- return NULL;
+ if (RCLASS_SINGLETON_P(klass)) {
+ return 0;
}
return RCLASS_EXT(klass)->as.class.allocator;
}
@@ -155,7 +214,7 @@ RCLASS_ALLOCATOR(VALUE klass)
static inline void
RCLASS_SET_ALLOCATOR(VALUE klass, rb_alloc_func_t allocator)
{
- assert(!FL_TEST(klass, FL_SINGLETON));
+ assert(!RCLASS_SINGLETON_P(klass));
RCLASS_EXT(klass)->as.class.allocator = allocator;
}
@@ -215,8 +274,7 @@ RCLASS_SET_CLASSPATH(VALUE klass, VALUE classpath, bool permanent)
static inline VALUE
RCLASS_SET_ATTACHED_OBJECT(VALUE klass, VALUE attached_object)
{
- assert(BUILTIN_TYPE(klass) == T_CLASS);
- assert(FL_TEST_RAW(klass, FL_SINGLETON));
+ assert(RCLASS_SINGLETON_P(klass));
RB_OBJ_WRITE(klass, &RCLASS_EXT(klass)->as.singleton_class.attached_object, attached_object);
return attached_object;
diff --git a/internal/cmdlineopt.h b/internal/cmdlineopt.h
index 483a5d4f75..e79b993345 100644
--- a/internal/cmdlineopt.h
+++ b/internal/cmdlineopt.h
@@ -23,11 +23,15 @@ typedef struct ruby_cmdline_options {
ruby_features_t features;
ruby_features_t warn;
unsigned int dump;
+ long backtrace_length_limit;
#if USE_RJIT
struct rb_rjit_options rjit;
#endif
- int sflag, xflag;
+ const char *crash_report;
+
+ signed int sflag: 2;
+ unsigned int xflag: 1;
unsigned int warning: 1;
unsigned int verbose: 1;
unsigned int do_loop: 1;
diff --git a/internal/compile.h b/internal/compile.h
index eebb7605cd..2ece5396f6 100644
--- a/internal/compile.h
+++ b/internal/compile.h
@@ -17,7 +17,6 @@ struct rb_iseq_struct; /* in vm_core.h */
/* compile.c */
int rb_dvar_defined(ID, const struct rb_iseq_struct *);
int rb_local_defined(ID, const struct rb_iseq_struct *);
-bool rb_insns_leaf_p(int i);
int rb_insn_len(VALUE insn);
const char *rb_insns_name(int i);
VALUE rb_insns_name_array(void);
diff --git a/internal/cont.h b/internal/cont.h
index c3b091668a..3c2528a02a 100644
--- a/internal/cont.h
+++ b/internal/cont.h
@@ -22,6 +22,9 @@ void rb_jit_cont_init(void);
void rb_jit_cont_each_iseq(rb_iseq_callback callback, void *data);
void rb_jit_cont_finish(void);
+/* vm.c */
+void rb_free_shared_fiber_pool(void);
+
// Copy locals from the current execution to the specified fiber.
VALUE rb_fiber_inherit_storage(struct rb_execution_context_struct *ec, struct rb_fiber_struct *fiber);
diff --git a/internal/encoding.h b/internal/encoding.h
index a3b81bd388..fe9ea10ec4 100644
--- a/internal/encoding.h
+++ b/internal/encoding.h
@@ -18,6 +18,7 @@
/* encoding.c */
ID rb_id_encoding(void);
+const char * rb_enc_inspect_name(rb_encoding *enc);
rb_encoding *rb_enc_get_from_index(int index);
rb_encoding *rb_enc_check_str(VALUE str1, VALUE str2);
int rb_encdb_replicate(const char *alias, const char *orig);
@@ -29,4 +30,7 @@ void rb_enc_set_base(const char *name, const char *orig);
int rb_enc_set_dummy(int index);
PUREFUNC(int rb_data_is_encoding(VALUE obj));
+/* vm.c */
+void rb_free_global_enc_table(void);
+
#endif /* INTERNAL_ENCODING_H */
diff --git a/internal/error.h b/internal/error.h
index 5fee468929..7e41f134d7 100644
--- a/internal/error.h
+++ b/internal/error.h
@@ -169,6 +169,9 @@ VALUE rb_syserr_new_path_in(const char *func_name, int n, VALUE path);
#endif
RUBY_SYMBOL_EXPORT_END
+/* vm.c */
+void rb_free_warning(void);
+
static inline void
rb_raise_cstr_i(VALUE etype, VALUE mesg)
{
diff --git a/internal/eval.h b/internal/eval.h
index 73bb656d96..e594d8516d 100644
--- a/internal/eval.h
+++ b/internal/eval.h
@@ -21,6 +21,7 @@ extern ID ruby_static_id_status;
VALUE rb_refinement_module_get_refined_class(VALUE module);
void rb_class_modify_check(VALUE);
NORETURN(VALUE rb_f_raise(int argc, VALUE *argv));
+VALUE rb_top_main_class(const char *method);
/* eval_error.c */
VALUE rb_get_backtrace(VALUE info);
diff --git a/internal/gc.h b/internal/gc.h
index d19b09f6fc..ecc3f11b2c 100644
--- a/internal/gc.h
+++ b/internal/gc.h
@@ -16,6 +16,10 @@
#include "ruby/ruby.h" /* for rb_event_flag_t */
#include "vm_core.h" /* for GET_EC() */
+#ifndef USE_SHARED_GC
+# define USE_SHARED_GC 0
+#endif
+
#if defined(__x86_64__) && !defined(_ILP32) && defined(__GNUC__)
#define SET_MACHINE_STACK_END(p) __asm__ __volatile__ ("movq\t%%rsp, %0" : "=r" (*(p)))
#elif defined(__i386) && defined(__GNUC__)
@@ -79,14 +83,6 @@ rb_gc_debug_body(const char *mode, const char *msg, int st, void *ptr)
#define RUBY_GC_INFO if(0)printf
#endif
-#define RUBY_MARK_MOVABLE_UNLESS_NULL(ptr) do { \
- VALUE markobj = (ptr); \
- if (RTEST(markobj)) {rb_gc_mark_movable(markobj);} \
-} while (0)
-#define RUBY_MARK_UNLESS_NULL(ptr) do { \
- VALUE markobj = (ptr); \
- if (RTEST(markobj)) {rb_gc_mark(markobj);} \
-} while (0)
#define RUBY_FREE_UNLESS_NULL(ptr) if(ptr){ruby_xfree(ptr);(ptr)=NULL;}
#if STACK_GROW_DIRECTION > 0
@@ -123,30 +119,14 @@ int ruby_get_stack_grow_direction(volatile VALUE *addr);
const char *rb_obj_info(VALUE obj);
const char *rb_raw_obj_info(char *const buff, const size_t buff_size, VALUE obj);
-size_t rb_size_pool_slot_size(unsigned char pool_id);
-
struct rb_execution_context_struct; /* in vm_core.h */
struct rb_objspace; /* in vm_core.h */
-#ifdef NEWOBJ_OF
-# undef NEWOBJ_OF
-# undef RB_NEWOBJ_OF
-#endif
-
-#define NEWOBJ_OF_0(var, T, c, f, s, ec) \
- T *(var) = (T *)(((f) & FL_WB_PROTECTED) ? \
- rb_wb_protected_newobj_of(GET_EC(), (c), (f) & ~FL_WB_PROTECTED, s) : \
- rb_wb_unprotected_newobj_of((c), (f), s))
-#define NEWOBJ_OF_ec(var, T, c, f, s, ec) \
+#define NEWOBJ_OF(var, T, c, f, s, ec) \
T *(var) = (T *)(((f) & FL_WB_PROTECTED) ? \
- rb_wb_protected_newobj_of((ec), (c), (f) & ~FL_WB_PROTECTED, s) : \
+ rb_wb_protected_newobj_of((ec ? ec : GET_EC()), (c), (f) & ~FL_WB_PROTECTED, s) : \
rb_wb_unprotected_newobj_of((c), (f), s))
-#define NEWOBJ_OF(var, T, c, f, s, ec) \
- NEWOBJ_OF_HELPER(ec)(var, T, c, f, s, ec)
-
-#define NEWOBJ_OF_HELPER(ec) NEWOBJ_OF_ ## ec
-
#define RB_OBJ_GC_FLAGS_MAX 6 /* used in ext/objspace */
#ifndef USE_UNALIGNED_MEMBER_ACCESS
@@ -189,6 +169,17 @@ struct rb_objspace; /* in vm_core.h */
# define SIZE_POOL_COUNT 5
#endif
+/* Used in places that could malloc during, which can cause the GC to run. We
+ * need to temporarily disable the GC to allow the malloc to happen.
+ * Allocating memory during GC is a bad idea, so use this only when absolutely
+ * necessary. */
+#define DURING_GC_COULD_MALLOC_REGION_START() \
+ assert(rb_during_gc()); \
+ VALUE _already_disabled = rb_gc_disable_no_rest()
+
+#define DURING_GC_COULD_MALLOC_REGION_END() \
+ if (_already_disabled == Qfalse) rb_gc_enable()
+
typedef struct ractor_newobj_size_pool_cache {
struct RVALUE *freelist;
struct heap_page *using_page;
@@ -200,21 +191,19 @@ typedef struct ractor_newobj_cache {
} rb_ractor_newobj_cache_t;
/* gc.c */
-extern VALUE *ruby_initial_gc_stress_ptr;
extern int ruby_disable_gc;
RUBY_ATTR_MALLOC void *ruby_mimmalloc(size_t size);
+RUBY_ATTR_MALLOC void *ruby_mimcalloc(size_t num, size_t size);
void ruby_mimfree(void *ptr);
+void rb_gc_prepare_heap(void);
void rb_objspace_set_event_hook(const rb_event_flag_t event);
VALUE rb_objspace_gc_enable(struct rb_objspace *);
VALUE rb_objspace_gc_disable(struct rb_objspace *);
void ruby_gc_set_params(void);
-void rb_copy_wb_protected_attribute(VALUE dest, VALUE obj);
-#if __has_attribute(alloc_align)
-__attribute__((__alloc_align__(1)))
-#endif
-RUBY_ATTR_MALLOC void *rb_aligned_malloc(size_t, size_t) RUBY_ATTR_ALLOC_SIZE((2));
+void rb_gc_copy_attributes(VALUE dest, VALUE obj);
size_t rb_size_mul_or_raise(size_t, size_t, VALUE); /* used in compile.c */
size_t rb_size_mul_add_or_raise(size_t, size_t, size_t, VALUE); /* used in iseq.h */
+size_t rb_malloc_grow_capa(size_t current_capacity, size_t type_size);
RUBY_ATTR_MALLOC void *rb_xmalloc_mul_add(size_t, size_t, size_t);
RUBY_ATTR_MALLOC void *rb_xcalloc_mul_add(size_t, size_t, size_t);
void *rb_xrealloc_mul_add(const void *, size_t, size_t, size_t);
@@ -223,17 +212,22 @@ RUBY_ATTR_MALLOC void *rb_xcalloc_mul_add_mul(size_t, size_t, size_t, size_t);
static inline void *ruby_sized_xrealloc_inlined(void *ptr, size_t new_size, size_t old_size) RUBY_ATTR_RETURNS_NONNULL RUBY_ATTR_ALLOC_SIZE((2));
static inline void *ruby_sized_xrealloc2_inlined(void *ptr, size_t new_count, size_t elemsiz, size_t old_count) RUBY_ATTR_RETURNS_NONNULL RUBY_ATTR_ALLOC_SIZE((2, 3));
static inline void ruby_sized_xfree_inlined(void *ptr, size_t size);
-VALUE rb_class_allocate_instance(VALUE klass);
void rb_gc_ractor_newobj_cache_clear(rb_ractor_newobj_cache_t *newobj_cache);
-size_t rb_gc_obj_slot_size(VALUE obj);
bool rb_gc_size_allocatable_p(size_t size);
+size_t *rb_gc_size_pool_sizes(void);
+size_t rb_gc_size_pool_id_for_size(size_t size);
int rb_objspace_garbage_object_p(VALUE obj);
-bool rb_gc_is_ptr_to_obj(void *ptr);
-VALUE rb_gc_id2ref_obj_tbl(VALUE objid);
-VALUE rb_define_finalizer_no_check(VALUE obj, VALUE block);
+bool rb_gc_is_ptr_to_obj(const void *ptr);
void rb_gc_mark_and_move(VALUE *ptr);
+void rb_gc_mark_weak(VALUE *ptr);
+void rb_gc_remove_weak(VALUE parent_obj, VALUE *ptr);
+
+void rb_gc_ref_update_table_values_only(st_table *tbl);
+
+void rb_gc_initial_stress_set(VALUE flag);
+
#define rb_gc_mark_and_move_ptr(ptr) do { \
VALUE _obj = (VALUE)*(ptr); \
rb_gc_mark_and_move(&_obj); \
@@ -242,21 +236,15 @@ void rb_gc_mark_and_move(VALUE *ptr);
RUBY_SYMBOL_EXPORT_BEGIN
/* exports for objspace module */
-size_t rb_objspace_data_type_memsize(VALUE obj);
void rb_objspace_reachable_objects_from(VALUE obj, void (func)(VALUE, void *), void *data);
void rb_objspace_reachable_objects_from_root(void (func)(const char *category, VALUE, void *), void *data);
int rb_objspace_markable_object_p(VALUE obj);
int rb_objspace_internal_object_p(VALUE obj);
-int rb_objspace_marked_object_p(VALUE obj);
void rb_objspace_each_objects(
int (*callback)(void *start, void *end, size_t stride, void *data),
void *data);
-void rb_objspace_each_objects_without_setup(
- int (*callback)(void *, void *, size_t, void *),
- void *data);
-
size_t rb_gc_obj_slot_size(VALUE obj);
VALUE rb_gc_disable_no_rest(void);
@@ -271,6 +259,7 @@ void rb_gc_verify_internal_consistency(void);
size_t rb_obj_gc_flags(VALUE, ID[], size_t);
void rb_gc_mark_values(long n, const VALUE *values);
void rb_gc_mark_vm_stack_values(long n, const VALUE *values);
+void rb_gc_update_values(long n, VALUE *values);
void *ruby_sized_xrealloc(void *ptr, size_t new_size, size_t old_size) RUBY_ATTR_RETURNS_NONNULL RUBY_ATTR_ALLOC_SIZE((2));
void *ruby_sized_xrealloc2(void *ptr, size_t new_count, size_t element_size, size_t old_count) RUBY_ATTR_RETURNS_NONNULL RUBY_ATTR_ALLOC_SIZE((2, 3));
void ruby_sized_xfree(void *x, size_t size);
@@ -302,6 +291,12 @@ ruby_sized_xfree_inlined(void *ptr, size_t size)
# define SIZED_REALLOC_N(x, y, z, w) REALLOC_N(x, y, z)
+static inline void *
+ruby_sized_realloc_n(void *ptr, size_t new_count, size_t element_size, size_t old_count)
+{
+ return ruby_xrealloc2(ptr, new_count, element_size);
+}
+
#else
static inline void *
@@ -325,6 +320,12 @@ ruby_sized_xfree_inlined(void *ptr, size_t size)
# define SIZED_REALLOC_N(v, T, m, n) \
((v) = (T *)ruby_sized_xrealloc2((void *)(v), (m), sizeof(T), (n)))
+static inline void *
+ruby_sized_realloc_n(void *ptr, size_t new_count, size_t element_size, size_t old_count)
+{
+ return ruby_sized_xrealloc2(ptr, new_count, element_size, old_count);
+}
+
#endif /* HAVE_MALLOC_USABLE_SIZE */
#define ruby_sized_xrealloc ruby_sized_xrealloc_inlined
diff --git a/internal/hash.h b/internal/hash.h
index 3874abeaa5..fe859cb716 100644
--- a/internal/hash.h
+++ b/internal/hash.h
@@ -28,10 +28,6 @@ enum ruby_rhash_flags {
RHASH_AR_TABLE_BOUND_MASK = (FL_USER8|FL_USER9|FL_USER10|FL_USER11), /* FL 8..11 */
RHASH_AR_TABLE_BOUND_SHIFT = (FL_USHIFT+8),
-#if USE_TRANSIENT_HEAP
- RHASH_TRANSIENT_FLAG = FL_USER12, /* FL 12 */
-#endif
-
// we can not put it in "enum" because it can exceed "int" range.
#define RHASH_LEV_MASK (FL_USER13 | FL_USER14 | FL_USER15 | /* FL 13..19 */ \
FL_USER16 | FL_USER17 | FL_USER18 | FL_USER19)
@@ -91,6 +87,7 @@ int rb_hash_stlike_delete(VALUE hash, st_data_t *pkey, st_data_t *pval);
int rb_hash_stlike_foreach_with_replace(VALUE hash, st_foreach_check_callback_func *func, st_update_callback_func *replace, st_data_t arg);
int rb_hash_stlike_update(VALUE hash, st_data_t key, st_update_callback_func *func, st_data_t arg);
VALUE rb_ident_hash_new_with_size(st_index_t size);
+void rb_hash_free(VALUE hash);
static inline unsigned RHASH_AR_TABLE_SIZE_RAW(VALUE h);
static inline VALUE RHASH_IFNONE(VALUE h);
@@ -128,12 +125,14 @@ RHASH_AR_TABLE_P(VALUE h)
return ! FL_TEST_RAW(h, RHASH_ST_TABLE_FLAG);
}
+RBIMPL_ATTR_RETURNS_NONNULL()
static inline struct ar_table_struct *
RHASH_AR_TABLE(VALUE h)
{
return (struct ar_table_struct *)((uintptr_t)h + sizeof(struct RHash));
}
+RBIMPL_ATTR_RETURNS_NONNULL()
static inline st_table *
RHASH_ST_TABLE(VALUE h)
{
diff --git a/internal/imemo.h b/internal/imemo.h
index 57a705ce77..36c0776987 100644
--- a/internal/imemo.h
+++ b/internal/imemo.h
@@ -39,7 +39,7 @@ enum imemo_type {
imemo_ment = 6,
imemo_iseq = 7,
imemo_tmpbuf = 8,
- imemo_ast = 9,
+ imemo_ast = 9, // Obsolete due to the universal parser
imemo_parser_strterm = 10,
imemo_callinfo = 11,
imemo_callcache = 12,
@@ -113,11 +113,12 @@ struct MEMO {
} u3;
};
+#define IMEMO_NEW(T, type, v0) ((T *)rb_imemo_new((type), (v0)))
+
/* ment is in method.h */
#define THROW_DATA_P(err) imemo_throw_data_p((VALUE)err)
#define MEMO_CAST(m) ((struct MEMO *)(m))
-#define MEMO_NEW(a, b, c) ((struct MEMO *)rb_imemo_new(imemo_memo, (VALUE)(a), (VALUE)(b), (VALUE)(c), 0))
#define MEMO_FOR(type, value) ((type *)RARRAY_PTR(value))
#define NEW_MEMO_FOR(type, value) \
((value) = rb_ary_hidden_new_fill(type_roomof(type, VALUE)), MEMO_FOR(type, value))
@@ -126,10 +127,11 @@ struct MEMO {
rb_ary_set_len((value), offsetof(type, member) / sizeof(VALUE)), \
MEMO_FOR(type, value))
+#ifndef RUBY_RUBYPARSER_H
typedef struct rb_imemo_tmpbuf_struct rb_imemo_tmpbuf_t;
+#endif
rb_imemo_tmpbuf_t *rb_imemo_tmpbuf_parser_heap(void *buf, rb_imemo_tmpbuf_t *old_heap, size_t cnt);
struct vm_ifunc *rb_vm_ifunc_new(rb_block_call_func_t func, const void *data, int min_argc, int max_argc);
-void rb_strterm_mark(VALUE obj);
static inline enum imemo_type imemo_type(VALUE imemo);
static inline int imemo_type_p(VALUE imemo, enum imemo_type imemo_type);
static inline bool imemo_throw_data_p(VALUE imemo);
@@ -141,16 +143,33 @@ static inline VALUE rb_imemo_tmpbuf_auto_free_pointer_new_from_an_RString(VALUE
static inline void MEMO_V1_SET(struct MEMO *m, VALUE v);
static inline void MEMO_V2_SET(struct MEMO *m, VALUE v);
+size_t rb_imemo_memsize(VALUE obj);
+void rb_cc_table_mark(VALUE klass);
+void rb_imemo_mark_and_move(VALUE obj, bool reference_updating);
+void rb_cc_table_free(VALUE klass);
+void rb_imemo_free(VALUE obj);
+
RUBY_SYMBOL_EXPORT_BEGIN
#if IMEMO_DEBUG
-VALUE rb_imemo_new_debug(enum imemo_type type, VALUE v1, VALUE v2, VALUE v3, VALUE v0, const char *file, int line);
+VALUE rb_imemo_new_debug(enum imemo_type type, VALUE v0, const char *file, int line);
#define rb_imemo_new(type, v1, v2, v3, v0) rb_imemo_new_debug(type, v1, v2, v3, v0, __FILE__, __LINE__)
#else
-VALUE rb_imemo_new(enum imemo_type type, VALUE v1, VALUE v2, VALUE v3, VALUE v0);
+VALUE rb_imemo_new(enum imemo_type type, VALUE v0);
#endif
const char *rb_imemo_name(enum imemo_type type);
RUBY_SYMBOL_EXPORT_END
+static inline struct MEMO *
+MEMO_NEW(VALUE a, VALUE b, VALUE c)
+{
+ struct MEMO *memo = IMEMO_NEW(struct MEMO, imemo_memo, 0);
+ *((VALUE *)&memo->v1) = a;
+ *((VALUE *)&memo->v2) = b;
+ *((VALUE *)&memo->u3.value) = c;
+
+ return memo;
+}
+
static inline enum imemo_type
imemo_type(VALUE imemo)
{
@@ -172,7 +191,7 @@ imemo_type_p(VALUE imemo, enum imemo_type imemo_type)
}
}
-#define IMEMO_TYPE_P(v, t) imemo_type_p((VALUE)v, t)
+#define IMEMO_TYPE_P(v, t) imemo_type_p((VALUE)(v), t)
static inline bool
imemo_throw_data_p(VALUE imemo)
@@ -189,7 +208,7 @@ rb_vm_ifunc_proc_new(rb_block_call_func_t func, const void *data)
static inline VALUE
rb_imemo_tmpbuf_auto_free_pointer(void)
{
- return rb_imemo_new(imemo_tmpbuf, 0, 0, 0, 0);
+ return rb_imemo_new(imemo_tmpbuf, 0);
}
static inline void *
@@ -214,7 +233,7 @@ rb_imemo_tmpbuf_auto_free_pointer_new_from_an_RString(VALUE str)
void *dst;
size_t len;
- SafeStringValue(str);
+ StringValue(str);
/* create tmpbuf to keep the pointer before xmalloc */
imemo = rb_imemo_tmpbuf_auto_free_pointer();
tmpbuf = (rb_imemo_tmpbuf_t *)imemo;
diff --git a/internal/inits.h b/internal/inits.h
index 03e180f77b..03de289dd4 100644
--- a/internal/inits.h
+++ b/internal/inits.h
@@ -19,9 +19,6 @@ void Init_ext(void);
/* file.c */
void Init_File(void);
-/* gc.c */
-void Init_heap(void);
-
/* localeinit.c */
int Init_enc_set_filesystem_encoding(void);
diff --git a/internal/io.h b/internal/io.h
index b5f15499d7..1891248a19 100644
--- a/internal/io.h
+++ b/internal/io.h
@@ -9,8 +9,110 @@
* @brief Internal header for IO.
*/
#include "ruby/ruby.h" /* for VALUE */
+
+#define HAVE_RB_IO_T
+struct rb_io;
+
#include "ruby/io.h" /* for rb_io_t */
+#define IO_WITHOUT_GVL(func, arg) rb_thread_call_without_gvl(func, arg, RUBY_UBF_IO, 0)
+#define IO_WITHOUT_GVL_INT(func, arg) (int)(VALUE)IO_WITHOUT_GVL(func, arg)
+
+/** Ruby's IO, metadata and buffers. */
+struct rb_io {
+
+ /** The IO's Ruby level counterpart. */
+ VALUE self;
+
+ /** stdio ptr for read/write, if available. */
+ FILE *stdio_file;
+
+ /** file descriptor. */
+ int fd;
+
+ /** mode flags: FMODE_XXXs */
+ int mode;
+
+ /** child's pid (for pipes) */
+ rb_pid_t pid;
+
+ /** number of lines read */
+ int lineno;
+
+ /** pathname for file */
+ VALUE pathv;
+
+ /** finalize proc */
+ void (*finalize)(struct rb_io*,int);
+
+ /** Write buffer. */
+ rb_io_buffer_t wbuf;
+
+ /**
+ * (Byte) read buffer. Note also that there is a field called
+ * ::rb_io_t::cbuf, which also concerns read IO.
+ */
+ rb_io_buffer_t rbuf;
+
+ /**
+ * Duplex IO object, if set.
+ *
+ * @see rb_io_set_write_io()
+ */
+ VALUE tied_io_for_writing;
+
+ struct rb_io_encoding encs; /**< Decomposed encoding flags. */
+
+ /** Encoding converter used when reading from this IO. */
+ rb_econv_t *readconv;
+
+ /**
+ * rb_io_ungetc() destination. This buffer is read before checking
+ * ::rb_io_t::rbuf
+ */
+ rb_io_buffer_t cbuf;
+
+ /** Encoding converter used when writing to this IO. */
+ rb_econv_t *writeconv;
+
+ /**
+ * This is, when set, an instance of ::rb_cString which holds the "common"
+ * encoding. Write conversion can convert strings twice... In case
+ * conversion from encoding X to encoding Y does not exist, Ruby finds an
+ * encoding Z that bridges the two, so that X to Z to Y conversion happens.
+ */
+ VALUE writeconv_asciicompat;
+
+ /** Whether ::rb_io_t::writeconv is already set up. */
+ int writeconv_initialized;
+
+ /**
+ * Value of ::rb_io_t::rb_io_enc_t::ecflags stored right before
+ * initialising ::rb_io_t::writeconv.
+ */
+ int writeconv_pre_ecflags;
+
+ /**
+ * Value of ::rb_io_t::rb_io_enc_t::ecopts stored right before initialising
+ * ::rb_io_t::writeconv.
+ */
+ VALUE writeconv_pre_ecopts;
+
+ /**
+ * This is a Ruby level mutex. It avoids multiple threads to write to an
+ * IO at once; helps for instance rb_io_puts() to ensure newlines right
+ * next to its arguments.
+ *
+ * This of course doesn't help inter-process IO interleaves, though.
+ */
+ VALUE write_lock;
+
+ /**
+ * The timeout associated with this IO when performing blocking operations.
+ */
+ VALUE timeout;
+};
+
/* io.c */
void ruby_set_inplace_mode(const char *);
void rb_stdio_set_default_encoding(void);
diff --git a/internal/missing.h b/internal/missing.h
index c0992a151a..6ca508c8f9 100644
--- a/internal/missing.h
+++ b/internal/missing.h
@@ -13,6 +13,7 @@
/* missing/setproctitle.c */
#ifndef HAVE_SETPROCTITLE
extern void ruby_init_setproctitle(int argc, char *argv[]);
+extern void ruby_free_proctitle(void);
#endif
#endif /* INTERNAL_MISSING_H */
diff --git a/internal/numeric.h b/internal/numeric.h
index f7b8d0ad2d..6406cfc2fa 100644
--- a/internal/numeric.h
+++ b/internal/numeric.h
@@ -86,6 +86,7 @@ VALUE rb_int_equal(VALUE x, VALUE y);
VALUE rb_int_divmod(VALUE x, VALUE y);
VALUE rb_int_and(VALUE x, VALUE y);
VALUE rb_int_lshift(VALUE x, VALUE y);
+VALUE rb_int_rshift(VALUE x, VALUE y);
VALUE rb_int_div(VALUE x, VALUE y);
int rb_int_positive_p(VALUE num);
int rb_int_negative_p(VALUE num);
@@ -158,7 +159,7 @@ rb_num_compare_with_zero(VALUE num, ID mid)
{
VALUE zero = INT2FIX(0);
VALUE r = rb_check_funcall(num, mid, 1, &zero);
- if (r == Qundef) {
+ if (RB_UNDEF_P(r)) {
rb_cmperr(num, zero);
}
return r;
diff --git a/internal/object.h b/internal/object.h
index 58e989562a..92ad37fdc8 100644
--- a/internal/object.h
+++ b/internal/object.h
@@ -11,10 +11,14 @@
#include "ruby/ruby.h" /* for VALUE */
/* object.c */
+size_t rb_obj_embedded_size(uint32_t numiv);
+VALUE rb_class_allocate_instance(VALUE klass);
VALUE rb_class_search_ancestor(VALUE klass, VALUE super);
NORETURN(void rb_undefined_alloc(VALUE klass));
double rb_num_to_dbl(VALUE val);
VALUE rb_obj_dig(int argc, VALUE *argv, VALUE self, VALUE notfound);
+VALUE rb_obj_clone_setup(VALUE obj, VALUE clone, VALUE kwfreeze);
+VALUE rb_obj_dup_setup(VALUE obj, VALUE dup);
VALUE rb_immutable_obj_clone(int, VALUE *, VALUE);
VALUE rb_check_convert_type_with_id(VALUE,int,const char*,ID);
int rb_bool_expected(VALUE, const char *, int raise);
diff --git a/internal/parse.h b/internal/parse.h
index f242c384ad..5d68993017 100644
--- a/internal/parse.h
+++ b/internal/parse.h
@@ -8,18 +8,110 @@
* file COPYING are met. Consult the file for details.
* @brief Internal header for the parser.
*/
-#include "ruby/ruby.h" /* for VALUE */
+#include <limits.h>
+#include "rubyparser.h"
+#include "internal/static_assert.h"
+
+#ifdef UNIVERSAL_PARSER
+#define rb_encoding void
+#endif
+
struct rb_iseq_struct; /* in vm_core.h */
+/* structs for managing terminator of string literal and heredocment */
+typedef struct rb_strterm_literal_struct {
+ long nest;
+ int func; /* STR_FUNC_* (e.g., STR_FUNC_ESCAPE and STR_FUNC_EXPAND) */
+ int paren; /* '(' of `%q(...)` */
+ int term; /* ')' of `%q(...)` */
+} rb_strterm_literal_t;
+
+typedef struct rb_strterm_heredoc_struct {
+ rb_parser_string_t *lastline; /* the string of line that contains `<<"END"` */
+ long offset; /* the column of END in `<<"END"` */
+ int sourceline; /* lineno of the line that contains `<<"END"` */
+ unsigned length; /* the length of END in `<<"END"` */
+ uint8_t quote;
+ uint8_t func;
+} rb_strterm_heredoc_t;
+
+#define HERETERM_LENGTH_MAX UINT_MAX
+
+typedef struct rb_strterm_struct {
+ bool heredoc;
+ union {
+ rb_strterm_literal_t literal;
+ rb_strterm_heredoc_t heredoc;
+ } u;
+} rb_strterm_t;
+
/* parse.y */
-VALUE rb_parser_set_yydebug(VALUE, VALUE);
-void *rb_parser_load_file(VALUE parser, VALUE name);
-void rb_parser_keep_script_lines(VALUE vparser);
-void rb_parser_error_tolerant(VALUE vparser);
-void rb_parser_keep_tokens(VALUE vparser);
+void rb_ruby_parser_mark(void *ptr);
+size_t rb_ruby_parser_memsize(const void *ptr);
+
+void rb_ruby_parser_set_options(rb_parser_t *p, int print, int loop, int chomp, int split);
+rb_parser_t *rb_ruby_parser_set_context(rb_parser_t *p, const struct rb_iseq_struct *base, int main);
+void rb_ruby_parser_set_script_lines(rb_parser_t *p);
+void rb_ruby_parser_error_tolerant(rb_parser_t *p);
+void rb_ruby_parser_keep_tokens(rb_parser_t *p);
+typedef VALUE (rb_parser_lex_gets_func)(struct parser_params*, rb_parser_input_data, int);
+rb_ast_t *rb_parser_compile(rb_parser_t *p, rb_parser_lex_gets_func *gets, const char *fname_ptr, long fname_len, rb_encoding *fname_enc, rb_parser_input_data input, int line);
RUBY_SYMBOL_EXPORT_BEGIN
-VALUE rb_parser_set_context(VALUE, const struct rb_iseq_struct *, int);
+
+rb_encoding *rb_ruby_parser_encoding(rb_parser_t *p);
+int rb_ruby_parser_end_seen_p(rb_parser_t *p);
+int rb_ruby_parser_set_yydebug(rb_parser_t *p, int flag);
+rb_parser_string_t *rb_str_to_parser_string(rb_parser_t *p, VALUE str);
+
+int rb_parser_dvar_defined_ref(struct parser_params*, ID, ID**);
+ID rb_parser_internal_id(struct parser_params*);
+int rb_parser_reg_fragment_check(struct parser_params*, rb_parser_string_t*, int);
+int rb_reg_named_capture_assign_iter_impl(struct parser_params *p, const char *s, long len, rb_encoding *enc, NODE **succ_block, const rb_code_location_t *loc);
+int rb_parser_local_defined(struct parser_params *p, ID id, const struct rb_iseq_struct *iseq);
+
RUBY_SYMBOL_EXPORT_END
+#ifndef UNIVERSAL_PARSER
+rb_parser_t *rb_ruby_parser_allocate(void);
+rb_parser_t *rb_ruby_parser_new(void);
+#endif
+
+#ifdef RIPPER
+void ripper_parser_mark(void *ptr);
+void ripper_parser_free(void *ptr);
+size_t ripper_parser_memsize(const void *ptr);
+void ripper_error(struct parser_params *p);
+VALUE ripper_value(struct parser_params *p);
+int rb_ruby_parser_get_yydebug(rb_parser_t *p);
+void rb_ruby_parser_set_value(rb_parser_t *p, VALUE value);
+int rb_ruby_parser_error_p(rb_parser_t *p);
+VALUE rb_ruby_parser_debug_output(rb_parser_t *p);
+void rb_ruby_parser_set_debug_output(rb_parser_t *p, VALUE output);
+VALUE rb_ruby_parser_parsing_thread(rb_parser_t *p);
+void rb_ruby_parser_set_parsing_thread(rb_parser_t *p, VALUE parsing_thread);
+void rb_ruby_parser_ripper_initialize(rb_parser_t *p, rb_parser_lex_gets_func *gets, rb_parser_input_data input, VALUE sourcefile_string, const char *sourcefile, int sourceline);
+VALUE rb_ruby_parser_result(rb_parser_t *p);
+rb_encoding *rb_ruby_parser_enc(rb_parser_t *p);
+VALUE rb_ruby_parser_ruby_sourcefile_string(rb_parser_t *p);
+int rb_ruby_parser_ruby_sourceline(rb_parser_t *p);
+int rb_ruby_parser_lex_state(rb_parser_t *p);
+void rb_ruby_ripper_parse0(rb_parser_t *p);
+int rb_ruby_ripper_dedent_string(rb_parser_t *p, VALUE string, int width);
+int rb_ruby_ripper_initialized_p(rb_parser_t *p);
+void rb_ruby_ripper_parser_initialize(rb_parser_t *p);
+long rb_ruby_ripper_column(rb_parser_t *p);
+long rb_ruby_ripper_token_len(rb_parser_t *p);
+rb_parser_string_t *rb_ruby_ripper_lex_lastline(rb_parser_t *p);
+VALUE rb_ruby_ripper_lex_state_name(struct parser_params *p, int state);
+#ifdef UNIVERSAL_PARSER
+rb_parser_t *rb_ripper_parser_params_allocate(const rb_parser_config_t *config);
+#endif
+struct parser_params *rb_ruby_ripper_parser_allocate(void);
+#endif
+
+#ifdef UNIVERSAL_PARSER
+#undef rb_encoding
+#endif
+
#endif /* INTERNAL_PARSE_H */
diff --git a/internal/random.h b/internal/random.h
index 231e2d5d7e..127b908e16 100644
--- a/internal/random.h
+++ b/internal/random.h
@@ -12,5 +12,6 @@
/* random.c */
int ruby_fill_random_bytes(void *, size_t, int);
+void rb_free_default_rand_key(void);
#endif /* INTERNAL_RANDOM_H */
diff --git a/internal/re.h b/internal/re.h
index 7b2505b9a8..3e20114665 100644
--- a/internal/re.h
+++ b/internal/re.h
@@ -22,7 +22,7 @@ VALUE rb_reg_equal(VALUE re1, VALUE re2);
void rb_backref_set_string(VALUE string, long pos, long len);
void rb_match_unbusy(VALUE);
int rb_match_count(VALUE match);
-int rb_match_nth_defined(int nth, VALUE match);
VALUE rb_reg_new_ary(VALUE ary, int options);
+VALUE rb_reg_last_defined(VALUE match);
#endif /* INTERNAL_RE_H */
diff --git a/internal/ruby_parser.h b/internal/ruby_parser.h
new file mode 100644
index 0000000000..98c3461954
--- /dev/null
+++ b/internal/ruby_parser.h
@@ -0,0 +1,102 @@
+#ifndef INTERNAL_RUBY_PARSE_H
+#define INTERNAL_RUBY_PARSE_H
+
+#include "internal.h"
+#include "internal/bignum.h"
+#include "internal/compilers.h"
+#include "internal/complex.h"
+#include "internal/parse.h"
+#include "internal/rational.h"
+#include "rubyparser.h"
+#include "vm.h"
+
+struct lex_pointer_string {
+ VALUE str;
+ long ptr;
+};
+
+RUBY_SYMBOL_EXPORT_BEGIN
+#ifdef UNIVERSAL_PARSER
+const rb_parser_config_t *rb_ruby_parser_config(void);
+rb_parser_t *rb_parser_params_new(void);
+#endif
+VALUE rb_parser_set_context(VALUE, const struct rb_iseq_struct *, int);
+VALUE rb_parser_new(void);
+VALUE rb_parser_compile_string_path(VALUE vparser, VALUE fname, VALUE src, int line);
+VALUE rb_str_new_parser_string(rb_parser_string_t *str);
+VALUE rb_str_new_mutable_parser_string(rb_parser_string_t *str);
+VALUE rb_parser_lex_get_str(struct lex_pointer_string *ptr_str);
+
+VALUE rb_node_str_string_val(const NODE *);
+VALUE rb_node_sym_string_val(const NODE *);
+VALUE rb_node_dstr_string_val(const NODE *);
+VALUE rb_node_regx_string_val(const NODE *);
+VALUE rb_node_dregx_string_val(const NODE *);
+VALUE rb_node_line_lineno_val(const NODE *);
+VALUE rb_node_file_path_val(const NODE *);
+VALUE rb_node_encoding_val(const NODE *);
+
+VALUE rb_node_integer_literal_val(const NODE *);
+VALUE rb_node_float_literal_val(const NODE *);
+VALUE rb_node_rational_literal_val(const NODE *);
+VALUE rb_node_imaginary_literal_val(const NODE *);
+RUBY_SYMBOL_EXPORT_END
+
+VALUE rb_parser_end_seen_p(VALUE);
+VALUE rb_parser_encoding(VALUE);
+VALUE rb_parser_set_yydebug(VALUE, VALUE);
+VALUE rb_parser_build_script_lines_from(rb_parser_ary_t *script_lines);
+void rb_parser_set_options(VALUE, int, int, int, int);
+VALUE rb_parser_load_file(VALUE parser, VALUE name);
+void rb_parser_set_script_lines(VALUE vparser);
+void rb_parser_error_tolerant(VALUE vparser);
+void rb_parser_keep_tokens(VALUE vparser);
+
+VALUE rb_parser_compile_string(VALUE, const char*, VALUE, int);
+VALUE rb_parser_compile_file_path(VALUE vparser, VALUE fname, VALUE input, int line);
+VALUE rb_parser_compile_generic(VALUE vparser, rb_parser_lex_gets_func *lex_gets, VALUE fname, VALUE input, int line);
+VALUE rb_parser_compile_array(VALUE vparser, VALUE fname, VALUE array, int start);
+
+enum lex_state_bits {
+ EXPR_BEG_bit, /* ignore newline, +/- is a sign. */
+ EXPR_END_bit, /* newline significant, +/- is an operator. */
+ EXPR_ENDARG_bit, /* ditto, and unbound braces. */
+ EXPR_ENDFN_bit, /* ditto, and unbound braces. */
+ EXPR_ARG_bit, /* newline significant, +/- is an operator. */
+ EXPR_CMDARG_bit, /* newline significant, +/- is an operator. */
+ EXPR_MID_bit, /* newline significant, +/- is an operator. */
+ EXPR_FNAME_bit, /* ignore newline, no reserved words. */
+ EXPR_DOT_bit, /* right after `.', `&.' or `::', no reserved words. */
+ EXPR_CLASS_bit, /* immediate after `class', no here document. */
+ EXPR_LABEL_bit, /* flag bit, label is allowed. */
+ EXPR_LABELED_bit, /* flag bit, just after a label. */
+ EXPR_FITEM_bit, /* symbol literal as FNAME. */
+ EXPR_MAX_STATE
+};
+/* examine combinations */
+enum lex_state_e {
+#define DEF_EXPR(n) EXPR_##n = (1 << EXPR_##n##_bit)
+ DEF_EXPR(BEG),
+ DEF_EXPR(END),
+ DEF_EXPR(ENDARG),
+ DEF_EXPR(ENDFN),
+ DEF_EXPR(ARG),
+ DEF_EXPR(CMDARG),
+ DEF_EXPR(MID),
+ DEF_EXPR(FNAME),
+ DEF_EXPR(DOT),
+ DEF_EXPR(CLASS),
+ DEF_EXPR(LABEL),
+ DEF_EXPR(LABELED),
+ DEF_EXPR(FITEM),
+ EXPR_VALUE = EXPR_BEG,
+ EXPR_BEG_ANY = (EXPR_BEG | EXPR_MID | EXPR_CLASS),
+ EXPR_ARG_ANY = (EXPR_ARG | EXPR_CMDARG),
+ EXPR_END_ANY = (EXPR_END | EXPR_ENDARG | EXPR_ENDFN),
+ EXPR_NONE = 0
+};
+
+VALUE rb_ruby_ast_new(const NODE *const root);
+rb_ast_t *rb_ruby_ast_data_get(VALUE vast);
+
+#endif /* INTERNAL_RUBY_PARSE_H */
diff --git a/internal/sanitizers.h b/internal/sanitizers.h
index 7b7d166c74..b0eb1fc851 100644
--- a/internal/sanitizers.h
+++ b/internal/sanitizers.h
@@ -16,11 +16,15 @@
#endif
#ifdef HAVE_SANITIZER_ASAN_INTERFACE_H
-# include <sanitizer/asan_interface.h>
+# if __has_feature(address_sanitizer)
+# define RUBY_ASAN_ENABLED
+# include <sanitizer/asan_interface.h>
+# endif
#endif
#ifdef HAVE_SANITIZER_MSAN_INTERFACE_H
# if __has_feature(memory_sanitizer)
+# define RUBY_MSAN_ENABLED
# include <sanitizer/msan_interface.h>
# endif
#endif
@@ -29,10 +33,10 @@
#include "ruby/ruby.h" /* for VALUE */
#if 0
-#elif __has_feature(memory_sanitizer) && __has_feature(address_sanitizer)
+#elif defined(RUBY_ASAN_ENABLED) && defined(RUBY_MSAN_ENABLED)
# define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(x) \
__attribute__((__no_sanitize__("memory, address"), __noinline__)) x
-#elif __has_feature(address_sanitizer)
+#elif defined(RUBY_ASAN_ENABLED)
# define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(x) \
__attribute__((__no_sanitize__("address"), __noinline__)) x
#elif defined(NO_SANITIZE_ADDRESS)
@@ -60,13 +64,15 @@
# define NO_SANITIZE(x, y) y
#endif
-#if !__has_feature(address_sanitizer)
+#ifndef RUBY_ASAN_ENABLED
# define __asan_poison_memory_region(x, y)
# define __asan_unpoison_memory_region(x, y)
# define __asan_region_is_poisoned(x, y) 0
+# define __asan_get_current_fake_stack() NULL
+# define __asan_addr_is_in_fake_stack(fake_stack, slot, start, end) NULL
#endif
-#if !__has_feature(memory_sanitizer)
+#ifndef RUBY_MSAN_ENABLED
# define __msan_allocated_memory(x, y) ((void)(x), (void)(y))
# define __msan_poison(x, y) ((void)(x), (void)(y))
# define __msan_unpoison(x, y) ((void)(x), (void)(y))
@@ -89,7 +95,7 @@
# define VALGRIND_MAKE_MEM_UNDEFINED(p, n) 0
#endif
-/*!
+/**
* This function asserts that a (continuous) memory region from ptr to size
* being "poisoned". Both read / write access to such memory region are
* prohibited until properly unpoisoned. The region must be previously
@@ -99,8 +105,8 @@
* region to reuse later: poison when you keep it unused, and unpoison when you
* reuse.
*
- * \param[in] ptr pointer to the beginning of the memory region to poison.
- * \param[in] size the length of the memory region to poison.
+ * @param[in] ptr pointer to the beginning of the memory region to poison.
+ * @param[in] size the length of the memory region to poison.
*/
static inline void
asan_poison_memory_region(const volatile void *ptr, size_t size)
@@ -109,10 +115,10 @@ asan_poison_memory_region(const volatile void *ptr, size_t size)
__asan_poison_memory_region(ptr, size);
}
-/*!
+/**
* This is a variant of asan_poison_memory_region that takes a VALUE.
*
- * \param[in] obj target object.
+ * @param[in] obj target object.
*/
static inline void
asan_poison_object(VALUE obj)
@@ -121,20 +127,20 @@ asan_poison_object(VALUE obj)
asan_poison_memory_region(ptr, SIZEOF_VALUE);
}
-#if !__has_feature(address_sanitizer)
-#define asan_poison_object_if(ptr, obj) ((void)(ptr), (void)(obj))
-#else
+#ifdef RUBY_ASAN_ENABLED
#define asan_poison_object_if(ptr, obj) do { \
if (ptr) asan_poison_object(obj); \
} while (0)
+#else
+#define asan_poison_object_if(ptr, obj) ((void)(ptr), (void)(obj))
#endif
-/*!
+/**
* This function predicates if the given object is fully addressable or not.
*
- * \param[in] obj target object.
- * \retval 0 the given object is fully addressable.
- * \retval otherwise pointer to first such byte who is poisoned.
+ * @param[in] obj target object.
+ * @retval 0 the given object is fully addressable.
+ * @retval otherwise pointer to first such byte who is poisoned.
*/
static inline void *
asan_poisoned_object_p(VALUE obj)
@@ -143,7 +149,7 @@ asan_poisoned_object_p(VALUE obj)
return __asan_region_is_poisoned(ptr, SIZEOF_VALUE);
}
-/*!
+/**
* This function asserts that a (formally poisoned) memory region from ptr to
* size is now addressable. Write access to such memory region gets allowed.
* However read access might or might not be possible depending on situations,
@@ -154,9 +160,9 @@ asan_poisoned_object_p(VALUE obj)
* the other hand, that memory region is fully defined and can be read
* immediately.
*
- * \param[in] ptr pointer to the beginning of the memory region to unpoison.
- * \param[in] size the length of the memory region.
- * \param[in] malloc_p if the memory region is like a malloc's return value or not.
+ * @param[in] ptr pointer to the beginning of the memory region to unpoison.
+ * @param[in] size the length of the memory region.
+ * @param[in] malloc_p if the memory region is like a malloc's return value or not.
*/
static inline void
asan_unpoison_memory_region(const volatile void *ptr, size_t size, bool malloc_p)
@@ -170,11 +176,11 @@ asan_unpoison_memory_region(const volatile void *ptr, size_t size, bool malloc_p
}
}
-/*!
+/**
* This is a variant of asan_unpoison_memory_region that takes a VALUE.
*
- * \param[in] obj target object.
- * \param[in] malloc_p if the memory region is like a malloc's return value or not.
+ * @param[in] obj target object.
+ * @param[in] malloc_p if the memory region is like a malloc's return value or not.
*/
static inline void
asan_unpoison_object(VALUE obj, bool newobj_p)
@@ -183,4 +189,109 @@ asan_unpoison_object(VALUE obj, bool newobj_p)
asan_unpoison_memory_region(ptr, SIZEOF_VALUE, newobj_p);
}
+static inline void *
+asan_unpoison_object_temporary(VALUE obj)
+{
+ void *ptr = asan_poisoned_object_p(obj);
+ asan_unpoison_object(obj, false);
+ return ptr;
+}
+
+static inline void *
+asan_poison_object_restore(VALUE obj, void *ptr)
+{
+ if (ptr) {
+ asan_poison_object(obj);
+ }
+ return NULL;
+}
+
+
+/**
+ * Checks if the given pointer is on an ASAN fake stack. If so, it returns the
+ * address this variable has on the real frame; if not, it returns the origin
+ * address unmodified.
+ *
+ * n.b. - _dereferencing_ the returned address is meaningless and should not
+ * be done; even though ASAN reserves space for the variable in both the real and
+ * fake stacks, the _value_ of that variable is only in the fake stack.
+ *
+ * n.b. - this only works for addresses passed in from local variables on the same
+ * thread, because the ASAN fake stacks are threadlocal.
+ *
+ * @param[in] slot the address of some local variable
+ * @retval a pointer to something from that frame on the _real_ machine stack
+ */
+static inline void *
+asan_get_real_stack_addr(void* slot)
+{
+ VALUE *addr;
+ addr = __asan_addr_is_in_fake_stack(__asan_get_current_fake_stack(), slot, NULL, NULL);
+ return addr ? addr : slot;
+}
+
+/**
+ * Gets the current thread's fake stack handle, which can be passed into get_fake_stack_extents
+ *
+ * @retval An opaque value which can be passed to asan_get_fake_stack_extents
+ */
+static inline void *
+asan_get_thread_fake_stack_handle(void)
+{
+ return __asan_get_current_fake_stack();
+}
+
+/**
+ * Checks if the given VALUE _actually_ represents a pointer to an ASAN fake stack.
+ *
+ * If the given slot _is_ actually a reference to an ASAN fake stack, and that fake stack
+ * contains the real values for the passed-in range of machine stack addresses, returns true
+ * and the range of the fake stack through the outparams.
+ *
+ * Otherwise, returns false, and sets the outparams to NULL.
+ *
+ * Note that this function expects "start" to be > "end" on downward-growing stack architectures;
+ *
+ * @param[in] thread_fake_stack_handle The asan fake stack reference for the thread we're scanning
+ * @param[in] slot The value on the machine stack we want to inspect
+ * @param[in] machine_stack_start The extents of the real machine stack on which slot lives
+ * @param[in] machine_stack_end The extents of the real machine stack on which slot lives
+ * @param[out] fake_stack_start_out The extents of the fake stack which contains real VALUEs
+ * @param[out] fake_stack_end_out The extents of the fake stack which contains real VALUEs
+ * @return Whether slot is a pointer to a fake stack for the given machine stack range
+*/
+
+static inline bool
+asan_get_fake_stack_extents(void *thread_fake_stack_handle, VALUE slot,
+ void *machine_stack_start, void *machine_stack_end,
+ void **fake_stack_start_out, void **fake_stack_end_out)
+{
+ /* the ifdef is needed here to suppress a warning about fake_frame_{start/end} being
+ uninitialized if __asan_addr_is_in_fake_stack is an empty macro */
+#ifdef RUBY_ASAN_ENABLED
+ void *fake_frame_start;
+ void *fake_frame_end;
+ void *real_stack_frame = __asan_addr_is_in_fake_stack(
+ thread_fake_stack_handle, (void *)slot, &fake_frame_start, &fake_frame_end
+ );
+ if (real_stack_frame) {
+ bool in_range;
+#if STACK_GROW_DIRECTION < 0
+ in_range = machine_stack_start >= real_stack_frame && real_stack_frame >= machine_stack_end;
+#else
+ in_range = machine_stack_start <= real_stack_frame && real_stack_frame <= machine_stack_end;
+#endif
+ if (in_range) {
+ *fake_stack_start_out = fake_frame_start;
+ *fake_stack_end_out = fake_frame_end;
+ return true;
+ }
+ }
+#endif
+ *fake_stack_start_out = 0;
+ *fake_stack_end_out = 0;
+ return false;
+}
+
+
#endif /* INTERNAL_SANITIZERS_H */
diff --git a/internal/signal.h b/internal/signal.h
index 86fb54e949..2363bf412c 100644
--- a/internal/signal.h
+++ b/internal/signal.h
@@ -13,9 +13,12 @@
extern int ruby_enable_coredump;
int rb_get_next_signal(void);
+#ifdef POSIX_SIGNAL
+void (*ruby_posix_signal(int, void (*)(int)))(int);
+#endif
+
RUBY_SYMBOL_EXPORT_BEGIN
/* signal.c (export) */
-int rb_grantpt(int fd);
RUBY_SYMBOL_EXPORT_END
#endif /* INTERNAL_SIGNAL_H */
diff --git a/internal/st.h b/internal/st.h
new file mode 100644
index 0000000000..a26b224505
--- /dev/null
+++ b/internal/st.h
@@ -0,0 +1,11 @@
+#ifndef INTERNAL_ST_H
+#define INTERNAL_ST_H
+
+#include "include/ruby/st.h"
+
+st_table *rb_st_replace(st_table *new_tab, st_table *old_tab);
+#define st_replace rb_st_replace
+st_table *rb_st_init_existing_table_with_size(st_table *tab, const struct st_hash_type *type, st_index_t size);
+#define st_init_existing_table_with_size rb_st_init_existing_table_with_size
+
+#endif
diff --git a/internal/string.h b/internal/string.h
index 5f59d9621b..fb37f73114 100644
--- a/internal/string.h
+++ b/internal/string.h
@@ -17,6 +17,7 @@
#define STR_NOEMBED FL_USER1
#define STR_SHARED FL_USER2 /* = ELTS_SHARED */
+#define STR_CHILLED FL_USER3
#ifdef rb_fstring_cstr
# undef rb_fstring_cstr
@@ -45,6 +46,7 @@ void rb_str_make_independent(VALUE str);
int rb_enc_str_coderange_scan(VALUE str, rb_encoding *enc);
int rb_ascii8bit_appendable_encoding_index(rb_encoding *enc, unsigned int code);
VALUE rb_str_include(VALUE str, VALUE arg);
+VALUE rb_str_byte_substr(VALUE str, VALUE beg, VALUE len);
static inline bool STR_EMBED_P(VALUE str);
static inline bool STR_SHARED_P(VALUE str);
@@ -57,6 +59,7 @@ static inline VALUE rb_str_eql_internal(const VALUE str1, const VALUE str2);
RUBY_SYMBOL_EXPORT_BEGIN
/* string.c (export) */
VALUE rb_str_tmp_frozen_acquire(VALUE str);
+VALUE rb_str_tmp_frozen_no_embed_acquire(VALUE str);
void rb_str_tmp_frozen_release(VALUE str, VALUE tmp);
VALUE rb_setup_fake_str(struct RString *fake_str, const char *name, long len, rb_encoding *enc);
VALUE rb_str_upto_each(VALUE, VALUE, int, int (*each)(VALUE, VALUE), VALUE);
@@ -64,7 +67,6 @@ VALUE rb_str_upto_endless_each(VALUE, int (*each)(VALUE, VALUE), VALUE);
void rb_str_make_embedded(VALUE);
size_t rb_str_size_as_embedded(VALUE);
bool rb_str_reembeddable_p(VALUE);
-void rb_str_update_shared_ary(VALUE str, VALUE old_root, VALUE new_root);
RUBY_SYMBOL_EXPORT_END
VALUE rb_fstring_new(const char *ptr, long len);
@@ -76,7 +78,7 @@ VALUE rb_id_quote_unprintable(ID);
VALUE rb_sym_proc_call(ID mid, int argc, const VALUE *argv, int kw_splat, VALUE passed_proc);
struct rb_execution_context_struct;
-VALUE rb_ec_str_resurrect(struct rb_execution_context_struct *ec, VALUE str);
+VALUE rb_ec_str_resurrect(struct rb_execution_context_struct *ec, VALUE str, bool chilled);
#define rb_fstring_lit(str) rb_fstring_new((str), rb_strlen_lit(str))
#define rb_fstring_literal(str) rb_fstring_lit(str)
@@ -108,6 +110,26 @@ STR_SHARED_P(VALUE str)
}
static inline bool
+CHILLED_STRING_P(VALUE obj)
+{
+ return RB_TYPE_P(obj, T_STRING) && FL_TEST_RAW(obj, STR_CHILLED);
+}
+
+static inline void
+CHILLED_STRING_MUTATED(VALUE str)
+{
+ rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "literal string will be frozen in the future");
+ FL_UNSET_RAW(str, STR_CHILLED | FL_FREEZE);
+}
+
+static inline void
+STR_CHILL_RAW(VALUE str)
+{
+ // Chilled strings are always also frozen
+ FL_SET_RAW(str, STR_CHILLED | RUBY_FL_FREEZE);
+}
+
+static inline bool
is_ascii_string(VALUE str)
{
return rb_enc_str_coderange(str) == ENC_CODERANGE_7BIT;
@@ -119,6 +141,21 @@ is_broken_string(VALUE str)
return rb_enc_str_coderange(str) == ENC_CODERANGE_BROKEN;
}
+static inline bool
+at_char_boundary(const char *s, const char *p, const char *e, rb_encoding *enc)
+{
+ return rb_enc_left_char_head(s, p, e, enc) == p;
+}
+
+static inline bool
+at_char_right_boundary(const char *s, const char *p, const char *e, rb_encoding *enc)
+{
+ RUBY_ASSERT(s <= p);
+ RUBY_ASSERT(p <= e);
+
+ return rb_enc_right_char_head(s, p, e, enc) == p;
+}
+
/* expect tail call optimization */
// YJIT needs this function to never allocate and never raise
static inline VALUE
diff --git a/internal/struct.h b/internal/struct.h
index 9b56254541..6da5bad10a 100644
--- a/internal/struct.h
+++ b/internal/struct.h
@@ -15,7 +15,6 @@ enum {
RSTRUCT_EMBED_LEN_MASK = RUBY_FL_USER7 | RUBY_FL_USER6 | RUBY_FL_USER5 | RUBY_FL_USER4 |
RUBY_FL_USER3 | RUBY_FL_USER2 | RUBY_FL_USER1,
RSTRUCT_EMBED_LEN_SHIFT = (RUBY_FL_USHIFT+1),
- RSTRUCT_TRANSIENT_FLAG = RUBY_FL_USER8,
};
struct RStruct {
@@ -61,9 +60,6 @@ VALUE rb_struct_init_copy(VALUE copy, VALUE s);
VALUE rb_struct_lookup(VALUE s, VALUE idx);
VALUE rb_struct_s_keyword_init(VALUE klass);
static inline const VALUE *rb_struct_const_heap_ptr(VALUE st);
-static inline bool RSTRUCT_TRANSIENT_P(VALUE st);
-static inline void RSTRUCT_TRANSIENT_SET(VALUE st);
-static inline void RSTRUCT_TRANSIENT_UNSET(VALUE st);
static inline long RSTRUCT_EMBED_LEN(VALUE st);
static inline long RSTRUCT_LEN(VALUE st);
static inline int RSTRUCT_LENINT(VALUE st);
@@ -71,32 +67,6 @@ static inline const VALUE *RSTRUCT_CONST_PTR(VALUE st);
static inline void RSTRUCT_SET(VALUE st, long k, VALUE v);
static inline VALUE RSTRUCT_GET(VALUE st, long k);
-static inline bool
-RSTRUCT_TRANSIENT_P(VALUE st)
-{
-#if USE_TRANSIENT_HEAP
- return FL_TEST_RAW(st, RSTRUCT_TRANSIENT_FLAG);
-#else
- return false;
-#endif
-}
-
-static inline void
-RSTRUCT_TRANSIENT_SET(VALUE st)
-{
-#if USE_TRANSIENT_HEAP
- FL_SET_RAW(st, RSTRUCT_TRANSIENT_FLAG);
-#endif
-}
-
-static inline void
-RSTRUCT_TRANSIENT_UNSET(VALUE st)
-{
-#if USE_TRANSIENT_HEAP
- FL_UNSET_RAW(st, RSTRUCT_TRANSIENT_FLAG);
-#endif
-}
-
static inline long
RSTRUCT_EMBED_LEN(VALUE st)
{
diff --git a/internal/symbol.h b/internal/symbol.h
index 30c81ea004..e7730cb70f 100644
--- a/internal/symbol.h
+++ b/internal/symbol.h
@@ -32,6 +32,9 @@ ID rb_make_temporary_id(size_t n);
void rb_gc_free_dsymbol(VALUE);
int rb_static_id_valid_p(ID id);
+/* vm.c */
+void rb_free_static_symid_str(void);
+
#if __has_builtin(__builtin_constant_p)
#define rb_sym_intern_ascii_cstr(ptr) \
(__builtin_constant_p(ptr) ? \
diff --git a/internal/thread.h b/internal/thread.h
index c41a16c12d..47273436e3 100644
--- a/internal/thread.h
+++ b/internal/thread.h
@@ -50,6 +50,7 @@ void rb_mutex_allow_trap(VALUE self, int val);
VALUE rb_uninterruptible(VALUE (*b_proc)(VALUE), VALUE data);
VALUE rb_mutex_owned_p(VALUE self);
VALUE rb_exec_recursive_outer_mid(VALUE (*f)(VALUE g, VALUE h, int r), VALUE g, VALUE h, ID mid);
+void ruby_mn_threads_params(void);
int rb_thread_wait_for_single_fd(int fd, int events, struct timeval * timeout);
@@ -62,13 +63,17 @@ int rb_notify_fd_close(int fd, struct rb_io_close_wait_list *busy);
void rb_notify_fd_close_wait(struct rb_io_close_wait_list *busy);
RUBY_SYMBOL_EXPORT_BEGIN
+
/* Temporary. This API will be removed (renamed). */
VALUE rb_thread_io_blocking_region(rb_blocking_function_t *func, void *data1, int fd);
+VALUE rb_thread_io_blocking_call(rb_blocking_function_t *func, void *data1, int fd, int events);
/* thread.c (export) */
int ruby_thread_has_gvl_p(void); /* for ext/fiddle/closure.c */
+
RUBY_SYMBOL_EXPORT_END
int rb_threadptr_execute_interrupts(struct rb_thread_struct *th, int blocking_timing);
+bool rb_thread_mn_schedulable(VALUE thread);
#endif /* INTERNAL_THREAD_H */
diff --git a/internal/transcode.h b/internal/transcode.h
index 9922332ea9..ce4f2341be 100644
--- a/internal/transcode.h
+++ b/internal/transcode.h
@@ -17,4 +17,7 @@
extern VALUE rb_cEncodingConverter;
size_t rb_econv_memsize(rb_econv_t *);
+/* vm.c */
+void rb_free_transcoder_table(void);
+
#endif /* INTERNAL_TRANSCODE_H */
diff --git a/internal/variable.h b/internal/variable.h
index 4436cd789c..b2a30c7c58 100644
--- a/internal/variable.h
+++ b/internal/variable.h
@@ -15,10 +15,6 @@
#include "ruby/ruby.h" /* for VALUE */
#include "shape.h" /* for rb_shape_t */
-/* global variable */
-
-#define ROBJECT_TRANSIENT_FLAG FL_USER2
-
/* variable.c */
void rb_gc_mark_global_tbl(void);
void rb_gc_update_global_tbl(void);
@@ -32,17 +28,33 @@ rb_gvar_getter_t *rb_gvar_getter_function_of(ID);
rb_gvar_setter_t *rb_gvar_setter_function_of(ID);
void rb_gvar_readonly_setter(VALUE v, ID id, VALUE *_);
void rb_gvar_ractor_local(const char *name);
-static inline bool ROBJ_TRANSIENT_P(VALUE obj);
-static inline void ROBJ_TRANSIENT_SET(VALUE obj);
-static inline void ROBJ_TRANSIENT_UNSET(VALUE obj);
+
+/**
+ * Sets the name of a module.
+ *
+ * Non-permanently named classes can have a temporary name assigned (or
+ * cleared). In that case the name will be used for `#inspect` and `#to_s`, and
+ * nested classes/modules will be named with the temporary name as a prefix.
+ *
+ * After the module is assigned to a constant, the temporary name will be
+ * discarded, and the name will be computed based on the nesting.
+ *
+ * @param[in] mod An instance of ::rb_cModule.
+ * @param[in] name An instance of ::rb_cString.
+ * @retval mod
+ */
+VALUE rb_mod_set_temporary_name(VALUE, VALUE);
struct gen_ivtbl;
int rb_gen_ivtbl_get(VALUE obj, ID id, struct gen_ivtbl **ivtbl);
-int rb_obj_evacuate_ivs_to_hash_table(ID key, VALUE val, st_data_t arg);
+void rb_obj_copy_ivs_to_hash_table(VALUE obj, st_table *table);
+void rb_obj_convert_to_too_complex(VALUE obj, st_table *table);
+void rb_evict_ivars_to_hash(VALUE obj);
RUBY_SYMBOL_EXPORT_BEGIN
/* variable.c (export) */
-void rb_mark_and_update_generic_ivar(VALUE);
+void rb_mark_generic_ivar(VALUE obj);
+void rb_ref_update_generic_ivar(VALUE);
void rb_mv_generic_ivar(VALUE src, VALUE dst);
VALUE rb_const_missing(VALUE klass, VALUE name);
int rb_class_ivar_set(VALUE klass, ID vid, VALUE value);
@@ -54,35 +66,7 @@ VALUE rb_gvar_get(ID);
VALUE rb_gvar_set(ID, VALUE);
VALUE rb_gvar_defined(ID);
void rb_const_warn_if_deprecated(const rb_const_entry_t *, VALUE, ID);
-rb_shape_t * rb_grow_iv_list(VALUE obj);
void rb_ensure_iv_list_size(VALUE obj, uint32_t len, uint32_t newsize);
-struct gen_ivtbl *rb_ensure_generic_iv_list_size(VALUE obj, rb_shape_t *shape, uint32_t newsize);
attr_index_t rb_obj_ivar_set(VALUE obj, ID id, VALUE val);
-static inline bool
-ROBJ_TRANSIENT_P(VALUE obj)
-{
-#if USE_TRANSIENT_HEAP
- return FL_TEST_RAW(obj, ROBJECT_TRANSIENT_FLAG);
-#else
- return false;
-#endif
-}
-
-static inline void
-ROBJ_TRANSIENT_SET(VALUE obj)
-{
-#if USE_TRANSIENT_HEAP
- FL_SET_RAW(obj, ROBJECT_TRANSIENT_FLAG);
-#endif
-}
-
-static inline void
-ROBJ_TRANSIENT_UNSET(VALUE obj)
-{
-#if USE_TRANSIENT_HEAP
- FL_UNSET_RAW(obj, ROBJECT_TRANSIENT_FLAG);
-#endif
-}
-
#endif /* INTERNAL_VARIABLE_H */
diff --git a/internal/vm.h b/internal/vm.h
index 7943027946..74635e6ad8 100644
--- a/internal/vm.h
+++ b/internal/vm.h
@@ -45,19 +45,21 @@ VALUE rb_vm_push_frame_fname(struct rb_execution_context_struct *ec, VALUE fname
/* vm.c */
VALUE rb_obj_is_thread(VALUE obj);
void rb_vm_mark(void *ptr);
+void rb_vm_register_global_object(VALUE obj);
void rb_vm_each_stack_value(void *ptr, void (*cb)(VALUE, void*), void *ctx);
PUREFUNC(VALUE rb_vm_top_self(void));
const void **rb_vm_get_insns_address_table(void);
VALUE rb_source_location(int *pline);
const char *rb_source_location_cstr(int *pline);
void rb_vm_pop_cfunc_frame(void);
-int rb_vm_add_root_module(VALUE module);
void rb_vm_check_redefinition_by_prepend(VALUE klass);
int rb_vm_check_optimizable_mid(VALUE mid);
VALUE rb_yield_refine_block(VALUE refinement, VALUE refinements);
VALUE ruby_vm_special_exception_copy(VALUE);
PUREFUNC(st_table *rb_vm_fstring_table(void));
+void rb_lastline_set_up(VALUE val, unsigned int up);
+
/* vm_eval.c */
VALUE rb_current_realfilepath(void);
VALUE rb_check_block_call(VALUE, ID, int, const VALUE *, rb_block_call_func_t, VALUE);
@@ -76,6 +78,16 @@ VALUE rb_lambda_call(VALUE obj, ID mid, int argc, const VALUE *argv,
VALUE data2);
void rb_check_stack_overflow(void);
+#if USE_YJIT
+/* vm_exec.c */
+extern uint64_t rb_vm_insns_count;
+#endif
+
+extern bool rb_free_at_exit;
+
+/* miniinit.c and builtin.c */
+void rb_free_loaded_builtin_table(void);
+
/* vm_insnhelper.c */
VALUE rb_equal_opt(VALUE obj1, VALUE obj2);
VALUE rb_eql_opt(VALUE obj1, VALUE obj2);
@@ -90,7 +102,7 @@ int rb_ec_obj_respond_to(struct rb_execution_context_struct *ec, VALUE obj, ID i
void rb_clear_constant_cache(void);
/* vm_dump.c */
-void rb_print_backtrace(void);
+void rb_print_backtrace(FILE *);
/* vm_backtrace.c */
VALUE rb_vm_thread_backtrace(int argc, const VALUE *argv, VALUE thval);
@@ -98,10 +110,11 @@ VALUE rb_vm_thread_backtrace_locations(int argc, const VALUE *argv, VALUE thval)
VALUE rb_vm_backtrace(int argc, const VALUE * argv, struct rb_execution_context_struct * ec);
VALUE rb_vm_backtrace_locations(int argc, const VALUE * argv, struct rb_execution_context_struct * ec);
VALUE rb_make_backtrace(void);
-void rb_backtrace_print_as_bugreport(void);
+void rb_backtrace_print_as_bugreport(FILE*);
int rb_backtrace_p(VALUE obj);
VALUE rb_backtrace_to_str_ary(VALUE obj);
VALUE rb_backtrace_to_location_ary(VALUE obj);
+VALUE rb_location_ary_to_backtrace(VALUE ary);
void rb_backtrace_each(VALUE (*iter)(VALUE recv, VALUE str), VALUE output);
int rb_frame_info_p(VALUE obj);
int rb_get_node_id_from_frame_info(VALUE obj);
diff --git a/io.c b/io.c
index 772aa8fe1e..feff445943 100644
--- a/io.c
+++ b/io.c
@@ -112,6 +112,7 @@
#include "encindex.h"
#include "id.h"
#include "internal.h"
+#include "internal/class.h"
#include "internal/encoding.h"
#include "internal/error.h"
#include "internal/inits.h"
@@ -170,6 +171,7 @@ off_t __syscall(quad_t number, ...);
#define open rb_w32_uopen
#undef rename
#define rename(f, t) rb_w32_urename((f), (t))
+#include "win32/file.h"
#endif
VALUE rb_cIO;
@@ -532,10 +534,12 @@ static rb_io_t *flush_before_seek(rb_io_t *fptr);
extern ID ruby_static_id_signo;
-NORETURN(static void raise_on_write(rb_io_t *fptr, int e, VALUE errinfo));
+NORETURN(static void rb_sys_fail_on_write(rb_io_t *fptr));
static void
-raise_on_write(rb_io_t *fptr, int e, VALUE errinfo)
+rb_sys_fail_on_write(rb_io_t *fptr)
{
+ int e = errno;
+ VALUE errinfo = rb_syserr_new_path(e, (fptr)->pathv);
#if defined EPIPE
if (fptr_signal_on_epipe(fptr) && (e == EPIPE)) {
const VALUE sig =
@@ -549,12 +553,6 @@ raise_on_write(rb_io_t *fptr, int e, VALUE errinfo)
rb_exc_raise(errinfo);
}
-#define rb_sys_fail_on_write(fptr) \
- do { \
- int e = errno; \
- raise_on_write(fptr, e, rb_syserr_new_path(e, (fptr)->pathv)); \
- } while (0)
-
#define NEED_NEWLINE_DECORATOR_ON_READ(fptr) ((fptr)->mode & FMODE_TEXTMODE)
#define NEED_NEWLINE_DECORATOR_ON_WRITE(fptr) ((fptr)->mode & FMODE_TEXTMODE)
#if defined(RUBY_TEST_CRLF_ENVIRONMENT) || defined(_WIN32)
@@ -846,7 +844,7 @@ rb_io_timeout(VALUE self)
* timeout = duration -> duration
* timeout = nil -> nil
*
- * \Set the internal timeout to the specified duration or nil. The timeout
+ * Sets the internal timeout to the specified duration or nil. The timeout
* applies to all blocking operations where possible.
*
* When the operation performs longer than the timeout set, IO::TimeoutError
@@ -1063,20 +1061,24 @@ rb_gc_for_fd(int err)
return 0;
}
+/* try `expr` upto twice while it returns false and `errno`
+ * is to GC. Each `errno`s are available as `first_errno` and
+ * `retried_errno` respectively */
+#define TRY_WITH_GC(expr) \
+ for (int first_errno, retried_errno = 0, retried = 0; \
+ (!retried && \
+ !(expr) && \
+ (!rb_gc_for_fd(first_errno = errno) || !(expr)) && \
+ (retried_errno = errno, 1)); \
+ (void)retried_errno, retried = 1)
+
static int
ruby_dup(int orig)
{
- int fd;
+ int fd = -1;
- fd = rb_cloexec_dup(orig);
- if (fd < 0) {
- int e = errno;
- if (rb_gc_for_fd(e)) {
- fd = rb_cloexec_dup(orig);
- }
- if (fd < 0) {
- rb_syserr_fail(e, 0);
- }
+ TRY_WITH_GC((fd = rb_cloexec_dup(orig)) >= 0) {
+ rb_syserr_fail(first_errno, 0);
}
rb_update_max_fd(fd);
return fd;
@@ -1141,6 +1143,11 @@ static int nogvl_wait_for(VALUE th, rb_io_t *fptr, short events, struct timeval
static inline int
io_internal_wait(VALUE thread, rb_io_t *fptr, int error, int events, struct timeval *timeout)
{
+ if (!timeout && rb_thread_mn_schedulable(thread)) {
+ RUBY_ASSERT(errno == EWOULDBLOCK || errno == EAGAIN);
+ return -1;
+ }
+
int ready = nogvl_wait_for(thread, fptr, events, timeout);
if (ready > 0) {
@@ -1281,7 +1288,7 @@ rb_io_read_memory(rb_io_t *fptr, void *buf, size_t count)
iis.timeout = &timeout_storage;
}
- return (ssize_t)rb_thread_io_blocking_region(internal_read_func, &iis, fptr->fd);
+ return (ssize_t)rb_thread_io_blocking_call(internal_read_func, &iis, fptr->fd, RB_WAITFD_IN);
}
static ssize_t
@@ -1314,7 +1321,7 @@ rb_io_write_memory(rb_io_t *fptr, const void *buf, size_t count)
iis.timeout = &timeout_storage;
}
- return (ssize_t)rb_thread_io_blocking_region(internal_write_func, &iis, fptr->fd);
+ return (ssize_t)rb_thread_io_blocking_call(internal_write_func, &iis, fptr->fd, RB_WAITFD_OUT);
}
#ifdef HAVE_WRITEV
@@ -1351,7 +1358,7 @@ rb_writev_internal(rb_io_t *fptr, const struct iovec *iov, int iovcnt)
iis.timeout = &timeout_storage;
}
- return (ssize_t)rb_thread_io_blocking_region(internal_writev_func, &iis, fptr->fd);
+ return (ssize_t)rb_thread_io_blocking_call(internal_writev_func, &iis, fptr->fd, RB_WAITFD_OUT);
}
#endif
@@ -1381,7 +1388,7 @@ static VALUE
io_flush_buffer_async(VALUE arg)
{
rb_io_t *fptr = (rb_io_t *)arg;
- return rb_thread_io_blocking_region(io_flush_buffer_sync, fptr, fptr->fd);
+ return rb_thread_io_blocking_call(io_flush_buffer_sync, fptr, fptr->fd, RB_WAITFD_OUT);
}
static inline int
@@ -1694,7 +1701,6 @@ make_writeconv(rb_io_t *fptr)
/* writing functions */
struct binwrite_arg {
rb_io_t *fptr;
- VALUE str;
const char *ptr;
long length;
};
@@ -1790,13 +1796,11 @@ io_binwrite_string(VALUE arg)
// Write as much as possible:
ssize_t result = io_binwrite_string_internal(p->fptr, ptr, remaining);
- // If only the internal buffer is written, result will be zero [bytes of given data written]. This means we
- // should try again.
if (result == 0) {
- errno = EWOULDBLOCK;
+ // If only the internal buffer is written, result will be zero [bytes of given data written]. This means we
+ // should try again immediately.
}
-
- if (result > 0) {
+ else if (result > 0) {
if ((size_t)result == remaining) break;
ptr += result;
remaining -= result;
@@ -1846,7 +1850,7 @@ io_binwrite_requires_flush_write(rb_io_t *fptr, long len, int nosync)
}
static long
-io_binwrite(VALUE str, const char *ptr, long len, rb_io_t *fptr, int nosync)
+io_binwrite(const char *ptr, long len, rb_io_t *fptr, int nosync)
{
if (len <= 0) return len;
@@ -1859,7 +1863,6 @@ io_binwrite(VALUE str, const char *ptr, long len, rb_io_t *fptr, int nosync)
struct binwrite_arg arg;
arg.fptr = fptr;
- arg.str = str;
arg.ptr = ptr;
arg.length = len;
@@ -1967,9 +1970,9 @@ io_fwrite(VALUE str, rb_io_t *fptr, int nosync)
if (converted)
OBJ_FREEZE(str);
- tmp = rb_str_tmp_frozen_acquire(str);
+ tmp = rb_str_tmp_frozen_no_embed_acquire(str);
RSTRING_GETMEM(tmp, ptr, len);
- n = io_binwrite(tmp, ptr, len, fptr, nosync);
+ n = io_binwrite(ptr, len, fptr, nosync);
rb_str_tmp_frozen_release(str, tmp);
return n;
@@ -1982,7 +1985,7 @@ rb_io_bufwrite(VALUE io, const void *buf, size_t size)
GetOpenFile(io, fptr);
rb_io_check_writable(fptr);
- return (ssize_t)io_binwrite(0, buf, (long)size, fptr, 0);
+ return (ssize_t)io_binwrite(buf, (long)size, fptr, 0);
}
static VALUE
@@ -2274,7 +2277,7 @@ rb_io_writev(VALUE io, int argc, const VALUE *argv)
if (argc > 1 && rb_obj_method_arity(io, id_write) == 1) {
if (io != rb_ractor_stderr() && RTEST(ruby_verbose)) {
VALUE klass = CLASS_OF(io);
- char sep = FL_TEST(klass, FL_SINGLETON) ? (klass = io, '.') : '#';
+ char sep = RCLASS_SINGLETON_P(klass) ? (klass = io, '.') : '#';
rb_category_warning(
RB_WARN_CATEGORY_DEPRECATED, "%+"PRIsVALUE"%c""write is outdated interface"
" which accepts just one argument",
@@ -2668,7 +2671,7 @@ rb_io_eof(VALUE io)
READ_CHECK(fptr);
#if RUBY_CRLF_ENVIRONMENT
if (!NEED_READCONV(fptr) && NEED_NEWLINE_DECORATOR_ON_READ(fptr)) {
- return RBOOL(eof(fptr->fd));;
+ return RBOOL(eof(fptr->fd));
}
#endif
return RBOOL(io_fillbuf(fptr) < 0);
@@ -2865,11 +2868,19 @@ rb_io_descriptor(VALUE io)
return fptr->fd;
}
else {
- return RB_NUM2INT(rb_funcall(io, id_fileno, 0));
+ VALUE fileno = rb_check_funcall(io, id_fileno, 0, NULL);
+ if (!UNDEF_P(fileno)) {
+ return RB_NUM2INT(fileno);
+ }
}
+
+ rb_raise(rb_eTypeError, "expected IO or #fileno, %"PRIsVALUE" given", rb_obj_class(io));
+
+ UNREACHABLE_RETURN(-1);
}
-int rb_io_mode(VALUE io)
+int
+rb_io_mode(VALUE io)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
@@ -3263,9 +3274,10 @@ io_setstrbuf(VALUE *str, long len)
}
else {
VALUE s = StringValue(*str);
+ rb_str_modify(s);
+
long clen = RSTRING_LEN(s);
if (clen >= len) {
- rb_str_modify(s);
return FALSE;
}
len -= clen;
@@ -3396,7 +3408,12 @@ io_read_memory_call(VALUE arg)
}
}
- return rb_thread_io_blocking_region(internal_read_func, iis, iis->fptr->fd);
+ if (iis->nonblock) {
+ return rb_thread_io_blocking_call(internal_read_func, iis, iis->fptr->fd, 0);
+ }
+ else {
+ return rb_thread_io_blocking_call(internal_read_func, iis, iis->fptr->fd, RB_WAITFD_IN);
+ }
}
static long
@@ -4139,8 +4156,7 @@ rb_io_getline_0(VALUE rs, long limit, int chomp, rb_io_t *fptr)
s = RSTRING_PTR(str);
e = RSTRING_END(str);
p = e - rslen;
- pp = rb_enc_left_char_head(s, p, e, enc);
- if (pp != p) continue;
+ if (!at_char_boundary(s, p, e, enc)) continue;
if (!rspara) rscheck(rsptr, rslen, rs);
if (memcmp(p, rsptr, rslen) == 0) {
if (chomp) {
@@ -4281,11 +4297,8 @@ rb_io_gets_internal(VALUE io)
* File.open('t.txt') {|f| f.gets(12) } # => "First line\n"
*
* With arguments +sep+ and +limit+ given,
- * combines the two behaviors:
- *
- * - Returns the next line as determined by line separator +sep+,
- * or +nil+ if none.
- * - But returns no more bytes than are allowed by the limit.
+ * combines the two behaviors
+ * (see {Line Separator and Line Limit}[rdoc-ref:IO@Line+Separator+and+Line+Limit]).
*
* Optional keyword argument +chomp+ specifies whether line separators
* are to be omitted:
@@ -4352,22 +4365,28 @@ rb_io_set_lineno(VALUE io, VALUE lineno)
return lineno;
}
-/*
- * call-seq:
- * readline(sep = $/, chomp: false) -> string
- * readline(limit, chomp: false) -> string
- * readline(sep, limit, chomp: false) -> string
- *
- * Reads a line as with IO#gets, but raises EOFError if already at end-of-stream.
- *
- * Optional keyword argument +chomp+ specifies whether line separators
- * are to be omitted.
- */
-
+/* :nodoc: */
static VALUE
-rb_io_readline(int argc, VALUE *argv, VALUE io)
+io_readline(rb_execution_context_t *ec, VALUE io, VALUE sep, VALUE lim, VALUE chomp)
{
- VALUE line = rb_io_gets_m(argc, argv, io);
+ if (NIL_P(lim)) {
+ // If sep is specified, but it's not a string and not nil, then assume
+ // it's the limit (it should be an integer)
+ if (!NIL_P(sep) && NIL_P(rb_check_string_type(sep))) {
+ // If the user has specified a non-nil / non-string value
+ // for the separator, we assume it's the limit and set the
+ // separator to default: rb_rs.
+ lim = sep;
+ sep = rb_rs;
+ }
+ }
+
+ if (!NIL_P(sep)) {
+ StringValue(sep);
+ }
+
+ VALUE line = rb_io_getline_1(sep, NIL_P(lim) ? -1L : NUM2LONG(lim), RTEST(chomp), io);
+ rb_lastline_set_up(line, 1);
if (NIL_P(line)) {
rb_eof_error();
@@ -4428,10 +4447,8 @@ static VALUE io_readlines(const struct getline_arg *arg, VALUE io);
* f.close
*
* With arguments +sep+ and +limit+ given,
- * combines the two behaviors:
- *
- * - Returns lines as determined by line separator +sep+.
- * - But returns no more bytes in a line than are allowed by the limit.
+ * combines the two behaviors
+ * (see {Line Separator and Line Limit}[rdoc-ref:IO@Line+Separator+and+Line+Limit]).
*
* Optional keyword argument +chomp+ specifies whether line separators
* are to be omitted:
@@ -4551,10 +4568,8 @@ io_readlines(const struct getline_arg *arg, VALUE io)
* "ne\n"
*
* With arguments +sep+ and +limit+ given,
- * combines the two behaviors:
- *
- * - Calls with the next line as determined by line separator +sep+.
- * - But returns no more bytes than are allowed by the limit.
+ * combines the two behaviors
+ * (see {Line Separator and Line Limit}[rdoc-ref:IO@Line+Separator+and+Line+Limit]).
*
* Optional keyword argument +chomp+ specifies whether line separators
* are to be omitted:
@@ -5092,7 +5107,7 @@ rb_io_ungetbyte(VALUE io, VALUE b)
b = rb_str_new((const char *)&c, 1);
break;
default:
- SafeStringValue(b);
+ StringValue(b);
}
io_ungetbyte(b, fptr);
return Qnil;
@@ -5154,7 +5169,7 @@ rb_io_ungetc(VALUE io, VALUE c)
c = rb_enc_uint_chr(NUM2UINT(c), io_read_encoding(fptr));
}
else {
- SafeStringValue(c);
+ StringValue(c);
}
if (NEED_READCONV(fptr)) {
SET_BINARY_MODE(fptr);
@@ -5255,7 +5270,7 @@ rb_io_close_on_exec_p(VALUE io)
*
* Sets a close-on-exec flag.
*
- * f = open("/dev/null")
+ * f = File.open(File::NULL)
* f.close_on_exec = true
* system("cat", "/proc/self/fd/#{f.fileno}") # cat: /proc/self/fd/3: No such file or directory
* f.closed? #=> false
@@ -5400,7 +5415,7 @@ maygvl_close(int fd, int keepgvl)
* close() may block for certain file types (NFS, SO_LINGER sockets,
* inotify), so let other threads run.
*/
- return (int)(intptr_t)rb_thread_call_without_gvl(nogvl_close, &fd, RUBY_UBF_IO, 0);
+ return IO_WITHOUT_GVL_INT(nogvl_close, &fd);
}
static void*
@@ -5417,7 +5432,7 @@ maygvl_fclose(FILE *file, int keepgvl)
if (keepgvl)
return fclose(file);
- return (int)(intptr_t)rb_thread_call_without_gvl(nogvl_fclose, file, RUBY_UBF_IO, 0);
+ return IO_WITHOUT_GVL_INT(nogvl_fclose, file);
}
static void free_io_buffer(rb_io_buffer_t *buf);
@@ -5570,12 +5585,9 @@ clear_codeconv(rb_io_t *fptr)
clear_writeconv(fptr);
}
-void
-rb_io_fptr_finalize_internal(void *ptr)
+static void
+rb_io_fptr_cleanup_all(rb_io_t *fptr)
{
- rb_io_t *fptr = ptr;
-
- if (!ptr) return;
fptr->pathv = Qnil;
if (0 <= fptr->fd)
rb_io_fptr_cleanup(fptr, TRUE);
@@ -5583,7 +5595,14 @@ rb_io_fptr_finalize_internal(void *ptr)
free_io_buffer(&fptr->rbuf);
free_io_buffer(&fptr->wbuf);
clear_codeconv(fptr);
- free(fptr);
+}
+
+void
+rb_io_fptr_finalize_internal(void *ptr)
+{
+ if (!ptr) return;
+ rb_io_fptr_cleanup_all(ptr);
+ free(ptr);
}
#undef rb_io_fptr_finalize
@@ -5600,7 +5619,7 @@ rb_io_fptr_finalize(rb_io_t *fptr)
}
#define rb_io_fptr_finalize(fptr) rb_io_fptr_finalize_internal(fptr)
-RUBY_FUNC_EXPORTED size_t
+size_t
rb_io_memsize(const rb_io_t *fptr)
{
size_t size = sizeof(rb_io_t);
@@ -6092,7 +6111,7 @@ pread_internal_call(VALUE _arg)
}
}
- return rb_thread_io_blocking_region(internal_pread_func, arg, arg->fd);
+ return rb_thread_io_blocking_call(internal_pread_func, arg, arg->fd, RB_WAITFD_IN);
}
/*
@@ -6225,7 +6244,7 @@ rb_io_pwrite(VALUE io, VALUE str, VALUE offset)
arg.buf = RSTRING_PTR(tmp);
arg.count = (size_t)RSTRING_LEN(tmp);
- n = (ssize_t)rb_thread_io_blocking_region(internal_pwrite_func, &arg, fptr->fd);
+ n = (ssize_t)rb_thread_io_blocking_call(internal_pwrite_func, &arg, fptr->fd, RB_WAITFD_OUT);
if (n < 0) rb_sys_fail_path(fptr->pathv);
rb_str_tmp_frozen_release(str, tmp);
@@ -6801,7 +6820,7 @@ rb_io_extract_modeenc(VALUE *vmode_p, VALUE *vperm_p, VALUE opthash,
else {
const char *p;
- SafeStringValue(vmode);
+ StringValue(vmode);
p = StringValueCStr(vmode);
fmode = rb_io_modestr_fmode(p);
oflags = rb_io_fmode_oflags(fmode);
@@ -6934,8 +6953,7 @@ sysopen_func(void *ptr)
static inline int
rb_sysopen_internal(struct sysopen_struct *data)
{
- int fd;
- fd = (int)(VALUE)rb_thread_call_without_gvl(sysopen_func, data, RUBY_UBF_IO, 0);
+ int fd = IO_WITHOUT_GVL_INT(sysopen_func, data);
if (0 <= fd)
rb_update_max_fd(fd);
return fd;
@@ -6944,7 +6962,7 @@ rb_sysopen_internal(struct sysopen_struct *data)
static int
rb_sysopen(VALUE fname, int oflags, mode_t perm)
{
- int fd;
+ int fd = -1;
struct sysopen_struct data;
data.fname = rb_str_encode_ospath(fname);
@@ -6952,21 +6970,14 @@ rb_sysopen(VALUE fname, int oflags, mode_t perm)
data.oflags = oflags;
data.perm = perm;
- fd = rb_sysopen_internal(&data);
- if (fd < 0) {
- int e = errno;
- if (rb_gc_for_fd(e)) {
- fd = rb_sysopen_internal(&data);
- }
- if (fd < 0) {
- rb_syserr_fail_path(e, fname);
- }
+ TRY_WITH_GC((fd = rb_sysopen_internal(&data)) >= 0) {
+ rb_syserr_fail_path(first_errno, fname);
}
return fd;
}
-FILE *
-rb_fdopen(int fd, const char *modestr)
+static inline FILE *
+fdopen_internal(int fd, const char *modestr)
{
FILE *file;
@@ -6975,26 +6986,22 @@ rb_fdopen(int fd, const char *modestr)
#endif
file = fdopen(fd, modestr);
if (!file) {
- int e = errno;
-#if defined(__sun)
- if (e == 0) {
- rb_gc();
- errno = 0;
- file = fdopen(fd, modestr);
- }
- else
-#endif
- if (rb_gc_for_fd(e)) {
- file = fdopen(fd, modestr);
- }
- if (!file) {
#ifdef _WIN32
- if (e == 0) e = EINVAL;
+ if (errno == 0) errno = EINVAL;
#elif defined(__sun)
- if (e == 0) e = EMFILE;
+ if (errno == 0) errno = EMFILE;
#endif
- rb_syserr_fail(e, 0);
- }
+ }
+ return file;
+}
+
+FILE *
+rb_fdopen(int fd, const char *modestr)
+{
+ FILE *file = 0;
+
+ TRY_WITH_GC((file = fdopen_internal(fd, modestr)) != 0) {
+ rb_syserr_fail(first_errno, 0);
}
/* xxx: should be _IONBF? A buffer in FILE may have trouble. */
@@ -7144,8 +7151,6 @@ rb_file_open_internal(VALUE io, VALUE filename, const char *modestr)
if (p) {
parse_mode_enc(p+1, rb_usascii_encoding(),
&convconfig.enc, &convconfig.enc2, &fmode);
- convconfig.ecflags = 0;
- convconfig.ecopts = Qnil;
}
else {
rb_encoding *e;
@@ -7153,10 +7158,19 @@ rb_file_open_internal(VALUE io, VALUE filename, const char *modestr)
e = (fmode & FMODE_BINMODE) ? rb_ascii8bit_encoding() : NULL;
rb_io_ext_int_to_encs(e, NULL, &convconfig.enc, &convconfig.enc2, fmode);
- convconfig.ecflags = 0;
- convconfig.ecopts = Qnil;
}
+ convconfig.ecflags = (fmode & FMODE_READABLE) ?
+ MODE_BTMODE(ECONV_DEFAULT_NEWLINE_DECORATOR,
+ 0, ECONV_UNIVERSAL_NEWLINE_DECORATOR) : 0;
+#ifdef TEXTMODE_NEWLINE_DECORATOR_ON_WRITE
+ convconfig.ecflags |= (fmode & FMODE_WRITABLE) ?
+ MODE_BTMODE(TEXTMODE_NEWLINE_DECORATOR_ON_WRITE,
+ 0, TEXTMODE_NEWLINE_DECORATOR_ON_WRITE) : 0;
+#endif
+ SET_UNIVERSAL_NEWLINE_DECORATOR_IF_ENC2(convconfig.enc2, convconfig.ecflags);
+ convconfig.ecopts = Qnil;
+
return rb_file_open_generic(io, filename,
rb_io_fmode_oflags(fmode),
fmode,
@@ -7285,12 +7299,7 @@ int
rb_pipe(int *pipes)
{
int ret;
- ret = rb_cloexec_pipe(pipes);
- if (ret < 0) {
- if (rb_gc_for_fd(errno)) {
- ret = rb_cloexec_pipe(pipes);
- }
- }
+ TRY_WITH_GC((ret = rb_cloexec_pipe(pipes)) >= 0);
if (ret == 0) {
rb_update_max_fd(pipes[0]);
rb_update_max_fd(pipes[1]);
@@ -7926,7 +7935,7 @@ rb_io_popen(VALUE pname, VALUE pmode, VALUE env, VALUE opt)
RB_GC_GUARD(tmp);
}
else {
- SafeStringValue(pname);
+ StringValue(pname);
execarg_obj = Qnil;
if (!is_popen_fork(pname))
execarg_obj = rb_execarg_new(1, &pname, TRUE, FALSE);
@@ -7963,6 +7972,60 @@ popen_finish(VALUE port, VALUE klass)
return port;
}
+#if defined(HAVE_WORKING_FORK) && !defined(__EMSCRIPTEN__)
+struct popen_writer_arg {
+ char *const *argv;
+ struct popen_arg popen;
+};
+
+static int
+exec_popen_writer(void *arg, char *errmsg, size_t buflen)
+{
+ struct popen_writer_arg *pw = arg;
+ pw->popen.modef = FMODE_WRITABLE;
+ popen_redirect(&pw->popen);
+ execv(pw->argv[0], pw->argv);
+ strlcpy(errmsg, strerror(errno), buflen);
+ return -1;
+}
+#endif
+
+FILE *
+ruby_popen_writer(char *const *argv, rb_pid_t *pid)
+{
+#if (defined(HAVE_WORKING_FORK) && !defined(__EMSCRIPTEN__)) || defined(_WIN32)
+# ifdef HAVE_WORKING_FORK
+ struct popen_writer_arg pw;
+ int *const write_pair = pw.popen.pair;
+# else
+ int write_pair[2];
+# endif
+
+ int result = rb_cloexec_pipe(write_pair);
+ *pid = -1;
+ if (result == 0) {
+# ifdef HAVE_WORKING_FORK
+ pw.argv = argv;
+ int status;
+ char errmsg[80] = {'\0'};
+ *pid = rb_fork_async_signal_safe(&status, exec_popen_writer, &pw, Qnil, errmsg, sizeof(errmsg));
+# else
+ *pid = rb_w32_uspawn_process(P_NOWAIT, argv[0], argv, write_pair[0], -1, -1, 0);
+ const char *errmsg = (*pid < 0) ? strerror(errno) : NULL;
+# endif
+ close(write_pair[0]);
+ if (*pid < 0) {
+ close(write_pair[1]);
+ fprintf(stderr, "ruby_popen_writer(%s): %s\n", argv[0], errmsg);
+ }
+ else {
+ return fdopen(write_pair[1], "w");
+ }
+ }
+#endif
+ return NULL;
+}
+
static void
rb_scan_open_args(int argc, const VALUE *argv,
VALUE *fname_p, int *oflags_p, int *fmode_p,
@@ -8006,11 +8069,11 @@ rb_open_file(int argc, const VALUE *argv, VALUE io)
* File.open(path, mode = 'r', perm = 0666, **opts) -> file
* File.open(path, mode = 'r', perm = 0666, **opts) {|f| ... } -> object
*
- * Creates a new \File object, via File.new with the given arguments.
+ * Creates a new File object, via File.new with the given arguments.
*
- * With no block given, returns the \File object.
+ * With no block given, returns the File object.
*
- * With a block given, calls the block with the \File object
+ * With a block given, calls the block with the File object
* and returns the block's value.
*
*/
@@ -8077,7 +8140,7 @@ rb_io_s_sysopen(int argc, VALUE *argv, VALUE _)
else if (!NIL_P(intmode = rb_check_to_integer(vmode, "to_int")))
oflags = NUM2INT(intmode);
else {
- SafeStringValue(vmode);
+ StringValue(vmode);
oflags = rb_io_modestr_oflags(StringValueCStr(vmode));
}
if (NIL_P(vperm)) perm = 0666;
@@ -8108,20 +8171,10 @@ check_pipe_command(VALUE filename_or_command)
* open(path, mode = 'r', perm = 0666, **opts) -> io or nil
* open(path, mode = 'r', perm = 0666, **opts) {|io| ... } -> obj
*
- * Creates an IO object connected to the given stream, file, or subprocess.
- *
- * Required string argument +path+ determines which of the following occurs:
+ * Creates an IO object connected to the given file.
*
- * - The file at the specified +path+ is opened.
- * - The process forks.
- * - A subprocess is created.
- *
- * Each of these is detailed below.
- *
- * <b>File Opened</b>
-
- * If +path+ does _not_ start with a pipe character (<tt>'|'</tt>),
- * a file stream is opened with <tt>File.open(path, mode, perm, **opts)</tt>.
+ * This method has potential security vulnerabilities if called with untrusted input;
+ * see {Command Injection}[rdoc-ref:command_injection.rdoc].
*
* With no block given, file stream is returned:
*
@@ -8138,67 +8191,6 @@ check_pipe_command(VALUE filename_or_command)
*
* See File.open for details.
*
- * <b>Process Forked</b>
- *
- * If +path+ is the 2-character string <tt>'|-'</tt>, the process forks
- * and the child process is connected to the parent.
- *
- * With no block given:
- *
- * io = open('|-')
- * if io
- * $stderr.puts "In parent, child pid is #{io.pid}."
- * else
- * $stderr.puts "In child, pid is #{$$}."
- * end
- *
- * Output:
- *
- * In parent, child pid is 27903.
- * In child, pid is 27903.
- *
- * With a block given:
- *
- * open('|-') do |io|
- * if io
- * $stderr.puts "In parent, child pid is #{io.pid}."
- * else
- * $stderr.puts "In child, pid is #{$$}."
- * end
- * end
- *
- * Output:
- *
- * In parent, child pid is 28427.
- * In child, pid is 28427.
- *
- * <b>Subprocess Created</b>
- *
- * If +path+ is <tt>'|command'</tt> (<tt>'command' != '-'</tt>),
- * a new subprocess runs the command; its open stream is returned.
- * Note that the command may be processed by shell if it contains
- * shell metacharacters.
- *
- * With no block given:
- *
- * io = open('|echo "Hi!"') # => #<IO:fd 12>
- * print io.gets
- * io.close
- *
- * Output:
- *
- * "Hi!"
- *
- * With a block given, calls the block with the stream, then closes the stream:
- *
- * open('|echo "Hi!"') do |io|
- * print io.gets
- * end
- *
- * Output:
- *
- * "Hi!"
- *
*/
static VALUE
@@ -8221,6 +8213,8 @@ rb_f_open(int argc, VALUE *argv, VALUE _)
else {
VALUE cmd = check_pipe_command(tmp);
if (!NIL_P(cmd)) {
+ // TODO: when removed in 4.0, update command_injection.rdoc
+ rb_warn_deprecated_to_remove_at(4.0, "Calling Kernel#open with a leading '|'", "IO.popen");
argv[0] = cmd;
return rb_io_s_popen(argc, argv, rb_cIO);
}
@@ -8258,6 +8252,8 @@ rb_io_open_generic(VALUE klass, VALUE filename, int oflags, int fmode,
{
VALUE cmd;
if (klass == rb_cIO && !NIL_P(cmd = check_pipe_command(filename))) {
+ // TODO: when removed in 4.0, update command_injection.rdoc
+ rb_warn_deprecated_to_remove_at(4.0, "IO process creation with a leading '|'", "IO.popen");
return pipe_open_s(cmd, rb_io_oflags_modestr(oflags), fmode, convconfig);
}
else {
@@ -8604,7 +8600,7 @@ deprecated_str_setter(VALUE val, ID id, VALUE *var)
{
rb_str_setter(val, id, &val);
if (!NIL_P(val)) {
- rb_warn_deprecated("`%s'", NULL, rb_id2name(id));
+ rb_warn_deprecated("'%s'", NULL, rb_id2name(id));
}
*var = val;
}
@@ -8865,9 +8861,9 @@ io_puts_ary(VALUE ary, VALUE out, int recur)
*
* Treatment for each object:
*
- * - \String: writes the string.
+ * - String: writes the string.
* - Neither string nor array: writes <tt>object.to_s</tt>.
- * - \Array: writes each element of the array; arrays may be nested.
+ * - Array: writes each element of the array; arrays may be nested.
*
* To keep these examples brief, we define this helper method:
*
@@ -9023,6 +9019,10 @@ rb_p_result(int argc, const VALUE *argv)
* 0..4
* [0..4, 0..4, 0..4]
*
+ * Kernel#p is designed for debugging purposes.
+ * Ruby implementations may define Kernel#p to be uninterruptible
+ * in whole or in part.
+ * On CRuby, Kernel#p's writing of data is uninterruptible.
*/
static VALUE
@@ -9233,11 +9233,27 @@ static VALUE
prep_io(int fd, int fmode, VALUE klass, const char *path)
{
VALUE path_value = Qnil;
+ rb_encoding *e;
+ struct rb_io_encoding convconfig;
+
if (path) {
path_value = rb_obj_freeze(rb_str_new_cstr(path));
}
- VALUE self = rb_io_open_descriptor(klass, fd, fmode, path_value, Qnil, NULL);
+ e = (fmode & FMODE_BINMODE) ? rb_ascii8bit_encoding() : NULL;
+ rb_io_ext_int_to_encs(e, NULL, &convconfig.enc, &convconfig.enc2, fmode);
+ convconfig.ecflags = (fmode & FMODE_READABLE) ?
+ MODE_BTMODE(ECONV_DEFAULT_NEWLINE_DECORATOR,
+ 0, ECONV_UNIVERSAL_NEWLINE_DECORATOR) : 0;
+#ifdef TEXTMODE_NEWLINE_DECORATOR_ON_WRITE
+ convconfig.ecflags |= (fmode & FMODE_WRITABLE) ?
+ MODE_BTMODE(TEXTMODE_NEWLINE_DECORATOR_ON_WRITE,
+ 0, TEXTMODE_NEWLINE_DECORATOR_ON_WRITE) : 0;
+#endif
+ SET_UNIVERSAL_NEWLINE_DECORATOR_IF_ENC2(convconfig.enc2, convconfig.ecflags);
+ convconfig.ecopts = Qnil;
+
+ VALUE self = rb_io_open_descriptor(klass, fd, fmode, path_value, Qnil, &convconfig);
rb_io_t*io = RFILE(self)->fptr;
if (!io_check_tty(io)) {
@@ -9526,9 +9542,9 @@ rb_io_set_encoding_by_bom(VALUE io)
* File.new(path, mode = 'r', perm = 0666, **opts) -> file
*
* Opens the file at the given +path+ according to the given +mode+;
- * creates and returns a new \File object for that file.
+ * creates and returns a new File object for that file.
*
- * The new \File object is buffered mode (or non-sync mode), unless
+ * The new File object is buffered mode (or non-sync mode), unless
* +filename+ is a tty.
* See IO#flush, IO#fsync, IO#fdatasync, and IO#sync=.
*
@@ -9633,11 +9649,11 @@ rb_io_autoclose_p(VALUE io)
*
* Sets auto-close flag.
*
- * f = open("/dev/null")
+ * f = File.open(File::NULL)
* IO.for_fd(f.fileno).close
* f.gets # raises Errno::EBADF
*
- * f = open("/dev/null")
+ * f = File.open(File::NULL)
* g = IO.for_fd(f.fileno)
* g.autoclose = false
* g.close
@@ -10445,8 +10461,9 @@ static VALUE argf_readlines(int, VALUE *, VALUE);
* $cat t.txt | ruby -e "p readlines 12"
* ["First line\n", "Second line\n", "\n", "Fourth line\n", "Fifth line\n"]
*
- * With arguments +sep+ and +limit+ given, combines the two behaviors;
- * see {Line Separator and Line Limit}[rdoc-ref:IO@Line+Separator+and+Line+Limit].
+ * With arguments +sep+ and +limit+ given,
+ * combines the two behaviors
+ * (see {Line Separator and Line Limit}[rdoc-ref:IO@Line+Separator+and+Line+Limit]).
*
* Optional keyword argument +chomp+ specifies whether line separators
* are to be omitted:
@@ -10470,19 +10487,21 @@ rb_f_readlines(int argc, VALUE *argv, VALUE recv)
/*
* call-seq:
- * ARGF.readlines(sep = $/) -> array
- * ARGF.readlines(limit) -> array
- * ARGF.readlines(sep, limit) -> array
+ * ARGF.readlines(sep = $/, chomp: false) -> array
+ * ARGF.readlines(limit, chomp: false) -> array
+ * ARGF.readlines(sep, limit, chomp: false) -> array
*
- * ARGF.to_a(sep = $/) -> array
- * ARGF.to_a(limit) -> array
- * ARGF.to_a(sep, limit) -> array
+ * ARGF.to_a(sep = $/, chomp: false) -> array
+ * ARGF.to_a(limit, chomp: false) -> array
+ * ARGF.to_a(sep, limit, chomp: false) -> array
*
* Reads each file in ARGF in its entirety, returning an Array containing
* lines from the files. Lines are assumed to be separated by _sep_.
*
* lines = ARGF.readlines
* lines[0] #=> "This is line one\n"
+ *
+ * See +IO.readlines+ for a full description of all options.
*/
static VALUE
argf_readlines(int argc, VALUE *argv, VALUE argf)
@@ -10536,17 +10555,15 @@ rb_f_backquote(VALUE obj, VALUE str)
VALUE result;
rb_io_t *fptr;
- SafeStringValue(str);
+ StringValue(str);
rb_last_status_clear();
port = pipe_open_s(str, "r", FMODE_READABLE|DEFAULT_TEXTMODE, NULL);
if (NIL_P(port)) return rb_str_new(0,0);
GetOpenFile(port, fptr);
- rb_obj_hide(port);
result = read_all(fptr, remain_size(fptr), Qnil);
rb_io_close(port);
- RFILE(port)->fptr = NULL;
- rb_io_fptr_finalize(fptr);
+ rb_io_fptr_cleanup_all(fptr);
RB_GC_GUARD(port);
return result;
@@ -11447,7 +11464,7 @@ rb_fcntl(VALUE io, VALUE req, VALUE arg)
* a file-oriented I/O stream. Arguments and results are platform
* dependent.
*
- * If +argument is a number, its value is passed directly;
+ * If +argument+ is a number, its value is passed directly;
* if it is a string, it is interpreted as a binary sequence of bytes.
* (Array#pack might be a useful way to build this string.)
*
@@ -11544,7 +11561,7 @@ rb_f_syscall(int argc, VALUE *argv, VALUE _)
VALUE v = rb_check_string_type(argv[i]);
if (!NIL_P(v)) {
- SafeStringValue(v);
+ StringValue(v);
rb_str_modify(v);
arg[i] = (VALUE)StringValueCStr(v);
}
@@ -11913,9 +11930,6 @@ io_s_foreach(VALUE v)
* IO.foreach(path, sep = $/, **opts) {|line| block } -> nil
* IO.foreach(path, limit, **opts) {|line| block } -> nil
* IO.foreach(path, sep, limit, **opts) {|line| block } -> nil
- * IO.foreach(command, sep = $/, **opts) {|line| block } -> nil
- * IO.foreach(command, limit, **opts) {|line| block } -> nil
- * IO.foreach(command, sep, limit, **opts) {|line| block } -> nil
* IO.foreach(...) -> an_enumerator
*
* Calls the block with each successive line read from the stream.
@@ -11924,16 +11938,7 @@ io_s_foreach(VALUE v)
* this method has potential security vulnerabilities if called with untrusted input;
* see {Command Injection}[rdoc-ref:command_injection.rdoc].
*
- * The first argument must be a string that is one of the following:
- *
- * - Path: if +self+ is a subclass of \IO (\File, for example),
- * or if the string _does_ _not_ start with the pipe character (<tt>'|'</tt>),
- * the string is the path to a file.
- * - Command: if +self+ is the class \IO,
- * and if the string starts with the pipe character,
- * the rest of the string is a command to be executed as a subprocess.
- * This usage has potential security vulnerabilities if called with untrusted input;
- * see {Command Injection}[rdoc-ref:command_injection.rdoc].
+ * The first argument must be a string that is the path to a file.
*
* With only argument +path+ given, parses lines from the file at the given +path+,
* as determined by the default line separator,
@@ -11969,7 +11974,7 @@ io_s_foreach(VALUE v)
*
* With argument +limit+ given, parses lines as determined by the default
* line separator and the given line-length limit
- * (see {Line Limit}[rdoc-ref:IO@Line+Limit]):
+ * (see {Line Separator}[rdoc-ref:IO@Line+Separator] and {Line Limit}[rdoc-ref:IO@Line+Limit]):
*
* File.foreach('t.txt', 7) {|line| p line }
*
@@ -11985,16 +11990,15 @@ io_s_foreach(VALUE v)
* "Fourth l"
* "line\n"
*
- * With arguments +sep+ and +limit+ given,
- * parses lines as determined by the given
- * line separator and the given line-length limit
- * (see {Line Separator and Line Limit}[rdoc-ref:IO@Line+Separator+and+Line+Limit]):
+ * With arguments +sep+ and +limit+ given,
+ * combines the two behaviors
+ * (see {Line Separator and Line Limit}[rdoc-ref:IO@Line+Separator+and+Line+Limit]).
*
* Optional keyword arguments +opts+ specify:
*
* - {Open Options}[rdoc-ref:IO@Open+Options].
* - {Encoding options}[rdoc-ref:encodings.rdoc@Encoding+Options].
- * - {Line Options}[rdoc-ref:IO@Line+Options].
+ * - {Line Options}[rdoc-ref:IO@Line+IO].
*
* Returns an Enumerator if no block is given.
*
@@ -12027,9 +12031,6 @@ io_s_readlines(VALUE v)
/*
* call-seq:
- * IO.readlines(command, sep = $/, **opts) -> array
- * IO.readlines(command, limit, **opts) -> array
- * IO.readlines(command, sep, limit, **opts) -> array
* IO.readlines(path, sep = $/, **opts) -> array
* IO.readlines(path, limit, **opts) -> array
* IO.readlines(path, sep, limit, **opts) -> array
@@ -12040,19 +12041,7 @@ io_s_readlines(VALUE v)
* this method has potential security vulnerabilities if called with untrusted input;
* see {Command Injection}[rdoc-ref:command_injection.rdoc].
*
- * The first argument must be a string;
- * its meaning depends on whether it starts with the pipe character (<tt>'|'</tt>):
- *
- * - If so (and if +self+ is \IO),
- * the rest of the string is a command to be executed as a subprocess.
- * - Otherwise, the string is the path to a file.
- *
- * With only argument +command+ given, executes the command in a shell,
- * parses its $stdout into lines, as determined by the default line separator,
- * and returns those lines in an array:
- *
- * IO.readlines('| cat t.txt')
- * # => ["First line\n", "Second line\n", "\n", "Third line\n", "Fourth line\n"]
+ * The first argument must be a string that is the path to a file.
*
* With only argument +path+ given, parses lines from the file at the given +path+,
* as determined by the default line separator,
@@ -12061,8 +12050,6 @@ io_s_readlines(VALUE v)
* IO.readlines('t.txt')
* # => ["First line\n", "Second line\n", "\n", "Third line\n", "Fourth line\n"]
*
- * For both forms, command and path, the remaining arguments are the same.
- *
* With argument +sep+ given, parses lines as determined by that line separator
* (see {Line Separator}[rdoc-ref:IO@Line+Separator]):
*
@@ -12078,21 +12065,20 @@ io_s_readlines(VALUE v)
*
* With argument +limit+ given, parses lines as determined by the default
* line separator and the given line-length limit
- * (see {Line Limit}[rdoc-ref:IO@Line+Limit]):
+ * (see {Line Separator}[rdoc-ref:IO@Line+Separator] and {Line Limit}[rdoc-ref:IO@Line+Limit]:
*
* IO.readlines('t.txt', 7)
* # => ["First l", "ine\n", "Second ", "line\n", "\n", "Third l", "ine\n", "Fourth ", "line\n"]
*
- * With arguments +sep+ and +limit+ given,
- * parses lines as determined by the given
- * line separator and the given line-length limit
- * (see {Line Separator and Line Limit}[rdoc-ref:IO@Line+Separator+and+Line+Limit]):
+ * With arguments +sep+ and +limit+ given,
+ * combines the two behaviors
+ * (see {Line Separator and Line Limit}[rdoc-ref:IO@Line+Separator+and+Line+Limit]).
*
* Optional keyword arguments +opts+ specify:
*
* - {Open Options}[rdoc-ref:IO@Open+Options].
* - {Encoding options}[rdoc-ref:encodings.rdoc@Encoding+Options].
- * - {Line Options}[rdoc-ref:IO@Line+Options].
+ * - {Line Options}[rdoc-ref:IO@Line+IO].
*
*/
@@ -12135,7 +12121,6 @@ seek_before_access(VALUE argp)
/*
* call-seq:
- * IO.read(command, length = nil, offset = 0, **opts) -> string or nil
* IO.read(path, length = nil, offset = 0, **opts) -> string or nil
*
* Opens the stream, reads and returns some or all of its content,
@@ -12145,18 +12130,7 @@ seek_before_access(VALUE argp)
* this method has potential security vulnerabilities if called with untrusted input;
* see {Command Injection}[rdoc-ref:command_injection.rdoc].
*
- * The first argument must be a string;
- * its meaning depends on whether it starts with the pipe character (<tt>'|'</tt>):
- *
- * - If so (and if +self+ is \IO),
- * the rest of the string is a command to be executed as a subprocess.
- * - Otherwise, the string is the path to a file.
- *
- * With only argument +command+ given, executes the command in a shell,
- * returns its entire $stdout:
- *
- * IO.read('| cat t.txt')
- * # => "First line\nSecond line\n\nThird line\nFourth line\n"
+ * The first argument must be a string that is the path to a file.
*
* With only argument +path+ given, reads in text mode and returns the entire content
* of the file at the given path:
@@ -12168,8 +12142,6 @@ seek_before_access(VALUE argp)
* unread when encountering certain special bytes. Consider using
* IO.binread if all bytes in the file should be read.
*
- * For both forms, command and path, the remaining arguments are the same.
- *
* With argument +length+, returns +length+ bytes if available:
*
* IO.read('t.txt', 7) # => "First l"
@@ -12220,7 +12192,6 @@ rb_io_s_read(int argc, VALUE *argv, VALUE io)
/*
* call-seq:
- * IO.binread(command, length = nil, offset = 0) -> string or nil
* IO.binread(path, length = nil, offset = 0) -> string or nil
*
* Behaves like IO.read, except that the stream is opened in binary mode
@@ -12325,7 +12296,6 @@ io_s_write(int argc, VALUE *argv, VALUE klass, int binary)
/*
* call-seq:
- * IO.write(command, data, **opts) -> integer
* IO.write(path, data, offset = 0, **opts) -> integer
*
* Opens the stream, writes the given +data+ to it,
@@ -12335,25 +12305,9 @@ io_s_write(int argc, VALUE *argv, VALUE klass, int binary)
* this method has potential security vulnerabilities if called with untrusted input;
* see {Command Injection}[rdoc-ref:command_injection.rdoc].
*
- * The first argument must be a string;
- * its meaning depends on whether it starts with the pipe character (<tt>'|'</tt>):
- *
- * - If so (and if +self+ is \IO),
- * the rest of the string is a command to be executed as a subprocess.
- * - Otherwise, the string is the path to a file.
- *
- * With argument +command+ given, executes the command in a shell,
- * passes +data+ through standard input, writes its output to $stdout,
- * and returns the length of the given +data+:
+ * The first argument must be a string that is the path to a file.
*
- * IO.write('| cat', 'Hello World!') # => 12
- *
- * Output:
- *
- * Hello World!
- *
- * With argument +path+ given, writes the given +data+ to the file
- * at that path:
+ * With only argument +path+ given, writes the given +data+ to the file at that path:
*
* IO.write('t.tmp', 'abc') # => 3
* File.read('t.tmp') # => "abc"
@@ -12392,7 +12346,6 @@ rb_io_s_write(int argc, VALUE *argv, VALUE io)
/*
* call-seq:
- * IO.binwrite(command, string, offset = 0) -> integer
* IO.binwrite(path, string, offset = 0) -> integer
*
* Behaves like IO.write, except that the stream is opened in binary mode
@@ -13269,7 +13222,7 @@ copy_stream_body(VALUE arg)
rb_str_resize(str,len);
read_buffered_data(RSTRING_PTR(str), len, stp->src_fptr);
if (stp->dst_fptr) { /* IO or filename */
- if (io_binwrite(str, RSTRING_PTR(str), RSTRING_LEN(str), stp->dst_fptr, 0) < 0)
+ if (io_binwrite(RSTRING_PTR(str), RSTRING_LEN(str), stp->dst_fptr, 0) < 0)
rb_sys_fail_on_write(stp->dst_fptr);
}
else /* others such as StringIO */
@@ -13291,7 +13244,7 @@ copy_stream_body(VALUE arg)
return copy_stream_fallback(stp);
}
- rb_thread_call_without_gvl(nogvl_copy_stream_func, (void*)stp, RUBY_UBF_IO, 0);
+ IO_WITHOUT_GVL(nogvl_copy_stream_func, stp);
return Qnil;
}
@@ -14618,14 +14571,14 @@ argf_write_io(VALUE argf)
/*
* call-seq:
- * ARGF.write(string) -> integer
+ * ARGF.write(*objects) -> integer
*
- * Writes _string_ if inplace mode.
+ * Writes each of the given +objects+ if inplace mode.
*/
static VALUE
-argf_write(VALUE argf, VALUE str)
+argf_write(int argc, VALUE *argv, VALUE argf)
{
- return rb_io_write(argf_write_io(argf), str);
+ return rb_io_writev(argf_write_io(argf), argc, argv);
}
void
@@ -14731,42 +14684,253 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
/*
* Document-class: ARGF
*
- * ARGF is a stream designed for use in scripts that process files given as
- * command-line arguments or passed in via STDIN.
+ * == \ARGF and +ARGV+
+ *
+ * The \ARGF object works with the array at global variable +ARGV+
+ * to make <tt>$stdin</tt> and file streams available in the Ruby program:
+ *
+ * - **ARGV** may be thought of as the <b>argument vector</b> array.
+ *
+ * Initially, it contains the command-line arguments and options
+ * that are passed to the Ruby program;
+ * the program can modify that array as it likes.
+ *
+ * - **ARGF** may be thought of as the <b>argument files</b> object.
+ *
+ * It can access file streams and/or the <tt>$stdin</tt> stream,
+ * based on what it finds in +ARGV+.
+ * This provides a convenient way for the command line
+ * to specify streams for a Ruby program to read.
+ *
+ * == Reading
+ *
+ * \ARGF may read from _source_ streams,
+ * which at any particular time are determined by the content of +ARGV+.
+ *
+ * === Simplest Case
+ *
+ * When the <i>very first</i> \ARGF read occurs with an empty +ARGV+ (<tt>[]</tt>),
+ * the source is <tt>$stdin</tt>:
+ *
+ * - \File +t.rb+:
+ *
+ * p ['ARGV', ARGV]
+ * p ['ARGF.read', ARGF.read]
+ *
+ * - Commands and outputs
+ * (see below for the content of files +foo.txt+ and +bar.txt+):
+ *
+ * $ echo "Open the pod bay doors, Hal." | ruby t.rb
+ * ["ARGV", []]
+ * ["ARGF.read", "Open the pod bay doors, Hal.\n"]
+ *
+ * $ cat foo.txt bar.txt | ruby t.rb
+ * ["ARGV", []]
+ * ["ARGF.read", "Foo 0\nFoo 1\nBar 0\nBar 1\nBar 2\nBar 3\n"]
+ *
+ * === About the Examples
+ *
+ * Many examples here assume the existence of files +foo.txt+ and +bar.txt+:
+ *
+ * $ cat foo.txt
+ * Foo 0
+ * Foo 1
+ * $ cat bar.txt
+ * Bar 0
+ * Bar 1
+ * Bar 2
+ * Bar 3
+ *
+ * === Sources in +ARGV+
+ *
+ * For any \ARGF read _except_ the {simplest case}[rdoc-ref:ARGF@Simplest+Case]
+ * (that is, _except_ for the <i>very first</i> \ARGF read with an empty +ARGV+),
+ * the sources are found in +ARGV+.
+ *
+ * \ARGF assumes that each element in array +ARGV+ is a potential source,
+ * and is one of:
+ *
+ * - The string path to a file that may be opened as a stream.
+ * - The character <tt>'-'</tt>, meaning stream <tt>$stdin</tt>.
+ *
+ * Each element that is _not_ one of these
+ * should be removed from +ARGV+ before \ARGF accesses that source.
+ *
+ * In the following example:
+ *
+ * - Filepaths +foo.txt+ and +bar.txt+ may be retained as potential sources.
+ * - Options <tt>--xyzzy</tt> and <tt>--mojo</tt> should be removed.
+ *
+ * Example:
+ *
+ * - \File +t.rb+:
+ *
+ * # Print arguments (and options, if any) found on command line.
+ * p ['ARGV', ARGV]
+ *
+ * - Command and output:
+ *
+ * $ ruby t.rb --xyzzy --mojo foo.txt bar.txt
+ * ["ARGV", ["--xyzzy", "--mojo", "foo.txt", "bar.txt"]]
+ *
+ * \ARGF's stream access considers the elements of +ARGV+, left to right:
+ *
+ * - \File +t.rb+:
+ *
+ * p "ARGV: #{ARGV}"
+ * p "Line: #{ARGF.read}" # Read everything from all specified streams.
+ *
+ * - Command and output:
+ *
+ * $ ruby t.rb foo.txt bar.txt
+ * "ARGV: [\"foo.txt\", \"bar.txt\"]"
+ * "Read: Foo 0\nFoo 1\nBar 0\nBar 1\nBar 2\nBar 3\n"
+ *
+ * Because the value at +ARGV+ is an ordinary array,
+ * you can manipulate it to control which sources \ARGF considers:
+ *
+ * - If you remove an element from +ARGV+, \ARGF will not consider the corresponding source.
+ * - If you add an element to +ARGV+, \ARGF will consider the corresponding source.
+ *
+ * Each element in +ARGV+ is removed when its corresponding source is accessed;
+ * when all sources have been accessed, the array is empty:
+ *
+ * - \File +t.rb+:
*
- * The arguments passed to your script are stored in the +ARGV+ Array, one
- * argument per element. ARGF assumes that any arguments that aren't
- * filenames have been removed from +ARGV+. For example:
+ * until ARGV.empty? && ARGF.eof?
+ * p "ARGV: #{ARGV}"
+ * p "Line: #{ARGF.readline}" # Read each line from each specified stream.
+ * end
+ *
+ * - Command and output:
+ *
+ * $ ruby t.rb foo.txt bar.txt
+ * "ARGV: [\"foo.txt\", \"bar.txt\"]"
+ * "Line: Foo 0\n"
+ * "ARGV: [\"bar.txt\"]"
+ * "Line: Foo 1\n"
+ * "ARGV: [\"bar.txt\"]"
+ * "Line: Bar 0\n"
+ * "ARGV: []"
+ * "Line: Bar 1\n"
+ * "ARGV: []"
+ * "Line: Bar 2\n"
+ * "ARGV: []"
+ * "Line: Bar 3\n"
+ *
+ * ==== Filepaths in +ARGV+
+ *
+ * The +ARGV+ array may contain filepaths the specify sources for \ARGF reading.
+ *
+ * This program prints what it reads from files at the paths specified
+ * on the command line:
+ *
+ * - \File +t.rb+:
+ *
+ * p ['ARGV', ARGV]
+ * # Read and print all content from the specified sources.
+ * p ['ARGF.read', ARGF.read]
+ *
+ * - Command and output:
+ *
+ * $ ruby t.rb foo.txt bar.txt
+ * ["ARGV", [foo.txt, bar.txt]
+ * ["ARGF.read", "Foo 0\nFoo 1\nBar 0\nBar 1\nBar 2\nBar 3\n"]
+ *
+ * ==== Specifying <tt>$stdin</tt> in +ARGV+
+ *
+ * To specify stream <tt>$stdin</tt> in +ARGV+, us the character <tt>'-'</tt>:
+ *
+ * - \File +t.rb+:
+ *
+ * p ['ARGV', ARGV]
+ * p ['ARGF.read', ARGF.read]
+ *
+ * - Command and output:
+ *
+ * $ echo "Open the pod bay doors, Hal." | ruby t.rb -
+ * ["ARGV", ["-"]]
+ * ["ARGF.read", "Open the pod bay doors, Hal.\n"]
+ *
+ * When no character <tt>'-'</tt> is given, stream <tt>$stdin</tt> is ignored
+ * (exception:
+ * see {Specifying $stdin in ARGV}[rdoc-ref:ARGF@Specifying+-24stdin+in+ARGV]):
+ *
+ * - Command and output:
*
- * $ ruby argf.rb --verbose file1 file2
+ * $ echo "Open the pod bay doors, Hal." | ruby t.rb foo.txt bar.txt
+ * "ARGV: [\"foo.txt\", \"bar.txt\"]"
+ * "Read: Foo 0\nFoo 1\nBar 0\nBar 1\nBar 2\nBar 3\n"
*
- * ARGV #=> ["--verbose", "file1", "file2"]
- * option = ARGV.shift #=> "--verbose"
- * ARGV #=> ["file1", "file2"]
+ * ==== Mixtures and Repetitions in +ARGV+
*
- * You can now use ARGF to work with a concatenation of each of these named
- * files. For instance, ARGF.read will return the contents of _file1_
- * followed by the contents of _file2_.
+ * For an \ARGF reader, +ARGV+ may contain any mixture of filepaths
+ * and character <tt>'-'</tt>, including repetitions.
*
- * After a file in +ARGV+ has been read ARGF removes it from the Array.
- * Thus, after all files have been read +ARGV+ will be empty.
+ * ==== Modifications to +ARGV+
*
- * You can manipulate +ARGV+ yourself to control what ARGF operates on. If
- * you remove a file from +ARGV+, it is ignored by ARGF; if you add files to
- * +ARGV+, they are treated as if they were named on the command line. For
- * example:
+ * The running Ruby program may make any modifications to the +ARGV+ array;
+ * the current value of +ARGV+ affects \ARGF reading.
*
- * ARGV.replace ["file1"]
- * ARGF.readlines # Returns the contents of file1 as an Array
- * ARGV #=> []
- * ARGV.replace ["file2", "file3"]
- * ARGF.read # Returns the contents of file2 and file3
+ * ==== Empty +ARGV+
*
- * If +ARGV+ is empty, ARGF acts as if it contained STDIN, i.e. the data
- * piped to your script. For example:
+ * For an empty +ARGV+, an \ARGF read method either returns +nil+
+ * or raises an exception, depending on the specific method.
+ *
+ * === More Read Methods
+ *
+ * As seen above, method ARGF#read reads the content of all sources
+ * into a single string.
+ * Other \ARGF methods provide other ways to access that content;
+ * these include:
+ *
+ * - Byte access: #each_byte, #getbyte, #readbyte.
+ * - Character access: #each_char, #getc, #readchar.
+ * - Codepoint access: #each_codepoint.
+ * - Line access: #each_line, #gets, #readline, #readlines.
+ * - Source access: #read, #read_nonblock, #readpartial.
+ *
+ * === About \Enumerable
+ *
+ * \ARGF includes module Enumerable.
+ * Virtually all methods in \Enumerable call method <tt>#each</tt> in the including class.
+ *
+ * <b>Note well</b>: In \ARGF, method #each returns data from the _sources_,
+ * _not_ from +ARGV+;
+ * therefore, for example, <tt>ARGF#entries</tt> returns an array of lines from the sources,
+ * not an array of the strings from +ARGV+:
+ *
+ * - \File +t.rb+:
+ *
+ * p ['ARGV', ARGV]
+ * p ['ARGF.entries', ARGF.entries]
+ *
+ * - Command and output:
+ *
+ * $ ruby t.rb foo.txt bar.txt
+ * ["ARGV", ["foo.txt", "bar.txt"]]
+ * ["ARGF.entries", ["Foo 0\n", "Foo 1\n", "Bar 0\n", "Bar 1\n", "Bar 2\n", "Bar 3\n"]]
+ *
+ * == Writing
+ *
+ * If <i>inplace mode</i> is in effect,
+ * \ARGF may write to target streams,
+ * which at any particular time are determined by the content of ARGV.
+ *
+ * Methods about inplace mode:
+ *
+ * - #inplace_mode
+ * - #inplace_mode=
+ * - #to_write_io
+ *
+ * Methods for writing:
+ *
+ * - #print
+ * - #printf
+ * - #putc
+ * - #puts
+ * - #write
*
- * $ echo "glark" | ruby -e 'p ARGF.read'
- * "glark\n"
*/
/*
@@ -14784,7 +14948,7 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
* ARGF is not itself a subclass of \IO.
*
* \Class StringIO provides an IO-like stream that handles a String.
- * \StringIO is not itself a subclass of \IO.
+ * StringIO is not itself a subclass of \IO.
*
* Important objects based on \IO include:
*
@@ -14802,7 +14966,7 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
* - Kernel#open: Returns a new \IO object connected to a given source:
* stream, file, or subprocess.
*
- * Like a \File stream, an \IO stream has:
+ * Like a File stream, an \IO stream has:
*
* - A read/write mode, which may be read-only, write-only, or read/write;
* see {Read/Write Mode}[rdoc-ref:File@Read-2FWrite+Mode].
@@ -14838,7 +15002,7 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
* that determine how a new stream is to be opened:
*
* - +:mode+: Stream mode.
- * - +:flags+: \Integer file open flags;
+ * - +:flags+: Integer file open flags;
* If +mode+ is also given, the two are bitwise-ORed.
* - +:external_encoding+: External encoding for the stream.
* - +:internal_encoding+: Internal encoding for the stream.
@@ -14853,7 +15017,7 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
* #path method.
*
* Also available are the options offered in String#encode,
- * which may control conversion between external internal encoding.
+ * which may control conversion between external and internal encoding.
*
* == Basic \IO
*
@@ -14918,56 +15082,64 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
*
* == Line \IO
*
- * You can read an \IO stream line-by-line using these methods:
+ * \Class \IO supports line-oriented
+ * {input}[rdoc-ref:IO@Line+Input] and {output}[rdoc-ref:IO@Line+Output]
*
- * - IO#each_line: Reads each remaining line, passing it to the given block.
- * - IO#gets: Returns the next line.
- * - IO#readline: Like #gets, but raises an exception at end-of-stream.
- * - IO#readlines: Returns all remaining lines in an array.
+ * === Line Input
+ *
+ * \Class \IO supports line-oriented input for
+ * {files}[rdoc-ref:IO@File+Line+Input] and {IO streams}[rdoc-ref:IO@Stream+Line+Input]
+ *
+ * ==== \File Line Input
+ *
+ * You can read lines from a file using these methods:
*
- * Each of these reader methods accepts:
+ * - IO.foreach: Reads each line and passes it to the given block.
+ * - IO.readlines: Reads and returns all lines in an array.
*
- * - An optional line separator, +sep+;
+ * For each of these methods:
+ *
+ * - You can specify {open options}[rdoc-ref:IO@Open+Options].
+ * - Line parsing depends on the effective <i>line separator</i>;
* see {Line Separator}[rdoc-ref:IO@Line+Separator].
- * - An optional line-size limit, +limit+;
+ * - The length of each returned line depends on the effective <i>line limit</i>;
* see {Line Limit}[rdoc-ref:IO@Line+Limit].
*
- * For each of these reader methods, reading may begin mid-line,
- * depending on the stream's position;
- * see {Position}[rdoc-ref:IO@Position]:
- *
- * f = File.new('t.txt')
- * f.pos = 27
- * f.each_line {|line| p line }
- * f.close
+ * ==== Stream Line Input
*
- * Output:
+ * You can read lines from an \IO stream using these methods:
*
- * "rth line\n"
- * "Fifth line\n"
+ * - IO#each_line: Reads each remaining line, passing it to the given block.
+ * - IO#gets: Returns the next line.
+ * - IO#readline: Like #gets, but raises an exception at end-of-stream.
+ * - IO#readlines: Returns all remaining lines in an array.
*
- * You can write to an \IO stream line-by-line using this method:
+ * For each of these methods:
*
- * - IO#puts: Writes objects to the stream.
+ * - Reading may begin mid-line,
+ * depending on the stream's _position_;
+ * see {Position}[rdoc-ref:IO@Position].
+ * - Line parsing depends on the effective <i>line separator</i>;
+ * see {Line Separator}[rdoc-ref:IO@Line+Separator].
+ * - The length of each returned line depends on the effective <i>line limit</i>;
+ * see {Line Limit}[rdoc-ref:IO@Line+Limit].
*
- * === Line Separator
+ * ===== Line Separator
*
- * Each of these methods uses a <i>line separator</i>,
- * which is the string that delimits lines:
+ * Each of the {line input methods}[rdoc-ref:IO@Line+Input] uses a <i>line separator</i>:
+ * the string that determines what is considered a line;
+ * it is sometimes called the <i>input record separator</i>.
*
- * - IO.foreach.
- * - IO.readlines.
- * - IO#each_line.
- * - IO#gets.
- * - IO#readline.
- * - IO#readlines.
+ * The default line separator is taken from global variable <tt>$/</tt>,
+ * whose initial value is <tt>"\n"</tt>.
*
- * The default line separator is the given by the global variable <tt>$/</tt>,
- * whose value is by default <tt>"\n"</tt>.
- * The line to be read next is all data from the current position
- * to the next line separator:
+ * Generally, the line to be read next is all data
+ * from the current {position}[rdoc-ref:IO@Position]
+ * to the next line separator
+ * (but see {Special Line Separator Values}[rdoc-ref:IO@Special+Line+Separator+Values]):
*
* f = File.new('t.txt')
+ * # Method gets with no sep argument returns the next line, according to $/.
* f.gets # => "First line\n"
* f.gets # => "Second line\n"
* f.gets # => "\n"
@@ -14975,7 +15147,7 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
* f.gets # => "Fifth line\n"
* f.close
*
- * You can specify a different line separator:
+ * You can use a different line separator by passing argument +sep+:
*
* f = File.new('t.txt')
* f.gets('l') # => "First l"
@@ -14984,15 +15156,27 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
* f.gets # => "e\n"
* f.close
*
- * There are two special line separators:
+ * Or by setting global variable <tt>$/</tt>:
+ *
+ * f = File.new('t.txt')
+ * $/ = 'l'
+ * f.gets # => "First l"
+ * f.gets # => "ine\nSecond l"
+ * f.gets # => "ine\n\nFourth l"
+ * f.close
+ *
+ * ===== Special Line Separator Values
+ *
+ * Each of the {line input methods}[rdoc-ref:IO@Line+Input]
+ * accepts two special values for parameter +sep+:
*
- * - +nil+: The entire stream is read into a single string:
+ * - +nil+: The entire stream is to be read ("slurped") into a single string:
*
* f = File.new('t.txt')
* f.gets(nil) # => "First line\nSecond line\n\nFourth line\nFifth line\n"
* f.close
*
- * - <tt>''</tt> (the empty string): The next "paragraph" is read
+ * - <tt>''</tt> (the empty string): The next "paragraph" is to be read
* (paragraphs being separated by two consecutive line separators):
*
* f = File.new('t.txt')
@@ -15000,23 +15184,18 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
* f.gets('') # => "Fourth line\nFifth line\n"
* f.close
*
- * === Line Limit
+ * ===== Line Limit
*
- * Each of these methods uses a <i>line limit</i>,
- * which specifies that the number of bytes returned may not be (much) longer
- * than the given +limit+;
+ * Each of the {line input methods}[rdoc-ref:IO@Line+Input]
+ * uses an integer <i>line limit</i>,
+ * which restricts the number of bytes that may be returned.
+ * (A multi-byte character will not be split, and so a returned line may be slightly longer
+ * than the limit).
*
- * - IO.foreach.
- * - IO.readlines.
- * - IO#each_line.
- * - IO#gets.
- * - IO#readline.
- * - IO#readlines.
+ * The default limit value is <tt>-1</tt>;
+ * any negative limit value means that there is no limit.
*
- * A multi-byte character will not be split, and so a line may be slightly longer
- * than the given limit.
- *
- * If +limit+ is not given, the line is determined only by +sep+.
+ * If there is no limit, the line is determined only by +sep+.
*
* # Text with 1-byte characters.
* File.open('t.txt') {|f| f.gets(1) } # => "F"
@@ -15034,24 +15213,21 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
* File.open('t.rus') {|f| f.gets(3).size } # => 2
* File.open('t.rus') {|f| f.gets(4).size } # => 2
*
- * === Line Separator and Line Limit
+ * ===== Line Separator and Line Limit
*
- * With arguments +sep+ and +limit+ given,
- * combines the two behaviors:
+ * With arguments +sep+ and +limit+ given, combines the two behaviors:
*
* - Returns the next line as determined by line separator +sep+.
- * - But returns no more bytes than are allowed by the limit.
+ * - But returns no more bytes than are allowed by the limit +limit+.
*
* Example:
*
* File.open('t.txt') {|f| f.gets('li', 20) } # => "First li"
* File.open('t.txt') {|f| f.gets('li', 2) } # => "Fi"
*
- * === Line Number
+ * ===== Line Number
*
- * A readable \IO stream has a non-negative integer <i>line number</i>.
- *
- * The relevant methods:
+ * A readable \IO stream has a non-negative integer <i>line number</i>:
*
* - IO#lineno: Returns the line number.
* - IO#lineno=: Resets and returns the line number.
@@ -15059,7 +15235,7 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
* Unless modified by a call to method IO#lineno=,
* the line number is the number of lines read
* by certain line-oriented methods,
- * according to the given line separator +sep+:
+ * according to the effective {line separator}[rdoc-ref:IO@Line+Separator]:
*
* - IO.foreach: Increments the line number on each call to the block.
* - IO#each_line: Increments the line number on each call to the block.
@@ -15128,7 +15304,7 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
* # => 41
* f.close
*
- * - When a stream is read, <tt>#.</tt> is set to the line number for that stream:
+ * - When a stream is read, <tt>$.</tt> is set to the line number for that stream:
*
* f0 = File.new('t.txt')
* f1 = File.new('t.dat')
@@ -15149,6 +15325,12 @@ set_LAST_READ_LINE(VALUE val, ID _x, VALUE *_y)
* $. # => 5
* f.close
*
+ * === Line Output
+ *
+ * You can write to an \IO stream line-by-line using this method:
+ *
+ * - IO#puts: Writes objects to the stream.
+ *
* == Character \IO
*
* You can process an \IO stream character-by-character using these methods:
@@ -15380,8 +15562,11 @@ Init_IO(void)
/* Can be raised by IO operations when IO#timeout= is set. */
rb_eIOTimeoutError = rb_define_class_under(rb_cIO, "TimeoutError", rb_eIOError);
+ /* Readable event mask for IO#wait. */
rb_define_const(rb_cIO, "READABLE", INT2NUM(RUBY_IO_READABLE));
+ /* Writable event mask for IO#wait. */
rb_define_const(rb_cIO, "WRITABLE", INT2NUM(RUBY_IO_WRITABLE));
+ /* Priority event mask for IO#wait. */
rb_define_const(rb_cIO, "PRIORITY", INT2NUM(RUBY_IO_PRIORITY));
/* exception to wait for reading. see IO.select. */
@@ -15444,7 +15629,7 @@ Init_IO(void)
rb_define_hooked_variable("$,", &rb_output_fs, 0, deprecated_str_setter);
rb_default_rs = rb_fstring_lit("\n"); /* avoid modifying RS_default */
- rb_gc_register_mark_object(rb_default_rs);
+ rb_vm_register_global_object(rb_default_rs);
rb_rs = rb_default_rs;
rb_output_rs = Qnil;
rb_define_hooked_variable("$/", &rb_rs, 0, deprecated_str_setter);
@@ -15495,7 +15680,6 @@ Init_IO(void)
rb_define_method(rb_cIO, "read", io_read, -1);
rb_define_method(rb_cIO, "write", io_write_m, -1);
rb_define_method(rb_cIO, "gets", rb_io_gets_m, -1);
- rb_define_method(rb_cIO, "readline", rb_io_readline, -1);
rb_define_method(rb_cIO, "getc", rb_io_getc, 0);
rb_define_method(rb_cIO, "getbyte", rb_io_getbyte, 0);
rb_define_method(rb_cIO, "readchar", rb_io_readchar, 0);
@@ -15639,7 +15823,7 @@ Init_IO(void)
rb_define_method(rb_cARGF, "binmode", argf_binmode_m, 0);
rb_define_method(rb_cARGF, "binmode?", argf_binmode_p, 0);
- rb_define_method(rb_cARGF, "write", argf_write, 1);
+ rb_define_method(rb_cARGF, "write", argf_write, -1);
rb_define_method(rb_cARGF, "print", rb_io_print, -1);
rb_define_method(rb_cARGF, "putc", rb_io_putc, 1);
rb_define_method(rb_cARGF, "puts", rb_io_puts, -1);
diff --git a/io.rb b/io.rb
index 40873ea4fd..549c5e62bd 100644
--- a/io.rb
+++ b/io.rb
@@ -120,4 +120,17 @@ class IO
def write_nonblock(buf, exception: true)
Primitive.io_write_nonblock(buf, exception)
end
+
+ # call-seq:
+ # readline(sep = $/, chomp: false) -> string
+ # readline(limit, chomp: false) -> string
+ # readline(sep, limit, chomp: false) -> string
+ #
+ # Reads a line as with IO#gets, but raises EOFError if already at end-of-stream.
+ #
+ # Optional keyword argument +chomp+ specifies whether line separators
+ # are to be omitted.
+ def readline(sep = $/, limit = nil, chomp: false)
+ Primitive.io_readline(sep, limit, chomp)
+ end
end
diff --git a/io_buffer.c b/io_buffer.c
index 8d09e09a89..7715aa0d37 100644
--- a/io_buffer.c
+++ b/io_buffer.c
@@ -34,6 +34,18 @@ size_t RUBY_IO_BUFFER_DEFAULT_SIZE;
#include <sys/mman.h>
#endif
+enum {
+ RB_IO_BUFFER_HEXDUMP_DEFAULT_WIDTH = 16,
+
+ RB_IO_BUFFER_INSPECT_HEXDUMP_MAXIMUM_SIZE = 256,
+ RB_IO_BUFFER_INSPECT_HEXDUMP_WIDTH = 16,
+
+ // This is used to validate the flags given by the user.
+ RB_IO_BUFFER_FLAGS_MASK = RB_IO_BUFFER_EXTERNAL | RB_IO_BUFFER_INTERNAL | RB_IO_BUFFER_MAPPED | RB_IO_BUFFER_SHARED | RB_IO_BUFFER_LOCKED | RB_IO_BUFFER_PRIVATE | RB_IO_BUFFER_READONLY,
+
+ RB_IO_BUFFER_DEBUG = 0,
+};
+
struct rb_io_buffer {
void *base;
size_t size;
@@ -91,11 +103,9 @@ io_buffer_map_file(struct rb_io_buffer *buffer, int descriptor, size_t size, rb_
access = FILE_MAP_WRITE;
}
- HANDLE mapping = CreateFileMapping(file, NULL, protect, 0, 0, NULL);
- if (!mapping) rb_sys_fail("io_buffer_map_descriptor:CreateFileMapping");
-
if (flags & RB_IO_BUFFER_PRIVATE) {
- access |= FILE_MAP_COPY;
+ protect = PAGE_WRITECOPY;
+ access = FILE_MAP_COPY;
buffer->flags |= RB_IO_BUFFER_PRIVATE;
}
else {
@@ -104,6 +114,10 @@ io_buffer_map_file(struct rb_io_buffer *buffer, int descriptor, size_t size, rb_
buffer->flags |= RB_IO_BUFFER_SHARED;
}
+ HANDLE mapping = CreateFileMapping(file, NULL, protect, 0, 0, NULL);
+ if (RB_IO_BUFFER_DEBUG) fprintf(stderr, "io_buffer_map_file:CreateFileMapping -> %p\n", mapping);
+ if (!mapping) rb_sys_fail("io_buffer_map_descriptor:CreateFileMapping");
+
void *base = MapViewOfFile(mapping, access, (DWORD)(offset >> 32), (DWORD)(offset & 0xFFFFFFFF), size);
if (!base) {
@@ -124,6 +138,7 @@ io_buffer_map_file(struct rb_io_buffer *buffer, int descriptor, size_t size, rb_
if (flags & RB_IO_BUFFER_PRIVATE) {
buffer->flags |= RB_IO_BUFFER_PRIVATE;
+ access |= MAP_PRIVATE;
}
else {
// This buffer refers to external buffer.
@@ -143,16 +158,7 @@ io_buffer_map_file(struct rb_io_buffer *buffer, int descriptor, size_t size, rb_
buffer->size = size;
buffer->flags |= RB_IO_BUFFER_MAPPED;
-}
-
-static inline void
-io_buffer_unmap(void* base, size_t size)
-{
-#ifdef _WIN32
- VirtualFree(base, 0, MEM_RELEASE);
-#else
- munmap(base, size);
-#endif
+ buffer->flags |= RB_IO_BUFFER_FILE;
}
static void
@@ -183,7 +189,7 @@ io_buffer_zero(struct rb_io_buffer *buffer)
}
static void
-io_buffer_initialize(struct rb_io_buffer *buffer, void *base, size_t size, enum rb_io_buffer_flags flags, VALUE source)
+io_buffer_initialize(VALUE self, struct rb_io_buffer *buffer, void *base, size_t size, enum rb_io_buffer_flags flags, VALUE source)
{
if (base) {
// If we are provided a pointer, we use it.
@@ -209,10 +215,14 @@ io_buffer_initialize(struct rb_io_buffer *buffer, void *base, size_t size, enum
buffer->base = base;
buffer->size = size;
buffer->flags = flags;
- buffer->source = source;
+ RB_OBJ_WRITE(self, &buffer->source, source);
+
+#if defined(_WIN32)
+ buffer->mapping = NULL;
+#endif
}
-static int
+static void
io_buffer_free(struct rb_io_buffer *buffer)
{
if (buffer->base) {
@@ -221,7 +231,16 @@ io_buffer_free(struct rb_io_buffer *buffer)
}
if (buffer->flags & RB_IO_BUFFER_MAPPED) {
- io_buffer_unmap(buffer->base, buffer->size);
+#ifdef _WIN32
+ if (buffer->flags & RB_IO_BUFFER_FILE) {
+ UnmapViewOfFile(buffer->base);
+ }
+ else {
+ VirtualFree(buffer->base, 0, MEM_RELEASE);
+ }
+#else
+ munmap(buffer->base, buffer->size);
+#endif
}
// Previously we had this, but we found out due to the way GC works, we
@@ -232,20 +251,20 @@ io_buffer_free(struct rb_io_buffer *buffer)
buffer->base = NULL;
-#if defined(_WIN32)
- if (buffer->mapping) {
- CloseHandle(buffer->mapping);
- buffer->mapping = NULL;
- }
-#endif
buffer->size = 0;
buffer->flags = 0;
buffer->source = Qnil;
-
- return 1;
}
- return 0;
+#if defined(_WIN32)
+ if (buffer->mapping) {
+ if (RB_IO_BUFFER_DEBUG) fprintf(stderr, "io_buffer_free:CloseHandle -> %p\n", buffer->mapping);
+ if (!CloseHandle(buffer->mapping)) {
+ fprintf(stderr, "io_buffer_free:GetLastError -> %d\n", GetLastError());
+ }
+ buffer->mapping = NULL;
+ }
+#endif
}
void
@@ -261,8 +280,6 @@ rb_io_buffer_type_free(void *_buffer)
struct rb_io_buffer *buffer = _buffer;
io_buffer_free(buffer);
-
- free(buffer);
}
size_t
@@ -286,9 +303,151 @@ static const rb_data_type_t rb_io_buffer_type = {
.dsize = rb_io_buffer_type_size,
},
.data = NULL,
- .flags = RUBY_TYPED_FREE_IMMEDIATELY,
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE,
};
+static inline enum rb_io_buffer_flags
+io_buffer_extract_flags(VALUE argument)
+{
+ if (rb_int_negative_p(argument)) {
+ rb_raise(rb_eArgError, "Flags can't be negative!");
+ }
+
+ enum rb_io_buffer_flags flags = RB_NUM2UINT(argument);
+
+ // We deliberately ignore unknown flags. Any future flags which are exposed this way should be safe to ignore.
+ return flags & RB_IO_BUFFER_FLAGS_MASK;
+}
+
+// Extract an offset argument, which must be a non-negative integer.
+static inline size_t
+io_buffer_extract_offset(VALUE argument)
+{
+ if (rb_int_negative_p(argument)) {
+ rb_raise(rb_eArgError, "Offset can't be negative!");
+ }
+
+ return NUM2SIZET(argument);
+}
+
+// Extract a length argument, which must be a non-negative integer.
+// Length is generally considered a mutable property of an object and
+// semantically should be considered a subset of "size" as a concept.
+static inline size_t
+io_buffer_extract_length(VALUE argument)
+{
+ if (rb_int_negative_p(argument)) {
+ rb_raise(rb_eArgError, "Length can't be negative!");
+ }
+
+ return NUM2SIZET(argument);
+}
+
+// Extract a size argument, which must be a non-negative integer.
+// Size is generally considered an immutable property of an object.
+static inline size_t
+io_buffer_extract_size(VALUE argument)
+{
+ if (rb_int_negative_p(argument)) {
+ rb_raise(rb_eArgError, "Size can't be negative!");
+ }
+
+ return NUM2SIZET(argument);
+}
+
+// Extract a width argument, which must be a non-negative integer, and must be
+// at least the given minimum.
+static inline size_t
+io_buffer_extract_width(VALUE argument, size_t minimum)
+{
+ if (rb_int_negative_p(argument)) {
+ rb_raise(rb_eArgError, "Width can't be negative!");
+ }
+
+ size_t width = NUM2SIZET(argument);
+
+ if (width < minimum) {
+ rb_raise(rb_eArgError, "Width must be at least %" PRIuSIZE "!", minimum);
+ }
+
+ return width;
+}
+
+// Compute the default length for a buffer, given an offset into that buffer.
+// The default length is the size of the buffer minus the offset. The offset
+// must be less than the size of the buffer otherwise the length will be
+// invalid; in that case, an ArgumentError exception will be raised.
+static inline size_t
+io_buffer_default_length(const struct rb_io_buffer *buffer, size_t offset)
+{
+ if (offset > buffer->size) {
+ rb_raise(rb_eArgError, "The given offset is bigger than the buffer size!");
+ }
+
+ // Note that the "length" is computed by the size the offset.
+ return buffer->size - offset;
+}
+
+// Extract the optional length and offset arguments, returning the buffer.
+// The length and offset are optional, but if they are provided, they must be
+// positive integers. If the length is not provided, the default length is
+// computed from the buffer size and offset. If the offset is not provided, it
+// defaults to zero.
+static inline struct rb_io_buffer *
+io_buffer_extract_length_offset(VALUE self, int argc, VALUE argv[], size_t *length, size_t *offset)
+{
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
+ if (argc >= 2 && !NIL_P(argv[1])) {
+ *offset = io_buffer_extract_offset(argv[1]);
+ }
+ else {
+ *offset = 0;
+ }
+
+ if (argc >= 1 && !NIL_P(argv[0])) {
+ *length = io_buffer_extract_length(argv[0]);
+ }
+ else {
+ *length = io_buffer_default_length(buffer, *offset);
+ }
+
+ return buffer;
+}
+
+// Extract the optional offset and length arguments, returning the buffer.
+// Similar to `io_buffer_extract_length_offset` but with the order of arguments
+// reversed.
+//
+// After much consideration, I decided to accept both forms.
+// The `(offset, length)` order is more natural when referring about data,
+// while the `(length, offset)` order is more natural when referring to
+// read/write operations. In many cases, with the latter form, `offset`
+// is usually not supplied.
+static inline struct rb_io_buffer *
+io_buffer_extract_offset_length(VALUE self, int argc, VALUE argv[], size_t *offset, size_t *length)
+{
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
+ if (argc >= 1 && !NIL_P(argv[0])) {
+ *offset = io_buffer_extract_offset(argv[0]);
+ }
+ else {
+ *offset = 0;
+ }
+
+ if (argc >= 2 && !NIL_P(argv[1])) {
+ *length = io_buffer_extract_length(argv[1]);
+ }
+ else {
+ *length = io_buffer_default_length(buffer, *offset);
+ }
+
+ return buffer;
+}
+
VALUE
rb_io_buffer_type_allocate(VALUE self)
{
@@ -315,7 +474,7 @@ static VALUE io_buffer_for_make_instance(VALUE klass, VALUE string, enum rb_io_b
if (!(flags & RB_IO_BUFFER_READONLY))
rb_str_modify(string);
- io_buffer_initialize(buffer, RSTRING_PTR(string), RSTRING_LEN(string), flags, string);
+ io_buffer_initialize(instance, buffer, RSTRING_PTR(string), RSTRING_LEN(string), flags, string);
return instance;
}
@@ -358,11 +517,11 @@ io_buffer_for_yield_instance_ensure(VALUE _arguments)
* IO::Buffer.for(string) -> readonly io_buffer
* IO::Buffer.for(string) {|io_buffer| ... read/write io_buffer ...}
*
- * Creates a IO::Buffer from the given string's memory. Without a block a
- * frozen internal copy of the string is created efficiently and used as the
- * buffer source. When a block is provided, the buffer is associated directly
- * with the string's internal buffer and updating the buffer will update the
- * string.
+ * Creates a zero-copy IO::Buffer from the given string's memory. Without a
+ * block a frozen internal copy of the string is created efficiently and used
+ * as the buffer source. When a block is provided, the buffer is associated
+ * directly with the string's internal buffer and updating the buffer will
+ * update the string.
*
* Until #free is invoked on the buffer, either explicitly or via the garbage
* collector, the source string will be locked and cannot be modified.
@@ -417,9 +576,9 @@ rb_io_buffer_type_for(VALUE klass, VALUE string)
* call-seq:
* IO::Buffer.string(length) {|io_buffer| ... read/write io_buffer ...} -> string
*
- * Creates a new string of the given length and yields a IO::Buffer instance
- * to the block which uses the string as a source. The block is expected to
- * write to the buffer and the string will be returned.
+ * Creates a new string of the given length and yields a zero-copy IO::Buffer
+ * instance to the block which uses the string as a source. The block is
+ * expected to write to the buffer and the string will be returned.
*
* IO::Buffer.string(4) do |buffer|
* buffer.set_string("Ruby")
@@ -450,7 +609,7 @@ rb_io_buffer_new(void *base, size_t size, enum rb_io_buffer_flags flags)
struct rb_io_buffer *buffer = NULL;
TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- io_buffer_initialize(buffer, base, size, flags, Qnil);
+ io_buffer_initialize(instance, buffer, base, size, flags, Qnil);
return instance;
}
@@ -484,8 +643,6 @@ rb_io_buffer_map(VALUE io, size_t size, rb_off_t offset, enum rb_io_buffer_flags
* mapping, you need to open a file in read-write mode, and explicitly pass
* +flags+ argument without IO::Buffer::IMMUTABLE.
*
- * Example:
- *
* File.write('test.txt', 'test')
*
* buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
@@ -521,7 +678,7 @@ io_buffer_map(int argc, VALUE *argv, VALUE klass)
size_t size;
if (argc >= 2 && !RB_NIL_P(argv[1])) {
- size = RB_NUM2SIZE(argv[1]);
+ size = io_buffer_extract_size(argv[1]);
}
else {
rb_off_t file_size = rb_file_size(io);
@@ -540,6 +697,7 @@ io_buffer_map(int argc, VALUE *argv, VALUE klass)
}
}
+ // This is the file offset, not the buffer offset:
rb_off_t offset = 0;
if (argc >= 3) {
offset = NUM2OFFT(argv[2]);
@@ -547,7 +705,7 @@ io_buffer_map(int argc, VALUE *argv, VALUE klass)
enum rb_io_buffer_flags flags = 0;
if (argc >= 4) {
- flags = RB_NUM2UINT(argv[3]);
+ flags = io_buffer_extract_flags(argv[3]);
}
return rb_io_buffer_map(io, size, offset, flags);
@@ -575,8 +733,6 @@ io_flags_for_size(size_t size)
* on Windows). The behavior can be forced by passing IO::Buffer::MAPPED
* as a second parameter.
*
- * Examples
- *
* buffer = IO::Buffer.new(4)
* # =>
* # #<IO::Buffer 0x000055b34497ea10+4 INTERNAL>
@@ -601,9 +757,8 @@ rb_io_buffer_initialize(int argc, VALUE *argv, VALUE self)
TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
size_t size;
-
if (argc > 0) {
- size = RB_NUM2SIZE(argv[0]);
+ size = io_buffer_extract_size(argv[0]);
}
else {
size = RUBY_IO_BUFFER_DEFAULT_SIZE;
@@ -611,13 +766,13 @@ rb_io_buffer_initialize(int argc, VALUE *argv, VALUE self)
enum rb_io_buffer_flags flags = 0;
if (argc >= 2) {
- flags = RB_NUM2UINT(argv[1]);
+ flags = io_buffer_extract_flags(argv[1]);
}
else {
flags |= io_flags_for_size(size);
}
- io_buffer_initialize(buffer, NULL, size, flags, Qnil);
+ io_buffer_initialize(self, buffer, NULL, size, flags, Qnil);
return self;
}
@@ -663,6 +818,82 @@ io_buffer_validate(struct rb_io_buffer *buffer)
}
}
+enum rb_io_buffer_flags
+rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size)
+{
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
+ if (io_buffer_validate(buffer)) {
+ if (buffer->base) {
+ *base = buffer->base;
+ *size = buffer->size;
+
+ return buffer->flags;
+ }
+ }
+
+ *base = NULL;
+ *size = 0;
+
+ return 0;
+}
+
+// Internal function for accessing bytes for writing, wil
+static inline void
+io_buffer_get_bytes_for_writing(struct rb_io_buffer *buffer, void **base, size_t *size)
+{
+ if (buffer->flags & RB_IO_BUFFER_READONLY) {
+ rb_raise(rb_eIOBufferAccessError, "Buffer is not writable!");
+ }
+
+ if (!io_buffer_validate(buffer)) {
+ rb_raise(rb_eIOBufferInvalidatedError, "Buffer is invalid!");
+ }
+
+ if (buffer->base) {
+ *base = buffer->base;
+ *size = buffer->size;
+ } else {
+ *base = NULL;
+ *size = 0;
+ }
+}
+
+void
+rb_io_buffer_get_bytes_for_writing(VALUE self, void **base, size_t *size)
+{
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
+ io_buffer_get_bytes_for_writing(buffer, base, size);
+}
+
+static void
+io_buffer_get_bytes_for_reading(struct rb_io_buffer *buffer, const void **base, size_t *size)
+{
+ if (!io_buffer_validate(buffer)) {
+ rb_raise(rb_eIOBufferInvalidatedError, "Buffer has been invalidated!");
+ }
+
+ if (buffer->base) {
+ *base = buffer->base;
+ *size = buffer->size;
+ } else {
+ *base = NULL;
+ *size = 0;
+ }
+}
+
+void
+rb_io_buffer_get_bytes_for_reading(VALUE self, const void **base, size_t *size)
+{
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
+ io_buffer_get_bytes_for_reading(buffer, base, size);
+}
+
/*
* call-seq: to_s -> string
*
@@ -699,6 +930,10 @@ rb_io_buffer_to_s(VALUE self)
rb_str_cat2(result, " MAPPED");
}
+ if (buffer->flags & RB_IO_BUFFER_FILE) {
+ rb_str_cat2(result, " FILE");
+ }
+
if (buffer->flags & RB_IO_BUFFER_SHARED) {
rb_str_cat2(result, " SHARED");
}
@@ -707,6 +942,10 @@ rb_io_buffer_to_s(VALUE self)
rb_str_cat2(result, " LOCKED");
}
+ if (buffer->flags & RB_IO_BUFFER_PRIVATE) {
+ rb_str_cat2(result, " PRIVATE");
+ }
+
if (buffer->flags & RB_IO_BUFFER_READONLY) {
rb_str_cat2(result, " READONLY");
}
@@ -722,13 +961,38 @@ rb_io_buffer_to_s(VALUE self)
return rb_str_cat2(result, ">");
}
+// Compute the output size of a hexdump of the given width (bytes per line), total size, and whether it is the first line in the output.
+// This is used to preallocate the output string.
+inline static size_t
+io_buffer_hexdump_output_size(size_t width, size_t size, int first)
+{
+ // The preview on the right hand side is 1:1:
+ size_t total = size;
+
+ size_t whole_lines = (size / width);
+ size_t partial_line = (size % width) ? 1 : 0;
+
+ // For each line:
+ // 1 byte 10 bytes 1 byte width*3 bytes 1 byte size bytes
+ // (newline) (address) (space) (hexdump ) (space) (preview)
+ total += (whole_lines + partial_line) * (1 + 10 + width*3 + 1 + 1);
+
+ // If the hexdump is the first line, one less newline will be emitted:
+ if (size && first) total -= 1;
+
+ return total;
+}
+
+// Append a hexdump of the given width (bytes per line), base address, size, and whether it is the first line in the output.
+// If the hexdump is not the first line, it will prepend a newline if there is any output at all.
+// If formatting here is adjusted, please update io_buffer_hexdump_output_size accordingly.
static VALUE
-io_buffer_hexdump(VALUE string, size_t width, char *base, size_t size, int first)
+io_buffer_hexdump(VALUE string, size_t width, const char *base, size_t length, size_t offset, int first)
{
char *text = alloca(width+1);
text[width] = '\0';
- for (size_t offset = 0; offset < size; offset += width) {
+ for (; offset < length; offset += width) {
memset(text, '\0', width);
if (first) {
rb_str_catf(string, "0x%08" PRIxSIZE " ", offset);
@@ -739,7 +1003,7 @@ io_buffer_hexdump(VALUE string, size_t width, char *base, size_t size, int first
}
for (size_t i = 0; i < width; i += 1) {
- if (offset+i < size) {
+ if (offset+i < length) {
unsigned char value = ((unsigned char*)base)[offset+i];
if (value < 127 && isprint(value)) {
@@ -762,23 +1026,18 @@ io_buffer_hexdump(VALUE string, size_t width, char *base, size_t size, int first
return string;
}
-static VALUE
-rb_io_buffer_hexdump(VALUE self)
-{
- struct rb_io_buffer *buffer = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
-
- VALUE result = Qnil;
-
- if (io_buffer_validate(buffer) && buffer->base) {
- result = rb_str_buf_new(buffer->size*3 + (buffer->size/16)*12 + 1);
-
- io_buffer_hexdump(result, 16, buffer->base, buffer->size, 1);
- }
-
- return result;
-}
-
+/*
+ * call-seq: inspect -> string
+ *
+ * Inspect the buffer and report useful information about it's internal state.
+ * Only a limited portion of the buffer will be displayed in a hexdump style
+ * format.
+ *
+ * buffer = IO::Buffer.for("Hello World")
+ * puts buffer.inspect
+ * # #<IO::Buffer 0x000000010198ccd8+11 EXTERNAL READONLY SLICE>
+ * # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World
+ */
VALUE
rb_io_buffer_inspect(VALUE self)
{
@@ -788,9 +1047,19 @@ rb_io_buffer_inspect(VALUE self)
VALUE result = rb_io_buffer_to_s(self);
if (io_buffer_validate(buffer)) {
- // Limit the maximum size generated by inspect.
- if (buffer->size <= 256) {
- io_buffer_hexdump(result, 16, buffer->base, buffer->size, 0);
+ // Limit the maximum size generated by inspect:
+ size_t size = buffer->size;
+ int clamped = 0;
+
+ if (size > RB_IO_BUFFER_INSPECT_HEXDUMP_MAXIMUM_SIZE) {
+ size = RB_IO_BUFFER_INSPECT_HEXDUMP_MAXIMUM_SIZE;
+ clamped = 1;
+ }
+
+ io_buffer_hexdump(result, RB_IO_BUFFER_INSPECT_HEXDUMP_WIDTH, buffer->base, size, 0, 0);
+
+ if (clamped) {
+ rb_str_catf(result, "\n(and %" PRIuSIZE " more bytes not printed)", buffer->size - size);
}
}
@@ -817,8 +1086,8 @@ rb_io_buffer_size(VALUE self)
*
* Returns whether the buffer buffer is accessible.
*
- * A buffer becomes invalid if it is a slice of another buffer which has been
- * freed.
+ * A buffer becomes invalid if it is a slice of another buffer (or string)
+ * which has been freed or re-allocated at a different address.
*/
static VALUE
rb_io_buffer_valid_p(VALUE self)
@@ -832,8 +1101,16 @@ rb_io_buffer_valid_p(VALUE self)
/*
* call-seq: null? -> true or false
*
- * If the buffer was freed with #free or was never allocated in the first
- * place.
+ * If the buffer was freed with #free, transferred with #transfer, or was
+ * never allocated in the first place.
+ *
+ * buffer = IO::Buffer.new(0)
+ * buffer.null? #=> true
+ *
+ * buffer = IO::Buffer.new(4)
+ * buffer.null? #=> false
+ * buffer.free
+ * buffer.null? #=> true
*/
static VALUE
rb_io_buffer_null_p(VALUE self)
@@ -933,6 +1210,22 @@ rb_io_buffer_mapped_p(VALUE self)
* If the buffer is _shared_, meaning it references memory that can be shared
* with other processes (and thus might change without being modified
* locally).
+ *
+ * # Create a test file:
+ * File.write('test.txt', 'test')
+ *
+ * # Create a shared mapping from the given file, the file must be opened in
+ * # read-write mode unless we also specify IO::Buffer::READONLY:
+ * buffer = IO::Buffer.map(File.open('test.txt', 'r+'), nil, 0)
+ * # => #<IO::Buffer 0x00007f1bffd5e000+4 EXTERNAL MAPPED SHARED>
+ *
+ * # Write to the buffer, which will modify the mapped file:
+ * buffer.set_string('b', 0)
+ * # => 1
+ *
+ * # The file itself is modified:
+ * File.read('test.txt')
+ * # => "best"
*/
static VALUE
rb_io_buffer_shared_p(VALUE self)
@@ -953,8 +1246,6 @@ rb_io_buffer_shared_p(VALUE self)
* Locking is not thread safe, but is a semantic used to ensure buffers don't
* move while being used by a system call.
*
- * Example:
- *
* buffer.locked do
* buffer.write(io) # theoretical system call interface
* end
@@ -968,6 +1259,37 @@ rb_io_buffer_locked_p(VALUE self)
return RBOOL(buffer->flags & RB_IO_BUFFER_LOCKED);
}
+/* call-seq: private? -> true or false
+ *
+ * If the buffer is _private_, meaning modifications to the buffer will not
+ * be replicated to the underlying file mapping.
+ *
+ * # Create a test file:
+ * File.write('test.txt', 'test')
+ *
+ * # Create a private mapping from the given file. Note that the file here
+ * # is opened in read-only mode, but it doesn't matter due to the private
+ * # mapping:
+ * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::PRIVATE)
+ * # => #<IO::Buffer 0x00007fce63f11000+4 MAPPED PRIVATE>
+ *
+ * # Write to the buffer (invoking CoW of the underlying file buffer):
+ * buffer.set_string('b', 0)
+ * # => 1
+ *
+ * # The file itself is not modified:
+ * File.read('test.txt')
+ * # => "test"
+ */
+static VALUE
+rb_io_buffer_private_p(VALUE self)
+{
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
+ return RBOOL(buffer->flags & RB_IO_BUFFER_PRIVATE);
+}
+
int
rb_io_buffer_readonly_p(VALUE self)
{
@@ -1061,8 +1383,6 @@ rb_io_buffer_try_unlock(VALUE self)
* non-blocking system calls. You can only share a buffer between threads with
* appropriate synchronisation techniques.
*
- * Example:
- *
* buffer = IO::Buffer.new(4)
* buffer.locked? #=> false
*
@@ -1110,8 +1430,6 @@ rb_io_buffer_locked(VALUE self)
*
* You can resize a freed buffer to re-allocate it.
*
- * Example:
- *
* buffer = IO::Buffer.for('test')
* buffer.free
* # => #<IO::Buffer 0x0000000000000000+0 NULL>
@@ -1156,9 +1474,53 @@ VALUE rb_io_buffer_free_locked(VALUE self)
static inline void
io_buffer_validate_range(struct rb_io_buffer *buffer, size_t offset, size_t length)
{
+ // We assume here that offset + length won't overflow:
if (offset + length > buffer->size) {
- rb_raise(rb_eArgError, "Specified offset+length exceeds buffer size!");
+ rb_raise(rb_eArgError, "Specified offset+length is bigger than the buffer size!");
+ }
+}
+
+/*
+ * call-seq: hexdump([offset, [length, [width]]]) -> string
+ *
+ * Returns a human-readable string representation of the buffer. The exact
+ * format is subject to change.
+ *
+ * buffer = IO::Buffer.for("Hello World")
+ * puts buffer.hexdump
+ * # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World
+ *
+ * As buffers are usually fairly big, you may want to limit the output by
+ * specifying the offset and length:
+ *
+ * puts buffer.hexdump(6, 5)
+ * # 0x00000006 57 6f 72 6c 64 World
+ */
+static VALUE
+rb_io_buffer_hexdump(int argc, VALUE *argv, VALUE self)
+{
+ rb_check_arity(argc, 0, 3);
+
+ size_t offset, length;
+ struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);
+
+ size_t width = RB_IO_BUFFER_HEXDUMP_DEFAULT_WIDTH;
+ if (argc >= 3) {
+ width = io_buffer_extract_width(argv[2], 1);
+ }
+
+ // This may raise an exception if the offset/length is invalid:
+ io_buffer_validate_range(buffer, offset, length);
+
+ VALUE result = Qnil;
+
+ if (io_buffer_validate(buffer) && buffer->base) {
+ result = rb_str_buf_new(io_buffer_hexdump_output_size(width, length, 1));
+
+ io_buffer_hexdump(result, width, buffer->base, offset+length, offset, 1);
}
+
+ return result;
}
static VALUE
@@ -1174,16 +1536,18 @@ rb_io_buffer_slice(struct rb_io_buffer *buffer, VALUE self, size_t offset, size_
slice->size = length;
// The source should be the root buffer:
- if (buffer->source != Qnil)
- slice->source = buffer->source;
- else
- slice->source = self;
+ if (buffer->source != Qnil) {
+ RB_OBJ_WRITE(instance, &slice->source, buffer->source);
+ }
+ else {
+ RB_OBJ_WRITE(instance, &slice->source, self);
+ }
return instance;
}
/*
- * call-seq: slice([offset = 0, [length]]) -> io_buffer
+ * call-seq: slice([offset, [length]]) -> io_buffer
*
* Produce another IO::Buffer which is a slice (or view into) the current one
* starting at +offset+ bytes and going for +length+ bytes.
@@ -1201,8 +1565,6 @@ rb_io_buffer_slice(struct rb_io_buffer *buffer, VALUE self, size_t offset, size_
* Raises RuntimeError if the <tt>offset+length</tt> is out of the current
* buffer's bounds.
*
- * Example:
- *
* string = 'test'
* buffer = IO::Buffer.for(string)
*
@@ -1243,116 +1605,17 @@ io_buffer_slice(int argc, VALUE *argv, VALUE self)
{
rb_check_arity(argc, 0, 2);
- struct rb_io_buffer *buffer = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
-
- size_t offset = 0, length = 0;
-
- if (argc > 0) {
- if (rb_int_negative_p(argv[0])) {
- rb_raise(rb_eArgError, "Offset can't be negative!");
- }
-
- offset = NUM2SIZET(argv[0]);
- }
-
- if (argc > 1) {
- if (rb_int_negative_p(argv[1])) {
- rb_raise(rb_eArgError, "Length can't be negative!");
- }
-
- length = NUM2SIZET(argv[1]);
- }
- else {
- length = buffer->size - offset;
- }
+ size_t offset, length;
+ struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);
return rb_io_buffer_slice(buffer, self, offset, length);
}
-int
-rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size)
-{
- struct rb_io_buffer *buffer = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
-
- if (io_buffer_validate(buffer)) {
- if (buffer->base) {
- *base = buffer->base;
- *size = buffer->size;
-
- return buffer->flags;
- }
- }
-
- *base = NULL;
- *size = 0;
-
- return 0;
-}
-
-static inline void
-io_buffer_get_bytes_for_writing(struct rb_io_buffer *buffer, void **base, size_t *size)
-{
- if (buffer->flags & RB_IO_BUFFER_READONLY) {
- rb_raise(rb_eIOBufferAccessError, "Buffer is not writable!");
- }
-
- if (!io_buffer_validate(buffer)) {
- rb_raise(rb_eIOBufferInvalidatedError, "Buffer is invalid!");
- }
-
- if (buffer->base) {
- *base = buffer->base;
- *size = buffer->size;
-
- return;
- }
-
- rb_raise(rb_eIOBufferAllocationError, "The buffer is not allocated!");
-}
-
-void
-rb_io_buffer_get_bytes_for_writing(VALUE self, void **base, size_t *size)
-{
- struct rb_io_buffer *buffer = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
-
- io_buffer_get_bytes_for_writing(buffer, base, size);
-}
-
-static void
-io_buffer_get_bytes_for_reading(struct rb_io_buffer *buffer, const void **base, size_t *size)
-{
- if (!io_buffer_validate(buffer)) {
- rb_raise(rb_eIOBufferInvalidatedError, "Buffer has been invalidated!");
- }
-
- if (buffer->base) {
- *base = buffer->base;
- *size = buffer->size;
-
- return;
- }
-
- rb_raise(rb_eIOBufferAllocationError, "The buffer is not allocated!");
-}
-
-void
-rb_io_buffer_get_bytes_for_reading(VALUE self, const void **base, size_t *size)
-{
- struct rb_io_buffer *buffer = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
-
- io_buffer_get_bytes_for_reading(buffer, base, size);
-}
-
/*
* call-seq: transfer -> new_io_buffer
*
- * Transfers ownership to a new buffer, deallocating the current one.
- *
- * Example:
+ * Transfers ownership of the underlying memory to a new buffer, causing the
+ * current buffer to become uninitialized.
*
* buffer = IO::Buffer.new('test')
* other = buffer.transfer
@@ -1395,11 +1658,11 @@ io_buffer_resize_clear(struct rb_io_buffer *buffer, void* base, size_t size)
}
static void
-io_buffer_resize_copy(struct rb_io_buffer *buffer, size_t size)
+io_buffer_resize_copy(VALUE self, struct rb_io_buffer *buffer, size_t size)
{
// Slow path:
struct rb_io_buffer resized;
- io_buffer_initialize(&resized, NULL, size, io_flags_for_size(size), Qnil);
+ io_buffer_initialize(self, &resized, NULL, size, io_flags_for_size(size), Qnil);
if (buffer->base) {
size_t preserve = buffer->size;
@@ -1424,7 +1687,7 @@ rb_io_buffer_resize(VALUE self, size_t size)
}
if (buffer->base == NULL) {
- io_buffer_initialize(buffer, NULL, size, io_flags_for_size(size), Qnil);
+ io_buffer_initialize(self, buffer, NULL, size, io_flags_for_size(size), Qnil);
return;
}
@@ -1469,7 +1732,7 @@ rb_io_buffer_resize(VALUE self, size_t size)
return;
}
- io_buffer_resize_copy(buffer, size);
+ io_buffer_resize_copy(self, buffer, size);
}
/*
@@ -1493,7 +1756,7 @@ rb_io_buffer_resize(VALUE self, size_t size)
static VALUE
io_buffer_resize(VALUE self, VALUE size)
{
- rb_io_buffer_resize(self, NUM2SIZET(size));
+ rb_io_buffer_resize(self, io_buffer_extract_size(size));
return self;
}
@@ -1664,8 +1927,6 @@ io_buffer_buffer_type_size(ID buffer_type)
*
* Returns the size of the given buffer type(s) in bytes.
*
- * Example:
- *
* IO::Buffer.size_of(:u32) # => 4
* IO::Buffer.size_of([:u32, :u32]) # => 8
*/
@@ -1744,8 +2005,6 @@ rb_io_buffer_get_value(const void* base, size_t size, ID buffer_type, size_t *of
* in the buffer. For example, a +:u32+ buffer type is a 32-bit unsigned
* integer in little-endian format.
*
- * Example:
- *
* string = [1.5].pack('f')
* # => "\x00\x00\xC0?"
* IO::Buffer.for(string).get_value(:f32, 0)
@@ -1756,7 +2015,7 @@ io_buffer_get_value(VALUE self, VALUE type, VALUE _offset)
{
const void *base;
size_t size;
- size_t offset = NUM2SIZET(_offset);
+ size_t offset = io_buffer_extract_offset(_offset);
rb_io_buffer_get_bytes_for_reading(self, &base, &size);
@@ -1769,8 +2028,6 @@ io_buffer_get_value(VALUE self, VALUE type, VALUE _offset)
* Similar to #get_value, except that it can handle multiple buffer types and
* returns an array of values.
*
- * Example:
- *
* string = [1.5, 2.5].pack('ff')
* IO::Buffer.for(string).get_values([:f32, :f32], 0)
* # => [1.5, 2.5]
@@ -1778,7 +2035,7 @@ io_buffer_get_value(VALUE self, VALUE type, VALUE _offset)
static VALUE
io_buffer_get_values(VALUE self, VALUE buffer_types, VALUE _offset)
{
- size_t offset = NUM2SIZET(_offset);
+ size_t offset = io_buffer_extract_offset(_offset);
const void *base;
size_t size;
@@ -1799,6 +2056,40 @@ io_buffer_get_values(VALUE self, VALUE buffer_types, VALUE _offset)
return array;
}
+// Extract a count argument, which must be a positive integer.
+// Count is generally considered relative to the number of things.
+static inline size_t
+io_buffer_extract_count(VALUE argument)
+{
+ if (rb_int_negative_p(argument)) {
+ rb_raise(rb_eArgError, "Count can't be negative!");
+ }
+
+ return NUM2SIZET(argument);
+}
+
+static inline void
+io_buffer_extract_offset_count(ID buffer_type, size_t size, int argc, VALUE *argv, size_t *offset, size_t *count)
+{
+ if (argc >= 1) {
+ *offset = io_buffer_extract_offset(argv[0]);
+ }
+ else {
+ *offset = 0;
+ }
+
+ if (argc >= 2) {
+ *count = io_buffer_extract_count(argv[1]);
+ }
+ else {
+ if (*offset > size) {
+ rb_raise(rb_eArgError, "The given offset is bigger than the buffer size!");
+ }
+
+ *count = (size - *offset) / io_buffer_buffer_type_size(buffer_type);
+ }
+}
+
/*
* call-seq:
* each(buffer_type, [offset, [count]]) {|offset, value| ...} -> self
@@ -1809,8 +2100,6 @@ io_buffer_get_values(VALUE self, VALUE buffer_types, VALUE _offset)
*
* If +count+ is given, only +count+ values will be yielded.
*
- * Example:
- *
* IO::Buffer.for("Hello World").each(:U8, 2, 2) do |offset, value|
* puts "#{offset}: #{value}"
* end
@@ -1835,21 +2124,8 @@ io_buffer_each(int argc, VALUE *argv, VALUE self)
buffer_type = RB_IO_BUFFER_DATA_TYPE_U8;
}
- size_t offset;
- if (argc >= 2) {
- offset = NUM2SIZET(argv[1]);
- }
- else {
- offset = 0;
- }
-
- size_t count;
- if (argc >= 3) {
- count = NUM2SIZET(argv[2]);
- }
- else {
- count = (size - offset) / io_buffer_buffer_type_size(buffer_type);
- }
+ size_t offset, count;
+ io_buffer_extract_offset_count(buffer_type, size, argc-1, argv+1, &offset, &count);
for (size_t i = 0; i < count; i++) {
size_t current_offset = offset;
@@ -1867,8 +2143,6 @@ io_buffer_each(int argc, VALUE *argv, VALUE self)
*
* If +count+ is given, only +count+ values will be returned.
*
- * Example:
- *
* IO::Buffer.for("Hello World").values(:U8, 2, 2)
* # => [108, 108]
*/
@@ -1888,21 +2162,8 @@ io_buffer_values(int argc, VALUE *argv, VALUE self)
buffer_type = RB_IO_BUFFER_DATA_TYPE_U8;
}
- size_t offset;
- if (argc >= 2) {
- offset = NUM2SIZET(argv[1]);
- }
- else {
- offset = 0;
- }
-
- size_t count;
- if (argc >= 3) {
- count = NUM2SIZET(argv[2]);
- }
- else {
- count = (size - offset) / io_buffer_buffer_type_size(buffer_type);
- }
+ size_t offset, count;
+ io_buffer_extract_offset_count(buffer_type, size, argc-1, argv+1, &offset, &count);
VALUE array = rb_ary_new_capa(count);
@@ -1923,8 +2184,6 @@ io_buffer_values(int argc, VALUE *argv, VALUE self)
*
* If +count+ is given, only +count+ bytes will be yielded.
*
- * Example:
- *
* IO::Buffer.for("Hello World").each_byte(2, 2) do |offset, byte|
* puts "#{offset}: #{byte}"
* end
@@ -1941,21 +2200,8 @@ io_buffer_each_byte(int argc, VALUE *argv, VALUE self)
rb_io_buffer_get_bytes_for_reading(self, &base, &size);
- size_t offset;
- if (argc >= 2) {
- offset = NUM2SIZET(argv[1]);
- }
- else {
- offset = 0;
- }
-
- size_t count;
- if (argc >= 3) {
- count = NUM2SIZET(argv[2]);
- }
- else {
- count = (size - offset);
- }
+ size_t offset, count;
+ io_buffer_extract_offset_count(RB_IO_BUFFER_DATA_TYPE_U8, size, argc-1, argv+1, &offset, &count);
for (size_t i = 0; i < count; i++) {
unsigned char *value = (unsigned char *)base + i + offset;
@@ -2031,7 +2277,7 @@ io_buffer_set_value(VALUE self, VALUE type, VALUE _offset, VALUE value)
{
void *base;
size_t size;
- size_t offset = NUM2SIZET(_offset);
+ size_t offset = io_buffer_extract_offset(_offset);
rb_io_buffer_get_bytes_for_writing(self, &base, &size);
@@ -2047,8 +2293,6 @@ io_buffer_set_value(VALUE self, VALUE type, VALUE _offset, VALUE value)
* should be an array of symbols as described in #get_value. +values+ should
* be an array of values to write.
*
- * Example:
- *
* buffer = IO::Buffer.new(8)
* buffer.set_values([:U8, :U16], 0, [1, 2])
* buffer
@@ -2071,7 +2315,7 @@ io_buffer_set_values(VALUE self, VALUE buffer_types, VALUE _offset, VALUE values
rb_raise(rb_eArgError, "Argument buffer_types and values should have the same length!");
}
- size_t offset = NUM2SIZET(_offset);
+ size_t offset = io_buffer_extract_offset(_offset);
void *base;
size_t size;
@@ -2096,7 +2340,7 @@ io_buffer_memcpy(struct rb_io_buffer *buffer, size_t offset, const void *source_
io_buffer_validate_range(buffer, offset, length);
if (source_offset + length > source_size) {
- rb_raise(rb_eArgError, "The computed source range exceeds the size of the source!");
+ rb_raise(rb_eArgError, "The computed source range exceeds the size of the source buffer!");
}
memcpy((unsigned char*)base+offset, (unsigned char*)source_base+source_offset, length);
@@ -2106,21 +2350,18 @@ io_buffer_memcpy(struct rb_io_buffer *buffer, size_t offset, const void *source_
static VALUE
io_buffer_copy_from(struct rb_io_buffer *buffer, const void *source_base, size_t source_size, int argc, VALUE *argv)
{
- size_t offset;
+ size_t offset = 0;
size_t length;
size_t source_offset;
// The offset we copy into the buffer:
if (argc >= 1) {
- offset = NUM2SIZET(argv[0]);
- }
- else {
- offset = 0;
+ offset = io_buffer_extract_offset(argv[0]);
}
// The offset we start from within the string:
if (argc >= 3) {
- source_offset = NUM2SIZET(argv[2]);
+ source_offset = io_buffer_extract_offset(argv[2]);
if (source_offset > source_size) {
rb_raise(rb_eArgError, "The given source offset is bigger than the source itself!");
@@ -2132,7 +2373,7 @@ io_buffer_copy_from(struct rb_io_buffer *buffer, const void *source_base, size_t
// The length we are going to copy:
if (argc >= 2 && !RB_NIL_P(argv[1])) {
- length = NUM2SIZET(argv[1]);
+ length = io_buffer_extract_length(argv[1]);
}
else {
// Default to the source offset -> source size:
@@ -2172,7 +2413,7 @@ rb_io_buffer_initialize_copy(VALUE self, VALUE source)
rb_io_buffer_get_bytes_for_reading(source, &source_base, &source_size);
- io_buffer_initialize(buffer, NULL, source_size, io_flags_for_size(source_size), Qnil);
+ io_buffer_initialize(self, buffer, NULL, source_size, io_flags_for_size(source_size), Qnil);
return io_buffer_copy_from(buffer, source_base, source_size, 0, NULL);
}
@@ -2181,8 +2422,8 @@ rb_io_buffer_initialize_copy(VALUE self, VALUE source)
* call-seq:
* copy(source, [offset, [length, [source_offset]]]) -> size
*
- * Efficiently copy buffer from a source IO::Buffer into the buffer,
- * at +offset+ using +memcpy+. For copying String instances, see #set_string.
+ * Efficiently copy from a source IO::Buffer into the buffer, at +offset+
+ * using +memcpy+. For copying String instances, see #set_string.
*
* buffer = IO::Buffer.new(32)
* # =>
@@ -2229,7 +2470,7 @@ rb_io_buffer_initialize_copy(VALUE self, VALUE source)
*
* buffer = IO::Buffer.new(2)
* buffer.copy(IO::Buffer.for('test'), 0)
- * # in `copy': Specified offset+length exceeds source size! (ArgumentError)
+ * # in `copy': Specified offset+length is bigger than the buffer size! (ArgumentError)
*/
static VALUE
io_buffer_copy(int argc, VALUE *argv, VALUE self)
@@ -2267,31 +2508,20 @@ io_buffer_get_string(int argc, VALUE *argv, VALUE self)
{
rb_check_arity(argc, 0, 3);
- struct rb_io_buffer *buffer = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+ size_t offset, length;
+ struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);
const void *base;
size_t size;
io_buffer_get_bytes_for_reading(buffer, &base, &size);
- size_t offset = 0;
- size_t length = size;
- rb_encoding *encoding = rb_ascii8bit_encoding();
-
- if (argc >= 1) {
- offset = NUM2SIZET(argv[0]);
- }
-
- if (argc >= 2 && !RB_NIL_P(argv[1])) {
- length = NUM2SIZET(argv[1]);
- }
- else {
- length = size - offset;
- }
-
+ rb_encoding *encoding;
if (argc >= 3) {
encoding = rb_find_encoding(argv[2]);
}
+ else {
+ encoding = rb_ascii8bit_encoding();
+ }
io_buffer_validate_range(buffer, offset, length);
@@ -2301,8 +2531,8 @@ io_buffer_get_string(int argc, VALUE *argv, VALUE self)
/*
* call-seq: set_string(string, [offset, [length, [source_offset]]]) -> size
*
- * Efficiently copy buffer from a source String into the buffer,
- * at +offset+ using +memcpy+.
+ * Efficiently copy from a source String into the buffer, at +offset+ using
+ * +memcpy+.
*
* buf = IO::Buffer.new(8)
* # =>
@@ -2340,14 +2570,14 @@ io_buffer_set_string(int argc, VALUE *argv, VALUE self)
void
rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length)
{
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
void *base;
size_t size;
+ io_buffer_get_bytes_for_writing(buffer, &base, &size);
- rb_io_buffer_get_bytes_for_writing(self, &base, &size);
-
- if (offset + length > size) {
- rb_raise(rb_eArgError, "The given offset + length out of bounds!");
- }
+ io_buffer_validate_range(buffer, offset, length);
memset((char*)base + offset, value, length);
}
@@ -2388,26 +2618,13 @@ io_buffer_clear(int argc, VALUE *argv, VALUE self)
{
rb_check_arity(argc, 0, 3);
- struct rb_io_buffer *buffer = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
-
uint8_t value = 0;
if (argc >= 1) {
value = NUM2UINT(argv[0]);
}
- size_t offset = 0;
- if (argc >= 2) {
- offset = NUM2SIZET(argv[1]);
- }
-
- size_t length;
- if (argc >= 3) {
- length = NUM2SIZET(argv[2]);
- }
- else {
- length = buffer->size - offset;
- }
+ size_t offset, length;
+ io_buffer_extract_offset_length(self, argc-1, argv+1, &offset, &length);
rb_io_buffer_clear(self, value, offset, length);
@@ -2486,35 +2703,6 @@ io_buffer_blocking_region(struct rb_io_buffer *buffer, rb_blocking_function_t *f
}
}
-static inline struct rb_io_buffer *
-io_buffer_extract_arguments(VALUE self, int argc, VALUE argv[], size_t *length, size_t *offset)
-{
- struct rb_io_buffer *buffer = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
-
- *offset = 0;
- if (argc >= 2) {
- if (rb_int_negative_p(argv[1])) {
- rb_raise(rb_eArgError, "Offset can't be negative!");
- }
-
- *offset = NUM2SIZET(argv[1]);
- }
-
- if (argc >= 1 && !NIL_P(argv[0])) {
- if (rb_int_negative_p(argv[0])) {
- rb_raise(rb_eArgError, "Length can't be negative!");
- }
-
- *length = NUM2SIZET(argv[0]);
- }
- else {
- *length = buffer->size - *offset;
- }
-
- return buffer;
-}
-
struct io_buffer_read_internal_argument {
// The file descriptor to read from:
int descriptor;
@@ -2604,8 +2792,6 @@ rb_io_buffer_read(VALUE self, VALUE io, size_t length, size_t offset)
* If +offset+ is not given, it defaults to zero, i.e. the beginning of the
* buffer.
*
- * Example:
- *
* IO::Buffer.for('test') do |buffer|
* p buffer
* # =>
@@ -2626,7 +2812,7 @@ io_buffer_read(int argc, VALUE *argv, VALUE self)
VALUE io = argv[0];
size_t length, offset;
- io_buffer_extract_arguments(self, argc-1, argv+1, &length, &offset);
+ io_buffer_extract_length_offset(self, argc-1, argv+1, &length, &offset);
return rb_io_buffer_read(self, io, length, offset);
}
@@ -2725,8 +2911,6 @@ rb_io_buffer_pread(VALUE self, VALUE io, rb_off_t from, size_t length, size_t of
* If +offset+ is not given, it defaults to zero, i.e. the beginning of the
* buffer.
*
- * Example:
- *
* IO::Buffer.for('test') do |buffer|
* p buffer
* # =>
@@ -2751,7 +2935,7 @@ io_buffer_pread(int argc, VALUE *argv, VALUE self)
rb_off_t from = NUM2OFFT(argv[1]);
size_t length, offset;
- io_buffer_extract_arguments(self, argc-2, argv+2, &length, &offset);
+ io_buffer_extract_length_offset(self, argc-2, argv+2, &length, &offset);
return rb_io_buffer_pread(self, io, from, length, offset);
}
@@ -2845,8 +3029,6 @@ rb_io_buffer_write(VALUE self, VALUE io, size_t length, size_t offset)
* If +offset+ is not given, it defaults to zero, i.e. the beginning of the
* buffer.
*
- * Example:
- *
* out = File.open('output.txt', 'wb')
* IO::Buffer.for('1234567').write(out, 3)
*
@@ -2860,7 +3042,7 @@ io_buffer_write(int argc, VALUE *argv, VALUE self)
VALUE io = argv[0];
size_t length, offset;
- io_buffer_extract_arguments(self, argc-1, argv+1, &length, &offset);
+ io_buffer_extract_length_offset(self, argc-1, argv+1, &length, &offset);
return rb_io_buffer_write(self, io, length, offset);
}
@@ -2969,8 +3151,6 @@ rb_io_buffer_pwrite(VALUE self, VALUE io, rb_off_t from, size_t length, size_t o
* If the +from+ position is beyond the end of the file, the gap will be
* filled with null (0 value) bytes.
*
- * Example:
- *
* out = File.open('output.txt', File::RDWR) # open for read/write, no truncation
* IO::Buffer.for('1234567').pwrite(out, 2, 3, 1)
*
@@ -2986,7 +3166,7 @@ io_buffer_pwrite(int argc, VALUE *argv, VALUE self)
rb_off_t from = NUM2OFFT(argv[1]);
size_t length, offset;
- io_buffer_extract_arguments(self, argc-2, argv+2, &length, &offset);
+ io_buffer_extract_length_offset(self, argc-2, argv+2, &length, &offset);
return rb_io_buffer_pwrite(self, io, from, length, offset);
}
@@ -3351,24 +3531,28 @@ io_buffer_not_inplace(VALUE self)
/*
* Document-class: IO::Buffer
*
- * IO::Buffer is a low-level efficient buffer for input/output. There are three
- * ways of using buffer:
+ * IO::Buffer is a efficient zero-copy buffer for input/output. There are
+ * typical use cases:
*
* * Create an empty buffer with ::new, fill it with buffer using #copy or
- * #set_value, #set_string, get buffer with #get_string;
+ * #set_value, #set_string, get buffer with #get_string or write it directly
+ * to some file with #write.
* * Create a buffer mapped to some string with ::for, then it could be used
* both for reading with #get_string or #get_value, and writing (writing will
- * change the source string, too);
+ * change the source string, too).
* * Create a buffer mapped to some file with ::map, then it could be used for
* reading and writing the underlying file.
+ * * Create a string of a fixed size with ::string, then #read into it, or
+ * modify it using #set_value.
*
* Interaction with string and file memory is performed by efficient low-level
* C mechanisms like `memcpy`.
*
* The class is meant to be an utility for implementing more high-level mechanisms
- * like Fiber::SchedulerInterface#io_read and Fiber::SchedulerInterface#io_write.
+ * like Fiber::Scheduler#io_read and Fiber::Scheduler#io_write and parsing binary
+ * protocols.
*
- * <b>Examples of usage:</b>
+ * == Examples of Usage
*
* Empty buffer:
*
@@ -3429,16 +3613,28 @@ io_buffer_not_inplace(VALUE self)
* File.read('test.txt')
* # => "t--- buffer"
*
- * <b>The class is experimental and the interface is subject to change.</b>
+ * <b>The class is experimental and the interface is subject to change, this
+ * is especially true of file mappings which may be removed entirely in
+ * the future.</b>
*/
void
Init_IO_Buffer(void)
{
rb_cIOBuffer = rb_define_class_under(rb_cIO, "Buffer", rb_cObject);
+
+ /* Raised when an operation would resize or re-allocate a locked buffer. */
rb_eIOBufferLockedError = rb_define_class_under(rb_cIOBuffer, "LockedError", rb_eRuntimeError);
+
+ /* Raised when the buffer cannot be allocated for some reason, or you try to use a buffer that's not allocated. */
rb_eIOBufferAllocationError = rb_define_class_under(rb_cIOBuffer, "AllocationError", rb_eRuntimeError);
+
+ /* Raised when you try to write to a read-only buffer, or resize an external buffer. */
rb_eIOBufferAccessError = rb_define_class_under(rb_cIOBuffer, "AccessError", rb_eRuntimeError);
+
+ /* Raised if you try to access a buffer slice which no longer references a valid memory range of the underlying source. */
rb_eIOBufferInvalidatedError = rb_define_class_under(rb_cIOBuffer, "InvalidatedError", rb_eRuntimeError);
+
+ /* Raised if the mask given to a binary operation is invalid, e.g. zero length or overlaps the target buffer. */
rb_eIOBufferMaskError = rb_define_class_under(rb_cIOBuffer, "MaskError", rb_eArgError);
rb_define_alloc_func(rb_cIOBuffer, rb_io_buffer_type_allocate);
@@ -3455,37 +3651,57 @@ Init_IO_Buffer(void)
RUBY_IO_BUFFER_DEFAULT_SIZE = io_buffer_default_size(RUBY_IO_BUFFER_PAGE_SIZE);
- // Efficient sizing of mapped buffers:
+ /* The operating system page size. Used for efficient page-aligned memory allocations. */
rb_define_const(rb_cIOBuffer, "PAGE_SIZE", SIZET2NUM(RUBY_IO_BUFFER_PAGE_SIZE));
+
+ /* The default buffer size, typically a (small) multiple of the PAGE_SIZE.
+ Can be explicitly specified by setting the RUBY_IO_BUFFER_DEFAULT_SIZE
+ environment variable. */
rb_define_const(rb_cIOBuffer, "DEFAULT_SIZE", SIZET2NUM(RUBY_IO_BUFFER_DEFAULT_SIZE));
rb_define_singleton_method(rb_cIOBuffer, "map", io_buffer_map, -1);
- // General use:
rb_define_method(rb_cIOBuffer, "initialize", rb_io_buffer_initialize, -1);
rb_define_method(rb_cIOBuffer, "initialize_copy", rb_io_buffer_initialize_copy, 1);
rb_define_method(rb_cIOBuffer, "inspect", rb_io_buffer_inspect, 0);
- rb_define_method(rb_cIOBuffer, "hexdump", rb_io_buffer_hexdump, 0);
+ rb_define_method(rb_cIOBuffer, "hexdump", rb_io_buffer_hexdump, -1);
rb_define_method(rb_cIOBuffer, "to_s", rb_io_buffer_to_s, 0);
rb_define_method(rb_cIOBuffer, "size", rb_io_buffer_size, 0);
rb_define_method(rb_cIOBuffer, "valid?", rb_io_buffer_valid_p, 0);
- // Ownership:
rb_define_method(rb_cIOBuffer, "transfer", rb_io_buffer_transfer, 0);
- // Flags:
+ /* Indicates that the memory in the buffer is owned by someone else. See #external? for more details. */
rb_define_const(rb_cIOBuffer, "EXTERNAL", RB_INT2NUM(RB_IO_BUFFER_EXTERNAL));
+
+ /* Indicates that the memory in the buffer is owned by the buffer. See #internal? for more details. */
rb_define_const(rb_cIOBuffer, "INTERNAL", RB_INT2NUM(RB_IO_BUFFER_INTERNAL));
+
+ /* Indicates that the memory in the buffer is mapped by the operating system. See #mapped? for more details. */
rb_define_const(rb_cIOBuffer, "MAPPED", RB_INT2NUM(RB_IO_BUFFER_MAPPED));
+
+ /* Indicates that the memory in the buffer is also mapped such that it can be shared with other processes. See #shared? for more details. */
rb_define_const(rb_cIOBuffer, "SHARED", RB_INT2NUM(RB_IO_BUFFER_SHARED));
+
+ /* Indicates that the memory in the buffer is locked and cannot be resized or freed. See #locked? and #locked for more details. */
rb_define_const(rb_cIOBuffer, "LOCKED", RB_INT2NUM(RB_IO_BUFFER_LOCKED));
+
+ /* Indicates that the memory in the buffer is mapped privately and changes won't be replicated to the underlying file. See #private? for more details. */
rb_define_const(rb_cIOBuffer, "PRIVATE", RB_INT2NUM(RB_IO_BUFFER_PRIVATE));
+
+ /* Indicates that the memory in the buffer is read only, and attempts to modify it will fail. See #readonly? for more details.*/
rb_define_const(rb_cIOBuffer, "READONLY", RB_INT2NUM(RB_IO_BUFFER_READONLY));
- // Endian:
+ /* Refers to little endian byte order, where the least significant byte is stored first. See #get_value for more details. */
rb_define_const(rb_cIOBuffer, "LITTLE_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_LITTLE_ENDIAN));
+
+ /* Refers to big endian byte order, where the most significant byte is stored first. See #get_value for more details. */
rb_define_const(rb_cIOBuffer, "BIG_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_BIG_ENDIAN));
+
+ /* Refers to the byte order of the host machine. See #get_value for more details. */
rb_define_const(rb_cIOBuffer, "HOST_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_HOST_ENDIAN));
+
+ /* Refers to network byte order, which is the same as big endian. See #get_value for more details. */
rb_define_const(rb_cIOBuffer, "NETWORK_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_NETWORK_ENDIAN));
rb_define_method(rb_cIOBuffer, "null?", rb_io_buffer_null_p, 0);
@@ -3495,6 +3711,7 @@ Init_IO_Buffer(void)
rb_define_method(rb_cIOBuffer, "mapped?", rb_io_buffer_mapped_p, 0);
rb_define_method(rb_cIOBuffer, "shared?", rb_io_buffer_shared_p, 0);
rb_define_method(rb_cIOBuffer, "locked?", rb_io_buffer_locked_p, 0);
+ rb_define_method(rb_cIOBuffer, "private?", rb_io_buffer_private_p, 0);
rb_define_method(rb_cIOBuffer, "readonly?", io_buffer_readonly_p, 0);
// Locking to prevent changes while using pointer:
diff --git a/iseq.c b/iseq.c
index bbeb537053..34381c6a46 100644
--- a/iseq.c
+++ b/iseq.c
@@ -28,7 +28,8 @@
#include "internal/file.h"
#include "internal/gc.h"
#include "internal/hash.h"
-#include "internal/parse.h"
+#include "internal/io.h"
+#include "internal/ruby_parser.h"
#include "internal/sanitizers.h"
#include "internal/symbol.h"
#include "internal/thread.h"
@@ -166,28 +167,33 @@ rb_iseq_free(const rb_iseq_t *iseq)
struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
rb_rjit_free_iseq(iseq); /* Notify RJIT */
#if USE_YJIT
- rb_yjit_iseq_free(body->yjit_payload);
+ rb_yjit_iseq_free(iseq);
+ if (FL_TEST_RAW((VALUE)iseq, ISEQ_TRANSLATED)) {
+ RUBY_ASSERT(rb_yjit_live_iseq_count > 0);
+ rb_yjit_live_iseq_count--;
+ }
#endif
ruby_xfree((void *)body->iseq_encoded);
ruby_xfree((void *)body->insns_info.body);
- if (body->insns_info.positions) ruby_xfree((void *)body->insns_info.positions);
+ ruby_xfree((void *)body->insns_info.positions);
#if VM_INSN_INFO_TABLE_IMPL == 2
- if (body->insns_info.succ_index_table) ruby_xfree(body->insns_info.succ_index_table);
+ ruby_xfree(body->insns_info.succ_index_table);
#endif
if (LIKELY(body->local_table != rb_iseq_shared_exc_local_tbl))
ruby_xfree((void *)body->local_table);
ruby_xfree((void *)body->is_entries);
-
- if (body->call_data) {
- ruby_xfree(body->call_data);
- }
+ ruby_xfree(body->call_data);
ruby_xfree((void *)body->catch_table);
ruby_xfree((void *)body->param.opt_table);
if (ISEQ_MBITS_BUFLEN(body->iseq_size) > 1 && body->mark_bits.list) {
ruby_xfree((void *)body->mark_bits.list);
}
+ ruby_xfree(body->variable.original_iseq);
+
if (body->param.keyword != NULL) {
+ if (body->param.keyword->table != &body->local_table[body->param.keyword->bits_start - body->param.keyword->num])
+ ruby_xfree((void *)body->param.keyword->table);
ruby_xfree((void *)body->param.keyword->default_values);
ruby_xfree((void *)body->param.keyword);
}
@@ -282,6 +288,33 @@ rb_iseq_mark_and_move_each_value(const rb_iseq_t *iseq, VALUE *original_iseq)
}
}
+static bool
+cc_is_active(const struct rb_callcache *cc, bool reference_updating)
+{
+ if (cc) {
+ if (cc == rb_vm_empty_cc() || rb_vm_empty_cc_for_super()) {
+ return false;
+ }
+
+ if (reference_updating) {
+ cc = (const struct rb_callcache *)rb_gc_location((VALUE)cc);
+ }
+
+ if (vm_cc_markable(cc)) {
+ if (cc->klass) { // cc is not invalidated
+ const struct rb_callable_method_entry_struct *cme = vm_cc_cme(cc);
+ if (reference_updating) {
+ cme = (const struct rb_callable_method_entry_struct *)rb_gc_location((VALUE)cme);
+ }
+ if (!METHOD_ENTRY_INVALIDATED(cme)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
void
rb_iseq_mark_and_move(rb_iseq_t *iseq, bool reference_updating)
{
@@ -310,27 +343,11 @@ rb_iseq_mark_and_move(rb_iseq_t *iseq, bool reference_updating)
if (cds[i].ci) rb_gc_mark_and_move_ptr(&cds[i].ci);
- const struct rb_callcache *cc = cds[i].cc;
- if (cc) {
- if (reference_updating) {
- cc = (const struct rb_callcache *)rb_gc_location((VALUE)cc);
- }
-
- if (vm_cc_markable(cc)) {
- VM_ASSERT((cc->flags & VM_CALLCACHE_ON_STACK) == 0);
-
- const struct rb_callable_method_entry_struct *cme = vm_cc_cme(cc);
- if (reference_updating) {
- cme = (const struct rb_callable_method_entry_struct *)rb_gc_location((VALUE)cme);
- }
-
- if (cc->klass && !METHOD_ENTRY_INVALIDATED(cme)) {
- rb_gc_mark_and_move_ptr(&cds[i].cc);
- }
- else {
- cds[i].cc = rb_vm_empty_cc();
- }
- }
+ if (cc_is_active(cds[i].cc, reference_updating)) {
+ rb_gc_mark_and_move_ptr(&cds[i].cc);
+ }
+ else {
+ cds[i].cc = rb_vm_empty_cc();
}
}
}
@@ -360,7 +377,7 @@ rb_iseq_mark_and_move(rb_iseq_t *iseq, bool reference_updating)
rb_rjit_iseq_update_references(body);
#endif
#if USE_YJIT
- rb_yjit_iseq_update_references(body->yjit_payload);
+ rb_yjit_iseq_update_references(iseq);
#endif
}
else {
@@ -379,7 +396,14 @@ rb_iseq_mark_and_move(rb_iseq_t *iseq, bool reference_updating)
else if (FL_TEST_RAW((VALUE)iseq, ISEQ_USE_COMPILE_DATA)) {
const struct iseq_compile_data *const compile_data = ISEQ_COMPILE_DATA(iseq);
- rb_iseq_mark_and_move_insn_storage(compile_data->insn.storage_head);
+ if (!reference_updating) {
+ /* The operands in each instruction needs to be pinned because
+ * if auto-compaction runs in iseq_set_sequence, then the objects
+ * could exist on the generated_iseq buffer, which would not be
+ * reference updated which can lead to T_MOVED (and subsequently
+ * T_NONE) objects on the iseq. */
+ rb_iseq_mark_and_pin_insn_storage(compile_data->insn.storage_head);
+ }
rb_gc_mark_and_move((VALUE *)&compile_data->err_info);
rb_gc_mark_and_move((VALUE *)&compile_data->catch_table_ary);
@@ -518,6 +542,11 @@ iseq_location_setup(rb_iseq_t *iseq, VALUE name, VALUE path, VALUE realpath, int
RB_OBJ_WRITE(iseq, &loc->label, name);
RB_OBJ_WRITE(iseq, &loc->base_label, name);
loc->first_lineno = first_lineno;
+
+ if (ISEQ_BODY(iseq)->local_iseq == iseq && strcmp(RSTRING_PTR(name), "initialize") == 0) {
+ ISEQ_BODY(iseq)->param.flags.use_block = 1;
+ }
+
if (code_location) {
loc->node_id = node_id;
loc->code_location = *code_location;
@@ -700,19 +729,26 @@ finish_iseq_build(rb_iseq_t *iseq)
}
static rb_compile_option_t COMPILE_OPTION_DEFAULT = {
- OPT_INLINE_CONST_CACHE, /* int inline_const_cache; */
- OPT_PEEPHOLE_OPTIMIZATION, /* int peephole_optimization; */
- OPT_TAILCALL_OPTIMIZATION, /* int tailcall_optimization */
- OPT_SPECIALISED_INSTRUCTION, /* int specialized_instruction; */
- OPT_OPERANDS_UNIFICATION, /* int operands_unification; */
- OPT_INSTRUCTIONS_UNIFICATION, /* int instructions_unification; */
- OPT_STACK_CACHING, /* int stack_caching; */
- OPT_FROZEN_STRING_LITERAL,
- OPT_DEBUG_FROZEN_STRING_LITERAL,
- TRUE, /* coverage_enabled */
+ .inline_const_cache = OPT_INLINE_CONST_CACHE,
+ .peephole_optimization = OPT_PEEPHOLE_OPTIMIZATION,
+ .tailcall_optimization = OPT_TAILCALL_OPTIMIZATION,
+ .specialized_instruction = OPT_SPECIALISED_INSTRUCTION,
+ .operands_unification = OPT_OPERANDS_UNIFICATION,
+ .instructions_unification = OPT_INSTRUCTIONS_UNIFICATION,
+ .frozen_string_literal = OPT_FROZEN_STRING_LITERAL,
+ .debug_frozen_string_literal = OPT_DEBUG_FROZEN_STRING_LITERAL,
+ .coverage_enabled = TRUE,
};
-static const rb_compile_option_t COMPILE_OPTION_FALSE = {0};
+static const rb_compile_option_t COMPILE_OPTION_FALSE = {
+ .frozen_string_literal = -1, // unspecified
+};
+
+int
+rb_iseq_opt_frozen_string_literal(void)
+{
+ return COMPILE_OPTION_DEFAULT.frozen_string_literal;
+}
static void
set_compile_option_from_hash(rb_compile_option_t *option, VALUE opt)
@@ -723,7 +759,7 @@ set_compile_option_from_hash(rb_compile_option_t *option, VALUE opt)
else if (flag == Qfalse) { (o)->mem = 0; } \
}
#define SET_COMPILE_OPTION_NUM(o, h, mem) \
- { VALUE num = rb_hash_aref(opt, ID2SYM(rb_intern(#mem))); \
+ { VALUE num = rb_hash_aref((h), ID2SYM(rb_intern(#mem))); \
if (!NIL_P(num)) (o)->mem = NUM2INT(num); \
}
SET_COMPILE_OPTION(option, opt, inline_const_cache);
@@ -732,7 +768,6 @@ set_compile_option_from_hash(rb_compile_option_t *option, VALUE opt)
SET_COMPILE_OPTION(option, opt, specialized_instruction);
SET_COMPILE_OPTION(option, opt, operands_unification);
SET_COMPILE_OPTION(option, opt, instructions_unification);
- SET_COMPILE_OPTION(option, opt, stack_caching);
SET_COMPILE_OPTION(option, opt, frozen_string_literal);
SET_COMPILE_OPTION(option, opt, debug_frozen_string_literal);
SET_COMPILE_OPTION(option, opt, coverage_enabled);
@@ -741,11 +776,17 @@ set_compile_option_from_hash(rb_compile_option_t *option, VALUE opt)
#undef SET_COMPILE_OPTION_NUM
}
-static void
-rb_iseq_make_compile_option(rb_compile_option_t *option, VALUE opt)
+static rb_compile_option_t *
+set_compile_option_from_ast(rb_compile_option_t *option, const rb_ast_body_t *ast)
{
- Check_Type(opt, T_HASH);
- set_compile_option_from_hash(option, opt);
+#define SET_COMPILE_OPTION(o, a, mem) \
+ ((a)->mem < 0 ? 0 : ((o)->mem = (a)->mem > 0))
+ SET_COMPILE_OPTION(option, ast, coverage_enabled);
+#undef SET_COMPILE_OPTION
+ if (ast->frozen_string_literal >= 0) {
+ option->frozen_string_literal = ast->frozen_string_literal;
+ }
+ return option;
}
static void
@@ -786,43 +827,36 @@ make_compile_option_value(rb_compile_option_t *option)
SET_COMPILE_OPTION(option, opt, specialized_instruction);
SET_COMPILE_OPTION(option, opt, operands_unification);
SET_COMPILE_OPTION(option, opt, instructions_unification);
- SET_COMPILE_OPTION(option, opt, stack_caching);
- SET_COMPILE_OPTION(option, opt, frozen_string_literal);
SET_COMPILE_OPTION(option, opt, debug_frozen_string_literal);
SET_COMPILE_OPTION(option, opt, coverage_enabled);
SET_COMPILE_OPTION_NUM(option, opt, debug_level);
}
#undef SET_COMPILE_OPTION
#undef SET_COMPILE_OPTION_NUM
+ VALUE frozen_string_literal = option->frozen_string_literal == -1 ? Qnil : RBOOL(option->frozen_string_literal);
+ rb_hash_aset(opt, ID2SYM(rb_intern("frozen_string_literal")), frozen_string_literal);
return opt;
}
rb_iseq_t *
-rb_iseq_new(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath,
+rb_iseq_new(const VALUE vast, VALUE name, VALUE path, VALUE realpath,
const rb_iseq_t *parent, enum rb_iseq_type type)
{
- return rb_iseq_new_with_opt(ast, name, path, realpath, 0, parent,
- 0, type, &COMPILE_OPTION_DEFAULT);
+ return rb_iseq_new_with_opt(vast, name, path, realpath, 0, parent,
+ 0, type, &COMPILE_OPTION_DEFAULT,
+ Qnil);
}
static int
-ast_line_count(const rb_ast_body_t *ast)
+ast_line_count(const VALUE vast)
{
- if (ast->script_lines == Qfalse) {
- // this occurs when failed to parse the source code with a syntax error
- return 0;
- }
- if (RB_TYPE_P(ast->script_lines, T_ARRAY)){
- return (int)RARRAY_LEN(ast->script_lines);
- }
- return FIX2INT(ast->script_lines);
+ rb_ast_t *ast = rb_ruby_ast_data_get(vast);
+ return ast->body.line_count;
}
static VALUE
-iseq_setup_coverage(VALUE coverages, VALUE path, const rb_ast_body_t *ast, int line_offset)
+iseq_setup_coverage(VALUE coverages, VALUE path, int line_count)
{
- int line_count = line_offset + ast_line_count(ast);
-
if (line_count >= 0) {
int len = (rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES) ? 0 : line_count;
@@ -836,45 +870,89 @@ iseq_setup_coverage(VALUE coverages, VALUE path, const rb_ast_body_t *ast, int l
}
static inline void
-iseq_new_setup_coverage(VALUE path, const rb_ast_body_t *ast, int line_offset)
+iseq_new_setup_coverage(VALUE path, int line_count)
{
VALUE coverages = rb_get_coverages();
if (RTEST(coverages)) {
- iseq_setup_coverage(coverages, path, ast, line_offset);
+ iseq_setup_coverage(coverages, path, line_count);
}
}
rb_iseq_t *
-rb_iseq_new_top(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent)
+rb_iseq_new_top(const VALUE vast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent)
+{
+ iseq_new_setup_coverage(path, ast_line_count(vast));
+
+ return rb_iseq_new_with_opt(vast, name, path, realpath, 0, parent, 0,
+ ISEQ_TYPE_TOP, &COMPILE_OPTION_DEFAULT,
+ Qnil);
+}
+
+/**
+ * The main entry-point into the prism compiler when a file is required.
+ */
+rb_iseq_t *
+pm_iseq_new_top(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent)
{
- iseq_new_setup_coverage(path, ast, 0);
+ iseq_new_setup_coverage(path, (int) (node->parser->newline_list.size - 1));
- return rb_iseq_new_with_opt(ast, name, path, realpath, 0, parent, 0,
+ return pm_iseq_new_with_opt(node, name, path, realpath, 0, parent, 0,
ISEQ_TYPE_TOP, &COMPILE_OPTION_DEFAULT);
}
rb_iseq_t *
-rb_iseq_new_main(const rb_ast_body_t *ast, VALUE path, VALUE realpath, const rb_iseq_t *parent, int opt)
+rb_iseq_new_main(const VALUE vast, VALUE path, VALUE realpath, const rb_iseq_t *parent, int opt)
+{
+ iseq_new_setup_coverage(path, ast_line_count(vast));
+
+ return rb_iseq_new_with_opt(vast, rb_fstring_lit("<main>"),
+ path, realpath, 0,
+ parent, 0, ISEQ_TYPE_MAIN, opt ? &COMPILE_OPTION_DEFAULT : &COMPILE_OPTION_FALSE,
+ Qnil);
+}
+
+/**
+ * The main entry-point into the prism compiler when a file is executed as the
+ * main file in the program.
+ */
+rb_iseq_t *
+pm_iseq_new_main(pm_scope_node_t *node, VALUE path, VALUE realpath, const rb_iseq_t *parent, int opt)
{
- iseq_new_setup_coverage(path, ast, 0);
+ iseq_new_setup_coverage(path, (int) (node->parser->newline_list.size - 1));
- return rb_iseq_new_with_opt(ast, rb_fstring_lit("<main>"),
+ return pm_iseq_new_with_opt(node, rb_fstring_lit("<main>"),
path, realpath, 0,
parent, 0, ISEQ_TYPE_MAIN, opt ? &COMPILE_OPTION_DEFAULT : &COMPILE_OPTION_FALSE);
}
rb_iseq_t *
-rb_iseq_new_eval(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth)
+rb_iseq_new_eval(const VALUE vast, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth)
{
if (rb_get_coverage_mode() & COVERAGE_TARGET_EVAL) {
VALUE coverages = rb_get_coverages();
if (RTEST(coverages) && RTEST(path) && !RTEST(rb_hash_has_key(coverages, path))) {
- iseq_setup_coverage(coverages, path, ast, first_lineno - 1);
+ iseq_setup_coverage(coverages, path, ast_line_count(vast) + first_lineno - 1);
}
}
- return rb_iseq_new_with_opt(ast, name, path, realpath, first_lineno,
+ return rb_iseq_new_with_opt(vast, name, path, realpath, first_lineno,
+ parent, isolated_depth, ISEQ_TYPE_EVAL, &COMPILE_OPTION_DEFAULT,
+ Qnil);
+}
+
+rb_iseq_t *
+pm_iseq_new_eval(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath,
+ int first_lineno, const rb_iseq_t *parent, int isolated_depth)
+{
+ if (rb_get_coverage_mode() & COVERAGE_TARGET_EVAL) {
+ VALUE coverages = rb_get_coverages();
+ if (RTEST(coverages) && RTEST(path) && !RTEST(rb_hash_has_key(coverages, path))) {
+ iseq_setup_coverage(coverages, path, ((int) (node->parser->newline_list.size - 1)) + first_lineno - 1);
+ }
+ }
+
+ return pm_iseq_new_with_opt(node, name, path, realpath, first_lineno,
parent, isolated_depth, ISEQ_TYPE_EVAL, &COMPILE_OPTION_DEFAULT);
}
@@ -893,34 +971,36 @@ iseq_translate(rb_iseq_t *iseq)
}
rb_iseq_t *
-rb_iseq_new_with_opt(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath,
+rb_iseq_new_with_opt(const VALUE vast, VALUE name, VALUE path, VALUE realpath,
int first_lineno, const rb_iseq_t *parent, int isolated_depth,
- enum rb_iseq_type type, const rb_compile_option_t *option)
+ enum rb_iseq_type type, const rb_compile_option_t *option,
+ VALUE script_lines)
{
- const NODE *node = ast ? ast->root : 0;
+ rb_ast_t *ast = rb_ruby_ast_data_get(vast);
+ rb_ast_body_t *body = ast ? &ast->body : NULL;
+ const NODE *node = body ? body->root : 0;
/* TODO: argument check */
rb_iseq_t *iseq = iseq_alloc();
rb_compile_option_t new_opt;
- if (option) {
+ if (!option) option = &COMPILE_OPTION_DEFAULT;
+ if (body) {
new_opt = *option;
+ option = set_compile_option_from_ast(&new_opt, body);
}
- else {
- new_opt = COMPILE_OPTION_DEFAULT;
- }
- if (ast && ast->compile_option) rb_iseq_make_compile_option(&new_opt, ast->compile_option);
-
- VALUE script_lines = Qnil;
- if (ast && !FIXNUM_P(ast->script_lines) && ast->script_lines) {
- script_lines = ast->script_lines;
+ if (!NIL_P(script_lines)) {
+ // noop
+ }
+ else if (body && body->script_lines) {
+ script_lines = rb_parser_build_script_lines_from(body->script_lines);
}
else if (parent) {
script_lines = ISEQ_BODY(parent)->variable.script_lines;
}
prepare_iseq_build(iseq, name, path, realpath, first_lineno, node ? &node->nd_loc : NULL, node ? nd_node_id(node) : -1,
- parent, isolated_depth, type, script_lines, &new_opt);
+ parent, isolated_depth, type, script_lines, option);
rb_iseq_compile_node(iseq, node);
finish_iseq_build(iseq);
@@ -928,6 +1008,49 @@ rb_iseq_new_with_opt(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE rea
return iseq_translate(iseq);
}
+/**
+ * This is a step in the prism compiler that is called once all of the various
+ * options have been established. It is called from one of the pm_iseq_new_*
+ * functions or from the RubyVM::InstructionSequence APIs. It is responsible for
+ * allocating the instruction sequence, calling into the compiler, and returning
+ * the built instruction sequence.
+ *
+ * Importantly, this is also the function where the compiler is re-entered to
+ * compile child instruction sequences. A child instruction sequence is always
+ * compiled using a scope node, which is why we cast it explicitly to that here
+ * in the parameters (as opposed to accepting a generic pm_node_t *).
+ */
+rb_iseq_t *
+pm_iseq_new_with_opt(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath,
+ int first_lineno, const rb_iseq_t *parent, int isolated_depth,
+ enum rb_iseq_type type, const rb_compile_option_t *option)
+{
+ rb_iseq_t *iseq = iseq_alloc();
+ ISEQ_BODY(iseq)->prism = true;
+ ISEQ_BODY(iseq)->param.flags.use_block = true; // unused block warning is not supported yet
+
+ if (!option) option = &COMPILE_OPTION_DEFAULT;
+
+ pm_location_t *location = &node->base.location;
+ int32_t start_line = node->parser->start_line;
+
+ pm_line_column_t start = pm_newline_list_line_column(&node->parser->newline_list, location->start, start_line);
+ pm_line_column_t end = pm_newline_list_line_column(&node->parser->newline_list, location->end, start_line);
+
+ rb_code_location_t code_location = (rb_code_location_t) {
+ .beg_pos = { .lineno = (int) start.line, .column = (int) start.column },
+ .end_pos = { .lineno = (int) end.line, .column = (int) end.column }
+ };
+
+ prepare_iseq_build(iseq, name, path, realpath, first_lineno, &code_location, -1,
+ parent, isolated_depth, type, Qnil, option);
+
+ pm_iseq_compile_node(iseq, node);
+ finish_iseq_build(iseq);
+
+ return iseq_translate(iseq);
+}
+
rb_iseq_t *
rb_iseq_new_with_callback(
const struct rb_iseq_new_with_callback_callback_func * ifunc,
@@ -1049,6 +1172,10 @@ iseq_load(VALUE data, const rb_iseq_t *parent, VALUE opt)
tmp_loc.end_pos.column = NUM2INT(rb_ary_entry(code_location, 3));
}
+ if (SYM2ID(rb_hash_aref(misc, ID2SYM(rb_intern("parser")))) == rb_intern("prism")) {
+ ISEQ_BODY(iseq)->prism = true;
+ }
+
make_compile_option(&option, opt);
option.peephole_optimization = FALSE; /* because peephole optimization can modify original iseq */
prepare_iseq_build(iseq, name, path, realpath, first_lineno, &tmp_loc, NUM2INT(node_id),
@@ -1088,9 +1215,10 @@ rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, V
#else
# define INITIALIZED /* volatile */
#endif
- rb_ast_t *(*parse)(VALUE vparser, VALUE fname, VALUE file, int start);
+ VALUE (*parse)(VALUE vparser, VALUE fname, VALUE file, int start);
int ln;
- rb_ast_t *INITIALIZED ast;
+ VALUE INITIALIZED vast;
+ rb_ast_t *ast;
VALUE name = rb_fstring_lit("<compiled>");
/* safe results first */
@@ -1106,26 +1234,69 @@ rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, V
}
{
const VALUE parser = rb_parser_new();
- const rb_iseq_t *outer_scope = rb_iseq_new(NULL, name, name, Qnil, 0, ISEQ_TYPE_TOP);
+ const rb_iseq_t *outer_scope = rb_iseq_new(Qnil, name, name, Qnil, 0, ISEQ_TYPE_TOP);
VALUE outer_scope_v = (VALUE)outer_scope;
rb_parser_set_context(parser, outer_scope, FALSE);
+ if (ruby_vm_keep_script_lines) rb_parser_set_script_lines(parser);
RB_GC_GUARD(outer_scope_v);
- ast = (*parse)(parser, file, src, ln);
+ vast = (*parse)(parser, file, src, ln);
}
- if (!ast->body.root) {
+ ast = rb_ruby_ast_data_get(vast);
+
+ if (!ast || !ast->body.root) {
rb_ast_dispose(ast);
rb_exc_raise(GET_EC()->errinfo);
}
else {
- iseq = rb_iseq_new_with_opt(&ast->body, name, file, realpath, ln,
- NULL, 0, ISEQ_TYPE_TOP, &option);
+ iseq = rb_iseq_new_with_opt(vast, name, file, realpath, ln,
+ NULL, 0, ISEQ_TYPE_TOP, &option,
+ Qnil);
rb_ast_dispose(ast);
}
return iseq;
}
+static rb_iseq_t *
+pm_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, VALUE opt)
+{
+ rb_iseq_t *iseq = NULL;
+ rb_compile_option_t option;
+ int ln;
+ VALUE name = rb_fstring_lit("<compiled>");
+
+ /* safe results first */
+ make_compile_option(&option, opt);
+ ln = NUM2INT(line);
+ StringValueCStr(file);
+
+ pm_parse_result_t result = { 0 };
+ pm_options_line_set(&result.options, NUM2INT(line));
+
+ VALUE error;
+ if (RB_TYPE_P(src, T_FILE)) {
+ VALUE filepath = rb_io_path(src);
+ error = pm_load_parse_file(&result, filepath);
+ RB_GC_GUARD(filepath);
+ }
+ else {
+ src = StringValue(src);
+ error = pm_parse_string(&result, src, file);
+ }
+
+ if (error == Qnil) {
+ iseq = pm_iseq_new_with_opt(&result.node, name, file, realpath, ln, NULL, 0, ISEQ_TYPE_TOP, &option);
+ pm_parse_result_free(&result);
+ }
+ else {
+ pm_parse_result_free(&result);
+ rb_exc_raise(error);
+ }
+
+ return iseq;
+}
+
VALUE
rb_iseq_path(const rb_iseq_t *iseq)
{
@@ -1234,18 +1405,30 @@ rb_iseq_remove_coverage_all(void)
static void
iseqw_mark(void *ptr)
{
- rb_gc_mark((VALUE)ptr);
+ rb_gc_mark_movable(*(VALUE *)ptr);
}
static size_t
iseqw_memsize(const void *ptr)
{
- return rb_iseq_memsize((const rb_iseq_t *)ptr);
+ return rb_iseq_memsize(*(const rb_iseq_t **)ptr);
+}
+
+static void
+iseqw_ref_update(void *ptr)
+{
+ VALUE *vptr = ptr;
+ *vptr = rb_gc_location(*vptr);
}
static const rb_data_type_t iseqw_data_type = {
"T_IMEMO/iseq",
- {iseqw_mark, NULL, iseqw_memsize,},
+ {
+ iseqw_mark,
+ RUBY_TYPED_DEFAULT_FREE,
+ iseqw_memsize,
+ iseqw_ref_update,
+ },
0, 0, RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED
};
@@ -1253,14 +1436,16 @@ static VALUE
iseqw_new(const rb_iseq_t *iseq)
{
if (iseq->wrapper) {
+ if (*(const rb_iseq_t **)rb_check_typeddata(iseq->wrapper, &iseqw_data_type) != iseq) {
+ rb_raise(rb_eTypeError, "wrong iseq wrapper: %" PRIsVALUE " for %p",
+ iseq->wrapper, (void *)iseq);
+ }
return iseq->wrapper;
}
else {
- union { const rb_iseq_t *in; void *out; } deconst;
- VALUE obj;
- deconst.in = iseq;
- obj = TypedData_Wrap_Struct(rb_cISeq, &iseqw_data_type, deconst.out);
- RB_OBJ_WRITTEN(obj, Qundef, iseq);
+ rb_iseq_t **ptr;
+ VALUE obj = TypedData_Make_Struct(rb_cISeq, rb_iseq_t *, &iseqw_data_type, ptr);
+ RB_OBJ_WRITE(obj, ptr, iseq);
/* cache a wrapper object */
RB_OBJ_WRITE((VALUE)iseq, &iseq->wrapper, obj);
@@ -1276,13 +1461,51 @@ rb_iseqw_new(const rb_iseq_t *iseq)
return iseqw_new(iseq);
}
+/**
+ * Accept the options given to InstructionSequence.compile and
+ * InstructionSequence.compile_prism and share the logic for creating the
+ * instruction sequence.
+ */
+static VALUE
+iseqw_s_compile_parser(int argc, VALUE *argv, VALUE self, bool prism)
+{
+ VALUE src, file = Qnil, path = Qnil, line = Qnil, opt = Qnil;
+ int i;
+
+ i = rb_scan_args(argc, argv, "1*:", &src, NULL, &opt);
+ if (i > 4+NIL_P(opt)) rb_error_arity(argc, 1, 5);
+ switch (i) {
+ case 5: opt = argv[--i];
+ case 4: line = argv[--i];
+ case 3: path = argv[--i];
+ case 2: file = argv[--i];
+ }
+
+ if (NIL_P(file)) file = rb_fstring_lit("<compiled>");
+ if (NIL_P(path)) path = file;
+ if (NIL_P(line)) line = INT2FIX(1);
+
+ Check_Type(path, T_STRING);
+ Check_Type(file, T_STRING);
+
+ rb_iseq_t *iseq;
+ if (prism) {
+ iseq = pm_iseq_compile_with_option(src, file, path, line, opt);
+ }
+ else {
+ iseq = rb_iseq_compile_with_option(src, file, path, line, opt);
+ }
+
+ return iseqw_new(iseq);
+}
+
/*
* call-seq:
* InstructionSequence.compile(source[, file[, path[, line[, options]]]]) -> iseq
* InstructionSequence.new(source[, file[, path[, line[, options]]]]) -> iseq
*
- * Takes +source+, a String of Ruby code and compiles it to an
- * InstructionSequence.
+ * Takes +source+, which can be a string of Ruby code, or an open +File+ object.
+ * that contains Ruby source code.
*
* Optionally takes +file+, +path+, and +line+ which describe the file path,
* real path and first line number of the ruby code in +source+ which are
@@ -1304,6 +1527,10 @@ rb_iseqw_new(const rb_iseq_t *iseq)
* RubyVM::InstructionSequence.compile(File.read(path), path, File.expand_path(path))
* #=> <RubyVM::InstructionSequence:<compiled>@test.rb:1>
*
+ * file = File.open("test.rb")
+ * RubyVM::InstructionSequence.compile(file)
+ * #=> <RubyVM::InstructionSequence:<compiled>@<compiled>:1>
+ *
* path = File.expand_path("test.rb")
* RubyVM::InstructionSequence.compile(File.read(path), path, path)
* #=> <RubyVM::InstructionSequence:<compiled>@/absolute/path/to/test.rb:1>
@@ -1312,26 +1539,49 @@ rb_iseqw_new(const rb_iseq_t *iseq)
static VALUE
iseqw_s_compile(int argc, VALUE *argv, VALUE self)
{
- VALUE src, file = Qnil, path = Qnil, line = INT2FIX(1), opt = Qnil;
- int i;
-
- i = rb_scan_args(argc, argv, "1*:", &src, NULL, &opt);
- if (i > 4+NIL_P(opt)) rb_error_arity(argc, 1, 5);
- switch (i) {
- case 5: opt = argv[--i];
- case 4: line = argv[--i];
- case 3: path = argv[--i];
- case 2: file = argv[--i];
- }
-
- if (NIL_P(file)) file = rb_fstring_lit("<compiled>");
- if (NIL_P(path)) path = file;
- if (NIL_P(line)) line = INT2FIX(1);
-
- Check_Type(path, T_STRING);
- Check_Type(file, T_STRING);
+ return iseqw_s_compile_parser(argc, argv, self, *rb_ruby_prism_ptr());
+}
- return iseqw_new(rb_iseq_compile_with_option(src, file, path, line, opt));
+/*
+ * call-seq:
+ * InstructionSequence.compile_prism(source[, file[, path[, line[, options]]]]) -> iseq
+ *
+ * Takes +source+, which can be a string of Ruby code, or an open +File+ object.
+ * that contains Ruby source code. It parses and compiles using prism.
+ *
+ * Optionally takes +file+, +path+, and +line+ which describe the file path,
+ * real path and first line number of the ruby code in +source+ which are
+ * metadata attached to the returned +iseq+.
+ *
+ * +file+ is used for `__FILE__` and exception backtrace. +path+ is used for
+ * +require_relative+ base. It is recommended these should be the same full
+ * path.
+ *
+ * +options+, which can be +true+, +false+ or a +Hash+, is used to
+ * modify the default behavior of the Ruby iseq compiler.
+ *
+ * For details regarding valid compile options see ::compile_option=.
+ *
+ * RubyVM::InstructionSequence.compile("a = 1 + 2")
+ * #=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
+ *
+ * path = "test.rb"
+ * RubyVM::InstructionSequence.compile(File.read(path), path, File.expand_path(path))
+ * #=> <RubyVM::InstructionSequence:<compiled>@test.rb:1>
+ *
+ * file = File.open("test.rb")
+ * RubyVM::InstructionSequence.compile(file)
+ * #=> <RubyVM::InstructionSequence:<compiled>@<compiled>:1>
+ *
+ * path = File.expand_path("test.rb")
+ * RubyVM::InstructionSequence.compile(File.read(path), path, path)
+ * #=> <RubyVM::InstructionSequence:<compiled>@/absolute/path/to/test.rb:1>
+ *
+ */
+static VALUE
+iseqw_s_compile_prism(int argc, VALUE *argv, VALUE self)
+{
+ return iseqw_s_compile_parser(argc, argv, self, true);
}
/*
@@ -1360,6 +1610,7 @@ iseqw_s_compile_file(int argc, VALUE *argv, VALUE self)
VALUE file, opt = Qnil;
VALUE parser, f, exc = Qnil, ret;
rb_ast_t *ast;
+ VALUE vast;
rb_compile_option_t option;
int i;
@@ -1378,7 +1629,8 @@ iseqw_s_compile_file(int argc, VALUE *argv, VALUE self)
parser = rb_parser_new();
rb_parser_set_context(parser, NULL, FALSE);
- ast = (rb_ast_t *)rb_parser_load_file(parser, file);
+ vast = rb_parser_load_file(parser, file);
+ ast = rb_ruby_ast_data_get(vast);
if (!ast->body.root) exc = GET_EC()->errinfo;
rb_io_close(f);
@@ -1389,10 +1641,11 @@ iseqw_s_compile_file(int argc, VALUE *argv, VALUE self)
make_compile_option(&option, opt);
- ret = iseqw_new(rb_iseq_new_with_opt(&ast->body, rb_fstring_lit("<main>"),
+ ret = iseqw_new(rb_iseq_new_with_opt(vast, rb_fstring_lit("<main>"),
file,
rb_realpath_internal(Qnil, file, 1),
- 1, NULL, 0, ISEQ_TYPE_TOP, &option));
+ 1, NULL, 0, ISEQ_TYPE_TOP, &option,
+ Qnil));
rb_ast_dispose(ast);
rb_vm_pop_frame(ec);
@@ -1402,6 +1655,69 @@ iseqw_s_compile_file(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
+ * InstructionSequence.compile_file_prism(file[, options]) -> iseq
+ *
+ * Takes +file+, a String with the location of a Ruby source file, reads,
+ * parses and compiles the file, and returns +iseq+, the compiled
+ * InstructionSequence with source location metadata set. It parses and
+ * compiles using prism.
+ *
+ * Optionally takes +options+, which can be +true+, +false+ or a +Hash+, to
+ * modify the default behavior of the Ruby iseq compiler.
+ *
+ * For details regarding valid compile options see ::compile_option=.
+ *
+ * # /tmp/hello.rb
+ * puts "Hello, world!"
+ *
+ * # elsewhere
+ * RubyVM::InstructionSequence.compile_file_prism("/tmp/hello.rb")
+ * #=> <RubyVM::InstructionSequence:<main>@/tmp/hello.rb>
+ */
+static VALUE
+iseqw_s_compile_file_prism(int argc, VALUE *argv, VALUE self)
+{
+ VALUE file, opt = Qnil, ret;
+ rb_compile_option_t option;
+ int i;
+
+ i = rb_scan_args(argc, argv, "1*:", &file, NULL, &opt);
+ if (i > 1+NIL_P(opt)) rb_error_arity(argc, 1, 2);
+ switch (i) {
+ case 2: opt = argv[--i];
+ }
+ FilePathValue(file);
+ file = rb_fstring(file); /* rb_io_t->pathv gets frozen anyways */
+
+ rb_execution_context_t *ec = GET_EC();
+ VALUE v = rb_vm_push_frame_fname(ec, file);
+
+ pm_parse_result_t result = { 0 };
+ result.options.line = 1;
+
+ VALUE error = pm_load_parse_file(&result, file);
+
+ if (error == Qnil) {
+ make_compile_option(&option, opt);
+
+ ret = iseqw_new(pm_iseq_new_with_opt(&result.node, rb_fstring_lit("<main>"),
+ file,
+ rb_realpath_internal(Qnil, file, 1),
+ 1, NULL, 0, ISEQ_TYPE_TOP, &option));
+ pm_parse_result_free(&result);
+ rb_vm_pop_frame(ec);
+ RB_GC_GUARD(v);
+ return ret;
+ } else {
+ pm_parse_result_free(&result);
+ rb_vm_pop_frame(ec);
+ RB_GC_GUARD(v);
+ rb_exc_raise(error);
+ }
+}
+
+/*
+ * call-seq:
* InstructionSequence.compile_option = options
*
* Sets the default values for various optimizations in the Ruby iseq
@@ -1422,7 +1738,6 @@ iseqw_s_compile_file(int argc, VALUE *argv, VALUE self)
* * +:operands_unification+
* * +:peephole_optimization+
* * +:specialized_instruction+
- * * +:stack_caching+
* * +:tailcall_optimization+
*
* Additionally, +:debug_level+ can be set to an integer.
@@ -1457,7 +1772,9 @@ iseqw_s_compile_option_get(VALUE self)
static const rb_iseq_t *
iseqw_check(VALUE iseqw)
{
- rb_iseq_t *iseq = DATA_PTR(iseqw);
+ rb_iseq_t **iseq_ptr;
+ TypedData_Get_Struct(iseqw, rb_iseq_t *, &iseqw_data_type, iseq_ptr);
+ rb_iseq_t *iseq = *iseq_ptr;
if (!ISEQ_BODY(iseq)) {
rb_ibf_load_iseq_complete(iseq);
@@ -1950,7 +2267,7 @@ local_var_name(const rb_iseq_t *diseq, VALUE level, VALUE op)
if (!name) {
name = rb_str_new_cstr("?");
}
- else if (!rb_str_symname_p(name)) {
+ else if (!rb_is_local_id(lid)) {
name = rb_str_inspect(name);
}
else {
@@ -2100,6 +2417,7 @@ rb_insn_operand_intern(const rb_iseq_t *iseq,
VALUE flags = rb_ary_new();
# define CALL_FLAG(n) if (vm_ci_flag(ci) & VM_CALL_##n) rb_ary_push(flags, rb_str_new2(#n))
CALL_FLAG(ARGS_SPLAT);
+ CALL_FLAG(ARGS_SPLAT_MUT);
CALL_FLAG(ARGS_BLOCKARG);
CALL_FLAG(FCALL);
CALL_FLAG(VCALL);
@@ -2207,7 +2525,7 @@ rb_iseq_disasm_insn(VALUE ret, const VALUE *code, size_t pos,
{
rb_event_flag_t events = rb_iseq_event_flags(iseq, pos);
if (events) {
- str = rb_str_catf(str, "[%s%s%s%s%s%s%s%s%s%s%s]",
+ str = rb_str_catf(str, "[%s%s%s%s%s%s%s%s%s%s%s%s]",
events & RUBY_EVENT_LINE ? "Li" : "",
events & RUBY_EVENT_CLASS ? "Cl" : "",
events & RUBY_EVENT_END ? "En" : "",
@@ -2217,6 +2535,7 @@ rb_iseq_disasm_insn(VALUE ret, const VALUE *code, size_t pos,
events & RUBY_EVENT_C_RETURN ? "Cr" : "",
events & RUBY_EVENT_B_CALL ? "Bc" : "",
events & RUBY_EVENT_B_RETURN ? "Br" : "",
+ events & RUBY_EVENT_RESCUE ? "Rs" : "",
events & RUBY_EVENT_COVERAGE_LINE ? "Cli" : "",
events & RUBY_EVENT_COVERAGE_BRANCH ? "Cbr" : "");
}
@@ -2310,6 +2629,15 @@ rb_iseq_disasm_recursive(const rb_iseq_t *iseq, VALUE indent)
rb_str_modify_expand(str, header_minlen - l);
memset(RSTRING_END(str), '=', header_minlen - l);
}
+ if (iseq->body->builtin_attrs) {
+#define disasm_builtin_attr(str, iseq, attr) \
+ if (iseq->body->builtin_attrs & BUILTIN_ATTR_ ## attr) { \
+ rb_str_cat2(str, " " #attr); \
+ }
+ disasm_builtin_attr(str, iseq, LEAF);
+ disasm_builtin_attr(str, iseq, SINGLE_NOARG_LEAF);
+ disasm_builtin_attr(str, iseq, INLINE_BLOCK);
+ }
rb_str_cat2(str, "\n");
/* show catch table information */
@@ -2561,6 +2889,7 @@ push_event_info(const rb_iseq_t *iseq, rb_event_flag_t events, int line, VALUE a
C(RUBY_EVENT_END, "end", INT2FIX(line));
C(RUBY_EVENT_RETURN, "return", INT2FIX(line));
C(RUBY_EVENT_B_RETURN, "b_return", INT2FIX(line));
+ C(RUBY_EVENT_RESCUE, "rescue", INT2FIX(line));
#undef C
}
@@ -2881,6 +3210,7 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
}
if (iseq_body->param.flags.has_kwrest) rb_hash_aset(params, ID2SYM(rb_intern("kwrest")), INT2FIX(keyword->rest_start));
if (iseq_body->param.flags.ambiguous_param0) rb_hash_aset(params, ID2SYM(rb_intern("ambiguous_param0")), Qtrue);
+ if (iseq_body->param.flags.use_block) rb_hash_aset(params, ID2SYM(rb_intern("use_block")), Qtrue);
}
/* body */
@@ -3078,6 +3408,7 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
CHECK_EVENT(RUBY_EVENT_RETURN);
CHECK_EVENT(RUBY_EVENT_B_CALL);
CHECK_EVENT(RUBY_EVENT_B_RETURN);
+ CHECK_EVENT(RUBY_EVENT_RESCUE);
#undef CHECK_EVENT
prev_insn_info = info;
}
@@ -3101,6 +3432,7 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
#ifdef USE_ISEQ_NODE_ID
rb_hash_aset(misc, ID2SYM(rb_intern("node_ids")), node_ids);
#endif
+ rb_hash_aset(misc, ID2SYM(rb_intern("parser")), iseq_body->prism ? ID2SYM(rb_intern("prism")) : ID2SYM(rb_intern("parse.y")));
/*
* [:magic, :major_version, :minor_version, :format_type, :misc,
@@ -3266,6 +3598,12 @@ typedef struct insn_data_struct {
static insn_data_t insn_data[VM_INSTRUCTION_SIZE/2];
void
+rb_free_encoded_insn_data(void)
+{
+ st_free_table(encoded_insn_data);
+}
+
+void
rb_vm_encoded_insn_data_table_init(void)
{
#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
@@ -3904,6 +4242,8 @@ Init_ISeq(void)
(void)iseq_s_load;
rb_define_singleton_method(rb_cISeq, "compile", iseqw_s_compile, -1);
+ rb_define_singleton_method(rb_cISeq, "compile_prism", iseqw_s_compile_prism, -1);
+ rb_define_singleton_method(rb_cISeq, "compile_file_prism", iseqw_s_compile_file_prism, -1);
rb_define_singleton_method(rb_cISeq, "new", iseqw_s_compile, -1);
rb_define_singleton_method(rb_cISeq, "compile_file", iseqw_s_compile_file, -1);
rb_define_singleton_method(rb_cISeq, "compile_option", iseqw_s_compile_option_get, 0);
diff --git a/iseq.h b/iseq.h
index b12228eed9..a0b59c441f 100644
--- a/iseq.h
+++ b/iseq.h
@@ -13,6 +13,7 @@
#include "internal/gc.h"
#include "shape.h"
#include "vm_core.h"
+#include "prism_compile.h"
RUBY_EXTERN const int ruby_api_version[];
#define ISEQ_MAJOR_VERSION ((unsigned int)ruby_api_version[0])
@@ -46,6 +47,10 @@ extern const ID rb_iseq_shared_exc_local_tbl[];
#define ISEQ_FLIP_CNT(iseq) ISEQ_BODY(iseq)->variable.flip_count
+#define ISEQ_FROZEN_STRING_LITERAL_ENABLED 1
+#define ISEQ_FROZEN_STRING_LITERAL_DISABLED 0
+#define ISEQ_FROZEN_STRING_LITERAL_UNSET -1
+
static inline rb_snum_t
ISEQ_FLIP_CNT_INCREMENT(const rb_iseq_t *iseq)
{
@@ -65,9 +70,7 @@ ISEQ_ORIGINAL_ISEQ_CLEAR(const rb_iseq_t *iseq)
{
void *ptr = ISEQ_BODY(iseq)->variable.original_iseq;
ISEQ_BODY(iseq)->variable.original_iseq = NULL;
- if (ptr) {
- ruby_xfree(ptr);
- }
+ ruby_xfree(ptr);
}
static inline VALUE *
@@ -83,9 +86,10 @@ ISEQ_ORIGINAL_ISEQ_ALLOC(const rb_iseq_t *iseq, long size)
RUBY_EVENT_CALL | \
RUBY_EVENT_RETURN| \
RUBY_EVENT_C_CALL| \
- RUBY_EVENT_C_RETURN| \
- RUBY_EVENT_B_CALL| \
- RUBY_EVENT_B_RETURN| \
+ RUBY_EVENT_C_RETURN | \
+ RUBY_EVENT_B_CALL | \
+ RUBY_EVENT_B_RETURN | \
+ RUBY_EVENT_RESCUE | \
RUBY_EVENT_COVERAGE_LINE| \
RUBY_EVENT_COVERAGE_BRANCH)
@@ -160,7 +164,7 @@ ISEQ_COMPILE_DATA_CLEAR(rb_iseq_t *iseq)
static inline rb_iseq_t *
iseq_imemo_alloc(void)
{
- return (rb_iseq_t *)rb_imemo_new(imemo_iseq, 0, 0, 0, 0);
+ return IMEMO_NEW(rb_iseq_t, imemo_iseq, 0);
}
VALUE rb_iseq_ibf_dump(const rb_iseq_t *iseq, VALUE opt);
@@ -172,6 +176,7 @@ void rb_iseq_init_trace(rb_iseq_t *iseq);
int rb_iseq_add_local_tracepoint_recursively(const rb_iseq_t *iseq, rb_event_flag_t turnon_events, VALUE tpval, unsigned int target_line, bool target_bmethod);
int rb_iseq_remove_local_tracepoint_recursively(const rb_iseq_t *iseq, VALUE tpval);
const rb_iseq_t *rb_iseq_load_iseq(VALUE fname);
+int rb_iseq_opt_frozen_string_literal(void);
#if VM_INSN_INFO_TABLE_IMPL == 2
unsigned int *rb_iseq_insns_info_decode_positions(const struct rb_iseq_constant_body *body);
@@ -188,7 +193,7 @@ VALUE *rb_iseq_original_iseq(const rb_iseq_t *iseq);
void rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc,
VALUE locals, VALUE args,
VALUE exception, VALUE body);
-void rb_iseq_mark_and_move_insn_storage(struct iseq_compile_data_storage *arena);
+void rb_iseq_mark_and_pin_insn_storage(struct iseq_compile_data_storage *arena);
VALUE rb_iseq_load(VALUE data, VALUE parent, VALUE opt);
VALUE rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc);
@@ -226,8 +231,7 @@ struct rb_compile_option_struct {
unsigned int specialized_instruction: 1;
unsigned int operands_unification: 1;
unsigned int instructions_unification: 1;
- unsigned int stack_caching: 1;
- unsigned int frozen_string_literal: 1;
+ signed int frozen_string_literal: 2; /* -1: not specified, 0: false, 1: true */
unsigned int debug_frozen_string_literal: 1;
unsigned int coverage_enabled: 1;
int debug_level;
@@ -329,6 +333,8 @@ VALUE rb_iseq_local_variables(const rb_iseq_t *iseq);
attr_index_t rb_estimate_iv_count(VALUE klass, const rb_iseq_t * initialize_iseq);
+void rb_free_encoded_insn_data(void);
+
RUBY_SYMBOL_EXPORT_END
#endif /* RUBY_ISEQ_H */
diff --git a/kernel.rb b/kernel.rb
index c8cbc99175..541d0cfd9d 100644
--- a/kernel.rb
+++ b/kernel.rb
@@ -58,10 +58,10 @@ module Kernel
# a.freeze #=> ["a", "b", "c"]
# a.frozen? #=> true
#--
- # Determines if the object is frozen. Equivalent to \c Object\#frozen? in Ruby.
- # \param[in] obj the object to be determines
- # \retval Qtrue if frozen
- # \retval Qfalse if not frozen
+ # Determines if the object is frozen. Equivalent to `Object#frozen?` in Ruby.
+ # @param[in] obj the object to be determines
+ # @retval Qtrue if frozen
+ # @retval Qfalse if not frozen
#++
#
def frozen?
@@ -87,6 +87,7 @@ module Kernel
#++
#
def tap
+ Primitive.attr! :inline_block
yield(self)
self
end
@@ -117,8 +118,18 @@ module Kernel
# # does not meet condition, drop value
# 2.then.detect(&:odd?) # => nil
#
+ # Good usage for +then+ is value piping in method chains:
+ #
+ # require 'open-uri'
+ # require 'json'
+ #
+ # construct_url(arguments).
+ # then {|url| URI(url).read }.
+ # then {|response| JSON.parse(response) }
+ #
def then
- unless block_given?
+ Primitive.attr! :inline_block
+ unless defined?(yield)
return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 0, 0, rb_obj_size)'
end
yield(self)
@@ -132,17 +143,9 @@ module Kernel
#
# "my string".yield_self {|s| s.upcase } #=> "MY STRING"
#
- # Good usage for +then+ is value piping in method chains:
- #
- # require 'open-uri'
- # require 'json'
- #
- # construct_url(arguments).
- # then {|url| URI(url).read }.
- # then {|response| JSON.parse(response) }
- #
def yield_self
- unless block_given?
+ Primitive.attr! :inline_block
+ unless defined?(yield)
return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 0, 0, rb_obj_size)'
end
yield(self)
@@ -178,8 +181,9 @@ module Kernel
# puts enum.next
# } #=> :ok
def loop
- unless block_given?
- return enum_for(:loop) { Float::INFINITY }
+ Primitive.attr! :inline_block
+ unless defined?(yield)
+ return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 0, 0, rb_f_loop_size)'
end
begin
@@ -216,4 +220,97 @@ module Kernel
Primitive.rb_f_float(arg, exception)
end
end
+
+ # call-seq:
+ # Integer(object, base = 0, exception: true) -> integer or nil
+ #
+ # Returns an integer converted from +object+.
+ #
+ # Tries to convert +object+ to an integer
+ # using +to_int+ first and +to_i+ second;
+ # see below for exceptions.
+ #
+ # With a non-zero +base+, +object+ must be a string or convertible
+ # to a string.
+ #
+ # ==== numeric objects
+ #
+ # With integer argument +object+ given, returns +object+:
+ #
+ # Integer(1) # => 1
+ # Integer(-1) # => -1
+ #
+ # With floating-point argument +object+ given,
+ # returns +object+ truncated to an integer:
+ #
+ # Integer(1.9) # => 1 # Rounds toward zero.
+ # Integer(-1.9) # => -1 # Rounds toward zero.
+ #
+ # ==== string objects
+ #
+ # With string argument +object+ and zero +base+ given,
+ # returns +object+ converted to an integer in base 10:
+ #
+ # Integer('100') # => 100
+ # Integer('-100') # => -100
+ #
+ # With +base+ zero, string +object+ may contain leading characters
+ # to specify the actual base (radix indicator):
+ #
+ # Integer('0100') # => 64 # Leading '0' specifies base 8.
+ # Integer('0b100') # => 4 # Leading '0b', specifies base 2.
+ # Integer('0x100') # => 256 # Leading '0x' specifies base 16.
+ #
+ # With a positive +base+ (in range 2..36) given, returns +object+
+ # converted to an integer in the given base:
+ #
+ # Integer('100', 2) # => 4
+ # Integer('100', 8) # => 64
+ # Integer('-100', 16) # => -256
+ #
+ # With a negative +base+ (in range -36..-2) given, returns +object+
+ # converted to an integer in the radix indicator if exists or
+ # +-base+:
+ #
+ # Integer('0x100', -2) # => 256
+ # Integer('100', -2) # => 4
+ # Integer('0b100', -8) # => 4
+ # Integer('100', -8) # => 64
+ # Integer('0o100', -10) # => 64
+ # Integer('100', -10) # => 100
+ #
+ # +base+ -1 is equal the -10 case.
+ #
+ # When converting strings, surrounding whitespace and embedded underscores
+ # are allowed and ignored:
+ #
+ # Integer(' 100 ') # => 100
+ # Integer('-1_0_0', 16) # => -256
+ #
+ # ==== other classes
+ #
+ # Examples with +object+ of various other classes:
+ #
+ # Integer(Rational(9, 10)) # => 0 # Rounds toward zero.
+ # Integer(Complex(2, 0)) # => 2 # Imaginary part must be zero.
+ # Integer(Time.now) # => 1650974042
+ #
+ # ==== keywords
+ #
+ # With optional keyword argument +exception+ given as +true+ (the default):
+ #
+ # - Raises TypeError if +object+ does not respond to +to_int+ or +to_i+.
+ # - Raises TypeError if +object+ is +nil+.
+ # - Raise ArgumentError if +object+ is an invalid string.
+ #
+ # With +exception+ given as +false+, an exception of any kind is suppressed
+ # and +nil+ is returned.
+
+ def Integer(arg, base = 0, exception: true)
+ if Primitive.mandatory_only?
+ Primitive.rb_f_integer1(arg)
+ else
+ Primitive.rb_f_integer(arg, base, exception);
+ end
+ end
end
diff --git a/lib/English.gemspec b/lib/English.gemspec
index 5d0a396575..5f4eb420c2 100644
--- a/lib/English.gemspec
+++ b/lib/English.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "english"
- spec.version = "0.7.2"
+ spec.version = "0.8.0"
spec.authors = ["Yukihiro Matsumoto"]
spec.email = ["matz@ruby-lang.org"]
diff --git a/lib/English.rb b/lib/English.rb
index da50dfdc29..03fe721991 100644
--- a/lib/English.rb
+++ b/lib/English.rb
@@ -39,7 +39,6 @@
# $PROCESS_ID:: $$
# $CHILD_STATUS:: $?
# $LAST_MATCH_INFO:: $~
-# $IGNORECASE:: $=
# $ARGV:: $*
# $MATCH:: $&
# $PREMATCH:: $`
@@ -58,44 +57,32 @@ alias $ERROR_POSITION $@
# The default separator pattern used by String#split. May be set from
# the command line using the <code>-F</code> flag.
alias $FS $;
-
-# The default separator pattern used by String#split. May be set from
-# the command line using the <code>-F</code> flag.
alias $FIELD_SEPARATOR $;
# The separator string output between the parameters to methods such
# as Kernel#print and Array#join. Defaults to +nil+, which adds no
# text.
-alias $OFS $,
# The separator string output between the parameters to methods such
# as Kernel#print and Array#join. Defaults to +nil+, which adds no
# text.
+alias $OFS $,
alias $OUTPUT_FIELD_SEPARATOR $,
# The input record separator (newline by default). This is the value
# that routines such as Kernel#gets use to determine record
# boundaries. If set to +nil+, +gets+ will read the entire file.
alias $RS $/
-
-# The input record separator (newline by default). This is the value
-# that routines such as Kernel#gets use to determine record
-# boundaries. If set to +nil+, +gets+ will read the entire file.
alias $INPUT_RECORD_SEPARATOR $/
# The string appended to the output of every call to methods such as
# Kernel#print and IO#write. The default value is +nil+.
alias $ORS $\
-
-# The string appended to the output of every call to methods such as
-# Kernel#print and IO#write. The default value is +nil+.
alias $OUTPUT_RECORD_SEPARATOR $\
# The number of the last line read from the current input file.
-alias $INPUT_LINE_NUMBER $.
-
-# The number of the last line read from the current input file.
alias $NR $.
+alias $INPUT_LINE_NUMBER $.
# The last line read by Kernel#gets or
# Kernel#readline. Many string-related functions in the
@@ -135,8 +122,6 @@ alias $DEFAULT_INPUT $<
# The process number of the program being executed. Read only.
alias $PID $$
-
-# The process number of the program being executed. Read only.
alias $PROCESS_ID $$
# The exit status of the last child process to terminate. Read
@@ -151,9 +136,6 @@ alias $CHILD_STATUS $?
# scope.
alias $LAST_MATCH_INFO $~
-# This variable is no longer effective. Deprecated.
-alias $IGNORECASE $=
-
# An array of strings containing the command-line
# options from the invocation of the program. Options
# used by the Ruby interpreter will have been
diff --git a/lib/abbrev.gemspec b/lib/abbrev.gemspec
deleted file mode 100644
index 50c500bbc7..0000000000
--- a/lib/abbrev.gemspec
+++ /dev/null
@@ -1,29 +0,0 @@
-name = File.basename(__FILE__, ".gemspec")
-version = ["lib", Array.new(name.count("-")+1, ".").join("/")].find do |dir|
- break File.foreach(File.join(__dir__, dir, "#{name.tr('-', '/')}.rb")) do |line|
- /^\s*VERSION\s*=\s*"(.*)"/ =~ line and break $1
- end rescue nil
-end
-
-Gem::Specification.new do |spec|
- spec.name = name
- spec.version = version
- spec.authors = ["Akinori MUSHA"]
- spec.email = ["knu@idaemons.org"]
-
- spec.summary = %q{Calculates a set of unique abbreviations for a given set of strings}
- spec.description = %q{Calculates a set of unique abbreviations for a given set of strings}
- spec.homepage = "https://github.com/ruby/abbrev"
- spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
- spec.licenses = ["Ruby", "BSD-2-Clause"]
-
- spec.metadata["homepage_uri"] = spec.homepage
- spec.metadata["source_code_uri"] = spec.homepage
-
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
- end
- spec.bindir = "exe"
- spec.executables = []
- spec.require_paths = ["lib"]
-end
diff --git a/lib/abbrev.rb b/lib/abbrev.rb
deleted file mode 100644
index ee86f06874..0000000000
--- a/lib/abbrev.rb
+++ /dev/null
@@ -1,133 +0,0 @@
-# frozen_string_literal: true
-#--
-# Copyright (c) 2001,2003 Akinori MUSHA <knu@iDaemons.org>
-#
-# All rights reserved. You can redistribute and/or modify it under
-# the same terms as Ruby.
-#
-# $Idaemons: /home/cvs/rb/abbrev.rb,v 1.2 2001/05/30 09:37:45 knu Exp $
-# $RoughId: abbrev.rb,v 1.4 2003/10/14 19:45:42 knu Exp $
-# $Id$
-#++
-
-##
-# Calculates the set of unambiguous abbreviations for a given set of strings.
-#
-# require 'abbrev'
-# require 'pp'
-#
-# pp Abbrev.abbrev(['ruby'])
-# #=> {"ruby"=>"ruby", "rub"=>"ruby", "ru"=>"ruby", "r"=>"ruby"}
-#
-# pp Abbrev.abbrev(%w{ ruby rules })
-#
-# _Generates:_
-# { "ruby" => "ruby",
-# "rub" => "ruby",
-# "rules" => "rules",
-# "rule" => "rules",
-# "rul" => "rules" }
-#
-# It also provides an array core extension, Array#abbrev.
-#
-# pp %w{ summer winter }.abbrev
-#
-# _Generates:_
-# { "summer" => "summer",
-# "summe" => "summer",
-# "summ" => "summer",
-# "sum" => "summer",
-# "su" => "summer",
-# "s" => "summer",
-# "winter" => "winter",
-# "winte" => "winter",
-# "wint" => "winter",
-# "win" => "winter",
-# "wi" => "winter",
-# "w" => "winter" }
-
-module Abbrev
- VERSION = "0.1.1"
-
- # Given a set of strings, calculate the set of unambiguous abbreviations for
- # those strings, and return a hash where the keys are all the possible
- # abbreviations and the values are the full strings.
- #
- # Thus, given +words+ is "car" and "cone", the keys pointing to "car" would
- # be "ca" and "car", while those pointing to "cone" would be "co", "con", and
- # "cone".
- #
- # require 'abbrev'
- #
- # Abbrev.abbrev(%w{ car cone })
- # #=> {"ca"=>"car", "con"=>"cone", "co"=>"cone", "car"=>"car", "cone"=>"cone"}
- #
- # The optional +pattern+ parameter is a pattern or a string. Only input
- # strings that match the pattern or start with the string are included in the
- # output hash.
- #
- # Abbrev.abbrev(%w{car box cone crab}, /b/)
- # #=> {"box"=>"box", "bo"=>"box", "b"=>"box", "crab" => "crab"}
- #
- # Abbrev.abbrev(%w{car box cone}, 'ca')
- # #=> {"car"=>"car", "ca"=>"car"}
- def abbrev(words, pattern = nil)
- table = {}
- seen = Hash.new(0)
-
- if pattern.is_a?(String)
- pattern = /\A#{Regexp.quote(pattern)}/ # regard as a prefix
- end
-
- words.each do |word|
- next if word.empty?
- word.size.downto(1) { |len|
- abbrev = word[0...len]
-
- next if pattern && pattern !~ abbrev
-
- case seen[abbrev] += 1
- when 1
- table[abbrev] = word
- when 2
- table.delete(abbrev)
- else
- break
- end
- }
- end
-
- words.each do |word|
- next if pattern && pattern !~ word
-
- table[word] = word
- end
-
- table
- end
-
- module_function :abbrev
-end
-
-class Array
- # Calculates the set of unambiguous abbreviations for the strings in +self+.
- #
- # require 'abbrev'
- # %w{ car cone }.abbrev
- # #=> {"car"=>"car", "ca"=>"car", "cone"=>"cone", "con"=>"cone", "co"=>"cone"}
- #
- # The optional +pattern+ parameter is a pattern or a string. Only input
- # strings that match the pattern or start with the string are included in the
- # output hash.
- #
- # %w{ fast boat day }.abbrev(/^.a/)
- # #=> {"fast"=>"fast", "fas"=>"fast", "fa"=>"fast", "day"=>"day", "da"=>"day"}
- #
- # Abbrev.abbrev(%w{car box cone}, "ca")
- # #=> {"car"=>"car", "ca"=>"car"}
- #
- # See also Abbrev.abbrev
- def abbrev(pattern = nil)
- Abbrev::abbrev(self, pattern)
- end
-end
diff --git a/lib/base64.gemspec b/lib/base64.gemspec
deleted file mode 100644
index 44a2ce1fd1..0000000000
--- a/lib/base64.gemspec
+++ /dev/null
@@ -1,27 +0,0 @@
-name = File.basename(__FILE__, ".gemspec")
-version = ["lib", Array.new(name.count("-")+1).join("/")].find do |dir|
- break File.foreach(File.join(__dir__, dir, "#{name.tr('-', '/')}.rb")) do |line|
- /^\s*VERSION\s*=\s*"(.*)"/ =~ line and break $1
- end rescue nil
-end
-
-Gem::Specification.new do |spec|
- spec.name = name
- spec.version = version
- spec.authors = ["Yusuke Endoh"]
- spec.email = ["mame@ruby-lang.org"]
-
- spec.summary = %q{Support for encoding and decoding binary data using a Base64 representation.}
- spec.description = %q{Support for encoding and decoding binary data using a Base64 representation.}
- spec.homepage = "https://github.com/ruby/base64"
- spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
- spec.licenses = ["Ruby", "BSD-2-Clause"]
-
- spec.metadata["homepage_uri"] = spec.homepage
- spec.metadata["source_code_uri"] = spec.homepage
-
- spec.files = ["README.md", "LICENSE.txt", "lib/base64.rb"]
- spec.bindir = "exe"
- spec.executables = []
- spec.require_paths = ["lib"]
-end
diff --git a/lib/base64.rb b/lib/base64.rb
deleted file mode 100644
index 15c0b297ce..0000000000
--- a/lib/base64.rb
+++ /dev/null
@@ -1,113 +0,0 @@
-# frozen_string_literal: true
-#
-# = base64.rb: methods for base64-encoding and -decoding strings
-#
-
-# The Base64 module provides for the encoding (#encode64, #strict_encode64,
-# #urlsafe_encode64) and decoding (#decode64, #strict_decode64,
-# #urlsafe_decode64) of binary data using a Base64 representation.
-#
-# == Example
-#
-# A simple encoding and decoding.
-#
-# require "base64"
-#
-# enc = Base64.encode64('Send reinforcements')
-# # -> "U2VuZCByZWluZm9yY2VtZW50cw==\n"
-# plain = Base64.decode64(enc)
-# # -> "Send reinforcements"
-#
-# The purpose of using base64 to encode data is that it translates any
-# binary data into purely printable characters.
-
-module Base64
-
- VERSION = "0.1.1"
-
- module_function
-
- # Returns the Base64-encoded version of +bin+.
- # This method complies with RFC 2045.
- # Line feeds are added to every 60 encoded characters.
- #
- # require 'base64'
- # Base64.encode64("Now is the time for all good coders\nto learn Ruby")
- #
- # <i>Generates:</i>
- #
- # Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g
- # UnVieQ==
- def encode64(bin)
- [bin].pack("m")
- end
-
- # Returns the Base64-decoded version of +str+.
- # This method complies with RFC 2045.
- # Characters outside the base alphabet are ignored.
- #
- # require 'base64'
- # str = 'VGhpcyBpcyBsaW5lIG9uZQpUaGlzIG' +
- # 'lzIGxpbmUgdHdvClRoaXMgaXMgbGlu' +
- # 'ZSB0aHJlZQpBbmQgc28gb24uLi4K'
- # puts Base64.decode64(str)
- #
- # <i>Generates:</i>
- #
- # This is line one
- # This is line two
- # This is line three
- # And so on...
- def decode64(str)
- str.unpack1("m")
- end
-
- # Returns the Base64-encoded version of +bin+.
- # This method complies with RFC 4648.
- # No line feeds are added.
- def strict_encode64(bin)
- [bin].pack("m0")
- end
-
- # Returns the Base64-decoded version of +str+.
- # This method complies with RFC 4648.
- # ArgumentError is raised if +str+ is incorrectly padded or contains
- # non-alphabet characters. Note that CR or LF are also rejected.
- def strict_decode64(str)
- str.unpack1("m0")
- end
-
- # Returns the Base64-encoded version of +bin+.
- # This method complies with ``Base 64 Encoding with URL and Filename Safe
- # Alphabet'' in RFC 4648.
- # The alphabet uses '-' instead of '+' and '_' instead of '/'.
- # Note that the result can still contain '='.
- # You can remove the padding by setting +padding+ as false.
- def urlsafe_encode64(bin, padding: true)
- str = strict_encode64(bin)
- str.chomp!("==") or str.chomp!("=") unless padding
- str.tr!("+/", "-_")
- str
- end
-
- # Returns the Base64-decoded version of +str+.
- # This method complies with ``Base 64 Encoding with URL and Filename Safe
- # Alphabet'' in RFC 4648.
- # The alphabet uses '-' instead of '+' and '_' instead of '/'.
- #
- # The padding character is optional.
- # This method accepts both correctly-padded and unpadded input.
- # Note that it still rejects incorrectly-padded input.
- def urlsafe_decode64(str)
- # NOTE: RFC 4648 does say nothing about unpadded input, but says that
- # "the excess pad characters MAY also be ignored", so it is inferred that
- # unpadded input is also acceptable.
- if !str.end_with?("=") && str.length % 4 != 0
- str = str.ljust((str.length + 3) & ~3, "=")
- str.tr!("-_", "+/")
- else
- str = str.tr("-_", "+/")
- end
- strict_decode64(str)
- end
-end
diff --git a/lib/benchmark.rb b/lib/benchmark.rb
index d9f89239fc..9f43255e42 100644
--- a/lib/benchmark.rb
+++ b/lib/benchmark.rb
@@ -121,7 +121,7 @@
module Benchmark
- VERSION = "0.2.1"
+ VERSION = "0.3.0"
BENCHMARK_VERSION = "2002-04-25" # :nodoc:
diff --git a/lib/bundled_gems.rb b/lib/bundled_gems.rb
new file mode 100644
index 0000000000..ed5f940b57
--- /dev/null
+++ b/lib/bundled_gems.rb
@@ -0,0 +1,189 @@
+# -*- frozen-string-literal: true -*-
+
+# :stopdoc:
+
+module Gem::BUNDLED_GEMS
+ SINCE = {
+ "rexml" => "3.0.0",
+ "rss" => "3.0.0",
+ "webrick" => "3.0.0",
+ "matrix" => "3.1.0",
+ "net-ftp" => "3.1.0",
+ "net-imap" => "3.1.0",
+ "net-pop" => "3.1.0",
+ "net-smtp" => "3.1.0",
+ "prime" => "3.1.0",
+ "racc" => "3.3.0",
+ "abbrev" => "3.4.0",
+ "base64" => "3.4.0",
+ "bigdecimal" => "3.4.0",
+ "csv" => "3.4.0",
+ "drb" => "3.4.0",
+ "getoptlong" => "3.4.0",
+ "mutex_m" => "3.4.0",
+ "nkf" => "3.4.0",
+ "observer" => "3.4.0",
+ "resolv-replace" => "3.4.0",
+ "rinda" => "3.4.0",
+ "syslog" => "3.4.0",
+ "ostruct" => "3.5.0",
+ "pstore" => "3.5.0",
+ }.freeze
+
+ EXACT = {
+ "abbrev" => true,
+ "base64" => true,
+ "bigdecimal" => true,
+ "csv" => true,
+ "drb" => true,
+ "getoptlong" => true,
+ "mutex_m" => true,
+ "nkf" => true, "kconv" => "nkf",
+ "observer" => true,
+ "resolv-replace" => true,
+ "rinda" => true,
+ "syslog" => true,
+ "ostruct" => true,
+ "pstore" => true,
+ }.freeze
+
+ PREFIXED = {
+ "bigdecimal" => true,
+ "csv" => true,
+ "drb" => true,
+ "rinda" => true,
+ "syslog" => true,
+ }.freeze
+
+ WARNED = {} # unfrozen
+
+ conf = ::RbConfig::CONFIG
+ LIBDIR = (conf["rubylibdir"] + "/").freeze
+ ARCHDIR = (conf["rubyarchdir"] + "/").freeze
+ dlext = [conf["DLEXT"], "so"].uniq
+ DLEXT = /\.#{Regexp.union(dlext)}\z/
+ LIBEXT = /\.#{Regexp.union("rb", *dlext)}\z/
+
+ def self.replace_require(specs)
+ return if [::Kernel.singleton_class, ::Kernel].any? {|klass| klass.respond_to?(:no_warning_require) }
+
+ spec_names = specs.to_a.each_with_object({}) {|spec, h| h[spec.name] = true }
+
+ [::Kernel.singleton_class, ::Kernel].each do |kernel_class|
+ kernel_class.send(:alias_method, :no_warning_require, :require)
+ kernel_class.send(:define_method, :require) do |name|
+ if message = ::Gem::BUNDLED_GEMS.warning?(name, specs: spec_names) # rubocop:disable Style/HashSyntax
+ warn message, :uplevel => 1
+ end
+ kernel_class.send(:no_warning_require, name)
+ end
+ if kernel_class == ::Kernel
+ kernel_class.send(:private, :require)
+ else
+ kernel_class.send(:public, :require)
+ end
+ end
+ end
+
+ def self.find_gem(path)
+ if !path
+ return
+ elsif path.start_with?(ARCHDIR)
+ n = path.delete_prefix(ARCHDIR).sub(DLEXT, "")
+ elsif path.start_with?(LIBDIR)
+ n = path.delete_prefix(LIBDIR).chomp(".rb")
+ else
+ return
+ end
+ EXACT[n] or PREFIXED[n = n[%r[\A[^/]+(?=/)]]] && n
+ end
+
+ def self.warning?(name, specs: nil)
+ # name can be a feature name or a file path with String or Pathname
+ feature = File.path(name)
+ # bootsnap expands `require "csv"` to `require "#{LIBDIR}/csv.rb"`,
+ # and `require "syslog"` to `require "#{ARCHDIR}/syslog.so"`.
+ name = feature.delete_prefix(ARCHDIR)
+ name.delete_prefix!(LIBDIR)
+ name.tr!("/", "-")
+ name.sub!(LIBEXT, "")
+ return if specs.include?(name)
+ _t, path = $:.resolve_feature_path(feature)
+ if gem = find_gem(path)
+ return if specs.include?(gem)
+ caller = caller_locations(3, 3)&.find {|c| c&.absolute_path}
+ return if find_gem(caller&.absolute_path)
+ elsif SINCE[name] && !path
+ gem = true
+ else
+ return
+ end
+
+ return if WARNED[name]
+ WARNED[name] = true
+ if gem == true
+ gem = name
+ "#{feature} was loaded from the standard library, but"
+ elsif gem
+ return if WARNED[gem]
+ WARNED[gem] = true
+ "#{feature} is found in #{gem}, which"
+ else
+ return
+ end + build_message(gem)
+ end
+
+ def self.build_message(gem)
+ msg = " #{RUBY_VERSION < SINCE[gem] ? "will no longer be" : "is not"} part of the default gems since Ruby #{SINCE[gem]}."
+
+ if defined?(Bundler)
+ msg += " Add #{gem} to your Gemfile or gemspec."
+
+ # We detect the gem name from caller_locations. We need to skip 2 frames like:
+ # lib/ruby/3.3.0+0/bundled_gems.rb:90:in `warning?'",
+ # lib/ruby/3.3.0+0/bundler/rubygems_integration.rb:247:in `block (2 levels) in replace_require'",
+ #
+ # Additionally, we need to skip Bootsnap and Zeitwerk if present, these
+ # gems decorate Kernel#require, so they are not really the ones issuing
+ # the require call users should be warned about. Those are upwards.
+ location = Thread.each_caller_location(2) do |cl|
+ break cl.path unless cl.base_label == "require"
+ end
+
+ if location && File.file?(location) && !location.start_with?(Gem::BUNDLED_GEMS::LIBDIR)
+ caller_gem = nil
+ Gem.path.each do |path|
+ if location =~ %r{#{path}/gems/([\w\-\.]+)}
+ caller_gem = $1
+ break
+ end
+ end
+ if caller_gem
+ msg += " Also contact author of #{caller_gem} to add #{gem} into its gemspec."
+ end
+ end
+ else
+ msg += " Install #{gem} from RubyGems."
+ end
+
+ msg
+ end
+
+ freeze
+end
+
+# for RubyGems without Bundler environment.
+# If loading library is not part of the default gems and the bundled gems, warn it.
+class LoadError
+ def message
+ return super unless path
+
+ name = path.tr("/", "-")
+ if !defined?(Bundler) && Gem::BUNDLED_GEMS::SINCE[name] && !Gem::BUNDLED_GEMS::WARNED[name]
+ warn name + Gem::BUNDLED_GEMS.build_message(name)
+ end
+ super
+ end
+end
+
+# :startdoc:
diff --git a/lib/bundler.rb b/lib/bundler.rb
index f83268e9cd..5033109db6 100644
--- a/lib/bundler.rb
+++ b/lib/bundler.rb
@@ -17,7 +17,7 @@ require_relative "bundler/build_metadata"
# Bundler provides a consistent environment for Ruby projects by
# tracking and installing the exact gems and versions that are needed.
#
-# Since Ruby 2.6, Bundler is a part of Ruby's standard library.
+# Bundler is a part of Ruby's standard library.
#
# Bundler is used by creating _gemfiles_ listing all the project dependencies
# and (optionally) their versions and then using
@@ -39,6 +39,9 @@ module Bundler
environment_preserver.replace_with_backup
SUDO_MUTEX = Thread::Mutex.new
+ autoload :Checksum, File.expand_path("bundler/checksum", __dir__)
+ autoload :CLI, File.expand_path("bundler/cli", __dir__)
+ autoload :CIDetector, File.expand_path("bundler/ci_detector", __dir__)
autoload :Definition, File.expand_path("bundler/definition", __dir__)
autoload :Dependency, File.expand_path("bundler/dependency", __dir__)
autoload :Deprecate, File.expand_path("bundler/deprecate", __dir__)
@@ -98,9 +101,7 @@ module Bundler
end
def create_bundle_path
- SharedHelpers.filesystem_access(bundle_path.to_s) do |p|
- mkdir_p(p)
- end unless bundle_path.exist?
+ mkdir_p(bundle_path) unless bundle_path.exist?
@bundle_path = bundle_path.realpath
rescue Errno::EEXIST
@@ -117,7 +118,7 @@ module Bundler
@bin_path ||= begin
path = settings[:bin] || "bin"
path = Pathname.new(path).expand_path(root).expand_path
- SharedHelpers.filesystem_access(path) {|p| FileUtils.mkdir_p(p) }
+ mkdir_p(path)
path
end
end
@@ -165,6 +166,25 @@ module Bundler
end
end
+ # Automatically install dependencies if Bundler.settings[:auto_install] exists.
+ # This is set through config cmd `bundle config set --global auto_install 1`.
+ #
+ # Note that this method `nil`s out the global Definition object, so it
+ # should be called first, before you instantiate anything like an
+ # `Installer` that'll keep a reference to the old one instead.
+ def auto_install
+ return unless settings[:auto_install]
+
+ begin
+ definition.specs
+ rescue GemNotFound, GitError
+ ui.info "Automatically installing missing gems."
+ reset!
+ CLI::Install.new({}).run
+ reset!
+ end
+ end
+
# Setups Bundler environment (see Bundler.setup) if it is not already set,
# and loads all gems from groups specified. Unlike ::setup, can be called
# multiple times with different groups (if they were allowed by setup).
@@ -184,6 +204,7 @@ module Bundler
# Bundler.require(:test) # requires second_gem
#
def require(*groups)
+ load_plugins
setup(*groups).require(*groups)
end
@@ -192,7 +213,7 @@ module Bundler
end
def environment
- SharedHelpers.major_deprecation 2, "Bundler.environment has been removed in favor of Bundler.load", :print_caller_location => true
+ SharedHelpers.major_deprecation 2, "Bundler.environment has been removed in favor of Bundler.load", print_caller_location: true
load
end
@@ -200,12 +221,13 @@ module Bundler
#
# @param unlock [Hash, Boolean, nil] Gems that have been requested
# to be updated or true if all gems should be updated
+ # @param lockfile [Pathname] Path to Gemfile.lock
# @return [Bundler::Definition]
- def definition(unlock = nil)
+ def definition(unlock = nil, lockfile = default_lockfile)
@definition = nil if unlock
@definition ||= begin
configure
- Definition.build(default_gemfile, default_lockfile, unlock)
+ Definition.build(default_gemfile, lockfile, unlock)
end
end
@@ -330,14 +352,6 @@ module Bundler
def rm_rf(path)
FileUtils.remove_entry_secure(path) if path && File.exist?(path)
- rescue ArgumentError
- message = <<EOF
-It is a security vulnerability to allow your home directory to be world-writable, and bundler cannot continue.
-You should probably consider fixing this issue by running `chmod o-w ~` on *nix.
-Please refer to https://ruby-doc.org/stdlib-3.1.2/libdoc/fileutils/rdoc/FileUtils.html#method-c-remove_entry_secure for details.
-EOF
- File.world_writable?(path) ? Bundler.ui.warn(message) : raise
- raise PathError, "Please fix the world-writable issue with your #{path} directory"
end
def settings
@@ -353,13 +367,13 @@ EOF
# @deprecated Use `unbundled_env` instead
def clean_env
- Bundler::SharedHelpers.major_deprecation(
- 2,
+ message =
"`Bundler.clean_env` has been deprecated in favor of `Bundler.unbundled_env`. " \
- "If you instead want the environment before bundler was originally loaded, use `Bundler.original_env`",
- :print_caller_location => true
- )
-
+ "If you instead want the environment before bundler was originally loaded, use `Bundler.original_env`"
+ removed_message =
+ "`Bundler.clean_env` has been removed in favor of `Bundler.unbundled_env`. " \
+ "If you instead want the environment before bundler was originally loaded, use `Bundler.original_env`"
+ Bundler::SharedHelpers.major_deprecation(2, message, removed_message: removed_message, print_caller_location: true)
unbundled_env
end
@@ -396,13 +410,13 @@ EOF
# @deprecated Use `with_unbundled_env` instead
def with_clean_env
- Bundler::SharedHelpers.major_deprecation(
- 2,
+ message =
"`Bundler.with_clean_env` has been deprecated in favor of `Bundler.with_unbundled_env`. " \
- "If you instead want the environment before bundler was originally loaded, use `Bundler.with_original_env`",
- :print_caller_location => true
- )
-
+ "If you instead want the environment before bundler was originally loaded, use `Bundler.with_original_env`"
+ removed_message =
+ "`Bundler.with_clean_env` has been removed in favor of `Bundler.with_unbundled_env`. " \
+ "If you instead want the environment before bundler was originally loaded, use `Bundler.with_original_env`"
+ Bundler::SharedHelpers.major_deprecation(2, message, removed_message: removed_message, print_caller_location: true)
with_env(unbundled_env) { yield }
end
@@ -418,13 +432,13 @@ EOF
# @deprecated Use `unbundled_system` instead
def clean_system(*args)
- Bundler::SharedHelpers.major_deprecation(
- 2,
+ message =
"`Bundler.clean_system` has been deprecated in favor of `Bundler.unbundled_system`. " \
- "If you instead want to run the command in the environment before bundler was originally loaded, use `Bundler.original_system`",
- :print_caller_location => true
- )
-
+ "If you instead want to run the command in the environment before bundler was originally loaded, use `Bundler.original_system`"
+ removed_message =
+ "`Bundler.clean_system` has been removed in favor of `Bundler.unbundled_system`. " \
+ "If you instead want to run the command in the environment before bundler was originally loaded, use `Bundler.original_system`"
+ Bundler::SharedHelpers.major_deprecation(2, message, removed_message: removed_message, print_caller_location: true)
with_env(unbundled_env) { Kernel.system(*args) }
end
@@ -440,13 +454,13 @@ EOF
# @deprecated Use `unbundled_exec` instead
def clean_exec(*args)
- Bundler::SharedHelpers.major_deprecation(
- 2,
+ message =
"`Bundler.clean_exec` has been deprecated in favor of `Bundler.unbundled_exec`. " \
- "If you instead want to exec to a command in the environment before bundler was originally loaded, use `Bundler.original_exec`",
- :print_caller_location => true
- )
-
+ "If you instead want to exec to a command in the environment before bundler was originally loaded, use `Bundler.original_exec`"
+ removed_message =
+ "`Bundler.clean_exec` has been removed in favor of `Bundler.unbundled_exec`. " \
+ "If you instead want to exec to a command in the environment before bundler was originally loaded, use `Bundler.original_exec`"
+ Bundler::SharedHelpers.major_deprecation(2, message, removed_message: removed_message, print_caller_location: true)
with_env(unbundled_env) { Kernel.exec(*args) }
end
@@ -489,7 +503,7 @@ EOF
configured_bundle_path.use_system_gems?
end
- def mkdir_p(path, options = {})
+ def mkdir_p(path)
SharedHelpers.filesystem_access(path, :write) do |p|
FileUtils.mkdir_p(p)
end
@@ -515,7 +529,16 @@ EOF
end
def safe_load_marshal(data)
- load_marshal(data, :marshal_proc => SafeMarshal.proc)
+ if Gem.respond_to?(:load_safe_marshal)
+ Gem.load_safe_marshal
+ begin
+ Gem::SafeMarshal.safe_load(data)
+ rescue Gem::SafeMarshal::Reader::Error, Gem::SafeMarshal::Visitors::ToRuby::Error => e
+ raise MarshalError, "#{e.class}: #{e.message}"
+ end
+ else
+ load_marshal(data, marshal_proc: SafeMarshal.proc)
+ end
end
def load_gemspec(file, validate = false)
@@ -558,6 +581,23 @@ EOF
@feature_flag ||= FeatureFlag.new(VERSION)
end
+ def load_plugins(definition = Bundler.definition)
+ return if defined?(@load_plugins_ran)
+
+ Bundler.rubygems.load_plugins
+
+ requested_path_gems = definition.requested_specs.select {|s| s.source.is_a?(Source::Path) }
+ path_plugin_files = requested_path_gems.map do |spec|
+ Bundler.rubygems.spec_matches_for_glob(spec, "rubygems_plugin#{Bundler.rubygems.suffix_pattern}")
+ rescue TypeError
+ error_message = "#{spec.name} #{spec.version} has an invalid gemspec"
+ raise Gem::InvalidSpecificationException, error_message
+ end.flatten
+ Bundler.rubygems.load_plugin_files(path_plugin_files)
+ Bundler.rubygems.load_env_plugins
+ @load_plugins_ran = true
+ end
+
def reset!
reset_paths!
Plugin.reset!
diff --git a/lib/bundler/build_metadata.rb b/lib/bundler/build_metadata.rb
index 8bffb2fae7..5d2a8b53bb 100644
--- a/lib/bundler/build_metadata.rb
+++ b/lib/bundler/build_metadata.rb
@@ -29,7 +29,7 @@ module Bundler
# commit instance variable then we can't determine its commits SHA.
git_dir = File.expand_path("../../../.git", __dir__)
if File.directory?(git_dir)
- return @git_commit_sha = Dir.chdir(git_dir) { `git rev-parse --short HEAD`.strip.freeze }
+ return @git_commit_sha = IO.popen(%w[git rev-parse --short HEAD], { chdir: git_dir }, &:read).strip.freeze
end
@git_commit_sha ||= "unknown"
diff --git a/lib/bundler/bundler.gemspec b/lib/bundler/bundler.gemspec
index da50b46225..2d6269fae1 100644
--- a/lib/bundler/bundler.gemspec
+++ b/lib/bundler/bundler.gemspec
@@ -29,15 +29,17 @@ Gem::Specification.new do |s|
"source_code_uri" => "https://github.com/rubygems/rubygems/tree/master/bundler",
}
- s.required_ruby_version = ">= 2.6.0"
- s.required_rubygems_version = ">= 3.0.1"
+ s.required_ruby_version = ">= 3.0.0"
+
+ # It should match the RubyGems version shipped with `required_ruby_version` above
+ s.required_rubygems_version = ">= 3.2.3"
s.files = Dir.glob("lib/bundler{.rb,/**/*}", File::FNM_DOTMATCH).reject {|f| File.directory?(f) }
# include the gemspec itself because warbler breaks w/o it
s.files += %w[lib/bundler/bundler.gemspec]
- s.bindir = "libexec"
+ s.bindir = "exe"
s.executables = %w[bundle bundler]
s.require_paths = ["lib"]
end
diff --git a/lib/bundler/capistrano.rb b/lib/bundler/capistrano.rb
index 1f3712d48e..705840143f 100644
--- a/lib/bundler/capistrano.rb
+++ b/lib/bundler/capistrano.rb
@@ -17,6 +17,6 @@ end
Capistrano::Configuration.instance(:must_exist).load do
before "deploy:finalize_update", "bundle:install"
- Bundler::Deployment.define_task(self, :task, :except => { :no_release => true })
+ Bundler::Deployment.define_task(self, :task, except: { no_release: true })
set :rake, lambda { "#{fetch(:bundle_cmd, "bundle")} exec rake" }
end
diff --git a/lib/bundler/checksum.rb b/lib/bundler/checksum.rb
new file mode 100644
index 0000000000..60ba93417c
--- /dev/null
+++ b/lib/bundler/checksum.rb
@@ -0,0 +1,254 @@
+# frozen_string_literal: true
+
+module Bundler
+ class Checksum
+ ALGO_SEPARATOR = "="
+ DEFAULT_ALGORITHM = "sha256"
+ private_constant :DEFAULT_ALGORITHM
+ DEFAULT_BLOCK_SIZE = 16_384
+ private_constant :DEFAULT_BLOCK_SIZE
+
+ class << self
+ def from_gem_package(gem_package, algo = DEFAULT_ALGORITHM)
+ return if Bundler.settings[:disable_checksum_validation]
+ return unless source = gem_package.instance_variable_get(:@gem)
+ return unless source.respond_to?(:with_read_io)
+
+ source.with_read_io do |io|
+ from_gem(io, source.path)
+ ensure
+ io.rewind
+ end
+ end
+
+ def from_gem(io, pathname, algo = DEFAULT_ALGORITHM)
+ digest = Bundler::SharedHelpers.digest(algo.upcase).new
+ buf = String.new(capacity: DEFAULT_BLOCK_SIZE)
+ digest << io.readpartial(DEFAULT_BLOCK_SIZE, buf) until io.eof?
+ Checksum.new(algo, digest.hexdigest!, Source.new(:gem, pathname))
+ end
+
+ def from_api(digest, source_uri, algo = DEFAULT_ALGORITHM)
+ return if Bundler.settings[:disable_checksum_validation]
+
+ Checksum.new(algo, to_hexdigest(digest, algo), Source.new(:api, source_uri))
+ end
+
+ def from_lock(lock_checksum, lockfile_location)
+ algo, digest = lock_checksum.strip.split(ALGO_SEPARATOR, 2)
+ Checksum.new(algo, to_hexdigest(digest, algo), Source.new(:lock, lockfile_location))
+ end
+
+ def to_hexdigest(digest, algo = DEFAULT_ALGORITHM)
+ return digest unless algo == DEFAULT_ALGORITHM
+ return digest if digest.match?(/\A[0-9a-f]{64}\z/i)
+
+ if digest.match?(%r{\A[-0-9a-z_+/]{43}={0,2}\z}i)
+ digest = digest.tr("-_", "+/") # fix urlsafe base64
+ digest.unpack1("m0").unpack1("H*")
+ else
+ raise ArgumentError, "#{digest.inspect} is not a valid SHA256 hex or base64 digest"
+ end
+ end
+ end
+
+ attr_reader :algo, :digest, :sources
+
+ def initialize(algo, digest, source)
+ @algo = algo
+ @digest = digest
+ @sources = [source]
+ end
+
+ def ==(other)
+ match?(other) && other.sources == sources
+ end
+
+ alias_method :eql?, :==
+
+ def same_source?(other)
+ sources.include?(other.sources.first)
+ end
+
+ def match?(other)
+ other.is_a?(self.class) && other.digest == digest && other.algo == algo
+ end
+
+ def hash
+ digest.hash
+ end
+
+ def to_s
+ "#{to_lock} (from #{sources.first}#{", ..." if sources.size > 1})"
+ end
+
+ def to_lock
+ "#{algo}#{ALGO_SEPARATOR}#{digest}"
+ end
+
+ def merge!(other)
+ return nil unless match?(other)
+
+ @sources.concat(other.sources).uniq!
+ self
+ end
+
+ def formatted_sources
+ sources.join("\n and ").concat("\n")
+ end
+
+ def removable?
+ sources.all?(&:removable?)
+ end
+
+ def removal_instructions
+ msg = +""
+ i = 1
+ sources.each do |source|
+ msg << " #{i}. #{source.removal}\n"
+ i += 1
+ end
+ msg << " #{i}. run `bundle install`\n"
+ end
+
+ def inspect
+ abbr = "#{algo}#{ALGO_SEPARATOR}#{digest[0, 8]}"
+ from = "from #{sources.join(" and ")}"
+ "#<#{self.class}:#{object_id} #{abbr} #{from}>"
+ end
+
+ class Source
+ attr_reader :type, :location
+
+ def initialize(type, location)
+ @type = type
+ @location = location
+ end
+
+ def removable?
+ type == :lock || type == :gem
+ end
+
+ def ==(other)
+ other.is_a?(self.class) && other.type == type && other.location == location
+ end
+
+ # phrased so that the usual string format is grammatically correct
+ # rake (10.3.2) sha256=abc123 from #{to_s}
+ def to_s
+ case type
+ when :lock
+ "the lockfile CHECKSUMS at #{location}"
+ when :gem
+ "the gem at #{location}"
+ when :api
+ "the API at #{location}"
+ else
+ "#{location} (#{type})"
+ end
+ end
+
+ # A full sentence describing how to remove the checksum
+ def removal
+ case type
+ when :lock
+ "remove the matching checksum in #{location}"
+ when :gem
+ "remove the gem at #{location}"
+ when :api
+ "checksums from #{location} cannot be locally modified, you may need to update your sources"
+ else
+ "remove #{location} (#{type})"
+ end
+ end
+ end
+
+ class Store
+ attr_reader :store
+ protected :store
+
+ def initialize
+ @store = {}
+ @store_mutex = Mutex.new
+ end
+
+ def inspect
+ "#<#{self.class}:#{object_id} size=#{store.size}>"
+ end
+
+ # Replace when the new checksum is from the same source.
+ # The primary purpose is registering checksums from gems where there are
+ # duplicates of the same gem (according to full_name) in the index.
+ #
+ # In particular, this is when 2 gems have two similar platforms, e.g.
+ # "darwin20" and "darwin-20", both of which resolve to darwin-20.
+ # In the Index, the later gem replaces the former, so we do that here.
+ #
+ # However, if the new checksum is from a different source, we register like normal.
+ # This ensures a mismatch error where there are multiple top level sources
+ # that contain the same gem with different checksums.
+ def replace(spec, checksum)
+ return unless checksum
+
+ lock_name = spec.name_tuple.lock_name
+ @store_mutex.synchronize do
+ existing = fetch_checksum(lock_name, checksum.algo)
+ if !existing || existing.same_source?(checksum)
+ store_checksum(lock_name, checksum)
+ else
+ merge_checksum(lock_name, checksum, existing)
+ end
+ end
+ end
+
+ def register(spec, checksum)
+ return unless checksum
+
+ register_checksum(spec.name_tuple.lock_name, checksum)
+ end
+
+ def merge!(other)
+ other.store.each do |lock_name, checksums|
+ checksums.each do |_algo, checksum|
+ register_checksum(lock_name, checksum)
+ end
+ end
+ end
+
+ def to_lock(spec)
+ lock_name = spec.name_tuple.lock_name
+ checksums = @store[lock_name]
+ if checksums
+ "#{lock_name} #{checksums.values.map(&:to_lock).sort.join(",")}"
+ else
+ lock_name
+ end
+ end
+
+ private
+
+ def register_checksum(lock_name, checksum)
+ @store_mutex.synchronize do
+ existing = fetch_checksum(lock_name, checksum.algo)
+ if existing
+ merge_checksum(lock_name, checksum, existing)
+ else
+ store_checksum(lock_name, checksum)
+ end
+ end
+ end
+
+ def merge_checksum(lock_name, checksum, existing)
+ existing.merge!(checksum) || raise(ChecksumMismatchError.new(lock_name, existing, checksum))
+ end
+
+ def store_checksum(lock_name, checksum)
+ (@store[lock_name] ||= {})[checksum.algo] = checksum
+ end
+
+ def fetch_checksum(lock_name, algo)
+ @store[lock_name]&.fetch(algo, nil)
+ end
+ end
+ end
+end
diff --git a/lib/bundler/ci_detector.rb b/lib/bundler/ci_detector.rb
new file mode 100644
index 0000000000..e5fedbdea8
--- /dev/null
+++ b/lib/bundler/ci_detector.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+module Bundler
+ module CIDetector
+ # NOTE: Any changes made here will need to be made to both lib/rubygems/ci_detector.rb and
+ # bundler/lib/bundler/ci_detector.rb (which are enforced duplicates).
+ # TODO: Drop that duplication once bundler drops support for RubyGems 3.4
+ #
+ # ## Recognized CI providers, their signifiers, and the relevant docs ##
+ #
+ # Travis CI - CI, TRAVIS https://docs.travis-ci.com/user/environment-variables/#default-environment-variables
+ # Cirrus CI - CI, CIRRUS_CI https://cirrus-ci.org/guide/writing-tasks/#environment-variables
+ # Circle CI - CI, CIRCLECI https://circleci.com/docs/variables/#built-in-environment-variables
+ # Gitlab CI - CI, GITLAB_CI https://docs.gitlab.com/ee/ci/variables/
+ # AppVeyor - CI, APPVEYOR https://www.appveyor.com/docs/environment-variables/
+ # CodeShip - CI_NAME https://docs.cloudbees.com/docs/cloudbees-codeship/latest/pro-builds-and-configuration/environment-variables#_default_environment_variables
+ # dsari - CI, DSARI https://github.com/rfinnie/dsari#running
+ # Jenkins - BUILD_NUMBER https://www.jenkins.io/doc/book/pipeline/jenkinsfile/#using-environment-variables
+ # TeamCity - TEAMCITY_VERSION https://www.jetbrains.com/help/teamcity/predefined-build-parameters.html#Predefined+Server+Build+Parameters
+ # Appflow - CI_BUILD_ID https://ionic.io/docs/appflow/automation/environments#predefined-environments
+ # TaskCluster - TASKCLUSTER_ROOT_URL https://docs.taskcluster.net/docs/manual/design/env-vars
+ # Semaphore - CI, SEMAPHORE https://docs.semaphoreci.com/ci-cd-environment/environment-variables/
+ # BuildKite - CI, BUILDKITE https://buildkite.com/docs/pipelines/environment-variables
+ # GoCD - GO_SERVER_URL https://docs.gocd.org/current/faq/dev_use_current_revision_in_build.html
+ # GH Actions - CI, GITHUB_ACTIONS https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables
+ #
+ # ### Some "standard" ENVs that multiple providers may set ###
+ #
+ # * CI - this is set by _most_ (but not all) CI providers now; it's approaching a standard.
+ # * CI_NAME - Not as frequently used, but some providers set this to specify their own name
+
+ # Any of these being set is a reasonably reliable indicator that we are
+ # executing in a CI environment.
+ ENV_INDICATORS = [
+ "CI",
+ "CI_NAME",
+ "CONTINUOUS_INTEGRATION",
+ "BUILD_NUMBER",
+ "CI_APP_ID",
+ "CI_BUILD_ID",
+ "CI_BUILD_NUMBER",
+ "RUN_ID",
+ "TASKCLUSTER_ROOT_URL",
+ ].freeze
+
+ # For each CI, this env suffices to indicate that we're on _that_ CI's
+ # containers. (A few of them only supply a CI_NAME variable, which is also
+ # nice). And if they set "CI" but we can't tell which one they are, we also
+ # want to know that - a bare "ci" without another token tells us as much.
+ ENV_DESCRIPTORS = {
+ "TRAVIS" => "travis",
+ "CIRCLECI" => "circle",
+ "CIRRUS_CI" => "cirrus",
+ "DSARI" => "dsari",
+ "SEMAPHORE" => "semaphore",
+ "JENKINS_URL" => "jenkins",
+ "BUILDKITE" => "buildkite",
+ "GO_SERVER_URL" => "go",
+ "GITLAB_CI" => "gitlab",
+ "GITHUB_ACTIONS" => "github",
+ "TASKCLUSTER_ROOT_URL" => "taskcluster",
+ "CI" => "ci",
+ }.freeze
+
+ def self.ci?
+ ENV_INDICATORS.any? {|var| ENV.include?(var) }
+ end
+
+ def self.ci_strings
+ matching_names = ENV_DESCRIPTORS.select {|env, _| ENV[env] }.values
+ matching_names << ENV["CI_NAME"].downcase if ENV["CI_NAME"]
+ matching_names.reject(&:empty?).sort.uniq
+ end
+ end
+end
diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb
index dd91038d64..40f19c7fed 100644
--- a/lib/bundler/cli.rb
+++ b/lib/bundler/cli.rb
@@ -5,6 +5,7 @@ require_relative "vendored_thor"
module Bundler
class CLI < Thor
require_relative "cli/common"
+ require_relative "cli/install"
package_name "Bundler"
@@ -69,7 +70,7 @@ module Bundler
Bundler.settings.set_command_option_if_given :retry, options[:retry]
current_cmd = args.last[:current_command].name
- auto_install if AUTO_INSTALL_CMDS.include?(current_cmd)
+ Bundler.auto_install if AUTO_INSTALL_CMDS.include?(current_cmd)
rescue UnknownArgumentError => e
raise InvalidOption, e.message
ensure
@@ -80,10 +81,10 @@ module Bundler
unprinted_warnings.each {|w| Bundler.ui.warn(w) }
end
- check_unknown_options!(:except => [:config, :exec])
+ check_unknown_options!(except: [:config, :exec])
stop_on_unknown_option! :exec
- desc "cli_help", "Prints a summary of bundler commands", :hide => true
+ desc "cli_help", "Prints a summary of bundler commands", hide: true
def cli_help
version
Bundler.ui.info "\n"
@@ -99,21 +100,23 @@ module Bundler
shell.say "Bundler commands:\n\n"
shell.say " Primary commands:\n"
- shell.print_table(primary_commands, :indent => 4, :truncate => true)
+ shell.print_table(primary_commands, indent: 4, truncate: true)
shell.say
shell.say " Utilities:\n"
- shell.print_table(utilities, :indent => 4, :truncate => true)
+ shell.print_table(utilities, indent: 4, truncate: true)
shell.say
self.class.send(:class_options_help, shell)
end
default_task(Bundler.feature_flag.default_cli_command)
- class_option "no-color", :type => :boolean, :desc => "Disable colorization in output"
- class_option "retry", :type => :numeric, :aliases => "-r", :banner => "NUM",
- :desc => "Specify the number of times you wish to attempt network commands"
- class_option "verbose", :type => :boolean, :desc => "Enable verbose output mode", :aliases => "-V"
+ class_option "no-color", type: :boolean, desc: "Disable colorization in output"
+ class_option "retry", type: :numeric, aliases: "-r", banner: "NUM",
+ desc: "Specify the number of times you wish to attempt network commands"
+ class_option "verbose", type: :boolean, desc: "Enable verbose output mode", aliases: "-V"
def help(cli = nil)
+ cli = self.class.all_aliases[cli] if self.class.all_aliases[cli]
+
case cli
when "gemfile" then command = "gemfile"
when nil then command = "bundle"
@@ -127,8 +130,8 @@ module Bundler
if man_pages.include?(command)
man_page = man_pages[command]
- if Bundler.which("man") && man_path !~ %r{^file:/.+!/META-INF/jruby.home/.+}
- Kernel.exec "man #{man_page}"
+ if Bundler.which("man") && !man_path.match?(%r{^file:/.+!/META-INF/jruby.home/.+})
+ Kernel.exec("man", man_page)
else
puts File.read("#{man_path}/#{File.basename(man_page)}.ronn")
end
@@ -155,8 +158,8 @@ module Bundler
Gemfile to a gem with a gemspec, the --gemspec option will automatically add each
dependency listed in the gemspec file to the newly created Gemfile.
D
- method_option "gemspec", :type => :string, :banner => "Use the specified .gemspec to create the Gemfile"
- method_option "gemfile", :type => :string, :banner => "Use the specified name for the gemfile instead of 'Gemfile'"
+ method_option "gemspec", type: :string, banner: "Use the specified .gemspec to create the Gemfile"
+ method_option "gemfile", type: :string, banner: "Use the specified name for the gemfile instead of 'Gemfile'"
def init
require_relative "cli/init"
Init.new(options.dup).run
@@ -168,12 +171,9 @@ module Bundler
all gems are found, Bundler prints a success message and exits with a status of 0.
If not, the first missing gem is listed and Bundler exits status 1.
D
- method_option "dry-run", :type => :boolean, :default => false, :banner =>
- "Lock the Gemfile"
- method_option "gemfile", :type => :string, :banner =>
- "Use the specified gemfile instead of Gemfile"
- method_option "path", :type => :string, :banner =>
- "Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME).#{" Bundler will remember this value for future installs on this machine" unless Bundler.feature_flag.forget_cli_options?}"
+ method_option "dry-run", type: :boolean, default: false, banner: "Lock the Gemfile"
+ method_option "gemfile", type: :string, banner: "Use the specified gemfile instead of Gemfile"
+ method_option "path", type: :string, banner: "Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME).#{" Bundler will remember this value for future installs on this machine" unless Bundler.feature_flag.forget_cli_options?}"
def check
remembered_flag_deprecation("path")
@@ -187,10 +187,14 @@ module Bundler
long_desc <<-D
Removes the given gems from the Gemfile while ensuring that the resulting Gemfile is still valid. If the gem is not found, Bundler prints a error message and if gem could not be removed due to any reason Bundler will display a warning.
D
- method_option "install", :type => :boolean, :banner =>
- "Runs 'bundle install' after removing the gems from the Gemfile"
+ method_option "install", type: :boolean, banner: "Runs 'bundle install' after removing the gems from the Gemfile"
def remove(*gems)
- SharedHelpers.major_deprecation(2, "The `--install` flag has been deprecated. `bundle install` is triggered by default.") if ARGV.include?("--install")
+ if ARGV.include?("--install")
+ message = "The `--install` flag has been deprecated. `bundle install` is triggered by default."
+ removed_message = "The `--install` flag has been removed. `bundle install` is triggered by default."
+ SharedHelpers.major_deprecation(2, message, removed_message: removed_message)
+ end
+
require_relative "cli/remove"
Remove.new(gems, options).run
end
@@ -206,58 +210,40 @@ module Bundler
If the bundle has already been installed, bundler will tell you so and then exit.
D
- method_option "binstubs", :type => :string, :lazy_default => "bin", :banner =>
- "Generate bin stubs for bundled gems to ./bin"
- method_option "clean", :type => :boolean, :banner =>
- "Run bundle clean automatically after install"
- method_option "deployment", :type => :boolean, :banner =>
- "Install using defaults tuned for deployment environments"
- method_option "frozen", :type => :boolean, :banner =>
- "Do not allow the Gemfile.lock to be updated after this install"
- method_option "full-index", :type => :boolean, :banner =>
- "Fall back to using the single-file index of all gems"
- method_option "gemfile", :type => :string, :banner =>
- "Use the specified gemfile instead of Gemfile"
- method_option "jobs", :aliases => "-j", :type => :numeric, :banner =>
- "Specify the number of jobs to run in parallel"
- method_option "local", :type => :boolean, :banner =>
- "Do not attempt to fetch gems remotely and use the gem cache instead"
- method_option "prefer-local", :type => :boolean, :banner =>
- "Only attempt to fetch gems remotely if not present locally, even if newer versions are available remotely"
- method_option "no-cache", :type => :boolean, :banner =>
- "Don't update the existing gem cache."
- method_option "redownload", :type => :boolean, :aliases => "--force", :banner =>
- "Force downloading every gem."
- method_option "no-prune", :type => :boolean, :banner =>
- "Don't remove stale gems from the cache."
- method_option "path", :type => :string, :banner =>
- "Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME).#{" Bundler will remember this value for future installs on this machine" unless Bundler.feature_flag.forget_cli_options?}"
- method_option "quiet", :type => :boolean, :banner =>
- "Only output warnings and errors."
- method_option "shebang", :type => :string, :banner =>
- "Specify a different shebang executable name than the default (usually 'ruby')"
- method_option "standalone", :type => :array, :lazy_default => [], :banner =>
- "Make a bundle that can work without the Bundler runtime"
- method_option "system", :type => :boolean, :banner =>
- "Install to the system location ($BUNDLE_PATH or $GEM_HOME) even if the bundle was previously installed somewhere else for this application"
- method_option "trust-policy", :alias => "P", :type => :string, :banner =>
- "Gem trust policy (like gem install -P). Must be one of " +
- Bundler.rubygems.security_policy_keys.join("|")
- method_option "without", :type => :array, :banner =>
- "Exclude gems that are part of the specified named group."
- method_option "with", :type => :array, :banner =>
- "Include gems that are part of the specified named group."
+ method_option "binstubs", type: :string, lazy_default: "bin", banner: "Generate bin stubs for bundled gems to ./bin"
+ method_option "clean", type: :boolean, banner: "Run bundle clean automatically after install"
+ method_option "deployment", type: :boolean, banner: "Install using defaults tuned for deployment environments"
+ method_option "frozen", type: :boolean, banner: "Do not allow the Gemfile.lock to be updated after this install"
+ method_option "full-index", type: :boolean, banner: "Fall back to using the single-file index of all gems"
+ method_option "gemfile", type: :string, banner: "Use the specified gemfile instead of Gemfile"
+ method_option "jobs", aliases: "-j", type: :numeric, banner: "Specify the number of jobs to run in parallel"
+ method_option "local", type: :boolean, banner: "Do not attempt to fetch gems remotely and use the gem cache instead"
+ method_option "prefer-local", type: :boolean, banner: "Only attempt to fetch gems remotely if not present locally, even if newer versions are available remotely"
+ method_option "no-cache", type: :boolean, banner: "Don't update the existing gem cache."
+ method_option "redownload", type: :boolean, aliases: "--force", banner: "Force downloading every gem."
+ method_option "no-prune", type: :boolean, banner: "Don't remove stale gems from the cache."
+ method_option "path", type: :string, banner: "Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME).#{" Bundler will remember this value for future installs on this machine" unless Bundler.feature_flag.forget_cli_options?}"
+ method_option "quiet", type: :boolean, banner: "Only output warnings and errors."
+ method_option "shebang", type: :string, banner: "Specify a different shebang executable name than the default (usually 'ruby')"
+ method_option "standalone", type: :array, lazy_default: [], banner: "Make a bundle that can work without the Bundler runtime"
+ method_option "system", type: :boolean, banner: "Install to the system location ($BUNDLE_PATH or $GEM_HOME) even if the bundle was previously installed somewhere else for this application"
+ method_option "trust-policy", alias: "P", type: :string, banner: "Gem trust policy (like gem install -P). Must be one of " +
+ Bundler.rubygems.security_policy_keys.join("|")
+ method_option "without", type: :array, banner: "Exclude gems that are part of the specified named group."
+ method_option "with", type: :array, banner: "Include gems that are part of the specified named group."
def install
SharedHelpers.major_deprecation(2, "The `--force` option has been renamed to `--redownload`") if ARGV.include?("--force")
- %w[clean deployment frozen no-prune path shebang system without with].each do |option|
+ %w[clean deployment frozen no-prune path shebang without with].each do |option|
remembered_flag_deprecation(option)
end
+ print_remembered_flag_deprecation("--system", "path.system", "true") if ARGV.include?("--system")
+
remembered_negative_flag_deprecation("no-deployment")
require_relative "cli/install"
- Bundler.settings.temporary(:no_install => false) do
+ Bundler.settings.temporary(no_install: false) do
Install.new(options.dup).run
end
end
@@ -270,44 +256,27 @@ module Bundler
update when you have changed the Gemfile, or if you want to get the newest
possible versions of the gems in the bundle.
D
- method_option "full-index", :type => :boolean, :banner =>
- "Fall back to using the single-file index of all gems"
- method_option "gemfile", :type => :string, :banner =>
- "Use the specified gemfile instead of Gemfile"
- method_option "group", :aliases => "-g", :type => :array, :banner =>
- "Update a specific group"
- method_option "jobs", :aliases => "-j", :type => :numeric, :banner =>
- "Specify the number of jobs to run in parallel"
- method_option "local", :type => :boolean, :banner =>
- "Do not attempt to fetch gems remotely and use the gem cache instead"
- method_option "quiet", :type => :boolean, :banner =>
- "Only output warnings and errors."
- method_option "source", :type => :array, :banner =>
- "Update a specific source (and all gems associated with it)"
- method_option "redownload", :type => :boolean, :aliases => "--force", :banner =>
- "Force downloading every gem."
- method_option "ruby", :type => :boolean, :banner =>
- "Update ruby specified in Gemfile.lock"
- method_option "bundler", :type => :string, :lazy_default => "> 0.a", :banner =>
- "Update the locked version of bundler"
- method_option "patch", :type => :boolean, :banner =>
- "Prefer updating only to next patch version"
- method_option "minor", :type => :boolean, :banner =>
- "Prefer updating only to next minor version"
- method_option "major", :type => :boolean, :banner =>
- "Prefer updating to next major version (default)"
- method_option "pre", :type => :boolean, :banner =>
- "Always choose the highest allowed version when updating gems, regardless of prerelease status"
- method_option "strict", :type => :boolean, :banner =>
- "Do not allow any gem to be updated past latest --patch | --minor | --major"
- method_option "conservative", :type => :boolean, :banner =>
- "Use bundle install conservative update behavior and do not allow shared dependencies to be updated."
- method_option "all", :type => :boolean, :banner =>
- "Update everything."
+ method_option "full-index", type: :boolean, banner: "Fall back to using the single-file index of all gems"
+ method_option "gemfile", type: :string, banner: "Use the specified gemfile instead of Gemfile"
+ method_option "group", aliases: "-g", type: :array, banner: "Update a specific group"
+ method_option "jobs", aliases: "-j", type: :numeric, banner: "Specify the number of jobs to run in parallel"
+ method_option "local", type: :boolean, banner: "Do not attempt to fetch gems remotely and use the gem cache instead"
+ method_option "quiet", type: :boolean, banner: "Only output warnings and errors."
+ method_option "source", type: :array, banner: "Update a specific source (and all gems associated with it)"
+ method_option "redownload", type: :boolean, aliases: "--force", banner: "Force downloading every gem."
+ method_option "ruby", type: :boolean, banner: "Update ruby specified in Gemfile.lock"
+ method_option "bundler", type: :string, lazy_default: "> 0.a", banner: "Update the locked version of bundler"
+ method_option "patch", type: :boolean, banner: "Prefer updating only to next patch version"
+ method_option "minor", type: :boolean, banner: "Prefer updating only to next minor version"
+ method_option "major", type: :boolean, banner: "Prefer updating to next major version (default)"
+ method_option "pre", type: :boolean, banner: "Always choose the highest allowed version when updating gems, regardless of prerelease status"
+ method_option "strict", type: :boolean, banner: "Do not allow any gem to be updated past latest --patch | --minor | --major"
+ method_option "conservative", type: :boolean, banner: "Use bundle install conservative update behavior and do not allow shared dependencies to be updated."
+ method_option "all", type: :boolean, banner: "Update everything."
def update(*gems)
SharedHelpers.major_deprecation(2, "The `--force` option has been renamed to `--redownload`") if ARGV.include?("--force")
require_relative "cli/update"
- Bundler.settings.temporary(:no_install => false) do
+ Bundler.settings.temporary(no_install: false) do
Update.new(options, gems).run
end
end
@@ -317,21 +286,25 @@ module Bundler
Show lists the names and versions of all gems that are required by your Gemfile.
Calling show with [GEM] will list the exact location of that gem on your machine.
D
- method_option "paths", :type => :boolean,
- :banner => "List the paths of all gems that are required by your Gemfile."
- method_option "outdated", :type => :boolean,
- :banner => "Show verbose output including whether gems are outdated."
+ method_option "paths", type: :boolean,
+ banner: "List the paths of all gems that are required by your Gemfile."
+ method_option "outdated", type: :boolean,
+ banner: "Show verbose output including whether gems are outdated."
def show(gem_name = nil)
- SharedHelpers.major_deprecation(2, "the `--outdated` flag to `bundle show` was undocumented and will be removed without replacement") if ARGV.include?("--outdated")
+ if ARGV.include?("--outdated")
+ message = "the `--outdated` flag to `bundle show` was undocumented and will be removed without replacement"
+ removed_message = "the `--outdated` flag to `bundle show` was undocumented and has been removed without replacement"
+ SharedHelpers.major_deprecation(2, message, removed_message: removed_message)
+ end
require_relative "cli/show"
Show.new(options, gem_name).run
end
desc "list", "List all gems in the bundle"
- method_option "name-only", :type => :boolean, :banner => "print only the gem names"
- method_option "only-group", :type => :array, :default => [], :banner => "print gems from a given set of groups"
- method_option "without-group", :type => :array, :default => [], :banner => "print all gems except from a given set of groups"
- method_option "paths", :type => :boolean, :banner => "print the path to each gem in the bundle"
+ method_option "name-only", type: :boolean, banner: "print only the gem names"
+ method_option "only-group", type: :array, default: [], banner: "print gems from a given set of groups"
+ method_option "without-group", type: :array, default: [], banner: "print all gems except from a given set of groups"
+ method_option "paths", type: :boolean, banner: "print the path to each gem in the bundle"
def list
require_relative "cli/list"
List.new(options).run
@@ -340,8 +313,8 @@ module Bundler
map aliases_for("list")
desc "info GEM [OPTIONS]", "Show information for the given gem"
- method_option "path", :type => :boolean, :banner => "Print full path to gem"
- method_option "version", :type => :boolean, :banner => "Print gem version"
+ method_option "path", type: :boolean, banner: "Print full path to gem"
+ method_option "version", type: :boolean, banner: "Print gem version"
def info(gem_name)
require_relative "cli/info"
Info.new(options, gem_name).run
@@ -353,18 +326,12 @@ module Bundler
or the --binstubs directory if one has been set. Calling binstubs with [GEM [GEM]]
will create binstubs for all given gems.
D
- method_option "force", :type => :boolean, :default => false, :banner =>
- "Overwrite existing binstubs if they exist"
- method_option "path", :type => :string, :lazy_default => "bin", :banner =>
- "Binstub destination directory (default bin)"
- method_option "shebang", :type => :string, :banner =>
- "Specify a different shebang executable name than the default (usually 'ruby')"
- method_option "standalone", :type => :boolean, :banner =>
- "Make binstubs that can work without the Bundler runtime"
- method_option "all", :type => :boolean, :banner =>
- "Install binstubs for all gems"
- method_option "all-platforms", :type => :boolean, :default => false, :banner =>
- "Install binstubs for all platforms"
+ method_option "force", type: :boolean, default: false, banner: "Overwrite existing binstubs if they exist"
+ method_option "path", type: :string, lazy_default: "bin", banner: "Binstub destination directory (default bin)"
+ method_option "shebang", type: :string, banner: "Specify a different shebang executable name than the default (usually 'ruby')"
+ method_option "standalone", type: :boolean, banner: "Make binstubs that can work without the Bundler runtime"
+ method_option "all", type: :boolean, banner: "Install binstubs for all gems"
+ method_option "all-platforms", type: :boolean, default: false, banner: "Install binstubs for all platforms"
def binstubs(*gems)
require_relative "cli/binstubs"
Binstubs.new(options, gems).run
@@ -374,19 +341,19 @@ module Bundler
long_desc <<-D
Adds the specified gem to Gemfile (if valid) and run 'bundle install' in one step.
D
- method_option "version", :aliases => "-v", :type => :string
- method_option "group", :aliases => "-g", :type => :string
- method_option "source", :aliases => "-s", :type => :string
- method_option "require", :aliases => "-r", :type => :string, :banner => "Adds require path to gem. Provide false, or a path as a string."
- method_option "path", :type => :string
- method_option "git", :type => :string
- method_option "github", :type => :string
- method_option "branch", :type => :string
- method_option "ref", :type => :string
- method_option "skip-install", :type => :boolean, :banner =>
- "Adds gem to the Gemfile but does not install it"
- method_option "optimistic", :type => :boolean, :banner => "Adds optimistic declaration of version to gem"
- method_option "strict", :type => :boolean, :banner => "Adds strict declaration of version to gem"
+ method_option "version", aliases: "-v", type: :string
+ method_option "group", aliases: "-g", type: :string
+ method_option "source", aliases: "-s", type: :string
+ method_option "require", aliases: "-r", type: :string, banner: "Adds require path to gem. Provide false, or a path as a string."
+ method_option "path", type: :string
+ method_option "git", type: :string
+ method_option "github", type: :string
+ method_option "branch", type: :string
+ method_option "ref", type: :string
+ method_option "glob", type: :string, banner: "The location of a dependency's .gemspec, expanded within Ruby (single quotes recommended)"
+ method_option "skip-install", type: :boolean, banner: "Adds gem to the Gemfile but does not install it"
+ method_option "optimistic", type: :boolean, banner: "Adds optimistic declaration of version to gem"
+ method_option "strict", type: :boolean, banner: "Adds strict declaration of version to gem"
def add(*gems)
require_relative "cli/add"
Add.new(options.dup, gems).run
@@ -402,54 +369,45 @@ module Bundler
For more information on patch level options (--major, --minor, --patch,
--strict) see documentation on the same options on the update command.
D
- method_option "group", :type => :string, :banner => "List gems from a specific group"
- method_option "groups", :type => :boolean, :banner => "List gems organized by groups"
- method_option "local", :type => :boolean, :banner =>
- "Do not attempt to fetch gems remotely and use the gem cache instead"
- method_option "pre", :type => :boolean, :banner => "Check for newer pre-release gems"
- method_option "source", :type => :array, :banner => "Check against a specific source"
- method_option "filter-strict", :type => :boolean, :aliases => "--strict", :banner =>
- "Only list newer versions allowed by your Gemfile requirements"
- method_option "update-strict", :type => :boolean, :banner =>
- "Strict conservative resolution, do not allow any gem to be updated past latest --patch | --minor | --major"
- method_option "minor", :type => :boolean, :banner => "Prefer updating only to next minor version"
- method_option "major", :type => :boolean, :banner => "Prefer updating to next major version (default)"
- method_option "patch", :type => :boolean, :banner => "Prefer updating only to next patch version"
- method_option "filter-major", :type => :boolean, :banner => "Only list major newer versions"
- method_option "filter-minor", :type => :boolean, :banner => "Only list minor newer versions"
- method_option "filter-patch", :type => :boolean, :banner => "Only list patch newer versions"
- method_option "parseable", :aliases => "--porcelain", :type => :boolean, :banner =>
- "Use minimal formatting for more parseable output"
- method_option "only-explicit", :type => :boolean, :banner =>
- "Only list gems specified in your Gemfile, not their dependencies"
+ method_option "group", type: :string, banner: "List gems from a specific group"
+ method_option "groups", type: :boolean, banner: "List gems organized by groups"
+ method_option "local", type: :boolean, banner: "Do not attempt to fetch gems remotely and use the gem cache instead"
+ method_option "pre", type: :boolean, banner: "Check for newer pre-release gems"
+ method_option "source", type: :array, banner: "Check against a specific source"
+ method_option "filter-strict", type: :boolean, aliases: "--strict", banner: "Only list newer versions allowed by your Gemfile requirements"
+ method_option "update-strict", type: :boolean, banner: "Strict conservative resolution, do not allow any gem to be updated past latest --patch | --minor | --major"
+ method_option "minor", type: :boolean, banner: "Prefer updating only to next minor version"
+ method_option "major", type: :boolean, banner: "Prefer updating to next major version (default)"
+ method_option "patch", type: :boolean, banner: "Prefer updating only to next patch version"
+ method_option "filter-major", type: :boolean, banner: "Only list major newer versions"
+ method_option "filter-minor", type: :boolean, banner: "Only list minor newer versions"
+ method_option "filter-patch", type: :boolean, banner: "Only list patch newer versions"
+ method_option "parseable", aliases: "--porcelain", type: :boolean, banner: "Use minimal formatting for more parseable output"
+ method_option "only-explicit", type: :boolean, banner: "Only list gems specified in your Gemfile, not their dependencies"
def outdated(*gems)
require_relative "cli/outdated"
Outdated.new(options, gems).run
end
desc "fund [OPTIONS]", "Lists information about gems seeking funding assistance"
- method_option "group", :aliases => "-g", :type => :array, :banner =>
- "Fetch funding information for a specific group"
+ method_option "group", aliases: "-g", type: :array, banner: "Fetch funding information for a specific group"
def fund
require_relative "cli/fund"
Fund.new(options).run
end
desc "cache [OPTIONS]", "Locks and then caches all of the gems into vendor/cache"
- method_option "all", :type => :boolean,
- :default => Bundler.feature_flag.cache_all?,
- :banner => "Include all sources (including path and git)."
- method_option "all-platforms", :type => :boolean, :banner => "Include gems for all platforms present in the lockfile, not only the current one"
- method_option "cache-path", :type => :string, :banner =>
- "Specify a different cache path than the default (vendor/cache)."
- method_option "gemfile", :type => :string, :banner => "Use the specified gemfile instead of Gemfile"
- method_option "no-install", :type => :boolean, :banner => "Don't install the gems, only update the cache."
- method_option "no-prune", :type => :boolean, :banner => "Don't remove stale gems from the cache."
- method_option "path", :type => :string, :banner =>
- "Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME).#{" Bundler will remember this value for future installs on this machine" unless Bundler.feature_flag.forget_cli_options?}"
- method_option "quiet", :type => :boolean, :banner => "Only output warnings and errors."
- method_option "frozen", :type => :boolean, :banner =>
- "Do not allow the Gemfile.lock to be updated after this bundle cache operation's install"
+ method_option "all", type: :boolean,
+ default: Bundler.feature_flag.cache_all?,
+ banner: "Include all sources (including path and git)."
+ method_option "all-platforms", type: :boolean, banner: "Include gems for all platforms present in the lockfile, not only the current one"
+ method_option "cache-path", type: :string, banner: "Specify a different cache path than the default (vendor/cache)."
+ method_option "gemfile", type: :string, banner: "Use the specified gemfile instead of Gemfile"
+ method_option "no-install", type: :boolean, banner: "Don't install the gems, only update the cache."
+ method_option "no-prune", type: :boolean, banner: "Don't remove stale gems from the cache."
+ method_option "path", type: :string, banner: "Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME).#{" Bundler will remember this value for future installs on this machine" unless Bundler.feature_flag.forget_cli_options?}"
+ method_option "quiet", type: :boolean, banner: "Only output warnings and errors."
+ method_option "frozen", type: :boolean, banner: "Do not allow the Gemfile.lock to be updated after this bundle cache operation's install"
long_desc <<-D
The cache command will copy the .gem files for every gem in the bundle into the
directory ./vendor/cache. If you then check that directory into your source
@@ -457,17 +415,20 @@ module Bundler
bundle without having to download any additional gems.
D
def cache
- SharedHelpers.major_deprecation 2,
- "The `--all` flag is deprecated because it relies on being " \
- "remembered across bundler invocations, which bundler will no longer " \
- "do in future versions. Instead please use `bundle config set cache_all true`, " \
- "and stop using this flag" if ARGV.include?("--all")
-
- SharedHelpers.major_deprecation 2,
- "The `--path` flag is deprecated because its semantics are unclear. " \
- "Use `bundle config cache_path` to configure the path of your cache of gems, " \
- "and `bundle config path` to configure the path where your gems are installed, " \
- "and stop using this flag" if ARGV.include?("--path")
+ print_remembered_flag_deprecation("--all", "cache_all", "true") if ARGV.include?("--all")
+
+ if ARGV.include?("--path")
+ message =
+ "The `--path` flag is deprecated because its semantics are unclear. " \
+ "Use `bundle config cache_path` to configure the path of your cache of gems, " \
+ "and `bundle config path` to configure the path where your gems are installed, " \
+ "and stop using this flag"
+ removed_message =
+ "The `--path` flag has been removed because its semantics were unclear. " \
+ "Use `bundle config cache_path` to configure the path of your cache of gems, " \
+ "and `bundle config path` to configure the path where your gems are installed."
+ SharedHelpers.major_deprecation 2, message, removed_message: removed_message
+ end
require_relative "cli/cache"
Cache.new(options).run
@@ -476,8 +437,8 @@ module Bundler
map aliases_for("cache")
desc "exec [OPTIONS]", "Run the command in context of the bundle"
- method_option :keep_file_descriptors, :type => :boolean, :default => true
- method_option :gemfile, :type => :string, :required => false
+ method_option :keep_file_descriptors, type: :boolean, default: true
+ method_option :gemfile, type: :string, required: false
long_desc <<-D
Exec runs a command, providing it access to the gems in the bundle. While using
bundle exec you can require and call the bundled gems as if they were installed
@@ -485,7 +446,9 @@ module Bundler
D
def exec(*args)
if ARGV.include?("--no-keep-file-descriptors")
- SharedHelpers.major_deprecation(2, "The `--no-keep-file-descriptors` has been deprecated. `bundle exec` no longer mess with your file descriptors. Close them in the exec'd script if you need to")
+ message = "The `--no-keep-file-descriptors` has been deprecated. `bundle exec` no longer mess with your file descriptors. Close them in the exec'd script if you need to"
+ removed_message = "The `--no-keep-file-descriptors` has been removed. `bundle exec` no longer mess with your file descriptors. Close them in the exec'd script if you need to"
+ SharedHelpers.major_deprecation(2, message, removed_message: removed_message)
end
require_relative "cli/exec"
@@ -510,7 +473,7 @@ module Bundler
subcommand "config", Config
desc "open GEM", "Opens the source directory of the given bundled gem"
- method_option "path", :type => :string, :lazy_default => "", :banner => "Open relative path of the gem source."
+ method_option "path", type: :string, lazy_default: "", banner: "Open relative path of the gem source."
def open(name)
require_relative "cli/open"
Open.new(options, name).run
@@ -555,17 +518,17 @@ module Bundler
end
unless Bundler.feature_flag.bundler_3_mode?
- desc "viz [OPTIONS]", "Generates a visual dependency graph", :hide => true
+ desc "viz [OPTIONS]", "Generates a visual dependency graph", hide: true
long_desc <<-D
Viz generates a PNG file of the current Gemfile as a dependency graph.
Viz requires the ruby-graphviz gem (and its dependencies).
The associated gems must also be installed via 'bundle install'.
D
- method_option :file, :type => :string, :default => "gem_graph", :aliases => "-f", :desc => "The name to use for the generated file. see format option"
- method_option :format, :type => :string, :default => "png", :aliases => "-F", :desc => "This is output format option. Supported format is png, jpg, svg, dot ..."
- method_option :requirements, :type => :boolean, :default => false, :aliases => "-R", :desc => "Set to show the version of each required dependency."
- method_option :version, :type => :boolean, :default => false, :aliases => "-v", :desc => "Set to show each gem version."
- method_option :without, :type => :array, :default => [], :aliases => "-W", :banner => "GROUP[ GROUP...]", :desc => "Exclude gems that are part of the specified named group."
+ method_option :file, type: :string, default: "gem_graph", aliases: "-f", desc: "The name to use for the generated file. see format option"
+ method_option :format, type: :string, default: "png", aliases: "-F", desc: "This is output format option. Supported format is png, jpg, svg, dot ..."
+ method_option :requirements, type: :boolean, default: false, aliases: "-R", desc: "Set to show the version of each required dependency."
+ method_option :version, type: :boolean, default: false, aliases: "-v", desc: "Set to show each gem version."
+ method_option :without, type: :array, default: [], aliases: "-W", banner: "GROUP[ GROUP...]", desc: "Exclude gems that are part of the specified named group."
def viz
SharedHelpers.major_deprecation 2, "The `viz` command has been renamed to `graph` and moved to a plugin. See https://github.com/rubygems/bundler-graph"
require_relative "cli/viz"
@@ -576,23 +539,23 @@ module Bundler
old_gem = instance_method(:gem)
desc "gem NAME [OPTIONS]", "Creates a skeleton for creating a rubygem"
- method_option :exe, :type => :boolean, :default => false, :aliases => ["--bin", "-b"], :desc => "Generate a binary executable for your library."
- method_option :coc, :type => :boolean, :desc => "Generate a code of conduct file. Set a default with `bundle config set --global gem.coc true`."
- method_option :edit, :type => :string, :aliases => "-e", :required => false, :banner => "EDITOR",
- :lazy_default => [ENV["BUNDLER_EDITOR"], ENV["VISUAL"], ENV["EDITOR"]].find {|e| !e.nil? && !e.empty? },
- :desc => "Open generated gemspec in the specified editor (defaults to $EDITOR or $BUNDLER_EDITOR)"
- method_option :ext, :type => :string, :desc => "Generate the boilerplate for C extension code.", :enum => EXTENSIONS
- method_option :git, :type => :boolean, :default => true, :desc => "Initialize a git repo inside your library."
- method_option :mit, :type => :boolean, :desc => "Generate an MIT license file. Set a default with `bundle config set --global gem.mit true`."
- method_option :rubocop, :type => :boolean, :desc => "Add rubocop to the generated Rakefile and gemspec. Set a default with `bundle config set --global gem.rubocop true`."
- method_option :changelog, :type => :boolean, :desc => "Generate changelog file. Set a default with `bundle config set --global gem.changelog true`."
- method_option :test, :type => :string, :lazy_default => Bundler.settings["gem.test"] || "", :aliases => "-t", :banner => "Use the specified test framework for your library",
- :desc => "Generate a test directory for your library, either rspec, minitest or test-unit. Set a default with `bundle config set --global gem.test (rspec|minitest|test-unit)`."
- method_option :ci, :type => :string, :lazy_default => Bundler.settings["gem.ci"] || "",
- :desc => "Generate CI configuration, either GitHub Actions, GitLab CI or CircleCI. Set a default with `bundle config set --global gem.ci (github|gitlab|circle)`"
- method_option :linter, :type => :string, :lazy_default => Bundler.settings["gem.linter"] || "",
- :desc => "Add a linter and code formatter, either RuboCop or Standard. Set a default with `bundle config set --global gem.linter (rubocop|standard)`"
- method_option :github_username, :type => :string, :default => Bundler.settings["gem.github_username"], :banner => "Set your username on GitHub", :desc => "Fill in GitHub username on README so that you don't have to do it manually. Set a default with `bundle config set --global gem.github_username <your_username>`."
+ method_option :exe, type: :boolean, default: false, aliases: ["--bin", "-b"], desc: "Generate a binary executable for your library."
+ method_option :coc, type: :boolean, desc: "Generate a code of conduct file. Set a default with `bundle config set --global gem.coc true`."
+ method_option :edit, type: :string, aliases: "-e", required: false, banner: "EDITOR",
+ lazy_default: [ENV["BUNDLER_EDITOR"], ENV["VISUAL"], ENV["EDITOR"]].find {|e| !e.nil? && !e.empty? },
+ desc: "Open generated gemspec in the specified editor (defaults to $EDITOR or $BUNDLER_EDITOR)"
+ method_option :ext, type: :string, desc: "Generate the boilerplate for C extension code.", enum: EXTENSIONS
+ method_option :git, type: :boolean, default: true, desc: "Initialize a git repo inside your library."
+ method_option :mit, type: :boolean, desc: "Generate an MIT license file. Set a default with `bundle config set --global gem.mit true`."
+ method_option :rubocop, type: :boolean, desc: "Add rubocop to the generated Rakefile and gemspec. Set a default with `bundle config set --global gem.rubocop true`."
+ method_option :changelog, type: :boolean, desc: "Generate changelog file. Set a default with `bundle config set --global gem.changelog true`."
+ method_option :test, type: :string, lazy_default: Bundler.settings["gem.test"] || "", aliases: "-t", banner: "Use the specified test framework for your library",
+ desc: "Generate a test directory for your library, either rspec, minitest or test-unit. Set a default with `bundle config set --global gem.test (rspec|minitest|test-unit)`."
+ method_option :ci, type: :string, lazy_default: Bundler.settings["gem.ci"] || "",
+ desc: "Generate CI configuration, either GitHub Actions, GitLab CI or CircleCI. Set a default with `bundle config set --global gem.ci (github|gitlab|circle)`"
+ method_option :linter, type: :string, lazy_default: Bundler.settings["gem.linter"] || "",
+ desc: "Add a linter and code formatter, either RuboCop or Standard. Set a default with `bundle config set --global gem.linter (rubocop|standard)`"
+ method_option :github_username, type: :string, default: Bundler.settings["gem.github_username"], banner: "Set your username on GitHub", desc: "Fill in GitHub username on README so that you don't have to do it manually. Set a default with `bundle config set --global gem.github_username <your_username>`."
def gem(name)
end
@@ -623,29 +586,24 @@ module Bundler
File.expand_path("templates", __dir__)
end
- desc "clean [OPTIONS]", "Cleans up unused gems in your bundler directory", :hide => true
- method_option "dry-run", :type => :boolean, :default => false, :banner =>
- "Only print out changes, do not clean gems"
- method_option "force", :type => :boolean, :default => false, :banner =>
- "Forces cleaning up unused gems even if Bundler is configured to use globally installed gems. As a consequence, removes all system gems except for the ones in the current application."
+ desc "clean [OPTIONS]", "Cleans up unused gems in your bundler directory", hide: true
+ method_option "dry-run", type: :boolean, default: false, banner: "Only print out changes, do not clean gems"
+ method_option "force", type: :boolean, default: false, banner: "Forces cleaning up unused gems even if Bundler is configured to use globally installed gems. As a consequence, removes all system gems except for the ones in the current application."
def clean
require_relative "cli/clean"
Clean.new(options.dup).run
end
desc "platform [OPTIONS]", "Displays platform compatibility information"
- method_option "ruby", :type => :boolean, :default => false, :banner =>
- "only display ruby related platform information"
+ method_option "ruby", type: :boolean, default: false, banner: "only display ruby related platform information"
def platform
require_relative "cli/platform"
Platform.new(options).run
end
- desc "inject GEM VERSION", "Add the named gem, with version requirements, to the resolved Gemfile", :hide => true
- method_option "source", :type => :string, :banner =>
- "Install gem from the given source"
- method_option "group", :type => :string, :banner =>
- "Install gem into a bundler group"
+ desc "inject GEM VERSION", "Add the named gem, with version requirements, to the resolved Gemfile", hide: true
+ method_option "source", type: :string, banner: "Install gem from the given source"
+ method_option "group", type: :string, banner: "Install gem into a bundler group"
def inject(name, version)
SharedHelpers.major_deprecation 2, "The `inject` command has been replaced by the `add` command"
require_relative "cli/inject"
@@ -653,36 +611,21 @@ module Bundler
end
desc "lock", "Creates a lockfile without installing"
- method_option "update", :type => :array, :lazy_default => true, :banner =>
- "ignore the existing lockfile, update all gems by default, or update list of given gems"
- method_option "local", :type => :boolean, :default => false, :banner =>
- "do not attempt to fetch remote gemspecs and use the local gem cache only"
- method_option "print", :type => :boolean, :default => false, :banner =>
- "print the lockfile to STDOUT instead of writing to the file system"
- method_option "gemfile", :type => :string, :banner =>
- "Use the specified gemfile instead of Gemfile"
- method_option "lockfile", :type => :string, :default => nil, :banner =>
- "the path the lockfile should be written to"
- method_option "full-index", :type => :boolean, :default => false, :banner =>
- "Fall back to using the single-file index of all gems"
- method_option "add-platform", :type => :array, :default => [], :banner =>
- "Add a new platform to the lockfile"
- method_option "remove-platform", :type => :array, :default => [], :banner =>
- "Remove a platform from the lockfile"
- method_option "patch", :type => :boolean, :banner =>
- "If updating, prefer updating only to next patch version"
- method_option "minor", :type => :boolean, :banner =>
- "If updating, prefer updating only to next minor version"
- method_option "major", :type => :boolean, :banner =>
- "If updating, prefer updating to next major version (default)"
- method_option "pre", :type => :boolean, :banner =>
- "If updating, always choose the highest allowed version, regardless of prerelease status"
- method_option "strict", :type => :boolean, :banner =>
- "If updating, do not allow any gem to be updated past latest --patch | --minor | --major"
- method_option "conservative", :type => :boolean, :banner =>
- "If updating, use bundle install conservative update behavior and do not allow shared dependencies to be updated"
- method_option "bundler", :type => :string, :lazy_default => "> 0.a", :banner =>
- "Update the locked version of bundler"
+ method_option "update", type: :array, lazy_default: true, banner: "ignore the existing lockfile, update all gems by default, or update list of given gems"
+ method_option "local", type: :boolean, default: false, banner: "do not attempt to fetch remote gemspecs and use the local gem cache only"
+ method_option "print", type: :boolean, default: false, banner: "print the lockfile to STDOUT instead of writing to the file system"
+ method_option "gemfile", type: :string, banner: "Use the specified gemfile instead of Gemfile"
+ method_option "lockfile", type: :string, default: nil, banner: "the path the lockfile should be written to"
+ method_option "full-index", type: :boolean, default: false, banner: "Fall back to using the single-file index of all gems"
+ method_option "add-platform", type: :array, default: [], banner: "Add a new platform to the lockfile"
+ method_option "remove-platform", type: :array, default: [], banner: "Remove a platform from the lockfile"
+ method_option "patch", type: :boolean, banner: "If updating, prefer updating only to next patch version"
+ method_option "minor", type: :boolean, banner: "If updating, prefer updating only to next minor version"
+ method_option "major", type: :boolean, banner: "If updating, prefer updating to next major version (default)"
+ method_option "pre", type: :boolean, banner: "If updating, always choose the highest allowed version, regardless of prerelease status"
+ method_option "strict", type: :boolean, banner: "If updating, do not allow any gem to be updated past latest --patch | --minor | --major"
+ method_option "conservative", type: :boolean, banner: "If updating, use bundle install conservative update behavior and do not allow shared dependencies to be updated"
+ method_option "bundler", type: :string, lazy_default: "> 0.a", banner: "Update the locked version of bundler"
def lock
require_relative "cli/lock"
Lock.new(options).run
@@ -699,10 +642,8 @@ module Bundler
missing dependencies are detected, Bundler prints them and exits status 1.
Otherwise, Bundler prints a success message and exits with a status of 0.
D
- method_option "gemfile", :type => :string, :banner =>
- "Use the specified gemfile instead of Gemfile"
- method_option "quiet", :type => :boolean, :banner =>
- "Only output warnings and errors."
+ method_option "gemfile", type: :string, banner: "Use the specified gemfile instead of Gemfile"
+ method_option "quiet", type: :boolean, banner: "Only output warnings and errors."
def doctor
require_relative "cli/doctor"
Doctor.new(options).run
@@ -722,7 +663,9 @@ module Bundler
D
def pristine(*gems)
require_relative "cli/pristine"
- Pristine.new(gems).run
+ Bundler.settings.temporary(no_install: false) do
+ Pristine.new(gems).run
+ end
end
if Bundler.feature_flag.plugins?
@@ -743,7 +686,6 @@ module Bundler
exec_used = args.index {|a| exec_commands.include? a }
command = args.find {|a| bundler_commands.include? a }
- command = all_aliases[command] if all_aliases[command]
if exec_used && help_used
if exec_used + help_used == 1
@@ -764,7 +706,9 @@ module Bundler
# when deprecated version of `--ext` is called
# print out deprecation warning and pretend `--ext=c` was provided
if deprecated_ext_value?(arguments)
- SharedHelpers.major_deprecation 2, "Extensions can now be generated using C or Rust, so `--ext` with no arguments has been deprecated. Please select a language, e.g. `--ext=rust` to generate a Rust extension. This gem will now be generated as if `--ext=c` was used."
+ message = "Extensions can now be generated using C or Rust, so `--ext` with no arguments has been deprecated. Please select a language, e.g. `--ext=rust` to generate a Rust extension. This gem will now be generated as if `--ext=c` was used."
+ removed_message = "Extensions can now be generated using C or Rust, so `--ext` with no arguments has been removed. Please select a language, e.g. `--ext=rust` to generate a Rust extension."
+ SharedHelpers.major_deprecation 2, message, removed_message: removed_message
arguments[arguments.index("--ext")] = "--ext=c"
end
end
@@ -794,26 +738,6 @@ module Bundler
private
- # Automatically invoke `bundle install` and resume if
- # Bundler.settings[:auto_install] exists. This is set through config cmd
- # `bundle config set --global auto_install 1`.
- #
- # Note that this method `nil`s out the global Definition object, so it
- # should be called first, before you instantiate anything like an
- # `Installer` that'll keep a reference to the old one instead.
- def auto_install
- return unless Bundler.settings[:auto_install]
-
- begin
- Bundler.definition.specs
- rescue GemNotFound
- Bundler.ui.info "Automatically installing missing gems."
- Bundler.reset!
- invoke :install, []
- Bundler.reset!
- end
- end
-
def current_command
_, _, config = @_initializer
config[:current_command]
@@ -844,7 +768,7 @@ module Bundler
return unless SharedHelpers.md5_available?
latest = Fetcher::CompactIndex.
- new(nil, Source::Rubygems::Remote.new(Bundler::URI("https://rubygems.org")), nil).
+ new(nil, Source::Rubygems::Remote.new(Gem::URI("https://rubygems.org")), nil, nil).
send(:compact_index_client).
instance_variable_get(:@cache).
dependencies("bundler").
@@ -883,12 +807,23 @@ module Bundler
value = options[name]
value = value.join(" ").to_s if option.type == :array
+ value = "'#{value}'" unless option.type == :boolean
- Bundler::SharedHelpers.major_deprecation 2,
+ print_remembered_flag_deprecation(flag_name, name.tr("-", "_"), value)
+ end
+
+ def print_remembered_flag_deprecation(flag_name, option_name, option_value)
+ message =
"The `#{flag_name}` flag is deprecated because it relies on being " \
"remembered across bundler invocations, which bundler will no longer " \
- "do in future versions. Instead please use `bundle config set --local #{name.tr("-", "_")} " \
- "'#{value}'`, and stop using this flag"
+ "do in future versions. Instead please use `bundle config set #{option_name} " \
+ "#{option_value}`, and stop using this flag"
+ removed_message =
+ "The `#{flag_name}` flag has been removed because it relied on being " \
+ "remembered across bundler invocations, which bundler will no longer " \
+ "do. Instead please use `bundle config set #{option_name} " \
+ "#{option_value}`, and stop using this flag"
+ Bundler::SharedHelpers.major_deprecation 2, message, removed_message: removed_message
end
end
end
diff --git a/lib/bundler/cli/add.rb b/lib/bundler/cli/add.rb
index 08fa6547fb..002d9e1d33 100644
--- a/lib/bundler/cli/add.rb
+++ b/lib/bundler/cli/add.rb
@@ -28,9 +28,9 @@ module Bundler
dependencies = gems.map {|g| Bundler::Dependency.new(g, version, options) }
Injector.inject(dependencies,
- :conservative_versioning => options[:version].nil?, # Perform conservative versioning only when version is not specified
- :optimistic => options[:optimistic],
- :strict => options[:strict])
+ conservative_versioning: options[:version].nil?, # Perform conservative versioning only when version is not specified
+ optimistic: options[:optimistic],
+ strict: options[:strict])
end
def validate_options!
diff --git a/lib/bundler/cli/binstubs.rb b/lib/bundler/cli/binstubs.rb
index fc2fad47a5..8ce138df96 100644
--- a/lib/bundler/cli/binstubs.rb
+++ b/lib/bundler/cli/binstubs.rb
@@ -17,9 +17,9 @@ module Bundler
installer = Installer.new(Bundler.root, Bundler.definition)
installer_opts = {
- :force => options[:force],
- :binstubs_cmd => true,
- :all_platforms => options["all-platforms"],
+ force: options[:force],
+ binstubs_cmd: true,
+ all_platforms: options["all-platforms"],
}
if options[:all]
@@ -45,7 +45,7 @@ module Bundler
next
end
- Bundler.settings.temporary(:path => (Bundler.settings[:path] || Bundler.root)) do
+ Bundler.settings.temporary(path: Bundler.settings[:path] || Bundler.root) do
installer.generate_standalone_bundler_executable_stubs(spec, installer_opts)
end
else
diff --git a/lib/bundler/cli/cache.rb b/lib/bundler/cli/cache.rb
index c8698ed7e3..2e63a16ec3 100644
--- a/lib/bundler/cli/cache.rb
+++ b/lib/bundler/cli/cache.rb
@@ -19,7 +19,7 @@ module Bundler
# TODO: move cache contents here now that all bundles are locked
custom_path = Bundler.settings[:path] if options[:path]
- Bundler.settings.temporary(:cache_all_platforms => options["all-platforms"]) do
+ Bundler.settings.temporary(cache_all_platforms: options["all-platforms"]) do
Bundler.load.cache(custom_path)
end
end
diff --git a/lib/bundler/cli/check.rb b/lib/bundler/cli/check.rb
index cc1f37f0c3..33d31cdd27 100644
--- a/lib/bundler/cli/check.rb
+++ b/lib/bundler/cli/check.rb
@@ -29,10 +29,10 @@ module Bundler
Bundler.ui.warn "Install missing gems with `bundle install`"
exit 1
elsif !Bundler.default_lockfile.file? && Bundler.frozen_bundle?
- Bundler.ui.error "This bundle has been frozen, but there is no #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} present"
+ Bundler.ui.error "This bundle has been frozen, but there is no #{SharedHelpers.relative_lockfile_path} present"
exit 1
else
- Bundler.load.lock(:preserve_unknown_sections => true) unless options[:"dry-run"]
+ Bundler.load.lock(preserve_unknown_sections: true) unless options[:"dry-run"]
Bundler.ui.info "The Gemfile's dependencies are satisfied"
end
end
diff --git a/lib/bundler/cli/common.rb b/lib/bundler/cli/common.rb
index d654406f65..7ef6deb2cf 100644
--- a/lib/bundler/cli/common.rb
+++ b/lib/bundler/cli/common.rb
@@ -54,9 +54,12 @@ module Bundler
Bundler.definition.specs.each do |spec|
return spec if spec.name == name
- specs << spec if regexp && spec.name =~ regexp
+ specs << spec if regexp && spec.name.match?(regexp)
end
+ default_spec = default_gem_spec(name)
+ specs << default_spec if default_spec
+
case specs.count
when 0
dep_in_other_group = Bundler.definition.current_dependencies.find {|dep|dep.name == name }
@@ -75,6 +78,11 @@ module Bundler
raise GemNotFound, gem_not_found_message(name, Bundler.definition.dependencies)
end
+ def self.default_gem_spec(name)
+ gem_spec = Gem::Specification.find_all_by_name(name).last
+ gem_spec if gem_spec&.default_gem?
+ end
+
def self.ask_for_spec_from(specs)
specs.each_with_index do |spec, index|
Bundler.ui.info "#{index.succ} : #{spec.name}", true
diff --git a/lib/bundler/cli/config.rb b/lib/bundler/cli/config.rb
index e1222c75dd..77b502fe60 100644
--- a/lib/bundler/cli/config.rb
+++ b/lib/bundler/cli/config.rb
@@ -2,17 +2,17 @@
module Bundler
class CLI::Config < Thor
- class_option :parseable, :type => :boolean, :banner => "Use minimal formatting for more parseable output"
+ class_option :parseable, type: :boolean, banner: "Use minimal formatting for more parseable output"
def self.scope_options
- method_option :global, :type => :boolean, :banner => "Only change the global config"
- method_option :local, :type => :boolean, :banner => "Only change the local config"
+ method_option :global, type: :boolean, banner: "Only change the global config"
+ method_option :local, type: :boolean, banner: "Only change the local config"
end
private_class_method :scope_options
- desc "base NAME [VALUE]", "The Bundler 1 config interface", :hide => true
+ desc "base NAME [VALUE]", "The Bundler 1 config interface", hide: true
scope_options
- method_option :delete, :type => :boolean, :banner => "delete"
+ method_option :delete, type: :boolean, banner: "delete"
def base(name = nil, *value)
new_args =
if ARGV.size == 1
@@ -25,8 +25,9 @@ module Bundler
["config", "get", ARGV[1]]
end
- SharedHelpers.major_deprecation 3,
- "Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle #{new_args.join(" ")}` instead."
+ message = "Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle #{new_args.join(" ")}` instead."
+ removed_message = "Using the `config` command without a subcommand [list, get, set, unset] is has been removed. Use `bundle #{new_args.join(" ")}` instead."
+ SharedHelpers.major_deprecation 3, message, removed_message: removed_message
Base.new(options, name, value, self).run
end
diff --git a/lib/bundler/cli/console.rb b/lib/bundler/cli/console.rb
index 1eb8ea8254..840cf14fd7 100644
--- a/lib/bundler/cli/console.rb
+++ b/lib/bundler/cli/console.rb
@@ -9,8 +9,9 @@ module Bundler
end
def run
- Bundler::SharedHelpers.major_deprecation 2, "bundle console will be replaced " \
- "by `bin/console` generated by `bundle gem <name>`"
+ message = "bundle console will be replaced by `bin/console` generated by `bundle gem <name>`"
+ removed_message = "bundle console has been replaced by `bin/console` generated by `bundle gem <name>`"
+ Bundler::SharedHelpers.major_deprecation 2, message, removed_message: removed_message
group ? Bundler.require(:default, *group.split(" ").map!(&:to_sym)) : Bundler.require
ARGV.clear
diff --git a/lib/bundler/cli/doctor.rb b/lib/bundler/cli/doctor.rb
index e299a5a8c2..1f6fc93c16 100644
--- a/lib/bundler/cli/doctor.rb
+++ b/lib/bundler/cli/doctor.rb
@@ -6,8 +6,8 @@ require "fiddle"
module Bundler
class CLI::Doctor
- DARWIN_REGEX = /\s+(.+) \(compatibility /.freeze
- LDD_REGEX = /\t\S+ => (\S+) \(\S+\)/.freeze
+ DARWIN_REGEX = /\s+(.+) \(compatibility /
+ LDD_REGEX = /\t\S+ => (\S+) \(\S+\)/
attr_reader :options
diff --git a/lib/bundler/cli/exec.rb b/lib/bundler/cli/exec.rb
index 42b602a055..f81cd5d2c4 100644
--- a/lib/bundler/cli/exec.rb
+++ b/lib/bundler/cli/exec.rb
@@ -12,7 +12,7 @@ module Bundler
@options = options
@cmd = args.shift
@args = args
- @args << { :close_others => !options.keep_file_descriptors? } unless Bundler.current_ruby.jruby?
+ @args << { close_others: !options.keep_file_descriptors? } unless Bundler.current_ruby.jruby?
end
def run
diff --git a/lib/bundler/cli/gem.rb b/lib/bundler/cli/gem.rb
index 7f1200f4a0..b6571d0e86 100644
--- a/lib/bundler/cli/gem.rb
+++ b/lib/bundler/cli/gem.rb
@@ -11,7 +11,7 @@ module Bundler
class CLI::Gem
TEST_FRAMEWORK_VERSIONS = {
"rspec" => "3.0",
- "minitest" => "5.0",
+ "minitest" => "5.16",
"test-unit" => "3.0",
}.freeze
@@ -32,7 +32,6 @@ module Bundler
validate_ext_name if @extension
validate_rust_builder_rubygems_version if @extension == "rust"
- travis_removal_info
end
def run
@@ -59,23 +58,23 @@ module Bundler
end
config = {
- :name => name,
- :underscored_name => underscored_name,
- :namespaced_path => namespaced_path,
- :makefile_path => "#{underscored_name}/#{underscored_name}",
- :constant_name => constant_name,
- :constant_array => constant_array,
- :author => git_author_name.empty? ? "TODO: Write your name" : git_author_name,
- :email => git_user_email.empty? ? "TODO: Write your email address" : git_user_email,
- :test => options[:test],
- :ext => extension,
- :exe => options[:exe],
- :bundler_version => bundler_dependency_version,
- :git => use_git,
- :github_username => github_username.empty? ? "[USERNAME]" : github_username,
- :required_ruby_version => required_ruby_version,
- :rust_builder_required_rubygems_version => rust_builder_required_rubygems_version,
- :minitest_constant_name => minitest_constant_name,
+ name: name,
+ underscored_name: underscored_name,
+ namespaced_path: namespaced_path,
+ makefile_path: "#{underscored_name}/#{underscored_name}",
+ constant_name: constant_name,
+ constant_array: constant_array,
+ author: git_author_name.empty? ? "TODO: Write your name" : git_author_name,
+ email: git_user_email.empty? ? "TODO: Write your email address" : git_user_email,
+ test: options[:test],
+ ext: extension,
+ exe: options[:exe],
+ bundler_version: bundler_dependency_version,
+ git: use_git,
+ github_username: github_username.empty? ? "[USERNAME]" : github_username,
+ required_ruby_version: required_ruby_version,
+ rust_builder_required_rubygems_version: rust_builder_required_rubygems_version,
+ minitest_constant_name: minitest_constant_name,
}
ensure_safe_gem_name(name, constant_array)
@@ -137,10 +136,13 @@ module Bundler
case config[:ci]
when "github"
templates.merge!("github/workflows/main.yml.tt" => ".github/workflows/main.yml")
+ config[:ci_config_path] = ".github "
when "gitlab"
templates.merge!("gitlab-ci.yml.tt" => ".gitlab-ci.yml")
+ config[:ci_config_path] = ".gitlab-ci.yml "
when "circle"
templates.merge!("circleci/config.yml.tt" => ".circleci/config.yml")
+ config[:ci_config_path] = ".circleci "
end
if ask_and_set(:mit, "Do you want to license your code permissively under the MIT license?",
@@ -233,9 +235,7 @@ module Bundler
end
if use_git
- Dir.chdir(target) do
- `git add .`
- end
+ IO.popen(%w[git add .], { chdir: target }, &:read)
end
# Open gemspec in editor
@@ -348,7 +348,7 @@ module Bundler
Bundler.ui.confirm "Do you want to add a code linter and formatter to your gem? " \
"Supported Linters:\n" \
"* RuboCop: https://rubocop.org\n" \
- "* Standard: https://github.com/testdouble/standard\n" \
+ "* Standard: https://github.com/standardrb/standard\n" \
"\n"
Bundler.ui.info hint_text("linter")
@@ -379,15 +379,20 @@ module Bundler
def deprecated_rubocop_option
if !options[:rubocop].nil?
if options[:rubocop]
- Bundler::SharedHelpers.major_deprecation 2, "--rubocop is deprecated, use --linter=rubocop"
+ Bundler::SharedHelpers.major_deprecation 2,
+ "--rubocop is deprecated, use --linter=rubocop",
+ removed_message: "--rubocop has been removed, use --linter=rubocop"
"rubocop"
else
- Bundler::SharedHelpers.major_deprecation 2, "--no-rubocop is deprecated, use --linter"
+ Bundler::SharedHelpers.major_deprecation 2,
+ "--no-rubocop is deprecated, use --linter",
+ removed_message: "--no-rubocop has been removed, use --linter"
false
end
elsif !Bundler.settings["gem.rubocop"].nil?
Bundler::SharedHelpers.major_deprecation 2,
- "config gem.rubocop is deprecated; we've updated your config to use gem.linter instead"
+ "config gem.rubocop is deprecated; we've updated your config to use gem.linter instead",
+ removed_message: "config gem.rubocop has been removed; we've updated your config to use gem.linter instead"
Bundler.settings["gem.rubocop"] ? "rubocop" : false
end
end
@@ -431,7 +436,7 @@ module Bundler
end
def required_ruby_version
- "2.6.0"
+ "3.0.0"
end
def rubocop_version
@@ -442,19 +447,6 @@ module Bundler
"1.3"
end
- # TODO: remove at next minor release
- def travis_removal_info
- if options[:ci] == "travis"
- Bundler.ui.error "Support for Travis CI was removed from gem skeleton generator."
- exit 1
- end
-
- if Bundler.settings["gem.ci"] == "travis"
- Bundler.ui.error "Support for Travis CI was removed from gem skeleton generator, but it is present in bundle config. Please configure another provider using `bundle config set gem.ci SERVICE` (where SERVICE is one of github/gitlab/circle) or unset configuration using `bundle config unset gem.ci`."
- exit 1
- end
- end
-
def validate_rust_builder_rubygems_version
if Gem::Version.new(rust_builder_required_rubygems_version) > Gem.rubygems_version
Bundler.ui.error "Your RubyGems version (#{Gem.rubygems_version}) is too old to build Rust extension. Please update your RubyGems using `gem update --system` or any other way and try again."
diff --git a/lib/bundler/cli/info.rb b/lib/bundler/cli/info.rb
index 36c7a58f12..8f34956aca 100644
--- a/lib/bundler/cli/info.rb
+++ b/lib/bundler/cli/info.rb
@@ -25,19 +25,8 @@ module Bundler
private
- def spec_for_gem(gem_name)
- spec = Bundler.definition.specs.find {|s| s.name == gem_name }
- spec || default_gem_spec(gem_name) || Bundler::CLI::Common.select_spec(gem_name, :regex_match)
- end
-
- def default_gem_spec(gem_name)
- return unless Gem::Specification.respond_to?(:find_all_by_name)
- gem_spec = Gem::Specification.find_all_by_name(gem_name).last
- return gem_spec if gem_spec&.default_gem?
- end
-
- def spec_not_found(gem_name)
- raise GemNotFound, Bundler::CLI::Common.gem_not_found_message(gem_name, Bundler.definition.dependencies)
+ def spec_for_gem(name)
+ Bundler::CLI::Common.select_spec(name, :regex_match)
end
def print_gem_version(spec)
diff --git a/lib/bundler/cli/install.rb b/lib/bundler/cli/install.rb
index c71bcf159f..6c102d537d 100644
--- a/lib/bundler/cli/install.rb
+++ b/lib/bundler/cli/install.rb
@@ -28,8 +28,8 @@ module Bundler
flag = "--deployment flag" if options[:deployment]
flag ||= "--frozen flag" if options[:frozen]
flag ||= "deployment setting"
- raise ProductionError, "The #{flag} requires a #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}. Please make " \
- "sure you have checked your #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} into version control " \
+ raise ProductionError, "The #{flag} requires a lockfile. Please make " \
+ "sure you have checked your #{SharedHelpers.relative_lockfile_path} into version control " \
"before deploying."
end
@@ -51,7 +51,8 @@ module Bundler
if options["binstubs"]
Bundler::SharedHelpers.major_deprecation 2,
- "The --binstubs option will be removed in favor of `bundle binstubs --all`"
+ "The --binstubs option will be removed in favor of `bundle binstubs --all`",
+ removed_message: "The --binstubs option have been removed in favor of `bundle binstubs --all`"
end
Plugin.gemfile_install(Bundler.default_gemfile) if Bundler.feature_flag.plugins?
@@ -61,7 +62,7 @@ module Bundler
installer = Installer.install(Bundler.root, definition, options)
- Bundler.settings.temporary(:cache_all_platforms => options[:local] ? false : Bundler.settings[:cache_all_platforms]) do
+ Bundler.settings.temporary(cache_all_platforms: options[:local] ? false : Bundler.settings[:cache_all_platforms]) do
Bundler.load.cache(nil, options[:local]) if Bundler.app_cache.exist? && !options["no-cache"] && !Bundler.frozen_bundle?
end
@@ -95,7 +96,7 @@ module Bundler
def warn_if_root
return if Bundler.settings[:silence_root_warning] || Gem.win_platform? || !Process.uid.zero?
Bundler.ui.warn "Don't run Bundler as root. Installing your bundle as root " \
- "will break this application for all non-root users on this machine.", :wrap => true
+ "will break this application for all non-root users on this machine.", wrap: true
end
def dependencies_count_for(definition)
@@ -148,7 +149,7 @@ module Bundler
Bundler.settings.set_command_option_if_given :path, options[:path]
if options["standalone"] && Bundler.settings[:path].nil? && !options["local"]
- Bundler.settings.temporary(:path_relative_to_cwd => false) do
+ Bundler.settings.temporary(path_relative_to_cwd: false) do
Bundler.settings.set_command_option :path, "bundle"
end
end
diff --git a/lib/bundler/cli/lock.rb b/lib/bundler/cli/lock.rb
index cb3ed27138..dac3d2a09a 100644
--- a/lib/bundler/cli/lock.rb
+++ b/lib/bundler/cli/lock.rb
@@ -26,42 +26,46 @@ module Bundler
if update.is_a?(Array) # unlocking specific gems
Bundler::CLI::Common.ensure_all_gems_in_lockfile!(update)
- update = { :gems => update, :conservative => conservative }
+ update = { gems: update, conservative: conservative }
elsif update && conservative
- update = { :conservative => conservative }
+ update = { conservative: conservative }
elsif update && bundler
- update = { :bundler => bundler }
+ update = { bundler: bundler }
end
- definition = Bundler.definition(update)
- Bundler::CLI::Common.configure_gem_version_promoter(Bundler.definition, options) if options[:update]
+ file = options[:lockfile]
+ file = file ? Pathname.new(file).expand_path : Bundler.default_lockfile
- options["remove-platform"].each do |platform|
- definition.remove_platform(platform)
- end
+ Bundler.settings.temporary(frozen: false) do
+ definition = Bundler.definition(update, file)
+
+ Bundler::CLI::Common.configure_gem_version_promoter(definition, options) if options[:update]
- options["add-platform"].each do |platform_string|
- platform = Gem::Platform.new(platform_string)
- if platform.to_s == "unknown"
- Bundler.ui.warn "The platform `#{platform_string}` is unknown to RubyGems " \
- "and adding it will likely lead to resolution errors"
+ options["remove-platform"].each do |platform|
+ definition.remove_platform(platform)
end
- definition.add_platform(platform)
- end
- if definition.platforms.empty?
- raise InvalidOption, "Removing all platforms from the bundle is not allowed"
- end
+ options["add-platform"].each do |platform_string|
+ platform = Gem::Platform.new(platform_string)
+ if platform.to_s == "unknown"
+ Bundler.ui.warn "The platform `#{platform_string}` is unknown to RubyGems " \
+ "and adding it will likely lead to resolution errors"
+ end
+ definition.add_platform(platform)
+ end
+
+ if definition.platforms.empty?
+ raise InvalidOption, "Removing all platforms from the bundle is not allowed"
+ end
- definition.resolve_remotely! unless options[:local]
+ definition.resolve_remotely! unless options[:local]
- if print
- puts definition.to_lock
- else
- file = options[:lockfile]
- file = file ? File.expand_path(file) : Bundler.default_lockfile
- puts "Writing lockfile to #{file}"
- definition.lock(file)
+ if print
+ puts definition.to_lock
+ else
+ puts "Writing lockfile to #{file}"
+ definition.lock
+ end
end
Bundler.ui.level = previous_ui_level
diff --git a/lib/bundler/cli/open.rb b/lib/bundler/cli/open.rb
index 8522ec92d6..f24693b843 100644
--- a/lib/bundler/cli/open.rb
+++ b/lib/bundler/cli/open.rb
@@ -18,13 +18,11 @@ module Bundler
Bundler.ui.info "Unable to open #{name} because it's a default gem, so the directory it would normally be installed to does not exist."
else
root_path = spec.full_gem_path
- Dir.chdir(root_path) do
- require "shellwords"
- command = Shellwords.split(editor) << File.join([root_path, path].compact)
- Bundler.with_original_env do
- system(*command)
- end || Bundler.ui.info("Could not run '#{command.join(" ")}'")
- end
+ require "shellwords"
+ command = Shellwords.split(editor) << File.join([root_path, path].compact)
+ Bundler.with_original_env do
+ system(*command, { chdir: root_path })
+ end || Bundler.ui.info("Could not run '#{command.join(" ")}'")
end
end
end
diff --git a/lib/bundler/cli/outdated.rb b/lib/bundler/cli/outdated.rb
index 68c701aefb..ec42e631bb 100644
--- a/lib/bundler/cli/outdated.rb
+++ b/lib/bundler/cli/outdated.rb
@@ -41,12 +41,12 @@ module Bundler
# We're doing a full update
Bundler.definition(true)
else
- Bundler.definition(:gems => gems, :sources => sources)
+ Bundler.definition(gems: gems, sources: sources)
end
Bundler::CLI::Common.configure_gem_version_promoter(
Bundler.definition,
- options.merge(:strict => @strict)
+ options.merge(strict: @strict)
)
definition_resolution = proc do
@@ -90,10 +90,10 @@ module Bundler
end
outdated_gems << {
- :active_spec => active_spec,
- :current_spec => current_spec,
- :dependency => dependency,
- :groups => groups,
+ active_spec: active_spec,
+ current_spec: current_spec,
+ dependency: dependency,
+ groups: groups,
}
end
diff --git a/lib/bundler/cli/plugin.rb b/lib/bundler/cli/plugin.rb
index fe3f4412fa..fd61ef0d95 100644
--- a/lib/bundler/cli/plugin.rb
+++ b/lib/bundler/cli/plugin.rb
@@ -5,20 +5,15 @@ module Bundler
class CLI::Plugin < Thor
desc "install PLUGINS", "Install the plugin from the source"
long_desc <<-D
- Install plugins either from the rubygems source provided (with --source option) or from a git source provided with --git (for remote repos) or --local_git (for local repos). If no sources are provided, it uses Gem.sources
+ Install plugins either from the rubygems source provided (with --source option), from a git source provided with --git, or a local path provided with --path. If no sources are provided, it uses Gem.sources
D
- method_option "source", :type => :string, :default => nil, :banner =>
- "URL of the RubyGems source to fetch the plugin from"
- method_option "version", :type => :string, :default => nil, :banner =>
- "The version of the plugin to fetch"
- method_option "git", :type => :string, :default => nil, :banner =>
- "URL of the git repo to fetch from"
- method_option "local_git", :type => :string, :default => nil, :banner =>
- "Path of the local git repo to fetch from"
- method_option "branch", :type => :string, :default => nil, :banner =>
- "The git branch to checkout"
- method_option "ref", :type => :string, :default => nil, :banner =>
- "The git revision to check out"
+ method_option "source", type: :string, default: nil, banner: "URL of the RubyGems source to fetch the plugin from"
+ method_option "version", type: :string, default: nil, banner: "The version of the plugin to fetch"
+ method_option "git", type: :string, default: nil, banner: "URL of the git repo to fetch from"
+ method_option "local_git", type: :string, default: nil, banner: "Path of the local git repo to fetch from (deprecated)"
+ method_option "branch", type: :string, default: nil, banner: "The git branch to checkout"
+ method_option "ref", type: :string, default: nil, banner: "The git revision to check out"
+ method_option "path", type: :string, default: nil, banner: "Path of a local gem to directly use"
def install(*plugins)
Bundler::Plugin.install(plugins, options)
end
@@ -27,8 +22,7 @@ module Bundler
long_desc <<-D
Uninstall given list of plugins. To uninstall all the plugins, use -all option.
D
- method_option "all", :type => :boolean, :default => nil, :banner =>
- "Uninstall all the installed plugins. If no plugin is installed, then it does nothing."
+ method_option "all", type: :boolean, default: nil, banner: "Uninstall all the installed plugins. If no plugin is installed, then it does nothing."
def uninstall(*plugins)
Bundler::Plugin.uninstall(plugins, options)
end
diff --git a/lib/bundler/cli/pristine.rb b/lib/bundler/cli/pristine.rb
index d6654f8053..e0d7452c44 100644
--- a/lib/bundler/cli/pristine.rb
+++ b/lib/bundler/cli/pristine.rb
@@ -12,40 +12,48 @@ module Bundler
definition.validate_runtime!
installer = Bundler::Installer.new(Bundler.root, definition)
- Bundler.load.specs.each do |spec|
- next if spec.name == "bundler" # Source::Rubygems doesn't install bundler
- next if !@gems.empty? && !@gems.include?(spec.name)
-
- gem_name = "#{spec.name} (#{spec.version}#{spec.git_version})"
- gem_name += " (#{spec.platform})" if !spec.platform.nil? && spec.platform != Gem::Platform::RUBY
-
- case source = spec.source
- when Source::Rubygems
- cached_gem = spec.cache_file
- unless File.exist?(cached_gem)
- Bundler.ui.error("Failed to pristine #{gem_name}. Cached gem #{cached_gem} does not exist.")
+ ProcessLock.lock do
+ installed_specs = definition.specs.reject do |spec|
+ next if spec.name == "bundler" # Source::Rubygems doesn't install bundler
+ next if !@gems.empty? && !@gems.include?(spec.name)
+
+ gem_name = "#{spec.name} (#{spec.version}#{spec.git_version})"
+ gem_name += " (#{spec.platform})" if !spec.platform.nil? && spec.platform != Gem::Platform::RUBY
+
+ case source = spec.source
+ when Source::Rubygems
+ cached_gem = spec.cache_file
+ unless File.exist?(cached_gem)
+ Bundler.ui.error("Failed to pristine #{gem_name}. Cached gem #{cached_gem} does not exist.")
+ next
+ end
+
+ FileUtils.rm_rf spec.full_gem_path
+ when Source::Git
+ if source.local?
+ Bundler.ui.warn("Cannot pristine #{gem_name}. Gem is locally overridden.")
+ next
+ end
+
+ source.remote!
+ if extension_cache_path = source.extension_cache_path(spec)
+ FileUtils.rm_rf extension_cache_path
+ end
+ FileUtils.rm_rf spec.extension_dir
+ FileUtils.rm_rf spec.full_gem_path
+ else
+ Bundler.ui.warn("Cannot pristine #{gem_name}. Gem is sourced from local path.")
next
end
- FileUtils.rm_rf spec.full_gem_path
- when Source::Git
- if source.local?
- Bundler.ui.warn("Cannot pristine #{gem_name}. Gem is locally overridden.")
- next
- end
+ true
+ end.map(&:name)
- source.remote!
- if extension_cache_path = source.extension_cache_path(spec)
- FileUtils.rm_rf extension_cache_path
- end
- FileUtils.rm_rf spec.extension_dir
- FileUtils.rm_rf spec.full_gem_path
- else
- Bundler.ui.warn("Cannot pristine #{gem_name}. Gem is sourced from local path.")
- next
- end
-
- Bundler::GemInstaller.new(spec, installer, false, 0, true).install_from_spec
+ jobs = installer.send(:installation_parallelization, {})
+ pristine_count = definition.specs.count - installed_specs.count
+ # allow a pristining a single gem to skip the parallel worker
+ jobs = [jobs, pristine_count].min
+ ParallelInstaller.call(installer, definition.specs, jobs, false, true, skip: installed_specs)
end
end
end
diff --git a/lib/bundler/cli/update.rb b/lib/bundler/cli/update.rb
index b49182655b..985e8db051 100644
--- a/lib/bundler/cli/update.rb
+++ b/lib/bundler/cli/update.rb
@@ -35,7 +35,7 @@ module Bundler
if full_update
if conservative
- Bundler.definition(:conservative => conservative)
+ Bundler.definition(conservative: conservative)
else
Bundler.definition(true)
end
@@ -51,9 +51,9 @@ module Bundler
gems.concat(deps.map(&:name))
end
- Bundler.definition(:gems => gems, :sources => sources, :ruby => options[:ruby],
- :conservative => conservative,
- :bundler => update_bundler)
+ Bundler.definition(gems: gems, sources: sources, ruby: options[:ruby],
+ conservative: conservative,
+ bundler: update_bundler)
end
Bundler::CLI::Common.configure_gem_version_promoter(Bundler.definition, options)
@@ -63,6 +63,7 @@ module Bundler
opts = options.dup
opts["update"] = true
opts["local"] = options[:local]
+ opts["force"] = options[:redownload]
Bundler.settings.set_command_option_if_given :jobs, opts["jobs"]
@@ -70,7 +71,7 @@ module Bundler
if locked_gems = Bundler.definition.locked_gems
previous_locked_info = locked_gems.specs.reduce({}) do |h, s|
- h[s.name] = { :spec => s, :version => s.version, :source => s.source.identifier }
+ h[s.name] = { spec: s, version: s.version, source: s.source.identifier }
h
end
end
diff --git a/lib/bundler/compact_index_client.rb b/lib/bundler/compact_index_client.rb
index 127a50e810..68e0d7e0d5 100644
--- a/lib/bundler/compact_index_client.rb
+++ b/lib/bundler/compact_index_client.rb
@@ -5,7 +5,13 @@ require "set"
module Bundler
class CompactIndexClient
+ # NOTE: MD5 is here not because we expect a server to respond with it, but
+ # because we use it to generate the etag on first request during the upgrade
+ # to the compact index client that uses opaque etags saved to files.
+ # Remove once 2.5.0 has been out for a while.
+ SUPPORTED_DIGESTS = { "sha-256" => :SHA256, "md5" => :MD5 }.freeze
DEBUG_MUTEX = Thread::Mutex.new
+
def self.debug
return unless ENV["DEBUG_COMPACT_INDEX"]
DEBUG_MUTEX.synchronize { warn("[#{self}] #{yield}") }
@@ -14,6 +20,7 @@ module Bundler
class Error < StandardError; end
require_relative "compact_index_client/cache"
+ require_relative "compact_index_client/cache_file"
require_relative "compact_index_client/updater"
attr_reader :directory
@@ -54,13 +61,13 @@ module Bundler
def names
Bundler::CompactIndexClient.debug { "/names" }
- update(@cache.names_path, "names")
+ update("names", @cache.names_path, @cache.names_etag_path)
@cache.names
end
def versions
Bundler::CompactIndexClient.debug { "/versions" }
- update(@cache.versions_path, "versions")
+ update("versions", @cache.versions_path, @cache.versions_etag_path)
versions, @info_checksums_by_name = @cache.versions
versions
end
@@ -76,36 +83,36 @@ module Bundler
def update_and_parse_checksums!
Bundler::CompactIndexClient.debug { "update_and_parse_checksums!" }
return @info_checksums_by_name if @parsed_checksums
- update(@cache.versions_path, "versions")
+ update("versions", @cache.versions_path, @cache.versions_etag_path)
@info_checksums_by_name = @cache.checksums
@parsed_checksums = true
end
private
- def update(local_path, remote_path)
+ def update(remote_path, local_path, local_etag_path)
Bundler::CompactIndexClient.debug { "update(#{local_path}, #{remote_path})" }
unless synchronize { @endpoints.add?(remote_path) }
Bundler::CompactIndexClient.debug { "already fetched #{remote_path}" }
return
end
- @updater.update(local_path, url(remote_path))
+ @updater.update(url(remote_path), local_path, local_etag_path)
end
def update_info(name)
Bundler::CompactIndexClient.debug { "update_info(#{name})" }
path = @cache.info_path(name)
- checksum = @updater.checksum_for_file(path)
unless existing = @info_checksums_by_name[name]
Bundler::CompactIndexClient.debug { "skipping updating info for #{name} since it is missing from versions" }
return
end
+ checksum = SharedHelpers.checksum_for_file(path, :MD5)
if checksum == existing
Bundler::CompactIndexClient.debug { "skipping updating info for #{name} since the versions checksum matches the local checksum" }
return
end
Bundler::CompactIndexClient.debug { "updating info for #{name} since the versions checksum #{existing} != the local checksum #{checksum}" }
- update(path, "info/#{name}")
+ update("info/#{name}", path, @cache.info_etag_path(name))
end
def url(path)
diff --git a/lib/bundler/compact_index_client/cache.rb b/lib/bundler/compact_index_client/cache.rb
index 0b43581c11..55911fdecf 100644
--- a/lib/bundler/compact_index_client/cache.rb
+++ b/lib/bundler/compact_index_client/cache.rb
@@ -9,11 +9,8 @@ module Bundler
def initialize(directory)
@directory = Pathname.new(directory).expand_path
- info_roots.each do |dir|
- SharedHelpers.filesystem_access(dir) do
- FileUtils.mkdir_p(dir)
- end
- end
+ info_roots.each {|dir| mkdir(dir) }
+ mkdir(info_etag_root)
end
def names
@@ -24,6 +21,10 @@ module Bundler
directory.join("names")
end
+ def names_etag_path
+ directory.join("names.etag")
+ end
+
def versions
versions_by_name = Hash.new {|hash, key| hash[key] = [] }
info_checksums_by_name = {}
@@ -31,12 +32,12 @@ module Bundler
lines(versions_path).each do |line|
name, versions_string, info_checksum = line.split(" ", 3)
info_checksums_by_name[name] = info_checksum || ""
- versions_string.split(",").each do |version|
- if version.start_with?("-")
- version = version[1..-1].split("-", 2).unshift(name)
+ versions_string.split(",") do |version|
+ delete = version.delete_prefix!("-")
+ version = version.split("-", 2).unshift(name)
+ if delete
versions_by_name[name].delete(version)
else
- version = version.split("-", 2).unshift(name)
versions_by_name[name] << version
end
end
@@ -49,6 +50,10 @@ module Bundler
directory.join("versions")
end
+ def versions_etag_path
+ directory.join("versions.etag")
+ end
+
def checksums
checksums = {}
@@ -76,8 +81,19 @@ module Bundler
end
end
+ def info_etag_path(name)
+ name = name.to_s
+ info_etag_root.join("#{name}-#{SharedHelpers.digest(:MD5).hexdigest(name).downcase}")
+ end
+
private
+ def mkdir(dir)
+ SharedHelpers.filesystem_access(dir) do
+ FileUtils.mkdir_p(dir)
+ end
+ end
+
def lines(path)
return [] unless path.file?
lines = SharedHelpers.filesystem_access(path, :read, &:read).split("\n")
@@ -96,6 +112,10 @@ module Bundler
directory.join("info-special-characters"),
]
end
+
+ def info_etag_root
+ directory.join("info-etags")
+ end
end
end
end
diff --git a/lib/bundler/compact_index_client/cache_file.rb b/lib/bundler/compact_index_client/cache_file.rb
new file mode 100644
index 0000000000..5988bc91b3
--- /dev/null
+++ b/lib/bundler/compact_index_client/cache_file.rb
@@ -0,0 +1,153 @@
+# frozen_string_literal: true
+
+require_relative "../vendored_fileutils"
+require "rubygems/package"
+
+module Bundler
+ class CompactIndexClient
+ # write cache files in a way that is robust to concurrent modifications
+ # if digests are given, the checksums will be verified
+ class CacheFile
+ DEFAULT_FILE_MODE = 0o644
+ private_constant :DEFAULT_FILE_MODE
+
+ class Error < RuntimeError; end
+ class ClosedError < Error; end
+
+ class DigestMismatchError < Error
+ def initialize(digests, expected_digests)
+ super "Calculated checksums #{digests.inspect} did not match expected #{expected_digests.inspect}."
+ end
+ end
+
+ # Initialize with a copy of the original file, then yield the instance.
+ def self.copy(path, &block)
+ new(path) do |file|
+ file.initialize_digests
+
+ SharedHelpers.filesystem_access(path, :read) do
+ path.open("rb") do |s|
+ file.open {|f| IO.copy_stream(s, f) }
+ end
+ end
+
+ yield file
+ end
+ end
+
+ # Write data to a temp file, then replace the original file with it verifying the digests if given.
+ def self.write(path, data, digests = nil)
+ return unless data
+ new(path) do |file|
+ file.digests = digests
+ file.write(data)
+ end
+ end
+
+ attr_reader :original_path, :path
+
+ def initialize(original_path, &block)
+ @original_path = original_path
+ @perm = original_path.file? ? original_path.stat.mode : DEFAULT_FILE_MODE
+ @path = original_path.sub(/$/, ".#{$$}.tmp")
+ return unless block_given?
+ begin
+ yield self
+ ensure
+ close
+ end
+ end
+
+ def size
+ path.size
+ end
+
+ # initialize the digests using CompactIndexClient::SUPPORTED_DIGESTS, or a subset based on keys.
+ def initialize_digests(keys = nil)
+ @digests = keys ? SUPPORTED_DIGESTS.slice(*keys) : SUPPORTED_DIGESTS.dup
+ @digests.transform_values! {|algo_class| SharedHelpers.digest(algo_class).new }
+ end
+
+ # reset the digests so they don't contain any previously read data
+ def reset_digests
+ @digests&.each_value(&:reset)
+ end
+
+ # set the digests that will be verified at the end
+ def digests=(expected_digests)
+ @expected_digests = expected_digests
+
+ if @expected_digests.nil?
+ @digests = nil
+ elsif @digests
+ @digests = @digests.slice(*@expected_digests.keys)
+ else
+ initialize_digests(@expected_digests.keys)
+ end
+ end
+
+ # remove this method when we stop generating md5 digests for legacy etags
+ def md5
+ @digests && @digests["md5"]
+ end
+
+ def digests?
+ @digests&.any?
+ end
+
+ # Open the temp file for writing, reusing original permissions, yielding the IO object.
+ def open(write_mode = "wb", perm = @perm, &block)
+ raise ClosedError, "Cannot reopen closed file" if @closed
+ SharedHelpers.filesystem_access(path, :write) do
+ path.open(write_mode, perm) do |f|
+ yield digests? ? Gem::Package::DigestIO.new(f, @digests) : f
+ end
+ end
+ end
+
+ # Returns false without appending when no digests since appending is too error prone to do without digests.
+ def append(data)
+ return false unless digests?
+ open("a") {|f| f.write data }
+ verify && commit
+ end
+
+ def write(data)
+ reset_digests
+ open {|f| f.write data }
+ commit!
+ end
+
+ def commit!
+ verify || raise(DigestMismatchError.new(@base64digests, @expected_digests))
+ commit
+ end
+
+ # Verify the digests, returning true on match, false on mismatch.
+ def verify
+ return true unless @expected_digests && digests?
+ @base64digests = @digests.transform_values!(&:base64digest)
+ @digests = nil
+ @base64digests.all? {|algo, digest| @expected_digests[algo] == digest }
+ end
+
+ # Replace the original file with the temp file without verifying digests.
+ # The file is permanently closed.
+ def commit
+ raise ClosedError, "Cannot commit closed file" if @closed
+ SharedHelpers.filesystem_access(original_path, :write) do
+ FileUtils.mv(path, original_path)
+ end
+ @closed = true
+ end
+
+ # Remove the temp file without replacing the original file.
+ # The file is permanently closed.
+ def close
+ return if @closed
+ FileUtils.remove_file(path) if @path&.file?
+ @closed = true
+ end
+ end
+ end
+end
diff --git a/lib/bundler/compact_index_client/gem_parser.rb b/lib/bundler/compact_index_client/gem_parser.rb
index e7bf4c6001..60a1817607 100644
--- a/lib/bundler/compact_index_client/gem_parser.rb
+++ b/lib/bundler/compact_index_client/gem_parser.rb
@@ -6,12 +6,15 @@ module Bundler
GemParser = Gem::Resolver::APISet::GemParser
else
class GemParser
+ EMPTY_ARRAY = [].freeze
+ private_constant :EMPTY_ARRAY
+
def parse(line)
version_and_platform, rest = line.split(" ", 2)
version, platform = version_and_platform.split("-", 2)
- dependencies, requirements = rest.split("|", 2).map {|s| s.split(",") } if rest
- dependencies = dependencies ? dependencies.map {|d| parse_dependency(d) } : []
- requirements = requirements ? requirements.map {|d| parse_dependency(d) } : []
+ dependencies, requirements = rest.split("|", 2).map! {|s| s.split(",") } if rest
+ dependencies = dependencies ? dependencies.map! {|d| parse_dependency(d) } : EMPTY_ARRAY
+ requirements = requirements ? requirements.map! {|d| parse_dependency(d) } : EMPTY_ARRAY
[version, platform, dependencies, requirements]
end
@@ -20,6 +23,7 @@ module Bundler
def parse_dependency(string)
dependency = string.split(":")
dependency[-1] = dependency[-1].split("&") if dependency.size > 1
+ dependency[0] = -dependency[0]
dependency
end
end
diff --git a/lib/bundler/compact_index_client/updater.rb b/lib/bundler/compact_index_client/updater.rb
index 0f7bf9bb50..36f6b81db8 100644
--- a/lib/bundler/compact_index_client/updater.rb
+++ b/lib/bundler/compact_index_client/updater.rb
@@ -1,20 +1,11 @@
# frozen_string_literal: true
-require_relative "../vendored_fileutils"
-
module Bundler
class CompactIndexClient
class Updater
- class MisMatchedChecksumError < Error
- def initialize(path, server_checksum, local_checksum)
- @path = path
- @server_checksum = server_checksum
- @local_checksum = local_checksum
- end
-
- def message
- "The checksum of /#{@path} does not match the checksum provided by the server! Something is wrong " \
- "(local checksum is #{@local_checksum.inspect}, was expecting #{@server_checksum.inspect})."
+ class MismatchedChecksumError < Error
+ def initialize(path, message)
+ super "The checksum of /#{path} does not match the checksum provided by the server! Something is wrong. #{message}"
end
end
@@ -22,95 +13,102 @@ module Bundler
@fetcher = fetcher
end
- def update(local_path, remote_path, retrying = nil)
- headers = {}
-
- local_temp_path = local_path.sub(/$/, ".#{$$}")
- local_temp_path = local_temp_path.sub(/$/, ".retrying") if retrying
- local_temp_path = local_temp_path.sub(/$/, ".tmp")
-
- # first try to fetch any new bytes on the existing file
- if retrying.nil? && local_path.file?
- copy_file local_path, local_temp_path
+ def update(remote_path, local_path, etag_path)
+ append(remote_path, local_path, etag_path) || replace(remote_path, local_path, etag_path)
+ rescue CacheFile::DigestMismatchError => e
+ raise MismatchedChecksumError.new(remote_path, e.message)
+ rescue Zlib::GzipFile::Error
+ raise Bundler::HTTPError
+ end
- headers["If-None-Match"] = etag_for(local_temp_path)
- headers["Range"] =
- if local_temp_path.size.nonzero?
- # Subtract a byte to ensure the range won't be empty.
- # Avoids 416 (Range Not Satisfiable) responses.
- "bytes=#{local_temp_path.size - 1}-"
- else
- "bytes=#{local_temp_path.size}-"
- end
- end
+ private
- response = @fetcher.call(remote_path, headers)
- return nil if response.is_a?(Net::HTTPNotModified)
+ def append(remote_path, local_path, etag_path)
+ return false unless local_path.file? && local_path.size.nonzero?
- content = response.body
+ CacheFile.copy(local_path) do |file|
+ etag = etag_path.read.tap(&:chomp!) if etag_path.file?
+ etag ||= generate_etag(etag_path, file) # Remove this after 2.5.0 has been out for a while.
- etag = (response["ETag"] || "").gsub(%r{\AW/}, "")
- correct_response = SharedHelpers.filesystem_access(local_temp_path) do
- if response.is_a?(Net::HTTPPartialContent) && local_temp_path.size.nonzero?
- local_temp_path.open("a") {|f| f << slice_body(content, 1..-1) }
+ # Subtract a byte to ensure the range won't be empty.
+ # Avoids 416 (Range Not Satisfiable) responses.
+ response = @fetcher.call(remote_path, request_headers(etag, file.size - 1))
+ break true if response.is_a?(Gem::Net::HTTPNotModified)
- etag_for(local_temp_path) == etag
+ file.digests = parse_digests(response)
+ # server may ignore Range and return the full response
+ if response.is_a?(Gem::Net::HTTPPartialContent)
+ break false unless file.append(response.body.byteslice(1..-1))
else
- local_temp_path.open("wb") {|f| f << content }
-
- etag.length.zero? || etag_for(local_temp_path) == etag
+ file.write(response.body)
end
+ CacheFile.write(etag_path, etag_from_response(response))
+ true
end
+ end
- if correct_response
- SharedHelpers.filesystem_access(local_path) do
- FileUtils.mv(local_temp_path, local_path)
- end
- return nil
- end
+ # request without range header to get the full file or a 304 Not Modified
+ def replace(remote_path, local_path, etag_path)
+ etag = etag_path.read.tap(&:chomp!) if etag_path.file?
+ response = @fetcher.call(remote_path, request_headers(etag))
+ return true if response.is_a?(Gem::Net::HTTPNotModified)
+ CacheFile.write(local_path, response.body, parse_digests(response))
+ CacheFile.write(etag_path, etag_from_response(response))
+ end
- if retrying
- raise MisMatchedChecksumError.new(remote_path, etag, etag_for(local_temp_path))
- end
+ def request_headers(etag, range_start = nil)
+ headers = {}
+ headers["Range"] = "bytes=#{range_start}-" if range_start
+ headers["If-None-Match"] = %("#{etag}") if etag
+ headers
+ end
- update(local_path, remote_path, :retrying)
- rescue Zlib::GzipFile::Error
- raise Bundler::HTTPError
- ensure
- FileUtils.remove_file(local_temp_path) if File.exist?(local_temp_path)
+ def etag_for_request(etag_path)
+ etag_path.read.tap(&:chomp!) if etag_path.file?
end
- def etag_for(path)
- sum = checksum_for_file(path)
- sum ? %("#{sum}") : nil
+ # When first releasing this opaque etag feature, we want to generate the old MD5 etag
+ # based on the content of the file. After that it will always use the saved opaque etag.
+ # This transparently saves existing users with good caches from updating a bunch of files.
+ # Remove this behavior after 2.5.0 has been out for a while.
+ def generate_etag(etag_path, file)
+ etag = file.md5.hexdigest
+ CacheFile.write(etag_path, etag)
+ etag
end
- def slice_body(body, range)
- body.byteslice(range)
+ def etag_from_response(response)
+ return unless response["ETag"]
+ etag = response["ETag"].delete_prefix("W/")
+ return if etag.delete_prefix!('"') && !etag.delete_suffix!('"')
+ etag
end
- def checksum_for_file(path)
- return nil unless path.file?
- # This must use File.read instead of Digest.file().hexdigest
- # because we need to preserve \n line endings on windows when calculating
- # the checksum
- SharedHelpers.filesystem_access(path, :read) do
- SharedHelpers.digest(:MD5).hexdigest(File.read(path))
+ # Unwraps and returns a Hash of digest algorithms and base64 values
+ # according to RFC 8941 Structured Field Values for HTTP.
+ # https://www.rfc-editor.org/rfc/rfc8941#name-parsing-a-byte-sequence
+ # Ignores unsupported algorithms.
+ def parse_digests(response)
+ return unless header = response["Repr-Digest"] || response["Digest"]
+ digests = {}
+ header.split(",") do |param|
+ algorithm, value = param.split("=", 2)
+ algorithm.strip!
+ algorithm.downcase!
+ next unless SUPPORTED_DIGESTS.key?(algorithm)
+ next unless value = byte_sequence(value)
+ digests[algorithm] = value
end
+ digests.empty? ? nil : digests
end
- private
-
- def copy_file(source, dest)
- SharedHelpers.filesystem_access(source, :read) do
- File.open(source, "r") do |s|
- SharedHelpers.filesystem_access(dest, :write) do
- File.open(dest, "wb", s.stat.mode) do |f|
- IO.copy_stream(s, f)
- end
- end
- end
- end
+ # Unwrap surrounding colons (byte sequence)
+ # The wrapping characters must be matched or we return nil.
+ # Also handles quotes because right now rubygems.org sends them.
+ def byte_sequence(value)
+ return if value.delete_prefix!(":") && !value.delete_suffix!(":")
+ return if value.delete_prefix!('"') && !value.delete_suffix!('"')
+ value
end
end
end
diff --git a/lib/bundler/constants.rb b/lib/bundler/constants.rb
index 8dd8a53815..de9698b577 100644
--- a/lib/bundler/constants.rb
+++ b/lib/bundler/constants.rb
@@ -3,5 +3,5 @@
module Bundler
WINDOWS = RbConfig::CONFIG["host_os"] =~ /(msdos|mswin|djgpp|mingw)/
FREEBSD = RbConfig::CONFIG["host_os"].to_s.include?("bsd")
- NULL = WINDOWS ? "NUL" : "/dev/null"
+ NULL = File::NULL
end
diff --git a/lib/bundler/current_ruby.rb b/lib/bundler/current_ruby.rb
index 1d2d8ff2fb..93e0c401c0 100644
--- a/lib/bundler/current_ruby.rb
+++ b/lib/bundler/current_ruby.rb
@@ -43,7 +43,7 @@ module Bundler
].freeze
def ruby?
- return true if Bundler::GemHelpers.generic_local_platform == Gem::Platform::RUBY
+ return true if Bundler::GemHelpers.generic_local_platform_is_ruby?
!windows? && (RUBY_ENGINE == "ruby" || RUBY_ENGINE == "rbx" || RUBY_ENGINE == "maglev" || RUBY_ENGINE == "truffleruby")
end
diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb
index 7ccbc2248d..22070b6b17 100644
--- a/lib/bundler/definition.rb
+++ b/lib/bundler/definition.rb
@@ -18,7 +18,8 @@ module Bundler
:platforms,
:ruby_version,
:lockfile,
- :gemfiles
+ :gemfiles,
+ :locked_checksums
)
# Given a gemfile and lockfile creates a Bundler definition
@@ -76,22 +77,27 @@ module Bundler
@lockfile = lockfile
@lockfile_contents = String.new
+
@locked_bundler_version = nil
- @locked_ruby_version = nil
+ @resolved_bundler_version = nil
+
+ @locked_ruby_version = nil
@new_platform = nil
@removed_platform = nil
- if lockfile && File.exist?(lockfile)
+ if lockfile_exists?
@lockfile_contents = Bundler.read_file(lockfile)
@locked_gems = LockfileParser.new(@lockfile_contents)
@locked_platforms = @locked_gems.platforms
@platforms = @locked_platforms.dup
@locked_bundler_version = @locked_gems.bundler_version
@locked_ruby_version = @locked_gems.ruby_version
+ @originally_locked_deps = @locked_gems.dependencies
@originally_locked_specs = SpecSet.new(@locked_gems.specs)
+ @locked_checksums = @locked_gems.checksums
if unlock != true
- @locked_deps = @locked_gems.dependencies
+ @locked_deps = @originally_locked_deps
@locked_specs = @originally_locked_specs
@locked_sources = @locked_gems.sources
else
@@ -106,9 +112,11 @@ module Bundler
@locked_gems = nil
@locked_deps = {}
@locked_specs = SpecSet.new([])
+ @originally_locked_deps = {}
@originally_locked_specs = @locked_specs
@locked_sources = []
@locked_platforms = []
+ @locked_checksums = nil
end
locked_gem_sources = @locked_sources.select {|s| s.is_a?(Source::Rubygems) }
@@ -124,7 +132,7 @@ module Bundler
@sources.merged_gem_lockfile_sections!(locked_gem_sources.first)
end
- @unlock[:sources] ||= []
+ @sources_to_unlock = @unlock.delete(:sources) || []
@unlock[:ruby] ||= if @ruby_version && locked_ruby_version_object
@ruby_version.diff(locked_ruby_version_object)
end
@@ -136,17 +144,19 @@ module Bundler
@path_changes = converge_paths
@source_changes = converge_sources
+ @explicit_unlocks = @unlock.delete(:gems) || []
+
if @unlock[:conservative]
- @unlock[:gems] ||= @dependencies.map(&:name)
+ @gems_to_unlock = @explicit_unlocks.any? ? @explicit_unlocks : @dependencies.map(&:name)
else
- eager_unlock = (@unlock[:gems] || []).map {|name| Dependency.new(name, ">= 0") }
- @unlock[:gems] = @locked_specs.for(eager_unlock, false, platforms).map(&:name).uniq
+ eager_unlock = @explicit_unlocks.map {|name| Dependency.new(name, ">= 0") }
+ @gems_to_unlock = @locked_specs.for(eager_unlock, false, platforms).map(&:name).uniq
end
@dependency_changes = converge_dependencies
@local_changes = converge_locals
- @incomplete_lockfile = check_missing_lockfile_specs
+ check_lockfile
end
def gem_version_promoter
@@ -219,7 +229,6 @@ module Bundler
@resolver = nil
@resolution_packages = nil
@specs = nil
- @gem_version_promoter = nil
Bundler.ui.debug "The definition is missing dependencies, failed to resolve & materialize locally (#{e})"
true
@@ -234,8 +243,17 @@ module Bundler
end
def current_dependencies
+ filter_relevant(dependencies)
+ end
+
+ def current_locked_dependencies
+ filter_relevant(locked_dependencies)
+ end
+
+ def filter_relevant(dependencies)
+ platforms_array = [generic_local_platform].freeze
dependencies.select do |d|
- d.should_include? && !d.gem_platforms([generic_local_platform]).empty?
+ d.should_include? && !d.gem_platforms(platforms_array).empty?
end
end
@@ -259,9 +277,15 @@ module Bundler
def dependencies_for(groups)
groups.map!(&:to_sym)
- current_dependencies.reject do |d|
- (d.groups & groups).empty?
+ deps = current_dependencies # always returns a new array
+ deps.select! do |d|
+ if RUBY_VERSION >= "3.1"
+ d.groups.intersect?(groups)
+ else
+ !(d.groups & groups).empty?
+ end
end
+ deps
end
# Resolve all the dependencies specified in Gemfile. It ensures that
@@ -273,7 +297,7 @@ module Bundler
@resolve ||= if Bundler.frozen_bundle?
Bundler.ui.debug "Frozen, using resolution from the lockfile"
@locked_specs
- elsif !unlocking? && nothing_changed?
+ elsif no_resolve_needed?
if deleted_deps.any?
Bundler.ui.debug "Some dependencies were deleted, using a subset of the resolution from the lockfile"
SpecSet.new(filter_specs(@locked_specs, @dependencies - deleted_deps))
@@ -299,34 +323,26 @@ module Bundler
dependencies.map(&:groups).flatten.uniq
end
- def lock(file, preserve_unknown_sections = false)
- return if Definition.no_lock
-
- contents = to_lock
-
- # Convert to \r\n if the existing lock has them
- # i.e., Windows with `git config core.autocrlf=true`
- contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match?("\r\n")
-
- if @locked_bundler_version
- locked_major = @locked_bundler_version.segments.first
- current_major = Bundler.gem_version.segments.first
-
- updating_major = locked_major < current_major
- end
+ def lock(file_or_preserve_unknown_sections = false, preserve_unknown_sections_or_unused = false)
+ if [true, false, nil].include?(file_or_preserve_unknown_sections)
+ target_lockfile = lockfile || Bundler.default_lockfile
+ preserve_unknown_sections = file_or_preserve_unknown_sections
+ else
+ target_lockfile = file_or_preserve_unknown_sections
+ preserve_unknown_sections = preserve_unknown_sections_or_unused
- preserve_unknown_sections ||= !updating_major && (Bundler.frozen_bundle? || !(unlocking? || @unlocking_bundler))
+ suggestion = if target_lockfile == lockfile
+ "To fix this warning, remove it from the `Definition#lock` call."
+ else
+ "Instead, instantiate a new definition passing `#{target_lockfile}`, and call `lock` without a file argument on that definition"
+ end
- return if file && File.exist?(file) && lockfiles_equal?(@lockfile_contents, contents, preserve_unknown_sections)
+ msg = "`Definition#lock` was passed a target file argument. #{suggestion}"
- if Bundler.frozen_bundle?
- Bundler.ui.error "Cannot write a changed lockfile while frozen."
- return
+ Bundler::SharedHelpers.major_deprecation 2, msg
end
- SharedHelpers.filesystem_access(file) do |p|
- File.open(p, "wb") {|f| f.puts(contents) }
- end
+ write_lock(target_lockfile, preserve_unknown_sections)
end
def locked_ruby_version
@@ -350,25 +366,16 @@ module Bundler
end
end
+ def bundler_version_to_lock
+ @resolved_bundler_version || Bundler.gem_version
+ end
+
def to_lock
require_relative "lockfile_generator"
LockfileGenerator.generate(self)
end
def ensure_equivalent_gemfile_and_lockfile(explicit_flag = false)
- msg = String.new
- msg << "You are trying to install in deployment mode after changing\n" \
- "your Gemfile. Run `bundle install` elsewhere and add the\n" \
- "updated #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} to version control."
-
- unless explicit_flag
- suggested_command = unless Bundler.settings.locations("frozen").keys.include?(:env)
- "bundle config set frozen false"
- end
- msg << "\n\nIf this is a development machine, remove the #{Bundler.default_gemfile} " \
- "freeze \nby running `#{suggested_command}`." if suggested_command
- end
-
added = []
deleted = []
changed = []
@@ -382,32 +389,36 @@ module Bundler
deleted.concat deleted_deps.map {|d| "* #{pretty_dep(d)}" } if deleted_deps.any?
both_sources = Hash.new {|h, k| h[k] = [] }
- @dependencies.each {|d| both_sources[d.name][0] = d }
-
- locked_dependencies.each do |d|
- next if !Bundler.feature_flag.bundler_3_mode? && @locked_specs[d.name].empty?
-
- both_sources[d.name][1] = d
- end
+ current_dependencies.each {|d| both_sources[d.name][0] = d }
+ current_locked_dependencies.each {|d| both_sources[d.name][1] = d }
both_sources.each do |name, (dep, lock_dep)|
next if dep.nil? || lock_dep.nil?
- gemfile_source = dep.source || sources.default_source
- lock_source = lock_dep.source || sources.default_source
+ gemfile_source = dep.source || default_source
+ lock_source = lock_dep.source || default_source
next if lock_source.include?(gemfile_source)
- gemfile_source_name = dep.source ? gemfile_source.identifier : "no specified source"
- lockfile_source_name = lock_dep.source ? lock_source.identifier : "no specified source"
+ gemfile_source_name = dep.source ? gemfile_source.to_gemfile : "no specified source"
+ lockfile_source_name = lock_dep.source ? lock_source.to_gemfile : "no specified source"
changed << "* #{name} from `#{lockfile_source_name}` to `#{gemfile_source_name}`"
end
reason = change_reason
- msg << "\n\n#{reason.split(", ").map(&:capitalize).join("\n")}" unless reason.strip.empty?
+ msg = String.new
+ msg << "#{reason.capitalize.strip}, but the lockfile can't be updated because frozen mode is set"
msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any?
msg << "\n\nYou have deleted from the Gemfile:\n" << deleted.join("\n") if deleted.any?
msg << "\n\nYou have changed in the Gemfile:\n" << changed.join("\n") if changed.any?
- msg << "\n"
+ msg << "\n\nRun `bundle install` elsewhere and add the updated #{SharedHelpers.relative_gemfile_path} to version control.\n"
+
+ unless explicit_flag
+ suggested_command = unless Bundler.settings.locations("frozen").keys.include?(:env)
+ "bundle config set frozen false"
+ end
+ msg << "If this is a development machine, remove the #{SharedHelpers.relative_lockfile_path} " \
+ "freeze by running `#{suggested_command}`." if suggested_command
+ end
raise ProductionError, msg if added.any? || deleted.any? || changed.any? || !nothing_changed?
end
@@ -446,8 +457,8 @@ module Bundler
return if current_platform_locked?
raise ProductionError, "Your bundle only supports platforms #{@platforms.map(&:to_s)} " \
- "but your local platform is #{Bundler.local_platform}. " \
- "Add the current platform to the lockfile with\n`bundle lock --add-platform #{Bundler.local_platform}` and try again."
+ "but your local platform is #{local_platform}. " \
+ "Add the current platform to the lockfile with\n`bundle lock --add-platform #{local_platform}` and try again."
end
def add_platform(platform)
@@ -472,7 +483,19 @@ module Bundler
private :sources
def nothing_changed?
- !@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes && !@incomplete_lockfile
+ !@source_changes &&
+ !@dependency_changes &&
+ !@new_platform &&
+ !@path_changes &&
+ !@local_changes &&
+ !@missing_lockfile_dep &&
+ !@unlocking_bundler &&
+ !@locked_spec_with_missing_deps &&
+ !@locked_spec_with_invalid_deps
+ end
+
+ def no_resolve_needed?
+ !unlocking? && nothing_changed?
end
def unlocking?
@@ -481,20 +504,75 @@ module Bundler
private
+ def should_add_extra_platforms?
+ !lockfile_exists? && generic_local_platform_is_ruby? && !Bundler.settings[:force_ruby_platform]
+ end
+
+ def lockfile_exists?
+ file_exists?(lockfile)
+ end
+
+ def file_exists?(file)
+ file && File.exist?(file)
+ end
+
+ def write_lock(file, preserve_unknown_sections)
+ return if Definition.no_lock
+
+ contents = to_lock
+
+ # Convert to \r\n if the existing lock has them
+ # i.e., Windows with `git config core.autocrlf=true`
+ contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match?("\r\n")
+
+ if @locked_bundler_version
+ locked_major = @locked_bundler_version.segments.first
+ current_major = bundler_version_to_lock.segments.first
+
+ updating_major = locked_major < current_major
+ end
+
+ preserve_unknown_sections ||= !updating_major && (Bundler.frozen_bundle? || !(unlocking? || @unlocking_bundler))
+
+ if file_exists?(file) && lockfiles_equal?(@lockfile_contents, contents, preserve_unknown_sections)
+ return if Bundler.frozen_bundle?
+ SharedHelpers.filesystem_access(file) { FileUtils.touch(file) }
+ return
+ end
+
+ if Bundler.frozen_bundle?
+ Bundler.ui.error "Cannot write a changed lockfile while frozen."
+ return
+ end
+
+ SharedHelpers.filesystem_access(file) do |p|
+ File.open(p, "wb") {|f| f.puts(contents) }
+ end
+ end
+
def resolver
@resolver ||= Resolver.new(resolution_packages, gem_version_promoter)
end
def expanded_dependencies
- dependencies + metadata_dependencies
+ dependencies_with_bundler + metadata_dependencies
+ end
+
+ def dependencies_with_bundler
+ return dependencies unless @unlocking_bundler
+ return dependencies if dependencies.map(&:name).include?("bundler")
+
+ [Dependency.new("bundler", @unlocking_bundler)] + dependencies
end
def resolution_packages
@resolution_packages ||= begin
last_resolve = converge_locked_specs
- remove_ruby_from_platforms_if_necessary!(current_dependencies)
- packages = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, :locked_specs => @originally_locked_specs, :unlock => @unlock[:gems], :prerelease => gem_version_promoter.pre?)
- additional_base_requirements_for_resolve(packages, last_resolve)
+ remove_invalid_platforms!(current_dependencies)
+ packages = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: @gems_to_unlock, prerelease: gem_version_promoter.pre?)
+ packages = additional_base_requirements_to_prevent_downgrades(packages, last_resolve)
+ packages = additional_base_requirements_to_force_updates(packages)
+ packages
end
end
@@ -550,9 +628,14 @@ module Bundler
end
def start_resolution
- result = resolver.start
+ result = SpecSet.new(resolver.start)
- SpecSet.new(SpecSet.new(result).for(dependencies, false, @platforms))
+ @resolved_bundler_version = result.find {|spec| spec.name == "bundler" }&.version
+ @platforms = result.add_extra_platforms!(platforms) if should_add_extra_platforms?
+
+ result.complete_platforms!(platforms)
+
+ SpecSet.new(result.for(dependencies, false, @platforms))
end
def precompute_source_requirements_for_indirect_dependencies?
@@ -573,7 +656,7 @@ module Bundler
end
def current_ruby_platform_locked?
- return false unless generic_local_platform == Gem::Platform::RUBY
+ return false unless generic_local_platform_is_ruby?
return false if Bundler.settings[:force_ruby_platform] && !@platforms.include?(Gem::Platform::RUBY)
current_platform_locked?
@@ -581,7 +664,7 @@ module Bundler
def current_platform_locked?
@platforms.any? do |bundle_platform|
- MatchPlatform.platforms_match?(bundle_platform, Bundler.local_platform)
+ MatchPlatform.platforms_match?(bundle_platform, local_platform)
end
end
@@ -593,14 +676,18 @@ module Bundler
def change_reason
if unlocking?
- unlock_reason = @unlock.reject {|_k, v| Array(v).empty? }.map do |k, v|
- if v == true
- k.to_s
- else
- v = Array(v)
- "#{k}: (#{v.join(", ")})"
- end
- end.join(", ")
+ unlock_targets = if @gems_to_unlock.any?
+ ["gems", @gems_to_unlock]
+ elsif @sources_to_unlock.any?
+ ["sources", @sources_to_unlock]
+ end
+
+ unlock_reason = if unlock_targets
+ "#{unlock_targets.first}: (#{unlock_targets.last.join(", ")})"
+ else
+ @unlock[:ruby] ? "ruby" : ""
+ end
+
return "bundler is unlocking #{unlock_reason}"
end
[
@@ -609,7 +696,10 @@ module Bundler
[@new_platform, "you added a new platform to your gemfile"],
[@path_changes, "the gemspecs for path gems changed"],
[@local_changes, "the gemspecs for git local gems changed"],
- [@incomplete_lockfile, "your lock file is missing some gems"],
+ [@missing_lockfile_dep, "your lock file is missing \"#{@missing_lockfile_dep}\""],
+ [@unlocking_bundler, "an update to the version of Bundler itself was requested"],
+ [@locked_spec_with_missing_deps, "your lock file includes \"#{@locked_spec_with_missing_deps}\" but not some of its dependencies"],
+ [@locked_spec_with_invalid_deps, "your lockfile does not satisfy dependencies of \"#{@locked_spec_with_invalid_deps}\""],
].select(&:first).map(&:last).join(", ")
end
@@ -636,8 +726,7 @@ module Bundler
locked_index = Index.new
locked_index.use(@locked_specs.select {|s| source.can_lock?(s) })
- # order here matters, since Index#== is checking source.specs.include?(locked_index)
- locked_index != source.specs
+ !locked_index.subset?(source.specs)
rescue PathError, GitError => e
Bundler.ui.debug "Assuming that #{source} has not changed since fetching its specs errored (#{e})"
false
@@ -653,7 +742,7 @@ module Bundler
spec = @dependencies.find {|s| s.name == k }
source = spec&.source
if source&.respond_to?(:local_override!)
- source.unlock! if @unlock[:gems].include?(spec.name)
+ source.unlock! if @gems_to_unlock.include?(spec.name)
locals << [source, source.local_override!(v)]
end
end
@@ -661,22 +750,39 @@ module Bundler
sources_with_changes = locals.select do |source, changed|
changed || specs_changed?(source)
end.map(&:first)
- !sources_with_changes.each {|source| @unlock[:sources] << source.name }.empty?
+ !sources_with_changes.each {|source| @sources_to_unlock << source.name }.empty?
end
- def check_missing_lockfile_specs
- all_locked_specs = @locked_specs.map(&:name) << "bundler"
+ def check_lockfile
+ @missing_lockfile_dep = nil
+
+ @locked_spec_with_invalid_deps = nil
+ @locked_spec_with_missing_deps = nil
+
+ missing = []
+ invalid = []
+
+ @locked_specs.each do |s|
+ validation = @locked_specs.validate_deps(s)
- missing = @locked_specs.select do |s|
- s.dependencies.any? {|dep| !all_locked_specs.include?(dep.name) }
+ missing << s if validation == :missing
+ invalid << s if validation == :invalid
end
if missing.any?
@locked_specs.delete(missing)
- true
- else
- false
+ @locked_spec_with_missing_deps = missing.first.name
+ elsif !@dependency_changes
+ @missing_lockfile_dep = current_dependencies.find do |d|
+ @locked_specs[d.name].empty? && d.name != "bundler"
+ end&.name
+ end
+
+ if invalid.any?
+ @locked_specs.delete(invalid)
+
+ @locked_spec_with_invalid_deps = invalid.first.name
end
end
@@ -711,12 +817,17 @@ module Bundler
changes = sources.replace_sources!(@locked_sources)
sources.all_sources.each do |source|
+ # has to be done separately, because we want to keep the locked checksum
+ # store for a source, even when doing a full update
+ if @locked_checksums && @locked_gems && locked_source = @locked_gems.sources.find {|s| s == source && !s.equal?(source) }
+ source.checksum_store.merge!(locked_source.checksum_store)
+ end
# If the source is unlockable and the current command allows an unlock of
# the source (for example, you are doing a `bundle update <foo>` of a git-pinned
# gem), unlock it. For git sources, this means to unlock the revision, which
# will cause the `ref` used to be the most recent for the branch (or master) if
# an explicit `ref` is not used.
- if source.respond_to?(:unlock!) && @unlock[:sources].include?(source.name)
+ if source.respond_to?(:unlock!) && @sources_to_unlock.include?(source.name)
source.unlock!
changes = true
end
@@ -733,9 +844,7 @@ module Bundler
dep.source = sources.get(dep.source)
end
- next if unlocking?
-
- unless locked_dep = @locked_deps[dep.name]
+ unless locked_dep = @originally_locked_deps[dep.name]
changes = true
next
end
@@ -762,7 +871,7 @@ module Bundler
def converge_locked_specs
converged = converge_specs(@locked_specs)
- resolve = SpecSet.new(converged.reject {|s| @unlock[:gems].include?(s.name) })
+ resolve = SpecSet.new(converged.reject {|s| @gems_to_unlock.include?(s.name) })
diff = nil
@@ -781,37 +890,38 @@ module Bundler
def converge_specs(specs)
converged = []
-
- deps = @dependencies.select do |dep|
- specs[dep].any? {|s| s.satisfies?(dep) && (!dep.source || s.source.include?(dep.source)) }
- end
+ deps = []
@specs_that_changed_sources = []
specs.each do |s|
+ name = s.name
dep = @dependencies.find {|d| s.satisfies?(d) }
+ lockfile_source = s.source
- # Replace the locked dependency's source with the equivalent source from the Gemfile
- s.source = if dep&.source
- gemfile_source = dep.source
- lockfile_source = s.source
+ if dep
+ gemfile_source = dep.source || default_source
@specs_that_changed_sources << s if gemfile_source != lockfile_source
+ deps << dep if !dep.source || lockfile_source.include?(dep.source)
+ @gems_to_unlock << name if lockfile_source.include?(dep.source) && lockfile_source != gemfile_source
- gemfile_source
+ # Replace the locked dependency's source with the equivalent source from the Gemfile
+ s.source = gemfile_source
else
- sources.get_with_fallback(s.source)
+ # Replace the locked dependency's source with the default source, if the locked source is no longer in the Gemfile
+ s.source = default_source unless sources.get(lockfile_source)
end
- next if @unlock[:sources].include?(s.source.name)
+ next if @sources_to_unlock.include?(s.source.name)
# Path sources have special logic
if s.source.instance_of?(Source::Path) || s.source.instance_of?(Source::Gemspec)
new_specs = begin
s.source.specs
- rescue PathError, GitError
+ rescue PathError
# if we won't need the source (according to the lockfile),
- # don't error if the path/git source isn't available
+ # don't error if the path source isn't available
next if specs.
for(requested_dependencies, false).
none? {|locked_spec| locked_spec.source == s.source }
@@ -825,12 +935,12 @@ module Bundler
else
# If the spec is no longer in the path source, unlock it. This
# commonly happens if the version changed in the gemspec
- @unlock[:gems] << s.name
+ @gems_to_unlock << name
end
end
- if dep.nil? && requested_dependencies.find {|d| s.name == d.name }
- @unlock[:gems] << s.name
+ if dep.nil? && requested_dependencies.find {|d| name == d.name }
+ @gems_to_unlock << s.name
else
converged << s
end
@@ -841,7 +951,7 @@ module Bundler
def metadata_dependencies
@metadata_dependencies ||= [
- Dependency.new("Ruby\0", Gem.ruby_version),
+ Dependency.new("Ruby\0", Bundler::RubyVersion.system.gem_version),
Dependency.new("RubyGems\0", Gem::VERSION),
]
end
@@ -853,20 +963,32 @@ module Bundler
source_requirements = if precompute_source_requirements_for_indirect_dependencies?
all_requirements = source_map.all_requirements
all_requirements = pin_locally_available_names(all_requirements) if @prefer_local
- { :default => sources.default_source }.merge(all_requirements)
+ { default: default_source }.merge(all_requirements)
else
- { :default => Source::RubygemsAggregate.new(sources, source_map) }.merge(source_map.direct_requirements)
+ { default: Source::RubygemsAggregate.new(sources, source_map) }.merge(source_map.direct_requirements)
end
source_requirements.merge!(source_map.locked_requirements) unless @remote
metadata_dependencies.each do |dep|
source_requirements[dep.name] = sources.metadata_source
end
- source_requirements[:default_bundler] = source_requirements["bundler"] || sources.default_source
- source_requirements["bundler"] = sources.metadata_source # needs to come last to override
+
+ default_bundler_source = source_requirements["bundler"] || default_source
+
+ if @unlocking_bundler
+ default_bundler_source.add_dependency_names("bundler")
+ else
+ source_requirements[:default_bundler] = default_bundler_source
+ source_requirements["bundler"] = sources.metadata_source # needs to come last to override
+ end
+
verify_changed_sources!
source_requirements
end
+ def default_source
+ sources.default_source
+ end
+
def verify_changed_sources!
@specs_that_changed_sources.each do |s|
if s.source.specs.search(s.name).empty?
@@ -895,7 +1017,7 @@ module Bundler
current == proposed
end
- def additional_base_requirements_for_resolve(resolution_packages, last_resolve)
+ def additional_base_requirements_to_prevent_downgrades(resolution_packages, last_resolve)
return resolution_packages unless @locked_gems && !sources.expired_sources?(@locked_gems.sources)
converge_specs(@originally_locked_specs - last_resolve).each do |locked_spec|
next if locked_spec.source.is_a?(Source::Path)
@@ -904,16 +1026,41 @@ module Bundler
resolution_packages
end
- def remove_ruby_from_platforms_if_necessary!(dependencies)
- return if Bundler.frozen_bundle? ||
- Bundler.local_platform == Gem::Platform::RUBY ||
- !platforms.include?(Gem::Platform::RUBY) ||
- (@new_platform && platforms.last == Gem::Platform::RUBY) ||
+ def additional_base_requirements_to_force_updates(resolution_packages)
+ return resolution_packages if @explicit_unlocks.empty?
+ full_update = dup_for_full_unlock.resolve
+ @explicit_unlocks.each do |name|
+ version = full_update[name].first&.version
+ resolution_packages.base_requirements[name] = Gem::Requirement.new("= #{version}") if version
+ end
+ resolution_packages
+ end
+
+ def dup_for_full_unlock
+ unlocked_definition = self.class.new(@lockfile, @dependencies, @sources, true, @ruby_version, @optional_groups, @gemfiles)
+ unlocked_definition.resolution_mode = { "local" => !@remote }
+ unlocked_definition.setup_sources_for_resolve
+ unlocked_definition.gem_version_promoter.tap do |gvp|
+ gvp.level = gem_version_promoter.level
+ gvp.strict = gem_version_promoter.strict
+ gvp.pre = gem_version_promoter.pre
+ end
+ unlocked_definition
+ end
+
+ def remove_invalid_platforms!(dependencies)
+ return if Bundler.frozen_bundle?
+
+ platforms.reverse_each do |platform|
+ next if local_platform == platform ||
+ (@new_platform && platforms.last == platform) ||
+ @path_changes ||
@dependency_changes ||
- !@originally_locked_specs.incomplete_ruby_specs?(dependencies)
+ !@originally_locked_specs.incomplete_for_platform?(dependencies, platform)
- remove_platform(Gem::Platform::RUBY)
- add_current_platform
+ remove_platform(platform)
+ add_current_platform if platform == Gem::Platform::RUBY
+ end
end
def source_map
diff --git a/lib/bundler/dependency.rb b/lib/bundler/dependency.rb
index 21a4564dcc..2a4f72fe55 100644
--- a/lib/bundler/dependency.rb
+++ b/lib/bundler/dependency.rb
@@ -7,21 +7,21 @@ require_relative "rubygems_ext"
module Bundler
class Dependency < Gem::Dependency
attr_reader :autorequire
- attr_reader :groups, :platforms, :gemfile, :path, :git, :github, :branch, :ref
+ attr_reader :groups, :platforms, :gemfile, :path, :git, :github, :branch, :ref, :glob
- ALL_RUBY_VERSIONS = ((18..27).to_a + (30..33).to_a).freeze
+ ALL_RUBY_VERSIONS = (18..27).to_a.concat((30..34).to_a).freeze
PLATFORM_MAP = {
- :ruby => [Gem::Platform::RUBY, ALL_RUBY_VERSIONS],
- :mri => [Gem::Platform::RUBY, ALL_RUBY_VERSIONS],
- :rbx => [Gem::Platform::RUBY],
- :truffleruby => [Gem::Platform::RUBY],
- :jruby => [Gem::Platform::JAVA, [18, 19]],
- :windows => [Gem::Platform::WINDOWS, ALL_RUBY_VERSIONS],
+ ruby: [Gem::Platform::RUBY, ALL_RUBY_VERSIONS],
+ mri: [Gem::Platform::RUBY, ALL_RUBY_VERSIONS],
+ rbx: [Gem::Platform::RUBY],
+ truffleruby: [Gem::Platform::RUBY],
+ jruby: [Gem::Platform::JAVA, [18, 19]],
+ windows: [Gem::Platform::WINDOWS, ALL_RUBY_VERSIONS],
# deprecated
- :mswin => [Gem::Platform::MSWIN, ALL_RUBY_VERSIONS],
- :mswin64 => [Gem::Platform::MSWIN64, ALL_RUBY_VERSIONS - [18]],
- :mingw => [Gem::Platform::MINGW, ALL_RUBY_VERSIONS],
- :x64_mingw => [Gem::Platform::X64_MINGW, ALL_RUBY_VERSIONS - [18, 19]],
+ mswin: [Gem::Platform::MSWIN, ALL_RUBY_VERSIONS],
+ mswin64: [Gem::Platform::MSWIN64, ALL_RUBY_VERSIONS - [18]],
+ mingw: [Gem::Platform::MINGW, ALL_RUBY_VERSIONS],
+ x64_mingw: [Gem::Platform::X64_MINGW, ALL_RUBY_VERSIONS - [18, 19]],
}.each_with_object({}) do |(platform, spec), hash|
hash[platform] = spec[0]
spec[1]&.each {|version| hash[:"#{platform}_#{version}"] = spec[0] }
@@ -39,6 +39,7 @@ module Bundler
@github = options["github"]
@branch = options["branch"]
@ref = options["ref"]
+ @glob = options["glob"]
@platforms = Array(options["platforms"])
@env = options["env"]
@should_include = options.fetch("should_include", true)
@@ -48,10 +49,13 @@ module Bundler
@autorequire = Array(options["require"] || []) if options.key?("require")
end
+ RUBY_PLATFORM_ARRAY = [Gem::Platform::RUBY].freeze
+ private_constant :RUBY_PLATFORM_ARRAY
+
# Returns the platforms this dependency is valid for, in the same order as
# passed in the `valid_platforms` parameter
def gem_platforms(valid_platforms)
- return [Gem::Platform::RUBY] if force_ruby_platform
+ return RUBY_PLATFORM_ARRAY if force_ruby_platform
return valid_platforms if @platforms.empty?
valid_platforms.select {|p| expanded_platforms.include?(GemHelpers.generic(p)) }
@@ -65,6 +69,10 @@ module Bundler
@should_include && current_env? && current_platform?
end
+ def gemspec_dev_dep?
+ type == :development
+ end
+
def current_env?
return true unless @env
if @env.is_a?(Hash)
diff --git a/lib/bundler/digest.rb b/lib/bundler/digest.rb
index 148e9f7788..2c6d971f1b 100644
--- a/lib/bundler/digest.rb
+++ b/lib/bundler/digest.rb
@@ -50,7 +50,7 @@ module Bundler
words.map!.with_index {|word, index| SHA1_MASK & (word + mutated[index]) }
end
- words.pack("N*").unpack("H*").first
+ words.pack("N*").unpack1("H*")
end
private
diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb
index 03c80a408c..6af80fb31f 100644
--- a/lib/bundler/dsl.rb
+++ b/lib/bundler/dsl.rb
@@ -18,9 +18,10 @@ module Bundler
VALID_KEYS = %w[group groups git path glob name branch ref tag require submodules
platform platforms type source install_if gemfile force_ruby_platform].freeze
- GITHUB_PULL_REQUEST_URL = %r{\Ahttps://github\.com/([A-Za-z0-9_\-\.]+/[A-Za-z0-9_\-\.]+)/pull/(\d+)\z}.freeze
+ GITHUB_PULL_REQUEST_URL = %r{\Ahttps://github\.com/([A-Za-z0-9_\-\.]+/[A-Za-z0-9_\-\.]+)/pull/(\d+)\z}
+ GITLAB_MERGE_REQUEST_URL = %r{\Ahttps://gitlab\.com/([A-Za-z0-9_\-\./]+)/-/merge_requests/(\d+)\z}
- attr_reader :gemspecs
+ attr_reader :gemspecs, :gemfile
attr_accessor :dependencies
def initialize
@@ -46,7 +47,7 @@ module Bundler
@gemfile = expanded_gemfile_path
@gemfiles << expanded_gemfile_path
contents ||= Bundler.read_file(@gemfile.to_s)
- instance_eval(contents.dup.tap {|x| x.untaint if RUBY_VERSION < "2.7" }, gemfile.to_s, 1)
+ instance_eval(contents, @gemfile.to_s, 1)
rescue Exception => e # rubocop:disable Lint/RescueException
message = "There was an error " \
"#{e.is_a?(GemfileEvalError) ? "evaluating" : "parsing"} " \
@@ -76,11 +77,11 @@ module Bundler
@gemspecs << spec
- gem spec.name, :name => spec.name, :path => path, :glob => glob
+ gem spec.name, name: spec.name, path: path, glob: glob
group(development_group) do
spec.development_dependencies.each do |dep|
- gem dep.name, *(dep.requirement.as_list + [:type => :development])
+ gem dep.name, *(dep.requirement.as_list + [type: :development])
end
end
when 0
@@ -102,39 +103,51 @@ module Bundler
# if there's already a dependency with this name we try to prefer one
if current = @dependencies.find {|d| d.name == dep.name }
- deleted_dep = @dependencies.delete(current) if current.type == :development
+ if current.requirement != dep.requirement
+ current_requirement_open = current.requirements_list.include?(">= 0")
- unless deleted_dep
- if current.requirement != dep.requirement
- return if dep.type == :development
+ gemspec_dep = [dep, current].find(&:gemspec_dev_dep?)
+ if gemspec_dep
+ gemfile_dep = [dep, current].find(&:runtime?)
+ unless current_requirement_open
+ Bundler.ui.warn "A gemspec development dependency (#{gemspec_dep.name}, #{gemspec_dep.requirement}) is being overridden by a Gemfile dependency (#{gemfile_dep.name}, #{gemfile_dep.requirement}).\n" \
+ "This behaviour may change in the future. Please remove either of them, or make sure they both have the same requirement\n"
+ end
+ else
update_prompt = ""
if File.basename(@gemfile) == Injector::INJECTED_GEMS
- if dep.requirements_list.include?(">= 0") && !current.requirements_list.include?(">= 0")
+ if dep.requirements_list.include?(">= 0") && !current_requirement_open
update_prompt = ". Gem already added"
else
update_prompt = ". If you want to update the gem version, run `bundle update #{current.name}`"
- update_prompt += ". You may also need to change the version requirement specified in the Gemfile if it's too restrictive." unless current.requirements_list.include?(">= 0")
+ update_prompt += ". You may also need to change the version requirement specified in the Gemfile if it's too restrictive." unless current_requirement_open
end
end
raise GemfileError, "You cannot specify the same gem twice with different version requirements.\n" \
- "You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})" \
- "#{update_prompt}"
- elsif current.source != dep.source
- return if dep.type == :development
- raise GemfileError, "You cannot specify the same gem twice coming from different sources.\n" \
- "You specified that #{dep.name} (#{dep.requirement}) should come from " \
- "#{current.source || "an unspecified source"} and #{dep.source}\n"
- else
- Bundler.ui.warn "Your Gemfile lists the gem #{current.name} (#{current.requirement}) more than once.\n" \
- "You should probably keep only one of them.\n" \
- "Remove any duplicate entries and specify the gem only once.\n" \
- "While it's not a problem now, it could cause errors if you change the version of one of them later."
+ "You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})" \
+ "#{update_prompt}"
end
end
+
+ # Always prefer the dependency from the Gemfile
+ if current.gemspec_dev_dep?
+ @dependencies.delete(current)
+ elsif dep.gemspec_dev_dep?
+ return
+ elsif current.source != dep.source
+ raise GemfileError, "You cannot specify the same gem twice coming from different sources.\n" \
+ "You specified that #{dep.name} (#{dep.requirement}) should come from " \
+ "#{current.source || "an unspecified source"} and #{dep.source}\n"
+ else
+ Bundler.ui.warn "Your Gemfile lists the gem #{current.name} (#{current.requirement}) more than once.\n" \
+ "You should probably keep only one of them.\n" \
+ "Remove any duplicate entries and specify the gem only once.\n" \
+ "While it's not a problem now, it could cause errors if you change the version of one of them later."
+ end
end
@dependencies << dep
@@ -296,6 +309,20 @@ module Bundler
repo_name ||= user_name
"https://#{user_name}@bitbucket.org/#{user_name}/#{repo_name}.git"
end
+
+ git_source(:gitlab) do |repo_name|
+ if repo_name =~ GITLAB_MERGE_REQUEST_URL
+ {
+ "git" => "https://gitlab.com/#{$1}.git",
+ "branch" => nil,
+ "ref" => "refs/merge-requests/#{$2}/head",
+ "tag" => nil,
+ }
+ else
+ repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
+ "https://gitlab.com/#{repo_name}.git"
+ end
+ end
end
def with_source(source)
@@ -397,13 +424,11 @@ module Bundler
end
def validate_keys(command, opts, valid_keys)
- invalid_keys = opts.keys - valid_keys
-
- git_source = opts.keys & @git_sources.keys.map(&:to_s)
- if opts["branch"] && !(opts["git"] || opts["github"] || git_source.any?)
+ if opts["branch"] && !(opts["git"] || opts["github"] || (opts.keys & @git_sources.keys.map(&:to_s)).any?)
raise GemfileError, %(The `branch` option for `#{command}` is not allowed. Only gems with a git source can specify a branch)
end
+ invalid_keys = opts.keys - valid_keys
return true unless invalid_keys.any?
message = String.new
@@ -422,9 +447,13 @@ module Bundler
def normalize_source(source)
case source
when :gemcutter, :rubygems, :rubyforge
- Bundler::SharedHelpers.major_deprecation 2, "The source :#{source} is deprecated because HTTP " \
- "requests are insecure.\nPlease change your source to 'https://" \
- "rubygems.org' if possible, or 'http://rubygems.org' if not."
+ message =
+ "The source :#{source} is deprecated because HTTP requests are insecure.\n" \
+ "Please change your source to 'https://rubygems.org' if possible, or 'http://rubygems.org' if not."
+ removed_message =
+ "The source :#{source} is disallowed because HTTP requests are insecure.\n" \
+ "Please change your source to 'https://rubygems.org' if possible, or 'http://rubygems.org' if not."
+ Bundler::SharedHelpers.major_deprecation 2, message, removed_message: removed_message
"http://rubygems.org"
when String
source
@@ -469,10 +498,17 @@ module Bundler
"should come from that source"
raise GemfileEvalError, msg
else
- Bundler::SharedHelpers.major_deprecation 2, "Your Gemfile contains multiple global sources. " \
+ message =
+ "Your Gemfile contains multiple global sources. " \
"Using `source` more than once without a block is a security risk, and " \
"may result in installing unexpected gems. To resolve this warning, use " \
"a block to indicate which gems should come from the secondary source."
+ removed_message =
+ "Your Gemfile contains multiple global sources. " \
+ "Using `source` more than once without a block is a security risk, and " \
+ "may result in installing unexpected gems. To resolve this error, use " \
+ "a block to indicate which gems should come from the secondary source."
+ Bundler::SharedHelpers.major_deprecation 2, message, removed_message: removed_message
end
end
diff --git a/lib/bundler/endpoint_specification.rb b/lib/bundler/endpoint_specification.rb
index 863544b1f9..87cb352efa 100644
--- a/lib/bundler/endpoint_specification.rb
+++ b/lib/bundler/endpoint_specification.rb
@@ -94,7 +94,7 @@ module Bundler
def _local_specification
return unless @loaded_from && File.exist?(local_specification_path)
- eval(File.read(local_specification_path)).tap do |spec|
+ eval(File.read(local_specification_path), nil, local_specification_path).tap do |spec|
spec.loaded_from = @loaded_from
end
end
@@ -125,7 +125,11 @@ module Bundler
next unless v
case k.to_s
when "checksum"
- @checksum = v.last
+ begin
+ @checksum = Checksum.from_api(v.last, @spec_fetcher.uri)
+ rescue ArgumentError => e
+ raise ArgumentError, "Invalid checksum for #{full_name}: #{e.message}"
+ end
when "rubygems"
@required_rubygems_version = Gem::Requirement.new(v)
when "ruby"
diff --git a/lib/bundler/env.rb b/lib/bundler/env.rb
index 4f8b6f605d..f6cb198e38 100644
--- a/lib/bundler/env.rb
+++ b/lib/bundler/env.rb
@@ -40,11 +40,11 @@ module Bundler
out << "\n## Gemfile\n"
gemfiles.each do |gemfile|
- out << "\n### #{Pathname.new(gemfile).relative_path_from(SharedHelpers.pwd)}\n\n"
+ out << "\n### #{SharedHelpers.relative_path_to(gemfile)}\n\n"
out << "```ruby\n" << read_file(gemfile).chomp << "\n```\n"
end
- out << "\n### #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}\n\n"
+ out << "\n### #{SharedHelpers.relative_path_to(Bundler.default_lockfile)}\n\n"
out << "```\n" << read_file(Bundler.default_lockfile).chomp << "\n```\n"
end
diff --git a/lib/bundler/environment_preserver.rb b/lib/bundler/environment_preserver.rb
index 57013f5d50..444ab6fd37 100644
--- a/lib/bundler/environment_preserver.rb
+++ b/lib/bundler/environment_preserver.rb
@@ -19,14 +19,7 @@ module Bundler
BUNDLER_PREFIX = "BUNDLER_ORIG_"
def self.from_env
- new(env_to_hash(ENV), BUNDLER_KEYS)
- end
-
- def self.env_to_hash(env)
- to_hash = env.to_hash
- return to_hash unless Gem.win_platform?
-
- to_hash.each_with_object({}) {|(k,v), a| a[k.upcase] = v }
+ new(ENV.to_hash, BUNDLER_KEYS)
end
# @param env [Hash]
@@ -39,18 +32,7 @@ module Bundler
# Replaces `ENV` with the bundler environment variables backed up
def replace_with_backup
- unless Gem.win_platform?
- ENV.replace(backup)
- return
- end
-
- # Fallback logic for Windows below to workaround
- # https://bugs.ruby-lang.org/issues/16798. Can be dropped once all
- # supported rubies include the fix for that.
-
- ENV.clear
-
- backup.each {|k, v| ENV[k] = v }
+ ENV.replace(backup)
end
# @return [Hash]
@@ -58,9 +40,9 @@ module Bundler
env = @original.clone
@keys.each do |key|
value = env[key]
- if !value.nil? && !value.empty?
+ if !value.nil?
env[@prefix + key] ||= value
- elsif value.nil?
+ else
env[@prefix + key] ||= INTENTIONALLY_NIL
end
end
@@ -72,7 +54,7 @@ module Bundler
env = @original.clone
@keys.each do |key|
value_original = env[@prefix + key]
- next if value_original.nil? || value_original.empty?
+ next if value_original.nil?
if value_original == INTENTIONALLY_NIL
env.delete(key)
else
diff --git a/lib/bundler/errors.rb b/lib/bundler/errors.rb
index 5839fc6a73..b6a11cc721 100644
--- a/lib/bundler/errors.rb
+++ b/lib/bundler/errors.rb
@@ -52,6 +52,49 @@ module Bundler
class GemfileEvalError < GemfileError; end
class MarshalError < StandardError; end
+ class ChecksumMismatchError < SecurityError
+ def initialize(lock_name, existing, checksum)
+ @lock_name = lock_name
+ @existing = existing
+ @checksum = checksum
+ end
+
+ def message
+ <<~MESSAGE
+ Bundler found mismatched checksums. This is a potential security risk.
+ #{@lock_name} #{@existing.to_lock}
+ from #{@existing.sources.join("\n and ")}
+ #{@lock_name} #{@checksum.to_lock}
+ from #{@checksum.sources.join("\n and ")}
+
+ #{mismatch_resolution_instructions}
+ To ignore checksum security warnings, disable checksum validation with
+ `bundle config set --local disable_checksum_validation true`
+ MESSAGE
+ end
+
+ def mismatch_resolution_instructions
+ removable, remote = [@existing, @checksum].partition(&:removable?)
+ case removable.size
+ when 0
+ msg = +"Mismatched checksums each have an authoritative source:\n"
+ msg << " 1. #{@existing.sources.reject(&:removable?).map(&:to_s).join(" and ")}\n"
+ msg << " 2. #{@checksum.sources.reject(&:removable?).map(&:to_s).join(" and ")}\n"
+ msg << "You may need to alter your Gemfile sources to resolve this issue.\n"
+ when 1
+ msg = +"If you trust #{remote.first.sources.first}, to resolve this issue you can:\n"
+ msg << removable.first.removal_instructions
+ when 2
+ msg = +"To resolve this issue you can either:\n"
+ msg << @checksum.removal_instructions
+ msg << "or if you are sure that the new checksum from #{@checksum.sources.first} is correct:\n"
+ msg << @existing.removal_instructions
+ end
+ end
+
+ status_code(37)
+ end
+
class PermissionError < BundlerError
def initialize(path, permission_type = :write)
@path = path
@@ -172,4 +215,19 @@ module Bundler
status_code(36)
end
+
+ class InsecureInstallPathError < BundlerError
+ def initialize(path)
+ @path = path
+ end
+
+ def message
+ "The installation path is insecure. Bundler cannot continue.\n" \
+ "#{@path} is world-writable (without sticky bit).\n" \
+ "Bundler cannot safely replace gems in world-writeable directories due to potential vulnerabilities.\n" \
+ "Please change the permissions of this directory or choose a different install path."
+ end
+
+ status_code(38)
+ end
end
diff --git a/lib/bundler/feature_flag.rb b/lib/bundler/feature_flag.rb
index 983de3137c..ab2189f7f0 100644
--- a/lib/bundler/feature_flag.rb
+++ b/lib/bundler/feature_flag.rb
@@ -37,7 +37,6 @@ module Bundler
settings_flag(:plugins) { @bundler_version >= Gem::Version.new("1.14") }
settings_flag(:print_only_version_number) { bundler_3_mode? }
settings_flag(:setup_makes_kernel_gem_public) { !bundler_3_mode? }
- settings_flag(:suppress_install_using_messages) { bundler_3_mode? }
settings_flag(:update_requires_all_flag) { bundler_4_mode? }
settings_option(:default_cli_command) { bundler_3_mode? ? :cli_help : :install }
diff --git a/lib/bundler/fetcher.rb b/lib/bundler/fetcher.rb
index e12c15af8a..6288b22dcd 100644
--- a/lib/bundler/fetcher.rb
+++ b/lib/bundler/fetcher.rb
@@ -1,14 +1,15 @@
# frozen_string_literal: true
require_relative "vendored_persistent"
+require_relative "vendored_timeout"
require "cgi"
require "securerandom"
require "zlib"
-require "rubygems/request"
module Bundler
# Handles all the fetching with the rubygems server
class Fetcher
+ autoload :Base, File.expand_path("fetcher/base", __dir__)
autoload :CompactIndex, File.expand_path("fetcher/compact_index", __dir__)
autoload :Downloader, File.expand_path("fetcher/downloader", __dir__)
autoload :Dependency, File.expand_path("fetcher/dependency", __dir__)
@@ -61,6 +62,16 @@ module Bundler
end
end
+ # This error is raised if HTTP authentication is correct, but lacks
+ # necessary permissions.
+ class AuthenticationForbiddenError < HTTPError
+ def initialize(remote_uri)
+ remote_uri = filter_uri(remote_uri)
+ super "Access token could not be authenticated for #{remote_uri}.\n" \
+ "Make sure it's valid and has the necessary scopes configured."
+ end
+ end
+
# Exceptions classes that should bypass retry attempts. If your password didn't work the
# first time, it's not going to the third time.
NET_ERRORS = [:HTTPBadGateway, :HTTPBadRequest, :HTTPFailedDependency,
@@ -70,9 +81,9 @@ module Bundler
:HTTPRequestURITooLong, :HTTPUnauthorized, :HTTPUnprocessableEntity,
:HTTPUnsupportedMediaType, :HTTPVersionNotSupported].freeze
FAIL_ERRORS = begin
- fail_errors = [AuthenticationRequiredError, BadAuthenticationError, FallbackError]
+ fail_errors = [AuthenticationRequiredError, BadAuthenticationError, AuthenticationForbiddenError, FallbackError, SecurityError]
fail_errors << Gem::Requirement::BadRequirementError
- fail_errors.concat(NET_ERRORS.map {|e| Net.const_get(e) })
+ fail_errors.concat(NET_ERRORS.map {|e| Gem::Net.const_get(e) })
end.freeze
class << self
@@ -84,6 +95,7 @@ module Bundler
self.max_retries = Bundler.settings[:retry] # How many retries for the API call
def initialize(remote)
+ @cis = nil
@remote = remote
Socket.do_not_reverse_lookup = true
@@ -99,15 +111,17 @@ module Bundler
spec -= [nil, "ruby", ""]
spec_file_name = "#{spec.join "-"}.gemspec"
- uri = Bundler::URI.parse("#{remote_uri}#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}.rz")
- if uri.scheme == "file"
- path = Bundler.rubygems.correct_for_windows_path(uri.path)
+ uri = Gem::URI.parse("#{remote_uri}#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}.rz")
+ spec = if uri.scheme == "file"
+ path = Gem::Util.correct_for_windows_path(uri.path)
Bundler.safe_load_marshal Bundler.rubygems.inflate(Gem.read_binary(path))
elsif cached_spec_path = gemspec_cached_path(spec_file_name)
Bundler.load_gemspec(cached_spec_path)
else
Bundler.safe_load_marshal Bundler.rubygems.inflate(downloader.fetch(uri).body)
end
+ raise MarshalError, "is #{spec.inspect}" unless spec.is_a?(Gem::Specification)
+ spec
rescue MarshalError
raise HTTPError, "Gemspec #{spec} contained invalid data.\n" \
"Your network or your gem server is probably having issues right now."
@@ -124,20 +138,11 @@ module Bundler
def specs(gem_names, source)
index = Bundler::Index.new
- if Bundler::Fetcher.disable_endpoint
- @use_api = false
- specs = fetchers.last.specs(gem_names)
- else
- specs = []
- @fetchers = fetchers.drop_while do |f|
- !f.available? || (f.api_fetcher? && !gem_names) || !specs = f.specs(gem_names)
- end
- @use_api = false if fetchers.none?(&:api_fetcher?)
- end
-
- specs.each do |name, version, platform, dependencies, metadata|
+ fetch_specs(gem_names).each do |name, version, platform, dependencies, metadata|
spec = if dependencies
- EndpointSpecification.new(name, version, platform, self, dependencies, metadata)
+ EndpointSpecification.new(name, version, platform, self, dependencies, metadata).tap do |es|
+ source.checksum_store.replace(es, es.checksum)
+ end
else
RemoteSpecification.new(name, version, platform, self)
end
@@ -148,22 +153,10 @@ module Bundler
index
rescue CertificateFailureError
- Bundler.ui.info "" if gem_names && use_api # newline after dots
+ Bundler.ui.info "" if gem_names && api_fetcher? # newline after dots
raise
end
- def use_api
- return @use_api if defined?(@use_api)
-
- fetchers.shift until fetchers.first.available?
-
- @use_api = if remote_uri.scheme == "file" || Bundler::Fetcher.disable_endpoint
- false
- else
- fetchers.first.api_fetcher?
- end
- end
-
def user_agent
@user_agent ||= begin
ruby = Bundler::RubyVersion.system
@@ -199,10 +192,6 @@ module Bundler
end
end
- def fetchers
- @fetchers ||= FETCHERS.map {|f| f.new(downloader, @remote, uri) }
- end
-
def http_proxy
return unless uri = connection.proxy_uri
uri.to_s
@@ -212,25 +201,49 @@ module Bundler
"#<#{self.class}:0x#{object_id} uri=#{uri}>"
end
+ def api_fetcher?
+ fetchers.first.api_fetcher?
+ end
+
+ def gem_remote_fetcher
+ @gem_remote_fetcher ||= begin
+ require_relative "fetcher/gem_remote_fetcher"
+ fetcher = GemRemoteFetcher.new Gem.configuration[:http_proxy]
+ fetcher.headers["User-Agent"] = user_agent
+ fetcher.headers["X-Gemfile-Source"] = @remote.original_uri.to_s if @remote.original_uri
+ fetcher
+ end
+ end
+
private
- FETCHERS = [CompactIndex, Dependency, Index].freeze
+ def available_fetchers
+ if Bundler::Fetcher.disable_endpoint
+ [Index]
+ elsif remote_uri.scheme == "file"
+ Bundler.ui.debug("Using a local server, bundler won't use the CompactIndex API")
+ [Index]
+ else
+ [CompactIndex, Dependency, Index]
+ end
+ end
+
+ def fetchers
+ @fetchers ||= available_fetchers.map {|f| f.new(downloader, @remote, uri, gem_remote_fetcher) }.drop_while {|f| !f.available? }
+ end
+
+ def fetch_specs(gem_names)
+ fetchers.reject!(&:api_fetcher?) unless gem_names
+ fetchers.reject! do |f|
+ specs = f.specs(gem_names)
+ return specs if specs
+ true
+ end
+ []
+ end
def cis
- env_cis = {
- "TRAVIS" => "travis",
- "CIRCLECI" => "circle",
- "SEMAPHORE" => "semaphore",
- "JENKINS_URL" => "jenkins",
- "BUILDBOX" => "buildbox",
- "GO_SERVER_URL" => "go",
- "SNAP_CI" => "snap",
- "GITLAB_CI" => "gitlab",
- "GITHUB_ACTIONS" => "github",
- "CI_NAME" => ENV["CI_NAME"],
- "CI" => "ci",
- }
- env_cis.find_all {|env, _| ENV[env] }.map {|_, ci| ci }
+ @cis ||= Bundler::CIDetector.ci_strings
end
def connection
@@ -240,9 +253,9 @@ module Bundler
Bundler.settings[:ssl_client_cert]
raise SSLError if needs_ssl && !defined?(OpenSSL::SSL)
- con = PersistentHTTP.new :name => "bundler", :proxy => :ENV
+ con = Gem::Net::HTTP::Persistent.new name: "bundler", proxy: :ENV
if gem_proxy = Gem.configuration[:http_proxy]
- con.proxy = Bundler::URI.parse(gem_proxy) if gem_proxy != :no_proxy
+ con.proxy = Gem::URI.parse(gem_proxy) if gem_proxy != :no_proxy
end
if remote_uri.scheme == "https"
@@ -275,10 +288,10 @@ module Bundler
end
HTTP_ERRORS = [
- Timeout::Error, EOFError, SocketError, Errno::ENETDOWN, Errno::ENETUNREACH,
+ Gem::Timeout::Error, EOFError, SocketError, Errno::ENETDOWN, Errno::ENETUNREACH,
Errno::EINVAL, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EAGAIN,
- Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError,
- PersistentHTTP::Error, Zlib::BufError, Errno::EHOSTUNREACH
+ Gem::Net::HTTPBadResponse, Gem::Net::HTTPHeaderSyntaxError, Gem::Net::ProtocolError,
+ Gem::Net::HTTP::Persistent::Error, Zlib::BufError, Errno::EHOSTUNREACH
].freeze
def bundler_cert_store
@@ -294,6 +307,7 @@ module Bundler
end
else
store.set_default_paths
+ require "rubygems/request"
Gem::Request.get_cert_files.each {|c| store.add_file c }
end
store
diff --git a/lib/bundler/fetcher/base.rb b/lib/bundler/fetcher/base.rb
index 62cc75add8..cfec2f8e94 100644
--- a/lib/bundler/fetcher/base.rb
+++ b/lib/bundler/fetcher/base.rb
@@ -6,12 +6,14 @@ module Bundler
attr_reader :downloader
attr_reader :display_uri
attr_reader :remote
+ attr_reader :gem_remote_fetcher
- def initialize(downloader, remote, display_uri)
+ def initialize(downloader, remote, display_uri, gem_remote_fetcher)
raise "Abstract class" if self.class == Base
@downloader = downloader
@remote = remote
@display_uri = display_uri
+ @gem_remote_fetcher = gem_remote_fetcher
end
def remote_uri
@@ -38,9 +40,9 @@ module Bundler
private
- def log_specs(debug_msg)
+ def log_specs(&block)
if Bundler.ui.debug?
- Bundler.ui.debug debug_msg
+ Bundler.ui.debug yield
else
Bundler.ui.info ".", false
end
diff --git a/lib/bundler/fetcher/compact_index.rb b/lib/bundler/fetcher/compact_index.rb
index 674d2b49f1..db914839b1 100644
--- a/lib/bundler/fetcher/compact_index.rb
+++ b/lib/bundler/fetcher/compact_index.rb
@@ -13,9 +13,9 @@ module Bundler
undef_method(method_name)
define_method(method_name) do |*args, &blk|
method.bind(self).call(*args, &blk)
- rescue NetworkDownError, CompactIndexClient::Updater::MisMatchedChecksumError => e
+ rescue NetworkDownError, CompactIndexClient::Updater::MismatchedChecksumError => e
raise HTTPError, e.message
- rescue AuthenticationRequiredError
+ rescue AuthenticationRequiredError, BadAuthenticationError
# Fail since we got a 401 from the server.
raise
rescue HTTPError => e
@@ -35,7 +35,7 @@ module Bundler
remaining_gems = gem_names.dup
until remaining_gems.empty?
- log_specs "Looking up gems #{remaining_gems.inspect}"
+ log_specs { "Looking up gems #{remaining_gems.inspect}" }
deps = begin
parallel_compact_index_client.dependencies(remaining_gems)
@@ -44,7 +44,7 @@ module Bundler
@bundle_worker = nil # reset it. Not sure if necessary
serial_compact_index_client.dependencies(remaining_gems)
end
- next_gems = deps.map {|d| d[3].map(&:first).flatten(1) }.flatten(1).uniq
+ next_gems = deps.flat_map {|d| d[3].flat_map(&:first) }.uniq
deps.each {|dep| gem_info << dep }
complete_gems.concat(deps.map(&:first)).uniq!
remaining_gems = next_gems - complete_gems
@@ -60,13 +60,9 @@ module Bundler
Bundler.ui.debug("FIPS mode is enabled, bundler can't use the CompactIndex API")
return nil
end
- if fetch_uri.scheme == "file"
- Bundler.ui.debug("Using a local server, bundler won't use the CompactIndex API")
- return false
- end
# Read info file checksums out of /versions, so we can know if gems are up to date
compact_index_client.update_and_parse_checksums!
- rescue CompactIndexClient::Updater::MisMatchedChecksumError => e
+ rescue CompactIndexClient::Updater::MismatchedChecksumError => e
Bundler.ui.debug(e.message)
nil
end
@@ -125,7 +121,7 @@ module Bundler
rescue NetworkDownError => e
raise unless Bundler.feature_flag.allow_offline_install? && headers["If-None-Match"]
ui.warn "Using the cached data for the new index because of a network error: #{e}"
- Net::HTTPNotModified.new(nil, nil, nil)
+ Gem::Net::HTTPNotModified.new(nil, nil, nil)
end
end
end
diff --git a/lib/bundler/fetcher/dependency.rb b/lib/bundler/fetcher/dependency.rb
index 18b606abb6..0b807c9a4b 100644
--- a/lib/bundler/fetcher/dependency.rb
+++ b/lib/bundler/fetcher/dependency.rb
@@ -24,7 +24,7 @@ module Bundler
def specs(gem_names, full_dependency_list = [], last_spec_list = [])
query_list = gem_names.uniq - full_dependency_list
- log_specs "Query List: #{query_list.inspect}"
+ log_specs { "Query List: #{query_list.inspect}" }
return last_spec_list if query_list.empty?
diff --git a/lib/bundler/fetcher/downloader.rb b/lib/bundler/fetcher/downloader.rb
index 28d33f1aed..868b39b959 100644
--- a/lib/bundler/fetcher/downloader.rb
+++ b/lib/bundler/fetcher/downloader.rb
@@ -20,31 +20,35 @@ module Bundler
Bundler.ui.debug("HTTP #{response.code} #{response.message} #{filtered_uri}")
case response
- when Net::HTTPSuccess, Net::HTTPNotModified
+ when Gem::Net::HTTPSuccess, Gem::Net::HTTPNotModified
response
- when Net::HTTPRedirection
- new_uri = Bundler::URI.parse(response["location"])
+ when Gem::Net::HTTPRedirection
+ new_uri = Gem::URI.parse(response["location"])
if new_uri.host == uri.host
new_uri.user = uri.user
new_uri.password = uri.password
end
fetch(new_uri, headers, counter + 1)
- when Net::HTTPRequestedRangeNotSatisfiable
+ when Gem::Net::HTTPRequestedRangeNotSatisfiable
new_headers = headers.dup
new_headers.delete("Range")
new_headers["Accept-Encoding"] = "gzip"
fetch(uri, new_headers)
- when Net::HTTPRequestEntityTooLarge
+ when Gem::Net::HTTPRequestEntityTooLarge
raise FallbackError, response.body
- when Net::HTTPTooManyRequests
+ when Gem::Net::HTTPTooManyRequests
raise TooManyRequestsError, response.body
- when Net::HTTPUnauthorized
+ when Gem::Net::HTTPUnauthorized
raise BadAuthenticationError, uri.host if uri.userinfo
raise AuthenticationRequiredError, uri.host
- when Net::HTTPNotFound
- raise FallbackError, "Net::HTTPNotFound: #{filtered_uri}"
+ when Gem::Net::HTTPForbidden
+ raise AuthenticationForbiddenError, uri.host
+ when Gem::Net::HTTPNotFound
+ raise FallbackError, "Gem::Net::HTTPNotFound: #{filtered_uri}"
else
- raise HTTPError, "#{response.class}#{": #{response.body}" unless response.body.empty?}"
+ message = "Gem::#{response.class.name.gsub(/\AGem::/, "")}"
+ message += ": #{response.body}" unless response.body.empty?
+ raise HTTPError, message
end
end
@@ -54,7 +58,7 @@ module Bundler
filtered_uri = URICredentialsFilter.credential_filtered_uri(uri)
Bundler.ui.debug "HTTP GET #{filtered_uri}"
- req = Net::HTTP::Get.new uri.request_uri, headers
+ req = Gem::Net::HTTP::Get.new uri.request_uri, headers
if uri.user
user = CGI.unescape(uri.user)
password = uri.password ? CGI.unescape(uri.password) : nil
diff --git a/lib/bundler/fetcher/gem_remote_fetcher.rb b/lib/bundler/fetcher/gem_remote_fetcher.rb
new file mode 100644
index 0000000000..3fc7b68263
--- /dev/null
+++ b/lib/bundler/fetcher/gem_remote_fetcher.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require "rubygems/remote_fetcher"
+
+module Bundler
+ class Fetcher
+ class GemRemoteFetcher < Gem::RemoteFetcher
+ def request(*args)
+ super do |req|
+ req.delete("User-Agent") if headers["User-Agent"]
+ yield req if block_given?
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/fetcher/index.rb b/lib/bundler/fetcher/index.rb
index 6bb9fcc193..6e37e1e5d1 100644
--- a/lib/bundler/fetcher/index.rb
+++ b/lib/bundler/fetcher/index.rb
@@ -6,7 +6,7 @@ module Bundler
class Fetcher
class Index < Base
def specs(_gem_names)
- Bundler.rubygems.fetch_all_remote_specs(remote)
+ Bundler.rubygems.fetch_all_remote_specs(remote, gem_remote_fetcher)
rescue Gem::RemoteFetcher::FetchError => e
case e.message
when /certificate verify failed/
@@ -15,8 +15,7 @@ module Bundler
raise BadAuthenticationError, remote_uri if remote_uri.userinfo
raise AuthenticationRequiredError, remote_uri
when /403/
- raise BadAuthenticationError, remote_uri if remote_uri.userinfo
- raise AuthenticationRequiredError, remote_uri
+ raise AuthenticationForbiddenError, remote_uri
else
raise HTTPError, "Could not fetch specs from #{display_uri} due to underlying error <#{e.message}>"
end
diff --git a/lib/bundler/friendly_errors.rb b/lib/bundler/friendly_errors.rb
index b66a046d37..e61ed64450 100644
--- a/lib/bundler/friendly_errors.rb
+++ b/lib/bundler/friendly_errors.rb
@@ -32,7 +32,7 @@ module Bundler
if Bundler.ui.debug?
Bundler.ui.trace error
else
- Bundler.ui.error error.message, :wrap => true
+ Bundler.ui.error error.message, wrap: true
end
when Thor::Error
Bundler.ui.error error.message
@@ -40,7 +40,7 @@ module Bundler
Bundler.ui.error "\nQuitting..."
Bundler.ui.trace error
when Gem::InvalidSpecificationException
- Bundler.ui.error error.message, :wrap => true
+ Bundler.ui.error error.message, wrap: true
when SystemExit
when *[defined?(Java::JavaLang::OutOfMemoryError) && Java::JavaLang::OutOfMemoryError].compact
Bundler.ui.error "\nYour JVM has run out of memory, and Bundler cannot continue. " \
diff --git a/lib/bundler/gem_helper.rb b/lib/bundler/gem_helper.rb
index dcf759cded..d535d54f9b 100644
--- a/lib/bundler/gem_helper.rb
+++ b/lib/bundler/gem_helper.rb
@@ -215,7 +215,7 @@ module Bundler
def sh_with_status(cmd, &block)
Bundler.ui.debug(cmd)
SharedHelpers.chdir(base) do
- outbuf = IO.popen(cmd, :err => [:child, :out], &:read)
+ outbuf = IO.popen(cmd, err: [:child, :out], &:read)
status = $?
block&.call(outbuf) if status.success?
[outbuf, status]
diff --git a/lib/bundler/gem_helpers.rb b/lib/bundler/gem_helpers.rb
index 2e6d788f9c..de007523ec 100644
--- a/lib/bundler/gem_helpers.rb
+++ b/lib/bundler/gem_helpers.rb
@@ -34,6 +34,11 @@ module Bundler
end
module_function :local_platform
+ def generic_local_platform_is_ruby?
+ generic_local_platform == Gem::Platform::RUBY
+ end
+ module_function :generic_local_platform_is_ruby?
+
def platform_specificity_match(spec_platform, user_platform)
spec_platform = Gem::Platform.new(spec_platform)
@@ -48,6 +53,13 @@ module Bundler
end
module_function :select_best_platform_match
+ def force_ruby_platform(specs)
+ matching = specs.select {|spec| spec.match_platform(Gem::Platform::RUBY) && spec.force_ruby_platform! }
+
+ sort_best_platform_match(matching, Gem::Platform::RUBY)
+ end
+ module_function :force_ruby_platform
+
def sort_best_platform_match(matching, platform)
exact = matching.select {|spec| spec.platform == platform }
return exact if exact.any?
@@ -107,8 +119,6 @@ module Bundler
def same_deps(spec, exemplary_spec)
same_runtime_deps = spec.dependencies.sort == exemplary_spec.dependencies.sort
- return same_runtime_deps unless spec.is_a?(Gem::Specification) && exemplary_spec.is_a?(Gem::Specification)
-
same_metadata_deps = spec.required_ruby_version == exemplary_spec.required_ruby_version && spec.required_rubygems_version == exemplary_spec.required_rubygems_version
same_runtime_deps && same_metadata_deps
end
diff --git a/lib/bundler/gem_version_promoter.rb b/lib/bundler/gem_version_promoter.rb
index d281f46eeb..ecc65b4956 100644
--- a/lib/bundler/gem_version_promoter.rb
+++ b/lib/bundler/gem_version_promoter.rb
@@ -45,17 +45,37 @@ module Bundler
# Given a Resolver::Package and an Array of Specifications of available
# versions for a gem, this method will return the Array of Specifications
- # sorted (and possibly truncated if strict is true) in an order to give
- # preference to the current level (:major, :minor or :patch) when resolution
- # is deciding what versions best resolve all dependencies in the bundle.
+ # sorted in an order to give preference to the current level (:major, :minor
+ # or :patch) when resolution is deciding what versions best resolve all
+ # dependencies in the bundle.
# @param package [Resolver::Package] The package being resolved.
# @param specs [Specification] An array of Specifications for the package.
- # @return [Specification] A new instance of the Specification Array sorted and
- # possibly filtered.
+ # @return [Specification] A new instance of the Specification Array sorted.
def sort_versions(package, specs)
- specs = filter_dep_specs(specs, package) if strict
+ locked_version = package.locked_version
+
+ result = specs.sort do |a, b|
+ unless package.prerelease_specified? || pre?
+ a_pre = a.prerelease?
+ b_pre = b.prerelease?
- sort_dep_specs(specs, package)
+ next 1 if a_pre && !b_pre
+ next -1 if b_pre && !a_pre
+ end
+
+ if major? || locked_version.nil?
+ b <=> a
+ elsif either_version_older_than_locked?(a, b, locked_version)
+ b <=> a
+ elsif segments_do_not_match?(a, b, :major)
+ a <=> b
+ elsif !minor? && segments_do_not_match?(a, b, :minor)
+ a <=> b
+ else
+ b <=> a
+ end
+ end
+ post_sort(result, package.unlock?, locked_version)
end
# @return [bool] Convenience method for testing value of level variable.
@@ -73,9 +93,18 @@ module Bundler
pre == true
end
- private
+ # Given a Resolver::Package and an Array of Specifications of available
+ # versions for a gem, this method will truncate the Array if strict
+ # is true. That means filtering out downgrades from the version currently
+ # locked, and filtering out upgrades that go past the selected level (major,
+ # minor, or patch).
+ # @param package [Resolver::Package] The package being resolved.
+ # @param specs [Specification] An array of Specifications for the package.
+ # @return [Specification] A new instance of the Specification Array
+ # truncated.
+ def filter_versions(package, specs)
+ return specs unless strict
- def filter_dep_specs(specs, package)
locked_version = package.locked_version
return specs if locked_version.nil? || major?
@@ -89,35 +118,10 @@ module Bundler
end
end
- def sort_dep_specs(specs, package)
- locked_version = package.locked_version
-
- result = specs.sort do |a, b|
- unless package.prerelease_specified? || pre?
- a_pre = a.prerelease?
- b_pre = b.prerelease?
-
- next -1 if a_pre && !b_pre
- next 1 if b_pre && !a_pre
- end
-
- if major?
- a <=> b
- elsif either_version_older_than_locked?(a, b, locked_version)
- a <=> b
- elsif segments_do_not_match?(a, b, :major)
- b <=> a
- elsif !minor? && segments_do_not_match?(a, b, :minor)
- b <=> a
- else
- a <=> b
- end
- end
- post_sort(result, package.unlock?, locked_version)
- end
+ private
def either_version_older_than_locked?(a, b, locked_version)
- locked_version && (a.version < locked_version || b.version < locked_version)
+ a.version < locked_version || b.version < locked_version
end
def segments_do_not_match?(a, b, level)
@@ -133,13 +137,13 @@ module Bundler
if unlock || locked_version.nil?
result
else
- move_version_to_end(result, locked_version)
+ move_version_to_beginning(result, locked_version)
end
end
- def move_version_to_end(result, version)
+ def move_version_to_beginning(result, version)
move, keep = result.partition {|s| s.version.to_s == version.to_s }
- keep.concat(move)
+ move.concat(keep)
end
end
end
diff --git a/lib/bundler/graph.rb b/lib/bundler/graph.rb
index 3c008e63e3..b22b17a453 100644
--- a/lib/bundler/graph.rb
+++ b/lib/bundler/graph.rb
@@ -84,7 +84,7 @@ module Bundler
else
raise ArgumentError, "2nd argument is invalid"
end
- label.nil? ? {} : { :label => label }
+ label.nil? ? {} : { label: label }
end
def spec_for_dependency(dependency)
@@ -103,7 +103,7 @@ module Bundler
end
def g
- @g ||= ::GraphViz.digraph(@graph_name, :concentrate => true, :normalize => true, :nodesep => 0.55) do |g|
+ @g ||= ::GraphViz.digraph(@graph_name, concentrate: true, normalize: true, nodesep: 0.55) do |g|
g.edge[:weight] = 2
g.edge[:fontname] = g.node[:fontname] = "Arial, Helvetica, SansSerif"
g.edge[:fontsize] = 12
@@ -114,10 +114,10 @@ module Bundler
@groups.each do |group|
g.add_nodes(
group, {
- :style => "filled",
- :fillcolor => "#B9B9D5",
- :shape => "box3d",
- :fontsize => 16,
+ style: "filled",
+ fillcolor: "#B9B9D5",
+ shape: "box3d",
+ fontsize: 16,
}.merge(@node_options[group])
)
end
@@ -125,8 +125,8 @@ module Bundler
@relations.each do |parent, children|
children.each do |child|
if @groups.include?(parent)
- g.add_nodes(child, { :style => "filled", :fillcolor => "#B9B9D5" }.merge(@node_options[child]))
- g.add_edges(parent, child, { :constraint => false }.merge(@edge_options["#{parent}_#{child}"]))
+ g.add_nodes(child, { style: "filled", fillcolor: "#B9B9D5" }.merge(@node_options[child]))
+ g.add_edges(parent, child, { constraint: false }.merge(@edge_options["#{parent}_#{child}"]))
else
g.add_nodes(child, @node_options[child])
g.add_edges(parent, child, @edge_options["#{parent}_#{child}"])
@@ -135,7 +135,7 @@ module Bundler
end
if @output_format.to_s == "debug"
- $stdout.puts g.output :none => String
+ $stdout.puts g.output none: String
Bundler.ui.info "debugging bundle viz..."
else
begin
diff --git a/lib/bundler/index.rb b/lib/bundler/index.rb
index b8c599f63a..df46facc88 100644
--- a/lib/bundler/index.rb
+++ b/lib/bundler/index.rb
@@ -10,8 +10,8 @@ module Bundler
i
end
- attr_reader :specs, :all_specs, :sources
- protected :specs, :all_specs
+ attr_reader :specs, :duplicates, :sources
+ protected :specs, :duplicates
RUBY = "ruby"
NULL = "\0"
@@ -19,21 +19,21 @@ module Bundler
def initialize
@sources = []
@cache = {}
- @specs = Hash.new {|h, k| h[k] = {} }
- @all_specs = Hash.new {|h, k| h[k] = EMPTY_SEARCH }
+ @specs = {}
+ @duplicates = {}
end
def initialize_copy(o)
@sources = o.sources.dup
@cache = {}
- @specs = Hash.new {|h, k| h[k] = {} }
- @all_specs = Hash.new {|h, k| h[k] = EMPTY_SEARCH }
+ @specs = {}
+ @duplicates = {}
o.specs.each do |name, hash|
@specs[name] = hash.dup
end
- o.all_specs.each do |name, array|
- @all_specs[name] = array.dup
+ o.duplicates.each do |name, array|
+ @duplicates[name] = array.dup
end
end
@@ -46,12 +46,11 @@ module Bundler
true
end
- def search_all(name)
- all_matches = local_search(name) + @all_specs[name]
- @sources.each do |source|
- all_matches.concat(source.search_all(name))
- end
- all_matches
+ def search_all(name, &blk)
+ return enum_for(:search_all, name) unless blk
+ specs_by_name(name).each(&blk)
+ @duplicates[name]&.each(&blk)
+ @sources.each {|source| source.search_all(name, &blk) }
end
# Search this index's specs, and any source indexes that this index knows
@@ -61,11 +60,14 @@ module Bundler
return results unless @sources.any?
@sources.each do |source|
- results.concat(source.search(query))
+ results = safe_concat(results, source.search(query))
end
- results.uniq(&:full_name)
+ results.uniq!(&:full_name) unless results.empty? # avoid modifying frozen EMPTY_SEARCH
+ results
end
+ alias_method :[], :search
+
def local_search(query)
case query
when Gem::Specification, RemoteSpecification, LazySpecification, EndpointSpecification then search_by_spec(query)
@@ -76,12 +78,10 @@ module Bundler
end
end
- alias_method :[], :search
-
- def <<(spec)
- @specs[spec.name][spec.full_name] = spec
- spec
+ def add(spec)
+ (@specs[spec.name] ||= {}).store(spec.full_name, spec)
end
+ alias_method :<<, :add
def each(&blk)
return enum_for(:each) unless blk
@@ -115,15 +115,25 @@ module Bundler
names.uniq
end
- def use(other, override_dupes = false)
+ # Combines indexes proritizing existing specs, like `Hash#reverse_merge!`
+ # Duplicate specs found in `other` are stored in `@duplicates`.
+ def use(other)
return unless other
- other.each do |s|
- if (dupes = search_by_spec(s)) && !dupes.empty?
- # safe to << since it's a new array when it has contents
- @all_specs[s.name] = dupes << s
- next unless override_dupes
+ other.each do |spec|
+ exist?(spec) ? add_duplicate(spec) : add(spec)
+ end
+ self
+ end
+
+ # Combines indexes proritizing specs from `other`, like `Hash#merge!`
+ # Duplicate specs found in `self` are saved in `@duplicates`.
+ def merge!(other)
+ return unless other
+ other.each do |spec|
+ if existing = find_by_spec(spec)
+ add_duplicate(existing)
end
- self << s
+ add spec
end
self
end
@@ -135,8 +145,7 @@ module Bundler
end
# Whether all the specs in self are in other
- # TODO: rename to #include?
- def ==(other)
+ def subset?(other)
all? do |spec|
other_spec = other[spec].first
other_spec && dependencies_eql?(spec, other_spec) && spec.source == other_spec.source
@@ -157,19 +166,40 @@ module Bundler
private
+ def safe_concat(a, b)
+ return a if b.empty?
+ return b if a.empty?
+ a.concat(b)
+ end
+
+ def add_duplicate(spec)
+ (@duplicates[spec.name] ||= []) << spec
+ end
+
def specs_by_name_and_version(name, version)
- specs_by_name(name).select {|spec| spec.version == version }
+ results = @specs[name]&.values
+ return EMPTY_SEARCH unless results
+ results.select! {|spec| spec.version == version }
+ results
end
def specs_by_name(name)
- @specs[name].values
+ @specs[name]&.values || EMPTY_SEARCH
end
EMPTY_SEARCH = [].freeze
def search_by_spec(spec)
- spec = @specs[spec.name][spec.full_name]
+ spec = find_by_spec(spec)
spec ? [spec] : EMPTY_SEARCH
end
+
+ def find_by_spec(spec)
+ @specs[spec.name]&.fetch(spec.full_name, nil)
+ end
+
+ def exist?(spec)
+ @specs[spec.name]&.key?(spec.full_name)
+ end
end
end
diff --git a/lib/bundler/injector.rb b/lib/bundler/injector.rb
index cb644a7f69..879b481339 100644
--- a/lib/bundler/injector.rb
+++ b/lib/bundler/injector.rb
@@ -29,7 +29,7 @@ module Bundler
end
# temporarily unfreeze
- Bundler.settings.temporary(:deployment => false, :frozen => false) do
+ Bundler.settings.temporary(deployment: false, frozen: false) do
# evaluate the Gemfile we have now
builder = Dsl.new
builder.eval_gemfile(gemfile_path)
@@ -50,7 +50,7 @@ module Bundler
append_to(gemfile_path, build_gem_lines(@options[:conservative_versioning])) if @deps.any?
# since we resolved successfully, write out the lockfile
- @definition.lock(Bundler.default_lockfile)
+ @definition.lock
# invalidate the cached Bundler.definition
Bundler.reset_paths!
@@ -86,7 +86,7 @@ module Bundler
segments = version.segments
seg_end_index = version >= Gem::Version.new("1.0") ? 1 : 2
- prerelease_suffix = version.to_s.gsub(version.release.to_s, "") if version.prerelease?
+ prerelease_suffix = version.to_s.delete_prefix(version.release.to_s) if version.prerelease?
"#{version_prefix}#{segments[0..seg_end_index].join(".")}#{prerelease_suffix}"
end
@@ -120,9 +120,10 @@ module Bundler
github = ", :github => \"#{d.github}\"" unless d.github.nil?
branch = ", :branch => \"#{d.branch}\"" unless d.branch.nil?
ref = ", :ref => \"#{d.ref}\"" unless d.ref.nil?
+ glob = ", :glob => \"#{d.glob}\"" unless d.glob.nil?
require_path = ", :require => #{convert_autorequire(d.autorequire)}" unless d.autorequire.nil?
- %(gem #{name}#{requirement}#{group}#{source}#{path}#{git}#{github}#{branch}#{ref}#{require_path})
+ %(gem #{name}#{requirement}#{group}#{source}#{path}#{git}#{github}#{branch}#{ref}#{glob}#{require_path})
end.join("\n")
end
diff --git a/lib/bundler/inline.rb b/lib/bundler/inline.rb
index 5c184f67a1..ae4ccf2138 100644
--- a/lib/bundler/inline.rb
+++ b/lib/bundler/inline.rb
@@ -48,14 +48,14 @@ def gemfile(install = false, options = {}, &gemfile)
builder.instance_eval(&gemfile)
builder.check_primary_source_safety
- Bundler.settings.temporary(:deployment => false, :frozen => false) do
+ Bundler.settings.temporary(deployment: false, frozen: false) do
definition = builder.to_definition(nil, true)
def definition.lock(*); end
definition.validate_runtime!
if install || definition.missing_specs?
- Bundler.settings.temporary(:inline => true, :no_install => false) do
- installer = Bundler::Installer.install(Bundler.root, definition, :system => true)
+ Bundler.settings.temporary(inline: true, no_install: false) do
+ installer = Bundler::Installer.install(Bundler.root, definition, system: true)
installer.post_install_messages.each do |name, message|
Bundler.ui.info "Post-install message from #{name}:\n#{message}"
end
diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb
index 59b6a6ad22..72e5602cc3 100644
--- a/lib/bundler/installer.rb
+++ b/lib/bundler/installer.rb
@@ -81,7 +81,7 @@ module Bundler
if resolve_if_needed(options)
ensure_specs_are_compatible!
- load_plugins
+ Bundler.load_plugins(@definition)
options.delete(:jobs)
else
options[:jobs] = 1 # to avoid the overhead of Bundler::Worker
@@ -136,12 +136,12 @@ module Bundler
mode = Gem.win_platform? ? "wb:UTF-8" : "w"
require "erb"
- content = ERB.new(template, :trim_mode => "-").result(binding)
+ content = ERB.new(template, trim_mode: "-").result(binding)
- File.write(binstub_path, content, :mode => mode, :perm => 0o777 & ~File.umask)
+ File.write(binstub_path, content, mode: mode, perm: 0o777 & ~File.umask)
if Gem.win_platform? || options[:all_platforms]
prefix = "@ruby -x \"%~f0\" %*\n@exit /b %ERRORLEVEL%\n\n"
- File.write("#{binstub_path}.cmd", prefix + content, :mode => mode)
+ File.write("#{binstub_path}.cmd", prefix + content, mode: mode)
end
end
@@ -179,12 +179,12 @@ module Bundler
mode = Gem.win_platform? ? "wb:UTF-8" : "w"
require "erb"
- content = ERB.new(template, :trim_mode => "-").result(binding)
+ content = ERB.new(template, trim_mode: "-").result(binding)
- File.write("#{bin_path}/#{executable}", content, :mode => mode, :perm => 0o755)
+ File.write("#{bin_path}/#{executable}", content, mode: mode, perm: 0o755)
if Gem.win_platform? || options[:all_platforms]
prefix = "@ruby -x \"%~f0\" %*\n@exit /b %ERRORLEVEL%\n\n"
- File.write("#{bin_path}/#{executable}.cmd", prefix + content, :mode => mode)
+ File.write("#{bin_path}/#{executable}.cmd", prefix + content, mode: mode)
end
end
end
@@ -213,20 +213,6 @@ module Bundler
Bundler.settings.processor_count
end
- def load_plugins
- Bundler.rubygems.load_plugins
-
- requested_path_gems = @definition.requested_specs.select {|s| s.source.is_a?(Source::Path) }
- path_plugin_files = requested_path_gems.map do |spec|
- Bundler.rubygems.spec_matches_for_glob(spec, "rubygems_plugin#{Bundler.rubygems.suffix_pattern}")
- rescue TypeError
- error_message = "#{spec.name} #{spec.version} has an invalid gemspec"
- raise Gem::InvalidSpecificationException, error_message
- end.flatten
- Bundler.rubygems.load_plugin_files(path_plugin_files)
- Bundler.rubygems.load_env_plugins
- end
-
def ensure_specs_are_compatible!
@definition.specs.each do |spec|
unless spec.matches_current_ruby?
@@ -260,8 +246,8 @@ module Bundler
true
end
- def lock(opts = {})
- @definition.lock(Bundler.default_lockfile, opts[:preserve_unknown_sections])
+ def lock
+ @definition.lock
end
end
end
diff --git a/lib/bundler/installer/gem_installer.rb b/lib/bundler/installer/gem_installer.rb
index c855c7afa0..d3bbcc90f5 100644
--- a/lib/bundler/installer/gem_installer.rb
+++ b/lib/bundler/installer/gem_installer.rb
@@ -17,11 +17,11 @@ module Bundler
Bundler.ui.debug "#{worker}: #{spec.name} (#{spec.version}) from #{spec.loaded_from}"
generate_executable_stubs
[true, post_install_message]
- rescue Bundler::InstallHookError, Bundler::SecurityError, Bundler::APIResponseMismatchError
+ rescue Bundler::InstallHookError, Bundler::SecurityError, Bundler::APIResponseMismatchError, Bundler::InsecureInstallPathError
raise
rescue Errno::ENOSPC
[false, out_of_space_message]
- rescue Bundler::BundlerError, Gem::InstallError, Bundler::APIResponseInvalidDependenciesError => e
+ rescue Bundler::BundlerError, Gem::InstallError => e
[false, specific_failure_message(e)]
end
@@ -53,10 +53,10 @@ module Bundler
def install
spec.source.install(
spec,
- :force => force,
- :ensure_builtin_gems_cached => standalone,
- :build_args => Array(spec_settings),
- :previous_spec => previous_spec,
+ force: force,
+ ensure_builtin_gems_cached: standalone,
+ build_args: Array(spec_settings),
+ previous_spec: previous_spec,
)
end
@@ -77,7 +77,7 @@ module Bundler
if Bundler.settings[:bin] && standalone
installer.generate_standalone_bundler_executable_stubs(spec)
elsif Bundler.settings[:bin]
- installer.generate_bundler_executable_stubs(spec, :force => true)
+ installer.generate_bundler_executable_stubs(spec, force: true)
end
end
end
diff --git a/lib/bundler/installer/parallel_installer.rb b/lib/bundler/installer/parallel_installer.rb
index 83a381f592..e745088f81 100644
--- a/lib/bundler/installer/parallel_installer.rb
+++ b/lib/bundler/installer/parallel_installer.rb
@@ -42,8 +42,7 @@ module Bundler
# Checks installed dependencies against spec's dependencies to make
# sure needed dependencies have been installed.
- def dependencies_installed?(all_specs)
- installed_specs = all_specs.select(&:installed?).map(&:name)
+ def dependencies_installed?(installed_specs)
dependencies.all? {|d| installed_specs.include? d.name }
end
@@ -63,20 +62,23 @@ module Bundler
end
end
- def self.call(*args)
- new(*args).call
+ def self.call(*args, **kwargs)
+ new(*args, **kwargs).call
end
attr_reader :size
- def initialize(installer, all_specs, size, standalone, force)
+ def initialize(installer, all_specs, size, standalone, force, skip: nil)
@installer = installer
@size = size
@standalone = standalone
@force = force
@specs = all_specs.map {|s| SpecInstallation.new(s) }
+ @specs.each do |spec_install|
+ spec_install.state = :installed if skip.include?(spec_install.name)
+ end if skip
@spec_set = all_specs
- @rake = @specs.find {|s| s.name == "rake" }
+ @rake = @specs.find {|s| s.name == "rake" unless s.installed? }
end
def call
@@ -91,38 +93,12 @@ module Bundler
install_serially
end
- check_for_unmet_dependencies
-
handle_error if failed_specs.any?
@specs
ensure
worker_pool&.stop
end
- def check_for_unmet_dependencies
- unmet_dependencies = @specs.map do |s|
- [
- s,
- s.dependencies.reject {|dep| @specs.any? {|spec| dep.matches_spec?(spec.spec) } },
- ]
- end.reject {|a| a.last.empty? }
- return if unmet_dependencies.empty?
-
- warning = []
- warning << "Your lockfile doesn't include a valid resolution."
- warning << "You can fix this by regenerating your lockfile or manually editing the bad locked gems to a version that satisfies all dependencies."
- warning << "The unmet dependencies are:"
-
- unmet_dependencies.each do |spec, unmet_spec_dependencies|
- unmet_spec_dependencies.each do |unmet_spec_dependency|
- found = @specs.find {|s| s.name == unmet_spec_dependency.name && !unmet_spec_dependency.matches_spec?(s.spec) }
- warning << "* #{unmet_spec_dependency}, dependency of #{spec.full_name}, unsatisfied by #{found.full_name}"
- end
- end
-
- Bundler.ui.warn(warning.join("\n"))
- end
-
private
def failed_specs
@@ -209,8 +185,14 @@ module Bundler
# previously installed specifications. We continue until all specs
# are installed.
def enqueue_specs
- @specs.select(&:ready_to_enqueue?).each do |spec|
- if spec.dependencies_installed? @specs
+ installed_specs = {}
+ @specs.each do |spec|
+ next unless spec.installed?
+ installed_specs[spec.name] = true
+ end
+
+ @specs.each do |spec|
+ if spec.ready_to_enqueue? && spec.dependencies_installed?(installed_specs)
spec.state = :enqueued
worker_pool.enq spec
end
diff --git a/lib/bundler/installer/standalone.rb b/lib/bundler/installer/standalone.rb
index 2a8c9a432d..5331df2e95 100644
--- a/lib/bundler/installer/standalone.rb
+++ b/lib/bundler/installer/standalone.rb
@@ -12,6 +12,7 @@ module Bundler
end
File.open File.join(bundler_path, "setup.rb"), "w" do |file|
file.puts "require 'rbconfig'"
+ file.puts prevent_gem_activation
file.puts define_path_helpers
file.puts reverse_rubygems_kernel_mixin
paths.each do |path|
@@ -55,13 +56,26 @@ module Bundler
if spec.source.instance_of?(Source::Path) && spec.source.path.absolute?
full_path
else
- Pathname.new(full_path).relative_path_from(Bundler.root.join(bundler_path)).to_s
+ SharedHelpers.relative_path_to(full_path, from: Bundler.root.join(bundler_path))
end
rescue TypeError
error_message = "#{spec.name} #{spec.version} has an invalid gemspec"
raise Gem::InvalidSpecificationException.new(error_message)
end
+ def prevent_gem_activation
+ <<~'END'
+ module Kernel
+ remove_method(:gem) if private_method_defined?(:gem)
+
+ def gem(*)
+ end
+
+ private :gem
+ end
+ END
+ end
+
def define_path_helpers
<<~'END'
unless defined?(Gem)
@@ -87,8 +101,7 @@ module Bundler
if Gem.respond_to?(:discover_gems_on_require=)
Gem.discover_gems_on_require = false
else
- kernel = (class << ::Kernel; self; end)
- [kernel, ::Kernel].each do |k|
+ [::Kernel.singleton_class, ::Kernel].each do |k|
if k.private_method_defined?(:gem_original_require)
private_require = k.private_method_defined?(:require)
k.send(:remove_method, :require)
diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb
index c9b161dc0e..8669e021c2 100644
--- a/lib/bundler/lazy_specification.rb
+++ b/lib/bundler/lazy_specification.rb
@@ -4,16 +4,29 @@ require_relative "force_platform"
module Bundler
class LazySpecification
+ include MatchMetadata
include MatchPlatform
include ForcePlatform
- attr_reader :name, :version, :dependencies, :platform
- attr_accessor :source, :remote, :force_ruby_platform
+ attr_reader :name, :version, :platform
+ attr_accessor :source, :remote, :force_ruby_platform, :dependencies, :required_ruby_version, :required_rubygems_version
+
+ alias_method :runtime_dependencies, :dependencies
+
+ def self.from_spec(s)
+ lazy_spec = new(s.name, s.version, s.platform, s.source)
+ lazy_spec.dependencies = s.dependencies
+ lazy_spec.required_ruby_version = s.required_ruby_version
+ lazy_spec.required_rubygems_version = s.required_rubygems_version
+ lazy_spec
+ end
def initialize(name, version, platform, source = nil)
@name = name
@version = version
@dependencies = []
+ @required_ruby_version = Gem::Requirement.default
+ @required_rubygems_version = Gem::Requirement.default
@platform = platform || Gem::Platform::RUBY
@source = source
@force_ruby_platform = default_force_ruby_platform
@@ -27,6 +40,14 @@ module Bundler
end
end
+ def lock_name
+ @lock_name ||= name_tuple.lock_name
+ end
+
+ def name_tuple
+ Gem::NameTuple.new(@name, @version, @platform)
+ end
+
def ==(other)
full_name == other.full_name
end
@@ -61,12 +82,7 @@ module Bundler
def to_lock
out = String.new
-
- if platform == Gem::Platform::RUBY
- out << " #{name} (#{version})\n"
- else
- out << " #{name} (#{version}-#{platform})\n"
- end
+ out << " #{lock_name}\n"
dependencies.sort_by(&:to_s).uniq.each do |dep|
next if dep.type == :development
@@ -89,7 +105,7 @@ module Bundler
installable_candidates = GemHelpers.select_best_platform_match(matching_specs, target_platform)
- specification = __materialize__(installable_candidates, :fallback_to_non_installable => false)
+ specification = __materialize__(installable_candidates, fallback_to_non_installable: false)
return specification unless specification.nil?
if target_platform != platform
@@ -109,9 +125,7 @@ module Bundler
# bad gem.
def __materialize__(candidates, fallback_to_non_installable: Bundler.frozen_bundle?)
search = candidates.reverse.find do |spec|
- spec.is_a?(StubSpecification) ||
- (spec.matches_current_ruby? &&
- spec.matches_current_rubygems?)
+ spec.is_a?(StubSpecification) || spec.matches_current_metadata?
end
if search.nil? && fallback_to_non_installable
search = candidates.last
@@ -122,11 +136,7 @@ module Bundler
end
def to_s
- @to_s ||= if platform == Gem::Platform::RUBY
- "#{name} (#{version})"
- else
- "#{name} (#{version}-#{platform})"
- end
+ lock_name
end
def git_version
@@ -134,6 +144,10 @@ module Bundler
" #{source.revision[0..6]}"
end
+ def force_ruby_platform!
+ @force_ruby_platform = true
+ end
+
private
def use_exact_resolved_specifications?
diff --git a/lib/bundler/lockfile_generator.rb b/lib/bundler/lockfile_generator.rb
index a7ee026f67..a646d00ee1 100644
--- a/lib/bundler/lockfile_generator.rb
+++ b/lib/bundler/lockfile_generator.rb
@@ -19,6 +19,7 @@ module Bundler
add_sources
add_platforms
add_dependencies
+ add_checksums
add_locked_ruby_version
add_bundled_with
@@ -65,13 +66,21 @@ module Bundler
end
end
+ def add_checksums
+ return unless definition.locked_checksums
+ checksums = definition.resolve.map do |spec|
+ spec.source.checksum_store.to_lock(spec)
+ end
+ add_section("CHECKSUMS", checksums)
+ end
+
def add_locked_ruby_version
return unless locked_ruby_version = definition.locked_ruby_version
add_section("RUBY VERSION", locked_ruby_version.to_s)
end
def add_bundled_with
- add_section("BUNDLED WITH", Bundler::VERSION)
+ add_section("BUNDLED WITH", definition.bundler_version_to_lock.to_s)
end
def add_section(name, value)
diff --git a/lib/bundler/lockfile_parser.rb b/lib/bundler/lockfile_parser.rb
index 7360a36752..1e11621e55 100644
--- a/lib/bundler/lockfile_parser.rb
+++ b/lib/bundler/lockfile_parser.rb
@@ -2,10 +2,41 @@
module Bundler
class LockfileParser
- attr_reader :sources, :dependencies, :specs, :platforms, :bundler_version, :ruby_version
+ class Position
+ attr_reader :line, :column
+ def initialize(line, column)
+ @line = line
+ @column = column
+ end
+
+ def advance!(string)
+ lines = string.count("\n")
+ if lines > 0
+ @line += lines
+ @column = string.length - string.rindex("\n")
+ else
+ @column += string.length
+ end
+ end
+
+ def to_s
+ "#{line}:#{column}"
+ end
+ end
+
+ attr_reader(
+ :sources,
+ :dependencies,
+ :specs,
+ :platforms,
+ :bundler_version,
+ :ruby_version,
+ :checksums,
+ )
BUNDLED = "BUNDLED WITH"
DEPENDENCIES = "DEPENDENCIES"
+ CHECKSUMS = "CHECKSUMS"
PLATFORMS = "PLATFORMS"
RUBY = "RUBY VERSION"
GIT = "GIT"
@@ -13,7 +44,7 @@ module Bundler
PATH = "PATH"
PLUGIN = "PLUGIN SOURCE"
SPECS = " specs:"
- OPTIONS = /^ ([a-z]+): (.*)$/i.freeze
+ OPTIONS = /^ ([a-z]+): (.*)$/i
SOURCE = [GIT, GEM, PATH, PLUGIN].freeze
SECTIONS_BY_VERSION_INTRODUCED = {
@@ -21,15 +52,18 @@ module Bundler
Gem::Version.create("1.10") => [BUNDLED].freeze,
Gem::Version.create("1.12") => [RUBY].freeze,
Gem::Version.create("1.13") => [PLUGIN].freeze,
+ Gem::Version.create("2.5.0") => [CHECKSUMS].freeze,
}.freeze
- KNOWN_SECTIONS = SECTIONS_BY_VERSION_INTRODUCED.values.flatten.freeze
+ KNOWN_SECTIONS = SECTIONS_BY_VERSION_INTRODUCED.values.flatten!.freeze
ENVIRONMENT_VERSION_SECTIONS = [BUNDLED, RUBY].freeze
deprecate_constant(:ENVIRONMENT_VERSION_SECTIONS)
def self.sections_in_lockfile(lockfile_contents)
- lockfile_contents.scan(/^\w[\w ]*$/).uniq
+ sections = lockfile_contents.scan(/^\w[\w ]*$/)
+ sections.uniq!
+ sections
end
def self.unknown_sections_in_lockfile(lockfile_contents)
@@ -38,7 +72,7 @@ module Bundler
def self.sections_to_ignore(base_version = nil)
base_version &&= base_version.release
- base_version ||= Gem::Version.create("1.0".dup)
+ base_version ||= Gem::Version.create("1.0")
attributes = []
SECTIONS_BY_VERSION_INTRODUCED.each do |version, introduced|
next if version <= base_version
@@ -61,37 +95,53 @@ module Bundler
@platforms = []
@sources = []
@dependencies = {}
- @state = nil
+ @parse_method = nil
@specs = {}
+ @lockfile_path = begin
+ SharedHelpers.relative_lockfile_path
+ rescue GemfileNotFound
+ "Gemfile.lock"
+ end
+ @pos = Position.new(1, 1)
if lockfile.match?(/<<<<<<<|=======|>>>>>>>|\|\|\|\|\|\|\|/)
- raise LockfileError, "Your #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} contains merge conflicts.\n" \
- "Run `git checkout HEAD -- #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}` first to get a clean lock."
+ raise LockfileError, "Your #{@lockfile_path} contains merge conflicts.\n" \
+ "Run `git checkout HEAD -- #{@lockfile_path}` first to get a clean lock."
end
- lockfile.split(/(?:\r?\n)+/).each do |line|
+ lockfile.split(/((?:\r?\n)+)/) do |line|
+ # split alternates between the line and the following whitespace
+ next @pos.advance!(line) if line.match?(/^\s*$/)
+
if SOURCE.include?(line)
- @state = :source
+ @parse_method = :parse_source
parse_source(line)
elsif line == DEPENDENCIES
- @state = :dependency
+ @parse_method = :parse_dependency
+ elsif line == CHECKSUMS
+ # This is a temporary solution to make this feature disabled by default
+ # for all gemfiles that don't already explicitly include the feature.
+ @checksums = true
+ @parse_method = :parse_checksum
elsif line == PLATFORMS
- @state = :platform
+ @parse_method = :parse_platform
elsif line == RUBY
- @state = :ruby
+ @parse_method = :parse_ruby
elsif line == BUNDLED
- @state = :bundled_with
+ @parse_method = :parse_bundled_with
elsif /^[^\s]/.match?(line)
- @state = nil
- elsif @state
- send("parse_#{@state}", line)
+ @parse_method = nil
+ elsif @parse_method
+ send(@parse_method, line)
end
+ @pos.advance!(line)
end
- @specs = @specs.values.sort_by(&:full_name)
+ @specs = @specs.values.sort_by!(&:full_name)
rescue ArgumentError => e
Bundler.ui.debug(e)
- raise LockfileError, "Your lockfile is unreadable. Run `rm #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}` " \
- "and then `bundle install` to generate a new lockfile."
+ raise LockfileError, "Your lockfile is unreadable. Run `rm #{@lockfile_path}` " \
+ "and then `bundle install` to generate a new lockfile. The error occurred while " \
+ "evaluating #{@lockfile_path}:#{@pos}"
end
def may_include_redundant_platform_specific_gems?
@@ -110,21 +160,9 @@ module Bundler
def parse_source(line)
case line
when SPECS
- case @type
- when PATH
- @current_source = TYPES[@type].from_lock(@opts)
- @sources << @current_source
- when GIT
- @current_source = TYPES[@type].from_lock(@opts)
- @sources << @current_source
- when GEM
- @opts["remotes"] = Array(@opts.delete("remote")).reverse
- @current_source = TYPES[@type].from_lock(@opts)
- @sources << @current_source
- when PLUGIN
- @current_source = Plugin.source_from_lock(@opts)
- @sources << @current_source
- end
+ return unless TYPES.key?(@type)
+ @current_source = TYPES[@type].from_lock(@opts)
+ @sources << @current_source
when OPTIONS
value = $2
value = true if value == "true"
@@ -154,18 +192,19 @@ module Bundler
(?:#{space}\(([^-]*) # Space, followed by version
(?:-(.*))?\))? # Optional platform
(!)? # Optional pinned marker
+ (?:#{space}([^ ]+))? # Optional checksum
$ # Line end
- /xo.freeze
+ /xo
def parse_dependency(line)
return unless line =~ NAME_VERSION
spaces = $1
return unless spaces.size == 2
- name = $2
+ name = -$2
version = $3
pinned = $5
- version = version.split(",").map(&:strip) if version
+ version = version.split(",").each(&:strip!) if version
dep = Bundler::Dependency.new(name, version)
@@ -186,23 +225,47 @@ module Bundler
@dependencies[dep.name] = dep
end
- def parse_spec(line)
+ def parse_checksum(line)
return unless line =~ NAME_VERSION
+
spaces = $1
+ return unless spaces.size == 2
+ checksums = $6
+ return unless checksums
name = $2
version = $3
platform = $4
+ version = Gem::Version.new(version)
+ platform = platform ? Gem::Platform.new(platform) : Gem::Platform::RUBY
+ full_name = Gem::NameTuple.new(name, version, platform).full_name
+ return unless spec = @specs[full_name]
+
+ checksums.split(",") do |lock_checksum|
+ column = line.index(lock_checksum) + 1
+ checksum = Checksum.from_lock(lock_checksum, "#{@lockfile_path}:#{@pos.line}:#{column}")
+ spec.source.checksum_store.register(spec, checksum)
+ end
+ end
+
+ def parse_spec(line)
+ return unless line =~ NAME_VERSION
+ spaces = $1
+ name = -$2
+ version = $3
+
if spaces.size == 4
+ # only load platform for non-dependency (spec) line
+ platform = $4
+
version = Gem::Version.new(version)
platform = platform ? Gem::Platform.new(platform) : Gem::Platform::RUBY
- @current_spec = LazySpecification.new(name, version, platform)
- @current_spec.source = @current_source
+ @current_spec = LazySpecification.new(name, version, platform, @current_source)
@current_source.add_dependency_names(name)
@specs[@current_spec.full_name] = @current_spec
elsif spaces.size == 6
- version = version.split(",").map(&:strip) if version
+ version = version.split(",").each(&:strip!) if version
dep = Gem::Dependency.new(name, version)
@current_spec.dependencies << dep
end
@@ -213,13 +276,14 @@ module Bundler
end
def parse_bundled_with(line)
- line = line.strip
+ line.strip!
return unless Gem::Version.correct?(line)
@bundler_version = Gem::Version.create(line)
end
def parse_ruby(line)
- @ruby_version = line.strip
+ line.strip!
+ @ruby_version = line
end
end
end
diff --git a/lib/bundler/man/bundle-add.1 b/lib/bundler/man/bundle-add.1
index 0e21c75506..0b09480f88 100644
--- a/lib/bundler/man/bundle-add.1
+++ b/lib/bundler/man/bundle-add.1
@@ -1,81 +1,58 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-ADD" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-ADD" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-add\fR \- Add gem to the Gemfile and run bundle install
-.
.SH "SYNOPSIS"
\fBbundle add\fR \fIGEM_NAME\fR [\-\-group=GROUP] [\-\-version=VERSION] [\-\-source=SOURCE] [\-\-path=PATH] [\-\-git=GIT] [\-\-github=GITHUB] [\-\-branch=BRANCH] [\-\-ref=REF] [\-\-skip\-install] [\-\-strict] [\-\-optimistic]
-.
.SH "DESCRIPTION"
Adds the named gem to the Gemfile and run \fBbundle install\fR\. \fBbundle install\fR can be avoided by using the flag \fB\-\-skip\-install\fR\.
-.
.P
Example:
-.
.P
bundle add rails
-.
.P
bundle add rails \-\-version "< 3\.0, > 1\.1"
-.
.P
bundle add rails \-\-version "~> 5\.0\.0" \-\-source "https://gems\.example\.com" \-\-group "development"
-.
.P
bundle add rails \-\-skip\-install
-.
.P
bundle add rails \-\-group "development, test"
-.
.SH "OPTIONS"
-.
.TP
\fB\-\-version\fR, \fB\-v\fR
Specify version requirements(s) for the added gem\.
-.
.TP
\fB\-\-group\fR, \fB\-g\fR
Specify the group(s) for the added gem\. Multiple groups should be separated by commas\.
-.
.TP
\fB\-\-source\fR, \fB\-s\fR
Specify the source for the added gem\.
-.
.TP
\fB\-\-require\fR, \fB\-r\fR
Adds require path to gem\. Provide false, or a path as a string\.
-.
.TP
\fB\-\-path\fR
Specify the file system path for the added gem\.
-.
.TP
\fB\-\-git\fR
Specify the git source for the added gem\.
-.
.TP
\fB\-\-github\fR
Specify the github source for the added gem\.
-.
.TP
\fB\-\-branch\fR
Specify the git branch for the added gem\.
-.
.TP
\fB\-\-ref\fR
Specify the git ref for the added gem\.
-.
.TP
\fB\-\-skip\-install\fR
Adds the gem to the Gemfile but does not install it\.
-.
.TP
\fB\-\-optimistic\fR
Adds optimistic declaration of version\.
-.
.TP
\fB\-\-strict\fR
Adds strict declaration of version\.
diff --git a/lib/bundler/man/bundle-binstubs.1 b/lib/bundler/man/bundle-binstubs.1
index 2774e9d28a..fdb86bfd29 100644
--- a/lib/bundler/man/bundle-binstubs.1
+++ b/lib/bundler/man/bundle-binstubs.1
@@ -1,41 +1,29 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-BINSTUBS" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-BINSTUBS" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-binstubs\fR \- Install the binstubs of the listed gems
-.
.SH "SYNOPSIS"
\fBbundle binstubs\fR \fIGEM_NAME\fR [\-\-force] [\-\-path PATH] [\-\-standalone]
-.
.SH "DESCRIPTION"
Binstubs are scripts that wrap around executables\. Bundler creates a small Ruby file (a binstub) that loads Bundler, runs the command, and puts it into \fBbin/\fR\. Binstubs are a shortcut\-or alternative\- to always using \fBbundle exec\fR\. This gives you a file that can be run directly, and one that will always run the correct gem version used by the application\.
-.
.P
For example, if you run \fBbundle binstubs rspec\-core\fR, Bundler will create the file \fBbin/rspec\fR\. That file will contain enough code to load Bundler, tell it to load the bundled gems, and then run rspec\.
-.
.P
This command generates binstubs for executables in \fBGEM_NAME\fR\. Binstubs are put into \fBbin\fR, or the \fB\-\-path\fR directory if one has been set\. Calling binstubs with [GEM [GEM]] will create binstubs for all given gems\.
-.
.SH "OPTIONS"
-.
.TP
\fB\-\-force\fR
Overwrite existing binstubs if they exist\.
-.
.TP
\fB\-\-path\fR
The location to install the specified binstubs to\. This defaults to \fBbin\fR\.
-.
.TP
\fB\-\-standalone\fR
Makes binstubs that can work without depending on Rubygems or Bundler at runtime\.
-.
.TP
\fB\-\-shebang\fR
-Specify a different shebang executable name than the default (default \'ruby\')
-.
+Specify a different shebang executable name than the default (default 'ruby')
.TP
\fB\-\-all\fR
Create binstubs for all gems in the bundle\.
diff --git a/lib/bundler/man/bundle-cache.1 b/lib/bundler/man/bundle-cache.1
index 67a8fdd14d..c5899ace1a 100644
--- a/lib/bundler/man/bundle-cache.1
+++ b/lib/bundler/man/bundle-cache.1
@@ -1,61 +1,40 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-CACHE" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-CACHE" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-cache\fR \- Package your needed \fB\.gem\fR files into your application
-.
.SH "SYNOPSIS"
\fBbundle cache\fR
-.
.P
alias: \fBpackage\fR, \fBpack\fR
-.
.SH "DESCRIPTION"
Copy all of the \fB\.gem\fR files needed to run the application into the \fBvendor/cache\fR directory\. In the future, when running \fBbundle install(1)\fR \fIbundle\-install\.1\.html\fR, use the gems in the cache in preference to the ones on \fBrubygems\.org\fR\.
-.
.SH "GIT AND PATH GEMS"
The \fBbundle cache\fR command can also package \fB:git\fR and \fB:path\fR dependencies besides \.gem files\. This needs to be explicitly enabled via the \fB\-\-all\fR option\. Once used, the \fB\-\-all\fR option will be remembered\.
-.
.SH "SUPPORT FOR MULTIPLE PLATFORMS"
When using gems that have different packages for different platforms, Bundler supports caching of gems for other platforms where the Gemfile has been resolved (i\.e\. present in the lockfile) in \fBvendor/cache\fR\. This needs to be enabled via the \fB\-\-all\-platforms\fR option\. This setting will be remembered in your local bundler configuration\.
-.
.SH "REMOTE FETCHING"
By default, if you run \fBbundle install(1)\fR \fIbundle\-install\.1\.html\fR after running bundle cache(1) \fIbundle\-cache\.1\.html\fR, bundler will still connect to \fBrubygems\.org\fR to check whether a platform\-specific gem exists for any of the gems in \fBvendor/cache\fR\.
-.
.P
For instance, consider this Gemfile(5):
-.
.IP "" 4
-.
.nf
-
source "https://rubygems\.org"
gem "nokogiri"
-.
.fi
-.
.IP "" 0
-.
.P
If you run \fBbundle cache\fR under C Ruby, bundler will retrieve the version of \fBnokogiri\fR for the \fB"ruby"\fR platform\. If you deploy to JRuby and run \fBbundle install\fR, bundler is forced to check to see whether a \fB"java"\fR platformed \fBnokogiri\fR exists\.
-.
.P
Even though the \fBnokogiri\fR gem for the Ruby platform is \fItechnically\fR acceptable on JRuby, it has a C extension that does not run on JRuby\. As a result, bundler will, by default, still connect to \fBrubygems\.org\fR to check whether it has a version of one of your gems more specific to your platform\.
-.
.P
This problem is also not limited to the \fB"java"\fR platform\. A similar (common) problem can happen when developing on Windows and deploying to Linux, or even when developing on OSX and deploying to Linux\.
-.
.P
If you know for sure that the gems packaged in \fBvendor/cache\fR are appropriate for the platform you are on, you can run \fBbundle install \-\-local\fR to skip checking for more appropriate gems, and use the ones in \fBvendor/cache\fR\.
-.
.P
One way to be sure that you have the right platformed versions of all your gems is to run \fBbundle cache\fR on an identical machine and check in the gems\. For instance, you can run \fBbundle cache\fR on an identical staging box during your staging process, and check in the \fBvendor/cache\fR before deploying to production\.
-.
.P
By default, bundle cache(1) \fIbundle\-cache\.1\.html\fR fetches and also installs the gems to the default location\. To package the dependencies to \fBvendor/cache\fR without installing them to the local install location, you can run \fBbundle cache \-\-no\-install\fR\.
-.
.SH "HISTORY"
In Bundler 2\.1, \fBcache\fR took in the functionalities of \fBpackage\fR and now \fBpackage\fR and \fBpack\fR are aliases of \fBcache\fR\.
diff --git a/lib/bundler/man/bundle-check.1 b/lib/bundler/man/bundle-check.1
index 7679945c48..e1a971edae 100644
--- a/lib/bundler/man/bundle-check.1
+++ b/lib/bundler/man/bundle-check.1
@@ -1,30 +1,23 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-CHECK" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-CHECK" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-check\fR \- Verifies if dependencies are satisfied by installed gems
-.
.SH "SYNOPSIS"
\fBbundle check\fR [\-\-dry\-run] [\-\-gemfile=FILE] [\-\-path=PATH]
-.
.SH "DESCRIPTION"
\fBcheck\fR searches the local machine for each of the gems requested in the Gemfile\. If all gems are found, Bundler prints a success message and exits with a status of 0\.
-.
.P
If not, the first missing gem is listed and Bundler exits status 1\.
-.
+.P
+If the lockfile needs to be updated then it will be resolved using the gems installed on the local machine, if they satisfy the requirements\.
.SH "OPTIONS"
-.
.TP
\fB\-\-dry\-run\fR
Locks the [\fBGemfile(5)\fR][Gemfile(5)] before running the command\.
-.
.TP
\fB\-\-gemfile\fR
Use the specified gemfile instead of the [\fBGemfile(5)\fR][Gemfile(5)]\.
-.
.TP
\fB\-\-path\fR
Specify a different path than the system default (\fB$BUNDLE_PATH\fR or \fB$GEM_HOME\fR)\. Bundler will remember this value for future installs on this machine\.
diff --git a/lib/bundler/man/bundle-check.1.ronn b/lib/bundler/man/bundle-check.1.ronn
index f2846b8ff2..eb3ff1daf9 100644
--- a/lib/bundler/man/bundle-check.1.ronn
+++ b/lib/bundler/man/bundle-check.1.ronn
@@ -15,6 +15,9 @@ a status of 0.
If not, the first missing gem is listed and Bundler exits status 1.
+If the lockfile needs to be updated then it will be resolved using the gems
+installed on the local machine, if they satisfy the requirements.
+
## OPTIONS
* `--dry-run`:
diff --git a/lib/bundler/man/bundle-clean.1 b/lib/bundler/man/bundle-clean.1
index 2eb745698c..ca2fb65f59 100644
--- a/lib/bundler/man/bundle-clean.1
+++ b/lib/bundler/man/bundle-clean.1
@@ -1,23 +1,16 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-CLEAN" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-CLEAN" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-clean\fR \- Cleans up unused gems in your bundler directory
-.
.SH "SYNOPSIS"
\fBbundle clean\fR [\-\-dry\-run] [\-\-force]
-.
.SH "DESCRIPTION"
This command will remove all unused gems in your bundler directory\. This is useful when you have made many changes to your gem dependencies\.
-.
.SH "OPTIONS"
-.
.TP
\fB\-\-dry\-run\fR
Print the changes, but do not clean the unused gems\.
-.
.TP
\fB\-\-force\fR
Forces cleaning up unused gems even if Bundler is configured to use globally installed gems\. As a consequence, removes all system gems except for the ones in the current application\.
diff --git a/lib/bundler/man/bundle-config.1 b/lib/bundler/man/bundle-config.1
index 493b57e1de..a78c6b0a18 100644
--- a/lib/bundler/man/bundle-config.1
+++ b/lib/bundler/man/bundle-config.1
@@ -1,515 +1,319 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-CONFIG" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-CONFIG" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-config\fR \- Set bundler configuration options
-.
.SH "SYNOPSIS"
\fBbundle config\fR list
-.
.br
\fBbundle config\fR [get] NAME
-.
.br
\fBbundle config\fR [set] NAME VALUE
-.
.br
\fBbundle config\fR unset NAME
-.
.SH "DESCRIPTION"
-This command allows you to interact with Bundler\'s configuration system\.
-.
+This command allows you to interact with Bundler's configuration system\.
.P
Bundler loads configuration settings in this order:
-.
.IP "1." 4
Local config (\fB<project_root>/\.bundle/config\fR or \fB$BUNDLE_APP_CONFIG/config\fR)
-.
.IP "2." 4
Environmental variables (\fBENV\fR)
-.
.IP "3." 4
Global config (\fB~/\.bundle/config\fR)
-.
.IP "4." 4
Bundler default config
-.
.IP "" 0
-.
.P
Executing \fBbundle config list\fR will print a list of all bundler configuration for the current bundle, and where that configuration was set\.
-.
.P
Executing \fBbundle config get <name>\fR will print the value of that configuration setting, and where it was set\.
-.
.P
Executing \fBbundle config set <name> <value>\fR defaults to setting \fBlocal\fR configuration if executing from within a local application, otherwise it will set \fBglobal\fR configuration\. See \fB\-\-local\fR and \fB\-\-global\fR options below\.
-.
.P
Executing \fBbundle config set \-\-local <name> <value>\fR will set that configuration in the directory for the local application\. The configuration will be stored in \fB<project_root>/\.bundle/config\fR\. If \fBBUNDLE_APP_CONFIG\fR is set, the configuration will be stored in \fB$BUNDLE_APP_CONFIG/config\fR\.
-.
.P
Executing \fBbundle config set \-\-global <name> <value>\fR will set that configuration to the value specified for all bundles executed as the current user\. The configuration will be stored in \fB~/\.bundle/config\fR\. If \fIname\fR already is set, \fIname\fR will be overridden and user will be warned\.
-.
.P
Executing \fBbundle config unset <name>\fR will delete the configuration in both local and global sources\.
-.
.P
Executing \fBbundle config unset \-\-global <name>\fR will delete the configuration only from the user configuration\.
-.
.P
Executing \fBbundle config unset \-\-local <name>\fR will delete the configuration only from the local application\.
-.
.P
Executing bundle with the \fBBUNDLE_IGNORE_CONFIG\fR environment variable set will cause it to ignore all configuration\.
-.
.SH "REMEMBERING OPTIONS"
-Flags passed to \fBbundle install\fR or the Bundler runtime, such as \fB\-\-path foo\fR or \fB\-\-without production\fR, are remembered between commands and saved to your local application\'s configuration (normally, \fB\./\.bundle/config\fR)\.
-.
+Flags passed to \fBbundle install\fR or the Bundler runtime, such as \fB\-\-path foo\fR or \fB\-\-without production\fR, are remembered between commands and saved to your local application's configuration (normally, \fB\./\.bundle/config\fR)\.
.P
-However, this will be changed in bundler 3, so it\'s better not to rely on this behavior\. If these options must be remembered, it\'s better to set them using \fBbundle config\fR (e\.g\., \fBbundle config set \-\-local path foo\fR)\.
-.
+However, this will be changed in bundler 3, so it's better not to rely on this behavior\. If these options must be remembered, it's better to set them using \fBbundle config\fR (e\.g\., \fBbundle config set \-\-local path foo\fR)\.
.P
The options that can be configured are:
-.
.TP
\fBbin\fR
-Creates a directory (defaults to \fB~/bin\fR) and place any executables from the gem there\. These executables run in Bundler\'s context\. If used, you might add this directory to your environment\'s \fBPATH\fR variable\. For instance, if the \fBrails\fR gem comes with a \fBrails\fR executable, this flag will create a \fBbin/rails\fR executable that ensures that all referred dependencies will be resolved using the bundled gems\.
-.
+Creates a directory (defaults to \fB~/bin\fR) and place any executables from the gem there\. These executables run in Bundler's context\. If used, you might add this directory to your environment's \fBPATH\fR variable\. For instance, if the \fBrails\fR gem comes with a \fBrails\fR executable, this flag will create a \fBbin/rails\fR executable that ensures that all referred dependencies will be resolved using the bundled gems\.
.TP
\fBdeployment\fR
-In deployment mode, Bundler will \'roll\-out\' the bundle for \fBproduction\fR use\. Please check carefully if you want to have this option enabled in \fBdevelopment\fR or \fBtest\fR environments\.
-.
+In deployment mode, Bundler will 'roll\-out' the bundle for \fBproduction\fR use\. Please check carefully if you want to have this option enabled in \fBdevelopment\fR or \fBtest\fR environments\.
.TP
\fBonly\fR
A space\-separated list of groups to install only gems of the specified groups\.
-.
.TP
\fBpath\fR
-The location to install the specified gems to\. This defaults to Rubygems\' setting\. Bundler shares this location with Rubygems, \fBgem install \.\.\.\fR will have gem installed there, too\. Therefore, gems installed without a \fB\-\-path \.\.\.\fR setting will show up by calling \fBgem list\fR\. Accordingly, gems installed to other locations will not get listed\.
-.
+The location to install the specified gems to\. This defaults to Rubygems' setting\. Bundler shares this location with Rubygems, \fBgem install \|\.\|\.\|\.\fR will have gem installed there, too\. Therefore, gems installed without a \fB\-\-path \|\.\|\.\|\.\fR setting will show up by calling \fBgem list\fR\. Accordingly, gems installed to other locations will not get listed\.
.TP
\fBwithout\fR
A space\-separated list of groups referencing gems to skip during installation\.
-.
.TP
\fBwith\fR
A space\-separated list of \fBoptional\fR groups referencing gems to include during installation\.
-.
.SH "BUILD OPTIONS"
You can use \fBbundle config\fR to give Bundler the flags to pass to the gem installer every time bundler tries to install a particular gem\.
-.
.P
A very common example, the \fBmysql\fR gem, requires Snow Leopard users to pass configuration flags to \fBgem install\fR to specify where to find the \fBmysql_config\fR executable\.
-.
.IP "" 4
-.
.nf
-
gem install mysql \-\- \-\-with\-mysql\-config=/usr/local/mysql/bin/mysql_config
-.
.fi
-.
.IP "" 0
-.
.P
Since the specific location of that executable can change from machine to machine, you can specify these flags on a per\-machine basis\.
-.
.IP "" 4
-.
.nf
-
bundle config set \-\-global build\.mysql \-\-with\-mysql\-config=/usr/local/mysql/bin/mysql_config
-.
.fi
-.
.IP "" 0
-.
.P
After running this command, every time bundler needs to install the \fBmysql\fR gem, it will pass along the flags you specified\.
-.
.SH "CONFIGURATION KEYS"
Configuration keys in bundler have two forms: the canonical form and the environment variable form\.
-.
.P
-For instance, passing the \fB\-\-without\fR flag to bundle install(1) \fIbundle\-install\.1\.html\fR prevents Bundler from installing certain groups specified in the Gemfile(5)\. Bundler persists this value in \fBapp/\.bundle/config\fR so that calls to \fBBundler\.setup\fR do not try to find gems from the \fBGemfile\fR that you didn\'t install\. Additionally, subsequent calls to bundle install(1) \fIbundle\-install\.1\.html\fR remember this setting and skip those groups\.
-.
+For instance, passing the \fB\-\-without\fR flag to bundle install(1) \fIbundle\-install\.1\.html\fR prevents Bundler from installing certain groups specified in the Gemfile(5)\. Bundler persists this value in \fBapp/\.bundle/config\fR so that calls to \fBBundler\.setup\fR do not try to find gems from the \fBGemfile\fR that you didn't install\. Additionally, subsequent calls to bundle install(1) \fIbundle\-install\.1\.html\fR remember this setting and skip those groups\.
.P
The canonical form of this configuration is \fB"without"\fR\. To convert the canonical form to the environment variable form, capitalize it, and prepend \fBBUNDLE_\fR\. The environment variable form of \fB"without"\fR is \fBBUNDLE_WITHOUT\fR\.
-.
.P
Any periods in the configuration keys must be replaced with two underscores when setting it via environment variables\. The configuration key \fBlocal\.rack\fR becomes the environment variable \fBBUNDLE_LOCAL__RACK\fR\.
-.
.SH "LIST OF AVAILABLE KEYS"
The following is a list of all configuration keys and their purpose\. You can learn more about their operation in bundle install(1) \fIbundle\-install\.1\.html\fR\.
-.
-.IP "\(bu" 4
-\fBallow_deployment_source_credential_changes\fR (\fBBUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES\fR): When in deployment mode, allow changing the credentials to a gem\'s source\. Ex: \fBhttps://some\.host\.com/gems/path/\fR \-> \fBhttps://user_name:password@some\.host\.com/gems/path\fR
-.
.IP "\(bu" 4
\fBallow_offline_install\fR (\fBBUNDLE_ALLOW_OFFLINE_INSTALL\fR): Allow Bundler to use cached data when installing without network access\.
-.
.IP "\(bu" 4
\fBauto_clean_without_path\fR (\fBBUNDLE_AUTO_CLEAN_WITHOUT_PATH\fR): Automatically run \fBbundle clean\fR after installing when an explicit \fBpath\fR has not been set and Bundler is not installing into the system gems\.
-.
.IP "\(bu" 4
\fBauto_install\fR (\fBBUNDLE_AUTO_INSTALL\fR): Automatically run \fBbundle install\fR when gems are missing\.
-.
.IP "\(bu" 4
\fBbin\fR (\fBBUNDLE_BIN\fR): Install executables from gems in the bundle to the specified directory\. Defaults to \fBfalse\fR\.
-.
.IP "\(bu" 4
\fBcache_all\fR (\fBBUNDLE_CACHE_ALL\fR): Cache all gems, including path and git gems\. This needs to be explicitly configured on bundler 1 and bundler 2, but will be the default on bundler 3\.
-.
.IP "\(bu" 4
\fBcache_all_platforms\fR (\fBBUNDLE_CACHE_ALL_PLATFORMS\fR): Cache gems for all platforms\.
-.
.IP "\(bu" 4
\fBcache_path\fR (\fBBUNDLE_CACHE_PATH\fR): The directory that bundler will place cached gems in when running \fBbundle package\fR, and that bundler will look in when installing gems\. Defaults to \fBvendor/cache\fR\.
-.
.IP "\(bu" 4
\fBclean\fR (\fBBUNDLE_CLEAN\fR): Whether Bundler should run \fBbundle clean\fR automatically after \fBbundle install\fR\.
-.
.IP "\(bu" 4
\fBconsole\fR (\fBBUNDLE_CONSOLE\fR): The console that \fBbundle console\fR starts\. Defaults to \fBirb\fR\.
-.
.IP "\(bu" 4
\fBdefault_install_uses_path\fR (\fBBUNDLE_DEFAULT_INSTALL_USES_PATH\fR): Whether a \fBbundle install\fR without an explicit \fB\-\-path\fR argument defaults to installing gems in \fB\.bundle\fR\.
-.
.IP "\(bu" 4
\fBdeployment\fR (\fBBUNDLE_DEPLOYMENT\fR): Disallow changes to the \fBGemfile\fR\. When the \fBGemfile\fR is changed and the lockfile has not been updated, running Bundler commands will be blocked\.
-.
.IP "\(bu" 4
\fBdisable_checksum_validation\fR (\fBBUNDLE_DISABLE_CHECKSUM_VALIDATION\fR): Allow installing gems even if they do not match the checksum provided by RubyGems\.
-.
.IP "\(bu" 4
\fBdisable_exec_load\fR (\fBBUNDLE_DISABLE_EXEC_LOAD\fR): Stop Bundler from using \fBload\fR to launch an executable in\-process in \fBbundle exec\fR\.
-.
.IP "\(bu" 4
\fBdisable_local_branch_check\fR (\fBBUNDLE_DISABLE_LOCAL_BRANCH_CHECK\fR): Allow Bundler to use a local git override without a branch specified in the Gemfile\.
-.
.IP "\(bu" 4
\fBdisable_local_revision_check\fR (\fBBUNDLE_DISABLE_LOCAL_REVISION_CHECK\fR): Allow Bundler to use a local git override without checking if the revision present in the lockfile is present in the repository\.
-.
.IP "\(bu" 4
-\fBdisable_shared_gems\fR (\fBBUNDLE_DISABLE_SHARED_GEMS\fR): Stop Bundler from accessing gems installed to RubyGems\' normal location\.
-.
+\fBdisable_shared_gems\fR (\fBBUNDLE_DISABLE_SHARED_GEMS\fR): Stop Bundler from accessing gems installed to RubyGems' normal location\.
.IP "\(bu" 4
\fBdisable_version_check\fR (\fBBUNDLE_DISABLE_VERSION_CHECK\fR): Stop Bundler from checking if a newer Bundler version is available on rubygems\.org\.
-.
.IP "\(bu" 4
-\fBforce_ruby_platform\fR (\fBBUNDLE_FORCE_RUBY_PLATFORM\fR): Ignore the current machine\'s platform and install only \fBruby\fR platform gems\. As a result, gems with native extensions will be compiled from source\.
-.
+\fBforce_ruby_platform\fR (\fBBUNDLE_FORCE_RUBY_PLATFORM\fR): Ignore the current machine's platform and install only \fBruby\fR platform gems\. As a result, gems with native extensions will be compiled from source\.
.IP "\(bu" 4
\fBfrozen\fR (\fBBUNDLE_FROZEN\fR): Disallow changes to the \fBGemfile\fR\. When the \fBGemfile\fR is changed and the lockfile has not been updated, running Bundler commands will be blocked\. Defaults to \fBtrue\fR when \fB\-\-deployment\fR is used\.
-.
.IP "\(bu" 4
\fBgem\.github_username\fR (\fBBUNDLE_GEM__GITHUB_USERNAME\fR): Sets a GitHub username or organization to be used in \fBREADME\fR file when you create a new gem via \fBbundle gem\fR command\. It can be overridden by passing an explicit \fB\-\-github\-username\fR flag to \fBbundle gem\fR\.
-.
.IP "\(bu" 4
\fBgem\.push_key\fR (\fBBUNDLE_GEM__PUSH_KEY\fR): Sets the \fB\-\-key\fR parameter for \fBgem push\fR when using the \fBrake release\fR command with a private gemstash server\.
-.
.IP "\(bu" 4
\fBgemfile\fR (\fBBUNDLE_GEMFILE\fR): The name of the file that bundler should use as the \fBGemfile\fR\. This location of this file also sets the root of the project, which is used to resolve relative paths in the \fBGemfile\fR, among other things\. By default, bundler will search up from the current working directory until it finds a \fBGemfile\fR\.
-.
.IP "\(bu" 4
\fBglobal_gem_cache\fR (\fBBUNDLE_GLOBAL_GEM_CACHE\fR): Whether Bundler should cache all gems globally, rather than locally to the installing Ruby installation\.
-.
.IP "\(bu" 4
\fBignore_funding_requests\fR (\fBBUNDLE_IGNORE_FUNDING_REQUESTS\fR): When set, no funding requests will be printed\.
-.
.IP "\(bu" 4
\fBignore_messages\fR (\fBBUNDLE_IGNORE_MESSAGES\fR): When set, no post install messages will be printed\. To silence a single gem, use dot notation like \fBignore_messages\.httparty true\fR\.
-.
.IP "\(bu" 4
\fBinit_gems_rb\fR (\fBBUNDLE_INIT_GEMS_RB\fR): Generate a \fBgems\.rb\fR instead of a \fBGemfile\fR when running \fBbundle init\fR\.
-.
.IP "\(bu" 4
\fBjobs\fR (\fBBUNDLE_JOBS\fR): The number of gems Bundler can install in parallel\. Defaults to the number of available processors\.
-.
.IP "\(bu" 4
\fBno_install\fR (\fBBUNDLE_NO_INSTALL\fR): Whether \fBbundle package\fR should skip installing gems\.
-.
.IP "\(bu" 4
\fBno_prune\fR (\fBBUNDLE_NO_PRUNE\fR): Whether Bundler should leave outdated gems unpruned when caching\.
-.
.IP "\(bu" 4
\fBonly\fR (\fBBUNDLE_ONLY\fR): A space\-separated list of groups to install only gems of the specified groups\.
-.
.IP "\(bu" 4
\fBpath\fR (\fBBUNDLE_PATH\fR): The location on disk where all gems in your bundle will be located regardless of \fB$GEM_HOME\fR or \fB$GEM_PATH\fR values\. Bundle gems not found in this location will be installed by \fBbundle install\fR\. Defaults to \fBGem\.dir\fR\. When \-\-deployment is used, defaults to vendor/bundle\.
-.
.IP "\(bu" 4
\fBpath\.system\fR (\fBBUNDLE_PATH__SYSTEM\fR): Whether Bundler will install gems into the default system path (\fBGem\.dir\fR)\.
-.
.IP "\(bu" 4
\fBpath_relative_to_cwd\fR (\fBBUNDLE_PATH_RELATIVE_TO_CWD\fR) Makes \fB\-\-path\fR relative to the CWD instead of the \fBGemfile\fR\.
-.
.IP "\(bu" 4
-\fBplugins\fR (\fBBUNDLE_PLUGINS\fR): Enable Bundler\'s experimental plugin system\.
-.
+\fBplugins\fR (\fBBUNDLE_PLUGINS\fR): Enable Bundler's experimental plugin system\.
.IP "\(bu" 4
\fBprefer_patch\fR (BUNDLE_PREFER_PATCH): Prefer updating only to next patch version during updates\. Makes \fBbundle update\fR calls equivalent to \fBbundler update \-\-patch\fR\.
-.
.IP "\(bu" 4
\fBprint_only_version_number\fR (\fBBUNDLE_PRINT_ONLY_VERSION_NUMBER\fR): Print only version number from \fBbundler \-\-version\fR\.
-.
.IP "\(bu" 4
\fBredirect\fR (\fBBUNDLE_REDIRECT\fR): The number of redirects allowed for network requests\. Defaults to \fB5\fR\.
-.
.IP "\(bu" 4
\fBretry\fR (\fBBUNDLE_RETRY\fR): The number of times to retry failed network requests\. Defaults to \fB3\fR\.
-.
.IP "\(bu" 4
\fBsetup_makes_kernel_gem_public\fR (\fBBUNDLE_SETUP_MAKES_KERNEL_GEM_PUBLIC\fR): Have \fBBundler\.setup\fR make the \fBKernel#gem\fR method public, even though RubyGems declares it as private\.
-.
.IP "\(bu" 4
\fBshebang\fR (\fBBUNDLE_SHEBANG\fR): The program name that should be invoked for generated binstubs\. Defaults to the ruby install name used to generate the binstub\.
-.
.IP "\(bu" 4
\fBsilence_deprecations\fR (\fBBUNDLE_SILENCE_DEPRECATIONS\fR): Whether Bundler should silence deprecation warnings for behavior that will be changed in the next major version\.
-.
.IP "\(bu" 4
\fBsilence_root_warning\fR (\fBBUNDLE_SILENCE_ROOT_WARNING\fR): Silence the warning Bundler prints when installing gems as root\.
-.
.IP "\(bu" 4
\fBssl_ca_cert\fR (\fBBUNDLE_SSL_CA_CERT\fR): Path to a designated CA certificate file or folder containing multiple certificates for trusted CAs in PEM format\.
-.
.IP "\(bu" 4
\fBssl_client_cert\fR (\fBBUNDLE_SSL_CLIENT_CERT\fR): Path to a designated file containing a X\.509 client certificate and key in PEM format\.
-.
.IP "\(bu" 4
\fBssl_verify_mode\fR (\fBBUNDLE_SSL_VERIFY_MODE\fR): The SSL verification mode Bundler uses when making HTTPS requests\. Defaults to verify peer\.
-.
-.IP "\(bu" 4
-\fBsuppress_install_using_messages\fR (\fBBUNDLE_SUPPRESS_INSTALL_USING_MESSAGES\fR): Avoid printing \fBUsing \.\.\.\fR messages during installation when the version of a gem has not changed\.
-.
.IP "\(bu" 4
\fBsystem_bindir\fR (\fBBUNDLE_SYSTEM_BINDIR\fR): The location where RubyGems installs binstubs\. Defaults to \fBGem\.bindir\fR\.
-.
.IP "\(bu" 4
\fBtimeout\fR (\fBBUNDLE_TIMEOUT\fR): The seconds allowed before timing out for network requests\. Defaults to \fB10\fR\.
-.
.IP "\(bu" 4
\fBupdate_requires_all_flag\fR (\fBBUNDLE_UPDATE_REQUIRES_ALL_FLAG\fR): Require passing \fB\-\-all\fR to \fBbundle update\fR when everything should be updated, and disallow passing no options to \fBbundle update\fR\.
-.
.IP "\(bu" 4
\fBuser_agent\fR (\fBBUNDLE_USER_AGENT\fR): The custom user agent fragment Bundler includes in API requests\.
-.
+.IP "\(bu" 4
+\fBversion\fR (\fBBUNDLE_VERSION\fR): The version of Bundler to use when running under Bundler environment\. Defaults to \fBlockfile\fR\. You can also specify \fBsystem\fR or \fBx\.y\.z\fR\. \fBlockfile\fR will use the Bundler version specified in the \fBGemfile\.lock\fR, \fBsystem\fR will use the system version of Bundler, and \fBx\.y\.z\fR will use the specified version of Bundler\.
.IP "\(bu" 4
\fBwith\fR (\fBBUNDLE_WITH\fR): A \fB:\fR\-separated list of groups whose gems bundler should install\.
-.
.IP "\(bu" 4
\fBwithout\fR (\fBBUNDLE_WITHOUT\fR): A \fB:\fR\-separated list of groups whose gems bundler should not install\.
-.
.IP "" 0
-.
.P
In general, you should set these settings per\-application by using the applicable flag to the bundle install(1) \fIbundle\-install\.1\.html\fR or bundle cache(1) \fIbundle\-cache\.1\.html\fR command\.
-.
.P
You can set them globally either via environment variables or \fBbundle config\fR, whichever is preferable for your setup\. If you use both, environment variables will take preference over global settings\.
-.
.SH "LOCAL GIT REPOS"
Bundler also allows you to work against a git repository locally instead of using the remote version\. This can be achieved by setting up a local override:
-.
.IP "" 4
-.
.nf
-
bundle config set \-\-local local\.GEM_NAME /path/to/local/git/repository
-.
.fi
-.
.IP "" 0
-.
.P
For example, in order to use a local Rack repository, a developer could call:
-.
.IP "" 4
-.
.nf
-
bundle config set \-\-local local\.rack ~/Work/git/rack
-.
.fi
-.
.IP "" 0
-.
.P
-Now instead of checking out the remote git repository, the local override will be used\. Similar to a path source, every time the local git repository change, changes will be automatically picked up by Bundler\. This means a commit in the local git repo will update the revision in the \fBGemfile\.lock\fR to the local git repo revision\. This requires the same attention as git submodules\. Before pushing to the remote, you need to ensure the local override was pushed, otherwise you may point to a commit that only exists in your local machine\. You\'ll also need to CGI escape your usernames and passwords as well\.
-.
+Now instead of checking out the remote git repository, the local override will be used\. Similar to a path source, every time the local git repository change, changes will be automatically picked up by Bundler\. This means a commit in the local git repo will update the revision in the \fBGemfile\.lock\fR to the local git repo revision\. This requires the same attention as git submodules\. Before pushing to the remote, you need to ensure the local override was pushed, otherwise you may point to a commit that only exists in your local machine\. You'll also need to CGI escape your usernames and passwords as well\.
.P
-Bundler does many checks to ensure a developer won\'t work with invalid references\. Particularly, we force a developer to specify a branch in the \fBGemfile\fR in order to use this feature\. If the branch specified in the \fBGemfile\fR and the current branch in the local git repository do not match, Bundler will abort\. This ensures that a developer is always working against the correct branches, and prevents accidental locking to a different branch\.
-.
+Bundler does many checks to ensure a developer won't work with invalid references\. Particularly, we force a developer to specify a branch in the \fBGemfile\fR in order to use this feature\. If the branch specified in the \fBGemfile\fR and the current branch in the local git repository do not match, Bundler will abort\. This ensures that a developer is always working against the correct branches, and prevents accidental locking to a different branch\.
.P
Finally, Bundler also ensures that the current revision in the \fBGemfile\.lock\fR exists in the local git repository\. By doing this, Bundler forces you to fetch the latest changes in the remotes\.
-.
.SH "MIRRORS OF GEM SOURCES"
Bundler supports overriding gem sources with mirrors\. This allows you to configure rubygems\.org as the gem source in your Gemfile while still using your mirror to fetch gems\.
-.
.IP "" 4
-.
.nf
-
bundle config set \-\-global mirror\.SOURCE_URL MIRROR_URL
-.
.fi
-.
.IP "" 0
-.
.P
For example, to use a mirror of https://rubygems\.org hosted at https://example\.org:
-.
.IP "" 4
-.
.nf
-
bundle config set \-\-global mirror\.https://rubygems\.org https://example\.org
-.
.fi
-.
.IP "" 0
-.
.P
Each mirror also provides a fallback timeout setting\. If the mirror does not respond within the fallback timeout, Bundler will try to use the original server instead of the mirror\.
-.
.IP "" 4
-.
.nf
-
bundle config set \-\-global mirror\.SOURCE_URL\.fallback_timeout TIMEOUT
-.
.fi
-.
.IP "" 0
-.
.P
For example, to fall back to rubygems\.org after 3 seconds:
-.
.IP "" 4
-.
.nf
-
bundle config set \-\-global mirror\.https://rubygems\.org\.fallback_timeout 3
-.
.fi
-.
.IP "" 0
-.
.P
The default fallback timeout is 0\.1 seconds, but the setting can currently only accept whole seconds (for example, 1, 15, or 30)\.
-.
.SH "CREDENTIALS FOR GEM SOURCES"
Bundler allows you to configure credentials for any gem source, which allows you to avoid putting secrets into your Gemfile\.
-.
.IP "" 4
-.
.nf
-
bundle config set \-\-global SOURCE_HOSTNAME USERNAME:PASSWORD
-.
.fi
-.
.IP "" 0
-.
.P
For example, to save the credentials of user \fBclaudette\fR for the gem source at \fBgems\.longerous\.com\fR, you would run:
-.
.IP "" 4
-.
.nf
-
bundle config set \-\-global gems\.longerous\.com claudette:s00pers3krit
-.
.fi
-.
.IP "" 0
-.
.P
Or you can set the credentials as an environment variable like this:
-.
.IP "" 4
-.
.nf
-
export BUNDLE_GEMS__LONGEROUS__COM="claudette:s00pers3krit"
-.
.fi
-.
.IP "" 0
-.
.P
For gems with a git source with HTTP(S) URL you can specify credentials like so:
-.
.IP "" 4
-.
.nf
-
bundle config set \-\-global https://github\.com/rubygems/rubygems\.git username:password
-.
.fi
-.
.IP "" 0
-.
.P
Or you can set the credentials as an environment variable like so:
-.
.IP "" 4
-.
.nf
-
export BUNDLE_GITHUB__COM=username:password
-.
.fi
-.
.IP "" 0
-.
.P
This is especially useful for private repositories on hosts such as GitHub, where you can use personal OAuth tokens:
-.
.IP "" 4
-.
.nf
-
export BUNDLE_GITHUB__COM=abcd0123generatedtoken:x\-oauth\-basic
-.
.fi
-.
.IP "" 0
-.
.P
Note that any configured credentials will be redacted by informative commands such as \fBbundle config list\fR or \fBbundle config get\fR, unless you use the \fB\-\-parseable\fR flag\. This is to avoid unintentionally leaking credentials when copy\-pasting bundler output\.
-.
.P
Also note that to guarantee a sane mapping between valid environment variable names and valid host names, bundler makes the following transformations:
-.
.IP "\(bu" 4
-Any \fB\-\fR characters in a host name are mapped to a triple dash (\fB___\fR) in the corresponding environment variable\.
-.
+Any \fB\-\fR characters in a host name are mapped to a triple underscore (\fB___\fR) in the corresponding environment variable\.
.IP "\(bu" 4
-Any \fB\.\fR characters in a host name are mapped to a double dash (\fB__\fR) in the corresponding environment variable\.
-.
+Any \fB\.\fR characters in a host name are mapped to a double underscore (\fB__\fR) in the corresponding environment variable\.
.IP "" 0
-.
.P
-This means that if you have a gem server named \fBmy\.gem\-host\.com\fR, you\'ll need to use the \fBBUNDLE_MY__GEM___HOST__COM\fR variable to configure credentials for it through ENV\.
-.
+This means that if you have a gem server named \fBmy\.gem\-host\.com\fR, you'll need to use the \fBBUNDLE_MY__GEM___HOST__COM\fR variable to configure credentials for it through ENV\.
.SH "CONFIGURE BUNDLER DIRECTORIES"
-Bundler\'s home, config, cache and plugin directories are able to be configured through environment variables\. The default location for Bundler\'s home directory is \fB~/\.bundle\fR, which all directories inherit from by default\. The following outlines the available environment variables and their default values
-.
+Bundler's home, config, cache and plugin directories are able to be configured through environment variables\. The default location for Bundler's home directory is \fB~/\.bundle\fR, which all directories inherit from by default\. The following outlines the available environment variables and their default values
.IP "" 4
-.
.nf
-
BUNDLE_USER_HOME : $HOME/\.bundle
BUNDLE_USER_CACHE : $BUNDLE_USER_HOME/cache
BUNDLE_USER_CONFIG : $BUNDLE_USER_HOME/config
BUNDLE_USER_PLUGIN : $BUNDLE_USER_HOME/plugin
-.
.fi
-.
.IP "" 0
diff --git a/lib/bundler/man/bundle-config.1.ronn b/lib/bundler/man/bundle-config.1.ronn
index bc8b27cf89..7e5f458fb2 100644
--- a/lib/bundler/man/bundle-config.1.ronn
+++ b/lib/bundler/man/bundle-config.1.ronn
@@ -137,9 +137,6 @@ the environment variable `BUNDLE_LOCAL__RACK`.
The following is a list of all configuration keys and their purpose. You can
learn more about their operation in [bundle install(1)](bundle-install.1.html).
-* `allow_deployment_source_credential_changes` (`BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES`):
- When in deployment mode, allow changing the credentials to a gem's source.
- Ex: `https://some.host.com/gems/path/` -> `https://user_name:password@some.host.com/gems/path`
* `allow_offline_install` (`BUNDLE_ALLOW_OFFLINE_INSTALL`):
Allow Bundler to use cached data when installing without network access.
* `auto_clean_without_path` (`BUNDLE_AUTO_CLEAN_WITHOUT_PATH`):
@@ -265,9 +262,6 @@ learn more about their operation in [bundle install(1)](bundle-install.1.html).
* `ssl_verify_mode` (`BUNDLE_SSL_VERIFY_MODE`):
The SSL verification mode Bundler uses when making HTTPS requests.
Defaults to verify peer.
-* `suppress_install_using_messages` (`BUNDLE_SUPPRESS_INSTALL_USING_MESSAGES`):
- Avoid printing `Using ...` messages during installation when the version of
- a gem has not changed.
* `system_bindir` (`BUNDLE_SYSTEM_BINDIR`):
The location where RubyGems installs binstubs. Defaults to `Gem.bindir`.
* `timeout` (`BUNDLE_TIMEOUT`):
@@ -277,6 +271,12 @@ learn more about their operation in [bundle install(1)](bundle-install.1.html).
and disallow passing no options to `bundle update`.
* `user_agent` (`BUNDLE_USER_AGENT`):
The custom user agent fragment Bundler includes in API requests.
+* `version` (`BUNDLE_VERSION`):
+ The version of Bundler to use when running under Bundler environment.
+ Defaults to `lockfile`. You can also specify `system` or `x.y.z`.
+ `lockfile` will use the Bundler version specified in the `Gemfile.lock`,
+ `system` will use the system version of Bundler, and `x.y.z` will use
+ the specified version of Bundler.
* `with` (`BUNDLE_WITH`):
A `:`-separated list of groups whose gems bundler should install.
* `without` (`BUNDLE_WITHOUT`):
@@ -385,10 +385,10 @@ copy-pasting bundler output.
Also note that to guarantee a sane mapping between valid environment variable
names and valid host names, bundler makes the following transformations:
-* Any `-` characters in a host name are mapped to a triple dash (`___`) in the
+* Any `-` characters in a host name are mapped to a triple underscore (`___`) in the
corresponding environment variable.
-* Any `.` characters in a host name are mapped to a double dash (`__`) in the
+* Any `.` characters in a host name are mapped to a double underscore (`__`) in the
corresponding environment variable.
This means that if you have a gem server named `my.gem-host.com`, you'll need to
diff --git a/lib/bundler/man/bundle-console.1 b/lib/bundler/man/bundle-console.1
index ff239004bf..86be5e4185 100644
--- a/lib/bundler/man/bundle-console.1
+++ b/lib/bundler/man/bundle-console.1
@@ -1,53 +1,35 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-CONSOLE" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-CONSOLE" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-console\fR \- Deprecated way to open an IRB session with the bundle pre\-loaded
-.
.SH "SYNOPSIS"
\fBbundle console\fR [GROUP]
-.
.SH "DESCRIPTION"
Starts an interactive Ruby console session in the context of the current bundle\.
-.
.P
If no \fBGROUP\fR is specified, all gems in the \fBdefault\fR group in the Gemfile(5) \fIhttps://bundler\.io/man/gemfile\.5\.html\fR are preliminarily loaded\.
-.
.P
If \fBGROUP\fR is specified, all gems in the given group in the Gemfile in addition to the gems in \fBdefault\fR group are loaded\. Even if the given group does not exist in the Gemfile, IRB console starts without any warning or error\.
-.
.P
The environment variable \fBBUNDLE_CONSOLE\fR or \fBbundle config set console\fR can be used to change the shell from the following:
-.
.IP "\(bu" 4
\fBirb\fR (default)
-.
.IP "\(bu" 4
\fBpry\fR (https://github\.com/pry/pry)
-.
.IP "\(bu" 4
\fBripl\fR (https://github\.com/cldwalker/ripl)
-.
.IP "" 0
-.
.P
\fBbundle console\fR uses irb by default\. An alternative Pry or Ripl can be used with \fBbundle console\fR by adjusting the \fBconsole\fR Bundler setting\. Also make sure that \fBpry\fR or \fBripl\fR is in your Gemfile\.
-.
.SH "EXAMPLE"
-.
.nf
-
$ bundle config set console pry
$ bundle console
-Resolving dependencies\.\.\.
+Resolving dependencies\|\.\|\.\|\.
[1] pry(main)>
-.
.fi
-.
.SH "NOTES"
This command was deprecated in Bundler 2\.1 and will be removed in 3\.0\. Use \fBbin/console\fR script, which can be generated by \fBbundle gem <NAME>\fR\.
-.
.SH "SEE ALSO"
Gemfile(5) \fIhttps://bundler\.io/man/gemfile\.5\.html\fR
diff --git a/lib/bundler/man/bundle-doctor.1 b/lib/bundler/man/bundle-doctor.1
index e463b67477..94cbff4cbd 100644
--- a/lib/bundler/man/bundle-doctor.1
+++ b/lib/bundler/man/bundle-doctor.1
@@ -1,44 +1,30 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-DOCTOR" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-DOCTOR" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-doctor\fR \- Checks the bundle for common problems
-.
.SH "SYNOPSIS"
\fBbundle doctor\fR [\-\-quiet] [\-\-gemfile=GEMFILE]
-.
.SH "DESCRIPTION"
Checks your Gemfile and gem environment for common problems\. If issues are detected, Bundler prints them and exits status 1\. Otherwise, Bundler prints a success message and exits status 0\.
-.
.P
Examples of common problems caught by bundle\-doctor include:
-.
.IP "\(bu" 4
Invalid Bundler settings
-.
.IP "\(bu" 4
Mismatched Ruby versions
-.
.IP "\(bu" 4
Mismatched platforms
-.
.IP "\(bu" 4
Uninstalled gems
-.
.IP "\(bu" 4
Missing dependencies
-.
.IP "" 0
-.
.SH "OPTIONS"
-.
.TP
\fB\-\-quiet\fR
Only output warnings and errors\.
-.
.TP
\fB\-\-gemfile=<gemfile>\fR
-The location of the Gemfile(5) which Bundler should use\. This defaults to a Gemfile(5) in the current working directory\. In general, Bundler will assume that the location of the Gemfile(5) is also the project\'s root and will try to find \fBGemfile\.lock\fR and \fBvendor/cache\fR relative to this location\.
+The location of the Gemfile(5) which Bundler should use\. This defaults to a Gemfile(5) in the current working directory\. In general, Bundler will assume that the location of the Gemfile(5) is also the project's root and will try to find \fBGemfile\.lock\fR and \fBvendor/cache\fR relative to this location\.
diff --git a/lib/bundler/man/bundle-exec.1 b/lib/bundler/man/bundle-exec.1
index 9e9efe8b6d..7aa49e0096 100644
--- a/lib/bundler/man/bundle-exec.1
+++ b/lib/bundler/man/bundle-exec.1
@@ -1,165 +1,104 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-EXEC" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-EXEC" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-exec\fR \- Execute a command in the context of the bundle
-.
.SH "SYNOPSIS"
\fBbundle exec\fR [\-\-keep\-file\-descriptors] \fIcommand\fR
-.
.SH "DESCRIPTION"
This command executes the command, making all gems specified in the [\fBGemfile(5)\fR][Gemfile(5)] available to \fBrequire\fR in Ruby programs\.
-.
.P
Essentially, if you would normally have run something like \fBrspec spec/my_spec\.rb\fR, and you want to use the gems specified in the [\fBGemfile(5)\fR][Gemfile(5)] and installed via bundle install(1) \fIbundle\-install\.1\.html\fR, you should run \fBbundle exec rspec spec/my_spec\.rb\fR\.
-.
.P
-Note that \fBbundle exec\fR does not require that an executable is available on your shell\'s \fB$PATH\fR\.
-.
+Note that \fBbundle exec\fR does not require that an executable is available on your shell's \fB$PATH\fR\.
.SH "OPTIONS"
-.
.TP
\fB\-\-keep\-file\-descriptors\fR
-Exec in Ruby 2\.0 began discarding non\-standard file descriptors\. When this flag is passed, exec will revert to the 1\.9 behaviour of passing all file descriptors to the new process\.
-.
+Passes all file descriptors to the new processes\. Default is true from bundler version 2\.2\.26\. Setting it to false is now deprecated\.
.SH "BUNDLE INSTALL \-\-BINSTUBS"
If you use the \fB\-\-binstubs\fR flag in bundle install(1) \fIbundle\-install\.1\.html\fR, Bundler will automatically create a directory (which defaults to \fBapp_root/bin\fR) containing all of the executables available from gems in the bundle\.
-.
.P
After using \fB\-\-binstubs\fR, \fBbin/rspec spec/my_spec\.rb\fR is identical to \fBbundle exec rspec spec/my_spec\.rb\fR\.
-.
.SH "ENVIRONMENT MODIFICATIONS"
\fBbundle exec\fR makes a number of changes to the shell environment, then executes the command you specify in full\.
-.
.IP "\(bu" 4
-make sure that it\'s still possible to shell out to \fBbundle\fR from inside a command invoked by \fBbundle exec\fR (using \fB$BUNDLE_BIN_PATH\fR)
-.
+make sure that it's still possible to shell out to \fBbundle\fR from inside a command invoked by \fBbundle exec\fR (using \fB$BUNDLE_BIN_PATH\fR)
.IP "\(bu" 4
put the directory containing executables (like \fBrails\fR, \fBrspec\fR, \fBrackup\fR) for your bundle on \fB$PATH\fR
-.
.IP "\(bu" 4
make sure that if bundler is invoked in the subshell, it uses the same \fBGemfile\fR (by setting \fBBUNDLE_GEMFILE\fR)
-.
.IP "\(bu" 4
add \fB\-rbundler/setup\fR to \fB$RUBYOPT\fR, which makes sure that Ruby programs invoked in the subshell can see the gems in the bundle
-.
.IP "" 0
-.
.P
It also modifies Rubygems:
-.
.IP "\(bu" 4
disallow loading additional gems not in the bundle
-.
.IP "\(bu" 4
-modify the \fBgem\fR method to be a no\-op if a gem matching the requirements is in the bundle, and to raise a \fBGem::LoadError\fR if it\'s not
-.
+modify the \fBgem\fR method to be a no\-op if a gem matching the requirements is in the bundle, and to raise a \fBGem::LoadError\fR if it's not
.IP "\(bu" 4
Define \fBGem\.refresh\fR to be a no\-op, since the source index is always frozen when using bundler, and to prevent gems from the system leaking into the environment
-.
.IP "\(bu" 4
Override \fBGem\.bin_path\fR to use the gems in the bundle, making system executables work
-.
.IP "\(bu" 4
Add all gems in the bundle into Gem\.loaded_specs
-.
.IP "" 0
-.
.P
-Finally, \fBbundle exec\fR also implicitly modifies \fBGemfile\.lock\fR if the lockfile and the Gemfile do not match\. Bundler needs the Gemfile to determine things such as a gem\'s groups, \fBautorequire\fR, and platforms, etc\., and that information isn\'t stored in the lockfile\. The Gemfile and lockfile must be synced in order to \fBbundle exec\fR successfully, so \fBbundle exec\fR updates the lockfile beforehand\.
-.
+Finally, \fBbundle exec\fR also implicitly modifies \fBGemfile\.lock\fR if the lockfile and the Gemfile do not match\. Bundler needs the Gemfile to determine things such as a gem's groups, \fBautorequire\fR, and platforms, etc\., and that information isn't stored in the lockfile\. The Gemfile and lockfile must be synced in order to \fBbundle exec\fR successfully, so \fBbundle exec\fR updates the lockfile beforehand\.
.SS "Loading"
By default, when attempting to \fBbundle exec\fR to a file with a ruby shebang, Bundler will \fBKernel\.load\fR that file instead of using \fBKernel\.exec\fR\. For the vast majority of cases, this is a performance improvement\. In a rare few cases, this could cause some subtle side\-effects (such as dependence on the exact contents of \fB$0\fR or \fB__FILE__\fR) and the optimization can be disabled by enabling the \fBdisable_exec_load\fR setting\.
-.
.SS "Shelling out"
-Any Ruby code that opens a subshell (like \fBsystem\fR, backticks, or \fB%x{}\fR) will automatically use the current Bundler environment\. If you need to shell out to a Ruby command that is not part of your current bundle, use the \fBwith_unbundled_env\fR method with a block\. Any subshells created inside the block will be given the environment present before Bundler was activated\. For example, Homebrew commands run Ruby, but don\'t work inside a bundle:
-.
+Any Ruby code that opens a subshell (like \fBsystem\fR, backticks, or \fB%x{}\fR) will automatically use the current Bundler environment\. If you need to shell out to a Ruby command that is not part of your current bundle, use the \fBwith_unbundled_env\fR method with a block\. Any subshells created inside the block will be given the environment present before Bundler was activated\. For example, Homebrew commands run Ruby, but don't work inside a bundle:
.IP "" 4
-.
.nf
-
Bundler\.with_unbundled_env do
`brew install wget`
end
-.
.fi
-.
.IP "" 0
-.
.P
Using \fBwith_unbundled_env\fR is also necessary if you are shelling out to a different bundle\. Any Bundler commands run in a subshell will inherit the current Gemfile, so commands that need to run in the context of a different bundle also need to use \fBwith_unbundled_env\fR\.
-.
.IP "" 4
-.
.nf
-
Bundler\.with_unbundled_env do
Dir\.chdir "/other/bundler/project" do
`bundle exec \./script`
end
end
-.
.fi
-.
.IP "" 0
-.
.P
Bundler provides convenience helpers that wrap \fBsystem\fR and \fBexec\fR, and they can be used like this:
-.
.IP "" 4
-.
.nf
-
-Bundler\.clean_system(\'brew install wget\')
-Bundler\.clean_exec(\'brew install wget\')
-.
+Bundler\.clean_system('brew install wget')
+Bundler\.clean_exec('brew install wget')
.fi
-.
.IP "" 0
-.
.SH "RUBYGEMS PLUGINS"
At present, the Rubygems plugin system requires all files named \fBrubygems_plugin\.rb\fR on the load path of \fIany\fR installed gem when any Ruby code requires \fBrubygems\.rb\fR\. This includes executables installed into the system, like \fBrails\fR, \fBrackup\fR, and \fBrspec\fR\.
-.
.P
Since Rubygems plugins can contain arbitrary Ruby code, they commonly end up activating themselves or their dependencies\.
-.
.P
For instance, the \fBgemcutter 0\.5\fR gem depended on \fBjson_pure\fR\. If you had that version of gemcutter installed (even if you \fIalso\fR had a newer version without this problem), Rubygems would activate \fBgemcutter 0\.5\fR and \fBjson_pure <latest>\fR\.
-.
.P
If your Gemfile(5) also contained \fBjson_pure\fR (or a gem with a dependency on \fBjson_pure\fR), the latest version on your system might conflict with the version in your Gemfile(5), or the snapshot version in your \fBGemfile\.lock\fR\.
-.
.P
If this happens, bundler will say:
-.
.IP "" 4
-.
.nf
-
You have already activated json_pure 1\.4\.6 but your Gemfile
requires json_pure 1\.4\.3\. Consider using bundle exec\.
-.
.fi
-.
.IP "" 0
-.
.P
In this situation, you almost certainly want to remove the underlying gem with the problematic gem plugin\. In general, the authors of these plugins (in this case, the \fBgemcutter\fR gem) have released newer versions that are more careful in their plugins\.
-.
.P
You can find a list of all the gems containing gem plugins by running
-.
.IP "" 4
-.
.nf
-
-ruby \-e "puts Gem\.find_files(\'rubygems_plugin\.rb\')"
-.
+ruby \-e "puts Gem\.find_files('rubygems_plugin\.rb')"
.fi
-.
.IP "" 0
-.
.P
-At the very least, you should remove all but the newest version of each gem plugin, and also remove all gem plugins that you aren\'t using (\fBgem uninstall gem_name\fR)\.
+At the very least, you should remove all but the newest version of each gem plugin, and also remove all gem plugins that you aren't using (\fBgem uninstall gem_name\fR)\.
diff --git a/lib/bundler/man/bundle-exec.1.ronn b/lib/bundler/man/bundle-exec.1.ronn
index 05948095e2..9d5b559f26 100644
--- a/lib/bundler/man/bundle-exec.1.ronn
+++ b/lib/bundler/man/bundle-exec.1.ronn
@@ -21,9 +21,8 @@ available on your shell's `$PATH`.
## OPTIONS
* `--keep-file-descriptors`:
- Exec in Ruby 2.0 began discarding non-standard file descriptors. When this
- flag is passed, exec will revert to the 1.9 behaviour of passing all file
- descriptors to the new process.
+ Passes all file descriptors to the new processes. Default is true from
+ bundler version 2.2.26. Setting it to false is now deprecated.
## BUNDLE INSTALL --BINSTUBS
diff --git a/lib/bundler/man/bundle-gem.1 b/lib/bundler/man/bundle-gem.1
index ea64871fb6..89eb9f1980 100644
--- a/lib/bundler/man/bundle-gem.1
+++ b/lib/bundler/man/bundle-gem.1
@@ -1,105 +1,69 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-GEM" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-GEM" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-gem\fR \- Generate a project skeleton for creating a rubygem
-.
.SH "SYNOPSIS"
\fBbundle gem\fR \fIGEM_NAME\fR \fIOPTIONS\fR
-.
.SH "DESCRIPTION"
Generates a directory named \fBGEM_NAME\fR with a \fBRakefile\fR, \fBGEM_NAME\.gemspec\fR, and other supporting files and directories that can be used to develop a rubygem with that name\.
-.
.P
Run \fBrake \-T\fR in the resulting project for a list of Rake tasks that can be used to test and publish the gem to rubygems\.org\.
-.
.P
-The generated project skeleton can be customized with OPTIONS, as explained below\. Note that these options can also be specified via Bundler\'s global configuration file using the following names:
-.
+The generated project skeleton can be customized with OPTIONS, as explained below\. Note that these options can also be specified via Bundler's global configuration file using the following names:
.IP "\(bu" 4
\fBgem\.coc\fR
-.
.IP "\(bu" 4
\fBgem\.mit\fR
-.
.IP "\(bu" 4
\fBgem\.test\fR
-.
.IP "" 0
-.
.SH "OPTIONS"
-.
.IP "\(bu" 4
\fB\-\-exe\fR or \fB\-b\fR or \fB\-\-bin\fR: Specify that Bundler should create a binary executable (as \fBexe/GEM_NAME\fR) in the generated rubygem project\. This binary will also be added to the \fBGEM_NAME\.gemspec\fR manifest\. This behavior is disabled by default\.
-.
.IP "\(bu" 4
\fB\-\-no\-exe\fR: Do not create a binary (overrides \fB\-\-exe\fR specified in the global config)\.
-.
.IP "\(bu" 4
-\fB\-\-coc\fR: Add a \fBCODE_OF_CONDUCT\.md\fR file to the root of the generated project\. If this option is unspecified, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
-.
+\fB\-\-coc\fR: Add a \fBCODE_OF_CONDUCT\.md\fR file to the root of the generated project\. If this option is unspecified, an interactive prompt will be displayed and the answer will be saved in Bundler's global config for future \fBbundle gem\fR use\.
.IP "\(bu" 4
\fB\-\-no\-coc\fR: Do not create a \fBCODE_OF_CONDUCT\.md\fR (overrides \fB\-\-coc\fR specified in the global config)\.
-.
.IP "\(bu" 4
\fB\-\-ext=c\fR, \fB\-\-ext=rust\fR Add boilerplate for C or Rust (currently magnus \fIhttps://docs\.rs/magnus\fR based) extension code to the generated project\. This behavior is disabled by default\.
-.
.IP "\(bu" 4
\fB\-\-no\-ext\fR: Do not add extension code (overrides \fB\-\-ext\fR specified in the global config)\.
-.
.IP "\(bu" 4
-\fB\-\-mit\fR: Add an MIT license to a \fBLICENSE\.txt\fR file in the root of the generated project\. Your name from the global git config is used for the copyright statement\. If this option is unspecified, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
-.
+\fB\-\-mit\fR: Add an MIT license to a \fBLICENSE\.txt\fR file in the root of the generated project\. Your name from the global git config is used for the copyright statement\. If this option is unspecified, an interactive prompt will be displayed and the answer will be saved in Bundler's global config for future \fBbundle gem\fR use\.
.IP "\(bu" 4
\fB\-\-no\-mit\fR: Do not create a \fBLICENSE\.txt\fR (overrides \fB\-\-mit\fR specified in the global config)\.
-.
.IP "\(bu" 4
\fB\-t\fR, \fB\-\-test=minitest\fR, \fB\-\-test=rspec\fR, \fB\-\-test=test\-unit\fR: Specify the test framework that Bundler should use when generating the project\. Acceptable values are \fBminitest\fR, \fBrspec\fR and \fBtest\-unit\fR\. The \fBGEM_NAME\.gemspec\fR will be configured and a skeleton test/spec directory will be created based on this option\. Given no option is specified:
-.
.IP
-When Bundler is configured to generate tests, this defaults to Bundler\'s global config setting \fBgem\.test\fR\.
-.
+When Bundler is configured to generate tests, this defaults to Bundler's global config setting \fBgem\.test\fR\.
.IP
When Bundler is configured to not generate tests, an interactive prompt will be displayed and the answer will be used for the current rubygem project\.
-.
.IP
-When Bundler is unconfigured, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
-.
+When Bundler is unconfigured, an interactive prompt will be displayed and the answer will be saved in Bundler's global config for future \fBbundle gem\fR use\.
.IP "\(bu" 4
\fB\-\-ci\fR, \fB\-\-ci=github\fR, \fB\-\-ci=gitlab\fR, \fB\-\-ci=circle\fR: Specify the continuous integration service that Bundler should use when generating the project\. Acceptable values are \fBgithub\fR, \fBgitlab\fR and \fBcircle\fR\. A configuration file will be generated in the project directory\. Given no option is specified:
-.
.IP
-When Bundler is configured to generate CI files, this defaults to Bundler\'s global config setting \fBgem\.ci\fR\.
-.
+When Bundler is configured to generate CI files, this defaults to Bundler's global config setting \fBgem\.ci\fR\.
.IP
When Bundler is configured to not generate CI files, an interactive prompt will be displayed and the answer will be used for the current rubygem project\.
-.
.IP
-When Bundler is unconfigured, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
-.
+When Bundler is unconfigured, an interactive prompt will be displayed and the answer will be saved in Bundler's global config for future \fBbundle gem\fR use\.
.IP "\(bu" 4
-\fB\-\-linter\fR, \fB\-\-linter=rubocop\fR, \fB\-\-linter=standard\fR: Specify the linter and code formatter that Bundler should add to the project\'s development dependencies\. Acceptable values are \fBrubocop\fR and \fBstandard\fR\. A configuration file will be generated in the project directory\. Given no option is specified:
-.
+\fB\-\-linter\fR, \fB\-\-linter=rubocop\fR, \fB\-\-linter=standard\fR: Specify the linter and code formatter that Bundler should add to the project's development dependencies\. Acceptable values are \fBrubocop\fR and \fBstandard\fR\. A configuration file will be generated in the project directory\. Given no option is specified:
.IP
-When Bundler is configured to add a linter, this defaults to Bundler\'s global config setting \fBgem\.linter\fR\.
-.
+When Bundler is configured to add a linter, this defaults to Bundler's global config setting \fBgem\.linter\fR\.
.IP
When Bundler is configured not to add a linter, an interactive prompt will be displayed and the answer will be used for the current rubygem project\.
-.
.IP
-When Bundler is unconfigured, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
-.
+When Bundler is unconfigured, an interactive prompt will be displayed and the answer will be saved in Bundler's global config for future \fBbundle gem\fR use\.
.IP "\(bu" 4
\fB\-e\fR, \fB\-\-edit[=EDITOR]\fR: Open the resulting GEM_NAME\.gemspec in EDITOR, or the default editor if not specified\. The default is \fB$BUNDLER_EDITOR\fR, \fB$VISUAL\fR, or \fB$EDITOR\fR\.
-.
.IP "" 0
-.
.SH "SEE ALSO"
-.
.IP "\(bu" 4
bundle config(1) \fIbundle\-config\.1\.html\fR
-.
.IP "" 0
diff --git a/lib/bundler/man/bundle-help.1 b/lib/bundler/man/bundle-help.1
index a3b059ea07..441ec12735 100644
--- a/lib/bundler/man/bundle-help.1
+++ b/lib/bundler/man/bundle-help.1
@@ -1,13 +1,9 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-HELP" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-HELP" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-help\fR \- Displays detailed help for each subcommand
-.
.SH "SYNOPSIS"
\fBbundle help\fR [COMMAND]
-.
.SH "DESCRIPTION"
Displays detailed help for the given subcommand\. You can specify a single \fBCOMMAND\fR at the same time\. When \fBCOMMAND\fR is omitted, help for \fBhelp\fR command will be displayed\.
diff --git a/lib/bundler/man/bundle-info.1 b/lib/bundler/man/bundle-info.1
index 5af60c6a77..287965c06a 100644
--- a/lib/bundler/man/bundle-info.1
+++ b/lib/bundler/man/bundle-info.1
@@ -1,19 +1,13 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-INFO" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-INFO" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-info\fR \- Show information for the given gem in your bundle
-.
.SH "SYNOPSIS"
-\fBbundle info\fR [GEM] [\-\-path]
-.
+\fBbundle info\fR [GEM_NAME] [\-\-path]
.SH "DESCRIPTION"
-Print the basic information about the provided GEM such as homepage, version, path and summary\.
-.
+Given a gem name present in your bundle, print the basic information about it such as homepage, version, path and summary\.
.SH "OPTIONS"
-.
.TP
\fB\-\-path\fR
Print the path of the given gem
diff --git a/lib/bundler/man/bundle-info.1.ronn b/lib/bundler/man/bundle-info.1.ronn
index 47e457aa3c..cecdeb564f 100644
--- a/lib/bundler/man/bundle-info.1.ronn
+++ b/lib/bundler/man/bundle-info.1.ronn
@@ -3,13 +3,13 @@ bundle-info(1) -- Show information for the given gem in your bundle
## SYNOPSIS
-`bundle info` [GEM]
+`bundle info` [GEM_NAME]
[--path]
## DESCRIPTION
-Print the basic information about the provided GEM such as homepage, version,
-path and summary.
+Given a gem name present in your bundle, print the basic information about it
+ such as homepage, version, path and summary.
## OPTIONS
diff --git a/lib/bundler/man/bundle-init.1 b/lib/bundler/man/bundle-init.1
index e93b4fd5e9..d13f8ddc3b 100644
--- a/lib/bundler/man/bundle-init.1
+++ b/lib/bundler/man/bundle-init.1
@@ -1,29 +1,20 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-INIT" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-INIT" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-init\fR \- Generates a Gemfile into the current working directory
-.
.SH "SYNOPSIS"
\fBbundle init\fR [\-\-gemspec=FILE]
-.
.SH "DESCRIPTION"
Init generates a default [\fBGemfile(5)\fR][Gemfile(5)] in the current working directory\. When adding a [\fBGemfile(5)\fR][Gemfile(5)] to a gem with a gemspec, the \fB\-\-gemspec\fR option will automatically add each dependency listed in the gemspec file to the newly created [\fBGemfile(5)\fR][Gemfile(5)]\.
-.
.SH "OPTIONS"
-.
.TP
\fB\-\-gemspec\fR
Use the specified \.gemspec to create the [\fBGemfile(5)\fR][Gemfile(5)]
-.
.TP
\fB\-\-gemfile\fR
Use the specified name for the gemfile instead of \fBGemfile\fR
-.
.SH "FILES"
Included in the default [\fBGemfile(5)\fR][Gemfile(5)] generated is the line \fB# frozen_string_literal: true\fR\. This is a magic comment supported for the first time in Ruby 2\.3\. The presence of this line results in all string literals in the file being implicitly frozen\.
-.
.SH "SEE ALSO"
Gemfile(5) \fIhttps://bundler\.io/man/gemfile\.5\.html\fR
diff --git a/lib/bundler/man/bundle-inject.1 b/lib/bundler/man/bundle-inject.1
index 657d5ef4a7..086fc88156 100644
--- a/lib/bundler/man/bundle-inject.1
+++ b/lib/bundler/man/bundle-inject.1
@@ -1,36 +1,23 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-INJECT" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-INJECT" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-inject\fR \- Add named gem(s) with version requirements to Gemfile
-.
.SH "SYNOPSIS"
\fBbundle inject\fR [GEM] [VERSION]
-.
.SH "DESCRIPTION"
Adds the named gem(s) with their version requirements to the resolved [\fBGemfile(5)\fR][Gemfile(5)]\.
-.
.P
-This command will add the gem to both your [\fBGemfile(5)\fR][Gemfile(5)] and Gemfile\.lock if it isn\'t listed yet\.
-.
+This command will add the gem to both your [\fBGemfile(5)\fR][Gemfile(5)] and Gemfile\.lock if it isn't listed yet\.
.P
Example:
-.
.IP "" 4
-.
.nf
-
bundle install
-bundle inject \'rack\' \'> 0\'
-.
+bundle inject 'rack' '> 0'
.fi
-.
.IP "" 0
-.
.P
-This will inject the \'rack\' gem with a version greater than 0 in your [\fBGemfile(5)\fR][Gemfile(5)] and Gemfile\.lock\.
-.
+This will inject the 'rack' gem with a version greater than 0 in your [\fBGemfile(5)\fR][Gemfile(5)] and Gemfile\.lock\.
.P
The \fBbundle inject\fR command was deprecated in Bundler 2\.1 and will be removed in Bundler 3\.0\.
diff --git a/lib/bundler/man/bundle-install.1 b/lib/bundler/man/bundle-install.1
index 8f144692f3..762c99e7c0 100644
--- a/lib/bundler/man/bundle-install.1
+++ b/lib/bundler/man/bundle-install.1
@@ -1,313 +1,215 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-INSTALL" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-INSTALL" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-install\fR \- Install the dependencies specified in your Gemfile
-.
.SH "SYNOPSIS"
-\fBbundle install\fR [\-\-binstubs[=DIRECTORY]] [\-\-clean] [\-\-deployment] [\-\-frozen] [\-\-full\-index] [\-\-gemfile=GEMFILE] [\-\-jobs=NUMBER] [\-\-local] [\-\-no\-cache] [\-\-no\-prune] [\-\-path PATH] [\-\-quiet] [\-\-redownload] [\-\-retry=NUMBER] [\-\-shebang] [\-\-standalone[=GROUP[ GROUP\.\.\.]]] [\-\-system] [\-\-trust\-policy=POLICY] [\-\-with=GROUP[ GROUP\.\.\.]] [\-\-without=GROUP[ GROUP\.\.\.]]
-.
+\fBbundle install\fR [\-\-binstubs[=DIRECTORY]] [\-\-clean] [\-\-deployment] [\-\-frozen] [\-\-full\-index] [\-\-gemfile=GEMFILE] [\-\-jobs=NUMBER] [\-\-local] [\-\-no\-cache] [\-\-no\-prune] [\-\-path PATH] [\-\-prefer\-local] [\-\-quiet] [\-\-redownload] [\-\-retry=NUMBER] [\-\-shebang] [\-\-standalone[=GROUP[ GROUP\|\.\|\.\|\.]]] [\-\-system] [\-\-trust\-policy=POLICY] [\-\-with=GROUP[ GROUP\|\.\|\.\|\.]] [\-\-without=GROUP[ GROUP\|\.\|\.\|\.]]
.SH "DESCRIPTION"
Install the gems specified in your Gemfile(5)\. If this is the first time you run bundle install (and a \fBGemfile\.lock\fR does not exist), Bundler will fetch all remote sources, resolve dependencies and install all needed gems\.
-.
.P
If a \fBGemfile\.lock\fR does exist, and you have not updated your Gemfile(5), Bundler will fetch all remote sources, but use the dependencies specified in the \fBGemfile\.lock\fR instead of resolving dependencies\.
-.
.P
If a \fBGemfile\.lock\fR does exist, and you have updated your Gemfile(5), Bundler will use the dependencies in the \fBGemfile\.lock\fR for all gems that you did not update, but will re\-resolve the dependencies of gems that you did update\. You can find more information about this update process below under \fICONSERVATIVE UPDATING\fR\.
-.
.SH "OPTIONS"
The \fB\-\-clean\fR, \fB\-\-deployment\fR, \fB\-\-frozen\fR, \fB\-\-no\-prune\fR, \fB\-\-path\fR, \fB\-\-shebang\fR, \fB\-\-system\fR, \fB\-\-without\fR and \fB\-\-with\fR options are deprecated because they only make sense if they are applied to every subsequent \fBbundle install\fR run automatically and that requires \fBbundler\fR to silently remember them\. Since \fBbundler\fR will no longer remember CLI flags in future versions, \fBbundle config\fR (see bundle\-config(1)) should be used to apply them permanently\.
-.
.TP
\fB\-\-binstubs[=<directory>]\fR
Binstubs are scripts that wrap around executables\. Bundler creates a small Ruby file (a binstub) that loads Bundler, runs the command, and puts it in \fBbin/\fR\. This lets you link the binstub inside of an application to the exact gem version the application needs\.
-.
.IP
-Creates a directory (defaults to \fB~/bin\fR) and places any executables from the gem there\. These executables run in Bundler\'s context\. If used, you might add this directory to your environment\'s \fBPATH\fR variable\. For instance, if the \fBrails\fR gem comes with a \fBrails\fR executable, this flag will create a \fBbin/rails\fR executable that ensures that all referred dependencies will be resolved using the bundled gems\.
-.
+Creates a directory (defaults to \fB~/bin\fR) and places any executables from the gem there\. These executables run in Bundler's context\. If used, you might add this directory to your environment's \fBPATH\fR variable\. For instance, if the \fBrails\fR gem comes with a \fBrails\fR executable, this flag will create a \fBbin/rails\fR executable that ensures that all referred dependencies will be resolved using the bundled gems\.
.TP
\fB\-\-clean\fR
-On finishing the installation Bundler is going to remove any gems not present in the current Gemfile(5)\. Don\'t worry, gems currently in use will not be removed\.
-.
+On finishing the installation Bundler is going to remove any gems not present in the current Gemfile(5)\. Don't worry, gems currently in use will not be removed\.
.IP
This option is deprecated in favor of the \fBclean\fR setting\.
-.
.TP
\fB\-\-deployment\fR
-In \fIdeployment mode\fR, Bundler will \'roll\-out\' the bundle for production or CI use\. Please check carefully if you want to have this option enabled in your development environment\.
-.
+In \fIdeployment mode\fR, Bundler will 'roll\-out' the bundle for production or CI use\. Please check carefully if you want to have this option enabled in your development environment\.
.IP
This option is deprecated in favor of the \fBdeployment\fR setting\.
-.
.TP
\fB\-\-redownload\fR
Force download every gem, even if the required versions are already available locally\.
-.
.TP
\fB\-\-frozen\fR
Do not allow the Gemfile\.lock to be updated after this install\. Exits non\-zero if there are going to be changes to the Gemfile\.lock\.
-.
.IP
This option is deprecated in favor of the \fBfrozen\fR setting\.
-.
.TP
\fB\-\-full\-index\fR
-Bundler will not call Rubygems\' API endpoint (default) but download and cache a (currently big) index file of all gems\. Performance can be improved for large bundles that seldom change by enabling this option\.
-.
+Bundler will not call Rubygems' API endpoint (default) but download and cache a (currently big) index file of all gems\. Performance can be improved for large bundles that seldom change by enabling this option\.
.TP
\fB\-\-gemfile=<gemfile>\fR
-The location of the Gemfile(5) which Bundler should use\. This defaults to a Gemfile(5) in the current working directory\. In general, Bundler will assume that the location of the Gemfile(5) is also the project\'s root and will try to find \fBGemfile\.lock\fR and \fBvendor/cache\fR relative to this location\.
-.
+The location of the Gemfile(5) which Bundler should use\. This defaults to a Gemfile(5) in the current working directory\. In general, Bundler will assume that the location of the Gemfile(5) is also the project's root and will try to find \fBGemfile\.lock\fR and \fBvendor/cache\fR relative to this location\.
.TP
\fB\-\-jobs=[<number>]\fR, \fB\-j[<number>]\fR
The maximum number of parallel download and install jobs\. The default is the number of available processors\.
-.
.TP
\fB\-\-local\fR
-Do not attempt to connect to \fBrubygems\.org\fR\. Instead, Bundler will use the gems already present in Rubygems\' cache or in \fBvendor/cache\fR\. Note that if an appropriate platform\-specific gem exists on \fBrubygems\.org\fR it will not be found\.
-.
+Do not attempt to connect to \fBrubygems\.org\fR\. Instead, Bundler will use the gems already present in Rubygems' cache or in \fBvendor/cache\fR\. Note that if an appropriate platform\-specific gem exists on \fBrubygems\.org\fR it will not be found\.
.TP
\fB\-\-prefer\-local\fR
-Force using locally installed gems, or gems already present in Rubygems\' cache or in \fBvendor/cache\fR, when resolving, even if newer versions are available remotely\. Only attempt to connect to \fBrubygems\.org\fR for gems that are not present locally\.
-.
+Force using locally installed gems, or gems already present in Rubygems' cache or in \fBvendor/cache\fR, when resolving, even if newer versions are available remotely\. Only attempt to connect to \fBrubygems\.org\fR for gems that are not present locally\.
.TP
\fB\-\-no\-cache\fR
Do not update the cache in \fBvendor/cache\fR with the newly bundled gems\. This does not remove any gems in the cache but keeps the newly bundled gems from being cached during the install\.
-.
.TP
\fB\-\-no\-prune\fR
-Don\'t remove stale gems from the cache when the installation finishes\.
-.
+Don't remove stale gems from the cache when the installation finishes\.
.IP
This option is deprecated in favor of the \fBno_prune\fR setting\.
-.
.TP
\fB\-\-path=<path>\fR
-The location to install the specified gems to\. This defaults to Rubygems\' setting\. Bundler shares this location with Rubygems, \fBgem install \.\.\.\fR will have gem installed there, too\. Therefore, gems installed without a \fB\-\-path \.\.\.\fR setting will show up by calling \fBgem list\fR\. Accordingly, gems installed to other locations will not get listed\.
-.
+The location to install the specified gems to\. This defaults to Rubygems' setting\. Bundler shares this location with Rubygems, \fBgem install \|\.\|\.\|\.\fR will have gem installed there, too\. Therefore, gems installed without a \fB\-\-path \|\.\|\.\|\.\fR setting will show up by calling \fBgem list\fR\. Accordingly, gems installed to other locations will not get listed\.
.IP
This option is deprecated in favor of the \fBpath\fR setting\.
-.
.TP
\fB\-\-quiet\fR
Do not print progress information to the standard output\. Instead, Bundler will exit using a status code (\fB$?\fR)\.
-.
.TP
\fB\-\-retry=[<number>]\fR
Retry failed network or git requests for \fInumber\fR times\.
-.
.TP
\fB\-\-shebang=<ruby\-executable>\fR
Uses the specified ruby executable (usually \fBruby\fR) to execute the scripts created with \fB\-\-binstubs\fR\. In addition, if you use \fB\-\-binstubs\fR together with \fB\-\-shebang jruby\fR these executables will be changed to execute \fBjruby\fR instead\.
-.
.IP
This option is deprecated in favor of the \fBshebang\fR setting\.
-.
.TP
\fB\-\-standalone[=<list>]\fR
-Makes a bundle that can work without depending on Rubygems or Bundler at runtime\. A space separated list of groups to install has to be specified\. Bundler creates a directory named \fBbundle\fR and installs the bundle there\. It also generates a \fBbundle/bundler/setup\.rb\fR file to replace Bundler\'s own setup in the manner required\. Using this option implicitly sets \fBpath\fR, which is a [remembered option][REMEMBERED OPTIONS]\.
-.
+Makes a bundle that can work without depending on Rubygems or Bundler at runtime\. A space separated list of groups to install has to be specified\. Bundler creates a directory named \fBbundle\fR and installs the bundle there\. It also generates a \fBbundle/bundler/setup\.rb\fR file to replace Bundler's own setup in the manner required\. Using this option implicitly sets \fBpath\fR, which is a [remembered option][REMEMBERED OPTIONS]\.
.TP
\fB\-\-system\fR
-Installs the gems specified in the bundle to the system\'s Rubygems location\. This overrides any previous configuration of \fB\-\-path\fR\.
-.
+Installs the gems specified in the bundle to the system's Rubygems location\. This overrides any previous configuration of \fB\-\-path\fR\.
.IP
This option is deprecated in favor of the \fBsystem\fR setting\.
-.
.TP
\fB\-\-trust\-policy=[<policy>]\fR
Apply the Rubygems security policy \fIpolicy\fR, where policy is one of \fBHighSecurity\fR, \fBMediumSecurity\fR, \fBLowSecurity\fR, \fBAlmostNoSecurity\fR, or \fBNoSecurity\fR\. For more details, please see the Rubygems signing documentation linked below in \fISEE ALSO\fR\.
-.
.TP
\fB\-\-with=<list>\fR
A space\-separated list of groups referencing gems to install\. If an optional group is given it is installed\. If a group is given that is in the remembered list of groups given to \-\-without, it is removed from that list\.
-.
.IP
This option is deprecated in favor of the \fBwith\fR setting\.
-.
.TP
\fB\-\-without=<list>\fR
A space\-separated list of groups referencing gems to skip during installation\. If a group is given that is in the remembered list of groups given to \-\-with, it is removed from that list\.
-.
.IP
This option is deprecated in favor of the \fBwithout\fR setting\.
-.
.SH "DEPLOYMENT MODE"
-Bundler\'s defaults are optimized for development\. To switch to defaults optimized for deployment and for CI, use the \fB\-\-deployment\fR flag\. Do not activate deployment mode on development machines, as it will cause an error when the Gemfile(5) is modified\.
-.
+Bundler's defaults are optimized for development\. To switch to defaults optimized for deployment and for CI, use the \fB\-\-deployment\fR flag\. Do not activate deployment mode on development machines, as it will cause an error when the Gemfile(5) is modified\.
.IP "1." 4
A \fBGemfile\.lock\fR is required\.
-.
.IP
To ensure that the same versions of the gems you developed with and tested with are also used in deployments, a \fBGemfile\.lock\fR is required\.
-.
.IP
This is mainly to ensure that you remember to check your \fBGemfile\.lock\fR into version control\.
-.
.IP "2." 4
The \fBGemfile\.lock\fR must be up to date
-.
.IP
In development, you can modify your Gemfile(5) and re\-run \fBbundle install\fR to \fIconservatively update\fR your \fBGemfile\.lock\fR snapshot\.
-.
.IP
In deployment, your \fBGemfile\.lock\fR should be up\-to\-date with changes made in your Gemfile(5)\.
-.
.IP "3." 4
Gems are installed to \fBvendor/bundle\fR not your default system location
-.
.IP
-In development, it\'s convenient to share the gems used in your application with other applications and other scripts that run on the system\.
-.
+In development, it's convenient to share the gems used in your application with other applications and other scripts that run on the system\.
.IP
In deployment, isolation is a more important default\. In addition, the user deploying the application may not have permission to install gems to the system, or the web server may not have permission to read them\.
-.
.IP
As a result, \fBbundle install \-\-deployment\fR installs gems to the \fBvendor/bundle\fR directory in the application\. This may be overridden using the \fB\-\-path\fR option\.
-.
.IP "" 0
-.
.SH "INSTALLING GROUPS"
By default, \fBbundle install\fR will install all gems in all groups in your Gemfile(5), except those declared for a different platform\.
-.
.P
However, you can explicitly tell Bundler to skip installing certain groups with the \fB\-\-without\fR option\. This option takes a space\-separated list of groups\.
-.
.P
While the \fB\-\-without\fR option will skip \fIinstalling\fR the gems in the specified groups, it will still \fIdownload\fR those gems and use them to resolve the dependencies of every gem in your Gemfile(5)\.
-.
.P
This is so that installing a different set of groups on another machine (such as a production server) will not change the gems and versions that you have already developed and tested against\.
-.
.P
\fBBundler offers a rock\-solid guarantee that the third\-party code you are running in development and testing is also the third\-party code you are running in production\. You can choose to exclude some of that code in different environments, but you will never be caught flat\-footed by different versions of third\-party code being used in different environments\.\fR
-.
.P
For a simple illustration, consider the following Gemfile(5):
-.
.IP "" 4
-.
.nf
+source 'https://rubygems\.org'
-source \'https://rubygems\.org\'
-
-gem \'sinatra\'
+gem 'sinatra'
group :production do
- gem \'rack\-perftools\-profiler\'
+ gem 'rack\-perftools\-profiler'
end
-.
.fi
-.
.IP "" 0
-.
.P
In this case, \fBsinatra\fR depends on any version of Rack (\fB>= 1\.0\fR), while \fBrack\-perftools\-profiler\fR depends on 1\.x (\fB~> 1\.0\fR)\.
-.
.P
When you run \fBbundle install \-\-without production\fR in development, we look at the dependencies of \fBrack\-perftools\-profiler\fR as well\. That way, you do not spend all your time developing against Rack 2\.0, using new APIs unavailable in Rack 1\.x, only to have Bundler switch to Rack 1\.2 when the \fBproduction\fR group \fIis\fR used\.
-.
.P
This should not cause any problems in practice, because we do not attempt to \fBinstall\fR the gems in the excluded groups, and only evaluate as part of the dependency resolution process\.
-.
.P
This also means that you cannot include different versions of the same gem in different groups, because doing so would result in different sets of dependencies used in development and production\. Because of the vagaries of the dependency resolution process, this usually affects more than the gems you list in your Gemfile(5), and can (surprisingly) radically change the gems you are using\.
-.
.SH "THE GEMFILE\.LOCK"
When you run \fBbundle install\fR, Bundler will persist the full names and versions of all gems that you used (including dependencies of the gems specified in the Gemfile(5)) into a file called \fBGemfile\.lock\fR\.
-.
.P
Bundler uses this file in all subsequent calls to \fBbundle install\fR, which guarantees that you always use the same exact code, even as your application moves across machines\.
-.
.P
Because of the way dependency resolution works, even a seemingly small change (for instance, an update to a point\-release of a dependency of a gem in your Gemfile(5)) can result in radically different gems being needed to satisfy all dependencies\.
-.
.P
As a result, you \fBSHOULD\fR check your \fBGemfile\.lock\fR into version control, in both applications and gems\. If you do not, every machine that checks out your repository (including your production server) will resolve all dependencies again, which will result in different versions of third\-party code being used if \fBany\fR of the gems in the Gemfile(5) or any of their dependencies have been updated\.
-.
.P
When Bundler first shipped, the \fBGemfile\.lock\fR was included in the \fB\.gitignore\fR file included with generated gems\. Over time, however, it became clear that this practice forces the pain of broken dependencies onto new contributors, while leaving existing contributors potentially unaware of the problem\. Since \fBbundle install\fR is usually the first step towards a contribution, the pain of broken dependencies would discourage new contributors from contributing\. As a result, we have revised our guidance for gem authors to now recommend checking in the lock for gems\.
-.
.SH "CONSERVATIVE UPDATING"
When you make a change to the Gemfile(5) and then run \fBbundle install\fR, Bundler will update only the gems that you modified\.
-.
.P
In other words, if a gem that you \fBdid not modify\fR worked before you called \fBbundle install\fR, it will continue to use the exact same versions of all dependencies as it used before the update\.
-.
.P
-Let\'s take a look at an example\. Here\'s your original Gemfile(5):
-.
+Let's take a look at an example\. Here's your original Gemfile(5):
.IP "" 4
-.
.nf
+source 'https://rubygems\.org'
-source \'https://rubygems\.org\'
-
-gem \'actionpack\', \'2\.3\.8\'
-gem \'activemerchant\'
-.
+gem 'actionpack', '2\.3\.8'
+gem 'activemerchant'
.fi
-.
.IP "" 0
-.
.P
In this case, both \fBactionpack\fR and \fBactivemerchant\fR depend on \fBactivesupport\fR\. The \fBactionpack\fR gem depends on \fBactivesupport 2\.3\.8\fR and \fBrack ~> 1\.1\.0\fR, while the \fBactivemerchant\fR gem depends on \fBactivesupport >= 2\.3\.2\fR, \fBbraintree >= 2\.0\.0\fR, and \fBbuilder >= 2\.0\.0\fR\.
-.
.P
When the dependencies are first resolved, Bundler will select \fBactivesupport 2\.3\.8\fR, which satisfies the requirements of both gems in your Gemfile(5)\.
-.
.P
Next, you modify your Gemfile(5) to:
-.
.IP "" 4
-.
.nf
+source 'https://rubygems\.org'
-source \'https://rubygems\.org\'
-
-gem \'actionpack\', \'3\.0\.0\.rc\'
-gem \'activemerchant\'
-.
+gem 'actionpack', '3\.0\.0\.rc'
+gem 'activemerchant'
.fi
-.
.IP "" 0
-.
.P
The \fBactionpack 3\.0\.0\.rc\fR gem has a number of new dependencies, and updates the \fBactivesupport\fR dependency to \fB= 3\.0\.0\.rc\fR and the \fBrack\fR dependency to \fB~> 1\.2\.1\fR\.
-.
.P
When you run \fBbundle install\fR, Bundler notices that you changed the \fBactionpack\fR gem, but not the \fBactivemerchant\fR gem\. It evaluates the gems currently being used to satisfy its requirements:
-.
.TP
\fBactivesupport 2\.3\.8\fR
also used to satisfy a dependency in \fBactivemerchant\fR, which is not being updated
-.
.TP
\fBrack ~> 1\.1\.0\fR
not currently being used to satisfy another dependency
-.
.P
Because you did not explicitly ask to update \fBactivemerchant\fR, you would not expect it to suddenly stop working after updating \fBactionpack\fR\. However, satisfying the new \fBactivesupport 3\.0\.0\.rc\fR dependency of actionpack requires updating one of its dependencies\.
-.
.P
Even though \fBactivemerchant\fR declares a very loose dependency that theoretically matches \fBactivesupport 3\.0\.0\.rc\fR, Bundler treats gems in your Gemfile(5) that have not changed as an atomic unit together with their dependencies\. In this case, the \fBactivemerchant\fR dependency is treated as \fBactivemerchant 1\.7\.1 + activesupport 2\.3\.8\fR, so \fBbundle install\fR will report that it cannot update \fBactionpack\fR\.
-.
.P
To explicitly update \fBactionpack\fR, including its dependencies which other gems in the Gemfile(5) still depend on, run \fBbundle update actionpack\fR (see \fBbundle update(1)\fR)\.
-.
.P
\fBSummary\fR: In general, after making a change to the Gemfile(5) , you should first try to run \fBbundle install\fR, which will guarantee that no other gem in the Gemfile(5) is impacted by the change\. If that does not work, run bundle update(1) \fIbundle\-update\.1\.html\fR\.
-.
.SH "SEE ALSO"
-.
.IP "\(bu" 4
-Gem install docs \fIhttp://guides\.rubygems\.org/rubygems\-basics/#installing\-gems\fR
-.
+Gem install docs \fIhttps://guides\.rubygems\.org/rubygems\-basics/#installing\-gems\fR
.IP "\(bu" 4
-Rubygems signing docs \fIhttp://guides\.rubygems\.org/security/\fR
-.
+Rubygems signing docs \fIhttps://guides\.rubygems\.org/security/\fR
.IP "" 0
diff --git a/lib/bundler/man/bundle-install.1.ronn b/lib/bundler/man/bundle-install.1.ronn
index be9ed0f974..ed8169de05 100644
--- a/lib/bundler/man/bundle-install.1.ronn
+++ b/lib/bundler/man/bundle-install.1.ronn
@@ -14,6 +14,7 @@ bundle-install(1) -- Install the dependencies specified in your Gemfile
[--no-cache]
[--no-prune]
[--path PATH]
+ [--prefer-local]
[--quiet]
[--redownload]
[--retry=NUMBER]
@@ -378,5 +379,5 @@ does not work, run [bundle update(1)](bundle-update.1.html).
## SEE ALSO
-* [Gem install docs](http://guides.rubygems.org/rubygems-basics/#installing-gems)
-* [Rubygems signing docs](http://guides.rubygems.org/security/)
+* [Gem install docs](https://guides.rubygems.org/rubygems-basics/#installing-gems)
+* [Rubygems signing docs](https://guides.rubygems.org/security/)
diff --git a/lib/bundler/man/bundle-list.1 b/lib/bundler/man/bundle-list.1
index f24f62dd38..93db700091 100644
--- a/lib/bundler/man/bundle-list.1
+++ b/lib/bundler/man/bundle-list.1
@@ -1,49 +1,34 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-LIST" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-LIST" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-list\fR \- List all the gems in the bundle
-.
.SH "SYNOPSIS"
-\fBbundle list\fR [\-\-name\-only] [\-\-paths] [\-\-without\-group=GROUP[ GROUP\.\.\.]] [\-\-only\-group=GROUP[ GROUP\.\.\.]]
-.
+\fBbundle list\fR [\-\-name\-only] [\-\-paths] [\-\-without\-group=GROUP[ GROUP\|\.\|\.\|\.]] [\-\-only\-group=GROUP[ GROUP\|\.\|\.\|\.]]
.SH "DESCRIPTION"
Prints a list of all the gems in the bundle including their version\.
-.
.P
Example:
-.
.P
bundle list \-\-name\-only
-.
.P
bundle list \-\-paths
-.
.P
bundle list \-\-without\-group test
-.
.P
bundle list \-\-only\-group dev
-.
.P
bundle list \-\-only\-group dev test \-\-paths
-.
.SH "OPTIONS"
-.
.TP
\fB\-\-name\-only\fR
Print only the name of each gem\.
-.
.TP
\fB\-\-paths\fR
Print the path to each gem in the bundle\.
-.
.TP
\fB\-\-without\-group=<list>\fR
A space\-separated list of groups of gems to skip during printing\.
-.
.TP
\fB\-\-only\-group=<list>\fR
A space\-separated list of groups of gems to print\.
diff --git a/lib/bundler/man/bundle-lock.1 b/lib/bundler/man/bundle-lock.1
index 55d1035d77..c0190f91de 100644
--- a/lib/bundler/man/bundle-lock.1
+++ b/lib/bundler/man/bundle-lock.1
@@ -1,84 +1,60 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-LOCK" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-LOCK" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-lock\fR \- Creates / Updates a lockfile without installing
-.
.SH "SYNOPSIS"
\fBbundle lock\fR [\-\-update] [\-\-local] [\-\-print] [\-\-lockfile=PATH] [\-\-full\-index] [\-\-add\-platform] [\-\-remove\-platform] [\-\-patch] [\-\-minor] [\-\-major] [\-\-strict] [\-\-conservative]
-.
.SH "DESCRIPTION"
Lock the gems specified in Gemfile\.
-.
.SH "OPTIONS"
-.
.TP
\fB\-\-update=<*gems>\fR
Ignores the existing lockfile\. Resolve then updates lockfile\. Taking a list of gems or updating all gems if no list is given\.
-.
.TP
\fB\-\-local\fR
-Do not attempt to connect to \fBrubygems\.org\fR\. Instead, Bundler will use the gems already present in Rubygems\' cache or in \fBvendor/cache\fR\. Note that if a appropriate platform\-specific gem exists on \fBrubygems\.org\fR it will not be found\.
-.
+Do not attempt to connect to \fBrubygems\.org\fR\. Instead, Bundler will use the gems already present in Rubygems' cache or in \fBvendor/cache\fR\. Note that if a appropriate platform\-specific gem exists on \fBrubygems\.org\fR it will not be found\.
.TP
\fB\-\-print\fR
Prints the lockfile to STDOUT instead of writing to the file system\.
-.
.TP
\fB\-\-lockfile=<path>\fR
The path where the lockfile should be written to\.
-.
.TP
\fB\-\-full\-index\fR
Fall back to using the single\-file index of all gems\.
-.
.TP
\fB\-\-add\-platform\fR
Add a new platform to the lockfile, re\-resolving for the addition of that platform\.
-.
.TP
\fB\-\-remove\-platform\fR
Remove a platform from the lockfile\.
-.
.TP
\fB\-\-patch\fR
If updating, prefer updating only to next patch version\.
-.
.TP
\fB\-\-minor\fR
If updating, prefer updating only to next minor version\.
-.
.TP
\fB\-\-major\fR
If updating, prefer updating to next major version (default)\.
-.
.TP
\fB\-\-strict\fR
If updating, do not allow any gem to be updated past latest \-\-patch | \-\-minor | \-\-major\.
-.
.TP
\fB\-\-conservative\fR
If updating, use bundle install conservative update behavior and do not allow shared dependencies to be updated\.
-.
.SH "UPDATING ALL GEMS"
If you run \fBbundle lock\fR with \fB\-\-update\fR option without list of gems, bundler will ignore any previously installed gems and resolve all dependencies again based on the latest versions of all gems available in the sources\.
-.
.SH "UPDATING A LIST OF GEMS"
Sometimes, you want to update a single gem in the Gemfile(5), and leave the rest of the gems that you specified locked to the versions in the \fBGemfile\.lock\fR\.
-.
.P
For instance, you only want to update \fBnokogiri\fR, run \fBbundle lock \-\-update nokogiri\fR\.
-.
.P
Bundler will update \fBnokogiri\fR and any of its dependencies, but leave the rest of the gems that you specified locked to the versions in the \fBGemfile\.lock\fR\.
-.
.SH "SUPPORTING OTHER PLATFORMS"
-If you want your bundle to support platforms other than the one you\'re running locally, you can run \fBbundle lock \-\-add\-platform PLATFORM\fR to add PLATFORM to the lockfile, force bundler to re\-resolve and consider the new platform when picking gems, all without needing to have a machine that matches PLATFORM handy to install those platform\-specific gems on\.
-.
+If you want your bundle to support platforms other than the one you're running locally, you can run \fBbundle lock \-\-add\-platform PLATFORM\fR to add PLATFORM to the lockfile, force bundler to re\-resolve and consider the new platform when picking gems, all without needing to have a machine that matches PLATFORM handy to install those platform\-specific gems on\.
.P
For a full explanation of gem platforms, see \fBgem help platform\fR\.
-.
.SH "PATCH LEVEL OPTIONS"
See bundle update(1) \fIbundle\-update\.1\.html\fR for details\.
diff --git a/lib/bundler/man/bundle-open.1 b/lib/bundler/man/bundle-open.1
index ff44d1224f..38bacb67dc 100644
--- a/lib/bundler/man/bundle-open.1
+++ b/lib/bundler/man/bundle-open.1
@@ -1,51 +1,31 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-OPEN" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-OPEN" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-open\fR \- Opens the source directory for a gem in your bundle
-.
.SH "SYNOPSIS"
\fBbundle open\fR [GEM] [\-\-path=PATH]
-.
.SH "DESCRIPTION"
Opens the source directory of the provided GEM in your editor\.
-.
.P
For this to work the \fBEDITOR\fR or \fBBUNDLER_EDITOR\fR environment variable has to be set\.
-.
.P
Example:
-.
.IP "" 4
-.
.nf
-
-bundle open \'rack\'
-.
+bundle open 'rack'
.fi
-.
.IP "" 0
-.
.P
-Will open the source directory for the \'rack\' gem in your bundle\.
-.
+Will open the source directory for the 'rack' gem in your bundle\.
.IP "" 4
-.
.nf
-
-bundle open \'rack\' \-\-path \'README\.md\'
-.
+bundle open 'rack' \-\-path 'README\.md'
.fi
-.
.IP "" 0
-.
.P
-Will open the README\.md file of the \'rack\' gem source in your bundle\.
-.
+Will open the README\.md file of the 'rack' gem source in your bundle\.
.SH "OPTIONS"
-.
.TP
\fB\-\-path\fR
Specify GEM source relative path to open\.
diff --git a/lib/bundler/man/bundle-outdated.1 b/lib/bundler/man/bundle-outdated.1
index 8455b71b45..cee9d63155 100644
--- a/lib/bundler/man/bundle-outdated.1
+++ b/lib/bundler/man/bundle-outdated.1
@@ -1,148 +1,100 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-OUTDATED" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-OUTDATED" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-outdated\fR \- List installed gems with newer versions available
-.
.SH "SYNOPSIS"
\fBbundle outdated\fR [GEM] [\-\-local] [\-\-pre] [\-\-source] [\-\-strict] [\-\-parseable | \-\-porcelain] [\-\-group=GROUP] [\-\-groups] [\-\-patch|\-\-minor|\-\-major] [\-\-filter\-major] [\-\-filter\-minor] [\-\-filter\-patch] [\-\-only\-explicit]
-.
.SH "DESCRIPTION"
Outdated lists the names and versions of gems that have a newer version available in the given source\. Calling outdated with [GEM [GEM]] will only check for newer versions of the given gems\. Prerelease gems are ignored by default\. If your gems are up to date, Bundler will exit with a status of 0\. Otherwise, it will exit 1\.
-.
.SH "OPTIONS"
-.
.TP
\fB\-\-local\fR
Do not attempt to fetch gems remotely and use the gem cache instead\.
-.
.TP
\fB\-\-pre\fR
Check for newer pre\-release gems\.
-.
.TP
\fB\-\-source\fR
Check against a specific source\.
-.
.TP
\fB\-\-strict\fR
Only list newer versions allowed by your Gemfile requirements, also respecting conservative update flags (\-\-patch, \-\-minor, \-\-major)\.
-.
.TP
\fB\-\-parseable\fR, \fB\-\-porcelain\fR
Use minimal formatting for more parseable output\.
-.
.TP
\fB\-\-group\fR
List gems from a specific group\.
-.
.TP
\fB\-\-groups\fR
List gems organized by groups\.
-.
.TP
\fB\-\-minor\fR
Prefer updating only to next minor version\.
-.
.TP
\fB\-\-major\fR
Prefer updating to next major version (default)\.
-.
.TP
\fB\-\-patch\fR
Prefer updating only to next patch version\.
-.
.TP
\fB\-\-filter\-major\fR
Only list major newer versions\.
-.
.TP
\fB\-\-filter\-minor\fR
Only list minor newer versions\.
-.
.TP
\fB\-\-filter\-patch\fR
Only list patch newer versions\.
-.
.TP
\fB\-\-only\-explicit\fR
Only list gems specified in your Gemfile, not their dependencies\.
-.
.SH "PATCH LEVEL OPTIONS"
See bundle update(1) \fIbundle\-update\.1\.html\fR for details\.
-.
.SH "FILTERING OUTPUT"
The 3 filtering options do not affect the resolution of versions, merely what versions are shown in the output\.
-.
.P
If the regular output shows the following:
-.
.IP "" 4
-.
.nf
-
-* faker (newest 1\.6\.6, installed 1\.6\.5, requested ~> 1\.4) in groups "development, test"
-* hashie (newest 3\.4\.6, installed 1\.2\.0, requested = 1\.2\.0) in groups "default"
-* headless (newest 2\.3\.1, installed 2\.2\.3) in groups "test"
-.
+* Gem Current Latest Requested Groups
+* faker 1\.6\.5 1\.6\.6 ~> 1\.4 development, test
+* hashie 1\.2\.0 3\.4\.6 = 1\.2\.0 default
+* headless 2\.2\.3 2\.3\.1 = 2\.2\.3 test
.fi
-.
.IP "" 0
-.
.P
\fB\-\-filter\-major\fR would only show:
-.
.IP "" 4
-.
.nf
-
-* hashie (newest 3\.4\.6, installed 1\.2\.0, requested = 1\.2\.0) in groups "default"
-.
+* Gem Current Latest Requested Groups
+* hashie 1\.2\.0 3\.4\.6 = 1\.2\.0 default
.fi
-.
.IP "" 0
-.
.P
\fB\-\-filter\-minor\fR would only show:
-.
.IP "" 4
-.
.nf
-
-* headless (newest 2\.3\.1, installed 2\.2\.3) in groups "test"
-.
+* Gem Current Latest Requested Groups
+* headless 2\.2\.3 2\.3\.1 = 2\.2\.3 test
.fi
-.
.IP "" 0
-.
.P
\fB\-\-filter\-patch\fR would only show:
-.
.IP "" 4
-.
.nf
-
-* faker (newest 1\.6\.6, installed 1\.6\.5, requested ~> 1\.4) in groups "development, test"
-.
+* Gem Current Latest Requested Groups
+* faker 1\.6\.5 1\.6\.6 ~> 1\.4 development, test
.fi
-.
.IP "" 0
-.
.P
Filter options can be combined\. \fB\-\-filter\-minor\fR and \fB\-\-filter\-patch\fR would show:
-.
.IP "" 4
-.
.nf
-
-* faker (newest 1\.6\.6, installed 1\.6\.5, requested ~> 1\.4) in groups "development, test"
-* headless (newest 2\.3\.1, installed 2\.2\.3) in groups "test"
-.
+* Gem Current Latest Requested Groups
+* faker 1\.6\.5 1\.6\.6 ~> 1\.4 development, test
.fi
-.
.IP "" 0
-.
.P
Combining all three \fBfilter\fR options would be the same result as providing none of them\.
diff --git a/lib/bundler/man/bundle-outdated.1.ronn b/lib/bundler/man/bundle-outdated.1.ronn
index 04096ffbb6..4ac65d0532 100644
--- a/lib/bundler/man/bundle-outdated.1.ronn
+++ b/lib/bundler/man/bundle-outdated.1.ronn
@@ -78,25 +78,29 @@ in the output.
If the regular output shows the following:
- * faker (newest 1.6.6, installed 1.6.5, requested ~> 1.4) in groups "development, test"
- * hashie (newest 3.4.6, installed 1.2.0, requested = 1.2.0) in groups "default"
- * headless (newest 2.3.1, installed 2.2.3) in groups "test"
+ * Gem Current Latest Requested Groups
+ * faker 1.6.5 1.6.6 ~> 1.4 development, test
+ * hashie 1.2.0 3.4.6 = 1.2.0 default
+ * headless 2.2.3 2.3.1 = 2.2.3 test
`--filter-major` would only show:
- * hashie (newest 3.4.6, installed 1.2.0, requested = 1.2.0) in groups "default"
+ * Gem Current Latest Requested Groups
+ * hashie 1.2.0 3.4.6 = 1.2.0 default
`--filter-minor` would only show:
- * headless (newest 2.3.1, installed 2.2.3) in groups "test"
+ * Gem Current Latest Requested Groups
+ * headless 2.2.3 2.3.1 = 2.2.3 test
`--filter-patch` would only show:
- * faker (newest 1.6.6, installed 1.6.5, requested ~> 1.4) in groups "development, test"
+ * Gem Current Latest Requested Groups
+ * faker 1.6.5 1.6.6 ~> 1.4 development, test
Filter options can be combined. `--filter-minor` and `--filter-patch` would show:
- * faker (newest 1.6.6, installed 1.6.5, requested ~> 1.4) in groups "development, test"
- * headless (newest 2.3.1, installed 2.2.3) in groups "test"
+ * Gem Current Latest Requested Groups
+ * faker 1.6.5 1.6.6 ~> 1.4 development, test
Combining all three `filter` options would be the same result as providing none of them.
diff --git a/lib/bundler/man/bundle-platform.1 b/lib/bundler/man/bundle-platform.1
index 2794878719..069966f1b2 100644
--- a/lib/bundler/man/bundle-platform.1
+++ b/lib/bundler/man/bundle-platform.1
@@ -1,41 +1,27 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-PLATFORM" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-PLATFORM" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-platform\fR \- Displays platform compatibility information
-.
.SH "SYNOPSIS"
\fBbundle platform\fR [\-\-ruby]
-.
.SH "DESCRIPTION"
\fBplatform\fR displays information from your Gemfile, Gemfile\.lock, and Ruby VM about your platform\.
-.
.P
For instance, using this Gemfile(5):
-.
.IP "" 4
-.
.nf
-
source "https://rubygems\.org"
ruby "3\.1\.2"
gem "rack"
-.
.fi
-.
.IP "" 0
-.
.P
If you run \fBbundle platform\fR on Ruby 3\.1\.2, it displays the following output:
-.
.IP "" 4
-.
.nf
-
Your platform is: x86_64\-linux
Your app has gems that work on these platforms:
@@ -48,24 +34,16 @@ Your Gemfile specifies a Ruby version requirement:
* ruby 3\.1\.2
Your current platform satisfies the Ruby version requirement\.
-.
.fi
-.
.IP "" 0
-.
.P
-\fBplatform\fR lists all the platforms in your \fBGemfile\.lock\fR as well as the \fBruby\fR directive if applicable from your Gemfile(5)\. It also lets you know if the \fBruby\fR directive requirement has been met\. If \fBruby\fR directive doesn\'t match the running Ruby VM, it tells you what part does not\.
-.
+\fBplatform\fR lists all the platforms in your \fBGemfile\.lock\fR as well as the \fBruby\fR directive if applicable from your Gemfile(5)\. It also lets you know if the \fBruby\fR directive requirement has been met\. If \fBruby\fR directive doesn't match the running Ruby VM, it tells you what part does not\.
.SH "OPTIONS"
-.
.TP
\fB\-\-ruby\fR
-It will display the ruby directive information, so you don\'t have to parse it from the Gemfile(5)\.
-.
+It will display the ruby directive information, so you don't have to parse it from the Gemfile(5)\.
.SH "SEE ALSO"
-.
.IP "\(bu" 4
bundle\-lock(1) \fIbundle\-lock\.1\.html\fR
-.
.IP "" 0
diff --git a/lib/bundler/man/bundle-plugin.1 b/lib/bundler/man/bundle-plugin.1
index 39d3dfa04e..f4e043c363 100644
--- a/lib/bundler/man/bundle-plugin.1
+++ b/lib/bundler/man/bundle-plugin.1
@@ -1,81 +1,58 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-PLUGIN" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-PLUGIN" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-plugin\fR \- Manage Bundler plugins
-.
.SH "SYNOPSIS"
-\fBbundle plugin\fR install PLUGINS [\-\-source=\fISOURCE\fR] [\-\-version=\fIversion\fR] [\-\-git|\-\-local_git=\fIgit\-url\fR] [\-\-branch=\fIbranch\fR|\-\-ref=\fIrev\fR]
-.
+\fBbundle plugin\fR install PLUGINS [\-\-source=\fISOURCE\fR] [\-\-version=\fIversion\fR] [\-\-git=\fIgit\-url\fR] [\-\-branch=\fIbranch\fR|\-\-ref=\fIrev\fR] [\-\-path=\fIpath\fR]
.br
\fBbundle plugin\fR uninstall PLUGINS
-.
.br
\fBbundle plugin\fR list
-.
.br
\fBbundle plugin\fR help [COMMAND]
-.
.SH "DESCRIPTION"
You can install, uninstall, and list plugin(s) with this command to extend functionalities of Bundler\.
-.
.SH "SUB\-COMMANDS"
-.
.SS "install"
Install the given plugin(s)\.
-.
-.IP "\(bu" 4
-\fBbundle plugin install bundler\-graph\fR: Install bundler\-graph gem from RubyGems\.org\. The global source, specified in source in Gemfile is ignored\.
-.
-.IP "\(bu" 4
-\fBbundle plugin install bundler\-graph \-\-source https://example\.com\fR: Install bundler\-graph gem from example\.com\. The global source, specified in source in Gemfile is not considered\.
-.
-.IP "\(bu" 4
-\fBbundle plugin install bundler\-graph \-\-version 0\.2\.1\fR: You can specify the version of the gem via \fB\-\-version\fR\.
-.
-.IP "\(bu" 4
-\fBbundle plugin install bundler\-graph \-\-git https://github\.com/rubygems/bundler\-graph\fR: Install bundler\-graph gem from Git repository\. \fB\-\-git\fR can be replaced with \fB\-\-local\-git\fR\. You cannot use both \fB\-\-git\fR and \fB\-\-local\-git\fR\. You can use standard Git URLs like:
-.
-.IP "\(bu" 4
+.TP
+\fBbundle plugin install bundler\-graph\fR
+Install bundler\-graph gem from globally configured sources (defaults to RubyGems\.org)\. The global source, specified in source in Gemfile is ignored\.
+.TP
+\fBbundle plugin install bundler\-graph \-\-source https://example\.com\fR
+Install bundler\-graph gem from example\.com\. The global source, specified in source in Gemfile is not considered\.
+.TP
+\fBbundle plugin install bundler\-graph \-\-version 0\.2\.1\fR
+You can specify the version of the gem via \fB\-\-version\fR\.
+.TP
+\fBbundle plugin install bundler\-graph \-\-git https://github\.com/rubygems/bundler\-graph\fR
+Install bundler\-graph gem from Git repository\. You can use standard Git URLs like:
+.IP
\fBssh://[user@]host\.xz[:port]/path/to/repo\.git\fR
-.
-.IP "\(bu" 4
+.br
\fBhttp[s]://host\.xz[:port]/path/to/repo\.git\fR
-.
-.IP "\(bu" 4
+.br
\fB/path/to/repo\fR
-.
-.IP "\(bu" 4
+.br
\fBfile:///path/to/repo\fR
-.
-.IP "" 0
-.
.IP
-When you specify \fB\-\-git\fR/\fB\-\-local\-git\fR, you can use \fB\-\-branch\fR or \fB\-\-ref\fR to specify any branch, tag, or commit hash (revision) to use\. When you specify both, only the latter is used\.
-.
-.IP "" 0
-.
+When you specify \fB\-\-git\fR, you can use \fB\-\-branch\fR or \fB\-\-ref\fR to specify any branch, tag, or commit hash (revision) to use\.
+.TP
+\fBbundle plugin install bundler\-graph \-\-path \.\./bundler\-graph\fR
+Install bundler\-graph gem from a local path\.
.SS "uninstall"
Uninstall the plugin(s) specified in PLUGINS\.
-.
.SS "list"
List the installed plugins and available commands\.
-.
.P
No options\.
-.
.SS "help"
Describe subcommands or one specific subcommand\.
-.
.P
No options\.
-.
.SH "SEE ALSO"
-.
.IP "\(bu" 4
How to write a Bundler plugin \fIhttps://bundler\.io/guides/bundler_plugins\.html\fR
-.
.IP "" 0
diff --git a/lib/bundler/man/bundle-plugin.1.ronn b/lib/bundler/man/bundle-plugin.1.ronn
index 4f234eeba7..b0a34660ea 100644
--- a/lib/bundler/man/bundle-plugin.1.ronn
+++ b/lib/bundler/man/bundle-plugin.1.ronn
@@ -4,7 +4,8 @@ bundle-plugin(1) -- Manage Bundler plugins
## SYNOPSIS
`bundle plugin` install PLUGINS [--source=<SOURCE>] [--version=<version>]
- [--git|--local_git=<git-url>] [--branch=<branch>|--ref=<rev>]<br>
+ [--git=<git-url>] [--branch=<branch>|--ref=<rev>]
+ [--path=<path>]<br>
`bundle plugin` uninstall PLUGINS<br>
`bundle plugin` list<br>
`bundle plugin` help [COMMAND]
@@ -20,7 +21,7 @@ You can install, uninstall, and list plugin(s) with this command to extend funct
Install the given plugin(s).
* `bundle plugin install bundler-graph`:
- Install bundler-graph gem from RubyGems.org. The global source, specified in source in Gemfile is ignored.
+ Install bundler-graph gem from globally configured sources (defaults to RubyGems.org). The global source, specified in source in Gemfile is ignored.
* `bundle plugin install bundler-graph --source https://example.com`:
Install bundler-graph gem from example.com. The global source, specified in source in Gemfile is not considered.
@@ -29,14 +30,17 @@ Install the given plugin(s).
You can specify the version of the gem via `--version`.
* `bundle plugin install bundler-graph --git https://github.com/rubygems/bundler-graph`:
- Install bundler-graph gem from Git repository. `--git` can be replaced with `--local-git`. You cannot use both `--git` and `--local-git`. You can use standard Git URLs like:
+ Install bundler-graph gem from Git repository. You can use standard Git URLs like:
- * `ssh://[user@]host.xz[:port]/path/to/repo.git`
- * `http[s]://host.xz[:port]/path/to/repo.git`
- * `/path/to/repo`
- * `file:///path/to/repo`
+ `ssh://[user@]host.xz[:port]/path/to/repo.git`<br>
+ `http[s]://host.xz[:port]/path/to/repo.git`<br>
+ `/path/to/repo`<br>
+ `file:///path/to/repo`
- When you specify `--git`/`--local-git`, you can use `--branch` or `--ref` to specify any branch, tag, or commit hash (revision) to use. When you specify both, only the latter is used.
+ When you specify `--git`, you can use `--branch` or `--ref` to specify any branch, tag, or commit hash (revision) to use.
+
+* `bundle plugin install bundler-graph --path ../bundler-graph`:
+ Install bundler-graph gem from a local path.
### uninstall
diff --git a/lib/bundler/man/bundle-pristine.1 b/lib/bundler/man/bundle-pristine.1
index f42c7ce156..76a0479bc6 100644
--- a/lib/bundler/man/bundle-pristine.1
+++ b/lib/bundler/man/bundle-pristine.1
@@ -1,34 +1,23 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-PRISTINE" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-PRISTINE" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-pristine\fR \- Restores installed gems to their pristine condition
-.
.SH "SYNOPSIS"
\fBbundle pristine\fR
-.
.SH "DESCRIPTION"
\fBpristine\fR restores the installed gems in the bundle to their pristine condition using the local gem cache from RubyGems\. For git gems, a forced checkout will be performed\.
-.
.P
-For further explanation, \fBbundle pristine\fR ignores unpacked files on disk\. In other words, this command utilizes the local \fB\.gem\fR cache or the gem\'s git repository as if one were installing from scratch\.
-.
+For further explanation, \fBbundle pristine\fR ignores unpacked files on disk\. In other words, this command utilizes the local \fB\.gem\fR cache or the gem's git repository as if one were installing from scratch\.
.P
-Note: the Bundler gem cannot be restored to its original state with \fBpristine\fR\. One also cannot use \fBbundle pristine\fR on gems with a \'path\' option in the Gemfile, because bundler has no original copy it can restore from\.
-.
+Note: the Bundler gem cannot be restored to its original state with \fBpristine\fR\. One also cannot use \fBbundle pristine\fR on gems with a 'path' option in the Gemfile, because bundler has no original copy it can restore from\.
.P
When is it practical to use \fBbundle pristine\fR?
-.
.P
It comes in handy when a developer is debugging a gem\. \fBbundle pristine\fR is a great way to get rid of experimental changes to a gem that one may not want\.
-.
.P
Why use \fBbundle pristine\fR over \fBgem pristine \-\-all\fR?
-.
.P
Both commands are very similar\. For context: \fBbundle pristine\fR, without arguments, cleans all gems from the lockfile\. Meanwhile, \fBgem pristine \-\-all\fR cleans all installed gems for that Ruby version\.
-.
.P
If a developer forgets which gems in their project they might have been debugging, the Rubygems \fBgem pristine [GEMNAME]\fR command may be inconvenient\. One can avoid waiting for \fBgem pristine \-\-all\fR, and instead run \fBbundle pristine\fR\.
diff --git a/lib/bundler/man/bundle-remove.1 b/lib/bundler/man/bundle-remove.1
index b18d80554d..90a664e331 100644
--- a/lib/bundler/man/bundle-remove.1
+++ b/lib/bundler/man/bundle-remove.1
@@ -1,31 +1,21 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-REMOVE" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-REMOVE" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-remove\fR \- Removes gems from the Gemfile
-.
.SH "SYNOPSIS"
-\fBbundle remove [GEM [GEM \.\.\.]] [\-\-install]\fR
-.
+\fBbundle remove [GEM [GEM \|\.\|\.\|\.]] [\-\-install]\fR
.SH "DESCRIPTION"
Removes the given gems from the Gemfile while ensuring that the resulting Gemfile is still valid\. If a gem cannot be removed, a warning is printed\. If a gem is already absent from the Gemfile, and error is raised\.
-.
.SH "OPTIONS"
-.
.TP
\fB\-\-install\fR
Runs \fBbundle install\fR after the given gems have been removed from the Gemfile, which ensures that both the lockfile and the installed gems on disk are also updated to remove the given gem(s)\.
-.
.P
Example:
-.
.P
bundle remove rails
-.
.P
bundle remove rails rack
-.
.P
bundle remove rails rack \-\-install
diff --git a/lib/bundler/man/bundle-show.1 b/lib/bundler/man/bundle-show.1
index efd9ccb0e0..f9f1fd1fa3 100644
--- a/lib/bundler/man/bundle-show.1
+++ b/lib/bundler/man/bundle-show.1
@@ -1,22 +1,15 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-SHOW" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-SHOW" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-show\fR \- Shows all the gems in your bundle, or the path to a gem
-.
.SH "SYNOPSIS"
\fBbundle show\fR [GEM] [\-\-paths]
-.
.SH "DESCRIPTION"
Without the [GEM] option, \fBshow\fR will print a list of the names and versions of all gems that are required by your [\fBGemfile(5)\fR][Gemfile(5)], sorted by name\.
-.
.P
Calling show with [GEM] will list the exact location of that gem on your machine\.
-.
.SH "OPTIONS"
-.
.TP
\fB\-\-paths\fR
List the paths of all gems that are required by your [\fBGemfile(5)\fR][Gemfile(5)], sorted by gem name\.
diff --git a/lib/bundler/man/bundle-update.1 b/lib/bundler/man/bundle-update.1
index c67c44ff86..340ebac687 100644
--- a/lib/bundler/man/bundle-update.1
+++ b/lib/bundler/man/bundle-update.1
@@ -1,114 +1,81 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-UPDATE" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-UPDATE" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-update\fR \- Update your gems to the latest available versions
-.
.SH "SYNOPSIS"
\fBbundle update\fR \fI*gems\fR [\-\-all] [\-\-group=NAME] [\-\-source=NAME] [\-\-local] [\-\-ruby] [\-\-bundler[=VERSION]] [\-\-full\-index] [\-\-jobs=JOBS] [\-\-quiet] [\-\-patch|\-\-minor|\-\-major] [\-\-redownload] [\-\-strict] [\-\-conservative]
-.
.SH "DESCRIPTION"
Update the gems specified (all gems, if \fB\-\-all\fR flag is used), ignoring the previously installed gems specified in the \fBGemfile\.lock\fR\. In general, you should use bundle install(1) \fIbundle\-install\.1\.html\fR to install the same exact gems and versions across machines\.
-.
.P
You would use \fBbundle update\fR to explicitly update the version of a gem\.
-.
.SH "OPTIONS"
-.
.TP
\fB\-\-all\fR
Update all gems specified in Gemfile\.
-.
.TP
\fB\-\-group=<name>\fR, \fB\-g=[<name>]\fR
Only update the gems in the specified group\. For instance, you can update all gems in the development group with \fBbundle update \-\-group development\fR\. You can also call \fBbundle update rails \-\-group test\fR to update the rails gem and all gems in the test group, for example\.
-.
.TP
\fB\-\-source=<name>\fR
The name of a \fB:git\fR or \fB:path\fR source used in the Gemfile(5)\. For instance, with a \fB:git\fR source of \fBhttp://github\.com/rails/rails\.git\fR, you would call \fBbundle update \-\-source rails\fR
-.
.TP
\fB\-\-local\fR
Do not attempt to fetch gems remotely and use the gem cache instead\.
-.
.TP
\fB\-\-ruby\fR
Update the locked version of Ruby to the current version of Ruby\.
-.
.TP
\fB\-\-bundler\fR
Update the locked version of bundler to the invoked bundler version\.
-.
.TP
\fB\-\-full\-index\fR
Fall back to using the single\-file index of all gems\.
-.
.TP
\fB\-\-jobs=[<number>]\fR, \fB\-j[<number>]\fR
Specify the number of jobs to run in parallel\. The default is the number of available processors\.
-.
.TP
\fB\-\-retry=[<number>]\fR
Retry failed network or git requests for \fInumber\fR times\.
-.
.TP
\fB\-\-quiet\fR
Only output warnings and errors\.
-.
.TP
\fB\-\-redownload\fR
Force downloading every gem\.
-.
.TP
\fB\-\-patch\fR
Prefer updating only to next patch version\.
-.
.TP
\fB\-\-minor\fR
Prefer updating only to next minor version\.
-.
.TP
\fB\-\-major\fR
Prefer updating to next major version (default)\.
-.
.TP
\fB\-\-strict\fR
Do not allow any gem to be updated past latest \fB\-\-patch\fR | \fB\-\-minor\fR | \fB\-\-major\fR\.
-.
.TP
\fB\-\-conservative\fR
Use bundle install conservative update behavior and do not allow indirect dependencies to be updated\.
-.
.SH "UPDATING ALL GEMS"
If you run \fBbundle update \-\-all\fR, bundler will ignore any previously installed gems and resolve all dependencies again based on the latest versions of all gems available in the sources\.
-.
.P
Consider the following Gemfile(5):
-.
.IP "" 4
-.
.nf
-
source "https://rubygems\.org"
gem "rails", "3\.0\.0\.rc"
gem "nokogiri"
-.
.fi
-.
.IP "" 0
-.
.P
When you run bundle install(1) \fIbundle\-install\.1\.html\fR the first time, bundler will resolve all of the dependencies, all the way down, and install what you need:
-.
.IP "" 4
-.
.nf
-
-Fetching gem metadata from https://rubygems\.org/\.\.\.\.\.\.\.\.\.
-Resolving dependencies\.\.\.
+Fetching gem metadata from https://rubygems\.org/\|\.\|\.\|\.\|\.\|\.\|\.\|\.\|\.\|\.
+Resolving dependencies\|\.\|\.\|\.
Installing builder 2\.1\.2
Installing abstract 1\.0\.0
Installing rack 1\.2\.8
@@ -138,55 +105,36 @@ Installing nokogiri 1\.6\.5
Bundle complete! 2 Gemfile dependencies, 26 gems total\.
Use `bundle show [gemname]` to see where a bundled gem is installed\.
-.
.fi
-.
.IP "" 0
-.
.P
As you can see, even though you have two gems in the Gemfile(5), your application needs 26 different gems in order to run\. Bundler remembers the exact versions it installed in \fBGemfile\.lock\fR\. The next time you run bundle install(1) \fIbundle\-install\.1\.html\fR, bundler skips the dependency resolution and installs the same gems as it installed last time\.
-.
.P
-After checking in the \fBGemfile\.lock\fR into version control and cloning it on another machine, running bundle install(1) \fIbundle\-install\.1\.html\fR will \fIstill\fR install the gems that you installed last time\. You don\'t need to worry that a new release of \fBerubis\fR or \fBmail\fR changes the gems you use\.
-.
+After checking in the \fBGemfile\.lock\fR into version control and cloning it on another machine, running bundle install(1) \fIbundle\-install\.1\.html\fR will \fIstill\fR install the gems that you installed last time\. You don't need to worry that a new release of \fBerubis\fR or \fBmail\fR changes the gems you use\.
.P
However, from time to time, you might want to update the gems you are using to the newest versions that still match the gems in your Gemfile(5)\.
-.
.P
To do this, run \fBbundle update \-\-all\fR, which will ignore the \fBGemfile\.lock\fR, and resolve all the dependencies again\. Keep in mind that this process can result in a significantly different set of the 25 gems, based on the requirements of new gems that the gem authors released since the last time you ran \fBbundle update \-\-all\fR\.
-.
.SH "UPDATING A LIST OF GEMS"
Sometimes, you want to update a single gem in the Gemfile(5), and leave the rest of the gems that you specified locked to the versions in the \fBGemfile\.lock\fR\.
-.
.P
For instance, in the scenario above, imagine that \fBnokogiri\fR releases version \fB1\.4\.4\fR, and you want to update it \fIwithout\fR updating Rails and all of its dependencies\. To do this, run \fBbundle update nokogiri\fR\.
-.
.P
Bundler will update \fBnokogiri\fR and any of its dependencies, but leave alone Rails and its dependencies\.
-.
.SH "OVERLAPPING DEPENDENCIES"
Sometimes, multiple gems declared in your Gemfile(5) are satisfied by the same second\-level dependency\. For instance, consider the case of \fBthin\fR and \fBrack\-perftools\-profiler\fR\.
-.
.IP "" 4
-.
.nf
-
source "https://rubygems\.org"
gem "thin"
gem "rack\-perftools\-profiler"
-.
.fi
-.
.IP "" 0
-.
.P
The \fBthin\fR gem depends on \fBrack >= 1\.0\fR, while \fBrack\-perftools\-profiler\fR depends on \fBrack ~> 1\.0\fR\. If you run bundle install, you get:
-.
.IP "" 4
-.
.nf
-
Fetching source index for https://rubygems\.org/
Installing daemons (1\.1\.0)
Installing eventmachine (0\.12\.10) with native extensions
@@ -196,199 +144,132 @@ Installing rack (1\.2\.1)
Installing rack\-perftools_profiler (0\.0\.2)
Installing thin (1\.2\.7) with native extensions
Using bundler (1\.0\.0\.rc\.3)
-.
.fi
-.
.IP "" 0
-.
.P
-In this case, the two gems have their own set of dependencies, but they share \fBrack\fR in common\. If you run \fBbundle update thin\fR, bundler will update \fBdaemons\fR, \fBeventmachine\fR and \fBrack\fR, which are dependencies of \fBthin\fR, but not \fBopen4\fR or \fBperftools\.rb\fR, which are dependencies of \fBrack\-perftools_profiler\fR\. Note that \fBbundle update thin\fR will update \fBrack\fR even though it\'s \fIalso\fR a dependency of \fBrack\-perftools_profiler\fR\.
-.
+In this case, the two gems have their own set of dependencies, but they share \fBrack\fR in common\. If you run \fBbundle update thin\fR, bundler will update \fBdaemons\fR, \fBeventmachine\fR and \fBrack\fR, which are dependencies of \fBthin\fR, but not \fBopen4\fR or \fBperftools\.rb\fR, which are dependencies of \fBrack\-perftools_profiler\fR\. Note that \fBbundle update thin\fR will update \fBrack\fR even though it's \fIalso\fR a dependency of \fBrack\-perftools_profiler\fR\.
.P
In short, by default, when you update a gem using \fBbundle update\fR, bundler will update all dependencies of that gem, including those that are also dependencies of another gem\.
-.
.P
To prevent updating indirect dependencies, prior to version 1\.14 the only option was the \fBCONSERVATIVE UPDATING\fR behavior in bundle install(1) \fIbundle\-install\.1\.html\fR:
-.
.P
In this scenario, updating the \fBthin\fR version manually in the Gemfile(5), and then running bundle install(1) \fIbundle\-install\.1\.html\fR will only update \fBdaemons\fR and \fBeventmachine\fR, but not \fBrack\fR\. For more information, see the \fBCONSERVATIVE UPDATING\fR section of bundle install(1) \fIbundle\-install\.1\.html\fR\.
-.
.P
Starting with 1\.14, specifying the \fB\-\-conservative\fR option will also prevent indirect dependencies from being updated\.
-.
.SH "PATCH LEVEL OPTIONS"
Version 1\.14 introduced 4 patch\-level options that will influence how gem versions are resolved\. One of the following options can be used: \fB\-\-patch\fR, \fB\-\-minor\fR or \fB\-\-major\fR\. \fB\-\-strict\fR can be added to further influence resolution\.
-.
.TP
\fB\-\-patch\fR
Prefer updating only to next patch version\.
-.
.TP
\fB\-\-minor\fR
Prefer updating only to next minor version\.
-.
.TP
\fB\-\-major\fR
Prefer updating to next major version (default)\.
-.
.TP
\fB\-\-strict\fR
Do not allow any gem to be updated past latest \fB\-\-patch\fR | \fB\-\-minor\fR | \fB\-\-major\fR\.
-.
.P
-When Bundler is resolving what versions to use to satisfy declared requirements in the Gemfile or in parent gems, it looks up all available versions, filters out any versions that don\'t satisfy the requirement, and then, by default, sorts them from newest to oldest, considering them in that order\.
-.
+When Bundler is resolving what versions to use to satisfy declared requirements in the Gemfile or in parent gems, it looks up all available versions, filters out any versions that don't satisfy the requirement, and then, by default, sorts them from newest to oldest, considering them in that order\.
.P
Providing one of the patch level options (e\.g\. \fB\-\-patch\fR) changes the sort order of the satisfying versions, causing Bundler to consider the latest \fB\-\-patch\fR or \fB\-\-minor\fR version available before other versions\. Note that versions outside the stated patch level could still be resolved to if necessary to find a suitable dependency graph\.
-.
.P
-For example, if gem \'foo\' is locked at 1\.0\.2, with no gem requirement defined in the Gemfile, and versions 1\.0\.3, 1\.0\.4, 1\.1\.0, 1\.1\.1, 2\.0\.0 all exist, the default order of preference by default (\fB\-\-major\fR) will be "2\.0\.0, 1\.1\.1, 1\.1\.0, 1\.0\.4, 1\.0\.3, 1\.0\.2"\.
-.
+For example, if gem 'foo' is locked at 1\.0\.2, with no gem requirement defined in the Gemfile, and versions 1\.0\.3, 1\.0\.4, 1\.1\.0, 1\.1\.1, 2\.0\.0 all exist, the default order of preference by default (\fB\-\-major\fR) will be "2\.0\.0, 1\.1\.1, 1\.1\.0, 1\.0\.4, 1\.0\.3, 1\.0\.2"\.
.P
If the \fB\-\-patch\fR option is used, the order of preference will change to "1\.0\.4, 1\.0\.3, 1\.0\.2, 1\.1\.1, 1\.1\.0, 2\.0\.0"\.
-.
.P
If the \fB\-\-minor\fR option is used, the order of preference will change to "1\.1\.1, 1\.1\.0, 1\.0\.4, 1\.0\.3, 1\.0\.2, 2\.0\.0"\.
-.
.P
Combining the \fB\-\-strict\fR option with any of the patch level options will remove any versions beyond the scope of the patch level option, to ensure that no gem is updated that far\.
-.
.P
To continue the previous example, if both \fB\-\-patch\fR and \fB\-\-strict\fR options are used, the available versions for resolution would be "1\.0\.4, 1\.0\.3, 1\.0\.2"\. If \fB\-\-minor\fR and \fB\-\-strict\fR are used, it would be "1\.1\.1, 1\.1\.0, 1\.0\.4, 1\.0\.3, 1\.0\.2"\.
-.
.P
-Gem requirements as defined in the Gemfile will still be the first determining factor for what versions are available\. If the gem requirement for \fBfoo\fR in the Gemfile is \'~> 1\.0\', that will accomplish the same thing as providing the \fB\-\-minor\fR and \fB\-\-strict\fR options\.
-.
+Gem requirements as defined in the Gemfile will still be the first determining factor for what versions are available\. If the gem requirement for \fBfoo\fR in the Gemfile is '~> 1\.0', that will accomplish the same thing as providing the \fB\-\-minor\fR and \fB\-\-strict\fR options\.
.SH "PATCH LEVEL EXAMPLES"
Given the following gem specifications:
-.
.IP "" 4
-.
.nf
-
foo 1\.4\.3, requires: ~> bar 2\.0
foo 1\.4\.4, requires: ~> bar 2\.0
foo 1\.4\.5, requires: ~> bar 2\.1
foo 1\.5\.0, requires: ~> bar 2\.1
foo 1\.5\.1, requires: ~> bar 3\.0
bar with versions 2\.0\.3, 2\.0\.4, 2\.1\.0, 2\.1\.1, 3\.0\.0
-.
.fi
-.
.IP "" 0
-.
.P
Gemfile:
-.
.IP "" 4
-.
.nf
-
-gem \'foo\'
-.
+gem 'foo'
.fi
-.
.IP "" 0
-.
.P
Gemfile\.lock:
-.
.IP "" 4
-.
.nf
-
foo (1\.4\.3)
bar (~> 2\.0)
bar (2\.0\.3)
-.
.fi
-.
.IP "" 0
-.
.P
Cases:
-.
.IP "" 4
-.
.nf
-
# Command Line Result
\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
-1 bundle update \-\-patch \'foo 1\.4\.5\', \'bar 2\.1\.1\'
-2 bundle update \-\-patch foo \'foo 1\.4\.5\', \'bar 2\.1\.1\'
-3 bundle update \-\-minor \'foo 1\.5\.1\', \'bar 3\.0\.0\'
-4 bundle update \-\-minor \-\-strict \'foo 1\.5\.0\', \'bar 2\.1\.1\'
-5 bundle update \-\-patch \-\-strict \'foo 1\.4\.4\', \'bar 2\.0\.4\'
-.
+1 bundle update \-\-patch 'foo 1\.4\.5', 'bar 2\.1\.1'
+2 bundle update \-\-patch foo 'foo 1\.4\.5', 'bar 2\.1\.1'
+3 bundle update \-\-minor 'foo 1\.5\.1', 'bar 3\.0\.0'
+4 bundle update \-\-minor \-\-strict 'foo 1\.5\.0', 'bar 2\.1\.1'
+5 bundle update \-\-patch \-\-strict 'foo 1\.4\.4', 'bar 2\.0\.4'
.fi
-.
.IP "" 0
-.
.P
In case 1, bar is upgraded to 2\.1\.1, a minor version increase, because the dependency from foo 1\.4\.5 required it\.
-.
.P
-In case 2, only foo is requested to be unlocked, but bar is also allowed to move because it\'s not a declared dependency in the Gemfile\.
-.
+In case 2, only foo is requested to be unlocked, but bar is also allowed to move because it's not a declared dependency in the Gemfile\.
.P
In case 3, bar goes up a whole major release, because a minor increase is preferred now for foo, and when it goes to 1\.5\.1, it requires 3\.0\.0 of bar\.
-.
.P
-In case 4, foo is preferred up to a minor version, but 1\.5\.1 won\'t work because the \-\-strict flag removes bar 3\.0\.0 from consideration since it\'s a major increment\.
-.
+In case 4, foo is preferred up to a minor version, but 1\.5\.1 won't work because the \-\-strict flag removes bar 3\.0\.0 from consideration since it's a major increment\.
.P
In case 5, both foo and bar have any minor or major increments removed from consideration because of the \-\-strict flag, so the most they can move is up to 1\.4\.4 and 2\.0\.4\.
-.
.SH "RECOMMENDED WORKFLOW"
In general, when working with an application managed with bundler, you should use the following workflow:
-.
.IP "\(bu" 4
After you create your Gemfile(5) for the first time, run
-.
.IP
$ bundle install
-.
.IP "\(bu" 4
Check the resulting \fBGemfile\.lock\fR into version control
-.
.IP
$ git add Gemfile\.lock
-.
.IP "\(bu" 4
When checking out this repository on another development machine, run
-.
.IP
$ bundle install
-.
.IP "\(bu" 4
When checking out this repository on a deployment machine, run
-.
.IP
$ bundle install \-\-deployment
-.
.IP "\(bu" 4
After changing the Gemfile(5) to reflect a new or update dependency, run
-.
.IP
$ bundle install
-.
.IP "\(bu" 4
Make sure to check the updated \fBGemfile\.lock\fR into version control
-.
.IP
$ git add Gemfile\.lock
-.
.IP "\(bu" 4
If bundle install(1) \fIbundle\-install\.1\.html\fR reports a conflict, manually update the specific gems that you changed in the Gemfile(5)
-.
.IP
$ bundle update rails thin
-.
.IP "\(bu" 4
If you want to update all the gems to the latest possible versions that still match the gems listed in the Gemfile(5), run
-.
.IP
$ bundle update \-\-all
-.
.IP "" 0
diff --git a/lib/bundler/man/bundle-version.1 b/lib/bundler/man/bundle-version.1
index 9a3820f3e6..00ff0153cb 100644
--- a/lib/bundler/man/bundle-version.1
+++ b/lib/bundler/man/bundle-version.1
@@ -1,35 +1,22 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-VERSION" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-VERSION" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-version\fR \- Prints Bundler version information
-.
.SH "SYNOPSIS"
\fBbundle version\fR
-.
.SH "DESCRIPTION"
Prints Bundler version information\.
-.
.SH "OPTIONS"
No options\.
-.
.SH "EXAMPLE"
Print the version of Bundler with build date and commit hash of the in the Git source\.
-.
.IP "" 4
-.
.nf
-
bundle version
-.
.fi
-.
.IP "" 0
-.
.P
shows \fBBundler version 2\.3\.21 (2022\-08\-24 commit d54be5fdd8)\fR for example\.
-.
.P
cf\. \fBbundle \-\-version\fR shows \fBBundler version 2\.3\.21\fR\.
diff --git a/lib/bundler/man/bundle-viz.1 b/lib/bundler/man/bundle-viz.1
index 3a07010309..eba3b7df25 100644
--- a/lib/bundler/man/bundle-viz.1
+++ b/lib/bundler/man/bundle-viz.1
@@ -1,41 +1,29 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE\-VIZ" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE\-VIZ" "1" "April 2024" ""
.SH "NAME"
\fBbundle\-viz\fR \- Generates a visual dependency graph for your Gemfile
-.
.SH "SYNOPSIS"
\fBbundle viz\fR [\-\-file=FILE] [\-\-format=FORMAT] [\-\-requirements] [\-\-version] [\-\-without=GROUP GROUP]
-.
.SH "DESCRIPTION"
\fBviz\fR generates a PNG file of the current \fBGemfile(5)\fR as a dependency graph\. \fBviz\fR requires the ruby\-graphviz gem (and its dependencies)\.
-.
.P
The associated gems must also be installed via \fBbundle install(1)\fR \fIbundle\-install\.1\.html\fR\.
-.
.P
\fBviz\fR command was deprecated in Bundler 2\.2\. Use bundler\-graph plugin \fIhttps://github\.com/rubygems/bundler\-graph\fR instead\.
-.
.SH "OPTIONS"
-.
.TP
\fB\-\-file\fR, \fB\-f\fR
The name to use for the generated file\. See \fB\-\-format\fR option
-.
.TP
\fB\-\-format\fR, \fB\-F\fR
-This is output format option\. Supported format is png, jpg, svg, dot \.\.\.
-.
+This is output format option\. Supported format is png, jpg, svg, dot \|\.\|\.\|\.
.TP
\fB\-\-requirements\fR, \fB\-R\fR
Set to show the version of each required dependency\.
-.
.TP
\fB\-\-version\fR, \fB\-v\fR
Set to show each gem version\.
-.
.TP
\fB\-\-without\fR, \fB\-W\fR
Exclude gems that are part of the specified named group\.
diff --git a/lib/bundler/man/bundle.1 b/lib/bundler/man/bundle.1
index 873ba566b1..4bd2bcaf67 100644
--- a/lib/bundler/man/bundle.1
+++ b/lib/bundler/man/bundle.1
@@ -1,141 +1,102 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "BUNDLE" "1" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "BUNDLE" "1" "April 2024" ""
.SH "NAME"
\fBbundle\fR \- Ruby Dependency Management
-.
.SH "SYNOPSIS"
\fBbundle\fR COMMAND [\-\-no\-color] [\-\-verbose] [ARGS]
-.
.SH "DESCRIPTION"
-Bundler manages an \fBapplication\'s dependencies\fR through its entire life across many machines systematically and repeatably\.
-.
+Bundler manages an \fBapplication's dependencies\fR through its entire life across many machines systematically and repeatably\.
.P
See the bundler website \fIhttps://bundler\.io\fR for information on getting started, and Gemfile(5) for more information on the \fBGemfile\fR format\.
-.
.SH "OPTIONS"
-.
.TP
\fB\-\-no\-color\fR
Print all output without color
-.
.TP
\fB\-\-retry\fR, \fB\-r\fR
Specify the number of times you wish to attempt network commands
-.
.TP
\fB\-\-verbose\fR, \fB\-V\fR
Print out additional logging information
-.
.SH "BUNDLE COMMANDS"
We divide \fBbundle\fR subcommands into primary commands and utilities:
-.
.SH "PRIMARY COMMANDS"
-.
.TP
\fBbundle install(1)\fR \fIbundle\-install\.1\.html\fR
Install the gems specified by the \fBGemfile\fR or \fBGemfile\.lock\fR
-.
.TP
\fBbundle update(1)\fR \fIbundle\-update\.1\.html\fR
Update dependencies to their latest versions
-.
.TP
\fBbundle cache(1)\fR \fIbundle\-cache\.1\.html\fR
Package the \.gem files required by your application into the \fBvendor/cache\fR directory (aliases: \fBbundle package\fR, \fBbundle pack\fR)
-.
.TP
\fBbundle exec(1)\fR \fIbundle\-exec\.1\.html\fR
Execute a script in the current bundle
-.
.TP
\fBbundle config(1)\fR \fIbundle\-config\.1\.html\fR
Specify and read configuration options for Bundler
-.
.TP
\fBbundle help(1)\fR \fIbundle\-help\.1\.html\fR
Display detailed help for each subcommand
-.
.SH "UTILITIES"
-.
.TP
\fBbundle add(1)\fR \fIbundle\-add\.1\.html\fR
Add the named gem to the Gemfile and run \fBbundle install\fR
-.
.TP
\fBbundle binstubs(1)\fR \fIbundle\-binstubs\.1\.html\fR
Generate binstubs for executables in a gem
-.
.TP
\fBbundle check(1)\fR \fIbundle\-check\.1\.html\fR
Determine whether the requirements for your application are installed and available to Bundler
-.
.TP
\fBbundle show(1)\fR \fIbundle\-show\.1\.html\fR
Show the source location of a particular gem in the bundle
-.
.TP
\fBbundle outdated(1)\fR \fIbundle\-outdated\.1\.html\fR
Show all of the outdated gems in the current bundle
-.
.TP
\fBbundle console(1)\fR (deprecated)
Start an IRB session in the current bundle
-.
.TP
\fBbundle open(1)\fR \fIbundle\-open\.1\.html\fR
Open an installed gem in the editor
-.
.TP
\fBbundle lock(1)\fR \fIbundle\-lock\.1\.html\fR
Generate a lockfile for your dependencies
-.
.TP
\fBbundle viz(1)\fR \fIbundle\-viz\.1\.html\fR (deprecated)
Generate a visual representation of your dependencies
-.
.TP
\fBbundle init(1)\fR \fIbundle\-init\.1\.html\fR
Generate a simple \fBGemfile\fR, placed in the current directory
-.
.TP
\fBbundle gem(1)\fR \fIbundle\-gem\.1\.html\fR
Create a simple gem, suitable for development with Bundler
-.
.TP
\fBbundle platform(1)\fR \fIbundle\-platform\.1\.html\fR
Display platform compatibility information
-.
.TP
\fBbundle clean(1)\fR \fIbundle\-clean\.1\.html\fR
Clean up unused gems in your Bundler directory
-.
.TP
\fBbundle doctor(1)\fR \fIbundle\-doctor\.1\.html\fR
Display warnings about common problems
-.
.TP
\fBbundle remove(1)\fR \fIbundle\-remove\.1\.html\fR
Removes gems from the Gemfile
-.
.TP
\fBbundle plugin(1)\fR \fIbundle\-plugin\.1\.html\fR
Manage Bundler plugins
-.
.TP
\fBbundle version(1)\fR \fIbundle\-version\.1\.html\fR
Prints Bundler version information
-.
.SH "PLUGINS"
-When running a command that isn\'t listed in PRIMARY COMMANDS or UTILITIES, Bundler will try to find an executable on your path named \fBbundler\-<command>\fR and execute it, passing down any extra arguments to it\.
-.
+When running a command that isn't listed in PRIMARY COMMANDS or UTILITIES, Bundler will try to find an executable on your path named \fBbundler\-<command>\fR and execute it, passing down any extra arguments to it\.
.SH "OBSOLETE"
These commands are obsolete and should no longer be used:
-.
.IP "\(bu" 4
\fBbundle inject(1)\fR
-.
.IP "" 0
diff --git a/lib/bundler/man/gemfile.5 b/lib/bundler/man/gemfile.5
index 8e56506c88..c9478a953e 100644
--- a/lib/bundler/man/gemfile.5
+++ b/lib/bundler/man/gemfile.5
@@ -1,215 +1,143 @@
-.\" generated with Ronn/v0.7.3
-.\" http://github.com/rtomayko/ronn/tree/0.7.3
-.
-.TH "GEMFILE" "5" "February 2023" "" ""
-.
+.\" generated with nRonn/v0.11.1
+.\" https://github.com/n-ronn/nronn/tree/0.11.1
+.TH "GEMFILE" "5" "April 2024" ""
.SH "NAME"
\fBGemfile\fR \- A format for describing gem dependencies for Ruby programs
-.
.SH "SYNOPSIS"
A \fBGemfile\fR describes the gem dependencies required to execute associated Ruby code\.
-.
.P
Place the \fBGemfile\fR in the root of the directory containing the associated code\. For instance, in a Rails application, place the \fBGemfile\fR in the same directory as the \fBRakefile\fR\.
-.
.SH "SYNTAX"
A \fBGemfile\fR is evaluated as Ruby code, in a context which makes available a number of methods used to describe the gem requirements\.
-.
.SH "GLOBAL SOURCE"
At the top of the \fBGemfile\fR, add a single line for the \fBRubyGems\fR source that contains the gems listed in the \fBGemfile\fR\.
-.
.IP "" 4
-.
.nf
-
source "https://rubygems\.org"
-.
.fi
-.
.IP "" 0
-.
.P
You can add only one global source\. In Bundler 1\.13, adding multiple global sources was deprecated\. The \fBsource\fR \fBMUST\fR be a valid RubyGems repository\.
-.
.P
To use more than one source of RubyGems, you should use \fI\fBsource\fR block\fR\.
-.
.P
A source is checked for gems following the heuristics described in \fISOURCE PRIORITY\fR\.
-.
.P
\fBNote about a behavior of the feature deprecated in Bundler 1\.13\fR: If a gem is found in more than one global source, Bundler will print a warning after installing the gem indicating which source was used, and listing the other sources where the gem is available\. A specific source can be selected for gems that need to use a non\-standard repository, suppressing this warning, by using the \fI\fB:source\fR option\fR or \fBsource\fR block\.
-.
.SS "CREDENTIALS"
Some gem sources require a username and password\. Use bundle config(1) \fIbundle\-config\.1\.html\fR to set the username and password for any of the sources that need it\. The command must be run once on each computer that will install the Gemfile, but this keeps the credentials from being stored in plain text in version control\.
-.
.IP "" 4
-.
.nf
-
bundle config gems\.example\.com user:password
-.
.fi
-.
.IP "" 0
-.
.P
For some sources, like a company Gemfury account, it may be easier to include the credentials in the Gemfile as part of the source URL\.
-.
.IP "" 4
-.
.nf
-
source "https://user:password@gems\.example\.com"
-.
.fi
-.
.IP "" 0
-.
.P
Credentials in the source URL will take precedence over credentials set using \fBconfig\fR\.
-.
.SH "RUBY"
If your application requires a specific Ruby version or engine, specify your requirements using the \fBruby\fR method, with the following arguments\. All parameters are \fBOPTIONAL\fR unless otherwise specified\.
-.
.SS "VERSION (required)"
The version of Ruby that your application requires\. If your application requires an alternate Ruby engine, such as JRuby, TruffleRuby, etc\., this should be the Ruby version that the engine is compatible with\.
-.
.IP "" 4
-.
.nf
-
ruby "3\.1\.2"
-.
.fi
-.
.IP "" 0
-.
+.P
+If you wish to derive your Ruby version from a version file (ie \.ruby\-version), you can use the \fBfile\fR option instead\.
+.IP "" 4
+.nf
+ruby file: "\.ruby\-version"
+.fi
+.IP "" 0
+.P
+The version file should conform to any of the following formats:
+.IP "\(bu" 4
+\fB3\.1\.2\fR (\.ruby\-version)
+.IP "\(bu" 4
+\fBruby 3\.1\.2\fR (\.tool\-versions, read: https://asdf\-vm\.com/manage/configuration\.html#tool\-versions)
+.IP "" 0
.SS "ENGINE"
Each application \fImay\fR specify a Ruby engine\. If an engine is specified, an engine version \fImust\fR also be specified\.
-.
.P
-What exactly is an Engine? \- A Ruby engine is an implementation of the Ruby language\.
-.
+What exactly is an Engine?
+.IP "\(bu" 4
+A Ruby engine is an implementation of the Ruby language\.
.IP "\(bu" 4
-For background: the reference or original implementation of the Ruby programming language is called Matz\'s Ruby Interpreter \fIhttps://en\.wikipedia\.org/wiki/Ruby_MRI\fR, or MRI for short\. This is named after Ruby creator Yukihiro Matsumoto, also known as Matz\. MRI is also known as CRuby, because it is written in C\. MRI is the most widely used Ruby engine\.
-.
+For background: the reference or original implementation of the Ruby programming language is called Matz's Ruby Interpreter \fIhttps://en\.wikipedia\.org/wiki/Ruby_MRI\fR, or MRI for short\. This is named after Ruby creator Yukihiro Matsumoto, also known as Matz\. MRI is also known as CRuby, because it is written in C\. MRI is the most widely used Ruby engine\.
.IP "\(bu" 4
-Other implementations \fIhttps://www\.ruby\-lang\.org/en/about/\fR of Ruby exist\. Some of the more well\-known implementations include JRuby \fIhttp://jruby\.org/\fR and TruffleRuby \fIhttps://www\.graalvm\.org/ruby/\fR\. Rubinius is an alternative implementation of Ruby written in Ruby\. JRuby is an implementation of Ruby on the JVM, short for Java Virtual Machine\. TruffleRuby is a Ruby implementation on the GraalVM, a language toolkit built on the JVM\.
-.
+Other implementations \fIhttps://www\.ruby\-lang\.org/en/about/\fR of Ruby exist\. Some of the more well\-known implementations include JRuby \fIhttps://www\.jruby\.org/\fR and TruffleRuby \fIhttps://www\.graalvm\.org/ruby/\fR\. Rubinius is an alternative implementation of Ruby written in Ruby\. JRuby is an implementation of Ruby on the JVM, short for Java Virtual Machine\. TruffleRuby is a Ruby implementation on the GraalVM, a language toolkit built on the JVM\.
.IP "" 0
-.
.SS "ENGINE VERSION"
Each application \fImay\fR specify a Ruby engine version\. If an engine version is specified, an engine \fImust\fR also be specified\. If the engine is "ruby" the engine version specified \fImust\fR match the Ruby version\.
-.
.IP "" 4
-.
.nf
-
ruby "2\.6\.8", engine: "jruby", engine_version: "9\.3\.8\.0"
-.
.fi
-.
.IP "" 0
-.
.SS "PATCHLEVEL"
Each application \fImay\fR specify a Ruby patchlevel\. Specifying the patchlevel has been meaningless since Ruby 2\.1\.0 was released as the patchlevel is now uniquely determined by a combination of major, minor, and teeny version numbers\.
-.
.P
This option was implemented in Bundler 1\.4\.0 for Ruby 2\.0 or earlier\.
-.
.IP "" 4
-.
.nf
-
ruby "3\.1\.2", patchlevel: "20"
-.
.fi
-.
.IP "" 0
-.
.SH "GEMS"
Specify gem requirements using the \fBgem\fR method, with the following arguments\. All parameters are \fBOPTIONAL\fR unless otherwise specified\.
-.
.SS "NAME (required)"
For each gem requirement, list a single \fIgem\fR line\.
-.
.IP "" 4
-.
.nf
-
gem "nokogiri"
-.
.fi
-.
.IP "" 0
-.
.SS "VERSION"
Each \fIgem\fR \fBMAY\fR have one or more version specifiers\.
-.
.IP "" 4
-.
.nf
-
gem "nokogiri", ">= 1\.4\.2"
gem "RedCloth", ">= 4\.1\.0", "< 4\.2\.0"
-.
.fi
-.
.IP "" 0
-.
.SS "REQUIRE AS"
Each \fIgem\fR \fBMAY\fR specify files that should be used when autorequiring via \fBBundler\.require\fR\. You may pass an array with multiple files or \fBtrue\fR if the file you want \fBrequired\fR has the same name as \fIgem\fR or \fBfalse\fR to prevent any file from being autorequired\.
-.
.IP "" 4
-.
.nf
-
gem "redis", require: ["redis/connection/hiredis", "redis"]
gem "webmock", require: false
gem "byebug", require: true
-.
.fi
-.
.IP "" 0
-.
.P
The argument defaults to the name of the gem\. For example, these are identical:
-.
.IP "" 4
-.
.nf
-
gem "nokogiri"
gem "nokogiri", require: "nokogiri"
gem "nokogiri", require: true
-.
.fi
-.
.IP "" 0
-.
.SS "GROUPS"
Each \fIgem\fR \fBMAY\fR specify membership in one or more groups\. Any \fIgem\fR that does not specify membership in any group is placed in the \fBdefault\fR group\.
-.
.IP "" 4
-.
.nf
-
gem "rspec", group: :test
gem "wirble", groups: [:development, :test]
-.
.fi
-.
.IP "" 0
-.
.P
The Bundler runtime allows its two main methods, \fBBundler\.setup\fR and \fBBundler\.require\fR, to limit their impact to particular groups\.
-.
.IP "" 4
-.
.nf
-
-# setup adds gems to Ruby\'s load path
+# setup adds gems to Ruby's load path
Bundler\.setup # defaults to all groups
require "bundler/setup" # same as Bundler\.setup
Bundler\.setup(:default) # only set up the _default_ group
@@ -221,437 +149,269 @@ Bundler\.require # defaults to the _default_ group
Bundler\.require(:default) # identical
Bundler\.require(:default, :test) # requires the _default_ and _test_ groups
Bundler\.require(:test) # requires the _test_ group
-.
.fi
-.
.IP "" 0
-.
.P
The Bundler CLI allows you to specify a list of groups whose gems \fBbundle install\fR should not install with the \fBwithout\fR configuration\.
-.
.P
To specify multiple groups to ignore, specify a list of groups separated by spaces\.
-.
.IP "" 4
-.
.nf
-
bundle config set \-\-local without test
bundle config set \-\-local without development test
-.
.fi
-.
.IP "" 0
-.
.P
Also, calling \fBBundler\.setup\fR with no parameters, or calling \fBrequire "bundler/setup"\fR will setup all groups except for the ones you excluded via \fB\-\-without\fR (since they are not available)\.
-.
.P
Note that on \fBbundle install\fR, bundler downloads and evaluates all gems, in order to create a single canonical list of all of the required gems and their dependencies\. This means that you cannot list different versions of the same gems in different groups\. For more details, see Understanding Bundler \fIhttps://bundler\.io/rationale\.html\fR\.
-.
.SS "PLATFORMS"
If a gem should only be used in a particular platform or set of platforms, you can specify them\. Platforms are essentially identical to groups, except that you do not need to use the \fB\-\-without\fR install\-time flag to exclude groups of gems for other platforms\.
-.
.P
There are a number of \fBGemfile\fR platforms:
-.
.TP
\fBruby\fR
C Ruby (MRI), Rubinius, or TruffleRuby, but not Windows
-.
.TP
\fBmri\fR
C Ruby (MRI) only, but not Windows
-.
.TP
\fBwindows\fR
Windows C Ruby (MRI), including RubyInstaller 32\-bit and 64\-bit versions
-.
.TP
\fBmswin\fR
Windows C Ruby (MRI), including RubyInstaller 32\-bit versions
-.
.TP
\fBmswin64\fR
Windows C Ruby (MRI), including RubyInstaller 64\-bit versions
-.
.TP
\fBrbx\fR
Rubinius
-.
.TP
\fBjruby\fR
JRuby
-.
.TP
\fBtruffleruby\fR
TruffleRuby
-.
.P
On platforms \fBruby\fR, \fBmri\fR, \fBmswin\fR, \fBmswin64\fR, and \fBwindows\fR, you may additionally specify a version by appending the major and minor version numbers without a delimiter\. For example, to specify that a gem should only be used on platform \fBruby\fR version 3\.1, use:
-.
.IP "" 4
-.
.nf
-
ruby_31
-.
.fi
-.
.IP "" 0
-.
.P
As with groups (above), you may specify one or more platforms:
-.
.IP "" 4
-.
.nf
-
gem "weakling", platforms: :jruby
gem "ruby\-debug", platforms: :mri_31
gem "nokogiri", platforms: [:windows_31, :jruby]
-.
.fi
-.
.IP "" 0
-.
.P
All operations involving groups (\fBbundle install\fR \fIbundle\-install\.1\.html\fR, \fBBundler\.setup\fR, \fBBundler\.require\fR) behave exactly the same as if any groups not matching the current platform were explicitly excluded\.
-.
.P
The following platform values are deprecated and should be replaced with \fBwindows\fR:
-.
.IP "\(bu" 4
\fBmswin\fR, \fBmswin64\fR, \fBmingw32\fR, \fBx64_mingw\fR
-.
.IP "" 0
-.
.SS "FORCE_RUBY_PLATFORM"
If you always want the pure ruby variant of a gem to be chosen over platform specific variants, you can use the \fBforce_ruby_platform\fR option:
-.
.IP "" 4
-.
.nf
-
gem "ffi", force_ruby_platform: true
-.
.fi
-.
.IP "" 0
-.
.P
This can be handy (assuming the pure ruby variant works fine) when:
-.
.IP "\(bu" 4
-You\'re having issues with the platform specific variant\.
-.
+You're having issues with the platform specific variant\.
.IP "\(bu" 4
The platform specific variant does not yet support a newer ruby (and thus has a \fBrequired_ruby_version\fR upper bound), but you still want your Gemfile{\.lock} files to resolve under that ruby\.
-.
.IP "" 0
-.
.SS "SOURCE"
-You can select an alternate RubyGems repository for a gem using the \':source\' option\.
-.
+You can select an alternate RubyGems repository for a gem using the ':source' option\.
.IP "" 4
-.
.nf
-
gem "some_internal_gem", source: "https://gems\.example\.com"
-.
.fi
-.
.IP "" 0
-.
.P
This forces the gem to be loaded from this source and ignores the global source declared at the top level of the file\. If the gem does not exist in this source, it will not be installed\.
-.
.P
Bundler will search for child dependencies of this gem by first looking in the source selected for the parent, but if they are not found there, it will fall back on the global source\.
-.
.P
\fBNote about a behavior of the feature deprecated in Bundler 1\.13\fR: Selecting a specific source repository this way also suppresses the ambiguous gem warning described above in \fIGLOBAL SOURCE\fR\.
-.
.P
Using the \fB:source\fR option for an individual gem will also make that source available as a possible global source for any other gems which do not specify explicit sources\. Thus, when adding gems with explicit sources, it is recommended that you also ensure all other gems in the Gemfile are using explicit sources\.
-.
.SS "GIT"
If necessary, you can specify that a gem is located at a particular git repository using the \fB:git\fR parameter\. The repository can be accessed via several protocols:
-.
.TP
\fBHTTP(S)\fR
gem "rails", git: "https://github\.com/rails/rails\.git"
-.
.TP
\fBSSH\fR
gem "rails", git: "git@github\.com:rails/rails\.git"
-.
.TP
\fBgit\fR
gem "rails", git: "git://github\.com/rails/rails\.git"
-.
.P
If using SSH, the user that you use to run \fBbundle install\fR \fBMUST\fR have the appropriate keys available in their \fB$HOME/\.ssh\fR\.
-.
.P
\fBNOTE\fR: \fBhttp://\fR and \fBgit://\fR URLs should be avoided if at all possible\. These protocols are unauthenticated, so a man\-in\-the\-middle attacker can deliver malicious code and compromise your system\. HTTPS and SSH are strongly preferred\.
-.
.P
The \fBgroup\fR, \fBplatforms\fR, and \fBrequire\fR options are available and behave exactly the same as they would for a normal gem\.
-.
.P
A git repository \fBSHOULD\fR have at least one file, at the root of the directory containing the gem, with the extension \fB\.gemspec\fR\. This file \fBMUST\fR contain a valid gem specification, as expected by the \fBgem build\fR command\.
-.
.P
If a git repository does not have a \fB\.gemspec\fR, bundler will attempt to create one, but it will not contain any dependencies, executables, or C extension compilation instructions\. As a result, it may fail to properly integrate into your application\.
-.
.P
If a git repository does have a \fB\.gemspec\fR for the gem you attached it to, a version specifier, if provided, means that the git repository is only valid if the \fB\.gemspec\fR specifies a version matching the version specifier\. If not, bundler will print a warning\.
-.
.IP "" 4
-.
.nf
-
gem "rails", "2\.3\.8", git: "https://github\.com/rails/rails\.git"
# bundle install will fail, because the \.gemspec in the rails
-# repository\'s master branch specifies version 3\.0\.0
-.
+# repository's master branch specifies version 3\.0\.0
.fi
-.
.IP "" 0
-.
.P
If a git repository does \fBnot\fR have a \fB\.gemspec\fR for the gem you attached it to, a version specifier \fBMUST\fR be provided\. Bundler will use this version in the simple \fB\.gemspec\fR it creates\.
-.
.P
Git repositories support a number of additional options\.
-.
.TP
\fBbranch\fR, \fBtag\fR, and \fBref\fR
You \fBMUST\fR only specify at most one of these options\. The default is \fBbranch: "master"\fR\. For example:
-.
.IP
gem "rails", git: "https://github\.com/rails/rails\.git", branch: "5\-0\-stable"
-.
.IP
gem "rails", git: "https://github\.com/rails/rails\.git", tag: "v5\.0\.0"
-.
.IP
gem "rails", git: "https://github\.com/rails/rails\.git", ref: "4aded"
-.
.TP
\fBsubmodules\fR
For reference, a git submodule \fIhttps://git\-scm\.com/book/en/v2/Git\-Tools\-Submodules\fR lets you have another git repository within a subfolder of your repository\. Specify \fBsubmodules: true\fR to cause bundler to expand any submodules included in the git repository
-.
.P
If a git repository contains multiple \fB\.gemspecs\fR, each \fB\.gemspec\fR represents a gem located at the same place in the file system as the \fB\.gemspec\fR\.
-.
.IP "" 4
-.
.nf
-
|~rails [git root]
| |\-rails\.gemspec [rails gem located here]
|~actionpack
| |\-actionpack\.gemspec [actionpack gem located here]
|~activesupport
| |\-activesupport\.gemspec [activesupport gem located here]
-|\.\.\.
-.
+|\|\.\|\.\|\.
.fi
-.
.IP "" 0
-.
.P
To install a gem located in a git repository, bundler changes to the directory containing the gemspec, runs \fBgem build name\.gemspec\fR and then installs the resulting gem\. The \fBgem build\fR command, which comes standard with Rubygems, evaluates the \fB\.gemspec\fR in the context of the directory in which it is located\.
-.
.SS "GIT SOURCE"
-A custom git source can be defined via the \fBgit_source\fR method\. Provide the source\'s name as an argument, and a block which receives a single argument and interpolates it into a string to return the full repo address:
-.
+A custom git source can be defined via the \fBgit_source\fR method\. Provide the source's name as an argument, and a block which receives a single argument and interpolates it into a string to return the full repo address:
.IP "" 4
-.
.nf
-
git_source(:stash){ |repo_name| "https://stash\.corp\.acme\.pl/#{repo_name}\.git" }
-gem \'rails\', stash: \'forks/rails\'
-.
+gem 'rails', stash: 'forks/rails'
.fi
-.
.IP "" 0
-.
.P
In addition, if you wish to choose a specific branch:
-.
.IP "" 4
-.
.nf
-
gem "rails", stash: "forks/rails", branch: "branch_name"
-.
.fi
-.
.IP "" 0
-.
.SS "GITHUB"
\fBNOTE\fR: This shorthand should be avoided until Bundler 2\.0, since it currently expands to an insecure \fBgit://\fR URL\. This allows a man\-in\-the\-middle attacker to compromise your system\.
-.
.P
If the git repository you want to use is hosted on GitHub and is public, you can use the :github shorthand to specify the github username and repository name (without the trailing "\.git"), separated by a slash\. If both the username and repository name are the same, you can omit one\.
-.
.IP "" 4
-.
.nf
-
gem "rails", github: "rails/rails"
gem "rails", github: "rails"
-.
.fi
-.
.IP "" 0
-.
.P
Are both equivalent to
-.
.IP "" 4
-.
.nf
-
gem "rails", git: "https://github\.com/rails/rails\.git"
-.
.fi
-.
.IP "" 0
-.
.P
Since the \fBgithub\fR method is a specialization of \fBgit_source\fR, it accepts a \fB:branch\fR named argument\.
-.
.P
You can also directly pass a pull request URL:
-.
.IP "" 4
-.
.nf
-
gem "rails", github: "https://github\.com/rails/rails/pull/43753"
-.
.fi
-.
.IP "" 0
-.
.P
Which is equivalent to:
-.
.IP "" 4
-.
.nf
-
gem "rails", github: "rails/rails", branch: "refs/pull/43753/head"
-.
.fi
-.
.IP "" 0
-.
.SS "GIST"
If the git repository you want to use is hosted as a GitHub Gist and is public, you can use the :gist shorthand to specify the gist identifier (without the trailing "\.git")\.
-.
.IP "" 4
-.
.nf
-
gem "the_hatch", gist: "4815162342"
-.
.fi
-.
.IP "" 0
-.
.P
Is equivalent to:
-.
.IP "" 4
-.
.nf
-
gem "the_hatch", git: "https://gist\.github\.com/4815162342\.git"
-.
.fi
-.
.IP "" 0
-.
.P
Since the \fBgist\fR method is a specialization of \fBgit_source\fR, it accepts a \fB:branch\fR named argument\.
-.
.SS "BITBUCKET"
If the git repository you want to use is hosted on Bitbucket and is public, you can use the :bitbucket shorthand to specify the bitbucket username and repository name (without the trailing "\.git"), separated by a slash\. If both the username and repository name are the same, you can omit one\.
-.
.IP "" 4
-.
.nf
-
gem "rails", bitbucket: "rails/rails"
gem "rails", bitbucket: "rails"
-.
.fi
-.
.IP "" 0
-.
.P
Are both equivalent to
-.
.IP "" 4
-.
.nf
-
gem "rails", git: "https://rails@bitbucket\.org/rails/rails\.git"
-.
.fi
-.
.IP "" 0
-.
.P
Since the \fBbitbucket\fR method is a specialization of \fBgit_source\fR, it accepts a \fB:branch\fR named argument\.
-.
.SS "PATH"
You can specify that a gem is located in a particular location on the file system\. Relative paths are resolved relative to the directory containing the \fBGemfile\fR\.
-.
.P
Similar to the semantics of the \fB:git\fR option, the \fB:path\fR option requires that the directory in question either contains a \fB\.gemspec\fR for the gem, or that you specify an explicit version that bundler should use\.
-.
.P
Unlike \fB:git\fR, bundler does not compile C extensions for gems specified as paths\.
-.
.IP "" 4
-.
.nf
-
gem "rails", path: "vendor/rails"
-.
.fi
-.
.IP "" 0
-.
.P
-If you would like to use multiple local gems directly from the filesystem, you can set a global \fBpath\fR option to the path containing the gem\'s files\. This will automatically load gemspec files from subdirectories\.
-.
+If you would like to use multiple local gems directly from the filesystem, you can set a global \fBpath\fR option to the path containing the gem's files\. This will automatically load gemspec files from subdirectories\.
.IP "" 4
-.
.nf
-
-path \'components\' do
- gem \'admin_ui\'
- gem \'public_ui\'
+path 'components' do
+ gem 'admin_ui'
+ gem 'public_ui'
end
-.
.fi
-.
.IP "" 0
-.
.SH "BLOCK FORM OF SOURCE, GIT, PATH, GROUP and PLATFORMS"
The \fB:source\fR, \fB:git\fR, \fB:path\fR, \fB:group\fR, and \fB:platforms\fR options may be applied to a group of gems by using block form\.
-.
.IP "" 4
-.
.nf
-
source "https://gems\.example\.com" do
gem "some_internal_gem"
gem "another_internal_gem"
@@ -671,61 +431,40 @@ group :development, optional: true do
gem "wirble"
gem "faker"
end
-.
.fi
-.
.IP "" 0
-.
.P
In the case of the group block form the :optional option can be given to prevent a group from being installed unless listed in the \fB\-\-with\fR option given to the \fBbundle install\fR command\.
-.
.P
In the case of the \fBgit\fR block form, the \fB:ref\fR, \fB:branch\fR, \fB:tag\fR, and \fB:submodules\fR options may be passed to the \fBgit\fR method, and all gems in the block will inherit those options\.
-.
.P
The presence of a \fBsource\fR block in a Gemfile also makes that source available as a possible global source for any other gems which do not specify explicit sources\. Thus, when defining source blocks, it is recommended that you also ensure all other gems in the Gemfile are using explicit sources, either via source blocks or \fB:source\fR directives on individual gems\.
-.
.SH "INSTALL_IF"
The \fBinstall_if\fR method allows gems to be installed based on a proc or lambda\. This is especially useful for optional gems that can only be used if certain software is installed or some other conditions are met\.
-.
.IP "" 4
-.
.nf
-
install_if \-> { RUBY_PLATFORM =~ /darwin/ } do
gem "pasteboard"
end
-.
.fi
-.
.IP "" 0
-.
.SH "GEMSPEC"
-The \fB\.gemspec\fR \fIhttp://guides\.rubygems\.org/specification\-reference/\fR file is where you provide metadata about your gem to Rubygems\. Some required Gemspec attributes include the name, description, and homepage of your gem\. This is also where you specify the dependencies your gem needs to run\.
-.
+The \fB\.gemspec\fR \fIhttps://guides\.rubygems\.org/specification\-reference/\fR file is where you provide metadata about your gem to Rubygems\. Some required Gemspec attributes include the name, description, and homepage of your gem\. This is also where you specify the dependencies your gem needs to run\.
.P
If you wish to use Bundler to help install dependencies for a gem while it is being developed, use the \fBgemspec\fR method to pull in the dependencies listed in the \fB\.gemspec\fR file\.
-.
.P
-The \fBgemspec\fR method adds any runtime dependencies as gem requirements in the default group\. It also adds development dependencies as gem requirements in the \fBdevelopment\fR group\. Finally, it adds a gem requirement on your project (\fBpath: \'\.\'\fR)\. In conjunction with \fBBundler\.setup\fR, this allows you to require project files in your test code as you would if the project were installed as a gem; you need not manipulate the load path manually or require project files via relative paths\.
-.
+The \fBgemspec\fR method adds any runtime dependencies as gem requirements in the default group\. It also adds development dependencies as gem requirements in the \fBdevelopment\fR group\. Finally, it adds a gem requirement on your project (\fBpath: '\.'\fR)\. In conjunction with \fBBundler\.setup\fR, this allows you to require project files in your test code as you would if the project were installed as a gem; you need not manipulate the load path manually or require project files via relative paths\.
.P
-The \fBgemspec\fR method supports optional \fB:path\fR, \fB:glob\fR, \fB:name\fR, and \fB:development_group\fR options, which control where bundler looks for the \fB\.gemspec\fR, the glob it uses to look for the gemspec (defaults to: "{,\fI,\fR/*}\.gemspec"), what named \fB\.gemspec\fR it uses (if more than one is present), and which group development dependencies are included in\.
-.
+The \fBgemspec\fR method supports optional \fB:path\fR, \fB:glob\fR, \fB:name\fR, and \fB:development_group\fR options, which control where bundler looks for the \fB\.gemspec\fR, the glob it uses to look for the gemspec (defaults to: \fB{,*,*/*}\.gemspec\fR), what named \fB\.gemspec\fR it uses (if more than one is present), and which group development dependencies are included in\.
.P
When a \fBgemspec\fR dependency encounters version conflicts during resolution, the local version under development will always be selected \-\- even if there are remote versions that better match other requirements for the \fBgemspec\fR gem\.
-.
.SH "SOURCE PRIORITY"
When attempting to locate a gem to satisfy a gem requirement, bundler uses the following priority order:
-.
.IP "1." 4
The source explicitly attached to the gem (using \fB:source\fR, \fB:path\fR, or \fB:git\fR)
-.
.IP "2." 4
For implicit gems (dependencies of explicit gems), any source, git, or path repository declared on the parent\. This results in bundler prioritizing the ActiveSupport gem from the Rails git repository over ones from \fBrubygems\.org\fR
-.
.IP "3." 4
If neither of the above conditions are met, the global source will be used\. If multiple global sources are specified, they will be prioritized from last to first, but this is deprecated since Bundler 1\.13, so Bundler prints a warning and will abort with an error in the future\.
-.
.IP "" 0
diff --git a/lib/bundler/man/gemfile.5.ronn b/lib/bundler/man/gemfile.5.ronn
index a5d2288b96..7c1e00d13a 100644
--- a/lib/bundler/man/gemfile.5.ronn
+++ b/lib/bundler/man/gemfile.5.ronn
@@ -69,6 +69,16 @@ should be the Ruby version that the engine is compatible with.
ruby "3.1.2"
+If you wish to derive your Ruby version from a version file (ie .ruby-version),
+you can use the `file` option instead.
+
+ ruby file: ".ruby-version"
+
+The version file should conform to any of the following formats:
+
+ - `3.1.2` (.ruby-version)
+ - `ruby 3.1.2` (.tool-versions, read: https://asdf-vm.com/manage/configuration.html#tool-versions)
+
### ENGINE
Each application _may_ specify a Ruby engine. If an engine is specified, an
@@ -86,7 +96,7 @@ What exactly is an Engine?
- [Other implementations](https://www.ruby-lang.org/en/about/) of Ruby exist.
Some of the more well-known implementations include
- [JRuby](http://jruby.org/) and [TruffleRuby](https://www.graalvm.org/ruby/).
+ [JRuby](https://www.jruby.org/) and [TruffleRuby](https://www.graalvm.org/ruby/).
Rubinius is an alternative implementation of Ruby written in Ruby.
JRuby is an implementation of Ruby on the JVM, short for Java Virtual Machine.
TruffleRuby is a Ruby implementation on the GraalVM, a language toolkit built on the JVM.
@@ -499,7 +509,7 @@ software is installed or some other conditions are met.
## GEMSPEC
-The [`.gemspec`](http://guides.rubygems.org/specification-reference/) file is where
+The [`.gemspec`](https://guides.rubygems.org/specification-reference/) file is where
you provide metadata about your gem to Rubygems. Some required Gemspec
attributes include the name, description, and homepage of your gem. This is
also where you specify the dependencies your gem needs to run.
@@ -518,7 +528,7 @@ paths.
The `gemspec` method supports optional `:path`, `:glob`, `:name`, and `:development_group`
options, which control where bundler looks for the `.gemspec`, the glob it uses to look
-for the gemspec (defaults to: "{,*,*/*}.gemspec"), what named `.gemspec` it uses
+for the gemspec (defaults to: `{,*,*/*}.gemspec`), what named `.gemspec` it uses
(if more than one is present), and which group development dependencies are included in.
When a `gemspec` dependency encounters version conflicts during resolution, the
diff --git a/lib/bundler/match_metadata.rb b/lib/bundler/match_metadata.rb
index 499036ca93..f6cc27df32 100644
--- a/lib/bundler/match_metadata.rb
+++ b/lib/bundler/match_metadata.rb
@@ -2,6 +2,10 @@
module Bundler
module MatchMetadata
+ def matches_current_metadata?
+ matches_current_ruby? && matches_current_rubygems?
+ end
+
def matches_current_ruby?
@required_ruby_version.satisfied_by?(Gem.ruby_version)
end
diff --git a/lib/bundler/match_platform.rb b/lib/bundler/match_platform.rb
index 7f7e8227f9..ece9fb8679 100644
--- a/lib/bundler/match_platform.rb
+++ b/lib/bundler/match_platform.rb
@@ -12,7 +12,7 @@ module Bundler
def self.platforms_match?(gemspec_platform, local_platform)
return true if gemspec_platform.nil?
- return true if Gem::Platform::RUBY == gemspec_platform
+ return true if gemspec_platform == Gem::Platform::RUBY
return true if local_platform == gemspec_platform
gemspec_platform = Gem::Platform.new(gemspec_platform)
return true if gemspec_platform === local_platform
diff --git a/lib/bundler/mirror.rb b/lib/bundler/mirror.rb
index 9d437a0951..494a6d6aef 100644
--- a/lib/bundler/mirror.rb
+++ b/lib/bundler/mirror.rb
@@ -47,7 +47,7 @@ module Bundler
def fetch_valid_mirror_for(uri)
downcased = uri.to_s.downcase
- mirror = @mirrors[downcased] || @mirrors[Bundler::URI(downcased).host] || Mirror.new(uri)
+ mirror = @mirrors[downcased] || @mirrors[Gem::URI(downcased).host] || Mirror.new(uri)
mirror.validate!(@prober)
mirror = Mirror.new(uri) unless mirror.valid?
mirror
@@ -74,7 +74,7 @@ module Bundler
@uri = if uri.nil?
nil
else
- Bundler::URI(uri.to_s)
+ Gem::URI(uri.to_s)
end
@valid = nil
end
@@ -126,7 +126,7 @@ module Bundler
if uri == "all"
@all = true
else
- @uri = Bundler::URI(uri).absolute? ? Settings.normalize_uri(uri) : uri
+ @uri = Gem::URI(uri).absolute? ? Settings.normalize_uri(uri) : uri
end
@value = value
end
diff --git a/lib/bundler/plugin.rb b/lib/bundler/plugin.rb
index f3caff8963..588fa79be8 100644
--- a/lib/bundler/plugin.rb
+++ b/lib/bundler/plugin.rb
@@ -62,7 +62,8 @@ module Bundler
if names.any?
names.each do |name|
if index.installed?(name)
- Bundler.rm_rf(index.plugin_path(name))
+ path = index.plugin_path(name).to_s
+ Bundler.rm_rf(path) if index.installed_in_plugin_root?(name)
index.unregister_plugin(name)
Bundler.ui.info "Uninstalled plugin #{name}"
else
@@ -100,7 +101,7 @@ module Bundler
# @param [Pathname] gemfile path
# @param [Proc] block that can be evaluated for (inline) Gemfile
def gemfile_install(gemfile = nil, &inline)
- Bundler.settings.temporary(:frozen => false, :deployment => false) do
+ Bundler.settings.temporary(frozen: false, deployment: false) do
builder = DSL.new
if block_given?
builder.instance_eval(&inline)
@@ -197,7 +198,7 @@ module Bundler
# @param [Hash] The options that are present in the lock file
# @return [API::Source] the instance of the class that handles the source
# type passed in locked_opts
- def source_from_lock(locked_opts)
+ def from_lock(locked_opts)
src = source(locked_opts["type"])
src.new(locked_opts.merge("uri" => locked_opts["remote"]))
@@ -227,7 +228,7 @@ module Bundler
plugins = index.hook_plugins(event)
return unless plugins.any?
- (plugins - @loaded_plugin_names).each {|name| load_plugin(name) }
+ plugins.each {|name| load_plugin(name) }
@hooks_by_event[event].each {|blk| blk.call(*args, &arg_blk) }
end
@@ -239,6 +240,11 @@ module Bundler
Index.new.installed?(plugin)
end
+ # @return [true, false] whether the plugin is loaded
+ def loaded?(plugin)
+ @loaded_plugin_names.include?(plugin)
+ end
+
# Post installation processing and registering with index
#
# @param [Array<String>] plugins list to be installed
@@ -301,7 +307,7 @@ module Bundler
@hooks_by_event = Hash.new {|h, k| h[k] = [] }
load_paths = spec.load_paths
- Bundler.rubygems.add_to_load_path(load_paths)
+ Gem.add_to_load_path(*load_paths)
path = Pathname.new spec.full_gem_path
begin
@@ -329,13 +335,14 @@ module Bundler
# @param [String] name of the plugin
def load_plugin(name)
return unless name && !name.empty?
+ return if loaded?(name)
# Need to ensure before this that plugin root where the rest of gems
# are installed to be on load path to support plugin deps. Currently not
# done to avoid conflicts
path = index.plugin_path(name)
- Bundler.rubygems.add_to_load_path(index.load_paths(name))
+ Gem.add_to_load_path(*index.load_paths(name))
load path.join(PLUGIN_FILE_NAME)
diff --git a/lib/bundler/plugin/api/source.rb b/lib/bundler/plugin/api/source.rb
index 67c45bd204..8563ee358a 100644
--- a/lib/bundler/plugin/api/source.rb
+++ b/lib/bundler/plugin/api/source.rb
@@ -39,7 +39,7 @@ module Bundler
# is present to be compatible with `Definition` and is used by
# rubygems source.
module Source
- attr_reader :uri, :options, :name
+ attr_reader :uri, :options, :name, :checksum_store
attr_accessor :dependency_names
def initialize(opts)
@@ -48,6 +48,7 @@ module Bundler
@uri = opts["uri"]
@type = opts["type"]
@name = opts["name"] || "#{@type} at #{@uri}"
+ @checksum_store = Checksum::Store.new
end
# This is used by the default `spec` method to constructs the
@@ -95,7 +96,7 @@ module Bundler
#
# Note: Do not override if you don't know what you are doing.
def post_install(spec, disable_exts = false)
- opts = { :env_shebang => false, :disable_extensions => disable_exts }
+ opts = { env_shebang: false, disable_extensions: disable_exts }
installer = Bundler::Source::Path::Installer.new(spec, opts)
installer.post_install
end
@@ -106,7 +107,7 @@ module Bundler
def install_path
@install_path ||=
begin
- base_name = File.basename(Bundler::URI.parse(uri).normalize.path)
+ base_name = File.basename(Gem::URI.parse(uri).normalize.path)
gem_install_dir.join("#{base_name}-#{uri_hash[0..11]}")
end
@@ -175,7 +176,7 @@ module Bundler
#
# This is used by `app_cache_path`
def app_cache_dirname
- base_name = File.basename(Bundler::URI.parse(uri).normalize.path)
+ base_name = File.basename(Gem::URI.parse(uri).normalize.path)
"#{base_name}-#{uri_hash}"
end
diff --git a/lib/bundler/plugin/events.rb b/lib/bundler/plugin/events.rb
index bc037d1af5..29c05098ae 100644
--- a/lib/bundler/plugin/events.rb
+++ b/lib/bundler/plugin/events.rb
@@ -56,6 +56,30 @@ module Bundler
# Includes an Array of Bundler::Dependency objects
# GEM_AFTER_INSTALL_ALL = "after-install-all"
define :GEM_AFTER_INSTALL_ALL, "after-install-all"
+
+ # @!parse
+ # A hook called before each individual gem is required
+ # Includes a Bundler::Dependency.
+ # GEM_BEFORE_REQUIRE = "before-require"
+ define :GEM_BEFORE_REQUIRE, "before-require"
+
+ # @!parse
+ # A hook called after each individual gem is required
+ # Includes a Bundler::Dependency.
+ # GEM_AFTER_REQUIRE = "after-require"
+ define :GEM_AFTER_REQUIRE, "after-require"
+
+ # @!parse
+ # A hook called before any gems require
+ # Includes an Array of Bundler::Dependency objects.
+ # GEM_BEFORE_REQUIRE_ALL = "before-require-all"
+ define :GEM_BEFORE_REQUIRE_ALL, "before-require-all"
+
+ # @!parse
+ # A hook called after all gems required
+ # Includes an Array of Bundler::Dependency objects.
+ # GEM_AFTER_REQUIRE_ALL = "after-require-all"
+ define :GEM_AFTER_REQUIRE_ALL, "after-require-all"
end
end
end
diff --git a/lib/bundler/plugin/index.rb b/lib/bundler/plugin/index.rb
index a2d5eaa38a..c2ab8f90da 100644
--- a/lib/bundler/plugin/index.rb
+++ b/lib/bundler/plugin/index.rb
@@ -136,6 +136,14 @@ module Bundler
@hooks[event] || []
end
+ # This plugin is installed inside the .bundle/plugin directory,
+ # and thus is managed solely by Bundler
+ def installed_in_plugin_root?(name)
+ return false unless (path = installed?(name))
+
+ path.start_with?("#{Plugin.root}/")
+ end
+
private
# Reads the index file from the directory and initializes the instance
diff --git a/lib/bundler/plugin/installer.rb b/lib/bundler/plugin/installer.rb
index c9ff12ce4b..4f60862bb4 100644
--- a/lib/bundler/plugin/installer.rb
+++ b/lib/bundler/plugin/installer.rb
@@ -10,6 +10,7 @@ module Bundler
class Installer
autoload :Rubygems, File.expand_path("installer/rubygems", __dir__)
autoload :Git, File.expand_path("installer/git", __dir__)
+ autoload :Path, File.expand_path("installer/path", __dir__)
def install(names, options)
check_sources_consistency!(options)
@@ -18,8 +19,8 @@ module Bundler
if options[:git]
install_git(names, version, options)
- elsif options[:local_git]
- install_local_git(names, version, options)
+ elsif options[:path]
+ install_path(names, version, options[:path])
else
sources = options[:source] || Gem.sources
install_rubygems(names, version, sources)
@@ -45,20 +46,40 @@ module Bundler
if options.key?(:git) && options.key?(:local_git)
raise InvalidOption, "Remote and local plugin git sources can't be both specified"
end
+
+ # back-compat; local_git is an alias for git
+ if options.key?(:local_git)
+ Bundler::SharedHelpers.major_deprecation(2, "--local_git is deprecated, use --git")
+ options[:git] = options.delete(:local_git)
+ end
+
+ if (options.keys & [:source, :git, :path]).length > 1
+ raise InvalidOption, "Only one of --source, --git, or --path may be specified"
+ end
+
+ if (options.key?(:branch) || options.key?(:ref)) && !options.key?(:git)
+ raise InvalidOption, "--#{options.key?(:branch) ? "branch" : "ref"} can only be used with git sources"
+ end
+
+ if options.key?(:branch) && options.key?(:ref)
+ raise InvalidOption, "--branch and --ref can't be both specified"
+ end
end
def install_git(names, version, options)
- uri = options.delete(:git)
- options["uri"] = uri
+ source_list = SourceList.new
+ source = source_list.add_git_source({ "uri" => options[:git],
+ "branch" => options[:branch],
+ "ref" => options[:ref] })
- install_all_sources(names, version, options, options[:source])
+ install_all_sources(names, version, source_list, source)
end
- def install_local_git(names, version, options)
- uri = options.delete(:local_git)
- options["uri"] = uri
+ def install_path(names, version, path)
+ source_list = SourceList.new
+ source = source_list.add_path_source({ "path" => path, "root_path" => SharedHelpers.pwd })
- install_all_sources(names, version, options, options[:source])
+ install_all_sources(names, version, source_list, source)
end
# Installs the plugin from rubygems source and returns the path where the
@@ -70,20 +91,19 @@ module Bundler
#
# @return [Hash] map of names to the specs of plugins installed
def install_rubygems(names, version, sources)
- install_all_sources(names, version, nil, sources)
- end
-
- def install_all_sources(names, version, git_source_options, rubygems_source)
source_list = SourceList.new
- source_list.add_git_source(git_source_options) if git_source_options
- Array(rubygems_source).each {|remote| source_list.add_global_rubygems_remote(remote) } if rubygems_source
+ Array(sources).each {|remote| source_list.add_global_rubygems_remote(remote) }
+
+ install_all_sources(names, version, source_list)
+ end
- deps = names.map {|name| Dependency.new name, version }
+ def install_all_sources(names, version, source_list, source = nil)
+ deps = names.map {|name| Dependency.new(name, version, { "source" => source }) }
Bundler.configure_gem_home_and_path(Plugin.root)
- Bundler.settings.temporary(:deployment => false, :frozen => false) do
+ Bundler.settings.temporary(deployment: false, frozen: false) do
definition = Definition.new(nil, deps, source_list, true)
install_definition(definition)
diff --git a/lib/bundler/plugin/installer/path.rb b/lib/bundler/plugin/installer/path.rb
new file mode 100644
index 0000000000..58a8fa7426
--- /dev/null
+++ b/lib/bundler/plugin/installer/path.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Bundler
+ module Plugin
+ class Installer
+ class Path < Bundler::Source::Path
+ def root
+ SharedHelpers.in_bundle? ? Bundler.root : Plugin.root
+ end
+
+ def generate_bin(spec, disable_extensions = false)
+ # Need to find a way without code duplication
+ # For now, we can ignore this
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/plugin/source_list.rb b/lib/bundler/plugin/source_list.rb
index 547661cf2f..746996de55 100644
--- a/lib/bundler/plugin/source_list.rb
+++ b/lib/bundler/plugin/source_list.rb
@@ -9,6 +9,10 @@ module Bundler
add_source_to_list Plugin::Installer::Git.new(options), git_sources
end
+ def add_path_source(options = {})
+ add_source_to_list Plugin::Installer::Path.new(options), path_sources
+ end
+
def add_rubygems_source(options = {})
add_source_to_list Plugin::Installer::Rubygems.new(options), @rubygems_sources
end
@@ -17,10 +21,6 @@ module Bundler
path_sources + git_sources + rubygems_sources + [metadata_source]
end
- def default_source
- git_sources.first || global_rubygems_source
- end
-
private
def rubygems_aggregate_class
diff --git a/lib/bundler/remote_specification.rb b/lib/bundler/remote_specification.rb
index f626a3218e..9d237f3fa0 100644
--- a/lib/bundler/remote_specification.rb
+++ b/lib/bundler/remote_specification.rb
@@ -88,6 +88,10 @@ module Bundler
end
end
+ def runtime_dependencies
+ dependencies.select(&:runtime?)
+ end
+
def git_version
return unless loaded_from && source.is_a?(Bundler::Source::Git)
" #{source.revision[0..6]}"
diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb
index c8cc88a3ee..1a6711ea6f 100644
--- a/lib/bundler/resolver.rb
+++ b/lib/bundler/resolver.rb
@@ -29,7 +29,7 @@ module Bundler
Bundler.ui.info "Resolving dependencies...", true
- solve_versions(:root => root, :logger => logger)
+ solve_versions(root: root, logger: logger)
end
def setup_solver
@@ -37,31 +37,39 @@ module Bundler
root_version = Resolver::Candidate.new(0)
@all_specs = Hash.new do |specs, name|
- specs[name] = source_for(name).specs.search(name).reject do |s|
- s.dependencies.any? {|d| d.name == name && !d.requirement.satisfied_by?(s.version) } # ignore versions that depend on themselves incorrectly
- end.sort_by {|s| [s.version, s.platform.to_s] }
+ source = source_for(name)
+ matches = source.specs.search(name)
+
+ # Don't bother to check for circular deps when no dependency API are
+ # available, since it's too slow to be usable. That edge case won't work
+ # but resolution other than that should work fine and reasonably fast.
+ if source.respond_to?(:dependency_api_available?) && source.dependency_api_available?
+ matches = filter_invalid_self_dependencies(matches, name)
+ end
+
+ specs[name] = matches.sort_by {|s| [s.version, s.platform.to_s] }
+ end
+
+ @all_versions = Hash.new do |candidates, package|
+ candidates[package] = all_versions_for(package)
end
@sorted_versions = Hash.new do |candidates, package|
- candidates[package] = if package.root?
- [root_version]
- else
- all_versions_for(package).sort
- end
+ candidates[package] = filtered_versions_for(package).sort
end
+ @sorted_versions[root] = [root_version]
+
root_dependencies = prepare_dependencies(@requirements, @packages)
@cached_dependencies = Hash.new do |dependencies, package|
- dependencies[package] = if package.root?
- { root_version => root_dependencies }
- else
- Hash.new do |versions, version|
- versions[version] = to_dependency_hash(version.dependencies.reject {|d| d.name == package.name }, @packages)
- end
+ dependencies[package] = Hash.new do |versions, version|
+ versions[version] = to_dependency_hash(version.dependencies.reject {|d| d.name == package.name }, @packages)
end
end
+ @cached_dependencies[root] = { root_version => root_dependencies }
+
logger = Bundler::UI::Shell.new
logger.level = debug? ? "debug" : "warn"
@@ -69,7 +77,7 @@ module Bundler
end
def solve_versions(root:, logger:)
- solver = PubGrub::VersionSolver.new(:source => self, :root => root, :logger => logger)
+ solver = PubGrub::VersionSolver.new(source: self, root: root, logger: logger)
result = solver.solve
result.map {|package, version| version.to_specs(package) }.flatten.uniq
rescue PubGrub::SolveFailure => e
@@ -123,7 +131,7 @@ module Bundler
if base_requirements[name]
names_to_unlock << name
- elsif package.ignores_prereleases?
+ elsif package.ignores_prereleases? && @all_specs[name].any? {|s| s.version.prerelease? }
names_to_allow_prereleases_for << name
end
@@ -144,13 +152,19 @@ module Bundler
requirement_to_range(dependency)
end
- PubGrub::VersionConstraint.new(package, :range => range)
+ PubGrub::VersionConstraint.new(package, range: range)
end
def versions_for(package, range=VersionRange.any)
- versions = range.select_versions(@sorted_versions[package])
+ versions = select_sorted_versions(package, range)
- sort_versions(package, versions)
+ # Conditional avoids (among other things) calling
+ # sort_versions_by_preferred with the root package
+ if versions.size > 1
+ sort_versions_by_preferred(package, versions)
+ else
+ versions
+ end
end
def no_versions_incompatibility_for(package, unsatisfied_term)
@@ -160,7 +174,7 @@ module Bundler
constraint_string = constraint.constraint_string
requirements = constraint_string.split(" OR ").map {|req| Gem::Requirement.new(req.split(",")) }
- if name == "bundler"
+ if name == "bundler" && bundler_pinned_to_current_version?
custom_explanation = "the current Bundler version (#{Bundler::VERSION}) does not satisfy #{constraint}"
extended_explanation = bundler_not_found_message(requirements)
else
@@ -173,7 +187,7 @@ module Bundler
extended_explanation = other_specs_matching_message(specs_matching_other_platforms, label) if specs_matching_other_platforms.any?
end
- Incompatibility.new([unsatisfied_term], :cause => cause, :custom_explanation => custom_explanation, :extended_explanation => extended_explanation)
+ Incompatibility.new([unsatisfied_term], cause: cause, custom_explanation: custom_explanation, extended_explanation: extended_explanation)
end
def debug?
@@ -212,9 +226,9 @@ module Bundler
sorted_versions[high]
end
- range = PubGrub::VersionRange.new(:min => low, :max => high, :include_min => true)
+ range = PubGrub::VersionRange.new(min: low, max: high, include_min: true)
- self_constraint = PubGrub::VersionConstraint.new(package, :range => range)
+ self_constraint = PubGrub::VersionConstraint.new(package, range: range)
dep_term = PubGrub::Term.new(dep_constraint, false)
self_term = PubGrub::Term.new(self_constraint, true)
@@ -223,37 +237,63 @@ module Bundler
"current #{dep_package} version is #{dep_constraint.constraint_string}"
end
- PubGrub::Incompatibility.new([self_term, dep_term], :cause => :dependency, :custom_explanation => custom_explanation)
+ PubGrub::Incompatibility.new([self_term, dep_term], cause: :dependency, custom_explanation: custom_explanation)
end
end
def all_versions_for(package)
name = package.name
results = (@base[name] + filter_prereleases(@all_specs[name], package)).uniq {|spec| [spec.version.hash, spec.platform] }
+
+ if name == "bundler" && !bundler_pinned_to_current_version?
+ bundler_spec = Gem.loaded_specs["bundler"]
+ results << bundler_spec if bundler_spec
+ end
+
locked_requirement = base_requirements[name]
results = filter_matching_specs(results, locked_requirement) if locked_requirement
- versions = results.group_by(&:version).reduce([]) do |groups, (version, specs)|
- platform_specs = package.platforms.flat_map {|platform| select_best_platform_match(specs, platform) }
- next groups if platform_specs.empty?
+ results.group_by(&:version).reduce([]) do |groups, (version, specs)|
+ platform_specs = package.platforms.map {|platform| select_best_platform_match(specs, platform) }
+
+ # If package is a top-level dependency,
+ # candidate is only valid if there are matching versions for all resolution platforms.
+ #
+ # If package is not a top-level deependency,
+ # then it's not necessary that it has matching versions for all platforms, since it may have been introduced only as
+ # a dependency for a platform specific variant, so it will only need to have a valid version for that platform.
+ #
+ if package.top_level?
+ next groups if platform_specs.any?(&:empty?)
+ else
+ next groups if platform_specs.all?(&:empty?)
+ end
+
+ platform_specs.flatten!
ruby_specs = select_best_platform_match(specs, Gem::Platform::RUBY)
- groups << Resolver::Candidate.new(version, :specs => ruby_specs) if ruby_specs.any?
+ groups << Resolver::Candidate.new(version, specs: ruby_specs) if ruby_specs.any?
next groups if platform_specs == ruby_specs || package.force_ruby_platform?
- groups << Resolver::Candidate.new(version, :specs => platform_specs)
+ groups << Resolver::Candidate.new(version, specs: platform_specs)
groups
end
-
- sort_versions(package, versions)
end
def source_for(name)
@source_requirements[name] || @source_requirements[:default]
end
+ def default_bundler_source
+ @source_requirements[:default_bundler]
+ end
+
+ def bundler_pinned_to_current_version?
+ !default_bundler_source.nil?
+ end
+
def name_for_explicit_dependency_source
Bundler.default_gemfile.basename.to_s
rescue StandardError
@@ -273,15 +313,21 @@ module Bundler
end
specs_matching_requirement = filter_matching_specs(specs, package.dependency.requirement)
- if specs_matching_requirement.any?
+ not_found_message = if specs_matching_requirement.any?
specs = specs_matching_requirement
matching_part = requirement_label
platforms = package.platforms
- platform_label = platforms.size == 1 ? "platform '#{platforms.first}" : "platforms '#{platforms.join("', '")}"
- requirement_label = "#{requirement_label}' with #{platform_label}"
+
+ if platforms.size == 1
+ "Could not find gem '#{requirement_label}' with platform '#{platforms.first}'"
+ else
+ "Could not find gems matching '#{requirement_label}' valid for all resolution platforms (#{platforms.join(", ")})"
+ end
+ else
+ "Could not find gem '#{requirement_label}'"
end
- message = String.new("Could not find gem '#{requirement_label}' in #{source}#{cache_message}.\n")
+ message = String.new("#{not_found_message} in #{source}#{cache_message}.\n")
if specs.any?
message << "\n#{other_specs_matching_message(specs, matching_part)}"
@@ -292,6 +338,21 @@ module Bundler
private
+ def filtered_versions_for(package)
+ @gem_version_promoter.filter_versions(package, @all_versions[package])
+ end
+
+ def raise_all_versions_filtered_out!(package)
+ level = @gem_version_promoter.level
+ name = package.name
+ locked_version = package.locked_version
+ requirement = package.dependency
+
+ raise GemNotFound,
+ "#{name} is locked to #{locked_version}, while Gemfile is requesting #{requirement}. " \
+ "--strict --#{level} was specified, but there are no #{level} level upgrades from #{locked_version} satisfying #{requirement}, so version solving has failed"
+ end
+
def filter_matching_specs(specs, requirements)
Array(requirements).flat_map do |requirement|
specs.select {| spec| requirement_satisfied_by?(requirement, spec) }
@@ -304,16 +365,19 @@ module Bundler
specs.reject {|s| s.version.prerelease? }
end
+ # Ignore versions that depend on themselves incorrectly
+ def filter_invalid_self_dependencies(specs, name)
+ specs.reject do |s|
+ s.dependencies.any? {|d| d.name == name && !d.requirement.satisfied_by?(s.version) }
+ end
+ end
+
def requirement_satisfied_by?(requirement, spec)
requirement.satisfied_by?(spec.version) || spec.source.is_a?(Source::Gemspec)
end
- def sort_versions(package, versions)
- if versions.size > 1
- @gem_version_promoter.sort_versions(package, versions).reverse
- else
- versions
- end
+ def sort_versions_by_preferred(package, versions)
+ @gem_version_promoter.sort_versions(package, versions)
end
def repository_for(package)
@@ -330,12 +394,19 @@ module Bundler
next [dep_package, dep_constraint] if name == "bundler"
- versions = versions_for(dep_package, dep_constraint.range)
+ dep_range = dep_constraint.range
+ versions = select_sorted_versions(dep_package, dep_range)
if versions.empty? && dep_package.ignores_prereleases?
+ @all_versions.delete(dep_package)
@sorted_versions.delete(dep_package)
dep_package.consider_prereleases!
- versions = versions_for(dep_package, dep_constraint.range)
+ versions = select_sorted_versions(dep_package, dep_range)
+ end
+
+ if versions.empty? && select_all_versions(dep_package, dep_range).any?
+ raise_all_versions_filtered_out!(dep_package)
end
+
next [dep_package, dep_constraint] unless versions.empty?
next unless dep_package.current_platform?
@@ -344,6 +415,14 @@ module Bundler
end.compact.to_h
end
+ def select_sorted_versions(package, range)
+ range.select_versions(@sorted_versions[package])
+ end
+
+ def select_all_versions(package, range)
+ range.select_versions(@all_versions[package])
+ end
+
def other_specs_matching_message(specs, requirement)
message = String.new("The source contains the following gems matching '#{requirement}':\n")
message << specs.map {|s| " * #{s.full_name}" }.join("\n")
@@ -359,19 +438,19 @@ module Bundler
when "~>"
name = "~> #{ver}"
bump = Resolver::Candidate.new(version.bump.to_s + ".A")
- PubGrub::VersionRange.new(:name => name, :min => ver, :max => bump, :include_min => true)
+ PubGrub::VersionRange.new(name: name, min: ver, max: bump, include_min: true)
when ">"
- PubGrub::VersionRange.new(:min => platform_ver)
+ PubGrub::VersionRange.new(min: platform_ver)
when ">="
- PubGrub::VersionRange.new(:min => ver, :include_min => true)
+ PubGrub::VersionRange.new(min: ver, include_min: true)
when "<"
- PubGrub::VersionRange.new(:max => ver)
+ PubGrub::VersionRange.new(max: ver)
when "<="
- PubGrub::VersionRange.new(:max => platform_ver, :include_max => true)
+ PubGrub::VersionRange.new(max: platform_ver, include_max: true)
when "="
- PubGrub::VersionRange.new(:min => ver, :max => platform_ver, :include_min => true, :include_max => true)
+ PubGrub::VersionRange.new(min: ver, max: platform_ver, include_min: true, include_max: true)
when "!="
- PubGrub::VersionRange.new(:min => ver, :max => platform_ver, :include_min => true, :include_max => true).invert
+ PubGrub::VersionRange.new(min: ver, max: platform_ver, include_min: true, include_max: true).invert
else
raise "bad version specifier: #{op}"
end
@@ -398,7 +477,7 @@ module Bundler
end
def bundler_not_found_message(conflict_dependencies)
- candidate_specs = filter_matching_specs(source_for(:default_bundler).specs.search("bundler"), conflict_dependencies)
+ candidate_specs = filter_matching_specs(default_bundler_source.specs.search("bundler"), conflict_dependencies)
if candidate_specs.any?
target_version = candidate_specs.last.version
diff --git a/lib/bundler/resolver/base.rb b/lib/bundler/resolver/base.rb
index e5c3763c3f..ad19eeb3f4 100644
--- a/lib/bundler/resolver/base.rb
+++ b/lib/bundler/resolver/base.rb
@@ -24,7 +24,7 @@ module Bundler
name = dep.name
- @packages[name] = Package.new(name, dep_platforms, **options.merge(:dependency => dep))
+ @packages[name] = Package.new(name, dep_platforms, **options.merge(dependency: dep))
dep
end.compact
diff --git a/lib/bundler/resolver/candidate.rb b/lib/bundler/resolver/candidate.rb
index e695ef08ee..9e8b913335 100644
--- a/lib/bundler/resolver/candidate.rb
+++ b/lib/bundler/resolver/candidate.rb
@@ -15,7 +15,7 @@ module Bundler
# considered separately.
#
# Some candidates may also keep some information explicitly about the
- # package the refer to. These candidates are referred to as "canonical" and
+ # package they refer to. These candidates are referred to as "canonical" and
# are used when materializing resolution results back into RubyGems
# specifications that can be installed, written to lock files, and so on.
#
diff --git a/lib/bundler/resolver/incompatibility.rb b/lib/bundler/resolver/incompatibility.rb
index c61151fbeb..4ac1b2e1ea 100644
--- a/lib/bundler/resolver/incompatibility.rb
+++ b/lib/bundler/resolver/incompatibility.rb
@@ -8,7 +8,7 @@ module Bundler
def initialize(terms, cause:, custom_explanation: nil, extended_explanation: nil)
@extended_explanation = extended_explanation
- super(terms, :cause => cause, :custom_explanation => custom_explanation)
+ super(terms, cause: cause, custom_explanation: custom_explanation)
end
end
end
diff --git a/lib/bundler/resolver/package.rb b/lib/bundler/resolver/package.rb
index 7499a75006..0461328683 100644
--- a/lib/bundler/resolver/package.rb
+++ b/lib/bundler/resolver/package.rb
@@ -21,6 +21,7 @@ module Bundler
@locked_version = locked_specs[name].first&.version
@unlock = unlock
@dependency = dependency || Dependency.new(name, @locked_version)
+ @top_level = !dependency.nil?
@prerelease = @dependency.prerelease? || @locked_version&.prerelease? || prerelease ? :consider_first : :ignore
end
@@ -32,6 +33,10 @@ module Bundler
false
end
+ def top_level?
+ @top_level
+ end
+
def meta?
@name.end_with?("\0")
end
diff --git a/lib/bundler/resolver/spec_group.rb b/lib/bundler/resolver/spec_group.rb
index b44c19a73f..5cee444e5e 100644
--- a/lib/bundler/resolver/spec_group.rb
+++ b/lib/bundler/resolver/spec_group.rb
@@ -25,9 +25,8 @@ module Bundler
def to_specs(force_ruby_platform)
@specs.map do |s|
- lazy_spec = LazySpecification.new(name, version, s.platform, source)
+ lazy_spec = LazySpecification.from_spec(s)
lazy_spec.force_ruby_platform = force_ruby_platform
- lazy_spec.dependencies.replace s.dependencies
lazy_spec
end
end
@@ -64,8 +63,6 @@ module Bundler
end
def metadata_dependencies(spec)
- return [] if spec.is_a?(LazySpecification)
-
[
metadata_dependency("Ruby", spec.required_ruby_version),
metadata_dependency("RubyGems", spec.required_rubygems_version),
diff --git a/lib/bundler/retry.rb b/lib/bundler/retry.rb
index 2415ade200..b95c42c361 100644
--- a/lib/bundler/retry.rb
+++ b/lib/bundler/retry.rb
@@ -56,7 +56,7 @@ module Bundler
def keep_trying?
return true if current_run.zero?
return false if last_attempt?
- return true if @failed
+ true if @failed
end
def last_attempt?
diff --git a/lib/bundler/ruby_dsl.rb b/lib/bundler/ruby_dsl.rb
index 3b3a0583a5..fb4b79c4df 100644
--- a/lib/bundler/ruby_dsl.rb
+++ b/lib/bundler/ruby_dsl.rb
@@ -3,16 +3,51 @@
module Bundler
module RubyDsl
def ruby(*ruby_version)
- options = ruby_version.last.is_a?(Hash) ? ruby_version.pop : {}
+ options = ruby_version.pop if ruby_version.last.is_a?(Hash)
ruby_version.flatten!
- raise GemfileError, "Please define :engine_version" if options[:engine] && options[:engine_version].nil?
- raise GemfileError, "Please define :engine" if options[:engine_version] && options[:engine].nil?
- if options[:engine] == "ruby" && options[:engine_version] &&
- ruby_version != Array(options[:engine_version])
- raise GemfileEvalError, "ruby_version must match the :engine_version for MRI"
+ if options
+ patchlevel = options[:patchlevel]
+ engine = options[:engine]
+ engine_version = options[:engine_version]
+
+ raise GemfileError, "Please define :engine_version" if engine && engine_version.nil?
+ raise GemfileError, "Please define :engine" if engine_version && engine.nil?
+
+ if options[:file]
+ raise GemfileError, "Do not pass version argument when using :file option" unless ruby_version.empty?
+ ruby_version << normalize_ruby_file(options[:file])
+ end
+
+ if engine == "ruby" && engine_version && ruby_version != Array(engine_version)
+ raise GemfileEvalError, "ruby_version must match the :engine_version for MRI"
+ end
+ end
+
+ @ruby_version = RubyVersion.new(ruby_version, patchlevel, engine, engine_version)
+ end
+
+ # Support the various file formats found in .ruby-version files.
+ #
+ # 3.2.2
+ # ruby-3.2.2
+ #
+ # Also supports .tool-versions files for asdf. Lines not starting with "ruby" are ignored.
+ #
+ # ruby 2.5.1 # comment is ignored
+ # ruby 2.5.1# close comment and extra spaces doesn't confuse
+ #
+ # Intentionally does not support `3.2.1@gemset` since rvm recommends using .ruby-gemset instead
+ #
+ # Loads the file relative to the dirname of the Gemfile itself.
+ def normalize_ruby_file(filename)
+ file_content = Bundler.read_file(gemfile.dirname.join(filename))
+ # match "ruby-3.2.2" or "ruby 3.2.2" capturing version string up to the first space or comment
+ if /^ruby(-|\s+)([^\s#]+)/.match(file_content)
+ $2
+ else
+ file_content.strip
end
- @ruby_version = RubyVersion.new(ruby_version, options[:patchlevel], options[:engine], options[:engine_version])
end
end
end
diff --git a/lib/bundler/ruby_version.rb b/lib/bundler/ruby_version.rb
index b5396abb6e..7e9e072b83 100644
--- a/lib/bundler/ruby_version.rb
+++ b/lib/bundler/ruby_version.rb
@@ -23,7 +23,7 @@ module Bundler
# specified must match the version.
@versions = Array(versions).map do |v|
- op, v = Gem::Requirement.parse(v)
+ op, v = Gem::Requirement.parse(normalize_version(v))
op == "=" ? v.to_s : "#{op} #{v}"
end
@@ -49,7 +49,7 @@ module Bundler
(\d+\.\d+\.\d+(?:\.\S+)?) # ruby version
(?:p(-?\d+))? # optional patchlevel
(?:\s\((\S+)\s(.+)\))? # optional engine info
- /xo.freeze
+ /xo
# Returns a RubyVersion from the given string.
# @param [String] the version string to match.
@@ -112,6 +112,13 @@ module Bundler
private
+ # Ruby's official preview version format uses a `-`: Example: 3.3.0-preview2
+ # However, RubyGems recognizes preview version format with a `.`: Example: 3.3.0.preview2
+ # Returns version string after replacing `-` with `.`
+ def normalize_version(version)
+ version.tr("-", ".")
+ end
+
def matches?(requirements, version)
# Handles RUBY_PATCHLEVEL of -1 for instances like ruby-head
return requirements == version if requirements.to_s == "-1" || version.to_s == "-1"
diff --git a/lib/bundler/rubygems_ext.rb b/lib/bundler/rubygems_ext.rb
index b96edd5e2d..a7539f4adb 100644
--- a/lib/bundler/rubygems_ext.rb
+++ b/lib/bundler/rubygems_ext.rb
@@ -2,6 +2,8 @@
require "pathname"
+require "rubygems" unless defined?(Gem)
+
require "rubygems/specification"
# We can't let `Gem::Source` be autoloaded in the `Gem::Specification#source`
@@ -46,7 +48,7 @@ module Gem
def full_gem_path
if source.respond_to?(:root)
- Pathname.new(loaded_from).dirname.expand_path(source.root).to_s.tap {|x| x.untaint if RUBY_VERSION < "2.7" }
+ Pathname.new(loaded_from).dirname.expand_path(source.root).to_s
else
rg_full_gem_path
end
@@ -76,25 +78,7 @@ module Gem
end
end
- alias_method :rg_missing_extensions?, :missing_extensions?
- def missing_extensions?
- # When we use this methods with local gemspec, we don't handle
- # build status of extension correctly. So We need to find extension
- # files in require_paths.
- # TODO: Gem::Specification couldn't access extension name from extconf.rb
- # so we find them with heuristic way. We should improve it.
- if source.respond_to?(:root)
- return false if raw_require_paths.any? do |path|
- ext_dir = File.join(full_gem_path, path)
- File.exist?(File.join(ext_dir, "#{name}.#{RbConfig::CONFIG["DLEXT"]}")) ||
- !Dir.glob(File.join(ext_dir, name, "*.#{RbConfig::CONFIG["DLEXT"]}")).empty?
- end
- end
-
- rg_missing_extensions?
- end
-
- remove_method :gem_dir if instance_methods(false).include?(:gem_dir)
+ remove_method :gem_dir
def gem_dir
full_gem_path
end
@@ -135,17 +119,6 @@ module Gem
gemfile
end
- # Backfill missing YAML require when not defined. Fixed since 3.1.0.pre1.
- module YamlBackfiller
- def to_yaml(opts = {})
- Gem.load_yaml unless defined?(::YAML)
-
- super(opts)
- end
- end
-
- prepend YamlBackfiller
-
def nondevelopment_dependencies
dependencies - development_dependencies
end
@@ -173,6 +146,18 @@ module Gem
end
end
+ module BetterPermissionError
+ def data
+ Bundler::SharedHelpers.filesystem_access(loaded_from, :read) do
+ super
+ end
+ end
+ end
+
+ class StubSpecification
+ prepend BetterPermissionError
+ end
+
class Dependency
include ::Bundler::ForcePlatform
@@ -206,37 +191,7 @@ module Gem
end
end
- # comparison is done order independently since rubygems 3.2.0.rc.2
- unless Gem::Requirement.new("> 1", "< 2") == Gem::Requirement.new("< 2", "> 1")
- class Requirement
- module OrderIndependentComparison
- def ==(other)
- return unless Gem::Requirement === other
-
- if _requirements_sorted? && other._requirements_sorted?
- super
- else
- _with_sorted_requirements == other._with_sorted_requirements
- end
- end
-
- protected
-
- def _requirements_sorted?
- return @_requirements_sorted if defined?(@_requirements_sorted)
- strings = as_list
- @_requirements_sorted = strings == strings.sort
- end
-
- def _with_sorted_requirements
- @_with_sorted_requirements ||= _requirements_sorted? ? self : self.class.new(as_list.sort)
- end
- end
-
- prepend OrderIndependentComparison
- end
- end
-
+ # Requirements using lambda operator differentiate trailing zeros since rubygems 3.2.6
if Gem::Requirement.new("~> 2.0").hash == Gem::Requirement.new("~> 2.0.0").hash
class Requirement
module CorrectHashForLambdaOperator
@@ -338,7 +293,7 @@ module Gem
end
# On universal Rubies, resolve the "universal" arch to the real CPU arch, without changing the extension directory.
- class Specification
+ class BasicSpecification
if /^universal\.(?<arch>.*?)-/ =~ (CROSS_COMPILING || RUBY_PLATFORM)
local_platform = Platform.local
if local_platform.cpu == "universal"
@@ -351,23 +306,35 @@ module Gem
end
def extensions_dir
- Gem.default_ext_dir_for(base_dir) ||
- File.join(base_dir, "extensions", ORIGINAL_LOCAL_PLATFORM,
- Gem.extension_api_version)
+ @extensions_dir ||=
+ Gem.default_ext_dir_for(base_dir) || File.join(base_dir, "extensions", ORIGINAL_LOCAL_PLATFORM, Gem.extension_api_version)
end
end
end
end
- require "rubygems/util"
+ require "rubygems/name_tuple"
+
+ class NameTuple
+ # Versions of RubyGems before about 3.5.0 don't to_s the platform.
+ unless Gem::NameTuple.new("a", Gem::Version.new("1"), Gem::Platform.new("x86_64-linux")).platform.is_a?(String)
+ alias_method :initialize_with_platform, :initialize
- Util.singleton_class.module_eval do
- if Util.singleton_methods.include?(:glob_files_in_dir) # since 3.0.0.beta.2
- remove_method :glob_files_in_dir
+ def initialize(name, version, platform=Gem::Platform::RUBY)
+ if Gem::Platform === platform
+ initialize_with_platform(name, version, platform.to_s)
+ else
+ initialize_with_platform(name, version, platform)
+ end
+ end
end
- def glob_files_in_dir(glob, base_path)
- Dir.glob(glob, :base => base_path).map! {|f| File.expand_path(f, base_path) }
+ def lock_name
+ if platform == Gem::Platform::RUBY
+ "#{name} (#{version})"
+ else
+ "#{name} (#{version}-#{platform})"
+ end
end
end
end
diff --git a/lib/bundler/rubygems_gem_installer.rb b/lib/bundler/rubygems_gem_installer.rb
index 38035a00ac..d563868cd2 100644
--- a/lib/bundler/rubygems_gem_installer.rb
+++ b/lib/bundler/rubygems_gem_installer.rb
@@ -20,7 +20,7 @@ module Bundler
strict_rm_rf spec.extension_dir
SharedHelpers.filesystem_access(gem_dir, :create) do
- FileUtils.mkdir_p gem_dir, :mode => 0o755
+ FileUtils.mkdir_p gem_dir, mode: 0o755
end
extract_files
@@ -45,6 +45,14 @@ module Bundler
spec
end
+ def pre_install_checks
+ super
+ rescue Gem::FilePermissionError
+ # Ignore permission checks in RubyGems. Instead, go on, and try to write
+ # for real. We properly handle permission errors when they happen.
+ nil
+ end
+
def generate_plugins
return unless Gem::Installer.instance_methods(false).include?(:generate_plugins)
@@ -60,10 +68,6 @@ module Bundler
end
end
- def pre_install_checks
- super && validate_bundler_checksum(options[:bundler_expected_checksum])
- end
-
def build_extensions
extension_cache_path = options[:bundler_extension_cache_path]
extension_dir = spec.extension_dir
@@ -98,6 +102,10 @@ module Bundler
end
end
+ def gem_checksum
+ Checksum.from_gem_package(@package)
+ end
+
private
def prepare_extension_build(extension_dir)
@@ -108,64 +116,21 @@ module Bundler
end
def strict_rm_rf(dir)
- Bundler.rm_rf dir
- rescue StandardError => e
- raise unless File.exist?(dir)
+ return unless File.exist?(dir)
- raise DirectoryRemovalError.new(e, "Could not delete previous installation of `#{dir}`")
- end
-
- def validate_bundler_checksum(checksum)
- return true if Bundler.settings[:disable_checksum_validation]
- return true unless checksum
- return true unless source = @package.instance_variable_get(:@gem)
- return true unless source.respond_to?(:with_read_io)
- digest = source.with_read_io do |io|
- digest = SharedHelpers.digest(:SHA256).new
- digest << io.read(16_384) until io.eof?
- io.rewind
- send(checksum_type(checksum), digest)
- end
- unless digest == checksum
- raise SecurityError, <<-MESSAGE
- Bundler cannot continue installing #{spec.name} (#{spec.version}).
- The checksum for the downloaded `#{spec.full_name}.gem` does not match \
- the checksum given by the server. This means the contents of the downloaded \
- gem is different from what was uploaded to the server, and could be a potential security issue.
-
- To resolve this issue:
- 1. delete the downloaded gem located at: `#{spec.gem_dir}/#{spec.full_name}.gem`
- 2. run `bundle install`
-
- If you wish to continue installing the downloaded gem, and are certain it does not pose a \
- security issue despite the mismatching checksum, do the following:
- 1. run `bundle config set --local disable_checksum_validation true` to turn off checksum verification
- 2. run `bundle install`
-
- (More info: The expected SHA256 checksum was #{checksum.inspect}, but the \
- checksum for the downloaded gem was #{digest.inspect}.)
- MESSAGE
- end
- true
- end
+ parent = File.dirname(dir)
+ parent_st = File.stat(parent)
- def checksum_type(checksum)
- case checksum.length
- when 64 then :hexdigest!
- when 44 then :base64digest!
- else raise InstallError, "The given checksum for #{spec.full_name} (#{checksum.inspect}) is not a valid SHA256 hexdigest nor base64digest"
+ if parent_st.world_writable? && !parent_st.sticky?
+ raise InsecureInstallPathError.new(parent)
end
- end
- def hexdigest!(digest)
- digest.hexdigest!
- end
+ begin
+ FileUtils.remove_entry_secure(dir)
+ rescue StandardError => e
+ raise unless File.exist?(dir)
- def base64digest!(digest)
- if digest.respond_to?(:base64digest!)
- digest.base64digest!
- else
- [digest.digest!].pack("m0")
+ raise DirectoryRemovalError.new(e, "Could not delete previous installation of `#{dir}`")
end
end
end
diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb
index d8b7886af7..494030eab2 100644
--- a/lib/bundler/rubygems_integration.rb
+++ b/lib/bundler/rubygems_integration.rb
@@ -4,17 +4,12 @@ require "rubygems" unless defined?(Gem)
module Bundler
class RubygemsIntegration
- if defined?(Gem::Ext::Builder::CHDIR_MONITOR)
- EXT_LOCK = Gem::Ext::Builder::CHDIR_MONITOR
- else
- require "monitor"
+ require "monitor"
- EXT_LOCK = Monitor.new
- end
+ EXT_LOCK = Monitor.new
def initialize
@replaced_methods = {}
- backport_ext_builder_monitor
end
def version
@@ -43,18 +38,6 @@ module Bundler
Gem.loaded_specs[name]
end
- def add_to_load_path(paths)
- return Gem.add_to_load_path(*paths) if Gem.respond_to?(:add_to_load_path)
-
- if insert_index = Gem.load_path_insert_index
- # Gem directories must come after -I and ENV['RUBYLIB']
- $LOAD_PATH.insert(insert_index, *paths)
- else
- # We are probably testing in core, -I and RUBYLIB don't apply
- $LOAD_PATH.unshift(*paths)
- end
- end
-
def mark_loaded(spec)
if spec.respond_to?(:activated=)
current = Gem.loaded_specs[spec.name]
@@ -91,9 +74,9 @@ module Bundler
def spec_matches_for_glob(spec, glob)
return spec.matches_for_glob(glob) if spec.respond_to?(:matches_for_glob)
- spec.load_paths.map do |lp|
+ spec.load_paths.flat_map do |lp|
Dir["#{lp}/#{glob}#{suffix_pattern}"]
- end.flatten(1)
+ end
end
def stub_set_spec(stub, spec)
@@ -116,16 +99,6 @@ module Bundler
Gem::Util.inflate(obj)
end
- def correct_for_windows_path(path)
- if Gem::Util.respond_to?(:correct_for_windows_path)
- Gem::Util.correct_for_windows_path(path)
- elsif path[0].chr == "/" && path[1].chr =~ /[a-z]/i && path[2].chr == ":"
- path[1..-1]
- else
- path
- end
- end
-
def gem_dir
Gem.dir
end
@@ -161,7 +134,7 @@ module Bundler
def spec_cache_dirs
@spec_cache_dirs ||= begin
dirs = gem_path.map {|dir| File.join(dir, "specifications") }
- dirs << Gem.spec_cache_dir if Gem.respond_to?(:spec_cache_dir) # Not in RubyGems 2.0.3 or earlier
+ dirs << Gem.spec_cache_dir
dirs.uniq.select {|dir| File.directory? dir }
end
end
@@ -184,15 +157,15 @@ module Bundler
end
def load_plugins
- Gem.load_plugins if Gem.respond_to?(:load_plugins)
+ Gem.load_plugins
end
- def load_plugin_files(files)
- Gem.load_plugin_files(files) if Gem.respond_to?(:load_plugin_files)
+ def load_plugin_files(plugin_files)
+ Gem.load_plugin_files(plugin_files)
end
def load_env_plugins
- Gem.load_env_plugins if Gem.respond_to?(:load_env_plugins)
+ Gem.load_env_plugins
end
def ui=(obj)
@@ -230,8 +203,7 @@ module Bundler
if Gem.respond_to?(:discover_gems_on_require=)
Gem.discover_gems_on_require = false
else
- kernel = (class << ::Kernel; self; end)
- [kernel, ::Kernel].each do |k|
+ [::Kernel.singleton_class, ::Kernel].each do |k|
if k.private_method_defined?(:gem_original_require)
redefine_method(k, :require, k.instance_method(:gem_original_require))
end
@@ -240,12 +212,9 @@ module Bundler
end
def replace_gem(specs, specs_by_name)
- reverse_rubygems_kernel_mixin
-
executables = nil
- kernel = (class << ::Kernel; self; end)
- [kernel, ::Kernel].each do |kernel_class|
+ [::Kernel.singleton_class, ::Kernel].each do |kernel_class|
redefine_method(kernel_class, :gem) do |dep, *reqs|
if executables&.include?(File.basename(caller.first.split(":").first))
break
@@ -358,6 +327,14 @@ module Bundler
def replace_entrypoints(specs)
specs_by_name = add_default_gems_to(specs)
+ reverse_rubygems_kernel_mixin
+ begin
+ # bundled_gems only provide with Ruby 3.3 or later
+ require "bundled_gems"
+ rescue LoadError
+ else
+ Gem::BUNDLED_GEMS.replace_require(specs) if Gem::BUNDLED_GEMS.respond_to?(:replace_require)
+ end
replace_gem(specs, specs_by_name)
stub_rubygems(specs)
replace_bin_path(specs_by_name)
@@ -447,30 +424,28 @@ module Bundler
Gem::Specification.all = specs
end
- def fetch_specs(remote, name)
+ def fetch_specs(remote, name, fetcher)
require "rubygems/remote_fetcher"
path = remote.uri.to_s + "#{name}.#{Gem.marshal_version}.gz"
- fetcher = gem_remote_fetcher
- fetcher.headers = { "X-Gemfile-Source" => remote.original_uri.to_s } if remote.original_uri
string = fetcher.fetch_path(path)
- Bundler.safe_load_marshal(string)
+ specs = Bundler.safe_load_marshal(string)
+ raise MarshalError, "Specs #{name} from #{remote} is expected to be an Array but was unexpected class #{specs.class}" unless specs.is_a?(Array)
+ specs
rescue Gem::RemoteFetcher::FetchError
# it's okay for prerelease to fail
raise unless name == "prerelease_specs"
end
- def fetch_all_remote_specs(remote)
- specs = fetch_specs(remote, "specs")
- pres = fetch_specs(remote, "prerelease_specs") || []
+ def fetch_all_remote_specs(remote, gem_remote_fetcher)
+ specs = fetch_specs(remote, "specs", gem_remote_fetcher)
+ pres = fetch_specs(remote, "prerelease_specs", gem_remote_fetcher) || []
specs.concat(pres)
end
- def download_gem(spec, uri, cache_dir)
+ def download_gem(spec, uri, cache_dir, fetcher)
require "rubygems/remote_fetcher"
uri = Bundler.settings.mirror_for(uri)
- fetcher = gem_remote_fetcher
- fetcher.headers = { "X-Gemfile-Source" => spec.remote.original_uri.to_s } if spec.remote.original_uri
Bundler::Retry.new("download gem from #{uri}").attempts do
gem_file_name = spec.file_name
local_gem_path = File.join cache_dir, gem_file_name
@@ -478,7 +453,6 @@ module Bundler
begin
remote_gem_path = uri + "gems/#{gem_file_name}"
- remote_gem_path = remote_gem_path.to_s if provides?("< 3.2.0.rc.1")
SharedHelpers.filesystem_access(local_gem_path) do
fetcher.cache_update_path remote_gem_path, local_gem_path
@@ -497,12 +471,6 @@ module Bundler
raise Bundler::HTTPError, "Could not download gem from #{uri} due to underlying error <#{e.message}>"
end
- def gem_remote_fetcher
- require "rubygems/remote_fetcher"
- proxy = Gem.configuration[:http_proxy]
- Gem::RemoteFetcher.new(proxy)
- end
-
def build(spec, skip_validation = false)
require "rubygems/package"
Gem::Package.build(spec, skip_validation)
@@ -518,25 +486,6 @@ module Bundler
end
end
- def backport_ext_builder_monitor
- # So we can avoid requiring "rubygems/ext" in its entirety
- Gem.module_eval <<-RUBY, __FILE__, __LINE__ + 1
- module Ext
- end
- RUBY
-
- require "rubygems/ext/builder"
-
- Gem::Ext::Builder.class_eval do
- unless const_defined?(:CHDIR_MONITOR)
- const_set(:CHDIR_MONITOR, EXT_LOCK)
- end
-
- remove_const(:CHDIR_MUTEX) if const_defined?(:CHDIR_MUTEX)
- const_set(:CHDIR_MUTEX, const_get(:CHDIR_MONITOR))
- end
- end
-
def find_bundler(version)
find_name("bundler").find {|s| s.version.to_s == version }
end
@@ -545,14 +494,8 @@ module Bundler
Gem::Specification.stubs_for(name).map(&:to_spec)
end
- if Gem::Specification.respond_to?(:default_stubs)
- def default_stubs
- Gem::Specification.default_stubs("*.gemspec")
- end
- else
- def default_stubs
- Gem::Specification.send(:default_stubs, "*.gemspec")
- end
+ def default_stubs
+ Gem::Specification.default_stubs("*.gemspec")
end
end
diff --git a/lib/bundler/runtime.rb b/lib/bundler/runtime.rb
index bd38353d3c..54aa30ce0b 100644
--- a/lib/bundler/runtime.rb
+++ b/lib/bundler/runtime.rb
@@ -28,11 +28,11 @@ module Bundler
spec.load_paths.reject {|path| $LOAD_PATH.include?(path) }
end.reverse.flatten
- Bundler.rubygems.add_to_load_path(load_paths)
+ Gem.add_to_load_path(*load_paths)
setup_manpath
- lock(:preserve_unknown_sections => true)
+ lock(preserve_unknown_sections: true)
self
end
@@ -41,12 +41,17 @@ module Bundler
groups.map!(&:to_sym)
groups = [:default] if groups.empty?
- @definition.dependencies.each do |dep|
- # Skip the dependency if it is not in any of the requested groups, or
- # not for the current platform, or doesn't match the gem constraints.
- next unless (dep.groups & groups).any? && dep.should_include?
+ dependencies = @definition.dependencies.select do |dep|
+ # Select the dependency if it is in any of the requested groups, and
+ # for the current platform, and matches the gem constraints.
+ (dep.groups & groups).any? && dep.should_include?
+ end
+
+ Plugin.hook(Plugin::Events::GEM_BEFORE_REQUIRE_ALL, dependencies)
+ dependencies.each do |dep|
required_file = nil
+ Plugin.hook(Plugin::Events::GEM_BEFORE_REQUIRE, dep)
begin
# Loop through all the specified autorequires for the
@@ -76,7 +81,13 @@ module Bundler
end
end
end
+
+ Plugin.hook(Plugin::Events::GEM_AFTER_REQUIRE, dep)
end
+
+ Plugin.hook(Plugin::Events::GEM_AFTER_REQUIRE_ALL, dependencies)
+
+ dependencies
end
def self.definition_method(meth)
@@ -94,8 +105,8 @@ module Bundler
definition_method :requires
def lock(opts = {})
- return if @definition.nothing_changed? && !@definition.unlocking?
- @definition.lock(Bundler.default_lockfile, opts[:preserve_unknown_sections])
+ return if @definition.no_resolve_needed?
+ @definition.lock(opts[:preserve_unknown_sections])
end
alias_method :gems, :specs
diff --git a/lib/bundler/self_manager.rb b/lib/bundler/self_manager.rb
index 827f3f9222..bfd000b1a0 100644
--- a/lib/bundler/self_manager.rb
+++ b/lib/bundler/self_manager.rb
@@ -9,17 +9,23 @@ module Bundler
def restart_with_locked_bundler_if_needed
return unless needs_switching? && installed?
- restart_with(lockfile_version)
+ restart_with(restart_version)
end
def install_locked_bundler_and_restart_with_it_if_needed
return unless needs_switching?
- Bundler.ui.info \
- "Bundler #{current_version} is running, but your lockfile was generated with #{lockfile_version}. " \
- "Installing Bundler #{lockfile_version} and restarting using that version."
+ if restart_version == lockfile_version
+ Bundler.ui.info \
+ "Bundler #{current_version} is running, but your lockfile was generated with #{lockfile_version}. " \
+ "Installing Bundler #{lockfile_version} and restarting using that version."
+ else
+ Bundler.ui.info \
+ "Bundler #{current_version} is running, but your configuration was #{restart_version}. " \
+ "Installing Bundler #{restart_version} and restarting using that version."
+ end
- install_and_restart_with(lockfile_version)
+ install_and_restart_with(restart_version)
end
def update_bundler_and_restart_with_it_if_needed(target)
@@ -79,7 +85,8 @@ module Bundler
autoswitching_applies? &&
released?(lockfile_version) &&
!running?(lockfile_version) &&
- !updating?
+ !updating? &&
+ Bundler.settings[:version] != "system"
end
def autoswitching_applies?
@@ -106,7 +113,7 @@ module Bundler
end
def local_specs
- @local_specs ||= Bundler::Source::Rubygems.new("allow_local" => true).specs.select {|spec| spec.name == "bundler" }
+ @local_specs ||= Bundler::Source::Rubygems.new("allow_local" => true, "allow_cached" => true).specs.select {|spec| spec.name == "bundler" }
end
def remote_specs
@@ -114,7 +121,7 @@ module Bundler
source = Bundler::Source::Rubygems.new("remotes" => "https://rubygems.org")
source.remote!
source.add_dependency_names("bundler")
- source.specs
+ source.specs.select(&:matches_current_metadata?)
end
end
@@ -151,7 +158,7 @@ module Bundler
def installed?
Bundler.configure
- Bundler.rubygems.find_bundler(lockfile_version.to_s)
+ Bundler.rubygems.find_bundler(restart_version.to_s)
end
def current_version
@@ -163,6 +170,17 @@ module Bundler
parsed_version = Bundler::LockfileParser.bundled_with
@lockfile_version = parsed_version ? Gem::Version.new(parsed_version) : nil
+ rescue ArgumentError
+ @lockfile_version = nil
+ end
+
+ def restart_version
+ return @restart_version if defined?(@restart_version)
+ # BUNDLE_VERSION=x.y.z
+ @restart_version = Gem::Version.new(Bundler.settings[:version])
+ rescue ArgumentError
+ # BUNDLE_VERSION=lockfile
+ @restart_version = lockfile_version
end
end
end
diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb
index b424bcfd52..abbaec9783 100644
--- a/lib/bundler/settings.rb
+++ b/lib/bundler/settings.rb
@@ -7,7 +7,6 @@ module Bundler
autoload :Validator, File.expand_path("settings/validator", __dir__)
BOOL_KEYS = %w[
- allow_deployment_source_credential_changes
allow_offline_install
auto_clean_without_path
auto_install
@@ -43,10 +42,23 @@ module Bundler
setup_makes_kernel_gem_public
silence_deprecations
silence_root_warning
- suppress_install_using_messages
update_requires_all_flag
].freeze
+ REMEMBERED_KEYS = %w[
+ bin
+ cache_all
+ clean
+ deployment
+ frozen
+ no_prune
+ path
+ shebang
+ path.system
+ without
+ with
+ ].freeze
+
NUMBER_KEYS = %w[
jobs
redirect
@@ -75,6 +87,7 @@ module Bundler
shebang
system_bindir
trust-policy
+ version
].freeze
DEFAULT_CONFIG = {
@@ -84,25 +97,38 @@ module Bundler
"BUNDLE_REDIRECT" => 5,
"BUNDLE_RETRY" => 3,
"BUNDLE_TIMEOUT" => 10,
+ "BUNDLE_VERSION" => "lockfile",
}.freeze
def initialize(root = nil)
@root = root
@local_config = load_config(local_config_file)
- @env_config = ENV.to_h.select {|key, _value| key =~ /\ABUNDLE_.+/ }
+
+ @env_config = ENV.to_h
+ @env_config.select! {|key, _value| key.start_with?("BUNDLE_") }
+ @env_config.delete("BUNDLE_")
+
@global_config = load_config(global_config_file)
@temporary = {}
+
+ @key_cache = {}
end
def [](name)
key = key_for(name)
- value = configs.values.map {|config| config[key] }.compact.first
+
+ value = nil
+ configs.each do |_, config|
+ value = config[key]
+ next if value.nil?
+ break
+ end
converted_value(value, name)
end
def set_command_option(key, value)
- if Bundler.feature_flag.forget_cli_options?
+ if !is_remembered(key) || Bundler.feature_flag.forget_cli_options?
temporary(key => value)
value
else
@@ -139,17 +165,22 @@ module Bundler
end
def all
- keys = @temporary.keys | @global_config.keys | @local_config.keys | @env_config.keys
+ keys = @temporary.keys.union(@global_config.keys, @local_config.keys, @env_config.keys)
- keys.map do |key|
- key.sub(/^BUNDLE_/, "").gsub(/___/, "-").gsub(/__/, ".").downcase
- end.sort
+ keys.map! do |key|
+ key = key.delete_prefix("BUNDLE_")
+ key.gsub!("___", "-")
+ key.gsub!("__", ".")
+ key.downcase!
+ key
+ end.sort!
+ keys
end
def local_overrides
repos = {}
all.each do |k|
- repos[$'] = self[k] if k =~ /^local\./
+ repos[k.delete_prefix("local.")] = self[k] if k.start_with?("local.")
end
repos
end
@@ -157,7 +188,7 @@ module Bundler
def mirror_for(uri)
if uri.is_a?(String)
require_relative "vendored_uri"
- uri = Bundler::URI(uri)
+ uri = Gem::URI(uri)
end
gem_mirrors.for(uri.to_s).uri
@@ -296,18 +327,18 @@ module Bundler
end
def key_for(key)
- self.class.key_for(key)
+ @key_cache[key] ||= self.class.key_for(key)
end
private
def configs
- {
- :temporary => @temporary,
- :local => @local_config,
- :env => @env_config,
- :global => @global_config,
- :default => DEFAULT_CONFIG,
+ @configs ||= {
+ temporary: @temporary,
+ local: @local_config,
+ env: @env_config,
+ global: @global_config,
+ default: DEFAULT_CONFIG,
}
end
@@ -328,16 +359,20 @@ module Bundler
end
def is_bool(name)
- BOOL_KEYS.include?(name.to_s) || BOOL_KEYS.include?(parent_setting_for(name.to_s))
+ name = self.class.key_to_s(name)
+ BOOL_KEYS.include?(name) || BOOL_KEYS.include?(parent_setting_for(name))
end
def is_string(name)
- STRING_KEYS.include?(name.to_s) || name.to_s.start_with?("local.") || name.to_s.start_with?("mirror.") || name.to_s.start_with?("build.")
+ name = self.class.key_to_s(name)
+ STRING_KEYS.include?(name) || name.start_with?("local.") || name.start_with?("mirror.") || name.start_with?("build.")
end
def to_bool(value)
case value
- when nil, /\A(false|f|no|n|0|)\z/i, false
+ when String
+ value.match?(/\A(false|f|no|n|0|)\z/i) ? false : true
+ when nil, false
false
else
true
@@ -345,11 +380,15 @@ module Bundler
end
def is_num(key)
- NUMBER_KEYS.include?(key.to_s)
+ NUMBER_KEYS.include?(self.class.key_to_s(key))
end
def is_array(key)
- ARRAY_KEYS.include?(key.to_s)
+ ARRAY_KEYS.include?(self.class.key_to_s(key))
+ end
+
+ def is_remembered(key)
+ REMEMBERED_KEYS.include?(self.class.key_to_s(key))
end
def is_credential(key)
@@ -372,7 +411,7 @@ module Bundler
end
def set_key(raw_key, value, hash, file)
- raw_key = raw_key.to_s
+ raw_key = self.class.key_to_s(raw_key)
value = array_to_s(value) if is_array(raw_key)
key = key_for(raw_key)
@@ -392,6 +431,8 @@ module Bundler
end
def converted_value(value, key)
+ key = self.class.key_to_s(key)
+
if is_array(key)
to_array(value)
elsif value.nil?
@@ -450,17 +491,19 @@ module Bundler
valid_file = file.exist? && !file.size.zero?
return {} unless valid_file
serializer_class.load(file.read).inject({}) do |config, (k, v)|
- new_k = k
+ unless k.start_with?("#")
+ if k.include?("-")
+ Bundler.ui.warn "Your #{file} config includes `#{k}`, which contains the dash character (`-`).\n" \
+ "This is deprecated, because configuration through `ENV` should be possible, but `ENV` keys cannot include dashes.\n" \
+ "Please edit #{file} and replace any dashes in configuration keys with a triple underscore (`___`)."
- if k.include?("-")
- Bundler.ui.warn "Your #{file} config includes `#{k}`, which contains the dash character (`-`).\n" \
- "This is deprecated, because configuration through `ENV` should be possible, but `ENV` keys cannot include dashes.\n" \
- "Please edit #{file} and replace any dashes in configuration keys with a triple underscore (`___`)."
+ # string hash keys are frozen
+ k = k.gsub("-", "___")
+ end
- new_k = k.gsub("-", "___")
+ config[k] = v
end
- config[new_k] = v
config
end
end
@@ -486,12 +529,15 @@ module Bundler
(https?.*?) # URI
(\.#{Regexp.union(PER_URI_OPTIONS)})? # optional suffix key
\z
- /ix.freeze
+ /ix
def self.key_for(key)
key = normalize_uri(key).to_s if key.is_a?(String) && key.start_with?("http", "mirror.http")
- key = key.to_s.gsub(".", "__").gsub("-", "___").upcase
- "BUNDLE_#{key}"
+ key = key_to_s(key).gsub(".", "__")
+ key.gsub!("-", "___")
+ key.upcase!
+
+ key.prepend("BUNDLE_")
end
# TODO: duplicates Rubygems#normalize_uri
@@ -505,11 +551,40 @@ module Bundler
end
uri = URINormalizer.normalize_suffix(uri)
require_relative "vendored_uri"
- uri = Bundler::URI(uri)
+ uri = Gem::URI(uri)
unless uri.absolute?
raise ArgumentError, format("Gem sources must be absolute. You provided '%s'.", uri)
end
"#{prefix}#{uri}#{suffix}"
end
+
+ # This is a hot method, so avoid respond_to? checks on every invocation
+ if :read.respond_to?(:name)
+ def self.key_to_s(key)
+ case key
+ when String
+ key
+ when Symbol
+ key.name
+ when Gem::URI::HTTP
+ key.to_s
+ else
+ raise ArgumentError, "Invalid key: #{key.inspect}"
+ end
+ end
+ else
+ def self.key_to_s(key)
+ case key
+ when String
+ key
+ when Symbol
+ key.to_s
+ when Gem::URI::HTTP
+ key.to_s
+ else
+ raise ArgumentError, "Invalid key: #{key.inspect}"
+ end
+ end
+ end
end
end
diff --git a/lib/bundler/setup.rb b/lib/bundler/setup.rb
index 801fd5312a..6010d66742 100644
--- a/lib/bundler/setup.rb
+++ b/lib/bundler/setup.rb
@@ -5,6 +5,9 @@ require_relative "shared_helpers"
if Bundler::SharedHelpers.in_bundle?
require_relative "../bundler"
+ # try to auto_install first before we get to the `Bundler.ui.silence`, so user knows what is happening
+ Bundler.auto_install
+
if STDOUT.tty? || ENV["BUNDLER_FORCE_TTY"]
begin
Bundler.ui.silence { Bundler.setup }
@@ -12,7 +15,10 @@ if Bundler::SharedHelpers.in_bundle?
Bundler.ui.error e.message
Bundler.ui.warn e.backtrace.join("\n") if ENV["DEBUG"]
if e.is_a?(Bundler::GemNotFound)
- suggested_cmd = "bundle install"
+ default_bundle = Gem.bin_path("bundler", "bundle")
+ current_bundle = Bundler::SharedHelpers.bundle_bin_path
+ suggested_bundle = default_bundle == current_bundle ? "bundle" : current_bundle
+ suggested_cmd = "#{suggested_bundle} install"
original_gemfile = Bundler.original_env["BUNDLE_GEMFILE"]
suggested_cmd += " --gemfile #{original_gemfile}" if original_gemfile
Bundler.ui.warn "Run `#{suggested_cmd}` to install missing gems."
diff --git a/lib/bundler/shared_helpers.rb b/lib/bundler/shared_helpers.rb
index d1d4e1d07a..78760e6fa4 100644
--- a/lib/bundler/shared_helpers.rb
+++ b/lib/bundler/shared_helpers.rb
@@ -13,13 +13,13 @@ module Bundler
def root
gemfile = find_gemfile
raise GemfileNotFound, "Could not locate Gemfile" unless gemfile
- Pathname.new(gemfile).tap {|x| x.untaint if RUBY_VERSION < "2.7" }.expand_path.parent
+ Pathname.new(gemfile).expand_path.parent
end
def default_gemfile
gemfile = find_gemfile
raise GemfileNotFound, "Could not locate Gemfile" unless gemfile
- Pathname.new(gemfile).tap {|x| x.untaint if RUBY_VERSION < "2.7" }.expand_path
+ Pathname.new(gemfile).expand_path
end
def default_lockfile
@@ -28,7 +28,7 @@ module Bundler
case gemfile.basename.to_s
when "gems.rb" then Pathname.new(gemfile.sub(/.rb$/, ".locked"))
else Pathname.new("#{gemfile}.lock")
- end.tap {|x| x.untaint if RUBY_VERSION < "2.7" }
+ end
end
def default_bundle_dir
@@ -100,7 +100,7 @@ module Bundler
#
# @see {Bundler::PermissionError}
def filesystem_access(path, action = :write, &block)
- yield(path.dup.tap {|x| x.untaint if RUBY_VERSION < "2.7" })
+ yield(path.dup)
rescue Errno::EACCES
raise PermissionError.new(path, action)
rescue Errno::EAGAIN
@@ -117,16 +117,18 @@ module Bundler
raise GenericSystemCallError.new(e, "There was an error accessing `#{path}`.")
end
- def major_deprecation(major_version, message, print_caller_location: false)
+ def major_deprecation(major_version, message, removed_message: nil, print_caller_location: false)
if print_caller_location
caller_location = caller_locations(2, 2).first
- message = "#{message} (called at #{caller_location.path}:#{caller_location.lineno})"
+ suffix = " (called at #{caller_location.path}:#{caller_location.lineno})"
+ message += suffix
+ removed_message += suffix if removed_message
end
bundler_major_version = Bundler.bundler_major_version
if bundler_major_version > major_version
require_relative "errors"
- raise DeprecatedError, "[REMOVED] #{message}"
+ raise DeprecatedError, "[REMOVED] #{removed_message || message}"
end
return unless bundler_major_version >= major_version && prints_major_deprecations?
@@ -193,10 +195,40 @@ module Bundler
Digest(name)
end
+ def checksum_for_file(path, digest)
+ return unless path.file?
+ # This must use File.read instead of Digest.file().hexdigest
+ # because we need to preserve \n line endings on windows when calculating
+ # the checksum
+ SharedHelpers.filesystem_access(path, :read) do
+ File.open(path, "rb") do |f|
+ digest = SharedHelpers.digest(digest).new
+ buf = String.new(capacity: 16_384, encoding: Encoding::BINARY)
+ digest << buf while f.read(16_384, buf)
+ digest.hexdigest
+ end
+ end
+ end
+
def write_to_gemfile(gemfile_path, contents)
filesystem_access(gemfile_path) {|g| File.open(g, "w") {|file| file.puts contents } }
end
+ def relative_gemfile_path
+ relative_path_to(Bundler.default_gemfile)
+ end
+
+ def relative_lockfile_path
+ relative_path_to(Bundler.default_lockfile)
+ end
+
+ def relative_path_to(destination, from: pwd)
+ Pathname.new(destination).relative_path_from(from).to_s
+ rescue ArgumentError
+ # on Windows, if source and destination are on different drivers, there's no relative path from one to the other
+ destination
+ end
+
private
def validate_bundle_path
@@ -235,7 +267,7 @@ module Bundler
def search_up(*names)
previous = nil
- current = File.expand_path(SharedHelpers.pwd).tap {|x| x.untaint if RUBY_VERSION < "2.7" }
+ current = File.expand_path(SharedHelpers.pwd)
until !File.directory?(current) || current == previous
if ENV["BUNDLER_SPEC_RUN"]
@@ -272,6 +304,13 @@ module Bundler
public :set_env
def set_bundle_variables
+ Bundler::SharedHelpers.set_env "BUNDLE_BIN_PATH", bundle_bin_path
+ Bundler::SharedHelpers.set_env "BUNDLE_GEMFILE", find_gemfile.to_s
+ Bundler::SharedHelpers.set_env "BUNDLER_VERSION", Bundler::VERSION
+ Bundler::SharedHelpers.set_env "BUNDLER_SETUP", File.expand_path("setup", __dir__)
+ end
+
+ def bundle_bin_path
# bundler exe & lib folders have same root folder, typical gem installation
exe_file = File.expand_path("../../exe/bundle", __dir__)
@@ -281,11 +320,9 @@ module Bundler
# bundler is a default gem, exe path is separate
exe_file = Bundler.rubygems.bin_path("bundler", "bundle", VERSION) unless File.exist?(exe_file)
- Bundler::SharedHelpers.set_env "BUNDLE_BIN_PATH", exe_file
- Bundler::SharedHelpers.set_env "BUNDLE_GEMFILE", find_gemfile.to_s
- Bundler::SharedHelpers.set_env "BUNDLER_VERSION", Bundler::VERSION
- Bundler::SharedHelpers.set_env "BUNDLER_SETUP", File.expand_path("setup", __dir__) unless RUBY_VERSION < "2.7"
+ exe_file
end
+ public :bundle_bin_path
def set_path
validate_bundle_path
@@ -297,7 +334,7 @@ module Bundler
def set_rubyopt
rubyopt = [ENV["RUBYOPT"]].compact
setup_require = "-r#{File.expand_path("setup", __dir__)}"
- return if !rubyopt.empty? && rubyopt.first =~ /#{setup_require}/
+ return if !rubyopt.empty? && rubyopt.first.include?(setup_require)
rubyopt.unshift setup_require
Bundler::SharedHelpers.set_env "RUBYOPT", rubyopt.join(" ")
end
diff --git a/lib/bundler/source.rb b/lib/bundler/source.rb
index 69804a2e63..115dbd1378 100644
--- a/lib/bundler/source.rb
+++ b/lib/bundler/source.rb
@@ -11,6 +11,8 @@ module Bundler
attr_accessor :dependency_names
+ attr_reader :checksum_store
+
def unmet_deps
specs.unmet_dependency_names
end
@@ -100,7 +102,7 @@ module Bundler
end
def print_using_message(message)
- if !message.include?("(was ") && Bundler.feature_flag.suppress_install_using_messages?
+ if !message.include?("(was ")
Bundler.ui.debug message
else
Bundler.ui.info message
diff --git a/lib/bundler/source/git.rb b/lib/bundler/source/git.rb
index 42897813b4..198e335bb6 100644
--- a/lib/bundler/source/git.rb
+++ b/lib/bundler/source/git.rb
@@ -11,6 +11,7 @@ module Bundler
def initialize(options)
@options = options
+ @checksum_store = Checksum::Store.new
@glob = options["glob"] || DEFAULT_GLOB
@allow_cached = false
@@ -19,7 +20,7 @@ module Bundler
# Stringify options that could be set as symbols
%w[ref branch tag revision].each {|k| options[k] = options[k].to_s if options[k] }
- @uri = URINormalizer.normalize_suffix(options["uri"] || "", :trailing_slash => false)
+ @uri = URINormalizer.normalize_suffix(options["uri"] || "", trailing_slash: false)
@safe_uri = URICredentialsFilter.credential_filtered_uri(@uri)
@branch = options["branch"]
@ref = options["ref"] || options["branch"] || options["tag"]
@@ -46,6 +47,14 @@ module Bundler
out << " specs:\n"
end
+ def to_gemfile
+ specifiers = %w[ref branch tag submodules glob].map do |opt|
+ "#{opt}: #{options[opt]}" if options[opt]
+ end
+
+ uri_with_specifiers(specifiers)
+ end
+
def hash
[self.class, uri, ref, branch, name, version, glob, submodules].hash
end
@@ -59,28 +68,32 @@ module Bundler
alias_method :==, :eql?
+ def include?(other)
+ other.is_a?(Git) && uri == other.uri &&
+ name == other.name &&
+ glob == other.glob &&
+ submodules == other.submodules
+ end
+
def to_s
begin
- at = if local?
- path
- elsif user_ref = options["ref"]
- if /\A[a-z0-9]{4,}\z/i.match?(ref)
- shortref_for_display(user_ref)
- else
- user_ref
- end
- elsif ref
- ref
- else
- current_branch
- end
+ at = humanized_ref || current_branch
rev = "at #{at}@#{shortref_for_display(revision)}"
rescue GitError
""
end
- specifiers = [rev, glob_for_display].compact
+ uri_with_specifiers([rev, glob_for_display])
+ end
+
+ def identifier
+ uri_with_specifiers([humanized_ref, cached_revision, glob_for_display])
+ end
+
+ def uri_with_specifiers(specifiers)
+ specifiers.compact!
+
suffix =
if specifiers.any?
" (#{specifiers.join(", ")})"
@@ -185,7 +198,7 @@ module Bundler
@copied = true
end
- generate_bin_options = { :disable_extensions => !Bundler.rubygems.spec_missing_extensions?(spec), :build_args => options[:build_args] }
+ generate_bin_options = { disable_extensions: !Bundler.rubygems.spec_missing_extensions?(spec), build_args: options[:build_args] }
generate_bin(spec, generate_bin_options)
requires_checkout? ? spec.post_install_message : nil
@@ -243,6 +256,20 @@ module Bundler
private
+ def humanized_ref
+ if local?
+ path
+ elsif user_ref = options["ref"]
+ if /\A[a-z0-9]{4,}\z/i.match?(ref)
+ shortref_for_display(user_ref)
+ else
+ user_ref
+ end
+ elsif ref
+ ref
+ end
+ end
+
def serialize_gemspecs_in(destination)
destination = destination.expand_path(Bundler.root) if destination.relative?
Dir["#{destination}/#{@glob}"].each do |spec_path|
@@ -299,7 +326,7 @@ module Bundler
if %r{^\w+://(\w+@)?}.match?(uri)
# Downcase the domain component of the URI
# and strip off a trailing slash, if one is present
- input = Bundler::URI.parse(uri).normalize.to_s.sub(%r{/$}, "")
+ input = Gem::URI.parse(uri).normalize.to_s.sub(%r{/$}, "")
else
# If there is no URI scheme, assume it is an ssh/git URI
input = uri
@@ -333,7 +360,7 @@ module Bundler
def load_gemspec(file)
stub = Gem::StubSpecification.gemspec_stub(file, install_path.parent, install_path.parent)
- stub.full_gem_path = Pathname.new(file).dirname.expand_path(root).to_s.tap {|x| x.untaint if RUBY_VERSION < "2.7" }
+ stub.full_gem_path = Pathname.new(file).dirname.expand_path(root).to_s
StubSpecification.from_stub(stub)
end
diff --git a/lib/bundler/source/git/git_proxy.rb b/lib/bundler/source/git/git_proxy.rb
index 926c9b8ead..645851286c 100644
--- a/lib/bundler/source/git/git_proxy.rb
+++ b/lib/bundler/source/git/git_proxy.rb
@@ -43,6 +43,13 @@ module Bundler
end
end
+ class AmbiguousGitReference < GitError
+ def initialize(options)
+ msg = "Specification of branch or ref with tag is ambiguous. You specified #{options.inspect}"
+ super msg
+ end
+ end
+
# The GitProxy is responsible to interact with git repositories.
# All actions required by the Git source is encapsulated in this
# object.
@@ -53,10 +60,15 @@ module Bundler
def initialize(path, uri, options = {}, revision = nil, git = nil)
@path = path
@uri = uri
- @branch = options["branch"]
@tag = options["tag"]
+ @branch = options["branch"]
@ref = options["ref"]
- @explicit_ref = branch || tag || ref
+ if @tag
+ raise AmbiguousGitReference.new(options) if @branch || @ref
+ @explicit_ref = @tag
+ else
+ @explicit_ref = @ref || @branch
+ end
@revision = revision
@git = git
@commit_ref = nil
@@ -67,15 +79,15 @@ module Bundler
end
def current_branch
- @current_branch ||= allowed_with_path do
- git("rev-parse", "--abbrev-ref", "HEAD", :dir => path).strip
+ @current_branch ||= with_path do
+ git_local("rev-parse", "--abbrev-ref", "HEAD", dir: path).strip
end
end
def contains?(commit)
allowed_with_path do
- result, status = git_null("branch", "--contains", commit, :dir => path)
- status.success? && result =~ /^\* (.*)$/
+ result, status = git_null("branch", "--contains", commit, dir: path)
+ status.success? && result.match?(/^\* (.*)$/)
end
end
@@ -84,7 +96,7 @@ module Bundler
end
def full_version
- @full_version ||= git("--version").sub(/git version\s*/, "").strip
+ @full_version ||= git_local("--version").sub(/git version\s*/, "").strip
end
def checkout
@@ -118,15 +130,20 @@ module Bundler
end
end
- git "fetch", "--force", "--quiet", *extra_fetch_args, :dir => destination if @commit_ref
+ ref = @commit_ref || (locked_to_full_sha? && @revision)
+ if ref
+ git "config", "uploadpack.allowAnySHA1InWant", "true", dir: path.to_s if @commit_ref.nil? && needs_allow_any_sha1_in_want?
- git "reset", "--hard", @revision, :dir => destination
+ git "fetch", "--force", "--quiet", *extra_fetch_args(ref), dir: destination
+ end
+
+ git "reset", "--hard", @revision, dir: destination
if submodules
- git_retry "submodule", "update", "--init", "--recursive", :dir => destination
+ git_retry "submodule", "update", "--init", "--recursive", dir: destination
elsif Gem::Version.create(version) >= Gem::Version.create("2.9.0")
inner_command = "git -C $toplevel submodule deinit --force $sm_path"
- git_retry "submodule", "foreach", "--quiet", inner_command, :dir => destination
+ git_retry "submodule", "foreach", "--quiet", inner_command, dir: destination
end
end
@@ -235,38 +252,46 @@ module Bundler
end
def pinned_to_full_sha?
- ref =~ /\A\h{40}\z/
+ full_sha_revision?(ref)
+ end
+
+ def locked_to_full_sha?
+ full_sha_revision?(@revision)
+ end
+
+ def full_sha_revision?(ref)
+ ref&.match?(/\A\h{40}\z/)
end
def git_null(*command, dir: nil)
check_allowed(command)
- capture(command, dir, :ignore_err => true)
+ capture(command, dir, ignore_err: true)
end
def git_retry(*command, dir: nil)
command_with_no_credentials = check_allowed(command)
Bundler::Retry.new("`#{command_with_no_credentials}` at #{dir || SharedHelpers.pwd}").attempts do
- git(*command, :dir => dir)
+ git(*command, dir: dir)
end
end
def git(*command, dir: nil)
- command_with_no_credentials = check_allowed(command)
-
- out, err, status = capture(command, dir)
-
- raise GitCommandError.new(command_with_no_credentials, dir || SharedHelpers.pwd, err) unless status.success?
-
- Bundler.ui.warn err unless err.empty?
+ run_command(*command, dir: dir) do |unredacted_command|
+ check_allowed(unredacted_command)
+ end
+ end
- out
+ def git_local(*command, dir: nil)
+ run_command(*command, dir: dir) do |unredacted_command|
+ redact_and_check_presence(unredacted_command)
+ end
end
def has_revision_cached?
return unless @revision && path.exist?
- git("cat-file", "-e", @revision, :dir => path)
+ git("cat-file", "-e", @revision, dir: path)
true
rescue GitError
false
@@ -289,13 +314,13 @@ module Bundler
end
def verify(reference)
- git("rev-parse", "--verify", reference, :dir => path).strip
+ git("rev-parse", "--verify", reference, dir: path).strip
end
# Adds credentials to the URI
def configured_uri
if /https?:/.match?(uri)
- remote = Bundler::URI(uri)
+ remote = Gem::URI(uri)
config_auth = Bundler.settings[remote.to_s] || Bundler.settings[remote.host]
remote.userinfo ||= config_auth
remote.to_s
@@ -330,12 +355,30 @@ module Bundler
end
def check_allowed(command)
- require "shellwords"
- command_with_no_credentials = URICredentialsFilter.credential_filtered_string("git #{command.shelljoin}", uri)
+ command_with_no_credentials = redact_and_check_presence(command)
raise GitNotAllowedError.new(command_with_no_credentials) unless allow?
command_with_no_credentials
end
+ def redact_and_check_presence(command)
+ raise GitNotInstalledError.new unless Bundler.git_present?
+
+ require "shellwords"
+ URICredentialsFilter.credential_filtered_string("git #{command.shelljoin}", uri)
+ end
+
+ def run_command(*command, dir: nil)
+ command_with_no_credentials = yield(command)
+
+ out, err, status = capture(command, dir)
+
+ raise GitCommandError.new(command_with_no_credentials, dir || SharedHelpers.pwd, err) unless status.success?
+
+ Bundler.ui.warn err unless err.empty?
+
+ out
+ end
+
def capture(cmd, dir, ignore_err: false)
SharedHelpers.with_clean_git_env do
require "open3"
@@ -355,7 +398,7 @@ module Bundler
if Bundler.feature_flag.bundler_3_mode? || supports_minus_c?
["git", "-C", dir.to_s, *cmd]
else
- ["git", *cmd, { :chdir => dir.to_s }]
+ ["git", *cmd, { chdir: dir.to_s }]
end
end
@@ -381,9 +424,9 @@ module Bundler
["--depth", depth.to_s]
end
- def extra_fetch_args
+ def extra_fetch_args(ref)
extra_args = [path.to_s, *depth_args]
- extra_args.push(@commit_ref)
+ extra_args.push(ref)
extra_args
end
@@ -395,6 +438,10 @@ module Bundler
@supports_minus_c ||= Gem::Version.new(version) >= Gem::Version.new("1.8.5")
end
+ def needs_allow_any_sha1_in_want?
+ @needs_allow_any_sha1_in_want ||= Gem::Version.new(version) <= Gem::Version.new("2.13.7")
+ end
+
def supports_fetching_unreachable_refs?
@supports_fetching_unreachable_refs ||= Gem::Version.new(version) >= Gem::Version.new("2.5.0")
end
diff --git a/lib/bundler/source/metadata.rb b/lib/bundler/source/metadata.rb
index 593da6d1a7..4d27761365 100644
--- a/lib/bundler/source/metadata.rb
+++ b/lib/bundler/source/metadata.rb
@@ -5,27 +5,27 @@ module Bundler
class Metadata < Source
def specs
@specs ||= Index.build do |idx|
- idx << Gem::Specification.new("Ruby\0", Gem.ruby_version)
+ idx << Gem::Specification.new("Ruby\0", Bundler::RubyVersion.system.gem_version)
idx << Gem::Specification.new("RubyGems\0", Gem::VERSION) do |s|
s.required_rubygems_version = Gem::Requirement.default
end
- idx << Gem::Specification.new do |s|
- s.name = "bundler"
- s.version = VERSION
- s.license = "MIT"
- s.platform = Gem::Platform::RUBY
- s.authors = ["bundler team"]
- s.bindir = "exe"
- s.homepage = "https://bundler.io"
- s.summary = "The best way to manage your application's dependencies"
- s.executables = %w[bundle]
- # can't point to the actual gemspec or else the require paths will be wrong
- s.loaded_from = __dir__
- end
-
- if local_spec = Bundler.rubygems.find_bundler(VERSION)
+ if local_spec = Gem.loaded_specs["bundler"]
idx << local_spec
+ else
+ idx << Gem::Specification.new do |s|
+ s.name = "bundler"
+ s.version = VERSION
+ s.license = "MIT"
+ s.platform = Gem::Platform::RUBY
+ s.authors = ["bundler team"]
+ s.bindir = "exe"
+ s.homepage = "https://bundler.io"
+ s.summary = "The best way to manage your application's dependencies"
+ s.executables = %w[bundle]
+ # can't point to the actual gemspec or else the require paths will be wrong
+ s.loaded_from = __dir__
+ end
end
idx.each {|s| s.source = self }
diff --git a/lib/bundler/source/path.rb b/lib/bundler/source/path.rb
index bdfcf8274a..978b0b2c9f 100644
--- a/lib/bundler/source/path.rb
+++ b/lib/bundler/source/path.rb
@@ -14,6 +14,7 @@ module Bundler
DEFAULT_GLOB = "{,*,*/*}.gemspec"
def initialize(options)
+ @checksum_store = Checksum::Store.new
@options = options.dup
@glob = options["glob"] || DEFAULT_GLOB
@@ -85,7 +86,7 @@ module Bundler
using_message = "Using #{version_message(spec, options[:previous_spec])} from #{self}"
using_message += " and installing its executables" unless spec.executables.empty?
print_using_message using_message
- generate_bin(spec, :disable_extensions => true)
+ generate_bin(spec, disable_extensions: true)
nil # no post-install message
end
@@ -225,7 +226,7 @@ module Bundler
# Some gem authors put absolute paths in their gemspec
# and we have to save them from themselves
spec.files = spec.files.map do |path|
- next path unless /\A#{Pathname::SEPARATOR_PAT}/.match?(path)
+ next path unless /\A#{Pathname::SEPARATOR_PAT}/o.match?(path)
next if File.directory?(path)
begin
Pathname.new(path).relative_path_from(gem_dir).to_s
@@ -236,10 +237,10 @@ module Bundler
installer = Path::Installer.new(
spec,
- :env_shebang => false,
- :disable_extensions => options[:disable_extensions],
- :build_args => options[:build_args],
- :bundler_extension_cache_path => extension_cache_path(spec)
+ env_shebang: false,
+ disable_extensions: options[:disable_extensions],
+ build_args: options[:build_args],
+ bundler_extension_cache_path: extension_cache_path(spec)
)
installer.post_install
rescue Gem::InvalidSpecificationException => e
diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb
index af82ca6b6c..2e76becb84 100644
--- a/lib/bundler/source/rubygems.rb
+++ b/lib/bundler/source/rubygems.rb
@@ -10,15 +10,16 @@ module Bundler
# Ask for X gems per API request
API_REQUEST_SIZE = 50
- attr_reader :remotes
+ attr_accessor :remotes
def initialize(options = {})
@options = options
@remotes = []
@dependency_names = []
@allow_remote = false
- @allow_cached = false
+ @allow_cached = options["allow_cached"] || false
@allow_local = options["allow_local"] || false
+ @checksum_store = Checksum::Store.new
Array(options["remotes"]).reverse_each {|r| add_remote(r) }
end
@@ -88,13 +89,14 @@ module Bundler
end
def self.from_lock(options)
+ options["remotes"] = Array(options.delete("remote")).reverse
new(options)
end
def to_lock
out = String.new("GEM\n")
remotes.reverse_each do |remote|
- out << " remote: #{suppress_configured_credentials remote}\n"
+ out << " remote: #{remove_auth remote}\n"
end
out << " specs:\n"
end
@@ -123,16 +125,17 @@ module Bundler
end
end
alias_method :name, :identifier
+ alias_method :to_gemfile, :identifier
def specs
@specs ||= begin
# remote_specs usually generates a way larger Index than the other
- # sources, and large_idx.use small_idx is way faster than
- # small_idx.use large_idx.
- idx = @allow_remote ? remote_specs.dup : Index.new
- idx.use(cached_specs, :override_dupes) if @allow_cached || @allow_remote
- idx.use(installed_specs, :override_dupes) if @allow_local
- idx
+ # sources, and large_idx.merge! small_idx is way faster than
+ # small_idx.merge! large_idx.
+ index = @allow_remote ? remote_specs.dup : Index.new
+ index.merge!(cached_specs) if @allow_cached
+ index.merge!(installed_specs) if @allow_local
+ index
end
end
@@ -168,15 +171,14 @@ module Bundler
installer = Bundler::RubyGemsGemInstaller.at(
path,
- :security_policy => Bundler.rubygems.security_policies[Bundler.settings["trust-policy"]],
- :install_dir => install_path.to_s,
- :bin_dir => bin_path.to_s,
- :ignore_dependencies => true,
- :wrappers => true,
- :env_shebang => true,
- :build_args => options[:build_args],
- :bundler_expected_checksum => spec.respond_to?(:checksum) && spec.checksum,
- :bundler_extension_cache_path => extension_cache_path(spec)
+ security_policy: Bundler.rubygems.security_policies[Bundler.settings["trust-policy"]],
+ install_dir: install_path.to_s,
+ bin_dir: bin_path.to_s,
+ ignore_dependencies: true,
+ wrappers: true,
+ env_shebang: true,
+ build_args: options[:build_args],
+ bundler_extension_cache_path: extension_cache_path(spec)
)
if spec.remote
@@ -194,6 +196,8 @@ module Bundler
spec.__swap__(s)
end
+ spec.source.checksum_store.register(spec, installer.gem_checksum)
+
message = "Installing #{version_message(spec, options[:previous_spec])}"
message += " with native extensions" if spec.extensions.any?
Bundler.ui.confirm message
@@ -236,7 +240,7 @@ module Bundler
end
def spec_names
- if @allow_remote && dependency_api_available?
+ if dependency_api_available?
remote_specs.spec_names
else
[]
@@ -244,22 +248,25 @@ module Bundler
end
def unmet_deps
- if @allow_remote && dependency_api_available?
+ if dependency_api_available?
remote_specs.unmet_dependency_names
else
[]
end
end
- def fetchers
- @fetchers ||= remotes.map do |uri|
+ def remote_fetchers
+ @remote_fetchers ||= remotes.to_h do |uri|
remote = Source::Rubygems::Remote.new(uri)
- Bundler::Fetcher.new(remote)
- end
+ [remote, Bundler::Fetcher.new(remote)]
+ end.freeze
+ end
+
+ def fetchers
+ @fetchers ||= remote_fetchers.values.freeze
end
def double_check_for(unmet_dependency_names)
- return unless @allow_remote
return unless dependency_api_available?
unmet_dependency_names = unmet_dependency_names.call
@@ -274,7 +281,9 @@ module Bundler
Bundler.ui.debug "Double checking for #{unmet_dependency_names || "all specs (due to the size of the request)"} in #{self}"
- fetch_names(api_fetchers, unmet_dependency_names, specs, false)
+ fetch_names(api_fetchers, unmet_dependency_names, remote_specs)
+
+ specs.use remote_specs
end
def dependency_names_to_double_check
@@ -303,11 +312,7 @@ module Bundler
end
def credless_remotes
- if Bundler.settings[:allow_deployment_source_credential_changes]
- remotes.map(&method(:remove_auth))
- else
- remotes.map(&method(:suppress_configured_credentials))
- end
+ remotes.map(&method(:remove_auth))
end
def remotes_for_spec(spec)
@@ -340,21 +345,12 @@ module Bundler
def normalize_uri(uri)
uri = URINormalizer.normalize_suffix(uri.to_s)
require_relative "../vendored_uri"
- uri = Bundler::URI(uri)
+ uri = Gem::URI(uri)
raise ArgumentError, "The source must be an absolute URI. For example:\n" \
- "source 'https://rubygems.org'" if !uri.absolute? || (uri.is_a?(Bundler::URI::HTTP) && uri.host.nil?)
+ "source 'https://rubygems.org'" if !uri.absolute? || (uri.is_a?(Gem::URI::HTTP) && uri.host.nil?)
uri
end
- def suppress_configured_credentials(remote)
- remote_nouser = remove_auth(remote)
- if remote.userinfo && remote.userinfo == Bundler.settings[remote_nouser]
- remote_nouser
- else
- remote
- end
- end
-
def remove_auth(remote)
if remote.user || remote.password
remote.dup.tap {|uri| uri.user = uri.password = nil }.to_s
@@ -378,10 +374,9 @@ module Bundler
def cached_specs
@cached_specs ||= begin
- idx = @allow_local ? installed_specs.dup : Index.new
+ idx = Index.new
Dir["#{cache_path}/*.gem"].each do |gemfile|
- next if /^bundler\-[\d\.]+?\.gem/.match?(gemfile)
s ||= Bundler.rubygems.spec_from_gem(gemfile)
s.source = self
idx << s
@@ -392,35 +387,30 @@ module Bundler
end
def api_fetchers
- fetchers.select {|f| f.use_api && f.fetchers.first.api_fetcher? }
+ fetchers.select(&:api_fetcher?)
end
def remote_specs
@remote_specs ||= Index.build do |idx|
index_fetchers = fetchers - api_fetchers
- # gather lists from non-api sites
- fetch_names(index_fetchers, nil, idx, false)
-
- # legacy multi-remote sources need special logic to figure out
- # dependency names and that logic can be very costly if one remote
- # uses the dependency API but others don't. So use full indexes
- # consistently in that particular case.
- allow_api = !multiple_remotes?
-
- fetch_names(api_fetchers, allow_api && dependency_names, idx, false)
+ if index_fetchers.empty?
+ fetch_names(api_fetchers, dependency_names, idx)
+ else
+ fetch_names(fetchers, nil, idx)
+ end
end
end
- def fetch_names(fetchers, dependency_names, index, override_dupes)
+ def fetch_names(fetchers, dependency_names, index)
fetchers.each do |f|
if dependency_names
Bundler.ui.info "Fetching gem metadata from #{URICredentialsFilter.credential_filtered_uri(f.uri)}", Bundler.ui.debug?
- index.use f.specs_with_retry(dependency_names, self), override_dupes
+ index.use f.specs_with_retry(dependency_names, self)
Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over
else
Bundler.ui.info "Fetching source index from #{URICredentialsFilter.credential_filtered_uri(f.uri)}"
- index.use f.specs_with_retry(nil, self), override_dupes
+ index.use f.specs_with_retry(nil, self)
end
end
end
@@ -481,7 +471,8 @@ module Bundler
def download_gem(spec, download_cache_path, previous_spec = nil)
uri = spec.remote.uri
Bundler.ui.confirm("Fetching #{version_message(spec, previous_spec)}")
- Bundler.rubygems.download_gem(spec, uri, download_cache_path)
+ gem_remote_fetcher = remote_fetchers.fetch(spec.remote).gem_remote_fetcher
+ Bundler.rubygems.download_gem(spec, uri, download_cache_path, gem_remote_fetcher)
end
# Returns the global cache path of the calling Rubygems::Source object.
diff --git a/lib/bundler/source/rubygems/remote.rb b/lib/bundler/source/rubygems/remote.rb
index 82c850ffbb..9c5c06de24 100644
--- a/lib/bundler/source/rubygems/remote.rb
+++ b/lib/bundler/source/rubygems/remote.rb
@@ -48,7 +48,7 @@ module Bundler
end
uri
- rescue Bundler::URI::InvalidComponentError
+ rescue Gem::URI::InvalidComponentError
error_message = "Please CGI escape your usernames and passwords before " \
"setting them for authentication."
raise HTTPError.new(error_message)
diff --git a/lib/bundler/source_list.rb b/lib/bundler/source_list.rb
index 63798db941..bbaac33a95 100644
--- a/lib/bundler/source_list.rb
+++ b/lib/bundler/source_list.rb
@@ -9,7 +9,7 @@ module Bundler
:metadata_source
def global_rubygems_source
- @global_rubygems_source ||= rubygems_aggregate_class.new("allow_local" => true)
+ @global_rubygems_source ||= rubygems_aggregate_class.new("allow_local" => true, "allow_cached" => true)
end
def initialize
@@ -101,10 +101,6 @@ module Bundler
source_list_for(source).find {|s| equivalent_source?(source, s) }
end
- def get_with_fallback(source)
- get(source) || default_source
- end
-
def lock_sources
lock_other_sources + lock_rubygems_sources
end
@@ -161,7 +157,11 @@ module Bundler
end
def map_sources(replacement_sources)
- rubygems, git, plugin = [@rubygems_sources, @git_sources, @plugin_sources].map do |sources|
+ rubygems = @rubygems_sources.map do |source|
+ replace_rubygems_source(replacement_sources, source) || source
+ end
+
+ git, plugin = [@git_sources, @plugin_sources].map do |sources|
sources.map do |source|
replacement_sources.find {|s| s == source } || source
end
@@ -175,10 +175,19 @@ module Bundler
end
def global_replacement_source(replacement_sources)
- replacement_source = replacement_sources.find {|s| s == global_rubygems_source }
+ replacement_source = replace_rubygems_source(replacement_sources, global_rubygems_source)
return global_rubygems_source unless replacement_source
- replacement_source.local!
+ replacement_source.cached!
+ replacement_source
+ end
+
+ def replace_rubygems_source(replacement_sources, gemfile_source)
+ replacement_source = replacement_sources.find {|s| s == gemfile_source }
+ return unless replacement_source
+
+ # locked sources never include credentials so always prefer remotes from the gemfile
+ replacement_source.remotes = gemfile_source.remotes
replacement_source
end
diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb
index 21630e3a3e..2933d28450 100644
--- a/lib/bundler/spec_set.rb
+++ b/lib/bundler/spec_set.rb
@@ -37,7 +37,7 @@ module Bundler
specs_for_dep.first.dependencies.each do |d|
next if d.type == :development
- incomplete = true if d.name != "bundler" && lookup[d.name].empty?
+ incomplete = true if d.name != "bundler" && lookup[d.name].nil?
deps << [d, dep[1]]
end
else
@@ -45,28 +45,64 @@ module Bundler
end
if incomplete && check
- @incomplete_specs += lookup[name].any? ? lookup[name] : [LazySpecification.new(name, nil, nil)]
+ @incomplete_specs += lookup[name] || [LazySpecification.new(name, nil, nil)]
end
end
specs.uniq
end
+ def add_extra_platforms!(platforms)
+ return platforms.concat([Gem::Platform::RUBY]).uniq if @specs.empty?
+
+ new_platforms = all_platforms.select do |platform|
+ next if platforms.include?(platform)
+ next unless GemHelpers.generic(platform) == Gem::Platform::RUBY
+
+ complete_platform(platform)
+ end
+ return platforms if new_platforms.empty?
+
+ platforms.concat(new_platforms)
+
+ less_specific_platform = new_platforms.find {|platform| platform != Gem::Platform::RUBY && Bundler.local_platform === platform && platform === Bundler.local_platform }
+ platforms.delete(Bundler.local_platform) if less_specific_platform
+
+ platforms
+ end
+
+ def complete_platforms!(platforms)
+ platforms.each do |platform|
+ complete_platform(platform)
+ end
+ end
+
+ def validate_deps(s)
+ s.runtime_dependencies.each do |dep|
+ next if dep.name == "bundler"
+
+ return :missing unless names.include?(dep.name)
+ return :invalid if none? {|spec| dep.matches_spec?(spec) }
+ end
+
+ :valid
+ end
+
def [](key)
key = key.name if key.respond_to?(:name)
- lookup[key].reverse
+ lookup[key]&.reverse || []
end
def []=(key, value)
@specs << value
- @lookup = nil
- @sorted = nil
+
+ reset!
end
def delete(specs)
specs.each {|spec| @specs.delete(spec) }
- @lookup = nil
- @sorted = nil
+
+ reset!
end
def sort!
@@ -100,12 +136,12 @@ module Bundler
end
end
- def incomplete_ruby_specs?(deps)
+ def incomplete_for_platform?(deps, platform)
return false if @specs.empty?
@incomplete_specs = []
- self.for(deps, true, [Gem::Platform::RUBY])
+ self.for(deps, true, [platform])
@incomplete_specs.any?
end
@@ -114,16 +150,6 @@ module Bundler
@specs.select {|s| s.is_a?(LazySpecification) }
end
- def merge(set)
- arr = sorted.dup
- set.each do |set_spec|
- full_name = set_spec.full_name
- next if arr.any? {|spec| spec.full_name == full_name }
- arr << set_spec
- end
- SpecSet.new(arr)
- end
-
def -(other)
SpecSet.new(to_a - other.to_a)
end
@@ -134,12 +160,12 @@ module Bundler
def delete_by_name(name)
@specs.reject! {|spec| spec.name == name }
- @lookup = nil
- @sorted = nil
+
+ reset!
end
def what_required(spec)
- unless req = find {|s| s.dependencies.any? {|d| d.type == :runtime && d.name == spec.name } }
+ unless req = find {|s| s.runtime_dependencies.any? {|d| d.name == spec.name } }
return [spec]
end
what_required(req) << spec
@@ -165,8 +191,52 @@ module Bundler
sorted.each(&b)
end
+ def names
+ lookup.keys
+ end
+
private
+ def reset!
+ @sorted = nil
+ @lookup = nil
+ end
+
+ def complete_platform(platform)
+ new_specs = []
+
+ valid_platform = lookup.all? do |_, specs|
+ spec = specs.first
+ matching_specs = spec.source.specs.search([spec.name, spec.version])
+ platform_spec = GemHelpers.select_best_platform_match(matching_specs, platform).find do |s|
+ s.matches_current_metadata? && valid_dependencies?(s)
+ end
+
+ if platform_spec
+ new_specs << LazySpecification.from_spec(platform_spec) unless specs.include?(platform_spec)
+ true
+ else
+ false
+ end
+ end
+
+ if valid_platform && new_specs.any?
+ @specs.concat(new_specs)
+
+ reset!
+ end
+
+ valid_platform
+ end
+
+ def all_platforms
+ @specs.flat_map {|spec| spec.source.specs.search([spec.name, spec.version]).map(&:platform) }.uniq
+ end
+
+ def valid_dependencies?(s)
+ validate_deps(s) == :valid
+ end
+
def sorted
rake = @specs.find {|s| s.name == "rake" }
begin
@@ -185,8 +255,9 @@ module Bundler
def lookup
@lookup ||= begin
- lookup = Hash.new {|h, k| h[k] = [] }
+ lookup = {}
@specs.each do |s|
+ lookup[s.name] ||= []
lookup[s.name] << s
end
lookup
@@ -200,8 +271,13 @@ module Bundler
def specs_for_dependency(dep, platform)
specs_for_name = lookup[dep.name]
- target_platform = dep.force_ruby_platform ? Gem::Platform::RUBY : (platform || Bundler.local_platform)
- matching_specs = GemHelpers.select_best_platform_match(specs_for_name, target_platform)
+ return [] unless specs_for_name
+
+ matching_specs = if dep.force_ruby_platform
+ GemHelpers.force_ruby_platform(specs_for_name)
+ else
+ GemHelpers.select_best_platform_match(specs_for_name, platform || Bundler.local_platform)
+ end
matching_specs.map!(&:materialize_for_installation).compact! if platform.nil?
matching_specs
end
@@ -209,7 +285,11 @@ module Bundler
def tsort_each_child(s)
s.dependencies.sort_by(&:name).each do |d|
next if d.type == :development
- lookup[d.name].each {|s2| yield s2 }
+
+ specs_for_name = lookup[d.name]
+ next unless specs_for_name
+
+ specs_for_name.each {|s2| yield s2 }
end
end
end
diff --git a/lib/bundler/stub_specification.rb b/lib/bundler/stub_specification.rb
index 88a4257fa4..da830cf8d4 100644
--- a/lib/bundler/stub_specification.rb
+++ b/lib/bundler/stub_specification.rb
@@ -9,6 +9,7 @@ module Bundler
spec
end
+ attr_reader :checksum
attr_accessor :stub, :ignored
def source=(source)
@@ -16,7 +17,8 @@ module Bundler
# Stub has no concept of source, which means that extension_dir may be wrong
# This is the case for git-based gems. So, instead manually assign the extension dir
return unless source.respond_to?(:extension_dir_name)
- path = File.join(stub.extensions_dir, source.extension_dir_name)
+ unique_extension_dir = [source.extension_dir_name, File.basename(full_gem_path)].uniq.join("-")
+ path = File.join(stub.extensions_dir, unique_extension_dir)
stub.extension_dir = File.expand_path(path)
end
@@ -56,7 +58,7 @@ module Bundler
end
def gem_build_complete_path
- File.join(extension_dir, "gem.build_complete")
+ stub.gem_build_complete_path
end
def default_gem?
@@ -108,6 +110,7 @@ module Bundler
end
rs.source = source
+ rs.base_dir = stub.base_dir
rs
end
diff --git a/lib/bundler/templates/Executable.bundler b/lib/bundler/templates/Executable.bundler
index e290fe91eb..caa2021701 100644
--- a/lib/bundler/templates/Executable.bundler
+++ b/lib/bundler/templates/Executable.bundler
@@ -27,7 +27,7 @@ m = Module.new do
bundler_version = nil
update_index = nil
ARGV.each_with_index do |a, i|
- if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
+ if update_index && update_index.succ == i && a.match?(Gem::Version::ANCHORED_VERSION_PATTERN)
bundler_version = a
end
next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
diff --git a/lib/bundler/templates/newgem/Rakefile.tt b/lib/bundler/templates/newgem/Rakefile.tt
index b5a5c4e392..172183d4b4 100644
--- a/lib/bundler/templates/newgem/Rakefile.tt
+++ b/lib/bundler/templates/newgem/Rakefile.tt
@@ -4,13 +4,9 @@ require "bundler/gem_tasks"
<% default_task_names = [config[:test_task]].compact -%>
<% case config[:test] -%>
<% when "minitest" -%>
-require "rake/testtask"
+require "minitest/test_task"
-Rake::TestTask.new(:test) do |t|
- t.libs << "test"
- t.libs << "lib"
- t.test_files = FileList["test/**/test_*.rb"]
-end
+Minitest::TestTask.create
<% when "test-unit" -%>
require "rake/testtask"
@@ -46,7 +42,9 @@ require "rb_sys/extensiontask"
task build: :compile
-RbSys::ExtensionTask.new(<%= config[:name].inspect %>) do |ext|
+GEMSPEC = Gem::Specification.load("<%= config[:underscored_name] %>.gemspec")
+
+RbSys::ExtensionTask.new(<%= config[:name].inspect %>, GEMSPEC) do |ext|
ext.lib_dir = "lib/<%= config[:namespaced_path] %>"
end
<% else -%>
@@ -54,7 +52,9 @@ require "rake/extensiontask"
task build: :compile
-Rake::ExtensionTask.new("<%= config[:underscored_name] %>") do |ext|
+GEMSPEC = Gem::Specification.load("<%= config[:underscored_name] %>.gemspec")
+
+Rake::ExtensionTask.new("<%= config[:underscored_name] %>", GEMSPEC) do |ext|
ext.lib_dir = "lib/<%= config[:namespaced_path] %>"
end
<% end -%>
diff --git a/lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt b/lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt
index 4b6e9587f7..0ebce0e4a0 100644
--- a/lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt
+++ b/lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt
@@ -12,4 +12,4 @@ publish = false
crate-type = ["cdylib"]
[dependencies]
-magnus = { version = "0.4" }
+magnus = { version = "0.6.2" }
diff --git a/lib/bundler/templates/newgem/ext/newgem/src/lib.rs.tt b/lib/bundler/templates/newgem/ext/newgem/src/lib.rs.tt
index b311283997..ba234529a3 100644
--- a/lib/bundler/templates/newgem/ext/newgem/src/lib.rs.tt
+++ b/lib/bundler/templates/newgem/ext/newgem/src/lib.rs.tt
@@ -1,12 +1,12 @@
-use magnus::{define_module, function, prelude::*, Error};
+use magnus::{function, prelude::*, Error, Ruby};
fn hello(subject: String) -> String {
- format!("Hello from Rust, {}!", subject)
+ format!("Hello from Rust, {subject}!")
}
#[magnus::init]
-fn init() -> Result<(), Error> {
- let module = <%= config[:constant_array].map {|c| "define_module(#{c.dump})?"}.join(".") %>;
+fn init(ruby: &Ruby) -> Result<(), Error> {
+ let module = ruby.<%= config[:constant_array].map {|c| "define_module(#{c.dump})?"}.join(".") %>;
module.define_singleton_method("hello", function!(hello, 1))?;
Ok(())
}
diff --git a/lib/bundler/templates/newgem/github/workflows/main.yml.tt b/lib/bundler/templates/newgem/github/workflows/main.yml.tt
index be58dd8156..32b39558d8 100644
--- a/lib/bundler/templates/newgem/github/workflows/main.yml.tt
+++ b/lib/bundler/templates/newgem/github/workflows/main.yml.tt
@@ -17,7 +17,7 @@ jobs:
- '<%= RUBY_VERSION %>'
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
<%- if config[:ext] == 'rust' -%>
- name: Set up Ruby & Rust
uses: oxidize-rb/actions/setup-ruby-and-rust@v1
diff --git a/lib/bundler/templates/newgem/newgem.gemspec.tt b/lib/bundler/templates/newgem/newgem.gemspec.tt
index da81f046d4..6e88f4dab1 100644
--- a/lib/bundler/templates/newgem/newgem.gemspec.tt
+++ b/lib/bundler/templates/newgem/newgem.gemspec.tt
@@ -27,9 +27,11 @@ Gem::Specification.new do |spec|
# Specify which files should be added to the gem when it is released.
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
- spec.files = Dir.chdir(__dir__) do
- `git ls-files -z`.split("\x0").reject do |f|
- (File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor])
+ gemspec = File.basename(__FILE__)
+ spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls|
+ ls.readlines("\x0", chomp: true).reject do |f|
+ (f == gemspec) ||
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git <%= config[:ci_config_path] %>appveyor Gemfile])
end
end
spec.bindir = "exe"
diff --git a/lib/bundler/templates/newgem/rubocop.yml.tt b/lib/bundler/templates/newgem/rubocop.yml.tt
index 9ecec78807..3d1c4ee7b2 100644
--- a/lib/bundler/templates/newgem/rubocop.yml.tt
+++ b/lib/bundler/templates/newgem/rubocop.yml.tt
@@ -2,12 +2,7 @@ AllCops:
TargetRubyVersion: <%= ::Gem::Version.new(config[:required_ruby_version]).segments[0..1].join(".") %>
Style/StringLiterals:
- Enabled: true
EnforcedStyle: double_quotes
Style/StringLiteralsInInterpolation:
- Enabled: true
EnforcedStyle: double_quotes
-
-Layout/LineLength:
- Max: 120
diff --git a/lib/bundler/templates/newgem/standard.yml.tt b/lib/bundler/templates/newgem/standard.yml.tt
index 934b0b2c37..a0696cd2e9 100644
--- a/lib/bundler/templates/newgem/standard.yml.tt
+++ b/lib/bundler/templates/newgem/standard.yml.tt
@@ -1,3 +1,3 @@
# For available configuration options, see:
-# https://github.com/testdouble/standard
+# https://github.com/standardrb/standard
ruby_version: <%= ::Gem::Version.new(config[:required_ruby_version]).segments[0..1].join(".") %>
diff --git a/lib/bundler/ui/shell.rb b/lib/bundler/ui/shell.rb
index 4139585c47..4555612dbb 100644
--- a/lib/bundler/ui/shell.rb
+++ b/lib/bundler/ui/shell.rb
@@ -130,7 +130,7 @@ module Bundler
def tell_err(message, color = nil, newline = nil)
return if @shell.send(:stderr).closed?
- newline ||= message.to_s !~ /( |\t)\Z/
+ newline ||= !message.to_s.match?(/( |\t)\Z/)
message = word_wrap(message) if newline.is_a?(Hash) && newline[:wrap]
color = nil if color && !$stderr.tty?
@@ -147,7 +147,7 @@ module Bundler
spaces ? text.gsub(/#{spaces}/, "") : text
end
- def word_wrap(text, line_width = @shell.terminal_width)
+ def word_wrap(text, line_width = Thor::Terminal.terminal_width)
strip_leading_spaces(text).split("\n").collect do |line|
line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip : line
end * "\n"
diff --git a/lib/bundler/uri_credentials_filter.rb b/lib/bundler/uri_credentials_filter.rb
index ccfaf0bc5d..a83f5304e2 100644
--- a/lib/bundler/uri_credentials_filter.rb
+++ b/lib/bundler/uri_credentials_filter.rb
@@ -11,7 +11,7 @@ module Bundler
return uri if File.exist?(uri)
require_relative "vendored_uri"
- uri = Bundler::URI(uri)
+ uri = Gem::URI(uri)
end
if uri.userinfo
@@ -25,7 +25,7 @@ module Bundler
end
return uri.to_s if uri_to_anonymize.is_a?(String)
uri
- rescue Bundler::URI::InvalidURIError # uri is not canonical uri scheme
+ rescue Gem::URI::InvalidURIError # uri is not canonical uri scheme
uri
end
diff --git a/lib/rubygems/optparse/.document b/lib/bundler/vendor/connection_pool/.document
index 0c43bbd6b3..0c43bbd6b3 100644
--- a/lib/rubygems/optparse/.document
+++ b/lib/bundler/vendor/connection_pool/.document
diff --git a/lib/bundler/vendor/connection_pool/lib/connection_pool.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool.rb
index 455319efe3..317088a866 100644
--- a/lib/bundler/vendor/connection_pool/lib/connection_pool.rb
+++ b/lib/bundler/vendor/connection_pool/lib/connection_pool.rb
@@ -1,4 +1,4 @@
-require "timeout"
+require_relative "../../../vendored_timeout"
require_relative "connection_pool/version"
class Bundler::ConnectionPool
@@ -6,7 +6,7 @@ class Bundler::ConnectionPool
class PoolShuttingDownError < ::Bundler::ConnectionPool::Error; end
- class TimeoutError < ::Timeout::Error; end
+ class TimeoutError < ::Gem::Timeout::Error; end
end
# Generic connection pool class for sharing a limited number of objects or network connections
@@ -36,14 +36,57 @@ end
# Accepts the following options:
# - :size - number of connections to pool, defaults to 5
# - :timeout - amount of time to wait for a connection if none currently available, defaults to 5 seconds
+# - :auto_reload_after_fork - automatically drop all connections after fork, defaults to true
#
class Bundler::ConnectionPool
- DEFAULTS = {size: 5, timeout: 5}
+ DEFAULTS = {size: 5, timeout: 5, auto_reload_after_fork: true}
def self.wrap(options, &block)
Wrapper.new(options, &block)
end
+ if Process.respond_to?(:fork)
+ INSTANCES = ObjectSpace::WeakMap.new
+ private_constant :INSTANCES
+
+ def self.after_fork
+ INSTANCES.values.each do |pool|
+ next unless pool.auto_reload_after_fork
+
+ # We're on after fork, so we know all other threads are dead.
+ # All we need to do is to ensure the main thread doesn't have a
+ # checked out connection
+ pool.checkin(force: true)
+ pool.reload do |connection|
+ # Unfortunately we don't know what method to call to close the connection,
+ # so we try the most common one.
+ connection.close if connection.respond_to?(:close)
+ end
+ end
+ nil
+ end
+
+ if ::Process.respond_to?(:_fork) # MRI 3.1+
+ module ForkTracker
+ def _fork
+ pid = super
+ if pid == 0
+ Bundler::ConnectionPool.after_fork
+ end
+ pid
+ end
+ end
+ Process.singleton_class.prepend(ForkTracker)
+ end
+ else
+ INSTANCES = nil
+ private_constant :INSTANCES
+
+ def self.after_fork
+ # noop
+ end
+ end
+
def initialize(options = {}, &block)
raise ArgumentError, "Connection pool requires a block" unless block
@@ -51,10 +94,12 @@ class Bundler::ConnectionPool
@size = Integer(options.fetch(:size))
@timeout = options.fetch(:timeout)
+ @auto_reload_after_fork = options.fetch(:auto_reload_after_fork)
@available = TimedStack.new(@size, &block)
@key = :"pool-#{@available.object_id}"
@key_count = :"pool-#{@available.object_id}-count"
+ INSTANCES[self] = self if INSTANCES
end
def with(options = {})
@@ -81,16 +126,16 @@ class Bundler::ConnectionPool
end
end
- def checkin
+ def checkin(force: false)
if ::Thread.current[@key]
- if ::Thread.current[@key_count] == 1
+ if ::Thread.current[@key_count] == 1 || force
@available.push(::Thread.current[@key])
::Thread.current[@key] = nil
::Thread.current[@key_count] = nil
else
::Thread.current[@key_count] -= 1
end
- else
+ elsif !force
raise Bundler::ConnectionPool::Error, "no connections are checked out"
end
@@ -117,6 +162,8 @@ class Bundler::ConnectionPool
# Size of this connection pool
attr_reader :size
+ # Automatically drop all connections after fork
+ attr_reader :auto_reload_after_fork
# Number of pool entries available for checkout at this instant.
def available
diff --git a/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb
index 56ebf69902..384d6fc977 100644
--- a/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb
+++ b/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb
@@ -1,3 +1,3 @@
class Bundler::ConnectionPool
- VERSION = "2.3.0"
+ VERSION = "2.4.1"
end
diff --git a/lib/rubygems/tsort/.document b/lib/bundler/vendor/fileutils/.document
index 0c43bbd6b3..0c43bbd6b3 100644
--- a/lib/rubygems/tsort/.document
+++ b/lib/bundler/vendor/fileutils/.document
diff --git a/lib/bundler/vendor/fileutils/lib/fileutils.rb b/lib/bundler/vendor/fileutils/lib/fileutils.rb
index 211311c069..6db19caf6f 100644
--- a/lib/bundler/vendor/fileutils/lib/fileutils.rb
+++ b/lib/bundler/vendor/fileutils/lib/fileutils.rb
@@ -180,7 +180,7 @@ end
# - {CVE-2004-0452}[https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452].
#
module Bundler::FileUtils
- VERSION = "1.7.0"
+ VERSION = "1.7.2"
def self.private_module_function(name) #:nodoc:
module_function name
@@ -192,8 +192,6 @@ module Bundler::FileUtils
#
# Bundler::FileUtils.pwd # => "/rdoc/fileutils"
#
- # Bundler::FileUtils.getwd is an alias for Bundler::FileUtils.pwd.
- #
# Related: Bundler::FileUtils.cd.
#
def pwd
@@ -235,8 +233,6 @@ module Bundler::FileUtils
# cd ..
# cd fileutils
#
- # Bundler::FileUtils.chdir is an alias for Bundler::FileUtils.cd.
- #
# Related: Bundler::FileUtils.pwd.
#
def cd(dir, verbose: nil, &block) # :yield: dir
@@ -515,8 +511,6 @@ module Bundler::FileUtils
# Raises an exception if +dest+ is the path to an existing file
# and keyword argument +force+ is not +true+.
#
- # Bundler::FileUtils#link is an alias for Bundler::FileUtils#ln.
- #
# Related: Bundler::FileUtils.link_entry (has different options).
#
def ln(src, dest, force: nil, noop: nil, verbose: nil)
@@ -707,8 +701,6 @@ module Bundler::FileUtils
# ln -sf src2.txt dest2.txt
# ln -s srcdir3/src0.txt srcdir3/src1.txt destdir3
#
- # Bundler::FileUtils.symlink is an alias for Bundler::FileUtils.ln_s.
- #
# Related: Bundler::FileUtils.ln_sf.
#
def ln_s(src, dest, force: nil, relative: false, target_directory: true, noop: nil, verbose: nil)
@@ -876,8 +868,6 @@ module Bundler::FileUtils
#
# Raises an exception if +src+ is a directory.
#
- # Bundler::FileUtils.copy is an alias for Bundler::FileUtils.cp.
- #
# Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
#
def cp(src, dest, preserve: nil, noop: nil, verbose: nil)
@@ -1164,8 +1154,6 @@ module Bundler::FileUtils
# mv src0 dest0
# mv src1.txt src1 dest1
#
- # Bundler::FileUtils.move is an alias for Bundler::FileUtils.mv.
- #
def mv(src, dest, force: nil, noop: nil, verbose: nil, secure: nil)
fu_output_message "mv#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
@@ -1223,8 +1211,6 @@ module Bundler::FileUtils
#
# rm src0.dat src0.txt
#
- # Bundler::FileUtils.remove is an alias for Bundler::FileUtils.rm.
- #
# Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
def rm(list, force: nil, noop: nil, verbose: nil)
@@ -1250,8 +1236,6 @@ module Bundler::FileUtils
#
# See Bundler::FileUtils.rm for keyword arguments.
#
- # Bundler::FileUtils.safe_unlink is an alias for Bundler::FileUtils.rm_f.
- #
# Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
def rm_f(list, noop: nil, verbose: nil)
@@ -1339,8 +1323,6 @@ module Bundler::FileUtils
#
# See Bundler::FileUtils.rm_r for keyword arguments.
#
- # Bundler::FileUtils.rmtree is an alias for Bundler::FileUtils.rm_rf.
- #
# Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
def rm_rf(list, noop: nil, verbose: nil, secure: nil)
@@ -1642,7 +1624,13 @@ module Bundler::FileUtils
st = File.stat(s)
unless File.exist?(d) and compare_file(s, d)
remove_file d, true
- copy_file s, d
+ if d.end_with?('/')
+ mkdir_p d
+ copy_file s, d + File.basename(s)
+ else
+ mkdir_p File.expand_path('..', d)
+ copy_file s, d
+ end
File.utime st.atime, st.mtime, d if preserve
File.chmod fu_mode(mode, st), d if mode
File.chown uid, gid, d if uid or gid
diff --git a/lib/bundler/vendor/net-http-persistent/.document b/lib/bundler/vendor/net-http-persistent/.document
new file mode 100644
index 0000000000..0c43bbd6b3
--- /dev/null
+++ b/lib/bundler/vendor/net-http-persistent/.document
@@ -0,0 +1 @@
+# Vendored files do not need to be documented
diff --git a/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb
index a4e1c5a750..c15b346330 100644
--- a/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb
+++ b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb
@@ -1,14 +1,14 @@
-require 'net/http'
-require_relative '../../../../uri/lib/uri'
+require_relative '../../../../../vendored_net_http'
+require_relative '../../../../../vendored_uri'
require 'cgi' # for escaping
require_relative '../../../../connection_pool/lib/connection_pool'
autoload :OpenSSL, 'openssl'
##
-# Persistent connections for Net::HTTP
+# Persistent connections for Gem::Net::HTTP
#
-# Bundler::Persistent::Net::HTTP::Persistent maintains persistent connections across all the
+# Gem::Net::HTTP::Persistent maintains persistent connections across all the
# servers you wish to talk to. For each host:port you communicate with a
# single persistent connection is created.
#
@@ -22,34 +22,34 @@ autoload :OpenSSL, 'openssl'
#
# require 'bundler/vendor/net-http-persistent/lib/net/http/persistent'
#
-# uri = Bundler::URI 'http://example.com/awesome/web/service'
+# uri = Gem::URI 'http://example.com/awesome/web/service'
#
-# http = Bundler::Persistent::Net::HTTP::Persistent.new
+# http = Gem::Net::HTTP::Persistent.new
#
# # perform a GET
# response = http.request uri
#
# # or
#
-# get = Net::HTTP::Get.new uri.request_uri
+# get = Gem::Net::HTTP::Get.new uri.request_uri
# response = http.request get
#
# # create a POST
# post_uri = uri + 'create'
-# post = Net::HTTP::Post.new post_uri.path
+# post = Gem::Net::HTTP::Post.new post_uri.path
# post.set_form_data 'some' => 'cool data'
#
-# # perform the POST, the Bundler::URI is always required
+# # perform the POST, the Gem::URI is always required
# response http.request post_uri, post
#
# Note that for GET, HEAD and other requests that do not have a body you want
-# to use Bundler::URI#request_uri not Bundler::URI#path. The request_uri contains the query
+# to use Gem::URI#request_uri not Gem::URI#path. The request_uri contains the query
# params which are sent in the body for other requests.
#
# == TLS/SSL
#
# TLS connections are automatically created depending upon the scheme of the
-# Bundler::URI. TLS connections are automatically verified against the default
+# Gem::URI. TLS connections are automatically verified against the default
# certificate store for your computer. You can override this by changing
# verify_mode or by specifying an alternate cert_store.
#
@@ -72,7 +72,7 @@ autoload :OpenSSL, 'openssl'
# == Proxies
#
# A proxy can be set through #proxy= or at initialization time by providing a
-# second argument to ::new. The proxy may be the Bundler::URI of the proxy server or
+# second argument to ::new. The proxy may be the Gem::URI of the proxy server or
# <code>:ENV</code> which will consult environment variables.
#
# See #proxy= and #proxy_from_env for details.
@@ -92,7 +92,7 @@ autoload :OpenSSL, 'openssl'
#
# === Segregation
#
-# Each Bundler::Persistent::Net::HTTP::Persistent instance has its own pool of connections. There
+# Each Gem::Net::HTTP::Persistent instance has its own pool of connections. There
# is no sharing with other instances (as was true in earlier versions).
#
# === Idle Timeout
@@ -131,7 +131,7 @@ autoload :OpenSSL, 'openssl'
#
# === Connection Termination
#
-# If you are done using the Bundler::Persistent::Net::HTTP::Persistent instance you may shut down
+# If you are done using the Gem::Net::HTTP::Persistent instance you may shut down
# all the connections in the current thread with #shutdown. This is not
# recommended for normal use, it should only be used when it will be several
# minutes before you make another HTTP request.
@@ -141,7 +141,7 @@ autoload :OpenSSL, 'openssl'
# Ruby will automatically garbage collect and shutdown your HTTP connections
# when the thread terminates.
-class Bundler::Persistent::Net::HTTP::Persistent
+class Gem::Net::HTTP::Persistent
##
# The beginning of Time
@@ -172,12 +172,12 @@ class Bundler::Persistent::Net::HTTP::Persistent
end
##
- # The version of Bundler::Persistent::Net::HTTP::Persistent you are using
+ # The version of Gem::Net::HTTP::Persistent you are using
- VERSION = '4.0.1'
+ VERSION = '4.0.2'
##
- # Error class for errors raised by Bundler::Persistent::Net::HTTP::Persistent. Various
+ # Error class for errors raised by Gem::Net::HTTP::Persistent. Various
# SystemCallErrors are re-raised with a human-readable message under this
# class.
@@ -197,10 +197,10 @@ class Bundler::Persistent::Net::HTTP::Persistent
# NOTE: This may not work on ruby > 1.9.
def self.detect_idle_timeout uri, max = 10
- uri = Bundler::URI uri unless Bundler::URI::Generic === uri
+ uri = Gem::URI uri unless Gem::URI::Generic === uri
uri += '/'
- req = Net::HTTP::Head.new uri.request_uri
+ req = Gem::Net::HTTP::Head.new uri.request_uri
http = new 'net-http-persistent detect_idle_timeout'
@@ -214,7 +214,7 @@ class Bundler::Persistent::Net::HTTP::Persistent
$stderr.puts "HEAD #{uri} => #{response.code}" if $DEBUG
- unless Net::HTTPOK === response then
+ unless Gem::Net::HTTPOK === response then
raise Error, "bad response code #{response.code} detecting idle timeout"
end
@@ -238,7 +238,7 @@ class Bundler::Persistent::Net::HTTP::Persistent
attr_reader :certificate
##
- # For Net::HTTP parity
+ # For Gem::Net::HTTP parity
alias cert certificate
@@ -266,7 +266,7 @@ class Bundler::Persistent::Net::HTTP::Persistent
attr_reader :ciphers
##
- # Sends debug_output to this IO via Net::HTTP#set_debug_output.
+ # Sends debug_output to this IO via Gem::Net::HTTP#set_debug_output.
#
# Never use this method in production code, it causes a serious security
# hole.
@@ -279,7 +279,7 @@ class Bundler::Persistent::Net::HTTP::Persistent
attr_reader :generation # :nodoc:
##
- # Headers that are added to every request using Net::HTTP#add_field
+ # Headers that are added to every request using Gem::Net::HTTP#add_field
attr_reader :headers
@@ -304,7 +304,7 @@ class Bundler::Persistent::Net::HTTP::Persistent
##
# Number of retries to perform if a request fails.
#
- # See also #max_retries=, Net::HTTP#max_retries=.
+ # See also #max_retries=, Gem::Net::HTTP#max_retries=.
attr_reader :max_retries
@@ -325,12 +325,12 @@ class Bundler::Persistent::Net::HTTP::Persistent
attr_reader :name
##
- # Seconds to wait until a connection is opened. See Net::HTTP#open_timeout
+ # Seconds to wait until a connection is opened. See Gem::Net::HTTP#open_timeout
attr_accessor :open_timeout
##
- # Headers that are added to every request using Net::HTTP#[]=
+ # Headers that are added to every request using Gem::Net::HTTP#[]=
attr_reader :override_headers
@@ -340,7 +340,7 @@ class Bundler::Persistent::Net::HTTP::Persistent
attr_reader :private_key
##
- # For Net::HTTP parity
+ # For Gem::Net::HTTP parity
alias key private_key
@@ -360,12 +360,12 @@ class Bundler::Persistent::Net::HTTP::Persistent
attr_reader :pool # :nodoc:
##
- # Seconds to wait until reading one block. See Net::HTTP#read_timeout
+ # Seconds to wait until reading one block. See Gem::Net::HTTP#read_timeout
attr_accessor :read_timeout
##
- # Seconds to wait until writing one block. See Net::HTTP#write_timeout
+ # Seconds to wait until writing one block. See Gem::Net::HTTP#write_timeout
attr_accessor :write_timeout
@@ -450,18 +450,18 @@ class Bundler::Persistent::Net::HTTP::Persistent
attr_reader :verify_mode
##
- # Creates a new Bundler::Persistent::Net::HTTP::Persistent.
+ # Creates a new Gem::Net::HTTP::Persistent.
#
# Set a +name+ for fun. Your library name should be good enough, but this
# otherwise has no purpose.
#
- # +proxy+ may be set to a Bundler::URI::HTTP or :ENV to pick up proxy options from
+ # +proxy+ may be set to a Gem::URI::HTTP or :ENV to pick up proxy options from
# the environment. See proxy_from_env for details.
#
- # In order to use a Bundler::URI for the proxy you may need to do some extra work
- # beyond Bundler::URI parsing if the proxy requires a password:
+ # In order to use a Gem::URI for the proxy you may need to do some extra work
+ # beyond Gem::URI parsing if the proxy requires a password:
#
- # proxy = Bundler::URI 'http://proxy.example'
+ # proxy = Gem::URI 'http://proxy.example'
# proxy.user = 'AzureDiamond'
# proxy.password = 'hunter2'
#
@@ -492,8 +492,8 @@ class Bundler::Persistent::Net::HTTP::Persistent
@socket_options << [Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1] if
Socket.const_defined? :TCP_NODELAY
- @pool = Bundler::Persistent::Net::HTTP::Persistent::Pool.new size: pool_size do |http_args|
- Bundler::Persistent::Net::HTTP::Persistent::Connection.new Net::HTTP, http_args, @ssl_generation
+ @pool = Gem::Net::HTTP::Persistent::Pool.new size: pool_size do |http_args|
+ Gem::Net::HTTP::Persistent::Connection.new Gem::Net::HTTP, http_args, @ssl_generation
end
@certificate = nil
@@ -510,7 +510,7 @@ class Bundler::Persistent::Net::HTTP::Persistent
@verify_mode = nil
@cert_store = nil
- @generation = 0 # incremented when proxy Bundler::URI changes
+ @generation = 0 # incremented when proxy Gem::URI changes
if HAVE_OPENSSL then
@verify_mode = OpenSSL::SSL::VERIFY_PEER
@@ -529,7 +529,7 @@ class Bundler::Persistent::Net::HTTP::Persistent
reconnect_ssl
end
- # For Net::HTTP parity
+ # For Gem::Net::HTTP parity
alias cert= certificate=
##
@@ -648,7 +648,7 @@ class Bundler::Persistent::Net::HTTP::Persistent
end
##
- # Starts the Net::HTTP +connection+
+ # Starts the Gem::Net::HTTP +connection+
def start http
http.set_debug_output @debug_output if @debug_output
@@ -666,7 +666,7 @@ class Bundler::Persistent::Net::HTTP::Persistent
end
##
- # Finishes the Net::HTTP +connection+
+ # Finishes the Gem::Net::HTTP +connection+
def finish connection
connection.finish
@@ -716,16 +716,16 @@ class Bundler::Persistent::Net::HTTP::Persistent
reconnect_ssl
end
- # For Net::HTTP parity
+ # For Gem::Net::HTTP parity
alias key= private_key=
##
- # Sets the proxy server. The +proxy+ may be the Bundler::URI of the proxy server,
+ # Sets the proxy server. The +proxy+ may be the Gem::URI of the proxy server,
# the symbol +:ENV+ which will read the proxy from the environment or nil to
# disable use of a proxy. See #proxy_from_env for details on setting the
# proxy from the environment.
#
- # If the proxy Bundler::URI is set after requests have been made, the next request
+ # If the proxy Gem::URI is set after requests have been made, the next request
# will shut-down and re-open all connections.
#
# The +no_proxy+ query parameter can be used to specify hosts which shouldn't
@@ -736,9 +736,9 @@ class Bundler::Persistent::Net::HTTP::Persistent
def proxy= proxy
@proxy_uri = case proxy
when :ENV then proxy_from_env
- when Bundler::URI::HTTP then proxy
+ when Gem::URI::HTTP then proxy
when nil then # ignore
- else raise ArgumentError, 'proxy must be :ENV or a Bundler::URI::HTTP'
+ else raise ArgumentError, 'proxy must be :ENV or a Gem::URI::HTTP'
end
@no_proxy.clear
@@ -763,13 +763,13 @@ class Bundler::Persistent::Net::HTTP::Persistent
end
##
- # Creates a Bundler::URI for an HTTP proxy server from ENV variables.
+ # Creates a Gem::URI for an HTTP proxy server from ENV variables.
#
# If +HTTP_PROXY+ is set a proxy will be returned.
#
- # If +HTTP_PROXY_USER+ or +HTTP_PROXY_PASS+ are set the Bundler::URI is given the
+ # If +HTTP_PROXY_USER+ or +HTTP_PROXY_PASS+ are set the Gem::URI is given the
# indicated user and password unless HTTP_PROXY contains either of these in
- # the Bundler::URI.
+ # the Gem::URI.
#
# The +NO_PROXY+ ENV variable can be used to specify hosts which shouldn't
# be reached via proxy; if set it should be a comma separated list of
@@ -785,7 +785,7 @@ class Bundler::Persistent::Net::HTTP::Persistent
return nil if env_proxy.nil? or env_proxy.empty?
- uri = Bundler::URI normalize_uri env_proxy
+ uri = Gem::URI normalize_uri env_proxy
env_no_proxy = ENV['no_proxy'] || ENV['NO_PROXY']
@@ -835,7 +835,7 @@ class Bundler::Persistent::Net::HTTP::Persistent
end
##
- # Finishes then restarts the Net::HTTP +connection+
+ # Finishes then restarts the Gem::Net::HTTP +connection+
def reset connection
http = connection.http
@@ -854,16 +854,16 @@ class Bundler::Persistent::Net::HTTP::Persistent
end
##
- # Makes a request on +uri+. If +req+ is nil a Net::HTTP::Get is performed
+ # Makes a request on +uri+. If +req+ is nil a Gem::Net::HTTP::Get is performed
# against +uri+.
#
- # If a block is passed #request behaves like Net::HTTP#request (the body of
+ # If a block is passed #request behaves like Gem::Net::HTTP#request (the body of
# the response will not have been read).
#
- # +req+ must be a Net::HTTPGenericRequest subclass (see Net::HTTP for a list).
+ # +req+ must be a Gem::Net::HTTPGenericRequest subclass (see Gem::Net::HTTP for a list).
def request uri, req = nil, &block
- uri = Bundler::URI uri
+ uri = Gem::URI uri
req = request_setup req || uri
response = nil
@@ -896,14 +896,14 @@ class Bundler::Persistent::Net::HTTP::Persistent
end
##
- # Creates a GET request if +req_or_uri+ is a Bundler::URI and adds headers to the
+ # Creates a GET request if +req_or_uri+ is a Gem::URI and adds headers to the
# request.
#
# Returns the request.
def request_setup req_or_uri # :nodoc:
req = if req_or_uri.respond_to? 'request_uri' then
- Net::HTTP::Get.new req_or_uri.request_uri
+ Gem::Net::HTTP::Get.new req_or_uri.request_uri
else
req_or_uri
end
diff --git a/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/connection.rb b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/connection.rb
index a57a5d1352..8b9ab5cc78 100644
--- a/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/connection.rb
+++ b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/connection.rb
@@ -1,8 +1,8 @@
##
-# A Net::HTTP connection wrapper that holds extra information for managing the
+# A Gem::Net::HTTP connection wrapper that holds extra information for managing the
# connection's lifetime.
-class Bundler::Persistent::Net::HTTP::Persistent::Connection # :nodoc:
+class Gem::Net::HTTP::Persistent::Connection # :nodoc:
attr_accessor :http
@@ -25,9 +25,10 @@ class Bundler::Persistent::Net::HTTP::Persistent::Connection # :nodoc:
ensure
reset
end
+ alias_method :close, :finish
def reset
- @last_use = Bundler::Persistent::Net::HTTP::Persistent::EPOCH
+ @last_use = Gem::Net::HTTP::Persistent::EPOCH
@requests = 0
end
diff --git a/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/pool.rb b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/pool.rb
index 9dfa6ffdb1..04a1e754bf 100644
--- a/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/pool.rb
+++ b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/pool.rb
@@ -1,4 +1,4 @@
-class Bundler::Persistent::Net::HTTP::Persistent::Pool < Bundler::ConnectionPool # :nodoc:
+class Gem::Net::HTTP::Persistent::Pool < Bundler::ConnectionPool # :nodoc:
attr_reader :available # :nodoc:
attr_reader :key # :nodoc:
@@ -6,25 +6,37 @@ class Bundler::Persistent::Net::HTTP::Persistent::Pool < Bundler::ConnectionPool
def initialize(options = {}, &block)
super
- @available = Bundler::Persistent::Net::HTTP::Persistent::TimedStackMulti.new(@size, &block)
+ @available = Gem::Net::HTTP::Persistent::TimedStackMulti.new(@size, &block)
@key = "current-#{@available.object_id}"
end
def checkin net_http_args
- stack = Thread.current[@key][net_http_args] ||= []
+ if net_http_args.is_a?(Hash) && net_http_args.size == 1 && net_http_args[:force]
+ # Bundler::ConnectionPool 2.4+ calls `checkin(force: true)` after fork.
+ # When this happens, we should remove all connections from Thread.current
+ if stacks = Thread.current[@key]
+ stacks.each do |http_args, connections|
+ connections.each do |conn|
+ @available.push conn, connection_args: http_args
+ end
+ connections.clear
+ end
+ end
+ else
+ stack = Thread.current[@key][net_http_args] ||= []
- raise Bundler::ConnectionPool::Error, 'no connections are checked out' if
- stack.empty?
+ raise Bundler::ConnectionPool::Error, 'no connections are checked out' if
+ stack.empty?
- conn = stack.pop
+ conn = stack.pop
- if stack.empty?
- @available.push conn, connection_args: net_http_args
+ if stack.empty?
+ @available.push conn, connection_args: net_http_args
- Thread.current[@key].delete(net_http_args)
- Thread.current[@key] = nil if Thread.current[@key].empty?
+ Thread.current[@key].delete(net_http_args)
+ Thread.current[@key] = nil if Thread.current[@key].empty?
+ end
end
-
nil
end
diff --git a/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb
index 2da881c554..214804fcd9 100644
--- a/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb
+++ b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb
@@ -1,4 +1,4 @@
-class Bundler::Persistent::Net::HTTP::Persistent::TimedStackMulti < Bundler::ConnectionPool::TimedStack # :nodoc:
+class Gem::Net::HTTP::Persistent::TimedStackMulti < Bundler::ConnectionPool::TimedStack # :nodoc:
##
# Returns a new hash that has arrays for keys
diff --git a/lib/bundler/vendor/pub_grub/.document b/lib/bundler/vendor/pub_grub/.document
new file mode 100644
index 0000000000..0c43bbd6b3
--- /dev/null
+++ b/lib/bundler/vendor/pub_grub/.document
@@ -0,0 +1 @@
+# Vendored files do not need to be documented
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb
index 4bf61461b2..36ab06254d 100644
--- a/lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb
@@ -1,4 +1,5 @@
require_relative 'package'
+require_relative 'rubygems'
require_relative 'version_constraint'
require_relative 'incompatibility'
require_relative 'basic_package_source'
diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb
index 2cb8412cf3..4caf6b355b 100644
--- a/lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb
+++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb
@@ -162,7 +162,7 @@ module Bundler::PubGrub
def resolve_conflict(incompatibility)
logger.info { "conflict: #{incompatibility}" }
- new_incompatibility = false
+ new_incompatibility = nil
while !incompatibility.failure?
most_recent_term = nil
@@ -204,7 +204,7 @@ module Bundler::PubGrub
solution.backtrack(previous_level)
if new_incompatibility
- add_incompatibility(incompatibility)
+ add_incompatibility(new_incompatibility)
end
return incompatibility
@@ -219,9 +219,14 @@ module Bundler::PubGrub
new_terms << difference.invert
end
- incompatibility = Incompatibility.new(new_terms, cause: Incompatibility::ConflictCause.new(incompatibility, most_recent_satisfier.cause))
+ new_incompatibility = Incompatibility.new(new_terms, cause: Incompatibility::ConflictCause.new(incompatibility, most_recent_satisfier.cause))
- new_incompatibility = true
+ if incompatibility.to_s == new_incompatibility.to_s
+ logger.info { "!! failed to resolve conflicts, this shouldn't have happened" }
+ break
+ end
+
+ incompatibility = new_incompatibility
partially = difference ? " partially" : ""
logger.info { "! #{most_recent_term} is#{partially} satisfied by #{most_recent_satisfier.term}" }
diff --git a/lib/bundler/vendor/thor/.document b/lib/bundler/vendor/thor/.document
new file mode 100644
index 0000000000..0c43bbd6b3
--- /dev/null
+++ b/lib/bundler/vendor/thor/.document
@@ -0,0 +1 @@
+# Vendored files do not need to be documented
diff --git a/lib/bundler/vendor/thor/lib/thor.rb b/lib/bundler/vendor/thor/lib/thor.rb
index 83ebe25593..627722164f 100644
--- a/lib/bundler/vendor/thor/lib/thor.rb
+++ b/lib/bundler/vendor/thor/lib/thor.rb
@@ -65,8 +65,15 @@ class Bundler::Thor
# Defines the long description of the next command.
#
+ # Long description is by default indented, line-wrapped and repeated whitespace merged.
+ # In order to print long description verbatim, with indentation and spacing exactly
+ # as found in the code, use the +wrap+ option
+ #
+ # long_desc 'your very long description', wrap: false
+ #
# ==== Parameters
# long description<String>
+ # options<Hash>
#
def long_desc(long_description, options = {})
if options[:for]
@@ -74,6 +81,7 @@ class Bundler::Thor
command.long_description = long_description if long_description
else
@long_desc = long_description
+ @long_desc_wrap = options[:wrap] != false
end
end
@@ -133,7 +141,7 @@ class Bundler::Thor
# # magic
# end
#
- # method_option :foo => :bar, :for => :previous_command
+ # method_option :foo, :for => :previous_command
#
# def next_command
# # magic
@@ -153,6 +161,9 @@ class Bundler::Thor
# :hide - If you want to hide this option from the help.
#
def method_option(name, options = {})
+ unless [ Symbol, String ].any? { |klass| name.is_a?(klass) }
+ raise ArgumentError, "Expected a Symbol or String, got #{name.inspect}"
+ end
scope = if options[:for]
find_and_refresh_command(options[:for]).options
else
@@ -163,6 +174,81 @@ class Bundler::Thor
end
alias_method :option, :method_option
+ # Adds and declares option group for exclusive options in the
+ # block and arguments. You can declare options as the outside of the block.
+ #
+ # If :for is given as option, it allows you to change the options from
+ # a previous defined command.
+ #
+ # ==== Parameters
+ # Array[Bundler::Thor::Option.name]
+ # options<Hash>:: :for is applied for previous defined command.
+ #
+ # ==== Examples
+ #
+ # exclusive do
+ # option :one
+ # option :two
+ # end
+ #
+ # Or
+ #
+ # option :one
+ # option :two
+ # exclusive :one, :two
+ #
+ # If you give "--one" and "--two" at the same time ExclusiveArgumentsError
+ # will be raised.
+ #
+ def method_exclusive(*args, &block)
+ register_options_relation_for(:method_options,
+ :method_exclusive_option_names, *args, &block)
+ end
+ alias_method :exclusive, :method_exclusive
+
+ # Adds and declares option group for required at least one of options in the
+ # block of arguments. You can declare options as the outside of the block.
+ #
+ # If :for is given as option, it allows you to change the options from
+ # a previous defined command.
+ #
+ # ==== Parameters
+ # Array[Bundler::Thor::Option.name]
+ # options<Hash>:: :for is applied for previous defined command.
+ #
+ # ==== Examples
+ #
+ # at_least_one do
+ # option :one
+ # option :two
+ # end
+ #
+ # Or
+ #
+ # option :one
+ # option :two
+ # at_least_one :one, :two
+ #
+ # If you do not give "--one" and "--two" AtLeastOneRequiredArgumentError
+ # will be raised.
+ #
+ # You can use at_least_one and exclusive at the same time.
+ #
+ # exclusive do
+ # at_least_one do
+ # option :one
+ # option :two
+ # end
+ # end
+ #
+ # Then it is required either only one of "--one" or "--two".
+ #
+ def method_at_least_one(*args, &block)
+ register_options_relation_for(:method_options,
+ :method_at_least_one_option_names, *args, &block)
+ end
+ alias_method :at_least_one, :method_at_least_one
+
# Prints help information for the given command.
#
# ==== Parameters
@@ -178,9 +264,16 @@ class Bundler::Thor
shell.say " #{banner(command).split("\n").join("\n ")}"
shell.say
class_options_help(shell, nil => command.options.values)
+ print_exclusive_options(shell, command)
+ print_at_least_one_required_options(shell, command)
+
if command.long_description
shell.say "Description:"
- shell.print_wrapped(command.long_description, :indent => 2)
+ if command.wrap_long_description
+ shell.print_wrapped(command.long_description, indent: 2)
+ else
+ shell.say command.long_description
+ end
else
shell.say command.description
end
@@ -197,7 +290,7 @@ class Bundler::Thor
Bundler::Thor::Util.thor_classes_in(self).each do |klass|
list += klass.printable_commands(false)
end
- list.sort! { |a, b| a[0] <=> b[0] }
+ sort_commands!(list)
if defined?(@package_name) && @package_name
shell.say "#{@package_name} commands:"
@@ -205,9 +298,11 @@ class Bundler::Thor
shell.say "Commands:"
end
- shell.print_table(list, :indent => 2, :truncate => true)
+ shell.print_table(list, indent: 2, truncate: true)
shell.say
class_options_help(shell)
+ print_exclusive_options(shell)
+ print_at_least_one_required_options(shell)
end
# Returns commands ready to be printed.
@@ -238,7 +333,7 @@ class Bundler::Thor
define_method(subcommand) do |*args|
args, opts = Bundler::Thor::Arguments.split(args)
- invoke_args = [args, opts, {:invoked_via_subcommand => true, :class_options => options}]
+ invoke_args = [args, opts, {invoked_via_subcommand: true, class_options: options}]
invoke_args.unshift "help" if opts.delete("--help") || opts.delete("-h")
invoke subcommand_class, *invoke_args
end
@@ -346,6 +441,24 @@ class Bundler::Thor
protected
+ # Returns this class exclusive options array set.
+ #
+ # ==== Returns
+ # Array[Array[Bundler::Thor::Option.name]]
+ #
+ def method_exclusive_option_names #:nodoc:
+ @method_exclusive_option_names ||= []
+ end
+
+ # Returns this class at least one of required options array set.
+ #
+ # ==== Returns
+ # Array[Array[Bundler::Thor::Option.name]]
+ #
+ def method_at_least_one_option_names #:nodoc:
+ @method_at_least_one_option_names ||= []
+ end
+
def stop_on_unknown_option #:nodoc:
@stop_on_unknown_option ||= []
end
@@ -355,6 +468,28 @@ class Bundler::Thor
@disable_required_check ||= [:help]
end
+ def print_exclusive_options(shell, command = nil) # :nodoc:
+ opts = []
+ opts = command.method_exclusive_option_names unless command.nil?
+ opts += class_exclusive_option_names
+ unless opts.empty?
+ shell.say "Exclusive Options:"
+ shell.print_table(opts.map{ |ex| ex.map{ |e| "--#{e}"}}, indent: 2 )
+ shell.say
+ end
+ end
+
+ def print_at_least_one_required_options(shell, command = nil) # :nodoc:
+ opts = []
+ opts = command.method_at_least_one_option_names unless command.nil?
+ opts += class_at_least_one_option_names
+ unless opts.empty?
+ shell.say "Required At Least One:"
+ shell.print_table(opts.map{ |ex| ex.map{ |e| "--#{e}"}}, indent: 2 )
+ shell.say
+ end
+ end
+
# The method responsible for dispatching given the args.
def dispatch(meth, given_args, given_opts, config) #:nodoc:
meth ||= retrieve_command_name(given_args)
@@ -415,12 +550,16 @@ class Bundler::Thor
@usage ||= nil
@desc ||= nil
@long_desc ||= nil
+ @long_desc_wrap ||= nil
@hide ||= nil
if @usage && @desc
base_class = @hide ? Bundler::Thor::HiddenCommand : Bundler::Thor::Command
- commands[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options)
- @usage, @desc, @long_desc, @method_options, @hide = nil
+ relations = {exclusive_option_names: method_exclusive_option_names,
+ at_least_one_option_names: method_at_least_one_option_names}
+ commands[meth] = base_class.new(meth, @desc, @long_desc, @long_desc_wrap, @usage, method_options, relations)
+ @usage, @desc, @long_desc, @long_desc_wrap, @method_options, @hide = nil
+ @method_exclusive_option_names, @method_at_least_one_option_names = nil
true
elsif all_commands[meth] || meth == "method_missing"
true
@@ -495,6 +634,14 @@ class Bundler::Thor
"
end
alias_method :subtask_help, :subcommand_help
+
+ # Sort the commands, lexicographically by default.
+ #
+ # Can be overridden in the subclass to change the display order of the
+ # commands.
+ def sort_commands!(list)
+ list.sort! { |a, b| a[0] <=> b[0] }
+ end
end
include Bundler::Thor::Base
diff --git a/lib/bundler/vendor/thor/lib/thor/actions.rb b/lib/bundler/vendor/thor/lib/thor/actions.rb
index a7afaf1d07..ca58182691 100644
--- a/lib/bundler/vendor/thor/lib/thor/actions.rb
+++ b/lib/bundler/vendor/thor/lib/thor/actions.rb
@@ -46,17 +46,17 @@ class Bundler::Thor
# Add runtime options that help actions execution.
#
def add_runtime_options!
- class_option :force, :type => :boolean, :aliases => "-f", :group => :runtime,
- :desc => "Overwrite files that already exist"
+ class_option :force, type: :boolean, aliases: "-f", group: :runtime,
+ desc: "Overwrite files that already exist"
- class_option :pretend, :type => :boolean, :aliases => "-p", :group => :runtime,
- :desc => "Run but do not make any changes"
+ class_option :pretend, type: :boolean, aliases: "-p", group: :runtime,
+ desc: "Run but do not make any changes"
- class_option :quiet, :type => :boolean, :aliases => "-q", :group => :runtime,
- :desc => "Suppress status output"
+ class_option :quiet, type: :boolean, aliases: "-q", group: :runtime,
+ desc: "Suppress status output"
- class_option :skip, :type => :boolean, :aliases => "-s", :group => :runtime,
- :desc => "Skip files that already exist"
+ class_option :skip, type: :boolean, aliases: "-s", group: :runtime,
+ desc: "Skip files that already exist"
end
end
@@ -113,9 +113,9 @@ class Bundler::Thor
#
def relative_to_original_destination_root(path, remove_dot = true)
root = @destination_stack[0]
- if path.start_with?(root) && [File::SEPARATOR, File::ALT_SEPARATOR, nil, ''].include?(path[root.size..root.size])
+ if path.start_with?(root) && [File::SEPARATOR, File::ALT_SEPARATOR, nil, ""].include?(path[root.size..root.size])
path = path.dup
- path[0...root.size] = '.'
+ path[0...root.size] = "."
remove_dot ? (path[2..-1] || "") : path
else
path
@@ -223,8 +223,7 @@ class Bundler::Thor
contents = if is_uri
require "open-uri"
- # for ruby 2.1-2.4
- URI.send(:open, path, "Accept" => "application/x-thor-template", &:read)
+ URI.open(path, "Accept" => "application/x-thor-template", &:read)
else
File.open(path, &:read)
end
@@ -285,7 +284,7 @@ class Bundler::Thor
#
def run_ruby_script(command, config = {})
return unless behavior == :invoke
- run command, config.merge(:with => Bundler::Thor::Util.ruby_command)
+ run command, config.merge(with: Bundler::Thor::Util.ruby_command)
end
# Run a thor command. A hash of options can be given and it's converted to
@@ -316,7 +315,7 @@ class Bundler::Thor
args.push Bundler::Thor::Options.to_switches(config)
command = args.join(" ").strip
- run command, :with => :thor, :verbose => verbose, :pretend => pretend, :capture => capture
+ run command, with: :thor, verbose: verbose, pretend: pretend, capture: capture
end
protected
@@ -324,7 +323,7 @@ class Bundler::Thor
# Allow current root to be shared between invocations.
#
def _shared_configuration #:nodoc:
- super.merge!(:destination_root => destination_root)
+ super.merge!(destination_root: destination_root)
end
def _cleanup_options_and_set(options, key) #:nodoc:
diff --git a/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb b/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb
index 1b90e567ba..6724835b01 100644
--- a/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb
+++ b/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb
@@ -43,7 +43,8 @@ class Bundler::Thor
# Boolean:: true if it is identical, false otherwise.
#
def identical?
- exists? && File.binread(destination) == render
+ # binread uses ASCII-8BIT, so to avoid false negatives, the string must use the same
+ exists? && File.binread(destination) == String.new(render).force_encoding("ASCII-8BIT")
end
# Holds the content to be added to the file.
diff --git a/lib/bundler/vendor/thor/lib/thor/actions/directory.rb b/lib/bundler/vendor/thor/lib/thor/actions/directory.rb
index d37327a139..2f9687c0a5 100644
--- a/lib/bundler/vendor/thor/lib/thor/actions/directory.rb
+++ b/lib/bundler/vendor/thor/lib/thor/actions/directory.rb
@@ -58,7 +58,7 @@ class Bundler::Thor
def initialize(base, source, destination = nil, config = {}, &block)
@source = File.expand_path(Dir[Util.escape_globs(base.find_in_source_paths(source.to_s))].first)
@block = block
- super(base, destination, {:recursive => true}.merge(config))
+ super(base, destination, {recursive: true}.merge(config))
end
def invoke!
diff --git a/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb b/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb
index 284d92c19a..c0bca78525 100644
--- a/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb
+++ b/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb
@@ -33,7 +33,7 @@ class Bundler::Thor
#
def initialize(base, destination, config = {})
@base = base
- @config = {:verbose => true}.merge(config)
+ @config = {verbose: true}.merge(config)
self.destination = destination
end
diff --git a/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb b/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb
index ffc29aa8e2..80a0255996 100644
--- a/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb
+++ b/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb
@@ -66,12 +66,15 @@ class Bundler::Thor
# ==== Parameters
# source<String>:: the address of the given content.
# destination<String>:: the relative path to the destination root.
- # config<Hash>:: give :verbose => false to not log the status.
+ # config<Hash>:: give :verbose => false to not log the status, and
+ # :http_headers => <Hash> to add headers to an http request.
#
# ==== Examples
#
# get "http://gist.github.com/103208", "doc/README"
#
+ # get "http://gist.github.com/103208", "doc/README", :http_headers => {"Content-Type" => "application/json"}
+ #
# get "http://gist.github.com/103208" do |content|
# content.split("\n").first
# end
@@ -82,7 +85,7 @@ class Bundler::Thor
render = if source =~ %r{^https?\://}
require "open-uri"
- URI.send(:open, source) { |input| input.binmode.read }
+ URI.send(:open, source, config.fetch(:http_headers, {})) { |input| input.binmode.read }
else
source = File.expand_path(find_in_source_paths(source.to_s))
File.open(source) { |input| input.binmode.read }
@@ -120,12 +123,7 @@ class Bundler::Thor
context = config.delete(:context) || instance_eval("binding")
create_file destination, nil, config do
- match = ERB.version.match(/(\d+\.\d+\.\d+)/)
- capturable_erb = if match && match[1] >= "2.2.0" # Ruby 2.6+
- CapturableERB.new(::File.binread(source), :trim_mode => "-", :eoutvar => "@output_buffer")
- else
- CapturableERB.new(::File.binread(source), nil, "-", "@output_buffer")
- end
+ capturable_erb = CapturableERB.new(::File.binread(source), trim_mode: "-", eoutvar: "@output_buffer")
content = capturable_erb.tap do |erb|
erb.filename = source
end.result(context)
diff --git a/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb b/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb
index 6572f7a4f1..70526e615f 100644
--- a/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb
+++ b/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb
@@ -21,7 +21,7 @@ class Bundler::Thor
# gems.split(" ").map{ |gem| " config.gem :#{gem}" }.join("\n")
# end
#
- WARNINGS = { unchanged_no_flag: 'File unchanged! Either the supplied flag value not found or the content has already been inserted!' }
+ WARNINGS = {unchanged_no_flag: "File unchanged! Either the supplied flag value not found or the content has already been inserted!"}
def insert_into_file(destination, *args, &block)
data = block_given? ? block : args.shift
@@ -37,7 +37,7 @@ class Bundler::Thor
attr_reader :replacement, :flag, :behavior
def initialize(base, destination, data, config)
- super(base, destination, {:verbose => true}.merge(config))
+ super(base, destination, {verbose: true}.merge(config))
@behavior, @flag = if @config.key?(:after)
[:after, @config.delete(:after)]
@@ -59,6 +59,8 @@ class Bundler::Thor
if exists?
if replace!(/#{flag}/, content, config[:force])
say_status(:invoke)
+ elsif replacement_present?
+ say_status(:unchanged, color: :blue)
else
say_status(:unchanged, warning: WARNINGS[:unchanged_no_flag], color: :red)
end
@@ -96,6 +98,8 @@ class Bundler::Thor
end
elsif warning
warning
+ elsif behavior == :unchanged
+ :unchanged
else
:subtract
end
@@ -103,11 +107,18 @@ class Bundler::Thor
super(status, (color || config[:verbose]))
end
+ def content
+ @content ||= File.read(destination)
+ end
+
+ def replacement_present?
+ content.include?(replacement)
+ end
+
# Adds the content to the file.
#
def replace!(regexp, string, force)
- content = File.read(destination)
- if force || !content.include?(replacement)
+ if force || !replacement_present?
success = content.gsub!(regexp, string)
File.open(destination, "wb") { |file| file.write(content) } unless pretend?
diff --git a/lib/bundler/vendor/thor/lib/thor/base.rb b/lib/bundler/vendor/thor/lib/thor/base.rb
index 211855680c..b156899c1e 100644
--- a/lib/bundler/vendor/thor/lib/thor/base.rb
+++ b/lib/bundler/vendor/thor/lib/thor/base.rb
@@ -24,9 +24,9 @@ class Bundler::Thor
class << self
def deprecation_warning(message) #:nodoc:
- unless ENV['THOR_SILENCE_DEPRECATION']
+ unless ENV["THOR_SILENCE_DEPRECATION"]
warn "Deprecation warning: #{message}\n" +
- 'You can silence deprecations warning by setting the environment variable THOR_SILENCE_DEPRECATION.'
+ "You can silence deprecations warning by setting the environment variable THOR_SILENCE_DEPRECATION."
end
end
end
@@ -60,6 +60,7 @@ class Bundler::Thor
command_options = config.delete(:command_options) # hook for start
parse_options = parse_options.merge(command_options) if command_options
+
if local_options.is_a?(Array)
array_options = local_options
hash_options = {}
@@ -73,9 +74,24 @@ class Bundler::Thor
# Let Bundler::Thor::Options parse the options first, so it can remove
# declared options from the array. This will leave us with
# a list of arguments that weren't declared.
- stop_on_unknown = self.class.stop_on_unknown_option? config[:current_command]
- disable_required_check = self.class.disable_required_check? config[:current_command]
- opts = Bundler::Thor::Options.new(parse_options, hash_options, stop_on_unknown, disable_required_check)
+ current_command = config[:current_command]
+ stop_on_unknown = self.class.stop_on_unknown_option? current_command
+
+ # Give a relation of options.
+ # After parsing, Bundler::Thor::Options check whether right relations are kept
+ relations = if current_command.nil?
+ {exclusive_option_names: [], at_least_one_option_names: []}
+ else
+ current_command.options_relation
+ end
+
+ self.class.class_exclusive_option_names.map { |n| relations[:exclusive_option_names] << n }
+ self.class.class_at_least_one_option_names.map { |n| relations[:at_least_one_option_names] << n }
+
+ disable_required_check = self.class.disable_required_check? current_command
+
+ opts = Bundler::Thor::Options.new(parse_options, hash_options, stop_on_unknown, disable_required_check, relations)
+
self.options = opts.parse(array_options)
self.options = config[:class_options].merge(options) if config[:class_options]
@@ -310,9 +326,92 @@ class Bundler::Thor
# :hide:: -- If you want to hide this option from the help.
#
def class_option(name, options = {})
+ unless [ Symbol, String ].any? { |klass| name.is_a?(klass) }
+ raise ArgumentError, "Expected a Symbol or String, got #{name.inspect}"
+ end
build_option(name, options, class_options)
end
+ # Adds and declares option group for exclusive options in the
+ # block and arguments. You can declare options as the outside of the block.
+ #
+ # ==== Parameters
+ # Array[Bundler::Thor::Option.name]
+ #
+ # ==== Examples
+ #
+ # class_exclusive do
+ # class_option :one
+ # class_option :two
+ # end
+ #
+ # Or
+ #
+ # class_option :one
+ # class_option :two
+ # class_exclusive :one, :two
+ #
+ # If you give "--one" and "--two" at the same time ExclusiveArgumentsError
+ # will be raised.
+ #
+ def class_exclusive(*args, &block)
+ register_options_relation_for(:class_options,
+ :class_exclusive_option_names, *args, &block)
+ end
+
+ # Adds and declares option group for required at least one of options in the
+ # block and arguments. You can declare options as the outside of the block.
+ #
+ # ==== Examples
+ #
+ # class_at_least_one do
+ # class_option :one
+ # class_option :two
+ # end
+ #
+ # Or
+ #
+ # class_option :one
+ # class_option :two
+ # class_at_least_one :one, :two
+ #
+ # If you do not give "--one" and "--two" AtLeastOneRequiredArgumentError
+ # will be raised.
+ #
+ # You can use class_at_least_one and class_exclusive at the same time.
+ #
+ # class_exclusive do
+ # class_at_least_one do
+ # class_option :one
+ # class_option :two
+ # end
+ # end
+ #
+ # Then it is required either only one of "--one" or "--two".
+ #
+ def class_at_least_one(*args, &block)
+ register_options_relation_for(:class_options,
+ :class_at_least_one_option_names, *args, &block)
+ end
+
+ # Returns this class exclusive options array set, looking up in the ancestors chain.
+ #
+ # ==== Returns
+ # Array[Array[Bundler::Thor::Option.name]]
+ #
+ def class_exclusive_option_names
+ @class_exclusive_option_names ||= from_superclass(:class_exclusive_option_names, [])
+ end
+
+ # Returns this class at least one of required options array set, looking up in the ancestors chain.
+ #
+ # ==== Returns
+ # Array[Array[Bundler::Thor::Option.name]]
+ #
+ def class_at_least_one_option_names
+ @class_at_least_one_option_names ||= from_superclass(:class_at_least_one_option_names, [])
+ end
+
# Removes a previous defined argument. If :undefine is given, undefine
# accessors as well.
#
@@ -565,12 +664,12 @@ class Bundler::Thor
item.push(option.description ? "# #{option.description}" : "")
list << item
- list << ["", "# Default: #{option.default}"] if option.show_default?
- list << ["", "# Possible values: #{option.enum.join(', ')}"] if option.enum
+ list << ["", "# Default: #{option.print_default}"] if option.show_default?
+ list << ["", "# Possible values: #{option.enum_to_s}"] if option.enum
end
shell.say(group_name ? "#{group_name} options:" : "Options:")
- shell.print_table(list, :indent => 2)
+ shell.print_table(list, indent: 2)
shell.say ""
end
@@ -587,7 +686,7 @@ class Bundler::Thor
# options<Hash>:: Described in both class_option and method_option.
# scope<Hash>:: Options hash that is being built up
def build_option(name, options, scope) #:nodoc:
- scope[name] = Bundler::Thor::Option.new(name, {:check_default_type => check_default_type}.merge!(options))
+ scope[name] = Bundler::Thor::Option.new(name, {check_default_type: check_default_type}.merge!(options))
end
# Receives a hash of options, parse them and add to the scope. This is a
@@ -693,6 +792,34 @@ class Bundler::Thor
def dispatch(command, given_args, given_opts, config) #:nodoc:
raise NotImplementedError
end
+
+ # Register a relation of options for target(method_option/class_option)
+ # by args and block.
+ def register_options_relation_for(target, relation, *args, &block) # :nodoc:
+ opt = args.pop if args.last.is_a? Hash
+ opt ||= {}
+ names = args.map{ |arg| arg.to_s }
+ names += built_option_names(target, opt, &block) if block_given?
+ command_scope_member(relation, opt) << names
+ end
+
+ # Get target(method_options or class_options) options
+ # of before and after by block evaluation.
+ def built_option_names(target, opt = {}, &block) # :nodoc:
+ before = command_scope_member(target, opt).map{ |k,v| v.name }
+ instance_eval(&block)
+ after = command_scope_member(target, opt).map{ |k,v| v.name }
+ after - before
+ end
+
+ # Get command scope member by name.
+ def command_scope_member(name, options = {}) # :nodoc:
+ if options[:for]
+ find_and_refresh_command(options[:for]).send(name)
+ else
+ send(name)
+ end
+ end
end
end
end
diff --git a/lib/bundler/vendor/thor/lib/thor/command.rb b/lib/bundler/vendor/thor/lib/thor/command.rb
index 040c971c0c..68c8fffedb 100644
--- a/lib/bundler/vendor/thor/lib/thor/command.rb
+++ b/lib/bundler/vendor/thor/lib/thor/command.rb
@@ -1,14 +1,15 @@
class Bundler::Thor
- class Command < Struct.new(:name, :description, :long_description, :usage, :options, :ancestor_name)
+ class Command < Struct.new(:name, :description, :long_description, :wrap_long_description, :usage, :options, :options_relation, :ancestor_name)
FILE_REGEXP = /^#{Regexp.escape(File.dirname(__FILE__))}/
- def initialize(name, description, long_description, usage, options = nil)
- super(name.to_s, description, long_description, usage, options || {})
+ def initialize(name, description, long_description, wrap_long_description, usage, options = nil, options_relation = nil)
+ super(name.to_s, description, long_description, wrap_long_description, usage, options || {}, options_relation || {})
end
def initialize_copy(other) #:nodoc:
super(other)
self.options = other.options.dup if other.options
+ self.options_relation = other.options_relation.dup if other.options_relation
end
def hidden?
@@ -62,6 +63,14 @@ class Bundler::Thor
end.join("\n")
end
+ def method_exclusive_option_names #:nodoc:
+ self.options_relation[:exclusive_option_names] || []
+ end
+
+ def method_at_least_one_option_names #:nodoc:
+ self.options_relation[:at_least_one_option_names] || []
+ end
+
protected
# Add usage with required arguments
@@ -127,7 +136,7 @@ class Bundler::Thor
# A dynamic command that handles method missing scenarios.
class DynamicCommand < Command
def initialize(name, options = nil)
- super(name.to_s, "A dynamically-generated command", name.to_s, name.to_s, options)
+ super(name.to_s, "A dynamically-generated command", name.to_s, nil, name.to_s, options)
end
def run(instance, args = [])
diff --git a/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb b/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb
index 3c4483e5dd..b16a98f782 100644
--- a/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb
+++ b/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb
@@ -38,6 +38,10 @@ class Bundler::Thor
super(convert_key(key), *args)
end
+ def slice(*keys)
+ super(*keys.map{ |key| convert_key(key) })
+ end
+
def key?(key)
super(convert_key(key))
end
diff --git a/lib/bundler/vendor/thor/lib/thor/error.rb b/lib/bundler/vendor/thor/lib/thor/error.rb
index dd64f750c9..928646e501 100644
--- a/lib/bundler/vendor/thor/lib/thor/error.rb
+++ b/lib/bundler/vendor/thor/lib/thor/error.rb
@@ -1,26 +1,15 @@
class Bundler::Thor
Correctable = if defined?(DidYouMean::SpellChecker) && defined?(DidYouMean::Correctable) # rubocop:disable Naming/ConstantName
- # In order to support versions of Ruby that don't have keyword
- # arguments, we need our own spell checker class that doesn't take key
- # words. Even though this code wouldn't be hit because of the check
- # above, it's still necessary because the interpreter would otherwise be
- # unable to parse the file.
- class NoKwargSpellChecker < DidYouMean::SpellChecker # :nodoc:
- def initialize(dictionary)
- @dictionary = dictionary
- end
- end
-
- Module.new do
- def to_s
- super + DidYouMean.formatter.message_for(corrections)
- end
-
- def corrections
- @corrections ||= self.class.const_get(:SpellChecker).new(self).corrections
- end
- end
- end
+ Module.new do
+ def to_s
+ super + DidYouMean.formatter.message_for(corrections)
+ end
+
+ def corrections
+ @corrections ||= self.class.const_get(:SpellChecker).new(self).corrections
+ end
+ end
+ end
# Bundler::Thor::Error is raised when it's caused by wrong usage of thor classes. Those
# errors have their backtrace suppressed and are nicely shown to the user.
@@ -45,7 +34,7 @@ class Bundler::Thor
end
def spell_checker
- NoKwargSpellChecker.new(error.all_commands)
+ DidYouMean::SpellChecker.new(dictionary: error.all_commands)
end
end
@@ -87,7 +76,7 @@ class Bundler::Thor
end
def spell_checker
- @spell_checker ||= NoKwargSpellChecker.new(error.switches)
+ @spell_checker ||= DidYouMean::SpellChecker.new(dictionary: error.switches)
end
end
@@ -108,4 +97,10 @@ class Bundler::Thor
class MalformattedArgumentError < InvocationError
end
+
+ class ExclusiveArgumentError < InvocationError
+ end
+
+ class AtLeastOneRequiredArgumentError < InvocationError
+ end
end
diff --git a/lib/bundler/vendor/thor/lib/thor/invocation.rb b/lib/bundler/vendor/thor/lib/thor/invocation.rb
index 248a466f8e..5ce74710ba 100644
--- a/lib/bundler/vendor/thor/lib/thor/invocation.rb
+++ b/lib/bundler/vendor/thor/lib/thor/invocation.rb
@@ -143,7 +143,7 @@ class Bundler::Thor
# Configuration values that are shared between invocations.
def _shared_configuration #:nodoc:
- {:invocations => @_invocations}
+ {invocations: @_invocations}
end
# This method simply retrieves the class and command to be invoked.
diff --git a/lib/bundler/vendor/thor/lib/thor/nested_context.rb b/lib/bundler/vendor/thor/lib/thor/nested_context.rb
index fd36b9d43f..7d60cb1c12 100644
--- a/lib/bundler/vendor/thor/lib/thor/nested_context.rb
+++ b/lib/bundler/vendor/thor/lib/thor/nested_context.rb
@@ -13,10 +13,10 @@ class Bundler::Thor
end
def entered?
- @depth > 0
+ @depth.positive?
end
- private
+ private
def push
@depth += 1
diff --git a/lib/bundler/vendor/thor/lib/thor/parser/argument.rb b/lib/bundler/vendor/thor/lib/thor/parser/argument.rb
index dfe7398583..b9e94e4669 100644
--- a/lib/bundler/vendor/thor/lib/thor/parser/argument.rb
+++ b/lib/bundler/vendor/thor/lib/thor/parser/argument.rb
@@ -24,6 +24,17 @@ class Bundler::Thor
validate! # Trigger specific validations
end
+ def print_default
+ if @type == :array and @default.is_a?(Array)
+ @default.map { |x|
+ p = x.gsub('"','\\"')
+ "\"#{p}\""
+ }.join(" ")
+ else
+ @default
+ end
+ end
+
def usage
required? ? banner : "[#{banner}]"
end
@@ -41,11 +52,19 @@ class Bundler::Thor
end
end
+ def enum_to_s
+ if enum.respond_to? :join
+ enum.join(", ")
+ else
+ "#{enum.first}..#{enum.last}"
+ end
+ end
+
protected
def validate!
raise ArgumentError, "An argument cannot be required and have default value." if required? && !default.nil?
- raise ArgumentError, "An argument cannot have an enum other than an array." if @enum && !@enum.is_a?(Array)
+ raise ArgumentError, "An argument cannot have an enum other than an enumerable." if @enum && !@enum.is_a?(Enumerable)
end
def valid_type?(type)
diff --git a/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb b/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb
index af395a0346..b6f9c9a37a 100644
--- a/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb
+++ b/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb
@@ -30,11 +30,7 @@ class Bundler::Thor
arguments.each do |argument|
if !argument.default.nil?
- begin
- @assigns[argument.human_name] = argument.default.dup
- rescue TypeError # Compatibility shim for un-dup-able Fixnum in Ruby < 2.4
- @assigns[argument.human_name] = argument.default
- end
+ @assigns[argument.human_name] = argument.default.dup
elsif argument.required?
@non_assigned_required << argument
end
@@ -121,8 +117,18 @@ class Bundler::Thor
#
def parse_array(name)
return shift if peek.is_a?(Array)
+
array = []
- array << shift while current_is_value?
+
+ while current_is_value?
+ value = shift
+
+ if !value.empty?
+ validate_enum_value!(name, value, "Expected all values of '%s' to be one of %s; got %s")
+ end
+
+ array << value
+ end
array
end
@@ -138,11 +144,9 @@ class Bundler::Thor
end
value = $&.index(".") ? shift.to_f : shift.to_i
- if @switches.is_a?(Hash) && switch = @switches[name]
- if switch.enum && !switch.enum.include?(value)
- raise MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}"
- end
- end
+
+ validate_enum_value!(name, value, "Expected '%s' to be one of %s; got %s")
+
value
end
@@ -156,15 +160,27 @@ class Bundler::Thor
nil
else
value = shift
- if @switches.is_a?(Hash) && switch = @switches[name]
- if switch.enum && !switch.enum.include?(value)
- raise MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}"
- end
- end
+
+ validate_enum_value!(name, value, "Expected '%s' to be one of %s; got %s")
+
value
end
end
+ # Raises an error if the switch is an enum and the values aren't included on it.
+ #
+ def validate_enum_value!(name, value, message)
+ return unless @switches.is_a?(Hash)
+
+ switch = @switches[name]
+
+ return unless switch
+
+ if switch.enum && !switch.enum.include?(value)
+ raise MalformattedArgumentError, message % [name, switch.enum_to_s, value]
+ end
+ end
+
# Raises an error if @non_assigned_required array is not empty.
#
def check_requirement!
diff --git a/lib/bundler/vendor/thor/lib/thor/parser/option.rb b/lib/bundler/vendor/thor/lib/thor/parser/option.rb
index 393955f107..c6af4e1e87 100644
--- a/lib/bundler/vendor/thor/lib/thor/parser/option.rb
+++ b/lib/bundler/vendor/thor/lib/thor/parser/option.rb
@@ -11,7 +11,7 @@ class Bundler::Thor
super
@lazy_default = options[:lazy_default]
@group = options[:group].to_s.capitalize if options[:group]
- @aliases = Array(options[:aliases])
+ @aliases = normalize_aliases(options[:aliases])
@hide = options[:hide]
end
@@ -69,7 +69,7 @@ class Bundler::Thor
value.class.name.downcase.to_sym
end
- new(name.to_s, :required => required, :type => type, :default => default, :aliases => aliases)
+ new(name.to_s, required: required, type: type, default: default, aliases: aliases)
end
def switch_name
@@ -90,7 +90,7 @@ class Bundler::Thor
sample = "[#{sample}]".dup unless required?
if boolean?
- sample << ", [#{dasherize('no-' + human_name)}]" unless (name == "force") || name.start_with?("no-")
+ sample << ", [#{dasherize('no-' + human_name)}]" unless (name == "force") || name.match(/\Ano[\-_]/)
end
aliases_for_usage.ljust(padding) + sample
@@ -104,6 +104,15 @@ class Bundler::Thor
end
end
+ def show_default?
+ case default
+ when TrueClass, FalseClass
+ true
+ else
+ super
+ end
+ end
+
VALID_TYPES.each do |type|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{type}?
@@ -142,8 +151,8 @@ class Bundler::Thor
raise ArgumentError, err
elsif @check_default_type == nil
Bundler::Thor.deprecation_warning "#{err}.\n" +
- 'This will be rejected in the future unless you explicitly pass the options `check_default_type: false`' +
- ' or call `allow_incompatible_default_type!` in your code'
+ "This will be rejected in the future unless you explicitly pass the options `check_default_type: false`" +
+ " or call `allow_incompatible_default_type!` in your code"
end
end
end
@@ -159,5 +168,11 @@ class Bundler::Thor
def dasherize(str)
(str.length > 1 ? "--" : "-") + str.tr("_", "-")
end
+
+ private
+
+ def normalize_aliases(aliases)
+ Array(aliases).map { |short| short.to_s.sub(/^(?!\-)/, "-") }
+ end
end
end
diff --git a/lib/bundler/vendor/thor/lib/thor/parser/options.rb b/lib/bundler/vendor/thor/lib/thor/parser/options.rb
index 499ce15339..978e76b132 100644
--- a/lib/bundler/vendor/thor/lib/thor/parser/options.rb
+++ b/lib/bundler/vendor/thor/lib/thor/parser/options.rb
@@ -29,8 +29,10 @@ class Bundler::Thor
#
# If +stop_on_unknown+ is true, #parse will stop as soon as it encounters
# an unknown option or a regular argument.
- def initialize(hash_options = {}, defaults = {}, stop_on_unknown = false, disable_required_check = false)
+ def initialize(hash_options = {}, defaults = {}, stop_on_unknown = false, disable_required_check = false, relations = {})
@stop_on_unknown = stop_on_unknown
+ @exclusives = (relations[:exclusive_option_names] || []).select{|array| !array.empty?}
+ @at_least_ones = (relations[:at_least_one_option_names] || []).select{|array| !array.empty?}
@disable_required_check = disable_required_check
options = hash_options.values
super(options)
@@ -50,8 +52,7 @@ class Bundler::Thor
options.each do |option|
@switches[option.switch_name] = option
- option.aliases.each do |short|
- name = short.to_s.sub(/^(?!\-)/, "-")
+ option.aliases.each do |name|
@shorts[name] ||= option.switch_name
end
end
@@ -101,7 +102,7 @@ class Bundler::Thor
unshift($1.split("").map { |f| "-#{f}" })
next
when EQ_RE
- unshift($2, :is_value => true)
+ unshift($2, is_value: true)
switch = $1
when SHORT_NUM
unshift($2)
@@ -132,12 +133,38 @@ class Bundler::Thor
end
check_requirement! unless @disable_required_check
+ check_exclusive!
+ check_at_least_one!
assigns = Bundler::Thor::CoreExt::HashWithIndifferentAccess.new(@assigns)
assigns.freeze
assigns
end
+ def check_exclusive!
+ opts = @assigns.keys
+ # When option A and B are exclusive, if A and B are given at the same time,
+ # the diffrence of argument array size will decrease.
+ found = @exclusives.find{ |ex| (ex - opts).size < ex.size - 1 }
+ if found
+ names = names_to_switch_names(found & opts).map{|n| "'#{n}'"}
+ class_name = self.class.name.split("::").last.downcase
+ fail ExclusiveArgumentError, "Found exclusive #{class_name} #{names.join(", ")}"
+ end
+ end
+
+ def check_at_least_one!
+ opts = @assigns.keys
+ # When at least one is required of the options A and B,
+ # if the both options were not given, none? would be true.
+ found = @at_least_ones.find{ |one_reqs| one_reqs.none?{ |o| opts.include? o} }
+ if found
+ names = names_to_switch_names(found).map{|n| "'#{n}'"}
+ class_name = self.class.name.split("::").last.downcase
+ fail AtLeastOneRequiredArgumentError, "Not found at least one of required #{class_name} #{names.join(", ")}"
+ end
+ end
+
def check_unknown!
to_check = @stopped_parsing_after_extra_index ? @extra[0...@stopped_parsing_after_extra_index] : @extra
@@ -148,6 +175,17 @@ class Bundler::Thor
protected
+ # Option names changes to swith name or human name
+ def names_to_switch_names(names = [])
+ @switches.map do |_, o|
+ if names.include? o.name
+ o.respond_to?(:switch_name) ? o.switch_name : o.human_name
+ else
+ nil
+ end
+ end.compact
+ end
+
def assign_result!(option, result)
if option.repeatable && option.type == :hash
(@assigns[option.human_name] ||= {}).merge!(result)
diff --git a/lib/bundler/vendor/thor/lib/thor/runner.rb b/lib/bundler/vendor/thor/lib/thor/runner.rb
index 8157c6c5b2..c7cc873131 100644
--- a/lib/bundler/vendor/thor/lib/thor/runner.rb
+++ b/lib/bundler/vendor/thor/lib/thor/runner.rb
@@ -23,7 +23,7 @@ class Bundler::Thor::Runner < Bundler::Thor #:nodoc:
initialize_thorfiles(meth)
klass, command = Bundler::Thor::Util.find_class_and_command_by_namespace(meth)
self.class.handle_no_command_error(command, false) if klass.nil?
- klass.start(["-h", command].compact, :shell => shell)
+ klass.start(["-h", command].compact, shell: shell)
else
super
end
@@ -38,11 +38,11 @@ class Bundler::Thor::Runner < Bundler::Thor #:nodoc:
klass, command = Bundler::Thor::Util.find_class_and_command_by_namespace(meth)
self.class.handle_no_command_error(command, false) if klass.nil?
args.unshift(command) if command
- klass.start(args, :shell => shell)
+ klass.start(args, shell: shell)
end
desc "install NAME", "Install an optionally named Bundler::Thor file into your system commands"
- method_options :as => :string, :relative => :boolean, :force => :boolean
+ method_options as: :string, relative: :boolean, force: :boolean
def install(name) # rubocop:disable Metrics/MethodLength
initialize_thorfiles
@@ -53,7 +53,7 @@ class Bundler::Thor::Runner < Bundler::Thor #:nodoc:
package = :file
require "open-uri"
begin
- contents = URI.send(:open, name, &:read) # Using `send` for Ruby 2.4- support
+ contents = URI.open(name, &:read)
rescue OpenURI::HTTPError
raise Error, "Error opening URI '#{name}'"
end
@@ -69,7 +69,7 @@ class Bundler::Thor::Runner < Bundler::Thor #:nodoc:
base = name
package = :file
require "open-uri"
- contents = URI.send(:open, name, &:read) # for ruby 2.1-2.4
+ contents = URI.open(name, &:read)
end
rescue Errno::ENOENT
raise Error, "Error opening file '#{name}'"
@@ -101,9 +101,9 @@ class Bundler::Thor::Runner < Bundler::Thor #:nodoc:
end
thor_yaml[as] = {
- :filename => Digest::SHA256.hexdigest(name + as),
- :location => location,
- :namespaces => Bundler::Thor::Util.namespaces_in_content(contents, base)
+ filename: Digest::SHA256.hexdigest(name + as),
+ location: location,
+ namespaces: Bundler::Thor::Util.namespaces_in_content(contents, base)
}
save_yaml(thor_yaml)
@@ -164,14 +164,14 @@ class Bundler::Thor::Runner < Bundler::Thor #:nodoc:
end
desc "installed", "List the installed Bundler::Thor modules and commands"
- method_options :internal => :boolean
+ method_options internal: :boolean
def installed
initialize_thorfiles(nil, true)
display_klasses(true, options["internal"])
end
desc "list [SEARCH]", "List the available thor commands (--substring means .*SEARCH)"
- method_options :substring => :boolean, :group => :string, :all => :boolean, :debug => :boolean
+ method_options substring: :boolean, group: :string, all: :boolean, debug: :boolean
def list(search = "")
initialize_thorfiles
@@ -313,7 +313,7 @@ private
say shell.set_color(namespace, :blue, true)
say "-" * namespace.size
- print_table(list, :truncate => true)
+ print_table(list, truncate: true)
say
end
alias_method :display_tasks, :display_commands
diff --git a/lib/bundler/vendor/thor/lib/thor/shell.rb b/lib/bundler/vendor/thor/lib/thor/shell.rb
index a4137d1bde..265f3ba046 100644
--- a/lib/bundler/vendor/thor/lib/thor/shell.rb
+++ b/lib/bundler/vendor/thor/lib/thor/shell.rb
@@ -75,7 +75,7 @@ class Bundler::Thor
# Allow shell to be shared between invocations.
#
def _shared_configuration #:nodoc:
- super.merge!(:shell => shell)
+ super.merge!(shell: shell)
end
end
end
diff --git a/lib/bundler/vendor/thor/lib/thor/shell/basic.rb b/lib/bundler/vendor/thor/lib/thor/shell/basic.rb
index 7f414e9a08..dc3179e5f3 100644
--- a/lib/bundler/vendor/thor/lib/thor/shell/basic.rb
+++ b/lib/bundler/vendor/thor/lib/thor/shell/basic.rb
@@ -1,8 +1,10 @@
+require_relative "column_printer"
+require_relative "table_printer"
+require_relative "wrapped_printer"
+
class Bundler::Thor
module Shell
class Basic
- DEFAULT_TERMINAL_WIDTH = 80
-
attr_accessor :base
attr_reader :padding
@@ -145,14 +147,14 @@ class Bundler::Thor
# "yes".
#
def yes?(statement, color = nil)
- !!(ask(statement, color, :add_to_history => false) =~ is?(:yes))
+ !!(ask(statement, color, add_to_history: false) =~ is?(:yes))
end
# Make a question the to user and returns true if the user replies "n" or
# "no".
#
def no?(statement, color = nil)
- !!(ask(statement, color, :add_to_history => false) =~ is?(:no))
+ !!(ask(statement, color, add_to_history: false) =~ is?(:no))
end
# Prints values in columns
@@ -161,16 +163,8 @@ class Bundler::Thor
# Array[String, String, ...]
#
def print_in_columns(array)
- return if array.empty?
- colwidth = (array.map { |el| el.to_s.size }.max || 0) + 2
- array.each_with_index do |value, index|
- # Don't output trailing spaces when printing the last column
- if ((((index + 1) % (terminal_width / colwidth))).zero? && !index.zero?) || index + 1 == array.length
- stdout.puts value
- else
- stdout.printf("%-#{colwidth}s", value)
- end
- end
+ printer = ColumnPrinter.new(stdout)
+ printer.print(array)
end
# Prints a table.
@@ -181,58 +175,11 @@ class Bundler::Thor
# ==== Options
# indent<Integer>:: Indent the first column by indent value.
# colwidth<Integer>:: Force the first column to colwidth spaces wide.
+ # borders<Boolean>:: Adds ascii borders.
#
def print_table(array, options = {}) # rubocop:disable Metrics/MethodLength
- return if array.empty?
-
- formats = []
- indent = options[:indent].to_i
- colwidth = options[:colwidth]
- options[:truncate] = terminal_width if options[:truncate] == true
-
- formats << "%-#{colwidth + 2}s".dup if colwidth
- start = colwidth ? 1 : 0
-
- colcount = array.max { |a, b| a.size <=> b.size }.size
-
- maximas = []
-
- start.upto(colcount - 1) do |index|
- maxima = array.map { |row| row[index] ? row[index].to_s.size : 0 }.max
- maximas << maxima
- formats << if index == colcount - 1
- # Don't output 2 trailing spaces when printing the last column
- "%-s".dup
- else
- "%-#{maxima + 2}s".dup
- end
- end
-
- formats[0] = formats[0].insert(0, " " * indent)
- formats << "%s"
-
- array.each do |row|
- sentence = "".dup
-
- row.each_with_index do |column, index|
- maxima = maximas[index]
-
- f = if column.is_a?(Numeric)
- if index == row.size - 1
- # Don't output 2 trailing spaces when printing the last column
- "%#{maxima}s"
- else
- "%#{maxima}s "
- end
- else
- formats[index]
- end
- sentence << f % column.to_s
- end
-
- sentence = truncate(sentence, options[:truncate]) if options[:truncate]
- stdout.puts sentence
- end
+ printer = TablePrinter.new(stdout, options)
+ printer.print(array)
end
# Prints a long string, word-wrapping the text to the current width of the
@@ -245,33 +192,8 @@ class Bundler::Thor
# indent<Integer>:: Indent each line of the printed paragraph by indent value.
#
def print_wrapped(message, options = {})
- indent = options[:indent] || 0
- width = terminal_width - indent
- paras = message.split("\n\n")
-
- paras.map! do |unwrapped|
- words = unwrapped.split(" ")
- counter = words.first.length
- words.inject do |memo, word|
- word = word.gsub(/\n\005/, "\n").gsub(/\005/, "\n")
- counter = 0 if word.include? "\n"
- if (counter + word.length + 1) < width
- memo = "#{memo} #{word}"
- counter += (word.length + 1)
- else
- memo = "#{memo}\n#{word}"
- counter = word.length
- end
- memo
- end
- end.compact!
-
- paras.each do |para|
- para.split("\n").each do |line|
- stdout.puts line.insert(0, " " * indent)
- end
- stdout.puts unless para == paras.last
- end
+ printer = WrappedPrinter.new(stdout, options)
+ printer.print(message)
end
# Deals with file collision and returns true if the file should be
@@ -289,7 +211,7 @@ class Bundler::Thor
loop do
answer = ask(
%[Overwrite #{destination}? (enter "h" for help) #{options}],
- :add_to_history => false
+ add_to_history: false
)
case answer
@@ -316,24 +238,11 @@ class Bundler::Thor
say "Please specify merge tool to `THOR_MERGE` env."
else
- say file_collision_help
+ say file_collision_help(block_given?)
end
end
end
- # This code was copied from Rake, available under MIT-LICENSE
- # Copyright (c) 2003, 2004 Jim Weirich
- def terminal_width
- result = if ENV["THOR_COLUMNS"]
- ENV["THOR_COLUMNS"].to_i
- else
- unix? ? dynamic_width : DEFAULT_TERMINAL_WIDTH
- end
- result < 10 ? DEFAULT_TERMINAL_WIDTH : result
- rescue
- DEFAULT_TERMINAL_WIDTH
- end
-
# Called if something goes wrong during the execution. This is used by Bundler::Thor
# internally and should not be used inside your scripts. If something went
# wrong, you can always raise an exception. If you raise a Bundler::Thor::Error, it
@@ -384,16 +293,21 @@ class Bundler::Thor
end
end
- def file_collision_help #:nodoc:
- <<-HELP
+ def file_collision_help(block_given) #:nodoc:
+ help = <<-HELP
Y - yes, overwrite
n - no, do not overwrite
a - all, overwrite this and all others
q - quit, abort
- d - diff, show the differences between the old and the new
h - help, show this help
- m - merge, run merge tool
HELP
+ if block_given
+ help << <<-HELP
+ d - diff, show the differences between the old and the new
+ m - merge, run merge tool
+ HELP
+ end
+ help
end
def show_diff(destination, content) #:nodoc:
@@ -411,46 +325,8 @@ class Bundler::Thor
mute? || (base && base.options[:quiet])
end
- # Calculate the dynamic width of the terminal
- def dynamic_width
- @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
- end
-
- def dynamic_width_stty
- `stty size 2>/dev/null`.split[1].to_i
- end
-
- def dynamic_width_tput
- `tput cols 2>/dev/null`.to_i
- end
-
def unix?
- RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris)/i
- end
-
- def truncate(string, width)
- as_unicode do
- chars = string.chars.to_a
- if chars.length <= width
- chars.join
- else
- chars[0, width - 3].join + "..."
- end
- end
- end
-
- if "".respond_to?(:encode)
- def as_unicode
- yield
- end
- else
- def as_unicode
- old = $KCODE
- $KCODE = "U"
- yield
- ensure
- $KCODE = old
- end
+ Terminal.unix?
end
def ask_simply(statement, color, options)
diff --git a/lib/bundler/vendor/thor/lib/thor/shell/color.rb b/lib/bundler/vendor/thor/lib/thor/shell/color.rb
index ccb7c3c079..5d708fadca 100644
--- a/lib/bundler/vendor/thor/lib/thor/shell/color.rb
+++ b/lib/bundler/vendor/thor/lib/thor/shell/color.rb
@@ -105,52 +105,7 @@ class Bundler::Thor
end
def are_colors_disabled?
- !ENV['NO_COLOR'].nil? && !ENV['NO_COLOR'].empty?
- end
-
- # Overwrite show_diff to show diff with colors if Diff::LCS is
- # available.
- #
- def show_diff(destination, content) #:nodoc:
- if diff_lcs_loaded? && ENV["THOR_DIFF"].nil? && ENV["RAILS_DIFF"].nil?
- actual = File.binread(destination).to_s.split("\n")
- content = content.to_s.split("\n")
-
- Diff::LCS.sdiff(actual, content).each do |diff|
- output_diff_line(diff)
- end
- else
- super
- end
- end
-
- def output_diff_line(diff) #:nodoc:
- case diff.action
- when "-"
- say "- #{diff.old_element.chomp}", :red, true
- when "+"
- say "+ #{diff.new_element.chomp}", :green, true
- when "!"
- say "- #{diff.old_element.chomp}", :red, true
- say "+ #{diff.new_element.chomp}", :green, true
- else
- say " #{diff.old_element.chomp}", nil, true
- end
- end
-
- # Check if Diff::LCS is loaded. If it is, use it to create pretty output
- # for diff.
- #
- def diff_lcs_loaded? #:nodoc:
- return true if defined?(Diff::LCS)
- return @diff_lcs_loaded unless @diff_lcs_loaded.nil?
-
- @diff_lcs_loaded = begin
- require "diff/lcs"
- true
- rescue LoadError
- false
- end
+ !ENV["NO_COLOR"].nil? && !ENV["NO_COLOR"].empty?
end
end
end
diff --git a/lib/bundler/vendor/thor/lib/thor/shell/column_printer.rb b/lib/bundler/vendor/thor/lib/thor/shell/column_printer.rb
new file mode 100644
index 0000000000..56a9e6181b
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/shell/column_printer.rb
@@ -0,0 +1,29 @@
+require_relative "terminal"
+
+class Bundler::Thor
+ module Shell
+ class ColumnPrinter
+ attr_reader :stdout, :options
+
+ def initialize(stdout, options = {})
+ @stdout = stdout
+ @options = options
+ @indent = options[:indent].to_i
+ end
+
+ def print(array)
+ return if array.empty?
+ colwidth = (array.map { |el| el.to_s.size }.max || 0) + 2
+ array.each_with_index do |value, index|
+ # Don't output trailing spaces when printing the last column
+ if ((((index + 1) % (Terminal.terminal_width / colwidth))).zero? && !index.zero?) || index + 1 == array.length
+ stdout.puts value
+ else
+ stdout.printf("%-#{colwidth}s", value)
+ end
+ end
+ end
+ end
+ end
+end
+
diff --git a/lib/bundler/vendor/thor/lib/thor/shell/html.rb b/lib/bundler/vendor/thor/lib/thor/shell/html.rb
index 77a6d13a23..0277b882b7 100644
--- a/lib/bundler/vendor/thor/lib/thor/shell/html.rb
+++ b/lib/bundler/vendor/thor/lib/thor/shell/html.rb
@@ -76,51 +76,6 @@ class Bundler::Thor
def can_display_colors?
true
end
-
- # Overwrite show_diff to show diff with colors if Diff::LCS is
- # available.
- #
- def show_diff(destination, content) #:nodoc:
- if diff_lcs_loaded? && ENV["THOR_DIFF"].nil? && ENV["RAILS_DIFF"].nil?
- actual = File.binread(destination).to_s.split("\n")
- content = content.to_s.split("\n")
-
- Diff::LCS.sdiff(actual, content).each do |diff|
- output_diff_line(diff)
- end
- else
- super
- end
- end
-
- def output_diff_line(diff) #:nodoc:
- case diff.action
- when "-"
- say "- #{diff.old_element.chomp}", :red, true
- when "+"
- say "+ #{diff.new_element.chomp}", :green, true
- when "!"
- say "- #{diff.old_element.chomp}", :red, true
- say "+ #{diff.new_element.chomp}", :green, true
- else
- say " #{diff.old_element.chomp}", nil, true
- end
- end
-
- # Check if Diff::LCS is loaded. If it is, use it to create pretty output
- # for diff.
- #
- def diff_lcs_loaded? #:nodoc:
- return true if defined?(Diff::LCS)
- return @diff_lcs_loaded unless @diff_lcs_loaded.nil?
-
- @diff_lcs_loaded = begin
- require "diff/lcs"
- true
- rescue LoadError
- false
- end
- end
end
end
end
diff --git a/lib/bundler/vendor/thor/lib/thor/shell/table_printer.rb b/lib/bundler/vendor/thor/lib/thor/shell/table_printer.rb
new file mode 100644
index 0000000000..525f9ce5bb
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/shell/table_printer.rb
@@ -0,0 +1,134 @@
+require_relative "column_printer"
+require_relative "terminal"
+
+class Bundler::Thor
+ module Shell
+ class TablePrinter < ColumnPrinter
+ BORDER_SEPARATOR = :separator
+
+ def initialize(stdout, options = {})
+ super
+ @formats = []
+ @maximas = []
+ @colwidth = options[:colwidth]
+ @truncate = options[:truncate] == true ? Terminal.terminal_width : options[:truncate]
+ @padding = 1
+ end
+
+ def print(array)
+ return if array.empty?
+
+ prepare(array)
+
+ print_border_separator if options[:borders]
+
+ array.each do |row|
+ if options[:borders] && row == BORDER_SEPARATOR
+ print_border_separator
+ next
+ end
+
+ sentence = "".dup
+
+ row.each_with_index do |column, index|
+ sentence << format_cell(column, row.size, index)
+ end
+
+ sentence = truncate(sentence)
+ sentence << "|" if options[:borders]
+ stdout.puts indentation + sentence
+
+ end
+ print_border_separator if options[:borders]
+ end
+
+ private
+
+ def prepare(array)
+ array = array.reject{|row| row == BORDER_SEPARATOR }
+
+ @formats << "%-#{@colwidth + 2}s".dup if @colwidth
+ start = @colwidth ? 1 : 0
+
+ colcount = array.max { |a, b| a.size <=> b.size }.size
+
+ start.upto(colcount - 1) do |index|
+ maxima = array.map { |row| row[index] ? row[index].to_s.size : 0 }.max
+
+ @maximas << maxima
+ @formats << if options[:borders]
+ "%-#{maxima}s".dup
+ elsif index == colcount - 1
+ # Don't output 2 trailing spaces when printing the last column
+ "%-s".dup
+ else
+ "%-#{maxima + 2}s".dup
+ end
+ end
+
+ @formats << "%s"
+ end
+
+ def format_cell(column, row_size, index)
+ maxima = @maximas[index]
+
+ f = if column.is_a?(Numeric)
+ if options[:borders]
+ # With borders we handle padding separately
+ "%#{maxima}s"
+ elsif index == row_size - 1
+ # Don't output 2 trailing spaces when printing the last column
+ "%#{maxima}s"
+ else
+ "%#{maxima}s "
+ end
+ else
+ @formats[index]
+ end
+
+ cell = "".dup
+ cell << "|" + " " * @padding if options[:borders]
+ cell << f % column.to_s
+ cell << " " * @padding if options[:borders]
+ cell
+ end
+
+ def print_border_separator
+ separator = @maximas.map do |maxima|
+ "+" + "-" * (maxima + 2 * @padding)
+ end
+ stdout.puts indentation + separator.join + "+"
+ end
+
+ def truncate(string)
+ return string unless @truncate
+ as_unicode do
+ chars = string.chars.to_a
+ if chars.length <= @truncate
+ chars.join
+ else
+ chars[0, @truncate - 3 - @indent].join + "..."
+ end
+ end
+ end
+
+ def indentation
+ " " * @indent
+ end
+
+ if "".respond_to?(:encode)
+ def as_unicode
+ yield
+ end
+ else
+ def as_unicode
+ old = $KCODE # rubocop:disable Style/GlobalVars
+ $KCODE = "U" # rubocop:disable Style/GlobalVars
+ yield
+ ensure
+ $KCODE = old # rubocop:disable Style/GlobalVars
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/shell/terminal.rb b/lib/bundler/vendor/thor/lib/thor/shell/terminal.rb
new file mode 100644
index 0000000000..2c60684308
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/shell/terminal.rb
@@ -0,0 +1,42 @@
+class Bundler::Thor
+ module Shell
+ module Terminal
+ DEFAULT_TERMINAL_WIDTH = 80
+
+ class << self
+ # This code was copied from Rake, available under MIT-LICENSE
+ # Copyright (c) 2003, 2004 Jim Weirich
+ def terminal_width
+ result = if ENV["THOR_COLUMNS"]
+ ENV["THOR_COLUMNS"].to_i
+ else
+ unix? ? dynamic_width : DEFAULT_TERMINAL_WIDTH
+ end
+ result < 10 ? DEFAULT_TERMINAL_WIDTH : result
+ rescue
+ DEFAULT_TERMINAL_WIDTH
+ end
+
+ def unix?
+ RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris)/i
+ end
+
+ private
+
+ # Calculate the dynamic width of the terminal
+ def dynamic_width
+ @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
+ end
+
+ def dynamic_width_stty
+ `stty size 2>/dev/null`.split[1].to_i
+ end
+
+ def dynamic_width_tput
+ `tput cols 2>/dev/null`.to_i
+ end
+
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/shell/wrapped_printer.rb b/lib/bundler/vendor/thor/lib/thor/shell/wrapped_printer.rb
new file mode 100644
index 0000000000..ba88e952db
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/shell/wrapped_printer.rb
@@ -0,0 +1,38 @@
+require_relative "column_printer"
+require_relative "terminal"
+
+class Bundler::Thor
+ module Shell
+ class WrappedPrinter < ColumnPrinter
+ def print(message)
+ width = Terminal.terminal_width - @indent
+ paras = message.split("\n\n")
+
+ paras.map! do |unwrapped|
+ words = unwrapped.split(" ")
+ counter = words.first.length
+ words.inject do |memo, word|
+ word = word.gsub(/\n\005/, "\n").gsub(/\005/, "\n")
+ counter = 0 if word.include? "\n"
+ if (counter + word.length + 1) < width
+ memo = "#{memo} #{word}"
+ counter += (word.length + 1)
+ else
+ memo = "#{memo}\n#{word}"
+ counter = word.length
+ end
+ memo
+ end
+ end.compact!
+
+ paras.each do |para|
+ para.split("\n").each do |line|
+ stdout.puts line.insert(0, " " * @indent)
+ end
+ stdout.puts unless para == paras.last
+ end
+ end
+ end
+ end
+end
+
diff --git a/lib/bundler/vendor/thor/lib/thor/util.rb b/lib/bundler/vendor/thor/lib/thor/util.rb
index a17eabc22c..68916daf2e 100644
--- a/lib/bundler/vendor/thor/lib/thor/util.rb
+++ b/lib/bundler/vendor/thor/lib/thor/util.rb
@@ -130,9 +130,10 @@ class Bundler::Thor
#
def find_class_and_command_by_namespace(namespace, fallback = true)
if namespace.include?(":") # look for a namespaced command
- pieces = namespace.split(":")
- command = pieces.pop
- klass = Bundler::Thor::Util.find_by_namespace(pieces.join(":"))
+ *pieces, command = namespace.split(":")
+ namespace = pieces.join(":")
+ namespace = "default" if namespace.empty?
+ klass = Bundler::Thor::Base.subclasses.detect { |thor| thor.namespace == namespace && thor.commands.keys.include?(command) }
end
unless klass # look for a Bundler::Thor::Group with the right name
klass = Bundler::Thor::Util.find_by_namespace(namespace)
diff --git a/lib/bundler/vendor/thor/lib/thor/version.rb b/lib/bundler/vendor/thor/lib/thor/version.rb
index 43e462c531..1fb00017ed 100644
--- a/lib/bundler/vendor/thor/lib/thor/version.rb
+++ b/lib/bundler/vendor/thor/lib/thor/version.rb
@@ -1,3 +1,3 @@
class Bundler::Thor
- VERSION = "1.2.2"
+ VERSION = "1.3.0"
end
diff --git a/lib/bundler/vendor/tsort/.document b/lib/bundler/vendor/tsort/.document
new file mode 100644
index 0000000000..0c43bbd6b3
--- /dev/null
+++ b/lib/bundler/vendor/tsort/.document
@@ -0,0 +1 @@
+# Vendored files do not need to be documented
diff --git a/lib/bundler/vendor/tsort/lib/tsort.rb b/lib/bundler/vendor/tsort/lib/tsort.rb
index 4a0e1a4e25..cf8731f760 100644
--- a/lib/bundler/vendor/tsort/lib/tsort.rb
+++ b/lib/bundler/vendor/tsort/lib/tsort.rb
@@ -122,6 +122,9 @@
#
module Bundler::TSort
+
+ VERSION = "0.2.0"
+
class Cyclic < StandardError
end
diff --git a/lib/bundler/vendor/uri/.document b/lib/bundler/vendor/uri/.document
new file mode 100644
index 0000000000..0c43bbd6b3
--- /dev/null
+++ b/lib/bundler/vendor/uri/.document
@@ -0,0 +1 @@
+# Vendored files do not need to be documented
diff --git a/lib/bundler/vendor/uri/lib/uri/common.rb b/lib/bundler/vendor/uri/lib/uri/common.rb
index 914a4c7581..93f4f226ad 100644
--- a/lib/bundler/vendor/uri/lib/uri/common.rb
+++ b/lib/bundler/vendor/uri/lib/uri/common.rb
@@ -68,16 +68,32 @@ module Bundler::URI
end
private_constant :Schemes
+ # Registers the given +klass+ as the class to be instantiated
+ # when parsing a \Bundler::URI with the given +scheme+:
#
- # Register the given +klass+ to be instantiated when parsing URLs with the given +scheme+.
- # Note that currently only schemes which after .upcase are valid constant names
- # can be registered (no -/+/. allowed).
+ # Bundler::URI.register_scheme('MS_SEARCH', Bundler::URI::Generic) # => Bundler::URI::Generic
+ # Bundler::URI.scheme_list['MS_SEARCH'] # => Bundler::URI::Generic
#
+ # Note that after calling String#upcase on +scheme+, it must be a valid
+ # constant name.
def self.register_scheme(scheme, klass)
Schemes.const_set(scheme.to_s.upcase, klass)
end
- # Returns a Hash of the defined schemes.
+ # Returns a hash of the defined schemes:
+ #
+ # Bundler::URI.scheme_list
+ # # =>
+ # {"MAILTO"=>Bundler::URI::MailTo,
+ # "LDAPS"=>Bundler::URI::LDAPS,
+ # "WS"=>Bundler::URI::WS,
+ # "HTTP"=>Bundler::URI::HTTP,
+ # "HTTPS"=>Bundler::URI::HTTPS,
+ # "LDAP"=>Bundler::URI::LDAP,
+ # "FILE"=>Bundler::URI::File,
+ # "FTP"=>Bundler::URI::FTP}
+ #
+ # Related: Bundler::URI.register_scheme.
def self.scheme_list
Schemes.constants.map { |name|
[name.to_s.upcase, Schemes.const_get(name)]
@@ -88,9 +104,21 @@ module Bundler::URI
private_constant :INITIAL_SCHEMES
Ractor.make_shareable(INITIAL_SCHEMES) if defined?(Ractor)
+ # Returns a new object constructed from the given +scheme+, +arguments+,
+ # and +default+:
+ #
+ # - The new object is an instance of <tt>Bundler::URI.scheme_list[scheme.upcase]</tt>.
+ # - The object is initialized by calling the class initializer
+ # using +scheme+ and +arguments+.
+ # See Bundler::URI::Generic.new.
+ #
+ # Examples:
#
- # Construct a Bundler::URI instance, using the scheme to detect the appropriate class
- # from +Bundler::URI.scheme_list+.
+ # values = ['john.doe', 'www.example.com', '123', nil, '/forum/questions/', nil, 'tag=networking&order=newest', 'top']
+ # Bundler::URI.for('https', *values)
+ # # => #<Bundler::URI::HTTPS https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top>
+ # Bundler::URI.for('foo', *values, default: Bundler::URI::HTTP)
+ # # => #<Bundler::URI::HTTP foo://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top>
#
def self.for(scheme, *arguments, default: Generic)
const_name = scheme.to_s.upcase
@@ -121,95 +149,49 @@ module Bundler::URI
#
class BadURIError < Error; end
- #
- # == Synopsis
- #
- # Bundler::URI::split(uri)
- #
- # == Args
- #
- # +uri+::
- # String with Bundler::URI.
- #
- # == Description
- #
- # Splits the string on following parts and returns array with result:
- #
- # * Scheme
- # * Userinfo
- # * Host
- # * Port
- # * Registry
- # * Path
- # * Opaque
- # * Query
- # * Fragment
- #
- # == Usage
- #
- # require 'bundler/vendor/uri/lib/uri'
- #
- # Bundler::URI.split("http://www.ruby-lang.org/")
- # # => ["http", nil, "www.ruby-lang.org", nil, nil, "/", nil, nil, nil]
+ # Returns a 9-element array representing the parts of the \Bundler::URI
+ # formed from the string +uri+;
+ # each array element is a string or +nil+:
+ #
+ # names = %w[scheme userinfo host port registry path opaque query fragment]
+ # values = Bundler::URI.split('https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top')
+ # names.zip(values)
+ # # =>
+ # [["scheme", "https"],
+ # ["userinfo", "john.doe"],
+ # ["host", "www.example.com"],
+ # ["port", "123"],
+ # ["registry", nil],
+ # ["path", "/forum/questions/"],
+ # ["opaque", nil],
+ # ["query", "tag=networking&order=newest"],
+ # ["fragment", "top"]]
#
def self.split(uri)
RFC3986_PARSER.split(uri)
end
+ # Returns a new \Bundler::URI object constructed from the given string +uri+:
#
- # == Synopsis
- #
- # Bundler::URI::parse(uri_str)
- #
- # == Args
- #
- # +uri_str+::
- # String with Bundler::URI.
- #
- # == Description
- #
- # Creates one of the Bundler::URI's subclasses instance from the string.
- #
- # == Raises
- #
- # Bundler::URI::InvalidURIError::
- # Raised if Bundler::URI given is not a correct one.
+ # Bundler::URI.parse('https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top')
+ # # => #<Bundler::URI::HTTPS https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top>
+ # Bundler::URI.parse('http://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top')
+ # # => #<Bundler::URI::HTTP http://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top>
#
- # == Usage
- #
- # require 'bundler/vendor/uri/lib/uri'
- #
- # uri = Bundler::URI.parse("http://www.ruby-lang.org/")
- # # => #<Bundler::URI::HTTP http://www.ruby-lang.org/>
- # uri.scheme
- # # => "http"
- # uri.host
- # # => "www.ruby-lang.org"
- #
- # It's recommended to first ::escape the provided +uri_str+ if there are any
- # invalid Bundler::URI characters.
+ # It's recommended to first ::escape string +uri+
+ # if it may contain invalid Bundler::URI characters.
#
def self.parse(uri)
RFC3986_PARSER.parse(uri)
end
+ # Merges the given Bundler::URI strings +str+
+ # per {RFC 2396}[https://www.rfc-editor.org/rfc/rfc2396.html].
#
- # == Synopsis
- #
- # Bundler::URI::join(str[, str, ...])
+ # Each string in +str+ is converted to an
+ # {RFC3986 Bundler::URI}[https://www.rfc-editor.org/rfc/rfc3986.html] before being merged.
#
- # == Args
- #
- # +str+::
- # String(s) to work with, will be converted to RFC3986 URIs before merging.
- #
- # == Description
- #
- # Joins URIs.
- #
- # == Usage
- #
- # require 'bundler/vendor/uri/lib/uri'
+ # Examples:
#
# Bundler::URI.join("http://example.com/","main.rbx")
# # => #<Bundler::URI::HTTP http://example.com/main.rbx>
@@ -254,7 +236,7 @@ module Bundler::URI
# Bundler::URI.extract("text here http://foo.example.org/bla and here mailto:test@example.com and here also.")
# # => ["http://foo.example.com/bla", "mailto:test@example.com"]
#
- def self.extract(str, schemes = nil, &block)
+ def self.extract(str, schemes = nil, &block) # :nodoc:
warn "Bundler::URI.extract is obsolete", uplevel: 1 if $VERBOSE
DEFAULT_PARSER.extract(str, schemes, &block)
end
@@ -291,7 +273,7 @@ module Bundler::URI
# p $&
# end
#
- def self.regexp(schemes = nil)
+ def self.regexp(schemes = nil)# :nodoc:
warn "Bundler::URI.regexp is obsolete", uplevel: 1 if $VERBOSE
DEFAULT_PARSER.make_regexp(schemes)
end
@@ -314,40 +296,86 @@ module Bundler::URI
TBLDECWWWCOMP_['+'] = ' '
TBLDECWWWCOMP_.freeze
- # Encodes given +str+ to URL-encoded form data.
+ # Returns a URL-encoded string derived from the given string +str+.
+ #
+ # The returned string:
+ #
+ # - Preserves:
+ #
+ # - Characters <tt>'*'</tt>, <tt>'.'</tt>, <tt>'-'</tt>, and <tt>'_'</tt>.
+ # - Character in ranges <tt>'a'..'z'</tt>, <tt>'A'..'Z'</tt>,
+ # and <tt>'0'..'9'</tt>.
+ #
+ # Example:
#
- # This method doesn't convert *, -, ., 0-9, A-Z, _, a-z, but does convert SP
- # (ASCII space) to + and converts others to %XX.
+ # Bundler::URI.encode_www_form_component('*.-_azAZ09')
+ # # => "*.-_azAZ09"
#
- # If +enc+ is given, convert +str+ to the encoding before percent encoding.
+ # - Converts:
#
- # This is an implementation of
- # https://www.w3.org/TR/2013/CR-html5-20130806/forms.html#url-encoded-form-data.
+ # - Character <tt>' '</tt> to character <tt>'+'</tt>.
+ # - Any other character to "percent notation";
+ # the percent notation for character <i>c</i> is <tt>'%%%X' % c.ord</tt>.
#
- # See Bundler::URI.decode_www_form_component, Bundler::URI.encode_www_form.
+ # Example:
+ #
+ # Bundler::URI.encode_www_form_component('Here are some punctuation characters: ,;?:')
+ # # => "Here+are+some+punctuation+characters%3A+%2C%3B%3F%3A"
+ #
+ # Encoding:
+ #
+ # - If +str+ has encoding Encoding::ASCII_8BIT, argument +enc+ is ignored.
+ # - Otherwise +str+ is converted first to Encoding::UTF_8
+ # (with suitable character replacements),
+ # and then to encoding +enc+.
+ #
+ # In either case, the returned string has forced encoding Encoding::US_ASCII.
+ #
+ # Related: Bundler::URI.encode_uri_component (encodes <tt>' '</tt> as <tt>'%20'</tt>).
def self.encode_www_form_component(str, enc=nil)
_encode_uri_component(/[^*\-.0-9A-Z_a-z]/, TBLENCWWWCOMP_, str, enc)
end
- # Decodes given +str+ of URL-encoded form data.
+ # Returns a string decoded from the given \URL-encoded string +str+.
+ #
+ # The given string is first encoded as Encoding::ASCII-8BIT (using String#b),
+ # then decoded (as below), and finally force-encoded to the given encoding +enc+.
+ #
+ # The returned string:
+ #
+ # - Preserves:
+ #
+ # - Characters <tt>'*'</tt>, <tt>'.'</tt>, <tt>'-'</tt>, and <tt>'_'</tt>.
+ # - Character in ranges <tt>'a'..'z'</tt>, <tt>'A'..'Z'</tt>,
+ # and <tt>'0'..'9'</tt>.
+ #
+ # Example:
+ #
+ # Bundler::URI.decode_www_form_component('*.-_azAZ09')
+ # # => "*.-_azAZ09"
+ #
+ # - Converts:
+ #
+ # - Character <tt>'+'</tt> to character <tt>' '</tt>.
+ # - Each "percent notation" to an ASCII character.
#
- # This decodes + to SP.
+ # Example:
#
- # See Bundler::URI.encode_www_form_component, Bundler::URI.decode_www_form.
+ # Bundler::URI.decode_www_form_component('Here+are+some+punctuation+characters%3A+%2C%3B%3F%3A')
+ # # => "Here are some punctuation characters: ,;?:"
+ #
+ # Related: Bundler::URI.decode_uri_component (preserves <tt>'+'</tt>).
def self.decode_www_form_component(str, enc=Encoding::UTF_8)
_decode_uri_component(/\+|%\h\h/, str, enc)
end
- # Encodes +str+ using URL encoding
- #
- # This encodes SP to %20 instead of +.
+ # Like Bundler::URI.encode_www_form_component, except that <tt>' '</tt> (space)
+ # is encoded as <tt>'%20'</tt> (instead of <tt>'+'</tt>).
def self.encode_uri_component(str, enc=nil)
_encode_uri_component(/[^*\-.0-9A-Z_a-z]/, TBLENCURICOMP_, str, enc)
end
- # Decodes given +str+ of URL-encoded data.
- #
- # This does not decode + to SP.
+ # Like Bundler::URI.decode_www_form_component, except that <tt>'+'</tt> is preserved.
def self.decode_uri_component(str, enc=Encoding::UTF_8)
_decode_uri_component(/%\h\h/, str, enc)
end
@@ -372,33 +400,104 @@ module Bundler::URI
end
private_class_method :_decode_uri_component
- # Generates URL-encoded form data from given +enum+.
+ # Returns a URL-encoded string derived from the given
+ # {Enumerable}[rdoc-ref:Enumerable@Enumerable+in+Ruby+Classes]
+ # +enum+.
+ #
+ # The result is suitable for use as form data
+ # for an \HTTP request whose <tt>Content-Type</tt> is
+ # <tt>'application/x-www-form-urlencoded'</tt>.
+ #
+ # The returned string consists of the elements of +enum+,
+ # each converted to one or more URL-encoded strings,
+ # and all joined with character <tt>'&'</tt>.
+ #
+ # Simple examples:
+ #
+ # Bundler::URI.encode_www_form([['foo', 0], ['bar', 1], ['baz', 2]])
+ # # => "foo=0&bar=1&baz=2"
+ # Bundler::URI.encode_www_form({foo: 0, bar: 1, baz: 2})
+ # # => "foo=0&bar=1&baz=2"
+ #
+ # The returned string is formed using method Bundler::URI.encode_www_form_component,
+ # which converts certain characters:
+ #
+ # Bundler::URI.encode_www_form('f#o': '/', 'b-r': '$', 'b z': '@')
+ # # => "f%23o=%2F&b-r=%24&b+z=%40"
+ #
+ # When +enum+ is Array-like, each element +ele+ is converted to a field:
+ #
+ # - If +ele+ is an array of two or more elements,
+ # the field is formed from its first two elements
+ # (and any additional elements are ignored):
+ #
+ # name = Bundler::URI.encode_www_form_component(ele[0], enc)
+ # value = Bundler::URI.encode_www_form_component(ele[1], enc)
+ # "#{name}=#{value}"
#
- # This generates application/x-www-form-urlencoded data defined in HTML5
- # from given an Enumerable object.
+ # Examples:
#
- # This internally uses Bundler::URI.encode_www_form_component(str).
+ # Bundler::URI.encode_www_form([%w[foo bar], %w[baz bat bah]])
+ # # => "foo=bar&baz=bat"
+ # Bundler::URI.encode_www_form([['foo', 0], ['bar', :baz, 'bat']])
+ # # => "foo=0&bar=baz"
#
- # This method doesn't convert the encoding of given items, so convert them
- # before calling this method if you want to send data as other than original
- # encoding or mixed encoding data. (Strings which are encoded in an HTML5
- # ASCII incompatible encoding are converted to UTF-8.)
+ # - If +ele+ is an array of one element,
+ # the field is formed from <tt>ele[0]</tt>:
#
- # This method doesn't handle files. When you send a file, use
- # multipart/form-data.
+ # Bundler::URI.encode_www_form_component(ele[0])
#
- # This refers https://url.spec.whatwg.org/#concept-urlencoded-serializer
+ # Example:
#
- # Bundler::URI.encode_www_form([["q", "ruby"], ["lang", "en"]])
- # #=> "q=ruby&lang=en"
- # Bundler::URI.encode_www_form("q" => "ruby", "lang" => "en")
- # #=> "q=ruby&lang=en"
- # Bundler::URI.encode_www_form("q" => ["ruby", "perl"], "lang" => "en")
- # #=> "q=ruby&q=perl&lang=en"
- # Bundler::URI.encode_www_form([["q", "ruby"], ["q", "perl"], ["lang", "en"]])
- # #=> "q=ruby&q=perl&lang=en"
+ # Bundler::URI.encode_www_form([['foo'], [:bar], [0]])
+ # # => "foo&bar&0"
+ #
+ # - Otherwise the field is formed from +ele+:
+ #
+ # Bundler::URI.encode_www_form_component(ele)
+ #
+ # Example:
+ #
+ # Bundler::URI.encode_www_form(['foo', :bar, 0])
+ # # => "foo&bar&0"
+ #
+ # The elements of an Array-like +enum+ may be mixture:
+ #
+ # Bundler::URI.encode_www_form([['foo', 0], ['bar', 1, 2], ['baz'], :bat])
+ # # => "foo=0&bar=1&baz&bat"
+ #
+ # When +enum+ is Hash-like,
+ # each +key+/+value+ pair is converted to one or more fields:
+ #
+ # - If +value+ is
+ # {Array-convertible}[rdoc-ref:implicit_conversion.rdoc@Array-Convertible+Objects],
+ # each element +ele+ in +value+ is paired with +key+ to form a field:
+ #
+ # name = Bundler::URI.encode_www_form_component(key, enc)
+ # value = Bundler::URI.encode_www_form_component(ele, enc)
+ # "#{name}=#{value}"
+ #
+ # Example:
+ #
+ # Bundler::URI.encode_www_form({foo: [:bar, 1], baz: [:bat, :bam, 2]})
+ # # => "foo=bar&foo=1&baz=bat&baz=bam&baz=2"
+ #
+ # - Otherwise, +key+ and +value+ are paired to form a field:
+ #
+ # name = Bundler::URI.encode_www_form_component(key, enc)
+ # value = Bundler::URI.encode_www_form_component(value, enc)
+ # "#{name}=#{value}"
+ #
+ # Example:
+ #
+ # Bundler::URI.encode_www_form({foo: 0, bar: 1, baz: 2})
+ # # => "foo=0&bar=1&baz=2"
+ #
+ # The elements of a Hash-like +enum+ may be mixture:
+ #
+ # Bundler::URI.encode_www_form({foo: [0, 1], bar: 2})
+ # # => "foo=0&foo=1&bar=2"
#
- # See Bundler::URI.encode_www_form_component, Bundler::URI.decode_www_form.
def self.encode_www_form(enum, enc=nil)
enum.map do |k,v|
if v.nil?
@@ -419,22 +518,39 @@ module Bundler::URI
end.join('&')
end
- # Decodes URL-encoded form data from given +str+.
+ # Returns name/value pairs derived from the given string +str+,
+ # which must be an ASCII string.
+ #
+ # The method may be used to decode the body of Net::HTTPResponse object +res+
+ # for which <tt>res['Content-Type']</tt> is <tt>'application/x-www-form-urlencoded'</tt>.
+ #
+ # The returned data is an array of 2-element subarrays;
+ # each subarray is a name/value pair (both are strings).
+ # Each returned string has encoding +enc+,
+ # and has had invalid characters removed via
+ # {String#scrub}[rdoc-ref:String#scrub].
#
- # This decodes application/x-www-form-urlencoded data
- # and returns an array of key-value arrays.
+ # A simple example:
#
- # This refers http://url.spec.whatwg.org/#concept-urlencoded-parser,
- # so this supports only &-separator, and doesn't support ;-separator.
+ # Bundler::URI.decode_www_form('foo=0&bar=1&baz')
+ # # => [["foo", "0"], ["bar", "1"], ["baz", ""]]
#
- # ary = Bundler::URI.decode_www_form("a=1&a=2&b=3")
- # ary #=> [['a', '1'], ['a', '2'], ['b', '3']]
- # ary.assoc('a').last #=> '1'
- # ary.assoc('b').last #=> '3'
- # ary.rassoc('a').last #=> '2'
- # Hash[ary] #=> {"a"=>"2", "b"=>"3"}
+ # The returned strings have certain conversions,
+ # similar to those performed in Bundler::URI.decode_www_form_component:
+ #
+ # Bundler::URI.decode_www_form('f%23o=%2F&b-r=%24&b+z=%40')
+ # # => [["f#o", "/"], ["b-r", "$"], ["b z", "@"]]
+ #
+ # The given string may contain consecutive separators:
+ #
+ # Bundler::URI.decode_www_form('foo=0&&bar=1&&baz=2')
+ # # => [["foo", "0"], ["", ""], ["bar", "1"], ["", ""], ["baz", "2"]]
+ #
+ # A different separator may be specified:
+ #
+ # Bundler::URI.decode_www_form('foo=0--bar=1--baz', separator: '--')
+ # # => [["foo", "0"], ["bar", "1"], ["baz", ""]]
#
- # See Bundler::URI.decode_www_form_component, Bundler::URI.encode_www_form.
def self.decode_www_form(str, enc=Encoding::UTF_8, separator: '&', use__charset_: false, isindex: false)
raise ArgumentError, "the input of #{self.name}.#{__method__} must be ASCII only string" unless str.ascii_only?
ary = []
@@ -713,7 +829,15 @@ end # module Bundler::URI
module Bundler
#
- # Returns +uri+ converted to an Bundler::URI object.
+ # Returns a \Bundler::URI object derived from the given +uri+,
+ # which may be a \Bundler::URI string or an existing \Bundler::URI object:
+ #
+ # # Returns a new Bundler::URI.
+ # uri = Bundler::URI('http://github.com/ruby/ruby')
+ # # => #<Bundler::URI::HTTP http://github.com/ruby/ruby>
+ # # Returns the given Bundler::URI.
+ # Bundler::URI(uri)
+ # # => #<Bundler::URI::HTTP http://github.com/ruby/ruby>
#
def URI(uri)
if uri.is_a?(Bundler::URI::Generic)
diff --git a/lib/bundler/vendor/uri/lib/uri/generic.rb b/lib/bundler/vendor/uri/lib/uri/generic.rb
index 9ae6915826..762c425ac1 100644
--- a/lib/bundler/vendor/uri/lib/uri/generic.rb
+++ b/lib/bundler/vendor/uri/lib/uri/generic.rb
@@ -1376,6 +1376,7 @@ module Bundler::URI
end
str
end
+ alias to_str to_s
#
# Compares two URIs.
diff --git a/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb b/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb
index 2f8d55353b..09c22c9906 100644
--- a/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb
+++ b/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb
@@ -497,8 +497,8 @@ module Bundler::URI
ret = {}
# for Bundler::URI::split
- ret[:ABS_URI] = Regexp.new('\A\s*' + pattern[:X_ABS_URI] + '\s*\z', Regexp::EXTENDED)
- ret[:REL_URI] = Regexp.new('\A\s*' + pattern[:X_REL_URI] + '\s*\z', Regexp::EXTENDED)
+ ret[:ABS_URI] = Regexp.new('\A\s*+' + pattern[:X_ABS_URI] + '\s*\z', Regexp::EXTENDED)
+ ret[:REL_URI] = Regexp.new('\A\s*+' + pattern[:X_REL_URI] + '\s*\z', Regexp::EXTENDED)
# for Bundler::URI::extract
ret[:URI_REF] = Regexp.new(pattern[:URI_REF])
diff --git a/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb b/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb
index d527072db4..4c9882f595 100644
--- a/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb
+++ b/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb
@@ -1,9 +1,73 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Bundler::URI
class RFC3986_Parser # :nodoc:
# Bundler::URI defined in RFC3986
- RFC3986_URI = /\A(?<Bundler::URI>(?<scheme>[A-Za-z][+\-.0-9A-Za-z]*+):(?<hier-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*+))(?::(?<port>\d*+))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g<segment>)*+)?)|(?<path-rootless>\g<segment-nz>(?:\/\g<segment>)*+)|(?<path-empty>))(?:\?(?<query>[^#]*+))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/
- RFC3986_relative_ref = /\A(?<relative-ref>(?<relative-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])++))?(?::(?<port>\d*+))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g<segment>)*+)?)|(?<path-noscheme>(?<segment-nz-nc>(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])++)(?:\/\g<segment>)*+)|(?<path-empty>))(?:\?(?<query>[^#]*+))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/
+ HOST = %r[
+ (?<IP-literal>\[(?:
+ (?<IPv6address>
+ (?:\h{1,4}:){6}
+ (?<ls32>\h{1,4}:\h{1,4}
+ | (?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)
+ \.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>)
+ )
+ | ::(?:\h{1,4}:){5}\g<ls32>
+ | \h{1,4}?::(?:\h{1,4}:){4}\g<ls32>
+ | (?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>
+ | (?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>
+ | (?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>
+ | (?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>
+ | (?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}
+ | (?:(?:\h{1,4}:){,6}\h{1,4})?::
+ )
+ | (?<IPvFuture>v\h++\.[!$&-.0-9:;=A-Z_a-z~]++)
+ )\])
+ | \g<IPv4address>
+ | (?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*+)
+ ]x
+
+ USERINFO = /(?:%\h\h|[!$&-.0-9:;=A-Z_a-z~])*+/
+
+ SCHEME = %r[[A-Za-z][+\-.0-9A-Za-z]*+].source
+ SEG = %r[(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/])].source
+ SEG_NC = %r[(?:%\h\h|[!$&-.0-9;=@A-Z_a-z~])].source
+ FRAGMENT = %r[(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/?])*+].source
+
+ RFC3986_URI = %r[\A
+ (?<seg>#{SEG}){0}
+ (?<Bundler::URI>
+ (?<scheme>#{SCHEME}):
+ (?<hier-part>//
+ (?<authority>
+ (?:(?<userinfo>#{USERINFO.source})@)?
+ (?<host>#{HOST.source.delete(" \n")})
+ (?::(?<port>\d*+))?
+ )
+ (?<path-abempty>(?:/\g<seg>*+)?)
+ | (?<path-absolute>/((?!/)\g<seg>++)?)
+ | (?<path-rootless>(?!/)\g<seg>++)
+ | (?<path-empty>)
+ )
+ (?:\?(?<query>[^\#]*+))?
+ (?:\#(?<fragment>#{FRAGMENT}))?
+ )\z]x
+
+ RFC3986_relative_ref = %r[\A
+ (?<seg>#{SEG}){0}
+ (?<relative-ref>
+ (?<relative-part>//
+ (?<authority>
+ (?:(?<userinfo>#{USERINFO.source})@)?
+ (?<host>#{HOST.source.delete(" \n")}(?<!/))?
+ (?::(?<port>\d*+))?
+ )
+ (?<path-abempty>(?:/\g<seg>*+)?)
+ | (?<path-absolute>/\g<seg>*+)
+ | (?<path-noscheme>#{SEG_NC}++(?:/\g<seg>*+)?)
+ | (?<path-empty>)
+ )
+ (?:\?(?<query>[^#]*+))?
+ (?:\#(?<fragment>#{FRAGMENT}))?
+ )\z]x
attr_reader :regexp
def initialize
@@ -19,9 +83,9 @@ module Bundler::URI
uri.ascii_only? or
raise InvalidURIError, "Bundler::URI must be ascii only #{uri.dump}"
if m = RFC3986_URI.match(uri)
- query = m["query".freeze]
- scheme = m["scheme".freeze]
- opaque = m["path-rootless".freeze]
+ query = m["query"]
+ scheme = m["scheme"]
+ opaque = m["path-rootless"]
if opaque
opaque << "?#{query}" if query
[ scheme,
@@ -32,35 +96,35 @@ module Bundler::URI
nil, # path
opaque,
nil, # query
- m["fragment".freeze]
+ m["fragment"]
]
else # normal
[ scheme,
- m["userinfo".freeze],
- m["host".freeze],
- m["port".freeze],
+ m["userinfo"],
+ m["host"],
+ m["port"],
nil, # registry
- (m["path-abempty".freeze] ||
- m["path-absolute".freeze] ||
- m["path-empty".freeze]),
+ (m["path-abempty"] ||
+ m["path-absolute"] ||
+ m["path-empty"]),
nil, # opaque
query,
- m["fragment".freeze]
+ m["fragment"]
]
end
elsif m = RFC3986_relative_ref.match(uri)
[ nil, # scheme
- m["userinfo".freeze],
- m["host".freeze],
- m["port".freeze],
+ m["userinfo"],
+ m["host"],
+ m["port"],
nil, # registry,
- (m["path-abempty".freeze] ||
- m["path-absolute".freeze] ||
- m["path-noscheme".freeze] ||
- m["path-empty".freeze]),
+ (m["path-abempty"] ||
+ m["path-absolute"] ||
+ m["path-noscheme"] ||
+ m["path-empty"]),
nil, # opaque
- m["query".freeze],
- m["fragment".freeze]
+ m["query"],
+ m["fragment"]
]
else
raise InvalidURIError, "bad Bundler::URI(is not Bundler::URI?): #{uri.inspect}"
@@ -92,15 +156,15 @@ module Bundler::URI
def default_regexp # :nodoc:
{
- SCHEME: /\A[A-Za-z][A-Za-z0-9+\-.]*\z/,
- USERINFO: /\A(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*\z/,
- HOST: /\A(?:(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{,4}::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*))\z/,
- ABS_PATH: /\A\/(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*(?:\/(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*)*\z/,
- REL_PATH: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+(?:\/(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*)*\z/,
- QUERY: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/,
- FRAGMENT: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/,
- OPAQUE: /\A(?:[^\/].*)?\z/,
- PORT: /\A[\x09\x0a\x0c\x0d ]*\d*[\x09\x0a\x0c\x0d ]*\z/,
+ SCHEME: %r[\A#{SCHEME}\z]o,
+ USERINFO: %r[\A#{USERINFO}\z]o,
+ HOST: %r[\A#{HOST}\z]o,
+ ABS_PATH: %r[\A/#{SEG}*+\z]o,
+ REL_PATH: %r[\A(?!/)#{SEG}++\z]o,
+ QUERY: %r[\A(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/?])*+\z],
+ FRAGMENT: %r[\A#{FRAGMENT}\z]o,
+ OPAQUE: %r[\A(?:[^/].*)?\z],
+ PORT: /\A[\x09\x0a\x0c\x0d ]*+\d*[\x09\x0a\x0c\x0d ]*\z/,
}
end
diff --git a/lib/bundler/vendor/uri/lib/uri/version.rb b/lib/bundler/vendor/uri/lib/uri/version.rb
index 500fdb3f99..1fa1c7c09a 100644
--- a/lib/bundler/vendor/uri/lib/uri/version.rb
+++ b/lib/bundler/vendor/uri/lib/uri/version.rb
@@ -1,6 +1,6 @@
module Bundler::URI
# :stopdoc:
- VERSION_CODE = '001201'.freeze
+ VERSION_CODE = '001300'.freeze
VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.').freeze
# :startdoc:
end
diff --git a/lib/bundler/vendored_net_http.rb b/lib/bundler/vendored_net_http.rb
new file mode 100644
index 0000000000..0dcabaa7d7
--- /dev/null
+++ b/lib/bundler/vendored_net_http.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+begin
+ require "rubygems/vendored_net_http"
+rescue LoadError
+ begin
+ require "rubygems/net/http"
+ rescue LoadError
+ require "net/http"
+ Gem::Net = Net
+ end
+end
diff --git a/lib/bundler/vendored_persistent.rb b/lib/bundler/vendored_persistent.rb
index e29f27cdfd..ab985c267f 100644
--- a/lib/bundler/vendored_persistent.rb
+++ b/lib/bundler/vendored_persistent.rb
@@ -9,7 +9,3 @@ module Bundler
end
end
require_relative "vendor/net-http-persistent/lib/net/http/persistent"
-
-module Bundler
- PersistentHTTP = Persistent::Net::HTTP::Persistent
-end
diff --git a/lib/bundler/vendored_timeout.rb b/lib/bundler/vendored_timeout.rb
new file mode 100644
index 0000000000..9b2507c0cc
--- /dev/null
+++ b/lib/bundler/vendored_timeout.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+begin
+ require "rubygems/vendored_timeout"
+rescue LoadError
+ begin
+ require "rubygems/timeout"
+ rescue LoadError
+ require "timeout"
+ Gem::Timeout = Timeout
+ end
+end
diff --git a/lib/bundler/vendored_uri.rb b/lib/bundler/vendored_uri.rb
index 905e8158e8..2efddc65f9 100644
--- a/lib/bundler/vendored_uri.rb
+++ b/lib/bundler/vendored_uri.rb
@@ -1,4 +1,21 @@
# frozen_string_literal: true
module Bundler; end
-require_relative "vendor/uri/lib/uri"
+
+# Use RubyGems vendored copy when available. Otherwise fallback to Bundler
+# vendored copy. The vendored copy in Bundler can be removed once support for
+# RubyGems 3.5 is dropped.
+
+begin
+ require "rubygems/vendor/uri/lib/uri"
+rescue LoadError
+ require_relative "vendor/uri/lib/uri"
+ Gem::URI = Bundler::URI
+
+ module Gem
+ def URI(uri) # rubocop:disable Naming/MethodName
+ Bundler::URI(uri)
+ end
+ module_function :URI
+ end
+end
diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb
index 7c03d8687f..f2f6236cda 100644
--- a/lib/bundler/version.rb
+++ b/lib/bundler/version.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
module Bundler
- VERSION = "2.5.0.dev".freeze
+ VERSION = "2.6.0.dev".freeze
def self.bundler_major_version
@bundler_major_version ||= VERSION.split(".").first.to_i
diff --git a/lib/bundler/vlad.rb b/lib/bundler/vlad.rb
index 538e8c3e74..6179d0e4eb 100644
--- a/lib/bundler/vlad.rb
+++ b/lib/bundler/vlad.rb
@@ -13,5 +13,5 @@ require_relative "deployment"
include Rake::DSL if defined? Rake::DSL
namespace :vlad do
- Bundler::Deployment.define_task(Rake::RemoteTask, :remote_task, :roles => :app)
+ Bundler::Deployment.define_task(Rake::RemoteTask, :remote_task, roles: :app)
end
diff --git a/lib/bundler/yaml_serializer.rb b/lib/bundler/yaml_serializer.rb
index d5ecbd4aef..42e6aaf89d 100644
--- a/lib/bundler/yaml_serializer.rb
+++ b/lib/bundler/yaml_serializer.rb
@@ -17,7 +17,11 @@ module Bundler
if v.is_a?(Hash)
yaml << dump_hash(v).gsub(/^(?!$)/, " ") # indent all non-empty lines
elsif v.is_a?(Array) # Expected to be array of strings
- yaml << "\n- " << v.map {|s| s.to_s.gsub(/\s+/, " ").inspect }.join("\n- ") << "\n"
+ if v.empty?
+ yaml << " []\n"
+ else
+ yaml << "\n- " << v.map {|s| s.to_s.gsub(/\s+/, " ").inspect }.join("\n- ") << "\n"
+ end
else
yaml << " " << v.to_s.gsub(/\s+/, " ").inspect << "\n"
end
@@ -32,7 +36,7 @@ module Bundler
(.*) # value
\1 # matching closing quote
$
- /xo.freeze
+ /xo
HASH_REGEX = /
^
@@ -44,18 +48,20 @@ module Bundler
(.*) # value
\3 # matching closing quote
$
- /xo.freeze
+ /xo
def load(str)
res = {}
stack = [res]
last_hash = nil
last_empty_key = nil
- str.split(/\r?\n/).each do |line|
+ str.split(/\r?\n/) do |line|
if match = HASH_REGEX.match(line)
indent, key, quote, val = match.captures
- key = convert_to_backward_compatible_key(key)
- depth = indent.scan(/ /).length
+ val = strip_comment(val)
+
+ convert_to_backward_compatible_key!(key)
+ depth = indent.size / 2
if quote.empty? && val.empty?
new_hash = {}
stack[depth][key] = new_hash
@@ -63,10 +69,13 @@ module Bundler
last_empty_key = key
last_hash = stack[depth]
else
+ val = [] if val == "[]" # empty array
stack[depth][key] = val
end
elsif match = ARRAY_REGEX.match(line)
_, val = match.captures
+ val = strip_comment(val)
+
last_hash[last_empty_key] = [] unless last_hash[last_empty_key].is_a?(Array)
last_hash[last_empty_key].push(val)
@@ -75,15 +84,22 @@ module Bundler
res
end
+ def strip_comment(val)
+ if val.include?("#") && !val.start_with?("#")
+ val.split("#", 2).first.strip
+ else
+ val
+ end
+ end
+
# for settings' keys
- def convert_to_backward_compatible_key(key)
- key = "#{key}/" if key =~ /https?:/i && key !~ %r{/\Z}
- key = key.gsub(".", "__") if key.include?(".")
- key
+ def convert_to_backward_compatible_key!(key)
+ key << "/" if /https?:/i.match?(key) && !%r{/\Z}.match?(key)
+ key.gsub!(".", "__")
end
class << self
- private :dump_hash, :convert_to_backward_compatible_key
+ private :dump_hash, :convert_to_backward_compatible_key!
end
end
end
diff --git a/lib/cgi.rb b/lib/cgi.rb
index 4cd6b3bd8e..7af85e7fc8 100644
--- a/lib/cgi.rb
+++ b/lib/cgi.rb
@@ -288,7 +288,7 @@
#
class CGI
- VERSION = "0.3.6"
+ VERSION = "0.4.1"
end
require 'cgi/core'
diff --git a/lib/cgi/util.rb b/lib/cgi/util.rb
index 5f335898dc..4986e544e0 100644
--- a/lib/cgi/util.rb
+++ b/lib/cgi/util.rb
@@ -36,7 +36,7 @@ module CGI::Util
# URL-encode a string following RFC 3986
# Space characters (+" "+) are encoded with (+"%20"+)
- # url_encoded_string = CGI.escape("'Stop!' said Fred")
+ # url_encoded_string = CGI.escapeURIComponent("'Stop!' said Fred")
# # => "%27Stop%21%27%20said%20Fred"
def escapeURIComponent(string)
encoding = string.encoding
@@ -46,9 +46,10 @@ module CGI::Util
end
buffer.force_encoding(encoding)
end
+ alias escape_uri_component escapeURIComponent
# URL-decode a string following RFC 3986 with encoding(optional).
- # string = CGI.unescape("%27Stop%21%27+said%20Fred")
+ # string = CGI.unescapeURIComponent("%27Stop%21%27+said%20Fred")
# # => "'Stop!'+said Fred"
def unescapeURIComponent(string, encoding = @@accept_charset)
str = string.b
@@ -59,6 +60,8 @@ module CGI::Util
str.valid_encoding? ? str : str.force_encoding(string.encoding)
end
+ alias unescape_uri_component unescapeURIComponent
+
# The set of special characters and their escaped values
TABLE_FOR_ESCAPE_HTML__ = {
"'" => '&#39;',
diff --git a/lib/csv.rb b/lib/csv.rb
deleted file mode 100644
index d4dc569b83..0000000000
--- a/lib/csv.rb
+++ /dev/null
@@ -1,2881 +0,0 @@
-# encoding: US-ASCII
-# frozen_string_literal: true
-# = csv.rb -- CSV Reading and Writing
-#
-# Created by James Edward Gray II on 2005-10-31.
-#
-# See CSV for documentation.
-#
-# == Description
-#
-# Welcome to the new and improved CSV.
-#
-# This version of the CSV library began its life as FasterCSV. FasterCSV was
-# intended as a replacement to Ruby's then standard CSV library. It was
-# designed to address concerns users of that library had and it had three
-# primary goals:
-#
-# 1. Be significantly faster than CSV while remaining a pure Ruby library.
-# 2. Use a smaller and easier to maintain code base. (FasterCSV eventually
-# grew larger, was also but considerably richer in features. The parsing
-# core remains quite small.)
-# 3. Improve on the CSV interface.
-#
-# Obviously, the last one is subjective. I did try to defer to the original
-# interface whenever I didn't have a compelling reason to change it though, so
-# hopefully this won't be too radically different.
-#
-# We must have met our goals because FasterCSV was renamed to CSV and replaced
-# the original library as of Ruby 1.9. If you are migrating code from 1.8 or
-# earlier, you may have to change your code to comply with the new interface.
-#
-# == What's the Different From the Old CSV?
-#
-# I'm sure I'll miss something, but I'll try to mention most of the major
-# differences I am aware of, to help others quickly get up to speed:
-#
-# === \CSV Parsing
-#
-# * This parser is m17n aware. See CSV for full details.
-# * This library has a stricter parser and will throw MalformedCSVErrors on
-# problematic data.
-# * This library has a less liberal idea of a line ending than CSV. What you
-# set as the <tt>:row_sep</tt> is law. It can auto-detect your line endings
-# though.
-# * The old library returned empty lines as <tt>[nil]</tt>. This library calls
-# them <tt>[]</tt>.
-# * This library has a much faster parser.
-#
-# === Interface
-#
-# * CSV now uses keyword parameters to set options.
-# * CSV no longer has generate_row() or parse_row().
-# * The old CSV's Reader and Writer classes have been dropped.
-# * CSV::open() is now more like Ruby's open().
-# * CSV objects now support most standard IO methods.
-# * CSV now has a new() method used to wrap objects like String and IO for
-# reading and writing.
-# * CSV::generate() is different from the old method.
-# * CSV no longer supports partial reads. It works line-by-line.
-# * CSV no longer allows the instance methods to override the separators for
-# performance reasons. They must be set in the constructor.
-#
-# If you use this library and find yourself missing any functionality I have
-# trimmed, please {let me know}[mailto:james@grayproductions.net].
-#
-# == Documentation
-#
-# See CSV for documentation.
-#
-# == What is CSV, really?
-#
-# CSV maintains a pretty strict definition of CSV taken directly from
-# {the RFC}[https://www.ietf.org/rfc/rfc4180.txt]. I relax the rules in only one
-# place and that is to make using this library easier. CSV will parse all valid
-# CSV.
-#
-# What you don't want to do is to feed CSV invalid data. Because of the way the
-# CSV format works, it's common for a parser to need to read until the end of
-# the file to be sure a field is invalid. This consumes a lot of time and memory.
-#
-# Luckily, when working with invalid CSV, Ruby's built-in methods will almost
-# always be superior in every way. For example, parsing non-quoted fields is as
-# easy as:
-#
-# data.split(",")
-#
-# == Questions and/or Comments
-#
-# Feel free to email {James Edward Gray II}[mailto:james@grayproductions.net]
-# with any questions.
-
-require "forwardable"
-require "date"
-require "stringio"
-
-require_relative "csv/fields_converter"
-require_relative "csv/input_record_separator"
-require_relative "csv/parser"
-require_relative "csv/row"
-require_relative "csv/table"
-require_relative "csv/writer"
-
-# == \CSV
-#
-# === In a Hurry?
-#
-# If you are familiar with \CSV data and have a particular task in mind,
-# you may want to go directly to the:
-# - {Recipes for CSV}[doc/csv/recipes/recipes_rdoc.html].
-#
-# Otherwise, read on here, about the API: classes, methods, and constants.
-#
-# === \CSV Data
-#
-# \CSV (comma-separated values) data is a text representation of a table:
-# - A _row_ _separator_ delimits table rows.
-# A common row separator is the newline character <tt>"\n"</tt>.
-# - A _column_ _separator_ delimits fields in a row.
-# A common column separator is the comma character <tt>","</tt>.
-#
-# This \CSV \String, with row separator <tt>"\n"</tt>
-# and column separator <tt>","</tt>,
-# has three rows and two columns:
-# "foo,0\nbar,1\nbaz,2\n"
-#
-# Despite the name \CSV, a \CSV representation can use different separators.
-#
-# For more about tables, see the Wikipedia article
-# "{Table (information)}[https://en.wikipedia.org/wiki/Table_(information)]",
-# especially its section
-# "{Simple table}[https://en.wikipedia.org/wiki/Table_(information)#Simple_table]"
-#
-# == \Class \CSV
-#
-# Class \CSV provides methods for:
-# - Parsing \CSV data from a \String object, a \File (via its file path), or an \IO object.
-# - Generating \CSV data to a \String object.
-#
-# To make \CSV available:
-# require 'csv'
-#
-# All examples here assume that this has been done.
-#
-# == Keeping It Simple
-#
-# A \CSV object has dozens of instance methods that offer fine-grained control
-# of parsing and generating \CSV data.
-# For many needs, though, simpler approaches will do.
-#
-# This section summarizes the singleton methods in \CSV
-# that allow you to parse and generate without explicitly
-# creating \CSV objects.
-# For details, follow the links.
-#
-# === Simple Parsing
-#
-# Parsing methods commonly return either of:
-# - An \Array of Arrays of Strings:
-# - The outer \Array is the entire "table".
-# - Each inner \Array is a row.
-# - Each \String is a field.
-# - A CSV::Table object. For details, see
-# {\CSV with Headers}[#class-CSV-label-CSV+with+Headers].
-#
-# ==== Parsing a \String
-#
-# The input to be parsed can be a string:
-# string = "foo,0\nbar,1\nbaz,2\n"
-#
-# \Method CSV.parse returns the entire \CSV data:
-# CSV.parse(string) # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
-#
-# \Method CSV.parse_line returns only the first row:
-# CSV.parse_line(string) # => ["foo", "0"]
-#
-# \CSV extends class \String with instance method String#parse_csv,
-# which also returns only the first row:
-# string.parse_csv # => ["foo", "0"]
-#
-# ==== Parsing Via a \File Path
-#
-# The input to be parsed can be in a file:
-# string = "foo,0\nbar,1\nbaz,2\n"
-# path = 't.csv'
-# File.write(path, string)
-#
-# \Method CSV.read returns the entire \CSV data:
-# CSV.read(path) # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
-#
-# \Method CSV.foreach iterates, passing each row to the given block:
-# CSV.foreach(path) do |row|
-# p row
-# end
-# Output:
-# ["foo", "0"]
-# ["bar", "1"]
-# ["baz", "2"]
-#
-# \Method CSV.table returns the entire \CSV data as a CSV::Table object:
-# CSV.table(path) # => #<CSV::Table mode:col_or_row row_count:3>
-#
-# ==== Parsing from an Open \IO Stream
-#
-# The input to be parsed can be in an open \IO stream:
-#
-# \Method CSV.read returns the entire \CSV data:
-# File.open(path) do |file|
-# CSV.read(file)
-# end # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
-#
-# As does method CSV.parse:
-# File.open(path) do |file|
-# CSV.parse(file)
-# end # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
-#
-# \Method CSV.parse_line returns only the first row:
-# File.open(path) do |file|
-# CSV.parse_line(file)
-# end # => ["foo", "0"]
-#
-# \Method CSV.foreach iterates, passing each row to the given block:
-# File.open(path) do |file|
-# CSV.foreach(file) do |row|
-# p row
-# end
-# end
-# Output:
-# ["foo", "0"]
-# ["bar", "1"]
-# ["baz", "2"]
-#
-# \Method CSV.table returns the entire \CSV data as a CSV::Table object:
-# File.open(path) do |file|
-# CSV.table(file)
-# end # => #<CSV::Table mode:col_or_row row_count:3>
-#
-# === Simple Generating
-#
-# \Method CSV.generate returns a \String;
-# this example uses method CSV#<< to append the rows
-# that are to be generated:
-# output_string = CSV.generate do |csv|
-# csv << ['foo', 0]
-# csv << ['bar', 1]
-# csv << ['baz', 2]
-# end
-# output_string # => "foo,0\nbar,1\nbaz,2\n"
-#
-# \Method CSV.generate_line returns a \String containing the single row
-# constructed from an \Array:
-# CSV.generate_line(['foo', '0']) # => "foo,0\n"
-#
-# \CSV extends class \Array with instance method <tt>Array#to_csv</tt>,
-# which forms an \Array into a \String:
-# ['foo', '0'].to_csv # => "foo,0\n"
-#
-# === "Filtering" \CSV
-#
-# \Method CSV.filter provides a Unix-style filter for \CSV data.
-# The input data is processed to form the output data:
-# in_string = "foo,0\nbar,1\nbaz,2\n"
-# out_string = ''
-# CSV.filter(in_string, out_string) do |row|
-# row[0] = row[0].upcase
-# row[1] *= 4
-# end
-# out_string # => "FOO,0000\nBAR,1111\nBAZ,2222\n"
-#
-# == \CSV Objects
-#
-# There are three ways to create a \CSV object:
-# - \Method CSV.new returns a new \CSV object.
-# - \Method CSV.instance returns a new or cached \CSV object.
-# - \Method \CSV() also returns a new or cached \CSV object.
-#
-# === Instance Methods
-#
-# \CSV has three groups of instance methods:
-# - Its own internally defined instance methods.
-# - Methods included by module Enumerable.
-# - Methods delegated to class IO. See below.
-#
-# ==== Delegated Methods
-#
-# For convenience, a CSV object will delegate to many methods in class IO.
-# (A few have wrapper "guard code" in \CSV.) You may call:
-# * IO#binmode
-# * #binmode?
-# * IO#close
-# * IO#close_read
-# * IO#close_write
-# * IO#closed?
-# * #eof
-# * #eof?
-# * IO#external_encoding
-# * IO#fcntl
-# * IO#fileno
-# * #flock
-# * IO#flush
-# * IO#fsync
-# * IO#internal_encoding
-# * #ioctl
-# * IO#isatty
-# * #path
-# * IO#pid
-# * IO#pos
-# * IO#pos=
-# * IO#reopen
-# * #rewind
-# * IO#seek
-# * #stat
-# * IO#string
-# * IO#sync
-# * IO#sync=
-# * IO#tell
-# * #to_i
-# * #to_io
-# * IO#truncate
-# * IO#tty?
-#
-# === Options
-#
-# The default values for options are:
-# DEFAULT_OPTIONS = {
-# # For both parsing and generating.
-# col_sep: ",",
-# row_sep: :auto,
-# quote_char: '"',
-# # For parsing.
-# field_size_limit: nil,
-# converters: nil,
-# unconverted_fields: nil,
-# headers: false,
-# return_headers: false,
-# header_converters: nil,
-# skip_blanks: false,
-# skip_lines: nil,
-# liberal_parsing: false,
-# nil_value: nil,
-# empty_value: "",
-# strip: false,
-# # For generating.
-# write_headers: nil,
-# quote_empty: true,
-# force_quotes: false,
-# write_converters: nil,
-# write_nil_value: nil,
-# write_empty_value: "",
-# }
-#
-# ==== Options for Parsing
-#
-# Options for parsing, described in detail below, include:
-# - +row_sep+: Specifies the row separator; used to delimit rows.
-# - +col_sep+: Specifies the column separator; used to delimit fields.
-# - +quote_char+: Specifies the quote character; used to quote fields.
-# - +field_size_limit+: Specifies the maximum field size + 1 allowed.
-# Deprecated since 3.2.3. Use +max_field_size+ instead.
-# - +max_field_size+: Specifies the maximum field size allowed.
-# - +converters+: Specifies the field converters to be used.
-# - +unconverted_fields+: Specifies whether unconverted fields are to be available.
-# - +headers+: Specifies whether data contains headers,
-# or specifies the headers themselves.
-# - +return_headers+: Specifies whether headers are to be returned.
-# - +header_converters+: Specifies the header converters to be used.
-# - +skip_blanks+: Specifies whether blanks lines are to be ignored.
-# - +skip_lines+: Specifies how comments lines are to be recognized.
-# - +strip+: Specifies whether leading and trailing whitespace are to be
-# stripped from fields. This must be compatible with +col_sep+; if it is not,
-# then an +ArgumentError+ exception will be raised.
-# - +liberal_parsing+: Specifies whether \CSV should attempt to parse
-# non-compliant data.
-# - +nil_value+: Specifies the object that is to be substituted for each null (no-text) field.
-# - +empty_value+: Specifies the object that is to be substituted for each empty field.
-#
-# :include: ../doc/csv/options/common/row_sep.rdoc
-#
-# :include: ../doc/csv/options/common/col_sep.rdoc
-#
-# :include: ../doc/csv/options/common/quote_char.rdoc
-#
-# :include: ../doc/csv/options/parsing/field_size_limit.rdoc
-#
-# :include: ../doc/csv/options/parsing/converters.rdoc
-#
-# :include: ../doc/csv/options/parsing/unconverted_fields.rdoc
-#
-# :include: ../doc/csv/options/parsing/headers.rdoc
-#
-# :include: ../doc/csv/options/parsing/return_headers.rdoc
-#
-# :include: ../doc/csv/options/parsing/header_converters.rdoc
-#
-# :include: ../doc/csv/options/parsing/skip_blanks.rdoc
-#
-# :include: ../doc/csv/options/parsing/skip_lines.rdoc
-#
-# :include: ../doc/csv/options/parsing/strip.rdoc
-#
-# :include: ../doc/csv/options/parsing/liberal_parsing.rdoc
-#
-# :include: ../doc/csv/options/parsing/nil_value.rdoc
-#
-# :include: ../doc/csv/options/parsing/empty_value.rdoc
-#
-# ==== Options for Generating
-#
-# Options for generating, described in detail below, include:
-# - +row_sep+: Specifies the row separator; used to delimit rows.
-# - +col_sep+: Specifies the column separator; used to delimit fields.
-# - +quote_char+: Specifies the quote character; used to quote fields.
-# - +write_headers+: Specifies whether headers are to be written.
-# - +force_quotes+: Specifies whether each output field is to be quoted.
-# - +quote_empty+: Specifies whether each empty output field is to be quoted.
-# - +write_converters+: Specifies the field converters to be used in writing.
-# - +write_nil_value+: Specifies the object that is to be substituted for each +nil+-valued field.
-# - +write_empty_value+: Specifies the object that is to be substituted for each empty field.
-#
-# :include: ../doc/csv/options/common/row_sep.rdoc
-#
-# :include: ../doc/csv/options/common/col_sep.rdoc
-#
-# :include: ../doc/csv/options/common/quote_char.rdoc
-#
-# :include: ../doc/csv/options/generating/write_headers.rdoc
-#
-# :include: ../doc/csv/options/generating/force_quotes.rdoc
-#
-# :include: ../doc/csv/options/generating/quote_empty.rdoc
-#
-# :include: ../doc/csv/options/generating/write_converters.rdoc
-#
-# :include: ../doc/csv/options/generating/write_nil_value.rdoc
-#
-# :include: ../doc/csv/options/generating/write_empty_value.rdoc
-#
-# === \CSV with Headers
-#
-# CSV allows to specify column names of CSV file, whether they are in data, or
-# provided separately. If headers are specified, reading methods return an instance
-# of CSV::Table, consisting of CSV::Row.
-#
-# # Headers are part of data
-# data = CSV.parse(<<~ROWS, headers: true)
-# Name,Department,Salary
-# Bob,Engineering,1000
-# Jane,Sales,2000
-# John,Management,5000
-# ROWS
-#
-# data.class #=> CSV::Table
-# data.first #=> #<CSV::Row "Name":"Bob" "Department":"Engineering" "Salary":"1000">
-# data.first.to_h #=> {"Name"=>"Bob", "Department"=>"Engineering", "Salary"=>"1000"}
-#
-# # Headers provided by developer
-# data = CSV.parse('Bob,Engineering,1000', headers: %i[name department salary])
-# data.first #=> #<CSV::Row name:"Bob" department:"Engineering" salary:"1000">
-#
-# === \Converters
-#
-# By default, each value (field or header) parsed by \CSV is formed into a \String.
-# You can use a _field_ _converter_ or _header_ _converter_
-# to intercept and modify the parsed values:
-# - See {Field Converters}[#class-CSV-label-Field+Converters].
-# - See {Header Converters}[#class-CSV-label-Header+Converters].
-#
-# Also by default, each value to be written during generation is written 'as-is'.
-# You can use a _write_ _converter_ to modify values before writing.
-# - See {Write Converters}[#class-CSV-label-Write+Converters].
-#
-# ==== Specifying \Converters
-#
-# You can specify converters for parsing or generating in the +options+
-# argument to various \CSV methods:
-# - Option +converters+ for converting parsed field values.
-# - Option +header_converters+ for converting parsed header values.
-# - Option +write_converters+ for converting values to be written (generated).
-#
-# There are three forms for specifying converters:
-# - A converter proc: executable code to be used for conversion.
-# - A converter name: the name of a stored converter.
-# - A converter list: an array of converter procs, converter names, and converter lists.
-#
-# ===== Converter Procs
-#
-# This converter proc, +strip_converter+, accepts a value +field+
-# and returns <tt>field.strip</tt>:
-# strip_converter = proc {|field| field.strip }
-# In this call to <tt>CSV.parse</tt>,
-# the keyword argument <tt>converters: string_converter</tt>
-# specifies that:
-# - \Proc +string_converter+ is to be called for each parsed field.
-# - The converter's return value is to replace the +field+ value.
-# Example:
-# string = " foo , 0 \n bar , 1 \n baz , 2 \n"
-# array = CSV.parse(string, converters: strip_converter)
-# array # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
-#
-# A converter proc can receive a second argument, +field_info+,
-# that contains details about the field.
-# This modified +strip_converter+ displays its arguments:
-# strip_converter = proc do |field, field_info|
-# p [field, field_info]
-# field.strip
-# end
-# string = " foo , 0 \n bar , 1 \n baz , 2 \n"
-# array = CSV.parse(string, converters: strip_converter)
-# array # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
-# Output:
-# [" foo ", #<struct CSV::FieldInfo index=0, line=1, header=nil>]
-# [" 0 ", #<struct CSV::FieldInfo index=1, line=1, header=nil>]
-# [" bar ", #<struct CSV::FieldInfo index=0, line=2, header=nil>]
-# [" 1 ", #<struct CSV::FieldInfo index=1, line=2, header=nil>]
-# [" baz ", #<struct CSV::FieldInfo index=0, line=3, header=nil>]
-# [" 2 ", #<struct CSV::FieldInfo index=1, line=3, header=nil>]
-# Each CSV::FieldInfo object shows:
-# - The 0-based field index.
-# - The 1-based line index.
-# - The field header, if any.
-#
-# ===== Stored \Converters
-#
-# A converter may be given a name and stored in a structure where
-# the parsing methods can find it by name.
-#
-# The storage structure for field converters is the \Hash CSV::Converters.
-# It has several built-in converter procs:
-# - <tt>:integer</tt>: converts each \String-embedded integer into a true \Integer.
-# - <tt>:float</tt>: converts each \String-embedded float into a true \Float.
-# - <tt>:date</tt>: converts each \String-embedded date into a true \Date.
-# - <tt>:date_time</tt>: converts each \String-embedded date-time into a true \DateTime
-# .
-# This example creates a converter proc, then stores it:
-# strip_converter = proc {|field| field.strip }
-# CSV::Converters[:strip] = strip_converter
-# Then the parsing method call can refer to the converter
-# by its name, <tt>:strip</tt>:
-# string = " foo , 0 \n bar , 1 \n baz , 2 \n"
-# array = CSV.parse(string, converters: :strip)
-# array # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
-#
-# The storage structure for header converters is the \Hash CSV::HeaderConverters,
-# which works in the same way.
-# It also has built-in converter procs:
-# - <tt>:downcase</tt>: Downcases each header.
-# - <tt>:symbol</tt>: Converts each header to a \Symbol.
-#
-# There is no such storage structure for write headers.
-#
-# In order for the parsing methods to access stored converters in non-main-Ractors, the
-# storage structure must be made shareable first.
-# Therefore, <tt>Ractor.make_shareable(CSV::Converters)</tt> and
-# <tt>Ractor.make_shareable(CSV::HeaderConverters)</tt> must be called before the creation
-# of Ractors that use the converters stored in these structures. (Since making the storage
-# structures shareable involves freezing them, any custom converters that are to be used
-# must be added first.)
-#
-# ===== Converter Lists
-#
-# A _converter_ _list_ is an \Array that may include any assortment of:
-# - Converter procs.
-# - Names of stored converters.
-# - Nested converter lists.
-#
-# Examples:
-# numeric_converters = [:integer, :float]
-# date_converters = [:date, :date_time]
-# [numeric_converters, strip_converter]
-# [strip_converter, date_converters, :float]
-#
-# Like a converter proc, a converter list may be named and stored in either
-# \CSV::Converters or CSV::HeaderConverters:
-# CSV::Converters[:custom] = [strip_converter, date_converters, :float]
-# CSV::HeaderConverters[:custom] = [:downcase, :symbol]
-#
-# There are two built-in converter lists:
-# CSV::Converters[:numeric] # => [:integer, :float]
-# CSV::Converters[:all] # => [:date_time, :numeric]
-#
-# ==== Field \Converters
-#
-# With no conversion, all parsed fields in all rows become Strings:
-# string = "foo,0\nbar,1\nbaz,2\n"
-# ary = CSV.parse(string)
-# ary # => # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
-#
-# When you specify a field converter, each parsed field is passed to the converter;
-# its return value becomes the stored value for the field.
-# A converter might, for example, convert an integer embedded in a \String
-# into a true \Integer.
-# (In fact, that's what built-in field converter +:integer+ does.)
-#
-# There are three ways to use field \converters.
-#
-# - Using option {converters}[#class-CSV-label-Option+converters] with a parsing method:
-# ary = CSV.parse(string, converters: :integer)
-# ary # => [0, 1, 2] # => [["foo", 0], ["bar", 1], ["baz", 2]]
-# - Using option {converters}[#class-CSV-label-Option+converters] with a new \CSV instance:
-# csv = CSV.new(string, converters: :integer)
-# # Field converters in effect:
-# csv.converters # => [:integer]
-# csv.read # => [["foo", 0], ["bar", 1], ["baz", 2]]
-# - Using method #convert to add a field converter to a \CSV instance:
-# csv = CSV.new(string)
-# # Add a converter.
-# csv.convert(:integer)
-# csv.converters # => [:integer]
-# csv.read # => [["foo", 0], ["bar", 1], ["baz", 2]]
-#
-# Installing a field converter does not affect already-read rows:
-# csv = CSV.new(string)
-# csv.shift # => ["foo", "0"]
-# # Add a converter.
-# csv.convert(:integer)
-# csv.converters # => [:integer]
-# csv.read # => [["bar", 1], ["baz", 2]]
-#
-# There are additional built-in \converters, and custom \converters are also supported.
-#
-# ===== Built-In Field \Converters
-#
-# The built-in field converters are in \Hash CSV::Converters:
-# - Each key is a field converter name.
-# - Each value is one of:
-# - A \Proc field converter.
-# - An \Array of field converter names.
-#
-# Display:
-# CSV::Converters.each_pair do |name, value|
-# if value.kind_of?(Proc)
-# p [name, value.class]
-# else
-# p [name, value]
-# end
-# end
-# Output:
-# [:integer, Proc]
-# [:float, Proc]
-# [:numeric, [:integer, :float]]
-# [:date, Proc]
-# [:date_time, Proc]
-# [:all, [:date_time, :numeric]]
-#
-# Each of these converters transcodes values to UTF-8 before attempting conversion.
-# If a value cannot be transcoded to UTF-8 the conversion will
-# fail and the value will remain unconverted.
-#
-# Converter +:integer+ converts each field that Integer() accepts:
-# data = '0,1,2,x'
-# # Without the converter
-# csv = CSV.parse_line(data)
-# csv # => ["0", "1", "2", "x"]
-# # With the converter
-# csv = CSV.parse_line(data, converters: :integer)
-# csv # => [0, 1, 2, "x"]
-#
-# Converter +:float+ converts each field that Float() accepts:
-# data = '1.0,3.14159,x'
-# # Without the converter
-# csv = CSV.parse_line(data)
-# csv # => ["1.0", "3.14159", "x"]
-# # With the converter
-# csv = CSV.parse_line(data, converters: :float)
-# csv # => [1.0, 3.14159, "x"]
-#
-# Converter +:numeric+ converts with both +:integer+ and +:float+..
-#
-# Converter +:date+ converts each field that Date::parse accepts:
-# data = '2001-02-03,x'
-# # Without the converter
-# csv = CSV.parse_line(data)
-# csv # => ["2001-02-03", "x"]
-# # With the converter
-# csv = CSV.parse_line(data, converters: :date)
-# csv # => [#<Date: 2001-02-03 ((2451944j,0s,0n),+0s,2299161j)>, "x"]
-#
-# Converter +:date_time+ converts each field that DateTime::parse accepts:
-# data = '2020-05-07T14:59:00-05:00,x'
-# # Without the converter
-# csv = CSV.parse_line(data)
-# csv # => ["2020-05-07T14:59:00-05:00", "x"]
-# # With the converter
-# csv = CSV.parse_line(data, converters: :date_time)
-# csv # => [#<DateTime: 2020-05-07T14:59:00-05:00 ((2458977j,71940s,0n),-18000s,2299161j)>, "x"]
-#
-# Converter +:numeric+ converts with both +:date_time+ and +:numeric+..
-#
-# As seen above, method #convert adds \converters to a \CSV instance,
-# and method #converters returns an \Array of the \converters in effect:
-# csv = CSV.new('0,1,2')
-# csv.converters # => []
-# csv.convert(:integer)
-# csv.converters # => [:integer]
-# csv.convert(:date)
-# csv.converters # => [:integer, :date]
-#
-# ===== Custom Field \Converters
-#
-# You can define a custom field converter:
-# strip_converter = proc {|field| field.strip }
-# string = " foo , 0 \n bar , 1 \n baz , 2 \n"
-# array = CSV.parse(string, converters: strip_converter)
-# array # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
-# You can register the converter in \Converters \Hash,
-# which allows you to refer to it by name:
-# CSV::Converters[:strip] = strip_converter
-# string = " foo , 0 \n bar , 1 \n baz , 2 \n"
-# array = CSV.parse(string, converters: :strip)
-# array # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
-#
-# ==== Header \Converters
-#
-# Header converters operate only on headers (and not on other rows).
-#
-# There are three ways to use header \converters;
-# these examples use built-in header converter +:downcase+,
-# which downcases each parsed header.
-#
-# - Option +header_converters+ with a singleton parsing method:
-# string = "Name,Count\nFoo,0\n,Bar,1\nBaz,2"
-# tbl = CSV.parse(string, headers: true, header_converters: :downcase)
-# tbl.class # => CSV::Table
-# tbl.headers # => ["name", "count"]
-#
-# - Option +header_converters+ with a new \CSV instance:
-# csv = CSV.new(string, header_converters: :downcase)
-# # Header converters in effect:
-# csv.header_converters # => [:downcase]
-# tbl = CSV.parse(string, headers: true)
-# tbl.headers # => ["Name", "Count"]
-#
-# - Method #header_convert adds a header converter to a \CSV instance:
-# csv = CSV.new(string)
-# # Add a header converter.
-# csv.header_convert(:downcase)
-# csv.header_converters # => [:downcase]
-# tbl = CSV.parse(string, headers: true)
-# tbl.headers # => ["Name", "Count"]
-#
-# ===== Built-In Header \Converters
-#
-# The built-in header \converters are in \Hash CSV::HeaderConverters.
-# The keys there are the names of the \converters:
-# CSV::HeaderConverters.keys # => [:downcase, :symbol]
-#
-# Converter +:downcase+ converts each header by downcasing it:
-# string = "Name,Count\nFoo,0\n,Bar,1\nBaz,2"
-# tbl = CSV.parse(string, headers: true, header_converters: :downcase)
-# tbl.class # => CSV::Table
-# tbl.headers # => ["name", "count"]
-#
-# Converter +:symbol+ converts each header by making it into a \Symbol:
-# string = "Name,Count\nFoo,0\n,Bar,1\nBaz,2"
-# tbl = CSV.parse(string, headers: true, header_converters: :symbol)
-# tbl.headers # => [:name, :count]
-# Details:
-# - Strips leading and trailing whitespace.
-# - Downcases the header.
-# - Replaces embedded spaces with underscores.
-# - Removes non-word characters.
-# - Makes the string into a \Symbol.
-#
-# ===== Custom Header \Converters
-#
-# You can define a custom header converter:
-# upcase_converter = proc {|header| header.upcase }
-# string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
-# table = CSV.parse(string, headers: true, header_converters: upcase_converter)
-# table # => #<CSV::Table mode:col_or_row row_count:4>
-# table.headers # => ["NAME", "VALUE"]
-# You can register the converter in \HeaderConverters \Hash,
-# which allows you to refer to it by name:
-# CSV::HeaderConverters[:upcase] = upcase_converter
-# table = CSV.parse(string, headers: true, header_converters: :upcase)
-# table # => #<CSV::Table mode:col_or_row row_count:4>
-# table.headers # => ["NAME", "VALUE"]
-#
-# ===== Write \Converters
-#
-# When you specify a write converter for generating \CSV,
-# each field to be written is passed to the converter;
-# its return value becomes the new value for the field.
-# A converter might, for example, strip whitespace from a field.
-#
-# Using no write converter (all fields unmodified):
-# output_string = CSV.generate do |csv|
-# csv << [' foo ', 0]
-# csv << [' bar ', 1]
-# csv << [' baz ', 2]
-# end
-# output_string # => " foo ,0\n bar ,1\n baz ,2\n"
-# Using option +write_converters+ with two custom write converters:
-# strip_converter = proc {|field| field.respond_to?(:strip) ? field.strip : field }
-# upcase_converter = proc {|field| field.respond_to?(:upcase) ? field.upcase : field }
-# write_converters = [strip_converter, upcase_converter]
-# output_string = CSV.generate(write_converters: write_converters) do |csv|
-# csv << [' foo ', 0]
-# csv << [' bar ', 1]
-# csv << [' baz ', 2]
-# end
-# output_string # => "FOO,0\nBAR,1\nBAZ,2\n"
-#
-# === Character Encodings (M17n or Multilingualization)
-#
-# This new CSV parser is m17n savvy. The parser works in the Encoding of the IO
-# or String object being read from or written to. Your data is never transcoded
-# (unless you ask Ruby to transcode it for you) and will literally be parsed in
-# the Encoding it is in. Thus CSV will return Arrays or Rows of Strings in the
-# Encoding of your data. This is accomplished by transcoding the parser itself
-# into your Encoding.
-#
-# Some transcoding must take place, of course, to accomplish this multiencoding
-# support. For example, <tt>:col_sep</tt>, <tt>:row_sep</tt>, and
-# <tt>:quote_char</tt> must be transcoded to match your data. Hopefully this
-# makes the entire process feel transparent, since CSV's defaults should just
-# magically work for your data. However, you can set these values manually in
-# the target Encoding to avoid the translation.
-#
-# It's also important to note that while all of CSV's core parser is now
-# Encoding agnostic, some features are not. For example, the built-in
-# converters will try to transcode data to UTF-8 before making conversions.
-# Again, you can provide custom converters that are aware of your Encodings to
-# avoid this translation. It's just too hard for me to support native
-# conversions in all of Ruby's Encodings.
-#
-# Anyway, the practical side of this is simple: make sure IO and String objects
-# passed into CSV have the proper Encoding set and everything should just work.
-# CSV methods that allow you to open IO objects (CSV::foreach(), CSV::open(),
-# CSV::read(), and CSV::readlines()) do allow you to specify the Encoding.
-#
-# One minor exception comes when generating CSV into a String with an Encoding
-# that is not ASCII compatible. There's no existing data for CSV to use to
-# prepare itself and thus you will probably need to manually specify the desired
-# Encoding for most of those cases. It will try to guess using the fields in a
-# row of output though, when using CSV::generate_line() or Array#to_csv().
-#
-# I try to point out any other Encoding issues in the documentation of methods
-# as they come up.
-#
-# This has been tested to the best of my ability with all non-"dummy" Encodings
-# Ruby ships with. However, it is brave new code and may have some bugs.
-# Please feel free to {report}[mailto:james@grayproductions.net] any issues you
-# find with it.
-#
-class CSV
-
- # The error thrown when the parser encounters illegal CSV formatting.
- class MalformedCSVError < RuntimeError
- attr_reader :line_number
- alias_method :lineno, :line_number
- def initialize(message, line_number)
- @line_number = line_number
- super("#{message} in line #{line_number}.")
- end
- end
-
- #
- # A FieldInfo Struct contains details about a field's position in the data
- # source it was read from. CSV will pass this Struct to some blocks that make
- # decisions based on field structure. See CSV.convert_fields() for an
- # example.
- #
- # <b><tt>index</tt></b>:: The zero-based index of the field in its row.
- # <b><tt>line</tt></b>:: The line of the data source this row is from.
- # <b><tt>header</tt></b>:: The header for the column, when available.
- # <b><tt>quoted?</tt></b>:: True or false, whether the original value is quoted or not.
- #
- FieldInfo = Struct.new(:index, :line, :header, :quoted?)
-
- # A Regexp used to find and convert some common Date formats.
- DateMatcher = / \A(?: (\w+,?\s+)?\w+\s+\d{1,2},?\s+\d{2,4} |
- \d{4}-\d{2}-\d{2} )\z /x
- # A Regexp used to find and convert some common DateTime formats.
- DateTimeMatcher =
- / \A(?: (\w+,?\s+)?\w+\s+\d{1,2}\s+\d{1,2}:\d{1,2}:\d{1,2},?\s+\d{2,4} |
- # ISO-8601 and RFC-3339 (space instead of T) recognized by DateTime.parse
- \d{4}-\d{2}-\d{2}
- (?:[T\s]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?(?:[+-]\d{2}(?::\d{2})|Z)?)?)?
- )\z /x
-
- # The encoding used by all converters.
- ConverterEncoding = Encoding.find("UTF-8")
-
- # A \Hash containing the names and \Procs for the built-in field converters.
- # See {Built-In Field Converters}[#class-CSV-label-Built-In+Field+Converters].
- #
- # This \Hash is intentionally left unfrozen, and may be extended with
- # custom field converters.
- # See {Custom Field Converters}[#class-CSV-label-Custom+Field+Converters].
- Converters = {
- integer: lambda { |f|
- Integer(f.encode(ConverterEncoding)) rescue f
- },
- float: lambda { |f|
- Float(f.encode(ConverterEncoding)) rescue f
- },
- numeric: [:integer, :float],
- date: lambda { |f|
- begin
- e = f.encode(ConverterEncoding)
- e.match?(DateMatcher) ? Date.parse(e) : f
- rescue # encoding conversion or date parse errors
- f
- end
- },
- date_time: lambda { |f|
- begin
- e = f.encode(ConverterEncoding)
- e.match?(DateTimeMatcher) ? DateTime.parse(e) : f
- rescue # encoding conversion or date parse errors
- f
- end
- },
- all: [:date_time, :numeric],
- }
-
- # A \Hash containing the names and \Procs for the built-in header converters.
- # See {Built-In Header Converters}[#class-CSV-label-Built-In+Header+Converters].
- #
- # This \Hash is intentionally left unfrozen, and may be extended with
- # custom field converters.
- # See {Custom Header Converters}[#class-CSV-label-Custom+Header+Converters].
- HeaderConverters = {
- downcase: lambda { |h| h.encode(ConverterEncoding).downcase },
- symbol: lambda { |h|
- h.encode(ConverterEncoding).downcase.gsub(/[^\s\w]+/, "").strip.
- gsub(/\s+/, "_").to_sym
- },
- symbol_raw: lambda { |h| h.encode(ConverterEncoding).to_sym }
- }
-
- # Default values for method options.
- DEFAULT_OPTIONS = {
- # For both parsing and generating.
- col_sep: ",",
- row_sep: :auto,
- quote_char: '"',
- # For parsing.
- field_size_limit: nil,
- max_field_size: nil,
- converters: nil,
- unconverted_fields: nil,
- headers: false,
- return_headers: false,
- header_converters: nil,
- skip_blanks: false,
- skip_lines: nil,
- liberal_parsing: false,
- nil_value: nil,
- empty_value: "",
- strip: false,
- # For generating.
- write_headers: nil,
- quote_empty: true,
- force_quotes: false,
- write_converters: nil,
- write_nil_value: nil,
- write_empty_value: "",
- }.freeze
-
- class << self
- # :call-seq:
- # instance(string, **options)
- # instance(io = $stdout, **options)
- # instance(string, **options) {|csv| ... }
- # instance(io = $stdout, **options) {|csv| ... }
- #
- # Creates or retrieves cached \CSV objects.
- # For arguments and options, see CSV.new.
- #
- # This API is not Ractor-safe.
- #
- # ---
- #
- # With no block given, returns a \CSV object.
- #
- # The first call to +instance+ creates and caches a \CSV object:
- # s0 = 's0'
- # csv0 = CSV.instance(s0)
- # csv0.class # => CSV
- #
- # Subsequent calls to +instance+ with that _same_ +string+ or +io+
- # retrieve that same cached object:
- # csv1 = CSV.instance(s0)
- # csv1.class # => CSV
- # csv1.equal?(csv0) # => true # Same CSV object
- #
- # A subsequent call to +instance+ with a _different_ +string+ or +io+
- # creates and caches a _different_ \CSV object.
- # s1 = 's1'
- # csv2 = CSV.instance(s1)
- # csv2.equal?(csv0) # => false # Different CSV object
- #
- # All the cached objects remains available:
- # csv3 = CSV.instance(s0)
- # csv3.equal?(csv0) # true # Same CSV object
- # csv4 = CSV.instance(s1)
- # csv4.equal?(csv2) # true # Same CSV object
- #
- # ---
- #
- # When a block is given, calls the block with the created or retrieved
- # \CSV object; returns the block's return value:
- # CSV.instance(s0) {|csv| :foo } # => :foo
- def instance(data = $stdout, **options)
- # create a _signature_ for this method call, data object and options
- sig = [data.object_id] +
- options.values_at(*DEFAULT_OPTIONS.keys)
-
- # fetch or create the instance for this signature
- @@instances ||= Hash.new
- instance = (@@instances[sig] ||= new(data, **options))
-
- if block_given?
- yield instance # run block, if given, returning result
- else
- instance # or return the instance
- end
- end
-
- # :call-seq:
- # filter(in_string_or_io, **options) {|row| ... } -> array_of_arrays or csv_table
- # filter(in_string_or_io, out_string_or_io, **options) {|row| ... } -> array_of_arrays or csv_table
- # filter(**options) {|row| ... } -> array_of_arrays or csv_table
- #
- # - Parses \CSV from a source (\String, \IO stream, or ARGF).
- # - Calls the given block with each parsed row:
- # - Without headers, each row is an \Array.
- # - With headers, each row is a CSV::Row.
- # - Generates \CSV to an output (\String, \IO stream, or STDOUT).
- # - Returns the parsed source:
- # - Without headers, an \Array of \Arrays.
- # - With headers, a CSV::Table.
- #
- # When +in_string_or_io+ is given, but not +out_string_or_io+,
- # parses from the given +in_string_or_io+
- # and generates to STDOUT.
- #
- # \String input without headers:
- #
- # in_string = "foo,0\nbar,1\nbaz,2"
- # CSV.filter(in_string) do |row|
- # row[0].upcase!
- # row[1] = - row[1].to_i
- # end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
- #
- # Output (to STDOUT):
- #
- # FOO,0
- # BAR,-1
- # BAZ,-2
- #
- # \String input with headers:
- #
- # in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2"
- # CSV.filter(in_string, headers: true) do |row|
- # row[0].upcase!
- # row[1] = - row[1].to_i
- # end # => #<CSV::Table mode:col_or_row row_count:4>
- #
- # Output (to STDOUT):
- #
- # Name,Value
- # FOO,0
- # BAR,-1
- # BAZ,-2
- #
- # \IO stream input without headers:
- #
- # File.write('t.csv', "foo,0\nbar,1\nbaz,2")
- # File.open('t.csv') do |in_io|
- # CSV.filter(in_io) do |row|
- # row[0].upcase!
- # row[1] = - row[1].to_i
- # end
- # end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
- #
- # Output (to STDOUT):
- #
- # FOO,0
- # BAR,-1
- # BAZ,-2
- #
- # \IO stream input with headers:
- #
- # File.write('t.csv', "Name,Value\nfoo,0\nbar,1\nbaz,2")
- # File.open('t.csv') do |in_io|
- # CSV.filter(in_io, headers: true) do |row|
- # row[0].upcase!
- # row[1] = - row[1].to_i
- # end
- # end # => #<CSV::Table mode:col_or_row row_count:4>
- #
- # Output (to STDOUT):
- #
- # Name,Value
- # FOO,0
- # BAR,-1
- # BAZ,-2
- #
- # When both +in_string_or_io+ and +out_string_or_io+ are given,
- # parses from +in_string_or_io+ and generates to +out_string_or_io+.
- #
- # \String output without headers:
- #
- # in_string = "foo,0\nbar,1\nbaz,2"
- # out_string = ''
- # CSV.filter(in_string, out_string) do |row|
- # row[0].upcase!
- # row[1] = - row[1].to_i
- # end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
- # out_string # => "FOO,0\nBAR,-1\nBAZ,-2\n"
- #
- # \String output with headers:
- #
- # in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2"
- # out_string = ''
- # CSV.filter(in_string, out_string, headers: true) do |row|
- # row[0].upcase!
- # row[1] = - row[1].to_i
- # end # => #<CSV::Table mode:col_or_row row_count:4>
- # out_string # => "Name,Value\nFOO,0\nBAR,-1\nBAZ,-2\n"
- #
- # \IO stream output without headers:
- #
- # in_string = "foo,0\nbar,1\nbaz,2"
- # File.open('t.csv', 'w') do |out_io|
- # CSV.filter(in_string, out_io) do |row|
- # row[0].upcase!
- # row[1] = - row[1].to_i
- # end
- # end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
- # File.read('t.csv') # => "FOO,0\nBAR,-1\nBAZ,-2\n"
- #
- # \IO stream output with headers:
- #
- # in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2"
- # File.open('t.csv', 'w') do |out_io|
- # CSV.filter(in_string, out_io, headers: true) do |row|
- # row[0].upcase!
- # row[1] = - row[1].to_i
- # end
- # end # => #<CSV::Table mode:col_or_row row_count:4>
- # File.read('t.csv') # => "Name,Value\nFOO,0\nBAR,-1\nBAZ,-2\n"
- #
- # When neither +in_string_or_io+ nor +out_string_or_io+ given,
- # parses from {ARGF}[rdoc-ref:ARGF]
- # and generates to STDOUT.
- #
- # Without headers:
- #
- # # Put Ruby code into a file.
- # ruby = <<-EOT
- # require 'csv'
- # CSV.filter do |row|
- # row[0].upcase!
- # row[1] = - row[1].to_i
- # end
- # EOT
- # File.write('t.rb', ruby)
- # # Put some CSV into a file.
- # File.write('t.csv', "foo,0\nbar,1\nbaz,2")
- # # Run the Ruby code with CSV filename as argument.
- # system(Gem.ruby, "t.rb", "t.csv")
- #
- # Output (to STDOUT):
- #
- # FOO,0
- # BAR,-1
- # BAZ,-2
- #
- # With headers:
- #
- # # Put Ruby code into a file.
- # ruby = <<-EOT
- # require 'csv'
- # CSV.filter(headers: true) do |row|
- # row[0].upcase!
- # row[1] = - row[1].to_i
- # end
- # EOT
- # File.write('t.rb', ruby)
- # # Put some CSV into a file.
- # File.write('t.csv', "Name,Value\nfoo,0\nbar,1\nbaz,2")
- # # Run the Ruby code with CSV filename as argument.
- # system(Gem.ruby, "t.rb", "t.csv")
- #
- # Output (to STDOUT):
- #
- # Name,Value
- # FOO,0
- # BAR,-1
- # BAZ,-2
- #
- # Arguments:
- #
- # * Argument +in_string_or_io+ must be a \String or an \IO stream.
- # * Argument +out_string_or_io+ must be a \String or an \IO stream.
- # * Arguments <tt>**options</tt> must be keyword options.
- # See {Options for Parsing}[#class-CSV-label-Options+for+Parsing].
- def filter(input=nil, output=nil, **options)
- # parse options for input, output, or both
- in_options, out_options = Hash.new, {row_sep: InputRecordSeparator.value}
- options.each do |key, value|
- case key
- when /\Ain(?:put)?_(.+)\Z/
- in_options[$1.to_sym] = value
- when /\Aout(?:put)?_(.+)\Z/
- out_options[$1.to_sym] = value
- else
- in_options[key] = value
- out_options[key] = value
- end
- end
-
- # build input and output wrappers
- input = new(input || ARGF, **in_options)
- output = new(output || $stdout, **out_options)
-
- # process headers
- need_manual_header_output =
- (in_options[:headers] and
- out_options[:headers] == true and
- out_options[:write_headers])
- if need_manual_header_output
- first_row = input.shift
- if first_row
- if first_row.is_a?(Row)
- headers = first_row.headers
- yield headers
- output << headers
- end
- yield first_row
- output << first_row
- end
- end
-
- # read, yield, write
- input.each do |row|
- yield row
- output << row
- end
- end
-
- #
- # :call-seq:
- # foreach(path_or_io, mode='r', **options) {|row| ... )
- # foreach(path_or_io, mode='r', **options) -> new_enumerator
- #
- # Calls the block with each row read from source +path_or_io+.
- #
- # \Path input without headers:
- #
- # string = "foo,0\nbar,1\nbaz,2\n"
- # in_path = 't.csv'
- # File.write(in_path, string)
- # CSV.foreach(in_path) {|row| p row }
- #
- # Output:
- #
- # ["foo", "0"]
- # ["bar", "1"]
- # ["baz", "2"]
- #
- # \Path input with headers:
- #
- # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # in_path = 't.csv'
- # File.write(in_path, string)
- # CSV.foreach(in_path, headers: true) {|row| p row }
- #
- # Output:
- #
- # <CSV::Row "Name":"foo" "Value":"0">
- # <CSV::Row "Name":"bar" "Value":"1">
- # <CSV::Row "Name":"baz" "Value":"2">
- #
- # \IO stream input without headers:
- #
- # string = "foo,0\nbar,1\nbaz,2\n"
- # path = 't.csv'
- # File.write(path, string)
- # File.open('t.csv') do |in_io|
- # CSV.foreach(in_io) {|row| p row }
- # end
- #
- # Output:
- #
- # ["foo", "0"]
- # ["bar", "1"]
- # ["baz", "2"]
- #
- # \IO stream input with headers:
- #
- # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # path = 't.csv'
- # File.write(path, string)
- # File.open('t.csv') do |in_io|
- # CSV.foreach(in_io, headers: true) {|row| p row }
- # end
- #
- # Output:
- #
- # <CSV::Row "Name":"foo" "Value":"0">
- # <CSV::Row "Name":"bar" "Value":"1">
- # <CSV::Row "Name":"baz" "Value":"2">
- #
- # With no block given, returns an \Enumerator:
- #
- # string = "foo,0\nbar,1\nbaz,2\n"
- # path = 't.csv'
- # File.write(path, string)
- # CSV.foreach(path) # => #<Enumerator: CSV:foreach("t.csv", "r")>
- #
- # Arguments:
- # * Argument +path_or_io+ must be a file path or an \IO stream.
- # * Argument +mode+, if given, must be a \File mode
- # See {Open Mode}[https://ruby-doc.org/core/IO.html#method-c-new-label-Open+Mode].
- # * Arguments <tt>**options</tt> must be keyword options.
- # See {Options for Parsing}[#class-CSV-label-Options+for+Parsing].
- # * This method optionally accepts an additional <tt>:encoding</tt> option
- # that you can use to specify the Encoding of the data read from +path+ or +io+.
- # You must provide this unless your data is in the encoding
- # given by <tt>Encoding::default_external</tt>.
- # Parsing will use this to determine how to parse the data.
- # You may provide a second Encoding to
- # have the data transcoded as it is read. For example,
- # encoding: 'UTF-32BE:UTF-8'
- # would read +UTF-32BE+ data from the file
- # but transcode it to +UTF-8+ before parsing.
- def foreach(path, mode="r", **options, &block)
- return to_enum(__method__, path, mode, **options) unless block_given?
- open(path, mode, **options) do |csv|
- csv.each(&block)
- end
- end
-
- #
- # :call-seq:
- # generate(csv_string, **options) {|csv| ... }
- # generate(**options) {|csv| ... }
- #
- # * Argument +csv_string+, if given, must be a \String object;
- # defaults to a new empty \String.
- # * Arguments +options+, if given, should be generating options.
- # See {Options for Generating}[#class-CSV-label-Options+for+Generating].
- #
- # ---
- #
- # Creates a new \CSV object via <tt>CSV.new(csv_string, **options)</tt>;
- # calls the block with the \CSV object, which the block may modify;
- # returns the \String generated from the \CSV object.
- #
- # Note that a passed \String *is* modified by this method.
- # Pass <tt>csv_string</tt>.dup if the \String must be preserved.
- #
- # This method has one additional option: <tt>:encoding</tt>,
- # which sets the base Encoding for the output if no no +str+ is specified.
- # CSV needs this hint if you plan to output non-ASCII compatible data.
- #
- # ---
- #
- # Add lines:
- # input_string = "foo,0\nbar,1\nbaz,2\n"
- # output_string = CSV.generate(input_string) do |csv|
- # csv << ['bat', 3]
- # csv << ['bam', 4]
- # end
- # output_string # => "foo,0\nbar,1\nbaz,2\nbat,3\nbam,4\n"
- # input_string # => "foo,0\nbar,1\nbaz,2\nbat,3\nbam,4\n"
- # output_string.equal?(input_string) # => true # Same string, modified
- #
- # Add lines into new string, preserving old string:
- # input_string = "foo,0\nbar,1\nbaz,2\n"
- # output_string = CSV.generate(input_string.dup) do |csv|
- # csv << ['bat', 3]
- # csv << ['bam', 4]
- # end
- # output_string # => "foo,0\nbar,1\nbaz,2\nbat,3\nbam,4\n"
- # input_string # => "foo,0\nbar,1\nbaz,2\n"
- # output_string.equal?(input_string) # => false # Different strings
- #
- # Create lines from nothing:
- # output_string = CSV.generate do |csv|
- # csv << ['foo', 0]
- # csv << ['bar', 1]
- # csv << ['baz', 2]
- # end
- # output_string # => "foo,0\nbar,1\nbaz,2\n"
- #
- # ---
- #
- # Raises an exception if +csv_string+ is not a \String object:
- # # Raises TypeError (no implicit conversion of Integer into String)
- # CSV.generate(0)
- #
- def generate(str=nil, **options)
- encoding = options[:encoding]
- # add a default empty String, if none was given
- if str
- str = StringIO.new(str)
- str.seek(0, IO::SEEK_END)
- str.set_encoding(encoding) if encoding
- else
- str = +""
- str.force_encoding(encoding) if encoding
- end
- csv = new(str, **options) # wrap
- yield csv # yield for appending
- csv.string # return final String
- end
-
- # :call-seq:
- # CSV.generate_line(ary)
- # CSV.generate_line(ary, **options)
- #
- # Returns the \String created by generating \CSV from +ary+
- # using the specified +options+.
- #
- # Argument +ary+ must be an \Array.
- #
- # Special options:
- # * Option <tt>:row_sep</tt> defaults to <tt>"\n"> on Ruby 3.0 or later
- # and <tt>$INPUT_RECORD_SEPARATOR</tt> (<tt>$/</tt>) otherwise.:
- # $INPUT_RECORD_SEPARATOR # => "\n"
- # * This method accepts an additional option, <tt>:encoding</tt>, which sets the base
- # Encoding for the output. This method will try to guess your Encoding from
- # the first non-+nil+ field in +row+, if possible, but you may need to use
- # this parameter as a backup plan.
- #
- # For other +options+,
- # see {Options for Generating}[#class-CSV-label-Options+for+Generating].
- #
- # ---
- #
- # Returns the \String generated from an \Array:
- # CSV.generate_line(['foo', '0']) # => "foo,0\n"
- #
- # ---
- #
- # Raises an exception if +ary+ is not an \Array:
- # # Raises NoMethodError (undefined method `find' for :foo:Symbol)
- # CSV.generate_line(:foo)
- #
- def generate_line(row, **options)
- options = {row_sep: InputRecordSeparator.value}.merge(options)
- str = +""
- if options[:encoding]
- str.force_encoding(options[:encoding])
- else
- fallback_encoding = nil
- output_encoding = nil
- row.each do |field|
- next unless field.is_a?(String)
- fallback_encoding ||= field.encoding
- next if field.ascii_only?
- output_encoding = field.encoding
- break
- end
- output_encoding ||= fallback_encoding
- if output_encoding
- str.force_encoding(output_encoding)
- end
- end
- (new(str, **options) << row).string
- end
-
- # :call-seq:
- # CSV.generate_lines(rows)
- # CSV.generate_lines(rows, **options)
- #
- # Returns the \String created by generating \CSV from
- # using the specified +options+.
- #
- # Argument +rows+ must be an \Array of row. Row is \Array of \String or \CSV::Row.
- #
- # Special options:
- # * Option <tt>:row_sep</tt> defaults to <tt>"\n"</tt> on Ruby 3.0 or later
- # and <tt>$INPUT_RECORD_SEPARATOR</tt> (<tt>$/</tt>) otherwise.:
- # $INPUT_RECORD_SEPARATOR # => "\n"
- # * This method accepts an additional option, <tt>:encoding</tt>, which sets the base
- # Encoding for the output. This method will try to guess your Encoding from
- # the first non-+nil+ field in +row+, if possible, but you may need to use
- # this parameter as a backup plan.
- #
- # For other +options+,
- # see {Options for Generating}[#class-CSV-label-Options+for+Generating].
- #
- # ---
- #
- # Returns the \String generated from an
- # CSV.generate_lines([['foo', '0'], ['bar', '1'], ['baz', '2']]) # => "foo,0\nbar,1\nbaz,2\n"
- #
- # ---
- #
- # Raises an exception
- # # Raises NoMethodError (undefined method `each' for :foo:Symbol)
- # CSV.generate_lines(:foo)
- #
- def generate_lines(rows, **options)
- self.generate(**options) do |csv|
- rows.each do |row|
- csv << row
- end
- end
- end
-
- #
- # :call-seq:
- # open(file_path, mode = "rb", **options ) -> new_csv
- # open(io, mode = "rb", **options ) -> new_csv
- # open(file_path, mode = "rb", **options ) { |csv| ... } -> object
- # open(io, mode = "rb", **options ) { |csv| ... } -> object
- #
- # possible options elements:
- # keyword form:
- # :invalid => nil # raise error on invalid byte sequence (default)
- # :invalid => :replace # replace invalid byte sequence
- # :undef => :replace # replace undefined conversion
- # :replace => string # replacement string ("?" or "\uFFFD" if not specified)
- #
- # * Argument +path+, if given, must be the path to a file.
- # :include: ../doc/csv/arguments/io.rdoc
- # * Argument +mode+, if given, must be a \File mode
- # See {Open Mode}[IO.html#method-c-new-label-Open+Mode].
- # * Arguments <tt>**options</tt> must be keyword options.
- # See {Options for Generating}[#class-CSV-label-Options+for+Generating].
- # * This method optionally accepts an additional <tt>:encoding</tt> option
- # that you can use to specify the Encoding of the data read from +path+ or +io+.
- # You must provide this unless your data is in the encoding
- # given by <tt>Encoding::default_external</tt>.
- # Parsing will use this to determine how to parse the data.
- # You may provide a second Encoding to
- # have the data transcoded as it is read. For example,
- # encoding: 'UTF-32BE:UTF-8'
- # would read +UTF-32BE+ data from the file
- # but transcode it to +UTF-8+ before parsing.
- #
- # ---
- #
- # These examples assume prior execution of:
- # string = "foo,0\nbar,1\nbaz,2\n"
- # path = 't.csv'
- # File.write(path, string)
- #
- # ---
- #
- # With no block given, returns a new \CSV object.
- #
- # Create a \CSV object using a file path:
- # csv = CSV.open(path)
- # csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
- #
- # Create a \CSV object using an open \File:
- # csv = CSV.open(File.open(path))
- # csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
- #
- # ---
- #
- # With a block given, calls the block with the created \CSV object;
- # returns the block's return value:
- #
- # Using a file path:
- # csv = CSV.open(path) {|csv| p csv}
- # csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
- # Output:
- # #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
- #
- # Using an open \File:
- # csv = CSV.open(File.open(path)) {|csv| p csv}
- # csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
- # Output:
- # #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
- #
- # ---
- #
- # Raises an exception if the argument is not a \String object or \IO object:
- # # Raises TypeError (no implicit conversion of Symbol into String)
- # CSV.open(:foo)
- def open(filename, mode="r", **options)
- # wrap a File opened with the remaining +args+ with no newline
- # decorator
- file_opts = options.dup
- unless file_opts.key?(:newline)
- file_opts[:universal_newline] ||= false
- end
- options.delete(:invalid)
- options.delete(:undef)
- options.delete(:replace)
- options.delete_if {|k, _| /newline\z/.match?(k)}
-
- begin
- f = File.open(filename, mode, **file_opts)
- rescue ArgumentError => e
- raise unless /needs binmode/.match?(e.message) and mode == "r"
- mode = "rb"
- file_opts = {encoding: Encoding.default_external}.merge(file_opts)
- retry
- end
- begin
- csv = new(f, **options)
- rescue Exception
- f.close
- raise
- end
-
- # handle blocks like Ruby's open(), not like the CSV library
- if block_given?
- begin
- yield csv
- ensure
- csv.close
- end
- else
- csv
- end
- end
-
- #
- # :call-seq:
- # parse(string) -> array_of_arrays
- # parse(io) -> array_of_arrays
- # parse(string, headers: ..., **options) -> csv_table
- # parse(io, headers: ..., **options) -> csv_table
- # parse(string, **options) {|row| ... }
- # parse(io, **options) {|row| ... }
- #
- # Parses +string+ or +io+ using the specified +options+.
- #
- # - Argument +string+ should be a \String object;
- # it will be put into a new StringIO object positioned at the beginning.
- # :include: ../doc/csv/arguments/io.rdoc
- # - Argument +options+: see {Options for Parsing}[#class-CSV-label-Options+for+Parsing]
- #
- # ====== Without Option +headers+
- #
- # Without {option +headers+}[#class-CSV-label-Option+headers] case.
- #
- # These examples assume prior execution of:
- # string = "foo,0\nbar,1\nbaz,2\n"
- # path = 't.csv'
- # File.write(path, string)
- #
- # ---
- #
- # With no block given, returns an \Array of Arrays formed from the source.
- #
- # Parse a \String:
- # a_of_a = CSV.parse(string)
- # a_of_a # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
- #
- # Parse an open \File:
- # a_of_a = File.open(path) do |file|
- # CSV.parse(file)
- # end
- # a_of_a # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
- #
- # ---
- #
- # With a block given, calls the block with each parsed row:
- #
- # Parse a \String:
- # CSV.parse(string) {|row| p row }
- #
- # Output:
- # ["foo", "0"]
- # ["bar", "1"]
- # ["baz", "2"]
- #
- # Parse an open \File:
- # File.open(path) do |file|
- # CSV.parse(file) {|row| p row }
- # end
- #
- # Output:
- # ["foo", "0"]
- # ["bar", "1"]
- # ["baz", "2"]
- #
- # ====== With Option +headers+
- #
- # With {option +headers+}[#class-CSV-label-Option+headers] case.
- #
- # These examples assume prior execution of:
- # string = "Name,Count\nfoo,0\nbar,1\nbaz,2\n"
- # path = 't.csv'
- # File.write(path, string)
- #
- # ---
- #
- # With no block given, returns a CSV::Table object formed from the source.
- #
- # Parse a \String:
- # csv_table = CSV.parse(string, headers: ['Name', 'Count'])
- # csv_table # => #<CSV::Table mode:col_or_row row_count:5>
- #
- # Parse an open \File:
- # csv_table = File.open(path) do |file|
- # CSV.parse(file, headers: ['Name', 'Count'])
- # end
- # csv_table # => #<CSV::Table mode:col_or_row row_count:4>
- #
- # ---
- #
- # With a block given, calls the block with each parsed row,
- # which has been formed into a CSV::Row object:
- #
- # Parse a \String:
- # CSV.parse(string, headers: ['Name', 'Count']) {|row| p row }
- #
- # Output:
- # # <CSV::Row "Name":"foo" "Count":"0">
- # # <CSV::Row "Name":"bar" "Count":"1">
- # # <CSV::Row "Name":"baz" "Count":"2">
- #
- # Parse an open \File:
- # File.open(path) do |file|
- # CSV.parse(file, headers: ['Name', 'Count']) {|row| p row }
- # end
- #
- # Output:
- # # <CSV::Row "Name":"foo" "Count":"0">
- # # <CSV::Row "Name":"bar" "Count":"1">
- # # <CSV::Row "Name":"baz" "Count":"2">
- #
- # ---
- #
- # Raises an exception if the argument is not a \String object or \IO object:
- # # Raises NoMethodError (undefined method `close' for :foo:Symbol)
- # CSV.parse(:foo)
- def parse(str, **options, &block)
- csv = new(str, **options)
-
- return csv.each(&block) if block_given?
-
- # slurp contents, if no block is given
- begin
- csv.read
- ensure
- csv.close
- end
- end
-
- # :call-seq:
- # CSV.parse_line(string) -> new_array or nil
- # CSV.parse_line(io) -> new_array or nil
- # CSV.parse_line(string, **options) -> new_array or nil
- # CSV.parse_line(io, **options) -> new_array or nil
- # CSV.parse_line(string, headers: true, **options) -> csv_row or nil
- # CSV.parse_line(io, headers: true, **options) -> csv_row or nil
- #
- # Returns the data created by parsing the first line of +string+ or +io+
- # using the specified +options+.
- #
- # - Argument +string+ should be a \String object;
- # it will be put into a new StringIO object positioned at the beginning.
- # :include: ../doc/csv/arguments/io.rdoc
- # - Argument +options+: see {Options for Parsing}[#class-CSV-label-Options+for+Parsing]
- #
- # ====== Without Option +headers+
- #
- # Without option +headers+, returns the first row as a new \Array.
- #
- # These examples assume prior execution of:
- # string = "foo,0\nbar,1\nbaz,2\n"
- # path = 't.csv'
- # File.write(path, string)
- #
- # Parse the first line from a \String object:
- # CSV.parse_line(string) # => ["foo", "0"]
- #
- # Parse the first line from a File object:
- # File.open(path) do |file|
- # CSV.parse_line(file) # => ["foo", "0"]
- # end # => ["foo", "0"]
- #
- # Returns +nil+ if the argument is an empty \String:
- # CSV.parse_line('') # => nil
- #
- # ====== With Option +headers+
- #
- # With {option +headers+}[#class-CSV-label-Option+headers],
- # returns the first row as a CSV::Row object.
- #
- # These examples assume prior execution of:
- # string = "Name,Count\nfoo,0\nbar,1\nbaz,2\n"
- # path = 't.csv'
- # File.write(path, string)
- #
- # Parse the first line from a \String object:
- # CSV.parse_line(string, headers: true) # => #<CSV::Row "Name":"foo" "Count":"0">
- #
- # Parse the first line from a File object:
- # File.open(path) do |file|
- # CSV.parse_line(file, headers: true)
- # end # => #<CSV::Row "Name":"foo" "Count":"0">
- #
- # ---
- #
- # Raises an exception if the argument is +nil+:
- # # Raises ArgumentError (Cannot parse nil as CSV):
- # CSV.parse_line(nil)
- #
- def parse_line(line, **options)
- new(line, **options).each.first
- end
-
- #
- # :call-seq:
- # read(source, **options) -> array_of_arrays
- # read(source, headers: true, **options) -> csv_table
- #
- # Opens the given +source+ with the given +options+ (see CSV.open),
- # reads the source (see CSV#read), and returns the result,
- # which will be either an \Array of Arrays or a CSV::Table.
- #
- # Without headers:
- # string = "foo,0\nbar,1\nbaz,2\n"
- # path = 't.csv'
- # File.write(path, string)
- # CSV.read(path) # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
- #
- # With headers:
- # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # path = 't.csv'
- # File.write(path, string)
- # CSV.read(path, headers: true) # => #<CSV::Table mode:col_or_row row_count:4>
- def read(path, **options)
- open(path, **options) { |csv| csv.read }
- end
-
- # :call-seq:
- # CSV.readlines(source, **options)
- #
- # Alias for CSV.read.
- def readlines(path, **options)
- read(path, **options)
- end
-
- # :call-seq:
- # CSV.table(source, **options)
- #
- # Calls CSV.read with +source+, +options+, and certain default options:
- # - +headers+: +true+
- # - +converters+: +:numeric+
- # - +header_converters+: +:symbol+
- #
- # Returns a CSV::Table object.
- #
- # Example:
- # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # path = 't.csv'
- # File.write(path, string)
- # CSV.table(path) # => #<CSV::Table mode:col_or_row row_count:4>
- def table(path, **options)
- default_options = {
- headers: true,
- converters: :numeric,
- header_converters: :symbol,
- }
- options = default_options.merge(options)
- read(path, **options)
- end
- end
-
- # :call-seq:
- # CSV.new(string)
- # CSV.new(io)
- # CSV.new(string, **options)
- # CSV.new(io, **options)
- #
- # Returns the new \CSV object created using +string+ or +io+
- # and the specified +options+.
- #
- # - Argument +string+ should be a \String object;
- # it will be put into a new StringIO object positioned at the beginning.
- # :include: ../doc/csv/arguments/io.rdoc
- # - Argument +options+: See:
- # * {Options for Parsing}[#class-CSV-label-Options+for+Parsing]
- # * {Options for Generating}[#class-CSV-label-Options+for+Generating]
- # For performance reasons, the options cannot be overridden
- # in a \CSV object, so those specified here will endure.
- #
- # In addition to the \CSV instance methods, several \IO methods are delegated.
- # See {Delegated Methods}[#class-CSV-label-Delegated+Methods].
- #
- # ---
- #
- # Create a \CSV object from a \String object:
- # csv = CSV.new('foo,0')
- # csv # => #<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
- #
- # Create a \CSV object from a \File object:
- # File.write('t.csv', 'foo,0')
- # csv = CSV.new(File.open('t.csv'))
- # csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
- #
- # ---
- #
- # Raises an exception if the argument is +nil+:
- # # Raises ArgumentError (Cannot parse nil as CSV):
- # CSV.new(nil)
- #
- def initialize(data,
- col_sep: ",",
- row_sep: :auto,
- quote_char: '"',
- field_size_limit: nil,
- max_field_size: nil,
- converters: nil,
- unconverted_fields: nil,
- headers: false,
- return_headers: false,
- write_headers: nil,
- header_converters: nil,
- skip_blanks: false,
- force_quotes: false,
- skip_lines: nil,
- liberal_parsing: false,
- internal_encoding: nil,
- external_encoding: nil,
- encoding: nil,
- nil_value: nil,
- empty_value: "",
- strip: false,
- quote_empty: true,
- write_converters: nil,
- write_nil_value: nil,
- write_empty_value: "")
- raise ArgumentError.new("Cannot parse nil as CSV") if data.nil?
-
- if data.is_a?(String)
- if encoding
- if encoding.is_a?(String)
- data_external_encoding, data_internal_encoding = encoding.split(":", 2)
- if data_internal_encoding
- data = data.encode(data_internal_encoding, data_external_encoding)
- else
- data = data.dup.force_encoding(data_external_encoding)
- end
- else
- data = data.dup.force_encoding(encoding)
- end
- end
- @io = StringIO.new(data)
- else
- @io = data
- end
- @encoding = determine_encoding(encoding, internal_encoding)
-
- @base_fields_converter_options = {
- nil_value: nil_value,
- empty_value: empty_value,
- }
- @write_fields_converter_options = {
- nil_value: write_nil_value,
- empty_value: write_empty_value,
- }
- @initial_converters = converters
- @initial_header_converters = header_converters
- @initial_write_converters = write_converters
-
- if max_field_size.nil? and field_size_limit
- max_field_size = field_size_limit - 1
- end
- @parser_options = {
- column_separator: col_sep,
- row_separator: row_sep,
- quote_character: quote_char,
- max_field_size: max_field_size,
- unconverted_fields: unconverted_fields,
- headers: headers,
- return_headers: return_headers,
- skip_blanks: skip_blanks,
- skip_lines: skip_lines,
- liberal_parsing: liberal_parsing,
- encoding: @encoding,
- nil_value: nil_value,
- empty_value: empty_value,
- strip: strip,
- }
- @parser = nil
- @parser_enumerator = nil
- @eof_error = nil
-
- @writer_options = {
- encoding: @encoding,
- force_encoding: (not encoding.nil?),
- force_quotes: force_quotes,
- headers: headers,
- write_headers: write_headers,
- column_separator: col_sep,
- row_separator: row_sep,
- quote_character: quote_char,
- quote_empty: quote_empty,
- }
-
- @writer = nil
- writer if @writer_options[:write_headers]
- end
-
- # :call-seq:
- # csv.col_sep -> string
- #
- # Returns the encoded column separator; used for parsing and writing;
- # see {Option +col_sep+}[#class-CSV-label-Option+col_sep]:
- # CSV.new('').col_sep # => ","
- def col_sep
- parser.column_separator
- end
-
- # :call-seq:
- # csv.row_sep -> string
- #
- # Returns the encoded row separator; used for parsing and writing;
- # see {Option +row_sep+}[#class-CSV-label-Option+row_sep]:
- # CSV.new('').row_sep # => "\n"
- def row_sep
- parser.row_separator
- end
-
- # :call-seq:
- # csv.quote_char -> character
- #
- # Returns the encoded quote character; used for parsing and writing;
- # see {Option +quote_char+}[#class-CSV-label-Option+quote_char]:
- # CSV.new('').quote_char # => "\""
- def quote_char
- parser.quote_character
- end
-
- # :call-seq:
- # csv.field_size_limit -> integer or nil
- #
- # Returns the limit for field size; used for parsing;
- # see {Option +field_size_limit+}[#class-CSV-label-Option+field_size_limit]:
- # CSV.new('').field_size_limit # => nil
- #
- # Deprecated since 3.2.3. Use +max_field_size+ instead.
- def field_size_limit
- parser.field_size_limit
- end
-
- # :call-seq:
- # csv.max_field_size -> integer or nil
- #
- # Returns the limit for field size; used for parsing;
- # see {Option +max_field_size+}[#class-CSV-label-Option+max_field_size]:
- # CSV.new('').max_field_size # => nil
- #
- # Since 3.2.3.
- def max_field_size
- parser.max_field_size
- end
-
- # :call-seq:
- # csv.skip_lines -> regexp or nil
- #
- # Returns the \Regexp used to identify comment lines; used for parsing;
- # see {Option +skip_lines+}[#class-CSV-label-Option+skip_lines]:
- # CSV.new('').skip_lines # => nil
- def skip_lines
- parser.skip_lines
- end
-
- # :call-seq:
- # csv.converters -> array
- #
- # Returns an \Array containing field converters;
- # see {Field Converters}[#class-CSV-label-Field+Converters]:
- # csv = CSV.new('')
- # csv.converters # => []
- # csv.convert(:integer)
- # csv.converters # => [:integer]
- # csv.convert(proc {|x| x.to_s })
- # csv.converters
- #
- # Notes that you need to call
- # +Ractor.make_shareable(CSV::Converters)+ on the main Ractor to use
- # this method.
- def converters
- parser_fields_converter.map do |converter|
- name = Converters.rassoc(converter)
- name ? name.first : converter
- end
- end
-
- # :call-seq:
- # csv.unconverted_fields? -> object
- #
- # Returns the value that determines whether unconverted fields are to be
- # available; used for parsing;
- # see {Option +unconverted_fields+}[#class-CSV-label-Option+unconverted_fields]:
- # CSV.new('').unconverted_fields? # => nil
- def unconverted_fields?
- parser.unconverted_fields?
- end
-
- # :call-seq:
- # csv.headers -> object
- #
- # Returns the value that determines whether headers are used; used for parsing;
- # see {Option +headers+}[#class-CSV-label-Option+headers]:
- # CSV.new('').headers # => nil
- def headers
- if @writer
- @writer.headers
- else
- parsed_headers = parser.headers
- return parsed_headers if parsed_headers
- raw_headers = @parser_options[:headers]
- raw_headers = nil if raw_headers == false
- raw_headers
- end
- end
-
- # :call-seq:
- # csv.return_headers? -> true or false
- #
- # Returns the value that determines whether headers are to be returned; used for parsing;
- # see {Option +return_headers+}[#class-CSV-label-Option+return_headers]:
- # CSV.new('').return_headers? # => false
- def return_headers?
- parser.return_headers?
- end
-
- # :call-seq:
- # csv.write_headers? -> true or false
- #
- # Returns the value that determines whether headers are to be written; used for generating;
- # see {Option +write_headers+}[#class-CSV-label-Option+write_headers]:
- # CSV.new('').write_headers? # => nil
- def write_headers?
- @writer_options[:write_headers]
- end
-
- # :call-seq:
- # csv.header_converters -> array
- #
- # Returns an \Array containing header converters; used for parsing;
- # see {Header Converters}[#class-CSV-label-Header+Converters]:
- # CSV.new('').header_converters # => []
- #
- # Notes that you need to call
- # +Ractor.make_shareable(CSV::HeaderConverters)+ on the main Ractor
- # to use this method.
- def header_converters
- header_fields_converter.map do |converter|
- name = HeaderConverters.rassoc(converter)
- name ? name.first : converter
- end
- end
-
- # :call-seq:
- # csv.skip_blanks? -> true or false
- #
- # Returns the value that determines whether blank lines are to be ignored; used for parsing;
- # see {Option +skip_blanks+}[#class-CSV-label-Option+skip_blanks]:
- # CSV.new('').skip_blanks? # => false
- def skip_blanks?
- parser.skip_blanks?
- end
-
- # :call-seq:
- # csv.force_quotes? -> true or false
- #
- # Returns the value that determines whether all output fields are to be quoted;
- # used for generating;
- # see {Option +force_quotes+}[#class-CSV-label-Option+force_quotes]:
- # CSV.new('').force_quotes? # => false
- def force_quotes?
- @writer_options[:force_quotes]
- end
-
- # :call-seq:
- # csv.liberal_parsing? -> true or false
- #
- # Returns the value that determines whether illegal input is to be handled; used for parsing;
- # see {Option +liberal_parsing+}[#class-CSV-label-Option+liberal_parsing]:
- # CSV.new('').liberal_parsing? # => false
- def liberal_parsing?
- parser.liberal_parsing?
- end
-
- # :call-seq:
- # csv.encoding -> encoding
- #
- # Returns the encoding used for parsing and generating;
- # see {Character Encodings (M17n or Multilingualization)}[#class-CSV-label-Character+Encodings+-28M17n+or+Multilingualization-29]:
- # CSV.new('').encoding # => #<Encoding:UTF-8>
- attr_reader :encoding
-
- # :call-seq:
- # csv.line_no -> integer
- #
- # Returns the count of the rows parsed or generated.
- #
- # Parsing:
- # string = "foo,0\nbar,1\nbaz,2\n"
- # path = 't.csv'
- # File.write(path, string)
- # CSV.open(path) do |csv|
- # csv.each do |row|
- # p [csv.lineno, row]
- # end
- # end
- # Output:
- # [1, ["foo", "0"]]
- # [2, ["bar", "1"]]
- # [3, ["baz", "2"]]
- #
- # Generating:
- # CSV.generate do |csv|
- # p csv.lineno; csv << ['foo', 0]
- # p csv.lineno; csv << ['bar', 1]
- # p csv.lineno; csv << ['baz', 2]
- # end
- # Output:
- # 0
- # 1
- # 2
- def lineno
- if @writer
- @writer.lineno
- else
- parser.lineno
- end
- end
-
- # :call-seq:
- # csv.line -> array
- #
- # Returns the line most recently read:
- # string = "foo,0\nbar,1\nbaz,2\n"
- # path = 't.csv'
- # File.write(path, string)
- # CSV.open(path) do |csv|
- # csv.each do |row|
- # p [csv.lineno, csv.line]
- # end
- # end
- # Output:
- # [1, "foo,0\n"]
- # [2, "bar,1\n"]
- # [3, "baz,2\n"]
- def line
- parser.line
- end
-
- ### IO and StringIO Delegation ###
-
- extend Forwardable
- def_delegators :@io, :binmode, :close, :close_read, :close_write,
- :closed?, :external_encoding, :fcntl,
- :fileno, :flush, :fsync, :internal_encoding,
- :isatty, :pid, :pos, :pos=, :reopen,
- :seek, :string, :sync, :sync=, :tell,
- :truncate, :tty?
-
- def binmode?
- if @io.respond_to?(:binmode?)
- @io.binmode?
- else
- false
- end
- end
-
- def flock(*args)
- raise NotImplementedError unless @io.respond_to?(:flock)
- @io.flock(*args)
- end
-
- def ioctl(*args)
- raise NotImplementedError unless @io.respond_to?(:ioctl)
- @io.ioctl(*args)
- end
-
- def path
- @io.path if @io.respond_to?(:path)
- end
-
- def stat(*args)
- raise NotImplementedError unless @io.respond_to?(:stat)
- @io.stat(*args)
- end
-
- def to_i
- raise NotImplementedError unless @io.respond_to?(:to_i)
- @io.to_i
- end
-
- def to_io
- @io.respond_to?(:to_io) ? @io.to_io : @io
- end
-
- def eof?
- return false if @eof_error
- begin
- parser_enumerator.peek
- false
- rescue MalformedCSVError => error
- @eof_error = error
- false
- rescue StopIteration
- true
- end
- end
- alias_method :eof, :eof?
-
- # Rewinds the underlying IO object and resets CSV's lineno() counter.
- def rewind
- @parser = nil
- @parser_enumerator = nil
- @eof_error = nil
- @writer.rewind if @writer
- @io.rewind
- end
-
- ### End Delegation ###
-
- # :call-seq:
- # csv << row -> self
- #
- # Appends a row to +self+.
- #
- # - Argument +row+ must be an \Array object or a CSV::Row object.
- # - The output stream must be open for writing.
- #
- # ---
- #
- # Append Arrays:
- # CSV.generate do |csv|
- # csv << ['foo', 0]
- # csv << ['bar', 1]
- # csv << ['baz', 2]
- # end # => "foo,0\nbar,1\nbaz,2\n"
- #
- # Append CSV::Rows:
- # headers = []
- # CSV.generate do |csv|
- # csv << CSV::Row.new(headers, ['foo', 0])
- # csv << CSV::Row.new(headers, ['bar', 1])
- # csv << CSV::Row.new(headers, ['baz', 2])
- # end # => "foo,0\nbar,1\nbaz,2\n"
- #
- # Headers in CSV::Row objects are not appended:
- # headers = ['Name', 'Count']
- # CSV.generate do |csv|
- # csv << CSV::Row.new(headers, ['foo', 0])
- # csv << CSV::Row.new(headers, ['bar', 1])
- # csv << CSV::Row.new(headers, ['baz', 2])
- # end # => "foo,0\nbar,1\nbaz,2\n"
- #
- # ---
- #
- # Raises an exception if +row+ is not an \Array or \CSV::Row:
- # CSV.generate do |csv|
- # # Raises NoMethodError (undefined method `collect' for :foo:Symbol)
- # csv << :foo
- # end
- #
- # Raises an exception if the output stream is not opened for writing:
- # path = 't.csv'
- # File.write(path, '')
- # File.open(path) do |file|
- # CSV.open(file) do |csv|
- # # Raises IOError (not opened for writing)
- # csv << ['foo', 0]
- # end
- # end
- def <<(row)
- writer << row
- self
- end
- alias_method :add_row, :<<
- alias_method :puts, :<<
-
- # :call-seq:
- # convert(converter_name) -> array_of_procs
- # convert {|field, field_info| ... } -> array_of_procs
- #
- # - With no block, installs a field converter (a \Proc).
- # - With a block, defines and installs a custom field converter.
- # - Returns the \Array of installed field converters.
- #
- # - Argument +converter_name+, if given, should be the name
- # of an existing field converter.
- #
- # See {Field Converters}[#class-CSV-label-Field+Converters].
- # ---
- #
- # With no block, installs a field converter:
- # csv = CSV.new('')
- # csv.convert(:integer)
- # csv.convert(:float)
- # csv.convert(:date)
- # csv.converters # => [:integer, :float, :date]
- #
- # ---
- #
- # The block, if given, is called for each field:
- # - Argument +field+ is the field value.
- # - Argument +field_info+ is a CSV::FieldInfo object
- # containing details about the field.
- #
- # The examples here assume the prior execution of:
- # string = "foo,0\nbar,1\nbaz,2\n"
- # path = 't.csv'
- # File.write(path, string)
- #
- # Example giving a block:
- # csv = CSV.open(path)
- # csv.convert {|field, field_info| p [field, field_info]; field.upcase }
- # csv.read # => [["FOO", "0"], ["BAR", "1"], ["BAZ", "2"]]
- #
- # Output:
- # ["foo", #<struct CSV::FieldInfo index=0, line=1, header=nil>]
- # ["0", #<struct CSV::FieldInfo index=1, line=1, header=nil>]
- # ["bar", #<struct CSV::FieldInfo index=0, line=2, header=nil>]
- # ["1", #<struct CSV::FieldInfo index=1, line=2, header=nil>]
- # ["baz", #<struct CSV::FieldInfo index=0, line=3, header=nil>]
- # ["2", #<struct CSV::FieldInfo index=1, line=3, header=nil>]
- #
- # The block need not return a \String object:
- # csv = CSV.open(path)
- # csv.convert {|field, field_info| field.to_sym }
- # csv.read # => [[:foo, :"0"], [:bar, :"1"], [:baz, :"2"]]
- #
- # If +converter_name+ is given, the block is not called:
- # csv = CSV.open(path)
- # csv.convert(:integer) {|field, field_info| fail 'Cannot happen' }
- # csv.read # => [["foo", 0], ["bar", 1], ["baz", 2]]
- #
- # ---
- #
- # Raises a parse-time exception if +converter_name+ is not the name of a built-in
- # field converter:
- # csv = CSV.open(path)
- # csv.convert(:nosuch) => [nil]
- # # Raises NoMethodError (undefined method `arity' for nil:NilClass)
- # csv.read
- def convert(name = nil, &converter)
- parser_fields_converter.add_converter(name, &converter)
- end
-
- # :call-seq:
- # header_convert(converter_name) -> array_of_procs
- # header_convert {|header, field_info| ... } -> array_of_procs
- #
- # - With no block, installs a header converter (a \Proc).
- # - With a block, defines and installs a custom header converter.
- # - Returns the \Array of installed header converters.
- #
- # - Argument +converter_name+, if given, should be the name
- # of an existing header converter.
- #
- # See {Header Converters}[#class-CSV-label-Header+Converters].
- # ---
- #
- # With no block, installs a header converter:
- # csv = CSV.new('')
- # csv.header_convert(:symbol)
- # csv.header_convert(:downcase)
- # csv.header_converters # => [:symbol, :downcase]
- #
- # ---
- #
- # The block, if given, is called for each header:
- # - Argument +header+ is the header value.
- # - Argument +field_info+ is a CSV::FieldInfo object
- # containing details about the header.
- #
- # The examples here assume the prior execution of:
- # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # path = 't.csv'
- # File.write(path, string)
- #
- # Example giving a block:
- # csv = CSV.open(path, headers: true)
- # csv.header_convert {|header, field_info| p [header, field_info]; header.upcase }
- # table = csv.read
- # table # => #<CSV::Table mode:col_or_row row_count:4>
- # table.headers # => ["NAME", "VALUE"]
- #
- # Output:
- # ["Name", #<struct CSV::FieldInfo index=0, line=1, header=nil>]
- # ["Value", #<struct CSV::FieldInfo index=1, line=1, header=nil>]
-
- # The block need not return a \String object:
- # csv = CSV.open(path, headers: true)
- # csv.header_convert {|header, field_info| header.to_sym }
- # table = csv.read
- # table.headers # => [:Name, :Value]
- #
- # If +converter_name+ is given, the block is not called:
- # csv = CSV.open(path, headers: true)
- # csv.header_convert(:downcase) {|header, field_info| fail 'Cannot happen' }
- # table = csv.read
- # table.headers # => ["name", "value"]
- # ---
- #
- # Raises a parse-time exception if +converter_name+ is not the name of a built-in
- # field converter:
- # csv = CSV.open(path, headers: true)
- # csv.header_convert(:nosuch)
- # # Raises NoMethodError (undefined method `arity' for nil:NilClass)
- # csv.read
- def header_convert(name = nil, &converter)
- header_fields_converter.add_converter(name, &converter)
- end
-
- include Enumerable
-
- # :call-seq:
- # csv.each -> enumerator
- # csv.each {|row| ...}
- #
- # Calls the block with each successive row.
- # The data source must be opened for reading.
- #
- # Without headers:
- # string = "foo,0\nbar,1\nbaz,2\n"
- # csv = CSV.new(string)
- # csv.each do |row|
- # p row
- # end
- # Output:
- # ["foo", "0"]
- # ["bar", "1"]
- # ["baz", "2"]
- #
- # With headers:
- # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # csv = CSV.new(string, headers: true)
- # csv.each do |row|
- # p row
- # end
- # Output:
- # <CSV::Row "Name":"foo" "Value":"0">
- # <CSV::Row "Name":"bar" "Value":"1">
- # <CSV::Row "Name":"baz" "Value":"2">
- #
- # ---
- #
- # Raises an exception if the source is not opened for reading:
- # string = "foo,0\nbar,1\nbaz,2\n"
- # csv = CSV.new(string)
- # csv.close
- # # Raises IOError (not opened for reading)
- # csv.each do |row|
- # p row
- # end
- def each(&block)
- return to_enum(__method__) unless block_given?
- begin
- while true
- yield(parser_enumerator.next)
- end
- rescue StopIteration
- end
- end
-
- # :call-seq:
- # csv.read -> array or csv_table
- #
- # Forms the remaining rows from +self+ into:
- # - A CSV::Table object, if headers are in use.
- # - An \Array of Arrays, otherwise.
- #
- # The data source must be opened for reading.
- #
- # Without headers:
- # string = "foo,0\nbar,1\nbaz,2\n"
- # path = 't.csv'
- # File.write(path, string)
- # csv = CSV.open(path)
- # csv.read # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
- #
- # With headers:
- # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # path = 't.csv'
- # File.write(path, string)
- # csv = CSV.open(path, headers: true)
- # csv.read # => #<CSV::Table mode:col_or_row row_count:4>
- #
- # ---
- #
- # Raises an exception if the source is not opened for reading:
- # string = "foo,0\nbar,1\nbaz,2\n"
- # csv = CSV.new(string)
- # csv.close
- # # Raises IOError (not opened for reading)
- # csv.read
- def read
- rows = to_a
- if parser.use_headers?
- Table.new(rows, headers: parser.headers)
- else
- rows
- end
- end
- alias_method :readlines, :read
-
- # :call-seq:
- # csv.header_row? -> true or false
- #
- # Returns +true+ if the next row to be read is a header row\;
- # +false+ otherwise.
- #
- # Without headers:
- # string = "foo,0\nbar,1\nbaz,2\n"
- # csv = CSV.new(string)
- # csv.header_row? # => false
- #
- # With headers:
- # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # csv = CSV.new(string, headers: true)
- # csv.header_row? # => true
- # csv.shift # => #<CSV::Row "Name":"foo" "Value":"0">
- # csv.header_row? # => false
- #
- # ---
- #
- # Raises an exception if the source is not opened for reading:
- # string = "foo,0\nbar,1\nbaz,2\n"
- # csv = CSV.new(string)
- # csv.close
- # # Raises IOError (not opened for reading)
- # csv.header_row?
- def header_row?
- parser.header_row?
- end
-
- # :call-seq:
- # csv.shift -> array, csv_row, or nil
- #
- # Returns the next row of data as:
- # - An \Array if no headers are used.
- # - A CSV::Row object if headers are used.
- #
- # The data source must be opened for reading.
- #
- # Without headers:
- # string = "foo,0\nbar,1\nbaz,2\n"
- # csv = CSV.new(string)
- # csv.shift # => ["foo", "0"]
- # csv.shift # => ["bar", "1"]
- # csv.shift # => ["baz", "2"]
- # csv.shift # => nil
- #
- # With headers:
- # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # csv = CSV.new(string, headers: true)
- # csv.shift # => #<CSV::Row "Name":"foo" "Value":"0">
- # csv.shift # => #<CSV::Row "Name":"bar" "Value":"1">
- # csv.shift # => #<CSV::Row "Name":"baz" "Value":"2">
- # csv.shift # => nil
- #
- # ---
- #
- # Raises an exception if the source is not opened for reading:
- # string = "foo,0\nbar,1\nbaz,2\n"
- # csv = CSV.new(string)
- # csv.close
- # # Raises IOError (not opened for reading)
- # csv.shift
- def shift
- if @eof_error
- eof_error, @eof_error = @eof_error, nil
- raise eof_error
- end
- begin
- parser_enumerator.next
- rescue StopIteration
- nil
- end
- end
- alias_method :gets, :shift
- alias_method :readline, :shift
-
- # :call-seq:
- # csv.inspect -> string
- #
- # Returns a \String showing certain properties of +self+:
- # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # csv = CSV.new(string, headers: true)
- # s = csv.inspect
- # s # => "#<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:\",\" row_sep:\"\\n\" quote_char:\"\\\"\" headers:true>"
- def inspect
- str = ["#<", self.class.to_s, " io_type:"]
- # show type of wrapped IO
- if @io == $stdout then str << "$stdout"
- elsif @io == $stdin then str << "$stdin"
- elsif @io == $stderr then str << "$stderr"
- else str << @io.class.to_s
- end
- # show IO.path(), if available
- if @io.respond_to?(:path) and (p = @io.path)
- str << " io_path:" << p.inspect
- end
- # show encoding
- str << " encoding:" << @encoding.name
- # show other attributes
- ["lineno", "col_sep", "row_sep", "quote_char"].each do |attr_name|
- if a = __send__(attr_name)
- str << " " << attr_name << ":" << a.inspect
- end
- end
- ["skip_blanks", "liberal_parsing"].each do |attr_name|
- if a = __send__("#{attr_name}?")
- str << " " << attr_name << ":" << a.inspect
- end
- end
- _headers = headers
- str << " headers:" << _headers.inspect if _headers
- str << ">"
- begin
- str.join('')
- rescue # any encoding error
- str.map do |s|
- e = Encoding::Converter.asciicompat_encoding(s.encoding)
- e ? s.encode(e) : s.force_encoding("ASCII-8BIT")
- end.join('')
- end
- end
-
- private
-
- def determine_encoding(encoding, internal_encoding)
- # honor the IO encoding if we can, otherwise default to ASCII-8BIT
- io_encoding = raw_encoding
- return io_encoding if io_encoding
-
- return Encoding.find(internal_encoding) if internal_encoding
-
- if encoding
- encoding, = encoding.split(":", 2) if encoding.is_a?(String)
- return Encoding.find(encoding)
- end
-
- Encoding.default_internal || Encoding.default_external
- end
-
- def normalize_converters(converters)
- converters ||= []
- unless converters.is_a?(Array)
- converters = [converters]
- end
- converters.collect do |converter|
- case converter
- when Proc # custom code block
- [nil, converter]
- else # by name
- [converter, nil]
- end
- end
- end
-
- #
- # Processes +fields+ with <tt>@converters</tt>, or <tt>@header_converters</tt>
- # if +headers+ is passed as +true+, returning the converted field set. Any
- # converter that changes the field into something other than a String halts
- # the pipeline of conversion for that field. This is primarily an efficiency
- # shortcut.
- #
- def convert_fields(fields, headers = false)
- if headers
- header_fields_converter.convert(fields, nil, 0)
- else
- parser_fields_converter.convert(fields, @headers, lineno)
- end
- end
-
- #
- # Returns the encoding of the internal IO object.
- #
- def raw_encoding
- if @io.respond_to? :internal_encoding
- @io.internal_encoding || @io.external_encoding
- elsif @io.respond_to? :encoding
- @io.encoding
- else
- nil
- end
- end
-
- def parser_fields_converter
- @parser_fields_converter ||= build_parser_fields_converter
- end
-
- def build_parser_fields_converter
- specific_options = {
- builtin_converters_name: :Converters,
- }
- options = @base_fields_converter_options.merge(specific_options)
- build_fields_converter(@initial_converters, options)
- end
-
- def header_fields_converter
- @header_fields_converter ||= build_header_fields_converter
- end
-
- def build_header_fields_converter
- specific_options = {
- builtin_converters_name: :HeaderConverters,
- accept_nil: true,
- }
- options = @base_fields_converter_options.merge(specific_options)
- build_fields_converter(@initial_header_converters, options)
- end
-
- def writer_fields_converter
- @writer_fields_converter ||= build_writer_fields_converter
- end
-
- def build_writer_fields_converter
- build_fields_converter(@initial_write_converters,
- @write_fields_converter_options)
- end
-
- def build_fields_converter(initial_converters, options)
- fields_converter = FieldsConverter.new(options)
- normalize_converters(initial_converters).each do |name, converter|
- fields_converter.add_converter(name, &converter)
- end
- fields_converter
- end
-
- def parser
- @parser ||= Parser.new(@io, parser_options)
- end
-
- def parser_options
- @parser_options.merge(header_fields_converter: header_fields_converter,
- fields_converter: parser_fields_converter)
- end
-
- def parser_enumerator
- @parser_enumerator ||= parser.parse
- end
-
- def writer
- @writer ||= Writer.new(@io, writer_options)
- end
-
- def writer_options
- @writer_options.merge(header_fields_converter: header_fields_converter,
- fields_converter: writer_fields_converter)
- end
-end
-
-# Passes +args+ to CSV::instance.
-#
-# CSV("CSV,data").read
-# #=> [["CSV", "data"]]
-#
-# If a block is given, the instance is passed the block and the return value
-# becomes the return value of the block.
-#
-# CSV("CSV,data") { |c|
-# c.read.any? { |a| a.include?("data") }
-# } #=> true
-#
-# CSV("CSV,data") { |c|
-# c.read.any? { |a| a.include?("zombies") }
-# } #=> false
-#
-# CSV options may also be given.
-#
-# io = StringIO.new
-# CSV(io, col_sep: ";") { |csv| csv << ["a", "b", "c"] }
-#
-# This API is not Ractor-safe.
-#
-def CSV(*args, **options, &block)
- CSV.instance(*args, **options, &block)
-end
-
-require_relative "csv/version"
-require_relative "csv/core_ext/array"
-require_relative "csv/core_ext/string"
diff --git a/lib/csv/core_ext/array.rb b/lib/csv/core_ext/array.rb
deleted file mode 100644
index 8beb06b082..0000000000
--- a/lib/csv/core_ext/array.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class Array # :nodoc:
- # Equivalent to CSV::generate_line(self, options)
- #
- # ["CSV", "data"].to_csv
- # #=> "CSV,data\n"
- def to_csv(**options)
- CSV.generate_line(self, **options)
- end
-end
diff --git a/lib/csv/core_ext/string.rb b/lib/csv/core_ext/string.rb
deleted file mode 100644
index 9b1d31c2a4..0000000000
--- a/lib/csv/core_ext/string.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class String # :nodoc:
- # Equivalent to CSV::parse_line(self, options)
- #
- # "CSV,data".parse_csv
- # #=> ["CSV", "data"]
- def parse_csv(**options)
- CSV.parse_line(self, **options)
- end
-end
diff --git a/lib/csv/csv.gemspec b/lib/csv/csv.gemspec
deleted file mode 100644
index 11c5b0f2a6..0000000000
--- a/lib/csv/csv.gemspec
+++ /dev/null
@@ -1,64 +0,0 @@
-# frozen_string_literal: true
-
-begin
- require_relative "lib/csv/version"
-rescue LoadError
- # for Ruby core repository
- require_relative "version"
-end
-
-Gem::Specification.new do |spec|
- spec.name = "csv"
- spec.version = CSV::VERSION
- spec.authors = ["James Edward Gray II", "Kouhei Sutou"]
- spec.email = [nil, "kou@cozmixng.org"]
-
- spec.summary = "CSV Reading and Writing"
- spec.description = "The CSV library provides a complete interface to CSV files and data. It offers tools to enable you to read and write to and from Strings or IO objects, as needed."
- spec.homepage = "https://github.com/ruby/csv"
- spec.licenses = ["Ruby", "BSD-2-Clause"]
-
- lib_path = "lib"
- spec.require_paths = [lib_path]
- files = []
- lib_dir = File.join(__dir__, lib_path)
- if File.exist?(lib_dir)
- Dir.chdir(lib_dir) do
- Dir.glob("**/*.rb").each do |file|
- files << "lib/#{file}"
- end
- end
- end
- doc_dir = File.join(__dir__, "doc")
- if File.exist?(doc_dir)
- Dir.chdir(doc_dir) do
- Dir.glob("**/*.rdoc").each do |rdoc_file|
- files << "doc/#{rdoc_file}"
- end
- end
- end
- spec.files = files
- spec.rdoc_options.concat(["--main", "README.md"])
- rdoc_files = [
- "LICENSE.txt",
- "NEWS.md",
- "README.md",
- ]
- recipes_dir = File.join(doc_dir, "csv", "recipes")
- if File.exist?(recipes_dir)
- Dir.chdir(recipes_dir) do
- Dir.glob("**/*.rdoc").each do |recipe_file|
- rdoc_files << "doc/csv/recipes/#{recipe_file}"
- end
- end
- end
- spec.extra_rdoc_files = rdoc_files
-
- spec.required_ruby_version = ">= 2.5.0"
-
- # spec.add_dependency "stringio", ">= 0.1.3"
- spec.add_development_dependency "bundler"
- spec.add_development_dependency "rake"
- spec.add_development_dependency "benchmark_driver"
- spec.add_development_dependency "test-unit", ">= 3.4.8"
-end
diff --git a/lib/csv/fields_converter.rb b/lib/csv/fields_converter.rb
deleted file mode 100644
index d15977d379..0000000000
--- a/lib/csv/fields_converter.rb
+++ /dev/null
@@ -1,89 +0,0 @@
-# frozen_string_literal: true
-
-class CSV
- # Note: Don't use this class directly. This is an internal class.
- class FieldsConverter
- include Enumerable
- #
- # A CSV::FieldsConverter is a data structure for storing the
- # fields converter properties to be passed as a parameter
- # when parsing a new file (e.g. CSV::Parser.new(@io, parser_options))
- #
-
- def initialize(options={})
- @converters = []
- @nil_value = options[:nil_value]
- @empty_value = options[:empty_value]
- @empty_value_is_empty_string = (@empty_value == "")
- @accept_nil = options[:accept_nil]
- @builtin_converters_name = options[:builtin_converters_name]
- @need_static_convert = need_static_convert?
- end
-
- def add_converter(name=nil, &converter)
- if name.nil? # custom converter
- @converters << converter
- else # named converter
- combo = builtin_converters[name]
- case combo
- when Array # combo converter
- combo.each do |sub_name|
- add_converter(sub_name)
- end
- else # individual named converter
- @converters << combo
- end
- end
- end
-
- def each(&block)
- @converters.each(&block)
- end
-
- def empty?
- @converters.empty?
- end
-
- def convert(fields, headers, lineno, quoted_fields)
- return fields unless need_convert?
-
- fields.collect.with_index do |field, index|
- if field.nil?
- field = @nil_value
- elsif field.is_a?(String) and field.empty?
- field = @empty_value unless @empty_value_is_empty_string
- end
- @converters.each do |converter|
- break if field.nil? and @accept_nil
- if converter.arity == 1 # straight field converter
- field = converter[field]
- else # FieldInfo converter
- if headers
- header = headers[index]
- else
- header = nil
- end
- quoted = quoted_fields[index]
- field = converter[field, FieldInfo.new(index, lineno, header, quoted)]
- end
- break unless field.is_a?(String) # short-circuit pipeline for speed
- end
- field # final state of each field, converted or original
- end
- end
-
- private
- def need_static_convert?
- not (@nil_value.nil? and @empty_value_is_empty_string)
- end
-
- def need_convert?
- @need_static_convert or
- (not @converters.empty?)
- end
-
- def builtin_converters
- @builtin_converters ||= ::CSV.const_get(@builtin_converters_name)
- end
- end
-end
diff --git a/lib/csv/input_record_separator.rb b/lib/csv/input_record_separator.rb
deleted file mode 100644
index 7a99343c0c..0000000000
--- a/lib/csv/input_record_separator.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-require "English"
-require "stringio"
-
-class CSV
- module InputRecordSeparator
- class << self
- if RUBY_VERSION >= "3.0.0"
- def value
- "\n"
- end
- else
- def value
- $INPUT_RECORD_SEPARATOR
- end
- end
- end
- end
-end
diff --git a/lib/csv/parser.rb b/lib/csv/parser.rb
deleted file mode 100644
index 1f8b150e19..0000000000
--- a/lib/csv/parser.rb
+++ /dev/null
@@ -1,1288 +0,0 @@
-# frozen_string_literal: true
-
-require "strscan"
-
-require_relative "input_record_separator"
-require_relative "row"
-require_relative "table"
-
-class CSV
- # Note: Don't use this class directly. This is an internal class.
- class Parser
- #
- # A CSV::Parser is m17n aware. The parser works in the Encoding of the IO
- # or String object being read from or written to. Your data is never transcoded
- # (unless you ask Ruby to transcode it for you) and will literally be parsed in
- # the Encoding it is in. Thus CSV will return Arrays or Rows of Strings in the
- # Encoding of your data. This is accomplished by transcoding the parser itself
- # into your Encoding.
- #
-
- # Raised when encoding is invalid.
- class InvalidEncoding < StandardError
- end
-
- # Raised when unexpected case is happen.
- class UnexpectedError < StandardError
- end
-
- #
- # CSV::Scanner receives a CSV output, scans it and return the content.
- # It also controls the life cycle of the object with its methods +keep_start+,
- # +keep_end+, +keep_back+, +keep_drop+.
- #
- # Uses StringScanner (the official strscan gem). Strscan provides lexical
- # scanning operations on a String. We inherit its object and take advantage
- # on the methods. For more information, please visit:
- # https://ruby-doc.org/stdlib-2.6.1/libdoc/strscan/rdoc/StringScanner.html
- #
- class Scanner < StringScanner
- alias_method :scan_all, :scan
-
- def initialize(*args)
- super
- @keeps = []
- end
-
- def each_line(row_separator)
- position = pos
- rest.each_line(row_separator) do |line|
- position += line.bytesize
- self.pos = position
- yield(line)
- end
- end
-
- def keep_start
- @keeps.push(pos)
- end
-
- def keep_end
- start = @keeps.pop
- string.byteslice(start, pos - start)
- end
-
- def keep_back
- self.pos = @keeps.pop
- end
-
- def keep_drop
- @keeps.pop
- end
- end
-
- #
- # CSV::InputsScanner receives IO inputs, encoding and the chunk_size.
- # It also controls the life cycle of the object with its methods +keep_start+,
- # +keep_end+, +keep_back+, +keep_drop+.
- #
- # CSV::InputsScanner.scan() tries to match with pattern at the current position.
- # If there's a match, the scanner advances the "scan pointer" and returns the matched string.
- # Otherwise, the scanner returns nil.
- #
- # CSV::InputsScanner.rest() returns the "rest" of the string (i.e. everything after the scan pointer).
- # If there is no more data (eos? = true), it returns "".
- #
- class InputsScanner
- def initialize(inputs, encoding, row_separator, chunk_size: 8192)
- @inputs = inputs.dup
- @encoding = encoding
- @row_separator = row_separator
- @chunk_size = chunk_size
- @last_scanner = @inputs.empty?
- @keeps = []
- read_chunk
- end
-
- def each_line(row_separator)
- return enum_for(__method__, row_separator) unless block_given?
- buffer = nil
- input = @scanner.rest
- position = @scanner.pos
- offset = 0
- n_row_separator_chars = row_separator.size
- # trace(__method__, :start, line, input)
- while true
- input.each_line(row_separator) do |line|
- @scanner.pos += line.bytesize
- if buffer
- if n_row_separator_chars == 2 and
- buffer.end_with?(row_separator[0]) and
- line.start_with?(row_separator[1])
- buffer << line[0]
- line = line[1..-1]
- position += buffer.bytesize + offset
- @scanner.pos = position
- offset = 0
- yield(buffer)
- buffer = nil
- next if line.empty?
- else
- buffer << line
- line = buffer
- buffer = nil
- end
- end
- if line.end_with?(row_separator)
- position += line.bytesize + offset
- @scanner.pos = position
- offset = 0
- yield(line)
- else
- buffer = line
- end
- end
- break unless read_chunk
- input = @scanner.rest
- position = @scanner.pos
- offset = -buffer.bytesize if buffer
- end
- yield(buffer) if buffer
- end
-
- def scan(pattern)
- # trace(__method__, pattern, :start)
- value = @scanner.scan(pattern)
- # trace(__method__, pattern, :done, :last, value) if @last_scanner
- return value if @last_scanner
-
- read_chunk if value and @scanner.eos?
- # trace(__method__, pattern, :done, value)
- value
- end
-
- def scan_all(pattern)
- # trace(__method__, pattern, :start)
- value = @scanner.scan(pattern)
- # trace(__method__, pattern, :done, :last, value) if @last_scanner
- return value if @last_scanner
-
- return nil if value.nil?
- while @scanner.eos? and read_chunk and (sub_value = @scanner.scan(pattern))
- # trace(__method__, pattern, :sub, sub_value)
- value << sub_value
- end
- # trace(__method__, pattern, :done, value)
- value
- end
-
- def eos?
- @scanner.eos?
- end
-
- def keep_start
- # trace(__method__, :start)
- adjust_last_keep
- @keeps.push([@scanner, @scanner.pos, nil])
- # trace(__method__, :done)
- end
-
- def keep_end
- # trace(__method__, :start)
- scanner, start, buffer = @keeps.pop
- if scanner == @scanner
- keep = @scanner.string.byteslice(start, @scanner.pos - start)
- else
- keep = @scanner.string.byteslice(0, @scanner.pos)
- end
- if buffer
- buffer << keep
- keep = buffer
- end
- # trace(__method__, :done, keep)
- keep
- end
-
- def keep_back
- # trace(__method__, :start)
- scanner, start, buffer = @keeps.pop
- if buffer
- # trace(__method__, :rescan, start, buffer)
- string = @scanner.string
- if scanner == @scanner
- keep = string.byteslice(start, string.bytesize - start)
- else
- keep = string
- end
- if keep and not keep.empty?
- @inputs.unshift(StringIO.new(keep))
- @last_scanner = false
- end
- @scanner = StringScanner.new(buffer)
- else
- if @scanner != scanner
- message = "scanners are different but no buffer: "
- message += "#{@scanner.inspect}(#{@scanner.object_id}): "
- message += "#{scanner.inspect}(#{scanner.object_id})"
- raise UnexpectedError, message
- end
- # trace(__method__, :repos, start, buffer)
- @scanner.pos = start
- end
- read_chunk if @scanner.eos?
- end
-
- def keep_drop
- _, _, buffer = @keeps.pop
- # trace(__method__, :done, :empty) unless buffer
- return unless buffer
-
- last_keep = @keeps.last
- # trace(__method__, :done, :no_last_keep) unless last_keep
- return unless last_keep
-
- if last_keep[2]
- last_keep[2] << buffer
- else
- last_keep[2] = buffer
- end
- # trace(__method__, :done)
- end
-
- def rest
- @scanner.rest
- end
-
- def check(pattern)
- @scanner.check(pattern)
- end
-
- private
- def trace(*args)
- pp([*args, @scanner, @scanner&.string, @scanner&.pos, @keeps])
- end
-
- def adjust_last_keep
- # trace(__method__, :start)
-
- keep = @keeps.last
- # trace(__method__, :done, :empty) if keep.nil?
- return if keep.nil?
-
- scanner, start, buffer = keep
- string = @scanner.string
- if @scanner != scanner
- start = 0
- end
- if start == 0 and @scanner.eos?
- keep_data = string
- else
- keep_data = string.byteslice(start, @scanner.pos - start)
- end
- if keep_data
- if buffer
- buffer << keep_data
- else
- keep[2] = keep_data.dup
- end
- end
-
- # trace(__method__, :done)
- end
-
- def read_chunk
- return false if @last_scanner
-
- adjust_last_keep
-
- input = @inputs.first
- case input
- when StringIO
- string = input.read
- raise InvalidEncoding unless string.valid_encoding?
- # trace(__method__, :stringio, string)
- @scanner = StringScanner.new(string)
- @inputs.shift
- @last_scanner = @inputs.empty?
- true
- else
- chunk = input.gets(@row_separator, @chunk_size)
- if chunk
- raise InvalidEncoding unless chunk.valid_encoding?
- # trace(__method__, :chunk, chunk)
- @scanner = StringScanner.new(chunk)
- if input.respond_to?(:eof?) and input.eof?
- @inputs.shift
- @last_scanner = @inputs.empty?
- end
- true
- else
- # trace(__method__, :no_chunk)
- @scanner = StringScanner.new("".encode(@encoding))
- @inputs.shift
- @last_scanner = @inputs.empty?
- if @last_scanner
- false
- else
- read_chunk
- end
- end
- end
- end
- end
-
- def initialize(input, options)
- @input = input
- @options = options
- @samples = []
-
- prepare
- end
-
- def column_separator
- @column_separator
- end
-
- def row_separator
- @row_separator
- end
-
- def quote_character
- @quote_character
- end
-
- def field_size_limit
- @max_field_size&.succ
- end
-
- def max_field_size
- @max_field_size
- end
-
- def skip_lines
- @skip_lines
- end
-
- def unconverted_fields?
- @unconverted_fields
- end
-
- def headers
- @headers
- end
-
- def header_row?
- @use_headers and @headers.nil?
- end
-
- def return_headers?
- @return_headers
- end
-
- def skip_blanks?
- @skip_blanks
- end
-
- def liberal_parsing?
- @liberal_parsing
- end
-
- def lineno
- @lineno
- end
-
- def line
- last_line
- end
-
- def parse(&block)
- return to_enum(__method__) unless block_given?
-
- if @return_headers and @headers and @raw_headers
- headers = Row.new(@headers, @raw_headers, true)
- if @unconverted_fields
- headers = add_unconverted_fields(headers, [])
- end
- yield headers
- end
-
- begin
- @scanner ||= build_scanner
- if quote_character.nil?
- parse_no_quote(&block)
- elsif @need_robust_parsing
- parse_quotable_robust(&block)
- else
- parse_quotable_loose(&block)
- end
- rescue InvalidEncoding
- if @scanner
- ignore_broken_line
- lineno = @lineno
- else
- lineno = @lineno + 1
- end
- message = "Invalid byte sequence in #{@encoding}"
- raise MalformedCSVError.new(message, lineno)
- rescue UnexpectedError => error
- if @scanner
- ignore_broken_line
- lineno = @lineno
- else
- lineno = @lineno + 1
- end
- message = "This should not be happen: #{error.message}: "
- message += "Please report this to https://github.com/ruby/csv/issues"
- raise MalformedCSVError.new(message, lineno)
- end
- end
-
- def use_headers?
- @use_headers
- end
-
- private
- # A set of tasks to prepare the file in order to parse it
- def prepare
- prepare_variable
- prepare_quote_character
- prepare_backslash
- prepare_skip_lines
- prepare_strip
- prepare_separators
- validate_strip_and_col_sep_options
- prepare_quoted
- prepare_unquoted
- prepare_line
- prepare_header
- prepare_parser
- end
-
- def prepare_variable
- @need_robust_parsing = false
- @encoding = @options[:encoding]
- liberal_parsing = @options[:liberal_parsing]
- if liberal_parsing
- @liberal_parsing = true
- if liberal_parsing.is_a?(Hash)
- @double_quote_outside_quote =
- liberal_parsing[:double_quote_outside_quote]
- @backslash_quote = liberal_parsing[:backslash_quote]
- else
- @double_quote_outside_quote = false
- @backslash_quote = false
- end
- @need_robust_parsing = true
- else
- @liberal_parsing = false
- @backslash_quote = false
- end
- @unconverted_fields = @options[:unconverted_fields]
- @max_field_size = @options[:max_field_size]
- @skip_blanks = @options[:skip_blanks]
- @fields_converter = @options[:fields_converter]
- @header_fields_converter = @options[:header_fields_converter]
- end
-
- def prepare_quote_character
- @quote_character = @options[:quote_character]
- if @quote_character.nil?
- @escaped_quote_character = nil
- @escaped_quote = nil
- else
- @quote_character = @quote_character.to_s.encode(@encoding)
- if @quote_character.length != 1
- message = ":quote_char has to be nil or a single character String"
- raise ArgumentError, message
- end
- @escaped_quote_character = Regexp.escape(@quote_character)
- @escaped_quote = Regexp.new(@escaped_quote_character)
- end
- end
-
- def prepare_backslash
- return unless @backslash_quote
-
- @backslash_character = "\\".encode(@encoding)
-
- @escaped_backslash_character = Regexp.escape(@backslash_character)
- @escaped_backslash = Regexp.new(@escaped_backslash_character)
- if @quote_character.nil?
- @backslash_quote_character = nil
- else
- @backslash_quote_character =
- @backslash_character + @escaped_quote_character
- end
- end
-
- def prepare_skip_lines
- skip_lines = @options[:skip_lines]
- case skip_lines
- when String
- @skip_lines = skip_lines.encode(@encoding)
- when Regexp, nil
- @skip_lines = skip_lines
- else
- unless skip_lines.respond_to?(:match)
- message =
- ":skip_lines has to respond to \#match: #{skip_lines.inspect}"
- raise ArgumentError, message
- end
- @skip_lines = skip_lines
- end
- end
-
- def prepare_strip
- @strip = @options[:strip]
- @escaped_strip = nil
- @strip_value = nil
- @rstrip_value = nil
- if @strip.is_a?(String)
- case @strip.length
- when 0
- raise ArgumentError, ":strip must not be an empty String"
- when 1
- # ok
- else
- raise ArgumentError, ":strip doesn't support 2 or more characters yet"
- end
- @strip = @strip.encode(@encoding)
- @escaped_strip = Regexp.escape(@strip)
- if @quote_character
- @strip_value = Regexp.new(@escaped_strip +
- "+".encode(@encoding))
- @rstrip_value = Regexp.new(@escaped_strip +
- "+\\z".encode(@encoding))
- end
- @need_robust_parsing = true
- elsif @strip
- strip_values = " \t\f\v"
- @escaped_strip = strip_values.encode(@encoding)
- if @quote_character
- @strip_value = Regexp.new("[#{strip_values}]+".encode(@encoding))
- @rstrip_value = Regexp.new("[#{strip_values}]+\\z".encode(@encoding))
- end
- @need_robust_parsing = true
- end
- end
-
- begin
- StringScanner.new("x").scan("x")
- rescue TypeError
- STRING_SCANNER_SCAN_ACCEPT_STRING = false
- else
- STRING_SCANNER_SCAN_ACCEPT_STRING = true
- end
-
- def prepare_separators
- column_separator = @options[:column_separator]
- @column_separator = column_separator.to_s.encode(@encoding)
- if @column_separator.size < 1
- message = ":col_sep must be 1 or more characters: "
- message += column_separator.inspect
- raise ArgumentError, message
- end
- @row_separator =
- resolve_row_separator(@options[:row_separator]).encode(@encoding)
-
- @escaped_column_separator = Regexp.escape(@column_separator)
- @escaped_first_column_separator = Regexp.escape(@column_separator[0])
- if @column_separator.size > 1
- @column_end = Regexp.new(@escaped_column_separator)
- @column_ends = @column_separator.each_char.collect do |char|
- Regexp.new(Regexp.escape(char))
- end
- @first_column_separators = Regexp.new(@escaped_first_column_separator +
- "+".encode(@encoding))
- else
- if STRING_SCANNER_SCAN_ACCEPT_STRING
- @column_end = @column_separator
- else
- @column_end = Regexp.new(@escaped_column_separator)
- end
- @column_ends = nil
- @first_column_separators = nil
- end
-
- escaped_row_separator = Regexp.escape(@row_separator)
- @row_end = Regexp.new(escaped_row_separator)
- if @row_separator.size > 1
- @row_ends = @row_separator.each_char.collect do |char|
- Regexp.new(Regexp.escape(char))
- end
- else
- @row_ends = nil
- end
-
- @cr = "\r".encode(@encoding)
- @lf = "\n".encode(@encoding)
- @line_end = Regexp.new("\r\n|\n|\r".encode(@encoding))
- @not_line_end = Regexp.new("[^\r\n]+".encode(@encoding))
- end
-
- # This method verifies that there are no (obvious) ambiguities with the
- # provided +col_sep+ and +strip+ parsing options. For example, if +col_sep+
- # and +strip+ were both equal to +\t+, then there would be no clear way to
- # parse the input.
- def validate_strip_and_col_sep_options
- return unless @strip
-
- if @strip.is_a?(String)
- if @column_separator.start_with?(@strip) || @column_separator.end_with?(@strip)
- raise ArgumentError,
- "The provided strip (#{@escaped_strip}) and " \
- "col_sep (#{@escaped_column_separator}) options are incompatible."
- end
- else
- if Regexp.new("\\A[#{@escaped_strip}]|[#{@escaped_strip}]\\z").match?(@column_separator)
- raise ArgumentError,
- "The provided strip (true) and " \
- "col_sep (#{@escaped_column_separator}) options are incompatible."
- end
- end
- end
-
- def prepare_quoted
- if @quote_character
- @quotes = Regexp.new(@escaped_quote_character +
- "+".encode(@encoding))
- no_quoted_values = @escaped_quote_character.dup
- if @backslash_quote
- no_quoted_values << @escaped_backslash_character
- end
- @quoted_value = Regexp.new("[^".encode(@encoding) +
- no_quoted_values +
- "]+".encode(@encoding))
- end
- if @escaped_strip
- @split_column_separator = Regexp.new(@escaped_strip +
- "*".encode(@encoding) +
- @escaped_column_separator +
- @escaped_strip +
- "*".encode(@encoding))
- else
- if @column_separator == " ".encode(@encoding)
- @split_column_separator = Regexp.new(@escaped_column_separator)
- else
- @split_column_separator = @column_separator
- end
- end
- end
-
- def prepare_unquoted
- return if @quote_character.nil?
-
- no_unquoted_values = "\r\n".encode(@encoding)
- no_unquoted_values << @escaped_first_column_separator
- unless @liberal_parsing
- no_unquoted_values << @escaped_quote_character
- end
- @unquoted_value = Regexp.new("[^".encode(@encoding) +
- no_unquoted_values +
- "]+".encode(@encoding))
- end
-
- def resolve_row_separator(separator)
- if separator == :auto
- cr = "\r".encode(@encoding)
- lf = "\n".encode(@encoding)
- if @input.is_a?(StringIO)
- pos = @input.pos
- separator = detect_row_separator(@input.read, cr, lf)
- @input.seek(pos)
- elsif @input.respond_to?(:gets)
- if @input.is_a?(File)
- chunk_size = 32 * 1024
- else
- chunk_size = 1024
- end
- begin
- while separator == :auto
- #
- # if we run out of data, it's probably a single line
- # (ensure will set default value)
- #
- break unless sample = @input.gets(nil, chunk_size)
-
- # extend sample if we're unsure of the line ending
- if sample.end_with?(cr)
- sample << (@input.gets(nil, 1) || "")
- end
-
- @samples << sample
-
- separator = detect_row_separator(sample, cr, lf)
- end
- rescue IOError
- # do nothing: ensure will set default
- end
- end
- separator = InputRecordSeparator.value if separator == :auto
- end
- separator.to_s.encode(@encoding)
- end
-
- def detect_row_separator(sample, cr, lf)
- lf_index = sample.index(lf)
- if lf_index
- cr_index = sample[0, lf_index].index(cr)
- else
- cr_index = sample.index(cr)
- end
- if cr_index and lf_index
- if cr_index + 1 == lf_index
- cr + lf
- elsif cr_index < lf_index
- cr
- else
- lf
- end
- elsif cr_index
- cr
- elsif lf_index
- lf
- else
- :auto
- end
- end
-
- def prepare_line
- @lineno = 0
- @last_line = nil
- @scanner = nil
- end
-
- def last_line
- if @scanner
- @last_line ||= @scanner.keep_end
- else
- @last_line
- end
- end
-
- def prepare_header
- @return_headers = @options[:return_headers]
-
- headers = @options[:headers]
- case headers
- when Array
- @raw_headers = headers
- quoted_fields = [false] * @raw_headers.size
- @use_headers = true
- when String
- @raw_headers, quoted_fields = parse_headers(headers)
- @use_headers = true
- when nil, false
- @raw_headers = nil
- @use_headers = false
- else
- @raw_headers = nil
- @use_headers = true
- end
- if @raw_headers
- @headers = adjust_headers(@raw_headers, quoted_fields)
- else
- @headers = nil
- end
- end
-
- def parse_headers(row)
- quoted_fields = []
- converter = lambda do |field, info|
- quoted_fields << info.quoted?
- field
- end
- headers = CSV.parse_line(row,
- col_sep: @column_separator,
- row_sep: @row_separator,
- quote_char: @quote_character,
- converters: [converter])
- [headers, quoted_fields]
- end
-
- def adjust_headers(headers, quoted_fields)
- adjusted_headers = @header_fields_converter.convert(headers, nil, @lineno, quoted_fields)
- adjusted_headers.each {|h| h.freeze if h.is_a? String}
- adjusted_headers
- end
-
- def prepare_parser
- @may_quoted = may_quoted?
- end
-
- def may_quoted?
- return false if @quote_character.nil?
-
- if @input.is_a?(StringIO)
- pos = @input.pos
- sample = @input.read
- @input.seek(pos)
- else
- return false if @samples.empty?
- sample = @samples.first
- end
- sample[0, 128].index(@quote_character)
- end
-
- class UnoptimizedStringIO # :nodoc:
- def initialize(string)
- @io = StringIO.new(string, "rb:#{string.encoding}")
- end
-
- def gets(*args)
- @io.gets(*args)
- end
-
- def each_line(*args, &block)
- @io.each_line(*args, &block)
- end
-
- def eof?
- @io.eof?
- end
- end
-
- SCANNER_TEST = (ENV["CSV_PARSER_SCANNER_TEST"] == "yes")
- if SCANNER_TEST
- SCANNER_TEST_CHUNK_SIZE_NAME = "CSV_PARSER_SCANNER_TEST_CHUNK_SIZE"
- SCANNER_TEST_CHUNK_SIZE_VALUE = ENV[SCANNER_TEST_CHUNK_SIZE_NAME]
- def build_scanner
- inputs = @samples.collect do |sample|
- UnoptimizedStringIO.new(sample)
- end
- if @input.is_a?(StringIO)
- inputs << UnoptimizedStringIO.new(@input.read)
- else
- inputs << @input
- end
- begin
- chunk_size_value = ENV[SCANNER_TEST_CHUNK_SIZE_NAME]
- rescue # Ractor::IsolationError
- # Ractor on Ruby 3.0 can't read ENV value.
- chunk_size_value = SCANNER_TEST_CHUNK_SIZE_VALUE
- end
- chunk_size = Integer((chunk_size_value || "1"), 10)
- InputsScanner.new(inputs,
- @encoding,
- @row_separator,
- chunk_size: chunk_size)
- end
- else
- def build_scanner
- string = nil
- if @samples.empty? and @input.is_a?(StringIO)
- string = @input.read
- elsif @samples.size == 1 and
- @input != ARGF and
- @input.respond_to?(:eof?) and
- @input.eof?
- string = @samples[0]
- end
- if string
- unless string.valid_encoding?
- index = string.lines(@row_separator).index do |line|
- !line.valid_encoding?
- end
- if index
- message = "Invalid byte sequence in #{@encoding}"
- raise MalformedCSVError.new(message, @lineno + index + 1)
- end
- end
- Scanner.new(string)
- else
- inputs = @samples.collect do |sample|
- StringIO.new(sample)
- end
- inputs << @input
- InputsScanner.new(inputs, @encoding, @row_separator)
- end
- end
- end
-
- def skip_needless_lines
- return unless @skip_lines
-
- until @scanner.eos?
- @scanner.keep_start
- line = @scanner.scan_all(@not_line_end) || "".encode(@encoding)
- line << @row_separator if parse_row_end
- if skip_line?(line)
- @lineno += 1
- @scanner.keep_drop
- else
- @scanner.keep_back
- return
- end
- end
- end
-
- def skip_line?(line)
- line = line.delete_suffix(@row_separator)
- case @skip_lines
- when String
- line.include?(@skip_lines)
- when Regexp
- @skip_lines.match?(line)
- else
- @skip_lines.match(line)
- end
- end
-
- def validate_field_size(field)
- return unless @max_field_size
- return if field.size <= @max_field_size
- ignore_broken_line
- message = "Field size exceeded: #{field.size} > #{@max_field_size}"
- raise MalformedCSVError.new(message, @lineno)
- end
-
- def parse_no_quote(&block)
- @scanner.each_line(@row_separator) do |line|
- next if @skip_lines and skip_line?(line)
- original_line = line
- line = line.delete_suffix(@row_separator)
-
- if line.empty?
- next if @skip_blanks
- row = []
- quoted_fields = []
- else
- line = strip_value(line)
- row = line.split(@split_column_separator, -1)
- quoted_fields = [false] * row.size
- if @max_field_size
- row.each do |column|
- validate_field_size(column)
- end
- end
- n_columns = row.size
- i = 0
- while i < n_columns
- row[i] = nil if row[i].empty?
- i += 1
- end
- end
- @last_line = original_line
- emit_row(row, quoted_fields, &block)
- end
- end
-
- def parse_quotable_loose(&block)
- @scanner.keep_start
- @scanner.each_line(@row_separator) do |line|
- if @skip_lines and skip_line?(line)
- @scanner.keep_drop
- @scanner.keep_start
- next
- end
- original_line = line
- line = line.delete_suffix(@row_separator)
-
- if line.empty?
- if @skip_blanks
- @scanner.keep_drop
- @scanner.keep_start
- next
- end
- row = []
- quoted_fields = []
- elsif line.include?(@cr) or line.include?(@lf)
- @scanner.keep_back
- @need_robust_parsing = true
- return parse_quotable_robust(&block)
- else
- row = line.split(@split_column_separator, -1)
- quoted_fields = []
- n_columns = row.size
- i = 0
- while i < n_columns
- column = row[i]
- if column.empty?
- quoted_fields << false
- row[i] = nil
- else
- n_quotes = column.count(@quote_character)
- if n_quotes.zero?
- quoted_fields << false
- # no quote
- elsif n_quotes == 2 and
- column.start_with?(@quote_character) and
- column.end_with?(@quote_character)
- quoted_fields << true
- row[i] = column[1..-2]
- else
- @scanner.keep_back
- @need_robust_parsing = true
- return parse_quotable_robust(&block)
- end
- validate_field_size(row[i])
- end
- i += 1
- end
- end
- @scanner.keep_drop
- @scanner.keep_start
- @last_line = original_line
- emit_row(row, quoted_fields, &block)
- end
- @scanner.keep_drop
- end
-
- def parse_quotable_robust(&block)
- row = []
- quoted_fields = []
- skip_needless_lines
- start_row
- while true
- @quoted_column_value = false
- @unquoted_column_value = false
- @scanner.scan_all(@strip_value) if @strip_value
- value = parse_column_value
- if value
- @scanner.scan_all(@strip_value) if @strip_value
- validate_field_size(value)
- end
- if parse_column_end
- row << value
- quoted_fields << @quoted_column_value
- elsif parse_row_end
- if row.empty? and value.nil?
- emit_row([], [], &block) unless @skip_blanks
- else
- row << value
- quoted_fields << @quoted_column_value
- emit_row(row, quoted_fields, &block)
- row = []
- quoted_fields = []
- end
- skip_needless_lines
- start_row
- elsif @scanner.eos?
- break if row.empty? and value.nil?
- row << value
- quoted_fields << @quoted_column_value
- emit_row(row, quoted_fields, &block)
- break
- else
- if @quoted_column_value
- if liberal_parsing? and (new_line = @scanner.check(@line_end))
- message =
- "Illegal end-of-line sequence outside of a quoted field " +
- "<#{new_line.inspect}>"
- else
- message = "Any value after quoted field isn't allowed"
- end
- ignore_broken_line
- raise MalformedCSVError.new(message, @lineno)
- elsif @unquoted_column_value and
- (new_line = @scanner.scan(@line_end))
- ignore_broken_line
- message = "Unquoted fields do not allow new line " +
- "<#{new_line.inspect}>"
- raise MalformedCSVError.new(message, @lineno)
- elsif @scanner.rest.start_with?(@quote_character)
- ignore_broken_line
- message = "Illegal quoting"
- raise MalformedCSVError.new(message, @lineno)
- elsif (new_line = @scanner.scan(@line_end))
- ignore_broken_line
- message = "New line must be <#{@row_separator.inspect}> " +
- "not <#{new_line.inspect}>"
- raise MalformedCSVError.new(message, @lineno)
- else
- ignore_broken_line
- raise MalformedCSVError.new("TODO: Meaningful message",
- @lineno)
- end
- end
- end
- end
-
- def parse_column_value
- if @liberal_parsing
- quoted_value = parse_quoted_column_value
- if quoted_value
- @scanner.scan_all(@strip_value) if @strip_value
- unquoted_value = parse_unquoted_column_value
- if unquoted_value
- if @double_quote_outside_quote
- unquoted_value = unquoted_value.gsub(@quote_character * 2,
- @quote_character)
- if quoted_value.empty? # %Q{""...} case
- return @quote_character + unquoted_value
- end
- end
- @quote_character + quoted_value + @quote_character + unquoted_value
- else
- quoted_value
- end
- else
- parse_unquoted_column_value
- end
- elsif @may_quoted
- parse_quoted_column_value ||
- parse_unquoted_column_value
- else
- parse_unquoted_column_value ||
- parse_quoted_column_value
- end
- end
-
- def parse_unquoted_column_value
- value = @scanner.scan_all(@unquoted_value)
- return nil unless value
-
- @unquoted_column_value = true
- if @first_column_separators
- while true
- @scanner.keep_start
- is_column_end = @column_ends.all? do |column_end|
- @scanner.scan(column_end)
- end
- @scanner.keep_back
- break if is_column_end
- sub_separator = @scanner.scan_all(@first_column_separators)
- break if sub_separator.nil?
- value << sub_separator
- sub_value = @scanner.scan_all(@unquoted_value)
- break if sub_value.nil?
- value << sub_value
- end
- end
- value.gsub!(@backslash_quote_character, @quote_character) if @backslash_quote
- if @rstrip_value
- value.gsub!(@rstrip_value, "")
- end
- value
- end
-
- def parse_quoted_column_value
- quotes = @scanner.scan_all(@quotes)
- return nil unless quotes
-
- @quoted_column_value = true
- n_quotes = quotes.size
- if (n_quotes % 2).zero?
- quotes[0, (n_quotes - 2) / 2]
- else
- value = quotes[0, n_quotes / 2]
- while true
- quoted_value = @scanner.scan_all(@quoted_value)
- value << quoted_value if quoted_value
- if @backslash_quote
- if @scanner.scan(@escaped_backslash)
- if @scanner.scan(@escaped_quote)
- value << @quote_character
- else
- value << @backslash_character
- end
- next
- end
- end
-
- quotes = @scanner.scan_all(@quotes)
- unless quotes
- ignore_broken_line
- message = "Unclosed quoted field"
- raise MalformedCSVError.new(message, @lineno)
- end
- n_quotes = quotes.size
- if n_quotes == 1
- break
- else
- value << quotes[0, n_quotes / 2]
- break if (n_quotes % 2) == 1
- end
- end
- value
- end
- end
-
- def parse_column_end
- return true if @scanner.scan(@column_end)
- return false unless @column_ends
-
- @scanner.keep_start
- if @column_ends.all? {|column_end| @scanner.scan(column_end)}
- @scanner.keep_drop
- true
- else
- @scanner.keep_back
- false
- end
- end
-
- def parse_row_end
- return true if @scanner.scan(@row_end)
- return false unless @row_ends
- @scanner.keep_start
- if @row_ends.all? {|row_end| @scanner.scan(row_end)}
- @scanner.keep_drop
- true
- else
- @scanner.keep_back
- false
- end
- end
-
- def strip_value(value)
- return value unless @strip
- return value if value.nil?
-
- case @strip
- when String
- while value.delete_prefix!(@strip)
- # do nothing
- end
- while value.delete_suffix!(@strip)
- # do nothing
- end
- else
- value.strip!
- end
- value
- end
-
- def ignore_broken_line
- @scanner.scan_all(@not_line_end)
- @scanner.scan_all(@line_end)
- @lineno += 1
- end
-
- def start_row
- if @last_line
- @last_line = nil
- else
- @scanner.keep_drop
- end
- @scanner.keep_start
- end
-
- def emit_row(row, quoted_fields, &block)
- @lineno += 1
-
- raw_row = row
- if @use_headers
- if @headers.nil?
- @headers = adjust_headers(row, quoted_fields)
- return unless @return_headers
- row = Row.new(@headers, row, true)
- else
- row = Row.new(@headers,
- @fields_converter.convert(raw_row, @headers, @lineno, quoted_fields))
- end
- else
- # convert fields, if needed...
- row = @fields_converter.convert(raw_row, nil, @lineno, quoted_fields)
- end
-
- # inject unconverted fields and accessor, if requested...
- if @unconverted_fields and not row.respond_to?(:unconverted_fields)
- add_unconverted_fields(row, raw_row)
- end
-
- yield(row)
- end
-
- # This method injects an instance variable <tt>unconverted_fields</tt> into
- # +row+ and an accessor method for +row+ called unconverted_fields(). The
- # variable is set to the contents of +fields+.
- def add_unconverted_fields(row, fields)
- class << row
- attr_reader :unconverted_fields
- end
- row.instance_variable_set(:@unconverted_fields, fields)
- row
- end
- end
-end
diff --git a/lib/csv/row.rb b/lib/csv/row.rb
deleted file mode 100644
index 86323f7d0a..0000000000
--- a/lib/csv/row.rb
+++ /dev/null
@@ -1,757 +0,0 @@
-# frozen_string_literal: true
-
-require "forwardable"
-
-class CSV
- # = \CSV::Row
- # A \CSV::Row instance represents a \CSV table row.
- # (see {class CSV}[../CSV.html]).
- #
- # The instance may have:
- # - Fields: each is an object, not necessarily a \String.
- # - Headers: each serves a key, and also need not be a \String.
- #
- # === Instance Methods
- #
- # \CSV::Row has three groups of instance methods:
- # - Its own internally defined instance methods.
- # - Methods included by module Enumerable.
- # - Methods delegated to class Array.:
- # * Array#empty?
- # * Array#length
- # * Array#size
- #
- # == Creating a \CSV::Row Instance
- #
- # Commonly, a new \CSV::Row instance is created by parsing \CSV source
- # that has headers:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # table.each {|row| p row }
- # Output:
- # #<CSV::Row "Name":"foo" "Value":"0">
- # #<CSV::Row "Name":"bar" "Value":"1">
- # #<CSV::Row "Name":"baz" "Value":"2">
- #
- # You can also create a row directly. See ::new.
- #
- # == Headers
- #
- # Like a \CSV::Table, a \CSV::Row has headers.
- #
- # A \CSV::Row that was created by parsing \CSV source
- # inherits its headers from the table:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # row = table.first
- # row.headers # => ["Name", "Value"]
- #
- # You can also create a new row with headers;
- # like the keys in a \Hash, the headers need not be Strings:
- # row = CSV::Row.new([:name, :value], ['foo', 0])
- # row.headers # => [:name, :value]
- #
- # The new row retains its headers even if added to a table
- # that has headers:
- # table << row # => #<CSV::Table mode:col_or_row row_count:5>
- # row.headers # => [:name, :value]
- # row[:name] # => "foo"
- # row['Name'] # => nil
- #
- #
- #
- # == Accessing Fields
- #
- # You may access a field in a \CSV::Row with either its \Integer index
- # (\Array-style) or its header (\Hash-style).
- #
- # Fetch a field using method #[]:
- # row = CSV::Row.new(['Name', 'Value'], ['foo', 0])
- # row[1] # => 0
- # row['Value'] # => 0
- #
- # Set a field using method #[]=:
- # row = CSV::Row.new(['Name', 'Value'], ['foo', 0])
- # row # => #<CSV::Row "Name":"foo" "Value":0>
- # row[0] = 'bar'
- # row['Value'] = 1
- # row # => #<CSV::Row "Name":"bar" "Value":1>
- #
- class Row
- # :call-seq:
- # CSV::Row.new(headers, fields, header_row = false) -> csv_row
- #
- # Returns the new \CSV::Row instance constructed from
- # arguments +headers+ and +fields+; both should be Arrays;
- # note that the fields need not be Strings:
- # row = CSV::Row.new(['Name', 'Value'], ['foo', 0])
- # row # => #<CSV::Row "Name":"foo" "Value":0>
- #
- # If the \Array lengths are different, the shorter is +nil+-filled:
- # row = CSV::Row.new(['Name', 'Value', 'Date', 'Size'], ['foo', 0])
- # row # => #<CSV::Row "Name":"foo" "Value":0 "Date":nil "Size":nil>
- #
- # Each \CSV::Row object is either a <i>field row</i> or a <i>header row</i>;
- # by default, a new row is a field row; for the row created above:
- # row.field_row? # => true
- # row.header_row? # => false
- #
- # If the optional argument +header_row+ is given as +true+,
- # the created row is a header row:
- # row = CSV::Row.new(['Name', 'Value'], ['foo', 0], header_row = true)
- # row # => #<CSV::Row "Name":"foo" "Value":0>
- # row.field_row? # => false
- # row.header_row? # => true
- def initialize(headers, fields, header_row = false)
- @header_row = header_row
- headers.each { |h| h.freeze if h.is_a? String }
-
- # handle extra headers or fields
- @row = if headers.size >= fields.size
- headers.zip(fields)
- else
- fields.zip(headers).each(&:reverse!)
- end
- end
-
- # Internal data format used to compare equality.
- attr_reader :row
- protected :row
-
- ### Array Delegation ###
-
- extend Forwardable
- def_delegators :@row, :empty?, :length, :size
-
- # :call-seq:
- # row.initialize_copy(other_row) -> self
- #
- # Calls superclass method.
- def initialize_copy(other)
- super_return_value = super
- @row = @row.collect(&:dup)
- super_return_value
- end
-
- # :call-seq:
- # row.header_row? -> true or false
- #
- # Returns +true+ if this is a header row, +false+ otherwise.
- def header_row?
- @header_row
- end
-
- # :call-seq:
- # row.field_row? -> true or false
- #
- # Returns +true+ if this is a field row, +false+ otherwise.
- def field_row?
- not header_row?
- end
-
- # :call-seq:
- # row.headers -> array_of_headers
- #
- # Returns the headers for this row:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # row = table.first
- # row.headers # => ["Name", "Value"]
- def headers
- @row.map(&:first)
- end
-
- # :call-seq:
- # field(index) -> value
- # field(header) -> value
- # field(header, offset) -> value
- #
- # Returns the field value for the given +index+ or +header+.
- #
- # ---
- #
- # Fetch field value by \Integer index:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row.field(0) # => "foo"
- # row.field(1) # => "bar"
- #
- # Counts backward from the last column if +index+ is negative:
- # row.field(-1) # => "0"
- # row.field(-2) # => "foo"
- #
- # Returns +nil+ if +index+ is out of range:
- # row.field(2) # => nil
- # row.field(-3) # => nil
- #
- # ---
- #
- # Fetch field value by header (first found):
- # source = "Name,Name,Name\nFoo,Bar,Baz\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row.field('Name') # => "Foo"
- #
- # Fetch field value by header, ignoring +offset+ leading fields:
- # source = "Name,Name,Name\nFoo,Bar,Baz\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row.field('Name', 2) # => "Baz"
- #
- # Returns +nil+ if the header does not exist.
- def field(header_or_index, minimum_index = 0)
- # locate the pair
- finder = (header_or_index.is_a?(Integer) || header_or_index.is_a?(Range)) ? :[] : :assoc
- pair = @row[minimum_index..-1].public_send(finder, header_or_index)
-
- # return the field if we have a pair
- if pair.nil?
- nil
- else
- header_or_index.is_a?(Range) ? pair.map(&:last) : pair.last
- end
- end
- alias_method :[], :field
-
- #
- # :call-seq:
- # fetch(header) -> value
- # fetch(header, default) -> value
- # fetch(header) {|row| ... } -> value
- #
- # Returns the field value as specified by +header+.
- #
- # ---
- #
- # With the single argument +header+, returns the field value
- # for that header (first found):
- # source = "Name,Name,Name\nFoo,Bar,Baz\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row.fetch('Name') # => "Foo"
- #
- # Raises exception +KeyError+ if the header does not exist.
- #
- # ---
- #
- # With arguments +header+ and +default+ given,
- # returns the field value for the header (first found)
- # if the header exists, otherwise returns +default+:
- # source = "Name,Name,Name\nFoo,Bar,Baz\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row.fetch('Name', '') # => "Foo"
- # row.fetch(:nosuch, '') # => ""
- #
- # ---
- #
- # With argument +header+ and a block given,
- # returns the field value for the header (first found)
- # if the header exists; otherwise calls the block
- # and returns its return value:
- # source = "Name,Name,Name\nFoo,Bar,Baz\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row.fetch('Name') {|header| fail 'Cannot happen' } # => "Foo"
- # row.fetch(:nosuch) {|header| "Header '#{header} not found'" } # => "Header 'nosuch not found'"
- def fetch(header, *varargs)
- raise ArgumentError, "Too many arguments" if varargs.length > 1
- pair = @row.assoc(header)
- if pair
- pair.last
- else
- if block_given?
- yield header
- elsif varargs.empty?
- raise KeyError, "key not found: #{header}"
- else
- varargs.first
- end
- end
- end
-
- # :call-seq:
- # row.has_key?(header) -> true or false
- #
- # Returns +true+ if there is a field with the given +header+,
- # +false+ otherwise.
- def has_key?(header)
- !!@row.assoc(header)
- end
- alias_method :include?, :has_key?
- alias_method :key?, :has_key?
- alias_method :member?, :has_key?
- alias_method :header?, :has_key?
-
- #
- # :call-seq:
- # row[index] = value -> value
- # row[header, offset] = value -> value
- # row[header] = value -> value
- #
- # Assigns the field value for the given +index+ or +header+;
- # returns +value+.
- #
- # ---
- #
- # Assign field value by \Integer index:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row[0] = 'Bat'
- # row[1] = 3
- # row # => #<CSV::Row "Name":"Bat" "Value":3>
- #
- # Counts backward from the last column if +index+ is negative:
- # row[-1] = 4
- # row[-2] = 'Bam'
- # row # => #<CSV::Row "Name":"Bam" "Value":4>
- #
- # Extends the row with <tt>nil:nil</tt> if positive +index+ is not in the row:
- # row[4] = 5
- # row # => #<CSV::Row "Name":"bad" "Value":4 nil:nil nil:nil nil:5>
- #
- # Raises IndexError if negative +index+ is too small (too far from zero).
- #
- # ---
- #
- # Assign field value by header (first found):
- # source = "Name,Name,Name\nFoo,Bar,Baz\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row['Name'] = 'Bat'
- # row # => #<CSV::Row "Name":"Bat" "Name":"Bar" "Name":"Baz">
- #
- # Assign field value by header, ignoring +offset+ leading fields:
- # source = "Name,Name,Name\nFoo,Bar,Baz\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row['Name', 2] = 4
- # row # => #<CSV::Row "Name":"Foo" "Name":"Bar" "Name":4>
- #
- # Append new field by (new) header:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row['New'] = 6
- # row# => #<CSV::Row "Name":"foo" "Value":"0" "New":6>
- def []=(*args)
- value = args.pop
-
- if args.first.is_a? Integer
- if @row[args.first].nil? # extending past the end with index
- @row[args.first] = [nil, value]
- @row.map! { |pair| pair.nil? ? [nil, nil] : pair }
- else # normal index assignment
- @row[args.first][1] = value
- end
- else
- index = index(*args)
- if index.nil? # appending a field
- self << [args.first, value]
- else # normal header assignment
- @row[index][1] = value
- end
- end
- end
-
- #
- # :call-seq:
- # row << [header, value] -> self
- # row << hash -> self
- # row << value -> self
- #
- # Adds a field to +self+; returns +self+:
- #
- # If the argument is a 2-element \Array <tt>[header, value]</tt>,
- # a field is added with the given +header+ and +value+:
- # source = "Name,Name,Name\nFoo,Bar,Baz\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row << ['NAME', 'Bat']
- # row # => #<CSV::Row "Name":"Foo" "Name":"Bar" "Name":"Baz" "NAME":"Bat">
- #
- # If the argument is a \Hash, each <tt>key-value</tt> pair is added
- # as a field with header +key+ and value +value+.
- # source = "Name,Name,Name\nFoo,Bar,Baz\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row << {NAME: 'Bat', name: 'Bam'}
- # row # => #<CSV::Row "Name":"Foo" "Name":"Bar" "Name":"Baz" NAME:"Bat" name:"Bam">
- #
- # Otherwise, the given +value+ is added as a field with no header.
- # source = "Name,Name,Name\nFoo,Bar,Baz\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row << 'Bag'
- # row # => #<CSV::Row "Name":"Foo" "Name":"Bar" "Name":"Baz" nil:"Bag">
- def <<(arg)
- if arg.is_a?(Array) and arg.size == 2 # appending a header and name
- @row << arg
- elsif arg.is_a?(Hash) # append header and name pairs
- arg.each { |pair| @row << pair }
- else # append field value
- @row << [nil, arg]
- end
-
- self # for chaining
- end
-
- # :call-seq:
- # row.push(*values) -> self
- #
- # Appends each of the given +values+ to +self+ as a field; returns +self+:
- # source = "Name,Name,Name\nFoo,Bar,Baz\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row.push('Bat', 'Bam')
- # row # => #<CSV::Row "Name":"Foo" "Name":"Bar" "Name":"Baz" nil:"Bat" nil:"Bam">
- def push(*args)
- args.each { |arg| self << arg }
-
- self # for chaining
- end
-
- #
- # :call-seq:
- # delete(index) -> [header, value] or nil
- # delete(header) -> [header, value] or empty_array
- # delete(header, offset) -> [header, value] or empty_array
- #
- # Removes a specified field from +self+; returns the 2-element \Array
- # <tt>[header, value]</tt> if the field exists.
- #
- # If an \Integer argument +index+ is given,
- # removes and returns the field at offset +index+,
- # or returns +nil+ if the field does not exist:
- # source = "Name,Name,Name\nFoo,Bar,Baz\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row.delete(1) # => ["Name", "Bar"]
- # row.delete(50) # => nil
- #
- # Otherwise, if the single argument +header+ is given,
- # removes and returns the first-found field with the given header,
- # of returns a new empty \Array if the field does not exist:
- # source = "Name,Name,Name\nFoo,Bar,Baz\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row.delete('Name') # => ["Name", "Foo"]
- # row.delete('NAME') # => []
- #
- # If argument +header+ and \Integer argument +offset+ are given,
- # removes and returns the first-found field with the given header
- # whose +index+ is at least as large as +offset+:
- # source = "Name,Name,Name\nFoo,Bar,Baz\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row.delete('Name', 1) # => ["Name", "Bar"]
- # row.delete('NAME', 1) # => []
- def delete(header_or_index, minimum_index = 0)
- if header_or_index.is_a? Integer # by index
- @row.delete_at(header_or_index)
- elsif i = index(header_or_index, minimum_index) # by header
- @row.delete_at(i)
- else
- [ ]
- end
- end
-
- # :call-seq:
- # row.delete_if {|header, value| ... } -> self
- #
- # Removes fields from +self+ as selected by the block; returns +self+.
- #
- # Removes each field for which the block returns a truthy value:
- # source = "Name,Name,Name\nFoo,Bar,Baz\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row.delete_if {|header, value| value.start_with?('B') } # => true
- # row # => #<CSV::Row "Name":"Foo">
- # row.delete_if {|header, value| header.start_with?('B') } # => false
- #
- # If no block is given, returns a new Enumerator:
- # row.delete_if # => #<Enumerator: #<CSV::Row "Name":"Foo">:delete_if>
- def delete_if(&block)
- return enum_for(__method__) { size } unless block_given?
-
- @row.delete_if(&block)
-
- self # for chaining
- end
-
- # :call-seq:
- # self.fields(*specifiers) -> array_of_fields
- #
- # Returns field values per the given +specifiers+, which may be any mixture of:
- # - \Integer index.
- # - \Range of \Integer indexes.
- # - 2-element \Array containing a header and offset.
- # - Header.
- # - \Range of headers.
- #
- # For +specifier+ in one of the first four cases above,
- # returns the result of <tt>self.field(specifier)</tt>; see #field.
- #
- # Although there may be any number of +specifiers+,
- # the examples here will illustrate one at a time.
- #
- # When the specifier is an \Integer +index+,
- # returns <tt>self.field(index)</tt>L
- # source = "Name,Name,Name\nFoo,Bar,Baz\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row.fields(1) # => ["Bar"]
- #
- # When the specifier is a \Range of \Integers +range+,
- # returns <tt>self.field(range)</tt>:
- # row.fields(1..2) # => ["Bar", "Baz"]
- #
- # When the specifier is a 2-element \Array +array+,
- # returns <tt>self.field(array)</tt>L
- # row.fields('Name', 1) # => ["Foo", "Bar"]
- #
- # When the specifier is a header +header+,
- # returns <tt>self.field(header)</tt>L
- # row.fields('Name') # => ["Foo"]
- #
- # When the specifier is a \Range of headers +range+,
- # forms a new \Range +new_range+ from the indexes of
- # <tt>range.start</tt> and <tt>range.end</tt>,
- # and returns <tt>self.field(new_range)</tt>:
- # source = "Name,NAME,name\nFoo,Bar,Baz\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row.fields('Name'..'NAME') # => ["Foo", "Bar"]
- #
- # Returns all fields if no argument given:
- # row.fields # => ["Foo", "Bar", "Baz"]
- def fields(*headers_and_or_indices)
- if headers_and_or_indices.empty? # return all fields--no arguments
- @row.map(&:last)
- else # or work like values_at()
- all = []
- headers_and_or_indices.each do |h_or_i|
- if h_or_i.is_a? Range
- index_begin = h_or_i.begin.is_a?(Integer) ? h_or_i.begin :
- index(h_or_i.begin)
- index_end = h_or_i.end.is_a?(Integer) ? h_or_i.end :
- index(h_or_i.end)
- new_range = h_or_i.exclude_end? ? (index_begin...index_end) :
- (index_begin..index_end)
- all.concat(fields.values_at(new_range))
- else
- all << field(*Array(h_or_i))
- end
- end
- return all
- end
- end
- alias_method :values_at, :fields
-
- # :call-seq:
- # index(header) -> index
- # index(header, offset) -> index
- #
- # Returns the index for the given header, if it exists;
- # otherwise returns +nil+.
- #
- # With the single argument +header+, returns the index
- # of the first-found field with the given +header+:
- # source = "Name,Name,Name\nFoo,Bar,Baz\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row.index('Name') # => 0
- # row.index('NAME') # => nil
- #
- # With arguments +header+ and +offset+,
- # returns the index of the first-found field with given +header+,
- # but ignoring the first +offset+ fields:
- # row.index('Name', 1) # => 1
- # row.index('Name', 3) # => nil
- def index(header, minimum_index = 0)
- # find the pair
- index = headers[minimum_index..-1].index(header)
- # return the index at the right offset, if we found one
- index.nil? ? nil : index + minimum_index
- end
-
- # :call-seq:
- # row.field?(value) -> true or false
- #
- # Returns +true+ if +value+ is a field in this row, +false+ otherwise:
- # source = "Name,Name,Name\nFoo,Bar,Baz\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row.field?('Bar') # => true
- # row.field?('BAR') # => false
- def field?(data)
- fields.include? data
- end
-
- include Enumerable
-
- # :call-seq:
- # row.each {|header, value| ... } -> self
- #
- # Calls the block with each header-value pair; returns +self+:
- # source = "Name,Name,Name\nFoo,Bar,Baz\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row.each {|header, value| p [header, value] }
- # Output:
- # ["Name", "Foo"]
- # ["Name", "Bar"]
- # ["Name", "Baz"]
- #
- # If no block is given, returns a new Enumerator:
- # row.each # => #<Enumerator: #<CSV::Row "Name":"Foo" "Name":"Bar" "Name":"Baz">:each>
- def each(&block)
- return enum_for(__method__) { size } unless block_given?
-
- @row.each(&block)
-
- self # for chaining
- end
-
- alias_method :each_pair, :each
-
- # :call-seq:
- # row == other -> true or false
- #
- # Returns +true+ if +other+ is a /CSV::Row that has the same
- # fields (headers and values) in the same order as +self+;
- # otherwise returns +false+:
- # source = "Name,Name,Name\nFoo,Bar,Baz\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # other_row = table[0]
- # row == other_row # => true
- # other_row = table[1]
- # row == other_row # => false
- def ==(other)
- return @row == other.row if other.is_a? CSV::Row
- @row == other
- end
-
- # :call-seq:
- # row.to_h -> hash
- #
- # Returns the new \Hash formed by adding each header-value pair in +self+
- # as a key-value pair in the \Hash.
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row.to_h # => {"Name"=>"foo", "Value"=>"0"}
- #
- # Header order is preserved, but repeated headers are ignored:
- # source = "Name,Name,Name\nFoo,Bar,Baz\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row.to_h # => {"Name"=>"Foo"}
- def to_h
- hash = {}
- each do |key, _value|
- hash[key] = self[key] unless hash.key?(key)
- end
- hash
- end
- alias_method :to_hash, :to_h
-
- # :call-seq:
- # row.deconstruct_keys(keys) -> hash
- #
- # Returns the new \Hash suitable for pattern matching containing only the
- # keys specified as an argument.
- def deconstruct_keys(keys)
- if keys.nil?
- to_h
- else
- keys.to_h { |key| [key, self[key]] }
- end
- end
-
- alias_method :to_ary, :to_a
-
- # :call-seq:
- # row.deconstruct -> array
- #
- # Returns the new \Array suitable for pattern matching containing the values
- # of the row.
- def deconstruct
- fields
- end
-
- # :call-seq:
- # row.to_csv -> csv_string
- #
- # Returns the row as a \CSV String. Headers are not included:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row.to_csv # => "foo,0\n"
- def to_csv(**options)
- fields.to_csv(**options)
- end
- alias_method :to_s, :to_csv
-
- # :call-seq:
- # row.dig(index_or_header, *identifiers) -> object
- #
- # Finds and returns the object in nested object that is specified
- # by +index_or_header+ and +specifiers+.
- #
- # The nested objects may be instances of various classes.
- # See {Dig Methods}[rdoc-ref:dig_methods.rdoc].
- #
- # Examples:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row.dig(1) # => "0"
- # row.dig('Value') # => "0"
- # row.dig(5) # => nil
- def dig(index_or_header, *indexes)
- value = field(index_or_header)
- if value.nil?
- nil
- elsif indexes.empty?
- value
- else
- unless value.respond_to?(:dig)
- raise TypeError, "#{value.class} does not have \#dig method"
- end
- value.dig(*indexes)
- end
- end
-
- # :call-seq:
- # row.inspect -> string
- #
- # Returns an ASCII-compatible \String showing:
- # - Class \CSV::Row.
- # - Header-value pairs.
- # Example:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # row = table[0]
- # row.inspect # => "#<CSV::Row \"Name\":\"foo\" \"Value\":\"0\">"
- def inspect
- str = ["#<", self.class.to_s]
- each do |header, field|
- str << " " << (header.is_a?(Symbol) ? header.to_s : header.inspect) <<
- ":" << field.inspect
- end
- str << ">"
- begin
- str.join('')
- rescue # any encoding error
- str.map do |s|
- e = Encoding::Converter.asciicompat_encoding(s.encoding)
- e ? s.encode(e) : s.force_encoding("ASCII-8BIT")
- end.join('')
- end
- end
- end
-end
diff --git a/lib/csv/table.rb b/lib/csv/table.rb
deleted file mode 100644
index fb19f5453f..0000000000
--- a/lib/csv/table.rb
+++ /dev/null
@@ -1,1055 +0,0 @@
-# frozen_string_literal: true
-
-require "forwardable"
-
-class CSV
- # = \CSV::Table
- # A \CSV::Table instance represents \CSV data.
- # (see {class CSV}[../CSV.html]).
- #
- # The instance may have:
- # - Rows: each is a Table::Row object.
- # - Headers: names for the columns.
- #
- # === Instance Methods
- #
- # \CSV::Table has three groups of instance methods:
- # - Its own internally defined instance methods.
- # - Methods included by module Enumerable.
- # - Methods delegated to class Array.:
- # * Array#empty?
- # * Array#length
- # * Array#size
- #
- # == Creating a \CSV::Table Instance
- #
- # Commonly, a new \CSV::Table instance is created by parsing \CSV source
- # using headers:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # table.class # => CSV::Table
- #
- # You can also create an instance directly. See ::new.
- #
- # == Headers
- #
- # If a table has headers, the headers serve as labels for the columns of data.
- # Each header serves as the label for its column.
- #
- # The headers for a \CSV::Table object are stored as an \Array of Strings.
- #
- # Commonly, headers are defined in the first row of \CSV source:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # table.headers # => ["Name", "Value"]
- #
- # If no headers are defined, the \Array is empty:
- # table = CSV::Table.new([])
- # table.headers # => []
- #
- # == Access Modes
- #
- # \CSV::Table provides three modes for accessing table data:
- # - \Row mode.
- # - Column mode.
- # - Mixed mode (the default for a new table).
- #
- # The access mode for a\CSV::Table instance affects the behavior
- # of some of its instance methods:
- # - #[]
- # - #[]=
- # - #delete
- # - #delete_if
- # - #each
- # - #values_at
- #
- # === \Row Mode
- #
- # Set a table to row mode with method #by_row!:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # table.by_row! # => #<CSV::Table mode:row row_count:4>
- #
- # Specify a single row by an \Integer index:
- # # Get a row.
- # table[1] # => #<CSV::Row "Name":"bar" "Value":"1">
- # # Set a row, then get it.
- # table[1] = CSV::Row.new(['Name', 'Value'], ['bam', 3])
- # table[1] # => #<CSV::Row "Name":"bam" "Value":3>
- #
- # Specify a sequence of rows by a \Range:
- # # Get rows.
- # table[1..2] # => [#<CSV::Row "Name":"bam" "Value":3>, #<CSV::Row "Name":"baz" "Value":"2">]
- # # Set rows, then get them.
- # table[1..2] = [
- # CSV::Row.new(['Name', 'Value'], ['bat', 4]),
- # CSV::Row.new(['Name', 'Value'], ['bad', 5]),
- # ]
- # table[1..2] # => [["Name", #<CSV::Row "Name":"bat" "Value":4>], ["Value", #<CSV::Row "Name":"bad" "Value":5>]]
- #
- # === Column Mode
- #
- # Set a table to column mode with method #by_col!:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # table.by_col! # => #<CSV::Table mode:col row_count:4>
- #
- # Specify a column by an \Integer index:
- # # Get a column.
- # table[0]
- # # Set a column, then get it.
- # table[0] = ['FOO', 'BAR', 'BAZ']
- # table[0] # => ["FOO", "BAR", "BAZ"]
- #
- # Specify a column by its \String header:
- # # Get a column.
- # table['Name'] # => ["FOO", "BAR", "BAZ"]
- # # Set a column, then get it.
- # table['Name'] = ['Foo', 'Bar', 'Baz']
- # table['Name'] # => ["Foo", "Bar", "Baz"]
- #
- # === Mixed Mode
- #
- # In mixed mode, you can refer to either rows or columns:
- # - An \Integer index refers to a row.
- # - A \Range index refers to multiple rows.
- # - A \String index refers to a column.
- #
- # Set a table to mixed mode with method #by_col_or_row!:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # table.by_col_or_row! # => #<CSV::Table mode:col_or_row row_count:4>
- #
- # Specify a single row by an \Integer index:
- # # Get a row.
- # table[1] # => #<CSV::Row "Name":"bar" "Value":"1">
- # # Set a row, then get it.
- # table[1] = CSV::Row.new(['Name', 'Value'], ['bam', 3])
- # table[1] # => #<CSV::Row "Name":"bam" "Value":3>
- #
- # Specify a sequence of rows by a \Range:
- # # Get rows.
- # table[1..2] # => [#<CSV::Row "Name":"bam" "Value":3>, #<CSV::Row "Name":"baz" "Value":"2">]
- # # Set rows, then get them.
- # table[1] = CSV::Row.new(['Name', 'Value'], ['bat', 4])
- # table[2] = CSV::Row.new(['Name', 'Value'], ['bad', 5])
- # table[1..2] # => [["Name", #<CSV::Row "Name":"bat" "Value":4>], ["Value", #<CSV::Row "Name":"bad" "Value":5>]]
- #
- # Specify a column by its \String header:
- # # Get a column.
- # table['Name'] # => ["foo", "bat", "bad"]
- # # Set a column, then get it.
- # table['Name'] = ['Foo', 'Bar', 'Baz']
- # table['Name'] # => ["Foo", "Bar", "Baz"]
- class Table
- # :call-seq:
- # CSV::Table.new(array_of_rows, headers = nil) -> csv_table
- #
- # Returns a new \CSV::Table object.
- #
- # - Argument +array_of_rows+ must be an \Array of CSV::Row objects.
- # - Argument +headers+, if given, may be an \Array of Strings.
- #
- # ---
- #
- # Create an empty \CSV::Table object:
- # table = CSV::Table.new([])
- # table # => #<CSV::Table mode:col_or_row row_count:1>
- #
- # Create a non-empty \CSV::Table object:
- # rows = [
- # CSV::Row.new([], []),
- # CSV::Row.new([], []),
- # CSV::Row.new([], []),
- # ]
- # table = CSV::Table.new(rows)
- # table # => #<CSV::Table mode:col_or_row row_count:4>
- #
- # ---
- #
- # If argument +headers+ is an \Array of Strings,
- # those Strings become the table's headers:
- # table = CSV::Table.new([], headers: ['Name', 'Age'])
- # table.headers # => ["Name", "Age"]
- #
- # If argument +headers+ is not given and the table has rows,
- # the headers are taken from the first row:
- # rows = [
- # CSV::Row.new(['Foo', 'Bar'], []),
- # CSV::Row.new(['foo', 'bar'], []),
- # CSV::Row.new(['FOO', 'BAR'], []),
- # ]
- # table = CSV::Table.new(rows)
- # table.headers # => ["Foo", "Bar"]
- #
- # If argument +headers+ is not given and the table is empty (has no rows),
- # the headers are also empty:
- # table = CSV::Table.new([])
- # table.headers # => []
- #
- # ---
- #
- # Raises an exception if argument +array_of_rows+ is not an \Array object:
- # # Raises NoMethodError (undefined method `first' for :foo:Symbol):
- # CSV::Table.new(:foo)
- #
- # Raises an exception if an element of +array_of_rows+ is not a \CSV::Table object:
- # # Raises NoMethodError (undefined method `headers' for :foo:Symbol):
- # CSV::Table.new([:foo])
- def initialize(array_of_rows, headers: nil)
- @table = array_of_rows
- @headers = headers
- unless @headers
- if @table.empty?
- @headers = []
- else
- @headers = @table.first.headers
- end
- end
-
- @mode = :col_or_row
- end
-
- # The current access mode for indexing and iteration.
- attr_reader :mode
-
- # Internal data format used to compare equality.
- attr_reader :table
- protected :table
-
- ### Array Delegation ###
-
- extend Forwardable
- def_delegators :@table, :empty?, :length, :size
-
- # :call-seq:
- # table.by_col -> table_dup
- #
- # Returns a duplicate of +self+, in column mode
- # (see {Column Mode}[#class-CSV::Table-label-Column+Mode]):
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # table.mode # => :col_or_row
- # dup_table = table.by_col
- # dup_table.mode # => :col
- # dup_table.equal?(table) # => false # It's a dup
- #
- # This may be used to chain method calls without changing the mode
- # (but also will affect performance and memory usage):
- # dup_table.by_col['Name']
- #
- # Also note that changes to the duplicate table will not affect the original.
- def by_col
- self.class.new(@table.dup).by_col!
- end
-
- # :call-seq:
- # table.by_col! -> self
- #
- # Sets the mode for +self+ to column mode
- # (see {Column Mode}[#class-CSV::Table-label-Column+Mode]); returns +self+:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # table.mode # => :col_or_row
- # table1 = table.by_col!
- # table.mode # => :col
- # table1.equal?(table) # => true # Returned self
- def by_col!
- @mode = :col
-
- self
- end
-
- # :call-seq:
- # table.by_col_or_row -> table_dup
- #
- # Returns a duplicate of +self+, in mixed mode
- # (see {Mixed Mode}[#class-CSV::Table-label-Mixed+Mode]):
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true).by_col!
- # table.mode # => :col
- # dup_table = table.by_col_or_row
- # dup_table.mode # => :col_or_row
- # dup_table.equal?(table) # => false # It's a dup
- #
- # This may be used to chain method calls without changing the mode
- # (but also will affect performance and memory usage):
- # dup_table.by_col_or_row['Name']
- #
- # Also note that changes to the duplicate table will not affect the original.
- def by_col_or_row
- self.class.new(@table.dup).by_col_or_row!
- end
-
- # :call-seq:
- # table.by_col_or_row! -> self
- #
- # Sets the mode for +self+ to mixed mode
- # (see {Mixed Mode}[#class-CSV::Table-label-Mixed+Mode]); returns +self+:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true).by_col!
- # table.mode # => :col
- # table1 = table.by_col_or_row!
- # table.mode # => :col_or_row
- # table1.equal?(table) # => true # Returned self
- def by_col_or_row!
- @mode = :col_or_row
-
- self
- end
-
- # :call-seq:
- # table.by_row -> table_dup
- #
- # Returns a duplicate of +self+, in row mode
- # (see {Row Mode}[#class-CSV::Table-label-Row+Mode]):
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # table.mode # => :col_or_row
- # dup_table = table.by_row
- # dup_table.mode # => :row
- # dup_table.equal?(table) # => false # It's a dup
- #
- # This may be used to chain method calls without changing the mode
- # (but also will affect performance and memory usage):
- # dup_table.by_row[1]
- #
- # Also note that changes to the duplicate table will not affect the original.
- def by_row
- self.class.new(@table.dup).by_row!
- end
-
- # :call-seq:
- # table.by_row! -> self
- #
- # Sets the mode for +self+ to row mode
- # (see {Row Mode}[#class-CSV::Table-label-Row+Mode]); returns +self+:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # table.mode # => :col_or_row
- # table1 = table.by_row!
- # table.mode # => :row
- # table1.equal?(table) # => true # Returned self
- def by_row!
- @mode = :row
-
- self
- end
-
- # :call-seq:
- # table.headers -> array_of_headers
- #
- # Returns a new \Array containing the \String headers for the table.
- #
- # If the table is not empty, returns the headers from the first row:
- # rows = [
- # CSV::Row.new(['Foo', 'Bar'], []),
- # CSV::Row.new(['FOO', 'BAR'], []),
- # CSV::Row.new(['foo', 'bar'], []),
- # ]
- # table = CSV::Table.new(rows)
- # table.headers # => ["Foo", "Bar"]
- # table.delete(0)
- # table.headers # => ["FOO", "BAR"]
- # table.delete(0)
- # table.headers # => ["foo", "bar"]
- #
- # If the table is empty, returns a copy of the headers in the table itself:
- # table.delete(0)
- # table.headers # => ["Foo", "Bar"]
- def headers
- if @table.empty?
- @headers.dup
- else
- @table.first.headers
- end
- end
-
- # :call-seq:
- # table[n] -> row or column_data
- # table[range] -> array_of_rows or array_of_column_data
- # table[header] -> array_of_column_data
- #
- # Returns data from the table; does not modify the table.
- #
- # ---
- #
- # Fetch a \Row by Its \Integer Index::
- # - Form: <tt>table[n]</tt>, +n+ an integer.
- # - Access mode: <tt>:row</tt> or <tt>:col_or_row</tt>.
- # - Return value: _nth_ row of the table, if that row exists;
- # otherwise +nil+.
- #
- # Returns the _nth_ row of the table if that row exists:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # table.by_row! # => #<CSV::Table mode:row row_count:4>
- # table[1] # => #<CSV::Row "Name":"bar" "Value":"1">
- # table.by_col_or_row! # => #<CSV::Table mode:col_or_row row_count:4>
- # table[1] # => #<CSV::Row "Name":"bar" "Value":"1">
- #
- # Counts backward from the last row if +n+ is negative:
- # table[-1] # => #<CSV::Row "Name":"baz" "Value":"2">
- #
- # Returns +nil+ if +n+ is too large or too small:
- # table[4] # => nil
- # table[-4] # => nil
- #
- # Raises an exception if the access mode is <tt>:row</tt>
- # and +n+ is not an \Integer:
- # table.by_row! # => #<CSV::Table mode:row row_count:4>
- # # Raises TypeError (no implicit conversion of String into Integer):
- # table['Name']
- #
- # ---
- #
- # Fetch a Column by Its \Integer Index::
- # - Form: <tt>table[n]</tt>, +n+ an \Integer.
- # - Access mode: <tt>:col</tt>.
- # - Return value: _nth_ column of the table, if that column exists;
- # otherwise an \Array of +nil+ fields of length <tt>self.size</tt>.
- #
- # Returns the _nth_ column of the table if that column exists:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # table.by_col! # => #<CSV::Table mode:col row_count:4>
- # table[1] # => ["0", "1", "2"]
- #
- # Counts backward from the last column if +n+ is negative:
- # table[-2] # => ["foo", "bar", "baz"]
- #
- # Returns an \Array of +nil+ fields if +n+ is too large or too small:
- # table[4] # => [nil, nil, nil]
- # table[-4] # => [nil, nil, nil]
- #
- # ---
- #
- # Fetch Rows by \Range::
- # - Form: <tt>table[range]</tt>, +range+ a \Range object.
- # - Access mode: <tt>:row</tt> or <tt>:col_or_row</tt>.
- # - Return value: rows from the table, beginning at row <tt>range.start</tt>,
- # if those rows exists.
- #
- # Returns rows from the table, beginning at row <tt>range.first</tt>,
- # if those rows exist:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # table.by_row! # => #<CSV::Table mode:row row_count:4>
- # rows = table[1..2] # => #<CSV::Row "Name":"bar" "Value":"1">
- # rows # => [#<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">]
- # table.by_col_or_row! # => #<CSV::Table mode:col_or_row row_count:4>
- # rows = table[1..2] # => #<CSV::Row "Name":"bar" "Value":"1">
- # rows # => [#<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">]
- #
- # If there are too few rows, returns all from <tt>range.start</tt> to the end:
- # rows = table[1..50] # => #<CSV::Row "Name":"bar" "Value":"1">
- # rows # => [#<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">]
- #
- # Special case: if <tt>range.start == table.size</tt>, returns an empty \Array:
- # table[table.size..50] # => []
- #
- # If <tt>range.end</tt> is negative, calculates the ending index from the end:
- # rows = table[0..-1]
- # rows # => [#<CSV::Row "Name":"foo" "Value":"0">, #<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">]
- #
- # If <tt>range.start</tt> is negative, calculates the starting index from the end:
- # rows = table[-1..2]
- # rows # => [#<CSV::Row "Name":"baz" "Value":"2">]
- #
- # If <tt>range.start</tt> is larger than <tt>table.size</tt>, returns +nil+:
- # table[4..4] # => nil
- #
- # ---
- #
- # Fetch Columns by \Range::
- # - Form: <tt>table[range]</tt>, +range+ a \Range object.
- # - Access mode: <tt>:col</tt>.
- # - Return value: column data from the table, beginning at column <tt>range.start</tt>,
- # if those columns exist.
- #
- # Returns column values from the table, if the column exists;
- # the values are arranged by row:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # table.by_col!
- # table[0..1] # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
- #
- # Special case: if <tt>range.start == headers.size</tt>,
- # returns an \Array (size: <tt>table.size</tt>) of empty \Arrays:
- # table[table.headers.size..50] # => [[], [], []]
- #
- # If <tt>range.end</tt> is negative, calculates the ending index from the end:
- # table[0..-1] # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
- #
- # If <tt>range.start</tt> is negative, calculates the starting index from the end:
- # table[-2..2] # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
- #
- # If <tt>range.start</tt> is larger than <tt>table.size</tt>,
- # returns an \Array of +nil+ values:
- # table[4..4] # => [nil, nil, nil]
- #
- # ---
- #
- # Fetch a Column by Its \String Header::
- # - Form: <tt>table[header]</tt>, +header+ a \String header.
- # - Access mode: <tt>:col</tt> or <tt>:col_or_row</tt>
- # - Return value: column data from the table, if that +header+ exists.
- #
- # Returns column values from the table, if the column exists:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # table.by_col! # => #<CSV::Table mode:col row_count:4>
- # table['Name'] # => ["foo", "bar", "baz"]
- # table.by_col_or_row! # => #<CSV::Table mode:col_or_row row_count:4>
- # col = table['Name']
- # col # => ["foo", "bar", "baz"]
- #
- # Modifying the returned column values does not modify the table:
- # col[0] = 'bat'
- # col # => ["bat", "bar", "baz"]
- # table['Name'] # => ["foo", "bar", "baz"]
- #
- # Returns an \Array of +nil+ values if there is no such column:
- # table['Nosuch'] # => [nil, nil, nil]
- def [](index_or_header)
- if @mode == :row or # by index
- (@mode == :col_or_row and (index_or_header.is_a?(Integer) or index_or_header.is_a?(Range)))
- @table[index_or_header]
- else # by header
- @table.map { |row| row[index_or_header] }
- end
- end
-
- # :call-seq:
- # table[n] = row -> row
- # table[n] = field_or_array_of_fields -> field_or_array_of_fields
- # table[header] = field_or_array_of_fields -> field_or_array_of_fields
- #
- # Puts data onto the table.
- #
- # ---
- #
- # Set a \Row by Its \Integer Index::
- # - Form: <tt>table[n] = row</tt>, +n+ an \Integer,
- # +row+ a \CSV::Row instance or an \Array of fields.
- # - Access mode: <tt>:row</tt> or <tt>:col_or_row</tt>.
- # - Return value: +row+.
- #
- # If the row exists, it is replaced:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # new_row = CSV::Row.new(['Name', 'Value'], ['bat', 3])
- # table.by_row! # => #<CSV::Table mode:row row_count:4>
- # return_value = table[0] = new_row
- # return_value.equal?(new_row) # => true # Returned the row
- # table[0].to_h # => {"Name"=>"bat", "Value"=>3}
- #
- # With access mode <tt>:col_or_row</tt>:
- # table.by_col_or_row! # => #<CSV::Table mode:col_or_row row_count:4>
- # table[0] = CSV::Row.new(['Name', 'Value'], ['bam', 4])
- # table[0].to_h # => {"Name"=>"bam", "Value"=>4}
- #
- # With an \Array instead of a \CSV::Row, inherits headers from the table:
- # array = ['bad', 5]
- # return_value = table[0] = array
- # return_value.equal?(array) # => true # Returned the array
- # table[0].to_h # => {"Name"=>"bad", "Value"=>5}
- #
- # If the row does not exist, extends the table by adding rows:
- # assigns rows with +nil+ as needed:
- # table.size # => 3
- # table[5] = ['bag', 6]
- # table.size # => 6
- # table[3] # => nil
- # table[4]# => nil
- # table[5].to_h # => {"Name"=>"bag", "Value"=>6}
- #
- # Note that the +nil+ rows are actually +nil+, not a row of +nil+ fields.
- #
- # ---
- #
- # Set a Column by Its \Integer Index::
- # - Form: <tt>table[n] = array_of_fields</tt>, +n+ an \Integer,
- # +array_of_fields+ an \Array of \String fields.
- # - Access mode: <tt>:col</tt>.
- # - Return value: +array_of_fields+.
- #
- # If the column exists, it is replaced:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # new_col = [3, 4, 5]
- # table.by_col! # => #<CSV::Table mode:col row_count:4>
- # return_value = table[1] = new_col
- # return_value.equal?(new_col) # => true # Returned the column
- # table[1] # => [3, 4, 5]
- # # The rows, as revised:
- # table.by_row! # => #<CSV::Table mode:row row_count:4>
- # table[0].to_h # => {"Name"=>"foo", "Value"=>3}
- # table[1].to_h # => {"Name"=>"bar", "Value"=>4}
- # table[2].to_h # => {"Name"=>"baz", "Value"=>5}
- # table.by_col! # => #<CSV::Table mode:col row_count:4>
- #
- # If there are too few values, fills with +nil+ values:
- # table[1] = [0]
- # table[1] # => [0, nil, nil]
- #
- # If there are too many values, ignores the extra values:
- # table[1] = [0, 1, 2, 3, 4]
- # table[1] # => [0, 1, 2]
- #
- # If a single value is given, replaces all fields in the column with that value:
- # table[1] = 'bat'
- # table[1] # => ["bat", "bat", "bat"]
- #
- # ---
- #
- # Set a Column by Its \String Header::
- # - Form: <tt>table[header] = field_or_array_of_fields</tt>,
- # +header+ a \String header, +field_or_array_of_fields+ a field value
- # or an \Array of \String fields.
- # - Access mode: <tt>:col</tt> or <tt>:col_or_row</tt>.
- # - Return value: +field_or_array_of_fields+.
- #
- # If the column exists, it is replaced:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # new_col = [3, 4, 5]
- # table.by_col! # => #<CSV::Table mode:col row_count:4>
- # return_value = table['Value'] = new_col
- # return_value.equal?(new_col) # => true # Returned the column
- # table['Value'] # => [3, 4, 5]
- # # The rows, as revised:
- # table.by_row! # => #<CSV::Table mode:row row_count:4>
- # table[0].to_h # => {"Name"=>"foo", "Value"=>3}
- # table[1].to_h # => {"Name"=>"bar", "Value"=>4}
- # table[2].to_h # => {"Name"=>"baz", "Value"=>5}
- # table.by_col! # => #<CSV::Table mode:col row_count:4>
- #
- # If there are too few values, fills with +nil+ values:
- # table['Value'] = [0]
- # table['Value'] # => [0, nil, nil]
- #
- # If there are too many values, ignores the extra values:
- # table['Value'] = [0, 1, 2, 3, 4]
- # table['Value'] # => [0, 1, 2]
- #
- # If the column does not exist, extends the table by adding columns:
- # table['Note'] = ['x', 'y', 'z']
- # table['Note'] # => ["x", "y", "z"]
- # # The rows, as revised:
- # table.by_row!
- # table[0].to_h # => {"Name"=>"foo", "Value"=>0, "Note"=>"x"}
- # table[1].to_h # => {"Name"=>"bar", "Value"=>1, "Note"=>"y"}
- # table[2].to_h # => {"Name"=>"baz", "Value"=>2, "Note"=>"z"}
- # table.by_col!
- #
- # If a single value is given, replaces all fields in the column with that value:
- # table['Value'] = 'bat'
- # table['Value'] # => ["bat", "bat", "bat"]
- def []=(index_or_header, value)
- if @mode == :row or # by index
- (@mode == :col_or_row and index_or_header.is_a? Integer)
- if value.is_a? Array
- @table[index_or_header] = Row.new(headers, value)
- else
- @table[index_or_header] = value
- end
- else # set column
- unless index_or_header.is_a? Integer
- index = @headers.index(index_or_header) || @headers.size
- @headers[index] = index_or_header
- end
- if value.is_a? Array # multiple values
- @table.each_with_index do |row, i|
- if row.header_row?
- row[index_or_header] = index_or_header
- else
- row[index_or_header] = value[i]
- end
- end
- else # repeated value
- @table.each do |row|
- if row.header_row?
- row[index_or_header] = index_or_header
- else
- row[index_or_header] = value
- end
- end
- end
- end
- end
-
- # :call-seq:
- # table.values_at(*indexes) -> array_of_rows
- # table.values_at(*headers) -> array_of_columns_data
- #
- # If the access mode is <tt>:row</tt> or <tt>:col_or_row</tt>,
- # and each argument is either an \Integer or a \Range,
- # returns rows.
- # Otherwise, returns columns data.
- #
- # In either case, the returned values are in the order
- # specified by the arguments. Arguments may be repeated.
- #
- # ---
- #
- # Returns rows as an \Array of \CSV::Row objects.
- #
- # No argument:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # table.values_at # => []
- #
- # One index:
- # values = table.values_at(0)
- # values # => [#<CSV::Row "Name":"foo" "Value":"0">]
- #
- # Two indexes:
- # values = table.values_at(2, 0)
- # values # => [#<CSV::Row "Name":"baz" "Value":"2">, #<CSV::Row "Name":"foo" "Value":"0">]
- #
- # One \Range:
- # values = table.values_at(1..2)
- # values # => [#<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">]
- #
- # \Ranges and indexes:
- # values = table.values_at(0..1, 1..2, 0, 2)
- # pp values
- # Output:
- # [#<CSV::Row "Name":"foo" "Value":"0">,
- # #<CSV::Row "Name":"bar" "Value":"1">,
- # #<CSV::Row "Name":"bar" "Value":"1">,
- # #<CSV::Row "Name":"baz" "Value":"2">,
- # #<CSV::Row "Name":"foo" "Value":"0">,
- # #<CSV::Row "Name":"baz" "Value":"2">]
- #
- # ---
- #
- # Returns columns data as row Arrays,
- # each consisting of the specified columns data for that row:
- # values = table.values_at('Name')
- # values # => [["foo"], ["bar"], ["baz"]]
- # values = table.values_at('Value', 'Name')
- # values # => [["0", "foo"], ["1", "bar"], ["2", "baz"]]
- def values_at(*indices_or_headers)
- if @mode == :row or # by indices
- ( @mode == :col_or_row and indices_or_headers.all? do |index|
- index.is_a?(Integer) or
- ( index.is_a?(Range) and
- index.first.is_a?(Integer) and
- index.last.is_a?(Integer) )
- end )
- @table.values_at(*indices_or_headers)
- else # by headers
- @table.map { |row| row.values_at(*indices_or_headers) }
- end
- end
-
- # :call-seq:
- # table << row_or_array -> self
- #
- # If +row_or_array+ is a \CSV::Row object,
- # it is appended to the table:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # table << CSV::Row.new(table.headers, ['bat', 3])
- # table[3] # => #<CSV::Row "Name":"bat" "Value":3>
- #
- # If +row_or_array+ is an \Array, it is used to create a new
- # \CSV::Row object which is then appended to the table:
- # table << ['bam', 4]
- # table[4] # => #<CSV::Row "Name":"bam" "Value":4>
- def <<(row_or_array)
- if row_or_array.is_a? Array # append Array
- @table << Row.new(headers, row_or_array)
- else # append Row
- @table << row_or_array
- end
-
- self # for chaining
- end
-
- #
- # :call-seq:
- # table.push(*rows_or_arrays) -> self
- #
- # A shortcut for appending multiple rows. Equivalent to:
- # rows.each {|row| self << row }
- #
- # Each argument may be either a \CSV::Row object or an \Array:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # rows = [
- # CSV::Row.new(table.headers, ['bat', 3]),
- # ['bam', 4]
- # ]
- # table.push(*rows)
- # table[3..4] # => [#<CSV::Row "Name":"bat" "Value":3>, #<CSV::Row "Name":"bam" "Value":4>]
- def push(*rows)
- rows.each { |row| self << row }
-
- self # for chaining
- end
-
- # :call-seq:
- # table.delete(*indexes) -> deleted_values
- # table.delete(*headers) -> deleted_values
- #
- # If the access mode is <tt>:row</tt> or <tt>:col_or_row</tt>,
- # and each argument is either an \Integer or a \Range,
- # returns deleted rows.
- # Otherwise, returns deleted columns data.
- #
- # In either case, the returned values are in the order
- # specified by the arguments. Arguments may be repeated.
- #
- # ---
- #
- # Returns rows as an \Array of \CSV::Row objects.
- #
- # One index:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # deleted_values = table.delete(0)
- # deleted_values # => [#<CSV::Row "Name":"foo" "Value":"0">]
- #
- # Two indexes:
- # table = CSV.parse(source, headers: true)
- # deleted_values = table.delete(2, 0)
- # deleted_values # => [#<CSV::Row "Name":"baz" "Value":"2">, #<CSV::Row "Name":"foo" "Value":"0">]
- #
- # ---
- #
- # Returns columns data as column Arrays.
- #
- # One header:
- # table = CSV.parse(source, headers: true)
- # deleted_values = table.delete('Name')
- # deleted_values # => ["foo", "bar", "baz"]
- #
- # Two headers:
- # table = CSV.parse(source, headers: true)
- # deleted_values = table.delete('Value', 'Name')
- # deleted_values # => [["0", "1", "2"], ["foo", "bar", "baz"]]
- def delete(*indexes_or_headers)
- if indexes_or_headers.empty?
- raise ArgumentError, "wrong number of arguments (given 0, expected 1+)"
- end
- deleted_values = indexes_or_headers.map do |index_or_header|
- if @mode == :row or # by index
- (@mode == :col_or_row and index_or_header.is_a? Integer)
- @table.delete_at(index_or_header)
- else # by header
- if index_or_header.is_a? Integer
- @headers.delete_at(index_or_header)
- else
- @headers.delete(index_or_header)
- end
- @table.map { |row| row.delete(index_or_header).last }
- end
- end
- if indexes_or_headers.size == 1
- deleted_values[0]
- else
- deleted_values
- end
- end
-
- # :call-seq:
- # table.delete_if {|row_or_column| ... } -> self
- #
- # Removes rows or columns for which the block returns a truthy value;
- # returns +self+.
- #
- # Removes rows when the access mode is <tt>:row</tt> or <tt>:col_or_row</tt>;
- # calls the block with each \CSV::Row object:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # table.by_row! # => #<CSV::Table mode:row row_count:4>
- # table.size # => 3
- # table.delete_if {|row| row['Name'].start_with?('b') }
- # table.size # => 1
- #
- # Removes columns when the access mode is <tt>:col</tt>;
- # calls the block with each column as a 2-element array
- # containing the header and an \Array of column fields:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # table.by_col! # => #<CSV::Table mode:col row_count:4>
- # table.headers.size # => 2
- # table.delete_if {|column_data| column_data[1].include?('2') }
- # table.headers.size # => 1
- #
- # Returns a new \Enumerator if no block is given:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # table.delete_if # => #<Enumerator: #<CSV::Table mode:col_or_row row_count:4>:delete_if>
- def delete_if(&block)
- return enum_for(__method__) { @mode == :row or @mode == :col_or_row ? size : headers.size } unless block_given?
-
- if @mode == :row or @mode == :col_or_row # by index
- @table.delete_if(&block)
- else # by header
- headers.each do |header|
- delete(header) if yield([header, self[header]])
- end
- end
-
- self # for chaining
- end
-
- include Enumerable
-
- # :call-seq:
- # table.each {|row_or_column| ... ) -> self
- #
- # Calls the block with each row or column; returns +self+.
- #
- # When the access mode is <tt>:row</tt> or <tt>:col_or_row</tt>,
- # calls the block with each \CSV::Row object:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # table.by_row! # => #<CSV::Table mode:row row_count:4>
- # table.each {|row| p row }
- # Output:
- # #<CSV::Row "Name":"foo" "Value":"0">
- # #<CSV::Row "Name":"bar" "Value":"1">
- # #<CSV::Row "Name":"baz" "Value":"2">
- #
- # When the access mode is <tt>:col</tt>,
- # calls the block with each column as a 2-element array
- # containing the header and an \Array of column fields:
- # table.by_col! # => #<CSV::Table mode:col row_count:4>
- # table.each {|column_data| p column_data }
- # Output:
- # ["Name", ["foo", "bar", "baz"]]
- # ["Value", ["0", "1", "2"]]
- #
- # Returns a new \Enumerator if no block is given:
- # table.each # => #<Enumerator: #<CSV::Table mode:col row_count:4>:each>
- def each(&block)
- return enum_for(__method__) { @mode == :col ? headers.size : size } unless block_given?
-
- if @mode == :col
- headers.each.with_index do |header, i|
- yield([header, @table.map {|row| row[header, i]}])
- end
- else
- @table.each(&block)
- end
-
- self # for chaining
- end
-
- # :call-seq:
- # table == other_table -> true or false
- #
- # Returns +true+ if all each row of +self+ <tt>==</tt>
- # the corresponding row of +other_table+, otherwise, +false+.
- #
- # The access mode does no affect the result.
- #
- # Equal tables:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # other_table = CSV.parse(source, headers: true)
- # table == other_table # => true
- #
- # Different row count:
- # other_table.delete(2)
- # table == other_table # => false
- #
- # Different last row:
- # other_table << ['bat', 3]
- # table == other_table # => false
- def ==(other)
- return @table == other.table if other.is_a? CSV::Table
- @table == other
- end
-
- # :call-seq:
- # table.to_a -> array_of_arrays
- #
- # Returns the table as an \Array of \Arrays;
- # the headers are in the first row:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # table.to_a # => [["Name", "Value"], ["foo", "0"], ["bar", "1"], ["baz", "2"]]
- def to_a
- array = [headers]
- @table.each do |row|
- array.push(row.fields) unless row.header_row?
- end
-
- array
- end
-
- # :call-seq:
- # table.to_csv(**options) -> csv_string
- #
- # Returns the table as \CSV string.
- # See {Options for Generating}[../CSV.html#class-CSV-label-Options+for+Generating].
- #
- # Defaults option +write_headers+ to +true+:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # table.to_csv # => "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- #
- # Omits the headers if option +write_headers+ is given as +false+
- # (see {Option +write_headers+}[../CSV.html#class-CSV-label-Option+write_headers]):
- # table.to_csv(write_headers: false) # => "foo,0\nbar,1\nbaz,2\n"
- #
- # Limit rows if option +limit+ is given like +2+:
- # table.to_csv(limit: 2) # => "Name,Value\nfoo,0\nbar,1\n"
- def to_csv(write_headers: true, limit: nil, **options)
- array = write_headers ? [headers.to_csv(**options)] : []
- limit ||= @table.size
- limit = @table.size + 1 + limit if limit < 0
- limit = 0 if limit < 0
- @table.first(limit).each do |row|
- array.push(row.fields.to_csv(**options)) unless row.header_row?
- end
-
- array.join("")
- end
- alias_method :to_s, :to_csv
-
- #
- # Extracts the nested value specified by the sequence of +index+ or +header+ objects by calling dig at each step,
- # returning nil if any intermediate step is nil.
- #
- def dig(index_or_header, *index_or_headers)
- value = self[index_or_header]
- if value.nil?
- nil
- elsif index_or_headers.empty?
- value
- else
- unless value.respond_to?(:dig)
- raise TypeError, "#{value.class} does not have \#dig method"
- end
- value.dig(*index_or_headers)
- end
- end
-
- # :call-seq:
- # table.inspect => string
- #
- # Returns a <tt>US-ASCII</tt>-encoded \String showing table:
- # - Class: <tt>CSV::Table</tt>.
- # - Access mode: <tt>:row</tt>, <tt>:col</tt>, or <tt>:col_or_row</tt>.
- # - Size: Row count, including the header row.
- #
- # Example:
- # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
- # table = CSV.parse(source, headers: true)
- # table.inspect # => "#<CSV::Table mode:col_or_row row_count:4>\nName,Value\nfoo,0\nbar,1\nbaz,2\n"
- #
- def inspect
- inspected = +"#<#{self.class} mode:#{@mode} row_count:#{to_a.size}>"
- summary = to_csv(limit: 5)
- inspected << "\n" << summary if summary.encoding.ascii_compatible?
- inspected
- end
- end
-end
diff --git a/lib/csv/version.rb b/lib/csv/version.rb
deleted file mode 100644
index edafc6f128..0000000000
--- a/lib/csv/version.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-# frozen_string_literal: true
-
-class CSV
- # The version of the installed library.
- VERSION = "3.2.7"
-end
diff --git a/lib/csv/writer.rb b/lib/csv/writer.rb
deleted file mode 100644
index 030a295bc9..0000000000
--- a/lib/csv/writer.rb
+++ /dev/null
@@ -1,210 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "input_record_separator"
-require_relative "row"
-
-class CSV
- # Note: Don't use this class directly. This is an internal class.
- class Writer
- #
- # A CSV::Writer receives an output, prepares the header, format and output.
- # It allows us to write new rows in the object and rewind it.
- #
- attr_reader :lineno
- attr_reader :headers
-
- def initialize(output, options)
- @output = output
- @options = options
- @lineno = 0
- @fields_converter = nil
- prepare
- if @options[:write_headers] and @headers
- self << @headers
- end
- @fields_converter = @options[:fields_converter]
- end
-
- #
- # Adds a new row
- #
- def <<(row)
- case row
- when Row
- row = row.fields
- when Hash
- row = @headers.collect {|header| row[header]}
- end
-
- @headers ||= row if @use_headers
- @lineno += 1
-
- if @fields_converter
- quoted_fields = [false] * row.size
- row = @fields_converter.convert(row, nil, lineno, quoted_fields)
- end
-
- i = -1
- converted_row = row.collect do |field|
- i += 1
- quote(field, i)
- end
- line = converted_row.join(@column_separator) + @row_separator
- if @output_encoding
- line = line.encode(@output_encoding)
- end
- @output << line
-
- self
- end
-
- #
- # Winds back to the beginning
- #
- def rewind
- @lineno = 0
- @headers = nil if @options[:headers].nil?
- end
-
- private
- def prepare
- @encoding = @options[:encoding]
-
- prepare_header
- prepare_format
- prepare_output
- end
-
- def prepare_header
- headers = @options[:headers]
- case headers
- when Array
- @headers = headers
- @use_headers = true
- when String
- @headers = CSV.parse_line(headers,
- col_sep: @options[:column_separator],
- row_sep: @options[:row_separator],
- quote_char: @options[:quote_character])
- @use_headers = true
- when true
- @headers = nil
- @use_headers = true
- else
- @headers = nil
- @use_headers = false
- end
- return unless @headers
-
- converter = @options[:header_fields_converter]
- @headers = converter.convert(@headers, nil, 0, [])
- @headers.each do |header|
- header.freeze if header.is_a?(String)
- end
- end
-
- def prepare_force_quotes_fields(force_quotes)
- @force_quotes_fields = {}
- force_quotes.each do |name_or_index|
- case name_or_index
- when Integer
- index = name_or_index
- @force_quotes_fields[index] = true
- when String, Symbol
- name = name_or_index.to_s
- if @headers.nil?
- message = ":headers is required when you use field name " +
- "in :force_quotes: " +
- "#{name_or_index.inspect}: #{force_quotes.inspect}"
- raise ArgumentError, message
- end
- index = @headers.index(name)
- next if index.nil?
- @force_quotes_fields[index] = true
- else
- message = ":force_quotes element must be " +
- "field index or field name: " +
- "#{name_or_index.inspect}: #{force_quotes.inspect}"
- raise ArgumentError, message
- end
- end
- end
-
- def prepare_format
- @column_separator = @options[:column_separator].to_s.encode(@encoding)
- row_separator = @options[:row_separator]
- if row_separator == :auto
- @row_separator = InputRecordSeparator.value.encode(@encoding)
- else
- @row_separator = row_separator.to_s.encode(@encoding)
- end
- @quote_character = @options[:quote_character]
- force_quotes = @options[:force_quotes]
- if force_quotes.is_a?(Array)
- prepare_force_quotes_fields(force_quotes)
- @force_quotes = false
- elsif force_quotes
- @force_quotes_fields = nil
- @force_quotes = true
- else
- @force_quotes_fields = nil
- @force_quotes = false
- end
- unless @force_quotes
- @quotable_pattern =
- Regexp.new("[\r\n".encode(@encoding) +
- Regexp.escape(@column_separator) +
- Regexp.escape(@quote_character.encode(@encoding)) +
- "]".encode(@encoding))
- end
- @quote_empty = @options.fetch(:quote_empty, true)
- end
-
- def prepare_output
- @output_encoding = nil
- return unless @output.is_a?(StringIO)
-
- output_encoding = @output.internal_encoding || @output.external_encoding
- if @encoding != output_encoding
- if @options[:force_encoding]
- @output_encoding = output_encoding
- else
- compatible_encoding = Encoding.compatible?(@encoding, output_encoding)
- if compatible_encoding
- @output.set_encoding(compatible_encoding)
- @output.seek(0, IO::SEEK_END)
- end
- end
- end
- end
-
- def quote_field(field)
- field = String(field)
- encoded_quote_character = @quote_character.encode(field.encoding)
- encoded_quote_character +
- field.gsub(encoded_quote_character,
- encoded_quote_character * 2) +
- encoded_quote_character
- end
-
- def quote(field, i)
- if @force_quotes
- quote_field(field)
- elsif @force_quotes_fields and @force_quotes_fields[i]
- quote_field(field)
- else
- if field.nil? # represent +nil+ fields as empty unquoted fields
- ""
- else
- field = String(field) # Stringify fields
- # represent empty fields as empty quoted fields
- if (@quote_empty and field.empty?) or (field.valid_encoding? and @quotable_pattern.match?(field))
- quote_field(field)
- else
- field # unquoted field
- end
- end
- end
- end
- end
-end
diff --git a/lib/delegate.rb b/lib/delegate.rb
index 387a5f063d..1ea4fb985b 100644
--- a/lib/delegate.rb
+++ b/lib/delegate.rb
@@ -39,7 +39,7 @@
# Be advised, RDoc will not detect delegated methods.
#
class Delegator < BasicObject
- VERSION = "0.3.0"
+ VERSION = "0.3.1"
kernel = ::Kernel.dup
kernel.class_eval do
@@ -186,7 +186,7 @@ class Delegator < BasicObject
# method calls are being delegated to.
#
def __getobj__
- __raise__ ::NotImplementedError, "need to define `__getobj__'"
+ __raise__ ::NotImplementedError, "need to define '__getobj__'"
end
#
@@ -194,7 +194,7 @@ class Delegator < BasicObject
# to _obj_.
#
def __setobj__(obj)
- __raise__ ::NotImplementedError, "need to define `__setobj__'"
+ __raise__ ::NotImplementedError, "need to define '__setobj__'"
end
#
diff --git a/lib/did_you_mean/jaro_winkler.rb b/lib/did_you_mean/jaro_winkler.rb
index 56db130af4..9a3e57f6d7 100644
--- a/lib/did_you_mean/jaro_winkler.rb
+++ b/lib/did_you_mean/jaro_winkler.rb
@@ -8,8 +8,7 @@ module DidYouMean
m = 0.0
t = 0.0
- range = (length2 / 2).floor - 1
- range = 0 if range < 0
+ range = length2 > 3 ? length2 / 2 - 1 : 0
flags1 = 0
flags2 = 0
@@ -72,10 +71,8 @@ module DidYouMean
codepoints2 = str2.codepoints
prefix_bonus = 0
- i = 0
str1.each_codepoint do |char1|
- char1 == codepoints2[i] && i < 4 ? prefix_bonus += 1 : break
- i += 1
+ char1 == codepoints2[prefix_bonus] && prefix_bonus < 4 ? prefix_bonus += 1 : break
end
jaro_distance + (prefix_bonus * WEIGHT * (1 - jaro_distance))
diff --git a/lib/did_you_mean/spell_checkers/key_error_checker.rb b/lib/did_you_mean/spell_checkers/key_error_checker.rb
index be4bea7789..955bff1be6 100644
--- a/lib/did_you_mean/spell_checkers/key_error_checker.rb
+++ b/lib/did_you_mean/spell_checkers/key_error_checker.rb
@@ -14,7 +14,15 @@ module DidYouMean
private
def exact_matches
- @exact_matches ||= @keys.select { |word| @key == word.to_s }.map(&:inspect)
+ @exact_matches ||= @keys.select { |word| @key == word.to_s }.map { |obj| format_object(obj) }
+ end
+
+ def format_object(symbol_or_object)
+ if symbol_or_object.is_a?(Symbol)
+ ":#{symbol_or_object}"
+ else
+ symbol_or_object.to_s
+ end
end
end
end
diff --git a/lib/did_you_mean/spell_checkers/pattern_key_name_checker.rb b/lib/did_you_mean/spell_checkers/pattern_key_name_checker.rb
index ed263c8f93..622d4dee25 100644
--- a/lib/did_you_mean/spell_checkers/pattern_key_name_checker.rb
+++ b/lib/did_you_mean/spell_checkers/pattern_key_name_checker.rb
@@ -14,7 +14,15 @@ module DidYouMean
private
def exact_matches
- @exact_matches ||= @keys.select { |word| @key == word.to_s }.map(&:inspect)
+ @exact_matches ||= @keys.select { |word| @key == word.to_s }.map { |obj| format_object(obj) }
+ end
+
+ def format_object(symbol_or_object)
+ if symbol_or_object.is_a?(Symbol)
+ ":#{symbol_or_object}"
+ else
+ symbol_or_object.to_s
+ end
end
end
end
diff --git a/lib/drb.rb b/lib/drb.rb
deleted file mode 100644
index 2bb4716fa2..0000000000
--- a/lib/drb.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-# frozen_string_literal: false
-require 'drb/drb'
-
diff --git a/lib/drb/acl.rb b/lib/drb/acl.rb
deleted file mode 100644
index b004656f09..0000000000
--- a/lib/drb/acl.rb
+++ /dev/null
@@ -1,239 +0,0 @@
-# frozen_string_literal: false
-# Copyright (c) 2000,2002,2003 Masatoshi SEKI
-#
-# acl.rb is copyrighted free software by Masatoshi SEKI.
-# You can redistribute it and/or modify it under the same terms as Ruby.
-
-require 'ipaddr'
-
-##
-# Simple Access Control Lists.
-#
-# Access control lists are composed of "allow" and "deny" halves to control
-# access. Use "all" or "*" to match any address. To match a specific address
-# use any address or address mask that IPAddr can understand.
-#
-# Example:
-#
-# list = %w[
-# deny all
-# allow 192.168.1.1
-# allow ::ffff:192.168.1.2
-# allow 192.168.1.3
-# ]
-#
-# # From Socket#peeraddr, see also ACL#allow_socket?
-# addr = ["AF_INET", 10, "lc630", "192.168.1.3"]
-#
-# acl = ACL.new
-# p acl.allow_addr?(addr) # => true
-#
-# acl = ACL.new(list, ACL::DENY_ALLOW)
-# p acl.allow_addr?(addr) # => true
-
-class ACL
-
- ##
- # The current version of ACL
-
- VERSION=["2.0.0"]
-
- ##
- # An entry in an ACL
-
- class ACLEntry
-
- ##
- # Creates a new entry using +str+.
- #
- # +str+ may be "*" or "all" to match any address, an IP address string
- # to match a specific address, an IP address mask per IPAddr, or one
- # containing "*" to match part of an IPv4 address.
- #
- # IPAddr::InvalidPrefixError may be raised when an IP network
- # address with an invalid netmask/prefix is given.
-
- def initialize(str)
- if str == '*' or str == 'all'
- @pat = [:all]
- elsif str.include?('*')
- @pat = [:name, dot_pat(str)]
- else
- begin
- @pat = [:ip, IPAddr.new(str)]
- rescue IPAddr::InvalidPrefixError
- # In this case, `str` shouldn't be a host name pattern
- # because it contains a slash.
- raise
- rescue ArgumentError
- @pat = [:name, dot_pat(str)]
- end
- end
- end
-
- private
-
- ##
- # Creates a regular expression to match IPv4 addresses
-
- def dot_pat_str(str)
- list = str.split('.').collect { |s|
- (s == '*') ? '.+' : s
- }
- list.join("\\.")
- end
-
- private
-
- ##
- # Creates a Regexp to match an address.
-
- def dot_pat(str)
- /\A#{dot_pat_str(str)}\z/
- end
-
- public
-
- ##
- # Matches +addr+ against this entry.
-
- def match(addr)
- case @pat[0]
- when :all
- true
- when :ip
- begin
- ipaddr = IPAddr.new(addr[3])
- ipaddr = ipaddr.ipv4_mapped if @pat[1].ipv6? && ipaddr.ipv4?
- rescue ArgumentError
- return false
- end
- (@pat[1].include?(ipaddr)) ? true : false
- when :name
- (@pat[1] =~ addr[2]) ? true : false
- else
- false
- end
- end
- end
-
- ##
- # A list of ACLEntry objects. Used to implement the allow and deny halves
- # of an ACL
-
- class ACLList
-
- ##
- # Creates an empty ACLList
-
- def initialize
- @list = []
- end
-
- public
-
- ##
- # Matches +addr+ against each ACLEntry in this list.
-
- def match(addr)
- @list.each do |e|
- return true if e.match(addr)
- end
- false
- end
-
- public
-
- ##
- # Adds +str+ as an ACLEntry in this list
-
- def add(str)
- @list.push(ACLEntry.new(str))
- end
-
- end
-
- ##
- # Default to deny
-
- DENY_ALLOW = 0
-
- ##
- # Default to allow
-
- ALLOW_DENY = 1
-
- ##
- # Creates a new ACL from +list+ with an evaluation +order+ of DENY_ALLOW or
- # ALLOW_DENY.
- #
- # An ACL +list+ is an Array of "allow" or "deny" and an address or address
- # mask or "all" or "*" to match any address:
- #
- # %w[
- # deny all
- # allow 192.0.2.2
- # allow 192.0.2.128/26
- # ]
-
- def initialize(list=nil, order = DENY_ALLOW)
- @order = order
- @deny = ACLList.new
- @allow = ACLList.new
- install_list(list) if list
- end
-
- public
-
- ##
- # Allow connections from Socket +soc+?
-
- def allow_socket?(soc)
- allow_addr?(soc.peeraddr)
- end
-
- public
-
- ##
- # Allow connections from addrinfo +addr+? It must be formatted like
- # Socket#peeraddr:
- #
- # ["AF_INET", 10, "lc630", "192.0.2.1"]
-
- def allow_addr?(addr)
- case @order
- when DENY_ALLOW
- return true if @allow.match(addr)
- return false if @deny.match(addr)
- return true
- when ALLOW_DENY
- return false if @deny.match(addr)
- return true if @allow.match(addr)
- return false
- else
- false
- end
- end
-
- public
-
- ##
- # Adds +list+ of ACL entries to this ACL.
-
- def install_list(list)
- i = 0
- while i < list.size
- permission, domain = list.slice(i,2)
- case permission.downcase
- when 'allow'
- @allow.add(domain)
- when 'deny'
- @deny.add(domain)
- else
- raise "Invalid ACL entry #{list}"
- end
- i += 2
- end
- end
-
-end
diff --git a/lib/drb/drb.gemspec b/lib/drb/drb.gemspec
deleted file mode 100644
index c9d7e40a51..0000000000
--- a/lib/drb/drb.gemspec
+++ /dev/null
@@ -1,43 +0,0 @@
-begin
- require_relative "lib/drb/version"
-rescue LoadError # Fallback to load version file in ruby core repository
- require_relative "version"
-end
-
-Gem::Specification.new do |spec|
- spec.name = "drb"
- spec.version = DRb::VERSION
- spec.authors = ["Masatoshi SEKI"]
- spec.email = ["seki@ruby-lang.org"]
-
- spec.summary = %q{Distributed object system for Ruby}
- spec.description = %q{Distributed object system for Ruby}
- spec.homepage = "https://github.com/ruby/drb"
- spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
- spec.licenses = ["Ruby", "BSD-2-Clause"]
-
- spec.metadata["homepage_uri"] = spec.homepage
- spec.metadata["source_code_uri"] = spec.homepage
-
- spec.files = %w[
- LICENSE.txt
- drb.gemspec
- lib/drb.rb
- lib/drb/acl.rb
- lib/drb/drb.rb
- lib/drb/eq.rb
- lib/drb/extserv.rb
- lib/drb/extservm.rb
- lib/drb/gw.rb
- lib/drb/invokemethod.rb
- lib/drb/observer.rb
- lib/drb/ssl.rb
- lib/drb/timeridconv.rb
- lib/drb/unix.rb
- lib/drb/version.rb
- lib/drb/weakidconv.rb
- ]
- spec.require_paths = ["lib"]
-
- spec.add_dependency "ruby2_keywords"
-end
diff --git a/lib/drb/drb.rb b/lib/drb/drb.rb
deleted file mode 100644
index 23181bb834..0000000000
--- a/lib/drb/drb.rb
+++ /dev/null
@@ -1,1942 +0,0 @@
-# frozen_string_literal: false
-#
-# = drb/drb.rb
-#
-# Distributed Ruby: _dRuby_ version 2.0.4
-#
-# Copyright (c) 1999-2003 Masatoshi SEKI. You can redistribute it and/or
-# modify it under the same terms as Ruby.
-#
-# Author:: Masatoshi SEKI
-#
-# Documentation:: William Webber (william@williamwebber.com)
-#
-# == Overview
-#
-# dRuby is a distributed object system for Ruby. It allows an object in one
-# Ruby process to invoke methods on an object in another Ruby process on the
-# same or a different machine.
-#
-# The Ruby standard library contains the core classes of the dRuby package.
-# However, the full package also includes access control lists and the
-# Rinda tuple-space distributed task management system, as well as a
-# large number of samples. The full dRuby package can be downloaded from
-# the dRuby home page (see *References*).
-#
-# For an introduction and examples of usage see the documentation to the
-# DRb module.
-#
-# == References
-#
-# [http://www2a.biglobe.ne.jp/~seki/ruby/druby.html]
-# The dRuby home page, in Japanese. Contains the full dRuby package
-# and links to other Japanese-language sources.
-#
-# [http://www2a.biglobe.ne.jp/~seki/ruby/druby.en.html]
-# The English version of the dRuby home page.
-#
-# [http://pragprog.com/book/sidruby/the-druby-book]
-# The dRuby Book: Distributed and Parallel Computing with Ruby
-# by Masatoshi Seki and Makoto Inoue
-#
-# [http://www.ruby-doc.org/docs/ProgrammingRuby/html/ospace.html]
-# The chapter from *Programming* *Ruby* by Dave Thomas and Andy Hunt
-# which discusses dRuby.
-#
-# [http://www.clio.ne.jp/home/web-i31s/Flotuard/Ruby/PRC2K_seki/dRuby.en.html]
-# Translation of presentation on Ruby by Masatoshi Seki.
-
-require 'socket'
-require 'io/wait'
-require 'monitor'
-require_relative 'eq'
-
-#
-# == Overview
-#
-# dRuby is a distributed object system for Ruby. It is written in
-# pure Ruby and uses its own protocol. No add-in services are needed
-# beyond those provided by the Ruby runtime, such as TCP sockets. It
-# does not rely on or interoperate with other distributed object
-# systems such as CORBA, RMI, or .NET.
-#
-# dRuby allows methods to be called in one Ruby process upon a Ruby
-# object located in another Ruby process, even on another machine.
-# References to objects can be passed between processes. Method
-# arguments and return values are dumped and loaded in marshalled
-# format. All of this is done transparently to both the caller of the
-# remote method and the object that it is called upon.
-#
-# An object in a remote process is locally represented by a
-# DRb::DRbObject instance. This acts as a sort of proxy for the
-# remote object. Methods called upon this DRbObject instance are
-# forwarded to its remote object. This is arranged dynamically at run
-# time. There are no statically declared interfaces for remote
-# objects, such as CORBA's IDL.
-#
-# dRuby calls made into a process are handled by a DRb::DRbServer
-# instance within that process. This reconstitutes the method call,
-# invokes it upon the specified local object, and returns the value to
-# the remote caller. Any object can receive calls over dRuby. There
-# is no need to implement a special interface, or mixin special
-# functionality. Nor, in the general case, does an object need to
-# explicitly register itself with a DRbServer in order to receive
-# dRuby calls.
-#
-# One process wishing to make dRuby calls upon another process must
-# somehow obtain an initial reference to an object in the remote
-# process by some means other than as the return value of a remote
-# method call, as there is initially no remote object reference it can
-# invoke a method upon. This is done by attaching to the server by
-# URI. Each DRbServer binds itself to a URI such as
-# 'druby://example.com:8787'. A DRbServer can have an object attached
-# to it that acts as the server's *front* *object*. A DRbObject can
-# be explicitly created from the server's URI. This DRbObject's
-# remote object will be the server's front object. This front object
-# can then return references to other Ruby objects in the DRbServer's
-# process.
-#
-# Method calls made over dRuby behave largely the same as normal Ruby
-# method calls made within a process. Method calls with blocks are
-# supported, as are raising exceptions. In addition to a method's
-# standard errors, a dRuby call may also raise one of the
-# dRuby-specific errors, all of which are subclasses of DRb::DRbError.
-#
-# Any type of object can be passed as an argument to a dRuby call or
-# returned as its return value. By default, such objects are dumped
-# or marshalled at the local end, then loaded or unmarshalled at the
-# remote end. The remote end therefore receives a copy of the local
-# object, not a distributed reference to it; methods invoked upon this
-# copy are executed entirely in the remote process, not passed on to
-# the local original. This has semantics similar to pass-by-value.
-#
-# However, if an object cannot be marshalled, a dRuby reference to it
-# is passed or returned instead. This will turn up at the remote end
-# as a DRbObject instance. All methods invoked upon this remote proxy
-# are forwarded to the local object, as described in the discussion of
-# DRbObjects. This has semantics similar to the normal Ruby
-# pass-by-reference.
-#
-# The easiest way to signal that we want an otherwise marshallable
-# object to be passed or returned as a DRbObject reference, rather
-# than marshalled and sent as a copy, is to include the
-# DRb::DRbUndumped mixin module.
-#
-# dRuby supports calling remote methods with blocks. As blocks (or
-# rather the Proc objects that represent them) are not marshallable,
-# the block executes in the local, not the remote, context. Each
-# value yielded to the block is passed from the remote object to the
-# local block, then the value returned by each block invocation is
-# passed back to the remote execution context to be collected, before
-# the collected values are finally returned to the local context as
-# the return value of the method invocation.
-#
-# == Examples of usage
-#
-# For more dRuby samples, see the +samples+ directory in the full
-# dRuby distribution.
-#
-# === dRuby in client/server mode
-#
-# This illustrates setting up a simple client-server drb
-# system. Run the server and client code in different terminals,
-# starting the server code first.
-#
-# ==== Server code
-#
-# require 'drb/drb'
-#
-# # The URI for the server to connect to
-# URI="druby://localhost:8787"
-#
-# class TimeServer
-#
-# def get_current_time
-# return Time.now
-# end
-#
-# end
-#
-# # The object that handles requests on the server
-# FRONT_OBJECT=TimeServer.new
-#
-# DRb.start_service(URI, FRONT_OBJECT)
-# # Wait for the drb server thread to finish before exiting.
-# DRb.thread.join
-#
-# ==== Client code
-#
-# require 'drb/drb'
-#
-# # The URI to connect to
-# SERVER_URI="druby://localhost:8787"
-#
-# # Start a local DRbServer to handle callbacks.
-# #
-# # Not necessary for this small example, but will be required
-# # as soon as we pass a non-marshallable object as an argument
-# # to a dRuby call.
-# #
-# # Note: this must be called at least once per process to take any effect.
-# # This is particularly important if your application forks.
-# DRb.start_service
-#
-# timeserver = DRbObject.new_with_uri(SERVER_URI)
-# puts timeserver.get_current_time
-#
-# === Remote objects under dRuby
-#
-# This example illustrates returning a reference to an object
-# from a dRuby call. The Logger instances live in the server
-# process. References to them are returned to the client process,
-# where methods can be invoked upon them. These methods are
-# executed in the server process.
-#
-# ==== Server code
-#
-# require 'drb/drb'
-#
-# URI="druby://localhost:8787"
-#
-# class Logger
-#
-# # Make dRuby send Logger instances as dRuby references,
-# # not copies.
-# include DRb::DRbUndumped
-#
-# def initialize(n, fname)
-# @name = n
-# @filename = fname
-# end
-#
-# def log(message)
-# File.open(@filename, "a") do |f|
-# f.puts("#{Time.now}: #{@name}: #{message}")
-# end
-# end
-#
-# end
-#
-# # We have a central object for creating and retrieving loggers.
-# # This retains a local reference to all loggers created. This
-# # is so an existing logger can be looked up by name, but also
-# # to prevent loggers from being garbage collected. A dRuby
-# # reference to an object is not sufficient to prevent it being
-# # garbage collected!
-# class LoggerFactory
-#
-# def initialize(bdir)
-# @basedir = bdir
-# @loggers = {}
-# end
-#
-# def get_logger(name)
-# if !@loggers.has_key? name
-# # make the filename safe, then declare it to be so
-# fname = name.gsub(/[.\/\\\:]/, "_")
-# @loggers[name] = Logger.new(name, @basedir + "/" + fname)
-# end
-# return @loggers[name]
-# end
-#
-# end
-#
-# FRONT_OBJECT=LoggerFactory.new("/tmp/dlog")
-#
-# DRb.start_service(URI, FRONT_OBJECT)
-# DRb.thread.join
-#
-# ==== Client code
-#
-# require 'drb/drb'
-#
-# SERVER_URI="druby://localhost:8787"
-#
-# DRb.start_service
-#
-# log_service=DRbObject.new_with_uri(SERVER_URI)
-#
-# ["loga", "logb", "logc"].each do |logname|
-#
-# logger=log_service.get_logger(logname)
-#
-# logger.log("Hello, world!")
-# logger.log("Goodbye, world!")
-# logger.log("=== EOT ===")
-#
-# end
-#
-# == Security
-#
-# As with all network services, security needs to be considered when
-# using dRuby. By allowing external access to a Ruby object, you are
-# not only allowing outside clients to call the methods you have
-# defined for that object, but by default to execute arbitrary Ruby
-# code on your server. Consider the following:
-#
-# # !!! UNSAFE CODE !!!
-# ro = DRbObject::new_with_uri("druby://your.server.com:8989")
-# class << ro
-# undef :instance_eval # force call to be passed to remote object
-# end
-# ro.instance_eval("`rm -rf *`")
-#
-# The dangers posed by instance_eval and friends are such that a
-# DRbServer should only be used when clients are trusted.
-#
-# A DRbServer can be configured with an access control list to
-# selectively allow or deny access from specified IP addresses. The
-# main druby distribution provides the ACL class for this purpose. In
-# general, this mechanism should only be used alongside, rather than
-# as a replacement for, a good firewall.
-#
-# == dRuby internals
-#
-# dRuby is implemented using three main components: a remote method
-# call marshaller/unmarshaller; a transport protocol; and an
-# ID-to-object mapper. The latter two can be directly, and the first
-# indirectly, replaced, in order to provide different behaviour and
-# capabilities.
-#
-# Marshalling and unmarshalling of remote method calls is performed by
-# a DRb::DRbMessage instance. This uses the Marshal module to dump
-# the method call before sending it over the transport layer, then
-# reconstitute it at the other end. There is normally no need to
-# replace this component, and no direct way is provided to do so.
-# However, it is possible to implement an alternative marshalling
-# scheme as part of an implementation of the transport layer.
-#
-# The transport layer is responsible for opening client and server
-# network connections and forwarding dRuby request across them.
-# Normally, it uses DRb::DRbMessage internally to manage marshalling
-# and unmarshalling. The transport layer is managed by
-# DRb::DRbProtocol. Multiple protocols can be installed in
-# DRbProtocol at the one time; selection between them is determined by
-# the scheme of a dRuby URI. The default transport protocol is
-# selected by the scheme 'druby:', and implemented by
-# DRb::DRbTCPSocket. This uses plain TCP/IP sockets for
-# communication. An alternative protocol, using UNIX domain sockets,
-# is implemented by DRb::DRbUNIXSocket in the file drb/unix.rb, and
-# selected by the scheme 'drbunix:'. A sample implementation over
-# HTTP can be found in the samples accompanying the main dRuby
-# distribution.
-#
-# The ID-to-object mapping component maps dRuby object ids to the
-# objects they refer to, and vice versa. The implementation to use
-# can be specified as part of a DRb::DRbServer's configuration. The
-# default implementation is provided by DRb::DRbIdConv. It uses an
-# object's ObjectSpace id as its dRuby id. This means that the dRuby
-# reference to that object only remains meaningful for the lifetime of
-# the object's process and the lifetime of the object within that
-# process. A modified implementation is provided by DRb::TimerIdConv
-# in the file drb/timeridconv.rb. This implementation retains a local
-# reference to all objects exported over dRuby for a configurable
-# period of time (defaulting to ten minutes), to prevent them being
-# garbage-collected within this time. Another sample implementation
-# is provided in sample/name.rb in the main dRuby distribution. This
-# allows objects to specify their own id or "name". A dRuby reference
-# can be made persistent across processes by having each process
-# register an object using the same dRuby name.
-#
-module DRb
-
- # Superclass of all errors raised in the DRb module.
- class DRbError < RuntimeError; end
-
- # Error raised when an error occurs on the underlying communication
- # protocol.
- class DRbConnError < DRbError; end
-
- # Class responsible for converting between an object and its id.
- #
- # This, the default implementation, uses an object's local ObjectSpace
- # __id__ as its id. This means that an object's identification over
- # drb remains valid only while that object instance remains alive
- # within the server runtime.
- #
- # For alternative mechanisms, see DRb::TimerIdConv in drb/timeridconv.rb
- # and DRbNameIdConv in sample/name.rb in the full drb distribution.
- class DRbIdConv
-
- # Convert an object reference id to an object.
- #
- # This implementation looks up the reference id in the local object
- # space and returns the object it refers to.
- def to_obj(ref)
- ObjectSpace._id2ref(ref)
- end
-
- # Convert an object into a reference id.
- #
- # This implementation returns the object's __id__ in the local
- # object space.
- def to_id(obj)
- case obj
- when Object
- obj.nil? ? nil : obj.__id__
- when BasicObject
- obj.__id__
- end
- end
- end
-
- # Mixin module making an object undumpable or unmarshallable.
- #
- # If an object which includes this module is returned by method
- # called over drb, then the object remains in the server space
- # and a reference to the object is returned, rather than the
- # object being marshalled and moved into the client space.
- module DRbUndumped
- def _dump(dummy) # :nodoc:
- raise TypeError, 'can\'t dump'
- end
- end
-
- # Error raised by the DRb module when an attempt is made to refer to
- # the context's current drb server but the context does not have one.
- # See #current_server.
- class DRbServerNotFound < DRbError; end
-
- # Error raised by the DRbProtocol module when it cannot find any
- # protocol implementation support the scheme specified in a URI.
- class DRbBadURI < DRbError; end
-
- # Error raised by a dRuby protocol when it doesn't support the
- # scheme specified in a URI. See DRb::DRbProtocol.
- class DRbBadScheme < DRbError; end
-
- # An exception wrapping a DRb::DRbUnknown object
- class DRbUnknownError < DRbError
-
- # Create a new DRbUnknownError for the DRb::DRbUnknown object +unknown+
- def initialize(unknown)
- @unknown = unknown
- super(unknown.name)
- end
-
- # Get the wrapped DRb::DRbUnknown object.
- attr_reader :unknown
-
- def self._load(s) # :nodoc:
- Marshal::load(s)
- end
-
- def _dump(lv) # :nodoc:
- Marshal::dump(@unknown)
- end
- end
-
- # An exception wrapping an error object
- class DRbRemoteError < DRbError
-
- # Creates a new remote error that wraps the Exception +error+
- def initialize(error)
- @reason = error.class.to_s
- super("#{error.message} (#{error.class})")
- set_backtrace(error.backtrace)
- end
-
- # the class of the error, as a string.
- attr_reader :reason
- end
-
- # Class wrapping a marshalled object whose type is unknown locally.
- #
- # If an object is returned by a method invoked over drb, but the
- # class of the object is unknown in the client namespace, or
- # the object is a constant unknown in the client namespace, then
- # the still-marshalled object is returned wrapped in a DRbUnknown instance.
- #
- # If this object is passed as an argument to a method invoked over
- # drb, then the wrapped object is passed instead.
- #
- # The class or constant name of the object can be read from the
- # +name+ attribute. The marshalled object is held in the +buf+
- # attribute.
- class DRbUnknown
-
- # Create a new DRbUnknown object.
- #
- # +buf+ is a string containing a marshalled object that could not
- # be unmarshalled. +err+ is the error message that was raised
- # when the unmarshalling failed. It is used to determine the
- # name of the unmarshalled object.
- def initialize(err, buf)
- case err.to_s
- when /uninitialized constant (\S+)/
- @name = $1
- when /undefined class\/module (\S+)/
- @name = $1
- else
- @name = nil
- end
- @buf = buf
- end
-
- # The name of the unknown thing.
- #
- # Class name for unknown objects; variable name for unknown
- # constants.
- attr_reader :name
-
- # Buffer contained the marshalled, unknown object.
- attr_reader :buf
-
- def self._load(s) # :nodoc:
- begin
- Marshal::load(s)
- rescue NameError, ArgumentError
- DRbUnknown.new($!, s)
- end
- end
-
- def _dump(lv) # :nodoc:
- @buf
- end
-
- # Attempt to load the wrapped marshalled object again.
- #
- # If the class of the object is now known locally, the object
- # will be unmarshalled and returned. Otherwise, a new
- # but identical DRbUnknown object will be returned.
- def reload
- self.class._load(@buf)
- end
-
- # Create a DRbUnknownError exception containing this object.
- def exception
- DRbUnknownError.new(self)
- end
- end
-
- # An Array wrapper that can be sent to another server via DRb.
- #
- # All entries in the array will be dumped or be references that point to
- # the local server.
-
- class DRbArray
-
- # Creates a new DRbArray that either dumps or wraps all the items in the
- # Array +ary+ so they can be loaded by a remote DRb server.
-
- def initialize(ary)
- @ary = ary.collect { |obj|
- if obj.kind_of? DRbUndumped
- DRbObject.new(obj)
- else
- begin
- Marshal.dump(obj)
- obj
- rescue
- DRbObject.new(obj)
- end
- end
- }
- end
-
- def self._load(s) # :nodoc:
- Marshal::load(s)
- end
-
- def _dump(lv) # :nodoc:
- Marshal.dump(@ary)
- end
- end
-
- # Handler for sending and receiving drb messages.
- #
- # This takes care of the low-level marshalling and unmarshalling
- # of drb requests and responses sent over the wire between server
- # and client. This relieves the implementor of a new drb
- # protocol layer with having to deal with these details.
- #
- # The user does not have to directly deal with this object in
- # normal use.
- class DRbMessage
- def initialize(config) # :nodoc:
- @load_limit = config[:load_limit]
- @argc_limit = config[:argc_limit]
- end
-
- def dump(obj, error=false) # :nodoc:
- case obj
- when DRbUndumped
- obj = make_proxy(obj, error)
- when Object
- # nothing
- else
- obj = make_proxy(obj, error)
- end
- begin
- str = Marshal::dump(obj)
- rescue
- str = Marshal::dump(make_proxy(obj, error))
- end
- [str.size].pack('N') + str
- end
-
- def load(soc) # :nodoc:
- begin
- sz = soc.read(4) # sizeof (N)
- rescue
- raise(DRbConnError, $!.message, $!.backtrace)
- end
- raise(DRbConnError, 'connection closed') if sz.nil?
- raise(DRbConnError, 'premature header') if sz.size < 4
- sz = sz.unpack('N')[0]
- raise(DRbConnError, "too large packet #{sz}") if @load_limit < sz
- begin
- str = soc.read(sz)
- rescue
- raise(DRbConnError, $!.message, $!.backtrace)
- end
- raise(DRbConnError, 'connection closed') if str.nil?
- raise(DRbConnError, 'premature marshal format(can\'t read)') if str.size < sz
- DRb.mutex.synchronize do
- begin
- Marshal::load(str)
- rescue NameError, ArgumentError
- DRbUnknown.new($!, str)
- end
- end
- end
-
- def send_request(stream, ref, msg_id, arg, b) # :nodoc:
- ary = []
- ary.push(dump(ref.__drbref))
- ary.push(dump(msg_id.id2name))
- ary.push(dump(arg.length))
- arg.each do |e|
- ary.push(dump(e))
- end
- ary.push(dump(b))
- stream.write(ary.join(''))
- rescue
- raise(DRbConnError, $!.message, $!.backtrace)
- end
-
- def recv_request(stream) # :nodoc:
- ref = load(stream)
- ro = DRb.to_obj(ref)
- msg = load(stream)
- argc = load(stream)
- raise(DRbConnError, "too many arguments") if @argc_limit < argc
- argv = Array.new(argc, nil)
- argc.times do |n|
- argv[n] = load(stream)
- end
- block = load(stream)
- return ro, msg, argv, block
- end
-
- def send_reply(stream, succ, result) # :nodoc:
- stream.write(dump(succ) + dump(result, !succ))
- rescue
- raise(DRbConnError, $!.message, $!.backtrace)
- end
-
- def recv_reply(stream) # :nodoc:
- succ = load(stream)
- result = load(stream)
- [succ, result]
- end
-
- private
- def make_proxy(obj, error=false) # :nodoc:
- if error
- DRbRemoteError.new(obj)
- else
- DRbObject.new(obj)
- end
- end
- end
-
- # Module managing the underlying network protocol(s) used by drb.
- #
- # By default, drb uses the DRbTCPSocket protocol. Other protocols
- # can be defined. A protocol must define the following class methods:
- #
- # [open(uri, config)] Open a client connection to the server at +uri+,
- # using configuration +config+. Return a protocol
- # instance for this connection.
- # [open_server(uri, config)] Open a server listening at +uri+,
- # using configuration +config+. Return a
- # protocol instance for this listener.
- # [uri_option(uri, config)] Take a URI, possibly containing an option
- # component (e.g. a trailing '?param=val'),
- # and return a [uri, option] tuple.
- #
- # All of these methods should raise a DRbBadScheme error if the URI
- # does not identify the protocol they support (e.g. "druby:" for
- # the standard Ruby protocol). This is how the DRbProtocol module,
- # given a URI, determines which protocol implementation serves that
- # protocol.
- #
- # The protocol instance returned by #open_server must have the
- # following methods:
- #
- # [accept] Accept a new connection to the server. Returns a protocol
- # instance capable of communicating with the client.
- # [close] Close the server connection.
- # [uri] Get the URI for this server.
- #
- # The protocol instance returned by #open must have the following methods:
- #
- # [send_request (ref, msg_id, arg, b)]
- # Send a request to +ref+ with the given message id and arguments.
- # This is most easily implemented by calling DRbMessage.send_request,
- # providing a stream that sits on top of the current protocol.
- # [recv_reply]
- # Receive a reply from the server and return it as a [success-boolean,
- # reply-value] pair. This is most easily implemented by calling
- # DRb.recv_reply, providing a stream that sits on top of the
- # current protocol.
- # [alive?]
- # Is this connection still alive?
- # [close]
- # Close this connection.
- #
- # The protocol instance returned by #open_server().accept() must have
- # the following methods:
- #
- # [recv_request]
- # Receive a request from the client and return a [object, message,
- # args, block] tuple. This is most easily implemented by calling
- # DRbMessage.recv_request, providing a stream that sits on top of
- # the current protocol.
- # [send_reply(succ, result)]
- # Send a reply to the client. This is most easily implemented
- # by calling DRbMessage.send_reply, providing a stream that sits
- # on top of the current protocol.
- # [close]
- # Close this connection.
- #
- # A new protocol is registered with the DRbProtocol module using
- # the add_protocol method.
- #
- # For examples of other protocols, see DRbUNIXSocket in drb/unix.rb,
- # and HTTP0 in sample/http0.rb and sample/http0serv.rb in the full
- # drb distribution.
- module DRbProtocol
-
- # Add a new protocol to the DRbProtocol module.
- def add_protocol(prot)
- @protocol.push(prot)
- end
- module_function :add_protocol
-
- # Open a client connection to +uri+ with the configuration +config+.
- #
- # The DRbProtocol module asks each registered protocol in turn to
- # try to open the URI. Each protocol signals that it does not handle that
- # URI by raising a DRbBadScheme error. If no protocol recognises the
- # URI, then a DRbBadURI error is raised. If a protocol accepts the
- # URI, but an error occurs in opening it, a DRbConnError is raised.
- def open(uri, config, first=true)
- @protocol.each do |prot|
- begin
- return prot.open(uri, config)
- rescue DRbBadScheme
- rescue DRbConnError
- raise($!)
- rescue
- raise(DRbConnError, "#{uri} - #{$!.inspect}")
- end
- end
- if first && (config[:auto_load] != false)
- auto_load(uri)
- return open(uri, config, false)
- end
- raise DRbBadURI, 'can\'t parse uri:' + uri
- end
- module_function :open
-
- # Open a server listening for connections at +uri+ with
- # configuration +config+.
- #
- # The DRbProtocol module asks each registered protocol in turn to
- # try to open a server at the URI. Each protocol signals that it does
- # not handle that URI by raising a DRbBadScheme error. If no protocol
- # recognises the URI, then a DRbBadURI error is raised. If a protocol
- # accepts the URI, but an error occurs in opening it, the underlying
- # error is passed on to the caller.
- def open_server(uri, config, first=true)
- @protocol.each do |prot|
- begin
- return prot.open_server(uri, config)
- rescue DRbBadScheme
- end
- end
- if first && (config[:auto_load] != false)
- auto_load(uri)
- return open_server(uri, config, false)
- end
- raise DRbBadURI, 'can\'t parse uri:' + uri
- end
- module_function :open_server
-
- # Parse +uri+ into a [uri, option] pair.
- #
- # The DRbProtocol module asks each registered protocol in turn to
- # try to parse the URI. Each protocol signals that it does not handle that
- # URI by raising a DRbBadScheme error. If no protocol recognises the
- # URI, then a DRbBadURI error is raised.
- def uri_option(uri, config, first=true)
- @protocol.each do |prot|
- begin
- uri, opt = prot.uri_option(uri, config)
- # opt = nil if opt == ''
- return uri, opt
- rescue DRbBadScheme
- end
- end
- if first && (config[:auto_load] != false)
- auto_load(uri)
- return uri_option(uri, config, false)
- end
- raise DRbBadURI, 'can\'t parse uri:' + uri
- end
- module_function :uri_option
-
- def auto_load(uri) # :nodoc:
- if /\Adrb([a-z0-9]+):/ =~ uri
- require("drb/#{$1}") rescue nil
- end
- end
- module_function :auto_load
- end
-
- # The default drb protocol which communicates over a TCP socket.
- #
- # The DRb TCP protocol URI looks like:
- # <code>druby://<host>:<port>?<option></code>. The option is optional.
-
- class DRbTCPSocket
- # :stopdoc:
- private
- def self.parse_uri(uri)
- if /\Adruby:\/\/(.*?):(\d+)(\?(.*))?\z/ =~ uri
- host = $1
- port = $2.to_i
- option = $4
- [host, port, option]
- else
- raise(DRbBadScheme, uri) unless uri.start_with?('druby:')
- raise(DRbBadURI, 'can\'t parse uri:' + uri)
- end
- end
-
- public
-
- # Open a client connection to +uri+ (DRb URI string) using configuration
- # +config+.
- #
- # This can raise DRb::DRbBadScheme or DRb::DRbBadURI if +uri+ is not for a
- # recognized protocol. See DRb::DRbServer.new for information on built-in
- # URI protocols.
- def self.open(uri, config)
- host, port, = parse_uri(uri)
- soc = TCPSocket.open(host, port)
- self.new(uri, soc, config)
- end
-
- # Returns the hostname of this server
- def self.getservername
- host = Socket::gethostname
- begin
- Socket::getaddrinfo(host, nil,
- Socket::AF_UNSPEC,
- Socket::SOCK_STREAM,
- 0,
- Socket::AI_PASSIVE)[0][3]
- rescue
- 'localhost'
- end
- end
-
- # For the families available for +host+, returns a TCPServer on +port+.
- # If +port+ is 0 the first available port is used. IPv4 servers are
- # preferred over IPv6 servers.
- def self.open_server_inaddr_any(host, port)
- infos = Socket::getaddrinfo(host, nil,
- Socket::AF_UNSPEC,
- Socket::SOCK_STREAM,
- 0,
- Socket::AI_PASSIVE)
- families = Hash[*infos.collect { |af, *_| af }.uniq.zip([]).flatten]
- return TCPServer.open('0.0.0.0', port) if families.has_key?('AF_INET')
- return TCPServer.open('::', port) if families.has_key?('AF_INET6')
- return TCPServer.open(port)
- # :stopdoc:
- end
-
- # Open a server listening for connections at +uri+ using
- # configuration +config+.
- def self.open_server(uri, config)
- uri = 'druby://:0' unless uri
- host, port, _ = parse_uri(uri)
- config = {:tcp_original_host => host}.update(config)
- if host.size == 0
- host = getservername
- soc = open_server_inaddr_any(host, port)
- else
- soc = TCPServer.open(host, port)
- end
- port = soc.addr[1] if port == 0
- config[:tcp_port] = port
- uri = "druby://#{host}:#{port}"
- self.new(uri, soc, config)
- end
-
- # Parse +uri+ into a [uri, option] pair.
- def self.uri_option(uri, config)
- host, port, option = parse_uri(uri)
- return "druby://#{host}:#{port}", option
- end
-
- # Create a new DRbTCPSocket instance.
- #
- # +uri+ is the URI we are connected to.
- # +soc+ is the tcp socket we are bound to. +config+ is our
- # configuration.
- def initialize(uri, soc, config={})
- @uri = uri
- @socket = soc
- @config = config
- @acl = config[:tcp_acl]
- @msg = DRbMessage.new(config)
- set_sockopt(@socket)
- @shutdown_pipe_r, @shutdown_pipe_w = IO.pipe
- end
-
- # Get the URI that we are connected to.
- attr_reader :uri
-
- # Get the address of our TCP peer (the other end of the socket
- # we are bound to.
- def peeraddr
- @socket.peeraddr
- end
-
- # Get the socket.
- def stream; @socket; end
-
- # On the client side, send a request to the server.
- def send_request(ref, msg_id, arg, b)
- @msg.send_request(stream, ref, msg_id, arg, b)
- end
-
- # On the server side, receive a request from the client.
- def recv_request
- @msg.recv_request(stream)
- end
-
- # On the server side, send a reply to the client.
- def send_reply(succ, result)
- @msg.send_reply(stream, succ, result)
- end
-
- # On the client side, receive a reply from the server.
- def recv_reply
- @msg.recv_reply(stream)
- end
-
- public
-
- # Close the connection.
- #
- # If this is an instance returned by #open_server, then this stops
- # listening for new connections altogether. If this is an instance
- # returned by #open or by #accept, then it closes this particular
- # client-server session.
- def close
- shutdown
- if @socket
- @socket.close
- @socket = nil
- end
- close_shutdown_pipe
- end
-
- def close_shutdown_pipe
- @shutdown_pipe_w.close
- @shutdown_pipe_r.close
- end
- private :close_shutdown_pipe
-
- # On the server side, for an instance returned by #open_server,
- # accept a client connection and return a new instance to handle
- # the server's side of this client-server session.
- def accept
- while true
- s = accept_or_shutdown
- return nil unless s
- break if (@acl ? @acl.allow_socket?(s) : true)
- s.close
- end
- if @config[:tcp_original_host].to_s.size == 0
- uri = "druby://#{s.addr[3]}:#{@config[:tcp_port]}"
- else
- uri = @uri
- end
- self.class.new(uri, s, @config)
- end
-
- def accept_or_shutdown
- readables, = IO.select([@socket, @shutdown_pipe_r])
- if readables.include? @shutdown_pipe_r
- return nil
- end
- @socket.accept
- end
- private :accept_or_shutdown
-
- # Graceful shutdown
- def shutdown
- @shutdown_pipe_w.close
- end
-
- # Check to see if this connection is alive.
- def alive?
- return false unless @socket
- if @socket.to_io.wait_readable(0)
- close
- return false
- end
- true
- end
-
- def set_sockopt(soc) # :nodoc:
- soc.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
- rescue IOError, Errno::ECONNRESET, Errno::EINVAL
- # closed/shutdown socket, ignore error
- end
- end
-
- module DRbProtocol
- @protocol = [DRbTCPSocket] # default
- end
-
- class DRbURIOption # :nodoc: I don't understand the purpose of this class...
- def initialize(option)
- @option = option.to_s
- end
- attr_reader :option
- def to_s; @option; end
-
- def ==(other)
- return false unless DRbURIOption === other
- @option == other.option
- end
-
- def hash
- @option.hash
- end
-
- alias eql? ==
- end
-
- # Object wrapping a reference to a remote drb object.
- #
- # Method calls on this object are relayed to the remote
- # object that this object is a stub for.
- class DRbObject
-
- # Unmarshall a marshalled DRbObject.
- #
- # If the referenced object is located within the local server, then
- # the object itself is returned. Otherwise, a new DRbObject is
- # created to act as a stub for the remote referenced object.
- def self._load(s)
- uri, ref = Marshal.load(s)
-
- if DRb.here?(uri)
- obj = DRb.to_obj(ref)
- return obj
- end
-
- self.new_with(uri, ref)
- end
-
- # Creates a DRb::DRbObject given the reference information to the remote
- # host +uri+ and object +ref+.
-
- def self.new_with(uri, ref)
- it = self.allocate
- it.instance_variable_set(:@uri, uri)
- it.instance_variable_set(:@ref, ref)
- it
- end
-
- # Create a new DRbObject from a URI alone.
- def self.new_with_uri(uri)
- self.new(nil, uri)
- end
-
- # Marshall this object.
- #
- # The URI and ref of the object are marshalled.
- def _dump(lv)
- Marshal.dump([@uri, @ref])
- end
-
- # Create a new remote object stub.
- #
- # +obj+ is the (local) object we want to create a stub for. Normally
- # this is +nil+. +uri+ is the URI of the remote object that this
- # will be a stub for.
- def initialize(obj, uri=nil)
- @uri = nil
- @ref = nil
- case obj
- when Object
- is_nil = obj.nil?
- when BasicObject
- is_nil = false
- end
-
- if is_nil
- return if uri.nil?
- @uri, option = DRbProtocol.uri_option(uri, DRb.config)
- @ref = DRbURIOption.new(option) unless option.nil?
- else
- @uri = uri ? uri : (DRb.uri rescue nil)
- @ref = obj ? DRb.to_id(obj) : nil
- end
- end
-
- # Get the URI of the remote object.
- def __drburi
- @uri
- end
-
- # Get the reference of the object, if local.
- def __drbref
- @ref
- end
-
- undef :to_s
- undef :to_a if respond_to?(:to_a)
-
- # Routes respond_to? to the referenced remote object.
- def respond_to?(msg_id, priv=false)
- case msg_id
- when :_dump
- true
- when :marshal_dump
- false
- else
- method_missing(:respond_to?, msg_id, priv)
- end
- end
-
- # Routes method calls to the referenced remote object.
- ruby2_keywords def method_missing(msg_id, *a, &b)
- if DRb.here?(@uri)
- obj = DRb.to_obj(@ref)
- DRb.current_server.check_insecure_method(obj, msg_id)
- return obj.__send__(msg_id, *a, &b)
- end
-
- succ, result = self.class.with_friend(@uri) do
- DRbConn.open(@uri) do |conn|
- conn.send_message(self, msg_id, a, b)
- end
- end
-
- if succ
- return result
- elsif DRbUnknown === result
- raise result
- else
- bt = self.class.prepare_backtrace(@uri, result)
- result.set_backtrace(bt + caller)
- raise result
- end
- end
-
- # Given the +uri+ of another host executes the block provided.
- def self.with_friend(uri) # :nodoc:
- friend = DRb.fetch_server(uri)
- return yield() unless friend
-
- save = Thread.current['DRb']
- Thread.current['DRb'] = { 'server' => friend }
- return yield
- ensure
- Thread.current['DRb'] = save if friend
- end
-
- # Returns a modified backtrace from +result+ with the +uri+ where each call
- # in the backtrace came from.
- def self.prepare_backtrace(uri, result) # :nodoc:
- prefix = "(#{uri}) "
- bt = []
- result.backtrace.each do |x|
- break if /`__send__'$/ =~ x
- if /\A\(druby:\/\// =~ x
- bt.push(x)
- else
- bt.push(prefix + x)
- end
- end
- bt
- end
-
- def pretty_print(q) # :nodoc:
- q.pp_object(self)
- end
-
- def pretty_print_cycle(q) # :nodoc:
- q.object_address_group(self) {
- q.breakable
- q.text '...'
- }
- end
- end
-
- class ThreadObject
- include MonitorMixin
-
- def initialize(&blk)
- super()
- @wait_ev = new_cond
- @req_ev = new_cond
- @res_ev = new_cond
- @status = :wait
- @req = nil
- @res = nil
- @thread = Thread.new(self, &blk)
- end
-
- def alive?
- @thread.alive?
- end
-
- def kill
- @thread.kill
- @thread.join
- end
-
- def method_missing(msg, *arg, &blk)
- synchronize do
- @wait_ev.wait_until { @status == :wait }
- @req = [msg] + arg
- @status = :req
- @req_ev.broadcast
- @res_ev.wait_until { @status == :res }
- value = @res
- @req = @res = nil
- @status = :wait
- @wait_ev.broadcast
- return value
- end
- end
-
- def _execute()
- synchronize do
- @req_ev.wait_until { @status == :req }
- @res = yield(@req)
- @status = :res
- @res_ev.signal
- end
- end
- end
-
- # Class handling the connection between a DRbObject and the
- # server the real object lives on.
- #
- # This class maintains a pool of connections, to reduce the
- # overhead of starting and closing down connections for each
- # method call.
- #
- # This class is used internally by DRbObject. The user does
- # not normally need to deal with it directly.
- class DRbConn
- POOL_SIZE = 16 # :nodoc:
-
- def self.make_pool
- ThreadObject.new do |queue|
- pool = []
- while true
- queue._execute do |message|
- case(message[0])
- when :take then
- remote_uri = message[1]
- conn = nil
- new_pool = []
- pool.each do |c|
- if conn.nil? and c.uri == remote_uri
- conn = c if c.alive?
- else
- new_pool.push c
- end
- end
- pool = new_pool
- conn
- when :store then
- conn = message[1]
- pool.unshift(conn)
- pool.pop.close while pool.size > POOL_SIZE
- conn
- else
- nil
- end
- end
- end
- end
- end
- @pool_proxy = nil
-
- def self.stop_pool
- @pool_proxy&.kill
- @pool_proxy = nil
- end
-
- def self.open(remote_uri) # :nodoc:
- begin
- @pool_proxy = make_pool unless @pool_proxy&.alive?
-
- conn = @pool_proxy.take(remote_uri)
- conn = self.new(remote_uri) unless conn
- succ, result = yield(conn)
- return succ, result
-
- ensure
- if conn
- if succ
- @pool_proxy.store(conn)
- else
- conn.close
- end
- end
- end
- end
-
- def initialize(remote_uri) # :nodoc:
- @uri = remote_uri
- @protocol = DRbProtocol.open(remote_uri, DRb.config)
- end
- attr_reader :uri # :nodoc:
-
- def send_message(ref, msg_id, arg, block) # :nodoc:
- @protocol.send_request(ref, msg_id, arg, block)
- @protocol.recv_reply
- end
-
- def close # :nodoc:
- @protocol.close
- @protocol = nil
- end
-
- def alive? # :nodoc:
- return false unless @protocol
- @protocol.alive?
- end
- end
-
- # Class representing a drb server instance.
- #
- # A DRbServer must be running in the local process before any incoming
- # dRuby calls can be accepted, or any local objects can be passed as
- # dRuby references to remote processes, even if those local objects are
- # never actually called remotely. You do not need to start a DRbServer
- # in the local process if you are only making outgoing dRuby calls
- # passing marshalled parameters.
- #
- # Unless multiple servers are being used, the local DRbServer is normally
- # started by calling DRb.start_service.
- class DRbServer
- @@acl = nil
- @@idconv = DRbIdConv.new
- @@secondary_server = nil
- @@argc_limit = 256
- @@load_limit = 0xffffffff
- @@verbose = false
-
- # Set the default value for the :argc_limit option.
- #
- # See #new(). The initial default value is 256.
- def self.default_argc_limit(argc)
- @@argc_limit = argc
- end
-
- # Set the default value for the :load_limit option.
- #
- # See #new(). The initial default value is 25 MB.
- def self.default_load_limit(sz)
- @@load_limit = sz
- end
-
- # Set the default access control list to +acl+. The default ACL is +nil+.
- #
- # See also DRb::ACL and #new()
- def self.default_acl(acl)
- @@acl = acl
- end
-
- # Set the default value for the :id_conv option.
- #
- # See #new(). The initial default value is a DRbIdConv instance.
- def self.default_id_conv(idconv)
- @@idconv = idconv
- end
-
- # Set the default value of the :verbose option.
- #
- # See #new(). The initial default value is false.
- def self.verbose=(on)
- @@verbose = on
- end
-
- # Get the default value of the :verbose option.
- def self.verbose
- @@verbose
- end
-
- def self.make_config(hash={}) # :nodoc:
- default_config = {
- :idconv => @@idconv,
- :verbose => @@verbose,
- :tcp_acl => @@acl,
- :load_limit => @@load_limit,
- :argc_limit => @@argc_limit,
- }
- default_config.update(hash)
- end
-
- # Create a new DRbServer instance.
- #
- # +uri+ is the URI to bind to. This is normally of the form
- # 'druby://<hostname>:<port>' where <hostname> is a hostname of
- # the local machine. If nil, then the system's default hostname
- # will be bound to, on a port selected by the system; these value
- # can be retrieved from the +uri+ attribute. 'druby:' specifies
- # the default dRuby transport protocol: another protocol, such
- # as 'drbunix:', can be specified instead.
- #
- # +front+ is the front object for the server, that is, the object
- # to which remote method calls on the server will be passed. If
- # nil, then the server will not accept remote method calls.
- #
- # If +config_or_acl+ is a hash, it is the configuration to
- # use for this server. The following options are recognised:
- #
- # :idconv :: an id-to-object conversion object. This defaults
- # to an instance of the class DRb::DRbIdConv.
- # :verbose :: if true, all unsuccessful remote calls on objects
- # in the server will be logged to $stdout. false
- # by default.
- # :tcp_acl :: the access control list for this server. See
- # the ACL class from the main dRuby distribution.
- # :load_limit :: the maximum message size in bytes accepted by
- # the server. Defaults to 25 MB (26214400).
- # :argc_limit :: the maximum number of arguments to a remote
- # method accepted by the server. Defaults to
- # 256.
- # The default values of these options can be modified on
- # a class-wide basis by the class methods #default_argc_limit,
- # #default_load_limit, #default_acl, #default_id_conv,
- # and #verbose=
- #
- # If +config_or_acl+ is not a hash, but is not nil, it is
- # assumed to be the access control list for this server.
- # See the :tcp_acl option for more details.
- #
- # If no other server is currently set as the primary server,
- # this will become the primary server.
- #
- # The server will immediately start running in its own thread.
- def initialize(uri=nil, front=nil, config_or_acl=nil)
- if Hash === config_or_acl
- config = config_or_acl.dup
- else
- acl = config_or_acl || @@acl
- config = {
- :tcp_acl => acl
- }
- end
-
- @config = self.class.make_config(config)
-
- @protocol = DRbProtocol.open_server(uri, @config)
- @uri = @protocol.uri
- @exported_uri = [@uri]
-
- @front = front
- @idconv = @config[:idconv]
-
- @grp = ThreadGroup.new
- @thread = run
-
- DRb.regist_server(self)
- end
-
- # The URI of this DRbServer.
- attr_reader :uri
-
- # The main thread of this DRbServer.
- #
- # This is the thread that listens for and accepts connections
- # from clients, not that handles each client's request-response
- # session.
- attr_reader :thread
-
- # The front object of the DRbServer.
- #
- # This object receives remote method calls made on the server's
- # URI alone, with an object id.
- attr_reader :front
-
- # The configuration of this DRbServer
- attr_reader :config
-
- # Set whether to operate in verbose mode.
- #
- # In verbose mode, failed calls are logged to stdout.
- def verbose=(v); @config[:verbose]=v; end
-
- # Get whether the server is in verbose mode.
- #
- # In verbose mode, failed calls are logged to stdout.
- def verbose; @config[:verbose]; end
-
- # Is this server alive?
- def alive?
- @thread.alive?
- end
-
- # Is +uri+ the URI for this server?
- def here?(uri)
- @exported_uri.include?(uri)
- end
-
- # Stop this server.
- def stop_service
- DRb.remove_server(self)
- if Thread.current['DRb'] && Thread.current['DRb']['server'] == self
- Thread.current['DRb']['stop_service'] = true
- else
- shutdown
- end
- end
-
- # Convert a dRuby reference to the local object it refers to.
- def to_obj(ref)
- return front if ref.nil?
- return front[ref.to_s] if DRbURIOption === ref
- @idconv.to_obj(ref)
- end
-
- # Convert a local object to a dRuby reference.
- def to_id(obj)
- return nil if obj.__id__ == front.__id__
- @idconv.to_id(obj)
- end
-
- private
-
- def shutdown
- current = Thread.current
- if @protocol.respond_to? :shutdown
- @protocol.shutdown
- else
- [@thread, *@grp.list].each { |thread|
- thread.kill unless thread == current # xxx: Thread#kill
- }
- end
- @thread.join unless @thread == current
- end
-
- ##
- # Starts the DRb main loop in a new thread.
-
- def run
- Thread.start do
- begin
- while main_loop
- end
- ensure
- @protocol.close if @protocol
- end
- end
- end
-
- # List of insecure methods.
- #
- # These methods are not callable via dRuby.
- INSECURE_METHOD = [
- :__send__
- ]
-
- # Has a method been included in the list of insecure methods?
- def insecure_method?(msg_id)
- INSECURE_METHOD.include?(msg_id)
- end
-
- # Coerce an object to a string, providing our own representation if
- # to_s is not defined for the object.
- def any_to_s(obj)
- "#{obj}:#{obj.class}"
- rescue
- Kernel.instance_method(:to_s).bind_call(obj)
- end
-
- # Check that a method is callable via dRuby.
- #
- # +obj+ is the object we want to invoke the method on. +msg_id+ is the
- # method name, as a Symbol.
- #
- # If the method is an insecure method (see #insecure_method?) a
- # SecurityError is thrown. If the method is private or undefined,
- # a NameError is thrown.
- def check_insecure_method(obj, msg_id)
- return true if Proc === obj && msg_id == :__drb_yield
- raise(ArgumentError, "#{any_to_s(msg_id)} is not a symbol") unless Symbol == msg_id.class
- raise(SecurityError, "insecure method `#{msg_id}'") if insecure_method?(msg_id)
-
- case obj
- when Object
- if obj.private_methods.include?(msg_id)
- desc = any_to_s(obj)
- raise NoMethodError, "private method `#{msg_id}' called for #{desc}"
- elsif obj.protected_methods.include?(msg_id)
- desc = any_to_s(obj)
- raise NoMethodError, "protected method `#{msg_id}' called for #{desc}"
- else
- true
- end
- else
- if Kernel.instance_method(:private_methods).bind(obj).call.include?(msg_id)
- desc = any_to_s(obj)
- raise NoMethodError, "private method `#{msg_id}' called for #{desc}"
- elsif Kernel.instance_method(:protected_methods).bind(obj).call.include?(msg_id)
- desc = any_to_s(obj)
- raise NoMethodError, "protected method `#{msg_id}' called for #{desc}"
- else
- true
- end
- end
- end
- public :check_insecure_method
-
- class InvokeMethod # :nodoc:
- def initialize(drb_server, client)
- @drb_server = drb_server
- @client = client
- end
-
- def perform
- begin
- setup_message
- ensure
- @result = nil
- @succ = false
- end
-
- if @block
- @result = perform_with_block
- else
- @result = perform_without_block
- end
- @succ = true
- case @result
- when Array
- if @msg_id == :to_ary
- @result = DRbArray.new(@result)
- end
- end
- return @succ, @result
- rescue NoMemoryError, SystemExit, SystemStackError, SecurityError
- raise
- rescue Exception
- @result = $!
- return @succ, @result
- end
-
- private
- def init_with_client
- obj, msg, argv, block = @client.recv_request
- @obj = obj
- @msg_id = msg.intern
- @argv = argv
- @block = block
- end
-
- def check_insecure_method
- @drb_server.check_insecure_method(@obj, @msg_id)
- end
-
- def setup_message
- init_with_client
- check_insecure_method
- end
-
- def perform_without_block
- if Proc === @obj && @msg_id == :__drb_yield
- if @argv.size == 1
- ary = @argv
- else
- ary = [@argv]
- end
- ary.collect(&@obj)[0]
- else
- @obj.__send__(@msg_id, *@argv)
- end
- end
-
- end
-
- require_relative 'invokemethod'
- class InvokeMethod
- include InvokeMethod18Mixin
- end
-
- def error_print(exception)
- exception.backtrace.inject(true) do |first, x|
- if first
- $stderr.puts "#{x}: #{exception} (#{exception.class})"
- else
- $stderr.puts "\tfrom #{x}"
- end
- false
- end
- end
-
- # The main loop performed by a DRbServer's internal thread.
- #
- # Accepts a connection from a client, and starts up its own
- # thread to handle it. This thread loops, receiving requests
- # from the client, invoking them on a local object, and
- # returning responses, until the client closes the connection
- # or a local method call fails.
- def main_loop
- client0 = @protocol.accept
- return nil if !client0
- Thread.start(client0) do |client|
- @grp.add Thread.current
- Thread.current['DRb'] = { 'client' => client ,
- 'server' => self }
- DRb.mutex.synchronize do
- client_uri = client.uri
- @exported_uri << client_uri unless @exported_uri.include?(client_uri)
- end
- _last_invoke_method = nil
- loop do
- begin
- succ = false
- invoke_method = InvokeMethod.new(self, client)
- succ, result = invoke_method.perform
- error_print(result) if !succ && verbose
- unless DRbConnError === result && result.message == 'connection closed'
- client.send_reply(succ, result)
- end
- rescue Exception => e
- error_print(e) if verbose
- ensure
- _last_invoke_method = invoke_method
- client.close unless succ
- if Thread.current['DRb']['stop_service']
- shutdown
- break
- end
- break unless succ
- end
- end
- end
- end
- end
-
- @primary_server = nil
-
- # Start a dRuby server locally.
- #
- # The new dRuby server will become the primary server, even
- # if another server is currently the primary server.
- #
- # +uri+ is the URI for the server to bind to. If nil,
- # the server will bind to random port on the default local host
- # name and use the default dRuby protocol.
- #
- # +front+ is the server's front object. This may be nil.
- #
- # +config+ is the configuration for the new server. This may
- # be nil.
- #
- # See DRbServer::new.
- def start_service(uri=nil, front=nil, config=nil)
- @primary_server = DRbServer.new(uri, front, config)
- end
- module_function :start_service
-
- # The primary local dRuby server.
- #
- # This is the server created by the #start_service call.
- attr_accessor :primary_server
- module_function :primary_server=, :primary_server
-
- # Get the 'current' server.
- #
- # In the context of execution taking place within the main
- # thread of a dRuby server (typically, as a result of a remote
- # call on the server or one of its objects), the current
- # server is that server. Otherwise, the current server is
- # the primary server.
- #
- # If the above rule fails to find a server, a DRbServerNotFound
- # error is raised.
- def current_server
- drb = Thread.current['DRb']
- server = (drb && drb['server']) ? drb['server'] : @primary_server
- raise DRbServerNotFound unless server
- return server
- end
- module_function :current_server
-
- # Stop the local dRuby server.
- #
- # This operates on the primary server. If there is no primary
- # server currently running, it is a noop.
- def stop_service
- @primary_server.stop_service if @primary_server
- @primary_server = nil
- end
- module_function :stop_service
-
- # Get the URI defining the local dRuby space.
- #
- # This is the URI of the current server. See #current_server.
- def uri
- drb = Thread.current['DRb']
- client = (drb && drb['client'])
- if client
- uri = client.uri
- return uri if uri
- end
- current_server.uri
- end
- module_function :uri
-
- # Is +uri+ the URI for the current local server?
- def here?(uri)
- current_server.here?(uri) rescue false
- # (current_server.uri rescue nil) == uri
- end
- module_function :here?
-
- # Get the configuration of the current server.
- #
- # If there is no current server, this returns the default configuration.
- # See #current_server and DRbServer::make_config.
- def config
- current_server.config
- rescue
- DRbServer.make_config
- end
- module_function :config
-
- # Get the front object of the current server.
- #
- # This raises a DRbServerNotFound error if there is no current server.
- # See #current_server.
- def front
- current_server.front
- end
- module_function :front
-
- # Convert a reference into an object using the current server.
- #
- # This raises a DRbServerNotFound error if there is no current server.
- # See #current_server.
- def to_obj(ref)
- current_server.to_obj(ref)
- end
-
- # Get a reference id for an object using the current server.
- #
- # This raises a DRbServerNotFound error if there is no current server.
- # See #current_server.
- def to_id(obj)
- current_server.to_id(obj)
- end
- module_function :to_id
- module_function :to_obj
-
- # Get the thread of the primary server.
- #
- # This returns nil if there is no primary server. See #primary_server.
- def thread
- @primary_server ? @primary_server.thread : nil
- end
- module_function :thread
-
- # Set the default id conversion object.
- #
- # This is expected to be an instance such as DRb::DRbIdConv that responds to
- # #to_id and #to_obj that can convert objects to and from DRb references.
- #
- # See DRbServer#default_id_conv.
- def install_id_conv(idconv)
- DRbServer.default_id_conv(idconv)
- end
- module_function :install_id_conv
-
- # Set the default ACL to +acl+.
- #
- # See DRb::DRbServer.default_acl.
- def install_acl(acl)
- DRbServer.default_acl(acl)
- end
- module_function :install_acl
-
- @mutex = Thread::Mutex.new
- def mutex # :nodoc:
- @mutex
- end
- module_function :mutex
-
- @server = {}
- # Registers +server+ with DRb.
- #
- # This is called when a new DRb::DRbServer is created.
- #
- # If there is no primary server then +server+ becomes the primary server.
- #
- # Example:
- #
- # require 'drb'
- #
- # s = DRb::DRbServer.new # automatically calls regist_server
- # DRb.fetch_server s.uri #=> #<DRb::DRbServer:0x...>
- def regist_server(server)
- @server[server.uri] = server
- mutex.synchronize do
- @primary_server = server unless @primary_server
- end
- end
- module_function :regist_server
-
- # Removes +server+ from the list of registered servers.
- def remove_server(server)
- @server.delete(server.uri)
- mutex.synchronize do
- if @primary_server == server
- @primary_server = nil
- end
- end
- end
- module_function :remove_server
-
- # Retrieves the server with the given +uri+.
- #
- # See also regist_server and remove_server.
- def fetch_server(uri)
- @server[uri]
- end
- module_function :fetch_server
-end
-
-# :stopdoc:
-DRbObject = DRb::DRbObject
-DRbUndumped = DRb::DRbUndumped
-DRbIdConv = DRb::DRbIdConv
diff --git a/lib/drb/eq.rb b/lib/drb/eq.rb
deleted file mode 100644
index 15ca5cae42..0000000000
--- a/lib/drb/eq.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: false
-module DRb
- class DRbObject # :nodoc:
- def ==(other)
- return false unless DRbObject === other
- (@ref == other.__drbref) && (@uri == other.__drburi)
- end
-
- def hash
- [@uri, @ref].hash
- end
-
- alias eql? ==
- end
-end
diff --git a/lib/drb/extserv.rb b/lib/drb/extserv.rb
deleted file mode 100644
index 9523fe84e3..0000000000
--- a/lib/drb/extserv.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-# frozen_string_literal: false
-=begin
- external service
- Copyright (c) 2000,2002 Masatoshi SEKI
-=end
-
-require_relative 'drb'
-require 'monitor'
-
-module DRb
- class ExtServ
- include MonitorMixin
- include DRbUndumped
-
- def initialize(there, name, server=nil)
- super()
- @server = server || DRb::primary_server
- @name = name
- ro = DRbObject.new(nil, there)
- synchronize do
- @invoker = ro.register(name, DRbObject.new(self, @server.uri))
- end
- end
- attr_reader :server
-
- def front
- DRbObject.new(nil, @server.uri)
- end
-
- def stop_service
- synchronize do
- @invoker.unregister(@name)
- server = @server
- @server = nil
- server.stop_service
- true
- end
- end
-
- def alive?
- @server ? @server.alive? : false
- end
- end
-end
diff --git a/lib/drb/extservm.rb b/lib/drb/extservm.rb
deleted file mode 100644
index 9333a108e5..0000000000
--- a/lib/drb/extservm.rb
+++ /dev/null
@@ -1,94 +0,0 @@
-# frozen_string_literal: false
-=begin
- external service manager
- Copyright (c) 2000 Masatoshi SEKI
-=end
-
-require_relative 'drb'
-require 'monitor'
-
-module DRb
- class ExtServManager
- include DRbUndumped
- include MonitorMixin
-
- @@command = {}
-
- def self.command
- @@command
- end
-
- def self.command=(cmd)
- @@command = cmd
- end
-
- def initialize
- super()
- @cond = new_cond
- @servers = {}
- @waiting = []
- @queue = Thread::Queue.new
- @thread = invoke_thread
- @uri = nil
- end
- attr_accessor :uri
-
- def service(name)
- synchronize do
- while true
- server = @servers[name]
- return server if server && server.alive? # server may be `false'
- invoke_service(name)
- @cond.wait
- end
- end
- end
-
- def register(name, ro)
- synchronize do
- @servers[name] = ro
- @cond.signal
- end
- self
- end
- alias regist register
-
- def unregister(name)
- synchronize do
- @servers.delete(name)
- end
- end
- alias unregist unregister
-
- private
- def invoke_thread
- Thread.new do
- while name = @queue.pop
- invoke_service_command(name, @@command[name])
- end
- end
- end
-
- def invoke_service(name)
- @queue.push(name)
- end
-
- def invoke_service_command(name, command)
- raise "invalid command. name: #{name}" unless command
- synchronize do
- return if @servers.include?(name)
- @servers[name] = false
- end
- uri = @uri || DRb.uri
- if command.respond_to? :to_ary
- command = command.to_ary + [uri, name]
- pid = spawn(*command)
- else
- pid = spawn("#{command} #{uri} #{name}")
- end
- th = Process.detach(pid)
- th[:drb_service] = name
- th
- end
- end
-end
diff --git a/lib/drb/gw.rb b/lib/drb/gw.rb
deleted file mode 100644
index 65a525476e..0000000000
--- a/lib/drb/gw.rb
+++ /dev/null
@@ -1,161 +0,0 @@
-# frozen_string_literal: false
-require_relative 'drb'
-require 'monitor'
-
-module DRb
-
- # Gateway id conversion forms a gateway between different DRb protocols or
- # networks.
- #
- # The gateway needs to install this id conversion and create servers for
- # each of the protocols or networks it will be a gateway between. It then
- # needs to create a server that attaches to each of these networks. For
- # example:
- #
- # require 'drb/drb'
- # require 'drb/unix'
- # require 'drb/gw'
- #
- # DRb.install_id_conv DRb::GWIdConv.new
- # gw = DRb::GW.new
- # s1 = DRb::DRbServer.new 'drbunix:/path/to/gateway', gw
- # s2 = DRb::DRbServer.new 'druby://example:10000', gw
- #
- # s1.thread.join
- # s2.thread.join
- #
- # Each client must register services with the gateway, for example:
- #
- # DRb.start_service 'drbunix:', nil # an anonymous server
- # gw = DRbObject.new nil, 'drbunix:/path/to/gateway'
- # gw[:unix] = some_service
- # DRb.thread.join
-
- class GWIdConv < DRbIdConv
- def to_obj(ref) # :nodoc:
- if Array === ref && ref[0] == :DRbObject
- return DRbObject.new_with(ref[1], ref[2])
- end
- super(ref)
- end
- end
-
- # The GW provides a synchronized store for participants in the gateway to
- # communicate.
-
- class GW
- include MonitorMixin
-
- # Creates a new GW
-
- def initialize
- super()
- @hash = {}
- end
-
- # Retrieves +key+ from the GW
-
- def [](key)
- synchronize do
- @hash[key]
- end
- end
-
- # Stores value +v+ at +key+ in the GW
-
- def []=(key, v)
- synchronize do
- @hash[key] = v
- end
- end
- end
-
- class DRbObject # :nodoc:
- def self._load(s)
- uri, ref = Marshal.load(s)
- if DRb.uri == uri
- return ref ? DRb.to_obj(ref) : DRb.front
- end
-
- self.new_with(DRb.uri, [:DRbObject, uri, ref])
- end
-
- def _dump(lv)
- if DRb.uri == @uri
- if Array === @ref && @ref[0] == :DRbObject
- Marshal.dump([@ref[1], @ref[2]])
- else
- Marshal.dump([@uri, @ref]) # ??
- end
- else
- Marshal.dump([DRb.uri, [:DRbObject, @uri, @ref]])
- end
- end
- end
-end
-
-=begin
-DRb.install_id_conv(DRb::GWIdConv.new)
-
-front = DRb::GW.new
-
-s1 = DRb::DRbServer.new('drbunix:/tmp/gw_b_a', front)
-s2 = DRb::DRbServer.new('drbunix:/tmp/gw_b_c', front)
-
-s1.thread.join
-s2.thread.join
-=end
-
-=begin
-# foo.rb
-
-require 'drb/drb'
-
-class Foo
- include DRbUndumped
- def initialize(name, peer=nil)
- @name = name
- @peer = peer
- end
-
- def ping(obj)
- puts "#{@name}: ping: #{obj.inspect}"
- @peer.ping(self) if @peer
- end
-end
-=end
-
-=begin
-# gw_a.rb
-require 'drb/unix'
-require 'foo'
-
-obj = Foo.new('a')
-DRb.start_service("drbunix:/tmp/gw_a", obj)
-
-robj = DRbObject.new_with_uri('drbunix:/tmp/gw_b_a')
-robj[:a] = obj
-
-DRb.thread.join
-=end
-
-=begin
-# gw_c.rb
-require 'drb/unix'
-require 'foo'
-
-foo = Foo.new('c', nil)
-
-DRb.start_service("drbunix:/tmp/gw_c", nil)
-
-robj = DRbObject.new_with_uri("drbunix:/tmp/gw_b_c")
-
-puts "c->b"
-a = robj[:a]
-sleep 2
-
-a.ping(foo)
-
-DRb.thread.join
-=end
-
diff --git a/lib/drb/invokemethod.rb b/lib/drb/invokemethod.rb
deleted file mode 100644
index 0fae6d52b6..0000000000
--- a/lib/drb/invokemethod.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-# frozen_string_literal: false
-# for ruby-1.8.0
-
-module DRb # :nodoc: all
- class DRbServer
- module InvokeMethod18Mixin
- def block_yield(x)
- if x.size == 1 && x[0].class == Array
- x[0] = DRbArray.new(x[0])
- end
- @block.call(*x)
- end
-
- def perform_with_block
- @obj.__send__(@msg_id, *@argv) do |*x|
- jump_error = nil
- begin
- block_value = block_yield(x)
- rescue LocalJumpError
- jump_error = $!
- end
- if jump_error
- case jump_error.reason
- when :break
- break(jump_error.exit_value)
- else
- raise jump_error
- end
- end
- block_value
- end
- end
- end
- end
-end
diff --git a/lib/drb/observer.rb b/lib/drb/observer.rb
deleted file mode 100644
index 0fb7301edf..0000000000
--- a/lib/drb/observer.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-# frozen_string_literal: false
-require 'observer'
-
-module DRb
- # The Observable module extended to DRb. See Observable for details.
- module DRbObservable
- include Observable
-
- # Notifies observers of a change in state. See also
- # Observable#notify_observers
- def notify_observers(*arg)
- if defined? @observer_state and @observer_state
- if defined? @observer_peers
- @observer_peers.each do |observer, method|
- begin
- observer.__send__(method, *arg)
- rescue
- delete_observer(observer)
- end
- end
- end
- @observer_state = false
- end
- end
- end
-end
diff --git a/lib/drb/ssl.rb b/lib/drb/ssl.rb
deleted file mode 100644
index 54ab1ef395..0000000000
--- a/lib/drb/ssl.rb
+++ /dev/null
@@ -1,344 +0,0 @@
-# frozen_string_literal: false
-require 'socket'
-require 'openssl'
-require_relative 'drb'
-require 'singleton'
-
-module DRb
-
- # The protocol for DRb over an SSL socket
- #
- # The URI for a DRb socket over SSL is:
- # <code>drbssl://<host>:<port>?<option></code>. The option is optional
- class DRbSSLSocket < DRbTCPSocket
-
- # SSLConfig handles the needed SSL information for establishing a
- # DRbSSLSocket connection, including generating the X509 / RSA pair.
- #
- # An instance of this config can be passed to DRbSSLSocket.new,
- # DRbSSLSocket.open and DRbSSLSocket.open_server
- #
- # See DRb::DRbSSLSocket::SSLConfig.new for more details
- class SSLConfig
-
- # Default values for a SSLConfig instance.
- #
- # See DRb::DRbSSLSocket::SSLConfig.new for more details
- DEFAULT = {
- :SSLCertificate => nil,
- :SSLPrivateKey => nil,
- :SSLClientCA => nil,
- :SSLCACertificatePath => nil,
- :SSLCACertificateFile => nil,
- :SSLTmpDhCallback => nil,
- :SSLVerifyMode => ::OpenSSL::SSL::VERIFY_NONE,
- :SSLVerifyDepth => nil,
- :SSLVerifyCallback => nil, # custom verification
- :SSLCertificateStore => nil,
- # Must specify if you use auto generated certificate.
- :SSLCertName => nil, # e.g. [["CN","fqdn.example.com"]]
- :SSLCertComment => "Generated by Ruby/OpenSSL"
- }
-
- # Create a new DRb::DRbSSLSocket::SSLConfig instance
- #
- # The DRb::DRbSSLSocket will take either a +config+ Hash or an instance
- # of SSLConfig, and will setup the certificate for its session for the
- # configuration. If want it to generate a generic certificate, the bare
- # minimum is to provide the :SSLCertName
- #
- # === Config options
- #
- # From +config+ Hash:
- #
- # :SSLCertificate ::
- # An instance of OpenSSL::X509::Certificate. If this is not provided,
- # then a generic X509 is generated, with a correspond :SSLPrivateKey
- #
- # :SSLPrivateKey ::
- # A private key instance, like OpenSSL::PKey::RSA. This key must be
- # the key that signed the :SSLCertificate
- #
- # :SSLClientCA ::
- # An OpenSSL::X509::Certificate, or Array of certificates that will
- # used as ClientCAs in the SSL Context
- #
- # :SSLCACertificatePath ::
- # A path to the directory of CA certificates. The certificates must
- # be in PEM format.
- #
- # :SSLCACertificateFile ::
- # A path to a CA certificate file, in PEM format.
- #
- # :SSLTmpDhCallback ::
- # A DH callback. See OpenSSL::SSL::SSLContext.tmp_dh_callback
- #
- # :SSLVerifyMode ::
- # This is the SSL verification mode. See OpenSSL::SSL::VERIFY_* for
- # available modes. The default is OpenSSL::SSL::VERIFY_NONE
- #
- # :SSLVerifyDepth ::
- # Number of CA certificates to walk, when verifying a certificate
- # chain.
- #
- # :SSLVerifyCallback ::
- # A callback to be used for additional verification. See
- # OpenSSL::SSL::SSLContext.verify_callback
- #
- # :SSLCertificateStore ::
- # A OpenSSL::X509::Store used for verification of certificates
- #
- # :SSLCertName ::
- # Issuer name for the certificate. This is required when generating
- # the certificate (if :SSLCertificate and :SSLPrivateKey were not
- # given). The value of this is to be an Array of pairs:
- #
- # [["C", "Raleigh"], ["ST","North Carolina"],
- # ["CN","fqdn.example.com"]]
- #
- # See also OpenSSL::X509::Name
- #
- # :SSLCertComment ::
- # A comment to be used for generating the certificate. The default is
- # "Generated by Ruby/OpenSSL"
- #
- #
- # === Example
- #
- # These values can be added after the fact, like a Hash.
- #
- # require 'drb/ssl'
- # c = DRb::DRbSSLSocket::SSLConfig.new {}
- # c[:SSLCertificate] =
- # OpenSSL::X509::Certificate.new(File.read('mycert.crt'))
- # c[:SSLPrivateKey] = OpenSSL::PKey::RSA.new(File.read('mycert.key'))
- # c[:SSLVerifyMode] = OpenSSL::SSL::VERIFY_PEER
- # c[:SSLCACertificatePath] = "/etc/ssl/certs/"
- # c.setup_certificate
- #
- # or
- #
- # require 'drb/ssl'
- # c = DRb::DRbSSLSocket::SSLConfig.new({
- # :SSLCertName => [["CN" => DRb::DRbSSLSocket.getservername]]
- # })
- # c.setup_certificate
- #
- def initialize(config)
- @config = config
- @cert = config[:SSLCertificate]
- @pkey = config[:SSLPrivateKey]
- @ssl_ctx = nil
- end
-
- # A convenience method to access the values like a Hash
- def [](key);
- @config[key] || DEFAULT[key]
- end
-
- # Connect to IO +tcp+, with context of the current certificate
- # configuration
- def connect(tcp)
- ssl = ::OpenSSL::SSL::SSLSocket.new(tcp, @ssl_ctx)
- ssl.sync = true
- ssl.connect
- ssl
- end
-
- # Accept connection to IO +tcp+, with context of the current certificate
- # configuration
- def accept(tcp)
- ssl = OpenSSL::SSL::SSLSocket.new(tcp, @ssl_ctx)
- ssl.sync = true
- ssl.accept
- ssl
- end
-
- # Ensures that :SSLCertificate and :SSLPrivateKey have been provided
- # or that a new certificate is generated with the other parameters
- # provided.
- def setup_certificate
- if @cert && @pkey
- return
- end
-
- rsa = OpenSSL::PKey::RSA.new(2048){|p, n|
- next unless self[:verbose]
- case p
- when 0; $stderr.putc "." # BN_generate_prime
- when 1; $stderr.putc "+" # BN_generate_prime
- when 2; $stderr.putc "*" # searching good prime,
- # n = #of try,
- # but also data from BN_generate_prime
- when 3; $stderr.putc "\n" # found good prime, n==0 - p, n==1 - q,
- # but also data from BN_generate_prime
- else; $stderr.putc "*" # BN_generate_prime
- end
- }
-
- cert = OpenSSL::X509::Certificate.new
- cert.version = 3
- cert.serial = 0
- name = OpenSSL::X509::Name.new(self[:SSLCertName])
- cert.subject = name
- cert.issuer = name
- cert.not_before = Time.now
- cert.not_after = Time.now + (365*24*60*60)
- cert.public_key = rsa.public_key
-
- ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
- cert.extensions = [
- ef.create_extension("basicConstraints","CA:FALSE"),
- ef.create_extension("subjectKeyIdentifier", "hash") ]
- ef.issuer_certificate = cert
- cert.add_extension(ef.create_extension("authorityKeyIdentifier",
- "keyid:always,issuer:always"))
- if comment = self[:SSLCertComment]
- cert.add_extension(ef.create_extension("nsComment", comment))
- end
- cert.sign(rsa, "SHA256")
-
- @cert = cert
- @pkey = rsa
- end
-
- # Establish the OpenSSL::SSL::SSLContext with the configuration
- # parameters provided.
- def setup_ssl_context
- ctx = ::OpenSSL::SSL::SSLContext.new
- ctx.cert = @cert
- ctx.key = @pkey
- ctx.client_ca = self[:SSLClientCA]
- ctx.ca_path = self[:SSLCACertificatePath]
- ctx.ca_file = self[:SSLCACertificateFile]
- ctx.tmp_dh_callback = self[:SSLTmpDhCallback]
- ctx.verify_mode = self[:SSLVerifyMode]
- ctx.verify_depth = self[:SSLVerifyDepth]
- ctx.verify_callback = self[:SSLVerifyCallback]
- ctx.cert_store = self[:SSLCertificateStore]
- @ssl_ctx = ctx
- end
- end
-
- # Parse the dRuby +uri+ for an SSL connection.
- #
- # Expects drbssl://...
- #
- # Raises DRbBadScheme or DRbBadURI if +uri+ is not matching or malformed
- def self.parse_uri(uri) # :nodoc:
- if /\Adrbssl:\/\/(.*?):(\d+)(\?(.*))?\z/ =~ uri
- host = $1
- port = $2.to_i
- option = $4
- [host, port, option]
- else
- raise(DRbBadScheme, uri) unless uri.start_with?('drbssl:')
- raise(DRbBadURI, 'can\'t parse uri:' + uri)
- end
- end
-
- # Return an DRb::DRbSSLSocket instance as a client-side connection,
- # with the SSL connected. This is called from DRb::start_service or while
- # connecting to a remote object:
- #
- # DRb.start_service 'drbssl://localhost:0', front, config
- #
- # +uri+ is the URI we are connected to,
- # <code>'drbssl://localhost:0'</code> above, +config+ is our
- # configuration. Either a Hash or DRb::DRbSSLSocket::SSLConfig
- def self.open(uri, config)
- host, port, = parse_uri(uri)
- soc = TCPSocket.open(host, port)
- ssl_conf = SSLConfig::new(config)
- ssl_conf.setup_ssl_context
- ssl = ssl_conf.connect(soc)
- self.new(uri, ssl, ssl_conf, true)
- end
-
- # Returns a DRb::DRbSSLSocket instance as a server-side connection, with
- # the SSL connected. This is called from DRb::start_service or while
- # connecting to a remote object:
- #
- # DRb.start_service 'drbssl://localhost:0', front, config
- #
- # +uri+ is the URI we are connected to,
- # <code>'drbssl://localhost:0'</code> above, +config+ is our
- # configuration. Either a Hash or DRb::DRbSSLSocket::SSLConfig
- def self.open_server(uri, config)
- uri = 'drbssl://:0' unless uri
- host, port, = parse_uri(uri)
- if host.size == 0
- host = getservername
- soc = open_server_inaddr_any(host, port)
- else
- soc = TCPServer.open(host, port)
- end
- port = soc.addr[1] if port == 0
- @uri = "drbssl://#{host}:#{port}"
-
- ssl_conf = SSLConfig.new(config)
- ssl_conf.setup_certificate
- ssl_conf.setup_ssl_context
- self.new(@uri, soc, ssl_conf, false)
- end
-
- # This is a convenience method to parse +uri+ and separate out any
- # additional options appended in the +uri+.
- #
- # Returns an option-less uri and the option => [uri,option]
- #
- # The +config+ is completely unused, so passing nil is sufficient.
- def self.uri_option(uri, config) # :nodoc:
- host, port, option = parse_uri(uri)
- return "drbssl://#{host}:#{port}", option
- end
-
- # Create a DRb::DRbSSLSocket instance.
- #
- # +uri+ is the URI we are connected to.
- # +soc+ is the tcp socket we are bound to.
- # +config+ is our configuration. Either a Hash or SSLConfig
- # +is_established+ is a boolean of whether +soc+ is currently established
- #
- # This is called automatically based on the DRb protocol.
- def initialize(uri, soc, config, is_established)
- @ssl = is_established ? soc : nil
- super(uri, soc.to_io, config)
- end
-
- # Returns the SSL stream
- def stream; @ssl; end # :nodoc:
-
- # Closes the SSL stream before closing the dRuby connection.
- def close # :nodoc:
- if @ssl
- @ssl.close
- @ssl = nil
- end
- super
- end
-
- def accept # :nodoc:
- begin
- while true
- soc = accept_or_shutdown
- return nil unless soc
- break if (@acl ? @acl.allow_socket?(soc) : true)
- soc.close
- end
- begin
- ssl = @config.accept(soc)
- rescue Exception
- soc.close
- raise
- end
- self.class.new(uri, ssl, @config, true)
- rescue OpenSSL::SSL::SSLError
- warn("#{$!.message} (#{$!.class})", uplevel: 0) if @config[:verbose]
- retry
- end
- end
- end
-
- DRbProtocol.add_protocol(DRbSSLSocket)
-end
diff --git a/lib/drb/timeridconv.rb b/lib/drb/timeridconv.rb
deleted file mode 100644
index 3ead98a7f2..0000000000
--- a/lib/drb/timeridconv.rb
+++ /dev/null
@@ -1,97 +0,0 @@
-# frozen_string_literal: false
-require_relative 'drb'
-require 'monitor'
-
-module DRb
-
- # Timer id conversion keeps objects alive for a certain amount of time after
- # their last access. The default time period is 600 seconds and can be
- # changed upon initialization.
- #
- # To use TimerIdConv:
- #
- # DRb.install_id_conv TimerIdConv.new 60 # one minute
-
- class TimerIdConv < DRbIdConv
- class TimerHolder2 # :nodoc:
- include MonitorMixin
-
- class InvalidIndexError < RuntimeError; end
-
- def initialize(keeping=600)
- super()
- @sentinel = Object.new
- @gc = {}
- @renew = {}
- @keeping = keeping
- @expires = nil
- end
-
- def add(obj)
- synchronize do
- rotate
- key = obj.__id__
- @renew[key] = obj
- invoke_keeper
- return key
- end
- end
-
- def fetch(key)
- synchronize do
- rotate
- obj = peek(key)
- raise InvalidIndexError if obj == @sentinel
- @renew[key] = obj # KeepIt
- return obj
- end
- end
-
- private
- def peek(key)
- return @renew.fetch(key) { @gc.fetch(key, @sentinel) }
- end
-
- def invoke_keeper
- return if @expires
- @expires = Time.now + @keeping
- on_gc
- end
-
- def on_gc
- return unless Thread.main.alive?
- return if @expires.nil?
- Thread.new { rotate } if @expires < Time.now
- ObjectSpace.define_finalizer(Object.new) {on_gc}
- end
-
- def rotate
- synchronize do
- if @expires &.< Time.now
- @gc = @renew # GCed
- @renew = {}
- @expires = @gc.empty? ? nil : Time.now + @keeping
- end
- end
- end
- end
-
- # Creates a new TimerIdConv which will hold objects for +keeping+ seconds.
- def initialize(keeping=600)
- @holder = TimerHolder2.new(keeping)
- end
-
- def to_obj(ref) # :nodoc:
- return super if ref.nil?
- @holder.fetch(ref)
- rescue TimerHolder2::InvalidIndexError
- raise "invalid reference"
- end
-
- def to_id(obj) # :nodoc:
- return @holder.add(obj)
- end
- end
-end
-
-# DRb.install_id_conv(TimerIdConv.new)
diff --git a/lib/drb/unix.rb b/lib/drb/unix.rb
deleted file mode 100644
index 1629ad3bcd..0000000000
--- a/lib/drb/unix.rb
+++ /dev/null
@@ -1,118 +0,0 @@
-# frozen_string_literal: false
-require 'socket'
-require_relative 'drb'
-require 'tmpdir'
-
-raise(LoadError, "UNIXServer is required") unless defined?(UNIXServer)
-
-module DRb
-
- # Implements DRb over a UNIX socket
- #
- # DRb UNIX socket URIs look like <code>drbunix:<path>?<option></code>. The
- # option is optional.
-
- class DRbUNIXSocket < DRbTCPSocket
- # :stopdoc:
- def self.parse_uri(uri)
- if /\Adrbunix:(.*?)(\?(.*))?\z/ =~ uri
- filename = $1
- option = $3
- [filename, option]
- else
- raise(DRbBadScheme, uri) unless uri.start_with?('drbunix:')
- raise(DRbBadURI, 'can\'t parse uri:' + uri)
- end
- end
-
- def self.open(uri, config)
- filename, = parse_uri(uri)
- soc = UNIXSocket.open(filename)
- self.new(uri, soc, config)
- end
-
- def self.open_server(uri, config)
- filename, = parse_uri(uri)
- if filename.size == 0
- soc = temp_server
- filename = soc.path
- uri = 'drbunix:' + soc.path
- else
- soc = UNIXServer.open(filename)
- end
- owner = config[:UNIXFileOwner]
- group = config[:UNIXFileGroup]
- if owner || group
- require 'etc'
- owner = Etc.getpwnam( owner ).uid if owner
- group = Etc.getgrnam( group ).gid if group
- File.chown owner, group, filename
- end
- mode = config[:UNIXFileMode]
- File.chmod(mode, filename) if mode
-
- self.new(uri, soc, config, true)
- end
-
- def self.uri_option(uri, config)
- filename, option = parse_uri(uri)
- return "drbunix:#{filename}", option
- end
-
- def initialize(uri, soc, config={}, server_mode = false)
- super(uri, soc, config)
- set_sockopt(@socket)
- @server_mode = server_mode
- @acl = nil
- end
-
- # import from tempfile.rb
- Max_try = 10
- private
- def self.temp_server
- tmpdir = Dir::tmpdir
- n = 0
- while true
- begin
- tmpname = sprintf('%s/druby%d.%d', tmpdir, $$, n)
- lock = tmpname + '.lock'
- unless File.exist?(tmpname) or File.exist?(lock)
- Dir.mkdir(lock)
- break
- end
- rescue
- raise "cannot generate tempfile `%s'" % tmpname if n >= Max_try
- #sleep(1)
- end
- n += 1
- end
- soc = UNIXServer.new(tmpname)
- Dir.rmdir(lock)
- soc
- end
-
- public
- def close
- return unless @socket
- shutdown # DRbProtocol#shutdown
- path = @socket.path if @server_mode
- @socket.close
- File.unlink(path) if @server_mode
- @socket = nil
- close_shutdown_pipe
- end
-
- def accept
- s = accept_or_shutdown
- return nil unless s
- self.class.new(nil, s, @config)
- end
-
- def set_sockopt(soc)
- # no-op for now
- end
- end
-
- DRbProtocol.add_protocol(DRbUNIXSocket)
- # :startdoc:
-end
diff --git a/lib/drb/version.rb b/lib/drb/version.rb
deleted file mode 100644
index 10d33445b6..0000000000
--- a/lib/drb/version.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-module DRb
- VERSION = "2.1.1"
-end
diff --git a/lib/drb/weakidconv.rb b/lib/drb/weakidconv.rb
deleted file mode 100644
index ecf0bf515f..0000000000
--- a/lib/drb/weakidconv.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-# frozen_string_literal: false
-require_relative 'drb'
-require 'monitor'
-
-module DRb
-
- # To use WeakIdConv:
- #
- # DRb.start_service(nil, nil, {:idconv => DRb::WeakIdConv.new})
-
- class WeakIdConv < DRbIdConv
- class WeakSet
- include MonitorMixin
- def initialize
- super()
- @immutable = {}
- @map = ObjectSpace::WeakMap.new
- end
-
- def add(obj)
- synchronize do
- begin
- @map[obj] = self
- rescue ArgumentError
- @immutable[obj.__id__] = obj
- end
- return obj.__id__
- end
- end
-
- def fetch(ref)
- synchronize do
- @immutable.fetch(ref) {
- @map.each { |key, _|
- return key if key.__id__ == ref
- }
- raise RangeError.new("invalid reference")
- }
- end
- end
- end
-
- def initialize()
- super()
- @weak_set = WeakSet.new
- end
-
- def to_obj(ref) # :nodoc:
- return super if ref.nil?
- @weak_set.fetch(ref)
- end
-
- def to_id(obj) # :nodoc:
- return @weak_set.add(obj)
- end
- end
-end
-
-# DRb.install_id_conv(WeakIdConv.new)
diff --git a/lib/erb/compiler.rb b/lib/erb/compiler.rb
index 547d2c4c44..7096c8dcea 100644
--- a/lib/erb/compiler.rb
+++ b/lib/erb/compiler.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: true
#--
# ERB::Compiler
#
diff --git a/lib/erb/def_method.rb b/lib/erb/def_method.rb
index 17f9c0f9fa..aee989a926 100644
--- a/lib/erb/def_method.rb
+++ b/lib/erb/def_method.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: true
#--
# ERB::DefMethod
#
diff --git a/lib/erb/util.rb b/lib/erb/util.rb
index 0c1e7482a8..1d2a36275d 100644
--- a/lib/erb/util.rb
+++ b/lib/erb/util.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: true
#--
# ERB::Escape
#
diff --git a/lib/erb/version.rb b/lib/erb/version.rb
index 38e1b76ff4..b5fe39b330 100644
--- a/lib/erb/version.rb
+++ b/lib/erb/version.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
class ERB
- VERSION = '4.0.2'
+ VERSION = '4.0.4'
private_constant :VERSION
end
diff --git a/lib/error_highlight/base.rb b/lib/error_highlight/base.rb
index 7d2ff0c889..b9c68b8eb8 100644
--- a/lib/error_highlight/base.rb
+++ b/lib/error_highlight/base.rb
@@ -54,7 +54,16 @@ module ErrorHighlight
return nil unless Thread::Backtrace::Location === loc
- node = RubyVM::AbstractSyntaxTree.of(loc, keep_script_lines: true)
+ node =
+ begin
+ RubyVM::AbstractSyntaxTree.of(loc, keep_script_lines: true)
+ rescue RuntimeError => error
+ # RubyVM::AbstractSyntaxTree.of raises an error with a message that
+ # includes "prism" when the ISEQ was compiled with the prism compiler.
+ # In this case, we'll set the node to `nil`. In the future, we will
+ # reparse with the prism parser and pass the parsed node to Spotter.
+ raise unless error.message.include?("prism")
+ end
Spotter.new(node, **opts).spot
diff --git a/lib/error_highlight/version.rb b/lib/error_highlight/version.rb
index 5afe5f06d6..506d37fbc1 100644
--- a/lib/error_highlight/version.rb
+++ b/lib/error_highlight/version.rb
@@ -1,3 +1,3 @@
module ErrorHighlight
- VERSION = "0.5.1"
+ VERSION = "0.6.0"
end
diff --git a/lib/fileutils.rb b/lib/fileutils.rb
index 6e7c3174cb..e8cc355760 100644
--- a/lib/fileutils.rb
+++ b/lib/fileutils.rb
@@ -180,7 +180,8 @@ end
# - {CVE-2004-0452}[https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452].
#
module FileUtils
- VERSION = "1.7.1"
+ # The version number.
+ VERSION = "1.7.2"
def self.private_module_function(name) #:nodoc:
module_function name
@@ -1651,7 +1652,7 @@ module FileUtils
when "a"
mask | 07777
else
- raise ArgumentError, "invalid `who' symbol in file mode: #{chr}"
+ raise ArgumentError, "invalid 'who' symbol in file mode: #{chr}"
end
end
end
@@ -1705,7 +1706,7 @@ module FileUtils
copy_mask = user_mask(chr)
(current_mode & copy_mask) / (copy_mask & 0111) * (user_mask & 0111)
else
- raise ArgumentError, "invalid `perm' symbol in file mode: #{chr}"
+ raise ArgumentError, "invalid 'perm' symbol in file mode: #{chr}"
end
end
@@ -2028,21 +2029,22 @@ module FileUtils
private
- module StreamUtils_
+ module StreamUtils_ # :nodoc:
+
private
case (defined?(::RbConfig) ? ::RbConfig::CONFIG['host_os'] : ::RUBY_PLATFORM)
when /mswin|mingw/
- def fu_windows?; true end
+ def fu_windows?; true end #:nodoc:
else
- def fu_windows?; false end
+ def fu_windows?; false end #:nodoc:
end
def fu_copy_stream0(src, dest, blksize = nil) #:nodoc:
IO.copy_stream(src, dest)
end
- def fu_stream_blksize(*streams)
+ def fu_stream_blksize(*streams) #:nodoc:
streams.each do |s|
next unless s.respond_to?(:stat)
size = fu_blksize(s.stat)
@@ -2051,14 +2053,14 @@ module FileUtils
fu_default_blksize()
end
- def fu_blksize(st)
+ def fu_blksize(st) #:nodoc:
s = st.blksize
return nil unless s
return nil if s == 0
s
end
- def fu_default_blksize
+ def fu_default_blksize #:nodoc:
1024
end
end
@@ -2503,7 +2505,7 @@ module FileUtils
end
private_module_function :fu_output_message
- def fu_split_path(path)
+ def fu_split_path(path) #:nodoc:
path = File.path(path)
list = []
until (parent, base = File.split(path); parent == path or parent == ".")
@@ -2524,7 +2526,7 @@ module FileUtils
end
private_module_function :fu_relative_components_from
- def fu_clean_components(*comp)
+ def fu_clean_components(*comp) #:nodoc:
comp.shift while comp.first == "."
return comp if comp.empty?
clean = [comp.shift]
@@ -2543,11 +2545,11 @@ module FileUtils
private_module_function :fu_clean_components
if fu_windows?
- def fu_starting_path?(path)
+ def fu_starting_path?(path) #:nodoc:
path&.start_with?(%r(\w:|/))
end
else
- def fu_starting_path?(path)
+ def fu_starting_path?(path) #:nodoc:
path&.start_with?("/")
end
end
diff --git a/lib/find.rb b/lib/find.rb
index 79e2e2854d..98a79cc76d 100644
--- a/lib/find.rb
+++ b/lib/find.rb
@@ -27,7 +27,7 @@
#
module Find
- VERSION = "0.1.1"
+ VERSION = "0.2.0"
#
# Calls the associated block with the name of every file and directory listed
diff --git a/lib/getoptlong.gemspec b/lib/getoptlong.gemspec
deleted file mode 100644
index c2f65dcaab..0000000000
--- a/lib/getoptlong.gemspec
+++ /dev/null
@@ -1,30 +0,0 @@
-# frozen_string_literal: true
-
-name = File.basename(__FILE__, ".gemspec")
-version = ["lib", Array.new(name.count("-")+1, ".").join("/")].find do |dir|
- break File.foreach(File.join(__dir__, dir, "#{name.tr('-', '/')}.rb")) do |line|
- /^\s*VERSION\s*=\s*"(.*)"/ =~ line and break $1
- end rescue nil
-end
-
-Gem::Specification.new do |spec|
- spec.name = name
- spec.version = version
- spec.authors = ["Yukihiro Matsumoto"]
- spec.email = ["matz@ruby-lang.org"]
-
- spec.summary = %q{GetoptLong for Ruby}
- spec.description = spec.summary
- spec.homepage = "https://github.com/ruby/getoptlong"
- spec.licenses = ["Ruby", "BSD-2-Clause"]
-
- spec.metadata["homepage_uri"] = spec.homepage
- spec.metadata["source_code_uri"] = spec.homepage
-
- # Specify which files should be added to the gem when it is released.
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
- `git ls-files -z 2>#{IO::NULL}`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
- end
- spec.require_paths = ["lib"]
-end
diff --git a/lib/getoptlong.rb b/lib/getoptlong.rb
deleted file mode 100644
index 5ae0e1497c..0000000000
--- a/lib/getoptlong.rb
+++ /dev/null
@@ -1,867 +0,0 @@
-# frozen_string_literal: true
-#
-# GetoptLong for Ruby
-#
-# Copyright (C) 1998, 1999, 2000 Motoyuki Kasahara.
-#
-# You may redistribute and/or modify this library under the same license
-# terms as Ruby.
-
-# \Class \GetoptLong provides parsing both for options
-# and for regular arguments.
-#
-# Using \GetoptLong, you can define options for your program.
-# The program can then capture and respond to whatever options
-# are included in the command that executes the program.
-#
-# A simple example: file <tt>simple.rb</tt>:
-#
-# :include: ../sample/getoptlong/simple.rb
-#
-# If you are somewhat familiar with options,
-# you may want to skip to this
-# {full example}[#class-GetoptLong-label-Full+Example].
-#
-# == Options
-#
-# A \GetoptLong option has:
-#
-# - A string <em>option name</em>.
-# - Zero or more string <em>aliases</em> for the name.
-# - An <em>option type</em>.
-#
-# Options may be defined by calling singleton method GetoptLong.new,
-# which returns a new \GetoptLong object.
-# Options may then be processed by calling other methods
-# such as GetoptLong#each.
-#
-# === Option Name and Aliases
-#
-# In the array that defines an option,
-# the first element is the string option name.
-# Often the name takes the 'long' form, beginning with two hyphens.
-#
-# The option name may have any number of aliases,
-# which are defined by additional string elements.
-#
-# The name and each alias must be of one of two forms:
-#
-# - Two hyphens, followed by one or more letters.
-# - One hyphen, followed by a single letter.
-#
-# File <tt>aliases.rb</tt>:
-#
-# :include: ../sample/getoptlong/aliases.rb
-#
-# An option may be cited by its name,
-# or by any of its aliases;
-# the parsed option always reports the name, not an alias:
-#
-# $ ruby aliases.rb -a -p --xxx --aaa -x
-#
-# Output:
-#
-# ["--xxx", ""]
-# ["--xxx", ""]
-# ["--xxx", ""]
-# ["--xxx", ""]
-# ["--xxx", ""]
-#
-#
-# An option may also be cited by an abbreviation of its name or any alias,
-# as long as that abbreviation is unique among the options.
-#
-# File <tt>abbrev.rb</tt>:
-#
-# :include: ../sample/getoptlong/abbrev.rb
-#
-# Command line:
-#
-# $ ruby abbrev.rb --xxx --xx --xyz --xy
-#
-# Output:
-#
-# ["--xxx", ""]
-# ["--xxx", ""]
-# ["--xyz", ""]
-# ["--xyz", ""]
-#
-# This command line raises GetoptLong::AmbiguousOption:
-#
-# $ ruby abbrev.rb --x
-#
-# === Repetition
-#
-# An option may be cited more than once:
-#
-# $ ruby abbrev.rb --xxx --xyz --xxx --xyz
-#
-# Output:
-#
-# ["--xxx", ""]
-# ["--xyz", ""]
-# ["--xxx", ""]
-# ["--xyz", ""]
-#
-# === Treating Remaining Options as Arguments
-#
-# A option-like token that appears
-# anywhere after the token <tt>--</tt> is treated as an ordinary argument,
-# and is not processed as an option:
-#
-# $ ruby abbrev.rb --xxx --xyz -- --xxx --xyz
-#
-# Output:
-#
-# ["--xxx", ""]
-# ["--xyz", ""]
-#
-# === Option Types
-#
-# Each option definition includes an option type,
-# which controls whether the option takes an argument.
-#
-# File <tt>types.rb</tt>:
-#
-# :include: ../sample/getoptlong/types.rb
-#
-# Note that an option type has to do with the <em>option argument</em>
-# (whether it is required, optional, or forbidden),
-# not with whether the option itself is required.
-#
-# ==== Option with Required Argument
-#
-# An option of type <tt>GetoptLong::REQUIRED_ARGUMENT</tt>
-# must be followed by an argument, which is associated with that option:
-#
-# $ ruby types.rb --xxx foo
-#
-# Output:
-#
-# ["--xxx", "foo"]
-#
-# If the option is not last, its argument is whatever follows it
-# (even if the argument looks like another option):
-#
-# $ ruby types.rb --xxx --yyy
-#
-# Output:
-#
-# ["--xxx", "--yyy"]
-#
-# If the option is last, an exception is raised:
-#
-# $ ruby types.rb
-# # Raises GetoptLong::MissingArgument
-#
-# ==== Option with Optional Argument
-#
-# An option of type <tt>GetoptLong::OPTIONAL_ARGUMENT</tt>
-# may be followed by an argument, which if given is associated with that option.
-#
-# If the option is last, it does not have an argument:
-#
-# $ ruby types.rb --yyy
-#
-# Output:
-#
-# ["--yyy", ""]
-#
-# If the option is followed by another option, it does not have an argument:
-#
-# $ ruby types.rb --yyy --zzz
-#
-# Output:
-#
-# ["--yyy", ""]
-# ["--zzz", ""]
-#
-# Otherwise the option is followed by its argument, which is associated
-# with that option:
-#
-# $ ruby types.rb --yyy foo
-#
-# Output:
-#
-# ["--yyy", "foo"]
-#
-# ==== Option with No Argument
-#
-# An option of type <tt>GetoptLong::NO_ARGUMENT</tt> takes no argument:
-#
-# ruby types.rb --zzz foo
-#
-# Output:
-#
-# ["--zzz", ""]
-#
-# === ARGV
-#
-# You can process options either with method #each and a block,
-# or with method #get.
-#
-# During processing, each found option is removed, along with its argument
-# if there is one.
-# After processing, each remaining element was neither an option
-# nor the argument for an option.
-#
-# File <tt>argv.rb</tt>:
-#
-# :include: ../sample/getoptlong/argv.rb
-#
-# Command line:
-#
-# $ ruby argv.rb --xxx Foo --yyy Bar Baz --zzz Bat Bam
-#
-# Output:
-#
-# Original ARGV: ["--xxx", "Foo", "--yyy", "Bar", "Baz", "--zzz", "Bat", "Bam"]
-# ["--xxx", "Foo"]
-# ["--yyy", "Bar"]
-# ["--zzz", ""]
-# Remaining ARGV: ["Baz", "Bat", "Bam"]
-#
-# === Ordering
-#
-# There are three settings that control the way the options
-# are interpreted:
-#
-# - +PERMUTE+.
-# - +REQUIRE_ORDER+.
-# - +RETURN_IN_ORDER+.
-#
-# The initial setting for a new \GetoptLong object is +REQUIRE_ORDER+
-# if environment variable +POSIXLY_CORRECT+ is defined, +PERMUTE+ otherwise.
-#
-# ==== PERMUTE Ordering
-#
-# In the +PERMUTE+ ordering, options and other, non-option,
-# arguments may appear in any order and any mixture.
-#
-# File <tt>permute.rb</tt>:
-#
-# :include: ../sample/getoptlong/permute.rb
-#
-# Command line:
-#
-# $ ruby permute.rb Foo --zzz Bar --xxx Baz --yyy Bat Bam --xxx Bag Bah
-#
-# Output:
-#
-# Original ARGV: ["Foo", "--zzz", "Bar", "--xxx", "Baz", "--yyy", "Bat", "Bam", "--xxx", "Bag", "Bah"]
-# ["--zzz", ""]
-# ["--xxx", "Baz"]
-# ["--yyy", "Bat"]
-# ["--xxx", "Bag"]
-# Remaining ARGV: ["Foo", "Bar", "Bam", "Bah"]
-#
-# ==== REQUIRE_ORDER Ordering
-#
-# In the +REQUIRE_ORDER+ ordering, all options precede all non-options;
-# that is, each word after the first non-option word
-# is treated as a non-option word (even if it begins with a hyphen).
-#
-# File <tt>require_order.rb</tt>:
-#
-# :include: ../sample/getoptlong/require_order.rb
-#
-# Command line:
-#
-# $ ruby require_order.rb --xxx Foo Bar --xxx Baz --yyy Bat -zzz
-#
-# Output:
-#
-# Original ARGV: ["--xxx", "Foo", "Bar", "--xxx", "Baz", "--yyy", "Bat", "-zzz"]
-# ["--xxx", "Foo"]
-# Remaining ARGV: ["Bar", "--xxx", "Baz", "--yyy", "Bat", "-zzz"]
-#
-# ==== RETURN_IN_ORDER Ordering
-#
-# In the +RETURN_IN_ORDER+ ordering, every word is treated as an option.
-# A word that begins with a hyphen (or two) is treated in the usual way;
-# a word +word+ that does not so begin is treated as an option
-# whose name is an empty string, and whose value is +word+.
-#
-# File <tt>return_in_order.rb</tt>:
-#
-# :include: ../sample/getoptlong/return_in_order.rb
-#
-# Command line:
-#
-# $ ruby return_in_order.rb Foo --xxx Bar Baz --zzz Bat Bam
-#
-# Output:
-#
-# Original ARGV: ["Foo", "--xxx", "Bar", "Baz", "--zzz", "Bat", "Bam"]
-# ["", "Foo"]
-# ["--xxx", "Bar"]
-# ["", "Baz"]
-# ["--zzz", ""]
-# ["", "Bat"]
-# ["", "Bam"]
-# Remaining ARGV: []
-#
-# === Full Example
-#
-# File <tt>fibonacci.rb</tt>:
-#
-# :include: ../sample/getoptlong/fibonacci.rb
-#
-# Command line:
-#
-# $ ruby fibonacci.rb
-#
-# Output:
-#
-# Option --number is required.
-# Usage:
-#
-# -n n, --number n:
-# Compute Fibonacci number for n.
-# -v [boolean], --verbose [boolean]:
-# Show intermediate results; default is 'false'.
-# -h, --help:
-# Show this help.
-#
-# Command line:
-#
-# $ ruby fibonacci.rb --number
-#
-# Raises GetoptLong::MissingArgument:
-#
-# fibonacci.rb: option `--number' requires an argument
-#
-# Command line:
-#
-# $ ruby fibonacci.rb --number 6
-#
-# Output:
-#
-# 8
-#
-# Command line:
-#
-# $ ruby fibonacci.rb --number 6 --verbose
-#
-# Output:
-# 1
-# 2
-# 3
-# 5
-# 8
-#
-# Command line:
-#
-# $ ruby fibonacci.rb --number 6 --verbose yes
-#
-# Output:
-#
-# --verbose argument must be true or false
-# Usage:
-#
-# -n n, --number n:
-# Compute Fibonacci number for n.
-# -v [boolean], --verbose [boolean]:
-# Show intermediate results; default is 'false'.
-# -h, --help:
-# Show this help.
-#
-class GetoptLong
- # Version.
- VERSION = "0.2.0"
-
- #
- # Orderings.
- #
- ORDERINGS = [REQUIRE_ORDER = 0, PERMUTE = 1, RETURN_IN_ORDER = 2]
-
- #
- # Argument flags.
- #
- ARGUMENT_FLAGS = [NO_ARGUMENT = 0, REQUIRED_ARGUMENT = 1,
- OPTIONAL_ARGUMENT = 2]
-
- #
- # Status codes.
- #
- STATUS_YET, STATUS_STARTED, STATUS_TERMINATED = 0, 1, 2
-
- #
- # Error types.
- #
- class Error < StandardError; end
- class AmbiguousOption < Error; end
- class NeedlessArgument < Error; end
- class MissingArgument < Error; end
- class InvalidOption < Error; end
-
- #
- # Returns a new \GetoptLong object based on the given +arguments+.
- # See {Options}[#class-GetoptLong-label-Options].
- #
- # Example:
- #
- # :include: ../sample/getoptlong/simple.rb
- #
- # Raises an exception if:
- #
- # - Any of +arguments+ is not an array.
- # - Any option name or alias is not a string.
- # - Any option type is invalid.
- #
- def initialize(*arguments)
- #
- # Current ordering.
- #
- if ENV.include?('POSIXLY_CORRECT')
- @ordering = REQUIRE_ORDER
- else
- @ordering = PERMUTE
- end
-
- #
- # Hash table of option names.
- # Keys of the table are option names, and their values are canonical
- # names of the options.
- #
- @canonical_names = Hash.new
-
- #
- # Hash table of argument flags.
- # Keys of the table are option names, and their values are argument
- # flags of the options.
- #
- @argument_flags = Hash.new
-
- #
- # Whether error messages are output to $stderr.
- #
- @quiet = false
-
- #
- # Status code.
- #
- @status = STATUS_YET
-
- #
- # Error code.
- #
- @error = nil
-
- #
- # Error message.
- #
- @error_message = nil
-
- #
- # Rest of catenated short options.
- #
- @rest_singles = ''
-
- #
- # List of non-option-arguments.
- # Append them to ARGV when option processing is terminated.
- #
- @non_option_arguments = Array.new
-
- if 0 < arguments.length
- set_options(*arguments)
- end
- end
-
- # Sets the ordering; see {Ordering}[#class-GetoptLong-label-Ordering];
- # returns the new ordering.
- #
- # If the given +ordering+ is +PERMUTE+ and environment variable
- # +POSIXLY_CORRECT+ is defined, sets the ordering to +REQUIRE_ORDER+;
- # otherwise sets the ordering to +ordering+:
- #
- # options = GetoptLong.new
- # options.ordering == GetoptLong::PERMUTE # => true
- # options.ordering = GetoptLong::RETURN_IN_ORDER
- # options.ordering == GetoptLong::RETURN_IN_ORDER # => true
- # ENV['POSIXLY_CORRECT'] = 'true'
- # options.ordering = GetoptLong::PERMUTE
- # options.ordering == GetoptLong::REQUIRE_ORDER # => true
- #
- # Raises an exception if +ordering+ is invalid.
- #
- def ordering=(ordering)
- #
- # The method is failed if option processing has already started.
- #
- if @status != STATUS_YET
- set_error(ArgumentError, "argument error")
- raise RuntimeError,
- "invoke ordering=, but option processing has already started"
- end
-
- #
- # Check ordering.
- #
- if !ORDERINGS.include?(ordering)
- raise ArgumentError, "invalid ordering `#{ordering}'"
- end
- if ordering == PERMUTE && ENV.include?('POSIXLY_CORRECT')
- @ordering = REQUIRE_ORDER
- else
- @ordering = ordering
- end
- end
-
- #
- # Returns the ordering setting.
- #
- attr_reader :ordering
-
- #
- # Replaces existing options with those given by +arguments+,
- # which have the same form as the arguments to ::new;
- # returns +self+.
- #
- # Raises an exception if option processing has begun.
- #
- def set_options(*arguments)
- #
- # The method is failed if option processing has already started.
- #
- if @status != STATUS_YET
- raise RuntimeError,
- "invoke set_options, but option processing has already started"
- end
-
- #
- # Clear tables of option names and argument flags.
- #
- @canonical_names.clear
- @argument_flags.clear
-
- arguments.each do |arg|
- if !arg.is_a?(Array)
- raise ArgumentError, "the option list contains non-Array argument"
- end
-
- #
- # Find an argument flag and it set to `argument_flag'.
- #
- argument_flag = nil
- arg.each do |i|
- if ARGUMENT_FLAGS.include?(i)
- if argument_flag != nil
- raise ArgumentError, "too many argument-flags"
- end
- argument_flag = i
- end
- end
-
- raise ArgumentError, "no argument-flag" if argument_flag == nil
-
- canonical_name = nil
- arg.each do |i|
- #
- # Check an option name.
- #
- next if i == argument_flag
- begin
- if !i.is_a?(String) || i !~ /\A-([^-]|-.+)\z/
- raise ArgumentError, "an invalid option `#{i}'"
- end
- if (@canonical_names.include?(i))
- raise ArgumentError, "option redefined `#{i}'"
- end
- rescue
- @canonical_names.clear
- @argument_flags.clear
- raise
- end
-
- #
- # Register the option (`i') to the `@canonical_names' and
- # `@canonical_names' Hashes.
- #
- if canonical_name == nil
- canonical_name = i
- end
- @canonical_names[i] = canonical_name
- @argument_flags[i] = argument_flag
- end
- raise ArgumentError, "no option name" if canonical_name == nil
- end
- return self
- end
-
- #
- # Sets quiet mode and returns the given argument:
- #
- # - When +false+ or +nil+, error messages are written to <tt>$stdout</tt>.
- # - Otherwise, error messages are not written.
- #
- attr_writer :quiet
-
- #
- # Returns the quiet mode setting.
- #
- attr_reader :quiet
- alias quiet? quiet
-
- #
- # Terminate option processing;
- # returns +nil+ if processing has already terminated;
- # otherwise returns +self+.
- #
- def terminate
- return nil if @status == STATUS_TERMINATED
- raise RuntimeError, "an error has occurred" if @error != nil
-
- @status = STATUS_TERMINATED
- @non_option_arguments.reverse_each do |argument|
- ARGV.unshift(argument)
- end
-
- @canonical_names = nil
- @argument_flags = nil
- @rest_singles = nil
- @non_option_arguments = nil
-
- return self
- end
-
- #
- # Returns +true+ if option processing has terminated, +false+ otherwise.
- #
- def terminated?
- return @status == STATUS_TERMINATED
- end
-
- #
- # \Set an error (a protected method).
- #
- def set_error(type, message)
- $stderr.print("#{$0}: #{message}\n") if !@quiet
-
- @error = type
- @error_message = message
- @canonical_names = nil
- @argument_flags = nil
- @rest_singles = nil
- @non_option_arguments = nil
-
- raise type, message
- end
- protected :set_error
-
- #
- # Returns whether option processing has failed.
- #
- attr_reader :error
- alias error? error
-
- # Return the appropriate error message in POSIX-defined format.
- # If no error has occurred, returns +nil+.
- #
- def error_message
- return @error_message
- end
-
- #
- # Returns the next option as a 2-element array containing:
- #
- # - The option name (the name itself, not an alias).
- # - The option value.
- #
- # Returns +nil+ if there are no more options.
- #
- def get
- option_name, option_argument = nil, ''
-
- #
- # Check status.
- #
- return nil if @error != nil
- case @status
- when STATUS_YET
- @status = STATUS_STARTED
- when STATUS_TERMINATED
- return nil
- end
-
- #
- # Get next option argument.
- #
- if 0 < @rest_singles.length
- argument = '-' + @rest_singles
- elsif (ARGV.length == 0)
- terminate
- return nil
- elsif @ordering == PERMUTE
- while 0 < ARGV.length && ARGV[0] !~ /\A-./
- @non_option_arguments.push(ARGV.shift)
- end
- if ARGV.length == 0
- terminate
- return nil
- end
- argument = ARGV.shift
- elsif @ordering == REQUIRE_ORDER
- if (ARGV[0] !~ /\A-./)
- terminate
- return nil
- end
- argument = ARGV.shift
- else
- argument = ARGV.shift
- end
-
- #
- # Check the special argument `--'.
- # `--' indicates the end of the option list.
- #
- if argument == '--' && @rest_singles.length == 0
- terminate
- return nil
- end
-
- #
- # Check for long and short options.
- #
- if argument =~ /\A(--[^=]+)/ && @rest_singles.length == 0
- #
- # This is a long style option, which start with `--'.
- #
- pattern = $1
- if @canonical_names.include?(pattern)
- option_name = pattern
- else
- #
- # The option `option_name' is not registered in `@canonical_names'.
- # It may be an abbreviated.
- #
- matches = []
- @canonical_names.each_key do |key|
- if key.index(pattern) == 0
- option_name = key
- matches << key
- end
- end
- if 2 <= matches.length
- set_error(AmbiguousOption, "option `#{argument}' is ambiguous between #{matches.join(', ')}")
- elsif matches.length == 0
- set_error(InvalidOption, "unrecognized option `#{argument}'")
- end
- end
-
- #
- # Check an argument to the option.
- #
- if @argument_flags[option_name] == REQUIRED_ARGUMENT
- if argument =~ /=(.*)/m
- option_argument = $1
- elsif 0 < ARGV.length
- option_argument = ARGV.shift
- else
- set_error(MissingArgument,
- "option `#{argument}' requires an argument")
- end
- elsif @argument_flags[option_name] == OPTIONAL_ARGUMENT
- if argument =~ /=(.*)/m
- option_argument = $1
- elsif 0 < ARGV.length && ARGV[0] !~ /\A-./
- option_argument = ARGV.shift
- else
- option_argument = ''
- end
- elsif argument =~ /=(.*)/m
- set_error(NeedlessArgument,
- "option `#{option_name}' doesn't allow an argument")
- end
-
- elsif argument =~ /\A(-(.))(.*)/m
- #
- # This is a short style option, which start with `-' (not `--').
- # Short options may be catenated (e.g. `-l -g' is equivalent to
- # `-lg').
- #
- option_name, ch, @rest_singles = $1, $2, $3
-
- if @canonical_names.include?(option_name)
- #
- # The option `option_name' is found in `@canonical_names'.
- # Check its argument.
- #
- if @argument_flags[option_name] == REQUIRED_ARGUMENT
- if 0 < @rest_singles.length
- option_argument = @rest_singles
- @rest_singles = ''
- elsif 0 < ARGV.length
- option_argument = ARGV.shift
- else
- # 1003.2 specifies the format of this message.
- set_error(MissingArgument, "option requires an argument -- #{ch}")
- end
- elsif @argument_flags[option_name] == OPTIONAL_ARGUMENT
- if 0 < @rest_singles.length
- option_argument = @rest_singles
- @rest_singles = ''
- elsif 0 < ARGV.length && ARGV[0] !~ /\A-./
- option_argument = ARGV.shift
- else
- option_argument = ''
- end
- end
- else
- #
- # This is an invalid option.
- # 1003.2 specifies the format of this message.
- #
- if ENV.include?('POSIXLY_CORRECT')
- set_error(InvalidOption, "invalid option -- #{ch}")
- else
- set_error(InvalidOption, "invalid option -- #{ch}")
- end
- end
- else
- #
- # This is a non-option argument.
- # Only RETURN_IN_ORDER fell into here.
- #
- return '', argument
- end
-
- return @canonical_names[option_name], option_argument
- end
- alias get_option get
-
- #
- # Calls the given block with each option;
- # each option is a 2-element array containing:
- #
- # - The option name (the name itself, not an alias).
- # - The option value.
- #
- # Example:
- #
- # :include: ../sample/getoptlong/each.rb
- #
- # Command line:
- #
- # ruby each.rb -xxx Foo -x Bar --yyy Baz -y Bat --zzz
- #
- # Output:
- #
- # Original ARGV: ["-xxx", "Foo", "-x", "Bar", "--yyy", "Baz", "-y", "Bat", "--zzz"]
- # ["--xxx", "xx"]
- # ["--xxx", "Bar"]
- # ["--yyy", "Baz"]
- # ["--yyy", "Bat"]
- # ["--zzz", ""]
- # Remaining ARGV: ["Foo"]
- #
- def each
- loop do
- option_name, option_argument = get_option
- break if option_name == nil
- yield option_name, option_argument
- end
- end
- alias each_option each
-end
diff --git a/lib/ipaddr.rb b/lib/ipaddr.rb
index 7a5cf94830..dbb213c90a 100644
--- a/lib/ipaddr.rb
+++ b/lib/ipaddr.rb
@@ -40,7 +40,7 @@ require 'socket'
# p ipaddr3 #=> #<IPAddr: IPv4:192.168.2.0/255.255.255.0>
class IPAddr
- VERSION = "1.2.5"
+ VERSION = "1.2.6"
# 32 bit mask for IPv4
IN4MASK = 0xffffffff
@@ -52,7 +52,7 @@ class IPAddr
# Regexp _internally_ used for parsing IPv4 address.
RE_IPV4ADDRLIKE = %r{
\A
- (\d+) \. (\d+) \. (\d+) \. (\d+)
+ \d+ \. \d+ \. \d+ \. \d+
\z
}x
@@ -110,8 +110,13 @@ class IPAddr
# Convert a network byte ordered string form of an IP address into
# human readable form.
+ # It expects the string to be encoded in Encoding::ASCII_8BIT (BINARY).
def self.ntop(addr)
- case addr.size
+ if addr.is_a?(String) && addr.encoding != Encoding::BINARY
+ raise InvalidAddressError, "invalid encoding (given #{addr.encoding}, expected BINARY)"
+ end
+
+ case addr.bytesize
when 4
addr.unpack('C4').join('.')
when 16
@@ -176,9 +181,7 @@ class IPAddr
def include?(other)
other = coerce_other(other)
return false unless other.family == family
- range = to_range
- other = other.to_range
- range.begin <= other.begin && range.end >= other.end
+ begin_addr <= other.begin_addr && end_addr >= other.end_addr
end
alias === include?
@@ -224,6 +227,12 @@ class IPAddr
return str
end
+ # Returns a string containing the IP address representation in
+ # cidr notation
+ def cidr
+ format("%s/%s", to_s, prefix)
+ end
+
# Returns a network byte ordered string form of the IP address.
def hton
case @family
@@ -249,12 +258,17 @@ class IPAddr
end
# Returns true if the ipaddr is a loopback address.
+ # Loopback IPv4 addresses in the IPv4-mapped IPv6
+ # address range are also considered as loopback addresses.
def loopback?
case @family
when Socket::AF_INET
- @addr & 0xff000000 == 0x7f000000
+ @addr & 0xff000000 == 0x7f000000 # 127.0.0.1/8
when Socket::AF_INET6
- @addr == 1
+ @addr == 1 || # ::1
+ (@addr & 0xffff_0000_0000 == 0xffff_0000_0000 && (
+ @addr & 0xff000000 == 0x7f000000 # ::ffff:127.0.0.1/8
+ ))
else
raise AddressFamilyError, "unsupported address family"
end
@@ -263,7 +277,8 @@ class IPAddr
# Returns true if the ipaddr is a private address. IPv4 addresses
# in 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16 as defined in RFC
# 1918 and IPv6 Unique Local Addresses in fc00::/7 as defined in RFC
- # 4193 are considered private.
+ # 4193 are considered private. Private IPv4 addresses in the
+ # IPv4-mapped IPv6 address range are also considered private.
def private?
case @family
when Socket::AF_INET
@@ -271,22 +286,31 @@ class IPAddr
@addr & 0xfff00000 == 0xac100000 || # 172.16.0.0/12
@addr & 0xffff0000 == 0xc0a80000 # 192.168.0.0/16
when Socket::AF_INET6
- @addr & 0xfe00_0000_0000_0000_0000_0000_0000_0000 == 0xfc00_0000_0000_0000_0000_0000_0000_0000
+ @addr & 0xfe00_0000_0000_0000_0000_0000_0000_0000 == 0xfc00_0000_0000_0000_0000_0000_0000_0000 ||
+ (@addr & 0xffff_0000_0000 == 0xffff_0000_0000 && (
+ @addr & 0xff000000 == 0x0a000000 || # ::ffff:10.0.0.0/8
+ @addr & 0xfff00000 == 0xac100000 || # ::ffff::172.16.0.0/12
+ @addr & 0xffff0000 == 0xc0a80000 # ::ffff::192.168.0.0/16
+ ))
else
raise AddressFamilyError, "unsupported address family"
end
end
# Returns true if the ipaddr is a link-local address. IPv4
- # addresses in 169.254.0.0/16 reserved by RFC 3927 and Link-Local
+ # addresses in 169.254.0.0/16 reserved by RFC 3927 and link-local
# IPv6 Unicast Addresses in fe80::/10 reserved by RFC 4291 are
- # considered link-local.
+ # considered link-local. Link-local IPv4 addresses in the
+ # IPv4-mapped IPv6 address range are also considered link-local.
def link_local?
case @family
when Socket::AF_INET
@addr & 0xffff0000 == 0xa9fe0000 # 169.254.0.0/16
when Socket::AF_INET6
- @addr & 0xffc0_0000_0000_0000_0000_0000_0000_0000 == 0xfe80_0000_0000_0000_0000_0000_0000_0000
+ @addr & 0xffc0_0000_0000_0000_0000_0000_0000_0000 == 0xfe80_0000_0000_0000_0000_0000_0000_0000 || # fe80::/10
+ (@addr & 0xffff_0000_0000 == 0xffff_0000_0000 && (
+ @addr & 0xffff0000 == 0xa9fe0000 # ::ffff:169.254.0.0/16
+ ))
else
raise AddressFamilyError, "unsupported address family"
end
@@ -400,17 +424,6 @@ class IPAddr
# Creates a Range object for the network address.
def to_range
- begin_addr = (@addr & @mask_addr)
-
- case @family
- when Socket::AF_INET
- end_addr = (@addr | (IN4MASK ^ @mask_addr))
- when Socket::AF_INET6
- end_addr = (@addr | (IN6MASK ^ @mask_addr))
- else
- raise AddressFamilyError, "unsupported address family"
- end
-
self.class.new(begin_addr, @family)..self.class.new(end_addr, @family)
end
@@ -439,7 +452,7 @@ class IPAddr
when Integer
mask!(prefix)
else
- raise InvalidPrefixError, "prefix must be an integer: #{@addr}"
+ raise InvalidPrefixError, "prefix must be an integer"
end
end
@@ -464,6 +477,20 @@ class IPAddr
_to_string(@mask_addr)
end
+ # Returns the wildcard mask in string format e.g. 0.0.255.255
+ def wildcard_mask
+ case @family
+ when Socket::AF_INET
+ mask = IN4MASK ^ @mask_addr
+ when Socket::AF_INET6
+ mask = IN6MASK ^ @mask_addr
+ else
+ raise AddressFamilyError, "unsupported address family"
+ end
+
+ _to_string(mask)
+ end
+
# Returns the IPv6 zone identifier, if present.
# Raises InvalidAddressError if not an IPv6 address.
def zone_id
@@ -491,6 +518,21 @@ class IPAddr
protected
+ def begin_addr
+ @addr & @mask_addr
+ end
+
+ def end_addr
+ case @family
+ when Socket::AF_INET
+ @addr | (IN4MASK ^ @mask_addr)
+ when Socket::AF_INET6
+ @addr | (IN6MASK ^ @mask_addr)
+ else
+ raise AddressFamilyError, "unsupported address family"
+ end
+ end
+
# Set +@addr+, the internal stored ip address, to given +addr+. The
# parameter +addr+ is validated using the first +family+ member,
# which is +Socket::AF_INET+ or +Socket::AF_INET6+.
@@ -498,11 +540,11 @@ class IPAddr
case family[0] ? family[0] : @family
when Socket::AF_INET
if addr < 0 || addr > IN4MASK
- raise InvalidAddressError, "invalid address: #{@addr}"
+ raise InvalidAddressError, "invalid address: #{addr}"
end
when Socket::AF_INET6
if addr < 0 || addr > IN6MASK
- raise InvalidAddressError, "invalid address: #{@addr}"
+ raise InvalidAddressError, "invalid address: #{addr}"
end
else
raise AddressFamilyError, "unsupported address family"
@@ -529,12 +571,12 @@ class IPAddr
else
m = IPAddr.new(mask)
if m.family != @family
- raise InvalidPrefixError, "address family is not same: #{@addr}"
+ raise InvalidPrefixError, "address family is not same"
end
@mask_addr = m.to_i
n = @mask_addr ^ m.instance_variable_get(:@mask_addr)
unless ((n + 1) & n).zero?
- raise InvalidPrefixError, "invalid mask #{mask}: #{@addr}"
+ raise InvalidPrefixError, "invalid mask #{mask}"
end
@addr &= @mask_addr
return self
@@ -545,13 +587,13 @@ class IPAddr
case @family
when Socket::AF_INET
if prefixlen < 0 || prefixlen > 32
- raise InvalidPrefixError, "invalid length: #{@addr}"
+ raise InvalidPrefixError, "invalid length"
end
masklen = 32 - prefixlen
@mask_addr = ((IN4MASK >> masklen) << masklen)
when Socket::AF_INET6
if prefixlen < 0 || prefixlen > 128
- raise InvalidPrefixError, "invalid length: #{@addr}"
+ raise InvalidPrefixError, "invalid length"
end
masklen = 128 - prefixlen
@mask_addr = ((IN6MASK >> masklen) << masklen)
@@ -647,12 +689,12 @@ class IPAddr
when Array
octets = addr
else
- m = RE_IPV4ADDRLIKE.match(addr) or return nil
- octets = m.captures
+ RE_IPV4ADDRLIKE.match?(addr) or return nil
+ octets = addr.split('.')
end
octets.inject(0) { |i, s|
(n = s.to_i) < 256 or raise InvalidAddressError, "invalid address: #{@addr}"
- s.match(/\A0./) and raise InvalidAddressError, "zero-filled number in IPv4 address is ambiguous: #{@addr}"
+ (s != '0') && s.start_with?('0') and raise InvalidAddressError, "zero-filled number in IPv4 address is ambiguous: #{@addr}"
i << 8 | n
}
end
diff --git a/lib/irb.rb b/lib/irb.rb
index 26432a0fac..5cb91a2938 100644
--- a/lib/irb.rb
+++ b/lib/irb.rb
@@ -1,5 +1,6 @@
-# frozen_string_literal: false
-#
+# frozen_string_literal: true
+
+# :markup: markdown
# irb.rb - irb main module
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
#
@@ -9,399 +10,883 @@ require "reline"
require_relative "irb/init"
require_relative "irb/context"
-require_relative "irb/extend-command"
+require_relative "irb/default_commands"
require_relative "irb/ruby-lex"
+require_relative "irb/statement"
require_relative "irb/input-method"
require_relative "irb/locale"
require_relative "irb/color"
require_relative "irb/version"
require_relative "irb/easter-egg"
+require_relative "irb/debug"
+require_relative "irb/pager"
-# IRB stands for "interactive Ruby" and is a tool to interactively execute Ruby
-# expressions read from the standard input.
+# ## IRB
#
-# The +irb+ command from your shell will start the interpreter.
+# Module IRB ("Interactive Ruby") provides a shell-like interface that supports
+# user interaction with the Ruby interpreter.
#
-# == Usage
+# It operates as a *read-eval-print loop*
+# ([REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop))
+# that:
#
-# Use of irb is easy if you know Ruby.
+# * ***Reads*** each character as you type. You can modify the IRB context to
+# change the way input works. See [Input](rdoc-ref:IRB@Input).
+# * ***Evaluates*** the code each time it has read a syntactically complete
+# passage.
+# * ***Prints*** after evaluating. You can modify the IRB context to change
+# the way output works. See [Output](rdoc-ref:IRB@Output).
#
-# When executing irb, prompts are displayed as follows. Then, enter the Ruby
-# expression. An input is executed when it is syntactically complete.
+#
+# Example:
#
# $ irb
-# irb(main):001:0> 1+2
-# #=> 3
-# irb(main):002:0> class Foo
-# irb(main):003:1> def foo
-# irb(main):004:2> print 1
-# irb(main):005:2> end
-# irb(main):006:1> end
-# #=> nil
-#
-# The singleline editor module or multiline editor module can be used with irb.
-# Use of multiline editor is default if it's installed.
-#
-# == Command line options
-#
-# :include: ./irb/lc/help-message
-#
-# == Commands
-#
-# The following commands are available on IRB.
-#
-# * cwws
-# * Show the current workspace.
-# * cb, cws, chws
-# * Change the current workspace to an object.
-# * bindings, workspaces
-# * Show workspaces.
-# * pushb, pushws
-# * Push an object to the workspace stack.
-# * popb, popws
-# * Pop a workspace from the workspace stack.
-# * load
-# * Load a Ruby file.
-# * require
-# * Require a Ruby file.
-# * source
-# * Loads a given file in the current session.
-# * irb
-# * Start a child IRB.
-# * jobs
-# * List of current sessions.
-# * fg
-# * Switches to the session of the given number.
-# * kill
-# * Kills the session with the given number.
-# * help
-# * Enter the mode to look up RI documents.
-# * irb_info
-# * Show information about IRB.
-# * ls
-# * Show methods, constants, and variables.
-# -g [query] or -G [query] allows you to filter out the output.
-# * measure
-# * measure enables the mode to measure processing time. measure :off disables it.
-# * $, show_source
-# * Show the source code of a given method or constant.
-# * @, whereami
-# * Show the source code around binding.irb again.
-# * debug
-# * Start the debugger of debug.gem.
-# * break, delete, next, step, continue, finish, backtrace, info, catch
-# * Start the debugger of debug.gem and run the command on it.
-#
-# == Configuration
-#
-# IRB reads a personal initialization file when it's invoked.
-# IRB searches a file in the following order and loads the first one found.
-#
-# * <tt>$IRBRC</tt> (if <tt>$IRBRC</tt> is set)
-# * <tt>$XDG_CONFIG_HOME/irb/irbrc</tt> (if <tt>$XDG_CONFIG_HOME</tt> is set)
-# * <tt>~/.irbrc</tt>
-# * +.config/irb/irbrc+
-# * +.irbrc+
-# * +irb.rc+
-# * +_irbrc+
-# * <code>$irbrc</code>
-#
-# The following are alternatives to the command line options. To use them type
-# as follows in an +irb+ session:
-#
-# IRB.conf[:IRB_NAME]="irb"
-# IRB.conf[:INSPECT_MODE]=nil
-# IRB.conf[:IRB_RC] = nil
-# IRB.conf[:BACK_TRACE_LIMIT]=16
-# IRB.conf[:USE_LOADER] = false
-# IRB.conf[:USE_MULTILINE] = nil
-# IRB.conf[:USE_SINGLELINE] = nil
-# IRB.conf[:USE_COLORIZE] = true
-# IRB.conf[:USE_TRACER] = false
-# IRB.conf[:USE_AUTOCOMPLETE] = true
-# IRB.conf[:IGNORE_SIGINT] = true
-# IRB.conf[:IGNORE_EOF] = false
-# IRB.conf[:PROMPT_MODE] = :DEFAULT
-# IRB.conf[:PROMPT] = {...}
-#
-# === Auto indentation
-#
-# To disable auto-indent mode in irb, add the following to your +.irbrc+:
+# irb(main):001> File.basename(Dir.pwd)
+# => "irb"
+# irb(main):002> Dir.entries('.').size
+# => 25
+# irb(main):003* Dir.entries('.').select do |entry|
+# irb(main):004* entry.start_with?('R')
+# irb(main):005> end
+# => ["README.md", "Rakefile"]
+#
+# The typed input may also include [\IRB-specific
+# commands](rdoc-ref:IRB@IRB-Specific+Commands).
+#
+# As seen above, you can start IRB by using the shell command `irb`.
+#
+# You can stop an IRB session by typing command `exit`:
+#
+# irb(main):006> exit
+# $
+#
+# At that point, IRB calls any hooks found in array `IRB.conf[:AT_EXIT]`, then
+# exits.
+#
+# ## Startup
+#
+# At startup, IRB:
+#
+# 1. Interprets (as Ruby code) the content of the [configuration
+# file](rdoc-ref:IRB@Configuration+File) (if given).
+# 2. Constructs the initial session context from [hash
+# IRB.conf](rdoc-ref:IRB@Hash+IRB.conf) and from default values; the hash
+# content may have been affected by [command-line
+# options](rdoc-ref:IB@Command-Line+Options), and by direct assignments in
+# the configuration file.
+# 3. Assigns the context to variable `conf`.
+# 4. Assigns command-line arguments to variable `ARGV`.
+# 5. Prints the [prompt](rdoc-ref:IRB@Prompt+and+Return+Formats).
+# 6. Puts the content of the [initialization
+# script](rdoc-ref:IRB@Initialization+Script) onto the IRB shell, just as if
+# it were user-typed commands.
+#
+#
+# ### The Command Line
+#
+# On the command line, all options precede all arguments; the first item that is
+# not recognized as an option is treated as an argument, as are all items that
+# follow.
+#
+# #### Command-Line Options
+#
+# Many command-line options affect entries in hash `IRB.conf`, which in turn
+# affect the initial configuration of the IRB session.
+#
+# Details of the options are described in the relevant subsections below.
+#
+# A cursory list of the IRB command-line options may be seen in the [help
+# message](https://raw.githubusercontent.com/ruby/irb/master/lib/irb/lc/help-message),
+# which is also displayed if you use command-line option `--help`.
+#
+# If you are interested in a specific option, consult the
+# [index](rdoc-ref:doc/irb/indexes.md@Index+of+Command-Line+Options).
+#
+# #### Command-Line Arguments
+#
+# Command-line arguments are passed to IRB in array `ARGV`:
+#
+# $ irb --noscript Foo Bar Baz
+# irb(main):001> ARGV
+# => ["Foo", "Bar", "Baz"]
+# irb(main):002> exit
+# $
+#
+# Command-line option `--` causes everything that follows to be treated as
+# arguments, even those that look like options:
+#
+# $ irb --noscript -- --noscript -- Foo Bar Baz
+# irb(main):001> ARGV
+# => ["--noscript", "--", "Foo", "Bar", "Baz"]
+# irb(main):002> exit
+# $
+#
+# ### Configuration File
+#
+# You can initialize IRB via a *configuration file*.
+#
+# If command-line option `-f` is given, no configuration file is looked for.
+#
+# Otherwise, IRB reads and interprets a configuration file if one is available.
+#
+# The configuration file can contain any Ruby code, and can usefully include
+# user code that:
+#
+# * Can then be debugged in IRB.
+# * Configures IRB itself.
+# * Requires or loads files.
+#
+#
+# The path to the configuration file is the first found among:
+#
+# * The value of variable `$IRBRC`, if defined.
+# * The value of variable `$XDG_CONFIG_HOME/irb/irbrc`, if defined.
+# * File `$HOME/.irbrc`, if it exists.
+# * File `$HOME/.config/irb/irbrc`, if it exists.
+# * File `.irbrc` in the current directory, if it exists.
+# * File `irb.rc` in the current directory, if it exists.
+# * File `_irbrc` in the current directory, if it exists.
+# * File `$irbrc` in the current directory, if it exists.
+#
+#
+# If the search fails, there is no configuration file.
+#
+# If the search succeeds, the configuration file is read as Ruby code, and so
+# can contain any Ruby programming you like.
+#
+# Method `conf.rc?` returns `true` if a configuration file was read, `false`
+# otherwise. Hash entry `IRB.conf[:RC]` also contains that value.
+#
+# ### Hash `IRB.conf`
+#
+# The initial entries in hash `IRB.conf` are determined by:
+#
+# * Default values.
+# * Command-line options, which may override defaults.
+# * Direct assignments in the configuration file.
+#
+#
+# You can see the hash by typing `IRB.conf`.
+#
+# Details of the entries' meanings are described in the relevant subsections
+# below.
+#
+# If you are interested in a specific entry, consult the
+# [index](rdoc-ref:doc/irb/indexes.md@Index+of+IRB.conf+Entries).
+#
+# ### Notes on Initialization Precedence
+#
+# * Any conflict between an entry in hash `IRB.conf` and a command-line option
+# is resolved in favor of the hash entry.
+# * Hash `IRB.conf` affects the context only once, when the configuration file
+# is interpreted; any subsequent changes to it do not affect the context and
+# are therefore essentially meaningless.
+#
+#
+# ### Initialization Script
+#
+# By default, the first command-line argument (after any options) is the path to
+# a Ruby initialization script.
+#
+# IRB reads the initialization script and puts its content onto the IRB shell,
+# just as if it were user-typed commands.
+#
+# Command-line option `--noscript` causes the first command-line argument to be
+# treated as an ordinary argument (instead of an initialization script);
+# `--script` is the default.
+#
+# ## Input
+#
+# This section describes the features that allow you to change the way IRB input
+# works; see also [Input and Output](rdoc-ref:IRB@Input+and+Output).
+#
+# ### Input Command History
+#
+# By default, IRB stores a history of up to 1000 input commands in a file named
+# `.irb_history`. The history file will be in the same directory as the
+# [configuration file](rdoc-ref:IRB@Configuration+File) if one is found, or in
+# `~/` otherwise.
+#
+# A new IRB session creates the history file if it does not exist, and appends
+# to the file if it does exist.
+#
+# You can change the filepath by adding to your configuration file:
+# `IRB.conf[:HISTORY_FILE] = *filepath*`, where *filepath* is a string filepath.
+#
+# During the session, method `conf.history_file` returns the filepath, and
+# method `conf.history_file = *new_filepath*` copies the history to the file at
+# *new_filepath*, which becomes the history file for the session.
+#
+# You can change the number of commands saved by adding to your configuration
+# file: `IRB.conf[:SAVE_HISTORY] = *n*`, wheHISTORY_FILEre *n* is one of:
+#
+# * Positive integer: the number of commands to be saved,
+# * Zero: all commands are to be saved.
+# * `nil`: no commands are to be saved,.
+#
+#
+# During the session, you can use methods `conf.save_history` or
+# `conf.save_history=` to retrieve or change the count.
+#
+# ### Command Aliases
+#
+# By default, IRB defines several command aliases:
+#
+# irb(main):001> conf.command_aliases
+# => {:"$"=>:show_source, :"@"=>:whereami}
+#
+# You can change the initial aliases in the configuration file with:
+#
+# IRB.conf[:COMMAND_ALIASES] = {foo: :show_source, bar: :whereami}
+#
+# You can replace the current aliases at any time with configuration method
+# `conf.command_aliases=`; Because `conf.command_aliases` is a hash, you can
+# modify it.
+#
+# ### End-of-File
+#
+# By default, `IRB.conf[:IGNORE_EOF]` is `false`, which means that typing the
+# end-of-file character `Ctrl-D` causes the session to exit.
+#
+# You can reverse that behavior by adding `IRB.conf[:IGNORE_EOF] = true` to the
+# configuration file.
+#
+# During the session, method `conf.ignore_eof?` returns the setting, and method
+# `conf.ignore_eof = *boolean*` sets it.
+#
+# ### SIGINT
+#
+# By default, `IRB.conf[:IGNORE_SIGINT]` is `true`, which means that typing the
+# interrupt character `Ctrl-C` causes the session to exit.
+#
+# You can reverse that behavior by adding `IRB.conf[:IGNORE_SIGING] = false` to
+# the configuration file.
+#
+# During the session, method `conf.ignore_siging?` returns the setting, and
+# method `conf.ignore_sigint = *boolean*` sets it.
+#
+# ### Automatic Completion
+#
+# By default, IRB enables [automatic
+# completion](https://en.wikipedia.org/wiki/Autocomplete#In_command-line_interpr
+# eters):
+#
+# You can disable it by either of these:
+#
+# * Adding `IRB.conf[:USE_AUTOCOMPLETE] = false` to the configuration file.
+# * Giving command-line option `--noautocomplete` (`--autocomplete` is the
+# default).
+#
+#
+# Method `conf.use_autocomplete?` returns `true` if automatic completion is
+# enabled, `false` otherwise.
+#
+# The setting may not be changed during the session.
+#
+# ### Automatic Indentation
+#
+# By default, IRB automatically indents lines of code to show structure (e.g.,
+# it indent the contents of a block).
+#
+# The current setting is returned by the configuration method
+# `conf.auto_indent_mode`.
+#
+# The default initial setting is `true`:
+#
+# irb(main):001> conf.auto_indent_mode
+# => true
+# irb(main):002* Dir.entries('.').select do |entry|
+# irb(main):003* entry.start_with?('R')
+# irb(main):004> end
+# => ["README.md", "Rakefile"]
+#
+# You can change the initial setting in the configuration file with:
#
# IRB.conf[:AUTO_INDENT] = false
#
-# === Autocompletion
+# Note that the *current* setting *may not* be changed in the IRB session.
+#
+# ### Input Method
+#
+# The IRB input method determines how command input is to be read; by default,
+# the input method for a session is IRB::RelineInputMethod.
+#
+# You can set the input method by:
+#
+# * Adding to the configuration file:
+#
+# * `IRB.conf[:USE_SINGLELINE] = true` or `IRB.conf[:USE_MULTILINE]=
+# false` sets the input method to IRB::ReadlineInputMethod.
+# * `IRB.conf[:USE_SINGLELINE] = false` or `IRB.conf[:USE_MULTILINE] =
+# true` sets the input method to IRB::RelineInputMethod.
+#
+#
+# * Giving command-line options:
+#
+# * `--singleline` or `--nomultiline` sets the input method to
+# IRB::ReadlineInputMethod.
+# * `--nosingleline` or `--multiline` sets the input method to
+# IRB::RelineInputMethod.
+#
+#
+#
+# Method `conf.use_multiline?` and its synonym `conf.use_reline` return:
+#
+# * `true` if option `--multiline` was given.
+# * `false` if option `--nomultiline` was given.
+# * `nil` if neither was given.
+#
+#
+# Method `conf.use_singleline?` and its synonym `conf.use_readline` return:
+#
+# * `true` if option `--singleline` was given.
+# * `false` if option `--nosingleline` was given.
+# * `nil` if neither was given.
+#
+#
+# ## Output
+#
+# This section describes the features that allow you to change the way IRB
+# output works; see also [Input and Output](rdoc-ref:IRB@Input+and+Output).
+#
+# ### Return-Value Printing (Echoing)
+#
+# By default, IRB prints (echoes) the values returned by all input commands.
+#
+# You can change the initial behavior and suppress all echoing by:
+#
+# * Adding to the configuration file: `IRB.conf[:ECHO] = false`. (The default
+# value for this entry is `nil`, which means the same as `true`.)
+# * Giving command-line option `--noecho`. (The default is `--echo`.)
+#
+#
+# During the session, you can change the current setting with configuration
+# method `conf.echo=` (set to `true` or `false`).
+#
+# As stated above, by default IRB prints the values returned by all input
+# commands; but IRB offers special treatment for values returned by assignment
+# statements, which may be:
+#
+# * Printed with truncation (to fit on a single line of output), which is the
+# default; an ellipsis (`...` is suffixed, to indicate the truncation):
+#
+# irb(main):001> x = 'abc' * 100
+#
+#
+# > "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc...
+#
+# * Printed in full (regardless of the length).
+# * Suppressed (not printed at all)
+#
+#
+# You can change the initial behavior by:
+#
+# * Adding to the configuration file: `IRB.conf[:ECHO_ON_ASSIGNMENT] = false`.
+# (The default value for this entry is `niL`, which means the same as
+# `:truncate`.)
+# * Giving command-line option `--noecho-on-assignment` or
+# `--echo-on-assignment`. (The default is `--truncate-echo-on-assignment`.)
+#
+#
+# During the session, you can change the current setting with configuration
+# method `conf.echo_on_assignment=` (set to `true`, `false`, or `:truncate`).
#
-# To disable autocompletion for irb, add the following to your +.irbrc+:
+# By default, IRB formats returned values by calling method `inspect`.
#
-# IRB.conf[:USE_AUTOCOMPLETE] = false
+# You can change the initial behavior by:
#
-# === History
+# * Adding to the configuration file: `IRB.conf[:INSPECT_MODE] = false`. (The
+# default value for this entry is `true`.)
+# * Giving command-line option `--noinspect`. (The default is `--inspect`.)
#
-# By default, irb will store the last 1000 commands you used in
-# <code>IRB.conf[:HISTORY_FILE]</code> (<code>~/.irb_history</code> by default).
#
-# If you want to disable history, add the following to your +.irbrc+:
+# During the session, you can change the setting using method
+# `conf.inspect_mode=`.
#
-# IRB.conf[:SAVE_HISTORY] = nil
+# ### Multiline Output
#
-# See IRB::Context#save_history= for more information.
+# By default, IRB prefixes a newline to a multiline response.
#
-# The history of _results_ of commands evaluated is not stored by default,
-# but can be turned on to be stored with this +.irbrc+ setting:
+# You can change the initial default value by adding to the configuration file:
#
-# IRB.conf[:EVAL_HISTORY] = <number>
+# IRB.conf[:NEWLINE_BEFORE_MULTILINE_OUTPUT] = false
#
-# See IRB::Context#eval_history= and History class. The history of command
-# results is not permanently saved in any file.
+# During a session, you can retrieve or set the value using methods
+# `conf.newline_before_multiline_output?` and
+# `conf.newline_before_multiline_output=`.
#
-# == Customizing the IRB Prompt
+# Examples:
#
-# In order to customize the prompt, you can change the following Hash:
+# irb(main):001> conf.inspect_mode = false
+# => false
+# irb(main):002> "foo\nbar"
+# =>
+# foo
+# bar
+# irb(main):003> conf.newline_before_multiline_output = false
+# => false
+# irb(main):004> "foo\nbar"
+# => foo
+# bar
#
-# IRB.conf[:PROMPT]
+# ### Evaluation History
#
-# This example can be used in your +.irbrc+
+# By default, IRB saves no history of evaluations (returned values), and the
+# related methods `conf.eval_history`, `_`, and `__` are undefined.
#
-# IRB.conf[:PROMPT][:MY_PROMPT] = { # name of prompt mode
-# :AUTO_INDENT => false, # disables auto-indent mode
-# :PROMPT_I => ">> ", # simple prompt
-# :PROMPT_S => nil, # prompt for continuated strings
-# :PROMPT_C => nil, # prompt for continuated statement
-# :RETURN => " ==>%s\n" # format to return value
-# }
+# You can turn on that history, and set the maximum number of evaluations to be
+# stored:
+#
+# * In the configuration file: add `IRB.conf[:EVAL_HISTORY] = *n*`. (Examples
+# below assume that we've added `IRB.conf[:EVAL_HISTORY] = 5`.)
+# * In the session (at any time): `conf.eval_history = *n*`.
+#
+#
+# If `n` is zero, all evaluation history is stored.
+#
+# Doing either of the above:
+#
+# * Sets the maximum size of the evaluation history; defines method
+# `conf.eval_history`, which returns the maximum size `n` of the evaluation
+# history:
+#
+# irb(main):001> conf.eval_history = 5
+# => 5
+# irb(main):002> conf.eval_history
+# => 5
+#
+# * Defines variable `_`, which contains the most recent evaluation, or `nil`
+# if none; same as method `conf.last_value`:
+#
+# irb(main):003> _
+# => 5
+# irb(main):004> :foo
+# => :foo
+# irb(main):005> :bar
+# => :bar
+# irb(main):006> _
+# => :bar
+# irb(main):007> _
+# => :bar
+#
+# * Defines variable `__`:
+#
+# * `__` unadorned: contains all evaluation history:
+#
+# irb(main):008> :foo
+# => :foo
+# irb(main):009> :bar
+# => :bar
+# irb(main):010> :baz
+# => :baz
+# irb(main):011> :bat
+# => :bat
+# irb(main):012> :bam
+# => :bam
+# irb(main):013> __
+# =>
+# 9 :bar
+# 10 :baz
+# 11 :bat
+# 12 :bam
+# irb(main):014> __
+# =>
+# 10 :baz
+# 11 :bat
+# 12 :bam
+# 13 ...self-history...
+#
+# Note that when the evaluation is multiline, it is displayed
+# differently.
+#
+# * `__[`*m*`]`:
+#
+# * Positive *m*: contains the evaluation for the given line number,
+# or `nil` if that line number is not in the evaluation history:
+#
+# irb(main):015> __[12]
+# => :bam
+# irb(main):016> __[1]
+# => nil
+#
+# * Negative *m*: contains the `mth`-from-end evaluation, or `nil` if
+# that evaluation is not in the evaluation history:
+#
+# irb(main):017> __[-3]
+# => :bam
+# irb(main):018> __[-13]
+# => nil
+#
+# * Zero *m*: contains `nil`:
+#
+# irb(main):019> __[0]
+# => nil
+#
+#
+#
+#
+# ### Prompt and Return Formats
+#
+# By default, IRB uses the prompt and return value formats defined in its
+# `:DEFAULT` prompt mode.
+#
+# #### The Default Prompt and Return Format
+#
+# The default prompt and return values look like this:
+#
+# irb(main):001> 1 + 1
+# => 2
+# irb(main):002> 2 + 2
+# => 4
+#
+# The prompt includes:
+#
+# * The name of the running program (`irb`); see [IRB
+# Name](rdoc-ref:IRB@IRB+Name).
+# * The name of the current session (`main`); See [IRB
+# Sessions](rdoc-ref:IRB@IRB+Sessions).
+# * A 3-digit line number (1-based).
+#
+#
+# The default prompt actually defines three formats:
+#
+# * One for most situations (as above):
+#
+# irb(main):003> Dir
+# => Dir
+#
+# * One for when the typed command is a statement continuation (adds trailing
+# asterisk):
+#
+# irb(main):004* Dir.
+#
+# * One for when the typed command is a string continuation (adds trailing
+# single-quote):
+#
+# irb(main):005' Dir.entries('.
+#
+#
+# You can see the prompt change as you type the characters in the following:
+#
+# irb(main):001* Dir.entries('.').select do |entry|
+# irb(main):002* entry.start_with?('R')
+# irb(main):003> end
+# => ["README.md", "Rakefile"]
+#
+# #### Pre-Defined Prompts
+#
+# IRB has several pre-defined prompts, stored in hash `IRB.conf[:PROMPT]`:
+#
+# irb(main):001> IRB.conf[:PROMPT].keys
+# => [:NULL, :DEFAULT, :CLASSIC, :SIMPLE, :INF_RUBY, :XMP]
+#
+# To see the full data for these, type `IRB.conf[:PROMPT]`.
+#
+# Most of these prompt definitions include specifiers that represent values like
+# the IRB name, session name, and line number; see [Prompt
+# Specifiers](rdoc-ref:IRB@Prompt+Specifiers).
+#
+# You can change the initial prompt and return format by:
+#
+# * Adding to the configuration file: `IRB.conf[:PROMPT] = *mode*` where
+# *mode* is the symbol name of a prompt mode.
+# * Giving a command-line option:
+#
+# * `--prompt *mode*`: sets the prompt mode to *mode*. where *mode* is the
+# symbol name of a prompt mode.
+# * `--simple-prompt` or `--sample-book-mode`: sets the prompt mode to
+# `:SIMPLE`.
+# * `--inf-ruby-mode`: sets the prompt mode to `:INF_RUBY` and suppresses
+# both `--multiline` and `--singleline`.
+# * `--noprompt`: suppresses prompting; does not affect echoing.
+#
+#
+#
+# You can retrieve or set the current prompt mode with methods
+#
+# `conf.prompt_mode` and `conf.prompt_mode=`.
+#
+# If you're interested in prompts and return formats other than the defaults,
+# you might experiment by trying some of the others.
+#
+# #### Custom Prompts
+#
+# You can also define custom prompts and return formats, which may be done
+# either in an IRB session or in the configuration file.
+#
+# A prompt in IRB actually defines three prompts, as seen above. For simple
+# custom data, we'll make all three the same:
+#
+# irb(main):001* IRB.conf[:PROMPT][:MY_PROMPT] = {
+# irb(main):002* PROMPT_I: ': ',
+# irb(main):003* PROMPT_C: ': ',
+# irb(main):004* PROMPT_S: ': ',
+# irb(main):005* RETURN: '=> '
+# irb(main):006> }
+# => {:PROMPT_I=>": ", :PROMPT_C=>": ", :PROMPT_S=>": ", :RETURN=>"=> "}
+#
+# If you define the custom prompt in the configuration file, you can also make
+# it the current prompt by adding:
#
# IRB.conf[:PROMPT_MODE] = :MY_PROMPT
#
-# Or, invoke irb with the above prompt mode by:
-#
-# irb --prompt my-prompt
-#
-# Constants +PROMPT_I+, +PROMPT_S+ and +PROMPT_C+ specify the format. In the
-# prompt specification, some special strings are available:
-#
-# %N # command name which is running
-# %m # to_s of main object (self)
-# %M # inspect of main object (self)
-# %l # type of string(", ', /, ]), `]' is inner %w[...]
-# %NNi # indent level. NN is digits and means as same as printf("%NNd").
-# # It can be omitted
-# %NNn # line number.
-# %% # %
-#
-# For instance, the default prompt mode is defined as follows:
-#
-# IRB.conf[:PROMPT_MODE][:DEFAULT] = {
-# :PROMPT_I => "%N(%m):%03n:%i> ",
-# :PROMPT_N => "%N(%m):%03n:%i> ",
-# :PROMPT_S => "%N(%m):%03n:%i%l ",
-# :PROMPT_C => "%N(%m):%03n:%i* ",
-# :RETURN => "%s\n" # used to printf
-# }
-#
-# irb comes with a number of available modes:
-#
-# # :NULL:
-# # :PROMPT_I:
-# # :PROMPT_N:
-# # :PROMPT_S:
-# # :PROMPT_C:
-# # :RETURN: |
-# # %s
-# # :DEFAULT:
-# # :PROMPT_I: ! '%N(%m):%03n:%i> '
-# # :PROMPT_N: ! '%N(%m):%03n:%i> '
-# # :PROMPT_S: ! '%N(%m):%03n:%i%l '
-# # :PROMPT_C: ! '%N(%m):%03n:%i* '
-# # :RETURN: |
-# # => %s
-# # :CLASSIC:
-# # :PROMPT_I: ! '%N(%m):%03n:%i> '
-# # :PROMPT_N: ! '%N(%m):%03n:%i> '
-# # :PROMPT_S: ! '%N(%m):%03n:%i%l '
-# # :PROMPT_C: ! '%N(%m):%03n:%i* '
-# # :RETURN: |
-# # %s
-# # :SIMPLE:
-# # :PROMPT_I: ! '>> '
-# # :PROMPT_N: ! '>> '
-# # :PROMPT_S:
-# # :PROMPT_C: ! '?> '
-# # :RETURN: |
-# # => %s
-# # :INF_RUBY:
-# # :PROMPT_I: ! '%N(%m):%03n:%i> '
-# # :PROMPT_N:
-# # :PROMPT_S:
-# # :PROMPT_C:
-# # :RETURN: |
-# # %s
-# # :AUTO_INDENT: true
-# # :XMP:
-# # :PROMPT_I:
-# # :PROMPT_N:
-# # :PROMPT_S:
-# # :PROMPT_C:
-# # :RETURN: |2
-# # ==>%s
-#
-# == Restrictions
-#
-# Because irb evaluates input immediately after it is syntactically complete,
-# the results may be slightly different than directly using Ruby.
-#
-# == IRB Sessions
+# Regardless of where it's defined, you can make it the current prompt in a
+# session:
#
-# IRB has a special feature, that allows you to manage many sessions at once.
+# conf.prompt_mode = :MY_PROMPT
#
-# You can create new sessions with Irb.irb, and get a list of current sessions
-# with the +jobs+ command in the prompt.
+# You can view or modify the current prompt data with various configuration
+# methods:
+#
+# * `conf.prompt_mode`, `conf.prompt_mode=`.
+# * `conf.prompt_c`, `conf.c=`.
+# * `conf.prompt_i`, `conf.i=`.
+# * `conf.prompt_s`, `conf.s=`.
+# * `conf.return_format`, `return_format=`.
+#
+#
+# #### Prompt Specifiers
+#
+# A prompt's definition can include specifiers for which certain values are
+# substituted:
+#
+# * `%N`: the name of the running program.
+# * `%m`: the value of `self.to_s`.
+# * `%M`: the value of `self.inspect`.
+# * `%l`: an indication of the type of string; one of `"`, `'`, `/`, `]`.
+# * `%NNi`: Indentation level. NN is a 2-digit number that specifies the number
+# of digits of the indentation level (03 will result in 001).
+# * `%NNn`: Line number. NN is a 2-digit number that specifies the number
+# of digits of the line number (03 will result in 001).
+# * `%%`: Literal `%`.
+#
+#
+# ### Verbosity
+#
+# By default, IRB verbosity is disabled, which means that output is smaller
+# rather than larger.
+#
+# You can enable verbosity by:
+#
+# * Adding to the configuration file: `IRB.conf[:VERBOSE] = true` (the default
+# is `nil`).
+# * Giving command-line options `--verbose` (the default is `--noverbose`).
+#
+#
+# During a session, you can retrieve or set verbosity with methods
+# `conf.verbose` and `conf.verbose=`.
+#
+# ### Help
+#
+# Command-line option `--version` causes IRB to print its help text and exit.
+#
+# ### Version
+#
+# Command-line option `--version` causes IRB to print its version text and exit.
+#
+# ## Input and Output
+#
+# ### Color Highlighting
+#
+# By default, IRB color highlighting is enabled, and is used for both:
+#
+# * Input: As you type, IRB reads the typed characters and highlights elements
+# that it recognizes; it also highlights errors such as mismatched
+# parentheses.
+# * Output: IRB highlights syntactical elements.
+#
+#
+# You can disable color highlighting by:
+#
+# * Adding to the configuration file: `IRB.conf[:USE_COLORIZE] = false` (the
+# default value is `true`).
+# * Giving command-line option `--nocolorize`
+#
+#
+# ## Debugging
+#
+# Command-line option `-d` sets variables `$VERBOSE` and `$DEBUG` to `true`;
+# these have no effect on IRB output.
+#
+# ### Warnings
+#
+# Command-line option `-w` suppresses warnings.
+#
+# Command-line option `-W[*level*]` sets warning level;
+#
+# * 0=silence
+# * 1=medium
+# * 2=verbose
+#
+# ## Other Features
+#
+# ### Load Modules
+#
+# You can specify the names of modules that are to be required at startup.
+#
+# Array `conf.load_modules` determines the modules (if any) that are to be
+# required during session startup. The array is used only during session
+# startup, so the initial value is the only one that counts.
+#
+# The default initial value is `[]` (load no modules):
+#
+# irb(main):001> conf.load_modules
+# => []
+#
+# You can set the default initial value via:
+#
+# * Command-line option `-r`
+#
+# $ irb -r csv -r json
+# irb(main):001> conf.load_modules
+# => ["csv", "json"]
+#
+# * Hash entry `IRB.conf[:LOAD_MODULES] = *array*`:
+#
+# IRB.conf[:LOAD_MODULES] = %w[csv, json]
+#
+#
+# Note that the configuration file entry overrides the command-line options.
+#
+# ### RI Documentation Directories
+#
+# You can specify the paths to RI documentation directories that are to be
+# loaded (in addition to the default directories) at startup; see details about
+# RI by typing `ri --help`.
+#
+# Array `conf.extra_doc_dirs` determines the directories (if any) that are to be
+# loaded during session startup. The array is used only during session startup,
+# so the initial value is the only one that counts.
+#
+# The default initial value is `[]` (load no extra documentation):
+#
+# irb(main):001> conf.extra_doc_dirs
+# => []
+#
+# You can set the default initial value via:
+#
+# * Command-line option `--extra_doc_dir`
+#
+# $ irb --extra-doc-dir your_doc_dir --extra-doc-dir my_doc_dir
+# irb(main):001> conf.extra_doc_dirs
+# => ["your_doc_dir", "my_doc_dir"]
+#
+# * Hash entry `IRB.conf[:EXTRA_DOC_DIRS] = *array*`:
+#
+# IRB.conf[:EXTRA_DOC_DIRS] = %w[your_doc_dir my_doc_dir]
+#
+#
+# Note that the configuration file entry overrides the command-line options.
+#
+# ### IRB Name
+#
+# You can specify a name for IRB.
+#
+# The default initial value is `'irb'`:
+#
+# irb(main):001> conf.irb_name
+# => "irb"
+#
+# You can set the default initial value via hash entry `IRB.conf[:IRB_NAME] =
+# *string*`:
+#
+# IRB.conf[:IRB_NAME] = 'foo'
+#
+# ### Application Name
+#
+# You can specify an application name for the IRB session.
#
-# === Commands
+# The default initial value is `'irb'`:
#
-# JobManager provides commands to handle the current sessions:
+# irb(main):001> conf.ap_name
+# => "irb"
#
-# jobs # List of current sessions
-# fg # Switches to the session of the given number
-# kill # Kills the session with the given number
+# You can set the default initial value via hash entry `IRB.conf[:AP_NAME] =
+# *string*`:
#
-# The +exit+ command, or ::irb_exit, will quit the current session and call any
-# exit hooks with IRB.irb_at_exit.
+# IRB.conf[:AP_NAME] = 'my_ap_name'
#
-# A few commands for loading files within the session are also available:
+# ### Configuration Monitor
#
-# +source+::
-# Loads a given file in the current session and displays the source lines,
-# see IrbLoader#source_file
-# +irb_load+::
-# Loads the given file similarly to Kernel#load, see IrbLoader#irb_load
-# +irb_require+::
-# Loads the given file similarly to Kernel#require
+# You can monitor changes to the configuration by assigning a proc to
+# `IRB.conf[:IRB_RC]` in the configuration file:
#
-# === Configuration
+# IRB.conf[:IRB_RC] = proc {|conf| puts conf.class }
+#
+# Each time the configuration is changed, that proc is called with argument
+# `conf`:
+#
+# ### Encodings
+#
+# Command-line option `-E *ex*[:*in*]` sets initial external (ex) and internal
+# (in) encodings.
+#
+# Command-line option `-U` sets both to UTF-8.
+#
+# ### Commands
+#
+# Please use the `help` command to see the list of available commands.
+#
+# ### IRB Sessions
+#
+# IRB has a special feature, that allows you to manage many sessions at once.
+#
+# You can create new sessions with Irb.irb, and get a list of current sessions
+# with the `jobs` command in the prompt.
+#
+# #### Configuration
#
# The command line options, or IRB.conf, specify the default behavior of
# Irb.irb.
#
-# On the other hand, each conf in IRB@Command+line+options is used to
+# On the other hand, each conf in IRB@Command-Line+Options is used to
# individually configure IRB.irb.
#
-# If a proc is set for <code>IRB.conf[:IRB_RC]</code>, its will be invoked after execution
+# If a proc is set for `IRB.conf[:IRB_RC]`, its will be invoked after execution
# of that proc with the context of the current session as its argument. Each
# session can be configured using this mechanism.
#
-# === Session variables
+# #### Session variables
#
# There are a few variables in every Irb session that can come in handy:
#
-# <code>_</code>::
-# The value command executed, as a local variable
-# <code>__</code>::
-# The history of evaluated commands. Available only if
-# <code>IRB.conf[:EVAL_HISTORY]</code> is not +nil+ (which is the default).
-# See also IRB::Context#eval_history= and IRB::History.
-# <code>__[line_no]</code>::
-# Returns the evaluation value at the given line number, +line_no+.
-# If +line_no+ is a negative, the return value +line_no+ many lines before
-# the most recent return value.
-#
-# === Example using IRB Sessions
-#
-# # invoke a new session
-# irb(main):001:0> irb
-# # list open sessions
-# irb.1(main):001:0> jobs
-# #0->irb on main (#<Thread:0x400fb7e4> : stop)
-# #1->irb#1 on main (#<Thread:0x40125d64> : running)
-#
-# # change the active session
-# irb.1(main):002:0> fg 0
-# # define class Foo in top-level session
-# irb(main):002:0> class Foo;end
-# # invoke a new session with the context of Foo
-# irb(main):003:0> irb Foo
-# # define Foo#foo
-# irb.2(Foo):001:0> def foo
-# irb.2(Foo):002:1> print 1
-# irb.2(Foo):003:1> end
-#
-# # change the active session
-# irb.2(Foo):004:0> fg 0
-# # list open sessions
-# irb(main):004:0> jobs
-# #0->irb on main (#<Thread:0x400fb7e4> : running)
-# #1->irb#1 on main (#<Thread:0x40125d64> : stop)
-# #2->irb#2 on Foo (#<Thread:0x4011d54c> : stop)
-# # check if Foo#foo is available
-# irb(main):005:0> Foo.instance_methods #=> [:foo, ...]
-#
-# # change the active session
-# irb(main):006:0> fg 2
-# # define Foo#bar in the context of Foo
-# irb.2(Foo):005:0> def bar
-# irb.2(Foo):006:1> print "bar"
-# irb.2(Foo):007:1> end
-# irb.2(Foo):010:0> Foo.instance_methods #=> [:bar, :foo, ...]
-#
-# # change the active session
-# irb.2(Foo):011:0> fg 0
-# irb(main):007:0> f = Foo.new #=> #<Foo:0x4010af3c>
-# # invoke a new session with the context of f (instance of Foo)
-# irb(main):008:0> irb f
-# # list open sessions
-# irb.3(<Foo:0x4010af3c>):001:0> jobs
-# #0->irb on main (#<Thread:0x400fb7e4> : stop)
-# #1->irb#1 on main (#<Thread:0x40125d64> : stop)
-# #2->irb#2 on Foo (#<Thread:0x4011d54c> : stop)
-# #3->irb#3 on #<Foo:0x4010af3c> (#<Thread:0x4010a1e0> : running)
-# # evaluate f.foo
-# irb.3(<Foo:0x4010af3c>):002:0> foo #=> 1 => nil
-# # evaluate f.bar
-# irb.3(<Foo:0x4010af3c>):003:0> bar #=> bar => nil
-# # kill jobs 1, 2, and 3
-# irb.3(<Foo:0x4010af3c>):004:0> kill 1, 2, 3
-# # list open sessions, should only include main session
-# irb(main):009:0> jobs
-# #0->irb on main (#<Thread:0x400fb7e4> : running)
-# # quit irb
-# irb(main):010:0> exit
+# `_`
+# : The value command executed, as a local variable
+# `__`
+# : The history of evaluated commands. Available only if
+# `IRB.conf[:EVAL_HISTORY]` is not `nil` (which is the default). See also
+# IRB::Context#eval_history= and IRB::History.
+# `__[line_no]`
+# : Returns the evaluation value at the given line number, `line_no`. If
+# `line_no` is a negative, the return value `line_no` many lines before the
+# most recent return value.
+#
+#
+# ## Restrictions
+#
+# Ruby code typed into IRB behaves the same as Ruby code in a file, except that:
+#
+# * Because IRB evaluates input immediately after it is syntactically
+# complete, some results may be slightly different.
+# * Forking may not be well behaved.
+#
module IRB
# An exception raised by IRB.irb_abort
class Abort < Exception;end
- @CONF = {}
-
-
- # Displays current configuration.
- #
- # Modifying the configuration is achieved by sending a message to IRB.conf.
- #
- # See IRB@Configuration for more information.
- def IRB.conf
- @CONF
- end
-
- # Returns the current version of IRB, including release version and last
- # updated date.
- def IRB.version
- if v = @CONF[:VERSION] then return v end
-
- @CONF[:VERSION] = format("irb %s (%s)", @RELEASE_VERSION, @LAST_UPDATE_DATE)
- end
-
# The current IRB::Context of the session, see IRB.conf
#
- # irb
- # irb(main):001:0> IRB.CurrentContext.irb_name = "foo"
- # foo(main):002:0> IRB.conf[:MAIN_CONTEXT].irb_name #=> "foo"
- def IRB.CurrentContext
+ # irb
+ # irb(main):001:0> IRB.CurrentContext.irb_name = "foo"
+ # foo(main):002:0> IRB.conf[:MAIN_CONTEXT].irb_name #=> "foo"
+ def IRB.CurrentContext # :nodoc:
IRB.conf[:MAIN_CONTEXT]
end
- # Initializes IRB and creates a new Irb.irb object at the +TOPLEVEL_BINDING+
+ # Initializes IRB and creates a new Irb.irb object at the `TOPLEVEL_BINDING`
def IRB.start(ap_path = nil)
STDOUT.sync = true
$0 = File::basename(ap_path, ".rb") if ap_path
@@ -417,62 +902,45 @@ module IRB
end
# Quits irb
- def IRB.irb_exit(irb, ret)
- throw :IRB_EXIT, ret
+ def IRB.irb_exit(*) # :nodoc:
+ throw :IRB_EXIT, false
end
# Aborts then interrupts irb.
#
- # Will raise an Abort exception, or the given +exception+.
- def IRB.irb_abort(irb, exception = Abort)
+ # Will raise an Abort exception, or the given `exception`.
+ def IRB.irb_abort(irb, exception = Abort) # :nodoc:
irb.context.thread.raise exception, "abort then interrupt!"
end
class Irb
- ASSIGNMENT_NODE_TYPES = [
- # Local, instance, global, class, constant, instance, and index assignment:
- # "foo = bar",
- # "@foo = bar",
- # "$foo = bar",
- # "@@foo = bar",
- # "::Foo = bar",
- # "a::Foo = bar",
- # "Foo = bar"
- # "foo.bar = 1"
- # "foo[1] = bar"
- :assign,
-
- # Operation assignment:
- # "foo += bar"
- # "foo -= bar"
- # "foo ||= bar"
- # "foo &&= bar"
- :opassign,
-
- # Multiple assignment:
- # "foo, bar = 1, 2
- :massign,
- ]
# Note: instance and index assignment expressions could also be written like:
- # "foo.bar=(1)" and "foo.[]=(1, bar)", when expressed that way, the former
- # be parsed as :assign and echo will be suppressed, but the latter is
- # parsed as a :method_add_arg and the output won't be suppressed
+ # "foo.bar=(1)" and "foo.[]=(1, bar)", when expressed that way, the former be
+ # parsed as :assign and echo will be suppressed, but the latter is parsed as a
+ # :method_add_arg and the output won't be suppressed
PROMPT_MAIN_TRUNCATE_LENGTH = 32
- PROMPT_MAIN_TRUNCATE_OMISSION = '...'.freeze
- CONTROL_CHARACTERS_PATTERN = "\x00-\x1F".freeze
+ PROMPT_MAIN_TRUNCATE_OMISSION = '...'
+ CONTROL_CHARACTERS_PATTERN = "\x00-\x1F"
+
+ # Returns the current context of this irb session
+ attr_reader :context
+ # The lexer used by this irb session
+ attr_accessor :scanner
# Creates a new irb session
def initialize(workspace = nil, input_method = nil)
@context = Context.new(self, workspace, input_method)
- @context.main.extend ExtendCommandBundle
+ @context.workspace.load_helper_methods_to_main
@signal_status = :IN_IRB
- @scanner = RubyLex.new(@context)
+ @scanner = RubyLex.new
+ @line_no = 1
end
- # A hook point for `debug` command's TracePoint after :IRB_EXIT as well as its clean-up
+ # A hook point for `debug` command's breakpoint after :IRB_EXIT as well as its
+ # clean-up
def debug_break
- # it means the debug command is executed
+ # it means the debug integration has been activated
if defined?(DEBUGGER__) && DEBUGGER__.respond_to?(:capture_frames_without_irb)
# after leaving this initial breakpoint, revert the capture_frames patch
DEBUGGER__.singleton_class.send(:alias_method, :capture_frames, :capture_frames_without_irb)
@@ -481,107 +949,101 @@ module IRB
end
end
+ def debug_readline(binding)
+ workspace = IRB::WorkSpace.new(binding)
+ context.replace_workspace(workspace)
+ context.workspace.load_helper_methods_to_main
+ @line_no += 1
+
+ # When users run:
+ # 1. Debugging commands, like `step 2`
+ # 2. Any input that's not irb-command, like `foo = 123`
+ #
+ #
+ # Irb#eval_input will simply return the input, and we need to pass it to the
+ # debugger.
+ input = nil
+ forced_exit = catch(:IRB_EXIT) do
+ if IRB.conf[:SAVE_HISTORY] && context.io.support_history_saving?
+ # Previous IRB session's history has been saved when `Irb#run` is exited We need
+ # to make sure the saved history is not saved again by resetting the counter
+ context.io.reset_history_counter
+
+ begin
+ input = eval_input
+ ensure
+ context.io.save_history
+ end
+ else
+ input = eval_input
+ end
+ false
+ end
+
+ Kernel.exit if forced_exit
+
+ if input&.include?("\n")
+ @line_no += input.count("\n") - 1
+ end
+
+ input
+ end
+
def run(conf = IRB.conf)
+ in_nested_session = !!conf[:MAIN_CONTEXT]
conf[:IRB_RC].call(context) if conf[:IRB_RC]
+ prev_context = conf[:MAIN_CONTEXT]
conf[:MAIN_CONTEXT] = context
+ save_history = !in_nested_session && conf[:SAVE_HISTORY] && context.io.support_history_saving?
+
+ if save_history
+ context.io.load_history
+ end
+
prev_trap = trap("SIGINT") do
signal_handle
end
begin
- catch(:IRB_EXIT) do
+ if defined?(RubyVM.keep_script_lines)
+ keep_script_lines_backup = RubyVM.keep_script_lines
+ RubyVM.keep_script_lines = true
+ end
+
+ forced_exit = catch(:IRB_EXIT) do
eval_input
end
ensure
+ # Do not restore to nil. It will cause IRB crash when used with threads.
+ IRB.conf[:MAIN_CONTEXT] = prev_context if prev_context
+
+ RubyVM.keep_script_lines = keep_script_lines_backup if defined?(RubyVM.keep_script_lines)
trap("SIGINT", prev_trap)
conf[:AT_EXIT].each{|hook| hook.call}
+
+ context.io.save_history if save_history
+ Kernel.exit if forced_exit
end
end
- # Returns the current context of this irb session
- attr_reader :context
- # The lexer used by this irb session
- attr_accessor :scanner
-
# Evaluates input for this session.
def eval_input
- exc = nil
-
- @scanner.set_prompt do
- |ltype, indent, continue, line_no|
- if ltype
- f = @context.prompt_s
- elsif continue
- f = @context.prompt_c
- elsif indent > 0
- f = @context.prompt_n
- else
- f = @context.prompt_i
- end
- f = "" unless f
- if @context.prompting?
- @context.io.prompt = p = prompt(f, ltype, indent, line_no)
- else
- @context.io.prompt = p = ""
- end
- if @context.auto_indent_mode and !@context.io.respond_to?(:auto_indent)
- unless ltype
- prompt_i = @context.prompt_i.nil? ? "" : @context.prompt_i
- ind = prompt(prompt_i, ltype, indent, line_no)[/.*\z/].size +
- indent * 2 - p.size
- ind += 2 if continue
- @context.io.prompt = p + " " * ind if ind > 0
- end
- end
- @context.io.prompt
- end
-
- @scanner.set_input do
- signal_status(:IN_INPUT) do
- if l = @context.io.gets
- print l if @context.verbose?
- else
- if @context.ignore_eof? and @context.io.readable_after_eof?
- l = "\n"
- if @context.verbose?
- printf "Use \"exit\" to leave %s\n", @context.ap_name
- end
- else
- print "\n" if @context.prompting?
- end
- end
- l
- end
- end
-
- @scanner.configure_io(@context.io)
+ configure_io
- @scanner.each_top_level_statement do |line, line_no|
+ each_top_level_statement do |statement, line_no|
signal_status(:IN_EVAL) do
begin
- if IRB.conf[:MEASURE] && IRB.conf[:MEASURE_CALLBACKS].empty?
- IRB.set_measure_callback
- end
- # Assignment expression check should be done before @context.evaluate to handle code like `a /2#/ if false; a = 1`
- is_assignment = assignment_expression?(line)
- if IRB.conf[:MEASURE] && !IRB.conf[:MEASURE_CALLBACKS].empty?
- result = nil
- last_proc = proc{ result = @context.evaluate(line, line_no, exception: exc) }
- IRB.conf[:MEASURE_CALLBACKS].inject(last_proc) { |chain, item|
- _name, callback, arg = item
- proc {
- callback.(@context, line, line_no, arg, exception: exc) do
- chain.call
- end
- }
- }.call
- @context.set_last_value(result)
- else
- @context.evaluate(line, line_no, exception: exc)
+ # If the integration with debugger is activated, we return certain input if it
+ # should be dealt with by debugger
+ if @context.with_debugger && statement.should_be_handled_by_debugger?
+ return statement.code
end
- if @context.echo?
- if is_assignment
+
+ @context.evaluate(statement, line_no)
+
+ if @context.echo? && !statement.suppresses_echo?
+ if statement.is_assignment?
if @context.echo_on_assignment?
output_value(@context.echo_on_assignment? == :truncate)
end
@@ -589,17 +1051,157 @@ module IRB
output_value
end
end
- rescue Interrupt => exc
rescue SystemExit, SignalException
raise
- rescue Exception => exc
+ rescue Interrupt, Exception => exc
+ handle_exception(exc)
+ @context.workspace.local_variable_set(:_, exc)
+ end
+ end
+ end
+ end
+
+ def read_input(prompt)
+ signal_status(:IN_INPUT) do
+ @context.io.prompt = prompt
+ if l = @context.io.gets
+ print l if @context.verbose?
+ else
+ if @context.ignore_eof? and @context.io.readable_after_eof?
+ l = "\n"
+ if @context.verbose?
+ printf "Use \"exit\" to leave %s\n", @context.ap_name
+ end
else
- exc = nil
- next
+ print "\n" if @context.prompting?
+ end
+ end
+ l
+ end
+ end
+
+ def readmultiline
+ prompt = generate_prompt([], false, 0)
+
+ # multiline
+ return read_input(prompt) if @context.io.respond_to?(:check_termination)
+
+ # nomultiline
+ code = +''
+ line_offset = 0
+ loop do
+ line = read_input(prompt)
+ unless line
+ return code.empty? ? nil : code
+ end
+
+ code << line
+ return code if command?(code)
+
+ tokens, opens, terminated = @scanner.check_code_state(code, local_variables: @context.local_variables)
+ return code if terminated
+
+ line_offset += 1
+ continue = @scanner.should_continue?(tokens)
+ prompt = generate_prompt(opens, continue, line_offset)
+ end
+ end
+
+ def each_top_level_statement
+ loop do
+ code = readmultiline
+ break unless code
+ yield build_statement(code), @line_no
+ @line_no += code.count("\n")
+ rescue RubyLex::TerminateLineInput
+ end
+ end
+
+ def build_statement(code)
+ if code.match?(/\A\n*\z/)
+ return Statement::EmptyInput.new
+ end
+
+ code.force_encoding(@context.io.encoding)
+ if (command, arg = parse_command(code))
+ command_class = Command.load_command(command)
+ Statement::Command.new(code, command_class, arg)
+ else
+ is_assignment_expression = @scanner.assignment_expression?(code, local_variables: @context.local_variables)
+ Statement::Expression.new(code, is_assignment_expression)
+ end
+ end
+
+ def parse_command(code)
+ command_name, arg = code.strip.split(/\s+/, 2)
+ return unless code.lines.size == 1 && command_name
+
+ arg ||= ''
+ command = command_name.to_sym
+ # Command aliases are always command. example: $, @
+ if (alias_name = @context.command_aliases[command])
+ return [alias_name, arg]
+ end
+
+ # Check visibility
+ public_method = !!Kernel.instance_method(:public_method).bind_call(@context.main, command) rescue false
+ private_method = !public_method && !!Kernel.instance_method(:method).bind_call(@context.main, command) rescue false
+ if Command.execute_as_command?(command, public_method: public_method, private_method: private_method)
+ [command, arg]
+ end
+ end
+
+ def command?(code)
+ !!parse_command(code)
+ end
+
+ def configure_io
+ if @context.io.respond_to?(:check_termination)
+ @context.io.check_termination do |code|
+ if Reline::IOGate.in_pasting?
+ rest = @scanner.check_termination_in_prev_line(code, local_variables: @context.local_variables)
+ if rest
+ Reline.delete_text
+ rest.bytes.reverse_each do |c|
+ Reline.ungetc(c)
+ end
+ true
+ else
+ false
+ end
+ else
+ next true if command?(code)
+
+ _tokens, _opens, terminated = @scanner.check_code_state(code, local_variables: @context.local_variables)
+ terminated
+ end
+ end
+ end
+ if @context.io.respond_to?(:dynamic_prompt)
+ @context.io.dynamic_prompt do |lines|
+ tokens = RubyLex.ripper_lex_without_warning(lines.map{ |l| l + "\n" }.join, local_variables: @context.local_variables)
+ line_results = IRB::NestingParser.parse_by_line(tokens)
+ tokens_until_line = []
+ line_results.map.with_index do |(line_tokens, _prev_opens, next_opens, _min_depth), line_num_offset|
+ line_tokens.each do |token, _s|
+ # Avoid appending duplicated token. Tokens that include "n" like multiline
+ # tstring_content can exist in multiple lines.
+ tokens_until_line << token if token != tokens_until_line.last
+ end
+ continue = @scanner.should_continue?(tokens_until_line)
+ generate_prompt(next_opens, continue, line_num_offset)
end
- handle_exception(exc)
- @context.workspace.local_variable_set(:_, exc)
- exc = nil
+ end
+ end
+
+ if @context.io.respond_to?(:auto_indent) and @context.auto_indent_mode
+ @context.io.auto_indent do |lines, line_index, byte_pointer, is_newline|
+ next nil if lines == [nil] # Workaround for exit IRB with CTRL+d
+ next nil if !is_newline && lines[line_index]&.byteslice(0, byte_pointer)&.match?(/\A\s*\z/)
+
+ code = lines[0..line_index].map { |l| "#{l}\n" }.join
+ tokens = RubyLex.ripper_lex_without_warning(code, local_variables: @context.local_variables)
+ @scanner.process_indent_level(tokens, lines, line_index, is_newline)
end
end
end
@@ -634,60 +1236,69 @@ module IRB
end
def handle_exception(exc)
- if exc.backtrace && exc.backtrace[0] =~ /\/irb(2)?(\/.*|-.*|\.rb)?:/ && exc.class.to_s !~ /^IRB/ &&
+ if exc.backtrace[0] =~ /\/irb(2)?(\/.*|-.*|\.rb)?:/ && exc.class.to_s !~ /^IRB/ &&
!(SyntaxError === exc) && !(EncodingError === exc)
# The backtrace of invalid encoding hash (ex. {"\xAE": 1}) raises EncodingError without lineno.
irb_bug = true
else
irb_bug = false
+ # This is mostly to make IRB work nicely with Rails console's backtrace filtering, which patches WorkSpace#filter_backtrace
+ # In such use case, we want to filter the exception's backtrace before its displayed through Exception#full_message
+ # And we clone the exception object in order to avoid mutating the original exception
+ # TODO: introduce better API to expose exception backtrace externally
+ backtrace = exc.backtrace.map { |l| @context.workspace.filter_backtrace(l) }.compact
+ exc = exc.clone
+ exc.set_backtrace(backtrace)
end
- if exc.backtrace
- order = nil
- if RUBY_VERSION < '3.0.0'
- if STDOUT.tty?
- message = exc.full_message(order: :bottom)
- order = :bottom
- else
- message = exc.full_message(order: :top)
- order = :top
- end
- else # '3.0.0' <= RUBY_VERSION
+ if RUBY_VERSION < '3.0.0'
+ if STDOUT.tty?
+ message = exc.full_message(order: :bottom)
+ order = :bottom
+ else
message = exc.full_message(order: :top)
order = :top
end
- message = convert_invalid_byte_sequence(message, exc.message.encoding)
- message = encode_with_invalid_byte_sequence(message, IRB.conf[:LC_MESSAGES].encoding) unless message.encoding.to_s.casecmp?(IRB.conf[:LC_MESSAGES].encoding.to_s)
- message = message.gsub(/((?:^\t.+$\n)+)/) { |m|
- case order
- when :top
- lines = m.split("\n")
- when :bottom
- lines = m.split("\n").reverse
- end
- unless irb_bug
- lines = lines.map { |l| @context.workspace.filter_backtrace(l) }.compact
- if lines.size > @context.back_trace_limit
- omit = lines.size - @context.back_trace_limit
- lines = lines[0..(@context.back_trace_limit - 1)]
- lines << "\t... %d levels..." % omit
- end
+ else # '3.0.0' <= RUBY_VERSION
+ message = exc.full_message(order: :top)
+ order = :top
+ end
+ message = convert_invalid_byte_sequence(message, exc.message.encoding)
+ message = encode_with_invalid_byte_sequence(message, IRB.conf[:LC_MESSAGES].encoding) unless message.encoding.to_s.casecmp?(IRB.conf[:LC_MESSAGES].encoding.to_s)
+ message = message.gsub(/((?:^\t.+$\n)+)/) { |m|
+ case order
+ when :top
+ lines = m.split("\n")
+ when :bottom
+ lines = m.split("\n").reverse
+ end
+ unless irb_bug
+ if lines.size > @context.back_trace_limit
+ omit = lines.size - @context.back_trace_limit
+ lines = lines[0..(@context.back_trace_limit - 1)]
+ lines << "\t... %d levels..." % omit
end
- lines = lines.reverse if order == :bottom
- lines.map{ |l| l + "\n" }.join
- }
- # The "<top (required)>" in "(irb)" may be the top level of IRB so imitate the main object.
- message = message.gsub(/\(irb\):(?<num>\d+):in `<(?<frame>top \(required\))>'/) { "(irb):#{$~[:num]}:in `<main>'" }
- puts message
- end
- print "Maybe IRB bug!\n" if irb_bug
+ end
+ lines = lines.reverse if order == :bottom
+ lines.map{ |l| l + "\n" }.join
+ }
+ # The "<top (required)>" in "(irb)" may be the top level of IRB so imitate the main object.
+ message = message.gsub(/\(irb\):(?<num>\d+):in (?<open_quote>[`'])<(?<frame>top \(required\))>'/) { "(irb):#{$~[:num]}:in #{$~[:open_quote]}<main>'" }
+ puts message
+ puts 'Maybe IRB bug!' if irb_bug
+ rescue Exception => handler_exc
+ begin
+ puts exc.inspect
+ puts "backtraces are hidden because #{handler_exc} was raised when processing them"
+ rescue Exception
+ puts 'Uninspectable exception occurred'
+ end
end
- # Evaluates the given block using the given +path+ as the Context#irb_path
- # and +name+ as the Context#irb_name.
+ # Evaluates the given block using the given `path` as the Context#irb_path and
+ # `name` as the Context#irb_name.
#
- # Used by the irb command +source+, see IRB@IRB+Sessions for more
- # information.
+ # Used by the irb command `source`, see IRB@IRB+Sessions for more information.
def suspend_name(path = nil, name = nil)
@context.irb_path, back_path = path, @context.irb_path if path
@context.irb_name, back_name = name, @context.irb_name if name
@@ -699,25 +1310,22 @@ module IRB
end
end
- # Evaluates the given block using the given +workspace+ as the
+ # Evaluates the given block using the given `workspace` as the
# Context#workspace.
#
- # Used by the irb command +irb_load+, see IRB@IRB+Sessions for more
- # information.
+ # Used by the irb command `irb_load`, see IRB@IRB+Sessions for more information.
def suspend_workspace(workspace)
- @context.workspace, back_workspace = workspace, @context.workspace
- begin
- yield back_workspace
- ensure
- @context.workspace = back_workspace
- end
+ current_workspace = @context.workspace
+ @context.replace_workspace(workspace)
+ yield
+ ensure
+ @context.replace_workspace current_workspace
end
- # Evaluates the given block using the given +input_method+ as the
- # Context#io.
+ # Evaluates the given block using the given `input_method` as the Context#io.
#
- # Used by the irb commands +source+ and +irb_load+, see IRB@IRB+Sessions
- # for more information.
+ # Used by the irb commands `source` and `irb_load`, see IRB@IRB+Sessions for
+ # more information.
def suspend_input_method(input_method)
back_io = @context.io
@context.instance_eval{@io = input_method}
@@ -728,16 +1336,6 @@ module IRB
end
end
- # Evaluates the given block using the given +context+ as the Context.
- def suspend_context(context)
- @context, back_context = context, @context
- begin
- yield back_context
- ensure
- @context = back_context
- end
- end
-
# Handler for the signal SIGINT, see Kernel#trap for more information.
def signal_handle
unless @context.ignore_sigint?
@@ -760,7 +1358,7 @@ module IRB
end
end
- # Evaluates the given block using the given +status+.
+ # Evaluates the given block using the given `status`.
def signal_status(status)
return yield if @signal_status == :IN_LOAD
@@ -773,54 +1371,6 @@ module IRB
end
end
- def truncate_prompt_main(str) # :nodoc:
- str = str.tr(CONTROL_CHARACTERS_PATTERN, ' ')
- if str.size <= PROMPT_MAIN_TRUNCATE_LENGTH
- str
- else
- str[0, PROMPT_MAIN_TRUNCATE_LENGTH - PROMPT_MAIN_TRUNCATE_OMISSION.size] + PROMPT_MAIN_TRUNCATE_OMISSION
- end
- end
-
- def prompt(prompt, ltype, indent, line_no) # :nodoc:
- p = prompt.dup
- p.gsub!(/%([0-9]+)?([a-zA-Z])/) do
- case $2
- when "N"
- @context.irb_name
- when "m"
- truncate_prompt_main(@context.main.to_s)
- when "M"
- truncate_prompt_main(@context.main.inspect)
- when "l"
- ltype
- when "i"
- if indent < 0
- if $1
- "-".rjust($1.to_i)
- else
- "-"
- end
- else
- if $1
- format("%" + $1 + "d", indent)
- else
- indent.to_s
- end
- end
- when "n"
- if $1
- format("%" + $1 + "d", line_no)
- else
- line_no.to_s
- end
- when "%"
- "%"
- end
- end
- p
- end
-
def output_value(omit = false) # :nodoc:
str = @context.inspect_last_value
multiline_p = str.include?("\n")
@@ -850,15 +1400,16 @@ module IRB
end
end
end
+
if multiline_p && @context.newline_before_multiline_output?
- printf @context.return_format, "\n#{str}"
- else
- printf @context.return_format, str
+ str = "\n" + str
end
+
+ Pager.page_content(format(@context.return_format, str), retain_content: true)
end
- # Outputs the local variables to this current session, including
- # #signal_status and #context, using IRB::Locale.
+ # Outputs the local variables to this current session, including #signal_status
+ # and #context, using IRB::Locale.
def inspect
ary = []
for iv in instance_variables
@@ -874,55 +1425,94 @@ module IRB
format("#<%s: %s>", self.class, ary.join(", "))
end
- def assignment_expression?(line)
- # Try to parse the line and check if the last of possibly multiple
- # expressions is an assignment type.
-
- # If the expression is invalid, Ripper.sexp should return nil which will
- # result in false being returned. Any valid expression should return an
- # s-expression where the second element of the top level array is an
- # array of parsed expressions. The first element of each expression is the
- # expression's type.
- verbose, $VERBOSE = $VERBOSE, nil
- code = "#{RubyLex.generate_local_variables_assign_code(@context.local_variables) || 'nil;'}\n#{line}"
- # Get the last node_type of the line. drop(1) is to ignore the local_variables_assign_code part.
- node_type = Ripper.sexp(code)&.dig(1)&.drop(1)&.dig(-1, 0)
- ASSIGNMENT_NODE_TYPES.include?(node_type)
- ensure
- $VERBOSE = verbose
+ private
+
+ def generate_prompt(opens, continue, line_offset)
+ ltype = @scanner.ltype_from_open_tokens(opens)
+ indent = @scanner.calc_indent_level(opens)
+ continue = opens.any? || continue
+ line_no = @line_no + line_offset
+
+ if ltype
+ f = @context.prompt_s
+ elsif continue
+ f = @context.prompt_c
+ else
+ f = @context.prompt_i
+ end
+ f = "" unless f
+ if @context.prompting?
+ p = format_prompt(f, ltype, indent, line_no)
+ else
+ p = ""
+ end
+ if @context.auto_indent_mode and !@context.io.respond_to?(:auto_indent)
+ unless ltype
+ prompt_i = @context.prompt_i.nil? ? "" : @context.prompt_i
+ ind = format_prompt(prompt_i, ltype, indent, line_no)[/.*\z/].size +
+ indent * 2 - p.size
+ p += " " * ind if ind > 0
+ end
+ end
+ p
end
- end
- def @CONF.inspect
- IRB.version unless self[:VERSION]
-
- array = []
- for k, v in sort{|a1, a2| a1[0].id2name <=> a2[0].id2name}
- case k
- when :MAIN_CONTEXT, :__TMP__EHV__
- array.push format("CONF[:%s]=...myself...", k.id2name)
- when :PROMPT
- s = v.collect{
- |kk, vv|
- ss = vv.collect{|kkk, vvv| ":#{kkk.id2name}=>#{vvv.inspect}"}
- format(":%s=>{%s}", kk.id2name, ss.join(", "))
- }
- array.push format("CONF[:%s]={%s}", k.id2name, s.join(", "))
+ def truncate_prompt_main(str) # :nodoc:
+ str = str.tr(CONTROL_CHARACTERS_PATTERN, ' ')
+ if str.size <= PROMPT_MAIN_TRUNCATE_LENGTH
+ str
else
- array.push format("CONF[:%s]=%s", k.id2name, v.inspect)
+ str[0, PROMPT_MAIN_TRUNCATE_LENGTH - PROMPT_MAIN_TRUNCATE_OMISSION.size] + PROMPT_MAIN_TRUNCATE_OMISSION
+ end
+ end
+
+ def format_prompt(format, ltype, indent, line_no) # :nodoc:
+ format.gsub(/%([0-9]+)?([a-zA-Z%])/) do
+ case $2
+ when "N"
+ @context.irb_name
+ when "m"
+ main_str = @context.main.to_s rescue "!#{$!.class}"
+ truncate_prompt_main(main_str)
+ when "M"
+ main_str = @context.main.inspect rescue "!#{$!.class}"
+ truncate_prompt_main(main_str)
+ when "l"
+ ltype
+ when "i"
+ if indent < 0
+ if $1
+ "-".rjust($1.to_i)
+ else
+ "-"
+ end
+ else
+ if $1
+ format("%" + $1 + "d", indent)
+ else
+ indent.to_s
+ end
+ end
+ when "n"
+ if $1
+ format("%" + $1 + "d", line_no)
+ else
+ line_no.to_s
+ end
+ when "%"
+ "%" unless $1
+ end
end
end
- array.join("\n")
end
end
class Binding
- # Opens an IRB session where +binding.irb+ is called which allows for
- # interactive debugging. You can call any methods or variables available in
- # the current scope, and mutate state if you need to.
- #
+ # Opens an IRB session where `binding.irb` is called which allows for
+ # interactive debugging. You can call any methods or variables available in the
+ # current scope, and mutate state if you need to.
#
- # Given a Ruby file called +potato.rb+ containing the following code:
+ # Given a Ruby file called `potato.rb` containing the following code:
#
# class Potato
# def initialize
@@ -934,8 +1524,8 @@ class Binding
#
# Potato.new
#
- # Running <code>ruby potato.rb</code> will open an IRB session where
- # +binding.irb+ is called, and you will see the following:
+ # Running `ruby potato.rb` will open an IRB session where `binding.irb` is
+ # called, and you will see the following:
#
# $ ruby potato.rb
#
@@ -965,22 +1555,42 @@ class Binding
# irb(#<Potato:0x00007feea1916670>):004:0> @cooked = true
# => true
#
- # You can exit the IRB session with the +exit+ command. Note that exiting will
- # resume execution where +binding.irb+ had paused it, as you can see from the
+ # You can exit the IRB session with the `exit` command. Note that exiting will
+ # resume execution where `binding.irb` had paused it, as you can see from the
# output printed to standard output in this example:
#
# irb(#<Potato:0x00007feea1916670>):005:0> exit
# Cooked potato: true
#
- #
- # See IRB@IRB+Usage for more information.
+ # See IRB for more information.
def irb(show_code: true)
- IRB.setup(source_location[0], argv: [])
+ # Setup IRB with the current file's path and no command line arguments
+ IRB.setup(source_location[0], argv: []) unless IRB.initialized?
+ # Create a new workspace using the current binding
workspace = IRB::WorkSpace.new(self)
+ # Print the code around the binding if show_code is true
STDOUT.print(workspace.code_around_binding) if show_code
- binding_irb = IRB::Irb.new(workspace)
- binding_irb.context.irb_path = File.expand_path(source_location[0])
- binding_irb.run(IRB.conf)
- binding_irb.debug_break
+ # Get the original IRB instance
+ debugger_irb = IRB.instance_variable_get(:@debugger_irb)
+
+ irb_path = File.expand_path(source_location[0])
+
+ if debugger_irb
+ # If we're already in a debugger session, set the workspace and irb_path for the original IRB instance
+ debugger_irb.context.replace_workspace(workspace)
+ debugger_irb.context.irb_path = irb_path
+ # If we've started a debugger session and hit another binding.irb, we don't want
+ # to start an IRB session instead, we want to resume the irb:rdbg session.
+ IRB::Debug.setup(debugger_irb)
+ IRB::Debug.insert_debug_break
+ debugger_irb.debug_break
+ else
+ # If we're not in a debugger session, create a new IRB instance with the current
+ # workspace
+ binding_irb = IRB::Irb.new(workspace)
+ binding_irb.context.irb_path = irb_path
+ binding_irb.run(IRB.conf)
+ binding_irb.debug_break
+ end
end
end
diff --git a/lib/irb/cmd/backtrace.rb b/lib/irb/cmd/backtrace.rb
deleted file mode 100644
index f632894618..0000000000
--- a/lib/irb/cmd/backtrace.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "debug"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class Backtrace < DebugCommand
- def self.transform_args(args)
- args&.dump
- end
-
- def execute(*args)
- super(pre_cmds: ["backtrace", *args].join(" "))
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/break.rb b/lib/irb/cmd/break.rb
deleted file mode 100644
index df259a90ca..0000000000
--- a/lib/irb/cmd/break.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "debug"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class Break < DebugCommand
- def self.transform_args(args)
- args&.dump
- end
-
- def execute(args = nil)
- super(pre_cmds: "break #{args}")
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/catch.rb b/lib/irb/cmd/catch.rb
deleted file mode 100644
index 40b62c7533..0000000000
--- a/lib/irb/cmd/catch.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "debug"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class Catch < DebugCommand
- def self.transform_args(args)
- args&.dump
- end
-
- def execute(*args)
- super(pre_cmds: ["catch", *args].join(" "))
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/chws.rb b/lib/irb/cmd/chws.rb
deleted file mode 100644
index 44712fa445..0000000000
--- a/lib/irb/cmd/chws.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: false
-#
-# change-ws.rb -
-# by Keiju ISHITSUKA(keiju@ruby-lang.org)
-#
-
-require_relative "nop"
-require_relative "../ext/change-ws"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
-
- class CurrentWorkingWorkspace < Nop
- category "IRB"
- description "Show the current workspace."
-
- def execute(*obj)
- irb_context.main
- end
- end
-
- class ChangeWorkspace < Nop
- category "IRB"
- description "Change the current workspace to an object."
-
- def execute(*obj)
- irb_context.change_workspace(*obj)
- irb_context.main
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/continue.rb b/lib/irb/cmd/continue.rb
deleted file mode 100644
index 9136177eef..0000000000
--- a/lib/irb/cmd/continue.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "debug"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class Continue < DebugCommand
- def execute(*args)
- super(do_cmds: ["continue", *args].join(" "))
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/debug.rb b/lib/irb/cmd/debug.rb
deleted file mode 100644
index 7d39b9fa27..0000000000
--- a/lib/irb/cmd/debug.rb
+++ /dev/null
@@ -1,136 +0,0 @@
-require_relative "nop"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class Debug < Nop
- category "Debugging"
- description "Start the debugger of debug.gem."
-
- BINDING_IRB_FRAME_REGEXPS = [
- '<internal:prelude>',
- binding.method(:irb).source_location.first,
- ].map { |file| /\A#{Regexp.escape(file)}:\d+:in `irb'\z/ }
- IRB_DIR = File.expand_path('..', __dir__)
-
- def execute(pre_cmds: nil, do_cmds: nil)
- unless binding_irb?
- puts "`debug` command is only available when IRB is started with binding.irb"
- return
- end
-
- unless setup_debugger
- puts <<~MSG
- You need to install the debug gem before using this command.
- If you use `bundle exec`, please add `gem "debug"` into your Gemfile.
- MSG
- return
- end
-
- options = { oneshot: true, hook_call: false }
- if pre_cmds || do_cmds
- options[:command] = ['irb', pre_cmds, do_cmds]
- end
- if DEBUGGER__::LineBreakpoint.instance_method(:initialize).parameters.include?([:key, :skip_src])
- options[:skip_src] = true
- end
-
- # To make debugger commands like `next` or `continue` work without asking
- # the user to quit IRB after that, we need to exit IRB first and then hit
- # a TracePoint on #debug_break.
- file, lineno = IRB::Irb.instance_method(:debug_break).source_location
- DEBUGGER__::SESSION.add_line_breakpoint(file, lineno + 1, **options)
- # exit current Irb#run call
- throw :IRB_EXIT
- end
-
- private
-
- def binding_irb?
- caller.any? do |frame|
- BINDING_IRB_FRAME_REGEXPS.any? do |regexp|
- frame.match?(regexp)
- end
- end
- end
-
- module SkipPathHelperForIRB
- def skip_internal_path?(path)
- # The latter can be removed once https://github.com/ruby/debug/issues/866 is resolved
- super || path.match?(IRB_DIR) || path.match?('<internal:prelude>')
- end
- end
-
- def setup_debugger
- unless defined?(DEBUGGER__::SESSION)
- begin
- require "debug/session"
- rescue LoadError # debug.gem is not written in Gemfile
- return false unless load_bundled_debug_gem
- end
- DEBUGGER__.start(nonstop: true)
- end
-
- unless DEBUGGER__.respond_to?(:capture_frames_without_irb)
- DEBUGGER__.singleton_class.send(:alias_method, :capture_frames_without_irb, :capture_frames)
-
- def DEBUGGER__.capture_frames(*args)
- frames = capture_frames_without_irb(*args)
- frames.reject! do |frame|
- frame.realpath&.start_with?(IRB_DIR) || frame.path == "<internal:prelude>"
- end
- frames
- end
-
- DEBUGGER__::ThreadClient.prepend(SkipPathHelperForIRB)
- end
-
- true
- end
-
- # This is used when debug.gem is not written in Gemfile. Even if it's not
- # installed by `bundle install`, debug.gem is installed by default because
- # it's a bundled gem. This method tries to activate and load that.
- def load_bundled_debug_gem
- # Discover latest debug.gem under GEM_PATH
- debug_gem = Gem.paths.path.flat_map { |path| Dir.glob("#{path}/gems/debug-*") }.select do |path|
- File.basename(path).match?(/\Adebug-\d+\.\d+\.\d+(\w+)?\z/)
- end.sort_by do |path|
- Gem::Version.new(File.basename(path).delete_prefix('debug-'))
- end.last
- return false unless debug_gem
-
- # Discover debug/debug.so under extensions for Ruby 3.2+
- ext_name = "/debug/debug.#{RbConfig::CONFIG['DLEXT']}"
- ext_path = Gem.paths.path.flat_map do |path|
- Dir.glob("#{path}/extensions/**/#{File.basename(debug_gem)}#{ext_name}")
- end.first
-
- # Attempt to forcibly load the bundled gem
- if ext_path
- $LOAD_PATH << ext_path.delete_suffix(ext_name)
- end
- $LOAD_PATH << "#{debug_gem}/lib"
- begin
- require "debug/session"
- puts "Loaded #{File.basename(debug_gem)}"
- true
- rescue LoadError
- false
- end
- end
- end
-
- class DebugCommand < Debug
- def self.category
- "Debugging"
- end
-
- def self.description
- command_name = self.name.split("::").last.downcase
- "Start the debugger of debug.gem and run its `#{command_name}` command."
- end
- end
- end
-end
diff --git a/lib/irb/cmd/delete.rb b/lib/irb/cmd/delete.rb
deleted file mode 100644
index aeb26d2572..0000000000
--- a/lib/irb/cmd/delete.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "debug"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class Delete < DebugCommand
- def execute(*args)
- super(pre_cmds: ["delete", *args].join(" "))
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/edit.rb b/lib/irb/cmd/edit.rb
deleted file mode 100644
index 0103891cf4..0000000000
--- a/lib/irb/cmd/edit.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-require 'shellwords'
-require_relative "nop"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class Edit < Nop
- category "Misc"
- description 'Open a file with the editor command defined with `ENV["EDITOR"]`.'
-
- class << self
- def transform_args(args)
- # Return a string literal as is for backward compatibility
- if args.nil? || args.empty? || string_literal?(args)
- args
- else # Otherwise, consider the input as a String for convenience
- args.strip.dump
- end
- end
- end
-
- def execute(*args)
- path = args.first
-
- if path.nil? && (irb_path = @irb_context.irb_path)
- path = irb_path
- end
-
- if !File.exist?(path)
- require_relative "show_source"
-
- source =
- begin
- ShowSource.find_source(path, @irb_context)
- rescue NameError
- # if user enters a path that doesn't exist, it'll cause NameError when passed here because find_source would try to evaluate it as well
- # in this case, we should just ignore the error
- end
-
- if source && File.exist?(source.file)
- path = source.file
- else
- puts "Can not find file: #{path}"
- return
- end
- end
-
- if editor = ENV['EDITOR']
- puts "command: '#{editor}'"
- puts " path: #{path}"
- system(*Shellwords.split(editor), path)
- else
- puts "Can not find editor setting: ENV['EDITOR']"
- end
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/finish.rb b/lib/irb/cmd/finish.rb
deleted file mode 100644
index 29f100feb5..0000000000
--- a/lib/irb/cmd/finish.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "debug"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class Finish < DebugCommand
- def execute(*args)
- super(do_cmds: ["finish", *args].join(" "))
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/fork.rb b/lib/irb/cmd/fork.rb
deleted file mode 100644
index 1cd235997f..0000000000
--- a/lib/irb/cmd/fork.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# frozen_string_literal: false
-#
-# fork.rb -
-# by Keiju ISHITSUKA(keiju@ruby-lang.org)
-#
-
-require_relative "nop"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class Fork < Nop
- def execute
- pid = __send__ ExtendCommand.irb_original_method_name("fork")
- unless pid
- class << self
- alias_method :exit, ExtendCommand.irb_original_method_name('exit')
- end
- if block_given?
- begin
- yield
- ensure
- exit
- end
- end
- end
- pid
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/help.rb b/lib/irb/cmd/help.rb
deleted file mode 100644
index 64b885c383..0000000000
--- a/lib/irb/cmd/help.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "show_doc"
-
-module IRB
- module ExtendCommand
- class Help < ShowDoc
- category "Context"
- description "[DEPRECATED] Enter the mode to look up RI documents."
-
- DEPRECATION_MESSAGE = <<~MSG
- [Deprecation] The `help` command will be repurposed to display command help in the future.
- For RI document lookup, please use the `show_doc` command instead.
- For command help, please use `show_cmds` for now.
- MSG
-
- def execute(*names)
- warn DEPRECATION_MESSAGE
- super
- end
- end
- end
-end
diff --git a/lib/irb/cmd/info.rb b/lib/irb/cmd/info.rb
deleted file mode 100644
index 2c0a32b34f..0000000000
--- a/lib/irb/cmd/info.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "debug"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class Info < DebugCommand
- def self.transform_args(args)
- args&.dump
- end
-
- def execute(*args)
- super(pre_cmds: ["info", *args].join(" "))
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/irb_info.rb b/lib/irb/cmd/irb_info.rb
deleted file mode 100644
index 75fdc38676..0000000000
--- a/lib/irb/cmd/irb_info.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-# frozen_string_literal: false
-
-require_relative "nop"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class IrbInfo < Nop
- category "IRB"
- description "Show information about IRB."
-
- def execute
- str = "Ruby version: #{RUBY_VERSION}\n"
- str += "IRB version: #{IRB.version}\n"
- str += "InputMethod: #{IRB.CurrentContext.io.inspect}\n"
- str += ".irbrc path: #{IRB.rc_file}\n" if File.exist?(IRB.rc_file)
- str += "RUBY_PLATFORM: #{RUBY_PLATFORM}\n"
- str += "LANG env: #{ENV["LANG"]}\n" if ENV["LANG"] && !ENV["LANG"].empty?
- str += "LC_ALL env: #{ENV["LC_ALL"]}\n" if ENV["LC_ALL"] && !ENV["LC_ALL"].empty?
- str += "East Asian Ambiguous Width: #{Reline.ambiguous_width.inspect}\n"
- if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
- codepage = `chcp`.b.sub(/.*: (\d+)\n/, '\1')
- str += "Code page: #{codepage}\n"
- end
- puts str
- nil
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/load.rb b/lib/irb/cmd/load.rb
deleted file mode 100644
index a3e797a7e0..0000000000
--- a/lib/irb/cmd/load.rb
+++ /dev/null
@@ -1,76 +0,0 @@
-# frozen_string_literal: false
-#
-# load.rb -
-# by Keiju ISHITSUKA(keiju@ruby-lang.org)
-#
-
-require_relative "nop"
-require_relative "../ext/loader"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class LoaderCommand < Nop
- include IrbLoader
-
- def raise_cmd_argument_error
- raise CommandArgumentError.new("Please specify the file name.")
- end
- end
-
- class Load < LoaderCommand
- category "IRB"
- description "Load a Ruby file."
-
- def execute(file_name = nil, priv = nil)
- raise_cmd_argument_error unless file_name
- irb_load(file_name, priv)
- end
- end
-
- class Require < LoaderCommand
- category "IRB"
- description "Require a Ruby file."
- def execute(file_name = nil)
- raise_cmd_argument_error unless file_name
-
- rex = Regexp.new("#{Regexp.quote(file_name)}(\.o|\.rb)?")
- return false if $".find{|f| f =~ rex}
-
- case file_name
- when /\.rb$/
- begin
- if irb_load(file_name)
- $".push file_name
- return true
- end
- rescue LoadError
- end
- when /\.(so|o|sl)$/
- return ruby_require(file_name)
- end
-
- begin
- irb_load(f = file_name + ".rb")
- $".push f
- return true
- rescue LoadError
- return ruby_require(file_name)
- end
- end
- end
-
- class Source < LoaderCommand
- category "IRB"
- description "Loads a given file in the current session."
-
- def execute(file_name = nil)
- raise_cmd_argument_error unless file_name
-
- source_file(file_name)
- end
- end
- end
- # :startdoc:
-end
diff --git a/lib/irb/cmd/ls.rb b/lib/irb/cmd/ls.rb
deleted file mode 100644
index 063a88d33f..0000000000
--- a/lib/irb/cmd/ls.rb
+++ /dev/null
@@ -1,132 +0,0 @@
-# frozen_string_literal: true
-
-require "reline"
-require_relative "nop"
-require_relative "../color"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class Ls < Nop
- category "Context"
- description "Show methods, constants, and variables. `-g [query]` or `-G [query]` allows you to filter out the output."
-
- def self.transform_args(args)
- if match = args&.match(/\A(?<args>.+\s|)(-g|-G)\s+(?<grep>[^\s]+)\s*\n\z/)
- args = match[:args]
- "#{args}#{',' unless args.chomp.empty?} grep: /#{match[:grep]}/"
- else
- args
- end
- end
-
- def execute(*arg, grep: nil)
- o = Output.new(grep: grep)
-
- obj = arg.empty? ? irb_context.workspace.main : arg.first
- locals = arg.empty? ? irb_context.workspace.binding.local_variables : []
- klass = (obj.class == Class || obj.class == Module ? obj : obj.class)
-
- o.dump("constants", obj.constants) if obj.respond_to?(:constants)
- dump_methods(o, klass, obj)
- o.dump("instance variables", obj.instance_variables)
- o.dump("class variables", klass.class_variables)
- o.dump("locals", locals)
- nil
- end
-
- def dump_methods(o, klass, obj)
- singleton_class = begin obj.singleton_class; rescue TypeError; nil end
- dumped_mods = Array.new
- ancestors = klass.ancestors
- ancestors = ancestors.reject { |c| c >= Object } if klass < Object
- singleton_ancestors = (singleton_class&.ancestors || []).reject { |c| c >= Class }
-
- # singleton_class' ancestors should be at the front
- maps = class_method_map(singleton_ancestors, dumped_mods) + class_method_map(ancestors, dumped_mods)
- maps.each do |mod, methods|
- name = mod == singleton_class ? "#{klass}.methods" : "#{mod}#methods"
- o.dump(name, methods)
- end
- end
-
- def class_method_map(classes, dumped_mods)
- dumped_methods = Array.new
- classes.map do |mod|
- next if dumped_mods.include? mod
-
- dumped_mods << mod
-
- methods = mod.public_instance_methods(false).select do |method|
- if dumped_methods.include? method
- false
- else
- dumped_methods << method
- true
- end
- end
-
- [mod, methods]
- end.compact
- end
-
- class Output
- MARGIN = " "
-
- def initialize(grep: nil)
- @grep = grep
- @line_width = screen_width - MARGIN.length # right padding
- end
-
- def dump(name, strs)
- strs = strs.grep(@grep) if @grep
- strs = strs.sort
- return if strs.empty?
-
- # Attempt a single line
- print "#{Color.colorize(name, [:BOLD, :BLUE])}: "
- if fits_on_line?(strs, cols: strs.size, offset: "#{name}: ".length)
- puts strs.join(MARGIN)
- return
- end
- puts
-
- # Dump with the largest # of columns that fits on a line
- cols = strs.size
- until fits_on_line?(strs, cols: cols, offset: MARGIN.length) || cols == 1
- cols -= 1
- end
- widths = col_widths(strs, cols: cols)
- strs.each_slice(cols) do |ss|
- puts ss.map.with_index { |s, i| "#{MARGIN}%-#{widths[i]}s" % s }.join
- end
- end
-
- private
-
- def fits_on_line?(strs, cols:, offset: 0)
- width = col_widths(strs, cols: cols).sum + MARGIN.length * (cols - 1)
- width <= @line_width - offset
- end
-
- def col_widths(strs, cols:)
- cols.times.map do |col|
- (col...strs.size).step(cols).map do |i|
- strs[i].length
- end.max
- end
- end
-
- def screen_width
- Reline.get_screen_size.last
- rescue Errno::EINVAL # in `winsize': Invalid argument - <STDIN>
- 80
- end
- end
- private_constant :Output
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/measure.rb b/lib/irb/cmd/measure.rb
deleted file mode 100644
index 9122e2dac9..0000000000
--- a/lib/irb/cmd/measure.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-require_relative "nop"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class Measure < Nop
- category "Misc"
- description "`measure` enables the mode to measure processing time. `measure :off` disables it."
-
- def initialize(*args)
- super(*args)
- end
-
- def execute(type = nil, arg = nil, &block)
- # Please check IRB.init_config in lib/irb/init.rb that sets
- # IRB.conf[:MEASURE_PROC] to register default "measure" methods,
- # "measure :time" (abbreviated as "measure") and "measure :stackprof".
- case type
- when :off
- IRB.conf[:MEASURE] = nil
- IRB.unset_measure_callback(arg)
- when :list
- IRB.conf[:MEASURE_CALLBACKS].each do |type_name, _, arg_val|
- puts "- #{type_name}" + (arg_val ? "(#{arg_val.inspect})" : '')
- end
- when :on
- IRB.conf[:MEASURE] = true
- added = IRB.set_measure_callback(type, arg)
- puts "#{added[0]} is added." if added
- else
- if block_given?
- IRB.conf[:MEASURE] = true
- added = IRB.set_measure_callback(&block)
- puts "#{added[0]} is added." if added
- else
- IRB.conf[:MEASURE] = true
- added = IRB.set_measure_callback(type, arg)
- puts "#{added[0]} is added." if added
- end
- end
- nil
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/next.rb b/lib/irb/cmd/next.rb
deleted file mode 100644
index d29c82e7fc..0000000000
--- a/lib/irb/cmd/next.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "debug"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class Next < DebugCommand
- def execute(*args)
- super(do_cmds: ["next", *args].join(" "))
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/nop.rb b/lib/irb/cmd/nop.rb
index 7fb197c51f..9d2e3c4d47 100644
--- a/lib/irb/cmd/nop.rb
+++ b/lib/irb/cmd/nop.rb
@@ -1,53 +1,4 @@
-# frozen_string_literal: false
-#
-# nop.rb -
-# by Keiju ISHITSUKA(keiju@ruby-lang.org)
-#
+# frozen_string_literal: true
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class CommandArgumentError < StandardError; end
-
- class Nop
- class << self
- def category(category = nil)
- @category = category if category
- @category
- end
-
- def description(description = nil)
- @description = description if description
- @description
- end
-
- private
-
- def string_literal?(args)
- sexp = Ripper.sexp(args)
- sexp && sexp.size == 2 && sexp.last&.first&.first == :string_literal
- end
- end
-
- def self.execute(irb_context, *opts, **kwargs, &block)
- command = new(irb_context)
- command.execute(*opts, **kwargs, &block)
- rescue CommandArgumentError => e
- puts e.message
- end
-
- def initialize(irb_context)
- @irb_context = irb_context
- end
-
- attr_reader :irb_context
-
- def execute(*opts)
- #nop
- end
- end
- end
-
- # :startdoc:
-end
+# This file is just a placeholder for backward-compatibility.
+# Please require 'irb' and inherit your command from `IRB::Command::Base` instead.
diff --git a/lib/irb/cmd/pushws.rb b/lib/irb/cmd/pushws.rb
deleted file mode 100644
index d64d822182..0000000000
--- a/lib/irb/cmd/pushws.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-# frozen_string_literal: false
-#
-# change-ws.rb -
-# by Keiju ISHITSUKA(keiju@ruby-lang.org)
-#
-
-require_relative "nop"
-require_relative "../ext/workspaces"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class Workspaces < Nop
- category "IRB"
- description "Show workspaces."
-
- def execute(*obj)
- irb_context.workspaces.collect{|ws| ws.main}
- end
- end
-
- class PushWorkspace < Workspaces
- category "IRB"
- description "Push an object to the workspace stack."
-
- def execute(*obj)
- irb_context.push_workspace(*obj)
- super
- end
- end
-
- class PopWorkspace < Workspaces
- category "IRB"
- description "Pop a workspace from the workspace stack."
-
- def execute(*obj)
- irb_context.pop_workspace(*obj)
- super
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/show_cmds.rb b/lib/irb/cmd/show_cmds.rb
deleted file mode 100644
index acced27d48..0000000000
--- a/lib/irb/cmd/show_cmds.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-# frozen_string_literal: true
-
-require "stringio"
-require_relative "nop"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class ShowCmds < Nop
- category "IRB"
- description "List all available commands and their description."
-
- def execute(*args)
- commands_info = IRB::ExtendCommandBundle.all_commands_info
- commands_grouped_by_categories = commands_info.group_by { |cmd| cmd[:category] }
- longest_cmd_name_length = commands_info.map { |c| c[:display_name] }.max { |a, b| a.length <=> b.length }.length
-
- output = StringIO.new
-
- commands_grouped_by_categories.each do |category, cmds|
- output.puts Color.colorize(category, [:BOLD])
-
- cmds.each do |cmd|
- output.puts " #{cmd[:display_name].to_s.ljust(longest_cmd_name_length)} #{cmd[:description]}"
- end
-
- output.puts
- end
-
- puts output.string
-
- nil
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/show_doc.rb b/lib/irb/cmd/show_doc.rb
deleted file mode 100644
index 99dd9ab95a..0000000000
--- a/lib/irb/cmd/show_doc.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "nop"
-
-module IRB
- module ExtendCommand
- class ShowDoc < Nop
- class << self
- def transform_args(args)
- # Return a string literal as is for backward compatibility
- if args.empty? || string_literal?(args)
- args
- else # Otherwise, consider the input as a String for convenience
- args.strip.dump
- end
- end
- end
-
- category "Context"
- description "Enter the mode to look up RI documents."
-
- def execute(*names)
- require 'rdoc/ri/driver'
-
- unless ShowDoc.const_defined?(:Ri)
- opts = RDoc::RI::Driver.process_args([])
- ShowDoc.const_set(:Ri, RDoc::RI::Driver.new(opts))
- end
-
- if names.empty?
- Ri.interactive
- else
- names.each do |name|
- begin
- Ri.display_name(name.to_s)
- rescue RDoc::RI::Error
- puts $!.message
- end
- end
- end
-
- nil
- rescue LoadError, SystemExit
- warn "Can't display document because `rdoc` is not installed."
- end
- end
- end
-end
diff --git a/lib/irb/cmd/show_source.rb b/lib/irb/cmd/show_source.rb
deleted file mode 100644
index a74895b2dc..0000000000
--- a/lib/irb/cmd/show_source.rb
+++ /dev/null
@@ -1,113 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "nop"
-require_relative "../color"
-require_relative "../ruby-lex"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class ShowSource < Nop
- category "Context"
- description "Show the source code of a given method or constant."
-
- class << self
- def transform_args(args)
- # Return a string literal as is for backward compatibility
- if args.empty? || string_literal?(args)
- args
- else # Otherwise, consider the input as a String for convenience
- args.strip.dump
- end
- end
-
- def find_source(str, irb_context)
- case str
- when /\A[A-Z]\w*(::[A-Z]\w*)*\z/ # Const::Name
- eval(str, irb_context.workspace.binding) # trigger autoload
- base = irb_context.workspace.binding.receiver.yield_self { |r| r.is_a?(Module) ? r : Object }
- file, line = base.const_source_location(str)
- when /\A(?<owner>[A-Z]\w*(::[A-Z]\w*)*)#(?<method>[^ :.]+)\z/ # Class#method
- owner = eval(Regexp.last_match[:owner], irb_context.workspace.binding)
- method = Regexp.last_match[:method]
- if owner.respond_to?(:instance_method)
- methods = owner.instance_methods + owner.private_instance_methods
- file, line = owner.instance_method(method).source_location if methods.include?(method.to_sym)
- end
- when /\A((?<receiver>.+)(\.|::))?(?<method>[^ :.]+)\z/ # method, receiver.method, receiver::method
- receiver = eval(Regexp.last_match[:receiver] || 'self', irb_context.workspace.binding)
- method = Regexp.last_match[:method]
- file, line = receiver.method(method).source_location if receiver.respond_to?(method, true)
- end
- if file && line
- Source.new(file: file, first_line: line, last_line: find_end(file, line, irb_context))
- end
- end
-
- private
-
- def find_end(file, first_line, irb_context)
- return first_line unless File.exist?(file)
- lex = RubyLex.new(irb_context)
- lines = File.read(file).lines[(first_line - 1)..-1]
- tokens = RubyLex.ripper_lex_without_warning(lines.join)
- prev_tokens = []
-
- # chunk with line number
- tokens.chunk { |tok| tok.pos[0] }.each do |lnum, chunk|
- code = lines[0..lnum].join
- prev_tokens.concat chunk
- continue = lex.process_continue(prev_tokens)
- code_block_open = lex.check_code_block(code, prev_tokens)
- if !continue && !code_block_open
- return first_line + lnum
- end
- end
- first_line
- end
- end
-
- def execute(str = nil)
- unless str.is_a?(String)
- puts "Error: Expected a string but got #{str.inspect}"
- return
- end
-
- source = self.class.find_source(str, @irb_context)
- if source && File.exist?(source.file)
- show_source(source)
- else
- puts "Error: Couldn't locate a definition for #{str}"
- end
- nil
- end
-
- private
-
- # @param [IRB::ExtendCommand::ShowSource::Source] source
- def show_source(source)
- puts
- puts "#{bold("From")}: #{source.file}:#{source.first_line}"
- puts
- code = IRB::Color.colorize_code(File.read(source.file))
- puts code.lines[(source.first_line - 1)...source.last_line].join
- puts
- end
-
- def bold(str)
- Color.colorize(str, [:BOLD])
- end
-
- Source = Struct.new(
- :file, # @param [String] - file name
- :first_line, # @param [String] - first line
- :last_line, # @param [String] - last line
- keyword_init: true,
- )
- private_constant :Source
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/step.rb b/lib/irb/cmd/step.rb
deleted file mode 100644
index 2bc74a9d79..0000000000
--- a/lib/irb/cmd/step.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "debug"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class Step < DebugCommand
- def execute(*args)
- super(do_cmds: ["step", *args].join(" "))
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/subirb.rb b/lib/irb/cmd/subirb.rb
deleted file mode 100644
index 5f3e02c988..0000000000
--- a/lib/irb/cmd/subirb.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-# frozen_string_literal: false
-#
-# multi.rb -
-# by Keiju ISHITSUKA(keiju@ruby-lang.org)
-#
-
-require_relative "nop"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class MultiIRBCommand < Nop
- def initialize(conf)
- super
- extend_irb_context
- end
-
- private
-
- def extend_irb_context
- # this extension patches IRB context like IRB.CurrentContext
- require_relative "../ext/multi-irb"
- end
- end
-
- class IrbCommand < MultiIRBCommand
- category "IRB"
- description "Start a child IRB."
-
- def execute(*obj)
- IRB.irb(nil, *obj)
- end
- end
-
- class Jobs < MultiIRBCommand
- category "IRB"
- description "List of current sessions."
-
- def execute
- IRB.JobManager
- end
- end
-
- class Foreground < MultiIRBCommand
- category "IRB"
- description "Switches to the session of the given number."
-
- def execute(key = nil)
- raise CommandArgumentError.new("Please specify the id of target IRB job (listed in the `jobs` command).") unless key
- IRB.JobManager.switch(key)
- end
- end
-
- class Kill < MultiIRBCommand
- category "IRB"
- description "Kills the session with the given number."
-
- def execute(*keys)
- IRB.JobManager.kill(*keys)
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/cmd/whereami.rb b/lib/irb/cmd/whereami.rb
deleted file mode 100644
index 8f56ba073d..0000000000
--- a/lib/irb/cmd/whereami.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "nop"
-
-module IRB
- # :stopdoc:
-
- module ExtendCommand
- class Whereami < Nop
- category "Context"
- description "Show the source code around binding.irb again."
-
- def execute(*)
- code = irb_context.workspace.code_around_binding
- if code
- puts code
- else
- puts "The current context doesn't have code."
- end
- end
- end
- end
-
- # :startdoc:
-end
diff --git a/lib/irb/color.rb b/lib/irb/color.rb
index fa99ed6fd6..ad8670160c 100644
--- a/lib/irb/color.rb
+++ b/lib/irb/color.rb
@@ -9,12 +9,14 @@ module IRB # :nodoc:
BOLD = 1
UNDERLINE = 4
REVERSE = 7
+ BLACK = 30
RED = 31
GREEN = 32
YELLOW = 33
BLUE = 34
MAGENTA = 35
CYAN = 36
+ WHITE = 37
TOKEN_KEYWORDS = {
on_kw: ['nil', 'self', 'true', 'false', '__FILE__', '__LINE__', '__ENCODING__'],
diff --git a/lib/irb/command.rb b/lib/irb/command.rb
new file mode 100644
index 0000000000..68a4b52727
--- /dev/null
+++ b/lib/irb/command.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+#
+# irb/command.rb - irb command
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+
+require_relative "command/base"
+
+module IRB # :nodoc:
+ module Command
+ @commands = {}
+
+ class << self
+ attr_reader :commands
+
+ # Registers a command with the given name.
+ # Aliasing is intentionally not supported at the moment.
+ def register(name, command_class)
+ @commands[name.to_sym] = [command_class, []]
+ end
+ end
+ end
+end
diff --git a/lib/irb/command/backtrace.rb b/lib/irb/command/backtrace.rb
new file mode 100644
index 0000000000..687bb075ac
--- /dev/null
+++ b/lib/irb/command/backtrace.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class Backtrace < DebugCommand
+ def execute(arg)
+ execute_debug_command(pre_cmds: "backtrace #{arg}")
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/base.rb b/lib/irb/command/base.rb
new file mode 100644
index 0000000000..b078b48237
--- /dev/null
+++ b/lib/irb/command/base.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+#
+# nop.rb -
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class CommandArgumentError < StandardError; end
+
+ def self.extract_ruby_args(*args, **kwargs)
+ throw :EXTRACT_RUBY_ARGS, [args, kwargs]
+ end
+
+ class Base
+ class << self
+ def category(category = nil)
+ @category = category if category
+ @category
+ end
+
+ def description(description = nil)
+ @description = description if description
+ @description
+ end
+
+ def help_message(help_message = nil)
+ @help_message = help_message if help_message
+ @help_message
+ end
+
+ private
+
+ def highlight(text)
+ Color.colorize(text, [:BOLD, :BLUE])
+ end
+ end
+
+ def self.execute(irb_context, arg)
+ new(irb_context).execute(arg)
+ rescue CommandArgumentError => e
+ puts e.message
+ end
+
+ def initialize(irb_context)
+ @irb_context = irb_context
+ end
+
+ attr_reader :irb_context
+
+ def execute(arg)
+ #nop
+ end
+ end
+
+ Nop = Base
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/break.rb b/lib/irb/command/break.rb
new file mode 100644
index 0000000000..a8f81fe665
--- /dev/null
+++ b/lib/irb/command/break.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class Break < DebugCommand
+ def execute(arg)
+ execute_debug_command(pre_cmds: "break #{arg}")
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/catch.rb b/lib/irb/command/catch.rb
new file mode 100644
index 0000000000..529dcbca5a
--- /dev/null
+++ b/lib/irb/command/catch.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class Catch < DebugCommand
+ def execute(arg)
+ execute_debug_command(pre_cmds: "catch #{arg}")
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/chws.rb b/lib/irb/command/chws.rb
new file mode 100644
index 0000000000..ef456d0961
--- /dev/null
+++ b/lib/irb/command/chws.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+#
+# change-ws.rb -
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+require_relative "../ext/change-ws"
+
+module IRB
+ # :stopdoc:
+
+ module Command
+
+ class CurrentWorkingWorkspace < Base
+ category "Workspace"
+ description "Show the current workspace."
+
+ def execute(_arg)
+ puts "Current workspace: #{irb_context.main}"
+ end
+ end
+
+ class ChangeWorkspace < Base
+ category "Workspace"
+ description "Change the current workspace to an object."
+
+ def execute(arg)
+ if arg.empty?
+ irb_context.change_workspace
+ else
+ obj = eval(arg, irb_context.workspace.binding)
+ irb_context.change_workspace(obj)
+ end
+
+ puts "Current workspace: #{irb_context.main}"
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/context.rb b/lib/irb/command/context.rb
new file mode 100644
index 0000000000..b4fc807343
--- /dev/null
+++ b/lib/irb/command/context.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module IRB
+ module Command
+ class Context < Base
+ category "IRB"
+ description "Displays current configuration."
+
+ def execute(_arg)
+ # This command just displays the configuration.
+ # Modifying the configuration is achieved by sending a message to IRB.conf.
+ Pager.page_content(IRB.CurrentContext.inspect)
+ end
+ end
+ end
+end
diff --git a/lib/irb/command/continue.rb b/lib/irb/command/continue.rb
new file mode 100644
index 0000000000..0daa029b15
--- /dev/null
+++ b/lib/irb/command/continue.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class Continue < DebugCommand
+ def execute(arg)
+ execute_debug_command(do_cmds: "continue #{arg}")
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/debug.rb b/lib/irb/command/debug.rb
new file mode 100644
index 0000000000..f9aca0a672
--- /dev/null
+++ b/lib/irb/command/debug.rb
@@ -0,0 +1,86 @@
+require_relative "../debug"
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class Debug < Base
+ category "Debugging"
+ description "Start the debugger of debug.gem."
+
+ BINDING_IRB_FRAME_REGEXPS = [
+ '<internal:prelude>',
+ binding.method(:irb).source_location.first,
+ ].map { |file| /\A#{Regexp.escape(file)}:\d+:in (`|'Binding#)irb'\z/ }
+
+ def execute(_arg)
+ execute_debug_command
+ end
+
+ def execute_debug_command(pre_cmds: nil, do_cmds: nil)
+ pre_cmds = pre_cmds&.rstrip
+ do_cmds = do_cmds&.rstrip
+
+ if irb_context.with_debugger
+ # If IRB is already running with a debug session, throw the command and IRB.debug_readline will pass it to the debugger.
+ if cmd = pre_cmds || do_cmds
+ throw :IRB_EXIT, cmd
+ else
+ puts "IRB is already running with a debug session."
+ return
+ end
+ else
+ # If IRB is not running with a debug session yet, then:
+ # 1. Check if the debugging command is run from a `binding.irb` call.
+ # 2. If so, try setting up the debug gem.
+ # 3. Insert a debug breakpoint at `Irb#debug_break` with the intended command.
+ # 4. Exit the current Irb#run call via `throw :IRB_EXIT`.
+ # 5. `Irb#debug_break` will be called and trigger the breakpoint, which will run the intended command.
+ unless binding_irb?
+ puts "Debugging commands are only available when IRB is started with binding.irb"
+ return
+ end
+
+ if IRB.respond_to?(:JobManager)
+ warn "Can't start the debugger when IRB is running in a multi-IRB session."
+ return
+ end
+
+ unless IRB::Debug.setup(irb_context.irb)
+ puts <<~MSG
+ You need to install the debug gem before using this command.
+ If you use `bundle exec`, please add `gem "debug"` into your Gemfile.
+ MSG
+ return
+ end
+
+ IRB::Debug.insert_debug_break(pre_cmds: pre_cmds, do_cmds: do_cmds)
+
+ # exit current Irb#run call
+ throw :IRB_EXIT
+ end
+ end
+
+ private
+
+ def binding_irb?
+ caller.any? do |frame|
+ BINDING_IRB_FRAME_REGEXPS.any? do |regexp|
+ frame.match?(regexp)
+ end
+ end
+ end
+ end
+
+ class DebugCommand < Debug
+ def self.category
+ "Debugging"
+ end
+
+ def self.description
+ command_name = self.name.split("::").last.downcase
+ "Start the debugger of debug.gem and run its `#{command_name}` command."
+ end
+ end
+ end
+end
diff --git a/lib/irb/command/delete.rb b/lib/irb/command/delete.rb
new file mode 100644
index 0000000000..2a57a4a3de
--- /dev/null
+++ b/lib/irb/command/delete.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class Delete < DebugCommand
+ def execute(arg)
+ execute_debug_command(pre_cmds: "delete #{arg}")
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/disable_irb.rb b/lib/irb/command/disable_irb.rb
new file mode 100644
index 0000000000..0b00d0302b
--- /dev/null
+++ b/lib/irb/command/disable_irb.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class DisableIrb < Base
+ category "IRB"
+ description "Disable binding.irb."
+
+ def execute(*)
+ ::Binding.define_method(:irb) {}
+ IRB.irb_exit
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/edit.rb b/lib/irb/command/edit.rb
new file mode 100644
index 0000000000..cb7e0c4873
--- /dev/null
+++ b/lib/irb/command/edit.rb
@@ -0,0 +1,63 @@
+require 'shellwords'
+
+require_relative "../color"
+require_relative "../source_finder"
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class Edit < Base
+ include RubyArgsExtractor
+
+ category "Misc"
+ description 'Open a file or source location.'
+ help_message <<~HELP_MESSAGE
+ Usage: edit [FILE or constant or method signature]
+
+ Open a file in the editor specified in #{highlight('ENV["VISUAL"]')} or #{highlight('ENV["EDITOR"]')}
+
+ - If no arguments are provided, IRB will attempt to open the file the current context was defined in.
+ - If FILE is provided, IRB will open the file.
+ - If a constant or method signature is provided, IRB will attempt to locate the source file and open it.
+
+ Examples:
+
+ edit
+ edit foo.rb
+ edit Foo
+ edit Foo#bar
+ HELP_MESSAGE
+
+ def execute(arg)
+ # Accept string literal for backward compatibility
+ path = unwrap_string_literal(arg)
+
+ if path.nil?
+ path = @irb_context.irb_path
+ elsif !File.exist?(path)
+ source = SourceFinder.new(@irb_context).find_source(path)
+
+ if source&.file_exist? && !source.binary_file?
+ path = source.file
+ end
+ end
+
+ unless File.exist?(path)
+ puts "Can not find file: #{path}"
+ return
+ end
+
+ if editor = (ENV['VISUAL'] || ENV['EDITOR'])
+ puts "command: '#{editor}'"
+ puts " path: #{path}"
+ system(*Shellwords.split(editor), path)
+ else
+ puts "Can not find editor setting: ENV['VISUAL'] or ENV['EDITOR']"
+ end
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/exit.rb b/lib/irb/command/exit.rb
new file mode 100644
index 0000000000..b4436f0343
--- /dev/null
+++ b/lib/irb/command/exit.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class Exit < Base
+ category "IRB"
+ description "Exit the current irb session."
+
+ def execute(_arg)
+ IRB.irb_exit
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/finish.rb b/lib/irb/command/finish.rb
new file mode 100644
index 0000000000..3311a0e6e9
--- /dev/null
+++ b/lib/irb/command/finish.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class Finish < DebugCommand
+ def execute(arg)
+ execute_debug_command(do_cmds: "finish #{arg}")
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/force_exit.rb b/lib/irb/command/force_exit.rb
new file mode 100644
index 0000000000..14086aa849
--- /dev/null
+++ b/lib/irb/command/force_exit.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class ForceExit < Base
+ category "IRB"
+ description "Exit the current process."
+
+ def execute(_arg)
+ throw :IRB_EXIT, true
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/help.rb b/lib/irb/command/help.rb
new file mode 100644
index 0000000000..1ed7a7707c
--- /dev/null
+++ b/lib/irb/command/help.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+module IRB
+ module Command
+ class Help < Base
+ category "Help"
+ description "List all available commands. Use `help <command>` to get information about a specific command."
+
+ def execute(command_name)
+ content =
+ if command_name.empty?
+ help_message
+ else
+ if command_class = Command.load_command(command_name)
+ command_class.help_message || command_class.description
+ else
+ "Can't find command `#{command_name}`. Please check the command name and try again.\n\n"
+ end
+ end
+ Pager.page_content(content)
+ end
+
+ private
+
+ def help_message
+ commands_info = IRB::Command.all_commands_info
+ helper_methods_info = IRB::HelperMethod.all_helper_methods_info
+ commands_grouped_by_categories = commands_info.group_by { |cmd| cmd[:category] }
+ commands_grouped_by_categories["Helper methods"] = helper_methods_info
+
+ user_aliases = irb_context.instance_variable_get(:@user_aliases)
+
+ commands_grouped_by_categories["Aliases"] = user_aliases.map do |alias_name, target|
+ { display_name: alias_name, description: "Alias for `#{target}`" }
+ end
+
+ if irb_context.with_debugger
+ # Remove the original "Debugging" category
+ commands_grouped_by_categories.delete("Debugging")
+ # Add an empty "Debugging (from debug.gem)" category at the end
+ commands_grouped_by_categories["Debugging (from debug.gem)"] = []
+ end
+
+ longest_cmd_name_length = commands_info.map { |c| c[:display_name].length }.max
+
+ output = StringIO.new
+
+ help_cmds = commands_grouped_by_categories.delete("Help")
+
+ add_category_to_output("Help", help_cmds, output, longest_cmd_name_length)
+
+ commands_grouped_by_categories.each do |category, cmds|
+ add_category_to_output(category, cmds, output, longest_cmd_name_length)
+ end
+
+ # Append the debugger help at the end
+ if irb_context.with_debugger
+ output.puts DEBUGGER__.help
+ end
+
+ output.string
+ end
+
+ def add_category_to_output(category, cmds, output, longest_cmd_name_length)
+ output.puts Color.colorize(category, [:BOLD])
+
+ cmds.each do |cmd|
+ output.puts " #{cmd[:display_name].to_s.ljust(longest_cmd_name_length)} #{cmd[:description]}"
+ end
+
+ output.puts
+ end
+ end
+ end
+end
diff --git a/lib/irb/command/history.rb b/lib/irb/command/history.rb
new file mode 100644
index 0000000000..90f87f9102
--- /dev/null
+++ b/lib/irb/command/history.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require "stringio"
+
+require_relative "../pager"
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class History < Base
+ category "IRB"
+ description "Shows the input history. `-g [query]` or `-G [query]` allows you to filter the output."
+
+ def execute(arg)
+
+ if (match = arg&.match(/(-g|-G)\s+(?<grep>.+)\s*\n\z/))
+ grep = Regexp.new(match[:grep])
+ end
+
+ formatted_inputs = irb_context.io.class::HISTORY.each_with_index.reverse_each.filter_map do |input, index|
+ next if grep && !input.match?(grep)
+
+ header = "#{index}: "
+
+ first_line, *other_lines = input.split("\n")
+ first_line = "#{header}#{first_line}"
+
+ truncated_lines = other_lines.slice!(1..) # Show 1 additional line (2 total)
+ other_lines << "..." if truncated_lines&.any?
+
+ other_lines.map! do |line|
+ " " * header.length + line
+ end
+
+ [first_line, *other_lines].join("\n") + "\n"
+ end
+
+ Pager.page_content(formatted_inputs.join)
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/info.rb b/lib/irb/command/info.rb
new file mode 100644
index 0000000000..d08ce00a32
--- /dev/null
+++ b/lib/irb/command/info.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class Info < DebugCommand
+ def execute(arg)
+ execute_debug_command(pre_cmds: "info #{arg}")
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/internal_helpers.rb b/lib/irb/command/internal_helpers.rb
new file mode 100644
index 0000000000..249b5cdede
--- /dev/null
+++ b/lib/irb/command/internal_helpers.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module IRB
+ module Command
+ # Internal use only, for default command's backward compatibility.
+ module RubyArgsExtractor # :nodoc:
+ def unwrap_string_literal(str)
+ return if str.empty?
+
+ sexp = Ripper.sexp(str)
+ if sexp && sexp.size == 2 && sexp.last&.first&.first == :string_literal
+ @irb_context.workspace.binding.eval(str).to_s
+ else
+ str
+ end
+ end
+
+ def ruby_args(arg)
+ # Use throw and catch to handle arg that includes `;`
+ # For example: "1, kw: (2; 3); 4" will be parsed to [[1], { kw: 3 }]
+ catch(:EXTRACT_RUBY_ARGS) do
+ @irb_context.workspace.binding.eval "IRB::Command.extract_ruby_args #{arg}"
+ end || [[], {}]
+ end
+ end
+ end
+end
diff --git a/lib/irb/command/irb_info.rb b/lib/irb/command/irb_info.rb
new file mode 100644
index 0000000000..6d868de94c
--- /dev/null
+++ b/lib/irb/command/irb_info.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class IrbInfo < Base
+ category "IRB"
+ description "Show information about IRB."
+
+ def execute(_arg)
+ str = "Ruby version: #{RUBY_VERSION}\n"
+ str += "IRB version: #{IRB.version}\n"
+ str += "InputMethod: #{IRB.CurrentContext.io.inspect}\n"
+ str += "Completion: #{IRB.CurrentContext.io.respond_to?(:completion_info) ? IRB.CurrentContext.io.completion_info : 'off'}\n"
+ rc_files = IRB.irbrc_files
+ str += ".irbrc paths: #{rc_files.join(", ")}\n" if rc_files.any?
+ str += "RUBY_PLATFORM: #{RUBY_PLATFORM}\n"
+ str += "LANG env: #{ENV["LANG"]}\n" if ENV["LANG"] && !ENV["LANG"].empty?
+ str += "LC_ALL env: #{ENV["LC_ALL"]}\n" if ENV["LC_ALL"] && !ENV["LC_ALL"].empty?
+ str += "East Asian Ambiguous Width: #{Reline.ambiguous_width.inspect}\n"
+ if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
+ codepage = `chcp`.b.sub(/.*: (\d+)\n/, '\1')
+ str += "Code page: #{codepage}\n"
+ end
+ puts str
+ nil
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/load.rb b/lib/irb/command/load.rb
new file mode 100644
index 0000000000..1cd3f279d1
--- /dev/null
+++ b/lib/irb/command/load.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+#
+# load.rb -
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+require_relative "../ext/loader"
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class LoaderCommand < Base
+ include RubyArgsExtractor
+ include IrbLoader
+
+ def raise_cmd_argument_error
+ raise CommandArgumentError.new("Please specify the file name.")
+ end
+ end
+
+ class Load < LoaderCommand
+ category "IRB"
+ description "Load a Ruby file."
+
+ def execute(arg)
+ args, kwargs = ruby_args(arg)
+ execute_internal(*args, **kwargs)
+ end
+
+ def execute_internal(file_name = nil, priv = nil)
+ raise_cmd_argument_error unless file_name
+ irb_load(file_name, priv)
+ end
+ end
+
+ class Require < LoaderCommand
+ category "IRB"
+ description "Require a Ruby file."
+
+ def execute(arg)
+ args, kwargs = ruby_args(arg)
+ execute_internal(*args, **kwargs)
+ end
+
+ def execute_internal(file_name = nil)
+ raise_cmd_argument_error unless file_name
+
+ rex = Regexp.new("#{Regexp.quote(file_name)}(\.o|\.rb)?")
+ return false if $".find{|f| f =~ rex}
+
+ case file_name
+ when /\.rb$/
+ begin
+ if irb_load(file_name)
+ $".push file_name
+ return true
+ end
+ rescue LoadError
+ end
+ when /\.(so|o|sl)$/
+ return ruby_require(file_name)
+ end
+
+ begin
+ irb_load(f = file_name + ".rb")
+ $".push f
+ return true
+ rescue LoadError
+ return ruby_require(file_name)
+ end
+ end
+ end
+
+ class Source < LoaderCommand
+ category "IRB"
+ description "Loads a given file in the current session."
+
+ def execute(arg)
+ args, kwargs = ruby_args(arg)
+ execute_internal(*args, **kwargs)
+ end
+
+ def execute_internal(file_name = nil)
+ raise_cmd_argument_error unless file_name
+
+ source_file(file_name)
+ end
+ end
+ end
+ # :startdoc:
+end
diff --git a/lib/irb/command/ls.rb b/lib/irb/command/ls.rb
new file mode 100644
index 0000000000..cbd9998bc4
--- /dev/null
+++ b/lib/irb/command/ls.rb
@@ -0,0 +1,155 @@
+# frozen_string_literal: true
+
+require "reline"
+require "stringio"
+
+require_relative "../pager"
+require_relative "../color"
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class Ls < Base
+ include RubyArgsExtractor
+
+ category "Context"
+ description "Show methods, constants, and variables."
+
+ help_message <<~HELP_MESSAGE
+ Usage: ls [obj] [-g [query]]
+
+ -g [query] Filter the output with a query.
+ HELP_MESSAGE
+
+ def execute(arg)
+ if match = arg.match(/\A(?<target>.+\s|)(-g|-G)\s+(?<grep>.+)$/)
+ if match[:target].empty?
+ use_main = true
+ else
+ obj = @irb_context.workspace.binding.eval(match[:target])
+ end
+ grep = Regexp.new(match[:grep])
+ else
+ args, kwargs = ruby_args(arg)
+ use_main = args.empty?
+ obj = args.first
+ grep = kwargs[:grep]
+ end
+
+ if use_main
+ obj = irb_context.workspace.main
+ locals = irb_context.workspace.binding.local_variables
+ end
+
+ o = Output.new(grep: grep)
+
+ klass = (obj.class == Class || obj.class == Module ? obj : obj.class)
+
+ o.dump("constants", obj.constants) if obj.respond_to?(:constants)
+ dump_methods(o, klass, obj)
+ o.dump("instance variables", obj.instance_variables)
+ o.dump("class variables", klass.class_variables)
+ o.dump("locals", locals) if locals
+ o.print_result
+ end
+
+ def dump_methods(o, klass, obj)
+ singleton_class = begin obj.singleton_class; rescue TypeError; nil end
+ dumped_mods = Array.new
+ ancestors = klass.ancestors
+ ancestors = ancestors.reject { |c| c >= Object } if klass < Object
+ singleton_ancestors = (singleton_class&.ancestors || []).reject { |c| c >= Class }
+
+ # singleton_class' ancestors should be at the front
+ maps = class_method_map(singleton_ancestors, dumped_mods) + class_method_map(ancestors, dumped_mods)
+ maps.each do |mod, methods|
+ name = mod == singleton_class ? "#{klass}.methods" : "#{mod}#methods"
+ o.dump(name, methods)
+ end
+ end
+
+ def class_method_map(classes, dumped_mods)
+ dumped_methods = Array.new
+ classes.map do |mod|
+ next if dumped_mods.include? mod
+
+ dumped_mods << mod
+
+ methods = mod.public_instance_methods(false).select do |method|
+ if dumped_methods.include? method
+ false
+ else
+ dumped_methods << method
+ true
+ end
+ end
+
+ [mod, methods]
+ end.compact
+ end
+
+ class Output
+ MARGIN = " "
+
+ def initialize(grep: nil)
+ @grep = grep
+ @line_width = screen_width - MARGIN.length # right padding
+ @io = StringIO.new
+ end
+
+ def print_result
+ Pager.page_content(@io.string)
+ end
+
+ def dump(name, strs)
+ strs = strs.grep(@grep) if @grep
+ strs = strs.sort
+ return if strs.empty?
+
+ # Attempt a single line
+ @io.print "#{Color.colorize(name, [:BOLD, :BLUE])}: "
+ if fits_on_line?(strs, cols: strs.size, offset: "#{name}: ".length)
+ @io.puts strs.join(MARGIN)
+ return
+ end
+ @io.puts
+
+ # Dump with the largest # of columns that fits on a line
+ cols = strs.size
+ until fits_on_line?(strs, cols: cols, offset: MARGIN.length) || cols == 1
+ cols -= 1
+ end
+ widths = col_widths(strs, cols: cols)
+ strs.each_slice(cols) do |ss|
+ @io.puts ss.map.with_index { |s, i| "#{MARGIN}%-#{widths[i]}s" % s }.join
+ end
+ end
+
+ private
+
+ def fits_on_line?(strs, cols:, offset: 0)
+ width = col_widths(strs, cols: cols).sum + MARGIN.length * (cols - 1)
+ width <= @line_width - offset
+ end
+
+ def col_widths(strs, cols:)
+ cols.times.map do |col|
+ (col...strs.size).step(cols).map do |i|
+ strs[i].length
+ end.max
+ end
+ end
+
+ def screen_width
+ Reline.get_screen_size.last
+ rescue Errno::EINVAL # in `winsize': Invalid argument - <STDIN>
+ 80
+ end
+ end
+ private_constant :Output
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/measure.rb b/lib/irb/command/measure.rb
new file mode 100644
index 0000000000..f96be20de8
--- /dev/null
+++ b/lib/irb/command/measure.rb
@@ -0,0 +1,49 @@
+module IRB
+ # :stopdoc:
+
+ module Command
+ class Measure < Base
+ include RubyArgsExtractor
+
+ category "Misc"
+ description "`measure` enables the mode to measure processing time. `measure :off` disables it."
+
+ def initialize(*args)
+ super(*args)
+ end
+
+ def execute(arg)
+ if arg&.match?(/^do$|^do[^\w]|^\{/)
+ warn 'Configure IRB.conf[:MEASURE_PROC] to add custom measure methods.'
+ return
+ end
+ args, kwargs = ruby_args(arg)
+ execute_internal(*args, **kwargs)
+ end
+
+ def execute_internal(type = nil, arg = nil)
+ # Please check IRB.init_config in lib/irb/init.rb that sets
+ # IRB.conf[:MEASURE_PROC] to register default "measure" methods,
+ # "measure :time" (abbreviated as "measure") and "measure :stackprof".
+
+ case type
+ when :off
+ IRB.unset_measure_callback(arg)
+ when :list
+ IRB.conf[:MEASURE_CALLBACKS].each do |type_name, _, arg_val|
+ puts "- #{type_name}" + (arg_val ? "(#{arg_val.inspect})" : '')
+ end
+ when :on
+ added = IRB.set_measure_callback(arg)
+ puts "#{added[0]} is added." if added
+ else
+ added = IRB.set_measure_callback(type, arg)
+ puts "#{added[0]} is added." if added
+ end
+ nil
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/next.rb b/lib/irb/command/next.rb
new file mode 100644
index 0000000000..3fc6b68d21
--- /dev/null
+++ b/lib/irb/command/next.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class Next < DebugCommand
+ def execute(arg)
+ execute_debug_command(do_cmds: "next #{arg}")
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/pushws.rb b/lib/irb/command/pushws.rb
new file mode 100644
index 0000000000..b51928c650
--- /dev/null
+++ b/lib/irb/command/pushws.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+#
+# change-ws.rb -
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+
+require_relative "../ext/workspaces"
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class Workspaces < Base
+ category "Workspace"
+ description "Show workspaces."
+
+ def execute(_arg)
+ inspection_resuls = irb_context.instance_variable_get(:@workspace_stack).map do |ws|
+ truncated_inspect(ws.main)
+ end
+
+ puts "[" + inspection_resuls.join(", ") + "]"
+ end
+
+ private
+
+ def truncated_inspect(obj)
+ obj_inspection = obj.inspect
+
+ if obj_inspection.size > 20
+ obj_inspection = obj_inspection[0, 19] + "...>"
+ end
+
+ obj_inspection
+ end
+ end
+
+ class PushWorkspace < Workspaces
+ category "Workspace"
+ description "Push an object to the workspace stack."
+
+ def execute(arg)
+ if arg.empty?
+ irb_context.push_workspace
+ else
+ obj = eval(arg, irb_context.workspace.binding)
+ irb_context.push_workspace(obj)
+ end
+ super
+ end
+ end
+
+ class PopWorkspace < Workspaces
+ category "Workspace"
+ description "Pop a workspace from the workspace stack."
+
+ def execute(_arg)
+ irb_context.pop_workspace
+ super
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/show_doc.rb b/lib/irb/command/show_doc.rb
new file mode 100644
index 0000000000..8a2188e4eb
--- /dev/null
+++ b/lib/irb/command/show_doc.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+module IRB
+ module Command
+ class ShowDoc < Base
+ include RubyArgsExtractor
+
+ category "Context"
+ description "Look up documentation with RI."
+
+ help_message <<~HELP_MESSAGE
+ Usage: show_doc [name]
+
+ When name is provided, IRB will look up the documentation for the given name.
+ When no name is provided, a RI session will be started.
+
+ Examples:
+
+ show_doc
+ show_doc Array
+ show_doc Array#each
+
+ HELP_MESSAGE
+
+ def execute(arg)
+ # Accept string literal for backward compatibility
+ name = unwrap_string_literal(arg)
+ require 'rdoc/ri/driver'
+
+ unless ShowDoc.const_defined?(:Ri)
+ opts = RDoc::RI::Driver.process_args([])
+ ShowDoc.const_set(:Ri, RDoc::RI::Driver.new(opts))
+ end
+
+ if name.nil?
+ Ri.interactive
+ else
+ begin
+ Ri.display_name(name)
+ rescue RDoc::RI::Error
+ puts $!.message
+ end
+ end
+
+ nil
+ rescue LoadError, SystemExit
+ warn "Can't display document because `rdoc` is not installed."
+ end
+ end
+ end
+end
diff --git a/lib/irb/command/show_source.rb b/lib/irb/command/show_source.rb
new file mode 100644
index 0000000000..f4c6f104a2
--- /dev/null
+++ b/lib/irb/command/show_source.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+require_relative "../source_finder"
+require_relative "../pager"
+require_relative "../color"
+
+module IRB
+ module Command
+ class ShowSource < Base
+ include RubyArgsExtractor
+
+ category "Context"
+ description "Show the source code of a given method, class/module, or constant."
+
+ help_message <<~HELP_MESSAGE
+ Usage: show_source [target] [-s]
+
+ -s Show the super method. You can stack it like `-ss` to show the super of the super, etc.
+
+ Examples:
+
+ show_source Foo
+ show_source Foo#bar
+ show_source Foo#bar -s
+ show_source Foo.baz
+ show_source Foo::BAR
+ HELP_MESSAGE
+
+ def execute(arg)
+ # Accept string literal for backward compatibility
+ str = unwrap_string_literal(arg)
+ unless str.is_a?(String)
+ puts "Error: Expected a string but got #{str.inspect}"
+ return
+ end
+
+ str, esses = str.split(" -")
+ super_level = esses ? esses.count("s") : 0
+ source = SourceFinder.new(@irb_context).find_source(str, super_level)
+
+ if source
+ show_source(source)
+ elsif super_level > 0
+ puts "Error: Couldn't locate a super definition for #{str}"
+ else
+ puts "Error: Couldn't locate a definition for #{str}"
+ end
+ nil
+ end
+
+ private
+
+ def show_source(source)
+ if source.binary_file?
+ content = "\n#{bold('Defined in binary file')}: #{source.file}\n\n"
+ else
+ code = source.colorized_content || 'Source not available'
+ content = <<~CONTENT
+
+ #{bold("From")}: #{source.file}:#{source.line}
+
+ #{code.chomp}
+
+ CONTENT
+ end
+ Pager.page_content(content)
+ end
+
+ def bold(str)
+ Color.colorize(str, [:BOLD])
+ end
+ end
+ end
+end
diff --git a/lib/irb/command/step.rb b/lib/irb/command/step.rb
new file mode 100644
index 0000000000..29e5e35ac0
--- /dev/null
+++ b/lib/irb/command/step.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "debug"
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class Step < DebugCommand
+ def execute(arg)
+ execute_debug_command(do_cmds: "step #{arg}")
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/subirb.rb b/lib/irb/command/subirb.rb
new file mode 100644
index 0000000000..85af28c1a5
--- /dev/null
+++ b/lib/irb/command/subirb.rb
@@ -0,0 +1,123 @@
+# frozen_string_literal: true
+#
+# multi.rb -
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class MultiIRBCommand < Base
+ include RubyArgsExtractor
+
+ private
+
+ def print_deprecated_warning
+ warn <<~MSG
+ Multi-irb commands are deprecated and will be removed in IRB 2.0.0. Please use workspace commands instead.
+ If you have any use case for multi-irb, please leave a comment at https://github.com/ruby/irb/issues/653
+ MSG
+ end
+
+ def extend_irb_context
+ # this extension patches IRB context like IRB.CurrentContext
+ require_relative "../ext/multi-irb"
+ end
+
+ def print_debugger_warning
+ warn "Multi-IRB commands are not available when the debugger is enabled."
+ end
+ end
+
+ class IrbCommand < MultiIRBCommand
+ category "Multi-irb (DEPRECATED)"
+ description "Start a child IRB."
+
+ def execute(arg)
+ args, kwargs = ruby_args(arg)
+ execute_internal(*args, **kwargs)
+ end
+
+ def execute_internal(*obj)
+ print_deprecated_warning
+
+ if irb_context.with_debugger
+ print_debugger_warning
+ return
+ end
+
+ extend_irb_context
+ IRB.irb(nil, *obj)
+ puts IRB.JobManager.inspect
+ end
+ end
+
+ class Jobs < MultiIRBCommand
+ category "Multi-irb (DEPRECATED)"
+ description "List of current sessions."
+
+ def execute(_arg)
+ print_deprecated_warning
+
+ if irb_context.with_debugger
+ print_debugger_warning
+ return
+ end
+
+ extend_irb_context
+ puts IRB.JobManager.inspect
+ end
+ end
+
+ class Foreground < MultiIRBCommand
+ category "Multi-irb (DEPRECATED)"
+ description "Switches to the session of the given number."
+
+ def execute(arg)
+ args, kwargs = ruby_args(arg)
+ execute_internal(*args, **kwargs)
+ end
+
+ def execute_internal(key = nil)
+ print_deprecated_warning
+
+ if irb_context.with_debugger
+ print_debugger_warning
+ return
+ end
+
+ extend_irb_context
+
+ raise CommandArgumentError.new("Please specify the id of target IRB job (listed in the `jobs` command).") unless key
+ IRB.JobManager.switch(key)
+ puts IRB.JobManager.inspect
+ end
+ end
+
+ class Kill < MultiIRBCommand
+ category "Multi-irb (DEPRECATED)"
+ description "Kills the session with the given number."
+
+ def execute(arg)
+ args, kwargs = ruby_args(arg)
+ execute_internal(*args, **kwargs)
+ end
+
+ def execute_internal(*keys)
+ print_deprecated_warning
+
+ if irb_context.with_debugger
+ print_debugger_warning
+ return
+ end
+
+ extend_irb_context
+ IRB.JobManager.kill(*keys)
+ puts IRB.JobManager.inspect
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/command/whereami.rb b/lib/irb/command/whereami.rb
new file mode 100644
index 0000000000..c8439f1212
--- /dev/null
+++ b/lib/irb/command/whereami.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module IRB
+ # :stopdoc:
+
+ module Command
+ class Whereami < Base
+ category "Context"
+ description "Show the source code around binding.irb again."
+
+ def execute(_arg)
+ code = irb_context.workspace.code_around_binding
+ if code
+ puts code
+ else
+ puts "The current context doesn't have code."
+ end
+ end
+ end
+ end
+
+ # :startdoc:
+end
diff --git a/lib/irb/completion.rb b/lib/irb/completion.rb
index 64e387d49c..a3d89373c3 100644
--- a/lib/irb/completion.rb
+++ b/lib/irb/completion.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/completion.rb -
# by Keiju ISHITSUKA(keiju@ishitsuka.com)
@@ -8,30 +8,7 @@
require_relative 'ruby-lex'
module IRB
- module InputCompletor # :nodoc:
- using Module.new {
- refine ::Binding do
- def eval_methods
- ::Kernel.instance_method(:methods).bind(eval("self")).call
- end
-
- def eval_private_methods
- ::Kernel.instance_method(:private_methods).bind(eval("self")).call
- end
-
- def eval_instance_variables
- ::Kernel.instance_method(:instance_variables).bind(eval("self")).call
- end
-
- def eval_global_variables
- ::Kernel.instance_method(:global_variables).bind(eval("self")).call
- end
-
- def eval_class_constants
- ::Module.instance_method(:constants).bind(eval("self.class")).call
- end
- end
- }
+ class BaseCompletor # :nodoc:
# Set of reserved words used by Ruby, you should not use these for
# constants or variables
@@ -56,7 +33,13 @@ module IRB
yield
]
- BASIC_WORD_BREAK_CHARACTERS = " \t\n`><=;|&{("
+ def completion_candidates(preposing, target, postposing, bind:)
+ raise NotImplementedError
+ end
+
+ def doc_namespace(preposing, matched, postposing, bind:)
+ raise NotImplementedError
+ end
GEM_PATHS =
if defined?(Gem::Specification)
@@ -73,7 +56,7 @@ module IRB
[]
end.freeze
- def self.retrieve_gem_and_system_load_path
+ def retrieve_gem_and_system_load_path
candidates = (GEM_PATHS | $LOAD_PATH)
candidates.map do |p|
if p.respond_to?(:to_path)
@@ -84,8 +67,8 @@ module IRB
end.compact.sort
end
- def self.retrieve_files_to_require_from_load_path
- @@files_from_load_path ||=
+ def retrieve_files_to_require_from_load_path
+ @files_from_load_path ||=
(
shortest = []
rest = retrieve_gem_and_system_load_path.each_with_object([]) { |path, result|
@@ -103,13 +86,74 @@ module IRB
)
end
- def self.retrieve_files_to_require_relative_from_current_dir
- @@files_from_current_dir ||= Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: '.').map { |path|
+ def command_completions(preposing, target)
+ if preposing.empty? && !target.empty?
+ IRB::Command.command_names.select { _1.start_with?(target) }
+ else
+ []
+ end
+ end
+
+ def retrieve_files_to_require_relative_from_current_dir
+ @files_from_current_dir ||= Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: '.').map { |path|
path.sub(/\.(rb|#{RbConfig::CONFIG['DLEXT']})\z/, '')
}
end
+ end
+
+ class TypeCompletor < BaseCompletor # :nodoc:
+ def initialize(context)
+ @context = context
+ end
+
+ def inspect
+ ReplTypeCompletor.info
+ end
+
+ def completion_candidates(preposing, target, _postposing, bind:)
+ commands = command_completions(preposing, target)
+ result = ReplTypeCompletor.analyze(preposing + target, binding: bind, filename: @context.irb_path)
+ return commands unless result
+
+ commands | result.completion_candidates.map { target + _1 }
+ end
+
+ def doc_namespace(preposing, matched, _postposing, bind:)
+ result = ReplTypeCompletor.analyze(preposing + matched, binding: bind, filename: @context.irb_path)
+ result&.doc_namespace('')
+ end
+ end
+
+ class RegexpCompletor < BaseCompletor # :nodoc:
+ using Module.new {
+ refine ::Binding do
+ def eval_methods
+ ::Kernel.instance_method(:methods).bind(eval("self")).call
+ end
+
+ def eval_private_methods
+ ::Kernel.instance_method(:private_methods).bind(eval("self")).call
+ end
+
+ def eval_instance_variables
+ ::Kernel.instance_method(:instance_variables).bind(eval("self")).call
+ end
+
+ def eval_global_variables
+ ::Kernel.instance_method(:global_variables).bind(eval("self")).call
+ end
+
+ def eval_class_constants
+ ::Module.instance_method(:constants).bind(eval("self.class")).call
+ end
+ end
+ }
+
+ def inspect
+ 'RegexpCompletor'
+ end
- CompletionRequireProc = lambda { |target, preposing = nil, postposing = nil|
+ def complete_require_path(target, preposing, postposing)
if target =~ /\A(['"])([^'"]+)\Z/
quote = $1
actual_target = $2
@@ -124,39 +168,38 @@ module IRB
break
end
end
- result = []
- if tok && tok.event == :on_ident && tok.state == Ripper::EXPR_CMDARG
- case tok.tok
- when 'require'
- result = retrieve_files_to_require_from_load_path.select { |path|
- path.start_with?(actual_target)
- }.map { |path|
- quote + path
- }
- when 'require_relative'
- result = retrieve_files_to_require_relative_from_current_dir.select { |path|
- path.start_with?(actual_target)
- }.map { |path|
- quote + path
- }
- end
+ return unless tok&.event == :on_ident && tok.state == Ripper::EXPR_CMDARG
+
+ case tok.tok
+ when 'require'
+ retrieve_files_to_require_from_load_path.select { |path|
+ path.start_with?(actual_target)
+ }.map { |path|
+ quote + path
+ }
+ when 'require_relative'
+ retrieve_files_to_require_relative_from_current_dir.select { |path|
+ path.start_with?(actual_target)
+ }.map { |path|
+ quote + path
+ }
end
- result
- }
+ end
- CompletionProc = lambda { |target, preposing = nil, postposing = nil|
+ def completion_candidates(preposing, target, postposing, bind:)
if preposing && postposing
- result = CompletionRequireProc.(target, preposing, postposing)
- unless result
- result = retrieve_completion_data(target).compact.map{ |i| i.encode(Encoding.default_external) }
- end
- result
- else
- retrieve_completion_data(target).compact.map{ |i| i.encode(Encoding.default_external) }
+ result = complete_require_path(target, preposing, postposing)
+ return result if result
end
- }
+ commands = command_completions(preposing || '', target)
+ commands | retrieve_completion_data(target, bind: bind, doc_namespace: false).compact.map{ |i| i.encode(Encoding.default_external) }
+ end
+
+ def doc_namespace(_preposing, matched, _postposing, bind:)
+ retrieve_completion_data(matched, bind: bind, doc_namespace: true)
+ end
- def self.retrieve_completion_data(input, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding, doc_namespace: false)
+ def retrieve_completion_data(input, bind:, doc_namespace:)
case input
# this regexp only matches the closing character because of irb's Reline.completer_quote_characters setting
# details are described in: https://github.com/ruby/irb/pull/523
@@ -199,16 +242,16 @@ module IRB
end
when /^([^\}]*\})\.([^.]*)$/
- # Proc or Hash
+ # Hash or Proc
receiver = $1
message = $2
if doc_namespace
- ["Proc.#{message}", "Hash.#{message}"]
+ ["Hash.#{message}", "Proc.#{message}"]
else
- proc_candidates = Proc.instance_methods.collect{|m| m.to_s}
hash_candidates = Hash.instance_methods.collect{|m| m.to_s}
- select_message(receiver, message, proc_candidates | hash_candidates)
+ proc_candidates = Proc.instance_methods.collect{|m| m.to_s}
+ select_message(receiver, message, hash_candidates | proc_candidates)
end
when /^(:[^:.]+)$/
@@ -218,7 +261,7 @@ module IRB
else
sym = $1
candidates = Symbol.all_symbols.collect do |s|
- ":" + s.id2name.encode(Encoding.default_external)
+ s.inspect
rescue EncodingError
# ignore
end
@@ -356,7 +399,7 @@ module IRB
if doc_namespace
rec_class = rec.is_a?(Module) ? rec : rec.class
- "#{rec_class.name}#{sep}#{candidates.find{ |i| i == message }}"
+ "#{rec_class.name}#{sep}#{candidates.find{ |i| i == message }}" rescue nil
else
select_message(receiver, message, candidates, sep)
end
@@ -374,13 +417,19 @@ module IRB
else
select_message(receiver, message, candidates.sort)
end
-
+ when /^\s*$/
+ # empty input
+ if doc_namespace
+ nil
+ else
+ []
+ end
else
if doc_namespace
vars = (bind.local_variables | bind.eval_instance_variables).collect{|m| m.to_s}
perfect_match_var = vars.find{|m| m.to_s == input}
if perfect_match_var
- eval("#{perfect_match_var}.class.name", bind)
+ eval("#{perfect_match_var}.class.name", bind) rescue nil
else
candidates = (bind.eval_methods | bind.eval_private_methods | bind.local_variables | bind.eval_instance_variables | bind.eval_class_constants).collect{|m| m.to_s}
candidates |= ReservedWords
@@ -394,44 +443,10 @@ module IRB
end
end
- PerfectMatchedProc = ->(matched, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding) {
- begin
- require 'rdoc'
- rescue LoadError
- return
- end
-
- RDocRIDriver ||= RDoc::RI::Driver.new
-
- if matched =~ /\A(?:::)?RubyVM/ and not ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
- IRB.__send__(:easter_egg)
- return
- end
-
- namespace = retrieve_completion_data(matched, bind: bind, doc_namespace: true)
- return unless namespace
-
- if namespace.is_a?(Array)
- out = RDoc::Markup::Document.new
- namespace.each do |m|
- begin
- RDocRIDriver.add_method(out, m)
- rescue RDoc::RI::Driver::NotFoundError
- end
- end
- RDocRIDriver.display(out)
- else
- begin
- RDocRIDriver.display_names([namespace])
- rescue RDoc::RI::Driver::NotFoundError
- end
- end
- }
-
# Set of available operators in Ruby
Operators = %w[% & * ** + - / < << <= <=> == === =~ > >= >> [] []= ^ ! != !~]
- def self.select_message(receiver, message, candidates, sep = ".")
+ def select_message(receiver, message, candidates, sep = ".")
candidates.grep(/^#{Regexp.quote(message)}/).collect do |e|
case e
when /^[a-zA-Z_]/
@@ -443,4 +458,20 @@ module IRB
end
end
end
+
+ module InputCompletor
+ class << self
+ private def regexp_completor
+ @regexp_completor ||= RegexpCompletor.new
+ end
+
+ def retrieve_completion_data(input, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding, doc_namespace: false)
+ regexp_completor.retrieve_completion_data(input, bind: bind, doc_namespace: doc_namespace)
+ end
+ end
+ CompletionProc = ->(target, preposing = nil, postposing = nil) {
+ regexp_completor.completion_candidates(preposing, target, postposing, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding)
+ }
+ end
+ deprecate_constant :InputCompletor
end
diff --git a/lib/irb/context.rb b/lib/irb/context.rb
index f398c6f75d..22e855f1ef 100644
--- a/lib/irb/context.rb
+++ b/lib/irb/context.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/context.rb - irb context
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -22,10 +22,11 @@ module IRB
# +other+:: uses this as InputMethod
def initialize(irb, workspace = nil, input_method = nil)
@irb = irb
+ @workspace_stack = []
if workspace
- @workspace = workspace
+ @workspace_stack << workspace
else
- @workspace = WorkSpace.new
+ @workspace_stack << WorkSpace.new
end
@thread = Thread.current
@@ -61,7 +62,7 @@ module IRB
@io = nil
self.inspect_mode = IRB.conf[:INSPECT_MODE]
- self.use_tracer = IRB.conf[:USE_TRACER] if IRB.conf[:USE_TRACER]
+ self.use_tracer = IRB.conf[:USE_TRACER]
self.use_loader = IRB.conf[:USE_LOADER] if IRB.conf[:USE_LOADER]
self.eval_history = IRB.conf[:EVAL_HISTORY] if IRB.conf[:EVAL_HISTORY]
@@ -77,7 +78,7 @@ module IRB
else
@irb_name = IRB.conf[:IRB_NAME]+"#"+IRB.JobManager.n_jobs.to_s
end
- @irb_path = "(" + @irb_name + ")"
+ self.irb_path = "(" + @irb_name + ")"
case input_method
when nil
@@ -86,14 +87,14 @@ module IRB
when nil
if STDIN.tty? && IRB.conf[:PROMPT_MODE] != :INF_RUBY && !use_singleline?
# Both of multiline mode and singleline mode aren't specified.
- @io = RelineInputMethod.new
+ @io = RelineInputMethod.new(build_completor)
else
@io = nil
end
when false
@io = nil
when true
- @io = RelineInputMethod.new
+ @io = RelineInputMethod.new(build_completor)
end
unless @io
case use_singleline?
@@ -121,16 +122,14 @@ module IRB
when '-'
@io = FileInputMethod.new($stdin)
@irb_name = '-'
- @irb_path = '-'
+ self.irb_path = '-'
when String
@io = FileInputMethod.new(input_method)
@irb_name = File.basename(input_method)
- @irb_path = input_method
+ self.irb_path = input_method
else
@io = input_method
end
- self.save_history = IRB.conf[:SAVE_HISTORY] if IRB.conf[:SAVE_HISTORY]
-
@extra_doc_dirs = IRB.conf[:EXTRA_DOC_DIRS]
@echo = IRB.conf[:ECHO]
@@ -148,18 +147,107 @@ module IRB
@newline_before_multiline_output = true
end
- @command_aliases = IRB.conf[:COMMAND_ALIASES]
+ @user_aliases = IRB.conf[:COMMAND_ALIASES].dup
+ @command_aliases = @user_aliases.merge(KEYWORD_ALIASES)
+ end
+
+ # because all input will eventually be evaluated as Ruby code,
+ # command names that conflict with Ruby keywords need special workaround
+ # we can remove them once we implemented a better command system for IRB
+ KEYWORD_ALIASES = {
+ :break => :irb_break,
+ :catch => :irb_catch,
+ :next => :irb_next,
+ }.freeze
+
+ private_constant :KEYWORD_ALIASES
+
+ def use_tracer=(val)
+ require_relative "ext/tracer" if val
+ IRB.conf[:USE_TRACER] = val
+ end
+
+ def eval_history=(val)
+ self.class.remove_method(__method__)
+ require_relative "ext/eval_history"
+ __send__(__method__, val)
+ end
+
+ def use_loader=(val)
+ self.class.remove_method(__method__)
+ require_relative "ext/use-loader"
+ __send__(__method__, val)
+ end
+
+ private def build_completor
+ completor_type = IRB.conf[:COMPLETOR]
+ case completor_type
+ when :regexp
+ return RegexpCompletor.new
+ when :type
+ completor = build_type_completor
+ return completor if completor
+ else
+ warn "Invalid value for IRB.conf[:COMPLETOR]: #{completor_type}"
+ end
+ # Fallback to RegexpCompletor
+ RegexpCompletor.new
+ end
+
+ private def build_type_completor
+ if RUBY_ENGINE == 'truffleruby'
+ # Avoid SyntaxError. truffleruby does not support endless method definition yet.
+ warn 'TypeCompletor is not supported on TruffleRuby yet'
+ return
+ end
+
+ begin
+ require 'repl_type_completor'
+ rescue LoadError => e
+ warn "TypeCompletor requires `gem repl_type_completor`: #{e.message}"
+ return
+ end
+
+ ReplTypeCompletor.preload_rbs
+ TypeCompletor.new(self)
+ end
+
+ def save_history=(val)
+ IRB.conf[:SAVE_HISTORY] = val
+ end
+
+ def save_history
+ IRB.conf[:SAVE_HISTORY]
+ end
+
+ # A copy of the default <code>IRB.conf[:HISTORY_FILE]</code>
+ def history_file
+ IRB.conf[:HISTORY_FILE]
+ end
+
+ # Set <code>IRB.conf[:HISTORY_FILE]</code> to the given +hist+.
+ def history_file=(hist)
+ IRB.conf[:HISTORY_FILE] = hist
+ end
+
+ # Workspace in the current context.
+ def workspace
+ @workspace_stack.last
+ end
+
+ # Replace the current workspace with the given +workspace+.
+ def replace_workspace(workspace)
+ @workspace_stack.pop
+ @workspace_stack.push(workspace)
end
# The top-level workspace, see WorkSpace#main
def main
- @workspace.main
+ workspace.main
end
# The toplevel workspace, see #home_workspace
attr_reader :workspace_home
- # WorkSpace in the current context.
- attr_accessor :workspace
# The current thread in this context.
attr_reader :thread
# The current input method.
@@ -180,9 +268,27 @@ module IRB
# Can be either name from <code>IRB.conf[:IRB_NAME]</code>, or the number of
# the current job set by JobManager, such as <code>irb#2</code>
attr_accessor :irb_name
- # Can be either the #irb_name surrounded by parenthesis, or the
- # +input_method+ passed to Context.new
- attr_accessor :irb_path
+
+ # Can be one of the following:
+ # - the #irb_name surrounded by parenthesis
+ # - the +input_method+ passed to Context.new
+ # - the file path of the current IRB context in a binding.irb session
+ attr_reader :irb_path
+
+ # Sets @irb_path to the given +path+ as well as @eval_path
+ # @eval_path is used for evaluating code in the context of IRB session
+ # It's the same as irb_path, but with the IRB name postfix
+ # This makes sure users can distinguish the methods defined in the IRB session
+ # from the methods defined in the current file's context, especially with binding.irb
+ def irb_path=(path)
+ @irb_path = path
+
+ if File.exist?(path)
+ @eval_path = "#{path}(#{IRB.conf[:IRB_NAME]})"
+ else
+ @eval_path = path
+ end
+ end
# Whether multiline editor mode is enabled or not.
#
@@ -203,18 +309,29 @@ module IRB
attr_reader :prompt_mode
# Standard IRB prompt.
#
- # See IRB@Customizing+the+IRB+Prompt for more information.
+ # See {Custom Prompts}[rdoc-ref:IRB@Custom+Prompts] for more information.
attr_accessor :prompt_i
# IRB prompt for continuated strings.
#
- # See IRB@Customizing+the+IRB+Prompt for more information.
+ # See {Custom Prompts}[rdoc-ref:IRB@Custom+Prompts] for more information.
attr_accessor :prompt_s
# IRB prompt for continuated statement. (e.g. immediately after an +if+)
#
- # See IRB@Customizing+the+IRB+Prompt for more information.
+ # See {Custom Prompts}[rdoc-ref:IRB@Custom+Prompts] for more information.
attr_accessor :prompt_c
- # See IRB@Customizing+the+IRB+Prompt for more information.
- attr_accessor :prompt_n
+
+ # TODO: Remove this when developing v2.0
+ def prompt_n
+ warn "IRB::Context#prompt_n is deprecated and will be removed in the next major release."
+ ""
+ end
+
+ # TODO: Remove this when developing v2.0
+ def prompt_n=(_)
+ warn "IRB::Context#prompt_n= is deprecated and will be removed in the next major release."
+ ""
+ end
+
# Can be either the default <code>IRB.conf[:AUTO_INDENT]</code>, or the
# mode set by #prompt_mode=
#
@@ -322,13 +439,13 @@ module IRB
# The default value is 16.
#
# Can also be set using the +--back-trace-limit+ command line option.
- #
- # See IRB@Command+line+options for more command line options.
attr_accessor :back_trace_limit
# User-defined IRB command aliases
attr_accessor :command_aliases
+ attr_accessor :with_debugger
+
# Alias for #use_multiline
alias use_multiline? use_multiline
# Alias for #use_singleline
@@ -372,9 +489,7 @@ module IRB
# StdioInputMethod or RelineInputMethod or ReadlineInputMethod, see #io
# for more information.
def prompting?
- verbose? || (STDIN.tty? && @io.kind_of?(StdioInputMethod) ||
- @io.kind_of?(RelineInputMethod) ||
- (defined?(ReadlineInputMethod) && @io.kind_of?(ReadlineInputMethod)))
+ verbose? || @io.prompting?
end
# The return value of the last statement evaluated.
@@ -384,19 +499,18 @@ module IRB
# to #last_value.
def set_last_value(value)
@last_value = value
- @workspace.local_variable_set :_, value
+ workspace.local_variable_set :_, value
end
# Sets the +mode+ of the prompt in this context.
#
- # See IRB@Customizing+the+IRB+Prompt for more information.
+ # See {Custom Prompts}[rdoc-ref:IRB@Custom+Prompts] for more information.
def prompt_mode=(mode)
@prompt_mode = mode
pconf = IRB.conf[:PROMPT][mode]
@prompt_i = pconf[:PROMPT_I]
@prompt_s = pconf[:PROMPT_S]
@prompt_c = pconf[:PROMPT_C]
- @prompt_n = pconf[:PROMPT_N]
@return_format = pconf[:RETURN]
@return_format = "%s\n" if @return_format == nil
if ai = pconf.include?(:AUTO_INDENT)
@@ -428,8 +542,6 @@ module IRB
#
# Can also be set using the +--inspect+ and +--noinspect+ command line
# options.
- #
- # See IRB@Command+line+options for more command line options.
def inspect_mode=(opt)
if i = Inspector::INSPECTORS[opt]
@@ -473,42 +585,51 @@ module IRB
@inspect_mode
end
- def evaluate(line, line_no, exception: nil) # :nodoc:
+ def evaluate(statement, line_no) # :nodoc:
@line_no = line_no
- if exception
- line_no -= 1
- line = "begin ::Kernel.raise _; rescue _.class\n#{line}\n""end"
- @workspace.local_variable_set(:_, exception)
- end
- # Transform a non-identifier alias (@, $) or keywords (next, break)
- command, args = line.split(/\s/, 2)
- if original = command_aliases[command.to_sym]
- line = line.gsub(/\A#{Regexp.escape(command)}/, original.to_s)
- command = original
+ case statement
+ when Statement::EmptyInput
+ return
+ when Statement::Expression
+ result = evaluate_expression(statement.code, line_no)
+ set_last_value(result)
+ when Statement::Command
+ statement.command_class.execute(self, statement.arg)
+ set_last_value(nil)
end
- # Hook command-specific transformation
- command_class = ExtendCommandBundle.load_command(command)
- if command_class&.respond_to?(:transform_args)
- line = "#{command} #{command_class.transform_args(args)}"
+ nil
+ end
+
+ def evaluate_expression(code, line_no) # :nodoc:
+ result = nil
+ if IRB.conf[:MEASURE] && IRB.conf[:MEASURE_CALLBACKS].empty?
+ IRB.set_measure_callback
end
- set_last_value(@workspace.evaluate(line, irb_path, line_no))
+ if IRB.conf[:MEASURE] && !IRB.conf[:MEASURE_CALLBACKS].empty?
+ last_proc = proc do
+ result = workspace.evaluate(code, @eval_path, line_no)
+ end
+ IRB.conf[:MEASURE_CALLBACKS].inject(last_proc) do |chain, item|
+ _name, callback, arg = item
+ proc do
+ callback.(self, code, line_no, arg) do
+ chain.call
+ end
+ end
+ end.call
+ else
+ result = workspace.evaluate(code, @eval_path, line_no)
+ end
+ result
end
def inspect_last_value # :nodoc:
@inspect_method.inspect_value(@last_value)
end
- alias __exit__ exit
- # Exits the current session, see IRB.irb_exit
- def exit(ret = 0)
- IRB.irb_exit(@irb, ret)
- rescue UncaughtThrowError
- super
- end
-
NOPRINTING_IVARS = ["@last_value"] # :nodoc:
NO_INSPECTING_IVARS = ["@irb", "@io"] # :nodoc:
IDNAME_IVARS = ["@prompt_mode"] # :nodoc:
@@ -539,17 +660,5 @@ module IRB
def local_variables # :nodoc:
workspace.binding.local_variables
end
-
- # Return true if it's aliased from the argument and it's not an identifier.
- def symbol_alias?(command)
- return nil if command.match?(/\A\w+\z/)
- command_aliases.key?(command.to_sym)
- end
-
- # Return true if the command supports transforming args
- def transform_args?(command)
- command = command_aliases.fetch(command.to_sym, command)
- ExtendCommandBundle.load_command(command)&.respond_to?(:transform_args)
- end
end
end
diff --git a/lib/irb/debug.rb b/lib/irb/debug.rb
new file mode 100644
index 0000000000..1ec2335a8e
--- /dev/null
+++ b/lib/irb/debug.rb
@@ -0,0 +1,130 @@
+# frozen_string_literal: true
+
+module IRB
+ module Debug
+ IRB_DIR = File.expand_path('..', __dir__)
+
+ class << self
+ def insert_debug_break(pre_cmds: nil, do_cmds: nil)
+ options = { oneshot: true, hook_call: false }
+
+ if pre_cmds || do_cmds
+ options[:command] = ['irb', pre_cmds, do_cmds]
+ end
+ if DEBUGGER__::LineBreakpoint.instance_method(:initialize).parameters.include?([:key, :skip_src])
+ options[:skip_src] = true
+ end
+
+ # To make debugger commands like `next` or `continue` work without asking
+ # the user to quit IRB after that, we need to exit IRB first and then hit
+ # a TracePoint on #debug_break.
+ file, lineno = IRB::Irb.instance_method(:debug_break).source_location
+ DEBUGGER__::SESSION.add_line_breakpoint(file, lineno + 1, **options)
+ end
+
+ def setup(irb)
+ # When debug session is not started at all
+ unless defined?(DEBUGGER__::SESSION)
+ begin
+ require "debug/session"
+ rescue LoadError # debug.gem is not written in Gemfile
+ return false unless load_bundled_debug_gem
+ end
+ DEBUGGER__::CONFIG.set_config
+ configure_irb_for_debugger(irb)
+
+ DEBUGGER__.initialize_session{ IRB::Debug::UI.new(irb) }
+ end
+
+ # When debug session was previously started but not by IRB
+ if defined?(DEBUGGER__::SESSION) && !irb.context.with_debugger
+ configure_irb_for_debugger(irb)
+ DEBUGGER__::SESSION.reset_ui(IRB::Debug::UI.new(irb))
+ end
+
+ # Apply patches to debug gem so it skips IRB frames
+ unless DEBUGGER__.respond_to?(:capture_frames_without_irb)
+ DEBUGGER__.singleton_class.send(:alias_method, :capture_frames_without_irb, :capture_frames)
+
+ def DEBUGGER__.capture_frames(*args)
+ frames = capture_frames_without_irb(*args)
+ frames.reject! do |frame|
+ frame.realpath&.start_with?(IRB_DIR) || frame.path == "<internal:prelude>"
+ end
+ frames
+ end
+
+ DEBUGGER__::ThreadClient.prepend(SkipPathHelperForIRB)
+ end
+
+ if !@output_modifier_defined && !DEBUGGER__::CONFIG[:no_hint]
+ irb_output_modifier_proc = Reline.output_modifier_proc
+
+ Reline.output_modifier_proc = proc do |output, complete:|
+ unless output.strip.empty?
+ cmd = output.split(/\s/, 2).first
+
+ if !complete && DEBUGGER__.commands.key?(cmd)
+ output = output.sub(/\n$/, " # debug command\n")
+ end
+ end
+
+ irb_output_modifier_proc.call(output, complete: complete)
+ end
+
+ @output_modifier_defined = true
+ end
+
+ true
+ end
+
+ private
+
+ def configure_irb_for_debugger(irb)
+ require 'irb/debug/ui'
+ IRB.instance_variable_set(:@debugger_irb, irb)
+ irb.context.with_debugger = true
+ irb.context.irb_name += ":rdbg"
+ end
+
+ module SkipPathHelperForIRB
+ def skip_internal_path?(path)
+ # The latter can be removed once https://github.com/ruby/debug/issues/866 is resolved
+ super || path.match?(IRB_DIR) || path.match?('<internal:prelude>')
+ end
+ end
+
+ # This is used when debug.gem is not written in Gemfile. Even if it's not
+ # installed by `bundle install`, debug.gem is installed by default because
+ # it's a bundled gem. This method tries to activate and load that.
+ def load_bundled_debug_gem
+ # Discover latest debug.gem under GEM_PATH
+ debug_gem = Gem.paths.path.flat_map { |path| Dir.glob("#{path}/gems/debug-*") }.select do |path|
+ File.basename(path).match?(/\Adebug-\d+\.\d+\.\d+(\w+)?\z/)
+ end.sort_by do |path|
+ Gem::Version.new(File.basename(path).delete_prefix('debug-'))
+ end.last
+ return false unless debug_gem
+
+ # Discover debug/debug.so under extensions for Ruby 3.2+
+ ext_name = "/debug/debug.#{RbConfig::CONFIG['DLEXT']}"
+ ext_path = Gem.paths.path.flat_map do |path|
+ Dir.glob("#{path}/extensions/**/#{File.basename(debug_gem)}#{ext_name}")
+ end.first
+
+ # Attempt to forcibly load the bundled gem
+ if ext_path
+ $LOAD_PATH << ext_path.delete_suffix(ext_name)
+ end
+ $LOAD_PATH << "#{debug_gem}/lib"
+ begin
+ require "debug/session"
+ puts "Loaded #{File.basename(debug_gem)}"
+ true
+ rescue LoadError
+ false
+ end
+ end
+ end
+ end
+end
diff --git a/lib/irb/debug/ui.rb b/lib/irb/debug/ui.rb
new file mode 100644
index 0000000000..307097b8c9
--- /dev/null
+++ b/lib/irb/debug/ui.rb
@@ -0,0 +1,103 @@
+require 'io/console/size'
+require 'debug/console'
+
+module IRB
+ module Debug
+ class UI < DEBUGGER__::UI_Base
+ def initialize(irb)
+ @irb = irb
+ end
+
+ def remote?
+ false
+ end
+
+ def activate session, on_fork: false
+ end
+
+ def deactivate
+ end
+
+ def width
+ if (w = IO.console_size[1]) == 0 # for tests PTY
+ 80
+ else
+ w
+ end
+ end
+
+ def quit n
+ yield
+ exit n
+ end
+
+ def ask prompt
+ setup_interrupt do
+ print prompt
+ ($stdin.gets || '').strip
+ end
+ end
+
+ def puts str = nil
+ case str
+ when Array
+ str.each{|line|
+ $stdout.puts line.chomp
+ }
+ when String
+ str.each_line{|line|
+ $stdout.puts line.chomp
+ }
+ when nil
+ $stdout.puts
+ end
+ end
+
+ def readline _
+ setup_interrupt do
+ tc = DEBUGGER__::SESSION.instance_variable_get(:@tc)
+ cmd = @irb.debug_readline(tc.current_frame.binding || TOPLEVEL_BINDING)
+
+ case cmd
+ when nil # when user types C-d
+ "continue"
+ else
+ cmd
+ end
+ end
+ end
+
+ def setup_interrupt
+ DEBUGGER__::SESSION.intercept_trap_sigint false do
+ current_thread = Thread.current # should be session_server thread
+
+ prev_handler = trap(:INT){
+ current_thread.raise Interrupt
+ }
+
+ yield
+ ensure
+ trap(:INT, prev_handler)
+ end
+ end
+
+ def after_fork_parent
+ parent_pid = Process.pid
+
+ at_exit{
+ DEBUGGER__::SESSION.intercept_trap_sigint_end
+ trap(:SIGINT, :IGNORE)
+
+ if Process.pid == parent_pid
+ # only check child process from its parent
+ begin
+ # wait for all child processes to keep terminal
+ Process.waitpid
+ rescue Errno::ESRCH, Errno::ECHILD
+ end
+ end
+ }
+ end
+ end
+ end
+end
diff --git a/lib/irb/default_commands.rb b/lib/irb/default_commands.rb
new file mode 100644
index 0000000000..1bbc68efa7
--- /dev/null
+++ b/lib/irb/default_commands.rb
@@ -0,0 +1,260 @@
+# frozen_string_literal: true
+
+require_relative "command"
+require_relative "command/internal_helpers"
+require_relative "command/context"
+require_relative "command/exit"
+require_relative "command/force_exit"
+require_relative "command/chws"
+require_relative "command/pushws"
+require_relative "command/subirb"
+require_relative "command/load"
+require_relative "command/debug"
+require_relative "command/edit"
+require_relative "command/break"
+require_relative "command/catch"
+require_relative "command/next"
+require_relative "command/delete"
+require_relative "command/step"
+require_relative "command/continue"
+require_relative "command/finish"
+require_relative "command/backtrace"
+require_relative "command/info"
+require_relative "command/help"
+require_relative "command/show_doc"
+require_relative "command/irb_info"
+require_relative "command/ls"
+require_relative "command/measure"
+require_relative "command/show_source"
+require_relative "command/whereami"
+require_relative "command/history"
+
+module IRB
+ module Command
+ NO_OVERRIDE = 0
+ OVERRIDE_PRIVATE_ONLY = 0x01
+ OVERRIDE_ALL = 0x02
+
+ class << self
+ # This API is for IRB's internal use only and may change at any time.
+ # Please do NOT use it.
+ def _register_with_aliases(name, command_class, *aliases)
+ @commands[name.to_sym] = [command_class, aliases]
+ end
+
+ def all_commands_info
+ user_aliases = IRB.CurrentContext.command_aliases.each_with_object({}) do |(alias_name, target), result|
+ result[target] ||= []
+ result[target] << alias_name
+ end
+
+ commands.map do |command_name, (command_class, aliases)|
+ aliases = aliases.map { |a| a.first }
+
+ if additional_aliases = user_aliases[command_name]
+ aliases += additional_aliases
+ end
+
+ display_name = aliases.shift || command_name
+ {
+ display_name: display_name,
+ description: command_class.description,
+ category: command_class.category
+ }
+ end
+ end
+
+ def command_override_policies
+ @@command_override_policies ||= commands.flat_map do |cmd_name, (cmd_class, aliases)|
+ [[cmd_name, OVERRIDE_ALL]] + aliases
+ end.to_h
+ end
+
+ def execute_as_command?(name, public_method:, private_method:)
+ case command_override_policies[name]
+ when OVERRIDE_ALL
+ true
+ when OVERRIDE_PRIVATE_ONLY
+ !public_method
+ when NO_OVERRIDE
+ !public_method && !private_method
+ end
+ end
+
+ def command_names
+ command_override_policies.keys.map(&:to_s)
+ end
+
+ # Convert a command name to its implementation class if such command exists
+ def load_command(command)
+ command = command.to_sym
+ commands.each do |command_name, (command_class, aliases)|
+ if command_name == command || aliases.any? { |alias_name, _| alias_name == command }
+ return command_class
+ end
+ end
+ nil
+ end
+ end
+
+ _register_with_aliases(:irb_context, Command::Context,
+ [:context, NO_OVERRIDE]
+ )
+
+ _register_with_aliases(:irb_exit, Command::Exit,
+ [:exit, OVERRIDE_PRIVATE_ONLY],
+ [:quit, OVERRIDE_PRIVATE_ONLY],
+ [:irb_quit, OVERRIDE_PRIVATE_ONLY]
+ )
+
+ _register_with_aliases(:irb_exit!, Command::ForceExit,
+ [:exit!, OVERRIDE_PRIVATE_ONLY]
+ )
+
+ _register_with_aliases(:irb_current_working_workspace, Command::CurrentWorkingWorkspace,
+ [:cwws, NO_OVERRIDE],
+ [:pwws, NO_OVERRIDE],
+ [:irb_print_working_workspace, OVERRIDE_ALL],
+ [:irb_cwws, OVERRIDE_ALL],
+ [:irb_pwws, OVERRIDE_ALL],
+ [:irb_current_working_binding, OVERRIDE_ALL],
+ [:irb_print_working_binding, OVERRIDE_ALL],
+ [:irb_cwb, OVERRIDE_ALL],
+ [:irb_pwb, OVERRIDE_ALL],
+ )
+
+ _register_with_aliases(:irb_change_workspace, Command::ChangeWorkspace,
+ [:chws, NO_OVERRIDE],
+ [:cws, NO_OVERRIDE],
+ [:irb_chws, OVERRIDE_ALL],
+ [:irb_cws, OVERRIDE_ALL],
+ [:irb_change_binding, OVERRIDE_ALL],
+ [:irb_cb, OVERRIDE_ALL],
+ [:cb, NO_OVERRIDE],
+ )
+
+ _register_with_aliases(:irb_workspaces, Command::Workspaces,
+ [:workspaces, NO_OVERRIDE],
+ [:irb_bindings, OVERRIDE_ALL],
+ [:bindings, NO_OVERRIDE],
+ )
+
+ _register_with_aliases(:irb_push_workspace, Command::PushWorkspace,
+ [:pushws, NO_OVERRIDE],
+ [:irb_pushws, OVERRIDE_ALL],
+ [:irb_push_binding, OVERRIDE_ALL],
+ [:irb_pushb, OVERRIDE_ALL],
+ [:pushb, NO_OVERRIDE],
+ )
+
+ _register_with_aliases(:irb_pop_workspace, Command::PopWorkspace,
+ [:popws, NO_OVERRIDE],
+ [:irb_popws, OVERRIDE_ALL],
+ [:irb_pop_binding, OVERRIDE_ALL],
+ [:irb_popb, OVERRIDE_ALL],
+ [:popb, NO_OVERRIDE],
+ )
+
+ _register_with_aliases(:irb_load, Command::Load)
+ _register_with_aliases(:irb_require, Command::Require)
+ _register_with_aliases(:irb_source, Command::Source,
+ [:source, NO_OVERRIDE]
+ )
+
+ _register_with_aliases(:irb, Command::IrbCommand)
+ _register_with_aliases(:irb_jobs, Command::Jobs,
+ [:jobs, NO_OVERRIDE]
+ )
+ _register_with_aliases(:irb_fg, Command::Foreground,
+ [:fg, NO_OVERRIDE]
+ )
+ _register_with_aliases(:irb_kill, Command::Kill,
+ [:kill, OVERRIDE_PRIVATE_ONLY]
+ )
+
+ _register_with_aliases(:irb_debug, Command::Debug,
+ [:debug, NO_OVERRIDE]
+ )
+ _register_with_aliases(:irb_edit, Command::Edit,
+ [:edit, NO_OVERRIDE]
+ )
+
+ _register_with_aliases(:irb_break, Command::Break)
+ _register_with_aliases(:irb_catch, Command::Catch)
+ _register_with_aliases(:irb_next, Command::Next)
+ _register_with_aliases(:irb_delete, Command::Delete,
+ [:delete, NO_OVERRIDE]
+ )
+
+ _register_with_aliases(:irb_step, Command::Step,
+ [:step, NO_OVERRIDE]
+ )
+ _register_with_aliases(:irb_continue, Command::Continue,
+ [:continue, NO_OVERRIDE]
+ )
+ _register_with_aliases(:irb_finish, Command::Finish,
+ [:finish, NO_OVERRIDE]
+ )
+ _register_with_aliases(:irb_backtrace, Command::Backtrace,
+ [:backtrace, NO_OVERRIDE],
+ [:bt, NO_OVERRIDE]
+ )
+
+ _register_with_aliases(:irb_debug_info, Command::Info,
+ [:info, NO_OVERRIDE]
+ )
+
+ _register_with_aliases(:irb_help, Command::Help,
+ [:help, NO_OVERRIDE],
+ [:show_cmds, NO_OVERRIDE]
+ )
+
+ _register_with_aliases(:irb_show_doc, Command::ShowDoc,
+ [:show_doc, NO_OVERRIDE]
+ )
+
+ _register_with_aliases(:irb_info, Command::IrbInfo)
+
+ _register_with_aliases(:irb_ls, Command::Ls,
+ [:ls, NO_OVERRIDE]
+ )
+
+ _register_with_aliases(:irb_measure, Command::Measure,
+ [:measure, NO_OVERRIDE]
+ )
+
+ _register_with_aliases(:irb_show_source, Command::ShowSource,
+ [:show_source, NO_OVERRIDE]
+ )
+
+ _register_with_aliases(:irb_whereami, Command::Whereami,
+ [:whereami, NO_OVERRIDE]
+ )
+
+ _register_with_aliases(:irb_history, Command::History,
+ [:history, NO_OVERRIDE],
+ [:hist, NO_OVERRIDE]
+ )
+ end
+
+ ExtendCommand = Command
+
+ # For backward compatibility, we need to keep this module:
+ # - As a container of helper methods
+ # - As a place to register commands with the deprecated def_extend_command method
+ module ExtendCommandBundle
+ # For backward compatibility
+ NO_OVERRIDE = Command::NO_OVERRIDE
+ OVERRIDE_PRIVATE_ONLY = Command::OVERRIDE_PRIVATE_ONLY
+ OVERRIDE_ALL = Command::OVERRIDE_ALL
+
+ # Deprecated. Doesn't have any effect.
+ @EXTEND_COMMANDS = []
+
+ # Drepcated. Use Command.regiser instead.
+ def self.def_extend_command(cmd_name, cmd_class, _, *aliases)
+ Command._register_with_aliases(cmd_name, cmd_class, *aliases)
+ Command.class_variable_set(:@@command_override_policies, nil)
+ end
+ end
+end
diff --git a/lib/irb/easter-egg.rb b/lib/irb/easter-egg.rb
index 3e79692de9..adc0834d55 100644
--- a/lib/irb/easter-egg.rb
+++ b/lib/irb/easter-egg.rb
@@ -98,18 +98,26 @@ module IRB
end
end
+ private def easter_egg_logo(type)
+ @easter_egg_logos ||= File.read(File.join(__dir__, 'ruby_logo.aa'), encoding: 'UTF-8:UTF-8')
+ .split(/TYPE: ([A-Z]+)\n/)[1..]
+ .each_slice(2)
+ .to_h
+ @easter_egg_logos[type.to_s.upcase]
+ end
+
private def easter_egg(type = nil)
type ||= [:logo, :dancing].sample
case type
when :logo
- File.open(File.join(__dir__, 'ruby_logo.aa')) do |f|
- require "rdoc"
- RDoc::RI::Driver.new.page do |io|
- IO.copy_stream(f, io)
- end
+ require "rdoc"
+ RDoc::RI::Driver.new.page do |io|
+ io.write easter_egg_logo(:large)
end
when :dancing
- begin
+ STDOUT.cooked do
+ interrupted = false
+ prev_trap = trap("SIGINT") { interrupted = true }
canvas = Canvas.new(Reline.get_screen_size)
Reline::IOGate.set_winch_handler do
canvas = Canvas.new(Reline.get_screen_size)
@@ -125,10 +133,12 @@ module IRB
buff[0, 20] = "\e[0mPress Ctrl+C to stop\e[31m\e[1m"
print "\e[H" + buff
sleep 0.05
+ break if interrupted
end
rescue Interrupt
ensure
print "\e[0m\e[?1049l"
+ trap("SIGINT", prev_trap)
end
end
end
diff --git a/lib/irb/ext/change-ws.rb b/lib/irb/ext/change-ws.rb
index c0f810a4c8..60e8afe31f 100644
--- a/lib/irb/ext/change-ws.rb
+++ b/lib/irb/ext/change-ws.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/ext/cb.rb -
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -12,7 +12,7 @@ module IRB # :nodoc:
if defined? @home_workspace
@home_workspace
else
- @home_workspace = @workspace
+ @home_workspace = workspace
end
end
@@ -25,15 +25,13 @@ module IRB # :nodoc:
# See IRB::WorkSpace.new for more information.
def change_workspace(*_main)
if _main.empty?
- @workspace = home_workspace
+ replace_workspace(home_workspace)
return main
end
- @workspace = WorkSpace.new(_main[0])
-
- if !(class<<main;ancestors;end).include?(ExtendCommandBundle)
- main.extend ExtendCommandBundle
- end
+ workspace = WorkSpace.new(_main[0])
+ replace_workspace(workspace)
+ workspace.load_helper_methods_to_main
end
end
end
diff --git a/lib/irb/ext/eval_history.rb b/lib/irb/ext/eval_history.rb
new file mode 100644
index 0000000000..6c21ff00ee
--- /dev/null
+++ b/lib/irb/ext/eval_history.rb
@@ -0,0 +1,149 @@
+# frozen_string_literal: true
+#
+# history.rb -
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+
+module IRB # :nodoc:
+
+ class Context
+
+ NOPRINTING_IVARS.push "@eval_history_values"
+
+ # See #set_last_value
+ alias _set_last_value set_last_value
+
+ def set_last_value(value)
+ _set_last_value(value)
+
+ if defined?(@eval_history) && @eval_history
+ @eval_history_values.push @line_no, @last_value
+ workspace.evaluate "__ = IRB.CurrentContext.instance_eval{@eval_history_values}"
+ end
+
+ @last_value
+ end
+
+ remove_method :eval_history= if method_defined?(:eval_history=)
+ # The command result history limit. This method is not available until
+ # #eval_history= was called with non-nil value (directly or via
+ # setting <code>IRB.conf[:EVAL_HISTORY]</code> in <code>.irbrc</code>).
+ attr_reader :eval_history
+ # Sets command result history limit. Default value is set from
+ # <code>IRB.conf[:EVAL_HISTORY]</code>.
+ #
+ # +no+ is an Integer or +nil+.
+ #
+ # Returns +no+ of history items if greater than 0.
+ #
+ # If +no+ is 0, the number of history items is unlimited.
+ #
+ # If +no+ is +nil+, execution result history isn't used (default).
+ #
+ # EvalHistory values are available via <code>__</code> variable, see
+ # IRB::EvalHistory.
+ def eval_history=(no)
+ if no
+ if defined?(@eval_history) && @eval_history
+ @eval_history_values.size(no)
+ else
+ @eval_history_values = EvalHistory.new(no)
+ IRB.conf[:__TMP__EHV__] = @eval_history_values
+ workspace.evaluate("__ = IRB.conf[:__TMP__EHV__]")
+ IRB.conf.delete(:__TMP_EHV__)
+ end
+ else
+ @eval_history_values = nil
+ end
+ @eval_history = no
+ end
+ end
+
+ # Represents history of results of previously evaluated commands.
+ #
+ # Available via <code>__</code> variable, only if <code>IRB.conf[:EVAL_HISTORY]</code>
+ # or <code>IRB::CurrentContext().eval_history</code> is non-nil integer value
+ # (by default it is +nil+).
+ #
+ # Example (in `irb`):
+ #
+ # # Initialize history
+ # IRB::CurrentContext().eval_history = 10
+ # # => 10
+ #
+ # # Perform some commands...
+ # 1 + 2
+ # # => 3
+ # puts 'x'
+ # # x
+ # # => nil
+ # raise RuntimeError
+ # # ...error raised
+ #
+ # # Inspect history (format is "<item number> <evaluated value>":
+ # __
+ # # => 1 10
+ # # 2 3
+ # # 3 nil
+ #
+ # __[1]
+ # # => 10
+ #
+ class EvalHistory
+
+ def initialize(size = 16) # :nodoc:
+ @size = size
+ @contents = []
+ end
+
+ def size(size) # :nodoc:
+ if size != 0 && size < @size
+ @contents = @contents[@size - size .. @size]
+ end
+ @size = size
+ end
+
+ # Get one item of the content (both positive and negative indexes work).
+ def [](idx)
+ begin
+ if idx >= 0
+ @contents.find{|no, val| no == idx}[1]
+ else
+ @contents[idx][1]
+ end
+ rescue NameError
+ nil
+ end
+ end
+
+ def push(no, val) # :nodoc:
+ @contents.push [no, val]
+ @contents.shift if @size != 0 && @contents.size > @size
+ end
+
+ alias real_inspect inspect
+
+ def inspect # :nodoc:
+ if @contents.empty?
+ return real_inspect
+ end
+
+ unless (last = @contents.pop)[1].equal?(self)
+ @contents.push last
+ last = nil
+ end
+ str = @contents.collect{|no, val|
+ if val.equal?(self)
+ "#{no} ...self-history..."
+ else
+ "#{no} #{val.inspect}"
+ end
+ }.join("\n")
+ if str == ""
+ str = "Empty."
+ end
+ @contents.push last if last
+ str
+ end
+ end
+end
diff --git a/lib/irb/ext/history.rb b/lib/irb/ext/history.rb
deleted file mode 100644
index 5243504efa..0000000000
--- a/lib/irb/ext/history.rb
+++ /dev/null
@@ -1,149 +0,0 @@
-# frozen_string_literal: false
-#
-# history.rb -
-# by Keiju ISHITSUKA(keiju@ruby-lang.org)
-#
-
-module IRB # :nodoc:
-
- class Context
-
- NOPRINTING_IVARS.push "@eval_history_values"
-
- # See #set_last_value
- alias _set_last_value set_last_value
-
- def set_last_value(value)
- _set_last_value(value)
-
- if defined?(@eval_history) && @eval_history
- @eval_history_values.push @line_no, @last_value
- @workspace.evaluate "__ = IRB.CurrentContext.instance_eval{@eval_history_values}"
- end
-
- @last_value
- end
-
- remove_method :eval_history= if method_defined?(:eval_history=)
- # The command result history limit. This method is not available until
- # #eval_history= was called with non-nil value (directly or via
- # setting <code>IRB.conf[:EVAL_HISTORY]</code> in <code>.irbrc</code>).
- attr_reader :eval_history
- # Sets command result history limit. Default value is set from
- # <code>IRB.conf[:EVAL_HISTORY]</code>.
- #
- # +no+ is an Integer or +nil+.
- #
- # Returns +no+ of history items if greater than 0.
- #
- # If +no+ is 0, the number of history items is unlimited.
- #
- # If +no+ is +nil+, execution result history isn't used (default).
- #
- # History values are available via <code>__</code> variable, see
- # IRB::History.
- def eval_history=(no)
- if no
- if defined?(@eval_history) && @eval_history
- @eval_history_values.size(no)
- else
- @eval_history_values = History.new(no)
- IRB.conf[:__TMP__EHV__] = @eval_history_values
- @workspace.evaluate("__ = IRB.conf[:__TMP__EHV__]")
- IRB.conf.delete(:__TMP_EHV__)
- end
- else
- @eval_history_values = nil
- end
- @eval_history = no
- end
- end
-
- # Represents history of results of previously evaluated commands.
- #
- # Available via <code>__</code> variable, only if <code>IRB.conf[:EVAL_HISTORY]</code>
- # or <code>IRB::CurrentContext().eval_history</code> is non-nil integer value
- # (by default it is +nil+).
- #
- # Example (in `irb`):
- #
- # # Initialize history
- # IRB::CurrentContext().eval_history = 10
- # # => 10
- #
- # # Perform some commands...
- # 1 + 2
- # # => 3
- # puts 'x'
- # # x
- # # => nil
- # raise RuntimeError
- # # ...error raised
- #
- # # Inspect history (format is "<item number> <evaluated value>":
- # __
- # # => 1 10
- # # 2 3
- # # 3 nil
- #
- # __[1]
- # # => 10
- #
- class History
-
- def initialize(size = 16) # :nodoc:
- @size = size
- @contents = []
- end
-
- def size(size) # :nodoc:
- if size != 0 && size < @size
- @contents = @contents[@size - size .. @size]
- end
- @size = size
- end
-
- # Get one item of the content (both positive and negative indexes work).
- def [](idx)
- begin
- if idx >= 0
- @contents.find{|no, val| no == idx}[1]
- else
- @contents[idx][1]
- end
- rescue NameError
- nil
- end
- end
-
- def push(no, val) # :nodoc:
- @contents.push [no, val]
- @contents.shift if @size != 0 && @contents.size > @size
- end
-
- alias real_inspect inspect
-
- def inspect # :nodoc:
- if @contents.empty?
- return real_inspect
- end
-
- unless (last = @contents.pop)[1].equal?(self)
- @contents.push last
- last = nil
- end
- str = @contents.collect{|no, val|
- if val.equal?(self)
- "#{no} ...self-history..."
- else
- "#{no} #{val.inspect}"
- end
- }.join("\n")
- if str == ""
- str = "Empty."
- end
- @contents.push last if last
- str
- end
- end
-end
diff --git a/lib/irb/ext/loader.rb b/lib/irb/ext/loader.rb
index d65695df3b..df5aaa8e5a 100644
--- a/lib/irb/ext/loader.rb
+++ b/lib/irb/ext/loader.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# loader.rb -
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -98,13 +98,13 @@ module IRB # :nodoc:
def old # :nodoc:
back_io = @io
- back_path = @irb_path
+ back_path = irb_path
back_name = @irb_name
back_scanner = @irb.scanner
begin
@io = FileInputMethod.new(path)
@irb_name = File.basename(path)
- @irb_path = path
+ self.irb_path = path
@irb.signal_status(:IN_LOAD) do
if back_io.kind_of?(FileInputMethod)
@irb.eval_input
@@ -119,7 +119,7 @@ module IRB # :nodoc:
ensure
@io = back_io
@irb_name = back_name
- @irb_path = back_path
+ self.irb_path = back_path
@irb.scanner = back_scanner
end
end
diff --git a/lib/irb/ext/multi-irb.rb b/lib/irb/ext/multi-irb.rb
index 1c20489137..9f234f0cdc 100644
--- a/lib/irb/ext/multi-irb.rb
+++ b/lib/irb/ext/multi-irb.rb
@@ -1,11 +1,11 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/multi-irb.rb - multiple irb module
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
#
module IRB
- class JobManager
+ class JobManager # :nodoc:
# Creates a new JobManager object
def initialize
@@ -166,12 +166,12 @@ module IRB
@JobManager = JobManager.new
# The current JobManager in the session
- def IRB.JobManager
+ def IRB.JobManager # :nodoc:
@JobManager
end
# The current Context in this session
- def IRB.CurrentContext
+ def IRB.CurrentContext # :nodoc:
IRB.JobManager.irb(Thread.current).context
end
@@ -179,7 +179,7 @@ module IRB
#
# The optional +file+ argument is given to Context.new, along with the
# workspace created with the remaining arguments, see WorkSpace.new
- def IRB.irb(file = nil, *main)
+ def IRB.irb(file = nil, *main) # :nodoc:
workspace = WorkSpace.new(*main)
parent_thread = Thread.current
Thread.start do
diff --git a/lib/irb/ext/save-history.rb b/lib/irb/ext/save-history.rb
deleted file mode 100644
index b1987ea646..0000000000
--- a/lib/irb/ext/save-history.rb
+++ /dev/null
@@ -1,125 +0,0 @@
-# frozen_string_literal: false
-#
-# save-history.rb -
-# by Keiju ISHITSUKA(keiju@ruby-lang.org)
-#
-
-module IRB
- module HistorySavingAbility # :nodoc:
- end
-
- class Context
- def init_save_history# :nodoc:
- unless (class<<@io;self;end).include?(HistorySavingAbility)
- @io.extend(HistorySavingAbility)
- end
- end
-
- # A copy of the default <code>IRB.conf[:SAVE_HISTORY]</code>
- def save_history
- IRB.conf[:SAVE_HISTORY]
- end
-
- remove_method(:save_history=) if method_defined?(:save_history=)
- # Sets <code>IRB.conf[:SAVE_HISTORY]</code> to the given +val+ and calls
- # #init_save_history with this context.
- #
- # Will store the number of +val+ entries of history in the #history_file
- #
- # Add the following to your +.irbrc+ to change the number of history
- # entries stored to 1000:
- #
- # IRB.conf[:SAVE_HISTORY] = 1000
- def save_history=(val)
- IRB.conf[:SAVE_HISTORY] = val
- if val
- main_context = IRB.conf[:MAIN_CONTEXT]
- main_context = self unless main_context
- main_context.init_save_history
- end
- end
-
- # A copy of the default <code>IRB.conf[:HISTORY_FILE]</code>
- def history_file
- IRB.conf[:HISTORY_FILE]
- end
-
- # Set <code>IRB.conf[:HISTORY_FILE]</code> to the given +hist+.
- def history_file=(hist)
- IRB.conf[:HISTORY_FILE] = hist
- end
- end
-
- module HistorySavingAbility # :nodoc:
- def HistorySavingAbility.extended(obj)
- IRB.conf[:AT_EXIT].push proc{obj.save_history}
- obj.load_history
- obj
- end
-
- def load_history
- return unless self.class.const_defined?(:HISTORY)
- history = self.class::HISTORY
- if history_file = IRB.conf[:HISTORY_FILE]
- history_file = File.expand_path(history_file)
- end
- history_file = IRB.rc_file("_history") unless history_file
- if File.exist?(history_file)
- File.open(history_file, "r:#{IRB.conf[:LC_MESSAGES].encoding}") do |f|
- f.each { |l|
- l = l.chomp
- if self.class == RelineInputMethod and history.last&.end_with?("\\")
- history.last.delete_suffix!("\\")
- history.last << "\n" << l
- else
- history << l
- end
- }
- end
- @loaded_history_lines = history.size
- @loaded_history_mtime = File.mtime(history_file)
- end
- end
-
- def save_history
- return unless self.class.const_defined?(:HISTORY)
- history = self.class::HISTORY
- if num = IRB.conf[:SAVE_HISTORY] and (num = num.to_i) != 0
- if history_file = IRB.conf[:HISTORY_FILE]
- history_file = File.expand_path(history_file)
- end
- history_file = IRB.rc_file("_history") unless history_file
-
- # Change the permission of a file that already exists[BUG #7694]
- begin
- if File.stat(history_file).mode & 066 != 0
- File.chmod(0600, history_file)
- end
- rescue Errno::ENOENT
- rescue Errno::EPERM
- return
- rescue
- raise
- end
-
- if File.exist?(history_file) &&
- File.mtime(history_file) != @loaded_history_mtime
- history = history[@loaded_history_lines..-1] if @loaded_history_lines
- append_history = true
- end
-
- File.open(history_file, (append_history ? 'a' : 'w'), 0o600, encoding: IRB.conf[:LC_MESSAGES]&.encoding) do |f|
- hist = history.map{ |l| l.split("\n").join("\\\n") }
- unless append_history
- begin
- hist = hist.last(num) if hist.size > num and num > 0
- rescue RangeError # bignum too big to convert into `long'
- # Do nothing because the bignum should be treated as inifinity
- end
- end
- f.puts(hist)
- end
- end
- end
- end
-end
diff --git a/lib/irb/ext/tracer.rb b/lib/irb/ext/tracer.rb
index 3eaeb70ef2..fd6daa88ae 100644
--- a/lib/irb/ext/tracer.rb
+++ b/lib/irb/ext/tracer.rb
@@ -1,78 +1,39 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/lib/tracer.rb -
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
#
-
+# Loading the gem "tracer" will cause it to extend IRB commands with:
+# https://github.com/ruby/tracer/blob/v0.2.2/lib/tracer/irb.rb
begin
require "tracer"
rescue LoadError
$stderr.puts "Tracer extension of IRB is enabled but tracer gem wasn't found."
- module IRB
- class Context
- def use_tracer=(opt)
- # do nothing
- end
- end
- end
return # This is about to disable loading below
end
module IRB
+ class CallTracer < ::CallTracer
+ IRB_DIR = File.expand_path('../..', __dir__)
- # initialize tracing function
- def IRB.initialize_tracer
- Tracer.verbose = false
- Tracer.add_filter {
- |event, file, line, id, binding, *rests|
- /^#{Regexp.quote(@CONF[:IRB_LIB_PATH])}/ !~ file and
- File::basename(file) != "irb.rb"
- }
- end
-
- class Context
- # Whether Tracer is used when evaluating statements in this context.
- #
- # See +lib/tracer.rb+ for more information.
- attr_reader :use_tracer
- alias use_tracer? use_tracer
-
- # Sets whether or not to use the Tracer library when evaluating statements
- # in this context.
- #
- # See +lib/tracer.rb+ for more information.
- def use_tracer=(opt)
- if opt
- Tracer.set_get_line_procs(@irb_path) {
- |line_no, *rests|
- @io.line(line_no)
- }
- elsif !opt && @use_tracer
- Tracer.off
- end
- @use_tracer=opt
+ def skip?(tp)
+ super || tp.path.match?(IRB_DIR) || tp.path.match?('<internal:prelude>')
end
end
-
class WorkSpace
alias __evaluate__ evaluate
# Evaluate the context of this workspace and use the Tracer library to
# output the exact lines of code are being executed in chronological order.
#
- # See +lib/tracer.rb+ for more information.
- def evaluate(context, statements, file = nil, line = nil)
- if context.use_tracer? && file != nil && line != nil
- Tracer.on
- begin
+ # See https://github.com/ruby/tracer for more information.
+ def evaluate(statements, file = __FILE__, line = __LINE__)
+ if IRB.conf[:USE_TRACER] == true
+ CallTracer.new(colorize: Color.colorable?).start do
__evaluate__(statements, file, line)
- ensure
- Tracer.off
end
else
- __evaluate__(statements, file || __FILE__, line || __LINE__)
+ __evaluate__(statements, file, line)
end
end
end
-
- IRB.initialize_tracer
end
diff --git a/lib/irb/ext/use-loader.rb b/lib/irb/ext/use-loader.rb
index d0b8c2d4f4..c8a3ea1fe8 100644
--- a/lib/irb/ext/use-loader.rb
+++ b/lib/irb/ext/use-loader.rb
@@ -1,10 +1,10 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# use-loader.rb -
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
#
-require_relative "../cmd/load"
+require_relative "../command/load"
require_relative "loader"
class Object
@@ -17,12 +17,12 @@ module IRB
remove_method :irb_load if method_defined?(:irb_load)
# Loads the given file similarly to Kernel#load, see IrbLoader#irb_load
def irb_load(*opts, &b)
- ExtendCommand::Load.execute(irb_context, *opts, &b)
+ Command::Load.execute(irb_context, *opts, &b)
end
remove_method :irb_require if method_defined?(:irb_require)
# Loads the given file similarly to Kernel#require
def irb_require(*opts, &b)
- ExtendCommand::Require.execute(irb_context, *opts, &b)
+ Command::Require.execute(irb_context, *opts, &b)
end
end
@@ -49,14 +49,12 @@ module IRB
if IRB.conf[:USE_LOADER] != opt
IRB.conf[:USE_LOADER] = opt
if opt
- if !$".include?("irb/cmd/load")
- end
- (class<<@workspace.main;self;end).instance_eval {
+ (class<<workspace.main;self;end).instance_eval {
alias_method :load, :irb_load
alias_method :require, :irb_require
}
else
- (class<<@workspace.main;self;end).instance_eval {
+ (class<<workspace.main;self;end).instance_eval {
alias_method :load, :__original__load__IRB_use_loader__
alias_method :require, :__original__require__IRB_use_loader__
}
diff --git a/lib/irb/ext/workspaces.rb b/lib/irb/ext/workspaces.rb
index 9defc3e17b..da09faa83e 100644
--- a/lib/irb/ext/workspaces.rb
+++ b/lib/irb/ext/workspaces.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# push-ws.rb -
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -6,21 +6,6 @@
module IRB # :nodoc:
class Context
-
- # Size of the current WorkSpace stack
- def irb_level
- workspace_stack.size
- end
-
- # WorkSpaces in the current stack
- def workspaces
- if defined? @workspaces
- @workspaces
- else
- @workspaces = []
- end
- end
-
# Creates a new workspace with the given object or binding, and appends it
# onto the current #workspaces stack.
#
@@ -28,20 +13,15 @@ module IRB # :nodoc:
# information.
def push_workspace(*_main)
if _main.empty?
- if workspaces.empty?
- print "No other workspace\n"
- return nil
+ if @workspace_stack.size > 1
+ # swap the top two workspaces
+ previous_workspace, current_workspace = @workspace_stack.pop(2)
+ @workspace_stack.push current_workspace, previous_workspace
end
- ws = workspaces.pop
- workspaces.push @workspace
- @workspace = ws
- return workspaces
- end
-
- workspaces.push @workspace
- @workspace = WorkSpace.new(@workspace.binding, _main[0])
- if !(class<<main;ancestors;end).include?(ExtendCommandBundle)
- main.extend ExtendCommandBundle
+ else
+ new_workspace = WorkSpace.new(workspace.binding, _main[0])
+ @workspace_stack.push new_workspace
+ new_workspace.load_helper_methods_to_main
end
end
@@ -50,11 +30,7 @@ module IRB # :nodoc:
#
# Also, see #push_workspace.
def pop_workspace
- if workspaces.empty?
- print "workspace stack empty\n"
- return
- end
- @workspace = workspaces.pop
+ @workspace_stack.pop if @workspace_stack.size > 1
end
end
end
diff --git a/lib/irb/extend-command.rb b/lib/irb/extend-command.rb
deleted file mode 100644
index e377148fb4..0000000000
--- a/lib/irb/extend-command.rb
+++ /dev/null
@@ -1,356 +0,0 @@
-# frozen_string_literal: false
-#
-# irb/extend-command.rb - irb extend command
-# by Keiju ISHITSUKA(keiju@ruby-lang.org)
-#
-
-module IRB # :nodoc:
- # Installs the default irb extensions command bundle.
- module ExtendCommandBundle
- EXCB = ExtendCommandBundle # :nodoc:
-
- # See #install_alias_method.
- NO_OVERRIDE = 0
- # See #install_alias_method.
- OVERRIDE_PRIVATE_ONLY = 0x01
- # See #install_alias_method.
- OVERRIDE_ALL = 0x02
-
- # Quits the current irb context
- #
- # +ret+ is the optional signal or message to send to Context#exit
- #
- # Same as <code>IRB.CurrentContext.exit</code>.
- def irb_exit(ret = 0)
- irb_context.exit(ret)
- end
-
- # Displays current configuration.
- #
- # Modifying the configuration is achieved by sending a message to IRB.conf.
- def irb_context
- IRB.CurrentContext
- end
-
- @ALIASES = [
- [:context, :irb_context, NO_OVERRIDE],
- [:conf, :irb_context, NO_OVERRIDE],
- [:irb_quit, :irb_exit, OVERRIDE_PRIVATE_ONLY],
- [:exit, :irb_exit, OVERRIDE_PRIVATE_ONLY],
- [:quit, :irb_exit, OVERRIDE_PRIVATE_ONLY],
- ]
-
-
- @EXTEND_COMMANDS = [
- [
- :irb_current_working_workspace, :CurrentWorkingWorkspace, "cmd/chws",
- [:cwws, NO_OVERRIDE],
- [:pwws, NO_OVERRIDE],
- [:irb_print_working_workspace, OVERRIDE_ALL],
- [:irb_cwws, OVERRIDE_ALL],
- [:irb_pwws, OVERRIDE_ALL],
- [:irb_current_working_binding, OVERRIDE_ALL],
- [:irb_print_working_binding, OVERRIDE_ALL],
- [:irb_cwb, OVERRIDE_ALL],
- [:irb_pwb, OVERRIDE_ALL],
- ],
- [
- :irb_change_workspace, :ChangeWorkspace, "cmd/chws",
- [:chws, NO_OVERRIDE],
- [:cws, NO_OVERRIDE],
- [:irb_chws, OVERRIDE_ALL],
- [:irb_cws, OVERRIDE_ALL],
- [:irb_change_binding, OVERRIDE_ALL],
- [:irb_cb, OVERRIDE_ALL],
- [:cb, NO_OVERRIDE],
- ],
-
- [
- :irb_workspaces, :Workspaces, "cmd/pushws",
- [:workspaces, NO_OVERRIDE],
- [:irb_bindings, OVERRIDE_ALL],
- [:bindings, NO_OVERRIDE],
- ],
- [
- :irb_push_workspace, :PushWorkspace, "cmd/pushws",
- [:pushws, NO_OVERRIDE],
- [:irb_pushws, OVERRIDE_ALL],
- [:irb_push_binding, OVERRIDE_ALL],
- [:irb_pushb, OVERRIDE_ALL],
- [:pushb, NO_OVERRIDE],
- ],
- [
- :irb_pop_workspace, :PopWorkspace, "cmd/pushws",
- [:popws, NO_OVERRIDE],
- [:irb_popws, OVERRIDE_ALL],
- [:irb_pop_binding, OVERRIDE_ALL],
- [:irb_popb, OVERRIDE_ALL],
- [:popb, NO_OVERRIDE],
- ],
-
- [
- :irb_load, :Load, "cmd/load"],
- [
- :irb_require, :Require, "cmd/load"],
- [
- :irb_source, :Source, "cmd/load",
- [:source, NO_OVERRIDE],
- ],
-
- [
- :irb, :IrbCommand, "cmd/subirb"],
- [
- :irb_jobs, :Jobs, "cmd/subirb",
- [:jobs, NO_OVERRIDE],
- ],
- [
- :irb_fg, :Foreground, "cmd/subirb",
- [:fg, NO_OVERRIDE],
- ],
- [
- :irb_kill, :Kill, "cmd/subirb",
- [:kill, OVERRIDE_PRIVATE_ONLY],
- ],
-
- [
- :irb_debug, :Debug, "cmd/debug",
- [:debug, NO_OVERRIDE],
- ],
- [
- :irb_edit, :Edit, "cmd/edit",
- [:edit, NO_OVERRIDE],
- ],
- [
- :irb_break, :Break, "cmd/break",
- ],
- [
- :irb_catch, :Catch, "cmd/catch",
- ],
- [
- :irb_next, :Next, "cmd/next"
- ],
- [
- :irb_delete, :Delete, "cmd/delete",
- [:delete, NO_OVERRIDE],
- ],
- [
- :irb_step, :Step, "cmd/step",
- [:step, NO_OVERRIDE],
- ],
- [
- :irb_continue, :Continue, "cmd/continue",
- [:continue, NO_OVERRIDE],
- ],
- [
- :irb_finish, :Finish, "cmd/finish",
- [:finish, NO_OVERRIDE],
- ],
- [
- :irb_backtrace, :Backtrace, "cmd/backtrace",
- [:backtrace, NO_OVERRIDE],
- [:bt, NO_OVERRIDE],
- ],
- [
- :irb_debug_info, :Info, "cmd/info",
- [:info, NO_OVERRIDE],
- ],
-
- [
- :irb_help, :Help, "cmd/help",
- [:help, NO_OVERRIDE],
- ],
-
- [
- :irb_show_doc, :ShowDoc, "cmd/show_doc",
- [:show_doc, NO_OVERRIDE],
- ],
-
- [
- :irb_info, :IrbInfo, "cmd/irb_info"
- ],
-
- [
- :irb_ls, :Ls, "cmd/ls",
- [:ls, NO_OVERRIDE],
- ],
-
- [
- :irb_measure, :Measure, "cmd/measure",
- [:measure, NO_OVERRIDE],
- ],
-
- [
- :irb_show_source, :ShowSource, "cmd/show_source",
- [:show_source, NO_OVERRIDE],
- ],
-
- [
- :irb_whereami, :Whereami, "cmd/whereami",
- [:whereami, NO_OVERRIDE],
- ],
- [
- :irb_show_cmds, :ShowCmds, "cmd/show_cmds",
- [:show_cmds, NO_OVERRIDE],
- ]
- ]
-
-
- @@commands = []
-
- def self.all_commands_info
- return @@commands unless @@commands.empty?
- user_aliases = IRB.CurrentContext.command_aliases.each_with_object({}) do |(alias_name, target), result|
- result[target] ||= []
- result[target] << alias_name
- end
-
- @EXTEND_COMMANDS.each do |cmd_name, cmd_class, load_file, *aliases|
- if !defined?(ExtendCommand) || !ExtendCommand.const_defined?(cmd_class, false)
- require_relative load_file
- end
-
- klass = ExtendCommand.const_get(cmd_class, false)
- aliases = aliases.map { |a| a.first }
-
- if additional_aliases = user_aliases[cmd_name]
- aliases += additional_aliases
- end
-
- display_name = aliases.shift || cmd_name
- @@commands << { display_name: display_name, description: klass.description, category: klass.category }
- end
-
- @@commands
- end
-
- # Convert a command name to its implementation class if such command exists
- def self.load_command(command)
- command = command.to_sym
- @EXTEND_COMMANDS.each do |cmd_name, cmd_class, load_file, *aliases|
- next if cmd_name != command && aliases.all? { |alias_name, _| alias_name != command }
-
- if !defined?(ExtendCommand) || !ExtendCommand.const_defined?(cmd_class, false)
- require_relative load_file
- end
- return ExtendCommand.const_get(cmd_class, false)
- end
- nil
- end
-
- # Installs the default irb commands.
- def self.install_extend_commands
- for args in @EXTEND_COMMANDS
- def_extend_command(*args)
- end
- end
-
- # Evaluate the given +cmd_name+ on the given +cmd_class+ Class.
- #
- # Will also define any given +aliases+ for the method.
- #
- # The optional +load_file+ parameter will be required within the method
- # definition.
- def self.def_extend_command(cmd_name, cmd_class, load_file, *aliases)
- case cmd_class
- when Symbol
- cmd_class = cmd_class.id2name
- when String
- when Class
- cmd_class = cmd_class.name
- end
-
- line = __LINE__; eval %[
- def #{cmd_name}(*opts, **kwargs, &b)
- Kernel.require_relative "#{load_file}"
- ::IRB::ExtendCommand::#{cmd_class}.execute(irb_context, *opts, **kwargs, &b)
- end
- ], nil, __FILE__, line
-
- for ali, flag in aliases
- @ALIASES.push [ali, cmd_name, flag]
- end
- end
-
- # Installs alias methods for the default irb commands, see
- # ::install_extend_commands.
- def install_alias_method(to, from, override = NO_OVERRIDE)
- to = to.id2name unless to.kind_of?(String)
- from = from.id2name unless from.kind_of?(String)
-
- if override == OVERRIDE_ALL or
- (override == OVERRIDE_PRIVATE_ONLY) && !respond_to?(to) or
- (override == NO_OVERRIDE) && !respond_to?(to, true)
- target = self
- (class << self; self; end).instance_eval{
- if target.respond_to?(to, true) &&
- !target.respond_to?(EXCB.irb_original_method_name(to), true)
- alias_method(EXCB.irb_original_method_name(to), to)
- end
- alias_method to, from
- }
- else
- Kernel.print "irb: warn: can't alias #{to} from #{from}.\n"
- end
- end
-
- def self.irb_original_method_name(method_name) # :nodoc:
- "irb_" + method_name + "_org"
- end
-
- # Installs alias methods for the default irb commands on the given object
- # using #install_alias_method.
- def self.extend_object(obj)
- unless (class << obj; ancestors; end).include?(EXCB)
- super
- for ali, com, flg in @ALIASES
- obj.install_alias_method(ali, com, flg)
- end
- end
- end
-
- install_extend_commands
- end
-
- # Extends methods for the Context module
- module ContextExtender
- CE = ContextExtender # :nodoc:
-
- @EXTEND_COMMANDS = [
- [:eval_history=, "ext/history.rb"],
- [:use_tracer=, "ext/tracer.rb"],
- [:use_loader=, "ext/use-loader.rb"],
- [:save_history=, "ext/save-history.rb"],
- ]
-
- # Installs the default context extensions as irb commands:
- #
- # Context#eval_history=:: +irb/ext/history.rb+
- # Context#use_tracer=:: +irb/ext/tracer.rb+
- # Context#use_loader=:: +irb/ext/use-loader.rb+
- # Context#save_history=:: +irb/ext/save-history.rb+
- def self.install_extend_commands
- for args in @EXTEND_COMMANDS
- def_extend_command(*args)
- end
- end
-
- # Evaluate the given +command+ from the given +load_file+ on the Context
- # module.
- #
- # Will also define any given +aliases+ for the method.
- def self.def_extend_command(cmd_name, load_file, *aliases)
- line = __LINE__; Context.module_eval %[
- def #{cmd_name}(*opts, &b)
- Context.module_eval {remove_method(:#{cmd_name})}
- require_relative "#{load_file}"
- __send__ :#{cmd_name}, *opts, &b
- end
- for ali in aliases
- alias_method ali, cmd_name
- end
- ], __FILE__, line
- end
-
- CE.install_extend_commands
- end
-end
diff --git a/lib/irb/frame.rb b/lib/irb/frame.rb
index 14768bd8f6..4b697c8719 100644
--- a/lib/irb/frame.rb
+++ b/lib/irb/frame.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# frame.rb -
# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
diff --git a/lib/irb/help.rb b/lib/irb/help.rb
index ca12810de1..a24bc10a15 100644
--- a/lib/irb/help.rb
+++ b/lib/irb/help.rb
@@ -1,12 +1,12 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/help.rb - print usage module
# by Keiju ISHITSUKA(keiju@ishitsuka.com)
#
module IRB
- # Outputs the irb help message, see IRB@Command+line+options.
- def IRB.print_usage
+ # Outputs the irb help message, see IRB@Command-Line+Options.
+ def IRB.print_usage # :nodoc:
lc = IRB.conf[:LC_MESSAGES]
path = lc.find("irb/help-message")
space_line = false
diff --git a/lib/irb/helper_method.rb b/lib/irb/helper_method.rb
new file mode 100644
index 0000000000..f1f6fff915
--- /dev/null
+++ b/lib/irb/helper_method.rb
@@ -0,0 +1,29 @@
+require_relative "helper_method/base"
+
+module IRB
+ module HelperMethod
+ @helper_methods = {}
+
+ class << self
+ attr_reader :helper_methods
+
+ def register(name, helper_class)
+ @helper_methods[name] = helper_class
+
+ if defined?(HelpersContainer)
+ HelpersContainer.install_helper_methods
+ end
+ end
+
+ def all_helper_methods_info
+ @helper_methods.map do |name, helper_class|
+ { display_name: name, description: helper_class.description }
+ end
+ end
+ end
+
+ # Default helper_methods
+ require_relative "helper_method/conf"
+ register(:conf, HelperMethod::Conf)
+ end
+end
diff --git a/lib/irb/helper_method/base.rb b/lib/irb/helper_method/base.rb
new file mode 100644
index 0000000000..a68001ed28
--- /dev/null
+++ b/lib/irb/helper_method/base.rb
@@ -0,0 +1,16 @@
+require "singleton"
+
+module IRB
+ module HelperMethod
+ class Base
+ include Singleton
+
+ class << self
+ def description(description = nil)
+ @description = description if description
+ @description
+ end
+ end
+ end
+ end
+end
diff --git a/lib/irb/helper_method/conf.rb b/lib/irb/helper_method/conf.rb
new file mode 100644
index 0000000000..460f5ab78a
--- /dev/null
+++ b/lib/irb/helper_method/conf.rb
@@ -0,0 +1,11 @@
+module IRB
+ module HelperMethod
+ class Conf < Base
+ description "Returns the current context."
+
+ def execute
+ IRB.CurrentContext
+ end
+ end
+ end
+end
diff --git a/lib/irb/history.rb b/lib/irb/history.rb
new file mode 100644
index 0000000000..685354b2d8
--- /dev/null
+++ b/lib/irb/history.rb
@@ -0,0 +1,87 @@
+require "pathname"
+
+module IRB
+ module HistorySavingAbility # :nodoc:
+ def support_history_saving?
+ true
+ end
+
+ def reset_history_counter
+ @loaded_history_lines = self.class::HISTORY.size
+ end
+
+ def load_history
+ history = self.class::HISTORY
+
+ if history_file = IRB.conf[:HISTORY_FILE]
+ history_file = File.expand_path(history_file)
+ end
+ history_file = IRB.rc_file("_history") unless history_file
+ if history_file && File.exist?(history_file)
+ File.open(history_file, "r:#{IRB.conf[:LC_MESSAGES].encoding}") do |f|
+ f.each { |l|
+ l = l.chomp
+ if self.class == RelineInputMethod and history.last&.end_with?("\\")
+ history.last.delete_suffix!("\\")
+ history.last << "\n" << l
+ else
+ history << l
+ end
+ }
+ end
+ @loaded_history_lines = history.size
+ @loaded_history_mtime = File.mtime(history_file)
+ end
+ end
+
+ def save_history
+ history = self.class::HISTORY.to_a
+
+ if num = IRB.conf[:SAVE_HISTORY] and (num = num.to_i) != 0
+ if history_file = IRB.conf[:HISTORY_FILE]
+ history_file = File.expand_path(history_file)
+ end
+ history_file = IRB.rc_file("_history") unless history_file
+
+ # When HOME and XDG_CONFIG_HOME are not available, history_file might be nil
+ return unless history_file
+
+ # Change the permission of a file that already exists[BUG #7694]
+ begin
+ if File.stat(history_file).mode & 066 != 0
+ File.chmod(0600, history_file)
+ end
+ rescue Errno::ENOENT
+ rescue Errno::EPERM
+ return
+ rescue
+ raise
+ end
+
+ if File.exist?(history_file) &&
+ File.mtime(history_file) != @loaded_history_mtime
+ history = history[@loaded_history_lines..-1] if @loaded_history_lines
+ append_history = true
+ end
+
+ pathname = Pathname.new(history_file)
+ unless Dir.exist?(pathname.dirname)
+ warn "Warning: The directory to save IRB's history file does not exist. Please double check `IRB.conf[:HISTORY_FILE]`'s value."
+ return
+ end
+
+ File.open(history_file, (append_history ? 'a' : 'w'), 0o600, encoding: IRB.conf[:LC_MESSAGES]&.encoding) do |f|
+ hist = history.map{ |l| l.scrub.split("\n").join("\\\n") }
+ unless append_history
+ begin
+ hist = hist.last(num) if hist.size > num and num > 0
+ rescue RangeError # bignum too big to convert into `long'
+ # Do nothing because the bignum should be treated as infinity
+ end
+ end
+ f.puts(hist)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/irb/init.rb b/lib/irb/init.rb
index 75a7f8d74c..355047519c 100644
--- a/lib/irb/init.rb
+++ b/lib/irb/init.rb
@@ -1,10 +1,50 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/init.rb - irb initialize module
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
#
module IRB # :nodoc:
+ @CONF = {}
+ @INITIALIZED = false
+ # Displays current configuration.
+ #
+ # Modifying the configuration is achieved by sending a message to IRB.conf.
+ #
+ # See IRB@Configuration for more information.
+ def IRB.conf
+ @CONF
+ end
+
+ def @CONF.inspect
+ array = []
+ for k, v in sort{|a1, a2| a1[0].id2name <=> a2[0].id2name}
+ case k
+ when :MAIN_CONTEXT, :__TMP__EHV__
+ array.push format("CONF[:%s]=...myself...", k.id2name)
+ when :PROMPT
+ s = v.collect{
+ |kk, vv|
+ ss = vv.collect{|kkk, vvv| ":#{kkk.id2name}=>#{vvv.inspect}"}
+ format(":%s=>{%s}", kk.id2name, ss.join(", "))
+ }
+ array.push format("CONF[:%s]={%s}", k.id2name, s.join(", "))
+ else
+ array.push format("CONF[:%s]=%s", k.id2name, v.inspect)
+ end
+ end
+ array.join("\n")
+ end
+
+ # Returns the current version of IRB, including release version and last
+ # updated date.
+ def IRB.version
+ format("irb %s (%s)", @RELEASE_VERSION, @LAST_UPDATE_DATE)
+ end
+
+ def IRB.initialized?
+ !!@INITIALIZED
+ end
# initialize config
def IRB.setup(ap_path, argv: ::ARGV)
@@ -17,17 +57,16 @@ module IRB # :nodoc:
unless @CONF[:PROMPT][@CONF[:PROMPT_MODE]]
fail UndefinedPromptMode, @CONF[:PROMPT_MODE]
end
+ @INITIALIZED = true
end
# @CONF default setting
def IRB.init_config(ap_path)
- # class instance variables
- @TRACER_INITIALIZED = false
-
# default configurations
unless ap_path and @CONF[:AP_NAME]
ap_path = File.join(File.dirname(File.dirname(__FILE__)), "irb.rb")
end
+ @CONF[:VERSION] = version
@CONF[:AP_NAME] = File::basename(ap_path, ".rb")
@CONF[:IRB_NAME] = "irb"
@@ -40,11 +79,13 @@ module IRB # :nodoc:
@CONF[:USE_SINGLELINE] = false unless defined?(ReadlineInputMethod)
@CONF[:USE_COLORIZE] = (nc = ENV['NO_COLOR']).nil? || nc.empty?
@CONF[:USE_AUTOCOMPLETE] = ENV.fetch("IRB_USE_AUTOCOMPLETE", "true") != "false"
+ @CONF[:COMPLETOR] = ENV.fetch("IRB_COMPLETOR", "regexp").to_sym
@CONF[:INSPECT_MODE] = true
@CONF[:USE_TRACER] = false
@CONF[:USE_LOADER] = false
@CONF[:IGNORE_SIGINT] = true
@CONF[:IGNORE_EOF] = false
+ @CONF[:USE_PAGER] = true
@CONF[:EXTRA_DOC_DIRS] = []
@CONF[:ECHO] = nil
@CONF[:ECHO_ON_ASSIGNMENT] = nil
@@ -58,35 +99,30 @@ module IRB # :nodoc:
@CONF[:PROMPT] = {
:NULL => {
:PROMPT_I => nil,
- :PROMPT_N => nil,
:PROMPT_S => nil,
:PROMPT_C => nil,
:RETURN => "%s\n"
},
:DEFAULT => {
- :PROMPT_I => "%N(%m):%03n:%i> ",
- :PROMPT_N => "%N(%m):%03n:%i> ",
- :PROMPT_S => "%N(%m):%03n:%i%l ",
- :PROMPT_C => "%N(%m):%03n:%i* ",
+ :PROMPT_I => "%N(%m):%03n> ",
+ :PROMPT_S => "%N(%m):%03n%l ",
+ :PROMPT_C => "%N(%m):%03n* ",
:RETURN => "=> %s\n"
},
:CLASSIC => {
:PROMPT_I => "%N(%m):%03n:%i> ",
- :PROMPT_N => "%N(%m):%03n:%i> ",
:PROMPT_S => "%N(%m):%03n:%i%l ",
:PROMPT_C => "%N(%m):%03n:%i* ",
:RETURN => "%s\n"
},
:SIMPLE => {
:PROMPT_I => ">> ",
- :PROMPT_N => ">> ",
:PROMPT_S => "%l> ",
:PROMPT_C => "?> ",
:RETURN => "=> %s\n"
},
:INF_RUBY => {
- :PROMPT_I => "%N(%m):%03n:%i> ",
- :PROMPT_N => nil,
+ :PROMPT_I => "%N(%m):%03n> ",
:PROMPT_S => nil,
:PROMPT_C => nil,
:RETURN => "%s\n",
@@ -94,7 +130,6 @@ module IRB # :nodoc:
},
:XMP => {
:PROMPT_I => nil,
- :PROMPT_N => nil,
:PROMPT_S => nil,
:PROMPT_C => nil,
:RETURN => " ==>%s\n"
@@ -157,10 +192,6 @@ module IRB # :nodoc:
# Symbol aliases
:'$' => :show_source,
:'@' => :whereami,
- # Keyword aliases
- :break => :irb_break,
- :catch => :irb_catch,
- :next => :irb_next,
}
end
@@ -187,6 +218,7 @@ module IRB # :nodoc:
added = [:TIME, IRB.conf[:MEASURE_PROC][:TIME], arg]
end
if added
+ IRB.conf[:MEASURE] = true
found = IRB.conf[:MEASURE_CALLBACKS].find{ |m| m[0] == added[0] && m[2] == added[2] }
if found
# already added
@@ -207,6 +239,7 @@ module IRB # :nodoc:
type_sym = type.upcase.to_sym
IRB.conf[:MEASURE_CALLBACKS].reject!{ |t, | t == type_sym }
end
+ IRB.conf[:MEASURE] = nil if IRB.conf[:MEASURE_CALLBACKS].empty?
end
def IRB.init_error
@@ -254,6 +287,8 @@ module IRB # :nodoc:
end
when "--noinspect"
@CONF[:INSPECT_MODE] = false
+ when "--no-pager"
+ @CONF[:USE_PAGER] = false
when "--singleline", "--readline", "--legacy"
@CONF[:USE_SINGLELINE] = true
when "--nosingleline", "--noreadline"
@@ -299,6 +334,10 @@ module IRB # :nodoc:
@CONF[:USE_AUTOCOMPLETE] = true
when "--noautocomplete"
@CONF[:USE_AUTOCOMPLETE] = false
+ when "--regexp-completor"
+ @CONF[:COMPLETOR] = :regexp
+ when "--type-completor"
+ @CONF[:COMPLETOR] = :type
when /^--prompt-mode(?:=(.+))?/, /^--prompt(?:=(.+))?/
opt = $1 || argv.shift
prompt_mode = opt.upcase.tr("-", "_").intern
@@ -353,61 +392,39 @@ module IRB # :nodoc:
$LOAD_PATH.unshift(*load_path)
end
- # running config
+ # Run the config file
def IRB.run_config
if @CONF[:RC]
- begin
- load rc_file
- rescue LoadError, Errno::ENOENT
- rescue # StandardError, ScriptError
- print "load error: #{rc_file}\n"
- print $!.class, ": ", $!, "\n"
- for err in $@[0, $@.size - 2]
- print "\t", err, "\n"
- end
+ irbrc_files.each do |rc|
+ load rc
+ rescue StandardError, ScriptError => e
+ warn "Error loading RC file '#{rc}':\n#{e.full_message(highlight: false)}"
end
end
end
IRBRC_EXT = "rc"
- def IRB.rc_file(ext = IRBRC_EXT)
- if !@CONF[:RC_NAME_GENERATOR]
- rc_file_generators do |rcgen|
- @CONF[:RC_NAME_GENERATOR] ||= rcgen
- if File.exist?(rcgen.call(IRBRC_EXT))
- @CONF[:RC_NAME_GENERATOR] = rcgen
- break
- end
- end
+
+ def IRB.rc_file(ext)
+ prepare_irbrc_name_generators
+
+ # When irbrc exist in default location
+ if (rcgen = @existing_rc_name_generators.first)
+ return rcgen.call(ext)
end
- case rc_file = @CONF[:RC_NAME_GENERATOR].call(ext)
- when String
- return rc_file
- else
- fail IllegalRCNameGenerator
+
+ # When irbrc does not exist in default location
+ rc_file_generators do |rcgen|
+ return rcgen.call(ext)
end
+
+ # When HOME and XDG_CONFIG_HOME are not available
+ nil
end
- # enumerate possible rc-file base name generators
- def IRB.rc_file_generators
- if irbrc = ENV["IRBRC"]
- yield proc{|rc| rc == "rc" ? irbrc : irbrc+rc}
- end
- if xdg_config_home = ENV["XDG_CONFIG_HOME"]
- irb_home = File.join(xdg_config_home, "irb")
- if File.directory?(irb_home)
- yield proc{|rc| irb_home + "/irb#{rc}"}
- end
- end
- if home = ENV["HOME"]
- yield proc{|rc| home+"/.irb#{rc}"}
- yield proc{|rc| home+"/.config/irb/irb#{rc}"}
- end
- current_dir = Dir.pwd
- yield proc{|rc| current_dir+"/.irb#{rc}"}
- yield proc{|rc| current_dir+"/irb#{rc.sub(/\A_?/, '.')}"}
- yield proc{|rc| current_dir+"/_irb#{rc}"}
- yield proc{|rc| current_dir+"/$irb#{rc}"}
+ def IRB.irbrc_files
+ prepare_irbrc_name_generators
+ @irbrc_files
end
# loading modules
@@ -423,6 +440,50 @@ module IRB # :nodoc:
class << IRB
private
+
+ def prepare_irbrc_name_generators
+ return if @existing_rc_name_generators
+
+ @existing_rc_name_generators = []
+ @irbrc_files = []
+ rc_file_generators do |rcgen|
+ irbrc = rcgen.call(IRBRC_EXT)
+ if File.exist?(irbrc)
+ @irbrc_files << irbrc
+ @existing_rc_name_generators << rcgen
+ end
+ end
+ generate_current_dir_irbrc_files.each do |irbrc|
+ @irbrc_files << irbrc if File.exist?(irbrc)
+ end
+ @irbrc_files.uniq!
+ end
+
+ # enumerate possible rc-file base name generators
+ def rc_file_generators
+ if irbrc = ENV["IRBRC"]
+ yield proc{|rc| rc == "rc" ? irbrc : irbrc+rc}
+ end
+ if xdg_config_home = ENV["XDG_CONFIG_HOME"]
+ irb_home = File.join(xdg_config_home, "irb")
+ if File.directory?(irb_home)
+ yield proc{|rc| irb_home + "/irb#{rc}"}
+ end
+ end
+ if home = ENV["HOME"]
+ yield proc{|rc| home+"/.irb#{rc}"}
+ if xdg_config_home.nil? || xdg_config_home.empty?
+ yield proc{|rc| home+"/.config/irb/irb#{rc}"}
+ end
+ end
+ end
+
+ # possible irbrc files in current directory
+ def generate_current_dir_irbrc_files
+ current_dir = Dir.pwd
+ %w[.irbrc irbrc _irbrc $irbrc].map { |file| "#{current_dir}/#{file}" }
+ end
+
def set_encoding(extern, intern = nil, override: true)
verbose, $VERBOSE = $VERBOSE, nil
Encoding.default_external = extern unless extern.nil? || extern.empty?
diff --git a/lib/irb/input-method.rb b/lib/irb/input-method.rb
index 4e049b22db..e5adb350e8 100644
--- a/lib/irb/input-method.rb
+++ b/lib/irb/input-method.rb
@@ -1,24 +1,17 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/input-method.rb - input methods used irb
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
#
-require_relative 'src_encoding'
require_relative 'completion'
+require_relative "history"
require 'io/console'
require 'reline'
module IRB
- STDIN_FILE_NAME = "(line)" # :nodoc:
class InputMethod
-
- # Creates a new input method object
- def initialize(file = STDIN_FILE_NAME)
- @file_name = file
- end
- # The file name of this input method, usually given during initialization.
- attr_reader :file_name
+ BASIC_WORD_BREAK_CHARACTERS = " \t\n`><=;|&{("
# The irb prompt associated with this input method
attr_accessor :prompt
@@ -27,7 +20,7 @@ module IRB
#
# See IO#gets for more information.
def gets
- fail NotImplementedError, "gets"
+ fail NotImplementedError
end
public :gets
@@ -47,6 +40,14 @@ module IRB
false
end
+ def support_history_saving?
+ false
+ end
+
+ def prompting?
+ false
+ end
+
# For debug message
def inspect
'Abstract InputMethod'
@@ -56,7 +57,6 @@ module IRB
class StdioInputMethod < InputMethod
# Creates a new input method object
def initialize
- super
@line_no = 0
@line = []
@stdin = IO.open(STDIN.to_i, :external_encoding => IRB.conf[:LC_MESSAGES].encoding, :internal_encoding => "-")
@@ -95,6 +95,10 @@ module IRB
true
end
+ def prompting?
+ STDIN.tty?
+ end
+
# Returns the current line number for #io.
#
# #line counts the number of times #gets is called.
@@ -130,12 +134,9 @@ module IRB
# Creates a new input method object
def initialize(file)
- super
@io = file.is_a?(IO) ? file : File.open(file)
@external_encoding = @io.external_encoding
end
- # The file name of this input method, usually given during initialization.
- attr_reader :file_name
# Whether the end of this input method has been reached, returns +true+ if
# there is no more data to read.
@@ -168,114 +169,99 @@ module IRB
end
end
- begin
- class ReadlineInputMethod < InputMethod
- def self.initialize_readline
- require "readline"
- rescue LoadError
- else
- include ::Readline
- end
+ class ReadlineInputMethod < StdioInputMethod
+ def self.initialize_readline
+ require "readline"
+ rescue LoadError
+ else
+ include ::Readline
+ end
- # Creates a new input method object using Readline
- def initialize
- self.class.initialize_readline
- if Readline.respond_to?(:encoding_system_needs)
- IRB.__send__(:set_encoding, Readline.encoding_system_needs.name, override: false)
- end
- super
+ include HistorySavingAbility
- @line_no = 0
- @line = []
- @eof = false
+ # Creates a new input method object using Readline
+ def initialize
+ self.class.initialize_readline
+ if Readline.respond_to?(:encoding_system_needs)
+ IRB.__send__(:set_encoding, Readline.encoding_system_needs.name, override: false)
+ end
- @stdin = IO.open(STDIN.to_i, :external_encoding => IRB.conf[:LC_MESSAGES].encoding, :internal_encoding => "-")
- @stdout = IO.open(STDOUT.to_i, 'w', :external_encoding => IRB.conf[:LC_MESSAGES].encoding, :internal_encoding => "-")
+ super
- if Readline.respond_to?("basic_word_break_characters=")
- Readline.basic_word_break_characters = IRB::InputCompletor::BASIC_WORD_BREAK_CHARACTERS
- end
- Readline.completion_append_character = nil
- Readline.completion_proc = IRB::InputCompletor::CompletionProc
- end
+ @eof = false
+ @completor = RegexpCompletor.new
- # Reads the next line from this input method.
- #
- # See IO#gets for more information.
- def gets
- Readline.input = @stdin
- Readline.output = @stdout
- if l = readline(@prompt, false)
- HISTORY.push(l) if !l.empty?
- @line[@line_no += 1] = l + "\n"
- else
- @eof = true
- l
- end
+ if Readline.respond_to?("basic_word_break_characters=")
+ Readline.basic_word_break_characters = BASIC_WORD_BREAK_CHARACTERS
end
+ Readline.completion_append_character = nil
+ Readline.completion_proc = ->(target) {
+ bind = IRB.conf[:MAIN_CONTEXT].workspace.binding
+ @completor.completion_candidates('', target, '', bind: bind)
+ }
+ end
- # Whether the end of this input method has been reached, returns +true+
- # if there is no more data to read.
- #
- # See IO#eof? for more information.
- def eof?
- @eof
- end
+ def completion_info
+ 'RegexpCompletor'
+ end
- # Whether this input method is still readable when there is no more data to
- # read.
- #
- # See IO#eof for more information.
- def readable_after_eof?
- true
+ # Reads the next line from this input method.
+ #
+ # See IO#gets for more information.
+ def gets
+ Readline.input = @stdin
+ Readline.output = @stdout
+ if l = readline(@prompt, false)
+ HISTORY.push(l) if !l.empty?
+ @line[@line_no += 1] = l + "\n"
+ else
+ @eof = true
+ l
end
+ end
- # Returns the current line number for #io.
- #
- # #line counts the number of times #gets is called.
- #
- # See IO#lineno for more information.
- def line(line_no)
- @line[line_no]
- end
+ # Whether the end of this input method has been reached, returns +true+
+ # if there is no more data to read.
+ #
+ # See IO#eof? for more information.
+ def eof?
+ @eof
+ end
- # The external encoding for standard input.
- def encoding
- @stdin.external_encoding
- end
+ def prompting?
+ true
+ end
- # For debug message
- def inspect
- readline_impl = (defined?(Reline) && Readline == Reline) ? 'Reline' : 'ext/readline'
- str = "ReadlineInputMethod with #{readline_impl} #{Readline::VERSION}"
- inputrc_path = File.expand_path(ENV['INPUTRC'] || '~/.inputrc')
- str += " and #{inputrc_path}" if File.exist?(inputrc_path)
- str
- end
+ # For debug message
+ def inspect
+ readline_impl = (defined?(Reline) && Readline == Reline) ? 'Reline' : 'ext/readline'
+ str = "ReadlineInputMethod with #{readline_impl} #{Readline::VERSION}"
+ inputrc_path = File.expand_path(ENV['INPUTRC'] || '~/.inputrc')
+ str += " and #{inputrc_path}" if File.exist?(inputrc_path)
+ str
end
end
- class RelineInputMethod < InputMethod
- include Reline
-
+ class RelineInputMethod < StdioInputMethod
+ HISTORY = Reline::HISTORY
+ include HistorySavingAbility
# Creates a new input method object using Reline
- def initialize
+ def initialize(completor)
IRB.__send__(:set_encoding, Reline.encoding_system_needs.name, override: false)
- super
- @line_no = 0
- @line = []
- @eof = false
+ super()
- @stdin = ::IO.open(STDIN.to_i, :external_encoding => IRB.conf[:LC_MESSAGES].encoding, :internal_encoding => "-")
- @stdout = ::IO.open(STDOUT.to_i, 'w', :external_encoding => IRB.conf[:LC_MESSAGES].encoding, :internal_encoding => "-")
+ @eof = false
+ @completor = completor
- if Reline.respond_to?("basic_word_break_characters=")
- Reline.basic_word_break_characters = IRB::InputCompletor::BASIC_WORD_BREAK_CHARACTERS
- end
+ Reline.basic_word_break_characters = BASIC_WORD_BREAK_CHARACTERS
Reline.completion_append_character = nil
Reline.completer_quote_characters = ''
- Reline.completion_proc = IRB::InputCompletor::CompletionProc
+ Reline.completion_proc = ->(target, preposing, postposing) {
+ bind = IRB.conf[:MAIN_CONTEXT].workspace.binding
+ @completion_params = [preposing, target, postposing, bind]
+ @completor.completion_candidates(preposing, target, postposing, bind: bind)
+ }
Reline.output_modifier_proc =
if IRB.conf[:USE_COLORIZE]
proc do |output, complete: |
@@ -288,18 +274,23 @@ module IRB
Reline::Unicode.escape_for_print(output)
end
end
- Reline.dig_perfect_match_proc = IRB::InputCompletor::PerfectMatchedProc
+ Reline.dig_perfect_match_proc = ->(matched) { display_document(matched) }
Reline.autocompletion = IRB.conf[:USE_AUTOCOMPLETE]
if IRB.conf[:USE_AUTOCOMPLETE]
begin
require 'rdoc'
- Reline.add_dialog_proc(:show_doc, SHOW_DOC_DIALOG, Reline::DEFAULT_DIALOG_CONTEXT)
+ Reline.add_dialog_proc(:show_doc, show_doc_dialog_proc, Reline::DEFAULT_DIALOG_CONTEXT)
rescue LoadError
end
end
end
+ def completion_info
+ autocomplete_message = Reline.autocompletion ? 'Autocomplete' : 'Tab Complete'
+ "#{autocomplete_message}, #{@completor.inspect}"
+ end
+
def check_termination(&block)
@check_termination_proc = block
end
@@ -312,100 +303,164 @@ module IRB
@auto_indent_proc = block
end
- SHOW_DOC_DIALOG = ->() {
- dialog.trap_key = nil
- alt_d = [
- [Reline::Key.new(nil, 0xE4, true)], # Normal Alt+d.
- [27, 100], # Normal Alt+d when convert-meta isn't used.
- [195, 164], # The "ä" that appears when Alt+d is pressed on xterm.
- [226, 136, 130] # The "∂" that appears when Alt+d in pressed on iTerm2.
- ]
+ def retrieve_doc_namespace(matched)
+ preposing, _target, postposing, bind = @completion_params
+ @completor.doc_namespace(preposing, matched, postposing, bind: bind)
+ end
- if just_cursor_moving and completion_journey_data.nil?
- return nil
- end
- cursor_pos_to_render, result, pointer, autocomplete_dialog = context.pop(4)
- return nil if result.nil? or pointer.nil? or pointer < 0
- name = result[pointer]
- name = IRB::InputCompletor.retrieve_completion_data(name, doc_namespace: true)
+ def rdoc_ri_driver
+ return @rdoc_ri_driver if defined?(@rdoc_ri_driver)
- options = {}
- options[:extra_doc_dirs] = IRB.conf[:EXTRA_DOC_DIRS] unless IRB.conf[:EXTRA_DOC_DIRS].empty?
- driver = RDoc::RI::Driver.new(options)
+ begin
+ require 'rdoc'
+ rescue LoadError
+ @rdoc_ri_driver = nil
+ else
+ options = {}
+ options[:extra_doc_dirs] = IRB.conf[:EXTRA_DOC_DIRS] unless IRB.conf[:EXTRA_DOC_DIRS].empty?
+ @rdoc_ri_driver = RDoc::RI::Driver.new(options)
+ end
+ end
- if key.match?(dialog.name)
- begin
- driver.display_names([name])
- rescue RDoc::RI::Driver::NotFoundError
+ def show_doc_dialog_proc
+ input_method = self # self is changed in the lambda below.
+ ->() {
+ dialog.trap_key = nil
+ alt_d = [
+ [Reline::Key.new(nil, 0xE4, true)], # Normal Alt+d.
+ [27, 100], # Normal Alt+d when convert-meta isn't used.
+ [195, 164], # The "ä" that appears when Alt+d is pressed on xterm.
+ [226, 136, 130] # The "∂" that appears when Alt+d in pressed on iTerm2.
+ ]
+
+ if just_cursor_moving and completion_journey_data.nil?
+ return nil
end
- end
+ cursor_pos_to_render, result, pointer, autocomplete_dialog = context.pop(4)
+ return nil if result.nil? or pointer.nil? or pointer < 0
- begin
- name = driver.expand_name(name)
- rescue RDoc::RI::Driver::NotFoundError
- return nil
- rescue
- return nil # unknown error
- end
- doc = nil
- used_for_class = false
- if not name =~ /#|\./
- found, klasses, includes, extends = driver.classes_and_includes_and_extends_for(name)
- if not found.empty?
- doc = driver.class_document(name, found, klasses, includes, extends)
- used_for_class = true
+ name = input_method.retrieve_doc_namespace(result[pointer])
+ # Use first one because document dialog does not support multiple namespaces.
+ name = name.first if name.is_a?(Array)
+
+ show_easter_egg = name&.match?(/\ARubyVM/) && !ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
+
+ driver = input_method.rdoc_ri_driver
+
+ if key.match?(dialog.name)
+ if show_easter_egg
+ IRB.__send__(:easter_egg)
+ else
+ begin
+ driver.display_names([name])
+ rescue RDoc::RI::Driver::NotFoundError
+ end
+ end
end
- end
- unless used_for_class
- doc = RDoc::Markup::Document.new
+
begin
- driver.add_method(doc, name)
+ name = driver.expand_name(name)
rescue RDoc::RI::Driver::NotFoundError
- doc = nil
+ return nil
rescue
return nil # unknown error
end
- end
- return nil if doc.nil?
- width = 40
-
- right_x = cursor_pos_to_render.x + autocomplete_dialog.width
- if right_x + width > screen_width
- right_width = screen_width - (right_x + 1)
- left_x = autocomplete_dialog.column - width
- left_x = 0 if left_x < 0
- left_width = width > autocomplete_dialog.column ? autocomplete_dialog.column : width
- if right_width.positive? and left_width.positive?
- if right_width >= left_width
+ doc = nil
+ used_for_class = false
+ if not name =~ /#|\./
+ found, klasses, includes, extends = driver.classes_and_includes_and_extends_for(name)
+ if not found.empty?
+ doc = driver.class_document(name, found, klasses, includes, extends)
+ used_for_class = true
+ end
+ end
+ unless used_for_class
+ doc = RDoc::Markup::Document.new
+ begin
+ driver.add_method(doc, name)
+ rescue RDoc::RI::Driver::NotFoundError
+ doc = nil
+ rescue
+ return nil # unknown error
+ end
+ end
+ return nil if doc.nil?
+ width = 40
+
+ right_x = cursor_pos_to_render.x + autocomplete_dialog.width
+ if right_x + width > screen_width
+ right_width = screen_width - (right_x + 1)
+ left_x = autocomplete_dialog.column - width
+ left_x = 0 if left_x < 0
+ left_width = width > autocomplete_dialog.column ? autocomplete_dialog.column : width
+ if right_width.positive? and left_width.positive?
+ if right_width >= left_width
+ width = right_width
+ x = right_x
+ else
+ width = left_width
+ x = left_x
+ end
+ elsif right_width.positive? and left_width <= 0
width = right_width
x = right_x
- else
+ elsif right_width <= 0 and left_width.positive?
width = left_width
x = left_x
+ else # Both are negative width.
+ return nil
end
- elsif right_width.positive? and left_width <= 0
- width = right_width
+ else
x = right_x
- elsif right_width <= 0 and left_width.positive?
- width = left_width
- x = left_x
- else # Both are negative width.
- return nil
end
+ formatter = RDoc::Markup::ToAnsi.new
+ formatter.width = width
+ dialog.trap_key = alt_d
+ mod_key = RUBY_PLATFORM.match?(/darwin/) ? "Option" : "Alt"
+ if show_easter_egg
+ type = STDOUT.external_encoding == Encoding::UTF_8 ? :unicode : :ascii
+ contents = IRB.send(:easter_egg_logo, type).split("\n")
+ message = "Press #{mod_key}+d to see more"
+ contents[0][0, message.size] = message
+ else
+ message = "Press #{mod_key}+d to read the full document"
+ contents = [message] + doc.accept(formatter).split("\n")
+ end
+ contents = contents.take(preferred_dialog_height)
+
+ y = cursor_pos_to_render.y
+ Reline::DialogRenderInfo.new(pos: Reline::CursorPos.new(x, y), contents: contents, width: width, bg_color: '49')
+ }
+ end
+
+ def display_document(matched)
+ driver = rdoc_ri_driver
+ return unless driver
+
+ if matched =~ /\A(?:::)?RubyVM/ and not ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
+ IRB.__send__(:easter_egg)
+ return
+ end
+
+ namespace = retrieve_doc_namespace(matched)
+ return unless namespace
+
+ if namespace.is_a?(Array)
+ out = RDoc::Markup::Document.new
+ namespace.each do |m|
+ begin
+ driver.add_method(out, m)
+ rescue RDoc::RI::Driver::NotFoundError
+ end
+ end
+ driver.display(out)
else
- x = right_x
+ begin
+ driver.display_names([namespace])
+ rescue RDoc::RI::Driver::NotFoundError
+ end
end
- formatter = RDoc::Markup::ToAnsi.new
- formatter.width = width
- dialog.trap_key = alt_d
- mod_key = RUBY_PLATFORM.match?(/darwin/) ? "Option" : "Alt"
- message = "Press #{mod_key}+d to read the full document"
- contents = [message] + doc.accept(formatter).split("\n")
- contents = contents.take(preferred_dialog_height) if respond_to?(:preferred_dialog_height)
-
- y = cursor_pos_to_render.y
- DialogRenderInfo.new(pos: Reline::CursorPos.new(x, y), contents: contents, width: width, bg_color: '49')
- }
+ end
# Reads the next line from this input method.
#
@@ -415,8 +470,8 @@ module IRB
Reline.output = @stdout
Reline.prompt_proc = @prompt_proc
Reline.auto_indent_proc = @auto_indent_proc if @auto_indent_proc
- if l = readmultiline(@prompt, false, &@check_termination_proc)
- HISTORY.push(l) if !l.empty?
+ if l = Reline.readmultiline(@prompt, false, &@check_termination_proc)
+ Reline::HISTORY.push(l) if !l.empty?
@line[@line_no += 1] = l + "\n"
else
@eof = true
@@ -432,37 +487,15 @@ module IRB
@eof
end
- # Whether this input method is still readable when there is no more data to
- # read.
- #
- # See IO#eof for more information.
- def readable_after_eof?
+ def prompting?
true
end
- # Returns the current line number for #io.
- #
- # #line counts the number of times #gets is called.
- #
- # See IO#lineno for more information.
- def line(line_no)
- @line[line_no]
- end
-
- # The external encoding for standard input.
- def encoding
- @stdin.external_encoding
- end
-
# For debug message
def inspect
config = Reline::Config.new
str = "RelineInputMethod with Reline #{Reline::VERSION}"
- if config.respond_to?(:inputrc_path)
- inputrc_path = File.expand_path(config.inputrc_path)
- else
- inputrc_path = File.expand_path(ENV['INPUTRC'] || '~/.inputrc')
- end
+ inputrc_path = File.expand_path(config.inputrc_path)
str += " and #{inputrc_path}" if File.exist?(inputrc_path)
str
end
diff --git a/lib/irb/inspector.rb b/lib/irb/inspector.rb
index ee3b19efdc..667087ccba 100644
--- a/lib/irb/inspector.rb
+++ b/lib/irb/inspector.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/inspector.rb - inspect methods
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -46,7 +46,7 @@ module IRB # :nodoc:
# Determines the inspector to use where +inspector+ is one of the keys passed
# during inspector definition.
def self.keys_with_inspector(inspector)
- INSPECTORS.select{|k,v| v == inspector}.collect{|k, v| k}
+ INSPECTORS.select{|k, v| v == inspector}.collect{|k, v| k}
end
# Example
@@ -113,7 +113,7 @@ module IRB # :nodoc:
Color.colorize_code(v.inspect, colorable: Color.colorable? && Color.inspect_colorable?(v))
}
Inspector.def_inspector([true, :pp, :pretty_inspect], proc{require_relative "color_printer"}){|v|
- IRB::ColorPrinter.pp(v, '').chomp
+ IRB::ColorPrinter.pp(v, +'').chomp
}
Inspector.def_inspector([:yaml, :YAML], proc{require "yaml"}){|v|
begin
diff --git a/lib/irb/irb.gemspec b/lib/irb/irb.gemspec
index e62429ca7b..b29002f593 100644
--- a/lib/irb/irb.gemspec
+++ b/lib/irb/irb.gemspec
@@ -41,5 +41,6 @@ Gem::Specification.new do |spec|
spec.required_ruby_version = Gem::Requirement.new(">= 2.7")
- spec.add_dependency "reline", ">= 0.3.0"
+ spec.add_dependency "reline", ">= 0.4.2"
+ spec.add_dependency "rdoc", ">= 4.0.0"
end
diff --git a/lib/irb/lc/error.rb b/lib/irb/lc/error.rb
index a5ec150865..ee0f047822 100644
--- a/lib/irb/lc/error.rb
+++ b/lib/irb/lc/error.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/lc/error.rb -
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -12,11 +12,6 @@ module IRB
super("Unrecognized switch: #{val}")
end
end
- class NotImplementedError < StandardError
- def initialize(val)
- super("Need to define `#{val}'")
- end
- end
class CantReturnToNormalMode < StandardError
def initialize
super("Can't return to normal mode.")
@@ -52,11 +47,6 @@ module IRB
super("Undefined prompt mode(#{val}).")
end
end
- class IllegalRCGenerator < StandardError
- def initialize
- super("Define illegal RC_NAME_GENERATOR.")
- end
- end
# :startdoc:
end
diff --git a/lib/irb/lc/help-message b/lib/irb/lc/help-message
index 7e66100be9..37347306e8 100644
--- a/lib/irb/lc/help-message
+++ b/lib/irb/lc/help-message
@@ -22,6 +22,7 @@ Usage: irb.rb [options] [programfile] [arguments]
Show truncated result on assignment (default).
--inspect Use 'inspect' for output.
--noinspect Don't use 'inspect' for output.
+ --no-pager Don't use pager.
--multiline Use multiline editor module (default).
--nomultiline Don't use multiline editor module.
--singleline Use single line editor module.
@@ -30,6 +31,9 @@ Usage: irb.rb [options] [programfile] [arguments]
--nocolorize Don't use color-highlighting.
--autocomplete Use auto-completion (default).
--noautocomplete Don't use auto-completion.
+ --regexp-completor
+ Use regexp based completion (default).
+ --type-completor Use type based completion.
--prompt prompt-mode, --prompt-mode prompt-mode
Set prompt mode. Pre-defined prompt modes are:
'default', 'classic', 'simple', 'inf-ruby', 'xmp', 'null'.
diff --git a/lib/irb/lc/ja/error.rb b/lib/irb/lc/ja/error.rb
index 50d72c4a10..9e2e5b8870 100644
--- a/lib/irb/lc/ja/error.rb
+++ b/lib/irb/lc/ja/error.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/lc/ja/error.rb -
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -12,11 +12,6 @@ module IRB
super("スイッãƒ(#{val})ãŒåˆ†ã‚Šã¾ã›ã‚“")
end
end
- class NotImplementedError < StandardError
- def initialize(val)
- super("`#{val}'ã®å®šç¾©ãŒå¿…è¦ã§ã™")
- end
- end
class CantReturnToNormalMode < StandardError
def initialize
super("Normalモードã«æˆ»ã‚Œã¾ã›ã‚“.")
@@ -52,11 +47,6 @@ module IRB
super("プロンプトモード(#{val})ã¯å®šç¾©ã•ã‚Œã¦ã„ã¾ã›ã‚“.")
end
end
- class IllegalRCGenerator < StandardError
- def initialize
- super("RC_NAME_GENERATORãŒæ­£ã—ã定義ã•ã‚Œã¦ã„ã¾ã›ã‚“.")
- end
- end
# :startdoc:
end
diff --git a/lib/irb/lc/ja/help-message b/lib/irb/lc/ja/help-message
index 1c15d331ea..99f4449b3b 100644
--- a/lib/irb/lc/ja/help-message
+++ b/lib/irb/lc/ja/help-message
@@ -9,10 +9,18 @@ Usage: irb.rb [options] [programfile] [arguments]
-W[level=2] ruby -W ã¨åŒã˜.
--context-mode n æ–°ã—ã„ワークスペースを作æˆã—ãŸæ™‚ã«é–¢é€£ã™ã‚‹ Binding
オブジェクトã®ä½œæˆæ–¹æ³•ã‚’ 0 ã‹ã‚‰ 3 ã®ã„ãšã‚Œã‹ã«è¨­å®šã™ã‚‹.
+ --extra-doc-dir 指定ã—ãŸãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã®ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã‚’追加ã§èª­ã¿è¾¼ã‚€.
--echo 実行çµæžœã‚’表示ã™ã‚‹(デフォルト).
--noecho 実行çµæžœã‚’表示ã—ãªã„.
+ --echo-on-assignment
+ 代入çµæžœã‚’表示ã™ã‚‹.
+ --noecho-on-assignment
+ 代入çµæžœã‚’表示ã—ãªã„.
+ --truncate-echo-on-assignment
+ truncateã•ã‚ŒãŸä»£å…¥çµæžœã‚’表示ã™ã‚‹(デフォルト).
--inspect çµæžœå‡ºåŠ›ã«inspectを用ã„ã‚‹.
--noinspect çµæžœå‡ºåŠ›ã«inspectを用ã„ãªã„.
+ --no-pager ページャを使用ã—ãªã„.
--multiline マルãƒãƒ©ã‚¤ãƒ³ã‚¨ãƒ‡ã‚£ã‚¿ã‚’利用ã™ã‚‹.
--nomultiline マルãƒãƒ©ã‚¤ãƒ³ã‚¨ãƒ‡ã‚£ã‚¿ã‚’利用ã—ãªã„.
--singleline シングルラインエディタを利用ã™ã‚‹.
@@ -21,6 +29,9 @@ Usage: irb.rb [options] [programfile] [arguments]
--nocolorize 色付ã‘を利用ã—ãªã„.
--autocomplete オートコンプリートを利用ã™ã‚‹.
--noautocomplete オートコンプリートを利用ã—ãªã„.
+ --regexp-completor
+ 補完ã«æ­£è¦è¡¨ç¾ã‚’利用ã™ã‚‹.
+ --type-completor 補完ã«åž‹æƒ…報を利用ã™ã‚‹.
--prompt prompt-mode/--prompt-mode prompt-mode
プロンプトモードを切替ãˆã¾ã™. ç¾åœ¨å®šç¾©ã•ã‚Œã¦ã„るプ
ロンプトモードã¯, default, simple, xmp, inf-rubyãŒ
@@ -31,6 +42,8 @@ Usage: irb.rb [options] [programfile] [arguments]
--sample-book-mode/--simple-prompt
éžå¸¸ã«ã‚·ãƒ³ãƒ—ルãªãƒ—ロンプトを用ã„るモードã§ã™.
--noprompt プロンプト表示を行ãªã‚ãªã„.
+ --script スクリプトモード(最åˆã®å¼•æ•°ã‚’スクリプトファイルã¨ã—ã¦æ‰±ã†ã€ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆ)
+ --noscript 引数をargvã¨ã—ã¦æ‰±ã†.
--single-irb irb 中㧠self を実行ã—ã¦å¾—られるオブジェクトをサ
ブ irb ã¨å…±æœ‰ã™ã‚‹.
--tracer コマンド実行時ã«ãƒˆãƒ¬ãƒ¼ã‚¹ã‚’è¡Œãªã†.
diff --git a/lib/irb/locale.rb b/lib/irb/locale.rb
index f94aa0f40b..2abcc7354b 100644
--- a/lib/irb/locale.rb
+++ b/lib/irb/locale.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/locale.rb - internationalization module
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -94,7 +94,7 @@ module IRB # :nodoc:
end
end
- def find(file , paths = $:)
+ def find(file, paths = $:)
dir = File.dirname(file)
dir = "" if dir == "."
base = File.basename(file)
diff --git a/lib/irb/nesting_parser.rb b/lib/irb/nesting_parser.rb
new file mode 100644
index 0000000000..5aa940cc28
--- /dev/null
+++ b/lib/irb/nesting_parser.rb
@@ -0,0 +1,237 @@
+# frozen_string_literal: true
+module IRB
+ module NestingParser
+ IGNORE_TOKENS = %i[on_sp on_ignored_nl on_comment on_embdoc_beg on_embdoc on_embdoc_end]
+
+ # Scan each token and call the given block with array of token and other information for parsing
+ def self.scan_opens(tokens)
+ opens = []
+ pending_heredocs = []
+ first_token_on_line = true
+ tokens.each do |t|
+ skip = false
+ last_tok, state, args = opens.last
+ case state
+ when :in_alias_undef
+ skip = t.event == :on_kw
+ when :in_unquoted_symbol
+ unless IGNORE_TOKENS.include?(t.event)
+ opens.pop
+ skip = true
+ end
+ when :in_lambda_head
+ opens.pop if t.event == :on_tlambeg || (t.event == :on_kw && t.tok == 'do')
+ when :in_method_head
+ unless IGNORE_TOKENS.include?(t.event)
+ next_args = []
+ body = nil
+ if args.include?(:receiver)
+ case t.event
+ when :on_lparen, :on_ivar, :on_gvar, :on_cvar
+ # def (receiver). | def @ivar. | def $gvar. | def @@cvar.
+ next_args << :dot
+ when :on_kw
+ case t.tok
+ when 'self', 'true', 'false', 'nil'
+ # def self(arg) | def self.
+ next_args.push(:arg, :dot)
+ else
+ # def if(arg)
+ skip = true
+ next_args << :arg
+ end
+ when :on_op, :on_backtick
+ # def +(arg)
+ skip = true
+ next_args << :arg
+ when :on_ident, :on_const
+ # def a(arg) | def a.
+ next_args.push(:arg, :dot)
+ end
+ end
+ if args.include?(:dot)
+ # def receiver.name
+ next_args << :name if t.event == :on_period || (t.event == :on_op && t.tok == '::')
+ end
+ if args.include?(:name)
+ if %i[on_ident on_const on_op on_kw on_backtick].include?(t.event)
+ # def name(arg) | def receiver.name(arg)
+ next_args << :arg
+ skip = true
+ end
+ end
+ if args.include?(:arg)
+ case t.event
+ when :on_nl, :on_semicolon
+ # def receiver.f;
+ body = :normal
+ when :on_lparen
+ # def receiver.f()
+ next_args << :eq
+ else
+ if t.event == :on_op && t.tok == '='
+ # def receiver.f =
+ body = :oneliner
+ else
+ # def receiver.f arg
+ next_args << :arg_without_paren
+ end
+ end
+ end
+ if args.include?(:eq)
+ if t.event == :on_op && t.tok == '='
+ body = :oneliner
+ else
+ body = :normal
+ end
+ end
+ if args.include?(:arg_without_paren)
+ if %i[on_semicolon on_nl].include?(t.event)
+ # def f a;
+ body = :normal
+ else
+ # def f a, b
+ next_args << :arg_without_paren
+ end
+ end
+ if body == :oneliner
+ opens.pop
+ elsif body
+ opens[-1] = [last_tok, nil]
+ else
+ opens[-1] = [last_tok, :in_method_head, next_args]
+ end
+ end
+ when :in_for_while_until_condition
+ if t.event == :on_semicolon || t.event == :on_nl || (t.event == :on_kw && t.tok == 'do')
+ skip = true if t.event == :on_kw && t.tok == 'do'
+ opens[-1] = [last_tok, nil]
+ end
+ end
+
+ unless skip
+ case t.event
+ when :on_kw
+ case t.tok
+ when 'begin', 'class', 'module', 'do', 'case'
+ opens << [t, nil]
+ when 'end'
+ opens.pop
+ when 'def'
+ opens << [t, :in_method_head, [:receiver, :name]]
+ when 'if', 'unless'
+ unless t.state.allbits?(Ripper::EXPR_LABEL)
+ opens << [t, nil]
+ end
+ when 'while', 'until'
+ unless t.state.allbits?(Ripper::EXPR_LABEL)
+ opens << [t, :in_for_while_until_condition]
+ end
+ when 'ensure', 'rescue'
+ unless t.state.allbits?(Ripper::EXPR_LABEL)
+ opens.pop
+ opens << [t, nil]
+ end
+ when 'alias'
+ opens << [t, :in_alias_undef, 2]
+ when 'undef'
+ opens << [t, :in_alias_undef, 1]
+ when 'elsif', 'else', 'when'
+ opens.pop
+ opens << [t, nil]
+ when 'for'
+ opens << [t, :in_for_while_until_condition]
+ when 'in'
+ if last_tok&.event == :on_kw && %w[case in].include?(last_tok.tok) && first_token_on_line
+ opens.pop
+ opens << [t, nil]
+ end
+ end
+ when :on_tlambda
+ opens << [t, :in_lambda_head]
+ when :on_lparen, :on_lbracket, :on_lbrace, :on_tlambeg, :on_embexpr_beg, :on_embdoc_beg
+ opens << [t, nil]
+ when :on_rparen, :on_rbracket, :on_rbrace, :on_embexpr_end, :on_embdoc_end
+ opens.pop
+ when :on_heredoc_beg
+ pending_heredocs << t
+ when :on_heredoc_end
+ opens.pop
+ when :on_backtick
+ opens << [t, nil] if t.state.allbits?(Ripper::EXPR_BEG)
+ when :on_tstring_beg, :on_words_beg, :on_qwords_beg, :on_symbols_beg, :on_qsymbols_beg, :on_regexp_beg
+ opens << [t, nil]
+ when :on_tstring_end, :on_regexp_end, :on_label_end
+ opens.pop
+ when :on_symbeg
+ if t.tok == ':'
+ opens << [t, :in_unquoted_symbol]
+ else
+ opens << [t, nil]
+ end
+ end
+ end
+ if t.event == :on_nl || t.event == :on_semicolon
+ first_token_on_line = true
+ elsif t.event != :on_sp
+ first_token_on_line = false
+ end
+ if pending_heredocs.any? && t.tok.include?("\n")
+ pending_heredocs.reverse_each { |t| opens << [t, nil] }
+ pending_heredocs = []
+ end
+ if opens.last && opens.last[1] == :in_alias_undef && !IGNORE_TOKENS.include?(t.event) && t.event != :on_heredoc_end
+ tok, state, arg = opens.pop
+ opens << [tok, state, arg - 1] if arg >= 1
+ end
+ yield t, opens if block_given?
+ end
+ opens.map(&:first) + pending_heredocs.reverse
+ end
+
+ def self.open_tokens(tokens)
+ # scan_opens without block will return a list of open tokens at last token position
+ scan_opens(tokens)
+ end
+
+ # Calculates token information [line_tokens, prev_opens, next_opens, min_depth] for each line.
+ # Example code
+ # ["hello
+ # world"+(
+ # First line
+ # line_tokens: [[lbracket, '['], [tstring_beg, '"'], [tstring_content("hello\nworld"), "hello\n"]]
+ # prev_opens: []
+ # next_tokens: [lbracket, tstring_beg]
+ # min_depth: 0 (minimum at beginning of line)
+ # Second line
+ # line_tokens: [[tstring_content("hello\nworld"), "world"], [tstring_end, '"'], [op, '+'], [lparen, '(']]
+ # prev_opens: [lbracket, tstring_beg]
+ # next_tokens: [lbracket, lparen]
+ # min_depth: 1 (minimum just after tstring_end)
+ def self.parse_by_line(tokens)
+ line_tokens = []
+ prev_opens = []
+ min_depth = 0
+ output = []
+ last_opens = scan_opens(tokens) do |t, opens|
+ depth = t == opens.last&.first ? opens.size - 1 : opens.size
+ min_depth = depth if depth < min_depth
+ if t.tok.include?("\n")
+ t.tok.each_line do |line|
+ line_tokens << [t, line]
+ next if line[-1] != "\n"
+ next_opens = opens.map(&:first)
+ output << [line_tokens, prev_opens, next_opens, min_depth]
+ prev_opens = next_opens
+ min_depth = prev_opens.size
+ line_tokens = []
+ end
+ else
+ line_tokens << [t, t.tok]
+ end
+ end
+ output << [line_tokens, prev_opens, last_opens, min_depth] if line_tokens.any?
+ output
+ end
+ end
+end
diff --git a/lib/irb/notifier.rb b/lib/irb/notifier.rb
index 612de3df16..dc1b9ef14b 100644
--- a/lib/irb/notifier.rb
+++ b/lib/irb/notifier.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# notifier.rb - output methods used by irb
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
diff --git a/lib/irb/output-method.rb b/lib/irb/output-method.rb
index f5ea57111d..69942f47a2 100644
--- a/lib/irb/output-method.rb
+++ b/lib/irb/output-method.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# output-method.rb - output methods used by irb
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -9,16 +9,10 @@ module IRB
# IRB::Notifier. You can define your own output method to use with Irb.new,
# or Context.new
class OutputMethod
- class NotImplementedError < StandardError
- def initialize(val)
- super("Need to define `#{val}'")
- end
- end
-
# Open this method to implement your own output method, raises a
# NotImplementedError if you don't define #print in your own class.
def print(*opts)
- raise NotImplementedError, "print"
+ raise NotImplementedError
end
# Prints the given +opts+, with a newline delimiter.
diff --git a/lib/irb/pager.rb b/lib/irb/pager.rb
new file mode 100644
index 0000000000..3391b32c66
--- /dev/null
+++ b/lib/irb/pager.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+module IRB
+ # The implementation of this class is borrowed from RDoc's lib/rdoc/ri/driver.rb.
+ # Please do NOT use this class directly outside of IRB.
+ class Pager
+ PAGE_COMMANDS = [ENV['RI_PAGER'], ENV['PAGER'], 'less', 'more'].compact.uniq
+
+ class << self
+ def page_content(content, **options)
+ if content_exceeds_screen_height?(content)
+ page(**options) do |io|
+ io.puts content
+ end
+ else
+ $stdout.puts content
+ end
+ end
+
+ def page(retain_content: false)
+ if should_page? && pager = setup_pager(retain_content: retain_content)
+ begin
+ pid = pager.pid
+ yield pager
+ ensure
+ pager.close
+ end
+ else
+ yield $stdout
+ end
+ # When user presses Ctrl-C, IRB would raise `IRB::Abort`
+ # But since Pager is implemented by running paging commands like `less` in another process with `IO.popen`,
+ # the `IRB::Abort` exception only interrupts IRB's execution but doesn't affect the pager
+ # So to properly terminate the pager with Ctrl-C, we need to catch `IRB::Abort` and kill the pager process
+ rescue IRB::Abort
+ Process.kill("TERM", pid) if pid
+ nil
+ rescue Errno::EPIPE
+ end
+
+ private
+
+ def should_page?
+ IRB.conf[:USE_PAGER] && STDIN.tty? && (ENV.key?("TERM") && ENV["TERM"] != "dumb")
+ end
+
+ def content_exceeds_screen_height?(content)
+ screen_height, screen_width = begin
+ Reline.get_screen_size
+ rescue Errno::EINVAL
+ [24, 80]
+ end
+
+ pageable_height = screen_height - 3 # leave some space for previous and the current prompt
+
+ # If the content has more lines than the pageable height
+ content.lines.count > pageable_height ||
+ # Or if the content is a few long lines
+ pageable_height * screen_width < Reline::Unicode.calculate_width(content, true)
+ end
+
+ def setup_pager(retain_content:)
+ require 'shellwords'
+
+ PAGE_COMMANDS.each do |pager_cmd|
+ cmd = Shellwords.split(pager_cmd)
+ next if cmd.empty?
+
+ if cmd.first == 'less'
+ cmd << '-R' unless cmd.include?('-R')
+ cmd << '-X' if retain_content && !cmd.include?('-X')
+ end
+
+ begin
+ io = IO.popen(cmd, 'w')
+ rescue
+ next
+ end
+
+ if $? && $?.pid == io.pid && $?.exited? # pager didn't work
+ next
+ end
+
+ return io
+ end
+
+ nil
+ end
+ end
+ end
+end
diff --git a/lib/irb/ruby-lex.rb b/lib/irb/ruby-lex.rb
index e29d52e47c..cfe36be83f 100644
--- a/lib/irb/ruby-lex.rb
+++ b/lib/irb/ruby-lex.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/ruby-lex.rb - ruby lexcal analyzer
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -6,824 +6,469 @@
require "ripper"
require "jruby" if RUBY_ENGINE == "jruby"
-
-# :stopdoc:
-class RubyLex
-
- class TerminateLineInput < StandardError
- def initialize
- super("Terminate Line Input")
- end
- end
-
- def initialize(context)
- @context = context
- @line_no = 1
- @prompt = nil
- end
-
- def self.compile_with_errors_suppressed(code, line_no: 1)
- begin
- result = yield code, line_no
- rescue ArgumentError
- # Ruby can issue an error for the code if there is an
- # incomplete magic comment for encoding in it. Force an
- # expression with a new line before the code in this
- # case to prevent magic comment handling. To make sure
- # line numbers in the lexed code remain the same,
- # decrease the line number by one.
- code = ";\n#{code}"
- line_no -= 1
- result = yield code, line_no
+require_relative "nesting_parser"
+
+module IRB
+ # :stopdoc:
+ class RubyLex
+ ASSIGNMENT_NODE_TYPES = [
+ # Local, instance, global, class, constant, instance, and index assignment:
+ # "foo = bar",
+ # "@foo = bar",
+ # "$foo = bar",
+ # "@@foo = bar",
+ # "::Foo = bar",
+ # "a::Foo = bar",
+ # "Foo = bar"
+ # "foo.bar = 1"
+ # "foo[1] = bar"
+ :assign,
+
+ # Operation assignment:
+ # "foo += bar"
+ # "foo -= bar"
+ # "foo ||= bar"
+ # "foo &&= bar"
+ :opassign,
+
+ # Multiple assignment:
+ # "foo, bar = 1, 2
+ :massign,
+ ]
+
+ class TerminateLineInput < StandardError
+ def initialize
+ super("Terminate Line Input")
+ end
end
- result
- end
-
- def single_line_command?(code)
- command = code.split(/\s/, 2).first
- @context.symbol_alias?(command) || @context.transform_args?(command)
- end
- # io functions
- def set_input(&block)
- @input = block
- end
-
- def configure_io(io)
- @io = io
- if @io.respond_to?(:check_termination)
- @io.check_termination do |code|
- if Reline::IOGate.in_pasting?
- lex = RubyLex.new(@context)
- rest = lex.check_termination_in_prev_line(code)
- if rest
- Reline.delete_text
- rest.bytes.reverse_each do |c|
- Reline.ungetc(c)
- end
- true
- else
- false
- end
- else
- # Accept any single-line input for symbol aliases or commands that transform args
- next true if single_line_command?(code)
-
- ltype, indent, continue, code_block_open = check_code_state(code)
- if ltype or indent > 0 or continue or code_block_open
- false
- else
- true
- end
- end
+ def self.compile_with_errors_suppressed(code, line_no: 1)
+ begin
+ result = yield code, line_no
+ rescue ArgumentError
+ # Ruby can issue an error for the code if there is an
+ # incomplete magic comment for encoding in it. Force an
+ # expression with a new line before the code in this
+ # case to prevent magic comment handling. To make sure
+ # line numbers in the lexed code remain the same,
+ # decrease the line number by one.
+ code = ";\n#{code}"
+ line_no -= 1
+ result = yield code, line_no
end
+ result
end
- if @io.respond_to?(:dynamic_prompt)
- @io.dynamic_prompt do |lines|
- lines << '' if lines.empty?
- result = []
- tokens = self.class.ripper_lex_without_warning(lines.map{ |l| l + "\n" }.join, context: @context)
- code = String.new
- partial_tokens = []
- unprocessed_tokens = []
- line_num_offset = 0
- tokens.each do |t|
- partial_tokens << t
- unprocessed_tokens << t
- if t.tok.include?("\n")
- t_str = t.tok
- t_str.each_line("\n") do |s|
- code << s
- next unless s.include?("\n")
- ltype, indent, continue, code_block_open = check_state(code, partial_tokens)
- result << @prompt.call(ltype, indent, continue || code_block_open, @line_no + line_num_offset)
- line_num_offset += 1
- end
- unprocessed_tokens = []
- else
- code << t.tok
- end
- end
- unless unprocessed_tokens.empty?
- ltype, indent, continue, code_block_open = check_state(code, unprocessed_tokens)
- result << @prompt.call(ltype, indent, continue || code_block_open, @line_no + line_num_offset)
- end
- result
- end
+ ERROR_TOKENS = [
+ :on_parse_error,
+ :compile_error,
+ :on_assign_error,
+ :on_alias_error,
+ :on_class_name_error,
+ :on_param_error
+ ]
+
+ def self.generate_local_variables_assign_code(local_variables)
+ "#{local_variables.join('=')}=nil;" unless local_variables.empty?
end
- if @io.respond_to?(:auto_indent) and @context.auto_indent_mode
- @io.auto_indent do |lines, line_index, byte_pointer, is_newline|
- if is_newline
- @tokens = self.class.ripper_lex_without_warning(lines[0..line_index].join("\n"), context: @context)
- prev_spaces = find_prev_spaces(line_index)
- depth_difference = check_newline_depth_difference
- depth_difference = 0 if depth_difference < 0
- prev_spaces + depth_difference * 2
- else
- code = line_index.zero? ? '' : lines[0..(line_index - 1)].map{ |l| l + "\n" }.join
- last_line = lines[line_index]&.byteslice(0, byte_pointer)
- code += last_line if last_line
- @tokens = self.class.ripper_lex_without_warning(code, context: @context)
- check_corresponding_token_depth(lines, line_index)
+ # Some part of the code is not included in Ripper's token.
+ # Example: DATA part, token after heredoc_beg when heredoc has unclosed embexpr.
+ # With interpolated tokens, tokens.map(&:tok).join will be equal to code.
+ def self.interpolate_ripper_ignored_tokens(code, tokens)
+ line_positions = [0]
+ code.lines.each do |line|
+ line_positions << line_positions.last + line.bytesize
+ end
+ prev_byte_pos = 0
+ interpolated = []
+ prev_line = 1
+ tokens.each do |t|
+ line, col = t.pos
+ byte_pos = line_positions[line - 1] + col
+ if prev_byte_pos < byte_pos
+ tok = code.byteslice(prev_byte_pos...byte_pos)
+ pos = [prev_line, prev_byte_pos - line_positions[prev_line - 1]]
+ interpolated << Ripper::Lexer::Elem.new(pos, :on_ignored_by_ripper, tok, 0)
+ prev_line += tok.count("\n")
end
+ interpolated << t
+ prev_byte_pos = byte_pos + t.tok.bytesize
+ prev_line += t.tok.count("\n")
end
- end
- end
-
- def set_prompt(&block)
- @prompt = block
- end
-
- ERROR_TOKENS = [
- :on_parse_error,
- :compile_error,
- :on_assign_error,
- :on_alias_error,
- :on_class_name_error,
- :on_param_error
- ]
-
- def self.generate_local_variables_assign_code(local_variables)
- "#{local_variables.join('=')}=nil;" unless local_variables.empty?
- end
-
- def self.ripper_lex_without_warning(code, context: nil)
- verbose, $VERBOSE = $VERBOSE, nil
- lvars_code = generate_local_variables_assign_code(context&.local_variables || [])
- if lvars_code
- code = "#{lvars_code}\n#{code}"
- line_no = 0
- else
- line_no = 1
+ if prev_byte_pos < code.bytesize
+ tok = code.byteslice(prev_byte_pos..)
+ pos = [prev_line, prev_byte_pos - line_positions[prev_line - 1]]
+ interpolated << Ripper::Lexer::Elem.new(pos, :on_ignored_by_ripper, tok, 0)
+ end
+ interpolated
end
- compile_with_errors_suppressed(code, line_no: line_no) do |inner_code, line_no|
- lexer = Ripper::Lexer.new(inner_code, '-', line_no)
- lexer.scan.each_with_object([]) do |t, tokens|
- next if t.pos.first == 0
- prev_tk = tokens.last
- position_overlapped = prev_tk && t.pos[0] == prev_tk.pos[0] && t.pos[1] < prev_tk.pos[1] + prev_tk.tok.bytesize
- if position_overlapped
- tokens[-1] = t if ERROR_TOKENS.include?(prev_tk.event) && !ERROR_TOKENS.include?(t.event)
- else
- tokens << t
- end
+ def self.ripper_lex_without_warning(code, local_variables: [])
+ verbose, $VERBOSE = $VERBOSE, nil
+ lvars_code = generate_local_variables_assign_code(local_variables)
+ original_code = code
+ if lvars_code
+ code = "#{lvars_code}\n#{code}"
+ line_no = 0
+ else
+ line_no = 1
end
- end
- ensure
- $VERBOSE = verbose
- end
- def find_prev_spaces(line_index)
- return 0 if @tokens.size == 0
- md = @tokens[0].tok.match(/(\A +)/)
- prev_spaces = md.nil? ? 0 : md[1].count(' ')
- line_count = 0
- @tokens.each_with_index do |t, i|
- if t.tok.include?("\n")
- line_count += t.tok.count("\n")
- if line_count >= line_index
- return prev_spaces
- end
- next if t.event == :on_tstring_content || t.event == :on_words_sep
- if (@tokens.size - 1) > i
- md = @tokens[i + 1].tok.match(/(\A +)/)
- prev_spaces = md.nil? ? 0 : md[1].count(' ')
+ compile_with_errors_suppressed(code, line_no: line_no) do |inner_code, line_no|
+ lexer = Ripper::Lexer.new(inner_code, '-', line_no)
+ tokens = []
+ lexer.scan.each do |t|
+ next if t.pos.first == 0
+ prev_tk = tokens.last
+ position_overlapped = prev_tk && t.pos[0] == prev_tk.pos[0] && t.pos[1] < prev_tk.pos[1] + prev_tk.tok.bytesize
+ if position_overlapped
+ tokens[-1] = t if ERROR_TOKENS.include?(prev_tk.event) && !ERROR_TOKENS.include?(t.event)
+ else
+ tokens << t
+ end
end
+ interpolate_ripper_ignored_tokens(original_code, tokens)
end
+ ensure
+ $VERBOSE = verbose
end
- prev_spaces
- end
-
- def check_state(code, tokens)
- ltype = process_literal_type(tokens)
- indent = process_nesting_level(tokens)
- continue = process_continue(tokens)
- lvars_code = self.class.generate_local_variables_assign_code(@context.local_variables)
- code = "#{lvars_code}\n#{code}" if lvars_code
- code_block_open = check_code_block(code, tokens)
- [ltype, indent, continue, code_block_open]
- end
-
- def check_code_state(code)
- check_target_code = code.gsub(/\s*\z/, '').concat("\n")
- tokens = self.class.ripper_lex_without_warning(check_target_code, context: @context)
- check_state(check_target_code, tokens)
- end
-
- def save_prompt_to_context_io(ltype, indent, continue, line_num_offset)
- # Implicitly saves prompt string to `@context.io.prompt`. This will be used in the next `@input.call`.
- @prompt.call(ltype, indent, continue, @line_no + line_num_offset)
- end
-
- def readmultiline
- save_prompt_to_context_io(nil, 0, false, 0)
-
- # multiline
- return @input.call if @io.respond_to?(:check_termination)
-
- # nomultiline
- code = ''
- line_offset = 0
- loop do
- line = @input.call
- unless line
- return code.empty? ? nil : code
- end
-
- code << line
- # Accept any single-line input for symbol aliases or commands that transform args
- return code if single_line_command?(code)
- ltype, indent, continue, code_block_open = check_code_state(code)
- return code unless ltype or indent > 0 or continue or code_block_open
-
- line_offset += 1
- save_prompt_to_context_io(ltype, indent, continue, line_offset)
+ def check_code_state(code, local_variables:)
+ tokens = self.class.ripper_lex_without_warning(code, local_variables: local_variables)
+ opens = NestingParser.open_tokens(tokens)
+ [tokens, opens, code_terminated?(code, tokens, opens, local_variables: local_variables)]
end
- end
-
- def each_top_level_statement
- loop do
- code = readmultiline
- break unless code
- if code != "\n"
- code.force_encoding(@io.encoding)
- yield code, @line_no
+ def code_terminated?(code, tokens, opens, local_variables:)
+ case check_code_syntax(code, local_variables: local_variables)
+ when :unrecoverable_error
+ true
+ when :recoverable_error
+ false
+ when :other_error
+ opens.empty? && !should_continue?(tokens)
+ when :valid
+ !should_continue?(tokens)
end
- @line_no += code.count("\n")
- rescue TerminateLineInput
end
- end
- def process_continue(tokens)
- # last token is always newline
- if tokens.size >= 2 and tokens[-2].event == :on_regexp_end
- # end of regexp literal
- return false
- elsif tokens.size >= 2 and tokens[-2].event == :on_semicolon
- return false
- elsif tokens.size >= 2 and tokens[-2].event == :on_kw and ['begin', 'else', 'ensure'].include?(tokens[-2].tok)
- return false
- elsif !tokens.empty? and tokens.last.tok == "\\\n"
- return true
- elsif tokens.size >= 1 and tokens[-1].event == :on_heredoc_end # "EOH\n"
- return false
- elsif tokens.size >= 2 and tokens[-2].state.anybits?(Ripper::EXPR_BEG | Ripper::EXPR_FNAME) and tokens[-2].tok !~ /\A\.\.\.?\z/
- # end of literal except for regexp
- # endless range at end of line is not a continue
- return true
- end
- false
- end
-
- def check_code_block(code, tokens)
- return true if tokens.empty?
- if tokens.last.event == :on_heredoc_beg
- return true
- end
+ def assignment_expression?(code, local_variables:)
+ # Try to parse the code and check if the last of possibly multiple
+ # expressions is an assignment type.
- begin # check if parser error are available
+ # If the expression is invalid, Ripper.sexp should return nil which will
+ # result in false being returned. Any valid expression should return an
+ # s-expression where the second element of the top level array is an
+ # array of parsed expressions. The first element of each expression is the
+ # expression's type.
verbose, $VERBOSE = $VERBOSE, nil
- case RUBY_ENGINE
- when 'ruby'
- self.class.compile_with_errors_suppressed(code) do |inner_code, line_no|
- RubyVM::InstructionSequence.compile(inner_code, nil, nil, line_no)
- end
- when 'jruby'
- JRuby.compile_ir(code)
- else
- catch(:valid) do
- eval("BEGIN { throw :valid, true }\n#{code}")
- false
- end
- end
- rescue EncodingError
- # This is for a hash with invalid encoding symbol, {"\xAE": 1}
- rescue SyntaxError => e
- case e.message
- when /unterminated (?:string|regexp) meets end of file/
- # "unterminated regexp meets end of file"
- #
- # example:
- # /
- #
- # "unterminated string meets end of file"
- #
- # example:
- # '
- return true
- when /syntax error, unexpected end-of-input/
- # "syntax error, unexpected end-of-input, expecting keyword_end"
- #
- # example:
- # if true
- # hoge
- # if false
- # fuga
- # end
- return true
- when /syntax error, unexpected keyword_end/
- # "syntax error, unexpected keyword_end"
- #
- # example:
- # if (
- # end
- #
- # example:
- # end
- return false
- when /syntax error, unexpected '\.'/
- # "syntax error, unexpected '.'"
- #
- # example:
- # .
- return false
- when /unexpected tREGEXP_BEG/
- # "syntax error, unexpected tREGEXP_BEG, expecting keyword_do or '{' or '('"
- #
- # example:
- # method / f /
- return false
- end
+ code = "#{RubyLex.generate_local_variables_assign_code(local_variables) || 'nil;'}\n#{code}"
+ # Get the last node_type of the line. drop(1) is to ignore the local_variables_assign_code part.
+ node_type = Ripper.sexp(code)&.dig(1)&.drop(1)&.dig(-1, 0)
+ ASSIGNMENT_NODE_TYPES.include?(node_type)
ensure
$VERBOSE = verbose
end
- last_lex_state = tokens.last.state
-
- if last_lex_state.allbits?(Ripper::EXPR_BEG)
- return false
- elsif last_lex_state.allbits?(Ripper::EXPR_DOT)
- return true
- elsif last_lex_state.allbits?(Ripper::EXPR_CLASS)
- return true
- elsif last_lex_state.allbits?(Ripper::EXPR_FNAME)
- return true
- elsif last_lex_state.allbits?(Ripper::EXPR_VALUE)
- return true
- elsif last_lex_state.allbits?(Ripper::EXPR_ARG)
- return false
+ def should_continue?(tokens)
+ # Look at the last token and check if IRB need to continue reading next line.
+ # Example code that should continue: `a\` `a +` `a.`
+ # Trailing spaces, newline, comments are skipped
+ return true if tokens.last&.event == :on_sp && tokens.last.tok == "\\\n"
+
+ tokens.reverse_each do |token|
+ case token.event
+ when :on_sp, :on_nl, :on_ignored_nl, :on_comment, :on_embdoc_beg, :on_embdoc, :on_embdoc_end
+ # Skip
+ when :on_regexp_end, :on_heredoc_end, :on_semicolon
+ # State is EXPR_BEG but should not continue
+ return false
+ else
+ # Endless range should not continue
+ return false if token.event == :on_op && token.tok.match?(/\A\.\.\.?\z/)
+
+ # EXPR_DOT and most of the EXPR_BEG should continue
+ return token.state.anybits?(Ripper::EXPR_BEG | Ripper::EXPR_DOT)
+ end
+ end
+ false
end
- false
- end
+ def check_code_syntax(code, local_variables:)
+ lvars_code = RubyLex.generate_local_variables_assign_code(local_variables)
+ code = "#{lvars_code}\n#{code}"
- def process_nesting_level(tokens)
- indent = 0
- in_oneliner_def = nil
- tokens.each_with_index { |t, index|
- # detecting one-liner method definition
- if in_oneliner_def.nil?
- if t.state.allbits?(Ripper::EXPR_ENDFN)
- in_oneliner_def = :ENDFN
- end
- else
- if t.state.allbits?(Ripper::EXPR_ENDFN)
- # continuing
- elsif t.state.allbits?(Ripper::EXPR_BEG)
- if t.tok == '='
- in_oneliner_def = :BODY
+ begin # check if parser error are available
+ verbose, $VERBOSE = $VERBOSE, nil
+ case RUBY_ENGINE
+ when 'ruby'
+ self.class.compile_with_errors_suppressed(code) do |inner_code, line_no|
+ RubyVM::InstructionSequence.compile(inner_code, nil, nil, line_no)
end
+ when 'jruby'
+ JRuby.compile_ir(code)
else
- if in_oneliner_def == :BODY
- # one-liner method definition
- indent -= 1
+ catch(:valid) do
+ eval("BEGIN { throw :valid, true }\n#{code}")
+ false
end
- in_oneliner_def = nil
end
- end
-
- case t.event
- when :on_lbracket, :on_lbrace, :on_lparen, :on_tlambeg
- indent += 1
- when :on_rbracket, :on_rbrace, :on_rparen
- indent -= 1
- when :on_kw
- next if index > 0 and tokens[index - 1].state.allbits?(Ripper::EXPR_FNAME)
- case t.tok
- when 'do'
- syntax_of_do = take_corresponding_syntax_to_kw_do(tokens, index)
- indent += 1 if syntax_of_do == :method_calling
- when 'def', 'case', 'for', 'begin', 'class', 'module'
- indent += 1
- when 'if', 'unless', 'while', 'until'
- # postfix if/unless/while/until must be Ripper::EXPR_LABEL
- indent += 1 unless t.state.allbits?(Ripper::EXPR_LABEL)
- when 'end'
- indent -= 1
+ rescue EncodingError
+ # This is for a hash with invalid encoding symbol, {"\xAE": 1}
+ :unrecoverable_error
+ rescue SyntaxError => e
+ case e.message
+ when /unterminated (?:string|regexp) meets end of file/
+ # "unterminated regexp meets end of file"
+ #
+ # example:
+ # /
+ #
+ # "unterminated string meets end of file"
+ #
+ # example:
+ # '
+ return :recoverable_error
+ when /syntax error, unexpected end-of-input/
+ # "syntax error, unexpected end-of-input, expecting keyword_end"
+ #
+ # example:
+ # if true
+ # hoge
+ # if false
+ # fuga
+ # end
+ return :recoverable_error
+ when /syntax error, unexpected keyword_end/
+ # "syntax error, unexpected keyword_end"
+ #
+ # example:
+ # if (
+ # end
+ #
+ # example:
+ # end
+ return :unrecoverable_error
+ when /syntax error, unexpected '\.'/
+ # "syntax error, unexpected '.'"
+ #
+ # example:
+ # .
+ return :unrecoverable_error
+ when /unexpected tREGEXP_BEG/
+ # "syntax error, unexpected tREGEXP_BEG, expecting keyword_do or '{' or '('"
+ #
+ # example:
+ # method / f /
+ return :unrecoverable_error
+ else
+ return :other_error
end
+ ensure
+ $VERBOSE = verbose
end
- # percent literals are not indented
- }
- indent
- end
+ :valid
+ end
- def is_method_calling?(tokens, index)
- tk = tokens[index]
- if tk.state.anybits?(Ripper::EXPR_CMDARG) and tk.event == :on_ident
- # The target method call to pass the block with "do".
- return true
- elsif tk.state.anybits?(Ripper::EXPR_ARG) and tk.event == :on_ident
- non_sp_index = tokens[0..(index - 1)].rindex{ |t| t.event != :on_sp }
- if non_sp_index
- prev_tk = tokens[non_sp_index]
- if prev_tk.state.anybits?(Ripper::EXPR_DOT) and prev_tk.event == :on_period
- # The target method call with receiver to pass the block with "do".
- return true
+ def calc_indent_level(opens)
+ indent_level = 0
+ opens.each_with_index do |t, index|
+ case t.event
+ when :on_heredoc_beg
+ if opens[index + 1]&.event != :on_heredoc_beg
+ if t.tok.match?(/^<<[~-]/)
+ indent_level += 1
+ else
+ indent_level = 0
+ end
+ end
+ when :on_tstring_beg, :on_regexp_beg, :on_symbeg, :on_backtick
+ # No indent: "", //, :"", ``
+ # Indent: %(), %r(), %i(), %x()
+ indent_level += 1 if t.tok.start_with? '%'
+ when :on_embdoc_beg
+ indent_level = 0
+ else
+ indent_level += 1 unless t.tok == 'alias' || t.tok == 'undef'
end
end
+ indent_level
end
- false
- end
- def take_corresponding_syntax_to_kw_do(tokens, index)
- syntax_of_do = nil
- # Finding a syntax corresponding to "do".
- index.downto(0) do |i|
- tk = tokens[i]
- # In "continue", the token isn't the corresponding syntax to "do".
- non_sp_index = tokens[0..(i - 1)].rindex{ |t| t.event != :on_sp }
- first_in_fomula = false
- if non_sp_index.nil?
- first_in_fomula = true
- elsif [:on_ignored_nl, :on_nl, :on_comment].include?(tokens[non_sp_index].event)
- first_in_fomula = true
- end
- if is_method_calling?(tokens, i)
- syntax_of_do = :method_calling
- break if first_in_fomula
- elsif tk.event == :on_kw && %w{while until for}.include?(tk.tok)
- # A loop syntax in front of "do" found.
- #
- # while cond do # also "until" or "for"
- # end
- #
- # This "do" doesn't increment indent because the loop syntax already
- # incremented.
- syntax_of_do = :loop_syntax
- break if first_in_fomula
- end
+ FREE_INDENT_TOKENS = %i[on_tstring_beg on_backtick on_regexp_beg on_symbeg]
+
+ def free_indent_token?(token)
+ FREE_INDENT_TOKENS.include?(token&.event)
end
- syntax_of_do
- end
- def is_the_in_correspond_to_a_for(tokens, index)
- syntax_of_in = nil
- # Finding a syntax corresponding to "do".
- index.downto(0) do |i|
- tk = tokens[i]
- # In "continue", the token isn't the corresponding syntax to "do".
- non_sp_index = tokens[0..(i - 1)].rindex{ |t| t.event != :on_sp }
- first_in_fomula = false
- if non_sp_index.nil?
- first_in_fomula = true
- elsif [:on_ignored_nl, :on_nl, :on_comment].include?(tokens[non_sp_index].event)
- first_in_fomula = true
- end
- if tk.event == :on_kw && tk.tok == 'for'
- # A loop syntax in front of "do" found.
- #
- # while cond do # also "until" or "for"
- # end
- #
- # This "do" doesn't increment indent because the loop syntax already
- # incremented.
- syntax_of_in = :for
+ # Calculates the difference of pasted code's indent and indent calculated from tokens
+ def indent_difference(lines, line_results, line_index)
+ loop do
+ _tokens, prev_opens, _next_opens, min_depth = line_results[line_index]
+ open_token = prev_opens.last
+ if !open_token || (open_token.event != :on_heredoc_beg && !free_indent_token?(open_token))
+ # If the leading whitespace is an indent, return the difference
+ indent_level = calc_indent_level(prev_opens.take(min_depth))
+ calculated_indent = 2 * indent_level
+ actual_indent = lines[line_index][/^ */].size
+ return actual_indent - calculated_indent
+ elsif open_token.event == :on_heredoc_beg && open_token.tok.match?(/^<<[^-~]/)
+ return 0
+ end
+ # If the leading whitespace is not an indent but part of a multiline token
+ # Calculate base_indent of the multiline token's beginning line
+ line_index = open_token.pos[0] - 1
end
- break if first_in_fomula
end
- syntax_of_in
- end
- def check_newline_depth_difference
- depth_difference = 0
- open_brace_on_line = 0
- in_oneliner_def = nil
- @tokens.each_with_index do |t, index|
- # detecting one-liner method definition
- if in_oneliner_def.nil?
- if t.state.allbits?(Ripper::EXPR_ENDFN)
- in_oneliner_def = :ENDFN
- end
+ def process_indent_level(tokens, lines, line_index, is_newline)
+ line_results = NestingParser.parse_by_line(tokens)
+ result = line_results[line_index]
+ if result
+ _tokens, prev_opens, next_opens, min_depth = result
else
- if t.state.allbits?(Ripper::EXPR_ENDFN)
- # continuing
- elsif t.state.allbits?(Ripper::EXPR_BEG)
- if t.tok == '='
- in_oneliner_def = :BODY
- end
- else
- if in_oneliner_def == :BODY
- # one-liner method definition
- depth_difference -= 1
- end
- in_oneliner_def = nil
- end
+ # When last line is empty
+ prev_opens = next_opens = line_results.last[2]
+ min_depth = next_opens.size
end
- case t.event
- when :on_ignored_nl, :on_nl, :on_comment
- if index != (@tokens.size - 1) and in_oneliner_def != :BODY
- depth_difference = 0
- open_brace_on_line = 0
- end
- next
- when :on_sp
- next
- end
+ # To correctly indent line like `end.map do`, we use shortest open tokens on each line for indent calculation.
+ # Shortest open tokens can be calculated by `opens.take(min_depth)`
+ indent = 2 * calc_indent_level(prev_opens.take(min_depth))
- case t.event
- when :on_lbracket, :on_lbrace, :on_lparen, :on_tlambeg
- depth_difference += 1
- open_brace_on_line += 1
- when :on_rbracket, :on_rbrace, :on_rparen
- depth_difference -= 1 if open_brace_on_line > 0
- when :on_kw
- next if index > 0 and @tokens[index - 1].state.allbits?(Ripper::EXPR_FNAME)
- case t.tok
- when 'do'
- syntax_of_do = take_corresponding_syntax_to_kw_do(@tokens, index)
- depth_difference += 1 if syntax_of_do == :method_calling
- when 'def', 'case', 'for', 'begin', 'class', 'module'
- depth_difference += 1
- when 'if', 'unless', 'while', 'until', 'rescue'
- # postfix if/unless/while/until/rescue must be Ripper::EXPR_LABEL
- unless t.state.allbits?(Ripper::EXPR_LABEL)
- depth_difference += 1
- end
- when 'else', 'elsif', 'ensure', 'when'
- depth_difference += 1
- when 'in'
- unless is_the_in_correspond_to_a_for(@tokens, index)
- depth_difference += 1
- end
- when 'end'
- depth_difference -= 1
- end
- end
- end
- depth_difference
- end
+ preserve_indent = lines[line_index - (is_newline ? 1 : 0)][/^ */].size
- def check_corresponding_token_depth(lines, line_index)
- corresponding_token_depth = nil
- is_first_spaces_of_line = true
- is_first_printable_of_line = true
- spaces_of_nest = []
- spaces_at_line_head = 0
- open_brace_on_line = 0
- in_oneliner_def = nil
-
- if heredoc_scope?
- return lines[line_index][/^ */].length
- end
+ prev_open_token = prev_opens.last
+ next_open_token = next_opens.last
- @tokens.each_with_index do |t, index|
- # detecting one-liner method definition
- if in_oneliner_def.nil?
- if t.state.allbits?(Ripper::EXPR_ENDFN)
- in_oneliner_def = :ENDFN
- end
+ # Calculates base indent for pasted code on the line where prev_open_token is located
+ # irb(main):001:1* if a # base_indent is 2, indent calculated from tokens is 0
+ # irb(main):002:1* if b # base_indent is 6, indent calculated from tokens is 2
+ # irb(main):003:0> c # base_indent is 6, indent calculated from tokens is 4
+ if prev_open_token
+ base_indent = [0, indent_difference(lines, line_results, prev_open_token.pos[0] - 1)].max
else
- if t.state.allbits?(Ripper::EXPR_ENDFN)
- # continuing
- elsif t.state.allbits?(Ripper::EXPR_BEG)
- if t.tok == '='
- in_oneliner_def = :BODY
- end
- else
- if in_oneliner_def == :BODY
- # one-liner method definition
- if is_first_printable_of_line
- corresponding_token_depth = spaces_of_nest.pop
- else
- spaces_of_nest.pop
- corresponding_token_depth = nil
- end
- end
- in_oneliner_def = nil
- end
+ base_indent = 0
end
- case t.event
- when :on_ignored_nl, :on_nl, :on_comment, :on_heredoc_end, :on_embdoc_end
- if in_oneliner_def != :BODY
- corresponding_token_depth = nil
- spaces_at_line_head = 0
- is_first_spaces_of_line = true
- is_first_printable_of_line = true
- open_brace_on_line = 0
+ if free_indent_token?(prev_open_token)
+ if is_newline && prev_open_token.pos[0] == line_index
+ # First newline inside free-indent token
+ base_indent + indent
+ else
+ # Accept any number of indent inside free-indent token
+ preserve_indent
end
- next
- when :on_sp
- spaces_at_line_head = t.tok.count(' ') if is_first_spaces_of_line
- is_first_spaces_of_line = false
- next
- end
-
- case t.event
- when :on_lbracket, :on_lbrace, :on_lparen, :on_tlambeg
- spaces_of_nest.push(spaces_at_line_head + open_brace_on_line * 2)
- open_brace_on_line += 1
- when :on_rbracket, :on_rbrace, :on_rparen
- if is_first_printable_of_line
- corresponding_token_depth = spaces_of_nest.pop
+ elsif prev_open_token&.event == :on_embdoc_beg || next_open_token&.event == :on_embdoc_beg
+ if prev_open_token&.event == next_open_token&.event
+ # Accept any number of indent inside embdoc content
+ preserve_indent
else
- spaces_of_nest.pop
- corresponding_token_depth = nil
+ # =begin or =end
+ 0
end
- open_brace_on_line -= 1
- when :on_kw
- next if index > 0 and @tokens[index - 1].state.allbits?(Ripper::EXPR_FNAME)
- case t.tok
- when 'do'
- syntax_of_do = take_corresponding_syntax_to_kw_do(@tokens, index)
- if syntax_of_do == :method_calling
- spaces_of_nest.push(spaces_at_line_head)
- end
- when 'def', 'case', 'for', 'begin', 'class', 'module'
- spaces_of_nest.push(spaces_at_line_head)
- when 'rescue'
- unless t.state.allbits?(Ripper::EXPR_LABEL)
- corresponding_token_depth = spaces_of_nest.last
- end
- when 'if', 'unless', 'while', 'until'
- # postfix if/unless/while/until must be Ripper::EXPR_LABEL
- unless t.state.allbits?(Ripper::EXPR_LABEL)
- spaces_of_nest.push(spaces_at_line_head)
- end
- when 'else', 'elsif', 'ensure', 'when'
- corresponding_token_depth = spaces_of_nest.last
- when 'in'
- if in_keyword_case_scope?
- corresponding_token_depth = spaces_of_nest.last
- end
- when 'end'
- if is_first_printable_of_line
- corresponding_token_depth = spaces_of_nest.pop
+ elsif prev_open_token&.event == :on_heredoc_beg
+ tok = prev_open_token.tok
+ if prev_opens.size <= next_opens.size
+ if is_newline && lines[line_index].empty? && line_results[line_index - 1][1].last != next_open_token
+ # First line in heredoc
+ tok.match?(/^<<[-~]/) ? base_indent + indent : indent
+ elsif tok.match?(/^<<~/)
+ # Accept extra indent spaces inside `<<~` heredoc
+ [base_indent + indent, preserve_indent].max
else
- spaces_of_nest.pop
- corresponding_token_depth = nil
+ # Accept any number of indent inside other heredoc
+ preserve_indent
end
+ else
+ # Heredoc close
+ prev_line_indent_level = calc_indent_level(prev_opens)
+ tok.match?(/^<<[~-]/) ? base_indent + 2 * (prev_line_indent_level - 1) : 0
end
+ else
+ base_indent + indent
end
- is_first_spaces_of_line = false
- is_first_printable_of_line = false
end
- corresponding_token_depth
- end
-
- def check_string_literal(tokens)
- i = 0
- start_token = []
- end_type = []
- pending_heredocs = []
- while i < tokens.size
- t = tokens[i]
- case t.event
- when *end_type.last
- start_token.pop
- end_type.pop
- when :on_tstring_beg
- start_token << t
- end_type << [:on_tstring_end, :on_label_end]
- when :on_regexp_beg
- start_token << t
- end_type << :on_regexp_end
- when :on_symbeg
- acceptable_single_tokens = %i{on_ident on_const on_op on_cvar on_ivar on_gvar on_kw on_int on_backtick}
- if (i + 1) < tokens.size
- if acceptable_single_tokens.all?{ |st| tokens[i + 1].event != st }
- start_token << t
- end_type << :on_tstring_end
- else
- i += 1
- end
- end
- when :on_backtick
- if t.state.allbits?(Ripper::EXPR_BEG)
- start_token << t
- end_type << :on_tstring_end
- end
- when :on_qwords_beg, :on_words_beg, :on_qsymbols_beg, :on_symbols_beg
- start_token << t
- end_type << :on_tstring_end
- when :on_heredoc_beg
- pending_heredocs << t
- end
- if pending_heredocs.any? && t.tok.include?("\n")
- pending_heredocs.reverse_each do |t|
- start_token << t
- end_type << :on_heredoc_end
- end
- pending_heredocs = []
- end
- i += 1
- end
- pending_heredocs.first || start_token.last
- end
+ LTYPE_TOKENS = %i[
+ on_heredoc_beg on_tstring_beg
+ on_regexp_beg on_symbeg on_backtick
+ on_symbols_beg on_qsymbols_beg
+ on_words_beg on_qwords_beg
+ ]
- def process_literal_type(tokens)
- start_token = check_string_literal(tokens)
- return nil if start_token == ""
-
- case start_token&.event
- when :on_tstring_beg
- case start_token&.tok
- when ?" then ?"
- when /^%.$/ then ?"
- when /^%Q.$/ then ?"
- when ?' then ?'
- when /^%q.$/ then ?'
+ def ltype_from_open_tokens(opens)
+ start_token = opens.reverse_each.find do |tok|
+ LTYPE_TOKENS.include?(tok.event)
end
- when :on_regexp_beg then ?/
- when :on_symbeg then ?:
- when :on_backtick then ?`
- when :on_qwords_beg then ?]
- when :on_words_beg then ?]
- when :on_qsymbols_beg then ?]
- when :on_symbols_beg then ?]
- when :on_heredoc_beg
- start_token&.tok =~ /<<[-~]?(['"`])\w+\1/
- $1 || ?"
- else
- nil
- end
- end
+ return nil unless start_token
- def check_termination_in_prev_line(code)
- tokens = self.class.ripper_lex_without_warning(code, context: @context)
- past_first_newline = false
- index = tokens.rindex do |t|
- # traverse first token before last line
- if past_first_newline
- if t.tok.include?("\n")
- true
+ case start_token&.event
+ when :on_tstring_beg
+ case start_token&.tok
+ when ?" then ?"
+ when /^%.$/ then ?"
+ when /^%Q.$/ then ?"
+ when ?' then ?'
+ when /^%q.$/ then ?'
end
- elsif t.tok.include?("\n")
- past_first_newline = true
- false
+ when :on_regexp_beg then ?/
+ when :on_symbeg then ?:
+ when :on_backtick then ?`
+ when :on_qwords_beg then ?]
+ when :on_words_beg then ?]
+ when :on_qsymbols_beg then ?]
+ when :on_symbols_beg then ?]
+ when :on_heredoc_beg
+ start_token&.tok =~ /<<[-~]?(['"`])\w+\1/
+ $1 || ?"
else
- false
+ nil
end
end
- if index
- first_token = nil
- last_line_tokens = tokens[(index + 1)..(tokens.size - 1)]
- last_line_tokens.each do |t|
- unless [:on_sp, :on_ignored_sp, :on_comment].include?(t.event)
- first_token = t
- break
- end
- end
-
- if first_token.nil?
- return false
- elsif first_token && first_token.state == Ripper::EXPR_DOT
- return false
- else
- tokens_without_last_line = tokens[0..index]
- ltype = process_literal_type(tokens_without_last_line)
- indent = process_nesting_level(tokens_without_last_line)
- continue = process_continue(tokens_without_last_line)
- code_block_open = check_code_block(tokens_without_last_line.map(&:tok).join(''), tokens_without_last_line)
- if ltype or indent > 0 or continue or code_block_open
- return false
+ def check_termination_in_prev_line(code, local_variables:)
+ tokens = self.class.ripper_lex_without_warning(code, local_variables: local_variables)
+ past_first_newline = false
+ index = tokens.rindex do |t|
+ # traverse first token before last line
+ if past_first_newline
+ if t.tok.include?("\n")
+ true
+ end
+ elsif t.tok.include?("\n")
+ past_first_newline = true
+ false
else
- return last_line_tokens.map(&:tok).join('')
+ false
end
end
- end
- false
- end
-
- private
- def heredoc_scope?
- heredoc_tokens = @tokens.select { |t| [:on_heredoc_beg, :on_heredoc_end].include?(t.event) }
- heredoc_tokens[-1]&.event == :on_heredoc_beg
- end
+ if index
+ first_token = nil
+ last_line_tokens = tokens[(index + 1)..(tokens.size - 1)]
+ last_line_tokens.each do |t|
+ unless [:on_sp, :on_ignored_sp, :on_comment].include?(t.event)
+ first_token = t
+ break
+ end
+ end
- def in_keyword_case_scope?
- kw_tokens = @tokens.select { |t| t.event == :on_kw && ['case', 'for', 'end'].include?(t.tok) }
- counter = 0
- kw_tokens.reverse.each do |t|
- if t.tok == 'case'
- return true if counter.zero?
- counter += 1
- elsif t.tok == 'for'
- counter += 1
- elsif t.tok == 'end'
- counter -= 1
+ if first_token && first_token.state != Ripper::EXPR_DOT
+ tokens_without_last_line = tokens[0..index]
+ code_without_last_line = tokens_without_last_line.map(&:tok).join
+ opens_without_last_line = NestingParser.open_tokens(tokens_without_last_line)
+ if code_terminated?(code_without_last_line, tokens_without_last_line, opens_without_last_line, local_variables: local_variables)
+ return last_line_tokens.map(&:tok).join
+ end
+ end
end
+ false
end
- false
end
+ # :startdoc:
end
-# :startdoc:
+
+RubyLex = IRB::RubyLex
+Object.deprecate_constant(:RubyLex)
diff --git a/lib/irb/ruby_logo.aa b/lib/irb/ruby_logo.aa
index a34a3e2f28..61fe22c94a 100644
--- a/lib/irb/ruby_logo.aa
+++ b/lib/irb/ruby_logo.aa
@@ -1,3 +1,4 @@
+TYPE: LARGE
-+smJYYN?mm-
HB"BBYT TQg NggT
@@ -35,3 +36,45 @@
m7 NW H N HSVO1z=?11-
NgTH bB kH WBHWWHBHWmQgg&gggggNNN
NNggggggNN
+TYPE: ASCII
+ ,,,;;;;''''';;;'';,
+ ,,;'' ';;,;;; ',
+ ,,'' ;;'';'''';;;;;;
+ ,;' ;; ',, ;
+ ,;' ,;' ';, ;
+ ;' ,;; ',,,;
+ ,' ,;;,,,,,,,,,,,;;;;
+ ;' ;;';;;; ,;;
+ ;' ,;' ;; '',, ,;;;
+ ;; ,;' ; '';, ,; ;'
+;; ,;;' ;; ;; ;;
+;;, ,,;;' ; ;'; ;;
+;';;,,,,;;;;;;;,,, ;; ,' ; ;;
+; ;;''' ,;'; ''';,,, ; ,;' ;;;;
+;;;;, ; '; ''';;;' ';;;
+;'; ;, ;' '; ,;' ', ;;;
+;;; ; ,; '; ,,' ',, ;;
+;;; '; ;' ';,,'' ';,;;
+ '; ';,; ,,;''''''''';;;;;;,,;;;
+ ';,,;;,,;;;;;;;;;;''''''''''''''
+TYPE: UNICODE
+ ⣀⣤⣴⣾⣿⣿⣿⡛⠛⠛⠛⠛⣻⣿⠿⠛⠛⠶⣤⡀
+ â£€â£´â ¾â ›â ‰â  â ™â£¿â£¶â£¤â£¶â£Ÿâ£‰ ⠈⠻⣦
+ ⣀⣴⠟⠋ ⢸⣿⠟⠻⣯⡙⠛⠛⠛⠶⠶⠶⢶⣽⣇
+ â£ â¡¾â ‹â  â£¾â¡¿ ⠈⠛⢦⣄ ⣿
+ ⣠⡾⠋ ⣰⣿⠃ ⠙⠷⣤⡀ ⣿
+ ⢀⡾⠋ ⣰⣿⡠⠈⠻⣦⣄⢠⣿
+ â£°â Ÿâ  â£´â£¿â£¿â£â£€â£ â£¤â£¤â£¤â£¤â£¤â£¤â£¤â£´â ¶â ¿â£¿â¡
+ â£¼â  â¢€â£¾â£¿â Ÿâ£¿â ¿â£¯â£â  ⣰⣿⡇
+ ⢀⣼⠋ â¢€â£´â£¿â Ÿâ  â¢¸â¡‡ ⠙⠻⢦⣄⡀ ⢠⡿⣿⡇
+â¢€â£¾â¡ â¢€â£´â£¿â Ÿâ  â£¿ ⠉⠻⢶⣄⡀⣰⡟ ⣿⠃
+â£¾â£¿â  â£ â£¶â¡¿â ‹â  â¢¹â¡‡ ⠈⣿⡠⢸⣿
+⣿⣿⡆ ⢀⣠⣴⣿⡿⠋ ⠈⣿ ⢀⡾⠋⣿ ⢸⣿
+⣿⠸⣿⣶⣤⣤⣤⣤⣶⣾⠿⠿⣿⣿⠶⣤⣤⣀⡀ ⢹⡇ â£´â Ÿâ  â£¿â¡€â¢¸â£¿
+â£¿â¢€â£¿â£Ÿâ ›â ‹â ‰â  â¢°â¡Ÿâ ¹â£§ ⠈⠉⠛⠻⠶⢦⣤⣀⡀ ⠈⣿ ⣠⡾⠃ ⢸⡇⢸⡇
+⣿⣾⣿⢿⡄ â£¿â  â ˜â£§ ⠉⠙⠛⠷⣿⣿⡋ ⠸⣇⣸⡇
+⣿⠃⣿⠈⢿⡄ ⣸⠇ ⠘⣧ ⢀⣤⠾⠋⠈⠻⣦⡀ ⣿⣿⡇
+⣿⢸⡠⠈⣷⡀ ⢠⡿ ⠘⣧⡀ â£ â¡´â Ÿâ  â ˆâ »â£¦â£€ ⢿⣿â 
+⢻⣾⡇ ⠘⣷ ⣼⠃ ⠘⣷⣠⣴⠟⠋ ⠙⢷⣄⢸⣿
+ ⠻⣧⡀ ⠘⣧⣰⡠⢀⣠⣤⠶⠛⠉⠛⠛⠛⠛⠛⠛⠻⢶⣶⣶⣶⣶⣶⣤⣤⣽⣿⣿
+ ⠈⠛⠷⢦⣤⣽⣿⣥⣤⣶⣶⡿⠿⠿⠶⠶⠶⠶⠾⠛⠛⠛⠛⠛⠛⠛⠋⠉⠉⠉⠉⠉⠉â 
diff --git a/lib/irb/source_finder.rb b/lib/irb/source_finder.rb
new file mode 100644
index 0000000000..5d7d729d19
--- /dev/null
+++ b/lib/irb/source_finder.rb
@@ -0,0 +1,139 @@
+# frozen_string_literal: true
+
+require_relative "ruby-lex"
+
+module IRB
+ class SourceFinder
+ class EvaluationError < StandardError; end
+
+ class Source
+ attr_reader :file, :line
+ def initialize(file, line, ast_source = nil)
+ @file = file
+ @line = line
+ @ast_source = ast_source
+ end
+
+ def file_exist?
+ File.exist?(@file)
+ end
+
+ def binary_file?
+ # If the line is zero, it means that the target's source is probably in a binary file.
+ @line.zero?
+ end
+
+ def file_content
+ @file_content ||= File.read(@file)
+ end
+
+ def colorized_content
+ if !binary_file? && file_exist?
+ end_line = find_end
+ # To correctly colorize, we need to colorize full content and extract the relevant lines.
+ colored = IRB::Color.colorize_code(file_content)
+ colored.lines[@line - 1...end_line].join
+ elsif @ast_source
+ IRB::Color.colorize_code(@ast_source)
+ end
+ end
+
+ private
+
+ def find_end
+ lex = RubyLex.new
+ code = file_content
+ lines = code.lines[(@line - 1)..-1]
+ tokens = RubyLex.ripper_lex_without_warning(lines.join)
+ prev_tokens = []
+
+ # chunk with line number
+ tokens.chunk { |tok| tok.pos[0] }.each do |lnum, chunk|
+ code = lines[0..lnum].join
+ prev_tokens.concat chunk
+ continue = lex.should_continue?(prev_tokens)
+ syntax = lex.check_code_syntax(code, local_variables: [])
+ if !continue && syntax == :valid
+ return @line + lnum
+ end
+ end
+ @line
+ end
+ end
+
+ private_constant :Source
+
+ def initialize(irb_context)
+ @irb_context = irb_context
+ end
+
+ def find_source(signature, super_level = 0)
+ case signature
+ when /\A(::)?[A-Z]\w*(::[A-Z]\w*)*\z/ # ConstName, ::ConstName, ConstPath::ConstName
+ eval_receiver_or_owner(signature) # trigger autoload
+ *parts, name = signature.split('::', -1)
+ base =
+ if parts.empty? # ConstName
+ find_const_owner(name)
+ elsif parts == [''] # ::ConstName
+ Object
+ else # ConstPath::ConstName
+ eval_receiver_or_owner(parts.join('::'))
+ end
+ file, line = base.const_source_location(name)
+ when /\A(?<owner>[A-Z]\w*(::[A-Z]\w*)*)#(?<method>[^ :.]+)\z/ # Class#method
+ owner = eval_receiver_or_owner(Regexp.last_match[:owner])
+ method = Regexp.last_match[:method]
+ return unless owner.respond_to?(:instance_method)
+ method = method_target(owner, super_level, method, "owner")
+ file, line = method&.source_location
+ when /\A((?<receiver>.+)(\.|::))?(?<method>[^ :.]+)\z/ # method, receiver.method, receiver::method
+ receiver = eval_receiver_or_owner(Regexp.last_match[:receiver] || 'self')
+ method = Regexp.last_match[:method]
+ return unless receiver.respond_to?(method, true)
+ method = method_target(receiver, super_level, method, "receiver")
+ file, line = method&.source_location
+ end
+ return unless file && line
+
+ if File.exist?(file)
+ Source.new(file, line)
+ elsif method
+ # Method defined with eval, probably in IRB session
+ source = RubyVM::AbstractSyntaxTree.of(method)&.source rescue nil
+ Source.new(file, line, source)
+ end
+ rescue EvaluationError
+ nil
+ end
+
+ private
+
+ def method_target(owner_receiver, super_level, method, type)
+ case type
+ when "owner"
+ target_method = owner_receiver.instance_method(method)
+ when "receiver"
+ target_method = owner_receiver.method(method)
+ end
+ super_level.times do |s|
+ target_method = target_method.super_method if target_method
+ end
+ target_method
+ rescue NameError
+ nil
+ end
+
+ def eval_receiver_or_owner(code)
+ context_binding = @irb_context.workspace.binding
+ eval(code, context_binding)
+ rescue NameError
+ raise EvaluationError
+ end
+
+ def find_const_owner(name)
+ module_nesting = @irb_context.workspace.binding.eval('::Module.nesting')
+ module_nesting.find { |mod| mod.const_defined?(name, false) } || module_nesting.find { |mod| mod.const_defined?(name) } || Object
+ end
+ end
+end
diff --git a/lib/irb/src_encoding.rb b/lib/irb/src_encoding.rb
deleted file mode 100644
index 99aea2b43e..0000000000
--- a/lib/irb/src_encoding.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# frozen_string_literal: false
-# DO NOT WRITE ANY MAGIC COMMENT HERE.
-module IRB
- def self.default_src_encoding
- return __ENCODING__
- end
-end
diff --git a/lib/irb/statement.rb b/lib/irb/statement.rb
new file mode 100644
index 0000000000..a3391c12a3
--- /dev/null
+++ b/lib/irb/statement.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+module IRB
+ class Statement
+ attr_reader :code
+
+ def is_assignment?
+ raise NotImplementedError
+ end
+
+ def suppresses_echo?
+ raise NotImplementedError
+ end
+
+ def should_be_handled_by_debugger?
+ raise NotImplementedError
+ end
+
+ class EmptyInput < Statement
+ def is_assignment?
+ false
+ end
+
+ def suppresses_echo?
+ true
+ end
+
+ # Debugger takes empty input to repeat the last command
+ def should_be_handled_by_debugger?
+ true
+ end
+
+ def code
+ ""
+ end
+ end
+
+ class Expression < Statement
+ def initialize(code, is_assignment)
+ @code = code
+ @is_assignment = is_assignment
+ end
+
+ def suppresses_echo?
+ @code.match?(/;\s*\z/)
+ end
+
+ def should_be_handled_by_debugger?
+ true
+ end
+
+ def is_assignment?
+ @is_assignment
+ end
+ end
+
+ class Command < Statement
+ attr_reader :command_class, :arg
+
+ def initialize(original_code, command_class, arg)
+ @code = original_code
+ @command_class = command_class
+ @arg = arg
+ end
+
+ def is_assignment?
+ false
+ end
+
+ def suppresses_echo?
+ false
+ end
+
+ def should_be_handled_by_debugger?
+ require_relative 'command/debug'
+ IRB::Command::DebugCommand > @command_class
+ end
+ end
+ end
+end
diff --git a/lib/irb/version.rb b/lib/irb/version.rb
index a1e15815a2..9a7b12766b 100644
--- a/lib/irb/version.rb
+++ b/lib/irb/version.rb
@@ -1,11 +1,11 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/version.rb - irb version definition file
# by Keiju ISHITSUKA(keiju@ishitsuka.com)
#
module IRB # :nodoc:
- VERSION = "1.7.0"
+ VERSION = "1.12.0"
@RELEASE_VERSION = VERSION
- @LAST_UPDATE_DATE = "2023-06-03"
+ @LAST_UPDATE_DATE = "2024-03-06"
end
diff --git a/lib/irb/workspace.rb b/lib/irb/workspace.rb
index d6fa67053d..d24d1cc38d 100644
--- a/lib/irb/workspace.rb
+++ b/lib/irb/workspace.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/workspace-binding.rb -
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -6,6 +6,8 @@
require "delegate"
+require_relative "helper_method"
+
IRB::TOPLEVEL_BINDING = binding
module IRB # :nodoc:
class WorkSpace
@@ -90,11 +92,11 @@ EOF
IRB.conf[:__MAIN__] = @main
@main.singleton_class.class_eval do
private
- define_method(:exit) do |*a, &b|
- # Do nothing, will be overridden
- end
define_method(:binding, Kernel.instance_method(:binding))
define_method(:local_variables, Kernel.instance_method(:local_variables))
+ # Define empty method to avoid delegator warning, will be overridden.
+ define_method(:exit) {|*a, &b| }
+ define_method(:exit!) {|*a, &b| }
end
@binding = eval("IRB.conf[:__MAIN__].instance_eval('binding', __FILE__, __LINE__)", @binding, *@binding.source_location)
end
@@ -108,6 +110,12 @@ EOF
# <code>IRB.conf[:__MAIN__]</code>
attr_reader :main
+ def load_helper_methods_to_main
+ ancestors = class<<main;ancestors;end
+ main.extend ExtendCommandBundle if !ancestors.include?(ExtendCommandBundle)
+ main.extend HelpersContainer if !ancestors.include?(HelpersContainer)
+ end
+
# Evaluate the given +statements+ within the context of this workspace.
def evaluate(statements, file = __FILE__, line = __LINE__)
eval(statements, @binding, file, line)
@@ -166,4 +174,16 @@ EOF
"\nFrom: #{file} @ line #{pos + 1} :\n\n#{body}#{Color.clear}\n"
end
end
+
+ module HelpersContainer
+ def self.install_helper_methods
+ HelperMethod.helper_methods.each do |name, helper_method_class|
+ define_method name do |*args, **opts, &block|
+ helper_method_class.instance.execute(*args, **opts, &block)
+ end unless method_defined?(name)
+ end
+ end
+
+ install_helper_methods
+ end
end
diff --git a/lib/irb/ws-for-case-2.rb b/lib/irb/ws-for-case-2.rb
index a0f617e4ed..03f42d73d9 100644
--- a/lib/irb/ws-for-case-2.rb
+++ b/lib/irb/ws-for-case-2.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# irb/ws-for-case-2.rb -
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
diff --git a/lib/irb/xmp.rb b/lib/irb/xmp.rb
index 94c700b484..b1bc53283e 100644
--- a/lib/irb/xmp.rb
+++ b/lib/irb/xmp.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# xmp.rb - irb version of gotoken xmp
# by Keiju ISHITSUKA(Nippon Rational Inc.)
@@ -44,8 +44,8 @@ class XMP
# The top-level binding or, optional +bind+ parameter will be used when
# creating the workspace. See WorkSpace.new for more information.
#
- # This uses the +:XMP+ prompt mode, see IRB@Customizing+the+IRB+Prompt for
- # full detail.
+ # This uses the +:XMP+ prompt mode.
+ # See {Custom Prompts}[rdoc-ref:IRB@Custom+Prompts] for more information.
def initialize(bind = nil)
IRB.init_config(nil)
diff --git a/lib/logger.rb b/lib/logger.rb
index acc65ac8bb..4099955ef2 100644
--- a/lib/logger.rb
+++ b/lib/logger.rb
@@ -265,8 +265,7 @@ require_relative 'logger/errors'
# logger.error! # => 3
# logger.fatal! # => 4
#
-# You can retrieve the log level with method
-# {level}[Logger.html#attribute-i-level]:
+# You can retrieve the log level with method #level.
#
# logger.level = Logger::ERROR
# logger.level # => 3
diff --git a/lib/logger/logger.gemspec b/lib/logger/logger.gemspec
index d12db625d9..5e8232e4ab 100644
--- a/lib/logger/logger.gemspec
+++ b/lib/logger/logger.gemspec
@@ -18,9 +18,5 @@ Gem::Specification.new do |spec|
spec.files = Dir.glob("lib/**/*.rb") + ["logger.gemspec"]
spec.require_paths = ["lib"]
- spec.required_ruby_version = ">= 2.3.0"
-
- spec.add_development_dependency "bundler", ">= 0"
- spec.add_development_dependency "rake", ">= 12.3.3"
- spec.add_development_dependency "test-unit"
+ spec.required_ruby_version = ">= 2.5.0"
end
diff --git a/lib/logger/version.rb b/lib/logger/version.rb
index f85c72eed3..202b6e4fba 100644
--- a/lib/logger/version.rb
+++ b/lib/logger/version.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
class Logger
- VERSION = "1.5.3"
+ VERSION = "1.6.0"
end
diff --git a/lib/mkmf.rb b/lib/mkmf.rb
index 20389ed705..73459ffeb9 100644
--- a/lib/mkmf.rb
+++ b/lib/mkmf.rb
@@ -44,6 +44,23 @@ end
# correctly compile and link the C extension to Ruby and a third-party
# library.
module MakeMakefile
+
+ target_rbconfig = nil
+ ARGV.delete_if do |arg|
+ opt = arg.delete_prefix("--target-rbconfig=")
+ unless opt == arg
+ target_rbconfig = opt
+ end
+ end
+ if target_rbconfig
+ # Load the RbConfig for the target platform into this module.
+ # Cross-compiling needs the same version of Ruby.
+ Kernel.load target_rbconfig, self
+ else
+ # The RbConfig for the target platform where the built extension runs.
+ RbConfig = ::RbConfig
+ end
+
#### defer until this module become global-state free.
# def self.extended(obj)
# obj.init_mkmf
@@ -59,6 +76,9 @@ module MakeMakefile
# The makefile configuration using the defaults from when Ruby was built.
CONFIG = RbConfig::MAKEFILE_CONFIG
+
+ ##
+ # The saved original value of +LIB+ environment variable
ORIG_LIBPATH = ENV['LIB']
##
@@ -245,12 +265,16 @@ MESSAGE
CSRCFLAG = CONFIG['CSRCFLAG']
CPPOUTFILE = config_string('CPPOUTFILE') {|str| str.sub(/\bconftest\b/, CONFTEST)}
+ # :startdoc:
+
+ # Removes _files_.
def rm_f(*files)
opt = (Hash === files.last ? [files.pop] : [])
FileUtils.rm_f(Dir[*files.flatten], *opt)
end
module_function :rm_f
+ # Removes _files_ recursively.
def rm_rf(*files)
opt = (Hash === files.last ? [files.pop] : [])
FileUtils.rm_rf(Dir[*files.flatten], *opt)
@@ -265,6 +289,8 @@ MESSAGE
t if times.all? {|n| n <= t}
end
+ # :stopdoc:
+
def split_libs(*strs)
sep = $mswin ? /\s+/ : /\s+(?=-|\z)/
strs.flat_map {|s| s.lstrip.split(sep)}
@@ -390,6 +416,11 @@ MESSAGE
env, *commands = commands if Hash === commands.first
envs.merge!(env) if env
end
+
+ # disable ASAN leak reporting - conftest programs almost always don't bother
+ # to free their memory.
+ envs['ASAN_OPTIONS'] = "detect_leaks=0" unless ENV.key?('ASAN_OPTIONS')
+
return envs, expand[commands]
end
@@ -397,11 +428,19 @@ MESSAGE
envs.map {|e, v| "#{e}=#{v.quote}"}
end
- def xsystem command, opts = nil
+ # :startdoc:
+
+ # call-seq:
+ # xsystem(command, werror: false) -> true or false
+ #
+ # Executes _command_ with expanding variables, and returns the exit
+ # status like as Kernel#system. If _werror_ is true and the error
+ # output is not empty, returns +false+. The output will logged.
+ def xsystem(command, werror: false)
env, command = expand_command(command)
Logging::open do
puts [env_quote(env), command.quote].join(' ')
- if opts and opts[:werror]
+ if werror
result = nil
Logging.postpone do |log|
output = IO.popen(env, command, &:read)
@@ -415,6 +454,7 @@ MESSAGE
end
end
+ # Executes _command_ similarly to xsystem, but yields opened pipe.
def xpopen command, *mode, &block
env, commands = expand_command(command)
command = [env_quote(env), command].join(' ')
@@ -429,6 +469,7 @@ MESSAGE
end
end
+ # Logs _src_
def log_src(src, heading="checked program was")
src = src.split(/^/)
fmt = "%#{src.size.to_s.size}d: %s"
@@ -443,10 +484,15 @@ EOM
EOM
end
+ # Returns the language-dependent source file name for configuration
+ # checks.
def conftest_source
CONFTEST_C
end
+ # Creats temporary source file from +COMMON_HEADERS+ and _src_.
+ # Yields the created source string and uses the returned string as
+ # the source code, if the block is given.
def create_tmpsrc(src)
src = "#{COMMON_HEADERS}\n#{src}"
src = yield(src) if block_given?
@@ -467,6 +513,8 @@ EOM
src
end
+ # :stopdoc:
+
def have_devel?
unless defined? $have_devel
$have_devel = true
@@ -475,7 +523,7 @@ EOM
$have_devel
end
- def try_do(src, command, *opts, &b)
+ def try_do(src, command, **opts, &b)
unless have_devel?
raise <<MSG
The compiler failed to generate an executable file.
@@ -484,7 +532,7 @@ MSG
end
begin
src = create_tmpsrc(src, &b)
- xsystem(command, *opts)
+ xsystem(command, **opts)
ensure
log_src(src)
end
@@ -545,18 +593,17 @@ MSG
}.join
end
+ def werror_flag(opt = nil)
+ config_string("WERRORFLAG") {|flag| opt = opt && !opt.empty? ? "#{opt} #{flag}" : flag}
+ opt
+ end
+
def with_werror(opt, opts = nil)
- if opts
- if opts[:werror] and config_string("WERRORFLAG") {|flag| opt = opt ? "#{opt} #{flag}" : flag}
- (opts = opts.dup).delete(:werror)
- end
- yield(opt, opts)
- else
- yield(opt)
- end
+ opt = werror_flag(opt) if opts and (opts = opts.dup).delete(:werror)
+ yield(opt, opts)
end
- def try_link0(src, opt="", *opts, &b) # :nodoc:
+ def try_link0(src, opt = "", **opts, &b) # :nodoc:
exe = CONFTEST+$EXEEXT
cmd = link_command("", opt)
if $universal
@@ -564,13 +611,13 @@ MSG
Dir.mktmpdir("mkmf_", oldtmpdir = ENV["TMPDIR"]) do |tmpdir|
begin
ENV["TMPDIR"] = tmpdir
- try_do(src, cmd, *opts, &b)
+ try_do(src, cmd, **opts, &b)
ensure
ENV["TMPDIR"] = oldtmpdir
end
end
else
- try_do(src, cmd, *opts, &b)
+ try_do(src, cmd, **opts, &b)
end and File.executable?(exe) or return nil
exe
ensure
@@ -579,31 +626,32 @@ MSG
# Returns whether or not the +src+ can be compiled as a C source and linked
# with its depending libraries successfully. +opt+ is passed to the linker
- # as options. Note that +$CFLAGS+ and +$LDFLAGS+ are also passed to the
- # linker.
+ # as options. Note that <tt>$CFLAGS</tt> and <tt>$LDFLAGS</tt> are also
+ # passed to the linker.
#
# If a block given, it is called with the source before compilation. You can
# modify the source in the block.
#
# [+src+] a String which contains a C source
# [+opt+] a String which contains linker options
- def try_link(src, opt="", *opts, &b)
- exe = try_link0(src, opt, *opts, &b) or return false
+ def try_link(src, opt = "", **opts, &b)
+ exe = try_link0(src, opt, **opts, &b) or return false
MakeMakefile.rm_f exe
true
end
# Returns whether or not the +src+ can be compiled as a C source. +opt+ is
- # passed to the C compiler as options. Note that +$CFLAGS+ is also passed to
- # the compiler.
+ # passed to the C compiler as options. Note that <tt>$CFLAGS</tt> is also
+ # passed to the compiler.
#
# If a block given, it is called with the source before compilation. You can
# modify the source in the block.
#
# [+src+] a String which contains a C source
# [+opt+] a String which contains compiler options
- def try_compile(src, opt="", *opts, &b)
- with_werror(opt, *opts) {|_opt, *| try_do(src, cc_command(_opt), *opts, &b)} and
+ def try_compile(src, opt = "", werror: nil, **opts, &b)
+ opt = werror_flag(opt) if werror
+ try_do(src, cc_command(opt), werror: werror, **opts, &b) and
File.file?("#{CONFTEST}.#{$OBJEXT}")
ensure
MakeMakefile.rm_f "#{CONFTEST}*"
@@ -611,15 +659,15 @@ MSG
# Returns whether or not the +src+ can be preprocessed with the C
# preprocessor. +opt+ is passed to the preprocessor as options. Note that
- # +$CFLAGS+ is also passed to the preprocessor.
+ # <tt>$CFLAGS</tt> is also passed to the preprocessor.
#
# If a block given, it is called with the source before preprocessing. You
# can modify the source in the block.
#
# [+src+] a String which contains a C source
# [+opt+] a String which contains preprocessor options
- def try_cpp(src, opt="", *opts, &b)
- try_do(src, cpp_command(CPPOUTFILE, opt), *opts, &b) and
+ def try_cpp(src, opt = "", **opts, &b)
+ try_do(src, cpp_command(CPPOUTFILE, opt), **opts, &b) and
File.file?("#{CONFTEST}.i")
ensure
MakeMakefile.rm_f "#{CONFTEST}*"
@@ -636,6 +684,14 @@ MSG
end
end
+ # :startdoc:
+
+ # Sets <tt>$CPPFLAGS</tt> to _flags_ and yields. If the block returns a
+ # falsy value, <tt>$CPPFLAGS</tt> is reset to its previous value, remains
+ # set to _flags_ otherwise.
+ #
+ # [+flags+] a C preprocessor flag as a +String+
+ #
def with_cppflags(flags)
cppflags = $CPPFLAGS
$CPPFLAGS = flags.dup
@@ -644,20 +700,29 @@ MSG
$CPPFLAGS = cppflags unless ret
end
- def try_cppflags(flags, opts = {})
- try_header(MAIN_DOES_NOTHING, flags, {:werror => true}.update(opts))
+ # :nodoc:
+ def try_cppflags(flags, werror: true, **opts)
+ try_header(MAIN_DOES_NOTHING, flags, werror: werror, **opts)
end
- def append_cppflags(flags, *opts)
+ # Check whether each given C preprocessor flag is acceptable and append it
+ # to <tt>$CPPFLAGS</tt> if so.
+ #
+ # [+flags+] a C preprocessor flag as a +String+ or an +Array+ of them
+ #
+ def append_cppflags(flags, **opts)
Array(flags).each do |flag|
if checking_for("whether #{flag} is accepted as CPPFLAGS") {
- try_cppflags(flag, *opts)
+ try_cppflags(flag, **opts)
}
$CPPFLAGS << " " << flag
end
end
end
+ # Sets <tt>$CFLAGS</tt> to _flags_ and yields. If the block returns a falsy
+ # value, <tt>$CFLAGS</tt> is reset to its previous value, remains set to
+ # _flags_ otherwise.
def with_cflags(flags)
cflags = $CFLAGS
$CFLAGS = flags.dup
@@ -666,10 +731,14 @@ MSG
$CFLAGS = cflags unless ret
end
- def try_cflags(flags, opts = {})
- try_compile(MAIN_DOES_NOTHING, flags, {:werror => true}.update(opts))
+ # :nodoc:
+ def try_cflags(flags, werror: true, **opts)
+ try_compile(MAIN_DOES_NOTHING, flags, werror: werror, **opts)
end
+ # Sets <tt>$LDFLAGS</tt> to _flags_ and yields. If the block returns a
+ # falsy value, <tt>$LDFLAGS</tt> is reset to its previous value, remains set
+ # to _flags_ otherwise.
def with_ldflags(flags)
ldflags = $LDFLAGS
$LDFLAGS = flags.dup
@@ -678,21 +747,30 @@ MSG
$LDFLAGS = ldflags unless ret
end
- def try_ldflags(flags, opts = {})
- opts = {:werror => true}.update(opts) if $mswin
- try_link(MAIN_DOES_NOTHING, flags, opts)
+ # :nodoc:
+ def try_ldflags(flags, werror: $mswin, **opts)
+ try_link(MAIN_DOES_NOTHING, flags, werror: werror, **opts)
end
- def append_ldflags(flags, *opts)
+ # :startdoc:
+
+ # Check whether each given linker flag is acceptable and append it to
+ # <tt>$LDFLAGS</tt> if so.
+ #
+ # [+flags+] a linker flag as a +String+ or an +Array+ of them
+ #
+ def append_ldflags(flags, **opts)
Array(flags).each do |flag|
if checking_for("whether #{flag} is accepted as LDFLAGS") {
- try_ldflags(flag, *opts)
+ try_ldflags(flag, **opts)
}
$LDFLAGS << " " << flag
end
end
end
+ # :stopdoc:
+
def try_static_assert(expr, headers = nil, opt = "", &b)
headers = cpp_include(headers)
try_compile(<<SRC, opt, &b)
@@ -828,6 +906,8 @@ int t(void) { const volatile void *volatile p; p = &(&#{var})[0]; return !p; }
SRC
end
+ # :startdoc:
+
# Returns whether or not the +src+ can be preprocessed with the C
# preprocessor and matches with +pat+.
#
@@ -866,6 +946,8 @@ SRC
log_src(src)
end
+ # :stopdoc:
+
# This is used internally by the have_macro? method.
def macro_defined?(macro, src, opt = "", &b)
src = src.sub(/[^\n]\z/, "\\&\n")
@@ -885,8 +967,8 @@ SRC
# * the linked file can be invoked as an executable
# * and the executable exits successfully
#
- # +opt+ is passed to the linker as options. Note that +$CFLAGS+ and
- # +$LDFLAGS+ are also passed to the linker.
+ # +opt+ is passed to the linker as options. Note that <tt>$CFLAGS</tt> and
+ # <tt>$LDFLAGS</tt> are also passed to the linker.
#
# If a block given, it is called with the source before compilation. You can
# modify the source in the block.
@@ -958,6 +1040,10 @@ SRC
format(LIBARG, lib) + " " + libs
end
+ # Prints messages to $stdout, if verbose mode.
+ #
+ # Internal use only.
+ #
def message(*s)
unless Logging.quiet and not $VERBOSE
printf(*s)
@@ -989,6 +1075,10 @@ SRC
r
end
+ # Build a message for checking.
+ #
+ # Internal use only.
+ #
def checking_message(target, place = nil, opt = nil)
[["in", place], ["with", opt]].inject("#{target}") do |msg, (pre, noun)|
if noun
@@ -1013,10 +1103,10 @@ SRC
#
# [+flags+] a C compiler flag as a +String+ or an +Array+ of them
#
- def append_cflags(flags, *opts)
+ def append_cflags(flags, **opts)
Array(flags).each do |flag|
if checking_for("whether #{flag} is accepted as CFLAGS") {
- try_cflags(flag, *opts)
+ try_cflags(flag, **opts)
}
$CFLAGS << " " << flag
end
@@ -1250,6 +1340,7 @@ SRC
end
end
+ # :nodoc:
# Returns whether or not the static type +type+ is defined.
#
# See also +have_type+
@@ -1307,6 +1398,7 @@ SRC
end
end
+ # :nodoc:
# Returns whether or not the constant +const+ is defined.
#
# See also +have_const+
@@ -1466,7 +1558,7 @@ SRC
u = "unsigned " if signed > 0
prelude << "extern rbcv_typedef_ foo();"
compat = UNIVERSAL_INTS.find {|t|
- try_compile([prelude, "extern #{u}#{t} foo();"].join("\n"), opts, :werror=>true, &b)
+ try_compile([prelude, "extern #{u}#{t} foo();"].join("\n"), opts, werror: true, &b)
}
end
if compat
@@ -1490,7 +1582,7 @@ SRC
# Used internally by the what_type? method to determine if +type+ is a scalar
# pointer.
def scalar_ptr_type?(type, member = nil, headers = nil, &b)
- try_compile(<<"SRC", &b) # pointer
+ try_compile(<<"SRC", &b)
#{cpp_include(headers)}
/*top*/
volatile #{type} conftestval;
@@ -1503,7 +1595,7 @@ SRC
# Used internally by the what_type? method to determine if +type+ is a scalar
# pointer.
def scalar_type?(type, member = nil, headers = nil, &b)
- try_compile(<<"SRC", &b) # pointer
+ try_compile(<<"SRC", &b)
#{cpp_include(headers)}
/*top*/
volatile #{type} conftestval;
@@ -1525,6 +1617,10 @@ SRC
end
end
+ # :startdoc:
+
+ # Returns a string represents the type of _type_, or _member_ of
+ # _type_ if _member_ is not +nil+.
def what_type?(type, member = nil, headers = nil, &b)
m = "#{type}"
var = val = "*rbcv_var_"
@@ -1584,6 +1680,8 @@ SRC
end
end
+ # :nodoc:
+ #
# This method is used internally by the find_executable method.
#
# Internal use only.
@@ -1622,8 +1720,6 @@ SRC
nil
end
- # :startdoc:
-
# Searches for the executable +bin+ on +path+. The default path is your
# +PATH+ environment variable. If that isn't defined, it will resort to
# searching /usr/local/bin, /usr/ucb, /usr/bin and /bin.
@@ -1792,7 +1888,8 @@ SRC
# application.
#
def dir_config(target, idefault=nil, ldefault=nil)
- if conf = $config_dirs[target]
+ key = [target, idefault, ldefault].compact.join("\0")
+ if conf = $config_dirs[key]
return conf
end
@@ -1802,9 +1899,13 @@ SRC
end
idir = with_config(target + "-include", idefault)
- $arg_config.last[1] ||= "${#{target}-dir}/include"
+ if conf = $arg_config.assoc("--with-#{target}-include")
+ conf[1] ||= "${#{target}-dir}/include"
+ end
ldir = with_config(target + "-lib", ldefault)
- $arg_config.last[1] ||= "${#{target}-dir}/#{_libdir_basename}"
+ if conf = $arg_config.assoc("--with-#{target}-lib")
+ conf[1] ||= "${#{target}-dir}/#{_libdir_basename}"
+ end
idirs = idir ? Array === idir ? idir.dup : idir.split(File::PATH_SEPARATOR) : []
if defaults
@@ -1826,7 +1927,7 @@ SRC
end
$LIBPATH = ldirs | $LIBPATH
- $config_dirs[target] = [idir, ldir]
+ $config_dirs[key] = [idir, ldir]
end
# Returns compile/link information about an installed library in a tuple of <code>[cflags,
@@ -2064,7 +2165,9 @@ ARCH_FLAG = #{$ARCH_FLAG}
DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG)
LDSHARED = #{CONFIG['LDSHARED']}
LDSHAREDXX = #{config_string('LDSHAREDXX') || '$(LDSHARED)'}
+POSTLINK = #{config_string('POSTLINK', RbConfig::CONFIG)}
AR = #{CONFIG['AR']}
+LD = #{CONFIG['LD']}
EXEEXT = #{CONFIG['EXEEXT']}
}
@@ -2372,7 +2475,7 @@ TARGET_ENTRY = #{EXPORT_PREFIX || ''}Init_$(TARGET_NAME)
DLLIB = #{dllib}
EXTSTATIC = #{$static || ""}
STATIC_LIB = #{staticlib unless $static.nil?}
-#{!$extout && defined?($installed_list) ? "INSTALLED_LIST = #{$installed_list}\n" : ""}
+#{!$extout && defined?($installed_list) ? %[INSTALLED_LIST = #{$installed_list}\n] : ""}
TIMESTAMP_DIR = #{$extout && $extmk ? '$(extout)/.timestamp' : '.'}
" #"
# TODO: fixme
@@ -2399,7 +2502,7 @@ TARGET_SO_DIR_TIMESTAMP = #{timestamp_file(sodir, target_prefix)}
mfile.puts(conf)
mfile.print "
all: #{$extout ? "install" : target ? "$(DLLIB)" : "Makefile"}
-static: #{$extmk && !$static ? "all" : "$(STATIC_LIB)#{$extout ? " install-rb" : ""}"}
+static: #{$extmk && !$static ? "all" : %[$(STATIC_LIB)#{$extout ? " install-rb" : ""}]}
.PHONY: all install static install-so install-rb
.PHONY: clean clean-so clean-static clean-rb
" #"
@@ -2430,6 +2533,7 @@ static: #{$extmk && !$static ? "all" : "$(STATIC_LIB)#{$extout ? " install-rb" :
mfile.puts dest
mfile.print "clean-so::\n"
mfile.print "\t-$(Q)$(RM) #{fseprepl[dest]} #{fseprepl[stamp]}\n"
+ mfile.print "\t-$(Q)$(RM_RF) #{fseprepl['$(CLEANLIBS)']}\n"
mfile.print "\t-$(Q)$(RMDIRS) #{fseprepl[dir]}#{$ignore_error}\n"
else
mfile.print "#{f} #{stamp}\n"
@@ -2699,7 +2803,7 @@ MESSAGE
when $mswin
$nmake = ?m if /nmake/i =~ make
end
- $ignore_error = $nmake ? '' : ' 2> /dev/null || true'
+ $ignore_error = " 2> #{File::NULL} || #{$mswin ? 'exit /b0' : 'true'}"
RbConfig::CONFIG["srcdir"] = CONFIG["srcdir"] =
$srcdir = arg_config("--srcdir", File.dirname($0))
@@ -2722,6 +2826,9 @@ MESSAGE
split = Shellwords.method(:shellwords).to_proc
+ ##
+ # The prefix added to exported symbols automatically
+
EXPORT_PREFIX = config_string('EXPORT_PREFIX') {|s| s.strip}
hdr = ['#include "ruby.h"' "\n"]
@@ -2751,6 +2858,10 @@ MESSAGE
# make compile rules
COMPILE_RULES = config_string('COMPILE_RULES', &split) || %w[.%s.%s:]
+
+ ##
+ # Substitution in rules for NMake
+
RULE_SUBST = config_string('RULE_SUBST')
##
@@ -2796,6 +2907,10 @@ MESSAGE
# Argument which will add a library path to the linker
LIBPATHFLAG = config_string('LIBPATHFLAG') || ' -L%s'
+
+ ##
+ # Argument which will add a runtime library path to the linker
+
RPATHFLAG = config_string('RPATHFLAG') || ''
##
@@ -2807,6 +2922,10 @@ MESSAGE
# A C main function which does no work
MAIN_DOES_NOTHING = config_string('MAIN_DOES_NOTHING') || "int main(int argc, char **argv)\n{\n return !!argv[argc];\n}"
+
+ ##
+ # The type names for convertible_int
+
UNIVERSAL_INTS = config_string('UNIVERSAL_INTS') {|s| Shellwords.shellwords(s)} ||
%w[int short long long\ long]
@@ -2837,18 +2956,32 @@ realclean: distclean
@lang = Hash.new(self)
+ ##
+ # Retrieves the module for _name_ language.
def self.[](name)
@lang.fetch(name)
end
+ ##
+ # Defines the module for _name_ language.
def self.[]=(name, mod)
@lang[name] = mod
end
- self["C++"] = Module.new do
+ ##
+ # The language that this module is for
+ LANGUAGE = -"C"
+
+ self[self::LANGUAGE] = self
+
+ cxx = Module.new do
+ # Module for C++
+
include MakeMakefile
extend self
+ # :stopdoc:
+
CONFTEST_CXX = "#{CONFTEST}.#{config_string('CXX_EXT') || CXX_EXT[0]}"
TRY_LINK_CXX = config_string('TRY_LINK_CXX') ||
@@ -2878,7 +3011,12 @@ realclean: distclean
conf = link_config(ldflags, *opts)
RbConfig::expand(TRY_LINK_CXX.dup, conf)
end
+
+ # :startdoc:
end
+
+ cxx::LANGUAGE = -"C++"
+ self[cxx::LANGUAGE] = cxx
end
# MakeMakefile::Global = #
diff --git a/lib/mutex_m.gemspec b/lib/mutex_m.gemspec
deleted file mode 100644
index ebbda2606c..0000000000
--- a/lib/mutex_m.gemspec
+++ /dev/null
@@ -1,28 +0,0 @@
-begin
- require_relative "lib/mutex_m"
-rescue LoadError
- # for Ruby core repository
- require_relative "mutex_m"
-end
-
-Gem::Specification.new do |spec|
- spec.name = "mutex_m"
- spec.version = Mutex_m::VERSION
- spec.authors = ["Keiju ISHITSUKA"]
- spec.email = ["keiju@ruby-lang.org"]
-
- spec.summary = %q{Mixin to extend objects to be handled like a Mutex.}
- spec.description = %q{Mixin to extend objects to be handled like a Mutex.}
- spec.homepage = "https://github.com/ruby/mutex_m"
- spec.licenses = ["Ruby", "BSD-2-Clause"]
-
- spec.files = ["Gemfile", "LICENSE.txt", "README.md", "Rakefile", "lib/mutex_m.rb", "mutex_m.gemspec"]
- spec.bindir = "exe"
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
- spec.require_paths = ["lib"]
- spec.required_ruby_version = '>= 2.5'
-
- spec.add_development_dependency "bundler"
- spec.add_development_dependency "rake"
- spec.add_development_dependency "test-unit"
-end
diff --git a/lib/mutex_m.rb b/lib/mutex_m.rb
deleted file mode 100644
index 4c888d6a17..0000000000
--- a/lib/mutex_m.rb
+++ /dev/null
@@ -1,116 +0,0 @@
-# frozen_string_literal: false
-#
-# mutex_m.rb -
-# $Release Version: 3.0$
-# $Revision: 1.7 $
-# Original from mutex.rb
-# by Keiju ISHITSUKA(keiju@ishitsuka.com)
-# modified by matz
-# patched by akira yamada
-#
-# --
-
-# = mutex_m.rb
-#
-# When 'mutex_m' is required, any object that extends or includes Mutex_m will
-# be treated like a Mutex.
-#
-# Start by requiring the standard library Mutex_m:
-#
-# require "mutex_m.rb"
-#
-# From here you can extend an object with Mutex instance methods:
-#
-# obj = Object.new
-# obj.extend Mutex_m
-#
-# Or mixin Mutex_m into your module to your class inherit Mutex instance
-# methods --- remember to call super() in your class initialize method.
-#
-# class Foo
-# include Mutex_m
-# def initialize
-# # ...
-# super()
-# end
-# # ...
-# end
-# obj = Foo.new
-# # this obj can be handled like Mutex
-#
-module Mutex_m
-
- VERSION = "0.1.2"
- Ractor.make_shareable(VERSION) if defined?(Ractor)
-
- def Mutex_m.define_aliases(cl) # :nodoc:
- cl.alias_method(:locked?, :mu_locked?)
- cl.alias_method(:lock, :mu_lock)
- cl.alias_method(:unlock, :mu_unlock)
- cl.alias_method(:try_lock, :mu_try_lock)
- cl.alias_method(:synchronize, :mu_synchronize)
- end
-
- def Mutex_m.append_features(cl) # :nodoc:
- super
- define_aliases(cl) unless cl.instance_of?(Module)
- end
-
- def Mutex_m.extend_object(obj) # :nodoc:
- super
- obj.mu_extended
- end
-
- def mu_extended # :nodoc:
- unless (defined? locked? and
- defined? lock and
- defined? unlock and
- defined? try_lock and
- defined? synchronize)
- Mutex_m.define_aliases(singleton_class)
- end
- mu_initialize
- end
-
- # See Thread::Mutex#synchronize
- def mu_synchronize(&block)
- @_mutex.synchronize(&block)
- end
-
- # See Thread::Mutex#locked?
- def mu_locked?
- @_mutex.locked?
- end
-
- # See Thread::Mutex#try_lock
- def mu_try_lock
- @_mutex.try_lock
- end
-
- # See Thread::Mutex#lock
- def mu_lock
- @_mutex.lock
- end
-
- # See Thread::Mutex#unlock
- def mu_unlock
- @_mutex.unlock
- end
-
- # See Thread::Mutex#sleep
- def sleep(timeout = nil)
- @_mutex.sleep(timeout)
- end
-
- private
-
- def mu_initialize # :nodoc:
- @_mutex = Thread::Mutex.new
- end
-
- def initialize(*args) # :nodoc:
- mu_initialize
- super
- end
- ruby2_keywords(:initialize) if respond_to?(:ruby2_keywords, true)
-end
diff --git a/lib/net/http.rb b/lib/net/http.rb
index 89277ca49d..6b78c264af 100644
--- a/lib/net/http.rb
+++ b/lib/net/http.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# = net/http.rb
#
@@ -456,6 +456,10 @@ module Net #:nodoc:
#
# == What's Here
#
+ # First, what's elsewhere. Class Net::HTTP:
+ #
+ # - Inherits from {class Object}[rdoc-ref:Object@What-27s+Here].
+ #
# This is a categorized summary of methods and attributes.
#
# === \Net::HTTP Objects
@@ -722,8 +726,7 @@ module Net #:nodoc:
class HTTP < Protocol
# :stopdoc:
- VERSION = "0.3.2"
- Revision = %q$Revision$.split[1]
+ VERSION = "0.4.1"
HTTPVersion = '1.1'
begin
require 'zlib'
@@ -1075,7 +1078,7 @@ module Net #:nodoc:
elsif p_addr == :ENV then
http.proxy_from_env = true
else
- if p_addr && p_no_proxy && !URI::Generic.use_proxy?(p_addr, p_addr, p_port, p_no_proxy)
+ if p_addr && p_no_proxy && !URI::Generic.use_proxy?(address, address, port, p_no_proxy)
p_addr = nil
p_port = nil
end
@@ -1615,8 +1618,8 @@ module Net #:nodoc:
write_timeout: @write_timeout,
continue_timeout: @continue_timeout,
debug_output: @debug_output)
- buf = "CONNECT #{conn_address}:#{@port} HTTP/#{HTTPVersion}\r\n"
- buf << "Host: #{@address}:#{@port}\r\n"
+ buf = +"CONNECT #{conn_address}:#{@port} HTTP/#{HTTPVersion}\r\n" \
+ "Host: #{@address}:#{@port}\r\n"
if proxy_user
credential = ["#{proxy_user}:#{proxy_pass}"].pack('m0')
buf << "Proxy-Authorization: Basic #{credential}\r\n"
@@ -1798,7 +1801,7 @@ module Net #:nodoc:
def proxy_uri # :nodoc:
return if @proxy_uri == false
@proxy_uri ||= URI::HTTP.new(
- "http".freeze, nil, address, port, nil, nil, nil, nil, nil
+ "http", nil, address, port, nil, nil, nil, nil, nil
).find_proxy || false
@proxy_uri || nil
end
@@ -2351,7 +2354,10 @@ module Net #:nodoc:
res
}
res.reading_body(@socket, req.response_body_permitted?) {
- yield res if block_given?
+ if block_given?
+ count = max_retries # Don't restart in the middle of a download
+ yield res
+ end
}
rescue Net::OpenTimeout
raise
diff --git a/lib/net/http/backward.rb b/lib/net/http/backward.rb
index 691e41e4f1..b44577edbd 100644
--- a/lib/net/http/backward.rb
+++ b/lib/net/http/backward.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# for backward compatibility
# :enddoc:
diff --git a/lib/net/http/exceptions.rb b/lib/net/http/exceptions.rb
index 9c425cae16..ceec8f7b0a 100644
--- a/lib/net/http/exceptions.rb
+++ b/lib/net/http/exceptions.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Net
# Net::HTTP exception class.
# You cannot use Net::HTTPExceptions directly; instead, you must use
diff --git a/lib/net/http/generic_request.rb b/lib/net/http/generic_request.rb
index 8877cd04ae..44e329a0c8 100644
--- a/lib/net/http/generic_request.rb
+++ b/lib/net/http/generic_request.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# \HTTPGenericRequest is the parent of the Net::HTTPRequest class.
#
@@ -23,7 +23,7 @@ class Net::HTTPGenericRequest
raise ArgumentError, "no host component for URI" unless (hostname && hostname.length > 0)
@uri = uri_or_path.dup
host = @uri.hostname.dup
- host << ":".freeze << @uri.port.to_s if @uri.port != @uri.default_port
+ host << ":" << @uri.port.to_s if @uri.port != @uri.default_port
@path = uri_or_path.request_uri
raise ArgumentError, "no HTTP request path given" unless @path
else
@@ -212,15 +212,15 @@ class Net::HTTPGenericRequest
return unless @uri
if ssl
- scheme = 'https'.freeze
+ scheme = 'https'
klass = URI::HTTPS
else
- scheme = 'http'.freeze
+ scheme = 'http'
klass = URI::HTTP
end
if host = self['host']
- host.sub!(/:.*/m, ''.freeze)
+ host.sub!(/:.*/m, '')
elsif host = @uri.host
else
host = addr
@@ -316,7 +316,7 @@ class Net::HTTPGenericRequest
boundary ||= SecureRandom.urlsafe_base64(40)
chunked_p = chunked?
- buf = ''
+ buf = +''
params.each do |key, value, h={}|
key = quote_string(key, charset)
filename =
@@ -401,7 +401,7 @@ class Net::HTTPGenericRequest
if /[\r\n]/ =~ reqline
raise ArgumentError, "A Request-Line must not contain CR or LF"
end
- buf = ""
+ buf = +''
buf << reqline << "\r\n"
each_capitalized do |k,v|
buf << "#{k}: #{v}\r\n"
diff --git a/lib/net/http/header.rb b/lib/net/http/header.rb
index b704860c90..6660c8075a 100644
--- a/lib/net/http/header.rb
+++ b/lib/net/http/header.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# The \HTTPHeader module provides access to \HTTP headers.
#
@@ -699,10 +699,14 @@ module Net::HTTPHeader
# res.content_type # => "application/json"
#
def content_type
- return nil unless main_type()
- if sub_type()
- then "#{main_type()}/#{sub_type()}"
- else main_type()
+ main = main_type()
+ return nil unless main
+
+ sub = sub_type()
+ if sub
+ "#{main}/#{sub}"
+ else
+ main
end
end
diff --git a/lib/net/http/proxy_delta.rb b/lib/net/http/proxy_delta.rb
index a2f770ebdb..e7d30def64 100644
--- a/lib/net/http/proxy_delta.rb
+++ b/lib/net/http/proxy_delta.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Net::HTTP::ProxyDelta #:nodoc: internal use only
private
diff --git a/lib/net/http/request.rb b/lib/net/http/request.rb
index e900b8a17a..4a138572e9 100644
--- a/lib/net/http/request.rb
+++ b/lib/net/http/request.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# This class is the base class for \Net::HTTP request classes.
# The class should not be used directly;
diff --git a/lib/net/http/requests.rb b/lib/net/http/requests.rb
index 96cedcabcc..5724164205 100644
--- a/lib/net/http/requests.rb
+++ b/lib/net/http/requests.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# HTTP/1.1 methods --- RFC2616
diff --git a/lib/net/http/response.rb b/lib/net/http/response.rb
index 1d83935c10..40de963868 100644
--- a/lib/net/http/response.rb
+++ b/lib/net/http/response.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# This class is the base class for \Net::HTTP response classes.
#
@@ -273,7 +273,7 @@ class Net::HTTPResponse
def error! #:nodoc:
message = @code
- message += ' ' + @message.dump if @message
+ message = "#{message} #{@message.dump}" if @message
raise error_type().new(message, self)
end
@@ -366,6 +366,7 @@ class Net::HTTPResponse
@body = nil
end
@read = true
+ return if @body.nil?
case enc = @body_encoding
when Encoding, false, nil
@@ -639,7 +640,7 @@ class Net::HTTPResponse
end
def stream_check
- raise IOError, 'attempt to read body out of block' if @socket.closed?
+ raise IOError, 'attempt to read body out of block' if @socket.nil? || @socket.closed?
end
def procdest(dest, block)
@@ -648,7 +649,7 @@ class Net::HTTPResponse
if block
Net::ReadAdapter.new(block)
else
- dest || ''
+ dest || +''
end
end
diff --git a/lib/net/https.rb b/lib/net/https.rb
index d46721c82a..0f23e1fb13 100644
--- a/lib/net/https.rb
+++ b/lib/net/https.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
=begin
= net/https -- SSL/TLS enhancement for Net::HTTP.
diff --git a/lib/net/net-protocol.gemspec b/lib/net/net-protocol.gemspec
index 29ad2504eb..f9fd83f12b 100644
--- a/lib/net/net-protocol.gemspec
+++ b/lib/net/net-protocol.gemspec
@@ -21,6 +21,7 @@ Gem::Specification.new do |spec|
spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = spec.homepage
+ spec.metadata["changelog_uri"] = spec.homepage + "/releases"
# Specify which files should be added to the gem when it is released.
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
diff --git a/lib/net/protocol.rb b/lib/net/protocol.rb
index ea0752a971..197ea09089 100644
--- a/lib/net/protocol.rb
+++ b/lib/net/protocol.rb
@@ -26,7 +26,7 @@ require 'io/wait'
module Net # :nodoc:
class Protocol #:nodoc: internal use only
- VERSION = "0.2.1"
+ VERSION = "0.2.2"
private
def Protocol.protocol_param(name, val)
diff --git a/lib/observer.gemspec b/lib/observer.gemspec
deleted file mode 100644
index 93e61b8c84..0000000000
--- a/lib/observer.gemspec
+++ /dev/null
@@ -1,32 +0,0 @@
-# frozen_string_literal: true
-
-name = File.basename(__FILE__, ".gemspec")
-version = ["lib", Array.new(name.count("-")+1, ".").join("/")].find do |dir|
- break File.foreach(File.join(__dir__, dir, "#{name.tr('-', '/')}.rb")) do |line|
- /^\s*VERSION\s*=\s*"(.*)"/ =~ line and break $1
- end rescue nil
-end
-
-Gem::Specification.new do |spec|
- spec.name = name
- spec.version = version
- spec.authors = ["Yukihiro Matsumoto"]
- spec.email = ["matz@ruby-lang.org"]
-
- spec.summary = %q{Implementation of the Observer object-oriented design pattern.}
- spec.description = spec.summary
- spec.homepage = "https://github.com/ruby/observer"
- spec.licenses = ["Ruby", "BSD-2-Clause"]
-
- spec.metadata["homepage_uri"] = spec.homepage
- spec.metadata["source_code_uri"] = spec.homepage
-
- # Specify which files should be added to the gem when it is released.
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
- `git ls-files -z 2>#{IO::NULL}`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
- end
- spec.bindir = "exe"
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
- spec.require_paths = ["lib"]
-end
diff --git a/lib/observer.rb b/lib/observer.rb
deleted file mode 100644
index ef70e39dd8..0000000000
--- a/lib/observer.rb
+++ /dev/null
@@ -1,229 +0,0 @@
-# frozen_string_literal: true
-#
-# Implementation of the _Observer_ object-oriented design pattern. The
-# following documentation is copied, with modifications, from "Programming
-# Ruby", by Hunt and Thomas; http://www.ruby-doc.org/docs/ProgrammingRuby/html/lib_patterns.html.
-#
-# See Observable for more info.
-
-# The Observer pattern (also known as publish/subscribe) provides a simple
-# mechanism for one object to inform a set of interested third-party objects
-# when its state changes.
-#
-# == Mechanism
-#
-# The notifying class mixes in the +Observable+
-# module, which provides the methods for managing the associated observer
-# objects.
-#
-# The observable object must:
-# * assert that it has +#changed+
-# * call +#notify_observers+
-#
-# An observer subscribes to updates using Observable#add_observer, which also
-# specifies the method called via #notify_observers. The default method for
-# #notify_observers is #update.
-#
-# === Example
-#
-# The following example demonstrates this nicely. A +Ticker+, when run,
-# continually receives the stock +Price+ for its <tt>@symbol</tt>. A +Warner+
-# is a general observer of the price, and two warners are demonstrated, a
-# +WarnLow+ and a +WarnHigh+, which print a warning if the price is below or
-# above their set limits, respectively.
-#
-# The +update+ callback allows the warners to run without being explicitly
-# called. The system is set up with the +Ticker+ and several observers, and the
-# observers do their duty without the top-level code having to interfere.
-#
-# Note that the contract between publisher and subscriber (observable and
-# observer) is not declared or enforced. The +Ticker+ publishes a time and a
-# price, and the warners receive that. But if you don't ensure that your
-# contracts are correct, nothing else can warn you.
-#
-# require "observer"
-#
-# class Ticker ### Periodically fetch a stock price.
-# include Observable
-#
-# def initialize(symbol)
-# @symbol = symbol
-# end
-#
-# def run
-# last_price = nil
-# loop do
-# price = Price.fetch(@symbol)
-# print "Current price: #{price}\n"
-# if price != last_price
-# changed # notify observers
-# last_price = price
-# notify_observers(Time.now, price)
-# end
-# sleep 1
-# end
-# end
-# end
-#
-# class Price ### A mock class to fetch a stock price (60 - 140).
-# def self.fetch(symbol)
-# 60 + rand(80)
-# end
-# end
-#
-# class Warner ### An abstract observer of Ticker objects.
-# def initialize(ticker, limit)
-# @limit = limit
-# ticker.add_observer(self)
-# end
-# end
-#
-# class WarnLow < Warner
-# def update(time, price) # callback for observer
-# if price < @limit
-# print "--- #{time.to_s}: Price below #@limit: #{price}\n"
-# end
-# end
-# end
-#
-# class WarnHigh < Warner
-# def update(time, price) # callback for observer
-# if price > @limit
-# print "+++ #{time.to_s}: Price above #@limit: #{price}\n"
-# end
-# end
-# end
-#
-# ticker = Ticker.new("MSFT")
-# WarnLow.new(ticker, 80)
-# WarnHigh.new(ticker, 120)
-# ticker.run
-#
-# Produces:
-#
-# Current price: 83
-# Current price: 75
-# --- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 75
-# Current price: 90
-# Current price: 134
-# +++ Sun Jun 09 00:10:25 CDT 2002: Price above 120: 134
-# Current price: 134
-# Current price: 112
-# Current price: 79
-# --- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 79
-#
-# === Usage with procs
-#
-# The +#notify_observers+ method can also be used with +proc+s by using
-# the +:call+ as +func+ parameter.
-#
-# The following example illustrates the use of a lambda:
-#
-# require 'observer'
-#
-# class Ticker
-# include Observable
-#
-# def run
-# # logic to retrieve the price (here 77.0)
-# changed
-# notify_observers(77.0)
-# end
-# end
-#
-# ticker = Ticker.new
-# warner = ->(price) { puts "New price received: #{price}" }
-# ticker.add_observer(warner, :call)
-# ticker.run
-module Observable
- VERSION = "0.1.1"
-
- #
- # Add +observer+ as an observer on this object. So that it will receive
- # notifications.
- #
- # +observer+:: the object that will be notified of changes.
- # +func+:: Symbol naming the method that will be called when this Observable
- # has changes.
- #
- # This method must return true for +observer.respond_to?+ and will
- # receive <tt>*arg</tt> when #notify_observers is called, where
- # <tt>*arg</tt> is the value passed to #notify_observers by this
- # Observable
- def add_observer(observer, func=:update)
- @observer_peers = {} unless defined? @observer_peers
- unless observer.respond_to? func
- raise NoMethodError, "observer does not respond to `#{func}'"
- end
- @observer_peers[observer] = func
- end
-
- #
- # Remove +observer+ as an observer on this object so that it will no longer
- # receive notifications.
- #
- # +observer+:: An observer of this Observable
- def delete_observer(observer)
- @observer_peers.delete observer if defined? @observer_peers
- end
-
- #
- # Remove all observers associated with this object.
- #
- def delete_observers
- @observer_peers.clear if defined? @observer_peers
- end
-
- #
- # Return the number of observers associated with this object.
- #
- def count_observers
- if defined? @observer_peers
- @observer_peers.size
- else
- 0
- end
- end
-
- #
- # Set the changed state of this object. Notifications will be sent only if
- # the changed +state+ is +true+.
- #
- # +state+:: Boolean indicating the changed state of this Observable.
- #
- def changed(state=true)
- @observer_state = state
- end
-
- #
- # Returns true if this object's state has been changed since the last
- # #notify_observers call.
- #
- def changed?
- if defined? @observer_state and @observer_state
- true
- else
- false
- end
- end
-
- #
- # Notify observers of a change in state *if* this object's changed state is
- # +true+.
- #
- # This will invoke the method named in #add_observer, passing <tt>*arg</tt>.
- # The changed state is then set to +false+.
- #
- # <tt>*arg</tt>:: Any arguments to pass to the observers.
- def notify_observers(*arg)
- if defined? @observer_state and @observer_state
- if defined? @observer_peers
- @observer_peers.each do |k, v|
- k.__send__(v, *arg)
- end
- end
- @observer_state = false
- end
- end
-
-end
diff --git a/lib/open-uri.rb b/lib/open-uri.rb
index 0052bb49fb..ba2379325f 100644
--- a/lib/open-uri.rb
+++ b/lib/open-uri.rb
@@ -31,6 +31,7 @@ module URI
super
end
end
+ singleton_class.send(:ruby2_keywords, :open) if respond_to?(:ruby2_keywords, true)
end
# OpenURI is an easy-to-use wrapper for Net::HTTP, Net::HTTPS and Net::FTP.
@@ -90,7 +91,7 @@ end
module OpenURI
- VERSION = "0.3.0"
+ VERSION = "0.4.1"
Options = {
:proxy => true,
@@ -107,6 +108,7 @@ module OpenURI
:ftp_active_mode => false,
:redirect => true,
:encoding => nil,
+ :max_redirects => 64,
}
def OpenURI.check_options(options) # :nodoc:
@@ -210,6 +212,7 @@ module OpenURI
end
uri_set = {}
+ max_redirects = options[:max_redirects]
buf = nil
while true
redirect = catch(:open_uri_redirect) {
@@ -237,6 +240,7 @@ module OpenURI
uri = redirect
raise "HTTP redirection loop: #{uri}" if uri_set.include? uri.to_s
uri_set[uri.to_s] = true
+ raise TooManyRedirects.new("Too many redirects", buf.io) if max_redirects && uri_set.size > max_redirects
else
break
end
@@ -391,6 +395,9 @@ module OpenURI
attr_reader :uri
end
+ class TooManyRedirects < HTTPError
+ end
+
class Buffer # :nodoc: all
def initialize
@io = StringIO.new
diff --git a/lib/open3.rb b/lib/open3.rb
index 9652b27194..74d00b86d9 100644
--- a/lib/open3.rb
+++ b/lib/open3.rb
@@ -31,57 +31,189 @@
require 'open3/version'
+# \Module \Open3 supports creating child processes
+# with access to their $stdin, $stdout, and $stderr streams.
+#
+# == What's Here
+#
+# Each of these methods executes a given command in a new process or subshell,
+# or multiple commands in new processes and/or subshells:
+#
+# - Each of these methods executes a single command in a process or subshell,
+# accepts a string for input to $stdin,
+# and returns string output from $stdout, $stderr, or both:
+#
+# - Open3.capture2: Executes the command;
+# returns the string from $stdout.
+# - Open3.capture2e: Executes the command;
+# returns the string from merged $stdout and $stderr.
+# - Open3.capture3: Executes the command;
+# returns strings from $stdout and $stderr.
+#
+# - Each of these methods executes a single command in a process or subshell,
+# and returns pipes for $stdin, $stdout, and/or $stderr:
+#
+# - Open3.popen2: Executes the command;
+# returns pipes for $stdin and $stdout.
+# - Open3.popen2e: Executes the command;
+# returns pipes for $stdin and merged $stdout and $stderr.
+# - Open3.popen3: Executes the command;
+# returns pipes for $stdin, $stdout, and $stderr.
+#
+# - Each of these methods executes one or more commands in processes and/or subshells,
+# returns pipes for the first $stdin, the last $stdout, or both:
+#
+# - Open3.pipeline_r: Returns a pipe for the last $stdout.
+# - Open3.pipeline_rw: Returns pipes for the first $stdin and the last $stdout.
+# - Open3.pipeline_w: Returns a pipe for the first $stdin.
+# - Open3.pipeline_start: Does not wait for processes to complete.
+# - Open3.pipeline: Waits for processes to complete.
+#
+# Each of the methods above accepts:
+#
+# - An optional hash of environment variable names and values;
+# see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
+# - A required string argument that is a +command_line+ or +exe_path+;
+# see {Argument command_line or exe_path}[rdoc-ref:Process@Argument+command_line+or+exe_path].
+# - An optional hash of execution options;
+# see {Execution Options}[rdoc-ref:Process@Execution+Options].
+#
module Open3
- # Open stdin, stdout, and stderr streams and start external executable.
- # In addition, a thread to wait for the started process is created.
- # The thread has a pid method and a thread variable :pid which is the pid of
- # the started process.
+ # :call-seq:
+ # Open3.popen3([env, ] command_line, options = {}) -> [stdin, stdout, stderr, wait_thread]
+ # Open3.popen3([env, ] exe_path, *args, options = {}) -> [stdin, stdout, stderr, wait_thread]
+ # Open3.popen3([env, ] command_line, options = {}) {|stdin, stdout, stderr, wait_thread| ... } -> object
+ # Open3.popen3([env, ] exe_path, *args, options = {}) {|stdin, stdout, stderr, wait_thread| ... } -> object
+ #
+ # Basically a wrapper for
+ # {Process.spawn}[rdoc-ref:Process.spawn]
+ # that:
+ #
+ # - Creates a child process, by calling Process.spawn with the given arguments.
+ # - Creates streams +stdin+, +stdout+, and +stderr+,
+ # which are the standard input, standard output, and standard error streams
+ # in the child process.
+ # - Creates thread +wait_thread+ that waits for the child process to exit;
+ # the thread has method +pid+, which returns the process ID
+ # of the child process.
+ #
+ # With no block given, returns the array
+ # <tt>[stdin, stdout, stderr, wait_thread]</tt>.
+ # The caller should close each of the three returned streams.
+ #
+ # stdin, stdout, stderr, wait_thread = Open3.popen3('echo')
+ # # => [#<IO:fd 8>, #<IO:fd 10>, #<IO:fd 12>, #<Process::Waiter:0x00007f58d5428f58 run>]
+ # stdin.close
+ # stdout.close
+ # stderr.close
+ # wait_thread.pid # => 2210481
+ # wait_thread.value # => #<Process::Status: pid 2210481 exit 0>
+ #
+ # With a block given, calls the block with the four variables
+ # (three streams and the wait thread)
+ # and returns the block's return value.
+ # The caller need not close the streams:
+ #
+ # Open3.popen3('echo') do |stdin, stdout, stderr, wait_thread|
+ # p stdin
+ # p stdout
+ # p stderr
+ # p wait_thread
+ # p wait_thread.pid
+ # p wait_thread.value
+ # end
#
- # Block form:
+ # Output:
#
- # Open3.popen3([env,] cmd... [, opts]) {|stdin, stdout, stderr, wait_thr|
- # pid = wait_thr.pid # pid of the started process.
- # ...
- # exit_status = wait_thr.value # Process::Status object returned.
- # }
+ # #<IO:fd 6>
+ # #<IO:fd 7>
+ # #<IO:fd 9>
+ # #<Process::Waiter:0x00007f58d53606e8 sleep>
+ # 2211047
+ # #<Process::Status: pid 2211047 exit 0>
#
- # Non-block form:
+ # Like Process.spawn, this method has potential security vulnerabilities
+ # if called with untrusted input;
+ # see {Command Injection}[rdoc-ref:command_injection.rdoc@Command+Injection].
#
- # stdin, stdout, stderr, wait_thr = Open3.popen3([env,] cmd... [, opts])
- # pid = wait_thr[:pid] # pid of the started process
- # ...
- # stdin.close # stdin, stdout and stderr should be closed explicitly in this form.
- # stdout.close
- # stderr.close
- # exit_status = wait_thr.value # Process::Status object returned.
+ # Unlike Process.spawn, this method waits for the child process to exit
+ # before returning, so the caller need not do so.
+ #
+ # If the first argument is a hash, it becomes leading argument +env+
+ # in the call to Process.spawn;
+ # see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
+ #
+ # If the last argument is a hash, it becomes trailing argument +options+
+ # in the call to Process.spawn;
+ # see {Execution Options}[rdoc-ref:Process@Execution+Options].
+ #
+ # The single required argument is one of the following:
+ #
+ # - +command_line+ if it is a string,
+ # and if it begins with a shell reserved word or special built-in,
+ # or if it contains one or more metacharacters.
+ # - +exe_path+ otherwise.
+ #
+ # <b>Argument +command_line+</b>
#
- # The parameters env, cmd, and opts are passed to Process.spawn.
- # A commandline string and a list of argument strings can be accepted as follows:
+ # \String argument +command_line+ is a command line to be passed to a shell;
+ # it must begin with a shell reserved word, begin with a special built-in,
+ # or contain meta characters:
#
- # Open3.popen3("echo abc") {|i, o, e, t| ... }
- # Open3.popen3("echo", "abc") {|i, o, e, t| ... }
- # Open3.popen3(["echo", "argv0"], "abc") {|i, o, e, t| ... }
+ # Open3.popen3('if true; then echo "Foo"; fi') {|*args| p args } # Shell reserved word.
+ # Open3.popen3('echo') {|*args| p args } # Built-in.
+ # Open3.popen3('date > date.tmp') {|*args| p args } # Contains meta character.
#
- # If the last parameter, opts, is a Hash, it is recognized as an option for Process.spawn.
+ # Output (similar for each call above):
#
- # Open3.popen3("pwd", :chdir=>"/") {|i,o,e,t|
- # p o.read.chomp #=> "/"
- # }
+ # [#<IO:(closed)>, #<IO:(closed)>, #<IO:(closed)>, #<Process::Waiter:0x00007f58d52f28c8 dead>]
#
- # wait_thr.value waits for the termination of the process.
- # The block form also waits for the process when it returns.
+ # The command line may also contain arguments and options for the command:
+ #
+ # Open3.popen3('echo "Foo"') { |i, o, e, t| o.gets }
+ # "Foo\n"
+ #
+ # <b>Argument +exe_path+</b>
+ #
+ # Argument +exe_path+ is one of the following:
+ #
+ # - The string path to an executable to be called.
+ # - A 2-element array containing the path to an executable
+ # and the string to be used as the name of the executing process.
+ #
+ # Example:
#
- # Closing stdin, stdout and stderr does not wait for the process to complete.
+ # Open3.popen3('/usr/bin/date') { |i, o, e, t| o.gets }
+ # # => "Wed Sep 27 02:56:44 PM CDT 2023\n"
#
- # You should be careful to avoid deadlocks.
- # Since pipes are fixed length buffers,
- # Open3.popen3("prog") {|i, o, e, t| o.read } deadlocks if
- # the program generates too much output on stderr.
- # You should read stdout and stderr simultaneously (using threads or IO.select).
- # However, if you don't need stderr output, you can use Open3.popen2.
- # If merged stdout and stderr output is not a problem, you can use Open3.popen2e.
- # If you really need stdout and stderr output as separate strings, you can consider Open3.capture3.
+ # Ruby invokes the executable directly, with no shell and no shell expansion:
+ #
+ # Open3.popen3('doesnt_exist') { |i, o, e, t| o.gets } # Raises Errno::ENOENT
+ #
+ # If one or more +args+ is given, each is an argument or option
+ # to be passed to the executable:
+ #
+ # Open3.popen3('echo', 'C #') { |i, o, e, t| o.gets }
+ # # => "C #\n"
+ # Open3.popen3('echo', 'hello', 'world') { |i, o, e, t| o.gets }
+ # # => "hello world\n"
+ #
+ # Take care to avoid deadlocks.
+ # Output streams +stdout+ and +stderr+ have fixed-size buffers,
+ # so reading extensively from one but not the other can cause a deadlock
+ # when the unread buffer fills.
+ # To avoid that, +stdout+ and +stderr+ should be read simultaneously
+ # (using threads or IO.select).
+ #
+ # Related:
+ #
+ # - Open3.popen2: Makes the standard input and standard output streams
+ # of the child process available as separate streams,
+ # with no access to the standard error stream.
+ # - Open3.popen2e: Makes the standard input and the merge
+ # of the standard output and standard error streams
+ # of the child process available as separate streams.
#
def popen3(*cmd, &block)
if Hash === cmd.last
@@ -104,45 +236,131 @@ module Open3
end
module_function :popen3
- # Open3.popen2 is similar to Open3.popen3 except that it doesn't create a pipe for
- # the standard error stream.
+ # :call-seq:
+ # Open3.popen2([env, ] command_line, options = {}) -> [stdin, stdout, wait_thread]
+ # Open3.popen2([env, ] exe_path, *args, options = {}) -> [stdin, stdout, wait_thread]
+ # Open3.popen2([env, ] command_line, options = {}) {|stdin, stdout, wait_thread| ... } -> object
+ # Open3.popen2([env, ] exe_path, *args, options = {}) {|stdin, stdout, wait_thread| ... } -> object
+ #
+ # Basically a wrapper for
+ # {Process.spawn}[rdoc-ref:Process.spawn]
+ # that:
+ #
+ # - Creates a child process, by calling Process.spawn with the given arguments.
+ # - Creates streams +stdin+ and +stdout+,
+ # which are the standard input and standard output streams
+ # in the child process.
+ # - Creates thread +wait_thread+ that waits for the child process to exit;
+ # the thread has method +pid+, which returns the process ID
+ # of the child process.
+ #
+ # With no block given, returns the array
+ # <tt>[stdin, stdout, wait_thread]</tt>.
+ # The caller should close each of the two returned streams.
+ #
+ # stdin, stdout, wait_thread = Open3.popen2('echo')
+ # # => [#<IO:fd 6>, #<IO:fd 7>, #<Process::Waiter:0x00007f58d52dbe98 run>]
+ # stdin.close
+ # stdout.close
+ # wait_thread.pid # => 2263572
+ # wait_thread.value # => #<Process::Status: pid 2263572 exit 0>
+ #
+ # With a block given, calls the block with the three variables
+ # (two streams and the wait thread)
+ # and returns the block's return value.
+ # The caller need not close the streams:
+ #
+ # Open3.popen2('echo') do |stdin, stdout, wait_thread|
+ # p stdin
+ # p stdout
+ # p wait_thread
+ # p wait_thread.pid
+ # p wait_thread.value
+ # end
#
- # Block form:
+ # Output:
#
- # Open3.popen2([env,] cmd... [, opts]) {|stdin, stdout, wait_thr|
- # pid = wait_thr.pid # pid of the started process.
- # ...
- # exit_status = wait_thr.value # Process::Status object returned.
- # }
+ # #<IO:fd 6>
+ # #<IO:fd 7>
+ # #<Process::Waiter:0x00007f58d59a34b0 sleep>
+ # 2263636
+ # #<Process::Status: pid 2263636 exit 0>
#
- # Non-block form:
+ # Like Process.spawn, this method has potential security vulnerabilities
+ # if called with untrusted input;
+ # see {Command Injection}[rdoc-ref:command_injection.rdoc@Command+Injection].
#
- # stdin, stdout, wait_thr = Open3.popen2([env,] cmd... [, opts])
- # ...
- # stdin.close # stdin and stdout should be closed explicitly in this form.
- # stdout.close
+ # Unlike Process.spawn, this method waits for the child process to exit
+ # before returning, so the caller need not do so.
+ #
+ # If the first argument is a hash, it becomes leading argument +env+
+ # in the call to Process.spawn;
+ # see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
+ #
+ # If the last argument is a hash, it becomes trailing argument +options+
+ # in the call to Process.spawn;
+ # see {Execution Options}[rdoc-ref:Process@Execution+Options].
+ #
+ # The single required argument is one of the following:
+ #
+ # - +command_line+ if it is a string,
+ # and if it begins with a shell reserved word or special built-in,
+ # or if it contains one or more metacharacters.
+ # - +exe_path+ otherwise.
#
- # See Process.spawn for the optional hash arguments _env_ and _opts_.
+ # <b>Argument +command_line+</b>
+ #
+ # \String argument +command_line+ is a command line to be passed to a shell;
+ # it must begin with a shell reserved word, begin with a special built-in,
+ # or contain meta characters:
+ #
+ # Open3.popen2('if true; then echo "Foo"; fi') {|*args| p args } # Shell reserved word.
+ # Open3.popen2('echo') {|*args| p args } # Built-in.
+ # Open3.popen2('date > date.tmp') {|*args| p args } # Contains meta character.
+ #
+ # Output (similar for each call above):
+ #
+ # # => [#<IO:(closed)>, #<IO:(closed)>, #<Process::Waiter:0x00007f7577dfe410 dead>]
+ #
+ # The command line may also contain arguments and options for the command:
+ #
+ # Open3.popen2('echo "Foo"') { |i, o, t| o.gets }
+ # "Foo\n"
+ #
+ # <b>Argument +exe_path+</b>
+ #
+ # Argument +exe_path+ is one of the following:
+ #
+ # - The string path to an executable to be called.
+ # - A 2-element array containing the path to an executable
+ # and the string to be used as the name of the executing process.
#
# Example:
#
- # Open3.popen2("wc -c") {|i,o,t|
- # i.print "answer to life the universe and everything"
- # i.close
- # p o.gets #=> "42\n"
- # }
+ # Open3.popen2('/usr/bin/date') { |i, o, t| o.gets }
+ # # => "Thu Sep 28 09:41:06 AM CDT 2023\n"
+ #
+ # Ruby invokes the executable directly, with no shell and no shell expansion:
+ #
+ # Open3.popen2('doesnt_exist') { |i, o, t| o.gets } # Raises Errno::ENOENT
+ #
+ # If one or more +args+ is given, each is an argument or option
+ # to be passed to the executable:
+ #
+ # Open3.popen2('echo', 'C #') { |i, o, t| o.gets }
+ # # => "C #\n"
+ # Open3.popen2('echo', 'hello', 'world') { |i, o, t| o.gets }
+ # # => "hello world\n"
#
- # Open3.popen2("bc -q") {|i,o,t|
- # i.puts "obase=13"
- # i.puts "6 * 9"
- # p o.gets #=> "42\n"
- # }
#
- # Open3.popen2("dc") {|i,o,t|
- # i.print "42P"
- # i.close
- # p o.read #=> "*"
- # }
+ # Related:
+ #
+ # - Open3.popen2e: Makes the standard input and the merge
+ # of the standard output and standard error streams
+ # of the child process available as separate streams.
+ # - Open3.popen3: Makes the standard input, standard output,
+ # and standard error streams
+ # of the child process available as separate streams.
#
def popen2(*cmd, &block)
if Hash === cmd.last
@@ -162,36 +380,130 @@ module Open3
end
module_function :popen2
- # Open3.popen2e is similar to Open3.popen3 except that it merges
- # the standard output stream and the standard error stream.
+ # :call-seq:
+ # Open3.popen2e([env, ] command_line, options = {}) -> [stdin, stdout_and_stderr, wait_thread]
+ # Open3.popen2e([env, ] exe_path, *args, options = {}) -> [stdin, stdout_and_stderr, wait_thread]
+ # Open3.popen2e([env, ] command_line, options = {}) {|stdin, stdout_and_stderr, wait_thread| ... } -> object
+ # Open3.popen2e([env, ] exe_path, *args, options = {}) {|stdin, stdout_and_stderr, wait_thread| ... } -> object
+ #
+ # Basically a wrapper for
+ # {Process.spawn}[rdoc-ref:Process.spawn]
+ # that:
+ #
+ # - Creates a child process, by calling Process.spawn with the given arguments.
+ # - Creates streams +stdin+, +stdout_and_stderr+,
+ # which are the standard input and the merge of the standard output
+ # and standard error streams in the child process.
+ # - Creates thread +wait_thread+ that waits for the child process to exit;
+ # the thread has method +pid+, which returns the process ID
+ # of the child process.
+ #
+ # With no block given, returns the array
+ # <tt>[stdin, stdout_and_stderr, wait_thread]</tt>.
+ # The caller should close each of the two returned streams.
+ #
+ # stdin, stdout_and_stderr, wait_thread = Open3.popen2e('echo')
+ # # => [#<IO:fd 6>, #<IO:fd 7>, #<Process::Waiter:0x00007f7577da4398 run>]
+ # stdin.close
+ # stdout_and_stderr.close
+ # wait_thread.pid # => 2274600
+ # wait_thread.value # => #<Process::Status: pid 2274600 exit 0>
+ #
+ # With a block given, calls the block with the three variables
+ # (two streams and the wait thread)
+ # and returns the block's return value.
+ # The caller need not close the streams:
+ #
+ # Open3.popen2e('echo') do |stdin, stdout_and_stderr, wait_thread|
+ # p stdin
+ # p stdout_and_stderr
+ # p wait_thread
+ # p wait_thread.pid
+ # p wait_thread.value
+ # end
#
- # Block form:
+ # Output:
#
- # Open3.popen2e([env,] cmd... [, opts]) {|stdin, stdout_and_stderr, wait_thr|
- # pid = wait_thr.pid # pid of the started process.
- # ...
- # exit_status = wait_thr.value # Process::Status object returned.
- # }
+ # #<IO:fd 6>
+ # #<IO:fd 7>
+ # #<Process::Waiter:0x00007f75777578c8 sleep>
+ # 2274763
+ # #<Process::Status: pid 2274763 exit 0>
#
- # Non-block form:
+ # Like Process.spawn, this method has potential security vulnerabilities
+ # if called with untrusted input;
+ # see {Command Injection}[rdoc-ref:command_injection.rdoc@Command+Injection].
#
- # stdin, stdout_and_stderr, wait_thr = Open3.popen2e([env,] cmd... [, opts])
- # ...
- # stdin.close # stdin and stdout_and_stderr should be closed explicitly in this form.
- # stdout_and_stderr.close
+ # Unlike Process.spawn, this method waits for the child process to exit
+ # before returning, so the caller need not do so.
+ #
+ # If the first argument is a hash, it becomes leading argument +env+
+ # in the call to Process.spawn;
+ # see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
+ #
+ # If the last argument is a hash, it becomes trailing argument +options+
+ # in the call to Process.spawn;
+ # see {Execution Options}[rdoc-ref:Process@Execution+Options].
+ #
+ # The single required argument is one of the following:
+ #
+ # - +command_line+ if it is a string,
+ # and if it begins with a shell reserved word or special built-in,
+ # or if it contains one or more metacharacters.
+ # - +exe_path+ otherwise.
+ #
+ # <b>Argument +command_line+</b>
+ #
+ # \String argument +command_line+ is a command line to be passed to a shell;
+ # it must begin with a shell reserved word, begin with a special built-in,
+ # or contain meta characters:
+ #
+ # Open3.popen2e('if true; then echo "Foo"; fi') {|*args| p args } # Shell reserved word.
+ # Open3.popen2e('echo') {|*args| p args } # Built-in.
+ # Open3.popen2e('date > date.tmp') {|*args| p args } # Contains meta character.
+ #
+ # Output (similar for each call above):
+ #
+ # # => [#<IO:(closed)>, #<IO:(closed)>, #<Process::Waiter:0x00007f7577d8a1f0 dead>]
#
- # See Process.spawn for the optional hash arguments _env_ and _opts_.
+ # The command line may also contain arguments and options for the command:
+ #
+ # Open3.popen2e('echo "Foo"') { |i, o_and_e, t| o_and_e.gets }
+ # "Foo\n"
+ #
+ # <b>Argument +exe_path+</b>
+ #
+ # Argument +exe_path+ is one of the following:
+ #
+ # - The string path to an executable to be called.
+ # - A 2-element array containing the path to an executable
+ # and the string to be used as the name of the executing process.
#
# Example:
- # # check gcc warnings
- # source = "foo.c"
- # Open3.popen2e("gcc", "-Wall", source) {|i,oe,t|
- # oe.each {|line|
- # if /warning/ =~ line
- # ...
- # end
- # }
- # }
+ #
+ # Open3.popen2e('/usr/bin/date') { |i, o_and_e, t| o_and_e.gets }
+ # # => "Thu Sep 28 01:58:45 PM CDT 2023\n"
+ #
+ # Ruby invokes the executable directly, with no shell and no shell expansion:
+ #
+ # Open3.popen2e('doesnt_exist') { |i, o_and_e, t| o_and_e.gets } # Raises Errno::ENOENT
+ #
+ # If one or more +args+ is given, each is an argument or option
+ # to be passed to the executable:
+ #
+ # Open3.popen2e('echo', 'C #') { |i, o_and_e, t| o_and_e.gets }
+ # # => "C #\n"
+ # Open3.popen2e('echo', 'hello', 'world') { |i, o_and_e, t| o_and_e.gets }
+ # # => "hello world\n"
+ #
+ # Related:
+ #
+ # - Open3.popen2: Makes the standard input and standard output streams
+ # of the child process available as separate streams,
+ # with no access to the standard error stream.
+ # - Open3.popen3: Makes the standard input, standard output,
+ # and standard error streams
+ # of the child process available as separate streams.
#
def popen2e(*cmd, &block)
if Hash === cmd.last
@@ -238,44 +550,100 @@ module Open3
private :popen_run
end
- # Open3.capture3 captures the standard output and the standard error of a command.
+ # :call-seq:
+ # Open3.capture3([env, ] command_line, options = {}) -> [stdout_s, stderr_s, status]
+ # Open3.capture3([env, ] exe_path, *args, options = {}) -> [stdout_s, stderr_s, status]
#
- # stdout_str, stderr_str, status = Open3.capture3([env,] cmd... [, opts])
+ # Basically a wrapper for Open3.popen3 that:
#
- # The arguments env, cmd and opts are passed to Open3.popen3 except
- # <code>opts[:stdin_data]</code> and <code>opts[:binmode]</code>. See Process.spawn.
+ # - Creates a child process, by calling Open3.popen3 with the given arguments
+ # (except for certain entries in hash +options+; see below).
+ # - Returns as strings +stdout_s+ and +stderr_s+ the standard output
+ # and standard error of the child process.
+ # - Returns as +status+ a <tt>Process::Status</tt> object
+ # that represents the exit status of the child process.
#
- # If <code>opts[:stdin_data]</code> is specified, it is sent to the command's standard input.
+ # Returns the array <tt>[stdout_s, stderr_s, status]</tt>:
#
- # If <code>opts[:binmode]</code> is true, internal pipes are set to binary mode.
+ # stdout_s, stderr_s, status = Open3.capture3('echo "Foo"')
+ # # => ["Foo\n", "", #<Process::Status: pid 2281954 exit 0>]
#
- # Examples:
+ # Like Process.spawn, this method has potential security vulnerabilities
+ # if called with untrusted input;
+ # see {Command Injection}[rdoc-ref:command_injection.rdoc@Command+Injection].
#
- # # dot is a command of graphviz.
- # graph = <<'End'
- # digraph g {
- # a -> b
- # }
- # End
- # drawn_graph, dot_log = Open3.capture3("dot -v", :stdin_data=>graph)
+ # Unlike Process.spawn, this method waits for the child process to exit
+ # before returning, so the caller need not do so.
#
- # o, e, s = Open3.capture3("echo abc; sort >&2", :stdin_data=>"foo\nbar\nbaz\n")
- # p o #=> "abc\n"
- # p e #=> "bar\nbaz\nfoo\n"
- # p s #=> #<Process::Status: pid 32682 exit 0>
+ # If the first argument is a hash, it becomes leading argument +env+
+ # in the call to Open3.popen3;
+ # see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
#
- # # generate a thumbnail image using the convert command of ImageMagick.
- # # However, if the image is really stored in a file,
- # # system("convert", "-thumbnail", "80", "png:#{filename}", "png:-") is better
- # # because of reduced memory consumption.
- # # But if the image is stored in a DB or generated by the gnuplot Open3.capture2 example,
- # # Open3.capture3 should be considered.
- # #
- # image = File.read("/usr/share/openclipart/png/animals/mammals/sheep-md-v0.1.png", :binmode=>true)
- # thumbnail, err, s = Open3.capture3("convert -thumbnail 80 png:- png:-", :stdin_data=>image, :binmode=>true)
- # if s.success?
- # STDOUT.binmode; print thumbnail
- # end
+ # If the last argument is a hash, it becomes trailing argument +options+
+ # in the call to Open3.popen3;
+ # see {Execution Options}[rdoc-ref:Process@Execution+Options].
+ #
+ # The hash +options+ is given;
+ # two options have local effect in method Open3.capture3:
+ #
+ # - If entry <tt>options[:stdin_data]</tt> exists, the entry is removed
+ # and its string value is sent to the command's standard input:
+ #
+ # Open3.capture3('tee', stdin_data: 'Foo')
+ # # => ["Foo", "", #<Process::Status: pid 2319575 exit 0>]
+ #
+ # - If entry <tt>options[:binmode]</tt> exists, the entry is removed and
+ # the internal streams are set to binary mode.
+ #
+ # The single required argument is one of the following:
+ #
+ # - +command_line+ if it is a string,
+ # and if it begins with a shell reserved word or special built-in,
+ # or if it contains one or more metacharacters.
+ # - +exe_path+ otherwise.
+ #
+ # <b>Argument +command_line+</b>
+ #
+ # \String argument +command_line+ is a command line to be passed to a shell;
+ # it must begin with a shell reserved word, begin with a special built-in,
+ # or contain meta characters:
+ #
+ # Open3.capture3('if true; then echo "Foo"; fi') # Shell reserved word.
+ # # => ["Foo\n", "", #<Process::Status: pid 2282025 exit 0>]
+ # Open3.capture3('echo') # Built-in.
+ # # => ["\n", "", #<Process::Status: pid 2282092 exit 0>]
+ # Open3.capture3('date > date.tmp') # Contains meta character.
+ # # => ["", "", #<Process::Status: pid 2282110 exit 0>]
+ #
+ # The command line may also contain arguments and options for the command:
+ #
+ # Open3.capture3('echo "Foo"')
+ # # => ["Foo\n", "", #<Process::Status: pid 2282092 exit 0>]
+ #
+ # <b>Argument +exe_path+</b>
+ #
+ # Argument +exe_path+ is one of the following:
+ #
+ # - The string path to an executable to be called.
+ # - A 2-element array containing the path to an executable
+ # and the string to be used as the name of the executing process.
+ #
+ # Example:
+ #
+ # Open3.capture3('/usr/bin/date')
+ # # => ["Thu Sep 28 05:03:51 PM CDT 2023\n", "", #<Process::Status: pid 2282300 exit 0>]
+ #
+ # Ruby invokes the executable directly, with no shell and no shell expansion:
+ #
+ # Open3.capture3('doesnt_exist') # Raises Errno::ENOENT
+ #
+ # If one or more +args+ is given, each is an argument or option
+ # to be passed to the executable:
+ #
+ # Open3.capture3('echo', 'C #')
+ # # => ["C #\n", "", #<Process::Status: pid 2282368 exit 0>]
+ # Open3.capture3('echo', 'hello', 'world')
+ # # => ["hello world\n", "", #<Process::Status: pid 2282372 exit 0>]
#
def capture3(*cmd)
if Hash === cmd.last
@@ -309,34 +677,100 @@ module Open3
end
module_function :capture3
- # Open3.capture2 captures the standard output of a command.
+ # :call-seq:
+ # Open3.capture2([env, ] command_line, options = {}) -> [stdout_s, status]
+ # Open3.capture2([env, ] exe_path, *args, options = {}) -> [stdout_s, status]
+ #
+ # Basically a wrapper for Open3.popen3 that:
+ #
+ # - Creates a child process, by calling Open3.popen3 with the given arguments
+ # (except for certain entries in hash +options+; see below).
+ # - Returns as string +stdout_s+ the standard output of the child process.
+ # - Returns as +status+ a <tt>Process::Status</tt> object
+ # that represents the exit status of the child process.
+ #
+ # Returns the array <tt>[stdout_s, status]</tt>:
+ #
+ # stdout_s, status = Open3.capture2('echo "Foo"')
+ # # => ["Foo\n", #<Process::Status: pid 2326047 exit 0>]
+ #
+ # Like Process.spawn, this method has potential security vulnerabilities
+ # if called with untrusted input;
+ # see {Command Injection}[rdoc-ref:command_injection.rdoc@Command+Injection].
+ #
+ # Unlike Process.spawn, this method waits for the child process to exit
+ # before returning, so the caller need not do so.
#
- # stdout_str, status = Open3.capture2([env,] cmd... [, opts])
+ # If the first argument is a hash, it becomes leading argument +env+
+ # in the call to Open3.popen3;
+ # see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
#
- # The arguments env, cmd and opts are passed to Open3.popen3 except
- # <code>opts[:stdin_data]</code> and <code>opts[:binmode]</code>. See Process.spawn.
+ # If the last argument is a hash, it becomes trailing argument +options+
+ # in the call to Open3.popen3;
+ # see {Execution Options}[rdoc-ref:Process@Execution+Options].
#
- # If <code>opts[:stdin_data]</code> is specified, it is sent to the command's standard input.
+ # The hash +options+ is given;
+ # two options have local effect in method Open3.capture2:
#
- # If <code>opts[:binmode]</code> is true, internal pipes are set to binary mode.
+ # - If entry <tt>options[:stdin_data]</tt> exists, the entry is removed
+ # and its string value is sent to the command's standard input:
+ #
+ # Open3.capture2('tee', stdin_data: 'Foo')
+ #
+ # # => ["Foo", #<Process::Status: pid 2326087 exit 0>]
+ #
+ # - If entry <tt>options[:binmode]</tt> exists, the entry is removed and
+ # the internal streams are set to binary mode.
+ #
+ # The single required argument is one of the following:
+ #
+ # - +command_line+ if it is a string,
+ # and if it begins with a shell reserved word or special built-in,
+ # or if it contains one or more metacharacters.
+ # - +exe_path+ otherwise.
+ #
+ # <b>Argument +command_line+</b>
+ #
+ # \String argument +command_line+ is a command line to be passed to a shell;
+ # it must begin with a shell reserved word, begin with a special built-in,
+ # or contain meta characters:
+ #
+ # Open3.capture2('if true; then echo "Foo"; fi') # Shell reserved word.
+ # # => ["Foo\n", #<Process::Status: pid 2326131 exit 0>]
+ # Open3.capture2('echo') # Built-in.
+ # # => ["\n", #<Process::Status: pid 2326139 exit 0>]
+ # Open3.capture2('date > date.tmp') # Contains meta character.
+ # # => ["", #<Process::Status: pid 2326174 exit 0>]
+ #
+ # The command line may also contain arguments and options for the command:
+ #
+ # Open3.capture2('echo "Foo"')
+ # # => ["Foo\n", #<Process::Status: pid 2326183 exit 0>]
+ #
+ # <b>Argument +exe_path+</b>
+ #
+ # Argument +exe_path+ is one of the following:
+ #
+ # - The string path to an executable to be called.
+ # - A 2-element array containing the path to an executable
+ # and the string to be used as the name of the executing process.
#
# Example:
#
- # # factor is a command for integer factorization.
- # o, s = Open3.capture2("factor", :stdin_data=>"42")
- # p o #=> "42: 2 3 7\n"
- #
- # # generate x**2 graph in png using gnuplot.
- # gnuplot_commands = <<"End"
- # set terminal png
- # plot x**2, "-" with lines
- # 1 14
- # 2 1
- # 3 8
- # 4 5
- # e
- # End
- # image, s = Open3.capture2("gnuplot", :stdin_data=>gnuplot_commands, :binmode=>true)
+ # Open3.capture2('/usr/bin/date')
+ # # => ["Fri Sep 29 01:00:39 PM CDT 2023\n", #<Process::Status: pid 2326222 exit 0>]
+ #
+ # Ruby invokes the executable directly, with no shell and no shell expansion:
+ #
+ # Open3.capture2('doesnt_exist') # Raises Errno::ENOENT
+ #
+ # If one or more +args+ is given, each is an argument or option
+ # to be passed to the executable:
+ #
+ # Open3.capture2('echo', 'C #')
+ # # => ["C #\n", #<Process::Status: pid 2326267 exit 0>]
+ # Open3.capture2('echo', 'hello', 'world')
+ # # => ["hello world\n", #<Process::Status: pid 2326299 exit 0>]
#
def capture2(*cmd)
if Hash === cmd.last
@@ -370,21 +804,100 @@ module Open3
end
module_function :capture2
- # Open3.capture2e captures the standard output and the standard error of a command.
+ # :call-seq:
+ # Open3.capture2e([env, ] command_line, options = {}) -> [stdout_and_stderr_s, status]
+ # Open3.capture2e([env, ] exe_path, *args, options = {}) -> [stdout_and_stderr_s, status]
+ #
+ # Basically a wrapper for Open3.popen3 that:
+ #
+ # - Creates a child process, by calling Open3.popen3 with the given arguments
+ # (except for certain entries in hash +options+; see below).
+ # - Returns as string +stdout_and_stderr_s+ the merged standard output
+ # and standard error of the child process.
+ # - Returns as +status+ a <tt>Process::Status</tt> object
+ # that represents the exit status of the child process.
+ #
+ # Returns the array <tt>[stdout_and_stderr_s, status]</tt>:
+ #
+ # stdout_and_stderr_s, status = Open3.capture2e('echo "Foo"')
+ # # => ["Foo\n", #<Process::Status: pid 2371692 exit 0>]
+ #
+ # Like Process.spawn, this method has potential security vulnerabilities
+ # if called with untrusted input;
+ # see {Command Injection}[rdoc-ref:command_injection.rdoc@Command+Injection].
+ #
+ # Unlike Process.spawn, this method waits for the child process to exit
+ # before returning, so the caller need not do so.
+ #
+ # If the first argument is a hash, it becomes leading argument +env+
+ # in the call to Open3.popen3;
+ # see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
+ #
+ # If the last argument is a hash, it becomes trailing argument +options+
+ # in the call to Open3.popen3;
+ # see {Execution Options}[rdoc-ref:Process@Execution+Options].
+ #
+ # The hash +options+ is given;
+ # two options have local effect in method Open3.capture2e:
+ #
+ # - If entry <tt>options[:stdin_data]</tt> exists, the entry is removed
+ # and its string value is sent to the command's standard input:
#
- # stdout_and_stderr_str, status = Open3.capture2e([env,] cmd... [, opts])
+ # Open3.capture2e('tee', stdin_data: 'Foo')
+ # # => ["Foo", #<Process::Status: pid 2371732 exit 0>]
#
- # The arguments env, cmd and opts are passed to Open3.popen3 except
- # <code>opts[:stdin_data]</code> and <code>opts[:binmode]</code>. See Process.spawn.
+ # - If entry <tt>options[:binmode]</tt> exists, the entry is removed and
+ # the internal streams are set to binary mode.
#
- # If <code>opts[:stdin_data]</code> is specified, it is sent to the command's standard input.
+ # The single required argument is one of the following:
#
- # If <code>opts[:binmode]</code> is true, internal pipes are set to binary mode.
+ # - +command_line+ if it is a string,
+ # and if it begins with a shell reserved word or special built-in,
+ # or if it contains one or more metacharacters.
+ # - +exe_path+ otherwise.
+ #
+ # <b>Argument +command_line+</b>
+ #
+ # \String argument +command_line+ is a command line to be passed to a shell;
+ # it must begin with a shell reserved word, begin with a special built-in,
+ # or contain meta characters:
+ #
+ # Open3.capture2e('if true; then echo "Foo"; fi') # Shell reserved word.
+ # # => ["Foo\n", #<Process::Status: pid 2371740 exit 0>]
+ # Open3.capture2e('echo') # Built-in.
+ # # => ["\n", #<Process::Status: pid 2371774 exit 0>]
+ # Open3.capture2e('date > date.tmp') # Contains meta character.
+ # # => ["", #<Process::Status: pid 2371812 exit 0>]
+ #
+ # The command line may also contain arguments and options for the command:
+ #
+ # Open3.capture2e('echo "Foo"')
+ # # => ["Foo\n", #<Process::Status: pid 2326183 exit 0>]
+ #
+ # <b>Argument +exe_path+</b>
+ #
+ # Argument +exe_path+ is one of the following:
+ #
+ # - The string path to an executable to be called.
+ # - A 2-element array containing the path to an executable
+ # and the string to be used as the name of the executing process.
#
# Example:
#
- # # capture make log
- # make_log, s = Open3.capture2e("make")
+ # Open3.capture2e('/usr/bin/date')
+ # # => ["Sat Sep 30 09:01:46 AM CDT 2023\n", #<Process::Status: pid 2371820 exit 0>]
+ #
+ # Ruby invokes the executable directly, with no shell and no shell expansion:
+ #
+ # Open3.capture2e('doesnt_exist') # Raises Errno::ENOENT
+ #
+ # If one or more +args+ is given, each is an argument or option
+ # to be passed to the executable:
+ #
+ # Open3.capture2e('echo', 'C #')
+ # # => ["C #\n", #<Process::Status: pid 2371856 exit 0>]
+ # Open3.capture2e('echo', 'hello', 'world')
+ # # => ["hello world\n", #<Process::Status: pid 2371894 exit 0>]
#
def capture2e(*cmd)
if Hash === cmd.last
@@ -418,48 +931,86 @@ module Open3
end
module_function :capture2e
- # Open3.pipeline_rw starts a list of commands as a pipeline with pipes
- # which connect to stdin of the first command and stdout of the last command.
- #
- # Open3.pipeline_rw(cmd1, cmd2, ... [, opts]) {|first_stdin, last_stdout, wait_threads|
- # ...
- # }
+ # :call-seq:
+ # Open3.pipeline_rw([env, ] *cmds, options = {}) -> [first_stdin, last_stdout, wait_threads]
#
- # first_stdin, last_stdout, wait_threads = Open3.pipeline_rw(cmd1, cmd2, ... [, opts])
- # ...
- # first_stdin.close
- # last_stdout.close
+ # Basically a wrapper for
+ # {Process.spawn}[rdoc-ref:Process.spawn]
+ # that:
#
- # Each cmd is a string or an array.
- # If it is an array, the elements are passed to Process.spawn.
+ # - Creates a child process for each of the given +cmds+
+ # by calling Process.spawn.
+ # - Pipes the +stdout+ from each child to the +stdin+ of the next child,
+ # or, for the first child, from the caller's +stdin+,
+ # or, for the last child, to the caller's +stdout+.
#
- # cmd:
- # commandline command line string which is passed to a shell
- # [env, commandline, opts] command line string which is passed to a shell
- # [env, cmdname, arg1, ..., opts] command name and one or more arguments (no shell)
- # [env, [cmdname, argv0], arg1, ..., opts] command name and arguments including argv[0] (no shell)
+ # The method does not wait for child processes to exit,
+ # so the caller must do so.
#
- # Note that env and opts are optional, as for Process.spawn.
+ # With no block given, returns a 3-element array containing:
#
- # The options to pass to Process.spawn are constructed by merging
- # +opts+, the last hash element of the array, and
- # specifications for the pipes between each of the commands.
+ # - The +stdin+ stream of the first child process.
+ # - The +stdout+ stream of the last child process.
+ # - An array of the wait threads for all of the child processes.
#
# Example:
#
- # Open3.pipeline_rw("tr -dc A-Za-z", "wc -c") {|i, o, ts|
- # i.puts "All persons more than a mile high to leave the court."
- # i.close
- # p o.gets #=> "42\n"
- # }
- #
- # Open3.pipeline_rw("sort", "cat -n") {|stdin, stdout, wait_thrs|
- # stdin.puts "foo"
- # stdin.puts "bar"
- # stdin.puts "baz"
- # stdin.close # send EOF to sort.
- # p stdout.read #=> " 1\tbar\n 2\tbaz\n 3\tfoo\n"
- # }
+ # first_stdin, last_stdout, wait_threads = Open3.pipeline_rw('sort', 'cat -n')
+ # # => [#<IO:fd 20>, #<IO:fd 21>, [#<Process::Waiter:0x000055e8de29ab40 sleep>, #<Process::Waiter:0x000055e8de29a690 sleep>]]
+ # first_stdin.puts("foo\nbar\nbaz")
+ # first_stdin.close # Send EOF to sort.
+ # puts last_stdout.read
+ # wait_threads.each do |wait_thread|
+ # wait_thread.join
+ # end
+ #
+ # Output:
+ #
+ # 1 bar
+ # 2 baz
+ # 3 foo
+ #
+ # With a block given, calls the block with the +stdin+ stream of the first child,
+ # the +stdout+ stream of the last child,
+ # and an array of the wait processes:
+ #
+ # Open3.pipeline_rw('sort', 'cat -n') do |first_stdin, last_stdout, wait_threads|
+ # first_stdin.puts "foo\nbar\nbaz"
+ # first_stdin.close # send EOF to sort.
+ # puts last_stdout.read
+ # wait_threads.each do |wait_thread|
+ # wait_thread.join
+ # end
+ # end
+ #
+ # Output:
+ #
+ # 1 bar
+ # 2 baz
+ # 3 foo
+ #
+ # Like Process.spawn, this method has potential security vulnerabilities
+ # if called with untrusted input;
+ # see {Command Injection}[rdoc-ref:command_injection.rdoc@Command+Injection].
+ #
+ # If the first argument is a hash, it becomes leading argument +env+
+ # in each call to Process.spawn;
+ # see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
+ #
+ # If the last argument is a hash, it becomes trailing argument +options+
+ # in each call to Process.spawn;
+ # see {Execution Options}[rdoc-ref:Process@Execution+Options].
+ #
+ # Each remaining argument in +cmds+ is one of:
+ #
+ # - A +command_line+: a string that begins with a shell reserved word
+ # or special built-in, or contains one or more metacharacters.
+ # - An +exe_path+: the string path to an executable to be called.
+ # - An array containing a +command_line+ or an +exe_path+,
+ # along with zero or more string arguments for the command.
+ #
+ # See {Argument command_line or exe_path}[rdoc-ref:Process@Argument+command_line+or+exe_path].
+ #
def pipeline_rw(*cmds, &block)
if Hash === cmds.last
opts = cmds.pop.dup
@@ -478,43 +1029,77 @@ module Open3
end
module_function :pipeline_rw
- # Open3.pipeline_r starts a list of commands as a pipeline with a pipe
- # which connects to stdout of the last command.
+ # :call-seq:
+ # Open3.pipeline_r([env, ] *cmds, options = {}) -> [last_stdout, wait_threads]
#
- # Open3.pipeline_r(cmd1, cmd2, ... [, opts]) {|last_stdout, wait_threads|
- # ...
- # }
+ # Basically a wrapper for
+ # {Process.spawn}[rdoc-ref:Process.spawn]
+ # that:
#
- # last_stdout, wait_threads = Open3.pipeline_r(cmd1, cmd2, ... [, opts])
- # ...
- # last_stdout.close
+ # - Creates a child process for each of the given +cmds+
+ # by calling Process.spawn.
+ # - Pipes the +stdout+ from each child to the +stdin+ of the next child,
+ # or, for the last child, to the caller's +stdout+.
#
- # Each cmd is a string or an array.
- # If it is an array, the elements are passed to Process.spawn.
+ # The method does not wait for child processes to exit,
+ # so the caller must do so.
#
- # cmd:
- # commandline command line string which is passed to a shell
- # [env, commandline, opts] command line string which is passed to a shell
- # [env, cmdname, arg1, ..., opts] command name and one or more arguments (no shell)
- # [env, [cmdname, argv0], arg1, ..., opts] command name and arguments including argv[0] (no shell)
+ # With no block given, returns a 2-element array containing:
#
- # Note that env and opts are optional, as for Process.spawn.
+ # - The +stdout+ stream of the last child process.
+ # - An array of the wait threads for all of the child processes.
#
# Example:
#
- # Open3.pipeline_r("zcat /var/log/apache2/access.log.*.gz",
- # [{"LANG"=>"C"}, "grep", "GET /favicon.ico"],
- # "logresolve") {|o, ts|
- # o.each_line {|line|
- # ...
- # }
- # }
+ # last_stdout, wait_threads = Open3.pipeline_r('ls', 'grep R')
+ # # => [#<IO:fd 5>, [#<Process::Waiter:0x000055e8de2f9898 dead>, #<Process::Waiter:0x000055e8de2f94b0 sleep>]]
+ # puts last_stdout.read
+ # wait_threads.each do |wait_thread|
+ # wait_thread.join
+ # end
+ #
+ # Output:
+ #
+ # Rakefile
+ # README.md
+ #
+ # With a block given, calls the block with the +stdout+ stream
+ # of the last child process,
+ # and an array of the wait processes:
+ #
+ # Open3.pipeline_r('ls', 'grep R') do |last_stdout, wait_threads|
+ # puts last_stdout.read
+ # wait_threads.each do |wait_thread|
+ # wait_thread.join
+ # end
+ # end
+ #
+ # Output:
+ #
+ # Rakefile
+ # README.md
+ #
+ # Like Process.spawn, this method has potential security vulnerabilities
+ # if called with untrusted input;
+ # see {Command Injection}[rdoc-ref:command_injection.rdoc@Command+Injection].
+ #
+ # If the first argument is a hash, it becomes leading argument +env+
+ # in each call to Process.spawn;
+ # see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
+ #
+ # If the last argument is a hash, it becomes trailing argument +options+
+ # in each call to Process.spawn;
+ # see {Execution Options}[rdoc-ref:Process@Execution+Options].
#
- # Open3.pipeline_r("yes", "head -10") {|o, ts|
- # p o.read #=> "y\ny\ny\ny\ny\ny\ny\ny\ny\ny\n"
- # p ts[0].value #=> #<Process::Status: pid 24910 SIGPIPE (signal 13)>
- # p ts[1].value #=> #<Process::Status: pid 24913 exit 0>
- # }
+ # Each remaining argument in +cmds+ is one of:
+ #
+ # - A +command_line+: a string that begins with a shell reserved word
+ # or special built-in, or contains one or more metacharacters.
+ # - An +exe_path+: the string path to an executable to be called.
+ # - An array containing a +command_line+ or an +exe_path+,
+ # along with zero or more string arguments for the command.
+ #
+ # See {Argument command_line or exe_path}[rdoc-ref:Process@Argument+command_line+or+exe_path].
#
def pipeline_r(*cmds, &block)
if Hash === cmds.last
@@ -530,33 +1115,82 @@ module Open3
end
module_function :pipeline_r
- # Open3.pipeline_w starts a list of commands as a pipeline with a pipe
- # which connects to stdin of the first command.
+
+ # :call-seq:
+ # Open3.pipeline_w([env, ] *cmds, options = {}) -> [first_stdin, wait_threads]
#
- # Open3.pipeline_w(cmd1, cmd2, ... [, opts]) {|first_stdin, wait_threads|
- # ...
- # }
+ # Basically a wrapper for
+ # {Process.spawn}[rdoc-ref:Process.spawn]
+ # that:
#
- # first_stdin, wait_threads = Open3.pipeline_w(cmd1, cmd2, ... [, opts])
- # ...
- # first_stdin.close
+ # - Creates a child process for each of the given +cmds+
+ # by calling Process.spawn.
+ # - Pipes the +stdout+ from each child to the +stdin+ of the next child,
+ # or, for the first child, pipes the caller's +stdout+ to the child's +stdin+.
#
- # Each cmd is a string or an array.
- # If it is an array, the elements are passed to Process.spawn.
+ # The method does not wait for child processes to exit,
+ # so the caller must do so.
#
- # cmd:
- # commandline command line string which is passed to a shell
- # [env, commandline, opts] command line string which is passed to a shell
- # [env, cmdname, arg1, ..., opts] command name and one or more arguments (no shell)
- # [env, [cmdname, argv0], arg1, ..., opts] command name and arguments including argv[0] (no shell)
+ # With no block given, returns a 2-element array containing:
#
- # Note that env and opts are optional, as for Process.spawn.
+ # - The +stdin+ stream of the first child process.
+ # - An array of the wait threads for all of the child processes.
#
# Example:
#
- # Open3.pipeline_w("bzip2 -c", :out=>"/tmp/hello.bz2") {|i, ts|
- # i.puts "hello"
- # }
+ # first_stdin, wait_threads = Open3.pipeline_w('sort', 'cat -n')
+ # # => [#<IO:fd 7>, [#<Process::Waiter:0x000055e8de928278 run>, #<Process::Waiter:0x000055e8de923e80 run>]]
+ # first_stdin.puts("foo\nbar\nbaz")
+ # first_stdin.close # Send EOF to sort.
+ # wait_threads.each do |wait_thread|
+ # wait_thread.join
+ # end
+ #
+ # Output:
+ #
+ # 1 bar
+ # 2 baz
+ # 3 foo
+ #
+ # With a block given, calls the block with the +stdin+ stream
+ # of the first child process,
+ # and an array of the wait processes:
+ #
+ # Open3.pipeline_w('sort', 'cat -n') do |first_stdin, wait_threads|
+ # first_stdin.puts("foo\nbar\nbaz")
+ # first_stdin.close # Send EOF to sort.
+ # wait_threads.each do |wait_thread|
+ # wait_thread.join
+ # end
+ # end
+ #
+ # Output:
+ #
+ # 1 bar
+ # 2 baz
+ # 3 foo
+ #
+ # Like Process.spawn, this method has potential security vulnerabilities
+ # if called with untrusted input;
+ # see {Command Injection}[rdoc-ref:command_injection.rdoc@Command+Injection].
+ #
+ # If the first argument is a hash, it becomes leading argument +env+
+ # in each call to Process.spawn;
+ # see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
+ #
+ # If the last argument is a hash, it becomes trailing argument +options+
+ # in each call to Process.spawn;
+ # see {Execution Options}[rdoc-ref:Process@Execution+Options].
+ #
+ # Each remaining argument in +cmds+ is one of:
+ #
+ # - A +command_line+: a string that begins with a shell reserved word
+ # or special built-in, or contains one or more metacharacters.
+ # - An +exe_path+: the string path to an executable to be called.
+ # - An array containing a +command_line+ or an +exe_path+,
+ # along with zero or more string arguments for the command.
+ #
+ # See {Argument command_line or exe_path}[rdoc-ref:Process@Argument+command_line+or+exe_path].
#
def pipeline_w(*cmds, &block)
if Hash === cmds.last
@@ -573,49 +1207,67 @@ module Open3
end
module_function :pipeline_w
- # Open3.pipeline_start starts a list of commands as a pipeline.
- # No pipes are created for stdin of the first command and
- # stdout of the last command.
+ # :call-seq:
+ # Open3.pipeline_start([env, ] *cmds, options = {}) -> [wait_threads]
#
- # Open3.pipeline_start(cmd1, cmd2, ... [, opts]) {|wait_threads|
- # ...
- # }
+ # Basically a wrapper for
+ # {Process.spawn}[rdoc-ref:Process.spawn]
+ # that:
#
- # wait_threads = Open3.pipeline_start(cmd1, cmd2, ... [, opts])
- # ...
+ # - Creates a child process for each of the given +cmds+
+ # by calling Process.spawn.
+ # - Does not wait for child processes to exit.
#
- # Each cmd is a string or an array.
- # If it is an array, the elements are passed to Process.spawn.
+ # With no block given, returns an array of the wait threads
+ # for all of the child processes.
#
- # cmd:
- # commandline command line string which is passed to a shell
- # [env, commandline, opts] command line string which is passed to a shell
- # [env, cmdname, arg1, ..., opts] command name and one or more arguments (no shell)
- # [env, [cmdname, argv0], arg1, ..., opts] command name and arguments including argv[0] (no shell)
+ # Example:
#
- # Note that env and opts are optional, as for Process.spawn.
+ # wait_threads = Open3.pipeline_start('ls', 'grep R')
+ # # => [#<Process::Waiter:0x000055e8de9d2bb0 run>, #<Process::Waiter:0x000055e8de9d2890 run>]
+ # wait_threads.each do |wait_thread|
+ # wait_thread.join
+ # end
#
- # Example:
+ # Output:
+ #
+ # Rakefile
+ # README.md
+ #
+ # With a block given, calls the block with an array of the wait processes:
+ #
+ # Open3.pipeline_start('ls', 'grep R') do |wait_threads|
+ # wait_threads.each do |wait_thread|
+ # wait_thread.join
+ # end
+ # end
+ #
+ # Output:
+ #
+ # Rakefile
+ # README.md
#
- # # Run xeyes in 10 seconds.
- # Open3.pipeline_start("xeyes") {|ts|
- # sleep 10
- # t = ts[0]
- # Process.kill("TERM", t.pid)
- # p t.value #=> #<Process::Status: pid 911 SIGTERM (signal 15)>
- # }
- #
- # # Convert pdf to ps and send it to a printer.
- # # Collect error message of pdftops and lpr.
- # pdf_file = "paper.pdf"
- # printer = "printer-name"
- # err_r, err_w = IO.pipe
- # Open3.pipeline_start(["pdftops", pdf_file, "-"],
- # ["lpr", "-P#{printer}"],
- # :err=>err_w) {|ts|
- # err_w.close
- # p err_r.read # error messages of pdftops and lpr.
- # }
+ # Like Process.spawn, this method has potential security vulnerabilities
+ # if called with untrusted input;
+ # see {Command Injection}[rdoc-ref:command_injection.rdoc@Command+Injection].
+ #
+ # If the first argument is a hash, it becomes leading argument +env+
+ # in each call to Process.spawn;
+ # see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
+ #
+ # If the last argument is a hash, it becomes trailing argument +options+
+ # in each call to Process.spawn;
+ # see {Execution Options}[rdoc-ref:Process@Execution+Options].
+ #
+ # Each remaining argument in +cmds+ is one of:
+ #
+ # - A +command_line+: a string that begins with a shell reserved word
+ # or special built-in, or contains one or more metacharacters.
+ # - An +exe_path+: the string path to an executable to be called.
+ # - An array containing a +command_line+ or an +exe_path+,
+ # along with zero or more string arguments for the command.
+ #
+ # See {Argument command_line or exe_path}[rdoc-ref:Process@Argument+command_line+or+exe_path].
#
def pipeline_start(*cmds, &block)
if Hash === cmds.last
@@ -633,57 +1285,51 @@ module Open3
end
module_function :pipeline_start
- # Open3.pipeline starts a list of commands as a pipeline.
- # It waits for the completion of the commands.
- # No pipes are created for stdin of the first command and
- # stdout of the last command.
+ # :call-seq:
+ # Open3.pipeline([env, ] *cmds, options = {}) -> array_of_statuses
#
- # status_list = Open3.pipeline(cmd1, cmd2, ... [, opts])
+ # Basically a wrapper for
+ # {Process.spawn}[rdoc-ref:Process.spawn]
+ # that:
#
- # Each cmd is a string or an array.
- # If it is an array, the elements are passed to Process.spawn.
+ # - Creates a child process for each of the given +cmds+
+ # by calling Process.spawn.
+ # - Pipes the +stdout+ from each child to the +stdin+ of the next child,
+ # or, for the last child, to the caller's +stdout+.
+ # - Waits for the child processes to exit.
+ # - Returns an array of Process::Status objects (one for each child).
#
- # cmd:
- # commandline command line string which is passed to a shell
- # [env, commandline, opts] command line string which is passed to a shell
- # [env, cmdname, arg1, ..., opts] command name and one or more arguments (no shell)
- # [env, [cmdname, argv0], arg1, ..., opts] command name and arguments including argv[0] (no shell)
+ # Example:
#
- # Note that env and opts are optional, as Process.spawn.
+ # wait_threads = Open3.pipeline('ls', 'grep R')
+ # # => [#<Process::Status: pid 2139200 exit 0>, #<Process::Status: pid 2139202 exit 0>]
#
- # Example:
+ # Output:
+ #
+ # Rakefile
+ # README.md
+ #
+ # Like Process.spawn, this method has potential security vulnerabilities
+ # if called with untrusted input;
+ # see {Command Injection}[rdoc-ref:command_injection.rdoc@Command+Injection].
+ #
+ # If the first argument is a hash, it becomes leading argument +env+
+ # in each call to Process.spawn;
+ # see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
+ #
+ # If the last argument is a hash, it becomes trailing argument +options+
+ # in each call to Process.spawn'
+ # see {Execution Options}[rdoc-ref:Process@Execution+Options].
+ #
+ # Each remaining argument in +cmds+ is one of:
+ #
+ # - A +command_line+: a string that begins with a shell reserved word
+ # or special built-in, or contains one or more metacharacters.
+ # - An +exe_path+: the string path to an executable to be called.
+ # - An array containing a +command_line+ or an +exe_path+,
+ # along with zero or more string arguments for the command.
#
- # fname = "/usr/share/man/man1/ruby.1.gz"
- # p Open3.pipeline(["zcat", fname], "nroff -man", "less")
- # #=> [#<Process::Status: pid 11817 exit 0>,
- # # #<Process::Status: pid 11820 exit 0>,
- # # #<Process::Status: pid 11828 exit 0>]
- #
- # fname = "/usr/share/man/man1/ls.1.gz"
- # Open3.pipeline(["zcat", fname], "nroff -man", "colcrt")
- #
- # # convert PDF to PS and send to a printer by lpr
- # pdf_file = "paper.pdf"
- # printer = "printer-name"
- # Open3.pipeline(["pdftops", pdf_file, "-"],
- # ["lpr", "-P#{printer}"])
- #
- # # count lines
- # Open3.pipeline("sort", "uniq -c", :in=>"names.txt", :out=>"count")
- #
- # # cyclic pipeline
- # r,w = IO.pipe
- # w.print "ibase=14\n10\n"
- # Open3.pipeline("bc", "tee /dev/tty", :in=>r, :out=>w)
- # #=> 14
- # # 18
- # # 22
- # # 30
- # # 42
- # # 58
- # # 78
- # # 106
- # # 202
+ # See {Argument command_line or exe_path}[rdoc-ref:Process@Argument+command_line+or+exe_path].
#
def pipeline(*cmds)
if Hash === cmds.last
diff --git a/lib/open3/version.rb b/lib/open3/version.rb
index b6b6ee2c9c..bfcec44ccc 100644
--- a/lib/open3/version.rb
+++ b/lib/open3/version.rb
@@ -1,3 +1,3 @@
module Open3
- VERSION = "0.1.2"
+ VERSION = "0.2.1"
end
diff --git a/lib/optparse.rb b/lib/optparse.rb
index 28b64af613..069c3e436e 100644
--- a/lib/optparse.rb
+++ b/lib/optparse.rb
@@ -8,7 +8,6 @@
# See OptionParser for documentation.
#
-
#--
# == Developer Documentation (not for RDoc output)
#
@@ -48,7 +47,7 @@
#
# == OptionParser
#
-# === New to \OptionParser?
+# === New to +OptionParser+?
#
# See the {Tutorial}[optparse/tutorial.rdoc].
#
@@ -425,7 +424,8 @@
# If you have any questions, file a ticket at http://bugs.ruby-lang.org.
#
class OptionParser
- OptionParser::Version = "0.4.0.pre.1"
+ # The version string
+ OptionParser::Version = "0.5.0"
# :stopdoc:
NoArgument = [NO_ARGUMENT = :NONE, nil].freeze
@@ -438,6 +438,8 @@ class OptionParser
# and resolved against a list of acceptable values.
#
module Completion
+ # :nodoc:
+
def self.regexp(key, icase)
Regexp.new('\A' + Regexp.quote(key).gsub(/\w+\b/, '\&\w*'), icase)
end
@@ -459,7 +461,7 @@ class OptionParser
candidates
end
- def candidate(key, icase = false, pat = nil)
+ def candidate(key, icase = false, pat = nil, &_)
Completion.candidate(key, icase, pat, &method(:each))
end
@@ -510,6 +512,8 @@ class OptionParser
# RequiredArgument, etc.
#
class Switch
+ # :nodoc:
+
attr_reader :pattern, :conv, :short, :long, :arg, :desc, :block
#
@@ -697,6 +701,11 @@ class OptionParser
q.object_group(self) {pretty_print_contents(q)}
end
+ def omitted_argument(val) # :nodoc:
+ val.pop if val.size == 3 and val.last.nil?
+ val
+ end
+
#
# Switch that takes no arguments.
#
@@ -710,10 +719,10 @@ class OptionParser
conv_arg(arg)
end
- def self.incompatible_argument_styles(*)
+ def self.incompatible_argument_styles(*) # :nodoc:
end
- def self.pattern
+ def self.pattern # :nodoc:
Object
end
@@ -730,7 +739,7 @@ class OptionParser
#
# Raises an exception if argument is not present.
#
- def parse(arg, argv)
+ def parse(arg, argv, &_)
unless arg
raise MissingArgument if argv.empty?
arg = argv.shift
@@ -755,7 +764,7 @@ class OptionParser
if arg
conv_arg(*parse_arg(arg, &error))
else
- conv_arg(arg)
+ omitted_argument conv_arg(arg)
end
end
@@ -774,13 +783,14 @@ class OptionParser
#
def parse(arg, argv, &error)
if !(val = arg) and (argv.empty? or /\A-./ =~ (val = argv[0]))
- return nil, block, nil
+ return nil, block
end
opt = (val = parse_arg(val, &error))[1]
val = conv_arg(*val)
if opt and !arg
argv.shift
else
+ omitted_argument val
val[0] = nil
end
val
@@ -798,6 +808,8 @@ class OptionParser
# matching pattern and converter pair. Also provides summary feature.
#
class List
+ # :nodoc:
+
# Map from acceptable argument types to pattern and converter pairs.
attr_reader :atype
@@ -837,7 +849,7 @@ class OptionParser
def accept(t, pat = /.*/m, &block)
if pat
pat.respond_to?(:match) or
- raise TypeError, "has no `match'", ParseError.filter_backtrace(caller(2))
+ raise TypeError, "has no 'match'", ParseError.filter_backtrace(caller(2))
else
pat = t if t.respond_to?(:match)
end
@@ -1033,11 +1045,31 @@ XXX
to << "#compdef #{name}\n"
to << COMPSYS_HEADER
visit(:compsys, {}, {}) {|o, d|
- to << %Q[ "#{o}[#{d.gsub(/[\"\[\]]/, '\\\\\&')}]" \\\n]
+ to << %Q[ "#{o}[#{d.gsub(/[\\\"\[\]]/, '\\\\\&')}]" \\\n]
}
to << " '*:file:_files' && return 0\n"
end
+ def help_exit
+ if STDOUT.tty? && (pager = ENV.values_at(*%w[RUBY_PAGER PAGER]).find {|e| e && !e.empty?})
+ less = ENV["LESS"]
+ args = [{"LESS" => "#{!less || less.empty? ? '-' : less}Fe"}, pager, "w"]
+ print = proc do |f|
+ f.puts help
+ rescue Errno::EPIPE
+ # pager terminated
+ end
+ if Process.respond_to?(:fork) and false
+ IO.popen("-") {|f| f ? Process.exec(*args, in: f) : print.call(STDOUT)}
+ # unreachable
+ end
+ IO.popen(*args, &print)
+ else
+ puts help
+ end
+ exit
+ end
+
#
# Default options for ARGV, which never appear in option summary.
#
@@ -1049,8 +1081,7 @@ XXX
#
Officious['help'] = proc do |parser|
Switch::NoArgument.new do |arg|
- puts parser.help
- exit
+ parser.help_exit
end
end
@@ -1129,6 +1160,10 @@ XXX
default.to_i + 1
end
end
+
+ #
+ # See self.inc
+ #
def inc(*args)
self.class.inc(*args)
end
@@ -1167,11 +1202,19 @@ XXX
def terminate(arg = nil)
self.class.terminate(arg)
end
+ #
+ # See #terminate.
+ #
def self.terminate(arg = nil)
throw :terminate, arg
end
@stack = [DefaultList]
+ #
+ # Returns the global top option list.
+ #
+ # Do not use directly.
+ #
def self.top() DefaultList end
#
@@ -1192,9 +1235,9 @@ XXX
#
# Directs to reject specified class argument.
#
- # +t+:: Argument class specifier, any object including Class.
+ # +type+:: Argument class specifier, any object including Class.
#
- # reject(t)
+ # reject(type)
#
def reject(*args, &blk) top.reject(*args, &blk) end
#
@@ -1284,10 +1327,24 @@ XXX
end
end
+ #
+ # Shows warning message with the program name
+ #
+ # +mesg+:: Message, defaulted to +$!+.
+ #
+ # See Kernel#warn.
+ #
def warn(mesg = $!)
super("#{program_name}: #{mesg}")
end
+ #
+ # Shows message with the program name then aborts.
+ #
+ # +mesg+:: Message, defaulted to +$!+.
+ #
+ # See Kernel#abort.
+ #
def abort(mesg = $!)
super("#{program_name}: #{mesg}")
end
@@ -1309,6 +1366,9 @@ XXX
#
# Pushes a new List.
#
+ # If a block is given, yields +self+ and returns the result of the
+ # block, otherwise returns +self+.
+ #
def new
@stack.push(List.new)
if block_given?
@@ -1532,6 +1592,12 @@ XXX
nolong
end
+ # ----
+ # Option definition phase methods
+ #
+ # These methods are used to define options, or to construct an
+ # OptionParser instance in other words.
+
# :call-seq:
# define(*params, &block)
#
@@ -1607,6 +1673,13 @@ XXX
top.append(string, nil, nil)
end
+ # ----
+ # Arguments parse phase methods
+ #
+ # These methods parse +argv+, convert, and store the results by
+ # calling handlers. As these methods do not modify +self+, +self+
+ # can be frozen.
+
#
# Parses command line arguments +argv+ in order. When a block is given,
# each non-option argument is yielded. When optional +into+ keyword
@@ -1616,21 +1689,21 @@ XXX
#
# Returns the rest of +argv+ left unparsed.
#
- def order(*argv, into: nil, &nonopt)
+ def order(*argv, **keywords, &nonopt)
argv = argv[0].dup if argv.size == 1 and Array === argv[0]
- order!(argv, into: into, &nonopt)
+ order!(argv, **keywords, &nonopt)
end
#
# Same as #order, but removes switches destructively.
# Non-option arguments remain in +argv+.
#
- def order!(argv = default_argv, into: nil, &nonopt)
+ def order!(argv = default_argv, into: nil, **keywords, &nonopt)
setter = ->(name, val) {into[name.to_sym] = val} if into
- parse_in_order(argv, setter, &nonopt)
+ parse_in_order(argv, setter, **keywords, &nonopt)
end
- def parse_in_order(argv = default_argv, setter = nil, &nonopt) # :nodoc:
+ def parse_in_order(argv = default_argv, setter = nil, exact: require_exact, **, &nonopt) # :nodoc:
opt, arg, val, rest = nil
nonopt ||= proc {|a| throw :terminate, a}
argv.unshift(arg) if arg = catch(:terminate) {
@@ -1641,19 +1714,24 @@ XXX
opt, rest = $1, $2
opt.tr!('_', '-')
begin
- sw, = complete(:long, opt, true)
- if require_exact && !sw.long.include?(arg)
- throw :terminate, arg unless raise_unknown
- raise InvalidOption, arg
+ if exact
+ sw, = search(:long, opt)
+ else
+ sw, = complete(:long, opt, true)
end
rescue ParseError
throw :terminate, arg unless raise_unknown
raise $!.set_option(arg, true)
+ else
+ unless sw
+ throw :terminate, arg unless raise_unknown
+ raise InvalidOption, arg
+ end
end
begin
- opt, cb, val = sw.parse(rest, argv) {|*exc| raise(*exc)}
- val = cb.call(val) if cb
- setter.call(sw.switch_name, val) if setter
+ opt, cb, *val = sw.parse(rest, argv) {|*exc| raise(*exc)}
+ val = callback!(cb, 1, *val) if cb
+ callback!(setter, 2, sw.switch_name, *val) if setter
rescue ParseError
raise $!.set_option(arg, rest)
end
@@ -1671,7 +1749,7 @@ XXX
val = arg.delete_prefix('-')
has_arg = true
rescue InvalidOption
- raise if require_exact
+ raise if exact
# if no short options match, try completion with long
# options.
sw, = complete(:long, opt)
@@ -1683,7 +1761,7 @@ XXX
raise $!.set_option(arg, true)
end
begin
- opt, cb, val = sw.parse(val, argv) {|*exc| raise(*exc) if eq}
+ opt, cb, *val = sw.parse(val, argv) {|*exc| raise(*exc) if eq}
rescue ParseError
raise $!.set_option(arg, arg.length > 2)
else
@@ -1691,8 +1769,8 @@ XXX
end
begin
argv.unshift(opt) if opt and (!rest or (opt = opt.sub(/\A-*/, '-')) != '-')
- val = cb.call(val) if cb
- setter.call(sw.switch_name, val) if setter
+ val = callback!(cb, 1, *val) if cb
+ callback!(setter, 2, sw.switch_name, *val) if setter
rescue ParseError
raise $!.set_option(arg, arg.length > 2)
end
@@ -1718,6 +1796,17 @@ XXX
end
private :parse_in_order
+ # Calls callback with _val_.
+ def callback!(cb, max_arity, *args) # :nodoc:
+ if (size = args.size) < max_arity and cb.to_proc.lambda?
+ (arity = cb.arity) < 0 and arity = (1-arity)
+ arity = max_arity if arity > max_arity
+ args[arity - 1] = nil if arity > size
+ end
+ cb.call(*args)
+ end
+ private :callback!
+
#
# Parses command line arguments +argv+ in permutation mode and returns
# list of non-option arguments. When optional +into+ keyword
@@ -1725,18 +1814,18 @@ XXX
# <code>[]=</code> method (so it can be Hash, or OpenStruct, or other
# similar object).
#
- def permute(*argv, into: nil)
+ def permute(*argv, **keywords)
argv = argv[0].dup if argv.size == 1 and Array === argv[0]
- permute!(argv, into: into)
+ permute!(argv, **keywords)
end
#
# Same as #permute, but removes switches destructively.
# Non-option arguments remain in +argv+.
#
- def permute!(argv = default_argv, into: nil)
+ def permute!(argv = default_argv, **keywords)
nonopts = []
- order!(argv, into: into, &nonopts.method(:<<))
+ order!(argv, **keywords, &nonopts.method(:<<))
argv[0, 0] = nonopts
argv
end
@@ -1748,20 +1837,20 @@ XXX
# values are stored there via <code>[]=</code> method (so it can be Hash,
# or OpenStruct, or other similar object).
#
- def parse(*argv, into: nil)
+ def parse(*argv, **keywords)
argv = argv[0].dup if argv.size == 1 and Array === argv[0]
- parse!(argv, into: into)
+ parse!(argv, **keywords)
end
#
# Same as #parse, but removes switches destructively.
# Non-option arguments remain in +argv+.
#
- def parse!(argv = default_argv, into: nil)
+ def parse!(argv = default_argv, **keywords)
if ENV.include?('POSIXLY_CORRECT')
- order!(argv, into: into)
+ order!(argv, **keywords)
else
- permute!(argv, into: into)
+ permute!(argv, **keywords)
end
end
@@ -1784,7 +1873,7 @@ XXX
# # params[:bar] = "x" # --bar x
# # params[:zot] = "z" # --zot Z
#
- def getopts(*args, symbolize_names: false)
+ def getopts(*args, symbolize_names: false, **keywords)
argv = Array === args.first ? args.shift : default_argv
single_options, *long_options = *args
@@ -1812,7 +1901,7 @@ XXX
end
end
- parse_in_order(argv, result.method(:[]=))
+ parse_in_order(argv, result.method(:[]=), **keywords)
symbolize_names ? result.transform_keys(&:to_sym) : result
end
@@ -1881,6 +1970,9 @@ XXX
DidYouMean.formatter.message_for(all_candidates & checker.correct(opt))
end
+ #
+ # Return candidates for +word+.
+ #
def candidate(word)
list = []
case word
@@ -1922,10 +2014,10 @@ XXX
# The optional +into+ keyword argument works exactly like that accepted in
# method #parse.
#
- def load(filename = nil, into: nil)
+ def load(filename = nil, **keywords)
unless filename
basename = File.basename($0, '.*')
- return true if load(File.expand_path(basename, '~/.options'), into: into) rescue nil
+ return true if load(File.expand_path(basename, '~/.options'), **keywords) rescue nil
basename << ".options"
return [
# XDG
@@ -1937,11 +2029,11 @@ XXX
'~/config/settings',
].any? {|dir|
next if !dir or dir.empty?
- load(File.expand_path(basename, dir), into: into) rescue nil
+ load(File.expand_path(basename, dir), **keywords) rescue nil
}
end
begin
- parse(*File.readlines(filename, chomp: true), into: into)
+ parse(*File.readlines(filename, chomp: true), **keywords)
true
rescue Errno::ENOENT, Errno::ENOTDIR
false
@@ -1954,10 +2046,10 @@ XXX
#
# +env+ defaults to the basename of the program.
#
- def environment(env = File.basename($0, '.*'))
+ def environment(env = File.basename($0, '.*'), **keywords)
env = ENV[env] || ENV[env.upcase] or return
require 'shellwords'
- parse(*Shellwords.shellwords(env))
+ parse(*Shellwords.shellwords(env), **keywords)
end
#
@@ -2123,6 +2215,7 @@ XXX
# Reason which caused the error.
Reason = 'parse error'
+ # :nodoc:
def initialize(*args, additional: nil)
@additional = additional
@arg0, = args
@@ -2273,19 +2366,19 @@ XXX
# Parses +self+ destructively in order and returns +self+ containing the
# rest arguments left unparsed.
#
- def order!(&blk) options.order!(self, &blk) end
+ def order!(**keywords, &blk) options.order!(self, **keywords, &blk) end
#
# Parses +self+ destructively in permutation mode and returns +self+
# containing the rest arguments left unparsed.
#
- def permute!() options.permute!(self) end
+ def permute!(**keywords) options.permute!(self, **keywords) end
#
# Parses +self+ destructively and returns +self+ containing the
# rest arguments left unparsed.
#
- def parse!() options.parse!(self) end
+ def parse!(**keywords) options.parse!(self, **keywords) end
#
# Substitution of getopts is possible as follows. Also see
@@ -2298,8 +2391,8 @@ XXX
# rescue OptionParser::ParseError
# end
#
- def getopts(*args, symbolize_names: false)
- options.getopts(self, *args, symbolize_names: symbolize_names)
+ def getopts(*args, symbolize_names: false, **keywords)
+ options.getopts(self, *args, symbolize_names: symbolize_names, **keywords)
end
#
@@ -2309,7 +2402,8 @@ XXX
super
obj.instance_eval {@optparse = nil}
end
- def initialize(*args)
+
+ def initialize(*args) # :nodoc:
super
@optparse = nil
end
diff --git a/lib/optparse/ac.rb b/lib/optparse/ac.rb
index 0953725e46..23fc740d10 100644
--- a/lib/optparse/ac.rb
+++ b/lib/optparse/ac.rb
@@ -1,7 +1,11 @@
# frozen_string_literal: false
require_relative '../optparse'
+#
+# autoconf-like options.
+#
class OptionParser::AC < OptionParser
+ # :stopdoc:
private
def _check_ac_args(name, block)
@@ -14,6 +18,7 @@ class OptionParser::AC < OptionParser
end
ARG_CONV = proc {|val| val.nil? ? true : val}
+ private_constant :ARG_CONV
def _ac_arg_enable(prefix, name, help_string, block)
_check_ac_args(name, block)
@@ -29,16 +34,27 @@ class OptionParser::AC < OptionParser
enable
end
+ # :startdoc:
+
public
+ # Define <tt>--enable</tt> / <tt>--disable</tt> style option
+ #
+ # Appears as <tt>--enable-<i>name</i></tt> in help message.
def ac_arg_enable(name, help_string, &block)
_ac_arg_enable("enable", name, help_string, block)
end
+ # Define <tt>--enable</tt> / <tt>--disable</tt> style option
+ #
+ # Appears as <tt>--disable-<i>name</i></tt> in help message.
def ac_arg_disable(name, help_string, &block)
_ac_arg_enable("disable", name, help_string, block)
end
+ # Define <tt>--with</tt> / <tt>--without</tt> style option
+ #
+ # Appears as <tt>--with-<i>name</i></tt> in help message.
def ac_arg_with(name, help_string, &block)
_check_ac_args(name, block)
diff --git a/lib/optparse/kwargs.rb b/lib/optparse/kwargs.rb
index 992aca467e..59a2f61544 100644
--- a/lib/optparse/kwargs.rb
+++ b/lib/optparse/kwargs.rb
@@ -7,12 +7,17 @@ class OptionParser
#
# :include: ../../doc/optparse/creates_option.rdoc
#
- def define_by_keywords(options, meth, **opts)
- meth.parameters.each do |type, name|
+ # Defines options which set in to _options_ for keyword parameters
+ # of _method_.
+ #
+ # Parameters for each keywords are given as elements of _params_.
+ #
+ def define_by_keywords(options, method, **params)
+ method.parameters.each do |type, name|
case type
when :key, :keyreq
op, cl = *(type == :key ? %w"[ ]" : ["", ""])
- define("--#{name}=#{op}#{name.upcase}#{cl}", *opts[name]) do |o|
+ define("--#{name}=#{op}#{name.upcase}#{cl}", *params[name]) do |o|
options[name] = o
end
end
diff --git a/lib/optparse/optparse.gemspec b/lib/optparse/optparse.gemspec
index a4287ddeee..1aa54aa781 100644
--- a/lib/optparse/optparse.gemspec
+++ b/lib/optparse/optparse.gemspec
@@ -22,7 +22,8 @@ Gem::Specification.new do |spec|
spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = spec.homepage
- spec.files = Dir["{doc,lib,misc}/**/*"] + %w[README.md ChangeLog COPYING]
+ spec.files = Dir["{doc,lib,misc}/**/{*,.document}"] +
+ %w[README.md ChangeLog COPYING .document .rdoc_options]
spec.rdoc_options = ["--main=README.md", "--op=rdoc", "--page-dir=doc"]
spec.bindir = "exe"
spec.executables = []
diff --git a/lib/optparse/version.rb b/lib/optparse/version.rb
index b869d8fe51..b5ed695146 100644
--- a/lib/optparse/version.rb
+++ b/lib/optparse/version.rb
@@ -2,6 +2,11 @@
# OptionParser internal utility
class << OptionParser
+ #
+ # Shows version string in packages if Version is defined.
+ #
+ # +pkgs+:: package list
+ #
def show_version(*pkgs)
progname = ARGV.options.program_name
result = false
@@ -47,6 +52,8 @@ class << OptionParser
result
end
+ # :stopdoc:
+
def each_const(path, base = ::Object)
path.split(/::|\//).inject(base) do |klass, name|
raise NameError, path unless Module === klass
@@ -68,4 +75,6 @@ class << OptionParser
end
end
end
+
+ # :startdoc:
end
diff --git a/lib/ostruct.gemspec b/lib/ostruct.gemspec
index 21cce18226..08a7aefb05 100644
--- a/lib/ostruct.gemspec
+++ b/lib/ostruct.gemspec
@@ -21,7 +21,4 @@ Gem::Specification.new do |spec|
spec.files = [".gitignore", "Gemfile", "LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup", "lib/ostruct.rb", "ostruct.gemspec"]
spec.require_paths = ["lib"]
-
- spec.add_development_dependency "bundler"
- spec.add_development_dependency "rake"
end
diff --git a/lib/ostruct.rb b/lib/ostruct.rb
index a08561d6c9..c762baa5a5 100644
--- a/lib/ostruct.rb
+++ b/lib/ostruct.rb
@@ -107,7 +107,15 @@
# For all these reasons, consider not using OpenStruct at all.
#
class OpenStruct
- VERSION = "0.5.5"
+ VERSION = "0.6.0"
+
+ HAS_PERFORMANCE_WARNINGS = begin
+ Warning[:performance]
+ true
+ rescue NoMethodError, ArgumentError
+ false
+ end
+ private_constant :HAS_PERFORMANCE_WARNINGS
#
# Creates a new OpenStruct object. By default, the resulting OpenStruct
@@ -124,6 +132,10 @@ class OpenStruct
# data # => #<OpenStruct country="Australia", capital="Canberra">
#
def initialize(hash=nil)
+ if HAS_PERFORMANCE_WARNINGS && Warning[:performance]
+ warn "OpenStruct use is discouraged for performance reasons", uplevel: 1, category: :performance
+ end
+
if hash
update_to_values!(hash)
else
@@ -364,7 +376,7 @@ class OpenStruct
end
@table.delete(sym) do
return yield if block
- raise! NameError.new("no field `#{sym}' in #{self}", sym)
+ raise! NameError.new("no field '#{sym}' in #{self}", sym)
end
end
diff --git a/lib/pp.rb b/lib/pp.rb
index b81f84cec5..1ec5a880eb 100644
--- a/lib/pp.rb
+++ b/lib/pp.rb
@@ -46,6 +46,7 @@ require 'prettyprint'
#
# To define a customized pretty printing function for your classes,
# redefine method <code>#pretty_print(pp)</code> in the class.
+# Note that <code>require 'pp'</code> is needed before redefining <code>#pretty_print(pp)</code>.
#
# <code>#pretty_print</code> takes the +pp+ argument, which is an instance of the PP class.
# The method uses #text, #breakable, #nest, #group and #pp to print the
@@ -62,7 +63,7 @@ require 'prettyprint'
class PP < PrettyPrint
- VERSION = "0.4.0"
+ VERSION = "0.5.0"
# Returns the usable width for +out+.
# As the width of +out+:
@@ -92,7 +93,7 @@ class PP < PrettyPrint
#
# PP.pp returns +out+.
def PP.pp(obj, out=$>, width=width_for(out))
- q = PP.new(out, width)
+ q = new(out, width)
q.guard_inspect_key {q.pp obj}
q.flush
#$pp = q
@@ -285,16 +286,21 @@ class PP < PrettyPrint
group(1, '{', '}') {
seplist(obj, nil, :each_pair) {|k, v|
group {
- pp k
- text '=>'
- group(1) {
- breakable ''
- pp v
- }
+ pp_hash_pair k, v
}
}
}
end
+
+ # A pretty print for a pair of Hash
+ def pp_hash_pair(k, v)
+ pp k
+ text '=>'
+ group(1) {
+ breakable ''
+ pp v
+ }
+ end
end
include PPMethods
@@ -421,8 +427,10 @@ end
class Data # :nodoc:
def pretty_print(q) # :nodoc:
- q.group(1, sprintf("#<data %s", PP.mcall(self, Kernel, :class).name), '>') {
- q.seplist(PP.mcall(self, Data, :members), lambda { q.text "," }) {|member|
+ class_name = PP.mcall(self, Kernel, :class).name
+ class_name = " #{class_name}" if class_name
+ q.group(1, "#<data#{class_name}", '>') {
+ q.seplist(PP.mcall(self, Kernel, :class).members, lambda { q.text "," }) {|member|
q.breakable
q.text member.to_s
q.text '='
@@ -437,11 +445,11 @@ class Data # :nodoc:
def pretty_print_cycle(q) # :nodoc:
q.text sprintf("#<data %s:...>", PP.mcall(self, Kernel, :class).name)
end
-end if "3.2" <= RUBY_VERSION
+end if defined?(Data.define)
class Range # :nodoc:
def pretty_print(q) # :nodoc:
- q.pp self.begin
+ q.pp self.begin if self.begin
q.breakable ''
q.text(self.exclude_end? ? '...' : '..')
q.breakable ''
@@ -632,10 +640,6 @@ end
module Kernel
# Returns a pretty printed object as a string.
#
- # In order to use this method you must first require the PP module:
- #
- # require 'pp'
- #
# See the PP module for more information.
def pretty_inspect
PP.pp(self, ''.dup)
diff --git a/lib/prettyprint.rb b/lib/prettyprint.rb
index 540b12fb70..6f50192f5d 100644
--- a/lib/prettyprint.rb
+++ b/lib/prettyprint.rb
@@ -23,17 +23,17 @@
#
# == References
# Christian Lindig, Strictly Pretty, March 2000,
-# http://www.st.cs.uni-sb.de/~lindig/papers/#pretty
+# https://lindig.github.io/papers/strictly-pretty-2000.pdf
#
# Philip Wadler, A prettier printer, March 1998,
-# http://homepages.inf.ed.ac.uk/wadler/topics/language-design.html#prettier
+# https://homepages.inf.ed.ac.uk/wadler/topics/language-design.html#prettier
#
# == Author
# Tanaka Akira <akr@fsij.org>
#
class PrettyPrint
- VERSION = "0.1.1"
+ VERSION = "0.2.0"
# This is a convenience method which is same as follows:
#
diff --git a/lib/prism.rb b/lib/prism.rb
new file mode 100644
index 0000000000..c733baf8c9
--- /dev/null
+++ b/lib/prism.rb
@@ -0,0 +1,90 @@
+# frozen_string_literal: true
+
+# The Prism Ruby parser.
+#
+# "Parsing Ruby is suddenly manageable!"
+# - You, hopefully
+#
+module Prism
+ # There are many files in prism that are templated to handle every node type,
+ # which means the files can end up being quite large. We autoload them to make
+ # our require speed faster since consuming libraries are unlikely to use all
+ # of these features.
+
+ autoload :BasicVisitor, "prism/visitor"
+ autoload :Compiler, "prism/compiler"
+ autoload :Debug, "prism/debug"
+ autoload :DesugarCompiler, "prism/desugar_compiler"
+ autoload :Dispatcher, "prism/dispatcher"
+ autoload :DotVisitor, "prism/dot_visitor"
+ autoload :DSL, "prism/dsl"
+ autoload :InspectVisitor, "prism/inspect_visitor"
+ autoload :LexCompat, "prism/lex_compat"
+ autoload :LexRipper, "prism/lex_compat"
+ autoload :MutationCompiler, "prism/mutation_compiler"
+ autoload :Pack, "prism/pack"
+ autoload :Pattern, "prism/pattern"
+ autoload :Reflection, "prism/reflection"
+ autoload :Serialize, "prism/serialize"
+ autoload :Translation, "prism/translation"
+ autoload :Visitor, "prism/visitor"
+
+ # Some of these constants are not meant to be exposed, so marking them as
+ # private here.
+
+ private_constant :Debug
+ private_constant :LexCompat
+ private_constant :LexRipper
+
+ # :call-seq:
+ # Prism::lex_compat(source, **options) -> LexCompat::Result
+ #
+ # Returns a parse result whose value is an array of tokens that closely
+ # resembles the return value of Ripper::lex. The main difference is that the
+ # `:on_sp` token is not emitted.
+ #
+ # For supported options, see Prism::parse.
+ def self.lex_compat(source, **options)
+ LexCompat.new(source, **options).result # steep:ignore
+ end
+
+ # :call-seq:
+ # Prism::lex_ripper(source) -> Array
+ #
+ # This lexes with the Ripper lex. It drops any space events but otherwise
+ # returns the same tokens. Raises SyntaxError if the syntax in source is
+ # invalid.
+ def self.lex_ripper(source)
+ LexRipper.new(source).result # steep:ignore
+ end
+
+ # :call-seq:
+ # Prism::load(source, serialized) -> ParseResult
+ #
+ # Load the serialized AST using the source as a reference into a tree.
+ def self.load(source, serialized)
+ Serialize.load(source, serialized)
+ end
+end
+
+require_relative "prism/node"
+require_relative "prism/node_ext"
+require_relative "prism/parse_result"
+require_relative "prism/parse_result/comments"
+require_relative "prism/parse_result/newlines"
+
+# This is a Ruby implementation of the prism parser. If we're running on CRuby
+# and we haven't explicitly set the PRISM_FFI_BACKEND environment variable, then
+# it's going to require the built library. Otherwise, it's going to require a
+# module that uses FFI to call into the library.
+if RUBY_ENGINE == "ruby" and !ENV["PRISM_FFI_BACKEND"]
+ require "prism/prism"
+
+ # The C extension is the default backend on CRuby.
+ Prism::BACKEND = :CEXT
+else
+ require_relative "prism/ffi"
+
+ # The FFI backend is used on other Ruby implementations.
+ Prism::BACKEND = :FFI
+end
diff --git a/lib/prism/debug.rb b/lib/prism/debug.rb
new file mode 100644
index 0000000000..74f824faa7
--- /dev/null
+++ b/lib/prism/debug.rb
@@ -0,0 +1,249 @@
+# frozen_string_literal: true
+
+module Prism
+ # This module is used for testing and debugging and is not meant to be used by
+ # consumers of this library.
+ module Debug
+ # A wrapper around a RubyVM::InstructionSequence that provides a more
+ # convenient interface for accessing parts of the iseq.
+ class ISeq # :nodoc:
+ attr_reader :parts
+
+ def initialize(parts)
+ @parts = parts
+ end
+
+ def type
+ parts[0]
+ end
+
+ def local_table
+ parts[10]
+ end
+
+ def instructions
+ parts[13]
+ end
+
+ def each_child
+ instructions.each do |instruction|
+ # Only look at arrays. Other instructions are line numbers or
+ # tracepoint events.
+ next unless instruction.is_a?(Array)
+
+ instruction.each do |opnd|
+ # Only look at arrays. Other operands are literals.
+ next unless opnd.is_a?(Array)
+
+ # Only look at instruction sequences. Other operands are literals.
+ next unless opnd[0] == "YARVInstructionSequence/SimpleDataFormat"
+
+ yield ISeq.new(opnd)
+ end
+ end
+ end
+ end
+
+ private_constant :ISeq
+
+ # :call-seq:
+ # Debug::cruby_locals(source) -> Array
+ #
+ # For the given source, compiles with CRuby and returns a list of all of the
+ # sets of local variables that were encountered.
+ def self.cruby_locals(source)
+ verbose, $VERBOSE = $VERBOSE, nil
+
+ begin
+ locals = [] #: Array[Array[Symbol | Integer]]
+ stack = [ISeq.new(RubyVM::InstructionSequence.compile(source).to_a)]
+
+ while (iseq = stack.pop)
+ names = [*iseq.local_table]
+ names.map!.with_index do |name, index|
+ # When an anonymous local variable is present in the iseq's local
+ # table, it is represented as the stack offset from the top.
+ # However, when these are dumped to binary and read back in, they
+ # are replaced with the symbol :#arg_rest. To consistently handle
+ # this, we replace them here with their index.
+ if name == :"#arg_rest"
+ names.length - index + 1
+ else
+ name
+ end
+ end
+
+ locals << names
+ iseq.each_child { |child| stack << child }
+ end
+
+ locals
+ ensure
+ $VERBOSE = verbose
+ end
+ end
+
+ # Used to hold the place of a local that will be in the local table but
+ # cannot be accessed directly from the source code. For example, the
+ # iteration variable in a for loop or the positional parameter on a method
+ # definition that is destructured.
+ AnonymousLocal = Object.new
+ private_constant :AnonymousLocal
+
+ # :call-seq:
+ # Debug::prism_locals(source) -> Array
+ #
+ # For the given source, parses with prism and returns a list of all of the
+ # sets of local variables that were encountered.
+ def self.prism_locals(source)
+ locals = [] #: Array[Array[Symbol | Integer]]
+ stack = [Prism.parse(source).value] #: Array[Prism::node]
+
+ while (node = stack.pop)
+ case node
+ when BlockNode, DefNode, LambdaNode
+ names = node.locals
+ params =
+ if node.is_a?(DefNode)
+ node.parameters
+ elsif node.parameters.is_a?(NumberedParametersNode)
+ nil
+ else
+ node.parameters&.parameters
+ end
+
+ # prism places parameters in the same order that they appear in the
+ # source. CRuby places them in the order that they need to appear
+ # according to their own internal calling convention. We mimic that
+ # order here so that we can compare properly.
+ if params
+ sorted = [
+ *params.requireds.map do |required|
+ if required.is_a?(RequiredParameterNode)
+ required.name
+ else
+ AnonymousLocal
+ end
+ end,
+ *params.optionals.map(&:name),
+ *((params.rest.name || :*) if params.rest && !params.rest.is_a?(ImplicitRestNode)),
+ *params.posts.map do |post|
+ if post.is_a?(RequiredParameterNode)
+ post.name
+ else
+ AnonymousLocal
+ end
+ end,
+ *params.keywords.grep(RequiredKeywordParameterNode).map(&:name),
+ *params.keywords.grep(OptionalKeywordParameterNode).map(&:name),
+ ]
+
+ sorted << AnonymousLocal if params.keywords.any?
+
+ if params.keyword_rest.is_a?(ForwardingParameterNode)
+ sorted.push(:*, :**, :&, :"...")
+ elsif params.keyword_rest.is_a?(KeywordRestParameterNode)
+ sorted << (params.keyword_rest.name || :**)
+ end
+
+ # Recurse down the parameter tree to find any destructured
+ # parameters and add them after the other parameters.
+ param_stack = params.requireds.concat(params.posts).grep(MultiTargetNode).reverse
+ while (param = param_stack.pop)
+ case param
+ when MultiTargetNode
+ param_stack.concat(param.rights.reverse)
+ param_stack << param.rest if param.rest&.expression && !sorted.include?(param.rest.expression.name)
+ param_stack.concat(param.lefts.reverse)
+ when RequiredParameterNode
+ sorted << param.name
+ when SplatNode
+ sorted << param.expression.name
+ end
+ end
+
+ if params.block
+ sorted << (params.block.name || :&)
+ end
+
+ names = sorted.concat(names - sorted)
+ end
+
+ names.map!.with_index do |name, index|
+ if name == AnonymousLocal
+ names.length - index + 1
+ else
+ name
+ end
+ end
+
+ locals << names
+ when ClassNode, ModuleNode, ProgramNode, SingletonClassNode
+ locals << node.locals
+ when ForNode
+ locals << [2]
+ when PostExecutionNode
+ locals.push([], [])
+ when InterpolatedRegularExpressionNode
+ locals << [] if node.once?
+ end
+
+ stack.concat(node.compact_child_nodes)
+ end
+
+ locals
+ end
+
+ # :call-seq:
+ # Debug::newlines(source) -> Array
+ #
+ # For the given source string, return the byte offsets of every newline in
+ # the source.
+ def self.newlines(source)
+ Prism.parse(source).source.offsets
+ end
+
+ # A wrapping around prism's internal encoding data structures. This is used
+ # for reflection and debugging purposes.
+ class Encoding
+ # The name of the encoding, that can be passed to Encoding.find.
+ attr_reader :name
+
+ # Initialize a new encoding with the given name and whether or not it is
+ # a multibyte encoding.
+ def initialize(name, multibyte)
+ @name = name
+ @multibyte = multibyte
+ end
+
+ # Whether or not the encoding is a multibyte encoding.
+ def multibyte?
+ @multibyte
+ end
+
+ # Returns the number of bytes of the first character in the source string,
+ # if it is valid for the encoding. Otherwise, returns 0.
+ def width(source)
+ Encoding._width(name, source)
+ end
+
+ # Returns true if the first character in the source string is a valid
+ # alphanumeric character for the encoding.
+ def alnum?(source)
+ Encoding._alnum?(name, source)
+ end
+
+ # Returns true if the first character in the source string is a valid
+ # alphabetic character for the encoding.
+ def alpha?(source)
+ Encoding._alpha?(name, source)
+ end
+
+ # Returns true if the first character in the source string is a valid
+ # uppercase character for the encoding.
+ def upper?(source)
+ Encoding._upper?(name, source)
+ end
+ end
+ end
+end
diff --git a/lib/prism/desugar_compiler.rb b/lib/prism/desugar_compiler.rb
new file mode 100644
index 0000000000..9b62c00df3
--- /dev/null
+++ b/lib/prism/desugar_compiler.rb
@@ -0,0 +1,354 @@
+# frozen_string_literal: true
+
+module Prism
+ class DesugarAndWriteNode # :nodoc:
+ attr_reader :node, :source, :read_class, :write_class, :arguments
+
+ def initialize(node, source, read_class, write_class, *arguments)
+ @node = node
+ @source = source
+ @read_class = read_class
+ @write_class = write_class
+ @arguments = arguments
+ end
+
+ # Desugar `x &&= y` to `x && x = y`
+ def compile
+ AndNode.new(
+ source,
+ read_class.new(source, *arguments, node.name_loc),
+ write_class.new(source, *arguments, node.name_loc, node.value, node.operator_loc, node.location),
+ node.operator_loc,
+ node.location
+ )
+ end
+ end
+
+ class DesugarOrWriteDefinedNode # :nodoc:
+ attr_reader :node, :source, :read_class, :write_class, :arguments
+
+ def initialize(node, source, read_class, write_class, *arguments)
+ @node = node
+ @source = source
+ @read_class = read_class
+ @write_class = write_class
+ @arguments = arguments
+ end
+
+ # Desugar `x ||= y` to `defined?(x) ? x : x = y`
+ def compile
+ IfNode.new(
+ source,
+ node.operator_loc,
+ DefinedNode.new(source, nil, read_class.new(source, *arguments, node.name_loc), nil, node.operator_loc, node.name_loc),
+ node.operator_loc,
+ StatementsNode.new(source, [read_class.new(source, *arguments, node.name_loc)], node.location),
+ ElseNode.new(
+ source,
+ node.operator_loc,
+ StatementsNode.new(
+ source,
+ [write_class.new(source, *arguments, node.name_loc, node.value, node.operator_loc, node.location)],
+ node.location
+ ),
+ node.operator_loc,
+ node.location
+ ),
+ node.operator_loc,
+ node.location
+ )
+ end
+ end
+
+ class DesugarOperatorWriteNode # :nodoc:
+ attr_reader :node, :source, :read_class, :write_class, :arguments
+
+ def initialize(node, source, read_class, write_class, *arguments)
+ @node = node
+ @source = source
+ @read_class = read_class
+ @write_class = write_class
+ @arguments = arguments
+ end
+
+ # Desugar `x += y` to `x = x + y`
+ def compile
+ operator_loc = node.operator_loc.chop
+
+ write_class.new(
+ source,
+ *arguments,
+ node.name_loc,
+ CallNode.new(
+ source,
+ 0,
+ read_class.new(source, *arguments, node.name_loc),
+ nil,
+ operator_loc.slice.to_sym,
+ operator_loc,
+ nil,
+ ArgumentsNode.new(source, 0, [node.value], node.value.location),
+ nil,
+ nil,
+ node.location
+ ),
+ node.operator_loc.copy(start_offset: node.operator_loc.end_offset - 1, length: 1),
+ node.location
+ )
+ end
+ end
+
+ class DesugarOrWriteNode # :nodoc:
+ attr_reader :node, :source, :read_class, :write_class, :arguments
+
+ def initialize(node, source, read_class, write_class, *arguments)
+ @node = node
+ @source = source
+ @read_class = read_class
+ @write_class = write_class
+ @arguments = arguments
+ end
+
+ # Desugar `x ||= y` to `x || x = y`
+ def compile
+ OrNode.new(
+ source,
+ read_class.new(source, *arguments, node.name_loc),
+ write_class.new(source, *arguments, node.name_loc, node.value, node.operator_loc, node.location),
+ node.operator_loc,
+ node.location
+ )
+ end
+ end
+
+ private_constant :DesugarAndWriteNode, :DesugarOrWriteNode, :DesugarOrWriteDefinedNode, :DesugarOperatorWriteNode
+
+ class ClassVariableAndWriteNode
+ def desugar # :nodoc:
+ DesugarAndWriteNode.new(self, source, ClassVariableReadNode, ClassVariableWriteNode, name).compile
+ end
+ end
+
+ class ClassVariableOrWriteNode
+ def desugar # :nodoc:
+ DesugarOrWriteDefinedNode.new(self, source, ClassVariableReadNode, ClassVariableWriteNode, name).compile
+ end
+ end
+
+ class ClassVariableOperatorWriteNode
+ def desugar # :nodoc:
+ DesugarOperatorWriteNode.new(self, source, ClassVariableReadNode, ClassVariableWriteNode, name).compile
+ end
+ end
+
+ class ConstantAndWriteNode
+ def desugar # :nodoc:
+ DesugarAndWriteNode.new(self, source, ConstantReadNode, ConstantWriteNode, name).compile
+ end
+ end
+
+ class ConstantOrWriteNode
+ def desugar # :nodoc:
+ DesugarOrWriteDefinedNode.new(self, source, ConstantReadNode, ConstantWriteNode, name).compile
+ end
+ end
+
+ class ConstantOperatorWriteNode
+ def desugar # :nodoc:
+ DesugarOperatorWriteNode.new(self, source, ConstantReadNode, ConstantWriteNode, name).compile
+ end
+ end
+
+ class GlobalVariableAndWriteNode
+ def desugar # :nodoc:
+ DesugarAndWriteNode.new(self, source, GlobalVariableReadNode, GlobalVariableWriteNode, name).compile
+ end
+ end
+
+ class GlobalVariableOrWriteNode
+ def desugar # :nodoc:
+ DesugarOrWriteDefinedNode.new(self, source, GlobalVariableReadNode, GlobalVariableWriteNode, name).compile
+ end
+ end
+
+ class GlobalVariableOperatorWriteNode
+ def desugar # :nodoc:
+ DesugarOperatorWriteNode.new(self, source, GlobalVariableReadNode, GlobalVariableWriteNode, name).compile
+ end
+ end
+
+ class InstanceVariableAndWriteNode
+ def desugar # :nodoc:
+ DesugarAndWriteNode.new(self, source, InstanceVariableReadNode, InstanceVariableWriteNode, name).compile
+ end
+ end
+
+ class InstanceVariableOrWriteNode
+ def desugar # :nodoc:
+ DesugarOrWriteNode.new(self, source, InstanceVariableReadNode, InstanceVariableWriteNode, name).compile
+ end
+ end
+
+ class InstanceVariableOperatorWriteNode
+ def desugar # :nodoc:
+ DesugarOperatorWriteNode.new(self, source, InstanceVariableReadNode, InstanceVariableWriteNode, name).compile
+ end
+ end
+
+ class LocalVariableAndWriteNode
+ def desugar # :nodoc:
+ DesugarAndWriteNode.new(self, source, LocalVariableReadNode, LocalVariableWriteNode, name, depth).compile
+ end
+ end
+
+ class LocalVariableOrWriteNode
+ def desugar # :nodoc:
+ DesugarOrWriteNode.new(self, source, LocalVariableReadNode, LocalVariableWriteNode, name, depth).compile
+ end
+ end
+
+ class LocalVariableOperatorWriteNode
+ def desugar # :nodoc:
+ DesugarOperatorWriteNode.new(self, source, LocalVariableReadNode, LocalVariableWriteNode, name, depth).compile
+ end
+ end
+
+ # DesugarCompiler is a compiler that desugars Ruby code into a more primitive
+ # form. This is useful for consumers that want to deal with fewer node types.
+ class DesugarCompiler < MutationCompiler
+ # @@foo &&= bar
+ #
+ # becomes
+ #
+ # @@foo && @@foo = bar
+ def visit_class_variable_and_write_node(node)
+ node.desugar
+ end
+
+ # @@foo ||= bar
+ #
+ # becomes
+ #
+ # defined?(@@foo) ? @@foo : @@foo = bar
+ def visit_class_variable_or_write_node(node)
+ node.desugar
+ end
+
+ # @@foo += bar
+ #
+ # becomes
+ #
+ # @@foo = @@foo + bar
+ def visit_class_variable_operator_write_node(node)
+ node.desugar
+ end
+
+ # Foo &&= bar
+ #
+ # becomes
+ #
+ # Foo && Foo = bar
+ def visit_constant_and_write_node(node)
+ node.desugar
+ end
+
+ # Foo ||= bar
+ #
+ # becomes
+ #
+ # defined?(Foo) ? Foo : Foo = bar
+ def visit_constant_or_write_node(node)
+ node.desugar
+ end
+
+ # Foo += bar
+ #
+ # becomes
+ #
+ # Foo = Foo + bar
+ def visit_constant_operator_write_node(node)
+ node.desugar
+ end
+
+ # $foo &&= bar
+ #
+ # becomes
+ #
+ # $foo && $foo = bar
+ def visit_global_variable_and_write_node(node)
+ node.desugar
+ end
+
+ # $foo ||= bar
+ #
+ # becomes
+ #
+ # defined?($foo) ? $foo : $foo = bar
+ def visit_global_variable_or_write_node(node)
+ node.desugar
+ end
+
+ # $foo += bar
+ #
+ # becomes
+ #
+ # $foo = $foo + bar
+ def visit_global_variable_operator_write_node(node)
+ node.desugar
+ end
+
+ # @foo &&= bar
+ #
+ # becomes
+ #
+ # @foo && @foo = bar
+ def visit_instance_variable_and_write_node(node)
+ node.desugar
+ end
+
+ # @foo ||= bar
+ #
+ # becomes
+ #
+ # @foo || @foo = bar
+ def visit_instance_variable_or_write_node(node)
+ node.desugar
+ end
+
+ # @foo += bar
+ #
+ # becomes
+ #
+ # @foo = @foo + bar
+ def visit_instance_variable_operator_write_node(node)
+ node.desugar
+ end
+
+ # foo &&= bar
+ #
+ # becomes
+ #
+ # foo && foo = bar
+ def visit_local_variable_and_write_node(node)
+ node.desugar
+ end
+
+ # foo ||= bar
+ #
+ # becomes
+ #
+ # foo || foo = bar
+ def visit_local_variable_or_write_node(node)
+ node.desugar
+ end
+
+ # foo += bar
+ #
+ # becomes
+ #
+ # foo = foo + bar
+ def visit_local_variable_operator_write_node(node)
+ node.desugar
+ end
+ end
+end
diff --git a/lib/prism/ffi.rb b/lib/prism/ffi.rb
new file mode 100644
index 0000000000..2014ccea31
--- /dev/null
+++ b/lib/prism/ffi.rb
@@ -0,0 +1,437 @@
+# frozen_string_literal: true
+# typed: ignore
+
+# This file is responsible for mirroring the API provided by the C extension by
+# using FFI to call into the shared library.
+
+require "rbconfig"
+require "ffi"
+
+module Prism
+ module LibRubyParser # :nodoc:
+ extend FFI::Library
+
+ # Define the library that we will be pulling functions from. Note that this
+ # must align with the build shared library from make/rake.
+ ffi_lib File.expand_path("../../build/libprism.#{RbConfig::CONFIG["SOEXT"]}", __dir__)
+
+ # Convert a native C type declaration into a symbol that FFI understands.
+ # For example:
+ #
+ # const char * -> :pointer
+ # bool -> :bool
+ # size_t -> :size_t
+ # void -> :void
+ #
+ def self.resolve_type(type, callbacks)
+ type = type.strip
+
+ if !type.end_with?("*")
+ type.delete_prefix("const ").to_sym
+ else
+ type = type.delete_suffix("*").rstrip
+ callbacks.include?(type.to_sym) ? type.to_sym : :pointer
+ end
+ end
+
+ # Read through the given header file and find the declaration of each of the
+ # given functions. For each one, define a function with the same name and
+ # signature as the C function.
+ def self.load_exported_functions_from(header, *functions, callbacks)
+ File.foreach(File.expand_path("../../include/#{header}", __dir__)) do |line|
+ # We only want to attempt to load exported functions.
+ next unless line.start_with?("PRISM_EXPORTED_FUNCTION ")
+
+ # We only want to load the functions that we are interested in.
+ next unless functions.any? { |function| line.include?(function) }
+
+ # Parse the function declaration.
+ unless /^PRISM_EXPORTED_FUNCTION (?<return_type>.+) (?<name>\w+)\((?<arg_types>.+)\);$/ =~ line
+ raise "Could not parse #{line}"
+ end
+
+ # Delete the function from the list of functions we are looking for to
+ # mark it as having been found.
+ functions.delete(name)
+
+ # Split up the argument types into an array, ensure we handle the case
+ # where there are no arguments (by explicit void).
+ arg_types = arg_types.split(",").map(&:strip)
+ arg_types = [] if arg_types == %w[void]
+
+ # Resolve the type of the argument by dropping the name of the argument
+ # first if it is present.
+ arg_types.map! { |type| resolve_type(type.sub(/\w+$/, ""), callbacks) }
+
+ # Attach the function using the FFI library.
+ attach_function name, arg_types, resolve_type(return_type, [])
+ end
+
+ # If we didn't find all of the functions, raise an error.
+ raise "Could not find functions #{functions.inspect}" unless functions.empty?
+ end
+
+ callback :pm_parse_stream_fgets_t, [:pointer, :int, :pointer], :pointer
+
+ load_exported_functions_from(
+ "prism.h",
+ "pm_version",
+ "pm_serialize_parse",
+ "pm_serialize_parse_stream",
+ "pm_serialize_parse_comments",
+ "pm_serialize_lex",
+ "pm_serialize_parse_lex",
+ "pm_parse_success_p",
+ [:pm_parse_stream_fgets_t]
+ )
+
+ load_exported_functions_from(
+ "prism/util/pm_buffer.h",
+ "pm_buffer_sizeof",
+ "pm_buffer_init",
+ "pm_buffer_value",
+ "pm_buffer_length",
+ "pm_buffer_free",
+ []
+ )
+
+ load_exported_functions_from(
+ "prism/util/pm_string.h",
+ "pm_string_mapped_init",
+ "pm_string_free",
+ "pm_string_source",
+ "pm_string_length",
+ "pm_string_sizeof",
+ []
+ )
+
+ # This object represents a pm_buffer_t. We only use it as an opaque pointer,
+ # so it doesn't need to know the fields of pm_buffer_t.
+ class PrismBuffer # :nodoc:
+ SIZEOF = LibRubyParser.pm_buffer_sizeof
+
+ attr_reader :pointer
+
+ def initialize(pointer)
+ @pointer = pointer
+ end
+
+ def value
+ LibRubyParser.pm_buffer_value(pointer)
+ end
+
+ def length
+ LibRubyParser.pm_buffer_length(pointer)
+ end
+
+ def read
+ value.read_string(length)
+ end
+
+ # Initialize a new buffer and yield it to the block. The buffer will be
+ # automatically freed when the block returns.
+ def self.with
+ FFI::MemoryPointer.new(SIZEOF) do |pointer|
+ raise unless LibRubyParser.pm_buffer_init(pointer)
+ return yield new(pointer)
+ ensure
+ LibRubyParser.pm_buffer_free(pointer)
+ end
+ end
+ end
+
+ # This object represents a pm_string_t. We only use it as an opaque pointer,
+ # so it doesn't have to be an FFI::Struct.
+ class PrismString # :nodoc:
+ SIZEOF = LibRubyParser.pm_string_sizeof
+
+ attr_reader :pointer, :length
+
+ def initialize(pointer, length, from_string)
+ @pointer = pointer
+ @length = length
+ @from_string = from_string
+ end
+
+ def read
+ raise "should use the original String instead" if @from_string
+ @pointer.read_string(@length)
+ end
+
+ # Yields a pm_string_t pointer to the given block.
+ def self.with_string(string)
+ raise TypeError unless string.is_a?(String)
+
+ length = string.bytesize
+ # + 1 to never get an address of 0, which pm_parser_init() asserts
+ FFI::MemoryPointer.new(:char, length + 1, false) do |pointer|
+ pointer.write_string(string)
+ # since we have the extra byte we might as well \0-terminate
+ pointer.put_char(length, 0)
+ return yield new(pointer, length, true)
+ end
+ end
+
+ # Yields a pm_string_t pointer to the given block.
+ def self.with_file(filepath)
+ raise TypeError unless filepath.is_a?(String)
+
+ FFI::MemoryPointer.new(SIZEOF) do |pm_string|
+ if LibRubyParser.pm_string_mapped_init(pm_string, filepath)
+ pointer = LibRubyParser.pm_string_source(pm_string)
+ length = LibRubyParser.pm_string_length(pm_string)
+ return yield new(pointer, length, false)
+ else
+ raise SystemCallError.new(filepath, FFI.errno)
+ end
+ ensure
+ LibRubyParser.pm_string_free(pm_string)
+ end
+ end
+ end
+ end
+
+ # Mark the LibRubyParser module as private as it should only be called through
+ # the prism module.
+ private_constant :LibRubyParser
+
+ # The version constant is set by reading the result of calling pm_version.
+ VERSION = LibRubyParser.pm_version.read_string
+
+ class << self
+ # Mirror the Prism.dump API by using the serialization API.
+ def dump(code, **options)
+ LibRubyParser::PrismString.with_string(code) { |string| dump_common(string, options) }
+ end
+
+ # Mirror the Prism.dump_file API by using the serialization API.
+ def dump_file(filepath, **options)
+ options[:filepath] = filepath
+ LibRubyParser::PrismString.with_file(filepath) { |string| dump_common(string, options) }
+ end
+
+ # Mirror the Prism.lex API by using the serialization API.
+ def lex(code, **options)
+ LibRubyParser::PrismString.with_string(code) { |string| lex_common(string, code, options) }
+ end
+
+ # Mirror the Prism.lex_file API by using the serialization API.
+ def lex_file(filepath, **options)
+ options[:filepath] = filepath
+ LibRubyParser::PrismString.with_file(filepath) { |string| lex_common(string, string.read, options) }
+ end
+
+ # Mirror the Prism.parse API by using the serialization API.
+ def parse(code, **options)
+ LibRubyParser::PrismString.with_string(code) { |string| parse_common(string, code, options) }
+ end
+
+ # Mirror the Prism.parse_file API by using the serialization API. This uses
+ # native strings instead of Ruby strings because it allows us to use mmap
+ # when it is available.
+ def parse_file(filepath, **options)
+ options[:filepath] = filepath
+ LibRubyParser::PrismString.with_file(filepath) { |string| parse_common(string, string.read, options) }
+ end
+
+ # Mirror the Prism.parse_stream API by using the serialization API.
+ def parse_stream(stream, **options)
+ LibRubyParser::PrismBuffer.with do |buffer|
+ source = +""
+ callback = -> (string, size, _) {
+ raise "Expected size to be >= 0, got: #{size}" if size <= 0
+
+ if !(line = stream.gets(size - 1)).nil?
+ source << line
+ string.write_string("#{line}\x00", line.bytesize + 1)
+ end
+ }
+
+ # In the pm_serialize_parse_stream function it accepts a pointer to the
+ # IO object as a void* and then passes it through to the callback as the
+ # third argument, but it never touches it itself. As such, since we have
+ # access to the IO object already through the closure of the lambda, we
+ # can pass a null pointer here and not worry.
+ LibRubyParser.pm_serialize_parse_stream(buffer.pointer, nil, callback, dump_options(options))
+ Prism.load(source, buffer.read)
+ end
+ end
+
+ # Mirror the Prism.parse_comments API by using the serialization API.
+ def parse_comments(code, **options)
+ LibRubyParser::PrismString.with_string(code) { |string| parse_comments_common(string, code, options) }
+ end
+
+ # Mirror the Prism.parse_file_comments API by using the serialization
+ # API. This uses native strings instead of Ruby strings because it allows us
+ # to use mmap when it is available.
+ def parse_file_comments(filepath, **options)
+ options[:filepath] = filepath
+ LibRubyParser::PrismString.with_file(filepath) { |string| parse_comments_common(string, string.read, options) }
+ end
+
+ # Mirror the Prism.parse_lex API by using the serialization API.
+ def parse_lex(code, **options)
+ LibRubyParser::PrismString.with_string(code) { |string| parse_lex_common(string, code, options) }
+ end
+
+ # Mirror the Prism.parse_lex_file API by using the serialization API.
+ def parse_lex_file(filepath, **options)
+ options[:filepath] = filepath
+ LibRubyParser::PrismString.with_file(filepath) { |string| parse_lex_common(string, string.read, options) }
+ end
+
+ # Mirror the Prism.parse_success? API by using the serialization API.
+ def parse_success?(code, **options)
+ LibRubyParser::PrismString.with_string(code) { |string| parse_file_success_common(string, options) }
+ end
+
+ # Mirror the Prism.parse_failure? API by using the serialization API.
+ def parse_failure?(code, **options)
+ !parse_success?(code, **options)
+ end
+
+ # Mirror the Prism.parse_file_success? API by using the serialization API.
+ def parse_file_success?(filepath, **options)
+ options[:filepath] = filepath
+ LibRubyParser::PrismString.with_file(filepath) { |string| parse_file_success_common(string, options) }
+ end
+
+ # Mirror the Prism.parse_file_failure? API by using the serialization API.
+ def parse_file_failure?(filepath, **options)
+ !parse_file_success?(filepath, **options)
+ end
+
+ private
+
+ def dump_common(string, options) # :nodoc:
+ LibRubyParser::PrismBuffer.with do |buffer|
+ LibRubyParser.pm_serialize_parse(buffer.pointer, string.pointer, string.length, dump_options(options))
+ buffer.read
+ end
+ end
+
+ def lex_common(string, code, options) # :nodoc:
+ serialized = LibRubyParser::PrismBuffer.with do |buffer|
+ LibRubyParser.pm_serialize_lex(buffer.pointer, string.pointer, string.length, dump_options(options))
+ buffer.read
+ end
+
+ Serialize.load_tokens(Source.new(code), serialized)
+ end
+
+ def parse_common(string, code, options) # :nodoc:
+ serialized = dump_common(string, options)
+ Prism.load(code, serialized)
+ end
+
+ def parse_comments_common(string, code, options) # :nodoc:
+ LibRubyParser::PrismBuffer.with do |buffer|
+ LibRubyParser.pm_serialize_parse_comments(buffer.pointer, string.pointer, string.length, dump_options(options))
+
+ source = Source.new(code)
+ loader = Serialize::Loader.new(source, buffer.read)
+
+ loader.load_header
+ loader.load_encoding
+ loader.load_start_line
+ loader.load_comments
+ end
+ end
+
+ def parse_lex_common(string, code, options) # :nodoc:
+ LibRubyParser::PrismBuffer.with do |buffer|
+ LibRubyParser.pm_serialize_parse_lex(buffer.pointer, string.pointer, string.length, dump_options(options))
+
+ source = Source.new(code)
+ loader = Serialize::Loader.new(source, buffer.read)
+
+ tokens = loader.load_tokens
+ node, comments, magic_comments, data_loc, errors, warnings = loader.load_nodes
+ tokens.each { |token,| token.value.force_encoding(loader.encoding) }
+
+ ParseLexResult.new([node, tokens], comments, magic_comments, data_loc, errors, warnings, source)
+ end
+ end
+
+ def parse_file_success_common(string, options) # :nodoc:
+ LibRubyParser.pm_parse_success_p(string.pointer, string.length, dump_options(options))
+ end
+
+ # Return the value that should be dumped for the command_line option.
+ def dump_options_command_line(options)
+ command_line = options.fetch(:command_line, "")
+ raise ArgumentError, "command_line must be a string" unless command_line.is_a?(String)
+
+ command_line.each_char.inject(0) do |value, char|
+ case char
+ when "a" then value | 0b000001
+ when "e" then value | 0b000010
+ when "l" then value | 0b000100
+ when "n" then value | 0b001000
+ when "p" then value | 0b010000
+ when "x" then value | 0b100000
+ else raise ArgumentError, "invalid command_line option: #{char}"
+ end
+ end
+ end
+
+ # Convert the given options into a serialized options string.
+ def dump_options(options)
+ template = +""
+ values = []
+
+ template << "L"
+ if (filepath = options[:filepath])
+ values.push(filepath.bytesize, filepath.b)
+ template << "A*"
+ else
+ values << 0
+ end
+
+ template << "l"
+ values << options.fetch(:line, 1)
+
+ template << "L"
+ if (encoding = options[:encoding])
+ name = encoding.name
+ values.push(name.bytesize, name.b)
+ template << "A*"
+ else
+ values << 0
+ end
+
+ template << "C"
+ values << (options.fetch(:frozen_string_literal, false) ? 1 : 0)
+
+ template << "C"
+ values << dump_options_command_line(options)
+
+ template << "C"
+ values << { nil => 0, "3.3.0" => 1, "3.4.0" => 0, "latest" => 0 }.fetch(options[:version])
+
+ template << "L"
+ if (scopes = options[:scopes])
+ values << scopes.length
+
+ scopes.each do |scope|
+ template << "L"
+ values << scope.length
+
+ scope.each do |local|
+ name = local.name
+ template << "L"
+ values << name.bytesize
+
+ template << "A*"
+ values << name.b
+ end
+ end
+ else
+ values << 0
+ end
+
+ values.pack(template)
+ end
+ end
+end
diff --git a/lib/prism/lex_compat.rb b/lib/prism/lex_compat.rb
new file mode 100644
index 0000000000..f199af1883
--- /dev/null
+++ b/lib/prism/lex_compat.rb
@@ -0,0 +1,927 @@
+# frozen_string_literal: true
+
+require "delegate"
+require "ripper"
+
+module Prism
+ # This class is responsible for lexing the source using prism and then
+ # converting those tokens to be compatible with Ripper. In the vast majority
+ # of cases, this is a one-to-one mapping of the token type. Everything else
+ # generally lines up. However, there are a few cases that require special
+ # handling.
+ class LexCompat # :nodoc:
+ # A result class specialized for holding tokens produced by the lexer.
+ class Result < Prism::Result
+ # The list of tokens that were produced by the lexer.
+ attr_reader :value
+
+ # Create a new lex compat result object with the given values.
+ def initialize(value, comments, magic_comments, data_loc, errors, warnings, source)
+ @value = value
+ super(comments, magic_comments, data_loc, errors, warnings, source)
+ end
+
+ # Implement the hash pattern matching interface for Result.
+ def deconstruct_keys(keys)
+ super.merge!(value: value)
+ end
+ end
+
+ # This is a mapping of prism token types to Ripper token types. This is a
+ # many-to-one mapping because we split up our token types, whereas Ripper
+ # tends to group them.
+ RIPPER = {
+ AMPERSAND: :on_op,
+ AMPERSAND_AMPERSAND: :on_op,
+ AMPERSAND_AMPERSAND_EQUAL: :on_op,
+ AMPERSAND_DOT: :on_op,
+ AMPERSAND_EQUAL: :on_op,
+ BACK_REFERENCE: :on_backref,
+ BACKTICK: :on_backtick,
+ BANG: :on_op,
+ BANG_EQUAL: :on_op,
+ BANG_TILDE: :on_op,
+ BRACE_LEFT: :on_lbrace,
+ BRACE_RIGHT: :on_rbrace,
+ BRACKET_LEFT: :on_lbracket,
+ BRACKET_LEFT_ARRAY: :on_lbracket,
+ BRACKET_LEFT_RIGHT: :on_op,
+ BRACKET_LEFT_RIGHT_EQUAL: :on_op,
+ BRACKET_RIGHT: :on_rbracket,
+ CARET: :on_op,
+ CARET_EQUAL: :on_op,
+ CHARACTER_LITERAL: :on_CHAR,
+ CLASS_VARIABLE: :on_cvar,
+ COLON: :on_op,
+ COLON_COLON: :on_op,
+ COMMA: :on_comma,
+ COMMENT: :on_comment,
+ CONSTANT: :on_const,
+ DOT: :on_period,
+ DOT_DOT: :on_op,
+ DOT_DOT_DOT: :on_op,
+ EMBDOC_BEGIN: :on_embdoc_beg,
+ EMBDOC_END: :on_embdoc_end,
+ EMBDOC_LINE: :on_embdoc,
+ EMBEXPR_BEGIN: :on_embexpr_beg,
+ EMBEXPR_END: :on_embexpr_end,
+ EMBVAR: :on_embvar,
+ EOF: :on_eof,
+ EQUAL: :on_op,
+ EQUAL_EQUAL: :on_op,
+ EQUAL_EQUAL_EQUAL: :on_op,
+ EQUAL_GREATER: :on_op,
+ EQUAL_TILDE: :on_op,
+ FLOAT: :on_float,
+ FLOAT_IMAGINARY: :on_imaginary,
+ FLOAT_RATIONAL: :on_rational,
+ FLOAT_RATIONAL_IMAGINARY: :on_imaginary,
+ GREATER: :on_op,
+ GREATER_EQUAL: :on_op,
+ GREATER_GREATER: :on_op,
+ GREATER_GREATER_EQUAL: :on_op,
+ GLOBAL_VARIABLE: :on_gvar,
+ HEREDOC_END: :on_heredoc_end,
+ HEREDOC_START: :on_heredoc_beg,
+ IDENTIFIER: :on_ident,
+ IGNORED_NEWLINE: :on_ignored_nl,
+ INTEGER: :on_int,
+ INTEGER_IMAGINARY: :on_imaginary,
+ INTEGER_RATIONAL: :on_rational,
+ INTEGER_RATIONAL_IMAGINARY: :on_imaginary,
+ INSTANCE_VARIABLE: :on_ivar,
+ INVALID: :INVALID,
+ KEYWORD___ENCODING__: :on_kw,
+ KEYWORD___LINE__: :on_kw,
+ KEYWORD___FILE__: :on_kw,
+ KEYWORD_ALIAS: :on_kw,
+ KEYWORD_AND: :on_kw,
+ KEYWORD_BEGIN: :on_kw,
+ KEYWORD_BEGIN_UPCASE: :on_kw,
+ KEYWORD_BREAK: :on_kw,
+ KEYWORD_CASE: :on_kw,
+ KEYWORD_CLASS: :on_kw,
+ KEYWORD_DEF: :on_kw,
+ KEYWORD_DEFINED: :on_kw,
+ KEYWORD_DO: :on_kw,
+ KEYWORD_DO_LOOP: :on_kw,
+ KEYWORD_ELSE: :on_kw,
+ KEYWORD_ELSIF: :on_kw,
+ KEYWORD_END: :on_kw,
+ KEYWORD_END_UPCASE: :on_kw,
+ KEYWORD_ENSURE: :on_kw,
+ KEYWORD_FALSE: :on_kw,
+ KEYWORD_FOR: :on_kw,
+ KEYWORD_IF: :on_kw,
+ KEYWORD_IF_MODIFIER: :on_kw,
+ KEYWORD_IN: :on_kw,
+ KEYWORD_MODULE: :on_kw,
+ KEYWORD_NEXT: :on_kw,
+ KEYWORD_NIL: :on_kw,
+ KEYWORD_NOT: :on_kw,
+ KEYWORD_OR: :on_kw,
+ KEYWORD_REDO: :on_kw,
+ KEYWORD_RESCUE: :on_kw,
+ KEYWORD_RESCUE_MODIFIER: :on_kw,
+ KEYWORD_RETRY: :on_kw,
+ KEYWORD_RETURN: :on_kw,
+ KEYWORD_SELF: :on_kw,
+ KEYWORD_SUPER: :on_kw,
+ KEYWORD_THEN: :on_kw,
+ KEYWORD_TRUE: :on_kw,
+ KEYWORD_UNDEF: :on_kw,
+ KEYWORD_UNLESS: :on_kw,
+ KEYWORD_UNLESS_MODIFIER: :on_kw,
+ KEYWORD_UNTIL: :on_kw,
+ KEYWORD_UNTIL_MODIFIER: :on_kw,
+ KEYWORD_WHEN: :on_kw,
+ KEYWORD_WHILE: :on_kw,
+ KEYWORD_WHILE_MODIFIER: :on_kw,
+ KEYWORD_YIELD: :on_kw,
+ LABEL: :on_label,
+ LABEL_END: :on_label_end,
+ LAMBDA_BEGIN: :on_tlambeg,
+ LESS: :on_op,
+ LESS_EQUAL: :on_op,
+ LESS_EQUAL_GREATER: :on_op,
+ LESS_LESS: :on_op,
+ LESS_LESS_EQUAL: :on_op,
+ METHOD_NAME: :on_ident,
+ MINUS: :on_op,
+ MINUS_EQUAL: :on_op,
+ MINUS_GREATER: :on_tlambda,
+ NEWLINE: :on_nl,
+ NUMBERED_REFERENCE: :on_backref,
+ PARENTHESIS_LEFT: :on_lparen,
+ PARENTHESIS_LEFT_PARENTHESES: :on_lparen,
+ PARENTHESIS_RIGHT: :on_rparen,
+ PERCENT: :on_op,
+ PERCENT_EQUAL: :on_op,
+ PERCENT_LOWER_I: :on_qsymbols_beg,
+ PERCENT_LOWER_W: :on_qwords_beg,
+ PERCENT_LOWER_X: :on_backtick,
+ PERCENT_UPPER_I: :on_symbols_beg,
+ PERCENT_UPPER_W: :on_words_beg,
+ PIPE: :on_op,
+ PIPE_EQUAL: :on_op,
+ PIPE_PIPE: :on_op,
+ PIPE_PIPE_EQUAL: :on_op,
+ PLUS: :on_op,
+ PLUS_EQUAL: :on_op,
+ QUESTION_MARK: :on_op,
+ RATIONAL_FLOAT: :on_rational,
+ RATIONAL_INTEGER: :on_rational,
+ REGEXP_BEGIN: :on_regexp_beg,
+ REGEXP_END: :on_regexp_end,
+ SEMICOLON: :on_semicolon,
+ SLASH: :on_op,
+ SLASH_EQUAL: :on_op,
+ STAR: :on_op,
+ STAR_EQUAL: :on_op,
+ STAR_STAR: :on_op,
+ STAR_STAR_EQUAL: :on_op,
+ STRING_BEGIN: :on_tstring_beg,
+ STRING_CONTENT: :on_tstring_content,
+ STRING_END: :on_tstring_end,
+ SYMBOL_BEGIN: :on_symbeg,
+ TILDE: :on_op,
+ UAMPERSAND: :on_op,
+ UCOLON_COLON: :on_op,
+ UDOT_DOT: :on_op,
+ UDOT_DOT_DOT: :on_op,
+ UMINUS: :on_op,
+ UMINUS_NUM: :on_op,
+ UPLUS: :on_op,
+ USTAR: :on_op,
+ USTAR_STAR: :on_op,
+ WORDS_SEP: :on_words_sep,
+ "__END__": :on___end__
+ }.freeze
+
+ # When we produce tokens, we produce the same arrays that Ripper does.
+ # However, we add a couple of convenience methods onto them to make them a
+ # little easier to work with. We delegate all other methods to the array.
+ class Token < SimpleDelegator
+ # @dynamic initialize, each, []
+
+ # The location of the token in the source.
+ def location
+ self[0]
+ end
+
+ # The type of the token.
+ def event
+ self[1]
+ end
+
+ # The slice of the source that this token represents.
+ def value
+ self[2]
+ end
+
+ # The state of the lexer when this token was produced.
+ def state
+ self[3]
+ end
+ end
+
+ # Ripper doesn't include the rest of the token in the event, so we need to
+ # trim it down to just the content on the first line when comparing.
+ class EndContentToken < Token
+ def ==(other) # :nodoc:
+ [self[0], self[1], self[2][0..self[2].index("\n")], self[3]] == other
+ end
+ end
+
+ # Tokens where state should be ignored
+ # used for :on_comment, :on_heredoc_end, :on_embexpr_end
+ class IgnoreStateToken < Token
+ def ==(other) # :nodoc:
+ self[0...-1] == other[0...-1]
+ end
+ end
+
+ # Ident tokens for the most part are exactly the same, except sometimes we
+ # know an ident is a local when ripper doesn't (when they are introduced
+ # through named captures in regular expressions). In that case we don't
+ # compare the state.
+ class IdentToken < Token
+ def ==(other) # :nodoc:
+ (self[0...-1] == other[0...-1]) && (
+ (other[3] == Ripper::EXPR_LABEL | Ripper::EXPR_END) ||
+ (other[3] & Ripper::EXPR_ARG_ANY != 0)
+ )
+ end
+ end
+
+ # Ignored newlines can occasionally have a LABEL state attached to them, so
+ # we compare the state differently here.
+ class IgnoredNewlineToken < Token
+ def ==(other) # :nodoc:
+ return false unless self[0...-1] == other[0...-1]
+
+ if self[3] == Ripper::EXPR_ARG | Ripper::EXPR_LABELED
+ other[3] & Ripper::EXPR_ARG | Ripper::EXPR_LABELED != 0
+ else
+ self[3] == other[3]
+ end
+ end
+ end
+
+ # If we have an identifier that follows a method name like:
+ #
+ # def foo bar
+ #
+ # then Ripper will mark bar as END|LABEL if there is a local in a parent
+ # scope named bar because it hasn't pushed the local table yet. We do this
+ # more accurately, so we need to allow comparing against both END and
+ # END|LABEL.
+ class ParamToken < Token
+ def ==(other) # :nodoc:
+ (self[0...-1] == other[0...-1]) && (
+ (other[3] == Ripper::EXPR_END) ||
+ (other[3] == Ripper::EXPR_END | Ripper::EXPR_LABEL)
+ )
+ end
+ end
+
+ # A heredoc in this case is a list of tokens that belong to the body of the
+ # heredoc that should be appended onto the list of tokens when the heredoc
+ # closes.
+ module Heredoc # :nodoc:
+ # Heredocs that are no dash or tilde heredocs are just a list of tokens.
+ # We need to keep them around so that we can insert them in the correct
+ # order back into the token stream and set the state of the last token to
+ # the state that the heredoc was opened in.
+ class PlainHeredoc # :nodoc:
+ attr_reader :tokens
+
+ def initialize
+ @tokens = []
+ end
+
+ def <<(token)
+ tokens << token
+ end
+
+ def to_a
+ tokens
+ end
+ end
+
+ # Dash heredocs are a little more complicated. They are a list of tokens
+ # that need to be split on "\\\n" to mimic Ripper's behavior. We also need
+ # to keep track of the state that the heredoc was opened in.
+ class DashHeredoc # :nodoc:
+ attr_reader :split, :tokens
+
+ def initialize(split)
+ @split = split
+ @tokens = []
+ end
+
+ def <<(token)
+ tokens << token
+ end
+
+ def to_a
+ embexpr_balance = 0
+
+ tokens.each_with_object([]) do |token, results| #$ Array[Token]
+ case token.event
+ when :on_embexpr_beg
+ embexpr_balance += 1
+ results << token
+ when :on_embexpr_end
+ embexpr_balance -= 1
+ results << token
+ when :on_tstring_content
+ if embexpr_balance == 0
+ lineno = token[0][0]
+ column = token[0][1]
+
+ if split
+ # Split on "\\\n" to mimic Ripper's behavior. Use a lookbehind
+ # to keep the delimiter in the result.
+ token.value.split(/(?<=[^\\]\\\n)|(?<=[^\\]\\\r\n)/).each_with_index do |value, index|
+ column = 0 if index > 0
+ results << Token.new([[lineno, column], :on_tstring_content, value, token.state])
+ lineno += value.count("\n")
+ end
+ else
+ results << token
+ end
+ else
+ results << token
+ end
+ else
+ results << token
+ end
+ end
+ end
+ end
+
+ # Heredocs that are dedenting heredocs are a little more complicated.
+ # Ripper outputs on_ignored_sp tokens for the whitespace that is being
+ # removed from the output. prism only modifies the node itself and keeps
+ # the token the same. This simplifies prism, but makes comparing against
+ # Ripper much harder because there is a length mismatch.
+ #
+ # Fortunately, we already have to pull out the heredoc tokens in order to
+ # insert them into the stream in the correct order. As such, we can do
+ # some extra manipulation on the tokens to make them match Ripper's
+ # output by mirroring the dedent logic that Ripper uses.
+ class DedentingHeredoc # :nodoc:
+ TAB_WIDTH = 8
+
+ attr_reader :tokens, :dedent_next, :dedent, :embexpr_balance
+
+ def initialize
+ @tokens = []
+ @dedent_next = true
+ @dedent = nil
+ @embexpr_balance = 0
+ @ended_on_newline = false
+ end
+
+ # As tokens are coming in, we track the minimum amount of common leading
+ # whitespace on plain string content tokens. This allows us to later
+ # remove that amount of whitespace from the beginning of each line.
+ def <<(token)
+ case token.event
+ when :on_embexpr_beg, :on_heredoc_beg
+ @embexpr_balance += 1
+ @dedent = 0 if @dedent_next && @ended_on_newline
+ when :on_embexpr_end, :on_heredoc_end
+ @embexpr_balance -= 1
+ when :on_tstring_content
+ if embexpr_balance == 0
+ line = token.value
+
+ if dedent_next && !(line.strip.empty? && line.end_with?("\n"))
+ leading = line[/\A(\s*)\n?/, 1]
+ next_dedent = 0
+
+ leading.each_char do |char|
+ if char == "\t"
+ next_dedent = next_dedent - (next_dedent % TAB_WIDTH) + TAB_WIDTH
+ else
+ next_dedent += 1
+ end
+ end
+
+ @dedent = [dedent, next_dedent].compact.min
+ @dedent_next = true
+ @ended_on_newline = line.end_with?("\n")
+ tokens << token
+ return
+ end
+ end
+ end
+
+ @dedent_next = token.event == :on_tstring_content && embexpr_balance == 0
+ @ended_on_newline = false
+ tokens << token
+ end
+
+ def to_a
+ # If every line in the heredoc is blank, we still need to split up the
+ # string content token into multiple tokens.
+ if dedent.nil?
+ results = [] #: Array[Token]
+ embexpr_balance = 0
+
+ tokens.each do |token|
+ case token.event
+ when :on_embexpr_beg, :on_heredoc_beg
+ embexpr_balance += 1
+ results << token
+ when :on_embexpr_end, :on_heredoc_end
+ embexpr_balance -= 1
+ results << token
+ when :on_tstring_content
+ if embexpr_balance == 0
+ lineno = token[0][0]
+ column = token[0][1]
+
+ token.value.split(/(?<=\n)/).each_with_index do |value, index|
+ column = 0 if index > 0
+ results << Token.new([[lineno, column], :on_tstring_content, value, token.state])
+ lineno += 1
+ end
+ else
+ results << token
+ end
+ else
+ results << token
+ end
+ end
+
+ return results
+ end
+
+ # If the minimum common whitespace is 0, then we need to concatenate
+ # string nodes together that are immediately adjacent.
+ if dedent == 0
+ results = [] #: Array[Token]
+ embexpr_balance = 0
+
+ index = 0
+ max_index = tokens.length
+
+ while index < max_index
+ token = tokens[index]
+ results << token
+ index += 1
+
+ case token.event
+ when :on_embexpr_beg, :on_heredoc_beg
+ embexpr_balance += 1
+ when :on_embexpr_end, :on_heredoc_end
+ embexpr_balance -= 1
+ when :on_tstring_content
+ if embexpr_balance == 0
+ while index < max_index && tokens[index].event == :on_tstring_content
+ token.value << tokens[index].value
+ index += 1
+ end
+ end
+ end
+ end
+
+ return results
+ end
+
+ # Otherwise, we're going to run through each token in the list and
+ # insert on_ignored_sp tokens for the amount of dedent that we need to
+ # perform. We also need to remove the dedent from the beginning of
+ # each line of plain string content tokens.
+ results = [] #: Array[Token]
+ dedent_next = true
+ embexpr_balance = 0
+
+ tokens.each do |token|
+ # Notice that the structure of this conditional largely matches the
+ # whitespace calculation we performed above. This is because
+ # checking if the subsequent token needs to be dedented is common to
+ # both the dedent calculation and the ignored_sp insertion.
+ case token.event
+ when :on_embexpr_beg
+ embexpr_balance += 1
+ results << token
+ when :on_embexpr_end
+ embexpr_balance -= 1
+ results << token
+ when :on_tstring_content
+ if embexpr_balance == 0
+ # Here we're going to split the string on newlines, but maintain
+ # the newlines in the resulting array. We'll do that with a look
+ # behind assertion.
+ splits = token.value.split(/(?<=\n)/)
+ index = 0
+
+ while index < splits.length
+ line = splits[index]
+ lineno = token[0][0] + index
+ column = token[0][1]
+
+ # Blank lines do not count toward common leading whitespace
+ # calculation and do not need to be dedented.
+ if dedent_next || index > 0
+ column = 0
+ end
+
+ # If the dedent is 0 and we're not supposed to dedent the next
+ # line or this line doesn't start with whitespace, then we
+ # should concatenate the rest of the string to match ripper.
+ if dedent == 0 && (!dedent_next || !line.start_with?(/\s/))
+ line = splits[index..].join
+ index = splits.length
+ end
+
+ # If we are supposed to dedent this line or if this is not the
+ # first line of the string and this line isn't entirely blank,
+ # then we need to insert an on_ignored_sp token and remove the
+ # dedent from the beginning of the line.
+ if (dedent > 0) && (dedent_next || index > 0)
+ deleting = 0
+ deleted_chars = [] #: Array[String]
+
+ # Gather up all of the characters that we're going to
+ # delete, stopping when you hit a character that would put
+ # you over the dedent amount.
+ line.each_char.with_index do |char, i|
+ case char
+ when "\r"
+ if line[i + 1] == "\n"
+ break
+ end
+ when "\n"
+ break
+ when "\t"
+ deleting = deleting - (deleting % TAB_WIDTH) + TAB_WIDTH
+ else
+ deleting += 1
+ end
+
+ break if deleting > dedent
+ deleted_chars << char
+ end
+
+ # If we have something to delete, then delete it from the
+ # string and insert an on_ignored_sp token.
+ if deleted_chars.any?
+ ignored = deleted_chars.join
+ line.delete_prefix!(ignored)
+
+ results << Token.new([[lineno, 0], :on_ignored_sp, ignored, token[3]])
+ column = ignored.length
+ end
+ end
+
+ results << Token.new([[lineno, column], token[1], line, token[3]]) unless line.empty?
+ index += 1
+ end
+ else
+ results << token
+ end
+ else
+ results << token
+ end
+
+ dedent_next =
+ ((token.event == :on_tstring_content) || (token.event == :on_heredoc_end)) &&
+ embexpr_balance == 0
+ end
+
+ results
+ end
+ end
+
+ # Here we will split between the two types of heredocs and return the
+ # object that will store their tokens.
+ def self.build(opening)
+ case opening.value[2]
+ when "~"
+ DedentingHeredoc.new
+ when "-"
+ DashHeredoc.new(opening.value[3] != "'")
+ else
+ PlainHeredoc.new
+ end
+ end
+ end
+
+ private_constant :Heredoc
+
+ attr_reader :source, :options
+
+ def initialize(source, **options)
+ @source = source
+ @options = options
+ end
+
+ def result
+ tokens = [] #: Array[LexCompat::Token]
+
+ state = :default
+ heredoc_stack = [[]] #: Array[Array[Heredoc::PlainHeredoc | Heredoc::DashHeredoc | Heredoc::DedentingHeredoc]]
+
+ result = Prism.lex(source, **options)
+ result_value = result.value
+ previous_state = nil #: Ripper::Lexer::State?
+ last_heredoc_end = nil #: Integer?
+
+ # In previous versions of Ruby, Ripper wouldn't flush the bom before the
+ # first token, so we had to have a hack in place to account for that. This
+ # checks for that behavior.
+ bom_flushed = Ripper.lex("\xEF\xBB\xBF# test")[0][0][1] == 0
+ bom = source.byteslice(0..2) == "\xEF\xBB\xBF"
+
+ result_value.each_with_index do |(token, lex_state), index|
+ lineno = token.location.start_line
+ column = token.location.start_column
+
+ # If there's a UTF-8 byte-order mark as the start of the file, then for
+ # certain tokens ripper sets the first token back by 3 bytes. It also
+ # keeps the byte order mark in the first token's value. This is weird,
+ # and I don't want to mirror that in our parser. So instead, we'll match
+ # up the columns and values here.
+ if bom && lineno == 1
+ column -= 3
+
+ if index == 0 && column == 0 && !bom_flushed
+ flushed =
+ case token.type
+ when :BACK_REFERENCE, :INSTANCE_VARIABLE, :CLASS_VARIABLE,
+ :GLOBAL_VARIABLE, :NUMBERED_REFERENCE, :PERCENT_LOWER_I,
+ :PERCENT_LOWER_X, :PERCENT_LOWER_W, :PERCENT_UPPER_I,
+ :PERCENT_UPPER_W, :STRING_BEGIN
+ true
+ when :REGEXP_BEGIN, :SYMBOL_BEGIN
+ token.value.start_with?("%")
+ else
+ false
+ end
+
+ unless flushed
+ column -= 3
+ value = token.value
+ value.prepend(String.new("\xEF\xBB\xBF", encoding: value.encoding))
+ end
+ end
+ end
+
+ event = RIPPER.fetch(token.type)
+ value = token.value
+ lex_state = Ripper::Lexer::State.new(lex_state)
+
+ token =
+ case event
+ when :on___end__
+ EndContentToken.new([[lineno, column], event, value, lex_state])
+ when :on_comment
+ IgnoreStateToken.new([[lineno, column], event, value, lex_state])
+ when :on_heredoc_end
+ # Heredoc end tokens can be emitted in an odd order, so we don't
+ # want to bother comparing the state on them.
+ last_heredoc_end = token.location.end_offset
+ IgnoreStateToken.new([[lineno, column], event, value, lex_state])
+ when :on_ident
+ if lex_state == Ripper::EXPR_END
+ # If we have an identifier that follows a method name like:
+ #
+ # def foo bar
+ #
+ # then Ripper will mark bar as END|LABEL if there is a local in a
+ # parent scope named bar because it hasn't pushed the local table
+ # yet. We do this more accurately, so we need to allow comparing
+ # against both END and END|LABEL.
+ ParamToken.new([[lineno, column], event, value, lex_state])
+ elsif lex_state == Ripper::EXPR_END | Ripper::EXPR_LABEL
+ # In the event that we're comparing identifiers, we're going to
+ # allow a little divergence. Ripper doesn't account for local
+ # variables introduced through named captures in regexes, and we
+ # do, which accounts for this difference.
+ IdentToken.new([[lineno, column], event, value, lex_state])
+ else
+ Token.new([[lineno, column], event, value, lex_state])
+ end
+ when :on_embexpr_end
+ IgnoreStateToken.new([[lineno, column], event, value, lex_state])
+ when :on_ignored_nl
+ # Ignored newlines can occasionally have a LABEL state attached to
+ # them which doesn't actually impact anything. We don't mirror that
+ # state so we ignored it.
+ IgnoredNewlineToken.new([[lineno, column], event, value, lex_state])
+ when :on_regexp_end
+ # On regex end, Ripper scans and then sets end state, so the ripper
+ # lexed output is begin, when it should be end. prism sets lex state
+ # correctly to end state, but we want to be able to compare against
+ # Ripper's lexed state. So here, if it's a regexp end token, we
+ # output the state as the previous state, solely for the sake of
+ # comparison.
+ previous_token = result_value[index - 1][0]
+ lex_state =
+ if RIPPER.fetch(previous_token.type) == :on_embexpr_end
+ # If the previous token is embexpr_end, then we have to do even
+ # more processing. The end of an embedded expression sets the
+ # state to the state that it had at the beginning of the
+ # embedded expression. So we have to go and find that state and
+ # set it here.
+ counter = 1
+ current_index = index - 1
+
+ until counter == 0
+ current_index -= 1
+ current_event = RIPPER.fetch(result_value[current_index][0].type)
+ counter += { on_embexpr_beg: -1, on_embexpr_end: 1 }[current_event] || 0
+ end
+
+ Ripper::Lexer::State.new(result_value[current_index][1])
+ else
+ previous_state
+ end
+
+ Token.new([[lineno, column], event, value, lex_state])
+ when :on_eof
+ previous_token = result_value[index - 1][0]
+
+ # If we're at the end of the file and the previous token was a
+ # comment and there is still whitespace after the comment, then
+ # Ripper will append a on_nl token (even though there isn't
+ # necessarily a newline). We mirror that here.
+ if previous_token.type == :COMMENT
+ # If the comment is at the start of a heredoc: <<HEREDOC # comment
+ # then the comment's end_offset is up near the heredoc_beg.
+ # This is not the correct offset to use for figuring out if
+ # there is trailing whitespace after the last token.
+ # Use the greater offset of the two to determine the start of
+ # the trailing whitespace.
+ start_offset = [previous_token.location.end_offset, last_heredoc_end].compact.max
+ end_offset = token.location.start_offset
+
+ if start_offset < end_offset
+ if bom
+ start_offset += 3
+ end_offset += 3
+ end
+
+ tokens << Token.new([[lineno, 0], :on_nl, source.byteslice(start_offset...end_offset), lex_state])
+ end
+ end
+
+ Token.new([[lineno, column], event, value, lex_state])
+ else
+ Token.new([[lineno, column], event, value, lex_state])
+ end
+
+ previous_state = lex_state
+
+ # The order in which tokens appear in our lexer is different from the
+ # order that they appear in Ripper. When we hit the declaration of a
+ # heredoc in prism, we skip forward and lex the rest of the content of
+ # the heredoc before going back and lexing at the end of the heredoc
+ # identifier.
+ #
+ # To match up to ripper, we keep a small state variable around here to
+ # track whether we're in the middle of a heredoc or not. In this way we
+ # can shuffle around the token to match Ripper's output.
+ case state
+ when :default
+ # The default state is when there are no heredocs at all. In this
+ # state we can append the token to the list of tokens and move on.
+ tokens << token
+
+ # If we get the declaration of a heredoc, then we open a new heredoc
+ # and move into the heredoc_opened state.
+ if event == :on_heredoc_beg
+ state = :heredoc_opened
+ heredoc_stack.last << Heredoc.build(token)
+ end
+ when :heredoc_opened
+ # The heredoc_opened state is when we've seen the declaration of a
+ # heredoc and are now lexing the body of the heredoc. In this state we
+ # push tokens onto the most recently created heredoc.
+ heredoc_stack.last.last << token
+
+ case event
+ when :on_heredoc_beg
+ # If we receive a heredoc declaration while lexing the body of a
+ # heredoc, this means we have nested heredocs. In this case we'll
+ # push a new heredoc onto the stack and stay in the heredoc_opened
+ # state since we're now lexing the body of the new heredoc.
+ heredoc_stack << [Heredoc.build(token)]
+ when :on_heredoc_end
+ # If we receive the end of a heredoc, then we're done lexing the
+ # body of the heredoc. In this case we now have a completed heredoc
+ # but need to wait for the next newline to push it into the token
+ # stream.
+ state = :heredoc_closed
+ end
+ when :heredoc_closed
+ if %i[on_nl on_ignored_nl on_comment].include?(event) || (event == :on_tstring_content && value.end_with?("\n"))
+ if heredoc_stack.size > 1
+ flushing = heredoc_stack.pop
+ heredoc_stack.last.last << token
+
+ flushing.each do |heredoc|
+ heredoc.to_a.each do |flushed_token|
+ heredoc_stack.last.last << flushed_token
+ end
+ end
+
+ state = :heredoc_opened
+ next
+ end
+ elsif event == :on_heredoc_beg
+ tokens << token
+ state = :heredoc_opened
+ heredoc_stack.last << Heredoc.build(token)
+ next
+ elsif heredoc_stack.size > 1
+ heredoc_stack[-2].last << token
+ next
+ end
+
+ heredoc_stack.last.each do |heredoc|
+ tokens.concat(heredoc.to_a)
+ end
+
+ heredoc_stack.last.clear
+ state = :default
+
+ tokens << token
+ end
+ end
+
+ # Drop the EOF token from the list
+ tokens = tokens[0...-1]
+
+ # We sort by location to compare against Ripper's output
+ tokens.sort_by!(&:location)
+
+ Result.new(tokens, result.comments, result.magic_comments, result.data_loc, result.errors, result.warnings, Source.new(source))
+ end
+ end
+
+ private_constant :LexCompat
+
+ # This is a class that wraps the Ripper lexer to produce almost exactly the
+ # same tokens.
+ class LexRipper # :nodoc:
+ attr_reader :source
+
+ def initialize(source)
+ @source = source
+ end
+
+ def result
+ previous = [] #: [[Integer, Integer], Symbol, String, untyped] | []
+ results = [] #: Array[[[Integer, Integer], Symbol, String, untyped]]
+
+ lex(source).each do |token|
+ case token[1]
+ when :on_sp
+ # skip
+ when :on_tstring_content
+ if previous[1] == :on_tstring_content && (token[2].start_with?("\#$") || token[2].start_with?("\#@"))
+ previous[2] << token[2]
+ else
+ results << token
+ previous = token
+ end
+ when :on_words_sep
+ if previous[1] == :on_words_sep
+ previous[2] << token[2]
+ else
+ results << token
+ previous = token
+ end
+ else
+ results << token
+ previous = token
+ end
+ end
+
+ results
+ end
+
+ private
+
+ if Ripper.method(:lex).parameters.assoc(:keyrest)
+ def lex(source)
+ Ripper.lex(source, raise_errors: true)
+ end
+ else
+ def lex(source)
+ ripper = Ripper::Lexer.new(source)
+ ripper.lex.tap do |result|
+ raise SyntaxError, ripper.errors.map(&:message).join(' ;') if ripper.errors.any?
+ end
+ end
+ end
+ end
+
+ private_constant :LexRipper
+end
diff --git a/lib/prism/node_ext.rb b/lib/prism/node_ext.rb
new file mode 100644
index 0000000000..8674544065
--- /dev/null
+++ b/lib/prism/node_ext.rb
@@ -0,0 +1,260 @@
+# frozen_string_literal: true
+
+# Here we are reopening the prism module to provide methods on nodes that aren't
+# templated and are meant as convenience methods.
+module Prism
+ module RegularExpressionOptions # :nodoc:
+ # Returns a numeric value that represents the flags that were used to create
+ # the regular expression.
+ def options
+ o = flags & (RegularExpressionFlags::IGNORE_CASE | RegularExpressionFlags::EXTENDED | RegularExpressionFlags::MULTI_LINE)
+ o |= Regexp::FIXEDENCODING if flags.anybits?(RegularExpressionFlags::EUC_JP | RegularExpressionFlags::WINDOWS_31J | RegularExpressionFlags::UTF_8)
+ o |= Regexp::NOENCODING if flags.anybits?(RegularExpressionFlags::ASCII_8BIT)
+ o
+ end
+ end
+
+ class InterpolatedMatchLastLineNode < Node
+ include RegularExpressionOptions
+ end
+
+ class InterpolatedRegularExpressionNode < Node
+ include RegularExpressionOptions
+ end
+
+ class MatchLastLineNode < Node
+ include RegularExpressionOptions
+ end
+
+ class RegularExpressionNode < Node
+ include RegularExpressionOptions
+ end
+
+ private_constant :RegularExpressionOptions
+
+ module HeredocQuery # :nodoc:
+ # Returns true if this node was represented as a heredoc in the source code.
+ def heredoc?
+ opening&.start_with?("<<")
+ end
+ end
+
+ class InterpolatedStringNode < Node
+ include HeredocQuery
+ end
+
+ class InterpolatedXStringNode < Node
+ include HeredocQuery
+ end
+
+ class StringNode < Node
+ include HeredocQuery
+
+ # Occasionally it's helpful to treat a string as if it were interpolated so
+ # that there's a consistent interface for working with strings.
+ def to_interpolated
+ InterpolatedStringNode.new(
+ source,
+ frozen? ? InterpolatedStringNodeFlags::FROZEN : 0,
+ opening_loc,
+ [copy(opening_loc: nil, closing_loc: nil, location: content_loc)],
+ closing_loc,
+ location
+ )
+ end
+ end
+
+ class XStringNode < Node
+ include HeredocQuery
+
+ # Occasionally it's helpful to treat a string as if it were interpolated so
+ # that there's a consistent interface for working with strings.
+ def to_interpolated
+ InterpolatedXStringNode.new(
+ source,
+ opening_loc,
+ [StringNode.new(source, 0, nil, content_loc, nil, unescaped, content_loc)],
+ closing_loc,
+ location
+ )
+ end
+ end
+
+ private_constant :HeredocQuery
+
+ class ImaginaryNode < Node
+ # Returns the value of the node as a Ruby Complex.
+ def value
+ Complex(0, numeric.value)
+ end
+ end
+
+ class RationalNode < Node
+ # Returns the value of the node as a Ruby Rational.
+ def value
+ Rational(numeric.is_a?(IntegerNode) ? numeric.value : slice.chomp("r"))
+ end
+ end
+
+ class ConstantReadNode < Node
+ # Returns the list of parts for the full name of this constant.
+ # For example: [:Foo]
+ def full_name_parts
+ [name]
+ end
+
+ # Returns the full name of this constant. For example: "Foo"
+ def full_name
+ name.to_s
+ end
+ end
+
+ class ConstantWriteNode < Node
+ # Returns the list of parts for the full name of this constant.
+ # For example: [:Foo]
+ def full_name_parts
+ [name]
+ end
+
+ # Returns the full name of this constant. For example: "Foo"
+ def full_name
+ name.to_s
+ end
+ end
+
+ class ConstantPathNode < Node
+ # An error class raised when dynamic parts are found while computing a
+ # constant path's full name. For example:
+ # Foo::Bar::Baz -> does not raise because all parts of the constant path are
+ # simple constants
+ # var::Bar::Baz -> raises because the first part of the constant path is a
+ # local variable
+ class DynamicPartsInConstantPathError < StandardError; end
+
+ # An error class raised when missing nodes are found while computing a
+ # constant path's full name. For example:
+ # Foo:: -> raises because the constant path is missing the last part
+ class MissingNodesInConstantPathError < StandardError; end
+
+ # Returns the list of parts for the full name of this constant path.
+ # For example: [:Foo, :Bar]
+ def full_name_parts
+ parts = [] #: Array[Symbol]
+ current = self #: node?
+
+ while current.is_a?(ConstantPathNode)
+ child = current.child
+ if child.is_a?(MissingNode)
+ raise MissingNodesInConstantPathError, "Constant path contains missing nodes. Cannot compute full name"
+ end
+ parts.unshift(child.name)
+ current = current.parent
+ end
+
+ if !current.is_a?(ConstantReadNode) && !current.nil?
+ raise DynamicPartsInConstantPathError, "Constant path contains dynamic parts. Cannot compute full name"
+ end
+
+ parts.unshift(current&.name || :"")
+ end
+
+ # Returns the full name of this constant path. For example: "Foo::Bar"
+ def full_name
+ full_name_parts.join("::")
+ end
+ end
+
+ class ConstantPathTargetNode < Node
+ # Returns the list of parts for the full name of this constant path.
+ # For example: [:Foo, :Bar]
+ def full_name_parts
+ parts =
+ case parent
+ when ConstantPathNode, ConstantReadNode
+ parent.full_name_parts
+ when nil
+ [:""]
+ else
+ # e.g. self::Foo, (var)::Bar = baz
+ raise ConstantPathNode::DynamicPartsInConstantPathError, "Constant target path contains dynamic parts. Cannot compute full name"
+ end
+
+ if child.is_a?(MissingNode)
+ raise ConstantPathNode::MissingNodesInConstantPathError, "Constant target path contains missing nodes. Cannot compute full name"
+ end
+
+ parts.push(child.name)
+ end
+
+ # Returns the full name of this constant path. For example: "Foo::Bar"
+ def full_name
+ full_name_parts.join("::")
+ end
+ end
+
+ class ConstantTargetNode < Node
+ # Returns the list of parts for the full name of this constant.
+ # For example: [:Foo]
+ def full_name_parts
+ [name]
+ end
+
+ # Returns the full name of this constant. For example: "Foo"
+ def full_name
+ name.to_s
+ end
+ end
+
+ class ParametersNode < Node
+ # Mirrors the Method#parameters method.
+ def signature
+ names = [] #: Array[[Symbol, Symbol] | [Symbol]]
+
+ requireds.each do |param|
+ names << (param.is_a?(MultiTargetNode) ? [:req] : [:req, param.name])
+ end
+
+ optionals.each { |param| names << [:opt, param.name] }
+
+ if rest && rest.is_a?(RestParameterNode)
+ names << [:rest, rest.name || :*]
+ end
+
+ posts.each do |param|
+ if param.is_a?(MultiTargetNode)
+ names << [:req]
+ elsif param.is_a?(NoKeywordsParameterNode)
+ # Invalid syntax, e.g. "def f(**nil, ...)" moves the NoKeywordsParameterNode to posts
+ raise "Invalid syntax"
+ else
+ names << [:req, param.name]
+ end
+ end
+
+ # Regardless of the order in which the keywords were defined, the required
+ # keywords always come first followed by the optional keywords.
+ keyopt = [] #: Array[OptionalKeywordParameterNode]
+ keywords.each do |param|
+ if param.is_a?(OptionalKeywordParameterNode)
+ keyopt << param
+ else
+ names << [:keyreq, param.name]
+ end
+ end
+
+ keyopt.each { |param| names << [:key, param.name] }
+
+ case keyword_rest
+ when ForwardingParameterNode
+ names.concat([[:rest, :*], [:keyrest, :**], [:block, :&]])
+ when KeywordRestParameterNode
+ names << [:keyrest, keyword_rest.name || :**]
+ when NoKeywordsParameterNode
+ names << [:nokey]
+ end
+
+ names << [:block, block.name || :&] if block
+ names
+ end
+ end
+end
diff --git a/lib/prism/pack.rb b/lib/prism/pack.rb
new file mode 100644
index 0000000000..c0de8ab8b7
--- /dev/null
+++ b/lib/prism/pack.rb
@@ -0,0 +1,228 @@
+# frozen_string_literal: true
+# typed: ignore
+
+module Prism
+ # A parser for the pack template language.
+ module Pack
+ %i[
+ SPACE
+ COMMENT
+ INTEGER
+ UTF8
+ BER
+ FLOAT
+ STRING_SPACE_PADDED
+ STRING_NULL_PADDED
+ STRING_NULL_TERMINATED
+ STRING_MSB
+ STRING_LSB
+ STRING_HEX_HIGH
+ STRING_HEX_LOW
+ STRING_UU
+ STRING_MIME
+ STRING_BASE64
+ STRING_FIXED
+ STRING_POINTER
+ MOVE
+ BACK
+ NULL
+
+ UNSIGNED
+ SIGNED
+ SIGNED_NA
+
+ AGNOSTIC_ENDIAN
+ LITTLE_ENDIAN
+ BIG_ENDIAN
+ NATIVE_ENDIAN
+ ENDIAN_NA
+
+ SIZE_SHORT
+ SIZE_INT
+ SIZE_LONG
+ SIZE_LONG_LONG
+ SIZE_8
+ SIZE_16
+ SIZE_32
+ SIZE_64
+ SIZE_P
+ SIZE_NA
+
+ LENGTH_FIXED
+ LENGTH_MAX
+ LENGTH_RELATIVE
+ LENGTH_NA
+ ].each do |const|
+ const_set(const, const)
+ end
+
+ # A directive in the pack template language.
+ class Directive
+ # A symbol representing the version of Ruby.
+ attr_reader :version
+
+ # A symbol representing whether or not we are packing or unpacking.
+ attr_reader :variant
+
+ # A byteslice of the source string that this directive represents.
+ attr_reader :source
+
+ # The type of the directive.
+ attr_reader :type
+
+ # The type of signedness of the directive.
+ attr_reader :signed
+
+ # The type of endianness of the directive.
+ attr_reader :endian
+
+ # The size of the directive.
+ attr_reader :size
+
+ # The length type of this directive (used for integers).
+ attr_reader :length_type
+
+ # The length of this directive (used for integers).
+ attr_reader :length
+
+ # Initialize a new directive with the given values.
+ def initialize(version, variant, source, type, signed, endian, size, length_type, length)
+ @version = version
+ @variant = variant
+ @source = source
+ @type = type
+ @signed = signed
+ @endian = endian
+ @size = size
+ @length_type = length_type
+ @length = length
+ end
+
+ # The descriptions of the various types of endianness.
+ ENDIAN_DESCRIPTIONS = {
+ AGNOSTIC_ENDIAN: "agnostic",
+ LITTLE_ENDIAN: "little-endian (VAX)",
+ BIG_ENDIAN: "big-endian (network)",
+ NATIVE_ENDIAN: "native-endian",
+ ENDIAN_NA: "n/a"
+ }
+
+ # The descriptions of the various types of signedness.
+ SIGNED_DESCRIPTIONS = {
+ UNSIGNED: "unsigned",
+ SIGNED: "signed",
+ SIGNED_NA: "n/a"
+ }
+
+ # The descriptions of the various types of sizes.
+ SIZE_DESCRIPTIONS = {
+ SIZE_SHORT: "short",
+ SIZE_INT: "int-width",
+ SIZE_LONG: "long",
+ SIZE_LONG_LONG: "long long",
+ SIZE_8: "8-bit",
+ SIZE_16: "16-bit",
+ SIZE_32: "32-bit",
+ SIZE_64: "64-bit",
+ SIZE_P: "pointer-width"
+ }
+
+ # Provide a human-readable description of the directive.
+ def describe
+ case type
+ when SPACE
+ "whitespace"
+ when COMMENT
+ "comment"
+ when INTEGER
+ if size == SIZE_8
+ base = "#{SIGNED_DESCRIPTIONS[signed]} #{SIZE_DESCRIPTIONS[size]} integer"
+ else
+ base = "#{SIGNED_DESCRIPTIONS[signed]} #{SIZE_DESCRIPTIONS[size]} #{ENDIAN_DESCRIPTIONS[endian]} integer"
+ end
+ case length_type
+ when LENGTH_FIXED
+ if length > 1
+ base + ", x#{length}"
+ else
+ base
+ end
+ when LENGTH_MAX
+ base + ", as many as possible"
+ else
+ raise
+ end
+ when UTF8
+ "UTF-8 character"
+ when BER
+ "BER-compressed integer"
+ when FLOAT
+ "#{SIZE_DESCRIPTIONS[size]} #{ENDIAN_DESCRIPTIONS[endian]} float"
+ when STRING_SPACE_PADDED
+ "arbitrary binary string (space padded)"
+ when STRING_NULL_PADDED
+ "arbitrary binary string (null padded, count is width)"
+ when STRING_NULL_TERMINATED
+ "arbitrary binary string (null padded, count is width), except that null is added with *"
+ when STRING_MSB
+ "bit string (MSB first)"
+ when STRING_LSB
+ "bit string (LSB first)"
+ when STRING_HEX_HIGH
+ "hex string (high nibble first)"
+ when STRING_HEX_LOW
+ "hex string (low nibble first)"
+ when STRING_UU
+ "UU-encoded string"
+ when STRING_MIME
+ "quoted printable, MIME encoding"
+ when STRING_BASE64
+ "base64 encoded string"
+ when STRING_FIXED
+ "pointer to a structure (fixed-length string)"
+ when STRING_POINTER
+ "pointer to a null-terminated string"
+ when MOVE
+ "move to absolute position"
+ when BACK
+ "back up a byte"
+ when NULL
+ "null byte"
+ else
+ raise
+ end
+ end
+ end
+
+ # The result of parsing a pack template.
+ class Format
+ # A list of the directives in the template.
+ attr_reader :directives
+
+ # The encoding of the template.
+ attr_reader :encoding
+
+ # Create a new Format with the given directives and encoding.
+ def initialize(directives, encoding)
+ @directives = directives
+ @encoding = encoding
+ end
+
+ # Provide a human-readable description of the format.
+ def describe
+ source_width = directives.map { |d| d.source.inspect.length }.max
+ directive_lines = directives.map do |directive|
+ if directive.type == SPACE
+ source = directive.source.inspect
+ else
+ source = directive.source
+ end
+ # @type var source_width: Integer
+ " #{source.ljust(source_width)} #{directive.describe}"
+ end
+
+ (["Directives:"] + directive_lines + ["Encoding:", " #{encoding}"]).join("\n")
+ end
+ end
+ end
+end
diff --git a/lib/prism/parse_result.rb b/lib/prism/parse_result.rb
new file mode 100644
index 0000000000..e01aa070c2
--- /dev/null
+++ b/lib/prism/parse_result.rb
@@ -0,0 +1,618 @@
+# frozen_string_literal: true
+
+module Prism
+ # This represents a source of Ruby code that has been parsed. It is used in
+ # conjunction with locations to allow them to resolve line numbers and source
+ # ranges.
+ class Source
+ # The source code that this source object represents.
+ attr_reader :source
+
+ # The line number where this source starts.
+ attr_reader :start_line
+
+ # The list of newline byte offsets in the source code.
+ attr_reader :offsets
+
+ # Create a new source object with the given source code.
+ def initialize(source, start_line = 1, offsets = [])
+ @source = source
+ @start_line = start_line # set after parsing is done
+ @offsets = offsets # set after parsing is done
+ end
+
+ # Returns the encoding of the source code, which is set by parameters to the
+ # parser or by the encoding magic comment.
+ def encoding
+ source.encoding
+ end
+
+ # Perform a byteslice on the source code using the given byte offset and
+ # byte length.
+ def slice(byte_offset, length)
+ source.byteslice(byte_offset, length) or raise
+ end
+
+ # Binary search through the offsets to find the line number for the given
+ # byte offset.
+ def line(byte_offset)
+ start_line + find_line(byte_offset)
+ end
+
+ # Return the byte offset of the start of the line corresponding to the given
+ # byte offset.
+ def line_start(byte_offset)
+ offsets[find_line(byte_offset)]
+ end
+
+ # Returns the byte offset of the end of the line corresponding to the given
+ # byte offset.
+ def line_end(byte_offset)
+ offsets[find_line(byte_offset) + 1] || source.bytesize
+ end
+
+ # Return the column number for the given byte offset.
+ def column(byte_offset)
+ byte_offset - line_start(byte_offset)
+ end
+
+ # Return the character offset for the given byte offset.
+ def character_offset(byte_offset)
+ (source.byteslice(0, byte_offset) or raise).length
+ end
+
+ # Return the column number in characters for the given byte offset.
+ def character_column(byte_offset)
+ character_offset(byte_offset) - character_offset(line_start(byte_offset))
+ end
+
+ # Returns the offset from the start of the file for the given byte offset
+ # counting in code units for the given encoding.
+ #
+ # This method is tested with UTF-8, UTF-16, and UTF-32. If there is the
+ # concept of code units that differs from the number of characters in other
+ # encodings, it is not captured here.
+ def code_units_offset(byte_offset, encoding)
+ byteslice = (source.byteslice(0, byte_offset) or raise).encode(encoding)
+ (encoding == Encoding::UTF_16LE || encoding == Encoding::UTF_16BE) ? (byteslice.bytesize / 2) : byteslice.length
+ end
+
+ # Returns the column number in code units for the given encoding for the
+ # given byte offset.
+ def code_units_column(byte_offset, encoding)
+ code_units_offset(byte_offset, encoding) - code_units_offset(line_start(byte_offset), encoding)
+ end
+
+ private
+
+ # Binary search through the offsets to find the line number for the given
+ # byte offset.
+ def find_line(byte_offset)
+ left = 0
+ right = offsets.length - 1
+
+ while left <= right
+ mid = left + (right - left) / 2
+ return mid if (offset = offsets[mid]) == byte_offset
+
+ if offset < byte_offset
+ left = mid + 1
+ else
+ right = mid - 1
+ end
+ end
+
+ left - 1
+ end
+ end
+
+ # This represents a location in the source.
+ class Location
+ # A Source object that is used to determine more information from the given
+ # offset and length.
+ attr_reader :source
+ protected :source
+
+ # The byte offset from the beginning of the source where this location
+ # starts.
+ attr_reader :start_offset
+
+ # The length of this location in bytes.
+ attr_reader :length
+
+ # Create a new location object with the given source, start byte offset, and
+ # byte length.
+ def initialize(source, start_offset, length)
+ @source = source
+ @start_offset = start_offset
+ @length = length
+
+ # These are used to store comments that are associated with this location.
+ # They are initialized to `nil` to save on memory when there are no
+ # comments to be attached and/or the comment-related APIs are not used.
+ @leading_comments = nil
+ @trailing_comments = nil
+ end
+
+ # These are the comments that are associated with this location that exist
+ # before the start of this location.
+ def leading_comments
+ @leading_comments ||= []
+ end
+
+ # Attach a comment to the leading comments of this location.
+ def leading_comment(comment)
+ leading_comments << comment
+ end
+
+ # These are the comments that are associated with this location that exist
+ # after the end of this location.
+ def trailing_comments
+ @trailing_comments ||= []
+ end
+
+ # Attach a comment to the trailing comments of this location.
+ def trailing_comment(comment)
+ trailing_comments << comment
+ end
+
+ # Returns all comments that are associated with this location (both leading
+ # and trailing comments).
+ def comments
+ [*@leading_comments, *@trailing_comments]
+ end
+
+ # Create a new location object with the given options.
+ def copy(source: self.source, start_offset: self.start_offset, length: self.length)
+ Location.new(source, start_offset, length)
+ end
+
+ # Returns a new location that is the result of chopping off the last byte.
+ def chop
+ copy(length: length == 0 ? length : length - 1)
+ end
+
+ # Returns a string representation of this location.
+ def inspect
+ "#<Prism::Location @start_offset=#{@start_offset} @length=#{@length} start_line=#{start_line}>"
+ end
+
+ # The source code that this location represents.
+ def slice
+ source.slice(start_offset, length)
+ end
+
+ # The source code that this location represents starting from the beginning
+ # of the line that this location starts on to the end of the line that this
+ # location ends on.
+ def slice_lines
+ line_start = source.line_start(start_offset)
+ line_end = source.line_end(end_offset)
+ source.slice(line_start, line_end - line_start)
+ end
+
+ # The character offset from the beginning of the source where this location
+ # starts.
+ def start_character_offset
+ source.character_offset(start_offset)
+ end
+
+ # The offset from the start of the file in code units of the given encoding.
+ def start_code_units_offset(encoding = Encoding::UTF_16LE)
+ source.code_units_offset(start_offset, encoding)
+ end
+
+ # The byte offset from the beginning of the source where this location ends.
+ def end_offset
+ start_offset + length
+ end
+
+ # The character offset from the beginning of the source where this location
+ # ends.
+ def end_character_offset
+ source.character_offset(end_offset)
+ end
+
+ # The offset from the start of the file in code units of the given encoding.
+ def end_code_units_offset(encoding = Encoding::UTF_16LE)
+ source.code_units_offset(end_offset, encoding)
+ end
+
+ # The line number where this location starts.
+ def start_line
+ source.line(start_offset)
+ end
+
+ # The content of the line where this location starts before this location.
+ def start_line_slice
+ offset = source.line_start(start_offset)
+ source.slice(offset, start_offset - offset)
+ end
+
+ # The line number where this location ends.
+ def end_line
+ source.line(end_offset)
+ end
+
+ # The column number in bytes where this location starts from the start of
+ # the line.
+ def start_column
+ source.column(start_offset)
+ end
+
+ # The column number in characters where this location ends from the start of
+ # the line.
+ def start_character_column
+ source.character_column(start_offset)
+ end
+
+ # The column number in code units of the given encoding where this location
+ # starts from the start of the line.
+ def start_code_units_column(encoding = Encoding::UTF_16LE)
+ source.code_units_column(start_offset, encoding)
+ end
+
+ # The column number in bytes where this location ends from the start of the
+ # line.
+ def end_column
+ source.column(end_offset)
+ end
+
+ # The column number in characters where this location ends from the start of
+ # the line.
+ def end_character_column
+ source.character_column(end_offset)
+ end
+
+ # The column number in code units of the given encoding where this location
+ # ends from the start of the line.
+ def end_code_units_column(encoding = Encoding::UTF_16LE)
+ source.code_units_column(end_offset, encoding)
+ end
+
+ # Implement the hash pattern matching interface for Location.
+ def deconstruct_keys(keys)
+ { start_offset: start_offset, end_offset: end_offset }
+ end
+
+ # Implement the pretty print interface for Location.
+ def pretty_print(q)
+ q.text("(#{start_line},#{start_column})-(#{end_line},#{end_column})")
+ end
+
+ # Returns true if the given other location is equal to this location.
+ def ==(other)
+ Location === other &&
+ other.start_offset == start_offset &&
+ other.end_offset == end_offset
+ end
+
+ # Returns a new location that stretches from this location to the given
+ # other location. Raises an error if this location is not before the other
+ # location or if they don't share the same source.
+ def join(other)
+ raise "Incompatible sources" if source != other.source
+ raise "Incompatible locations" if start_offset > other.start_offset
+
+ Location.new(source, start_offset, other.end_offset - start_offset)
+ end
+ end
+
+ # This represents a comment that was encountered during parsing. It is the
+ # base class for all comment types.
+ class Comment
+ # The location of this comment in the source.
+ attr_reader :location
+
+ # Create a new comment object with the given location.
+ def initialize(location)
+ @location = location
+ end
+
+ # Implement the hash pattern matching interface for Comment.
+ def deconstruct_keys(keys)
+ { location: location }
+ end
+
+ # Returns the content of the comment by slicing it from the source code.
+ def slice
+ location.slice
+ end
+ end
+
+ # InlineComment objects are the most common. They correspond to comments in
+ # the source file like this one that start with #.
+ class InlineComment < Comment
+ # Returns true if this comment happens on the same line as other code and
+ # false if the comment is by itself.
+ def trailing?
+ !location.start_line_slice.strip.empty?
+ end
+
+ # Returns a string representation of this comment.
+ def inspect
+ "#<Prism::InlineComment @location=#{location.inspect}>"
+ end
+ end
+
+ # EmbDocComment objects correspond to comments that are surrounded by =begin
+ # and =end.
+ class EmbDocComment < Comment
+ # This can only be true for inline comments.
+ def trailing?
+ false
+ end
+
+ # Returns a string representation of this comment.
+ def inspect
+ "#<Prism::EmbDocComment @location=#{location.inspect}>"
+ end
+ end
+
+ # This represents a magic comment that was encountered during parsing.
+ class MagicComment
+ # A Location object representing the location of the key in the source.
+ attr_reader :key_loc
+
+ # A Location object representing the location of the value in the source.
+ attr_reader :value_loc
+
+ # Create a new magic comment object with the given key and value locations.
+ def initialize(key_loc, value_loc)
+ @key_loc = key_loc
+ @value_loc = value_loc
+ end
+
+ # Returns the key of the magic comment by slicing it from the source code.
+ def key
+ key_loc.slice
+ end
+
+ # Returns the value of the magic comment by slicing it from the source code.
+ def value
+ value_loc.slice
+ end
+
+ # Implement the hash pattern matching interface for MagicComment.
+ def deconstruct_keys(keys)
+ { key_loc: key_loc, value_loc: value_loc }
+ end
+
+ # Returns a string representation of this magic comment.
+ def inspect
+ "#<Prism::MagicComment @key=#{key.inspect} @value=#{value.inspect}>"
+ end
+ end
+
+ # This represents an error that was encountered during parsing.
+ class ParseError
+ # The type of error. This is an _internal_ symbol that is used for
+ # communicating with translation layers. It is not meant to be public API.
+ attr_reader :type
+
+ # The message associated with this error.
+ attr_reader :message
+
+ # A Location object representing the location of this error in the source.
+ attr_reader :location
+
+ # The level of this error.
+ attr_reader :level
+
+ # Create a new error object with the given message and location.
+ def initialize(type, message, location, level)
+ @type = type
+ @message = message
+ @location = location
+ @level = level
+ end
+
+ # Implement the hash pattern matching interface for ParseError.
+ def deconstruct_keys(keys)
+ { type: type, message: message, location: location, level: level }
+ end
+
+ # Returns a string representation of this error.
+ def inspect
+ "#<Prism::ParseError @type=#{@type.inspect} @message=#{@message.inspect} @location=#{@location.inspect} @level=#{@level.inspect}>"
+ end
+ end
+
+ # This represents a warning that was encountered during parsing.
+ class ParseWarning
+ # The type of warning. This is an _internal_ symbol that is used for
+ # communicating with translation layers. It is not meant to be public API.
+ attr_reader :type
+
+ # The message associated with this warning.
+ attr_reader :message
+
+ # A Location object representing the location of this warning in the source.
+ attr_reader :location
+
+ # The level of this warning.
+ attr_reader :level
+
+ # Create a new warning object with the given message and location.
+ def initialize(type, message, location, level)
+ @type = type
+ @message = message
+ @location = location
+ @level = level
+ end
+
+ # Implement the hash pattern matching interface for ParseWarning.
+ def deconstruct_keys(keys)
+ { type: type, message: message, location: location, level: level }
+ end
+
+ # Returns a string representation of this warning.
+ def inspect
+ "#<Prism::ParseWarning @type=#{@type.inspect} @message=#{@message.inspect} @location=#{@location.inspect} @level=#{@level.inspect}>"
+ end
+ end
+
+ # This represents the result of a call to ::parse or ::parse_file. It contains
+ # the requested structure, any comments that were encounters, and any errors
+ # that were encountered.
+ class Result
+ # The list of comments that were encountered during parsing.
+ attr_reader :comments
+
+ # The list of magic comments that were encountered during parsing.
+ attr_reader :magic_comments
+
+ # An optional location that represents the location of the __END__ marker
+ # and the rest of the content of the file. This content is loaded into the
+ # DATA constant when the file being parsed is the main file being executed.
+ attr_reader :data_loc
+
+ # The list of errors that were generated during parsing.
+ attr_reader :errors
+
+ # The list of warnings that were generated during parsing.
+ attr_reader :warnings
+
+ # A Source instance that represents the source code that was parsed.
+ attr_reader :source
+
+ # Create a new result object with the given values.
+ def initialize(comments, magic_comments, data_loc, errors, warnings, source)
+ @comments = comments
+ @magic_comments = magic_comments
+ @data_loc = data_loc
+ @errors = errors
+ @warnings = warnings
+ @source = source
+ end
+
+ # Implement the hash pattern matching interface for Result.
+ def deconstruct_keys(keys)
+ { comments: comments, magic_comments: magic_comments, data_loc: data_loc, errors: errors, warnings: warnings }
+ end
+
+ # Returns the encoding of the source code that was parsed.
+ def encoding
+ source.encoding
+ end
+
+ # Returns true if there were no errors during parsing and false if there
+ # were.
+ def success?
+ errors.empty?
+ end
+
+ # Returns true if there were errors during parsing and false if there were
+ # not.
+ def failure?
+ !success?
+ end
+ end
+
+ # This is a result specific to the `parse` and `parse_file` methods.
+ class ParseResult < Result
+ # The syntax tree that was parsed from the source code.
+ attr_reader :value
+
+ # Create a new parse result object with the given values.
+ def initialize(value, comments, magic_comments, data_loc, errors, warnings, source)
+ @value = value
+ super(comments, magic_comments, data_loc, errors, warnings, source)
+ end
+
+ # Implement the hash pattern matching interface for ParseResult.
+ def deconstruct_keys(keys)
+ super.merge!(value: value)
+ end
+ end
+
+ # This is a result specific to the `lex` and `lex_file` methods.
+ class LexResult < Result
+ # The list of tokens that were parsed from the source code.
+ attr_reader :value
+
+ # Create a new lex result object with the given values.
+ def initialize(value, comments, magic_comments, data_loc, errors, warnings, source)
+ @value = value
+ super(comments, magic_comments, data_loc, errors, warnings, source)
+ end
+
+ # Implement the hash pattern matching interface for LexResult.
+ def deconstruct_keys(keys)
+ super.merge!(value: value)
+ end
+ end
+
+ # This is a result specific to the `parse_lex` and `parse_lex_file` methods.
+ class ParseLexResult < Result
+ # A tuple of the syntax tree and the list of tokens that were parsed from
+ # the source code.
+ attr_reader :value
+
+ # Create a new parse lex result object with the given values.
+ def initialize(value, comments, magic_comments, data_loc, errors, warnings, source)
+ @value = value
+ super(comments, magic_comments, data_loc, errors, warnings, source)
+ end
+
+ # Implement the hash pattern matching interface for ParseLexResult.
+ def deconstruct_keys(keys)
+ super.merge!(value: value)
+ end
+ end
+
+ # This represents a token from the Ruby source.
+ class Token
+ # The Source object that represents the source this token came from.
+ attr_reader :source
+ private :source
+
+ # The type of token that this token is.
+ attr_reader :type
+
+ # A byteslice of the source that this token represents.
+ attr_reader :value
+
+ # Create a new token object with the given type, value, and location.
+ def initialize(source, type, value, location)
+ @source = source
+ @type = type
+ @value = value
+ @location = location
+ end
+
+ # Implement the hash pattern matching interface for Token.
+ def deconstruct_keys(keys)
+ { type: type, value: value, location: location }
+ end
+
+ # A Location object representing the location of this token in the source.
+ def location
+ location = @location
+ return location if location.is_a?(Location)
+ @location = Location.new(source, location >> 32, location & 0xFFFFFFFF)
+ end
+
+ # Implement the pretty print interface for Token.
+ def pretty_print(q)
+ q.group do
+ q.text(type.to_s)
+ self.location.pretty_print(q)
+ q.text("(")
+ q.nest(2) do
+ q.breakable("")
+ q.pp(value)
+ end
+ q.breakable("")
+ q.text(")")
+ end
+ end
+
+ # Returns true if the given other token is equal to this token.
+ def ==(other)
+ Token === other &&
+ other.type == type &&
+ other.value == value
+ end
+ end
+end
diff --git a/lib/prism/parse_result/comments.rb b/lib/prism/parse_result/comments.rb
new file mode 100644
index 0000000000..f8f74d2503
--- /dev/null
+++ b/lib/prism/parse_result/comments.rb
@@ -0,0 +1,194 @@
+# frozen_string_literal: true
+
+module Prism
+ class ParseResult
+ # When we've parsed the source, we have both the syntax tree and the list of
+ # comments that we found in the source. This class is responsible for
+ # walking the tree and finding the nearest location to attach each comment.
+ #
+ # It does this by first finding the nearest locations to each comment.
+ # Locations can either come from nodes directly or from location fields on
+ # nodes. For example, a `ClassNode` has an overall location encompassing the
+ # entire class, but it also has a location for the `class` keyword.
+ #
+ # Once the nearest locations are found, it determines which one to attach
+ # to. If it's a trailing comment (a comment on the same line as other source
+ # code), it will favor attaching to the nearest location that occurs before
+ # the comment. Otherwise it will favor attaching to the nearest location
+ # that is after the comment.
+ class Comments
+ # A target for attaching comments that is based on a specific node's
+ # location.
+ class NodeTarget # :nodoc:
+ attr_reader :node
+
+ def initialize(node)
+ @node = node
+ end
+
+ def start_offset
+ node.start_offset
+ end
+
+ def end_offset
+ node.end_offset
+ end
+
+ def encloses?(comment)
+ start_offset <= comment.location.start_offset &&
+ comment.location.end_offset <= end_offset
+ end
+
+ def leading_comment(comment)
+ node.location.leading_comment(comment)
+ end
+
+ def trailing_comment(comment)
+ node.location.trailing_comment(comment)
+ end
+ end
+
+ # A target for attaching comments that is based on a location field on a
+ # node. For example, the `end` token of a ClassNode.
+ class LocationTarget # :nodoc:
+ attr_reader :location
+
+ def initialize(location)
+ @location = location
+ end
+
+ def start_offset
+ location.start_offset
+ end
+
+ def end_offset
+ location.end_offset
+ end
+
+ def encloses?(comment)
+ false
+ end
+
+ def leading_comment(comment)
+ location.leading_comment(comment)
+ end
+
+ def trailing_comment(comment)
+ location.trailing_comment(comment)
+ end
+ end
+
+ # The parse result that we are attaching comments to.
+ attr_reader :parse_result
+
+ # Create a new Comments object that will attach comments to the given
+ # parse result.
+ def initialize(parse_result)
+ @parse_result = parse_result
+ end
+
+ # Attach the comments to their respective locations in the tree by
+ # mutating the parse result.
+ def attach!
+ parse_result.comments.each do |comment|
+ preceding, enclosing, following = nearest_targets(parse_result.value, comment)
+
+ if comment.trailing?
+ if preceding
+ preceding.trailing_comment(comment)
+ else
+ (following || enclosing || NodeTarget.new(parse_result.value)).leading_comment(comment)
+ end
+ else
+ # If a comment exists on its own line, prefer a leading comment.
+ if following
+ following.leading_comment(comment)
+ elsif preceding
+ preceding.trailing_comment(comment)
+ else
+ (enclosing || NodeTarget.new(parse_result.value)).leading_comment(comment)
+ end
+ end
+ end
+ end
+
+ private
+
+ # Responsible for finding the nearest targets to the given comment within
+ # the context of the given encapsulating node.
+ def nearest_targets(node, comment)
+ comment_start = comment.location.start_offset
+ comment_end = comment.location.end_offset
+
+ targets = [] #: Array[_Target]
+ node.comment_targets.map do |value|
+ case value
+ when StatementsNode
+ targets.concat(value.body.map { |node| NodeTarget.new(node) })
+ when Node
+ targets << NodeTarget.new(value)
+ when Location
+ targets << LocationTarget.new(value)
+ end
+ end
+
+ targets.sort_by!(&:start_offset)
+ preceding = nil #: _Target?
+ following = nil #: _Target?
+
+ left = 0
+ right = targets.length
+
+ # This is a custom binary search that finds the nearest nodes to the
+ # given comment. When it finds a node that completely encapsulates the
+ # comment, it recurses downward into the tree.
+ while left < right
+ middle = (left + right) / 2
+ target = targets[middle]
+
+ target_start = target.start_offset
+ target_end = target.end_offset
+
+ if target.encloses?(comment)
+ # @type var target: NodeTarget
+ # The comment is completely contained by this target. Abandon the
+ # binary search at this level.
+ return nearest_targets(target.node, comment)
+ end
+
+ if target_end <= comment_start
+ # This target falls completely before the comment. Because we will
+ # never consider this target or any targets before it again, this
+ # target must be the closest preceding target we have encountered so
+ # far.
+ preceding = target
+ left = middle + 1
+ next
+ end
+
+ if comment_end <= target_start
+ # This target falls completely after the comment. Because we will
+ # never consider this target or any targets after it again, this
+ # target must be the closest following target we have encountered so
+ # far.
+ following = target
+ right = middle
+ next
+ end
+
+ # This should only happen if there is a bug in this parser.
+ raise "Comment location overlaps with a target location"
+ end
+
+ [preceding, NodeTarget.new(node), following]
+ end
+ end
+
+ private_constant :Comments
+
+ # Attach the list of comments to their respective locations in the tree.
+ def attach_comments!
+ Comments.new(self).attach! # steep:ignore
+ end
+ end
+end
diff --git a/lib/prism/parse_result/newlines.rb b/lib/prism/parse_result/newlines.rb
new file mode 100644
index 0000000000..927c17fe2f
--- /dev/null
+++ b/lib/prism/parse_result/newlines.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+module Prism
+ class ParseResult
+ # The :line tracepoint event gets fired whenever the Ruby VM encounters an
+ # expression on a new line. The types of expressions that can trigger this
+ # event are:
+ #
+ # * if statements
+ # * unless statements
+ # * nodes that are children of statements lists
+ #
+ # In order to keep track of the newlines, we have a list of offsets that
+ # come back from the parser. We assign these offsets to the first nodes that
+ # we find in the tree that are on those lines.
+ #
+ # Note that the logic in this file should be kept in sync with the Java
+ # MarkNewlinesVisitor, since that visitor is responsible for marking the
+ # newlines for JRuby/TruffleRuby.
+ class Newlines < Visitor
+ # Create a new Newlines visitor with the given newline offsets.
+ def initialize(newline_marked)
+ @newline_marked = newline_marked
+ end
+
+ # Permit block/lambda nodes to mark newlines within themselves.
+ def visit_block_node(node)
+ old_newline_marked = @newline_marked
+ @newline_marked = Array.new(old_newline_marked.size, false)
+
+ begin
+ super(node)
+ ensure
+ @newline_marked = old_newline_marked
+ end
+ end
+
+ alias_method :visit_lambda_node, :visit_block_node
+
+ # Mark if/unless nodes as newlines.
+ def visit_if_node(node)
+ node.set_newline_flag(@newline_marked)
+ super(node)
+ end
+
+ alias_method :visit_unless_node, :visit_if_node
+
+ # Permit statements lists to mark newlines within themselves.
+ def visit_statements_node(node)
+ node.body.each do |child|
+ child.set_newline_flag(@newline_marked)
+ end
+ super(node)
+ end
+ end
+
+ private_constant :Newlines
+
+ # Walk the tree and mark nodes that are on a new line.
+ def mark_newlines!
+ value.accept(Newlines.new(Array.new(1 + source.offsets.size, false))) # steep:ignore
+ end
+ end
+end
diff --git a/lib/prism/pattern.rb b/lib/prism/pattern.rb
new file mode 100644
index 0000000000..e12cfd597f
--- /dev/null
+++ b/lib/prism/pattern.rb
@@ -0,0 +1,262 @@
+# frozen_string_literal: true
+
+module Prism
+ # A pattern is an object that wraps a Ruby pattern matching expression. The
+ # expression would normally be passed to an `in` clause within a `case`
+ # expression or a rightward assignment expression. For example, in the
+ # following snippet:
+ #
+ # case node
+ # in ConstantPathNode[ConstantReadNode[name: :Prism], ConstantReadNode[name: :Pattern]]
+ # end
+ #
+ # the pattern is the <tt>ConstantPathNode[...]</tt> expression.
+ #
+ # The pattern gets compiled into an object that responds to #call by running
+ # the #compile method. This method itself will run back through Prism to
+ # parse the expression into a tree, then walk the tree to generate the
+ # necessary callable objects. For example, if you wanted to compile the
+ # expression above into a callable, you would:
+ #
+ # callable = Prism::Pattern.new("ConstantPathNode[ConstantReadNode[name: :Prism], ConstantReadNode[name: :Pattern]]").compile
+ # callable.call(node)
+ #
+ # The callable object returned by #compile is guaranteed to respond to #call
+ # with a single argument, which is the node to match against. It also is
+ # guaranteed to respond to #===, which means it itself can be used in a `case`
+ # expression, as in:
+ #
+ # case node
+ # when callable
+ # end
+ #
+ # If the query given to the initializer cannot be compiled into a valid
+ # matcher (either because of a syntax error or because it is using syntax we
+ # do not yet support) then a Prism::Pattern::CompilationError will be
+ # raised.
+ class Pattern
+ # Raised when the query given to a pattern is either invalid Ruby syntax or
+ # is using syntax that we don't yet support.
+ class CompilationError < StandardError
+ # Create a new CompilationError with the given representation of the node
+ # that caused the error.
+ def initialize(repr)
+ super(<<~ERROR)
+ prism was unable to compile the pattern you provided into a usable
+ expression. It failed on to understand the node represented by:
+
+ #{repr}
+
+ Note that not all syntax supported by Ruby's pattern matching syntax
+ is also supported by prism's patterns. If you're using some syntax
+ that you believe should be supported, please open an issue on
+ GitHub at https://github.com/ruby/prism/issues/new.
+ ERROR
+ end
+ end
+
+ # The query that this pattern was initialized with.
+ attr_reader :query
+
+ # Create a new pattern with the given query. The query should be a string
+ # containing a Ruby pattern matching expression.
+ def initialize(query)
+ @query = query
+ @compiled = nil
+ end
+
+ # Compile the query into a callable object that can be used to match against
+ # nodes.
+ def compile
+ result = Prism.parse("case nil\nin #{query}\nend")
+
+ case_match_node = result.value.statements.body.last
+ raise CompilationError, case_match_node.inspect unless case_match_node.is_a?(CaseMatchNode)
+
+ in_node = case_match_node.conditions.last
+ raise CompilationError, in_node.inspect unless in_node.is_a?(InNode)
+
+ compile_node(in_node.pattern)
+ end
+
+ # Scan the given node and all of its children for nodes that match the
+ # pattern. If a block is given, it will be called with each node that
+ # matches the pattern. If no block is given, an enumerator will be returned
+ # that will yield each node that matches the pattern.
+ def scan(root)
+ return to_enum(:scan, root) unless block_given?
+
+ @compiled ||= compile
+ queue = [root]
+
+ while (node = queue.shift)
+ yield node if @compiled.call(node) # steep:ignore
+ queue.concat(node.compact_child_nodes)
+ end
+ end
+
+ private
+
+ # Shortcut for combining two procs into one that returns true if both return
+ # true.
+ def combine_and(left, right)
+ ->(other) { left.call(other) && right.call(other) }
+ end
+
+ # Shortcut for combining two procs into one that returns true if either
+ # returns true.
+ def combine_or(left, right)
+ ->(other) { left.call(other) || right.call(other) }
+ end
+
+ # Raise an error because the given node is not supported.
+ def compile_error(node)
+ raise CompilationError, node.inspect
+ end
+
+ # in [foo, bar, baz]
+ def compile_array_pattern_node(node)
+ compile_error(node) if !node.rest.nil? || node.posts.any?
+
+ constant = node.constant
+ compiled_constant = compile_node(constant) if constant
+
+ preprocessed = node.requireds.map { |required| compile_node(required) }
+
+ compiled_requireds = ->(other) do
+ deconstructed = other.deconstruct
+
+ deconstructed.length == preprocessed.length &&
+ preprocessed
+ .zip(deconstructed)
+ .all? { |(matcher, value)| matcher.call(value) }
+ end
+
+ if compiled_constant
+ combine_and(compiled_constant, compiled_requireds)
+ else
+ compiled_requireds
+ end
+ end
+
+ # in foo | bar
+ def compile_alternation_pattern_node(node)
+ combine_or(compile_node(node.left), compile_node(node.right))
+ end
+
+ # in Prism::ConstantReadNode
+ def compile_constant_path_node(node)
+ parent = node.parent
+
+ if parent.is_a?(ConstantReadNode) && parent.slice == "Prism"
+ compile_node(node.child)
+ else
+ compile_error(node)
+ end
+ end
+
+ # in ConstantReadNode
+ # in String
+ def compile_constant_read_node(node)
+ value = node.slice
+
+ if Prism.const_defined?(value, false)
+ clazz = Prism.const_get(value)
+
+ ->(other) { clazz === other }
+ elsif Object.const_defined?(value, false)
+ clazz = Object.const_get(value)
+
+ ->(other) { clazz === other }
+ else
+ compile_error(node)
+ end
+ end
+
+ # in InstanceVariableReadNode[name: Symbol]
+ # in { name: Symbol }
+ def compile_hash_pattern_node(node)
+ compile_error(node) if node.rest
+ compiled_constant = compile_node(node.constant) if node.constant
+
+ preprocessed =
+ node.elements.to_h do |element|
+ key = element.key
+ if key.is_a?(SymbolNode)
+ [key.unescaped.to_sym, compile_node(element.value)]
+ else
+ raise CompilationError, element.inspect
+ end
+ end
+
+ compiled_keywords = ->(other) do
+ deconstructed = other.deconstruct_keys(preprocessed.keys)
+
+ preprocessed.all? do |keyword, matcher|
+ deconstructed.key?(keyword) && matcher.call(deconstructed[keyword])
+ end
+ end
+
+ if compiled_constant
+ combine_and(compiled_constant, compiled_keywords)
+ else
+ compiled_keywords
+ end
+ end
+
+ # in nil
+ def compile_nil_node(node)
+ ->(attribute) { attribute.nil? }
+ end
+
+ # in /foo/
+ def compile_regular_expression_node(node)
+ regexp = Regexp.new(node.unescaped, node.closing[1..])
+
+ ->(attribute) { regexp === attribute }
+ end
+
+ # in ""
+ # in "foo"
+ def compile_string_node(node)
+ string = node.unescaped
+
+ ->(attribute) { string === attribute }
+ end
+
+ # in :+
+ # in :foo
+ def compile_symbol_node(node)
+ symbol = node.unescaped.to_sym
+
+ ->(attribute) { symbol === attribute }
+ end
+
+ # Compile any kind of node. Dispatch out to the individual compilation
+ # methods based on the type of node.
+ def compile_node(node)
+ case node
+ when AlternationPatternNode
+ compile_alternation_pattern_node(node)
+ when ArrayPatternNode
+ compile_array_pattern_node(node)
+ when ConstantPathNode
+ compile_constant_path_node(node)
+ when ConstantReadNode
+ compile_constant_read_node(node)
+ when HashPatternNode
+ compile_hash_pattern_node(node)
+ when NilNode
+ compile_nil_node(node)
+ when RegularExpressionNode
+ compile_regular_expression_node(node)
+ when StringNode
+ compile_string_node(node)
+ when SymbolNode
+ compile_symbol_node(node)
+ else
+ compile_error(node)
+ end
+ end
+ end
+end
diff --git a/lib/prism/polyfill/string.rb b/lib/prism/polyfill/string.rb
new file mode 100644
index 0000000000..3fa9b5a0c5
--- /dev/null
+++ b/lib/prism/polyfill/string.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+# Polyfill for String#unpack1 with the offset parameter. Not all Ruby engines
+# have Method#parameters implemented, so we check the arity instead if
+# necessary.
+if (unpack1 = String.instance_method(:unpack1)).respond_to?(:parameters) ? unpack1.parameters.none? { |_, name| name == :offset } : (unpack1.arity == 1)
+ String.prepend(
+ Module.new {
+ def unpack1(format, offset: 0) # :nodoc:
+ offset == 0 ? super(format) : self[offset..].unpack1(format) # steep:ignore
+ end
+ }
+ )
+end
diff --git a/lib/prism/prism.gemspec b/lib/prism/prism.gemspec
new file mode 100644
index 0000000000..cfc10c1a07
--- /dev/null
+++ b/lib/prism/prism.gemspec
@@ -0,0 +1,165 @@
+# frozen_string_literal: true
+
+Gem::Specification.new do |spec|
+ spec.name = "prism"
+ spec.version = "0.27.0"
+ spec.authors = ["Shopify"]
+ spec.email = ["ruby@shopify.com"]
+
+ spec.summary = "Prism Ruby parser"
+ spec.homepage = "https://github.com/ruby/prism"
+ spec.license = "MIT"
+
+ spec.required_ruby_version = ">= 2.7.0"
+
+ spec.require_paths = ["lib"]
+ spec.files = [
+ "BSDmakefile",
+ "CHANGELOG.md",
+ "CODE_OF_CONDUCT.md",
+ "CONTRIBUTING.md",
+ "LICENSE.md",
+ "Makefile",
+ "README.md",
+ "config.yml",
+ "docs/build_system.md",
+ "docs/configuration.md",
+ "docs/cruby_compilation.md",
+ "docs/design.md",
+ "docs/encoding.md",
+ "docs/fuzzing.md",
+ "docs/heredocs.md",
+ "docs/javascript.md",
+ "docs/local_variable_depth.md",
+ "docs/mapping.md",
+ "docs/parser_translation.md",
+ "docs/parsing_rules.md",
+ "docs/releasing.md",
+ "docs/ripper_translation.md",
+ "docs/ruby_api.md",
+ "docs/ruby_parser_translation.md",
+ "docs/serialization.md",
+ "docs/testing.md",
+ "ext/prism/api_node.c",
+ "ext/prism/api_pack.c",
+ "ext/prism/extension.c",
+ "ext/prism/extension.h",
+ "include/prism.h",
+ "include/prism/ast.h",
+ "include/prism/defines.h",
+ "include/prism/diagnostic.h",
+ "include/prism/encoding.h",
+ "include/prism/node.h",
+ "include/prism/options.h",
+ "include/prism/pack.h",
+ "include/prism/parser.h",
+ "include/prism/prettyprint.h",
+ "include/prism/regexp.h",
+ "include/prism/static_literals.h",
+ "include/prism/util/pm_buffer.h",
+ "include/prism/util/pm_char.h",
+ "include/prism/util/pm_constant_pool.h",
+ "include/prism/util/pm_integer.h",
+ "include/prism/util/pm_list.h",
+ "include/prism/util/pm_memchr.h",
+ "include/prism/util/pm_newline_list.h",
+ "include/prism/util/pm_strncasecmp.h",
+ "include/prism/util/pm_string.h",
+ "include/prism/util/pm_string_list.h",
+ "include/prism/util/pm_strpbrk.h",
+ "include/prism/version.h",
+ "lib/prism.rb",
+ "lib/prism/compiler.rb",
+ "lib/prism/debug.rb",
+ "lib/prism/desugar_compiler.rb",
+ "lib/prism/dispatcher.rb",
+ "lib/prism/dot_visitor.rb",
+ "lib/prism/dsl.rb",
+ "lib/prism/ffi.rb",
+ "lib/prism/inspect_visitor.rb",
+ "lib/prism/lex_compat.rb",
+ "lib/prism/mutation_compiler.rb",
+ "lib/prism/node_ext.rb",
+ "lib/prism/node.rb",
+ "lib/prism/pack.rb",
+ "lib/prism/parse_result.rb",
+ "lib/prism/parse_result/comments.rb",
+ "lib/prism/parse_result/newlines.rb",
+ "lib/prism/pattern.rb",
+ "lib/prism/polyfill/string.rb",
+ "lib/prism/reflection.rb",
+ "lib/prism/serialize.rb",
+ "lib/prism/translation.rb",
+ "lib/prism/translation/parser.rb",
+ "lib/prism/translation/parser33.rb",
+ "lib/prism/translation/parser34.rb",
+ "lib/prism/translation/parser/compiler.rb",
+ "lib/prism/translation/parser/lexer.rb",
+ "lib/prism/translation/parser/rubocop.rb",
+ "lib/prism/translation/ripper.rb",
+ "lib/prism/translation/ripper/sexp.rb",
+ "lib/prism/translation/ripper/shim.rb",
+ "lib/prism/translation/ruby_parser.rb",
+ "lib/prism/visitor.rb",
+ "prism.gemspec",
+ "rbi/prism.rbi",
+ "rbi/prism/compiler.rbi",
+ "rbi/prism/desugar_compiler.rbi",
+ "rbi/prism/inspect_visitor.rbi",
+ "rbi/prism/mutation_compiler.rbi",
+ "rbi/prism/node_ext.rbi",
+ "rbi/prism/node.rbi",
+ "rbi/prism/parse_result.rbi",
+ "rbi/prism/reflection.rbi",
+ "rbi/prism/translation/parser.rbi",
+ "rbi/prism/translation/parser/compiler.rbi",
+ "rbi/prism/translation/parser33.rbi",
+ "rbi/prism/translation/parser34.rbi",
+ "rbi/prism/translation/ripper.rbi",
+ "rbi/prism/translation/ripper/ripper_compiler.rbi",
+ "rbi/prism/translation/ruby_parser.rbi",
+ "rbi/prism/visitor.rbi",
+ "sig/prism.rbs",
+ "sig/prism/compiler.rbs",
+ "sig/prism/dispatcher.rbs",
+ "sig/prism/dot_visitor.rbs",
+ "sig/prism/dsl.rbs",
+ "sig/prism/inspect_visitor.rbs",
+ "sig/prism/mutation_compiler.rbs",
+ "sig/prism/node_ext.rbs",
+ "sig/prism/node.rbs",
+ "sig/prism/pack.rbs",
+ "sig/prism/parse_result.rbs",
+ "sig/prism/pattern.rbs",
+ "sig/prism/reflection.rbs",
+ "sig/prism/serialize.rbs",
+ "sig/prism/visitor.rbs",
+ "src/diagnostic.c",
+ "src/encoding.c",
+ "src/node.c",
+ "src/options.c",
+ "src/pack.c",
+ "src/prettyprint.c",
+ "src/prism.c",
+ "src/regexp.c",
+ "src/serialize.c",
+ "src/static_literals.c",
+ "src/token_type.c",
+ "src/util/pm_buffer.c",
+ "src/util/pm_char.c",
+ "src/util/pm_constant_pool.c",
+ "src/util/pm_integer.c",
+ "src/util/pm_list.c",
+ "src/util/pm_memchr.c",
+ "src/util/pm_newline_list.c",
+ "src/util/pm_string_list.c",
+ "src/util/pm_string.c",
+ "src/util/pm_strncasecmp.c",
+ "src/util/pm_strpbrk.c"
+ ]
+
+ spec.extensions = ["ext/prism/extconf.rb"]
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
+ spec.metadata["source_code_uri"] = "https://github.com/ruby/prism"
+ spec.metadata["changelog_uri"] = "https://github.com/ruby/prism/blob/main/CHANGELOG.md"
+end
diff --git a/lib/prism/translation.rb b/lib/prism/translation.rb
new file mode 100644
index 0000000000..8b75e8a3ab
--- /dev/null
+++ b/lib/prism/translation.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Prism
+ # This module is responsible for converting the prism syntax tree into other
+ # syntax trees.
+ module Translation # steep:ignore
+ autoload :Parser, "prism/translation/parser"
+ autoload :Parser33, "prism/translation/parser33"
+ autoload :Parser34, "prism/translation/parser34"
+ autoload :Ripper, "prism/translation/ripper"
+ autoload :RubyParser, "prism/translation/ruby_parser"
+ end
+end
diff --git a/lib/prism/translation/parser.rb b/lib/prism/translation/parser.rb
new file mode 100644
index 0000000000..0d11b8f566
--- /dev/null
+++ b/lib/prism/translation/parser.rb
@@ -0,0 +1,302 @@
+# frozen_string_literal: true
+
+require "parser"
+
+module Prism
+ module Translation
+ # This class is the entry-point for converting a prism syntax tree into the
+ # whitequark/parser gem's syntax tree. It inherits from the base parser for
+ # the parser gem, and overrides the parse* methods to parse with prism and
+ # then translate.
+ class Parser < ::Parser::Base
+ Diagnostic = ::Parser::Diagnostic # :nodoc:
+ private_constant :Diagnostic
+
+ # The parser gem has a list of diagnostics with a hard-coded set of error
+ # messages. We create our own diagnostic class in order to set our own
+ # error messages.
+ class PrismDiagnostic < Diagnostic
+ # This is the cached message coming from prism.
+ attr_reader :message
+
+ # Initialize a new diagnostic with the given message and location.
+ def initialize(message, level, reason, location)
+ @message = message
+ super(level, reason, {}, location, [])
+ end
+ end
+
+ Racc_debug_parser = false # :nodoc:
+
+ def version # :nodoc:
+ 34
+ end
+
+ # The default encoding for Ruby files is UTF-8.
+ def default_encoding
+ Encoding::UTF_8
+ end
+
+ def yyerror # :nodoc:
+ end
+
+ # Parses a source buffer and returns the AST.
+ def parse(source_buffer)
+ @source_buffer = source_buffer
+ source = source_buffer.source
+
+ offset_cache = build_offset_cache(source)
+ result = unwrap(Prism.parse(source, filepath: source_buffer.name, version: convert_for_prism(version)), offset_cache)
+
+ build_ast(result.value, offset_cache)
+ ensure
+ @source_buffer = nil
+ end
+
+ # Parses a source buffer and returns the AST and the source code comments.
+ def parse_with_comments(source_buffer)
+ @source_buffer = source_buffer
+ source = source_buffer.source
+
+ offset_cache = build_offset_cache(source)
+ result = unwrap(Prism.parse(source, filepath: source_buffer.name, version: convert_for_prism(version)), offset_cache)
+
+ [
+ build_ast(result.value, offset_cache),
+ build_comments(result.comments, offset_cache)
+ ]
+ ensure
+ @source_buffer = nil
+ end
+
+ # Parses a source buffer and returns the AST, the source code comments,
+ # and the tokens emitted by the lexer.
+ def tokenize(source_buffer, recover = false)
+ @source_buffer = source_buffer
+ source = source_buffer.source
+
+ offset_cache = build_offset_cache(source)
+ result =
+ begin
+ unwrap(Prism.parse_lex(source, filepath: source_buffer.name, version: convert_for_prism(version)), offset_cache)
+ rescue ::Parser::SyntaxError
+ raise if !recover
+ end
+
+ program, tokens = result.value
+ ast = build_ast(program, offset_cache) if result.success?
+
+ [
+ ast,
+ build_comments(result.comments, offset_cache),
+ build_tokens(tokens, offset_cache)
+ ]
+ ensure
+ @source_buffer = nil
+ end
+
+ # Since prism resolves num params for us, we don't need to support this
+ # kind of logic here.
+ def try_declare_numparam(node)
+ node.children[0].match?(/\A_[1-9]\z/)
+ end
+
+ private
+
+ # This is a hook to allow consumers to disable some errors if they don't
+ # want them to block creating the syntax tree.
+ def valid_error?(error)
+ true
+ end
+
+ # This is a hook to allow consumers to disable some warnings if they don't
+ # want them to block creating the syntax tree.
+ def valid_warning?(warning)
+ true
+ end
+
+ # Build a diagnostic from the given prism parse error.
+ def error_diagnostic(error, offset_cache)
+ location = error.location
+ diagnostic_location = build_range(location, offset_cache)
+
+ case error.type
+ when :argument_block_multi
+ Diagnostic.new(:error, :block_and_blockarg, {}, diagnostic_location, [])
+ when :argument_formal_constant
+ Diagnostic.new(:error, :argument_const, {}, diagnostic_location, [])
+ when :argument_formal_class
+ Diagnostic.new(:error, :argument_cvar, {}, diagnostic_location, [])
+ when :argument_formal_global
+ Diagnostic.new(:error, :argument_gvar, {}, diagnostic_location, [])
+ when :argument_formal_ivar
+ Diagnostic.new(:error, :argument_ivar, {}, diagnostic_location, [])
+ when :argument_no_forwarding_amp
+ Diagnostic.new(:error, :no_anonymous_blockarg, {}, diagnostic_location, [])
+ when :argument_no_forwarding_star
+ Diagnostic.new(:error, :no_anonymous_restarg, {}, diagnostic_location, [])
+ when :argument_no_forwarding_star_star
+ Diagnostic.new(:error, :no_anonymous_kwrestarg, {}, diagnostic_location, [])
+ when :begin_lonely_else
+ location = location.copy(length: 4)
+ diagnostic_location = build_range(location, offset_cache)
+ Diagnostic.new(:error, :useless_else, {}, diagnostic_location, [])
+ when :class_name, :module_name
+ Diagnostic.new(:error, :module_name_const, {}, diagnostic_location, [])
+ when :class_in_method
+ Diagnostic.new(:error, :class_in_def, {}, diagnostic_location, [])
+ when :def_endless_setter
+ Diagnostic.new(:error, :endless_setter, {}, diagnostic_location, [])
+ when :embdoc_term
+ Diagnostic.new(:error, :embedded_document, {}, diagnostic_location, [])
+ when :incomplete_variable_class, :incomplete_variable_class_3_3_0
+ location = location.copy(length: location.length + 1)
+ diagnostic_location = build_range(location, offset_cache)
+
+ Diagnostic.new(:error, :cvar_name, { name: location.slice }, diagnostic_location, [])
+ when :incomplete_variable_instance, :incomplete_variable_instance_3_3_0
+ location = location.copy(length: location.length + 1)
+ diagnostic_location = build_range(location, offset_cache)
+
+ Diagnostic.new(:error, :ivar_name, { name: location.slice }, diagnostic_location, [])
+ when :invalid_variable_global, :invalid_variable_global_3_3_0
+ Diagnostic.new(:error, :gvar_name, { name: location.slice }, diagnostic_location, [])
+ when :module_in_method
+ Diagnostic.new(:error, :module_in_def, {}, diagnostic_location, [])
+ when :numbered_parameter_ordinary
+ Diagnostic.new(:error, :ordinary_param_defined, {}, diagnostic_location, [])
+ when :numbered_parameter_outer_scope
+ Diagnostic.new(:error, :numparam_used_in_outer_scope, {}, diagnostic_location, [])
+ when :parameter_circular
+ Diagnostic.new(:error, :circular_argument_reference, { var_name: location.slice }, diagnostic_location, [])
+ when :parameter_name_repeat
+ Diagnostic.new(:error, :duplicate_argument, {}, diagnostic_location, [])
+ when :parameter_numbered_reserved
+ Diagnostic.new(:error, :reserved_for_numparam, { name: location.slice }, diagnostic_location, [])
+ when :regexp_unknown_options
+ Diagnostic.new(:error, :regexp_options, { options: location.slice[1..] }, diagnostic_location, [])
+ when :singleton_for_literals
+ Diagnostic.new(:error, :singleton_literal, {}, diagnostic_location, [])
+ when :string_literal_eof
+ Diagnostic.new(:error, :string_eof, {}, diagnostic_location, [])
+ when :unexpected_token_ignore
+ Diagnostic.new(:error, :unexpected_token, { token: location.slice }, diagnostic_location, [])
+ when :write_target_in_method
+ Diagnostic.new(:error, :dynamic_const, {}, diagnostic_location, [])
+ else
+ PrismDiagnostic.new(error.message, :error, error.type, diagnostic_location)
+ end
+ end
+
+ # Build a diagnostic from the given prism parse warning.
+ def warning_diagnostic(warning, offset_cache)
+ diagnostic_location = build_range(warning.location, offset_cache)
+
+ case warning.type
+ when :ambiguous_first_argument_plus
+ Diagnostic.new(:warning, :ambiguous_prefix, { prefix: "+" }, diagnostic_location, [])
+ when :ambiguous_first_argument_minus
+ Diagnostic.new(:warning, :ambiguous_prefix, { prefix: "-" }, diagnostic_location, [])
+ when :ambiguous_prefix_ampersand
+ Diagnostic.new(:warning, :ambiguous_prefix, { prefix: "&" }, diagnostic_location, [])
+ when :ambiguous_prefix_star
+ Diagnostic.new(:warning, :ambiguous_prefix, { prefix: "*" }, diagnostic_location, [])
+ when :ambiguous_prefix_star_star
+ Diagnostic.new(:warning, :ambiguous_prefix, { prefix: "**" }, diagnostic_location, [])
+ when :ambiguous_slash
+ Diagnostic.new(:warning, :ambiguous_regexp, {}, diagnostic_location, [])
+ when :dot_dot_dot_eol
+ Diagnostic.new(:warning, :triple_dot_at_eol, {}, diagnostic_location, [])
+ when :duplicated_hash_key
+ # skip, parser does this on its own
+ else
+ PrismDiagnostic.new(warning.message, :warning, warning.type, diagnostic_location)
+ end
+ end
+
+ # If there was a error generated during the parse, then raise an
+ # appropriate syntax error. Otherwise return the result.
+ def unwrap(result, offset_cache)
+ result.errors.each do |error|
+ next unless valid_error?(error)
+ diagnostics.process(error_diagnostic(error, offset_cache))
+ end
+
+ result.warnings.each do |warning|
+ next unless valid_warning?(warning)
+ diagnostic = warning_diagnostic(warning, offset_cache)
+ diagnostics.process(diagnostic) if diagnostic
+ end
+
+ result
+ end
+
+ # Prism deals with offsets in bytes, while the parser gem deals with
+ # offsets in characters. We need to handle this conversion in order to
+ # build the parser gem AST.
+ #
+ # If the bytesize of the source is the same as the length, then we can
+ # just use the offset directly. Otherwise, we build an array where the
+ # index is the byte offset and the value is the character offset.
+ def build_offset_cache(source)
+ if source.bytesize == source.length
+ -> (offset) { offset }
+ else
+ offset_cache = []
+ offset = 0
+
+ source.each_char do |char|
+ char.bytesize.times { offset_cache << offset }
+ offset += 1
+ end
+
+ offset_cache << offset
+ end
+ end
+
+ # Build the parser gem AST from the prism AST.
+ def build_ast(program, offset_cache)
+ program.accept(Compiler.new(self, offset_cache))
+ end
+
+ # Build the parser gem comments from the prism comments.
+ def build_comments(comments, offset_cache)
+ comments.map do |comment|
+ ::Parser::Source::Comment.new(build_range(comment.location, offset_cache))
+ end
+ end
+
+ # Build the parser gem tokens from the prism tokens.
+ def build_tokens(tokens, offset_cache)
+ Lexer.new(source_buffer, tokens, offset_cache).to_a
+ end
+
+ # Build a range from a prism location.
+ def build_range(location, offset_cache)
+ ::Parser::Source::Range.new(
+ source_buffer,
+ offset_cache[location.start_offset],
+ offset_cache[location.end_offset]
+ )
+ end
+
+ # Converts the version format handled by Parser to the format handled by Prism.
+ def convert_for_prism(version)
+ case version
+ when 33
+ "3.3.0"
+ when 34
+ "3.4.0"
+ else
+ "latest"
+ end
+ end
+
+ require_relative "parser/compiler"
+ require_relative "parser/lexer"
+
+ private_constant :Compiler
+ private_constant :Lexer
+ end
+ end
+end
diff --git a/lib/prism/translation/parser/compiler.rb b/lib/prism/translation/parser/compiler.rb
new file mode 100644
index 0000000000..a4aaa41d6f
--- /dev/null
+++ b/lib/prism/translation/parser/compiler.rb
@@ -0,0 +1,1996 @@
+# frozen_string_literal: true
+
+module Prism
+ module Translation
+ class Parser
+ # A visitor that knows how to convert a prism syntax tree into the
+ # whitequark/parser gem's syntax tree.
+ class Compiler < ::Prism::Compiler
+ # Raised when the tree is malformed or there is a bug in the compiler.
+ class CompilationError < StandardError
+ end
+
+ # The Parser::Base instance that is being used to build the AST.
+ attr_reader :parser
+
+ # The Parser::Builders::Default instance that is being used to build the
+ # AST.
+ attr_reader :builder
+
+ # The Parser::Source::Buffer instance that is holding a reference to the
+ # source code.
+ attr_reader :source_buffer
+
+ # The offset cache that is used to map between byte and character
+ # offsets in the file.
+ attr_reader :offset_cache
+
+ # The types of values that can be forwarded in the current scope.
+ attr_reader :forwarding
+
+ # Whether or not the current node is in a destructure.
+ attr_reader :in_destructure
+
+ # Whether or not the current node is in a pattern.
+ attr_reader :in_pattern
+
+ # Initialize a new compiler with the given parser, offset cache, and
+ # options.
+ def initialize(parser, offset_cache, forwarding: [], in_destructure: false, in_pattern: false)
+ @parser = parser
+ @builder = parser.builder
+ @source_buffer = parser.source_buffer
+ @offset_cache = offset_cache
+
+ @forwarding = forwarding
+ @in_destructure = in_destructure
+ @in_pattern = in_pattern
+ end
+
+ # alias foo bar
+ # ^^^^^^^^^^^^^
+ def visit_alias_method_node(node)
+ builder.alias(token(node.keyword_loc), visit(node.new_name), visit(node.old_name))
+ end
+
+ # alias $foo $bar
+ # ^^^^^^^^^^^^^^^
+ def visit_alias_global_variable_node(node)
+ builder.alias(token(node.keyword_loc), visit(node.new_name), visit(node.old_name))
+ end
+
+ # foo => bar | baz
+ # ^^^^^^^^^
+ def visit_alternation_pattern_node(node)
+ builder.match_alt(visit(node.left), token(node.operator_loc), visit(node.right))
+ end
+
+ # a and b
+ # ^^^^^^^
+ def visit_and_node(node)
+ builder.logical_op(:and, visit(node.left), token(node.operator_loc), visit(node.right))
+ end
+
+ # []
+ # ^^
+ def visit_array_node(node)
+ builder.array(token(node.opening_loc), visit_all(node.elements), token(node.closing_loc))
+ end
+
+ # foo => [bar]
+ # ^^^^^
+ def visit_array_pattern_node(node)
+ elements = [*node.requireds]
+ elements << node.rest if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode)
+ elements.concat(node.posts)
+ visited = visit_all(elements)
+
+ if node.rest.is_a?(ImplicitRestNode)
+ visited[-1] = builder.match_with_trailing_comma(visited[-1], token(node.rest.location))
+ end
+
+ if node.constant
+ builder.const_pattern(visit(node.constant), token(node.opening_loc), builder.array_pattern(nil, visited, nil), token(node.closing_loc))
+ else
+ builder.array_pattern(token(node.opening_loc), visited, token(node.closing_loc))
+ end
+ end
+
+ # foo(bar)
+ # ^^^
+ def visit_arguments_node(node)
+ visit_all(node.arguments)
+ end
+
+ # { a: 1 }
+ # ^^^^
+ def visit_assoc_node(node)
+ if in_pattern
+ if node.value.is_a?(ImplicitNode)
+ if node.key.is_a?(SymbolNode)
+ builder.match_hash_var([node.key.unescaped, srange(node.key.location)])
+ else
+ builder.match_hash_var_from_str(token(node.key.opening_loc), visit_all(node.key.parts), token(node.key.closing_loc))
+ end
+ else
+ builder.pair_keyword([node.key.unescaped, srange(node.key.location)], visit(node.value))
+ end
+ elsif node.value.is_a?(ImplicitNode)
+ if (value = node.value.value).is_a?(LocalVariableReadNode)
+ builder.pair_keyword(
+ [node.key.unescaped, srange(node.key)],
+ builder.ident([value.name, srange(node.key.value_loc)]).updated(:lvar)
+ )
+ else
+ builder.pair_label([node.key.unescaped, srange(node.key.location)])
+ end
+ elsif node.operator_loc
+ builder.pair(visit(node.key), token(node.operator_loc), visit(node.value))
+ elsif node.key.is_a?(SymbolNode) && node.key.opening_loc.nil?
+ builder.pair_keyword([node.key.unescaped, srange(node.key.location)], visit(node.value))
+ else
+ parts =
+ if node.key.is_a?(SymbolNode)
+ [builder.string_internal([node.key.unescaped, srange(node.key.value_loc)])]
+ else
+ visit_all(node.key.parts)
+ end
+
+ builder.pair_quoted(token(node.key.opening_loc), parts, token(node.key.closing_loc), visit(node.value))
+ end
+ end
+
+ # def foo(**); bar(**); end
+ # ^^
+ #
+ # { **foo }
+ # ^^^^^
+ def visit_assoc_splat_node(node)
+ if node.value.nil? && forwarding.include?(:**)
+ builder.forwarded_kwrestarg(token(node.operator_loc))
+ else
+ builder.kwsplat(token(node.operator_loc), visit(node.value))
+ end
+ end
+
+ # $+
+ # ^^
+ def visit_back_reference_read_node(node)
+ builder.back_ref(token(node.location))
+ end
+
+ # begin end
+ # ^^^^^^^^^
+ def visit_begin_node(node)
+ rescue_bodies = []
+
+ if (rescue_clause = node.rescue_clause)
+ begin
+ find_start_offset = (rescue_clause.reference&.location || rescue_clause.exceptions.last&.location || rescue_clause.keyword_loc).end_offset
+ find_end_offset = (rescue_clause.statements&.location&.start_offset || rescue_clause.consequent&.location&.start_offset || (find_start_offset + 1))
+
+ rescue_bodies << builder.rescue_body(
+ token(rescue_clause.keyword_loc),
+ rescue_clause.exceptions.any? ? builder.array(nil, visit_all(rescue_clause.exceptions), nil) : nil,
+ token(rescue_clause.operator_loc),
+ visit(rescue_clause.reference),
+ srange_find(find_start_offset, find_end_offset, [";"]),
+ visit(rescue_clause.statements)
+ )
+ end until (rescue_clause = rescue_clause.consequent).nil?
+ end
+
+ begin_body =
+ builder.begin_body(
+ visit(node.statements),
+ rescue_bodies,
+ token(node.else_clause&.else_keyword_loc),
+ visit(node.else_clause),
+ token(node.ensure_clause&.ensure_keyword_loc),
+ visit(node.ensure_clause&.statements)
+ )
+
+ if node.begin_keyword_loc
+ builder.begin_keyword(token(node.begin_keyword_loc), begin_body, token(node.end_keyword_loc))
+ else
+ begin_body
+ end
+ end
+
+ # foo(&bar)
+ # ^^^^
+ def visit_block_argument_node(node)
+ builder.block_pass(token(node.operator_loc), visit(node.expression))
+ end
+
+ # foo { |; bar| }
+ # ^^^
+ def visit_block_local_variable_node(node)
+ builder.shadowarg(token(node.location))
+ end
+
+ # A block on a keyword or method call.
+ def visit_block_node(node)
+ raise CompilationError, "Cannot directly compile block nodes"
+ end
+
+ # def foo(&bar); end
+ # ^^^^
+ def visit_block_parameter_node(node)
+ builder.blockarg(token(node.operator_loc), token(node.name_loc))
+ end
+
+ # A block's parameters.
+ def visit_block_parameters_node(node)
+ [*visit(node.parameters)].concat(visit_all(node.locals))
+ end
+
+ # break
+ # ^^^^^
+ #
+ # break foo
+ # ^^^^^^^^^
+ def visit_break_node(node)
+ builder.keyword_cmd(:break, token(node.keyword_loc), nil, visit(node.arguments) || [], nil)
+ end
+
+ # foo
+ # ^^^
+ #
+ # foo.bar
+ # ^^^^^^^
+ #
+ # foo.bar() {}
+ # ^^^^^^^^^^^^
+ def visit_call_node(node)
+ name = node.name
+ arguments = node.arguments&.arguments || []
+ block = node.block
+
+ if block.is_a?(BlockArgumentNode)
+ arguments = [*arguments, block]
+ block = nil
+ end
+
+ if node.call_operator_loc.nil?
+ case name
+ when :-@
+ case (receiver = node.receiver).type
+ when :integer_node, :float_node, :rational_node, :imaginary_node
+ return visit(numeric_negate(node.message_loc, receiver))
+ end
+ when :!
+ return visit_block(builder.not_op(token(node.message_loc), token(node.opening_loc), visit(node.receiver), token(node.closing_loc)), block)
+ when :=~
+ if (receiver = node.receiver).is_a?(RegularExpressionNode)
+ return builder.match_op(visit(receiver), token(node.message_loc), visit(node.arguments.arguments.first))
+ end
+ when :[]
+ return visit_block(builder.index(visit(node.receiver), token(node.opening_loc), visit_all(arguments), token(node.closing_loc)), block)
+ when :[]=
+ if node.message != "[]=" && node.arguments && block.nil? && !node.safe_navigation?
+ arguments = node.arguments.arguments[...-1]
+ arguments << node.block if node.block
+
+ return visit_block(
+ builder.assign(
+ builder.index_asgn(
+ visit(node.receiver),
+ token(node.opening_loc),
+ visit_all(arguments),
+ token(node.closing_loc),
+ ),
+ srange_find(node.message_loc.end_offset, node.arguments.arguments.last.location.start_offset, ["="]),
+ visit(node.arguments.arguments.last)
+ ),
+ block
+ )
+ end
+ end
+ end
+
+ message_loc = node.message_loc
+ call_operator_loc = node.call_operator_loc
+ call_operator = [{ "." => :dot, "&." => :anddot, "::" => "::" }.fetch(call_operator_loc.slice), srange(call_operator_loc)] if call_operator_loc
+
+ visit_block(
+ if name.end_with?("=") && !message_loc.slice.end_with?("=") && node.arguments && block.nil?
+ builder.assign(
+ builder.attr_asgn(visit(node.receiver), call_operator, token(message_loc)),
+ srange_find(message_loc.end_offset, node.arguments.location.start_offset, ["="]),
+ visit(node.arguments.arguments.last)
+ )
+ else
+ builder.call_method(
+ visit(node.receiver),
+ call_operator,
+ message_loc ? [node.name, srange(message_loc)] : nil,
+ token(node.opening_loc),
+ visit_all(arguments),
+ token(node.closing_loc)
+ )
+ end,
+ block
+ )
+ end
+
+ # foo.bar += baz
+ # ^^^^^^^^^^^^^^^
+ def visit_call_operator_write_node(node)
+ call_operator_loc = node.call_operator_loc
+
+ builder.op_assign(
+ builder.call_method(
+ visit(node.receiver),
+ call_operator_loc.nil? ? nil : [{ "." => :dot, "&." => :anddot, "::" => "::" }.fetch(call_operator_loc.slice), srange(call_operator_loc)],
+ node.message_loc ? [node.read_name, srange(node.message_loc)] : nil,
+ nil,
+ [],
+ nil
+ ),
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
+ visit(node.value)
+ )
+ end
+
+ # foo.bar &&= baz
+ # ^^^^^^^^^^^^^^^
+ alias visit_call_and_write_node visit_call_operator_write_node
+
+ # foo.bar ||= baz
+ # ^^^^^^^^^^^^^^^
+ alias visit_call_or_write_node visit_call_operator_write_node
+
+ # foo.bar, = 1
+ # ^^^^^^^
+ def visit_call_target_node(node)
+ call_operator_loc = node.call_operator_loc
+
+ builder.attr_asgn(
+ visit(node.receiver),
+ call_operator_loc.nil? ? nil : [{ "." => :dot, "&." => :anddot, "::" => "::" }.fetch(call_operator_loc.slice), srange(call_operator_loc)],
+ token(node.message_loc)
+ )
+ end
+
+ # foo => bar => baz
+ # ^^^^^^^^^^
+ def visit_capture_pattern_node(node)
+ builder.match_as(visit(node.value), token(node.operator_loc), visit(node.target))
+ end
+
+ # case foo; when bar; end
+ # ^^^^^^^^^^^^^^^^^^^^^^^
+ def visit_case_node(node)
+ builder.case(
+ token(node.case_keyword_loc),
+ visit(node.predicate),
+ visit_all(node.conditions),
+ token(node.consequent&.else_keyword_loc),
+ visit(node.consequent),
+ token(node.end_keyword_loc)
+ )
+ end
+
+ # case foo; in bar; end
+ # ^^^^^^^^^^^^^^^^^^^^^
+ def visit_case_match_node(node)
+ builder.case_match(
+ token(node.case_keyword_loc),
+ visit(node.predicate),
+ visit_all(node.conditions),
+ token(node.consequent&.else_keyword_loc),
+ visit(node.consequent),
+ token(node.end_keyword_loc)
+ )
+ end
+
+ # class Foo; end
+ # ^^^^^^^^^^^^^^
+ def visit_class_node(node)
+ builder.def_class(
+ token(node.class_keyword_loc),
+ visit(node.constant_path),
+ token(node.inheritance_operator_loc),
+ visit(node.superclass),
+ node.body&.accept(copy_compiler(forwarding: [])),
+ token(node.end_keyword_loc)
+ )
+ end
+
+ # @@foo
+ # ^^^^^
+ def visit_class_variable_read_node(node)
+ builder.cvar(token(node.location))
+ end
+
+ # @@foo = 1
+ # ^^^^^^^^^
+ def visit_class_variable_write_node(node)
+ builder.assign(
+ builder.assignable(builder.cvar(token(node.name_loc))),
+ token(node.operator_loc),
+ visit(node.value)
+ )
+ end
+
+ # @@foo += bar
+ # ^^^^^^^^^^^^
+ def visit_class_variable_operator_write_node(node)
+ builder.op_assign(
+ builder.assignable(builder.cvar(token(node.name_loc))),
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
+ visit(node.value)
+ )
+ end
+
+ # @@foo &&= bar
+ # ^^^^^^^^^^^^^
+ alias visit_class_variable_and_write_node visit_class_variable_operator_write_node
+
+ # @@foo ||= bar
+ # ^^^^^^^^^^^^^
+ alias visit_class_variable_or_write_node visit_class_variable_operator_write_node
+
+ # @@foo, = bar
+ # ^^^^^
+ def visit_class_variable_target_node(node)
+ builder.assignable(builder.cvar(token(node.location)))
+ end
+
+ # Foo
+ # ^^^
+ def visit_constant_read_node(node)
+ builder.const([node.name, srange(node.location)])
+ end
+
+ # Foo = 1
+ # ^^^^^^^
+ #
+ # Foo, Bar = 1
+ # ^^^ ^^^
+ def visit_constant_write_node(node)
+ builder.assign(builder.assignable(builder.const([node.name, srange(node.name_loc)])), token(node.operator_loc), visit(node.value))
+ end
+
+ # Foo += bar
+ # ^^^^^^^^^^^
+ def visit_constant_operator_write_node(node)
+ builder.op_assign(
+ builder.assignable(builder.const([node.name, srange(node.name_loc)])),
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
+ visit(node.value)
+ )
+ end
+
+ # Foo &&= bar
+ # ^^^^^^^^^^^^
+ alias visit_constant_and_write_node visit_constant_operator_write_node
+
+ # Foo ||= bar
+ # ^^^^^^^^^^^^
+ alias visit_constant_or_write_node visit_constant_operator_write_node
+
+ # Foo, = bar
+ # ^^^
+ def visit_constant_target_node(node)
+ builder.assignable(builder.const([node.name, srange(node.location)]))
+ end
+
+ # Foo::Bar
+ # ^^^^^^^^
+ def visit_constant_path_node(node)
+ if node.parent.nil?
+ builder.const_global(
+ token(node.delimiter_loc),
+ [node.child.name, srange(node.child.location)]
+ )
+ else
+ builder.const_fetch(
+ visit(node.parent),
+ token(node.delimiter_loc),
+ [node.child.name, srange(node.child.location)]
+ )
+ end
+ end
+
+ # Foo::Bar = 1
+ # ^^^^^^^^^^^^
+ #
+ # Foo::Foo, Bar::Bar = 1
+ # ^^^^^^^^ ^^^^^^^^
+ def visit_constant_path_write_node(node)
+ builder.assign(
+ builder.assignable(visit(node.target)),
+ token(node.operator_loc),
+ visit(node.value)
+ )
+ end
+
+ # Foo::Bar += baz
+ # ^^^^^^^^^^^^^^^
+ def visit_constant_path_operator_write_node(node)
+ builder.op_assign(
+ builder.assignable(visit(node.target)),
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
+ visit(node.value)
+ )
+ end
+
+ # Foo::Bar &&= baz
+ # ^^^^^^^^^^^^^^^^
+ alias visit_constant_path_and_write_node visit_constant_path_operator_write_node
+
+ # Foo::Bar ||= baz
+ # ^^^^^^^^^^^^^^^^
+ alias visit_constant_path_or_write_node visit_constant_path_operator_write_node
+
+ # Foo::Bar, = baz
+ # ^^^^^^^^
+ def visit_constant_path_target_node(node)
+ builder.assignable(visit_constant_path_node(node))
+ end
+
+ # def foo; end
+ # ^^^^^^^^^^^^
+ #
+ # def self.foo; end
+ # ^^^^^^^^^^^^^^^^^
+ def visit_def_node(node)
+ if node.equal_loc
+ if node.receiver
+ builder.def_endless_singleton(
+ token(node.def_keyword_loc),
+ visit(node.receiver.is_a?(ParenthesesNode) ? node.receiver.body : node.receiver),
+ token(node.operator_loc),
+ token(node.name_loc),
+ builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false),
+ token(node.equal_loc),
+ node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters)))
+ )
+ else
+ builder.def_endless_method(
+ token(node.def_keyword_loc),
+ token(node.name_loc),
+ builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false),
+ token(node.equal_loc),
+ node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters)))
+ )
+ end
+ elsif node.receiver
+ builder.def_singleton(
+ token(node.def_keyword_loc),
+ visit(node.receiver.is_a?(ParenthesesNode) ? node.receiver.body : node.receiver),
+ token(node.operator_loc),
+ token(node.name_loc),
+ builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false),
+ node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters))),
+ token(node.end_keyword_loc)
+ )
+ else
+ builder.def_method(
+ token(node.def_keyword_loc),
+ token(node.name_loc),
+ builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false),
+ node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters))),
+ token(node.end_keyword_loc)
+ )
+ end
+ end
+
+ # defined? a
+ # ^^^^^^^^^^
+ #
+ # defined?(a)
+ # ^^^^^^^^^^^
+ def visit_defined_node(node)
+ builder.keyword_cmd(
+ :defined?,
+ token(node.keyword_loc),
+ token(node.lparen_loc),
+ [visit(node.value)],
+ token(node.rparen_loc)
+ )
+ end
+
+ # if foo then bar else baz end
+ # ^^^^^^^^^^^^
+ def visit_else_node(node)
+ visit(node.statements)
+ end
+
+ # "foo #{bar}"
+ # ^^^^^^
+ def visit_embedded_statements_node(node)
+ builder.begin(
+ token(node.opening_loc),
+ visit(node.statements),
+ token(node.closing_loc)
+ )
+ end
+
+ # "foo #@bar"
+ # ^^^^^
+ def visit_embedded_variable_node(node)
+ visit(node.variable)
+ end
+
+ # begin; foo; ensure; bar; end
+ # ^^^^^^^^^^^^
+ def visit_ensure_node(node)
+ raise CompilationError, "Cannot directly compile ensure nodes"
+ end
+
+ # false
+ # ^^^^^
+ def visit_false_node(node)
+ builder.false(token(node.location))
+ end
+
+ # foo => [*, bar, *]
+ # ^^^^^^^^^^^
+ def visit_find_pattern_node(node)
+ elements = [node.left, *node.requireds, node.right]
+
+ if node.constant
+ builder.const_pattern(visit(node.constant), token(node.opening_loc), builder.find_pattern(nil, visit_all(elements), nil), token(node.closing_loc))
+ else
+ builder.find_pattern(token(node.opening_loc), visit_all(elements), token(node.closing_loc))
+ end
+ end
+
+ # 1.0
+ # ^^^
+ def visit_float_node(node)
+ visit_numeric(node, builder.float([node.value, srange(node.location)]))
+ end
+
+ # for foo in bar do end
+ # ^^^^^^^^^^^^^^^^^^^^^
+ def visit_for_node(node)
+ builder.for(
+ token(node.for_keyword_loc),
+ visit(node.index),
+ token(node.in_keyword_loc),
+ visit(node.collection),
+ if node.do_keyword_loc
+ token(node.do_keyword_loc)
+ else
+ srange_find(node.collection.location.end_offset, (node.statements&.location || node.end_keyword_loc).start_offset, [";"])
+ end,
+ visit(node.statements),
+ token(node.end_keyword_loc)
+ )
+ end
+
+ # def foo(...); bar(...); end
+ # ^^^
+ def visit_forwarding_arguments_node(node)
+ builder.forwarded_args(token(node.location))
+ end
+
+ # def foo(...); end
+ # ^^^
+ def visit_forwarding_parameter_node(node)
+ builder.forward_arg(token(node.location))
+ end
+
+ # super
+ # ^^^^^
+ #
+ # super {}
+ # ^^^^^^^^
+ def visit_forwarding_super_node(node)
+ visit_block(
+ builder.keyword_cmd(
+ :zsuper,
+ ["super", srange_offsets(node.location.start_offset, node.location.start_offset + 5)]
+ ),
+ node.block
+ )
+ end
+
+ # $foo
+ # ^^^^
+ def visit_global_variable_read_node(node)
+ builder.gvar(token(node.location))
+ end
+
+ # $foo = 1
+ # ^^^^^^^^
+ def visit_global_variable_write_node(node)
+ builder.assign(
+ builder.assignable(builder.gvar(token(node.name_loc))),
+ token(node.operator_loc),
+ visit(node.value)
+ )
+ end
+
+ # $foo += bar
+ # ^^^^^^^^^^^
+ def visit_global_variable_operator_write_node(node)
+ builder.op_assign(
+ builder.assignable(builder.gvar(token(node.name_loc))),
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
+ visit(node.value)
+ )
+ end
+
+ # $foo &&= bar
+ # ^^^^^^^^^^^^
+ alias visit_global_variable_and_write_node visit_global_variable_operator_write_node
+
+ # $foo ||= bar
+ # ^^^^^^^^^^^^
+ alias visit_global_variable_or_write_node visit_global_variable_operator_write_node
+
+ # $foo, = bar
+ # ^^^^
+ def visit_global_variable_target_node(node)
+ builder.assignable(builder.gvar([node.slice, srange(node.location)]))
+ end
+
+ # {}
+ # ^^
+ def visit_hash_node(node)
+ builder.associate(
+ token(node.opening_loc),
+ visit_all(node.elements),
+ token(node.closing_loc)
+ )
+ end
+
+ # foo => {}
+ # ^^
+ def visit_hash_pattern_node(node)
+ elements = [*node.elements, *node.rest]
+
+ if node.constant
+ builder.const_pattern(visit(node.constant), token(node.opening_loc), builder.hash_pattern(nil, visit_all(elements), nil), token(node.closing_loc))
+ else
+ builder.hash_pattern(token(node.opening_loc), visit_all(elements), token(node.closing_loc))
+ end
+ end
+
+ # if foo then bar end
+ # ^^^^^^^^^^^^^^^^^^^
+ #
+ # bar if foo
+ # ^^^^^^^^^^
+ #
+ # foo ? bar : baz
+ # ^^^^^^^^^^^^^^^
+ def visit_if_node(node)
+ if !node.if_keyword_loc
+ builder.ternary(
+ visit(node.predicate),
+ token(node.then_keyword_loc),
+ visit(node.statements),
+ token(node.consequent.else_keyword_loc),
+ visit(node.consequent)
+ )
+ elsif node.if_keyword_loc.start_offset == node.location.start_offset
+ builder.condition(
+ token(node.if_keyword_loc),
+ visit(node.predicate),
+ if node.then_keyword_loc
+ token(node.then_keyword_loc)
+ else
+ srange_find(node.predicate.location.end_offset, (node.statements&.location || node.consequent&.location || node.end_keyword_loc).start_offset, [";"])
+ end,
+ visit(node.statements),
+ case node.consequent
+ when IfNode
+ token(node.consequent.if_keyword_loc)
+ when ElseNode
+ token(node.consequent.else_keyword_loc)
+ end,
+ visit(node.consequent),
+ if node.if_keyword != "elsif"
+ token(node.end_keyword_loc)
+ end
+ )
+ else
+ builder.condition_mod(
+ visit(node.statements),
+ visit(node.consequent),
+ token(node.if_keyword_loc),
+ visit(node.predicate)
+ )
+ end
+ end
+
+ # 1i
+ # ^^
+ def visit_imaginary_node(node)
+ visit_numeric(node, builder.complex([imaginary_value(node), srange(node.location)]))
+ end
+
+ # { foo: }
+ # ^^^^
+ def visit_implicit_node(node)
+ raise CompilationError, "Cannot directly compile implicit nodes"
+ end
+
+ # foo { |bar,| }
+ # ^
+ def visit_implicit_rest_node(node)
+ raise CompilationError, "Cannot compile implicit rest nodes"
+ end
+
+ # case foo; in bar; end
+ # ^^^^^^^^^^^^^^^^^^^^^
+ def visit_in_node(node)
+ pattern = nil
+ guard = nil
+
+ case node.pattern
+ when IfNode
+ pattern = within_pattern { |compiler| node.pattern.statements.accept(compiler) }
+ guard = builder.if_guard(token(node.pattern.if_keyword_loc), visit(node.pattern.predicate))
+ when UnlessNode
+ pattern = within_pattern { |compiler| node.pattern.statements.accept(compiler) }
+ guard = builder.unless_guard(token(node.pattern.keyword_loc), visit(node.pattern.predicate))
+ else
+ pattern = within_pattern { |compiler| node.pattern.accept(compiler) }
+ end
+
+ builder.in_pattern(
+ token(node.in_loc),
+ pattern,
+ guard,
+ srange_find(node.pattern.location.end_offset, node.statements&.location&.start_offset, [";", "then"]),
+ visit(node.statements)
+ )
+ end
+
+ # foo[bar] += baz
+ # ^^^^^^^^^^^^^^^
+ def visit_index_operator_write_node(node)
+ arguments = node.arguments&.arguments || []
+ arguments << node.block if node.block
+
+ builder.op_assign(
+ builder.index(
+ visit(node.receiver),
+ token(node.opening_loc),
+ visit_all(arguments),
+ token(node.closing_loc)
+ ),
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
+ visit(node.value)
+ )
+ end
+
+ # foo[bar] &&= baz
+ # ^^^^^^^^^^^^^^^^
+ alias visit_index_and_write_node visit_index_operator_write_node
+
+ # foo[bar] ||= baz
+ # ^^^^^^^^^^^^^^^^
+ alias visit_index_or_write_node visit_index_operator_write_node
+
+ # foo[bar], = 1
+ # ^^^^^^^^
+ def visit_index_target_node(node)
+ builder.index_asgn(
+ visit(node.receiver),
+ token(node.opening_loc),
+ visit_all(node.arguments.arguments),
+ token(node.closing_loc),
+ )
+ end
+
+ # @foo
+ # ^^^^
+ def visit_instance_variable_read_node(node)
+ builder.ivar(token(node.location))
+ end
+
+ # @foo = 1
+ # ^^^^^^^^
+ def visit_instance_variable_write_node(node)
+ builder.assign(
+ builder.assignable(builder.ivar(token(node.name_loc))),
+ token(node.operator_loc),
+ visit(node.value)
+ )
+ end
+
+ # @foo += bar
+ # ^^^^^^^^^^^
+ def visit_instance_variable_operator_write_node(node)
+ builder.op_assign(
+ builder.assignable(builder.ivar(token(node.name_loc))),
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
+ visit(node.value)
+ )
+ end
+
+ # @foo &&= bar
+ # ^^^^^^^^^^^^
+ alias visit_instance_variable_and_write_node visit_instance_variable_operator_write_node
+
+ # @foo ||= bar
+ # ^^^^^^^^^^^^
+ alias visit_instance_variable_or_write_node visit_instance_variable_operator_write_node
+
+ # @foo, = bar
+ # ^^^^
+ def visit_instance_variable_target_node(node)
+ builder.assignable(builder.ivar(token(node.location)))
+ end
+
+ # 1
+ # ^
+ def visit_integer_node(node)
+ visit_numeric(node, builder.integer([node.value, srange(node.location)]))
+ end
+
+ # /foo #{bar}/
+ # ^^^^^^^^^^^^
+ def visit_interpolated_regular_expression_node(node)
+ builder.regexp_compose(
+ token(node.opening_loc),
+ visit_all(node.parts),
+ [node.closing[0], srange_offsets(node.closing_loc.start_offset, node.closing_loc.start_offset + 1)],
+ builder.regexp_options([node.closing[1..], srange_offsets(node.closing_loc.start_offset + 1, node.closing_loc.end_offset)])
+ )
+ end
+
+ # if /foo #{bar}/ then end
+ # ^^^^^^^^^^^^
+ alias visit_interpolated_match_last_line_node visit_interpolated_regular_expression_node
+
+ # "foo #{bar}"
+ # ^^^^^^^^^^^^
+ def visit_interpolated_string_node(node)
+ if node.heredoc?
+ children, closing = visit_heredoc(node)
+ opening = token(node.opening_loc)
+
+ start_offset = node.opening_loc.end_offset + 1
+ end_offset = node.parts.first.location.start_offset
+
+ # In the below case, the offsets should be the same:
+ #
+ # <<~HEREDOC
+ # a #{b}
+ # HEREDOC
+ #
+ # But in this case, the end_offset would be greater than the start_offset:
+ #
+ # <<~HEREDOC
+ # #{b}
+ # HEREDOC
+ #
+ # So we need to make sure the result node's heredoc range is correct, without updating the children
+ result = if start_offset < end_offset
+ # We need to add a padding string to ensure that the heredoc has correct range for its body
+ padding_string_node = builder.string_internal(["", srange_offsets(start_offset, end_offset)])
+ node_with_correct_location = builder.string_compose(opening, [padding_string_node, *children], closing)
+ # But the padding string should not be included in the final AST, so we need to update the result's children
+ node_with_correct_location.updated(:dstr, children)
+ else
+ builder.string_compose(opening, children, closing)
+ end
+
+ return result
+ end
+
+ parts = if node.parts.one? { |part| part.type == :string_node }
+ node.parts.flat_map do |node|
+ if node.type == :string_node && node.unescaped.lines.count >= 2
+ start_offset = node.content_loc.start_offset
+
+ node.unescaped.lines.map do |line|
+ end_offset = start_offset + line.length
+ offsets = srange_offsets(start_offset, end_offset)
+ start_offset = end_offset
+
+ builder.string_internal([line, offsets])
+ end
+ else
+ visit(node)
+ end
+ end
+ else
+ visit_all(node.parts)
+ end
+
+ builder.string_compose(
+ token(node.opening_loc),
+ parts,
+ token(node.closing_loc)
+ )
+ end
+
+ # :"foo #{bar}"
+ # ^^^^^^^^^^^^^
+ def visit_interpolated_symbol_node(node)
+ builder.symbol_compose(
+ token(node.opening_loc),
+ visit_all(node.parts),
+ token(node.closing_loc)
+ )
+ end
+
+ # `foo #{bar}`
+ # ^^^^^^^^^^^^
+ def visit_interpolated_x_string_node(node)
+ if node.heredoc?
+ children, closing = visit_heredoc(node)
+ builder.xstring_compose(token(node.opening_loc), children, closing)
+ else
+ builder.xstring_compose(
+ token(node.opening_loc),
+ visit_all(node.parts),
+ token(node.closing_loc)
+ )
+ end
+ end
+
+ # -> { it }
+ # ^^^^^^^^^
+ def visit_it_parameters_node(node)
+ builder.args(nil, [], nil, false)
+ end
+
+ # foo(bar: baz)
+ # ^^^^^^^^
+ def visit_keyword_hash_node(node)
+ builder.associate(nil, visit_all(node.elements), nil)
+ end
+
+ # def foo(**bar); end
+ # ^^^^^
+ #
+ # def foo(**); end
+ # ^^
+ def visit_keyword_rest_parameter_node(node)
+ builder.kwrestarg(
+ token(node.operator_loc),
+ node.name ? [node.name, srange(node.name_loc)] : nil
+ )
+ end
+
+ # -> {}
+ # ^^^^^
+ def visit_lambda_node(node)
+ parameters = node.parameters
+ implicit_parameters = parameters.is_a?(NumberedParametersNode) || parameters.is_a?(ItParametersNode)
+
+ builder.block(
+ builder.call_lambda(token(node.operator_loc)),
+ [node.opening, srange(node.opening_loc)],
+ if parameters.nil?
+ builder.args(nil, [], nil, false)
+ elsif implicit_parameters
+ visit(node.parameters)
+ else
+ builder.args(
+ token(node.parameters.opening_loc),
+ visit(node.parameters),
+ token(node.parameters.closing_loc),
+ false
+ )
+ end,
+ node.body&.accept(copy_compiler(forwarding: implicit_parameters ? [] : find_forwarding(parameters&.parameters))),
+ [node.closing, srange(node.closing_loc)]
+ )
+ end
+
+ # foo
+ # ^^^
+ def visit_local_variable_read_node(node)
+ name = node.name
+
+ # This is just a guess. parser doesn't have support for the implicit
+ # `it` variable yet, so we'll probably have to visit this once it
+ # does.
+ name = :it if name == :"0it"
+
+ builder.ident([name, srange(node.location)]).updated(:lvar)
+ end
+
+ # foo = 1
+ # ^^^^^^^
+ def visit_local_variable_write_node(node)
+ builder.assign(
+ builder.assignable(builder.ident(token(node.name_loc))),
+ token(node.operator_loc),
+ visit(node.value)
+ )
+ end
+
+ # foo += bar
+ # ^^^^^^^^^^
+ def visit_local_variable_operator_write_node(node)
+ builder.op_assign(
+ builder.assignable(builder.ident(token(node.name_loc))),
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
+ visit(node.value)
+ )
+ end
+
+ # foo &&= bar
+ # ^^^^^^^^^^^
+ alias visit_local_variable_and_write_node visit_local_variable_operator_write_node
+
+ # foo ||= bar
+ # ^^^^^^^^^^^
+ alias visit_local_variable_or_write_node visit_local_variable_operator_write_node
+
+ # foo, = bar
+ # ^^^
+ def visit_local_variable_target_node(node)
+ if in_pattern
+ builder.assignable(builder.match_var([node.name, srange(node.location)]))
+ else
+ builder.assignable(builder.ident(token(node.location)))
+ end
+ end
+
+ # foo in bar
+ # ^^^^^^^^^^
+ def visit_match_predicate_node(node)
+ builder.match_pattern_p(
+ visit(node.value),
+ token(node.operator_loc),
+ within_pattern { |compiler| node.pattern.accept(compiler) }
+ )
+ end
+
+ # foo => bar
+ # ^^^^^^^^^^
+ def visit_match_required_node(node)
+ builder.match_pattern(
+ visit(node.value),
+ token(node.operator_loc),
+ within_pattern { |compiler| node.pattern.accept(compiler) }
+ )
+ end
+
+ # /(?<foo>foo)/ =~ bar
+ # ^^^^^^^^^^^^^^^^^^^^
+ def visit_match_write_node(node)
+ builder.match_op(
+ visit(node.call.receiver),
+ token(node.call.message_loc),
+ visit(node.call.arguments.arguments.first)
+ )
+ end
+
+ # A node that is missing from the syntax tree. This is only used in the
+ # case of a syntax error. The parser gem doesn't have such a concept, so
+ # we invent our own here.
+ def visit_missing_node(node)
+ ::AST::Node.new(:missing, [], location: ::Parser::Source::Map.new(srange(node.location)))
+ end
+
+ # module Foo; end
+ # ^^^^^^^^^^^^^^^
+ def visit_module_node(node)
+ builder.def_module(
+ token(node.module_keyword_loc),
+ visit(node.constant_path),
+ node.body&.accept(copy_compiler(forwarding: [])),
+ token(node.end_keyword_loc)
+ )
+ end
+
+ # foo, bar = baz
+ # ^^^^^^^^
+ def visit_multi_target_node(node)
+ elements = [*node.lefts]
+ elements << node.rest if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode)
+ elements.concat(node.rights)
+
+ builder.multi_lhs(
+ token(node.lparen_loc),
+ visit_all(elements),
+ token(node.rparen_loc)
+ )
+ end
+
+ # foo, bar = baz
+ # ^^^^^^^^^^^^^^
+ def visit_multi_write_node(node)
+ elements = [*node.lefts]
+ elements << node.rest if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode)
+ elements.concat(node.rights)
+
+ builder.multi_assign(
+ builder.multi_lhs(
+ token(node.lparen_loc),
+ visit_all(elements),
+ token(node.rparen_loc)
+ ),
+ token(node.operator_loc),
+ visit(node.value)
+ )
+ end
+
+ # next
+ # ^^^^
+ #
+ # next foo
+ # ^^^^^^^^
+ def visit_next_node(node)
+ builder.keyword_cmd(
+ :next,
+ token(node.keyword_loc),
+ nil,
+ visit(node.arguments) || [],
+ nil
+ )
+ end
+
+ # nil
+ # ^^^
+ def visit_nil_node(node)
+ builder.nil(token(node.location))
+ end
+
+ # def foo(**nil); end
+ # ^^^^^
+ def visit_no_keywords_parameter_node(node)
+ if in_pattern
+ builder.match_nil_pattern(token(node.operator_loc), token(node.keyword_loc))
+ else
+ builder.kwnilarg(token(node.operator_loc), token(node.keyword_loc))
+ end
+ end
+
+ # -> { _1 + _2 }
+ # ^^^^^^^^^^^^^^
+ def visit_numbered_parameters_node(node)
+ builder.numargs(node.maximum)
+ end
+
+ # $1
+ # ^^
+ def visit_numbered_reference_read_node(node)
+ builder.nth_ref([node.number, srange(node.location)])
+ end
+
+ # def foo(bar: baz); end
+ # ^^^^^^^^
+ def visit_optional_keyword_parameter_node(node)
+ builder.kwoptarg([node.name, srange(node.name_loc)], visit(node.value))
+ end
+
+ # def foo(bar = 1); end
+ # ^^^^^^^
+ def visit_optional_parameter_node(node)
+ builder.optarg(token(node.name_loc), token(node.operator_loc), visit(node.value))
+ end
+
+ # a or b
+ # ^^^^^^
+ def visit_or_node(node)
+ builder.logical_op(:or, visit(node.left), token(node.operator_loc), visit(node.right))
+ end
+
+ # def foo(bar, *baz); end
+ # ^^^^^^^^^
+ def visit_parameters_node(node)
+ params = []
+
+ if node.requireds.any?
+ node.requireds.each do |required|
+ if required.is_a?(RequiredParameterNode)
+ params << visit(required)
+ else
+ compiler = copy_compiler(in_destructure: true)
+ params << required.accept(compiler)
+ end
+ end
+ end
+
+ params.concat(visit_all(node.optionals)) if node.optionals.any?
+ params << visit(node.rest) if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode)
+
+ if node.posts.any?
+ node.posts.each do |post|
+ if post.is_a?(RequiredParameterNode)
+ params << visit(post)
+ else
+ compiler = copy_compiler(in_destructure: true)
+ params << post.accept(compiler)
+ end
+ end
+ end
+
+ params.concat(visit_all(node.keywords)) if node.keywords.any?
+ params << visit(node.keyword_rest) if !node.keyword_rest.nil?
+ params << visit(node.block) if !node.block.nil?
+ params
+ end
+
+ # ()
+ # ^^
+ #
+ # (1)
+ # ^^^
+ def visit_parentheses_node(node)
+ builder.begin(
+ token(node.opening_loc),
+ visit(node.body),
+ token(node.closing_loc)
+ )
+ end
+
+ # foo => ^(bar)
+ # ^^^^^^
+ def visit_pinned_expression_node(node)
+ expression = builder.begin(token(node.lparen_loc), visit(node.expression), token(node.rparen_loc))
+ builder.pin(token(node.operator_loc), expression)
+ end
+
+ # foo = 1 and bar => ^foo
+ # ^^^^
+ def visit_pinned_variable_node(node)
+ builder.pin(token(node.operator_loc), visit(node.variable))
+ end
+
+ # END {}
+ def visit_post_execution_node(node)
+ builder.postexe(
+ token(node.keyword_loc),
+ token(node.opening_loc),
+ visit(node.statements),
+ token(node.closing_loc)
+ )
+ end
+
+ # BEGIN {}
+ def visit_pre_execution_node(node)
+ builder.preexe(
+ token(node.keyword_loc),
+ token(node.opening_loc),
+ visit(node.statements),
+ token(node.closing_loc)
+ )
+ end
+
+ # The top-level program node.
+ def visit_program_node(node)
+ visit(node.statements)
+ end
+
+ # 0..5
+ # ^^^^
+ def visit_range_node(node)
+ if node.exclude_end?
+ builder.range_exclusive(
+ visit(node.left),
+ token(node.operator_loc),
+ visit(node.right)
+ )
+ else
+ builder.range_inclusive(
+ visit(node.left),
+ token(node.operator_loc),
+ visit(node.right)
+ )
+ end
+ end
+
+ # if foo .. bar; end
+ # ^^^^^^^^^^
+ alias visit_flip_flop_node visit_range_node
+
+ # 1r
+ # ^^
+ def visit_rational_node(node)
+ visit_numeric(node, builder.rational([rational_value(node), srange(node.location)]))
+ end
+
+ # redo
+ # ^^^^
+ def visit_redo_node(node)
+ builder.keyword_cmd(:redo, token(node.location))
+ end
+
+ # /foo/
+ # ^^^^^
+ def visit_regular_expression_node(node)
+ builder.regexp_compose(
+ token(node.opening_loc),
+ [builder.string_internal(token(node.content_loc))],
+ [node.closing[0], srange_offsets(node.closing_loc.start_offset, node.closing_loc.start_offset + 1)],
+ builder.regexp_options([node.closing[1..], srange_offsets(node.closing_loc.start_offset + 1, node.closing_loc.end_offset)])
+ )
+ end
+
+ # if /foo/ then end
+ # ^^^^^
+ alias visit_match_last_line_node visit_regular_expression_node
+
+ # def foo(bar:); end
+ # ^^^^
+ def visit_required_keyword_parameter_node(node)
+ builder.kwarg([node.name, srange(node.name_loc)])
+ end
+
+ # def foo(bar); end
+ # ^^^
+ def visit_required_parameter_node(node)
+ builder.arg(token(node.location))
+ end
+
+ # foo rescue bar
+ # ^^^^^^^^^^^^^^
+ def visit_rescue_modifier_node(node)
+ builder.begin_body(
+ visit(node.expression),
+ [
+ builder.rescue_body(
+ token(node.keyword_loc),
+ nil,
+ nil,
+ nil,
+ nil,
+ visit(node.rescue_expression)
+ )
+ ]
+ )
+ end
+
+ # begin; rescue; end
+ # ^^^^^^^
+ def visit_rescue_node(node)
+ raise CompilationError, "Cannot directly compile rescue nodes"
+ end
+
+ # def foo(*bar); end
+ # ^^^^
+ #
+ # def foo(*); end
+ # ^
+ def visit_rest_parameter_node(node)
+ builder.restarg(token(node.operator_loc), token(node.name_loc))
+ end
+
+ # retry
+ # ^^^^^
+ def visit_retry_node(node)
+ builder.keyword_cmd(:retry, token(node.location))
+ end
+
+ # return
+ # ^^^^^^
+ #
+ # return 1
+ # ^^^^^^^^
+ def visit_return_node(node)
+ builder.keyword_cmd(
+ :return,
+ token(node.keyword_loc),
+ nil,
+ visit(node.arguments) || [],
+ nil
+ )
+ end
+
+ # self
+ # ^^^^
+ def visit_self_node(node)
+ builder.self(token(node.location))
+ end
+
+ # A shareable constant.
+ def visit_shareable_constant_node(node)
+ visit(node.write)
+ end
+
+ # class << self; end
+ # ^^^^^^^^^^^^^^^^^^
+ def visit_singleton_class_node(node)
+ builder.def_sclass(
+ token(node.class_keyword_loc),
+ token(node.operator_loc),
+ visit(node.expression),
+ node.body&.accept(copy_compiler(forwarding: [])),
+ token(node.end_keyword_loc)
+ )
+ end
+
+ # __ENCODING__
+ # ^^^^^^^^^^^^
+ def visit_source_encoding_node(node)
+ builder.accessible(builder.__ENCODING__(token(node.location)))
+ end
+
+ # __FILE__
+ # ^^^^^^^^
+ def visit_source_file_node(node)
+ builder.accessible(builder.__FILE__(token(node.location)))
+ end
+
+ # __LINE__
+ # ^^^^^^^^
+ def visit_source_line_node(node)
+ builder.accessible(builder.__LINE__(token(node.location)))
+ end
+
+ # foo(*bar)
+ # ^^^^
+ #
+ # def foo((bar, *baz)); end
+ # ^^^^
+ #
+ # def foo(*); bar(*); end
+ # ^
+ def visit_splat_node(node)
+ if node.expression.nil? && forwarding.include?(:*)
+ builder.forwarded_restarg(token(node.operator_loc))
+ elsif in_destructure
+ builder.restarg(token(node.operator_loc), token(node.expression&.location))
+ elsif in_pattern
+ builder.match_rest(token(node.operator_loc), token(node.expression&.location))
+ else
+ builder.splat(token(node.operator_loc), visit(node.expression))
+ end
+ end
+
+ # A list of statements.
+ def visit_statements_node(node)
+ builder.compstmt(visit_all(node.body))
+ end
+
+ # "foo"
+ # ^^^^^
+ def visit_string_node(node)
+ if node.heredoc?
+ children, closing = visit_heredoc(node.to_interpolated)
+ builder.string_compose(token(node.opening_loc), children, closing)
+ elsif node.opening == "?"
+ builder.character([node.unescaped, srange(node.location)])
+ else
+ content_lines = node.content.lines
+ unescaped_lines = node.unescaped.lines
+
+ parts =
+ if content_lines.length <= 1 || unescaped_lines.length <= 1
+ [builder.string_internal([node.unescaped, srange(node.content_loc)])]
+ elsif content_lines.length != unescaped_lines.length
+ # This occurs when we have line continuations in the string. We
+ # need to come back and fix this, but for now this stops the
+ # code from breaking when we encounter it because of trying to
+ # transpose arrays of different lengths.
+ [builder.string_internal([node.unescaped, srange(node.content_loc)])]
+ else
+ start_offset = node.content_loc.start_offset
+
+ [content_lines, unescaped_lines].transpose.map do |content_line, unescaped_line|
+ end_offset = start_offset + content_line.length
+ offsets = srange_offsets(start_offset, end_offset)
+ start_offset = end_offset
+
+ builder.string_internal([unescaped_line, offsets])
+ end
+ end
+
+ builder.string_compose(
+ token(node.opening_loc),
+ parts,
+ token(node.closing_loc)
+ )
+ end
+ end
+
+ # super(foo)
+ # ^^^^^^^^^^
+ def visit_super_node(node)
+ arguments = node.arguments&.arguments || []
+ block = node.block
+
+ if block.is_a?(BlockArgumentNode)
+ arguments = [*arguments, block]
+ block = nil
+ end
+
+ visit_block(
+ builder.keyword_cmd(
+ :super,
+ token(node.keyword_loc),
+ token(node.lparen_loc),
+ visit_all(arguments),
+ token(node.rparen_loc)
+ ),
+ block
+ )
+ end
+
+ # :foo
+ # ^^^^
+ def visit_symbol_node(node)
+ if node.closing_loc.nil?
+ if node.opening_loc.nil?
+ builder.symbol_internal([node.unescaped, srange(node.location)])
+ else
+ builder.symbol([node.unescaped, srange(node.location)])
+ end
+ else
+ parts = if node.value.lines.one?
+ [builder.string_internal([node.unescaped, srange(node.value_loc)])]
+ else
+ start_offset = node.value_loc.start_offset
+
+ node.value.lines.map do |line|
+ end_offset = start_offset + line.length
+ offsets = srange_offsets(start_offset, end_offset)
+ start_offset = end_offset
+
+ builder.string_internal([line, offsets])
+ end
+ end
+
+ builder.symbol_compose(
+ token(node.opening_loc),
+ parts,
+ token(node.closing_loc)
+ )
+ end
+ end
+
+ # true
+ # ^^^^
+ def visit_true_node(node)
+ builder.true(token(node.location))
+ end
+
+ # undef foo
+ # ^^^^^^^^^
+ def visit_undef_node(node)
+ builder.undef_method(token(node.keyword_loc), visit_all(node.names))
+ end
+
+ # unless foo; bar end
+ # ^^^^^^^^^^^^^^^^^^^
+ #
+ # bar unless foo
+ # ^^^^^^^^^^^^^^
+ def visit_unless_node(node)
+ if node.keyword_loc.start_offset == node.location.start_offset
+ builder.condition(
+ token(node.keyword_loc),
+ visit(node.predicate),
+ if node.then_keyword_loc
+ token(node.then_keyword_loc)
+ else
+ srange_find(node.predicate.location.end_offset, (node.statements&.location || node.consequent&.location || node.end_keyword_loc).start_offset, [";"])
+ end,
+ visit(node.consequent),
+ token(node.consequent&.else_keyword_loc),
+ visit(node.statements),
+ token(node.end_keyword_loc)
+ )
+ else
+ builder.condition_mod(
+ visit(node.consequent),
+ visit(node.statements),
+ token(node.keyword_loc),
+ visit(node.predicate)
+ )
+ end
+ end
+
+ # until foo; bar end
+ # ^^^^^^^^^^^^^^^^^^
+ #
+ # bar until foo
+ # ^^^^^^^^^^^^^
+ def visit_until_node(node)
+ if node.location.start_offset == node.keyword_loc.start_offset
+ builder.loop(
+ :until,
+ token(node.keyword_loc),
+ visit(node.predicate),
+ srange_find(node.predicate.location.end_offset, (node.statements&.location || node.closing_loc).start_offset, [";", "do"]),
+ visit(node.statements),
+ token(node.closing_loc)
+ )
+ else
+ builder.loop_mod(
+ :until,
+ visit(node.statements),
+ token(node.keyword_loc),
+ visit(node.predicate)
+ )
+ end
+ end
+
+ # case foo; when bar; end
+ # ^^^^^^^^^^^^^
+ def visit_when_node(node)
+ builder.when(
+ token(node.keyword_loc),
+ visit_all(node.conditions),
+ if node.then_keyword_loc
+ token(node.then_keyword_loc)
+ else
+ srange_find(node.conditions.last.location.end_offset, node.statements&.location&.start_offset, [";"])
+ end,
+ visit(node.statements)
+ )
+ end
+
+ # while foo; bar end
+ # ^^^^^^^^^^^^^^^^^^
+ #
+ # bar while foo
+ # ^^^^^^^^^^^^^
+ def visit_while_node(node)
+ if node.location.start_offset == node.keyword_loc.start_offset
+ builder.loop(
+ :while,
+ token(node.keyword_loc),
+ visit(node.predicate),
+ srange_find(node.predicate.location.end_offset, (node.statements&.location || node.closing_loc).start_offset, [";", "do"]),
+ visit(node.statements),
+ token(node.closing_loc)
+ )
+ else
+ builder.loop_mod(
+ :while,
+ visit(node.statements),
+ token(node.keyword_loc),
+ visit(node.predicate)
+ )
+ end
+ end
+
+ # `foo`
+ # ^^^^^
+ def visit_x_string_node(node)
+ if node.heredoc?
+ children, closing = visit_heredoc(node.to_interpolated)
+ builder.xstring_compose(token(node.opening_loc), children, closing)
+ else
+ parts = if node.unescaped.lines.one?
+ [builder.string_internal([node.unescaped, srange(node.content_loc)])]
+ else
+ start_offset = node.content_loc.start_offset
+
+ node.unescaped.lines.map do |line|
+ end_offset = start_offset + line.length
+ offsets = srange_offsets(start_offset, end_offset)
+ start_offset = end_offset
+
+ builder.string_internal([line, offsets])
+ end
+ end
+
+ builder.xstring_compose(
+ token(node.opening_loc),
+ parts,
+ token(node.closing_loc)
+ )
+ end
+ end
+
+ # yield
+ # ^^^^^
+ #
+ # yield 1
+ # ^^^^^^^
+ def visit_yield_node(node)
+ builder.keyword_cmd(
+ :yield,
+ token(node.keyword_loc),
+ token(node.lparen_loc),
+ visit(node.arguments) || [],
+ token(node.rparen_loc)
+ )
+ end
+
+ private
+
+ # Initialize a new compiler with the given option overrides, used to
+ # visit a subtree with the given options.
+ def copy_compiler(forwarding: self.forwarding, in_destructure: self.in_destructure, in_pattern: self.in_pattern)
+ Compiler.new(parser, offset_cache, forwarding: forwarding, in_destructure: in_destructure, in_pattern: in_pattern)
+ end
+
+ # When *, **, &, or ... are used as an argument in a method call, we
+ # check if they were allowed by the current context. To determine that
+ # we build this lookup table.
+ def find_forwarding(node)
+ return [] if node.nil?
+
+ forwarding = []
+ forwarding << :* if node.rest.is_a?(RestParameterNode) && node.rest.name.nil?
+ forwarding << :** if node.keyword_rest.is_a?(KeywordRestParameterNode) && node.keyword_rest.name.nil?
+ forwarding << :& if !node.block.nil? && node.block.name.nil?
+ forwarding |= [:&, :"..."] if node.keyword_rest.is_a?(ForwardingParameterNode)
+
+ forwarding
+ end
+
+ # Because we have mutated the AST to allow for newlines in the middle of
+ # a rational, we need to manually handle the value here.
+ def imaginary_value(node)
+ Complex(0, node.numeric.is_a?(RationalNode) ? rational_value(node.numeric) : node.numeric.value)
+ end
+
+ # Negate the value of a numeric node. This is a special case where you
+ # have a negative sign on one line and then a number on the next line.
+ # In normal Ruby, this will always be a method call. The parser gem,
+ # however, marks this as a numeric literal. We have to massage the tree
+ # here to get it into the correct form.
+ def numeric_negate(message_loc, receiver)
+ case receiver.type
+ when :integer_node, :float_node
+ receiver.copy(value: -receiver.value, location: message_loc.join(receiver.location))
+ when :rational_node, :imaginary_node
+ receiver.copy(numeric: numeric_negate(message_loc, receiver.numeric), location: message_loc.join(receiver.location))
+ end
+ end
+
+ # Blocks can have a special set of parameters that automatically expand
+ # when given arrays if they have a single required parameter and no
+ # other parameters.
+ def procarg0?(parameters)
+ parameters &&
+ parameters.requireds.length == 1 &&
+ parameters.optionals.empty? &&
+ parameters.rest.nil? &&
+ parameters.posts.empty? &&
+ parameters.keywords.empty? &&
+ parameters.keyword_rest.nil? &&
+ parameters.block.nil?
+ end
+
+ # Because we have mutated the AST to allow for newlines in the middle of
+ # a rational, we need to manually handle the value here.
+ def rational_value(node)
+ if node.numeric.is_a?(IntegerNode)
+ Rational(node.numeric.value)
+ else
+ Rational(node.slice.gsub(/\s/, "").chomp("r"))
+ end
+ end
+
+ # Locations in the parser gem AST are generated using this class. We
+ # store a reference to its constant to make it slightly faster to look
+ # up.
+ Range = ::Parser::Source::Range
+
+ # Constructs a new source range from the given start and end offsets.
+ def srange(location)
+ Range.new(source_buffer, offset_cache[location.start_offset], offset_cache[location.end_offset]) if location
+ end
+
+ # Constructs a new source range from the given start and end offsets.
+ def srange_offsets(start_offset, end_offset)
+ Range.new(source_buffer, offset_cache[start_offset], offset_cache[end_offset])
+ end
+
+ # Constructs a new source range by finding the given tokens between the
+ # given start offset and end offset. If the needle is not found, it
+ # returns nil. Importantly it does not search past newlines or comments.
+ #
+ # Note that end_offset is allowed to be nil, in which case this will
+ # search until the end of the string.
+ def srange_find(start_offset, end_offset, tokens)
+ if (match = source_buffer.source.byteslice(start_offset...end_offset).match(/(\s*)(#{tokens.join("|")})/))
+ _, whitespace, token = *match
+ token_offset = start_offset + whitespace.bytesize
+
+ [token, Range.new(source_buffer, offset_cache[token_offset], offset_cache[token_offset + token.bytesize])]
+ end
+ end
+
+ # Transform a location into a token that the parser gem expects.
+ def token(location)
+ [location.slice, Range.new(source_buffer, offset_cache[location.start_offset], offset_cache[location.end_offset])] if location
+ end
+
+ # Visit a block node on a call.
+ def visit_block(call, block)
+ if block
+ parameters = block.parameters
+ implicit_parameters = parameters.is_a?(NumberedParametersNode) || parameters.is_a?(ItParametersNode)
+
+ builder.block(
+ call,
+ token(block.opening_loc),
+ if parameters.nil?
+ builder.args(nil, [], nil, false)
+ elsif implicit_parameters
+ visit(parameters)
+ else
+ builder.args(
+ token(parameters.opening_loc),
+ if procarg0?(parameters.parameters)
+ parameter = parameters.parameters.requireds.first
+ [builder.procarg0(visit(parameter))].concat(visit_all(parameters.locals))
+ else
+ visit(parameters)
+ end,
+ token(parameters.closing_loc),
+ false
+ )
+ end,
+ block.body&.accept(copy_compiler(forwarding: implicit_parameters ? [] : find_forwarding(parameters&.parameters))),
+ token(block.closing_loc)
+ )
+ else
+ call
+ end
+ end
+
+ # Visit a heredoc that can be either a string or an xstring.
+ def visit_heredoc(node)
+ children = Array.new
+ node.parts.each do |part|
+ pushing =
+ if part.is_a?(StringNode) && part.unescaped.include?("\n")
+ unescaped = part.unescaped.lines(chomp: true)
+ escaped = part.content.lines(chomp: true)
+
+ escaped_lengths =
+ if node.opening.end_with?("'")
+ escaped.map { |line| line.bytesize + 1 }
+ else
+ escaped.chunk_while { |before, after| before.match?(/(?<!\\)\\$/) }.map { |line| line.join.bytesize + line.length }
+ end
+
+ start_offset = part.location.start_offset
+ end_offset = nil
+
+ unescaped.zip(escaped_lengths).map do |unescaped_line, escaped_length|
+ end_offset = start_offset + (escaped_length || 0)
+ inner_part = builder.string_internal(["#{unescaped_line}\n", srange_offsets(start_offset, end_offset)])
+ start_offset = end_offset
+ inner_part
+ end
+ else
+ [visit(part)]
+ end
+
+ pushing.each do |child|
+ if child.type == :str && child.children.last == ""
+ # nothing
+ elsif child.type == :str && children.last && children.last.type == :str && !children.last.children.first.end_with?("\n")
+ children.last.children.first << child.children.first
+ else
+ children << child
+ end
+ end
+ end
+
+ closing = node.closing
+ closing_t = [closing.chomp, srange_offsets(node.closing_loc.start_offset, node.closing_loc.end_offset - (closing[/\s+$/]&.length || 0))]
+
+ [children, closing_t]
+ end
+
+ # Visit a numeric node and account for the optional sign.
+ def visit_numeric(node, value)
+ if (slice = node.slice).match?(/^[+-]/)
+ builder.unary_num(
+ [slice[0].to_sym, srange_offsets(node.location.start_offset, node.location.start_offset + 1)],
+ value
+ )
+ else
+ value
+ end
+ end
+
+ # Within the given block, track that we're within a pattern.
+ def within_pattern
+ begin
+ parser.pattern_variables.push
+ yield copy_compiler(in_pattern: true)
+ ensure
+ parser.pattern_variables.pop
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/prism/translation/parser/lexer.rb b/lib/prism/translation/parser/lexer.rb
new file mode 100644
index 0000000000..9d7caae0ba
--- /dev/null
+++ b/lib/prism/translation/parser/lexer.rb
@@ -0,0 +1,416 @@
+# frozen_string_literal: true
+
+module Prism
+ module Translation
+ class Parser
+ # Accepts a list of prism tokens and converts them into the expected
+ # format for the parser gem.
+ class Lexer
+ # The direct translating of types between the two lexers.
+ TYPES = {
+ # These tokens should never appear in the output of the lexer.
+ EOF: nil,
+ MISSING: nil,
+ NOT_PROVIDED: nil,
+ IGNORED_NEWLINE: nil,
+ EMBDOC_END: nil,
+ EMBDOC_LINE: nil,
+ __END__: nil,
+
+ # These tokens have more or less direct mappings.
+ AMPERSAND: :tAMPER2,
+ AMPERSAND_AMPERSAND: :tANDOP,
+ AMPERSAND_AMPERSAND_EQUAL: :tOP_ASGN,
+ AMPERSAND_DOT: :tANDDOT,
+ AMPERSAND_EQUAL: :tOP_ASGN,
+ BACK_REFERENCE: :tBACK_REF,
+ BACKTICK: :tXSTRING_BEG,
+ BANG: :tBANG,
+ BANG_EQUAL: :tNEQ,
+ BANG_TILDE: :tNMATCH,
+ BRACE_LEFT: :tLCURLY,
+ BRACE_RIGHT: :tRCURLY,
+ BRACKET_LEFT: :tLBRACK2,
+ BRACKET_LEFT_ARRAY: :tLBRACK,
+ BRACKET_LEFT_RIGHT: :tAREF,
+ BRACKET_LEFT_RIGHT_EQUAL: :tASET,
+ BRACKET_RIGHT: :tRBRACK,
+ CARET: :tCARET,
+ CARET_EQUAL: :tOP_ASGN,
+ CHARACTER_LITERAL: :tCHARACTER,
+ CLASS_VARIABLE: :tCVAR,
+ COLON: :tCOLON,
+ COLON_COLON: :tCOLON2,
+ COMMA: :tCOMMA,
+ COMMENT: :tCOMMENT,
+ CONSTANT: :tCONSTANT,
+ DOT: :tDOT,
+ DOT_DOT: :tDOT2,
+ DOT_DOT_DOT: :tDOT3,
+ EMBDOC_BEGIN: :tCOMMENT,
+ EMBEXPR_BEGIN: :tSTRING_DBEG,
+ EMBEXPR_END: :tSTRING_DEND,
+ EMBVAR: :tSTRING_DVAR,
+ EQUAL: :tEQL,
+ EQUAL_EQUAL: :tEQ,
+ EQUAL_EQUAL_EQUAL: :tEQQ,
+ EQUAL_GREATER: :tASSOC,
+ EQUAL_TILDE: :tMATCH,
+ FLOAT: :tFLOAT,
+ FLOAT_IMAGINARY: :tIMAGINARY,
+ FLOAT_RATIONAL: :tRATIONAL,
+ FLOAT_RATIONAL_IMAGINARY: :tIMAGINARY,
+ GLOBAL_VARIABLE: :tGVAR,
+ GREATER: :tGT,
+ GREATER_EQUAL: :tGEQ,
+ GREATER_GREATER: :tRSHFT,
+ GREATER_GREATER_EQUAL: :tOP_ASGN,
+ HEREDOC_START: :tSTRING_BEG,
+ HEREDOC_END: :tSTRING_END,
+ IDENTIFIER: :tIDENTIFIER,
+ INSTANCE_VARIABLE: :tIVAR,
+ INTEGER: :tINTEGER,
+ INTEGER_IMAGINARY: :tIMAGINARY,
+ INTEGER_RATIONAL: :tRATIONAL,
+ INTEGER_RATIONAL_IMAGINARY: :tIMAGINARY,
+ KEYWORD_ALIAS: :kALIAS,
+ KEYWORD_AND: :kAND,
+ KEYWORD_BEGIN: :kBEGIN,
+ KEYWORD_BEGIN_UPCASE: :klBEGIN,
+ KEYWORD_BREAK: :kBREAK,
+ KEYWORD_CASE: :kCASE,
+ KEYWORD_CLASS: :kCLASS,
+ KEYWORD_DEF: :kDEF,
+ KEYWORD_DEFINED: :kDEFINED,
+ KEYWORD_DO: :kDO,
+ KEYWORD_DO_LOOP: :kDO_COND,
+ KEYWORD_END: :kEND,
+ KEYWORD_END_UPCASE: :klEND,
+ KEYWORD_ENSURE: :kENSURE,
+ KEYWORD_ELSE: :kELSE,
+ KEYWORD_ELSIF: :kELSIF,
+ KEYWORD_FALSE: :kFALSE,
+ KEYWORD_FOR: :kFOR,
+ KEYWORD_IF: :kIF,
+ KEYWORD_IF_MODIFIER: :kIF_MOD,
+ KEYWORD_IN: :kIN,
+ KEYWORD_MODULE: :kMODULE,
+ KEYWORD_NEXT: :kNEXT,
+ KEYWORD_NIL: :kNIL,
+ KEYWORD_NOT: :kNOT,
+ KEYWORD_OR: :kOR,
+ KEYWORD_REDO: :kREDO,
+ KEYWORD_RESCUE: :kRESCUE,
+ KEYWORD_RESCUE_MODIFIER: :kRESCUE_MOD,
+ KEYWORD_RETRY: :kRETRY,
+ KEYWORD_RETURN: :kRETURN,
+ KEYWORD_SELF: :kSELF,
+ KEYWORD_SUPER: :kSUPER,
+ KEYWORD_THEN: :kTHEN,
+ KEYWORD_TRUE: :kTRUE,
+ KEYWORD_UNDEF: :kUNDEF,
+ KEYWORD_UNLESS: :kUNLESS,
+ KEYWORD_UNLESS_MODIFIER: :kUNLESS_MOD,
+ KEYWORD_UNTIL: :kUNTIL,
+ KEYWORD_UNTIL_MODIFIER: :kUNTIL_MOD,
+ KEYWORD_WHEN: :kWHEN,
+ KEYWORD_WHILE: :kWHILE,
+ KEYWORD_WHILE_MODIFIER: :kWHILE_MOD,
+ KEYWORD_YIELD: :kYIELD,
+ KEYWORD___ENCODING__: :k__ENCODING__,
+ KEYWORD___FILE__: :k__FILE__,
+ KEYWORD___LINE__: :k__LINE__,
+ LABEL: :tLABEL,
+ LABEL_END: :tLABEL_END,
+ LAMBDA_BEGIN: :tLAMBEG,
+ LESS: :tLT,
+ LESS_EQUAL: :tLEQ,
+ LESS_EQUAL_GREATER: :tCMP,
+ LESS_LESS: :tLSHFT,
+ LESS_LESS_EQUAL: :tOP_ASGN,
+ METHOD_NAME: :tFID,
+ MINUS: :tMINUS,
+ MINUS_EQUAL: :tOP_ASGN,
+ MINUS_GREATER: :tLAMBDA,
+ NEWLINE: :tNL,
+ NUMBERED_REFERENCE: :tNTH_REF,
+ PARENTHESIS_LEFT: :tLPAREN,
+ PARENTHESIS_LEFT_PARENTHESES: :tLPAREN_ARG,
+ PARENTHESIS_RIGHT: :tRPAREN,
+ PERCENT: :tPERCENT,
+ PERCENT_EQUAL: :tOP_ASGN,
+ PERCENT_LOWER_I: :tQSYMBOLS_BEG,
+ PERCENT_LOWER_W: :tQWORDS_BEG,
+ PERCENT_UPPER_I: :tSYMBOLS_BEG,
+ PERCENT_UPPER_W: :tWORDS_BEG,
+ PERCENT_LOWER_X: :tXSTRING_BEG,
+ PLUS: :tPLUS,
+ PLUS_EQUAL: :tOP_ASGN,
+ PIPE_EQUAL: :tOP_ASGN,
+ PIPE: :tPIPE,
+ PIPE_PIPE: :tOROP,
+ PIPE_PIPE_EQUAL: :tOP_ASGN,
+ QUESTION_MARK: :tEH,
+ REGEXP_BEGIN: :tREGEXP_BEG,
+ REGEXP_END: :tSTRING_END,
+ SEMICOLON: :tSEMI,
+ SLASH: :tDIVIDE,
+ SLASH_EQUAL: :tOP_ASGN,
+ STAR: :tSTAR2,
+ STAR_EQUAL: :tOP_ASGN,
+ STAR_STAR: :tPOW,
+ STAR_STAR_EQUAL: :tOP_ASGN,
+ STRING_BEGIN: :tSTRING_BEG,
+ STRING_CONTENT: :tSTRING_CONTENT,
+ STRING_END: :tSTRING_END,
+ SYMBOL_BEGIN: :tSYMBEG,
+ TILDE: :tTILDE,
+ UAMPERSAND: :tAMPER,
+ UCOLON_COLON: :tCOLON3,
+ UDOT_DOT: :tBDOT2,
+ UDOT_DOT_DOT: :tBDOT3,
+ UMINUS: :tUMINUS,
+ UMINUS_NUM: :tUNARY_NUM,
+ UPLUS: :tUPLUS,
+ USTAR: :tSTAR,
+ USTAR_STAR: :tPOW,
+ WORDS_SEP: :tSPACE
+ }
+
+ # These constants represent flags in our lex state. We really, really
+ # don't want to be using them and we really, really don't want to be
+ # exposing them as part of our public API. Unfortunately, we don't have
+ # another way of matching the exact tokens that the parser gem expects
+ # without them. We should find another way to do this, but in the
+ # meantime we'll hide them from the documentation and mark them as
+ # private constants.
+ EXPR_BEG = 0x1 # :nodoc:
+ EXPR_LABEL = 0x400 # :nodoc:
+
+ private_constant :TYPES, :EXPR_BEG, :EXPR_LABEL
+
+ # The Parser::Source::Buffer that the tokens were lexed from.
+ attr_reader :source_buffer
+
+ # An array of tuples that contain prism tokens and their associated lex
+ # state when they were lexed.
+ attr_reader :lexed
+
+ # A hash that maps offsets in bytes to offsets in characters.
+ attr_reader :offset_cache
+
+ # Initialize the lexer with the given source buffer, prism tokens, and
+ # offset cache.
+ def initialize(source_buffer, lexed, offset_cache)
+ @source_buffer = source_buffer
+ @lexed = lexed
+ @offset_cache = offset_cache
+ end
+
+ Range = ::Parser::Source::Range # :nodoc:
+ private_constant :Range
+
+ # Convert the prism tokens into the expected format for the parser gem.
+ def to_a
+ tokens = []
+
+ index = 0
+ length = lexed.length
+
+ heredoc_identifier_stack = []
+
+ while index < length
+ token, state = lexed[index]
+ index += 1
+ next if %i[IGNORED_NEWLINE __END__ EOF].include?(token.type)
+
+ type = TYPES.fetch(token.type)
+ value = token.value
+ location = Range.new(source_buffer, offset_cache[token.location.start_offset], offset_cache[token.location.end_offset])
+
+ case type
+ when :tCHARACTER
+ value.delete_prefix!("?")
+ when :tCOMMENT
+ if token.type == :EMBDOC_BEGIN
+ start_index = index
+
+ while !((next_token = lexed[index][0]) && next_token.type == :EMBDOC_END) && (index < length - 1)
+ value += next_token.value
+ index += 1
+ end
+
+ if start_index != index
+ value += next_token.value
+ location = Range.new(source_buffer, offset_cache[token.location.start_offset], offset_cache[lexed[index][0].location.end_offset])
+ index += 1
+ end
+ else
+ value.chomp!
+ location = Range.new(source_buffer, offset_cache[token.location.start_offset], offset_cache[token.location.end_offset - 1])
+ end
+ when :tNL
+ value = nil
+ when :tFLOAT
+ value = parse_float(value)
+ when :tIMAGINARY
+ value = parse_complex(value)
+ when :tINTEGER
+ if value.start_with?("+")
+ tokens << [:tUNARY_NUM, ["+", Range.new(source_buffer, offset_cache[token.location.start_offset], offset_cache[token.location.start_offset + 1])]]
+ location = Range.new(source_buffer, offset_cache[token.location.start_offset + 1], offset_cache[token.location.end_offset])
+ end
+
+ value = parse_integer(value)
+ when :tLABEL
+ value.chomp!(":")
+ when :tLABEL_END
+ value.chomp!(":")
+ when :tLCURLY
+ type = :tLBRACE if state == EXPR_BEG | EXPR_LABEL
+ when :tNTH_REF
+ value = parse_integer(value.delete_prefix("$"))
+ when :tOP_ASGN
+ value.chomp!("=")
+ when :tRATIONAL
+ value = parse_rational(value)
+ when :tSPACE
+ value = nil
+ when :tSTRING_BEG
+ if token.type == :HEREDOC_START
+ heredoc_identifier_stack.push(value.match(/<<[-~]?["'`]?(?<heredoc_identifier>.*?)["'`]?\z/)[:heredoc_identifier])
+ end
+ if ["\"", "'"].include?(value) && (next_token = lexed[index][0]) && next_token.type == :STRING_END
+ next_location = token.location.join(next_token.location)
+ type = :tSTRING
+ value = ""
+ location = Range.new(source_buffer, offset_cache[next_location.start_offset], offset_cache[next_location.end_offset])
+ index += 1
+ elsif ["\"", "'"].include?(value) && (next_token = lexed[index][0]) && next_token.type == :STRING_CONTENT && next_token.value.lines.count <= 1 && (next_next_token = lexed[index + 1][0]) && next_next_token.type == :STRING_END
+ next_location = token.location.join(next_next_token.location)
+ type = :tSTRING
+ value = next_token.value.gsub("\\\\", "\\")
+ location = Range.new(source_buffer, offset_cache[next_location.start_offset], offset_cache[next_location.end_offset])
+ index += 2
+ elsif value.start_with?("<<")
+ quote = value[2] == "-" || value[2] == "~" ? value[3] : value[2]
+ if quote == "`"
+ type = :tXSTRING_BEG
+ value = "<<`"
+ else
+ value = "<<#{quote == "'" || quote == "\"" ? quote : "\""}"
+ end
+ end
+ when :tSTRING_CONTENT
+ unless (lines = token.value.lines).one?
+ start_offset = offset_cache[token.location.start_offset]
+ lines.map do |line|
+ newline = line.end_with?("\r\n") ? "\r\n" : "\n"
+ chomped_line = line.chomp
+ if match = chomped_line.match(/(?<backslashes>\\+)\z/)
+ adjustment = match[:backslashes].size / 2
+ adjusted_line = chomped_line.delete_suffix("\\" * adjustment)
+ if match[:backslashes].size.odd?
+ adjusted_line.delete_suffix!("\\")
+ adjustment += 2
+ else
+ adjusted_line << newline
+ end
+ else
+ adjusted_line = line
+ adjustment = 0
+ end
+
+ end_offset = start_offset + adjusted_line.length + adjustment
+ tokens << [:tSTRING_CONTENT, [adjusted_line, Range.new(source_buffer, offset_cache[start_offset], offset_cache[end_offset])]]
+ start_offset = end_offset
+ end
+ next
+ end
+ when :tSTRING_DVAR
+ value = nil
+ when :tSTRING_END
+ if token.type == :HEREDOC_END && value.end_with?("\n")
+ newline_length = value.end_with?("\r\n") ? 2 : 1
+ value = heredoc_identifier_stack.pop
+ location = Range.new(source_buffer, offset_cache[token.location.start_offset], offset_cache[token.location.end_offset - newline_length])
+ elsif token.type == :REGEXP_END
+ value = value[0]
+ location = Range.new(source_buffer, offset_cache[token.location.start_offset], offset_cache[token.location.start_offset + 1])
+ end
+ when :tSYMBEG
+ if (next_token = lexed[index][0]) && next_token.type != :STRING_CONTENT && next_token.type != :EMBEXPR_BEGIN && next_token.type != :EMBVAR
+ next_location = token.location.join(next_token.location)
+ type = :tSYMBOL
+ value = next_token.value
+ value = { "~@" => "~", "!@" => "!" }.fetch(value, value)
+ location = Range.new(source_buffer, offset_cache[next_location.start_offset], offset_cache[next_location.end_offset])
+ index += 1
+ end
+ when :tFID
+ if !tokens.empty? && tokens.dig(-1, 0) == :kDEF
+ type = :tIDENTIFIER
+ end
+ when :tXSTRING_BEG
+ if (next_token = lexed[index][0]) && next_token.type != :STRING_CONTENT && next_token.type != :STRING_END
+ type = :tBACK_REF2
+ end
+ end
+
+ tokens << [type, [value, location]]
+
+ if token.type == :REGEXP_END
+ tokens << [:tREGEXP_OPT, [token.value[1..], Range.new(source_buffer, offset_cache[token.location.start_offset + 1], offset_cache[token.location.end_offset])]]
+ end
+ end
+
+ tokens
+ end
+
+ private
+
+ # Parse an integer from the string representation.
+ def parse_integer(value)
+ Integer(value)
+ rescue ArgumentError
+ 0
+ end
+
+ # Parse a float from the string representation.
+ def parse_float(value)
+ Float(value)
+ rescue ArgumentError
+ 0.0
+ end
+
+ # Parse a complex from the string representation.
+ def parse_complex(value)
+ value.chomp!("i")
+
+ if value.end_with?("r")
+ Complex(0, parse_rational(value))
+ elsif value.start_with?(/0[BbOoDdXx]/)
+ Complex(0, parse_integer(value))
+ else
+ Complex(0, value)
+ end
+ rescue ArgumentError
+ 0i
+ end
+
+ # Parse a rational from the string representation.
+ def parse_rational(value)
+ value.chomp!("r")
+
+ if value.start_with?(/0[BbOoDdXx]/)
+ Rational(parse_integer(value))
+ else
+ Rational(value)
+ end
+ rescue ArgumentError
+ 0r
+ end
+ end
+ end
+ end
+end
diff --git a/lib/prism/translation/parser/rubocop.rb b/lib/prism/translation/parser/rubocop.rb
new file mode 100644
index 0000000000..6c9687a5cc
--- /dev/null
+++ b/lib/prism/translation/parser/rubocop.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+# typed: ignore
+
+warn "WARN: Prism is directly supported since RuboCop 1.62. The `prism/translation/parser/rubocop` file is deprecated."
+
+require "parser"
+require "rubocop"
+
+require_relative "../../prism"
+require_relative "../parser"
+
+module Prism
+ module Translation
+ class Parser
+ # This is the special version numbers that should be used in RuboCop
+ # configuration files to trigger using prism.
+
+ # For Ruby 3.3
+ VERSION_3_3 = 80_82_73_83_77.33
+
+ # For Ruby 3.4
+ VERSION_3_4 = 80_82_73_83_77.34
+
+ # This module gets prepended into RuboCop::AST::ProcessedSource.
+ module ProcessedSource
+ # This condition is compatible with rubocop-ast versions up to 1.30.0.
+ if RuboCop::AST::ProcessedSource.instance_method(:parser_class).arity == 1
+ # Redefine parser_class so that we can inject the prism parser into the
+ # list of known parsers.
+ def parser_class(ruby_version)
+ if ruby_version == Prism::Translation::Parser::VERSION_3_3
+ warn "WARN: Setting `TargetRubyVersion: 80_82_73_83_77.33` is deprecated. " \
+ "Set to `ParserEngine: parser_prism` and `TargetRubyVersion: 3.3` instead."
+ require_relative "../parser33"
+ Prism::Translation::Parser33
+ elsif ruby_version == Prism::Translation::Parser::VERSION_3_4
+ warn "WARN: Setting `TargetRubyVersion: 80_82_73_83_77.34` is deprecated. " \
+ "Set to `ParserEngine: parser_prism` and `TargetRubyVersion: 3.4` instead."
+ require_relative "../parser34"
+ Prism::Translation::Parser34
+ else
+ super
+ end
+ end
+ else
+ # Redefine parser_class so that we can inject the prism parser into the
+ # list of known parsers.
+ def parser_class(ruby_version, _parser_engine)
+ if ruby_version == Prism::Translation::Parser::VERSION_3_3
+ warn "WARN: Setting `TargetRubyVersion: 80_82_73_83_77.33` is deprecated. " \
+ "Set to `ParserEngine: parser_prism` and `TargetRubyVersion: 3.3` instead."
+ require_relative "../parser33"
+ Prism::Translation::Parser33
+ elsif ruby_version == Prism::Translation::Parser::VERSION_3_4
+ warn "WARN: Setting `TargetRubyVersion: 80_82_73_83_77.34` is deprecated. " \
+ "Set to `ParserEngine: parser_prism` and `TargetRubyVersion: 3.4` instead."
+ require_relative "../parser34"
+ Prism::Translation::Parser34
+ else
+ super
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+# :stopdoc:
+RuboCop::AST::ProcessedSource.prepend(Prism::Translation::Parser::ProcessedSource)
+known_rubies = RuboCop::TargetRuby.const_get(:KNOWN_RUBIES)
+RuboCop::TargetRuby.send(:remove_const, :KNOWN_RUBIES)
+RuboCop::TargetRuby::KNOWN_RUBIES = [*known_rubies, Prism::Translation::Parser::VERSION_3_3].freeze
diff --git a/lib/prism/translation/parser33.rb b/lib/prism/translation/parser33.rb
new file mode 100644
index 0000000000..b09266e06a
--- /dev/null
+++ b/lib/prism/translation/parser33.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Prism
+ module Translation
+ # This class is the entry-point for Ruby 3.3 of `Prism::Translation::Parser`.
+ class Parser33 < Parser
+ def version # :nodoc:
+ 33
+ end
+ end
+ end
+end
diff --git a/lib/prism/translation/parser34.rb b/lib/prism/translation/parser34.rb
new file mode 100644
index 0000000000..0ead70ad3c
--- /dev/null
+++ b/lib/prism/translation/parser34.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Prism
+ module Translation
+ # This class is the entry-point for Ruby 3.4 of `Prism::Translation::Parser`.
+ class Parser34 < Parser
+ def version # :nodoc:
+ 34
+ end
+ end
+ end
+end
diff --git a/lib/prism/translation/ripper.rb b/lib/prism/translation/ripper.rb
new file mode 100644
index 0000000000..2c5e4569c2
--- /dev/null
+++ b/lib/prism/translation/ripper.rb
@@ -0,0 +1,3446 @@
+# frozen_string_literal: true
+
+require "ripper"
+
+module Prism
+ module Translation
+ # This class provides a compatibility layer between prism and Ripper. It
+ # functions by parsing the entire tree first and then walking it and
+ # executing each of the Ripper callbacks as it goes. To use this class, you
+ # treat `Prism::Translation::Ripper` effectively as you would treat the
+ # `Ripper` class.
+ #
+ # Note that this class will serve the most common use cases, but Ripper's
+ # API is extensive and undocumented. It relies on reporting the state of the
+ # parser at any given time. We do our best to replicate that here, but
+ # because it is a different architecture it is not possible to perfectly
+ # replicate the behavior of Ripper.
+ #
+ # The main known difference is that we may omit dispatching some events in
+ # some cases. This impacts the following events:
+ #
+ # - on_assign_error
+ # - on_comma
+ # - on_ignored_nl
+ # - on_ignored_sp
+ # - on_kw
+ # - on_label_end
+ # - on_lbrace
+ # - on_lbracket
+ # - on_lparen
+ # - on_nl
+ # - on_op
+ # - on_operator_ambiguous
+ # - on_rbrace
+ # - on_rbracket
+ # - on_rparen
+ # - on_semicolon
+ # - on_sp
+ # - on_symbeg
+ # - on_tstring_beg
+ # - on_tstring_end
+ #
+ class Ripper < Compiler
+ # Parses the given Ruby program read from +src+.
+ # +src+ must be a String or an IO or a object with a #gets method.
+ def self.parse(src, filename = "(ripper)", lineno = 1)
+ new(src, filename, lineno).parse
+ end
+
+ # Tokenizes the Ruby program and returns an array of an array,
+ # which is formatted like
+ # <code>[[lineno, column], type, token, state]</code>.
+ # The +filename+ argument is mostly ignored.
+ # By default, this method does not handle syntax errors in +src+,
+ # use the +raise_errors+ keyword to raise a SyntaxError for an error in +src+.
+ #
+ # require "ripper"
+ # require "pp"
+ #
+ # pp Ripper.lex("def m(a) nil end")
+ # #=> [[[1, 0], :on_kw, "def", FNAME ],
+ # [[1, 3], :on_sp, " ", FNAME ],
+ # [[1, 4], :on_ident, "m", ENDFN ],
+ # [[1, 5], :on_lparen, "(", BEG|LABEL],
+ # [[1, 6], :on_ident, "a", ARG ],
+ # [[1, 7], :on_rparen, ")", ENDFN ],
+ # [[1, 8], :on_sp, " ", BEG ],
+ # [[1, 9], :on_kw, "nil", END ],
+ # [[1, 12], :on_sp, " ", END ],
+ # [[1, 13], :on_kw, "end", END ]]
+ #
+ def self.lex(src, filename = "-", lineno = 1, raise_errors: false)
+ result = Prism.lex_compat(src, filepath: filename, line: lineno)
+
+ if result.failure? && raise_errors
+ raise SyntaxError, result.errors.first.message
+ else
+ result.value
+ end
+ end
+
+ # This contains a table of all of the parser events and their
+ # corresponding arity.
+ PARSER_EVENT_TABLE = {
+ BEGIN: 1,
+ END: 1,
+ alias: 2,
+ alias_error: 2,
+ aref: 2,
+ aref_field: 2,
+ arg_ambiguous: 1,
+ arg_paren: 1,
+ args_add: 2,
+ args_add_block: 2,
+ args_add_star: 2,
+ args_forward: 0,
+ args_new: 0,
+ array: 1,
+ aryptn: 4,
+ assign: 2,
+ assign_error: 2,
+ assoc_new: 2,
+ assoc_splat: 1,
+ assoclist_from_args: 1,
+ bare_assoc_hash: 1,
+ begin: 1,
+ binary: 3,
+ block_var: 2,
+ blockarg: 1,
+ bodystmt: 4,
+ brace_block: 2,
+ break: 1,
+ call: 3,
+ case: 2,
+ class: 3,
+ class_name_error: 2,
+ command: 2,
+ command_call: 4,
+ const_path_field: 2,
+ const_path_ref: 2,
+ const_ref: 1,
+ def: 3,
+ defined: 1,
+ defs: 5,
+ do_block: 2,
+ dot2: 2,
+ dot3: 2,
+ dyna_symbol: 1,
+ else: 1,
+ elsif: 3,
+ ensure: 1,
+ excessed_comma: 0,
+ fcall: 1,
+ field: 3,
+ fndptn: 4,
+ for: 3,
+ hash: 1,
+ heredoc_dedent: 2,
+ hshptn: 3,
+ if: 3,
+ if_mod: 2,
+ ifop: 3,
+ in: 3,
+ kwrest_param: 1,
+ lambda: 2,
+ magic_comment: 2,
+ massign: 2,
+ method_add_arg: 2,
+ method_add_block: 2,
+ mlhs_add: 2,
+ mlhs_add_post: 2,
+ mlhs_add_star: 2,
+ mlhs_new: 0,
+ mlhs_paren: 1,
+ module: 2,
+ mrhs_add: 2,
+ mrhs_add_star: 2,
+ mrhs_new: 0,
+ mrhs_new_from_args: 1,
+ next: 1,
+ nokw_param: 1,
+ opassign: 3,
+ operator_ambiguous: 2,
+ param_error: 2,
+ params: 7,
+ paren: 1,
+ parse_error: 1,
+ program: 1,
+ qsymbols_add: 2,
+ qsymbols_new: 0,
+ qwords_add: 2,
+ qwords_new: 0,
+ redo: 0,
+ regexp_add: 2,
+ regexp_literal: 2,
+ regexp_new: 0,
+ rescue: 4,
+ rescue_mod: 2,
+ rest_param: 1,
+ retry: 0,
+ return: 1,
+ return0: 0,
+ sclass: 2,
+ stmts_add: 2,
+ stmts_new: 0,
+ string_add: 2,
+ string_concat: 2,
+ string_content: 0,
+ string_dvar: 1,
+ string_embexpr: 1,
+ string_literal: 1,
+ super: 1,
+ symbol: 1,
+ symbol_literal: 1,
+ symbols_add: 2,
+ symbols_new: 0,
+ top_const_field: 1,
+ top_const_ref: 1,
+ unary: 2,
+ undef: 1,
+ unless: 3,
+ unless_mod: 2,
+ until: 2,
+ until_mod: 2,
+ var_alias: 2,
+ var_field: 1,
+ var_ref: 1,
+ vcall: 1,
+ void_stmt: 0,
+ when: 3,
+ while: 2,
+ while_mod: 2,
+ word_add: 2,
+ word_new: 0,
+ words_add: 2,
+ words_new: 0,
+ xstring_add: 2,
+ xstring_literal: 1,
+ xstring_new: 0,
+ yield: 1,
+ yield0: 0,
+ zsuper: 0
+ }
+
+ # This contains a table of all of the scanner events and their
+ # corresponding arity.
+ SCANNER_EVENT_TABLE = {
+ CHAR: 1,
+ __end__: 1,
+ backref: 1,
+ backtick: 1,
+ comma: 1,
+ comment: 1,
+ const: 1,
+ cvar: 1,
+ embdoc: 1,
+ embdoc_beg: 1,
+ embdoc_end: 1,
+ embexpr_beg: 1,
+ embexpr_end: 1,
+ embvar: 1,
+ float: 1,
+ gvar: 1,
+ heredoc_beg: 1,
+ heredoc_end: 1,
+ ident: 1,
+ ignored_nl: 1,
+ imaginary: 1,
+ int: 1,
+ ivar: 1,
+ kw: 1,
+ label: 1,
+ label_end: 1,
+ lbrace: 1,
+ lbracket: 1,
+ lparen: 1,
+ nl: 1,
+ op: 1,
+ period: 1,
+ qsymbols_beg: 1,
+ qwords_beg: 1,
+ rational: 1,
+ rbrace: 1,
+ rbracket: 1,
+ regexp_beg: 1,
+ regexp_end: 1,
+ rparen: 1,
+ semicolon: 1,
+ sp: 1,
+ symbeg: 1,
+ symbols_beg: 1,
+ tlambda: 1,
+ tlambeg: 1,
+ tstring_beg: 1,
+ tstring_content: 1,
+ tstring_end: 1,
+ words_beg: 1,
+ words_sep: 1,
+ ignored_sp: 1
+ }
+
+ # This array contains name of parser events.
+ PARSER_EVENTS = PARSER_EVENT_TABLE.keys
+
+ # This array contains name of scanner events.
+ SCANNER_EVENTS = SCANNER_EVENT_TABLE.keys
+
+ # This array contains name of all ripper events.
+ EVENTS = PARSER_EVENTS + SCANNER_EVENTS
+
+ # A list of all of the Ruby keywords.
+ KEYWORDS = [
+ "alias",
+ "and",
+ "begin",
+ "BEGIN",
+ "break",
+ "case",
+ "class",
+ "def",
+ "defined?",
+ "do",
+ "else",
+ "elsif",
+ "end",
+ "END",
+ "ensure",
+ "false",
+ "for",
+ "if",
+ "in",
+ "module",
+ "next",
+ "nil",
+ "not",
+ "or",
+ "redo",
+ "rescue",
+ "retry",
+ "return",
+ "self",
+ "super",
+ "then",
+ "true",
+ "undef",
+ "unless",
+ "until",
+ "when",
+ "while",
+ "yield",
+ "__ENCODING__",
+ "__FILE__",
+ "__LINE__"
+ ]
+
+ # A list of all of the Ruby binary operators.
+ BINARY_OPERATORS = [
+ :!=,
+ :!~,
+ :=~,
+ :==,
+ :===,
+ :<=>,
+ :>,
+ :>=,
+ :<,
+ :<=,
+ :&,
+ :|,
+ :^,
+ :>>,
+ :<<,
+ :-,
+ :+,
+ :%,
+ :/,
+ :*,
+ :**
+ ]
+
+ private_constant :KEYWORDS, :BINARY_OPERATORS
+
+ # Parses +src+ and create S-exp tree.
+ # Returns more readable tree rather than Ripper.sexp_raw.
+ # This method is mainly for developer use.
+ # The +filename+ argument is mostly ignored.
+ # By default, this method does not handle syntax errors in +src+,
+ # returning +nil+ in such cases. Use the +raise_errors+ keyword
+ # to raise a SyntaxError for an error in +src+.
+ #
+ # require "ripper"
+ # require "pp"
+ #
+ # pp Ripper.sexp("def m(a) nil end")
+ # #=> [:program,
+ # [[:def,
+ # [:@ident, "m", [1, 4]],
+ # [:paren, [:params, [[:@ident, "a", [1, 6]]], nil, nil, nil, nil, nil, nil]],
+ # [:bodystmt, [[:var_ref, [:@kw, "nil", [1, 9]]]], nil, nil, nil]]]]
+ #
+ def self.sexp(src, filename = "-", lineno = 1, raise_errors: false)
+ builder = SexpBuilderPP.new(src, filename, lineno)
+ sexp = builder.parse
+ if builder.error?
+ if raise_errors
+ raise SyntaxError, builder.error
+ end
+ else
+ sexp
+ end
+ end
+
+ # Parses +src+ and create S-exp tree.
+ # This method is mainly for developer use.
+ # The +filename+ argument is mostly ignored.
+ # By default, this method does not handle syntax errors in +src+,
+ # returning +nil+ in such cases. Use the +raise_errors+ keyword
+ # to raise a SyntaxError for an error in +src+.
+ #
+ # require "ripper"
+ # require "pp"
+ #
+ # pp Ripper.sexp_raw("def m(a) nil end")
+ # #=> [:program,
+ # [:stmts_add,
+ # [:stmts_new],
+ # [:def,
+ # [:@ident, "m", [1, 4]],
+ # [:paren, [:params, [[:@ident, "a", [1, 6]]], nil, nil, nil]],
+ # [:bodystmt,
+ # [:stmts_add, [:stmts_new], [:var_ref, [:@kw, "nil", [1, 9]]]],
+ # nil,
+ # nil,
+ # nil]]]]
+ #
+ def self.sexp_raw(src, filename = "-", lineno = 1, raise_errors: false)
+ builder = SexpBuilder.new(src, filename, lineno)
+ sexp = builder.parse
+ if builder.error?
+ if raise_errors
+ raise SyntaxError, builder.error
+ end
+ else
+ sexp
+ end
+ end
+
+ autoload :SexpBuilder, "prism/translation/ripper/sexp"
+ autoload :SexpBuilderPP, "prism/translation/ripper/sexp"
+
+ # The source that is being parsed.
+ attr_reader :source
+
+ # The filename of the source being parsed.
+ attr_reader :filename
+
+ # The current line number of the parser.
+ attr_reader :lineno
+
+ # The current column number of the parser.
+ attr_reader :column
+
+ # Create a new Translation::Ripper object with the given source.
+ def initialize(source, filename = "(ripper)", lineno = 1)
+ @source = source
+ @filename = filename
+ @lineno = lineno
+ @column = 0
+ @result = nil
+ end
+
+ ##########################################################################
+ # Public interface
+ ##########################################################################
+
+ # True if the parser encountered an error during parsing.
+ def error?
+ result.failure?
+ end
+
+ # Parse the source and return the result.
+ def parse
+ result.comments.each do |comment|
+ location = comment.location
+ bounds(location)
+
+ if comment.is_a?(InlineComment)
+ on_comment(comment.slice)
+ else
+ offset = location.start_offset
+ lines = comment.slice.lines
+
+ lines.each_with_index do |line, index|
+ bounds(location.copy(start_offset: offset))
+
+ if index == 0
+ on_embdoc_beg(line)
+ elsif index == lines.size - 1
+ on_embdoc_end(line)
+ else
+ on_embdoc(line)
+ end
+
+ offset += line.bytesize
+ end
+ end
+ end
+
+ result.magic_comments.each do |magic_comment|
+ on_magic_comment(magic_comment.key, magic_comment.value)
+ end
+
+ unless result.data_loc.nil?
+ on___end__(result.data_loc.slice.each_line.first)
+ end
+
+ result.warnings.each do |warning|
+ bounds(warning.location)
+
+ if warning.level == :default
+ warning(warning.message)
+ else
+ case warning.type
+ when :ambiguous_first_argument_plus
+ on_arg_ambiguous("+")
+ when :ambiguous_first_argument_minus
+ on_arg_ambiguous("-")
+ when :ambiguous_slash
+ on_arg_ambiguous("/")
+ else
+ warn(warning.message)
+ end
+ end
+ end
+
+ if error?
+ result.errors.each do |error|
+ location = error.location
+ bounds(location)
+
+ case error.type
+ when :alias_argument
+ on_alias_error("can't make alias for the number variables", location.slice)
+ when :argument_formal_class
+ on_param_error("formal argument cannot be a class variable", location.slice)
+ when :argument_format_constant
+ on_param_error("formal argument cannot be a constant", location.slice)
+ when :argument_formal_global
+ on_param_error("formal argument cannot be a global variable", location.slice)
+ when :argument_formal_ivar
+ on_param_error("formal argument cannot be an instance variable", location.slice)
+ when :class_name, :module_name
+ on_class_name_error("class/module name must be CONSTANT", location.slice)
+ else
+ on_parse_error(error.message)
+ end
+ end
+
+ nil
+ else
+ result.value.accept(self)
+ end
+ end
+
+ ##########################################################################
+ # Visitor methods
+ ##########################################################################
+
+ # alias foo bar
+ # ^^^^^^^^^^^^^
+ def visit_alias_method_node(node)
+ new_name = visit(node.new_name)
+ old_name = visit(node.old_name)
+
+ bounds(node.location)
+ on_alias(new_name, old_name)
+ end
+
+ # alias $foo $bar
+ # ^^^^^^^^^^^^^^^
+ def visit_alias_global_variable_node(node)
+ new_name = visit_alias_global_variable_node_value(node.new_name)
+ old_name = visit_alias_global_variable_node_value(node.old_name)
+
+ bounds(node.location)
+ on_var_alias(new_name, old_name)
+ end
+
+ # Visit one side of an alias global variable node.
+ private def visit_alias_global_variable_node_value(node)
+ bounds(node.location)
+
+ case node
+ when BackReferenceReadNode
+ on_backref(node.slice)
+ when GlobalVariableReadNode
+ on_gvar(node.name.to_s)
+ else
+ raise
+ end
+ end
+
+ # foo => bar | baz
+ # ^^^^^^^^^
+ def visit_alternation_pattern_node(node)
+ left = visit_pattern_node(node.left)
+ right = visit_pattern_node(node.right)
+
+ bounds(node.location)
+ on_binary(left, :|, right)
+ end
+
+ # Visit a pattern within a pattern match. This is used to bypass the
+ # parenthesis node that can be used to wrap patterns.
+ private def visit_pattern_node(node)
+ if node.is_a?(ParenthesesNode)
+ visit(node.body)
+ else
+ visit(node)
+ end
+ end
+
+ # a and b
+ # ^^^^^^^
+ def visit_and_node(node)
+ left = visit(node.left)
+ right = visit(node.right)
+
+ bounds(node.location)
+ on_binary(left, node.operator.to_sym, right)
+ end
+
+ # []
+ # ^^
+ def visit_array_node(node)
+ case (opening = node.opening)
+ when /^%w/
+ opening_loc = node.opening_loc
+ bounds(opening_loc)
+ on_qwords_beg(opening)
+
+ elements = on_qwords_new
+ previous = nil
+
+ node.elements.each do |element|
+ visit_words_sep(opening_loc, previous, element)
+
+ bounds(element.location)
+ elements = on_qwords_add(elements, on_tstring_content(element.content))
+
+ previous = element
+ end
+
+ bounds(node.closing_loc)
+ on_tstring_end(node.closing)
+ when /^%i/
+ opening_loc = node.opening_loc
+ bounds(opening_loc)
+ on_qsymbols_beg(opening)
+
+ elements = on_qsymbols_new
+ previous = nil
+
+ node.elements.each do |element|
+ visit_words_sep(opening_loc, previous, element)
+
+ bounds(element.location)
+ elements = on_qsymbols_add(elements, on_tstring_content(element.value))
+
+ previous = element
+ end
+
+ bounds(node.closing_loc)
+ on_tstring_end(node.closing)
+ when /^%W/
+ opening_loc = node.opening_loc
+ bounds(opening_loc)
+ on_words_beg(opening)
+
+ elements = on_words_new
+ previous = nil
+
+ node.elements.each do |element|
+ visit_words_sep(opening_loc, previous, element)
+
+ bounds(element.location)
+ elements =
+ on_words_add(
+ elements,
+ if element.is_a?(StringNode)
+ on_word_add(on_word_new, on_tstring_content(element.content))
+ else
+ element.parts.inject(on_word_new) do |word, part|
+ word_part =
+ if part.is_a?(StringNode)
+ bounds(part.location)
+ on_tstring_content(part.content)
+ else
+ visit(part)
+ end
+
+ on_word_add(word, word_part)
+ end
+ end
+ )
+
+ previous = element
+ end
+
+ bounds(node.closing_loc)
+ on_tstring_end(node.closing)
+ when /^%I/
+ opening_loc = node.opening_loc
+ bounds(opening_loc)
+ on_symbols_beg(opening)
+
+ elements = on_symbols_new
+ previous = nil
+
+ node.elements.each do |element|
+ visit_words_sep(opening_loc, previous, element)
+
+ bounds(element.location)
+ elements =
+ on_symbols_add(
+ elements,
+ if element.is_a?(SymbolNode)
+ on_word_add(on_word_new, on_tstring_content(element.value))
+ else
+ element.parts.inject(on_word_new) do |word, part|
+ word_part =
+ if part.is_a?(StringNode)
+ bounds(part.location)
+ on_tstring_content(part.content)
+ else
+ visit(part)
+ end
+
+ on_word_add(word, word_part)
+ end
+ end
+ )
+
+ previous = element
+ end
+
+ bounds(node.closing_loc)
+ on_tstring_end(node.closing)
+ else
+ bounds(node.opening_loc)
+ on_lbracket(opening)
+
+ elements = visit_arguments(node.elements) unless node.elements.empty?
+
+ bounds(node.closing_loc)
+ on_rbracket(node.closing)
+ end
+
+ bounds(node.location)
+ on_array(elements)
+ end
+
+ # Dispatch a words_sep event that contains the space between the elements
+ # of list literals.
+ private def visit_words_sep(opening_loc, previous, current)
+ end_offset = (previous.nil? ? opening_loc : previous.location).end_offset
+ start_offset = current.location.start_offset
+
+ if end_offset != start_offset
+ bounds(current.location.copy(start_offset: end_offset))
+ on_words_sep(source.byteslice(end_offset...start_offset))
+ end
+ end
+
+ # Visit a list of elements, like the elements of an array or arguments.
+ private def visit_arguments(elements)
+ bounds(elements.first.location)
+ elements.inject(on_args_new) do |args, element|
+ arg = visit(element)
+ bounds(element.location)
+
+ case element
+ when BlockArgumentNode
+ on_args_add_block(args, arg)
+ when SplatNode
+ on_args_add_star(args, arg)
+ else
+ on_args_add(args, arg)
+ end
+ end
+ end
+
+ # foo => [bar]
+ # ^^^^^
+ def visit_array_pattern_node(node)
+ constant = visit(node.constant)
+ requireds = visit_all(node.requireds) if node.requireds.any?
+ rest =
+ if (rest_node = node.rest).is_a?(SplatNode)
+ if rest_node.expression.nil?
+ bounds(rest_node.location)
+ on_var_field(nil)
+ else
+ visit(rest_node.expression)
+ end
+ end
+
+ posts = visit_all(node.posts) if node.posts.any?
+
+ bounds(node.location)
+ on_aryptn(constant, requireds, rest, posts)
+ end
+
+ # foo(bar)
+ # ^^^
+ def visit_arguments_node(node)
+ arguments, _ = visit_call_node_arguments(node, nil, false)
+ arguments
+ end
+
+ # { a: 1 }
+ # ^^^^
+ def visit_assoc_node(node)
+ key = visit(node.key)
+ value = visit(node.value)
+
+ bounds(node.location)
+ on_assoc_new(key, value)
+ end
+
+ # def foo(**); bar(**); end
+ # ^^
+ #
+ # { **foo }
+ # ^^^^^
+ def visit_assoc_splat_node(node)
+ value = visit(node.value)
+
+ bounds(node.location)
+ on_assoc_splat(value)
+ end
+
+ # $+
+ # ^^
+ def visit_back_reference_read_node(node)
+ bounds(node.location)
+ on_backref(node.slice)
+ end
+
+ # begin end
+ # ^^^^^^^^^
+ def visit_begin_node(node)
+ clauses = visit_begin_node_clauses(node.begin_keyword_loc, node, false)
+
+ bounds(node.location)
+ on_begin(clauses)
+ end
+
+ # Visit the clauses of a begin node to form an on_bodystmt call.
+ private def visit_begin_node_clauses(location, node, allow_newline)
+ statements =
+ if node.statements.nil?
+ on_stmts_add(on_stmts_new, on_void_stmt)
+ else
+ body = node.statements.body
+ body.unshift(nil) if void_stmt?(location, node.statements.body[0].location, allow_newline)
+
+ bounds(node.statements.location)
+ visit_statements_node_body(body)
+ end
+
+ rescue_clause = visit(node.rescue_clause)
+ else_clause =
+ unless (else_clause_node = node.else_clause).nil?
+ else_statements =
+ if else_clause_node.statements.nil?
+ [nil]
+ else
+ body = else_clause_node.statements.body
+ body.unshift(nil) if void_stmt?(else_clause_node.else_keyword_loc, else_clause_node.statements.body[0].location, allow_newline)
+ body
+ end
+
+ bounds(else_clause_node.location)
+ visit_statements_node_body(else_statements)
+ end
+ ensure_clause = visit(node.ensure_clause)
+
+ bounds(node.location)
+ on_bodystmt(statements, rescue_clause, else_clause, ensure_clause)
+ end
+
+ # Visit the body of a structure that can have either a set of statements
+ # or statements wrapped in rescue/else/ensure.
+ private def visit_body_node(location, node, allow_newline = false)
+ case node
+ when nil
+ bounds(location)
+ on_bodystmt(visit_statements_node_body([nil]), nil, nil, nil)
+ when StatementsNode
+ body = [*node.body]
+ body.unshift(nil) if void_stmt?(location, body[0].location, allow_newline)
+ stmts = visit_statements_node_body(body)
+
+ bounds(node.body.first.location)
+ on_bodystmt(stmts, nil, nil, nil)
+ when BeginNode
+ visit_begin_node_clauses(location, node, allow_newline)
+ else
+ raise
+ end
+ end
+
+ # foo(&bar)
+ # ^^^^
+ def visit_block_argument_node(node)
+ visit(node.expression)
+ end
+
+ # foo { |; bar| }
+ # ^^^
+ def visit_block_local_variable_node(node)
+ bounds(node.location)
+ on_ident(node.name.to_s)
+ end
+
+ # Visit a BlockNode.
+ def visit_block_node(node)
+ braces = node.opening == "{"
+ parameters = visit(node.parameters)
+
+ body =
+ case node.body
+ when nil
+ bounds(node.location)
+ stmts = on_stmts_add(on_stmts_new, on_void_stmt)
+
+ bounds(node.location)
+ braces ? stmts : on_bodystmt(stmts, nil, nil, nil)
+ when StatementsNode
+ stmts = node.body.body
+ stmts.unshift(nil) if void_stmt?(node.parameters&.location || node.opening_loc, node.body.location, false)
+ stmts = visit_statements_node_body(stmts)
+
+ bounds(node.body.location)
+ braces ? stmts : on_bodystmt(stmts, nil, nil, nil)
+ when BeginNode
+ visit_body_node(node.parameters&.location || node.opening_loc, node.body)
+ else
+ raise
+ end
+
+ if braces
+ bounds(node.location)
+ on_brace_block(parameters, body)
+ else
+ bounds(node.location)
+ on_do_block(parameters, body)
+ end
+ end
+
+ # def foo(&bar); end
+ # ^^^^
+ def visit_block_parameter_node(node)
+ if node.name_loc.nil?
+ bounds(node.location)
+ on_blockarg(nil)
+ else
+ bounds(node.name_loc)
+ name = visit_token(node.name.to_s)
+
+ bounds(node.location)
+ on_blockarg(name)
+ end
+ end
+
+ # A block's parameters.
+ def visit_block_parameters_node(node)
+ parameters =
+ if node.parameters.nil?
+ on_params(nil, nil, nil, nil, nil, nil, nil)
+ else
+ visit(node.parameters)
+ end
+
+ locals =
+ if node.locals.any?
+ visit_all(node.locals)
+ else
+ false
+ end
+
+ bounds(node.location)
+ on_block_var(parameters, locals)
+ end
+
+ # break
+ # ^^^^^
+ #
+ # break foo
+ # ^^^^^^^^^
+ def visit_break_node(node)
+ if node.arguments.nil?
+ bounds(node.location)
+ on_break(on_args_new)
+ else
+ arguments = visit(node.arguments)
+
+ bounds(node.location)
+ on_break(arguments)
+ end
+ end
+
+ # foo
+ # ^^^
+ #
+ # foo.bar
+ # ^^^^^^^
+ #
+ # foo.bar() {}
+ # ^^^^^^^^^^^^
+ def visit_call_node(node)
+ if node.call_operator_loc.nil?
+ case node.name
+ when :[]
+ receiver = visit(node.receiver)
+ arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
+
+ bounds(node.location)
+ call = on_aref(receiver, arguments)
+
+ if block.nil?
+ call
+ else
+ bounds(node.location)
+ on_method_add_block(call, block)
+ end
+ when :[]=
+ receiver = visit(node.receiver)
+
+ *arguments, last_argument = node.arguments.arguments
+ arguments << node.block if !node.block.nil?
+
+ arguments =
+ if arguments.any?
+ args = visit_arguments(arguments)
+
+ if !node.block.nil?
+ args
+ else
+ bounds(arguments.first.location)
+ on_args_add_block(args, false)
+ end
+ end
+
+ bounds(node.location)
+ call = on_aref_field(receiver, arguments)
+ value = visit_write_value(last_argument)
+
+ bounds(last_argument.location)
+ on_assign(call, value)
+ when :-@, :+@, :~
+ receiver = visit(node.receiver)
+
+ bounds(node.location)
+ on_unary(node.name, receiver)
+ when :!
+ receiver = visit(node.receiver)
+
+ bounds(node.location)
+ on_unary(node.message == "not" ? :not : :!, receiver)
+ when *BINARY_OPERATORS
+ receiver = visit(node.receiver)
+ value = visit(node.arguments.arguments.first)
+
+ bounds(node.location)
+ on_binary(receiver, node.name, value)
+ else
+ bounds(node.message_loc)
+ message = visit_token(node.message, false)
+
+ if node.variable_call?
+ on_vcall(message)
+ else
+ arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc || node.location))
+ call =
+ if node.opening_loc.nil? && arguments&.any?
+ bounds(node.location)
+ on_command(message, arguments)
+ elsif !node.opening_loc.nil?
+ bounds(node.location)
+ on_method_add_arg(on_fcall(message), on_arg_paren(arguments))
+ else
+ bounds(node.location)
+ on_method_add_arg(on_fcall(message), on_args_new)
+ end
+
+ if block.nil?
+ call
+ else
+ bounds(node.block.location)
+ on_method_add_block(call, block)
+ end
+ end
+ end
+ else
+ receiver = visit(node.receiver)
+
+ bounds(node.call_operator_loc)
+ call_operator = visit_token(node.call_operator)
+
+ message =
+ if node.message_loc.nil?
+ :call
+ else
+ bounds(node.message_loc)
+ visit_token(node.message, false)
+ end
+
+ if node.name.end_with?("=") && !node.message.end_with?("=") && !node.arguments.nil? && node.block.nil?
+ value = visit_write_value(node.arguments.arguments.first)
+
+ bounds(node.location)
+ on_assign(on_field(receiver, call_operator, message), value)
+ else
+ arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc || node.location))
+ call =
+ if node.opening_loc.nil?
+ bounds(node.location)
+
+ if node.arguments.nil? && !node.block.is_a?(BlockArgumentNode)
+ on_call(receiver, call_operator, message)
+ else
+ on_command_call(receiver, call_operator, message, arguments)
+ end
+ else
+ bounds(node.opening_loc)
+ arguments = on_arg_paren(arguments)
+
+ bounds(node.location)
+ on_method_add_arg(on_call(receiver, call_operator, message), arguments)
+ end
+
+ if block.nil?
+ call
+ else
+ bounds(node.block.location)
+ on_method_add_block(call, block)
+ end
+ end
+ end
+ end
+
+ # Visit the arguments and block of a call node and return the arguments
+ # and block as they should be used.
+ private def visit_call_node_arguments(arguments_node, block_node, trailing_comma)
+ arguments = arguments_node&.arguments || []
+ block = block_node
+
+ if block.is_a?(BlockArgumentNode)
+ arguments << block
+ block = nil
+ end
+
+ [
+ if arguments.length == 1 && arguments.first.is_a?(ForwardingArgumentsNode)
+ visit(arguments.first)
+ elsif arguments.any?
+ args = visit_arguments(arguments)
+
+ if block_node.is_a?(BlockArgumentNode) || arguments.last.is_a?(ForwardingArgumentsNode) || command?(arguments.last) || trailing_comma
+ args
+ else
+ bounds(arguments.first.location)
+ on_args_add_block(args, false)
+ end
+ end,
+ visit(block)
+ ]
+ end
+
+ # Returns true if the given node is a command node.
+ private def command?(node)
+ node.is_a?(CallNode) &&
+ node.opening_loc.nil? &&
+ (!node.arguments.nil? || node.block.is_a?(BlockArgumentNode)) &&
+ !BINARY_OPERATORS.include?(node.name)
+ end
+
+ # foo.bar += baz
+ # ^^^^^^^^^^^^^^^
+ def visit_call_operator_write_node(node)
+ receiver = visit(node.receiver)
+
+ bounds(node.call_operator_loc)
+ call_operator = visit_token(node.call_operator)
+
+ bounds(node.message_loc)
+ message = visit_token(node.message)
+
+ bounds(node.location)
+ target = on_field(receiver, call_operator, message)
+
+ bounds(node.operator_loc)
+ operator = on_op("#{node.operator}=")
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_opassign(target, operator, value)
+ end
+
+ # foo.bar &&= baz
+ # ^^^^^^^^^^^^^^^
+ def visit_call_and_write_node(node)
+ receiver = visit(node.receiver)
+
+ bounds(node.call_operator_loc)
+ call_operator = visit_token(node.call_operator)
+
+ bounds(node.message_loc)
+ message = visit_token(node.message)
+
+ bounds(node.location)
+ target = on_field(receiver, call_operator, message)
+
+ bounds(node.operator_loc)
+ operator = on_op("&&=")
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_opassign(target, operator, value)
+ end
+
+ # foo.bar ||= baz
+ # ^^^^^^^^^^^^^^^
+ def visit_call_or_write_node(node)
+ receiver = visit(node.receiver)
+
+ bounds(node.call_operator_loc)
+ call_operator = visit_token(node.call_operator)
+
+ bounds(node.message_loc)
+ message = visit_token(node.message)
+
+ bounds(node.location)
+ target = on_field(receiver, call_operator, message)
+
+ bounds(node.operator_loc)
+ operator = on_op("||=")
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_opassign(target, operator, value)
+ end
+
+ # foo.bar, = 1
+ # ^^^^^^^
+ def visit_call_target_node(node)
+ if node.call_operator == "::"
+ receiver = visit(node.receiver)
+
+ bounds(node.message_loc)
+ message = visit_token(node.message)
+
+ bounds(node.location)
+ on_const_path_field(receiver, message)
+ else
+ receiver = visit(node.receiver)
+
+ bounds(node.call_operator_loc)
+ call_operator = visit_token(node.call_operator)
+
+ bounds(node.message_loc)
+ message = visit_token(node.message)
+
+ bounds(node.location)
+ on_field(receiver, call_operator, message)
+ end
+ end
+
+ # foo => bar => baz
+ # ^^^^^^^^^^
+ def visit_capture_pattern_node(node)
+ value = visit(node.value)
+ target = visit(node.target)
+
+ bounds(node.location)
+ on_binary(value, :"=>", target)
+ end
+
+ # case foo; when bar; end
+ # ^^^^^^^^^^^^^^^^^^^^^^^
+ def visit_case_node(node)
+ predicate = visit(node.predicate)
+ clauses =
+ node.conditions.reverse_each.inject(visit(node.consequent)) do |consequent, condition|
+ on_when(*visit(condition), consequent)
+ end
+
+ bounds(node.location)
+ on_case(predicate, clauses)
+ end
+
+ # case foo; in bar; end
+ # ^^^^^^^^^^^^^^^^^^^^^
+ def visit_case_match_node(node)
+ predicate = visit(node.predicate)
+ clauses =
+ node.conditions.reverse_each.inject(visit(node.consequent)) do |consequent, condition|
+ on_in(*visit(condition), consequent)
+ end
+
+ bounds(node.location)
+ on_case(predicate, clauses)
+ end
+
+ # class Foo; end
+ # ^^^^^^^^^^^^^^
+ def visit_class_node(node)
+ constant_path =
+ if node.constant_path.is_a?(ConstantReadNode)
+ bounds(node.constant_path.location)
+ on_const_ref(on_const(node.constant_path.name.to_s))
+ else
+ visit(node.constant_path)
+ end
+
+ superclass = visit(node.superclass)
+ bodystmt = visit_body_node(node.superclass&.location || node.constant_path.location, node.body, node.superclass.nil?)
+
+ bounds(node.location)
+ on_class(constant_path, superclass, bodystmt)
+ end
+
+ # @@foo
+ # ^^^^^
+ def visit_class_variable_read_node(node)
+ bounds(node.location)
+ on_var_ref(on_cvar(node.slice))
+ end
+
+ # @@foo = 1
+ # ^^^^^^^^^
+ #
+ # @@foo, @@bar = 1
+ # ^^^^^ ^^^^^
+ def visit_class_variable_write_node(node)
+ bounds(node.name_loc)
+ target = on_var_field(on_cvar(node.name.to_s))
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_assign(target, value)
+ end
+
+ # @@foo += bar
+ # ^^^^^^^^^^^^
+ def visit_class_variable_operator_write_node(node)
+ bounds(node.name_loc)
+ target = on_var_field(on_cvar(node.name.to_s))
+
+ bounds(node.operator_loc)
+ operator = on_op("#{node.operator}=")
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_opassign(target, operator, value)
+ end
+
+ # @@foo &&= bar
+ # ^^^^^^^^^^^^^
+ def visit_class_variable_and_write_node(node)
+ bounds(node.name_loc)
+ target = on_var_field(on_cvar(node.name.to_s))
+
+ bounds(node.operator_loc)
+ operator = on_op("&&=")
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_opassign(target, operator, value)
+ end
+
+ # @@foo ||= bar
+ # ^^^^^^^^^^^^^
+ def visit_class_variable_or_write_node(node)
+ bounds(node.name_loc)
+ target = on_var_field(on_cvar(node.name.to_s))
+
+ bounds(node.operator_loc)
+ operator = on_op("||=")
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_opassign(target, operator, value)
+ end
+
+ # @@foo, = bar
+ # ^^^^^
+ def visit_class_variable_target_node(node)
+ bounds(node.location)
+ on_var_field(on_cvar(node.name.to_s))
+ end
+
+ # Foo
+ # ^^^
+ def visit_constant_read_node(node)
+ bounds(node.location)
+ on_var_ref(on_const(node.name.to_s))
+ end
+
+ # Foo = 1
+ # ^^^^^^^
+ #
+ # Foo, Bar = 1
+ # ^^^ ^^^
+ def visit_constant_write_node(node)
+ bounds(node.name_loc)
+ target = on_var_field(on_const(node.name.to_s))
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_assign(target, value)
+ end
+
+ # Foo += bar
+ # ^^^^^^^^^^^
+ def visit_constant_operator_write_node(node)
+ bounds(node.name_loc)
+ target = on_var_field(on_const(node.name.to_s))
+
+ bounds(node.operator_loc)
+ operator = on_op("#{node.operator}=")
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_opassign(target, operator, value)
+ end
+
+ # Foo &&= bar
+ # ^^^^^^^^^^^^
+ def visit_constant_and_write_node(node)
+ bounds(node.name_loc)
+ target = on_var_field(on_const(node.name.to_s))
+
+ bounds(node.operator_loc)
+ operator = on_op("&&=")
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_opassign(target, operator, value)
+ end
+
+ # Foo ||= bar
+ # ^^^^^^^^^^^^
+ def visit_constant_or_write_node(node)
+ bounds(node.name_loc)
+ target = on_var_field(on_const(node.name.to_s))
+
+ bounds(node.operator_loc)
+ operator = on_op("||=")
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_opassign(target, operator, value)
+ end
+
+ # Foo, = bar
+ # ^^^
+ def visit_constant_target_node(node)
+ bounds(node.location)
+ on_var_field(on_const(node.name.to_s))
+ end
+
+ # Foo::Bar
+ # ^^^^^^^^
+ def visit_constant_path_node(node)
+ if node.parent.nil?
+ bounds(node.child.location)
+ child = on_const(node.child.name.to_s)
+
+ bounds(node.location)
+ on_top_const_ref(child)
+ else
+ parent = visit(node.parent)
+
+ bounds(node.child.location)
+ child = on_const(node.child.name.to_s)
+
+ bounds(node.location)
+ on_const_path_ref(parent, child)
+ end
+ end
+
+ # Foo::Bar = 1
+ # ^^^^^^^^^^^^
+ #
+ # Foo::Foo, Bar::Bar = 1
+ # ^^^^^^^^ ^^^^^^^^
+ def visit_constant_path_write_node(node)
+ target = visit_constant_path_write_node_target(node.target)
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_assign(target, value)
+ end
+
+ # Visit a constant path that is part of a write node.
+ private def visit_constant_path_write_node_target(node)
+ if node.parent.nil?
+ bounds(node.child.location)
+ child = on_const(node.child.name.to_s)
+
+ bounds(node.location)
+ on_top_const_field(child)
+ else
+ parent = visit(node.parent)
+
+ bounds(node.child.location)
+ child = on_const(node.child.name.to_s)
+
+ bounds(node.location)
+ on_const_path_field(parent, child)
+ end
+ end
+
+ # Foo::Bar += baz
+ # ^^^^^^^^^^^^^^^
+ def visit_constant_path_operator_write_node(node)
+ target = visit_constant_path_write_node_target(node.target)
+ value = visit(node.value)
+
+ bounds(node.operator_loc)
+ operator = on_op("#{node.operator}=")
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_opassign(target, operator, value)
+ end
+
+ # Foo::Bar &&= baz
+ # ^^^^^^^^^^^^^^^^
+ def visit_constant_path_and_write_node(node)
+ target = visit_constant_path_write_node_target(node.target)
+ value = visit(node.value)
+
+ bounds(node.operator_loc)
+ operator = on_op("&&=")
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_opassign(target, operator, value)
+ end
+
+ # Foo::Bar ||= baz
+ # ^^^^^^^^^^^^^^^^
+ def visit_constant_path_or_write_node(node)
+ target = visit_constant_path_write_node_target(node.target)
+ value = visit(node.value)
+
+ bounds(node.operator_loc)
+ operator = on_op("||=")
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_opassign(target, operator, value)
+ end
+
+ # Foo::Bar, = baz
+ # ^^^^^^^^
+ def visit_constant_path_target_node(node)
+ visit_constant_path_write_node_target(node)
+ end
+
+ # def foo; end
+ # ^^^^^^^^^^^^
+ #
+ # def self.foo; end
+ # ^^^^^^^^^^^^^^^^^
+ def visit_def_node(node)
+ receiver = visit(node.receiver)
+ operator =
+ if !node.operator_loc.nil?
+ bounds(node.operator_loc)
+ visit_token(node.operator)
+ end
+
+ bounds(node.name_loc)
+ name = visit_token(node.name_loc.slice)
+
+ parameters =
+ if node.parameters.nil?
+ bounds(node.location)
+ on_params(nil, nil, nil, nil, nil, nil, nil)
+ else
+ visit(node.parameters)
+ end
+
+ if !node.lparen_loc.nil?
+ bounds(node.lparen_loc)
+ parameters = on_paren(parameters)
+ end
+
+ bodystmt =
+ if node.equal_loc.nil?
+ visit_body_node(node.rparen_loc || node.end_keyword_loc, node.body)
+ else
+ body = visit(node.body.body.first)
+
+ bounds(node.body.location)
+ on_bodystmt(body, nil, nil, nil)
+ end
+
+ bounds(node.location)
+ if receiver.nil?
+ on_def(name, parameters, bodystmt)
+ else
+ on_defs(receiver, operator, name, parameters, bodystmt)
+ end
+ end
+
+ # defined? a
+ # ^^^^^^^^^^
+ #
+ # defined?(a)
+ # ^^^^^^^^^^^
+ def visit_defined_node(node)
+ bounds(node.location)
+ on_defined(visit(node.value))
+ end
+
+ # if foo then bar else baz end
+ # ^^^^^^^^^^^^
+ def visit_else_node(node)
+ statements =
+ if node.statements.nil?
+ [nil]
+ else
+ body = node.statements.body
+ body.unshift(nil) if void_stmt?(node.else_keyword_loc, node.statements.body[0].location, false)
+ body
+ end
+
+ bounds(node.location)
+ on_else(visit_statements_node_body(statements))
+ end
+
+ # "foo #{bar}"
+ # ^^^^^^
+ def visit_embedded_statements_node(node)
+ bounds(node.opening_loc)
+ on_embexpr_beg(node.opening)
+
+ statements =
+ if node.statements.nil?
+ bounds(node.location)
+ on_stmts_add(on_stmts_new, on_void_stmt)
+ else
+ visit(node.statements)
+ end
+
+ bounds(node.closing_loc)
+ on_embexpr_end(node.closing)
+
+ bounds(node.location)
+ on_string_embexpr(statements)
+ end
+
+ # "foo #@bar"
+ # ^^^^^
+ def visit_embedded_variable_node(node)
+ bounds(node.operator_loc)
+ on_embvar(node.operator)
+
+ variable = visit(node.variable)
+
+ bounds(node.location)
+ on_string_dvar(variable)
+ end
+
+ # Visit an EnsureNode node.
+ def visit_ensure_node(node)
+ statements =
+ if node.statements.nil?
+ [nil]
+ else
+ body = node.statements.body
+ body.unshift(nil) if void_stmt?(node.ensure_keyword_loc, body[0].location, false)
+ body
+ end
+
+ statements = visit_statements_node_body(statements)
+
+ bounds(node.location)
+ on_ensure(statements)
+ end
+
+ # false
+ # ^^^^^
+ def visit_false_node(node)
+ bounds(node.location)
+ on_var_ref(on_kw("false"))
+ end
+
+ # foo => [*, bar, *]
+ # ^^^^^^^^^^^
+ def visit_find_pattern_node(node)
+ constant = visit(node.constant)
+ left =
+ if node.left.expression.nil?
+ bounds(node.left.location)
+ on_var_field(nil)
+ else
+ visit(node.left.expression)
+ end
+
+ requireds = visit_all(node.requireds) if node.requireds.any?
+ right =
+ if node.right.expression.nil?
+ bounds(node.right.location)
+ on_var_field(nil)
+ else
+ visit(node.right.expression)
+ end
+
+ bounds(node.location)
+ on_fndptn(constant, left, requireds, right)
+ end
+
+ # if foo .. bar; end
+ # ^^^^^^^^^^
+ def visit_flip_flop_node(node)
+ left = visit(node.left)
+ right = visit(node.right)
+
+ bounds(node.location)
+ if node.exclude_end?
+ on_dot3(left, right)
+ else
+ on_dot2(left, right)
+ end
+ end
+
+ # 1.0
+ # ^^^
+ def visit_float_node(node)
+ visit_number_node(node) { |text| on_float(text) }
+ end
+
+ # for foo in bar do end
+ # ^^^^^^^^^^^^^^^^^^^^^
+ def visit_for_node(node)
+ index = visit(node.index)
+ collection = visit(node.collection)
+ statements =
+ if node.statements.nil?
+ bounds(node.location)
+ on_stmts_add(on_stmts_new, on_void_stmt)
+ else
+ visit(node.statements)
+ end
+
+ bounds(node.location)
+ on_for(index, collection, statements)
+ end
+
+ # def foo(...); bar(...); end
+ # ^^^
+ def visit_forwarding_arguments_node(node)
+ bounds(node.location)
+ on_args_forward
+ end
+
+ # def foo(...); end
+ # ^^^
+ def visit_forwarding_parameter_node(node)
+ bounds(node.location)
+ on_args_forward
+ end
+
+ # super
+ # ^^^^^
+ #
+ # super {}
+ # ^^^^^^^^
+ def visit_forwarding_super_node(node)
+ if node.block.nil?
+ bounds(node.location)
+ on_zsuper
+ else
+ block = visit(node.block)
+
+ bounds(node.location)
+ on_method_add_block(on_zsuper, block)
+ end
+ end
+
+ # $foo
+ # ^^^^
+ def visit_global_variable_read_node(node)
+ bounds(node.location)
+ on_var_ref(on_gvar(node.name.to_s))
+ end
+
+ # $foo = 1
+ # ^^^^^^^^
+ #
+ # $foo, $bar = 1
+ # ^^^^ ^^^^
+ def visit_global_variable_write_node(node)
+ bounds(node.name_loc)
+ target = on_var_field(on_gvar(node.name.to_s))
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_assign(target, value)
+ end
+
+ # $foo += bar
+ # ^^^^^^^^^^^
+ def visit_global_variable_operator_write_node(node)
+ bounds(node.name_loc)
+ target = on_var_field(on_gvar(node.name.to_s))
+
+ bounds(node.operator_loc)
+ operator = on_op("#{node.operator}=")
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_opassign(target, operator, value)
+ end
+
+ # $foo &&= bar
+ # ^^^^^^^^^^^^
+ def visit_global_variable_and_write_node(node)
+ bounds(node.name_loc)
+ target = on_var_field(on_gvar(node.name.to_s))
+
+ bounds(node.operator_loc)
+ operator = on_op("&&=")
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_opassign(target, operator, value)
+ end
+
+ # $foo ||= bar
+ # ^^^^^^^^^^^^
+ def visit_global_variable_or_write_node(node)
+ bounds(node.name_loc)
+ target = on_var_field(on_gvar(node.name.to_s))
+
+ bounds(node.operator_loc)
+ operator = on_op("||=")
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_opassign(target, operator, value)
+ end
+
+ # $foo, = bar
+ # ^^^^
+ def visit_global_variable_target_node(node)
+ bounds(node.location)
+ on_var_field(on_gvar(node.name.to_s))
+ end
+
+ # {}
+ # ^^
+ def visit_hash_node(node)
+ elements =
+ if node.elements.any?
+ args = visit_all(node.elements)
+
+ bounds(node.elements.first.location)
+ on_assoclist_from_args(args)
+ end
+
+ bounds(node.location)
+ on_hash(elements)
+ end
+
+ # foo => {}
+ # ^^
+ def visit_hash_pattern_node(node)
+ constant = visit(node.constant)
+ elements =
+ if node.elements.any? || !node.rest.nil?
+ node.elements.map do |element|
+ [
+ if (key = element.key).opening_loc.nil?
+ visit(key)
+ else
+ bounds(key.value_loc)
+ if (value = key.value).empty?
+ on_string_content
+ else
+ on_string_add(on_string_content, on_tstring_content(value))
+ end
+ end,
+ visit(element.value)
+ ]
+ end
+ end
+
+ rest =
+ case node.rest
+ when AssocSplatNode
+ visit(node.rest.value)
+ when NoKeywordsParameterNode
+ bounds(node.rest.location)
+ on_var_field(visit(node.rest))
+ end
+
+ bounds(node.location)
+ on_hshptn(constant, elements, rest)
+ end
+
+ # if foo then bar end
+ # ^^^^^^^^^^^^^^^^^^^
+ #
+ # bar if foo
+ # ^^^^^^^^^^
+ #
+ # foo ? bar : baz
+ # ^^^^^^^^^^^^^^^
+ def visit_if_node(node)
+ if node.then_keyword == "?"
+ predicate = visit(node.predicate)
+ truthy = visit(node.statements.body.first)
+ falsy = visit(node.consequent.statements.body.first)
+
+ bounds(node.location)
+ on_ifop(predicate, truthy, falsy)
+ elsif node.statements.nil? || (node.predicate.location.start_offset < node.statements.location.start_offset)
+ predicate = visit(node.predicate)
+ statements =
+ if node.statements.nil?
+ bounds(node.location)
+ on_stmts_add(on_stmts_new, on_void_stmt)
+ else
+ visit(node.statements)
+ end
+ consequent = visit(node.consequent)
+
+ bounds(node.location)
+ if node.if_keyword == "if"
+ on_if(predicate, statements, consequent)
+ else
+ on_elsif(predicate, statements, consequent)
+ end
+ else
+ statements = visit(node.statements.body.first)
+ predicate = visit(node.predicate)
+
+ bounds(node.location)
+ on_if_mod(predicate, statements)
+ end
+ end
+
+ # 1i
+ # ^^
+ def visit_imaginary_node(node)
+ visit_number_node(node) { |text| on_imaginary(text) }
+ end
+
+ # { foo: }
+ # ^^^^
+ def visit_implicit_node(node)
+ end
+
+ # foo { |bar,| }
+ # ^
+ def visit_implicit_rest_node(node)
+ bounds(node.location)
+ on_excessed_comma
+ end
+
+ # case foo; in bar; end
+ # ^^^^^^^^^^^^^^^^^^^^^
+ def visit_in_node(node)
+ # This is a special case where we're not going to call on_in directly
+ # because we don't have access to the consequent. Instead, we'll return
+ # the component parts and let the parent node handle it.
+ pattern = visit_pattern_node(node.pattern)
+ statements =
+ if node.statements.nil?
+ bounds(node.location)
+ on_stmts_add(on_stmts_new, on_void_stmt)
+ else
+ visit(node.statements)
+ end
+
+ [pattern, statements]
+ end
+
+ # foo[bar] += baz
+ # ^^^^^^^^^^^^^^^
+ def visit_index_operator_write_node(node)
+ receiver = visit(node.receiver)
+ arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
+
+ bounds(node.location)
+ target = on_aref_field(receiver, arguments)
+
+ bounds(node.operator_loc)
+ operator = on_op("#{node.operator}=")
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_opassign(target, operator, value)
+ end
+
+ # foo[bar] &&= baz
+ # ^^^^^^^^^^^^^^^^
+ def visit_index_and_write_node(node)
+ receiver = visit(node.receiver)
+ arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
+
+ bounds(node.location)
+ target = on_aref_field(receiver, arguments)
+
+ bounds(node.operator_loc)
+ operator = on_op("&&=")
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_opassign(target, operator, value)
+ end
+
+ # foo[bar] ||= baz
+ # ^^^^^^^^^^^^^^^^
+ def visit_index_or_write_node(node)
+ receiver = visit(node.receiver)
+ arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
+
+ bounds(node.location)
+ target = on_aref_field(receiver, arguments)
+
+ bounds(node.operator_loc)
+ operator = on_op("||=")
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_opassign(target, operator, value)
+ end
+
+ # foo[bar], = 1
+ # ^^^^^^^^
+ def visit_index_target_node(node)
+ receiver = visit(node.receiver)
+ arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
+
+ bounds(node.location)
+ on_aref_field(receiver, arguments)
+ end
+
+ # @foo
+ # ^^^^
+ def visit_instance_variable_read_node(node)
+ bounds(node.location)
+ on_var_ref(on_ivar(node.name.to_s))
+ end
+
+ # @foo = 1
+ # ^^^^^^^^
+ def visit_instance_variable_write_node(node)
+ bounds(node.name_loc)
+ target = on_var_field(on_ivar(node.name.to_s))
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_assign(target, value)
+ end
+
+ # @foo += bar
+ # ^^^^^^^^^^^
+ def visit_instance_variable_operator_write_node(node)
+ bounds(node.name_loc)
+ target = on_var_field(on_ivar(node.name.to_s))
+
+ bounds(node.operator_loc)
+ operator = on_op("#{node.operator}=")
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_opassign(target, operator, value)
+ end
+
+ # @foo &&= bar
+ # ^^^^^^^^^^^^
+ def visit_instance_variable_and_write_node(node)
+ bounds(node.name_loc)
+ target = on_var_field(on_ivar(node.name.to_s))
+
+ bounds(node.operator_loc)
+ operator = on_op("&&=")
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_opassign(target, operator, value)
+ end
+
+ # @foo ||= bar
+ # ^^^^^^^^^^^^
+ def visit_instance_variable_or_write_node(node)
+ bounds(node.name_loc)
+ target = on_var_field(on_ivar(node.name.to_s))
+
+ bounds(node.operator_loc)
+ operator = on_op("||=")
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_opassign(target, operator, value)
+ end
+
+ # @foo, = bar
+ # ^^^^
+ def visit_instance_variable_target_node(node)
+ bounds(node.location)
+ on_var_field(on_ivar(node.name.to_s))
+ end
+
+ # 1
+ # ^
+ def visit_integer_node(node)
+ visit_number_node(node) { |text| on_int(text) }
+ end
+
+ # if /foo #{bar}/ then end
+ # ^^^^^^^^^^^^
+ def visit_interpolated_match_last_line_node(node)
+ bounds(node.opening_loc)
+ on_regexp_beg(node.opening)
+
+ bounds(node.parts.first.location)
+ parts =
+ node.parts.inject(on_regexp_new) do |content, part|
+ on_regexp_add(content, visit_string_content(part))
+ end
+
+ bounds(node.closing_loc)
+ closing = on_regexp_end(node.closing)
+
+ bounds(node.location)
+ on_regexp_literal(parts, closing)
+ end
+
+ # /foo #{bar}/
+ # ^^^^^^^^^^^^
+ def visit_interpolated_regular_expression_node(node)
+ bounds(node.opening_loc)
+ on_regexp_beg(node.opening)
+
+ bounds(node.parts.first.location)
+ parts =
+ node.parts.inject(on_regexp_new) do |content, part|
+ on_regexp_add(content, visit_string_content(part))
+ end
+
+ bounds(node.closing_loc)
+ closing = on_regexp_end(node.closing)
+
+ bounds(node.location)
+ on_regexp_literal(parts, closing)
+ end
+
+ # "foo #{bar}"
+ # ^^^^^^^^^^^^
+ def visit_interpolated_string_node(node)
+ if node.opening&.start_with?("<<~")
+ heredoc = visit_heredoc_string_node(node)
+
+ bounds(node.location)
+ on_string_literal(heredoc)
+ elsif !node.heredoc? && node.parts.length > 1 && node.parts.any? { |part| (part.is_a?(StringNode) || part.is_a?(InterpolatedStringNode)) && !part.opening_loc.nil? }
+ first, *rest = node.parts
+ rest.inject(visit(first)) do |content, part|
+ concat = visit(part)
+
+ bounds(part.location)
+ on_string_concat(content, concat)
+ end
+ else
+ bounds(node.parts.first.location)
+ parts =
+ node.parts.inject(on_string_content) do |content, part|
+ on_string_add(content, visit_string_content(part))
+ end
+
+ bounds(node.location)
+ on_string_literal(parts)
+ end
+ end
+
+ # :"foo #{bar}"
+ # ^^^^^^^^^^^^^
+ def visit_interpolated_symbol_node(node)
+ bounds(node.parts.first.location)
+ parts =
+ node.parts.inject(on_string_content) do |content, part|
+ on_string_add(content, visit_string_content(part))
+ end
+
+ bounds(node.location)
+ on_dyna_symbol(parts)
+ end
+
+ # `foo #{bar}`
+ # ^^^^^^^^^^^^
+ def visit_interpolated_x_string_node(node)
+ if node.opening.start_with?("<<~")
+ heredoc = visit_heredoc_x_string_node(node)
+
+ bounds(node.location)
+ on_xstring_literal(heredoc)
+ else
+ bounds(node.parts.first.location)
+ parts =
+ node.parts.inject(on_xstring_new) do |content, part|
+ on_xstring_add(content, visit_string_content(part))
+ end
+
+ bounds(node.location)
+ on_xstring_literal(parts)
+ end
+ end
+
+ # Visit an individual part of a string-like node.
+ private def visit_string_content(part)
+ if part.is_a?(StringNode)
+ bounds(part.content_loc)
+ on_tstring_content(part.content)
+ else
+ visit(part)
+ end
+ end
+
+ # -> { it }
+ # ^^^^^^^^^
+ def visit_it_parameters_node(node)
+ end
+
+ # foo(bar: baz)
+ # ^^^^^^^^
+ def visit_keyword_hash_node(node)
+ elements = visit_all(node.elements)
+
+ bounds(node.location)
+ on_bare_assoc_hash(elements)
+ end
+
+ # def foo(**bar); end
+ # ^^^^^
+ #
+ # def foo(**); end
+ # ^^
+ def visit_keyword_rest_parameter_node(node)
+ if node.name_loc.nil?
+ bounds(node.location)
+ on_kwrest_param(nil)
+ else
+ bounds(node.name_loc)
+ name = on_ident(node.name.to_s)
+
+ bounds(node.location)
+ on_kwrest_param(name)
+ end
+ end
+
+ # -> {}
+ def visit_lambda_node(node)
+ bounds(node.operator_loc)
+ on_tlambda(node.operator)
+
+ parameters =
+ if node.parameters.is_a?(BlockParametersNode)
+ # Ripper does not track block-locals within lambdas, so we skip
+ # directly to the parameters here.
+ params =
+ if node.parameters.parameters.nil?
+ bounds(node.location)
+ on_params(nil, nil, nil, nil, nil, nil, nil)
+ else
+ visit(node.parameters.parameters)
+ end
+
+ if node.parameters.opening_loc.nil?
+ params
+ else
+ bounds(node.parameters.opening_loc)
+ on_paren(params)
+ end
+ else
+ bounds(node.location)
+ on_params(nil, nil, nil, nil, nil, nil, nil)
+ end
+
+ braces = node.opening == "{"
+ if braces
+ bounds(node.opening_loc)
+ on_tlambeg(node.opening)
+ end
+
+ body =
+ case node.body
+ when nil
+ bounds(node.location)
+ stmts = on_stmts_add(on_stmts_new, on_void_stmt)
+
+ bounds(node.location)
+ braces ? stmts : on_bodystmt(stmts, nil, nil, nil)
+ when StatementsNode
+ stmts = node.body.body
+ stmts.unshift(nil) if void_stmt?(node.parameters&.location || node.opening_loc, node.body.location, false)
+ stmts = visit_statements_node_body(stmts)
+
+ bounds(node.body.location)
+ braces ? stmts : on_bodystmt(stmts, nil, nil, nil)
+ when BeginNode
+ visit_body_node(node.opening_loc, node.body)
+ else
+ raise
+ end
+
+ bounds(node.location)
+ on_lambda(parameters, body)
+ end
+
+ # foo
+ # ^^^
+ def visit_local_variable_read_node(node)
+ bounds(node.location)
+
+ if node.name == :"0it"
+ on_vcall(on_ident(node.slice))
+ else
+ on_var_ref(on_ident(node.slice))
+ end
+ end
+
+ # foo = 1
+ # ^^^^^^^
+ def visit_local_variable_write_node(node)
+ bounds(node.name_loc)
+ target = on_var_field(on_ident(node.name_loc.slice))
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_assign(target, value)
+ end
+
+ # foo += bar
+ # ^^^^^^^^^^
+ def visit_local_variable_operator_write_node(node)
+ bounds(node.name_loc)
+ target = on_var_field(on_ident(node.name_loc.slice))
+
+ bounds(node.operator_loc)
+ operator = on_op("#{node.operator}=")
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_opassign(target, operator, value)
+ end
+
+ # foo &&= bar
+ # ^^^^^^^^^^^
+ def visit_local_variable_and_write_node(node)
+ bounds(node.name_loc)
+ target = on_var_field(on_ident(node.name_loc.slice))
+
+ bounds(node.operator_loc)
+ operator = on_op("&&=")
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_opassign(target, operator, value)
+ end
+
+ # foo ||= bar
+ # ^^^^^^^^^^^
+ def visit_local_variable_or_write_node(node)
+ bounds(node.name_loc)
+ target = on_var_field(on_ident(node.name_loc.slice))
+
+ bounds(node.operator_loc)
+ operator = on_op("||=")
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_opassign(target, operator, value)
+ end
+
+ # foo, = bar
+ # ^^^
+ def visit_local_variable_target_node(node)
+ bounds(node.location)
+ on_var_field(on_ident(node.name.to_s))
+ end
+
+ # if /foo/ then end
+ # ^^^^^
+ def visit_match_last_line_node(node)
+ bounds(node.opening_loc)
+ on_regexp_beg(node.opening)
+
+ bounds(node.content_loc)
+ tstring_content = on_tstring_content(node.content)
+
+ bounds(node.closing_loc)
+ closing = on_regexp_end(node.closing)
+
+ on_regexp_literal(on_regexp_add(on_regexp_new, tstring_content), closing)
+ end
+
+ # foo in bar
+ # ^^^^^^^^^^
+ def visit_match_predicate_node(node)
+ value = visit(node.value)
+ pattern = on_in(visit_pattern_node(node.pattern), nil, nil)
+
+ on_case(value, pattern)
+ end
+
+ # foo => bar
+ # ^^^^^^^^^^
+ def visit_match_required_node(node)
+ value = visit(node.value)
+ pattern = on_in(visit_pattern_node(node.pattern), nil, nil)
+
+ on_case(value, pattern)
+ end
+
+ # /(?<foo>foo)/ =~ bar
+ # ^^^^^^^^^^^^^^^^^^^^
+ def visit_match_write_node(node)
+ visit(node.call)
+ end
+
+ # A node that is missing from the syntax tree. This is only used in the
+ # case of a syntax error.
+ def visit_missing_node(node)
+ raise "Cannot visit missing nodes directly."
+ end
+
+ # module Foo; end
+ # ^^^^^^^^^^^^^^^
+ def visit_module_node(node)
+ constant_path =
+ if node.constant_path.is_a?(ConstantReadNode)
+ bounds(node.constant_path.location)
+ on_const_ref(on_const(node.constant_path.name.to_s))
+ else
+ visit(node.constant_path)
+ end
+
+ bodystmt = visit_body_node(node.constant_path.location, node.body, true)
+
+ bounds(node.location)
+ on_module(constant_path, bodystmt)
+ end
+
+ # (foo, bar), bar = qux
+ # ^^^^^^^^^^
+ def visit_multi_target_node(node)
+ bounds(node.location)
+ targets = visit_multi_target_node_targets(node.lefts, node.rest, node.rights, true)
+
+ if node.lparen_loc.nil?
+ targets
+ else
+ bounds(node.lparen_loc)
+ on_mlhs_paren(targets)
+ end
+ end
+
+ # Visit the targets of a multi-target node.
+ private def visit_multi_target_node_targets(lefts, rest, rights, skippable)
+ if skippable && lefts.length == 1 && lefts.first.is_a?(MultiTargetNode) && rest.nil? && rights.empty?
+ return visit(lefts.first)
+ end
+
+ mlhs = on_mlhs_new
+
+ lefts.each do |left|
+ bounds(left.location)
+ mlhs = on_mlhs_add(mlhs, visit(left))
+ end
+
+ case rest
+ when nil
+ # do nothing
+ when ImplicitRestNode
+ # these do not get put into the generated tree
+ bounds(rest.location)
+ on_excessed_comma
+ else
+ bounds(rest.location)
+ mlhs = on_mlhs_add_star(mlhs, visit(rest))
+ end
+
+ if rights.any?
+ bounds(rights.first.location)
+ post = on_mlhs_new
+
+ rights.each do |right|
+ bounds(right.location)
+ post = on_mlhs_add(post, visit(right))
+ end
+
+ mlhs = on_mlhs_add_post(mlhs, post)
+ end
+
+ mlhs
+ end
+
+ # foo, bar = baz
+ # ^^^^^^^^^^^^^^
+ def visit_multi_write_node(node)
+ bounds(node.location)
+ targets = visit_multi_target_node_targets(node.lefts, node.rest, node.rights, true)
+
+ unless node.lparen_loc.nil?
+ bounds(node.lparen_loc)
+ targets = on_mlhs_paren(targets)
+ end
+
+ value = visit_write_value(node.value)
+
+ bounds(node.location)
+ on_massign(targets, value)
+ end
+
+ # next
+ # ^^^^
+ #
+ # next foo
+ # ^^^^^^^^
+ def visit_next_node(node)
+ if node.arguments.nil?
+ bounds(node.location)
+ on_next(on_args_new)
+ else
+ arguments = visit(node.arguments)
+
+ bounds(node.location)
+ on_next(arguments)
+ end
+ end
+
+ # nil
+ # ^^^
+ def visit_nil_node(node)
+ bounds(node.location)
+ on_var_ref(on_kw("nil"))
+ end
+
+ # def foo(**nil); end
+ # ^^^^^
+ def visit_no_keywords_parameter_node(node)
+ bounds(node.location)
+ on_nokw_param(nil)
+
+ :nil
+ end
+
+ # -> { _1 + _2 }
+ # ^^^^^^^^^^^^^^
+ def visit_numbered_parameters_node(node)
+ end
+
+ # $1
+ # ^^
+ def visit_numbered_reference_read_node(node)
+ bounds(node.location)
+ on_backref(node.slice)
+ end
+
+ # def foo(bar: baz); end
+ # ^^^^^^^^
+ def visit_optional_keyword_parameter_node(node)
+ bounds(node.name_loc)
+ name = on_label("#{node.name}:")
+ value = visit(node.value)
+
+ [name, value]
+ end
+
+ # def foo(bar = 1); end
+ # ^^^^^^^
+ def visit_optional_parameter_node(node)
+ bounds(node.name_loc)
+ name = visit_token(node.name.to_s)
+ value = visit(node.value)
+
+ [name, value]
+ end
+
+ # a or b
+ # ^^^^^^
+ def visit_or_node(node)
+ left = visit(node.left)
+ right = visit(node.right)
+
+ bounds(node.location)
+ on_binary(left, node.operator.to_sym, right)
+ end
+
+ # def foo(bar, *baz); end
+ # ^^^^^^^^^
+ def visit_parameters_node(node)
+ requireds = node.requireds.map { |required| required.is_a?(MultiTargetNode) ? visit_destructured_parameter_node(required) : visit(required) } if node.requireds.any?
+ optionals = visit_all(node.optionals) if node.optionals.any?
+ rest = visit(node.rest)
+ posts = node.posts.map { |post| post.is_a?(MultiTargetNode) ? visit_destructured_parameter_node(post) : visit(post) } if node.posts.any?
+ keywords = visit_all(node.keywords) if node.keywords.any?
+ keyword_rest = visit(node.keyword_rest)
+ block = visit(node.block)
+
+ bounds(node.location)
+ on_params(requireds, optionals, rest, posts, keywords, keyword_rest, block)
+ end
+
+ # Visit a destructured positional parameter node.
+ private def visit_destructured_parameter_node(node)
+ bounds(node.location)
+ targets = visit_multi_target_node_targets(node.lefts, node.rest, node.rights, false)
+
+ bounds(node.lparen_loc)
+ on_mlhs_paren(targets)
+ end
+
+ # ()
+ # ^^
+ #
+ # (1)
+ # ^^^
+ def visit_parentheses_node(node)
+ body =
+ if node.body.nil?
+ on_stmts_add(on_stmts_new, on_void_stmt)
+ else
+ visit(node.body)
+ end
+
+ bounds(node.location)
+ on_paren(body)
+ end
+
+ # foo => ^(bar)
+ # ^^^^^^
+ def visit_pinned_expression_node(node)
+ expression = visit(node.expression)
+
+ bounds(node.location)
+ on_begin(expression)
+ end
+
+ # foo = 1 and bar => ^foo
+ # ^^^^
+ def visit_pinned_variable_node(node)
+ visit(node.variable)
+ end
+
+ # END {}
+ # ^^^^^^
+ def visit_post_execution_node(node)
+ statements =
+ if node.statements.nil?
+ bounds(node.location)
+ on_stmts_add(on_stmts_new, on_void_stmt)
+ else
+ visit(node.statements)
+ end
+
+ bounds(node.location)
+ on_END(statements)
+ end
+
+ # BEGIN {}
+ # ^^^^^^^^
+ def visit_pre_execution_node(node)
+ statements =
+ if node.statements.nil?
+ bounds(node.location)
+ on_stmts_add(on_stmts_new, on_void_stmt)
+ else
+ visit(node.statements)
+ end
+
+ bounds(node.location)
+ on_BEGIN(statements)
+ end
+
+ # The top-level program node.
+ def visit_program_node(node)
+ body = node.statements.body
+ body << nil if body.empty?
+ statements = visit_statements_node_body(body)
+
+ bounds(node.location)
+ on_program(statements)
+ end
+
+ # 0..5
+ # ^^^^
+ def visit_range_node(node)
+ left = visit(node.left)
+ right = visit(node.right)
+
+ bounds(node.location)
+ if node.exclude_end?
+ on_dot3(left, right)
+ else
+ on_dot2(left, right)
+ end
+ end
+
+ # 1r
+ # ^^
+ def visit_rational_node(node)
+ visit_number_node(node) { |text| on_rational(text) }
+ end
+
+ # redo
+ # ^^^^
+ def visit_redo_node(node)
+ bounds(node.location)
+ on_redo
+ end
+
+ # /foo/
+ # ^^^^^
+ def visit_regular_expression_node(node)
+ bounds(node.opening_loc)
+ on_regexp_beg(node.opening)
+
+ if node.content.empty?
+ bounds(node.closing_loc)
+ closing = on_regexp_end(node.closing)
+
+ on_regexp_literal(on_regexp_new, closing)
+ else
+ bounds(node.content_loc)
+ tstring_content = on_tstring_content(node.content)
+
+ bounds(node.closing_loc)
+ closing = on_regexp_end(node.closing)
+
+ on_regexp_literal(on_regexp_add(on_regexp_new, tstring_content), closing)
+ end
+ end
+
+ # def foo(bar:); end
+ # ^^^^
+ def visit_required_keyword_parameter_node(node)
+ bounds(node.name_loc)
+ [on_label("#{node.name}:"), false]
+ end
+
+ # def foo(bar); end
+ # ^^^
+ def visit_required_parameter_node(node)
+ bounds(node.location)
+ on_ident(node.name.to_s)
+ end
+
+ # foo rescue bar
+ # ^^^^^^^^^^^^^^
+ def visit_rescue_modifier_node(node)
+ expression = visit_write_value(node.expression)
+ rescue_expression = visit(node.rescue_expression)
+
+ bounds(node.location)
+ on_rescue_mod(expression, rescue_expression)
+ end
+
+ # begin; rescue; end
+ # ^^^^^^^
+ def visit_rescue_node(node)
+ exceptions =
+ case node.exceptions.length
+ when 0
+ nil
+ when 1
+ if (exception = node.exceptions.first).is_a?(SplatNode)
+ bounds(exception.location)
+ on_mrhs_add_star(on_mrhs_new, visit(exception))
+ else
+ [visit(node.exceptions.first)]
+ end
+ else
+ bounds(node.location)
+ length = node.exceptions.length
+
+ node.exceptions.each_with_index.inject(on_args_new) do |mrhs, (exception, index)|
+ arg = visit(exception)
+
+ bounds(exception.location)
+ mrhs = on_mrhs_new_from_args(mrhs) if index == length - 1
+
+ if exception.is_a?(SplatNode)
+ if index == length - 1
+ on_mrhs_add_star(mrhs, arg)
+ else
+ on_args_add_star(mrhs, arg)
+ end
+ else
+ if index == length - 1
+ on_mrhs_add(mrhs, arg)
+ else
+ on_args_add(mrhs, arg)
+ end
+ end
+ end
+ end
+
+ reference = visit(node.reference)
+ statements =
+ if node.statements.nil?
+ bounds(node.location)
+ on_stmts_add(on_stmts_new, on_void_stmt)
+ else
+ visit(node.statements)
+ end
+
+ consequent = visit(node.consequent)
+
+ bounds(node.location)
+ on_rescue(exceptions, reference, statements, consequent)
+ end
+
+ # def foo(*bar); end
+ # ^^^^
+ #
+ # def foo(*); end
+ # ^
+ def visit_rest_parameter_node(node)
+ if node.name_loc.nil?
+ bounds(node.location)
+ on_rest_param(nil)
+ else
+ bounds(node.name_loc)
+ on_rest_param(visit_token(node.name.to_s))
+ end
+ end
+
+ # retry
+ # ^^^^^
+ def visit_retry_node(node)
+ bounds(node.location)
+ on_retry
+ end
+
+ # return
+ # ^^^^^^
+ #
+ # return 1
+ # ^^^^^^^^
+ def visit_return_node(node)
+ if node.arguments.nil?
+ bounds(node.location)
+ on_return0
+ else
+ arguments = visit(node.arguments)
+
+ bounds(node.location)
+ on_return(arguments)
+ end
+ end
+
+ # self
+ # ^^^^
+ def visit_self_node(node)
+ bounds(node.location)
+ on_var_ref(on_kw("self"))
+ end
+
+ # A shareable constant.
+ def visit_shareable_constant_node(node)
+ visit(node.write)
+ end
+
+ # class << self; end
+ # ^^^^^^^^^^^^^^^^^^
+ def visit_singleton_class_node(node)
+ expression = visit(node.expression)
+ bodystmt = visit_body_node(node.body&.location || node.end_keyword_loc, node.body)
+
+ bounds(node.location)
+ on_sclass(expression, bodystmt)
+ end
+
+ # __ENCODING__
+ # ^^^^^^^^^^^^
+ def visit_source_encoding_node(node)
+ bounds(node.location)
+ on_var_ref(on_kw("__ENCODING__"))
+ end
+
+ # __FILE__
+ # ^^^^^^^^
+ def visit_source_file_node(node)
+ bounds(node.location)
+ on_var_ref(on_kw("__FILE__"))
+ end
+
+ # __LINE__
+ # ^^^^^^^^
+ def visit_source_line_node(node)
+ bounds(node.location)
+ on_var_ref(on_kw("__LINE__"))
+ end
+
+ # foo(*bar)
+ # ^^^^
+ #
+ # def foo((bar, *baz)); end
+ # ^^^^
+ #
+ # def foo(*); bar(*); end
+ # ^
+ def visit_splat_node(node)
+ visit(node.expression)
+ end
+
+ # A list of statements.
+ def visit_statements_node(node)
+ bounds(node.location)
+ visit_statements_node_body(node.body)
+ end
+
+ # Visit the list of statements of a statements node. We support nil
+ # statements in the list. This would normally not be allowed by the
+ # structure of the prism parse tree, but we manually add them here so that
+ # we can mirror Ripper's void stmt.
+ private def visit_statements_node_body(body)
+ body.inject(on_stmts_new) do |stmts, stmt|
+ on_stmts_add(stmts, stmt.nil? ? on_void_stmt : visit(stmt))
+ end
+ end
+
+ # "foo"
+ # ^^^^^
+ def visit_string_node(node)
+ if (content = node.content).empty?
+ bounds(node.location)
+ on_string_literal(on_string_content)
+ elsif (opening = node.opening) == "?"
+ bounds(node.location)
+ on_CHAR("?#{node.content}")
+ elsif opening.start_with?("<<~")
+ heredoc = visit_heredoc_string_node(node.to_interpolated)
+
+ bounds(node.location)
+ on_string_literal(heredoc)
+ else
+ bounds(node.content_loc)
+ tstring_content = on_tstring_content(content)
+
+ bounds(node.location)
+ on_string_literal(on_string_add(on_string_content, tstring_content))
+ end
+ end
+
+ # Ripper gives back the escaped string content but strips out the common
+ # leading whitespace. Prism gives back the unescaped string content and
+ # a location for the escaped string content. Unfortunately these don't
+ # work well together, so here we need to re-derive the common leading
+ # whitespace.
+ private def visit_heredoc_node_whitespace(parts)
+ common_whitespace = nil
+ dedent_next = true
+
+ parts.each do |part|
+ if part.is_a?(StringNode)
+ if dedent_next && !(content = part.content).chomp.empty?
+ common_whitespace = [
+ common_whitespace || Float::INFINITY,
+ content[/\A\s*/].each_char.inject(0) do |part_whitespace, char|
+ char == "\t" ? ((part_whitespace / 8 + 1) * 8) : (part_whitespace + 1)
+ end
+ ].min
+ end
+
+ dedent_next = true
+ else
+ dedent_next = false
+ end
+ end
+
+ common_whitespace || 0
+ end
+
+ # Visit a string that is expressed using a <<~ heredoc.
+ private def visit_heredoc_node(parts, base)
+ common_whitespace = visit_heredoc_node_whitespace(parts)
+
+ if common_whitespace == 0
+ bounds(parts.first.location)
+
+ string = []
+ result = base
+
+ parts.each do |part|
+ if part.is_a?(StringNode)
+ if string.empty?
+ string = [part]
+ else
+ string << part
+ end
+ else
+ unless string.empty?
+ bounds(string[0].location)
+ result = yield result, on_tstring_content(string.map(&:content).join)
+ string = []
+ end
+
+ result = yield result, visit(part)
+ end
+ end
+
+ unless string.empty?
+ bounds(string[0].location)
+ result = yield result, on_tstring_content(string.map(&:content).join)
+ end
+
+ result
+ else
+ bounds(parts.first.location)
+ result =
+ parts.inject(base) do |string_content, part|
+ yield string_content, visit_string_content(part)
+ end
+
+ bounds(parts.first.location)
+ on_heredoc_dedent(result, common_whitespace)
+ end
+ end
+
+ # Visit a heredoc node that is representing a string.
+ private def visit_heredoc_string_node(node)
+ bounds(node.opening_loc)
+ on_heredoc_beg(node.opening)
+
+ bounds(node.location)
+ result =
+ visit_heredoc_node(node.parts, on_string_content) do |parts, part|
+ on_string_add(parts, part)
+ end
+
+ bounds(node.closing_loc)
+ on_heredoc_end(node.closing)
+
+ result
+ end
+
+ # Visit a heredoc node that is representing an xstring.
+ private def visit_heredoc_x_string_node(node)
+ bounds(node.opening_loc)
+ on_heredoc_beg(node.opening)
+
+ bounds(node.location)
+ result =
+ visit_heredoc_node(node.parts, on_xstring_new) do |parts, part|
+ on_xstring_add(parts, part)
+ end
+
+ bounds(node.closing_loc)
+ on_heredoc_end(node.closing)
+
+ result
+ end
+
+ # super(foo)
+ # ^^^^^^^^^^
+ def visit_super_node(node)
+ arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.rparen_loc || node.location))
+
+ if !node.lparen_loc.nil?
+ bounds(node.lparen_loc)
+ arguments = on_arg_paren(arguments)
+ end
+
+ bounds(node.location)
+ call = on_super(arguments)
+
+ if block.nil?
+ call
+ else
+ bounds(node.block.location)
+ on_method_add_block(call, block)
+ end
+ end
+
+ # :foo
+ # ^^^^
+ def visit_symbol_node(node)
+ if (opening = node.opening)&.match?(/^%s|['"]:?$/)
+ bounds(node.value_loc)
+ content = on_string_content
+
+ if !(value = node.value).empty?
+ content = on_string_add(content, on_tstring_content(value))
+ end
+
+ on_dyna_symbol(content)
+ elsif (closing = node.closing) == ":"
+ bounds(node.location)
+ on_label("#{node.value}:")
+ elsif opening.nil? && node.closing_loc.nil?
+ bounds(node.value_loc)
+ on_symbol_literal(visit_token(node.value))
+ else
+ bounds(node.value_loc)
+ on_symbol_literal(on_symbol(visit_token(node.value)))
+ end
+ end
+
+ # true
+ # ^^^^
+ def visit_true_node(node)
+ bounds(node.location)
+ on_var_ref(on_kw("true"))
+ end
+
+ # undef foo
+ # ^^^^^^^^^
+ def visit_undef_node(node)
+ names = visit_all(node.names)
+
+ bounds(node.location)
+ on_undef(names)
+ end
+
+ # unless foo; bar end
+ # ^^^^^^^^^^^^^^^^^^^
+ #
+ # bar unless foo
+ # ^^^^^^^^^^^^^^
+ def visit_unless_node(node)
+ if node.statements.nil? || (node.predicate.location.start_offset < node.statements.location.start_offset)
+ predicate = visit(node.predicate)
+ statements =
+ if node.statements.nil?
+ bounds(node.location)
+ on_stmts_add(on_stmts_new, on_void_stmt)
+ else
+ visit(node.statements)
+ end
+ consequent = visit(node.consequent)
+
+ bounds(node.location)
+ on_unless(predicate, statements, consequent)
+ else
+ statements = visit(node.statements.body.first)
+ predicate = visit(node.predicate)
+
+ bounds(node.location)
+ on_unless_mod(predicate, statements)
+ end
+ end
+
+ # until foo; bar end
+ # ^^^^^^^^^^^^^^^^^
+ #
+ # bar until foo
+ # ^^^^^^^^^^^^^
+ def visit_until_node(node)
+ if node.statements.nil? || (node.predicate.location.start_offset < node.statements.location.start_offset)
+ predicate = visit(node.predicate)
+ statements =
+ if node.statements.nil?
+ bounds(node.location)
+ on_stmts_add(on_stmts_new, on_void_stmt)
+ else
+ visit(node.statements)
+ end
+
+ bounds(node.location)
+ on_until(predicate, statements)
+ else
+ statements = visit(node.statements.body.first)
+ predicate = visit(node.predicate)
+
+ bounds(node.location)
+ on_until_mod(predicate, statements)
+ end
+ end
+
+ # case foo; when bar; end
+ # ^^^^^^^^^^^^^
+ def visit_when_node(node)
+ # This is a special case where we're not going to call on_when directly
+ # because we don't have access to the consequent. Instead, we'll return
+ # the component parts and let the parent node handle it.
+ conditions = visit_arguments(node.conditions)
+ statements =
+ if node.statements.nil?
+ bounds(node.location)
+ on_stmts_add(on_stmts_new, on_void_stmt)
+ else
+ visit(node.statements)
+ end
+
+ [conditions, statements]
+ end
+
+ # while foo; bar end
+ # ^^^^^^^^^^^^^^^^^^
+ #
+ # bar while foo
+ # ^^^^^^^^^^^^^
+ def visit_while_node(node)
+ if node.statements.nil? || (node.predicate.location.start_offset < node.statements.location.start_offset)
+ predicate = visit(node.predicate)
+ statements =
+ if node.statements.nil?
+ bounds(node.location)
+ on_stmts_add(on_stmts_new, on_void_stmt)
+ else
+ visit(node.statements)
+ end
+
+ bounds(node.location)
+ on_while(predicate, statements)
+ else
+ statements = visit(node.statements.body.first)
+ predicate = visit(node.predicate)
+
+ bounds(node.location)
+ on_while_mod(predicate, statements)
+ end
+ end
+
+ # `foo`
+ # ^^^^^
+ def visit_x_string_node(node)
+ if node.unescaped.empty?
+ bounds(node.location)
+ on_xstring_literal(on_xstring_new)
+ elsif node.opening.start_with?("<<~")
+ heredoc = visit_heredoc_x_string_node(node.to_interpolated)
+
+ bounds(node.location)
+ on_xstring_literal(heredoc)
+ else
+ bounds(node.content_loc)
+ content = on_tstring_content(node.content)
+
+ bounds(node.location)
+ on_xstring_literal(on_xstring_add(on_xstring_new, content))
+ end
+ end
+
+ # yield
+ # ^^^^^
+ #
+ # yield 1
+ # ^^^^^^^
+ def visit_yield_node(node)
+ if node.arguments.nil? && node.lparen_loc.nil?
+ bounds(node.location)
+ on_yield0
+ else
+ arguments =
+ if node.arguments.nil?
+ bounds(node.location)
+ on_args_new
+ else
+ visit(node.arguments)
+ end
+
+ unless node.lparen_loc.nil?
+ bounds(node.lparen_loc)
+ arguments = on_paren(arguments)
+ end
+
+ bounds(node.location)
+ on_yield(arguments)
+ end
+ end
+
+ private
+
+ # Lazily initialize the parse result.
+ def result
+ @result ||= Prism.parse(source)
+ end
+
+ ##########################################################################
+ # Helpers
+ ##########################################################################
+
+ # Returns true if there is a comma between the two locations.
+ def trailing_comma?(left, right)
+ source.byteslice(left.end_offset...right.start_offset).include?(",")
+ end
+
+ # Returns true if there is a semicolon between the two locations.
+ def void_stmt?(left, right, allow_newline)
+ pattern = allow_newline ? /[;\n]/ : /;/
+ source.byteslice(left.end_offset...right.start_offset).match?(pattern)
+ end
+
+ # Visit the string content of a particular node. This method is used to
+ # split into the various token types.
+ def visit_token(token, allow_keywords = true)
+ case token
+ when "."
+ on_period(token)
+ when "`"
+ on_backtick(token)
+ when *(allow_keywords ? KEYWORDS : [])
+ on_kw(token)
+ when /^_/
+ on_ident(token)
+ when /^[[:upper:]]\w*$/
+ on_const(token)
+ when /^@@/
+ on_cvar(token)
+ when /^@/
+ on_ivar(token)
+ when /^\$/
+ on_gvar(token)
+ when /^[[:punct:]]/
+ on_op(token)
+ else
+ on_ident(token)
+ end
+ end
+
+ # Visit a node that represents a number. We need to explicitly handle the
+ # unary - operator.
+ def visit_number_node(node)
+ slice = node.slice
+ location = node.location
+
+ if slice[0] == "-"
+ bounds(location.copy(start_offset: location.start_offset + 1))
+ value = yield slice[1..-1]
+
+ bounds(node.location)
+ on_unary(:-@, value)
+ else
+ bounds(location)
+ yield slice
+ end
+ end
+
+ # Visit a node that represents a write value. This is used to handle the
+ # special case of an implicit array that is generated without brackets.
+ def visit_write_value(node)
+ if node.is_a?(ArrayNode) && node.opening_loc.nil?
+ elements = node.elements
+ length = elements.length
+
+ bounds(elements.first.location)
+ elements.each_with_index.inject((elements.first.is_a?(SplatNode) && length == 1) ? on_mrhs_new : on_args_new) do |args, (element, index)|
+ arg = visit(element)
+ bounds(element.location)
+
+ if index == length - 1
+ if element.is_a?(SplatNode)
+ mrhs = index == 0 ? args : on_mrhs_new_from_args(args)
+ on_mrhs_add_star(mrhs, arg)
+ else
+ on_mrhs_add(on_mrhs_new_from_args(args), arg)
+ end
+ else
+ case element
+ when BlockArgumentNode
+ on_args_add_block(args, arg)
+ when SplatNode
+ on_args_add_star(args, arg)
+ else
+ on_args_add(args, arg)
+ end
+ end
+ end
+ else
+ visit(node)
+ end
+ end
+
+ # This method is responsible for updating lineno and column information
+ # to reflect the current node.
+ #
+ # This method could be drastically improved with some caching on the start
+ # of every line, but for now it's good enough.
+ def bounds(location)
+ @lineno = location.start_line
+ @column = location.start_column
+ end
+
+ ##########################################################################
+ # Ripper interface
+ ##########################################################################
+
+ # :stopdoc:
+ def _dispatch_0; end
+ def _dispatch_1(_); end
+ def _dispatch_2(_, _); end
+ def _dispatch_3(_, _, _); end
+ def _dispatch_4(_, _, _, _); end
+ def _dispatch_5(_, _, _, _, _); end
+ def _dispatch_7(_, _, _, _, _, _, _); end
+ # :startdoc:
+
+ #
+ # Parser Events
+ #
+
+ PARSER_EVENT_TABLE.each do |id, arity|
+ alias_method "on_#{id}", "_dispatch_#{arity}"
+ end
+
+ # This method is called when weak warning is produced by the parser.
+ # +fmt+ and +args+ is printf style.
+ def warn(fmt, *args)
+ end
+
+ # This method is called when strong warning is produced by the parser.
+ # +fmt+ and +args+ is printf style.
+ def warning(fmt, *args)
+ end
+
+ # This method is called when the parser found syntax error.
+ def compile_error(msg)
+ end
+
+ #
+ # Scanner Events
+ #
+
+ SCANNER_EVENTS.each do |id|
+ alias_method "on_#{id}", :_dispatch_1
+ end
+
+ # This method is provided by the Ripper C extension. It is called when a
+ # string needs to be dedented because of a tilde heredoc. It is expected
+ # that it will modify the string in place and return the number of bytes
+ # that were removed.
+ def dedent_string(string, width)
+ whitespace = 0
+ cursor = 0
+
+ while cursor < string.length && string[cursor].match?(/\s/) && whitespace < width
+ if string[cursor] == "\t"
+ whitespace = ((whitespace / 8 + 1) * 8)
+ break if whitespace > width
+ else
+ whitespace += 1
+ end
+
+ cursor += 1
+ end
+
+ string.replace(string[cursor..])
+ cursor
+ end
+ end
+ end
+end
diff --git a/lib/prism/translation/ripper/sexp.rb b/lib/prism/translation/ripper/sexp.rb
new file mode 100644
index 0000000000..dc26a639a3
--- /dev/null
+++ b/lib/prism/translation/ripper/sexp.rb
@@ -0,0 +1,125 @@
+# frozen_string_literal: true
+
+require_relative "../ripper"
+
+module Prism
+ module Translation
+ class Ripper
+ # This class mirrors the ::Ripper::SexpBuilder subclass of ::Ripper that
+ # returns the arrays of [type, *children].
+ class SexpBuilder < Ripper
+ # :stopdoc:
+
+ attr_reader :error
+
+ private
+
+ def dedent_element(e, width)
+ if (n = dedent_string(e[1], width)) > 0
+ e[2][1] += n
+ end
+ e
+ end
+
+ def on_heredoc_dedent(val, width)
+ sub = proc do |cont|
+ cont.map! do |e|
+ if Array === e
+ case e[0]
+ when :@tstring_content
+ e = dedent_element(e, width)
+ when /_add\z/
+ e[1] = sub[e[1]]
+ end
+ elsif String === e
+ dedent_string(e, width)
+ end
+ e
+ end
+ end
+ sub[val]
+ val
+ end
+
+ events = private_instance_methods(false).grep(/\Aon_/) {$'.to_sym}
+ (PARSER_EVENTS - events).each do |event|
+ module_eval(<<-End, __FILE__, __LINE__ + 1)
+ def on_#{event}(*args)
+ args.unshift :#{event}
+ end
+ End
+ end
+
+ SCANNER_EVENTS.each do |event|
+ module_eval(<<-End, __FILE__, __LINE__ + 1)
+ def on_#{event}(tok)
+ [:@#{event}, tok, [lineno(), column()]]
+ end
+ End
+ end
+
+ def on_error(mesg)
+ @error = mesg
+ end
+ remove_method :on_parse_error
+ alias on_parse_error on_error
+ alias compile_error on_error
+
+ # :startdoc:
+ end
+
+ # This class mirrors the ::Ripper::SexpBuilderPP subclass of ::Ripper that
+ # returns the same values as ::Ripper::SexpBuilder except with a couple of
+ # niceties that flatten linked lists into arrays.
+ class SexpBuilderPP < SexpBuilder
+ # :stopdoc:
+
+ private
+
+ def on_heredoc_dedent(val, width)
+ val.map! do |e|
+ next e if Symbol === e and /_content\z/ =~ e
+ if Array === e and e[0] == :@tstring_content
+ e = dedent_element(e, width)
+ elsif String === e
+ dedent_string(e, width)
+ end
+ e
+ end
+ val
+ end
+
+ def _dispatch_event_new
+ []
+ end
+
+ def _dispatch_event_push(list, item)
+ list.push item
+ list
+ end
+
+ def on_mlhs_paren(list)
+ [:mlhs, *list]
+ end
+
+ def on_mlhs_add_star(list, star)
+ list.push([:rest_param, star])
+ end
+
+ def on_mlhs_add_post(list, post)
+ list.concat(post)
+ end
+
+ PARSER_EVENT_TABLE.each do |event, arity|
+ if /_new\z/ =~ event and arity == 0
+ alias_method "on_#{event}", :_dispatch_event_new
+ elsif /_add\z/ =~ event
+ alias_method "on_#{event}", :_dispatch_event_push
+ end
+ end
+
+ # :startdoc:
+ end
+ end
+ end
+end
diff --git a/lib/prism/translation/ripper/shim.rb b/lib/prism/translation/ripper/shim.rb
new file mode 100644
index 0000000000..10e21cd16a
--- /dev/null
+++ b/lib/prism/translation/ripper/shim.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+# This writes the prism ripper translation into the Ripper constant so that
+# users can transparently use Ripper without any changes.
+Ripper = Prism::Translation::Ripper
diff --git a/lib/prism/translation/ruby_parser.rb b/lib/prism/translation/ruby_parser.rb
new file mode 100644
index 0000000000..a8692db5ea
--- /dev/null
+++ b/lib/prism/translation/ruby_parser.rb
@@ -0,0 +1,1576 @@
+# frozen_string_literal: true
+
+require "ruby_parser"
+
+module Prism
+ module Translation
+ # This module is the entry-point for converting a prism syntax tree into the
+ # seattlerb/ruby_parser gem's syntax tree.
+ class RubyParser
+ # A prism visitor that builds Sexp objects.
+ class Compiler < ::Prism::Compiler
+ # This is the name of the file that we are compiling. We set it on every
+ # Sexp object that is generated, and also use it to compile __FILE__
+ # nodes.
+ attr_reader :file
+
+ # Class variables will change their type based on if they are inside of
+ # a method definition or not, so we need to track that state.
+ attr_reader :in_def
+
+ # Some nodes will change their representation if they are inside of a
+ # pattern, so we need to track that state.
+ attr_reader :in_pattern
+
+ # Initialize a new compiler with the given file name.
+ def initialize(file, in_def: false, in_pattern: false)
+ @file = file
+ @in_def = in_def
+ @in_pattern = in_pattern
+ end
+
+ # alias foo bar
+ # ^^^^^^^^^^^^^
+ def visit_alias_method_node(node)
+ s(node, :alias, visit(node.new_name), visit(node.old_name))
+ end
+
+ # alias $foo $bar
+ # ^^^^^^^^^^^^^^^
+ def visit_alias_global_variable_node(node)
+ s(node, :valias, node.new_name.name, node.old_name.name)
+ end
+
+ # foo => bar | baz
+ # ^^^^^^^^^
+ def visit_alternation_pattern_node(node)
+ s(node, :or, visit(node.left), visit(node.right))
+ end
+
+ # a and b
+ # ^^^^^^^
+ def visit_and_node(node)
+ s(node, :and, visit(node.left), visit(node.right))
+ end
+
+ # []
+ # ^^
+ def visit_array_node(node)
+ if in_pattern
+ s(node, :array_pat, nil).concat(visit_all(node.elements))
+ else
+ s(node, :array).concat(visit_all(node.elements))
+ end
+ end
+
+ # foo => [bar]
+ # ^^^^^
+ def visit_array_pattern_node(node)
+ if node.constant.nil? && node.requireds.empty? && node.rest.nil? && node.posts.empty?
+ s(node, :array_pat)
+ else
+ result = s(node, :array_pat, visit_pattern_constant(node.constant)).concat(visit_all(node.requireds))
+
+ case node.rest
+ when SplatNode
+ result << :"*#{node.rest.expression&.name}"
+ when ImplicitRestNode
+ result << :*
+
+ # This doesn't make any sense at all, but since we're trying to
+ # replicate the behavior directly, we'll copy it.
+ result.line(666)
+ end
+
+ result.concat(visit_all(node.posts))
+ end
+ end
+
+ # foo(bar)
+ # ^^^
+ def visit_arguments_node(node)
+ raise "Cannot visit arguments directly"
+ end
+
+ # { a: 1 }
+ # ^^^^
+ def visit_assoc_node(node)
+ [visit(node.key), visit(node.value)]
+ end
+
+ # def foo(**); bar(**); end
+ # ^^
+ #
+ # { **foo }
+ # ^^^^^
+ def visit_assoc_splat_node(node)
+ if node.value.nil?
+ [s(node, :kwsplat)]
+ else
+ [s(node, :kwsplat, visit(node.value))]
+ end
+ end
+
+ # $+
+ # ^^
+ def visit_back_reference_read_node(node)
+ s(node, :back_ref, node.name.name.delete_prefix("$").to_sym)
+ end
+
+ # begin end
+ # ^^^^^^^^^
+ def visit_begin_node(node)
+ result = node.statements.nil? ? s(node, :nil) : visit(node.statements)
+
+ if !node.rescue_clause.nil?
+ if !node.statements.nil?
+ result = s(node.statements, :rescue, result, visit(node.rescue_clause))
+ else
+ result = s(node.rescue_clause, :rescue, visit(node.rescue_clause))
+ end
+
+ current = node.rescue_clause
+ until (current = current.consequent).nil?
+ result << visit(current)
+ end
+ end
+
+ if !node.else_clause&.statements.nil?
+ result << visit(node.else_clause)
+ end
+
+ if !node.ensure_clause.nil?
+ if !node.statements.nil? || !node.rescue_clause.nil? || !node.else_clause.nil?
+ result = s(node.statements || node.rescue_clause || node.else_clause || node.ensure_clause, :ensure, result, visit(node.ensure_clause))
+ else
+ result = s(node.ensure_clause, :ensure, visit(node.ensure_clause))
+ end
+ end
+
+ result
+ end
+
+ # foo(&bar)
+ # ^^^^
+ def visit_block_argument_node(node)
+ s(node, :block_pass).tap do |result|
+ result << visit(node.expression) unless node.expression.nil?
+ end
+ end
+
+ # foo { |; bar| }
+ # ^^^
+ def visit_block_local_variable_node(node)
+ node.name
+ end
+
+ # A block on a keyword or method call.
+ def visit_block_node(node)
+ s(node, :block_pass, visit(node.expression))
+ end
+
+ # def foo(&bar); end
+ # ^^^^
+ def visit_block_parameter_node(node)
+ :"&#{node.name}"
+ end
+
+ # A block's parameters.
+ def visit_block_parameters_node(node)
+ # If this block parameters has no parameters and is using pipes, then
+ # it inherits its location from its shadow locals, even if they're not
+ # on the same lines as the pipes.
+ shadow_loc = true
+
+ result =
+ if node.parameters.nil?
+ s(node, :args)
+ else
+ shadow_loc = false
+ visit(node.parameters)
+ end
+
+ if node.opening == "("
+ result.line = node.opening_loc.start_line
+ result.line_max = node.closing_loc.end_line
+ shadow_loc = false
+ end
+
+ if node.locals.any?
+ shadow = s(node, :shadow).concat(visit_all(node.locals))
+ shadow.line = node.locals.first.location.start_line
+ shadow.line_max = node.locals.last.location.end_line
+ result << shadow
+
+ if shadow_loc
+ result.line = shadow.line
+ result.line_max = shadow.line_max
+ end
+ end
+
+ result
+ end
+
+ # break
+ # ^^^^^
+ #
+ # break foo
+ # ^^^^^^^^^
+ def visit_break_node(node)
+ if node.arguments.nil?
+ s(node, :break)
+ elsif node.arguments.arguments.length == 1
+ s(node, :break, visit(node.arguments.arguments.first))
+ else
+ s(node, :break, s(node.arguments, :array).concat(visit_all(node.arguments.arguments)))
+ end
+ end
+
+ # foo
+ # ^^^
+ #
+ # foo.bar
+ # ^^^^^^^
+ #
+ # foo.bar() {}
+ # ^^^^^^^^^^^^
+ def visit_call_node(node)
+ case node.name
+ when :!~
+ return s(node, :not, visit(node.copy(name: :"=~")))
+ when :=~
+ if node.arguments&.arguments&.length == 1 && node.block.nil?
+ case node.receiver
+ when StringNode
+ return s(node, :match3, visit(node.arguments.arguments.first), visit(node.receiver))
+ when RegularExpressionNode, InterpolatedRegularExpressionNode
+ return s(node, :match2, visit(node.receiver), visit(node.arguments.arguments.first))
+ end
+ end
+ end
+
+ type = node.attribute_write? ? :attrasgn : :call
+ type = :"safe_#{type}" if node.safe_navigation?
+
+ arguments = node.arguments&.arguments || []
+ write_value = arguments.pop if type == :attrasgn
+ block = node.block
+
+ if block.is_a?(BlockArgumentNode)
+ arguments << block
+ block = nil
+ end
+
+ result = s(node, type, visit(node.receiver), node.name).concat(visit_all(arguments))
+ result << visit_write_value(write_value) unless write_value.nil?
+
+ visit_block(node, result, block)
+ end
+
+ # foo.bar += baz
+ # ^^^^^^^^^^^^^^^
+ def visit_call_operator_write_node(node)
+ if op_asgn?(node)
+ s(node, op_asgn_type(node, :op_asgn), visit(node.receiver), visit_write_value(node.value), node.read_name, node.operator)
+ else
+ s(node, op_asgn_type(node, :op_asgn2), visit(node.receiver), node.write_name, node.operator, visit_write_value(node.value))
+ end
+ end
+
+ # foo.bar &&= baz
+ # ^^^^^^^^^^^^^^^
+ def visit_call_and_write_node(node)
+ if op_asgn?(node)
+ s(node, op_asgn_type(node, :op_asgn), visit(node.receiver), visit_write_value(node.value), node.read_name, :"&&")
+ else
+ s(node, op_asgn_type(node, :op_asgn2), visit(node.receiver), node.write_name, :"&&", visit_write_value(node.value))
+ end
+ end
+
+ # foo.bar ||= baz
+ # ^^^^^^^^^^^^^^^
+ def visit_call_or_write_node(node)
+ if op_asgn?(node)
+ s(node, op_asgn_type(node, :op_asgn), visit(node.receiver), visit_write_value(node.value), node.read_name, :"||")
+ else
+ s(node, op_asgn_type(node, :op_asgn2), visit(node.receiver), node.write_name, :"||", visit_write_value(node.value))
+ end
+ end
+
+ # Call nodes with operators following them will either be op_asgn or
+ # op_asgn2 nodes. That is determined by their call operator and their
+ # right-hand side.
+ private def op_asgn?(node)
+ node.call_operator == "::" || (node.value.is_a?(CallNode) && node.value.opening_loc.nil? && !node.value.arguments.nil?)
+ end
+
+ # Call nodes with operators following them can use &. as an operator,
+ # which changes their type by prefixing "safe_".
+ private def op_asgn_type(node, type)
+ node.safe_navigation? ? :"safe_#{type}" : type
+ end
+
+ # foo.bar, = 1
+ # ^^^^^^^
+ def visit_call_target_node(node)
+ s(node, :attrasgn, visit(node.receiver), node.name)
+ end
+
+ # foo => bar => baz
+ # ^^^^^^^^^^
+ def visit_capture_pattern_node(node)
+ visit(node.target) << visit(node.value)
+ end
+
+ # case foo; when bar; end
+ # ^^^^^^^^^^^^^^^^^^^^^^^
+ def visit_case_node(node)
+ s(node, :case, visit(node.predicate)).concat(visit_all(node.conditions)) << visit(node.consequent)
+ end
+
+ # case foo; in bar; end
+ # ^^^^^^^^^^^^^^^^^^^^^
+ def visit_case_match_node(node)
+ s(node, :case, visit(node.predicate)).concat(visit_all(node.conditions)) << visit(node.consequent)
+ end
+
+ # class Foo; end
+ # ^^^^^^^^^^^^^^
+ def visit_class_node(node)
+ name =
+ if node.constant_path.is_a?(ConstantReadNode)
+ node.name
+ else
+ visit(node.constant_path)
+ end
+
+ if node.body.nil?
+ s(node, :class, name, visit(node.superclass))
+ elsif node.body.is_a?(StatementsNode)
+ compiler = copy_compiler(in_def: false)
+ s(node, :class, name, visit(node.superclass)).concat(node.body.body.map { |child| child.accept(compiler) })
+ else
+ s(node, :class, name, visit(node.superclass), node.body.accept(copy_compiler(in_def: false)))
+ end
+ end
+
+ # @@foo
+ # ^^^^^
+ def visit_class_variable_read_node(node)
+ s(node, :cvar, node.name)
+ end
+
+ # @@foo = 1
+ # ^^^^^^^^^
+ #
+ # @@foo, @@bar = 1
+ # ^^^^^ ^^^^^
+ def visit_class_variable_write_node(node)
+ s(node, class_variable_write_type, node.name, visit_write_value(node.value))
+ end
+
+ # @@foo += bar
+ # ^^^^^^^^^^^^
+ def visit_class_variable_operator_write_node(node)
+ s(node, class_variable_write_type, node.name, s(node, :call, s(node, :cvar, node.name), node.operator, visit_write_value(node.value)))
+ end
+
+ # @@foo &&= bar
+ # ^^^^^^^^^^^^^
+ def visit_class_variable_and_write_node(node)
+ s(node, :op_asgn_and, s(node, :cvar, node.name), s(node, class_variable_write_type, node.name, visit_write_value(node.value)))
+ end
+
+ # @@foo ||= bar
+ # ^^^^^^^^^^^^^
+ def visit_class_variable_or_write_node(node)
+ s(node, :op_asgn_or, s(node, :cvar, node.name), s(node, class_variable_write_type, node.name, visit_write_value(node.value)))
+ end
+
+ # @@foo, = bar
+ # ^^^^^
+ def visit_class_variable_target_node(node)
+ s(node, class_variable_write_type, node.name)
+ end
+
+ # If a class variable is written within a method definition, it has a
+ # different type than everywhere else.
+ private def class_variable_write_type
+ in_def ? :cvasgn : :cvdecl
+ end
+
+ # Foo
+ # ^^^
+ def visit_constant_read_node(node)
+ s(node, :const, node.name)
+ end
+
+ # Foo = 1
+ # ^^^^^^^
+ #
+ # Foo, Bar = 1
+ # ^^^ ^^^
+ def visit_constant_write_node(node)
+ s(node, :cdecl, node.name, visit_write_value(node.value))
+ end
+
+ # Foo += bar
+ # ^^^^^^^^^^^
+ def visit_constant_operator_write_node(node)
+ s(node, :cdecl, node.name, s(node, :call, s(node, :const, node.name), node.operator, visit_write_value(node.value)))
+ end
+
+ # Foo &&= bar
+ # ^^^^^^^^^^^^
+ def visit_constant_and_write_node(node)
+ s(node, :op_asgn_and, s(node, :const, node.name), s(node, :cdecl, node.name, visit(node.value)))
+ end
+
+ # Foo ||= bar
+ # ^^^^^^^^^^^^
+ def visit_constant_or_write_node(node)
+ s(node, :op_asgn_or, s(node, :const, node.name), s(node, :cdecl, node.name, visit(node.value)))
+ end
+
+ # Foo, = bar
+ # ^^^
+ def visit_constant_target_node(node)
+ s(node, :cdecl, node.name)
+ end
+
+ # Foo::Bar
+ # ^^^^^^^^
+ def visit_constant_path_node(node)
+ if node.parent.nil?
+ s(node, :colon3, node.child.name)
+ else
+ s(node, :colon2, visit(node.parent), node.child.name)
+ end
+ end
+
+ # Foo::Bar = 1
+ # ^^^^^^^^^^^^
+ #
+ # Foo::Foo, Bar::Bar = 1
+ # ^^^^^^^^ ^^^^^^^^
+ def visit_constant_path_write_node(node)
+ s(node, :cdecl, visit(node.target), visit_write_value(node.value))
+ end
+
+ # Foo::Bar += baz
+ # ^^^^^^^^^^^^^^^
+ def visit_constant_path_operator_write_node(node)
+ s(node, :op_asgn, visit(node.target), node.operator, visit_write_value(node.value))
+ end
+
+ # Foo::Bar &&= baz
+ # ^^^^^^^^^^^^^^^^
+ def visit_constant_path_and_write_node(node)
+ s(node, :op_asgn_and, visit(node.target), visit_write_value(node.value))
+ end
+
+ # Foo::Bar ||= baz
+ # ^^^^^^^^^^^^^^^^
+ def visit_constant_path_or_write_node(node)
+ s(node, :op_asgn_or, visit(node.target), visit_write_value(node.value))
+ end
+
+ # Foo::Bar, = baz
+ # ^^^^^^^^
+ def visit_constant_path_target_node(node)
+ inner =
+ if node.parent.nil?
+ s(node, :colon3, node.child.name)
+ else
+ s(node, :colon2, visit(node.parent), node.child.name)
+ end
+
+ s(node, :const, inner)
+ end
+
+ # def foo; end
+ # ^^^^^^^^^^^^
+ #
+ # def self.foo; end
+ # ^^^^^^^^^^^^^^^^^
+ def visit_def_node(node)
+ name = node.name_loc.slice.to_sym
+ result =
+ if node.receiver.nil?
+ s(node, :defn, name)
+ else
+ s(node, :defs, visit(node.receiver), name)
+ end
+
+ result.line(node.name_loc.start_line)
+ if node.parameters.nil?
+ result << s(node, :args).line(node.name_loc.start_line)
+ else
+ result << visit(node.parameters)
+ end
+
+ if node.body.nil?
+ result << s(node, :nil)
+ elsif node.body.is_a?(StatementsNode)
+ compiler = copy_compiler(in_def: true)
+ result.concat(node.body.body.map { |child| child.accept(compiler) })
+ else
+ result << node.body.accept(copy_compiler(in_def: true))
+ end
+ end
+
+ # defined? a
+ # ^^^^^^^^^^
+ #
+ # defined?(a)
+ # ^^^^^^^^^^^
+ def visit_defined_node(node)
+ s(node, :defined, visit(node.value))
+ end
+
+ # if foo then bar else baz end
+ # ^^^^^^^^^^^^
+ def visit_else_node(node)
+ visit(node.statements)
+ end
+
+ # "foo #{bar}"
+ # ^^^^^^
+ def visit_embedded_statements_node(node)
+ result = s(node, :evstr)
+ result << visit(node.statements) unless node.statements.nil?
+ result
+ end
+
+ # "foo #@bar"
+ # ^^^^^
+ def visit_embedded_variable_node(node)
+ s(node, :evstr, visit(node.variable))
+ end
+
+ # begin; foo; ensure; bar; end
+ # ^^^^^^^^^^^^
+ def visit_ensure_node(node)
+ node.statements.nil? ? s(node, :nil) : visit(node.statements)
+ end
+
+ # false
+ # ^^^^^
+ def visit_false_node(node)
+ s(node, :false)
+ end
+
+ # foo => [*, bar, *]
+ # ^^^^^^^^^^^
+ def visit_find_pattern_node(node)
+ s(node, :find_pat, visit_pattern_constant(node.constant), :"*#{node.left.expression&.name}", *visit_all(node.requireds), :"*#{node.right.expression&.name}")
+ end
+
+ # if foo .. bar; end
+ # ^^^^^^^^^^
+ def visit_flip_flop_node(node)
+ if node.left.is_a?(IntegerNode) && node.right.is_a?(IntegerNode)
+ s(node, :lit, Range.new(node.left.value, node.right.value, node.exclude_end?))
+ else
+ s(node, node.exclude_end? ? :flip3 : :flip2, visit(node.left), visit(node.right))
+ end
+ end
+
+ # 1.0
+ # ^^^
+ def visit_float_node(node)
+ s(node, :lit, node.value)
+ end
+
+ # for foo in bar do end
+ # ^^^^^^^^^^^^^^^^^^^^^
+ def visit_for_node(node)
+ s(node, :for, visit(node.collection), visit(node.index), visit(node.statements))
+ end
+
+ # def foo(...); bar(...); end
+ # ^^^
+ def visit_forwarding_arguments_node(node)
+ s(node, :forward_args)
+ end
+
+ # def foo(...); end
+ # ^^^
+ def visit_forwarding_parameter_node(node)
+ s(node, :forward_args)
+ end
+
+ # super
+ # ^^^^^
+ #
+ # super {}
+ # ^^^^^^^^
+ def visit_forwarding_super_node(node)
+ visit_block(node, s(node, :zsuper), node.block)
+ end
+
+ # $foo
+ # ^^^^
+ def visit_global_variable_read_node(node)
+ s(node, :gvar, node.name)
+ end
+
+ # $foo = 1
+ # ^^^^^^^^
+ #
+ # $foo, $bar = 1
+ # ^^^^ ^^^^
+ def visit_global_variable_write_node(node)
+ s(node, :gasgn, node.name, visit_write_value(node.value))
+ end
+
+ # $foo += bar
+ # ^^^^^^^^^^^
+ def visit_global_variable_operator_write_node(node)
+ s(node, :gasgn, node.name, s(node, :call, s(node, :gvar, node.name), node.operator, visit(node.value)))
+ end
+
+ # $foo &&= bar
+ # ^^^^^^^^^^^^
+ def visit_global_variable_and_write_node(node)
+ s(node, :op_asgn_and, s(node, :gvar, node.name), s(node, :gasgn, node.name, visit_write_value(node.value)))
+ end
+
+ # $foo ||= bar
+ # ^^^^^^^^^^^^
+ def visit_global_variable_or_write_node(node)
+ s(node, :op_asgn_or, s(node, :gvar, node.name), s(node, :gasgn, node.name, visit_write_value(node.value)))
+ end
+
+ # $foo, = bar
+ # ^^^^
+ def visit_global_variable_target_node(node)
+ s(node, :gasgn, node.name)
+ end
+
+ # {}
+ # ^^
+ def visit_hash_node(node)
+ s(node, :hash).concat(node.elements.flat_map { |element| visit(element) })
+ end
+
+ # foo => {}
+ # ^^
+ def visit_hash_pattern_node(node)
+ result = s(node, :hash_pat, visit_pattern_constant(node.constant)).concat(node.elements.flat_map { |element| visit(element) })
+
+ case node.rest
+ when AssocSplatNode
+ result << s(node.rest, :kwrest, :"**#{node.rest.value&.name}")
+ when NoKeywordsParameterNode
+ result << visit(node.rest)
+ end
+
+ result
+ end
+
+ # if foo then bar end
+ # ^^^^^^^^^^^^^^^^^^^
+ #
+ # bar if foo
+ # ^^^^^^^^^^
+ #
+ # foo ? bar : baz
+ # ^^^^^^^^^^^^^^^
+ def visit_if_node(node)
+ s(node, :if, visit(node.predicate), visit(node.statements), visit(node.consequent))
+ end
+
+ # 1i
+ def visit_imaginary_node(node)
+ s(node, :lit, node.value)
+ end
+
+ # { foo: }
+ # ^^^^
+ def visit_implicit_node(node)
+ end
+
+ # foo { |bar,| }
+ # ^
+ def visit_implicit_rest_node(node)
+ end
+
+ # case foo; in bar; end
+ # ^^^^^^^^^^^^^^^^^^^^^
+ def visit_in_node(node)
+ pattern =
+ if node.pattern.is_a?(ConstantPathNode)
+ s(node.pattern, :const, visit(node.pattern))
+ else
+ node.pattern.accept(copy_compiler(in_pattern: true))
+ end
+
+ s(node, :in, pattern).concat(node.statements.nil? ? [nil] : visit_all(node.statements.body))
+ end
+
+ # foo[bar] += baz
+ # ^^^^^^^^^^^^^^^
+ def visit_index_operator_write_node(node)
+ arglist = nil
+
+ if !node.arguments.nil? || !node.block.nil?
+ arglist = s(node, :arglist).concat(visit_all(node.arguments&.arguments || []))
+ arglist << visit(node.block) if !node.block.nil?
+ end
+
+ s(node, :op_asgn1, visit(node.receiver), arglist, node.operator, visit_write_value(node.value))
+ end
+
+ # foo[bar] &&= baz
+ # ^^^^^^^^^^^^^^^^
+ def visit_index_and_write_node(node)
+ arglist = nil
+
+ if !node.arguments.nil? || !node.block.nil?
+ arglist = s(node, :arglist).concat(visit_all(node.arguments&.arguments || []))
+ arglist << visit(node.block) if !node.block.nil?
+ end
+
+ s(node, :op_asgn1, visit(node.receiver), arglist, :"&&", visit_write_value(node.value))
+ end
+
+ # foo[bar] ||= baz
+ # ^^^^^^^^^^^^^^^^
+ def visit_index_or_write_node(node)
+ arglist = nil
+
+ if !node.arguments.nil? || !node.block.nil?
+ arglist = s(node, :arglist).concat(visit_all(node.arguments&.arguments || []))
+ arglist << visit(node.block) if !node.block.nil?
+ end
+
+ s(node, :op_asgn1, visit(node.receiver), arglist, :"||", visit_write_value(node.value))
+ end
+
+ # foo[bar], = 1
+ # ^^^^^^^^
+ def visit_index_target_node(node)
+ arguments = visit_all(node.arguments&.arguments || [])
+ arguments << visit(node.block) unless node.block.nil?
+
+ s(node, :attrasgn, visit(node.receiver), :[]=).concat(arguments)
+ end
+
+ # @foo
+ # ^^^^
+ def visit_instance_variable_read_node(node)
+ s(node, :ivar, node.name)
+ end
+
+ # @foo = 1
+ # ^^^^^^^^
+ #
+ # @foo, @bar = 1
+ # ^^^^ ^^^^
+ def visit_instance_variable_write_node(node)
+ s(node, :iasgn, node.name, visit_write_value(node.value))
+ end
+
+ # @foo += bar
+ # ^^^^^^^^^^^
+ def visit_instance_variable_operator_write_node(node)
+ s(node, :iasgn, node.name, s(node, :call, s(node, :ivar, node.name), node.operator, visit_write_value(node.value)))
+ end
+
+ # @foo &&= bar
+ # ^^^^^^^^^^^^
+ def visit_instance_variable_and_write_node(node)
+ s(node, :op_asgn_and, s(node, :ivar, node.name), s(node, :iasgn, node.name, visit(node.value)))
+ end
+
+ # @foo ||= bar
+ # ^^^^^^^^^^^^
+ def visit_instance_variable_or_write_node(node)
+ s(node, :op_asgn_or, s(node, :ivar, node.name), s(node, :iasgn, node.name, visit(node.value)))
+ end
+
+ # @foo, = bar
+ # ^^^^
+ def visit_instance_variable_target_node(node)
+ s(node, :iasgn, node.name)
+ end
+
+ # 1
+ # ^
+ def visit_integer_node(node)
+ s(node, :lit, node.value)
+ end
+
+ # if /foo #{bar}/ then end
+ # ^^^^^^^^^^^^
+ def visit_interpolated_match_last_line_node(node)
+ parts = visit_interpolated_parts(node.parts)
+ regexp =
+ if parts.length == 1
+ s(node, :lit, Regexp.new(parts.first, node.options))
+ else
+ s(node, :dregx).concat(parts).tap do |result|
+ options = node.options
+ result << options if options != 0
+ end
+ end
+
+ s(node, :match, regexp)
+ end
+
+ # /foo #{bar}/
+ # ^^^^^^^^^^^^
+ def visit_interpolated_regular_expression_node(node)
+ parts = visit_interpolated_parts(node.parts)
+
+ if parts.length == 1
+ s(node, :lit, Regexp.new(parts.first, node.options))
+ else
+ s(node, :dregx).concat(parts).tap do |result|
+ options = node.options
+ result << options if options != 0
+ end
+ end
+ end
+
+ # "foo #{bar}"
+ # ^^^^^^^^^^^^
+ def visit_interpolated_string_node(node)
+ parts = visit_interpolated_parts(node.parts)
+ parts.length == 1 ? s(node, :str, parts.first) : s(node, :dstr).concat(parts)
+ end
+
+ # :"foo #{bar}"
+ # ^^^^^^^^^^^^^
+ def visit_interpolated_symbol_node(node)
+ parts = visit_interpolated_parts(node.parts)
+ parts.length == 1 ? s(node, :lit, parts.first.to_sym) : s(node, :dsym).concat(parts)
+ end
+
+ # `foo #{bar}`
+ # ^^^^^^^^^^^^
+ def visit_interpolated_x_string_node(node)
+ source = node.heredoc? ? node.parts.first : node
+ parts = visit_interpolated_parts(node.parts)
+ parts.length == 1 ? s(source, :xstr, parts.first) : s(source, :dxstr).concat(parts)
+ end
+
+ # Visit the interpolated content of the string-like node.
+ private def visit_interpolated_parts(parts)
+ visited = []
+ parts.each do |part|
+ result = visit(part)
+
+ if result[0] == :evstr && result[1]
+ if result[1][0] == :str
+ visited << result[1]
+ elsif result[1][0] == :dstr
+ visited.concat(result[1][1..-1])
+ else
+ visited << result
+ end
+ else
+ visited << result
+ end
+ end
+
+ state = :beginning #: :beginning | :string_content | :interpolated_content
+
+ visited.each_with_object([]) do |result, results|
+ case state
+ when :beginning
+ if result.is_a?(String)
+ results << result
+ state = :string_content
+ elsif result.is_a?(Array) && result[0] == :str
+ results << result[1]
+ state = :string_content
+ else
+ results << ""
+ results << result
+ state = :interpolated_content
+ end
+ when :string_content
+ if result.is_a?(String)
+ results[0] << result
+ elsif result.is_a?(Array) && result[0] == :str
+ results[0] << result[1]
+ else
+ results << result
+ state = :interpolated_content
+ end
+ else
+ results << result
+ end
+ end
+ end
+
+ # foo(bar: baz)
+ # ^^^^^^^^
+ def visit_keyword_hash_node(node)
+ s(node, :hash).concat(node.elements.flat_map { |element| visit(element) })
+ end
+
+ # def foo(**bar); end
+ # ^^^^^
+ #
+ # def foo(**); end
+ # ^^
+ def visit_keyword_rest_parameter_node(node)
+ :"**#{node.name}"
+ end
+
+ # -> {}
+ def visit_lambda_node(node)
+ parameters =
+ case node.parameters
+ when nil, NumberedParametersNode
+ s(node, :args)
+ else
+ visit(node.parameters)
+ end
+
+ if node.body.nil?
+ s(node, :iter, s(node, :lambda), parameters)
+ else
+ s(node, :iter, s(node, :lambda), parameters, visit(node.body))
+ end
+ end
+
+ # foo
+ # ^^^
+ def visit_local_variable_read_node(node)
+ if node.name.match?(/^_\d$/)
+ s(node, :call, nil, node.name)
+ else
+ s(node, :lvar, node.name)
+ end
+ end
+
+ # foo = 1
+ # ^^^^^^^
+ #
+ # foo, bar = 1
+ # ^^^ ^^^
+ def visit_local_variable_write_node(node)
+ s(node, :lasgn, node.name, visit_write_value(node.value))
+ end
+
+ # foo += bar
+ # ^^^^^^^^^^
+ def visit_local_variable_operator_write_node(node)
+ s(node, :lasgn, node.name, s(node, :call, s(node, :lvar, node.name), node.operator, visit_write_value(node.value)))
+ end
+
+ # foo &&= bar
+ # ^^^^^^^^^^^
+ def visit_local_variable_and_write_node(node)
+ s(node, :op_asgn_and, s(node, :lvar, node.name), s(node, :lasgn, node.name, visit_write_value(node.value)))
+ end
+
+ # foo ||= bar
+ # ^^^^^^^^^^^
+ def visit_local_variable_or_write_node(node)
+ s(node, :op_asgn_or, s(node, :lvar, node.name), s(node, :lasgn, node.name, visit_write_value(node.value)))
+ end
+
+ # foo, = bar
+ # ^^^
+ def visit_local_variable_target_node(node)
+ s(node, :lasgn, node.name)
+ end
+
+ # if /foo/ then end
+ # ^^^^^
+ def visit_match_last_line_node(node)
+ s(node, :match, s(node, :lit, Regexp.new(node.unescaped, node.options)))
+ end
+
+ # foo in bar
+ # ^^^^^^^^^^
+ def visit_match_predicate_node(node)
+ s(node, :case, visit(node.value), s(node, :in, node.pattern.accept(copy_compiler(in_pattern: true)), nil), nil)
+ end
+
+ # foo => bar
+ # ^^^^^^^^^^
+ def visit_match_required_node(node)
+ s(node, :case, visit(node.value), s(node, :in, node.pattern.accept(copy_compiler(in_pattern: true)), nil), nil)
+ end
+
+ # /(?<foo>foo)/ =~ bar
+ # ^^^^^^^^^^^^^^^^^^^^
+ def visit_match_write_node(node)
+ s(node, :match2, visit(node.call.receiver), visit(node.call.arguments.arguments.first))
+ end
+
+ # A node that is missing from the syntax tree. This is only used in the
+ # case of a syntax error. The parser gem doesn't have such a concept, so
+ # we invent our own here.
+ def visit_missing_node(node)
+ raise "Cannot visit missing node directly"
+ end
+
+ # module Foo; end
+ # ^^^^^^^^^^^^^^^
+ def visit_module_node(node)
+ name =
+ if node.constant_path.is_a?(ConstantReadNode)
+ node.name
+ else
+ visit(node.constant_path)
+ end
+
+ if node.body.nil?
+ s(node, :module, name)
+ elsif node.body.is_a?(StatementsNode)
+ compiler = copy_compiler(in_def: false)
+ s(node, :module, name).concat(node.body.body.map { |child| child.accept(compiler) })
+ else
+ s(node, :module, name, node.body.accept(copy_compiler(in_def: false)))
+ end
+ end
+
+ # foo, bar = baz
+ # ^^^^^^^^
+ def visit_multi_target_node(node)
+ targets = [*node.lefts]
+ targets << node.rest if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode)
+ targets.concat(node.rights)
+
+ s(node, :masgn, s(node, :array).concat(visit_all(targets)))
+ end
+
+ # foo, bar = baz
+ # ^^^^^^^^^^^^^^
+ def visit_multi_write_node(node)
+ targets = [*node.lefts]
+ targets << node.rest if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode)
+ targets.concat(node.rights)
+
+ value =
+ if node.value.is_a?(ArrayNode) && node.value.opening_loc.nil?
+ if node.value.elements.length == 1 && node.value.elements.first.is_a?(SplatNode)
+ visit(node.value.elements.first)
+ else
+ visit(node.value)
+ end
+ else
+ s(node.value, :to_ary, visit(node.value))
+ end
+
+ s(node, :masgn, s(node, :array).concat(visit_all(targets)), value)
+ end
+
+ # next
+ # ^^^^
+ #
+ # next foo
+ # ^^^^^^^^
+ def visit_next_node(node)
+ if node.arguments.nil?
+ s(node, :next)
+ elsif node.arguments.arguments.length == 1
+ argument = node.arguments.arguments.first
+ s(node, :next, argument.is_a?(SplatNode) ? s(node, :svalue, visit(argument)) : visit(argument))
+ else
+ s(node, :next, s(node, :array).concat(visit_all(node.arguments.arguments)))
+ end
+ end
+
+ # nil
+ # ^^^
+ def visit_nil_node(node)
+ s(node, :nil)
+ end
+
+ # def foo(**nil); end
+ # ^^^^^
+ def visit_no_keywords_parameter_node(node)
+ in_pattern ? s(node, :kwrest, :"**nil") : :"**nil"
+ end
+
+ # -> { _1 + _2 }
+ # ^^^^^^^^^^^^^^
+ def visit_numbered_parameters_node(node)
+ raise "Cannot visit numbered parameters directly"
+ end
+
+ # $1
+ # ^^
+ def visit_numbered_reference_read_node(node)
+ s(node, :nth_ref, node.number)
+ end
+
+ # def foo(bar: baz); end
+ # ^^^^^^^^
+ def visit_optional_keyword_parameter_node(node)
+ s(node, :kwarg, node.name, visit(node.value))
+ end
+
+ # def foo(bar = 1); end
+ # ^^^^^^^
+ def visit_optional_parameter_node(node)
+ s(node, :lasgn, node.name, visit(node.value))
+ end
+
+ # a or b
+ # ^^^^^^
+ def visit_or_node(node)
+ s(node, :or, visit(node.left), visit(node.right))
+ end
+
+ # def foo(bar, *baz); end
+ # ^^^^^^^^^
+ def visit_parameters_node(node)
+ children =
+ node.compact_child_nodes.map do |element|
+ if element.is_a?(MultiTargetNode)
+ visit_destructured_parameter(element)
+ else
+ visit(element)
+ end
+ end
+
+ s(node, :args).concat(children)
+ end
+
+ # def foo((bar, baz)); end
+ # ^^^^^^^^^^
+ private def visit_destructured_parameter(node)
+ children =
+ [*node.lefts, *node.rest, *node.rights].map do |child|
+ case child
+ when RequiredParameterNode
+ visit(child)
+ when MultiTargetNode
+ visit_destructured_parameter(child)
+ when SplatNode
+ :"*#{child.expression&.name}"
+ else
+ raise
+ end
+ end
+
+ s(node, :masgn).concat(children)
+ end
+
+ # ()
+ # ^^
+ #
+ # (1)
+ # ^^^
+ def visit_parentheses_node(node)
+ if node.body.nil?
+ s(node, :nil)
+ else
+ visit(node.body)
+ end
+ end
+
+ # foo => ^(bar)
+ # ^^^^^^
+ def visit_pinned_expression_node(node)
+ node.expression.accept(copy_compiler(in_pattern: false))
+ end
+
+ # foo = 1 and bar => ^foo
+ # ^^^^
+ def visit_pinned_variable_node(node)
+ if node.variable.is_a?(LocalVariableReadNode) && node.variable.name.match?(/^_\d$/)
+ s(node, :lvar, node.variable.name)
+ else
+ visit(node.variable)
+ end
+ end
+
+ # END {}
+ def visit_post_execution_node(node)
+ s(node, :iter, s(node, :postexe), 0, visit(node.statements))
+ end
+
+ # BEGIN {}
+ def visit_pre_execution_node(node)
+ s(node, :iter, s(node, :preexe), 0, visit(node.statements))
+ end
+
+ # The top-level program node.
+ def visit_program_node(node)
+ visit(node.statements)
+ end
+
+ # 0..5
+ # ^^^^
+ def visit_range_node(node)
+ if !in_pattern && !node.left.nil? && !node.right.nil? && ([node.left.type, node.right.type] - %i[nil_node integer_node]).empty?
+ left = node.left.value if node.left.is_a?(IntegerNode)
+ right = node.right.value if node.right.is_a?(IntegerNode)
+ s(node, :lit, Range.new(left, right, node.exclude_end?))
+ else
+ s(node, node.exclude_end? ? :dot3 : :dot2, visit_range_bounds_node(node.left), visit_range_bounds_node(node.right))
+ end
+ end
+
+ # If the bounds of a range node are empty parentheses, then they do not
+ # get replaced by their usual s(:nil), but instead are s(:begin).
+ private def visit_range_bounds_node(node)
+ if node.is_a?(ParenthesesNode) && node.body.nil?
+ s(node, :begin)
+ else
+ visit(node)
+ end
+ end
+
+ # 1r
+ # ^^
+ def visit_rational_node(node)
+ s(node, :lit, node.value)
+ end
+
+ # redo
+ # ^^^^
+ def visit_redo_node(node)
+ s(node, :redo)
+ end
+
+ # /foo/
+ # ^^^^^
+ def visit_regular_expression_node(node)
+ s(node, :lit, Regexp.new(node.unescaped, node.options))
+ end
+
+ # def foo(bar:); end
+ # ^^^^
+ def visit_required_keyword_parameter_node(node)
+ s(node, :kwarg, node.name)
+ end
+
+ # def foo(bar); end
+ # ^^^
+ def visit_required_parameter_node(node)
+ node.name
+ end
+
+ # foo rescue bar
+ # ^^^^^^^^^^^^^^
+ def visit_rescue_modifier_node(node)
+ s(node, :rescue, visit(node.expression), s(node.rescue_expression, :resbody, s(node.rescue_expression, :array), visit(node.rescue_expression)))
+ end
+
+ # begin; rescue; end
+ # ^^^^^^^
+ def visit_rescue_node(node)
+ exceptions =
+ if node.exceptions.length == 1 && node.exceptions.first.is_a?(SplatNode)
+ visit(node.exceptions.first)
+ else
+ s(node, :array).concat(visit_all(node.exceptions))
+ end
+
+ if !node.reference.nil?
+ exceptions << (visit(node.reference) << s(node.reference, :gvar, :"$!"))
+ end
+
+ s(node, :resbody, exceptions).concat(node.statements.nil? ? [nil] : visit_all(node.statements.body))
+ end
+
+ # def foo(*bar); end
+ # ^^^^
+ #
+ # def foo(*); end
+ # ^
+ def visit_rest_parameter_node(node)
+ :"*#{node.name}"
+ end
+
+ # retry
+ # ^^^^^
+ def visit_retry_node(node)
+ s(node, :retry)
+ end
+
+ # return
+ # ^^^^^^
+ #
+ # return 1
+ # ^^^^^^^^
+ def visit_return_node(node)
+ if node.arguments.nil?
+ s(node, :return)
+ elsif node.arguments.arguments.length == 1
+ argument = node.arguments.arguments.first
+ s(node, :return, argument.is_a?(SplatNode) ? s(node, :svalue, visit(argument)) : visit(argument))
+ else
+ s(node, :return, s(node, :array).concat(visit_all(node.arguments.arguments)))
+ end
+ end
+
+ # self
+ # ^^^^
+ def visit_self_node(node)
+ s(node, :self)
+ end
+
+ # A shareable constant.
+ def visit_shareable_constant_node(node)
+ visit(node.write)
+ end
+
+ # class << self; end
+ # ^^^^^^^^^^^^^^^^^^
+ def visit_singleton_class_node(node)
+ s(node, :sclass, visit(node.expression)).tap do |sexp|
+ sexp << node.body.accept(copy_compiler(in_def: false)) unless node.body.nil?
+ end
+ end
+
+ # __ENCODING__
+ # ^^^^^^^^^^^^
+ def visit_source_encoding_node(node)
+ # TODO
+ s(node, :colon2, s(node, :const, :Encoding), :UTF_8)
+ end
+
+ # __FILE__
+ # ^^^^^^^^
+ def visit_source_file_node(node)
+ s(node, :str, node.filepath)
+ end
+
+ # __LINE__
+ # ^^^^^^^^
+ def visit_source_line_node(node)
+ s(node, :lit, node.location.start_line)
+ end
+
+ # foo(*bar)
+ # ^^^^
+ #
+ # def foo((bar, *baz)); end
+ # ^^^^
+ #
+ # def foo(*); bar(*); end
+ # ^
+ def visit_splat_node(node)
+ if node.expression.nil?
+ s(node, :splat)
+ else
+ s(node, :splat, visit(node.expression))
+ end
+ end
+
+ # A list of statements.
+ def visit_statements_node(node)
+ first, *rest = node.body
+
+ if rest.empty?
+ visit(first)
+ else
+ s(node, :block).concat(visit_all(node.body))
+ end
+ end
+
+ # "foo"
+ # ^^^^^
+ def visit_string_node(node)
+ s(node, :str, node.unescaped)
+ end
+
+ # super(foo)
+ # ^^^^^^^^^^
+ def visit_super_node(node)
+ arguments = node.arguments&.arguments || []
+ block = node.block
+
+ if block.is_a?(BlockArgumentNode)
+ arguments << block
+ block = nil
+ end
+
+ visit_block(node, s(node, :super).concat(visit_all(arguments)), block)
+ end
+
+ # :foo
+ # ^^^^
+ def visit_symbol_node(node)
+ node.value == "!@" ? s(node, :lit, :"!@") : s(node, :lit, node.unescaped.to_sym)
+ end
+
+ # true
+ # ^^^^
+ def visit_true_node(node)
+ s(node, :true)
+ end
+
+ # undef foo
+ # ^^^^^^^^^
+ def visit_undef_node(node)
+ names = node.names.map { |name| s(node, :undef, visit(name)) }
+ names.length == 1 ? names.first : s(node, :block).concat(names)
+ end
+
+ # unless foo; bar end
+ # ^^^^^^^^^^^^^^^^^^^
+ #
+ # bar unless foo
+ # ^^^^^^^^^^^^^^
+ def visit_unless_node(node)
+ s(node, :if, visit(node.predicate), visit(node.consequent), visit(node.statements))
+ end
+
+ # until foo; bar end
+ # ^^^^^^^^^^^^^^^^^
+ #
+ # bar until foo
+ # ^^^^^^^^^^^^^
+ def visit_until_node(node)
+ s(node, :until, visit(node.predicate), visit(node.statements), !node.begin_modifier?)
+ end
+
+ # case foo; when bar; end
+ # ^^^^^^^^^^^^^
+ def visit_when_node(node)
+ s(node, :when, s(node, :array).concat(visit_all(node.conditions))).concat(node.statements.nil? ? [nil] : visit_all(node.statements.body))
+ end
+
+ # while foo; bar end
+ # ^^^^^^^^^^^^^^^^^^
+ #
+ # bar while foo
+ # ^^^^^^^^^^^^^
+ def visit_while_node(node)
+ s(node, :while, visit(node.predicate), visit(node.statements), !node.begin_modifier?)
+ end
+
+ # `foo`
+ # ^^^^^
+ def visit_x_string_node(node)
+ result = s(node, :xstr, node.unescaped)
+
+ if node.heredoc?
+ result.line = node.content_loc.start_line
+ result.line_max = node.content_loc.end_line
+ end
+
+ result
+ end
+
+ # yield
+ # ^^^^^
+ #
+ # yield 1
+ # ^^^^^^^
+ def visit_yield_node(node)
+ s(node, :yield).concat(visit_all(node.arguments&.arguments || []))
+ end
+
+ private
+
+ # Create a new compiler with the given options.
+ def copy_compiler(in_def: self.in_def, in_pattern: self.in_pattern)
+ Compiler.new(file, in_def: in_def, in_pattern: in_pattern)
+ end
+
+ # Create a new Sexp object from the given prism node and arguments.
+ def s(node, *arguments)
+ result = Sexp.new(*arguments)
+ result.file = file
+ result.line = node.location.start_line
+ result.line_max = node.location.end_line
+ result
+ end
+
+ # Visit a block node, which will modify the AST by wrapping the given
+ # visited node in an iter node.
+ def visit_block(node, sexp, block)
+ if block.nil?
+ sexp
+ else
+ parameters =
+ case block.parameters
+ when nil, NumberedParametersNode
+ 0
+ else
+ visit(block.parameters)
+ end
+
+ if block.body.nil?
+ s(node, :iter, sexp, parameters)
+ else
+ s(node, :iter, sexp, parameters, visit(block.body))
+ end
+ end
+ end
+
+ # Pattern constants get wrapped in another layer of :const.
+ def visit_pattern_constant(node)
+ case node
+ when nil
+ # nothing
+ when ConstantReadNode
+ visit(node)
+ else
+ s(node, :const, visit(node))
+ end
+ end
+
+ # Visit the value of a write, which will be on the right-hand side of
+ # a write operator. Because implicit arrays can have splats, those could
+ # potentially be wrapped in an svalue node.
+ def visit_write_value(node)
+ if node.is_a?(ArrayNode) && node.opening_loc.nil?
+ if node.elements.length == 1 && node.elements.first.is_a?(SplatNode)
+ s(node, :svalue, visit(node.elements.first))
+ else
+ s(node, :svalue, visit(node))
+ end
+ else
+ visit(node)
+ end
+ end
+ end
+
+ private_constant :Compiler
+
+ # Parse the given source and translate it into the seattlerb/ruby_parser
+ # gem's Sexp format.
+ def parse(source, filepath = "(string)")
+ translate(Prism.parse(source, filepath: filepath), filepath)
+ end
+
+ # Parse the given file and translate it into the seattlerb/ruby_parser
+ # gem's Sexp format.
+ def parse_file(filepath)
+ translate(Prism.parse_file(filepath), filepath)
+ end
+
+ class << self
+ # Parse the given source and translate it into the seattlerb/ruby_parser
+ # gem's Sexp format.
+ def parse(source, filepath = "(string)")
+ new.parse(source, filepath)
+ end
+
+ # Parse the given file and translate it into the seattlerb/ruby_parser
+ # gem's Sexp format.
+ def parse_file(filepath)
+ new.parse_file(filepath)
+ end
+ end
+
+ private
+
+ # Translate the given parse result and filepath into the
+ # seattlerb/ruby_parser gem's Sexp format.
+ def translate(result, filepath)
+ if result.failure?
+ error = result.errors.first
+ raise ::RubyParser::SyntaxError, "#{filepath}:#{error.location.start_line} :: #{error.message}"
+ end
+
+ result.value.accept(Compiler.new(filepath))
+ end
+ end
+ end
+end
diff --git a/lib/pstore.rb b/lib/pstore.rb
index 97df3c1509..57ecb0ef5c 100644
--- a/lib/pstore.rb
+++ b/lib/pstore.rb
@@ -326,7 +326,7 @@ require "digest"
# end
#
class PStore
- VERSION = "0.1.2"
+ VERSION = "0.1.3"
RDWR_ACCESS = {mode: IO::RDWR | IO::CREAT | IO::BINARY, encoding: Encoding::ASCII_8BIT}.freeze
RD_ACCESS = {mode: IO::RDONLY | IO::BINARY, encoding: Encoding::ASCII_8BIT}.freeze
@@ -437,7 +437,7 @@ class PStore
in_transaction
unless @table.key? key
if default == PStore::Error
- raise PStore::Error, format("undefined key `%s'", key)
+ raise PStore::Error, format("undefined key '%s'", key)
else
return default
end
@@ -517,8 +517,8 @@ class PStore
end
# Exits the current transaction block, committing any changes
- # specified in the transaction block.
- # See {Committing or Aborting}[rdoc-ref:PStore@Committing+or+Aborting].
+ # specified in the
+ # {transaction block}[rdoc-ref:PStore@The+Transaction+Block].
#
# Raises an exception if called outside a transaction block.
def commit
@@ -528,8 +528,8 @@ class PStore
end
# Exits the current transaction block, discarding any changes
- # specified in the transaction block.
- # See {Committing or Aborting}[rdoc-ref:PStore@Committing+or+Aborting].
+ # specified in the
+ # {transaction block}[rdoc-ref:PStore@The+Transaction+Block].
#
# Raises an exception if called outside a transaction block.
def abort
diff --git a/lib/racc.rb b/lib/racc.rb
deleted file mode 100644
index f6e4ac03a8..0000000000
--- a/lib/racc.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-require 'racc/compat'
-require 'racc/debugflags'
-require 'racc/grammar'
-require 'racc/state'
-require 'racc/exception'
-require 'racc/info'
diff --git a/lib/racc/compat.rb b/lib/racc/compat.rb
deleted file mode 100644
index 62f4f630be..0000000000
--- a/lib/racc/compat.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-#--
-#
-#
-#
-# Copyright (c) 1999-2006 Minero Aoki
-#
-# This program is free software.
-# You can distribute/modify this program under the same terms of ruby.
-# see the file "COPYING".
-#
-#++
-
-unless Object.method_defined?(:__send)
- class Object
- alias __send __send__
- end
-end
-
-unless Object.method_defined?(:__send!)
- class Object
- alias __send! __send__
- end
-end
-
-unless Array.method_defined?(:map!)
- class Array
- if Array.method_defined?(:collect!)
- alias map! collect!
- else
- alias map! filter
- end
- end
-end
diff --git a/lib/racc/debugflags.rb b/lib/racc/debugflags.rb
deleted file mode 100644
index ee34cf2314..0000000000
--- a/lib/racc/debugflags.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-#--
-#
-#
-#
-# Copyright (c) 1999-2006 Minero Aoki
-#
-# This program is free software.
-# You can distribute/modify this program under the same terms of ruby.
-# see the file "COPYING".
-#
-#++
-
-module Racc
-
- class DebugFlags
- def DebugFlags.parse_option_string(s)
- parse = rule = token = state = la = prec = conf = false
- s.split(//).each do |ch|
- case ch
- when 'p' then parse = true
- when 'r' then rule = true
- when 't' then token = true
- when 's' then state = true
- when 'l' then la = true
- when 'c' then prec = true
- when 'o' then conf = true
- else
- raise "unknown debug flag char: #{ch.inspect}"
- end
- end
- new(parse, rule, token, state, la, prec, conf)
- end
-
- def initialize(parse = false, rule = false, token = false, state = false,
- la = false, prec = false, conf = false)
- @parse = parse
- @rule = rule
- @token = token
- @state = state
- @la = la
- @prec = prec
- @any = (parse || rule || token || state || la || prec)
- @status_logging = conf
- end
-
- attr_reader :parse
- attr_reader :rule
- attr_reader :token
- attr_reader :state
- attr_reader :la
- attr_reader :prec
-
- def any?
- @any
- end
-
- attr_reader :status_logging
- end
-
-end
diff --git a/lib/racc/exception.rb b/lib/racc/exception.rb
deleted file mode 100644
index c11dc2e43e..0000000000
--- a/lib/racc/exception.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-#--
-#
-#
-#
-# Copyright (c) 1999-2006 Minero Aoki
-#
-# This program is free software.
-# You can distribute/modify this program under the same terms of ruby.
-# see the file "COPYING".
-#
-#++
-
-module Racc
- class Error < StandardError; end
- class CompileError < Error; end
-end
diff --git a/lib/racc/grammar.rb b/lib/racc/grammar.rb
deleted file mode 100644
index 7159862ffb..0000000000
--- a/lib/racc/grammar.rb
+++ /dev/null
@@ -1,1114 +0,0 @@
-#--
-#
-#
-#
-# Copyright (c) 1999-2006 Minero Aoki
-#
-# This program is free software.
-# You can distribute/modify this program under the same terms of ruby.
-# see the file "COPYING".
-#
-#++
-
-require 'racc/compat'
-require 'racc/iset'
-require 'racc/sourcetext'
-require 'racc/logfilegenerator'
-require 'racc/exception'
-require 'forwardable'
-
-module Racc
-
- class Grammar
-
- def initialize(debug_flags = DebugFlags.new)
- @symboltable = SymbolTable.new
- @debug_symbol = debug_flags.token
- @rules = [] # :: [Rule]
- @start = nil
- @n_expected_srconflicts = nil
- @prec_table = []
- @prec_table_closed = false
- @closed = false
- @states = nil
- end
-
- attr_reader :start
- attr_reader :symboltable
- attr_accessor :n_expected_srconflicts
-
- def [](x)
- @rules[x]
- end
-
- def each_rule(&block)
- @rules.each(&block)
- end
-
- alias each each_rule
-
- def each_index(&block)
- @rules.each_index(&block)
- end
-
- def each_with_index(&block)
- @rules.each_with_index(&block)
- end
-
- def size
- @rules.size
- end
-
- def to_s
- "<Racc::Grammar>"
- end
-
- extend Forwardable
-
- def_delegator "@symboltable", :each, :each_symbol
- def_delegator "@symboltable", :each_terminal
- def_delegator "@symboltable", :each_nonterminal
-
- def intern(value, dummy = false)
- @symboltable.intern(value, dummy)
- end
-
- def symbols
- @symboltable.symbols
- end
-
- def nonterminal_base
- @symboltable.nt_base
- end
-
- def useless_nonterminal_exist?
- n_useless_nonterminals() != 0
- end
-
- def n_useless_nonterminals
- @n_useless_nonterminals ||= each_useless_nonterminal.count
- end
-
- def each_useless_nonterminal
- return to_enum __method__ unless block_given?
-
- @symboltable.each_nonterminal do |sym|
- yield sym if sym.useless?
- end
- end
-
- def useless_rule_exist?
- n_useless_rules() != 0
- end
-
- def n_useless_rules
- @n_useless_rules ||= each_useless_rule.count
- end
-
- def each_useless_rule
- return to_enum __method__ unless block_given?
-
- each do |r|
- yield r if r.useless?
- end
- end
-
- def nfa
- (@states ||= States.new(self)).nfa
- end
-
- def dfa
- (@states ||= States.new(self)).dfa
- end
-
- alias states dfa
-
- def state_transition_table
- states().state_transition_table
- end
-
- def parser_class
- states = states() # cache
- if $DEBUG
- srcfilename = caller(1).first.slice(/\A(.*?):/, 1)
- begin
- write_log srcfilename + ".output"
- rescue SystemCallError
- end
- report = lambda {|s| $stderr.puts "racc: #{srcfilename}: #{s}" }
- if states.should_report_srconflict?
- report["#{states.n_srconflicts} shift/reduce conflicts"]
- end
- if states.rrconflict_exist?
- report["#{states.n_rrconflicts} reduce/reduce conflicts"]
- end
- g = states.grammar
- if g.useless_nonterminal_exist?
- report["#{g.n_useless_nonterminals} useless nonterminals"]
- end
- if g.useless_rule_exist?
- report["#{g.n_useless_rules} useless rules"]
- end
- end
- states.state_transition_table.parser_class
- end
-
- def write_log(path)
- File.open(path, 'w') {|f|
- LogFileGenerator.new(states()).output f
- }
- end
-
- #
- # Grammar Definition Interface
- #
-
- def add(rule)
- raise ArgumentError, "rule added after the Grammar closed" if @closed
- @rules.push rule
- end
-
- def added?(sym)
- @rules.detect {|r| r.target == sym }
- end
-
- def start_symbol=(s)
- raise CompileError, "start symbol set twice'" if @start
- @start = s
- end
-
- def declare_precedence(assoc, syms)
- raise CompileError, "precedence table defined twice" if @prec_table_closed
- @prec_table.push [assoc, syms]
- end
-
- def end_precedence_declaration(reverse)
- @prec_table_closed = true
- return if @prec_table.empty?
- table = reverse ? @prec_table.reverse : @prec_table
- table.each_with_index do |(assoc, syms), idx|
- syms.each do |sym|
- sym.assoc = assoc
- sym.precedence = idx
- end
- end
- end
-
- #
- # Dynamic Generation Interface
- #
-
- def Grammar.define(&block)
- env = DefinitionEnv.new
- env.instance_eval(&block)
- env.grammar
- end
-
- class DefinitionEnv
- def initialize
- @grammar = Grammar.new
- @seqs = Hash.new(0)
- @delayed = []
- end
-
- def grammar
- flush_delayed
- @grammar.each do |rule|
- if rule.specified_prec
- rule.specified_prec = @grammar.intern(rule.specified_prec)
- end
- end
- @grammar.init
- @grammar
- end
-
- def precedence_table(&block)
- env = PrecedenceDefinitionEnv.new(@grammar)
- env.instance_eval(&block)
- @grammar.end_precedence_declaration env.reverse
- end
-
- def method_missing(mid, *args, &block)
- unless mid.to_s[-1,1] == '='
- super # raises NoMethodError
- end
- target = @grammar.intern(mid.to_s.chop.intern)
- unless args.size == 1
- raise ArgumentError, "too many arguments for #{mid} (#{args.size} for 1)"
- end
- _add target, args.first
- end
-
- def _add(target, x)
- case x
- when Sym
- @delayed.each do |rule|
- rule.replace x, target if rule.target == x
- end
- @grammar.symboltable.delete x
- else
- x.each_rule do |r|
- r.target = target
- @grammar.add r
- end
- end
- flush_delayed
- end
-
- def _delayed_add(rule)
- @delayed.push rule
- end
-
- def _added?(sym)
- @grammar.added?(sym) or @delayed.detect {|r| r.target == sym }
- end
-
- def flush_delayed
- return if @delayed.empty?
- @delayed.each do |rule|
- @grammar.add rule
- end
- @delayed.clear
- end
-
- def seq(*list, &block)
- Rule.new(nil, list.map {|x| _intern(x) }, UserAction.proc(block))
- end
-
- def null(&block)
- seq(&block)
- end
-
- def action(&block)
- id = "@#{@seqs["action"] += 1}".intern
- _delayed_add Rule.new(@grammar.intern(id), [], UserAction.proc(block))
- id
- end
-
- alias _ action
-
- def option(sym, default = nil, &block)
- _defmetasyntax("option", _intern(sym), block) {|target|
- seq() { default } | seq(sym)
- }
- end
-
- def many(sym, &block)
- _defmetasyntax("many", _intern(sym), block) {|target|
- seq() { [] }\
- | seq(target, sym) {|list, x| list.push x; list }
- }
- end
-
- def many1(sym, &block)
- _defmetasyntax("many1", _intern(sym), block) {|target|
- seq(sym) {|x| [x] }\
- | seq(target, sym) {|list, x| list.push x; list }
- }
- end
-
- def separated_by(sep, sym, &block)
- option(separated_by1(sep, sym), [], &block)
- end
-
- def separated_by1(sep, sym, &block)
- _defmetasyntax("separated_by1", _intern(sym), block) {|target|
- seq(sym) {|x| [x] }\
- | seq(target, sep, sym) {|list, _, x| list.push x; list }
- }
- end
-
- def _intern(x)
- case x
- when Symbol, String
- @grammar.intern(x)
- when Racc::Sym
- x
- else
- raise TypeError, "wrong type #{x.class} (expected Symbol/String/Racc::Sym)"
- end
- end
-
- private
-
- def _defmetasyntax(type, id, action, &block)
- if action
- idbase = "#{type}@#{id}-#{@seqs[type] += 1}"
- target = _wrap(idbase, "#{idbase}-core", action)
- _register("#{idbase}-core", &block)
- else
- target = _register("#{type}@#{id}", &block)
- end
- @grammar.intern(target)
- end
-
- def _register(target_name)
- target = target_name.intern
- unless _added?(@grammar.intern(target))
- yield(target).each_rule do |rule|
- rule.target = @grammar.intern(target)
- _delayed_add rule
- end
- end
- target
- end
-
- def _wrap(target_name, sym, block)
- target = target_name.intern
- _delayed_add Rule.new(@grammar.intern(target),
- [@grammar.intern(sym.intern)],
- UserAction.proc(block))
- target
- end
- end
-
- class PrecedenceDefinitionEnv
- def initialize(g)
- @grammar = g
- @prechigh_seen = false
- @preclow_seen = false
- @reverse = false
- end
-
- attr_reader :reverse
-
- def higher
- if @prechigh_seen
- raise CompileError, "prechigh used twice"
- end
- @prechigh_seen = true
- end
-
- def lower
- if @preclow_seen
- raise CompileError, "preclow used twice"
- end
- if @prechigh_seen
- @reverse = true
- end
- @preclow_seen = true
- end
-
- def left(*syms)
- @grammar.declare_precedence :Left, syms.map {|s| @grammar.intern(s) }
- end
-
- def right(*syms)
- @grammar.declare_precedence :Right, syms.map {|s| @grammar.intern(s) }
- end
-
- def nonassoc(*syms)
- @grammar.declare_precedence :Nonassoc, syms.map {|s| @grammar.intern(s)}
- end
- end
-
- #
- # Computation
- #
-
- def init
- return if @closed
- @closed = true
- @start ||= @rules.map {|r| r.target }.detect {|sym| not sym.dummy? }
- raise CompileError, 'no rule in input' if @rules.empty?
- add_start_rule
- @rules.freeze
- fix_ident
- compute_hash
- compute_heads
- determine_terminals
- compute_nullable_0
- @symboltable.fix
- compute_locate
- @symboltable.each_nonterminal {|t| compute_expand t }
- compute_nullable
- compute_useless
- end
-
- private
-
- def add_start_rule
- r = Rule.new(@symboltable.dummy,
- [@start, @symboltable.anchor, @symboltable.anchor],
- UserAction.empty)
- r.ident = 0
- r.hash = 0
- r.precedence = nil
- @rules.unshift r
- end
-
- # Rule#ident
- # LocationPointer#ident
- def fix_ident
- @rules.each_with_index do |rule, idx|
- rule.ident = idx
- end
- end
-
- # Rule#hash
- def compute_hash
- hash = 4 # size of dummy rule
- @rules.each do |rule|
- rule.hash = hash
- hash += (rule.size + 1)
- end
- end
-
- # Sym#heads
- def compute_heads
- @rules.each do |rule|
- rule.target.heads.push rule.ptrs[0]
- end
- end
-
- # Sym#terminal?
- def determine_terminals
- @symboltable.each do |s|
- s.term = s.heads.empty?
- end
- end
-
- # Sym#self_null?
- def compute_nullable_0
- @symboltable.each do |s|
- if s.terminal?
- s.snull = false
- else
- s.snull = s.heads.any? {|loc| loc.reduce? }
- end
- end
- end
-
- # Sym#locate
- def compute_locate
- @rules.each do |rule|
- t = nil
- rule.ptrs.each do |ptr|
- unless ptr.reduce?
- tok = ptr.dereference
- tok.locate.push ptr
- t = tok if tok.terminal?
- end
- end
- rule.precedence = t
- end
- end
-
- # Sym#expand
- def compute_expand(t)
- puts "expand> #{t.to_s}" if @debug_symbol
- t.expand = _compute_expand(t, ISet.new, [])
- puts "expand< #{t.to_s}: #{t.expand.to_s}" if @debug_symbol
- end
-
- def _compute_expand(t, set, lock)
- if tmp = t.expand
- set.update tmp
- return set
- end
- tok = nil
- set.update_a t.heads
- t.heads.each do |ptr|
- tok = ptr.dereference
- if tok and tok.nonterminal?
- unless lock[tok.ident]
- lock[tok.ident] = true
- _compute_expand tok, set, lock
- end
- end
- end
- set
- end
-
- # Sym#nullable?, Rule#nullable?
- def compute_nullable
- @rules.each {|r| r.null = false }
- @symboltable.each {|t| t.null = false }
- r = @rules.dup
- s = @symboltable.nonterminals
- begin
- rs = r.size
- ss = s.size
- check_rules_nullable r
- check_symbols_nullable s
- end until rs == r.size and ss == s.size
- end
-
- def check_rules_nullable(rules)
- rules.delete_if do |rule|
- rule.null = true
- rule.symbols.each do |t|
- unless t.nullable?
- rule.null = false
- break
- end
- end
- rule.nullable?
- end
- end
-
- def check_symbols_nullable(symbols)
- symbols.delete_if do |sym|
- sym.heads.each do |ptr|
- if ptr.rule.nullable?
- sym.null = true
- break
- end
- end
- sym.nullable?
- end
- end
-
- # Sym#useless?, Rule#useless?
- # FIXME: what means "useless"?
- def compute_useless
- @symboltable.each_terminal {|sym| sym.useless = false }
- @symboltable.each_nonterminal {|sym| sym.useless = true }
- @rules.each {|rule| rule.useless = true }
- r = @rules.dup
- s = @symboltable.nonterminals
- begin
- rs = r.size
- ss = s.size
- check_rules_useless r
- check_symbols_useless s
- end until r.size == rs and s.size == ss
- end
-
- def check_rules_useless(rules)
- rules.delete_if do |rule|
- rule.useless = false
- rule.symbols.each do |sym|
- if sym.useless?
- rule.useless = true
- break
- end
- end
- not rule.useless?
- end
- end
-
- def check_symbols_useless(s)
- s.delete_if do |t|
- t.heads.each do |ptr|
- unless ptr.rule.useless?
- t.useless = false
- break
- end
- end
- not t.useless?
- end
- end
-
- end # class Grammar
-
-
- class Rule
-
- def initialize(target, syms, act)
- @target = target
- @symbols = syms
- @action = act
- @alternatives = []
-
- @ident = nil
- @hash = nil
- @ptrs = nil
- @precedence = nil
- @specified_prec = nil
- @null = nil
- @useless = nil
- end
-
- attr_accessor :target
- attr_reader :symbols
- attr_reader :action
-
- def |(x)
- @alternatives.push x.rule
- self
- end
-
- def rule
- self
- end
-
- def each_rule(&block)
- yield self
- @alternatives.each(&block)
- end
-
- attr_accessor :ident
-
- attr_reader :hash
- attr_reader :ptrs
-
- def hash=(n)
- @hash = n
- ptrs = []
- @symbols.each_with_index do |sym, idx|
- ptrs.push LocationPointer.new(self, idx, sym)
- end
- ptrs.push LocationPointer.new(self, @symbols.size, nil)
- @ptrs = ptrs
- end
-
- def precedence
- @specified_prec || @precedence
- end
-
- def precedence=(sym)
- @precedence ||= sym
- end
-
- def prec(sym, &block)
- @specified_prec = sym
- if block
- unless @action.empty?
- raise CompileError, 'both of rule action block and prec block given'
- end
- @action = UserAction.proc(block)
- end
- self
- end
-
- attr_accessor :specified_prec
-
- def nullable?() @null end
- def null=(n) @null = n end
-
- def useless?() @useless end
- def useless=(u) @useless = u end
-
- def inspect
- "#<Racc::Rule id=#{@ident} (#{@target})>"
- end
-
- def ==(other)
- other.kind_of?(Rule) and @ident == other.ident
- end
-
- def [](idx)
- @symbols[idx]
- end
-
- def size
- @symbols.size
- end
-
- def empty?
- @symbols.empty?
- end
-
- def to_s
- "#<rule#{@ident}>"
- end
-
- def accept?
- if tok = @symbols[-1]
- tok.anchor?
- else
- false
- end
- end
-
- def each(&block)
- @symbols.each(&block)
- end
-
- def replace(src, dest)
- @target = dest
- @symbols = @symbols.map {|s| s == src ? dest : s }
- end
-
- end # class Rule
-
-
- class UserAction
-
- def UserAction.source_text(src)
- new(src, nil)
- end
-
- def UserAction.proc(pr = nil, &block)
- if pr and block
- raise ArgumentError, "both of argument and block given"
- end
- new(nil, pr || block)
- end
-
- def UserAction.empty
- new(nil, nil)
- end
-
- private_class_method :new
-
- def initialize(src, proc)
- @source = src
- @proc = proc
- end
-
- attr_reader :source
- attr_reader :proc
-
- def source?
- not @proc
- end
-
- def proc?
- not @source
- end
-
- def empty?
- not @proc and not @source
- end
-
- def name
- "{action type=#{@source || @proc || 'nil'}}"
- end
-
- alias inspect name
-
- end
-
-
- class OrMark
- def initialize(lineno)
- @lineno = lineno
- end
-
- def name
- '|'
- end
-
- alias inspect name
-
- attr_reader :lineno
- end
-
-
- class Prec
- def initialize(symbol, lineno)
- @symbol = symbol
- @lineno = lineno
- end
-
- def name
- "=#{@symbol}"
- end
-
- alias inspect name
-
- attr_reader :symbol
- attr_reader :lineno
- end
-
-
- #
- # A set of rule and position in it's RHS.
- # Note that the number of pointers is more than rule's RHS array,
- # because pointer points right edge of the final symbol when reducing.
- #
- class LocationPointer
-
- def initialize(rule, i, sym)
- @rule = rule
- @index = i
- @symbol = sym
- @ident = @rule.hash + i
- @reduce = sym.nil?
- end
-
- attr_reader :rule
- attr_reader :index
- attr_reader :symbol
-
- alias dereference symbol
-
- attr_reader :ident
- alias hash ident
- attr_reader :reduce
- alias reduce? reduce
-
- def to_s
- sprintf('(%d,%d %s)',
- @rule.ident, @index, (reduce?() ? '#' : @symbol.to_s))
- end
-
- alias inspect to_s
-
- def eql?(ot)
- @hash == ot.hash
- end
-
- alias == eql?
-
- def head?
- @index == 0
- end
-
- def next
- @rule.ptrs[@index + 1] or ptr_bug!
- end
-
- alias increment next
-
- def before(len)
- @rule.ptrs[@index - len] or ptr_bug!
- end
-
- private
-
- def ptr_bug!
- raise "racc: fatal: pointer not exist: self: #{to_s}"
- end
-
- end # class LocationPointer
-
-
- class SymbolTable
-
- include Enumerable
-
- def initialize
- @symbols = [] # :: [Racc::Sym]
- @cache = {} # :: {(String|Symbol) => Racc::Sym}
- @dummy = intern(:$start, true)
- @anchor = intern(false, true) # Symbol ID = 0
- @error = intern(:error, false) # Symbol ID = 1
- end
-
- attr_reader :dummy
- attr_reader :anchor
- attr_reader :error
-
- def [](id)
- @symbols[id]
- end
-
- def intern(val, dummy = false)
- @cache[val] ||=
- begin
- sym = Sym.new(val, dummy)
- @symbols.push sym
- sym
- end
- end
-
- attr_reader :symbols
- alias to_a symbols
-
- def delete(sym)
- @symbols.delete sym
- @cache.delete sym.value
- end
-
- attr_reader :nt_base
-
- def nt_max
- @symbols.size
- end
-
- def each(&block)
- @symbols.each(&block)
- end
-
- def terminals(&block)
- @symbols[0, @nt_base]
- end
-
- def each_terminal(&block)
- @terms.each(&block)
- end
-
- def nonterminals
- @symbols[@nt_base, @symbols.size - @nt_base]
- end
-
- def each_nonterminal(&block)
- @nterms.each(&block)
- end
-
- def fix
- terms, nterms = @symbols.partition {|s| s.terminal? }
- @symbols = terms + nterms
- @terms = terms
- @nterms = nterms
- @nt_base = terms.size
- fix_ident
- check_terminals
- end
-
- private
-
- def fix_ident
- @symbols.each_with_index do |t, i|
- t.ident = i
- end
- end
-
- def check_terminals
- return unless @symbols.any? {|s| s.should_terminal? }
- @anchor.should_terminal
- @error.should_terminal
- each_terminal do |t|
- t.should_terminal if t.string_symbol?
- end
- each do |s|
- s.should_terminal if s.assoc
- end
- terminals().reject {|t| t.should_terminal? }.each do |t|
- raise CompileError, "terminal #{t} not declared as terminal"
- end
- nonterminals().select {|n| n.should_terminal? }.each do |n|
- raise CompileError, "symbol #{n} declared as terminal but is not terminal"
- end
- end
-
- end # class SymbolTable
-
-
- # Stands terminal and nonterminal symbols.
- class Sym
-
- def initialize(value, dummyp)
- @ident = nil
- @value = value
- @dummyp = dummyp
-
- @term = nil
- @nterm = nil
- @should_terminal = false
- @precedence = nil
- case value
- when Symbol
- @to_s = value.to_s
- @serialized = value.inspect
- @string = false
- when String
- @to_s = value.inspect
- @serialized = value.dump
- @string = true
- when false
- @to_s = '$end'
- @serialized = 'false'
- @string = false
- else
- raise ArgumentError, "unknown symbol value: #{value.class}"
- end
-
- @heads = []
- @locate = []
- @snull = nil
- @null = nil
- @expand = nil
- @useless = nil
- end
-
- class << self
- def once_writer(nm)
- nm = nm.id2name
- module_eval(<<-EOS)
- def #{nm}=(v)
- raise 'racc: fatal: @#{nm} != nil' unless @#{nm}.nil?
- @#{nm} = v
- end
- EOS
- end
- end
-
- once_writer :ident
- attr_reader :ident
-
- alias hash ident
-
- attr_reader :value
-
- def dummy?
- @dummyp
- end
-
- def terminal?
- @term
- end
-
- def nonterminal?
- @nterm
- end
-
- def term=(t)
- raise 'racc: fatal: term= called twice' unless @term.nil?
- @term = t
- @nterm = !t
- end
-
- def should_terminal
- @should_terminal = true
- end
-
- def should_terminal?
- @should_terminal
- end
-
- def string_symbol?
- @string
- end
-
- def serialize
- @serialized
- end
-
- attr_writer :serialized
-
- attr_accessor :precedence
- attr_accessor :assoc
-
- def to_s
- @to_s.dup
- end
-
- alias inspect to_s
-
- def |(x)
- rule() | x.rule
- end
-
- def rule
- Rule.new(nil, [self], UserAction.empty)
- end
-
- #
- # cache
- #
-
- attr_reader :heads
- attr_reader :locate
-
- def self_null?
- @snull
- end
-
- once_writer :snull
-
- def nullable?
- @null
- end
-
- def null=(n)
- @null = n
- end
-
- attr_reader :expand
- once_writer :expand
-
- def useless?
- @useless
- end
-
- def useless=(f)
- @useless = f
- end
-
- end # class Sym
-
-end # module Racc
diff --git a/lib/racc/grammarfileparser.rb b/lib/racc/grammarfileparser.rb
deleted file mode 100644
index c7d1207f0b..0000000000
--- a/lib/racc/grammarfileparser.rb
+++ /dev/null
@@ -1,561 +0,0 @@
-#--
-#
-#
-#
-# Copyright (c) 1999-2006 Minero Aoki
-#
-# This program is free software.
-# You can distribute/modify this program under the same terms of ruby.
-# see the file "COPYING".
-#
-#++
-
-require 'racc'
-require 'racc/compat'
-require 'racc/grammar'
-require 'racc/parserfilegenerator'
-require 'racc/sourcetext'
-require 'stringio'
-
-module Racc
-
- grammar = Grammar.define {
- g = self
-
- g.class = seq(:CLASS, :cname, many(:param), :RULE, :rules, option(:END))
-
- g.cname = seq(:rubyconst) {|name|
- @result.params.classname = name
- }\
- | seq(:rubyconst, "<", :rubyconst) {|c, _, s|
- @result.params.classname = c
- @result.params.superclass = s
- }
-
- g.rubyconst = separated_by1(:colon2, :SYMBOL) {|syms|
- syms.map {|s| s.to_s }.join('::')
- }
-
- g.colon2 = seq(':', ':')
-
- g.param = seq(:CONV, many1(:convdef), :END) {|*|
- #@grammar.end_convert_block # FIXME
- }\
- | seq(:PRECHIGH, many1(:precdef), :PRECLOW) {|*|
- @grammar.end_precedence_declaration true
- }\
- | seq(:PRECLOW, many1(:precdef), :PRECHIGH) {|*|
- @grammar.end_precedence_declaration false
- }\
- | seq(:START, :symbol) {|_, sym|
- @grammar.start_symbol = sym
- }\
- | seq(:TOKEN, :symbols) {|_, syms|
- syms.each do |s|
- s.should_terminal
- end
- }\
- | seq(:OPTION, :options) {|_, syms|
- syms.each do |opt|
- case opt
- when 'result_var'
- @result.params.result_var = true
- when 'no_result_var'
- @result.params.result_var = false
- when 'omit_action_call'
- @result.params.omit_action_call = true
- when 'no_omit_action_call'
- @result.params.omit_action_call = false
- else
- raise CompileError, "unknown option: #{opt}"
- end
- end
- }\
- | seq(:EXPECT, :DIGIT) {|_, num|
- if @grammar.n_expected_srconflicts
- raise CompileError, "`expect' seen twice"
- end
- @grammar.n_expected_srconflicts = num
- }
-
- g.convdef = seq(:symbol, :STRING) {|sym, code|
- sym.serialized = code
- }
-
- g.precdef = seq(:LEFT, :symbols) {|_, syms|
- @grammar.declare_precedence :Left, syms
- }\
- | seq(:RIGHT, :symbols) {|_, syms|
- @grammar.declare_precedence :Right, syms
- }\
- | seq(:NONASSOC, :symbols) {|_, syms|
- @grammar.declare_precedence :Nonassoc, syms
- }
-
- g.symbols = seq(:symbol) {|sym|
- [sym]
- }\
- | seq(:symbols, :symbol) {|list, sym|
- list.push sym
- list
- }\
- | seq(:symbols, "|")
-
- g.symbol = seq(:SYMBOL) {|sym| @grammar.intern(sym) }\
- | seq(:STRING) {|str| @grammar.intern(str) }
-
- g.options = many(:SYMBOL) {|syms| syms.map {|s| s.to_s } }
-
- g.rules = option(:rules_core) {|list|
- add_rule_block list unless list.empty?
- nil
- }
-
- g.rules_core = seq(:symbol) {|sym|
- [sym]
- }\
- | seq(:rules_core, :rule_item) {|list, i|
- list.push i
- list
- }\
- | seq(:rules_core, ';') {|list, *|
- add_rule_block list unless list.empty?
- list.clear
- list
- }\
- | seq(:rules_core, ':') {|list, *|
- next_target = list.pop
- add_rule_block list unless list.empty?
- [next_target]
- }
-
- g.rule_item = seq(:symbol)\
- | seq("|") {|*|
- OrMark.new(@scanner.lineno)
- }\
- | seq("=", :symbol) {|_, sym|
- Prec.new(sym, @scanner.lineno)
- }\
- | seq(:ACTION) {|src|
- UserAction.source_text(src)
- }
- }
-
- GrammarFileParser = grammar.parser_class
-
- if grammar.states.srconflict_exist?
- raise 'Racc boot script fatal: S/R conflict in build'
- end
- if grammar.states.rrconflict_exist?
- raise 'Racc boot script fatal: R/R conflict in build'
- end
-
- class GrammarFileParser # reopen
-
- class Result
- def initialize(grammar)
- @grammar = grammar
- @params = ParserFileGenerator::Params.new
- end
-
- attr_reader :grammar
- attr_reader :params
- end
-
- def GrammarFileParser.parse_file(filename)
- parse(File.read(filename), filename, 1)
- end
-
- def GrammarFileParser.parse(src, filename = '-', lineno = 1)
- new().parse(src, filename, lineno)
- end
-
- def initialize(debug_flags = DebugFlags.new)
- @yydebug = debug_flags.parse
- end
-
- def parse(src, filename = '-', lineno = 1)
- @filename = filename
- @lineno = lineno
- @scanner = GrammarFileScanner.new(src, @filename)
- @scanner.debug = @yydebug
- @grammar = Grammar.new
- @result = Result.new(@grammar)
- @embedded_action_seq = 0
- yyparse @scanner, :yylex
- parse_user_code
- @result.grammar.init
- @result
- end
-
- private
-
- def next_token
- @scanner.scan
- end
-
- def on_error(tok, val, _values)
- if val.respond_to?(:id2name)
- v = val.id2name
- elsif val.kind_of?(String)
- v = val
- else
- v = val.inspect
- end
- raise CompileError, "#{location()}: unexpected token '#{v}'"
- end
-
- def location
- "#{@filename}:#{@lineno - 1 + @scanner.lineno}"
- end
-
- def add_rule_block(list)
- sprec = nil
- target = list.shift
- case target
- when OrMark, UserAction, Prec
- raise CompileError, "#{target.lineno}: unexpected symbol #{target.name}"
- end
- curr = []
- list.each do |i|
- case i
- when OrMark
- add_rule target, curr, sprec
- curr = []
- sprec = nil
- when Prec
- raise CompileError, "'=<prec>' used twice in one rule" if sprec
- sprec = i.symbol
- else
- curr.push i
- end
- end
- add_rule target, curr, sprec
- end
-
- def add_rule(target, list, sprec)
- if list.last.kind_of?(UserAction)
- act = list.pop
- else
- act = UserAction.empty
- end
- list.map! {|s| s.kind_of?(UserAction) ? embedded_action(s) : s }
- rule = Rule.new(target, list, act)
- rule.specified_prec = sprec
- @grammar.add rule
- end
-
- def embedded_action(act)
- sym = @grammar.intern("@#{@embedded_action_seq += 1}".intern, true)
- @grammar.add Rule.new(sym, [], act)
- sym
- end
-
- #
- # User Code Block
- #
-
- def parse_user_code
- line = @scanner.lineno
- _, *blocks = *@scanner.epilogue.split(/^----/)
- blocks.each do |block|
- header, *body = block.lines.to_a
- label0, paths = *header.sub(/\A-+/, '').split('=', 2)
- label = canonical_label(label0)
- (paths ? paths.strip.split(' ') : []).each do |path|
- add_user_code label, SourceText.new(File.read(path), path, 1)
- end
- add_user_code label, SourceText.new(body.join(''), @filename, line + 1)
- line += (1 + body.size)
- end
- end
-
- USER_CODE_LABELS = {
- 'header' => :header,
- 'prepare' => :header, # obsolete
- 'inner' => :inner,
- 'footer' => :footer,
- 'driver' => :footer # obsolete
- }
-
- def canonical_label(src)
- label = src.to_s.strip.downcase.slice(/\w+/)
- unless USER_CODE_LABELS.key?(label)
- raise CompileError, "unknown user code type: #{label.inspect}"
- end
- label
- end
-
- def add_user_code(label, src)
- @result.params.public_send(USER_CODE_LABELS[label]).push src
- end
-
- end
-
-
- class GrammarFileScanner
-
- def initialize(str, filename = '-')
- @lines = str.b.split(/\n|\r\n|\r/)
- @filename = filename
- @lineno = -1
- @line_head = true
- @in_rule_blk = false
- @in_conv_blk = false
- @in_block = nil
- @epilogue = ''
- @debug = false
- next_line
- end
-
- attr_reader :epilogue
-
- def lineno
- @lineno + 1
- end
-
- attr_accessor :debug
-
- def yylex(&block)
- unless @debug
- yylex0(&block)
- else
- yylex0 do |sym, tok|
- $stderr.printf "%7d %-10s %s\n", lineno(), sym.inspect, tok.inspect
- yield [sym, tok]
- end
- end
- end
-
- private
-
- def yylex0
- begin
- until @line.empty?
- @line.sub!(/\A\s+/, '')
- if /\A\#/ =~ @line
- break
- elsif /\A\/\*/ =~ @line
- skip_comment
- elsif s = reads(/\A[a-zA-Z_]\w*/)
- yield [atom_symbol(s), s.intern]
- elsif s = reads(/\A\d+/)
- yield [:DIGIT, s.to_i]
- elsif ch = reads(/\A./)
- case ch
- when '"', "'"
- yield [:STRING, eval(scan_quoted(ch))]
- when '{'
- lineno = lineno()
- yield [:ACTION, SourceText.new(scan_action(), @filename, lineno)]
- else
- if ch == '|'
- @line_head = false
- end
- yield [ch, ch]
- end
- else
- end
- end
- end while next_line()
- yield nil
- end
-
- def next_line
- @lineno += 1
- @line = @lines[@lineno]
- if not @line or /\A----/ =~ @line
- @epilogue = @lines.join("\n")
- @lines.clear
- @line = nil
- if @in_block
- @lineno -= 1
- scan_error! sprintf('unterminated %s', @in_block)
- end
- false
- else
- @line.sub!(/(?:\n|\r\n|\r)\z/, '')
- @line_head = true
- true
- end
- end
-
- ReservedWord = {
- 'right' => :RIGHT,
- 'left' => :LEFT,
- 'nonassoc' => :NONASSOC,
- 'preclow' => :PRECLOW,
- 'prechigh' => :PRECHIGH,
- 'token' => :TOKEN,
- 'convert' => :CONV,
- 'options' => :OPTION,
- 'start' => :START,
- 'expect' => :EXPECT,
- 'class' => :CLASS,
- 'rule' => :RULE,
- 'end' => :END
- }
-
- def atom_symbol(token)
- if token == 'end'
- symbol = :END
- @in_conv_blk = false
- @in_rule_blk = false
- else
- if @line_head and not @in_conv_blk and not @in_rule_blk
- symbol = ReservedWord[token] || :SYMBOL
- else
- symbol = :SYMBOL
- end
- case symbol
- when :RULE then @in_rule_blk = true
- when :CONV then @in_conv_blk = true
- end
- end
- @line_head = false
- symbol
- end
-
- def skip_comment
- @in_block = 'comment'
- until m = /\*\//.match(@line)
- next_line
- end
- @line = m.post_match
- @in_block = nil
- end
-
- $raccs_print_type = false
-
- def scan_action
- buf = String.new
- nest = 1
- pre = nil
- @in_block = 'action'
- begin
- pre = nil
- if s = reads(/\A\s+/)
- # does not set 'pre'
- buf << s
- end
- until @line.empty?
- if s = reads(/\A[^'"`{}%#\/\$]+/)
- buf << (pre = s)
- next
- end
- case ch = read(1)
- when '{'
- nest += 1
- buf << (pre = ch)
- when '}'
- nest -= 1
- if nest == 0
- @in_block = nil
- buf.sub!(/[ \t\f]+\z/, '')
- return buf
- end
- buf << (pre = ch)
- when '#' # comment
- buf << ch << @line
- break
- when "'", '"', '`'
- buf << (pre = scan_quoted(ch))
- when '%'
- if literal_head? pre, @line
- # % string, regexp, array
- buf << ch
- case ch = read(1)
- when /[qQx]/n
- buf << ch << (pre = scan_quoted(read(1), '%string'))
- when /wW/n
- buf << ch << (pre = scan_quoted(read(1), '%array'))
- when /s/n
- buf << ch << (pre = scan_quoted(read(1), '%symbol'))
- when /r/n
- buf << ch << (pre = scan_quoted(read(1), '%regexp'))
- when /[a-zA-Z0-9= ]/n # does not include "_"
- scan_error! "unknown type of % literal '%#{ch}'"
- else
- buf << (pre = scan_quoted(ch, '%string'))
- end
- else
- # operator
- buf << '||op->' if $raccs_print_type
- buf << (pre = ch)
- end
- when '/'
- if literal_head? pre, @line
- # regexp
- buf << (pre = scan_quoted(ch, 'regexp'))
- else
- # operator
- buf << '||op->' if $raccs_print_type
- buf << (pre = ch)
- end
- when '$' # gvar
- buf << ch << (pre = read(1))
- else
- raise 'racc: fatal: must not happen'
- end
- end
- buf << "\n"
- end while next_line()
- raise 'racc: fatal: scan finished before parser finished'
- end
-
- def literal_head?(pre, post)
- (!pre || /[a-zA-Z_0-9]/n !~ pre[-1,1]) &&
- !post.empty? && /\A[\s\=]/n !~ post
- end
-
- def read(len)
- s = @line[0, len]
- @line = @line[len .. -1]
- s
- end
-
- def reads(re)
- m = re.match(@line) or return nil
- @line = m.post_match
- m[0]
- end
-
- def scan_quoted(left, tag = 'string')
- buf = left.dup
- buf = "||#{tag}->" + buf if $raccs_print_type
- re = get_quoted_re(left)
- sv, @in_block = @in_block, tag
- begin
- if s = reads(re)
- buf << s
- break
- else
- buf << @line
- end
- end while next_line()
- @in_block = sv
- buf << "<-#{tag}||" if $raccs_print_type
- buf
- end
-
- LEFT_TO_RIGHT = {
- '(' => ')',
- '{' => '}',
- '[' => ']',
- '<' => '>'
- }
-
- CACHE = {}
-
- def get_quoted_re(left)
- term = Regexp.quote(LEFT_TO_RIGHT[left] || left)
- CACHE[left] ||= /\A[^#{term}\\]*(?:\\.[^\\#{term}]*)*#{term}/
- end
-
- def scan_error!(msg)
- raise CompileError, "#{lineno()}: #{msg}"
- end
-
- end
-
-end # module Racc
diff --git a/lib/racc/info.rb b/lib/racc/info.rb
deleted file mode 100644
index 37bff7edba..0000000000
--- a/lib/racc/info.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-#--
-#
-#
-#
-# Copyright (c) 1999-2006 Minero Aoki
-#
-# This program is free software.
-# You can distribute/modify this program under the same terms of ruby.
-# see the file "COPYING".
-#
-#++
-
-module Racc
- VERSION = '1.6.2'
- Version = VERSION
- Copyright = 'Copyright (c) 1999-2006 Minero Aoki'
-end
diff --git a/lib/racc/iset.rb b/lib/racc/iset.rb
deleted file mode 100644
index 339221d21b..0000000000
--- a/lib/racc/iset.rb
+++ /dev/null
@@ -1,92 +0,0 @@
-#--
-#
-#
-#
-# Copyright (c) 1999-2006 Minero Aoki
-#
-# This program is free software.
-# You can distribute/modify this program under the same terms of ruby.
-# see the file "COPYING".
-#
-#++
-
-module Racc
-
- # An "indexed" set. All items must respond to :ident.
- class ISet
-
- def initialize(a = [])
- @set = a
- end
-
- attr_reader :set
-
- def add(i)
- @set[i.ident] = i
- end
-
- def [](key)
- @set[key.ident]
- end
-
- def []=(key, val)
- @set[key.ident] = val
- end
-
- alias include? []
- alias key? []
-
- def update(other)
- s = @set
- o = other.set
- o.each_index do |idx|
- if t = o[idx]
- s[idx] = t
- end
- end
- end
-
- def update_a(a)
- s = @set
- a.each {|i| s[i.ident] = i }
- end
-
- def delete(key)
- i = @set[key.ident]
- @set[key.ident] = nil
- i
- end
-
- def each(&block)
- @set.compact.each(&block)
- end
-
- def to_a
- @set.compact
- end
-
- def to_s
- "[#{@set.compact.join(' ')}]"
- end
-
- alias inspect to_s
-
- def size
- @set.nitems
- end
-
- def empty?
- @set.nitems == 0
- end
-
- def clear
- @set.clear
- end
-
- def dup
- ISet.new(@set.dup)
- end
-
- end # class ISet
-
-end # module Racc
diff --git a/lib/racc/logfilegenerator.rb b/lib/racc/logfilegenerator.rb
deleted file mode 100644
index 2f5aa0c8b0..0000000000
--- a/lib/racc/logfilegenerator.rb
+++ /dev/null
@@ -1,212 +0,0 @@
-#--
-#
-#
-#
-# Copyright (c) 1999-2006 Minero Aoki
-#
-# This program is free software.
-# You can distribute/modify this program under the same terms of ruby.
-# see the file "COPYING".
-#
-#++
-
-module Racc
-
- class LogFileGenerator
-
- def initialize(states, debug_flags = DebugFlags.new)
- @states = states
- @grammar = states.grammar
- @debug_flags = debug_flags
- end
-
- def output(out)
- output_conflict out; out.puts
- output_useless out; out.puts
- output_rule out; out.puts
- output_token out; out.puts
- output_state out
- end
-
- #
- # Warnings
- #
-
- def output_conflict(out)
- @states.each do |state|
- if state.srconf
- out.printf "state %d contains %d shift/reduce conflicts\n",
- state.stateid, state.srconf.size
- end
- if state.rrconf
- out.printf "state %d contains %d reduce/reduce conflicts\n",
- state.stateid, state.rrconf.size
- end
- end
- end
-
- def output_useless(out)
- @grammar.each do |rl|
- if rl.useless?
- out.printf "rule %d (%s) never reduced\n",
- rl.ident, rl.target.to_s
- end
- end
- @grammar.each_nonterminal do |t|
- if t.useless?
- out.printf "useless nonterminal %s\n", t.to_s
- end
- end
- end
-
- #
- # States
- #
-
- def output_state(out)
- out << "--------- State ---------\n"
-
- showall = @debug_flags.la || @debug_flags.state
- @states.each do |state|
- out << "\nstate #{state.ident}\n\n"
-
- (showall ? state.closure : state.core).each do |ptr|
- pointer_out(out, ptr) if ptr.rule.ident != 0 or showall
- end
- out << "\n"
-
- action_out out, state
- end
- end
-
- def pointer_out(out, ptr)
- buf = sprintf("%4d) %s :", ptr.rule.ident, ptr.rule.target.to_s)
- ptr.rule.symbols.each_with_index do |tok, idx|
- buf << ' _' if idx == ptr.index
- buf << ' ' << tok.to_s
- end
- buf << ' _' if ptr.reduce?
- out.puts buf
- end
-
- def action_out(f, state)
- sr = state.srconf && state.srconf.dup
- rr = state.rrconf && state.rrconf.dup
- acts = state.action
- keys = acts.keys
- keys.sort! {|a,b| a.ident <=> b.ident }
-
- [ Shift, Reduce, Error, Accept ].each do |klass|
- keys.delete_if do |tok|
- act = acts[tok]
- if act.kind_of?(klass)
- outact f, tok, act
- if sr and c = sr.delete(tok)
- outsrconf f, c
- end
- if rr and c = rr.delete(tok)
- outrrconf f, c
- end
-
- true
- else
- false
- end
- end
- end
- sr.each {|tok, c| outsrconf f, c } if sr
- rr.each {|tok, c| outrrconf f, c } if rr
-
- act = state.defact
- if not act.kind_of?(Error) or @debug_flags.any?
- outact f, '$default', act
- end
-
- f.puts
- state.goto_table.each do |t, st|
- if t.nonterminal?
- f.printf " %-12s go to state %d\n", t.to_s, st.ident
- end
- end
- end
-
- def outact(f, t, act)
- case act
- when Shift
- f.printf " %-12s shift, and go to state %d\n",
- t.to_s, act.goto_id
- when Reduce
- f.printf " %-12s reduce using rule %d (%s)\n",
- t.to_s, act.ruleid, act.rule.target.to_s
- when Accept
- f.printf " %-12s accept\n", t.to_s
- when Error
- f.printf " %-12s error\n", t.to_s
- else
- raise "racc: fatal: wrong act for outact: act=#{act}(#{act.class})"
- end
- end
-
- def outsrconf(f, confs)
- confs.each do |c|
- r = c.reduce
- f.printf " %-12s [reduce using rule %d (%s)]\n",
- c.shift.to_s, r.ident, r.target.to_s
- end
- end
-
- def outrrconf(f, confs)
- confs.each do |c|
- r = c.low_prec
- f.printf " %-12s [reduce using rule %d (%s)]\n",
- c.token.to_s, r.ident, r.target.to_s
- end
- end
-
- #
- # Rules
- #
-
- def output_rule(out)
- out.print "-------- Grammar --------\n\n"
- @grammar.each do |rl|
- if @debug_flags.any? or rl.ident != 0
- out.printf "rule %d %s: %s\n",
- rl.ident, rl.target.to_s, rl.symbols.join(' ')
- end
- end
- end
-
- #
- # Tokens
- #
-
- def output_token(out)
- out.print "------- Symbols -------\n\n"
-
- out.print "**Nonterminals, with rules where they appear\n\n"
- @grammar.each_nonterminal do |t|
- tmp = <<SRC
- %s (%d)
- on right: %s
- on left : %s
-SRC
- out.printf tmp, t.to_s, t.ident,
- symbol_locations(t.locate).join(' '),
- symbol_locations(t.heads).join(' ')
- end
-
- out.print "\n**Terminals, with rules where they appear\n\n"
- @grammar.each_terminal do |t|
- out.printf " %s (%d) %s\n",
- t.to_s, t.ident, symbol_locations(t.locate).join(' ')
- end
- end
-
- def symbol_locations(locs)
- locs.map {|loc| loc.rule.ident }.reject {|n| n == 0 }.uniq
- end
-
- end
-
-end # module Racc
diff --git a/lib/racc/parser-text.rb b/lib/racc/parser-text.rb
deleted file mode 100644
index 63ac2e368b..0000000000
--- a/lib/racc/parser-text.rb
+++ /dev/null
@@ -1,637 +0,0 @@
-module Racc
- PARSER_TEXT = <<'__end_of_file__'
-# frozen_string_literal: false
-#--
-# Copyright (c) 1999-2006 Minero Aoki
-#
-# This program is free software.
-# You can distribute/modify this program under the same terms of ruby.
-#
-# As a special exception, when this code is copied by Racc
-# into a Racc output file, you may use that output file
-# without restriction.
-#++
-
-require 'racc/info'
-
-unless defined?(NotImplementedError)
- NotImplementedError = NotImplementError # :nodoc:
-end
-
-module Racc
- class ParseError < StandardError; end
-end
-unless defined?(::ParseError)
- ParseError = Racc::ParseError # :nodoc:
-end
-
-# Racc is a LALR(1) parser generator.
-# It is written in Ruby itself, and generates Ruby programs.
-#
-# == Command-line Reference
-#
-# racc [-o<var>filename</var>] [--output-file=<var>filename</var>]
-# [-e<var>rubypath</var>] [--executable=<var>rubypath</var>]
-# [-v] [--verbose]
-# [-O<var>filename</var>] [--log-file=<var>filename</var>]
-# [-g] [--debug]
-# [-E] [--embedded]
-# [-l] [--no-line-convert]
-# [-c] [--line-convert-all]
-# [-a] [--no-omit-actions]
-# [-C] [--check-only]
-# [-S] [--output-status]
-# [--version] [--copyright] [--help] <var>grammarfile</var>
-#
-# [+grammarfile+]
-# Racc grammar file. Any extension is permitted.
-# [-o+outfile+, --output-file=+outfile+]
-# A filename for output. default is <+filename+>.tab.rb
-# [-O+filename+, --log-file=+filename+]
-# Place logging output in file +filename+.
-# Default log file name is <+filename+>.output.
-# [-e+rubypath+, --executable=+rubypath+]
-# output executable file(mode 755). where +path+ is the Ruby interpreter.
-# [-v, --verbose]
-# verbose mode. create +filename+.output file, like yacc's y.output file.
-# [-g, --debug]
-# add debug code to parser class. To display debuggin information,
-# use this '-g' option and set @yydebug true in parser class.
-# [-E, --embedded]
-# Output parser which doesn't need runtime files (racc/parser.rb).
-# [-C, --check-only]
-# Check syntax of racc grammar file and quit.
-# [-S, --output-status]
-# Print messages time to time while compiling.
-# [-l, --no-line-convert]
-# turns off line number converting.
-# [-c, --line-convert-all]
-# Convert line number of actions, inner, header and footer.
-# [-a, --no-omit-actions]
-# Call all actions, even if an action is empty.
-# [--version]
-# print Racc version and quit.
-# [--copyright]
-# Print copyright and quit.
-# [--help]
-# Print usage and quit.
-#
-# == Generating Parser Using Racc
-#
-# To compile Racc grammar file, simply type:
-#
-# $ racc parse.y
-#
-# This creates Ruby script file "parse.tab.y". The -o option can change the output filename.
-#
-# == Writing A Racc Grammar File
-#
-# If you want your own parser, you have to write a grammar file.
-# A grammar file contains the name of your parser class, grammar for the parser,
-# user code, and anything else.
-# When writing a grammar file, yacc's knowledge is helpful.
-# If you have not used yacc before, Racc is not too difficult.
-#
-# Here's an example Racc grammar file.
-#
-# class Calcparser
-# rule
-# target: exp { print val[0] }
-#
-# exp: exp '+' exp
-# | exp '*' exp
-# | '(' exp ')'
-# | NUMBER
-# end
-#
-# Racc grammar files resemble yacc files.
-# But (of course), this is Ruby code.
-# yacc's $$ is the 'result', $0, $1... is
-# an array called 'val', and $-1, $-2... is an array called '_values'.
-#
-# See the {Grammar File Reference}[rdoc-ref:lib/racc/rdoc/grammar.en.rdoc] for
-# more information on grammar files.
-#
-# == Parser
-#
-# Then you must prepare the parse entry method. There are two types of
-# parse methods in Racc, Racc::Parser#do_parse and Racc::Parser#yyparse
-#
-# Racc::Parser#do_parse is simple.
-#
-# It's yyparse() of yacc, and Racc::Parser#next_token is yylex().
-# This method must returns an array like [TOKENSYMBOL, ITS_VALUE].
-# EOF is [false, false].
-# (TOKENSYMBOL is a Ruby symbol (taken from String#intern) by default.
-# If you want to change this, see the grammar reference.
-#
-# Racc::Parser#yyparse is little complicated, but useful.
-# It does not use Racc::Parser#next_token, instead it gets tokens from any iterator.
-#
-# For example, <code>yyparse(obj, :scan)</code> causes
-# calling +obj#scan+, and you can return tokens by yielding them from +obj#scan+.
-#
-# == Debugging
-#
-# When debugging, "-v" or/and the "-g" option is helpful.
-#
-# "-v" creates verbose log file (.output).
-# "-g" creates a "Verbose Parser".
-# Verbose Parser prints the internal status when parsing.
-# But it's _not_ automatic.
-# You must use -g option and set +@yydebug+ to +true+ in order to get output.
-# -g option only creates the verbose parser.
-#
-# === Racc reported syntax error.
-#
-# Isn't there too many "end"?
-# grammar of racc file is changed in v0.10.
-#
-# Racc does not use '%' mark, while yacc uses huge number of '%' marks..
-#
-# === Racc reported "XXXX conflicts".
-#
-# Try "racc -v xxxx.y".
-# It causes producing racc's internal log file, xxxx.output.
-#
-# === Generated parsers does not work correctly
-#
-# Try "racc -g xxxx.y".
-# This command let racc generate "debugging parser".
-# Then set @yydebug=true in your parser.
-# It produces a working log of your parser.
-#
-# == Re-distributing Racc runtime
-#
-# A parser, which is created by Racc, requires the Racc runtime module;
-# racc/parser.rb.
-#
-# Ruby 1.8.x comes with Racc runtime module,
-# you need NOT distribute Racc runtime files.
-#
-# If you want to include the Racc runtime module with your parser.
-# This can be done by using '-E' option:
-#
-# $ racc -E -omyparser.rb myparser.y
-#
-# This command creates myparser.rb which `includes' Racc runtime.
-# Only you must do is to distribute your parser file (myparser.rb).
-#
-# Note: parser.rb is ruby license, but your parser is not.
-# Your own parser is completely yours.
-module Racc
-
- unless defined?(Racc_No_Extensions)
- Racc_No_Extensions = false # :nodoc:
- end
-
- class Parser
-
- Racc_Runtime_Version = ::Racc::VERSION
- Racc_Runtime_Core_Version_R = ::Racc::VERSION
-
- begin
- if Object.const_defined?(:RUBY_ENGINE) and RUBY_ENGINE == 'jruby'
- require 'jruby'
- require 'racc/cparse-jruby.jar'
- com.headius.racc.Cparse.new.load(JRuby.runtime, false)
- else
- require 'racc/cparse'
- end
-
- unless new.respond_to?(:_racc_do_parse_c, true)
- raise LoadError, 'old cparse.so'
- end
- if Racc_No_Extensions
- raise LoadError, 'selecting ruby version of racc runtime core'
- end
-
- Racc_Main_Parsing_Routine = :_racc_do_parse_c # :nodoc:
- Racc_YY_Parse_Method = :_racc_yyparse_c # :nodoc:
- Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_C # :nodoc:
- Racc_Runtime_Type = 'c' # :nodoc:
- rescue LoadError
- Racc_Main_Parsing_Routine = :_racc_do_parse_rb
- Racc_YY_Parse_Method = :_racc_yyparse_rb
- Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_R
- Racc_Runtime_Type = 'ruby'
- end
-
- def Parser.racc_runtime_type # :nodoc:
- Racc_Runtime_Type
- end
-
- def _racc_setup
- @yydebug = false unless self.class::Racc_debug_parser
- @yydebug = false unless defined?(@yydebug)
- if @yydebug
- @racc_debug_out = $stderr unless defined?(@racc_debug_out)
- @racc_debug_out ||= $stderr
- end
- arg = self.class::Racc_arg
- arg[13] = true if arg.size < 14
- arg
- end
-
- def _racc_init_sysvars
- @racc_state = [0]
- @racc_tstack = []
- @racc_vstack = []
-
- @racc_t = nil
- @racc_val = nil
-
- @racc_read_next = true
-
- @racc_user_yyerror = false
- @racc_error_status = 0
- end
-
- # The entry point of the parser. This method is used with #next_token.
- # If Racc wants to get token (and its value), calls next_token.
- #
- # Example:
- # def parse
- # @q = [[1,1],
- # [2,2],
- # [3,3],
- # [false, '$']]
- # do_parse
- # end
- #
- # def next_token
- # @q.shift
- # end
- class_eval <<~RUBY, __FILE__, __LINE__ + 1
- def do_parse
- #{Racc_Main_Parsing_Routine}(_racc_setup(), false)
- end
- RUBY
-
- # The method to fetch next token.
- # If you use #do_parse method, you must implement #next_token.
- #
- # The format of return value is [TOKEN_SYMBOL, VALUE].
- # +token-symbol+ is represented by Ruby's symbol by default, e.g. :IDENT
- # for 'IDENT'. ";" (String) for ';'.
- #
- # The final symbol (End of file) must be false.
- def next_token
- raise NotImplementedError, "#{self.class}\#next_token is not defined"
- end
-
- def _racc_do_parse_rb(arg, in_debug)
- action_table, action_check, action_default, action_pointer,
- _, _, _, _,
- _, _, token_table, * = arg
-
- _racc_init_sysvars
- tok = act = i = nil
-
- catch(:racc_end_parse) {
- while true
- if i = action_pointer[@racc_state[-1]]
- if @racc_read_next
- if @racc_t != 0 # not EOF
- tok, @racc_val = next_token()
- unless tok # EOF
- @racc_t = 0
- else
- @racc_t = (token_table[tok] or 1) # error token
- end
- racc_read_token(@racc_t, tok, @racc_val) if @yydebug
- @racc_read_next = false
- end
- end
- i += @racc_t
- unless i >= 0 and
- act = action_table[i] and
- action_check[i] == @racc_state[-1]
- act = action_default[@racc_state[-1]]
- end
- else
- act = action_default[@racc_state[-1]]
- end
- while act = _racc_evalact(act, arg)
- ;
- end
- end
- }
- end
-
- # Another entry point for the parser.
- # If you use this method, you must implement RECEIVER#METHOD_ID method.
- #
- # RECEIVER#METHOD_ID is a method to get next token.
- # It must 'yield' the token, which format is [TOKEN-SYMBOL, VALUE].
- class_eval <<~RUBY, __FILE__, __LINE__ + 1
- def yyparse(recv, mid)
- #{Racc_YY_Parse_Method}(recv, mid, _racc_setup(), false)
- end
- RUBY
-
- def _racc_yyparse_rb(recv, mid, arg, c_debug)
- action_table, action_check, action_default, action_pointer,
- _, _, _, _,
- _, _, token_table, * = arg
-
- _racc_init_sysvars
-
- catch(:racc_end_parse) {
- until i = action_pointer[@racc_state[-1]]
- while act = _racc_evalact(action_default[@racc_state[-1]], arg)
- ;
- end
- end
- recv.__send__(mid) do |tok, val|
- unless tok
- @racc_t = 0
- else
- @racc_t = (token_table[tok] or 1) # error token
- end
- @racc_val = val
- @racc_read_next = false
-
- i += @racc_t
- unless i >= 0 and
- act = action_table[i] and
- action_check[i] == @racc_state[-1]
- act = action_default[@racc_state[-1]]
- end
- while act = _racc_evalact(act, arg)
- ;
- end
-
- while !(i = action_pointer[@racc_state[-1]]) ||
- ! @racc_read_next ||
- @racc_t == 0 # $
- unless i and i += @racc_t and
- i >= 0 and
- act = action_table[i] and
- action_check[i] == @racc_state[-1]
- act = action_default[@racc_state[-1]]
- end
- while act = _racc_evalact(act, arg)
- ;
- end
- end
- end
- }
- end
-
- ###
- ### common
- ###
-
- def _racc_evalact(act, arg)
- action_table, action_check, _, action_pointer,
- _, _, _, _,
- _, _, _, shift_n,
- reduce_n, * = arg
- nerr = 0 # tmp
-
- if act > 0 and act < shift_n
- #
- # shift
- #
- if @racc_error_status > 0
- @racc_error_status -= 1 unless @racc_t <= 1 # error token or EOF
- end
- @racc_vstack.push @racc_val
- @racc_state.push act
- @racc_read_next = true
- if @yydebug
- @racc_tstack.push @racc_t
- racc_shift @racc_t, @racc_tstack, @racc_vstack
- end
-
- elsif act < 0 and act > -reduce_n
- #
- # reduce
- #
- code = catch(:racc_jump) {
- @racc_state.push _racc_do_reduce(arg, act)
- false
- }
- if code
- case code
- when 1 # yyerror
- @racc_user_yyerror = true # user_yyerror
- return -reduce_n
- when 2 # yyaccept
- return shift_n
- else
- raise '[Racc Bug] unknown jump code'
- end
- end
-
- elsif act == shift_n
- #
- # accept
- #
- racc_accept if @yydebug
- throw :racc_end_parse, @racc_vstack[0]
-
- elsif act == -reduce_n
- #
- # error
- #
- case @racc_error_status
- when 0
- unless arg[21] # user_yyerror
- nerr += 1
- on_error @racc_t, @racc_val, @racc_vstack
- end
- when 3
- if @racc_t == 0 # is $
- # We're at EOF, and another error occurred immediately after
- # attempting auto-recovery
- throw :racc_end_parse, nil
- end
- @racc_read_next = true
- end
- @racc_user_yyerror = false
- @racc_error_status = 3
- while true
- if i = action_pointer[@racc_state[-1]]
- i += 1 # error token
- if i >= 0 and
- (act = action_table[i]) and
- action_check[i] == @racc_state[-1]
- break
- end
- end
- throw :racc_end_parse, nil if @racc_state.size <= 1
- @racc_state.pop
- @racc_vstack.pop
- if @yydebug
- @racc_tstack.pop
- racc_e_pop @racc_state, @racc_tstack, @racc_vstack
- end
- end
- return act
-
- else
- raise "[Racc Bug] unknown action #{act.inspect}"
- end
-
- racc_next_state(@racc_state[-1], @racc_state) if @yydebug
-
- nil
- end
-
- def _racc_do_reduce(arg, act)
- _, _, _, _,
- goto_table, goto_check, goto_default, goto_pointer,
- nt_base, reduce_table, _, _,
- _, use_result, * = arg
-
- state = @racc_state
- vstack = @racc_vstack
- tstack = @racc_tstack
-
- i = act * -3
- len = reduce_table[i]
- reduce_to = reduce_table[i+1]
- method_id = reduce_table[i+2]
- void_array = []
-
- tmp_t = tstack[-len, len] if @yydebug
- tmp_v = vstack[-len, len]
- tstack[-len, len] = void_array if @yydebug
- vstack[-len, len] = void_array
- state[-len, len] = void_array
-
- # tstack must be updated AFTER method call
- if use_result
- vstack.push __send__(method_id, tmp_v, vstack, tmp_v[0])
- else
- vstack.push __send__(method_id, tmp_v, vstack)
- end
- tstack.push reduce_to
-
- racc_reduce(tmp_t, reduce_to, tstack, vstack) if @yydebug
-
- k1 = reduce_to - nt_base
- if i = goto_pointer[k1]
- i += state[-1]
- if i >= 0 and (curstate = goto_table[i]) and goto_check[i] == k1
- return curstate
- end
- end
- goto_default[k1]
- end
-
- # This method is called when a parse error is found.
- #
- # ERROR_TOKEN_ID is an internal ID of token which caused error.
- # You can get string representation of this ID by calling
- # #token_to_str.
- #
- # ERROR_VALUE is a value of error token.
- #
- # value_stack is a stack of symbol values.
- # DO NOT MODIFY this object.
- #
- # This method raises ParseError by default.
- #
- # If this method returns, parsers enter "error recovering mode".
- def on_error(t, val, vstack)
- raise ParseError, sprintf("\nparse error on value %s (%s)",
- val.inspect, token_to_str(t) || '?')
- end
-
- # Enter error recovering mode.
- # This method does not call #on_error.
- def yyerror
- throw :racc_jump, 1
- end
-
- # Exit parser.
- # Return value is +Symbol_Value_Stack[0]+.
- def yyaccept
- throw :racc_jump, 2
- end
-
- # Leave error recovering mode.
- def yyerrok
- @racc_error_status = 0
- end
-
- # For debugging output
- def racc_read_token(t, tok, val)
- @racc_debug_out.print 'read '
- @racc_debug_out.print tok.inspect, '(', racc_token2str(t), ') '
- @racc_debug_out.puts val.inspect
- @racc_debug_out.puts
- end
-
- def racc_shift(tok, tstack, vstack)
- @racc_debug_out.puts "shift #{racc_token2str tok}"
- racc_print_stacks tstack, vstack
- @racc_debug_out.puts
- end
-
- def racc_reduce(toks, sim, tstack, vstack)
- out = @racc_debug_out
- out.print 'reduce '
- if toks.empty?
- out.print ' <none>'
- else
- toks.each {|t| out.print ' ', racc_token2str(t) }
- end
- out.puts " --> #{racc_token2str(sim)}"
- racc_print_stacks tstack, vstack
- @racc_debug_out.puts
- end
-
- def racc_accept
- @racc_debug_out.puts 'accept'
- @racc_debug_out.puts
- end
-
- def racc_e_pop(state, tstack, vstack)
- @racc_debug_out.puts 'error recovering mode: pop token'
- racc_print_states state
- racc_print_stacks tstack, vstack
- @racc_debug_out.puts
- end
-
- def racc_next_state(curstate, state)
- @racc_debug_out.puts "goto #{curstate}"
- racc_print_states state
- @racc_debug_out.puts
- end
-
- def racc_print_stacks(t, v)
- out = @racc_debug_out
- out.print ' ['
- t.each_index do |i|
- out.print ' (', racc_token2str(t[i]), ' ', v[i].inspect, ')'
- end
- out.puts ' ]'
- end
-
- def racc_print_states(s)
- out = @racc_debug_out
- out.print ' ['
- s.each {|st| out.print ' ', st }
- out.puts ' ]'
- end
-
- def racc_token2str(tok)
- self.class::Racc_token_to_s_table[tok] or
- raise "[Racc Bug] can't convert token #{tok} to string"
- end
-
- # Convert internal ID of token symbol to the string.
- def token_to_str(t)
- self.class::Racc_token_to_s_table[t]
- end
-
- end
-
-end
-
-__end_of_file__
-end
diff --git a/lib/racc/parser.rb b/lib/racc/parser.rb
deleted file mode 100644
index f2cc050826..0000000000
--- a/lib/racc/parser.rb
+++ /dev/null
@@ -1,632 +0,0 @@
-# frozen_string_literal: false
-#--
-# Copyright (c) 1999-2006 Minero Aoki
-#
-# This program is free software.
-# You can distribute/modify this program under the same terms of ruby.
-#
-# As a special exception, when this code is copied by Racc
-# into a Racc output file, you may use that output file
-# without restriction.
-#++
-
-require 'racc/info'
-
-unless defined?(NotImplementedError)
- NotImplementedError = NotImplementError # :nodoc:
-end
-
-module Racc
- class ParseError < StandardError; end
-end
-unless defined?(::ParseError)
- ParseError = Racc::ParseError # :nodoc:
-end
-
-# Racc is a LALR(1) parser generator.
-# It is written in Ruby itself, and generates Ruby programs.
-#
-# == Command-line Reference
-#
-# racc [-o<var>filename</var>] [--output-file=<var>filename</var>]
-# [-e<var>rubypath</var>] [--executable=<var>rubypath</var>]
-# [-v] [--verbose]
-# [-O<var>filename</var>] [--log-file=<var>filename</var>]
-# [-g] [--debug]
-# [-E] [--embedded]
-# [-l] [--no-line-convert]
-# [-c] [--line-convert-all]
-# [-a] [--no-omit-actions]
-# [-C] [--check-only]
-# [-S] [--output-status]
-# [--version] [--copyright] [--help] <var>grammarfile</var>
-#
-# [+grammarfile+]
-# Racc grammar file. Any extension is permitted.
-# [-o+outfile+, --output-file=+outfile+]
-# A filename for output. default is <+filename+>.tab.rb
-# [-O+filename+, --log-file=+filename+]
-# Place logging output in file +filename+.
-# Default log file name is <+filename+>.output.
-# [-e+rubypath+, --executable=+rubypath+]
-# output executable file(mode 755). where +path+ is the Ruby interpreter.
-# [-v, --verbose]
-# verbose mode. create +filename+.output file, like yacc's y.output file.
-# [-g, --debug]
-# add debug code to parser class. To display debuggin information,
-# use this '-g' option and set @yydebug true in parser class.
-# [-E, --embedded]
-# Output parser which doesn't need runtime files (racc/parser.rb).
-# [-C, --check-only]
-# Check syntax of racc grammar file and quit.
-# [-S, --output-status]
-# Print messages time to time while compiling.
-# [-l, --no-line-convert]
-# turns off line number converting.
-# [-c, --line-convert-all]
-# Convert line number of actions, inner, header and footer.
-# [-a, --no-omit-actions]
-# Call all actions, even if an action is empty.
-# [--version]
-# print Racc version and quit.
-# [--copyright]
-# Print copyright and quit.
-# [--help]
-# Print usage and quit.
-#
-# == Generating Parser Using Racc
-#
-# To compile Racc grammar file, simply type:
-#
-# $ racc parse.y
-#
-# This creates Ruby script file "parse.tab.y". The -o option can change the output filename.
-#
-# == Writing A Racc Grammar File
-#
-# If you want your own parser, you have to write a grammar file.
-# A grammar file contains the name of your parser class, grammar for the parser,
-# user code, and anything else.
-# When writing a grammar file, yacc's knowledge is helpful.
-# If you have not used yacc before, Racc is not too difficult.
-#
-# Here's an example Racc grammar file.
-#
-# class Calcparser
-# rule
-# target: exp { print val[0] }
-#
-# exp: exp '+' exp
-# | exp '*' exp
-# | '(' exp ')'
-# | NUMBER
-# end
-#
-# Racc grammar files resemble yacc files.
-# But (of course), this is Ruby code.
-# yacc's $$ is the 'result', $0, $1... is
-# an array called 'val', and $-1, $-2... is an array called '_values'.
-#
-# See the {Grammar File Reference}[rdoc-ref:lib/racc/rdoc/grammar.en.rdoc] for
-# more information on grammar files.
-#
-# == Parser
-#
-# Then you must prepare the parse entry method. There are two types of
-# parse methods in Racc, Racc::Parser#do_parse and Racc::Parser#yyparse
-#
-# Racc::Parser#do_parse is simple.
-#
-# It's yyparse() of yacc, and Racc::Parser#next_token is yylex().
-# This method must returns an array like [TOKENSYMBOL, ITS_VALUE].
-# EOF is [false, false].
-# (TOKENSYMBOL is a Ruby symbol (taken from String#intern) by default.
-# If you want to change this, see the grammar reference.
-#
-# Racc::Parser#yyparse is little complicated, but useful.
-# It does not use Racc::Parser#next_token, instead it gets tokens from any iterator.
-#
-# For example, <code>yyparse(obj, :scan)</code> causes
-# calling +obj#scan+, and you can return tokens by yielding them from +obj#scan+.
-#
-# == Debugging
-#
-# When debugging, "-v" or/and the "-g" option is helpful.
-#
-# "-v" creates verbose log file (.output).
-# "-g" creates a "Verbose Parser".
-# Verbose Parser prints the internal status when parsing.
-# But it's _not_ automatic.
-# You must use -g option and set +@yydebug+ to +true+ in order to get output.
-# -g option only creates the verbose parser.
-#
-# === Racc reported syntax error.
-#
-# Isn't there too many "end"?
-# grammar of racc file is changed in v0.10.
-#
-# Racc does not use '%' mark, while yacc uses huge number of '%' marks..
-#
-# === Racc reported "XXXX conflicts".
-#
-# Try "racc -v xxxx.y".
-# It causes producing racc's internal log file, xxxx.output.
-#
-# === Generated parsers does not work correctly
-#
-# Try "racc -g xxxx.y".
-# This command let racc generate "debugging parser".
-# Then set @yydebug=true in your parser.
-# It produces a working log of your parser.
-#
-# == Re-distributing Racc runtime
-#
-# A parser, which is created by Racc, requires the Racc runtime module;
-# racc/parser.rb.
-#
-# Ruby 1.8.x comes with Racc runtime module,
-# you need NOT distribute Racc runtime files.
-#
-# If you want to include the Racc runtime module with your parser.
-# This can be done by using '-E' option:
-#
-# $ racc -E -omyparser.rb myparser.y
-#
-# This command creates myparser.rb which `includes' Racc runtime.
-# Only you must do is to distribute your parser file (myparser.rb).
-#
-# Note: parser.rb is ruby license, but your parser is not.
-# Your own parser is completely yours.
-module Racc
-
- unless defined?(Racc_No_Extensions)
- Racc_No_Extensions = false # :nodoc:
- end
-
- class Parser
-
- Racc_Runtime_Version = ::Racc::VERSION
- Racc_Runtime_Core_Version_R = ::Racc::VERSION
-
- begin
- if Object.const_defined?(:RUBY_ENGINE) and RUBY_ENGINE == 'jruby'
- require 'jruby'
- require 'racc/cparse-jruby.jar'
- com.headius.racc.Cparse.new.load(JRuby.runtime, false)
- else
- require 'racc/cparse'
- end
-
- unless new.respond_to?(:_racc_do_parse_c, true)
- raise LoadError, 'old cparse.so'
- end
- if Racc_No_Extensions
- raise LoadError, 'selecting ruby version of racc runtime core'
- end
-
- Racc_Main_Parsing_Routine = :_racc_do_parse_c # :nodoc:
- Racc_YY_Parse_Method = :_racc_yyparse_c # :nodoc:
- Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_C # :nodoc:
- Racc_Runtime_Type = 'c' # :nodoc:
- rescue LoadError
- Racc_Main_Parsing_Routine = :_racc_do_parse_rb
- Racc_YY_Parse_Method = :_racc_yyparse_rb
- Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_R
- Racc_Runtime_Type = 'ruby'
- end
-
- def Parser.racc_runtime_type # :nodoc:
- Racc_Runtime_Type
- end
-
- def _racc_setup
- @yydebug = false unless self.class::Racc_debug_parser
- @yydebug = false unless defined?(@yydebug)
- if @yydebug
- @racc_debug_out = $stderr unless defined?(@racc_debug_out)
- @racc_debug_out ||= $stderr
- end
- arg = self.class::Racc_arg
- arg[13] = true if arg.size < 14
- arg
- end
-
- def _racc_init_sysvars
- @racc_state = [0]
- @racc_tstack = []
- @racc_vstack = []
-
- @racc_t = nil
- @racc_val = nil
-
- @racc_read_next = true
-
- @racc_user_yyerror = false
- @racc_error_status = 0
- end
-
- # The entry point of the parser. This method is used with #next_token.
- # If Racc wants to get token (and its value), calls next_token.
- #
- # Example:
- # def parse
- # @q = [[1,1],
- # [2,2],
- # [3,3],
- # [false, '$']]
- # do_parse
- # end
- #
- # def next_token
- # @q.shift
- # end
- class_eval <<~RUBY, __FILE__, __LINE__ + 1
- def do_parse
- #{Racc_Main_Parsing_Routine}(_racc_setup(), false)
- end
- RUBY
-
- # The method to fetch next token.
- # If you use #do_parse method, you must implement #next_token.
- #
- # The format of return value is [TOKEN_SYMBOL, VALUE].
- # +token-symbol+ is represented by Ruby's symbol by default, e.g. :IDENT
- # for 'IDENT'. ";" (String) for ';'.
- #
- # The final symbol (End of file) must be false.
- def next_token
- raise NotImplementedError, "#{self.class}\#next_token is not defined"
- end
-
- def _racc_do_parse_rb(arg, in_debug)
- action_table, action_check, action_default, action_pointer,
- _, _, _, _,
- _, _, token_table, * = arg
-
- _racc_init_sysvars
- tok = act = i = nil
-
- catch(:racc_end_parse) {
- while true
- if i = action_pointer[@racc_state[-1]]
- if @racc_read_next
- if @racc_t != 0 # not EOF
- tok, @racc_val = next_token()
- unless tok # EOF
- @racc_t = 0
- else
- @racc_t = (token_table[tok] or 1) # error token
- end
- racc_read_token(@racc_t, tok, @racc_val) if @yydebug
- @racc_read_next = false
- end
- end
- i += @racc_t
- unless i >= 0 and
- act = action_table[i] and
- action_check[i] == @racc_state[-1]
- act = action_default[@racc_state[-1]]
- end
- else
- act = action_default[@racc_state[-1]]
- end
- while act = _racc_evalact(act, arg)
- ;
- end
- end
- }
- end
-
- # Another entry point for the parser.
- # If you use this method, you must implement RECEIVER#METHOD_ID method.
- #
- # RECEIVER#METHOD_ID is a method to get next token.
- # It must 'yield' the token, which format is [TOKEN-SYMBOL, VALUE].
- class_eval <<~RUBY, __FILE__, __LINE__ + 1
- def yyparse(recv, mid)
- #{Racc_YY_Parse_Method}(recv, mid, _racc_setup(), false)
- end
- RUBY
-
- def _racc_yyparse_rb(recv, mid, arg, c_debug)
- action_table, action_check, action_default, action_pointer,
- _, _, _, _,
- _, _, token_table, * = arg
-
- _racc_init_sysvars
-
- catch(:racc_end_parse) {
- until i = action_pointer[@racc_state[-1]]
- while act = _racc_evalact(action_default[@racc_state[-1]], arg)
- ;
- end
- end
- recv.__send__(mid) do |tok, val|
- unless tok
- @racc_t = 0
- else
- @racc_t = (token_table[tok] or 1) # error token
- end
- @racc_val = val
- @racc_read_next = false
-
- i += @racc_t
- unless i >= 0 and
- act = action_table[i] and
- action_check[i] == @racc_state[-1]
- act = action_default[@racc_state[-1]]
- end
- while act = _racc_evalact(act, arg)
- ;
- end
-
- while !(i = action_pointer[@racc_state[-1]]) ||
- ! @racc_read_next ||
- @racc_t == 0 # $
- unless i and i += @racc_t and
- i >= 0 and
- act = action_table[i] and
- action_check[i] == @racc_state[-1]
- act = action_default[@racc_state[-1]]
- end
- while act = _racc_evalact(act, arg)
- ;
- end
- end
- end
- }
- end
-
- ###
- ### common
- ###
-
- def _racc_evalact(act, arg)
- action_table, action_check, _, action_pointer,
- _, _, _, _,
- _, _, _, shift_n,
- reduce_n, * = arg
- nerr = 0 # tmp
-
- if act > 0 and act < shift_n
- #
- # shift
- #
- if @racc_error_status > 0
- @racc_error_status -= 1 unless @racc_t <= 1 # error token or EOF
- end
- @racc_vstack.push @racc_val
- @racc_state.push act
- @racc_read_next = true
- if @yydebug
- @racc_tstack.push @racc_t
- racc_shift @racc_t, @racc_tstack, @racc_vstack
- end
-
- elsif act < 0 and act > -reduce_n
- #
- # reduce
- #
- code = catch(:racc_jump) {
- @racc_state.push _racc_do_reduce(arg, act)
- false
- }
- if code
- case code
- when 1 # yyerror
- @racc_user_yyerror = true # user_yyerror
- return -reduce_n
- when 2 # yyaccept
- return shift_n
- else
- raise '[Racc Bug] unknown jump code'
- end
- end
-
- elsif act == shift_n
- #
- # accept
- #
- racc_accept if @yydebug
- throw :racc_end_parse, @racc_vstack[0]
-
- elsif act == -reduce_n
- #
- # error
- #
- case @racc_error_status
- when 0
- unless arg[21] # user_yyerror
- nerr += 1
- on_error @racc_t, @racc_val, @racc_vstack
- end
- when 3
- if @racc_t == 0 # is $
- # We're at EOF, and another error occurred immediately after
- # attempting auto-recovery
- throw :racc_end_parse, nil
- end
- @racc_read_next = true
- end
- @racc_user_yyerror = false
- @racc_error_status = 3
- while true
- if i = action_pointer[@racc_state[-1]]
- i += 1 # error token
- if i >= 0 and
- (act = action_table[i]) and
- action_check[i] == @racc_state[-1]
- break
- end
- end
- throw :racc_end_parse, nil if @racc_state.size <= 1
- @racc_state.pop
- @racc_vstack.pop
- if @yydebug
- @racc_tstack.pop
- racc_e_pop @racc_state, @racc_tstack, @racc_vstack
- end
- end
- return act
-
- else
- raise "[Racc Bug] unknown action #{act.inspect}"
- end
-
- racc_next_state(@racc_state[-1], @racc_state) if @yydebug
-
- nil
- end
-
- def _racc_do_reduce(arg, act)
- _, _, _, _,
- goto_table, goto_check, goto_default, goto_pointer,
- nt_base, reduce_table, _, _,
- _, use_result, * = arg
-
- state = @racc_state
- vstack = @racc_vstack
- tstack = @racc_tstack
-
- i = act * -3
- len = reduce_table[i]
- reduce_to = reduce_table[i+1]
- method_id = reduce_table[i+2]
- void_array = []
-
- tmp_t = tstack[-len, len] if @yydebug
- tmp_v = vstack[-len, len]
- tstack[-len, len] = void_array if @yydebug
- vstack[-len, len] = void_array
- state[-len, len] = void_array
-
- # tstack must be updated AFTER method call
- if use_result
- vstack.push __send__(method_id, tmp_v, vstack, tmp_v[0])
- else
- vstack.push __send__(method_id, tmp_v, vstack)
- end
- tstack.push reduce_to
-
- racc_reduce(tmp_t, reduce_to, tstack, vstack) if @yydebug
-
- k1 = reduce_to - nt_base
- if i = goto_pointer[k1]
- i += state[-1]
- if i >= 0 and (curstate = goto_table[i]) and goto_check[i] == k1
- return curstate
- end
- end
- goto_default[k1]
- end
-
- # This method is called when a parse error is found.
- #
- # ERROR_TOKEN_ID is an internal ID of token which caused error.
- # You can get string representation of this ID by calling
- # #token_to_str.
- #
- # ERROR_VALUE is a value of error token.
- #
- # value_stack is a stack of symbol values.
- # DO NOT MODIFY this object.
- #
- # This method raises ParseError by default.
- #
- # If this method returns, parsers enter "error recovering mode".
- def on_error(t, val, vstack)
- raise ParseError, sprintf("\nparse error on value %s (%s)",
- val.inspect, token_to_str(t) || '?')
- end
-
- # Enter error recovering mode.
- # This method does not call #on_error.
- def yyerror
- throw :racc_jump, 1
- end
-
- # Exit parser.
- # Return value is +Symbol_Value_Stack[0]+.
- def yyaccept
- throw :racc_jump, 2
- end
-
- # Leave error recovering mode.
- def yyerrok
- @racc_error_status = 0
- end
-
- # For debugging output
- def racc_read_token(t, tok, val)
- @racc_debug_out.print 'read '
- @racc_debug_out.print tok.inspect, '(', racc_token2str(t), ') '
- @racc_debug_out.puts val.inspect
- @racc_debug_out.puts
- end
-
- def racc_shift(tok, tstack, vstack)
- @racc_debug_out.puts "shift #{racc_token2str tok}"
- racc_print_stacks tstack, vstack
- @racc_debug_out.puts
- end
-
- def racc_reduce(toks, sim, tstack, vstack)
- out = @racc_debug_out
- out.print 'reduce '
- if toks.empty?
- out.print ' <none>'
- else
- toks.each {|t| out.print ' ', racc_token2str(t) }
- end
- out.puts " --> #{racc_token2str(sim)}"
- racc_print_stacks tstack, vstack
- @racc_debug_out.puts
- end
-
- def racc_accept
- @racc_debug_out.puts 'accept'
- @racc_debug_out.puts
- end
-
- def racc_e_pop(state, tstack, vstack)
- @racc_debug_out.puts 'error recovering mode: pop token'
- racc_print_states state
- racc_print_stacks tstack, vstack
- @racc_debug_out.puts
- end
-
- def racc_next_state(curstate, state)
- @racc_debug_out.puts "goto #{curstate}"
- racc_print_states state
- @racc_debug_out.puts
- end
-
- def racc_print_stacks(t, v)
- out = @racc_debug_out
- out.print ' ['
- t.each_index do |i|
- out.print ' (', racc_token2str(t[i]), ' ', v[i].inspect, ')'
- end
- out.puts ' ]'
- end
-
- def racc_print_states(s)
- out = @racc_debug_out
- out.print ' ['
- s.each {|st| out.print ' ', st }
- out.puts ' ]'
- end
-
- def racc_token2str(tok)
- self.class::Racc_token_to_s_table[tok] or
- raise "[Racc Bug] can't convert token #{tok} to string"
- end
-
- # Convert internal ID of token symbol to the string.
- def token_to_str(t)
- self.class::Racc_token_to_s_table[t]
- end
-
- end
-
-end
diff --git a/lib/racc/parserfilegenerator.rb b/lib/racc/parserfilegenerator.rb
deleted file mode 100644
index 7846b584fa..0000000000
--- a/lib/racc/parserfilegenerator.rb
+++ /dev/null
@@ -1,470 +0,0 @@
-#--
-#
-#
-#
-# Copyright (c) 1999-2006 Minero Aoki
-#
-# This program is free software.
-# You can distribute/modify this program under the same terms of ruby.
-# see the file "COPYING".
-#
-#++
-
-require 'racc/compat'
-require 'racc/sourcetext'
-require 'racc/parser-text'
-require 'rbconfig'
-
-module Racc
-
- class ParserFileGenerator
-
- class Params
- def self.bool_attr(name)
- module_eval(<<-End)
- def #{name}?
- @#{name}
- end
-
- def #{name}=(b)
- @#{name} = b
- end
- End
- end
-
- attr_accessor :filename
- attr_accessor :classname
- attr_accessor :superclass
- bool_attr :omit_action_call
- bool_attr :result_var
- attr_accessor :header
- attr_accessor :inner
- attr_accessor :footer
-
- bool_attr :debug_parser
- bool_attr :convert_line
- bool_attr :convert_line_all
- bool_attr :embed_runtime
- bool_attr :make_executable
- attr_accessor :interpreter
-
- def initialize
- # Parameters derived from parser
- self.filename = nil
- self.classname = nil
- self.superclass = 'Racc::Parser'
- self.omit_action_call = true
- self.result_var = true
- self.header = []
- self.inner = []
- self.footer = []
-
- # Parameters derived from command line options
- self.debug_parser = false
- self.convert_line = true
- self.convert_line_all = false
- self.embed_runtime = false
- self.make_executable = false
- self.interpreter = nil
- end
- end
-
- def initialize(states, params)
- @states = states
- @grammar = states.grammar
- @params = params
- end
-
- def generate_parser
- string_io = StringIO.new
-
- init_line_conversion_system
- @f = string_io
- parser_file
-
- string_io.rewind
- string_io.read
- end
-
- def generate_parser_file(destpath)
- init_line_conversion_system
- File.open(destpath, 'w') {|f|
- @f = f
- parser_file
- }
- File.chmod 0755, destpath if @params.make_executable?
- end
-
- private
-
- def parser_file
- shebang @params.interpreter if @params.make_executable?
- notice
- line
- if @params.embed_runtime?
- embed_library runtime_source()
- else
- require 'racc/parser.rb'
- end
- header
- parser_class(@params.classname, @params.superclass) {
- inner
- state_transition_table
- }
- footer
- end
-
- c = ::RbConfig::CONFIG
- RUBY_PATH = "#{c['bindir']}/#{c['ruby_install_name']}#{c['EXEEXT']}"
-
- def shebang(path)
- line '#!' + (path == 'ruby' ? RUBY_PATH : path)
- end
-
- def notice
- line %q[#]
- line %q[# DO NOT MODIFY!!!!]
- line %Q[# This file is automatically generated by Racc #{Racc::Version}]
- line %Q[# from Racc grammar file "#{@params.filename}".]
- line %q[#]
- end
-
- def runtime_source
- SourceText.new(::Racc::PARSER_TEXT, 'racc/parser.rb', 1)
- end
-
- def embed_library(src)
- line %[###### #{src.filename} begin]
- line %[unless $".index '#{src.filename}']
- line %[$".push '#{src.filename}']
- put src, @params.convert_line?
- line %[end]
- line %[###### #{src.filename} end]
- end
-
- def require(feature)
- line "require '#{feature}'"
- end
-
- def parser_class(classname, superclass)
- mods = classname.split('::')
- classid = mods.pop
- mods.each do |mod|
- indent; line "module #{mod}"
- cref_push mod
- end
- indent; line "class #{classid} < #{superclass}"
- cref_push classid
- yield
- cref_pop
- indent; line "end \# class #{classid}"
- mods.reverse_each do |mod|
- cref_pop
- indent; line "end \# module #{mod}"
- end
- end
-
- def header
- @params.header.each do |src|
- line
- put src, @params.convert_line_all?
- end
- end
-
- def inner
- @params.inner.each do |src|
- line
- put src, @params.convert_line?
- end
- end
-
- def footer
- @params.footer.each do |src|
- line
- put src, @params.convert_line_all?
- end
- end
-
- # Low Level Routines
-
- def put(src, convert_line = false)
- if convert_line
- replace_location(src) {
- @f.puts src.text
- }
- else
- @f.puts src.text
- end
- end
-
- def line(str = '')
- @f.puts str
- end
-
- def init_line_conversion_system
- @cref = []
- @used_separator = {}
- end
-
- def cref_push(name)
- @cref.push name
- end
-
- def cref_pop
- @cref.pop
- end
-
- def indent
- @f.print ' ' * @cref.size
- end
-
- def toplevel?
- @cref.empty?
- end
-
- def replace_location(src)
- sep = make_separator(src)
- @f.print 'self.class.' if toplevel?
- @f.puts "module_eval(<<'#{sep}', '#{src.filename}', #{src.lineno})"
- yield
- @f.puts sep
- end
-
- def make_separator(src)
- sep = unique_separator(src.filename)
- sep *= 2 while src.text.index(sep)
- sep
- end
-
- def unique_separator(id)
- sep = String.new "...end #{id}/module_eval..."
- while @used_separator.key?(sep)
- sep.concat sprintf('%02x', rand(255))
- end
- @used_separator[sep] = true
- sep
- end
-
- #
- # State Transition Table Serialization
- #
-
- public
-
- def put_state_transition_table(f)
- @f = f
- state_transition_table
- end
-
- private
-
- def state_transition_table
- table = @states.state_transition_table
- table.use_result_var = @params.result_var?
- table.debug_parser = @params.debug_parser?
-
- line "##### State transition tables begin ###"
- line
- integer_list 'racc_action_table', table.action_table
- line
- integer_list 'racc_action_check', table.action_check
- line
- integer_list 'racc_action_pointer', table.action_pointer
- line
- integer_list 'racc_action_default', table.action_default
- line
- integer_list 'racc_goto_table', table.goto_table
- line
- integer_list 'racc_goto_check', table.goto_check
- line
- integer_list 'racc_goto_pointer', table.goto_pointer
- line
- integer_list 'racc_goto_default', table.goto_default
- line
- i_i_sym_list 'racc_reduce_table', table.reduce_table
- line
- line "racc_reduce_n = #{table.reduce_n}"
- line
- line "racc_shift_n = #{table.shift_n}"
- line
- sym_int_hash 'racc_token_table', table.token_table
- line
- line "racc_nt_base = #{table.nt_base}"
- line
- line "racc_use_result_var = #{table.use_result_var}"
- line
- @f.print(unindent_auto(<<-End))
- Racc_arg = [
- racc_action_table,
- racc_action_check,
- racc_action_default,
- racc_action_pointer,
- racc_goto_table,
- racc_goto_check,
- racc_goto_default,
- racc_goto_pointer,
- racc_nt_base,
- racc_reduce_table,
- racc_token_table,
- racc_shift_n,
- racc_reduce_n,
- racc_use_result_var ]
- End
- line "Ractor.make_shareable(Racc_arg) if defined?(Ractor)"
- line
- string_list 'Racc_token_to_s_table', table.token_to_s_table
- line "Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)"
- line
- line "Racc_debug_parser = #{table.debug_parser}"
- line
- line '##### State transition tables end #####'
- actions
- end
-
- def integer_list(name, table)
- sep = ''
- line "#{name} = ["
- table.each_slice(10) do |ns|
- @f.print sep; sep = ",\n"
- @f.print ns.map {|n| sprintf('%6s', n ? n.to_s : 'nil') }.join(',')
- end
- line ' ]'
- end
-
- def i_i_sym_list(name, table)
- sep = ''
- line "#{name} = ["
- table.each_slice(3) do |len, target, mid|
- @f.print sep; sep = ",\n"
- @f.printf ' %d, %d, %s', len, target, mid.inspect
- end
- line " ]"
- end
-
- def sym_int_hash(name, h)
- sep = "\n"
- @f.print "#{name} = {"
- h.to_a.sort_by {|sym, i| i }.each do |sym, i|
- @f.print sep; sep = ",\n"
- @f.printf " %s => %d", sym.serialize, i
- end
- line " }"
- end
-
- def string_list(name, list)
- sep = " "
- line "#{name} = ["
- list.each do |s|
- @f.print sep; sep = ",\n "
- @f.print s.dump
- end
- line ' ]'
- end
-
- def actions
- @grammar.each do |rule|
- unless rule.action.source?
- raise "racc: fatal: cannot generate parser file when any action is a Proc"
- end
- end
-
- if @params.result_var?
- decl = ', result'
- retval = "\n result"
- default_body = ''
- else
- decl = ''
- retval = ''
- default_body = 'val[0]'
- end
- @grammar.each do |rule|
- line
- if rule.action.empty? and @params.omit_action_call?
- line "# reduce #{rule.ident} omitted"
- else
- src0 = rule.action.source || SourceText.new(default_body, __FILE__, 0)
- if @params.convert_line?
- src = remove_blank_lines(src0)
- delim = make_delimiter(src.text)
- @f.printf unindent_auto(<<-End),
- module_eval(<<'%s', '%s', %d)
- def _reduce_%d(val, _values%s)
- %s%s
- end
- %s
- End
- delim, src.filename, src.lineno - 1,
- rule.ident, decl,
- src.text, retval,
- delim
- else
- src = remove_blank_lines(src0)
- @f.printf unindent_auto(<<-End),
- def _reduce_%d(val, _values%s)
- %s%s
- end
- End
- rule.ident, decl,
- src.text, retval
- end
- end
- end
- line
- @f.printf unindent_auto(<<-'End'), decl
- def _reduce_none(val, _values%s)
- val[0]
- end
- End
- line
- end
-
- def remove_blank_lines(src)
- body = src.text.dup
- line = src.lineno
- while body.slice!(/\A[ \t\f]*(?:\n|\r\n|\r)/)
- line += 1
- end
- SourceText.new(body, src.filename, line)
- end
-
- def make_delimiter(body)
- delim = '.,.,'
- while body.index(delim)
- delim *= 2
- end
- delim
- end
-
- def unindent_auto(str)
- lines = str.lines.to_a
- n = minimum_indent(lines)
- lines.map {|line| detab(line).sub(indent_re(n), '').rstrip + "\n" }.join('')
- end
-
- def minimum_indent(lines)
- lines.map {|line| n_indent(line) }.min
- end
-
- def n_indent(line)
- line.slice(/\A\s+/).size
- end
-
- RE_CACHE = {}
-
- def indent_re(n)
- RE_CACHE[n] ||= /\A {#{n}}/
- end
-
- def detab(str, ts = 8)
- add = 0
- len = nil
- str.gsub(/\t/) {
- len = ts - ($`.size + add) % ts
- add += len - 1
- ' ' * len
- }
- end
-
- end
-
-end
diff --git a/lib/racc/racc.gemspec b/lib/racc/racc.gemspec
deleted file mode 100644
index 1095c8f47e..0000000000
--- a/lib/racc/racc.gemspec
+++ /dev/null
@@ -1,58 +0,0 @@
-# -*- encoding: utf-8 -*-
-
-begin
- require_relative "lib/racc/info"
-rescue LoadError # Fallback to load version file in ruby core repository
- require_relative "info"
-end
-
-Gem::Specification.new do |s|
- s.name = "racc"
- s.version = Racc::VERSION
- s.summary = "Racc is a LALR(1) parser generator"
- s.description = <<DESC
-Racc is a LALR(1) parser generator.
- It is written in Ruby itself, and generates Ruby program.
-
- NOTE: Ruby 1.8.x comes with Racc runtime module. You
- can run your parsers generated by racc 1.4.x out of the
- box.
-DESC
- s.authors = ["Minero Aoki", "Aaron Patterson"]
- s.email = [nil, "aaron@tenderlovemaking.com"]
- s.homepage = "https://github.com/ruby/racc"
- s.licenses = ["Ruby", "BSD-2-Clause"]
- s.executables = ["racc"]
- s.files = [
- "COPYING", "ChangeLog", "TODO",
- "README.ja.rdoc", "README.rdoc", "bin/racc",
- "ext/racc/MANIFEST",
- "ext/racc/cparse/cparse.c",
- "ext/racc/cparse/extconf.rb",
- "lib/racc.rb", "lib/racc/compat.rb",
- "lib/racc/debugflags.rb", "lib/racc/exception.rb",
- "lib/racc/grammar.rb", "lib/racc/grammarfileparser.rb",
- "lib/racc/info.rb", "lib/racc/iset.rb",
- "lib/racc/logfilegenerator.rb", "lib/racc/parser-text.rb",
- "lib/racc/parser.rb", "lib/racc/parserfilegenerator.rb",
- "lib/racc/sourcetext.rb",
- "lib/racc/state.rb", "lib/racc/statetransitiontable.rb",
- "lib/racc/static.rb",
- "doc/en/NEWS.en.rdoc", "doc/en/grammar2.en.rdoc",
- "doc/en/grammar.en.rdoc", "doc/ja/NEWS.ja.rdoc",
- "doc/ja/command.ja.html", "doc/ja/debug.ja.rdoc",
- "doc/ja/grammar.ja.rdoc", "doc/ja/index.ja.html",
- "doc/ja/parser.ja.rdoc", "doc/ja/usage.ja.html",
- ]
- s.require_paths = ["lib"]
- s.required_ruby_version = ">= 2.5"
- s.rdoc_options = ["--main", "README.rdoc"]
- s.extra_rdoc_files = ["README.ja.rdoc", "README.rdoc"]
-
- if RUBY_PLATFORM =~ /java/
- s.files << 'lib/racc/cparse-jruby.jar'
- s.platform = 'java'
- else
- s.extensions = ["ext/racc/cparse/extconf.rb"]
- end
-end
diff --git a/lib/racc/sourcetext.rb b/lib/racc/sourcetext.rb
deleted file mode 100644
index de52dcae9b..0000000000
--- a/lib/racc/sourcetext.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-#--
-#
-#
-#
-# Copyright (c) 1999-2006 Minero Aoki
-#
-# This program is free software.
-# You can distribute/modify this program under the same terms of ruby.
-# see the file "COPYING".
-#
-#++
-
-module Racc
-
- class SourceText
- def initialize(text, filename, lineno)
- @text = text
- @filename = filename
- @lineno = lineno
- end
-
- attr_reader :text
- attr_reader :filename
- attr_reader :lineno
-
- def to_s
- "#<SourceText #{location()}>"
- end
-
- def location
- "#{@filename}:#{@lineno}"
- end
- end
-
-end
diff --git a/lib/racc/state.rb b/lib/racc/state.rb
deleted file mode 100644
index f85809fbeb..0000000000
--- a/lib/racc/state.rb
+++ /dev/null
@@ -1,972 +0,0 @@
-#--
-#
-#
-#
-# Copyright (c) 1999-2006 Minero Aoki
-#
-# This program is free software.
-# You can distribute/modify this program under the same terms of ruby.
-# see the file "COPYING".
-#
-#++
-
-require 'racc/iset'
-require 'racc/statetransitiontable'
-require 'racc/exception'
-require 'forwardable'
-
-module Racc
-
- # A table of LALR states.
- class States
-
- include Enumerable
-
- def initialize(grammar, debug_flags = DebugFlags.new)
- @grammar = grammar
- @symboltable = grammar.symboltable
- @d_state = debug_flags.state
- @d_la = debug_flags.la
- @d_prec = debug_flags.prec
- @states = []
- @statecache = {}
- @actions = ActionTable.new(@grammar, self)
- @nfa_computed = false
- @dfa_computed = false
- end
-
- attr_reader :grammar
- attr_reader :actions
-
- def size
- @states.size
- end
-
- def inspect
- '#<state table>'
- end
-
- alias to_s inspect
-
- def [](i)
- @states[i]
- end
-
- def each_state(&block)
- @states.each(&block)
- end
-
- alias each each_state
-
- def each_index(&block)
- @states.each_index(&block)
- end
-
- extend Forwardable
-
- def_delegator "@actions", :shift_n
- def_delegator "@actions", :reduce_n
- def_delegator "@actions", :nt_base
-
- def should_report_srconflict?
- srconflict_exist? and
- (n_srconflicts() != @grammar.n_expected_srconflicts)
- end
-
- def srconflict_exist?
- n_srconflicts() != 0
- end
-
- def n_srconflicts
- @n_srconflicts ||= inject(0) {|sum, st| sum + st.n_srconflicts }
- end
-
- def rrconflict_exist?
- n_rrconflicts() != 0
- end
-
- def n_rrconflicts
- @n_rrconflicts ||= inject(0) {|sum, st| sum + st.n_rrconflicts }
- end
-
- def state_transition_table
- @state_transition_table ||= StateTransitionTable.generate(self.dfa)
- end
-
- #
- # NFA (Non-deterministic Finite Automaton) Computation
- #
-
- public
-
- def nfa
- return self if @nfa_computed
- compute_nfa
- @nfa_computed = true
- self
- end
-
- private
-
- def compute_nfa
- @grammar.init
- # add state 0
- core_to_state [ @grammar[0].ptrs[0] ]
- # generate LALR states
- cur = 0
- @gotos = []
- while cur < @states.size
- generate_states @states[cur] # state is added here
- cur += 1
- end
- @actions.init
- end
-
- def generate_states(state)
- puts "dstate: #{state}" if @d_state
-
- table = {}
- state.closure.each do |ptr|
- if sym = ptr.dereference
- addsym table, sym, ptr.next
- end
- end
- table.each do |sym, core|
- puts "dstate: sym=#{sym} ncore=#{core}" if @d_state
-
- dest = core_to_state(core.to_a)
- state.goto_table[sym] = dest
- id = sym.nonterminal?() ? @gotos.size : nil
- g = Goto.new(id, sym, state, dest)
- @gotos.push g if sym.nonterminal?
- state.gotos[sym] = g
- puts "dstate: #{state.ident} --#{sym}--> #{dest.ident}" if @d_state
-
- # check infinite recursion
- if state.ident == dest.ident and state.closure.size == 1
- raise CompileError,
- sprintf("Infinite recursion: state %d, with rule %d",
- state.ident, state.ptrs[0].rule.ident)
- end
- end
- end
-
- def addsym(table, sym, ptr)
- unless s = table[sym]
- table[sym] = s = ISet.new
- end
- s.add ptr
- end
-
- def core_to_state(core)
- #
- # convert CORE to a State object.
- # If matching state does not exist, create it and add to the table.
- #
-
- k = fingerprint(core)
- unless dest = @statecache[k]
- # not registered yet
- dest = State.new(@states.size, core)
- @states.push dest
-
- @statecache[k] = dest
-
- puts "core_to_state: create state ID #{dest.ident}" if @d_state
- else
- if @d_state
- puts "core_to_state: dest is cached ID #{dest.ident}"
- puts "core_to_state: dest core #{dest.core.join(' ')}"
- end
- end
-
- dest
- end
-
- def fingerprint(arr)
- arr.map {|i| i.ident }.pack('L*')
- end
-
- #
- # DFA (Deterministic Finite Automaton) Generation
- #
-
- public
-
- def dfa
- return self if @dfa_computed
- nfa
- compute_dfa
- @dfa_computed = true
- self
- end
-
- private
-
- def compute_dfa
- la = lookahead()
- @states.each do |state|
- state.la = la
- resolve state
- end
- set_accept
- @states.each do |state|
- pack state
- end
- check_useless
- end
-
- def lookahead
- #
- # lookahead algorithm ver.3 -- from bison 1.26
- #
-
- gotos = @gotos
- if @d_la
- puts "\n--- goto ---"
- gotos.each_with_index {|g, i| print i, ' '; p g }
- end
-
- ### initialize_LA()
- ### set_goto_map()
- la_rules = []
- @states.each do |state|
- state.check_la la_rules
- end
-
- ### initialize_F()
- f = create_tmap(gotos.size)
- reads = []
- edge = []
- gotos.each do |goto|
- goto.to_state.goto_table.each do |t, st|
- if t.terminal?
- f[goto.ident] |= (1 << t.ident)
- elsif t.nullable?
- edge.push goto.to_state.gotos[t].ident
- end
- end
- if edge.empty?
- reads.push nil
- else
- reads.push edge
- edge = []
- end
- end
- digraph f, reads
- if @d_la
- puts "\n--- F1 (reads) ---"
- print_tab gotos, reads, f
- end
-
- ### build_relations()
- ### compute_FOLLOWS
- path = nil
- edge = []
- lookback = Array.new(la_rules.size, nil)
- includes = []
- gotos.each do |goto|
- goto.symbol.heads.each do |ptr|
- path = record_path(goto.from_state, ptr.rule)
- lastgoto = path.last
- st = lastgoto ? lastgoto.to_state : goto.from_state
- if st.conflict?
- addrel lookback, st.rruleid(ptr.rule), goto
- end
- path.reverse_each do |g|
- break if g.symbol.terminal?
- edge.push g.ident
- break unless g.symbol.nullable?
- end
- end
- if edge.empty?
- includes.push nil
- else
- includes.push edge
- edge = []
- end
- end
- includes = transpose(includes)
- digraph f, includes
- if @d_la
- puts "\n--- F2 (includes) ---"
- print_tab gotos, includes, f
- end
-
- ### compute_lookaheads
- la = create_tmap(la_rules.size)
- lookback.each_with_index do |arr, i|
- if arr
- arr.each do |g|
- la[i] |= f[g.ident]
- end
- end
- end
- if @d_la
- puts "\n--- LA (lookback) ---"
- print_tab la_rules, lookback, la
- end
-
- la
- end
-
- def create_tmap(size)
- Array.new(size, 0) # use Integer as bitmap
- end
-
- def addrel(tbl, i, item)
- if a = tbl[i]
- a.push item
- else
- tbl[i] = [item]
- end
- end
-
- def record_path(begst, rule)
- st = begst
- path = []
- rule.symbols.each do |t|
- goto = st.gotos[t]
- path.push goto
- st = goto.to_state
- end
- path
- end
-
- def transpose(rel)
- new = Array.new(rel.size, nil)
- rel.each_with_index do |arr, idx|
- if arr
- arr.each do |i|
- addrel new, i, idx
- end
- end
- end
- new
- end
-
- def digraph(map, relation)
- n = relation.size
- index = Array.new(n, nil)
- vertices = []
- @infinity = n + 2
-
- index.each_index do |i|
- if not index[i] and relation[i]
- traverse i, index, vertices, map, relation
- end
- end
- end
-
- def traverse(i, index, vertices, map, relation)
- vertices.push i
- index[i] = height = vertices.size
-
- if rp = relation[i]
- rp.each do |proci|
- unless index[proci]
- traverse proci, index, vertices, map, relation
- end
- if index[i] > index[proci]
- # circulative recursion !!!
- index[i] = index[proci]
- end
- map[i] |= map[proci]
- end
- end
-
- if index[i] == height
- while true
- proci = vertices.pop
- index[proci] = @infinity
- break if i == proci
-
- map[proci] |= map[i]
- end
- end
- end
-
- # for debug
- def print_atab(idx, tab)
- tab.each_with_index do |i,ii|
- printf '%-20s', idx[ii].inspect
- p i
- end
- end
-
- def print_tab(idx, rel, tab)
- tab.each_with_index do |bin,i|
- print i, ' ', idx[i].inspect, ' << '; p rel[i]
- print ' '
- each_t(@symboltable, bin) {|t| print ' ', t }
- puts
- end
- end
-
- # for debug
- def print_tab_i(idx, rel, tab, i)
- bin = tab[i]
- print i, ' ', idx[i].inspect, ' << '; p rel[i]
- print ' '
- each_t(@symboltable, bin) {|t| print ' ', t }
- end
-
- # for debug
- def printb(i)
- each_t(@symboltable, i) do |t|
- print t, ' '
- end
- puts
- end
-
- def each_t(tbl, set)
- 0.upto( set.size ) do |i|
- (0..7).each do |ii|
- if set[idx = i * 8 + ii] == 1
- yield tbl[idx]
- end
- end
- end
- end
-
- #
- # resolve
- #
-
- def resolve(state)
- if state.conflict?
- resolve_rr state, state.ritems
- resolve_sr state, state.stokens
- else
- if state.rrules.empty?
- # shift
- state.stokens.each do |t|
- state.action[t] = @actions.shift(state.goto_table[t])
- end
- else
- # reduce
- state.defact = @actions.reduce(state.rrules[0])
- end
- end
- end
-
- def resolve_rr(state, r)
- r.each do |item|
- item.each_la(@symboltable) do |t|
- act = state.action[t]
- if act
- unless act.kind_of?(Reduce)
- raise "racc: fatal: #{act.class} in action table"
- end
- # Cannot resolve R/R conflict (on t).
- # Reduce with upper rule as default.
- state.rr_conflict act.rule, item.rule, t
- else
- # No conflict.
- state.action[t] = @actions.reduce(item.rule)
- end
- end
- end
- end
-
- def resolve_sr(state, s)
- s.each do |stok|
- goto = state.goto_table[stok]
- act = state.action[stok]
-
- unless act
- # no conflict
- state.action[stok] = @actions.shift(goto)
- else
- unless act.kind_of?(Reduce)
- puts 'DEBUG -------------------------------'
- p stok
- p act
- state.action.each do |k,v|
- print k.inspect, ' ', v.inspect, "\n"
- end
- raise "racc: fatal: #{act.class} in action table"
- end
-
- # conflict on stok
-
- rtok = act.rule.precedence
- case do_resolve_sr(stok, rtok)
- when :Reduce
- # action is already set
-
- when :Shift
- # overwrite
- act.decref
- state.action[stok] = @actions.shift(goto)
-
- when :Error
- act.decref
- state.action[stok] = @actions.error
-
- when :CantResolve
- # shift as default
- act.decref
- state.action[stok] = @actions.shift(goto)
- state.sr_conflict stok, act.rule
- end
- end
- end
- end
-
- ASSOC = {
- :Left => :Reduce,
- :Right => :Shift,
- :Nonassoc => :Error
- }
-
- def do_resolve_sr(stok, rtok)
- puts "resolve_sr: s/r conflict: rtok=#{rtok}, stok=#{stok}" if @d_prec
-
- unless rtok and rtok.precedence
- puts "resolve_sr: no prec for #{rtok}(R)" if @d_prec
- return :CantResolve
- end
- rprec = rtok.precedence
-
- unless stok and stok.precedence
- puts "resolve_sr: no prec for #{stok}(S)" if @d_prec
- return :CantResolve
- end
- sprec = stok.precedence
-
- ret = if rprec == sprec
- ASSOC[rtok.assoc] or
- raise "racc: fatal: #{rtok}.assoc is not Left/Right/Nonassoc"
- else
- (rprec > sprec) ? (:Reduce) : (:Shift)
- end
-
- puts "resolve_sr: resolved as #{ret.id2name}" if @d_prec
- ret
- end
-
- #
- # complete
- #
-
- def set_accept
- anch = @symboltable.anchor
- init_state = @states[0].goto_table[@grammar.start]
- targ_state = init_state.action[anch].goto_state
- acc_state = targ_state.action[anch].goto_state
-
- acc_state.action.clear
- acc_state.goto_table.clear
- acc_state.defact = @actions.accept
- end
-
- def pack(state)
- ### find most frequently used reduce rule
- act = state.action
- arr = Array.new(@grammar.size, 0)
- act.each do |t, a|
- arr[a.ruleid] += 1 if a.kind_of?(Reduce)
- end
- i = arr.max
- s = (i > 0) ? arr.index(i) : nil
-
- ### set & delete default action
- if s
- r = @actions.reduce(s)
- if not state.defact or state.defact == r
- act.delete_if {|t, a| a == r }
- state.defact = r
- end
- else
- state.defact ||= @actions.error
- end
- end
-
- def check_useless
- used = []
- @actions.each_reduce do |act|
- if not act or act.refn == 0
- act.rule.useless = true
- else
- t = act.rule.target
- used[t.ident] = t
- end
- end
- @symboltable.nt_base.upto(@symboltable.nt_max - 1) do |n|
- unless used[n]
- @symboltable[n].useless = true
- end
- end
- end
-
- end # class StateTable
-
-
- # A LALR state.
- class State
-
- def initialize(ident, core)
- @ident = ident
- @core = core
- @goto_table = {}
- @gotos = {}
- @stokens = nil
- @ritems = nil
- @action = {}
- @defact = nil
- @rrconf = nil
- @srconf = nil
-
- @closure = make_closure(@core)
- end
-
- attr_reader :ident
- alias stateid ident
- alias hash ident
-
- attr_reader :core
- attr_reader :closure
-
- attr_reader :goto_table
- attr_reader :gotos
-
- attr_reader :stokens
- attr_reader :ritems
- attr_reader :rrules
-
- attr_reader :action
- attr_accessor :defact # default action
-
- attr_reader :rrconf
- attr_reader :srconf
-
- def inspect
- "<state #{@ident}>"
- end
-
- alias to_s inspect
-
- def ==(oth)
- @ident == oth.ident
- end
-
- alias eql? ==
-
- def make_closure(core)
- set = ISet.new
- core.each do |ptr|
- set.add ptr
- if t = ptr.dereference and t.nonterminal?
- set.update_a t.expand
- end
- end
- set.to_a
- end
-
- def check_la(la_rules)
- @conflict = false
- s = []
- r = []
- @closure.each do |ptr|
- if t = ptr.dereference
- if t.terminal?
- s[t.ident] = t
- if t.ident == 1 # $error
- @conflict = true
- end
- end
- else
- r.push ptr.rule
- end
- end
- unless r.empty?
- if not s.empty? or r.size > 1
- @conflict = true
- end
- end
- s.compact!
- @stokens = s
- @rrules = r
-
- if @conflict
- @la_rules_i = la_rules.size
- @la_rules = r.map {|i| i.ident }
- la_rules.concat r
- else
- @la_rules_i = @la_rules = nil
- end
- end
-
- def conflict?
- @conflict
- end
-
- def rruleid(rule)
- if i = @la_rules.index(rule.ident)
- @la_rules_i + i
- else
- puts '/// rruleid'
- p self
- p rule
- p @rrules
- p @la_rules_i
- raise 'racc: fatal: cannot get reduce rule id'
- end
- end
-
- def la=(la)
- return unless @conflict
- i = @la_rules_i
- @ritems = r = []
- @rrules.each do |rule|
- r.push Item.new(rule, la[i])
- i += 1
- end
- end
-
- def rr_conflict(high, low, ctok)
- c = RRconflict.new(@ident, high, low, ctok)
-
- @rrconf ||= {}
- if a = @rrconf[ctok]
- a.push c
- else
- @rrconf[ctok] = [c]
- end
- end
-
- def sr_conflict(shift, reduce)
- c = SRconflict.new(@ident, shift, reduce)
-
- @srconf ||= {}
- if a = @srconf[shift]
- a.push c
- else
- @srconf[shift] = [c]
- end
- end
-
- def n_srconflicts
- @srconf ? @srconf.size : 0
- end
-
- def n_rrconflicts
- @rrconf ? @rrconf.size : 0
- end
-
- end # class State
-
-
- #
- # Represents a transition on the grammar.
- # "Real goto" means a transition by nonterminal,
- # but this class treats also terminal's.
- # If one is a terminal transition, .ident returns nil.
- #
- class Goto
- def initialize(ident, sym, from, to)
- @ident = ident
- @symbol = sym
- @from_state = from
- @to_state = to
- end
-
- attr_reader :ident
- attr_reader :symbol
- attr_reader :from_state
- attr_reader :to_state
-
- def inspect
- "(#{@from_state.ident}-#{@symbol}->#{@to_state.ident})"
- end
- end
-
-
- # LALR item. A set of rule and its lookahead tokens.
- class Item
- def initialize(rule, la)
- @rule = rule
- @la = la
- end
-
- attr_reader :rule
- attr_reader :la
-
- def each_la(tbl)
- la = @la
- 0.upto(la.size - 1) do |i|
- (0..7).each do |ii|
- if la[idx = i * 8 + ii] == 1
- yield tbl[idx]
- end
- end
- end
- end
- end
-
-
- # The table of LALR actions. Actions are either of
- # Shift, Reduce, Accept and Error.
- class ActionTable
-
- def initialize(rt, st)
- @grammar = rt
- @statetable = st
-
- @reduce = []
- @shift = []
- @accept = nil
- @error = nil
- end
-
- def init
- @grammar.each do |rule|
- @reduce.push Reduce.new(rule)
- end
- @statetable.each do |state|
- @shift.push Shift.new(state)
- end
- @accept = Accept.new
- @error = Error.new
- end
-
- def reduce_n
- @reduce.size
- end
-
- def reduce(i)
- case i
- when Rule then i = i.ident
- when Integer then ;
- else
- raise "racc: fatal: wrong class #{i.class} for reduce"
- end
-
- r = @reduce[i] or raise "racc: fatal: reduce action #{i.inspect} not exist"
- r.incref
- r
- end
-
- def each_reduce(&block)
- @reduce.each(&block)
- end
-
- def shift_n
- @shift.size
- end
-
- def shift(i)
- case i
- when State then i = i.ident
- when Integer then ;
- else
- raise "racc: fatal: wrong class #{i.class} for shift"
- end
-
- @shift[i] or raise "racc: fatal: shift action #{i} does not exist"
- end
-
- def each_shift(&block)
- @shift.each(&block)
- end
-
- attr_reader :accept
- attr_reader :error
-
- end
-
-
- class Shift
- def initialize(goto)
- @goto_state = goto
- end
-
- attr_reader :goto_state
-
- def goto_id
- @goto_state.ident
- end
-
- def inspect
- "<shift #{@goto_state.ident}>"
- end
- end
-
-
- class Reduce
- def initialize(rule)
- @rule = rule
- @refn = 0
- end
-
- attr_reader :rule
- attr_reader :refn
-
- def ruleid
- @rule.ident
- end
-
- def inspect
- "<reduce #{@rule.ident}>"
- end
-
- def incref
- @refn += 1
- end
-
- def decref
- @refn -= 1
- raise 'racc: fatal: act.refn < 0' if @refn < 0
- end
- end
-
- class Accept
- def inspect
- "<accept>"
- end
- end
-
- class Error
- def inspect
- "<error>"
- end
- end
-
- class SRconflict
- def initialize(sid, shift, reduce)
- @stateid = sid
- @shift = shift
- @reduce = reduce
- end
-
- attr_reader :stateid
- attr_reader :shift
- attr_reader :reduce
-
- def to_s
- sprintf('state %d: S/R conflict rule %d reduce and shift %s',
- @stateid, @reduce.ruleid, @shift.to_s)
- end
- end
-
- class RRconflict
- def initialize(sid, high, low, tok)
- @stateid = sid
- @high_prec = high
- @low_prec = low
- @token = tok
- end
-
- attr_reader :stateid
- attr_reader :high_prec
- attr_reader :low_prec
- attr_reader :token
-
- def to_s
- sprintf('state %d: R/R conflict with rule %d and %d on %s',
- @stateid, @high_prec.ident, @low_prec.ident, @token.to_s)
- end
- end
-
-end
diff --git a/lib/racc/statetransitiontable.rb b/lib/racc/statetransitiontable.rb
deleted file mode 100644
index d75fa1657a..0000000000
--- a/lib/racc/statetransitiontable.rb
+++ /dev/null
@@ -1,311 +0,0 @@
-#--
-#
-#
-#
-# Copyright (c) 1999-2006 Minero Aoki
-#
-# This program is free software.
-# You can distribute/modify this program under the same terms of ruby.
-# see the file "COPYING".
-#
-#++
-
-require 'racc/parser'
-
-module Racc
-
- StateTransitionTable = Struct.new(:action_table,
- :action_check,
- :action_default,
- :action_pointer,
- :goto_table,
- :goto_check,
- :goto_default,
- :goto_pointer,
- :token_table,
- :reduce_table,
- :reduce_n,
- :shift_n,
- :nt_base,
- :token_to_s_table,
- :use_result_var,
- :debug_parser)
- class StateTransitionTable # reopen
- def StateTransitionTable.generate(states)
- StateTransitionTableGenerator.new(states).generate
- end
-
- def initialize(states)
- super()
- @states = states
- @grammar = states.grammar
- self.use_result_var = true
- self.debug_parser = true
- end
-
- attr_reader :states
- attr_reader :grammar
-
- def parser_class
- ParserClassGenerator.new(@states).generate
- end
-
- def token_value_table
- h = {}
- token_table().each do |sym, i|
- h[sym.value] = i
- end
- h
- end
- end
-
-
- class StateTransitionTableGenerator
-
- def initialize(states)
- @states = states
- @grammar = states.grammar
- end
-
- def generate
- t = StateTransitionTable.new(@states)
- gen_action_tables t, @states
- gen_goto_tables t, @grammar
- t.token_table = token_table(@grammar)
- t.reduce_table = reduce_table(@grammar)
- t.reduce_n = @states.reduce_n
- t.shift_n = @states.shift_n
- t.nt_base = @grammar.nonterminal_base
- t.token_to_s_table = @grammar.symbols.map {|sym| sym.to_s }
- t
- end
-
- def reduce_table(grammar)
- t = [0, 0, :racc_error]
- grammar.each_with_index do |rule, idx|
- next if idx == 0
- t.push rule.size
- t.push rule.target.ident
- t.push(if rule.action.empty? # and @params.omit_action_call?
- then :_reduce_none
- else "_reduce_#{idx}".intern
- end)
- end
- t
- end
-
- def token_table(grammar)
- h = {}
- grammar.symboltable.terminals.each do |t|
- h[t] = t.ident
- end
- h
- end
-
- def gen_action_tables(t, states)
- t.action_table = yytable = []
- t.action_check = yycheck = []
- t.action_default = yydefact = []
- t.action_pointer = yypact = []
- e1 = []
- e2 = []
- states.each do |state|
- yydefact.push act2actid(state.defact)
- if state.action.empty?
- yypact.push nil
- next
- end
- vector = []
- state.action.each do |tok, act|
- vector[tok.ident] = act2actid(act)
- end
- addent e1, vector, state.ident, yypact
- end
- set_table e1, e2, yytable, yycheck, yypact
- end
-
- def gen_goto_tables(t, grammar)
- t.goto_table = yytable2 = []
- t.goto_check = yycheck2 = []
- t.goto_pointer = yypgoto = []
- t.goto_default = yydefgoto = []
- e1 = []
- e2 = []
- grammar.each_nonterminal do |tok|
- tmp = []
-
- # decide default
- freq = Array.new(@states.size, 0)
- @states.each do |state|
- st = state.goto_table[tok]
- if st
- st = st.ident
- freq[st] += 1
- end
- tmp[state.ident] = st
- end
- max = freq.max
- if max > 1
- default = freq.index(max)
- tmp.map! {|i| default == i ? nil : i }
- else
- default = nil
- end
- yydefgoto.push default
-
- # delete default value
- tmp.pop until tmp.last or tmp.empty?
- if tmp.compact.empty?
- # only default
- yypgoto.push nil
- next
- end
-
- addent e1, tmp, (tok.ident - grammar.nonterminal_base), yypgoto
- end
- set_table e1, e2, yytable2, yycheck2, yypgoto
- end
-
- def addent(all, arr, chkval, ptr)
- max = arr.size
- min = nil
- arr.each_with_index do |item, idx|
- if item
- min ||= idx
- end
- end
- ptr.push(-7777) # mark
- arr = arr[min...max]
- all.push [arr, chkval, mkmapexp(arr), min, ptr.size - 1]
- end
-
- n = 2 ** 16
- begin
- Regexp.compile("a{#{n}}")
- RE_DUP_MAX = n
- rescue RegexpError
- n /= 2
- retry
- end
-
- def mkmapexp(arr)
- i = ii = 0
- as = arr.size
- map = String.new
- maxdup = RE_DUP_MAX
- curr = nil
- while i < as
- ii = i + 1
- if arr[i]
- ii += 1 while ii < as and arr[ii]
- curr = '-'
- else
- ii += 1 while ii < as and not arr[ii]
- curr = '.'
- end
-
- offset = ii - i
- if offset == 1
- map << curr
- else
- while offset > maxdup
- map << "#{curr}{#{maxdup}}"
- offset -= maxdup
- end
- map << "#{curr}{#{offset}}" if offset > 1
- end
- i = ii
- end
- Regexp.compile(map, Regexp::NOENCODING)
- end
-
- def set_table(entries, dummy, tbl, chk, ptr)
- upper = 0
- map = '-' * 10240
-
- # sort long to short
- entries.sort_by!.with_index {|a,i| [-a[0].size, i] }
-
- entries.each do |arr, chkval, expr, min, ptri|
- if upper + arr.size > map.size
- map << '-' * (arr.size + 1024)
- end
- idx = map.index(expr)
- ptr[ptri] = idx - min
- arr.each_with_index do |item, i|
- if item
- i += idx
- tbl[i] = item
- chk[i] = chkval
- map[i] = ?o
- end
- end
- upper = idx + arr.size
- end
- end
-
- def act2actid(act)
- case act
- when Shift then act.goto_id
- when Reduce then -act.ruleid
- when Accept then @states.shift_n
- when Error then @states.reduce_n * -1
- else
- raise "racc: fatal: wrong act type #{act.class} in action table"
- end
- end
-
- end
-
-
- class ParserClassGenerator
-
- def initialize(states)
- @states = states
- @grammar = states.grammar
- end
-
- def generate
- table = @states.state_transition_table
- c = Class.new(::Racc::Parser)
- c.const_set :Racc_arg, [table.action_table,
- table.action_check,
- table.action_default,
- table.action_pointer,
- table.goto_table,
- table.goto_check,
- table.goto_default,
- table.goto_pointer,
- table.nt_base,
- table.reduce_table,
- table.token_value_table,
- table.shift_n,
- table.reduce_n,
- false]
- c.const_set :Racc_token_to_s_table, table.token_to_s_table
- c.const_set :Racc_debug_parser, true
- define_actions c
- c
- end
-
- private
-
- def define_actions(c)
- c.module_eval "def _reduce_none(vals, vstack) vals[0] end"
- @grammar.each do |rule|
- if rule.action.empty?
- c.alias_method("_reduce_#{rule.ident}", :_reduce_none)
- else
- c.define_method("_racc_action_#{rule.ident}", &rule.action.proc)
- c.module_eval(<<-End, __FILE__, __LINE__ + 1)
- def _reduce_#{rule.ident}(vals, vstack)
- _racc_action_#{rule.ident}(*vals)
- end
- End
- end
- end
- end
-
- end
-
-end # module Racc
diff --git a/lib/racc/static.rb b/lib/racc/static.rb
deleted file mode 100644
index bebbeb5aa6..0000000000
--- a/lib/racc/static.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require 'racc'
-require 'racc/parser'
-require 'racc/grammarfileparser'
-require 'racc/parserfilegenerator'
-require 'racc/logfilegenerator'
diff --git a/lib/random/formatter.rb b/lib/random/formatter.rb
index 4dea61c16c..037f9d8748 100644
--- a/lib/random/formatter.rb
+++ b/lib/random/formatter.rb
@@ -165,15 +165,134 @@ module Random::Formatter
#
# The result contains 122 random bits (15.25 random bytes).
#
- # See RFC4122[https://datatracker.ietf.org/doc/html/rfc4122] for details of UUID.
+ # See RFC4122[https://www.rfc-editor.org/rfc/rfc4122] for details of UUID.
#
def uuid
- ary = random_bytes(16).unpack("NnnnnN")
- ary[2] = (ary[2] & 0x0fff) | 0x4000
- ary[3] = (ary[3] & 0x3fff) | 0x8000
- "%08x-%04x-%04x-%04x-%04x%08x" % ary
+ ary = random_bytes(16)
+ ary.setbyte(6, (ary.getbyte(6) & 0x0f) | 0x40)
+ ary.setbyte(8, (ary.getbyte(8) & 0x3f) | 0x80)
+ ary.unpack("H8H4H4H4H12").join(?-)
end
+ alias uuid_v4 uuid
+
+ # Generate a random v7 UUID (Universally Unique IDentifier).
+ #
+ # require 'random/formatter'
+ #
+ # Random.uuid_v7 # => "0188d4c3-1311-7f96-85c7-242a7aa58f1e"
+ # Random.uuid_v7 # => "0188d4c3-16fe-744f-86af-38fa04c62bb5"
+ # Random.uuid_v7 # => "0188d4c3-1af8-764f-b049-c204ce0afa23"
+ # Random.uuid_v7 # => "0188d4c3-1e74-7085-b14f-ef6415dc6f31"
+ # # |<--sorted-->| |<----- random ---->|
+ #
+ # # or
+ # prng = Random.new
+ # prng.uuid_v7 # => "0188ca51-5e72-7950-a11d-def7ff977c98"
+ #
+ # The version 7 UUID starts with the least significant 48 bits of a 64 bit
+ # Unix timestamp (milliseconds since the epoch) and fills the remaining bits
+ # with random data, excluding the version and variant bits.
+ #
+ # This allows version 7 UUIDs to be sorted by creation time. Time ordered
+ # UUIDs can be used for better database index locality of newly inserted
+ # records, which may have a significant performance benefit compared to random
+ # data inserts.
+ #
+ # The result contains 74 random bits (9.25 random bytes).
+ #
+ # Note that this method cannot be made reproducible because its output
+ # includes not only random bits but also timestamp.
+ #
+ # See draft-ietf-uuidrev-rfc4122bis[https://datatracker.ietf.org/doc/draft-ietf-uuidrev-rfc4122bis/]
+ # for details of UUIDv7.
+ #
+ # ==== Monotonicity
+ #
+ # UUIDv7 has millisecond precision by default, so multiple UUIDs created
+ # within the same millisecond are not issued in monotonically increasing
+ # order. To create UUIDs that are time-ordered with sub-millisecond
+ # precision, up to 12 bits of additional timestamp may added with
+ # +extra_timestamp_bits+. The extra timestamp precision comes at the expense
+ # of random bits. Setting <tt>extra_timestamp_bits: 12</tt> provides ~244ns
+ # of precision, but only 62 random bits (7.75 random bytes).
+ #
+ # prng = Random.new
+ # Array.new(4) { prng.uuid_v7(extra_timestamp_bits: 12) }
+ # # =>
+ # ["0188d4c7-13da-74f9-8b53-22a786ffdd5a",
+ # "0188d4c7-13da-753b-83a5-7fb9b2afaeea",
+ # "0188d4c7-13da-754a-88ea-ac0baeedd8db",
+ # "0188d4c7-13da-7557-83e1-7cad9cda0d8d"]
+ # # |<--- sorted --->| |<-- random --->|
+ #
+ # Array.new(4) { prng.uuid_v7(extra_timestamp_bits: 8) }
+ # # =>
+ # ["0188d4c7-3333-7a95-850a-de6edb858f7e",
+ # "0188d4c7-3333-7ae8-842e-bc3a8b7d0cf9", # <- out of order
+ # "0188d4c7-3333-7ae2-995a-9f135dc44ead", # <- out of order
+ # "0188d4c7-3333-7af9-87c3-8f612edac82e"]
+ # # |<--- sorted -->||<---- random --->|
+ #
+ # Any rollbacks of the system clock will break monotonicity. UUIDv7 is based
+ # on UTC, which excludes leap seconds and can rollback the clock. To avoid
+ # this, the system clock can synchronize with an NTP server configured to use
+ # a "leap smear" approach. NTP or PTP will also be needed to synchronize
+ # across distributed nodes.
+ #
+ # Counters and other mechanisms for stronger guarantees of monotonicity are
+ # not implemented. Applications with stricter requirements should follow
+ # {Section 6.2}[https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-07.html#monotonicity_counters]
+ # of the specification.
+ #
+ def uuid_v7(extra_timestamp_bits: 0)
+ case (extra_timestamp_bits = Integer(extra_timestamp_bits))
+ when 0 # min timestamp precision
+ ms = Process.clock_gettime(Process::CLOCK_REALTIME, :millisecond)
+ rand = random_bytes(10)
+ rand.setbyte(0, rand.getbyte(0) & 0x0f | 0x70) # version
+ rand.setbyte(2, rand.getbyte(2) & 0x3f | 0x80) # variant
+ "%08x-%04x-%s" % [
+ (ms & 0x0000_ffff_ffff_0000) >> 16,
+ (ms & 0x0000_0000_0000_ffff),
+ rand.unpack("H4H4H12").join("-")
+ ]
+
+ when 12 # max timestamp precision
+ ms, ns = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
+ .divmod(1_000_000)
+ extra_bits = ns * 4096 / 1_000_000
+ rand = random_bytes(8)
+ rand.setbyte(0, rand.getbyte(0) & 0x3f | 0x80) # variant
+ "%08x-%04x-7%03x-%s" % [
+ (ms & 0x0000_ffff_ffff_0000) >> 16,
+ (ms & 0x0000_0000_0000_ffff),
+ extra_bits,
+ rand.unpack("H4H12").join("-")
+ ]
+
+ when (0..12) # the generic version is slower than the special cases above
+ rand_a, rand_b1, rand_b2, rand_b3 = random_bytes(10).unpack("nnnN")
+ rand_mask_bits = 12 - extra_timestamp_bits
+ ms, ns = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
+ .divmod(1_000_000)
+ "%08x-%04x-%04x-%04x-%04x%08x" % [
+ (ms & 0x0000_ffff_ffff_0000) >> 16,
+ (ms & 0x0000_0000_0000_ffff),
+ 0x7000 |
+ ((ns * (1 << extra_timestamp_bits) / 1_000_000) << rand_mask_bits) |
+ rand_a & ((1 << rand_mask_bits) - 1),
+ 0x8000 | (rand_b1 & 0x3fff),
+ rand_b2,
+ rand_b3
+ ]
+
+ else
+ raise ArgumentError, "extra_timestamp_bits must be in 0..12"
+ end
+ end
+
+ # Internal interface to Random; Generate random data _n_ bytes.
private def gen_random(n)
self.bytes(n)
end
@@ -221,16 +340,20 @@ module Random::Formatter
result
end
+ # The default character list for #alphanumeric.
ALPHANUMERIC = [*'A'..'Z', *'a'..'z', *'0'..'9']
+
# Generate a random alphanumeric string.
#
# The argument _n_ specifies the length, in characters, of the alphanumeric
# string to be generated.
+ # The argument _chars_ specifies the character list which the result is
+ # consist of.
#
# If _n_ is not specified or is nil, 16 is assumed.
# It may be larger in the future.
#
- # The result may contain A-Z, a-z and 0-9.
+ # The result may contain A-Z, a-z and 0-9, unless _chars_ is specified.
#
# require 'random/formatter'
#
@@ -238,8 +361,13 @@ module Random::Formatter
# # or
# prng = Random.new
# prng.alphanumeric(10) #=> "i6K93NdqiH"
- def alphanumeric(n=nil)
+ #
+ # Random.alphanumeric(4, chars: [*"0".."9"]) #=> "2952"
+ # # or
+ # prng = Random.new
+ # prng.alphanumeric(10, chars: [*"!".."/"]) #=> ",.,++%/''."
+ def alphanumeric(n = nil, chars: ALPHANUMERIC)
n = 16 if n.nil?
- choose(ALPHANUMERIC, n)
+ choose(chars, n)
end
end
diff --git a/lib/rdoc.rb b/lib/rdoc.rb
index b62c22576d..9dc4595324 100644
--- a/lib/rdoc.rb
+++ b/lib/rdoc.rb
@@ -120,6 +120,17 @@ module RDoc
end
end
+ ##
+ # Searches and returns the directory for settings.
+ #
+ # 1. <tt>$HOME/.rdoc</tt> directory, if it exists.
+ # 2. The +rdoc+ directory under the path specified by the
+ # +XDG_DATA_HOME+ environment variable, if it is set.
+ # 3. <tt>$HOME/.local/share/rdoc</tt> directory.
+ #
+ # Other than the home directory, the containing directory will be
+ # created automatically.
+
def self.home
rdoc_dir = begin
File.expand_path('~/.rdoc')
@@ -129,6 +140,7 @@ module RDoc
if File.directory?(rdoc_dir)
rdoc_dir
else
+ require 'fileutils'
begin
# XDG
xdg_data_home = ENV["XDG_DATA_HOME"] || File.join(File.expand_path("~"), '.local', 'share')
diff --git a/lib/rdoc/any_method.rb b/lib/rdoc/any_method.rb
index 051f946a10..465c4a4fb2 100644
--- a/lib/rdoc/any_method.rb
+++ b/lib/rdoc/any_method.rb
@@ -116,6 +116,13 @@ class RDoc::AnyMethod < RDoc::MethodAttr
end
##
+ # Whether the method has a call-seq.
+
+ def has_call_seq?
+ !!(@call_seq || is_alias_for&._call_seq)
+ end
+
+ ##
# Loads is_alias_for from the internal name. Returns nil if the alias
# cannot be found.
@@ -297,6 +304,14 @@ class RDoc::AnyMethod < RDoc::MethodAttr
end
##
+ # Whether to skip the method description, true for methods that have
+ # aliases with a call-seq that doesn't include the method name.
+
+ def skip_description?
+ has_call_seq? && call_seq.nil? && !!(is_alias_for || !aliases.empty?)
+ end
+
+ ##
# Sets the store for this method and its referenced code objects.
def store= store
diff --git a/lib/rdoc/comment.rb b/lib/rdoc/comment.rb
index 63197492c4..04ec226436 100644
--- a/lib/rdoc/comment.rb
+++ b/lib/rdoc/comment.rb
@@ -6,8 +6,8 @@
# Each comment may have a different markup format set by #format=. By default
# 'rdoc' is used. The :markup: directive tells RDoc which format to use.
#
-# See RDoc::Markup@Other+directives for instructions on adding an alternate
-# format.
+# See RDoc::MarkupReference@Directive+for+Specifying+RDoc+Source+Format.
+
class RDoc::Comment
@@ -133,12 +133,7 @@ class RDoc::Comment
# HACK dubious
def encode! encoding
- # TODO: Remove this condition after Ruby 2.2 EOL
- if RUBY_VERSION < '2.3.0'
- @text = @text.force_encoding encoding
- else
- @text = String.new @text, encoding: encoding
- end
+ @text = String.new @text, encoding: encoding
self
end
diff --git a/lib/rdoc/context.rb b/lib/rdoc/context.rb
index c6edfb473c..c688d562c3 100644
--- a/lib/rdoc/context.rb
+++ b/lib/rdoc/context.rb
@@ -710,7 +710,7 @@ class RDoc::Context < RDoc::CodeObject
# This method exists to make it easy to work with Context subclasses that
# aren't part of RDoc.
- def each_ancestor # :nodoc:
+ def each_ancestor(&_) # :nodoc:
end
##
diff --git a/lib/rdoc/cross_reference.rb b/lib/rdoc/cross_reference.rb
index f3b703a559..4e011219e8 100644
--- a/lib/rdoc/cross_reference.rb
+++ b/lib/rdoc/cross_reference.rb
@@ -131,6 +131,9 @@ class RDoc::CrossReference
@seen = {}
end
+ ##
+ # Returns a method reference to +name+.
+
def resolve_method name
ref = nil
diff --git a/lib/rdoc/encoding.rb b/lib/rdoc/encoding.rb
index cf60badd24..67e190f782 100644
--- a/lib/rdoc/encoding.rb
+++ b/lib/rdoc/encoding.rb
@@ -86,17 +86,6 @@ module RDoc::Encoding
nil
end
- def self.remove_frozen_string_literal string
- string =~ /\A(?:#!.*\n)?(.*\n)/
- first_line = $1
-
- if first_line =~ /\A# +frozen[-_]string[-_]literal[=:].+$/i
- string = string.sub first_line, ''
- end
-
- string
- end
-
##
# Detects the encoding of +string+ based on the magic comment
@@ -124,12 +113,7 @@ module RDoc::Encoding
if text.kind_of? RDoc::Comment
text.encode! encoding
else
- # TODO: Remove this condition after Ruby 2.2 EOL
- if RUBY_VERSION < '2.3.0'
- text.force_encoding encoding
- else
- String.new text, encoding: encoding
- end
+ String.new text, encoding: encoding
end
end
diff --git a/lib/rdoc/erbio.rb b/lib/rdoc/erbio.rb
index 56c0511a3d..0f98eaedee 100644
--- a/lib/rdoc/erbio.rb
+++ b/lib/rdoc/erbio.rb
@@ -20,12 +20,8 @@ class RDoc::ERBIO < ERB
##
# Defaults +eoutvar+ to 'io', otherwise is identical to ERB's initialize
- def initialize str, safe_level = nil, legacy_trim_mode = nil, legacy_eoutvar = 'io', trim_mode: nil, eoutvar: 'io'
- if RUBY_VERSION >= '2.6'
- super(str, trim_mode: trim_mode, eoutvar: eoutvar)
- else
- super(str, safe_level, legacy_trim_mode, legacy_eoutvar)
- end
+ def initialize str, trim_mode: nil, eoutvar: 'io'
+ super(str, trim_mode: trim_mode, eoutvar: eoutvar)
end
##
diff --git a/lib/rdoc/generator/darkfish.rb b/lib/rdoc/generator/darkfish.rb
index 60e0265e8c..1b408a6f8e 100644
--- a/lib/rdoc/generator/darkfish.rb
+++ b/lib/rdoc/generator/darkfish.rb
@@ -610,7 +610,7 @@ class RDoc::Generator::Darkfish
@classes = @store.all_classes_and_modules.sort
@files = @store.all_files.sort
- @methods = @classes.map { |m| m.method_list }.flatten.sort
+ @methods = @classes.flat_map { |m| m.method_list }.sort
@modsort = get_sorted_module_list @classes
end
@@ -778,11 +778,7 @@ class RDoc::Generator::Darkfish
erbout = "_erbout_#{file_var}"
end
- if RUBY_VERSION >= '2.6'
- template = klass.new template, trim_mode: '-', eoutvar: erbout
- else
- template = klass.new template, nil, '-', erbout
- end
+ template = klass.new template, trim_mode: '-', eoutvar: erbout
@template_cache[file] = template
template
end
diff --git a/lib/rdoc/generator/json_index.rb b/lib/rdoc/generator/json_index.rb
index 3a1000033d..c454910d5c 100644
--- a/lib/rdoc/generator/json_index.rb
+++ b/lib/rdoc/generator/json_index.rb
@@ -230,9 +230,9 @@ class RDoc::Generator::JsonIndex
def index_methods
debug_msg " generating method search index"
- list = @classes.uniq.map do |klass|
+ list = @classes.uniq.flat_map do |klass|
klass.method_list
- end.flatten.sort_by do |method|
+ end.sort_by do |method|
[method.name, method.parent.full_name]
end
diff --git a/lib/rdoc/generator/pot.rb b/lib/rdoc/generator/pot.rb
index bee1133b07..b0b7c07179 100644
--- a/lib/rdoc/generator/pot.rb
+++ b/lib/rdoc/generator/pot.rb
@@ -81,6 +81,7 @@ class RDoc::Generator::POT
end
end
+ # :nodoc:
def class_dir
nil
end
diff --git a/lib/rdoc/generator/template/darkfish/_sidebar_search.rhtml b/lib/rdoc/generator/template/darkfish/_sidebar_search.rhtml
index 9c49b31376..afc7f7b88d 100644
--- a/lib/rdoc/generator/template/darkfish/_sidebar_search.rhtml
+++ b/lib/rdoc/generator/template/darkfish/_sidebar_search.rhtml
@@ -3,7 +3,7 @@
<div id="search-field-wrapper">
<input id="search-field" role="combobox" aria-label="Search"
aria-autocomplete="list" aria-controls="search-results"
- type="text" name="search" placeholder="Search" spellcheck="false"
+ type="text" name="search" placeholder="Search (/) for a class, method, ..." spellcheck="false"
title="Type to search, Up and Down to navigate, Enter to load">
</div>
diff --git a/lib/rdoc/generator/template/darkfish/class.rhtml b/lib/rdoc/generator/template/darkfish/class.rhtml
index 97d175dddc..d6510336df 100644
--- a/lib/rdoc/generator/template/darkfish/class.rhtml
+++ b/lib/rdoc/generator/template/darkfish/class.rhtml
@@ -112,6 +112,10 @@
<%- end -%>
</div>
<%- end -%>
+ <%- elsif method.has_call_seq? then -%>
+ <div class="method-heading">
+ <span class="method-name"><%= h method.name %></span>
+ </div>
<%- else -%>
<div class="method-heading">
<span class="method-name"><%= h method.name %></span><span
@@ -123,6 +127,7 @@
<%- end -%>
</div>
+ <%- unless method.skip_description? then -%>
<div class="method-description">
<%- if method.comment then -%>
<%= method.description.strip %>
@@ -145,6 +150,7 @@
</div>
<%- end -%>
</div>
+ <%- end -%>
<%- unless method.aliases.empty? then -%>
<div class="aliases">
diff --git a/lib/rdoc/generator/template/darkfish/css/rdoc.css b/lib/rdoc/generator/template/darkfish/css/rdoc.css
index ddaf4d47c6..2cc55e03b1 100644
--- a/lib/rdoc/generator/template/darkfish/css/rdoc.css
+++ b/lib/rdoc/generator/template/darkfish/css/rdoc.css
@@ -87,6 +87,17 @@ pre {
border-radius: 0.2em;
}
+em {
+ text-decoration-color: rgba(52, 48, 64, 0.25);
+ text-decoration-line: underline;
+ text-decoration-style: dotted;
+}
+
+strong,
+em {
+ background-color: rgba(158, 178, 255, 0.1);
+}
+
table {
margin: 0;
border-spacing: 0;
@@ -567,7 +578,7 @@ main .method-click-advice {
line-height: 20px;
background: url(../images/zoom.png) no-repeat right top;
}
-main .method-heading:hover .method-click-advice {
+main .method-header:hover .method-click-advice {
visibility: visible;
}
diff --git a/lib/rdoc/generator/template/darkfish/js/darkfish.js b/lib/rdoc/generator/template/darkfish/js/darkfish.js
index d0c9467751..19a85c54e1 100644
--- a/lib/rdoc/generator/template/darkfish/js/darkfish.js
+++ b/lib/rdoc/generator/template/darkfish/js/darkfish.js
@@ -78,7 +78,20 @@ function hookSearch() {
search.scrollIntoView = search.scrollInWindow;
};
+function hookFocus() {
+ document.addEventListener("keydown", (event) => {
+ if (document.activeElement.tagName === 'INPUT') {
+ return;
+ }
+ if (event.key === "/") {
+ event.preventDefault();
+ document.querySelector('#search-field').focus();
+ }
+ });
+}
+
document.addEventListener('DOMContentLoaded', function() {
hookSourceViews();
hookSearch();
+ hookFocus();
});
diff --git a/lib/rdoc/generator/template/darkfish/js/search.js b/lib/rdoc/generator/template/darkfish/js/search.js
index 58e52afecf..d3cded1d57 100644
--- a/lib/rdoc/generator/template/darkfish/js/search.js
+++ b/lib/rdoc/generator/template/darkfish/js/search.js
@@ -15,9 +15,9 @@ Search.prototype = Object.assign({}, Navigation, new function() {
this.init = function() {
var _this = this;
var observer = function(e) {
- switch(e.keyCode) {
- case 38: // Event.KEY_UP
- case 40: // Event.KEY_DOWN
+ switch(e.key) {
+ case 'ArrowUp':
+ case 'ArrowDown':
return;
}
_this.search(_this.input.value);
diff --git a/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml b/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml
index c8a422a521..54a376c9e5 100644
--- a/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml
+++ b/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml
@@ -47,9 +47,9 @@
<h2 id="methods">Methods</h2>
<ul>
-<%- @store.all_classes_and_modules.map do |mod|
+<%- @store.all_classes_and_modules.flat_map do |mod|
mod.method_list
- end.flatten.sort.each do |method| %>
+ end.sort.each do |method| %>
<li class="method">
<a href="<%= method.path %>"><%= h method.pretty_name %></a>
&mdash;
diff --git a/lib/rdoc/generator/template/json_index/js/navigation.js b/lib/rdoc/generator/template/json_index/js/navigation.js
index dfad74b1ae..137e3a0038 100644
--- a/lib/rdoc/generator/template/json_index/js/navigation.js
+++ b/lib/rdoc/generator/template/json_index/js/navigation.js
@@ -23,24 +23,24 @@ Navigation = new function() {
this.onkeydown = function(e) {
if (!this.navigationActive) return;
- switch(e.keyCode) {
- case 37: //Event.KEY_LEFT:
+ switch(e.key) {
+ case 'ArrowLeft':
if (this.moveLeft()) e.preventDefault();
break;
- case 38: //Event.KEY_UP:
- if (e.keyCode == 38 || e.ctrlKey) {
+ case 'ArrowUp':
+ if (e.key == 'ArrowUp' || e.ctrlKey) {
if (this.moveUp()) e.preventDefault();
}
break;
- case 39: //Event.KEY_RIGHT:
+ case 'ArrowRight':
if (this.moveRight()) e.preventDefault();
break;
- case 40: //Event.KEY_DOWN:
- if (e.keyCode == 40 || e.ctrlKey) {
+ case 'ArrowDown':
+ if (e.key == 'ArrowDown' || e.ctrlKey) {
if (this.moveDown()) e.preventDefault();
}
break;
- case 13: //Event.KEY_RETURN:
+ case 'Enter':
if (this.current) e.preventDefault();
this.select(this.current);
break;
diff --git a/lib/rdoc/markdown.rb b/lib/rdoc/markdown.rb
index a0709b6352..5c72a5f224 100644
--- a/lib/rdoc/markdown.rb
+++ b/lib/rdoc/markdown.rb
@@ -175,7 +175,7 @@
# [dingus]: http://daringfireball.net/projects/markdown/dingus
# [GFM]: https://github.github.com/gfm/
# [pegmarkdown]: https://github.com/jgm/peg-markdown
-# [PHPE]: http://michelf.com/projects/php-markdown/extra/#def-list
+# [PHPE]: https://michelf.ca/projects/php-markdown/extra/#def-list
# [syntax]: http://daringfireball.net/projects/markdown/syntax
#--
# Last updated to jgm/peg-markdown commit 8f8fc22ef0
@@ -16445,12 +16445,12 @@ class RDoc::Markdown
return _tmp
end
- # DefinitionListLabel = StrChunk:label @Sp @Newline { label }
+ # DefinitionListLabel = Inline:label @Sp @Newline { label }
def _DefinitionListLabel
_save = self.pos
while true # sequence
- _tmp = apply(:_StrChunk)
+ _tmp = apply(:_Inline)
label = @result
unless _tmp
self.pos = _save
@@ -16777,7 +16777,7 @@ class RDoc::Markdown
Rules[:_TableAlign] = rule_info("TableAlign", "< /:?-+:?/ > @Sp { text.start_with?(\":\") ? (text.end_with?(\":\") ? :center : :left) : (text.end_with?(\":\") ? :right : nil) }")
Rules[:_DefinitionList] = rule_info("DefinitionList", "&{ definition_lists? } DefinitionListItem+:list { RDoc::Markup::List.new :NOTE, *list.flatten }")
Rules[:_DefinitionListItem] = rule_info("DefinitionListItem", "DefinitionListLabel+:label DefinitionListDefinition+:defns { list_items = [] list_items << RDoc::Markup::ListItem.new(label, defns.shift) list_items.concat defns.map { |defn| RDoc::Markup::ListItem.new nil, defn } unless list_items.empty? list_items }")
- Rules[:_DefinitionListLabel] = rule_info("DefinitionListLabel", "StrChunk:label @Sp @Newline { label }")
+ Rules[:_DefinitionListLabel] = rule_info("DefinitionListLabel", "Inline:label @Sp @Newline { label }")
Rules[:_DefinitionListDefinition] = rule_info("DefinitionListDefinition", "@NonindentSpace \":\" @Space Inlines:a @BlankLine+ { paragraph a }")
# :startdoc:
end
diff --git a/lib/rdoc/markdown/literals.rb b/lib/rdoc/markdown/literals.rb
index 37659b7ae0..c5c15d3100 100644
--- a/lib/rdoc/markdown/literals.rb
+++ b/lib/rdoc/markdown/literals.rb
@@ -3,7 +3,6 @@
# :markup: markdown
##
-#--
# This set of literals is for Ruby 1.9 regular expressions and gives full
# unicode support.
#
diff --git a/lib/rdoc/markup/attribute_manager.rb b/lib/rdoc/markup/attribute_manager.rb
index 1162f27284..f6eb06da95 100644
--- a/lib/rdoc/markup/attribute_manager.rb
+++ b/lib/rdoc/markup/attribute_manager.rb
@@ -138,6 +138,7 @@ class RDoc::Markup::AttributeManager
res
end
+ # :nodoc:
def exclusive?(attr)
(attr & @exclusive_bitmap) != 0
end
@@ -155,6 +156,7 @@ class RDoc::Markup::AttributeManager
convert_attrs_word_pair_map(str, attrs, exclusive)
end
+ # :nodoc:
def convert_attrs_matching_word_pairs(str, attrs, exclusive)
# first do matching ones
tags = @matching_word_pairs.select { |start, bitmap|
@@ -179,6 +181,7 @@ class RDoc::Markup::AttributeManager
str.delete!(NON_PRINTING_START + NON_PRINTING_END)
end
+ # :nodoc:
def convert_attrs_word_pair_map(str, attrs, exclusive)
# then non-matching
unless @word_pair_map.empty? then
diff --git a/lib/rdoc/markup/formatter.rb b/lib/rdoc/markup/formatter.rb
index 37a2c2751a..9daffaabb8 100644
--- a/lib/rdoc/markup/formatter.rb
+++ b/lib/rdoc/markup/formatter.rb
@@ -90,7 +90,7 @@ class RDoc::Markup::Formatter
def add_regexp_handling_TIDYLINK
@markup.add_regexp_handling(/(?:
- \{.*?\} | # multi-word label
+ \{[^{}]*\} | # multi-word label
\b[^\s{}]+? # single-word label
)
diff --git a/lib/rdoc/markup/parser.rb b/lib/rdoc/markup/parser.rb
index 0029df7e65..9c77048591 100644
--- a/lib/rdoc/markup/parser.rb
+++ b/lib/rdoc/markup/parser.rb
@@ -218,7 +218,7 @@ class RDoc::Markup::Parser
break if peek_token.first == :BREAK
- data << ' ' if skip :NEWLINE
+ data << ' ' if skip :NEWLINE and /#{SPACE_SEPARATED_LETTER_CLASS}\z/o.match?(data)
else
unget
break
@@ -420,6 +420,8 @@ class RDoc::Markup::Parser
# A simple wrapper of StringScanner that is aware of the current column and lineno
class MyStringScanner
+ # :stopdoc:
+
def initialize(input)
@line = @column = 0
@s = StringScanner.new input
@@ -456,6 +458,8 @@ class RDoc::Markup::Parser
def [](i)
@s[i]
end
+
+ #:startdoc:
end
##
diff --git a/lib/rdoc/markup/table.rb b/lib/rdoc/markup/table.rb
index 7bcb10aff3..27a20f073a 100644
--- a/lib/rdoc/markup/table.rb
+++ b/lib/rdoc/markup/table.rb
@@ -3,12 +3,21 @@
# A section of table
class RDoc::Markup::Table
- attr_accessor :header, :align, :body
+ # headers of each column
+ attr_accessor :header
+ # alignments of each column
+ attr_accessor :align
+
+ # body texts of each column
+ attr_accessor :body
+
+ # Creates new instance
def initialize header, align, body
@header, @align, @body = header, align, body
end
+ # :stopdoc:
def == other
self.class == other.class and
@header == other.header and
@@ -20,7 +29,7 @@ class RDoc::Markup::Table
visitor.accept_table @header, @body, @align
end
- def pretty_print q # :nodoc:
+ def pretty_print q
q.group 2, '[Table: ', ']' do
q.group 2, '[Head: ', ']' do
q.seplist @header.zip(@align) do |text, align|
diff --git a/lib/rdoc/markup/to_bs.rb b/lib/rdoc/markup/to_bs.rb
index f9b86487db..afd9d6e981 100644
--- a/lib/rdoc/markup/to_bs.rb
+++ b/lib/rdoc/markup/to_bs.rb
@@ -41,6 +41,31 @@ class RDoc::Markup::ToBs < RDoc::Markup::ToRdoc
end
##
+ # Prepares the visitor for consuming +list_item+
+
+ def accept_list_item_start list_item
+ type = @list_type.last
+
+ case type
+ when :NOTE, :LABEL then
+ bullets = Array(list_item.label).map do |label|
+ attributes(label).strip
+ end.join "\n"
+
+ bullets << ":\n" unless bullets.empty?
+
+ @prefix = ' ' * @indent
+ @indent += 2
+ @prefix << bullets + (' ' * @indent)
+ else
+ bullet = type == :BULLET ? '*' : @list_index.last.to_s + '.'
+ @prefix = (' ' * @indent) + bullet.ljust(bullet.length + 1)
+ width = bullet.length + 1
+ @indent += width
+ end
+ end
+
+ ##
# Turns on or off regexp handling for +convert_string+
def annotate tag
diff --git a/lib/rdoc/markup/to_html.rb b/lib/rdoc/markup/to_html.rb
index b33cb620ec..91cadf9d16 100644
--- a/lib/rdoc/markup/to_html.rb
+++ b/lib/rdoc/markup/to_html.rb
@@ -61,6 +61,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
#
# These methods are used by regexp handling markup added by RDoc::Markup#add_regexp_handling.
+ # :nodoc:
URL_CHARACTERS_REGEXP_STR = /[A-Za-z0-9\-._~:\/\?#\[\]@!$&'\(\)*+,;%=]/.source
##
@@ -202,7 +203,9 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
def accept_paragraph paragraph
@res << "\n<p>"
text = paragraph.text @hard_break
- text = text.gsub(/\r?\n/, ' ')
+ text = text.gsub(/(#{SPACE_SEPARATED_LETTER_CLASS})?\K\r?\n(?=(?(1)(#{SPACE_SEPARATED_LETTER_CLASS})?))/o) {
+ defined?($2) && ' '
+ }
@res << to_html(text)
@res << "</p>\n"
end
@@ -430,7 +433,9 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
def parseable? text
verbose, $VERBOSE = $VERBOSE, nil
- eval("BEGIN {return true}\n#{text}")
+ catch(:valid) do
+ eval("BEGIN { throw :valid, true }\n#{text}")
+ end
rescue SyntaxError
false
ensure
diff --git a/lib/rdoc/markup/to_html_crossref.rb b/lib/rdoc/markup/to_html_crossref.rb
index 434b622495..9b5de62fd6 100644
--- a/lib/rdoc/markup/to_html_crossref.rb
+++ b/lib/rdoc/markup/to_html_crossref.rb
@@ -42,6 +42,7 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
@cross_reference = RDoc::CrossReference.new @context
end
+ # :nodoc:
def init_link_notation_regexp_handlings
add_regexp_handling_RDOCLINK
@@ -62,8 +63,8 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
name = name[1..-1] unless @show_hash if name[0, 1] == '#'
- if !(name.end_with?('+@', '-@')) and name =~ /(.*[^#:])@/
- text ||= "#{CGI.unescape $'} at <code>#{$1}</code>"
+ if !(name.end_with?('+@', '-@')) and name =~ /(.*[^#:])?@/
+ text ||= [CGI.unescape($'), (" at <code>#{$1}</code>" if $~.begin(1))].join("")
code = false
else
text ||= name
@@ -138,35 +139,34 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
# Creates an HTML link to +name+ with the given +text+.
def link name, text, code = true
- if !(name.end_with?('+@', '-@')) and name =~ /(.*[^#:])@/
+ if !(name.end_with?('+@', '-@')) and name =~ /(.*[^#:])?@/
name = $1
label = $'
end
- ref = @cross_reference.resolve name, text
+ ref = @cross_reference.resolve name, text if name
case ref
when String then
ref
else
- path = ref.as_href @from_path
+ path = ref ? ref.as_href(@from_path) : +""
if code and RDoc::CodeObject === ref and !(RDoc::TopLevel === ref)
text = "<code>#{CGI.escapeHTML text}</code>"
end
- if path =~ /#/ then
- path << "-label-#{label}"
- elsif ref.sections and
- ref.sections.any? { |section| label == section.title } then
- path << "##{label}"
- else
- if ref.respond_to?(:aref)
+ if label
+ if path =~ /#/
+ path << "-label-#{label}"
+ elsif ref&.sections&.any? { |section| label == section.title }
+ path << "##{label}"
+ elsif ref.respond_to?(:aref)
path << "##{ref.aref}-label-#{label}"
else
path << "#label-#{label}"
end
- end if label
+ end
"<a href=\"#{path}\">#{text}</a>"
end
diff --git a/lib/rdoc/markup/to_html_snippet.rb b/lib/rdoc/markup/to_html_snippet.rb
index 8219f0b169..f471395a3a 100644
--- a/lib/rdoc/markup/to_html_snippet.rb
+++ b/lib/rdoc/markup/to_html_snippet.rb
@@ -66,6 +66,9 @@ class RDoc::Markup::ToHtmlSnippet < RDoc::Markup::ToHtml
alias accept_rule ignore
+ ##
+ # Adds +paragraph+ to the output
+
def accept_paragraph paragraph
para = @in_list_entry.last || "<p>"
diff --git a/lib/rdoc/markup/to_joined_paragraph.rb b/lib/rdoc/markup/to_joined_paragraph.rb
index aaa343157e..31cbe0853c 100644
--- a/lib/rdoc/markup/to_joined_paragraph.rb
+++ b/lib/rdoc/markup/to_joined_paragraph.rb
@@ -25,9 +25,9 @@ class RDoc::Markup::ToJoinedParagraph < RDoc::Markup::Formatter
def accept_paragraph paragraph
parts = paragraph.parts.chunk do |part|
String === part
- end.map do |string, chunk|
+ end.flat_map do |string, chunk|
string ? chunk.join.rstrip : chunk
- end.flatten
+ end
paragraph.parts.replace parts
end
diff --git a/lib/rdoc/markup/to_markdown.rb b/lib/rdoc/markup/to_markdown.rb
index 5dd60e18f5..b915fab60b 100644
--- a/lib/rdoc/markup/to_markdown.rb
+++ b/lib/rdoc/markup/to_markdown.rb
@@ -45,8 +45,6 @@ class RDoc::Markup::ToMarkdown < RDoc::Markup::ToRdoc
# Finishes consumption of `list`
def accept_list_end list
- @res << "\n"
-
super
end
@@ -60,6 +58,8 @@ class RDoc::Markup::ToMarkdown < RDoc::Markup::ToRdoc
when :NOTE, :LABEL then
use_prefix
+ @res << "\n"
+
4
else
@list_index[-1] = @list_index.last.succ
@@ -81,11 +81,11 @@ class RDoc::Markup::ToMarkdown < RDoc::Markup::ToRdoc
attributes(label).strip
end.join "\n"
- bullets << "\n:"
+ bullets << "\n" unless bullets.empty?
@prefix = ' ' * @indent
@indent += 4
- @prefix << bullets + (' ' * (@indent - 1))
+ @prefix << bullets << ":" << (' ' * (@indent - 1))
else
bullet = type == :BULLET ? '*' : @list_index.last.to_s + '.'
@prefix = (' ' * @indent) + bullet.ljust(4)
diff --git a/lib/rdoc/markup/to_rdoc.rb b/lib/rdoc/markup/to_rdoc.rb
index 6929049582..88234f5096 100644
--- a/lib/rdoc/markup/to_rdoc.rb
+++ b/lib/rdoc/markup/to_rdoc.rb
@@ -145,11 +145,19 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
case type
when :NOTE, :LABEL then
- bullets = Array(list_item.label).map do |label|
+ stripped_labels = Array(list_item.label).map do |label|
attributes(label).strip
- end.join "\n"
+ end
+
+ bullets = case type
+ when :NOTE
+ stripped_labels.map { |b| "#{b}::" }
+ when :LABEL
+ stripped_labels.map { |b| "[#{b}]" }
+ end
- bullets << ":\n" unless bullets.empty?
+ bullets = bullets.join("\n")
+ bullets << "\n" unless stripped_labels.empty?
@prefix = ' ' * @indent
@indent += 2
diff --git a/lib/rdoc/options.rb b/lib/rdoc/options.rb
index 6ee29fd071..7518e6cc54 100644
--- a/lib/rdoc/options.rb
+++ b/lib/rdoc/options.rb
@@ -105,6 +105,7 @@ class RDoc::Options
generator_name
generator_options
generators
+ locale
op_dir
page_dir
option_parser
diff --git a/lib/rdoc/parser.rb b/lib/rdoc/parser.rb
index 3bb6f5d1f2..425105effa 100644
--- a/lib/rdoc/parser.rb
+++ b/lib/rdoc/parser.rb
@@ -125,9 +125,11 @@ class RDoc::Parser
return parser if ext_name.empty?
if parser == RDoc::Parser::Simple and ext_name !~ /txt|rdoc/ then
- case check_modeline file_name
+ case mode = check_modeline(file_name)
when nil, 'rdoc' then # continue
- else return nil
+ else
+ RDoc::Parser.parsers.find { |_, p| return p if mode.casecmp?(p.name[/\w+\z/]) }
+ return nil
end
end
diff --git a/lib/rdoc/parser/c.rb b/lib/rdoc/parser/c.rb
index 5695bf1acb..f8f238fd74 100644
--- a/lib/rdoc/parser/c.rb
+++ b/lib/rdoc/parser/c.rb
@@ -575,19 +575,18 @@ class RDoc::Parser::C < RDoc::Parser
table = {}
file_content.scan(%r{
((?>/\*.*?\*/\s*)?)
- ((?:(?:\w+)\s+)?
- (?:intern\s+)?VALUE\s+(\w+)
- \s*(?:\([^)]*\))(?:[^\);]|$))
+ ((?:\w+\s+){0,2} VALUE\s+(\w+)
+ \s*(?:\([^\)]*\))(?:[^\);]|$))
| ((?>/\*.*?\*/\s*))^\s*(\#\s*define\s+(\w+)\s+(\w+))
| ^\s*\#\s*define\s+(\w+)\s+(\w+)
}xm) do
case
- when $1
- table[$3] = [:func_def, $1, $2, $~.offset(2)] if !table[$3] || table[$3][0] != :func_def
- when $4
- table[$6] = [:macro_def, $4, $5, $~.offset(5), $7] if !table[$6] || table[$6][0] == :macro_alias
- when $8
- table[$8] ||= [:macro_alias, $9]
+ when name = $3
+ table[name] = [:func_def, $1, $2, $~.offset(2)] if !(t = table[name]) || t[0] != :func_def
+ when name = $6
+ table[name] = [:macro_def, $4, $5, $~.offset(5), $7] if !(t = table[name]) || t[0] == :macro_alias
+ when name = $8
+ table[name] ||= [:macro_alias, $9]
end
end
table
@@ -757,17 +756,27 @@ class RDoc::Parser::C < RDoc::Parser
def gen_const_table file_content
table = {}
@content.scan(%r{
- ((?>^\s*/\*.*?\*/\s+))
- rb_define_(\w+)\((?:\s*(?:\w+),)?\s*
- "(\w+)"\s*,
+ (?<doc>(?>^\s*/\*.*?\*/\s+))
+ rb_define_(?<type>\w+)\(\s*(?:\w+),\s*
+ "(?<name>\w+)"\s*,
.*?\)\s*;
+ | (?<doc>(?>^\s*/\*.*?\*/\s+))
+ rb_file_(?<type>const)\(\s*
+ "(?<name>\w+)"\s*,
+ .*?\)\s*;
+ | (?<doc>(?>^\s*/\*.*?\*/\s+))
+ rb_curses_define_(?<type>const)\(\s*
+ (?<name>\w+)
+ \s*\)\s*;
| Document-(?:const|global|variable):\s
- ((?:\w+::)*\w+)
- \s*?\n((?>.*?\*/))
+ (?<name>(?:\w+::)*\w+)
+ \s*?\n(?<doc>(?>.*?\*/))
}mxi) do
- case
- when $1 then table[[$2, $3]] = $1
- when $4 then table[$4] = "/*\n" + $5
+ name, doc, type = $~.values_at(:name, :doc, :type)
+ if type
+ table[[type, name]] = doc
+ else
+ table[name] = "/*\n" + doc
end
end
table
@@ -939,14 +948,13 @@ class RDoc::Parser::C < RDoc::Parser
# "/* definition: comment */" form. The literal ':' and '\' characters
# can be escaped with a backslash.
if type.downcase == 'const' then
- no_match, new_definition, new_comment = comment.text.split(/(\A.*):/)
+ if /\A(.+?)?:(?!\S)/ =~ comment.text
+ new_definition, new_comment = $1, $'
- if no_match and no_match.empty? then
- if new_definition.empty? then # Default to literal C definition
+ if !new_definition # Default to literal C definition
new_definition = definition
else
- new_definition = new_definition.gsub("\:", ":")
- new_definition = new_definition.gsub("\\", '\\')
+ new_definition = new_definition.gsub(/\\([\\:])/, '\1')
end
new_definition.sub!(/\A(\s+)/, '')
@@ -1217,6 +1225,9 @@ class RDoc::Parser::C < RDoc::Parser
@top_level
end
+ ##
+ # Creates a RDoc::Comment instance.
+
def new_comment text = nil, location = nil, language = nil
RDoc::Comment.new(text, location, language).tap do |comment|
comment.format = @markup
diff --git a/lib/rdoc/parser/changelog.rb b/lib/rdoc/parser/changelog.rb
index 2a685d1d79..a046241870 100644
--- a/lib/rdoc/parser/changelog.rb
+++ b/lib/rdoc/parser/changelog.rb
@@ -216,12 +216,22 @@ class RDoc::Parser::ChangeLog < RDoc::Parser
@top_level
end
+ ##
+ # The extension for Git commit log
+
module Git
+ ##
+ # Parses auxiliary info. Currentry `base-url` to expand
+ # references is effective.
+
def parse_info(info)
/^\s*base-url\s*=\s*(.*\S)/ =~ info
@base_url = $1
end
+ ##
+ # Parses the entries in the Git commit logs
+
def parse_entries
entries = []
@@ -244,6 +254,11 @@ class RDoc::Parser::ChangeLog < RDoc::Parser
entries
end
+ ##
+ # Returns a list of ChangeLog entries as
+ # RDoc::Parser::ChangeLog::Git::LogEntry list for the given
+ # +entries+.
+
def create_entries entries
# git log entries have no strictly itemized style like the old
# style, just assume Markdown.
diff --git a/lib/rdoc/parser/ripper_state_lex.rb b/lib/rdoc/parser/ripper_state_lex.rb
index 5492f08726..f6cefd0305 100644
--- a/lib/rdoc/parser/ripper_state_lex.rb
+++ b/lib/rdoc/parser/ripper_state_lex.rb
@@ -1,7 +1,12 @@
# frozen_string_literal: true
require 'ripper'
+##
+# Wrapper for Ripper lex states
+
class RDoc::Parser::RipperStateLex
+ # :stopdoc:
+
# TODO: Remove this constants after Ruby 2.4 EOL
RIPPER_HAS_LEX_STATE = Ripper::Filter.method_defined?(:state)
@@ -368,7 +373,7 @@ class RDoc::Parser::RipperStateLex
private def get_symbol_tk(tk)
is_symbol = true
symbol_tk = Token.new(tk.line_no, tk.char_no, :on_symbol)
- if ":'" == tk[:text] or ':"' == tk[:text]
+ if ":'" == tk[:text] or ':"' == tk[:text] or tk[:text].start_with?('%s')
tk1 = get_string_tk(tk)
symbol_tk[:text] = tk1[:text]
symbol_tk[:state] = tk1[:state]
@@ -565,6 +570,9 @@ class RDoc::Parser::RipperStateLex
tk
end
+ # :startdoc:
+
+ # New lexer for +code+.
def initialize(code)
@buf = []
@heredoc_queue = []
@@ -572,6 +580,7 @@ class RDoc::Parser::RipperStateLex
@tokens = @inner_lex.parse([])
end
+ # Returns tokens parsed from +code+.
def self.parse(code)
lex = self.new(code)
tokens = []
@@ -584,6 +593,7 @@ class RDoc::Parser::RipperStateLex
tokens
end
+ # Returns +true+ if lex state will be +END+ after +token+.
def self.end?(token)
(token[:state] & EXPR_END)
end
diff --git a/lib/rdoc/parser/ruby.rb b/lib/rdoc/parser/ruby.rb
index 2c8a19f608..85f1cd0391 100644
--- a/lib/rdoc/parser/ruby.rb
+++ b/lib/rdoc/parser/ruby.rb
@@ -180,6 +180,9 @@ class RDoc::Parser::Ruby < RDoc::Parser
reset
end
+ ##
+ # Return +true+ if +tk+ is a newline.
+
def tk_nl?(tk)
:on_nl == tk[:kind] or :on_ignored_nl == tk[:kind]
end
@@ -786,8 +789,10 @@ class RDoc::Parser::Ruby < RDoc::Parser
al.line = line_no
read_documentation_modifiers al, RDoc::ATTR_MODIFIERS
- context.add_alias al
- @stats.add_alias al
+ if al.document_self or not @track_visibility
+ context.add_alias al
+ @stats.add_alias al
+ end
al
end
@@ -1450,6 +1455,12 @@ class RDoc::Parser::Ruby < RDoc::Parser
meth = RDoc::AnyMethod.new get_tkread, name
look_for_directives_in meth, comment
meth.singleton = single == SINGLE ? true : singleton
+ if singleton
+ # `current_line_visibility' is useless because it works against
+ # the normal method named as same as the singleton method, after
+ # the latter was defined. Of course these are different things.
+ container.current_line_visibility = :public
+ end
record_location meth
meth.line = line_no
@@ -1773,6 +1784,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
nest = 1
save_visibility = container.visibility
+ container.visibility = :public unless current_method
non_comment_seen = true
diff --git a/lib/rdoc/rd/block_parser.rb b/lib/rdoc/rd/block_parser.rb
index 52ea1467d8..527147d91d 100644
--- a/lib/rdoc/rd/block_parser.rb
+++ b/lib/rdoc/rd/block_parser.rb
@@ -1,11 +1,659 @@
# frozen_string_literal: true
#
# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.6.2
-# from Racc grammar file "".
+# This file is automatically generated by Racc 1.7.3
+# from Racc grammar file "block_parser.ry".
#
-require 'racc/parser.rb'
+###### racc/parser.rb begin
+unless $".find {|p| p.end_with?('/racc/parser.rb')}
+$".push "#{__dir__}/racc/parser.rb"
+#--
+# Copyright (c) 1999-2006 Minero Aoki
+#
+# This program is free software.
+# You can distribute/modify this program under the same terms of ruby.
+#
+# As a special exception, when this code is copied by Racc
+# into a Racc output file, you may use that output file
+# without restriction.
+#++
+
+unless $".find {|p| p.end_with?('/racc/info.rb')}
+$".push "#{__dir__}/racc/info.rb"
+
+module Racc
+ VERSION = '1.7.3'
+ Version = VERSION
+ Copyright = 'Copyright (c) 1999-2006 Minero Aoki'
+end
+
+end
+
+
+unless defined?(NotImplementedError)
+ NotImplementedError = NotImplementError # :nodoc:
+end
+
+module Racc
+ class ParseError < StandardError; end
+end
+unless defined?(::ParseError)
+ ParseError = Racc::ParseError # :nodoc:
+end
+
+# Racc is a LALR(1) parser generator.
+# It is written in Ruby itself, and generates Ruby programs.
+#
+# == Command-line Reference
+#
+# racc [-o<var>filename</var>] [--output-file=<var>filename</var>]
+# [-e<var>rubypath</var>] [--executable=<var>rubypath</var>]
+# [-v] [--verbose]
+# [-O<var>filename</var>] [--log-file=<var>filename</var>]
+# [-g] [--debug]
+# [-E] [--embedded]
+# [-l] [--no-line-convert]
+# [-c] [--line-convert-all]
+# [-a] [--no-omit-actions]
+# [-C] [--check-only]
+# [-S] [--output-status]
+# [--version] [--copyright] [--help] <var>grammarfile</var>
+#
+# [+grammarfile+]
+# Racc grammar file. Any extension is permitted.
+# [-o+outfile+, --output-file=+outfile+]
+# A filename for output. default is <+filename+>.tab.rb
+# [-O+filename+, --log-file=+filename+]
+# Place logging output in file +filename+.
+# Default log file name is <+filename+>.output.
+# [-e+rubypath+, --executable=+rubypath+]
+# output executable file(mode 755). where +path+ is the Ruby interpreter.
+# [-v, --verbose]
+# verbose mode. create +filename+.output file, like yacc's y.output file.
+# [-g, --debug]
+# add debug code to parser class. To display debugging information,
+# use this '-g' option and set @yydebug true in parser class.
+# [-E, --embedded]
+# Output parser which doesn't need runtime files (racc/parser.rb).
+# [-F, --frozen]
+# Output parser which declares frozen_string_literals: true
+# [-C, --check-only]
+# Check syntax of racc grammar file and quit.
+# [-S, --output-status]
+# Print messages time to time while compiling.
+# [-l, --no-line-convert]
+# turns off line number converting.
+# [-c, --line-convert-all]
+# Convert line number of actions, inner, header and footer.
+# [-a, --no-omit-actions]
+# Call all actions, even if an action is empty.
+# [--version]
+# print Racc version and quit.
+# [--copyright]
+# Print copyright and quit.
+# [--help]
+# Print usage and quit.
+#
+# == Generating Parser Using Racc
+#
+# To compile Racc grammar file, simply type:
+#
+# $ racc parse.y
+#
+# This creates Ruby script file "parse.tab.y". The -o option can change the output filename.
+#
+# == Writing A Racc Grammar File
+#
+# If you want your own parser, you have to write a grammar file.
+# A grammar file contains the name of your parser class, grammar for the parser,
+# user code, and anything else.
+# When writing a grammar file, yacc's knowledge is helpful.
+# If you have not used yacc before, Racc is not too difficult.
+#
+# Here's an example Racc grammar file.
+#
+# class Calcparser
+# rule
+# target: exp { print val[0] }
+#
+# exp: exp '+' exp
+# | exp '*' exp
+# | '(' exp ')'
+# | NUMBER
+# end
+#
+# Racc grammar files resemble yacc files.
+# But (of course), this is Ruby code.
+# yacc's $$ is the 'result', $0, $1... is
+# an array called 'val', and $-1, $-2... is an array called '_values'.
+#
+# See the {Grammar File Reference}[rdoc-ref:lib/racc/rdoc/grammar.en.rdoc] for
+# more information on grammar files.
+#
+# == Parser
+#
+# Then you must prepare the parse entry method. There are two types of
+# parse methods in Racc, Racc::Parser#do_parse and Racc::Parser#yyparse
+#
+# Racc::Parser#do_parse is simple.
+#
+# It's yyparse() of yacc, and Racc::Parser#next_token is yylex().
+# This method must returns an array like [TOKENSYMBOL, ITS_VALUE].
+# EOF is [false, false].
+# (TOKENSYMBOL is a Ruby symbol (taken from String#intern) by default.
+# If you want to change this, see the grammar reference.
+#
+# Racc::Parser#yyparse is little complicated, but useful.
+# It does not use Racc::Parser#next_token, instead it gets tokens from any iterator.
+#
+# For example, <code>yyparse(obj, :scan)</code> causes
+# calling +obj#scan+, and you can return tokens by yielding them from +obj#scan+.
+#
+# == Debugging
+#
+# When debugging, "-v" or/and the "-g" option is helpful.
+#
+# "-v" creates verbose log file (.output).
+# "-g" creates a "Verbose Parser".
+# Verbose Parser prints the internal status when parsing.
+# But it's _not_ automatic.
+# You must use -g option and set +@yydebug+ to +true+ in order to get output.
+# -g option only creates the verbose parser.
+#
+# === Racc reported syntax error.
+#
+# Isn't there too many "end"?
+# grammar of racc file is changed in v0.10.
+#
+# Racc does not use '%' mark, while yacc uses huge number of '%' marks..
+#
+# === Racc reported "XXXX conflicts".
+#
+# Try "racc -v xxxx.y".
+# It causes producing racc's internal log file, xxxx.output.
+#
+# === Generated parsers does not work correctly
+#
+# Try "racc -g xxxx.y".
+# This command let racc generate "debugging parser".
+# Then set @yydebug=true in your parser.
+# It produces a working log of your parser.
+#
+# == Re-distributing Racc runtime
+#
+# A parser, which is created by Racc, requires the Racc runtime module;
+# racc/parser.rb.
+#
+# Ruby 1.8.x comes with Racc runtime module,
+# you need NOT distribute Racc runtime files.
+#
+# If you want to include the Racc runtime module with your parser.
+# This can be done by using '-E' option:
+#
+# $ racc -E -omyparser.rb myparser.y
+#
+# This command creates myparser.rb which `includes' Racc runtime.
+# Only you must do is to distribute your parser file (myparser.rb).
+#
+# Note: parser.rb is ruby license, but your parser is not.
+# Your own parser is completely yours.
+module Racc
+
+ unless defined?(Racc_No_Extensions)
+ Racc_No_Extensions = false # :nodoc:
+ end
+
+ class Parser
+
+ Racc_Runtime_Version = ::Racc::VERSION
+ Racc_Runtime_Core_Version_R = ::Racc::VERSION
+
+ begin
+ if Object.const_defined?(:RUBY_ENGINE) and RUBY_ENGINE == 'jruby'
+ require 'jruby'
+ require 'racc/cparse-jruby.jar'
+ com.headius.racc.Cparse.new.load(JRuby.runtime, false)
+ else
+ require 'racc/cparse'
+ end
+
+ unless new.respond_to?(:_racc_do_parse_c, true)
+ raise LoadError, 'old cparse.so'
+ end
+ if Racc_No_Extensions
+ raise LoadError, 'selecting ruby version of racc runtime core'
+ end
+
+ Racc_Main_Parsing_Routine = :_racc_do_parse_c # :nodoc:
+ Racc_YY_Parse_Method = :_racc_yyparse_c # :nodoc:
+ Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_C # :nodoc:
+ Racc_Runtime_Type = 'c' # :nodoc:
+ rescue LoadError
+ Racc_Main_Parsing_Routine = :_racc_do_parse_rb
+ Racc_YY_Parse_Method = :_racc_yyparse_rb
+ Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_R
+ Racc_Runtime_Type = 'ruby'
+ end
+
+ def Parser.racc_runtime_type # :nodoc:
+ Racc_Runtime_Type
+ end
+
+ def _racc_setup
+ @yydebug = false unless self.class::Racc_debug_parser
+ @yydebug = false unless defined?(@yydebug)
+ if @yydebug
+ @racc_debug_out = $stderr unless defined?(@racc_debug_out)
+ @racc_debug_out ||= $stderr
+ end
+ arg = self.class::Racc_arg
+ arg[13] = true if arg.size < 14
+ arg
+ end
+
+ def _racc_init_sysvars
+ @racc_state = [0]
+ @racc_tstack = []
+ @racc_vstack = []
+
+ @racc_t = nil
+ @racc_val = nil
+
+ @racc_read_next = true
+
+ @racc_user_yyerror = false
+ @racc_error_status = 0
+ end
+
+ # The entry point of the parser. This method is used with #next_token.
+ # If Racc wants to get token (and its value), calls next_token.
+ #
+ # Example:
+ # def parse
+ # @q = [[1,1],
+ # [2,2],
+ # [3,3],
+ # [false, '$']]
+ # do_parse
+ # end
+ #
+ # def next_token
+ # @q.shift
+ # end
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
+ def do_parse
+ #{Racc_Main_Parsing_Routine}(_racc_setup(), false)
+ end
+ RUBY
+
+ # The method to fetch next token.
+ # If you use #do_parse method, you must implement #next_token.
+ #
+ # The format of return value is [TOKEN_SYMBOL, VALUE].
+ # +token-symbol+ is represented by Ruby's symbol by default, e.g. :IDENT
+ # for 'IDENT'. ";" (String) for ';'.
+ #
+ # The final symbol (End of file) must be false.
+ def next_token
+ raise NotImplementedError, "#{self.class}\#next_token is not defined"
+ end
+
+ def _racc_do_parse_rb(arg, in_debug)
+ action_table, action_check, action_default, action_pointer,
+ _, _, _, _,
+ _, _, token_table, * = arg
+
+ _racc_init_sysvars
+ tok = act = i = nil
+
+ catch(:racc_end_parse) {
+ while true
+ if i = action_pointer[@racc_state[-1]]
+ if @racc_read_next
+ if @racc_t != 0 # not EOF
+ tok, @racc_val = next_token()
+ unless tok # EOF
+ @racc_t = 0
+ else
+ @racc_t = (token_table[tok] or 1) # error token
+ end
+ racc_read_token(@racc_t, tok, @racc_val) if @yydebug
+ @racc_read_next = false
+ end
+ end
+ i += @racc_t
+ unless i >= 0 and
+ act = action_table[i] and
+ action_check[i] == @racc_state[-1]
+ act = action_default[@racc_state[-1]]
+ end
+ else
+ act = action_default[@racc_state[-1]]
+ end
+ while act = _racc_evalact(act, arg)
+ ;
+ end
+ end
+ }
+ end
+
+ # Another entry point for the parser.
+ # If you use this method, you must implement RECEIVER#METHOD_ID method.
+ #
+ # RECEIVER#METHOD_ID is a method to get next token.
+ # It must 'yield' the token, which format is [TOKEN-SYMBOL, VALUE].
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
+ def yyparse(recv, mid)
+ #{Racc_YY_Parse_Method}(recv, mid, _racc_setup(), false)
+ end
+ RUBY
+
+ def _racc_yyparse_rb(recv, mid, arg, c_debug)
+ action_table, action_check, action_default, action_pointer,
+ _, _, _, _,
+ _, _, token_table, * = arg
+
+ _racc_init_sysvars
+
+ catch(:racc_end_parse) {
+ until i = action_pointer[@racc_state[-1]]
+ while act = _racc_evalact(action_default[@racc_state[-1]], arg)
+ ;
+ end
+ end
+ recv.__send__(mid) do |tok, val|
+ unless tok
+ @racc_t = 0
+ else
+ @racc_t = (token_table[tok] or 1) # error token
+ end
+ @racc_val = val
+ @racc_read_next = false
+
+ i += @racc_t
+ unless i >= 0 and
+ act = action_table[i] and
+ action_check[i] == @racc_state[-1]
+ act = action_default[@racc_state[-1]]
+ end
+ while act = _racc_evalact(act, arg)
+ ;
+ end
+
+ while !(i = action_pointer[@racc_state[-1]]) ||
+ ! @racc_read_next ||
+ @racc_t == 0 # $
+ unless i and i += @racc_t and
+ i >= 0 and
+ act = action_table[i] and
+ action_check[i] == @racc_state[-1]
+ act = action_default[@racc_state[-1]]
+ end
+ while act = _racc_evalact(act, arg)
+ ;
+ end
+ end
+ end
+ }
+ end
+
+ ###
+ ### common
+ ###
+
+ def _racc_evalact(act, arg)
+ action_table, action_check, _, action_pointer,
+ _, _, _, _,
+ _, _, _, shift_n,
+ reduce_n, * = arg
+ nerr = 0 # tmp
+
+ if act > 0 and act < shift_n
+ #
+ # shift
+ #
+ if @racc_error_status > 0
+ @racc_error_status -= 1 unless @racc_t <= 1 # error token or EOF
+ end
+ @racc_vstack.push @racc_val
+ @racc_state.push act
+ @racc_read_next = true
+ if @yydebug
+ @racc_tstack.push @racc_t
+ racc_shift @racc_t, @racc_tstack, @racc_vstack
+ end
+
+ elsif act < 0 and act > -reduce_n
+ #
+ # reduce
+ #
+ code = catch(:racc_jump) {
+ @racc_state.push _racc_do_reduce(arg, act)
+ false
+ }
+ if code
+ case code
+ when 1 # yyerror
+ @racc_user_yyerror = true # user_yyerror
+ return -reduce_n
+ when 2 # yyaccept
+ return shift_n
+ else
+ raise '[Racc Bug] unknown jump code'
+ end
+ end
+
+ elsif act == shift_n
+ #
+ # accept
+ #
+ racc_accept if @yydebug
+ throw :racc_end_parse, @racc_vstack[0]
+
+ elsif act == -reduce_n
+ #
+ # error
+ #
+ case @racc_error_status
+ when 0
+ unless arg[21] # user_yyerror
+ nerr += 1
+ on_error @racc_t, @racc_val, @racc_vstack
+ end
+ when 3
+ if @racc_t == 0 # is $
+ # We're at EOF, and another error occurred immediately after
+ # attempting auto-recovery
+ throw :racc_end_parse, nil
+ end
+ @racc_read_next = true
+ end
+ @racc_user_yyerror = false
+ @racc_error_status = 3
+ while true
+ if i = action_pointer[@racc_state[-1]]
+ i += 1 # error token
+ if i >= 0 and
+ (act = action_table[i]) and
+ action_check[i] == @racc_state[-1]
+ break
+ end
+ end
+ throw :racc_end_parse, nil if @racc_state.size <= 1
+ @racc_state.pop
+ @racc_vstack.pop
+ if @yydebug
+ @racc_tstack.pop
+ racc_e_pop @racc_state, @racc_tstack, @racc_vstack
+ end
+ end
+ return act
+
+ else
+ raise "[Racc Bug] unknown action #{act.inspect}"
+ end
+
+ racc_next_state(@racc_state[-1], @racc_state) if @yydebug
+
+ nil
+ end
+
+ def _racc_do_reduce(arg, act)
+ _, _, _, _,
+ goto_table, goto_check, goto_default, goto_pointer,
+ nt_base, reduce_table, _, _,
+ _, use_result, * = arg
+
+ state = @racc_state
+ vstack = @racc_vstack
+ tstack = @racc_tstack
+
+ i = act * -3
+ len = reduce_table[i]
+ reduce_to = reduce_table[i+1]
+ method_id = reduce_table[i+2]
+ void_array = []
+
+ tmp_t = tstack[-len, len] if @yydebug
+ tmp_v = vstack[-len, len]
+ tstack[-len, len] = void_array if @yydebug
+ vstack[-len, len] = void_array
+ state[-len, len] = void_array
+
+ # tstack must be updated AFTER method call
+ if use_result
+ vstack.push __send__(method_id, tmp_v, vstack, tmp_v[0])
+ else
+ vstack.push __send__(method_id, tmp_v, vstack)
+ end
+ tstack.push reduce_to
+
+ racc_reduce(tmp_t, reduce_to, tstack, vstack) if @yydebug
+
+ k1 = reduce_to - nt_base
+ if i = goto_pointer[k1]
+ i += state[-1]
+ if i >= 0 and (curstate = goto_table[i]) and goto_check[i] == k1
+ return curstate
+ end
+ end
+ goto_default[k1]
+ end
+
+ # This method is called when a parse error is found.
+ #
+ # ERROR_TOKEN_ID is an internal ID of token which caused error.
+ # You can get string representation of this ID by calling
+ # #token_to_str.
+ #
+ # ERROR_VALUE is a value of error token.
+ #
+ # value_stack is a stack of symbol values.
+ # DO NOT MODIFY this object.
+ #
+ # This method raises ParseError by default.
+ #
+ # If this method returns, parsers enter "error recovering mode".
+ def on_error(t, val, vstack)
+ raise ParseError, sprintf("parse error on value %s (%s)",
+ val.inspect, token_to_str(t) || '?')
+ end
+
+ # Enter error recovering mode.
+ # This method does not call #on_error.
+ def yyerror
+ throw :racc_jump, 1
+ end
+
+ # Exit parser.
+ # Return value is +Symbol_Value_Stack[0]+.
+ def yyaccept
+ throw :racc_jump, 2
+ end
+
+ # Leave error recovering mode.
+ def yyerrok
+ @racc_error_status = 0
+ end
+
+ # For debugging output
+ def racc_read_token(t, tok, val)
+ @racc_debug_out.print 'read '
+ @racc_debug_out.print tok.inspect, '(', racc_token2str(t), ') '
+ @racc_debug_out.puts val.inspect
+ @racc_debug_out.puts
+ end
+
+ def racc_shift(tok, tstack, vstack)
+ @racc_debug_out.puts "shift #{racc_token2str tok}"
+ racc_print_stacks tstack, vstack
+ @racc_debug_out.puts
+ end
+
+ def racc_reduce(toks, sim, tstack, vstack)
+ out = @racc_debug_out
+ out.print 'reduce '
+ if toks.empty?
+ out.print ' <none>'
+ else
+ toks.each {|t| out.print ' ', racc_token2str(t) }
+ end
+ out.puts " --> #{racc_token2str(sim)}"
+ racc_print_stacks tstack, vstack
+ @racc_debug_out.puts
+ end
+
+ def racc_accept
+ @racc_debug_out.puts 'accept'
+ @racc_debug_out.puts
+ end
+
+ def racc_e_pop(state, tstack, vstack)
+ @racc_debug_out.puts 'error recovering mode: pop token'
+ racc_print_states state
+ racc_print_stacks tstack, vstack
+ @racc_debug_out.puts
+ end
+
+ def racc_next_state(curstate, state)
+ @racc_debug_out.puts "goto #{curstate}"
+ racc_print_states state
+ @racc_debug_out.puts
+ end
+
+ def racc_print_stacks(t, v)
+ out = @racc_debug_out
+ out.print ' ['
+ t.each_index do |i|
+ out.print ' (', racc_token2str(t[i]), ' ', v[i].inspect, ')'
+ end
+ out.puts ' ]'
+ end
+
+ def racc_print_states(s)
+ out = @racc_debug_out
+ out.print ' ['
+ s.each {|st| out.print ' ', st }
+ out.puts ' ]'
+ end
+
+ def racc_token2str(tok)
+ self.class::Racc_token_to_s_table[tok] or
+ raise "[Racc Bug] can't convert token #{tok} to string"
+ end
+
+ # Convert internal ID of token symbol to the string.
+ def token_to_str(t)
+ self.class::Racc_token_to_s_table[t]
+ end
+
+ end
+
+end
+
+end
+###### racc/parser.rb end
class RDoc::RD
diff --git a/lib/rdoc/rd/inline_parser.rb b/lib/rdoc/rd/inline_parser.rb
index 0f0becefef..adacf64d5b 100644
--- a/lib/rdoc/rd/inline_parser.rb
+++ b/lib/rdoc/rd/inline_parser.rb
@@ -1,11 +1,659 @@
# frozen_string_literal: true
#
# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.6.2
-# from Racc grammar file "".
+# This file is automatically generated by Racc 1.7.3
+# from Racc grammar file "inline_parser.ry".
#
-require 'racc/parser.rb'
+###### racc/parser.rb begin
+unless $".find {|p| p.end_with?('/racc/parser.rb')}
+$".push "#{__dir__}/racc/parser.rb"
+#--
+# Copyright (c) 1999-2006 Minero Aoki
+#
+# This program is free software.
+# You can distribute/modify this program under the same terms of ruby.
+#
+# As a special exception, when this code is copied by Racc
+# into a Racc output file, you may use that output file
+# without restriction.
+#++
+
+unless $".find {|p| p.end_with?('/racc/info.rb')}
+$".push "#{__dir__}/racc/info.rb"
+
+module Racc
+ VERSION = '1.7.3'
+ Version = VERSION
+ Copyright = 'Copyright (c) 1999-2006 Minero Aoki'
+end
+
+end
+
+
+unless defined?(NotImplementedError)
+ NotImplementedError = NotImplementError # :nodoc:
+end
+
+module Racc
+ class ParseError < StandardError; end
+end
+unless defined?(::ParseError)
+ ParseError = Racc::ParseError # :nodoc:
+end
+
+# Racc is a LALR(1) parser generator.
+# It is written in Ruby itself, and generates Ruby programs.
+#
+# == Command-line Reference
+#
+# racc [-o<var>filename</var>] [--output-file=<var>filename</var>]
+# [-e<var>rubypath</var>] [--executable=<var>rubypath</var>]
+# [-v] [--verbose]
+# [-O<var>filename</var>] [--log-file=<var>filename</var>]
+# [-g] [--debug]
+# [-E] [--embedded]
+# [-l] [--no-line-convert]
+# [-c] [--line-convert-all]
+# [-a] [--no-omit-actions]
+# [-C] [--check-only]
+# [-S] [--output-status]
+# [--version] [--copyright] [--help] <var>grammarfile</var>
+#
+# [+grammarfile+]
+# Racc grammar file. Any extension is permitted.
+# [-o+outfile+, --output-file=+outfile+]
+# A filename for output. default is <+filename+>.tab.rb
+# [-O+filename+, --log-file=+filename+]
+# Place logging output in file +filename+.
+# Default log file name is <+filename+>.output.
+# [-e+rubypath+, --executable=+rubypath+]
+# output executable file(mode 755). where +path+ is the Ruby interpreter.
+# [-v, --verbose]
+# verbose mode. create +filename+.output file, like yacc's y.output file.
+# [-g, --debug]
+# add debug code to parser class. To display debugging information,
+# use this '-g' option and set @yydebug true in parser class.
+# [-E, --embedded]
+# Output parser which doesn't need runtime files (racc/parser.rb).
+# [-F, --frozen]
+# Output parser which declares frozen_string_literals: true
+# [-C, --check-only]
+# Check syntax of racc grammar file and quit.
+# [-S, --output-status]
+# Print messages time to time while compiling.
+# [-l, --no-line-convert]
+# turns off line number converting.
+# [-c, --line-convert-all]
+# Convert line number of actions, inner, header and footer.
+# [-a, --no-omit-actions]
+# Call all actions, even if an action is empty.
+# [--version]
+# print Racc version and quit.
+# [--copyright]
+# Print copyright and quit.
+# [--help]
+# Print usage and quit.
+#
+# == Generating Parser Using Racc
+#
+# To compile Racc grammar file, simply type:
+#
+# $ racc parse.y
+#
+# This creates Ruby script file "parse.tab.y". The -o option can change the output filename.
+#
+# == Writing A Racc Grammar File
+#
+# If you want your own parser, you have to write a grammar file.
+# A grammar file contains the name of your parser class, grammar for the parser,
+# user code, and anything else.
+# When writing a grammar file, yacc's knowledge is helpful.
+# If you have not used yacc before, Racc is not too difficult.
+#
+# Here's an example Racc grammar file.
+#
+# class Calcparser
+# rule
+# target: exp { print val[0] }
+#
+# exp: exp '+' exp
+# | exp '*' exp
+# | '(' exp ')'
+# | NUMBER
+# end
+#
+# Racc grammar files resemble yacc files.
+# But (of course), this is Ruby code.
+# yacc's $$ is the 'result', $0, $1... is
+# an array called 'val', and $-1, $-2... is an array called '_values'.
+#
+# See the {Grammar File Reference}[rdoc-ref:lib/racc/rdoc/grammar.en.rdoc] for
+# more information on grammar files.
+#
+# == Parser
+#
+# Then you must prepare the parse entry method. There are two types of
+# parse methods in Racc, Racc::Parser#do_parse and Racc::Parser#yyparse
+#
+# Racc::Parser#do_parse is simple.
+#
+# It's yyparse() of yacc, and Racc::Parser#next_token is yylex().
+# This method must returns an array like [TOKENSYMBOL, ITS_VALUE].
+# EOF is [false, false].
+# (TOKENSYMBOL is a Ruby symbol (taken from String#intern) by default.
+# If you want to change this, see the grammar reference.
+#
+# Racc::Parser#yyparse is little complicated, but useful.
+# It does not use Racc::Parser#next_token, instead it gets tokens from any iterator.
+#
+# For example, <code>yyparse(obj, :scan)</code> causes
+# calling +obj#scan+, and you can return tokens by yielding them from +obj#scan+.
+#
+# == Debugging
+#
+# When debugging, "-v" or/and the "-g" option is helpful.
+#
+# "-v" creates verbose log file (.output).
+# "-g" creates a "Verbose Parser".
+# Verbose Parser prints the internal status when parsing.
+# But it's _not_ automatic.
+# You must use -g option and set +@yydebug+ to +true+ in order to get output.
+# -g option only creates the verbose parser.
+#
+# === Racc reported syntax error.
+#
+# Isn't there too many "end"?
+# grammar of racc file is changed in v0.10.
+#
+# Racc does not use '%' mark, while yacc uses huge number of '%' marks..
+#
+# === Racc reported "XXXX conflicts".
+#
+# Try "racc -v xxxx.y".
+# It causes producing racc's internal log file, xxxx.output.
+#
+# === Generated parsers does not work correctly
+#
+# Try "racc -g xxxx.y".
+# This command let racc generate "debugging parser".
+# Then set @yydebug=true in your parser.
+# It produces a working log of your parser.
+#
+# == Re-distributing Racc runtime
+#
+# A parser, which is created by Racc, requires the Racc runtime module;
+# racc/parser.rb.
+#
+# Ruby 1.8.x comes with Racc runtime module,
+# you need NOT distribute Racc runtime files.
+#
+# If you want to include the Racc runtime module with your parser.
+# This can be done by using '-E' option:
+#
+# $ racc -E -omyparser.rb myparser.y
+#
+# This command creates myparser.rb which `includes' Racc runtime.
+# Only you must do is to distribute your parser file (myparser.rb).
+#
+# Note: parser.rb is ruby license, but your parser is not.
+# Your own parser is completely yours.
+module Racc
+
+ unless defined?(Racc_No_Extensions)
+ Racc_No_Extensions = false # :nodoc:
+ end
+
+ class Parser
+
+ Racc_Runtime_Version = ::Racc::VERSION
+ Racc_Runtime_Core_Version_R = ::Racc::VERSION
+
+ begin
+ if Object.const_defined?(:RUBY_ENGINE) and RUBY_ENGINE == 'jruby'
+ require 'jruby'
+ require 'racc/cparse-jruby.jar'
+ com.headius.racc.Cparse.new.load(JRuby.runtime, false)
+ else
+ require 'racc/cparse'
+ end
+
+ unless new.respond_to?(:_racc_do_parse_c, true)
+ raise LoadError, 'old cparse.so'
+ end
+ if Racc_No_Extensions
+ raise LoadError, 'selecting ruby version of racc runtime core'
+ end
+
+ Racc_Main_Parsing_Routine = :_racc_do_parse_c # :nodoc:
+ Racc_YY_Parse_Method = :_racc_yyparse_c # :nodoc:
+ Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_C # :nodoc:
+ Racc_Runtime_Type = 'c' # :nodoc:
+ rescue LoadError
+ Racc_Main_Parsing_Routine = :_racc_do_parse_rb
+ Racc_YY_Parse_Method = :_racc_yyparse_rb
+ Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_R
+ Racc_Runtime_Type = 'ruby'
+ end
+
+ def Parser.racc_runtime_type # :nodoc:
+ Racc_Runtime_Type
+ end
+
+ def _racc_setup
+ @yydebug = false unless self.class::Racc_debug_parser
+ @yydebug = false unless defined?(@yydebug)
+ if @yydebug
+ @racc_debug_out = $stderr unless defined?(@racc_debug_out)
+ @racc_debug_out ||= $stderr
+ end
+ arg = self.class::Racc_arg
+ arg[13] = true if arg.size < 14
+ arg
+ end
+
+ def _racc_init_sysvars
+ @racc_state = [0]
+ @racc_tstack = []
+ @racc_vstack = []
+
+ @racc_t = nil
+ @racc_val = nil
+
+ @racc_read_next = true
+
+ @racc_user_yyerror = false
+ @racc_error_status = 0
+ end
+
+ # The entry point of the parser. This method is used with #next_token.
+ # If Racc wants to get token (and its value), calls next_token.
+ #
+ # Example:
+ # def parse
+ # @q = [[1,1],
+ # [2,2],
+ # [3,3],
+ # [false, '$']]
+ # do_parse
+ # end
+ #
+ # def next_token
+ # @q.shift
+ # end
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
+ def do_parse
+ #{Racc_Main_Parsing_Routine}(_racc_setup(), false)
+ end
+ RUBY
+
+ # The method to fetch next token.
+ # If you use #do_parse method, you must implement #next_token.
+ #
+ # The format of return value is [TOKEN_SYMBOL, VALUE].
+ # +token-symbol+ is represented by Ruby's symbol by default, e.g. :IDENT
+ # for 'IDENT'. ";" (String) for ';'.
+ #
+ # The final symbol (End of file) must be false.
+ def next_token
+ raise NotImplementedError, "#{self.class}\#next_token is not defined"
+ end
+
+ def _racc_do_parse_rb(arg, in_debug)
+ action_table, action_check, action_default, action_pointer,
+ _, _, _, _,
+ _, _, token_table, * = arg
+
+ _racc_init_sysvars
+ tok = act = i = nil
+
+ catch(:racc_end_parse) {
+ while true
+ if i = action_pointer[@racc_state[-1]]
+ if @racc_read_next
+ if @racc_t != 0 # not EOF
+ tok, @racc_val = next_token()
+ unless tok # EOF
+ @racc_t = 0
+ else
+ @racc_t = (token_table[tok] or 1) # error token
+ end
+ racc_read_token(@racc_t, tok, @racc_val) if @yydebug
+ @racc_read_next = false
+ end
+ end
+ i += @racc_t
+ unless i >= 0 and
+ act = action_table[i] and
+ action_check[i] == @racc_state[-1]
+ act = action_default[@racc_state[-1]]
+ end
+ else
+ act = action_default[@racc_state[-1]]
+ end
+ while act = _racc_evalact(act, arg)
+ ;
+ end
+ end
+ }
+ end
+
+ # Another entry point for the parser.
+ # If you use this method, you must implement RECEIVER#METHOD_ID method.
+ #
+ # RECEIVER#METHOD_ID is a method to get next token.
+ # It must 'yield' the token, which format is [TOKEN-SYMBOL, VALUE].
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
+ def yyparse(recv, mid)
+ #{Racc_YY_Parse_Method}(recv, mid, _racc_setup(), false)
+ end
+ RUBY
+
+ def _racc_yyparse_rb(recv, mid, arg, c_debug)
+ action_table, action_check, action_default, action_pointer,
+ _, _, _, _,
+ _, _, token_table, * = arg
+
+ _racc_init_sysvars
+
+ catch(:racc_end_parse) {
+ until i = action_pointer[@racc_state[-1]]
+ while act = _racc_evalact(action_default[@racc_state[-1]], arg)
+ ;
+ end
+ end
+ recv.__send__(mid) do |tok, val|
+ unless tok
+ @racc_t = 0
+ else
+ @racc_t = (token_table[tok] or 1) # error token
+ end
+ @racc_val = val
+ @racc_read_next = false
+
+ i += @racc_t
+ unless i >= 0 and
+ act = action_table[i] and
+ action_check[i] == @racc_state[-1]
+ act = action_default[@racc_state[-1]]
+ end
+ while act = _racc_evalact(act, arg)
+ ;
+ end
+
+ while !(i = action_pointer[@racc_state[-1]]) ||
+ ! @racc_read_next ||
+ @racc_t == 0 # $
+ unless i and i += @racc_t and
+ i >= 0 and
+ act = action_table[i] and
+ action_check[i] == @racc_state[-1]
+ act = action_default[@racc_state[-1]]
+ end
+ while act = _racc_evalact(act, arg)
+ ;
+ end
+ end
+ end
+ }
+ end
+
+ ###
+ ### common
+ ###
+
+ def _racc_evalact(act, arg)
+ action_table, action_check, _, action_pointer,
+ _, _, _, _,
+ _, _, _, shift_n,
+ reduce_n, * = arg
+ nerr = 0 # tmp
+
+ if act > 0 and act < shift_n
+ #
+ # shift
+ #
+ if @racc_error_status > 0
+ @racc_error_status -= 1 unless @racc_t <= 1 # error token or EOF
+ end
+ @racc_vstack.push @racc_val
+ @racc_state.push act
+ @racc_read_next = true
+ if @yydebug
+ @racc_tstack.push @racc_t
+ racc_shift @racc_t, @racc_tstack, @racc_vstack
+ end
+
+ elsif act < 0 and act > -reduce_n
+ #
+ # reduce
+ #
+ code = catch(:racc_jump) {
+ @racc_state.push _racc_do_reduce(arg, act)
+ false
+ }
+ if code
+ case code
+ when 1 # yyerror
+ @racc_user_yyerror = true # user_yyerror
+ return -reduce_n
+ when 2 # yyaccept
+ return shift_n
+ else
+ raise '[Racc Bug] unknown jump code'
+ end
+ end
+
+ elsif act == shift_n
+ #
+ # accept
+ #
+ racc_accept if @yydebug
+ throw :racc_end_parse, @racc_vstack[0]
+
+ elsif act == -reduce_n
+ #
+ # error
+ #
+ case @racc_error_status
+ when 0
+ unless arg[21] # user_yyerror
+ nerr += 1
+ on_error @racc_t, @racc_val, @racc_vstack
+ end
+ when 3
+ if @racc_t == 0 # is $
+ # We're at EOF, and another error occurred immediately after
+ # attempting auto-recovery
+ throw :racc_end_parse, nil
+ end
+ @racc_read_next = true
+ end
+ @racc_user_yyerror = false
+ @racc_error_status = 3
+ while true
+ if i = action_pointer[@racc_state[-1]]
+ i += 1 # error token
+ if i >= 0 and
+ (act = action_table[i]) and
+ action_check[i] == @racc_state[-1]
+ break
+ end
+ end
+ throw :racc_end_parse, nil if @racc_state.size <= 1
+ @racc_state.pop
+ @racc_vstack.pop
+ if @yydebug
+ @racc_tstack.pop
+ racc_e_pop @racc_state, @racc_tstack, @racc_vstack
+ end
+ end
+ return act
+
+ else
+ raise "[Racc Bug] unknown action #{act.inspect}"
+ end
+
+ racc_next_state(@racc_state[-1], @racc_state) if @yydebug
+
+ nil
+ end
+
+ def _racc_do_reduce(arg, act)
+ _, _, _, _,
+ goto_table, goto_check, goto_default, goto_pointer,
+ nt_base, reduce_table, _, _,
+ _, use_result, * = arg
+
+ state = @racc_state
+ vstack = @racc_vstack
+ tstack = @racc_tstack
+
+ i = act * -3
+ len = reduce_table[i]
+ reduce_to = reduce_table[i+1]
+ method_id = reduce_table[i+2]
+ void_array = []
+
+ tmp_t = tstack[-len, len] if @yydebug
+ tmp_v = vstack[-len, len]
+ tstack[-len, len] = void_array if @yydebug
+ vstack[-len, len] = void_array
+ state[-len, len] = void_array
+
+ # tstack must be updated AFTER method call
+ if use_result
+ vstack.push __send__(method_id, tmp_v, vstack, tmp_v[0])
+ else
+ vstack.push __send__(method_id, tmp_v, vstack)
+ end
+ tstack.push reduce_to
+
+ racc_reduce(tmp_t, reduce_to, tstack, vstack) if @yydebug
+
+ k1 = reduce_to - nt_base
+ if i = goto_pointer[k1]
+ i += state[-1]
+ if i >= 0 and (curstate = goto_table[i]) and goto_check[i] == k1
+ return curstate
+ end
+ end
+ goto_default[k1]
+ end
+
+ # This method is called when a parse error is found.
+ #
+ # ERROR_TOKEN_ID is an internal ID of token which caused error.
+ # You can get string representation of this ID by calling
+ # #token_to_str.
+ #
+ # ERROR_VALUE is a value of error token.
+ #
+ # value_stack is a stack of symbol values.
+ # DO NOT MODIFY this object.
+ #
+ # This method raises ParseError by default.
+ #
+ # If this method returns, parsers enter "error recovering mode".
+ def on_error(t, val, vstack)
+ raise ParseError, sprintf("parse error on value %s (%s)",
+ val.inspect, token_to_str(t) || '?')
+ end
+
+ # Enter error recovering mode.
+ # This method does not call #on_error.
+ def yyerror
+ throw :racc_jump, 1
+ end
+
+ # Exit parser.
+ # Return value is +Symbol_Value_Stack[0]+.
+ def yyaccept
+ throw :racc_jump, 2
+ end
+
+ # Leave error recovering mode.
+ def yyerrok
+ @racc_error_status = 0
+ end
+
+ # For debugging output
+ def racc_read_token(t, tok, val)
+ @racc_debug_out.print 'read '
+ @racc_debug_out.print tok.inspect, '(', racc_token2str(t), ') '
+ @racc_debug_out.puts val.inspect
+ @racc_debug_out.puts
+ end
+
+ def racc_shift(tok, tstack, vstack)
+ @racc_debug_out.puts "shift #{racc_token2str tok}"
+ racc_print_stacks tstack, vstack
+ @racc_debug_out.puts
+ end
+
+ def racc_reduce(toks, sim, tstack, vstack)
+ out = @racc_debug_out
+ out.print 'reduce '
+ if toks.empty?
+ out.print ' <none>'
+ else
+ toks.each {|t| out.print ' ', racc_token2str(t) }
+ end
+ out.puts " --> #{racc_token2str(sim)}"
+ racc_print_stacks tstack, vstack
+ @racc_debug_out.puts
+ end
+
+ def racc_accept
+ @racc_debug_out.puts 'accept'
+ @racc_debug_out.puts
+ end
+
+ def racc_e_pop(state, tstack, vstack)
+ @racc_debug_out.puts 'error recovering mode: pop token'
+ racc_print_states state
+ racc_print_stacks tstack, vstack
+ @racc_debug_out.puts
+ end
+
+ def racc_next_state(curstate, state)
+ @racc_debug_out.puts "goto #{curstate}"
+ racc_print_states state
+ @racc_debug_out.puts
+ end
+
+ def racc_print_stacks(t, v)
+ out = @racc_debug_out
+ out.print ' ['
+ t.each_index do |i|
+ out.print ' (', racc_token2str(t[i]), ' ', v[i].inspect, ')'
+ end
+ out.puts ' ]'
+ end
+
+ def racc_print_states(s)
+ out = @racc_debug_out
+ out.print ' ['
+ s.each {|st| out.print ' ', st }
+ out.puts ' ]'
+ end
+
+ def racc_token2str(tok)
+ self.class::Racc_token_to_s_table[tok] or
+ raise "[Racc Bug] can't convert token #{tok} to string"
+ end
+
+ # Convert internal ID of token symbol to the string.
+ def token_to_str(t)
+ self.class::Racc_token_to_s_table[t]
+ end
+
+ end
+
+end
+
+end
+###### racc/parser.rb end
require 'strscan'
diff --git a/lib/rdoc/rdoc.gemspec b/lib/rdoc/rdoc.gemspec
index 9e925832ea..93a281c8ae 100644
--- a/lib/rdoc/rdoc.gemspec
+++ b/lib/rdoc/rdoc.gemspec
@@ -28,6 +28,10 @@ RDoc includes the +rdoc+ and +ri+ tools for generating and displaying documentat
s.homepage = "https://ruby.github.io/rdoc"
s.licenses = ["Ruby"]
+ s.metadata["homepage_uri"] = s.homepage
+ s.metadata["source_code_uri"] = "https://github.com/ruby/rdoc"
+ s.metadata["changelog_uri"] = "#{s.metadata["source_code_uri"]}/releases"
+
s.bindir = "exe"
s.executables = ["rdoc", "ri"]
s.require_paths = ["lib"]
diff --git a/lib/rdoc/ri/driver.rb b/lib/rdoc/ri/driver.rb
index 819cff8aa3..64783dc163 100644
--- a/lib/rdoc/ri/driver.rb
+++ b/lib/rdoc/ri/driver.rb
@@ -34,9 +34,9 @@ class RDoc::RI::Driver
class NotFoundError < Error
- def initialize(klass, suggestions = nil) # :nodoc:
+ def initialize(klass, suggestion_proc = nil) # :nodoc:
@klass = klass
- @suggestions = suggestions
+ @suggestion_proc = suggestion_proc
end
##
@@ -48,8 +48,9 @@ class RDoc::RI::Driver
def message # :nodoc:
str = "Nothing known about #{@klass}"
- if @suggestions and !@suggestions.empty?
- str += "\nDid you mean? #{@suggestions.join("\n ")}"
+ suggestions = @suggestion_proc&.call
+ if suggestions and !suggestions.empty?
+ str += "\nDid you mean? #{suggestions.join("\n ")}"
end
str
end
@@ -948,8 +949,8 @@ or the PAGER environment variable.
ary = class_names.grep(Regexp.new("\\A#{klass.gsub(/(?=::|\z)/, '[^:]*')}\\z"))
if ary.length != 1 && ary.first != klass
if check_did_you_mean
- suggestions = DidYouMean::SpellChecker.new(dictionary: class_names).correct(klass)
- raise NotFoundError.new(klass, suggestions)
+ suggestion_proc = -> { DidYouMean::SpellChecker.new(dictionary: class_names).correct(klass) }
+ raise NotFoundError.new(klass, suggestion_proc)
else
raise NotFoundError, klass
end
@@ -1087,7 +1088,7 @@ or the PAGER environment variable.
loop do
name = if defined? Readline then
- Readline.readline ">> "
+ Readline.readline ">> ", true
else
print ">> "
$stdin.gets
@@ -1237,8 +1238,8 @@ or the PAGER environment variable.
methods.push(*store.instance_methods[klass]) if [:instance, :both].include? types
end
methods = methods.uniq
- suggestions = DidYouMean::SpellChecker.new(dictionary: methods).correct(method_name)
- raise NotFoundError.new(name, suggestions)
+ suggestion_proc = -> { DidYouMean::SpellChecker.new(dictionary: methods).correct(method_name) }
+ raise NotFoundError.new(name, suggestion_proc)
else
raise NotFoundError, name
end
diff --git a/lib/rdoc/store.rb b/lib/rdoc/store.rb
index 9fc540d317..cd27d47dd1 100644
--- a/lib/rdoc/store.rb
+++ b/lib/rdoc/store.rb
@@ -197,6 +197,9 @@ class RDoc::Store
top_level
end
+ ##
+ # Sets the parser of +absolute_name+, unless it from a source code file.
+
def update_parser_of_file(absolute_name, parser)
if top_level = @files_hash[absolute_name] then
@text_files_hash[absolute_name] = top_level if top_level.text?
@@ -556,9 +559,7 @@ class RDoc::Store
def load_cache
#orig_enc = @encoding
- File.open cache_path, 'rb' do |io|
- @cache = Marshal.load io
- end
+ @cache = marshal_load(cache_path)
load_enc = @cache[:encoding]
@@ -615,9 +616,7 @@ class RDoc::Store
def load_class_data klass_name
file = class_file klass_name
- File.open file, 'rb' do |io|
- Marshal.load io
- end
+ marshal_load(file)
rescue Errno::ENOENT => e
error = MissingFileError.new(self, file, klass_name)
error.set_backtrace e.backtrace
@@ -630,14 +629,10 @@ class RDoc::Store
def load_method klass_name, method_name
file = method_file klass_name, method_name
- File.open file, 'rb' do |io|
- obj = Marshal.load io
- obj.store = self
- obj.parent =
- find_class_or_module(klass_name) || load_class(klass_name) unless
- obj.parent
- obj
- end
+ obj = marshal_load(file)
+ obj.store = self
+ obj.parent ||= find_class_or_module(klass_name) || load_class(klass_name)
+ obj
rescue Errno::ENOENT => e
error = MissingFileError.new(self, file, klass_name + method_name)
error.set_backtrace e.backtrace
@@ -650,11 +645,9 @@ class RDoc::Store
def load_page page_name
file = page_file page_name
- File.open file, 'rb' do |io|
- obj = Marshal.load io
- obj.store = self
- obj
- end
+ obj = marshal_load(file)
+ obj.store = self
+ obj
rescue Errno::ENOENT => e
error = MissingFileError.new(self, file, page_name)
error.set_backtrace e.backtrace
@@ -976,4 +969,21 @@ class RDoc::Store
@unique_modules
end
+ private
+ def marshal_load(file)
+ File.open(file, 'rb') {|io| Marshal.load(io, MarshalFilter)}
+ end
+
+ MarshalFilter = proc do |obj|
+ case obj
+ when true, false, nil, Array, Class, Encoding, Hash, Integer, String, Symbol, RDoc::Text
+ else
+ unless obj.class.name.start_with?("RDoc::")
+ raise TypeError, "not permitted class: #{obj.class.name}"
+ end
+ end
+ obj
+ end
+ private_constant :MarshalFilter
+
end
diff --git a/lib/rdoc/text.rb b/lib/rdoc/text.rb
index 0bc4aba428..9804f81abe 100644
--- a/lib/rdoc/text.rb
+++ b/lib/rdoc/text.rb
@@ -10,6 +10,10 @@ require 'strscan'
module RDoc::Text
+ ##
+ # The language for this text. This affects stripping comments
+ # markers.
+
attr_accessor :language
##
@@ -309,4 +313,10 @@ module RDoc::Text
res.join.strip
end
+ ##
+ # Character class to be separated by a space when concatenating
+ # lines.
+
+ SPACE_SEPARATED_LETTER_CLASS = /[\p{Nd}\p{Lc}\p{Pc}]|[!-~&&\W]/
+
end
diff --git a/lib/rdoc/token_stream.rb b/lib/rdoc/token_stream.rb
index 8fc6eadd85..19ca7ed248 100644
--- a/lib/rdoc/token_stream.rb
+++ b/lib/rdoc/token_stream.rb
@@ -112,7 +112,7 @@ module RDoc::TokenStream
# Returns a string representation of the token stream
def tokens_to_s
- token_stream.compact.map { |token| token[:text] }.join ''
+ (token_stream or return '').compact.map { |token| token[:text] }.join ''
end
end
diff --git a/lib/rdoc/top_level.rb b/lib/rdoc/top_level.rb
index e6caa34ee3..3864f66431 100644
--- a/lib/rdoc/top_level.rb
+++ b/lib/rdoc/top_level.rb
@@ -52,6 +52,9 @@ class RDoc::TopLevel < RDoc::Context
@classes_or_modules = []
end
+ ##
+ # Sets the parser for this toplevel context, also the store.
+
def parser=(val)
@parser = val
@store.update_parser_of_file(absolute_name, val) if @store
diff --git a/lib/rdoc/version.rb b/lib/rdoc/version.rb
index 04322a1970..87842d9847 100644
--- a/lib/rdoc/version.rb
+++ b/lib/rdoc/version.rb
@@ -5,6 +5,6 @@ module RDoc
##
# RDoc version you are using
- VERSION = '6.5.0'
+ VERSION = '6.6.3.1'
end
diff --git a/lib/readline.gemspec b/lib/readline.gemspec
index 3a18f9edb6..9221c29263 100644
--- a/lib/readline.gemspec
+++ b/lib/readline.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = 'readline'
- spec.version = '0.0.3'
+ spec.version = '0.0.4'
spec.authors = ['aycabta']
spec.email = ['aycabta@gmail.com']
diff --git a/lib/readline.rb b/lib/readline.rb
index 29cdf3a14f..d1c9d3a955 100644
--- a/lib/readline.rb
+++ b/lib/readline.rb
@@ -1,5 +1,5 @@
begin
- require 'readline.so'
+ require "readline.#{RbConfig::CONFIG["DLEXT"]}"
rescue LoadError
require 'reline' unless defined? Reline
Object.send(:remove_const, :Readline) if Object.const_defined?(:Readline)
diff --git a/lib/reline.rb b/lib/reline.rb
index fd70ffc9d0..0a266b9c58 100644
--- a/lib/reline.rb
+++ b/lib/reline.rb
@@ -1,5 +1,4 @@
require 'io/console'
-require 'timeout'
require 'forwardable'
require 'reline/version'
require 'reline/config'
@@ -8,6 +7,7 @@ require 'reline/key_stroke'
require 'reline/line_editor'
require 'reline/history'
require 'reline/terminfo'
+require 'reline/face'
require 'rbconfig'
module Reline
@@ -17,7 +17,7 @@ module Reline
class ConfigEncodingConversionError < StandardError; end
- Key = Struct.new('Key', :char, :combined_char, :with_meta) do
+ Key = Struct.new(:char, :combined_char, :with_meta) do
def match?(other)
case other
when Reline::Key
@@ -37,10 +37,8 @@ module Reline
DialogRenderInfo = Struct.new(
:pos,
:contents,
- :bg_color,
- :pointer_bg_color,
- :fg_color,
- :pointer_fg_color,
+ :face,
+ :bg_color, # For the time being, this line should stay here for the compatibility with IRB.
:width,
:height,
:scrollbar,
@@ -77,50 +75,54 @@ module Reline
def initialize
self.output = STDOUT
+ @mutex = Mutex.new
@dialog_proc_list = {}
yield self
@completion_quote_character = nil
- @bracketed_paste_finished = false
+ end
+
+ def io_gate
+ Reline::IOGate
end
def encoding
- Reline::IOGate.encoding
+ io_gate.encoding
end
def completion_append_character=(val)
if val.nil?
@completion_append_character = nil
elsif val.size == 1
- @completion_append_character = val.encode(Reline::IOGate.encoding)
+ @completion_append_character = val.encode(encoding)
elsif val.size > 1
- @completion_append_character = val[0].encode(Reline::IOGate.encoding)
+ @completion_append_character = val[0].encode(encoding)
else
@completion_append_character = nil
end
end
def basic_word_break_characters=(v)
- @basic_word_break_characters = v.encode(Reline::IOGate.encoding)
+ @basic_word_break_characters = v.encode(encoding)
end
def completer_word_break_characters=(v)
- @completer_word_break_characters = v.encode(Reline::IOGate.encoding)
+ @completer_word_break_characters = v.encode(encoding)
end
def basic_quote_characters=(v)
- @basic_quote_characters = v.encode(Reline::IOGate.encoding)
+ @basic_quote_characters = v.encode(encoding)
end
def completer_quote_characters=(v)
- @completer_quote_characters = v.encode(Reline::IOGate.encoding)
+ @completer_quote_characters = v.encode(encoding)
end
def filename_quote_characters=(v)
- @filename_quote_characters = v.encode(Reline::IOGate.encoding)
+ @filename_quote_characters = v.encode(encoding)
end
def special_prefixes=(v)
- @special_prefixes = v.encode(Reline::IOGate.encoding)
+ @special_prefixes = v.encode(encoding)
end
def completion_case_fold=(v)
@@ -181,20 +183,16 @@ module Reline
def input=(val)
raise TypeError unless val.respond_to?(:getc) or val.nil?
- if val.respond_to?(:getc)
- if defined?(Reline::ANSI) and Reline::IOGate == Reline::ANSI
- Reline::ANSI.input = val
- elsif Reline::IOGate == Reline::GeneralIO
- Reline::GeneralIO.input = val
- end
+ if val.respond_to?(:getc) && io_gate.respond_to?(:input=)
+ io_gate.input = val
end
end
def output=(val)
raise TypeError unless val.respond_to?(:write) or val.nil?
@output = val
- if defined?(Reline::ANSI) and Reline::IOGate == Reline::ANSI
- Reline::ANSI.output = val
+ if io_gate.respond_to?(:output=)
+ io_gate.output = val
end
end
@@ -217,37 +215,30 @@ module Reline
end
def get_screen_size
- Reline::IOGate.get_screen_size
+ io_gate.get_screen_size
end
Reline::DEFAULT_DIALOG_PROC_AUTOCOMPLETE = ->() {
# autocomplete
- return nil unless config.autocompletion
- if just_cursor_moving and completion_journey_data.nil?
- # Auto complete starts only when edited
- return nil
- end
- pre, target, post = retrieve_completion_block(true)
- if target.nil? or target.empty? or (completion_journey_data&.pointer == -1 and target.size <= 3)
- return nil
- end
- if completion_journey_data and completion_journey_data.list
- result = completion_journey_data.list.dup
- result.shift
- pointer = completion_journey_data.pointer - 1
- else
- result = call_completion_proc_with_checking_args(pre, target, post)
- pointer = nil
- end
- if result and result.size == 1 and result[0] == target and pointer != 0
- result = nil
- end
+ return unless config.autocompletion
+
+ journey_data = completion_journey_data
+ return unless journey_data
+
+ target = journey_data.list.first
+ completed = journey_data.list[journey_data.pointer]
+ result = journey_data.list.drop(1)
+ pointer = journey_data.pointer - 1
+ return if completed.empty? || (result == [completed] && pointer < 0)
+
target_width = Reline::Unicode.calculate_width(target)
- x = cursor_pos.x - target_width
- if x < 0
- x = screen_width + x
+ completed_width = Reline::Unicode.calculate_width(completed)
+ if cursor_pos.x <= completed_width - target_width
+ # When target is rendered on the line above cursor position
+ x = screen_width - completed_width
y = -1
else
+ x = [cursor_pos.x - completed_width, 0].max
y = 0
end
cursor_pos_to_render = Reline::CursorPos.new(x, y)
@@ -261,20 +252,21 @@ module Reline
contents: result,
scrollbar: true,
height: [15, preferred_dialog_height].min,
- bg_color: 46,
- pointer_bg_color: 45,
- fg_color: 37,
- pointer_fg_color: 37
+ face: :completion_dialog
)
}
Reline::DEFAULT_DIALOG_CONTEXT = Array.new
def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination)
- Reline::IOGate.with_raw_input do
+ @mutex.synchronize do
unless confirm_multiline_termination
raise ArgumentError.new('#readmultiline needs block to confirm multiline termination')
end
- inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
+
+ Reline.update_iogate
+ io_gate.with_raw_input do
+ inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
+ end
whole_buffer = line_editor.whole_buffer.dup
whole_buffer.taint if RUBY_VERSION < '2.7'
@@ -282,27 +274,37 @@ module Reline
Reline::HISTORY << whole_buffer
end
- line_editor.reset_line if line_editor.whole_buffer.nil?
- whole_buffer
+ if line_editor.eof?
+ line_editor.reset_line
+ # Return nil if the input is aborted by C-d.
+ nil
+ else
+ whole_buffer
+ end
end
end
def readline(prompt = '', add_hist = false)
- inner_readline(prompt, add_hist, false)
+ @mutex.synchronize do
+ Reline.update_iogate
+ io_gate.with_raw_input do
+ inner_readline(prompt, add_hist, false)
+ end
- line = line_editor.line.dup
- line.taint if RUBY_VERSION < '2.7'
- if add_hist and line and line.chomp("\n").size > 0
- Reline::HISTORY << line.chomp("\n")
- end
+ line = line_editor.line.dup
+ line.taint if RUBY_VERSION < '2.7'
+ if add_hist and line and line.chomp("\n").size > 0
+ Reline::HISTORY << line.chomp("\n")
+ end
- line_editor.reset_line if line_editor.line.nil?
- line
+ line_editor.reset_line if line_editor.line.nil?
+ line
+ end
end
private def inner_readline(prompt, add_hist, multiline, &confirm_multiline_termination)
if ENV['RELINE_STDERR_TTY']
- if Reline::IOGate.win?
+ if io_gate.win?
$stderr = File.open(ENV['RELINE_STDERR_TTY'], 'a')
else
$stderr.reopen(ENV['RELINE_STDERR_TTY'], 'w')
@@ -310,10 +312,10 @@ module Reline
$stderr.sync = true
$stderr.puts "Reline is used by #{Process.pid}"
end
- otio = Reline::IOGate.prep
+ otio = io_gate.prep
may_req_ambiguous_char_width
- line_editor.reset(prompt, encoding: Reline::IOGate.encoding)
+ line_editor.reset(prompt, encoding: encoding)
if multiline
line_editor.multiline_on
if block_given?
@@ -329,58 +331,45 @@ module Reline
line_editor.prompt_proc = prompt_proc
line_editor.auto_indent_proc = auto_indent_proc
line_editor.dig_perfect_match_proc = dig_perfect_match_proc
- line_editor.pre_input_hook = pre_input_hook
- @dialog_proc_list.each_pair do |name_sym, d|
- line_editor.add_dialog_proc(name_sym, d.dialog_proc, d.context)
+ pre_input_hook&.call
+ unless Reline::IOGate == Reline::GeneralIO
+ @dialog_proc_list.each_pair do |name_sym, d|
+ line_editor.add_dialog_proc(name_sym, d.dialog_proc, d.context)
+ end
end
unless config.test_mode
config.read
config.reset_default_key_bindings
- Reline::IOGate.set_default_key_bindings(config)
+ io_gate.set_default_key_bindings(config)
end
+ line_editor.print_nomultiline_prompt(prompt)
+ line_editor.update_dialogs
line_editor.rerender
begin
line_editor.set_signal_handlers
- prev_pasting_state = false
loop do
- prev_pasting_state = Reline::IOGate.in_pasting?
read_io(config.keyseq_timeout) { |inputs|
- line_editor.set_pasting_state(Reline::IOGate.in_pasting?)
- inputs.each { |c|
- line_editor.input_key(c)
- line_editor.rerender
- }
- if @bracketed_paste_finished
- line_editor.rerender_all
- @bracketed_paste_finished = false
- end
+ line_editor.set_pasting_state(io_gate.in_pasting?)
+ inputs.each { |key| line_editor.update(key) }
}
- if prev_pasting_state == true and not Reline::IOGate.in_pasting? and not line_editor.finished?
- line_editor.set_pasting_state(false)
- prev_pasting_state = false
- line_editor.rerender_all
+ if line_editor.finished?
+ line_editor.render_finished
+ break
+ else
+ line_editor.set_pasting_state(io_gate.in_pasting?)
+ line_editor.rerender
end
- break if line_editor.finished?
end
- Reline::IOGate.move_cursor_column(0)
+ io_gate.move_cursor_column(0)
rescue Errno::EIO
# Maybe the I/O has been closed.
- rescue StandardError => e
- line_editor.finalize
- Reline::IOGate.deprep(otio)
- raise e
- rescue Exception
- # Including Interrupt
+ ensure
line_editor.finalize
- Reline::IOGate.deprep(otio)
- raise
+ io_gate.deprep(otio)
end
-
- line_editor.finalize
- Reline::IOGate.deprep(otio)
end
# GNU Readline waits for "keyseq-timeout" milliseconds to see if the ESC
@@ -395,10 +384,9 @@ module Reline
private def read_io(keyseq_timeout, &block)
buffer = []
loop do
- c = Reline::IOGate.getc
+ c = io_gate.getc(Float::INFINITY)
if c == -1
result = :unmatched
- @bracketed_paste_finished = true
else
buffer << c
result = key_stroke.match_status(buffer)
@@ -432,15 +420,8 @@ module Reline
end
private def read_2nd_character_of_key_sequence(keyseq_timeout, buffer, c, block)
- begin
- succ_c = nil
- Timeout.timeout(keyseq_timeout / 1000.0) {
- succ_c = Reline::IOGate.getc
- }
- rescue Timeout::Error # cancel matching only when first byte
- block.([Reline::Key.new(c, c, false)])
- return :break
- else
+ succ_c = io_gate.getc(keyseq_timeout.fdiv(1000))
+ if succ_c
case key_stroke.match_status(buffer.dup.push(succ_c))
when :unmatched
if c == "\e".ord
@@ -450,7 +431,7 @@ module Reline
end
return :break
when :matching
- Reline::IOGate.ungetc(succ_c)
+ io_gate.ungetc(succ_c)
return :next
when :matched
buffer << succ_c
@@ -460,27 +441,23 @@ module Reline
block.(expanded)
return :break
end
+ else
+ block.([Reline::Key.new(c, c, false)])
+ return :break
end
end
private def read_escaped_key(keyseq_timeout, c, block)
- begin
- escaped_c = nil
- Timeout.timeout(keyseq_timeout / 1000.0) {
- escaped_c = Reline::IOGate.getc
- }
- rescue Timeout::Error # independent ESC
+ escaped_c = io_gate.getc(keyseq_timeout.fdiv(1000))
+
+ if escaped_c.nil?
block.([Reline::Key.new(c, c, false)])
+ elsif escaped_c >= 128 # maybe, first byte of multi byte
+ block.([Reline::Key.new(c, c, false), Reline::Key.new(escaped_c, escaped_c, false)])
+ elsif escaped_c == "\e".ord # escape twice
+ block.([Reline::Key.new(c, c, false), Reline::Key.new(c, c, false)])
else
- if escaped_c.nil?
- block.([Reline::Key.new(c, c, false)])
- elsif escaped_c >= 128 # maybe, first byte of multi byte
- block.([Reline::Key.new(c, c, false), Reline::Key.new(escaped_c, escaped_c, false)])
- elsif escaped_c == "\e".ord # escape twice
- block.([Reline::Key.new(c, c, false), Reline::Key.new(c, c, false)])
- else
- block.([Reline::Key.new(escaped_c, escaped_c | 0b10000000, true)])
- end
+ block.([Reline::Key.new(escaped_c, escaped_c | 0b10000000, true)])
end
end
@@ -490,19 +467,19 @@ module Reline
end
private def may_req_ambiguous_char_width
- @ambiguous_width = 2 if Reline::IOGate == Reline::GeneralIO or !STDOUT.tty?
+ @ambiguous_width = 2 if io_gate == Reline::GeneralIO or !STDOUT.tty?
return if defined? @ambiguous_width
- Reline::IOGate.move_cursor_column(0)
+ io_gate.move_cursor_column(0)
begin
output.write "\u{25bd}"
rescue Encoding::UndefinedConversionError
# LANG=C
@ambiguous_width = 1
else
- @ambiguous_width = Reline::IOGate.cursor_pos.x
+ @ambiguous_width = io_gate.cursor_pos.x
end
- Reline::IOGate.move_cursor_column(0)
- Reline::IOGate.erase_after_cursor
+ io_gate.move_cursor_column(0)
+ io_gate.erase_after_cursor
end
end
@@ -565,7 +542,7 @@ module Reline
@core ||= Core.new { |core|
core.config = Reline::Config.new
core.key_stroke = Reline::KeyStroke.new(core.config)
- core.line_editor = Reline::LineEditor.new(core.config, Reline::IOGate.encoding)
+ core.line_editor = Reline::LineEditor.new(core.config, core.encoding)
core.basic_word_break_characters = " \t\n`><=;|&{("
core.completer_word_break_characters = " \t\n`><=;|&{("
@@ -578,12 +555,24 @@ module Reline
end
def self.ungetc(c)
- Reline::IOGate.ungetc(c)
+ core.io_gate.ungetc(c)
end
def self.line_editor
core.line_editor
end
+
+ def self.update_iogate
+ return if core.config.test_mode
+
+ # Need to change IOGate when `$stdout.tty?` change from false to true by `$stdout.reopen`
+ # Example: rails/spring boot the application in non-tty, then run console in tty.
+ if ENV['TERM'] != 'dumb' && core.io_gate == Reline::GeneralIO && $stdout.tty?
+ require 'reline/ansi'
+ remove_const(:IOGate)
+ const_set(:IOGate, Reline::ANSI)
+ end
+ end
end
require 'reline/general_io'
@@ -604,4 +593,6 @@ else
io
end
+Reline::Face.load_initial_configs
+
Reline::HISTORY = Reline::History.new(Reline.core.config)
diff --git a/lib/reline/ansi.rb b/lib/reline/ansi.rb
index 06ea9efd09..5e1f7249e3 100644
--- a/lib/reline/ansi.rb
+++ b/lib/reline/ansi.rb
@@ -1,9 +1,10 @@
require 'io/console'
require 'io/wait'
-require 'timeout'
require_relative 'terminfo'
class Reline::ANSI
+ RESET_COLOR = "\e[0m"
+
CAPNAME_KEY_BINDINGS = {
'khome' => :ed_move_to_beg,
'kend' => :ed_move_to_end,
@@ -14,10 +15,21 @@ class Reline::ANSI
'kcud1' => :ed_next_history,
'kcuf1' => :ed_next_char,
'kcub1' => :ed_prev_char,
- 'cuu' => :ed_prev_history,
- 'cud' => :ed_next_history,
- 'cuf' => :ed_next_char,
- 'cub' => :ed_prev_char,
+ }
+
+ ANSI_CURSOR_KEY_BINDINGS = {
+ # Up
+ 'A' => [:ed_prev_history, {}],
+ # Down
+ 'B' => [:ed_next_history, {}],
+ # Right
+ 'C' => [:ed_next_char, { ctrl: :em_next_word, meta: :em_next_word }],
+ # Left
+ 'D' => [:ed_prev_char, { ctrl: :ed_prev_word, meta: :ed_prev_word }],
+ # End
+ 'F' => [:ed_move_to_end, {}],
+ # Home
+ 'H' => [:ed_move_to_beg, {}],
}
if Reline::Terminfo.enabled?
@@ -33,23 +45,13 @@ class Reline::ANSI
end
def self.set_default_key_bindings(config, allow_terminfo: true)
+ set_default_key_bindings_ansi_cursor(config)
if allow_terminfo && Reline::Terminfo.enabled?
set_default_key_bindings_terminfo(config)
else
set_default_key_bindings_comprehensive_list(config)
end
{
- # extended entries of terminfo
- [27, 91, 49, 59, 53, 67] => :em_next_word, # Ctrl+→, extended entry
- [27, 91, 49, 59, 53, 68] => :ed_prev_word, # Ctrl+â†, extended entry
- [27, 91, 49, 59, 51, 67] => :em_next_word, # Meta+→, extended entry
- [27, 91, 49, 59, 51, 68] => :ed_prev_word, # Meta+â†, extended entry
- }.each_pair do |key, func|
- config.add_default_key_binding_by_keymap(:emacs, key, func)
- config.add_default_key_binding_by_keymap(:vi_insert, key, func)
- config.add_default_key_binding_by_keymap(:vi_command, key, func)
- end
- {
[27, 91, 90] => :completion_journey_up, # S-Tab
}.each_pair do |key, func|
config.add_default_key_binding_by_keymap(:emacs, key, func)
@@ -64,18 +66,33 @@ class Reline::ANSI
end
end
+ def self.set_default_key_bindings_ansi_cursor(config)
+ ANSI_CURSOR_KEY_BINDINGS.each do |char, (default_func, modifiers)|
+ bindings = [["\e[#{char}", default_func]] # CSI + char
+ if modifiers[:ctrl]
+ # CSI + ctrl_key_modifier + char
+ bindings << ["\e[1;5#{char}", modifiers[:ctrl]]
+ end
+ if modifiers[:meta]
+ # CSI + meta_key_modifier + char
+ bindings << ["\e[1;3#{char}", modifiers[:meta]]
+ # Meta(ESC) + CSI + char
+ bindings << ["\e\e[#{char}", modifiers[:meta]]
+ end
+ bindings.each do |sequence, func|
+ key = sequence.bytes
+ config.add_default_key_binding_by_keymap(:emacs, key, func)
+ config.add_default_key_binding_by_keymap(:vi_insert, key, func)
+ config.add_default_key_binding_by_keymap(:vi_command, key, func)
+ end
+ end
+ end
+
def self.set_default_key_bindings_terminfo(config)
key_bindings = CAPNAME_KEY_BINDINGS.map do |capname, key_binding|
begin
key_code = Reline::Terminfo.tigetstr(capname)
- case capname
- # Escape sequences that omit the move distance and are set to defaults
- # value 1 may be sometimes sent by pressing the arrow-key.
- when 'cuu', 'cud', 'cuf', 'cub'
- [ key_code.sub(/%p1%d/, '').bytes, key_binding ]
- else
- [ key_code.bytes, key_binding ]
- end
+ [ key_code.bytes, key_binding ]
rescue Reline::Terminfo::TerminfoError
# capname is undefined
end
@@ -94,14 +111,8 @@ class Reline::ANSI
[27, 91, 49, 126] => :ed_move_to_beg, # Home
[27, 91, 52, 126] => :ed_move_to_end, # End
[27, 91, 51, 126] => :key_delete, # Del
- [27, 91, 65] => :ed_prev_history, # ↑
- [27, 91, 66] => :ed_next_history, # ↓
- [27, 91, 67] => :ed_next_char, # →
- [27, 91, 68] => :ed_prev_char, # â†
# KDE
- [27, 91, 72] => :ed_move_to_beg, # Home
- [27, 91, 70] => :ed_move_to_end, # End
# Del is 0x08
[27, 71, 65] => :ed_prev_history, # ↑
[27, 71, 66] => :ed_next_history, # ↓
@@ -118,12 +129,6 @@ class Reline::ANSI
# Del is 0x08
# Arrow keys are the same of KDE
- # iTerm2
- [27, 27, 91, 67] => :em_next_word, # Option+→, extended entry
- [27, 27, 91, 68] => :ed_prev_word, # Option+â†, extended entry
- [195, 166] => :em_next_word, # Option+f
- [195, 162] => :ed_prev_word, # Option+b
-
[27, 79, 65] => :ed_prev_history, # ↑
[27, 79, 66] => :ed_next_history, # ↓
[27, 79, 67] => :ed_next_char, # →
@@ -146,17 +151,25 @@ class Reline::ANSI
end
def self.with_raw_input
- @@input.raw { yield }
+ if @@input.tty?
+ @@input.raw(intr: true) { yield }
+ else
+ yield
+ end
end
@@buf = []
- def self.inner_getc
+ def self.inner_getc(timeout_second)
unless @@buf.empty?
return @@buf.shift
end
- until c = @@input.raw(intr: true) { @@input.wait_readable(0.1) && @@input.getbyte }
- Reline.core.line_editor.resize
+ until @@input.wait_readable(0.01)
+ timeout_second -= 0.01
+ return nil if timeout_second <= 0
+
+ Reline.core.line_editor.handle_signal
end
+ c = @@input.getbyte
(c == 0x16 && @@input.raw(min: 0, time: 0, &:getbyte)) || c
rescue Errno::EIO
# Maybe the I/O has been closed.
@@ -168,45 +181,43 @@ class Reline::ANSI
@@in_bracketed_paste_mode = false
START_BRACKETED_PASTE = String.new("\e[200~,", encoding: Encoding::ASCII_8BIT)
END_BRACKETED_PASTE = String.new("\e[200~.", encoding: Encoding::ASCII_8BIT)
- def self.getc_with_bracketed_paste
+ def self.getc_with_bracketed_paste(timeout_second)
buffer = String.new(encoding: Encoding::ASCII_8BIT)
- buffer << inner_getc
+ buffer << inner_getc(timeout_second)
while START_BRACKETED_PASTE.start_with?(buffer) or END_BRACKETED_PASTE.start_with?(buffer) do
if START_BRACKETED_PASTE == buffer
@@in_bracketed_paste_mode = true
- return inner_getc
+ return inner_getc(timeout_second)
elsif END_BRACKETED_PASTE == buffer
@@in_bracketed_paste_mode = false
ungetc(-1)
- return inner_getc
+ return inner_getc(timeout_second)
end
- begin
- succ_c = nil
- Timeout.timeout(Reline.core.config.keyseq_timeout * 100) {
- succ_c = inner_getc
- }
- rescue Timeout::Error
- break
- else
+ succ_c = inner_getc(Reline.core.config.keyseq_timeout)
+
+ if succ_c
buffer << succ_c
+ else
+ break
end
end
buffer.bytes.reverse_each do |ch|
ungetc ch
end
- inner_getc
+ inner_getc(timeout_second)
end
- def self.getc
+ # if the usage expects to wait indefinitely, use Float::INFINITY for timeout_second
+ def self.getc(timeout_second)
if Reline.core.config.enable_bracketed_paste
- getc_with_bracketed_paste
+ getc_with_bracketed_paste(timeout_second)
else
- inner_getc
+ inner_getc(timeout_second)
end
end
def self.in_pasting?
- @@in_bracketed_paste_mode or (not Reline::IOGate.empty_buffer?)
+ @@in_bracketed_paste_mode or (not empty_buffer?)
end
def self.empty_buffer?
@@ -273,7 +284,7 @@ class Reline::ANSI
buf = @@output.pread(@@output.pos, 0)
row = buf.count("\n")
column = buf.rindex("\n") ? (buf.size - buf.rindex("\n")) - 1 : 0
- rescue Errno::ESPIPE
+ rescue Errno::ESPIPE, IOError
# Just returns column 1 for ambiguous width because this I/O is not
# tty and can't seek.
row = 0
@@ -304,7 +315,7 @@ class Reline::ANSI
end
def self.hide_cursor
- if Reline::Terminfo.enabled?
+ if Reline::Terminfo.enabled? && Reline::Terminfo.term_supported?
begin
@@output.write Reline::Terminfo.tigetstr('civis')
rescue Reline::Terminfo::TerminfoError
@@ -316,7 +327,7 @@ class Reline::ANSI
end
def self.show_cursor
- if Reline::Terminfo.enabled?
+ if Reline::Terminfo.enabled? && Reline::Terminfo.term_supported?
begin
@@output.write Reline::Terminfo.tigetstr('cnorm')
rescue Reline::Terminfo::TerminfoError
@@ -331,9 +342,12 @@ class Reline::ANSI
@@output.write "\e[K"
end
+ # This only works when the cursor is at the bottom of the scroll range
+ # For more details, see https://github.com/ruby/reline/pull/577#issuecomment-1646679623
def self.scroll_down(x)
return if x.zero?
- @@output.write "\e[#{x}S"
+ # We use `\n` instead of CSI + S because CSI + S would cause https://github.com/ruby/reline/issues/576
+ @@output.write "\n" * x
end
def self.clear_screen
diff --git a/lib/reline/config.rb b/lib/reline/config.rb
index 87726393a6..bec1ca178d 100644
--- a/lib/reline/config.rb
+++ b/lib/reline/config.rb
@@ -53,8 +53,6 @@ class Reline::Config
@additional_key_bindings[:vi_insert] = {}
@additional_key_bindings[:vi_command] = {}
@oneshot_key_bindings = {}
- @skip_section = nil
- @if_stack = nil
@editing_mode_label = :emacs
@keymap_label = :emacs
@keymap_prefix = []
@@ -190,9 +188,7 @@ class Reline::Config
end
end
end
- conditions = [@skip_section, @if_stack]
- @skip_section = nil
- @if_stack = []
+ if_stack = []
lines.each_with_index do |line, no|
next if line.match(/\A\s*#/)
@@ -201,11 +197,11 @@ class Reline::Config
line = line.chomp.lstrip
if line.start_with?('$')
- handle_directive(line[1..-1], file, no)
+ handle_directive(line[1..-1], file, no, if_stack)
next
end
- next if @skip_section
+ next if if_stack.any? { |_no, skip| skip }
case line
when /^set +([^ ]+) +([^ ]+)/i
@@ -214,45 +210,49 @@ class Reline::Config
next
when /\s*("#{KEYSEQ_PATTERN}+")\s*:\s*(.*)\s*$/o
key, func_name = $1, $2
+ func_name = func_name.split.first
keystroke, func = bind_key(key, func_name)
next unless keystroke
@additional_key_bindings[@keymap_label][@keymap_prefix + keystroke] = func
end
end
- unless @if_stack.empty?
- raise InvalidInputrc, "#{file}:#{@if_stack.last[1]}: unclosed if"
+ unless if_stack.empty?
+ raise InvalidInputrc, "#{file}:#{if_stack.last[0]}: unclosed if"
end
- ensure
- @skip_section, @if_stack = conditions
end
- def handle_directive(directive, file, no)
+ def handle_directive(directive, file, no, if_stack)
directive, args = directive.split(' ')
case directive
when 'if'
condition = false
case args
- when 'mode'
+ when /^mode=(vi|emacs)$/i
+ mode = $1.downcase
+ # NOTE: mode=vi means vi-insert mode
+ mode = 'vi_insert' if mode == 'vi'
+ if @editing_mode_label == mode.to_sym
+ condition = true
+ end
when 'term'
when 'version'
else # application name
condition = true if args == 'Ruby'
condition = true if args == 'Reline'
end
- @if_stack << [file, no, @skip_section]
- @skip_section = !condition
+ if_stack << [no, !condition]
when 'else'
- if @if_stack.empty?
+ if if_stack.empty?
raise InvalidInputrc, "#{file}:#{no}: unmatched else"
end
- @skip_section = !@skip_section
+ if_stack.last[1] = !if_stack.last[1]
when 'endif'
- if @if_stack.empty?
+ if if_stack.empty?
raise InvalidInputrc, "#{file}:#{no}: unmatched endif"
end
- @skip_section = @if_stack.pop
+ if_stack.pop
when 'include'
- read(args)
+ read(File.expand_path(args))
end
end
diff --git a/lib/reline/face.rb b/lib/reline/face.rb
new file mode 100644
index 0000000000..d07196e2e7
--- /dev/null
+++ b/lib/reline/face.rb
@@ -0,0 +1,199 @@
+# frozen_string_literal: true
+
+class Reline::Face
+ SGR_PARAMETERS = {
+ foreground: {
+ black: 30,
+ red: 31,
+ green: 32,
+ yellow: 33,
+ blue: 34,
+ magenta: 35,
+ cyan: 36,
+ white: 37,
+ bright_black: 90,
+ gray: 90,
+ bright_red: 91,
+ bright_green: 92,
+ bright_yellow: 93,
+ bright_blue: 94,
+ bright_magenta: 95,
+ bright_cyan: 96,
+ bright_white: 97
+ },
+ background: {
+ black: 40,
+ red: 41,
+ green: 42,
+ yellow: 43,
+ blue: 44,
+ magenta: 45,
+ cyan: 46,
+ white: 47,
+ bright_black: 100,
+ gray: 100,
+ bright_red: 101,
+ bright_green: 102,
+ bright_yellow: 103,
+ bright_blue: 104,
+ bright_magenta: 105,
+ bright_cyan: 106,
+ bright_white: 107,
+ },
+ style: {
+ reset: 0,
+ bold: 1,
+ faint: 2,
+ italicized: 3,
+ underlined: 4,
+ slowly_blinking: 5,
+ blinking: 5,
+ rapidly_blinking: 6,
+ negative: 7,
+ concealed: 8,
+ crossed_out: 9
+ }
+ }.freeze
+
+ class Config
+ ESSENTIAL_DEFINE_NAMES = %i(default enhanced scrollbar).freeze
+ RESET_SGR = "\e[0m".freeze
+
+ def initialize(name, &block)
+ @definition = {}
+ block.call(self)
+ ESSENTIAL_DEFINE_NAMES.each do |name|
+ @definition[name] ||= { style: :reset, escape_sequence: RESET_SGR }
+ end
+ end
+
+ attr_reader :definition
+
+ def define(name, **values)
+ values[:escape_sequence] = format_to_sgr(values.to_a).freeze
+ @definition[name] = values
+ end
+
+ def reconfigure
+ @definition.each_value do |values|
+ values.delete(:escape_sequence)
+ values[:escape_sequence] = format_to_sgr(values.to_a).freeze
+ end
+ end
+
+ def [](name)
+ @definition.dig(name, :escape_sequence) or raise ArgumentError, "unknown face: #{name}"
+ end
+
+ private
+
+ def sgr_rgb(key, value)
+ return nil unless rgb_expression?(value)
+ if Reline::Face.truecolor?
+ sgr_rgb_truecolor(key, value)
+ else
+ sgr_rgb_256color(key, value)
+ end
+ end
+
+ def sgr_rgb_truecolor(key, value)
+ case key
+ when :foreground
+ "38;2;"
+ when :background
+ "48;2;"
+ end + value[1, 6].scan(/../).map(&:hex).join(";")
+ end
+
+ def sgr_rgb_256color(key, value)
+ # 256 colors are
+ # 0..15: standard colors, hight intensity colors
+ # 16..232: 216 colors (R, G, B each 6 steps)
+ # 233..255: grayscale colors (24 steps)
+ # This methods converts rgb_expression to 216 colors
+ rgb = value[1, 6].scan(/../).map(&:hex)
+ # Color steps are [0, 95, 135, 175, 215, 255]
+ r, g, b = rgb.map { |v| v <= 95 ? v / 48 : (v - 35) / 40 }
+ color = (16 + 36 * r + 6 * g + b)
+ case key
+ when :foreground
+ "38;5;#{color}"
+ when :background
+ "48;5;#{color}"
+ end
+ end
+
+ def format_to_sgr(ordered_values)
+ sgr = "\e[" + ordered_values.map do |key_value|
+ key, value = key_value
+ case key
+ when :foreground, :background
+ case value
+ when Symbol
+ SGR_PARAMETERS[key][value]
+ when String
+ sgr_rgb(key, value)
+ end
+ when :style
+ [ value ].flatten.map do |style_name|
+ SGR_PARAMETERS[:style][style_name]
+ end.then do |sgr_parameters|
+ sgr_parameters.include?(nil) ? nil : sgr_parameters
+ end
+ end.then do |rendition_expression|
+ unless rendition_expression
+ raise ArgumentError, "invalid SGR parameter: #{value.inspect}"
+ end
+ rendition_expression
+ end
+ end.join(';') + "m"
+ sgr == RESET_SGR ? RESET_SGR : RESET_SGR + sgr
+ end
+
+ def rgb_expression?(color)
+ color.respond_to?(:match?) and color.match?(/\A#[0-9a-fA-F]{6}\z/)
+ end
+ end
+
+ private_constant :SGR_PARAMETERS, :Config
+
+ def self.truecolor?
+ @force_truecolor || %w[truecolor 24bit].include?(ENV['COLORTERM'])
+ end
+
+ def self.force_truecolor
+ @force_truecolor = true
+ @configs&.each_value(&:reconfigure)
+ end
+
+ def self.[](name)
+ @configs[name]
+ end
+
+ def self.config(name, &block)
+ @configs ||= {}
+ @configs[name] = Config.new(name, &block)
+ end
+
+ def self.configs
+ @configs.transform_values(&:definition)
+ end
+
+ def self.load_initial_configs
+ config(:default) do |conf|
+ conf.define :default, style: :reset
+ conf.define :enhanced, style: :reset
+ conf.define :scrollbar, style: :reset
+ end
+ config(:completion_dialog) do |conf|
+ conf.define :default, foreground: :bright_white, background: :gray
+ conf.define :enhanced, foreground: :black, background: :white
+ conf.define :scrollbar, foreground: :white, background: :gray
+ end
+ end
+
+ def self.reset_to_initial_configs
+ @configs = {}
+ load_initial_configs
+ end
+end
diff --git a/lib/reline/general_io.rb b/lib/reline/general_io.rb
index 9929846568..d52151ad3c 100644
--- a/lib/reline/general_io.rb
+++ b/lib/reline/general_io.rb
@@ -1,10 +1,15 @@
-require 'timeout'
require 'io/wait'
class Reline::GeneralIO
+ RESET_COLOR = '' # Do not send color reset sequence
+
def self.reset(encoding: nil)
@@pasting = false
- @@encoding = encoding
+ if encoding
+ @@encoding = encoding
+ elsif defined?(@@encoding)
+ remove_class_variable(:@@encoding)
+ end
end
def self.encoding
@@ -35,12 +40,13 @@ class Reline::GeneralIO
yield
end
- def self.getc
+ def self.getc(_timeout_second)
unless @@buf.empty?
return @@buf.shift
end
c = nil
loop do
+ Reline.core.line_editor.handle_signal
result = @@input.wait_readable(0.1)
next if result.nil?
c = @@input.read(1)
@@ -54,7 +60,7 @@ class Reline::GeneralIO
end
def self.get_screen_size
- [1, 1]
+ [24, 80]
end
def self.cursor_pos
@@ -97,14 +103,6 @@ class Reline::GeneralIO
@@pasting
end
- def self.start_pasting
- @@pasting = true
- end
-
- def self.finish_pasting
- @@pasting = false
- end
-
def self.prep
end
diff --git a/lib/reline/history.rb b/lib/reline/history.rb
index 7a1ed6b90b..3f3b65fea6 100644
--- a/lib/reline/history.rb
+++ b/lib/reline/history.rb
@@ -62,7 +62,7 @@ class Reline::History < Array
private def check_index(index)
index += size if index < 0
if index < -2147483648 or 2147483647 < index
- raise RangeError.new("integer #{index} too big to convert to `int'")
+ raise RangeError.new("integer #{index} too big to convert to 'int'")
end
# If history_size is negative, history size is unlimited.
if @config.history_size.positive?
diff --git a/lib/reline/key_actor/emacs.rb b/lib/reline/key_actor/emacs.rb
index a561feee57..5d0a7fb63d 100644
--- a/lib/reline/key_actor/emacs.rb
+++ b/lib/reline/key_actor/emacs.rb
@@ -49,13 +49,13 @@ class Reline::KeyActor::Emacs < Reline::KeyActor::Base
# 23 ^W
:em_kill_region,
# 24 ^X
- :ed_sequence_lead_in,
+ :ed_unassigned,
# 25 ^Y
:em_yank,
# 26 ^Z
:ed_ignore,
# 27 ^[
- :em_meta_next,
+ :ed_unassigned,
# 28 ^\
:ed_ignore,
# 29 ^]
@@ -319,9 +319,9 @@ class Reline::KeyActor::Emacs < Reline::KeyActor::Base
# 158 M-^^
:ed_unassigned,
# 159 M-^_
- :em_copy_prev_word,
- # 160 M-SPACE
:ed_unassigned,
+ # 160 M-SPACE
+ :em_set_mark,
# 161 M-!
:ed_unassigned,
# 162 M-"
@@ -415,7 +415,7 @@ class Reline::KeyActor::Emacs < Reline::KeyActor::Base
# 206 M-N
:vi_search_next,
# 207 M-O
- :ed_sequence_lead_in,
+ :ed_unassigned,
# 208 M-P
:vi_search_prev,
# 209 M-Q
@@ -431,15 +431,15 @@ class Reline::KeyActor::Emacs < Reline::KeyActor::Base
# 214 M-V
:ed_unassigned,
# 215 M-W
- :em_copy_region,
+ :ed_unassigned,
# 216 M-X
- :ed_command,
- # 217 M-Y
:ed_unassigned,
+ # 217 M-Y
+ :em_yank_pop,
# 218 M-Z
:ed_unassigned,
# 219 M-[
- :ed_sequence_lead_in,
+ :ed_unassigned,
# 220 M-\
:ed_unassigned,
# 221 M-]
@@ -495,9 +495,9 @@ class Reline::KeyActor::Emacs < Reline::KeyActor::Base
# 246 M-v
:ed_unassigned,
# 247 M-w
- :em_copy_region,
+ :ed_unassigned,
# 248 M-x
- :ed_command,
+ :ed_unassigned,
# 249 M-y
:ed_unassigned,
# 250 M-z
diff --git a/lib/reline/key_actor/vi_command.rb b/lib/reline/key_actor/vi_command.rb
index 98146d2f77..06bb0ba8e4 100644
--- a/lib/reline/key_actor/vi_command.rb
+++ b/lib/reline/key_actor/vi_command.rb
@@ -17,7 +17,7 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
# 7 ^G
:ed_unassigned,
# 8 ^H
- :ed_unassigned,
+ :ed_prev_char,
# 9 ^I
:ed_unassigned,
# 10 ^J
@@ -41,7 +41,7 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
# 19 ^S
:ed_ignore,
# 20 ^T
- :ed_unassigned,
+ :ed_transpose_chars,
# 21 ^U
:vi_kill_line_prev,
# 22 ^V
@@ -51,7 +51,7 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
# 24 ^X
:ed_unassigned,
# 25 ^Y
- :ed_unassigned,
+ :em_yank,
# 26 ^Z
:ed_unassigned,
# 27 ^[
@@ -75,7 +75,7 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
# 36 $
:ed_move_to_end,
# 37 %
- :vi_match,
+ :ed_unassigned,
# 38 &
:ed_unassigned,
# 39 '
@@ -89,11 +89,11 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
# 43 +
:ed_next_history,
# 44 ,
- :vi_repeat_prev_char,
+ :ed_unassigned,
# 45 -
:ed_prev_history,
# 46 .
- :vi_redo,
+ :ed_unassigned,
# 47 /
:vi_search_prev,
# 48 0
@@ -117,9 +117,9 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
# 57 9
:ed_argument_digit,
# 58 :
- :ed_command,
+ :ed_unassigned,
# 59 ;
- :vi_repeat_next_char,
+ :ed_unassigned,
# 60 <
:ed_unassigned,
# 61 =
@@ -157,21 +157,21 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
# 77 M
:ed_unassigned,
# 78 N
- :vi_repeat_search_prev,
+ :ed_unassigned,
# 79 O
- :ed_sequence_lead_in,
+ :ed_unassigned,
# 80 P
:vi_paste_prev,
# 81 Q
:ed_unassigned,
# 82 R
- :vi_replace_mode,
+ :ed_unassigned,
# 83 S
- :vi_substitute_line,
+ :ed_unassigned,
# 84 T
:vi_to_prev_char,
# 85 U
- :vi_undo_line,
+ :ed_unassigned,
# 86 V
:ed_unassigned,
# 87 W
@@ -179,11 +179,11 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
# 88 X
:ed_delete_prev_char,
# 89 Y
- :vi_yank_end,
+ :ed_unassigned,
# 90 Z
:ed_unassigned,
# 91 [
- :ed_sequence_lead_in,
+ :ed_unassigned,
# 92 \
:ed_unassigned,
# 93 ]
@@ -191,7 +191,7 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
# 94 ^
:vi_first_print,
# 95 _
- :vi_history_word,
+ :ed_unassigned,
# 96 `
:ed_unassigned,
# 97 a
@@ -221,7 +221,7 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
# 109 m
:ed_unassigned,
# 110 n
- :vi_repeat_search_next,
+ :ed_unassigned,
# 111 o
:ed_unassigned,
# 112 p
@@ -231,11 +231,11 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
# 114 r
:vi_replace_char,
# 115 s
- :vi_substitute_char,
+ :ed_unassigned,
# 116 t
:vi_to_next_char,
# 117 u
- :vi_undo,
+ :ed_unassigned,
# 118 v
:vi_histedit,
# 119 w
@@ -253,9 +253,9 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
# 125 }
:ed_unassigned,
# 126 ~
- :vi_change_case,
- # 127 ^?
:ed_unassigned,
+ # 127 ^?
+ :em_delete_prev_char,
# 128 M-^@
:ed_unassigned,
# 129 M-^A
@@ -415,7 +415,7 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
# 206 M-N
:ed_unassigned,
# 207 M-O
- :ed_sequence_lead_in,
+ :ed_unassigned,
# 208 M-P
:ed_unassigned,
# 209 M-Q
@@ -439,7 +439,7 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
# 218 M-Z
:ed_unassigned,
# 219 M-[
- :ed_sequence_lead_in,
+ :ed_unassigned,
# 220 M-\
:ed_unassigned,
# 221 M-]
diff --git a/lib/reline/key_actor/vi_insert.rb b/lib/reline/key_actor/vi_insert.rb
index b8e89f81d8..c3d7f9c12d 100644
--- a/lib/reline/key_actor/vi_insert.rb
+++ b/lib/reline/key_actor/vi_insert.rb
@@ -41,7 +41,7 @@ class Reline::KeyActor::ViInsert < Reline::KeyActor::Base
# 19 ^S
:vi_search_next,
# 20 ^T
- :ed_insert,
+ :ed_transpose_chars,
# 21 ^U
:vi_kill_line_prev,
# 22 ^V
@@ -51,7 +51,7 @@ class Reline::KeyActor::ViInsert < Reline::KeyActor::Base
# 24 ^X
:ed_insert,
# 25 ^Y
- :ed_insert,
+ :em_yank,
# 26 ^Z
:ed_insert,
# 27 ^[
diff --git a/lib/reline/key_stroke.rb b/lib/reline/key_stroke.rb
index c1c61513a9..bceffbb53f 100644
--- a/lib/reline/key_stroke.rb
+++ b/lib/reline/key_stroke.rb
@@ -1,4 +1,8 @@
class Reline::KeyStroke
+ ESC_BYTE = 27
+ CSI_PARAMETER_BYTES_RANGE = 0x30..0x3f
+ CSI_INTERMEDIATE_BYTES_RANGE = (0x20..0x2f)
+
def initialize(config)
@config = config
end
@@ -73,17 +77,26 @@ class Reline::KeyStroke
return :matched if it.max_by(&:size)&.size&.< input.size
return :matching if it.size > 1
}
- key_mapping.keys.select { |lhs|
- start_with?(input, lhs)
- }.tap { |it|
- return it.size > 0 ? :matched : :unmatched
- }
+ if key_mapping.keys.any? { |lhs| start_with?(input, lhs) }
+ :matched
+ else
+ match_unknown_escape_sequence(input).first
+ end
end
def expand(input)
- input = compress_meta_key(input)
lhs = key_mapping.keys.select { |item| start_with?(input, item) }.sort_by(&:size).last
- return input unless lhs
+ unless lhs
+ status, size = match_unknown_escape_sequence(input)
+ case status
+ when :matched
+ return [:ed_unassigned] + expand(input.drop(size))
+ when :matching
+ return [:ed_unassigned]
+ else
+ return input
+ end
+ end
rhs = key_mapping[lhs]
case rhs
@@ -99,6 +112,36 @@ class Reline::KeyStroke
private
+ # returns match status of CSI/SS3 sequence and matched length
+ def match_unknown_escape_sequence(input)
+ idx = 0
+ return [:unmatched, nil] unless input[idx] == ESC_BYTE
+ idx += 1
+ idx += 1 if input[idx] == ESC_BYTE
+
+ case input[idx]
+ when nil
+ return [:matching, nil]
+ when 91 # == '['.ord
+ # CSI sequence
+ idx += 1
+ idx += 1 while idx < input.size && CSI_PARAMETER_BYTES_RANGE.cover?(input[idx])
+ idx += 1 while idx < input.size && CSI_INTERMEDIATE_BYTES_RANGE.cover?(input[idx])
+ input[idx] ? [:matched, idx + 1] : [:matching, nil]
+ when 79 # == 'O'.ord
+ # SS3 sequence
+ input[idx + 1] ? [:matched, idx + 2] : [:matching, nil]
+ else
+ if idx == 1
+ # `ESC char`, make it :unmatched so that it will be handled correctly in `read_2nd_character_of_key_sequence`
+ [:unmatched, nil]
+ else
+ # `ESC ESC char`
+ [:matched, idx + 1]
+ end
+ end
+ end
+
def key_mapping
@config.key_bindings
end
diff --git a/lib/reline/kill_ring.rb b/lib/reline/kill_ring.rb
index bb3684b42b..201f6f3ca0 100644
--- a/lib/reline/kill_ring.rb
+++ b/lib/reline/kill_ring.rb
@@ -14,7 +14,7 @@ class Reline::KillRing
end
def ==(other)
- object_id == other.object_id
+ equal?(other)
end
end
@@ -68,7 +68,7 @@ class Reline::KillRing
def append(string, before_p = false)
case @state
when State::FRESH, State::YANK
- @ring << RingPoint.new(string)
+ @ring << RingPoint.new(+string)
@state = State::CONTINUED
when State::CONTINUED, State::PROCESSED
if before_p
diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb
index e8b83e2a6e..123dca0aac 100644
--- a/lib/reline/line_editor.rb
+++ b/lib/reline/line_editor.rb
@@ -6,7 +6,6 @@ require 'tempfile'
class Reline::LineEditor
# TODO: undo
# TODO: Use "private alias_method" idiom after drop Ruby 2.5.
- attr_reader :line
attr_reader :byte_pointer
attr_accessor :confirm_multiline_termination_proc
attr_accessor :completion_proc
@@ -14,7 +13,6 @@ class Reline::LineEditor
attr_accessor :output_modifier_proc
attr_accessor :prompt_proc
attr_accessor :auto_indent_proc
- attr_accessor :pre_input_hook
attr_accessor :dig_perfect_match_proc
attr_writer :output
@@ -35,99 +33,93 @@ class Reline::LineEditor
vi_next_big_word
vi_prev_big_word
vi_end_big_word
- vi_repeat_next_char
- vi_repeat_prev_char
}
module CompletionState
NORMAL = :normal
COMPLETION = :completion
MENU = :menu
- JOURNEY = :journey
MENU_WITH_PERFECT_MATCH = :menu_with_perfect_match
PERFECT_MATCH = :perfect_match
end
- CompletionJourneyData = Struct.new('CompletionJourneyData', :preposing, :postposing, :list, :pointer)
- MenuInfo = Struct.new('MenuInfo', :target, :list)
+ RenderedScreen = Struct.new(:base_y, :lines, :cursor_y, keyword_init: true)
+
+ CompletionJourneyState = Struct.new(:line_index, :pre, :target, :post, :list, :pointer)
+
+ class MenuInfo
+ attr_reader :list
+
+ def initialize(list)
+ @list = list
+ end
+
+ def lines(screen_width)
+ return [] if @list.empty?
+
+ list = @list.sort
+ sizes = list.map { |item| Reline::Unicode.calculate_width(item) }
+ item_width = sizes.max + 2
+ num_cols = [screen_width / item_width, 1].max
+ num_rows = list.size.fdiv(num_cols).ceil
+ list_with_padding = list.zip(sizes).map { |item, size| item + ' ' * (item_width - size) }
+ aligned = (list_with_padding + [nil] * (num_rows * num_cols - list_with_padding.size)).each_slice(num_rows).to_a.transpose
+ aligned.map do |row|
+ row.join.rstrip
+ end
+ end
+ end
- PROMPT_LIST_CACHE_TIMEOUT = 0.5
MINIMUM_SCROLLBAR_HEIGHT = 1
def initialize(config, encoding)
@config = config
@completion_append_character = ''
+ @screen_size = Reline::IOGate.get_screen_size
reset_variables(encoding: encoding)
end
- def set_pasting_state(in_pasting)
- @in_pasting = in_pasting
+ def io_gate
+ Reline::IOGate
end
- def simplified_rendering?
- if finished?
- false
- elsif @just_cursor_moving and not @rerender_all
- true
- else
- not @rerender_all and not finished? and @in_pasting
- end
+ def set_pasting_state(in_pasting)
+ # While pasting, text to be inserted is stored to @continuous_insertion_buffer.
+ # After pasting, this buffer should be force inserted.
+ process_insert(force: true) if @in_pasting && !in_pasting
+ @in_pasting = in_pasting
end
private def check_mode_string
- mode_string = nil
if @config.show_mode_in_prompt
if @config.editing_mode_is?(:vi_command)
- mode_string = @config.vi_cmd_mode_string
+ @config.vi_cmd_mode_string
elsif @config.editing_mode_is?(:vi_insert)
- mode_string = @config.vi_ins_mode_string
+ @config.vi_ins_mode_string
elsif @config.editing_mode_is?(:emacs)
- mode_string = @config.emacs_mode_string
+ @config.emacs_mode_string
else
- mode_string = '?'
+ '?'
end
end
- if mode_string != @prev_mode_string
- @rerender_all = true
- end
- @prev_mode_string = mode_string
- mode_string
end
- private def check_multiline_prompt(buffer, force_recalc: false)
+ private def check_multiline_prompt(buffer, mode_string)
if @vi_arg
prompt = "(arg: #{@vi_arg}) "
- @rerender_all = true
elsif @searching_prompt
prompt = @searching_prompt
- @rerender_all = true
else
prompt = @prompt
end
- if simplified_rendering? && !force_recalc
+ if !@is_multiline
mode_string = check_mode_string
prompt = mode_string + prompt if mode_string
- return [prompt, calculate_width(prompt, true), [prompt] * buffer.size]
- end
- if @prompt_proc
- use_cached_prompt_list = false
- if @cached_prompt_list
- if @just_cursor_moving
- use_cached_prompt_list = true
- elsif Time.now.to_f < (@prompt_cache_time + PROMPT_LIST_CACHE_TIMEOUT) and buffer.size == @cached_prompt_list.size
- use_cached_prompt_list = true
- end
- end
- use_cached_prompt_list = false if @rerender_all
- if use_cached_prompt_list
- prompt_list = @cached_prompt_list
- else
- prompt_list = @cached_prompt_list = @prompt_proc.(buffer).map { |pr| pr.gsub("\n", "\\n") }
- @prompt_cache_time = Time.now.to_f
- end
+ [prompt] + [''] * (buffer.size - 1)
+ elsif @prompt_proc
+ prompt_list = @prompt_proc.(buffer).map { |pr| pr.gsub("\n", "\\n") }
prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
prompt_list = [prompt] if prompt_list.empty?
- mode_string = check_mode_string
prompt_list = prompt_list.map{ |pr| mode_string + pr } if mode_string
prompt = prompt_list[@line_index]
prompt = prompt_list[0] if prompt.nil?
@@ -137,24 +129,17 @@ class Reline::LineEditor
prompt_list << prompt_list.last
end
end
- prompt_width = calculate_width(prompt, true)
- [prompt, prompt_width, prompt_list]
+ prompt_list
else
- mode_string = check_mode_string
prompt = mode_string + prompt if mode_string
- prompt_width = calculate_width(prompt, true)
- [prompt, prompt_width, nil]
+ [prompt] * buffer.size
end
end
def reset(prompt = '', encoding:)
- @rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
@screen_size = Reline::IOGate.get_screen_size
- @screen_height = @screen_size.first
reset_variables(prompt, encoding: encoding)
- Reline::IOGate.set_winch_handler do
- @resized = true
- end
+ @rendered_screen.base_y = Reline::IOGate.cursor_pos.y
if ENV.key?('RELINE_ALT_SCROLLBAR')
@full_block = '::'
@upper_half_block = "''"
@@ -178,67 +163,53 @@ class Reline::LineEditor
end
end
- def resize
+ def handle_signal
+ handle_interrupted
+ handle_resized
+ end
+
+ private def handle_resized
return unless @resized
- @resized = false
- @rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
- old_screen_size = @screen_size
+
@screen_size = Reline::IOGate.get_screen_size
- @screen_height = @screen_size.first
- if old_screen_size.last < @screen_size.last # columns increase
- @rerender_all = true
- rerender
+ @resized = false
+ scroll_into_view
+ Reline::IOGate.move_cursor_up @rendered_screen.cursor_y
+ @rendered_screen.base_y = Reline::IOGate.cursor_pos.y
+ @rendered_screen.lines = []
+ @rendered_screen.cursor_y = 0
+ render_differential
+ end
+
+ private def handle_interrupted
+ return unless @interrupted
+
+ @interrupted = false
+ clear_dialogs
+ scrolldown = render_differential
+ Reline::IOGate.scroll_down scrolldown
+ Reline::IOGate.move_cursor_column 0
+ @rendered_screen.lines = []
+ @rendered_screen.cursor_y = 0
+ case @old_trap
+ when 'DEFAULT', 'SYSTEM_DEFAULT'
+ raise Interrupt
+ when 'IGNORE'
+ # Do nothing
+ when 'EXIT'
+ exit
else
- back = 0
- new_buffer = whole_lines
- prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer)
- new_buffer.each_with_index do |line, index|
- prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
- width = prompt_width + calculate_width(line)
- height = calculate_height_by_width(width)
- back += height
- end
- @highest_in_all = back
- @highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
- @first_line_started_from =
- if @line_index.zero?
- 0
- else
- calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
- end
- if @prompt_proc
- prompt = prompt_list[@line_index]
- prompt_width = calculate_width(prompt, true)
- end
- calculate_nearest_cursor
- @started_from = calculate_height_by_width(prompt_width + @cursor) - 1
- Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
- @highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
- @rerender_all = true
+ @old_trap.call if @old_trap.respond_to?(:call)
end
end
def set_signal_handlers
- @old_trap = Signal.trap('INT') {
- clear_dialog(0)
- if @scroll_partial_screen
- move_cursor_down(@screen_height - (@line_index - @scroll_partial_screen) - 1)
- else
- move_cursor_down(@highest_in_all - @line_index - 1)
- end
- Reline::IOGate.move_cursor_column(0)
- scroll_down(1)
- case @old_trap
- when 'DEFAULT', 'SYSTEM_DEFAULT'
- raise Interrupt
- when 'IGNORE'
- # Do nothing
- when 'EXIT'
- exit
- else
- @old_trap.call if @old_trap.respond_to?(:call)
- end
- }
+ Reline::IOGate.set_winch_handler do
+ @resized = true
+ end
+ @old_trap = Signal.trap('INT') do
+ @interrupted = true
+ end
end
def finalize
@@ -255,56 +226,42 @@ class Reline::LineEditor
@encoding = encoding
@is_multiline = false
@finished = false
- @cleared = false
- @rerender_all = false
@history_pointer = nil
@kill_ring ||= Reline::KillRing.new
@vi_clipboard = ''
@vi_arg = nil
@waiting_proc = nil
- @waiting_operator_proc = nil
- @waiting_operator_vi_arg = nil
- @completion_journey_data = nil
+ @vi_waiting_operator = nil
+ @vi_waiting_operator_arg = nil
+ @completion_journey_state = nil
@completion_state = CompletionState::NORMAL
+ @completion_occurs = false
@perfect_matched = nil
@menu_info = nil
- @first_prompt = true
@searching_prompt = nil
@first_char = true
- @add_newline_to_end_of_buffer = false
- @just_cursor_moving = nil
- @cached_prompt_list = nil
- @prompt_cache_time = nil
+ @just_cursor_moving = false
@eof = false
@continuous_insertion_buffer = String.new(encoding: @encoding)
- @scroll_partial_screen = nil
- @prev_mode_string = nil
+ @scroll_partial_screen = 0
@drop_terminate_spaces = false
@in_pasting = false
@auto_indent_proc = nil
@dialogs = []
- @previous_rendered_dialog_y = 0
- @last_key = nil
+ @interrupted = false
@resized = false
+ @cache = {}
+ @rendered_screen = RenderedScreen.new(base_y: 0, lines: [], cursor_y: 0)
reset_line
end
def reset_line
- @cursor = 0
- @cursor_max = 0
@byte_pointer = 0
@buffer_of_lines = [String.new(encoding: @encoding)]
@line_index = 0
- @previous_line_index = nil
- @line = @buffer_of_lines[0]
- @first_line_started_from = 0
- @move_up = 0
- @started_from = 0
- @highest_in_this = 1
- @highest_in_all = 1
+ @cache.clear
@line_backup_in_history = nil
@multibyte_buffer = String.new(encoding: 'ASCII-8BIT')
- @check_new_auto_indent = false
end
def multiline_on
@@ -315,68 +272,44 @@ class Reline::LineEditor
@is_multiline = false
end
- private def calculate_height_by_lines(lines, prompt)
- result = 0
- prompt_list = prompt.is_a?(Array) ? prompt : nil
- lines.each_with_index { |line, i|
- prompt = prompt_list[i] if prompt_list and prompt_list[i]
- result += calculate_height_by_width(calculate_width(prompt, true) + calculate_width(line))
- }
- result
- end
-
private def insert_new_line(cursor_line, next_line)
- @line = cursor_line
@buffer_of_lines.insert(@line_index + 1, String.new(next_line, encoding: @encoding))
- @previous_line_index = @line_index
+ @buffer_of_lines[@line_index] = cursor_line
@line_index += 1
- @just_cursor_moving = false
- end
-
- private def calculate_height_by_width(width)
- width.div(@screen_size.last) + 1
- end
-
- private def split_by_width(str, max_width)
- Reline::Unicode.split_by_width(str, max_width, @encoding)
- end
-
- private def scroll_down(val)
- if val <= @rest_height
- Reline::IOGate.move_cursor_down(val)
- @rest_height -= val
- else
- Reline::IOGate.move_cursor_down(@rest_height)
- Reline::IOGate.scroll_down(val - @rest_height)
- @rest_height = 0
+ @byte_pointer = 0
+ if @auto_indent_proc && !@in_pasting
+ if next_line.empty?
+ (
+ # For compatibility, use this calculation instead of just `process_auto_indent @line_index - 1, cursor_dependent: false`
+ indent1 = @auto_indent_proc.(@buffer_of_lines.take(@line_index - 1).push(''), @line_index - 1, 0, true)
+ indent2 = @auto_indent_proc.(@buffer_of_lines.take(@line_index), @line_index - 1, @buffer_of_lines[@line_index - 1].bytesize, false)
+ indent = indent2 || indent1
+ @buffer_of_lines[@line_index - 1] = ' ' * indent + @buffer_of_lines[@line_index - 1].gsub(/\A */, '')
+ )
+ process_auto_indent @line_index, add_newline: true
+ else
+ process_auto_indent @line_index - 1, cursor_dependent: false
+ process_auto_indent @line_index, add_newline: true # Need for compatibility
+ process_auto_indent @line_index, cursor_dependent: false
+ end
end
end
- private def move_cursor_up(val)
- if val > 0
- Reline::IOGate.move_cursor_up(val)
- @rest_height += val
- elsif val < 0
- move_cursor_down(-val)
- end
+ private def split_by_width(str, max_width, offset: 0)
+ Reline::Unicode.split_by_width(str, max_width, @encoding, offset: offset)
end
- private def move_cursor_down(val)
- if val > 0
- Reline::IOGate.move_cursor_down(val)
- @rest_height -= val
- @rest_height = 0 if @rest_height < 0
- elsif val < 0
- move_cursor_up(-val)
- end
+ def current_byte_pointer_cursor
+ calculate_width(current_line.byteslice(0, @byte_pointer))
end
- private def calculate_nearest_cursor(line_to_calc = @line, cursor = @cursor, started_from = @started_from, byte_pointer = @byte_pointer, update = true)
+ private def calculate_nearest_cursor(cursor)
+ line_to_calc = current_line
new_cursor_max = calculate_width(line_to_calc)
new_cursor = 0
new_byte_pointer = 0
height = 1
- max_width = @screen_size.last
+ max_width = screen_width
if @config.editing_mode_is?(:vi_command)
last_byte_size = Reline::Unicode.get_prev_mbchar_size(line_to_calc, line_to_calc.bytesize)
if last_byte_size > 0
@@ -402,113 +335,242 @@ class Reline::LineEditor
end
new_byte_pointer += gc.bytesize
end
- new_started_from = height - 1
- if update
- @cursor = new_cursor
- @cursor_max = new_cursor_max
- @started_from = new_started_from
- @byte_pointer = new_byte_pointer
- else
- [new_cursor, new_cursor_max, new_started_from, new_byte_pointer]
+ @byte_pointer = new_byte_pointer
+ end
+
+ def with_cache(key, *deps)
+ cached_deps, value = @cache[key]
+ if cached_deps != deps
+ @cache[key] = [deps, value = yield(*deps, cached_deps, value)]
end
+ value
end
- def rerender_all
- @rerender_all = true
- process_insert(force: true)
- rerender
+ def modified_lines
+ with_cache(__method__, whole_lines, finished?) do |whole, complete|
+ modify_lines(whole, complete)
+ end
end
- def rerender
- return if @line.nil?
- if @menu_info
- scroll_down(@highest_in_all - @first_line_started_from)
- @rerender_all = true
+ def prompt_list
+ with_cache(__method__, whole_lines, check_mode_string, @vi_arg, @searching_prompt) do |lines, mode_string|
+ check_multiline_prompt(lines, mode_string)
end
- if @menu_info
- show_menu
- @menu_info = nil
- end
- prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines)
- cursor_column = (prompt_width + @cursor) % @screen_size.last
- if @cleared
- clear_screen_buffer(prompt, prompt_list, prompt_width)
- @cleared = false
- return
+ end
+
+ def screen_height
+ @screen_size.first
+ end
+
+ def screen_width
+ @screen_size.last
+ end
+
+ def screen_scroll_top
+ @scroll_partial_screen
+ end
+
+ def wrapped_prompt_and_input_lines
+ with_cache(__method__, @buffer_of_lines.size, modified_lines, prompt_list, screen_width) do |n, lines, prompts, width, prev_cache_key, cached_value|
+ prev_n, prev_lines, prev_prompts, prev_width = prev_cache_key
+ cached_wraps = {}
+ if prev_width == width
+ prev_n.times do |i|
+ cached_wraps[[prev_prompts[i], prev_lines[i]]] = cached_value[i]
+ end
+ end
+
+ n.times.map do |i|
+ prompt = prompts[i] || ''
+ line = lines[i] || ''
+ if (cached = cached_wraps[[prompt, line]])
+ next cached
+ end
+ *wrapped_prompts, code_line_prompt = split_by_width(prompt, width).first.compact
+ wrapped_lines = split_by_width(line, width, offset: calculate_width(code_line_prompt)).first.compact
+ wrapped_prompts.map { |p| [p, ''] } + [[code_line_prompt, wrapped_lines.first]] + wrapped_lines.drop(1).map { |c| ['', c] }
+ end
end
- if @is_multiline and finished? and @scroll_partial_screen
- # Re-output all code higher than the screen when finished.
- Reline::IOGate.move_cursor_up(@first_line_started_from + @started_from - @scroll_partial_screen)
- Reline::IOGate.move_cursor_column(0)
- @scroll_partial_screen = nil
- new_lines = whole_lines
- prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines)
- modify_lines(new_lines).each_with_index do |line, index|
- @output.write "#{prompt_list ? prompt_list[index] : prompt}#{line}\r\n"
- Reline::IOGate.erase_after_cursor
- end
- @output.flush
- clear_dialog(cursor_column)
- return
+ end
+
+ def calculate_overlay_levels(overlay_levels)
+ levels = []
+ overlay_levels.each do |x, w, l|
+ levels.fill(l, x, w)
end
- new_highest_in_this = calculate_height_by_width(prompt_width + calculate_width(@line.nil? ? '' : @line))
- rendered = false
- if @add_newline_to_end_of_buffer
- clear_dialog_with_trap_key(cursor_column)
- rerender_added_newline(prompt, prompt_width, prompt_list)
- @add_newline_to_end_of_buffer = false
- else
- if @just_cursor_moving and not @rerender_all
- clear_dialog_with_trap_key(cursor_column)
- rendered = just_move_cursor
- @just_cursor_moving = false
- return
- elsif @previous_line_index or new_highest_in_this != @highest_in_this
- clear_dialog_with_trap_key(cursor_column)
- rerender_changed_current_line
- @previous_line_index = nil
- rendered = true
- elsif @rerender_all
- rerender_all_lines
- @rerender_all = false
- rendered = true
+ levels
+ end
+
+ def render_line_differential(old_items, new_items)
+ old_levels = calculate_overlay_levels(old_items.zip(new_items).each_with_index.map {|((x, w, c), (nx, _nw, nc)), i| [x, w, c == nc && x == nx ? i : -1] if x }.compact)
+ new_levels = calculate_overlay_levels(new_items.each_with_index.map { |(x, w), i| [x, w, i] if x }.compact).take(screen_width)
+ base_x = 0
+ new_levels.zip(old_levels).chunk { |n, o| n == o ? :skip : n || :blank }.each do |level, chunk|
+ width = chunk.size
+ if level == :skip
+ # do nothing
+ elsif level == :blank
+ Reline::IOGate.move_cursor_column base_x
+ @output.write "#{Reline::IOGate::RESET_COLOR}#{' ' * width}"
else
+ x, w, content = new_items[level]
+ cover_begin = base_x != 0 && new_levels[base_x - 1] == level
+ cover_end = new_levels[base_x + width] == level
+ pos = 0
+ unless x == base_x && w == width
+ content, pos = Reline::Unicode.take_mbchar_range(content, base_x - x, width, cover_begin: cover_begin, cover_end: cover_end, padding: true)
+ end
+ Reline::IOGate.move_cursor_column x + pos
+ @output.write "#{Reline::IOGate::RESET_COLOR}#{content}#{Reline::IOGate::RESET_COLOR}"
end
+ base_x += width
end
- if @is_multiline
- if finished?
- # Always rerender on finish because output_modifier_proc may return a different output.
- new_lines = whole_lines
- line = modify_lines(new_lines)[@line_index]
- clear_dialog(cursor_column)
- prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines)
- render_partial(prompt, prompt_width, line, @first_line_started_from)
- move_cursor_down(@highest_in_all - (@first_line_started_from + @highest_in_this - 1) - 1)
- scroll_down(1)
- Reline::IOGate.move_cursor_column(0)
- Reline::IOGate.erase_after_cursor
- else
- if not rendered and not @in_pasting
- line = modify_lines(whole_lines)[@line_index]
- prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines)
- render_partial(prompt, prompt_width, line, @first_line_started_from)
- end
- render_dialog(cursor_column)
+ if old_levels.size > new_levels.size
+ Reline::IOGate.move_cursor_column new_levels.size
+ Reline::IOGate.erase_after_cursor
+ end
+ end
+
+ # Calculate cursor position in word wrapped content.
+ def wrapped_cursor_position
+ prompt_width = calculate_width(prompt_list[@line_index], true)
+ line_before_cursor = whole_lines[@line_index].byteslice(0, @byte_pointer)
+ wrapped_line_before_cursor = split_by_width(' ' * prompt_width + line_before_cursor, screen_width).first.compact
+ wrapped_cursor_y = wrapped_prompt_and_input_lines[0...@line_index].sum(&:size) + wrapped_line_before_cursor.size - 1
+ wrapped_cursor_x = calculate_width(wrapped_line_before_cursor.last)
+ [wrapped_cursor_x, wrapped_cursor_y]
+ end
+
+ def clear_dialogs
+ @dialogs.each do |dialog|
+ dialog.contents = nil
+ dialog.trap_key = nil
+ end
+ end
+
+ def update_dialogs(key = nil)
+ wrapped_cursor_x, wrapped_cursor_y = wrapped_cursor_position
+ @dialogs.each do |dialog|
+ dialog.trap_key = nil
+ update_each_dialog(dialog, wrapped_cursor_x, wrapped_cursor_y - screen_scroll_top, key)
+ end
+ end
+
+ def render_finished
+ clear_rendered_lines
+ render_full_content
+ end
+
+ def clear_rendered_lines
+ Reline::IOGate.move_cursor_up @rendered_screen.cursor_y
+ Reline::IOGate.move_cursor_column 0
+
+ num_lines = @rendered_screen.lines.size
+ return unless num_lines && num_lines >= 1
+
+ Reline::IOGate.move_cursor_down num_lines - 1
+ (num_lines - 1).times do
+ Reline::IOGate.erase_after_cursor
+ Reline::IOGate.move_cursor_up 1
+ end
+ Reline::IOGate.erase_after_cursor
+ @rendered_screen.lines = []
+ @rendered_screen.cursor_y = 0
+ end
+
+ def render_full_content
+ lines = @buffer_of_lines.size.times.map do |i|
+ line = prompt_list[i] + modified_lines[i]
+ wrapped_lines, = split_by_width(line, screen_width)
+ wrapped_lines.last.empty? ? "#{line} " : line
+ end
+ @output.puts lines.map { |l| "#{l}\r\n" }.join
+ end
+
+ def print_nomultiline_prompt(prompt)
+ return unless prompt && !@is_multiline
+
+ # Readline's test `TestRelineAsReadline#test_readline` requires first output to be prompt, not cursor reset escape sequence.
+ @rendered_screen.lines = [[[0, Reline::Unicode.calculate_width(prompt, true), prompt]]]
+ @rendered_screen.cursor_y = 0
+ @output.write prompt
+ end
+
+ def render_differential
+ wrapped_cursor_x, wrapped_cursor_y = wrapped_cursor_position
+
+ rendered_lines = @rendered_screen.lines
+ new_lines = wrapped_prompt_and_input_lines.flatten(1)[screen_scroll_top, screen_height].map do |prompt, line|
+ prompt_width = Reline::Unicode.calculate_width(prompt, true)
+ [[0, prompt_width, prompt], [prompt_width, Reline::Unicode.calculate_width(line, true), line]]
+ end
+ if @menu_info
+ @menu_info.lines(screen_width).each do |item|
+ new_lines << [[0, Reline::Unicode.calculate_width(item), item]]
end
- @buffer_of_lines[@line_index] = @line
- @rest_height = 0 if @scroll_partial_screen
- else
- line = modify_lines(whole_lines)[@line_index]
- render_partial(prompt, prompt_width, line, 0)
- if finished?
- scroll_down(1)
- Reline::IOGate.move_cursor_column(0)
- Reline::IOGate.erase_after_cursor
+ @menu_info = nil # TODO: do not change state here
+ end
+
+ @dialogs.each_with_index do |dialog, index|
+ next unless dialog.contents
+
+ x_range, y_range = dialog_range dialog, wrapped_cursor_y - screen_scroll_top
+ y_range.each do |row|
+ next if row < 0 || row >= screen_height
+ dialog_rows = new_lines[row] ||= []
+ # index 0 is for prompt, index 1 is for line, index 2.. is for dialog
+ dialog_rows[index + 2] = [x_range.begin, dialog.width, dialog.contents[row - y_range.begin]]
end
end
+
+ cursor_y = @rendered_screen.cursor_y
+ if new_lines != rendered_lines
+ # Hide cursor while rendering to avoid cursor flickering.
+ Reline::IOGate.hide_cursor
+ num_lines = [[new_lines.size, rendered_lines.size].max, screen_height].min
+ if @rendered_screen.base_y + num_lines > screen_height
+ Reline::IOGate.scroll_down(num_lines - cursor_y - 1)
+ @rendered_screen.base_y = screen_height - num_lines
+ cursor_y = num_lines - 1
+ end
+ num_lines.times do |i|
+ rendered_line = rendered_lines[i] || []
+ line_to_render = new_lines[i] || []
+ next if rendered_line == line_to_render
+
+ Reline::IOGate.move_cursor_down i - cursor_y
+ cursor_y = i
+ unless rendered_lines[i]
+ Reline::IOGate.move_cursor_column 0
+ Reline::IOGate.erase_after_cursor
+ end
+ render_line_differential(rendered_line, line_to_render)
+ end
+ @rendered_screen.lines = new_lines
+ Reline::IOGate.show_cursor
+ end
+ y = wrapped_cursor_y - screen_scroll_top
+ Reline::IOGate.move_cursor_column wrapped_cursor_x
+ Reline::IOGate.move_cursor_down y - cursor_y
+ @rendered_screen.cursor_y = y
+ new_lines.size - y
+ end
+
+ def upper_space_height(wrapped_cursor_y)
+ wrapped_cursor_y - screen_scroll_top
+ end
+
+ def rest_height(wrapped_cursor_y)
+ screen_height - wrapped_cursor_y + screen_scroll_top - @rendered_screen.base_y - 1
+ end
+
+ def rerender
+ render_differential unless @in_pasting
end
class DialogProcScope
+ CompletionJourneyData = Struct.new(:preposing, :postposing, :list, :pointer)
+
def initialize(line_editor, config, proc_to_exec, context)
@line_editor = line_editor
@config = config
@@ -559,21 +621,20 @@ class Reline::LineEditor
end
def screen_width
- @line_editor.instance_variable_get(:@screen_size).last
+ @line_editor.screen_width
end
def screen_height
- @line_editor.instance_variable_get(:@screen_size).first
+ @line_editor.screen_height
end
def preferred_dialog_height
- rest_height = @line_editor.instance_variable_get(:@rest_height)
- scroll_partial_screen = @line_editor.instance_variable_get(:@scroll_partial_screen) || 0
- [cursor_pos.y - scroll_partial_screen, rest_height, (screen_height + 6) / 5].max
+ _wrapped_cursor_x, wrapped_cursor_y = @line_editor.wrapped_cursor_position
+ [@line_editor.upper_space_height(wrapped_cursor_y), @line_editor.rest_height(wrapped_cursor_y), (screen_height + 6) / 5].max
end
def completion_journey_data
- @line_editor.instance_variable_get(:@completion_journey_data)
+ @line_editor.dialog_proc_scope_completion_journey_data
end
def config
@@ -642,27 +703,6 @@ class Reline::LineEditor
end
DIALOG_DEFAULT_HEIGHT = 20
- private def render_dialog(cursor_column)
- changes = @dialogs.map do |dialog|
- old_dialog = dialog.dup
- update_each_dialog(dialog, cursor_column)
- [old_dialog, dialog]
- end
- render_dialog_changes(changes, cursor_column)
- end
-
- private def padding_space_with_escape_sequences(str, width)
- padding_width = width - calculate_width(str, true)
- # padding_width should be only positive value. But macOS and Alacritty returns negative value.
- padding_width = 0 if padding_width < 0
- str + (' ' * padding_width)
- end
-
- private def range_subtract(base_ranges, subtract_ranges)
- indices = base_ranges.flat_map(&:to_a).uniq.sort - subtract_ranges.flat_map(&:to_a)
- chunks = indices.chunk_while { |a, b| a + 1 == b }
- chunks.map { |a| a.first...a.last + 1 }
- end
private def dialog_range(dialog, dialog_y)
x_range = dialog.column...dialog.column + dialog.width
@@ -670,106 +710,9 @@ class Reline::LineEditor
[x_range, y_range]
end
- private def render_dialog_changes(changes, cursor_column)
- # Collect x-coordinate range and content of previous and current dialogs for each line
- old_dialog_ranges = {}
- new_dialog_ranges = {}
- new_dialog_contents = {}
- changes.each do |old_dialog, new_dialog|
- if old_dialog.contents
- x_range, y_range = dialog_range(old_dialog, @previous_rendered_dialog_y)
- y_range.each do |y|
- (old_dialog_ranges[y] ||= []) << x_range
- end
- end
- if new_dialog.contents
- x_range, y_range = dialog_range(new_dialog, @first_line_started_from + @started_from)
- y_range.each do |y|
- (new_dialog_ranges[y] ||= []) << x_range
- (new_dialog_contents[y] ||= []) << [x_range, new_dialog.contents[y - y_range.begin]]
- end
- end
- end
- return if old_dialog_ranges.empty? && new_dialog_ranges.empty?
-
- # Calculate x-coordinate ranges to restore text that was hidden behind dialogs for each line
- ranges_to_restore = {}
- subtract_cache = {}
- old_dialog_ranges.each do |y, old_x_ranges|
- new_x_ranges = new_dialog_ranges[y] || []
- ranges = subtract_cache[[old_x_ranges, new_x_ranges]] ||= range_subtract(old_x_ranges, new_x_ranges)
- ranges_to_restore[y] = ranges if ranges.any?
- end
-
- # Create visual_lines for restoring text hidden behind dialogs
- if ranges_to_restore.any?
- lines = whole_lines
- prompt, _prompt_width, prompt_list = check_multiline_prompt(lines, force_recalc: true)
- modified_lines = modify_lines(lines, force_recalc: true)
- visual_lines = []
- modified_lines.each_with_index { |l, i|
- pr = prompt_list ? prompt_list[i] : prompt
- vl, = split_by_width(pr + l, @screen_size.last)
- vl.compact!
- visual_lines.concat(vl)
- }
- end
-
- # Clear and rerender all dialogs line by line
- Reline::IOGate.hide_cursor
- ymin, ymax = (ranges_to_restore.keys + new_dialog_ranges.keys).minmax
- scroll_partial_screen = @scroll_partial_screen || 0
- screen_y_range = scroll_partial_screen..(scroll_partial_screen + @screen_height - 1)
- ymin = ymin.clamp(screen_y_range.begin, screen_y_range.end)
- ymax = ymax.clamp(screen_y_range.begin, screen_y_range.end)
- dialog_y = @first_line_started_from + @started_from
- cursor_y = dialog_y
- if @highest_in_all <= ymax
- scroll_down(ymax - cursor_y)
- move_cursor_up(ymax - cursor_y)
- end
- (ymin..ymax).each do |y|
- move_cursor_down(y - cursor_y)
- cursor_y = y
- new_x_ranges = new_dialog_ranges[y]
- restore_ranges = ranges_to_restore[y]
- # Restore text that was hidden behind dialogs
- if restore_ranges
- line = visual_lines[y] || ''
- restore_ranges.each do |range|
- col = range.begin
- width = range.end - range.begin
- s = padding_space_with_escape_sequences(Reline::Unicode.take_range(line, col, width), width)
- Reline::IOGate.move_cursor_column(col)
- @output.write "\e[0m#{s}\e[0m"
- end
- max_column = [calculate_width(line, true), new_x_ranges&.map(&:end)&.max || 0].max
- if max_column < restore_ranges.map(&:end).max
- Reline::IOGate.move_cursor_column(max_column)
- Reline::IOGate.erase_after_cursor
- end
- end
- # Render dialog contents
- new_dialog_contents[y]&.each do |x_range, content|
- Reline::IOGate.move_cursor_column(x_range.begin)
- @output.write "\e[0m#{content}\e[0m"
- end
- end
- move_cursor_up(cursor_y - dialog_y)
- Reline::IOGate.move_cursor_column(cursor_column)
- Reline::IOGate.show_cursor
-
- @previous_rendered_dialog_y = dialog_y
- end
-
- private def update_each_dialog(dialog, cursor_column)
- if @in_pasting
- dialog.contents = nil
- dialog.trap_key = nil
- return
- end
- dialog.set_cursor_pos(cursor_column, @first_line_started_from + @started_from)
- dialog_render_info = dialog.call(@last_key)
+ private def update_each_dialog(dialog, cursor_column, cursor_row, key = nil)
+ dialog.set_cursor_pos(cursor_column, cursor_row)
+ dialog_render_info = dialog.call(key)
if dialog_render_info.nil? or dialog_render_info.contents.nil? or dialog_render_info.contents.empty?
dialog.contents = nil
dialog.trap_key = nil
@@ -809,45 +752,41 @@ class Reline::LineEditor
else
scrollbar_pos = nil
end
- upper_space = @first_line_started_from - @started_from
dialog.column = dialog_render_info.pos.x
dialog.width += @block_elem_width if scrollbar_pos
- diff = (dialog.column + dialog.width) - (@screen_size.last)
+ diff = (dialog.column + dialog.width) - screen_width
if diff > 0
dialog.column -= diff
end
- if (@rest_height - dialog_render_info.pos.y) >= height
+ if rest_height(screen_scroll_top + cursor_row) - dialog_render_info.pos.y >= height
dialog.vertical_offset = dialog_render_info.pos.y + 1
- elsif upper_space >= height
+ elsif cursor_row >= height
dialog.vertical_offset = dialog_render_info.pos.y - height
else
dialog.vertical_offset = dialog_render_info.pos.y + 1
end
if dialog.column < 0
dialog.column = 0
- dialog.width = @screen_size.last
+ dialog.width = screen_width
end
+ face = Reline::Face[dialog_render_info.face || :default]
+ scrollbar_sgr = face[:scrollbar]
+ default_sgr = face[:default]
+ enhanced_sgr = face[:enhanced]
dialog.contents = contents.map.with_index do |item, i|
- if i == pointer
- fg_color = dialog_render_info.pointer_fg_color
- bg_color = dialog_render_info.pointer_bg_color
- else
- fg_color = dialog_render_info.fg_color
- bg_color = dialog_render_info.bg_color
- end
+ line_sgr = i == pointer ? enhanced_sgr : default_sgr
str_width = dialog.width - (scrollbar_pos.nil? ? 0 : @block_elem_width)
- str = padding_space_with_escape_sequences(Reline::Unicode.take_range(item, 0, str_width), str_width)
- colored_content = "\e[#{bg_color}m\e[#{fg_color}m#{str}"
+ str, = Reline::Unicode.take_mbchar_range(item, 0, str_width, padding: true)
+ colored_content = "#{line_sgr}#{str}"
if scrollbar_pos
- color_seq = "\e[37m"
if scrollbar_pos <= (i * 2) and (i * 2 + 1) < (scrollbar_pos + bar_height)
- colored_content + color_seq + @full_block
+ colored_content + scrollbar_sgr + @full_block
elsif scrollbar_pos <= (i * 2) and (i * 2) < (scrollbar_pos + bar_height)
- colored_content + color_seq + @upper_half_block
+ colored_content + scrollbar_sgr + @upper_half_block
elsif scrollbar_pos <= (i * 2 + 1) and (i * 2) < (scrollbar_pos + bar_height)
- colored_content + color_seq + @lower_half_block
+ colored_content + scrollbar_sgr + @lower_half_block
else
- colored_content + color_seq + ' ' * @block_elem_width
+ colored_content + scrollbar_sgr + ' ' * @block_elem_width
end
else
colored_content
@@ -855,379 +794,20 @@ class Reline::LineEditor
end
end
- private def clear_dialog(cursor_column)
- changes = @dialogs.map do |dialog|
- old_dialog = dialog.dup
- dialog.contents = nil
- [old_dialog, dialog]
- end
- render_dialog_changes(changes, cursor_column)
- end
-
- private def clear_dialog_with_trap_key(cursor_column)
- clear_dialog(cursor_column)
- @dialogs.each do |dialog|
- dialog.trap_key = nil
- end
- end
-
- private def calculate_scroll_partial_screen(highest_in_all, cursor_y)
- if @screen_height < highest_in_all
- old_scroll_partial_screen = @scroll_partial_screen
- if cursor_y == 0
- @scroll_partial_screen = 0
- elsif cursor_y == (highest_in_all - 1)
- @scroll_partial_screen = highest_in_all - @screen_height
- else
- if @scroll_partial_screen
- if cursor_y <= @scroll_partial_screen
- @scroll_partial_screen = cursor_y
- elsif (@scroll_partial_screen + @screen_height - 1) < cursor_y
- @scroll_partial_screen = cursor_y - (@screen_height - 1)
- end
- else
- if cursor_y > (@screen_height - 1)
- @scroll_partial_screen = cursor_y - (@screen_height - 1)
- else
- @scroll_partial_screen = 0
- end
- end
- end
- if @scroll_partial_screen != old_scroll_partial_screen
- @rerender_all = true
- end
- else
- if @scroll_partial_screen
- @rerender_all = true
- end
- @scroll_partial_screen = nil
- end
- end
-
- private def rerender_added_newline(prompt, prompt_width, prompt_list)
- @buffer_of_lines[@previous_line_index] = @line
- @line = @buffer_of_lines[@line_index]
- @previous_line_index = nil
- if @in_pasting
- scroll_down(1)
- else
- lines = whole_lines
- prev_line_prompt = @prompt_proc ? prompt_list[@line_index - 1] : prompt
- prev_line_prompt_width = @prompt_proc ? calculate_width(prev_line_prompt, true) : prompt_width
- prev_line = modify_lines(lines)[@line_index - 1]
- move_cursor_up(@started_from)
- render_partial(prev_line_prompt, prev_line_prompt_width, prev_line, @first_line_started_from + @started_from, with_control: false)
- scroll_down(1)
- render_partial(prompt, prompt_width, @line, @first_line_started_from + @started_from + 1, with_control: false)
- end
- @cursor = @cursor_max = calculate_width(@line)
- @byte_pointer = @line.bytesize
- @highest_in_all += @highest_in_this
- @highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
- @first_line_started_from += @started_from + 1
- @started_from = calculate_height_by_width(prompt_width + @cursor) - 1
- end
-
- def just_move_cursor
- prompt, prompt_width, prompt_list = check_multiline_prompt(@buffer_of_lines)
- move_cursor_up(@started_from)
- new_first_line_started_from =
- if @line_index.zero?
- 0
- else
- calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
- end
- first_line_diff = new_first_line_started_from - @first_line_started_from
- @cursor, @cursor_max, _, @byte_pointer = calculate_nearest_cursor(@buffer_of_lines[@line_index], @cursor, @started_from, @byte_pointer, false)
- new_started_from = calculate_height_by_width(prompt_width + @cursor) - 1
- calculate_scroll_partial_screen(@highest_in_all, new_first_line_started_from + new_started_from)
- @previous_line_index = nil
- @line = @buffer_of_lines[@line_index]
- if @rerender_all
- rerender_all_lines
- @rerender_all = false
- true
- else
- @first_line_started_from = new_first_line_started_from
- @started_from = new_started_from
- move_cursor_down(first_line_diff + @started_from)
- Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
- false
- end
- end
-
- private def rerender_changed_current_line
- new_lines = whole_lines
- prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines)
- all_height = calculate_height_by_lines(new_lines, prompt_list || prompt)
- diff = all_height - @highest_in_all
- move_cursor_down(@highest_in_all - @first_line_started_from - @started_from - 1)
- if diff > 0
- scroll_down(diff)
- move_cursor_up(all_height - 1)
- elsif diff < 0
- (-diff).times do
- Reline::IOGate.move_cursor_column(0)
- Reline::IOGate.erase_after_cursor
- move_cursor_up(1)
- end
- move_cursor_up(all_height - 1)
- else
- move_cursor_up(all_height - 1)
- end
- @highest_in_all = all_height
- back = render_whole_lines(new_lines, prompt_list || prompt, prompt_width)
- move_cursor_up(back)
- if @previous_line_index
- @buffer_of_lines[@previous_line_index] = @line
- @line = @buffer_of_lines[@line_index]
- end
- @first_line_started_from =
- if @line_index.zero?
- 0
- else
- calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
- end
- if @prompt_proc
- prompt = prompt_list[@line_index]
- prompt_width = calculate_width(prompt, true)
- end
- move_cursor_down(@first_line_started_from)
- calculate_nearest_cursor
- @started_from = calculate_height_by_width(prompt_width + @cursor) - 1
- move_cursor_down(@started_from)
- Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
- @highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
- end
-
- private def rerender_all_lines
- move_cursor_up(@first_line_started_from + @started_from)
- Reline::IOGate.move_cursor_column(0)
- back = 0
- new_buffer = whole_lines
- prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer)
- new_buffer.each_with_index do |line, index|
- prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
- width = prompt_width + calculate_width(line)
- height = calculate_height_by_width(width)
- back += height
- end
- old_highest_in_all = @highest_in_all
- if @line_index.zero?
- new_first_line_started_from = 0
- else
- new_first_line_started_from = calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list || prompt)
- end
- new_started_from = calculate_height_by_width(prompt_width + @cursor) - 1
- calculate_scroll_partial_screen(back, new_first_line_started_from + new_started_from)
- if @scroll_partial_screen
- move_cursor_up(@first_line_started_from + @started_from)
- scroll_down(@screen_height - 1)
- move_cursor_up(@screen_height)
- Reline::IOGate.move_cursor_column(0)
- elsif back > old_highest_in_all
- scroll_down(back - 1)
- move_cursor_up(back - 1)
- elsif back < old_highest_in_all
- scroll_down(back)
- Reline::IOGate.erase_after_cursor
- (old_highest_in_all - back - 1).times do
- scroll_down(1)
- Reline::IOGate.erase_after_cursor
- end
- move_cursor_up(old_highest_in_all - 1)
- end
- render_whole_lines(new_buffer, prompt_list || prompt, prompt_width)
- if @prompt_proc
- prompt = prompt_list[@line_index]
- prompt_width = calculate_width(prompt, true)
- end
- @highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
- @highest_in_all = back
- @first_line_started_from = new_first_line_started_from
- @started_from = new_started_from
- if @scroll_partial_screen
- Reline::IOGate.move_cursor_up(@screen_height - (@first_line_started_from + @started_from - @scroll_partial_screen) - 1)
- Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
- else
- move_cursor_down(@first_line_started_from + @started_from - back + 1)
- Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
- end
- end
-
- private def render_whole_lines(lines, prompt, prompt_width)
- rendered_height = 0
- modify_lines(lines).each_with_index do |line, index|
- if prompt.is_a?(Array)
- line_prompt = prompt[index]
- prompt_width = calculate_width(line_prompt, true)
- else
- line_prompt = prompt
- end
- height = render_partial(line_prompt, prompt_width, line, rendered_height, with_control: false)
- if index < (lines.size - 1)
- if @scroll_partial_screen
- if (@scroll_partial_screen - height) < rendered_height and (@scroll_partial_screen + @screen_height - 1) >= (rendered_height + height)
- move_cursor_down(1)
- end
- else
- scroll_down(1)
- end
- rendered_height += height
- else
- rendered_height += height - 1
- end
- end
- rendered_height
- end
-
- private def render_partial(prompt, prompt_width, line_to_render, this_started_from, with_control: true)
- visual_lines, height = split_by_width(line_to_render.nil? ? prompt : prompt + line_to_render, @screen_size.last)
- cursor_up_from_last_line = 0
- if @scroll_partial_screen
- last_visual_line = this_started_from + (height - 1)
- last_screen_line = @scroll_partial_screen + (@screen_height - 1)
- if (@scroll_partial_screen - this_started_from) >= height
- # Render nothing because this line is before the screen.
- visual_lines = []
- elsif this_started_from > last_screen_line
- # Render nothing because this line is after the screen.
- visual_lines = []
- else
- deleted_lines_before_screen = []
- if @scroll_partial_screen > this_started_from and last_visual_line >= @scroll_partial_screen
- # A part of visual lines are before the screen.
- deleted_lines_before_screen = visual_lines.shift((@scroll_partial_screen - this_started_from) * 2)
- deleted_lines_before_screen.compact!
- end
- if this_started_from <= last_screen_line and last_screen_line < last_visual_line
- # A part of visual lines are after the screen.
- visual_lines.pop((last_visual_line - last_screen_line) * 2)
- end
- move_cursor_up(deleted_lines_before_screen.size - @started_from)
- cursor_up_from_last_line = @started_from - deleted_lines_before_screen.size
- end
- end
- if with_control
- if height > @highest_in_this
- diff = height - @highest_in_this
- scroll_down(diff)
- @highest_in_all += diff
- @highest_in_this = height
- move_cursor_up(diff)
- elsif height < @highest_in_this
- diff = @highest_in_this - height
- @highest_in_all -= diff
- @highest_in_this = height
- end
- move_cursor_up(@started_from)
- @started_from = calculate_height_by_width(prompt_width + @cursor) - 1
- cursor_up_from_last_line = height - 1 - @started_from
- end
- if Reline::Unicode::CSI_REGEXP.match?(prompt + line_to_render)
- @output.write "\e[0m" # clear character decorations
- end
- visual_lines.each_with_index do |line, index|
- Reline::IOGate.move_cursor_column(0)
- if line.nil?
- if calculate_width(visual_lines[index - 1], true) == Reline::IOGate.get_screen_size.last
- # reaches the end of line
- if Reline::IOGate.win? and Reline::IOGate.win_legacy_console?
- # A newline is automatically inserted if a character is rendered at
- # eol on command prompt.
- else
- # When the cursor is at the end of the line and erases characters
- # after the cursor, some terminals delete the character at the
- # cursor position.
- move_cursor_down(1)
- Reline::IOGate.move_cursor_column(0)
- end
- else
- Reline::IOGate.erase_after_cursor
- move_cursor_down(1)
- Reline::IOGate.move_cursor_column(0)
- end
- next
- end
- @output.write line
- if Reline::IOGate.win? and Reline::IOGate.win_legacy_console? and calculate_width(line, true) == Reline::IOGate.get_screen_size.last
- # A newline is automatically inserted if a character is rendered at eol on command prompt.
- @rest_height -= 1 if @rest_height > 0
- end
- @output.flush
- if @first_prompt
- @first_prompt = false
- @pre_input_hook&.call
- end
- end
- unless visual_lines.empty?
- Reline::IOGate.erase_after_cursor
- Reline::IOGate.move_cursor_column(0)
- end
- if with_control
- # Just after rendring, so the cursor is on the last line.
- if finished?
- Reline::IOGate.move_cursor_column(0)
- else
- # Moves up from bottom of lines to the cursor position.
- move_cursor_up(cursor_up_from_last_line)
- # This logic is buggy if a fullwidth char is wrapped because there is only one halfwidth at end of a line.
- Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
- end
- end
- height
- end
-
- private def modify_lines(before, force_recalc: false)
- return before if !force_recalc && (before.nil? || before.empty? || simplified_rendering?)
-
- if after = @output_modifier_proc&.call("#{before.join("\n")}\n", complete: finished?)
+ private def modify_lines(before, complete)
+ if after = @output_modifier_proc&.call("#{before.join("\n")}\n", complete: complete)
after.lines("\n").map { |l| l.chomp('') }
else
- before
- end
- end
-
- private def show_menu
- scroll_down(@highest_in_all - @first_line_started_from)
- @rerender_all = true
- @menu_info.list.sort!.each do |item|
- Reline::IOGate.move_cursor_column(0)
- @output.write item
- @output.flush
- scroll_down(1)
- end
- scroll_down(@highest_in_all - 1)
- move_cursor_up(@highest_in_all - 1 - @first_line_started_from)
- end
-
- private def clear_screen_buffer(prompt, prompt_list, prompt_width)
- Reline::IOGate.clear_screen
- back = 0
- modify_lines(whole_lines).each_with_index do |line, index|
- if @prompt_proc
- pr = prompt_list[index]
- height = render_partial(pr, calculate_width(pr), line, back, with_control: false)
- else
- height = render_partial(prompt, prompt_width, line, back, with_control: false)
- end
- if index < (@buffer_of_lines.size - 1)
- move_cursor_down(1)
- back += height
- end
+ before.map { |l| Reline::Unicode.escape_for_print(l) }
end
- move_cursor_up(back)
- move_cursor_down(@first_line_started_from + @started_from)
- @rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
- Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
end
def editing_mode
@config.editing_mode
end
- private def menu(target, list)
- @menu_info = MenuInfo.new(target, list)
+ private def menu(_target, list)
+ @menu_info = MenuInfo.new(list)
end
private def complete_internal_proc(list, is_menu)
@@ -1255,7 +835,7 @@ class Reline::LineEditor
item_mbchars = item.grapheme_clusters
end
size = [memo_mbchars.size, item_mbchars.size].min
- result = ''
+ result = +''
size.times do |i|
if @config.completion_ignore_case
if memo_mbchars[i].casecmp?(item_mbchars[i])
@@ -1276,9 +856,9 @@ class Reline::LineEditor
[target, preposing, completed, postposing]
end
- private def complete(list, just_show_list = false)
+ private def complete(list, just_show_list)
case @completion_state
- when CompletionState::NORMAL, CompletionState::JOURNEY
+ when CompletionState::NORMAL
@completion_state = CompletionState::COMPLETION
when CompletionState::PERFECT_MATCH
@dig_perfect_match_proc&.(@perfect_matched)
@@ -1305,100 +885,79 @@ class Reline::LineEditor
@completion_state = CompletionState::PERFECT_MATCH
else
@completion_state = CompletionState::MENU_WITH_PERFECT_MATCH
+ complete(list, true) if @config.show_all_if_ambiguous
end
@perfect_matched = completed
else
@completion_state = CompletionState::MENU
+ complete(list, true) if @config.show_all_if_ambiguous
end
if not just_show_list and target < completed
- @line = (preposing + completed + completion_append_character.to_s + postposing).split("\n")[@line_index] || String.new(encoding: @encoding)
- line_to_pointer = (preposing + completed + completion_append_character.to_s).split("\n").last || String.new(encoding: @encoding)
- @cursor_max = calculate_width(@line)
- @cursor = calculate_width(line_to_pointer)
+ @buffer_of_lines[@line_index] = (preposing + completed + completion_append_character.to_s + postposing).split("\n")[@line_index] || String.new(encoding: @encoding)
+ line_to_pointer = (preposing + completed + completion_append_character.to_s).split("\n")[@line_index] || String.new(encoding: @encoding)
@byte_pointer = line_to_pointer.bytesize
end
end
end
- private def move_completed_list(list, direction)
- case @completion_state
- when CompletionState::NORMAL, CompletionState::COMPLETION,
- CompletionState::MENU, CompletionState::MENU_WITH_PERFECT_MATCH
- @completion_state = CompletionState::JOURNEY
- result = retrieve_completion_block
- return if result.nil?
- preposing, target, postposing = result
- @completion_journey_data = CompletionJourneyData.new(
- preposing, postposing,
- [target] + list.select{ |item| item.start_with?(target) }, 0)
- if @completion_journey_data.list.size == 1
- @completion_journey_data.pointer = 0
- else
- case direction
- when :up
- @completion_journey_data.pointer = @completion_journey_data.list.size - 1
- when :down
- @completion_journey_data.pointer = 1
- end
- end
- @completion_state = CompletionState::JOURNEY
- else
- case direction
- when :up
- @completion_journey_data.pointer -= 1
- if @completion_journey_data.pointer < 0
- @completion_journey_data.pointer = @completion_journey_data.list.size - 1
- end
- when :down
- @completion_journey_data.pointer += 1
- if @completion_journey_data.pointer >= @completion_journey_data.list.size
- @completion_journey_data.pointer = 0
- end
- end
+ def dialog_proc_scope_completion_journey_data
+ return nil unless @completion_journey_state
+ line_index = @completion_journey_state.line_index
+ pre_lines = @buffer_of_lines[0...line_index].map { |line| line + "\n" }
+ post_lines = @buffer_of_lines[(line_index + 1)..-1].map { |line| line + "\n" }
+ DialogProcScope::CompletionJourneyData.new(
+ pre_lines.join + @completion_journey_state.pre,
+ @completion_journey_state.post + post_lines.join,
+ @completion_journey_state.list,
+ @completion_journey_state.pointer
+ )
+ end
+
+ private def move_completed_list(direction)
+ @completion_journey_state ||= retrieve_completion_journey_state
+ return false unless @completion_journey_state
+
+ if (delta = { up: -1, down: +1 }[direction])
+ @completion_journey_state.pointer = (@completion_journey_state.pointer + delta) % @completion_journey_state.list.size
end
- completed = @completion_journey_data.list[@completion_journey_data.pointer]
- new_line = (@completion_journey_data.preposing + completed + @completion_journey_data.postposing).split("\n")[@line_index]
- @line = new_line.nil? ? String.new(encoding: @encoding) : new_line
- line_to_pointer = (@completion_journey_data.preposing + completed).split("\n").last
- line_to_pointer = String.new(encoding: @encoding) if line_to_pointer.nil?
- @cursor_max = calculate_width(@line)
- @cursor = calculate_width(line_to_pointer)
- @byte_pointer = line_to_pointer.bytesize
+ completed = @completion_journey_state.list[@completion_journey_state.pointer]
+ set_current_line(@completion_journey_state.pre + completed + @completion_journey_state.post, @completion_journey_state.pre.bytesize + completed.bytesize)
+ true
+ end
+
+ private def retrieve_completion_journey_state
+ preposing, target, postposing = retrieve_completion_block
+ list = call_completion_proc
+ return unless list.is_a?(Array)
+
+ candidates = list.select{ |item| item.start_with?(target) }
+ return if candidates.empty?
+
+ pre = preposing.split("\n", -1).last || ''
+ post = postposing.split("\n", -1).first || ''
+ CompletionJourneyState.new(
+ @line_index, pre, target, post, [target] + candidates, 0
+ )
end
private def run_for_operators(key, method_symbol, &block)
- if @waiting_operator_proc
+ if @vi_waiting_operator
if VI_MOTIONS.include?(method_symbol)
- old_cursor, old_byte_pointer = @cursor, @byte_pointer
- @vi_arg = @waiting_operator_vi_arg if @waiting_operator_vi_arg&.> 1
+ old_byte_pointer = @byte_pointer
+ @vi_arg = (@vi_arg || 1) * @vi_waiting_operator_arg
block.(true)
unless @waiting_proc
- cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
- @cursor, @byte_pointer = old_cursor, old_byte_pointer
- @waiting_operator_proc.(cursor_diff, byte_pointer_diff)
- else
- old_waiting_proc = @waiting_proc
- old_waiting_operator_proc = @waiting_operator_proc
- current_waiting_operator_proc = @waiting_operator_proc
- @waiting_proc = proc { |k|
- old_cursor, old_byte_pointer = @cursor, @byte_pointer
- old_waiting_proc.(k)
- cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
- @cursor, @byte_pointer = old_cursor, old_byte_pointer
- current_waiting_operator_proc.(cursor_diff, byte_pointer_diff)
- @waiting_operator_proc = old_waiting_operator_proc
- }
+ byte_pointer_diff = @byte_pointer - old_byte_pointer
+ @byte_pointer = old_byte_pointer
+ send(@vi_waiting_operator, byte_pointer_diff)
+ cleanup_waiting
end
else
# Ignores operator when not motion is given.
block.(false)
+ cleanup_waiting
end
- @waiting_operator_proc = nil
- @waiting_operator_vi_arg = nil
- if @vi_arg
- @rerender_all = true
- @vi_arg = nil
- end
+ @vi_arg = nil
else
block.(false)
end
@@ -1415,7 +974,7 @@ class Reline::LineEditor
end
def wrap_method_call(method_symbol, method_obj, key, with_operator = false)
- if @config.editing_mode_is?(:emacs, :vi_insert) and @waiting_proc.nil? and @waiting_operator_proc.nil?
+ if @config.editing_mode_is?(:emacs, :vi_insert) and @vi_waiting_operator.nil?
not_insertion = method_symbol != :ed_insert
process_insert(force: not_insertion)
end
@@ -1434,11 +993,32 @@ class Reline::LineEditor
end
end
+ private def cleanup_waiting
+ @waiting_proc = nil
+ @vi_waiting_operator = nil
+ @vi_waiting_operator_arg = nil
+ @searching_prompt = nil
+ @drop_terminate_spaces = false
+ end
+
private def process_key(key, method_symbol)
+ if key.is_a?(Symbol)
+ cleanup_waiting
+ elsif @waiting_proc
+ old_byte_pointer = @byte_pointer
+ @waiting_proc.call(key)
+ if @vi_waiting_operator
+ byte_pointer_diff = @byte_pointer - old_byte_pointer
+ @byte_pointer = old_byte_pointer
+ send(@vi_waiting_operator, byte_pointer_diff)
+ cleanup_waiting
+ end
+ @kill_ring.process
+ return
+ end
+
if method_symbol and respond_to?(method_symbol, true)
method_obj = method(method_symbol)
- else
- method_obj = nil
end
if method_symbol and key.is_a?(Symbol)
if @vi_arg and argumentable?(method_obj)
@@ -1450,7 +1030,6 @@ class Reline::LineEditor
end
@kill_ring.process
if @vi_arg
- @rerender_al = true
@vi_arg = nil
end
elsif @vi_arg
@@ -1461,8 +1040,6 @@ class Reline::LineEditor
run_for_operators(key, method_symbol) do |with_operator|
wrap_method_call(method_symbol, method_obj, key, with_operator)
end
- elsif @waiting_proc
- @waiting_proc.(key)
elsif method_obj
wrap_method_call(method_symbol, method_obj, key)
else
@@ -1470,13 +1047,9 @@ class Reline::LineEditor
end
@kill_ring.process
if @vi_arg
- @rerender_all = true
@vi_arg = nil
end
end
- elsif @waiting_proc
- @waiting_proc.(key)
- @kill_ring.process
elsif method_obj
if method_symbol == :ed_argument_digit
wrap_method_call(method_symbol, method_obj, key)
@@ -1492,7 +1065,6 @@ class Reline::LineEditor
end
private def normal_char(key)
- method_symbol = method_obj = nil
if key.combined_char.is_a?(Symbol)
process_key(key.combined_char, key.combined_char)
return
@@ -1510,97 +1082,105 @@ class Reline::LineEditor
return if key.char >= 128 # maybe, first byte of multi byte
method_symbol = @config.editing_mode.get_method(key.combined_char)
if key.with_meta and method_symbol == :ed_unassigned
- # split ESC + key
- method_symbol = @config.editing_mode.get_method("\e".ord)
- process_key("\e".ord, method_symbol)
- method_symbol = @config.editing_mode.get_method(key.char)
- process_key(key.char, method_symbol)
+ if @config.editing_mode_is?(:vi_command, :vi_insert)
+ # split ESC + key in vi mode
+ method_symbol = @config.editing_mode.get_method("\e".ord)
+ process_key("\e".ord, method_symbol)
+ method_symbol = @config.editing_mode.get_method(key.char)
+ process_key(key.char, method_symbol)
+ end
else
process_key(key.combined_char, method_symbol)
end
@multibyte_buffer.clear
end
- if @config.editing_mode_is?(:vi_command) and @cursor > 0 and @cursor == @cursor_max
- byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
+ if @config.editing_mode_is?(:vi_command) and @byte_pointer > 0 and @byte_pointer == current_line.bytesize
+ byte_size = Reline::Unicode.get_prev_mbchar_size(@buffer_of_lines[@line_index], @byte_pointer)
@byte_pointer -= byte_size
- mbchar = @line.byteslice(@byte_pointer, byte_size)
- width = Reline::Unicode.get_mbchar_width(mbchar)
- @cursor -= width
+ end
+ end
+
+ def update(key)
+ modified = input_key(key)
+ unless @in_pasting
+ scroll_into_view
+ @just_cursor_moving = !modified
+ update_dialogs(key)
+ @just_cursor_moving = false
end
end
def input_key(key)
- @last_key = key
@config.reset_oneshot_key_bindings
@dialogs.each do |dialog|
if key.char.instance_of?(Symbol) and key.char == dialog.name
return
end
end
- @just_cursor_moving = nil
if key.char.nil?
+ process_insert(force: true)
if @first_char
- @line = nil
+ @eof = true
end
finish
return
end
- old_line = @line.dup
+ old_lines = @buffer_of_lines.dup
@first_char = false
- completion_occurs = false
+ @completion_occurs = false
if @config.editing_mode_is?(:emacs, :vi_insert) and key.char == "\C-i".ord
- unless @config.disable_completion
- result = call_completion_proc
- if result.is_a?(Array)
- completion_occurs = true
- process_insert
- if @config.autocompletion
- move_completed_list(result, :down)
- else
- complete(result)
+ if !@config.disable_completion
+ process_insert(force: true)
+ if @config.autocompletion
+ @completion_state = CompletionState::NORMAL
+ @completion_occurs = move_completed_list(:down)
+ else
+ @completion_journey_state = nil
+ result = call_completion_proc
+ if result.is_a?(Array)
+ @completion_occurs = true
+ complete(result, false)
end
end
end
- elsif @config.editing_mode_is?(:emacs, :vi_insert) and key.char == :completion_journey_up
- if not @config.disable_completion and @config.autocompletion
- result = call_completion_proc
- if result.is_a?(Array)
- completion_occurs = true
- process_insert
- move_completed_list(result, :up)
- end
- end
- elsif not @config.disable_completion and @config.editing_mode_is?(:vi_insert) and ["\C-p".ord, "\C-n".ord].include?(key.char)
- unless @config.disable_completion
- result = call_completion_proc
- if result.is_a?(Array)
- completion_occurs = true
- process_insert
- move_completed_list(result, "\C-p".ord == key.char ? :up : :down)
- end
+ elsif @config.editing_mode_is?(:vi_insert) and ["\C-p".ord, "\C-n".ord].include?(key.char)
+ # In vi mode, move completed list even if autocompletion is off
+ if not @config.disable_completion
+ process_insert(force: true)
+ @completion_state = CompletionState::NORMAL
+ @completion_occurs = move_completed_list("\C-p".ord == key.char ? :up : :down)
end
elsif Symbol === key.char and respond_to?(key.char, true)
process_key(key.char, key.char)
else
normal_char(key)
end
- unless completion_occurs
+ unless @completion_occurs
@completion_state = CompletionState::NORMAL
- @completion_journey_data = nil
+ @completion_journey_state = nil
end
- if not @in_pasting and @just_cursor_moving.nil?
- if @previous_line_index and @buffer_of_lines[@previous_line_index] == @line
- @just_cursor_moving = true
- elsif @previous_line_index.nil? and @buffer_of_lines[@line_index] == @line and old_line == @line
- @just_cursor_moving = true
- else
- @just_cursor_moving = false
- end
- else
- @just_cursor_moving = false
+
+ if @in_pasting
+ clear_dialogs
+ return
end
- if @is_multiline and @auto_indent_proc and not simplified_rendering?
- process_auto_indent
+
+ modified = old_lines != @buffer_of_lines
+ if !@completion_occurs && modified && !@config.disable_completion && @config.autocompletion
+ # Auto complete starts only when edited
+ process_insert(force: true)
+ @completion_journey_state = retrieve_completion_journey_state
+ end
+ modified
+ end
+
+ def scroll_into_view
+ _wrapped_cursor_x, wrapped_cursor_y = wrapped_cursor_position
+ if wrapped_cursor_y < screen_scroll_top
+ @scroll_partial_screen = wrapped_cursor_y
+ end
+ if wrapped_cursor_y >= screen_scroll_top + screen_height
+ @scroll_partial_screen = wrapped_cursor_y - screen_height + 1
end
end
@@ -1634,43 +1214,40 @@ class Reline::LineEditor
result
end
- private def process_auto_indent
- return if not @check_new_auto_indent and @previous_line_index # move cursor up or down
- if @check_new_auto_indent and @previous_line_index and @previous_line_index > 0 and @line_index > @previous_line_index
- # Fix indent of a line when a newline is inserted to the next
- new_lines = whole_lines
- new_indent = @auto_indent_proc.(new_lines[0..-3].push(''), @line_index - 1, 0, true)
- md = @line.match(/\A */)
- prev_indent = md[0].count(' ')
- @line = ' ' * new_indent + @line.lstrip
-
- new_indent = nil
- result = @auto_indent_proc.(new_lines[0..-2], @line_index - 1, (new_lines[-2].size + 1), false)
- if result
- new_indent = result
- end
- if new_indent&.>= 0
- @line = ' ' * new_indent + @line.lstrip
- end
- end
- new_lines = whole_lines
- new_indent = @auto_indent_proc.(new_lines, @line_index, @byte_pointer, @check_new_auto_indent)
- if new_indent&.>= 0
- md = new_lines[@line_index].match(/\A */)
- prev_indent = md[0].count(' ')
- if @check_new_auto_indent
- line = @buffer_of_lines[@line_index] = ' ' * new_indent + @buffer_of_lines[@line_index].lstrip
- @cursor = new_indent
- @cursor_max = calculate_width(line)
- @byte_pointer = new_indent
- else
- @line = ' ' * new_indent + @line.lstrip
- @cursor += new_indent - prev_indent
- @cursor_max = calculate_width(@line)
- @byte_pointer += new_indent - prev_indent
- end
+ private def process_auto_indent(line_index = @line_index, cursor_dependent: true, add_newline: false)
+ return if @in_pasting
+ return unless @auto_indent_proc
+
+ line = @buffer_of_lines[line_index]
+ byte_pointer = cursor_dependent && @line_index == line_index ? @byte_pointer : line.bytesize
+ new_indent = @auto_indent_proc.(@buffer_of_lines.take(line_index + 1).push(''), line_index, byte_pointer, add_newline)
+ return unless new_indent
+
+ new_line = ' ' * new_indent + line.lstrip
+ @buffer_of_lines[line_index] = new_line
+ if @line_index == line_index
+ indent_diff = new_line.bytesize - line.bytesize
+ @byte_pointer = [@byte_pointer + indent_diff, 0].max
+ end
+ end
+
+ def line()
+ @buffer_of_lines.join("\n") unless eof?
+ end
+
+ def current_line
+ @buffer_of_lines[@line_index]
+ end
+
+ def set_current_line(line, byte_pointer = nil)
+ cursor = current_byte_pointer_cursor
+ @buffer_of_lines[@line_index] = line
+ if byte_pointer
+ @byte_pointer = byte_pointer
+ else
+ calculate_nearest_cursor(cursor)
end
- @check_new_auto_indent = false
+ process_auto_indent
end
def retrieve_completion_block(set_completion_quote_character = false)
@@ -1684,7 +1261,7 @@ class Reline::LineEditor
else
quote_characters_regexp = /\A[#{Regexp.escape(Reline.completer_quote_characters)}]/
end
- before = @line.byteslice(0, @byte_pointer)
+ before = current_line.byteslice(0, @byte_pointer)
rest = nil
break_pointer = nil
quote = nil
@@ -1692,7 +1269,7 @@ class Reline::LineEditor
escaped_quote = nil
i = 0
while i < @byte_pointer do
- slice = @line.byteslice(i, @byte_pointer - i)
+ slice = current_line.byteslice(i, @byte_pointer - i)
unless slice.valid_encoding?
i += 1
next
@@ -1714,15 +1291,15 @@ class Reline::LineEditor
elsif word_break_regexp and not quote and slice =~ word_break_regexp
rest = $'
i += 1
- before = @line.byteslice(i, @byte_pointer - i)
+ before = current_line.byteslice(i, @byte_pointer - i)
break_pointer = i
else
i += 1
end
end
- postposing = @line.byteslice(@byte_pointer, @line.bytesize - @byte_pointer)
+ postposing = current_line.byteslice(@byte_pointer, current_line.bytesize - @byte_pointer)
if rest
- preposing = @line.byteslice(0, break_pointer)
+ preposing = current_line.byteslice(0, break_pointer)
target = rest
if set_completion_quote_character and quote
Reline.core.instance_variable_set(:@completion_quote_character, quote)
@@ -1733,126 +1310,81 @@ class Reline::LineEditor
else
preposing = ''
if break_pointer
- preposing = @line.byteslice(0, break_pointer)
+ preposing = current_line.byteslice(0, break_pointer)
else
preposing = ''
end
target = before
end
- if @is_multiline
- lines = whole_lines
- if @line_index > 0
- preposing = lines[0..(@line_index - 1)].join("\n") + "\n" + preposing
- end
- if (lines.size - 1) > @line_index
- postposing = postposing + "\n" + lines[(@line_index + 1)..-1].join("\n")
- end
+ lines = whole_lines
+ if @line_index > 0
+ preposing = lines[0..(@line_index - 1)].join("\n") + "\n" + preposing
+ end
+ if (lines.size - 1) > @line_index
+ postposing = postposing + "\n" + lines[(@line_index + 1)..-1].join("\n")
end
[preposing.encode(@encoding), target.encode(@encoding), postposing.encode(@encoding)]
end
def confirm_multiline_termination
temp_buffer = @buffer_of_lines.dup
- if @previous_line_index and @line_index == (@buffer_of_lines.size - 1)
- temp_buffer[@previous_line_index] = @line
- else
- temp_buffer[@line_index] = @line
- end
@confirm_multiline_termination_proc.(temp_buffer.join("\n") + "\n")
end
def insert_text(text)
- width = calculate_width(text)
- if @cursor == @cursor_max
- @line += text
+ if @buffer_of_lines[@line_index].bytesize == @byte_pointer
+ @buffer_of_lines[@line_index] += text
else
- @line = byteinsert(@line, @byte_pointer, text)
+ @buffer_of_lines[@line_index] = byteinsert(@buffer_of_lines[@line_index], @byte_pointer, text)
end
@byte_pointer += text.bytesize
- @cursor += width
- @cursor_max += width
+ process_auto_indent
end
def delete_text(start = nil, length = nil)
if start.nil? and length.nil?
- if @is_multiline
- if @buffer_of_lines.size == 1
- @line&.clear
- @byte_pointer = 0
- @cursor = 0
- @cursor_max = 0
- elsif @line_index == (@buffer_of_lines.size - 1) and @line_index > 0
- @buffer_of_lines.pop
- @line_index -= 1
- @line = @buffer_of_lines[@line_index]
- @byte_pointer = 0
- @cursor = 0
- @cursor_max = calculate_width(@line)
- elsif @line_index < (@buffer_of_lines.size - 1)
- @buffer_of_lines.delete_at(@line_index)
- @line = @buffer_of_lines[@line_index]
- @byte_pointer = 0
- @cursor = 0
- @cursor_max = calculate_width(@line)
- end
- else
- @line&.clear
+ if @buffer_of_lines.size == 1
+ @buffer_of_lines[@line_index] = ''
+ @byte_pointer = 0
+ elsif @line_index == (@buffer_of_lines.size - 1) and @line_index > 0
+ @buffer_of_lines.pop
+ @line_index -= 1
+ @byte_pointer = 0
+ elsif @line_index < (@buffer_of_lines.size - 1)
+ @buffer_of_lines.delete_at(@line_index)
@byte_pointer = 0
- @cursor = 0
- @cursor_max = 0
end
elsif not start.nil? and not length.nil?
- if @line
- before = @line.byteslice(0, start)
- after = @line.byteslice(start + length, @line.bytesize)
- @line = before + after
- @byte_pointer = @line.bytesize if @byte_pointer > @line.bytesize
- str = @line.byteslice(0, @byte_pointer)
- @cursor = calculate_width(str)
- @cursor_max = calculate_width(@line)
+ if current_line
+ before = current_line.byteslice(0, start)
+ after = current_line.byteslice(start + length, current_line.bytesize)
+ set_current_line(before + after)
end
elsif start.is_a?(Range)
range = start
first = range.first
last = range.last
- last = @line.bytesize - 1 if last > @line.bytesize
- last += @line.bytesize if last < 0
- first += @line.bytesize if first < 0
+ last = current_line.bytesize - 1 if last > current_line.bytesize
+ last += current_line.bytesize if last < 0
+ first += current_line.bytesize if first < 0
range = range.exclude_end? ? first...last : first..last
- @line = @line.bytes.reject.with_index{ |c, i| range.include?(i) }.map{ |c| c.chr(Encoding::ASCII_8BIT) }.join.force_encoding(@encoding)
- @byte_pointer = @line.bytesize if @byte_pointer > @line.bytesize
- str = @line.byteslice(0, @byte_pointer)
- @cursor = calculate_width(str)
- @cursor_max = calculate_width(@line)
+ line = current_line.bytes.reject.with_index{ |c, i| range.include?(i) }.map{ |c| c.chr(Encoding::ASCII_8BIT) }.join.force_encoding(@encoding)
+ set_current_line(line)
else
- @line = @line.byteslice(0, start)
- @byte_pointer = @line.bytesize if @byte_pointer > @line.bytesize
- str = @line.byteslice(0, @byte_pointer)
- @cursor = calculate_width(str)
- @cursor_max = calculate_width(@line)
+ set_current_line(current_line.byteslice(0, start))
end
end
def byte_pointer=(val)
@byte_pointer = val
- str = @line.byteslice(0, @byte_pointer)
- @cursor = calculate_width(str)
- @cursor_max = calculate_width(@line)
end
def whole_lines
- index = @previous_line_index || @line_index
- temp_lines = @buffer_of_lines.dup
- temp_lines[index] = @line
- temp_lines
+ @buffer_of_lines.dup
end
def whole_buffer
- if @buffer_of_lines.size == 1 and @line.nil?
- nil
- else
- whole_lines.join("\n")
- end
+ whole_lines.join("\n")
end
def finished?
@@ -1861,7 +1393,6 @@ class Reline::LineEditor
def finish
@finished = true
- @rerender_all = true
@config.reset
end
@@ -1892,33 +1423,27 @@ class Reline::LineEditor
private def key_newline(key)
if @is_multiline
- if (@buffer_of_lines.size - 1) == @line_index and @line.bytesize == @byte_pointer
- @add_newline_to_end_of_buffer = true
- end
- next_line = @line.byteslice(@byte_pointer, @line.bytesize - @byte_pointer)
- cursor_line = @line.byteslice(0, @byte_pointer)
+ next_line = current_line.byteslice(@byte_pointer, current_line.bytesize - @byte_pointer)
+ cursor_line = current_line.byteslice(0, @byte_pointer)
insert_new_line(cursor_line, next_line)
- @cursor = 0
- @check_new_auto_indent = true unless @in_pasting
end
end
+ private def completion_journey_up(key)
+ if not @config.disable_completion and @config.autocompletion
+ @completion_state = CompletionState::NORMAL
+ @completion_occurs = move_completed_list(:up)
+ end
+ end
+ alias_method :menu_complete_backward, :completion_journey_up
+
# Editline:: +ed-unassigned+ This editor command always results in an error.
# GNU Readline:: There is no corresponding macro.
private def ed_unassigned(key) end # do nothing
private def process_insert(force: false)
return if @continuous_insertion_buffer.empty? or (@in_pasting and not force)
- width = Reline::Unicode.calculate_width(@continuous_insertion_buffer)
- bytesize = @continuous_insertion_buffer.bytesize
- if @cursor == @cursor_max
- @line += @continuous_insertion_buffer
- else
- @line = byteinsert(@line, @byte_pointer, @continuous_insertion_buffer)
- end
- @byte_pointer += bytesize
- @cursor += width
- @cursor_max += width
+ insert_text(@continuous_insertion_buffer)
@continuous_insertion_buffer.clear
end
@@ -1936,9 +1461,6 @@ class Reline::LineEditor
# million.
# GNU Readline:: +self-insert+ (a, b, A, 1, !, …) Insert yourself.
private def ed_insert(key)
- str = nil
- width = nil
- bytesize = nil
if key.instance_of?(String)
begin
key.encode(Encoding::UTF_8)
@@ -1946,7 +1468,6 @@ class Reline::LineEditor
return
end
str = key
- bytesize = key.bytesize
else
begin
key.chr.encode(Encoding::UTF_8)
@@ -1954,7 +1475,6 @@ class Reline::LineEditor
return
end
str = key.chr
- bytesize = 1
end
if @in_pasting
@continuous_insertion_buffer << str
@@ -1962,28 +1482,8 @@ class Reline::LineEditor
elsif not @continuous_insertion_buffer.empty?
process_insert
end
- width = Reline::Unicode.get_mbchar_width(str)
- if @cursor == @cursor_max
- @line += str
- else
- @line = byteinsert(@line, @byte_pointer, str)
- end
- last_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
- @byte_pointer += bytesize
- last_mbchar = @line.byteslice((@byte_pointer - bytesize - last_byte_size), last_byte_size)
- combined_char = last_mbchar + str
- if last_byte_size != 0 and combined_char.grapheme_clusters.size == 1
- # combined char
- last_mbchar_width = Reline::Unicode.get_mbchar_width(last_mbchar)
- combined_char_width = Reline::Unicode.get_mbchar_width(combined_char)
- if combined_char_width > last_mbchar_width
- width = combined_char_width - last_mbchar_width
- else
- width = 0
- end
- end
- @cursor += width
- @cursor_max += width
+
+ insert_text(str)
end
alias_method :ed_digit, :ed_insert
alias_method :self_insert, :ed_insert
@@ -2005,18 +1505,11 @@ class Reline::LineEditor
alias_method :quoted_insert, :ed_quoted_insert
private def ed_next_char(key, arg: 1)
- byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
- if (@byte_pointer < @line.bytesize)
- mbchar = @line.byteslice(@byte_pointer, byte_size)
- width = Reline::Unicode.get_mbchar_width(mbchar)
- @cursor += width if width
+ byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
+ if (@byte_pointer < current_line.bytesize)
@byte_pointer += byte_size
- elsif @is_multiline and @config.editing_mode_is?(:emacs) and @byte_pointer == @line.bytesize and @line_index < @buffer_of_lines.size - 1
- next_line = @buffer_of_lines[@line_index + 1]
- @cursor = 0
+ elsif @config.editing_mode_is?(:emacs) and @byte_pointer == current_line.bytesize and @line_index < @buffer_of_lines.size - 1
@byte_pointer = 0
- @cursor_max = calculate_width(next_line)
- @previous_line_index = @line_index
@line_index += 1
end
arg -= 1
@@ -2025,19 +1518,12 @@ class Reline::LineEditor
alias_method :forward_char, :ed_next_char
private def ed_prev_char(key, arg: 1)
- if @cursor > 0
- byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
+ if @byte_pointer > 0
+ byte_size = Reline::Unicode.get_prev_mbchar_size(current_line, @byte_pointer)
@byte_pointer -= byte_size
- mbchar = @line.byteslice(@byte_pointer, byte_size)
- width = Reline::Unicode.get_mbchar_width(mbchar)
- @cursor -= width
- elsif @is_multiline and @config.editing_mode_is?(:emacs) and @byte_pointer == 0 and @line_index > 0
- prev_line = @buffer_of_lines[@line_index - 1]
- @cursor = calculate_width(prev_line)
- @byte_pointer = prev_line.bytesize
- @cursor_max = calculate_width(prev_line)
- @previous_line_index = @line_index
+ elsif @config.editing_mode_is?(:emacs) and @byte_pointer == 0 and @line_index > 0
@line_index -= 1
+ @byte_pointer = current_line.bytesize
end
arg -= 1
ed_prev_char(key, arg: arg) if arg > 0
@@ -2045,157 +1531,109 @@ class Reline::LineEditor
alias_method :backward_char, :ed_prev_char
private def vi_first_print(key)
- @byte_pointer, @cursor = Reline::Unicode.vi_first_print(@line)
+ @byte_pointer, = Reline::Unicode.vi_first_print(current_line)
end
private def ed_move_to_beg(key)
- @byte_pointer = @cursor = 0
+ @byte_pointer = 0
end
alias_method :beginning_of_line, :ed_move_to_beg
+ alias_method :vi_zero, :ed_move_to_beg
private def ed_move_to_end(key)
- @byte_pointer = 0
- @cursor = 0
- byte_size = 0
- while @byte_pointer < @line.bytesize
- byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
- if byte_size > 0
- mbchar = @line.byteslice(@byte_pointer, byte_size)
- @cursor += Reline::Unicode.get_mbchar_width(mbchar)
- end
- @byte_pointer += byte_size
- end
+ @byte_pointer = current_line.bytesize
end
alias_method :end_of_line, :ed_move_to_end
- private def generate_searcher
- Fiber.new do |first_key|
- prev_search_key = first_key
- search_word = String.new(encoding: @encoding)
- multibyte_buf = String.new(encoding: 'ASCII-8BIT')
- last_hit = nil
- case first_key
- when "\C-r".ord
- prompt_name = 'reverse-i-search'
- when "\C-s".ord
- prompt_name = 'i-search'
+ private def generate_searcher(search_key)
+ search_word = String.new(encoding: @encoding)
+ multibyte_buf = String.new(encoding: 'ASCII-8BIT')
+ hit_pointer = nil
+ lambda do |key|
+ search_again = false
+ case key
+ when "\C-h".ord, "\C-?".ord
+ grapheme_clusters = search_word.grapheme_clusters
+ if grapheme_clusters.size > 0
+ grapheme_clusters.pop
+ search_word = grapheme_clusters.join
+ end
+ when "\C-r".ord, "\C-s".ord
+ search_again = true if search_key == key
+ search_key = key
+ else
+ multibyte_buf << key
+ if multibyte_buf.dup.force_encoding(@encoding).valid_encoding?
+ search_word << multibyte_buf.dup.force_encoding(@encoding)
+ multibyte_buf.clear
+ end
end
- loop do
- key = Fiber.yield(search_word)
- search_again = false
- case key
- when -1 # determined
- Reline.last_incremental_search = search_word
- break
- when "\C-h".ord, "\C-?".ord
- grapheme_clusters = search_word.grapheme_clusters
- if grapheme_clusters.size > 0
- grapheme_clusters.pop
- search_word = grapheme_clusters.join
- end
- when "\C-r".ord, "\C-s".ord
- search_again = true if prev_search_key == key
- prev_search_key = key
- else
- multibyte_buf << key
- if multibyte_buf.dup.force_encoding(@encoding).valid_encoding?
- search_word << multibyte_buf.dup.force_encoding(@encoding)
- multibyte_buf.clear
+ hit = nil
+ if not search_word.empty? and @line_backup_in_history&.include?(search_word)
+ hit_pointer = Reline::HISTORY.size
+ hit = @line_backup_in_history
+ else
+ if search_again
+ if search_word.empty? and Reline.last_incremental_search
+ search_word = Reline.last_incremental_search
end
- end
- hit = nil
- if not search_word.empty? and @line_backup_in_history&.include?(search_word)
- @history_pointer = nil
- hit = @line_backup_in_history
- else
- if search_again
- if search_word.empty? and Reline.last_incremental_search
- search_word = Reline.last_incremental_search
- end
- if @history_pointer
- case prev_search_key
- when "\C-r".ord
- history_pointer_base = 0
- history = Reline::HISTORY[0..(@history_pointer - 1)]
- when "\C-s".ord
- history_pointer_base = @history_pointer + 1
- history = Reline::HISTORY[(@history_pointer + 1)..-1]
- end
- else
- history_pointer_base = 0
- history = Reline::HISTORY
- end
- elsif @history_pointer
- case prev_search_key
+ if @history_pointer
+ case search_key
when "\C-r".ord
history_pointer_base = 0
- history = Reline::HISTORY[0..@history_pointer]
+ history = Reline::HISTORY[0..(@history_pointer - 1)]
when "\C-s".ord
- history_pointer_base = @history_pointer
- history = Reline::HISTORY[@history_pointer..-1]
+ history_pointer_base = @history_pointer + 1
+ history = Reline::HISTORY[(@history_pointer + 1)..-1]
end
else
history_pointer_base = 0
history = Reline::HISTORY
end
- case prev_search_key
+ elsif @history_pointer
+ case search_key
when "\C-r".ord
- hit_index = history.rindex { |item|
- item.include?(search_word)
- }
+ history_pointer_base = 0
+ history = Reline::HISTORY[0..@history_pointer]
when "\C-s".ord
- hit_index = history.index { |item|
- item.include?(search_word)
- }
- end
- if hit_index
- @history_pointer = history_pointer_base + hit_index
- hit = Reline::HISTORY[@history_pointer]
+ history_pointer_base = @history_pointer
+ history = Reline::HISTORY[@history_pointer..-1]
end
+ else
+ history_pointer_base = 0
+ history = Reline::HISTORY
end
- case prev_search_key
+ case search_key
when "\C-r".ord
- prompt_name = 'reverse-i-search'
+ hit_index = history.rindex { |item|
+ item.include?(search_word)
+ }
when "\C-s".ord
- prompt_name = 'i-search'
+ hit_index = history.index { |item|
+ item.include?(search_word)
+ }
end
- if hit
- if @is_multiline
- @buffer_of_lines = hit.split("\n")
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
- @line_index = @buffer_of_lines.size - 1
- @line = @buffer_of_lines.last
- @byte_pointer = @line.bytesize
- @cursor = @cursor_max = calculate_width(@line)
- @rerender_all = true
- @searching_prompt = "(%s)`%s'" % [prompt_name, search_word]
- else
- @line = hit
- @searching_prompt = "(%s)`%s': %s" % [prompt_name, search_word, hit]
- end
- last_hit = hit
- else
- if @is_multiline
- @rerender_all = true
- @searching_prompt = "(failed %s)`%s'" % [prompt_name, search_word]
- else
- @searching_prompt = "(failed %s)`%s': %s" % [prompt_name, search_word, last_hit]
- end
+ if hit_index
+ hit_pointer = history_pointer_base + hit_index
+ hit = Reline::HISTORY[hit_pointer]
end
end
+ case search_key
+ when "\C-r".ord
+ prompt_name = 'reverse-i-search'
+ when "\C-s".ord
+ prompt_name = 'i-search'
+ end
+ prompt_name = "failed #{prompt_name}" unless hit
+ [search_word, prompt_name, hit_pointer]
end
end
private def incremental_search_history(key)
unless @history_pointer
- if @is_multiline
- @line_backup_in_history = whole_buffer
- else
- @line_backup_in_history = @line
- end
+ @line_backup_in_history = whole_buffer
end
- searcher = generate_searcher
- searcher.resume(key)
+ searcher = generate_searcher(key)
@searching_prompt = "(reverse-i-search)`': "
termination_keys = ["\C-j".ord]
termination_keys.concat(@config.isearch_terminators&.chars&.map(&:ord)) if @config.isearch_terminators
@@ -2207,67 +1645,41 @@ class Reline::LineEditor
else
buffer = @line_backup_in_history
end
- if @is_multiline
- @buffer_of_lines = buffer.split("\n")
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
- @line_index = @buffer_of_lines.size - 1
- @line = @buffer_of_lines.last
- @rerender_all = true
- else
- @line = buffer
- end
+ @buffer_of_lines = buffer.split("\n")
+ @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
+ @line_index = @buffer_of_lines.size - 1
@searching_prompt = nil
@waiting_proc = nil
- @cursor_max = calculate_width(@line)
- @cursor = @byte_pointer = 0
- @rerender_all = true
- @cached_prompt_list = nil
- searcher.resume(-1)
+ @byte_pointer = 0
when "\C-g".ord
- if @is_multiline
- @buffer_of_lines = @line_backup_in_history.split("\n")
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
- @line_index = @buffer_of_lines.size - 1
- @line = @buffer_of_lines.last
- @rerender_all = true
- else
- @line = @line_backup_in_history
- end
- @history_pointer = nil
+ @buffer_of_lines = @line_backup_in_history.split("\n")
+ @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
+ @line_index = @buffer_of_lines.size - 1
+ move_history(nil, line: :end, cursor: :end, save_buffer: false)
@searching_prompt = nil
@waiting_proc = nil
- @line_backup_in_history = nil
- @cursor_max = calculate_width(@line)
- @cursor = @byte_pointer = 0
- @rerender_all = true
+ @byte_pointer = 0
else
chr = k.is_a?(String) ? k : k.chr(Encoding::ASCII_8BIT)
if chr.match?(/[[:print:]]/) or k == "\C-h".ord or k == "\C-?".ord or k == "\C-r".ord or k == "\C-s".ord
- searcher.resume(k)
+ search_word, prompt_name, hit_pointer = searcher.call(k)
+ Reline.last_incremental_search = search_word
+ @searching_prompt = "(%s)`%s'" % [prompt_name, search_word]
+ @searching_prompt += ': ' unless @is_multiline
+ move_history(hit_pointer, line: :end, cursor: :end, save_buffer: false) if hit_pointer
else
if @history_pointer
line = Reline::HISTORY[@history_pointer]
else
line = @line_backup_in_history
end
- if @is_multiline
- @line_backup_in_history = whole_buffer
- @buffer_of_lines = line.split("\n")
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
- @line_index = @buffer_of_lines.size - 1
- @line = @buffer_of_lines.last
- @rerender_all = true
- else
- @line_backup_in_history = @line
- @line = line
- end
+ @line_backup_in_history = whole_buffer
+ @buffer_of_lines = line.split("\n")
+ @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
+ @line_index = @buffer_of_lines.size - 1
@searching_prompt = nil
@waiting_proc = nil
- @cursor_max = calculate_width(@line)
- @cursor = @byte_pointer = 0
- @rerender_all = true
- @cached_prompt_list = nil
- searcher.resume(-1)
+ @byte_pointer = 0
end
end
}
@@ -2283,199 +1695,95 @@ class Reline::LineEditor
end
alias_method :forward_search_history, :vi_search_next
- private def ed_search_prev_history(key, arg: 1)
- history = nil
- h_pointer = nil
- line_no = nil
- substr = @line.slice(0, @byte_pointer)
- if @history_pointer.nil?
- return if not @line.empty? and substr.empty?
- history = Reline::HISTORY
- elsif @history_pointer.zero?
- history = nil
- h_pointer = nil
- else
- history = Reline::HISTORY.slice(0, @history_pointer)
- end
- return if history.nil?
- if @is_multiline
- h_pointer = history.rindex { |h|
- h.split("\n").each_with_index { |l, i|
- if l.start_with?(substr)
- line_no = i
- break
- end
- }
- not line_no.nil?
- }
- else
- h_pointer = history.rindex { |l|
- l.start_with?(substr)
- }
- end
- return if h_pointer.nil?
- @history_pointer = h_pointer
- if @is_multiline
- @buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
- @line_index = line_no
- @line = @buffer_of_lines[@line_index]
- @rerender_all = true
- else
- @line = Reline::HISTORY[@history_pointer]
+ private def search_history(prefix, pointer_range)
+ pointer_range.each do |pointer|
+ lines = Reline::HISTORY[pointer].split("\n")
+ lines.each_with_index do |line, index|
+ return [pointer, index] if line.start_with?(prefix)
+ end
end
- @cursor_max = calculate_width(@line)
+ nil
+ end
+
+ private def ed_search_prev_history(key, arg: 1)
+ substr = current_line.byteslice(0, @byte_pointer)
+ return if @history_pointer == 0
+ return if @history_pointer.nil? && substr.empty? && !current_line.empty?
+
+ history_range = 0...(@history_pointer || Reline::HISTORY.size)
+ h_pointer, line_index = search_history(substr, history_range.reverse_each)
+ return unless h_pointer
+ move_history(h_pointer, line: line_index || :start, cursor: @byte_pointer)
arg -= 1
ed_search_prev_history(key, arg: arg) if arg > 0
end
alias_method :history_search_backward, :ed_search_prev_history
private def ed_search_next_history(key, arg: 1)
- substr = @line.slice(0, @byte_pointer)
- if @history_pointer.nil?
- return
- elsif @history_pointer == (Reline::HISTORY.size - 1) and not substr.empty?
- return
- end
- history = Reline::HISTORY.slice((@history_pointer + 1)..-1)
- h_pointer = nil
- line_no = nil
- if @is_multiline
- h_pointer = history.index { |h|
- h.split("\n").each_with_index { |l, i|
- if l.start_with?(substr)
- line_no = i
- break
- end
- }
- not line_no.nil?
- }
- else
- h_pointer = history.index { |l|
- l.start_with?(substr)
- }
- end
- h_pointer += @history_pointer + 1 if h_pointer and @history_pointer
+ substr = current_line.byteslice(0, @byte_pointer)
+ return if @history_pointer.nil?
+
+ history_range = @history_pointer + 1...Reline::HISTORY.size
+ h_pointer, line_index = search_history(substr, history_range)
return if h_pointer.nil? and not substr.empty?
- @history_pointer = h_pointer
- if @is_multiline
- if @history_pointer.nil? and substr.empty?
- @buffer_of_lines = []
- @line_index = 0
- else
- @buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
- @line_index = line_no
- end
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
- @line = @buffer_of_lines[@line_index]
- @rerender_all = true
- else
- if @history_pointer.nil? and substr.empty?
- @line = ''
- else
- @line = Reline::HISTORY[@history_pointer]
- end
- end
- @cursor_max = calculate_width(@line)
+
+ move_history(h_pointer, line: line_index || :start, cursor: @byte_pointer)
arg -= 1
ed_search_next_history(key, arg: arg) if arg > 0
end
alias_method :history_search_forward, :ed_search_next_history
- private def ed_prev_history(key, arg: 1)
- if @is_multiline and @line_index > 0
- @previous_line_index = @line_index
- @line_index -= 1
- return
- end
- if Reline::HISTORY.empty?
- return
+ private def move_history(history_pointer, line:, cursor:, save_buffer: true)
+ history_pointer ||= Reline::HISTORY.size
+ return if history_pointer < 0 || history_pointer > Reline::HISTORY.size
+ old_history_pointer = @history_pointer || Reline::HISTORY.size
+ if old_history_pointer == Reline::HISTORY.size
+ @line_backup_in_history = save_buffer ? whole_buffer : ''
+ else
+ Reline::HISTORY[old_history_pointer] = whole_buffer if save_buffer
end
- if @history_pointer.nil?
- @history_pointer = Reline::HISTORY.size - 1
- if @is_multiline
- @line_backup_in_history = whole_buffer
- @buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
- @line_index = @buffer_of_lines.size - 1
- @line = @buffer_of_lines.last
- @rerender_all = true
- else
- @line_backup_in_history = @line
- @line = Reline::HISTORY[@history_pointer]
- end
- elsif @history_pointer.zero?
- return
+ if history_pointer == Reline::HISTORY.size
+ buf = @line_backup_in_history
+ @history_pointer = @line_backup_in_history = nil
else
- if @is_multiline
- Reline::HISTORY[@history_pointer] = whole_buffer
- @history_pointer -= 1
- @buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
- @line_index = @buffer_of_lines.size - 1
- @line = @buffer_of_lines.last
- @rerender_all = true
- else
- Reline::HISTORY[@history_pointer] = @line
- @history_pointer -= 1
- @line = Reline::HISTORY[@history_pointer]
- end
+ buf = Reline::HISTORY[history_pointer]
+ @history_pointer = history_pointer
end
- if @config.editing_mode_is?(:emacs, :vi_insert)
- @cursor_max = @cursor = calculate_width(@line)
- @byte_pointer = @line.bytesize
- elsif @config.editing_mode_is?(:vi_command)
- @byte_pointer = @cursor = 0
- @cursor_max = calculate_width(@line)
+ @buffer_of_lines = buf.split("\n")
+ @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
+ @line_index = line == :start ? 0 : line == :end ? @buffer_of_lines.size - 1 : line
+ @byte_pointer = cursor == :start ? 0 : cursor == :end ? current_line.bytesize : cursor
+ end
+
+ private def ed_prev_history(key, arg: 1)
+ if @line_index > 0
+ cursor = current_byte_pointer_cursor
+ @line_index -= 1
+ calculate_nearest_cursor(cursor)
+ return
end
+ move_history(
+ (@history_pointer || Reline::HISTORY.size) - 1,
+ line: :end,
+ cursor: @config.editing_mode_is?(:vi_command) ? :start : :end,
+ )
arg -= 1
ed_prev_history(key, arg: arg) if arg > 0
end
alias_method :previous_history, :ed_prev_history
private def ed_next_history(key, arg: 1)
- if @is_multiline and @line_index < (@buffer_of_lines.size - 1)
- @previous_line_index = @line_index
+ if @line_index < (@buffer_of_lines.size - 1)
+ cursor = current_byte_pointer_cursor
@line_index += 1
+ calculate_nearest_cursor(cursor)
return
end
- if @history_pointer.nil?
- return
- elsif @history_pointer == (Reline::HISTORY.size - 1)
- if @is_multiline
- @history_pointer = nil
- @buffer_of_lines = @line_backup_in_history.split("\n")
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
- @line_index = 0
- @line = @buffer_of_lines.first
- @rerender_all = true
- else
- @history_pointer = nil
- @line = @line_backup_in_history
- end
- else
- if @is_multiline
- Reline::HISTORY[@history_pointer] = whole_buffer
- @history_pointer += 1
- @buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
- @line_index = 0
- @line = @buffer_of_lines.first
- @rerender_all = true
- else
- Reline::HISTORY[@history_pointer] = @line
- @history_pointer += 1
- @line = Reline::HISTORY[@history_pointer]
- end
- end
- @line = '' unless @line
- if @config.editing_mode_is?(:emacs, :vi_insert)
- @cursor_max = @cursor = calculate_width(@line)
- @byte_pointer = @line.bytesize
- elsif @config.editing_mode_is?(:vi_command)
- @byte_pointer = @cursor = 0
- @cursor_max = calculate_width(@line)
- end
+ move_history(
+ (@history_pointer || Reline::HISTORY.size) + 1,
+ line: :start,
+ cursor: @config.editing_mode_is?(:vi_command) ? :start : :end,
+ )
arg -= 1
ed_next_history(key, arg: arg) if arg > 0
end
@@ -2500,40 +1808,29 @@ class Reline::LineEditor
end
else
# should check confirm_multiline_termination to finish?
- @previous_line_index = @line_index
@line_index = @buffer_of_lines.size - 1
+ @byte_pointer = current_line.bytesize
finish
end
end
else
- if @history_pointer
- Reline::HISTORY[@history_pointer] = @line
- @history_pointer = nil
- end
finish
end
end
private def em_delete_prev_char(key, arg: 1)
- if @is_multiline and @cursor == 0 and @line_index > 0
- @buffer_of_lines[@line_index] = @line
- @cursor = calculate_width(@buffer_of_lines[@line_index - 1])
- @byte_pointer = @buffer_of_lines[@line_index - 1].bytesize
- @buffer_of_lines[@line_index - 1] += @buffer_of_lines.delete_at(@line_index)
- @line_index -= 1
- @line = @buffer_of_lines[@line_index]
- @cursor_max = calculate_width(@line)
- @rerender_all = true
- elsif @cursor > 0
- byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
- @byte_pointer -= byte_size
- @line, mbchar = byteslice!(@line, @byte_pointer, byte_size)
- width = Reline::Unicode.get_mbchar_width(mbchar)
- @cursor -= width
- @cursor_max -= width
+ arg.times do
+ if @byte_pointer == 0 and @line_index > 0
+ @byte_pointer = @buffer_of_lines[@line_index - 1].bytesize
+ @buffer_of_lines[@line_index - 1] += @buffer_of_lines.delete_at(@line_index)
+ @line_index -= 1
+ elsif @byte_pointer > 0
+ byte_size = Reline::Unicode.get_prev_mbchar_size(current_line, @byte_pointer)
+ line, = byteslice!(current_line, @byte_pointer - byte_size, byte_size)
+ set_current_line(line, @byte_pointer - byte_size)
+ end
end
- arg -= 1
- em_delete_prev_char(key, arg: arg) if arg > 0
+ process_auto_indent
end
alias_method :backward_delete_char, :em_delete_prev_char
@@ -2543,23 +1840,23 @@ class Reline::LineEditor
# the line. With a negative numeric argument, kill backward
# from the cursor to the beginning of the current line.
private def ed_kill_line(key)
- if @line.bytesize > @byte_pointer
- @line, deleted = byteslice!(@line, @byte_pointer, @line.bytesize - @byte_pointer)
- @byte_pointer = @line.bytesize
- @cursor = @cursor_max = calculate_width(@line)
+ if current_line.bytesize > @byte_pointer
+ line, deleted = byteslice!(current_line, @byte_pointer, current_line.bytesize - @byte_pointer)
+ set_current_line(line, line.bytesize)
@kill_ring.append(deleted)
- elsif @is_multiline and @byte_pointer == @line.bytesize and @buffer_of_lines.size > @line_index + 1
- @cursor = calculate_width(@line)
- @byte_pointer = @line.bytesize
- @line += @buffer_of_lines.delete_at(@line_index + 1)
- @cursor_max = calculate_width(@line)
- @buffer_of_lines[@line_index] = @line
- @rerender_all = true
- @rest_height += 1
+ elsif @byte_pointer == current_line.bytesize and @buffer_of_lines.size > @line_index + 1
+ set_current_line(current_line + @buffer_of_lines.delete_at(@line_index + 1), current_line.bytesize)
end
end
alias_method :kill_line, :ed_kill_line
+ # Editline:: +vi_change_to_eol+ (vi command: +C+) + Kill and change from the cursor to the end of the line.
+ private def vi_change_to_eol(key)
+ ed_kill_line(key)
+
+ @config.editing_mode = :vi_insert
+ end
+
# Editline:: +vi-kill-line-prev+ (vi: +Ctrl-U+) Delete the string from the
# beginning of the edit buffer to the cursor and save it to the
# cut buffer.
@@ -2567,11 +1864,9 @@ class Reline::LineEditor
# to the beginning of the current line.
private def vi_kill_line_prev(key)
if @byte_pointer > 0
- @line, deleted = byteslice!(@line, 0, @byte_pointer)
- @byte_pointer = 0
+ line, deleted = byteslice!(current_line, 0, @byte_pointer)
+ set_current_line(line, 0)
@kill_ring.append(deleted, true)
- @cursor_max = calculate_width(@line)
- @cursor = 0
end
end
alias_method :unix_line_discard, :vi_kill_line_prev
@@ -2581,47 +1876,32 @@ class Reline::LineEditor
# GNU Readline:: +kill-whole-line+ (not bound) Kill all characters on the
# current line, no matter where point is.
private def em_kill_line(key)
- if @line.size > 0
- @kill_ring.append(@line.dup, true)
- @line.clear
- @byte_pointer = 0
- @cursor_max = 0
- @cursor = 0
+ if current_line.size > 0
+ @kill_ring.append(current_line.dup, true)
+ set_current_line('', 0)
end
end
alias_method :kill_whole_line, :em_kill_line
private def em_delete(key)
- if @line.empty? and (not @is_multiline or @buffer_of_lines.size == 1) and key == "\C-d".ord
- @line = nil
- if @buffer_of_lines.size > 1
- scroll_down(@highest_in_all - @first_line_started_from)
- end
- Reline::IOGate.move_cursor_column(0)
+ if current_line.empty? and @buffer_of_lines.size == 1 and key == "\C-d".ord
@eof = true
finish
- elsif @byte_pointer < @line.bytesize
- splitted_last = @line.byteslice(@byte_pointer, @line.bytesize)
+ elsif @byte_pointer < current_line.bytesize
+ splitted_last = current_line.byteslice(@byte_pointer, current_line.bytesize)
mbchar = splitted_last.grapheme_clusters.first
- width = Reline::Unicode.get_mbchar_width(mbchar)
- @cursor_max -= width
- @line, = byteslice!(@line, @byte_pointer, mbchar.bytesize)
- elsif @is_multiline and @byte_pointer == @line.bytesize and @buffer_of_lines.size > @line_index + 1
- @cursor = calculate_width(@line)
- @byte_pointer = @line.bytesize
- @line += @buffer_of_lines.delete_at(@line_index + 1)
- @cursor_max = calculate_width(@line)
- @buffer_of_lines[@line_index] = @line
- @rerender_all = true
- @rest_height += 1
+ line, = byteslice!(current_line, @byte_pointer, mbchar.bytesize)
+ set_current_line(line)
+ elsif @byte_pointer == current_line.bytesize and @buffer_of_lines.size > @line_index + 1
+ set_current_line(current_line + @buffer_of_lines.delete_at(@line_index + 1), current_line.bytesize)
end
end
alias_method :delete_char, :em_delete
private def em_delete_or_list(key)
- if @line.empty? or @byte_pointer < @line.bytesize
+ if current_line.empty? or @byte_pointer < current_line.bytesize
em_delete(key)
- else # show completed list
+ elsif !@config.autocompletion # show completed list
result = call_completion_proc
if result.is_a?(Array)
complete(result, true)
@@ -2632,162 +1912,136 @@ class Reline::LineEditor
private def em_yank(key)
yanked = @kill_ring.yank
- if yanked
- @line = byteinsert(@line, @byte_pointer, yanked)
- yanked_width = calculate_width(yanked)
- @cursor += yanked_width
- @cursor_max += yanked_width
- @byte_pointer += yanked.bytesize
- end
+ insert_text(yanked) if yanked
end
alias_method :yank, :em_yank
private def em_yank_pop(key)
yanked, prev_yank = @kill_ring.yank_pop
if yanked
- prev_yank_width = calculate_width(prev_yank)
- @cursor -= prev_yank_width
- @cursor_max -= prev_yank_width
- @byte_pointer -= prev_yank.bytesize
- @line, = byteslice!(@line, @byte_pointer, prev_yank.bytesize)
- @line = byteinsert(@line, @byte_pointer, yanked)
- yanked_width = calculate_width(yanked)
- @cursor += yanked_width
- @cursor_max += yanked_width
- @byte_pointer += yanked.bytesize
+ line, = byteslice!(current_line, @byte_pointer - prev_yank.bytesize, prev_yank.bytesize)
+ set_current_line(line, @byte_pointer - prev_yank.bytesize)
+ insert_text(yanked)
end
end
alias_method :yank_pop, :em_yank_pop
private def ed_clear_screen(key)
- @cleared = true
+ Reline::IOGate.clear_screen
+ @screen_size = Reline::IOGate.get_screen_size
+ @rendered_screen.lines = []
+ @rendered_screen.base_y = 0
+ @rendered_screen.cursor_y = 0
end
alias_method :clear_screen, :ed_clear_screen
private def em_next_word(key)
- if @line.bytesize > @byte_pointer
- byte_size, width = Reline::Unicode.em_forward_word(@line, @byte_pointer)
+ if current_line.bytesize > @byte_pointer
+ byte_size, _ = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
@byte_pointer += byte_size
- @cursor += width
end
end
alias_method :forward_word, :em_next_word
private def ed_prev_word(key)
if @byte_pointer > 0
- byte_size, width = Reline::Unicode.em_backward_word(@line, @byte_pointer)
+ byte_size, _ = Reline::Unicode.em_backward_word(current_line, @byte_pointer)
@byte_pointer -= byte_size
- @cursor -= width
end
end
alias_method :backward_word, :ed_prev_word
private def em_delete_next_word(key)
- if @line.bytesize > @byte_pointer
- byte_size, width = Reline::Unicode.em_forward_word(@line, @byte_pointer)
- @line, word = byteslice!(@line, @byte_pointer, byte_size)
+ if current_line.bytesize > @byte_pointer
+ byte_size, _ = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
+ line, word = byteslice!(current_line, @byte_pointer, byte_size)
+ set_current_line(line)
@kill_ring.append(word)
- @cursor_max -= width
end
end
+ alias_method :kill_word, :em_delete_next_word
private def ed_delete_prev_word(key)
if @byte_pointer > 0
- byte_size, width = Reline::Unicode.em_backward_word(@line, @byte_pointer)
- @line, word = byteslice!(@line, @byte_pointer - byte_size, byte_size)
+ byte_size, _ = Reline::Unicode.em_backward_word(current_line, @byte_pointer)
+ line, word = byteslice!(current_line, @byte_pointer - byte_size, byte_size)
+ set_current_line(line, @byte_pointer - byte_size)
@kill_ring.append(word, true)
- @byte_pointer -= byte_size
- @cursor -= width
- @cursor_max -= width
end
end
+ alias_method :backward_kill_word, :ed_delete_prev_word
private def ed_transpose_chars(key)
if @byte_pointer > 0
- if @cursor_max > @cursor
- byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
- mbchar = @line.byteslice(@byte_pointer, byte_size)
- width = Reline::Unicode.get_mbchar_width(mbchar)
- @cursor += width
+ if @byte_pointer < current_line.bytesize
+ byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
@byte_pointer += byte_size
end
- back1_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
+ back1_byte_size = Reline::Unicode.get_prev_mbchar_size(current_line, @byte_pointer)
if (@byte_pointer - back1_byte_size) > 0
- back2_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer - back1_byte_size)
+ back2_byte_size = Reline::Unicode.get_prev_mbchar_size(current_line, @byte_pointer - back1_byte_size)
back2_pointer = @byte_pointer - back1_byte_size - back2_byte_size
- @line, back2_mbchar = byteslice!(@line, back2_pointer, back2_byte_size)
- @line = byteinsert(@line, @byte_pointer - back2_byte_size, back2_mbchar)
+ line, back2_mbchar = byteslice!(current_line, back2_pointer, back2_byte_size)
+ set_current_line(byteinsert(line, @byte_pointer - back2_byte_size, back2_mbchar))
end
end
end
alias_method :transpose_chars, :ed_transpose_chars
private def ed_transpose_words(key)
- left_word_start, middle_start, right_word_start, after_start = Reline::Unicode.ed_transpose_words(@line, @byte_pointer)
- before = @line.byteslice(0, left_word_start)
- left_word = @line.byteslice(left_word_start, middle_start - left_word_start)
- middle = @line.byteslice(middle_start, right_word_start - middle_start)
- right_word = @line.byteslice(right_word_start, after_start - right_word_start)
- after = @line.byteslice(after_start, @line.bytesize - after_start)
+ left_word_start, middle_start, right_word_start, after_start = Reline::Unicode.ed_transpose_words(current_line, @byte_pointer)
+ before = current_line.byteslice(0, left_word_start)
+ left_word = current_line.byteslice(left_word_start, middle_start - left_word_start)
+ middle = current_line.byteslice(middle_start, right_word_start - middle_start)
+ right_word = current_line.byteslice(right_word_start, after_start - right_word_start)
+ after = current_line.byteslice(after_start, current_line.bytesize - after_start)
return if left_word.empty? or right_word.empty?
- @line = before + right_word + middle + left_word + after
from_head_to_left_word = before + right_word + middle + left_word
- @byte_pointer = from_head_to_left_word.bytesize
- @cursor = calculate_width(from_head_to_left_word)
+ set_current_line(from_head_to_left_word + after, from_head_to_left_word.bytesize)
end
alias_method :transpose_words, :ed_transpose_words
private def em_capitol_case(key)
- if @line.bytesize > @byte_pointer
- byte_size, _, new_str = Reline::Unicode.em_forward_word_with_capitalization(@line, @byte_pointer)
- before = @line.byteslice(0, @byte_pointer)
- after = @line.byteslice((@byte_pointer + byte_size)..-1)
- @line = before + new_str + after
- @byte_pointer += new_str.bytesize
- @cursor += calculate_width(new_str)
+ if current_line.bytesize > @byte_pointer
+ byte_size, _, new_str = Reline::Unicode.em_forward_word_with_capitalization(current_line, @byte_pointer)
+ before = current_line.byteslice(0, @byte_pointer)
+ after = current_line.byteslice((@byte_pointer + byte_size)..-1)
+ set_current_line(before + new_str + after, @byte_pointer + new_str.bytesize)
end
end
alias_method :capitalize_word, :em_capitol_case
private def em_lower_case(key)
- if @line.bytesize > @byte_pointer
- byte_size, = Reline::Unicode.em_forward_word(@line, @byte_pointer)
- part = @line.byteslice(@byte_pointer, byte_size).grapheme_clusters.map { |mbchar|
+ if current_line.bytesize > @byte_pointer
+ byte_size, = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
+ part = current_line.byteslice(@byte_pointer, byte_size).grapheme_clusters.map { |mbchar|
mbchar =~ /[A-Z]/ ? mbchar.downcase : mbchar
}.join
- rest = @line.byteslice((@byte_pointer + byte_size)..-1)
- @line = @line.byteslice(0, @byte_pointer) + part
- @byte_pointer = @line.bytesize
- @cursor = calculate_width(@line)
- @cursor_max = @cursor + calculate_width(rest)
- @line += rest
+ rest = current_line.byteslice((@byte_pointer + byte_size)..-1)
+ line = current_line.byteslice(0, @byte_pointer) + part
+ set_current_line(line + rest, line.bytesize)
end
end
alias_method :downcase_word, :em_lower_case
private def em_upper_case(key)
- if @line.bytesize > @byte_pointer
- byte_size, = Reline::Unicode.em_forward_word(@line, @byte_pointer)
- part = @line.byteslice(@byte_pointer, byte_size).grapheme_clusters.map { |mbchar|
+ if current_line.bytesize > @byte_pointer
+ byte_size, = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
+ part = current_line.byteslice(@byte_pointer, byte_size).grapheme_clusters.map { |mbchar|
mbchar =~ /[a-z]/ ? mbchar.upcase : mbchar
}.join
- rest = @line.byteslice((@byte_pointer + byte_size)..-1)
- @line = @line.byteslice(0, @byte_pointer) + part
- @byte_pointer = @line.bytesize
- @cursor = calculate_width(@line)
- @cursor_max = @cursor + calculate_width(rest)
- @line += rest
+ rest = current_line.byteslice((@byte_pointer + byte_size)..-1)
+ line = current_line.byteslice(0, @byte_pointer) + part
+ set_current_line(line + rest, line.bytesize)
end
end
alias_method :upcase_word, :em_upper_case
private def em_kill_region(key)
if @byte_pointer > 0
- byte_size, width = Reline::Unicode.em_big_backward_word(@line, @byte_pointer)
- @line, deleted = byteslice!(@line, @byte_pointer - byte_size, byte_size)
- @byte_pointer -= byte_size
- @cursor -= width
- @cursor_max -= width
+ byte_size, _ = Reline::Unicode.em_big_backward_word(current_line, @byte_pointer)
+ line, deleted = byteslice!(current_line, @byte_pointer - byte_size, byte_size)
+ set_current_line(line, @byte_pointer - byte_size)
@kill_ring.append(deleted, true)
end
end
@@ -2815,10 +2069,9 @@ class Reline::LineEditor
alias_method :vi_movement_mode, :vi_command_mode
private def vi_next_word(key, arg: 1)
- if @line.bytesize > @byte_pointer
- byte_size, width = Reline::Unicode.vi_forward_word(@line, @byte_pointer, @drop_terminate_spaces)
+ if current_line.bytesize > @byte_pointer
+ byte_size, _ = Reline::Unicode.vi_forward_word(current_line, @byte_pointer, @drop_terminate_spaces)
@byte_pointer += byte_size
- @cursor += width
end
arg -= 1
vi_next_word(key, arg: arg) if arg > 0
@@ -2826,38 +2079,32 @@ class Reline::LineEditor
private def vi_prev_word(key, arg: 1)
if @byte_pointer > 0
- byte_size, width = Reline::Unicode.vi_backward_word(@line, @byte_pointer)
+ byte_size, _ = Reline::Unicode.vi_backward_word(current_line, @byte_pointer)
@byte_pointer -= byte_size
- @cursor -= width
end
arg -= 1
vi_prev_word(key, arg: arg) if arg > 0
end
private def vi_end_word(key, arg: 1, inclusive: false)
- if @line.bytesize > @byte_pointer
- byte_size, width = Reline::Unicode.vi_forward_end_word(@line, @byte_pointer)
+ if current_line.bytesize > @byte_pointer
+ byte_size, _ = Reline::Unicode.vi_forward_end_word(current_line, @byte_pointer)
@byte_pointer += byte_size
- @cursor += width
end
arg -= 1
if inclusive and arg.zero?
- byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
+ byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
if byte_size > 0
- c = @line.byteslice(@byte_pointer, byte_size)
- width = Reline::Unicode.get_mbchar_width(c)
@byte_pointer += byte_size
- @cursor += width
end
end
vi_end_word(key, arg: arg) if arg > 0
end
private def vi_next_big_word(key, arg: 1)
- if @line.bytesize > @byte_pointer
- byte_size, width = Reline::Unicode.vi_big_forward_word(@line, @byte_pointer)
+ if current_line.bytesize > @byte_pointer
+ byte_size, _ = Reline::Unicode.vi_big_forward_word(current_line, @byte_pointer)
@byte_pointer += byte_size
- @cursor += width
end
arg -= 1
vi_next_big_word(key, arg: arg) if arg > 0
@@ -2865,50 +2112,39 @@ class Reline::LineEditor
private def vi_prev_big_word(key, arg: 1)
if @byte_pointer > 0
- byte_size, width = Reline::Unicode.vi_big_backward_word(@line, @byte_pointer)
+ byte_size, _ = Reline::Unicode.vi_big_backward_word(current_line, @byte_pointer)
@byte_pointer -= byte_size
- @cursor -= width
end
arg -= 1
vi_prev_big_word(key, arg: arg) if arg > 0
end
private def vi_end_big_word(key, arg: 1, inclusive: false)
- if @line.bytesize > @byte_pointer
- byte_size, width = Reline::Unicode.vi_big_forward_end_word(@line, @byte_pointer)
+ if current_line.bytesize > @byte_pointer
+ byte_size, _ = Reline::Unicode.vi_big_forward_end_word(current_line, @byte_pointer)
@byte_pointer += byte_size
- @cursor += width
end
arg -= 1
if inclusive and arg.zero?
- byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
+ byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
if byte_size > 0
- c = @line.byteslice(@byte_pointer, byte_size)
- width = Reline::Unicode.get_mbchar_width(c)
@byte_pointer += byte_size
- @cursor += width
end
end
vi_end_big_word(key, arg: arg) if arg > 0
end
private def vi_delete_prev_char(key)
- if @is_multiline and @cursor == 0 and @line_index > 0
- @buffer_of_lines[@line_index] = @line
- @cursor = calculate_width(@buffer_of_lines[@line_index - 1])
+ if @byte_pointer == 0 and @line_index > 0
@byte_pointer = @buffer_of_lines[@line_index - 1].bytesize
@buffer_of_lines[@line_index - 1] += @buffer_of_lines.delete_at(@line_index)
@line_index -= 1
- @line = @buffer_of_lines[@line_index]
- @cursor_max = calculate_width(@line)
- @rerender_all = true
- elsif @cursor > 0
- byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
+ process_auto_indent cursor_dependent: false
+ elsif @byte_pointer > 0
+ byte_size = Reline::Unicode.get_prev_mbchar_size(current_line, @byte_pointer)
@byte_pointer -= byte_size
- @line, mbchar = byteslice!(@line, @byte_pointer, byte_size)
- width = Reline::Unicode.get_mbchar_width(mbchar)
- @cursor -= width
- @cursor_max -= width
+ line, _ = byteslice!(current_line, @byte_pointer, byte_size)
+ set_current_line(line)
end
end
@@ -2923,78 +2159,81 @@ class Reline::LineEditor
end
private def ed_delete_prev_char(key, arg: 1)
- deleted = ''
+ deleted = +''
arg.times do
- if @cursor > 0
- byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
+ if @byte_pointer > 0
+ byte_size = Reline::Unicode.get_prev_mbchar_size(current_line, @byte_pointer)
@byte_pointer -= byte_size
- @line, mbchar = byteslice!(@line, @byte_pointer, byte_size)
+ line, mbchar = byteslice!(current_line, @byte_pointer, byte_size)
+ set_current_line(line)
deleted.prepend(mbchar)
- width = Reline::Unicode.get_mbchar_width(mbchar)
- @cursor -= width
- @cursor_max -= width
end
end
copy_for_vi(deleted)
end
- private def vi_zero(key)
- @byte_pointer = 0
- @cursor = 0
- end
-
- private def vi_change_meta(key, arg: 1)
- @drop_terminate_spaces = true
- @waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
- if byte_pointer_diff > 0
- @line, cut = byteslice!(@line, @byte_pointer, byte_pointer_diff)
- elsif byte_pointer_diff < 0
- @line, cut = byteslice!(@line, @byte_pointer + byte_pointer_diff, -byte_pointer_diff)
- end
- copy_for_vi(cut)
- @cursor += cursor_diff if cursor_diff < 0
- @cursor_max -= cursor_diff.abs
- @byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
- @config.editing_mode = :vi_insert
- @drop_terminate_spaces = false
- }
- @waiting_operator_vi_arg = arg
+ private def vi_change_meta(key, arg: nil)
+ if @vi_waiting_operator
+ set_current_line('', 0) if @vi_waiting_operator == :vi_change_meta_confirm && arg.nil?
+ @vi_waiting_operator = nil
+ @vi_waiting_operator_arg = nil
+ else
+ @drop_terminate_spaces = true
+ @vi_waiting_operator = :vi_change_meta_confirm
+ @vi_waiting_operator_arg = arg || 1
+ end
end
- private def vi_delete_meta(key, arg: 1)
- @waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
- if byte_pointer_diff > 0
- @line, cut = byteslice!(@line, @byte_pointer, byte_pointer_diff)
- elsif byte_pointer_diff < 0
- @line, cut = byteslice!(@line, @byte_pointer + byte_pointer_diff, -byte_pointer_diff)
- end
- copy_for_vi(cut)
- @cursor += cursor_diff if cursor_diff < 0
- @cursor_max -= cursor_diff.abs
- @byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
- }
- @waiting_operator_vi_arg = arg
+ private def vi_change_meta_confirm(byte_pointer_diff)
+ vi_delete_meta_confirm(byte_pointer_diff)
+ @config.editing_mode = :vi_insert
+ @drop_terminate_spaces = false
end
- private def vi_yank(key, arg: 1)
- @waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
- if byte_pointer_diff > 0
- cut = @line.byteslice(@byte_pointer, byte_pointer_diff)
- elsif byte_pointer_diff < 0
- cut = @line.byteslice(@byte_pointer + byte_pointer_diff, -byte_pointer_diff)
- end
- copy_for_vi(cut)
- }
- @waiting_operator_vi_arg = arg
+ private def vi_delete_meta(key, arg: nil)
+ if @vi_waiting_operator
+ set_current_line('', 0) if @vi_waiting_operator == :vi_delete_meta_confirm && arg.nil?
+ @vi_waiting_operator = nil
+ @vi_waiting_operator_arg = nil
+ else
+ @vi_waiting_operator = :vi_delete_meta_confirm
+ @vi_waiting_operator_arg = arg || 1
+ end
+ end
+
+ private def vi_delete_meta_confirm(byte_pointer_diff)
+ if byte_pointer_diff > 0
+ line, cut = byteslice!(current_line, @byte_pointer, byte_pointer_diff)
+ elsif byte_pointer_diff < 0
+ line, cut = byteslice!(current_line, @byte_pointer + byte_pointer_diff, -byte_pointer_diff)
+ end
+ copy_for_vi(cut)
+ set_current_line(line || '', @byte_pointer + (byte_pointer_diff < 0 ? byte_pointer_diff : 0))
+ end
+
+ private def vi_yank(key, arg: nil)
+ if @vi_waiting_operator
+ copy_for_vi(current_line) if @vi_waiting_operator == :vi_yank_confirm && arg.nil?
+ @vi_waiting_operator = nil
+ @vi_waiting_operator_arg = nil
+ else
+ @vi_waiting_operator = :vi_yank_confirm
+ @vi_waiting_operator_arg = arg || 1
+ end
+ end
+
+ private def vi_yank_confirm(byte_pointer_diff)
+ if byte_pointer_diff > 0
+ cut = current_line.byteslice(@byte_pointer, byte_pointer_diff)
+ elsif byte_pointer_diff < 0
+ cut = current_line.byteslice(@byte_pointer + byte_pointer_diff, -byte_pointer_diff)
+ end
+ copy_for_vi(cut)
end
private def vi_list_or_eof(key)
- if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1)
- @line = nil
- if @buffer_of_lines.size > 1
- scroll_down(@highest_in_all - @first_line_started_from)
- end
- Reline::IOGate.move_cursor_column(0)
+ if current_line.empty? and @buffer_of_lines.size == 1
+ set_current_line('', 0)
@eof = true
finish
else
@@ -3005,18 +2244,15 @@ class Reline::LineEditor
alias_method :vi_eof_maybe, :vi_list_or_eof
private def ed_delete_next_char(key, arg: 1)
- byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
- unless @line.empty? || byte_size == 0
- @line, mbchar = byteslice!(@line, @byte_pointer, byte_size)
+ byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
+ unless current_line.empty? || byte_size == 0
+ line, mbchar = byteslice!(current_line, @byte_pointer, byte_size)
copy_for_vi(mbchar)
- width = Reline::Unicode.get_mbchar_width(mbchar)
- @cursor_max -= width
- if @cursor > 0 and @cursor >= @cursor_max
- byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
- mbchar = @line.byteslice(@byte_pointer - byte_size, byte_size)
- width = Reline::Unicode.get_mbchar_width(mbchar)
- @byte_pointer -= byte_size
- @cursor -= width
+ if @byte_pointer > 0 && current_line.bytesize == @byte_pointer + byte_size
+ byte_size = Reline::Unicode.get_prev_mbchar_size(line, @byte_pointer)
+ set_current_line(line, @byte_pointer - byte_size)
+ else
+ set_current_line(line, @byte_pointer)
end
end
arg -= 1
@@ -3027,54 +2263,25 @@ class Reline::LineEditor
if Reline::HISTORY.empty?
return
end
- if @history_pointer.nil?
- @history_pointer = 0
- @line_backup_in_history = @line
- @line = Reline::HISTORY[@history_pointer]
- @cursor_max = calculate_width(@line)
- @cursor = 0
- @byte_pointer = 0
- elsif @history_pointer.zero?
- return
- else
- Reline::HISTORY[@history_pointer] = @line
- @history_pointer = 0
- @line = Reline::HISTORY[@history_pointer]
- @cursor_max = calculate_width(@line)
- @cursor = 0
- @byte_pointer = 0
- end
+ move_history(0, line: :start, cursor: :start)
end
private def vi_histedit(key)
path = Tempfile.open { |fp|
- if @is_multiline
- fp.write whole_lines.join("\n")
- else
- fp.write @line
- end
+ fp.write whole_lines.join("\n")
fp.path
}
system("#{ENV['EDITOR']} #{path}")
- if @is_multiline
- @buffer_of_lines = File.read(path).split("\n")
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
- @line_index = 0
- @line = @buffer_of_lines[@line_index]
- @rerender_all = true
- else
- @line = File.read(path)
- end
+ @buffer_of_lines = File.read(path).split("\n")
+ @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
+ @line_index = 0
finish
end
private def vi_paste_prev(key, arg: 1)
if @vi_clipboard.size > 0
- @line = byteinsert(@line, @byte_pointer, @vi_clipboard)
- @cursor_max += calculate_width(@vi_clipboard)
cursor_point = @vi_clipboard.grapheme_clusters[0..-2].join
- @cursor += calculate_width(cursor_point)
- @byte_pointer += cursor_point.bytesize
+ set_current_line(byteinsert(current_line, @byte_pointer, @vi_clipboard), @byte_pointer + cursor_point.bytesize)
end
arg -= 1
vi_paste_prev(key, arg: arg) if arg > 0
@@ -3082,11 +2289,9 @@ class Reline::LineEditor
private def vi_paste_next(key, arg: 1)
if @vi_clipboard.size > 0
- byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
- @line = byteinsert(@line, @byte_pointer + byte_size, @vi_clipboard)
- @cursor_max += calculate_width(@vi_clipboard)
- @cursor += calculate_width(@vi_clipboard)
- @byte_pointer += @vi_clipboard.bytesize
+ byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
+ line = byteinsert(current_line, @byte_pointer + byte_size, @vi_clipboard)
+ set_current_line(line, @byte_pointer + @vi_clipboard.bytesize)
end
arg -= 1
vi_paste_next(key, arg: arg) if arg > 0
@@ -3110,43 +2315,33 @@ class Reline::LineEditor
end
private def vi_to_column(key, arg: 0)
- @byte_pointer, @cursor = @line.grapheme_clusters.inject([0, 0]) { |total, gc|
- # total has [byte_size, cursor]
+ # Implementing behavior of vi, not Readline's vi-mode.
+ @byte_pointer, = current_line.grapheme_clusters.inject([0, 0]) { |(total_byte_size, total_width), gc|
mbchar_width = Reline::Unicode.get_mbchar_width(gc)
- if (total.last + mbchar_width) >= arg
- break total
- elsif (total.last + mbchar_width) >= @cursor_max
- break total
- else
- total = [total.first + gc.bytesize, total.last + mbchar_width]
- total
- end
+ break [total_byte_size, total_width] if (total_width + mbchar_width) >= arg
+ [total_byte_size + gc.bytesize, total_width + mbchar_width]
}
end
private def vi_replace_char(key, arg: 1)
@waiting_proc = ->(k) {
if arg == 1
- byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
- before = @line.byteslice(0, @byte_pointer)
+ byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
+ before = current_line.byteslice(0, @byte_pointer)
remaining_point = @byte_pointer + byte_size
- after = @line.byteslice(remaining_point, @line.bytesize - remaining_point)
- @line = before + k.chr + after
- @cursor_max = calculate_width(@line)
+ after = current_line.byteslice(remaining_point, current_line.bytesize - remaining_point)
+ set_current_line(before + k.chr + after)
@waiting_proc = nil
elsif arg > 1
byte_size = 0
arg.times do
- byte_size += Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer + byte_size)
+ byte_size += Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer + byte_size)
end
- before = @line.byteslice(0, @byte_pointer)
+ before = current_line.byteslice(0, @byte_pointer)
remaining_point = @byte_pointer + byte_size
- after = @line.byteslice(remaining_point, @line.bytesize - remaining_point)
+ after = current_line.byteslice(remaining_point, current_line.bytesize - remaining_point)
replaced = k.chr * arg
- @line = before + replaced + after
- @byte_pointer += replaced.bytesize
- @cursor += calculate_width(replaced)
- @cursor_max = calculate_width(@line)
+ set_current_line(before + replaced + after, @byte_pointer + replaced.bytesize)
@waiting_proc = nil
end
}
@@ -3169,7 +2364,7 @@ class Reline::LineEditor
prev_total = nil
total = nil
found = false
- @line.byteslice(@byte_pointer..-1).grapheme_clusters.each do |mbchar|
+ current_line.byteslice(@byte_pointer..-1).grapheme_clusters.each do |mbchar|
# total has [byte_size, cursor]
unless total
# skip cursor point
@@ -3189,21 +2384,16 @@ class Reline::LineEditor
end
end
if not need_prev_char and found and total
- byte_size, width = total
+ byte_size, _ = total
@byte_pointer += byte_size
- @cursor += width
elsif need_prev_char and found and prev_total
- byte_size, width = prev_total
+ byte_size, _ = prev_total
@byte_pointer += byte_size
- @cursor += width
end
if inclusive
- byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
+ byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
if byte_size > 0
- c = @line.byteslice(@byte_pointer, byte_size)
- width = Reline::Unicode.get_mbchar_width(c)
@byte_pointer += byte_size
- @cursor += width
end
end
@waiting_proc = nil
@@ -3226,7 +2416,7 @@ class Reline::LineEditor
prev_total = nil
total = nil
found = false
- @line.byteslice(0..@byte_pointer).grapheme_clusters.reverse_each do |mbchar|
+ current_line.byteslice(0..@byte_pointer).grapheme_clusters.reverse_each do |mbchar|
# total has [byte_size, cursor]
unless total
# skip cursor point
@@ -3246,26 +2436,19 @@ class Reline::LineEditor
end
end
if not need_next_char and found and total
- byte_size, width = total
+ byte_size, _ = total
@byte_pointer -= byte_size
- @cursor -= width
elsif need_next_char and found and prev_total
- byte_size, width = prev_total
+ byte_size, _ = prev_total
@byte_pointer -= byte_size
- @cursor -= width
end
@waiting_proc = nil
end
private def vi_join_lines(key, arg: 1)
- if @is_multiline and @buffer_of_lines.size > @line_index + 1
- @cursor = calculate_width(@line)
- @byte_pointer = @line.bytesize
- @line += ' ' + @buffer_of_lines.delete_at(@line_index + 1).lstrip
- @cursor_max = calculate_width(@line)
- @buffer_of_lines[@line_index] = @line
- @rerender_all = true
- @rest_height += 1
+ if @buffer_of_lines.size > @line_index + 1
+ next_line = @buffer_of_lines.delete_at(@line_index + 1).lstrip
+ set_current_line(current_line + ' ' + next_line, current_line.bytesize)
end
arg -= 1
vi_join_lines(key, arg: arg) if arg > 0
@@ -3279,11 +2462,16 @@ class Reline::LineEditor
private def em_exchange_mark(key)
return unless @mark_pointer
new_pointer = [@byte_pointer, @line_index]
- @previous_line_index = @line_index
@byte_pointer, @line_index = @mark_pointer
- @cursor = calculate_width(@line.byteslice(0, @byte_pointer))
- @cursor_max = calculate_width(@line)
@mark_pointer = new_pointer
end
alias_method :exchange_point_and_mark, :em_exchange_mark
+
+ private def emacs_editing_mode(key)
+ @config.editing_mode = :emacs
+ end
+
+ private def vi_editing_mode(key)
+ @config.editing_mode = :vi_insert
+ end
end
diff --git a/lib/reline/reline.gemspec b/lib/reline/reline.gemspec
index 7bf1f8758b..dfaf966728 100644
--- a/lib/reline/reline.gemspec
+++ b/lib/reline/reline.gemspec
@@ -18,6 +18,11 @@ Gem::Specification.new do |spec|
spec.files = Dir['BSDL', 'COPYING', 'README.md', 'license_of_rb-readline', 'lib/**/*']
spec.require_paths = ['lib']
+ spec.metadata = {
+ "bug_tracker_uri" => "https://github.com/ruby/reline/issues",
+ "changelog_uri" => "https://github.com/ruby/reline/releases",
+ "source_code_uri" => "https://github.com/ruby/reline"
+ }
spec.required_ruby_version = Gem::Requirement.new('>= 2.6')
diff --git a/lib/reline/terminfo.rb b/lib/reline/terminfo.rb
index 2cfa32b9f7..6885a0c6be 100644
--- a/lib/reline/terminfo.rb
+++ b/lib/reline/terminfo.rb
@@ -80,23 +80,11 @@ module Reline::Terminfo
def self.setupterm(term, fildes)
errret_int = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT)
ret = @setupterm.(term, fildes, errret_int)
- errret = errret_int[0, Fiddle::SIZEOF_INT].unpack1('i')
case ret
when 0 # OK
- 0
+ @term_supported = true
when -1 # ERR
- case errret
- when 1
- raise TerminfoError.new('The terminal is hardcopy, cannot be used for curses applications.')
- when 0
- raise TerminfoError.new('The terminal could not be found, or that it is a generic type, having too little information for curses applications to run.')
- when -1
- raise TerminfoError.new('The terminfo database could not be found.')
- else # unknown
- -1
- end
- else # unknown
- -2
+ @term_supported = false
end
end
@@ -148,9 +136,14 @@ module Reline::Terminfo
num
end
+ # NOTE: This means Fiddle and curses are enabled.
def self.enabled?
true
end
+
+ def self.term_supported?
+ @term_supported
+ end
end if Reline::Terminfo.curses_dl
module Reline::Terminfo
diff --git a/lib/reline/unicode.rb b/lib/reline/unicode.rb
index 0a7f59cf06..82c9ec427c 100644
--- a/lib/reline/unicode.rb
+++ b/lib/reline/unicode.rb
@@ -41,26 +41,6 @@ class Reline::Unicode
OSC_REGEXP = /\e\]\d+(?:;[^;\a\e]+)*(?:\a|\e\\)/
WIDTH_SCANNER = /\G(?:(#{NON_PRINTING_START})|(#{NON_PRINTING_END})|(#{CSI_REGEXP})|(#{OSC_REGEXP})|(\X))/o
- def self.get_mbchar_byte_size_by_first_char(c)
- # Checks UTF-8 character byte size
- case c.ord
- # 0b0xxxxxxx
- when ->(code) { (code ^ 0b10000000).allbits?(0b10000000) } then 1
- # 0b110xxxxx
- when ->(code) { (code ^ 0b00100000).allbits?(0b11100000) } then 2
- # 0b1110xxxx
- when ->(code) { (code ^ 0b00010000).allbits?(0b11110000) } then 3
- # 0b11110xxx
- when ->(code) { (code ^ 0b00001000).allbits?(0b11111000) } then 4
- # 0b111110xx
- when ->(code) { (code ^ 0b00000100).allbits?(0b11111100) } then 5
- # 0b1111110x
- when ->(code) { (code ^ 0b00000010).allbits?(0b11111110) } then 6
- # successor of mbchar
- else 0
- end
- end
-
def self.escape_for_print(str)
str.chars.map! { |gr|
escaped = EscapedPairs[gr.ord]
@@ -148,10 +128,10 @@ class Reline::Unicode
end
end
- def self.split_by_width(str, max_width, encoding = str.encoding)
+ def self.split_by_width(str, max_width, encoding = str.encoding, offset: 0)
lines = [String.new(encoding: encoding)]
height = 1
- width = 0
+ width = offset
rest = str.encode(Encoding::UTF_8)
in_zero_width = false
seq = String.new(encoding: encoding)
@@ -165,7 +145,13 @@ class Reline::Unicode
lines.last << NON_PRINTING_END
when csi
lines.last << csi
- seq << csi
+ unless in_zero_width
+ if csi == -"\e[m" || csi == -"\e[0m"
+ seq.clear
+ else
+ seq << csi
+ end
+ end
when osc
lines.last << osc
seq << osc
@@ -193,32 +179,78 @@ class Reline::Unicode
# Take a chunk of a String cut by width with escape sequences.
def self.take_range(str, start_col, max_width)
+ take_mbchar_range(str, start_col, max_width).first
+ end
+
+ def self.take_mbchar_range(str, start_col, width, cover_begin: false, cover_end: false, padding: false)
chunk = String.new(encoding: str.encoding)
+
+ end_col = start_col + width
total_width = 0
rest = str.encode(Encoding::UTF_8)
in_zero_width = false
+ chunk_start_col = nil
+ chunk_end_col = nil
+ has_csi = false
rest.scan(WIDTH_SCANNER) do |non_printing_start, non_printing_end, csi, osc, gc|
case
when non_printing_start
in_zero_width = true
+ chunk << NON_PRINTING_START
when non_printing_end
in_zero_width = false
+ chunk << NON_PRINTING_END
when csi
+ has_csi = true
chunk << csi
when osc
chunk << osc
when gc
if in_zero_width
chunk << gc
+ next
+ end
+
+ mbchar_width = get_mbchar_width(gc)
+ prev_width = total_width
+ total_width += mbchar_width
+
+ if (cover_begin || padding ? total_width <= start_col : prev_width < start_col)
+ # Current character haven't reached start_col yet
+ next
+ elsif padding && !cover_begin && prev_width < start_col && start_col < total_width
+ # Add preceding padding. This padding might have background color.
+ chunk << ' '
+ chunk_start_col ||= start_col
+ chunk_end_col = total_width
+ next
+ elsif (cover_end ? prev_width < end_col : total_width <= end_col)
+ # Current character is in the range
+ chunk << gc
+ chunk_start_col ||= prev_width
+ chunk_end_col = total_width
+ break if total_width >= end_col
else
- mbchar_width = get_mbchar_width(gc)
- total_width += mbchar_width
- break if (start_col + max_width) < total_width
- chunk << gc if start_col < total_width
+ # Current character exceeds end_col
+ if padding && end_col < total_width
+ # Add succeeding padding. This padding might have background color.
+ chunk << ' '
+ chunk_start_col ||= prev_width
+ chunk_end_col = end_col
+ end
+ break
end
end
end
- chunk
+ chunk_start_col ||= start_col
+ chunk_end_col ||= start_col
+ if padding && chunk_end_col < end_col
+ # Append padding. This padding should not include background color.
+ chunk << "\e[0m" if has_csi
+ chunk << ' ' * (end_col - chunk_end_col)
+ chunk_end_col = end_col
+ end
+ [chunk, chunk_start_col, chunk_end_col - chunk_start_col]
end
def self.get_next_mbchar_size(line, byte_pointer)
diff --git a/lib/reline/unicode/east_asian_width.rb b/lib/reline/unicode/east_asian_width.rb
index 97dfec4c52..fa16a1bb56 100644
--- a/lib/reline/unicode/east_asian_width.rb
+++ b/lib/reline/unicode/east_asian_width.rb
@@ -1,6 +1,6 @@
class Reline::Unicode::EastAsianWidth
# This is based on EastAsianWidth.txt
- # EastAsianWidth.txt
+ # UNICODE_VERSION = '15.1.0'
# Fullwidth
TYPE_F = /^[#{ %W(
@@ -60,14 +60,14 @@ class Reline::Unicode::EastAsianWidth
\u{2E80}-\u{2E99}
\u{2E9B}-\u{2EF3}
\u{2F00}-\u{2FD5}
- \u{2FF0}-\u{2FFB}
+ \u{2FF0}-\u{2FFF}
\u{3001}-\u{303E}
\u{3041}-\u{3096}
\u{3099}-\u{30FF}
\u{3105}-\u{312F}
\u{3131}-\u{318E}
\u{3190}-\u{31E3}
- \u{31F0}-\u{321E}
+ \u{31EF}-\u{321E}
\u{3220}-\u{3247}
\u{3250}-\u{4DBF}
\u{4E00}-\u{A48C}
diff --git a/lib/reline/version.rb b/lib/reline/version.rb
index abb200526c..3d521da4e8 100644
--- a/lib/reline/version.rb
+++ b/lib/reline/version.rb
@@ -1,3 +1,3 @@
module Reline
- VERSION = '0.3.5'
+ VERSION = '0.5.4'
end
diff --git a/lib/reline/windows.rb b/lib/reline/windows.rb
index 7ea2a00f63..ee3f73e383 100644
--- a/lib/reline/windows.rb
+++ b/lib/reline/windows.rb
@@ -1,6 +1,8 @@
require 'fiddle/import'
class Reline::Windows
+ RESET_COLOR = "\e[0m"
+
def self.encoding
Encoding::UTF_8
end
@@ -85,7 +87,7 @@ class Reline::Windows
def call(*args)
import = @proto.split("")
args.each_with_index do |x, i|
- args[i], = [x == 0 ? nil : x].pack("p").unpack(POINTER_TYPE) if import[i] == "S"
+ args[i], = [x == 0 ? nil : +x].pack("p").unpack(POINTER_TYPE) if import[i] == "S"
args[i], = [x].pack("I").unpack("i") if import[i] == "I"
end
ret, = @func.call(*args)
@@ -257,7 +259,7 @@ class Reline::Windows
def self.check_input_event
num_of_events = 0.chr * 8
while @@output_buf.empty?
- Reline.core.line_editor.resize
+ Reline.core.line_editor.handle_signal
if @@WaitForSingleObject.(@@hConsoleInputHandle, 100) != 0 # max 0.1 sec
# prevent for background consolemode change
@@legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0)
@@ -295,7 +297,7 @@ class Reline::Windows
yield
end
- def self.getc
+ def self.getc(_timeout_second)
check_input_event
@@output_buf.shift
end
diff --git a/lib/resolv-replace.gemspec b/lib/resolv-replace.gemspec
deleted file mode 100644
index 48f7108a8e..0000000000
--- a/lib/resolv-replace.gemspec
+++ /dev/null
@@ -1,22 +0,0 @@
-Gem::Specification.new do |spec|
- spec.name = "resolv-replace"
- spec.version = "0.1.1"
- spec.authors = ["Tanaka Akira"]
- spec.email = ["akr@fsij.org"]
-
- spec.summary = %q{Replace Socket DNS with Resolv.}
- spec.description = %q{Replace Socket DNS with Resolv.}
- spec.homepage = "https://github.com/ruby/resolv-replace"
- spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
- spec.licenses = ["Ruby", "BSD-2-Clause"]
-
- spec.metadata["homepage_uri"] = spec.homepage
- spec.metadata["source_code_uri"] = spec.homepage
-
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
- end
- spec.require_paths = ["lib"]
-
- spec.add_dependency "resolv"
-end
diff --git a/lib/resolv-replace.rb b/lib/resolv-replace.rb
deleted file mode 100644
index a83e79d996..0000000000
--- a/lib/resolv-replace.rb
+++ /dev/null
@@ -1,76 +0,0 @@
-# frozen_string_literal: true
-
-require 'socket'
-require 'resolv'
-
-class << IPSocket
- # :stopdoc:
- alias original_resolv_getaddress getaddress
- # :startdoc:
- def getaddress(host)
- begin
- return Resolv.getaddress(host).to_s
- rescue Resolv::ResolvError
- raise SocketError, "Hostname not known: #{host}"
- end
- end
-end
-
-class TCPSocket < IPSocket
- # :stopdoc:
- alias original_resolv_initialize initialize
- # :startdoc:
- def initialize(host, serv, *rest)
- rest[0] = IPSocket.getaddress(rest[0]) if rest[0]
- original_resolv_initialize(IPSocket.getaddress(host), serv, *rest)
- end
-end
-
-class UDPSocket < IPSocket
- # :stopdoc:
- alias original_resolv_bind bind
- # :startdoc:
- def bind(host, port)
- host = IPSocket.getaddress(host) if host != ""
- original_resolv_bind(host, port)
- end
-
- # :stopdoc:
- alias original_resolv_connect connect
- # :startdoc:
- def connect(host, port)
- original_resolv_connect(IPSocket.getaddress(host), port)
- end
-
- # :stopdoc:
- alias original_resolv_send send
- # :startdoc:
- def send(mesg, flags, *rest)
- if rest.length == 2
- host, port = rest
- begin
- addrs = Resolv.getaddresses(host)
- rescue Resolv::ResolvError
- raise SocketError, "Hostname not known: #{host}"
- end
- addrs[0...-1].each {|addr|
- begin
- return original_resolv_send(mesg, flags, addr, port)
- rescue SystemCallError
- end
- }
- original_resolv_send(mesg, flags, addrs[-1], port)
- else
- original_resolv_send(mesg, flags, *rest)
- end
- end
-end
-
-class SOCKSSocket < TCPSocket
- # :stopdoc:
- alias original_resolv_initialize initialize
- # :startdoc:
- def initialize(host, serv)
- original_resolv_initialize(IPSocket.getaddress(host), port)
- end
-end if defined? SOCKSSocket
diff --git a/lib/resolv.rb b/lib/resolv.rb
index 47c4ef6b3b..e36dbce259 100644
--- a/lib/resolv.rb
+++ b/lib/resolv.rb
@@ -37,7 +37,7 @@ end
class Resolv
- VERSION = "0.2.2"
+ VERSION = "0.4.0"
##
# Looks up the first IP address for +name+.
@@ -84,8 +84,8 @@ class Resolv
##
# Creates a new Resolv using +resolvers+.
- def initialize(resolvers=[Hosts.new, DNS.new])
- @resolvers = resolvers
+ def initialize(resolvers=nil, use_ipv6: nil)
+ @resolvers = resolvers || [Hosts.new, DNS.new(DNS::Config.default_config_hash.merge(use_ipv6: use_ipv6))]
end
##
@@ -194,17 +194,10 @@ class Resolv
File.open(@filename, 'rb') {|f|
f.each {|line|
line.sub!(/#.*/, '')
- addr, hostname, *aliases = line.split(/\s+/)
+ addr, *hostnames = line.split(/\s+/)
next unless addr
- @addr2name[addr] = [] unless @addr2name.include? addr
- @addr2name[addr] << hostname
- @addr2name[addr].concat(aliases)
- @name2addr[hostname] = [] unless @name2addr.include? hostname
- @name2addr[hostname] << addr
- aliases.each {|n|
- @name2addr[n] = [] unless @name2addr.include? n
- @name2addr[n] << addr
- }
+ (@addr2name[addr] ||= []).concat(hostnames)
+ hostnames.each {|hostname| (@name2addr[hostname] ||= []) << addr}
}
}
@name2addr.each {|name, arr| arr.reverse!}
@@ -312,6 +305,8 @@ class Resolv
# String:: Path to a file using /etc/resolv.conf's format.
# Hash:: Must contain :nameserver, :search and :ndots keys.
# :nameserver_port can be used to specify port number of nameserver address.
+ # :raise_timeout_errors can be used to raise timeout errors
+ # as exceptions instead of treating the same as an NXDOMAIN response.
#
# The value of :nameserver should be an address string or
# an array of address strings.
@@ -408,6 +403,11 @@ class Resolv
end
def use_ipv6? # :nodoc:
+ use_ipv6 = @config.use_ipv6?
+ unless use_ipv6.nil?
+ return use_ipv6
+ end
+
begin
list = Socket.ip_address_list
rescue NotImplementedError
@@ -750,7 +750,7 @@ class Resolv
next if @socks_hash[bind_host]
begin
sock = UDPSocket.new(af)
- rescue Errno::EAFNOSUPPORT
+ rescue Errno::EAFNOSUPPORT, Errno::EPROTONOSUPPORT
next # The kernel doesn't support the address family.
end
@socks << sock
@@ -1006,6 +1006,7 @@ class Resolv
@mutex.synchronize {
unless @initialized
@nameserver_port = []
+ @use_ipv6 = nil
@search = nil
@ndots = 1
case @config_info
@@ -1030,8 +1031,12 @@ class Resolv
if config_hash.include? :nameserver_port
@nameserver_port = config_hash[:nameserver_port].map {|ns, port| [ns, (port || Port)] }
end
+ if config_hash.include? :use_ipv6
+ @use_ipv6 = config_hash[:use_ipv6]
+ end
@search = config_hash[:search] if config_hash.include? :search
@ndots = config_hash[:ndots] if config_hash.include? :ndots
+ @raise_timeout_errors = config_hash[:raise_timeout_errors]
if @nameserver_port.empty?
@nameserver_port << ['0.0.0.0', Port]
@@ -1085,6 +1090,10 @@ class Resolv
@nameserver_port
end
+ def use_ipv6?
+ @use_ipv6
+ end
+
def generate_candidates(name)
candidates = nil
name = Name.create(name)
@@ -1118,6 +1127,7 @@ class Resolv
def resolv(name)
candidates = generate_candidates(name)
timeouts = @timeouts || generate_timeouts
+ timeout_error = false
begin
candidates.each {|candidate|
begin
@@ -1129,11 +1139,13 @@ class Resolv
end
}
}
+ timeout_error = true
raise ResolvError.new("DNS resolv timeout: #{name}")
rescue NXDomain
end
}
rescue ResolvError
+ raise if @raise_timeout_errors && timeout_error
end
end
@@ -1520,13 +1532,15 @@ class Resolv
id, flag, qdcount, ancount, nscount, arcount =
msg.get_unpack('nnnnnn')
o.id = id
+ o.tc = (flag >> 9) & 1
+ o.rcode = flag & 15
+ return o unless o.tc.zero?
+
o.qr = (flag >> 15) & 1
o.opcode = (flag >> 11) & 15
o.aa = (flag >> 10) & 1
- o.tc = (flag >> 9) & 1
o.rd = (flag >> 8) & 1
o.ra = (flag >> 7) & 1
- o.rcode = flag & 15
(1..qdcount).each {
name, typeclass = msg.get_question
o.add_question(name, typeclass)
@@ -1618,6 +1632,14 @@ class Resolv
strings
end
+ def get_list
+ [].tap do |values|
+ while @index < @limit
+ values << yield
+ end
+ end
+ end
+
def get_name
return Name.new(self.get_labels)
end
@@ -1679,6 +1701,378 @@ class Resolv
end
##
+ # SvcParams for service binding RRs. [RFC9460]
+
+ class SvcParams
+ include Enumerable
+
+ ##
+ # Create a list of SvcParams with the given initial content.
+ #
+ # +params+ has to be an enumerable of +SvcParam+s.
+ # If its content has +SvcParam+s with the duplicate key,
+ # the one appears last takes precedence.
+
+ def initialize(params = [])
+ @params = {}
+
+ params.each do |param|
+ add param
+ end
+ end
+
+ ##
+ # Get SvcParam for the given +key+ in this list.
+
+ def [](key)
+ @params[canonical_key(key)]
+ end
+
+ ##
+ # Get the number of SvcParams in this list.
+
+ def count
+ @params.count
+ end
+
+ ##
+ # Get whether this list is empty.
+
+ def empty?
+ @params.empty?
+ end
+
+ ##
+ # Add the SvcParam +param+ to this list, overwriting the existing one with the same key.
+
+ def add(param)
+ @params[param.class.key_number] = param
+ end
+
+ ##
+ # Remove the +SvcParam+ with the given +key+ and return it.
+
+ def delete(key)
+ @params.delete(canonical_key(key))
+ end
+
+ ##
+ # Enumerate the +SvcParam+s in this list.
+
+ def each(&block)
+ return enum_for(:each) unless block
+ @params.each_value(&block)
+ end
+
+ def encode(msg) # :nodoc:
+ @params.keys.sort.each do |key|
+ msg.put_pack('n', key)
+ msg.put_length16 do
+ @params.fetch(key).encode(msg)
+ end
+ end
+ end
+
+ def self.decode(msg) # :nodoc:
+ params = msg.get_list do
+ key, = msg.get_unpack('n')
+ msg.get_length16 do
+ SvcParam::ClassHash[key].decode(msg)
+ end
+ end
+
+ return self.new(params)
+ end
+
+ private
+
+ def canonical_key(key) # :nodoc:
+ case key
+ when Integer
+ key
+ when /\Akey(\d+)\z/
+ Integer($1)
+ when Symbol
+ SvcParam::ClassHash[key].key_number
+ else
+ raise TypeError, 'key must be either String or Symbol'
+ end
+ end
+ end
+
+
+ ##
+ # Base class for SvcParam. [RFC9460]
+
+ class SvcParam
+
+ ##
+ # Get the presentation name of the SvcParamKey.
+
+ def self.key_name
+ const_get(:KeyName)
+ end
+
+ ##
+ # Get the registered number of the SvcParamKey.
+
+ def self.key_number
+ const_get(:KeyNumber)
+ end
+
+ ClassHash = Hash.new do |h, key| # :nodoc:
+ case key
+ when Integer
+ Generic.create(key)
+ when /\Akey(?<key>\d+)\z/
+ Generic.create(key.to_int)
+ when Symbol
+ raise KeyError, "unknown key #{key}"
+ else
+ raise TypeError, 'key must be either String or Symbol'
+ end
+ end
+
+ ##
+ # Generic SvcParam abstract class.
+
+ class Generic < SvcParam
+
+ ##
+ # SvcParamValue in wire-format byte string.
+
+ attr_reader :value
+
+ ##
+ # Create generic SvcParam
+
+ def initialize(value)
+ @value = value
+ end
+
+ def encode(msg) # :nodoc:
+ msg.put_bytes(@value)
+ end
+
+ def self.decode(msg) # :nodoc:
+ return self.new(msg.get_bytes)
+ end
+
+ def self.create(key_number)
+ c = Class.new(Generic)
+ key_name = :"key#{key_number}"
+ c.const_set(:KeyName, key_name)
+ c.const_set(:KeyNumber, key_number)
+ self.const_set(:"Key#{key_number}", c)
+ ClassHash[key_name] = ClassHash[key_number] = c
+ return c
+ end
+ end
+
+ ##
+ # "mandatory" SvcParam -- Mandatory keys in service binding RR
+
+ class Mandatory < SvcParam
+ KeyName = :mandatory
+ KeyNumber = 0
+ ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
+
+ ##
+ # Mandatory keys.
+
+ attr_reader :keys
+
+ ##
+ # Initialize "mandatory" ScvParam.
+
+ def initialize(keys)
+ @keys = keys.map(&:to_int)
+ end
+
+ def encode(msg) # :nodoc:
+ @keys.sort.each do |key|
+ msg.put_pack('n', key)
+ end
+ end
+
+ def self.decode(msg) # :nodoc:
+ keys = msg.get_list { msg.get_unpack('n')[0] }
+ return self.new(keys)
+ end
+ end
+
+ ##
+ # "alpn" SvcParam -- Additional supported protocols
+
+ class ALPN < SvcParam
+ KeyName = :alpn
+ KeyNumber = 1
+ ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
+
+ ##
+ # Supported protocol IDs.
+
+ attr_reader :protocol_ids
+
+ ##
+ # Initialize "alpn" ScvParam.
+
+ def initialize(protocol_ids)
+ @protocol_ids = protocol_ids.map(&:to_str)
+ end
+
+ def encode(msg) # :nodoc:
+ msg.put_string_list(@protocol_ids)
+ end
+
+ def self.decode(msg) # :nodoc:
+ return self.new(msg.get_string_list)
+ end
+ end
+
+ ##
+ # "no-default-alpn" SvcParam -- No support for default protocol
+
+ class NoDefaultALPN < SvcParam
+ KeyName = :'no-default-alpn'
+ KeyNumber = 2
+ ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
+
+ def encode(msg) # :nodoc:
+ # no payload
+ end
+
+ def self.decode(msg) # :nodoc:
+ return self.new
+ end
+ end
+
+ ##
+ # "port" SvcParam -- Port for alternative endpoint
+
+ class Port < SvcParam
+ KeyName = :port
+ KeyNumber = 3
+ ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
+
+ ##
+ # Port number.
+
+ attr_reader :port
+
+ ##
+ # Initialize "port" ScvParam.
+
+ def initialize(port)
+ @port = port.to_int
+ end
+
+ def encode(msg) # :nodoc:
+ msg.put_pack('n', @port)
+ end
+
+ def self.decode(msg) # :nodoc:
+ port, = msg.get_unpack('n')
+ return self.new(port)
+ end
+ end
+
+ ##
+ # "ipv4hint" SvcParam -- IPv4 address hints
+
+ class IPv4Hint < SvcParam
+ KeyName = :ipv4hint
+ KeyNumber = 4
+ ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
+
+ ##
+ # Set of IPv4 addresses.
+
+ attr_reader :addresses
+
+ ##
+ # Initialize "ipv4hint" ScvParam.
+
+ def initialize(addresses)
+ @addresses = addresses.map {|address| IPv4.create(address) }
+ end
+
+ def encode(msg) # :nodoc:
+ @addresses.each do |address|
+ msg.put_bytes(address.address)
+ end
+ end
+
+ def self.decode(msg) # :nodoc:
+ addresses = msg.get_list { IPv4.new(msg.get_bytes(4)) }
+ return self.new(addresses)
+ end
+ end
+
+ ##
+ # "ipv6hint" SvcParam -- IPv6 address hints
+
+ class IPv6Hint < SvcParam
+ KeyName = :ipv6hint
+ KeyNumber = 6
+ ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
+
+ ##
+ # Set of IPv6 addresses.
+
+ attr_reader :addresses
+
+ ##
+ # Initialize "ipv6hint" ScvParam.
+
+ def initialize(addresses)
+ @addresses = addresses.map {|address| IPv6.create(address) }
+ end
+
+ def encode(msg) # :nodoc:
+ @addresses.each do |address|
+ msg.put_bytes(address.address)
+ end
+ end
+
+ def self.decode(msg) # :nodoc:
+ addresses = msg.get_list { IPv6.new(msg.get_bytes(16)) }
+ return self.new(addresses)
+ end
+ end
+
+ ##
+ # "dohpath" SvcParam -- DNS over HTTPS path template [RFC9461]
+
+ class DoHPath < SvcParam
+ KeyName = :dohpath
+ KeyNumber = 7
+ ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
+
+ ##
+ # URI template for DoH queries.
+
+ attr_reader :template
+
+ ##
+ # Initialize "dohpath" ScvParam.
+
+ def initialize(template)
+ @template = template.encode('utf-8')
+ end
+
+ def encode(msg) # :nodoc:
+ msg.put_bytes(@template)
+ end
+
+ def self.decode(msg) # :nodoc:
+ template = msg.get_bytes.force_encoding('utf-8')
+ return self.new(template)
+ end
+ end
+ end
+
+ ##
# A DNS query abstract class.
class Query
@@ -2143,8 +2537,70 @@ class Resolv
TypeValue = 255 # :nodoc:
end
+ ##
+ # CAA resource record defined in RFC 8659
+ #
+ # These records identify certificate authority allowed to issue
+ # certificates for the given domain.
+
+ class CAA < Resource
+ TypeValue = 257
+
+ ##
+ # Creates a new CAA for +flags+, +tag+ and +value+.
+
+ def initialize(flags, tag, value)
+ unless (0..255) === flags
+ raise ArgumentError.new('flags must be an Integer between 0 and 255')
+ end
+ unless (1..15) === tag.bytesize
+ raise ArgumentError.new('length of tag must be between 1 and 15')
+ end
+
+ @flags = flags
+ @tag = tag
+ @value = value
+ end
+
+ ##
+ # Flags for this proprty:
+ # - Bit 0 : 0 = not critical, 1 = critical
+
+ attr_reader :flags
+
+ ##
+ # Property tag ("issue", "issuewild", "iodef"...).
+
+ attr_reader :tag
+
+ ##
+ # Property value.
+
+ attr_reader :value
+
+ ##
+ # Whether the critical flag is set on this property.
+
+ def critical?
+ flags & 0x80 != 0
+ end
+
+ def encode_rdata(msg) # :nodoc:
+ msg.put_pack('C', @flags)
+ msg.put_string(@tag)
+ msg.put_bytes(@value)
+ end
+
+ def self.decode_rdata(msg) # :nodoc:
+ flags, = msg.get_unpack('C')
+ tag = msg.get_string
+ value = msg.get_bytes
+ self.new flags, tag, value
+ end
+ end
+
ClassInsensitiveTypes = [ # :nodoc:
- NS, CNAME, SOA, PTR, HINFO, MINFO, MX, TXT, LOC, ANY
+ NS, CNAME, SOA, PTR, HINFO, MINFO, MX, TXT, LOC, ANY, CAA
]
##
@@ -2341,6 +2797,84 @@ class Resolv
return self.new(priority, weight, port, target)
end
end
+
+ ##
+ # Common implementation for SVCB-compatible resource records.
+
+ class ServiceBinding
+
+ ##
+ # Create a service binding resource record.
+
+ def initialize(priority, target, params = [])
+ @priority = priority.to_int
+ @target = Name.create(target)
+ @params = SvcParams.new(params)
+ end
+
+ ##
+ # The priority of this target host.
+ #
+ # The range is 0-65535.
+ # If set to 0, this RR is in AliasMode. Otherwise, it is in ServiceMode.
+
+ attr_reader :priority
+
+ ##
+ # The domain name of the target host.
+
+ attr_reader :target
+
+ ##
+ # The service parameters for the target host.
+
+ attr_reader :params
+
+ ##
+ # Whether this RR is in AliasMode.
+
+ def alias_mode?
+ self.priority == 0
+ end
+
+ ##
+ # Whether this RR is in ServiceMode.
+
+ def service_mode?
+ !alias_mode?
+ end
+
+ def encode_rdata(msg) # :nodoc:
+ msg.put_pack("n", @priority)
+ msg.put_name(@target, compress: false)
+ @params.encode(msg)
+ end
+
+ def self.decode_rdata(msg) # :nodoc:
+ priority, = msg.get_unpack("n")
+ target = msg.get_name
+ params = SvcParams.decode(msg)
+ return self.new(priority, target, params)
+ end
+ end
+
+ ##
+ # SVCB resource record [RFC9460]
+
+ class SVCB < ServiceBinding
+ TypeValue = 64
+ ClassValue = IN::ClassValue
+ ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
+ end
+
+ ##
+ # HTTPS resource record [RFC9460]
+
+ class HTTPS < ServiceBinding
+ TypeValue = 65
+ ClassValue = IN::ClassValue
+ ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
+ end
end
end
end
@@ -2560,11 +3094,7 @@ class Resolv
attr_reader :address
def to_s # :nodoc:
- address = sprintf("%x:%x:%x:%x:%x:%x:%x:%x", *@address.unpack("nnnnnnnn"))
- unless address.sub!(/(^|:)0(:0)+(:|$)/, '::')
- address.sub!(/(^|:)0(:|$)/, '::')
- end
- return address
+ sprintf("%x:%x:%x:%x:%x:%x:%x:%x", *@address.unpack("nnnnnnnn")).sub(/(^|:)0(:0)+(:|$)/, '::')
end
def inspect # :nodoc:
diff --git a/lib/rinda/rinda.gemspec b/lib/rinda/rinda.gemspec
deleted file mode 100644
index e9f53dd864..0000000000
--- a/lib/rinda/rinda.gemspec
+++ /dev/null
@@ -1,35 +0,0 @@
-name = File.basename(__FILE__, ".gemspec")
-version = ["lib/rinda", "."].find do |dir|
- break File.foreach(File.join(__dir__, dir, "#{name}.rb")) do |line|
- /^\s*VERSION\s*=\s*"(.*)"/ =~ line and break $1
- end rescue nil
-end
-
-Gem::Specification.new do |spec|
- spec.name = name
- spec.version = version
- spec.authors = ["Masatoshi SEKI"]
- spec.email = ["seki@ruby-lang.org"]
-
- spec.summary = %q{The Linda distributed computing paradigm in Ruby.}
- spec.description = %q{The Linda distributed computing paradigm in Ruby.}
- spec.homepage = "https://github.com/ruby/rinda"
- spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
- spec.licenses = ["Ruby", "BSD-2-Clause"]
-
- spec.metadata["homepage_uri"] = spec.homepage
- spec.metadata["source_code_uri"] = spec.homepage
-
- # Specify which files should be added to the gem when it is released.
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
- end
- spec.bindir = "exe"
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
- spec.require_paths = ["lib"]
-
- spec.add_dependency "drb"
- spec.add_dependency "ipaddr"
- spec.add_dependency "forwardable"
-end
diff --git a/lib/rinda/rinda.rb b/lib/rinda/rinda.rb
deleted file mode 100644
index ac3444244d..0000000000
--- a/lib/rinda/rinda.rb
+++ /dev/null
@@ -1,329 +0,0 @@
-# frozen_string_literal: false
-require 'drb/drb'
-
-##
-# A module to implement the Linda distributed computing paradigm in Ruby.
-#
-# Rinda is part of DRb (dRuby).
-#
-# == Example(s)
-#
-# See the sample/drb/ directory in the Ruby distribution, from 1.8.2 onwards.
-#
-#--
-# TODO
-# == Introduction to Linda/rinda?
-#
-# == Why is this library separate from DRb?
-
-module Rinda
-
- VERSION = "0.1.1"
-
- ##
- # Rinda error base class
-
- class RindaError < RuntimeError; end
-
- ##
- # Raised when a hash-based tuple has an invalid key.
-
- class InvalidHashTupleKey < RindaError; end
-
- ##
- # Raised when trying to use a canceled tuple.
-
- class RequestCanceledError < ThreadError; end
-
- ##
- # Raised when trying to use an expired tuple.
-
- class RequestExpiredError < ThreadError; end
-
- ##
- # A tuple is the elementary object in Rinda programming.
- # Tuples may be matched against templates if the tuple and
- # the template are the same size.
-
- class Tuple
-
- ##
- # Creates a new Tuple from +ary_or_hash+ which must be an Array or Hash.
-
- def initialize(ary_or_hash)
- if hash?(ary_or_hash)
- init_with_hash(ary_or_hash)
- else
- init_with_ary(ary_or_hash)
- end
- end
-
- ##
- # The number of elements in the tuple.
-
- def size
- @tuple.size
- end
-
- ##
- # Accessor method for elements of the tuple.
-
- def [](k)
- @tuple[k]
- end
-
- ##
- # Fetches item +k+ from the tuple.
-
- def fetch(k)
- @tuple.fetch(k)
- end
-
- ##
- # Iterate through the tuple, yielding the index or key, and the
- # value, thus ensuring arrays are iterated similarly to hashes.
-
- def each # FIXME
- if Hash === @tuple
- @tuple.each { |k, v| yield(k, v) }
- else
- @tuple.each_with_index { |v, k| yield(k, v) }
- end
- end
-
- ##
- # Return the tuple itself
- def value
- @tuple
- end
-
- private
-
- def hash?(ary_or_hash)
- ary_or_hash.respond_to?(:keys)
- end
-
- ##
- # Munges +ary+ into a valid Tuple.
-
- def init_with_ary(ary)
- @tuple = Array.new(ary.size)
- @tuple.size.times do |i|
- @tuple[i] = ary[i]
- end
- end
-
- ##
- # Ensures +hash+ is a valid Tuple.
-
- def init_with_hash(hash)
- @tuple = Hash.new
- hash.each do |k, v|
- raise InvalidHashTupleKey unless String === k
- @tuple[k] = v
- end
- end
-
- end
-
- ##
- # Templates are used to match tuples in Rinda.
-
- class Template < Tuple
-
- ##
- # Matches this template against +tuple+. The +tuple+ must be the same
- # size as the template. An element with a +nil+ value in a template acts
- # as a wildcard, matching any value in the corresponding position in the
- # tuple. Elements of the template match the +tuple+ if the are #== or
- # #===.
- #
- # Template.new([:foo, 5]).match Tuple.new([:foo, 5]) # => true
- # Template.new([:foo, nil]).match Tuple.new([:foo, 5]) # => true
- # Template.new([String]).match Tuple.new(['hello']) # => true
- #
- # Template.new([:foo]).match Tuple.new([:foo, 5]) # => false
- # Template.new([:foo, 6]).match Tuple.new([:foo, 5]) # => false
- # Template.new([:foo, nil]).match Tuple.new([:foo]) # => false
- # Template.new([:foo, 6]).match Tuple.new([:foo]) # => false
-
- def match(tuple)
- return false unless tuple.respond_to?(:size)
- return false unless tuple.respond_to?(:fetch)
- return false unless self.size == tuple.size
- each do |k, v|
- begin
- it = tuple.fetch(k)
- rescue
- return false
- end
- next if v.nil?
- next if v == it
- next if v === it
- return false
- end
- return true
- end
-
- ##
- # Alias for #match.
-
- def ===(tuple)
- match(tuple)
- end
-
- end
-
- ##
- # <i>Documentation?</i>
-
- class DRbObjectTemplate
-
- ##
- # Creates a new DRbObjectTemplate that will match against +uri+ and +ref+.
-
- def initialize(uri=nil, ref=nil)
- @drb_uri = uri
- @drb_ref = ref
- end
-
- ##
- # This DRbObjectTemplate matches +ro+ if the remote object's drburi and
- # drbref are the same. +nil+ is used as a wildcard.
-
- def ===(ro)
- return true if super(ro)
- unless @drb_uri.nil?
- return false unless (@drb_uri === ro.__drburi rescue false)
- end
- unless @drb_ref.nil?
- return false unless (@drb_ref === ro.__drbref rescue false)
- end
- true
- end
-
- end
-
- ##
- # TupleSpaceProxy allows a remote Tuplespace to appear as local.
-
- class TupleSpaceProxy
- ##
- # A Port ensures that a moved tuple arrives properly at its destination
- # and does not get lost.
- #
- # See https://bugs.ruby-lang.org/issues/8125
-
- class Port # :nodoc:
- attr_reader :value
-
- def self.deliver
- port = new
-
- begin
- yield(port)
- ensure
- port.close
- end
-
- port.value
- end
-
- def initialize
- @open = true
- @value = nil
- end
-
- ##
- # Don't let the DRb thread push to it when remote sends tuple
-
- def close
- @open = false
- end
-
- ##
- # Stores +value+ and ensure it does not get marshaled multiple times.
-
- def push value
- raise 'port closed' unless @open
-
- @value = value
-
- nil # avoid Marshal
- end
- end
-
- ##
- # Creates a new TupleSpaceProxy to wrap +ts+.
-
- def initialize(ts)
- @ts = ts
- end
-
- ##
- # Adds +tuple+ to the proxied TupleSpace. See TupleSpace#write.
-
- def write(tuple, sec=nil)
- @ts.write(tuple, sec)
- end
-
- ##
- # Takes +tuple+ from the proxied TupleSpace. See TupleSpace#take.
-
- def take(tuple, sec=nil, &block)
- Port.deliver do |port|
- @ts.move(DRbObject.new(port), tuple, sec, &block)
- end
- end
-
- ##
- # Reads +tuple+ from the proxied TupleSpace. See TupleSpace#read.
-
- def read(tuple, sec=nil, &block)
- @ts.read(tuple, sec, &block)
- end
-
- ##
- # Reads all tuples matching +tuple+ from the proxied TupleSpace. See
- # TupleSpace#read_all.
-
- def read_all(tuple)
- @ts.read_all(tuple)
- end
-
- ##
- # Registers for notifications of event +ev+ on the proxied TupleSpace.
- # See TupleSpace#notify
-
- def notify(ev, tuple, sec=nil)
- @ts.notify(ev, tuple, sec)
- end
-
- end
-
- ##
- # An SimpleRenewer allows a TupleSpace to check if a TupleEntry is still
- # alive.
-
- class SimpleRenewer
-
- include DRbUndumped
-
- ##
- # Creates a new SimpleRenewer that keeps an object alive for another +sec+
- # seconds.
-
- def initialize(sec=180)
- @sec = sec
- end
-
- ##
- # Called by the TupleSpace to check if the object is still alive.
-
- def renew
- @sec
- end
- end
-
-end
-
diff --git a/lib/rinda/ring.rb b/lib/rinda/ring.rb
deleted file mode 100644
index 948cfaf208..0000000000
--- a/lib/rinda/ring.rb
+++ /dev/null
@@ -1,484 +0,0 @@
-# frozen_string_literal: false
-#
-# Note: Rinda::Ring API is unstable.
-#
-require 'drb/drb'
-require_relative 'rinda'
-require 'ipaddr'
-
-module Rinda
-
- ##
- # The default port Ring discovery will use.
-
- Ring_PORT = 7647
-
- ##
- # A RingServer allows a Rinda::TupleSpace to be located via UDP broadcasts.
- # Default service location uses the following steps:
- #
- # 1. A RingServer begins listening on the network broadcast UDP address.
- # 2. A RingFinger sends a UDP packet containing the DRb URI where it will
- # listen for a reply.
- # 3. The RingServer receives the UDP packet and connects back to the
- # provided DRb URI with the DRb service.
- #
- # A RingServer requires a TupleSpace:
- #
- # ts = Rinda::TupleSpace.new
- # rs = Rinda::RingServer.new
- #
- # RingServer can also listen on multicast addresses for announcements. This
- # allows multiple RingServers to run on the same host. To use network
- # broadcast and multicast:
- #
- # ts = Rinda::TupleSpace.new
- # rs = Rinda::RingServer.new ts, %w[Socket::INADDR_ANY, 239.0.0.1 ff02::1]
-
- class RingServer
-
- include DRbUndumped
-
- ##
- # Special renewer for the RingServer to allow shutdown
-
- class Renewer # :nodoc:
- include DRbUndumped
-
- ##
- # Set to false to shutdown future requests using this Renewer
-
- attr_writer :renew
-
- def initialize # :nodoc:
- @renew = true
- end
-
- def renew # :nodoc:
- @renew ? 1 : true
- end
- end
-
- ##
- # Advertises +ts+ on the given +addresses+ at +port+.
- #
- # If +addresses+ is omitted only the UDP broadcast address is used.
- #
- # +addresses+ can contain multiple addresses. If a multicast address is
- # given in +addresses+ then the RingServer will listen for multicast
- # queries.
- #
- # If you use IPv4 multicast you may need to set an address of the inbound
- # interface which joins a multicast group.
- #
- # ts = Rinda::TupleSpace.new
- # rs = Rinda::RingServer.new(ts, [['239.0.0.1', '9.5.1.1']])
- #
- # You can set addresses as an Array Object. The first element of the
- # Array is a multicast address and the second is an inbound interface
- # address. If the second is omitted then '0.0.0.0' is used.
- #
- # If you use IPv6 multicast you may need to set both the local interface
- # address and the inbound interface index:
- #
- # rs = Rinda::RingServer.new(ts, [['ff02::1', '::1', 1]])
- #
- # The first element is a multicast address and the second is an inbound
- # interface address. The third is an inbound interface index.
- #
- # At this time there is no easy way to get an interface index by name.
- #
- # If the second is omitted then '::1' is used.
- # If the third is omitted then 0 (default interface) is used.
-
- def initialize(ts, addresses=[Socket::INADDR_ANY], port=Ring_PORT)
- @port = port
-
- if Integer === addresses then
- addresses, @port = [Socket::INADDR_ANY], addresses
- end
-
- @renewer = Renewer.new
-
- @ts = ts
- @sockets = []
- addresses.each do |address|
- if Array === address
- make_socket(*address)
- else
- make_socket(address)
- end
- end
-
- @w_services = write_services
- @r_service = reply_service
- end
-
- ##
- # Creates a socket at +address+
- #
- # If +address+ is multicast address then +interface_address+ and
- # +multicast_interface+ can be set as optional.
- #
- # A created socket is bound to +interface_address+. If you use IPv4
- # multicast then the interface of +interface_address+ is used as the
- # inbound interface. If +interface_address+ is omitted or nil then
- # '0.0.0.0' or '::1' is used.
- #
- # If you use IPv6 multicast then +multicast_interface+ is used as the
- # inbound interface. +multicast_interface+ is a network interface index.
- # If +multicast_interface+ is omitted then 0 (default interface) is used.
-
- def make_socket(address, interface_address=nil, multicast_interface=0)
- addrinfo = Addrinfo.udp(address, @port)
-
- socket = Socket.new(addrinfo.pfamily, addrinfo.socktype,
- addrinfo.protocol)
-
- if addrinfo.ipv4_multicast? or addrinfo.ipv6_multicast? then
- if Socket.const_defined?(:SO_REUSEPORT) then
- socket.setsockopt(:SOCKET, :SO_REUSEPORT, true)
- else
- socket.setsockopt(:SOCKET, :SO_REUSEADDR, true)
- end
-
- if addrinfo.ipv4_multicast? then
- interface_address = '0.0.0.0' if interface_address.nil?
- socket.bind(Addrinfo.udp(interface_address, @port))
-
- mreq = IPAddr.new(addrinfo.ip_address).hton +
- IPAddr.new(interface_address).hton
-
- socket.setsockopt(:IPPROTO_IP, :IP_ADD_MEMBERSHIP, mreq)
- else
- interface_address = '::1' if interface_address.nil?
- socket.bind(Addrinfo.udp(interface_address, @port))
-
- mreq = IPAddr.new(addrinfo.ip_address).hton +
- [multicast_interface].pack('I')
-
- socket.setsockopt(:IPPROTO_IPV6, :IPV6_JOIN_GROUP, mreq)
- end
- else
- socket.bind(addrinfo)
- end
-
- socket
- rescue
- socket = socket.close if socket
- raise
- ensure
- @sockets << socket if socket
- end
-
- ##
- # Creates threads that pick up UDP packets and passes them to do_write for
- # decoding.
-
- def write_services
- @sockets.map do |s|
- Thread.new(s) do |socket|
- loop do
- msg = socket.recv(1024)
- do_write(msg)
- end
- end
- end
- end
-
- ##
- # Extracts the response URI from +msg+ and adds it to TupleSpace where it
- # will be picked up by +reply_service+ for notification.
-
- def do_write(msg)
- Thread.new do
- begin
- tuple, sec = Marshal.load(msg)
- @ts.write(tuple, sec)
- rescue
- end
- end
- end
-
- ##
- # Creates a thread that notifies waiting clients from the TupleSpace.
-
- def reply_service
- Thread.new do
- loop do
- do_reply
- end
- end
- end
-
- ##
- # Pulls lookup tuples out of the TupleSpace and sends their DRb object the
- # address of the local TupleSpace.
-
- def do_reply
- tuple = @ts.take([:lookup_ring, nil], @renewer)
- Thread.new { tuple[1].call(@ts) rescue nil}
- rescue
- end
-
- ##
- # Shuts down the RingServer
-
- def shutdown
- @renewer.renew = false
-
- @w_services.each do |thread|
- thread.kill
- thread.join
- end
-
- @sockets.each do |socket|
- socket.close
- end
-
- @r_service.kill
- @r_service.join
- end
-
- end
-
- ##
- # RingFinger is used by RingServer clients to discover the RingServer's
- # TupleSpace. Typically, all a client needs to do is call
- # RingFinger.primary to retrieve the remote TupleSpace, which it can then
- # begin using.
- #
- # To find the first available remote TupleSpace:
- #
- # Rinda::RingFinger.primary
- #
- # To create a RingFinger that broadcasts to a custom list:
- #
- # rf = Rinda::RingFinger.new ['localhost', '192.0.2.1']
- # rf.primary
- #
- # Rinda::RingFinger also understands multicast addresses and sets them up
- # properly. This allows you to run multiple RingServers on the same host:
- #
- # rf = Rinda::RingFinger.new ['239.0.0.1']
- # rf.primary
- #
- # You can set the hop count (or TTL) for multicast searches using
- # #multicast_hops.
- #
- # If you use IPv6 multicast you may need to set both an address and the
- # outbound interface index:
- #
- # rf = Rinda::RingFinger.new ['ff02::1']
- # rf.multicast_interface = 1
- # rf.primary
- #
- # At this time there is no easy way to get an interface index by name.
-
- class RingFinger
-
- @@broadcast_list = ['<broadcast>', 'localhost']
-
- @@finger = nil
-
- ##
- # Creates a singleton RingFinger and looks for a RingServer. Returns the
- # created RingFinger.
-
- def self.finger
- unless @@finger
- @@finger = self.new
- @@finger.lookup_ring_any
- end
- @@finger
- end
-
- ##
- # Returns the first advertised TupleSpace.
-
- def self.primary
- finger.primary
- end
-
- ##
- # Contains all discovered TupleSpaces except for the primary.
-
- def self.to_a
- finger.to_a
- end
-
- ##
- # The list of addresses where RingFinger will send query packets.
-
- attr_accessor :broadcast_list
-
- ##
- # Maximum number of hops for sent multicast packets (if using a multicast
- # address in the broadcast list). The default is 1 (same as UDP
- # broadcast).
-
- attr_accessor :multicast_hops
-
- ##
- # The interface index to send IPv6 multicast packets from.
-
- attr_accessor :multicast_interface
-
- ##
- # The port that RingFinger will send query packets to.
-
- attr_accessor :port
-
- ##
- # Contain the first advertised TupleSpace after lookup_ring_any is called.
-
- attr_accessor :primary
-
- ##
- # Creates a new RingFinger that will look for RingServers at +port+ on
- # the addresses in +broadcast_list+.
- #
- # If +broadcast_list+ contains a multicast address then multicast queries
- # will be made using the given multicast_hops and multicast_interface.
-
- def initialize(broadcast_list=@@broadcast_list, port=Ring_PORT)
- @broadcast_list = broadcast_list || ['localhost']
- @port = port
- @primary = nil
- @rings = []
-
- @multicast_hops = 1
- @multicast_interface = 0
- end
-
- ##
- # Contains all discovered TupleSpaces except for the primary.
-
- def to_a
- @rings
- end
-
- ##
- # Iterates over all discovered TupleSpaces starting with the primary.
-
- def each
- lookup_ring_any unless @primary
- return unless @primary
- yield(@primary)
- @rings.each { |x| yield(x) }
- end
-
- ##
- # Looks up RingServers waiting +timeout+ seconds. RingServers will be
- # given +block+ as a callback, which will be called with the remote
- # TupleSpace.
-
- def lookup_ring(timeout=5, &block)
- return lookup_ring_any(timeout) unless block_given?
-
- msg = Marshal.dump([[:lookup_ring, DRbObject.new(block)], timeout])
- @broadcast_list.each do |it|
- send_message(it, msg)
- end
- sleep(timeout)
- end
-
- ##
- # Returns the first found remote TupleSpace. Any further recovered
- # TupleSpaces can be found by calling +to_a+.
-
- def lookup_ring_any(timeout=5)
- queue = Thread::Queue.new
-
- Thread.new do
- self.lookup_ring(timeout) do |ts|
- queue.push(ts)
- end
- queue.push(nil)
- end
-
- @primary = queue.pop
- raise('RingNotFound') if @primary.nil?
-
- Thread.new do
- while it = queue.pop
- @rings.push(it)
- end
- end
-
- @primary
- end
-
- ##
- # Creates a socket for +address+ with the appropriate multicast options
- # for multicast addresses.
-
- def make_socket(address) # :nodoc:
- addrinfo = Addrinfo.udp(address, @port)
-
- soc = Socket.new(addrinfo.pfamily, addrinfo.socktype, addrinfo.protocol)
- begin
- if addrinfo.ipv4_multicast? then
- soc.setsockopt(Socket::Option.ipv4_multicast_loop(1))
- soc.setsockopt(Socket::Option.ipv4_multicast_ttl(@multicast_hops))
- elsif addrinfo.ipv6_multicast? then
- soc.setsockopt(:IPPROTO_IPV6, :IPV6_MULTICAST_LOOP, true)
- soc.setsockopt(:IPPROTO_IPV6, :IPV6_MULTICAST_HOPS,
- [@multicast_hops].pack('I'))
- soc.setsockopt(:IPPROTO_IPV6, :IPV6_MULTICAST_IF,
- [@multicast_interface].pack('I'))
- else
- soc.setsockopt(:SOL_SOCKET, :SO_BROADCAST, true)
- end
-
- soc.connect(addrinfo)
- rescue Exception
- soc.close
- raise
- end
-
- soc
- end
-
- def send_message(address, message) # :nodoc:
- soc = make_socket(address)
-
- soc.send(message, 0)
- rescue
- nil
- ensure
- soc.close if soc
- end
-
- end
-
- ##
- # RingProvider uses a RingServer advertised TupleSpace as a name service.
- # TupleSpace clients can register themselves with the remote TupleSpace and
- # look up other provided services via the remote TupleSpace.
- #
- # Services are registered with a tuple of the format [:name, klass,
- # DRbObject, description].
-
- class RingProvider
-
- ##
- # Creates a RingProvider that will provide a +klass+ service running on
- # +front+, with a +description+. +renewer+ is optional.
-
- def initialize(klass, front, desc, renewer = nil)
- @tuple = [:name, klass, front, desc]
- @renewer = renewer || Rinda::SimpleRenewer.new
- end
-
- ##
- # Advertises this service on the primary remote TupleSpace.
-
- def provide
- ts = Rinda::RingFinger.primary
- ts.write(@tuple, @renewer)
- end
-
- end
-
-end
diff --git a/lib/rinda/tuplespace.rb b/lib/rinda/tuplespace.rb
deleted file mode 100644
index 6a41a7ba75..0000000000
--- a/lib/rinda/tuplespace.rb
+++ /dev/null
@@ -1,641 +0,0 @@
-# frozen_string_literal: false
-require 'monitor'
-require 'drb/drb'
-require_relative 'rinda'
-require 'forwardable'
-
-module Rinda
-
- ##
- # A TupleEntry is a Tuple (i.e. a possible entry in some Tuplespace)
- # together with expiry and cancellation data.
-
- class TupleEntry
-
- include DRbUndumped
-
- attr_accessor :expires
-
- ##
- # Creates a TupleEntry based on +ary+ with an optional renewer or expiry
- # time +sec+.
- #
- # A renewer must implement the +renew+ method which returns a Numeric,
- # nil, or true to indicate when the tuple has expired.
-
- def initialize(ary, sec=nil)
- @cancel = false
- @expires = nil
- @tuple = make_tuple(ary)
- @renewer = nil
- renew(sec)
- end
-
- ##
- # Marks this TupleEntry as canceled.
-
- def cancel
- @cancel = true
- end
-
- ##
- # A TupleEntry is dead when it is canceled or expired.
-
- def alive?
- !canceled? && !expired?
- end
-
- ##
- # Return the object which makes up the tuple itself: the Array
- # or Hash.
-
- def value; @tuple.value; end
-
- ##
- # Returns the canceled status.
-
- def canceled?; @cancel; end
-
- ##
- # Has this tuple expired? (true/false).
- #
- # A tuple has expired when its expiry timer based on the +sec+ argument to
- # #initialize runs out.
-
- def expired?
- return true unless @expires
- return false if @expires > Time.now
- return true if @renewer.nil?
- renew(@renewer)
- return true unless @expires
- return @expires < Time.now
- end
-
- ##
- # Reset the expiry time according to +sec_or_renewer+.
- #
- # +nil+:: it is set to expire in the far future.
- # +true+:: it has expired.
- # Numeric:: it will expire in that many seconds.
- #
- # Otherwise the argument refers to some kind of renewer object
- # which will reset its expiry time.
-
- def renew(sec_or_renewer)
- sec, @renewer = get_renewer(sec_or_renewer)
- @expires = make_expires(sec)
- end
-
- ##
- # Returns an expiry Time based on +sec+ which can be one of:
- # Numeric:: +sec+ seconds into the future
- # +true+:: the expiry time is the start of 1970 (i.e. expired)
- # +nil+:: it is Tue Jan 19 03:14:07 GMT Standard Time 2038 (i.e. when
- # UNIX clocks will die)
-
- def make_expires(sec=nil)
- case sec
- when Numeric
- Time.now + sec
- when true
- Time.at(1)
- when nil
- Time.at(2**31-1)
- end
- end
-
- ##
- # Retrieves +key+ from the tuple.
-
- def [](key)
- @tuple[key]
- end
-
- ##
- # Fetches +key+ from the tuple.
-
- def fetch(key)
- @tuple.fetch(key)
- end
-
- ##
- # The size of the tuple.
-
- def size
- @tuple.size
- end
-
- ##
- # Creates a Rinda::Tuple for +ary+.
-
- def make_tuple(ary)
- Rinda::Tuple.new(ary)
- end
-
- private
-
- ##
- # Returns a valid argument to make_expires and the renewer or nil.
- #
- # Given +true+, +nil+, or Numeric, returns that value and +nil+ (no actual
- # renewer). Otherwise it returns an expiry value from calling +it.renew+
- # and the renewer.
-
- def get_renewer(it)
- case it
- when Numeric, true, nil
- return it, nil
- else
- begin
- return it.renew, it
- rescue Exception
- return it, nil
- end
- end
- end
-
- end
-
- ##
- # A TemplateEntry is a Template together with expiry and cancellation data.
-
- class TemplateEntry < TupleEntry
- ##
- # Matches this TemplateEntry against +tuple+. See Template#match for
- # details on how a Template matches a Tuple.
-
- def match(tuple)
- @tuple.match(tuple)
- end
-
- alias === match
-
- def make_tuple(ary) # :nodoc:
- Rinda::Template.new(ary)
- end
-
- end
-
- ##
- # <i>Documentation?</i>
-
- class WaitTemplateEntry < TemplateEntry
-
- attr_reader :found
-
- def initialize(place, ary, expires=nil)
- super(ary, expires)
- @place = place
- @cond = place.new_cond
- @found = nil
- end
-
- def cancel
- super
- signal
- end
-
- def wait
- @cond.wait
- end
-
- def read(tuple)
- @found = tuple
- signal
- end
-
- def signal
- @place.synchronize do
- @cond.signal
- end
- end
-
- end
-
- ##
- # A NotifyTemplateEntry is returned by TupleSpace#notify and is notified of
- # TupleSpace changes. You may receive either your subscribed event or the
- # 'close' event when iterating over notifications.
- #
- # See TupleSpace#notify_event for valid notification types.
- #
- # == Example
- #
- # ts = Rinda::TupleSpace.new
- # observer = ts.notify 'write', [nil]
- #
- # Thread.start do
- # observer.each { |t| p t }
- # end
- #
- # 3.times { |i| ts.write [i] }
- #
- # Outputs:
- #
- # ['write', [0]]
- # ['write', [1]]
- # ['write', [2]]
-
- class NotifyTemplateEntry < TemplateEntry
-
- ##
- # Creates a new NotifyTemplateEntry that watches +place+ for +event+s that
- # match +tuple+.
-
- def initialize(place, event, tuple, expires=nil)
- ary = [event, Rinda::Template.new(tuple)]
- super(ary, expires)
- @queue = Thread::Queue.new
- @done = false
- end
-
- ##
- # Called by TupleSpace to notify this NotifyTemplateEntry of a new event.
-
- def notify(ev)
- @queue.push(ev)
- end
-
- ##
- # Retrieves a notification. Raises RequestExpiredError when this
- # NotifyTemplateEntry expires.
-
- def pop
- raise RequestExpiredError if @done
- it = @queue.pop
- @done = true if it[0] == 'close'
- return it
- end
-
- ##
- # Yields event/tuple pairs until this NotifyTemplateEntry expires.
-
- def each # :yields: event, tuple
- while !@done
- it = pop
- yield(it)
- end
- rescue
- ensure
- cancel
- end
-
- end
-
- ##
- # TupleBag is an unordered collection of tuples. It is the basis
- # of Tuplespace.
-
- class TupleBag
- class TupleBin
- extend Forwardable
- def_delegators '@bin', :find_all, :delete_if, :each, :empty?
-
- def initialize
- @bin = []
- end
-
- def add(tuple)
- @bin.push(tuple)
- end
-
- def delete(tuple)
- idx = @bin.rindex(tuple)
- @bin.delete_at(idx) if idx
- end
-
- def find
- @bin.reverse_each do |x|
- return x if yield(x)
- end
- nil
- end
- end
-
- def initialize # :nodoc:
- @hash = {}
- @enum = enum_for(:each_entry)
- end
-
- ##
- # +true+ if the TupleBag to see if it has any expired entries.
-
- def has_expires?
- @enum.find do |tuple|
- tuple.expires
- end
- end
-
- ##
- # Add +tuple+ to the TupleBag.
-
- def push(tuple)
- key = bin_key(tuple)
- @hash[key] ||= TupleBin.new
- @hash[key].add(tuple)
- end
-
- ##
- # Removes +tuple+ from the TupleBag.
-
- def delete(tuple)
- key = bin_key(tuple)
- bin = @hash[key]
- return nil unless bin
- bin.delete(tuple)
- @hash.delete(key) if bin.empty?
- tuple
- end
-
- ##
- # Finds all live tuples that match +template+.
- def find_all(template)
- bin_for_find(template).find_all do |tuple|
- tuple.alive? && template.match(tuple)
- end
- end
-
- ##
- # Finds a live tuple that matches +template+.
-
- def find(template)
- bin_for_find(template).find do |tuple|
- tuple.alive? && template.match(tuple)
- end
- end
-
- ##
- # Finds all tuples in the TupleBag which when treated as templates, match
- # +tuple+ and are alive.
-
- def find_all_template(tuple)
- @enum.find_all do |template|
- template.alive? && template.match(tuple)
- end
- end
-
- ##
- # Delete tuples which dead tuples from the TupleBag, returning the deleted
- # tuples.
-
- def delete_unless_alive
- deleted = []
- @hash.each do |key, bin|
- bin.delete_if do |tuple|
- if tuple.alive?
- false
- else
- deleted.push(tuple)
- true
- end
- end
- end
- deleted
- end
-
- private
- def each_entry(&blk)
- @hash.each do |k, v|
- v.each(&blk)
- end
- end
-
- def bin_key(tuple)
- head = tuple[0]
- if head.class == Symbol
- return head
- else
- false
- end
- end
-
- def bin_for_find(template)
- key = bin_key(template)
- key ? @hash.fetch(key, []) : @enum
- end
- end
-
- ##
- # The Tuplespace manages access to the tuples it contains,
- # ensuring mutual exclusion requirements are met.
- #
- # The +sec+ option for the write, take, move, read and notify methods may
- # either be a number of seconds or a Renewer object.
-
- class TupleSpace
-
- include DRbUndumped
- include MonitorMixin
-
- ##
- # Creates a new TupleSpace. +period+ is used to control how often to look
- # for dead tuples after modifications to the TupleSpace.
- #
- # If no dead tuples are found +period+ seconds after the last
- # modification, the TupleSpace will stop looking for dead tuples.
-
- def initialize(period=60)
- super()
- @bag = TupleBag.new
- @read_waiter = TupleBag.new
- @take_waiter = TupleBag.new
- @notify_waiter = TupleBag.new
- @period = period
- @keeper = nil
- end
-
- ##
- # Adds +tuple+
-
- def write(tuple, sec=nil)
- entry = create_entry(tuple, sec)
- synchronize do
- if entry.expired?
- @read_waiter.find_all_template(entry).each do |template|
- template.read(tuple)
- end
- notify_event('write', entry.value)
- notify_event('delete', entry.value)
- else
- @bag.push(entry)
- start_keeper if entry.expires
- @read_waiter.find_all_template(entry).each do |template|
- template.read(tuple)
- end
- @take_waiter.find_all_template(entry).each do |template|
- template.signal
- end
- notify_event('write', entry.value)
- end
- end
- entry
- end
-
- ##
- # Removes +tuple+
-
- def take(tuple, sec=nil, &block)
- move(nil, tuple, sec, &block)
- end
-
- ##
- # Moves +tuple+ to +port+.
-
- def move(port, tuple, sec=nil)
- template = WaitTemplateEntry.new(self, tuple, sec)
- yield(template) if block_given?
- synchronize do
- entry = @bag.find(template)
- if entry
- port.push(entry.value) if port
- @bag.delete(entry)
- notify_event('take', entry.value)
- return port ? nil : entry.value
- end
- raise RequestExpiredError if template.expired?
-
- begin
- @take_waiter.push(template)
- start_keeper if template.expires
- while true
- raise RequestCanceledError if template.canceled?
- raise RequestExpiredError if template.expired?
- entry = @bag.find(template)
- if entry
- port.push(entry.value) if port
- @bag.delete(entry)
- notify_event('take', entry.value)
- return port ? nil : entry.value
- end
- template.wait
- end
- ensure
- @take_waiter.delete(template)
- end
- end
- end
-
- ##
- # Reads +tuple+, but does not remove it.
-
- def read(tuple, sec=nil)
- template = WaitTemplateEntry.new(self, tuple, sec)
- yield(template) if block_given?
- synchronize do
- entry = @bag.find(template)
- return entry.value if entry
- raise RequestExpiredError if template.expired?
-
- begin
- @read_waiter.push(template)
- start_keeper if template.expires
- template.wait
- raise RequestCanceledError if template.canceled?
- raise RequestExpiredError if template.expired?
- return template.found
- ensure
- @read_waiter.delete(template)
- end
- end
- end
-
- ##
- # Returns all tuples matching +tuple+. Does not remove the found tuples.
-
- def read_all(tuple)
- template = WaitTemplateEntry.new(self, tuple, nil)
- synchronize do
- entry = @bag.find_all(template)
- entry.collect do |e|
- e.value
- end
- end
- end
-
- ##
- # Registers for notifications of +event+. Returns a NotifyTemplateEntry.
- # See NotifyTemplateEntry for examples of how to listen for notifications.
- #
- # +event+ can be:
- # 'write':: A tuple was added
- # 'take':: A tuple was taken or moved
- # 'delete':: A tuple was lost after being overwritten or expiring
- #
- # The TupleSpace will also notify you of the 'close' event when the
- # NotifyTemplateEntry has expired.
-
- def notify(event, tuple, sec=nil)
- template = NotifyTemplateEntry.new(self, event, tuple, sec)
- synchronize do
- @notify_waiter.push(template)
- end
- template
- end
-
- private
-
- def create_entry(tuple, sec)
- TupleEntry.new(tuple, sec)
- end
-
- ##
- # Removes dead tuples.
-
- def keep_clean
- synchronize do
- @read_waiter.delete_unless_alive.each do |e|
- e.signal
- end
- @take_waiter.delete_unless_alive.each do |e|
- e.signal
- end
- @notify_waiter.delete_unless_alive.each do |e|
- e.notify(['close'])
- end
- @bag.delete_unless_alive.each do |e|
- notify_event('delete', e.value)
- end
- end
- end
-
- ##
- # Notifies all registered listeners for +event+ of a status change of
- # +tuple+.
-
- def notify_event(event, tuple)
- ev = [event, tuple]
- @notify_waiter.find_all_template(ev).each do |template|
- template.notify(ev)
- end
- end
-
- ##
- # Creates a thread that scans the tuplespace for expired tuples.
-
- def start_keeper
- return if @keeper && @keeper.alive?
- @keeper = Thread.new do
- while true
- sleep(@period)
- synchronize do
- break unless need_keeper?
- keep_clean
- end
- end
- end
- end
-
- ##
- # Checks the tuplespace to see if it needs cleaning.
-
- def need_keeper?
- return true if @bag.has_expires?
- return true if @read_waiter.has_expires?
- return true if @take_waiter.has_expires?
- return true if @notify_waiter.has_expires?
- end
-
- end
-
-end
-
diff --git a/lib/ruby_vm/rjit/.document b/lib/ruby_vm/rjit/.document
new file mode 100644
index 0000000000..0a603afe3d
--- /dev/null
+++ b/lib/ruby_vm/rjit/.document
@@ -0,0 +1 @@
+stats.rb
diff --git a/lib/ruby_vm/rjit/assembler.rb b/lib/ruby_vm/rjit/assembler.rb
index 64f663ec0e..645072d11b 100644
--- a/lib/ruby_vm/rjit/assembler.rb
+++ b/lib/ruby_vm/rjit/assembler.rb
@@ -328,6 +328,17 @@ module RubyVM::RJIT
disp: left_disp,
imm: imm8(right_imm),
)
+ # CMP r/m64, imm32 (Mod 01: [reg]+disp8)
+ in [QwordPtr[R64 => left_reg, IMM8 => left_disp], IMM32 => right_imm]
+ # REX.W + 81 /7 id
+ # MI: Operand 1: ModRM:r/m (r), Operand 2: imm8/16/32
+ insn(
+ prefix: REX_W,
+ opcode: 0x81,
+ mod_rm: ModRM[mod: Mod01, reg: 7, rm: left_reg],
+ disp: left_disp,
+ imm: imm32(right_imm),
+ )
# CMP r/m64, imm8 (Mod 10: [reg]+disp32)
in [QwordPtr[R64 => left_reg, IMM32 => left_disp], IMM8 => right_imm]
# REX.W + 83 /7 ib
diff --git a/lib/ruby_vm/rjit/c_pointer.rb b/lib/ruby_vm/rjit/c_pointer.rb
index d65d5a93a5..db00c4cd11 100644
--- a/lib/ruby_vm/rjit/c_pointer.rb
+++ b/lib/ruby_vm/rjit/c_pointer.rb
@@ -238,6 +238,40 @@ module RubyVM::RJIT
end
end
+ # Basically Immediate but without #* to skip auto-dereference of structs.
+ class Array
+ attr_reader :type
+
+ # @param addr [Integer]
+ # @param type [Class] RubyVM::RJIT::CType::*
+ def initialize(addr, type)
+ @addr = addr
+ @type = type
+ end
+
+ # Array access
+ def [](index)
+ @type.new(@addr)[index]
+ end
+
+ # Array set
+ # @param index [Integer]
+ # @param value [Integer, RubyVM::RJIT::CPointer::Struct] an address itself or an object that return an address with to_i
+ def []=(index, value)
+ @type.new(@addr)[index] = value
+ end
+
+ private
+
+ def self.define(block)
+ Class.new(self) do
+ define_method(:initialize) do |addr|
+ super(addr, block.call)
+ end
+ end
+ end
+ end
+
class Pointer
attr_reader :type
@@ -338,7 +372,7 @@ module RubyVM::RJIT
# Give a name to a dynamic CPointer class to see it on inspect
def self.with_class_name(prefix, name, cache: false, &block)
- return block.call if name.empty?
+ return block.call if !name.nil? && name.empty?
# Use a cached result only if cache: true
class_name = "#{prefix}_#{name}"
diff --git a/lib/ruby_vm/rjit/c_type.rb b/lib/ruby_vm/rjit/c_type.rb
index bec7e21c38..3b313a658b 100644
--- a/lib/ruby_vm/rjit/c_type.rb
+++ b/lib/ruby_vm/rjit/c_type.rb
@@ -62,6 +62,14 @@ module RubyVM::RJIT
end
end
+ class Array
+ def self.new(&block)
+ CPointer.with_class_name('Array', block.object_id.to_s) do
+ CPointer::Array.define(block)
+ end
+ end
+ end
+
class Pointer
# This takes a block to avoid "stack level too deep" on a cyclic reference
# @param block [Proc]
diff --git a/lib/ruby_vm/rjit/code_block.rb b/lib/ruby_vm/rjit/code_block.rb
index 6260ec8b4b..260bd98671 100644
--- a/lib/ruby_vm/rjit/code_block.rb
+++ b/lib/ruby_vm/rjit/code_block.rb
@@ -4,7 +4,7 @@ module RubyVM::RJIT
# @param mem_size [Integer] JIT buffer size
# @param outliend [TrueClass,FalseClass] true for outlined CodeBlock
def initialize(mem_block:, mem_size:, outlined: false)
- @comments = Hash.new { |h, k| h[k] = [] }
+ @comments = Hash.new { |h, k| h[k] = [] } if dump_disasm?
@mem_block = mem_block
@mem_size = mem_size
@write_pos = 0
@@ -26,7 +26,7 @@ module RubyVM::RJIT
# Convert comment indexes to addresses
asm.comments.each do |index, comments|
- @comments[start_addr + index] += comments
+ @comments[start_addr + index] += comments if dump_disasm?
end
asm.comments.clear
@@ -39,7 +39,7 @@ module RubyVM::RJIT
def set_write_addr(addr)
@write_pos = addr - @mem_block
- @comments.delete(addr) # TODO: clean up old comments for all the overwritten range?
+ @comments.delete(addr) if dump_disasm?
end
def with_write_addr(addr)
@@ -83,5 +83,9 @@ module RubyVM::RJIT
def bold(text)
"\e[1m#{text}\e[0m"
end
+
+ def dump_disasm?
+ C.rjit_opts.dump_disasm
+ end
end
end
diff --git a/lib/ruby_vm/rjit/compiler.rb b/lib/ruby_vm/rjit/compiler.rb
index 1c024c4c40..e5c3adf0ec 100644
--- a/lib/ruby_vm/rjit/compiler.rb
+++ b/lib/ruby_vm/rjit/compiler.rb
@@ -59,14 +59,16 @@ module RubyVM::RJIT
# @param iseq `RubyVM::RJIT::CPointer::Struct_rb_iseq_t`
# @param cfp `RubyVM::RJIT::CPointer::Struct_rb_control_frame_t`
def compile(iseq, cfp)
+ return unless supported_platform?
pc = cfp.pc.to_i
jit = JITState.new(iseq:, cfp:)
asm = Assembler.new
compile_prologue(asm, iseq, pc)
compile_block(asm, jit:, pc:)
- iseq.body.jit_func = @cb.write(asm)
+ iseq.body.jit_entry = @cb.write(asm)
rescue Exception => e
- $stderr.puts e.full_message
+ STDERR.puts "#{e.class}: #{e.message}"
+ STDERR.puts e.backtrace
exit 1
end
@@ -108,7 +110,7 @@ module RubyVM::RJIT
return block.start_addr
rescue Exception => e
- $stderr.puts e.full_message
+ STDERR.puts e.full_message
exit 1
end
@@ -163,7 +165,7 @@ module RubyVM::RJIT
return target.address
rescue Exception => e
- $stderr.puts e.full_message
+ STDERR.puts e.full_message
exit 1
end
@@ -176,8 +178,8 @@ module RubyVM::RJIT
# If they were the ISEQ's first blocks, re-compile RJIT entry as well
if iseq.body.iseq_encoded.to_i == pc
- iseq.body.jit_func = 0
- iseq.body.total_calls = 0
+ iseq.body.jit_entry = 0
+ iseq.body.jit_entry_calls = 0
end
end
@@ -505,5 +507,12 @@ module RubyVM::RJIT
raise "'#{cond.inspect}' was not true"
end
end
+
+ def supported_platform?
+ return @supported_platform if defined?(@supported_platform)
+ @supported_platform = RUBY_PLATFORM.match?(/x86_64/).tap do |supported|
+ warn "warning: RJIT does not support #{RUBY_PLATFORM} yet" unless supported
+ end
+ end
end
end
diff --git a/lib/ruby_vm/rjit/context.rb b/lib/ruby_vm/rjit/context.rb
index 66da478c7f..a2a7ecc6dc 100644
--- a/lib/ruby_vm/rjit/context.rb
+++ b/lib/ruby_vm/rjit/context.rb
@@ -264,9 +264,9 @@ module RubyVM::RJIT
# noop
in MapToSelf
# noop
- in MapToLocal[local_idx]
- if stack_idx == local_idx
- self.temp_types[stack_idx] = self.local_types[local_idx];
+ in MapToLocal[idx]
+ if idx == local_idx
+ self.temp_types[stack_idx] = self.local_types[idx]
self.temp_mapping[stack_idx] = MapToStack
else
# noop
diff --git a/lib/ruby_vm/rjit/insn_compiler.rb b/lib/ruby_vm/rjit/insn_compiler.rb
index 619f5078dc..2346c92bd1 100644
--- a/lib/ruby_vm/rjit/insn_compiler.rb
+++ b/lib/ruby_vm/rjit/insn_compiler.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: true
module RubyVM::RJIT
class InsnCompiler
# struct rb_calling_info. Storing flags instead of ci.
@@ -56,6 +57,7 @@ module RubyVM::RJIT
when :putobject then putobject(jit, ctx, asm)
when :putspecialobject then putspecialobject(jit, ctx, asm)
when :putstring then putstring(jit, ctx, asm)
+ when :putchilledstring then putchilledstring(jit, ctx, asm)
when :concatstrings then concatstrings(jit, ctx, asm)
when :anytostring then anytostring(jit, ctx, asm)
when :toregexp then toregexp(jit, ctx, asm)
@@ -502,30 +504,7 @@ module RubyVM::RJIT
shape = C.rb_shape_get_shape_by_id(shape_id)
current_capacity = shape.capacity
- new_capacity = current_capacity * 2
-
- # If the object doesn't have the capacity to store the IV,
- # then we'll need to allocate it.
- needs_extension = shape.next_iv_index >= current_capacity
-
- # We can write to the object, but we need to transition the shape
- ivar_index = shape.next_iv_index
-
- capa_shape =
- if needs_extension
- # We need to add an extended table to the object
- # First, create an outgoing transition that increases the capacity
- C.rb_shape_transition_shape_capa(shape, new_capacity)
- else
- nil
- end
-
- dest_shape =
- if capa_shape
- C.rb_shape_get_next(capa_shape, comptime_receiver, ivar_name)
- else
- C.rb_shape_get_next(shape, comptime_receiver, ivar_name)
- end
+ dest_shape = C.rb_shape_get_next(shape, comptime_receiver, ivar_name)
new_shape_id = C.rb_shape_id(dest_shape)
if new_shape_id == C::OBJ_TOO_COMPLEX_SHAPE_ID
@@ -533,12 +512,18 @@ module RubyVM::RJIT
return CantCompile
end
+ ivar_index = shape.next_iv_index
+
+ # If the new shape has a different capacity, we need to
+ # reallocate the object.
+ needs_extension = dest_shape.capacity != shape.capacity
+
if needs_extension
# Generate the C call so that runtime code will increase
# the capacity and set the buffer.
asm.mov(C_ARGS[0], :rax)
asm.mov(C_ARGS[1], current_capacity)
- asm.mov(C_ARGS[2], new_capacity)
+ asm.mov(C_ARGS[2], dest_shape.capacity)
asm.call(C.rb_ensure_iv_list_size)
# Load the receiver again after the function call
@@ -792,9 +777,30 @@ module RubyVM::RJIT
asm.mov(C_ARGS[0], EC)
asm.mov(C_ARGS[1], to_value(put_val))
+ asm.mov(C_ARGS[2], 0)
asm.call(C.rb_ec_str_resurrect)
- stack_top = ctx.stack_push(Type::CString)
+ stack_top = ctx.stack_push(Type::TString)
+ asm.mov(stack_top, C_RET)
+
+ KeepCompiling
+ end
+
+ # @param jit [RubyVM::RJIT::JITState]
+ # @param ctx [RubyVM::RJIT::Context]
+ # @param asm [RubyVM::RJIT::Assembler]
+ def putchilledstring(jit, ctx, asm)
+ put_val = jit.operand(0, ruby: true)
+
+ # Save the PC and SP because the callee will allocate
+ jit_prepare_routine_call(jit, ctx, asm)
+
+ asm.mov(C_ARGS[0], EC)
+ asm.mov(C_ARGS[1], to_value(put_val))
+ asm.mov(C_ARGS[2], 1)
+ asm.call(C.rb_ec_str_resurrect)
+
+ stack_top = ctx.stack_push(Type::TString)
asm.mov(stack_top, C_RET)
KeepCompiling
@@ -817,7 +823,7 @@ module RubyVM::RJIT
asm.call(C.rb_str_concat_literals)
ctx.stack_pop(n)
- stack_ret = ctx.stack_push(Type::CString)
+ stack_ret = ctx.stack_push(Type::TString)
asm.mov(stack_ret, C_RET)
KeepCompiling
@@ -932,7 +938,7 @@ module RubyVM::RJIT
asm.call(C.rb_ec_ary_new_from_values)
ctx.stack_pop(n)
- stack_ret = ctx.stack_push(Type::CArray)
+ stack_ret = ctx.stack_push(Type::TArray)
asm.mov(stack_ret, C_RET)
KeepCompiling
@@ -954,7 +960,7 @@ module RubyVM::RJIT
asm.mov(C_ARGS[0], ary)
asm.call(C.rb_ary_resurrect)
- stack_ret = ctx.stack_push(Type::CArray)
+ stack_ret = ctx.stack_push(Type::TArray)
asm.mov(stack_ret, C_RET)
KeepCompiling
@@ -1940,26 +1946,30 @@ module RubyVM::RJIT
end
# Jump to target0 on jnz
- branch_stub.compile = proc do |branch_asm|
- branch_asm.comment("branchif #{branch_stub.shape}")
- branch_asm.stub(branch_stub) do
- case branch_stub.shape
- in Default
- branch_asm.jnz(branch_stub.target0.address)
- branch_asm.jmp(branch_stub.target1.address)
- in Next0
- branch_asm.jz(branch_stub.target1.address)
- in Next1
- branch_asm.jnz(branch_stub.target0.address)
- end
- end
- end
+ branch_stub.compile = compile_branchif(branch_stub)
branch_stub.compile.call(asm)
end
EndBlock
end
+ def compile_branchif(branch_stub) # Proc escapes arguments in memory
+ proc do |branch_asm|
+ branch_asm.comment("branchif #{branch_stub.shape}")
+ branch_asm.stub(branch_stub) do
+ case branch_stub.shape
+ in Default
+ branch_asm.jnz(branch_stub.target0.address)
+ branch_asm.jmp(branch_stub.target1.address)
+ in Next0
+ branch_asm.jz(branch_stub.target1.address)
+ in Next1
+ branch_asm.jnz(branch_stub.target0.address)
+ end
+ end
+ end
+ end
+
# @param jit [RubyVM::RJIT::JITState]
# @param ctx [RubyVM::RJIT::Context]
# @param asm [RubyVM::RJIT::Assembler]
@@ -2001,26 +2011,30 @@ module RubyVM::RJIT
end
# Jump to target0 on jz
- branch_stub.compile = proc do |branch_asm|
- branch_asm.comment("branchunless #{branch_stub.shape}")
- branch_asm.stub(branch_stub) do
- case branch_stub.shape
- in Default
- branch_asm.jz(branch_stub.target0.address)
- branch_asm.jmp(branch_stub.target1.address)
- in Next0
- branch_asm.jnz(branch_stub.target1.address)
- in Next1
- branch_asm.jz(branch_stub.target0.address)
- end
- end
- end
+ branch_stub.compile = compile_branchunless(branch_stub)
branch_stub.compile.call(asm)
end
EndBlock
end
+ def compile_branchunless(branch_stub) # Proc escapes arguments in memory
+ proc do |branch_asm|
+ branch_asm.comment("branchunless #{branch_stub.shape}")
+ branch_asm.stub(branch_stub) do
+ case branch_stub.shape
+ in Default
+ branch_asm.jz(branch_stub.target0.address)
+ branch_asm.jmp(branch_stub.target1.address)
+ in Next0
+ branch_asm.jnz(branch_stub.target1.address)
+ in Next1
+ branch_asm.jz(branch_stub.target0.address)
+ end
+ end
+ end
+ end
+
# @param jit [RubyVM::RJIT::JITState]
# @param ctx [RubyVM::RJIT::Context]
# @param asm [RubyVM::RJIT::Assembler]
@@ -2061,26 +2075,30 @@ module RubyVM::RJIT
end
# Jump to target0 on je
- branch_stub.compile = proc do |branch_asm|
- branch_asm.comment("branchnil #{branch_stub.shape}")
- branch_asm.stub(branch_stub) do
- case branch_stub.shape
- in Default
- branch_asm.je(branch_stub.target0.address)
- branch_asm.jmp(branch_stub.target1.address)
- in Next0
- branch_asm.jne(branch_stub.target1.address)
- in Next1
- branch_asm.je(branch_stub.target0.address)
- end
- end
- end
+ branch_stub.compile = compile_branchnil(branch_stub)
branch_stub.compile.call(asm)
end
EndBlock
end
+ def compile_branchnil(branch_stub) # Proc escapes arguments in memory
+ proc do |branch_asm|
+ branch_asm.comment("branchnil #{branch_stub.shape}")
+ branch_asm.stub(branch_stub) do
+ case branch_stub.shape
+ in Default
+ branch_asm.je(branch_stub.target0.address)
+ branch_asm.jmp(branch_stub.target1.address)
+ in Next0
+ branch_asm.jne(branch_stub.target1.address)
+ in Next1
+ branch_asm.je(branch_stub.target0.address)
+ end
+ end
+ end
+ end
+
# once
# @param jit [RubyVM::RJIT::JITState]
@@ -2116,7 +2134,7 @@ module RubyVM::RJIT
end
# Check if the key is the same value
- asm.cmp(key_opnd, comptime_key)
+ asm.cmp(key_opnd, to_value(comptime_key))
side_exit = side_exit(jit, starting_context)
jit_chain_guard(:jne, jit, starting_context, asm, side_exit)
@@ -2732,7 +2750,7 @@ module RubyVM::RJIT
sample_rhs = jit.peek_at_stack(0)
sample_lhs = jit.peek_at_stack(1)
- # We are not allowing module here because the module hierachy can change at runtime.
+ # We are not allowing module here because the module hierarchy can change at runtime.
if C.RB_TYPE_P(sample_rhs, C::RUBY_T_CLASS)
return false
end
@@ -2994,15 +3012,12 @@ module RubyVM::RJIT
# @param ctx [RubyVM::RJIT::Context]
# @param asm [RubyVM::RJIT::Assembler]
def jit_rb_str_empty_p(jit, ctx, asm, argc, known_recv_class)
- # Assume same offset to len embedded or not so we can use one code path to read the length
- #assert_equal(C.RString.offsetof(:as, :heap, :len), C.RString.offsetof(:as, :embed, :len))
-
recv_opnd = ctx.stack_pop(1)
out_opnd = ctx.stack_push(Type::UnknownImm)
asm.comment('get string length')
asm.mov(:rax, recv_opnd)
- str_len_opnd = [:rax, C.RString.offsetof(:as, :heap, :len)]
+ str_len_opnd = [:rax, C.RString.offsetof(:len)]
asm.cmp(str_len_opnd, 0)
asm.mov(:rax, Qfalse)
@@ -3085,7 +3100,7 @@ module RubyVM::RJIT
asm.test(recv_reg, C::RUBY_ENCODING_MASK)
# Push once, use the resulting operand in both branches below.
- stack_ret = ctx.stack_push(Type::CString)
+ stack_ret = ctx.stack_push(Type::TString)
enc_mismatch = asm.new_label('enc_mismatch')
asm.jnz(enc_mismatch)
@@ -3644,21 +3659,25 @@ module RubyVM::RJIT
@exit_compiler.compile_branch_stub(deeper, ocb_asm, branch_stub, true)
@ocb.write(ocb_asm)
end
- branch_stub.compile = proc do |branch_asm|
- # Not using `asm.comment` here since it's usually put before cmp/test before this.
- branch_asm.stub(branch_stub) do
- case branch_stub.shape
- in Default
- branch_asm.public_send(opcode, branch_stub.target0.address)
- end
- end
- end
+ branch_stub.compile = compile_jit_chain_guard(branch_stub, opcode:)
branch_stub.compile.call(asm)
else
asm.public_send(opcode, side_exit)
end
end
+ def compile_jit_chain_guard(branch_stub, opcode:) # Proc escapes arguments in memory
+ proc do |branch_asm|
+ # Not using `asm.comment` here since it's usually put before cmp/test before this.
+ branch_asm.stub(branch_stub) do
+ case branch_stub.shape
+ in Default
+ branch_asm.public_send(opcode, branch_stub.target0.address)
+ end
+ end
+ end
+ end
+
# @param jit [RubyVM::RJIT::JITState]
# @param ctx [RubyVM::RJIT::Context]
# @param asm [RubyVM::RJIT::Assembler]
@@ -3739,7 +3758,7 @@ module RubyVM::RJIT
ctx.upgrade_opnd_type(insn_opnd, Type::Flonum)
end
- elsif C.FL_TEST(known_klass, C::RUBY_FL_SINGLETON) && comptime_obj == C.rb_class_attached_object(known_klass)
+ elsif C.RCLASS_SINGLETON_P(known_klass) && comptime_obj == C.rb_class_attached_object(known_klass)
# Singleton classes are attached to one specific object, so we can
# avoid one memory access (and potentially the is_heap check) by
# looking for the expected object directly.
@@ -3782,9 +3801,14 @@ module RubyVM::RJIT
jit_chain_guard(:jne, jit, ctx, asm, side_exit, limit:)
if known_klass == C.rb_cString
- ctx.upgrade_opnd_type(insn_opnd, Type::CString)
+ # Upgrading to Type::CString here is incorrect.
+ # The guard we put only checks RBASIC_CLASS(obj),
+ # which adding a singleton class can change. We
+ # additionally need to know the string is frozen
+ # to claim Type::CString.
+ ctx.upgrade_opnd_type(insn_opnd, Type::TString)
elsif known_klass == C.rb_cArray
- ctx.upgrade_opnd_type(insn_opnd, Type::CArray)
+ ctx.upgrade_opnd_type(insn_opnd, Type::TArray)
end
end
end
@@ -4450,6 +4474,11 @@ module RubyVM::RJIT
return CantCompile
end
+ if flags & C::VM_CALL_KW_SPLAT != 0
+ asm.incr_counter(:send_iseq_kw_splat)
+ return CantCompile
+ end
+
if iseq_has_rest && opt_num != 0
asm.incr_counter(:send_iseq_has_rest_and_optional)
return CantCompile
@@ -4556,7 +4585,8 @@ module RubyVM::RJIT
# Check if we need the arg0 splat handling of vm_callee_setup_block_arg
arg_setup_block = (calling.block_handler == :captured) # arg_setup_type: arg_setup_block (invokeblock)
block_arg0_splat = arg_setup_block && argc == 1 &&
- iseq.body.param.flags.has_lead && !iseq.body.param.flags.ambiguous_param0
+ (iseq.body.param.flags.has_lead || opt_num > 1) &&
+ !iseq.body.param.flags.ambiguous_param0
if block_arg0_splat
# If block_arg0_splat, we still need side exits after splat, but
# doing push_splat_args here disallows it. So bail out.
@@ -4570,6 +4600,13 @@ module RubyVM::RJIT
asm.incr_counter(:invokeblock_iseq_arg0_has_kw)
return CantCompile
end
+ # The block_arg0_splat implementation cannot deal with optional parameters.
+ # This is a setup_parameters_complex() situation and interacts with the
+ # starting position of the callee.
+ if opt_num > 1
+ asm.incr_counter(:invokeblock_iseq_arg0_optional)
+ return CantCompile
+ end
end
if flags & C::VM_CALL_ARGS_SPLAT != 0 && !iseq_has_rest
array = jit.peek_at_stack(block_arg ? 1 : 0)
@@ -4718,7 +4755,7 @@ module RubyVM::RJIT
asm.call(C.rb_ec_ary_new_from_values)
ctx.stack_pop(n)
- stack_ret = ctx.stack_push(Type::CArray)
+ stack_ret = ctx.stack_push(Type::TArray)
asm.mov(stack_ret, C_RET)
end
end
@@ -4914,13 +4951,10 @@ module RubyVM::RJIT
asm.comment('inlined leaf builtin')
- # Skip this if it doesn't trigger GC
- if iseq.body.builtin_attrs & C::BUILTIN_ATTR_NO_GC == 0
- # The callee may allocate, e.g. Integer#abs on a Bignum.
- # Save SP for GC, save PC for allocation tracing, and prepare
- # for global invalidation after GC's VM lock contention.
- jit_prepare_routine_call(jit, ctx, asm)
- end
+ # The callee may allocate, e.g. Integer#abs on a Bignum.
+ # Save SP for GC, save PC for allocation tracing, and prepare
+ # for global invalidation after GC's VM lock contention.
+ jit_prepare_routine_call(jit, ctx, asm)
# Call the builtin func (ec, recv, arg1, arg2, ...)
asm.mov(C_ARGS[0], EC)
@@ -5613,7 +5647,6 @@ module RubyVM::RJIT
sp_reg = iseq ? SP : :rax
asm.lea(sp_reg, [SP, C.VALUE.size * sp_offset])
asm.mov([CFP, cfp_offset + C.rb_control_frame_t.offsetof(:sp)], sp_reg)
- asm.mov([CFP, cfp_offset + C.rb_control_frame_t.offsetof(:__bp__)], sp_reg) # TODO: get rid of this!!
# cfp->jit_return is used only for ISEQs
if iseq
@@ -5635,16 +5668,7 @@ module RubyVM::RJIT
@exit_compiler.compile_branch_stub(return_ctx, ocb_asm, branch_stub, true)
@ocb.write(ocb_asm)
end
- branch_stub.compile = proc do |branch_asm|
- branch_asm.comment('set jit_return to callee CFP')
- branch_asm.stub(branch_stub) do
- case branch_stub.shape
- in Default
- branch_asm.mov(:rax, branch_stub.target0.address)
- branch_asm.mov([CFP, cfp_offset + C.rb_control_frame_t.offsetof(:jit_return)], :rax)
- end
- end
- end
+ branch_stub.compile = compile_jit_return(branch_stub, cfp_offset:)
branch_stub.compile.call(asm)
end
@@ -5655,6 +5679,19 @@ module RubyVM::RJIT
asm.mov([EC, C.rb_execution_context_t.offsetof(:cfp)], cfp_reg)
end
+ def compile_jit_return(branch_stub, cfp_offset:) # Proc escapes arguments in memory
+ proc do |branch_asm|
+ branch_asm.comment('set jit_return to callee CFP')
+ branch_asm.stub(branch_stub) do
+ case branch_stub.shape
+ in Default
+ branch_asm.mov(:rax, branch_stub.target0.address)
+ branch_asm.mov([CFP, cfp_offset + C.rb_control_frame_t.offsetof(:jit_return)], :rax)
+ end
+ end
+ end
+ end
+
# CALLER_SETUP_ARG: Return CantCompile if not supported
# @param jit [RubyVM::RJIT::JITState]
# @param ctx [RubyVM::RJIT::Context]
@@ -5805,7 +5842,7 @@ module RubyVM::RJIT
asm.cmovz(len_reg, [array_reg, C.RArray.offsetof(:as, :heap, :len)])
end
- # Generate RARRAY_CONST_PTR_TRANSIENT (part of RARRAY_AREF)
+ # Generate RARRAY_CONST_PTR (part of RARRAY_AREF)
def jit_array_ptr(asm, array_reg, ary_opnd) # clobbers array_reg
asm.comment('get array pointer for embedded or heap')
@@ -5875,7 +5912,12 @@ module RubyVM::RJIT
@exit_compiler.compile_branch_stub(ctx, ocb_asm, branch_stub, true)
@ocb.write(ocb_asm)
end
- branch_stub.compile = proc do |branch_asm|
+ branch_stub.compile = compile_jit_direct_jump(branch_stub, comment:)
+ branch_stub.compile.call(asm)
+ end
+
+ def compile_jit_direct_jump(branch_stub, comment:) # Proc escapes arguments in memory
+ proc do |branch_asm|
branch_asm.comment(comment)
branch_asm.stub(branch_stub) do
case branch_stub.shape
@@ -5886,7 +5928,6 @@ module RubyVM::RJIT
end
end
end
- branch_stub.compile.call(asm)
end
# @param jit [RubyVM::RJIT::JITState]
diff --git a/lib/ruby_vm/rjit/invariants.rb b/lib/ruby_vm/rjit/invariants.rb
index db1068b5c2..5b061d1994 100644
--- a/lib/ruby_vm/rjit/invariants.rb
+++ b/lib/ruby_vm/rjit/invariants.rb
@@ -143,11 +143,11 @@ module RubyVM::RJIT
C.rjit_for_each_iseq do |iseq|
# Avoid entering past code
- iseq.body.jit_func = 0
+ iseq.body.jit_entry = 0
# Avoid reusing past code
iseq.body.rjit_blocks.clear if iseq.body.rjit_blocks
# Compile this again if not converted to trace_* insns
- iseq.body.total_calls = 0
+ iseq.body.jit_entry_calls = 0
end
end
end
diff --git a/lib/ruby_vm/rjit/stats.rb b/lib/ruby_vm/rjit/stats.rb
index 8c4253880a..7e353c698e 100644
--- a/lib/ruby_vm/rjit/stats.rb
+++ b/lib/ruby_vm/rjit/stats.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
module RubyVM::RJIT
+ # Return a Hash for \RJIT statistics. \--rjit-stats makes more information available.
def self.runtime_stats
stats = {}
@@ -15,6 +16,7 @@ module RubyVM::RJIT
C.rb_rjit_runtime_counters.members.each do |member|
stats[member] = C.rb_rjit_counters.public_send(member)
end
+ stats[:vm_insns_count] = C.rb_vm_insns_count
# Other stats are calculated here
stats[:side_exit_count] = stats.select { |name, _count| name.start_with?('exit_') }.sum(&:last)
@@ -29,6 +31,7 @@ module RubyVM::RJIT
stats
end
+ # :nodoc: all
class << self
private
diff --git a/lib/ruby_vm/rjit/type.rb b/lib/ruby_vm/rjit/type.rb
index 155e141d63..119692014b 100644
--- a/lib/ruby_vm/rjit/type.rb
+++ b/lib/ruby_vm/rjit/type.rb
@@ -35,7 +35,6 @@ module RubyVM::RJIT
case self
in Type::UnknownHeap then true
in Type::TArray then true
- in Type::CArray then true
in Type::Hash then true
in Type::HeapSymbol then true
in Type::TString then true
@@ -45,11 +44,10 @@ module RubyVM::RJIT
end
end
- # Check if it's a T_ARRAY object (both TArray and CArray are T_ARRAY)
+ # Check if it's a T_ARRAY object
def array?
case self
in Type::TArray then true
- in Type::CArray then true
else false
end
end
@@ -73,7 +71,6 @@ module RubyVM::RJIT
in Type::Flonum then C.rb_cFloat
in Type::ImmSymbol | Type::HeapSymbol then C.rb_cSymbol
in Type::CString then C.rb_cString
- in Type::CArray then C.rb_cArray
else nil
end
end
@@ -115,11 +112,6 @@ module RubyVM::RJIT
return TypeDiff::Compatible[1]
end
- # A CArray is also a TArray.
- if self == Type::CArray && dst == Type::TArray
- return TypeDiff::Compatible[1]
- end
-
# Specific heap type into unknown heap type is imperfect but valid
if self.heap? && dst == Type::UnknownHeap
return TypeDiff::Compatible[1]
@@ -169,12 +161,9 @@ module RubyVM::RJIT
end
else
val_class = C.to_value(C.rb_class_of(val))
- if val_class == C.rb_cString
+ if val_class == C.rb_cString && C.rb_obj_frozen_p(val)
return Type::CString
end
- if val_class == C.rb_cArray
- return Type::CArray
- end
if C.to_value(val) == C.rb_block_param_proxy
return Type::BlockParamProxy
end
@@ -222,7 +211,6 @@ module RubyVM::RJIT
Type::TString = Type[:TString] # An object with the T_STRING flag set, possibly an rb_cString
Type::CString = Type[:CString] # An un-subclassed string of type rb_cString (can have instance vars in some cases)
Type::TArray = Type[:TArray] # An object with the T_ARRAY flag set, possibly an rb_cArray
- Type::CArray = Type[:CArray] # An un-subclassed string of type rb_cArray (can have instance vars in some cases)
Type::BlockParamProxy = Type[:BlockParamProxy] # A special sentinel value indicating the block parameter should be read from
diff --git a/lib/rubygems.rb b/lib/rubygems.rb
index cb71657018..ad7ab10756 100644
--- a/lib/rubygems.rb
+++ b/lib/rubygems.rb
@@ -9,7 +9,7 @@
require "rbconfig"
module Gem
- VERSION = "3.5.0.dev"
+ VERSION = "3.6.0.dev"
end
# Must be first since it unloads the prelude from 1.9.2
@@ -115,11 +115,6 @@ require_relative "rubygems/errors"
module Gem
RUBYGEMS_DIR = __dir__
- # Taint support is deprecated in Ruby 2.7.
- # This allows switching ".untaint" to ".tap(&Gem::UNTAINT)",
- # to avoid deprecation warnings in Ruby 2.7.
- UNTAINT = RUBY_VERSION < "2.7" ? :untaint.to_sym : proc {}
-
##
# An Array of Regexps that match windows Ruby platforms.
@@ -496,7 +491,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
glob_with_suffixes = "#{glob}#{Gem.suffix_pattern}"
$LOAD_PATH.map do |load_path|
Gem::Util.glob_files_in_dir(glob_with_suffixes, load_path)
- end.flatten.select {|file| File.file? file.tap(&Gem::UNTAINT) }
+ end.flatten.select {|file| File.file? file }
end
##
@@ -572,7 +567,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
##
# The number of paths in the +$LOAD_PATH+ from activated gems. Used to
- # prioritize +-I+ and +ENV['RUBYLIB']+ entries during +require+.
+ # prioritize +-I+ and <code>ENV['RUBYLIB']</code> entries during +require+.
def self.activated_gem_paths
@activated_gem_paths ||= 0
@@ -604,6 +599,16 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
@yaml_loaded = true
end
+ @safe_marshal_loaded = false
+
+ def self.load_safe_marshal
+ return if @safe_marshal_loaded
+
+ require_relative "rubygems/safe_marshal"
+
+ @safe_marshal_loaded = true
+ end
+
##
# The file name and line number of the caller of the caller of this method.
#
@@ -938,6 +943,13 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
end
##
+ # Suffixes for dynamic library require-able paths.
+
+ def self.dynamic_library_suffixes
+ @dynamic_library_suffixes ||= suffixes - [".rb"]
+ end
+
+ ##
# Prints the amount of time the supplied block takes to run using the debug
# UI output.
@@ -1074,8 +1086,6 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
end
end
- path.tap(&Gem::UNTAINT)
-
unless File.file? path
return unless raise_exception
@@ -1206,9 +1216,16 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
##
# Find a Gem::Specification of default gem from +path+
+ def find_default_spec(path)
+ @path_to_default_spec_map[path]
+ end
+
+ ##
+ # Find an unresolved Gem::Specification of default gem from +path+
+
def find_unresolved_default_spec(path)
default_spec = @path_to_default_spec_map[path]
- return default_spec if default_spec && loaded_specs[default_spec.name] != default_spec
+ default_spec if default_spec && loaded_specs[default_spec.name] != default_spec
end
##
@@ -1284,9 +1301,10 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
##
# Location of Marshal quick gemspecs on remote repositories
- MARSHAL_SPEC_DIR = "quick/Marshal.#{Gem.marshal_version}/"
+ MARSHAL_SPEC_DIR = "quick/Marshal.#{Gem.marshal_version}/".freeze
autoload :ConfigFile, File.expand_path("rubygems/config_file", __dir__)
+ autoload :CIDetector, File.expand_path("rubygems/ci_detector", __dir__)
autoload :Dependency, File.expand_path("rubygems/dependency", __dir__)
autoload :DependencyList, File.expand_path("rubygems/dependency_list", __dir__)
autoload :Installer, File.expand_path("rubygems/installer", __dir__)
@@ -1333,6 +1351,17 @@ begin
rescue LoadError
end
+# TruffleRuby >= 24 defines REUSE_AS_BINARY_ON_TRUFFLERUBY in defaults/truffleruby.
+# However, TruffleRuby < 24 defines REUSE_AS_BINARY_ON_TRUFFLERUBY directly in its copy
+# of lib/rubygems/platform.rb, so it is not defined if RubyGems is updated (gem update --system).
+# Instead, we define it here in that case, similar to bundler/lib/bundler/rubygems_ext.rb.
+# We must define it here and not in platform.rb because platform.rb is loaded before defaults/truffleruby.
+class Gem::Platform
+ if RUBY_ENGINE == "truffleruby" && !defined?(REUSE_AS_BINARY_ON_TRUFFLERUBY)
+ REUSE_AS_BINARY_ON_TRUFFLERUBY = %w[libv8 libv8-node sorbet-static].freeze
+ end
+end
+
##
# Loads the default specs.
Gem::Specification.load_defaults
@@ -1342,7 +1371,7 @@ require_relative "rubygems/core_ext/kernel_gem"
path = File.join(__dir__, "rubygems/core_ext/kernel_require.rb")
# When https://bugs.ruby-lang.org/issues/17259 is available, there is no need to override Kernel#warn
if RUBY_ENGINE == "truffleruby" ||
- (RUBY_ENGINE == "ruby" && RUBY_VERSION >= "3.0")
+ RUBY_ENGINE == "ruby"
file = "<internal:#{path}>"
else
require_relative "rubygems/core_ext/kernel_warn"
diff --git a/lib/rubygems/basic_specification.rb b/lib/rubygems/basic_specification.rb
index f9bedc967f..0380fceece 100644
--- a/lib/rubygems/basic_specification.rb
+++ b/lib/rubygems/basic_specification.rb
@@ -76,7 +76,7 @@ class Gem::BasicSpecification
elsif missing_extensions?
@ignored = true
- if Gem::Platform::RUBY == platform || Gem::Platform.local === platform
+ if platform == Gem::Platform::RUBY || Gem::Platform.local === platform
warn "Ignoring #{full_name} because its extensions are not built. " \
"Try: gem pristine #{name} --version #{version}"
end
@@ -84,7 +84,13 @@ class Gem::BasicSpecification
return false
end
- have_file? file, Gem.suffixes
+ is_soext = file.end_with?(".so", ".o")
+
+ if is_soext
+ have_file? file.delete_suffix(File.extname(file)), Gem.dynamic_library_suffixes
+ else
+ have_file? file, Gem.suffixes
+ end
end
def default_gem?
@@ -96,7 +102,7 @@ class Gem::BasicSpecification
# Returns full path to the directory where gem's extensions are installed.
def extension_dir
- @extension_dir ||= File.expand_path(File.join(extensions_dir, full_name)).tap(&Gem::UNTAINT)
+ @extension_dir ||= File.expand_path(File.join(extensions_dir, full_name))
end
##
@@ -110,9 +116,7 @@ class Gem::BasicSpecification
def find_full_gem_path # :nodoc:
# TODO: also, shouldn't it default to full_name if it hasn't been written?
- path = File.expand_path File.join(gems_dir, full_name)
- path.tap(&Gem::UNTAINT)
- path
+ File.expand_path File.join(gems_dir, full_name)
end
private :find_full_gem_path
@@ -133,9 +137,9 @@ class Gem::BasicSpecification
def full_name
if platform == Gem::Platform::RUBY || platform.nil?
- "#{name}-#{version}".dup.tap(&Gem::UNTAINT)
+ "#{name}-#{version}"
else
- "#{name}-#{version}-#{platform}".dup.tap(&Gem::UNTAINT)
+ "#{name}-#{version}-#{platform}"
end
end
@@ -147,7 +151,7 @@ class Gem::BasicSpecification
@full_require_paths ||=
begin
full_paths = raw_require_paths.map do |path|
- File.join full_gem_path, path.tap(&Gem::UNTAINT)
+ File.join full_gem_path, path
end
full_paths << extension_dir if have_extensions?
@@ -161,7 +165,7 @@ class Gem::BasicSpecification
def datadir
# TODO: drop the extra ", gem_name" which is uselessly redundant
- File.expand_path(File.join(gems_dir, full_name, "data", name)).tap(&Gem::UNTAINT)
+ File.expand_path(File.join(gems_dir, full_name, "data", name))
end
##
@@ -270,7 +274,7 @@ class Gem::BasicSpecification
def matches_for_glob(glob) # TODO: rename?
glob = File.join(lib_dirs_glob, glob)
- Dir[glob].map {|f| f.tap(&Gem::UNTAINT) } # FIX our tests are broken, run w/ SAFE=1
+ Dir[glob]
end
##
@@ -295,7 +299,7 @@ class Gem::BasicSpecification
"lib" # default value for require_paths for bundler/inline
end
- "#{full_gem_path}/#{dirs}".dup.tap(&Gem::UNTAINT)
+ "#{full_gem_path}/#{dirs}"
end
##
@@ -332,7 +336,7 @@ class Gem::BasicSpecification
def have_file?(file, suffixes)
return true if raw_require_paths.any? do |path|
- base = File.join(gems_dir, full_name, path.tap(&Gem::UNTAINT), file).tap(&Gem::UNTAINT)
+ base = File.join(gems_dir, full_name, path, file)
suffixes.any? {|suf| File.file? base + suf }
end
diff --git a/lib/rubygems/bundler_version_finder.rb b/lib/rubygems/bundler_version_finder.rb
index cd51c37c59..dd2fd77418 100644
--- a/lib/rubygems/bundler_version_finder.rb
+++ b/lib/rubygems/bundler_version_finder.rb
@@ -52,7 +52,7 @@ module Gem::BundlerVersionFinder
unless gemfile
begin
Gem::Util.traverse_parents(Dir.pwd) do |directory|
- next unless gemfile = Gem::GEM_DEP_FILES.find {|f| File.file?(f.tap(&Gem::UNTAINT)) }
+ next unless gemfile = Gem::GEM_DEP_FILES.find {|f| File.file?(f) }
gemfile = File.join directory, gemfile
break
@@ -67,7 +67,7 @@ module Gem::BundlerVersionFinder
lockfile = case gemfile
when "gems.rb" then "gems.locked"
else "#{gemfile}.lock"
- end.dup.tap(&Gem::UNTAINT)
+ end
return unless File.file?(lockfile)
diff --git a/lib/rubygems/ci_detector.rb b/lib/rubygems/ci_detector.rb
new file mode 100644
index 0000000000..7a2d4ee29a
--- /dev/null
+++ b/lib/rubygems/ci_detector.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+module Gem
+ module CIDetector
+ # NOTE: Any changes made here will need to be made to both lib/rubygems/ci_detector.rb and
+ # bundler/lib/bundler/ci_detector.rb (which are enforced duplicates).
+ # TODO: Drop that duplication once bundler drops support for RubyGems 3.4
+ #
+ # ## Recognized CI providers, their signifiers, and the relevant docs ##
+ #
+ # Travis CI - CI, TRAVIS https://docs.travis-ci.com/user/environment-variables/#default-environment-variables
+ # Cirrus CI - CI, CIRRUS_CI https://cirrus-ci.org/guide/writing-tasks/#environment-variables
+ # Circle CI - CI, CIRCLECI https://circleci.com/docs/variables/#built-in-environment-variables
+ # Gitlab CI - CI, GITLAB_CI https://docs.gitlab.com/ee/ci/variables/
+ # AppVeyor - CI, APPVEYOR https://www.appveyor.com/docs/environment-variables/
+ # CodeShip - CI_NAME https://docs.cloudbees.com/docs/cloudbees-codeship/latest/pro-builds-and-configuration/environment-variables#_default_environment_variables
+ # dsari - CI, DSARI https://github.com/rfinnie/dsari#running
+ # Jenkins - BUILD_NUMBER https://www.jenkins.io/doc/book/pipeline/jenkinsfile/#using-environment-variables
+ # TeamCity - TEAMCITY_VERSION https://www.jetbrains.com/help/teamcity/predefined-build-parameters.html#Predefined+Server+Build+Parameters
+ # Appflow - CI_BUILD_ID https://ionic.io/docs/appflow/automation/environments#predefined-environments
+ # TaskCluster - TASKCLUSTER_ROOT_URL https://docs.taskcluster.net/docs/manual/design/env-vars
+ # Semaphore - CI, SEMAPHORE https://docs.semaphoreci.com/ci-cd-environment/environment-variables/
+ # BuildKite - CI, BUILDKITE https://buildkite.com/docs/pipelines/environment-variables
+ # GoCD - GO_SERVER_URL https://docs.gocd.org/current/faq/dev_use_current_revision_in_build.html
+ # GH Actions - CI, GITHUB_ACTIONS https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables
+ #
+ # ### Some "standard" ENVs that multiple providers may set ###
+ #
+ # * CI - this is set by _most_ (but not all) CI providers now; it's approaching a standard.
+ # * CI_NAME - Not as frequently used, but some providers set this to specify their own name
+
+ # Any of these being set is a reasonably reliable indicator that we are
+ # executing in a CI environment.
+ ENV_INDICATORS = [
+ "CI",
+ "CI_NAME",
+ "CONTINUOUS_INTEGRATION",
+ "BUILD_NUMBER",
+ "CI_APP_ID",
+ "CI_BUILD_ID",
+ "CI_BUILD_NUMBER",
+ "RUN_ID",
+ "TASKCLUSTER_ROOT_URL",
+ ].freeze
+
+ # For each CI, this env suffices to indicate that we're on _that_ CI's
+ # containers. (A few of them only supply a CI_NAME variable, which is also
+ # nice). And if they set "CI" but we can't tell which one they are, we also
+ # want to know that - a bare "ci" without another token tells us as much.
+ ENV_DESCRIPTORS = {
+ "TRAVIS" => "travis",
+ "CIRCLECI" => "circle",
+ "CIRRUS_CI" => "cirrus",
+ "DSARI" => "dsari",
+ "SEMAPHORE" => "semaphore",
+ "JENKINS_URL" => "jenkins",
+ "BUILDKITE" => "buildkite",
+ "GO_SERVER_URL" => "go",
+ "GITLAB_CI" => "gitlab",
+ "GITHUB_ACTIONS" => "github",
+ "TASKCLUSTER_ROOT_URL" => "taskcluster",
+ "CI" => "ci",
+ }.freeze
+
+ def self.ci?
+ ENV_INDICATORS.any? {|var| ENV.include?(var) }
+ end
+
+ def self.ci_strings
+ matching_names = ENV_DESCRIPTORS.select {|env, _| ENV[env] }.values
+ matching_names << ENV["CI_NAME"].downcase if ENV["CI_NAME"]
+ matching_names.reject(&:empty?).sort.uniq
+ end
+ end
+end
diff --git a/lib/rubygems/command.rb b/lib/rubygems/command.rb
index 53bb609ae0..ec498a8b94 100644
--- a/lib/rubygems/command.rb
+++ b/lib/rubygems/command.rb
@@ -6,7 +6,7 @@
# See LICENSE.txt for permissions.
#++
-require_relative "optparse"
+require_relative "vendored_optparse"
require_relative "requirement"
require_relative "user_interaction"
@@ -190,7 +190,7 @@ class Gem::Command
"Please specify at least one gem name (e.g. gem build GEMNAME)"
end
- args.select {|arg| arg !~ /^-/ }
+ args.reject {|arg| arg.start_with?("-") }
end
##
@@ -485,7 +485,7 @@ class Gem::Command
@parser.separator nil
@parser.separator " Description:"
- formatted.split("\n").each do |line|
+ formatted.each_line do |line|
@parser.separator " #{line.rstrip}"
end
end
@@ -512,8 +512,8 @@ class Gem::Command
@parser.separator nil
@parser.separator " #{title}:"
- content.split(/\n/).each do |line|
- @parser.separator " #{line}"
+ content.each_line do |line|
+ @parser.separator " #{line.rstrip}"
end
end
@@ -522,7 +522,7 @@ class Gem::Command
@parser.separator nil
@parser.separator " Summary:"
- wrap(@summary, 80 - 4).split("\n").each do |line|
+ wrap(@summary, 80 - 4).each_line do |line|
@parser.separator " #{line.strip}"
end
end
diff --git a/lib/rubygems/command_manager.rb b/lib/rubygems/command_manager.rb
index c53f0231af..8e578dc196 100644
--- a/lib/rubygems/command_manager.rb
+++ b/lib/rubygems/command_manager.rb
@@ -60,6 +60,7 @@ class Gem::CommandManager
:push,
:query,
:rdoc,
+ :rebuild,
:search,
:server,
:signin,
@@ -106,7 +107,7 @@ class Gem::CommandManager
# Register all the subcommands supported by the gem command.
def initialize
- require "timeout"
+ require_relative "vendored_timeout"
@commands = {}
BUILTIN_COMMANDS.each do |name|
@@ -149,7 +150,7 @@ class Gem::CommandManager
def run(args, build_args=nil)
process_args(args, build_args)
- rescue StandardError, Timeout::Error => ex
+ rescue StandardError, Gem::Timeout::Error => ex
if ex.respond_to?(:detailed_message)
msg = ex.detailed_message(highlight: false).sub(/\A(.*?)(?: \(.+?\))/) { $1 }
else
@@ -249,6 +250,7 @@ class Gem::CommandManager
def invoke_command(args, build_args)
cmd_name = args.shift.downcase
cmd = find_command cmd_name
+ terminate_interaction 1 unless cmd
cmd.deprecation_warning if cmd.deprecated?
cmd.invoke_with_build_args args, build_args
end
diff --git a/lib/rubygems/commands/build_command.rb b/lib/rubygems/commands/build_command.rb
index 0ebdec565b..2ec8324141 100644
--- a/lib/rubygems/commands/build_command.rb
+++ b/lib/rubygems/commands/build_command.rb
@@ -1,11 +1,13 @@
# frozen_string_literal: true
require_relative "../command"
+require_relative "../gemspec_helpers"
require_relative "../package"
require_relative "../version_option"
class Gem::Commands::BuildCommand < Gem::Command
include Gem::VersionOption
+ include Gem::GemspecHelpers
def initialize
super "build", "Build a gem from a gemspec"
@@ -75,17 +77,6 @@ Gems can be saved to a specified filename with the output option:
private
- def find_gemspec(glob = "*.gemspec")
- gemspecs = Dir.glob(glob).sort
-
- if gemspecs.size > 1
- alert_error "Multiple gemspecs found: #{gemspecs}, please specify one"
- terminate_interaction(1)
- end
-
- gemspecs.first
- end
-
def build_gem
gemspec = resolve_gem_name
diff --git a/lib/rubygems/commands/cert_command.rb b/lib/rubygems/commands/cert_command.rb
index 22864a9b29..72dcf1dd17 100644
--- a/lib/rubygems/commands/cert_command.rb
+++ b/lib/rubygems/commands/cert_command.rb
@@ -6,7 +6,7 @@ require_relative "../security"
class Gem::Commands::CertCommand < Gem::Command
def initialize
super "cert", "Manage RubyGems certificates and signing settings",
- :add => [], :remove => [], :list => [], :build => [], :sign => []
+ add: [], remove: [], list: [], build: [], sign: []
add_option("-a", "--add CERT",
"Add a trusted certificate.") do |cert_file, options|
diff --git a/lib/rubygems/commands/check_command.rb b/lib/rubygems/commands/check_command.rb
index 6552258552..fb23dd9cb4 100644
--- a/lib/rubygems/commands/check_command.rb
+++ b/lib/rubygems/commands/check_command.rb
@@ -10,7 +10,7 @@ class Gem::Commands::CheckCommand < Gem::Command
def initialize
super "check", "Check a gem repository for added or missing files",
- :alien => true, :doctor => false, :dry_run => false, :gems => true
+ alien: true, doctor: false, dry_run: false, gems: true
add_option("-a", "--[no-]alien",
'Report "unmanaged" or rogue files in the',
diff --git a/lib/rubygems/commands/cleanup_command.rb b/lib/rubygems/commands/cleanup_command.rb
index 26beba48df..08fb598cea 100644
--- a/lib/rubygems/commands/cleanup_command.rb
+++ b/lib/rubygems/commands/cleanup_command.rb
@@ -8,8 +8,8 @@ class Gem::Commands::CleanupCommand < Gem::Command
def initialize
super "cleanup",
"Clean up old versions of installed gems",
- :force => false, :install_dir => Gem.dir,
- :check_dev => true
+ force: false, install_dir: Gem.dir,
+ check_dev: true
add_option("-n", "-d", "--dry-run",
"Do not uninstall gems") do |_value, options|
@@ -166,8 +166,8 @@ If no gems are named all gems in GEM_HOME are cleaned.
say "Attempting to uninstall #{spec.full_name}"
uninstall_options = {
- :executables => false,
- :version => "= #{spec.version}",
+ executables: false,
+ version: "= #{spec.version}",
}
uninstall_options[:user_install] = Gem.user_dir == spec.base_dir
diff --git a/lib/rubygems/commands/contents_command.rb b/lib/rubygems/commands/contents_command.rb
index f3783468aa..807158d9c9 100644
--- a/lib/rubygems/commands/contents_command.rb
+++ b/lib/rubygems/commands/contents_command.rb
@@ -8,8 +8,8 @@ class Gem::Commands::ContentsCommand < Gem::Command
def initialize
super "contents", "Display the contents of the installed gems",
- :specdirs => [], :lib_only => false, :prefix => true,
- :show_install_dir => false
+ specdirs: [], lib_only: false, prefix: true,
+ show_install_dir: false
add_version_option
diff --git a/lib/rubygems/commands/dependency_command.rb b/lib/rubygems/commands/dependency_command.rb
index 109bf03aff..9aaefae999 100644
--- a/lib/rubygems/commands/dependency_command.rb
+++ b/lib/rubygems/commands/dependency_command.rb
@@ -11,7 +11,7 @@ class Gem::Commands::DependencyCommand < Gem::Command
def initialize
super "dependency",
"Show the dependencies of an installed gem",
- :version => Gem::Requirement.default, :domain => :local
+ version: Gem::Requirement.default, domain: :local
add_version_option
add_platform_option
diff --git a/lib/rubygems/commands/fetch_command.rb b/lib/rubygems/commands/fetch_command.rb
index 950d4fe30e..f7f5b62306 100644
--- a/lib/rubygems/commands/fetch_command.rb
+++ b/lib/rubygems/commands/fetch_command.rb
@@ -10,8 +10,8 @@ class Gem::Commands::FetchCommand < Gem::Command
def initialize
defaults = {
- :suggest_alternate => true,
- :version => Gem::Requirement.default,
+ suggest_alternate: true,
+ version: Gem::Requirement.default,
}
super "fetch", "Download a gem and place it in the current directory", defaults
diff --git a/lib/rubygems/commands/generate_index_command.rb b/lib/rubygems/commands/generate_index_command.rb
index ce580cfaf9..13be92593b 100644
--- a/lib/rubygems/commands/generate_index_command.rb
+++ b/lib/rubygems/commands/generate_index_command.rb
@@ -1,86 +1,51 @@
# frozen_string_literal: true
require_relative "../command"
-require_relative "../indexer"
-##
-# Generates a index files for use as a gem server.
-#
-# See `gem help generate_index`
-
-class Gem::Commands::GenerateIndexCommand < Gem::Command
- def initialize
- super "generate_index",
- "Generates the index files for a gem server directory",
- :directory => ".", :build_modern => true
+unless defined? Gem::Commands::GenerateIndexCommand
+ class Gem::Commands::GenerateIndexCommand < Gem::Command
+ module RubygemsTrampoline
+ def description # :nodoc:
+ <<~EOF
+ The generate_index command has been moved to the rubygems-generate_index gem.
+ EOF
+ end
- add_option "-d", "--directory=DIRNAME",
- "repository base dir containing gems subdir" do |dir, options|
- options[:directory] = File.expand_path dir
- end
+ def execute
+ alert_error "Install the rubygems-generate_index gem for the generate_index command"
+ end
- add_option "--[no-]modern",
- "Generate indexes for RubyGems",
- "(always true)" do |value, options|
- options[:build_modern] = value
+ def invoke_with_build_args(args, build_args)
+ name = "rubygems-generate_index"
+ spec = begin
+ Gem::Specification.find_by_name(name)
+ rescue Gem::LoadError
+ require "rubygems/dependency_installer"
+ Gem.install(name, Gem::Requirement.default, Gem::DependencyInstaller::DEFAULT_OPTIONS).find {|s| s.name == name }
+ end
+
+ # remove the methods defined in this file so that the methods defined in the gem are used instead,
+ # and without a method redefinition warning
+ %w[description execute invoke_with_build_args].each do |method|
+ RubygemsTrampoline.remove_method(method)
+ end
+ self.class.singleton_class.remove_method(:new)
+
+ spec.activate
+ Gem.load_plugin_files spec.matches_for_glob("rubygems_plugin#{Gem.suffix_pattern}")
+
+ self.class.new.invoke_with_build_args(args, build_args)
+ end
end
+ private_constant :RubygemsTrampoline
- deprecate_option("--modern", version: "4.0", extra_msg: "Modern indexes (specs, latest_specs, and prerelease_specs) are always generated, so this option is not needed.")
- deprecate_option("--no-modern", version: "4.0", extra_msg: "The `--no-modern` option is currently ignored. Modern indexes (specs, latest_specs, and prerelease_specs) are always generated.")
-
- add_option "--update",
- "Update modern indexes with gems added",
- "since the last update" do |value, options|
- options[:update] = value
+ # remove_method(:initialize) warns, but removing new does not warn
+ def self.new
+ command = allocate
+ command.send(:initialize, "generate_index", "Generates the index files for a gem server directory (requires rubygems-generate_index)")
+ command
end
- end
-
- def defaults_str # :nodoc:
- "--directory . --modern"
- end
-
- def description # :nodoc:
- <<-EOF
-The generate_index command creates a set of indexes for serving gems
-statically. The command expects a 'gems' directory under the path given to
-the --directory option. The given directory will be the directory you serve
-as the gem repository.
-For `gem generate_index --directory /path/to/repo`, expose /path/to/repo via
-your HTTP server configuration (not /path/to/repo/gems).
-
-When done, it will generate a set of files like this:
-
- gems/*.gem # .gem files you want to
- # index
-
- specs.<version>.gz # specs index
- latest_specs.<version>.gz # latest specs index
- prerelease_specs.<version>.gz # prerelease specs index
- quick/Marshal.<version>/<gemname>.gemspec.rz # Marshal quick index file
-
-The .rz extension files are compressed with the inflate algorithm.
-The Marshal version number comes from ruby's Marshal::MAJOR_VERSION and
-Marshal::MINOR_VERSION constants. It is used to ensure compatibility.
- EOF
- end
-
- def execute
- # This is always true because it's the only way now.
- options[:build_modern] = true
-
- if !File.exist?(options[:directory]) ||
- !File.directory?(options[:directory])
- alert_error "unknown directory name #{options[:directory]}."
- terminate_interaction 1
- else
- indexer = Gem::Indexer.new options.delete(:directory), options
-
- if options[:update]
- indexer.update_index
- else
- indexer.generate_index
- end
- end
+ prepend(RubygemsTrampoline)
end
end
diff --git a/lib/rubygems/commands/help_command.rb b/lib/rubygems/commands/help_command.rb
index 043e7d3691..1619b152e7 100644
--- a/lib/rubygems/commands/help_command.rb
+++ b/lib/rubygems/commands/help_command.rb
@@ -59,7 +59,7 @@ multiple environments. The RubyGems implementation is designed to be
compatible with Bundler's Gemfile format. You can see additional
documentation on the format at:
- http://bundler.io
+ https://bundler.io
RubyGems automatically looks for these gem dependencies files:
@@ -172,7 +172,7 @@ and #platforms methods:
See the bundler Gemfile manual page for a list of platforms supported in a gem
dependencies file.:
- http://bundler.io/v1.6/man/gemfile.5.html
+ https://bundler.io/v2.5/man/gemfile.5.html
Ruby Version and Engine Dependency
==================================
@@ -333,7 +333,7 @@ platform.
@command_manager.command_names.each do |cmd_name|
command = @command_manager[cmd_name]
- next if command.deprecated?
+ next if command&.deprecated?
summary =
if command
diff --git a/lib/rubygems/commands/info_command.rb b/lib/rubygems/commands/info_command.rb
index ced7751ff5..f65c639662 100644
--- a/lib/rubygems/commands/info_command.rb
+++ b/lib/rubygems/commands/info_command.rb
@@ -8,8 +8,8 @@ class Gem::Commands::InfoCommand < Gem::Command
def initialize
super "info", "Show information for the given gem",
- :name => //, :domain => :local, :details => false, :versions => true,
- :installed => nil, :version => Gem::Requirement.default
+ name: //, domain: :local, details: false, versions: true,
+ installed: nil, version: Gem::Requirement.default
add_query_options
diff --git a/lib/rubygems/commands/install_command.rb b/lib/rubygems/commands/install_command.rb
index b8dfc90111..2091634a29 100644
--- a/lib/rubygems/commands/install_command.rb
+++ b/lib/rubygems/commands/install_command.rb
@@ -23,11 +23,11 @@ class Gem::Commands::InstallCommand < Gem::Command
def initialize
defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({
- :format_executable => false,
- :lock => true,
- :suggest_alternate => true,
- :version => Gem::Requirement.default,
- :without_groups => [],
+ format_executable: false,
+ lock: true,
+ suggest_alternate: true,
+ version: Gem::Requirement.default,
+ without_groups: [],
})
defaults.merge!(install_update_options)
@@ -136,13 +136,6 @@ You can use `i` command instead of `install`.
"#{program_name} [options] GEMNAME [GEMNAME ...] -- --build-flags"
end
- def check_install_dir # :nodoc:
- if options[:install_dir] && options[:user_install]
- alert_error "Use --install-dir or --user-install but not both"
- terminate_interaction 1
- end
- end
-
def check_version # :nodoc:
if options[:version] != Gem::Requirement.default &&
get_all_gem_names.size > 1
@@ -162,7 +155,6 @@ You can use `i` command instead of `install`.
ENV.delete "GEM_PATH" if options[:install_dir].nil?
- check_install_dir
check_version
load_hooks
@@ -171,7 +163,7 @@ You can use `i` command instead of `install`.
show_installed
- say update_suggestion if eglible_for_update?
+ say update_suggestion if eligible_for_update?
terminate_interaction exit_code
end
diff --git a/lib/rubygems/commands/list_command.rb b/lib/rubygems/commands/list_command.rb
index 522c771f90..fab4b73814 100644
--- a/lib/rubygems/commands/list_command.rb
+++ b/lib/rubygems/commands/list_command.rb
@@ -11,8 +11,8 @@ class Gem::Commands::ListCommand < Gem::Command
def initialize
super "list", "Display local gems whose name matches REGEXP",
- :domain => :local, :details => false, :versions => true,
- :installed => nil, :version => Gem::Requirement.default
+ domain: :local, details: false, versions: true,
+ installed: nil, version: Gem::Requirement.default
add_query_options
end
diff --git a/lib/rubygems/commands/lock_command.rb b/lib/rubygems/commands/lock_command.rb
index 3a9512fe3f..f7fd5ada16 100644
--- a/lib/rubygems/commands/lock_command.rb
+++ b/lib/rubygems/commands/lock_command.rb
@@ -5,7 +5,7 @@ require_relative "../command"
class Gem::Commands::LockCommand < Gem::Command
def initialize
super "lock", "Generate a lockdown list of gems",
- :strict => false
+ strict: false
add_option "-s", "--[no-]strict",
"fail if unable to satisfy a dependency" do |strict, options|
diff --git a/lib/rubygems/commands/open_command.rb b/lib/rubygems/commands/open_command.rb
index 5a13074a1d..0fe90dc8b8 100644
--- a/lib/rubygems/commands/open_command.rb
+++ b/lib/rubygems/commands/open_command.rb
@@ -70,9 +70,7 @@ class Gem::Commands::OpenCommand < Gem::Command
end
def open_editor(path)
- Dir.chdir(path) do
- system(*@editor.split(/\s+/) + [path])
- end
+ system(*@editor.split(/\s+/) + [path], { chdir: path })
end
def spec_for(name)
diff --git a/lib/rubygems/commands/owner_command.rb b/lib/rubygems/commands/owner_command.rb
index fce32aca3e..12bfe3a834 100644
--- a/lib/rubygems/commands/owner_command.rb
+++ b/lib/rubygems/commands/owner_command.rb
@@ -39,7 +39,7 @@ permission to.
add_proxy_option
add_key_option
add_otp_option
- defaults.merge! :add => [], :remove => []
+ defaults.merge! add: [], remove: []
add_option "-a", "--add NEW_OWNER", "Add an owner by user identifier" do |value, options|
options[:add] << value
diff --git a/lib/rubygems/commands/pristine_command.rb b/lib/rubygems/commands/pristine_command.rb
index fff59855f5..456d897df2 100644
--- a/lib/rubygems/commands/pristine_command.rb
+++ b/lib/rubygems/commands/pristine_command.rb
@@ -11,10 +11,10 @@ class Gem::Commands::PristineCommand < Gem::Command
def initialize
super "pristine",
"Restores installed gems to pristine condition from files located in the gem cache",
- :version => Gem::Requirement.default,
- :extensions => true,
- :extensions_set => false,
- :all => false
+ version: Gem::Requirement.default,
+ extensions: true,
+ extensions_set: false,
+ all: false
add_option("--all",
"Restore all installed gems to pristine",
@@ -121,7 +121,7 @@ extensions will be restored.
end.flatten
end
- specs = specs.select {|spec| RUBY_ENGINE == spec.platform || Gem::Platform.local === spec.platform || spec.platform == Gem::Platform::RUBY }
+ specs = specs.select {|spec| spec.platform == RUBY_ENGINE || Gem::Platform.local === spec.platform || spec.platform == Gem::Platform::RUBY }
if specs.to_a.empty?
raise Gem::Exception,
@@ -179,12 +179,12 @@ extensions will be restored.
install_dir = options[:install_dir] if options[:install_dir]
installer_options = {
- :wrappers => true,
- :force => true,
- :install_dir => install_dir || spec.base_dir,
- :env_shebang => env_shebang,
- :build_args => spec.build_args,
- :bin_dir => bin_dir,
+ wrappers: true,
+ force: true,
+ install_dir: install_dir || spec.base_dir,
+ env_shebang: env_shebang,
+ build_args: spec.build_args,
+ bin_dir: bin_dir,
}
if options[:only_executables]
diff --git a/lib/rubygems/commands/push_command.rb b/lib/rubygems/commands/push_command.rb
index 418b46acc5..591ddc3a80 100644
--- a/lib/rubygems/commands/push_command.rb
+++ b/lib/rubygems/commands/push_command.rb
@@ -30,7 +30,7 @@ The push command will use ~/.gem/credentials to authenticate to a server, but yo
end
def initialize
- super "push", "Push a gem up to the gem server", :host => host
+ super "push", "Push a gem up to the gem server", host: host
@user_defined_host = false
diff --git a/lib/rubygems/commands/query_command.rb b/lib/rubygems/commands/query_command.rb
index e7d21f341b..3b527974a3 100644
--- a/lib/rubygems/commands/query_command.rb
+++ b/lib/rubygems/commands/query_command.rb
@@ -20,8 +20,8 @@ class Gem::Commands::QueryCommand < Gem::Command
def initialize(name = "query", summary = "Query gem information in local or remote repositories")
super name, summary,
- :domain => :local, :details => false, :versions => true,
- :installed => nil, :version => Gem::Requirement.default
+ domain: :local, details: false, versions: true,
+ installed: nil, version: Gem::Requirement.default
add_option("-n", "--name-matches REGEXP",
"Name of gem(s) to query on matches the",
diff --git a/lib/rubygems/commands/rdoc_command.rb b/lib/rubygems/commands/rdoc_command.rb
index e318a52914..977c90b8c4 100644
--- a/lib/rubygems/commands/rdoc_command.rb
+++ b/lib/rubygems/commands/rdoc_command.rb
@@ -10,8 +10,8 @@ class Gem::Commands::RdocCommand < Gem::Command
def initialize
super "rdoc", "Generates RDoc for pre-installed gems",
- :version => Gem::Requirement.default,
- :include_rdoc => false, :include_ri => true, :overwrite => false
+ version: Gem::Requirement.default,
+ include_rdoc: false, include_ri: true, overwrite: false
add_option("--all",
"Generate RDoc/RI documentation for all",
@@ -84,14 +84,7 @@ Use --overwrite to force rebuilding of documentation.
FileUtils.rm_rf File.join(spec.doc_dir, "rdoc")
end
- begin
- doc.generate
- rescue Errno::ENOENT => e
- match = / - /.match(e.message)
- alert_error "Unable to document #{spec.full_name}, " \
- " #{match.post_match} is missing, skipping"
- terminate_interaction 1 if specs.length == 1
- end
+ doc.generate
end
end
end
diff --git a/lib/rubygems/commands/rebuild_command.rb b/lib/rubygems/commands/rebuild_command.rb
new file mode 100644
index 0000000000..77a474ef1d
--- /dev/null
+++ b/lib/rubygems/commands/rebuild_command.rb
@@ -0,0 +1,262 @@
+# frozen_string_literal: true
+
+require "date"
+require "digest"
+require "fileutils"
+require "tmpdir"
+require_relative "../gemspec_helpers"
+require_relative "../package"
+
+class Gem::Commands::RebuildCommand < Gem::Command
+ include Gem::GemspecHelpers
+
+ def initialize
+ super "rebuild", "Attempt to reproduce a build of a gem."
+
+ add_option "--diff", "If the files don't match, compare them using diffoscope." do |_value, options|
+ options[:diff] = true
+ end
+
+ add_option "--force", "Skip validation of the spec." do |_value, options|
+ options[:force] = true
+ end
+
+ add_option "--strict", "Consider warnings as errors when validating the spec." do |_value, options|
+ options[:strict] = true
+ end
+
+ add_option "--source GEM_SOURCE", "Specify the source to download the gem from." do |value, options|
+ options[:source] = value
+ end
+
+ add_option "--original GEM_FILE", "Specify a local file to compare against (instead of downloading it)." do |value, options|
+ options[:original_gem_file] = value
+ end
+
+ add_option "--gemspec GEMSPEC_FILE", "Specify the name of the gemspec file." do |value, options|
+ options[:gemspec_file] = value
+ end
+
+ add_option "-C PATH", "Run as if gem build was started in <PATH> instead of the current working directory." do |value, options|
+ options[:build_path] = value
+ end
+ end
+
+ def arguments # :nodoc:
+ "GEM_NAME gem name on gem server\n" \
+ "GEM_VERSION gem version you are attempting to rebuild"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The rebuild command allows you to (attempt to) reproduce a build of a gem
+from a ruby gemspec.
+
+This command assumes the gemspec can be built with the `gem build` command.
+If you use any of `gem build`, `rake build`, or`rake release` in the
+build/release process for a gem, it is a potential candidate.
+
+You will need to match the RubyGems version used, since this is included in
+the Gem metadata.
+
+If the gem includes lockfiles (e.g. Gemfile.lock) and similar, it will
+require more effort to reproduce a build. For example, it might require
+more precisely matched versions of Ruby and/or Bundler to be used.
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEM_NAME GEM_VERSION"
+ end
+
+ def execute
+ gem_name, gem_version = get_gem_name_and_version
+
+ old_dir, new_dir = prep_dirs
+
+ gem_filename = "#{gem_name}-#{gem_version}.gem"
+ old_file = File.join(old_dir, gem_filename)
+ new_file = File.join(new_dir, gem_filename)
+
+ if options[:original_gem_file]
+ FileUtils.copy_file(options[:original_gem_file], old_file)
+ else
+ download_gem(gem_name, gem_version, old_file)
+ end
+
+ rg_version = rubygems_version(old_file)
+ unless rg_version == Gem::VERSION
+ alert_error <<-EOF
+You need to use the same RubyGems version #{gem_name} v#{gem_version} was built with.
+
+#{gem_name} v#{gem_version} was built using RubyGems v#{rg_version}.
+Gem files include the version of RubyGems used to build them.
+This means in order to reproduce #{gem_filename}, you must also use RubyGems v#{rg_version}.
+
+You're using RubyGems v#{Gem::VERSION}.
+
+Please install RubyGems v#{rg_version} and try again.
+ EOF
+ terminate_interaction 1
+ end
+
+ source_date_epoch = get_timestamp(old_file).to_s
+
+ if build_path = options[:build_path]
+ Dir.chdir(build_path) { build_gem(gem_name, source_date_epoch, new_file) }
+ else
+ build_gem(gem_name, source_date_epoch, new_file)
+ end
+
+ compare(source_date_epoch, old_file, new_file)
+ end
+
+ private
+
+ def sha256(file)
+ Digest::SHA256.hexdigest(Gem.read_binary(file))
+ end
+
+ def get_timestamp(file)
+ mtime = nil
+ File.open(file, Gem.binary_mode) do |f|
+ Gem::Package::TarReader.new(f) do |tar|
+ mtime = tar.seek("metadata.gz") {|tf| tf.header.mtime }
+ end
+ end
+
+ mtime
+ end
+
+ def compare(source_date_epoch, old_file, new_file)
+ date = Time.at(source_date_epoch.to_i).strftime("%F %T %Z")
+
+ old_hash = sha256(old_file)
+ new_hash = sha256(new_file)
+
+ say
+ say "Built at: #{date} (#{source_date_epoch})"
+ say "Original build saved to: #{old_file}"
+ say "Reproduced build saved to: #{new_file}"
+ say "Working directory: #{options[:build_path] || Dir.pwd}"
+ say
+ say "Hash comparison:"
+ say " #{old_hash}\t#{old_file}"
+ say " #{new_hash}\t#{new_file}"
+ say
+
+ if old_hash == new_hash
+ say "SUCCESS - original and rebuild hashes matched"
+ else
+ say "FAILURE - original and rebuild hashes did not match"
+ say
+
+ if options[:diff]
+ if system("diffoscope", old_file, new_file).nil?
+ alert_error "error: could not find `diffoscope` executable"
+ end
+ else
+ say "Pass --diff for more details (requires diffoscope to be installed)."
+ end
+
+ terminate_interaction 1
+ end
+ end
+
+ def prep_dirs
+ rebuild_dir = Dir.mktmpdir("gem_rebuild")
+ old_dir = File.join(rebuild_dir, "old")
+ new_dir = File.join(rebuild_dir, "new")
+
+ FileUtils.mkdir_p(old_dir)
+ FileUtils.mkdir_p(new_dir)
+
+ [old_dir, new_dir]
+ end
+
+ def get_gem_name_and_version
+ args = options[:args] || []
+ if args.length == 2
+ gem_name, gem_version = args
+ elsif args.length > 2
+ raise Gem::CommandLineError, "Too many arguments"
+ else
+ raise Gem::CommandLineError, "Expected GEM_NAME and GEM_VERSION arguments (gem rebuild GEM_NAME GEM_VERSION)"
+ end
+
+ [gem_name, gem_version]
+ end
+
+ def build_gem(gem_name, source_date_epoch, output_file)
+ gemspec = options[:gemspec_file] || find_gemspec("#{gem_name}.gemspec")
+
+ if gemspec
+ build_package(gemspec, source_date_epoch, output_file)
+ else
+ alert_error error_message(gem_name)
+ terminate_interaction(1)
+ end
+ end
+
+ def build_package(gemspec, source_date_epoch, output_file)
+ with_source_date_epoch(source_date_epoch) do
+ spec = Gem::Specification.load(gemspec)
+ if spec
+ Gem::Package.build(
+ spec,
+ options[:force],
+ options[:strict],
+ output_file
+ )
+ else
+ alert_error "Error loading gemspec. Aborting."
+ terminate_interaction 1
+ end
+ end
+ end
+
+ def with_source_date_epoch(source_date_epoch)
+ old_sde = ENV["SOURCE_DATE_EPOCH"]
+ ENV["SOURCE_DATE_EPOCH"] = source_date_epoch.to_s
+
+ yield
+ ensure
+ ENV["SOURCE_DATE_EPOCH"] = old_sde
+ end
+
+ def error_message(gem_name)
+ if gem_name
+ "Couldn't find a gemspec file matching '#{gem_name}' in #{Dir.pwd}"
+ else
+ "Couldn't find a gemspec file in #{Dir.pwd}"
+ end
+ end
+
+ def download_gem(gem_name, gem_version, old_file)
+ # This code was based loosely off the `gem fetch` command.
+ version = "= #{gem_version}"
+ dep = Gem::Dependency.new gem_name, version
+
+ specs_and_sources, errors =
+ Gem::SpecFetcher.fetcher.spec_for_dependency dep
+
+ # There should never be more than one item in specs_and_sources,
+ # since we search for an exact version.
+ spec, source = specs_and_sources[0]
+
+ if spec.nil?
+ show_lookup_failure gem_name, version, errors, options[:domain]
+ terminate_interaction 1
+ end
+
+ download_path = source.download spec
+
+ FileUtils.move(download_path, old_file)
+
+ say "Downloaded #{gem_name} version #{gem_version} as #{old_file}."
+ end
+
+ def rubygems_version(gem_file)
+ Gem::Package.new(gem_file).spec.rubygems_version
+ end
+end
diff --git a/lib/rubygems/commands/search_command.rb b/lib/rubygems/commands/search_command.rb
index c7469e1fa8..50e161ac9b 100644
--- a/lib/rubygems/commands/search_command.rb
+++ b/lib/rubygems/commands/search_command.rb
@@ -8,8 +8,8 @@ class Gem::Commands::SearchCommand < Gem::Command
def initialize
super "search", "Display remote gems whose name matches REGEXP",
- :domain => :remote, :details => false, :versions => true,
- :installed => nil, :version => Gem::Requirement.default
+ domain: :remote, details: false, versions: true,
+ installed: nil, version: Gem::Requirement.default
add_query_options
end
diff --git a/lib/rubygems/commands/setup_command.rb b/lib/rubygems/commands/setup_command.rb
index c35d0f5ccc..3f38074280 100644
--- a/lib/rubygems/commands/setup_command.rb
+++ b/lib/rubygems/commands/setup_command.rb
@@ -7,19 +7,19 @@ require_relative "../command"
# RubyGems checkout or tarball.
class Gem::Commands::SetupCommand < Gem::Command
- HISTORY_HEADER = %r{^#\s*[\d.a-zA-Z]+\s*/\s*\d{4}-\d{2}-\d{2}\s*$}.freeze
- VERSION_MATCHER = %r{^#\s*([\d.a-zA-Z]+)\s*/\s*\d{4}-\d{2}-\d{2}\s*$}.freeze
+ HISTORY_HEADER = %r{^#\s*[\d.a-zA-Z]+\s*/\s*\d{4}-\d{2}-\d{2}\s*$}
+ VERSION_MATCHER = %r{^#\s*([\d.a-zA-Z]+)\s*/\s*\d{4}-\d{2}-\d{2}\s*$}
ENV_PATHS = %w[/usr/bin/env /bin/env].freeze
def initialize
super "setup", "Install RubyGems",
- :format_executable => false, :document => %w[ri],
- :force => true,
- :site_or_vendor => "sitelibdir",
- :destdir => "", :prefix => "", :previous_version => "",
- :regenerate_binstubs => true,
- :regenerate_plugins => true
+ format_executable: false, document: %w[ri],
+ force: true,
+ site_or_vendor: "sitelibdir",
+ destdir: "", prefix: "", previous_version: "",
+ regenerate_binstubs: true,
+ regenerate_plugins: true
add_option "--previous-version=VERSION",
"Previous version of RubyGems",
@@ -265,7 +265,7 @@ By default, this RubyGems will install gem as:
fp.puts bin.join
end
- install bin_tmp_file, dest_file, :mode => prog_mode
+ install bin_tmp_file, dest_file, mode: prog_mode
bin_file_names << dest_file
ensure
rm bin_tmp_file
@@ -287,7 +287,7 @@ By default, this RubyGems will install gem as:
TEXT
end
- install bin_cmd_file, "#{dest_file}.bat", :mode => prog_mode
+ install bin_cmd_file, "#{dest_file}.bat", mode: prog_mode
ensure
rm bin_cmd_file
end
@@ -369,18 +369,21 @@ By default, this RubyGems will install gem as:
File.dirname(loaded_from)
else
target_specs_dir = File.join(default_dir, "specifications", "default")
- mkdir_p target_specs_dir, :mode => 0o755
+ mkdir_p target_specs_dir, mode: 0o755
target_specs_dir
end
- bundler_spec = Dir.chdir("bundler") { Gem::Specification.load("bundler.gemspec") }
- default_spec_path = File.join(specs_dir, "#{bundler_spec.full_name}.gemspec")
- Gem.write_binary(default_spec_path, bundler_spec.to_ruby)
+ new_bundler_spec = Dir.chdir("bundler") { Gem::Specification.load("bundler.gemspec") }
+ full_name = new_bundler_spec.full_name
+ gemspec_path = "#{full_name}.gemspec"
+
+ default_spec_path = File.join(specs_dir, gemspec_path)
+ Gem.write_binary(default_spec_path, new_bundler_spec.to_ruby)
bundler_spec = Gem::Specification.load(default_spec_path)
# Remove gemspec that was same version of vendored bundler.
- normal_gemspec = File.join(default_dir, "specifications", "bundler-#{bundler_spec.version}.gemspec")
+ normal_gemspec = File.join(default_dir, "specifications", gemspec_path)
if File.file? normal_gemspec
File.delete normal_gemspec
end
@@ -388,20 +391,14 @@ By default, this RubyGems will install gem as:
# Remove gem files that were same version of vendored bundler.
if File.directory? bundler_spec.gems_dir
Dir.entries(bundler_spec.gems_dir).
- select {|default_gem| File.basename(default_gem) == "bundler-#{bundler_spec.version}" }.
+ select {|default_gem| File.basename(default_gem) == full_name }.
each {|default_gem| rm_r File.join(bundler_spec.gems_dir, default_gem) }
end
- bundler_bin_dir = bundler_spec.bin_dir
- mkdir_p bundler_bin_dir, :mode => 0o755
- bundler_spec.executables.each do |e|
- cp File.join("bundler", bundler_spec.bindir, e), File.join(bundler_bin_dir, e)
- end
-
require_relative "../installer"
Dir.chdir("bundler") do
- built_gem = Gem::Package.build(bundler_spec)
+ built_gem = Gem::Package.build(new_bundler_spec)
begin
Gem::Installer.at(
built_gem,
@@ -418,9 +415,9 @@ By default, this RubyGems will install gem as:
end
end
- bundler_spec.executables.each {|executable| bin_file_names << target_bin_path(bin_dir, executable) }
+ new_bundler_spec.executables.each {|executable| bin_file_names << target_bin_path(bin_dir, executable) }
- say "Bundler #{bundler_spec.version} installed"
+ say "Bundler #{new_bundler_spec.version} installed"
end
def make_destination_dirs
@@ -430,8 +427,8 @@ By default, this RubyGems will install gem as:
lib_dir, bin_dir = generate_default_dirs
end
- mkdir_p lib_dir, :mode => 0o755
- mkdir_p bin_dir, :mode => 0o755
+ mkdir_p lib_dir, mode: 0o755
+ mkdir_p bin_dir, mode: 0o755
[lib_dir, bin_dir]
end
@@ -576,8 +573,8 @@ abort "#{deprecation_message}"
def uninstall_old_gemcutter
require_relative "../uninstaller"
- ui = Gem::Uninstaller.new("gemcutter", :all => true, :ignore => true,
- :version => "< 0.4")
+ ui = Gem::Uninstaller.new("gemcutter", all: true, ignore: true,
+ version: "< 0.4")
ui.uninstall
rescue Gem::InstallError
end
@@ -639,10 +636,10 @@ abort "#{deprecation_message}"
dest_file = File.join dest_dir, file
dest_dir = File.dirname dest_file
unless File.directory? dest_dir
- mkdir_p dest_dir, :mode => 0o755
+ mkdir_p dest_dir, mode: 0o755
end
- install file, dest_file, :mode => options[:data_mode] || 0o644
+ install file, dest_file, mode: options[:data_mode] || 0o644
end
def remove_file_list(files, dir)
diff --git a/lib/rubygems/commands/sources_command.rb b/lib/rubygems/commands/sources_command.rb
index c9c6ee80ed..976f4a4ea2 100644
--- a/lib/rubygems/commands/sources_command.rb
+++ b/lib/rubygems/commands/sources_command.rb
@@ -59,7 +59,7 @@ class Gem::Commands::SourcesCommand < Gem::Command
say "#{source_uri} added to sources"
end
- rescue URI::Error, ArgumentError
+ rescue Gem::URI::Error, ArgumentError
say "#{source_uri} is not a URI"
terminate_interaction 1
rescue Gem::RemoteFetcher::FetchError => e
@@ -81,7 +81,7 @@ Do you want to add this source?
end
def check_rubygems_https(source_uri) # :nodoc:
- uri = URI source_uri
+ uri = Gem::URI source_uri
if uri.scheme && uri.scheme.casecmp("http").zero? &&
uri.host.casecmp("rubygems.org").zero?
diff --git a/lib/rubygems/commands/specification_command.rb b/lib/rubygems/commands/specification_command.rb
index 938b9507f9..a21ed35be3 100644
--- a/lib/rubygems/commands/specification_command.rb
+++ b/lib/rubygems/commands/specification_command.rb
@@ -13,8 +13,8 @@ class Gem::Commands::SpecificationCommand < Gem::Command
Gem.load_yaml
super "specification", "Display gem specification (in yaml)",
- :domain => :local, :version => Gem::Requirement.default,
- :format => :yaml
+ domain: :local, version: Gem::Requirement.default,
+ format: :yaml
add_version_option("examine")
add_platform_option
diff --git a/lib/rubygems/commands/uninstall_command.rb b/lib/rubygems/commands/uninstall_command.rb
index c66bbe42e1..2a77ec72cf 100644
--- a/lib/rubygems/commands/uninstall_command.rb
+++ b/lib/rubygems/commands/uninstall_command.rb
@@ -15,8 +15,8 @@ class Gem::Commands::UninstallCommand < Gem::Command
def initialize
super "uninstall", "Uninstall gems from the local repository",
- :version => Gem::Requirement.default, :user_install => true,
- :check_dev => false, :vendor => false
+ version: Gem::Requirement.default, user_install: true,
+ check_dev: false, vendor: false
add_option("-a", "--[no-]all",
"Uninstall all matching versions") do |value, options|
@@ -168,10 +168,10 @@ that is a dependency of an existing gem. You can use the
gems_to_uninstall = {}
deps.each do |dep|
- next if gems_to_uninstall[dep.name]
- gems_to_uninstall[dep.name] = true
-
- unless original_gem_version[dep.name] == Gem::Requirement.default
+ if original_gem_version[dep.name] == Gem::Requirement.default
+ next if gems_to_uninstall[dep.name]
+ gems_to_uninstall[dep.name] = true
+ else
options[:version] = dep.version
end
diff --git a/lib/rubygems/commands/unpack_command.rb b/lib/rubygems/commands/unpack_command.rb
index 73eefe153c..c2fc720297 100644
--- a/lib/rubygems/commands/unpack_command.rb
+++ b/lib/rubygems/commands/unpack_command.rb
@@ -21,8 +21,8 @@ class Gem::Commands::UnpackCommand < Gem::Command
require "fileutils"
super "unpack", "Unpack an installed gem to the current directory",
- :version => Gem::Requirement.default,
- :target => Dir.pwd
+ version: Gem::Requirement.default,
+ target: Dir.pwd
add_option("--target=DIR",
"target directory for unpacking") do |value, options|
@@ -143,12 +143,6 @@ command help for an example.
# get_path 'rake', '< 0.1' # nil
# get_path 'rak' # nil (exact name required)
#--
- # TODO: This should be refactored so that it's a general service. I don't
- # think any of our existing classes are the right place though. Just maybe
- # 'Cache'?
- #
- # TODO: It just uses Gem.dir for now. What's an easy way to get the list of
- # source directories?
def get_path(dependency)
return dependency.name if /\.gem$/i.match?(dependency.name)
diff --git a/lib/rubygems/commands/update_command.rb b/lib/rubygems/commands/update_command.rb
index fb27e755bc..8e80d46856 100644
--- a/lib/rubygems/commands/update_command.rb
+++ b/lib/rubygems/commands/update_command.rb
@@ -21,7 +21,7 @@ class Gem::Commands::UpdateCommand < Gem::Command
def initialize
options = {
- :force => false,
+ force: false,
}
options.merge!(install_update_options)
@@ -197,18 +197,17 @@ command to remove old versions.
yield
else
require "tmpdir"
- tmpdir = Dir.mktmpdir
- FileUtils.mv Gem.plugindir, tmpdir
+ Dir.mktmpdir("gem_update") do |tmpdir|
+ FileUtils.mv Gem.plugindir, tmpdir
- status = yield
+ status = yield
- if status
- FileUtils.rm_rf tmpdir
- else
- FileUtils.mv File.join(tmpdir, "plugins"), Gem.plugindir
- end
+ unless status
+ FileUtils.mv File.join(tmpdir, "plugins"), Gem.plugindir
+ end
- status
+ status
+ end
end
end
@@ -244,7 +243,7 @@ command to remove old versions.
@installer = Gem::DependencyInstaller.new update_options
- say "Updating #{name}" unless options[:system] && options[:silent]
+ say "Updating #{name}" unless options[:system]
begin
@installer.install name, Gem::Requirement.new(version)
rescue Gem::InstallError, Gem::DependencyError => e
@@ -282,7 +281,7 @@ command to remove old versions.
check_oldest_rubygems version
installed_gems = Gem::Specification.find_all_by_name "rubygems-update", requirement
- installed_gems = update_gem("rubygems-update", version) if installed_gems.empty? || installed_gems.first.version != version
+ installed_gems = update_gem("rubygems-update", requirement) if installed_gems.empty? || installed_gems.first.version != version
return if installed_gems.empty?
install_rubygems installed_gems.first
@@ -294,9 +293,7 @@ command to remove old versions.
args << "--prefix" << Gem.prefix if Gem.prefix
args << "--no-document" unless options[:document].include?("rdoc") || options[:document].include?("ri")
args << "--no-format-executable" if options[:no_format_executable]
- args << "--previous-version" << Gem::VERSION if
- options[:system] == true ||
- Gem::Version.new(options[:system]) >= Gem::Version.new(2)
+ args << "--previous-version" << Gem::VERSION
args
end
@@ -328,12 +325,8 @@ command to remove old versions.
@oldest_supported_version ||=
if Gem.ruby_version > Gem::Version.new("3.1.a")
Gem::Version.new("3.3.3")
- elsif Gem.ruby_version > Gem::Version.new("3.0.a")
- Gem::Version.new("3.2.3")
- elsif Gem.ruby_version > Gem::Version.new("2.7.a")
- Gem::Version.new("3.1.2")
else
- Gem::Version.new("3.0.1")
+ Gem::Version.new("3.2.3")
end
end
end
diff --git a/lib/rubygems/commands/which_command.rb b/lib/rubygems/commands/which_command.rb
index ec464d9672..5ed4d9d142 100644
--- a/lib/rubygems/commands/which_command.rb
+++ b/lib/rubygems/commands/which_command.rb
@@ -5,7 +5,7 @@ require_relative "../command"
class Gem::Commands::WhichCommand < Gem::Command
def initialize
super "which", "Find the location of a library file you can require",
- :search_gems_first => false, :show_all => false
+ search_gems_first: false, show_all: false
add_option "-a", "--[no-]all", "show all matching files" do |show_all, options|
options[:show_all] = show_all
diff --git a/lib/rubygems/config_file.rb b/lib/rubygems/config_file.rb
index d90ec9f92c..7874ad0dc9 100644
--- a/lib/rubygems/config_file.rb
+++ b/lib/rubygems/config_file.rb
@@ -47,6 +47,8 @@ class Gem::ConfigFile
DEFAULT_CONCURRENT_DOWNLOADS = 8
DEFAULT_CERT_EXPIRATION_LENGTH_DAYS = 365
DEFAULT_IPV4_FALLBACK_ENABLED = false
+ # TODO: Use false as default value for this option in RubyGems 4.0
+ DEFAULT_INSTALL_EXTENSION_IN_LIB = true
##
# For Ruby packagers to set configuration defaults. Set in
@@ -143,6 +145,11 @@ class Gem::ConfigFile
attr_accessor :cert_expiration_length_days
##
+ # Install extensions into lib as well as into the extension directory.
+
+ attr_accessor :install_extension_in_lib
+
+ ##
# == Experimental ==
# Fallback to IPv4 when IPv6 is not reachable or slow (default: false)
@@ -183,12 +190,13 @@ class Gem::ConfigFile
@update_sources = DEFAULT_UPDATE_SOURCES
@concurrent_downloads = DEFAULT_CONCURRENT_DOWNLOADS
@cert_expiration_length_days = DEFAULT_CERT_EXPIRATION_LENGTH_DAYS
+ @install_extension_in_lib = DEFAULT_INSTALL_EXTENSION_IN_LIB
@ipv4_fallback_enabled = ENV["IPV4_FALLBACK_ENABLED"] == "true" || DEFAULT_IPV4_FALLBACK_ENABLED
operating_system_config = Marshal.load Marshal.dump(OPERATING_SYSTEM_DEFAULTS)
platform_config = Marshal.load Marshal.dump(PLATFORM_DEFAULTS)
system_config = load_file SYSTEM_WIDE_CONFIG_FILE
- user_config = load_file config_file_name.dup.tap(&Gem::UNTAINT)
+ user_config = load_file config_file_name
environment_config = (ENV["GEMRC"] || "").
split(File::PATH_SEPARATOR).inject({}) do |result, file|
@@ -202,21 +210,34 @@ class Gem::ConfigFile
@hash = @hash.merge environment_config
end
+ @hash.transform_keys! do |k|
+ # gemhome and gempath are not working with symbol keys
+ if %w[backtrace bulk_threshold verbose update_sources cert_expiration_length_days
+ install_extension_in_lib ipv4_fallback_enabled sources disable_default_gem_server
+ ssl_verify_mode ssl_ca_cert ssl_client_cert].include?(k)
+ k.to_sym
+ else
+ k
+ end
+ end
+
# HACK: these override command-line args, which is bad
@backtrace = @hash[:backtrace] if @hash.key? :backtrace
@bulk_threshold = @hash[:bulk_threshold] if @hash.key? :bulk_threshold
- @home = @hash[:gemhome] if @hash.key? :gemhome
- @path = @hash[:gempath] if @hash.key? :gempath
- @update_sources = @hash[:update_sources] if @hash.key? :update_sources
@verbose = @hash[:verbose] if @hash.key? :verbose
- @disable_default_gem_server = @hash[:disable_default_gem_server] if @hash.key? :disable_default_gem_server
- @sources = @hash[:sources] if @hash.key? :sources
+ @update_sources = @hash[:update_sources] if @hash.key? :update_sources
+ # TODO: We should handle concurrent_downloads same as other options
@cert_expiration_length_days = @hash[:cert_expiration_length_days] if @hash.key? :cert_expiration_length_days
+ @install_extension_in_lib = @hash[:install_extension_in_lib] if @hash.key? :install_extension_in_lib
@ipv4_fallback_enabled = @hash[:ipv4_fallback_enabled] if @hash.key? :ipv4_fallback_enabled
- @ssl_verify_mode = @hash[:ssl_verify_mode] if @hash.key? :ssl_verify_mode
- @ssl_ca_cert = @hash[:ssl_ca_cert] if @hash.key? :ssl_ca_cert
- @ssl_client_cert = @hash[:ssl_client_cert] if @hash.key? :ssl_client_cert
+ @home = @hash[:gemhome] if @hash.key? :gemhome
+ @path = @hash[:gempath] if @hash.key? :gempath
+ @sources = @hash[:sources] if @hash.key? :sources
+ @disable_default_gem_server = @hash[:disable_default_gem_server] if @hash.key? :disable_default_gem_server
+ @ssl_verify_mode = @hash[:ssl_verify_mode] if @hash.key? :ssl_verify_mode
+ @ssl_ca_cert = @hash[:ssl_ca_cert] if @hash.key? :ssl_ca_cert
+ @ssl_client_cert = @hash[:ssl_client_cert] if @hash.key? :ssl_client_cert
@api_keys = nil
@rubygems_api_key = nil
@@ -464,6 +485,9 @@ if you believe they were disclosed to a third party.
yaml_hash[:concurrent_downloads] =
@hash.fetch(:concurrent_downloads, DEFAULT_CONCURRENT_DOWNLOADS)
+ yaml_hash[:install_extension_in_lib] =
+ @hash.fetch(:install_extension_in_lib, DEFAULT_INSTALL_EXTENSION_IN_LIB)
+
yaml_hash[:ssl_verify_mode] =
@hash[:ssl_verify_mode] if @hash.key? :ssl_verify_mode
diff --git a/lib/rubygems/core_ext/kernel_require.rb b/lib/rubygems/core_ext/kernel_require.rb
index e967dfc016..073966b696 100644
--- a/lib/rubygems/core_ext/kernel_require.rb
+++ b/lib/rubygems/core_ext/kernel_require.rb
@@ -36,53 +36,45 @@ module Kernel
def require(path) # :doc:
return gem_original_require(path) unless Gem.discover_gems_on_require
- begin
- RUBYGEMS_ACTIVATION_MONITOR.enter
+ RUBYGEMS_ACTIVATION_MONITOR.synchronize do
+ path = File.path(path)
+
+ # If +path+ belongs to a default gem, we activate it and then go straight
+ # to normal require
+
+ if spec = Gem.find_default_spec(path)
+ name = spec.name
- path = path.to_path if path.respond_to? :to_path
+ next if Gem.loaded_specs[name]
- if spec = Gem.find_unresolved_default_spec(path)
# Ensure -I beats a default gem
resolved_path = begin
rp = nil
load_path_check_index = Gem.load_path_insert_index - Gem.activated_gem_paths
- Gem.suffixes.each do |s|
- $LOAD_PATH[0...load_path_check_index].each do |lp|
- safe_lp = lp.dup.tap(&Gem::UNTAINT)
- begin
- if File.symlink? safe_lp # for backward compatibility
- next
- end
- rescue SecurityError
- RUBYGEMS_ACTIVATION_MONITOR.exit
- raise
+ Gem.suffixes.find do |s|
+ $LOAD_PATH[0...load_path_check_index].find do |lp|
+ if File.symlink? lp # for backward compatibility
+ next
end
- full_path = File.expand_path(File.join(safe_lp, "#{path}#{s}"))
- if File.file?(full_path)
- rp = full_path
- break
- end
+ full_path = File.expand_path(File.join(lp, "#{path}#{s}"))
+ rp = full_path if File.file?(full_path)
end
- break if rp
end
rp
end
- begin
- Kernel.send(:gem, spec.name, Gem::Requirement.default_prerelease)
- rescue StandardError
- RUBYGEMS_ACTIVATION_MONITOR.exit
- raise
- end unless resolved_path
+ Kernel.send(:gem, name, Gem::Requirement.default_prerelease) unless
+ resolved_path
+
+ next
end
# If there are no unresolved deps, then we can use just try
# normal require handle loading a gem from the rescue below.
if Gem::Specification.unresolved_deps.empty?
- RUBYGEMS_ACTIVATION_MONITOR.exit
- return gem_original_require(path)
+ next
end
# If +path+ is for a gem that has already been loaded, don't
@@ -92,8 +84,7 @@ module Kernel
# TODO request access to the C implementation of this to speed up RubyGems
if Gem::Specification.find_active_stub_by_path(path)
- RUBYGEMS_ACTIVATION_MONITOR.exit
- return gem_original_require(path)
+ next
end
# Attempt to find +path+ in any unresolved gems...
@@ -124,7 +115,6 @@ module Kernel
names = found_specs.map(&:name).uniq
if names.size > 1
- RUBYGEMS_ACTIVATION_MONITOR.exit
raise Gem::LoadError, "#{path} found in multiple gems: #{names.join ", "}"
end
@@ -135,26 +125,20 @@ module Kernel
unless valid
le = Gem::LoadError.new "unable to find a version of '#{names.first}' to activate"
le.name = names.first
- RUBYGEMS_ACTIVATION_MONITOR.exit
raise le
end
valid.activate
end
+ end
- RUBYGEMS_ACTIVATION_MONITOR.exit
+ begin
gem_original_require(path)
rescue LoadError => load_error
- if load_error.path == path
- RUBYGEMS_ACTIVATION_MONITOR.enter
+ if load_error.path == path &&
+ RUBYGEMS_ACTIVATION_MONITOR.synchronize { Gem.try_activate(path) }
- begin
- require_again = Gem.try_activate(path)
- ensure
- RUBYGEMS_ACTIVATION_MONITOR.exit
- end
-
- return gem_original_require(path) if require_again
+ return gem_original_require(path)
end
raise load_error
diff --git a/lib/rubygems/defaults.rb b/lib/rubygems/defaults.rb
index 7888e8cee5..1bd208feb9 100644
--- a/lib/rubygems/defaults.rb
+++ b/lib/rubygems/defaults.rb
@@ -24,7 +24,7 @@ module Gem
default_spec_cache_dir = File.join Gem.user_home, ".gem", "specs"
unless File.exist?(default_spec_cache_dir)
- default_spec_cache_dir = File.join Gem.data_home, "gem", "specs"
+ default_spec_cache_dir = File.join Gem.cache_home, "gem", "specs"
end
default_spec_cache_dir
@@ -94,7 +94,7 @@ module Gem
# The home directory for the user.
def self.user_home
- @user_home ||= find_home.tap(&Gem::UNTAINT)
+ @user_home ||= find_home
end
##
@@ -112,7 +112,7 @@ module Gem
# The path to standard location of the user's configuration directory.
def self.config_home
- @config_home ||= (ENV["XDG_CONFIG_HOME"] || File.join(Gem.user_home, ".config"))
+ @config_home ||= ENV["XDG_CONFIG_HOME"] || File.join(Gem.user_home, ".config")
end
##
@@ -131,35 +131,35 @@ module Gem
# The path to standard location of the user's .gemrc file.
def self.config_file
- @config_file ||= find_config_file.tap(&Gem::UNTAINT)
+ @config_file ||= find_config_file
end
##
# The path to standard location of the user's state file.
def self.state_file
- @state_file ||= File.join(Gem.state_home, "gem", "last_update_check").tap(&Gem::UNTAINT)
+ @state_file ||= File.join(Gem.state_home, "gem", "last_update_check")
end
##
# The path to standard location of the user's cache directory.
def self.cache_home
- @cache_home ||= (ENV["XDG_CACHE_HOME"] || File.join(Gem.user_home, ".cache"))
+ @cache_home ||= ENV["XDG_CACHE_HOME"] || File.join(Gem.user_home, ".cache")
end
##
# The path to standard location of the user's data directory.
def self.data_home
- @data_home ||= (ENV["XDG_DATA_HOME"] || File.join(Gem.user_home, ".local", "share"))
+ @data_home ||= ENV["XDG_DATA_HOME"] || File.join(Gem.user_home, ".local", "share")
end
##
# The path to standard location of the user's state directory.
def self.state_home
- @state_home ||= (ENV["XDG_STATE_HOME"] || File.join(Gem.user_home, ".local", "state"))
+ @state_home ||= ENV["XDG_STATE_HOME"] || File.join(Gem.user_home, ".local", "state")
end
##
@@ -236,10 +236,22 @@ module Gem
end
##
+ # Enables automatic installation into user directory
+
+ def self.default_user_install # :nodoc:
+ if !ENV.key?("GEM_HOME") && (File.exist?(Gem.dir) && !File.writable?(Gem.dir))
+ Gem.ui.say "Defaulting to user installation because default installation directory (#{Gem.dir}) is not writable."
+ return true
+ end
+
+ false
+ end
+
+ ##
# Install extensions into lib as well as into the extension directory.
def self.install_extension_in_lib # :nodoc:
- true
+ Gem.configuration.install_extension_in_lib
end
##
diff --git a/lib/rubygems/dependency.rb b/lib/rubygems/dependency.rb
index 00eff2dfe7..d1bf074441 100644
--- a/lib/rubygems/dependency.rb
+++ b/lib/rubygems/dependency.rb
@@ -328,9 +328,9 @@ class Gem::Dependency
return active if active
unless prerelease?
- # Move prereleases to the end of the list for >= 0 requirements
+ # Consider prereleases only as a fallback
pre, matches = matches.partition {|spec| spec.version.prerelease? }
- matches += pre if requirement == Gem::Requirement.default
+ matches = pre if matches.empty?
end
matches.first
diff --git a/lib/rubygems/dependency_installer.rb b/lib/rubygems/dependency_installer.rb
index 56a9309a7e..b119dca1cf 100644
--- a/lib/rubygems/dependency_installer.rb
+++ b/lib/rubygems/dependency_installer.rb
@@ -17,18 +17,18 @@ class Gem::DependencyInstaller
extend Gem::Deprecate
DEFAULT_OPTIONS = { # :nodoc:
- :env_shebang => false,
- :document => %w[ri],
- :domain => :both, # HACK: dup
- :force => false,
- :format_executable => false, # HACK: dup
- :ignore_dependencies => false,
- :prerelease => false,
- :security_policy => nil, # HACK: NoSecurity requires OpenSSL. AlmostNo? Low?
- :wrappers => true,
- :build_args => nil,
- :build_docs_in_background => false,
- :install_as_default => false,
+ env_shebang: false,
+ document: %w[ri],
+ domain: :both, # HACK: dup
+ force: false,
+ format_executable: false, # HACK: dup
+ ignore_dependencies: false,
+ prerelease: false,
+ security_policy: nil, # HACK: NoSecurity requires OpenSSL. AlmostNo? Low?
+ wrappers: true,
+ build_args: nil,
+ build_docs_in_background: false,
+ install_as_default: false,
}.freeze
##
@@ -228,22 +228,22 @@ class Gem::DependencyInstaller
@installed_gems = []
options = {
- :bin_dir => @bin_dir,
- :build_args => @build_args,
- :document => @document,
- :env_shebang => @env_shebang,
- :force => @force,
- :format_executable => @format_executable,
- :ignore_dependencies => @ignore_dependencies,
- :prerelease => @prerelease,
- :security_policy => @security_policy,
- :user_install => @user_install,
- :wrappers => @wrappers,
- :build_root => @build_root,
- :install_as_default => @install_as_default,
- :dir_mode => @dir_mode,
- :data_mode => @data_mode,
- :prog_mode => @prog_mode,
+ bin_dir: @bin_dir,
+ build_args: @build_args,
+ document: @document,
+ env_shebang: @env_shebang,
+ force: @force,
+ format_executable: @format_executable,
+ ignore_dependencies: @ignore_dependencies,
+ prerelease: @prerelease,
+ security_policy: @security_policy,
+ user_install: @user_install,
+ wrappers: @wrappers,
+ build_root: @build_root,
+ install_as_default: @install_as_default,
+ dir_mode: @dir_mode,
+ data_mode: @data_mode,
+ prog_mode: @prog_mode,
}
options[:install_dir] = @install_dir if @only_install_dir
diff --git a/lib/rubygems/dependency_list.rb b/lib/rubygems/dependency_list.rb
index 30098ff0b5..ad5e59e8c1 100644
--- a/lib/rubygems/dependency_list.rb
+++ b/lib/rubygems/dependency_list.rb
@@ -6,7 +6,7 @@
# See LICENSE.txt for permissions.
#++
-require_relative "tsort"
+require_relative "vendored_tsort"
require_relative "deprecate"
##
diff --git a/lib/rubygems/deprecate.rb b/lib/rubygems/deprecate.rb
index 58a6c5b7dc..7d24f9cbfc 100644
--- a/lib/rubygems/deprecate.rb
+++ b/lib/rubygems/deprecate.rb
@@ -69,99 +69,101 @@
# end
# end
-module Gem::Deprecate
- def self.skip # :nodoc:
- @skip ||= false
- end
+module Gem
+ module Deprecate
+ def self.skip # :nodoc:
+ @skip ||= false
+ end
- def self.skip=(v) # :nodoc:
- @skip = v
- end
+ def self.skip=(v) # :nodoc:
+ @skip = v
+ end
- ##
- # Temporarily turn off warnings. Intended for tests only.
+ ##
+ # Temporarily turn off warnings. Intended for tests only.
- def skip_during
- original = Gem::Deprecate.skip
- Gem::Deprecate.skip = true
- yield
- ensure
- Gem::Deprecate.skip = original
- end
+ def skip_during
+ original = Gem::Deprecate.skip
+ Gem::Deprecate.skip = true
+ yield
+ ensure
+ Gem::Deprecate.skip = original
+ end
- def self.next_rubygems_major_version # :nodoc:
- Gem::Version.new(Gem.rubygems_version.segments.first).bump
- end
+ def self.next_rubygems_major_version # :nodoc:
+ Gem::Version.new(Gem.rubygems_version.segments.first).bump
+ end
- ##
- # Simple deprecation method that deprecates +name+ by wrapping it up
- # in a dummy method. It warns on each call to the dummy method
- # telling the user of +repl+ (unless +repl+ is :none) and the
- # year/month that it is planned to go away.
+ ##
+ # Simple deprecation method that deprecates +name+ by wrapping it up
+ # in a dummy method. It warns on each call to the dummy method
+ # telling the user of +repl+ (unless +repl+ is :none) and the
+ # year/month that it is planned to go away.
- def deprecate(name, repl, year, month)
- class_eval do
- old = "_deprecated_#{name}"
- alias_method old, name
- define_method name do |*args, &block|
- klass = is_a? Module
- target = klass ? "#{self}." : "#{self.class}#"
- msg = [
- "NOTE: #{target}#{name} is deprecated",
- repl == :none ? " with no replacement" : "; use #{repl} instead",
- format(". It will be removed on or after %4d-%02d.", year, month),
- "\n#{target}#{name} called from #{Gem.location_of_caller.join(":")}",
- ]
- warn "#{msg.join}." unless Gem::Deprecate.skip
- send old, *args, &block
+ def deprecate(name, repl, year, month)
+ class_eval do
+ old = "_deprecated_#{name}"
+ alias_method old, name
+ define_method name do |*args, &block|
+ klass = is_a? Module
+ target = klass ? "#{self}." : "#{self.class}#"
+ msg = [
+ "NOTE: #{target}#{name} is deprecated",
+ repl == :none ? " with no replacement" : "; use #{repl} instead",
+ format(". It will be removed on or after %4d-%02d.", year, month),
+ "\n#{target}#{name} called from #{Gem.location_of_caller.join(":")}",
+ ]
+ warn "#{msg.join}." unless Gem::Deprecate.skip
+ send old, *args, &block
+ end
+ ruby2_keywords name if respond_to?(:ruby2_keywords, true)
end
- ruby2_keywords name if respond_to?(:ruby2_keywords, true)
end
- end
- ##
- # Simple deprecation method that deprecates +name+ by wrapping it up
- # in a dummy method. It warns on each call to the dummy method
- # telling the user of +repl+ (unless +repl+ is :none) and the
- # Rubygems version that it is planned to go away.
+ ##
+ # Simple deprecation method that deprecates +name+ by wrapping it up
+ # in a dummy method. It warns on each call to the dummy method
+ # telling the user of +repl+ (unless +repl+ is :none) and the
+ # Rubygems version that it is planned to go away.
- def rubygems_deprecate(name, replacement=:none)
- class_eval do
- old = "_deprecated_#{name}"
- alias_method old, name
- define_method name do |*args, &block|
- klass = is_a? Module
- target = klass ? "#{self}." : "#{self.class}#"
- msg = [
- "NOTE: #{target}#{name} is deprecated",
- replacement == :none ? " with no replacement" : "; use #{replacement} instead",
- ". It will be removed in Rubygems #{Gem::Deprecate.next_rubygems_major_version}",
- "\n#{target}#{name} called from #{Gem.location_of_caller.join(":")}",
- ]
- warn "#{msg.join}." unless Gem::Deprecate.skip
- send old, *args, &block
+ def rubygems_deprecate(name, replacement=:none)
+ class_eval do
+ old = "_deprecated_#{name}"
+ alias_method old, name
+ define_method name do |*args, &block|
+ klass = is_a? Module
+ target = klass ? "#{self}." : "#{self.class}#"
+ msg = [
+ "NOTE: #{target}#{name} is deprecated",
+ replacement == :none ? " with no replacement" : "; use #{replacement} instead",
+ ". It will be removed in Rubygems #{Gem::Deprecate.next_rubygems_major_version}",
+ "\n#{target}#{name} called from #{Gem.location_of_caller.join(":")}",
+ ]
+ warn "#{msg.join}." unless Gem::Deprecate.skip
+ send old, *args, &block
+ end
+ ruby2_keywords name if respond_to?(:ruby2_keywords, true)
end
- ruby2_keywords name if respond_to?(:ruby2_keywords, true)
end
- end
- # Deprecation method to deprecate Rubygems commands
- def rubygems_deprecate_command(version = Gem::Deprecate.next_rubygems_major_version)
- class_eval do
- define_method "deprecated?" do
- true
- end
+ # Deprecation method to deprecate Rubygems commands
+ def rubygems_deprecate_command(version = Gem::Deprecate.next_rubygems_major_version)
+ class_eval do
+ define_method "deprecated?" do
+ true
+ end
- define_method "deprecation_warning" do
- msg = [
- "#{command} command is deprecated",
- ". It will be removed in Rubygems #{version}.\n",
- ]
+ define_method "deprecation_warning" do
+ msg = [
+ "#{command} command is deprecated",
+ ". It will be removed in Rubygems #{version}.\n",
+ ]
- alert_warning msg.join.to_s unless Gem::Deprecate.skip
+ alert_warning msg.join.to_s unless Gem::Deprecate.skip
+ end
end
end
- end
- module_function :rubygems_deprecate, :rubygems_deprecate_command, :skip_during
+ module_function :rubygems_deprecate, :rubygems_deprecate_command, :skip_during
+ end
end
diff --git a/lib/rubygems/exceptions.rb b/lib/rubygems/exceptions.rb
index 65caaab8b1..0308b4687f 100644
--- a/lib/rubygems/exceptions.rb
+++ b/lib/rubygems/exceptions.rb
@@ -292,9 +292,3 @@ class Gem::UnsatisfiableDependencyError < Gem::DependencyError
@dependency.requirement
end
end
-
-##
-# Backwards compatible typo'd exception class for early RubyGems 2.0.x
-
-Gem::UnsatisfiableDepedencyError = Gem::UnsatisfiableDependencyError # :nodoc:
-Gem.deprecate_constant :UnsatisfiableDepedencyError
diff --git a/lib/rubygems/ext/builder.rb b/lib/rubygems/ext/builder.rb
index c39afc9c9d..be1ba3031c 100644
--- a/lib/rubygems/ext/builder.rb
+++ b/lib/rubygems/ext/builder.rb
@@ -7,6 +7,7 @@
#++
require_relative "../user_interaction"
+require_relative "../shellwords"
class Gem::Ext::Builder
include Gem::UserInteraction
@@ -55,9 +56,8 @@ class Gem::Ext::Builder
end
def self.ruby
- require "shellwords"
# Gem.ruby is quoted if it contains whitespace
- cmd = Gem.ruby.shellsplit
+ cmd = Shellwords.split(Gem.ruby)
# This load_path is only needed when running rubygems test without a proper installation.
# Prepending it in a normal installation will cause problem with order of $LOAD_PATH.
@@ -82,20 +82,26 @@ class Gem::Ext::Builder
p(command)
end
results << "current directory: #{dir}"
- require "shellwords"
- results << command.shelljoin
+ results << Shellwords.join(command)
require "open3"
# Set $SOURCE_DATE_EPOCH for the subprocess.
build_env = { "SOURCE_DATE_EPOCH" => Gem.source_date_epoch_string }.merge(env)
output, status = begin
- Open3.capture2e(build_env, *command, :chdir => dir)
+ Open3.popen2e(build_env, *command, chdir: dir) do |_stdin, stdouterr, wait_thread|
+ output = String.new
+ while line = stdouterr.gets
+ output << line
+ if verbose
+ print line
+ end
+ end
+ [output, wait_thread.value]
+ end
rescue StandardError => error
raise Gem::InstallError, "#{command_name || class_name} failed#{error.message}"
end
- if verbose
- puts output
- else
+ unless verbose
results << output
end
ensure
diff --git a/lib/rubygems/ext/cargo_builder.rb b/lib/rubygems/ext/cargo_builder.rb
index fcead1577b..86a0e73f28 100644
--- a/lib/rubygems/ext/cargo_builder.rb
+++ b/lib/rubygems/ext/cargo_builder.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require_relative "../shellwords"
+
# This class is used by rubygems to build Rust extensions. It is a thin-wrapper
# over the `cargo rustc` command which takes care of building Rust code in a way
# that Ruby can use.
@@ -45,7 +47,6 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
nesting = extension_nesting(extension)
- # TODO: remove in RubyGems 4
if Gem.install_extension_in_lib && lib_dir
nested_lib_dir = File.join(lib_dir, nesting)
FileUtils.mkdir_p nested_lib_dir
@@ -73,8 +74,6 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
end
def cargo_command(cargo_toml, dest_path, args = [], crate_name = nil)
- require "shellwords"
-
cmd = []
cmd += [cargo, "rustc"]
cmd += ["--crate-type", "cdylib"]
@@ -198,7 +197,7 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
output, status =
begin
- Open3.capture2e(cargo, "metadata", "--no-deps", "--format-version", "1", :chdir => cargo_dir)
+ Open3.capture2e(cargo, "metadata", "--no-deps", "--format-version", "1", chdir: cargo_dir)
rescue StandardError => error
raise Gem::InstallError, "cargo metadata failed #{error.message}"
end
@@ -293,7 +292,7 @@ EOF
case var_name
# On windows, it is assumed that mkmf has setup an exports file for the
- # extension, so we have to to create one ourselves.
+ # extension, so we have to create one ourselves.
when "DEFFILE"
write_deffile(dest_dir, crate_name)
else
@@ -313,7 +312,7 @@ EOF
deffile_path
end
- # We have to basically reimplement RbConfig::CONFIG['SOEXT'] here to support
+ # We have to basically reimplement <code>RbConfig::CONFIG['SOEXT']</code> here to support
# Ruby < 2.5
#
# @see https://github.com/ruby/ruby/blob/c87c027f18c005460746a74c07cd80ee355b16e4/configure.ac#L3185
diff --git a/lib/rubygems/ext/ext_conf_builder.rb b/lib/rubygems/ext/ext_conf_builder.rb
index 7efd4a06b7..fb68a7a8cc 100644
--- a/lib/rubygems/ext/ext_conf_builder.rb
+++ b/lib/rubygems/ext/ext_conf_builder.rb
@@ -43,12 +43,11 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
full_tmp_dest = File.join(extension_dir, tmp_dest_relative)
- # TODO: remove in RubyGems 4
if Gem.install_extension_in_lib && lib_dir
FileUtils.mkdir_p lib_dir
entries = Dir.entries(full_tmp_dest) - %w[. ..]
entries = entries.map {|entry| File.join full_tmp_dest, entry }
- FileUtils.cp_r entries, lib_dir, :remove_destination => true
+ FileUtils.cp_r entries, lib_dir, remove_destination: true
end
FileUtils::Entry_.new(full_tmp_dest).traverse do |ent|
diff --git a/lib/rubygems/ext/rake_builder.rb b/lib/rubygems/ext/rake_builder.rb
index 2b9a23a23f..0171807b39 100644
--- a/lib/rubygems/ext/rake_builder.rb
+++ b/lib/rubygems/ext/rake_builder.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require_relative "../shellwords"
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
@@ -15,8 +17,7 @@ class Gem::Ext::RakeBuilder < Gem::Ext::Builder
rake = ENV["rake"]
if rake
- require "shellwords"
- rake = rake.shellsplit
+ rake = Shellwords.split(rake)
else
begin
rake = ruby << "-rrubygems" << Gem.bin_path("rake", "rake")
diff --git a/lib/rubygems/gemcutter_utilities.rb b/lib/rubygems/gemcutter_utilities.rb
index 15e61440e3..a8361b7ff1 100644
--- a/lib/rubygems/gemcutter_utilities.rb
+++ b/lib/rubygems/gemcutter_utilities.rb
@@ -2,14 +2,16 @@
require_relative "remote_fetcher"
require_relative "text"
-require_relative "webauthn_listener"
+require_relative "gemcutter_utilities/webauthn_listener"
+require_relative "gemcutter_utilities/webauthn_poller"
##
# Utility methods for using the RubyGems API.
module Gem::GemcutterUtilities
ERROR_CODE = 1
- API_SCOPES = [:index_rubygems, :push_rubygem, :yank_rubygem, :add_owner, :remove_owner, :access_webhooks, :show_dashboard].freeze
+ API_SCOPES = [:index_rubygems, :push_rubygem, :yank_rubygem, :add_owner, :remove_owner, :access_webhooks].freeze
+ EXCLUSIVELY_API_SCOPES = [:show_dashboard].freeze
include Gem::Text
@@ -83,7 +85,7 @@ module Gem::GemcutterUtilities
# If +allowed_push_host+ metadata is present, then it will only allow that host.
def rubygems_api_request(method, path, host = nil, allowed_push_host = nil, scope: nil, credentials: {}, &block)
- require "net/http"
+ require_relative "vendored_net_http"
self.host = host if host
unless self.host
@@ -92,8 +94,8 @@ module Gem::GemcutterUtilities
end
if allowed_push_host
- allowed_host_uri = URI.parse(allowed_push_host)
- host_uri = URI.parse(self.host)
+ allowed_host_uri = Gem::URI.parse(allowed_push_host)
+ host_uri = Gem::URI.parse(self.host)
unless (host_uri.scheme == allowed_host_uri.scheme) && (host_uri.host == allowed_host_uri.host)
alert_error "#{self.host.inspect} is not allowed by the gemspec, which only allows #{allowed_push_host.inspect}"
@@ -101,7 +103,7 @@ module Gem::GemcutterUtilities
end
end
- uri = URI.parse "#{self.host}/#{path}"
+ uri = Gem::URI.parse "#{self.host}/#{path}"
response = request_with_otp(method, uri, &block)
if mfa_unauthorized?(response)
@@ -118,7 +120,7 @@ module Gem::GemcutterUtilities
end
def mfa_unauthorized?(response)
- response.is_a?(Net::HTTPUnauthorized) && response.body.start_with?("You have enabled multifactor authentication")
+ response.is_a?(Gem::Net::HTTPUnauthorized) && response.body.start_with?("You have enabled multifactor authentication")
end
def update_scope(scope)
@@ -128,14 +130,14 @@ module Gem::GemcutterUtilities
say "The existing key doesn't have access of #{scope} on #{pretty_host}. Please sign in to update access."
- email = ask " Email: "
- password = ask_for_password "Password: "
+ identifier = ask "Username/email: "
+ password = ask_for_password " Password: "
response = rubygems_api_request(:put, "api/v1/api_key",
sign_in_host, scope: scope) do |request|
- request.basic_auth email, password
+ request.basic_auth identifier, password
request["OTP"] = otp if otp
- request.body = URI.encode_www_form({ :api_key => api_key }.merge(update_scope_params))
+ request.body = Gem::URI.encode_www_form({ api_key: api_key }.merge(update_scope_params))
end
with_response response do |_resp|
@@ -157,25 +159,25 @@ module Gem::GemcutterUtilities
say "Don't have an account yet? " \
"Create one at #{sign_in_host}/sign_up"
- email = ask " Email: "
- password = ask_for_password "Password: "
+ identifier = ask "Username/email: "
+ password = ask_for_password " Password: "
say "\n"
key_name = get_key_name(scope)
scope_params = get_scope_params(scope)
- profile = get_user_profile(email, password)
+ profile = get_user_profile(identifier, password)
mfa_params = get_mfa_params(profile)
all_params = scope_params.merge(mfa_params)
warning = profile["warning"]
- credentials = { email: email, password: password }
+ credentials = { identifier: identifier, password: password }
say "#{warning}\n" if warning
response = rubygems_api_request(:post, "api/v1/api_key",
sign_in_host, credentials: credentials, scope: scope) do |request|
- request.basic_auth email, password
+ request.basic_auth identifier, password
request["OTP"] = otp if otp
- request.body = URI.encode_www_form({ name: key_name }.merge(all_params))
+ request.body = Gem::URI.encode_www_form({ name: key_name }.merge(all_params))
end
with_response response do |resp|
@@ -207,13 +209,13 @@ module Gem::GemcutterUtilities
def with_response(response, error_prefix = nil)
case response
- when Net::HTTPSuccess then
+ when Gem::Net::HTTPSuccess then
if block_given?
yield response
else
say clean_text(response.body)
end
- when Net::HTTPPermanentRedirect, Net::HTTPRedirection then
+ when Gem::Net::HTTPPermanentRedirect, Gem::Net::HTTPRedirection then
message = "The request has redirected permanently to #{response["location"]}. Please check your defined push host URL."
message = "#{error_prefix}: #{message}" if error_prefix
@@ -243,7 +245,7 @@ module Gem::GemcutterUtilities
private
def request_with_otp(method, uri, &block)
- request_method = Net::HTTP.const_get method.to_s.capitalize
+ request_method = Gem::Net::HTTP.const_get method.to_s.capitalize
Gem::RemoteFetcher.fetcher.request(uri, request_method) do |req|
req["OTP"] = otp if otp
@@ -253,36 +255,39 @@ module Gem::GemcutterUtilities
def fetch_otp(credentials)
options[:otp] = if webauthn_url = webauthn_verification_url(credentials)
- wait_for_otp(webauthn_url)
+ server = TCPServer.new 0
+ port = server.addr[1].to_s
+
+ url_with_port = "#{webauthn_url}?port=#{port}"
+ say "You have enabled multi-factor authentication. Please visit #{url_with_port} to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin command with the `--otp [your_code]` option."
+
+ threads = [WebauthnListener.listener_thread(host, server), WebauthnPoller.poll_thread(options, host, webauthn_url, credentials)]
+ otp_thread = wait_for_otp_thread(*threads)
+
+ threads.each(&:join)
+
+ if error = otp_thread[:error]
+ alert_error error.message
+ terminate_interaction(1)
+ end
+
+ say "You are verified with a security device. You may close the browser window."
+ otp_thread[:otp]
else
say "You have enabled multi-factor authentication. Please enter OTP code."
ask "Code: "
end
end
- def wait_for_otp(webauthn_url)
- server = TCPServer.new 0
- port = server.addr[1].to_s
-
- thread = Thread.new do
- Thread.current[:otp] = Gem::WebauthnListener.wait_for_otp_code(host, server)
- rescue Gem::WebauthnVerificationError => e
- Thread.current[:error] = e
- end
- thread.abort_on_exception = true
- thread.report_on_exception = false
-
- url_with_port = "#{webauthn_url}?port=#{port}"
- say "You have enabled multi-factor authentication. Please visit #{url_with_port} to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin command with the `--otp [your_code]` option."
-
- thread.join
- if error = thread[:error]
- alert_error error.message
- terminate_interaction(1)
+ def wait_for_otp_thread(*threads)
+ loop do
+ threads.each do |otp_thread|
+ return otp_thread unless otp_thread.alive?
+ end
+ sleep 0.1
end
-
- say "You are verified with a security device. You may close the browser window."
- thread[:otp]
+ ensure
+ threads.each(&:exit)
end
def webauthn_verification_url(credentials)
@@ -290,10 +295,10 @@ module Gem::GemcutterUtilities
if credentials.empty?
request.add_field "Authorization", api_key
else
- request.basic_auth credentials[:email], credentials[:password]
+ request.basic_auth credentials[:identifier], credentials[:password]
end
end
- response.is_a?(Net::HTTPSuccess) ? response.body : nil
+ response.is_a?(Gem::Net::HTTPSuccess) ? response.body : nil
end
def pretty_host(host)
@@ -305,15 +310,31 @@ module Gem::GemcutterUtilities
end
def get_scope_params(scope)
- scope_params = {}
+ scope_params = { index_rubygems: true }
if scope
scope_params = { scope => true }
else
- say "Please select scopes you want to enable for the API key (y/n)"
- API_SCOPES.each do |s|
- selected = ask_yes_no(s.to_s, false)
- scope_params[s] = true if selected
+ say "The default access scope is:"
+ scope_params.each do |k, _v|
+ say " #{k}: y"
+ end
+ say "\n"
+ customise = ask_yes_no("Do you want to customise scopes?", false)
+ if customise
+ EXCLUSIVELY_API_SCOPES.each do |excl_scope|
+ selected = ask_yes_no("#{excl_scope} (exclusive scope, answering yes will not prompt for other scopes)", false)
+ next unless selected
+
+ return { excl_scope => true }
+ end
+
+ scope_params = {}
+
+ API_SCOPES.each do |s|
+ selected = ask_yes_no(s.to_s, false)
+ scope_params[s] = true if selected
+ end
end
say "\n"
end
@@ -325,11 +346,11 @@ module Gem::GemcutterUtilities
host == Gem::DEFAULT_HOST
end
- def get_user_profile(email, password)
+ def get_user_profile(identifier, password)
return {} unless default_host?
response = rubygems_api_request(:get, "api/v1/profile/me.yaml") do |request|
- request.basic_auth email, password
+ request.basic_auth identifier, password
end
with_response response do |resp|
@@ -362,6 +383,6 @@ module Gem::GemcutterUtilities
end
def api_key_forbidden?(response)
- response.is_a?(Net::HTTPForbidden) && response.body.start_with?("The API key doesn't have access")
+ response.is_a?(Gem::Net::HTTPForbidden) && response.body.start_with?("The API key doesn't have access")
end
end
diff --git a/lib/rubygems/gemcutter_utilities/webauthn_listener.rb b/lib/rubygems/gemcutter_utilities/webauthn_listener.rb
new file mode 100644
index 0000000000..abf65efe37
--- /dev/null
+++ b/lib/rubygems/gemcutter_utilities/webauthn_listener.rb
@@ -0,0 +1,105 @@
+# frozen_string_literal: true
+
+require_relative "webauthn_listener/response"
+
+##
+# The WebauthnListener class retrieves an OTP after a user successfully WebAuthns with the Gem host.
+# An instance opens a socket using the TCPServer instance given and listens for a request from the Gem host.
+# The request should be a GET request to the root path and contains the OTP code in the form
+# of a query parameter `code`. The listener will return the code which will be used as the OTP for
+# API requests.
+#
+# Types of responses sent by the listener after receiving a request:
+# - 200 OK: OTP code was successfully retrieved
+# - 204 No Content: If the request was an OPTIONS request
+# - 400 Bad Request: If the request did not contain a query parameter `code`
+# - 404 Not Found: The request was not to the root path
+# - 405 Method Not Allowed: OTP code was not retrieved because the request was not a GET/OPTIONS request
+#
+# Example usage:
+#
+# thread = Gem::WebauthnListener.listener_thread("https://rubygems.example", server)
+# thread.join
+# otp = thread[:otp]
+# error = thread[:error]
+#
+
+module Gem::GemcutterUtilities
+ class WebauthnListener
+ attr_reader :host
+
+ def initialize(host)
+ @host = host
+ end
+
+ def self.listener_thread(host, server)
+ Thread.new do
+ thread = Thread.current
+ thread.abort_on_exception = true
+ thread.report_on_exception = false
+ thread[:otp] = new(host).wait_for_otp_code(server)
+ rescue Gem::WebauthnVerificationError => e
+ thread[:error] = e
+ ensure
+ server.close
+ end
+ end
+
+ def wait_for_otp_code(server)
+ loop do
+ socket = server.accept
+ request_line = socket.gets
+
+ method, req_uri, _protocol = request_line.split(" ")
+ req_uri = Gem::URI.parse(req_uri)
+
+ responder = SocketResponder.new(socket)
+
+ unless root_path?(req_uri)
+ responder.send(NotFoundResponse.for(host))
+ raise Gem::WebauthnVerificationError, "Page at #{req_uri.path} not found."
+ end
+
+ case method.upcase
+ when "OPTIONS"
+ responder.send(NoContentResponse.for(host))
+ next # will be GET
+ when "GET"
+ if otp = parse_otp_from_uri(req_uri)
+ responder.send(OkResponse.for(host))
+ return otp
+ end
+ responder.send(BadRequestResponse.for(host))
+ raise Gem::WebauthnVerificationError, "Did not receive OTP from #{host}."
+ else
+ responder.send(MethodNotAllowedResponse.for(host))
+ raise Gem::WebauthnVerificationError, "Invalid HTTP method #{method.upcase} received."
+ end
+ end
+ end
+
+ private
+
+ def root_path?(uri)
+ uri.path == "/"
+ end
+
+ def parse_otp_from_uri(uri)
+ require "cgi"
+
+ return if uri.query.nil?
+ CGI.parse(uri.query).dig("code", 0)
+ end
+
+ class SocketResponder
+ def initialize(socket)
+ @socket = socket
+ end
+
+ def send(response)
+ @socket.print response.to_s
+ @socket.close
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/gemcutter_utilities/webauthn_listener/response.rb b/lib/rubygems/gemcutter_utilities/webauthn_listener/response.rb
new file mode 100644
index 0000000000..17baa64fff
--- /dev/null
+++ b/lib/rubygems/gemcutter_utilities/webauthn_listener/response.rb
@@ -0,0 +1,163 @@
+# frozen_string_literal: true
+
+##
+# The WebauthnListener Response class is used by the WebauthnListener to create
+# responses to be sent to the Gem host. It creates a Gem::Net::HTTPResponse instance
+# when initialized and can be converted to the appropriate format to be sent by a socket using `to_s`.
+# Gem::Net::HTTPResponse instances cannot be directly sent over a socket.
+#
+# Types of response classes:
+# - OkResponse
+# - NoContentResponse
+# - BadRequestResponse
+# - NotFoundResponse
+# - MethodNotAllowedResponse
+#
+# Example usage:
+#
+# server = TCPServer.new(0)
+# socket = server.accept
+#
+# response = OkResponse.for("https://rubygems.example")
+# socket.print response.to_s
+# socket.close
+#
+
+module Gem::GemcutterUtilities
+ class WebauthnListener
+ class Response
+ attr_reader :http_response
+
+ def self.for(host)
+ new(host)
+ end
+
+ def initialize(host)
+ @host = host
+
+ build_http_response
+ end
+
+ def to_s
+ status_line = "HTTP/#{@http_response.http_version} #{@http_response.code} #{@http_response.message}\r\n"
+ headers = @http_response.to_hash.map {|header, value| "#{header}: #{value.join(", ")}\r\n" }.join + "\r\n"
+ body = @http_response.body ? "#{@http_response.body}\n" : ""
+
+ status_line + headers + body
+ end
+
+ private
+
+ # Must be implemented in subclasses
+ def code
+ raise NotImplementedError
+ end
+
+ def reason_phrase
+ raise NotImplementedError
+ end
+
+ def body; end
+
+ def build_http_response
+ response_class = Gem::Net::HTTPResponse::CODE_TO_OBJ[code.to_s]
+ @http_response = response_class.new("1.1", code, reason_phrase)
+ @http_response.instance_variable_set(:@read, true)
+
+ add_connection_header
+ add_access_control_headers
+ add_body
+ end
+
+ def add_connection_header
+ @http_response["connection"] = "close"
+ end
+
+ def add_access_control_headers
+ @http_response["access-control-allow-origin"] = @host
+ @http_response["access-control-allow-methods"] = "POST"
+ @http_response["access-control-allow-headers"] = %w[Content-Type Authorization x-csrf-token]
+ end
+
+ def add_body
+ return unless body
+ @http_response["content-type"] = "text/plain; charset=utf-8"
+ @http_response["content-length"] = body.bytesize
+ @http_response.instance_variable_set(:@body, body)
+ end
+ end
+
+ class OkResponse < Response
+ private
+
+ def code
+ 200
+ end
+
+ def reason_phrase
+ "OK"
+ end
+
+ def body
+ "success"
+ end
+ end
+
+ class NoContentResponse < Response
+ private
+
+ def code
+ 204
+ end
+
+ def reason_phrase
+ "No Content"
+ end
+ end
+
+ class BadRequestResponse < Response
+ private
+
+ def code
+ 400
+ end
+
+ def reason_phrase
+ "Bad Request"
+ end
+
+ def body
+ "missing code parameter"
+ end
+ end
+
+ class NotFoundResponse < Response
+ private
+
+ def code
+ 404
+ end
+
+ def reason_phrase
+ "Not Found"
+ end
+ end
+
+ class MethodNotAllowedResponse < Response
+ private
+
+ def code
+ 405
+ end
+
+ def reason_phrase
+ "Method Not Allowed"
+ end
+
+ def add_access_control_headers
+ super
+ @http_response["allow"] = %w[GET OPTIONS]
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/gemcutter_utilities/webauthn_poller.rb b/lib/rubygems/gemcutter_utilities/webauthn_poller.rb
new file mode 100644
index 0000000000..0fdd1d5bf4
--- /dev/null
+++ b/lib/rubygems/gemcutter_utilities/webauthn_poller.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+##
+# The WebauthnPoller class retrieves an OTP after a user successfully WebAuthns. An instance
+# polls the Gem host for the OTP code. The polling request (api/v1/webauthn_verification/<webauthn_token>/status.json)
+# is sent to the Gem host every 5 seconds and will timeout after 5 minutes. If the status field in the json response
+# is "success", the code field will contain the OTP code.
+#
+# Example usage:
+#
+# thread = Gem::WebauthnPoller.poll_thread(
+# {},
+# "RubyGems.org",
+# "https://rubygems.org/api/v1/webauthn_verification/odow34b93t6aPCdY",
+# { email: "email@example.com", password: "password" }
+# )
+# thread.join
+# otp = thread[:otp]
+# error = thread[:error]
+#
+
+module Gem::GemcutterUtilities
+ class WebauthnPoller
+ include Gem::GemcutterUtilities
+ TIMEOUT_IN_SECONDS = 300
+
+ attr_reader :options, :host
+
+ def initialize(options, host)
+ @options = options
+ @host = host
+ end
+
+ def self.poll_thread(options, host, webauthn_url, credentials)
+ Thread.new do
+ thread = Thread.current
+ thread.abort_on_exception = true
+ thread.report_on_exception = false
+ thread[:otp] = new(options, host).poll_for_otp(webauthn_url, credentials)
+ rescue Gem::WebauthnVerificationError, Gem::Timeout::Error => e
+ thread[:error] = e
+ end
+ end
+
+ def poll_for_otp(webauthn_url, credentials)
+ Gem::Timeout.timeout(TIMEOUT_IN_SECONDS) do
+ loop do
+ response = webauthn_verification_poll_response(webauthn_url, credentials)
+ raise Gem::WebauthnVerificationError, response.message unless response.is_a?(Gem::Net::HTTPSuccess)
+
+ require "json"
+ parsed_response = JSON.parse(response.body)
+ case parsed_response["status"]
+ when "pending"
+ sleep 5
+ when "success"
+ return parsed_response["code"]
+ else
+ raise Gem::WebauthnVerificationError, parsed_response.fetch("message", "Invalid response from server")
+ end
+ end
+ end
+ end
+
+ private
+
+ def webauthn_verification_poll_response(webauthn_url, credentials)
+ webauthn_token = %r{(?<=\/)[^\/]+(?=$)}.match(webauthn_url)[0]
+ rubygems_api_request(:get, "api/v1/webauthn_verification/#{webauthn_token}/status.json") do |request|
+ if credentials.empty?
+ request.add_field "Authorization", api_key
+ else
+ request.basic_auth credentials[:email], credentials[:password]
+ end
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/gemspec_helpers.rb b/lib/rubygems/gemspec_helpers.rb
new file mode 100644
index 0000000000..2b20fcafa1
--- /dev/null
+++ b/lib/rubygems/gemspec_helpers.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require_relative "../rubygems"
+
+##
+# Mixin methods for commands that work with gemspecs.
+
+module Gem::GemspecHelpers
+ def find_gemspec(glob = "*.gemspec")
+ gemspecs = Dir.glob(glob).sort
+
+ if gemspecs.size > 1
+ alert_error "Multiple gemspecs found: #{gemspecs}, please specify one"
+ terminate_interaction(1)
+ end
+
+ gemspecs.first
+ end
+end
diff --git a/lib/rubygems/indexer.rb b/lib/rubygems/indexer.rb
deleted file mode 100644
index c6691517b3..0000000000
--- a/lib/rubygems/indexer.rb
+++ /dev/null
@@ -1,428 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "../rubygems"
-require_relative "package"
-require "tmpdir"
-
-##
-# Top level class for building the gem repository index.
-
-class Gem::Indexer
- include Gem::UserInteraction
-
- ##
- # Build indexes for RubyGems 1.2.0 and newer when true
-
- attr_accessor :build_modern
-
- ##
- # Index install location
-
- attr_reader :dest_directory
-
- ##
- # Specs index install location
-
- attr_reader :dest_specs_index
-
- ##
- # Latest specs index install location
-
- attr_reader :dest_latest_specs_index
-
- ##
- # Prerelease specs index install location
-
- attr_reader :dest_prerelease_specs_index
-
- ##
- # Index build directory
-
- attr_reader :directory
-
- ##
- # Create an indexer that will index the gems in +directory+.
-
- def initialize(directory, options = {})
- require "fileutils"
- require "tmpdir"
- require "zlib"
-
- options = { :build_modern => true }.merge options
-
- @build_modern = options[:build_modern]
-
- @dest_directory = directory
- @directory = Dir.mktmpdir "gem_generate_index"
-
- marshal_name = "Marshal.#{Gem.marshal_version}"
-
- @master_index = File.join @directory, "yaml"
- @marshal_index = File.join @directory, marshal_name
-
- @quick_dir = File.join @directory, "quick"
- @quick_marshal_dir = File.join @quick_dir, marshal_name
- @quick_marshal_dir_base = File.join "quick", marshal_name # FIX: UGH
-
- @quick_index = File.join @quick_dir, "index"
- @latest_index = File.join @quick_dir, "latest_index"
-
- @specs_index = File.join @directory, "specs.#{Gem.marshal_version}"
- @latest_specs_index =
- File.join(@directory, "latest_specs.#{Gem.marshal_version}")
- @prerelease_specs_index =
- File.join(@directory, "prerelease_specs.#{Gem.marshal_version}")
- @dest_specs_index =
- File.join(@dest_directory, "specs.#{Gem.marshal_version}")
- @dest_latest_specs_index =
- File.join(@dest_directory, "latest_specs.#{Gem.marshal_version}")
- @dest_prerelease_specs_index =
- File.join(@dest_directory, "prerelease_specs.#{Gem.marshal_version}")
-
- @files = []
- end
-
- ##
- # Build various indices
-
- def build_indices
- specs = map_gems_to_specs gem_file_list
- Gem::Specification._resort! specs
- build_marshal_gemspecs specs
- build_modern_indices specs if @build_modern
-
- compress_indices
- end
-
- ##
- # Builds Marshal quick index gemspecs.
-
- def build_marshal_gemspecs(specs)
- count = specs.count
- progress = ui.progress_reporter count,
- "Generating Marshal quick index gemspecs for #{count} gems",
- "Complete"
-
- files = []
-
- Gem.time "Generated Marshal quick index gemspecs" do
- specs.each do |spec|
- next if spec.default_gem?
- spec_file_name = "#{spec.original_name}.gemspec.rz"
- marshal_name = File.join @quick_marshal_dir, spec_file_name
-
- marshal_zipped = Gem.deflate Marshal.dump(spec)
-
- File.open marshal_name, "wb" do |io|
- io.write marshal_zipped
- end
-
- files << marshal_name
-
- progress.updated spec.original_name
- end
-
- progress.done
- end
-
- @files << @quick_marshal_dir
-
- files
- end
-
- ##
- # Build a single index for RubyGems 1.2 and newer
-
- def build_modern_index(index, file, name)
- say "Generating #{name} index"
-
- Gem.time "Generated #{name} index" do
- File.open(file, "wb") do |io|
- specs = index.map do |*spec|
- # We have to splat here because latest_specs is an array, while the
- # others are hashes.
- spec = spec.flatten.last
- platform = spec.original_platform
-
- # win32-api-1.0.4-x86-mswin32-60
- unless String === platform
- alert_warning "Skipping invalid platform in gem: #{spec.full_name}"
- next
- end
-
- platform = Gem::Platform::RUBY if platform.nil? || platform.empty?
- [spec.name, spec.version, platform]
- end
-
- specs = compact_specs(specs)
- Marshal.dump(specs, io)
- end
- end
- end
-
- ##
- # Builds indices for RubyGems 1.2 and newer. Handles full, latest, prerelease
-
- def build_modern_indices(specs)
- prerelease, released = specs.partition do |s|
- s.version.prerelease?
- end
- latest_specs =
- Gem::Specification._latest_specs specs
-
- build_modern_index(released.sort, @specs_index, "specs")
- build_modern_index(latest_specs.sort, @latest_specs_index, "latest specs")
- build_modern_index(prerelease.sort, @prerelease_specs_index,
- "prerelease specs")
-
- @files += [@specs_index,
- "#{@specs_index}.gz",
- @latest_specs_index,
- "#{@latest_specs_index}.gz",
- @prerelease_specs_index,
- "#{@prerelease_specs_index}.gz"]
- end
-
- def map_gems_to_specs(gems)
- gems.map do |gemfile|
- if File.size(gemfile) == 0
- alert_warning "Skipping zero-length gem: #{gemfile}"
- next
- end
-
- begin
- spec = Gem::Package.new(gemfile).spec
- spec.loaded_from = gemfile
-
- spec.abbreviate
- spec.sanitize
-
- spec
- rescue SignalException
- alert_error "Received signal, exiting"
- raise
- rescue StandardError => e
- msg = ["Unable to process #{gemfile}",
- "#{e.message} (#{e.class})",
- "\t#{e.backtrace.join "\n\t"}"].join("\n")
- alert_error msg
- end
- end.compact
- end
-
- ##
- # Compresses indices on disk
- #--
- # All future files should be compressed using gzip, not deflate
-
- def compress_indices
- say "Compressing indices"
-
- Gem.time "Compressed indices" do
- if @build_modern
- gzip @specs_index
- gzip @latest_specs_index
- gzip @prerelease_specs_index
- end
- end
- end
-
- ##
- # Compacts Marshal output for the specs index data source by using identical
- # objects as much as possible.
-
- def compact_specs(specs)
- names = {}
- versions = {}
- platforms = {}
-
- specs.map do |(name, version, platform)|
- names[name] = name unless names.include? name
- versions[version] = version unless versions.include? version
- platforms[platform] = platform unless platforms.include? platform
-
- [names[name], versions[version], platforms[platform]]
- end
- end
-
- ##
- # Compress +filename+ with +extension+.
-
- def compress(filename, extension)
- data = Gem.read_binary filename
-
- zipped = Gem.deflate data
-
- File.open "#{filename}.#{extension}", "wb" do |io|
- io.write zipped
- end
- end
-
- ##
- # List of gem file names to index.
-
- def gem_file_list
- Gem::Util.glob_files_in_dir("*.gem", File.join(@dest_directory, "gems"))
- end
-
- ##
- # Builds and installs indices.
-
- def generate_index
- make_temp_directories
- build_indices
- install_indices
- rescue SignalException
- ensure
- FileUtils.rm_rf @directory
- end
-
- ##
- # Zlib::GzipWriter wrapper that gzips +filename+ on disk.
-
- def gzip(filename)
- Zlib::GzipWriter.open "#{filename}.gz" do |io|
- io.write Gem.read_binary(filename)
- end
- end
-
- ##
- # Install generated indices into the destination directory.
-
- def install_indices
- verbose = Gem.configuration.really_verbose
-
- say "Moving index into production dir #{@dest_directory}" if verbose
-
- files = @files
- files.delete @quick_marshal_dir if files.include? @quick_dir
-
- if files.include?(@quick_marshal_dir) && !files.include?(@quick_dir)
- files.delete @quick_marshal_dir
-
- dst_name = File.join(@dest_directory, @quick_marshal_dir_base)
-
- FileUtils.mkdir_p File.dirname(dst_name), :verbose => verbose
- FileUtils.rm_rf dst_name, :verbose => verbose
- FileUtils.mv(@quick_marshal_dir, dst_name,
- :verbose => verbose, :force => true)
- end
-
- files = files.map do |path|
- path.sub(%r{^#{Regexp.escape @directory}/?}, "") # HACK?
- end
-
- files.each do |file|
- src_name = File.join @directory, file
- dst_name = File.join @dest_directory, file
-
- FileUtils.rm_rf dst_name, :verbose => verbose
- FileUtils.mv(src_name, @dest_directory,
- :verbose => verbose, :force => true)
- end
- end
-
- ##
- # Make directories for index generation
-
- def make_temp_directories
- FileUtils.rm_rf @directory
- FileUtils.mkdir_p @directory, :mode => 0o700
- FileUtils.mkdir_p @quick_marshal_dir
- end
-
- ##
- # Ensure +path+ and path with +extension+ are identical.
-
- def paranoid(path, extension)
- data = Gem.read_binary path
- compressed_data = Gem.read_binary "#{path}.#{extension}"
-
- unless data == Gem::Util.inflate(compressed_data)
- raise "Compressed file #{compressed_path} does not match uncompressed file #{path}"
- end
- end
-
- ##
- # Perform an in-place update of the repository from newly added gems.
-
- def update_index
- make_temp_directories
-
- specs_mtime = File.stat(@dest_specs_index).mtime
- newest_mtime = Time.at 0
-
- updated_gems = gem_file_list.select do |gem|
- gem_mtime = File.stat(gem).mtime
- newest_mtime = gem_mtime if gem_mtime > newest_mtime
- gem_mtime >= specs_mtime
- end
-
- if updated_gems.empty?
- say "No new gems"
- terminate_interaction 0
- end
-
- specs = map_gems_to_specs updated_gems
- prerelease, released = specs.partition {|s| s.version.prerelease? }
-
- files = build_marshal_gemspecs specs
-
- Gem.time "Updated indexes" do
- update_specs_index released, @dest_specs_index, @specs_index
- update_specs_index released, @dest_latest_specs_index, @latest_specs_index
- update_specs_index(prerelease,
- @dest_prerelease_specs_index,
- @prerelease_specs_index)
- end
-
- compress_indices
-
- verbose = Gem.configuration.really_verbose
-
- say "Updating production dir #{@dest_directory}" if verbose
-
- files << @specs_index
- files << "#{@specs_index}.gz"
- files << @latest_specs_index
- files << "#{@latest_specs_index}.gz"
- files << @prerelease_specs_index
- files << "#{@prerelease_specs_index}.gz"
-
- files = files.map do |path|
- path.sub(%r{^#{Regexp.escape @directory}/?}, "") # HACK?
- end
-
- files.each do |file|
- src_name = File.join @directory, file
- dst_name = File.join @dest_directory, file # REFACTOR: duped above
-
- FileUtils.mv src_name, dst_name, :verbose => verbose,
- :force => true
-
- File.utime newest_mtime, newest_mtime, dst_name
- end
- ensure
- FileUtils.rm_rf @directory
- end
-
- ##
- # Combines specs in +index+ and +source+ then writes out a new copy to
- # +dest+. For a latest index, does not ensure the new file is minimal.
-
- def update_specs_index(index, source, dest)
- specs_index = Marshal.load Gem.read_binary(source)
-
- index.each do |spec|
- platform = spec.original_platform
- platform = Gem::Platform::RUBY if platform.nil? || platform.empty?
- specs_index << [spec.name, spec.version, platform]
- end
-
- specs_index = compact_specs specs_index.uniq.sort
-
- File.open dest, "wb" do |io|
- Marshal.dump specs_index, io
- end
- end
-end
diff --git a/lib/rubygems/install_update_options.rb b/lib/rubygems/install_update_options.rb
index 5c7289426e..aad207a718 100644
--- a/lib/rubygems/install_update_options.rb
+++ b/lib/rubygems/install_update_options.rb
@@ -186,7 +186,7 @@ module Gem::InstallUpdateOptions
def install_update_options
{
- :document => %w[ri],
+ document: %w[ri],
}
end
diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb
index 162a50fdb7..8f6f9a5aa8 100644
--- a/lib/rubygems/installer.rb
+++ b/lib/rubygems/installer.rb
@@ -189,10 +189,12 @@ class Gem::Installer
@package.prog_mode = options[:prog_mode]
@package.data_mode = options[:data_mode]
- if options[:user_install]
- @gem_home = Gem.user_dir
- @bin_dir = Gem.bindir gem_home unless options[:bin_dir]
- @plugins_dir = Gem.plugindir(gem_home)
+ if @gem_home == Gem.user_dir
+ # If we get here, then one of the following likely happened:
+ # - `--user-install` was specified
+ # - `Gem::PathSupport#home` fell back to `Gem.user_dir`
+ # - GEM_HOME was manually set to `Gem.user_dir`
+
check_that_user_bin_dir_is_in_path
end
end
@@ -222,10 +224,11 @@ class Gem::Installer
File.open generated_bin, "rb" do |io|
line = io.gets
- shebang = /^#!.*ruby/
+ shebang = /^#!.*ruby/o
- if load_relative_enabled?
- until line.nil? || line =~ shebang do
+ # TruffleRuby uses a bash prelude in default launchers
+ if load_relative_enabled? || RUBY_ENGINE == "truffleruby"
+ until line.nil? || shebang.match?(line) do
line = io.gets
end
end
@@ -236,7 +239,7 @@ class Gem::Installer
# TODO: detect a specially formatted comment instead of trying
# to find a string inside Ruby code.
- next unless io.gets.to_s.include?("This file was generated by RubyGems")
+ next unless io.gets&.include?("This file was generated by RubyGems")
ruby_executable = true
existing = io.read.slice(/
@@ -316,7 +319,7 @@ class Gem::Installer
FileUtils.rm_rf spec.extension_dir
dir_mode = options[:dir_mode]
- FileUtils.mkdir_p gem_dir, :mode => dir_mode && 0o755
+ FileUtils.mkdir_p gem_dir, mode: dir_mode && 0o755
if @options[:install_as_default]
extract_bin
@@ -348,11 +351,9 @@ class Gem::Installer
run_post_install_hooks
spec
-
- # TODO: This rescue is in the wrong place. What is raising this exception?
- # move this rescue to around the code that actually might raise it.
- rescue Zlib::GzipFile::Error
- raise Gem::InstallError, "gzip error installing #{gem}"
+ rescue Errno::EACCES => e
+ # Permission denied - /path/to/foo
+ raise Gem::FilePermissionError, e.message.split(" - ").last
end
def run_pre_install_hooks # :nodoc:
@@ -393,7 +394,7 @@ class Gem::Installer
specs = []
Gem::Util.glob_files_in_dir("*.gemspec", File.join(gem_home, "specifications")).each do |path|
- spec = Gem::Specification.load path.tap(&Gem::UNTAINT)
+ spec = Gem::Specification.load path
specs << spec if spec
end
@@ -492,7 +493,6 @@ class Gem::Installer
ensure_writable_dir @bin_dir
spec.executables.each do |filename|
- filename.tap(&Gem::UNTAINT)
bin_path = File.join gem_dir, spec.bindir, filename
next unless File.exist? bin_path
@@ -569,7 +569,7 @@ class Gem::Installer
File.unlink dst
end
- FileUtils.symlink src, dst, :verbose => Gem.configuration.really_verbose
+ FileUtils.symlink src, dst, verbose: Gem.configuration.really_verbose
rescue NotImplementedError, SystemCallError
alert_warning "Unable to use symlinks, installing wrapper"
generate_bin_script filename, bindir
@@ -634,7 +634,6 @@ class Gem::Installer
def ensure_loadable_spec
ruby = spec.to_ruby_for_cache
- ruby.tap(&Gem::UNTAINT)
begin
eval ruby
@@ -655,32 +654,37 @@ class Gem::Installer
def process_options # :nodoc:
@options = {
- :bin_dir => nil,
- :env_shebang => false,
- :force => false,
- :only_install_dir => false,
- :post_install_message => true,
+ bin_dir: nil,
+ env_shebang: false,
+ force: false,
+ only_install_dir: false,
+ post_install_message: true,
}.merge options
@env_shebang = options[:env_shebang]
@force = options[:force]
@install_dir = options[:install_dir]
- @gem_home = options[:install_dir] || Gem.dir
- @plugins_dir = Gem.plugindir(@gem_home)
+ @user_install = options[:user_install]
@ignore_dependencies = options[:ignore_dependencies]
@format_executable = options[:format_executable]
@wrappers = options[:wrappers]
@only_install_dir = options[:only_install_dir]
- # If the user has asked for the gem to be installed in a directory that is
- # the system gem directory, then use the system bin directory, else create
- # (or use) a new bin dir under the gem_home.
- @bin_dir = options[:bin_dir] || Gem.bindir(gem_home)
+ @bin_dir = options[:bin_dir]
@development = options[:development]
@build_root = options[:build_root]
@build_args = options[:build_args]
+ @gem_home = @install_dir || user_install_dir || Gem.dir
+
+ # If the user has asked for the gem to be installed in a directory that is
+ # the system gem directory, then use the system bin directory, else create
+ # (or use) a new bin dir under the gem_home.
+ @bin_dir ||= Gem.bindir(@gem_home)
+
+ @plugins_dir = Gem.plugindir(@gem_home)
+
unless @build_root.nil?
@bin_dir = File.join(@build_root, @bin_dir.gsub(/^[a-zA-Z]:/, ""))
@gem_home = File.join(@build_root, @gem_home.gsub(/^[a-zA-Z]:/, ""))
@@ -714,8 +718,7 @@ class Gem::Installer
end
def verify_gem_home # :nodoc:
- FileUtils.mkdir_p gem_home, :mode => options[:dir_mode] && 0o755
- raise Gem::FilePermissionError, gem_home unless File.writable?(gem_home)
+ FileUtils.mkdir_p gem_home, mode: options[:dir_mode] && 0o755
end
def verify_spec
@@ -936,7 +939,7 @@ TEXT
build_info_dir = File.join gem_home, "build_info"
dir_mode = options[:dir_mode]
- FileUtils.mkdir_p build_info_dir, :mode => dir_mode && 0o755
+ FileUtils.mkdir_p build_info_dir, mode: dir_mode && 0o755
build_info_file = File.join build_info_dir, "#{spec.full_name}.info"
@@ -969,6 +972,19 @@ TEXT
private
+ def user_install_dir
+ # never install to user home in --build-root mode
+ return unless @build_root.nil?
+
+ # Please note that @user_install might have three states:
+ # * `true`: `--user-install`
+ # * `false`: `--no-user-install` and
+ # * `nil`: option was not specified
+ if @user_install || (@user_install.nil? && Gem.default_user_install)
+ Gem.user_dir
+ end
+ end
+
def build_args
@build_args ||= begin
require_relative "command"
diff --git a/lib/rubygems/local_remote_options.rb b/lib/rubygems/local_remote_options.rb
index e2a008fada..51a61213a5 100644
--- a/lib/rubygems/local_remote_options.rb
+++ b/lib/rubygems/local_remote_options.rb
@@ -6,7 +6,7 @@
# See LICENSE.txt for permissions.
#++
-require "uri"
+require_relative "vendor/uri/lib/uri"
require_relative "../rubygems"
##
@@ -17,10 +17,10 @@ module Gem::LocalRemoteOptions
# Allows Gem::OptionParser to handle HTTP URIs.
def accept_uri_http
- Gem::OptionParser.accept URI::HTTP do |value|
+ Gem::OptionParser.accept Gem::URI::HTTP do |value|
begin
- uri = URI.parse value
- rescue URI::InvalidURIError
+ uri = Gem::URI.parse value
+ rescue Gem::URI::InvalidURIError
raise Gem::OptionParser::InvalidArgument, value
end
@@ -88,7 +88,7 @@ module Gem::LocalRemoteOptions
def add_proxy_option
accept_uri_http
- add_option(:"Local/Remote", "-p", "--[no-]http-proxy [URL]", URI::HTTP,
+ add_option(:"Local/Remote", "-p", "--[no-]http-proxy [URL]", Gem::URI::HTTP,
"Use HTTP proxy for remote operations") do |value, options|
options[:http_proxy] = value == false ? :no_proxy : value
Gem.configuration[:http_proxy] = options[:http_proxy]
@@ -101,7 +101,7 @@ module Gem::LocalRemoteOptions
def add_source_option
accept_uri_http
- add_option(:"Local/Remote", "-s", "--source URL", URI::HTTP,
+ add_option(:"Local/Remote", "-s", "--source URL", Gem::URI::HTTP,
"Append URL to list of remote gem sources") do |source, options|
source << "/" unless source.end_with?("/")
diff --git a/lib/rubygems/name_tuple.rb b/lib/rubygems/name_tuple.rb
index 9963c8bc5c..3f4a6fcf3d 100644
--- a/lib/rubygems/name_tuple.rb
+++ b/lib/rubygems/name_tuple.rb
@@ -6,14 +6,12 @@
# wrap the data returned from the indexes.
class Gem::NameTuple
- def initialize(name, version, platform="ruby")
+ def initialize(name, version, platform=Gem::Platform::RUBY)
@name = name
@version = version
- unless platform.is_a? Gem::Platform
- platform = "ruby" if !platform || platform.empty?
- end
-
+ platform &&= platform.to_s
+ platform = Gem::Platform::RUBY if !platform || platform.empty?
@platform = platform
end
@@ -49,11 +47,11 @@ class Gem::NameTuple
def full_name
case @platform
- when nil, "ruby", ""
+ when nil, "", Gem::Platform::RUBY
"#{@name}-#{@version}"
else
"#{@name}-#{@version}-#{@platform}"
- end.dup.tap(&Gem::UNTAINT)
+ end
end
##
diff --git a/lib/rubygems/optparse.rb b/lib/rubygems/optparse.rb
deleted file mode 100644
index 6ed718423c..0000000000
--- a/lib/rubygems/optparse.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "optparse/lib/optparse"
diff --git a/lib/rubygems/optparse/lib/optparse.rb b/lib/rubygems/optparse/lib/optparse.rb
deleted file mode 100644
index 1e50bda769..0000000000
--- a/lib/rubygems/optparse/lib/optparse.rb
+++ /dev/null
@@ -1,2308 +0,0 @@
-# frozen_string_literal: true
-#
-# optparse.rb - command-line option analysis with the Gem::OptionParser class.
-#
-# Author:: Nobu Nakada
-# Documentation:: Nobu Nakada and Gavin Sinclair.
-#
-# See Gem::OptionParser for documentation.
-#
-
-
-#--
-# == Developer Documentation (not for RDoc output)
-#
-# === Class tree
-#
-# - Gem::OptionParser:: front end
-# - Gem::OptionParser::Switch:: each switches
-# - Gem::OptionParser::List:: options list
-# - Gem::OptionParser::ParseError:: errors on parsing
-# - Gem::OptionParser::AmbiguousOption
-# - Gem::OptionParser::NeedlessArgument
-# - Gem::OptionParser::MissingArgument
-# - Gem::OptionParser::InvalidOption
-# - Gem::OptionParser::InvalidArgument
-# - Gem::OptionParser::AmbiguousArgument
-#
-# === Object relationship diagram
-#
-# +--------------+
-# | Gem::OptionParser |<>-----+
-# +--------------+ | +--------+
-# | ,-| Switch |
-# on_head -------->+---------------+ / +--------+
-# accept/reject -->| List |<|>-
-# | |<|>- +----------+
-# on ------------->+---------------+ `-| argument |
-# : : | class |
-# +---------------+ |==========|
-# on_tail -------->| | |pattern |
-# +---------------+ |----------|
-# Gem::OptionParser.accept ->| DefaultList | |converter |
-# reject |(shared between| +----------+
-# | all instances)|
-# +---------------+
-#
-#++
-#
-# == Gem::OptionParser
-#
-# === New to \Gem::OptionParser?
-#
-# See the {Tutorial}[optparse/tutorial.rdoc].
-#
-# === Introduction
-#
-# Gem::OptionParser is a class for command-line option analysis. It is much more
-# advanced, yet also easier to use, than GetoptLong, and is a more Ruby-oriented
-# solution.
-#
-# === Features
-#
-# 1. The argument specification and the code to handle it are written in the
-# same place.
-# 2. It can output an option summary; you don't need to maintain this string
-# separately.
-# 3. Optional and mandatory arguments are specified very gracefully.
-# 4. Arguments can be automatically converted to a specified class.
-# 5. Arguments can be restricted to a certain set.
-#
-# All of these features are demonstrated in the examples below. See
-# #make_switch for full documentation.
-#
-# === Minimal example
-#
-# require 'rubygems/optparse/lib/optparse'
-#
-# options = {}
-# Gem::OptionParser.new do |parser|
-# parser.banner = "Usage: example.rb [options]"
-#
-# parser.on("-v", "--[no-]verbose", "Run verbosely") do |v|
-# options[:verbose] = v
-# end
-# end.parse!
-#
-# p options
-# p ARGV
-#
-# === Generating Help
-#
-# Gem::OptionParser can be used to automatically generate help for the commands you
-# write:
-#
-# require 'rubygems/optparse/lib/optparse'
-#
-# Options = Struct.new(:name)
-#
-# class Parser
-# def self.parse(options)
-# args = Options.new("world")
-#
-# opt_parser = Gem::OptionParser.new do |parser|
-# parser.banner = "Usage: example.rb [options]"
-#
-# parser.on("-nNAME", "--name=NAME", "Name to say hello to") do |n|
-# args.name = n
-# end
-#
-# parser.on("-h", "--help", "Prints this help") do
-# puts parser
-# exit
-# end
-# end
-#
-# opt_parser.parse!(options)
-# return args
-# end
-# end
-# options = Parser.parse %w[--help]
-#
-# #=>
-# # Usage: example.rb [options]
-# # -n, --name=NAME Name to say hello to
-# # -h, --help Prints this help
-#
-# === Required Arguments
-#
-# For options that require an argument, option specification strings may include an
-# option name in all caps. If an option is used without the required argument,
-# an exception will be raised.
-#
-# require 'rubygems/optparse/lib/optparse'
-#
-# options = {}
-# Gem::OptionParser.new do |parser|
-# parser.on("-r", "--require LIBRARY",
-# "Require the LIBRARY before executing your script") do |lib|
-# puts "You required #{lib}!"
-# end
-# end.parse!
-#
-# Used:
-#
-# $ ruby optparse-test.rb -r
-# optparse-test.rb:9:in `<main>': missing argument: -r (Gem::OptionParser::MissingArgument)
-# $ ruby optparse-test.rb -r my-library
-# You required my-library!
-#
-# === Type Coercion
-#
-# Gem::OptionParser supports the ability to coerce command line arguments
-# into objects for us.
-#
-# Gem::OptionParser comes with a few ready-to-use kinds of type
-# coercion. They are:
-#
-# - Date -- Anything accepted by +Date.parse+
-# - DateTime -- Anything accepted by +DateTime.parse+
-# - Time -- Anything accepted by +Time.httpdate+ or +Time.parse+
-# - URI -- Anything accepted by +URI.parse+
-# - Shellwords -- Anything accepted by +Shellwords.shellwords+
-# - String -- Any non-empty string
-# - Integer -- Any integer. Will convert octal. (e.g. 124, -3, 040)
-# - Float -- Any float. (e.g. 10, 3.14, -100E+13)
-# - Numeric -- Any integer, float, or rational (1, 3.4, 1/3)
-# - DecimalInteger -- Like +Integer+, but no octal format.
-# - OctalInteger -- Like +Integer+, but no decimal format.
-# - DecimalNumeric -- Decimal integer or float.
-# - TrueClass -- Accepts '+, yes, true, -, no, false' and
-# defaults as +true+
-# - FalseClass -- Same as +TrueClass+, but defaults to +false+
-# - Array -- Strings separated by ',' (e.g. 1,2,3)
-# - Regexp -- Regular expressions. Also includes options.
-#
-# We can also add our own coercions, which we will cover below.
-#
-# ==== Using Built-in Conversions
-#
-# As an example, the built-in +Time+ conversion is used. The other built-in
-# conversions behave in the same way.
-# Gem::OptionParser will attempt to parse the argument
-# as a +Time+. If it succeeds, that time will be passed to the
-# handler block. Otherwise, an exception will be raised.
-#
-# require 'rubygems/optparse/lib/optparse'
-# require 'rubygems/optparse/lib/optparse/time'
-# Gem::OptionParser.new do |parser|
-# parser.on("-t", "--time [TIME]", Time, "Begin execution at given time") do |time|
-# p time
-# end
-# end.parse!
-#
-# Used:
-#
-# $ ruby optparse-test.rb -t nonsense
-# ... invalid argument: -t nonsense (Gem::OptionParser::InvalidArgument)
-# $ ruby optparse-test.rb -t 10-11-12
-# 2010-11-12 00:00:00 -0500
-# $ ruby optparse-test.rb -t 9:30
-# 2014-08-13 09:30:00 -0400
-#
-# ==== Creating Custom Conversions
-#
-# The +accept+ method on Gem::OptionParser may be used to create converters.
-# It specifies which conversion block to call whenever a class is specified.
-# The example below uses it to fetch a +User+ object before the +on+ handler receives it.
-#
-# require 'rubygems/optparse/lib/optparse'
-#
-# User = Struct.new(:id, :name)
-#
-# def find_user id
-# not_found = ->{ raise "No User Found for id #{id}" }
-# [ User.new(1, "Sam"),
-# User.new(2, "Gandalf") ].find(not_found) do |u|
-# u.id == id
-# end
-# end
-#
-# op = Gem::OptionParser.new
-# op.accept(User) do |user_id|
-# find_user user_id.to_i
-# end
-#
-# op.on("--user ID", User) do |user|
-# puts user
-# end
-#
-# op.parse!
-#
-# Used:
-#
-# $ ruby optparse-test.rb --user 1
-# #<struct User id=1, name="Sam">
-# $ ruby optparse-test.rb --user 2
-# #<struct User id=2, name="Gandalf">
-# $ ruby optparse-test.rb --user 3
-# optparse-test.rb:15:in `block in find_user': No User Found for id 3 (RuntimeError)
-#
-# === Store options to a Hash
-#
-# The +into+ option of +order+, +parse+ and so on methods stores command line options into a Hash.
-#
-# require 'rubygems/optparse/lib/optparse'
-#
-# options = {}
-# Gem::OptionParser.new do |parser|
-# parser.on('-a')
-# parser.on('-b NUM', Integer)
-# parser.on('-v', '--verbose')
-# end.parse!(into: options)
-#
-# p options
-#
-# Used:
-#
-# $ ruby optparse-test.rb -a
-# {:a=>true}
-# $ ruby optparse-test.rb -a -v
-# {:a=>true, :verbose=>true}
-# $ ruby optparse-test.rb -a -b 100
-# {:a=>true, :b=>100}
-#
-# === Complete example
-#
-# The following example is a complete Ruby program. You can run it and see the
-# effect of specifying various options. This is probably the best way to learn
-# the features of +optparse+.
-#
-# require 'rubygems/optparse/lib/optparse'
-# require 'rubygems/optparse/lib/optparse/time'
-# require 'ostruct'
-# require 'pp'
-#
-# class OptparseExample
-# Version = '1.0.0'
-#
-# CODES = %w[iso-2022-jp shift_jis euc-jp utf8 binary]
-# CODE_ALIASES = { "jis" => "iso-2022-jp", "sjis" => "shift_jis" }
-#
-# class ScriptOptions
-# attr_accessor :library, :inplace, :encoding, :transfer_type,
-# :verbose, :extension, :delay, :time, :record_separator,
-# :list
-#
-# def initialize
-# self.library = []
-# self.inplace = false
-# self.encoding = "utf8"
-# self.transfer_type = :auto
-# self.verbose = false
-# end
-#
-# def define_options(parser)
-# parser.banner = "Usage: example.rb [options]"
-# parser.separator ""
-# parser.separator "Specific options:"
-#
-# # add additional options
-# perform_inplace_option(parser)
-# delay_execution_option(parser)
-# execute_at_time_option(parser)
-# specify_record_separator_option(parser)
-# list_example_option(parser)
-# specify_encoding_option(parser)
-# optional_option_argument_with_keyword_completion_option(parser)
-# boolean_verbose_option(parser)
-#
-# parser.separator ""
-# parser.separator "Common options:"
-# # No argument, shows at tail. This will print an options summary.
-# # Try it and see!
-# parser.on_tail("-h", "--help", "Show this message") do
-# puts parser
-# exit
-# end
-# # Another typical switch to print the version.
-# parser.on_tail("--version", "Show version") do
-# puts Version
-# exit
-# end
-# end
-#
-# def perform_inplace_option(parser)
-# # Specifies an optional option argument
-# parser.on("-i", "--inplace [EXTENSION]",
-# "Edit ARGV files in place",
-# "(make backup if EXTENSION supplied)") do |ext|
-# self.inplace = true
-# self.extension = ext || ''
-# self.extension.sub!(/\A\.?(?=.)/, ".") # Ensure extension begins with dot.
-# end
-# end
-#
-# def delay_execution_option(parser)
-# # Cast 'delay' argument to a Float.
-# parser.on("--delay N", Float, "Delay N seconds before executing") do |n|
-# self.delay = n
-# end
-# end
-#
-# def execute_at_time_option(parser)
-# # Cast 'time' argument to a Time object.
-# parser.on("-t", "--time [TIME]", Time, "Begin execution at given time") do |time|
-# self.time = time
-# end
-# end
-#
-# def specify_record_separator_option(parser)
-# # Cast to octal integer.
-# parser.on("-F", "--irs [OCTAL]", Gem::OptionParser::OctalInteger,
-# "Specify record separator (default \\0)") do |rs|
-# self.record_separator = rs
-# end
-# end
-#
-# def list_example_option(parser)
-# # List of arguments.
-# parser.on("--list x,y,z", Array, "Example 'list' of arguments") do |list|
-# self.list = list
-# end
-# end
-#
-# def specify_encoding_option(parser)
-# # Keyword completion. We are specifying a specific set of arguments (CODES
-# # and CODE_ALIASES - notice the latter is a Hash), and the user may provide
-# # the shortest unambiguous text.
-# code_list = (CODE_ALIASES.keys + CODES).join(', ')
-# parser.on("--code CODE", CODES, CODE_ALIASES, "Select encoding",
-# "(#{code_list})") do |encoding|
-# self.encoding = encoding
-# end
-# end
-#
-# def optional_option_argument_with_keyword_completion_option(parser)
-# # Optional '--type' option argument with keyword completion.
-# parser.on("--type [TYPE]", [:text, :binary, :auto],
-# "Select transfer type (text, binary, auto)") do |t|
-# self.transfer_type = t
-# end
-# end
-#
-# def boolean_verbose_option(parser)
-# # Boolean switch.
-# parser.on("-v", "--[no-]verbose", "Run verbosely") do |v|
-# self.verbose = v
-# end
-# end
-# end
-#
-# #
-# # Return a structure describing the options.
-# #
-# def parse(args)
-# # The options specified on the command line will be collected in
-# # *options*.
-#
-# @options = ScriptOptions.new
-# @args = Gem::OptionParser.new do |parser|
-# @options.define_options(parser)
-# parser.parse!(args)
-# end
-# @options
-# end
-#
-# attr_reader :parser, :options
-# end # class OptparseExample
-#
-# example = OptparseExample.new
-# options = example.parse(ARGV)
-# pp options # example.options
-# pp ARGV
-#
-# === Shell Completion
-#
-# For modern shells (e.g. bash, zsh, etc.), you can use shell
-# completion for command line options.
-#
-# === Further documentation
-#
-# The above examples, along with the accompanying
-# {Tutorial}[optparse/tutorial.rdoc],
-# should be enough to learn how to use this class.
-# If you have any questions, file a ticket at http://bugs.ruby-lang.org.
-#
-class Gem::OptionParser
- Gem::OptionParser::Version = "0.3.0"
-
- # :stopdoc:
- NoArgument = [NO_ARGUMENT = :NONE, nil].freeze
- RequiredArgument = [REQUIRED_ARGUMENT = :REQUIRED, true].freeze
- OptionalArgument = [OPTIONAL_ARGUMENT = :OPTIONAL, false].freeze
- # :startdoc:
-
- #
- # Keyword completion module. This allows partial arguments to be specified
- # and resolved against a list of acceptable values.
- #
- module Completion
- def self.regexp(key, icase)
- Regexp.new('\A' + Regexp.quote(key).gsub(/\w+\b/, '\&\w*'), icase)
- end
-
- def self.candidate(key, icase = false, pat = nil, &block)
- pat ||= Completion.regexp(key, icase)
- candidates = []
- block.call do |k, *v|
- (if Regexp === k
- kn = ""
- k === key
- else
- kn = defined?(k.id2name) ? k.id2name : k
- pat === kn
- end) or next
- v << k if v.empty?
- candidates << [k, v, kn]
- end
- candidates
- end
-
- def candidate(key, icase = false, pat = nil)
- Completion.candidate(key, icase, pat, &method(:each))
- end
-
- public
- def complete(key, icase = false, pat = nil)
- candidates = candidate(key, icase, pat, &method(:each)).sort_by {|k, v, kn| kn.size}
- if candidates.size == 1
- canon, sw, * = candidates[0]
- elsif candidates.size > 1
- canon, sw, cn = candidates.shift
- candidates.each do |k, v, kn|
- next if sw == v
- if String === cn and String === kn
- if cn.rindex(kn, 0)
- canon, sw, cn = k, v, kn
- next
- elsif kn.rindex(cn, 0)
- next
- end
- end
- throw :ambiguous, key
- end
- end
- if canon
- block_given? or return key, *sw
- yield(key, *sw)
- end
- end
-
- def convert(opt = nil, val = nil, *)
- val
- end
- end
-
-
- #
- # Map from option/keyword string to object with completion.
- #
- class OptionMap < Hash
- include Completion
- end
-
-
- #
- # Individual switch class. Not important to the user.
- #
- # Defined within Switch are several Switch-derived classes: NoArgument,
- # RequiredArgument, etc.
- #
- class Switch
- attr_reader :pattern, :conv, :short, :long, :arg, :desc, :block
-
- #
- # Guesses argument style from +arg+. Returns corresponding
- # Gem::OptionParser::Switch class (OptionalArgument, etc.).
- #
- def self.guess(arg)
- case arg
- when ""
- t = self
- when /\A=?\[/
- t = Switch::OptionalArgument
- when /\A\s+\[/
- t = Switch::PlacedArgument
- else
- t = Switch::RequiredArgument
- end
- self >= t or incompatible_argument_styles(arg, t)
- t
- end
-
- def self.incompatible_argument_styles(arg, t)
- raise(ArgumentError, "#{arg}: incompatible argument styles\n #{self}, #{t}",
- ParseError.filter_backtrace(caller(2)))
- end
-
- def self.pattern
- NilClass
- end
-
- def initialize(pattern = nil, conv = nil,
- short = nil, long = nil, arg = nil,
- desc = ([] if short or long), block = nil, &_block)
- raise if Array === pattern
- block ||= _block
- @pattern, @conv, @short, @long, @arg, @desc, @block =
- pattern, conv, short, long, arg, desc, block
- end
-
- #
- # Parses +arg+ and returns rest of +arg+ and matched portion to the
- # argument pattern. Yields when the pattern doesn't match substring.
- #
- def parse_arg(arg) # :nodoc:
- pattern or return nil, [arg]
- unless m = pattern.match(arg)
- yield(InvalidArgument, arg)
- return arg, []
- end
- if String === m
- m = [s = m]
- else
- m = m.to_a
- s = m[0]
- return nil, m unless String === s
- end
- raise InvalidArgument, arg unless arg.rindex(s, 0)
- return nil, m if s.length == arg.length
- yield(InvalidArgument, arg) # didn't match whole arg
- return arg[s.length..-1], m
- end
- private :parse_arg
-
- #
- # Parses argument, converts and returns +arg+, +block+ and result of
- # conversion. Yields at semi-error condition instead of raising an
- # exception.
- #
- def conv_arg(arg, val = []) # :nodoc:
- if conv
- val = conv.call(*val)
- else
- val = proc {|v| v}.call(*val)
- end
- return arg, block, val
- end
- private :conv_arg
-
- #
- # Produces the summary text. Each line of the summary is yielded to the
- # block (without newline).
- #
- # +sdone+:: Already summarized short style options keyed hash.
- # +ldone+:: Already summarized long style options keyed hash.
- # +width+:: Width of left side (option part). In other words, the right
- # side (description part) starts after +width+ columns.
- # +max+:: Maximum width of left side -> the options are filled within
- # +max+ columns.
- # +indent+:: Prefix string indents all summarized lines.
- #
- def summarize(sdone = {}, ldone = {}, width = 1, max = width - 1, indent = "")
- sopts, lopts = [], [], nil
- @short.each {|s| sdone.fetch(s) {sopts << s}; sdone[s] = true} if @short
- @long.each {|s| ldone.fetch(s) {lopts << s}; ldone[s] = true} if @long
- return if sopts.empty? and lopts.empty? # completely hidden
-
- left = [sopts.join(', ')]
- right = desc.dup
-
- while s = lopts.shift
- l = left[-1].length + s.length
- l += arg.length if left.size == 1 && arg
- l < max or sopts.empty? or left << +''
- left[-1] << (left[-1].empty? ? ' ' * 4 : ', ') << s
- end
-
- if arg
- left[0] << (left[1] ? arg.sub(/\A(\[?)=/, '\1') + ',' : arg)
- end
- mlen = left.collect {|ss| ss.length}.max.to_i
- while mlen > width and l = left.shift
- mlen = left.collect {|ss| ss.length}.max.to_i if l.length == mlen
- if l.length < width and (r = right[0]) and !r.empty?
- l = l.to_s.ljust(width) + ' ' + r
- right.shift
- end
- yield(indent + l)
- end
-
- while begin l = left.shift; r = right.shift; l or r end
- l = l.to_s.ljust(width) + ' ' + r if r and !r.empty?
- yield(indent + l)
- end
-
- self
- end
-
- def add_banner(to) # :nodoc:
- unless @short or @long
- s = desc.join
- to << " [" + s + "]..." unless s.empty?
- end
- to
- end
-
- def match_nonswitch?(str) # :nodoc:
- @pattern =~ str unless @short or @long
- end
-
- #
- # Main name of the switch.
- #
- def switch_name
- (long.first || short.first).sub(/\A-+(?:\[no-\])?/, '')
- end
-
- def compsys(sdone, ldone) # :nodoc:
- sopts, lopts = [], []
- @short.each {|s| sdone.fetch(s) {sopts << s}; sdone[s] = true} if @short
- @long.each {|s| ldone.fetch(s) {lopts << s}; ldone[s] = true} if @long
- return if sopts.empty? and lopts.empty? # completely hidden
-
- (sopts+lopts).each do |opt|
- # "(-x -c -r)-l[left justify]"
- if /^--\[no-\](.+)$/ =~ opt
- o = $1
- yield("--#{o}", desc.join(""))
- yield("--no-#{o}", desc.join(""))
- else
- yield("#{opt}", desc.join(""))
- end
- end
- end
-
- def pretty_print_contents(q) # :nodoc:
- if @block
- q.text ":" + @block.source_location.join(":") + ":"
- first = false
- else
- first = true
- end
- [@short, @long].each do |list|
- list.each do |opt|
- if first
- q.text ":"
- first = false
- end
- q.breakable
- q.text opt
- end
- end
- end
-
- def pretty_print(q) # :nodoc:
- q.object_group(self) {pretty_print_contents(q)}
- end
-
- #
- # Switch that takes no arguments.
- #
- class NoArgument < self
-
- #
- # Raises an exception if any arguments given.
- #
- def parse(arg, argv)
- yield(NeedlessArgument, arg) if arg
- conv_arg(arg)
- end
-
- def self.incompatible_argument_styles(*)
- end
-
- def self.pattern
- Object
- end
-
- def pretty_head # :nodoc:
- "NoArgument"
- end
- end
-
- #
- # Switch that takes an argument.
- #
- class RequiredArgument < self
-
- #
- # Raises an exception if argument is not present.
- #
- def parse(arg, argv)
- unless arg
- raise MissingArgument if argv.empty?
- arg = argv.shift
- end
- conv_arg(*parse_arg(arg, &method(:raise)))
- end
-
- def pretty_head # :nodoc:
- "Required"
- end
- end
-
- #
- # Switch that can omit argument.
- #
- class OptionalArgument < self
-
- #
- # Parses argument if given, or uses default value.
- #
- def parse(arg, argv, &error)
- if arg
- conv_arg(*parse_arg(arg, &error))
- else
- conv_arg(arg)
- end
- end
-
- def pretty_head # :nodoc:
- "Optional"
- end
- end
-
- #
- # Switch that takes an argument, which does not begin with '-' or is '-'.
- #
- class PlacedArgument < self
-
- #
- # Returns nil if argument is not present or begins with '-' and is not '-'.
- #
- def parse(arg, argv, &error)
- if !(val = arg) and (argv.empty? or /\A-./ =~ (val = argv[0]))
- return nil, block, nil
- end
- opt = (val = parse_arg(val, &error))[1]
- val = conv_arg(*val)
- if opt and !arg
- argv.shift
- else
- val[0] = nil
- end
- val
- end
-
- def pretty_head # :nodoc:
- "Placed"
- end
- end
- end
-
- #
- # Simple option list providing mapping from short and/or long option
- # string to Gem::OptionParser::Switch and mapping from acceptable argument to
- # matching pattern and converter pair. Also provides summary feature.
- #
- class List
- # Map from acceptable argument types to pattern and converter pairs.
- attr_reader :atype
-
- # Map from short style option switches to actual switch objects.
- attr_reader :short
-
- # Map from long style option switches to actual switch objects.
- attr_reader :long
-
- # List of all switches and summary string.
- attr_reader :list
-
- #
- # Just initializes all instance variables.
- #
- def initialize
- @atype = {}
- @short = OptionMap.new
- @long = OptionMap.new
- @list = []
- end
-
- def pretty_print(q) # :nodoc:
- q.group(1, "(", ")") do
- @list.each do |sw|
- next unless Switch === sw
- q.group(1, "(" + sw.pretty_head, ")") do
- sw.pretty_print_contents(q)
- end
- end
- end
- end
-
- #
- # See Gem::OptionParser.accept.
- #
- def accept(t, pat = /.*/m, &block)
- if pat
- pat.respond_to?(:match) or
- raise TypeError, "has no `match'", ParseError.filter_backtrace(caller(2))
- else
- pat = t if t.respond_to?(:match)
- end
- unless block
- block = pat.method(:convert).to_proc if pat.respond_to?(:convert)
- end
- @atype[t] = [pat, block]
- end
-
- #
- # See Gem::OptionParser.reject.
- #
- def reject(t)
- @atype.delete(t)
- end
-
- #
- # Adds +sw+ according to +sopts+, +lopts+ and +nlopts+.
- #
- # +sw+:: Gem::OptionParser::Switch instance to be added.
- # +sopts+:: Short style option list.
- # +lopts+:: Long style option list.
- # +nlopts+:: Negated long style options list.
- #
- def update(sw, sopts, lopts, nsw = nil, nlopts = nil) # :nodoc:
- sopts.each {|o| @short[o] = sw} if sopts
- lopts.each {|o| @long[o] = sw} if lopts
- nlopts.each {|o| @long[o] = nsw} if nsw and nlopts
- used = @short.invert.update(@long.invert)
- @list.delete_if {|o| Switch === o and !used[o]}
- end
- private :update
-
- #
- # Inserts +switch+ at the head of the list, and associates short, long
- # and negated long options. Arguments are:
- #
- # +switch+:: Gem::OptionParser::Switch instance to be inserted.
- # +short_opts+:: List of short style options.
- # +long_opts+:: List of long style options.
- # +nolong_opts+:: List of long style options with "no-" prefix.
- #
- # prepend(switch, short_opts, long_opts, nolong_opts)
- #
- def prepend(*args)
- update(*args)
- @list.unshift(args[0])
- end
-
- #
- # Appends +switch+ at the tail of the list, and associates short, long
- # and negated long options. Arguments are:
- #
- # +switch+:: Gem::OptionParser::Switch instance to be inserted.
- # +short_opts+:: List of short style options.
- # +long_opts+:: List of long style options.
- # +nolong_opts+:: List of long style options with "no-" prefix.
- #
- # append(switch, short_opts, long_opts, nolong_opts)
- #
- def append(*args)
- update(*args)
- @list.push(args[0])
- end
-
- #
- # Searches +key+ in +id+ list. The result is returned or yielded if a
- # block is given. If it isn't found, nil is returned.
- #
- def search(id, key)
- if list = __send__(id)
- val = list.fetch(key) {return nil}
- block_given? ? yield(val) : val
- end
- end
-
- #
- # Searches list +id+ for +opt+ and the optional patterns for completion
- # +pat+. If +icase+ is true, the search is case insensitive. The result
- # is returned or yielded if a block is given. If it isn't found, nil is
- # returned.
- #
- def complete(id, opt, icase = false, *pat, &block)
- __send__(id).complete(opt, icase, *pat, &block)
- end
-
- def get_candidates(id)
- yield __send__(id).keys
- end
-
- #
- # Iterates over each option, passing the option to the +block+.
- #
- def each_option(&block)
- list.each(&block)
- end
-
- #
- # Creates the summary table, passing each line to the +block+ (without
- # newline). The arguments +args+ are passed along to the summarize
- # method which is called on every option.
- #
- def summarize(*args, &block)
- sum = []
- list.reverse_each do |opt|
- if opt.respond_to?(:summarize) # perhaps Gem::OptionParser::Switch
- s = []
- opt.summarize(*args) {|l| s << l}
- sum.concat(s.reverse)
- elsif !opt or opt.empty?
- sum << ""
- elsif opt.respond_to?(:each_line)
- sum.concat([*opt.each_line].reverse)
- else
- sum.concat([*opt.each].reverse)
- end
- end
- sum.reverse_each(&block)
- end
-
- def add_banner(to) # :nodoc:
- list.each do |opt|
- if opt.respond_to?(:add_banner)
- opt.add_banner(to)
- end
- end
- to
- end
-
- def compsys(*args, &block) # :nodoc:
- list.each do |opt|
- if opt.respond_to?(:compsys)
- opt.compsys(*args, &block)
- end
- end
- end
- end
-
- #
- # Hash with completion search feature. See Gem::OptionParser::Completion.
- #
- class CompletingHash < Hash
- include Completion
-
- #
- # Completion for hash key.
- #
- def match(key)
- *values = fetch(key) {
- raise AmbiguousArgument, catch(:ambiguous) {return complete(key)}
- }
- return key, *values
- end
- end
-
- # :stopdoc:
-
- #
- # Enumeration of acceptable argument styles. Possible values are:
- #
- # NO_ARGUMENT:: The switch takes no arguments. (:NONE)
- # REQUIRED_ARGUMENT:: The switch requires an argument. (:REQUIRED)
- # OPTIONAL_ARGUMENT:: The switch requires an optional argument. (:OPTIONAL)
- #
- # Use like --switch=argument (long style) or -Xargument (short style). For
- # short style, only portion matched to argument pattern is treated as
- # argument.
- #
- ArgumentStyle = {}
- NoArgument.each {|el| ArgumentStyle[el] = Switch::NoArgument}
- RequiredArgument.each {|el| ArgumentStyle[el] = Switch::RequiredArgument}
- OptionalArgument.each {|el| ArgumentStyle[el] = Switch::OptionalArgument}
- ArgumentStyle.freeze
-
- #
- # Switches common used such as '--', and also provides default
- # argument classes
- #
- DefaultList = List.new
- DefaultList.short['-'] = Switch::NoArgument.new {}
- DefaultList.long[''] = Switch::NoArgument.new {throw :terminate}
-
-
- COMPSYS_HEADER = <<'XXX' # :nodoc:
-
-typeset -A opt_args
-local context state line
-
-_arguments -s -S \
-XXX
-
- def compsys(to, name = File.basename($0)) # :nodoc:
- to << "#compdef #{name}\n"
- to << COMPSYS_HEADER
- visit(:compsys, {}, {}) {|o, d|
- to << %Q[ "#{o}[#{d.gsub(/[\"\[\]]/, '\\\\\&')}]" \\\n]
- }
- to << " '*:file:_files' && return 0\n"
- end
-
- #
- # Default options for ARGV, which never appear in option summary.
- #
- Officious = {}
-
- #
- # --help
- # Shows option summary.
- #
- Officious['help'] = proc do |parser|
- Switch::NoArgument.new do |arg|
- puts parser.help
- exit
- end
- end
-
- #
- # --*-completion-bash=WORD
- # Shows candidates for command line completion.
- #
- Officious['*-completion-bash'] = proc do |parser|
- Switch::RequiredArgument.new do |arg|
- puts parser.candidate(arg)
- exit
- end
- end
-
- #
- # --*-completion-zsh[=NAME:FILE]
- # Creates zsh completion file.
- #
- Officious['*-completion-zsh'] = proc do |parser|
- Switch::OptionalArgument.new do |arg|
- parser.compsys(STDOUT, arg)
- exit
- end
- end
-
- #
- # --version
- # Shows version string if Version is defined.
- #
- Officious['version'] = proc do |parser|
- Switch::OptionalArgument.new do |pkg|
- if pkg
- begin
- require 'rubygems/optparse/lib/optparse/version'
- rescue LoadError
- else
- show_version(*pkg.split(/,/)) or
- abort("#{parser.program_name}: no version found in package #{pkg}")
- exit
- end
- end
- v = parser.ver or abort("#{parser.program_name}: version unknown")
- puts v
- exit
- end
- end
-
- # :startdoc:
-
- #
- # Class methods
- #
-
- #
- # Initializes a new instance and evaluates the optional block in context
- # of the instance. Arguments +args+ are passed to #new, see there for
- # description of parameters.
- #
- # This method is *deprecated*, its behavior corresponds to the older #new
- # method.
- #
- def self.with(*args, &block)
- opts = new(*args)
- opts.instance_eval(&block)
- opts
- end
-
- #
- # Returns an incremented value of +default+ according to +arg+.
- #
- def self.inc(arg, default = nil)
- case arg
- when Integer
- arg.nonzero?
- when nil
- default.to_i + 1
- end
- end
- def inc(*args)
- self.class.inc(*args)
- end
-
- #
- # Initializes the instance and yields itself if called with a block.
- #
- # +banner+:: Banner message.
- # +width+:: Summary width.
- # +indent+:: Summary indent.
- #
- def initialize(banner = nil, width = 32, indent = ' ' * 4)
- @stack = [DefaultList, List.new, List.new]
- @program_name = nil
- @banner = banner
- @summary_width = width
- @summary_indent = indent
- @default_argv = ARGV
- @require_exact = false
- @raise_unknown = true
- add_officious
- yield self if block_given?
- end
-
- def add_officious # :nodoc:
- list = base()
- Officious.each do |opt, block|
- list.long[opt] ||= block.call(self)
- end
- end
-
- #
- # Terminates option parsing. Optional parameter +arg+ is a string pushed
- # back to be the first non-option argument.
- #
- def terminate(arg = nil)
- self.class.terminate(arg)
- end
- def self.terminate(arg = nil)
- throw :terminate, arg
- end
-
- @stack = [DefaultList]
- def self.top() DefaultList end
-
- #
- # Directs to accept specified class +t+. The argument string is passed to
- # the block in which it should be converted to the desired class.
- #
- # +t+:: Argument class specifier, any object including Class.
- # +pat+:: Pattern for argument, defaults to +t+ if it responds to match.
- #
- # accept(t, pat, &block)
- #
- def accept(*args, &blk) top.accept(*args, &blk) end
- #
- # See #accept.
- #
- def self.accept(*args, &blk) top.accept(*args, &blk) end
-
- #
- # Directs to reject specified class argument.
- #
- # +t+:: Argument class specifier, any object including Class.
- #
- # reject(t)
- #
- def reject(*args, &blk) top.reject(*args, &blk) end
- #
- # See #reject.
- #
- def self.reject(*args, &blk) top.reject(*args, &blk) end
-
- #
- # Instance methods
- #
-
- # Heading banner preceding summary.
- attr_writer :banner
-
- # Program name to be emitted in error message and default banner,
- # defaults to $0.
- attr_writer :program_name
-
- # Width for option list portion of summary. Must be Numeric.
- attr_accessor :summary_width
-
- # Indentation for summary. Must be String (or have + String method).
- attr_accessor :summary_indent
-
- # Strings to be parsed in default.
- attr_accessor :default_argv
-
- # Whether to require that options match exactly (disallows providing
- # abbreviated long option as short option).
- attr_accessor :require_exact
-
- # Whether to raise at unknown option.
- attr_accessor :raise_unknown
-
- #
- # Heading banner preceding summary.
- #
- def banner
- unless @banner
- @banner = +"Usage: #{program_name} [options]"
- visit(:add_banner, @banner)
- end
- @banner
- end
-
- #
- # Program name to be emitted in error message and default banner, defaults
- # to $0.
- #
- def program_name
- @program_name || File.basename($0, '.*')
- end
-
- # for experimental cascading :-)
- alias set_banner banner=
- alias set_program_name program_name=
- alias set_summary_width summary_width=
- alias set_summary_indent summary_indent=
-
- # Version
- attr_writer :version
- # Release code
- attr_writer :release
-
- #
- # Version
- #
- def version
- (defined?(@version) && @version) || (defined?(::Version) && ::Version)
- end
-
- #
- # Release code
- #
- def release
- (defined?(@release) && @release) || (defined?(::Release) && ::Release) || (defined?(::RELEASE) && ::RELEASE)
- end
-
- #
- # Returns version string from program_name, version and release.
- #
- def ver
- if v = version
- str = +"#{program_name} #{[v].join('.')}"
- str << " (#{v})" if v = release
- str
- end
- end
-
- def warn(mesg = $!)
- super("#{program_name}: #{mesg}")
- end
-
- def abort(mesg = $!)
- super("#{program_name}: #{mesg}")
- end
-
- #
- # Subject of #on / #on_head, #accept / #reject
- #
- def top
- @stack[-1]
- end
-
- #
- # Subject of #on_tail.
- #
- def base
- @stack[1]
- end
-
- #
- # Pushes a new List.
- #
- def new
- @stack.push(List.new)
- if block_given?
- yield self
- else
- self
- end
- end
-
- #
- # Removes the last List.
- #
- def remove
- @stack.pop
- end
-
- #
- # Puts option summary into +to+ and returns +to+. Yields each line if
- # a block is given.
- #
- # +to+:: Output destination, which must have method <<. Defaults to [].
- # +width+:: Width of left side, defaults to @summary_width.
- # +max+:: Maximum length allowed for left side, defaults to +width+ - 1.
- # +indent+:: Indentation, defaults to @summary_indent.
- #
- def summarize(to = [], width = @summary_width, max = width - 1, indent = @summary_indent, &blk)
- nl = "\n"
- blk ||= proc {|l| to << (l.index(nl, -1) ? l : l + nl)}
- visit(:summarize, {}, {}, width, max, indent, &blk)
- to
- end
-
- #
- # Returns option summary string.
- #
- def help; summarize("#{banner}".sub(/\n?\z/, "\n")) end
- alias to_s help
-
- def pretty_print(q) # :nodoc:
- q.object_group(self) do
- first = true
- if @stack.size > 2
- @stack.each_with_index do |s, i|
- next if i < 2
- next if s.list.empty?
- if first
- first = false
- q.text ":"
- end
- q.breakable
- s.pretty_print(q)
- end
- end
- end
- end
-
- def inspect # :nodoc:
- require 'pp'
- pretty_print_inspect
- end
-
- #
- # Returns option summary list.
- #
- def to_a; summarize("#{banner}".split(/^/)) end
-
- #
- # Checks if an argument is given twice, in which case an ArgumentError is
- # raised. Called from Gem::OptionParser#switch only.
- #
- # +obj+:: New argument.
- # +prv+:: Previously specified argument.
- # +msg+:: Exception message.
- #
- def notwice(obj, prv, msg) # :nodoc:
- unless !prv or prv == obj
- raise(ArgumentError, "argument #{msg} given twice: #{obj}",
- ParseError.filter_backtrace(caller(2)))
- end
- obj
- end
- private :notwice
-
- SPLAT_PROC = proc {|*a| a.length <= 1 ? a.first : a} # :nodoc:
-
- # :call-seq:
- # make_switch(params, block = nil)
- #
- # :include: ../doc/optparse/creates_option.rdoc
- #
- def make_switch(opts, block = nil)
- short, long, nolong, style, pattern, conv, not_pattern, not_conv, not_style = [], [], []
- ldesc, sdesc, desc, arg = [], [], []
- default_style = Switch::NoArgument
- default_pattern = nil
- klass = nil
- q, a = nil
- has_arg = false
-
- opts.each do |o|
- # argument class
- next if search(:atype, o) do |pat, c|
- klass = notwice(o, klass, 'type')
- if not_style and not_style != Switch::NoArgument
- not_pattern, not_conv = pat, c
- else
- default_pattern, conv = pat, c
- end
- end
-
- # directly specified pattern(any object possible to match)
- if (!(String === o || Symbol === o)) and o.respond_to?(:match)
- pattern = notwice(o, pattern, 'pattern')
- if pattern.respond_to?(:convert)
- conv = pattern.method(:convert).to_proc
- else
- conv = SPLAT_PROC
- end
- next
- end
-
- # anything others
- case o
- when Proc, Method
- block = notwice(o, block, 'block')
- when Array, Hash
- case pattern
- when CompletingHash
- when nil
- pattern = CompletingHash.new
- conv = pattern.method(:convert).to_proc if pattern.respond_to?(:convert)
- else
- raise ArgumentError, "argument pattern given twice"
- end
- o.each {|pat, *v| pattern[pat] = v.fetch(0) {pat}}
- when Module
- raise ArgumentError, "unsupported argument type: #{o}", ParseError.filter_backtrace(caller(4))
- when *ArgumentStyle.keys
- style = notwice(ArgumentStyle[o], style, 'style')
- when /^--no-([^\[\]=\s]*)(.+)?/
- q, a = $1, $2
- o = notwice(a ? Object : TrueClass, klass, 'type')
- not_pattern, not_conv = search(:atype, o) unless not_style
- not_style = (not_style || default_style).guess(arg = a) if a
- default_style = Switch::NoArgument
- default_pattern, conv = search(:atype, FalseClass) unless default_pattern
- ldesc << "--no-#{q}"
- (q = q.downcase).tr!('_', '-')
- long << "no-#{q}"
- nolong << q
- when /^--\[no-\]([^\[\]=\s]*)(.+)?/
- q, a = $1, $2
- o = notwice(a ? Object : TrueClass, klass, 'type')
- if a
- default_style = default_style.guess(arg = a)
- default_pattern, conv = search(:atype, o) unless default_pattern
- end
- ldesc << "--[no-]#{q}"
- (o = q.downcase).tr!('_', '-')
- long << o
- not_pattern, not_conv = search(:atype, FalseClass) unless not_style
- not_style = Switch::NoArgument
- nolong << "no-#{o}"
- when /^--([^\[\]=\s]*)(.+)?/
- q, a = $1, $2
- if a
- o = notwice(NilClass, klass, 'type')
- default_style = default_style.guess(arg = a)
- default_pattern, conv = search(:atype, o) unless default_pattern
- end
- ldesc << "--#{q}"
- (o = q.downcase).tr!('_', '-')
- long << o
- when /^-(\[\^?\]?(?:[^\\\]]|\\.)*\])(.+)?/
- q, a = $1, $2
- o = notwice(Object, klass, 'type')
- if a
- default_style = default_style.guess(arg = a)
- default_pattern, conv = search(:atype, o) unless default_pattern
- else
- has_arg = true
- end
- sdesc << "-#{q}"
- short << Regexp.new(q)
- when /^-(.)(.+)?/
- q, a = $1, $2
- if a
- o = notwice(NilClass, klass, 'type')
- default_style = default_style.guess(arg = a)
- default_pattern, conv = search(:atype, o) unless default_pattern
- end
- sdesc << "-#{q}"
- short << q
- when /^=/
- style = notwice(default_style.guess(arg = o), style, 'style')
- default_pattern, conv = search(:atype, Object) unless default_pattern
- else
- desc.push(o) if o && !o.empty?
- end
- end
-
- default_pattern, conv = search(:atype, default_style.pattern) unless default_pattern
- if !(short.empty? and long.empty?)
- if has_arg and default_style == Switch::NoArgument
- default_style = Switch::RequiredArgument
- end
- s = (style || default_style).new(pattern || default_pattern,
- conv, sdesc, ldesc, arg, desc, block)
- elsif !block
- if style or pattern
- raise ArgumentError, "no switch given", ParseError.filter_backtrace(caller)
- end
- s = desc
- else
- short << pattern
- s = (style || default_style).new(pattern,
- conv, nil, nil, arg, desc, block)
- end
- return s, short, long,
- (not_style.new(not_pattern, not_conv, sdesc, ldesc, nil, desc, block) if not_style),
- nolong
- end
-
- # :call-seq:
- # define(*params, &block)
- #
- # :include: ../doc/optparse/creates_option.rdoc
- #
- def define(*opts, &block)
- top.append(*(sw = make_switch(opts, block)))
- sw[0]
- end
-
- # :call-seq:
- # on(*params, &block)
- #
- # :include: ../doc/optparse/creates_option.rdoc
- #
- def on(*opts, &block)
- define(*opts, &block)
- self
- end
- alias def_option define
-
- # :call-seq:
- # define_head(*params, &block)
- #
- # :include: ../doc/optparse/creates_option.rdoc
- #
- def define_head(*opts, &block)
- top.prepend(*(sw = make_switch(opts, block)))
- sw[0]
- end
-
- # :call-seq:
- # on_head(*params, &block)
- #
- # :include: ../doc/optparse/creates_option.rdoc
- #
- # The new option is added at the head of the summary.
- #
- def on_head(*opts, &block)
- define_head(*opts, &block)
- self
- end
- alias def_head_option define_head
-
- # :call-seq:
- # define_tail(*params, &block)
- #
- # :include: ../doc/optparse/creates_option.rdoc
- #
- def define_tail(*opts, &block)
- base.append(*(sw = make_switch(opts, block)))
- sw[0]
- end
-
- #
- # :call-seq:
- # on_tail(*params, &block)
- #
- # :include: ../doc/optparse/creates_option.rdoc
- #
- # The new option is added at the tail of the summary.
- #
- def on_tail(*opts, &block)
- define_tail(*opts, &block)
- self
- end
- alias def_tail_option define_tail
-
- #
- # Add separator in summary.
- #
- def separator(string)
- top.append(string, nil, nil)
- end
-
- #
- # Parses command line arguments +argv+ in order. When a block is given,
- # each non-option argument is yielded. When optional +into+ keyword
- # argument is provided, the parsed option values are stored there via
- # <code>[]=</code> method (so it can be Hash, or OpenStruct, or other
- # similar object).
- #
- # Returns the rest of +argv+ left unparsed.
- #
- def order(*argv, into: nil, &nonopt)
- argv = argv[0].dup if argv.size == 1 and Array === argv[0]
- order!(argv, into: into, &nonopt)
- end
-
- #
- # Same as #order, but removes switches destructively.
- # Non-option arguments remain in +argv+.
- #
- def order!(argv = default_argv, into: nil, &nonopt)
- setter = ->(name, val) {into[name.to_sym] = val} if into
- parse_in_order(argv, setter, &nonopt)
- end
-
- def parse_in_order(argv = default_argv, setter = nil, &nonopt) # :nodoc:
- opt, arg, val, rest = nil
- nonopt ||= proc {|a| throw :terminate, a}
- argv.unshift(arg) if arg = catch(:terminate) {
- while arg = argv.shift
- case arg
- # long option
- when /\A--([^=]*)(?:=(.*))?/m
- opt, rest = $1, $2
- opt.tr!('_', '-')
- begin
- sw, = complete(:long, opt, true)
- if require_exact && !sw.long.include?(arg)
- throw :terminate, arg unless raise_unknown
- raise InvalidOption, arg
- end
- rescue ParseError
- throw :terminate, arg unless raise_unknown
- raise $!.set_option(arg, true)
- end
- begin
- opt, cb, val = sw.parse(rest, argv) {|*exc| raise(*exc)}
- val = cb.call(val) if cb
- setter.call(sw.switch_name, val) if setter
- rescue ParseError
- raise $!.set_option(arg, rest)
- end
-
- # short option
- when /\A-(.)((=).*|.+)?/m
- eq, rest, opt = $3, $2, $1
- has_arg, val = eq, rest
- begin
- sw, = search(:short, opt)
- unless sw
- begin
- sw, = complete(:short, opt)
- # short option matched.
- val = arg.delete_prefix('-')
- has_arg = true
- rescue InvalidOption
- raise if require_exact
- # if no short options match, try completion with long
- # options.
- sw, = complete(:long, opt)
- eq ||= !rest
- end
- end
- rescue ParseError
- throw :terminate, arg unless raise_unknown
- raise $!.set_option(arg, true)
- end
- begin
- opt, cb, val = sw.parse(val, argv) {|*exc| raise(*exc) if eq}
- rescue ParseError
- raise $!.set_option(arg, arg.length > 2)
- else
- raise InvalidOption, arg if has_arg and !eq and arg == "-#{opt}"
- end
- begin
- argv.unshift(opt) if opt and (!rest or (opt = opt.sub(/\A-*/, '-')) != '-')
- val = cb.call(val) if cb
- setter.call(sw.switch_name, val) if setter
- rescue ParseError
- raise $!.set_option(arg, arg.length > 2)
- end
-
- # non-option argument
- else
- catch(:prune) do
- visit(:each_option) do |sw0|
- sw = sw0
- sw.block.call(arg) if Switch === sw and sw.match_nonswitch?(arg)
- end
- nonopt.call(arg)
- end
- end
- end
-
- nil
- }
-
- visit(:search, :short, nil) {|sw| sw.block.call(*argv) if !sw.pattern}
-
- argv
- end
- private :parse_in_order
-
- #
- # Parses command line arguments +argv+ in permutation mode and returns
- # list of non-option arguments. When optional +into+ keyword
- # argument is provided, the parsed option values are stored there via
- # <code>[]=</code> method (so it can be Hash, or OpenStruct, or other
- # similar object).
- #
- def permute(*argv, into: nil)
- argv = argv[0].dup if argv.size == 1 and Array === argv[0]
- permute!(argv, into: into)
- end
-
- #
- # Same as #permute, but removes switches destructively.
- # Non-option arguments remain in +argv+.
- #
- def permute!(argv = default_argv, into: nil)
- nonopts = []
- order!(argv, into: into, &nonopts.method(:<<))
- argv[0, 0] = nonopts
- argv
- end
-
- #
- # Parses command line arguments +argv+ in order when environment variable
- # POSIXLY_CORRECT is set, and in permutation mode otherwise.
- # When optional +into+ keyword argument is provided, the parsed option
- # values are stored there via <code>[]=</code> method (so it can be Hash,
- # or OpenStruct, or other similar object).
- #
- def parse(*argv, into: nil)
- argv = argv[0].dup if argv.size == 1 and Array === argv[0]
- parse!(argv, into: into)
- end
-
- #
- # Same as #parse, but removes switches destructively.
- # Non-option arguments remain in +argv+.
- #
- def parse!(argv = default_argv, into: nil)
- if ENV.include?('POSIXLY_CORRECT')
- order!(argv, into: into)
- else
- permute!(argv, into: into)
- end
- end
-
- #
- # Wrapper method for getopts.rb.
- #
- # params = ARGV.getopts("ab:", "foo", "bar:", "zot:Z;zot option")
- # # params["a"] = true # -a
- # # params["b"] = "1" # -b1
- # # params["foo"] = "1" # --foo
- # # params["bar"] = "x" # --bar x
- # # params["zot"] = "z" # --zot Z
- #
- def getopts(*args)
- argv = Array === args.first ? args.shift : default_argv
- single_options, *long_options = *args
-
- result = {}
-
- single_options.scan(/(.)(:)?/) do |opt, val|
- if val
- result[opt] = nil
- define("-#{opt} VAL")
- else
- result[opt] = false
- define("-#{opt}")
- end
- end if single_options
-
- long_options.each do |arg|
- arg, desc = arg.split(';', 2)
- opt, val = arg.split(':', 2)
- if val
- result[opt] = val.empty? ? nil : val
- define("--#{opt}=#{result[opt] || "VAL"}", *[desc].compact)
- else
- result[opt] = false
- define("--#{opt}", *[desc].compact)
- end
- end
-
- parse_in_order(argv, result.method(:[]=))
- result
- end
-
- #
- # See #getopts.
- #
- def self.getopts(*args)
- new.getopts(*args)
- end
-
- #
- # Traverses @stack, sending each element method +id+ with +args+ and
- # +block+.
- #
- def visit(id, *args, &block) # :nodoc:
- @stack.reverse_each do |el|
- el.__send__(id, *args, &block)
- end
- nil
- end
- private :visit
-
- #
- # Searches +key+ in @stack for +id+ hash and returns or yields the result.
- #
- def search(id, key) # :nodoc:
- block_given = block_given?
- visit(:search, id, key) do |k|
- return block_given ? yield(k) : k
- end
- end
- private :search
-
- #
- # Completes shortened long style option switch and returns pair of
- # canonical switch and switch descriptor Gem::OptionParser::Switch.
- #
- # +typ+:: Searching table.
- # +opt+:: Searching key.
- # +icase+:: Search case insensitive if true.
- # +pat+:: Optional pattern for completion.
- #
- def complete(typ, opt, icase = false, *pat) # :nodoc:
- if pat.empty?
- search(typ, opt) {|sw| return [sw, opt]} # exact match or...
- end
- ambiguous = catch(:ambiguous) {
- visit(:complete, typ, opt, icase, *pat) {|o, *sw| return sw}
- }
- exc = ambiguous ? AmbiguousOption : InvalidOption
- raise exc.new(opt, additional: self.method(:additional_message).curry[typ])
- end
- private :complete
-
- #
- # Returns additional info.
- #
- def additional_message(typ, opt)
- return unless typ and opt and defined?(DidYouMean::SpellChecker)
- all_candidates = []
- visit(:get_candidates, typ) do |candidates|
- all_candidates.concat(candidates)
- end
- all_candidates.select! {|cand| cand.is_a?(String) }
- checker = DidYouMean::SpellChecker.new(dictionary: all_candidates)
- DidYouMean.formatter.message_for(all_candidates & checker.correct(opt))
- end
-
- def candidate(word)
- list = []
- case word
- when '-'
- long = short = true
- when /\A--/
- word, arg = word.split(/=/, 2)
- argpat = Completion.regexp(arg, false) if arg and !arg.empty?
- long = true
- when /\A-/
- short = true
- end
- pat = Completion.regexp(word, long)
- visit(:each_option) do |opt|
- next unless Switch === opt
- opts = (long ? opt.long : []) + (short ? opt.short : [])
- opts = Completion.candidate(word, true, pat, &opts.method(:each)).map(&:first) if pat
- if /\A=/ =~ opt.arg
- opts.map! {|sw| sw + "="}
- if arg and CompletingHash === opt.pattern
- if opts = opt.pattern.candidate(arg, false, argpat)
- opts.map!(&:last)
- end
- end
- end
- list.concat(opts)
- end
- list
- end
-
- #
- # Loads options from file names as +filename+. Does nothing when the file
- # is not present. Returns whether successfully loaded.
- #
- # +filename+ defaults to basename of the program without suffix in a
- # directory ~/.options, then the basename with '.options' suffix
- # under XDG and Haiku standard places.
- #
- # The optional +into+ keyword argument works exactly like that accepted in
- # method #parse.
- #
- def load(filename = nil, into: nil)
- unless filename
- basename = File.basename($0, '.*')
- return true if load(File.expand_path(basename, '~/.options'), into: into) rescue nil
- basename << ".options"
- return [
- # XDG
- ENV['XDG_CONFIG_HOME'],
- '~/.config',
- *ENV['XDG_CONFIG_DIRS']&.split(File::PATH_SEPARATOR),
-
- # Haiku
- '~/config/settings',
- ].any? {|dir|
- next if !dir or dir.empty?
- load(File.expand_path(basename, dir), into: into) rescue nil
- }
- end
- begin
- parse(*File.readlines(filename, chomp: true), into: into)
- true
- rescue Errno::ENOENT, Errno::ENOTDIR
- false
- end
- end
-
- #
- # Parses environment variable +env+ or its uppercase with splitting like a
- # shell.
- #
- # +env+ defaults to the basename of the program.
- #
- def environment(env = File.basename($0, '.*'))
- env = ENV[env] || ENV[env.upcase] or return
- require 'shellwords'
- parse(*Shellwords.shellwords(env))
- end
-
- #
- # Acceptable argument classes
- #
-
- #
- # Any string and no conversion. This is fall-back.
- #
- accept(Object) {|s,|s or s.nil?}
-
- accept(NilClass) {|s,|s}
-
- #
- # Any non-empty string, and no conversion.
- #
- accept(String, /.+/m) {|s,*|s}
-
- #
- # Ruby/C-like integer, octal for 0-7 sequence, binary for 0b, hexadecimal
- # for 0x, and decimal for others; with optional sign prefix. Converts to
- # Integer.
- #
- decimal = '\d+(?:_\d+)*'
- binary = 'b[01]+(?:_[01]+)*'
- hex = 'x[\da-f]+(?:_[\da-f]+)*'
- octal = "0(?:[0-7]+(?:_[0-7]+)*|#{binary}|#{hex})?"
- integer = "#{octal}|#{decimal}"
-
- accept(Integer, %r"\A[-+]?(?:#{integer})\z"io) {|s,|
- begin
- Integer(s)
- rescue ArgumentError
- raise Gem::OptionParser::InvalidArgument, s
- end if s
- }
-
- #
- # Float number format, and converts to Float.
- #
- float = "(?:#{decimal}(?=(.)?)(?:\\.(?:#{decimal})?)?|\\.#{decimal})(?:E[-+]?#{decimal})?"
- floatpat = %r"\A[-+]?#{float}\z"io
- accept(Float, floatpat) {|s,| s.to_f if s}
-
- #
- # Generic numeric format, converts to Integer for integer format, Float
- # for float format, and Rational for rational format.
- #
- real = "[-+]?(?:#{octal}|#{float})"
- accept(Numeric, /\A(#{real})(?:\/(#{real}))?\z/io) {|s, d, f, n,|
- if n
- Rational(d, n)
- elsif f
- Float(s)
- else
- Integer(s)
- end
- }
-
- #
- # Decimal integer format, to be converted to Integer.
- #
- DecimalInteger = /\A[-+]?#{decimal}\z/io
- accept(DecimalInteger, DecimalInteger) {|s,|
- begin
- Integer(s, 10)
- rescue ArgumentError
- raise Gem::OptionParser::InvalidArgument, s
- end if s
- }
-
- #
- # Ruby/C like octal/hexadecimal/binary integer format, to be converted to
- # Integer.
- #
- OctalInteger = /\A[-+]?(?:[0-7]+(?:_[0-7]+)*|0(?:#{binary}|#{hex}))\z/io
- accept(OctalInteger, OctalInteger) {|s,|
- begin
- Integer(s, 8)
- rescue ArgumentError
- raise Gem::OptionParser::InvalidArgument, s
- end if s
- }
-
- #
- # Decimal integer/float number format, to be converted to Integer for
- # integer format, Float for float format.
- #
- DecimalNumeric = floatpat # decimal integer is allowed as float also.
- accept(DecimalNumeric, floatpat) {|s, f|
- begin
- if f
- Float(s)
- else
- Integer(s)
- end
- rescue ArgumentError
- raise Gem::OptionParser::InvalidArgument, s
- end if s
- }
-
- #
- # Boolean switch, which means whether it is present or not, whether it is
- # absent or not with prefix no-, or it takes an argument
- # yes/no/true/false/+/-.
- #
- yesno = CompletingHash.new
- %w[- no false].each {|el| yesno[el] = false}
- %w[+ yes true].each {|el| yesno[el] = true}
- yesno['nil'] = false # should be nil?
- accept(TrueClass, yesno) {|arg, val| val == nil or val}
- #
- # Similar to TrueClass, but defaults to false.
- #
- accept(FalseClass, yesno) {|arg, val| val != nil and val}
-
- #
- # List of strings separated by ",".
- #
- accept(Array) do |s, |
- if s
- s = s.split(',').collect {|ss| ss unless ss.empty?}
- end
- s
- end
-
- #
- # Regular expression with options.
- #
- accept(Regexp, %r"\A/((?:\\.|[^\\])*)/([[:alpha:]]+)?\z|.*") do |all, s, o|
- f = 0
- if o
- f |= Regexp::IGNORECASE if /i/ =~ o
- f |= Regexp::MULTILINE if /m/ =~ o
- f |= Regexp::EXTENDED if /x/ =~ o
- k = o.delete("imx")
- k = nil if k.empty?
- end
- Regexp.new(s || all, f, k)
- end
-
- #
- # Exceptions
- #
-
- #
- # Base class of exceptions from Gem::OptionParser.
- #
- class ParseError < RuntimeError
- # Reason which caused the error.
- Reason = 'parse error'
-
- def initialize(*args, additional: nil)
- @additional = additional
- @arg0, = args
- @args = args
- @reason = nil
- end
-
- attr_reader :args
- attr_writer :reason
- attr_accessor :additional
-
- #
- # Pushes back erred argument(s) to +argv+.
- #
- def recover(argv)
- argv[0, 0] = @args
- argv
- end
-
- def self.filter_backtrace(array)
- unless $DEBUG
- array.delete_if(&%r"\A#{Regexp.quote(__FILE__)}:"o.method(:=~))
- end
- array
- end
-
- def set_backtrace(array)
- super(self.class.filter_backtrace(array))
- end
-
- def set_option(opt, eq)
- if eq
- @args[0] = opt
- else
- @args.unshift(opt)
- end
- self
- end
-
- #
- # Returns error reason. Override this for I18N.
- #
- def reason
- @reason || self.class::Reason
- end
-
- def inspect
- "#<#{self.class}: #{args.join(' ')}>"
- end
-
- #
- # Default stringizing method to emit standard error message.
- #
- def message
- "#{reason}: #{args.join(' ')}#{additional[@arg0] if additional}"
- end
-
- alias to_s message
- end
-
- #
- # Raises when ambiguously completable string is encountered.
- #
- class AmbiguousOption < ParseError
- const_set(:Reason, 'ambiguous option')
- end
-
- #
- # Raises when there is an argument for a switch which takes no argument.
- #
- class NeedlessArgument < ParseError
- const_set(:Reason, 'needless argument')
- end
-
- #
- # Raises when a switch with mandatory argument has no argument.
- #
- class MissingArgument < ParseError
- const_set(:Reason, 'missing argument')
- end
-
- #
- # Raises when switch is undefined.
- #
- class InvalidOption < ParseError
- const_set(:Reason, 'invalid option')
- end
-
- #
- # Raises when the given argument does not match required format.
- #
- class InvalidArgument < ParseError
- const_set(:Reason, 'invalid argument')
- end
-
- #
- # Raises when the given argument word can't be completed uniquely.
- #
- class AmbiguousArgument < InvalidArgument
- const_set(:Reason, 'ambiguous argument')
- end
-
- #
- # Miscellaneous
- #
-
- #
- # Extends command line arguments array (ARGV) to parse itself.
- #
- module Arguable
-
- #
- # Sets Gem::OptionParser object, when +opt+ is +false+ or +nil+, methods
- # Gem::OptionParser::Arguable#options and Gem::OptionParser::Arguable#options= are
- # undefined. Thus, there is no ways to access the Gem::OptionParser object
- # via the receiver object.
- #
- def options=(opt)
- unless @optparse = opt
- class << self
- undef_method(:options)
- undef_method(:options=)
- end
- end
- end
-
- #
- # Actual Gem::OptionParser object, automatically created if nonexistent.
- #
- # If called with a block, yields the Gem::OptionParser object and returns the
- # result of the block. If an Gem::OptionParser::ParseError exception occurs
- # in the block, it is rescued, a error message printed to STDERR and
- # +nil+ returned.
- #
- def options
- @optparse ||= Gem::OptionParser.new
- @optparse.default_argv = self
- block_given? or return @optparse
- begin
- yield @optparse
- rescue ParseError
- @optparse.warn $!
- nil
- end
- end
-
- #
- # Parses +self+ destructively in order and returns +self+ containing the
- # rest arguments left unparsed.
- #
- def order!(&blk) options.order!(self, &blk) end
-
- #
- # Parses +self+ destructively in permutation mode and returns +self+
- # containing the rest arguments left unparsed.
- #
- def permute!() options.permute!(self) end
-
- #
- # Parses +self+ destructively and returns +self+ containing the
- # rest arguments left unparsed.
- #
- def parse!() options.parse!(self) end
-
- #
- # Substitution of getopts is possible as follows. Also see
- # Gem::OptionParser#getopts.
- #
- # def getopts(*args)
- # ($OPT = ARGV.getopts(*args)).each do |opt, val|
- # eval "$OPT_#{opt.gsub(/[^A-Za-z0-9_]/, '_')} = val"
- # end
- # rescue Gem::OptionParser::ParseError
- # end
- #
- def getopts(*args)
- options.getopts(self, *args)
- end
-
- #
- # Initializes instance variable.
- #
- def self.extend_object(obj)
- super
- obj.instance_eval {@optparse = nil}
- end
- def initialize(*args)
- super
- @optparse = nil
- end
- end
-
- #
- # Acceptable argument classes. Now contains DecimalInteger, OctalInteger
- # and DecimalNumeric. See Acceptable argument classes (in source code).
- #
- module Acceptables
- const_set(:DecimalInteger, Gem::OptionParser::DecimalInteger)
- const_set(:OctalInteger, Gem::OptionParser::OctalInteger)
- const_set(:DecimalNumeric, Gem::OptionParser::DecimalNumeric)
- end
-end
-
-# ARGV is arguable by Gem::OptionParser
-ARGV.extend(Gem::OptionParser::Arguable)
diff --git a/lib/rubygems/optparse/lib/optparse/uri.rb b/lib/rubygems/optparse/lib/optparse/uri.rb
deleted file mode 100644
index 664d7f2af4..0000000000
--- a/lib/rubygems/optparse/lib/optparse/uri.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# frozen_string_literal: false
-# -*- ruby -*-
-
-require_relative '../optparse'
-require 'uri'
-
-Gem::OptionParser.accept(URI) {|s,| URI.parse(s) if s}
diff --git a/lib/rubygems/package.rb b/lib/rubygems/package.rb
index 61cae75357..9b2829c894 100644
--- a/lib/rubygems/package.rb
+++ b/lib/rubygems/package.rb
@@ -7,7 +7,6 @@
# rubocop:enable Style/AsciiComments
-require_relative "../rubygems"
require_relative "security"
require_relative "user_interaction"
@@ -59,7 +58,7 @@ class Gem::Package
def initialize(message, source = nil)
if source
- @path = source.path
+ @path = source.is_a?(String) ? source : source.path
message += " in #{path}" if path
end
@@ -155,7 +154,7 @@ class Gem::Package
Gem::Package::FileSource.new gem
end
- return super unless Gem::Package == self
+ return super unless self == Gem::Package
return super unless gem.present?
return super unless gem.start
@@ -268,7 +267,7 @@ class Gem::Package
tar.add_file_simple file, stat.mode, stat.size do |dst_io|
File.open file, "rb" do |src_io|
- dst_io.write src_io.read 16_384 until src_io.eof?
+ copy_stream(src_io, dst_io)
end
end
end
@@ -347,6 +346,8 @@ EOM
return @contents
end
end
+ rescue Zlib::GzipFile::Error, EOFError, Gem::Package::TarInvalidError => e
+ raise Gem::Package::FormatError.new e.message, @gem
end
##
@@ -355,18 +356,21 @@ EOM
def digest(entry) # :nodoc:
algorithms = if @checksums
- @checksums.keys
- else
- [Gem::Security::DIGEST_NAME].compact
+ @checksums.to_h {|algorithm, _| [algorithm, Gem::Security.create_digest(algorithm)] }
+ elsif Gem::Security::DIGEST_NAME
+ { Gem::Security::DIGEST_NAME => Gem::Security.create_digest(Gem::Security::DIGEST_NAME) }
end
- algorithms.each do |algorithm|
- digester = Gem::Security.create_digest(algorithm)
+ return @digests if algorithms.nil? || algorithms.empty?
- digester << entry.read(16_384) until entry.eof?
-
- entry.rewind
+ buf = String.new(capacity: 16_384, encoding: Encoding::BINARY)
+ until entry.eof?
+ entry.readpartial(16_384, buf)
+ algorithms.each_value {|digester| digester << buf }
+ end
+ entry.rewind
+ algorithms.each do |algorithm, digester|
@digests[algorithm][entry.full_name] = digester
end
@@ -382,7 +386,7 @@ EOM
def extract_files(destination_dir, pattern = "*")
verify unless @spec
- FileUtils.mkdir_p destination_dir, :mode => dir_mode && 0o755
+ FileUtils.mkdir_p destination_dir, mode: dir_mode && 0o755
@gem.with_read_io do |io|
reader = Gem::Package::TarReader.new io
@@ -395,6 +399,8 @@ EOM
break # ignore further entries
end
end
+ rescue Zlib::GzipFile::Error, EOFError, Gem::Package::TarInvalidError => e
+ raise Gem::Package::FormatError.new e.message, @gem
end
##
@@ -409,6 +415,8 @@ EOM
# extracted.
def extract_tar_gz(io, destination_dir, pattern = "*") # :nodoc:
+ destination_dir = File.realpath(destination_dir)
+
directories = []
symlinks = []
@@ -431,8 +439,6 @@ EOM
FileUtils.rm_rf destination
- mkdir_options = {}
- mkdir_options[:mode] = dir_mode ? 0o755 : (entry.header.mode if entry.directory?)
mkdir =
if entry.directory?
destination
@@ -441,13 +447,13 @@ EOM
end
unless directories.include?(mkdir)
- FileUtils.mkdir_p mkdir, **mkdir_options
+ FileUtils.mkdir_p mkdir, mode: dir_mode ? 0o755 : (entry.header.mode if entry.directory?)
directories << mkdir
end
if entry.file?
- File.open(destination, "wb") {|out| out.write entry.read }
- FileUtils.chmod file_mode(entry.header.mode), destination
+ File.open(destination, "wb") {|out| copy_stream(entry, out) }
+ FileUtils.chmod file_mode(entry.header.mode) & ~File.umask, destination
end
verbose destination
@@ -506,7 +512,6 @@ EOM
raise Gem::Package::PathError.new(destination, destination_dir) unless
normalize_path(destination).start_with? normalize_path(destination_dir + "/")
- destination.tap(&Gem::UNTAINT)
destination
end
@@ -522,12 +527,13 @@ EOM
# Loads a Gem::Specification from the TarEntry +entry+
def load_spec(entry) # :nodoc:
+ limit = 10 * 1024 * 1024
case entry.full_name
when "metadata" then
- @spec = Gem::Specification.from_yaml entry.read
+ @spec = Gem::Specification.from_yaml limit_read(entry, "metadata", limit)
when "metadata.gz" then
Zlib::GzipReader.wrap(entry, external_encoding: Encoding::UTF_8) do |gzio|
- @spec = Gem::Specification.from_yaml gzio.read
+ @spec = Gem::Specification.from_yaml limit_read(gzio, "metadata.gz", limit)
end
end
end
@@ -551,7 +557,7 @@ EOM
@checksums = gem.seek "checksums.yaml.gz" do |entry|
Zlib::GzipReader.wrap entry do |gz_io|
- Gem::SafeYAML.safe_load gz_io.read
+ Gem::SafeYAML.safe_load limit_read(gz_io, "checksums.yaml.gz", 10 * 1024 * 1024)
end
end
end
@@ -626,7 +632,7 @@ EOM
raise
rescue Errno::ENOENT => e
raise Gem::Package::FormatError.new e.message
- rescue Gem::Package::TarInvalidError => e
+ rescue Zlib::GzipFile::Error, EOFError, Gem::Package::TarInvalidError => e
raise Gem::Package::FormatError.new e.message, @gem
end
@@ -658,7 +664,7 @@ EOM
case file_name
when /\.sig$/ then
- @signatures[$`] = entry.read if @security_policy
+ @signatures[$`] = limit_read(entry, file_name, 1024 * 1024) if @security_policy
return
else
digest entry
@@ -702,11 +708,28 @@ EOM
def verify_gz(entry) # :nodoc:
Zlib::GzipReader.wrap entry do |gzio|
+ # TODO: read into a buffer once zlib supports it
gzio.read 16_384 until gzio.eof? # gzip checksum verification
end
rescue Zlib::GzipFile::Error => e
raise Gem::Package::FormatError.new(e.message, entry.full_name)
end
+
+ if RUBY_ENGINE == "truffleruby"
+ def copy_stream(src, dst) # :nodoc:
+ dst.write src.read
+ end
+ else
+ def copy_stream(src, dst) # :nodoc:
+ IO.copy_stream(src, dst)
+ end
+ end
+
+ def limit_read(io, name, limit)
+ bytes = io.read(limit + 1)
+ raise Gem::Package::FormatError, "#{name} is too big (over #{limit} bytes)" if bytes.size > limit
+ bytes
+ end
end
require_relative "package/digest_io"
diff --git a/lib/rubygems/package/old.rb b/lib/rubygems/package/old.rb
index 41f6986b8f..1a13ac3e29 100644
--- a/lib/rubygems/package/old.rb
+++ b/lib/rubygems/package/old.rb
@@ -70,7 +70,7 @@ class Gem::Package::Old < Gem::Package
file_data << line
end
- file_data = file_data.strip.unpack("m")[0]
+ file_data = file_data.strip.unpack1("m")
file_data = Zlib::Inflate.inflate file_data
raise Gem::Package::FormatError, "#{full_name} in #{@gem} is corrupt" if
@@ -78,7 +78,7 @@ class Gem::Package::Old < Gem::Package
FileUtils.rm_rf destination
- FileUtils.mkdir_p File.dirname(destination), :mode => dir_mode && 0o755
+ FileUtils.mkdir_p File.dirname(destination), mode: dir_mode && 0o755
File.open destination, "wb", file_mode(entry["mode"]) do |out|
out.write file_data
diff --git a/lib/rubygems/package/tar_header.rb b/lib/rubygems/package/tar_header.rb
index 062a943942..087f13f6c9 100644
--- a/lib/rubygems/package/tar_header.rb
+++ b/lib/rubygems/package/tar_header.rb
@@ -102,32 +102,33 @@ class Gem::Package::TarHeader
def self.from(stream)
header = stream.read 512
- empty = (EMPTY_HEADER == header)
+ empty = (header == EMPTY_HEADER)
fields = header.unpack UNPACK_FORMAT
- new :name => fields.shift,
- :mode => strict_oct(fields.shift),
- :uid => oct_or_256based(fields.shift),
- :gid => oct_or_256based(fields.shift),
- :size => strict_oct(fields.shift),
- :mtime => strict_oct(fields.shift),
- :checksum => strict_oct(fields.shift),
- :typeflag => fields.shift,
- :linkname => fields.shift,
- :magic => fields.shift,
- :version => strict_oct(fields.shift),
- :uname => fields.shift,
- :gname => fields.shift,
- :devmajor => strict_oct(fields.shift),
- :devminor => strict_oct(fields.shift),
- :prefix => fields.shift,
-
- :empty => empty
+ new name: fields.shift,
+ mode: strict_oct(fields.shift),
+ uid: oct_or_256based(fields.shift),
+ gid: oct_or_256based(fields.shift),
+ size: strict_oct(fields.shift),
+ mtime: strict_oct(fields.shift),
+ checksum: strict_oct(fields.shift),
+ typeflag: fields.shift,
+ linkname: fields.shift,
+ magic: fields.shift,
+ version: strict_oct(fields.shift),
+ uname: fields.shift,
+ gname: fields.shift,
+ devmajor: strict_oct(fields.shift),
+ devminor: strict_oct(fields.shift),
+ prefix: fields.shift,
+
+ empty: empty
end
def self.strict_oct(str)
- return str.strip.oct if /\A[0-7]*\z/.match?(str.strip)
+ str.strip!
+ return str.oct if /\A[0-7]*\z/.match?(str)
raise ArgumentError, "#{str.inspect} is not an octal string"
end
@@ -137,7 +138,8 @@ class Gem::Package::TarHeader
# \ff flags a negative 256-based number
# In case we have a match, parse it as a signed binary value
# in big-endian order, except that the high-order bit is ignored.
- return str.unpack("N2").last if /\A[\x80\xff]/n.match?(str)
+
+ return str.unpack1("@4N") if /\A[\x80\xff]/n.match?(str)
strict_oct(str)
end
@@ -149,21 +151,23 @@ class Gem::Package::TarHeader
raise ArgumentError, ":name, :size, :prefix and :mode required"
end
- vals[:uid] ||= 0
- vals[:gid] ||= 0
- vals[:mtime] ||= 0
- vals[:checksum] ||= ""
- vals[:typeflag] = "0" if vals[:typeflag].nil? || vals[:typeflag].empty?
- vals[:magic] ||= "ustar"
- vals[:version] ||= "00"
- vals[:uname] ||= "wheel"
- vals[:gname] ||= "wheel"
- vals[:devmajor] ||= 0
- vals[:devminor] ||= 0
-
- FIELDS.each do |name|
- instance_variable_set "@#{name}", vals[name]
- end
+ @checksum = vals[:checksum] || ""
+ @devmajor = vals[:devmajor] || 0
+ @devminor = vals[:devminor] || 0
+ @gid = vals[:gid] || 0
+ @gname = vals[:gname] || "wheel"
+ @linkname = vals[:linkname]
+ @magic = vals[:magic] || "ustar"
+ @mode = vals[:mode]
+ @mtime = vals[:mtime] || 0
+ @name = vals[:name]
+ @prefix = vals[:prefix]
+ @size = vals[:size]
+ @typeflag = vals[:typeflag]
+ @typeflag = "0" if @typeflag.nil? || @typeflag.empty?
+ @uid = vals[:uid] || 0
+ @uname = vals[:uname] || "wheel"
+ @version = vals[:version] || "00"
@empty = vals[:empty]
end
diff --git a/lib/rubygems/package/tar_reader.rb b/lib/rubygems/package/tar_reader.rb
index b12e83a703..25f9b2f945 100644
--- a/lib/rubygems/package/tar_reader.rb
+++ b/lib/rubygems/package/tar_reader.rb
@@ -14,11 +14,6 @@ class Gem::Package::TarReader
include Enumerable
##
- # Raised if the tar IO is not seekable
-
- class UnexpectedEOF < StandardError; end
-
- ##
# Creates a new TarReader on +io+ and yields it to the block, if given.
def self.new(io)
@@ -57,7 +52,14 @@ class Gem::Package::TarReader
return enum_for __method__ unless block_given?
until @io.eof? do
- header = Gem::Package::TarHeader.from @io
+ begin
+ header = Gem::Package::TarHeader.from @io
+ rescue ArgumentError => e
+ # Specialize only exceptions from Gem::Package::TarHeader.strict_oct
+ raise e unless e.message.match?(/ is not an octal string$/)
+ raise Gem::Package::TarInvalidError, e.message
+ end
+
return if header.empty?
entry = Gem::Package::TarReader::Entry.new header, @io
yield entry
diff --git a/lib/rubygems/package/tar_reader/entry.rb b/lib/rubygems/package/tar_reader/entry.rb
index e22efa95b3..5e9d9af5c6 100644
--- a/lib/rubygems/package/tar_reader/entry.rb
+++ b/lib/rubygems/package/tar_reader/entry.rb
@@ -102,9 +102,7 @@ class Gem::Package::TarReader::Entry
# Read one byte from the tar entry
def getc
- check_closed
-
- return nil if @read >= @header.size
+ return nil if eof?
ret = @io.getc
@read += 1 if ret
@@ -156,30 +154,28 @@ class Gem::Package::TarReader::Entry
alias_method :length, :size
##
- # Reads +len+ bytes from the tar file entry, or the rest of the entry if
- # nil
-
- def read(len = nil)
- check_closed
-
- len ||= @header.size - @read
+ # Reads +maxlen+ bytes from the tar file entry, or the rest of the entry if nil
- return nil if len > 0 && @read >= @header.size
+ def read(maxlen = nil)
+ if eof?
+ return maxlen.to_i.zero? ? "" : nil
+ end
- max_read = [len, @header.size - @read].min
+ max_read = [maxlen, @header.size - @read].compact.min
ret = @io.read max_read
+ if ret.nil?
+ return maxlen ? nil : "" # IO.read returns nil on EOF with len argument
+ end
@read += ret.size
ret
end
- def readpartial(maxlen = nil, outbuf = "".b)
- check_closed
-
- maxlen ||= @header.size - @read
-
- raise EOFError if maxlen > 0 && @read >= @header.size
+ def readpartial(maxlen, outbuf = "".b)
+ if eof? && maxlen > 0
+ raise EOFError, "end of file reached"
+ end
max_read = [maxlen, @header.size - @read].min
@@ -213,6 +209,8 @@ class Gem::Package::TarReader::Entry
pending = new_pos - @io.pos
+ return 0 if pending == 0
+
if @io.respond_to?(:seek)
begin
# avoid reading if the @io supports seeking
@@ -230,8 +228,8 @@ class Gem::Package::TarReader::Entry
end
while pending > 0 do
- size_read = @io.read([pending, 4096].min).size
- raise UnexpectedEOF if @io.eof?
+ size_read = @io.read([pending, 4096].min)&.size
+ raise(EOFError, "end of file reached") if size_read.nil?
pending -= size_read
end
diff --git a/lib/rubygems/package/tar_writer.rb b/lib/rubygems/package/tar_writer.rb
index e06ec25961..b24bdb63e7 100644
--- a/lib/rubygems/package/tar_writer.rb
+++ b/lib/rubygems/package/tar_writer.rb
@@ -116,9 +116,9 @@ class Gem::Package::TarWriter
final_pos = @io.pos
@io.pos = init_pos
- header = Gem::Package::TarHeader.new :name => name, :mode => mode,
- :size => size, :prefix => prefix,
- :mtime => Gem.source_date_epoch
+ header = Gem::Package::TarHeader.new name: name, mode: mode,
+ size: size, prefix: prefix,
+ mtime: Gem.source_date_epoch
@io.write header
@io.pos = final_pos
@@ -209,9 +209,9 @@ class Gem::Package::TarWriter
name, prefix = split_name name
- header = Gem::Package::TarHeader.new(:name => name, :mode => mode,
- :size => size, :prefix => prefix,
- :mtime => Gem.source_date_epoch).to_s
+ header = Gem::Package::TarHeader.new(name: name, mode: mode,
+ size: size, prefix: prefix,
+ mtime: Gem.source_date_epoch).to_s
@io.write header
os = BoundedStream.new @io, size
@@ -235,11 +235,11 @@ class Gem::Package::TarWriter
name, prefix = split_name name
- header = Gem::Package::TarHeader.new(:name => name, :mode => mode,
- :size => 0, :typeflag => "2",
- :linkname => target,
- :prefix => prefix,
- :mtime => Gem.source_date_epoch).to_s
+ header = Gem::Package::TarHeader.new(name: name, mode: mode,
+ size: 0, typeflag: "2",
+ linkname: target,
+ prefix: prefix,
+ mtime: Gem.source_date_epoch).to_s
@io.write header
@@ -289,10 +289,10 @@ class Gem::Package::TarWriter
name, prefix = split_name(name)
- header = Gem::Package::TarHeader.new :name => name, :mode => mode,
- :typeflag => "5", :size => 0,
- :prefix => prefix,
- :mtime => Gem.source_date_epoch
+ header = Gem::Package::TarHeader.new name: name, mode: mode,
+ typeflag: "5", size: 0,
+ prefix: prefix,
+ mtime: Gem.source_date_epoch
@io.write header
diff --git a/lib/rubygems/package_task.rb b/lib/rubygems/package_task.rb
index a67d8cb916..d26411684d 100644
--- a/lib/rubygems/package_task.rb
+++ b/lib/rubygems/package_task.rb
@@ -97,13 +97,13 @@ class Gem::PackageTask < Rake::PackageTask
gem_path = File.join package_dir, gem_file
gem_dir = File.join package_dir, gem_spec.full_name
- task :package => [:gem]
+ task package: [:gem]
directory package_dir
directory gem_dir
desc "Build the gem file #{gem_file}"
- task :gem => [gem_path]
+ task gem: [gem_path]
trace = Rake.application.options.trace
Gem.configuration.verbose = trace
diff --git a/lib/rubygems/path_support.rb b/lib/rubygems/path_support.rb
index b596b4f6b8..13091e29ba 100644
--- a/lib/rubygems/path_support.rb
+++ b/lib/rubygems/path_support.rb
@@ -24,23 +24,22 @@ class Gem::PathSupport
# hashtable, or defaults to ENV, the system environment.
#
def initialize(env)
- @home = env["GEM_HOME"] || Gem.default_dir
-
- if File::ALT_SEPARATOR
- @home = @home.gsub(File::ALT_SEPARATOR, File::SEPARATOR)
- end
-
- @home = expand(@home)
-
+ @home = normalize_home_dir(env["GEM_HOME"] || Gem.default_dir)
@path = split_gem_path env["GEM_PATH"], @home
@spec_cache_dir = env["GEM_SPEC_CACHE"] || Gem.default_spec_cache_dir
-
- @spec_cache_dir = @spec_cache_dir.dup.tap(&Gem::UNTAINT)
end
private
+ def normalize_home_dir(home)
+ if File::ALT_SEPARATOR
+ home = home.gsub(File::ALT_SEPARATOR, File::SEPARATOR)
+ end
+
+ expand(home)
+ end
+
##
# Split the Gem search path (as reported by Gem.path).
diff --git a/lib/rubygems/platform.rb b/lib/rubygems/platform.rb
index cccfa06ae9..48b7344aee 100644
--- a/lib/rubygems/platform.rb
+++ b/lib/rubygems/platform.rb
@@ -13,15 +13,22 @@ class Gem::Platform
attr_accessor :cpu, :os, :version
def self.local
- arch = RbConfig::CONFIG["arch"]
- arch = "#{arch}_60" if /mswin(?:32|64)$/.match?(arch)
- @local ||= new(arch)
+ @local ||= begin
+ arch = RbConfig::CONFIG["arch"]
+ arch = "#{arch}_60" if /mswin(?:32|64)$/.match?(arch)
+ new(arch)
+ end
end
def self.match(platform)
match_platforms?(platform, Gem.platforms)
end
+ class << self
+ extend Gem::Deprecate
+ rubygems_deprecate :match, "Gem::Platform.match_spec? or match_gem?"
+ end
+
def self.match_platforms?(platform, platforms)
platform = Gem::Platform.new(platform) unless platform.is_a?(Gem::Platform)
platforms.any? do |local_platform|
@@ -36,10 +43,20 @@ class Gem::Platform
match_gem?(spec.platform, spec.name)
end
- def self.match_gem?(platform, gem_name)
- # NOTE: this method might be redefined by Ruby implementations to
- # customize behavior per RUBY_ENGINE, gem_name or other criteria.
- match_platforms?(platform, Gem.platforms)
+ if RUBY_ENGINE == "truffleruby"
+ def self.match_gem?(platform, gem_name)
+ raise "Not a string: #{gem_name.inspect}" unless String === gem_name
+
+ if REUSE_AS_BINARY_ON_TRUFFLERUBY.include?(gem_name)
+ match_platforms?(platform, [Gem::Platform::RUBY, Gem::Platform.local])
+ else
+ match_platforms?(platform, Gem.platforms)
+ end
+ end
+ else
+ def self.match_gem?(platform, gem_name)
+ match_platforms?(platform, Gem.platforms)
+ end
end
def self.sort_priority(platform)
@@ -72,7 +89,7 @@ class Gem::Platform
when String then
arch = arch.split "-"
- if arch.length > 2 && arch.last !~ /\d+(\.\d+)?$/ # reassemble x86-linux-{libc}
+ if arch.length > 2 && !arch.last.match?(/\d+(\.\d+)?$/) # reassemble x86-linux-{libc}
extra = arch.pop
arch.last << "-#{extra}"
end
@@ -84,7 +101,7 @@ class Gem::Platform
else cpu
end
- if arch.length == 2 && arch.last =~ /^\d+(\.\d+)?$/ # for command-line
+ if arch.length == 2 && arch.last.match?(/^\d+(\.\d+)?$/) # for command-line
@os, @version = arch
return
end
diff --git a/lib/rubygems/psych_tree.rb b/lib/rubygems/psych_tree.rb
index 2d478c94d9..24857adb9d 100644
--- a/lib/rubygems/psych_tree.rb
+++ b/lib/rubygems/psych_tree.rb
@@ -14,6 +14,10 @@ module Gem
@emitter.scalar str, nil, nil, false, true, quote
end
+ def visit_Hash(o)
+ super(o.compact)
+ end
+
# Noop this out so there are no anchors
def register(target, obj)
end
diff --git a/lib/rubygems/query_utils.rb b/lib/rubygems/query_utils.rb
index 9a6a736461..a95a759401 100644
--- a/lib/rubygems/query_utils.rb
+++ b/lib/rubygems/query_utils.rb
@@ -311,8 +311,8 @@ module Gem::QueryUtils
label = "Installed at"
specs.each do |s|
version = s.version.to_s
- version << ", default" if s.default_gem?
- entry << "\n" << " #{label} (#{version}): #{s.base_dir}"
+ default = ", default" if s.default_gem?
+ entry << "\n" << " #{label} (#{version}#{default}): #{s.base_dir}"
label = " " * label.length
end
end
diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb
index f9f8c45817..c3a41592f6 100644
--- a/lib/rubygems/remote_fetcher.rb
+++ b/lib/rubygems/remote_fetcher.rb
@@ -74,9 +74,9 @@ class Gem::RemoteFetcher
def initialize(proxy=nil, dns=nil, headers={})
require_relative "core_ext/tcpsocket_init" if Gem.configuration.ipv4_fallback_enabled
- require "net/http"
+ require_relative "vendored_net_http"
require "stringio"
- require "uri"
+ require_relative "vendor/uri/lib/uri"
Socket.do_not_reverse_lookup = true
@@ -135,7 +135,7 @@ class Gem::RemoteFetcher
scheme = source_uri.scheme
- # URI.parse gets confused by MS Windows paths with forward slashes.
+ # Gem::URI.parse gets confused by MS Windows paths with forward slashes.
scheme = nil if /^[a-z]$/i.match?(scheme)
# REFACTOR: split this up and dispatch on scheme (eg download_http)
@@ -210,17 +210,17 @@ class Gem::RemoteFetcher
# HTTP Fetcher. Dispatched by +fetch_path+. Use it instead.
def fetch_http(uri, last_modified = nil, head = false, depth = 0)
- fetch_type = head ? Net::HTTP::Head : Net::HTTP::Get
+ fetch_type = head ? Gem::Net::HTTP::Head : Gem::Net::HTTP::Get
response = request uri, fetch_type, last_modified do |req|
headers.each {|k,v| req.add_field(k,v) }
end
case response
- when Net::HTTPOK, Net::HTTPNotModified then
+ when Gem::Net::HTTPOK, Gem::Net::HTTPNotModified then
response.uri = uri
head ? response : response.body
- when Net::HTTPMovedPermanently, Net::HTTPFound, Net::HTTPSeeOther,
- Net::HTTPTemporaryRedirect then
+ when Gem::Net::HTTPMovedPermanently, Gem::Net::HTTPFound, Gem::Net::HTTPSeeOther,
+ Gem::Net::HTTPTemporaryRedirect then
raise FetchError.new("too many redirects", uri) if depth > 10
unless location = response["Location"]
@@ -261,7 +261,7 @@ class Gem::RemoteFetcher
end
data
- rescue Timeout::Error, IOError, SocketError, SystemCallError,
+ rescue Gem::Timeout::Error, IOError, SocketError, SystemCallError,
*(OpenSSL::SSL::SSLError if Gem::HAVE_OPENSSL) => e
raise FetchError.new("#{e.class}: #{e}", uri)
end
@@ -305,8 +305,8 @@ class Gem::RemoteFetcher
end
##
- # Performs a Net::HTTP request of type +request_class+ on +uri+ returning
- # a Net::HTTP response object. request maintains a table of persistent
+ # Performs a Gem::Net::HTTP request of type +request_class+ on +uri+ returning
+ # a Gem::Net::HTTP response object. request maintains a table of persistent
# connections to reduce connect overhead.
def request(uri, request_class, last_modified = nil)
diff --git a/lib/rubygems/request.rb b/lib/rubygems/request.rb
index 619a5934b1..9116785231 100644
--- a/lib/rubygems/request.rb
+++ b/lib/rubygems/request.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require "net/http"
+require_relative "vendored_net_http"
require_relative "user_interaction"
class Gem::Request
@@ -18,11 +18,11 @@ class Gem::Request
end
def self.proxy_uri(proxy) # :nodoc:
- require "uri"
+ require_relative "vendor/uri/lib/uri"
case proxy
when :no_proxy then nil
- when URI::HTTP then proxy
- else URI.parse(proxy)
+ when Gem::URI::HTTP then proxy
+ else Gem::URI.parse(proxy)
end
end
@@ -30,7 +30,7 @@ class Gem::Request
@uri = uri
@request_class = request_class
@last_modified = last_modified
- @requests = Hash.new 0
+ @requests = Hash.new(0).compare_by_identity
@user_agent = user_agent
@connection_pool = pool
@@ -176,7 +176,7 @@ class Gem::Request
end
require "uri"
- uri = URI(Gem::UriFormatter.new(env_proxy).normalize)
+ uri = Gem::URI(Gem::UriFormatter.new(env_proxy).normalize)
if uri && uri.user.nil? && uri.password.nil?
user = ENV["#{downcase_scheme}_proxy_user"] || ENV["#{upcase_scheme}_PROXY_USER"]
@@ -196,7 +196,7 @@ class Gem::Request
bad_response = false
begin
- @requests[connection.object_id] += 1
+ @requests[connection] += 1
verbose "#{request.method} #{Gem::Uri.redact(@uri)}"
@@ -205,7 +205,7 @@ class Gem::Request
if request.response_body_permitted? && file_name =~ /\.gem$/
reporter = ui.download_reporter
response = connection.request(request) do |incomplete_response|
- if Net::HTTPOK === incomplete_response
+ if Gem::Net::HTTPOK === incomplete_response
reporter.fetch(file_name, incomplete_response.content_length)
downloaded = 0
data = String.new
@@ -228,7 +228,7 @@ class Gem::Request
end
verbose "#{response.code} #{response.message}"
- rescue Net::HTTPBadResponse
+ rescue Gem::Net::HTTPBadResponse
verbose "bad response"
reset connection
@@ -237,17 +237,17 @@ class Gem::Request
bad_response = true
retry
- rescue Net::HTTPFatalError
+ rescue Gem::Net::HTTPFatalError
verbose "fatal error"
raise Gem::RemoteFetcher::FetchError.new("fatal error", @uri)
- # HACK: work around EOFError bug in Net::HTTP
+ # HACK: work around EOFError bug in Gem::Net::HTTP
# NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible
# to install gems.
- rescue EOFError, Timeout::Error,
+ rescue EOFError, Gem::Timeout::Error,
Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE
- requests = @requests[connection.object_id]
+ requests = @requests[connection]
verbose "connection reset after #{requests} requests, retrying"
raise Gem::RemoteFetcher::FetchError.new("too many connection resets", @uri) if retried
@@ -267,7 +267,7 @@ class Gem::Request
# Resets HTTP connection +connection+.
def reset(connection)
- @requests.delete connection.object_id
+ @requests.delete connection
connection.finish
connection.start
diff --git a/lib/rubygems/request/connection_pools.rb b/lib/rubygems/request/connection_pools.rb
index b7ad92f5c6..6c1b04ab65 100644
--- a/lib/rubygems/request/connection_pools.rb
+++ b/lib/rubygems/request/connection_pools.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class Gem::Request::ConnectionPools # :nodoc:
- @client = Net::HTTP
+ @client = Gem::Net::HTTP
class << self
attr_accessor :client
diff --git a/lib/rubygems/request_set.rb b/lib/rubygems/request_set.rb
index 46e9cc346c..875df7e019 100644
--- a/lib/rubygems/request_set.rb
+++ b/lib/rubygems/request_set.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require_relative "tsort"
+require_relative "vendored_tsort"
##
# A RequestSet groups a request to activate a set of dependencies.
@@ -324,7 +324,7 @@ class Gem::RequestSet
@git_set.root_dir = @install_dir
- lock_file = "#{File.expand_path(path)}.lock".dup.tap(&Gem::UNTAINT)
+ lock_file = "#{File.expand_path(path)}.lock"
begin
tokenizer = Gem::RequestSet::Lockfile::Tokenizer.from_file lock_file
parser = tokenizer.make_parser self, []
diff --git a/lib/rubygems/request_set/gem_dependency_api.rb b/lib/rubygems/request_set/gem_dependency_api.rb
index a7b3c78f6d..4347d22ccb 100644
--- a/lib/rubygems/request_set/gem_dependency_api.rb
+++ b/lib/rubygems/request_set/gem_dependency_api.rb
@@ -33,22 +33,22 @@
class Gem::RequestSet::GemDependencyAPI
ENGINE_MAP = { # :nodoc:
- :jruby => %w[jruby],
- :jruby_18 => %w[jruby],
- :jruby_19 => %w[jruby],
- :maglev => %w[maglev],
- :mri => %w[ruby],
- :mri_18 => %w[ruby],
- :mri_19 => %w[ruby],
- :mri_20 => %w[ruby],
- :mri_21 => %w[ruby],
- :rbx => %w[rbx],
- :truffleruby => %w[truffleruby],
- :ruby => %w[ruby rbx maglev truffleruby],
- :ruby_18 => %w[ruby rbx maglev truffleruby],
- :ruby_19 => %w[ruby rbx maglev truffleruby],
- :ruby_20 => %w[ruby rbx maglev truffleruby],
- :ruby_21 => %w[ruby rbx maglev truffleruby],
+ jruby: %w[jruby],
+ jruby_18: %w[jruby],
+ jruby_19: %w[jruby],
+ maglev: %w[maglev],
+ mri: %w[ruby],
+ mri_18: %w[ruby],
+ mri_19: %w[ruby],
+ mri_20: %w[ruby],
+ mri_21: %w[ruby],
+ rbx: %w[rbx],
+ truffleruby: %w[truffleruby],
+ ruby: %w[ruby rbx maglev truffleruby],
+ ruby_18: %w[ruby rbx maglev truffleruby],
+ ruby_19: %w[ruby rbx maglev truffleruby],
+ ruby_20: %w[ruby rbx maglev truffleruby],
+ ruby_21: %w[ruby rbx maglev truffleruby],
}.freeze
mswin = Gem::Platform.new "x86-mswin32"
@@ -57,39 +57,39 @@ class Gem::RequestSet::GemDependencyAPI
x64_mingw = Gem::Platform.new "x64-mingw32"
PLATFORM_MAP = { # :nodoc:
- :jruby => Gem::Platform::RUBY,
- :jruby_18 => Gem::Platform::RUBY,
- :jruby_19 => Gem::Platform::RUBY,
- :maglev => Gem::Platform::RUBY,
- :mingw => x86_mingw,
- :mingw_18 => x86_mingw,
- :mingw_19 => x86_mingw,
- :mingw_20 => x86_mingw,
- :mingw_21 => x86_mingw,
- :mri => Gem::Platform::RUBY,
- :mri_18 => Gem::Platform::RUBY,
- :mri_19 => Gem::Platform::RUBY,
- :mri_20 => Gem::Platform::RUBY,
- :mri_21 => Gem::Platform::RUBY,
- :mswin => mswin,
- :mswin_18 => mswin,
- :mswin_19 => mswin,
- :mswin_20 => mswin,
- :mswin_21 => mswin,
- :mswin64 => mswin64,
- :mswin64_19 => mswin64,
- :mswin64_20 => mswin64,
- :mswin64_21 => mswin64,
- :rbx => Gem::Platform::RUBY,
- :ruby => Gem::Platform::RUBY,
- :ruby_18 => Gem::Platform::RUBY,
- :ruby_19 => Gem::Platform::RUBY,
- :ruby_20 => Gem::Platform::RUBY,
- :ruby_21 => Gem::Platform::RUBY,
- :truffleruby => Gem::Platform::RUBY,
- :x64_mingw => x64_mingw,
- :x64_mingw_20 => x64_mingw,
- :x64_mingw_21 => x64_mingw,
+ jruby: Gem::Platform::RUBY,
+ jruby_18: Gem::Platform::RUBY,
+ jruby_19: Gem::Platform::RUBY,
+ maglev: Gem::Platform::RUBY,
+ mingw: x86_mingw,
+ mingw_18: x86_mingw,
+ mingw_19: x86_mingw,
+ mingw_20: x86_mingw,
+ mingw_21: x86_mingw,
+ mri: Gem::Platform::RUBY,
+ mri_18: Gem::Platform::RUBY,
+ mri_19: Gem::Platform::RUBY,
+ mri_20: Gem::Platform::RUBY,
+ mri_21: Gem::Platform::RUBY,
+ mswin: mswin,
+ mswin_18: mswin,
+ mswin_19: mswin,
+ mswin_20: mswin,
+ mswin_21: mswin,
+ mswin64: mswin64,
+ mswin64_19: mswin64,
+ mswin64_20: mswin64,
+ mswin64_21: mswin64,
+ rbx: Gem::Platform::RUBY,
+ ruby: Gem::Platform::RUBY,
+ ruby_18: Gem::Platform::RUBY,
+ ruby_19: Gem::Platform::RUBY,
+ ruby_20: Gem::Platform::RUBY,
+ ruby_21: Gem::Platform::RUBY,
+ truffleruby: Gem::Platform::RUBY,
+ x64_mingw: x64_mingw,
+ x64_mingw_20: x64_mingw,
+ x64_mingw_21: x64_mingw,
}.freeze
gt_eq_0 = Gem::Requirement.new ">= 0"
@@ -99,70 +99,70 @@ class Gem::RequestSet::GemDependencyAPI
tilde_gt_2_1_0 = Gem::Requirement.new "~> 2.1.0"
VERSION_MAP = { # :nodoc:
- :jruby => gt_eq_0,
- :jruby_18 => tilde_gt_1_8_0,
- :jruby_19 => tilde_gt_1_9_0,
- :maglev => gt_eq_0,
- :mingw => gt_eq_0,
- :mingw_18 => tilde_gt_1_8_0,
- :mingw_19 => tilde_gt_1_9_0,
- :mingw_20 => tilde_gt_2_0_0,
- :mingw_21 => tilde_gt_2_1_0,
- :mri => gt_eq_0,
- :mri_18 => tilde_gt_1_8_0,
- :mri_19 => tilde_gt_1_9_0,
- :mri_20 => tilde_gt_2_0_0,
- :mri_21 => tilde_gt_2_1_0,
- :mswin => gt_eq_0,
- :mswin_18 => tilde_gt_1_8_0,
- :mswin_19 => tilde_gt_1_9_0,
- :mswin_20 => tilde_gt_2_0_0,
- :mswin_21 => tilde_gt_2_1_0,
- :mswin64 => gt_eq_0,
- :mswin64_19 => tilde_gt_1_9_0,
- :mswin64_20 => tilde_gt_2_0_0,
- :mswin64_21 => tilde_gt_2_1_0,
- :rbx => gt_eq_0,
- :ruby => gt_eq_0,
- :ruby_18 => tilde_gt_1_8_0,
- :ruby_19 => tilde_gt_1_9_0,
- :ruby_20 => tilde_gt_2_0_0,
- :ruby_21 => tilde_gt_2_1_0,
- :truffleruby => gt_eq_0,
- :x64_mingw => gt_eq_0,
- :x64_mingw_20 => tilde_gt_2_0_0,
- :x64_mingw_21 => tilde_gt_2_1_0,
+ jruby: gt_eq_0,
+ jruby_18: tilde_gt_1_8_0,
+ jruby_19: tilde_gt_1_9_0,
+ maglev: gt_eq_0,
+ mingw: gt_eq_0,
+ mingw_18: tilde_gt_1_8_0,
+ mingw_19: tilde_gt_1_9_0,
+ mingw_20: tilde_gt_2_0_0,
+ mingw_21: tilde_gt_2_1_0,
+ mri: gt_eq_0,
+ mri_18: tilde_gt_1_8_0,
+ mri_19: tilde_gt_1_9_0,
+ mri_20: tilde_gt_2_0_0,
+ mri_21: tilde_gt_2_1_0,
+ mswin: gt_eq_0,
+ mswin_18: tilde_gt_1_8_0,
+ mswin_19: tilde_gt_1_9_0,
+ mswin_20: tilde_gt_2_0_0,
+ mswin_21: tilde_gt_2_1_0,
+ mswin64: gt_eq_0,
+ mswin64_19: tilde_gt_1_9_0,
+ mswin64_20: tilde_gt_2_0_0,
+ mswin64_21: tilde_gt_2_1_0,
+ rbx: gt_eq_0,
+ ruby: gt_eq_0,
+ ruby_18: tilde_gt_1_8_0,
+ ruby_19: tilde_gt_1_9_0,
+ ruby_20: tilde_gt_2_0_0,
+ ruby_21: tilde_gt_2_1_0,
+ truffleruby: gt_eq_0,
+ x64_mingw: gt_eq_0,
+ x64_mingw_20: tilde_gt_2_0_0,
+ x64_mingw_21: tilde_gt_2_1_0,
}.freeze
WINDOWS = { # :nodoc:
- :mingw => :only,
- :mingw_18 => :only,
- :mingw_19 => :only,
- :mingw_20 => :only,
- :mingw_21 => :only,
- :mri => :never,
- :mri_18 => :never,
- :mri_19 => :never,
- :mri_20 => :never,
- :mri_21 => :never,
- :mswin => :only,
- :mswin_18 => :only,
- :mswin_19 => :only,
- :mswin_20 => :only,
- :mswin_21 => :only,
- :mswin64 => :only,
- :mswin64_19 => :only,
- :mswin64_20 => :only,
- :mswin64_21 => :only,
- :rbx => :never,
- :ruby => :never,
- :ruby_18 => :never,
- :ruby_19 => :never,
- :ruby_20 => :never,
- :ruby_21 => :never,
- :x64_mingw => :only,
- :x64_mingw_20 => :only,
- :x64_mingw_21 => :only,
+ mingw: :only,
+ mingw_18: :only,
+ mingw_19: :only,
+ mingw_20: :only,
+ mingw_21: :only,
+ mri: :never,
+ mri_18: :never,
+ mri_19: :never,
+ mri_20: :never,
+ mri_21: :never,
+ mswin: :only,
+ mswin_18: :only,
+ mswin_19: :only,
+ mswin_20: :only,
+ mswin_21: :only,
+ mswin64: :only,
+ mswin64_19: :only,
+ mswin64_20: :only,
+ mswin64_21: :only,
+ rbx: :never,
+ ruby: :never,
+ ruby_18: :never,
+ ruby_19: :never,
+ ruby_20: :never,
+ ruby_21: :never,
+ x64_mingw: :only,
+ x64_mingw_20: :only,
+ x64_mingw_21: :only,
}.freeze
##
@@ -280,7 +280,7 @@ class Gem::RequestSet::GemDependencyAPI
# Loads the gem dependency file and returns self.
def load
- instance_eval File.read(@path).tap(&Gem::UNTAINT), @path, 1
+ instance_eval File.read(@path), @path, 1
self
end
@@ -790,7 +790,7 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
return true if @installing
- unless RUBY_VERSION == version
+ unless version == RUBY_VERSION
message = "Your Ruby version is #{RUBY_VERSION}, " \
"but your #{gem_deps_file} requires #{version}"
diff --git a/lib/rubygems/request_set/lockfile.rb b/lib/rubygems/request_set/lockfile.rb
index d9ddc43734..c446b3ae51 100644
--- a/lib/rubygems/request_set/lockfile.rb
+++ b/lib/rubygems/request_set/lockfile.rb
@@ -76,11 +76,6 @@ class Gem::RequestSet::Lockfile
@dependencies = dependencies
@gem_deps_file = File.expand_path(gem_deps_file)
@gem_deps_dir = File.dirname(@gem_deps_file)
-
- if RUBY_VERSION < "2.7"
- @gem_deps_file.untaint unless gem_deps_file.tainted?
- end
-
@platforms = []
end
@@ -109,7 +104,7 @@ class Gem::RequestSet::Lockfile
requests.sort_by(&:name).each do |request|
next if request.spec.name == "bundler"
platform = "-#{request.spec.platform}" unless
- Gem::Platform::RUBY == request.spec.platform
+ request.spec.platform == Gem::Platform::RUBY
out << " #{request.name} (#{request.version}#{platform})"
diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb
index 09497f1a2e..02543cb14a 100644
--- a/lib/rubygems/requirement.rb
+++ b/lib/rubygems/requirement.rb
@@ -23,12 +23,12 @@ class Gem::Requirement
SOURCE_SET_REQUIREMENT = Struct.new(:for_lockfile).new "!" # :nodoc:
quoted = OPS.keys.map {|k| Regexp.quote k }.join "|"
- PATTERN_RAW = "\\s*(#{quoted})?\\s*(#{Gem::Version::VERSION_PATTERN})\\s*" # :nodoc:
+ PATTERN_RAW = "\\s*(#{quoted})?\\s*(#{Gem::Version::VERSION_PATTERN})\\s*".freeze # :nodoc:
##
# A regular expression that matches a requirement
- PATTERN = /\A#{PATTERN_RAW}\z/.freeze
+ PATTERN = /\A#{PATTERN_RAW}\z/
##
# The default requirement matches any non-prerelease version
@@ -284,6 +284,11 @@ class Gem::Requirement
def _tilde_requirements
@_tilde_requirements ||= _sorted_requirements.select {|r| r.first == "~>" }
end
+
+ def initialize_copy(other) # :nodoc:
+ @requirements = other.requirements.dup
+ super
+ end
end
class Gem::Version
diff --git a/lib/rubygems/resolver.rb b/lib/rubygems/resolver.rb
index 8b9d41b40f..115c716b6b 100644
--- a/lib/rubygems/resolver.rb
+++ b/lib/rubygems/resolver.rb
@@ -11,7 +11,7 @@ require_relative "util/list"
# all the requirements.
class Gem::Resolver
- require_relative "resolver/molinillo"
+ require_relative "vendored_molinillo"
##
# If the DEBUG_RESOLVER environment variable is set then debugging mode is
@@ -38,8 +38,6 @@ class Gem::Resolver
##
# List of dependencies that could not be found in the configured sources.
- attr_reader :missing
-
attr_reader :stats
##
@@ -49,8 +47,7 @@ class Gem::Resolver
attr_accessor :skip_gems
##
- # When a missing dependency, don't stop. Just go on and record what was
- # missing.
+ #
attr_accessor :soft_missing
@@ -106,7 +103,6 @@ class Gem::Resolver
@development = false
@development_shallow = false
@ignore_dependencies = false
- @missing = []
@skip_gems = {}
@soft_missing = false
@stats = Gem::Resolver::Stats.new
@@ -171,7 +167,7 @@ class Gem::Resolver
reqs
end
- include Molinillo::UI
+ include Gem::Molinillo::UI
def output
@output ||= debug? ? $stdout : File.open(IO::NULL, "w")
@@ -181,15 +177,14 @@ class Gem::Resolver
DEBUG_RESOLVER
end
- include Molinillo::SpecificationProvider
+ include Gem::Molinillo::SpecificationProvider
##
# Proceed with resolution! Returns an array of ActivationRequest objects.
def resolve
- locking_dg = Molinillo::DependencyGraph.new
- Molinillo::Resolver.new(self, self).resolve(@needed.map {|d| DependencyRequest.new d, nil }, locking_dg).tsort.map(&:payload).compact
- rescue Molinillo::VersionConflict => e
+ Gem::Molinillo::Resolver.new(self, self).resolve(@needed.map {|d| DependencyRequest.new d, nil }).tsort.map(&:payload).compact
+ rescue Gem::Molinillo::VersionConflict => e
conflict = e.conflicts.values.first
raise Gem::DependencyResolutionError, Conflict.new(conflict.requirement_trees.first.first, conflict.existing, conflict.requirement)
ensure
@@ -228,7 +223,6 @@ class Gem::Resolver
def search_for(dependency)
possibles, all = find_possible(dependency)
if !@soft_missing && possibles.empty?
- @missing << dependency
exc = Gem::UnsatisfiableDependencyError.new dependency, all
exc.errors = @set.errors
raise exc
@@ -275,7 +269,6 @@ class Gem::Resolver
end
def allow_missing?(dependency)
- @missing << dependency
@soft_missing
end
diff --git a/lib/rubygems/resolver/api_set.rb b/lib/rubygems/resolver/api_set.rb
index e8e3747361..3e4dadc40f 100644
--- a/lib/rubygems/resolver/api_set.rb
+++ b/lib/rubygems/resolver/api_set.rb
@@ -30,7 +30,7 @@ class Gem::Resolver::APISet < Gem::Resolver::Set
def initialize(dep_uri = "https://index.rubygems.org/info/")
super()
- dep_uri = URI dep_uri unless URI === dep_uri
+ dep_uri = Gem::URI dep_uri unless Gem::URI === dep_uri
@dep_uri = dep_uri
@uri = dep_uri + ".."
diff --git a/lib/rubygems/resolver/api_set/gem_parser.rb b/lib/rubygems/resolver/api_set/gem_parser.rb
index 685c39558d..643b857107 100644
--- a/lib/rubygems/resolver/api_set/gem_parser.rb
+++ b/lib/rubygems/resolver/api_set/gem_parser.rb
@@ -1,12 +1,15 @@
# frozen_string_literal: true
class Gem::Resolver::APISet::GemParser
+ EMPTY_ARRAY = [].freeze
+ private_constant :EMPTY_ARRAY
+
def parse(line)
version_and_platform, rest = line.split(" ", 2)
version, platform = version_and_platform.split("-", 2)
- dependencies, requirements = rest.split("|", 2).map {|s| s.split(",") } if rest
- dependencies = dependencies ? dependencies.map {|d| parse_dependency(d) } : []
- requirements = requirements ? requirements.map {|d| parse_dependency(d) } : []
+ dependencies, requirements = rest.split("|", 2).map! {|s| s.split(",") } if rest
+ dependencies = dependencies ? dependencies.map! {|d| parse_dependency(d) } : EMPTY_ARRAY
+ requirements = requirements ? requirements.map! {|d| parse_dependency(d) } : EMPTY_ARRAY
[version, platform, dependencies, requirements]
end
@@ -15,6 +18,7 @@ class Gem::Resolver::APISet::GemParser
def parse_dependency(string)
dependency = string.split(":")
dependency[-1] = dependency[-1].split("&") if dependency.size > 1
+ dependency[0] = -dependency[0]
dependency
end
end
diff --git a/lib/rubygems/resolver/api_specification.rb b/lib/rubygems/resolver/api_specification.rb
index f26f82757e..a14bcbfeb1 100644
--- a/lib/rubygems/resolver/api_specification.rb
+++ b/lib/rubygems/resolver/api_specification.rb
@@ -22,7 +22,7 @@ class Gem::Resolver::APISpecification < Gem::Resolver::Specification
# Creates an APISpecification for the given +set+ from the rubygems.org
# +api_data+.
#
- # See https://guides.rubygems.org/rubygems-org-api/#misc_methods for the
+ # See https://guides.rubygems.org/rubygems-org-api/#misc-methods for the
# format of the +api_data+.
def initialize(set, api_data)
diff --git a/lib/rubygems/resolver/best_set.rb b/lib/rubygems/resolver/best_set.rb
index c2e8982047..a983f8c6b6 100644
--- a/lib/rubygems/resolver/best_set.rb
+++ b/lib/rubygems/resolver/best_set.rb
@@ -60,7 +60,7 @@ class Gem::Resolver::BestSet < Gem::Resolver::ComposedSet
def replace_failed_api_set(error) # :nodoc:
uri = error.original_uri
- uri = URI uri unless URI === uri
+ uri = Gem::URI uri unless Gem::URI === uri
uri += "."
raise error unless api_set = @sets.find do |set|
diff --git a/lib/rubygems/resolver/index_specification.rb b/lib/rubygems/resolver/index_specification.rb
index b23807682b..7b95608071 100644
--- a/lib/rubygems/resolver/index_specification.rb
+++ b/lib/rubygems/resolver/index_specification.rb
@@ -76,7 +76,7 @@ class Gem::Resolver::IndexSpecification < Gem::Resolver::Specification
q.breakable
q.text full_name
- unless Gem::Platform::RUBY == @platform
+ unless @platform == Gem::Platform::RUBY
q.breakable
q.text @platform.to_s
end
diff --git a/lib/rubygems/resolver/installer_set.rb b/lib/rubygems/resolver/installer_set.rb
index b0e3241a1a..d9fe36c589 100644
--- a/lib/rubygems/resolver/installer_set.rb
+++ b/lib/rubygems/resolver/installer_set.rb
@@ -148,6 +148,8 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
res << Gem::Resolver::InstalledSpecification.new(self, gemspec)
end unless @ignore_installed
+ matching_local = []
+
if consider_local?
matching_local = @local.values.select do |spec, _|
req.match? spec
@@ -169,7 +171,7 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
end
end
- res.concat @remote_set.find_all req if consider_remote?
+ res.concat @remote_set.find_all req if consider_remote? && matching_local.empty?
res
end
diff --git a/lib/rubygems/resolver/molinillo.rb b/lib/rubygems/resolver/molinillo.rb
deleted file mode 100644
index d703505410..0000000000
--- a/lib/rubygems/resolver/molinillo.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "molinillo/lib/molinillo"
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo.rb b/lib/rubygems/resolver/molinillo/lib/molinillo.rb
deleted file mode 100644
index f67badbde7..0000000000
--- a/lib/rubygems/resolver/molinillo/lib/molinillo.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'molinillo/gem_metadata'
-require_relative 'molinillo/errors'
-require_relative 'molinillo/resolver'
-require_relative 'molinillo/modules/ui'
-require_relative 'molinillo/modules/specification_provider'
-
-# Gem::Resolver::Molinillo is a generic dependency resolution algorithm.
-module Gem::Resolver::Molinillo
-end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/resolution_state.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/resolution_state.rb
deleted file mode 100644
index d540d3baff..0000000000
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/resolution_state.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-# frozen_string_literal: true
-
-module Gem::Resolver::Molinillo
- # @!visibility private
- module Delegates
- # Delegates all {Gem::Resolver::Molinillo::ResolutionState} methods to a `#state` property.
- module ResolutionState
- # (see Gem::Resolver::Molinillo::ResolutionState#name)
- def name
- current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty
- current_state.name
- end
-
- # (see Gem::Resolver::Molinillo::ResolutionState#requirements)
- def requirements
- current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty
- current_state.requirements
- end
-
- # (see Gem::Resolver::Molinillo::ResolutionState#activated)
- def activated
- current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty
- current_state.activated
- end
-
- # (see Gem::Resolver::Molinillo::ResolutionState#requirement)
- def requirement
- current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty
- current_state.requirement
- end
-
- # (see Gem::Resolver::Molinillo::ResolutionState#possibilities)
- def possibilities
- current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty
- current_state.possibilities
- end
-
- # (see Gem::Resolver::Molinillo::ResolutionState#depth)
- def depth
- current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty
- current_state.depth
- end
-
- # (see Gem::Resolver::Molinillo::ResolutionState#conflicts)
- def conflicts
- current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty
- current_state.conflicts
- end
-
- # (see Gem::Resolver::Molinillo::ResolutionState#unused_unwind_options)
- def unused_unwind_options
- current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty
- current_state.unused_unwind_options
- end
- end
- end
-end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider.rb
deleted file mode 100644
index b765226fb0..0000000000
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider.rb
+++ /dev/null
@@ -1,88 +0,0 @@
-# frozen_string_literal: true
-
-module Gem::Resolver::Molinillo
- module Delegates
- # Delegates all {Gem::Resolver::Molinillo::SpecificationProvider} methods to a
- # `#specification_provider` property.
- module SpecificationProvider
- # (see Gem::Resolver::Molinillo::SpecificationProvider#search_for)
- def search_for(dependency)
- with_no_such_dependency_error_handling do
- specification_provider.search_for(dependency)
- end
- end
-
- # (see Gem::Resolver::Molinillo::SpecificationProvider#dependencies_for)
- def dependencies_for(specification)
- with_no_such_dependency_error_handling do
- specification_provider.dependencies_for(specification)
- end
- end
-
- # (see Gem::Resolver::Molinillo::SpecificationProvider#requirement_satisfied_by?)
- def requirement_satisfied_by?(requirement, activated, spec)
- with_no_such_dependency_error_handling do
- specification_provider.requirement_satisfied_by?(requirement, activated, spec)
- end
- end
-
- # (see Gem::Resolver::Molinillo::SpecificationProvider#dependencies_equal?)
- def dependencies_equal?(dependencies, other_dependencies)
- with_no_such_dependency_error_handling do
- specification_provider.dependencies_equal?(dependencies, other_dependencies)
- end
- end
-
- # (see Gem::Resolver::Molinillo::SpecificationProvider#name_for)
- def name_for(dependency)
- with_no_such_dependency_error_handling do
- specification_provider.name_for(dependency)
- end
- end
-
- # (see Gem::Resolver::Molinillo::SpecificationProvider#name_for_explicit_dependency_source)
- def name_for_explicit_dependency_source
- with_no_such_dependency_error_handling do
- specification_provider.name_for_explicit_dependency_source
- end
- end
-
- # (see Gem::Resolver::Molinillo::SpecificationProvider#name_for_locking_dependency_source)
- def name_for_locking_dependency_source
- with_no_such_dependency_error_handling do
- specification_provider.name_for_locking_dependency_source
- end
- end
-
- # (see Gem::Resolver::Molinillo::SpecificationProvider#sort_dependencies)
- def sort_dependencies(dependencies, activated, conflicts)
- with_no_such_dependency_error_handling do
- specification_provider.sort_dependencies(dependencies, activated, conflicts)
- end
- end
-
- # (see Gem::Resolver::Molinillo::SpecificationProvider#allow_missing?)
- def allow_missing?(dependency)
- with_no_such_dependency_error_handling do
- specification_provider.allow_missing?(dependency)
- end
- end
-
- private
-
- # Ensures any raised {NoSuchDependencyError} has its
- # {NoSuchDependencyError#required_by} set.
- # @yield
- def with_no_such_dependency_error_handling
- yield
- rescue NoSuchDependencyError => error
- if state
- vertex = activated.vertex_named(name_for(error.dependency))
- error.required_by += vertex.incoming_edges.map { |e| e.origin.name }
- error.required_by << name_for_explicit_dependency_source unless vertex.explicit_requirements.empty?
- end
- raise
- end
- end
- end
-end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb
deleted file mode 100644
index 731a9e3e90..0000000000
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb
+++ /dev/null
@@ -1,255 +0,0 @@
-# frozen_string_literal: true
-
-require_relative '../../../../tsort'
-
-require_relative 'dependency_graph/log'
-require_relative 'dependency_graph/vertex'
-
-module Gem::Resolver::Molinillo
- # A directed acyclic graph that is tuned to hold named dependencies
- class DependencyGraph
- include Enumerable
-
- # Enumerates through the vertices of the graph.
- # @return [Array<Vertex>] The graph's vertices.
- def each
- return vertices.values.each unless block_given?
- vertices.values.each { |v| yield v }
- end
-
- include Gem::TSort
-
- # @!visibility private
- alias tsort_each_node each
-
- # @!visibility private
- def tsort_each_child(vertex, &block)
- vertex.successors.each(&block)
- end
-
- # Topologically sorts the given vertices.
- # @param [Enumerable<Vertex>] vertices the vertices to be sorted, which must
- # all belong to the same graph.
- # @return [Array<Vertex>] The sorted vertices.
- def self.tsort(vertices)
- Gem::TSort.tsort(
- lambda { |b| vertices.each(&b) },
- lambda { |v, &b| (v.successors & vertices).each(&b) }
- )
- end
-
- # A directed edge of a {DependencyGraph}
- # @attr [Vertex] origin The origin of the directed edge
- # @attr [Vertex] destination The destination of the directed edge
- # @attr [Object] requirement The requirement the directed edge represents
- Edge = Struct.new(:origin, :destination, :requirement)
-
- # @return [{String => Vertex}] the vertices of the dependency graph, keyed
- # by {Vertex#name}
- attr_reader :vertices
-
- # @return [Log] the op log for this graph
- attr_reader :log
-
- # Initializes an empty dependency graph
- def initialize
- @vertices = {}
- @log = Log.new
- end
-
- # Tags the current state of the dependency as the given tag
- # @param [Object] tag an opaque tag for the current state of the graph
- # @return [Void]
- def tag(tag)
- log.tag(self, tag)
- end
-
- # Rewinds the graph to the state tagged as `tag`
- # @param [Object] tag the tag to rewind to
- # @return [Void]
- def rewind_to(tag)
- log.rewind_to(self, tag)
- end
-
- # Initializes a copy of a {DependencyGraph}, ensuring that all {#vertices}
- # are properly copied.
- # @param [DependencyGraph] other the graph to copy.
- def initialize_copy(other)
- super
- @vertices = {}
- @log = other.log.dup
- traverse = lambda do |new_v, old_v|
- return if new_v.outgoing_edges.size == old_v.outgoing_edges.size
- old_v.outgoing_edges.each do |edge|
- destination = add_vertex(edge.destination.name, edge.destination.payload)
- add_edge_no_circular(new_v, destination, edge.requirement)
- traverse.call(destination, edge.destination)
- end
- end
- other.vertices.each do |name, vertex|
- new_vertex = add_vertex(name, vertex.payload, vertex.root?)
- new_vertex.explicit_requirements.replace(vertex.explicit_requirements)
- traverse.call(new_vertex, vertex)
- end
- end
-
- # @return [String] a string suitable for debugging
- def inspect
- "#{self.class}:#{vertices.values.inspect}"
- end
-
- # @param [Hash] options options for dot output.
- # @return [String] Returns a dot format representation of the graph
- def to_dot(options = {})
- edge_label = options.delete(:edge_label)
- raise ArgumentError, "Unknown options: #{options.keys}" unless options.empty?
-
- dot_vertices = []
- dot_edges = []
- vertices.each do |n, v|
- dot_vertices << " #{n} [label=\"{#{n}|#{v.payload}}\"]"
- v.outgoing_edges.each do |e|
- label = edge_label ? edge_label.call(e) : e.requirement
- dot_edges << " #{e.origin.name} -> #{e.destination.name} [label=#{label.to_s.dump}]"
- end
- end
-
- dot_vertices.uniq!
- dot_vertices.sort!
- dot_edges.uniq!
- dot_edges.sort!
-
- dot = dot_vertices.unshift('digraph G {').push('') + dot_edges.push('}')
- dot.join("\n")
- end
-
- # @param [DependencyGraph] other
- # @return [Boolean] whether the two dependency graphs are equal, determined
- # by a recursive traversal of each {#root_vertices} and its
- # {Vertex#successors}
- def ==(other)
- return false unless other
- return true if equal?(other)
- vertices.each do |name, vertex|
- other_vertex = other.vertex_named(name)
- return false unless other_vertex
- return false unless vertex.payload == other_vertex.payload
- return false unless other_vertex.successors.to_set == vertex.successors.to_set
- end
- end
-
- # @param [String] name
- # @param [Object] payload
- # @param [Array<String>] parent_names
- # @param [Object] requirement the requirement that is requiring the child
- # @return [void]
- def add_child_vertex(name, payload, parent_names, requirement)
- root = !parent_names.delete(nil) { true }
- vertex = add_vertex(name, payload, root)
- vertex.explicit_requirements << requirement if root
- parent_names.each do |parent_name|
- parent_vertex = vertex_named(parent_name)
- add_edge(parent_vertex, vertex, requirement)
- end
- vertex
- end
-
- # Adds a vertex with the given name, or updates the existing one.
- # @param [String] name
- # @param [Object] payload
- # @return [Vertex] the vertex that was added to `self`
- def add_vertex(name, payload, root = false)
- log.add_vertex(self, name, payload, root)
- end
-
- # Detaches the {#vertex_named} `name` {Vertex} from the graph, recursively
- # removing any non-root vertices that were orphaned in the process
- # @param [String] name
- # @return [Array<Vertex>] the vertices which have been detached
- def detach_vertex_named(name)
- log.detach_vertex_named(self, name)
- end
-
- # @param [String] name
- # @return [Vertex,nil] the vertex with the given name
- def vertex_named(name)
- vertices[name]
- end
-
- # @param [String] name
- # @return [Vertex,nil] the root vertex with the given name
- def root_vertex_named(name)
- vertex = vertex_named(name)
- vertex if vertex && vertex.root?
- end
-
- # Adds a new {Edge} to the dependency graph
- # @param [Vertex] origin
- # @param [Vertex] destination
- # @param [Object] requirement the requirement that this edge represents
- # @return [Edge] the added edge
- def add_edge(origin, destination, requirement)
- if destination.path_to?(origin)
- raise CircularDependencyError.new(path(destination, origin))
- end
- add_edge_no_circular(origin, destination, requirement)
- end
-
- # Deletes an {Edge} from the dependency graph
- # @param [Edge] edge
- # @return [Void]
- def delete_edge(edge)
- log.delete_edge(self, edge.origin.name, edge.destination.name, edge.requirement)
- end
-
- # Sets the payload of the vertex with the given name
- # @param [String] name the name of the vertex
- # @param [Object] payload the payload
- # @return [Void]
- def set_payload(name, payload)
- log.set_payload(self, name, payload)
- end
-
- private
-
- # Adds a new {Edge} to the dependency graph without checking for
- # circularity.
- # @param (see #add_edge)
- # @return (see #add_edge)
- def add_edge_no_circular(origin, destination, requirement)
- log.add_edge_no_circular(self, origin.name, destination.name, requirement)
- end
-
- # Returns the path between two vertices
- # @raise [ArgumentError] if there is no path between the vertices
- # @param [Vertex] from
- # @param [Vertex] to
- # @return [Array<Vertex>] the shortest path from `from` to `to`
- def path(from, to)
- distances = Hash.new(vertices.size + 1)
- distances[from.name] = 0
- predecessors = {}
- each do |vertex|
- vertex.successors.each do |successor|
- if distances[successor.name] > distances[vertex.name] + 1
- distances[successor.name] = distances[vertex.name] + 1
- predecessors[successor] = vertex
- end
- end
- end
-
- path = [to]
- while before = predecessors[to]
- path << before
- to = before
- break if to == from
- end
-
- unless path.last.equal?(from)
- raise ArgumentError, "There is no path from #{from.name} to #{to.name}"
- end
-
- path.reverse
- end
- end
-end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action.rb
deleted file mode 100644
index cc140031b3..0000000000
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: true
-
-module Gem::Resolver::Molinillo
- class DependencyGraph
- # An action that modifies a {DependencyGraph} that is reversible.
- # @abstract
- class Action
- # rubocop:disable Lint/UnusedMethodArgument
-
- # @return [Symbol] The name of the action.
- def self.action_name
- raise 'Abstract'
- end
-
- # Performs the action on the given graph.
- # @param [DependencyGraph] graph the graph to perform the action on.
- # @return [Void]
- def up(graph)
- raise 'Abstract'
- end
-
- # Reverses the action on the given graph.
- # @param [DependencyGraph] graph the graph to reverse the action on.
- # @return [Void]
- def down(graph)
- raise 'Abstract'
- end
-
- # @return [Action,Nil] The previous action
- attr_accessor :previous
-
- # @return [Action,Nil] The next action
- attr_accessor :next
- end
- end
-end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb
deleted file mode 100644
index 5570483253..0000000000
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'action'
-module Gem::Resolver::Molinillo
- class DependencyGraph
- # @!visibility private
- # (see DependencyGraph#add_edge_no_circular)
- class AddEdgeNoCircular < Action
- # @!group Action
-
- # (see Action.action_name)
- def self.action_name
- :add_vertex
- end
-
- # (see Action#up)
- def up(graph)
- edge = make_edge(graph)
- edge.origin.outgoing_edges << edge
- edge.destination.incoming_edges << edge
- edge
- end
-
- # (see Action#down)
- def down(graph)
- edge = make_edge(graph)
- delete_first(edge.origin.outgoing_edges, edge)
- delete_first(edge.destination.incoming_edges, edge)
- end
-
- # @!group AddEdgeNoCircular
-
- # @return [String] the name of the origin of the edge
- attr_reader :origin
-
- # @return [String] the name of the destination of the edge
- attr_reader :destination
-
- # @return [Object] the requirement that the edge represents
- attr_reader :requirement
-
- # @param [DependencyGraph] graph the graph to find vertices from
- # @return [Edge] The edge this action adds
- def make_edge(graph)
- Edge.new(graph.vertex_named(origin), graph.vertex_named(destination), requirement)
- end
-
- # Initialize an action to add an edge to a dependency graph
- # @param [String] origin the name of the origin of the edge
- # @param [String] destination the name of the destination of the edge
- # @param [Object] requirement the requirement that the edge represents
- def initialize(origin, destination, requirement)
- @origin = origin
- @destination = destination
- @requirement = requirement
- end
-
- private
-
- def delete_first(array, item)
- return unless index = array.index(item)
- array.delete_at(index)
- end
- end
- end
-end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_vertex.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_vertex.rb
deleted file mode 100644
index f1411d5efa..0000000000
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_vertex.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'action'
-module Gem::Resolver::Molinillo
- class DependencyGraph
- # @!visibility private
- # (see DependencyGraph#add_vertex)
- class AddVertex < Action # :nodoc:
- # @!group Action
-
- # (see Action.action_name)
- def self.action_name
- :add_vertex
- end
-
- # (see Action#up)
- def up(graph)
- if existing = graph.vertices[name]
- @existing_payload = existing.payload
- @existing_root = existing.root
- end
- vertex = existing || Vertex.new(name, payload)
- graph.vertices[vertex.name] = vertex
- vertex.payload ||= payload
- vertex.root ||= root
- vertex
- end
-
- # (see Action#down)
- def down(graph)
- if defined?(@existing_payload)
- vertex = graph.vertices[name]
- vertex.payload = @existing_payload
- vertex.root = @existing_root
- else
- graph.vertices.delete(name)
- end
- end
-
- # @!group AddVertex
-
- # @return [String] the name of the vertex
- attr_reader :name
-
- # @return [Object] the payload for the vertex
- attr_reader :payload
-
- # @return [Boolean] whether the vertex is root or not
- attr_reader :root
-
- # Initialize an action to add a vertex to a dependency graph
- # @param [String] name the name of the vertex
- # @param [Object] payload the payload for the vertex
- # @param [Boolean] root whether the vertex is root or not
- def initialize(name, payload, root)
- @name = name
- @payload = payload
- @root = root
- end
- end
- end
-end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/delete_edge.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/delete_edge.rb
deleted file mode 100644
index 3b48d77a50..0000000000
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/delete_edge.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'action'
-module Gem::Resolver::Molinillo
- class DependencyGraph
- # @!visibility private
- # (see DependencyGraph#delete_edge)
- class DeleteEdge < Action
- # @!group Action
-
- # (see Action.action_name)
- def self.action_name
- :delete_edge
- end
-
- # (see Action#up)
- def up(graph)
- edge = make_edge(graph)
- edge.origin.outgoing_edges.delete(edge)
- edge.destination.incoming_edges.delete(edge)
- end
-
- # (see Action#down)
- def down(graph)
- edge = make_edge(graph)
- edge.origin.outgoing_edges << edge
- edge.destination.incoming_edges << edge
- edge
- end
-
- # @!group DeleteEdge
-
- # @return [String] the name of the origin of the edge
- attr_reader :origin_name
-
- # @return [String] the name of the destination of the edge
- attr_reader :destination_name
-
- # @return [Object] the requirement that the edge represents
- attr_reader :requirement
-
- # @param [DependencyGraph] graph the graph to find vertices from
- # @return [Edge] The edge this action adds
- def make_edge(graph)
- Edge.new(
- graph.vertex_named(origin_name),
- graph.vertex_named(destination_name),
- requirement
- )
- end
-
- # Initialize an action to add an edge to a dependency graph
- # @param [String] origin_name the name of the origin of the edge
- # @param [String] destination_name the name of the destination of the edge
- # @param [Object] requirement the requirement that the edge represents
- def initialize(origin_name, destination_name, requirement)
- @origin_name = origin_name
- @destination_name = destination_name
- @requirement = requirement
- end
- end
- end
-end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb
deleted file mode 100644
index 92f60d5be8..0000000000
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'action'
-module Gem::Resolver::Molinillo
- class DependencyGraph
- # @!visibility private
- # @see DependencyGraph#detach_vertex_named
- class DetachVertexNamed < Action
- # @!group Action
-
- # (see Action#name)
- def self.action_name
- :add_vertex
- end
-
- # (see Action#up)
- def up(graph)
- return [] unless @vertex = graph.vertices.delete(name)
-
- removed_vertices = [@vertex]
- @vertex.outgoing_edges.each do |e|
- v = e.destination
- v.incoming_edges.delete(e)
- if !v.root? && v.incoming_edges.empty?
- removed_vertices.concat graph.detach_vertex_named(v.name)
- end
- end
-
- @vertex.incoming_edges.each do |e|
- v = e.origin
- v.outgoing_edges.delete(e)
- end
-
- removed_vertices
- end
-
- # (see Action#down)
- def down(graph)
- return unless @vertex
- graph.vertices[@vertex.name] = @vertex
- @vertex.outgoing_edges.each do |e|
- e.destination.incoming_edges << e
- end
- @vertex.incoming_edges.each do |e|
- e.origin.outgoing_edges << e
- end
- end
-
- # @!group DetachVertexNamed
-
- # @return [String] the name of the vertex to detach
- attr_reader :name
-
- # Initialize an action to detach a vertex from a dependency graph
- # @param [String] name the name of the vertex to detach
- def initialize(name)
- @name = name
- end
- end
- end
-end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/log.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/log.rb
deleted file mode 100644
index 7aeb8847ec..0000000000
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/log.rb
+++ /dev/null
@@ -1,126 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'add_edge_no_circular'
-require_relative 'add_vertex'
-require_relative 'delete_edge'
-require_relative 'detach_vertex_named'
-require_relative 'set_payload'
-require_relative 'tag'
-
-module Gem::Resolver::Molinillo
- class DependencyGraph
- # A log for dependency graph actions
- class Log
- # Initializes an empty log
- def initialize
- @current_action = @first_action = nil
- end
-
- # @!macro [new] action
- # {include:DependencyGraph#$0}
- # @param [Graph] graph the graph to perform the action on
- # @param (see DependencyGraph#$0)
- # @return (see DependencyGraph#$0)
-
- # @macro action
- def tag(graph, tag)
- push_action(graph, Tag.new(tag))
- end
-
- # @macro action
- def add_vertex(graph, name, payload, root)
- push_action(graph, AddVertex.new(name, payload, root))
- end
-
- # @macro action
- def detach_vertex_named(graph, name)
- push_action(graph, DetachVertexNamed.new(name))
- end
-
- # @macro action
- def add_edge_no_circular(graph, origin, destination, requirement)
- push_action(graph, AddEdgeNoCircular.new(origin, destination, requirement))
- end
-
- # {include:DependencyGraph#delete_edge}
- # @param [Graph] graph the graph to perform the action on
- # @param [String] origin_name
- # @param [String] destination_name
- # @param [Object] requirement
- # @return (see DependencyGraph#delete_edge)
- def delete_edge(graph, origin_name, destination_name, requirement)
- push_action(graph, DeleteEdge.new(origin_name, destination_name, requirement))
- end
-
- # @macro action
- def set_payload(graph, name, payload)
- push_action(graph, SetPayload.new(name, payload))
- end
-
- # Pops the most recent action from the log and undoes the action
- # @param [DependencyGraph] graph
- # @return [Action] the action that was popped off the log
- def pop!(graph)
- return unless action = @current_action
- unless @current_action = action.previous
- @first_action = nil
- end
- action.down(graph)
- action
- end
-
- extend Enumerable
-
- # @!visibility private
- # Enumerates each action in the log
- # @yield [Action]
- def each
- return enum_for unless block_given?
- action = @first_action
- loop do
- break unless action
- yield action
- action = action.next
- end
- self
- end
-
- # @!visibility private
- # Enumerates each action in the log in reverse order
- # @yield [Action]
- def reverse_each
- return enum_for(:reverse_each) unless block_given?
- action = @current_action
- loop do
- break unless action
- yield action
- action = action.previous
- end
- self
- end
-
- # @macro action
- def rewind_to(graph, tag)
- loop do
- action = pop!(graph)
- raise "No tag #{tag.inspect} found" unless action
- break if action.class.action_name == :tag && action.tag == tag
- end
- end
-
- private
-
- # Adds the given action to the log, running the action
- # @param [DependencyGraph] graph
- # @param [Action] action
- # @return The value returned by `action.up`
- def push_action(graph, action)
- action.previous = @current_action
- @current_action.next = action if @current_action
- @current_action = action
- @first_action ||= action
- action.up(graph)
- end
- end
- end
-end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/set_payload.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/set_payload.rb
deleted file mode 100644
index 726292a2c3..0000000000
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/set_payload.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'action'
-module Gem::Resolver::Molinillo
- class DependencyGraph
- # @!visibility private
- # @see DependencyGraph#set_payload
- class SetPayload < Action # :nodoc:
- # @!group Action
-
- # (see Action.action_name)
- def self.action_name
- :set_payload
- end
-
- # (see Action#up)
- def up(graph)
- vertex = graph.vertex_named(name)
- @old_payload = vertex.payload
- vertex.payload = payload
- end
-
- # (see Action#down)
- def down(graph)
- graph.vertex_named(name).payload = @old_payload
- end
-
- # @!group SetPayload
-
- # @return [String] the name of the vertex
- attr_reader :name
-
- # @return [Object] the payload for the vertex
- attr_reader :payload
-
- # Initialize an action to add set the payload for a vertex in a dependency
- # graph
- # @param [String] name the name of the vertex
- # @param [Object] payload the payload for the vertex
- def initialize(name, payload)
- @name = name
- @payload = payload
- end
- end
- end
-end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/tag.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/tag.rb
deleted file mode 100644
index bfe6fd31f8..0000000000
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/tag.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'action'
-module Gem::Resolver::Molinillo
- class DependencyGraph
- # @!visibility private
- # @see DependencyGraph#tag
- class Tag < Action
- # @!group Action
-
- # (see Action.action_name)
- def self.action_name
- :tag
- end
-
- # (see Action#up)
- def up(graph)
- end
-
- # (see Action#down)
- def down(graph)
- end
-
- # @!group Tag
-
- # @return [Object] An opaque tag
- attr_reader :tag
-
- # Initialize an action to tag a state of a dependency graph
- # @param [Object] tag an opaque tag
- def initialize(tag)
- @tag = tag
- end
- end
- end
-end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb
deleted file mode 100644
index 77114951b2..0000000000
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb
+++ /dev/null
@@ -1,164 +0,0 @@
-# frozen_string_literal: true
-
-module Gem::Resolver::Molinillo
- class DependencyGraph
- # A vertex in a {DependencyGraph} that encapsulates a {#name} and a
- # {#payload}
- class Vertex
- # @return [String] the name of the vertex
- attr_accessor :name
-
- # @return [Object] the payload the vertex holds
- attr_accessor :payload
-
- # @return [Array<Object>] the explicit requirements that required
- # this vertex
- attr_reader :explicit_requirements
-
- # @return [Boolean] whether the vertex is considered a root vertex
- attr_accessor :root
- alias root? root
-
- # Initializes a vertex with the given name and payload.
- # @param [String] name see {#name}
- # @param [Object] payload see {#payload}
- def initialize(name, payload)
- @name = name.frozen? ? name : name.dup.freeze
- @payload = payload
- @explicit_requirements = []
- @outgoing_edges = []
- @incoming_edges = []
- end
-
- # @return [Array<Object>] all of the requirements that required
- # this vertex
- def requirements
- (incoming_edges.map(&:requirement) + explicit_requirements).uniq
- end
-
- # @return [Array<Edge>] the edges of {#graph} that have `self` as their
- # {Edge#origin}
- attr_accessor :outgoing_edges
-
- # @return [Array<Edge>] the edges of {#graph} that have `self` as their
- # {Edge#destination}
- attr_accessor :incoming_edges
-
- # @return [Array<Vertex>] the vertices of {#graph} that have an edge with
- # `self` as their {Edge#destination}
- def predecessors
- incoming_edges.map(&:origin)
- end
-
- # @return [Set<Vertex>] the vertices of {#graph} where `self` is a
- # {#descendent?}
- def recursive_predecessors
- _recursive_predecessors
- end
-
- # @param [Set<Vertex>] vertices the set to add the predecessors to
- # @return [Set<Vertex>] the vertices of {#graph} where `self` is a
- # {#descendent?}
- def _recursive_predecessors(vertices = new_vertex_set)
- incoming_edges.each do |edge|
- vertex = edge.origin
- next unless vertices.add?(vertex)
- vertex._recursive_predecessors(vertices)
- end
-
- vertices
- end
- protected :_recursive_predecessors
-
- # @return [Array<Vertex>] the vertices of {#graph} that have an edge with
- # `self` as their {Edge#origin}
- def successors
- outgoing_edges.map(&:destination)
- end
-
- # @return [Set<Vertex>] the vertices of {#graph} where `self` is an
- # {#ancestor?}
- def recursive_successors
- _recursive_successors
- end
-
- # @param [Set<Vertex>] vertices the set to add the successors to
- # @return [Set<Vertex>] the vertices of {#graph} where `self` is an
- # {#ancestor?}
- def _recursive_successors(vertices = new_vertex_set)
- outgoing_edges.each do |edge|
- vertex = edge.destination
- next unless vertices.add?(vertex)
- vertex._recursive_successors(vertices)
- end
-
- vertices
- end
- protected :_recursive_successors
-
- # @return [String] a string suitable for debugging
- def inspect
- "#{self.class}:#{name}(#{payload.inspect})"
- end
-
- # @return [Boolean] whether the two vertices are equal, determined
- # by a recursive traversal of each {Vertex#successors}
- def ==(other)
- return true if equal?(other)
- shallow_eql?(other) &&
- successors.to_set == other.successors.to_set
- end
-
- # @param [Vertex] other the other vertex to compare to
- # @return [Boolean] whether the two vertices are equal, determined
- # solely by {#name} and {#payload} equality
- def shallow_eql?(other)
- return true if equal?(other)
- other &&
- name == other.name &&
- payload == other.payload
- end
-
- alias eql? ==
-
- # @return [Fixnum] a hash for the vertex based upon its {#name}
- def hash
- name.hash
- end
-
- # Is there a path from `self` to `other` following edges in the
- # dependency graph?
- # @return whether there is a path following edges within this {#graph}
- def path_to?(other)
- _path_to?(other)
- end
-
- alias descendent? path_to?
-
- # @param [Vertex] other the vertex to check if there's a path to
- # @param [Set<Vertex>] visited the vertices of {#graph} that have been visited
- # @return [Boolean] whether there is a path to `other` from `self`
- def _path_to?(other, visited = new_vertex_set)
- return false unless visited.add?(self)
- return true if equal?(other)
- successors.any? { |v| v._path_to?(other, visited) }
- end
- protected :_path_to?
-
- # Is there a path from `other` to `self` following edges in the
- # dependency graph?
- # @return whether there is a path following edges within this {#graph}
- def ancestor?(other)
- other.path_to?(self)
- end
-
- alias is_reachable_from? ancestor?
-
- def new_vertex_set
- require 'set'
- Set.new
- end
- private :new_vertex_set
- end
- end
-end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb
deleted file mode 100644
index 4289902828..0000000000
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb
+++ /dev/null
@@ -1,149 +0,0 @@
-# frozen_string_literal: true
-
-module Gem::Resolver::Molinillo
- # An error that occurred during the resolution process
- class ResolverError < StandardError; end
-
- # An error caused by searching for a dependency that is completely unknown,
- # i.e. has no versions available whatsoever.
- class NoSuchDependencyError < ResolverError
- # @return [Object] the dependency that could not be found
- attr_accessor :dependency
-
- # @return [Array<Object>] the specifications that depended upon {#dependency}
- attr_accessor :required_by
-
- # Initializes a new error with the given missing dependency.
- # @param [Object] dependency @see {#dependency}
- # @param [Array<Object>] required_by @see {#required_by}
- def initialize(dependency, required_by = [])
- @dependency = dependency
- @required_by = required_by.uniq
- super()
- end
-
- # The error message for the missing dependency, including the specifications
- # that had this dependency.
- def message
- sources = required_by.map { |r| "`#{r}`" }.join(' and ')
- message = "Unable to find a specification for `#{dependency}`"
- message += " depended upon by #{sources}" unless sources.empty?
- message
- end
- end
-
- # An error caused by attempting to fulfil a dependency that was circular
- #
- # @note This exception will be thrown if and only if a {Vertex} is added to a
- # {DependencyGraph} that has a {DependencyGraph::Vertex#path_to?} an
- # existing {DependencyGraph::Vertex}
- class CircularDependencyError < ResolverError
- # [Set<Object>] the dependencies responsible for causing the error
- attr_reader :dependencies
-
- # Initializes a new error with the given circular vertices.
- # @param [Array<DependencyGraph::Vertex>] vertices the vertices in the dependency
- # that caused the error
- def initialize(vertices)
- super "There is a circular dependency between #{vertices.map(&:name).join(' and ')}"
- @dependencies = vertices.map { |vertex| vertex.payload.possibilities.last }.to_set
- end
- end
-
- # An error caused by conflicts in version
- class VersionConflict < ResolverError
- # @return [{String => Resolution::Conflict}] the conflicts that caused
- # resolution to fail
- attr_reader :conflicts
-
- # @return [SpecificationProvider] the specification provider used during
- # resolution
- attr_reader :specification_provider
-
- # Initializes a new error with the given version conflicts.
- # @param [{String => Resolution::Conflict}] conflicts see {#conflicts}
- # @param [SpecificationProvider] specification_provider see {#specification_provider}
- def initialize(conflicts, specification_provider)
- pairs = []
- conflicts.values.flat_map(&:requirements).each do |conflicting|
- conflicting.each do |source, conflict_requirements|
- conflict_requirements.each do |c|
- pairs << [c, source]
- end
- end
- end
-
- super "Unable to satisfy the following requirements:\n\n" \
- "#{pairs.map { |r, d| "- `#{r}` required by `#{d}`" }.join("\n")}"
-
- @conflicts = conflicts
- @specification_provider = specification_provider
- end
-
- require_relative 'delegates/specification_provider'
- include Delegates::SpecificationProvider
-
- # @return [String] An error message that includes requirement trees,
- # which is much more detailed & customizable than the default message
- # @param [Hash] opts the options to create a message with.
- # @option opts [String] :solver_name The user-facing name of the solver
- # @option opts [String] :possibility_type The generic name of a possibility
- # @option opts [Proc] :reduce_trees A proc that reduced the list of requirement trees
- # @option opts [Proc] :printable_requirement A proc that pretty-prints requirements
- # @option opts [Proc] :additional_message_for_conflict A proc that appends additional
- # messages for each conflict
- # @option opts [Proc] :version_for_spec A proc that returns the version number for a
- # possibility
- def message_with_trees(opts = {})
- solver_name = opts.delete(:solver_name) { self.class.name.split('::').first }
- possibility_type = opts.delete(:possibility_type) { 'possibility named' }
- reduce_trees = opts.delete(:reduce_trees) { proc { |trees| trees.uniq.sort_by(&:to_s) } }
- printable_requirement = opts.delete(:printable_requirement) { proc { |req| req.to_s } }
- additional_message_for_conflict = opts.delete(:additional_message_for_conflict) { proc {} }
- version_for_spec = opts.delete(:version_for_spec) { proc(&:to_s) }
- incompatible_version_message_for_conflict = opts.delete(:incompatible_version_message_for_conflict) do
- proc do |name, _conflict|
- %(#{solver_name} could not find compatible versions for #{possibility_type} "#{name}":)
- end
- end
-
- full_message_for_conflict = opts.delete(:full_message_for_conflict) do
- proc do |name, conflict|
- o = "\n".dup << incompatible_version_message_for_conflict.call(name, conflict) << "\n"
- if conflict.locked_requirement
- o << %( In snapshot (#{name_for_locking_dependency_source}):\n)
- o << %( #{printable_requirement.call(conflict.locked_requirement)}\n)
- o << %(\n)
- end
- o << %( In #{name_for_explicit_dependency_source}:\n)
- trees = reduce_trees.call(conflict.requirement_trees)
-
- o << trees.map do |tree|
- t = ''.dup
- depth = 2
- tree.each do |req|
- t << ' ' * depth << printable_requirement.call(req)
- unless tree.last == req
- if spec = conflict.activated_by_name[name_for(req)]
- t << %( was resolved to #{version_for_spec.call(spec)}, which)
- end
- t << %( depends on)
- end
- t << %(\n)
- depth += 1
- end
- t
- end.join("\n")
-
- additional_message_for_conflict.call(o, name, conflict)
-
- o
- end
- end
-
- conflicts.sort.reduce(''.dup) do |o, (name, conflict)|
- o << full_message_for_conflict.call(name, conflict)
- end.strip
- end
- end
-end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb
deleted file mode 100644
index 86c249c404..0000000000
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-# frozen_string_literal: true
-
-module Gem::Resolver::Molinillo
- # The version of Gem::Resolver::Molinillo.
- VERSION = '0.8.0'.freeze
-end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider.rb
deleted file mode 100644
index 1067bf7439..0000000000
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider.rb
+++ /dev/null
@@ -1,112 +0,0 @@
-# frozen_string_literal: true
-
-module Gem::Resolver::Molinillo
- # Provides information about specifications and dependencies to the resolver,
- # allowing the {Resolver} class to remain generic while still providing power
- # and flexibility.
- #
- # This module contains the methods that users of Gem::Resolver::Molinillo must to implement,
- # using knowledge of their own model classes.
- module SpecificationProvider
- # Search for the specifications that match the given dependency.
- # The specifications in the returned array will be considered in reverse
- # order, so the latest version ought to be last.
- # @note This method should be 'pure', i.e. the return value should depend
- # only on the `dependency` parameter.
- #
- # @param [Object] dependency
- # @return [Array<Object>] the specifications that satisfy the given
- # `dependency`.
- def search_for(dependency)
- []
- end
-
- # Returns the dependencies of `specification`.
- # @note This method should be 'pure', i.e. the return value should depend
- # only on the `specification` parameter.
- #
- # @param [Object] specification
- # @return [Array<Object>] the dependencies that are required by the given
- # `specification`.
- def dependencies_for(specification)
- []
- end
-
- # Determines whether the given `requirement` is satisfied by the given
- # `spec`, in the context of the current `activated` dependency graph.
- #
- # @param [Object] requirement
- # @param [DependencyGraph] activated the current dependency graph in the
- # resolution process.
- # @param [Object] spec
- # @return [Boolean] whether `requirement` is satisfied by `spec` in the
- # context of the current `activated` dependency graph.
- def requirement_satisfied_by?(requirement, activated, spec)
- true
- end
-
- # Determines whether two arrays of dependencies are equal, and thus can be
- # grouped.
- #
- # @param [Array<Object>] dependencies
- # @param [Array<Object>] other_dependencies
- # @return [Boolean] whether `dependencies` and `other_dependencies` should
- # be considered equal.
- def dependencies_equal?(dependencies, other_dependencies)
- dependencies == other_dependencies
- end
-
- # Returns the name for the given `dependency`.
- # @note This method should be 'pure', i.e. the return value should depend
- # only on the `dependency` parameter.
- #
- # @param [Object] dependency
- # @return [String] the name for the given `dependency`.
- def name_for(dependency)
- dependency.to_s
- end
-
- # @return [String] the name of the source of explicit dependencies, i.e.
- # those passed to {Resolver#resolve} directly.
- def name_for_explicit_dependency_source
- 'user-specified dependency'
- end
-
- # @return [String] the name of the source of 'locked' dependencies, i.e.
- # those passed to {Resolver#resolve} directly as the `base`
- def name_for_locking_dependency_source
- 'Lockfile'
- end
-
- # Sort dependencies so that the ones that are easiest to resolve are first.
- # Easiest to resolve is (usually) defined by:
- # 1) Is this dependency already activated?
- # 2) How relaxed are the requirements?
- # 3) Are there any conflicts for this dependency?
- # 4) How many possibilities are there to satisfy this dependency?
- #
- # @param [Array<Object>] dependencies
- # @param [DependencyGraph] activated the current dependency graph in the
- # resolution process.
- # @param [{String => Array<Conflict>}] conflicts
- # @return [Array<Object>] a sorted copy of `dependencies`.
- def sort_dependencies(dependencies, activated, conflicts)
- dependencies.sort_by do |dependency|
- name = name_for(dependency)
- [
- activated.vertex_named(name).payload ? 0 : 1,
- conflicts[name] ? 0 : 1,
- ]
- end
- end
-
- # Returns whether this dependency, which has no possible matching
- # specifications, can safely be ignored.
- #
- # @param [Object] dependency
- # @return [Boolean] whether this dependency can safely be skipped.
- def allow_missing?(dependency)
- false
- end
- end
-end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb
deleted file mode 100644
index a810fd519c..0000000000
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-# frozen_string_literal: true
-
-module Gem::Resolver::Molinillo
- # Conveys information about the resolution process to a user.
- module UI
- # The {IO} object that should be used to print output. `STDOUT`, by default.
- #
- # @return [IO]
- def output
- STDOUT
- end
-
- # Called roughly every {#progress_rate}, this method should convey progress
- # to the user.
- #
- # @return [void]
- def indicate_progress
- output.print '.' unless debug?
- end
-
- # How often progress should be conveyed to the user via
- # {#indicate_progress}, in seconds. A third of a second, by default.
- #
- # @return [Float]
- def progress_rate
- 0.33
- end
-
- # Called before resolution begins.
- #
- # @return [void]
- def before_resolution
- output.print 'Resolving dependencies...'
- end
-
- # Called after resolution ends (either successfully or with an error).
- # By default, prints a newline.
- #
- # @return [void]
- def after_resolution
- output.puts
- end
-
- # Conveys debug information to the user.
- #
- # @param [Integer] depth the current depth of the resolution process.
- # @return [void]
- def debug(depth = 0)
- if debug?
- debug_info = yield
- debug_info = debug_info.inspect unless debug_info.is_a?(String)
- debug_info = debug_info.split("\n").map { |s| ":#{depth.to_s.rjust 4}: #{s}" }
- output.puts debug_info
- end
- end
-
- # Whether or not debug messages should be printed.
- # By default, whether or not the `MOLINILLO_DEBUG` environment variable is
- # set.
- #
- # @return [Boolean]
- def debug?
- return @debug_mode if defined?(@debug_mode)
- @debug_mode = ENV['MOLINILLO_DEBUG']
- end
- end
-end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb
deleted file mode 100644
index 8b40e59e42..0000000000
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb
+++ /dev/null
@@ -1,839 +0,0 @@
-# frozen_string_literal: true
-
-module Gem::Resolver::Molinillo
- class Resolver
- # A specific resolution from a given {Resolver}
- class Resolution
- # A conflict that the resolution process encountered
- # @attr [Object] requirement the requirement that immediately led to the conflict
- # @attr [{String,Nil=>[Object]}] requirements the requirements that caused the conflict
- # @attr [Object, nil] existing the existing spec that was in conflict with
- # the {#possibility}
- # @attr [Object] possibility_set the set of specs that was unable to be
- # activated due to a conflict.
- # @attr [Object] locked_requirement the relevant locking requirement.
- # @attr [Array<Array<Object>>] requirement_trees the different requirement
- # trees that led to every requirement for the conflicting name.
- # @attr [{String=>Object}] activated_by_name the already-activated specs.
- # @attr [Object] underlying_error an error that has occurred during resolution, and
- # will be raised at the end of it if no resolution is found.
- Conflict = Struct.new(
- :requirement,
- :requirements,
- :existing,
- :possibility_set,
- :locked_requirement,
- :requirement_trees,
- :activated_by_name,
- :underlying_error
- )
-
- class Conflict
- # @return [Object] a spec that was unable to be activated due to a conflict
- def possibility
- possibility_set && possibility_set.latest_version
- end
- end
-
- # A collection of possibility states that share the same dependencies
- # @attr [Array] dependencies the dependencies for this set of possibilities
- # @attr [Array] possibilities the possibilities
- PossibilitySet = Struct.new(:dependencies, :possibilities)
-
- class PossibilitySet
- # String representation of the possibility set, for debugging
- def to_s
- "[#{possibilities.join(', ')}]"
- end
-
- # @return [Object] most up-to-date dependency in the possibility set
- def latest_version
- possibilities.last
- end
- end
-
- # Details of the state to unwind to when a conflict occurs, and the cause of the unwind
- # @attr [Integer] state_index the index of the state to unwind to
- # @attr [Object] state_requirement the requirement of the state we're unwinding to
- # @attr [Array] requirement_tree for the requirement we're relaxing
- # @attr [Array] conflicting_requirements the requirements that combined to cause the conflict
- # @attr [Array] requirement_trees for the conflict
- # @attr [Array] requirements_unwound_to_instead array of unwind requirements that were chosen over this unwind
- UnwindDetails = Struct.new(
- :state_index,
- :state_requirement,
- :requirement_tree,
- :conflicting_requirements,
- :requirement_trees,
- :requirements_unwound_to_instead
- )
-
- class UnwindDetails
- include Comparable
-
- # We compare UnwindDetails when choosing which state to unwind to. If
- # two options have the same state_index we prefer the one most
- # removed from a requirement that caused the conflict. Both options
- # would unwind to the same state, but a `grandparent` option will
- # filter out fewer of its possibilities after doing so - where a state
- # is both a `parent` and a `grandparent` to requirements that have
- # caused a conflict this is the correct behaviour.
- # @param [UnwindDetail] other UnwindDetail to be compared
- # @return [Integer] integer specifying ordering
- def <=>(other)
- if state_index > other.state_index
- 1
- elsif state_index == other.state_index
- reversed_requirement_tree_index <=> other.reversed_requirement_tree_index
- else
- -1
- end
- end
-
- # @return [Integer] index of state requirement in reversed requirement tree
- # (the conflicting requirement itself will be at position 0)
- def reversed_requirement_tree_index
- @reversed_requirement_tree_index ||=
- if state_requirement
- requirement_tree.reverse.index(state_requirement)
- else
- 999_999
- end
- end
-
- # @return [Boolean] where the requirement of the state we're unwinding
- # to directly caused the conflict. Note: in this case, it is
- # impossible for the state we're unwinding to to be a parent of
- # any of the other conflicting requirements (or we would have
- # circularity)
- def unwinding_to_primary_requirement?
- requirement_tree.last == state_requirement
- end
-
- # @return [Array] array of sub-dependencies to avoid when choosing a
- # new possibility for the state we've unwound to. Only relevant for
- # non-primary unwinds
- def sub_dependencies_to_avoid
- @requirements_to_avoid ||=
- requirement_trees.map do |tree|
- index = tree.index(state_requirement)
- tree[index + 1] if index
- end.compact
- end
-
- # @return [Array] array of all the requirements that led to the need for
- # this unwind
- def all_requirements
- @all_requirements ||= requirement_trees.flatten(1)
- end
- end
-
- # @return [SpecificationProvider] the provider that knows about
- # dependencies, requirements, specifications, versions, etc.
- attr_reader :specification_provider
-
- # @return [UI] the UI that knows how to communicate feedback about the
- # resolution process back to the user
- attr_reader :resolver_ui
-
- # @return [DependencyGraph] the base dependency graph to which
- # dependencies should be 'locked'
- attr_reader :base
-
- # @return [Array] the dependencies that were explicitly required
- attr_reader :original_requested
-
- # Initializes a new resolution.
- # @param [SpecificationProvider] specification_provider
- # see {#specification_provider}
- # @param [UI] resolver_ui see {#resolver_ui}
- # @param [Array] requested see {#original_requested}
- # @param [DependencyGraph] base see {#base}
- def initialize(specification_provider, resolver_ui, requested, base)
- @specification_provider = specification_provider
- @resolver_ui = resolver_ui
- @original_requested = requested
- @base = base
- @states = []
- @iteration_counter = 0
- @parents_of = Hash.new { |h, k| h[k] = [] }
- end
-
- # Resolves the {#original_requested} dependencies into a full dependency
- # graph
- # @raise [ResolverError] if successful resolution is impossible
- # @return [DependencyGraph] the dependency graph of successfully resolved
- # dependencies
- def resolve
- start_resolution
-
- while state
- break if !state.requirement && state.requirements.empty?
- indicate_progress
- if state.respond_to?(:pop_possibility_state) # DependencyState
- debug(depth) { "Creating possibility state for #{requirement} (#{possibilities.count} remaining)" }
- state.pop_possibility_state.tap do |s|
- if s
- states.push(s)
- activated.tag(s)
- end
- end
- end
- process_topmost_state
- end
-
- resolve_activated_specs
- ensure
- end_resolution
- end
-
- # @return [Integer] the number of resolver iterations in between calls to
- # {#resolver_ui}'s {UI#indicate_progress} method
- attr_accessor :iteration_rate
- private :iteration_rate
-
- # @return [Time] the time at which resolution began
- attr_accessor :started_at
- private :started_at
-
- # @return [Array<ResolutionState>] the stack of states for the resolution
- attr_accessor :states
- private :states
-
- private
-
- # Sets up the resolution process
- # @return [void]
- def start_resolution
- @started_at = Time.now
-
- push_initial_state
-
- debug { "Starting resolution (#{@started_at})\nUser-requested dependencies: #{original_requested}" }
- resolver_ui.before_resolution
- end
-
- def resolve_activated_specs
- activated.vertices.each do |_, vertex|
- next unless vertex.payload
-
- latest_version = vertex.payload.possibilities.reverse_each.find do |possibility|
- vertex.requirements.all? { |req| requirement_satisfied_by?(req, activated, possibility) }
- end
-
- activated.set_payload(vertex.name, latest_version)
- end
- activated.freeze
- end
-
- # Ends the resolution process
- # @return [void]
- def end_resolution
- resolver_ui.after_resolution
- debug do
- "Finished resolution (#{@iteration_counter} steps) " \
- "(Took #{(ended_at = Time.now) - @started_at} seconds) (#{ended_at})"
- end
- debug { 'Unactivated: ' + Hash[activated.vertices.reject { |_n, v| v.payload }].keys.join(', ') } if state
- debug { 'Activated: ' + Hash[activated.vertices.select { |_n, v| v.payload }].keys.join(', ') } if state
- end
-
- require_relative 'state'
- require_relative 'modules/specification_provider'
-
- require_relative 'delegates/resolution_state'
- require_relative 'delegates/specification_provider'
-
- include Gem::Resolver::Molinillo::Delegates::ResolutionState
- include Gem::Resolver::Molinillo::Delegates::SpecificationProvider
-
- # Processes the topmost available {RequirementState} on the stack
- # @return [void]
- def process_topmost_state
- if possibility
- attempt_to_activate
- else
- create_conflict
- unwind_for_conflict
- end
- rescue CircularDependencyError => underlying_error
- create_conflict(underlying_error)
- unwind_for_conflict
- end
-
- # @return [Object] the current possibility that the resolution is trying
- # to activate
- def possibility
- possibilities.last
- end
-
- # @return [RequirementState] the current state the resolution is
- # operating upon
- def state
- states.last
- end
-
- # Creates and pushes the initial state for the resolution, based upon the
- # {#requested} dependencies
- # @return [void]
- def push_initial_state
- graph = DependencyGraph.new.tap do |dg|
- original_requested.each do |requested|
- vertex = dg.add_vertex(name_for(requested), nil, true)
- vertex.explicit_requirements << requested
- end
- dg.tag(:initial_state)
- end
-
- push_state_for_requirements(original_requested, true, graph)
- end
-
- # Unwinds the states stack because a conflict has been encountered
- # @return [void]
- def unwind_for_conflict
- details_for_unwind = build_details_for_unwind
- unwind_options = unused_unwind_options
- debug(depth) { "Unwinding for conflict: #{requirement} to #{details_for_unwind.state_index / 2}" }
- conflicts.tap do |c|
- sliced_states = states.slice!((details_for_unwind.state_index + 1)..-1)
- raise_error_unless_state(c)
- activated.rewind_to(sliced_states.first || :initial_state) if sliced_states
- state.conflicts = c
- state.unused_unwind_options = unwind_options
- filter_possibilities_after_unwind(details_for_unwind)
- index = states.size - 1
- @parents_of.each { |_, a| a.reject! { |i| i >= index } }
- state.unused_unwind_options.reject! { |uw| uw.state_index >= index }
- end
- end
-
- # Raises a VersionConflict error, or any underlying error, if there is no
- # current state
- # @return [void]
- def raise_error_unless_state(conflicts)
- return if state
-
- error = conflicts.values.map(&:underlying_error).compact.first
- raise error || VersionConflict.new(conflicts, specification_provider)
- end
-
- # @return [UnwindDetails] Details of the nearest index to which we could unwind
- def build_details_for_unwind
- # Get the possible unwinds for the current conflict
- current_conflict = conflicts[name]
- binding_requirements = binding_requirements_for_conflict(current_conflict)
- unwind_details = unwind_options_for_requirements(binding_requirements)
-
- last_detail_for_current_unwind = unwind_details.sort.last
- current_detail = last_detail_for_current_unwind
-
- # Look for past conflicts that could be unwound to affect the
- # requirement tree for the current conflict
- all_reqs = last_detail_for_current_unwind.all_requirements
- all_reqs_size = all_reqs.size
- relevant_unused_unwinds = unused_unwind_options.select do |alternative|
- diff_reqs = all_reqs - alternative.requirements_unwound_to_instead
- next if diff_reqs.size == all_reqs_size
- # Find the highest index unwind whilst looping through
- current_detail = alternative if alternative > current_detail
- alternative
- end
-
- # Add the current unwind options to the `unused_unwind_options` array.
- # The "used" option will be filtered out during `unwind_for_conflict`.
- state.unused_unwind_options += unwind_details.reject { |detail| detail.state_index == -1 }
-
- # Update the requirements_unwound_to_instead on any relevant unused unwinds
- relevant_unused_unwinds.each do |d|
- (d.requirements_unwound_to_instead << current_detail.state_requirement).uniq!
- end
- unwind_details.each do |d|
- (d.requirements_unwound_to_instead << current_detail.state_requirement).uniq!
- end
-
- current_detail
- end
-
- # @param [Array<Object>] binding_requirements array of requirements that combine to create a conflict
- # @return [Array<UnwindDetails>] array of UnwindDetails that have a chance
- # of resolving the passed requirements
- def unwind_options_for_requirements(binding_requirements)
- unwind_details = []
-
- trees = []
- binding_requirements.reverse_each do |r|
- partial_tree = [r]
- trees << partial_tree
- unwind_details << UnwindDetails.new(-1, nil, partial_tree, binding_requirements, trees, [])
-
- # If this requirement has alternative possibilities, check if any would
- # satisfy the other requirements that created this conflict
- requirement_state = find_state_for(r)
- if conflict_fixing_possibilities?(requirement_state, binding_requirements)
- unwind_details << UnwindDetails.new(
- states.index(requirement_state),
- r,
- partial_tree,
- binding_requirements,
- trees,
- []
- )
- end
-
- # Next, look at the parent of this requirement, and check if the requirement
- # could have been avoided if an alternative PossibilitySet had been chosen
- parent_r = parent_of(r)
- next if parent_r.nil?
- partial_tree.unshift(parent_r)
- requirement_state = find_state_for(parent_r)
- if requirement_state.possibilities.any? { |set| !set.dependencies.include?(r) }
- unwind_details << UnwindDetails.new(
- states.index(requirement_state),
- parent_r,
- partial_tree,
- binding_requirements,
- trees,
- []
- )
- end
-
- # Finally, look at the grandparent and up of this requirement, looking
- # for any possibilities that wouldn't create their parent requirement
- grandparent_r = parent_of(parent_r)
- until grandparent_r.nil?
- partial_tree.unshift(grandparent_r)
- requirement_state = find_state_for(grandparent_r)
- if requirement_state.possibilities.any? { |set| !set.dependencies.include?(parent_r) }
- unwind_details << UnwindDetails.new(
- states.index(requirement_state),
- grandparent_r,
- partial_tree,
- binding_requirements,
- trees,
- []
- )
- end
- parent_r = grandparent_r
- grandparent_r = parent_of(parent_r)
- end
- end
-
- unwind_details
- end
-
- # @param [DependencyState] state
- # @param [Array] binding_requirements array of requirements
- # @return [Boolean] whether or not the given state has any possibilities
- # that could satisfy the given requirements
- def conflict_fixing_possibilities?(state, binding_requirements)
- return false unless state
-
- state.possibilities.any? do |possibility_set|
- possibility_set.possibilities.any? do |poss|
- possibility_satisfies_requirements?(poss, binding_requirements)
- end
- end
- end
-
- # Filter's a state's possibilities to remove any that would not fix the
- # conflict we've just rewound from
- # @param [UnwindDetails] unwind_details details of the conflict just
- # unwound from
- # @return [void]
- def filter_possibilities_after_unwind(unwind_details)
- return unless state && !state.possibilities.empty?
-
- if unwind_details.unwinding_to_primary_requirement?
- filter_possibilities_for_primary_unwind(unwind_details)
- else
- filter_possibilities_for_parent_unwind(unwind_details)
- end
- end
-
- # Filter's a state's possibilities to remove any that would not satisfy
- # the requirements in the conflict we've just rewound from
- # @param [UnwindDetails] unwind_details details of the conflict just unwound from
- # @return [void]
- def filter_possibilities_for_primary_unwind(unwind_details)
- unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
- unwinds_to_state << unwind_details
- unwind_requirement_sets = unwinds_to_state.map(&:conflicting_requirements)
-
- state.possibilities.reject! do |possibility_set|
- possibility_set.possibilities.none? do |poss|
- unwind_requirement_sets.any? do |requirements|
- possibility_satisfies_requirements?(poss, requirements)
- end
- end
- end
- end
-
- # @param [Object] possibility a single possibility
- # @param [Array] requirements an array of requirements
- # @return [Boolean] whether the possibility satisfies all of the
- # given requirements
- def possibility_satisfies_requirements?(possibility, requirements)
- name = name_for(possibility)
-
- activated.tag(:swap)
- activated.set_payload(name, possibility) if activated.vertex_named(name)
- satisfied = requirements.all? { |r| requirement_satisfied_by?(r, activated, possibility) }
- activated.rewind_to(:swap)
-
- satisfied
- end
-
- # Filter's a state's possibilities to remove any that would (eventually)
- # create a requirement in the conflict we've just rewound from
- # @param [UnwindDetails] unwind_details details of the conflict just unwound from
- # @return [void]
- def filter_possibilities_for_parent_unwind(unwind_details)
- unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
- unwinds_to_state << unwind_details
-
- primary_unwinds = unwinds_to_state.select(&:unwinding_to_primary_requirement?).uniq
- parent_unwinds = unwinds_to_state.uniq - primary_unwinds
-
- allowed_possibility_sets = primary_unwinds.flat_map do |unwind|
- states[unwind.state_index].possibilities.select do |possibility_set|
- possibility_set.possibilities.any? do |poss|
- possibility_satisfies_requirements?(poss, unwind.conflicting_requirements)
- end
- end
- end
-
- requirements_to_avoid = parent_unwinds.flat_map(&:sub_dependencies_to_avoid)
-
- state.possibilities.reject! do |possibility_set|
- !allowed_possibility_sets.include?(possibility_set) &&
- (requirements_to_avoid - possibility_set.dependencies).empty?
- end
- end
-
- # @param [Conflict] conflict
- # @return [Array] minimal array of requirements that would cause the passed
- # conflict to occur.
- def binding_requirements_for_conflict(conflict)
- return [conflict.requirement] if conflict.possibility.nil?
-
- possible_binding_requirements = conflict.requirements.values.flatten(1).uniq
-
- # When there's a `CircularDependency` error the conflicting requirement
- # (the one causing the circular) won't be `conflict.requirement`
- # (which won't be for the right state, because we won't have created it,
- # because it's circular).
- # We need to make sure we have that requirement in the conflict's list,
- # otherwise we won't be able to unwind properly, so we just return all
- # the requirements for the conflict.
- return possible_binding_requirements if conflict.underlying_error
-
- possibilities = search_for(conflict.requirement)
-
- # If all the requirements together don't filter out all possibilities,
- # then the only two requirements we need to consider are the initial one
- # (where the dependency's version was first chosen) and the last
- if binding_requirement_in_set?(nil, possible_binding_requirements, possibilities)
- return [conflict.requirement, requirement_for_existing_name(name_for(conflict.requirement))].compact
- end
-
- # Loop through the possible binding requirements, removing each one
- # that doesn't bind. Use a `reverse_each` as we want the earliest set of
- # binding requirements, and don't use `reject!` as we wish to refine the
- # array *on each iteration*.
- binding_requirements = possible_binding_requirements.dup
- possible_binding_requirements.reverse_each do |req|
- next if req == conflict.requirement
- unless binding_requirement_in_set?(req, binding_requirements, possibilities)
- binding_requirements -= [req]
- end
- end
-
- binding_requirements
- end
-
- # @param [Object] requirement we wish to check
- # @param [Array] possible_binding_requirements array of requirements
- # @param [Array] possibilities array of possibilities the requirements will be used to filter
- # @return [Boolean] whether or not the given requirement is required to filter
- # out all elements of the array of possibilities.
- def binding_requirement_in_set?(requirement, possible_binding_requirements, possibilities)
- possibilities.any? do |poss|
- possibility_satisfies_requirements?(poss, possible_binding_requirements - [requirement])
- end
- end
-
- # @param [Object] requirement
- # @return [Object] the requirement that led to `requirement` being added
- # to the list of requirements.
- def parent_of(requirement)
- return unless requirement
- return unless index = @parents_of[requirement].last
- return unless parent_state = @states[index]
- parent_state.requirement
- end
-
- # @param [String] name
- # @return [Object] the requirement that led to a version of a possibility
- # with the given name being activated.
- def requirement_for_existing_name(name)
- return nil unless vertex = activated.vertex_named(name)
- return nil unless vertex.payload
- states.find { |s| s.name == name }.requirement
- end
-
- # @param [Object] requirement
- # @return [ResolutionState] the state whose `requirement` is the given
- # `requirement`.
- def find_state_for(requirement)
- return nil unless requirement
- states.find { |i| requirement == i.requirement }
- end
-
- # @param [Object] underlying_error
- # @return [Conflict] a {Conflict} that reflects the failure to activate
- # the {#possibility} in conjunction with the current {#state}
- def create_conflict(underlying_error = nil)
- vertex = activated.vertex_named(name)
- locked_requirement = locked_requirement_named(name)
-
- requirements = {}
- unless vertex.explicit_requirements.empty?
- requirements[name_for_explicit_dependency_source] = vertex.explicit_requirements
- end
- requirements[name_for_locking_dependency_source] = [locked_requirement] if locked_requirement
- vertex.incoming_edges.each do |edge|
- (requirements[edge.origin.payload.latest_version] ||= []).unshift(edge.requirement)
- end
-
- activated_by_name = {}
- activated.each { |v| activated_by_name[v.name] = v.payload.latest_version if v.payload }
- conflicts[name] = Conflict.new(
- requirement,
- requirements,
- vertex.payload && vertex.payload.latest_version,
- possibility,
- locked_requirement,
- requirement_trees,
- activated_by_name,
- underlying_error
- )
- end
-
- # @return [Array<Array<Object>>] The different requirement
- # trees that led to every requirement for the current spec.
- def requirement_trees
- vertex = activated.vertex_named(name)
- vertex.requirements.map { |r| requirement_tree_for(r) }
- end
-
- # @param [Object] requirement
- # @return [Array<Object>] the list of requirements that led to
- # `requirement` being required.
- def requirement_tree_for(requirement)
- tree = []
- while requirement
- tree.unshift(requirement)
- requirement = parent_of(requirement)
- end
- tree
- end
-
- # Indicates progress roughly once every second
- # @return [void]
- def indicate_progress
- @iteration_counter += 1
- @progress_rate ||= resolver_ui.progress_rate
- if iteration_rate.nil?
- if Time.now - started_at >= @progress_rate
- self.iteration_rate = @iteration_counter
- end
- end
-
- if iteration_rate && (@iteration_counter % iteration_rate) == 0
- resolver_ui.indicate_progress
- end
- end
-
- # Calls the {#resolver_ui}'s {UI#debug} method
- # @param [Integer] depth the depth of the {#states} stack
- # @param [Proc] block a block that yields a {#to_s}
- # @return [void]
- def debug(depth = 0, &block)
- resolver_ui.debug(depth, &block)
- end
-
- # Attempts to activate the current {#possibility}
- # @return [void]
- def attempt_to_activate
- debug(depth) { 'Attempting to activate ' + possibility.to_s }
- existing_vertex = activated.vertex_named(name)
- if existing_vertex.payload
- debug(depth) { "Found existing spec (#{existing_vertex.payload})" }
- attempt_to_filter_existing_spec(existing_vertex)
- else
- latest = possibility.latest_version
- possibility.possibilities.select! do |possibility|
- requirement_satisfied_by?(requirement, activated, possibility)
- end
- if possibility.latest_version.nil?
- # ensure there's a possibility for better error messages
- possibility.possibilities << latest if latest
- create_conflict
- unwind_for_conflict
- else
- activate_new_spec
- end
- end
- end
-
- # Attempts to update the existing vertex's `PossibilitySet` with a filtered version
- # @return [void]
- def attempt_to_filter_existing_spec(vertex)
- filtered_set = filtered_possibility_set(vertex)
- if !filtered_set.possibilities.empty?
- activated.set_payload(name, filtered_set)
- new_requirements = requirements.dup
- push_state_for_requirements(new_requirements, false)
- else
- create_conflict
- debug(depth) { "Unsatisfied by existing spec (#{vertex.payload})" }
- unwind_for_conflict
- end
- end
-
- # Generates a filtered version of the existing vertex's `PossibilitySet` using the
- # current state's `requirement`
- # @param [Object] vertex existing vertex
- # @return [PossibilitySet] filtered possibility set
- def filtered_possibility_set(vertex)
- PossibilitySet.new(vertex.payload.dependencies, vertex.payload.possibilities & possibility.possibilities)
- end
-
- # @param [String] requirement_name the spec name to search for
- # @return [Object] the locked spec named `requirement_name`, if one
- # is found on {#base}
- def locked_requirement_named(requirement_name)
- vertex = base.vertex_named(requirement_name)
- vertex && vertex.payload
- end
-
- # Add the current {#possibility} to the dependency graph of the current
- # {#state}
- # @return [void]
- def activate_new_spec
- conflicts.delete(name)
- debug(depth) { "Activated #{name} at #{possibility}" }
- activated.set_payload(name, possibility)
- require_nested_dependencies_for(possibility)
- end
-
- # Requires the dependencies that the recently activated spec has
- # @param [Object] possibility_set the PossibilitySet that has just been
- # activated
- # @return [void]
- def require_nested_dependencies_for(possibility_set)
- nested_dependencies = dependencies_for(possibility_set.latest_version)
- debug(depth) { "Requiring nested dependencies (#{nested_dependencies.join(', ')})" }
- nested_dependencies.each do |d|
- activated.add_child_vertex(name_for(d), nil, [name_for(possibility_set.latest_version)], d)
- parent_index = states.size - 1
- parents = @parents_of[d]
- parents << parent_index if parents.empty?
- end
-
- push_state_for_requirements(requirements + nested_dependencies, !nested_dependencies.empty?)
- end
-
- # Pushes a new {DependencyState} that encapsulates both existing and new
- # requirements
- # @param [Array] new_requirements
- # @param [Boolean] requires_sort
- # @param [Object] new_activated
- # @return [void]
- def push_state_for_requirements(new_requirements, requires_sort = true, new_activated = activated)
- new_requirements = sort_dependencies(new_requirements.uniq, new_activated, conflicts) if requires_sort
- new_requirement = nil
- loop do
- new_requirement = new_requirements.shift
- break if new_requirement.nil? || states.none? { |s| s.requirement == new_requirement }
- end
- new_name = new_requirement ? name_for(new_requirement) : ''.freeze
- possibilities = possibilities_for_requirement(new_requirement)
- handle_missing_or_push_dependency_state DependencyState.new(
- new_name, new_requirements, new_activated,
- new_requirement, possibilities, depth, conflicts.dup, unused_unwind_options.dup
- )
- end
-
- # Checks a proposed requirement with any existing locked requirement
- # before generating an array of possibilities for it.
- # @param [Object] requirement the proposed requirement
- # @param [Object] activated
- # @return [Array] possibilities
- def possibilities_for_requirement(requirement, activated = self.activated)
- return [] unless requirement
- if locked_requirement_named(name_for(requirement))
- return locked_requirement_possibility_set(requirement, activated)
- end
-
- group_possibilities(search_for(requirement))
- end
-
- # @param [Object] requirement the proposed requirement
- # @param [Object] activated
- # @return [Array] possibility set containing only the locked requirement, if any
- def locked_requirement_possibility_set(requirement, activated = self.activated)
- all_possibilities = search_for(requirement)
- locked_requirement = locked_requirement_named(name_for(requirement))
-
- # Longwinded way to build a possibilities array with either the locked
- # requirement or nothing in it. Required, since the API for
- # locked_requirement isn't guaranteed.
- locked_possibilities = all_possibilities.select do |possibility|
- requirement_satisfied_by?(locked_requirement, activated, possibility)
- end
-
- group_possibilities(locked_possibilities)
- end
-
- # Build an array of PossibilitySets, with each element representing a group of
- # dependency versions that all have the same sub-dependency version constraints
- # and are contiguous.
- # @param [Array] possibilities an array of possibilities
- # @return [Array<PossibilitySet>] an array of possibility sets
- def group_possibilities(possibilities)
- possibility_sets = []
- current_possibility_set = nil
-
- possibilities.reverse_each do |possibility|
- dependencies = dependencies_for(possibility)
- if current_possibility_set && dependencies_equal?(current_possibility_set.dependencies, dependencies)
- current_possibility_set.possibilities.unshift(possibility)
- else
- possibility_sets.unshift(PossibilitySet.new(dependencies, [possibility]))
- current_possibility_set = possibility_sets.first
- end
- end
-
- possibility_sets
- end
-
- # Pushes a new {DependencyState}.
- # If the {#specification_provider} says to
- # {SpecificationProvider#allow_missing?} that particular requirement, and
- # there are no possibilities for that requirement, then `state` is not
- # pushed, and the vertex in {#activated} is removed, and we continue
- # resolving the remaining requirements.
- # @param [DependencyState] state
- # @return [void]
- def handle_missing_or_push_dependency_state(state)
- if state.requirement && state.possibilities.empty? && allow_missing?(state.requirement)
- state.activated.detach_vertex_named(state.name)
- push_state_for_requirements(state.requirements.dup, false, state.activated)
- else
- states.push(state).tap { activated.tag(state) }
- end
- end
- end
- end
-end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/resolver.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/resolver.rb
deleted file mode 100644
index d43121f8ca..0000000000
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/resolver.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'dependency_graph'
-
-module Gem::Resolver::Molinillo
- # This class encapsulates a dependency resolver.
- # The resolver is responsible for determining which set of dependencies to
- # activate, with feedback from the {#specification_provider}
- #
- #
- class Resolver
- require_relative 'resolution'
-
- # @return [SpecificationProvider] the specification provider used
- # in the resolution process
- attr_reader :specification_provider
-
- # @return [UI] the UI module used to communicate back to the user
- # during the resolution process
- attr_reader :resolver_ui
-
- # Initializes a new resolver.
- # @param [SpecificationProvider] specification_provider
- # see {#specification_provider}
- # @param [UI] resolver_ui
- # see {#resolver_ui}
- def initialize(specification_provider, resolver_ui)
- @specification_provider = specification_provider
- @resolver_ui = resolver_ui
- end
-
- # Resolves the requested dependencies into a {DependencyGraph},
- # locking to the base dependency graph (if specified)
- # @param [Array] requested an array of 'requested' dependencies that the
- # {#specification_provider} can understand
- # @param [DependencyGraph,nil] base the base dependency graph to which
- # dependencies should be 'locked'
- def resolve(requested, base = DependencyGraph.new)
- Resolution.new(specification_provider,
- resolver_ui,
- requested,
- base).
- resolve
- end
- end
-end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/state.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/state.rb
deleted file mode 100644
index 6e7c715fce..0000000000
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/state.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-# frozen_string_literal: true
-
-module Gem::Resolver::Molinillo
- # A state that a {Resolution} can be in
- # @attr [String] name the name of the current requirement
- # @attr [Array<Object>] requirements currently unsatisfied requirements
- # @attr [DependencyGraph] activated the graph of activated dependencies
- # @attr [Object] requirement the current requirement
- # @attr [Object] possibilities the possibilities to satisfy the current requirement
- # @attr [Integer] depth the depth of the resolution
- # @attr [Hash] conflicts unresolved conflicts, indexed by dependency name
- # @attr [Array<UnwindDetails>] unused_unwind_options unwinds for previous conflicts that weren't explored
- ResolutionState = Struct.new(
- :name,
- :requirements,
- :activated,
- :requirement,
- :possibilities,
- :depth,
- :conflicts,
- :unused_unwind_options
- )
-
- class ResolutionState
- # Returns an empty resolution state
- # @return [ResolutionState] an empty state
- def self.empty
- new(nil, [], DependencyGraph.new, nil, nil, 0, {}, [])
- end
- end
-
- # A state that encapsulates a set of {#requirements} with an {Array} of
- # possibilities
- class DependencyState < ResolutionState
- # Removes a possibility from `self`
- # @return [PossibilityState] a state with a single possibility,
- # the possibility that was removed from `self`
- def pop_possibility_state
- PossibilityState.new(
- name,
- requirements.dup,
- activated,
- requirement,
- [possibilities.pop],
- depth + 1,
- conflicts.dup,
- unused_unwind_options.dup
- ).tap do |state|
- state.activated.tag(state)
- end
- end
- end
-
- # A state that encapsulates a single possibility to fulfill the given
- # {#requirement}
- class PossibilityState < ResolutionState
- end
-end
diff --git a/lib/rubygems/resolver/spec_specification.rb b/lib/rubygems/resolver/spec_specification.rb
index 79a34d8063..00ef9fdba0 100644
--- a/lib/rubygems/resolver/spec_specification.rb
+++ b/lib/rubygems/resolver/spec_specification.rb
@@ -66,4 +66,11 @@ class Gem::Resolver::SpecSpecification < Gem::Resolver::Specification
def version
spec.version
end
+
+ ##
+ # The hash value for this specification.
+
+ def hash
+ spec.hash
+ end
end
diff --git a/lib/rubygems/s3_uri_signer.rb b/lib/rubygems/s3_uri_signer.rb
index 20e95345d1..7c95a9d4f5 100644
--- a/lib/rubygems/s3_uri_signer.rb
+++ b/lib/rubygems/s3_uri_signer.rb
@@ -49,7 +49,7 @@ class Gem::S3URISigner
string_to_sign = generate_string_to_sign(date_time, credential_info, canonical_request)
signature = generate_signature(s3_config, date, string_to_sign)
- URI.parse("https://#{canonical_host}#{uri.path}?#{query_params}&X-Amz-Signature=#{signature}")
+ Gem::URI.parse("https://#{canonical_host}#{uri.path}?#{query_params}&X-Amz-Signature=#{signature}")
end
private
@@ -140,7 +140,7 @@ class Gem::S3URISigner
end
def ec2_metadata_credentials_json
- require "net/http"
+ require_relative "vendored_net_http"
require_relative "request"
require_relative "request/connection_pools"
require "json"
@@ -152,13 +152,13 @@ class Gem::S3URISigner
end
def ec2_metadata_request(url)
- uri = URI(url)
+ uri = Gem::URI(url)
@request_pool ||= create_request_pool(uri)
- request = Gem::Request.new(uri, Net::HTTP::Get, nil, @request_pool)
+ request = Gem::Request.new(uri, Gem::Net::HTTP::Get, nil, @request_pool)
response = request.fetch
case response
- when Net::HTTPOK then
+ when Gem::Net::HTTPOK then
JSON.parse(response.body)
else
raise InstanceProfileError.new("Unable to fetch AWS metadata from #{uri}: #{response.message} #{response.code}")
diff --git a/lib/rubygems/safe_marshal.rb b/lib/rubygems/safe_marshal.rb
new file mode 100644
index 0000000000..b81d1a0a47
--- /dev/null
+++ b/lib/rubygems/safe_marshal.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+require "stringio"
+
+require_relative "safe_marshal/reader"
+require_relative "safe_marshal/visitors/to_ruby"
+
+module Gem
+ ###
+ # This module is used for safely loading Marshal specs from a gem. The
+ # `safe_load` method defined on this module is specifically designed for
+ # loading Gem specifications.
+
+ module SafeMarshal
+ PERMITTED_CLASSES = %w[
+ Date
+ Time
+ Rational
+
+ Gem::Dependency
+ Gem::NameTuple
+ Gem::Platform
+ Gem::Requirement
+ Gem::Specification
+ Gem::Version
+ Gem::Version::Requirement
+
+ YAML::Syck::DefaultKey
+ YAML::PrivateType
+ ].freeze
+ private_constant :PERMITTED_CLASSES
+
+ PERMITTED_SYMBOLS = %w[
+ development
+ runtime
+
+ name
+ number
+ platform
+ dependencies
+ ].freeze
+ private_constant :PERMITTED_SYMBOLS
+
+ PERMITTED_IVARS = {
+ "String" => %w[E encoding @taguri @debug_created_info],
+ "Time" => %w[
+ offset zone nano_num nano_den submicro
+ @_zone @marshal_with_utc_coercion
+ ],
+ "Gem::Dependency" => %w[
+ @name @requirement @prerelease @version_requirement @version_requirements @type
+ @force_ruby_platform
+ ],
+ "Gem::NameTuple" => %w[@name @version @platform],
+ "Gem::Platform" => %w[@os @cpu @version],
+ "Psych::PrivateType" => %w[@value @type_id],
+ }.freeze
+ private_constant :PERMITTED_IVARS
+
+ def self.safe_load(input)
+ load(input, permitted_classes: PERMITTED_CLASSES, permitted_symbols: PERMITTED_SYMBOLS, permitted_ivars: PERMITTED_IVARS)
+ end
+
+ def self.load(input, permitted_classes: [::Symbol], permitted_symbols: [], permitted_ivars: {})
+ root = Reader.new(StringIO.new(input, "r").binmode).read!
+
+ Visitors::ToRuby.new(
+ permitted_classes: permitted_classes,
+ permitted_symbols: permitted_symbols,
+ permitted_ivars: permitted_ivars,
+ ).visit(root)
+ end
+ end
+end
diff --git a/lib/rubygems/safe_marshal/elements.rb b/lib/rubygems/safe_marshal/elements.rb
new file mode 100644
index 0000000000..f8874b1b2f
--- /dev/null
+++ b/lib/rubygems/safe_marshal/elements.rb
@@ -0,0 +1,146 @@
+# frozen_string_literal: true
+
+module Gem
+ module SafeMarshal
+ module Elements
+ class Element
+ end
+
+ class Symbol < Element
+ def initialize(name)
+ @name = name
+ end
+ attr_reader :name
+ end
+
+ class UserDefined < Element
+ def initialize(name, binary_string)
+ @name = name
+ @binary_string = binary_string
+ end
+
+ attr_reader :name, :binary_string
+ end
+
+ class UserMarshal < Element
+ def initialize(name, data)
+ @name = name
+ @data = data
+ end
+
+ attr_reader :name, :data
+ end
+
+ class String < Element
+ def initialize(str)
+ @str = str
+ end
+
+ attr_reader :str
+ end
+
+ class Hash < Element
+ def initialize(pairs)
+ @pairs = pairs
+ end
+
+ attr_reader :pairs
+ end
+
+ class HashWithDefaultValue < Hash
+ def initialize(pairs, default)
+ super(pairs)
+ @default = default
+ end
+
+ attr_reader :default
+ end
+
+ class Array < Element
+ def initialize(elements)
+ @elements = elements
+ end
+
+ attr_reader :elements
+ end
+
+ class Integer < Element
+ def initialize(int)
+ @int = int
+ end
+
+ attr_reader :int
+ end
+
+ class True < Element
+ def initialize
+ end
+ TRUE = new.freeze
+ end
+
+ class False < Element
+ def initialize
+ end
+
+ FALSE = new.freeze
+ end
+
+ class WithIvars < Element
+ def initialize(object, ivars)
+ @object = object
+ @ivars = ivars
+ end
+
+ attr_reader :object, :ivars
+ end
+
+ class Object < Element
+ def initialize(name)
+ @name = name
+ end
+ attr_reader :name
+ end
+
+ class Nil < Element
+ NIL = new.freeze
+ end
+
+ class ObjectLink < Element
+ def initialize(offset)
+ @offset = offset
+ end
+ attr_reader :offset
+ end
+
+ class SymbolLink < Element
+ def initialize(offset)
+ @offset = offset
+ end
+ attr_reader :offset
+ end
+
+ class Float < Element
+ def initialize(string)
+ @string = string
+ end
+ attr_reader :string
+ end
+
+ class Bignum < Element # rubocop:disable Lint/UnifiedInteger
+ def initialize(sign, data)
+ @sign = sign
+ @data = data
+ end
+ attr_reader :sign, :data
+ end
+
+ class UserClass < Element
+ def initialize(name, wrapped_object)
+ @name = name
+ @wrapped_object = wrapped_object
+ end
+ attr_reader :name, :wrapped_object
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/safe_marshal/reader.rb b/lib/rubygems/safe_marshal/reader.rb
new file mode 100644
index 0000000000..740be113e5
--- /dev/null
+++ b/lib/rubygems/safe_marshal/reader.rb
@@ -0,0 +1,308 @@
+# frozen_string_literal: true
+
+require_relative "elements"
+
+module Gem
+ module SafeMarshal
+ class Reader
+ class Error < StandardError
+ end
+
+ class UnsupportedVersionError < Error
+ end
+
+ class UnconsumedBytesError < Error
+ end
+
+ class NotImplementedError < Error
+ end
+
+ class EOFError < Error
+ end
+
+ def initialize(io)
+ @io = io
+ end
+
+ def read!
+ read_header
+ root = read_element
+ raise UnconsumedBytesError unless @io.eof?
+ root
+ end
+
+ private
+
+ MARSHAL_VERSION = [Marshal::MAJOR_VERSION, Marshal::MINOR_VERSION].map(&:chr).join.freeze
+ private_constant :MARSHAL_VERSION
+
+ def read_header
+ v = @io.read(2)
+ raise UnsupportedVersionError, "Unsupported marshal version #{v.bytes.map(&:ord).join(".")}, expected #{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}" unless v == MARSHAL_VERSION
+ end
+
+ def read_byte
+ @io.getbyte
+ end
+
+ def read_integer
+ b = read_byte
+
+ case b
+ when 0x00
+ 0
+ when 0x01
+ read_byte
+ when 0x02
+ read_byte | (read_byte << 8)
+ when 0x03
+ read_byte | (read_byte << 8) | (read_byte << 16)
+ when 0x04
+ read_byte | (read_byte << 8) | (read_byte << 16) | (read_byte << 24)
+ when 0xFC
+ read_byte | (read_byte << 8) | (read_byte << 16) | (read_byte << 24) | -0x100000000
+ when 0xFD
+ read_byte | (read_byte << 8) | (read_byte << 16) | -0x1000000
+ when 0xFE
+ read_byte | (read_byte << 8) | -0x10000
+ when 0xFF
+ read_byte | -0x100
+ when nil
+ raise EOFError, "Unexpected EOF"
+ else
+ signed = (b ^ 128) - 128
+ if b >= 128
+ signed + 5
+ else
+ signed - 5
+ end
+ end
+ end
+
+ def read_element
+ type = read_byte
+ case type
+ when 34 then read_string # ?"
+ when 48 then read_nil # ?0
+ when 58 then read_symbol # ?:
+ when 59 then read_symbol_link # ?;
+ when 64 then read_object_link # ?@
+ when 70 then read_false # ?F
+ when 73 then read_object_with_ivars # ?I
+ when 84 then read_true # ?T
+ when 85 then read_user_marshal # ?U
+ when 91 then read_array # ?[
+ when 102 then read_float # ?f
+ when 105 then Elements::Integer.new(read_integer) # ?i
+ when 108 then read_bignum # ?l
+ when 111 then read_object # ?o
+ when 117 then read_user_defined # ?u
+ when 123 then read_hash # ?{
+ when 125 then read_hash_with_default_value # ?}
+ when 101 then read_extended_object # ?e
+ when 99 then read_class # ?c
+ when 109 then read_module # ?m
+ when 77 then read_class_or_module # ?M
+ when 100 then read_data # ?d
+ when 47 then read_regexp # ?/
+ when 83 then read_struct # ?S
+ when 67 then read_user_class # ?C
+ when nil
+ raise EOFError, "Unexpected EOF"
+ else
+ raise Error, "Unknown marshal type discriminator #{type.chr.inspect} (#{type})"
+ end
+ end
+
+ STRING_E_SYMBOL = Elements::Symbol.new("E").freeze
+ private_constant :STRING_E_SYMBOL
+
+ def read_symbol
+ len = read_integer
+ if len == 1
+ byte = read_byte
+ if byte == 69 # ?E
+ STRING_E_SYMBOL
+ else
+ Elements::Symbol.new(byte.chr)
+ end
+ else
+ name = -@io.read(len)
+ Elements::Symbol.new(name)
+ end
+ end
+
+ EMPTY_STRING = Elements::String.new("".b.freeze).freeze
+ private_constant :EMPTY_STRING
+
+ def read_string
+ length = read_integer
+ return EMPTY_STRING if length == 0
+ str = @io.read(length)
+ Elements::String.new(str)
+ end
+
+ def read_true
+ Elements::True::TRUE
+ end
+
+ def read_false
+ Elements::False::FALSE
+ end
+
+ def read_user_defined
+ name = read_element
+ binary_string = @io.read(read_integer)
+ Elements::UserDefined.new(name, binary_string)
+ end
+
+ EMPTY_ARRAY = Elements::Array.new([].freeze).freeze
+ private_constant :EMPTY_ARRAY
+
+ def read_array
+ length = read_integer
+ return EMPTY_ARRAY if length == 0
+ elements = Array.new(length) do
+ read_element
+ end
+ Elements::Array.new(elements)
+ end
+
+ def read_object_with_ivars
+ object = read_element
+ ivars = Array.new(read_integer) do
+ [read_element, read_element]
+ end
+ Elements::WithIvars.new(object, ivars)
+ end
+
+ def read_symbol_link
+ offset = read_integer
+ Elements::SymbolLink.new(offset)
+ end
+
+ def read_user_marshal
+ name = read_element
+ data = read_element
+ Elements::UserMarshal.new(name, data)
+ end
+
+ # profiling bundle install --full-index shows that
+ # offset 6 is by far the most common object link,
+ # so we special case it to avoid allocating a new
+ # object a third of the time.
+ # the following are all the object links that
+ # appear more than 10000 times in my profiling
+
+ OBJECT_LINKS = {
+ 6 => Elements::ObjectLink.new(6).freeze,
+ 30 => Elements::ObjectLink.new(30).freeze,
+ 81 => Elements::ObjectLink.new(81).freeze,
+ 34 => Elements::ObjectLink.new(34).freeze,
+ 38 => Elements::ObjectLink.new(38).freeze,
+ 50 => Elements::ObjectLink.new(50).freeze,
+ 91 => Elements::ObjectLink.new(91).freeze,
+ 42 => Elements::ObjectLink.new(42).freeze,
+ 46 => Elements::ObjectLink.new(46).freeze,
+ 150 => Elements::ObjectLink.new(150).freeze,
+ 100 => Elements::ObjectLink.new(100).freeze,
+ 104 => Elements::ObjectLink.new(104).freeze,
+ 108 => Elements::ObjectLink.new(108).freeze,
+ 242 => Elements::ObjectLink.new(242).freeze,
+ 246 => Elements::ObjectLink.new(246).freeze,
+ 139 => Elements::ObjectLink.new(139).freeze,
+ 143 => Elements::ObjectLink.new(143).freeze,
+ 114 => Elements::ObjectLink.new(114).freeze,
+ 308 => Elements::ObjectLink.new(308).freeze,
+ 200 => Elements::ObjectLink.new(200).freeze,
+ 54 => Elements::ObjectLink.new(54).freeze,
+ 62 => Elements::ObjectLink.new(62).freeze,
+ 1_286_245 => Elements::ObjectLink.new(1_286_245).freeze,
+ }.freeze
+ private_constant :OBJECT_LINKS
+
+ def read_object_link
+ offset = read_integer
+ OBJECT_LINKS[offset] || Elements::ObjectLink.new(offset)
+ end
+
+ EMPTY_HASH = Elements::Hash.new([].freeze).freeze
+ private_constant :EMPTY_HASH
+
+ def read_hash
+ length = read_integer
+ return EMPTY_HASH if length == 0
+ pairs = Array.new(length) do
+ [read_element, read_element]
+ end
+ Elements::Hash.new(pairs)
+ end
+
+ def read_hash_with_default_value
+ pairs = Array.new(read_integer) do
+ [read_element, read_element]
+ end
+ default = read_element
+ Elements::HashWithDefaultValue.new(pairs, default)
+ end
+
+ def read_object
+ name = read_element
+ object = Elements::Object.new(name)
+ ivars = Array.new(read_integer) do
+ [read_element, read_element]
+ end
+ Elements::WithIvars.new(object, ivars)
+ end
+
+ def read_nil
+ Elements::Nil::NIL
+ end
+
+ def read_float
+ string = @io.read(read_integer)
+ Elements::Float.new(string)
+ end
+
+ def read_bignum
+ sign = read_byte
+ data = @io.read(read_integer * 2)
+ Elements::Bignum.new(sign, data)
+ end
+
+ def read_extended_object
+ raise NotImplementedError, "Reading Marshal objects of type extended_object is not implemented"
+ end
+
+ def read_class
+ raise NotImplementedError, "Reading Marshal objects of type class is not implemented"
+ end
+
+ def read_module
+ raise NotImplementedError, "Reading Marshal objects of type module is not implemented"
+ end
+
+ def read_class_or_module
+ raise NotImplementedError, "Reading Marshal objects of type class_or_module is not implemented"
+ end
+
+ def read_data
+ raise NotImplementedError, "Reading Marshal objects of type data is not implemented"
+ end
+
+ def read_regexp
+ raise NotImplementedError, "Reading Marshal objects of type regexp is not implemented"
+ end
+
+ def read_struct
+ raise NotImplementedError, "Reading Marshal objects of type struct is not implemented"
+ end
+
+ def read_user_class
+ name = read_element
+ wrapped_object = read_element
+ Elements::UserClass.new(name, wrapped_object)
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/safe_marshal/visitors/stream_printer.rb b/lib/rubygems/safe_marshal/visitors/stream_printer.rb
new file mode 100644
index 0000000000..162b36ad05
--- /dev/null
+++ b/lib/rubygems/safe_marshal/visitors/stream_printer.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require_relative "visitor"
+
+module Gem::SafeMarshal
+ module Visitors
+ class StreamPrinter < Visitor
+ def initialize(io, indent: "")
+ @io = io
+ @indent = indent
+ @level = 0
+ end
+
+ def visit(target)
+ @io.write("#{@indent * @level}#{target.class}")
+ target.instance_variables.each do |ivar|
+ value = target.instance_variable_get(ivar)
+ next if Elements::Element === value || Array === value
+ @io.write(" #{ivar}=#{value.inspect}")
+ end
+ @io.write("\n")
+ begin
+ @level += 1
+ super
+ ensure
+ @level -= 1
+ end
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/safe_marshal/visitors/to_ruby.rb b/lib/rubygems/safe_marshal/visitors/to_ruby.rb
new file mode 100644
index 0000000000..a9f1d048d4
--- /dev/null
+++ b/lib/rubygems/safe_marshal/visitors/to_ruby.rb
@@ -0,0 +1,415 @@
+# frozen_string_literal: true
+
+require_relative "visitor"
+
+module Gem::SafeMarshal
+ module Visitors
+ class ToRuby < Visitor
+ def initialize(permitted_classes:, permitted_symbols:, permitted_ivars:)
+ @permitted_classes = permitted_classes
+ @permitted_symbols = ["E"].concat(permitted_symbols).concat(permitted_classes)
+ @permitted_ivars = permitted_ivars
+
+ @objects = []
+ @symbols = []
+ @class_cache = {}
+
+ @stack = ["root"]
+ @stack_idx = 1
+ end
+
+ def inspect # :nodoc:
+ format("#<%s permitted_classes: %p permitted_symbols: %p permitted_ivars: %p>",
+ self.class, @permitted_classes, @permitted_symbols, @permitted_ivars)
+ end
+
+ def visit(target)
+ stack_idx = @stack_idx
+ super
+ ensure
+ @stack_idx = stack_idx - 1
+ end
+
+ private
+
+ def push_stack(element)
+ @stack[@stack_idx] = element
+ @stack_idx += 1
+ end
+
+ def visit_Gem_SafeMarshal_Elements_Array(a)
+ array = register_object([])
+
+ elements = a.elements
+ size = elements.size
+ idx = 0
+ # not idiomatic, but there's a huge number of IMEMOs allocated here, so we avoid the block
+ # because this is such a hot path when doing a bundle install with the full index
+ until idx == size
+ push_stack idx
+ array << visit(elements[idx])
+ idx += 1
+ end
+
+ array
+ end
+
+ def visit_Gem_SafeMarshal_Elements_Symbol(s)
+ name = s.name
+ raise UnpermittedSymbolError.new(symbol: name, stack: formatted_stack) unless @permitted_symbols.include?(name)
+ visit_symbol_type(s)
+ end
+
+ def map_ivars(klass, ivars)
+ stack_idx = @stack_idx
+ ivars.map.with_index do |(k, v), i|
+ @stack_idx = stack_idx
+
+ push_stack "ivar_"
+ push_stack i
+ k = resolve_ivar(klass, k)
+
+ @stack_idx = stack_idx
+ push_stack k
+
+ next k, visit(v)
+ end
+ end
+
+ def visit_Gem_SafeMarshal_Elements_WithIvars(e)
+ object_offset = @objects.size
+ push_stack "object"
+ object = visit(e.object)
+ ivars = map_ivars(object.class, e.ivars)
+
+ case e.object
+ when Elements::UserDefined
+ if object.class == ::Time
+ internal = []
+
+ ivars.reject! do |k, v|
+ case k
+ when :offset, :zone, :nano_num, :nano_den, :submicro
+ internal << [k, v]
+ true
+ else
+ false
+ end
+ end
+
+ s = e.object.binary_string
+
+ marshal_string = "\x04\bIu:\tTime".b
+ marshal_string.concat(s.size + 5)
+ marshal_string << s
+ marshal_string.concat(internal.size + 5)
+
+ internal.each do |k, v|
+ marshal_string.concat(":")
+ marshal_string.concat(k.size + 5)
+ marshal_string.concat(k.to_s)
+ dumped = Marshal.dump(v)
+ dumped[0, 2] = ""
+ marshal_string.concat(dumped)
+ end
+
+ object = @objects[object_offset] = Marshal.load(marshal_string)
+ end
+ when Elements::String
+ enc = nil
+
+ ivars.reject! do |k, v|
+ case k
+ when :E
+ case v
+ when TrueClass
+ enc = "UTF-8"
+ when FalseClass
+ enc = "US-ASCII"
+ else
+ raise FormatError, "Unexpected value for String :E #{v.inspect}"
+ end
+ when :encoding
+ enc = v
+ else
+ next false
+ end
+ true
+ end
+
+ object.force_encoding(enc) if enc
+ end
+
+ ivars.each do |k, v|
+ object.instance_variable_set k, v
+ end
+ object
+ end
+
+ def visit_Gem_SafeMarshal_Elements_Hash(o)
+ hash = register_object({})
+
+ o.pairs.each_with_index do |(k, v), i|
+ push_stack i
+ k = visit(k)
+ push_stack k
+ hash[k] = visit(v)
+ end
+
+ hash
+ end
+
+ def visit_Gem_SafeMarshal_Elements_HashWithDefaultValue(o)
+ hash = visit_Gem_SafeMarshal_Elements_Hash(o)
+ push_stack :default
+ hash.default = visit(o.default)
+ hash
+ end
+
+ def visit_Gem_SafeMarshal_Elements_Object(o)
+ register_object(resolve_class(o.name).allocate)
+ end
+
+ def visit_Gem_SafeMarshal_Elements_ObjectLink(o)
+ @objects[o.offset]
+ end
+
+ def visit_Gem_SafeMarshal_Elements_SymbolLink(o)
+ @symbols[o.offset]
+ end
+
+ def visit_Gem_SafeMarshal_Elements_UserDefined(o)
+ register_object(call_method(resolve_class(o.name), :_load, o.binary_string))
+ end
+
+ def visit_Gem_SafeMarshal_Elements_UserMarshal(o)
+ klass = resolve_class(o.name)
+ compat = COMPAT_CLASSES.fetch(klass, nil)
+ idx = @objects.size
+ object = register_object(call_method(compat || klass, :allocate))
+
+ push_stack :data
+ ret = call_method(object, :marshal_load, visit(o.data))
+
+ if compat
+ object = @objects[idx] = ret
+ end
+
+ object
+ end
+
+ def visit_Gem_SafeMarshal_Elements_Integer(i)
+ i.int
+ end
+
+ def visit_Gem_SafeMarshal_Elements_Nil(_)
+ nil
+ end
+
+ def visit_Gem_SafeMarshal_Elements_True(_)
+ true
+ end
+
+ def visit_Gem_SafeMarshal_Elements_False(_)
+ false
+ end
+
+ def visit_Gem_SafeMarshal_Elements_String(s)
+ register_object(+s.str)
+ end
+
+ def visit_Gem_SafeMarshal_Elements_Float(f)
+ case f.string
+ when "inf"
+ ::Float::INFINITY
+ when "-inf"
+ -::Float::INFINITY
+ when "nan"
+ ::Float::NAN
+ else
+ f.string.to_f
+ end
+ end
+
+ def visit_Gem_SafeMarshal_Elements_Bignum(b)
+ result = 0
+ b.data.each_byte.with_index do |byte, exp|
+ result += (byte * 2**(exp * 8))
+ end
+
+ case b.sign
+ when 43 # ?+
+ result
+ when 45 # ?-
+ -result
+ else
+ raise FormatError, "Unexpected sign for Bignum #{b.sign.chr.inspect} (#{b.sign})"
+ end
+ end
+
+ def visit_Gem_SafeMarshal_Elements_UserClass(r)
+ if resolve_class(r.name) == ::Hash && r.wrapped_object.is_a?(Elements::Hash)
+
+ hash = register_object({}.compare_by_identity)
+
+ o = r.wrapped_object
+ o.pairs.each_with_index do |(k, v), i|
+ push_stack i
+ k = visit(k)
+ push_stack k
+ hash[k] = visit(v)
+ end
+
+ if o.is_a?(Elements::HashWithDefaultValue)
+ push_stack :default
+ hash.default = visit(o.default)
+ end
+
+ hash
+ else
+ raise UnsupportedError.new("Unsupported user class #{resolve_class(r.name)} in marshal stream", stack: formatted_stack)
+ end
+ end
+
+ def resolve_class(n)
+ @class_cache[n] ||= begin
+ to_s = resolve_symbol_name(n)
+ raise UnpermittedClassError.new(name: to_s, stack: formatted_stack) unless @permitted_classes.include?(to_s)
+ visit_symbol_type(n)
+ begin
+ ::Object.const_get(to_s)
+ rescue NameError
+ raise ArgumentError, "Undefined class #{to_s.inspect}"
+ end
+ end
+ end
+
+ class RationalCompat
+ def marshal_load(s)
+ num, den = s
+ raise ArgumentError, "Expected 2 ints" unless s.size == 2 && num.is_a?(Integer) && den.is_a?(Integer)
+ Rational(num, den)
+ end
+ end
+ private_constant :RationalCompat
+
+ COMPAT_CLASSES = {}.tap do |h|
+ h[Rational] = RationalCompat
+ end.compare_by_identity.freeze
+ private_constant :COMPAT_CLASSES
+
+ def resolve_ivar(klass, name)
+ to_s = resolve_symbol_name(name)
+
+ raise UnpermittedIvarError.new(symbol: to_s, klass: klass, stack: formatted_stack) unless @permitted_ivars.fetch(klass.name, [].freeze).include?(to_s)
+
+ visit_symbol_type(name)
+ end
+
+ def visit_symbol_type(element)
+ case element
+ when Elements::Symbol
+ sym = element.name.to_sym
+ @symbols << sym
+ sym
+ when Elements::SymbolLink
+ visit_Gem_SafeMarshal_Elements_SymbolLink(element)
+ end
+ end
+
+ # This is a hot method, so avoid respond_to? checks on every invocation
+ if :read.respond_to?(:name)
+ def resolve_symbol_name(element)
+ case element
+ when Elements::Symbol
+ element.name
+ when Elements::SymbolLink
+ visit_Gem_SafeMarshal_Elements_SymbolLink(element).name
+ else
+ raise FormatError, "Expected symbol or symbol link, got #{element.inspect} @ #{formatted_stack.join(".")}"
+ end
+ end
+ else
+ def resolve_symbol_name(element)
+ case element
+ when Elements::Symbol
+ element.name
+ when Elements::SymbolLink
+ visit_Gem_SafeMarshal_Elements_SymbolLink(element).to_s
+ else
+ raise FormatError, "Expected symbol or symbol link, got #{element.inspect} @ #{formatted_stack.join(".")}"
+ end
+ end
+ end
+
+ def register_object(o)
+ @objects << o
+ o
+ end
+
+ def call_method(receiver, method, *args)
+ receiver.__send__(method, *args)
+ rescue NoMethodError => e
+ raise unless e.receiver == receiver
+
+ raise MethodCallError, "Unable to call #{method.inspect} on #{receiver.inspect}, perhaps it is a class using marshal compat, which is not visible in ruby? #{e}"
+ end
+
+ def formatted_stack
+ formatted = []
+ @stack[0, @stack_idx].each do |e|
+ if e.is_a?(Integer)
+ if formatted.last == "ivar_"
+ formatted[-1] = "ivar_#{e}"
+ else
+ formatted << "[#{e}]"
+ end
+ else
+ formatted << e
+ end
+ end
+ formatted
+ end
+
+ class Error < StandardError
+ end
+
+ class UnpermittedSymbolError < Error
+ def initialize(symbol:, stack:)
+ @symbol = symbol
+ @stack = stack
+ super "Attempting to load unpermitted symbol #{symbol.inspect} @ #{stack.join "."}"
+ end
+ end
+
+ class UnpermittedIvarError < Error
+ def initialize(symbol:, klass:, stack:)
+ @symbol = symbol
+ @klass = klass
+ @stack = stack
+ super "Attempting to set unpermitted ivar #{symbol.inspect} on object of class #{klass} @ #{stack.join "."}"
+ end
+ end
+
+ class UnpermittedClassError < Error
+ def initialize(name:, stack:)
+ @name = name
+ @stack = stack
+ super "Attempting to load unpermitted class #{name.inspect} @ #{stack.join "."}"
+ end
+ end
+
+ class UnsupportedError < Error
+ def initialize(message, stack:)
+ super "#{message} @ #{stack.join "."}"
+ end
+ end
+
+ class FormatError < Error
+ end
+
+ class MethodCallError < Error
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/safe_marshal/visitors/visitor.rb b/lib/rubygems/safe_marshal/visitors/visitor.rb
new file mode 100644
index 0000000000..c9a079dc0e
--- /dev/null
+++ b/lib/rubygems/safe_marshal/visitors/visitor.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+module Gem::SafeMarshal::Visitors
+ class Visitor
+ def visit(target)
+ send DISPATCH.fetch(target.class), target
+ end
+
+ private
+
+ DISPATCH = Gem::SafeMarshal::Elements.constants.each_with_object({}) do |c, h|
+ next if c == :Element
+
+ klass = Gem::SafeMarshal::Elements.const_get(c)
+ h[klass] = :"visit_#{klass.name.gsub("::", "_")}"
+ h.default = :visit_unknown_element
+ end.compare_by_identity.freeze
+ private_constant :DISPATCH
+
+ def visit_unknown_element(e)
+ raise ArgumentError, "Attempting to visit unknown element #{e.inspect}"
+ end
+
+ def visit_Gem_SafeMarshal_Elements_Array(target)
+ target.elements.each {|e| visit(e) }
+ end
+
+ def visit_Gem_SafeMarshal_Elements_Bignum(target); end
+ def visit_Gem_SafeMarshal_Elements_False(target); end
+ def visit_Gem_SafeMarshal_Elements_Float(target); end
+
+ def visit_Gem_SafeMarshal_Elements_Hash(target)
+ target.pairs.each do |k, v|
+ visit(k)
+ visit(v)
+ end
+ end
+
+ def visit_Gem_SafeMarshal_Elements_HashWithDefaultValue(target)
+ visit_Gem_SafeMarshal_Elements_Hash(target)
+ visit(target.default)
+ end
+
+ def visit_Gem_SafeMarshal_Elements_Integer(target); end
+ def visit_Gem_SafeMarshal_Elements_Nil(target); end
+
+ def visit_Gem_SafeMarshal_Elements_Object(target)
+ visit(target.name)
+ end
+
+ def visit_Gem_SafeMarshal_Elements_ObjectLink(target); end
+ def visit_Gem_SafeMarshal_Elements_String(target); end
+ def visit_Gem_SafeMarshal_Elements_Symbol(target); end
+ def visit_Gem_SafeMarshal_Elements_SymbolLink(target); end
+ def visit_Gem_SafeMarshal_Elements_True(target); end
+
+ def visit_Gem_SafeMarshal_Elements_UserDefined(target)
+ visit(target.name)
+ end
+
+ def visit_Gem_SafeMarshal_Elements_UserMarshal(target)
+ visit(target.name)
+ visit(target.data)
+ end
+
+ def visit_Gem_SafeMarshal_Elements_WithIvars(target)
+ visit(target.object)
+ target.ivars.each do |k, v|
+ visit(k)
+ visit(v)
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/safe_yaml.rb b/lib/rubygems/safe_yaml.rb
index dba3cfb16d..6a02a48230 100644
--- a/lib/rubygems/safe_yaml.rb
+++ b/lib/rubygems/safe_yaml.rb
@@ -25,8 +25,17 @@ module Gem
runtime
].freeze
+ @aliases_enabled = true
+ def self.aliases_enabled=(value) # :nodoc:
+ @aliases_enabled = !!value
+ end
+
+ def self.aliases_enabled? # :nodoc:
+ @aliases_enabled
+ end
+
def self.safe_load(input)
- ::Psych.safe_load(input, permitted_classes: PERMITTED_CLASSES, permitted_symbols: PERMITTED_SYMBOLS, aliases: true)
+ ::Psych.safe_load(input, permitted_classes: PERMITTED_CLASSES, permitted_symbols: PERMITTED_SYMBOLS, aliases: @aliases_enabled)
end
def self.load(input)
diff --git a/lib/rubygems/security.rb b/lib/rubygems/security.rb
index 1e8e57b5a9..69ba87b07f 100644
--- a/lib/rubygems/security.rb
+++ b/lib/rubygems/security.rb
@@ -323,7 +323,7 @@ require_relative "openssl"
# == Original author
#
# Paul Duncan <pabs@pablotron.org>
-# http://pablotron.org/
+# https://pablotron.org/
module Gem::Security
##
diff --git a/lib/rubygems/security/policies.rb b/lib/rubygems/security/policies.rb
index 4180832d85..41f66043ad 100644
--- a/lib/rubygems/security/policies.rb
+++ b/lib/rubygems/security/policies.rb
@@ -6,12 +6,12 @@ module Gem::Security
NoSecurity = Policy.new(
"No Security",
- :verify_data => false,
- :verify_signer => false,
- :verify_chain => false,
- :verify_root => false,
- :only_trusted => false,
- :only_signed => false
+ verify_data: false,
+ verify_signer: false,
+ verify_chain: false,
+ verify_root: false,
+ only_trusted: false,
+ only_signed: false
)
##
@@ -24,12 +24,12 @@ module Gem::Security
AlmostNoSecurity = Policy.new(
"Almost No Security",
- :verify_data => true,
- :verify_signer => false,
- :verify_chain => false,
- :verify_root => false,
- :only_trusted => false,
- :only_signed => false
+ verify_data: true,
+ verify_signer: false,
+ verify_chain: false,
+ verify_root: false,
+ only_trusted: false,
+ only_signed: false
)
##
@@ -41,12 +41,12 @@ module Gem::Security
LowSecurity = Policy.new(
"Low Security",
- :verify_data => true,
- :verify_signer => true,
- :verify_chain => false,
- :verify_root => false,
- :only_trusted => false,
- :only_signed => false
+ verify_data: true,
+ verify_signer: true,
+ verify_chain: false,
+ verify_root: false,
+ only_trusted: false,
+ only_signed: false
)
##
@@ -60,12 +60,12 @@ module Gem::Security
MediumSecurity = Policy.new(
"Medium Security",
- :verify_data => true,
- :verify_signer => true,
- :verify_chain => true,
- :verify_root => true,
- :only_trusted => true,
- :only_signed => false
+ verify_data: true,
+ verify_signer: true,
+ verify_chain: true,
+ verify_root: true,
+ only_trusted: true,
+ only_signed: false
)
##
@@ -79,12 +79,12 @@ module Gem::Security
HighSecurity = Policy.new(
"High Security",
- :verify_data => true,
- :verify_signer => true,
- :verify_chain => true,
- :verify_root => true,
- :only_trusted => true,
- :only_signed => true
+ verify_data: true,
+ verify_signer: true,
+ verify_chain: true,
+ verify_root: true,
+ only_trusted: true,
+ only_signed: true
)
##
@@ -92,12 +92,12 @@ module Gem::Security
SigningPolicy = Policy.new(
"Signing Policy",
- :verify_data => false,
- :verify_signer => true,
- :verify_chain => true,
- :verify_root => true,
- :only_trusted => false,
- :only_signed => false
+ verify_data: false,
+ verify_signer: true,
+ verify_chain: true,
+ verify_root: true,
+ only_trusted: false,
+ only_signed: false
)
##
diff --git a/lib/rubygems/security/trust_dir.rb b/lib/rubygems/security/trust_dir.rb
index ea011e6d65..d23d161cfe 100644
--- a/lib/rubygems/security/trust_dir.rb
+++ b/lib/rubygems/security/trust_dir.rb
@@ -9,8 +9,8 @@ class Gem::Security::TrustDir
# Default permissions for the trust directory and its contents
DEFAULT_PERMISSIONS = {
- :trust_dir => 0o700,
- :trusted_cert => 0o600,
+ trust_dir: 0o700,
+ trusted_cert: 0o600,
}.freeze
##
@@ -111,7 +111,7 @@ class Gem::Security::TrustDir
FileUtils.chmod 0o700, @dir
else
- FileUtils.mkdir_p @dir, :mode => @permissions[:trust_dir]
+ FileUtils.mkdir_p @dir, mode: @permissions[:trust_dir]
end
end
end
diff --git a/lib/rubygems/shellwords.rb b/lib/rubygems/shellwords.rb
new file mode 100644
index 0000000000..741dccb363
--- /dev/null
+++ b/lib/rubygems/shellwords.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+autoload :Shellwords, "shellwords"
diff --git a/lib/rubygems/source.rb b/lib/rubygems/source.rb
index 8b3a8828d1..d90e311b65 100644
--- a/lib/rubygems/source.rb
+++ b/lib/rubygems/source.rb
@@ -12,9 +12,9 @@ class Gem::Source
include Gem::Text
FILES = { # :nodoc:
- :released => "specs",
- :latest => "latest_specs",
- :prerelease => "prerelease_specs",
+ released: "specs",
+ latest: "latest_specs",
+ prerelease: "prerelease_specs",
}.freeze
##
@@ -101,7 +101,6 @@ class Gem::Source
def cache_dir(uri)
# Correct for windows paths
escaped_path = uri.path.sub(%r{^/([a-z]):/}i, '/\\1-/')
- escaped_path.tap(&Gem::UNTAINT)
File.join Gem.spec_cache_dir, "#{uri.host}%#{uri.port}", File.dirname(escaped_path)
end
@@ -135,8 +134,9 @@ class Gem::Source
if File.exist? local_spec
spec = Gem.read_binary local_spec
+ Gem.load_safe_marshal
spec = begin
- Marshal.load(spec)
+ Gem::SafeMarshal.safe_load(spec)
rescue StandardError
nil
end
@@ -157,8 +157,9 @@ class Gem::Source
end
end
+ Gem.load_safe_marshal
# TODO: Investigate setting Gem::Specification#loaded_from to a URI
- Marshal.load spec
+ Gem::SafeMarshal.safe_load spec
end
##
@@ -188,8 +189,9 @@ class Gem::Source
spec_dump = fetcher.cache_update_path spec_path, local_file, update_cache?
+ Gem.load_safe_marshal
begin
- Gem::NameTuple.from_list Marshal.load(spec_dump)
+ Gem::NameTuple.from_list Gem::SafeMarshal.safe_load(spec_dump)
rescue ArgumentError
if update_cache? && !retried
FileUtils.rm local_file
diff --git a/lib/rubygems/source/git.rb b/lib/rubygems/source/git.rb
index 70e8d1b06e..bda63c6844 100644
--- a/lib/rubygems/source/git.rb
+++ b/lib/rubygems/source/git.rb
@@ -221,14 +221,14 @@ class Gem::Source::Git < Gem::Source
end
##
- # A hash for the git gem based on the git repository URI.
+ # A hash for the git gem based on the git repository Gem::URI.
def uri_hash # :nodoc:
require_relative "../openssl"
normalized =
- if @repository =~ %r{^\w+://(\w+@)?}
- uri = URI(@repository).normalize.to_s.sub %r{/$},""
+ if @repository.match?(%r{^\w+://(\w+@)?})
+ uri = Gem::URI(@repository).normalize.to_s.sub %r{/$},""
uri.sub(/\A(\w+)/) { $1.downcase }
else
@repository
diff --git a/lib/rubygems/source/local.rb b/lib/rubygems/source/local.rb
index 533b0a4d06..d81d8343a8 100644
--- a/lib/rubygems/source/local.rb
+++ b/lib/rubygems/source/local.rb
@@ -40,10 +40,11 @@ class Gem::Source::Local < Gem::Source
Dir["*.gem"].each do |file|
pkg = Gem::Package.new(file)
+ spec = pkg.spec
rescue SystemCallError, Gem::Package::FormatError
# ignore
else
- tup = pkg.spec.name_tuple
+ tup = spec.name_tuple
@specs[tup] = [File.expand_path(file), pkg]
case type
diff --git a/lib/rubygems/source_list.rb b/lib/rubygems/source_list.rb
index 9e8a9e16ef..33db64fbc1 100644
--- a/lib/rubygems/source_list.rb
+++ b/lib/rubygems/source_list.rb
@@ -44,7 +44,7 @@ class Gem::SourceList
end
##
- # Appends +obj+ to the source list which may be a Gem::Source, URI or URI
+ # Appends +obj+ to the source list which may be a Gem::Source, Gem::URI or URI
# String.
def <<(obj)
diff --git a/lib/rubygems/spec_fetcher.rb b/lib/rubygems/spec_fetcher.rb
index 34e6d7638c..610edf25c9 100644
--- a/lib/rubygems/spec_fetcher.rb
+++ b/lib/rubygems/spec_fetcher.rb
@@ -69,9 +69,9 @@ class Gem::SpecFetcher
@prerelease_specs = {}
@caches = {
- :latest => @latest_specs,
- :prerelease => @prerelease_specs,
- :released => @specs,
+ latest: @latest_specs,
+ prerelease: @prerelease_specs,
+ released: @specs,
}
@fetcher = Gem::RemoteFetcher.fetcher
diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb
index abdc1dbf4b..29139cf725 100644
--- a/lib/rubygems/specification.rb
+++ b/lib/rubygems/specification.rb
@@ -108,7 +108,7 @@ class Gem::Specification < Gem::BasicSpecification
@load_cache = {} # :nodoc:
@load_cache_mutex = Thread::Mutex.new
- VALID_NAME_PATTERN = /\A[a-zA-Z0-9\.\-\_]+\z/.freeze # :nodoc:
+ VALID_NAME_PATTERN = /\A[a-zA-Z0-9\.\-\_]+\z/ # :nodoc:
# :startdoc:
@@ -127,35 +127,35 @@ class Gem::Specification < Gem::BasicSpecification
# Map of attribute names to default values.
@@default_value = {
- :authors => [],
- :autorequire => nil,
- :bindir => "bin",
- :cert_chain => [],
- :date => nil,
- :dependencies => [],
- :description => nil,
- :email => nil,
- :executables => [],
- :extensions => [],
- :extra_rdoc_files => [],
- :files => [],
- :homepage => nil,
- :licenses => [],
- :metadata => {},
- :name => nil,
- :platform => Gem::Platform::RUBY,
- :post_install_message => nil,
- :rdoc_options => [],
- :require_paths => ["lib"],
- :required_ruby_version => Gem::Requirement.default,
- :required_rubygems_version => Gem::Requirement.default,
- :requirements => [],
- :rubygems_version => Gem::VERSION,
- :signing_key => nil,
- :specification_version => CURRENT_SPECIFICATION_VERSION,
- :summary => nil,
- :test_files => [],
- :version => nil,
+ authors: [],
+ autorequire: nil,
+ bindir: "bin",
+ cert_chain: [],
+ date: nil,
+ dependencies: [],
+ description: nil,
+ email: nil,
+ executables: [],
+ extensions: [],
+ extra_rdoc_files: [],
+ files: [],
+ homepage: nil,
+ licenses: [],
+ metadata: {},
+ name: nil,
+ platform: Gem::Platform::RUBY,
+ post_install_message: nil,
+ rdoc_options: [],
+ require_paths: ["lib"],
+ required_ruby_version: Gem::Requirement.default,
+ required_rubygems_version: Gem::Requirement.default,
+ requirements: [],
+ rubygems_version: Gem::VERSION,
+ signing_key: nil,
+ specification_version: CURRENT_SPECIFICATION_VERSION,
+ summary: nil,
+ test_files: [],
+ version: nil,
}.freeze
# rubocop:disable Style/MutableConstant
@@ -301,7 +301,7 @@ class Gem::Specification < Gem::BasicSpecification
#
# Usage:
#
- # spec.description = <<-EOF
+ # spec.description = <<~EOF
# Rake is a Make-like program implemented in Ruby. Tasks and
# dependencies are specified in standard Ruby syntax.
# EOF
@@ -341,7 +341,7 @@ class Gem::Specification < Gem::BasicSpecification
# https://opensource.org/licenses/ approved.
#
# The most commonly used OSI-approved licenses are MIT and Apache-2.0.
- # GitHub also provides a license picker at http://choosealicense.com/.
+ # GitHub also provides a license picker at https://choosealicense.com/.
#
# You can also use a custom license file along with your gemspec and specify
# a LicenseRef-<idstring>, where idstring is the name of the file containing
@@ -426,11 +426,11 @@ class Gem::Specification < Gem::BasicSpecification
end
##
- # The path in the gem for executable scripts. Usually 'bin'
+ # The path in the gem for executable scripts. Usually 'exe'
#
# Usage:
#
- # spec.bindir = 'bin'
+ # spec.bindir = 'exe'
attr_accessor :bindir
@@ -531,13 +531,6 @@ class Gem::Specification < Gem::BasicSpecification
attr_reader :required_rubygems_version
##
- # The version of RubyGems used to create this gem.
- #
- # Do not set this, it is set automatically when the gem is packaged.
-
- attr_accessor :rubygems_version
-
- ##
# The key used to sign this gem. See Gem::Security for details.
attr_accessor :signing_key
@@ -725,6 +718,21 @@ class Gem::Specification < Gem::BasicSpecification
end
######################################################################
+ # :section: Read-only attributes
+
+ ##
+ # The version of RubyGems used to create this gem.
+
+ attr_accessor :rubygems_version
+
+ ##
+ # The path where this gem installs its extensions.
+
+ def extensions_dir
+ @extensions_dir ||= super
+ end
+
+ ######################################################################
# :section: Specification internals
##
@@ -775,7 +783,7 @@ class Gem::Specification < Gem::BasicSpecification
def self.each_gemspec(dirs) # :nodoc:
dirs.each do |dir|
Gem::Util.glob_files_in_dir("*.gemspec", dir).each do |path|
- yield path.tap(&Gem::UNTAINT)
+ yield path
end
end
end
@@ -961,7 +969,7 @@ class Gem::Specification < Gem::BasicSpecification
def self.dirs
@@dirs ||= Gem.path.collect do |dir|
- File.join dir.dup.tap(&Gem::UNTAINT), "specifications"
+ File.join dir, "specifications"
end
end
@@ -995,8 +1003,6 @@ class Gem::Specification < Gem::BasicSpecification
def self.find_all_by_name(name, *requirements)
requirements = Gem::Requirement.default if requirements.empty?
- # TODO: maybe try: find_all { |s| spec === dep }
-
Gem::Dependency.new(name, *requirements).matching_specs
end
@@ -1014,8 +1020,6 @@ class Gem::Specification < Gem::BasicSpecification
def self.find_by_name(name, *requirements)
requirements = Gem::Requirement.default if requirements.empty?
- # TODO: maybe try: find { |s| spec === dep }
-
Gem::Dependency.new(name, *requirements).to_spec
end
@@ -1154,13 +1158,10 @@ class Gem::Specification < Gem::BasicSpecification
spec = @load_cache_mutex.synchronize { @load_cache[file] }
return spec if spec
- file = file.dup.tap(&Gem::UNTAINT)
return unless File.file?(file)
code = Gem.open_file(file, "r:UTF-8:-", &:read)
- code.tap(&Gem::UNTAINT)
-
begin
spec = eval code, binding, file
@@ -1300,10 +1301,23 @@ class Gem::Specification < Gem::BasicSpecification
def self._load(str)
Gem.load_yaml
+ Gem.load_safe_marshal
+
+ yaml_set = false
+ retry_count = 0
array = begin
- Marshal.load str
+ Gem::SafeMarshal.safe_load str
rescue ArgumentError => e
+ # Avoid an infinite retry loop when the argument error has nothing to do
+ # with the classes not being defined.
+ # 1 retry each allowed in case all 3 of
+ # - YAML
+ # - YAML::Syck::DefaultKey
+ # - YAML::PrivateType
+ # need to be defined
+ raise if retry_count >= 3
+
#
# Some very old marshaled specs included references to `YAML::PrivateType`
# and `YAML::Syck::DefaultKey` constants due to bugs in the old emitter
@@ -1313,17 +1327,23 @@ class Gem::Specification < Gem::BasicSpecification
message = e.message
raise unless message.include?("YAML::")
- Object.const_set "YAML", Psych unless Object.const_defined?(:YAML)
+ unless Object.const_defined?(:YAML)
+ Object.const_set "YAML", Psych
+ yaml_set = true
+ end
if message.include?("YAML::Syck::")
YAML.const_set "Syck", YAML unless YAML.const_defined?(:Syck)
- YAML::Syck.const_set "DefaultKey", Class.new if message.include?("YAML::Syck::DefaultKey")
- elsif message.include?("YAML::PrivateType")
+ YAML::Syck.const_set "DefaultKey", Class.new if message.include?("YAML::Syck::DefaultKey") && !YAML::Syck.const_defined?(:DefaultKey)
+ elsif message.include?("YAML::PrivateType") && !YAML.const_defined?(:PrivateType)
YAML.const_set "PrivateType", Class.new
end
+ retry_count += 1
retry
+ ensure
+ Object.__send__(:remove_const, "YAML") if yaml_set
end
spec = Gem::Specification.new
@@ -1731,7 +1751,7 @@ class Gem::Specification < Gem::BasicSpecification
/\A
(\d{4})-(\d{2})-(\d{2})
(\s+ \d{2}:\d{2}:\d{2}\.\d+ \s* (Z | [-+]\d\d:\d\d) )?
- \Z/x.freeze
+ \Z/x
##
# The date this gem was created
@@ -1870,7 +1890,8 @@ class Gem::Specification < Gem::BasicSpecification
attributes = @@attributes.map(&:to_s) - %w[name version platform]
attributes.each do |name|
- coder.add name, instance_variable_get("@#{name}")
+ value = instance_variable_get("@#{name}")
+ coder.add name, value unless value.nil?
end
end
@@ -2055,7 +2076,8 @@ class Gem::Specification < Gem::BasicSpecification
end
##
- # Duplicates array_attributes from +other_spec+ so state isn't shared.
+ # Duplicates Array and Gem::Requirement attributes from +other_spec+ so state isn't shared.
+ #
def initialize_copy(other_spec)
self.class.array_attributes.each do |name|
@@ -2077,6 +2099,9 @@ class Gem::Specification < Gem::BasicSpecification
raise e
end
end
+
+ @required_ruby_version = other_spec.required_ruby_version.dup
+ @required_rubygems_version = other_spec.required_rubygems_version.dup
end
def base_dir
@@ -2344,12 +2369,12 @@ class Gem::Specification < Gem::BasicSpecification
when Hash then
seg = obj.keys.sort.map {|k| "#{k.to_s.dump} => #{obj[k].to_s.dump}" }
"{ #{seg.join(", ")} }"
- when Gem::Version then obj.to_s.dump
+ when Gem::Version then ruby_code(obj.to_s)
when DateLike then obj.strftime("%Y-%m-%d").dump
when Time then obj.strftime("%Y-%m-%d").dump
when Numeric then obj.inspect
when true, false, nil then obj.inspect
- when Gem::Platform then "Gem::Platform.new(#{obj.to_a.inspect})"
+ when Gem::Platform then "Gem::Platform.new(#{ruby_code obj.to_a})"
when Gem::Requirement then
list = obj.as_list
"Gem::Requirement.new(#{ruby_code(list.size == 1 ? obj.to_s : list)})"
@@ -2514,12 +2539,12 @@ class Gem::Specification < Gem::BasicSpecification
end
if String === signing_key
- result << " s.signing_key = #{signing_key.dump}.freeze"
+ result << " s.signing_key = #{ruby_code signing_key}"
end
if @installed_by_version
result << nil
- result << " s.installed_by_version = \"#{Gem::VERSION}\" if s.respond_to? :installed_by_version"
+ result << " s.installed_by_version = #{ruby_code Gem::VERSION} if s.respond_to? :installed_by_version"
end
unless dependencies.empty?
@@ -2528,9 +2553,8 @@ class Gem::Specification < Gem::BasicSpecification
result << nil
dependencies.each do |dep|
- req = dep.requirements_list.inspect
dep.instance_variable_set :@type, :runtime if dep.type.nil? # HACK
- result << " s.add_#{dep.type}_dependency(%q<#{dep.name}>.freeze, #{req})"
+ result << " s.add_#{dep.type}_dependency(%q<#{dep.name}>.freeze, #{ruby_code dep.requirements_list})"
end
end
@@ -2651,19 +2675,12 @@ class Gem::Specification < Gem::BasicSpecification
rubygems_deprecate :validate_permissions
##
- # Set the version to +version+, potentially also setting
- # required_rubygems_version if +version+ indicates it is a
- # prerelease.
+ # Set the version to +version+.
def version=(version)
@version = Gem::Version.create(version)
return if @version.nil?
- # skip to set required_ruby_version when pre-released rubygems.
- # It caused to raise CircularDependencyError
- if @version.prerelease? && (@name.nil? || @name.strip != "rubygems")
- self.required_rubygems_version = "> 1.3.1"
- end
invalidate_memoized_attributes
end
@@ -2676,9 +2693,9 @@ class Gem::Specification < Gem::BasicSpecification
case ivar
when "date"
# Force Date to go through the extra coerce logic in date=
- self.date = val.tap(&Gem::UNTAINT)
+ self.date = val
else
- instance_variable_set "@#{ivar}", val.tap(&Gem::UNTAINT)
+ instance_variable_set "@#{ivar}", val
end
end
diff --git a/lib/rubygems/specification_policy.rb b/lib/rubygems/specification_policy.rb
index da3beee628..516c26f53c 100644
--- a/lib/rubygems/specification_policy.rb
+++ b/lib/rubygems/specification_policy.rb
@@ -5,20 +5,21 @@ require_relative "user_interaction"
class Gem::SpecificationPolicy
include Gem::UserInteraction
- VALID_NAME_PATTERN = /\A[a-zA-Z0-9\.\-\_]+\z/.freeze # :nodoc:
+ VALID_NAME_PATTERN = /\A[a-zA-Z0-9\.\-\_]+\z/ # :nodoc:
- SPECIAL_CHARACTERS = /\A[#{Regexp.escape('.-_')}]+/.freeze # :nodoc:
+ SPECIAL_CHARACTERS = /\A[#{Regexp.escape(".-_")}]+/ # :nodoc:
- VALID_URI_PATTERN = %r{\Ahttps?:\/\/([^\s:@]+:[^\s:@]*@)?[A-Za-z\d\-]+(\.[A-Za-z\d\-]+)+\.?(:\d{1,5})?([\/?]\S*)?\z}.freeze # :nodoc:
+ VALID_URI_PATTERN = %r{\Ahttps?:\/\/([^\s:@]+:[^\s:@]*@)?[A-Za-z\d\-]+(\.[A-Za-z\d\-]+)+\.?(:\d{1,5})?([\/?]\S*)?\z} # :nodoc:
METADATA_LINK_KEYS = %w[
- bug_tracker_uri
- changelog_uri
- documentation_uri
homepage_uri
- mailing_list_uri
+ changelog_uri
source_code_uri
+ documentation_uri
wiki_uri
+ mailing_list_uri
+ bug_tracker_uri
+ download_uri
funding_uri
].freeze # :nodoc:
@@ -102,10 +103,14 @@ class Gem::SpecificationPolicy
validate_dependencies
+ validate_required_ruby_version
+
validate_extensions
validate_removed_attributes
+ validate_unique_links
+
if @warnings > 0
if strict
error "specification has warnings"
@@ -224,6 +229,12 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
end
end
+ def validate_required_ruby_version
+ if @specification.required_ruby_version.requirements == [Gem::Requirement::DefaultRequirement]
+ warning "make sure you specify the oldest ruby version constraint (like \">= 3.0\") that you want your gem to support by setting the `required_ruby_version` gemspec attribute"
+ end
+ end
+
##
# Issues a warning for each file to be packaged which is world-readable.
#
@@ -343,7 +354,7 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
String
end
- unless Array === val && val.all? {|x| x.is_a?(klass) }
+ unless Array === val && val.all? {|x| x.is_a?(klass) || (field == :licenses && x.nil?) }
error "#{field} must be an Array of #{klass}"
end
end
@@ -358,6 +369,8 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
licenses = @specification.licenses
licenses.each do |license|
+ next if license.nil?
+
if license.length > 64
error "each license must be 64 characters or less"
end
@@ -368,25 +381,38 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
licenses = @specification.licenses
licenses.each do |license|
- next if Gem::Licenses.match?(license)
+ next if Gem::Licenses.match?(license) || license.nil?
+ license_id_deprecated = Gem::Licenses.deprecated_license_id?(license)
+ exception_id_deprecated = Gem::Licenses.deprecated_exception_id?(license)
suggestions = Gem::Licenses.suggestions(license)
+
+ if license_id_deprecated
+ main_message = "License identifier '#{license}' is deprecated"
+ elsif exception_id_deprecated
+ main_message = "Exception identifier at '#{license}' is deprecated"
+ else
+ main_message = "License identifier '#{license}' is invalid"
+ end
+
message = <<-WARNING
-license value '#{license}' is invalid. Use a license identifier from
-http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard license.
+#{main_message}. Use an identifier from
+https://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard license,
+or set it to nil if you don't want to specify a license.
WARNING
message += "Did you mean #{suggestions.map {|s| "'#{s}'" }.join(", ")}?\n" unless suggestions.nil?
warning(message)
end
warning <<-WARNING if licenses.empty?
-licenses is empty, but is recommended. Use a license identifier from
-http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard license.
+licenses is empty, but is recommended. Use an license identifier from
+https://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard license,
+or set it to nil if you don't want to specify a license.
WARNING
end
LAZY = '"FIxxxXME" or "TOxxxDO"'.gsub(/xxx/, "")
- LAZY_PATTERN = /\AFI XME|\ATO DO/x.freeze
- HOMEPAGE_URI_PATTERN = /\A[a-z][a-z\d+.-]*:/i.freeze
+ LAZY_PATTERN = /\AFI XME|\ATO DO/x
+ HOMEPAGE_URI_PATTERN = /\A[a-z][a-z\d+.-]*:/i
def validate_lazy_metadata
unless @specification.authors.grep(LAZY_PATTERN).empty?
@@ -409,13 +435,13 @@ http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard li
# Make sure a homepage is valid HTTP/HTTPS URI
if homepage && !homepage.empty?
- require "uri"
+ require_relative "vendor/uri/lib/uri"
begin
- homepage_uri = URI.parse(homepage)
- unless [URI::HTTP, URI::HTTPS].member? homepage_uri.class
+ homepage_uri = Gem::URI.parse(homepage)
+ unless [Gem::URI::HTTP, Gem::URI::HTTPS].member? homepage_uri.class
error "\"#{homepage}\" is not a valid HTTP URI"
end
- rescue URI::InvalidURIError
+ rescue Gem::URI::InvalidURIError
error "\"#{homepage}\" is not a valid HTTP URI"
end
end
@@ -479,13 +505,29 @@ You have specified rust based extension, but Cargo.lock is not part of the gem f
def validate_rake_extensions(builder) # :nodoc:
rake_extension = @specification.extensions.any? {|s| builder.builder_for(s) == Gem::Ext::RakeBuilder }
- rake_dependency = @specification.dependencies.any? {|d| d.name == "rake" }
+ rake_dependency = @specification.dependencies.any? {|d| d.name == "rake" && d.type == :runtime }
warning <<-WARNING if rake_extension && !rake_dependency
-You have specified rake based extension, but rake is not added as dependency. It is recommended to add rake as a dependency in gemspec since there's no guarantee rake will be already installed.
+You have specified rake based extension, but rake is not added as runtime dependency. It is recommended to add rake as a runtime dependency in gemspec since there's no guarantee rake will be already installed.
WARNING
end
+ def validate_unique_links
+ links = @specification.metadata.slice(*METADATA_LINK_KEYS)
+ grouped = links.group_by {|_key, uri| uri }
+ grouped.each do |uri, copies|
+ next unless copies.length > 1
+ keys = copies.map(&:first).join("\n ")
+ warning <<~WARNING
+ You have specified the uri:
+ #{uri}
+ for all of the following keys:
+ #{keys}
+ Only the first one will be shown on rubygems.org
+ WARNING
+ end
+ end
+
def warning(statement) # :nodoc:
@warnings += 1
diff --git a/lib/rubygems/stub_specification.rb b/lib/rubygems/stub_specification.rb
index a3793eaa20..58748df5d6 100644
--- a/lib/rubygems/stub_specification.rb
+++ b/lib/rubygems/stub_specification.rb
@@ -35,7 +35,7 @@ class Gem::StubSpecification < Gem::BasicSpecification
def initialize(data, extensions)
parts = data[PREFIX.length..-1].split(" ", 4)
- @name = parts[0].freeze
+ @name = -parts[0]
@version = if Gem::Version.correct?(parts[1])
Gem::Version.new(parts[1])
else
@@ -69,7 +69,6 @@ class Gem::StubSpecification < Gem::BasicSpecification
def initialize(filename, base_dir, gems_dir, default_gem)
super()
- filename.tap(&Gem::UNTAINT)
self.loaded_from = filename
@data = nil
@@ -113,14 +112,19 @@ class Gem::StubSpecification < Gem::BasicSpecification
Gem.open_file loaded_from, OPEN_MODE do |file|
file.readline # discard encoding line
- stubline = file.readline.chomp
+ stubline = file.readline
if stubline.start_with?(PREFIX)
- extensions = if /\A#{PREFIX}/ =~ file.readline.chomp
- $'.split "\0"
- else
- StubLine::NO_EXTENSIONS
- end
+ extline = file.readline
+ extensions =
+ if extline.delete_prefix!(PREFIX)
+ extline.chomp!
+ extline.split "\0"
+ else
+ StubLine::NO_EXTENSIONS
+ end
+
+ stubline.chomp! # readline(chomp: true) allocates 3x as much as .readline.chomp!
@data = StubLine.new stubline, extensions
end
rescue EOFError
diff --git a/lib/rubygems/tsort.rb b/lib/rubygems/tsort.rb
deleted file mode 100644
index 60ebe22e81..0000000000
--- a/lib/rubygems/tsort.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "tsort/lib/tsort"
diff --git a/lib/rubygems/tsort/lib/tsort.rb b/lib/rubygems/tsort/lib/tsort.rb
deleted file mode 100644
index f825f14257..0000000000
--- a/lib/rubygems/tsort/lib/tsort.rb
+++ /dev/null
@@ -1,452 +0,0 @@
-# frozen_string_literal: true
-
-#--
-# tsort.rb - provides a module for topological sorting and strongly connected components.
-#++
-#
-
-#
-# Gem::TSort implements topological sorting using Tarjan's algorithm for
-# strongly connected components.
-#
-# Gem::TSort is designed to be able to be used with any object which can be
-# interpreted as a directed graph.
-#
-# Gem::TSort requires two methods to interpret an object as a graph,
-# tsort_each_node and tsort_each_child.
-#
-# * tsort_each_node is used to iterate for all nodes over a graph.
-# * tsort_each_child is used to iterate for child nodes of a given node.
-#
-# The equality of nodes are defined by eql? and hash since
-# Gem::TSort uses Hash internally.
-#
-# == A Simple Example
-#
-# The following example demonstrates how to mix the Gem::TSort module into an
-# existing class (in this case, Hash). Here, we're treating each key in
-# the hash as a node in the graph, and so we simply alias the required
-# #tsort_each_node method to Hash's #each_key method. For each key in the
-# hash, the associated value is an array of the node's child nodes. This
-# choice in turn leads to our implementation of the required #tsort_each_child
-# method, which fetches the array of child nodes and then iterates over that
-# array using the user-supplied block.
-#
-# require 'rubygems/tsort/lib/tsort'
-#
-# class Hash
-# include Gem::TSort
-# alias tsort_each_node each_key
-# def tsort_each_child(node, &block)
-# fetch(node).each(&block)
-# end
-# end
-#
-# {1=>[2, 3], 2=>[3], 3=>[], 4=>[]}.tsort
-# #=> [3, 2, 1, 4]
-#
-# {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}.strongly_connected_components
-# #=> [[4], [2, 3], [1]]
-#
-# == A More Realistic Example
-#
-# A very simple `make' like tool can be implemented as follows:
-#
-# require 'rubygems/tsort/lib/tsort'
-#
-# class Make
-# def initialize
-# @dep = {}
-# @dep.default = []
-# end
-#
-# def rule(outputs, inputs=[], &block)
-# triple = [outputs, inputs, block]
-# outputs.each {|f| @dep[f] = [triple]}
-# @dep[triple] = inputs
-# end
-#
-# def build(target)
-# each_strongly_connected_component_from(target) {|ns|
-# if ns.length != 1
-# fs = ns.delete_if {|n| Array === n}
-# raise Gem::TSort::Cyclic.new("cyclic dependencies: #{fs.join ', '}")
-# end
-# n = ns.first
-# if Array === n
-# outputs, inputs, block = n
-# inputs_time = inputs.map {|f| File.mtime f}.max
-# begin
-# outputs_time = outputs.map {|f| File.mtime f}.min
-# rescue Errno::ENOENT
-# outputs_time = nil
-# end
-# if outputs_time == nil ||
-# inputs_time != nil && outputs_time <= inputs_time
-# sleep 1 if inputs_time != nil && inputs_time.to_i == Time.now.to_i
-# block.call
-# end
-# end
-# }
-# end
-#
-# def tsort_each_child(node, &block)
-# @dep[node].each(&block)
-# end
-# include Gem::TSort
-# end
-#
-# def command(arg)
-# print arg, "\n"
-# system arg
-# end
-#
-# m = Make.new
-# m.rule(%w[t1]) { command 'date > t1' }
-# m.rule(%w[t2]) { command 'date > t2' }
-# m.rule(%w[t3]) { command 'date > t3' }
-# m.rule(%w[t4], %w[t1 t3]) { command 'cat t1 t3 > t4' }
-# m.rule(%w[t5], %w[t4 t2]) { command 'cat t4 t2 > t5' }
-# m.build('t5')
-#
-# == Bugs
-#
-# * 'tsort.rb' is wrong name because this library uses
-# Tarjan's algorithm for strongly connected components.
-# Although 'strongly_connected_components.rb' is correct but too long.
-#
-# == References
-#
-# R. E. Tarjan, "Depth First Search and Linear Graph Algorithms",
-# <em>SIAM Journal on Computing</em>, Vol. 1, No. 2, pp. 146-160, June 1972.
-#
-
-module Gem::TSort
- class Cyclic < StandardError
- end
-
- # Returns a topologically sorted array of nodes.
- # The array is sorted from children to parents, i.e.
- # the first element has no child and the last node has no parent.
- #
- # If there is a cycle, Gem::TSort::Cyclic is raised.
- #
- # class G
- # include Gem::TSort
- # def initialize(g)
- # @g = g
- # end
- # def tsort_each_child(n, &b) @g[n].each(&b) end
- # def tsort_each_node(&b) @g.each_key(&b) end
- # end
- #
- # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
- # p graph.tsort #=> [4, 2, 3, 1]
- #
- # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
- # p graph.tsort # raises Gem::TSort::Cyclic
- #
- def tsort
- each_node = method(:tsort_each_node)
- each_child = method(:tsort_each_child)
- Gem::TSort.tsort(each_node, each_child)
- end
-
- # Returns a topologically sorted array of nodes.
- # The array is sorted from children to parents, i.e.
- # the first element has no child and the last node has no parent.
- #
- # The graph is represented by _each_node_ and _each_child_.
- # _each_node_ should have +call+ method which yields for each node in the graph.
- # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
- #
- # If there is a cycle, Gem::TSort::Cyclic is raised.
- #
- # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
- # each_node = lambda {|&b| g.each_key(&b) }
- # each_child = lambda {|n, &b| g[n].each(&b) }
- # p Gem::TSort.tsort(each_node, each_child) #=> [4, 2, 3, 1]
- #
- # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
- # each_node = lambda {|&b| g.each_key(&b) }
- # each_child = lambda {|n, &b| g[n].each(&b) }
- # p Gem::TSort.tsort(each_node, each_child) # raises Gem::TSort::Cyclic
- #
- def self.tsort(each_node, each_child)
- tsort_each(each_node, each_child).to_a
- end
-
- # The iterator version of the #tsort method.
- # <tt><em>obj</em>.tsort_each</tt> is similar to <tt><em>obj</em>.tsort.each</tt>, but
- # modification of _obj_ during the iteration may lead to unexpected results.
- #
- # #tsort_each returns +nil+.
- # If there is a cycle, Gem::TSort::Cyclic is raised.
- #
- # class G
- # include Gem::TSort
- # def initialize(g)
- # @g = g
- # end
- # def tsort_each_child(n, &b) @g[n].each(&b) end
- # def tsort_each_node(&b) @g.each_key(&b) end
- # end
- #
- # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
- # graph.tsort_each {|n| p n }
- # #=> 4
- # # 2
- # # 3
- # # 1
- #
- def tsort_each(&block) # :yields: node
- each_node = method(:tsort_each_node)
- each_child = method(:tsort_each_child)
- Gem::TSort.tsort_each(each_node, each_child, &block)
- end
-
- # The iterator version of the Gem::TSort.tsort method.
- #
- # The graph is represented by _each_node_ and _each_child_.
- # _each_node_ should have +call+ method which yields for each node in the graph.
- # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
- #
- # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
- # each_node = lambda {|&b| g.each_key(&b) }
- # each_child = lambda {|n, &b| g[n].each(&b) }
- # Gem::TSort.tsort_each(each_node, each_child) {|n| p n }
- # #=> 4
- # # 2
- # # 3
- # # 1
- #
- def self.tsort_each(each_node, each_child) # :yields: node
- return to_enum(__method__, each_node, each_child) unless block_given?
-
- each_strongly_connected_component(each_node, each_child) {|component|
- if component.size == 1
- yield component.first
- else
- raise Cyclic.new("topological sort failed: #{component.inspect}")
- end
- }
- end
-
- # Returns strongly connected components as an array of arrays of nodes.
- # The array is sorted from children to parents.
- # Each elements of the array represents a strongly connected component.
- #
- # class G
- # include Gem::TSort
- # def initialize(g)
- # @g = g
- # end
- # def tsort_each_child(n, &b) @g[n].each(&b) end
- # def tsort_each_node(&b) @g.each_key(&b) end
- # end
- #
- # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
- # p graph.strongly_connected_components #=> [[4], [2], [3], [1]]
- #
- # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
- # p graph.strongly_connected_components #=> [[4], [2, 3], [1]]
- #
- def strongly_connected_components
- each_node = method(:tsort_each_node)
- each_child = method(:tsort_each_child)
- Gem::TSort.strongly_connected_components(each_node, each_child)
- end
-
- # Returns strongly connected components as an array of arrays of nodes.
- # The array is sorted from children to parents.
- # Each elements of the array represents a strongly connected component.
- #
- # The graph is represented by _each_node_ and _each_child_.
- # _each_node_ should have +call+ method which yields for each node in the graph.
- # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
- #
- # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
- # each_node = lambda {|&b| g.each_key(&b) }
- # each_child = lambda {|n, &b| g[n].each(&b) }
- # p Gem::TSort.strongly_connected_components(each_node, each_child)
- # #=> [[4], [2], [3], [1]]
- #
- # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
- # each_node = lambda {|&b| g.each_key(&b) }
- # each_child = lambda {|n, &b| g[n].each(&b) }
- # p Gem::TSort.strongly_connected_components(each_node, each_child)
- # #=> [[4], [2, 3], [1]]
- #
- def self.strongly_connected_components(each_node, each_child)
- each_strongly_connected_component(each_node, each_child).to_a
- end
-
- # The iterator version of the #strongly_connected_components method.
- # <tt><em>obj</em>.each_strongly_connected_component</tt> is similar to
- # <tt><em>obj</em>.strongly_connected_components.each</tt>, but
- # modification of _obj_ during the iteration may lead to unexpected results.
- #
- # #each_strongly_connected_component returns +nil+.
- #
- # class G
- # include Gem::TSort
- # def initialize(g)
- # @g = g
- # end
- # def tsort_each_child(n, &b) @g[n].each(&b) end
- # def tsort_each_node(&b) @g.each_key(&b) end
- # end
- #
- # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
- # graph.each_strongly_connected_component {|scc| p scc }
- # #=> [4]
- # # [2]
- # # [3]
- # # [1]
- #
- # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
- # graph.each_strongly_connected_component {|scc| p scc }
- # #=> [4]
- # # [2, 3]
- # # [1]
- #
- def each_strongly_connected_component(&block) # :yields: nodes
- each_node = method(:tsort_each_node)
- each_child = method(:tsort_each_child)
- Gem::TSort.each_strongly_connected_component(each_node, each_child, &block)
- end
-
- # The iterator version of the Gem::TSort.strongly_connected_components method.
- #
- # The graph is represented by _each_node_ and _each_child_.
- # _each_node_ should have +call+ method which yields for each node in the graph.
- # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
- #
- # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
- # each_node = lambda {|&b| g.each_key(&b) }
- # each_child = lambda {|n, &b| g[n].each(&b) }
- # Gem::TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc }
- # #=> [4]
- # # [2]
- # # [3]
- # # [1]
- #
- # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
- # each_node = lambda {|&b| g.each_key(&b) }
- # each_child = lambda {|n, &b| g[n].each(&b) }
- # Gem::TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc }
- # #=> [4]
- # # [2, 3]
- # # [1]
- #
- def self.each_strongly_connected_component(each_node, each_child) # :yields: nodes
- return to_enum(__method__, each_node, each_child) unless block_given?
-
- id_map = {}
- stack = []
- each_node.call {|node|
- unless id_map.include? node
- each_strongly_connected_component_from(node, each_child, id_map, stack) {|c|
- yield c
- }
- end
- }
- nil
- end
-
- # Iterates over strongly connected component in the subgraph reachable from
- # _node_.
- #
- # Return value is unspecified.
- #
- # #each_strongly_connected_component_from doesn't call #tsort_each_node.
- #
- # class G
- # include Gem::TSort
- # def initialize(g)
- # @g = g
- # end
- # def tsort_each_child(n, &b) @g[n].each(&b) end
- # def tsort_each_node(&b) @g.each_key(&b) end
- # end
- #
- # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
- # graph.each_strongly_connected_component_from(2) {|scc| p scc }
- # #=> [4]
- # # [2]
- #
- # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
- # graph.each_strongly_connected_component_from(2) {|scc| p scc }
- # #=> [4]
- # # [2, 3]
- #
- def each_strongly_connected_component_from(node, id_map={}, stack=[], &block) # :yields: nodes
- Gem::TSort.each_strongly_connected_component_from(node, method(:tsort_each_child), id_map, stack, &block)
- end
-
- # Iterates over strongly connected components in a graph.
- # The graph is represented by _node_ and _each_child_.
- #
- # _node_ is the first node.
- # _each_child_ should have +call+ method which takes a node argument
- # and yields for each child node.
- #
- # Return value is unspecified.
- #
- # #Gem::TSort.each_strongly_connected_component_from is a class method and
- # it doesn't need a class to represent a graph which includes Gem::TSort.
- #
- # graph = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
- # each_child = lambda {|n, &b| graph[n].each(&b) }
- # Gem::TSort.each_strongly_connected_component_from(1, each_child) {|scc|
- # p scc
- # }
- # #=> [4]
- # # [2, 3]
- # # [1]
- #
- def self.each_strongly_connected_component_from(node, each_child, id_map={}, stack=[]) # :yields: nodes
- return to_enum(__method__, node, each_child, id_map, stack) unless block_given?
-
- minimum_id = node_id = id_map[node] = id_map.size
- stack_length = stack.length
- stack << node
-
- each_child.call(node) {|child|
- if id_map.include? child
- child_id = id_map[child]
- minimum_id = child_id if child_id && child_id < minimum_id
- else
- sub_minimum_id =
- each_strongly_connected_component_from(child, each_child, id_map, stack) {|c|
- yield c
- }
- minimum_id = sub_minimum_id if sub_minimum_id < minimum_id
- end
- }
-
- if node_id == minimum_id
- component = stack.slice!(stack_length .. -1)
- component.each {|n| id_map[n] = nil}
- yield component
- end
-
- minimum_id
- end
-
- # Should be implemented by a extended class.
- #
- # #tsort_each_node is used to iterate for all nodes over a graph.
- #
- def tsort_each_node # :yields: node
- raise NotImplementedError.new
- end
-
- # Should be implemented by a extended class.
- #
- # #tsort_each_child is used to iterate for child nodes of _node_.
- #
- def tsort_each_child(node) # :yields: child
- raise NotImplementedError.new
- end
-end
diff --git a/lib/rubygems/uninstaller.rb b/lib/rubygems/uninstaller.rb
index ac5fbfc8cf..c96df2a085 100644
--- a/lib/rubygems/uninstaller.rb
+++ b/lib/rubygems/uninstaller.rb
@@ -133,7 +133,7 @@ class Gem::Uninstaller
if index == list.size
remove_all list
- elsif index >= 0 && index < list.size
+ elsif index && index >= 0 && index < list.size
uninstall_gem list[index]
else
say "Error: must enter a number [1-#{list.size + 1}]"
diff --git a/lib/rubygems/update_suggestion.rb b/lib/rubygems/update_suggestion.rb
index 4cf085fd4c..6f3ec5f493 100644
--- a/lib/rubygems/update_suggestion.rb
+++ b/lib/rubygems/update_suggestion.rb
@@ -4,15 +4,6 @@
# Mixin methods for Gem::Command to promote available RubyGems update
module Gem::UpdateSuggestion
- # list taken from https://github.com/watson/ci-info/blob/7a3c30d/index.js#L56-L66
- CI_ENV_VARS = [
- "CI", # Travis CI, CircleCI, Cirrus CI, Gitlab CI, Appveyor, CodeShip, dsari
- "CONTINUOUS_INTEGRATION", # Travis CI, Cirrus CI
- "BUILD_NUMBER", # Jenkins, TeamCity
- "CI_APP_ID", "CI_BUILD_ID", "CI_BUILD_NUMBER", # Applfow
- "RUN_ID" # TaskCluster, dsari
- ].freeze
-
ONE_WEEK = 7 * 24 * 60 * 60
##
@@ -28,9 +19,9 @@ Run `gem update --system #{Gem.latest_rubygems_version}` to update your installa
end
##
- # Determines if current environment is eglible for update suggestion.
+ # Determines if current environment is eligible for update suggestion.
- def eglible_for_update?
+ def eligible_for_update?
# explicit opt-out
return false if Gem.configuration[:prevent_update_suggestion]
return false if ENV["RUBYGEMS_PREVENT_UPDATE_SUGGESTION"]
@@ -39,7 +30,7 @@ Run `gem update --system #{Gem.latest_rubygems_version}` to update your installa
return false unless Gem.ui.tty?
return false if Gem.rubygems_version.prerelease?
return false if Gem.disable_system_update_message
- return false if ci?
+ return false if Gem::CIDetector.ci?
# check makes sense only when we can store timestamp of last try
# otherwise we will not be able to prevent "annoying" update message
@@ -53,17 +44,13 @@ Run `gem update --system #{Gem.latest_rubygems_version}` to update your installa
# compare current and latest version, this is the part where
# latest rubygems spec is fetched from remote
- (Gem.rubygems_version < Gem.latest_rubygems_version).tap do |eglible|
+ (Gem.rubygems_version < Gem.latest_rubygems_version).tap do |eligible|
# store the time of last successful check into state file
Gem.configuration.last_update_check = check_time
- return eglible
+ return eligible
end
rescue StandardError # don't block install command on any problem
false
end
-
- def ci?
- CI_ENV_VARS.any? {|var| ENV.include?(var) }
- end
end
diff --git a/lib/rubygems/uri.rb b/lib/rubygems/uri.rb
index 4b5d035aa0..a44aaceba5 100644
--- a/lib/rubygems/uri.rb
+++ b/lib/rubygems/uri.rb
@@ -16,9 +16,9 @@ class Gem::Uri
# Parses uri, raising if it's invalid
def self.parse!(uri)
- require "uri"
+ require_relative "vendor/uri/lib/uri"
- raise URI::InvalidURIError unless uri
+ raise Gem::URI::InvalidURIError unless uri
return uri unless uri.is_a?(String)
@@ -28,9 +28,9 @@ class Gem::Uri
# as "%7BDESede%7D". If this is escaped again the percentage
# symbols will be escaped.
begin
- URI.parse(uri)
- rescue URI::InvalidURIError
- URI.parse(URI::DEFAULT_PARSER.escape(uri))
+ Gem::URI.parse(uri)
+ rescue Gem::URI::InvalidURIError
+ Gem::URI.parse(Gem::URI::DEFAULT_PARSER.escape(uri))
end
end
@@ -39,7 +39,7 @@ class Gem::Uri
def self.parse(uri)
parse!(uri)
- rescue URI::InvalidURIError
+ rescue Gem::URI::InvalidURIError
uri
end
diff --git a/lib/rubygems/user_interaction.rb b/lib/rubygems/user_interaction.rb
index 9f0538a6b0..0172c4ee89 100644
--- a/lib/rubygems/user_interaction.rb
+++ b/lib/rubygems/user_interaction.rb
@@ -193,7 +193,7 @@ class Gem::StreamUI
# then special operations (like asking for passwords) will use the TTY
# commands to disable character echo.
- def initialize(in_stream, out_stream, err_stream=STDERR, usetty=true)
+ def initialize(in_stream, out_stream, err_stream=$stderr, usetty=true)
@ins = in_stream
@outs = out_stream
@errs = err_stream
@@ -237,6 +237,7 @@ class Gem::StreamUI
return nil, nil unless result
result = result.strip.to_i - 1
+ return nil, nil unless (0...list.size) === result
[list[result], result]
end
@@ -590,8 +591,8 @@ class Gem::StreamUI
end
##
-# Subclass of StreamUI that instantiates the user interaction using STDIN,
-# STDOUT, and STDERR.
+# Subclass of StreamUI that instantiates the user interaction using $stdin,
+# $stdout, and $stderr.
class Gem::ConsoleUI < Gem::StreamUI
##
@@ -599,7 +600,7 @@ class Gem::ConsoleUI < Gem::StreamUI
# stdin, output to stdout and warnings or errors to stderr.
def initialize
- super STDIN, STDOUT, STDERR, true
+ super $stdin, $stdout, $stderr, true
end
end
diff --git a/lib/rubygems/util.rb b/lib/rubygems/util.rb
index 5c65dcc424..51f9c2029f 100644
--- a/lib/rubygems/util.rb
+++ b/lib/rubygems/util.rb
@@ -60,7 +60,7 @@ module Gem::Util
# Invokes system, but silences all output.
def self.silent_system(*command)
- opt = { :out => IO::NULL, :err => [:child, :out] }
+ opt = { out: IO::NULL, err: [:child, :out] }
if Hash === command.last
opt.update(command.last)
cmds = command[0...-1]
@@ -105,11 +105,11 @@ module Gem::Util
end
##
- # Corrects +path+ (usually returned by `URI.parse().path` on Windows), that
+ # Corrects +path+ (usually returned by `Gem::URI.parse().path` on Windows), that
# comes with a leading slash.
def self.correct_for_windows_path(path)
- if path[0].chr == "/" && path[1].chr =~ /[a-z]/i && path[2].chr == ":"
+ if path[0].chr == "/" && path[1].chr.match?(/[a-z]/i) && path[2].chr == ":"
path[1..-1]
else
path
diff --git a/lib/rubygems/util/licenses.rb b/lib/rubygems/util/licenses.rb
index ab5485d020..f3c7201639 100644
--- a/lib/rubygems/util/licenses.rb
+++ b/lib/rubygems/util/licenses.rb
@@ -22,14 +22,13 @@ class Gem::Licenses
AFL-2.0
AFL-2.1
AFL-3.0
- AGPL-1.0
AGPL-1.0-only
AGPL-1.0-or-later
- AGPL-3.0
AGPL-3.0-only
AGPL-3.0-or-later
AMDPLPA
AML
+ AML-glslang
AMPAS
ANTLR-PD
ANTLR-PD-fallback
@@ -39,10 +38,14 @@ class Gem::Licenses
APSL-1.1
APSL-1.2
APSL-2.0
+ ASWF-Digital-Assets-1.0
+ ASWF-Digital-Assets-1.1
Abstyles
AdaCore-doc
Adobe-2006
+ Adobe-Display-PostScript
Adobe-Glyph
+ Adobe-Utopia
Afmparse
Aladdin
Apache-1.0
@@ -56,13 +59,13 @@ class Gem::Licenses
Artistic-2.0
BSD-1-Clause
BSD-2-Clause
- BSD-2-Clause-FreeBSD
- BSD-2-Clause-NetBSD
+ BSD-2-Clause-Darwin
BSD-2-Clause-Patent
BSD-2-Clause-Views
BSD-3-Clause
BSD-3-Clause-Attribution
BSD-3-Clause-Clear
+ BSD-3-Clause-HP
BSD-3-Clause-LBNL
BSD-3-Clause-Modification
BSD-3-Clause-No-Military-License
@@ -70,6 +73,9 @@ class Gem::Licenses
BSD-3-Clause-No-Nuclear-License-2014
BSD-3-Clause-No-Nuclear-Warranty
BSD-3-Clause-Open-MPI
+ BSD-3-Clause-Sun
+ BSD-3-Clause-acpica
+ BSD-3-Clause-flex
BSD-4-Clause
BSD-4-Clause-Shortened
BSD-4-Clause-UC
@@ -77,8 +83,12 @@ class Gem::Licenses
BSD-4.3TAHOE
BSD-Advertising-Acknowledgement
BSD-Attribution-HPND-disclaimer
+ BSD-Inferno-Nettverk
BSD-Protection
BSD-Source-Code
+ BSD-Source-beginning-file
+ BSD-Systemics
+ BSD-Systemics-W3Works
BSL-1.0
BUSL-1.1
Baekmuk
@@ -90,7 +100,9 @@ class Gem::Licenses
Bitstream-Charter
Bitstream-Vera
BlueOak-1.0.0
+ Boehm-GC
Borceux
+ Brian-Gladman-2-Clause
Brian-Gladman-3-Clause
C-UDA-1.0
CAL-1.0
@@ -102,6 +114,7 @@ class Gem::Licenses
CC-BY-2.5-AU
CC-BY-3.0
CC-BY-3.0-AT
+ CC-BY-3.0-AU
CC-BY-3.0-DE
CC-BY-3.0-IGO
CC-BY-3.0-NL
@@ -144,6 +157,7 @@ class Gem::Licenses
CC-BY-SA-3.0
CC-BY-SA-3.0-AT
CC-BY-SA-3.0-DE
+ CC-BY-SA-3.0-IGO
CC-BY-SA-4.0
CC-PDDC
CC0-1.0
@@ -166,6 +180,7 @@ class Gem::Licenses
CERN-OHL-W-2.0
CFITSIO
CMU-Mach
+ CMU-Mach-nodoc
CNRI-Jython
CNRI-Python
CNRI-Python-GPL-Compatible
@@ -175,18 +190,23 @@ class Gem::Licenses
CPOL-1.02
CUA-OPL-1.0
Caldera
+ Caldera-no-preamble
ClArtistic
Clips
Community-Spec-1.0
Condor-1.1
Cornell-Lossless-JPEG
+ Cronyx
Crossword
CrystalStacker
Cube
D-FSL-1.0
+ DEC-3-Clause
DL-DE-BY-2.0
+ DL-DE-ZERO-2.0
DOC
DRL-1.0
+ DRL-1.1
DSDP
Dotseqn
ECL-1.0
@@ -204,32 +224,34 @@ class Gem::Licenses
Entessa
ErlPL-1.1
Eurosym
+ FBM
FDK-AAC
FSFAP
+ FSFAP-no-warranty-disclaimer
FSFUL
FSFULLR
FSFULLRWD
FTL
Fair
+ Ferguson-Twofish
Frameworx-1.0
FreeBSD-DOC
FreeImage
+ Furuseth
+ GCR-docs
GD
- GFDL-1.1
GFDL-1.1-invariants-only
GFDL-1.1-invariants-or-later
GFDL-1.1-no-invariants-only
GFDL-1.1-no-invariants-or-later
GFDL-1.1-only
GFDL-1.1-or-later
- GFDL-1.2
GFDL-1.2-invariants-only
GFDL-1.2-invariants-or-later
GFDL-1.2-no-invariants-only
GFDL-1.2-no-invariants-or-later
GFDL-1.2-only
GFDL-1.2-or-later
- GFDL-1.3
GFDL-1.3-invariants-only
GFDL-1.3-invariants-or-later
GFDL-1.3-no-invariants-only
@@ -238,33 +260,33 @@ class Gem::Licenses
GFDL-1.3-or-later
GL2PS
GLWTPL
- GPL-1.0
- GPL-1.0+
GPL-1.0-only
GPL-1.0-or-later
- GPL-2.0
- GPL-2.0+
GPL-2.0-only
GPL-2.0-or-later
- GPL-2.0-with-GCC-exception
- GPL-2.0-with-autoconf-exception
- GPL-2.0-with-bison-exception
- GPL-2.0-with-classpath-exception
- GPL-2.0-with-font-exception
- GPL-3.0
- GPL-3.0+
GPL-3.0-only
GPL-3.0-or-later
- GPL-3.0-with-GCC-exception
- GPL-3.0-with-autoconf-exception
Giftware
Glide
Glulxe
Graphics-Gems
HP-1986
+ HP-1989
HPND
+ HPND-DEC
+ HPND-Fenneberg-Livingston
+ HPND-INRIA-IMAG
+ HPND-Kevlin-Henney
+ HPND-MIT-disclaimer
HPND-Markus-Kuhn
+ HPND-Pbmplus
+ HPND-UC
+ HPND-doc
+ HPND-doc-sell
HPND-export-US
+ HPND-export-US-modify
+ HPND-sell-MIT-disclaimer-xserver
+ HPND-sell-regexpr
HPND-sell-variant
HPND-sell-variant-MIT-disclaimer
HTMLTIDY
@@ -278,9 +300,11 @@ class Gem::Licenses
IPA
IPL-1.0
ISC
+ ISC-Veillard
ImageMagick
Imlib2
Info-ZIP
+ Inner-Net-2.0
Intel
Intel-ACPI
Interbase-1.0
@@ -289,24 +313,20 @@ class Gem::Licenses
JSON
Jam
JasPer-2.0
+ Kastrup
Kazlib
Knuth-CTAN
LAL-1.2
LAL-1.3
- LGPL-2.0
- LGPL-2.0+
LGPL-2.0-only
LGPL-2.0-or-later
- LGPL-2.1
- LGPL-2.1+
LGPL-2.1-only
LGPL-2.1-or-later
- LGPL-3.0
- LGPL-3.0+
LGPL-3.0-only
LGPL-3.0-or-later
LGPLLR
LOOP
+ LPD-document
LPL-1.0
LPL-1.02
LPPL-1.0
@@ -317,23 +337,32 @@ class Gem::Licenses
LZMA-SDK-9.11-to-9.20
LZMA-SDK-9.22
Latex2e
+ Latex2e-translated-notice
Leptonica
LiLiQ-P-1.1
LiLiQ-R-1.1
LiLiQ-Rplus-1.1
Libpng
Linux-OpenIB
+ Linux-man-pages-1-para
Linux-man-pages-copyleft
+ Linux-man-pages-copyleft-2-para
+ Linux-man-pages-copyleft-var
+ Lucida-Bitmap-Fonts
MIT
MIT-0
MIT-CMU
+ MIT-Festival
MIT-Modern-Variant
MIT-Wu
MIT-advertising
MIT-enna
MIT-feh
MIT-open-group
+ MIT-testregex
MITNFA
+ MMIXware
+ MPEG-SSG
MPL-1.0
MPL-1.1
MPL-2.0
@@ -342,8 +371,11 @@ class Gem::Licenses
MS-PL
MS-RL
MTLL
+ Mackerras-3-Clause
+ Mackerras-3-Clause-acknowledgment
MakeIndex
Martin-Birgmeier
+ McPhee-slideshow
Minpack
MirOS
Motosoto
@@ -360,6 +392,7 @@ class Gem::Licenses
NICTA-1.0
NIST-PD
NIST-PD-fallback
+ NIST-Software
NLOD-1.0
NLOD-2.0
NLPL
@@ -376,7 +409,6 @@ class Gem::Licenses
Newsletr
Nokia
Noweb
- Nunit
O-UDA-1.0
OCCT-PL
OCLC-2.0
@@ -412,8 +444,10 @@ class Gem::Licenses
OLDAP-2.6
OLDAP-2.7
OLDAP-2.8
+ OLFL-1.3
OML
OPL-1.0
+ OPL-UK-3.0
OPUBL-1.0
OSET-PL-2.1
OSL-1.0
@@ -423,12 +457,16 @@ class Gem::Licenses
OSL-3.0
OpenPBS-2.3
OpenSSL
+ OpenSSL-standalone
+ OpenVision
+ PADL
PDDL-1.0
PHP-3.0
PHP-3.01
PSF-2.0
Parity-6.0.0
Parity-7.0.0
+ Pixar
Plexus
PolyForm-Noncommercial-1.0.0
PolyForm-Small-Business-1.0.0
@@ -447,20 +485,25 @@ class Gem::Licenses
Rdisc
Ruby
SAX-PD
+ SAX-PD-2.0
SCEA
SGI-B-1.0
SGI-B-1.1
SGI-B-2.0
+ SGI-OpenGL
+ SGP4
SHL-0.5
SHL-0.51
SISSL
SISSL-1.2
+ SL
SMLNJ
SMPPL
SNIA
SPL-1.0
SSH-OpenSSH
SSH-short
+ SSLeay-standalone
SSPL-1.0
SWL
Saxpath
@@ -469,30 +512,38 @@ class Gem::Licenses
Sendmail-8.23
SimPL-2.0
Sleepycat
+ Soundex
Spencer-86
Spencer-94
Spencer-99
- StandardML-NJ
SugarCRM-1.1.3
+ Sun-PPP
SunPro
Symlinks
TAPR-OHL-1.0
TCL
TCP-wrappers
+ TGPPL-1.0
TMate
TORQUE-1.1
TOSL
TPDL
TPL-1.0
TTWL
+ TTYP0
TU-Berlin-1.0
TU-Berlin-2.0
+ TermReadKey
UCAR
UCL-1.0
+ UMich-Merit
UPL-1.0
+ URT-RLE
+ Unicode-3.0
Unicode-DFS-2015
Unicode-DFS-2016
Unicode-TOU
+ UnixCrypt
Unlicense
VOSTROM
VSL-1.0
@@ -502,12 +553,15 @@ class Gem::Licenses
W3C-20150513
WTFPL
Watcom-1.0
+ Widget-Workshop
Wsuipa
X11
X11-distribute-modifications-variant
XFree86-1.1
XSkat
+ Xdebug-1.03
Xerox
+ Xfig
Xnet
YPL-1.0
YPL-1.1
@@ -515,49 +569,103 @@ class Gem::Licenses
ZPL-2.0
ZPL-2.1
Zed
+ Zeeff
Zend-2.0
Zimbra-1.3
Zimbra-1.4
Zlib
+ bcrypt-Solar-Designer
blessing
- bzip2-1.0.5
bzip2-1.0.6
+ check-cvs
checkmk
copyleft-next-0.3.0
copyleft-next-0.3.1
curl
diffmark
+ dtoa
dvipdfm
- eCos-2.0
eGenix
etalab-2.0
+ fwlw
gSOAP-1.3b
gnuplot
+ gtkbook
+ hdparm
iMatix
libpng-2.0
libselinux-1.0
libtiff
libutil-David-Nugent
+ lsof
+ magaz
+ mailprio
+ metamail
mpi-permissive
mpich2
mplus
+ pnmstitch
psfrag
psutils
+ python-ldap
+ radvd
snprintf
+ softSurfer
+ ssh-keyscan
+ swrule
+ ulem
w3m
- wxWindows
xinetd
+ xkeyboard-config-Zinoviev
xlock
xpp
zlib-acknowledgement
].freeze
+ DEPRECATED_LICENSE_IDENTIFIERS = %w[
+ AGPL-1.0
+ AGPL-3.0
+ BSD-2-Clause-FreeBSD
+ BSD-2-Clause-NetBSD
+ GFDL-1.1
+ GFDL-1.2
+ GFDL-1.3
+ GPL-1.0
+ GPL-1.0+
+ GPL-2.0
+ GPL-2.0+
+ GPL-2.0-with-GCC-exception
+ GPL-2.0-with-autoconf-exception
+ GPL-2.0-with-bison-exception
+ GPL-2.0-with-classpath-exception
+ GPL-2.0-with-font-exception
+ GPL-3.0
+ GPL-3.0+
+ GPL-3.0-with-GCC-exception
+ GPL-3.0-with-autoconf-exception
+ LGPL-2.0
+ LGPL-2.0+
+ LGPL-2.1
+ LGPL-2.1+
+ LGPL-3.0
+ LGPL-3.0+
+ Nunit
+ StandardML-NJ
+ bzip2-1.0.5
+ eCos-2.0
+ wxWindows
+ ].freeze
+
# exception identifiers
EXCEPTION_IDENTIFIERS = %w[
389-exception
+ Asterisk-exception
Autoconf-exception-2.0
Autoconf-exception-3.0
Autoconf-exception-generic
+ Autoconf-exception-generic-3.0
+ Autoconf-exception-macro
+ Bison-exception-1.24
Bison-exception-2.2
Bootloader-exception
CLISP-exception-2.0
@@ -567,20 +675,25 @@ class Gem::Licenses
Fawkes-Runtime-exception
Font-exception-2.0
GCC-exception-2.0
+ GCC-exception-2.0-note
GCC-exception-3.1
GNAT-exception
+ GNOME-examples-exception
+ GNU-compiler-exception
+ GPL-3.0-interface-exception
GPL-3.0-linking-exception
GPL-3.0-linking-source-exception
GPL-CC-1.0
GStreamer-exception-2005
GStreamer-exception-2008
+ Gmsh-exception
KiCad-libraries-exception
LGPL-3.0-linking-exception
+ LLGPL
LLVM-exception
LZMA-exception
Libtool-exception
Linux-syscall-note
- Nokia-Qt-exception-1.1
OCCT-exception-1.0
OCaml-LGPL-linking-exception
OpenJDK-assembly-exception-1.0
@@ -589,23 +702,35 @@ class Gem::Licenses
Qt-GPL-exception-1.0
Qt-LGPL-exception-1.1
Qwt-exception-1.0
+ SANE-exception
SHL-2.0
SHL-2.1
SWI-exception
Swift-exception
+ Texinfo-exception
+ UBDL-exception
Universal-FOSS-exception-1.0
WxWindows-exception-3.1
+ cryptsetup-OpenSSL-exception
eCos-exception-2.0
+ fmt-exception
freertos-exception-2.0
gnu-javamail-exception
i2p-gpl-java-exception
+ libpri-OpenH323-exception
mif-exception
openvpn-openssl-exception
+ stunnel-exception
u-boot-exception-2.0
+ vsftpd-openssl-exception
x11vnc-openssl-exception
].freeze
- REGEXP = /
+ DEPRECATED_EXCEPTION_IDENTIFIERS = %w[
+ Nokia-Qt-exception-1.1
+ ].freeze
+
+ VALID_REGEXP = /
\A
(?:
#{Regexp.union(LICENSE_IDENTIFIERS)}
@@ -615,10 +740,34 @@ class Gem::Licenses
| #{LICENSE_REF}
)
\Z
- /ox.freeze
+ /ox
+
+ DEPRECATED_LICENSE_REGEXP = /
+ \A
+ #{Regexp.union(DEPRECATED_LICENSE_IDENTIFIERS)}
+ \+?
+ (?:\s WITH \s .+?)?
+ \Z
+ /ox
+
+ DEPRECATED_EXCEPTION_REGEXP = /
+ \A
+ .+?
+ \+?
+ (?:\s WITH \s #{Regexp.union(DEPRECATED_EXCEPTION_IDENTIFIERS)})
+ \Z
+ /ox
def self.match?(license)
- REGEXP.match?(license)
+ VALID_REGEXP.match?(license)
+ end
+
+ def self.deprecated_license_id?(license)
+ DEPRECATED_LICENSE_REGEXP.match?(license)
+ end
+
+ def self.deprecated_exception_id?(license)
+ DEPRECATED_EXCEPTION_REGEXP.match?(license)
end
def self.suggestions(license)
diff --git a/lib/rubygems/vendor/molinillo/.document b/lib/rubygems/vendor/molinillo/.document
new file mode 100644
index 0000000000..0c43bbd6b3
--- /dev/null
+++ b/lib/rubygems/vendor/molinillo/.document
@@ -0,0 +1 @@
+# Vendored files do not need to be documented
diff --git a/lib/rubygems/vendor/molinillo/lib/molinillo.rb b/lib/rubygems/vendor/molinillo/lib/molinillo.rb
new file mode 100644
index 0000000000..dd5600c9e3
--- /dev/null
+++ b/lib/rubygems/vendor/molinillo/lib/molinillo.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require_relative 'molinillo/gem_metadata'
+require_relative 'molinillo/errors'
+require_relative 'molinillo/resolver'
+require_relative 'molinillo/modules/ui'
+require_relative 'molinillo/modules/specification_provider'
+
+# Gem::Molinillo is a generic dependency resolution algorithm.
+module Gem::Molinillo
+end
diff --git a/lib/rubygems/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb b/lib/rubygems/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb
new file mode 100644
index 0000000000..34842d46d5
--- /dev/null
+++ b/lib/rubygems/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+module Gem::Molinillo
+ # @!visibility private
+ module Delegates
+ # Delegates all {Gem::Molinillo::ResolutionState} methods to a `#state` property.
+ module ResolutionState
+ # (see Gem::Molinillo::ResolutionState#name)
+ def name
+ current_state = state || Gem::Molinillo::ResolutionState.empty
+ current_state.name
+ end
+
+ # (see Gem::Molinillo::ResolutionState#requirements)
+ def requirements
+ current_state = state || Gem::Molinillo::ResolutionState.empty
+ current_state.requirements
+ end
+
+ # (see Gem::Molinillo::ResolutionState#activated)
+ def activated
+ current_state = state || Gem::Molinillo::ResolutionState.empty
+ current_state.activated
+ end
+
+ # (see Gem::Molinillo::ResolutionState#requirement)
+ def requirement
+ current_state = state || Gem::Molinillo::ResolutionState.empty
+ current_state.requirement
+ end
+
+ # (see Gem::Molinillo::ResolutionState#possibilities)
+ def possibilities
+ current_state = state || Gem::Molinillo::ResolutionState.empty
+ current_state.possibilities
+ end
+
+ # (see Gem::Molinillo::ResolutionState#depth)
+ def depth
+ current_state = state || Gem::Molinillo::ResolutionState.empty
+ current_state.depth
+ end
+
+ # (see Gem::Molinillo::ResolutionState#conflicts)
+ def conflicts
+ current_state = state || Gem::Molinillo::ResolutionState.empty
+ current_state.conflicts
+ end
+
+ # (see Gem::Molinillo::ResolutionState#unused_unwind_options)
+ def unused_unwind_options
+ current_state = state || Gem::Molinillo::ResolutionState.empty
+ current_state.unused_unwind_options
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb b/lib/rubygems/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb
new file mode 100644
index 0000000000..8417721537
--- /dev/null
+++ b/lib/rubygems/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+
+module Gem::Molinillo
+ module Delegates
+ # Delegates all {Gem::Molinillo::SpecificationProvider} methods to a
+ # `#specification_provider` property.
+ module SpecificationProvider
+ # (see Gem::Molinillo::SpecificationProvider#search_for)
+ def search_for(dependency)
+ with_no_such_dependency_error_handling do
+ specification_provider.search_for(dependency)
+ end
+ end
+
+ # (see Gem::Molinillo::SpecificationProvider#dependencies_for)
+ def dependencies_for(specification)
+ with_no_such_dependency_error_handling do
+ specification_provider.dependencies_for(specification)
+ end
+ end
+
+ # (see Gem::Molinillo::SpecificationProvider#requirement_satisfied_by?)
+ def requirement_satisfied_by?(requirement, activated, spec)
+ with_no_such_dependency_error_handling do
+ specification_provider.requirement_satisfied_by?(requirement, activated, spec)
+ end
+ end
+
+ # (see Gem::Molinillo::SpecificationProvider#dependencies_equal?)
+ def dependencies_equal?(dependencies, other_dependencies)
+ with_no_such_dependency_error_handling do
+ specification_provider.dependencies_equal?(dependencies, other_dependencies)
+ end
+ end
+
+ # (see Gem::Molinillo::SpecificationProvider#name_for)
+ def name_for(dependency)
+ with_no_such_dependency_error_handling do
+ specification_provider.name_for(dependency)
+ end
+ end
+
+ # (see Gem::Molinillo::SpecificationProvider#name_for_explicit_dependency_source)
+ def name_for_explicit_dependency_source
+ with_no_such_dependency_error_handling do
+ specification_provider.name_for_explicit_dependency_source
+ end
+ end
+
+ # (see Gem::Molinillo::SpecificationProvider#name_for_locking_dependency_source)
+ def name_for_locking_dependency_source
+ with_no_such_dependency_error_handling do
+ specification_provider.name_for_locking_dependency_source
+ end
+ end
+
+ # (see Gem::Molinillo::SpecificationProvider#sort_dependencies)
+ def sort_dependencies(dependencies, activated, conflicts)
+ with_no_such_dependency_error_handling do
+ specification_provider.sort_dependencies(dependencies, activated, conflicts)
+ end
+ end
+
+ # (see Gem::Molinillo::SpecificationProvider#allow_missing?)
+ def allow_missing?(dependency)
+ with_no_such_dependency_error_handling do
+ specification_provider.allow_missing?(dependency)
+ end
+ end
+
+ private
+
+ # Ensures any raised {NoSuchDependencyError} has its
+ # {NoSuchDependencyError#required_by} set.
+ # @yield
+ def with_no_such_dependency_error_handling
+ yield
+ rescue NoSuchDependencyError => error
+ if state
+ vertex = activated.vertex_named(name_for(error.dependency))
+ error.required_by += vertex.incoming_edges.map { |e| e.origin.name }
+ error.required_by << name_for_explicit_dependency_source unless vertex.explicit_requirements.empty?
+ end
+ raise
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph.rb b/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph.rb
new file mode 100644
index 0000000000..2dbbc589dc
--- /dev/null
+++ b/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph.rb
@@ -0,0 +1,255 @@
+# frozen_string_literal: true
+
+require_relative '../../../../vendored_tsort'
+
+require_relative 'dependency_graph/log'
+require_relative 'dependency_graph/vertex'
+
+module Gem::Molinillo
+ # A directed acyclic graph that is tuned to hold named dependencies
+ class DependencyGraph
+ include Enumerable
+
+ # Enumerates through the vertices of the graph.
+ # @return [Array<Vertex>] The graph's vertices.
+ def each
+ return vertices.values.each unless block_given?
+ vertices.values.each { |v| yield v }
+ end
+
+ include Gem::TSort
+
+ # @!visibility private
+ alias tsort_each_node each
+
+ # @!visibility private
+ def tsort_each_child(vertex, &block)
+ vertex.successors.each(&block)
+ end
+
+ # Topologically sorts the given vertices.
+ # @param [Enumerable<Vertex>] vertices the vertices to be sorted, which must
+ # all belong to the same graph.
+ # @return [Array<Vertex>] The sorted vertices.
+ def self.tsort(vertices)
+ Gem::TSort.tsort(
+ lambda { |b| vertices.each(&b) },
+ lambda { |v, &b| (v.successors & vertices).each(&b) }
+ )
+ end
+
+ # A directed edge of a {DependencyGraph}
+ # @attr [Vertex] origin The origin of the directed edge
+ # @attr [Vertex] destination The destination of the directed edge
+ # @attr [Object] requirement The requirement the directed edge represents
+ Edge = Struct.new(:origin, :destination, :requirement)
+
+ # @return [{String => Vertex}] the vertices of the dependency graph, keyed
+ # by {Vertex#name}
+ attr_reader :vertices
+
+ # @return [Log] the op log for this graph
+ attr_reader :log
+
+ # Initializes an empty dependency graph
+ def initialize
+ @vertices = {}
+ @log = Log.new
+ end
+
+ # Tags the current state of the dependency as the given tag
+ # @param [Object] tag an opaque tag for the current state of the graph
+ # @return [Void]
+ def tag(tag)
+ log.tag(self, tag)
+ end
+
+ # Rewinds the graph to the state tagged as `tag`
+ # @param [Object] tag the tag to rewind to
+ # @return [Void]
+ def rewind_to(tag)
+ log.rewind_to(self, tag)
+ end
+
+ # Initializes a copy of a {DependencyGraph}, ensuring that all {#vertices}
+ # are properly copied.
+ # @param [DependencyGraph] other the graph to copy.
+ def initialize_copy(other)
+ super
+ @vertices = {}
+ @log = other.log.dup
+ traverse = lambda do |new_v, old_v|
+ return if new_v.outgoing_edges.size == old_v.outgoing_edges.size
+ old_v.outgoing_edges.each do |edge|
+ destination = add_vertex(edge.destination.name, edge.destination.payload)
+ add_edge_no_circular(new_v, destination, edge.requirement)
+ traverse.call(destination, edge.destination)
+ end
+ end
+ other.vertices.each do |name, vertex|
+ new_vertex = add_vertex(name, vertex.payload, vertex.root?)
+ new_vertex.explicit_requirements.replace(vertex.explicit_requirements)
+ traverse.call(new_vertex, vertex)
+ end
+ end
+
+ # @return [String] a string suitable for debugging
+ def inspect
+ "#{self.class}:#{vertices.values.inspect}"
+ end
+
+ # @param [Hash] options options for dot output.
+ # @return [String] Returns a dot format representation of the graph
+ def to_dot(options = {})
+ edge_label = options.delete(:edge_label)
+ raise ArgumentError, "Unknown options: #{options.keys}" unless options.empty?
+
+ dot_vertices = []
+ dot_edges = []
+ vertices.each do |n, v|
+ dot_vertices << " #{n} [label=\"{#{n}|#{v.payload}}\"]"
+ v.outgoing_edges.each do |e|
+ label = edge_label ? edge_label.call(e) : e.requirement
+ dot_edges << " #{e.origin.name} -> #{e.destination.name} [label=#{label.to_s.dump}]"
+ end
+ end
+
+ dot_vertices.uniq!
+ dot_vertices.sort!
+ dot_edges.uniq!
+ dot_edges.sort!
+
+ dot = dot_vertices.unshift('digraph G {').push('') + dot_edges.push('}')
+ dot.join("\n")
+ end
+
+ # @param [DependencyGraph] other
+ # @return [Boolean] whether the two dependency graphs are equal, determined
+ # by a recursive traversal of each {#root_vertices} and its
+ # {Vertex#successors}
+ def ==(other)
+ return false unless other
+ return true if equal?(other)
+ vertices.each do |name, vertex|
+ other_vertex = other.vertex_named(name)
+ return false unless other_vertex
+ return false unless vertex.payload == other_vertex.payload
+ return false unless other_vertex.successors.to_set == vertex.successors.to_set
+ end
+ end
+
+ # @param [String] name
+ # @param [Object] payload
+ # @param [Array<String>] parent_names
+ # @param [Object] requirement the requirement that is requiring the child
+ # @return [void]
+ def add_child_vertex(name, payload, parent_names, requirement)
+ root = !parent_names.delete(nil) { true }
+ vertex = add_vertex(name, payload, root)
+ vertex.explicit_requirements << requirement if root
+ parent_names.each do |parent_name|
+ parent_vertex = vertex_named(parent_name)
+ add_edge(parent_vertex, vertex, requirement)
+ end
+ vertex
+ end
+
+ # Adds a vertex with the given name, or updates the existing one.
+ # @param [String] name
+ # @param [Object] payload
+ # @return [Vertex] the vertex that was added to `self`
+ def add_vertex(name, payload, root = false)
+ log.add_vertex(self, name, payload, root)
+ end
+
+ # Detaches the {#vertex_named} `name` {Vertex} from the graph, recursively
+ # removing any non-root vertices that were orphaned in the process
+ # @param [String] name
+ # @return [Array<Vertex>] the vertices which have been detached
+ def detach_vertex_named(name)
+ log.detach_vertex_named(self, name)
+ end
+
+ # @param [String] name
+ # @return [Vertex,nil] the vertex with the given name
+ def vertex_named(name)
+ vertices[name]
+ end
+
+ # @param [String] name
+ # @return [Vertex,nil] the root vertex with the given name
+ def root_vertex_named(name)
+ vertex = vertex_named(name)
+ vertex if vertex && vertex.root?
+ end
+
+ # Adds a new {Edge} to the dependency graph
+ # @param [Vertex] origin
+ # @param [Vertex] destination
+ # @param [Object] requirement the requirement that this edge represents
+ # @return [Edge] the added edge
+ def add_edge(origin, destination, requirement)
+ if destination.path_to?(origin)
+ raise CircularDependencyError.new(path(destination, origin))
+ end
+ add_edge_no_circular(origin, destination, requirement)
+ end
+
+ # Deletes an {Edge} from the dependency graph
+ # @param [Edge] edge
+ # @return [Void]
+ def delete_edge(edge)
+ log.delete_edge(self, edge.origin.name, edge.destination.name, edge.requirement)
+ end
+
+ # Sets the payload of the vertex with the given name
+ # @param [String] name the name of the vertex
+ # @param [Object] payload the payload
+ # @return [Void]
+ def set_payload(name, payload)
+ log.set_payload(self, name, payload)
+ end
+
+ private
+
+ # Adds a new {Edge} to the dependency graph without checking for
+ # circularity.
+ # @param (see #add_edge)
+ # @return (see #add_edge)
+ def add_edge_no_circular(origin, destination, requirement)
+ log.add_edge_no_circular(self, origin.name, destination.name, requirement)
+ end
+
+ # Returns the path between two vertices
+ # @raise [ArgumentError] if there is no path between the vertices
+ # @param [Vertex] from
+ # @param [Vertex] to
+ # @return [Array<Vertex>] the shortest path from `from` to `to`
+ def path(from, to)
+ distances = Hash.new(vertices.size + 1)
+ distances[from.name] = 0
+ predecessors = {}
+ each do |vertex|
+ vertex.successors.each do |successor|
+ if distances[successor.name] > distances[vertex.name] + 1
+ distances[successor.name] = distances[vertex.name] + 1
+ predecessors[successor] = vertex
+ end
+ end
+ end
+
+ path = [to]
+ while before = predecessors[to]
+ path << before
+ to = before
+ break if to == from
+ end
+
+ unless path.last.equal?(from)
+ raise ArgumentError, "There is no path from #{from.name} to #{to.name}"
+ end
+
+ path.reverse
+ end
+ end
+end
diff --git a/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/action.rb b/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/action.rb
new file mode 100644
index 0000000000..8707ec451d
--- /dev/null
+++ b/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/action.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module Gem::Molinillo
+ class DependencyGraph
+ # An action that modifies a {DependencyGraph} that is reversible.
+ # @abstract
+ class Action
+ # rubocop:disable Lint/UnusedMethodArgument
+
+ # @return [Symbol] The name of the action.
+ def self.action_name
+ raise 'Abstract'
+ end
+
+ # Performs the action on the given graph.
+ # @param [DependencyGraph] graph the graph to perform the action on.
+ # @return [Void]
+ def up(graph)
+ raise 'Abstract'
+ end
+
+ # Reverses the action on the given graph.
+ # @param [DependencyGraph] graph the graph to reverse the action on.
+ # @return [Void]
+ def down(graph)
+ raise 'Abstract'
+ end
+
+ # @return [Action,Nil] The previous action
+ attr_accessor :previous
+
+ # @return [Action,Nil] The next action
+ attr_accessor :next
+ end
+ end
+end
diff --git a/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb b/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb
new file mode 100644
index 0000000000..aa9815c5ae
--- /dev/null
+++ b/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require_relative 'action'
+module Gem::Molinillo
+ class DependencyGraph
+ # @!visibility private
+ # (see DependencyGraph#add_edge_no_circular)
+ class AddEdgeNoCircular < Action
+ # @!group Action
+
+ # (see Action.action_name)
+ def self.action_name
+ :add_vertex
+ end
+
+ # (see Action#up)
+ def up(graph)
+ edge = make_edge(graph)
+ edge.origin.outgoing_edges << edge
+ edge.destination.incoming_edges << edge
+ edge
+ end
+
+ # (see Action#down)
+ def down(graph)
+ edge = make_edge(graph)
+ delete_first(edge.origin.outgoing_edges, edge)
+ delete_first(edge.destination.incoming_edges, edge)
+ end
+
+ # @!group AddEdgeNoCircular
+
+ # @return [String] the name of the origin of the edge
+ attr_reader :origin
+
+ # @return [String] the name of the destination of the edge
+ attr_reader :destination
+
+ # @return [Object] the requirement that the edge represents
+ attr_reader :requirement
+
+ # @param [DependencyGraph] graph the graph to find vertices from
+ # @return [Edge] The edge this action adds
+ def make_edge(graph)
+ Edge.new(graph.vertex_named(origin), graph.vertex_named(destination), requirement)
+ end
+
+ # Initialize an action to add an edge to a dependency graph
+ # @param [String] origin the name of the origin of the edge
+ # @param [String] destination the name of the destination of the edge
+ # @param [Object] requirement the requirement that the edge represents
+ def initialize(origin, destination, requirement)
+ @origin = origin
+ @destination = destination
+ @requirement = requirement
+ end
+
+ private
+
+ def delete_first(array, item)
+ return unless index = array.index(item)
+ array.delete_at(index)
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb b/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb
new file mode 100644
index 0000000000..9c7066a669
--- /dev/null
+++ b/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require_relative 'action'
+module Gem::Molinillo
+ class DependencyGraph
+ # @!visibility private
+ # (see DependencyGraph#add_vertex)
+ class AddVertex < Action # :nodoc:
+ # @!group Action
+
+ # (see Action.action_name)
+ def self.action_name
+ :add_vertex
+ end
+
+ # (see Action#up)
+ def up(graph)
+ if existing = graph.vertices[name]
+ @existing_payload = existing.payload
+ @existing_root = existing.root
+ end
+ vertex = existing || Vertex.new(name, payload)
+ graph.vertices[vertex.name] = vertex
+ vertex.payload ||= payload
+ vertex.root ||= root
+ vertex
+ end
+
+ # (see Action#down)
+ def down(graph)
+ if defined?(@existing_payload)
+ vertex = graph.vertices[name]
+ vertex.payload = @existing_payload
+ vertex.root = @existing_root
+ else
+ graph.vertices.delete(name)
+ end
+ end
+
+ # @!group AddVertex
+
+ # @return [String] the name of the vertex
+ attr_reader :name
+
+ # @return [Object] the payload for the vertex
+ attr_reader :payload
+
+ # @return [Boolean] whether the vertex is root or not
+ attr_reader :root
+
+ # Initialize an action to add a vertex to a dependency graph
+ # @param [String] name the name of the vertex
+ # @param [Object] payload the payload for the vertex
+ # @param [Boolean] root whether the vertex is root or not
+ def initialize(name, payload, root)
+ @name = name
+ @payload = payload
+ @root = root
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb b/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb
new file mode 100644
index 0000000000..1e62c0a0b6
--- /dev/null
+++ b/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require_relative 'action'
+module Gem::Molinillo
+ class DependencyGraph
+ # @!visibility private
+ # (see DependencyGraph#delete_edge)
+ class DeleteEdge < Action
+ # @!group Action
+
+ # (see Action.action_name)
+ def self.action_name
+ :delete_edge
+ end
+
+ # (see Action#up)
+ def up(graph)
+ edge = make_edge(graph)
+ edge.origin.outgoing_edges.delete(edge)
+ edge.destination.incoming_edges.delete(edge)
+ end
+
+ # (see Action#down)
+ def down(graph)
+ edge = make_edge(graph)
+ edge.origin.outgoing_edges << edge
+ edge.destination.incoming_edges << edge
+ edge
+ end
+
+ # @!group DeleteEdge
+
+ # @return [String] the name of the origin of the edge
+ attr_reader :origin_name
+
+ # @return [String] the name of the destination of the edge
+ attr_reader :destination_name
+
+ # @return [Object] the requirement that the edge represents
+ attr_reader :requirement
+
+ # @param [DependencyGraph] graph the graph to find vertices from
+ # @return [Edge] The edge this action adds
+ def make_edge(graph)
+ Edge.new(
+ graph.vertex_named(origin_name),
+ graph.vertex_named(destination_name),
+ requirement
+ )
+ end
+
+ # Initialize an action to add an edge to a dependency graph
+ # @param [String] origin_name the name of the origin of the edge
+ # @param [String] destination_name the name of the destination of the edge
+ # @param [Object] requirement the requirement that the edge represents
+ def initialize(origin_name, destination_name, requirement)
+ @origin_name = origin_name
+ @destination_name = destination_name
+ @requirement = requirement
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb b/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb
new file mode 100644
index 0000000000..6132f969b9
--- /dev/null
+++ b/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+require_relative 'action'
+module Gem::Molinillo
+ class DependencyGraph
+ # @!visibility private
+ # @see DependencyGraph#detach_vertex_named
+ class DetachVertexNamed < Action
+ # @!group Action
+
+ # (see Action#name)
+ def self.action_name
+ :add_vertex
+ end
+
+ # (see Action#up)
+ def up(graph)
+ return [] unless @vertex = graph.vertices.delete(name)
+
+ removed_vertices = [@vertex]
+ @vertex.outgoing_edges.each do |e|
+ v = e.destination
+ v.incoming_edges.delete(e)
+ if !v.root? && v.incoming_edges.empty?
+ removed_vertices.concat graph.detach_vertex_named(v.name)
+ end
+ end
+
+ @vertex.incoming_edges.each do |e|
+ v = e.origin
+ v.outgoing_edges.delete(e)
+ end
+
+ removed_vertices
+ end
+
+ # (see Action#down)
+ def down(graph)
+ return unless @vertex
+ graph.vertices[@vertex.name] = @vertex
+ @vertex.outgoing_edges.each do |e|
+ e.destination.incoming_edges << e
+ end
+ @vertex.incoming_edges.each do |e|
+ e.origin.outgoing_edges << e
+ end
+ end
+
+ # @!group DetachVertexNamed
+
+ # @return [String] the name of the vertex to detach
+ attr_reader :name
+
+ # Initialize an action to detach a vertex from a dependency graph
+ # @param [String] name the name of the vertex to detach
+ def initialize(name)
+ @name = name
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/log.rb b/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/log.rb
new file mode 100644
index 0000000000..6954c4b1f8
--- /dev/null
+++ b/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/log.rb
@@ -0,0 +1,126 @@
+# frozen_string_literal: true
+
+require_relative 'add_edge_no_circular'
+require_relative 'add_vertex'
+require_relative 'delete_edge'
+require_relative 'detach_vertex_named'
+require_relative 'set_payload'
+require_relative 'tag'
+
+module Gem::Molinillo
+ class DependencyGraph
+ # A log for dependency graph actions
+ class Log
+ # Initializes an empty log
+ def initialize
+ @current_action = @first_action = nil
+ end
+
+ # @!macro [new] action
+ # {include:DependencyGraph#$0}
+ # @param [Graph] graph the graph to perform the action on
+ # @param (see DependencyGraph#$0)
+ # @return (see DependencyGraph#$0)
+
+ # @macro action
+ def tag(graph, tag)
+ push_action(graph, Tag.new(tag))
+ end
+
+ # @macro action
+ def add_vertex(graph, name, payload, root)
+ push_action(graph, AddVertex.new(name, payload, root))
+ end
+
+ # @macro action
+ def detach_vertex_named(graph, name)
+ push_action(graph, DetachVertexNamed.new(name))
+ end
+
+ # @macro action
+ def add_edge_no_circular(graph, origin, destination, requirement)
+ push_action(graph, AddEdgeNoCircular.new(origin, destination, requirement))
+ end
+
+ # {include:DependencyGraph#delete_edge}
+ # @param [Graph] graph the graph to perform the action on
+ # @param [String] origin_name
+ # @param [String] destination_name
+ # @param [Object] requirement
+ # @return (see DependencyGraph#delete_edge)
+ def delete_edge(graph, origin_name, destination_name, requirement)
+ push_action(graph, DeleteEdge.new(origin_name, destination_name, requirement))
+ end
+
+ # @macro action
+ def set_payload(graph, name, payload)
+ push_action(graph, SetPayload.new(name, payload))
+ end
+
+ # Pops the most recent action from the log and undoes the action
+ # @param [DependencyGraph] graph
+ # @return [Action] the action that was popped off the log
+ def pop!(graph)
+ return unless action = @current_action
+ unless @current_action = action.previous
+ @first_action = nil
+ end
+ action.down(graph)
+ action
+ end
+
+ extend Enumerable
+
+ # @!visibility private
+ # Enumerates each action in the log
+ # @yield [Action]
+ def each
+ return enum_for unless block_given?
+ action = @first_action
+ loop do
+ break unless action
+ yield action
+ action = action.next
+ end
+ self
+ end
+
+ # @!visibility private
+ # Enumerates each action in the log in reverse order
+ # @yield [Action]
+ def reverse_each
+ return enum_for(:reverse_each) unless block_given?
+ action = @current_action
+ loop do
+ break unless action
+ yield action
+ action = action.previous
+ end
+ self
+ end
+
+ # @macro action
+ def rewind_to(graph, tag)
+ loop do
+ action = pop!(graph)
+ raise "No tag #{tag.inspect} found" unless action
+ break if action.class.action_name == :tag && action.tag == tag
+ end
+ end
+
+ private
+
+ # Adds the given action to the log, running the action
+ # @param [DependencyGraph] graph
+ # @param [Action] action
+ # @return The value returned by `action.up`
+ def push_action(graph, action)
+ action.previous = @current_action
+ @current_action.next = action if @current_action
+ @current_action = action
+ @first_action ||= action
+ action.up(graph)
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb b/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb
new file mode 100644
index 0000000000..9bcaaae0f9
--- /dev/null
+++ b/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require_relative 'action'
+module Gem::Molinillo
+ class DependencyGraph
+ # @!visibility private
+ # @see DependencyGraph#set_payload
+ class SetPayload < Action # :nodoc:
+ # @!group Action
+
+ # (see Action.action_name)
+ def self.action_name
+ :set_payload
+ end
+
+ # (see Action#up)
+ def up(graph)
+ vertex = graph.vertex_named(name)
+ @old_payload = vertex.payload
+ vertex.payload = payload
+ end
+
+ # (see Action#down)
+ def down(graph)
+ graph.vertex_named(name).payload = @old_payload
+ end
+
+ # @!group SetPayload
+
+ # @return [String] the name of the vertex
+ attr_reader :name
+
+ # @return [Object] the payload for the vertex
+ attr_reader :payload
+
+ # Initialize an action to add set the payload for a vertex in a dependency
+ # graph
+ # @param [String] name the name of the vertex
+ # @param [Object] payload the payload for the vertex
+ def initialize(name, payload)
+ @name = name
+ @payload = payload
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb b/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb
new file mode 100644
index 0000000000..62f243a2af
--- /dev/null
+++ b/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require_relative 'action'
+module Gem::Molinillo
+ class DependencyGraph
+ # @!visibility private
+ # @see DependencyGraph#tag
+ class Tag < Action
+ # @!group Action
+
+ # (see Action.action_name)
+ def self.action_name
+ :tag
+ end
+
+ # (see Action#up)
+ def up(graph)
+ end
+
+ # (see Action#down)
+ def down(graph)
+ end
+
+ # @!group Tag
+
+ # @return [Object] An opaque tag
+ attr_reader :tag
+
+ # Initialize an action to tag a state of a dependency graph
+ # @param [Object] tag an opaque tag
+ def initialize(tag)
+ @tag = tag
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb b/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb
new file mode 100644
index 0000000000..074de369be
--- /dev/null
+++ b/lib/rubygems/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb
@@ -0,0 +1,164 @@
+# frozen_string_literal: true
+
+module Gem::Molinillo
+ class DependencyGraph
+ # A vertex in a {DependencyGraph} that encapsulates a {#name} and a
+ # {#payload}
+ class Vertex
+ # @return [String] the name of the vertex
+ attr_accessor :name
+
+ # @return [Object] the payload the vertex holds
+ attr_accessor :payload
+
+ # @return [Array<Object>] the explicit requirements that required
+ # this vertex
+ attr_reader :explicit_requirements
+
+ # @return [Boolean] whether the vertex is considered a root vertex
+ attr_accessor :root
+ alias root? root
+
+ # Initializes a vertex with the given name and payload.
+ # @param [String] name see {#name}
+ # @param [Object] payload see {#payload}
+ def initialize(name, payload)
+ @name = name.frozen? ? name : name.dup.freeze
+ @payload = payload
+ @explicit_requirements = []
+ @outgoing_edges = []
+ @incoming_edges = []
+ end
+
+ # @return [Array<Object>] all of the requirements that required
+ # this vertex
+ def requirements
+ (incoming_edges.map(&:requirement) + explicit_requirements).uniq
+ end
+
+ # @return [Array<Edge>] the edges of {#graph} that have `self` as their
+ # {Edge#origin}
+ attr_accessor :outgoing_edges
+
+ # @return [Array<Edge>] the edges of {#graph} that have `self` as their
+ # {Edge#destination}
+ attr_accessor :incoming_edges
+
+ # @return [Array<Vertex>] the vertices of {#graph} that have an edge with
+ # `self` as their {Edge#destination}
+ def predecessors
+ incoming_edges.map(&:origin)
+ end
+
+ # @return [Set<Vertex>] the vertices of {#graph} where `self` is a
+ # {#descendent?}
+ def recursive_predecessors
+ _recursive_predecessors
+ end
+
+ # @param [Set<Vertex>] vertices the set to add the predecessors to
+ # @return [Set<Vertex>] the vertices of {#graph} where `self` is a
+ # {#descendent?}
+ def _recursive_predecessors(vertices = new_vertex_set)
+ incoming_edges.each do |edge|
+ vertex = edge.origin
+ next unless vertices.add?(vertex)
+ vertex._recursive_predecessors(vertices)
+ end
+
+ vertices
+ end
+ protected :_recursive_predecessors
+
+ # @return [Array<Vertex>] the vertices of {#graph} that have an edge with
+ # `self` as their {Edge#origin}
+ def successors
+ outgoing_edges.map(&:destination)
+ end
+
+ # @return [Set<Vertex>] the vertices of {#graph} where `self` is an
+ # {#ancestor?}
+ def recursive_successors
+ _recursive_successors
+ end
+
+ # @param [Set<Vertex>] vertices the set to add the successors to
+ # @return [Set<Vertex>] the vertices of {#graph} where `self` is an
+ # {#ancestor?}
+ def _recursive_successors(vertices = new_vertex_set)
+ outgoing_edges.each do |edge|
+ vertex = edge.destination
+ next unless vertices.add?(vertex)
+ vertex._recursive_successors(vertices)
+ end
+
+ vertices
+ end
+ protected :_recursive_successors
+
+ # @return [String] a string suitable for debugging
+ def inspect
+ "#{self.class}:#{name}(#{payload.inspect})"
+ end
+
+ # @return [Boolean] whether the two vertices are equal, determined
+ # by a recursive traversal of each {Vertex#successors}
+ def ==(other)
+ return true if equal?(other)
+ shallow_eql?(other) &&
+ successors.to_set == other.successors.to_set
+ end
+
+ # @param [Vertex] other the other vertex to compare to
+ # @return [Boolean] whether the two vertices are equal, determined
+ # solely by {#name} and {#payload} equality
+ def shallow_eql?(other)
+ return true if equal?(other)
+ other &&
+ name == other.name &&
+ payload == other.payload
+ end
+
+ alias eql? ==
+
+ # @return [Fixnum] a hash for the vertex based upon its {#name}
+ def hash
+ name.hash
+ end
+
+ # Is there a path from `self` to `other` following edges in the
+ # dependency graph?
+ # @return whether there is a path following edges within this {#graph}
+ def path_to?(other)
+ _path_to?(other)
+ end
+
+ alias descendent? path_to?
+
+ # @param [Vertex] other the vertex to check if there's a path to
+ # @param [Set<Vertex>] visited the vertices of {#graph} that have been visited
+ # @return [Boolean] whether there is a path to `other` from `self`
+ def _path_to?(other, visited = new_vertex_set)
+ return false unless visited.add?(self)
+ return true if equal?(other)
+ successors.any? { |v| v._path_to?(other, visited) }
+ end
+ protected :_path_to?
+
+ # Is there a path from `other` to `self` following edges in the
+ # dependency graph?
+ # @return whether there is a path following edges within this {#graph}
+ def ancestor?(other)
+ other.path_to?(self)
+ end
+
+ alias is_reachable_from? ancestor?
+
+ def new_vertex_set
+ require 'set'
+ Set.new
+ end
+ private :new_vertex_set
+ end
+ end
+end
diff --git a/lib/rubygems/vendor/molinillo/lib/molinillo/errors.rb b/lib/rubygems/vendor/molinillo/lib/molinillo/errors.rb
new file mode 100644
index 0000000000..07ea5fdf37
--- /dev/null
+++ b/lib/rubygems/vendor/molinillo/lib/molinillo/errors.rb
@@ -0,0 +1,149 @@
+# frozen_string_literal: true
+
+module Gem::Molinillo
+ # An error that occurred during the resolution process
+ class ResolverError < StandardError; end
+
+ # An error caused by searching for a dependency that is completely unknown,
+ # i.e. has no versions available whatsoever.
+ class NoSuchDependencyError < ResolverError
+ # @return [Object] the dependency that could not be found
+ attr_accessor :dependency
+
+ # @return [Array<Object>] the specifications that depended upon {#dependency}
+ attr_accessor :required_by
+
+ # Initializes a new error with the given missing dependency.
+ # @param [Object] dependency @see {#dependency}
+ # @param [Array<Object>] required_by @see {#required_by}
+ def initialize(dependency, required_by = [])
+ @dependency = dependency
+ @required_by = required_by.uniq
+ super()
+ end
+
+ # The error message for the missing dependency, including the specifications
+ # that had this dependency.
+ def message
+ sources = required_by.map { |r| "`#{r}`" }.join(' and ')
+ message = "Unable to find a specification for `#{dependency}`"
+ message += " depended upon by #{sources}" unless sources.empty?
+ message
+ end
+ end
+
+ # An error caused by attempting to fulfil a dependency that was circular
+ #
+ # @note This exception will be thrown if and only if a {Vertex} is added to a
+ # {DependencyGraph} that has a {DependencyGraph::Vertex#path_to?} an
+ # existing {DependencyGraph::Vertex}
+ class CircularDependencyError < ResolverError
+ # [Set<Object>] the dependencies responsible for causing the error
+ attr_reader :dependencies
+
+ # Initializes a new error with the given circular vertices.
+ # @param [Array<DependencyGraph::Vertex>] vertices the vertices in the dependency
+ # that caused the error
+ def initialize(vertices)
+ super "There is a circular dependency between #{vertices.map(&:name).join(' and ')}"
+ @dependencies = vertices.map { |vertex| vertex.payload.possibilities.last }.to_set
+ end
+ end
+
+ # An error caused by conflicts in version
+ class VersionConflict < ResolverError
+ # @return [{String => Resolution::Conflict}] the conflicts that caused
+ # resolution to fail
+ attr_reader :conflicts
+
+ # @return [SpecificationProvider] the specification provider used during
+ # resolution
+ attr_reader :specification_provider
+
+ # Initializes a new error with the given version conflicts.
+ # @param [{String => Resolution::Conflict}] conflicts see {#conflicts}
+ # @param [SpecificationProvider] specification_provider see {#specification_provider}
+ def initialize(conflicts, specification_provider)
+ pairs = []
+ conflicts.values.flat_map(&:requirements).each do |conflicting|
+ conflicting.each do |source, conflict_requirements|
+ conflict_requirements.each do |c|
+ pairs << [c, source]
+ end
+ end
+ end
+
+ super "Unable to satisfy the following requirements:\n\n" \
+ "#{pairs.map { |r, d| "- `#{r}` required by `#{d}`" }.join("\n")}"
+
+ @conflicts = conflicts
+ @specification_provider = specification_provider
+ end
+
+ require_relative 'delegates/specification_provider'
+ include Delegates::SpecificationProvider
+
+ # @return [String] An error message that includes requirement trees,
+ # which is much more detailed & customizable than the default message
+ # @param [Hash] opts the options to create a message with.
+ # @option opts [String] :solver_name The user-facing name of the solver
+ # @option opts [String] :possibility_type The generic name of a possibility
+ # @option opts [Proc] :reduce_trees A proc that reduced the list of requirement trees
+ # @option opts [Proc] :printable_requirement A proc that pretty-prints requirements
+ # @option opts [Proc] :additional_message_for_conflict A proc that appends additional
+ # messages for each conflict
+ # @option opts [Proc] :version_for_spec A proc that returns the version number for a
+ # possibility
+ def message_with_trees(opts = {})
+ solver_name = opts.delete(:solver_name) { self.class.name.split('::').first }
+ possibility_type = opts.delete(:possibility_type) { 'possibility named' }
+ reduce_trees = opts.delete(:reduce_trees) { proc { |trees| trees.uniq.sort_by(&:to_s) } }
+ printable_requirement = opts.delete(:printable_requirement) { proc { |req| req.to_s } }
+ additional_message_for_conflict = opts.delete(:additional_message_for_conflict) { proc {} }
+ version_for_spec = opts.delete(:version_for_spec) { proc(&:to_s) }
+ incompatible_version_message_for_conflict = opts.delete(:incompatible_version_message_for_conflict) do
+ proc do |name, _conflict|
+ %(#{solver_name} could not find compatible versions for #{possibility_type} "#{name}":)
+ end
+ end
+
+ full_message_for_conflict = opts.delete(:full_message_for_conflict) do
+ proc do |name, conflict|
+ o = "\n".dup << incompatible_version_message_for_conflict.call(name, conflict) << "\n"
+ if conflict.locked_requirement
+ o << %( In snapshot (#{name_for_locking_dependency_source}):\n)
+ o << %( #{printable_requirement.call(conflict.locked_requirement)}\n)
+ o << %(\n)
+ end
+ o << %( In #{name_for_explicit_dependency_source}:\n)
+ trees = reduce_trees.call(conflict.requirement_trees)
+
+ o << trees.map do |tree|
+ t = ''.dup
+ depth = 2
+ tree.each do |req|
+ t << ' ' * depth << printable_requirement.call(req)
+ unless tree.last == req
+ if spec = conflict.activated_by_name[name_for(req)]
+ t << %( was resolved to #{version_for_spec.call(spec)}, which)
+ end
+ t << %( depends on)
+ end
+ t << %(\n)
+ depth += 1
+ end
+ t
+ end.join("\n")
+
+ additional_message_for_conflict.call(o, name, conflict)
+
+ o
+ end
+ end
+
+ conflicts.sort.reduce(''.dup) do |o, (name, conflict)|
+ o << full_message_for_conflict.call(name, conflict)
+ end.strip
+ end
+ end
+end
diff --git a/lib/rubygems/vendor/molinillo/lib/molinillo/gem_metadata.rb b/lib/rubygems/vendor/molinillo/lib/molinillo/gem_metadata.rb
new file mode 100644
index 0000000000..8ed3a920a2
--- /dev/null
+++ b/lib/rubygems/vendor/molinillo/lib/molinillo/gem_metadata.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+module Gem::Molinillo
+ # The version of Gem::Molinillo.
+ VERSION = '0.8.0'.freeze
+end
diff --git a/lib/rubygems/vendor/molinillo/lib/molinillo/modules/specification_provider.rb b/lib/rubygems/vendor/molinillo/lib/molinillo/modules/specification_provider.rb
new file mode 100644
index 0000000000..85860902fc
--- /dev/null
+++ b/lib/rubygems/vendor/molinillo/lib/molinillo/modules/specification_provider.rb
@@ -0,0 +1,112 @@
+# frozen_string_literal: true
+
+module Gem::Molinillo
+ # Provides information about specifications and dependencies to the resolver,
+ # allowing the {Resolver} class to remain generic while still providing power
+ # and flexibility.
+ #
+ # This module contains the methods that users of Gem::Molinillo must to implement,
+ # using knowledge of their own model classes.
+ module SpecificationProvider
+ # Search for the specifications that match the given dependency.
+ # The specifications in the returned array will be considered in reverse
+ # order, so the latest version ought to be last.
+ # @note This method should be 'pure', i.e. the return value should depend
+ # only on the `dependency` parameter.
+ #
+ # @param [Object] dependency
+ # @return [Array<Object>] the specifications that satisfy the given
+ # `dependency`.
+ def search_for(dependency)
+ []
+ end
+
+ # Returns the dependencies of `specification`.
+ # @note This method should be 'pure', i.e. the return value should depend
+ # only on the `specification` parameter.
+ #
+ # @param [Object] specification
+ # @return [Array<Object>] the dependencies that are required by the given
+ # `specification`.
+ def dependencies_for(specification)
+ []
+ end
+
+ # Determines whether the given `requirement` is satisfied by the given
+ # `spec`, in the context of the current `activated` dependency graph.
+ #
+ # @param [Object] requirement
+ # @param [DependencyGraph] activated the current dependency graph in the
+ # resolution process.
+ # @param [Object] spec
+ # @return [Boolean] whether `requirement` is satisfied by `spec` in the
+ # context of the current `activated` dependency graph.
+ def requirement_satisfied_by?(requirement, activated, spec)
+ true
+ end
+
+ # Determines whether two arrays of dependencies are equal, and thus can be
+ # grouped.
+ #
+ # @param [Array<Object>] dependencies
+ # @param [Array<Object>] other_dependencies
+ # @return [Boolean] whether `dependencies` and `other_dependencies` should
+ # be considered equal.
+ def dependencies_equal?(dependencies, other_dependencies)
+ dependencies == other_dependencies
+ end
+
+ # Returns the name for the given `dependency`.
+ # @note This method should be 'pure', i.e. the return value should depend
+ # only on the `dependency` parameter.
+ #
+ # @param [Object] dependency
+ # @return [String] the name for the given `dependency`.
+ def name_for(dependency)
+ dependency.to_s
+ end
+
+ # @return [String] the name of the source of explicit dependencies, i.e.
+ # those passed to {Resolver#resolve} directly.
+ def name_for_explicit_dependency_source
+ 'user-specified dependency'
+ end
+
+ # @return [String] the name of the source of 'locked' dependencies, i.e.
+ # those passed to {Resolver#resolve} directly as the `base`
+ def name_for_locking_dependency_source
+ 'Lockfile'
+ end
+
+ # Sort dependencies so that the ones that are easiest to resolve are first.
+ # Easiest to resolve is (usually) defined by:
+ # 1) Is this dependency already activated?
+ # 2) How relaxed are the requirements?
+ # 3) Are there any conflicts for this dependency?
+ # 4) How many possibilities are there to satisfy this dependency?
+ #
+ # @param [Array<Object>] dependencies
+ # @param [DependencyGraph] activated the current dependency graph in the
+ # resolution process.
+ # @param [{String => Array<Conflict>}] conflicts
+ # @return [Array<Object>] a sorted copy of `dependencies`.
+ def sort_dependencies(dependencies, activated, conflicts)
+ dependencies.sort_by do |dependency|
+ name = name_for(dependency)
+ [
+ activated.vertex_named(name).payload ? 0 : 1,
+ conflicts[name] ? 0 : 1,
+ ]
+ end
+ end
+
+ # Returns whether this dependency, which has no possible matching
+ # specifications, can safely be ignored.
+ #
+ # @param [Object] dependency
+ # @return [Boolean] whether this dependency can safely be skipped.
+ def allow_missing?(dependency)
+ false
+ end
+ end
+end
diff --git a/lib/rubygems/vendor/molinillo/lib/molinillo/modules/ui.rb b/lib/rubygems/vendor/molinillo/lib/molinillo/modules/ui.rb
new file mode 100644
index 0000000000..464722902e
--- /dev/null
+++ b/lib/rubygems/vendor/molinillo/lib/molinillo/modules/ui.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+module Gem::Molinillo
+ # Conveys information about the resolution process to a user.
+ module UI
+ # The {IO} object that should be used to print output. `STDOUT`, by default.
+ #
+ # @return [IO]
+ def output
+ STDOUT
+ end
+
+ # Called roughly every {#progress_rate}, this method should convey progress
+ # to the user.
+ #
+ # @return [void]
+ def indicate_progress
+ output.print '.' unless debug?
+ end
+
+ # How often progress should be conveyed to the user via
+ # {#indicate_progress}, in seconds. A third of a second, by default.
+ #
+ # @return [Float]
+ def progress_rate
+ 0.33
+ end
+
+ # Called before resolution begins.
+ #
+ # @return [void]
+ def before_resolution
+ output.print 'Resolving dependencies...'
+ end
+
+ # Called after resolution ends (either successfully or with an error).
+ # By default, prints a newline.
+ #
+ # @return [void]
+ def after_resolution
+ output.puts
+ end
+
+ # Conveys debug information to the user.
+ #
+ # @param [Integer] depth the current depth of the resolution process.
+ # @return [void]
+ def debug(depth = 0)
+ if debug?
+ debug_info = yield
+ debug_info = debug_info.inspect unless debug_info.is_a?(String)
+ debug_info = debug_info.split("\n").map { |s| ":#{depth.to_s.rjust 4}: #{s}" }
+ output.puts debug_info
+ end
+ end
+
+ # Whether or not debug messages should be printed.
+ # By default, whether or not the `MOLINILLO_DEBUG` environment variable is
+ # set.
+ #
+ # @return [Boolean]
+ def debug?
+ return @debug_mode if defined?(@debug_mode)
+ @debug_mode = ENV['MOLINILLO_DEBUG']
+ end
+ end
+end
diff --git a/lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb b/lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb
new file mode 100644
index 0000000000..84ec6cb095
--- /dev/null
+++ b/lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb
@@ -0,0 +1,839 @@
+# frozen_string_literal: true
+
+module Gem::Molinillo
+ class Resolver
+ # A specific resolution from a given {Resolver}
+ class Resolution
+ # A conflict that the resolution process encountered
+ # @attr [Object] requirement the requirement that immediately led to the conflict
+ # @attr [{String,Nil=>[Object]}] requirements the requirements that caused the conflict
+ # @attr [Object, nil] existing the existing spec that was in conflict with
+ # the {#possibility}
+ # @attr [Object] possibility_set the set of specs that was unable to be
+ # activated due to a conflict.
+ # @attr [Object] locked_requirement the relevant locking requirement.
+ # @attr [Array<Array<Object>>] requirement_trees the different requirement
+ # trees that led to every requirement for the conflicting name.
+ # @attr [{String=>Object}] activated_by_name the already-activated specs.
+ # @attr [Object] underlying_error an error that has occurred during resolution, and
+ # will be raised at the end of it if no resolution is found.
+ Conflict = Struct.new(
+ :requirement,
+ :requirements,
+ :existing,
+ :possibility_set,
+ :locked_requirement,
+ :requirement_trees,
+ :activated_by_name,
+ :underlying_error
+ )
+
+ class Conflict
+ # @return [Object] a spec that was unable to be activated due to a conflict
+ def possibility
+ possibility_set && possibility_set.latest_version
+ end
+ end
+
+ # A collection of possibility states that share the same dependencies
+ # @attr [Array] dependencies the dependencies for this set of possibilities
+ # @attr [Array] possibilities the possibilities
+ PossibilitySet = Struct.new(:dependencies, :possibilities)
+
+ class PossibilitySet
+ # String representation of the possibility set, for debugging
+ def to_s
+ "[#{possibilities.join(', ')}]"
+ end
+
+ # @return [Object] most up-to-date dependency in the possibility set
+ def latest_version
+ possibilities.last
+ end
+ end
+
+ # Details of the state to unwind to when a conflict occurs, and the cause of the unwind
+ # @attr [Integer] state_index the index of the state to unwind to
+ # @attr [Object] state_requirement the requirement of the state we're unwinding to
+ # @attr [Array] requirement_tree for the requirement we're relaxing
+ # @attr [Array] conflicting_requirements the requirements that combined to cause the conflict
+ # @attr [Array] requirement_trees for the conflict
+ # @attr [Array] requirements_unwound_to_instead array of unwind requirements that were chosen over this unwind
+ UnwindDetails = Struct.new(
+ :state_index,
+ :state_requirement,
+ :requirement_tree,
+ :conflicting_requirements,
+ :requirement_trees,
+ :requirements_unwound_to_instead
+ )
+
+ class UnwindDetails
+ include Comparable
+
+ # We compare UnwindDetails when choosing which state to unwind to. If
+ # two options have the same state_index we prefer the one most
+ # removed from a requirement that caused the conflict. Both options
+ # would unwind to the same state, but a `grandparent` option will
+ # filter out fewer of its possibilities after doing so - where a state
+ # is both a `parent` and a `grandparent` to requirements that have
+ # caused a conflict this is the correct behaviour.
+ # @param [UnwindDetail] other UnwindDetail to be compared
+ # @return [Integer] integer specifying ordering
+ def <=>(other)
+ if state_index > other.state_index
+ 1
+ elsif state_index == other.state_index
+ reversed_requirement_tree_index <=> other.reversed_requirement_tree_index
+ else
+ -1
+ end
+ end
+
+ # @return [Integer] index of state requirement in reversed requirement tree
+ # (the conflicting requirement itself will be at position 0)
+ def reversed_requirement_tree_index
+ @reversed_requirement_tree_index ||=
+ if state_requirement
+ requirement_tree.reverse.index(state_requirement)
+ else
+ 999_999
+ end
+ end
+
+ # @return [Boolean] where the requirement of the state we're unwinding
+ # to directly caused the conflict. Note: in this case, it is
+ # impossible for the state we're unwinding to be a parent of
+ # any of the other conflicting requirements (or we would have
+ # circularity)
+ def unwinding_to_primary_requirement?
+ requirement_tree.last == state_requirement
+ end
+
+ # @return [Array] array of sub-dependencies to avoid when choosing a
+ # new possibility for the state we've unwound to. Only relevant for
+ # non-primary unwinds
+ def sub_dependencies_to_avoid
+ @requirements_to_avoid ||=
+ requirement_trees.map do |tree|
+ index = tree.index(state_requirement)
+ tree[index + 1] if index
+ end.compact
+ end
+
+ # @return [Array] array of all the requirements that led to the need for
+ # this unwind
+ def all_requirements
+ @all_requirements ||= requirement_trees.flatten(1)
+ end
+ end
+
+ # @return [SpecificationProvider] the provider that knows about
+ # dependencies, requirements, specifications, versions, etc.
+ attr_reader :specification_provider
+
+ # @return [UI] the UI that knows how to communicate feedback about the
+ # resolution process back to the user
+ attr_reader :resolver_ui
+
+ # @return [DependencyGraph] the base dependency graph to which
+ # dependencies should be 'locked'
+ attr_reader :base
+
+ # @return [Array] the dependencies that were explicitly required
+ attr_reader :original_requested
+
+ # Initializes a new resolution.
+ # @param [SpecificationProvider] specification_provider
+ # see {#specification_provider}
+ # @param [UI] resolver_ui see {#resolver_ui}
+ # @param [Array] requested see {#original_requested}
+ # @param [DependencyGraph] base see {#base}
+ def initialize(specification_provider, resolver_ui, requested, base)
+ @specification_provider = specification_provider
+ @resolver_ui = resolver_ui
+ @original_requested = requested
+ @base = base
+ @states = []
+ @iteration_counter = 0
+ @parents_of = Hash.new { |h, k| h[k] = [] }
+ end
+
+ # Resolves the {#original_requested} dependencies into a full dependency
+ # graph
+ # @raise [ResolverError] if successful resolution is impossible
+ # @return [DependencyGraph] the dependency graph of successfully resolved
+ # dependencies
+ def resolve
+ start_resolution
+
+ while state
+ break if !state.requirement && state.requirements.empty?
+ indicate_progress
+ if state.respond_to?(:pop_possibility_state) # DependencyState
+ debug(depth) { "Creating possibility state for #{requirement} (#{possibilities.count} remaining)" }
+ state.pop_possibility_state.tap do |s|
+ if s
+ states.push(s)
+ activated.tag(s)
+ end
+ end
+ end
+ process_topmost_state
+ end
+
+ resolve_activated_specs
+ ensure
+ end_resolution
+ end
+
+ # @return [Integer] the number of resolver iterations in between calls to
+ # {#resolver_ui}'s {UI#indicate_progress} method
+ attr_accessor :iteration_rate
+ private :iteration_rate
+
+ # @return [Time] the time at which resolution began
+ attr_accessor :started_at
+ private :started_at
+
+ # @return [Array<ResolutionState>] the stack of states for the resolution
+ attr_accessor :states
+ private :states
+
+ private
+
+ # Sets up the resolution process
+ # @return [void]
+ def start_resolution
+ @started_at = Time.now
+
+ push_initial_state
+
+ debug { "Starting resolution (#{@started_at})\nUser-requested dependencies: #{original_requested}" }
+ resolver_ui.before_resolution
+ end
+
+ def resolve_activated_specs
+ activated.vertices.each do |_, vertex|
+ next unless vertex.payload
+
+ latest_version = vertex.payload.possibilities.reverse_each.find do |possibility|
+ vertex.requirements.all? { |req| requirement_satisfied_by?(req, activated, possibility) }
+ end
+
+ activated.set_payload(vertex.name, latest_version)
+ end
+ activated.freeze
+ end
+
+ # Ends the resolution process
+ # @return [void]
+ def end_resolution
+ resolver_ui.after_resolution
+ debug do
+ "Finished resolution (#{@iteration_counter} steps) " \
+ "(Took #{(ended_at = Time.now) - @started_at} seconds) (#{ended_at})"
+ end
+ debug { 'Unactivated: ' + Hash[activated.vertices.reject { |_n, v| v.payload }].keys.join(', ') } if state
+ debug { 'Activated: ' + Hash[activated.vertices.select { |_n, v| v.payload }].keys.join(', ') } if state
+ end
+
+ require_relative 'state'
+ require_relative 'modules/specification_provider'
+
+ require_relative 'delegates/resolution_state'
+ require_relative 'delegates/specification_provider'
+
+ include Gem::Molinillo::Delegates::ResolutionState
+ include Gem::Molinillo::Delegates::SpecificationProvider
+
+ # Processes the topmost available {RequirementState} on the stack
+ # @return [void]
+ def process_topmost_state
+ if possibility
+ attempt_to_activate
+ else
+ create_conflict
+ unwind_for_conflict
+ end
+ rescue CircularDependencyError => underlying_error
+ create_conflict(underlying_error)
+ unwind_for_conflict
+ end
+
+ # @return [Object] the current possibility that the resolution is trying
+ # to activate
+ def possibility
+ possibilities.last
+ end
+
+ # @return [RequirementState] the current state the resolution is
+ # operating upon
+ def state
+ states.last
+ end
+
+ # Creates and pushes the initial state for the resolution, based upon the
+ # {#requested} dependencies
+ # @return [void]
+ def push_initial_state
+ graph = DependencyGraph.new.tap do |dg|
+ original_requested.each do |requested|
+ vertex = dg.add_vertex(name_for(requested), nil, true)
+ vertex.explicit_requirements << requested
+ end
+ dg.tag(:initial_state)
+ end
+
+ push_state_for_requirements(original_requested, true, graph)
+ end
+
+ # Unwinds the states stack because a conflict has been encountered
+ # @return [void]
+ def unwind_for_conflict
+ details_for_unwind = build_details_for_unwind
+ unwind_options = unused_unwind_options
+ debug(depth) { "Unwinding for conflict: #{requirement} to #{details_for_unwind.state_index / 2}" }
+ conflicts.tap do |c|
+ sliced_states = states.slice!((details_for_unwind.state_index + 1)..-1)
+ raise_error_unless_state(c)
+ activated.rewind_to(sliced_states.first || :initial_state) if sliced_states
+ state.conflicts = c
+ state.unused_unwind_options = unwind_options
+ filter_possibilities_after_unwind(details_for_unwind)
+ index = states.size - 1
+ @parents_of.each { |_, a| a.reject! { |i| i >= index } }
+ state.unused_unwind_options.reject! { |uw| uw.state_index >= index }
+ end
+ end
+
+ # Raises a VersionConflict error, or any underlying error, if there is no
+ # current state
+ # @return [void]
+ def raise_error_unless_state(conflicts)
+ return if state
+
+ error = conflicts.values.map(&:underlying_error).compact.first
+ raise error || VersionConflict.new(conflicts, specification_provider)
+ end
+
+ # @return [UnwindDetails] Details of the nearest index to which we could unwind
+ def build_details_for_unwind
+ # Get the possible unwinds for the current conflict
+ current_conflict = conflicts[name]
+ binding_requirements = binding_requirements_for_conflict(current_conflict)
+ unwind_details = unwind_options_for_requirements(binding_requirements)
+
+ last_detail_for_current_unwind = unwind_details.sort.last
+ current_detail = last_detail_for_current_unwind
+
+ # Look for past conflicts that could be unwound to affect the
+ # requirement tree for the current conflict
+ all_reqs = last_detail_for_current_unwind.all_requirements
+ all_reqs_size = all_reqs.size
+ relevant_unused_unwinds = unused_unwind_options.select do |alternative|
+ diff_reqs = all_reqs - alternative.requirements_unwound_to_instead
+ next if diff_reqs.size == all_reqs_size
+ # Find the highest index unwind whilst looping through
+ current_detail = alternative if alternative > current_detail
+ alternative
+ end
+
+ # Add the current unwind options to the `unused_unwind_options` array.
+ # The "used" option will be filtered out during `unwind_for_conflict`.
+ state.unused_unwind_options += unwind_details.reject { |detail| detail.state_index == -1 }
+
+ # Update the requirements_unwound_to_instead on any relevant unused unwinds
+ relevant_unused_unwinds.each do |d|
+ (d.requirements_unwound_to_instead << current_detail.state_requirement).uniq!
+ end
+ unwind_details.each do |d|
+ (d.requirements_unwound_to_instead << current_detail.state_requirement).uniq!
+ end
+
+ current_detail
+ end
+
+ # @param [Array<Object>] binding_requirements array of requirements that combine to create a conflict
+ # @return [Array<UnwindDetails>] array of UnwindDetails that have a chance
+ # of resolving the passed requirements
+ def unwind_options_for_requirements(binding_requirements)
+ unwind_details = []
+
+ trees = []
+ binding_requirements.reverse_each do |r|
+ partial_tree = [r]
+ trees << partial_tree
+ unwind_details << UnwindDetails.new(-1, nil, partial_tree, binding_requirements, trees, [])
+
+ # If this requirement has alternative possibilities, check if any would
+ # satisfy the other requirements that created this conflict
+ requirement_state = find_state_for(r)
+ if conflict_fixing_possibilities?(requirement_state, binding_requirements)
+ unwind_details << UnwindDetails.new(
+ states.index(requirement_state),
+ r,
+ partial_tree,
+ binding_requirements,
+ trees,
+ []
+ )
+ end
+
+ # Next, look at the parent of this requirement, and check if the requirement
+ # could have been avoided if an alternative PossibilitySet had been chosen
+ parent_r = parent_of(r)
+ next if parent_r.nil?
+ partial_tree.unshift(parent_r)
+ requirement_state = find_state_for(parent_r)
+ if requirement_state.possibilities.any? { |set| !set.dependencies.include?(r) }
+ unwind_details << UnwindDetails.new(
+ states.index(requirement_state),
+ parent_r,
+ partial_tree,
+ binding_requirements,
+ trees,
+ []
+ )
+ end
+
+ # Finally, look at the grandparent and up of this requirement, looking
+ # for any possibilities that wouldn't create their parent requirement
+ grandparent_r = parent_of(parent_r)
+ until grandparent_r.nil?
+ partial_tree.unshift(grandparent_r)
+ requirement_state = find_state_for(grandparent_r)
+ if requirement_state.possibilities.any? { |set| !set.dependencies.include?(parent_r) }
+ unwind_details << UnwindDetails.new(
+ states.index(requirement_state),
+ grandparent_r,
+ partial_tree,
+ binding_requirements,
+ trees,
+ []
+ )
+ end
+ parent_r = grandparent_r
+ grandparent_r = parent_of(parent_r)
+ end
+ end
+
+ unwind_details
+ end
+
+ # @param [DependencyState] state
+ # @param [Array] binding_requirements array of requirements
+ # @return [Boolean] whether or not the given state has any possibilities
+ # that could satisfy the given requirements
+ def conflict_fixing_possibilities?(state, binding_requirements)
+ return false unless state
+
+ state.possibilities.any? do |possibility_set|
+ possibility_set.possibilities.any? do |poss|
+ possibility_satisfies_requirements?(poss, binding_requirements)
+ end
+ end
+ end
+
+ # Filter's a state's possibilities to remove any that would not fix the
+ # conflict we've just rewound from
+ # @param [UnwindDetails] unwind_details details of the conflict just
+ # unwound from
+ # @return [void]
+ def filter_possibilities_after_unwind(unwind_details)
+ return unless state && !state.possibilities.empty?
+
+ if unwind_details.unwinding_to_primary_requirement?
+ filter_possibilities_for_primary_unwind(unwind_details)
+ else
+ filter_possibilities_for_parent_unwind(unwind_details)
+ end
+ end
+
+ # Filter's a state's possibilities to remove any that would not satisfy
+ # the requirements in the conflict we've just rewound from
+ # @param [UnwindDetails] unwind_details details of the conflict just unwound from
+ # @return [void]
+ def filter_possibilities_for_primary_unwind(unwind_details)
+ unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
+ unwinds_to_state << unwind_details
+ unwind_requirement_sets = unwinds_to_state.map(&:conflicting_requirements)
+
+ state.possibilities.reject! do |possibility_set|
+ possibility_set.possibilities.none? do |poss|
+ unwind_requirement_sets.any? do |requirements|
+ possibility_satisfies_requirements?(poss, requirements)
+ end
+ end
+ end
+ end
+
+ # @param [Object] possibility a single possibility
+ # @param [Array] requirements an array of requirements
+ # @return [Boolean] whether the possibility satisfies all of the
+ # given requirements
+ def possibility_satisfies_requirements?(possibility, requirements)
+ name = name_for(possibility)
+
+ activated.tag(:swap)
+ activated.set_payload(name, possibility) if activated.vertex_named(name)
+ satisfied = requirements.all? { |r| requirement_satisfied_by?(r, activated, possibility) }
+ activated.rewind_to(:swap)
+
+ satisfied
+ end
+
+ # Filter's a state's possibilities to remove any that would (eventually)
+ # create a requirement in the conflict we've just rewound from
+ # @param [UnwindDetails] unwind_details details of the conflict just unwound from
+ # @return [void]
+ def filter_possibilities_for_parent_unwind(unwind_details)
+ unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
+ unwinds_to_state << unwind_details
+
+ primary_unwinds = unwinds_to_state.select(&:unwinding_to_primary_requirement?).uniq
+ parent_unwinds = unwinds_to_state.uniq - primary_unwinds
+
+ allowed_possibility_sets = primary_unwinds.flat_map do |unwind|
+ states[unwind.state_index].possibilities.select do |possibility_set|
+ possibility_set.possibilities.any? do |poss|
+ possibility_satisfies_requirements?(poss, unwind.conflicting_requirements)
+ end
+ end
+ end
+
+ requirements_to_avoid = parent_unwinds.flat_map(&:sub_dependencies_to_avoid)
+
+ state.possibilities.reject! do |possibility_set|
+ !allowed_possibility_sets.include?(possibility_set) &&
+ (requirements_to_avoid - possibility_set.dependencies).empty?
+ end
+ end
+
+ # @param [Conflict] conflict
+ # @return [Array] minimal array of requirements that would cause the passed
+ # conflict to occur.
+ def binding_requirements_for_conflict(conflict)
+ return [conflict.requirement] if conflict.possibility.nil?
+
+ possible_binding_requirements = conflict.requirements.values.flatten(1).uniq
+
+ # When there's a `CircularDependency` error the conflicting requirement
+ # (the one causing the circular) won't be `conflict.requirement`
+ # (which won't be for the right state, because we won't have created it,
+ # because it's circular).
+ # We need to make sure we have that requirement in the conflict's list,
+ # otherwise we won't be able to unwind properly, so we just return all
+ # the requirements for the conflict.
+ return possible_binding_requirements if conflict.underlying_error
+
+ possibilities = search_for(conflict.requirement)
+
+ # If all the requirements together don't filter out all possibilities,
+ # then the only two requirements we need to consider are the initial one
+ # (where the dependency's version was first chosen) and the last
+ if binding_requirement_in_set?(nil, possible_binding_requirements, possibilities)
+ return [conflict.requirement, requirement_for_existing_name(name_for(conflict.requirement))].compact
+ end
+
+ # Loop through the possible binding requirements, removing each one
+ # that doesn't bind. Use a `reverse_each` as we want the earliest set of
+ # binding requirements, and don't use `reject!` as we wish to refine the
+ # array *on each iteration*.
+ binding_requirements = possible_binding_requirements.dup
+ possible_binding_requirements.reverse_each do |req|
+ next if req == conflict.requirement
+ unless binding_requirement_in_set?(req, binding_requirements, possibilities)
+ binding_requirements -= [req]
+ end
+ end
+
+ binding_requirements
+ end
+
+ # @param [Object] requirement we wish to check
+ # @param [Array] possible_binding_requirements array of requirements
+ # @param [Array] possibilities array of possibilities the requirements will be used to filter
+ # @return [Boolean] whether or not the given requirement is required to filter
+ # out all elements of the array of possibilities.
+ def binding_requirement_in_set?(requirement, possible_binding_requirements, possibilities)
+ possibilities.any? do |poss|
+ possibility_satisfies_requirements?(poss, possible_binding_requirements - [requirement])
+ end
+ end
+
+ # @param [Object] requirement
+ # @return [Object] the requirement that led to `requirement` being added
+ # to the list of requirements.
+ def parent_of(requirement)
+ return unless requirement
+ return unless index = @parents_of[requirement].last
+ return unless parent_state = @states[index]
+ parent_state.requirement
+ end
+
+ # @param [String] name
+ # @return [Object] the requirement that led to a version of a possibility
+ # with the given name being activated.
+ def requirement_for_existing_name(name)
+ return nil unless vertex = activated.vertex_named(name)
+ return nil unless vertex.payload
+ states.find { |s| s.name == name }.requirement
+ end
+
+ # @param [Object] requirement
+ # @return [ResolutionState] the state whose `requirement` is the given
+ # `requirement`.
+ def find_state_for(requirement)
+ return nil unless requirement
+ states.find { |i| requirement == i.requirement }
+ end
+
+ # @param [Object] underlying_error
+ # @return [Conflict] a {Conflict} that reflects the failure to activate
+ # the {#possibility} in conjunction with the current {#state}
+ def create_conflict(underlying_error = nil)
+ vertex = activated.vertex_named(name)
+ locked_requirement = locked_requirement_named(name)
+
+ requirements = {}
+ unless vertex.explicit_requirements.empty?
+ requirements[name_for_explicit_dependency_source] = vertex.explicit_requirements
+ end
+ requirements[name_for_locking_dependency_source] = [locked_requirement] if locked_requirement
+ vertex.incoming_edges.each do |edge|
+ (requirements[edge.origin.payload.latest_version] ||= []).unshift(edge.requirement)
+ end
+
+ activated_by_name = {}
+ activated.each { |v| activated_by_name[v.name] = v.payload.latest_version if v.payload }
+ conflicts[name] = Conflict.new(
+ requirement,
+ requirements,
+ vertex.payload && vertex.payload.latest_version,
+ possibility,
+ locked_requirement,
+ requirement_trees,
+ activated_by_name,
+ underlying_error
+ )
+ end
+
+ # @return [Array<Array<Object>>] The different requirement
+ # trees that led to every requirement for the current spec.
+ def requirement_trees
+ vertex = activated.vertex_named(name)
+ vertex.requirements.map { |r| requirement_tree_for(r) }
+ end
+
+ # @param [Object] requirement
+ # @return [Array<Object>] the list of requirements that led to
+ # `requirement` being required.
+ def requirement_tree_for(requirement)
+ tree = []
+ while requirement
+ tree.unshift(requirement)
+ requirement = parent_of(requirement)
+ end
+ tree
+ end
+
+ # Indicates progress roughly once every second
+ # @return [void]
+ def indicate_progress
+ @iteration_counter += 1
+ @progress_rate ||= resolver_ui.progress_rate
+ if iteration_rate.nil?
+ if Time.now - started_at >= @progress_rate
+ self.iteration_rate = @iteration_counter
+ end
+ end
+
+ if iteration_rate && (@iteration_counter % iteration_rate) == 0
+ resolver_ui.indicate_progress
+ end
+ end
+
+ # Calls the {#resolver_ui}'s {UI#debug} method
+ # @param [Integer] depth the depth of the {#states} stack
+ # @param [Proc] block a block that yields a {#to_s}
+ # @return [void]
+ def debug(depth = 0, &block)
+ resolver_ui.debug(depth, &block)
+ end
+
+ # Attempts to activate the current {#possibility}
+ # @return [void]
+ def attempt_to_activate
+ debug(depth) { 'Attempting to activate ' + possibility.to_s }
+ existing_vertex = activated.vertex_named(name)
+ if existing_vertex.payload
+ debug(depth) { "Found existing spec (#{existing_vertex.payload})" }
+ attempt_to_filter_existing_spec(existing_vertex)
+ else
+ latest = possibility.latest_version
+ possibility.possibilities.select! do |possibility|
+ requirement_satisfied_by?(requirement, activated, possibility)
+ end
+ if possibility.latest_version.nil?
+ # ensure there's a possibility for better error messages
+ possibility.possibilities << latest if latest
+ create_conflict
+ unwind_for_conflict
+ else
+ activate_new_spec
+ end
+ end
+ end
+
+ # Attempts to update the existing vertex's `PossibilitySet` with a filtered version
+ # @return [void]
+ def attempt_to_filter_existing_spec(vertex)
+ filtered_set = filtered_possibility_set(vertex)
+ if !filtered_set.possibilities.empty?
+ activated.set_payload(name, filtered_set)
+ new_requirements = requirements.dup
+ push_state_for_requirements(new_requirements, false)
+ else
+ create_conflict
+ debug(depth) { "Unsatisfied by existing spec (#{vertex.payload})" }
+ unwind_for_conflict
+ end
+ end
+
+ # Generates a filtered version of the existing vertex's `PossibilitySet` using the
+ # current state's `requirement`
+ # @param [Object] vertex existing vertex
+ # @return [PossibilitySet] filtered possibility set
+ def filtered_possibility_set(vertex)
+ PossibilitySet.new(vertex.payload.dependencies, vertex.payload.possibilities & possibility.possibilities)
+ end
+
+ # @param [String] requirement_name the spec name to search for
+ # @return [Object] the locked spec named `requirement_name`, if one
+ # is found on {#base}
+ def locked_requirement_named(requirement_name)
+ vertex = base.vertex_named(requirement_name)
+ vertex && vertex.payload
+ end
+
+ # Add the current {#possibility} to the dependency graph of the current
+ # {#state}
+ # @return [void]
+ def activate_new_spec
+ conflicts.delete(name)
+ debug(depth) { "Activated #{name} at #{possibility}" }
+ activated.set_payload(name, possibility)
+ require_nested_dependencies_for(possibility)
+ end
+
+ # Requires the dependencies that the recently activated spec has
+ # @param [Object] possibility_set the PossibilitySet that has just been
+ # activated
+ # @return [void]
+ def require_nested_dependencies_for(possibility_set)
+ nested_dependencies = dependencies_for(possibility_set.latest_version)
+ debug(depth) { "Requiring nested dependencies (#{nested_dependencies.join(', ')})" }
+ nested_dependencies.each do |d|
+ activated.add_child_vertex(name_for(d), nil, [name_for(possibility_set.latest_version)], d)
+ parent_index = states.size - 1
+ parents = @parents_of[d]
+ parents << parent_index if parents.empty?
+ end
+
+ push_state_for_requirements(requirements + nested_dependencies, !nested_dependencies.empty?)
+ end
+
+ # Pushes a new {DependencyState} that encapsulates both existing and new
+ # requirements
+ # @param [Array] new_requirements
+ # @param [Boolean] requires_sort
+ # @param [Object] new_activated
+ # @return [void]
+ def push_state_for_requirements(new_requirements, requires_sort = true, new_activated = activated)
+ new_requirements = sort_dependencies(new_requirements.uniq, new_activated, conflicts) if requires_sort
+ new_requirement = nil
+ loop do
+ new_requirement = new_requirements.shift
+ break if new_requirement.nil? || states.none? { |s| s.requirement == new_requirement }
+ end
+ new_name = new_requirement ? name_for(new_requirement) : ''.freeze
+ possibilities = possibilities_for_requirement(new_requirement)
+ handle_missing_or_push_dependency_state DependencyState.new(
+ new_name, new_requirements, new_activated,
+ new_requirement, possibilities, depth, conflicts.dup, unused_unwind_options.dup
+ )
+ end
+
+ # Checks a proposed requirement with any existing locked requirement
+ # before generating an array of possibilities for it.
+ # @param [Object] requirement the proposed requirement
+ # @param [Object] activated
+ # @return [Array] possibilities
+ def possibilities_for_requirement(requirement, activated = self.activated)
+ return [] unless requirement
+ if locked_requirement_named(name_for(requirement))
+ return locked_requirement_possibility_set(requirement, activated)
+ end
+
+ group_possibilities(search_for(requirement))
+ end
+
+ # @param [Object] requirement the proposed requirement
+ # @param [Object] activated
+ # @return [Array] possibility set containing only the locked requirement, if any
+ def locked_requirement_possibility_set(requirement, activated = self.activated)
+ all_possibilities = search_for(requirement)
+ locked_requirement = locked_requirement_named(name_for(requirement))
+
+ # Longwinded way to build a possibilities array with either the locked
+ # requirement or nothing in it. Required, since the API for
+ # locked_requirement isn't guaranteed.
+ locked_possibilities = all_possibilities.select do |possibility|
+ requirement_satisfied_by?(locked_requirement, activated, possibility)
+ end
+
+ group_possibilities(locked_possibilities)
+ end
+
+ # Build an array of PossibilitySets, with each element representing a group of
+ # dependency versions that all have the same sub-dependency version constraints
+ # and are contiguous.
+ # @param [Array] possibilities an array of possibilities
+ # @return [Array<PossibilitySet>] an array of possibility sets
+ def group_possibilities(possibilities)
+ possibility_sets = []
+ current_possibility_set = nil
+
+ possibilities.reverse_each do |possibility|
+ dependencies = dependencies_for(possibility)
+ if current_possibility_set && dependencies_equal?(current_possibility_set.dependencies, dependencies)
+ current_possibility_set.possibilities.unshift(possibility)
+ else
+ possibility_sets.unshift(PossibilitySet.new(dependencies, [possibility]))
+ current_possibility_set = possibility_sets.first
+ end
+ end
+
+ possibility_sets
+ end
+
+ # Pushes a new {DependencyState}.
+ # If the {#specification_provider} says to
+ # {SpecificationProvider#allow_missing?} that particular requirement, and
+ # there are no possibilities for that requirement, then `state` is not
+ # pushed, and the vertex in {#activated} is removed, and we continue
+ # resolving the remaining requirements.
+ # @param [DependencyState] state
+ # @return [void]
+ def handle_missing_or_push_dependency_state(state)
+ if state.requirement && state.possibilities.empty? && allow_missing?(state.requirement)
+ state.activated.detach_vertex_named(state.name)
+ push_state_for_requirements(state.requirements.dup, false, state.activated)
+ else
+ states.push(state).tap { activated.tag(state) }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/vendor/molinillo/lib/molinillo/resolver.rb b/lib/rubygems/vendor/molinillo/lib/molinillo/resolver.rb
new file mode 100644
index 0000000000..86229c3fa1
--- /dev/null
+++ b/lib/rubygems/vendor/molinillo/lib/molinillo/resolver.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require_relative 'dependency_graph'
+
+module Gem::Molinillo
+ # This class encapsulates a dependency resolver.
+ # The resolver is responsible for determining which set of dependencies to
+ # activate, with feedback from the {#specification_provider}
+ #
+ #
+ class Resolver
+ require_relative 'resolution'
+
+ # @return [SpecificationProvider] the specification provider used
+ # in the resolution process
+ attr_reader :specification_provider
+
+ # @return [UI] the UI module used to communicate back to the user
+ # during the resolution process
+ attr_reader :resolver_ui
+
+ # Initializes a new resolver.
+ # @param [SpecificationProvider] specification_provider
+ # see {#specification_provider}
+ # @param [UI] resolver_ui
+ # see {#resolver_ui}
+ def initialize(specification_provider, resolver_ui)
+ @specification_provider = specification_provider
+ @resolver_ui = resolver_ui
+ end
+
+ # Resolves the requested dependencies into a {DependencyGraph},
+ # locking to the base dependency graph (if specified)
+ # @param [Array] requested an array of 'requested' dependencies that the
+ # {#specification_provider} can understand
+ # @param [DependencyGraph,nil] base the base dependency graph to which
+ # dependencies should be 'locked'
+ def resolve(requested, base = DependencyGraph.new)
+ Resolution.new(specification_provider,
+ resolver_ui,
+ requested,
+ base).
+ resolve
+ end
+ end
+end
diff --git a/lib/rubygems/vendor/molinillo/lib/molinillo/state.rb b/lib/rubygems/vendor/molinillo/lib/molinillo/state.rb
new file mode 100644
index 0000000000..c48ec6af9c
--- /dev/null
+++ b/lib/rubygems/vendor/molinillo/lib/molinillo/state.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+module Gem::Molinillo
+ # A state that a {Resolution} can be in
+ # @attr [String] name the name of the current requirement
+ # @attr [Array<Object>] requirements currently unsatisfied requirements
+ # @attr [DependencyGraph] activated the graph of activated dependencies
+ # @attr [Object] requirement the current requirement
+ # @attr [Object] possibilities the possibilities to satisfy the current requirement
+ # @attr [Integer] depth the depth of the resolution
+ # @attr [Hash] conflicts unresolved conflicts, indexed by dependency name
+ # @attr [Array<UnwindDetails>] unused_unwind_options unwinds for previous conflicts that weren't explored
+ ResolutionState = Struct.new(
+ :name,
+ :requirements,
+ :activated,
+ :requirement,
+ :possibilities,
+ :depth,
+ :conflicts,
+ :unused_unwind_options
+ )
+
+ class ResolutionState
+ # Returns an empty resolution state
+ # @return [ResolutionState] an empty state
+ def self.empty
+ new(nil, [], DependencyGraph.new, nil, nil, 0, {}, [])
+ end
+ end
+
+ # A state that encapsulates a set of {#requirements} with an {Array} of
+ # possibilities
+ class DependencyState < ResolutionState
+ # Removes a possibility from `self`
+ # @return [PossibilityState] a state with a single possibility,
+ # the possibility that was removed from `self`
+ def pop_possibility_state
+ PossibilityState.new(
+ name,
+ requirements.dup,
+ activated,
+ requirement,
+ [possibilities.pop],
+ depth + 1,
+ conflicts.dup,
+ unused_unwind_options.dup
+ ).tap do |state|
+ state.activated.tag(state)
+ end
+ end
+ end
+
+ # A state that encapsulates a single possibility to fulfill the given
+ # {#requirement}
+ class PossibilityState < ResolutionState
+ end
+end
diff --git a/lib/rubygems/vendor/net-http/.document b/lib/rubygems/vendor/net-http/.document
new file mode 100644
index 0000000000..0c43bbd6b3
--- /dev/null
+++ b/lib/rubygems/vendor/net-http/.document
@@ -0,0 +1 @@
+# Vendored files do not need to be documented
diff --git a/lib/rubygems/vendor/net-http/lib/net/http.rb b/lib/rubygems/vendor/net-http/lib/net/http.rb
new file mode 100644
index 0000000000..7b15c3cf54
--- /dev/null
+++ b/lib/rubygems/vendor/net-http/lib/net/http.rb
@@ -0,0 +1,2496 @@
+# frozen_string_literal: true
+#
+# = net/http.rb
+#
+# Copyright (c) 1999-2007 Yukihiro Matsumoto
+# Copyright (c) 1999-2007 Minero Aoki
+# Copyright (c) 2001 GOTOU Yuuzou
+#
+# Written and maintained by Minero Aoki <aamine@loveruby.net>.
+# HTTPS support added by GOTOU Yuuzou <gotoyuzo@notwork.org>.
+#
+# This file is derived from "http-access.rb".
+#
+# Documented by Minero Aoki; converted to RDoc by William Webber.
+#
+# This program is free software. You can re-distribute and/or
+# modify this program under the same terms of ruby itself ---
+# Ruby Distribution License or GNU General Public License.
+#
+# See Gem::Net::HTTP for an overview and examples.
+#
+
+require_relative '../../../net-protocol/lib/net/protocol'
+require_relative '../../../uri/lib/uri'
+require_relative '../../../resolv/lib/resolv'
+autoload :OpenSSL, 'openssl'
+
+module Gem::Net #:nodoc:
+
+ # :stopdoc:
+ class HTTPBadResponse < StandardError; end
+ class HTTPHeaderSyntaxError < StandardError; end
+ # :startdoc:
+
+ # \Class \Gem::Net::HTTP provides a rich library that implements the client
+ # in a client-server model that uses the \HTTP request-response protocol.
+ # For information about \HTTP, see:
+ #
+ # - {Hypertext Transfer Protocol}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol].
+ # - {Technical overview}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Technical_overview].
+ #
+ # == About the Examples
+ #
+ # :include: doc/net-http/examples.rdoc
+ #
+ # == Strategies
+ #
+ # - If you will make only a few GET requests,
+ # consider using {OpenURI}[rdoc-ref:OpenURI].
+ # - If you will make only a few requests of all kinds,
+ # consider using the various singleton convenience methods in this class.
+ # Each of the following methods automatically starts and finishes
+ # a {session}[rdoc-ref:Gem::Net::HTTP@Sessions] that sends a single request:
+ #
+ # # Return string response body.
+ # Gem::Net::HTTP.get(hostname, path)
+ # Gem::Net::HTTP.get(uri)
+ #
+ # # Write string response body to $stdout.
+ # Gem::Net::HTTP.get_print(hostname, path)
+ # Gem::Net::HTTP.get_print(uri)
+ #
+ # # Return response as Gem::Net::HTTPResponse object.
+ # Gem::Net::HTTP.get_response(hostname, path)
+ # Gem::Net::HTTP.get_response(uri)
+ # data = '{"title": "foo", "body": "bar", "userId": 1}'
+ # Gem::Net::HTTP.post(uri, data)
+ # params = {title: 'foo', body: 'bar', userId: 1}
+ # Gem::Net::HTTP.post_form(uri, params)
+ #
+ # - If performance is important, consider using sessions, which lower request overhead.
+ # This {session}[rdoc-ref:Gem::Net::HTTP@Sessions] has multiple requests for
+ # {HTTP methods}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods]
+ # and {WebDAV methods}[https://en.wikipedia.org/wiki/WebDAV#Implementation]:
+ #
+ # Gem::Net::HTTP.start(hostname) do |http|
+ # # Session started automatically before block execution.
+ # http.get(path)
+ # http.head(path)
+ # body = 'Some text'
+ # http.post(path, body) # Can also have a block.
+ # http.put(path, body)
+ # http.delete(path)
+ # http.options(path)
+ # http.trace(path)
+ # http.patch(path, body) # Can also have a block.
+ # http.copy(path)
+ # http.lock(path, body)
+ # http.mkcol(path, body)
+ # http.move(path)
+ # http.propfind(path, body)
+ # http.proppatch(path, body)
+ # http.unlock(path, body)
+ # # Session finished automatically at block exit.
+ # end
+ #
+ # The methods cited above are convenience methods that, via their few arguments,
+ # allow minimal control over the requests.
+ # For greater control, consider using {request objects}[rdoc-ref:Gem::Net::HTTPRequest].
+ #
+ # == URIs
+ #
+ # On the internet, a URI
+ # ({Universal Resource Identifier}[https://en.wikipedia.org/wiki/Uniform_Resource_Identifier])
+ # is a string that identifies a particular resource.
+ # It consists of some or all of: scheme, hostname, path, query, and fragment;
+ # see {URI syntax}[https://en.wikipedia.org/wiki/Uniform_Resource_Identifier#Syntax].
+ #
+ # A Ruby {Gem::URI::Generic}[rdoc-ref:Gem::URI::Generic] object
+ # represents an internet URI.
+ # It provides, among others, methods
+ # +scheme+, +hostname+, +path+, +query+, and +fragment+.
+ #
+ # === Schemes
+ #
+ # An internet \Gem::URI has
+ # a {scheme}[https://en.wikipedia.org/wiki/List_of_URI_schemes].
+ #
+ # The two schemes supported in \Gem::Net::HTTP are <tt>'https'</tt> and <tt>'http'</tt>:
+ #
+ # uri.scheme # => "https"
+ # Gem::URI('http://example.com').scheme # => "http"
+ #
+ # === Hostnames
+ #
+ # A hostname identifies a server (host) to which requests may be sent:
+ #
+ # hostname = uri.hostname # => "jsonplaceholder.typicode.com"
+ # Gem::Net::HTTP.start(hostname) do |http|
+ # # Some HTTP stuff.
+ # end
+ #
+ # === Paths
+ #
+ # A host-specific path identifies a resource on the host:
+ #
+ # _uri = uri.dup
+ # _uri.path = '/todos/1'
+ # hostname = _uri.hostname
+ # path = _uri.path
+ # Gem::Net::HTTP.get(hostname, path)
+ #
+ # === Queries
+ #
+ # A host-specific query adds name/value pairs to the URI:
+ #
+ # _uri = uri.dup
+ # params = {userId: 1, completed: false}
+ # _uri.query = Gem::URI.encode_www_form(params)
+ # _uri # => #<Gem::URI::HTTPS https://jsonplaceholder.typicode.com?userId=1&completed=false>
+ # Gem::Net::HTTP.get(_uri)
+ #
+ # === Fragments
+ #
+ # A {URI fragment}[https://en.wikipedia.org/wiki/URI_fragment] has no effect
+ # in \Gem::Net::HTTP;
+ # the same data is returned, regardless of whether a fragment is included.
+ #
+ # == Request Headers
+ #
+ # Request headers may be used to pass additional information to the host,
+ # similar to arguments passed in a method call;
+ # each header is a name/value pair.
+ #
+ # Each of the \Gem::Net::HTTP methods that sends a request to the host
+ # has optional argument +headers+,
+ # where the headers are expressed as a hash of field-name/value pairs:
+ #
+ # headers = {Accept: 'application/json', Connection: 'Keep-Alive'}
+ # Gem::Net::HTTP.get(uri, headers)
+ #
+ # See lists of both standard request fields and common request fields at
+ # {Request Fields}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Request_fields].
+ # A host may also accept other custom fields.
+ #
+ # == \HTTP Sessions
+ #
+ # A _session_ is a connection between a server (host) and a client that:
+ #
+ # - Is begun by instance method Gem::Net::HTTP#start.
+ # - May contain any number of requests.
+ # - Is ended by instance method Gem::Net::HTTP#finish.
+ #
+ # See example sessions at {Strategies}[rdoc-ref:Gem::Net::HTTP@Strategies].
+ #
+ # === Session Using \Gem::Net::HTTP.start
+ #
+ # If you have many requests to make to a single host (and port),
+ # consider using singleton method Gem::Net::HTTP.start with a block;
+ # the method handles the session automatically by:
+ #
+ # - Calling #start before block execution.
+ # - Executing the block.
+ # - Calling #finish after block execution.
+ #
+ # In the block, you can use these instance methods,
+ # each of which that sends a single request:
+ #
+ # - {HTTP methods}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods]:
+ #
+ # - #get, #request_get: GET.
+ # - #head, #request_head: HEAD.
+ # - #post, #request_post: POST.
+ # - #delete: DELETE.
+ # - #options: OPTIONS.
+ # - #trace: TRACE.
+ # - #patch: PATCH.
+ #
+ # - {WebDAV methods}[https://en.wikipedia.org/wiki/WebDAV#Implementation]:
+ #
+ # - #copy: COPY.
+ # - #lock: LOCK.
+ # - #mkcol: MKCOL.
+ # - #move: MOVE.
+ # - #propfind: PROPFIND.
+ # - #proppatch: PROPPATCH.
+ # - #unlock: UNLOCK.
+ #
+ # === Session Using \Gem::Net::HTTP.start and \Gem::Net::HTTP.finish
+ #
+ # You can manage a session manually using methods #start and #finish:
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.start
+ # http.get('/todos/1')
+ # http.get('/todos/2')
+ # http.delete('/posts/1')
+ # http.finish # Needed to free resources.
+ #
+ # === Single-Request Session
+ #
+ # Certain convenience methods automatically handle a session by:
+ #
+ # - Creating an \HTTP object
+ # - Starting a session.
+ # - Sending a single request.
+ # - Finishing the session.
+ # - Destroying the object.
+ #
+ # Such methods that send GET requests:
+ #
+ # - ::get: Returns the string response body.
+ # - ::get_print: Writes the string response body to $stdout.
+ # - ::get_response: Returns a Gem::Net::HTTPResponse object.
+ #
+ # Such methods that send POST requests:
+ #
+ # - ::post: Posts data to the host.
+ # - ::post_form: Posts form data to the host.
+ #
+ # == \HTTP Requests and Responses
+ #
+ # Many of the methods above are convenience methods,
+ # each of which sends a request and returns a string
+ # without directly using \Gem::Net::HTTPRequest and \Gem::Net::HTTPResponse objects.
+ #
+ # You can, however, directly create a request object, send the request,
+ # and retrieve the response object; see:
+ #
+ # - Gem::Net::HTTPRequest.
+ # - Gem::Net::HTTPResponse.
+ #
+ # == Following Redirection
+ #
+ # Each returned response is an instance of a subclass of Gem::Net::HTTPResponse.
+ # See the {response class hierarchy}[rdoc-ref:Gem::Net::HTTPResponse@Response+Subclasses].
+ #
+ # In particular, class Gem::Net::HTTPRedirection is the parent
+ # of all redirection classes.
+ # This allows you to craft a case statement to handle redirections properly:
+ #
+ # def fetch(uri, limit = 10)
+ # # You should choose a better exception.
+ # raise ArgumentError, 'Too many HTTP redirects' if limit == 0
+ #
+ # res = Gem::Net::HTTP.get_response(Gem::URI(uri))
+ # case res
+ # when Gem::Net::HTTPSuccess # Any success class.
+ # res
+ # when Gem::Net::HTTPRedirection # Any redirection class.
+ # location = res['Location']
+ # warn "Redirected to #{location}"
+ # fetch(location, limit - 1)
+ # else # Any other class.
+ # res.value
+ # end
+ # end
+ #
+ # fetch(uri)
+ #
+ # == Basic Authentication
+ #
+ # Basic authentication is performed according to
+ # {RFC2617}[http://www.ietf.org/rfc/rfc2617.txt]:
+ #
+ # req = Gem::Net::HTTP::Get.new(uri)
+ # req.basic_auth('user', 'pass')
+ # res = Gem::Net::HTTP.start(hostname) do |http|
+ # http.request(req)
+ # end
+ #
+ # == Streaming Response Bodies
+ #
+ # By default \Gem::Net::HTTP reads an entire response into memory. If you are
+ # handling large files or wish to implement a progress bar you can instead
+ # stream the body directly to an IO.
+ #
+ # Gem::Net::HTTP.start(hostname) do |http|
+ # req = Gem::Net::HTTP::Get.new(uri)
+ # http.request(req) do |res|
+ # open('t.tmp', 'w') do |f|
+ # res.read_body do |chunk|
+ # f.write chunk
+ # end
+ # end
+ # end
+ # end
+ #
+ # == HTTPS
+ #
+ # HTTPS is enabled for an \HTTP connection by Gem::Net::HTTP#use_ssl=:
+ #
+ # Gem::Net::HTTP.start(hostname, :use_ssl => true) do |http|
+ # req = Gem::Net::HTTP::Get.new(uri)
+ # res = http.request(req)
+ # end
+ #
+ # Or if you simply want to make a GET request, you may pass in a URI
+ # object that has an \HTTPS URL. \Gem::Net::HTTP automatically turns on TLS
+ # verification if the URI object has a 'https' :URI scheme:
+ #
+ # uri # => #<Gem::URI::HTTPS https://jsonplaceholder.typicode.com/>
+ # Gem::Net::HTTP.get(uri)
+ #
+ # == Proxy Server
+ #
+ # An \HTTP object can have
+ # a {proxy server}[https://en.wikipedia.org/wiki/Proxy_server].
+ #
+ # You can create an \HTTP object with a proxy server
+ # using method Gem::Net::HTTP.new or method Gem::Net::HTTP.start.
+ #
+ # The proxy may be defined either by argument +p_addr+
+ # or by environment variable <tt>'http_proxy'</tt>.
+ #
+ # === Proxy Using Argument +p_addr+ as a \String
+ #
+ # When argument +p_addr+ is a string hostname,
+ # the returned +http+ has the given host as its proxy:
+ #
+ # http = Gem::Net::HTTP.new(hostname, nil, 'proxy.example')
+ # http.proxy? # => true
+ # http.proxy_from_env? # => false
+ # http.proxy_address # => "proxy.example"
+ # # These use default values.
+ # http.proxy_port # => 80
+ # http.proxy_user # => nil
+ # http.proxy_pass # => nil
+ #
+ # The port, username, and password for the proxy may also be given:
+ #
+ # http = Gem::Net::HTTP.new(hostname, nil, 'proxy.example', 8000, 'pname', 'ppass')
+ # # => #<Gem::Net::HTTP jsonplaceholder.typicode.com:80 open=false>
+ # http.proxy? # => true
+ # http.proxy_from_env? # => false
+ # http.proxy_address # => "proxy.example"
+ # http.proxy_port # => 8000
+ # http.proxy_user # => "pname"
+ # http.proxy_pass # => "ppass"
+ #
+ # === Proxy Using '<tt>ENV['http_proxy']</tt>'
+ #
+ # When environment variable <tt>'http_proxy'</tt>
+ # is set to a \Gem::URI string,
+ # the returned +http+ will have the server at that URI as its proxy;
+ # note that the \Gem::URI string must have a protocol
+ # such as <tt>'http'</tt> or <tt>'https'</tt>:
+ #
+ # ENV['http_proxy'] = 'http://example.com'
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.proxy? # => true
+ # http.proxy_from_env? # => true
+ # http.proxy_address # => "example.com"
+ # # These use default values.
+ # http.proxy_port # => 80
+ # http.proxy_user # => nil
+ # http.proxy_pass # => nil
+ #
+ # The \Gem::URI string may include proxy username, password, and port number:
+ #
+ # ENV['http_proxy'] = 'http://pname:ppass@example.com:8000'
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.proxy? # => true
+ # http.proxy_from_env? # => true
+ # http.proxy_address # => "example.com"
+ # http.proxy_port # => 8000
+ # http.proxy_user # => "pname"
+ # http.proxy_pass # => "ppass"
+ #
+ # === Filtering Proxies
+ #
+ # With method Gem::Net::HTTP.new (but not Gem::Net::HTTP.start),
+ # you can use argument +p_no_proxy+ to filter proxies:
+ #
+ # - Reject a certain address:
+ #
+ # http = Gem::Net::HTTP.new('example.com', nil, 'proxy.example', 8000, 'pname', 'ppass', 'proxy.example')
+ # http.proxy_address # => nil
+ #
+ # - Reject certain domains or subdomains:
+ #
+ # http = Gem::Net::HTTP.new('example.com', nil, 'my.proxy.example', 8000, 'pname', 'ppass', 'proxy.example')
+ # http.proxy_address # => nil
+ #
+ # - Reject certain addresses and port combinations:
+ #
+ # http = Gem::Net::HTTP.new('example.com', nil, 'proxy.example', 8000, 'pname', 'ppass', 'proxy.example:1234')
+ # http.proxy_address # => "proxy.example"
+ #
+ # http = Gem::Net::HTTP.new('example.com', nil, 'proxy.example', 8000, 'pname', 'ppass', 'proxy.example:8000')
+ # http.proxy_address # => nil
+ #
+ # - Reject a list of the types above delimited using a comma:
+ #
+ # http = Gem::Net::HTTP.new('example.com', nil, 'proxy.example', 8000, 'pname', 'ppass', 'my.proxy,proxy.example:8000')
+ # http.proxy_address # => nil
+ #
+ # http = Gem::Net::HTTP.new('example.com', nil, 'my.proxy', 8000, 'pname', 'ppass', 'my.proxy,proxy.example:8000')
+ # http.proxy_address # => nil
+ #
+ # == Compression and Decompression
+ #
+ # \Gem::Net::HTTP does not compress the body of a request before sending.
+ #
+ # By default, \Gem::Net::HTTP adds header <tt>'Accept-Encoding'</tt>
+ # to a new {request object}[rdoc-ref:Gem::Net::HTTPRequest]:
+ #
+ # Gem::Net::HTTP::Get.new(uri)['Accept-Encoding']
+ # # => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3"
+ #
+ # This requests the server to zip-encode the response body if there is one;
+ # the server is not required to do so.
+ #
+ # \Gem::Net::HTTP does not automatically decompress a response body
+ # if the response has header <tt>'Content-Range'</tt>.
+ #
+ # Otherwise decompression (or not) depends on the value of header
+ # {Content-Encoding}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-encoding-response-header]:
+ #
+ # - <tt>'deflate'</tt>, <tt>'gzip'</tt>, or <tt>'x-gzip'</tt>:
+ # decompresses the body and deletes the header.
+ # - <tt>'none'</tt> or <tt>'identity'</tt>:
+ # does not decompress the body, but deletes the header.
+ # - Any other value:
+ # leaves the body and header unchanged.
+ #
+ # == What's Here
+ #
+ # This is a categorized summary of methods and attributes.
+ #
+ # === \Gem::Net::HTTP Objects
+ #
+ # - {::new}[rdoc-ref:Gem::Net::HTTP.new]:
+ # Creates a new instance.
+ # - {#inspect}[rdoc-ref:Gem::Net::HTTP#inspect]:
+ # Returns a string representation of +self+.
+ #
+ # === Sessions
+ #
+ # - {::start}[rdoc-ref:Gem::Net::HTTP.start]:
+ # Begins a new session in a new \Gem::Net::HTTP object.
+ # - {#started?}[rdoc-ref:Gem::Net::HTTP#started?]
+ # (aliased as {#active?}[rdoc-ref:Gem::Net::HTTP#active?]):
+ # Returns whether in a session.
+ # - {#finish}[rdoc-ref:Gem::Net::HTTP#finish]:
+ # Ends an active session.
+ # - {#start}[rdoc-ref:Gem::Net::HTTP#start]:
+ # Begins a new session in an existing \Gem::Net::HTTP object (+self+).
+ #
+ # === Connections
+ #
+ # - {:continue_timeout}[rdoc-ref:Gem::Net::HTTP#continue_timeout]:
+ # Returns the continue timeout.
+ # - {#continue_timeout=}[rdoc-ref:Gem::Net::HTTP#continue_timeout=]:
+ # Sets the continue timeout seconds.
+ # - {:keep_alive_timeout}[rdoc-ref:Gem::Net::HTTP#keep_alive_timeout]:
+ # Returns the keep-alive timeout.
+ # - {:keep_alive_timeout=}[rdoc-ref:Gem::Net::HTTP#keep_alive_timeout=]:
+ # Sets the keep-alive timeout.
+ # - {:max_retries}[rdoc-ref:Gem::Net::HTTP#max_retries]:
+ # Returns the maximum retries.
+ # - {#max_retries=}[rdoc-ref:Gem::Net::HTTP#max_retries=]:
+ # Sets the maximum retries.
+ # - {:open_timeout}[rdoc-ref:Gem::Net::HTTP#open_timeout]:
+ # Returns the open timeout.
+ # - {:open_timeout=}[rdoc-ref:Gem::Net::HTTP#open_timeout=]:
+ # Sets the open timeout.
+ # - {:read_timeout}[rdoc-ref:Gem::Net::HTTP#read_timeout]:
+ # Returns the open timeout.
+ # - {:read_timeout=}[rdoc-ref:Gem::Net::HTTP#read_timeout=]:
+ # Sets the read timeout.
+ # - {:ssl_timeout}[rdoc-ref:Gem::Net::HTTP#ssl_timeout]:
+ # Returns the ssl timeout.
+ # - {:ssl_timeout=}[rdoc-ref:Gem::Net::HTTP#ssl_timeout=]:
+ # Sets the ssl timeout.
+ # - {:write_timeout}[rdoc-ref:Gem::Net::HTTP#write_timeout]:
+ # Returns the write timeout.
+ # - {write_timeout=}[rdoc-ref:Gem::Net::HTTP#write_timeout=]:
+ # Sets the write timeout.
+ #
+ # === Requests
+ #
+ # - {::get}[rdoc-ref:Gem::Net::HTTP.get]:
+ # Sends a GET request and returns the string response body.
+ # - {::get_print}[rdoc-ref:Gem::Net::HTTP.get_print]:
+ # Sends a GET request and write the string response body to $stdout.
+ # - {::get_response}[rdoc-ref:Gem::Net::HTTP.get_response]:
+ # Sends a GET request and returns a response object.
+ # - {::post_form}[rdoc-ref:Gem::Net::HTTP.post_form]:
+ # Sends a POST request with form data and returns a response object.
+ # - {::post}[rdoc-ref:Gem::Net::HTTP.post]:
+ # Sends a POST request with data and returns a response object.
+ # - {#copy}[rdoc-ref:Gem::Net::HTTP#copy]:
+ # Sends a COPY request and returns a response object.
+ # - {#delete}[rdoc-ref:Gem::Net::HTTP#delete]:
+ # Sends a DELETE request and returns a response object.
+ # - {#get}[rdoc-ref:Gem::Net::HTTP#get]:
+ # Sends a GET request and returns a response object.
+ # - {#head}[rdoc-ref:Gem::Net::HTTP#head]:
+ # Sends a HEAD request and returns a response object.
+ # - {#lock}[rdoc-ref:Gem::Net::HTTP#lock]:
+ # Sends a LOCK request and returns a response object.
+ # - {#mkcol}[rdoc-ref:Gem::Net::HTTP#mkcol]:
+ # Sends a MKCOL request and returns a response object.
+ # - {#move}[rdoc-ref:Gem::Net::HTTP#move]:
+ # Sends a MOVE request and returns a response object.
+ # - {#options}[rdoc-ref:Gem::Net::HTTP#options]:
+ # Sends a OPTIONS request and returns a response object.
+ # - {#patch}[rdoc-ref:Gem::Net::HTTP#patch]:
+ # Sends a PATCH request and returns a response object.
+ # - {#post}[rdoc-ref:Gem::Net::HTTP#post]:
+ # Sends a POST request and returns a response object.
+ # - {#propfind}[rdoc-ref:Gem::Net::HTTP#propfind]:
+ # Sends a PROPFIND request and returns a response object.
+ # - {#proppatch}[rdoc-ref:Gem::Net::HTTP#proppatch]:
+ # Sends a PROPPATCH request and returns a response object.
+ # - {#put}[rdoc-ref:Gem::Net::HTTP#put]:
+ # Sends a PUT request and returns a response object.
+ # - {#request}[rdoc-ref:Gem::Net::HTTP#request]:
+ # Sends a request and returns a response object.
+ # - {#request_get}[rdoc-ref:Gem::Net::HTTP#request_get]
+ # (aliased as {#get2}[rdoc-ref:Gem::Net::HTTP#get2]):
+ # Sends a GET request and forms a response object;
+ # if a block given, calls the block with the object,
+ # otherwise returns the object.
+ # - {#request_head}[rdoc-ref:Gem::Net::HTTP#request_head]
+ # (aliased as {#head2}[rdoc-ref:Gem::Net::HTTP#head2]):
+ # Sends a HEAD request and forms a response object;
+ # if a block given, calls the block with the object,
+ # otherwise returns the object.
+ # - {#request_post}[rdoc-ref:Gem::Net::HTTP#request_post]
+ # (aliased as {#post2}[rdoc-ref:Gem::Net::HTTP#post2]):
+ # Sends a POST request and forms a response object;
+ # if a block given, calls the block with the object,
+ # otherwise returns the object.
+ # - {#send_request}[rdoc-ref:Gem::Net::HTTP#send_request]:
+ # Sends a request and returns a response object.
+ # - {#trace}[rdoc-ref:Gem::Net::HTTP#trace]:
+ # Sends a TRACE request and returns a response object.
+ # - {#unlock}[rdoc-ref:Gem::Net::HTTP#unlock]:
+ # Sends an UNLOCK request and returns a response object.
+ #
+ # === Responses
+ #
+ # - {:close_on_empty_response}[rdoc-ref:Gem::Net::HTTP#close_on_empty_response]:
+ # Returns whether to close connection on empty response.
+ # - {:close_on_empty_response=}[rdoc-ref:Gem::Net::HTTP#close_on_empty_response=]:
+ # Sets whether to close connection on empty response.
+ # - {:ignore_eof}[rdoc-ref:Gem::Net::HTTP#ignore_eof]:
+ # Returns whether to ignore end-of-file when reading a response body
+ # with <tt>Content-Length</tt> headers.
+ # - {:ignore_eof=}[rdoc-ref:Gem::Net::HTTP#ignore_eof=]:
+ # Sets whether to ignore end-of-file when reading a response body
+ # with <tt>Content-Length</tt> headers.
+ # - {:response_body_encoding}[rdoc-ref:Gem::Net::HTTP#response_body_encoding]:
+ # Returns the encoding to use for the response body.
+ # - {#response_body_encoding=}[rdoc-ref:Gem::Net::HTTP#response_body_encoding=]:
+ # Sets the response body encoding.
+ #
+ # === Proxies
+ #
+ # - {:proxy_address}[rdoc-ref:Gem::Net::HTTP#proxy_address]:
+ # Returns the proxy address.
+ # - {:proxy_address=}[rdoc-ref:Gem::Net::HTTP#proxy_address=]:
+ # Sets the proxy address.
+ # - {::proxy_class?}[rdoc-ref:Gem::Net::HTTP.proxy_class?]:
+ # Returns whether +self+ is a proxy class.
+ # - {#proxy?}[rdoc-ref:Gem::Net::HTTP#proxy?]:
+ # Returns whether +self+ has a proxy.
+ # - {#proxy_address}[rdoc-ref:Gem::Net::HTTP#proxy_address]
+ # (aliased as {#proxyaddr}[rdoc-ref:Gem::Net::HTTP#proxyaddr]):
+ # Returns the proxy address.
+ # - {#proxy_from_env?}[rdoc-ref:Gem::Net::HTTP#proxy_from_env?]:
+ # Returns whether the proxy is taken from an environment variable.
+ # - {:proxy_from_env=}[rdoc-ref:Gem::Net::HTTP#proxy_from_env=]:
+ # Sets whether the proxy is to be taken from an environment variable.
+ # - {:proxy_pass}[rdoc-ref:Gem::Net::HTTP#proxy_pass]:
+ # Returns the proxy password.
+ # - {:proxy_pass=}[rdoc-ref:Gem::Net::HTTP#proxy_pass=]:
+ # Sets the proxy password.
+ # - {:proxy_port}[rdoc-ref:Gem::Net::HTTP#proxy_port]:
+ # Returns the proxy port.
+ # - {:proxy_port=}[rdoc-ref:Gem::Net::HTTP#proxy_port=]:
+ # Sets the proxy port.
+ # - {#proxy_user}[rdoc-ref:Gem::Net::HTTP#proxy_user]:
+ # Returns the proxy user name.
+ # - {:proxy_user=}[rdoc-ref:Gem::Net::HTTP#proxy_user=]:
+ # Sets the proxy user.
+ #
+ # === Security
+ #
+ # - {:ca_file}[rdoc-ref:Gem::Net::HTTP#ca_file]:
+ # Returns the path to a CA certification file.
+ # - {:ca_file=}[rdoc-ref:Gem::Net::HTTP#ca_file=]:
+ # Sets the path to a CA certification file.
+ # - {:ca_path}[rdoc-ref:Gem::Net::HTTP#ca_path]:
+ # Returns the path of to CA directory containing certification files.
+ # - {:ca_path=}[rdoc-ref:Gem::Net::HTTP#ca_path=]:
+ # Sets the path of to CA directory containing certification files.
+ # - {:cert}[rdoc-ref:Gem::Net::HTTP#cert]:
+ # Returns the OpenSSL::X509::Certificate object to be used for client certification.
+ # - {:cert=}[rdoc-ref:Gem::Net::HTTP#cert=]:
+ # Sets the OpenSSL::X509::Certificate object to be used for client certification.
+ # - {:cert_store}[rdoc-ref:Gem::Net::HTTP#cert_store]:
+ # Returns the X509::Store to be used for verifying peer certificate.
+ # - {:cert_store=}[rdoc-ref:Gem::Net::HTTP#cert_store=]:
+ # Sets the X509::Store to be used for verifying peer certificate.
+ # - {:ciphers}[rdoc-ref:Gem::Net::HTTP#ciphers]:
+ # Returns the available SSL ciphers.
+ # - {:ciphers=}[rdoc-ref:Gem::Net::HTTP#ciphers=]:
+ # Sets the available SSL ciphers.
+ # - {:extra_chain_cert}[rdoc-ref:Gem::Net::HTTP#extra_chain_cert]:
+ # Returns the extra X509 certificates to be added to the certificate chain.
+ # - {:extra_chain_cert=}[rdoc-ref:Gem::Net::HTTP#extra_chain_cert=]:
+ # Sets the extra X509 certificates to be added to the certificate chain.
+ # - {:key}[rdoc-ref:Gem::Net::HTTP#key]:
+ # Returns the OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object.
+ # - {:key=}[rdoc-ref:Gem::Net::HTTP#key=]:
+ # Sets the OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object.
+ # - {:max_version}[rdoc-ref:Gem::Net::HTTP#max_version]:
+ # Returns the maximum SSL version.
+ # - {:max_version=}[rdoc-ref:Gem::Net::HTTP#max_version=]:
+ # Sets the maximum SSL version.
+ # - {:min_version}[rdoc-ref:Gem::Net::HTTP#min_version]:
+ # Returns the minimum SSL version.
+ # - {:min_version=}[rdoc-ref:Gem::Net::HTTP#min_version=]:
+ # Sets the minimum SSL version.
+ # - {#peer_cert}[rdoc-ref:Gem::Net::HTTP#peer_cert]:
+ # Returns the X509 certificate chain for the session's socket peer.
+ # - {:ssl_version}[rdoc-ref:Gem::Net::HTTP#ssl_version]:
+ # Returns the SSL version.
+ # - {:ssl_version=}[rdoc-ref:Gem::Net::HTTP#ssl_version=]:
+ # Sets the SSL version.
+ # - {#use_ssl=}[rdoc-ref:Gem::Net::HTTP#use_ssl=]:
+ # Sets whether a new session is to use Transport Layer Security.
+ # - {#use_ssl?}[rdoc-ref:Gem::Net::HTTP#use_ssl?]:
+ # Returns whether +self+ uses SSL.
+ # - {:verify_callback}[rdoc-ref:Gem::Net::HTTP#verify_callback]:
+ # Returns the callback for the server certification verification.
+ # - {:verify_callback=}[rdoc-ref:Gem::Net::HTTP#verify_callback=]:
+ # Sets the callback for the server certification verification.
+ # - {:verify_depth}[rdoc-ref:Gem::Net::HTTP#verify_depth]:
+ # Returns the maximum depth for the certificate chain verification.
+ # - {:verify_depth=}[rdoc-ref:Gem::Net::HTTP#verify_depth=]:
+ # Sets the maximum depth for the certificate chain verification.
+ # - {:verify_hostname}[rdoc-ref:Gem::Net::HTTP#verify_hostname]:
+ # Returns the flags for server the certification verification at the beginning of the SSL/TLS session.
+ # - {:verify_hostname=}[rdoc-ref:Gem::Net::HTTP#verify_hostname=]:
+ # Sets he flags for server the certification verification at the beginning of the SSL/TLS session.
+ # - {:verify_mode}[rdoc-ref:Gem::Net::HTTP#verify_mode]:
+ # Returns the flags for server the certification verification at the beginning of the SSL/TLS session.
+ # - {:verify_mode=}[rdoc-ref:Gem::Net::HTTP#verify_mode=]:
+ # Sets the flags for server the certification verification at the beginning of the SSL/TLS session.
+ #
+ # === Addresses and Ports
+ #
+ # - {:address}[rdoc-ref:Gem::Net::HTTP#address]:
+ # Returns the string host name or host IP.
+ # - {::default_port}[rdoc-ref:Gem::Net::HTTP.default_port]:
+ # Returns integer 80, the default port to use for HTTP requests.
+ # - {::http_default_port}[rdoc-ref:Gem::Net::HTTP.http_default_port]:
+ # Returns integer 80, the default port to use for HTTP requests.
+ # - {::https_default_port}[rdoc-ref:Gem::Net::HTTP.https_default_port]:
+ # Returns integer 443, the default port to use for HTTPS requests.
+ # - {#ipaddr}[rdoc-ref:Gem::Net::HTTP#ipaddr]:
+ # Returns the IP address for the connection.
+ # - {#ipaddr=}[rdoc-ref:Gem::Net::HTTP#ipaddr=]:
+ # Sets the IP address for the connection.
+ # - {:local_host}[rdoc-ref:Gem::Net::HTTP#local_host]:
+ # Returns the string local host used to establish the connection.
+ # - {:local_host=}[rdoc-ref:Gem::Net::HTTP#local_host=]:
+ # Sets the string local host used to establish the connection.
+ # - {:local_port}[rdoc-ref:Gem::Net::HTTP#local_port]:
+ # Returns the integer local port used to establish the connection.
+ # - {:local_port=}[rdoc-ref:Gem::Net::HTTP#local_port=]:
+ # Sets the integer local port used to establish the connection.
+ # - {:port}[rdoc-ref:Gem::Net::HTTP#port]:
+ # Returns the integer port number.
+ #
+ # === \HTTP Version
+ #
+ # - {::version_1_2?}[rdoc-ref:Gem::Net::HTTP.version_1_2?]
+ # (aliased as {::is_version_1_2?}[rdoc-ref:Gem::Net::HTTP.is_version_1_2?]
+ # and {::version_1_2}[rdoc-ref:Gem::Net::HTTP.version_1_2]):
+ # Returns true; retained for compatibility.
+ #
+ # === Debugging
+ #
+ # - {#set_debug_output}[rdoc-ref:Gem::Net::HTTP#set_debug_output]:
+ # Sets the output stream for debugging.
+ #
+ class HTTP < Protocol
+
+ # :stopdoc:
+ VERSION = "0.4.0"
+ HTTPVersion = '1.1'
+ begin
+ require 'zlib'
+ HAVE_ZLIB=true
+ rescue LoadError
+ HAVE_ZLIB=false
+ end
+ # :startdoc:
+
+ # Returns +true+; retained for compatibility.
+ def HTTP.version_1_2
+ true
+ end
+
+ # Returns +true+; retained for compatibility.
+ def HTTP.version_1_2?
+ true
+ end
+
+ # Returns +false+; retained for compatibility.
+ def HTTP.version_1_1? #:nodoc:
+ false
+ end
+
+ class << HTTP
+ alias is_version_1_1? version_1_1? #:nodoc:
+ alias is_version_1_2? version_1_2? #:nodoc:
+ end
+
+ # :call-seq:
+ # Gem::Net::HTTP.get_print(hostname, path, port = 80) -> nil
+ # Gem::Net::HTTP:get_print(uri, headers = {}, port = uri.port) -> nil
+ #
+ # Like Gem::Net::HTTP.get, but writes the returned body to $stdout;
+ # returns +nil+.
+ def HTTP.get_print(uri_or_host, path_or_headers = nil, port = nil)
+ get_response(uri_or_host, path_or_headers, port) {|res|
+ res.read_body do |chunk|
+ $stdout.print chunk
+ end
+ }
+ nil
+ end
+
+ # :call-seq:
+ # Gem::Net::HTTP.get(hostname, path, port = 80) -> body
+ # Gem::Net::HTTP:get(uri, headers = {}, port = uri.port) -> body
+ #
+ # Sends a GET request and returns the \HTTP response body as a string.
+ #
+ # With string arguments +hostname+ and +path+:
+ #
+ # hostname = 'jsonplaceholder.typicode.com'
+ # path = '/todos/1'
+ # puts Gem::Net::HTTP.get(hostname, path)
+ #
+ # Output:
+ #
+ # {
+ # "userId": 1,
+ # "id": 1,
+ # "title": "delectus aut autem",
+ # "completed": false
+ # }
+ #
+ # With URI object +uri+ and optional hash argument +headers+:
+ #
+ # uri = Gem::URI('https://jsonplaceholder.typicode.com/todos/1')
+ # headers = {'Content-type' => 'application/json; charset=UTF-8'}
+ # Gem::Net::HTTP.get(uri, headers)
+ #
+ # Related:
+ #
+ # - Gem::Net::HTTP::Get: request class for \HTTP method +GET+.
+ # - Gem::Net::HTTP#get: convenience method for \HTTP method +GET+.
+ #
+ def HTTP.get(uri_or_host, path_or_headers = nil, port = nil)
+ get_response(uri_or_host, path_or_headers, port).body
+ end
+
+ # :call-seq:
+ # Gem::Net::HTTP.get_response(hostname, path, port = 80) -> http_response
+ # Gem::Net::HTTP:get_response(uri, headers = {}, port = uri.port) -> http_response
+ #
+ # Like Gem::Net::HTTP.get, but returns a Gem::Net::HTTPResponse object
+ # instead of the body string.
+ def HTTP.get_response(uri_or_host, path_or_headers = nil, port = nil, &block)
+ if path_or_headers && !path_or_headers.is_a?(Hash)
+ host = uri_or_host
+ path = path_or_headers
+ new(host, port || HTTP.default_port).start {|http|
+ return http.request_get(path, &block)
+ }
+ else
+ uri = uri_or_host
+ headers = path_or_headers
+ start(uri.hostname, uri.port,
+ :use_ssl => uri.scheme == 'https') {|http|
+ return http.request_get(uri, headers, &block)
+ }
+ end
+ end
+
+ # Posts data to a host; returns a Gem::Net::HTTPResponse object.
+ #
+ # Argument +url+ must be a URL;
+ # argument +data+ must be a string:
+ #
+ # _uri = uri.dup
+ # _uri.path = '/posts'
+ # data = '{"title": "foo", "body": "bar", "userId": 1}'
+ # headers = {'content-type': 'application/json'}
+ # res = Gem::Net::HTTP.post(_uri, data, headers) # => #<Gem::Net::HTTPCreated 201 Created readbody=true>
+ # puts res.body
+ #
+ # Output:
+ #
+ # {
+ # "title": "foo",
+ # "body": "bar",
+ # "userId": 1,
+ # "id": 101
+ # }
+ #
+ # Related:
+ #
+ # - Gem::Net::HTTP::Post: request class for \HTTP method +POST+.
+ # - Gem::Net::HTTP#post: convenience method for \HTTP method +POST+.
+ #
+ def HTTP.post(url, data, header = nil)
+ start(url.hostname, url.port,
+ :use_ssl => url.scheme == 'https' ) {|http|
+ http.post(url, data, header)
+ }
+ end
+
+ # Posts data to a host; returns a Gem::Net::HTTPResponse object.
+ #
+ # Argument +url+ must be a URI;
+ # argument +data+ must be a hash:
+ #
+ # _uri = uri.dup
+ # _uri.path = '/posts'
+ # data = {title: 'foo', body: 'bar', userId: 1}
+ # res = Gem::Net::HTTP.post_form(_uri, data) # => #<Gem::Net::HTTPCreated 201 Created readbody=true>
+ # puts res.body
+ #
+ # Output:
+ #
+ # {
+ # "title": "foo",
+ # "body": "bar",
+ # "userId": "1",
+ # "id": 101
+ # }
+ #
+ def HTTP.post_form(url, params)
+ req = Post.new(url)
+ req.form_data = params
+ req.basic_auth url.user, url.password if url.user
+ start(url.hostname, url.port,
+ :use_ssl => url.scheme == 'https' ) {|http|
+ http.request(req)
+ }
+ end
+
+ #
+ # \HTTP session management
+ #
+
+ # Returns integer +80+, the default port to use for \HTTP requests:
+ #
+ # Gem::Net::HTTP.default_port # => 80
+ #
+ def HTTP.default_port
+ http_default_port()
+ end
+
+ # Returns integer +80+, the default port to use for \HTTP requests:
+ #
+ # Gem::Net::HTTP.http_default_port # => 80
+ #
+ def HTTP.http_default_port
+ 80
+ end
+
+ # Returns integer +443+, the default port to use for HTTPS requests:
+ #
+ # Gem::Net::HTTP.https_default_port # => 443
+ #
+ def HTTP.https_default_port
+ 443
+ end
+
+ def HTTP.socket_type #:nodoc: obsolete
+ BufferedIO
+ end
+
+ # :call-seq:
+ # HTTP.start(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, opts) -> http
+ # HTTP.start(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, opts) {|http| ... } -> object
+ #
+ # Creates a new \Gem::Net::HTTP object, +http+, via \Gem::Net::HTTP.new:
+ #
+ # - For arguments +address+ and +port+, see Gem::Net::HTTP.new.
+ # - For proxy-defining arguments +p_addr+ through +p_pass+,
+ # see {Proxy Server}[rdoc-ref:Gem::Net::HTTP@Proxy+Server].
+ # - For argument +opts+, see below.
+ #
+ # With no block given:
+ #
+ # - Calls <tt>http.start</tt> with no block (see #start),
+ # which opens a TCP connection and \HTTP session.
+ # - Returns +http+.
+ # - The caller should call #finish to close the session:
+ #
+ # http = Gem::Net::HTTP.start(hostname)
+ # http.started? # => true
+ # http.finish
+ # http.started? # => false
+ #
+ # With a block given:
+ #
+ # - Calls <tt>http.start</tt> with the block (see #start), which:
+ #
+ # - Opens a TCP connection and \HTTP session.
+ # - Calls the block,
+ # which may make any number of requests to the host.
+ # - Closes the \HTTP session and TCP connection on block exit.
+ # - Returns the block's value +object+.
+ #
+ # - Returns +object+.
+ #
+ # Example:
+ #
+ # hostname = 'jsonplaceholder.typicode.com'
+ # Gem::Net::HTTP.start(hostname) do |http|
+ # puts http.get('/todos/1').body
+ # puts http.get('/todos/2').body
+ # end
+ #
+ # Output:
+ #
+ # {
+ # "userId": 1,
+ # "id": 1,
+ # "title": "delectus aut autem",
+ # "completed": false
+ # }
+ # {
+ # "userId": 1,
+ # "id": 2,
+ # "title": "quis ut nam facilis et officia qui",
+ # "completed": false
+ # }
+ #
+ # If the last argument given is a hash, it is the +opts+ hash,
+ # where each key is a method or accessor to be called,
+ # and its value is the value to be set.
+ #
+ # The keys may include:
+ #
+ # - #ca_file
+ # - #ca_path
+ # - #cert
+ # - #cert_store
+ # - #ciphers
+ # - #close_on_empty_response
+ # - +ipaddr+ (calls #ipaddr=)
+ # - #keep_alive_timeout
+ # - #key
+ # - #open_timeout
+ # - #read_timeout
+ # - #ssl_timeout
+ # - #ssl_version
+ # - +use_ssl+ (calls #use_ssl=)
+ # - #verify_callback
+ # - #verify_depth
+ # - #verify_mode
+ # - #write_timeout
+ #
+ # Note: If +port+ is +nil+ and <tt>opts[:use_ssl]</tt> is a truthy value,
+ # the value passed to +new+ is Gem::Net::HTTP.https_default_port, not +port+.
+ #
+ def HTTP.start(address, *arg, &block) # :yield: +http+
+ arg.pop if opt = Hash.try_convert(arg[-1])
+ port, p_addr, p_port, p_user, p_pass = *arg
+ p_addr = :ENV if arg.size < 2
+ port = https_default_port if !port && opt && opt[:use_ssl]
+ http = new(address, port, p_addr, p_port, p_user, p_pass)
+ http.ipaddr = opt[:ipaddr] if opt && opt[:ipaddr]
+
+ if opt
+ if opt[:use_ssl]
+ opt = {verify_mode: OpenSSL::SSL::VERIFY_PEER}.update(opt)
+ end
+ http.methods.grep(/\A(\w+)=\z/) do |meth|
+ key = $1.to_sym
+ opt.key?(key) or next
+ http.__send__(meth, opt[key])
+ end
+ end
+
+ http.start(&block)
+ end
+
+ class << HTTP
+ alias newobj new # :nodoc:
+ end
+
+ # Returns a new \Gem::Net::HTTP object +http+
+ # (but does not open a TCP connection or \HTTP session).
+ #
+ # With only string argument +address+ given
+ # (and <tt>ENV['http_proxy']</tt> undefined or +nil+),
+ # the returned +http+:
+ #
+ # - Has the given address.
+ # - Has the default port number, Gem::Net::HTTP.default_port (80).
+ # - Has no proxy.
+ #
+ # Example:
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ # # => #<Gem::Net::HTTP jsonplaceholder.typicode.com:80 open=false>
+ # http.address # => "jsonplaceholder.typicode.com"
+ # http.port # => 80
+ # http.proxy? # => false
+ #
+ # With integer argument +port+ also given,
+ # the returned +http+ has the given port:
+ #
+ # http = Gem::Net::HTTP.new(hostname, 8000)
+ # # => #<Gem::Net::HTTP jsonplaceholder.typicode.com:8000 open=false>
+ # http.port # => 8000
+ #
+ # For proxy-defining arguments +p_addr+ through +p_no_proxy+,
+ # see {Proxy Server}[rdoc-ref:Gem::Net::HTTP@Proxy+Server].
+ #
+ def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, p_no_proxy = nil)
+ http = super address, port
+
+ if proxy_class? then # from Gem::Net::HTTP::Proxy()
+ http.proxy_from_env = @proxy_from_env
+ http.proxy_address = @proxy_address
+ http.proxy_port = @proxy_port
+ http.proxy_user = @proxy_user
+ http.proxy_pass = @proxy_pass
+ elsif p_addr == :ENV then
+ http.proxy_from_env = true
+ else
+ if p_addr && p_no_proxy && !Gem::URI::Generic.use_proxy?(address, address, port, p_no_proxy)
+ p_addr = nil
+ p_port = nil
+ end
+ http.proxy_address = p_addr
+ http.proxy_port = p_port || default_port
+ http.proxy_user = p_user
+ http.proxy_pass = p_pass
+ end
+
+ http
+ end
+
+ # Creates a new \Gem::Net::HTTP object for the specified server address,
+ # without opening the TCP connection or initializing the \HTTP session.
+ # The +address+ should be a DNS hostname or IP address.
+ def initialize(address, port = nil) # :nodoc:
+ @address = address
+ @port = (port || HTTP.default_port)
+ @ipaddr = nil
+ @local_host = nil
+ @local_port = nil
+ @curr_http_version = HTTPVersion
+ @keep_alive_timeout = 2
+ @last_communicated = nil
+ @close_on_empty_response = false
+ @socket = nil
+ @started = false
+ @open_timeout = 60
+ @read_timeout = 60
+ @write_timeout = 60
+ @continue_timeout = nil
+ @max_retries = 1
+ @debug_output = nil
+ @response_body_encoding = false
+ @ignore_eof = true
+
+ @proxy_from_env = false
+ @proxy_uri = nil
+ @proxy_address = nil
+ @proxy_port = nil
+ @proxy_user = nil
+ @proxy_pass = nil
+
+ @use_ssl = false
+ @ssl_context = nil
+ @ssl_session = nil
+ @sspi_enabled = false
+ SSL_IVNAMES.each do |ivname|
+ instance_variable_set ivname, nil
+ end
+ end
+
+ # Returns a string representation of +self+:
+ #
+ # Gem::Net::HTTP.new(hostname).inspect
+ # # => "#<Gem::Net::HTTP jsonplaceholder.typicode.com:80 open=false>"
+ #
+ def inspect
+ "#<#{self.class} #{@address}:#{@port} open=#{started?}>"
+ end
+
+ # *WARNING* This method opens a serious security hole.
+ # Never use this method in production code.
+ #
+ # Sets the output stream for debugging:
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ # File.open('t.tmp', 'w') do |file|
+ # http.set_debug_output(file)
+ # http.start
+ # http.get('/nosuch/1')
+ # http.finish
+ # end
+ # puts File.read('t.tmp')
+ #
+ # Output:
+ #
+ # opening connection to jsonplaceholder.typicode.com:80...
+ # opened
+ # <- "GET /nosuch/1 HTTP/1.1\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nHost: jsonplaceholder.typicode.com\r\n\r\n"
+ # -> "HTTP/1.1 404 Not Found\r\n"
+ # -> "Date: Mon, 12 Dec 2022 21:14:11 GMT\r\n"
+ # -> "Content-Type: application/json; charset=utf-8\r\n"
+ # -> "Content-Length: 2\r\n"
+ # -> "Connection: keep-alive\r\n"
+ # -> "X-Powered-By: Express\r\n"
+ # -> "X-Ratelimit-Limit: 1000\r\n"
+ # -> "X-Ratelimit-Remaining: 999\r\n"
+ # -> "X-Ratelimit-Reset: 1670879660\r\n"
+ # -> "Vary: Origin, Accept-Encoding\r\n"
+ # -> "Access-Control-Allow-Credentials: true\r\n"
+ # -> "Cache-Control: max-age=43200\r\n"
+ # -> "Pragma: no-cache\r\n"
+ # -> "Expires: -1\r\n"
+ # -> "X-Content-Type-Options: nosniff\r\n"
+ # -> "Etag: W/\"2-vyGp6PvFo4RvsFtPoIWeCReyIC8\"\r\n"
+ # -> "Via: 1.1 vegur\r\n"
+ # -> "CF-Cache-Status: MISS\r\n"
+ # -> "Server-Timing: cf-q-config;dur=1.3000000762986e-05\r\n"
+ # -> "Report-To: {\"endpoints\":[{\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v3?s=yOr40jo%2BwS1KHzhTlVpl54beJ5Wx2FcG4gGV0XVrh3X9OlR5q4drUn2dkt5DGO4GDcE%2BVXT7CNgJvGs%2BZleIyMu8CLieFiDIvOviOY3EhHg94m0ZNZgrEdpKD0S85S507l1vsEwEHkoTm%2Ff19SiO\"}],\"group\":\"cf-nel\",\"max_age\":604800}\r\n"
+ # -> "NEL: {\"success_fraction\":0,\"report_to\":\"cf-nel\",\"max_age\":604800}\r\n"
+ # -> "Server: cloudflare\r\n"
+ # -> "CF-RAY: 778977dc484ce591-DFW\r\n"
+ # -> "alt-svc: h3=\":443\"; ma=86400, h3-29=\":443\"; ma=86400\r\n"
+ # -> "\r\n"
+ # reading 2 bytes...
+ # -> "{}"
+ # read 2 bytes
+ # Conn keep-alive
+ #
+ def set_debug_output(output)
+ warn 'Gem::Net::HTTP#set_debug_output called after HTTP started', uplevel: 1 if started?
+ @debug_output = output
+ end
+
+ # Returns the string host name or host IP given as argument +address+ in ::new.
+ attr_reader :address
+
+ # Returns the integer port number given as argument +port+ in ::new.
+ attr_reader :port
+
+ # Sets or returns the string local host used to establish the connection;
+ # initially +nil+.
+ attr_accessor :local_host
+
+ # Sets or returns the integer local port used to establish the connection;
+ # initially +nil+.
+ attr_accessor :local_port
+
+ # Returns the encoding to use for the response body;
+ # see #response_body_encoding=.
+ attr_reader :response_body_encoding
+
+ # Sets the encoding to be used for the response body;
+ # returns the encoding.
+ #
+ # The given +value+ may be:
+ #
+ # - An Encoding object.
+ # - The name of an encoding.
+ # - An alias for an encoding name.
+ #
+ # See {Encoding}[rdoc-ref:Encoding].
+ #
+ # Examples:
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.response_body_encoding = Encoding::US_ASCII # => #<Encoding:US-ASCII>
+ # http.response_body_encoding = 'US-ASCII' # => "US-ASCII"
+ # http.response_body_encoding = 'ASCII' # => "ASCII"
+ #
+ def response_body_encoding=(value)
+ value = Encoding.find(value) if value.is_a?(String)
+ @response_body_encoding = value
+ end
+
+ # Sets whether to determine the proxy from environment variable
+ # '<tt>ENV['http_proxy']</tt>';
+ # see {Proxy Using ENV['http_proxy']}[rdoc-ref:Gem::Net::HTTP@Proxy+Using+-27ENV-5B-27http_proxy-27-5D-27].
+ attr_writer :proxy_from_env
+
+ # Sets the proxy address;
+ # see {Proxy Server}[rdoc-ref:Gem::Net::HTTP@Proxy+Server].
+ attr_writer :proxy_address
+
+ # Sets the proxy port;
+ # see {Proxy Server}[rdoc-ref:Gem::Net::HTTP@Proxy+Server].
+ attr_writer :proxy_port
+
+ # Sets the proxy user;
+ # see {Proxy Server}[rdoc-ref:Gem::Net::HTTP@Proxy+Server].
+ attr_writer :proxy_user
+
+ # Sets the proxy password;
+ # see {Proxy Server}[rdoc-ref:Gem::Net::HTTP@Proxy+Server].
+ attr_writer :proxy_pass
+
+ # Returns the IP address for the connection.
+ #
+ # If the session has not been started,
+ # returns the value set by #ipaddr=,
+ # or +nil+ if it has not been set:
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.ipaddr # => nil
+ # http.ipaddr = '172.67.155.76'
+ # http.ipaddr # => "172.67.155.76"
+ #
+ # If the session has been started,
+ # returns the IP address from the socket:
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.start
+ # http.ipaddr # => "172.67.155.76"
+ # http.finish
+ #
+ def ipaddr
+ started? ? @socket.io.peeraddr[3] : @ipaddr
+ end
+
+ # Sets the IP address for the connection:
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.ipaddr # => nil
+ # http.ipaddr = '172.67.155.76'
+ # http.ipaddr # => "172.67.155.76"
+ #
+ # The IP address may not be set if the session has been started.
+ def ipaddr=(addr)
+ raise IOError, "ipaddr value changed, but session already started" if started?
+ @ipaddr = addr
+ end
+
+ # Sets or returns the numeric (\Integer or \Float) number of seconds
+ # to wait for a connection to open;
+ # initially 60.
+ # If the connection is not made in the given interval,
+ # an exception is raised.
+ attr_accessor :open_timeout
+
+ # Returns the numeric (\Integer or \Float) number of seconds
+ # to wait for one block to be read (via one read(2) call);
+ # see #read_timeout=.
+ attr_reader :read_timeout
+
+ # Returns the numeric (\Integer or \Float) number of seconds
+ # to wait for one block to be written (via one write(2) call);
+ # see #write_timeout=.
+ attr_reader :write_timeout
+
+ # Sets the maximum number of times to retry an idempotent request in case of
+ # \Gem::Net::ReadTimeout, IOError, EOFError, Errno::ECONNRESET,
+ # Errno::ECONNABORTED, Errno::EPIPE, OpenSSL::SSL::SSLError,
+ # Gem::Timeout::Error.
+ # The initial value is 1.
+ #
+ # Argument +retries+ must be a non-negative numeric value:
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.max_retries = 2 # => 2
+ # http.max_retries # => 2
+ #
+ def max_retries=(retries)
+ retries = retries.to_int
+ if retries < 0
+ raise ArgumentError, 'max_retries should be non-negative integer number'
+ end
+ @max_retries = retries
+ end
+
+ # Returns the maximum number of times to retry an idempotent request;
+ # see #max_retries=.
+ attr_reader :max_retries
+
+ # Sets the read timeout, in seconds, for +self+ to integer +sec+;
+ # the initial value is 60.
+ #
+ # Argument +sec+ must be a non-negative numeric value:
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.read_timeout # => 60
+ # http.get('/todos/1') # => #<Gem::Net::HTTPOK 200 OK readbody=true>
+ # http.read_timeout = 0
+ # http.get('/todos/1') # Raises Gem::Net::ReadTimeout.
+ #
+ def read_timeout=(sec)
+ @socket.read_timeout = sec if @socket
+ @read_timeout = sec
+ end
+
+ # Sets the write timeout, in seconds, for +self+ to integer +sec+;
+ # the initial value is 60.
+ #
+ # Argument +sec+ must be a non-negative numeric value:
+ #
+ # _uri = uri.dup
+ # _uri.path = '/posts'
+ # body = 'bar' * 200000
+ # data = <<EOF
+ # {"title": "foo", "body": "#{body}", "userId": "1"}
+ # EOF
+ # headers = {'content-type': 'application/json'}
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.write_timeout # => 60
+ # http.post(_uri.path, data, headers)
+ # # => #<Gem::Net::HTTPCreated 201 Created readbody=true>
+ # http.write_timeout = 0
+ # http.post(_uri.path, data, headers) # Raises Gem::Net::WriteTimeout.
+ #
+ def write_timeout=(sec)
+ @socket.write_timeout = sec if @socket
+ @write_timeout = sec
+ end
+
+ # Returns the continue timeout value;
+ # see continue_timeout=.
+ attr_reader :continue_timeout
+
+ # Sets the continue timeout value,
+ # which is the number of seconds to wait for an expected 100 Continue response.
+ # If the \HTTP object does not receive a response in this many seconds
+ # it sends the request body.
+ def continue_timeout=(sec)
+ @socket.continue_timeout = sec if @socket
+ @continue_timeout = sec
+ end
+
+ # Sets or returns the numeric (\Integer or \Float) number of seconds
+ # to keep the connection open after a request is sent;
+ # initially 2.
+ # If a new request is made during the given interval,
+ # the still-open connection is used;
+ # otherwise the connection will have been closed
+ # and a new connection is opened.
+ attr_accessor :keep_alive_timeout
+
+ # Sets or returns whether to ignore end-of-file when reading a response body
+ # with <tt>Content-Length</tt> headers;
+ # initially +true+.
+ attr_accessor :ignore_eof
+
+ # Returns +true+ if the \HTTP session has been started:
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.started? # => false
+ # http.start
+ # http.started? # => true
+ # http.finish # => nil
+ # http.started? # => false
+ #
+ # Gem::Net::HTTP.start(hostname) do |http|
+ # http.started?
+ # end # => true
+ # http.started? # => false
+ #
+ def started?
+ @started
+ end
+
+ alias active? started? #:nodoc: obsolete
+
+ # Sets or returns whether to close the connection when the response is empty;
+ # initially +false+.
+ attr_accessor :close_on_empty_response
+
+ # Returns +true+ if +self+ uses SSL, +false+ otherwise.
+ # See Gem::Net::HTTP#use_ssl=.
+ def use_ssl?
+ @use_ssl
+ end
+
+ # Sets whether a new session is to use
+ # {Transport Layer Security}[https://en.wikipedia.org/wiki/Transport_Layer_Security]:
+ #
+ # Raises IOError if attempting to change during a session.
+ #
+ # Raises OpenSSL::SSL::SSLError if the port is not an HTTPS port.
+ def use_ssl=(flag)
+ flag = flag ? true : false
+ if started? and @use_ssl != flag
+ raise IOError, "use_ssl value changed, but session already started"
+ end
+ @use_ssl = flag
+ end
+
+ SSL_IVNAMES = [
+ :@ca_file,
+ :@ca_path,
+ :@cert,
+ :@cert_store,
+ :@ciphers,
+ :@extra_chain_cert,
+ :@key,
+ :@ssl_timeout,
+ :@ssl_version,
+ :@min_version,
+ :@max_version,
+ :@verify_callback,
+ :@verify_depth,
+ :@verify_mode,
+ :@verify_hostname,
+ ] # :nodoc:
+ SSL_ATTRIBUTES = [
+ :ca_file,
+ :ca_path,
+ :cert,
+ :cert_store,
+ :ciphers,
+ :extra_chain_cert,
+ :key,
+ :ssl_timeout,
+ :ssl_version,
+ :min_version,
+ :max_version,
+ :verify_callback,
+ :verify_depth,
+ :verify_mode,
+ :verify_hostname,
+ ] # :nodoc:
+
+ # Sets or returns the path to a CA certification file in PEM format.
+ attr_accessor :ca_file
+
+ # Sets or returns the path of to CA directory
+ # containing certification files in PEM format.
+ attr_accessor :ca_path
+
+ # Sets or returns the OpenSSL::X509::Certificate object
+ # to be used for client certification.
+ attr_accessor :cert
+
+ # Sets or returns the X509::Store to be used for verifying peer certificate.
+ attr_accessor :cert_store
+
+ # Sets or returns the available SSL ciphers.
+ # See {OpenSSL::SSL::SSLContext#ciphers=}[rdoc-ref:OpenSSL::SSL::SSLContext#ciphers-3D].
+ attr_accessor :ciphers
+
+ # Sets or returns the extra X509 certificates to be added to the certificate chain.
+ # See {OpenSSL::SSL::SSLContext#add_certificate}[rdoc-ref:OpenSSL::SSL::SSLContext#add_certificate].
+ attr_accessor :extra_chain_cert
+
+ # Sets or returns the OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object.
+ attr_accessor :key
+
+ # Sets or returns the SSL timeout seconds.
+ attr_accessor :ssl_timeout
+
+ # Sets or returns the SSL version.
+ # See {OpenSSL::SSL::SSLContext#ssl_version=}[rdoc-ref:OpenSSL::SSL::SSLContext#ssl_version-3D].
+ attr_accessor :ssl_version
+
+ # Sets or returns the minimum SSL version.
+ # See {OpenSSL::SSL::SSLContext#min_version=}[rdoc-ref:OpenSSL::SSL::SSLContext#min_version-3D].
+ attr_accessor :min_version
+
+ # Sets or returns the maximum SSL version.
+ # See {OpenSSL::SSL::SSLContext#max_version=}[rdoc-ref:OpenSSL::SSL::SSLContext#max_version-3D].
+ attr_accessor :max_version
+
+ # Sets or returns the callback for the server certification verification.
+ attr_accessor :verify_callback
+
+ # Sets or returns the maximum depth for the certificate chain verification.
+ attr_accessor :verify_depth
+
+ # Sets or returns the flags for server the certification verification
+ # at the beginning of the SSL/TLS session.
+ # OpenSSL::SSL::VERIFY_NONE or OpenSSL::SSL::VERIFY_PEER are acceptable.
+ attr_accessor :verify_mode
+
+ # Sets or returns whether to verify that the server certificate is valid
+ # for the hostname.
+ # See {OpenSSL::SSL::SSLContext#verify_hostname=}[rdoc-ref:OpenSSL::SSL::SSLContext#attribute-i-verify_mode].
+ attr_accessor :verify_hostname
+
+ # Returns the X509 certificate chain (an array of strings)
+ # for the session's socket peer,
+ # or +nil+ if none.
+ def peer_cert
+ if not use_ssl? or not @socket
+ return nil
+ end
+ @socket.io.peer_cert
+ end
+
+ # Starts an \HTTP session.
+ #
+ # Without a block, returns +self+:
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ # # => #<Gem::Net::HTTP jsonplaceholder.typicode.com:80 open=false>
+ # http.start
+ # # => #<Gem::Net::HTTP jsonplaceholder.typicode.com:80 open=true>
+ # http.started? # => true
+ # http.finish
+ #
+ # With a block, calls the block with +self+,
+ # finishes the session when the block exits,
+ # and returns the block's value:
+ #
+ # http.start do |http|
+ # http
+ # end
+ # # => #<Gem::Net::HTTP jsonplaceholder.typicode.com:80 open=false>
+ # http.started? # => false
+ #
+ def start # :yield: http
+ raise IOError, 'HTTP session already opened' if @started
+ if block_given?
+ begin
+ do_start
+ return yield(self)
+ ensure
+ do_finish
+ end
+ end
+ do_start
+ self
+ end
+
+ def do_start
+ connect
+ @started = true
+ end
+ private :do_start
+
+ def connect
+ if use_ssl?
+ # reference early to load OpenSSL before connecting,
+ # as OpenSSL may take time to load.
+ @ssl_context = OpenSSL::SSL::SSLContext.new
+ end
+
+ if proxy? then
+ conn_addr = proxy_address
+ conn_port = proxy_port
+ else
+ conn_addr = conn_address
+ conn_port = port
+ end
+
+ debug "opening connection to #{conn_addr}:#{conn_port}..."
+ s = Gem::Timeout.timeout(@open_timeout, Gem::Net::OpenTimeout) {
+ begin
+ TCPSocket.open(conn_addr, conn_port, @local_host, @local_port)
+ rescue => e
+ raise e, "Failed to open TCP connection to " +
+ "#{conn_addr}:#{conn_port} (#{e.message})"
+ end
+ }
+ s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
+ debug "opened"
+ if use_ssl?
+ if proxy?
+ plain_sock = BufferedIO.new(s, read_timeout: @read_timeout,
+ write_timeout: @write_timeout,
+ continue_timeout: @continue_timeout,
+ debug_output: @debug_output)
+ buf = +"CONNECT #{conn_address}:#{@port} HTTP/#{HTTPVersion}\r\n" \
+ "Host: #{@address}:#{@port}\r\n"
+ if proxy_user
+ credential = ["#{proxy_user}:#{proxy_pass}"].pack('m0')
+ buf << "Proxy-Authorization: Basic #{credential}\r\n"
+ end
+ buf << "\r\n"
+ plain_sock.write(buf)
+ HTTPResponse.read_new(plain_sock).value
+ # assuming nothing left in buffers after successful CONNECT response
+ end
+
+ ssl_parameters = Hash.new
+ iv_list = instance_variables
+ SSL_IVNAMES.each_with_index do |ivname, i|
+ if iv_list.include?(ivname)
+ value = instance_variable_get(ivname)
+ unless value.nil?
+ ssl_parameters[SSL_ATTRIBUTES[i]] = value
+ end
+ end
+ end
+ @ssl_context.set_params(ssl_parameters)
+ unless @ssl_context.session_cache_mode.nil? # a dummy method on JRuby
+ @ssl_context.session_cache_mode =
+ OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT |
+ OpenSSL::SSL::SSLContext::SESSION_CACHE_NO_INTERNAL_STORE
+ end
+ if @ssl_context.respond_to?(:session_new_cb) # not implemented under JRuby
+ @ssl_context.session_new_cb = proc {|sock, sess| @ssl_session = sess }
+ end
+
+ # Still do the post_connection_check below even if connecting
+ # to IP address
+ verify_hostname = @ssl_context.verify_hostname
+
+ # Server Name Indication (SNI) RFC 3546/6066
+ case @address
+ when Gem::Resolv::IPv4::Regex, Gem::Resolv::IPv6::Regex
+ # don't set SNI, as IP addresses in SNI is not valid
+ # per RFC 6066, section 3.
+
+ # Avoid openssl warning
+ @ssl_context.verify_hostname = false
+ else
+ ssl_host_address = @address
+ end
+
+ debug "starting SSL for #{conn_addr}:#{conn_port}..."
+ s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
+ s.sync_close = true
+ s.hostname = ssl_host_address if s.respond_to?(:hostname=) && ssl_host_address
+
+ if @ssl_session and
+ Process.clock_gettime(Process::CLOCK_REALTIME) < @ssl_session.time.to_f + @ssl_session.timeout
+ s.session = @ssl_session
+ end
+ ssl_socket_connect(s, @open_timeout)
+ if (@ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE) && verify_hostname
+ s.post_connection_check(@address)
+ end
+ debug "SSL established, protocol: #{s.ssl_version}, cipher: #{s.cipher[0]}"
+ end
+ @socket = BufferedIO.new(s, read_timeout: @read_timeout,
+ write_timeout: @write_timeout,
+ continue_timeout: @continue_timeout,
+ debug_output: @debug_output)
+ @last_communicated = nil
+ on_connect
+ rescue => exception
+ if s
+ debug "Conn close because of connect error #{exception}"
+ s.close
+ end
+ raise
+ end
+ private :connect
+
+ def on_connect
+ end
+ private :on_connect
+
+ # Finishes the \HTTP session:
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.start
+ # http.started? # => true
+ # http.finish # => nil
+ # http.started? # => false
+ #
+ # Raises IOError if not in a session.
+ def finish
+ raise IOError, 'HTTP session not yet started' unless started?
+ do_finish
+ end
+
+ def do_finish
+ @started = false
+ @socket.close if @socket
+ @socket = nil
+ end
+ private :do_finish
+
+ #
+ # proxy
+ #
+
+ public
+
+ # no proxy
+ @is_proxy_class = false
+ @proxy_from_env = false
+ @proxy_addr = nil
+ @proxy_port = nil
+ @proxy_user = nil
+ @proxy_pass = nil
+
+ # Creates an \HTTP proxy class which behaves like \Gem::Net::HTTP, but
+ # performs all access via the specified proxy.
+ #
+ # This class is obsolete. You may pass these same parameters directly to
+ # \Gem::Net::HTTP.new. See Gem::Net::HTTP.new for details of the arguments.
+ def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil) #:nodoc:
+ return self unless p_addr
+
+ Class.new(self) {
+ @is_proxy_class = true
+
+ if p_addr == :ENV then
+ @proxy_from_env = true
+ @proxy_address = nil
+ @proxy_port = nil
+ else
+ @proxy_from_env = false
+ @proxy_address = p_addr
+ @proxy_port = p_port || default_port
+ end
+
+ @proxy_user = p_user
+ @proxy_pass = p_pass
+ }
+ end
+
+ class << HTTP
+ # Returns true if self is a class which was created by HTTP::Proxy.
+ def proxy_class?
+ defined?(@is_proxy_class) ? @is_proxy_class : false
+ end
+
+ # Returns the address of the proxy host, or +nil+ if none;
+ # see Gem::Net::HTTP@Proxy+Server.
+ attr_reader :proxy_address
+
+ # Returns the port number of the proxy host, or +nil+ if none;
+ # see Gem::Net::HTTP@Proxy+Server.
+ attr_reader :proxy_port
+
+ # Returns the user name for accessing the proxy, or +nil+ if none;
+ # see Gem::Net::HTTP@Proxy+Server.
+ attr_reader :proxy_user
+
+ # Returns the password for accessing the proxy, or +nil+ if none;
+ # see Gem::Net::HTTP@Proxy+Server.
+ attr_reader :proxy_pass
+ end
+
+ # Returns +true+ if a proxy server is defined, +false+ otherwise;
+ # see {Proxy Server}[rdoc-ref:Gem::Net::HTTP@Proxy+Server].
+ def proxy?
+ !!(@proxy_from_env ? proxy_uri : @proxy_address)
+ end
+
+ # Returns +true+ if the proxy server is defined in the environment,
+ # +false+ otherwise;
+ # see {Proxy Server}[rdoc-ref:Gem::Net::HTTP@Proxy+Server].
+ def proxy_from_env?
+ @proxy_from_env
+ end
+
+ # The proxy URI determined from the environment for this connection.
+ def proxy_uri # :nodoc:
+ return if @proxy_uri == false
+ @proxy_uri ||= Gem::URI::HTTP.new(
+ "http", nil, address, port, nil, nil, nil, nil, nil
+ ).find_proxy || false
+ @proxy_uri || nil
+ end
+
+ # Returns the address of the proxy server, if defined, +nil+ otherwise;
+ # see {Proxy Server}[rdoc-ref:Gem::Net::HTTP@Proxy+Server].
+ def proxy_address
+ if @proxy_from_env then
+ proxy_uri&.hostname
+ else
+ @proxy_address
+ end
+ end
+
+ # Returns the port number of the proxy server, if defined, +nil+ otherwise;
+ # see {Proxy Server}[rdoc-ref:Gem::Net::HTTP@Proxy+Server].
+ def proxy_port
+ if @proxy_from_env then
+ proxy_uri&.port
+ else
+ @proxy_port
+ end
+ end
+
+ # Returns the user name of the proxy server, if defined, +nil+ otherwise;
+ # see {Proxy Server}[rdoc-ref:Gem::Net::HTTP@Proxy+Server].
+ def proxy_user
+ if @proxy_from_env
+ user = proxy_uri&.user
+ unescape(user) if user
+ else
+ @proxy_user
+ end
+ end
+
+ # Returns the password of the proxy server, if defined, +nil+ otherwise;
+ # see {Proxy Server}[rdoc-ref:Gem::Net::HTTP@Proxy+Server].
+ def proxy_pass
+ if @proxy_from_env
+ pass = proxy_uri&.password
+ unescape(pass) if pass
+ else
+ @proxy_pass
+ end
+ end
+
+ alias proxyaddr proxy_address #:nodoc: obsolete
+ alias proxyport proxy_port #:nodoc: obsolete
+
+ private
+
+ def unescape(value)
+ require 'cgi/util'
+ CGI.unescape(value)
+ end
+
+ # without proxy, obsolete
+
+ def conn_address # :nodoc:
+ @ipaddr || address()
+ end
+
+ def conn_port # :nodoc:
+ port()
+ end
+
+ def edit_path(path)
+ if proxy?
+ if path.start_with?("ftp://") || use_ssl?
+ path
+ else
+ "http://#{addr_port}#{path}"
+ end
+ else
+ path
+ end
+ end
+
+ #
+ # HTTP operations
+ #
+
+ public
+
+ # :call-seq:
+ # get(path, initheader = nil) {|res| ... }
+ #
+ # Sends a GET request to the server;
+ # returns an instance of a subclass of Gem::Net::HTTPResponse.
+ #
+ # The request is based on the Gem::Net::HTTP::Get object
+ # created from string +path+ and initial headers hash +initheader+.
+ #
+ # With a block given, calls the block with the response body:
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.get('/todos/1') do |res|
+ # p res
+ # end # => #<Gem::Net::HTTPOK 200 OK readbody=true>
+ #
+ # Output:
+ #
+ # "{\n \"userId\": 1,\n \"id\": 1,\n \"title\": \"delectus aut autem\",\n \"completed\": false\n}"
+ #
+ # With no block given, simply returns the response object:
+ #
+ # http.get('/') # => #<Gem::Net::HTTPOK 200 OK readbody=true>
+ #
+ # Related:
+ #
+ # - Gem::Net::HTTP::Get: request class for \HTTP method GET.
+ # - Gem::Net::HTTP.get: sends GET request, returns response body.
+ #
+ def get(path, initheader = nil, dest = nil, &block) # :yield: +body_segment+
+ res = nil
+
+ request(Get.new(path, initheader)) {|r|
+ r.read_body dest, &block
+ res = r
+ }
+ res
+ end
+
+ # Sends a HEAD request to the server;
+ # returns an instance of a subclass of Gem::Net::HTTPResponse.
+ #
+ # The request is based on the Gem::Net::HTTP::Head object
+ # created from string +path+ and initial headers hash +initheader+:
+ #
+ # res = http.head('/todos/1') # => #<Gem::Net::HTTPOK 200 OK readbody=true>
+ # res.body # => nil
+ # res.to_hash.take(3)
+ # # =>
+ # [["date", ["Wed, 15 Feb 2023 15:25:42 GMT"]],
+ # ["content-type", ["application/json; charset=utf-8"]],
+ # ["connection", ["close"]]]
+ #
+ def head(path, initheader = nil)
+ request(Head.new(path, initheader))
+ end
+
+ # :call-seq:
+ # post(path, data, initheader = nil) {|res| ... }
+ #
+ # Sends a POST request to the server;
+ # returns an instance of a subclass of Gem::Net::HTTPResponse.
+ #
+ # The request is based on the Gem::Net::HTTP::Post object
+ # created from string +path+, string +data+, and initial headers hash +initheader+.
+ #
+ # With a block given, calls the block with the response body:
+ #
+ # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.post('/todos', data) do |res|
+ # p res
+ # end # => #<Gem::Net::HTTPCreated 201 Created readbody=true>
+ #
+ # Output:
+ #
+ # "{\n \"{\\\"userId\\\": 1, \\\"id\\\": 1, \\\"title\\\": \\\"delectus aut autem\\\", \\\"completed\\\": false}\": \"\",\n \"id\": 201\n}"
+ #
+ # With no block given, simply returns the response object:
+ #
+ # http.post('/todos', data) # => #<Gem::Net::HTTPCreated 201 Created readbody=true>
+ #
+ # Related:
+ #
+ # - Gem::Net::HTTP::Post: request class for \HTTP method POST.
+ # - Gem::Net::HTTP.post: sends POST request, returns response body.
+ #
+ def post(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+
+ send_entity(path, data, initheader, dest, Post, &block)
+ end
+
+ # :call-seq:
+ # patch(path, data, initheader = nil) {|res| ... }
+ #
+ # Sends a PATCH request to the server;
+ # returns an instance of a subclass of Gem::Net::HTTPResponse.
+ #
+ # The request is based on the Gem::Net::HTTP::Patch object
+ # created from string +path+, string +data+, and initial headers hash +initheader+.
+ #
+ # With a block given, calls the block with the response body:
+ #
+ # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.patch('/todos/1', data) do |res|
+ # p res
+ # end # => #<Gem::Net::HTTPOK 200 OK readbody=true>
+ #
+ # Output:
+ #
+ # "{\n \"userId\": 1,\n \"id\": 1,\n \"title\": \"delectus aut autem\",\n \"completed\": false,\n \"{\\\"userId\\\": 1, \\\"id\\\": 1, \\\"title\\\": \\\"delectus aut autem\\\", \\\"completed\\\": false}\": \"\"\n}"
+ #
+ # With no block given, simply returns the response object:
+ #
+ # http.patch('/todos/1', data) # => #<Gem::Net::HTTPCreated 201 Created readbody=true>
+ #
+ def patch(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+
+ send_entity(path, data, initheader, dest, Patch, &block)
+ end
+
+ # Sends a PUT request to the server;
+ # returns an instance of a subclass of Gem::Net::HTTPResponse.
+ #
+ # The request is based on the Gem::Net::HTTP::Put object
+ # created from string +path+, string +data+, and initial headers hash +initheader+.
+ #
+ # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.put('/todos/1', data) # => #<Gem::Net::HTTPOK 200 OK readbody=true>
+ #
+ def put(path, data, initheader = nil)
+ request(Put.new(path, initheader), data)
+ end
+
+ # Sends a PROPPATCH request to the server;
+ # returns an instance of a subclass of Gem::Net::HTTPResponse.
+ #
+ # The request is based on the Gem::Net::HTTP::Proppatch object
+ # created from string +path+, string +body+, and initial headers hash +initheader+.
+ #
+ # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.proppatch('/todos/1', data)
+ #
+ def proppatch(path, body, initheader = nil)
+ request(Proppatch.new(path, initheader), body)
+ end
+
+ # Sends a LOCK request to the server;
+ # returns an instance of a subclass of Gem::Net::HTTPResponse.
+ #
+ # The request is based on the Gem::Net::HTTP::Lock object
+ # created from string +path+, string +body+, and initial headers hash +initheader+.
+ #
+ # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.lock('/todos/1', data)
+ #
+ def lock(path, body, initheader = nil)
+ request(Lock.new(path, initheader), body)
+ end
+
+ # Sends an UNLOCK request to the server;
+ # returns an instance of a subclass of Gem::Net::HTTPResponse.
+ #
+ # The request is based on the Gem::Net::HTTP::Unlock object
+ # created from string +path+, string +body+, and initial headers hash +initheader+.
+ #
+ # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.unlock('/todos/1', data)
+ #
+ def unlock(path, body, initheader = nil)
+ request(Unlock.new(path, initheader), body)
+ end
+
+ # Sends an Options request to the server;
+ # returns an instance of a subclass of Gem::Net::HTTPResponse.
+ #
+ # The request is based on the Gem::Net::HTTP::Options object
+ # created from string +path+ and initial headers hash +initheader+.
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.options('/')
+ #
+ def options(path, initheader = nil)
+ request(Options.new(path, initheader))
+ end
+
+ # Sends a PROPFIND request to the server;
+ # returns an instance of a subclass of Gem::Net::HTTPResponse.
+ #
+ # The request is based on the Gem::Net::HTTP::Propfind object
+ # created from string +path+, string +body+, and initial headers hash +initheader+.
+ #
+ # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.propfind('/todos/1', data)
+ #
+ def propfind(path, body = nil, initheader = {'Depth' => '0'})
+ request(Propfind.new(path, initheader), body)
+ end
+
+ # Sends a DELETE request to the server;
+ # returns an instance of a subclass of Gem::Net::HTTPResponse.
+ #
+ # The request is based on the Gem::Net::HTTP::Delete object
+ # created from string +path+ and initial headers hash +initheader+.
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.delete('/todos/1')
+ #
+ def delete(path, initheader = {'Depth' => 'Infinity'})
+ request(Delete.new(path, initheader))
+ end
+
+ # Sends a MOVE request to the server;
+ # returns an instance of a subclass of Gem::Net::HTTPResponse.
+ #
+ # The request is based on the Gem::Net::HTTP::Move object
+ # created from string +path+ and initial headers hash +initheader+.
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.move('/todos/1')
+ #
+ def move(path, initheader = nil)
+ request(Move.new(path, initheader))
+ end
+
+ # Sends a COPY request to the server;
+ # returns an instance of a subclass of Gem::Net::HTTPResponse.
+ #
+ # The request is based on the Gem::Net::HTTP::Copy object
+ # created from string +path+ and initial headers hash +initheader+.
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.copy('/todos/1')
+ #
+ def copy(path, initheader = nil)
+ request(Copy.new(path, initheader))
+ end
+
+ # Sends a MKCOL request to the server;
+ # returns an instance of a subclass of Gem::Net::HTTPResponse.
+ #
+ # The request is based on the Gem::Net::HTTP::Mkcol object
+ # created from string +path+, string +body+, and initial headers hash +initheader+.
+ #
+ # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}'
+ # http.mkcol('/todos/1', data)
+ # http = Gem::Net::HTTP.new(hostname)
+ #
+ def mkcol(path, body = nil, initheader = nil)
+ request(Mkcol.new(path, initheader), body)
+ end
+
+ # Sends a TRACE request to the server;
+ # returns an instance of a subclass of Gem::Net::HTTPResponse.
+ #
+ # The request is based on the Gem::Net::HTTP::Trace object
+ # created from string +path+ and initial headers hash +initheader+.
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.trace('/todos/1')
+ #
+ def trace(path, initheader = nil)
+ request(Trace.new(path, initheader))
+ end
+
+ # Sends a GET request to the server;
+ # forms the response into a Gem::Net::HTTPResponse object.
+ #
+ # The request is based on the Gem::Net::HTTP::Get object
+ # created from string +path+ and initial headers hash +initheader+.
+ #
+ # With no block given, returns the response object:
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.request_get('/todos') # => #<Gem::Net::HTTPOK 200 OK readbody=true>
+ #
+ # With a block given, calls the block with the response object
+ # and returns the response object:
+ #
+ # http.request_get('/todos') do |res|
+ # p res
+ # end # => #<Gem::Net::HTTPOK 200 OK readbody=true>
+ #
+ # Output:
+ #
+ # #<Gem::Net::HTTPOK 200 OK readbody=false>
+ #
+ def request_get(path, initheader = nil, &block) # :yield: +response+
+ request(Get.new(path, initheader), &block)
+ end
+
+ # Sends a HEAD request to the server;
+ # returns an instance of a subclass of Gem::Net::HTTPResponse.
+ #
+ # The request is based on the Gem::Net::HTTP::Head object
+ # created from string +path+ and initial headers hash +initheader+.
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.head('/todos/1') # => #<Gem::Net::HTTPOK 200 OK readbody=true>
+ #
+ def request_head(path, initheader = nil, &block)
+ request(Head.new(path, initheader), &block)
+ end
+
+ # Sends a POST request to the server;
+ # forms the response into a Gem::Net::HTTPResponse object.
+ #
+ # The request is based on the Gem::Net::HTTP::Post object
+ # created from string +path+, string +data+, and initial headers hash +initheader+.
+ #
+ # With no block given, returns the response object:
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.post('/todos', 'xyzzy')
+ # # => #<Gem::Net::HTTPCreated 201 Created readbody=true>
+ #
+ # With a block given, calls the block with the response body
+ # and returns the response object:
+ #
+ # http.post('/todos', 'xyzzy') do |res|
+ # p res
+ # end # => #<Gem::Net::HTTPCreated 201 Created readbody=true>
+ #
+ # Output:
+ #
+ # "{\n \"xyzzy\": \"\",\n \"id\": 201\n}"
+ #
+ def request_post(path, data, initheader = nil, &block) # :yield: +response+
+ request Post.new(path, initheader), data, &block
+ end
+
+ # Sends a PUT request to the server;
+ # returns an instance of a subclass of Gem::Net::HTTPResponse.
+ #
+ # The request is based on the Gem::Net::HTTP::Put object
+ # created from string +path+, string +data+, and initial headers hash +initheader+.
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.put('/todos/1', 'xyzzy')
+ # # => #<Gem::Net::HTTPOK 200 OK readbody=true>
+ #
+ def request_put(path, data, initheader = nil, &block) #:nodoc:
+ request Put.new(path, initheader), data, &block
+ end
+
+ alias get2 request_get #:nodoc: obsolete
+ alias head2 request_head #:nodoc: obsolete
+ alias post2 request_post #:nodoc: obsolete
+ alias put2 request_put #:nodoc: obsolete
+
+ # Sends an \HTTP request to the server;
+ # returns an instance of a subclass of Gem::Net::HTTPResponse.
+ #
+ # The request is based on the Gem::Net::HTTPRequest object
+ # created from string +path+, string +data+, and initial headers hash +header+.
+ # That object is an instance of the
+ # {subclass of Gem::Net::HTTPRequest}[rdoc-ref:Gem::Net::HTTPRequest@Request+Subclasses],
+ # that corresponds to the given uppercase string +name+,
+ # which must be
+ # an {HTTP request method}[https://en.wikipedia.org/wiki/HTTP#Request_methods]
+ # or a {WebDAV request method}[https://en.wikipedia.org/wiki/WebDAV#Implementation].
+ #
+ # Examples:
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ # http.send_request('GET', '/todos/1')
+ # # => #<Gem::Net::HTTPOK 200 OK readbody=true>
+ # http.send_request('POST', '/todos', 'xyzzy')
+ # # => #<Gem::Net::HTTPCreated 201 Created readbody=true>
+ #
+ def send_request(name, path, data = nil, header = nil)
+ has_response_body = name != 'HEAD'
+ r = HTTPGenericRequest.new(name,(data ? true : false),has_response_body,path,header)
+ request r, data
+ end
+
+ # Sends the given request +req+ to the server;
+ # forms the response into a Gem::Net::HTTPResponse object.
+ #
+ # The given +req+ must be an instance of a
+ # {subclass of Gem::Net::HTTPRequest}[rdoc-ref:Gem::Net::HTTPRequest@Request+Subclasses].
+ # Argument +body+ should be given only if needed for the request.
+ #
+ # With no block given, returns the response object:
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ #
+ # req = Gem::Net::HTTP::Get.new('/todos/1')
+ # http.request(req)
+ # # => #<Gem::Net::HTTPOK 200 OK readbody=true>
+ #
+ # req = Gem::Net::HTTP::Post.new('/todos')
+ # http.request(req, 'xyzzy')
+ # # => #<Gem::Net::HTTPCreated 201 Created readbody=true>
+ #
+ # With a block given, calls the block with the response and returns the response:
+ #
+ # req = Gem::Net::HTTP::Get.new('/todos/1')
+ # http.request(req) do |res|
+ # p res
+ # end # => #<Gem::Net::HTTPOK 200 OK readbody=true>
+ #
+ # Output:
+ #
+ # #<Gem::Net::HTTPOK 200 OK readbody=false>
+ #
+ def request(req, body = nil, &block) # :yield: +response+
+ unless started?
+ start {
+ req['connection'] ||= 'close'
+ return request(req, body, &block)
+ }
+ end
+ if proxy_user()
+ req.proxy_basic_auth proxy_user(), proxy_pass() unless use_ssl?
+ end
+ req.set_body_internal body
+ res = transport_request(req, &block)
+ if sspi_auth?(res)
+ sspi_auth(req)
+ res = transport_request(req, &block)
+ end
+ res
+ end
+
+ private
+
+ # Executes a request which uses a representation
+ # and returns its body.
+ def send_entity(path, data, initheader, dest, type, &block)
+ res = nil
+ request(type.new(path, initheader), data) {|r|
+ r.read_body dest, &block
+ res = r
+ }
+ res
+ end
+
+ IDEMPOTENT_METHODS_ = %w/GET HEAD PUT DELETE OPTIONS TRACE/ # :nodoc:
+
+ def transport_request(req)
+ count = 0
+ begin
+ begin_transport req
+ res = catch(:response) {
+ begin
+ req.exec @socket, @curr_http_version, edit_path(req.path)
+ rescue Errno::EPIPE
+ # Failure when writing full request, but we can probably
+ # still read the received response.
+ end
+
+ begin
+ res = HTTPResponse.read_new(@socket)
+ res.decode_content = req.decode_content
+ res.body_encoding = @response_body_encoding
+ res.ignore_eof = @ignore_eof
+ end while res.kind_of?(HTTPInformation)
+
+ res.uri = req.uri
+
+ res
+ }
+ res.reading_body(@socket, req.response_body_permitted?) {
+ yield res if block_given?
+ }
+ rescue Gem::Net::OpenTimeout
+ raise
+ rescue Gem::Net::ReadTimeout, IOError, EOFError,
+ Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPIPE, Errno::ETIMEDOUT,
+ # avoid a dependency on OpenSSL
+ defined?(OpenSSL::SSL) ? OpenSSL::SSL::SSLError : IOError,
+ Gem::Timeout::Error => exception
+ if count < max_retries && IDEMPOTENT_METHODS_.include?(req.method)
+ count += 1
+ @socket.close if @socket
+ debug "Conn close because of error #{exception}, and retry"
+ retry
+ end
+ debug "Conn close because of error #{exception}"
+ @socket.close if @socket
+ raise
+ end
+
+ end_transport req, res
+ res
+ rescue => exception
+ debug "Conn close because of error #{exception}"
+ @socket.close if @socket
+ raise exception
+ end
+
+ def begin_transport(req)
+ if @socket.closed?
+ connect
+ elsif @last_communicated
+ if @last_communicated + @keep_alive_timeout < Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ debug 'Conn close because of keep_alive_timeout'
+ @socket.close
+ connect
+ elsif @socket.io.to_io.wait_readable(0) && @socket.eof?
+ debug "Conn close because of EOF"
+ @socket.close
+ connect
+ end
+ end
+
+ if not req.response_body_permitted? and @close_on_empty_response
+ req['connection'] ||= 'close'
+ end
+
+ req.update_uri address, port, use_ssl?
+ req['host'] ||= addr_port()
+ end
+
+ def end_transport(req, res)
+ @curr_http_version = res.http_version
+ @last_communicated = nil
+ if @socket.closed?
+ debug 'Conn socket closed'
+ elsif not res.body and @close_on_empty_response
+ debug 'Conn close'
+ @socket.close
+ elsif keep_alive?(req, res)
+ debug 'Conn keep-alive'
+ @last_communicated = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ else
+ debug 'Conn close'
+ @socket.close
+ end
+ end
+
+ def keep_alive?(req, res)
+ return false if req.connection_close?
+ if @curr_http_version <= '1.0'
+ res.connection_keep_alive?
+ else # HTTP/1.1 or later
+ not res.connection_close?
+ end
+ end
+
+ def sspi_auth?(res)
+ return false unless @sspi_enabled
+ if res.kind_of?(HTTPProxyAuthenticationRequired) and
+ proxy? and res["Proxy-Authenticate"].include?("Negotiate")
+ begin
+ require 'win32/sspi'
+ true
+ rescue LoadError
+ false
+ end
+ else
+ false
+ end
+ end
+
+ def sspi_auth(req)
+ n = Win32::SSPI::NegotiateAuth.new
+ req["Proxy-Authorization"] = "Negotiate #{n.get_initial_token}"
+ # Some versions of ISA will close the connection if this isn't present.
+ req["Connection"] = "Keep-Alive"
+ req["Proxy-Connection"] = "Keep-Alive"
+ res = transport_request(req)
+ authphrase = res["Proxy-Authenticate"] or return res
+ req["Proxy-Authorization"] = "Negotiate #{n.complete_authentication(authphrase)}"
+ rescue => err
+ raise HTTPAuthenticationError.new('HTTP authentication failed', err)
+ end
+
+ #
+ # utils
+ #
+
+ private
+
+ def addr_port
+ addr = address
+ addr = "[#{addr}]" if addr.include?(":")
+ default_port = use_ssl? ? HTTP.https_default_port : HTTP.http_default_port
+ default_port == port ? addr : "#{addr}:#{port}"
+ end
+
+ # Adds a message to debugging output
+ def debug(msg)
+ return unless @debug_output
+ @debug_output << msg
+ @debug_output << "\n"
+ end
+
+ alias_method :D, :debug
+ end
+
+end
+
+require_relative 'http/exceptions'
+
+require_relative 'http/header'
+
+require_relative 'http/generic_request'
+require_relative 'http/request'
+require_relative 'http/requests'
+
+require_relative 'http/response'
+require_relative 'http/responses'
+
+require_relative 'http/proxy_delta'
+
+require_relative 'http/backward'
diff --git a/lib/rubygems/vendor/net-http/lib/net/http/backward.rb b/lib/rubygems/vendor/net-http/lib/net/http/backward.rb
new file mode 100644
index 0000000000..10dbc16224
--- /dev/null
+++ b/lib/rubygems/vendor/net-http/lib/net/http/backward.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+# for backward compatibility
+
+# :enddoc:
+
+class Gem::Net::HTTP
+ ProxyMod = ProxyDelta
+ deprecate_constant :ProxyMod
+end
+
+module Gem::Net::NetPrivate
+ HTTPRequest = ::Gem::Net::HTTPRequest
+ deprecate_constant :HTTPRequest
+end
+
+module Gem::Net
+ HTTPSession = HTTP
+
+ HTTPInformationCode = HTTPInformation
+ HTTPSuccessCode = HTTPSuccess
+ HTTPRedirectionCode = HTTPRedirection
+ HTTPRetriableCode = HTTPRedirection
+ HTTPClientErrorCode = HTTPClientError
+ HTTPFatalErrorCode = HTTPClientError
+ HTTPServerErrorCode = HTTPServerError
+ HTTPResponseReceiver = HTTPResponse
+
+ HTTPResponceReceiver = HTTPResponse # Typo since 2001
+
+ deprecate_constant :HTTPSession,
+ :HTTPInformationCode,
+ :HTTPSuccessCode,
+ :HTTPRedirectionCode,
+ :HTTPRetriableCode,
+ :HTTPClientErrorCode,
+ :HTTPFatalErrorCode,
+ :HTTPServerErrorCode,
+ :HTTPResponseReceiver,
+ :HTTPResponceReceiver
+end
diff --git a/lib/rubygems/vendor/net-http/lib/net/http/exceptions.rb b/lib/rubygems/vendor/net-http/lib/net/http/exceptions.rb
new file mode 100644
index 0000000000..c629c0113b
--- /dev/null
+++ b/lib/rubygems/vendor/net-http/lib/net/http/exceptions.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+module Gem::Net
+ # Gem::Net::HTTP exception class.
+ # You cannot use Gem::Net::HTTPExceptions directly; instead, you must use
+ # its subclasses.
+ module HTTPExceptions
+ def initialize(msg, res) #:nodoc:
+ super msg
+ @response = res
+ end
+ attr_reader :response
+ alias data response #:nodoc: obsolete
+ end
+
+ class HTTPError < ProtocolError
+ include HTTPExceptions
+ end
+
+ class HTTPRetriableError < ProtoRetriableError
+ include HTTPExceptions
+ end
+
+ class HTTPClientException < ProtoServerError
+ include HTTPExceptions
+ end
+
+ class HTTPFatalError < ProtoFatalError
+ include HTTPExceptions
+ end
+
+ # We cannot use the name "HTTPServerError", it is the name of the response.
+ HTTPServerException = HTTPClientException # :nodoc:
+ deprecate_constant(:HTTPServerException)
+end
diff --git a/lib/rubygems/vendor/net-http/lib/net/http/generic_request.rb b/lib/rubygems/vendor/net-http/lib/net/http/generic_request.rb
new file mode 100644
index 0000000000..5cfe75a7cd
--- /dev/null
+++ b/lib/rubygems/vendor/net-http/lib/net/http/generic_request.rb
@@ -0,0 +1,414 @@
+# frozen_string_literal: true
+#
+# \HTTPGenericRequest is the parent of the Gem::Net::HTTPRequest class.
+#
+# Do not use this directly; instead, use a subclass of Gem::Net::HTTPRequest.
+#
+# == About the Examples
+#
+# :include: doc/net-http/examples.rdoc
+#
+class Gem::Net::HTTPGenericRequest
+
+ include Gem::Net::HTTPHeader
+
+ def initialize(m, reqbody, resbody, uri_or_path, initheader = nil) # :nodoc:
+ @method = m
+ @request_has_body = reqbody
+ @response_has_body = resbody
+
+ if Gem::URI === uri_or_path then
+ raise ArgumentError, "not an HTTP Gem::URI" unless Gem::URI::HTTP === uri_or_path
+ hostname = uri_or_path.hostname
+ raise ArgumentError, "no host component for Gem::URI" unless (hostname && hostname.length > 0)
+ @uri = uri_or_path.dup
+ host = @uri.hostname.dup
+ host << ":" << @uri.port.to_s if @uri.port != @uri.default_port
+ @path = uri_or_path.request_uri
+ raise ArgumentError, "no HTTP request path given" unless @path
+ else
+ @uri = nil
+ host = nil
+ raise ArgumentError, "no HTTP request path given" unless uri_or_path
+ raise ArgumentError, "HTTP request path is empty" if uri_or_path.empty?
+ @path = uri_or_path.dup
+ end
+
+ @decode_content = false
+
+ if Gem::Net::HTTP::HAVE_ZLIB then
+ if !initheader ||
+ !initheader.keys.any? { |k|
+ %w[accept-encoding range].include? k.downcase
+ } then
+ @decode_content = true if @response_has_body
+ initheader = initheader ? initheader.dup : {}
+ initheader["accept-encoding"] =
+ "gzip;q=1.0,deflate;q=0.6,identity;q=0.3"
+ end
+ end
+
+ initialize_http_header initheader
+ self['Accept'] ||= '*/*'
+ self['User-Agent'] ||= 'Ruby'
+ self['Host'] ||= host if host
+ @body = nil
+ @body_stream = nil
+ @body_data = nil
+ end
+
+ # Returns the string method name for the request:
+ #
+ # Gem::Net::HTTP::Get.new(uri).method # => "GET"
+ # Gem::Net::HTTP::Post.new(uri).method # => "POST"
+ #
+ attr_reader :method
+
+ # Returns the string path for the request:
+ #
+ # Gem::Net::HTTP::Get.new(uri).path # => "/"
+ # Gem::Net::HTTP::Post.new('example.com').path # => "example.com"
+ #
+ attr_reader :path
+
+ # Returns the Gem::URI object for the request, or +nil+ if none:
+ #
+ # Gem::Net::HTTP::Get.new(uri).uri
+ # # => #<Gem::URI::HTTPS https://jsonplaceholder.typicode.com/>
+ # Gem::Net::HTTP::Get.new('example.com').uri # => nil
+ #
+ attr_reader :uri
+
+ # Returns +false+ if the request's header <tt>'Accept-Encoding'</tt>
+ # has been set manually or deleted
+ # (indicating that the user intends to handle encoding in the response),
+ # +true+ otherwise:
+ #
+ # req = Gem::Net::HTTP::Get.new(uri) # => #<Gem::Net::HTTP::Get GET>
+ # req['Accept-Encoding'] # => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3"
+ # req.decode_content # => true
+ # req['Accept-Encoding'] = 'foo'
+ # req.decode_content # => false
+ # req.delete('Accept-Encoding')
+ # req.decode_content # => false
+ #
+ attr_reader :decode_content
+
+ # Returns a string representation of the request:
+ #
+ # Gem::Net::HTTP::Post.new(uri).inspect # => "#<Gem::Net::HTTP::Post POST>"
+ #
+ def inspect
+ "\#<#{self.class} #{@method}>"
+ end
+
+ ##
+ # Don't automatically decode response content-encoding if the user indicates
+ # they want to handle it.
+
+ def []=(key, val) # :nodoc:
+ @decode_content = false if key.downcase == 'accept-encoding'
+
+ super key, val
+ end
+
+ # Returns whether the request may have a body:
+ #
+ # Gem::Net::HTTP::Post.new(uri).request_body_permitted? # => true
+ # Gem::Net::HTTP::Get.new(uri).request_body_permitted? # => false
+ #
+ def request_body_permitted?
+ @request_has_body
+ end
+
+ # Returns whether the response may have a body:
+ #
+ # Gem::Net::HTTP::Post.new(uri).response_body_permitted? # => true
+ # Gem::Net::HTTP::Head.new(uri).response_body_permitted? # => false
+ #
+ def response_body_permitted?
+ @response_has_body
+ end
+
+ def body_exist? # :nodoc:
+ warn "Gem::Net::HTTPRequest#body_exist? is obsolete; use response_body_permitted?", uplevel: 1 if $VERBOSE
+ response_body_permitted?
+ end
+
+ # Returns the string body for the request, or +nil+ if there is none:
+ #
+ # req = Gem::Net::HTTP::Post.new(uri)
+ # req.body # => nil
+ # req.body = '{"title": "foo","body": "bar","userId": 1}'
+ # req.body # => "{\"title\": \"foo\",\"body\": \"bar\",\"userId\": 1}"
+ #
+ attr_reader :body
+
+ # Sets the body for the request:
+ #
+ # req = Gem::Net::HTTP::Post.new(uri)
+ # req.body # => nil
+ # req.body = '{"title": "foo","body": "bar","userId": 1}'
+ # req.body # => "{\"title\": \"foo\",\"body\": \"bar\",\"userId\": 1}"
+ #
+ def body=(str)
+ @body = str
+ @body_stream = nil
+ @body_data = nil
+ str
+ end
+
+ # Returns the body stream object for the request, or +nil+ if there is none:
+ #
+ # req = Gem::Net::HTTP::Post.new(uri) # => #<Gem::Net::HTTP::Post POST>
+ # req.body_stream # => nil
+ # require 'stringio'
+ # req.body_stream = StringIO.new('xyzzy') # => #<StringIO:0x0000027d1e5affa8>
+ # req.body_stream # => #<StringIO:0x0000027d1e5affa8>
+ #
+ attr_reader :body_stream
+
+ # Sets the body stream for the request:
+ #
+ # req = Gem::Net::HTTP::Post.new(uri) # => #<Gem::Net::HTTP::Post POST>
+ # req.body_stream # => nil
+ # require 'stringio'
+ # req.body_stream = StringIO.new('xyzzy') # => #<StringIO:0x0000027d1e5affa8>
+ # req.body_stream # => #<StringIO:0x0000027d1e5affa8>
+ #
+ def body_stream=(input)
+ @body = nil
+ @body_stream = input
+ @body_data = nil
+ input
+ end
+
+ def set_body_internal(str) #:nodoc: internal use only
+ raise ArgumentError, "both of body argument and HTTPRequest#body set" if str and (@body or @body_stream)
+ self.body = str if str
+ if @body.nil? && @body_stream.nil? && @body_data.nil? && request_body_permitted?
+ self.body = ''
+ end
+ end
+
+ #
+ # write
+ #
+
+ def exec(sock, ver, path) #:nodoc: internal use only
+ if @body
+ send_request_with_body sock, ver, path, @body
+ elsif @body_stream
+ send_request_with_body_stream sock, ver, path, @body_stream
+ elsif @body_data
+ send_request_with_body_data sock, ver, path, @body_data
+ else
+ write_header sock, ver, path
+ end
+ end
+
+ def update_uri(addr, port, ssl) # :nodoc: internal use only
+ # reflect the connection and @path to @uri
+ return unless @uri
+
+ if ssl
+ scheme = 'https'
+ klass = Gem::URI::HTTPS
+ else
+ scheme = 'http'
+ klass = Gem::URI::HTTP
+ end
+
+ if host = self['host']
+ host.sub!(/:.*/m, '')
+ elsif host = @uri.host
+ else
+ host = addr
+ end
+ # convert the class of the Gem::URI
+ if @uri.is_a?(klass)
+ @uri.host = host
+ @uri.port = port
+ else
+ @uri = klass.new(
+ scheme, @uri.userinfo,
+ host, port, nil,
+ @uri.path, nil, @uri.query, nil)
+ end
+ end
+
+ private
+
+ class Chunker #:nodoc:
+ def initialize(sock)
+ @sock = sock
+ @prev = nil
+ end
+
+ def write(buf)
+ # avoid memcpy() of buf, buf can huge and eat memory bandwidth
+ rv = buf.bytesize
+ @sock.write("#{rv.to_s(16)}\r\n", buf, "\r\n")
+ rv
+ end
+
+ def finish
+ @sock.write("0\r\n\r\n")
+ end
+ end
+
+ def send_request_with_body(sock, ver, path, body)
+ self.content_length = body.bytesize
+ delete 'Transfer-Encoding'
+ supply_default_content_type
+ write_header sock, ver, path
+ wait_for_continue sock, ver if sock.continue_timeout
+ sock.write body
+ end
+
+ def send_request_with_body_stream(sock, ver, path, f)
+ unless content_length() or chunked?
+ raise ArgumentError,
+ "Content-Length not given and Transfer-Encoding is not `chunked'"
+ end
+ supply_default_content_type
+ write_header sock, ver, path
+ wait_for_continue sock, ver if sock.continue_timeout
+ if chunked?
+ chunker = Chunker.new(sock)
+ IO.copy_stream(f, chunker)
+ chunker.finish
+ else
+ IO.copy_stream(f, sock)
+ end
+ end
+
+ def send_request_with_body_data(sock, ver, path, params)
+ if /\Amultipart\/form-data\z/i !~ self.content_type
+ self.content_type = 'application/x-www-form-urlencoded'
+ return send_request_with_body(sock, ver, path, Gem::URI.encode_www_form(params))
+ end
+
+ opt = @form_option.dup
+ require 'securerandom' unless defined?(SecureRandom)
+ opt[:boundary] ||= SecureRandom.urlsafe_base64(40)
+ self.set_content_type(self.content_type, boundary: opt[:boundary])
+ if chunked?
+ write_header sock, ver, path
+ encode_multipart_form_data(sock, params, opt)
+ else
+ require 'tempfile'
+ file = Tempfile.new('multipart')
+ file.binmode
+ encode_multipart_form_data(file, params, opt)
+ file.rewind
+ self.content_length = file.size
+ write_header sock, ver, path
+ IO.copy_stream(file, sock)
+ file.close(true)
+ end
+ end
+
+ def encode_multipart_form_data(out, params, opt)
+ charset = opt[:charset]
+ boundary = opt[:boundary]
+ require 'securerandom' unless defined?(SecureRandom)
+ boundary ||= SecureRandom.urlsafe_base64(40)
+ chunked_p = chunked?
+
+ buf = +''
+ params.each do |key, value, h={}|
+ key = quote_string(key, charset)
+ filename =
+ h.key?(:filename) ? h[:filename] :
+ value.respond_to?(:to_path) ? File.basename(value.to_path) :
+ nil
+
+ buf << "--#{boundary}\r\n"
+ if filename
+ filename = quote_string(filename, charset)
+ type = h[:content_type] || 'application/octet-stream'
+ buf << "Content-Disposition: form-data; " \
+ "name=\"#{key}\"; filename=\"#{filename}\"\r\n" \
+ "Content-Type: #{type}\r\n\r\n"
+ if !out.respond_to?(:write) || !value.respond_to?(:read)
+ # if +out+ is not an IO or +value+ is not an IO
+ buf << (value.respond_to?(:read) ? value.read : value)
+ elsif value.respond_to?(:size) && chunked_p
+ # if +out+ is an IO and +value+ is a File, use IO.copy_stream
+ flush_buffer(out, buf, chunked_p)
+ out << "%x\r\n" % value.size if chunked_p
+ IO.copy_stream(value, out)
+ out << "\r\n" if chunked_p
+ else
+ # +out+ is an IO, and +value+ is not a File but an IO
+ flush_buffer(out, buf, chunked_p)
+ 1 while flush_buffer(out, value.read(4096), chunked_p)
+ end
+ else
+ # non-file field:
+ # HTML5 says, "The parts of the generated multipart/form-data
+ # resource that correspond to non-file fields must not have a
+ # Content-Type header specified."
+ buf << "Content-Disposition: form-data; name=\"#{key}\"\r\n\r\n"
+ buf << (value.respond_to?(:read) ? value.read : value)
+ end
+ buf << "\r\n"
+ end
+ buf << "--#{boundary}--\r\n"
+ flush_buffer(out, buf, chunked_p)
+ out << "0\r\n\r\n" if chunked_p
+ end
+
+ def quote_string(str, charset)
+ str = str.encode(charset, fallback:->(c){'&#%d;'%c.encode("UTF-8").ord}) if charset
+ str.gsub(/[\\"]/, '\\\\\&')
+ end
+
+ def flush_buffer(out, buf, chunked_p)
+ return unless buf
+ out << "%x\r\n"%buf.bytesize if chunked_p
+ out << buf
+ out << "\r\n" if chunked_p
+ buf.clear
+ end
+
+ def supply_default_content_type
+ return if content_type()
+ warn 'net/http: Content-Type did not set; using application/x-www-form-urlencoded', uplevel: 1 if $VERBOSE
+ set_content_type 'application/x-www-form-urlencoded'
+ end
+
+ ##
+ # Waits up to the continue timeout for a response from the server provided
+ # we're speaking HTTP 1.1 and are expecting a 100-continue response.
+
+ def wait_for_continue(sock, ver)
+ if ver >= '1.1' and @header['expect'] and
+ @header['expect'].include?('100-continue')
+ if sock.io.to_io.wait_readable(sock.continue_timeout)
+ res = Gem::Net::HTTPResponse.read_new(sock)
+ unless res.kind_of?(Gem::Net::HTTPContinue)
+ res.decode_content = @decode_content
+ throw :response, res
+ end
+ end
+ end
+ end
+
+ def write_header(sock, ver, path)
+ reqline = "#{@method} #{path} HTTP/#{ver}"
+ if /[\r\n]/ =~ reqline
+ raise ArgumentError, "A Request-Line must not contain CR or LF"
+ end
+ buf = +''
+ buf << reqline << "\r\n"
+ each_capitalized do |k,v|
+ buf << "#{k}: #{v}\r\n"
+ end
+ buf << "\r\n"
+ sock.write buf
+ end
+
+end
+
diff --git a/lib/rubygems/vendor/net-http/lib/net/http/header.rb b/lib/rubygems/vendor/net-http/lib/net/http/header.rb
new file mode 100644
index 0000000000..1488e60068
--- /dev/null
+++ b/lib/rubygems/vendor/net-http/lib/net/http/header.rb
@@ -0,0 +1,981 @@
+# frozen_string_literal: true
+#
+# The \HTTPHeader module provides access to \HTTP headers.
+#
+# The module is included in:
+#
+# - Gem::Net::HTTPGenericRequest (and therefore Gem::Net::HTTPRequest).
+# - Gem::Net::HTTPResponse.
+#
+# The headers are a hash-like collection of key/value pairs called _fields_.
+#
+# == Request and Response Fields
+#
+# Headers may be included in:
+#
+# - A Gem::Net::HTTPRequest object:
+# the object's headers will be sent with the request.
+# Any fields may be defined in the request;
+# see {Setters}[rdoc-ref:Gem::Net::HTTPHeader@Setters].
+# - A Gem::Net::HTTPResponse object:
+# the objects headers are usually those returned from the host.
+# Fields may be retrieved from the object;
+# see {Getters}[rdoc-ref:Gem::Net::HTTPHeader@Getters]
+# and {Iterators}[rdoc-ref:Gem::Net::HTTPHeader@Iterators].
+#
+# Exactly which fields should be sent or expected depends on the host;
+# see:
+#
+# - {Request fields}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Request_fields].
+# - {Response fields}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Response_fields].
+#
+# == About the Examples
+#
+# :include: doc/net-http/examples.rdoc
+#
+# == Fields
+#
+# A header field is a key/value pair.
+#
+# === Field Keys
+#
+# A field key may be:
+#
+# - A string: Key <tt>'Accept'</tt> is treated as if it were
+# <tt>'Accept'.downcase</tt>; i.e., <tt>'accept'</tt>.
+# - A symbol: Key <tt>:Accept</tt> is treated as if it were
+# <tt>:Accept.to_s.downcase</tt>; i.e., <tt>'accept'</tt>.
+#
+# Examples:
+#
+# req = Gem::Net::HTTP::Get.new(uri)
+# req[:accept] # => "*/*"
+# req['Accept'] # => "*/*"
+# req['ACCEPT'] # => "*/*"
+#
+# req['accept'] = 'text/html'
+# req[:accept] = 'text/html'
+# req['ACCEPT'] = 'text/html'
+#
+# === Field Values
+#
+# A field value may be returned as an array of strings or as a string:
+#
+# - These methods return field values as arrays:
+#
+# - #get_fields: Returns the array value for the given key,
+# or +nil+ if it does not exist.
+# - #to_hash: Returns a hash of all header fields:
+# each key is a field name; its value is the array value for the field.
+#
+# - These methods return field values as string;
+# the string value for a field is equivalent to
+# <tt>self[key.downcase.to_s].join(', '))</tt>:
+#
+# - #[]: Returns the string value for the given key,
+# or +nil+ if it does not exist.
+# - #fetch: Like #[], but accepts a default value
+# to be returned if the key does not exist.
+#
+# The field value may be set:
+#
+# - #[]=: Sets the value for the given key;
+# the given value may be a string, a symbol, an array, or a hash.
+# - #add_field: Adds a given value to a value for the given key
+# (not overwriting the existing value).
+# - #delete: Deletes the field for the given key.
+#
+# Example field values:
+#
+# - \String:
+#
+# req['Accept'] = 'text/html' # => "text/html"
+# req['Accept'] # => "text/html"
+# req.get_fields('Accept') # => ["text/html"]
+#
+# - \Symbol:
+#
+# req['Accept'] = :text # => :text
+# req['Accept'] # => "text"
+# req.get_fields('Accept') # => ["text"]
+#
+# - Simple array:
+#
+# req[:foo] = %w[bar baz bat]
+# req[:foo] # => "bar, baz, bat"
+# req.get_fields(:foo) # => ["bar", "baz", "bat"]
+#
+# - Simple hash:
+#
+# req[:foo] = {bar: 0, baz: 1, bat: 2}
+# req[:foo] # => "bar, 0, baz, 1, bat, 2"
+# req.get_fields(:foo) # => ["bar", "0", "baz", "1", "bat", "2"]
+#
+# - Nested:
+#
+# req[:foo] = [%w[bar baz], {bat: 0, bam: 1}]
+# req[:foo] # => "bar, baz, bat, 0, bam, 1"
+# req.get_fields(:foo) # => ["bar", "baz", "bat", "0", "bam", "1"]
+#
+# req[:foo] = {bar: %w[baz bat], bam: {bah: 0, bad: 1}}
+# req[:foo] # => "bar, baz, bat, bam, bah, 0, bad, 1"
+# req.get_fields(:foo) # => ["bar", "baz", "bat", "bam", "bah", "0", "bad", "1"]
+#
+# == Convenience Methods
+#
+# Various convenience methods retrieve values, set values, query values,
+# set form values, or iterate over fields.
+#
+# === Setters
+#
+# \Method #[]= can set any field, but does little to validate the new value;
+# some of the other setter methods provide some validation:
+#
+# - #[]=: Sets the string or array value for the given key.
+# - #add_field: Creates or adds to the array value for the given key.
+# - #basic_auth: Sets the string authorization header for <tt>'Authorization'</tt>.
+# - #content_length=: Sets the integer length for field <tt>'Content-Length</tt>.
+# - #content_type=: Sets the string value for field <tt>'Content-Type'</tt>.
+# - #proxy_basic_auth: Sets the string authorization header for <tt>'Proxy-Authorization'</tt>.
+# - #set_range: Sets the value for field <tt>'Range'</tt>.
+#
+# === Form Setters
+#
+# - #set_form: Sets an HTML form data set.
+# - #set_form_data: Sets header fields and a body from HTML form data.
+#
+# === Getters
+#
+# \Method #[] can retrieve the value of any field that exists,
+# but always as a string;
+# some of the other getter methods return something different
+# from the simple string value:
+#
+# - #[]: Returns the string field value for the given key.
+# - #content_length: Returns the integer value of field <tt>'Content-Length'</tt>.
+# - #content_range: Returns the Range value of field <tt>'Content-Range'</tt>.
+# - #content_type: Returns the string value of field <tt>'Content-Type'</tt>.
+# - #fetch: Returns the string field value for the given key.
+# - #get_fields: Returns the array field value for the given +key+.
+# - #main_type: Returns first part of the string value of field <tt>'Content-Type'</tt>.
+# - #sub_type: Returns second part of the string value of field <tt>'Content-Type'</tt>.
+# - #range: Returns an array of Range objects of field <tt>'Range'</tt>, or +nil+.
+# - #range_length: Returns the integer length of the range given in field <tt>'Content-Range'</tt>.
+# - #type_params: Returns the string parameters for <tt>'Content-Type'</tt>.
+#
+# === Queries
+#
+# - #chunked?: Returns whether field <tt>'Transfer-Encoding'</tt> is set to <tt>'chunked'</tt>.
+# - #connection_close?: Returns whether field <tt>'Connection'</tt> is set to <tt>'close'</tt>.
+# - #connection_keep_alive?: Returns whether field <tt>'Connection'</tt> is set to <tt>'keep-alive'</tt>.
+# - #key?: Returns whether a given key exists.
+#
+# === Iterators
+#
+# - #each_capitalized: Passes each field capitalized-name/value pair to the block.
+# - #each_capitalized_name: Passes each capitalized field name to the block.
+# - #each_header: Passes each field name/value pair to the block.
+# - #each_name: Passes each field name to the block.
+# - #each_value: Passes each string field value to the block.
+#
+module Gem::Net::HTTPHeader
+ MAX_KEY_LENGTH = 1024
+ MAX_FIELD_LENGTH = 65536
+
+ def initialize_http_header(initheader) #:nodoc:
+ @header = {}
+ return unless initheader
+ initheader.each do |key, value|
+ warn "net/http: duplicated HTTP header: #{key}", uplevel: 3 if key?(key) and $VERBOSE
+ if value.nil?
+ warn "net/http: nil HTTP header: #{key}", uplevel: 3 if $VERBOSE
+ else
+ value = value.strip # raise error for invalid byte sequences
+ if key.to_s.bytesize > MAX_KEY_LENGTH
+ raise ArgumentError, "too long (#{key.bytesize} bytes) header: #{key[0, 30].inspect}..."
+ end
+ if value.to_s.bytesize > MAX_FIELD_LENGTH
+ raise ArgumentError, "header #{key} has too long field value: #{value.bytesize}"
+ end
+ if value.count("\r\n") > 0
+ raise ArgumentError, "header #{key} has field value #{value.inspect}, this cannot include CR/LF"
+ end
+ @header[key.downcase.to_s] = [value]
+ end
+ end
+ end
+
+ def size #:nodoc: obsolete
+ @header.size
+ end
+
+ alias length size #:nodoc: obsolete
+
+ # Returns the string field value for the case-insensitive field +key+,
+ # or +nil+ if there is no such key;
+ # see {Fields}[rdoc-ref:Gem::Net::HTTPHeader@Fields]:
+ #
+ # res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
+ # res['Connection'] # => "keep-alive"
+ # res['Nosuch'] # => nil
+ #
+ # Note that some field values may be retrieved via convenience methods;
+ # see {Getters}[rdoc-ref:Gem::Net::HTTPHeader@Getters].
+ def [](key)
+ a = @header[key.downcase.to_s] or return nil
+ a.join(', ')
+ end
+
+ # Sets the value for the case-insensitive +key+ to +val+,
+ # overwriting the previous value if the field exists;
+ # see {Fields}[rdoc-ref:Gem::Net::HTTPHeader@Fields]:
+ #
+ # req = Gem::Net::HTTP::Get.new(uri)
+ # req['Accept'] # => "*/*"
+ # req['Accept'] = 'text/html'
+ # req['Accept'] # => "text/html"
+ #
+ # Note that some field values may be set via convenience methods;
+ # see {Setters}[rdoc-ref:Gem::Net::HTTPHeader@Setters].
+ def []=(key, val)
+ unless val
+ @header.delete key.downcase.to_s
+ return val
+ end
+ set_field(key, val)
+ end
+
+ # Adds value +val+ to the value array for field +key+ if the field exists;
+ # creates the field with the given +key+ and +val+ if it does not exist.
+ # see {Fields}[rdoc-ref:Gem::Net::HTTPHeader@Fields]:
+ #
+ # req = Gem::Net::HTTP::Get.new(uri)
+ # req.add_field('Foo', 'bar')
+ # req['Foo'] # => "bar"
+ # req.add_field('Foo', 'baz')
+ # req['Foo'] # => "bar, baz"
+ # req.add_field('Foo', %w[baz bam])
+ # req['Foo'] # => "bar, baz, baz, bam"
+ # req.get_fields('Foo') # => ["bar", "baz", "baz", "bam"]
+ #
+ def add_field(key, val)
+ stringified_downcased_key = key.downcase.to_s
+ if @header.key?(stringified_downcased_key)
+ append_field_value(@header[stringified_downcased_key], val)
+ else
+ set_field(key, val)
+ end
+ end
+
+ private def set_field(key, val)
+ case val
+ when Enumerable
+ ary = []
+ append_field_value(ary, val)
+ @header[key.downcase.to_s] = ary
+ else
+ val = val.to_s # for compatibility use to_s instead of to_str
+ if val.b.count("\r\n") > 0
+ raise ArgumentError, 'header field value cannot include CR/LF'
+ end
+ @header[key.downcase.to_s] = [val]
+ end
+ end
+
+ private def append_field_value(ary, val)
+ case val
+ when Enumerable
+ val.each{|x| append_field_value(ary, x)}
+ else
+ val = val.to_s
+ if /[\r\n]/n.match?(val.b)
+ raise ArgumentError, 'header field value cannot include CR/LF'
+ end
+ ary.push val
+ end
+ end
+
+ # Returns the array field value for the given +key+,
+ # or +nil+ if there is no such field;
+ # see {Fields}[rdoc-ref:Gem::Net::HTTPHeader@Fields]:
+ #
+ # res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
+ # res.get_fields('Connection') # => ["keep-alive"]
+ # res.get_fields('Nosuch') # => nil
+ #
+ def get_fields(key)
+ stringified_downcased_key = key.downcase.to_s
+ return nil unless @header[stringified_downcased_key]
+ @header[stringified_downcased_key].dup
+ end
+
+ # call-seq:
+ # fetch(key, default_val = nil) {|key| ... } -> object
+ # fetch(key, default_val = nil) -> value or default_val
+ #
+ # With a block, returns the string value for +key+ if it exists;
+ # otherwise returns the value of the block;
+ # ignores the +default_val+;
+ # see {Fields}[rdoc-ref:Gem::Net::HTTPHeader@Fields]:
+ #
+ # res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
+ #
+ # # Field exists; block not called.
+ # res.fetch('Connection') do |value|
+ # fail 'Cannot happen'
+ # end # => "keep-alive"
+ #
+ # # Field does not exist; block called.
+ # res.fetch('Nosuch') do |value|
+ # value.downcase
+ # end # => "nosuch"
+ #
+ # With no block, returns the string value for +key+ if it exists;
+ # otherwise, returns +default_val+ if it was given;
+ # otherwise raises an exception:
+ #
+ # res.fetch('Connection', 'Foo') # => "keep-alive"
+ # res.fetch('Nosuch', 'Foo') # => "Foo"
+ # res.fetch('Nosuch') # Raises KeyError.
+ #
+ def fetch(key, *args, &block) #:yield: +key+
+ a = @header.fetch(key.downcase.to_s, *args, &block)
+ a.kind_of?(Array) ? a.join(', ') : a
+ end
+
+ # Calls the block with each key/value pair:
+ #
+ # res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
+ # res.each_header do |key, value|
+ # p [key, value] if key.start_with?('c')
+ # end
+ #
+ # Output:
+ #
+ # ["content-type", "application/json; charset=utf-8"]
+ # ["connection", "keep-alive"]
+ # ["cache-control", "max-age=43200"]
+ # ["cf-cache-status", "HIT"]
+ # ["cf-ray", "771d17e9bc542cf5-ORD"]
+ #
+ # Returns an enumerator if no block is given.
+ #
+ # Gem::Net::HTTPHeader#each is an alias for Gem::Net::HTTPHeader#each_header.
+ def each_header #:yield: +key+, +value+
+ block_given? or return enum_for(__method__) { @header.size }
+ @header.each do |k,va|
+ yield k, va.join(', ')
+ end
+ end
+
+ alias each each_header
+
+ # Calls the block with each field key:
+ #
+ # res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
+ # res.each_key do |key|
+ # p key if key.start_with?('c')
+ # end
+ #
+ # Output:
+ #
+ # "content-type"
+ # "connection"
+ # "cache-control"
+ # "cf-cache-status"
+ # "cf-ray"
+ #
+ # Returns an enumerator if no block is given.
+ #
+ # Gem::Net::HTTPHeader#each_name is an alias for Gem::Net::HTTPHeader#each_key.
+ def each_name(&block) #:yield: +key+
+ block_given? or return enum_for(__method__) { @header.size }
+ @header.each_key(&block)
+ end
+
+ alias each_key each_name
+
+ # Calls the block with each capitalized field name:
+ #
+ # res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
+ # res.each_capitalized_name do |key|
+ # p key if key.start_with?('C')
+ # end
+ #
+ # Output:
+ #
+ # "Content-Type"
+ # "Connection"
+ # "Cache-Control"
+ # "Cf-Cache-Status"
+ # "Cf-Ray"
+ #
+ # The capitalization is system-dependent;
+ # see {Case Mapping}[https://docs.ruby-lang.org/en/master/case_mapping_rdoc.html].
+ #
+ # Returns an enumerator if no block is given.
+ def each_capitalized_name #:yield: +key+
+ block_given? or return enum_for(__method__) { @header.size }
+ @header.each_key do |k|
+ yield capitalize(k)
+ end
+ end
+
+ # Calls the block with each string field value:
+ #
+ # res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
+ # res.each_value do |value|
+ # p value if value.start_with?('c')
+ # end
+ #
+ # Output:
+ #
+ # "chunked"
+ # "cf-q-config;dur=6.0000002122251e-06"
+ # "cloudflare"
+ #
+ # Returns an enumerator if no block is given.
+ def each_value #:yield: +value+
+ block_given? or return enum_for(__method__) { @header.size }
+ @header.each_value do |va|
+ yield va.join(', ')
+ end
+ end
+
+ # Removes the header for the given case-insensitive +key+
+ # (see {Fields}[rdoc-ref:Gem::Net::HTTPHeader@Fields]);
+ # returns the deleted value, or +nil+ if no such field exists:
+ #
+ # req = Gem::Net::HTTP::Get.new(uri)
+ # req.delete('Accept') # => ["*/*"]
+ # req.delete('Nosuch') # => nil
+ #
+ def delete(key)
+ @header.delete(key.downcase.to_s)
+ end
+
+ # Returns +true+ if the field for the case-insensitive +key+ exists, +false+ otherwise:
+ #
+ # req = Gem::Net::HTTP::Get.new(uri)
+ # req.key?('Accept') # => true
+ # req.key?('Nosuch') # => false
+ #
+ def key?(key)
+ @header.key?(key.downcase.to_s)
+ end
+
+ # Returns a hash of the key/value pairs:
+ #
+ # req = Gem::Net::HTTP::Get.new(uri)
+ # req.to_hash
+ # # =>
+ # {"accept-encoding"=>["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"],
+ # "accept"=>["*/*"],
+ # "user-agent"=>["Ruby"],
+ # "host"=>["jsonplaceholder.typicode.com"]}
+ #
+ def to_hash
+ @header.dup
+ end
+
+ # Like #each_header, but the keys are returned in capitalized form.
+ #
+ # Gem::Net::HTTPHeader#canonical_each is an alias for Gem::Net::HTTPHeader#each_capitalized.
+ def each_capitalized
+ block_given? or return enum_for(__method__) { @header.size }
+ @header.each do |k,v|
+ yield capitalize(k), v.join(', ')
+ end
+ end
+
+ alias canonical_each each_capitalized
+
+ def capitalize(name)
+ name.to_s.split(/-/).map {|s| s.capitalize }.join('-')
+ end
+ private :capitalize
+
+ # Returns an array of Range objects that represent
+ # the value of field <tt>'Range'</tt>,
+ # or +nil+ if there is no such field;
+ # see {Range request header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#range-request-header]:
+ #
+ # req = Gem::Net::HTTP::Get.new(uri)
+ # req['Range'] = 'bytes=0-99,200-299,400-499'
+ # req.range # => [0..99, 200..299, 400..499]
+ # req.delete('Range')
+ # req.range # # => nil
+ #
+ def range
+ return nil unless @header['range']
+
+ value = self['Range']
+ # byte-range-set = *( "," OWS ) ( byte-range-spec / suffix-byte-range-spec )
+ # *( OWS "," [ OWS ( byte-range-spec / suffix-byte-range-spec ) ] )
+ # corrected collected ABNF
+ # http://tools.ietf.org/html/draft-ietf-httpbis-p5-range-19#section-5.4.1
+ # http://tools.ietf.org/html/draft-ietf-httpbis-p5-range-19#appendix-C
+ # http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-19#section-3.2.5
+ unless /\Abytes=((?:,[ \t]*)*(?:\d+-\d*|-\d+)(?:[ \t]*,(?:[ \t]*\d+-\d*|-\d+)?)*)\z/ =~ value
+ raise Gem::Net::HTTPHeaderSyntaxError, "invalid syntax for byte-ranges-specifier: '#{value}'"
+ end
+
+ byte_range_set = $1
+ result = byte_range_set.split(/,/).map {|spec|
+ m = /(\d+)?\s*-\s*(\d+)?/i.match(spec) or
+ raise Gem::Net::HTTPHeaderSyntaxError, "invalid byte-range-spec: '#{spec}'"
+ d1 = m[1].to_i
+ d2 = m[2].to_i
+ if m[1] and m[2]
+ if d1 > d2
+ raise Gem::Net::HTTPHeaderSyntaxError, "last-byte-pos MUST greater than or equal to first-byte-pos but '#{spec}'"
+ end
+ d1..d2
+ elsif m[1]
+ d1..-1
+ elsif m[2]
+ -d2..-1
+ else
+ raise Gem::Net::HTTPHeaderSyntaxError, 'range is not specified'
+ end
+ }
+ # if result.empty?
+ # byte-range-set must include at least one byte-range-spec or suffix-byte-range-spec
+ # but above regexp already denies it.
+ if result.size == 1 && result[0].begin == 0 && result[0].end == -1
+ raise Gem::Net::HTTPHeaderSyntaxError, 'only one suffix-byte-range-spec with zero suffix-length'
+ end
+ result
+ end
+
+ # call-seq:
+ # set_range(length) -> length
+ # set_range(offset, length) -> range
+ # set_range(begin..length) -> range
+ #
+ # Sets the value for field <tt>'Range'</tt>;
+ # see {Range request header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#range-request-header]:
+ #
+ # With argument +length+:
+ #
+ # req = Gem::Net::HTTP::Get.new(uri)
+ # req.set_range(100) # => 100
+ # req['Range'] # => "bytes=0-99"
+ #
+ # With arguments +offset+ and +length+:
+ #
+ # req.set_range(100, 100) # => 100...200
+ # req['Range'] # => "bytes=100-199"
+ #
+ # With argument +range+:
+ #
+ # req.set_range(100..199) # => 100..199
+ # req['Range'] # => "bytes=100-199"
+ #
+ # Gem::Net::HTTPHeader#range= is an alias for Gem::Net::HTTPHeader#set_range.
+ def set_range(r, e = nil)
+ unless r
+ @header.delete 'range'
+ return r
+ end
+ r = (r...r+e) if e
+ case r
+ when Numeric
+ n = r.to_i
+ rangestr = (n > 0 ? "0-#{n-1}" : "-#{-n}")
+ when Range
+ first = r.first
+ last = r.end
+ last -= 1 if r.exclude_end?
+ if last == -1
+ rangestr = (first > 0 ? "#{first}-" : "-#{-first}")
+ else
+ raise Gem::Net::HTTPHeaderSyntaxError, 'range.first is negative' if first < 0
+ raise Gem::Net::HTTPHeaderSyntaxError, 'range.last is negative' if last < 0
+ raise Gem::Net::HTTPHeaderSyntaxError, 'must be .first < .last' if first > last
+ rangestr = "#{first}-#{last}"
+ end
+ else
+ raise TypeError, 'Range/Integer is required'
+ end
+ @header['range'] = ["bytes=#{rangestr}"]
+ r
+ end
+
+ alias range= set_range
+
+ # Returns the value of field <tt>'Content-Length'</tt> as an integer,
+ # or +nil+ if there is no such field;
+ # see {Content-Length request header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-length-request-header]:
+ #
+ # res = Gem::Net::HTTP.get_response(hostname, '/nosuch/1')
+ # res.content_length # => 2
+ # res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
+ # res.content_length # => nil
+ #
+ def content_length
+ return nil unless key?('Content-Length')
+ len = self['Content-Length'].slice(/\d+/) or
+ raise Gem::Net::HTTPHeaderSyntaxError, 'wrong Content-Length format'
+ len.to_i
+ end
+
+ # Sets the value of field <tt>'Content-Length'</tt> to the given numeric;
+ # see {Content-Length response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-length-response-header]:
+ #
+ # _uri = uri.dup
+ # hostname = _uri.hostname # => "jsonplaceholder.typicode.com"
+ # _uri.path = '/posts' # => "/posts"
+ # req = Gem::Net::HTTP::Post.new(_uri) # => #<Gem::Net::HTTP::Post POST>
+ # req.body = '{"title": "foo","body": "bar","userId": 1}'
+ # req.content_length = req.body.size # => 42
+ # req.content_type = 'application/json'
+ # res = Gem::Net::HTTP.start(hostname) do |http|
+ # http.request(req)
+ # end # => #<Gem::Net::HTTPCreated 201 Created readbody=true>
+ #
+ def content_length=(len)
+ unless len
+ @header.delete 'content-length'
+ return nil
+ end
+ @header['content-length'] = [len.to_i.to_s]
+ end
+
+ # Returns +true+ if field <tt>'Transfer-Encoding'</tt>
+ # exists and has value <tt>'chunked'</tt>,
+ # +false+ otherwise;
+ # see {Transfer-Encoding response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#transfer-encoding-response-header]:
+ #
+ # res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
+ # res['Transfer-Encoding'] # => "chunked"
+ # res.chunked? # => true
+ #
+ def chunked?
+ return false unless @header['transfer-encoding']
+ field = self['Transfer-Encoding']
+ (/(?:\A|[^\-\w])chunked(?![\-\w])/i =~ field) ? true : false
+ end
+
+ # Returns a Range object representing the value of field
+ # <tt>'Content-Range'</tt>, or +nil+ if no such field exists;
+ # see {Content-Range response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-range-response-header]:
+ #
+ # res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
+ # res['Content-Range'] # => nil
+ # res['Content-Range'] = 'bytes 0-499/1000'
+ # res['Content-Range'] # => "bytes 0-499/1000"
+ # res.content_range # => 0..499
+ #
+ def content_range
+ return nil unless @header['content-range']
+ m = %r<\A\s*(\w+)\s+(\d+)-(\d+)/(\d+|\*)>.match(self['Content-Range']) or
+ raise Gem::Net::HTTPHeaderSyntaxError, 'wrong Content-Range format'
+ return unless m[1] == 'bytes'
+ m[2].to_i .. m[3].to_i
+ end
+
+ # Returns the integer representing length of the value of field
+ # <tt>'Content-Range'</tt>, or +nil+ if no such field exists;
+ # see {Content-Range response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-range-response-header]:
+ #
+ # res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
+ # res['Content-Range'] # => nil
+ # res['Content-Range'] = 'bytes 0-499/1000'
+ # res.range_length # => 500
+ #
+ def range_length
+ r = content_range() or return nil
+ r.end - r.begin + 1
+ end
+
+ # Returns the {media type}[https://en.wikipedia.org/wiki/Media_type]
+ # from the value of field <tt>'Content-Type'</tt>,
+ # or +nil+ if no such field exists;
+ # see {Content-Type response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-response-header]:
+ #
+ # res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
+ # res['content-type'] # => "application/json; charset=utf-8"
+ # res.content_type # => "application/json"
+ #
+ def content_type
+ main = main_type()
+ return nil unless main
+
+ sub = sub_type()
+ if sub
+ "#{main}/#{sub}"
+ else
+ main
+ end
+ end
+
+ # Returns the leading ('type') part of the
+ # {media type}[https://en.wikipedia.org/wiki/Media_type]
+ # from the value of field <tt>'Content-Type'</tt>,
+ # or +nil+ if no such field exists;
+ # see {Content-Type response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-response-header]:
+ #
+ # res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
+ # res['content-type'] # => "application/json; charset=utf-8"
+ # res.main_type # => "application"
+ #
+ def main_type
+ return nil unless @header['content-type']
+ self['Content-Type'].split(';').first.to_s.split('/')[0].to_s.strip
+ end
+
+ # Returns the trailing ('subtype') part of the
+ # {media type}[https://en.wikipedia.org/wiki/Media_type]
+ # from the value of field <tt>'Content-Type'</tt>,
+ # or +nil+ if no such field exists;
+ # see {Content-Type response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-response-header]:
+ #
+ # res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
+ # res['content-type'] # => "application/json; charset=utf-8"
+ # res.sub_type # => "json"
+ #
+ def sub_type
+ return nil unless @header['content-type']
+ _, sub = *self['Content-Type'].split(';').first.to_s.split('/')
+ return nil unless sub
+ sub.strip
+ end
+
+ # Returns the trailing ('parameters') part of the value of field <tt>'Content-Type'</tt>,
+ # or +nil+ if no such field exists;
+ # see {Content-Type response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-response-header]:
+ #
+ # res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
+ # res['content-type'] # => "application/json; charset=utf-8"
+ # res.type_params # => {"charset"=>"utf-8"}
+ #
+ def type_params
+ result = {}
+ list = self['Content-Type'].to_s.split(';')
+ list.shift
+ list.each do |param|
+ k, v = *param.split('=', 2)
+ result[k.strip] = v.strip
+ end
+ result
+ end
+
+ # Sets the value of field <tt>'Content-Type'</tt>;
+ # returns the new value;
+ # see {Content-Type request header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-request-header]:
+ #
+ # req = Gem::Net::HTTP::Get.new(uri)
+ # req.set_content_type('application/json') # => ["application/json"]
+ #
+ # Gem::Net::HTTPHeader#content_type= is an alias for Gem::Net::HTTPHeader#set_content_type.
+ def set_content_type(type, params = {})
+ @header['content-type'] = [type + params.map{|k,v|"; #{k}=#{v}"}.join('')]
+ end
+
+ alias content_type= set_content_type
+
+ # Sets the request body to a URL-encoded string derived from argument +params+,
+ # and sets request header field <tt>'Content-Type'</tt>
+ # to <tt>'application/x-www-form-urlencoded'</tt>.
+ #
+ # The resulting request is suitable for HTTP request +POST+ or +PUT+.
+ #
+ # Argument +params+ must be suitable for use as argument +enum+ to
+ # {Gem::URI.encode_www_form}[https://docs.ruby-lang.org/en/master/Gem::URI.html#method-c-encode_www_form].
+ #
+ # With only argument +params+ given,
+ # sets the body to a URL-encoded string with the default separator <tt>'&'</tt>:
+ #
+ # req = Gem::Net::HTTP::Post.new('example.com')
+ #
+ # req.set_form_data(q: 'ruby', lang: 'en')
+ # req.body # => "q=ruby&lang=en"
+ # req['Content-Type'] # => "application/x-www-form-urlencoded"
+ #
+ # req.set_form_data([['q', 'ruby'], ['lang', 'en']])
+ # req.body # => "q=ruby&lang=en"
+ #
+ # req.set_form_data(q: ['ruby', 'perl'], lang: 'en')
+ # req.body # => "q=ruby&q=perl&lang=en"
+ #
+ # req.set_form_data([['q', 'ruby'], ['q', 'perl'], ['lang', 'en']])
+ # req.body # => "q=ruby&q=perl&lang=en"
+ #
+ # With string argument +sep+ also given,
+ # uses that string as the separator:
+ #
+ # req.set_form_data({q: 'ruby', lang: 'en'}, '|')
+ # req.body # => "q=ruby|lang=en"
+ #
+ # Gem::Net::HTTPHeader#form_data= is an alias for Gem::Net::HTTPHeader#set_form_data.
+ def set_form_data(params, sep = '&')
+ query = Gem::URI.encode_www_form(params)
+ query.gsub!(/&/, sep) if sep != '&'
+ self.body = query
+ self.content_type = 'application/x-www-form-urlencoded'
+ end
+
+ alias form_data= set_form_data
+
+ # Stores form data to be used in a +POST+ or +PUT+ request.
+ #
+ # The form data given in +params+ consists of zero or more fields;
+ # each field is:
+ #
+ # - A scalar value.
+ # - A name/value pair.
+ # - An IO stream opened for reading.
+ #
+ # Argument +params+ should be an
+ # {Enumerable}[https://docs.ruby-lang.org/en/master/Enumerable.html#module-Enumerable-label-Enumerable+in+Ruby+Classes]
+ # (method <tt>params.map</tt> will be called),
+ # and is often an array or hash.
+ #
+ # First, we set up a request:
+ #
+ # _uri = uri.dup
+ # _uri.path ='/posts'
+ # req = Gem::Net::HTTP::Post.new(_uri)
+ #
+ # <b>Argument +params+ As an Array</b>
+ #
+ # When +params+ is an array,
+ # each of its elements is a subarray that defines a field;
+ # the subarray may contain:
+ #
+ # - One string:
+ #
+ # req.set_form([['foo'], ['bar'], ['baz']])
+ #
+ # - Two strings:
+ #
+ # req.set_form([%w[foo 0], %w[bar 1], %w[baz 2]])
+ #
+ # - When argument +enctype+ (see below) is given as
+ # <tt>'multipart/form-data'</tt>:
+ #
+ # - A string name and an IO stream opened for reading:
+ #
+ # require 'stringio'
+ # req.set_form([['file', StringIO.new('Ruby is cool.')]])
+ #
+ # - A string name, an IO stream opened for reading,
+ # and an options hash, which may contain these entries:
+ #
+ # - +:filename+: The name of the file to use.
+ # - +:content_type+: The content type of the uploaded file.
+ #
+ # Example:
+ #
+ # req.set_form([['file', file, {filename: "other-filename.foo"}]]
+ #
+ # The various forms may be mixed:
+ #
+ # req.set_form(['foo', %w[bar 1], ['file', file]])
+ #
+ # <b>Argument +params+ As a Hash</b>
+ #
+ # When +params+ is a hash,
+ # each of its entries is a name/value pair that defines a field:
+ #
+ # - The name is a string.
+ # - The value may be:
+ #
+ # - +nil+.
+ # - Another string.
+ # - An IO stream opened for reading
+ # (only when argument +enctype+ -- see below -- is given as
+ # <tt>'multipart/form-data'</tt>).
+ #
+ # Examples:
+ #
+ # # Nil-valued fields.
+ # req.set_form({'foo' => nil, 'bar' => nil, 'baz' => nil})
+ #
+ # # String-valued fields.
+ # req.set_form({'foo' => 0, 'bar' => 1, 'baz' => 2})
+ #
+ # # IO-valued field.
+ # require 'stringio'
+ # req.set_form({'file' => StringIO.new('Ruby is cool.')})
+ #
+ # # Mixture of fields.
+ # req.set_form({'foo' => nil, 'bar' => 1, 'file' => file})
+ #
+ # Optional argument +enctype+ specifies the value to be given
+ # to field <tt>'Content-Type'</tt>, and must be one of:
+ #
+ # - <tt>'application/x-www-form-urlencoded'</tt> (the default).
+ # - <tt>'multipart/form-data'</tt>;
+ # see {RFC 7578}[https://www.rfc-editor.org/rfc/rfc7578].
+ #
+ # Optional argument +formopt+ is a hash of options
+ # (applicable only when argument +enctype+
+ # is <tt>'multipart/form-data'</tt>)
+ # that may include the following entries:
+ #
+ # - +:boundary+: The value is the boundary string for the multipart message.
+ # If not given, the boundary is a random string.
+ # See {Boundary}[https://www.rfc-editor.org/rfc/rfc7578#section-4.1].
+ # - +:charset+: Value is the character set for the form submission.
+ # Field names and values of non-file fields should be encoded with this charset.
+ #
+ def set_form(params, enctype='application/x-www-form-urlencoded', formopt={})
+ @body_data = params
+ @body = nil
+ @body_stream = nil
+ @form_option = formopt
+ case enctype
+ when /\Aapplication\/x-www-form-urlencoded\z/i,
+ /\Amultipart\/form-data\z/i
+ self.content_type = enctype
+ else
+ raise ArgumentError, "invalid enctype: #{enctype}"
+ end
+ end
+
+ # Sets header <tt>'Authorization'</tt> using the given
+ # +account+ and +password+ strings:
+ #
+ # req.basic_auth('my_account', 'my_password')
+ # req['Authorization']
+ # # => "Basic bXlfYWNjb3VudDpteV9wYXNzd29yZA=="
+ #
+ def basic_auth(account, password)
+ @header['authorization'] = [basic_encode(account, password)]
+ end
+
+ # Sets header <tt>'Proxy-Authorization'</tt> using the given
+ # +account+ and +password+ strings:
+ #
+ # req.proxy_basic_auth('my_account', 'my_password')
+ # req['Proxy-Authorization']
+ # # => "Basic bXlfYWNjb3VudDpteV9wYXNzd29yZA=="
+ #
+ def proxy_basic_auth(account, password)
+ @header['proxy-authorization'] = [basic_encode(account, password)]
+ end
+
+ def basic_encode(account, password)
+ 'Basic ' + ["#{account}:#{password}"].pack('m0')
+ end
+ private :basic_encode
+
+# Returns whether the HTTP session is to be closed.
+ def connection_close?
+ token = /(?:\A|,)\s*close\s*(?:\z|,)/i
+ @header['connection']&.grep(token) {return true}
+ @header['proxy-connection']&.grep(token) {return true}
+ false
+ end
+
+# Returns whether the HTTP session is to be kept alive.
+ def connection_keep_alive?
+ token = /(?:\A|,)\s*keep-alive\s*(?:\z|,)/i
+ @header['connection']&.grep(token) {return true}
+ @header['proxy-connection']&.grep(token) {return true}
+ false
+ end
+
+end
diff --git a/lib/rubygems/vendor/net-http/lib/net/http/proxy_delta.rb b/lib/rubygems/vendor/net-http/lib/net/http/proxy_delta.rb
new file mode 100644
index 0000000000..137295a883
--- /dev/null
+++ b/lib/rubygems/vendor/net-http/lib/net/http/proxy_delta.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+module Gem::Net::HTTP::ProxyDelta #:nodoc: internal use only
+ private
+
+ def conn_address
+ proxy_address()
+ end
+
+ def conn_port
+ proxy_port()
+ end
+
+ def edit_path(path)
+ use_ssl? ? path : "http://#{addr_port()}#{path}"
+ end
+end
+
diff --git a/lib/rubygems/vendor/net-http/lib/net/http/request.rb b/lib/rubygems/vendor/net-http/lib/net/http/request.rb
new file mode 100644
index 0000000000..495ec9be54
--- /dev/null
+++ b/lib/rubygems/vendor/net-http/lib/net/http/request.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+
+# This class is the base class for \Gem::Net::HTTP request classes.
+# The class should not be used directly;
+# instead you should use its subclasses, listed below.
+#
+# == Creating a Request
+#
+# An request object may be created with either a Gem::URI or a string hostname:
+#
+# require 'rubygems/vendor/net-http/lib/net/http'
+# uri = Gem::URI('https://jsonplaceholder.typicode.com/')
+# req = Gem::Net::HTTP::Get.new(uri) # => #<Gem::Net::HTTP::Get GET>
+# req = Gem::Net::HTTP::Get.new(uri.hostname) # => #<Gem::Net::HTTP::Get GET>
+#
+# And with any of the subclasses:
+#
+# req = Gem::Net::HTTP::Head.new(uri) # => #<Gem::Net::HTTP::Head HEAD>
+# req = Gem::Net::HTTP::Post.new(uri) # => #<Gem::Net::HTTP::Post POST>
+# req = Gem::Net::HTTP::Put.new(uri) # => #<Gem::Net::HTTP::Put PUT>
+# # ...
+#
+# The new instance is suitable for use as the argument to Gem::Net::HTTP#request.
+#
+# == Request Headers
+#
+# A new request object has these header fields by default:
+#
+# req.to_hash
+# # =>
+# {"accept-encoding"=>["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"],
+# "accept"=>["*/*"],
+# "user-agent"=>["Ruby"],
+# "host"=>["jsonplaceholder.typicode.com"]}
+#
+# See:
+#
+# - {Request header Accept-Encoding}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Accept-Encoding]
+# and {Compression and Decompression}[rdoc-ref:Gem::Net::HTTP@Compression+and+Decompression].
+# - {Request header Accept}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#accept-request-header].
+# - {Request header User-Agent}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#user-agent-request-header].
+# - {Request header Host}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#host-request-header].
+#
+# You can add headers or override default headers:
+#
+# # res = Gem::Net::HTTP::Get.new(uri, {'foo' => '0', 'bar' => '1'})
+#
+# This class (and therefore its subclasses) also includes (indirectly)
+# module Gem::Net::HTTPHeader, which gives access to its
+# {methods for setting headers}[rdoc-ref:Gem::Net::HTTPHeader@Setters].
+#
+# == Request Subclasses
+#
+# Subclasses for HTTP requests:
+#
+# - Gem::Net::HTTP::Get
+# - Gem::Net::HTTP::Head
+# - Gem::Net::HTTP::Post
+# - Gem::Net::HTTP::Put
+# - Gem::Net::HTTP::Delete
+# - Gem::Net::HTTP::Options
+# - Gem::Net::HTTP::Trace
+# - Gem::Net::HTTP::Patch
+#
+# Subclasses for WebDAV requests:
+#
+# - Gem::Net::HTTP::Propfind
+# - Gem::Net::HTTP::Proppatch
+# - Gem::Net::HTTP::Mkcol
+# - Gem::Net::HTTP::Copy
+# - Gem::Net::HTTP::Move
+# - Gem::Net::HTTP::Lock
+# - Gem::Net::HTTP::Unlock
+#
+class Gem::Net::HTTPRequest < Gem::Net::HTTPGenericRequest
+ # Creates an HTTP request object for +path+.
+ #
+ # +initheader+ are the default headers to use. Gem::Net::HTTP adds
+ # Accept-Encoding to enable compression of the response body unless
+ # Accept-Encoding or Range are supplied in +initheader+.
+
+ def initialize(path, initheader = nil)
+ super self.class::METHOD,
+ self.class::REQUEST_HAS_BODY,
+ self.class::RESPONSE_HAS_BODY,
+ path, initheader
+ end
+end
diff --git a/lib/rubygems/vendor/net-http/lib/net/http/requests.rb b/lib/rubygems/vendor/net-http/lib/net/http/requests.rb
new file mode 100644
index 0000000000..1a57ddc7c2
--- /dev/null
+++ b/lib/rubygems/vendor/net-http/lib/net/http/requests.rb
@@ -0,0 +1,425 @@
+# frozen_string_literal: true
+
+# HTTP/1.1 methods --- RFC2616
+
+# \Class for representing
+# {HTTP method GET}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#GET_method]:
+#
+# require 'rubygems/vendor/net-http/lib/net/http'
+# uri = Gem::URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Gem::Net::HTTP::Get.new(uri) # => #<Gem::Net::HTTP::Get GET>
+# res = Gem::Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
+#
+# Properties:
+#
+# - Request body: optional.
+# - Response body: yes.
+# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: yes.
+# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
+# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: yes.
+#
+# Related:
+#
+# - Gem::Net::HTTP.get: sends +GET+ request, returns response body.
+# - Gem::Net::HTTP#get: sends +GET+ request, returns response object.
+#
+class Gem::Net::HTTP::Get < Gem::Net::HTTPRequest
+ METHOD = 'GET'
+ REQUEST_HAS_BODY = false
+ RESPONSE_HAS_BODY = true
+end
+
+# \Class for representing
+# {HTTP method HEAD}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#HEAD_method]:
+#
+# require 'rubygems/vendor/net-http/lib/net/http'
+# uri = Gem::URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Gem::Net::HTTP::Head.new(uri) # => #<Gem::Net::HTTP::Head HEAD>
+# res = Gem::Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
+#
+# Properties:
+#
+# - Request body: optional.
+# - Response body: no.
+# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: yes.
+# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
+# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: yes.
+#
+# Related:
+#
+# - Gem::Net::HTTP#head: sends +HEAD+ request, returns response object.
+#
+class Gem::Net::HTTP::Head < Gem::Net::HTTPRequest
+ METHOD = 'HEAD'
+ REQUEST_HAS_BODY = false
+ RESPONSE_HAS_BODY = false
+end
+
+# \Class for representing
+# {HTTP method POST}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#POST_method]:
+#
+# require 'rubygems/vendor/net-http/lib/net/http'
+# uri = Gem::URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# uri.path = '/posts'
+# req = Gem::Net::HTTP::Post.new(uri) # => #<Gem::Net::HTTP::Post POST>
+# req.body = '{"title": "foo","body": "bar","userId": 1}'
+# req.content_type = 'application/json'
+# res = Gem::Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
+#
+# Properties:
+#
+# - Request body: yes.
+# - Response body: yes.
+# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: no.
+# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: no.
+# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: yes.
+#
+# Related:
+#
+# - Gem::Net::HTTP.post: sends +POST+ request, returns response object.
+# - Gem::Net::HTTP#post: sends +POST+ request, returns response object.
+#
+class Gem::Net::HTTP::Post < Gem::Net::HTTPRequest
+ METHOD = 'POST'
+ REQUEST_HAS_BODY = true
+ RESPONSE_HAS_BODY = true
+end
+
+# \Class for representing
+# {HTTP method PUT}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#PUT_method]:
+#
+# require 'rubygems/vendor/net-http/lib/net/http'
+# uri = Gem::URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# uri.path = '/posts'
+# req = Gem::Net::HTTP::Put.new(uri) # => #<Gem::Net::HTTP::Put PUT>
+# req.body = '{"title": "foo","body": "bar","userId": 1}'
+# req.content_type = 'application/json'
+# res = Gem::Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
+#
+# Properties:
+#
+# - Request body: yes.
+# - Response body: yes.
+# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: no.
+# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
+# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
+#
+class Gem::Net::HTTP::Put < Gem::Net::HTTPRequest
+ METHOD = 'PUT'
+ REQUEST_HAS_BODY = true
+ RESPONSE_HAS_BODY = true
+end
+
+# \Class for representing
+# {HTTP method DELETE}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#DELETE_method]:
+#
+# require 'rubygems/vendor/net-http/lib/net/http'
+# uri = Gem::URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# uri.path = '/posts/1'
+# req = Gem::Net::HTTP::Delete.new(uri) # => #<Gem::Net::HTTP::Delete DELETE>
+# res = Gem::Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
+#
+# Properties:
+#
+# - Request body: optional.
+# - Response body: yes.
+# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: no.
+# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
+# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
+#
+# Related:
+#
+# - Gem::Net::HTTP#delete: sends +DELETE+ request, returns response object.
+#
+class Gem::Net::HTTP::Delete < Gem::Net::HTTPRequest
+ METHOD = 'DELETE'
+ REQUEST_HAS_BODY = false
+ RESPONSE_HAS_BODY = true
+end
+
+# \Class for representing
+# {HTTP method OPTIONS}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#OPTIONS_method]:
+#
+# require 'rubygems/vendor/net-http/lib/net/http'
+# uri = Gem::URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Gem::Net::HTTP::Options.new(uri) # => #<Gem::Net::HTTP::Options OPTIONS>
+# res = Gem::Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
+#
+# Properties:
+#
+# - Request body: optional.
+# - Response body: yes.
+# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: yes.
+# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
+# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
+#
+# Related:
+#
+# - Gem::Net::HTTP#options: sends +OPTIONS+ request, returns response object.
+#
+class Gem::Net::HTTP::Options < Gem::Net::HTTPRequest
+ METHOD = 'OPTIONS'
+ REQUEST_HAS_BODY = false
+ RESPONSE_HAS_BODY = true
+end
+
+# \Class for representing
+# {HTTP method TRACE}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#TRACE_method]:
+#
+# require 'rubygems/vendor/net-http/lib/net/http'
+# uri = Gem::URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Gem::Net::HTTP::Trace.new(uri) # => #<Gem::Net::HTTP::Trace TRACE>
+# res = Gem::Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
+#
+# Properties:
+#
+# - Request body: no.
+# - Response body: yes.
+# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: yes.
+# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
+# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
+#
+# Related:
+#
+# - Gem::Net::HTTP#trace: sends +TRACE+ request, returns response object.
+#
+class Gem::Net::HTTP::Trace < Gem::Net::HTTPRequest
+ METHOD = 'TRACE'
+ REQUEST_HAS_BODY = false
+ RESPONSE_HAS_BODY = true
+end
+
+# \Class for representing
+# {HTTP method PATCH}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#PATCH_method]:
+#
+# require 'rubygems/vendor/net-http/lib/net/http'
+# uri = Gem::URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# uri.path = '/posts'
+# req = Gem::Net::HTTP::Patch.new(uri) # => #<Gem::Net::HTTP::Patch PATCH>
+# req.body = '{"title": "foo","body": "bar","userId": 1}'
+# req.content_type = 'application/json'
+# res = Gem::Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
+#
+# Properties:
+#
+# - Request body: yes.
+# - Response body: yes.
+# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: no.
+# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: no.
+# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
+#
+# Related:
+#
+# - Gem::Net::HTTP#patch: sends +PATCH+ request, returns response object.
+#
+class Gem::Net::HTTP::Patch < Gem::Net::HTTPRequest
+ METHOD = 'PATCH'
+ REQUEST_HAS_BODY = true
+ RESPONSE_HAS_BODY = true
+end
+
+#
+# WebDAV methods --- RFC2518
+#
+
+# \Class for representing
+# {WebDAV method PROPFIND}[http://www.webdav.org/specs/rfc4918.html#METHOD_PROPFIND]:
+#
+# require 'rubygems/vendor/net-http/lib/net/http'
+# uri = Gem::URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Gem::Net::HTTP::Propfind.new(uri) # => #<Gem::Net::HTTP::Propfind PROPFIND>
+# res = Gem::Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
+#
+# Related:
+#
+# - Gem::Net::HTTP#propfind: sends +PROPFIND+ request, returns response object.
+#
+class Gem::Net::HTTP::Propfind < Gem::Net::HTTPRequest
+ METHOD = 'PROPFIND'
+ REQUEST_HAS_BODY = true
+ RESPONSE_HAS_BODY = true
+end
+
+# \Class for representing
+# {WebDAV method PROPPATCH}[http://www.webdav.org/specs/rfc4918.html#METHOD_PROPPATCH]:
+#
+# require 'rubygems/vendor/net-http/lib/net/http'
+# uri = Gem::URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Gem::Net::HTTP::Proppatch.new(uri) # => #<Gem::Net::HTTP::Proppatch PROPPATCH>
+# res = Gem::Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
+#
+# Related:
+#
+# - Gem::Net::HTTP#proppatch: sends +PROPPATCH+ request, returns response object.
+#
+class Gem::Net::HTTP::Proppatch < Gem::Net::HTTPRequest
+ METHOD = 'PROPPATCH'
+ REQUEST_HAS_BODY = true
+ RESPONSE_HAS_BODY = true
+end
+
+# \Class for representing
+# {WebDAV method MKCOL}[http://www.webdav.org/specs/rfc4918.html#METHOD_MKCOL]:
+#
+# require 'rubygems/vendor/net-http/lib/net/http'
+# uri = Gem::URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Gem::Net::HTTP::Mkcol.new(uri) # => #<Gem::Net::HTTP::Mkcol MKCOL>
+# res = Gem::Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
+#
+# Related:
+#
+# - Gem::Net::HTTP#mkcol: sends +MKCOL+ request, returns response object.
+#
+class Gem::Net::HTTP::Mkcol < Gem::Net::HTTPRequest
+ METHOD = 'MKCOL'
+ REQUEST_HAS_BODY = true
+ RESPONSE_HAS_BODY = true
+end
+
+# \Class for representing
+# {WebDAV method COPY}[http://www.webdav.org/specs/rfc4918.html#METHOD_COPY]:
+#
+# require 'rubygems/vendor/net-http/lib/net/http'
+# uri = Gem::URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Gem::Net::HTTP::Copy.new(uri) # => #<Gem::Net::HTTP::Copy COPY>
+# res = Gem::Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
+#
+# Related:
+#
+# - Gem::Net::HTTP#copy: sends +COPY+ request, returns response object.
+#
+class Gem::Net::HTTP::Copy < Gem::Net::HTTPRequest
+ METHOD = 'COPY'
+ REQUEST_HAS_BODY = false
+ RESPONSE_HAS_BODY = true
+end
+
+# \Class for representing
+# {WebDAV method MOVE}[http://www.webdav.org/specs/rfc4918.html#METHOD_MOVE]:
+#
+# require 'rubygems/vendor/net-http/lib/net/http'
+# uri = Gem::URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Gem::Net::HTTP::Move.new(uri) # => #<Gem::Net::HTTP::Move MOVE>
+# res = Gem::Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
+#
+# Related:
+#
+# - Gem::Net::HTTP#move: sends +MOVE+ request, returns response object.
+#
+class Gem::Net::HTTP::Move < Gem::Net::HTTPRequest
+ METHOD = 'MOVE'
+ REQUEST_HAS_BODY = false
+ RESPONSE_HAS_BODY = true
+end
+
+# \Class for representing
+# {WebDAV method LOCK}[http://www.webdav.org/specs/rfc4918.html#METHOD_LOCK]:
+#
+# require 'rubygems/vendor/net-http/lib/net/http'
+# uri = Gem::URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Gem::Net::HTTP::Lock.new(uri) # => #<Gem::Net::HTTP::Lock LOCK>
+# res = Gem::Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
+#
+# Related:
+#
+# - Gem::Net::HTTP#lock: sends +LOCK+ request, returns response object.
+#
+class Gem::Net::HTTP::Lock < Gem::Net::HTTPRequest
+ METHOD = 'LOCK'
+ REQUEST_HAS_BODY = true
+ RESPONSE_HAS_BODY = true
+end
+
+# \Class for representing
+# {WebDAV method UNLOCK}[http://www.webdav.org/specs/rfc4918.html#METHOD_UNLOCK]:
+#
+# require 'rubygems/vendor/net-http/lib/net/http'
+# uri = Gem::URI('http://example.com')
+# hostname = uri.hostname # => "example.com"
+# req = Gem::Net::HTTP::Unlock.new(uri) # => #<Gem::Net::HTTP::Unlock UNLOCK>
+# res = Gem::Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end
+#
+# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
+#
+# Related:
+#
+# - Gem::Net::HTTP#unlock: sends +UNLOCK+ request, returns response object.
+#
+class Gem::Net::HTTP::Unlock < Gem::Net::HTTPRequest
+ METHOD = 'UNLOCK'
+ REQUEST_HAS_BODY = true
+ RESPONSE_HAS_BODY = true
+end
+
diff --git a/lib/rubygems/vendor/net-http/lib/net/http/response.rb b/lib/rubygems/vendor/net-http/lib/net/http/response.rb
new file mode 100644
index 0000000000..cbbd191d87
--- /dev/null
+++ b/lib/rubygems/vendor/net-http/lib/net/http/response.rb
@@ -0,0 +1,738 @@
+# frozen_string_literal: true
+
+# This class is the base class for \Gem::Net::HTTP response classes.
+#
+# == About the Examples
+#
+# :include: doc/net-http/examples.rdoc
+#
+# == Returned Responses
+#
+# \Method Gem::Net::HTTP.get_response returns
+# an instance of one of the subclasses of \Gem::Net::HTTPResponse:
+#
+# Gem::Net::HTTP.get_response(uri)
+# # => #<Gem::Net::HTTPOK 200 OK readbody=true>
+# Gem::Net::HTTP.get_response(hostname, '/nosuch')
+# # => #<Gem::Net::HTTPNotFound 404 Not Found readbody=true>
+#
+# As does method Gem::Net::HTTP#request:
+#
+# req = Gem::Net::HTTP::Get.new(uri)
+# Gem::Net::HTTP.start(hostname) do |http|
+# http.request(req)
+# end # => #<Gem::Net::HTTPOK 200 OK readbody=true>
+#
+# \Class \Gem::Net::HTTPResponse includes module Gem::Net::HTTPHeader,
+# which provides access to response header values via (among others):
+#
+# - \Hash-like method <tt>[]</tt>.
+# - Specific reader methods, such as +content_type+.
+#
+# Examples:
+#
+# res = Gem::Net::HTTP.get_response(uri) # => #<Gem::Net::HTTPOK 200 OK readbody=true>
+# res['Content-Type'] # => "text/html; charset=UTF-8"
+# res.content_type # => "text/html"
+#
+# == Response Subclasses
+#
+# \Class \Gem::Net::HTTPResponse has a subclass for each
+# {HTTP status code}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes].
+# You can look up the response class for a given code:
+#
+# Gem::Net::HTTPResponse::CODE_TO_OBJ['200'] # => Gem::Net::HTTPOK
+# Gem::Net::HTTPResponse::CODE_TO_OBJ['400'] # => Gem::Net::HTTPBadRequest
+# Gem::Net::HTTPResponse::CODE_TO_OBJ['404'] # => Gem::Net::HTTPNotFound
+#
+# And you can retrieve the status code for a response object:
+#
+# Gem::Net::HTTP.get_response(uri).code # => "200"
+# Gem::Net::HTTP.get_response(hostname, '/nosuch').code # => "404"
+#
+# The response subclasses (indentation shows class hierarchy):
+#
+# - Gem::Net::HTTPUnknownResponse (for unhandled \HTTP extensions).
+#
+# - Gem::Net::HTTPInformation:
+#
+# - Gem::Net::HTTPContinue (100)
+# - Gem::Net::HTTPSwitchProtocol (101)
+# - Gem::Net::HTTPProcessing (102)
+# - Gem::Net::HTTPEarlyHints (103)
+#
+# - Gem::Net::HTTPSuccess:
+#
+# - Gem::Net::HTTPOK (200)
+# - Gem::Net::HTTPCreated (201)
+# - Gem::Net::HTTPAccepted (202)
+# - Gem::Net::HTTPNonAuthoritativeInformation (203)
+# - Gem::Net::HTTPNoContent (204)
+# - Gem::Net::HTTPResetContent (205)
+# - Gem::Net::HTTPPartialContent (206)
+# - Gem::Net::HTTPMultiStatus (207)
+# - Gem::Net::HTTPAlreadyReported (208)
+# - Gem::Net::HTTPIMUsed (226)
+#
+# - Gem::Net::HTTPRedirection:
+#
+# - Gem::Net::HTTPMultipleChoices (300)
+# - Gem::Net::HTTPMovedPermanently (301)
+# - Gem::Net::HTTPFound (302)
+# - Gem::Net::HTTPSeeOther (303)
+# - Gem::Net::HTTPNotModified (304)
+# - Gem::Net::HTTPUseProxy (305)
+# - Gem::Net::HTTPTemporaryRedirect (307)
+# - Gem::Net::HTTPPermanentRedirect (308)
+#
+# - Gem::Net::HTTPClientError:
+#
+# - Gem::Net::HTTPBadRequest (400)
+# - Gem::Net::HTTPUnauthorized (401)
+# - Gem::Net::HTTPPaymentRequired (402)
+# - Gem::Net::HTTPForbidden (403)
+# - Gem::Net::HTTPNotFound (404)
+# - Gem::Net::HTTPMethodNotAllowed (405)
+# - Gem::Net::HTTPNotAcceptable (406)
+# - Gem::Net::HTTPProxyAuthenticationRequired (407)
+# - Gem::Net::HTTPRequestTimeOut (408)
+# - Gem::Net::HTTPConflict (409)
+# - Gem::Net::HTTPGone (410)
+# - Gem::Net::HTTPLengthRequired (411)
+# - Gem::Net::HTTPPreconditionFailed (412)
+# - Gem::Net::HTTPRequestEntityTooLarge (413)
+# - Gem::Net::HTTPRequestURITooLong (414)
+# - Gem::Net::HTTPUnsupportedMediaType (415)
+# - Gem::Net::HTTPRequestedRangeNotSatisfiable (416)
+# - Gem::Net::HTTPExpectationFailed (417)
+# - Gem::Net::HTTPMisdirectedRequest (421)
+# - Gem::Net::HTTPUnprocessableEntity (422)
+# - Gem::Net::HTTPLocked (423)
+# - Gem::Net::HTTPFailedDependency (424)
+# - Gem::Net::HTTPUpgradeRequired (426)
+# - Gem::Net::HTTPPreconditionRequired (428)
+# - Gem::Net::HTTPTooManyRequests (429)
+# - Gem::Net::HTTPRequestHeaderFieldsTooLarge (431)
+# - Gem::Net::HTTPUnavailableForLegalReasons (451)
+#
+# - Gem::Net::HTTPServerError:
+#
+# - Gem::Net::HTTPInternalServerError (500)
+# - Gem::Net::HTTPNotImplemented (501)
+# - Gem::Net::HTTPBadGateway (502)
+# - Gem::Net::HTTPServiceUnavailable (503)
+# - Gem::Net::HTTPGatewayTimeOut (504)
+# - Gem::Net::HTTPVersionNotSupported (505)
+# - Gem::Net::HTTPVariantAlsoNegotiates (506)
+# - Gem::Net::HTTPInsufficientStorage (507)
+# - Gem::Net::HTTPLoopDetected (508)
+# - Gem::Net::HTTPNotExtended (510)
+# - Gem::Net::HTTPNetworkAuthenticationRequired (511)
+#
+# There is also the Gem::Net::HTTPBadResponse exception which is raised when
+# there is a protocol error.
+#
+class Gem::Net::HTTPResponse
+ class << self
+ # true if the response has a body.
+ def body_permitted?
+ self::HAS_BODY
+ end
+
+ def exception_type # :nodoc: internal use only
+ self::EXCEPTION_TYPE
+ end
+
+ def read_new(sock) #:nodoc: internal use only
+ httpv, code, msg = read_status_line(sock)
+ res = response_class(code).new(httpv, code, msg)
+ each_response_header(sock) do |k,v|
+ res.add_field k, v
+ end
+ res
+ end
+
+ private
+
+ def read_status_line(sock)
+ str = sock.readline
+ m = /\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)(?:\s+(.*))?\z/in.match(str) or
+ raise Gem::Net::HTTPBadResponse, "wrong status line: #{str.dump}"
+ m.captures
+ end
+
+ def response_class(code)
+ CODE_TO_OBJ[code] or
+ CODE_CLASS_TO_OBJ[code[0,1]] or
+ Gem::Net::HTTPUnknownResponse
+ end
+
+ def each_response_header(sock)
+ key = value = nil
+ while true
+ line = sock.readuntil("\n", true).sub(/\s+\z/, '')
+ break if line.empty?
+ if line[0] == ?\s or line[0] == ?\t and value
+ value << ' ' unless value.empty?
+ value << line.strip
+ else
+ yield key, value if key
+ key, value = line.strip.split(/\s*:\s*/, 2)
+ raise Gem::Net::HTTPBadResponse, 'wrong header line format' if value.nil?
+ end
+ end
+ yield key, value if key
+ end
+ end
+
+ # next is to fix bug in RDoc, where the private inside class << self
+ # spills out.
+ public
+
+ include Gem::Net::HTTPHeader
+
+ def initialize(httpv, code, msg) #:nodoc: internal use only
+ @http_version = httpv
+ @code = code
+ @message = msg
+ initialize_http_header nil
+ @body = nil
+ @read = false
+ @uri = nil
+ @decode_content = false
+ @body_encoding = false
+ @ignore_eof = true
+ end
+
+ # The HTTP version supported by the server.
+ attr_reader :http_version
+
+ # The HTTP result code string. For example, '302'. You can also
+ # determine the response type by examining which response subclass
+ # the response object is an instance of.
+ attr_reader :code
+
+ # The HTTP result message sent by the server. For example, 'Not Found'.
+ attr_reader :message
+ alias msg message # :nodoc: obsolete
+
+ # The Gem::URI used to fetch this response. The response Gem::URI is only available
+ # if a Gem::URI was used to create the request.
+ attr_reader :uri
+
+ # Set to true automatically when the request did not contain an
+ # Accept-Encoding header from the user.
+ attr_accessor :decode_content
+
+ # Returns the value set by body_encoding=, or +false+ if none;
+ # see #body_encoding=.
+ attr_reader :body_encoding
+
+ # Sets the encoding that should be used when reading the body:
+ #
+ # - If the given value is an Encoding object, that encoding will be used.
+ # - Otherwise if the value is a string, the value of
+ # {Encoding#find(value)}[https://docs.ruby-lang.org/en/master/Encoding.html#method-c-find]
+ # will be used.
+ # - Otherwise an encoding will be deduced from the body itself.
+ #
+ # Examples:
+ #
+ # http = Gem::Net::HTTP.new(hostname)
+ # req = Gem::Net::HTTP::Get.new('/')
+ #
+ # http.request(req) do |res|
+ # p res.body.encoding # => #<Encoding:ASCII-8BIT>
+ # end
+ #
+ # http.request(req) do |res|
+ # res.body_encoding = "UTF-8"
+ # p res.body.encoding # => #<Encoding:UTF-8>
+ # end
+ #
+ def body_encoding=(value)
+ value = Encoding.find(value) if value.is_a?(String)
+ @body_encoding = value
+ end
+
+ # Whether to ignore EOF when reading bodies with a specified Content-Length
+ # header.
+ attr_accessor :ignore_eof
+
+ def inspect
+ "#<#{self.class} #{@code} #{@message} readbody=#{@read}>"
+ end
+
+ #
+ # response <-> exception relationship
+ #
+
+ def code_type #:nodoc:
+ self.class
+ end
+
+ def error! #:nodoc:
+ message = @code
+ message = "#{message} #{@message.dump}" if @message
+ raise error_type().new(message, self)
+ end
+
+ def error_type #:nodoc:
+ self.class::EXCEPTION_TYPE
+ end
+
+ # Raises an HTTP error if the response is not 2xx (success).
+ def value
+ error! unless self.kind_of?(Gem::Net::HTTPSuccess)
+ end
+
+ def uri= uri # :nodoc:
+ @uri = uri.dup if uri
+ end
+
+ #
+ # header (for backward compatibility only; DO NOT USE)
+ #
+
+ def response #:nodoc:
+ warn "Gem::Net::HTTPResponse#response is obsolete", uplevel: 1 if $VERBOSE
+ self
+ end
+
+ def header #:nodoc:
+ warn "Gem::Net::HTTPResponse#header is obsolete", uplevel: 1 if $VERBOSE
+ self
+ end
+
+ def read_header #:nodoc:
+ warn "Gem::Net::HTTPResponse#read_header is obsolete", uplevel: 1 if $VERBOSE
+ self
+ end
+
+ #
+ # body
+ #
+
+ def reading_body(sock, reqmethodallowbody) #:nodoc: internal use only
+ @socket = sock
+ @body_exist = reqmethodallowbody && self.class.body_permitted?
+ begin
+ yield
+ self.body # ensure to read body
+ ensure
+ @socket = nil
+ end
+ end
+
+ # Gets the entity body returned by the remote HTTP server.
+ #
+ # If a block is given, the body is passed to the block, and
+ # the body is provided in fragments, as it is read in from the socket.
+ #
+ # If +dest+ argument is given, response is read into that variable,
+ # with <code>dest#<<</code> method (it could be String or IO, or any
+ # other object responding to <code><<</code>).
+ #
+ # Calling this method a second or subsequent time for the same
+ # HTTPResponse object will return the value already read.
+ #
+ # http.request_get('/index.html') {|res|
+ # puts res.read_body
+ # }
+ #
+ # http.request_get('/index.html') {|res|
+ # p res.read_body.object_id # 538149362
+ # p res.read_body.object_id # 538149362
+ # }
+ #
+ # # using iterator
+ # http.request_get('/index.html') {|res|
+ # res.read_body do |segment|
+ # print segment
+ # end
+ # }
+ #
+ def read_body(dest = nil, &block)
+ if @read
+ raise IOError, "#{self.class}\#read_body called twice" if dest or block
+ return @body
+ end
+ to = procdest(dest, block)
+ stream_check
+ if @body_exist
+ read_body_0 to
+ @body = to
+ else
+ @body = nil
+ end
+ @read = true
+ return if @body.nil?
+
+ case enc = @body_encoding
+ when Encoding, false, nil
+ # Encoding: force given encoding
+ # false/nil: do not force encoding
+ else
+ # other value: detect encoding from body
+ enc = detect_encoding(@body)
+ end
+
+ @body.force_encoding(enc) if enc
+
+ @body
+ end
+
+ # Returns the string response body;
+ # note that repeated calls for the unmodified body return a cached string:
+ #
+ # path = '/todos/1'
+ # Gem::Net::HTTP.start(hostname) do |http|
+ # res = http.get(path)
+ # p res.body
+ # p http.head(path).body # No body.
+ # end
+ #
+ # Output:
+ #
+ # "{\n \"userId\": 1,\n \"id\": 1,\n \"title\": \"delectus aut autem\",\n \"completed\": false\n}"
+ # nil
+ #
+ def body
+ read_body()
+ end
+
+ # Sets the body of the response to the given value.
+ def body=(value)
+ @body = value
+ end
+
+ alias entity body #:nodoc: obsolete
+
+ private
+
+ # :nodoc:
+ def detect_encoding(str, encoding=nil)
+ if encoding
+ elsif encoding = type_params['charset']
+ elsif encoding = check_bom(str)
+ else
+ encoding = case content_type&.downcase
+ when %r{text/x(?:ht)?ml|application/(?:[^+]+\+)?xml}
+ /\A<xml[ \t\r\n]+
+ version[ \t\r\n]*=[ \t\r\n]*(?:"[0-9.]+"|'[0-9.]*')[ \t\r\n]+
+ encoding[ \t\r\n]*=[ \t\r\n]*
+ (?:"([A-Za-z][\-A-Za-z0-9._]*)"|'([A-Za-z][\-A-Za-z0-9._]*)')/x =~ str
+ encoding = $1 || $2 || Encoding::UTF_8
+ when %r{text/html.*}
+ sniff_encoding(str)
+ end
+ end
+ return encoding
+ end
+
+ # :nodoc:
+ def sniff_encoding(str, encoding=nil)
+ # the encoding sniffing algorithm
+ # http://www.w3.org/TR/html5/parsing.html#determining-the-character-encoding
+ if enc = scanning_meta(str)
+ enc
+ # 6. last visited page or something
+ # 7. frequency
+ elsif str.ascii_only?
+ Encoding::US_ASCII
+ elsif str.dup.force_encoding(Encoding::UTF_8).valid_encoding?
+ Encoding::UTF_8
+ end
+ # 8. implementation-defined or user-specified
+ end
+
+ # :nodoc:
+ def check_bom(str)
+ case str.byteslice(0, 2)
+ when "\xFE\xFF"
+ return Encoding::UTF_16BE
+ when "\xFF\xFE"
+ return Encoding::UTF_16LE
+ end
+ if "\xEF\xBB\xBF" == str.byteslice(0, 3)
+ return Encoding::UTF_8
+ end
+ nil
+ end
+
+ # :nodoc:
+ def scanning_meta(str)
+ require 'strscan'
+ ss = StringScanner.new(str)
+ if ss.scan_until(/<meta[\t\n\f\r ]*/)
+ attrs = {} # attribute_list
+ got_pragma = false
+ need_pragma = nil
+ charset = nil
+
+ # step: Attributes
+ while attr = get_attribute(ss)
+ name, value = *attr
+ next if attrs[name]
+ attrs[name] = true
+ case name
+ when 'http-equiv'
+ got_pragma = true if value == 'content-type'
+ when 'content'
+ encoding = extracting_encodings_from_meta_elements(value)
+ unless charset
+ charset = encoding
+ end
+ need_pragma = true
+ when 'charset'
+ need_pragma = false
+ charset = value
+ end
+ end
+
+ # step: Processing
+ return if need_pragma.nil?
+ return if need_pragma && !got_pragma
+
+ charset = Encoding.find(charset) rescue nil
+ return unless charset
+ charset = Encoding::UTF_8 if charset == Encoding::UTF_16
+ return charset # tentative
+ end
+ nil
+ end
+
+ def get_attribute(ss)
+ ss.scan(/[\t\n\f\r \/]*/)
+ if ss.peek(1) == '>'
+ ss.getch
+ return nil
+ end
+ name = ss.scan(/[^=\t\n\f\r \/>]*/)
+ name.downcase!
+ raise if name.empty?
+ ss.skip(/[\t\n\f\r ]*/)
+ if ss.getch != '='
+ value = ''
+ return [name, value]
+ end
+ ss.skip(/[\t\n\f\r ]*/)
+ case ss.peek(1)
+ when '"'
+ ss.getch
+ value = ss.scan(/[^"]+/)
+ value.downcase!
+ ss.getch
+ when "'"
+ ss.getch
+ value = ss.scan(/[^']+/)
+ value.downcase!
+ ss.getch
+ when '>'
+ value = ''
+ else
+ value = ss.scan(/[^\t\n\f\r >]+/)
+ value.downcase!
+ end
+ [name, value]
+ end
+
+ def extracting_encodings_from_meta_elements(value)
+ # http://dev.w3.org/html5/spec/fetching-resources.html#algorithm-for-extracting-an-encoding-from-a-meta-element
+ if /charset[\t\n\f\r ]*=(?:"([^"]*)"|'([^']*)'|["']|\z|([^\t\n\f\r ;]+))/i =~ value
+ return $1 || $2 || $3
+ end
+ return nil
+ end
+
+ ##
+ # Checks for a supported Content-Encoding header and yields an Inflate
+ # wrapper for this response's socket when zlib is present. If the
+ # Content-Encoding is not supported or zlib is missing, the plain socket is
+ # yielded.
+ #
+ # If a Content-Range header is present, a plain socket is yielded as the
+ # bytes in the range may not be a complete deflate block.
+
+ def inflater # :nodoc:
+ return yield @socket unless Gem::Net::HTTP::HAVE_ZLIB
+ return yield @socket unless @decode_content
+ return yield @socket if self['content-range']
+
+ v = self['content-encoding']
+ case v&.downcase
+ when 'deflate', 'gzip', 'x-gzip' then
+ self.delete 'content-encoding'
+
+ inflate_body_io = Inflater.new(@socket)
+
+ begin
+ yield inflate_body_io
+ success = true
+ ensure
+ begin
+ inflate_body_io.finish
+ if self['content-length']
+ self['content-length'] = inflate_body_io.bytes_inflated.to_s
+ end
+ rescue => err
+ # Ignore #finish's error if there is an exception from yield
+ raise err if success
+ end
+ end
+ when 'none', 'identity' then
+ self.delete 'content-encoding'
+
+ yield @socket
+ else
+ yield @socket
+ end
+ end
+
+ def read_body_0(dest)
+ inflater do |inflate_body_io|
+ if chunked?
+ read_chunked dest, inflate_body_io
+ return
+ end
+
+ @socket = inflate_body_io
+
+ clen = content_length()
+ if clen
+ @socket.read clen, dest, @ignore_eof
+ return
+ end
+ clen = range_length()
+ if clen
+ @socket.read clen, dest
+ return
+ end
+ @socket.read_all dest
+ end
+ end
+
+ ##
+ # read_chunked reads from +@socket+ for chunk-size, chunk-extension, CRLF,
+ # etc. and +chunk_data_io+ for chunk-data which may be deflate or gzip
+ # encoded.
+ #
+ # See RFC 2616 section 3.6.1 for definitions
+
+ def read_chunked(dest, chunk_data_io) # :nodoc:
+ total = 0
+ while true
+ line = @socket.readline
+ hexlen = line.slice(/[0-9a-fA-F]+/) or
+ raise Gem::Net::HTTPBadResponse, "wrong chunk size line: #{line}"
+ len = hexlen.hex
+ break if len == 0
+ begin
+ chunk_data_io.read len, dest
+ ensure
+ total += len
+ @socket.read 2 # \r\n
+ end
+ end
+ until @socket.readline.empty?
+ # none
+ end
+ end
+
+ def stream_check
+ raise IOError, 'attempt to read body out of block' if @socket.nil? || @socket.closed?
+ end
+
+ def procdest(dest, block)
+ raise ArgumentError, 'both arg and block given for HTTP method' if
+ dest and block
+ if block
+ Gem::Net::ReadAdapter.new(block)
+ else
+ dest || +''
+ end
+ end
+
+ ##
+ # Inflater is a wrapper around Gem::Net::BufferedIO that transparently inflates
+ # zlib and gzip streams.
+
+ class Inflater # :nodoc:
+
+ ##
+ # Creates a new Inflater wrapping +socket+
+
+ def initialize socket
+ @socket = socket
+ # zlib with automatic gzip detection
+ @inflate = Zlib::Inflate.new(32 + Zlib::MAX_WBITS)
+ end
+
+ ##
+ # Finishes the inflate stream.
+
+ def finish
+ return if @inflate.total_in == 0
+ @inflate.finish
+ end
+
+ ##
+ # The number of bytes inflated, used to update the Content-Length of
+ # the response.
+
+ def bytes_inflated
+ @inflate.total_out
+ end
+
+ ##
+ # Returns a Gem::Net::ReadAdapter that inflates each read chunk into +dest+.
+ #
+ # This allows a large response body to be inflated without storing the
+ # entire body in memory.
+
+ def inflate_adapter(dest)
+ if dest.respond_to?(:set_encoding)
+ dest.set_encoding(Encoding::ASCII_8BIT)
+ elsif dest.respond_to?(:force_encoding)
+ dest.force_encoding(Encoding::ASCII_8BIT)
+ end
+ block = proc do |compressed_chunk|
+ @inflate.inflate(compressed_chunk) do |chunk|
+ compressed_chunk.clear
+ dest << chunk
+ end
+ end
+
+ Gem::Net::ReadAdapter.new(block)
+ end
+
+ ##
+ # Reads +clen+ bytes from the socket, inflates them, then writes them to
+ # +dest+. +ignore_eof+ is passed down to Gem::Net::BufferedIO#read
+ #
+ # Unlike Gem::Net::BufferedIO#read, this method returns more than +clen+ bytes.
+ # At this time there is no way for a user of Gem::Net::HTTPResponse to read a
+ # specific number of bytes from the HTTP response body, so this internal
+ # API does not return the same number of bytes as were requested.
+ #
+ # See https://bugs.ruby-lang.org/issues/6492 for further discussion.
+
+ def read clen, dest, ignore_eof = false
+ temp_dest = inflate_adapter(dest)
+
+ @socket.read clen, temp_dest, ignore_eof
+ end
+
+ ##
+ # Reads the rest of the socket, inflates it, then writes it to +dest+.
+
+ def read_all dest
+ temp_dest = inflate_adapter(dest)
+
+ @socket.read_all temp_dest
+ end
+
+ end
+
+end
+
diff --git a/lib/rubygems/vendor/net-http/lib/net/http/responses.rb b/lib/rubygems/vendor/net-http/lib/net/http/responses.rb
new file mode 100644
index 0000000000..0f26ae6c26
--- /dev/null
+++ b/lib/rubygems/vendor/net-http/lib/net/http/responses.rb
@@ -0,0 +1,1174 @@
+# frozen_string_literal: true
+#--
+# https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
+
+module Gem::Net
+
+ class HTTPUnknownResponse < HTTPResponse
+ HAS_BODY = true
+ EXCEPTION_TYPE = HTTPError #
+ end
+
+ # Parent class for informational (1xx) HTTP response classes.
+ #
+ # An informational response indicates that the request was received and understood.
+ #
+ # References:
+ #
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#status.1xx].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#1xx_informational_response].
+ #
+ class HTTPInformation < HTTPResponse
+ HAS_BODY = false
+ EXCEPTION_TYPE = HTTPError #
+ end
+
+ # Parent class for success (2xx) HTTP response classes.
+ #
+ # A success response indicates the action requested by the client
+ # was received, understood, and accepted.
+ #
+ # References:
+ #
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#status.2xx].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#2xx_success].
+ #
+ class HTTPSuccess < HTTPResponse
+ HAS_BODY = true
+ EXCEPTION_TYPE = HTTPError #
+ end
+
+ # Parent class for redirection (3xx) HTTP response classes.
+ #
+ # A redirection response indicates the client must take additional action
+ # to complete the request.
+ #
+ # References:
+ #
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#status.3xx].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_redirection].
+ #
+ class HTTPRedirection < HTTPResponse
+ HAS_BODY = true
+ EXCEPTION_TYPE = HTTPRetriableError #
+ end
+
+ # Parent class for client error (4xx) HTTP response classes.
+ #
+ # A client error response indicates that the client may have caused an error.
+ #
+ # References:
+ #
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#status.4xx].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#4xx_client_errors].
+ #
+ class HTTPClientError < HTTPResponse
+ HAS_BODY = true
+ EXCEPTION_TYPE = HTTPClientException #
+ end
+
+ # Parent class for server error (5xx) HTTP response classes.
+ #
+ # A server error response indicates that the server failed to fulfill a request.
+ #
+ # References:
+ #
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#status.5xx].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#5xx_server_errors].
+ #
+ class HTTPServerError < HTTPResponse
+ HAS_BODY = true
+ EXCEPTION_TYPE = HTTPFatalError #
+ end
+
+ # Response class for +Continue+ responses (status code 100).
+ #
+ # A +Continue+ response indicates that the server has received the request headers.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/100].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-100-continue].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#100].
+ #
+ class HTTPContinue < HTTPInformation
+ HAS_BODY = false
+ end
+
+ # Response class for <tt>Switching Protocol</tt> responses (status code 101).
+ #
+ # The <tt>Switching Protocol<tt> response indicates that the server has received
+ # a request to switch protocols, and has agreed to do so.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/101].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-101-switching-protocols].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#101].
+ #
+ class HTTPSwitchProtocol < HTTPInformation
+ HAS_BODY = false
+ end
+
+ # Response class for +Processing+ responses (status code 102).
+ #
+ # The +Processing+ response indicates that the server has received
+ # and is processing the request, but no response is available yet.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {RFC 2518}[https://www.rfc-editor.org/rfc/rfc2518#section-10.1].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#102].
+ #
+ class HTTPProcessing < HTTPInformation
+ HAS_BODY = false
+ end
+
+ # Response class for <tt>Early Hints</tt> responses (status code 103).
+ #
+ # The <tt>Early Hints</tt> indicates that the server has received
+ # and is processing the request, and contains certain headers;
+ # the final response is not available yet.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/103].
+ # - {RFC 8297}[https://www.rfc-editor.org/rfc/rfc8297.html#section-2].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#103].
+ #
+ class HTTPEarlyHints < HTTPInformation
+ HAS_BODY = false
+ end
+
+ # Response class for +OK+ responses (status code 200).
+ #
+ # The +OK+ response indicates that the server has received
+ # a request and has responded successfully.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/200].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#200].
+ #
+ class HTTPOK < HTTPSuccess
+ HAS_BODY = true
+ end
+
+ # Response class for +Created+ responses (status code 201).
+ #
+ # The +Created+ response indicates that the server has received
+ # and has fulfilled a request to create a new resource.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/201].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-201-created].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#201].
+ #
+ class HTTPCreated < HTTPSuccess
+ HAS_BODY = true
+ end
+
+ # Response class for +Accepted+ responses (status code 202).
+ #
+ # The +Accepted+ response indicates that the server has received
+ # and is processing a request, but the processing has not yet been completed.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/202].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-202-accepted].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#202].
+ #
+ class HTTPAccepted < HTTPSuccess
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Non-Authoritative Information</tt> responses (status code 203).
+ #
+ # The <tt>Non-Authoritative Information</tt> response indicates that the server
+ # is a transforming proxy (such as a Web accelerator)
+ # that received a 200 OK response from its origin,
+ # and is returning a modified version of the origin's response.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/203].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-203-non-authoritative-infor].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#203].
+ #
+ class HTTPNonAuthoritativeInformation < HTTPSuccess
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>No Content</tt> responses (status code 204).
+ #
+ # The <tt>No Content</tt> response indicates that the server
+ # successfully processed the request, and is not returning any content.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/204].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-204-no-content].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#204].
+ #
+ class HTTPNoContent < HTTPSuccess
+ HAS_BODY = false
+ end
+
+ # Response class for <tt>Reset Content</tt> responses (status code 205).
+ #
+ # The <tt>Reset Content</tt> response indicates that the server
+ # successfully processed the request,
+ # asks that the client reset its document view, and is not returning any content.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/205].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-205-reset-content].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#205].
+ #
+ class HTTPResetContent < HTTPSuccess
+ HAS_BODY = false
+ end
+
+ # Response class for <tt>Partial Content</tt> responses (status code 206).
+ #
+ # The <tt>Partial Content</tt> response indicates that the server is delivering
+ # only part of the resource (byte serving)
+ # due to a Range header in the request.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/206].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-206-partial-content].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#206].
+ #
+ class HTTPPartialContent < HTTPSuccess
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Multi-Status (WebDAV)</tt> responses (status code 207).
+ #
+ # The <tt>Multi-Status (WebDAV)</tt> response indicates that the server
+ # has received the request,
+ # and that the message body can contain a number of separate response codes.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {RFC 4818}[https://www.rfc-editor.org/rfc/rfc4918#section-11.1].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#207].
+ #
+ class HTTPMultiStatus < HTTPSuccess
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Already Reported (WebDAV)</tt> responses (status code 208).
+ #
+ # The <tt>Already Reported (WebDAV)</tt> response indicates that the server
+ # has received the request,
+ # and that the members of a DAV binding have already been enumerated
+ # in a preceding part of the (multi-status) response,
+ # and are not being included again.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {RFC 5842}[https://www.rfc-editor.org/rfc/rfc5842.html#section-7.1].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#208].
+ #
+ class HTTPAlreadyReported < HTTPSuccess
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>IM Used</tt> responses (status code 226).
+ #
+ # The <tt>IM Used</tt> response indicates that the server has fulfilled a request
+ # for the resource, and the response is a representation of the result
+ # of one or more instance-manipulations applied to the current instance.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {RFC 3229}[https://www.rfc-editor.org/rfc/rfc3229.html#section-10.4.1].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#226].
+ #
+ class HTTPIMUsed < HTTPSuccess
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Multiple Choices</tt> responses (status code 300).
+ #
+ # The <tt>Multiple Choices</tt> response indicates that the server
+ # offers multiple options for the resource from which the client may choose.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/300].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-300-multiple-choices].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#300].
+ #
+ class HTTPMultipleChoices < HTTPRedirection
+ HAS_BODY = true
+ end
+ HTTPMultipleChoice = HTTPMultipleChoices
+
+ # Response class for <tt>Moved Permanently</tt> responses (status code 301).
+ #
+ # The <tt>Moved Permanently</tt> response indicates that links or records
+ # returning this response should be updated to use the given URL.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/301].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-301-moved-permanently].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#301].
+ #
+ class HTTPMovedPermanently < HTTPRedirection
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Found</tt> responses (status code 302).
+ #
+ # The <tt>Found</tt> response indicates that the client
+ # should look at (browse to) another URL.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-302-found].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#302].
+ #
+ class HTTPFound < HTTPRedirection
+ HAS_BODY = true
+ end
+ HTTPMovedTemporarily = HTTPFound
+
+ # Response class for <tt>See Other</tt> responses (status code 303).
+ #
+ # The response to the request can be found under another Gem::URI using the GET method.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/303].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-303-see-other].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#303].
+ #
+ class HTTPSeeOther < HTTPRedirection
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Not Modified</tt> responses (status code 304).
+ #
+ # Indicates that the resource has not been modified since the version
+ # specified by the request headers.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/304].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-304-not-modified].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#304].
+ #
+ class HTTPNotModified < HTTPRedirection
+ HAS_BODY = false
+ end
+
+ # Response class for <tt>Use Proxy</tt> responses (status code 305).
+ #
+ # The requested resource is available only through a proxy,
+ # whose address is provided in the response.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-305-use-proxy].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#305].
+ #
+ class HTTPUseProxy < HTTPRedirection
+ HAS_BODY = false
+ end
+
+ # Response class for <tt>Temporary Redirect</tt> responses (status code 307).
+ #
+ # The request should be repeated with another Gem::URI;
+ # however, future requests should still use the original Gem::URI.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/307].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-307-temporary-redirect].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#307].
+ #
+ class HTTPTemporaryRedirect < HTTPRedirection
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Permanent Redirect</tt> responses (status code 308).
+ #
+ # This and all future requests should be directed to the given Gem::URI.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/308].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-308-permanent-redirect].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#308].
+ #
+ class HTTPPermanentRedirect < HTTPRedirection
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Bad Request</tt> responses (status code 400).
+ #
+ # The server cannot or will not process the request due to an apparent client error.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#400].
+ #
+ class HTTPBadRequest < HTTPClientError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Unauthorized</tt> responses (status code 401).
+ #
+ # Authentication is required, but either was not provided or failed.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-401-unauthorized].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#401].
+ #
+ class HTTPUnauthorized < HTTPClientError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Payment Required</tt> responses (status code 402).
+ #
+ # Reserved for future use.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/402].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-402-payment-required].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#402].
+ #
+ class HTTPPaymentRequired < HTTPClientError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Forbidden</tt> responses (status code 403).
+ #
+ # The request contained valid data and was understood by the server,
+ # but the server is refusing action.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-403-forbidden].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#403].
+ #
+ class HTTPForbidden < HTTPClientError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Not Found</tt> responses (status code 404).
+ #
+ # The requested resource could not be found but may be available in the future.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#404].
+ #
+ class HTTPNotFound < HTTPClientError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Method Not Allowed</tt> responses (status code 405).
+ #
+ # The request method is not supported for the requested resource.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-405-method-not-allowed].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#405].
+ #
+ class HTTPMethodNotAllowed < HTTPClientError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Not Acceptable</tt> responses (status code 406).
+ #
+ # The requested resource is capable of generating only content
+ # that not acceptable according to the Accept headers sent in the request.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/406].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-406-not-acceptable].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#406].
+ #
+ class HTTPNotAcceptable < HTTPClientError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Proxy Authentication Required</tt> responses (status code 407).
+ #
+ # The client must first authenticate itself with the proxy.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/407].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-407-proxy-authentication-re].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#407].
+ #
+ class HTTPProxyAuthenticationRequired < HTTPClientError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Request Gem::Timeout</tt> responses (status code 408).
+ #
+ # The server timed out waiting for the request.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-408-request-timeout].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#408].
+ #
+ class HTTPRequestTimeout < HTTPClientError
+ HAS_BODY = true
+ end
+ HTTPRequestTimeOut = HTTPRequestTimeout
+
+ # Response class for <tt>Conflict</tt> responses (status code 409).
+ #
+ # The request could not be processed because of conflict in the current state of the resource.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/409].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-409-conflict].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#409].
+ #
+ class HTTPConflict < HTTPClientError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Gone</tt> responses (status code 410).
+ #
+ # The resource requested was previously in use but is no longer available
+ # and will not be available again.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/410].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-410-gone].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#410].
+ #
+ class HTTPGone < HTTPClientError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Length Required</tt> responses (status code 411).
+ #
+ # The request did not specify the length of its content,
+ # which is required by the requested resource.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/411].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-411-length-required].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#411].
+ #
+ class HTTPLengthRequired < HTTPClientError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Precondition Failed</tt> responses (status code 412).
+ #
+ # The server does not meet one of the preconditions
+ # specified in the request headers.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/412].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-412-precondition-failed].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#412].
+ #
+ class HTTPPreconditionFailed < HTTPClientError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Payload Too Large</tt> responses (status code 413).
+ #
+ # The request is larger than the server is willing or able to process.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/413].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-413-content-too-large].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#413].
+ #
+ class HTTPPayloadTooLarge < HTTPClientError
+ HAS_BODY = true
+ end
+ HTTPRequestEntityTooLarge = HTTPPayloadTooLarge
+
+ # Response class for <tt>Gem::URI Too Long</tt> responses (status code 414).
+ #
+ # The Gem::URI provided was too long for the server to process.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/414].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-414-uri-too-long].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#414].
+ #
+ class HTTPURITooLong < HTTPClientError
+ HAS_BODY = true
+ end
+ HTTPRequestURITooLong = HTTPURITooLong
+ HTTPRequestURITooLarge = HTTPRequestURITooLong
+
+ # Response class for <tt>Unsupported Media Type</tt> responses (status code 415).
+ #
+ # The request entity has a media type which the server or resource does not support.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/415].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-415-unsupported-media-type].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#415].
+ #
+ class HTTPUnsupportedMediaType < HTTPClientError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Range Not Satisfiable</tt> responses (status code 416).
+ #
+ # The request entity has a media type which the server or resource does not support.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/416].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-416-range-not-satisfiable].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#416].
+ #
+ class HTTPRangeNotSatisfiable < HTTPClientError
+ HAS_BODY = true
+ end
+ HTTPRequestedRangeNotSatisfiable = HTTPRangeNotSatisfiable
+
+ # Response class for <tt>Expectation Failed</tt> responses (status code 417).
+ #
+ # The server cannot meet the requirements of the Expect request-header field.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/417].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-417-expectation-failed].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#417].
+ #
+ class HTTPExpectationFailed < HTTPClientError
+ HAS_BODY = true
+ end
+
+ # 418 I'm a teapot - RFC 2324; a joke RFC
+ # See https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#418.
+
+ # 420 Enhance Your Calm - Twitter
+
+ # Response class for <tt>Misdirected Request</tt> responses (status code 421).
+ #
+ # The request was directed at a server that is not able to produce a response.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-421-misdirected-request].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#421].
+ #
+ class HTTPMisdirectedRequest < HTTPClientError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Unprocessable Entity</tt> responses (status code 422).
+ #
+ # The request was well-formed but had semantic errors.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-422-unprocessable-content].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#422].
+ #
+ class HTTPUnprocessableEntity < HTTPClientError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Locked (WebDAV)</tt> responses (status code 423).
+ #
+ # The requested resource is locked.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {RFC 4918}[https://www.rfc-editor.org/rfc/rfc4918#section-11.3].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#423].
+ #
+ class HTTPLocked < HTTPClientError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Failed Dependency (WebDAV)</tt> responses (status code 424).
+ #
+ # The request failed because it depended on another request and that request failed.
+ # See {424 Failed Dependency (WebDAV)}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#424].
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {RFC 4918}[https://www.rfc-editor.org/rfc/rfc4918#section-11.4].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#424].
+ #
+ class HTTPFailedDependency < HTTPClientError
+ HAS_BODY = true
+ end
+
+ # 425 Too Early
+ # https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#425.
+
+ # Response class for <tt>Upgrade Required</tt> responses (status code 426).
+ #
+ # The client should switch to the protocol given in the Upgrade header field.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/426].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-426-upgrade-required].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#426].
+ #
+ class HTTPUpgradeRequired < HTTPClientError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Precondition Required</tt> responses (status code 428).
+ #
+ # The origin server requires the request to be conditional.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/428].
+ # - {RFC 6585}[https://www.rfc-editor.org/rfc/rfc6585#section-3].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#428].
+ #
+ class HTTPPreconditionRequired < HTTPClientError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Too Many Requests</tt> responses (status code 429).
+ #
+ # The user has sent too many requests in a given amount of time.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429].
+ # - {RFC 6585}[https://www.rfc-editor.org/rfc/rfc6585#section-4].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#429].
+ #
+ class HTTPTooManyRequests < HTTPClientError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Request Header Fields Too Large</tt> responses (status code 431).
+ #
+ # An individual header field is too large,
+ # or all the header fields collectively, are too large.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/431].
+ # - {RFC 6585}[https://www.rfc-editor.org/rfc/rfc6585#section-5].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#431].
+ #
+ class HTTPRequestHeaderFieldsTooLarge < HTTPClientError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Unavailable For Legal Reasons</tt> responses (status code 451).
+ #
+ # A server operator has received a legal demand to deny access to a resource or to a set of resources
+ # that includes the requested resource.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/451].
+ # - {RFC 7725}[https://www.rfc-editor.org/rfc/rfc7725.html#section-3].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#451].
+ #
+ class HTTPUnavailableForLegalReasons < HTTPClientError
+ HAS_BODY = true
+ end
+ # 444 No Response - Nginx
+ # 449 Retry With - Microsoft
+ # 450 Blocked by Windows Parental Controls - Microsoft
+ # 499 Client Closed Request - Nginx
+
+ # Response class for <tt>Internal Server Error</tt> responses (status code 500).
+ #
+ # An unexpected condition was encountered and no more specific message is suitable.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-500-internal-server-error].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#500].
+ #
+ class HTTPInternalServerError < HTTPServerError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Not Implemented</tt> responses (status code 501).
+ #
+ # The server either does not recognize the request method,
+ # or it lacks the ability to fulfil the request.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/501].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-501-not-implemented].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#501].
+ #
+ class HTTPNotImplemented < HTTPServerError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Bad Gateway</tt> responses (status code 502).
+ #
+ # The server was acting as a gateway or proxy
+ # and received an invalid response from the upstream server.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-502-bad-gateway].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#502].
+ #
+ class HTTPBadGateway < HTTPServerError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Service Unavailable</tt> responses (status code 503).
+ #
+ # The server cannot handle the request
+ # (because it is overloaded or down for maintenance).
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/503].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-503-service-unavailable].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#503].
+ #
+ class HTTPServiceUnavailable < HTTPServerError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Gateway Gem::Timeout</tt> responses (status code 504).
+ #
+ # The server was acting as a gateway or proxy
+ # and did not receive a timely response from the upstream server.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/504].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-504-gateway-timeout].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#504].
+ #
+ class HTTPGatewayTimeout < HTTPServerError
+ HAS_BODY = true
+ end
+ HTTPGatewayTimeOut = HTTPGatewayTimeout
+
+ # Response class for <tt>HTTP Version Not Supported</tt> responses (status code 505).
+ #
+ # The server does not support the HTTP version used in the request.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/505].
+ # - {RFC 9110}[https://www.rfc-editor.org/rfc/rfc9110.html#name-505-http-version-not-suppor].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#505].
+ #
+ class HTTPVersionNotSupported < HTTPServerError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Variant Also Negotiates</tt> responses (status code 506).
+ #
+ # Transparent content negotiation for the request results in a circular reference.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/506].
+ # - {RFC 2295}[https://www.rfc-editor.org/rfc/rfc2295#section-8.1].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#506].
+ #
+ class HTTPVariantAlsoNegotiates < HTTPServerError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Insufficient Storage (WebDAV)</tt> responses (status code 507).
+ #
+ # The server is unable to store the representation needed to complete the request.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/507].
+ # - {RFC 4918}[https://www.rfc-editor.org/rfc/rfc4918#section-11.5].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#507].
+ #
+ class HTTPInsufficientStorage < HTTPServerError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Loop Detected (WebDAV)</tt> responses (status code 508).
+ #
+ # The server detected an infinite loop while processing the request.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/508].
+ # - {RFC 5942}[https://www.rfc-editor.org/rfc/rfc5842.html#section-7.2].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#508].
+ #
+ class HTTPLoopDetected < HTTPServerError
+ HAS_BODY = true
+ end
+ # 509 Bandwidth Limit Exceeded - Apache bw/limited extension
+
+ # Response class for <tt>Not Extended</tt> responses (status code 510).
+ #
+ # Further extensions to the request are required for the server to fulfill it.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/510].
+ # - {RFC 2774}[https://www.rfc-editor.org/rfc/rfc2774.html#section-7].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#510].
+ #
+ class HTTPNotExtended < HTTPServerError
+ HAS_BODY = true
+ end
+
+ # Response class for <tt>Network Authentication Required</tt> responses (status code 511).
+ #
+ # The client needs to authenticate to gain network access.
+ #
+ # :include: doc/net-http/included_getters.rdoc
+ #
+ # References:
+ #
+ # - {Mozilla}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/511].
+ # - {RFC 6585}[https://www.rfc-editor.org/rfc/rfc6585#section-6].
+ # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#511].
+ #
+ class HTTPNetworkAuthenticationRequired < HTTPServerError
+ HAS_BODY = true
+ end
+
+end
+
+class Gem::Net::HTTPResponse
+ CODE_CLASS_TO_OBJ = {
+ '1' => Gem::Net::HTTPInformation,
+ '2' => Gem::Net::HTTPSuccess,
+ '3' => Gem::Net::HTTPRedirection,
+ '4' => Gem::Net::HTTPClientError,
+ '5' => Gem::Net::HTTPServerError
+ }
+ CODE_TO_OBJ = {
+ '100' => Gem::Net::HTTPContinue,
+ '101' => Gem::Net::HTTPSwitchProtocol,
+ '102' => Gem::Net::HTTPProcessing,
+ '103' => Gem::Net::HTTPEarlyHints,
+
+ '200' => Gem::Net::HTTPOK,
+ '201' => Gem::Net::HTTPCreated,
+ '202' => Gem::Net::HTTPAccepted,
+ '203' => Gem::Net::HTTPNonAuthoritativeInformation,
+ '204' => Gem::Net::HTTPNoContent,
+ '205' => Gem::Net::HTTPResetContent,
+ '206' => Gem::Net::HTTPPartialContent,
+ '207' => Gem::Net::HTTPMultiStatus,
+ '208' => Gem::Net::HTTPAlreadyReported,
+ '226' => Gem::Net::HTTPIMUsed,
+
+ '300' => Gem::Net::HTTPMultipleChoices,
+ '301' => Gem::Net::HTTPMovedPermanently,
+ '302' => Gem::Net::HTTPFound,
+ '303' => Gem::Net::HTTPSeeOther,
+ '304' => Gem::Net::HTTPNotModified,
+ '305' => Gem::Net::HTTPUseProxy,
+ '307' => Gem::Net::HTTPTemporaryRedirect,
+ '308' => Gem::Net::HTTPPermanentRedirect,
+
+ '400' => Gem::Net::HTTPBadRequest,
+ '401' => Gem::Net::HTTPUnauthorized,
+ '402' => Gem::Net::HTTPPaymentRequired,
+ '403' => Gem::Net::HTTPForbidden,
+ '404' => Gem::Net::HTTPNotFound,
+ '405' => Gem::Net::HTTPMethodNotAllowed,
+ '406' => Gem::Net::HTTPNotAcceptable,
+ '407' => Gem::Net::HTTPProxyAuthenticationRequired,
+ '408' => Gem::Net::HTTPRequestTimeout,
+ '409' => Gem::Net::HTTPConflict,
+ '410' => Gem::Net::HTTPGone,
+ '411' => Gem::Net::HTTPLengthRequired,
+ '412' => Gem::Net::HTTPPreconditionFailed,
+ '413' => Gem::Net::HTTPPayloadTooLarge,
+ '414' => Gem::Net::HTTPURITooLong,
+ '415' => Gem::Net::HTTPUnsupportedMediaType,
+ '416' => Gem::Net::HTTPRangeNotSatisfiable,
+ '417' => Gem::Net::HTTPExpectationFailed,
+ '421' => Gem::Net::HTTPMisdirectedRequest,
+ '422' => Gem::Net::HTTPUnprocessableEntity,
+ '423' => Gem::Net::HTTPLocked,
+ '424' => Gem::Net::HTTPFailedDependency,
+ '426' => Gem::Net::HTTPUpgradeRequired,
+ '428' => Gem::Net::HTTPPreconditionRequired,
+ '429' => Gem::Net::HTTPTooManyRequests,
+ '431' => Gem::Net::HTTPRequestHeaderFieldsTooLarge,
+ '451' => Gem::Net::HTTPUnavailableForLegalReasons,
+
+ '500' => Gem::Net::HTTPInternalServerError,
+ '501' => Gem::Net::HTTPNotImplemented,
+ '502' => Gem::Net::HTTPBadGateway,
+ '503' => Gem::Net::HTTPServiceUnavailable,
+ '504' => Gem::Net::HTTPGatewayTimeout,
+ '505' => Gem::Net::HTTPVersionNotSupported,
+ '506' => Gem::Net::HTTPVariantAlsoNegotiates,
+ '507' => Gem::Net::HTTPInsufficientStorage,
+ '508' => Gem::Net::HTTPLoopDetected,
+ '510' => Gem::Net::HTTPNotExtended,
+ '511' => Gem::Net::HTTPNetworkAuthenticationRequired,
+ }
+end
diff --git a/lib/rubygems/vendor/net-http/lib/net/http/status.rb b/lib/rubygems/vendor/net-http/lib/net/http/status.rb
new file mode 100644
index 0000000000..9110b108b8
--- /dev/null
+++ b/lib/rubygems/vendor/net-http/lib/net/http/status.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+require_relative '../http'
+
+if $0 == __FILE__
+ require 'open-uri'
+ File.foreach(__FILE__) do |line|
+ puts line
+ break if line.start_with?('end')
+ end
+ puts
+ puts "Gem::Net::HTTP::STATUS_CODES = {"
+ url = "https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv"
+ Gem::URI(url).read.each_line do |line|
+ code, mes, = line.split(',')
+ next if ['(Unused)', 'Unassigned', 'Description'].include?(mes)
+ puts " #{code} => '#{mes}',"
+ end
+ puts "} # :nodoc:"
+end
+
+Gem::Net::HTTP::STATUS_CODES = {
+ 100 => 'Continue',
+ 101 => 'Switching Protocols',
+ 102 => 'Processing',
+ 103 => 'Early Hints',
+ 200 => 'OK',
+ 201 => 'Created',
+ 202 => 'Accepted',
+ 203 => 'Non-Authoritative Information',
+ 204 => 'No Content',
+ 205 => 'Reset Content',
+ 206 => 'Partial Content',
+ 207 => 'Multi-Status',
+ 208 => 'Already Reported',
+ 226 => 'IM Used',
+ 300 => 'Multiple Choices',
+ 301 => 'Moved Permanently',
+ 302 => 'Found',
+ 303 => 'See Other',
+ 304 => 'Not Modified',
+ 305 => 'Use Proxy',
+ 307 => 'Temporary Redirect',
+ 308 => 'Permanent Redirect',
+ 400 => 'Bad Request',
+ 401 => 'Unauthorized',
+ 402 => 'Payment Required',
+ 403 => 'Forbidden',
+ 404 => 'Not Found',
+ 405 => 'Method Not Allowed',
+ 406 => 'Not Acceptable',
+ 407 => 'Proxy Authentication Required',
+ 408 => 'Request Timeout',
+ 409 => 'Conflict',
+ 410 => 'Gone',
+ 411 => 'Length Required',
+ 412 => 'Precondition Failed',
+ 413 => 'Content Too Large',
+ 414 => 'URI Too Long',
+ 415 => 'Unsupported Media Type',
+ 416 => 'Range Not Satisfiable',
+ 417 => 'Expectation Failed',
+ 421 => 'Misdirected Request',
+ 422 => 'Unprocessable Content',
+ 423 => 'Locked',
+ 424 => 'Failed Dependency',
+ 425 => 'Too Early',
+ 426 => 'Upgrade Required',
+ 428 => 'Precondition Required',
+ 429 => 'Too Many Requests',
+ 431 => 'Request Header Fields Too Large',
+ 451 => 'Unavailable For Legal Reasons',
+ 500 => 'Internal Server Error',
+ 501 => 'Not Implemented',
+ 502 => 'Bad Gateway',
+ 503 => 'Service Unavailable',
+ 504 => 'Gateway Timeout',
+ 505 => 'HTTP Version Not Supported',
+ 506 => 'Variant Also Negotiates',
+ 507 => 'Insufficient Storage',
+ 508 => 'Loop Detected',
+ 510 => 'Not Extended (OBSOLETED)',
+ 511 => 'Network Authentication Required',
+} # :nodoc:
diff --git a/lib/rubygems/vendor/net-http/lib/net/https.rb b/lib/rubygems/vendor/net-http/lib/net/https.rb
new file mode 100644
index 0000000000..d2784f0be0
--- /dev/null
+++ b/lib/rubygems/vendor/net-http/lib/net/https.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+=begin
+
+= net/https -- SSL/TLS enhancement for Gem::Net::HTTP.
+
+ This file has been merged with net/http. There is no longer any need to
+ require 'rubygems/vendor/net-http/lib/net/https' to use HTTPS.
+
+ See Gem::Net::HTTP for details on how to make HTTPS connections.
+
+== Info
+ 'OpenSSL for Ruby 2' project
+ Copyright (C) 2001 GOTOU Yuuzou <gotoyuzo@notwork.org>
+ All rights reserved.
+
+== Licence
+ This program is licensed under the same licence as Ruby.
+ (See the file 'LICENCE'.)
+
+=end
+
+require_relative 'http'
+require 'openssl'
diff --git a/lib/rubygems/vendor/net-protocol/.document b/lib/rubygems/vendor/net-protocol/.document
new file mode 100644
index 0000000000..0c43bbd6b3
--- /dev/null
+++ b/lib/rubygems/vendor/net-protocol/.document
@@ -0,0 +1 @@
+# Vendored files do not need to be documented
diff --git a/lib/rubygems/vendor/net-protocol/lib/net/protocol.rb b/lib/rubygems/vendor/net-protocol/lib/net/protocol.rb
new file mode 100644
index 0000000000..53d34d8d98
--- /dev/null
+++ b/lib/rubygems/vendor/net-protocol/lib/net/protocol.rb
@@ -0,0 +1,544 @@
+# frozen_string_literal: true
+#
+# = net/protocol.rb
+#
+#--
+# Copyright (c) 1999-2004 Yukihiro Matsumoto
+# Copyright (c) 1999-2004 Minero Aoki
+#
+# written and maintained by Minero Aoki <aamine@loveruby.net>
+#
+# This program is free software. You can re-distribute and/or
+# modify this program under the same terms as Ruby itself,
+# Ruby Distribute License or GNU General Public License.
+#
+# $Id$
+#++
+#
+# WARNING: This file is going to remove.
+# Do not rely on the implementation written in this file.
+#
+
+require 'socket'
+require_relative '../../../timeout/lib/timeout'
+require 'io/wait'
+
+module Gem::Net # :nodoc:
+
+ class Protocol #:nodoc: internal use only
+ VERSION = "0.2.2"
+
+ private
+ def Protocol.protocol_param(name, val)
+ module_eval(<<-End, __FILE__, __LINE__ + 1)
+ def #{name}
+ #{val}
+ end
+ End
+ end
+
+ def ssl_socket_connect(s, timeout)
+ if timeout
+ while true
+ raise Gem::Net::OpenTimeout if timeout <= 0
+ start = Process.clock_gettime Process::CLOCK_MONOTONIC
+ # to_io is required because SSLSocket doesn't have wait_readable yet
+ case s.connect_nonblock(exception: false)
+ when :wait_readable; s.to_io.wait_readable(timeout)
+ when :wait_writable; s.to_io.wait_writable(timeout)
+ else; break
+ end
+ timeout -= Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
+ end
+ else
+ s.connect
+ end
+ end
+ end
+
+
+ class ProtocolError < StandardError; end
+ class ProtoSyntaxError < ProtocolError; end
+ class ProtoFatalError < ProtocolError; end
+ class ProtoUnknownError < ProtocolError; end
+ class ProtoServerError < ProtocolError; end
+ class ProtoAuthError < ProtocolError; end
+ class ProtoCommandError < ProtocolError; end
+ class ProtoRetriableError < ProtocolError; end
+ ProtocRetryError = ProtoRetriableError
+
+ ##
+ # OpenTimeout, a subclass of Gem::Timeout::Error, is raised if a connection cannot
+ # be created within the open_timeout.
+
+ class OpenTimeout < Gem::Timeout::Error; end
+
+ ##
+ # ReadTimeout, a subclass of Gem::Timeout::Error, is raised if a chunk of the
+ # response cannot be read within the read_timeout.
+
+ class ReadTimeout < Gem::Timeout::Error
+ def initialize(io = nil)
+ @io = io
+ end
+ attr_reader :io
+
+ def message
+ msg = super
+ if @io
+ msg = "#{msg} with #{@io.inspect}"
+ end
+ msg
+ end
+ end
+
+ ##
+ # WriteTimeout, a subclass of Gem::Timeout::Error, is raised if a chunk of the
+ # response cannot be written within the write_timeout. Not raised on Windows.
+
+ class WriteTimeout < Gem::Timeout::Error
+ def initialize(io = nil)
+ @io = io
+ end
+ attr_reader :io
+
+ def message
+ msg = super
+ if @io
+ msg = "#{msg} with #{@io.inspect}"
+ end
+ msg
+ end
+ end
+
+
+ class BufferedIO #:nodoc: internal use only
+ def initialize(io, read_timeout: 60, write_timeout: 60, continue_timeout: nil, debug_output: nil)
+ @io = io
+ @read_timeout = read_timeout
+ @write_timeout = write_timeout
+ @continue_timeout = continue_timeout
+ @debug_output = debug_output
+ @rbuf = ''.b
+ @rbuf_empty = true
+ @rbuf_offset = 0
+ end
+
+ attr_reader :io
+ attr_accessor :read_timeout
+ attr_accessor :write_timeout
+ attr_accessor :continue_timeout
+ attr_accessor :debug_output
+
+ def inspect
+ "#<#{self.class} io=#{@io}>"
+ end
+
+ def eof?
+ @io.eof?
+ end
+
+ def closed?
+ @io.closed?
+ end
+
+ def close
+ @io.close
+ end
+
+ #
+ # Read
+ #
+
+ public
+
+ def read(len, dest = ''.b, ignore_eof = false)
+ LOG "reading #{len} bytes..."
+ read_bytes = 0
+ begin
+ while read_bytes + rbuf_size < len
+ if s = rbuf_consume_all
+ read_bytes += s.bytesize
+ dest << s
+ end
+ rbuf_fill
+ end
+ s = rbuf_consume(len - read_bytes)
+ read_bytes += s.bytesize
+ dest << s
+ rescue EOFError
+ raise unless ignore_eof
+ end
+ LOG "read #{read_bytes} bytes"
+ dest
+ end
+
+ def read_all(dest = ''.b)
+ LOG 'reading all...'
+ read_bytes = 0
+ begin
+ while true
+ if s = rbuf_consume_all
+ read_bytes += s.bytesize
+ dest << s
+ end
+ rbuf_fill
+ end
+ rescue EOFError
+ ;
+ end
+ LOG "read #{read_bytes} bytes"
+ dest
+ end
+
+ def readuntil(terminator, ignore_eof = false)
+ offset = @rbuf_offset
+ begin
+ until idx = @rbuf.index(terminator, offset)
+ offset = @rbuf.bytesize
+ rbuf_fill
+ end
+ return rbuf_consume(idx + terminator.bytesize - @rbuf_offset)
+ rescue EOFError
+ raise unless ignore_eof
+ return rbuf_consume
+ end
+ end
+
+ def readline
+ readuntil("\n").chop
+ end
+
+ private
+
+ BUFSIZE = 1024 * 16
+
+ def rbuf_fill
+ tmp = @rbuf_empty ? @rbuf : nil
+ case rv = @io.read_nonblock(BUFSIZE, tmp, exception: false)
+ when String
+ @rbuf_empty = false
+ if rv.equal?(tmp)
+ @rbuf_offset = 0
+ else
+ @rbuf << rv
+ rv.clear
+ end
+ return
+ when :wait_readable
+ (io = @io.to_io).wait_readable(@read_timeout) or raise Gem::Net::ReadTimeout.new(io)
+ # continue looping
+ when :wait_writable
+ # OpenSSL::Buffering#read_nonblock may fail with IO::WaitWritable.
+ # http://www.openssl.org/support/faq.html#PROG10
+ (io = @io.to_io).wait_writable(@read_timeout) or raise Gem::Net::ReadTimeout.new(io)
+ # continue looping
+ when nil
+ raise EOFError, 'end of file reached'
+ end while true
+ end
+
+ def rbuf_flush
+ if @rbuf_empty
+ @rbuf.clear
+ @rbuf_offset = 0
+ end
+ nil
+ end
+
+ def rbuf_size
+ @rbuf.bytesize - @rbuf_offset
+ end
+
+ def rbuf_consume_all
+ rbuf_consume if rbuf_size > 0
+ end
+
+ def rbuf_consume(len = nil)
+ if @rbuf_offset == 0 && (len.nil? || len == @rbuf.bytesize)
+ s = @rbuf
+ @rbuf = ''.b
+ @rbuf_offset = 0
+ @rbuf_empty = true
+ elsif len.nil?
+ s = @rbuf.byteslice(@rbuf_offset..-1)
+ @rbuf = ''.b
+ @rbuf_offset = 0
+ @rbuf_empty = true
+ else
+ s = @rbuf.byteslice(@rbuf_offset, len)
+ @rbuf_offset += len
+ @rbuf_empty = @rbuf_offset == @rbuf.bytesize
+ rbuf_flush
+ end
+
+ @debug_output << %Q[-> #{s.dump}\n] if @debug_output
+ s
+ end
+
+ #
+ # Write
+ #
+
+ public
+
+ def write(*strs)
+ writing {
+ write0(*strs)
+ }
+ end
+
+ alias << write
+
+ def writeline(str)
+ writing {
+ write0 str + "\r\n"
+ }
+ end
+
+ private
+
+ def writing
+ @written_bytes = 0
+ @debug_output << '<- ' if @debug_output
+ yield
+ @debug_output << "\n" if @debug_output
+ bytes = @written_bytes
+ @written_bytes = nil
+ bytes
+ end
+
+ def write0(*strs)
+ @debug_output << strs.map(&:dump).join if @debug_output
+ orig_written_bytes = @written_bytes
+ strs.each_with_index do |str, i|
+ need_retry = true
+ case len = @io.write_nonblock(str, exception: false)
+ when Integer
+ @written_bytes += len
+ len -= str.bytesize
+ if len == 0
+ if strs.size == i+1
+ return @written_bytes - orig_written_bytes
+ else
+ need_retry = false
+ # next string
+ end
+ elsif len < 0
+ str = str.byteslice(len, -len)
+ else # len > 0
+ need_retry = false
+ # next string
+ end
+ # continue looping
+ when :wait_writable
+ (io = @io.to_io).wait_writable(@write_timeout) or raise Gem::Net::WriteTimeout.new(io)
+ # continue looping
+ end while need_retry
+ end
+ end
+
+ #
+ # Logging
+ #
+
+ private
+
+ def LOG_off
+ @save_debug_out = @debug_output
+ @debug_output = nil
+ end
+
+ def LOG_on
+ @debug_output = @save_debug_out
+ end
+
+ def LOG(msg)
+ return unless @debug_output
+ @debug_output << msg + "\n"
+ end
+ end
+
+
+ class InternetMessageIO < BufferedIO #:nodoc: internal use only
+ def initialize(*, **)
+ super
+ @wbuf = nil
+ end
+
+ #
+ # Read
+ #
+
+ def each_message_chunk
+ LOG 'reading message...'
+ LOG_off()
+ read_bytes = 0
+ while (line = readuntil("\r\n")) != ".\r\n"
+ read_bytes += line.size
+ yield line.delete_prefix('.')
+ end
+ LOG_on()
+ LOG "read message (#{read_bytes} bytes)"
+ end
+
+ # *library private* (cannot handle 'break')
+ def each_list_item
+ while (str = readuntil("\r\n")) != ".\r\n"
+ yield str.chop
+ end
+ end
+
+ def write_message_0(src)
+ prev = @written_bytes
+ each_crlf_line(src) do |line|
+ write0 dot_stuff(line)
+ end
+ @written_bytes - prev
+ end
+
+ #
+ # Write
+ #
+
+ def write_message(src)
+ LOG "writing message from #{src.class}"
+ LOG_off()
+ len = writing {
+ using_each_crlf_line {
+ write_message_0 src
+ }
+ }
+ LOG_on()
+ LOG "wrote #{len} bytes"
+ len
+ end
+
+ def write_message_by_block(&block)
+ LOG 'writing message from block'
+ LOG_off()
+ len = writing {
+ using_each_crlf_line {
+ begin
+ block.call(WriteAdapter.new(self.method(:write_message_0)))
+ rescue LocalJumpError
+ # allow `break' from writer block
+ end
+ }
+ }
+ LOG_on()
+ LOG "wrote #{len} bytes"
+ len
+ end
+
+ private
+
+ def dot_stuff(s)
+ s.sub(/\A\./, '..')
+ end
+
+ def using_each_crlf_line
+ @wbuf = ''.b
+ yield
+ if not @wbuf.empty? # unterminated last line
+ write0 dot_stuff(@wbuf.chomp) + "\r\n"
+ elsif @written_bytes == 0 # empty src
+ write0 "\r\n"
+ end
+ write0 ".\r\n"
+ @wbuf = nil
+ end
+
+ def each_crlf_line(src)
+ buffer_filling(@wbuf, src) do
+ while line = @wbuf.slice!(/\A[^\r\n]*(?:\n|\r(?:\n|(?!\z)))/)
+ yield line.chomp("\n") + "\r\n"
+ end
+ end
+ end
+
+ def buffer_filling(buf, src)
+ case src
+ when String # for speeding up.
+ 0.step(src.size - 1, 1024) do |i|
+ buf << src[i, 1024]
+ yield
+ end
+ when File # for speeding up.
+ while s = src.read(1024)
+ buf << s
+ yield
+ end
+ else # generic reader
+ src.each do |str|
+ buf << str
+ yield if buf.size > 1024
+ end
+ yield unless buf.empty?
+ end
+ end
+ end
+
+
+ #
+ # The writer adapter class
+ #
+ class WriteAdapter
+ def initialize(writer)
+ @writer = writer
+ end
+
+ def inspect
+ "#<#{self.class} writer=#{@writer.inspect}>"
+ end
+
+ def write(str)
+ @writer.call(str)
+ end
+
+ alias print write
+
+ def <<(str)
+ write str
+ self
+ end
+
+ def puts(str = '')
+ write str.chomp("\n") + "\n"
+ end
+
+ def printf(*args)
+ write sprintf(*args)
+ end
+ end
+
+
+ class ReadAdapter #:nodoc: internal use only
+ def initialize(block)
+ @block = block
+ end
+
+ def inspect
+ "#<#{self.class}>"
+ end
+
+ def <<(str)
+ call_block(str, &@block) if @block
+ end
+
+ private
+
+ # This method is needed because @block must be called by yield,
+ # not Proc#call. You can see difference when using `break' in
+ # the block.
+ def call_block(str)
+ yield str
+ end
+ end
+
+
+ module NetPrivate #:nodoc: obsolete
+ Socket = ::Gem::Net::InternetMessageIO
+ end
+
+end # module Gem::Net
diff --git a/lib/rubygems/vendor/optparse/.document b/lib/rubygems/vendor/optparse/.document
new file mode 100644
index 0000000000..0c43bbd6b3
--- /dev/null
+++ b/lib/rubygems/vendor/optparse/.document
@@ -0,0 +1 @@
+# Vendored files do not need to be documented
diff --git a/lib/rubygems/optparse/lib/optionparser.rb b/lib/rubygems/vendor/optparse/lib/optionparser.rb
index 4b9b40d82a..4b9b40d82a 100644
--- a/lib/rubygems/optparse/lib/optionparser.rb
+++ b/lib/rubygems/vendor/optparse/lib/optionparser.rb
diff --git a/lib/rubygems/vendor/optparse/lib/optparse.rb b/lib/rubygems/vendor/optparse/lib/optparse.rb
new file mode 100644
index 0000000000..5937431720
--- /dev/null
+++ b/lib/rubygems/vendor/optparse/lib/optparse.rb
@@ -0,0 +1,2330 @@
+# frozen_string_literal: true
+#
+# optparse.rb - command-line option analysis with the Gem::OptionParser class.
+#
+# Author:: Nobu Nakada
+# Documentation:: Nobu Nakada and Gavin Sinclair.
+#
+# See Gem::OptionParser for documentation.
+#
+
+
+#--
+# == Developer Documentation (not for RDoc output)
+#
+# === Class tree
+#
+# - Gem::OptionParser:: front end
+# - Gem::OptionParser::Switch:: each switches
+# - Gem::OptionParser::List:: options list
+# - Gem::OptionParser::ParseError:: errors on parsing
+# - Gem::OptionParser::AmbiguousOption
+# - Gem::OptionParser::NeedlessArgument
+# - Gem::OptionParser::MissingArgument
+# - Gem::OptionParser::InvalidOption
+# - Gem::OptionParser::InvalidArgument
+# - Gem::OptionParser::AmbiguousArgument
+#
+# === Object relationship diagram
+#
+# +--------------+
+# | Gem::OptionParser |<>-----+
+# +--------------+ | +--------+
+# | ,-| Switch |
+# on_head -------->+---------------+ / +--------+
+# accept/reject -->| List |<|>-
+# | |<|>- +----------+
+# on ------------->+---------------+ `-| argument |
+# : : | class |
+# +---------------+ |==========|
+# on_tail -------->| | |pattern |
+# +---------------+ |----------|
+# Gem::OptionParser.accept ->| DefaultList | |converter |
+# reject |(shared between| +----------+
+# | all instances)|
+# +---------------+
+#
+#++
+#
+# == Gem::OptionParser
+#
+# === New to +Gem::OptionParser+?
+#
+# See the {Tutorial}[optparse/tutorial.rdoc].
+#
+# === Introduction
+#
+# Gem::OptionParser is a class for command-line option analysis. It is much more
+# advanced, yet also easier to use, than GetoptLong, and is a more Ruby-oriented
+# solution.
+#
+# === Features
+#
+# 1. The argument specification and the code to handle it are written in the
+# same place.
+# 2. It can output an option summary; you don't need to maintain this string
+# separately.
+# 3. Optional and mandatory arguments are specified very gracefully.
+# 4. Arguments can be automatically converted to a specified class.
+# 5. Arguments can be restricted to a certain set.
+#
+# All of these features are demonstrated in the examples below. See
+# #make_switch for full documentation.
+#
+# === Minimal example
+#
+# require 'rubygems/vendor/optparse/lib/optparse'
+#
+# options = {}
+# Gem::OptionParser.new do |parser|
+# parser.banner = "Usage: example.rb [options]"
+#
+# parser.on("-v", "--[no-]verbose", "Run verbosely") do |v|
+# options[:verbose] = v
+# end
+# end.parse!
+#
+# p options
+# p ARGV
+#
+# === Generating Help
+#
+# Gem::OptionParser can be used to automatically generate help for the commands you
+# write:
+#
+# require 'rubygems/vendor/optparse/lib/optparse'
+#
+# Options = Struct.new(:name)
+#
+# class Parser
+# def self.parse(options)
+# args = Options.new("world")
+#
+# opt_parser = Gem::OptionParser.new do |parser|
+# parser.banner = "Usage: example.rb [options]"
+#
+# parser.on("-nNAME", "--name=NAME", "Name to say hello to") do |n|
+# args.name = n
+# end
+#
+# parser.on("-h", "--help", "Prints this help") do
+# puts parser
+# exit
+# end
+# end
+#
+# opt_parser.parse!(options)
+# return args
+# end
+# end
+# options = Parser.parse %w[--help]
+#
+# #=>
+# # Usage: example.rb [options]
+# # -n, --name=NAME Name to say hello to
+# # -h, --help Prints this help
+#
+# === Required Arguments
+#
+# For options that require an argument, option specification strings may include an
+# option name in all caps. If an option is used without the required argument,
+# an exception will be raised.
+#
+# require 'rubygems/vendor/optparse/lib/optparse'
+#
+# options = {}
+# Gem::OptionParser.new do |parser|
+# parser.on("-r", "--require LIBRARY",
+# "Require the LIBRARY before executing your script") do |lib|
+# puts "You required #{lib}!"
+# end
+# end.parse!
+#
+# Used:
+#
+# $ ruby optparse-test.rb -r
+# optparse-test.rb:9:in `<main>': missing argument: -r (Gem::OptionParser::MissingArgument)
+# $ ruby optparse-test.rb -r my-library
+# You required my-library!
+#
+# === Type Coercion
+#
+# Gem::OptionParser supports the ability to coerce command line arguments
+# into objects for us.
+#
+# Gem::OptionParser comes with a few ready-to-use kinds of type
+# coercion. They are:
+#
+# - Date -- Anything accepted by +Date.parse+ (need to require +optparse/date+)
+# - DateTime -- Anything accepted by +DateTime.parse+ (need to require +optparse/date+)
+# - Time -- Anything accepted by +Time.httpdate+ or +Time.parse+ (need to require +optparse/time+)
+# - URI -- Anything accepted by +Gem::URI.parse+ (need to require +optparse/uri+)
+# - Shellwords -- Anything accepted by +Shellwords.shellwords+ (need to require +optparse/shellwords+)
+# - String -- Any non-empty string
+# - Integer -- Any integer. Will convert octal. (e.g. 124, -3, 040)
+# - Float -- Any float. (e.g. 10, 3.14, -100E+13)
+# - Numeric -- Any integer, float, or rational (1, 3.4, 1/3)
+# - DecimalInteger -- Like +Integer+, but no octal format.
+# - OctalInteger -- Like +Integer+, but no decimal format.
+# - DecimalNumeric -- Decimal integer or float.
+# - TrueClass -- Accepts '+, yes, true, -, no, false' and
+# defaults as +true+
+# - FalseClass -- Same as +TrueClass+, but defaults to +false+
+# - Array -- Strings separated by ',' (e.g. 1,2,3)
+# - Regexp -- Regular expressions. Also includes options.
+#
+# We can also add our own coercions, which we will cover below.
+#
+# ==== Using Built-in Conversions
+#
+# As an example, the built-in +Time+ conversion is used. The other built-in
+# conversions behave in the same way.
+# Gem::OptionParser will attempt to parse the argument
+# as a +Time+. If it succeeds, that time will be passed to the
+# handler block. Otherwise, an exception will be raised.
+#
+# require 'rubygems/vendor/optparse/lib/optparse'
+# require 'rubygems/vendor/optparse/lib/optparse/time'
+# Gem::OptionParser.new do |parser|
+# parser.on("-t", "--time [TIME]", Time, "Begin execution at given time") do |time|
+# p time
+# end
+# end.parse!
+#
+# Used:
+#
+# $ ruby optparse-test.rb -t nonsense
+# ... invalid argument: -t nonsense (Gem::OptionParser::InvalidArgument)
+# $ ruby optparse-test.rb -t 10-11-12
+# 2010-11-12 00:00:00 -0500
+# $ ruby optparse-test.rb -t 9:30
+# 2014-08-13 09:30:00 -0400
+#
+# ==== Creating Custom Conversions
+#
+# The +accept+ method on Gem::OptionParser may be used to create converters.
+# It specifies which conversion block to call whenever a class is specified.
+# The example below uses it to fetch a +User+ object before the +on+ handler receives it.
+#
+# require 'rubygems/vendor/optparse/lib/optparse'
+#
+# User = Struct.new(:id, :name)
+#
+# def find_user id
+# not_found = ->{ raise "No User Found for id #{id}" }
+# [ User.new(1, "Sam"),
+# User.new(2, "Gandalf") ].find(not_found) do |u|
+# u.id == id
+# end
+# end
+#
+# op = Gem::OptionParser.new
+# op.accept(User) do |user_id|
+# find_user user_id.to_i
+# end
+#
+# op.on("--user ID", User) do |user|
+# puts user
+# end
+#
+# op.parse!
+#
+# Used:
+#
+# $ ruby optparse-test.rb --user 1
+# #<struct User id=1, name="Sam">
+# $ ruby optparse-test.rb --user 2
+# #<struct User id=2, name="Gandalf">
+# $ ruby optparse-test.rb --user 3
+# optparse-test.rb:15:in `block in find_user': No User Found for id 3 (RuntimeError)
+#
+# === Store options to a Hash
+#
+# The +into+ option of +order+, +parse+ and so on methods stores command line options into a Hash.
+#
+# require 'rubygems/vendor/optparse/lib/optparse'
+#
+# options = {}
+# Gem::OptionParser.new do |parser|
+# parser.on('-a')
+# parser.on('-b NUM', Integer)
+# parser.on('-v', '--verbose')
+# end.parse!(into: options)
+#
+# p options
+#
+# Used:
+#
+# $ ruby optparse-test.rb -a
+# {:a=>true}
+# $ ruby optparse-test.rb -a -v
+# {:a=>true, :verbose=>true}
+# $ ruby optparse-test.rb -a -b 100
+# {:a=>true, :b=>100}
+#
+# === Complete example
+#
+# The following example is a complete Ruby program. You can run it and see the
+# effect of specifying various options. This is probably the best way to learn
+# the features of +optparse+.
+#
+# require 'rubygems/vendor/optparse/lib/optparse'
+# require 'rubygems/vendor/optparse/lib/optparse/time'
+# require 'ostruct'
+# require 'pp'
+#
+# class OptparseExample
+# Version = '1.0.0'
+#
+# CODES = %w[iso-2022-jp shift_jis euc-jp utf8 binary]
+# CODE_ALIASES = { "jis" => "iso-2022-jp", "sjis" => "shift_jis" }
+#
+# class ScriptOptions
+# attr_accessor :library, :inplace, :encoding, :transfer_type,
+# :verbose, :extension, :delay, :time, :record_separator,
+# :list
+#
+# def initialize
+# self.library = []
+# self.inplace = false
+# self.encoding = "utf8"
+# self.transfer_type = :auto
+# self.verbose = false
+# end
+#
+# def define_options(parser)
+# parser.banner = "Usage: example.rb [options]"
+# parser.separator ""
+# parser.separator "Specific options:"
+#
+# # add additional options
+# perform_inplace_option(parser)
+# delay_execution_option(parser)
+# execute_at_time_option(parser)
+# specify_record_separator_option(parser)
+# list_example_option(parser)
+# specify_encoding_option(parser)
+# optional_option_argument_with_keyword_completion_option(parser)
+# boolean_verbose_option(parser)
+#
+# parser.separator ""
+# parser.separator "Common options:"
+# # No argument, shows at tail. This will print an options summary.
+# # Try it and see!
+# parser.on_tail("-h", "--help", "Show this message") do
+# puts parser
+# exit
+# end
+# # Another typical switch to print the version.
+# parser.on_tail("--version", "Show version") do
+# puts Version
+# exit
+# end
+# end
+#
+# def perform_inplace_option(parser)
+# # Specifies an optional option argument
+# parser.on("-i", "--inplace [EXTENSION]",
+# "Edit ARGV files in place",
+# "(make backup if EXTENSION supplied)") do |ext|
+# self.inplace = true
+# self.extension = ext || ''
+# self.extension.sub!(/\A\.?(?=.)/, ".") # Ensure extension begins with dot.
+# end
+# end
+#
+# def delay_execution_option(parser)
+# # Cast 'delay' argument to a Float.
+# parser.on("--delay N", Float, "Delay N seconds before executing") do |n|
+# self.delay = n
+# end
+# end
+#
+# def execute_at_time_option(parser)
+# # Cast 'time' argument to a Time object.
+# parser.on("-t", "--time [TIME]", Time, "Begin execution at given time") do |time|
+# self.time = time
+# end
+# end
+#
+# def specify_record_separator_option(parser)
+# # Cast to octal integer.
+# parser.on("-F", "--irs [OCTAL]", Gem::OptionParser::OctalInteger,
+# "Specify record separator (default \\0)") do |rs|
+# self.record_separator = rs
+# end
+# end
+#
+# def list_example_option(parser)
+# # List of arguments.
+# parser.on("--list x,y,z", Array, "Example 'list' of arguments") do |list|
+# self.list = list
+# end
+# end
+#
+# def specify_encoding_option(parser)
+# # Keyword completion. We are specifying a specific set of arguments (CODES
+# # and CODE_ALIASES - notice the latter is a Hash), and the user may provide
+# # the shortest unambiguous text.
+# code_list = (CODE_ALIASES.keys + CODES).join(', ')
+# parser.on("--code CODE", CODES, CODE_ALIASES, "Select encoding",
+# "(#{code_list})") do |encoding|
+# self.encoding = encoding
+# end
+# end
+#
+# def optional_option_argument_with_keyword_completion_option(parser)
+# # Optional '--type' option argument with keyword completion.
+# parser.on("--type [TYPE]", [:text, :binary, :auto],
+# "Select transfer type (text, binary, auto)") do |t|
+# self.transfer_type = t
+# end
+# end
+#
+# def boolean_verbose_option(parser)
+# # Boolean switch.
+# parser.on("-v", "--[no-]verbose", "Run verbosely") do |v|
+# self.verbose = v
+# end
+# end
+# end
+#
+# #
+# # Return a structure describing the options.
+# #
+# def parse(args)
+# # The options specified on the command line will be collected in
+# # *options*.
+#
+# @options = ScriptOptions.new
+# @args = Gem::OptionParser.new do |parser|
+# @options.define_options(parser)
+# parser.parse!(args)
+# end
+# @options
+# end
+#
+# attr_reader :parser, :options
+# end # class OptparseExample
+#
+# example = OptparseExample.new
+# options = example.parse(ARGV)
+# pp options # example.options
+# pp ARGV
+#
+# === Shell Completion
+#
+# For modern shells (e.g. bash, zsh, etc.), you can use shell
+# completion for command line options.
+#
+# === Further documentation
+#
+# The above examples, along with the accompanying
+# {Tutorial}[optparse/tutorial.rdoc],
+# should be enough to learn how to use this class.
+# If you have any questions, file a ticket at http://bugs.ruby-lang.org.
+#
+class Gem::OptionParser
+ Gem::OptionParser::Version = "0.4.0"
+
+ # :stopdoc:
+ NoArgument = [NO_ARGUMENT = :NONE, nil].freeze
+ RequiredArgument = [REQUIRED_ARGUMENT = :REQUIRED, true].freeze
+ OptionalArgument = [OPTIONAL_ARGUMENT = :OPTIONAL, false].freeze
+ # :startdoc:
+
+ #
+ # Keyword completion module. This allows partial arguments to be specified
+ # and resolved against a list of acceptable values.
+ #
+ module Completion
+ def self.regexp(key, icase)
+ Regexp.new('\A' + Regexp.quote(key).gsub(/\w+\b/, '\&\w*'), icase)
+ end
+
+ def self.candidate(key, icase = false, pat = nil, &block)
+ pat ||= Completion.regexp(key, icase)
+ candidates = []
+ block.call do |k, *v|
+ (if Regexp === k
+ kn = ""
+ k === key
+ else
+ kn = defined?(k.id2name) ? k.id2name : k
+ pat === kn
+ end) or next
+ v << k if v.empty?
+ candidates << [k, v, kn]
+ end
+ candidates
+ end
+
+ def candidate(key, icase = false, pat = nil)
+ Completion.candidate(key, icase, pat, &method(:each))
+ end
+
+ public
+ def complete(key, icase = false, pat = nil)
+ candidates = candidate(key, icase, pat, &method(:each)).sort_by {|k, v, kn| kn.size}
+ if candidates.size == 1
+ canon, sw, * = candidates[0]
+ elsif candidates.size > 1
+ canon, sw, cn = candidates.shift
+ candidates.each do |k, v, kn|
+ next if sw == v
+ if String === cn and String === kn
+ if cn.rindex(kn, 0)
+ canon, sw, cn = k, v, kn
+ next
+ elsif kn.rindex(cn, 0)
+ next
+ end
+ end
+ throw :ambiguous, key
+ end
+ end
+ if canon
+ block_given? or return key, *sw
+ yield(key, *sw)
+ end
+ end
+
+ def convert(opt = nil, val = nil, *)
+ val
+ end
+ end
+
+
+ #
+ # Map from option/keyword string to object with completion.
+ #
+ class OptionMap < Hash
+ include Completion
+ end
+
+
+ #
+ # Individual switch class. Not important to the user.
+ #
+ # Defined within Switch are several Switch-derived classes: NoArgument,
+ # RequiredArgument, etc.
+ #
+ class Switch
+ attr_reader :pattern, :conv, :short, :long, :arg, :desc, :block
+
+ #
+ # Guesses argument style from +arg+. Returns corresponding
+ # Gem::OptionParser::Switch class (OptionalArgument, etc.).
+ #
+ def self.guess(arg)
+ case arg
+ when ""
+ t = self
+ when /\A=?\[/
+ t = Switch::OptionalArgument
+ when /\A\s+\[/
+ t = Switch::PlacedArgument
+ else
+ t = Switch::RequiredArgument
+ end
+ self >= t or incompatible_argument_styles(arg, t)
+ t
+ end
+
+ def self.incompatible_argument_styles(arg, t)
+ raise(ArgumentError, "#{arg}: incompatible argument styles\n #{self}, #{t}",
+ ParseError.filter_backtrace(caller(2)))
+ end
+
+ def self.pattern
+ NilClass
+ end
+
+ def initialize(pattern = nil, conv = nil,
+ short = nil, long = nil, arg = nil,
+ desc = ([] if short or long), block = nil, &_block)
+ raise if Array === pattern
+ block ||= _block
+ @pattern, @conv, @short, @long, @arg, @desc, @block =
+ pattern, conv, short, long, arg, desc, block
+ end
+
+ #
+ # Parses +arg+ and returns rest of +arg+ and matched portion to the
+ # argument pattern. Yields when the pattern doesn't match substring.
+ #
+ def parse_arg(arg) # :nodoc:
+ pattern or return nil, [arg]
+ unless m = pattern.match(arg)
+ yield(InvalidArgument, arg)
+ return arg, []
+ end
+ if String === m
+ m = [s = m]
+ else
+ m = m.to_a
+ s = m[0]
+ return nil, m unless String === s
+ end
+ raise InvalidArgument, arg unless arg.rindex(s, 0)
+ return nil, m if s.length == arg.length
+ yield(InvalidArgument, arg) # didn't match whole arg
+ return arg[s.length..-1], m
+ end
+ private :parse_arg
+
+ #
+ # Parses argument, converts and returns +arg+, +block+ and result of
+ # conversion. Yields at semi-error condition instead of raising an
+ # exception.
+ #
+ def conv_arg(arg, val = []) # :nodoc:
+ if conv
+ val = conv.call(*val)
+ else
+ val = proc {|v| v}.call(*val)
+ end
+ return arg, block, val
+ end
+ private :conv_arg
+
+ #
+ # Produces the summary text. Each line of the summary is yielded to the
+ # block (without newline).
+ #
+ # +sdone+:: Already summarized short style options keyed hash.
+ # +ldone+:: Already summarized long style options keyed hash.
+ # +width+:: Width of left side (option part). In other words, the right
+ # side (description part) starts after +width+ columns.
+ # +max+:: Maximum width of left side -> the options are filled within
+ # +max+ columns.
+ # +indent+:: Prefix string indents all summarized lines.
+ #
+ def summarize(sdone = {}, ldone = {}, width = 1, max = width - 1, indent = "")
+ sopts, lopts = [], [], nil
+ @short.each {|s| sdone.fetch(s) {sopts << s}; sdone[s] = true} if @short
+ @long.each {|s| ldone.fetch(s) {lopts << s}; ldone[s] = true} if @long
+ return if sopts.empty? and lopts.empty? # completely hidden
+
+ left = [sopts.join(', ')]
+ right = desc.dup
+
+ while s = lopts.shift
+ l = left[-1].length + s.length
+ l += arg.length if left.size == 1 && arg
+ l < max or sopts.empty? or left << +''
+ left[-1] << (left[-1].empty? ? ' ' * 4 : ', ') << s
+ end
+
+ if arg
+ left[0] << (left[1] ? arg.sub(/\A(\[?)=/, '\1') + ',' : arg)
+ end
+ mlen = left.collect {|ss| ss.length}.max.to_i
+ while mlen > width and l = left.shift
+ mlen = left.collect {|ss| ss.length}.max.to_i if l.length == mlen
+ if l.length < width and (r = right[0]) and !r.empty?
+ l = l.to_s.ljust(width) + ' ' + r
+ right.shift
+ end
+ yield(indent + l)
+ end
+
+ while begin l = left.shift; r = right.shift; l or r end
+ l = l.to_s.ljust(width) + ' ' + r if r and !r.empty?
+ yield(indent + l)
+ end
+
+ self
+ end
+
+ def add_banner(to) # :nodoc:
+ unless @short or @long
+ s = desc.join
+ to << " [" + s + "]..." unless s.empty?
+ end
+ to
+ end
+
+ def match_nonswitch?(str) # :nodoc:
+ @pattern =~ str unless @short or @long
+ end
+
+ #
+ # Main name of the switch.
+ #
+ def switch_name
+ (long.first || short.first).sub(/\A-+(?:\[no-\])?/, '')
+ end
+
+ def compsys(sdone, ldone) # :nodoc:
+ sopts, lopts = [], []
+ @short.each {|s| sdone.fetch(s) {sopts << s}; sdone[s] = true} if @short
+ @long.each {|s| ldone.fetch(s) {lopts << s}; ldone[s] = true} if @long
+ return if sopts.empty? and lopts.empty? # completely hidden
+
+ (sopts+lopts).each do |opt|
+ # "(-x -c -r)-l[left justify]"
+ if /^--\[no-\](.+)$/ =~ opt
+ o = $1
+ yield("--#{o}", desc.join(""))
+ yield("--no-#{o}", desc.join(""))
+ else
+ yield("#{opt}", desc.join(""))
+ end
+ end
+ end
+
+ def pretty_print_contents(q) # :nodoc:
+ if @block
+ q.text ":" + @block.source_location.join(":") + ":"
+ first = false
+ else
+ first = true
+ end
+ [@short, @long].each do |list|
+ list.each do |opt|
+ if first
+ q.text ":"
+ first = false
+ end
+ q.breakable
+ q.text opt
+ end
+ end
+ end
+
+ def pretty_print(q) # :nodoc:
+ q.object_group(self) {pretty_print_contents(q)}
+ end
+
+ #
+ # Switch that takes no arguments.
+ #
+ class NoArgument < self
+
+ #
+ # Raises an exception if any arguments given.
+ #
+ def parse(arg, argv)
+ yield(NeedlessArgument, arg) if arg
+ conv_arg(arg)
+ end
+
+ def self.incompatible_argument_styles(*)
+ end
+
+ def self.pattern
+ Object
+ end
+
+ def pretty_head # :nodoc:
+ "NoArgument"
+ end
+ end
+
+ #
+ # Switch that takes an argument.
+ #
+ class RequiredArgument < self
+
+ #
+ # Raises an exception if argument is not present.
+ #
+ def parse(arg, argv)
+ unless arg
+ raise MissingArgument if argv.empty?
+ arg = argv.shift
+ end
+ conv_arg(*parse_arg(arg, &method(:raise)))
+ end
+
+ def pretty_head # :nodoc:
+ "Required"
+ end
+ end
+
+ #
+ # Switch that can omit argument.
+ #
+ class OptionalArgument < self
+
+ #
+ # Parses argument if given, or uses default value.
+ #
+ def parse(arg, argv, &error)
+ if arg
+ conv_arg(*parse_arg(arg, &error))
+ else
+ conv_arg(arg)
+ end
+ end
+
+ def pretty_head # :nodoc:
+ "Optional"
+ end
+ end
+
+ #
+ # Switch that takes an argument, which does not begin with '-' or is '-'.
+ #
+ class PlacedArgument < self
+
+ #
+ # Returns nil if argument is not present or begins with '-' and is not '-'.
+ #
+ def parse(arg, argv, &error)
+ if !(val = arg) and (argv.empty? or /\A-./ =~ (val = argv[0]))
+ return nil, block, nil
+ end
+ opt = (val = parse_arg(val, &error))[1]
+ val = conv_arg(*val)
+ if opt and !arg
+ argv.shift
+ else
+ val[0] = nil
+ end
+ val
+ end
+
+ def pretty_head # :nodoc:
+ "Placed"
+ end
+ end
+ end
+
+ #
+ # Simple option list providing mapping from short and/or long option
+ # string to Gem::OptionParser::Switch and mapping from acceptable argument to
+ # matching pattern and converter pair. Also provides summary feature.
+ #
+ class List
+ # Map from acceptable argument types to pattern and converter pairs.
+ attr_reader :atype
+
+ # Map from short style option switches to actual switch objects.
+ attr_reader :short
+
+ # Map from long style option switches to actual switch objects.
+ attr_reader :long
+
+ # List of all switches and summary string.
+ attr_reader :list
+
+ #
+ # Just initializes all instance variables.
+ #
+ def initialize
+ @atype = {}
+ @short = OptionMap.new
+ @long = OptionMap.new
+ @list = []
+ end
+
+ def pretty_print(q) # :nodoc:
+ q.group(1, "(", ")") do
+ @list.each do |sw|
+ next unless Switch === sw
+ q.group(1, "(" + sw.pretty_head, ")") do
+ sw.pretty_print_contents(q)
+ end
+ end
+ end
+ end
+
+ #
+ # See Gem::OptionParser.accept.
+ #
+ def accept(t, pat = /.*/m, &block)
+ if pat
+ pat.respond_to?(:match) or
+ raise TypeError, "has no `match'", ParseError.filter_backtrace(caller(2))
+ else
+ pat = t if t.respond_to?(:match)
+ end
+ unless block
+ block = pat.method(:convert).to_proc if pat.respond_to?(:convert)
+ end
+ @atype[t] = [pat, block]
+ end
+
+ #
+ # See Gem::OptionParser.reject.
+ #
+ def reject(t)
+ @atype.delete(t)
+ end
+
+ #
+ # Adds +sw+ according to +sopts+, +lopts+ and +nlopts+.
+ #
+ # +sw+:: Gem::OptionParser::Switch instance to be added.
+ # +sopts+:: Short style option list.
+ # +lopts+:: Long style option list.
+ # +nlopts+:: Negated long style options list.
+ #
+ def update(sw, sopts, lopts, nsw = nil, nlopts = nil) # :nodoc:
+ sopts.each {|o| @short[o] = sw} if sopts
+ lopts.each {|o| @long[o] = sw} if lopts
+ nlopts.each {|o| @long[o] = nsw} if nsw and nlopts
+ used = @short.invert.update(@long.invert)
+ @list.delete_if {|o| Switch === o and !used[o]}
+ end
+ private :update
+
+ #
+ # Inserts +switch+ at the head of the list, and associates short, long
+ # and negated long options. Arguments are:
+ #
+ # +switch+:: Gem::OptionParser::Switch instance to be inserted.
+ # +short_opts+:: List of short style options.
+ # +long_opts+:: List of long style options.
+ # +nolong_opts+:: List of long style options with "no-" prefix.
+ #
+ # prepend(switch, short_opts, long_opts, nolong_opts)
+ #
+ def prepend(*args)
+ update(*args)
+ @list.unshift(args[0])
+ end
+
+ #
+ # Appends +switch+ at the tail of the list, and associates short, long
+ # and negated long options. Arguments are:
+ #
+ # +switch+:: Gem::OptionParser::Switch instance to be inserted.
+ # +short_opts+:: List of short style options.
+ # +long_opts+:: List of long style options.
+ # +nolong_opts+:: List of long style options with "no-" prefix.
+ #
+ # append(switch, short_opts, long_opts, nolong_opts)
+ #
+ def append(*args)
+ update(*args)
+ @list.push(args[0])
+ end
+
+ #
+ # Searches +key+ in +id+ list. The result is returned or yielded if a
+ # block is given. If it isn't found, nil is returned.
+ #
+ def search(id, key)
+ if list = __send__(id)
+ val = list.fetch(key) {return nil}
+ block_given? ? yield(val) : val
+ end
+ end
+
+ #
+ # Searches list +id+ for +opt+ and the optional patterns for completion
+ # +pat+. If +icase+ is true, the search is case insensitive. The result
+ # is returned or yielded if a block is given. If it isn't found, nil is
+ # returned.
+ #
+ def complete(id, opt, icase = false, *pat, &block)
+ __send__(id).complete(opt, icase, *pat, &block)
+ end
+
+ def get_candidates(id)
+ yield __send__(id).keys
+ end
+
+ #
+ # Iterates over each option, passing the option to the +block+.
+ #
+ def each_option(&block)
+ list.each(&block)
+ end
+
+ #
+ # Creates the summary table, passing each line to the +block+ (without
+ # newline). The arguments +args+ are passed along to the summarize
+ # method which is called on every option.
+ #
+ def summarize(*args, &block)
+ sum = []
+ list.reverse_each do |opt|
+ if opt.respond_to?(:summarize) # perhaps Gem::OptionParser::Switch
+ s = []
+ opt.summarize(*args) {|l| s << l}
+ sum.concat(s.reverse)
+ elsif !opt or opt.empty?
+ sum << ""
+ elsif opt.respond_to?(:each_line)
+ sum.concat([*opt.each_line].reverse)
+ else
+ sum.concat([*opt.each].reverse)
+ end
+ end
+ sum.reverse_each(&block)
+ end
+
+ def add_banner(to) # :nodoc:
+ list.each do |opt|
+ if opt.respond_to?(:add_banner)
+ opt.add_banner(to)
+ end
+ end
+ to
+ end
+
+ def compsys(*args, &block) # :nodoc:
+ list.each do |opt|
+ if opt.respond_to?(:compsys)
+ opt.compsys(*args, &block)
+ end
+ end
+ end
+ end
+
+ #
+ # Hash with completion search feature. See Gem::OptionParser::Completion.
+ #
+ class CompletingHash < Hash
+ include Completion
+
+ #
+ # Completion for hash key.
+ #
+ def match(key)
+ *values = fetch(key) {
+ raise AmbiguousArgument, catch(:ambiguous) {return complete(key)}
+ }
+ return key, *values
+ end
+ end
+
+ # :stopdoc:
+
+ #
+ # Enumeration of acceptable argument styles. Possible values are:
+ #
+ # NO_ARGUMENT:: The switch takes no arguments. (:NONE)
+ # REQUIRED_ARGUMENT:: The switch requires an argument. (:REQUIRED)
+ # OPTIONAL_ARGUMENT:: The switch requires an optional argument. (:OPTIONAL)
+ #
+ # Use like --switch=argument (long style) or -Xargument (short style). For
+ # short style, only portion matched to argument pattern is treated as
+ # argument.
+ #
+ ArgumentStyle = {}
+ NoArgument.each {|el| ArgumentStyle[el] = Switch::NoArgument}
+ RequiredArgument.each {|el| ArgumentStyle[el] = Switch::RequiredArgument}
+ OptionalArgument.each {|el| ArgumentStyle[el] = Switch::OptionalArgument}
+ ArgumentStyle.freeze
+
+ #
+ # Switches common used such as '--', and also provides default
+ # argument classes
+ #
+ DefaultList = List.new
+ DefaultList.short['-'] = Switch::NoArgument.new {}
+ DefaultList.long[''] = Switch::NoArgument.new {throw :terminate}
+
+
+ COMPSYS_HEADER = <<'XXX' # :nodoc:
+
+typeset -A opt_args
+local context state line
+
+_arguments -s -S \
+XXX
+
+ def compsys(to, name = File.basename($0)) # :nodoc:
+ to << "#compdef #{name}\n"
+ to << COMPSYS_HEADER
+ visit(:compsys, {}, {}) {|o, d|
+ to << %Q[ "#{o}[#{d.gsub(/[\"\[\]]/, '\\\\\&')}]" \\\n]
+ }
+ to << " '*:file:_files' && return 0\n"
+ end
+
+ #
+ # Default options for ARGV, which never appear in option summary.
+ #
+ Officious = {}
+
+ #
+ # --help
+ # Shows option summary.
+ #
+ Officious['help'] = proc do |parser|
+ Switch::NoArgument.new do |arg|
+ puts parser.help
+ exit
+ end
+ end
+
+ #
+ # --*-completion-bash=WORD
+ # Shows candidates for command line completion.
+ #
+ Officious['*-completion-bash'] = proc do |parser|
+ Switch::RequiredArgument.new do |arg|
+ puts parser.candidate(arg)
+ exit
+ end
+ end
+
+ #
+ # --*-completion-zsh[=NAME:FILE]
+ # Creates zsh completion file.
+ #
+ Officious['*-completion-zsh'] = proc do |parser|
+ Switch::OptionalArgument.new do |arg|
+ parser.compsys(STDOUT, arg)
+ exit
+ end
+ end
+
+ #
+ # --version
+ # Shows version string if Version is defined.
+ #
+ Officious['version'] = proc do |parser|
+ Switch::OptionalArgument.new do |pkg|
+ if pkg
+ begin
+ require 'rubygems/vendor/optparse/lib/optparse/version'
+ rescue LoadError
+ else
+ show_version(*pkg.split(/,/)) or
+ abort("#{parser.program_name}: no version found in package #{pkg}")
+ exit
+ end
+ end
+ v = parser.ver or abort("#{parser.program_name}: version unknown")
+ puts v
+ exit
+ end
+ end
+
+ # :startdoc:
+
+ #
+ # Class methods
+ #
+
+ #
+ # Initializes a new instance and evaluates the optional block in context
+ # of the instance. Arguments +args+ are passed to #new, see there for
+ # description of parameters.
+ #
+ # This method is *deprecated*, its behavior corresponds to the older #new
+ # method.
+ #
+ def self.with(*args, &block)
+ opts = new(*args)
+ opts.instance_eval(&block)
+ opts
+ end
+
+ #
+ # Returns an incremented value of +default+ according to +arg+.
+ #
+ def self.inc(arg, default = nil)
+ case arg
+ when Integer
+ arg.nonzero?
+ when nil
+ default.to_i + 1
+ end
+ end
+ def inc(*args)
+ self.class.inc(*args)
+ end
+
+ #
+ # Initializes the instance and yields itself if called with a block.
+ #
+ # +banner+:: Banner message.
+ # +width+:: Summary width.
+ # +indent+:: Summary indent.
+ #
+ def initialize(banner = nil, width = 32, indent = ' ' * 4)
+ @stack = [DefaultList, List.new, List.new]
+ @program_name = nil
+ @banner = banner
+ @summary_width = width
+ @summary_indent = indent
+ @default_argv = ARGV
+ @require_exact = false
+ @raise_unknown = true
+ add_officious
+ yield self if block_given?
+ end
+
+ def add_officious # :nodoc:
+ list = base()
+ Officious.each do |opt, block|
+ list.long[opt] ||= block.call(self)
+ end
+ end
+
+ #
+ # Terminates option parsing. Optional parameter +arg+ is a string pushed
+ # back to be the first non-option argument.
+ #
+ def terminate(arg = nil)
+ self.class.terminate(arg)
+ end
+ def self.terminate(arg = nil)
+ throw :terminate, arg
+ end
+
+ @stack = [DefaultList]
+ def self.top() DefaultList end
+
+ #
+ # Directs to accept specified class +t+. The argument string is passed to
+ # the block in which it should be converted to the desired class.
+ #
+ # +t+:: Argument class specifier, any object including Class.
+ # +pat+:: Pattern for argument, defaults to +t+ if it responds to match.
+ #
+ # accept(t, pat, &block)
+ #
+ def accept(*args, &blk) top.accept(*args, &blk) end
+ #
+ # See #accept.
+ #
+ def self.accept(*args, &blk) top.accept(*args, &blk) end
+
+ #
+ # Directs to reject specified class argument.
+ #
+ # +t+:: Argument class specifier, any object including Class.
+ #
+ # reject(t)
+ #
+ def reject(*args, &blk) top.reject(*args, &blk) end
+ #
+ # See #reject.
+ #
+ def self.reject(*args, &blk) top.reject(*args, &blk) end
+
+ #
+ # Instance methods
+ #
+
+ # Heading banner preceding summary.
+ attr_writer :banner
+
+ # Program name to be emitted in error message and default banner,
+ # defaults to $0.
+ attr_writer :program_name
+
+ # Width for option list portion of summary. Must be Numeric.
+ attr_accessor :summary_width
+
+ # Indentation for summary. Must be String (or have + String method).
+ attr_accessor :summary_indent
+
+ # Strings to be parsed in default.
+ attr_accessor :default_argv
+
+ # Whether to require that options match exactly (disallows providing
+ # abbreviated long option as short option).
+ attr_accessor :require_exact
+
+ # Whether to raise at unknown option.
+ attr_accessor :raise_unknown
+
+ #
+ # Heading banner preceding summary.
+ #
+ def banner
+ unless @banner
+ @banner = +"Usage: #{program_name} [options]"
+ visit(:add_banner, @banner)
+ end
+ @banner
+ end
+
+ #
+ # Program name to be emitted in error message and default banner, defaults
+ # to $0.
+ #
+ def program_name
+ @program_name || File.basename($0, '.*')
+ end
+
+ # for experimental cascading :-)
+ alias set_banner banner=
+ alias set_program_name program_name=
+ alias set_summary_width summary_width=
+ alias set_summary_indent summary_indent=
+
+ # Version
+ attr_writer :version
+ # Release code
+ attr_writer :release
+
+ #
+ # Version
+ #
+ def version
+ (defined?(@version) && @version) || (defined?(::Version) && ::Version)
+ end
+
+ #
+ # Release code
+ #
+ def release
+ (defined?(@release) && @release) || (defined?(::Release) && ::Release) || (defined?(::RELEASE) && ::RELEASE)
+ end
+
+ #
+ # Returns version string from program_name, version and release.
+ #
+ def ver
+ if v = version
+ str = +"#{program_name} #{[v].join('.')}"
+ str << " (#{v})" if v = release
+ str
+ end
+ end
+
+ def warn(mesg = $!)
+ super("#{program_name}: #{mesg}")
+ end
+
+ def abort(mesg = $!)
+ super("#{program_name}: #{mesg}")
+ end
+
+ #
+ # Subject of #on / #on_head, #accept / #reject
+ #
+ def top
+ @stack[-1]
+ end
+
+ #
+ # Subject of #on_tail.
+ #
+ def base
+ @stack[1]
+ end
+
+ #
+ # Pushes a new List.
+ #
+ def new
+ @stack.push(List.new)
+ if block_given?
+ yield self
+ else
+ self
+ end
+ end
+
+ #
+ # Removes the last List.
+ #
+ def remove
+ @stack.pop
+ end
+
+ #
+ # Puts option summary into +to+ and returns +to+. Yields each line if
+ # a block is given.
+ #
+ # +to+:: Output destination, which must have method <<. Defaults to [].
+ # +width+:: Width of left side, defaults to @summary_width.
+ # +max+:: Maximum length allowed for left side, defaults to +width+ - 1.
+ # +indent+:: Indentation, defaults to @summary_indent.
+ #
+ def summarize(to = [], width = @summary_width, max = width - 1, indent = @summary_indent, &blk)
+ nl = "\n"
+ blk ||= proc {|l| to << (l.index(nl, -1) ? l : l + nl)}
+ visit(:summarize, {}, {}, width, max, indent, &blk)
+ to
+ end
+
+ #
+ # Returns option summary string.
+ #
+ def help; summarize("#{banner}".sub(/\n?\z/, "\n")) end
+ alias to_s help
+
+ def pretty_print(q) # :nodoc:
+ q.object_group(self) do
+ first = true
+ if @stack.size > 2
+ @stack.each_with_index do |s, i|
+ next if i < 2
+ next if s.list.empty?
+ if first
+ first = false
+ q.text ":"
+ end
+ q.breakable
+ s.pretty_print(q)
+ end
+ end
+ end
+ end
+
+ def inspect # :nodoc:
+ require 'pp'
+ pretty_print_inspect
+ end
+
+ #
+ # Returns option summary list.
+ #
+ def to_a; summarize("#{banner}".split(/^/)) end
+
+ #
+ # Checks if an argument is given twice, in which case an ArgumentError is
+ # raised. Called from Gem::OptionParser#switch only.
+ #
+ # +obj+:: New argument.
+ # +prv+:: Previously specified argument.
+ # +msg+:: Exception message.
+ #
+ def notwice(obj, prv, msg) # :nodoc:
+ unless !prv or prv == obj
+ raise(ArgumentError, "argument #{msg} given twice: #{obj}",
+ ParseError.filter_backtrace(caller(2)))
+ end
+ obj
+ end
+ private :notwice
+
+ SPLAT_PROC = proc {|*a| a.length <= 1 ? a.first : a} # :nodoc:
+
+ # :call-seq:
+ # make_switch(params, block = nil)
+ #
+ # :include: ../doc/optparse/creates_option.rdoc
+ #
+ def make_switch(opts, block = nil)
+ short, long, nolong, style, pattern, conv, not_pattern, not_conv, not_style = [], [], []
+ ldesc, sdesc, desc, arg = [], [], []
+ default_style = Switch::NoArgument
+ default_pattern = nil
+ klass = nil
+ q, a = nil
+ has_arg = false
+
+ opts.each do |o|
+ # argument class
+ next if search(:atype, o) do |pat, c|
+ klass = notwice(o, klass, 'type')
+ if not_style and not_style != Switch::NoArgument
+ not_pattern, not_conv = pat, c
+ else
+ default_pattern, conv = pat, c
+ end
+ end
+
+ # directly specified pattern(any object possible to match)
+ if (!(String === o || Symbol === o)) and o.respond_to?(:match)
+ pattern = notwice(o, pattern, 'pattern')
+ if pattern.respond_to?(:convert)
+ conv = pattern.method(:convert).to_proc
+ else
+ conv = SPLAT_PROC
+ end
+ next
+ end
+
+ # anything others
+ case o
+ when Proc, Method
+ block = notwice(o, block, 'block')
+ when Array, Hash
+ case pattern
+ when CompletingHash
+ when nil
+ pattern = CompletingHash.new
+ conv = pattern.method(:convert).to_proc if pattern.respond_to?(:convert)
+ else
+ raise ArgumentError, "argument pattern given twice"
+ end
+ o.each {|pat, *v| pattern[pat] = v.fetch(0) {pat}}
+ when Module
+ raise ArgumentError, "unsupported argument type: #{o}", ParseError.filter_backtrace(caller(4))
+ when *ArgumentStyle.keys
+ style = notwice(ArgumentStyle[o], style, 'style')
+ when /^--no-([^\[\]=\s]*)(.+)?/
+ q, a = $1, $2
+ o = notwice(a ? Object : TrueClass, klass, 'type')
+ not_pattern, not_conv = search(:atype, o) unless not_style
+ not_style = (not_style || default_style).guess(arg = a) if a
+ default_style = Switch::NoArgument
+ default_pattern, conv = search(:atype, FalseClass) unless default_pattern
+ ldesc << "--no-#{q}"
+ (q = q.downcase).tr!('_', '-')
+ long << "no-#{q}"
+ nolong << q
+ when /^--\[no-\]([^\[\]=\s]*)(.+)?/
+ q, a = $1, $2
+ o = notwice(a ? Object : TrueClass, klass, 'type')
+ if a
+ default_style = default_style.guess(arg = a)
+ default_pattern, conv = search(:atype, o) unless default_pattern
+ end
+ ldesc << "--[no-]#{q}"
+ (o = q.downcase).tr!('_', '-')
+ long << o
+ not_pattern, not_conv = search(:atype, FalseClass) unless not_style
+ not_style = Switch::NoArgument
+ nolong << "no-#{o}"
+ when /^--([^\[\]=\s]*)(.+)?/
+ q, a = $1, $2
+ if a
+ o = notwice(NilClass, klass, 'type')
+ default_style = default_style.guess(arg = a)
+ default_pattern, conv = search(:atype, o) unless default_pattern
+ end
+ ldesc << "--#{q}"
+ (o = q.downcase).tr!('_', '-')
+ long << o
+ when /^-(\[\^?\]?(?:[^\\\]]|\\.)*\])(.+)?/
+ q, a = $1, $2
+ o = notwice(Object, klass, 'type')
+ if a
+ default_style = default_style.guess(arg = a)
+ default_pattern, conv = search(:atype, o) unless default_pattern
+ else
+ has_arg = true
+ end
+ sdesc << "-#{q}"
+ short << Regexp.new(q)
+ when /^-(.)(.+)?/
+ q, a = $1, $2
+ if a
+ o = notwice(NilClass, klass, 'type')
+ default_style = default_style.guess(arg = a)
+ default_pattern, conv = search(:atype, o) unless default_pattern
+ end
+ sdesc << "-#{q}"
+ short << q
+ when /^=/
+ style = notwice(default_style.guess(arg = o), style, 'style')
+ default_pattern, conv = search(:atype, Object) unless default_pattern
+ else
+ desc.push(o) if o && !o.empty?
+ end
+ end
+
+ default_pattern, conv = search(:atype, default_style.pattern) unless default_pattern
+ if !(short.empty? and long.empty?)
+ if has_arg and default_style == Switch::NoArgument
+ default_style = Switch::RequiredArgument
+ end
+ s = (style || default_style).new(pattern || default_pattern,
+ conv, sdesc, ldesc, arg, desc, block)
+ elsif !block
+ if style or pattern
+ raise ArgumentError, "no switch given", ParseError.filter_backtrace(caller)
+ end
+ s = desc
+ else
+ short << pattern
+ s = (style || default_style).new(pattern,
+ conv, nil, nil, arg, desc, block)
+ end
+ return s, short, long,
+ (not_style.new(not_pattern, not_conv, sdesc, ldesc, nil, desc, block) if not_style),
+ nolong
+ end
+
+ # :call-seq:
+ # define(*params, &block)
+ #
+ # :include: ../doc/optparse/creates_option.rdoc
+ #
+ def define(*opts, &block)
+ top.append(*(sw = make_switch(opts, block)))
+ sw[0]
+ end
+
+ # :call-seq:
+ # on(*params, &block)
+ #
+ # :include: ../doc/optparse/creates_option.rdoc
+ #
+ def on(*opts, &block)
+ define(*opts, &block)
+ self
+ end
+ alias def_option define
+
+ # :call-seq:
+ # define_head(*params, &block)
+ #
+ # :include: ../doc/optparse/creates_option.rdoc
+ #
+ def define_head(*opts, &block)
+ top.prepend(*(sw = make_switch(opts, block)))
+ sw[0]
+ end
+
+ # :call-seq:
+ # on_head(*params, &block)
+ #
+ # :include: ../doc/optparse/creates_option.rdoc
+ #
+ # The new option is added at the head of the summary.
+ #
+ def on_head(*opts, &block)
+ define_head(*opts, &block)
+ self
+ end
+ alias def_head_option define_head
+
+ # :call-seq:
+ # define_tail(*params, &block)
+ #
+ # :include: ../doc/optparse/creates_option.rdoc
+ #
+ def define_tail(*opts, &block)
+ base.append(*(sw = make_switch(opts, block)))
+ sw[0]
+ end
+
+ #
+ # :call-seq:
+ # on_tail(*params, &block)
+ #
+ # :include: ../doc/optparse/creates_option.rdoc
+ #
+ # The new option is added at the tail of the summary.
+ #
+ def on_tail(*opts, &block)
+ define_tail(*opts, &block)
+ self
+ end
+ alias def_tail_option define_tail
+
+ #
+ # Add separator in summary.
+ #
+ def separator(string)
+ top.append(string, nil, nil)
+ end
+
+ #
+ # Parses command line arguments +argv+ in order. When a block is given,
+ # each non-option argument is yielded. When optional +into+ keyword
+ # argument is provided, the parsed option values are stored there via
+ # <code>[]=</code> method (so it can be Hash, or OpenStruct, or other
+ # similar object).
+ #
+ # Returns the rest of +argv+ left unparsed.
+ #
+ def order(*argv, into: nil, &nonopt)
+ argv = argv[0].dup if argv.size == 1 and Array === argv[0]
+ order!(argv, into: into, &nonopt)
+ end
+
+ #
+ # Same as #order, but removes switches destructively.
+ # Non-option arguments remain in +argv+.
+ #
+ def order!(argv = default_argv, into: nil, &nonopt)
+ setter = ->(name, val) {into[name.to_sym] = val} if into
+ parse_in_order(argv, setter, &nonopt)
+ end
+
+ def parse_in_order(argv = default_argv, setter = nil, &nonopt) # :nodoc:
+ opt, arg, val, rest = nil
+ nonopt ||= proc {|a| throw :terminate, a}
+ argv.unshift(arg) if arg = catch(:terminate) {
+ while arg = argv.shift
+ case arg
+ # long option
+ when /\A--([^=]*)(?:=(.*))?/m
+ opt, rest = $1, $2
+ opt.tr!('_', '-')
+ begin
+ sw, = complete(:long, opt, true)
+ if require_exact && !sw.long.include?(arg)
+ throw :terminate, arg unless raise_unknown
+ raise InvalidOption, arg
+ end
+ rescue ParseError
+ throw :terminate, arg unless raise_unknown
+ raise $!.set_option(arg, true)
+ end
+ begin
+ opt, cb, val = sw.parse(rest, argv) {|*exc| raise(*exc)}
+ val = cb.call(val) if cb
+ setter.call(sw.switch_name, val) if setter
+ rescue ParseError
+ raise $!.set_option(arg, rest)
+ end
+
+ # short option
+ when /\A-(.)((=).*|.+)?/m
+ eq, rest, opt = $3, $2, $1
+ has_arg, val = eq, rest
+ begin
+ sw, = search(:short, opt)
+ unless sw
+ begin
+ sw, = complete(:short, opt)
+ # short option matched.
+ val = arg.delete_prefix('-')
+ has_arg = true
+ rescue InvalidOption
+ raise if require_exact
+ # if no short options match, try completion with long
+ # options.
+ sw, = complete(:long, opt)
+ eq ||= !rest
+ end
+ end
+ rescue ParseError
+ throw :terminate, arg unless raise_unknown
+ raise $!.set_option(arg, true)
+ end
+ begin
+ opt, cb, val = sw.parse(val, argv) {|*exc| raise(*exc) if eq}
+ rescue ParseError
+ raise $!.set_option(arg, arg.length > 2)
+ else
+ raise InvalidOption, arg if has_arg and !eq and arg == "-#{opt}"
+ end
+ begin
+ argv.unshift(opt) if opt and (!rest or (opt = opt.sub(/\A-*/, '-')) != '-')
+ val = cb.call(val) if cb
+ setter.call(sw.switch_name, val) if setter
+ rescue ParseError
+ raise $!.set_option(arg, arg.length > 2)
+ end
+
+ # non-option argument
+ else
+ catch(:prune) do
+ visit(:each_option) do |sw0|
+ sw = sw0
+ sw.block.call(arg) if Switch === sw and sw.match_nonswitch?(arg)
+ end
+ nonopt.call(arg)
+ end
+ end
+ end
+
+ nil
+ }
+
+ visit(:search, :short, nil) {|sw| sw.block.call(*argv) if !sw.pattern}
+
+ argv
+ end
+ private :parse_in_order
+
+ #
+ # Parses command line arguments +argv+ in permutation mode and returns
+ # list of non-option arguments. When optional +into+ keyword
+ # argument is provided, the parsed option values are stored there via
+ # <code>[]=</code> method (so it can be Hash, or OpenStruct, or other
+ # similar object).
+ #
+ def permute(*argv, into: nil)
+ argv = argv[0].dup if argv.size == 1 and Array === argv[0]
+ permute!(argv, into: into)
+ end
+
+ #
+ # Same as #permute, but removes switches destructively.
+ # Non-option arguments remain in +argv+.
+ #
+ def permute!(argv = default_argv, into: nil)
+ nonopts = []
+ order!(argv, into: into, &nonopts.method(:<<))
+ argv[0, 0] = nonopts
+ argv
+ end
+
+ #
+ # Parses command line arguments +argv+ in order when environment variable
+ # POSIXLY_CORRECT is set, and in permutation mode otherwise.
+ # When optional +into+ keyword argument is provided, the parsed option
+ # values are stored there via <code>[]=</code> method (so it can be Hash,
+ # or OpenStruct, or other similar object).
+ #
+ def parse(*argv, into: nil)
+ argv = argv[0].dup if argv.size == 1 and Array === argv[0]
+ parse!(argv, into: into)
+ end
+
+ #
+ # Same as #parse, but removes switches destructively.
+ # Non-option arguments remain in +argv+.
+ #
+ def parse!(argv = default_argv, into: nil)
+ if ENV.include?('POSIXLY_CORRECT')
+ order!(argv, into: into)
+ else
+ permute!(argv, into: into)
+ end
+ end
+
+ #
+ # Wrapper method for getopts.rb.
+ #
+ # params = ARGV.getopts("ab:", "foo", "bar:", "zot:Z;zot option")
+ # # params["a"] = true # -a
+ # # params["b"] = "1" # -b1
+ # # params["foo"] = "1" # --foo
+ # # params["bar"] = "x" # --bar x
+ # # params["zot"] = "z" # --zot Z
+ #
+ # Option +symbolize_names+ (boolean) specifies whether returned Hash keys should be Symbols; defaults to +false+ (use Strings).
+ #
+ # params = ARGV.getopts("ab:", "foo", "bar:", "zot:Z;zot option", symbolize_names: true)
+ # # params[:a] = true # -a
+ # # params[:b] = "1" # -b1
+ # # params[:foo] = "1" # --foo
+ # # params[:bar] = "x" # --bar x
+ # # params[:zot] = "z" # --zot Z
+ #
+ def getopts(*args, symbolize_names: false)
+ argv = Array === args.first ? args.shift : default_argv
+ single_options, *long_options = *args
+
+ result = {}
+
+ single_options.scan(/(.)(:)?/) do |opt, val|
+ if val
+ result[opt] = nil
+ define("-#{opt} VAL")
+ else
+ result[opt] = false
+ define("-#{opt}")
+ end
+ end if single_options
+
+ long_options.each do |arg|
+ arg, desc = arg.split(';', 2)
+ opt, val = arg.split(':', 2)
+ if val
+ result[opt] = val.empty? ? nil : val
+ define("--#{opt}=#{result[opt] || "VAL"}", *[desc].compact)
+ else
+ result[opt] = false
+ define("--#{opt}", *[desc].compact)
+ end
+ end
+
+ parse_in_order(argv, result.method(:[]=))
+ symbolize_names ? result.transform_keys(&:to_sym) : result
+ end
+
+ #
+ # See #getopts.
+ #
+ def self.getopts(*args, symbolize_names: false)
+ new.getopts(*args, symbolize_names: symbolize_names)
+ end
+
+ #
+ # Traverses @stack, sending each element method +id+ with +args+ and
+ # +block+.
+ #
+ def visit(id, *args, &block) # :nodoc:
+ @stack.reverse_each do |el|
+ el.__send__(id, *args, &block)
+ end
+ nil
+ end
+ private :visit
+
+ #
+ # Searches +key+ in @stack for +id+ hash and returns or yields the result.
+ #
+ def search(id, key) # :nodoc:
+ block_given = block_given?
+ visit(:search, id, key) do |k|
+ return block_given ? yield(k) : k
+ end
+ end
+ private :search
+
+ #
+ # Completes shortened long style option switch and returns pair of
+ # canonical switch and switch descriptor Gem::OptionParser::Switch.
+ #
+ # +typ+:: Searching table.
+ # +opt+:: Searching key.
+ # +icase+:: Search case insensitive if true.
+ # +pat+:: Optional pattern for completion.
+ #
+ def complete(typ, opt, icase = false, *pat) # :nodoc:
+ if pat.empty?
+ search(typ, opt) {|sw| return [sw, opt]} # exact match or...
+ end
+ ambiguous = catch(:ambiguous) {
+ visit(:complete, typ, opt, icase, *pat) {|o, *sw| return sw}
+ }
+ exc = ambiguous ? AmbiguousOption : InvalidOption
+ raise exc.new(opt, additional: self.method(:additional_message).curry[typ])
+ end
+ private :complete
+
+ #
+ # Returns additional info.
+ #
+ def additional_message(typ, opt)
+ return unless typ and opt and defined?(DidYouMean::SpellChecker)
+ all_candidates = []
+ visit(:get_candidates, typ) do |candidates|
+ all_candidates.concat(candidates)
+ end
+ all_candidates.select! {|cand| cand.is_a?(String) }
+ checker = DidYouMean::SpellChecker.new(dictionary: all_candidates)
+ DidYouMean.formatter.message_for(all_candidates & checker.correct(opt))
+ end
+
+ def candidate(word)
+ list = []
+ case word
+ when '-'
+ long = short = true
+ when /\A--/
+ word, arg = word.split(/=/, 2)
+ argpat = Completion.regexp(arg, false) if arg and !arg.empty?
+ long = true
+ when /\A-/
+ short = true
+ end
+ pat = Completion.regexp(word, long)
+ visit(:each_option) do |opt|
+ next unless Switch === opt
+ opts = (long ? opt.long : []) + (short ? opt.short : [])
+ opts = Completion.candidate(word, true, pat, &opts.method(:each)).map(&:first) if pat
+ if /\A=/ =~ opt.arg
+ opts.map! {|sw| sw + "="}
+ if arg and CompletingHash === opt.pattern
+ if opts = opt.pattern.candidate(arg, false, argpat)
+ opts.map!(&:last)
+ end
+ end
+ end
+ list.concat(opts)
+ end
+ list
+ end
+
+ #
+ # Loads options from file names as +filename+. Does nothing when the file
+ # is not present. Returns whether successfully loaded.
+ #
+ # +filename+ defaults to basename of the program without suffix in a
+ # directory ~/.options, then the basename with '.options' suffix
+ # under XDG and Haiku standard places.
+ #
+ # The optional +into+ keyword argument works exactly like that accepted in
+ # method #parse.
+ #
+ def load(filename = nil, into: nil)
+ unless filename
+ basename = File.basename($0, '.*')
+ return true if load(File.expand_path(basename, '~/.options'), into: into) rescue nil
+ basename << ".options"
+ return [
+ # XDG
+ ENV['XDG_CONFIG_HOME'],
+ '~/.config',
+ *ENV['XDG_CONFIG_DIRS']&.split(File::PATH_SEPARATOR),
+
+ # Haiku
+ '~/config/settings',
+ ].any? {|dir|
+ next if !dir or dir.empty?
+ load(File.expand_path(basename, dir), into: into) rescue nil
+ }
+ end
+ begin
+ parse(*File.readlines(filename, chomp: true), into: into)
+ true
+ rescue Errno::ENOENT, Errno::ENOTDIR
+ false
+ end
+ end
+
+ #
+ # Parses environment variable +env+ or its uppercase with splitting like a
+ # shell.
+ #
+ # +env+ defaults to the basename of the program.
+ #
+ def environment(env = File.basename($0, '.*'))
+ env = ENV[env] || ENV[env.upcase] or return
+ require 'shellwords'
+ parse(*Shellwords.shellwords(env))
+ end
+
+ #
+ # Acceptable argument classes
+ #
+
+ #
+ # Any string and no conversion. This is fall-back.
+ #
+ accept(Object) {|s,|s or s.nil?}
+
+ accept(NilClass) {|s,|s}
+
+ #
+ # Any non-empty string, and no conversion.
+ #
+ accept(String, /.+/m) {|s,*|s}
+
+ #
+ # Ruby/C-like integer, octal for 0-7 sequence, binary for 0b, hexadecimal
+ # for 0x, and decimal for others; with optional sign prefix. Converts to
+ # Integer.
+ #
+ decimal = '\d+(?:_\d+)*'
+ binary = 'b[01]+(?:_[01]+)*'
+ hex = 'x[\da-f]+(?:_[\da-f]+)*'
+ octal = "0(?:[0-7]+(?:_[0-7]+)*|#{binary}|#{hex})?"
+ integer = "#{octal}|#{decimal}"
+
+ accept(Integer, %r"\A[-+]?(?:#{integer})\z"io) {|s,|
+ begin
+ Integer(s)
+ rescue ArgumentError
+ raise Gem::OptionParser::InvalidArgument, s
+ end if s
+ }
+
+ #
+ # Float number format, and converts to Float.
+ #
+ float = "(?:#{decimal}(?=(.)?)(?:\\.(?:#{decimal})?)?|\\.#{decimal})(?:E[-+]?#{decimal})?"
+ floatpat = %r"\A[-+]?#{float}\z"io
+ accept(Float, floatpat) {|s,| s.to_f if s}
+
+ #
+ # Generic numeric format, converts to Integer for integer format, Float
+ # for float format, and Rational for rational format.
+ #
+ real = "[-+]?(?:#{octal}|#{float})"
+ accept(Numeric, /\A(#{real})(?:\/(#{real}))?\z/io) {|s, d, f, n,|
+ if n
+ Rational(d, n)
+ elsif f
+ Float(s)
+ else
+ Integer(s)
+ end
+ }
+
+ #
+ # Decimal integer format, to be converted to Integer.
+ #
+ DecimalInteger = /\A[-+]?#{decimal}\z/io
+ accept(DecimalInteger, DecimalInteger) {|s,|
+ begin
+ Integer(s, 10)
+ rescue ArgumentError
+ raise Gem::OptionParser::InvalidArgument, s
+ end if s
+ }
+
+ #
+ # Ruby/C like octal/hexadecimal/binary integer format, to be converted to
+ # Integer.
+ #
+ OctalInteger = /\A[-+]?(?:[0-7]+(?:_[0-7]+)*|0(?:#{binary}|#{hex}))\z/io
+ accept(OctalInteger, OctalInteger) {|s,|
+ begin
+ Integer(s, 8)
+ rescue ArgumentError
+ raise Gem::OptionParser::InvalidArgument, s
+ end if s
+ }
+
+ #
+ # Decimal integer/float number format, to be converted to Integer for
+ # integer format, Float for float format.
+ #
+ DecimalNumeric = floatpat # decimal integer is allowed as float also.
+ accept(DecimalNumeric, floatpat) {|s, f|
+ begin
+ if f
+ Float(s)
+ else
+ Integer(s)
+ end
+ rescue ArgumentError
+ raise Gem::OptionParser::InvalidArgument, s
+ end if s
+ }
+
+ #
+ # Boolean switch, which means whether it is present or not, whether it is
+ # absent or not with prefix no-, or it takes an argument
+ # yes/no/true/false/+/-.
+ #
+ yesno = CompletingHash.new
+ %w[- no false].each {|el| yesno[el] = false}
+ %w[+ yes true].each {|el| yesno[el] = true}
+ yesno['nil'] = false # should be nil?
+ accept(TrueClass, yesno) {|arg, val| val == nil or val}
+ #
+ # Similar to TrueClass, but defaults to false.
+ #
+ accept(FalseClass, yesno) {|arg, val| val != nil and val}
+
+ #
+ # List of strings separated by ",".
+ #
+ accept(Array) do |s, |
+ if s
+ s = s.split(',').collect {|ss| ss unless ss.empty?}
+ end
+ s
+ end
+
+ #
+ # Regular expression with options.
+ #
+ accept(Regexp, %r"\A/((?:\\.|[^\\])*)/([[:alpha:]]+)?\z|.*") do |all, s, o|
+ f = 0
+ if o
+ f |= Regexp::IGNORECASE if /i/ =~ o
+ f |= Regexp::MULTILINE if /m/ =~ o
+ f |= Regexp::EXTENDED if /x/ =~ o
+ case o = o.delete("imx")
+ when ""
+ when "u"
+ s = s.encode(Encoding::UTF_8)
+ when "e"
+ s = s.encode(Encoding::EUC_JP)
+ when "s"
+ s = s.encode(Encoding::SJIS)
+ when "n"
+ f |= Regexp::NOENCODING
+ else
+ raise Gem::OptionParser::InvalidArgument, "unknown regexp option - #{o}"
+ end
+ else
+ s ||= all
+ end
+ Regexp.new(s, f)
+ end
+
+ #
+ # Exceptions
+ #
+
+ #
+ # Base class of exceptions from Gem::OptionParser.
+ #
+ class ParseError < RuntimeError
+ # Reason which caused the error.
+ Reason = 'parse error'
+
+ def initialize(*args, additional: nil)
+ @additional = additional
+ @arg0, = args
+ @args = args
+ @reason = nil
+ end
+
+ attr_reader :args
+ attr_writer :reason
+ attr_accessor :additional
+
+ #
+ # Pushes back erred argument(s) to +argv+.
+ #
+ def recover(argv)
+ argv[0, 0] = @args
+ argv
+ end
+
+ def self.filter_backtrace(array)
+ unless $DEBUG
+ array.delete_if(&%r"\A#{Regexp.quote(__FILE__)}:"o.method(:=~))
+ end
+ array
+ end
+
+ def set_backtrace(array)
+ super(self.class.filter_backtrace(array))
+ end
+
+ def set_option(opt, eq)
+ if eq
+ @args[0] = opt
+ else
+ @args.unshift(opt)
+ end
+ self
+ end
+
+ #
+ # Returns error reason. Override this for I18N.
+ #
+ def reason
+ @reason || self.class::Reason
+ end
+
+ def inspect
+ "#<#{self.class}: #{args.join(' ')}>"
+ end
+
+ #
+ # Default stringizing method to emit standard error message.
+ #
+ def message
+ "#{reason}: #{args.join(' ')}#{additional[@arg0] if additional}"
+ end
+
+ alias to_s message
+ end
+
+ #
+ # Raises when ambiguously completable string is encountered.
+ #
+ class AmbiguousOption < ParseError
+ const_set(:Reason, 'ambiguous option')
+ end
+
+ #
+ # Raises when there is an argument for a switch which takes no argument.
+ #
+ class NeedlessArgument < ParseError
+ const_set(:Reason, 'needless argument')
+ end
+
+ #
+ # Raises when a switch with mandatory argument has no argument.
+ #
+ class MissingArgument < ParseError
+ const_set(:Reason, 'missing argument')
+ end
+
+ #
+ # Raises when switch is undefined.
+ #
+ class InvalidOption < ParseError
+ const_set(:Reason, 'invalid option')
+ end
+
+ #
+ # Raises when the given argument does not match required format.
+ #
+ class InvalidArgument < ParseError
+ const_set(:Reason, 'invalid argument')
+ end
+
+ #
+ # Raises when the given argument word can't be completed uniquely.
+ #
+ class AmbiguousArgument < InvalidArgument
+ const_set(:Reason, 'ambiguous argument')
+ end
+
+ #
+ # Miscellaneous
+ #
+
+ #
+ # Extends command line arguments array (ARGV) to parse itself.
+ #
+ module Arguable
+
+ #
+ # Sets Gem::OptionParser object, when +opt+ is +false+ or +nil+, methods
+ # Gem::OptionParser::Arguable#options and Gem::OptionParser::Arguable#options= are
+ # undefined. Thus, there is no ways to access the Gem::OptionParser object
+ # via the receiver object.
+ #
+ def options=(opt)
+ unless @optparse = opt
+ class << self
+ undef_method(:options)
+ undef_method(:options=)
+ end
+ end
+ end
+
+ #
+ # Actual Gem::OptionParser object, automatically created if nonexistent.
+ #
+ # If called with a block, yields the Gem::OptionParser object and returns the
+ # result of the block. If an Gem::OptionParser::ParseError exception occurs
+ # in the block, it is rescued, a error message printed to STDERR and
+ # +nil+ returned.
+ #
+ def options
+ @optparse ||= Gem::OptionParser.new
+ @optparse.default_argv = self
+ block_given? or return @optparse
+ begin
+ yield @optparse
+ rescue ParseError
+ @optparse.warn $!
+ nil
+ end
+ end
+
+ #
+ # Parses +self+ destructively in order and returns +self+ containing the
+ # rest arguments left unparsed.
+ #
+ def order!(&blk) options.order!(self, &blk) end
+
+ #
+ # Parses +self+ destructively in permutation mode and returns +self+
+ # containing the rest arguments left unparsed.
+ #
+ def permute!() options.permute!(self) end
+
+ #
+ # Parses +self+ destructively and returns +self+ containing the
+ # rest arguments left unparsed.
+ #
+ def parse!() options.parse!(self) end
+
+ #
+ # Substitution of getopts is possible as follows. Also see
+ # Gem::OptionParser#getopts.
+ #
+ # def getopts(*args)
+ # ($OPT = ARGV.getopts(*args)).each do |opt, val|
+ # eval "$OPT_#{opt.gsub(/[^A-Za-z0-9_]/, '_')} = val"
+ # end
+ # rescue Gem::OptionParser::ParseError
+ # end
+ #
+ def getopts(*args, symbolize_names: false)
+ options.getopts(self, *args, symbolize_names: symbolize_names)
+ end
+
+ #
+ # Initializes instance variable.
+ #
+ def self.extend_object(obj)
+ super
+ obj.instance_eval {@optparse = nil}
+ end
+ def initialize(*args)
+ super
+ @optparse = nil
+ end
+ end
+
+ #
+ # Acceptable argument classes. Now contains DecimalInteger, OctalInteger
+ # and DecimalNumeric. See Acceptable argument classes (in source code).
+ #
+ module Acceptables
+ const_set(:DecimalInteger, Gem::OptionParser::DecimalInteger)
+ const_set(:OctalInteger, Gem::OptionParser::OctalInteger)
+ const_set(:DecimalNumeric, Gem::OptionParser::DecimalNumeric)
+ end
+end
+
+# ARGV is arguable by Gem::OptionParser
+ARGV.extend(Gem::OptionParser::Arguable)
diff --git a/lib/rubygems/optparse/lib/optparse/ac.rb b/lib/rubygems/vendor/optparse/lib/optparse/ac.rb
index e84d01bf91..e84d01bf91 100644
--- a/lib/rubygems/optparse/lib/optparse/ac.rb
+++ b/lib/rubygems/vendor/optparse/lib/optparse/ac.rb
diff --git a/lib/rubygems/optparse/lib/optparse/date.rb b/lib/rubygems/vendor/optparse/lib/optparse/date.rb
index d9a9f4f48a..d9a9f4f48a 100644
--- a/lib/rubygems/optparse/lib/optparse/date.rb
+++ b/lib/rubygems/vendor/optparse/lib/optparse/date.rb
diff --git a/lib/rubygems/optparse/lib/optparse/kwargs.rb b/lib/rubygems/vendor/optparse/lib/optparse/kwargs.rb
index 6987a5ed62..6987a5ed62 100644
--- a/lib/rubygems/optparse/lib/optparse/kwargs.rb
+++ b/lib/rubygems/vendor/optparse/lib/optparse/kwargs.rb
diff --git a/lib/rubygems/optparse/lib/optparse/shellwords.rb b/lib/rubygems/vendor/optparse/lib/optparse/shellwords.rb
index d47ad60255..d47ad60255 100644
--- a/lib/rubygems/optparse/lib/optparse/shellwords.rb
+++ b/lib/rubygems/vendor/optparse/lib/optparse/shellwords.rb
diff --git a/lib/rubygems/optparse/lib/optparse/time.rb b/lib/rubygems/vendor/optparse/lib/optparse/time.rb
index c59e1e4ced..c59e1e4ced 100644
--- a/lib/rubygems/optparse/lib/optparse/time.rb
+++ b/lib/rubygems/vendor/optparse/lib/optparse/time.rb
diff --git a/lib/rubygems/vendor/optparse/lib/optparse/uri.rb b/lib/rubygems/vendor/optparse/lib/optparse/uri.rb
new file mode 100644
index 0000000000..398127479a
--- /dev/null
+++ b/lib/rubygems/vendor/optparse/lib/optparse/uri.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: false
+# -*- ruby -*-
+
+require_relative '../optparse'
+require_relative '../../../uri/lib/uri'
+
+Gem::OptionParser.accept(Gem::URI) {|s,| Gem::URI.parse(s) if s}
diff --git a/lib/rubygems/optparse/lib/optparse/version.rb b/lib/rubygems/vendor/optparse/lib/optparse/version.rb
index 5d79e9db44..5d79e9db44 100644
--- a/lib/rubygems/optparse/lib/optparse/version.rb
+++ b/lib/rubygems/vendor/optparse/lib/optparse/version.rb
diff --git a/lib/rubygems/vendor/resolv/.document b/lib/rubygems/vendor/resolv/.document
new file mode 100644
index 0000000000..0c43bbd6b3
--- /dev/null
+++ b/lib/rubygems/vendor/resolv/.document
@@ -0,0 +1 @@
+# Vendored files do not need to be documented
diff --git a/lib/rubygems/vendor/resolv/lib/resolv.rb b/lib/rubygems/vendor/resolv/lib/resolv.rb
new file mode 100644
index 0000000000..ac0ba0b313
--- /dev/null
+++ b/lib/rubygems/vendor/resolv/lib/resolv.rb
@@ -0,0 +1,3442 @@
+# frozen_string_literal: true
+
+require 'socket'
+require_relative '../../timeout/lib/timeout'
+require 'io/wait'
+
+begin
+ require 'securerandom'
+rescue LoadError
+end
+
+# Gem::Resolv is a thread-aware DNS resolver library written in Ruby. Gem::Resolv can
+# handle multiple DNS requests concurrently without blocking the entire Ruby
+# interpreter.
+#
+# See also resolv-replace.rb to replace the libc resolver with Gem::Resolv.
+#
+# Gem::Resolv can look up various DNS resources using the DNS module directly.
+#
+# Examples:
+#
+# p Gem::Resolv.getaddress "www.ruby-lang.org"
+# p Gem::Resolv.getname "210.251.121.214"
+#
+# Gem::Resolv::DNS.open do |dns|
+# ress = dns.getresources "www.ruby-lang.org", Gem::Resolv::DNS::Resource::IN::A
+# p ress.map(&:address)
+# ress = dns.getresources "ruby-lang.org", Gem::Resolv::DNS::Resource::IN::MX
+# p ress.map { |r| [r.exchange.to_s, r.preference] }
+# end
+#
+#
+# == Bugs
+#
+# * NIS is not supported.
+# * /etc/nsswitch.conf is not supported.
+
+class Gem::Resolv
+
+ VERSION = "0.4.0"
+
+ ##
+ # Looks up the first IP address for +name+.
+
+ def self.getaddress(name)
+ DefaultResolver.getaddress(name)
+ end
+
+ ##
+ # Looks up all IP address for +name+.
+
+ def self.getaddresses(name)
+ DefaultResolver.getaddresses(name)
+ end
+
+ ##
+ # Iterates over all IP addresses for +name+.
+
+ def self.each_address(name, &block)
+ DefaultResolver.each_address(name, &block)
+ end
+
+ ##
+ # Looks up the hostname of +address+.
+
+ def self.getname(address)
+ DefaultResolver.getname(address)
+ end
+
+ ##
+ # Looks up all hostnames for +address+.
+
+ def self.getnames(address)
+ DefaultResolver.getnames(address)
+ end
+
+ ##
+ # Iterates over all hostnames for +address+.
+
+ def self.each_name(address, &proc)
+ DefaultResolver.each_name(address, &proc)
+ end
+
+ ##
+ # Creates a new Gem::Resolv using +resolvers+.
+
+ def initialize(resolvers=nil, use_ipv6: nil)
+ @resolvers = resolvers || [Hosts.new, DNS.new(DNS::Config.default_config_hash.merge(use_ipv6: use_ipv6))]
+ end
+
+ ##
+ # Looks up the first IP address for +name+.
+
+ def getaddress(name)
+ each_address(name) {|address| return address}
+ raise ResolvError.new("no address for #{name}")
+ end
+
+ ##
+ # Looks up all IP address for +name+.
+
+ def getaddresses(name)
+ ret = []
+ each_address(name) {|address| ret << address}
+ return ret
+ end
+
+ ##
+ # Iterates over all IP addresses for +name+.
+
+ def each_address(name)
+ if AddressRegex =~ name
+ yield name
+ return
+ end
+ yielded = false
+ @resolvers.each {|r|
+ r.each_address(name) {|address|
+ yield address.to_s
+ yielded = true
+ }
+ return if yielded
+ }
+ end
+
+ ##
+ # Looks up the hostname of +address+.
+
+ def getname(address)
+ each_name(address) {|name| return name}
+ raise ResolvError.new("no name for #{address}")
+ end
+
+ ##
+ # Looks up all hostnames for +address+.
+
+ def getnames(address)
+ ret = []
+ each_name(address) {|name| ret << name}
+ return ret
+ end
+
+ ##
+ # Iterates over all hostnames for +address+.
+
+ def each_name(address)
+ yielded = false
+ @resolvers.each {|r|
+ r.each_name(address) {|name|
+ yield name.to_s
+ yielded = true
+ }
+ return if yielded
+ }
+ end
+
+ ##
+ # Indicates a failure to resolve a name or address.
+
+ class ResolvError < StandardError; end
+
+ ##
+ # Indicates a timeout resolving a name or address.
+
+ class ResolvTimeout < Gem::Timeout::Error; end
+
+ ##
+ # Gem::Resolv::Hosts is a hostname resolver that uses the system hosts file.
+
+ class Hosts
+ if /mswin|mingw|cygwin/ =~ RUBY_PLATFORM and
+ begin
+ require 'win32/resolv'
+ DefaultFileName = Win32::Resolv.get_hosts_path || IO::NULL
+ rescue LoadError
+ end
+ end
+ DefaultFileName ||= '/etc/hosts'
+
+ ##
+ # Creates a new Gem::Resolv::Hosts, using +filename+ for its data source.
+
+ def initialize(filename = DefaultFileName)
+ @filename = filename
+ @mutex = Thread::Mutex.new
+ @initialized = nil
+ end
+
+ def lazy_initialize # :nodoc:
+ @mutex.synchronize {
+ unless @initialized
+ @name2addr = {}
+ @addr2name = {}
+ File.open(@filename, 'rb') {|f|
+ f.each {|line|
+ line.sub!(/#.*/, '')
+ addr, *hostnames = line.split(/\s+/)
+ next unless addr
+ (@addr2name[addr] ||= []).concat(hostnames)
+ hostnames.each {|hostname| (@name2addr[hostname] ||= []) << addr}
+ }
+ }
+ @name2addr.each {|name, arr| arr.reverse!}
+ @initialized = true
+ end
+ }
+ self
+ end
+
+ ##
+ # Gets the IP address of +name+ from the hosts file.
+
+ def getaddress(name)
+ each_address(name) {|address| return address}
+ raise ResolvError.new("#{@filename} has no name: #{name}")
+ end
+
+ ##
+ # Gets all IP addresses for +name+ from the hosts file.
+
+ def getaddresses(name)
+ ret = []
+ each_address(name) {|address| ret << address}
+ return ret
+ end
+
+ ##
+ # Iterates over all IP addresses for +name+ retrieved from the hosts file.
+
+ def each_address(name, &proc)
+ lazy_initialize
+ @name2addr[name]&.each(&proc)
+ end
+
+ ##
+ # Gets the hostname of +address+ from the hosts file.
+
+ def getname(address)
+ each_name(address) {|name| return name}
+ raise ResolvError.new("#{@filename} has no address: #{address}")
+ end
+
+ ##
+ # Gets all hostnames for +address+ from the hosts file.
+
+ def getnames(address)
+ ret = []
+ each_name(address) {|name| ret << name}
+ return ret
+ end
+
+ ##
+ # Iterates over all hostnames for +address+ retrieved from the hosts file.
+
+ def each_name(address, &proc)
+ lazy_initialize
+ @addr2name[address]&.each(&proc)
+ end
+ end
+
+ ##
+ # Gem::Resolv::DNS is a DNS stub resolver.
+ #
+ # Information taken from the following places:
+ #
+ # * STD0013
+ # * RFC 1035
+ # * ftp://ftp.isi.edu/in-notes/iana/assignments/dns-parameters
+ # * etc.
+
+ class DNS
+
+ ##
+ # Default DNS Port
+
+ Port = 53
+
+ ##
+ # Default DNS UDP packet size
+
+ UDPSize = 512
+
+ ##
+ # Creates a new DNS resolver. See Gem::Resolv::DNS.new for argument details.
+ #
+ # Yields the created DNS resolver to the block, if given, otherwise
+ # returns it.
+
+ def self.open(*args)
+ dns = new(*args)
+ return dns unless block_given?
+ begin
+ yield dns
+ ensure
+ dns.close
+ end
+ end
+
+ ##
+ # Creates a new DNS resolver.
+ #
+ # +config_info+ can be:
+ #
+ # nil:: Uses /etc/resolv.conf.
+ # String:: Path to a file using /etc/resolv.conf's format.
+ # Hash:: Must contain :nameserver, :search and :ndots keys.
+ # :nameserver_port can be used to specify port number of nameserver address.
+ # :raise_timeout_errors can be used to raise timeout errors
+ # as exceptions instead of treating the same as an NXDOMAIN response.
+ #
+ # The value of :nameserver should be an address string or
+ # an array of address strings.
+ # - :nameserver => '8.8.8.8'
+ # - :nameserver => ['8.8.8.8', '8.8.4.4']
+ #
+ # The value of :nameserver_port should be an array of
+ # pair of nameserver address and port number.
+ # - :nameserver_port => [['8.8.8.8', 53], ['8.8.4.4', 53]]
+ #
+ # Example:
+ #
+ # Gem::Resolv::DNS.new(:nameserver => ['210.251.121.21'],
+ # :search => ['ruby-lang.org'],
+ # :ndots => 1)
+
+ def initialize(config_info=nil)
+ @mutex = Thread::Mutex.new
+ @config = Config.new(config_info)
+ @initialized = nil
+ end
+
+ # Sets the resolver timeouts. This may be a single positive number
+ # or an array of positive numbers representing timeouts in seconds.
+ # If an array is specified, a DNS request will retry and wait for
+ # each successive interval in the array until a successful response
+ # is received. Specifying +nil+ reverts to the default timeouts:
+ # [ 5, second = 5 * 2 / nameserver_count, 2 * second, 4 * second ]
+ #
+ # Example:
+ #
+ # dns.timeouts = 3
+ #
+ def timeouts=(values)
+ @config.timeouts = values
+ end
+
+ def lazy_initialize # :nodoc:
+ @mutex.synchronize {
+ unless @initialized
+ @config.lazy_initialize
+ @initialized = true
+ end
+ }
+ self
+ end
+
+ ##
+ # Closes the DNS resolver.
+
+ def close
+ @mutex.synchronize {
+ if @initialized
+ @initialized = false
+ end
+ }
+ end
+
+ ##
+ # Gets the IP address of +name+ from the DNS resolver.
+ #
+ # +name+ can be a Gem::Resolv::DNS::Name or a String. Retrieved address will
+ # be a Gem::Resolv::IPv4 or Gem::Resolv::IPv6
+
+ def getaddress(name)
+ each_address(name) {|address| return address}
+ raise ResolvError.new("DNS result has no information for #{name}")
+ end
+
+ ##
+ # Gets all IP addresses for +name+ from the DNS resolver.
+ #
+ # +name+ can be a Gem::Resolv::DNS::Name or a String. Retrieved addresses will
+ # be a Gem::Resolv::IPv4 or Gem::Resolv::IPv6
+
+ def getaddresses(name)
+ ret = []
+ each_address(name) {|address| ret << address}
+ return ret
+ end
+
+ ##
+ # Iterates over all IP addresses for +name+ retrieved from the DNS
+ # resolver.
+ #
+ # +name+ can be a Gem::Resolv::DNS::Name or a String. Retrieved addresses will
+ # be a Gem::Resolv::IPv4 or Gem::Resolv::IPv6
+
+ def each_address(name)
+ each_resource(name, Resource::IN::A) {|resource| yield resource.address}
+ if use_ipv6?
+ each_resource(name, Resource::IN::AAAA) {|resource| yield resource.address}
+ end
+ end
+
+ def use_ipv6? # :nodoc:
+ use_ipv6 = @config.use_ipv6?
+ unless use_ipv6.nil?
+ return use_ipv6
+ end
+
+ begin
+ list = Socket.ip_address_list
+ rescue NotImplementedError
+ return true
+ end
+ list.any? {|a| a.ipv6? && !a.ipv6_loopback? && !a.ipv6_linklocal? }
+ end
+ private :use_ipv6?
+
+ ##
+ # Gets the hostname for +address+ from the DNS resolver.
+ #
+ # +address+ must be a Gem::Resolv::IPv4, Gem::Resolv::IPv6 or a String. Retrieved
+ # name will be a Gem::Resolv::DNS::Name.
+
+ def getname(address)
+ each_name(address) {|name| return name}
+ raise ResolvError.new("DNS result has no information for #{address}")
+ end
+
+ ##
+ # Gets all hostnames for +address+ from the DNS resolver.
+ #
+ # +address+ must be a Gem::Resolv::IPv4, Gem::Resolv::IPv6 or a String. Retrieved
+ # names will be Gem::Resolv::DNS::Name instances.
+
+ def getnames(address)
+ ret = []
+ each_name(address) {|name| ret << name}
+ return ret
+ end
+
+ ##
+ # Iterates over all hostnames for +address+ retrieved from the DNS
+ # resolver.
+ #
+ # +address+ must be a Gem::Resolv::IPv4, Gem::Resolv::IPv6 or a String. Retrieved
+ # names will be Gem::Resolv::DNS::Name instances.
+
+ def each_name(address)
+ case address
+ when Name
+ ptr = address
+ when IPv4, IPv6
+ ptr = address.to_name
+ when IPv4::Regex
+ ptr = IPv4.create(address).to_name
+ when IPv6::Regex
+ ptr = IPv6.create(address).to_name
+ else
+ raise ResolvError.new("cannot interpret as address: #{address}")
+ end
+ each_resource(ptr, Resource::IN::PTR) {|resource| yield resource.name}
+ end
+
+ ##
+ # Look up the +typeclass+ DNS resource of +name+.
+ #
+ # +name+ must be a Gem::Resolv::DNS::Name or a String.
+ #
+ # +typeclass+ should be one of the following:
+ #
+ # * Gem::Resolv::DNS::Resource::IN::A
+ # * Gem::Resolv::DNS::Resource::IN::AAAA
+ # * Gem::Resolv::DNS::Resource::IN::ANY
+ # * Gem::Resolv::DNS::Resource::IN::CNAME
+ # * Gem::Resolv::DNS::Resource::IN::HINFO
+ # * Gem::Resolv::DNS::Resource::IN::MINFO
+ # * Gem::Resolv::DNS::Resource::IN::MX
+ # * Gem::Resolv::DNS::Resource::IN::NS
+ # * Gem::Resolv::DNS::Resource::IN::PTR
+ # * Gem::Resolv::DNS::Resource::IN::SOA
+ # * Gem::Resolv::DNS::Resource::IN::TXT
+ # * Gem::Resolv::DNS::Resource::IN::WKS
+ #
+ # Returned resource is represented as a Gem::Resolv::DNS::Resource instance,
+ # i.e. Gem::Resolv::DNS::Resource::IN::A.
+
+ def getresource(name, typeclass)
+ each_resource(name, typeclass) {|resource| return resource}
+ raise ResolvError.new("DNS result has no information for #{name}")
+ end
+
+ ##
+ # Looks up all +typeclass+ DNS resources for +name+. See #getresource for
+ # argument details.
+
+ def getresources(name, typeclass)
+ ret = []
+ each_resource(name, typeclass) {|resource| ret << resource}
+ return ret
+ end
+
+ ##
+ # Iterates over all +typeclass+ DNS resources for +name+. See
+ # #getresource for argument details.
+
+ def each_resource(name, typeclass, &proc)
+ fetch_resource(name, typeclass) {|reply, reply_name|
+ extract_resources(reply, reply_name, typeclass, &proc)
+ }
+ end
+
+ def fetch_resource(name, typeclass)
+ lazy_initialize
+ begin
+ requester = make_udp_requester
+ rescue Errno::EACCES
+ # fall back to TCP
+ end
+ senders = {}
+ begin
+ @config.resolv(name) {|candidate, tout, nameserver, port|
+ requester ||= make_tcp_requester(nameserver, port)
+ msg = Message.new
+ msg.rd = 1
+ msg.add_question(candidate, typeclass)
+ unless sender = senders[[candidate, nameserver, port]]
+ sender = requester.sender(msg, candidate, nameserver, port)
+ next if !sender
+ senders[[candidate, nameserver, port]] = sender
+ end
+ reply, reply_name = requester.request(sender, tout)
+ case reply.rcode
+ when RCode::NoError
+ if reply.tc == 1 and not Requester::TCP === requester
+ requester.close
+ # Retry via TCP:
+ requester = make_tcp_requester(nameserver, port)
+ senders = {}
+ # This will use TCP for all remaining candidates (assuming the
+ # current candidate does not already respond successfully via
+ # TCP). This makes sense because we already know the full
+ # response will not fit in an untruncated UDP packet.
+ redo
+ else
+ yield(reply, reply_name)
+ end
+ return
+ when RCode::NXDomain
+ raise Config::NXDomain.new(reply_name.to_s)
+ else
+ raise Config::OtherResolvError.new(reply_name.to_s)
+ end
+ }
+ ensure
+ requester&.close
+ end
+ end
+
+ def make_udp_requester # :nodoc:
+ nameserver_port = @config.nameserver_port
+ if nameserver_port.length == 1
+ Requester::ConnectedUDP.new(*nameserver_port[0])
+ else
+ Requester::UnconnectedUDP.new(*nameserver_port)
+ end
+ end
+
+ def make_tcp_requester(host, port) # :nodoc:
+ return Requester::TCP.new(host, port)
+ end
+
+ def extract_resources(msg, name, typeclass) # :nodoc:
+ if typeclass < Resource::ANY
+ n0 = Name.create(name)
+ msg.each_resource {|n, ttl, data|
+ yield data if n0 == n
+ }
+ end
+ yielded = false
+ n0 = Name.create(name)
+ msg.each_resource {|n, ttl, data|
+ if n0 == n
+ case data
+ when typeclass
+ yield data
+ yielded = true
+ when Resource::CNAME
+ n0 = data.name
+ end
+ end
+ }
+ return if yielded
+ msg.each_resource {|n, ttl, data|
+ if n0 == n
+ case data
+ when typeclass
+ yield data
+ end
+ end
+ }
+ end
+
+ if defined? SecureRandom
+ def self.random(arg) # :nodoc:
+ begin
+ SecureRandom.random_number(arg)
+ rescue NotImplementedError
+ rand(arg)
+ end
+ end
+ else
+ def self.random(arg) # :nodoc:
+ rand(arg)
+ end
+ end
+
+ RequestID = {} # :nodoc:
+ RequestIDMutex = Thread::Mutex.new # :nodoc:
+
+ def self.allocate_request_id(host, port) # :nodoc:
+ id = nil
+ RequestIDMutex.synchronize {
+ h = (RequestID[[host, port]] ||= {})
+ begin
+ id = random(0x0000..0xffff)
+ end while h[id]
+ h[id] = true
+ }
+ id
+ end
+
+ def self.free_request_id(host, port, id) # :nodoc:
+ RequestIDMutex.synchronize {
+ key = [host, port]
+ if h = RequestID[key]
+ h.delete id
+ if h.empty?
+ RequestID.delete key
+ end
+ end
+ }
+ end
+
+ def self.bind_random_port(udpsock, bind_host="0.0.0.0") # :nodoc:
+ begin
+ port = random(1024..65535)
+ udpsock.bind(bind_host, port)
+ rescue Errno::EADDRINUSE, # POSIX
+ Errno::EACCES, # SunOS: See PRIV_SYS_NFS in privileges(5)
+ Errno::EPERM # FreeBSD: security.mac.portacl.port_high is configurable. See mac_portacl(4).
+ retry
+ end
+ end
+
+ class Requester # :nodoc:
+ def initialize
+ @senders = {}
+ @socks = nil
+ end
+
+ def request(sender, tout)
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ timelimit = start + tout
+ begin
+ sender.send
+ rescue Errno::EHOSTUNREACH, # multi-homed IPv6 may generate this
+ Errno::ENETUNREACH
+ raise ResolvTimeout
+ end
+ while true
+ before_select = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ timeout = timelimit - before_select
+ if timeout <= 0
+ raise ResolvTimeout
+ end
+ if @socks.size == 1
+ select_result = @socks[0].wait_readable(timeout) ? [ @socks ] : nil
+ else
+ select_result = IO.select(@socks, nil, nil, timeout)
+ end
+ if !select_result
+ after_select = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ next if after_select < timelimit
+ raise ResolvTimeout
+ end
+ begin
+ reply, from = recv_reply(select_result[0])
+ rescue Errno::ECONNREFUSED, # GNU/Linux, FreeBSD
+ Errno::ECONNRESET # Windows
+ # No name server running on the server?
+ # Don't wait anymore.
+ raise ResolvTimeout
+ end
+ begin
+ msg = Message.decode(reply)
+ rescue DecodeError
+ next # broken DNS message ignored
+ end
+ if sender == sender_for(from, msg)
+ break
+ else
+ # unexpected DNS message ignored
+ end
+ end
+ return msg, sender.data
+ end
+
+ def sender_for(addr, msg)
+ @senders[[addr,msg.id]]
+ end
+
+ def close
+ socks = @socks
+ @socks = nil
+ socks&.each(&:close)
+ end
+
+ class Sender # :nodoc:
+ def initialize(msg, data, sock)
+ @msg = msg
+ @data = data
+ @sock = sock
+ end
+ end
+
+ class UnconnectedUDP < Requester # :nodoc:
+ def initialize(*nameserver_port)
+ super()
+ @nameserver_port = nameserver_port
+ @initialized = false
+ @mutex = Thread::Mutex.new
+ end
+
+ def lazy_initialize
+ @mutex.synchronize {
+ next if @initialized
+ @initialized = true
+ @socks_hash = {}
+ @socks = []
+ @nameserver_port.each {|host, port|
+ if host.index(':')
+ bind_host = "::"
+ af = Socket::AF_INET6
+ else
+ bind_host = "0.0.0.0"
+ af = Socket::AF_INET
+ end
+ next if @socks_hash[bind_host]
+ begin
+ sock = UDPSocket.new(af)
+ rescue Errno::EAFNOSUPPORT, Errno::EPROTONOSUPPORT
+ next # The kernel doesn't support the address family.
+ end
+ @socks << sock
+ @socks_hash[bind_host] = sock
+ sock.do_not_reverse_lookup = true
+ DNS.bind_random_port(sock, bind_host)
+ }
+ }
+ self
+ end
+
+ def recv_reply(readable_socks)
+ lazy_initialize
+ reply, from = readable_socks[0].recvfrom(UDPSize)
+ return reply, [from[3],from[1]]
+ end
+
+ def sender(msg, data, host, port=Port)
+ host = Addrinfo.ip(host).ip_address
+ lazy_initialize
+ sock = @socks_hash[host.index(':') ? "::" : "0.0.0.0"]
+ return nil if !sock
+ service = [host, port]
+ id = DNS.allocate_request_id(host, port)
+ request = msg.encode
+ request[0,2] = [id].pack('n')
+ return @senders[[service, id]] =
+ Sender.new(request, data, sock, host, port)
+ end
+
+ def close
+ @mutex.synchronize {
+ if @initialized
+ super
+ @senders.each_key {|service, id|
+ DNS.free_request_id(service[0], service[1], id)
+ }
+ @initialized = false
+ end
+ }
+ end
+
+ class Sender < Requester::Sender # :nodoc:
+ def initialize(msg, data, sock, host, port)
+ super(msg, data, sock)
+ @host = host
+ @port = port
+ end
+ attr_reader :data
+
+ def send
+ raise "@sock is nil." if @sock.nil?
+ @sock.send(@msg, 0, @host, @port)
+ end
+ end
+ end
+
+ class ConnectedUDP < Requester # :nodoc:
+ def initialize(host, port=Port)
+ super()
+ @host = host
+ @port = port
+ @mutex = Thread::Mutex.new
+ @initialized = false
+ end
+
+ def lazy_initialize
+ @mutex.synchronize {
+ next if @initialized
+ @initialized = true
+ is_ipv6 = @host.index(':')
+ sock = UDPSocket.new(is_ipv6 ? Socket::AF_INET6 : Socket::AF_INET)
+ @socks = [sock]
+ sock.do_not_reverse_lookup = true
+ DNS.bind_random_port(sock, is_ipv6 ? "::" : "0.0.0.0")
+ sock.connect(@host, @port)
+ }
+ self
+ end
+
+ def recv_reply(readable_socks)
+ lazy_initialize
+ reply = readable_socks[0].recv(UDPSize)
+ return reply, nil
+ end
+
+ def sender(msg, data, host=@host, port=@port)
+ lazy_initialize
+ unless host == @host && port == @port
+ raise RequestError.new("host/port don't match: #{host}:#{port}")
+ end
+ id = DNS.allocate_request_id(@host, @port)
+ request = msg.encode
+ request[0,2] = [id].pack('n')
+ return @senders[[nil,id]] = Sender.new(request, data, @socks[0])
+ end
+
+ def close
+ @mutex.synchronize do
+ if @initialized
+ super
+ @senders.each_key {|from, id|
+ DNS.free_request_id(@host, @port, id)
+ }
+ @initialized = false
+ end
+ end
+ end
+
+ class Sender < Requester::Sender # :nodoc:
+ def send
+ raise "@sock is nil." if @sock.nil?
+ @sock.send(@msg, 0)
+ end
+ attr_reader :data
+ end
+ end
+
+ class MDNSOneShot < UnconnectedUDP # :nodoc:
+ def sender(msg, data, host, port=Port)
+ lazy_initialize
+ id = DNS.allocate_request_id(host, port)
+ request = msg.encode
+ request[0,2] = [id].pack('n')
+ sock = @socks_hash[host.index(':') ? "::" : "0.0.0.0"]
+ return @senders[id] =
+ UnconnectedUDP::Sender.new(request, data, sock, host, port)
+ end
+
+ def sender_for(addr, msg)
+ lazy_initialize
+ @senders[msg.id]
+ end
+ end
+
+ class TCP < Requester # :nodoc:
+ def initialize(host, port=Port)
+ super()
+ @host = host
+ @port = port
+ sock = TCPSocket.new(@host, @port)
+ @socks = [sock]
+ @senders = {}
+ end
+
+ def recv_reply(readable_socks)
+ len = readable_socks[0].read(2).unpack('n')[0]
+ reply = @socks[0].read(len)
+ return reply, nil
+ end
+
+ def sender(msg, data, host=@host, port=@port)
+ unless host == @host && port == @port
+ raise RequestError.new("host/port don't match: #{host}:#{port}")
+ end
+ id = DNS.allocate_request_id(@host, @port)
+ request = msg.encode
+ request[0,2] = [request.length, id].pack('nn')
+ return @senders[[nil,id]] = Sender.new(request, data, @socks[0])
+ end
+
+ class Sender < Requester::Sender # :nodoc:
+ def send
+ @sock.print(@msg)
+ @sock.flush
+ end
+ attr_reader :data
+ end
+
+ def close
+ super
+ @senders.each_key {|from,id|
+ DNS.free_request_id(@host, @port, id)
+ }
+ end
+ end
+
+ ##
+ # Indicates a problem with the DNS request.
+
+ class RequestError < StandardError
+ end
+ end
+
+ class Config # :nodoc:
+ def initialize(config_info=nil)
+ @mutex = Thread::Mutex.new
+ @config_info = config_info
+ @initialized = nil
+ @timeouts = nil
+ end
+
+ def timeouts=(values)
+ if values
+ values = Array(values)
+ values.each do |t|
+ Numeric === t or raise ArgumentError, "#{t.inspect} is not numeric"
+ t > 0.0 or raise ArgumentError, "timeout=#{t} must be positive"
+ end
+ @timeouts = values
+ else
+ @timeouts = nil
+ end
+ end
+
+ def Config.parse_resolv_conf(filename)
+ nameserver = []
+ search = nil
+ ndots = 1
+ File.open(filename, 'rb') {|f|
+ f.each {|line|
+ line.sub!(/[#;].*/, '')
+ keyword, *args = line.split(/\s+/)
+ next unless keyword
+ case keyword
+ when 'nameserver'
+ nameserver.concat(args)
+ when 'domain'
+ next if args.empty?
+ search = [args[0]]
+ when 'search'
+ next if args.empty?
+ search = args
+ when 'options'
+ args.each {|arg|
+ case arg
+ when /\Andots:(\d+)\z/
+ ndots = $1.to_i
+ end
+ }
+ end
+ }
+ }
+ return { :nameserver => nameserver, :search => search, :ndots => ndots }
+ end
+
+ def Config.default_config_hash(filename="/etc/resolv.conf")
+ if File.exist? filename
+ config_hash = Config.parse_resolv_conf(filename)
+ else
+ if /mswin|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM
+ require 'win32/resolv'
+ search, nameserver = Win32::Resolv.get_resolv_info
+ config_hash = {}
+ config_hash[:nameserver] = nameserver if nameserver
+ config_hash[:search] = [search].flatten if search
+ end
+ end
+ config_hash || {}
+ end
+
+ def lazy_initialize
+ @mutex.synchronize {
+ unless @initialized
+ @nameserver_port = []
+ @use_ipv6 = nil
+ @search = nil
+ @ndots = 1
+ case @config_info
+ when nil
+ config_hash = Config.default_config_hash
+ when String
+ config_hash = Config.parse_resolv_conf(@config_info)
+ when Hash
+ config_hash = @config_info.dup
+ if String === config_hash[:nameserver]
+ config_hash[:nameserver] = [config_hash[:nameserver]]
+ end
+ if String === config_hash[:search]
+ config_hash[:search] = [config_hash[:search]]
+ end
+ else
+ raise ArgumentError.new("invalid resolv configuration: #{@config_info.inspect}")
+ end
+ if config_hash.include? :nameserver
+ @nameserver_port = config_hash[:nameserver].map {|ns| [ns, Port] }
+ end
+ if config_hash.include? :nameserver_port
+ @nameserver_port = config_hash[:nameserver_port].map {|ns, port| [ns, (port || Port)] }
+ end
+ if config_hash.include? :use_ipv6
+ @use_ipv6 = config_hash[:use_ipv6]
+ end
+ @search = config_hash[:search] if config_hash.include? :search
+ @ndots = config_hash[:ndots] if config_hash.include? :ndots
+ @raise_timeout_errors = config_hash[:raise_timeout_errors]
+
+ if @nameserver_port.empty?
+ @nameserver_port << ['0.0.0.0', Port]
+ end
+ if @search
+ @search = @search.map {|arg| Label.split(arg) }
+ else
+ hostname = Socket.gethostname
+ if /\./ =~ hostname
+ @search = [Label.split($')]
+ else
+ @search = [[]]
+ end
+ end
+
+ if !@nameserver_port.kind_of?(Array) ||
+ @nameserver_port.any? {|ns_port|
+ !(Array === ns_port) ||
+ ns_port.length != 2
+ !(String === ns_port[0]) ||
+ !(Integer === ns_port[1])
+ }
+ raise ArgumentError.new("invalid nameserver config: #{@nameserver_port.inspect}")
+ end
+
+ if !@search.kind_of?(Array) ||
+ !@search.all? {|ls| ls.all? {|l| Label::Str === l } }
+ raise ArgumentError.new("invalid search config: #{@search.inspect}")
+ end
+
+ if !@ndots.kind_of?(Integer)
+ raise ArgumentError.new("invalid ndots config: #{@ndots.inspect}")
+ end
+
+ @initialized = true
+ end
+ }
+ self
+ end
+
+ def single?
+ lazy_initialize
+ if @nameserver_port.length == 1
+ return @nameserver_port[0]
+ else
+ return nil
+ end
+ end
+
+ def nameserver_port
+ @nameserver_port
+ end
+
+ def use_ipv6?
+ @use_ipv6
+ end
+
+ def generate_candidates(name)
+ candidates = nil
+ name = Name.create(name)
+ if name.absolute?
+ candidates = [name]
+ else
+ if @ndots <= name.length - 1
+ candidates = [Name.new(name.to_a)]
+ else
+ candidates = []
+ end
+ candidates.concat(@search.map {|domain| Name.new(name.to_a + domain)})
+ fname = Name.create("#{name}.")
+ if !candidates.include?(fname)
+ candidates << fname
+ end
+ end
+ return candidates
+ end
+
+ InitialTimeout = 5
+
+ def generate_timeouts
+ ts = [InitialTimeout]
+ ts << ts[-1] * 2 / @nameserver_port.length
+ ts << ts[-1] * 2
+ ts << ts[-1] * 2
+ return ts
+ end
+
+ def resolv(name)
+ candidates = generate_candidates(name)
+ timeouts = @timeouts || generate_timeouts
+ timeout_error = false
+ begin
+ candidates.each {|candidate|
+ begin
+ timeouts.each {|tout|
+ @nameserver_port.each {|nameserver, port|
+ begin
+ yield candidate, tout, nameserver, port
+ rescue ResolvTimeout
+ end
+ }
+ }
+ timeout_error = true
+ raise ResolvError.new("DNS resolv timeout: #{name}")
+ rescue NXDomain
+ end
+ }
+ rescue ResolvError
+ raise if @raise_timeout_errors && timeout_error
+ end
+ end
+
+ ##
+ # Indicates no such domain was found.
+
+ class NXDomain < ResolvError
+ end
+
+ ##
+ # Indicates some other unhandled resolver error was encountered.
+
+ class OtherResolvError < ResolvError
+ end
+ end
+
+ module OpCode # :nodoc:
+ Query = 0
+ IQuery = 1
+ Status = 2
+ Notify = 4
+ Update = 5
+ end
+
+ module RCode # :nodoc:
+ NoError = 0
+ FormErr = 1
+ ServFail = 2
+ NXDomain = 3
+ NotImp = 4
+ Refused = 5
+ YXDomain = 6
+ YXRRSet = 7
+ NXRRSet = 8
+ NotAuth = 9
+ NotZone = 10
+ BADVERS = 16
+ BADSIG = 16
+ BADKEY = 17
+ BADTIME = 18
+ BADMODE = 19
+ BADNAME = 20
+ BADALG = 21
+ end
+
+ ##
+ # Indicates that the DNS response was unable to be decoded.
+
+ class DecodeError < StandardError
+ end
+
+ ##
+ # Indicates that the DNS request was unable to be encoded.
+
+ class EncodeError < StandardError
+ end
+
+ module Label # :nodoc:
+ def self.split(arg)
+ labels = []
+ arg.scan(/[^\.]+/) {labels << Str.new($&)}
+ return labels
+ end
+
+ class Str # :nodoc:
+ def initialize(string)
+ @string = string
+ # case insensivity of DNS labels doesn't apply non-ASCII characters. [RFC 4343]
+ # This assumes @string is given in ASCII compatible encoding.
+ @downcase = string.b.downcase
+ end
+ attr_reader :string, :downcase
+
+ def to_s
+ return @string
+ end
+
+ def inspect
+ return "#<#{self.class} #{self}>"
+ end
+
+ def ==(other)
+ return self.class == other.class && @downcase == other.downcase
+ end
+
+ def eql?(other)
+ return self == other
+ end
+
+ def hash
+ return @downcase.hash
+ end
+ end
+ end
+
+ ##
+ # A representation of a DNS name.
+
+ class Name
+
+ ##
+ # Creates a new DNS name from +arg+. +arg+ can be:
+ #
+ # Name:: returns +arg+.
+ # String:: Creates a new Name.
+
+ def self.create(arg)
+ case arg
+ when Name
+ return arg
+ when String
+ return Name.new(Label.split(arg), /\.\z/ =~ arg ? true : false)
+ else
+ raise ArgumentError.new("cannot interpret as DNS name: #{arg.inspect}")
+ end
+ end
+
+ def initialize(labels, absolute=true) # :nodoc:
+ labels = labels.map {|label|
+ case label
+ when String then Label::Str.new(label)
+ when Label::Str then label
+ else
+ raise ArgumentError, "unexpected label: #{label.inspect}"
+ end
+ }
+ @labels = labels
+ @absolute = absolute
+ end
+
+ def inspect # :nodoc:
+ "#<#{self.class}: #{self}#{@absolute ? '.' : ''}>"
+ end
+
+ ##
+ # True if this name is absolute.
+
+ def absolute?
+ return @absolute
+ end
+
+ def ==(other) # :nodoc:
+ return false unless Name === other
+ return false unless @absolute == other.absolute?
+ return @labels == other.to_a
+ end
+
+ alias eql? == # :nodoc:
+
+ ##
+ # Returns true if +other+ is a subdomain.
+ #
+ # Example:
+ #
+ # domain = Gem::Resolv::DNS::Name.create("y.z")
+ # p Gem::Resolv::DNS::Name.create("w.x.y.z").subdomain_of?(domain) #=> true
+ # p Gem::Resolv::DNS::Name.create("x.y.z").subdomain_of?(domain) #=> true
+ # p Gem::Resolv::DNS::Name.create("y.z").subdomain_of?(domain) #=> false
+ # p Gem::Resolv::DNS::Name.create("z").subdomain_of?(domain) #=> false
+ # p Gem::Resolv::DNS::Name.create("x.y.z.").subdomain_of?(domain) #=> false
+ # p Gem::Resolv::DNS::Name.create("w.z").subdomain_of?(domain) #=> false
+ #
+
+ def subdomain_of?(other)
+ raise ArgumentError, "not a domain name: #{other.inspect}" unless Name === other
+ return false if @absolute != other.absolute?
+ other_len = other.length
+ return false if @labels.length <= other_len
+ return @labels[-other_len, other_len] == other.to_a
+ end
+
+ def hash # :nodoc:
+ return @labels.hash ^ @absolute.hash
+ end
+
+ def to_a # :nodoc:
+ return @labels
+ end
+
+ def length # :nodoc:
+ return @labels.length
+ end
+
+ def [](i) # :nodoc:
+ return @labels[i]
+ end
+
+ ##
+ # returns the domain name as a string.
+ #
+ # The domain name doesn't have a trailing dot even if the name object is
+ # absolute.
+ #
+ # Example:
+ #
+ # p Gem::Resolv::DNS::Name.create("x.y.z.").to_s #=> "x.y.z"
+ # p Gem::Resolv::DNS::Name.create("x.y.z").to_s #=> "x.y.z"
+
+ def to_s
+ return @labels.join('.')
+ end
+ end
+
+ class Message # :nodoc:
+ @@identifier = -1
+
+ def initialize(id = (@@identifier += 1) & 0xffff)
+ @id = id
+ @qr = 0
+ @opcode = 0
+ @aa = 0
+ @tc = 0
+ @rd = 0 # recursion desired
+ @ra = 0 # recursion available
+ @rcode = 0
+ @question = []
+ @answer = []
+ @authority = []
+ @additional = []
+ end
+
+ attr_accessor :id, :qr, :opcode, :aa, :tc, :rd, :ra, :rcode
+ attr_reader :question, :answer, :authority, :additional
+
+ def ==(other)
+ return @id == other.id &&
+ @qr == other.qr &&
+ @opcode == other.opcode &&
+ @aa == other.aa &&
+ @tc == other.tc &&
+ @rd == other.rd &&
+ @ra == other.ra &&
+ @rcode == other.rcode &&
+ @question == other.question &&
+ @answer == other.answer &&
+ @authority == other.authority &&
+ @additional == other.additional
+ end
+
+ def add_question(name, typeclass)
+ @question << [Name.create(name), typeclass]
+ end
+
+ def each_question
+ @question.each {|name, typeclass|
+ yield name, typeclass
+ }
+ end
+
+ def add_answer(name, ttl, data)
+ @answer << [Name.create(name), ttl, data]
+ end
+
+ def each_answer
+ @answer.each {|name, ttl, data|
+ yield name, ttl, data
+ }
+ end
+
+ def add_authority(name, ttl, data)
+ @authority << [Name.create(name), ttl, data]
+ end
+
+ def each_authority
+ @authority.each {|name, ttl, data|
+ yield name, ttl, data
+ }
+ end
+
+ def add_additional(name, ttl, data)
+ @additional << [Name.create(name), ttl, data]
+ end
+
+ def each_additional
+ @additional.each {|name, ttl, data|
+ yield name, ttl, data
+ }
+ end
+
+ def each_resource
+ each_answer {|name, ttl, data| yield name, ttl, data}
+ each_authority {|name, ttl, data| yield name, ttl, data}
+ each_additional {|name, ttl, data| yield name, ttl, data}
+ end
+
+ def encode
+ return MessageEncoder.new {|msg|
+ msg.put_pack('nnnnnn',
+ @id,
+ (@qr & 1) << 15 |
+ (@opcode & 15) << 11 |
+ (@aa & 1) << 10 |
+ (@tc & 1) << 9 |
+ (@rd & 1) << 8 |
+ (@ra & 1) << 7 |
+ (@rcode & 15),
+ @question.length,
+ @answer.length,
+ @authority.length,
+ @additional.length)
+ @question.each {|q|
+ name, typeclass = q
+ msg.put_name(name)
+ msg.put_pack('nn', typeclass::TypeValue, typeclass::ClassValue)
+ }
+ [@answer, @authority, @additional].each {|rr|
+ rr.each {|r|
+ name, ttl, data = r
+ msg.put_name(name)
+ msg.put_pack('nnN', data.class::TypeValue, data.class::ClassValue, ttl)
+ msg.put_length16 {data.encode_rdata(msg)}
+ }
+ }
+ }.to_s
+ end
+
+ class MessageEncoder # :nodoc:
+ def initialize
+ @data = ''.dup
+ @names = {}
+ yield self
+ end
+
+ def to_s
+ return @data
+ end
+
+ def put_bytes(d)
+ @data << d
+ end
+
+ def put_pack(template, *d)
+ @data << d.pack(template)
+ end
+
+ def put_length16
+ length_index = @data.length
+ @data << "\0\0"
+ data_start = @data.length
+ yield
+ data_end = @data.length
+ @data[length_index, 2] = [data_end - data_start].pack("n")
+ end
+
+ def put_string(d)
+ self.put_pack("C", d.length)
+ @data << d
+ end
+
+ def put_string_list(ds)
+ ds.each {|d|
+ self.put_string(d)
+ }
+ end
+
+ def put_name(d, compress: true)
+ put_labels(d.to_a, compress: compress)
+ end
+
+ def put_labels(d, compress: true)
+ d.each_index {|i|
+ domain = d[i..-1]
+ if compress && idx = @names[domain]
+ self.put_pack("n", 0xc000 | idx)
+ return
+ else
+ if @data.length < 0x4000
+ @names[domain] = @data.length
+ end
+ self.put_label(d[i])
+ end
+ }
+ @data << "\0"
+ end
+
+ def put_label(d)
+ self.put_string(d.to_s)
+ end
+ end
+
+ def Message.decode(m)
+ o = Message.new(0)
+ MessageDecoder.new(m) {|msg|
+ id, flag, qdcount, ancount, nscount, arcount =
+ msg.get_unpack('nnnnnn')
+ o.id = id
+ o.tc = (flag >> 9) & 1
+ o.rcode = flag & 15
+ return o unless o.tc.zero?
+
+ o.qr = (flag >> 15) & 1
+ o.opcode = (flag >> 11) & 15
+ o.aa = (flag >> 10) & 1
+ o.rd = (flag >> 8) & 1
+ o.ra = (flag >> 7) & 1
+ (1..qdcount).each {
+ name, typeclass = msg.get_question
+ o.add_question(name, typeclass)
+ }
+ (1..ancount).each {
+ name, ttl, data = msg.get_rr
+ o.add_answer(name, ttl, data)
+ }
+ (1..nscount).each {
+ name, ttl, data = msg.get_rr
+ o.add_authority(name, ttl, data)
+ }
+ (1..arcount).each {
+ name, ttl, data = msg.get_rr
+ o.add_additional(name, ttl, data)
+ }
+ }
+ return o
+ end
+
+ class MessageDecoder # :nodoc:
+ def initialize(data)
+ @data = data
+ @index = 0
+ @limit = data.bytesize
+ yield self
+ end
+
+ def inspect
+ "\#<#{self.class}: #{@data.byteslice(0, @index).inspect} #{@data.byteslice(@index..-1).inspect}>"
+ end
+
+ def get_length16
+ len, = self.get_unpack('n')
+ save_limit = @limit
+ @limit = @index + len
+ d = yield(len)
+ if @index < @limit
+ raise DecodeError.new("junk exists")
+ elsif @limit < @index
+ raise DecodeError.new("limit exceeded")
+ end
+ @limit = save_limit
+ return d
+ end
+
+ def get_bytes(len = @limit - @index)
+ raise DecodeError.new("limit exceeded") if @limit < @index + len
+ d = @data.byteslice(@index, len)
+ @index += len
+ return d
+ end
+
+ def get_unpack(template)
+ len = 0
+ template.each_byte {|byte|
+ byte = "%c" % byte
+ case byte
+ when ?c, ?C
+ len += 1
+ when ?n
+ len += 2
+ when ?N
+ len += 4
+ else
+ raise StandardError.new("unsupported template: '#{byte.chr}' in '#{template}'")
+ end
+ }
+ raise DecodeError.new("limit exceeded") if @limit < @index + len
+ arr = @data.unpack("@#{@index}#{template}")
+ @index += len
+ return arr
+ end
+
+ def get_string
+ raise DecodeError.new("limit exceeded") if @limit <= @index
+ len = @data.getbyte(@index)
+ raise DecodeError.new("limit exceeded") if @limit < @index + 1 + len
+ d = @data.byteslice(@index + 1, len)
+ @index += 1 + len
+ return d
+ end
+
+ def get_string_list
+ strings = []
+ while @index < @limit
+ strings << self.get_string
+ end
+ strings
+ end
+
+ def get_list
+ [].tap do |values|
+ while @index < @limit
+ values << yield
+ end
+ end
+ end
+
+ def get_name
+ return Name.new(self.get_labels)
+ end
+
+ def get_labels
+ prev_index = @index
+ save_index = nil
+ d = []
+ while true
+ raise DecodeError.new("limit exceeded") if @limit <= @index
+ case @data.getbyte(@index)
+ when 0
+ @index += 1
+ if save_index
+ @index = save_index
+ end
+ return d
+ when 192..255
+ idx = self.get_unpack('n')[0] & 0x3fff
+ if prev_index <= idx
+ raise DecodeError.new("non-backward name pointer")
+ end
+ prev_index = idx
+ if !save_index
+ save_index = @index
+ end
+ @index = idx
+ else
+ d << self.get_label
+ end
+ end
+ end
+
+ def get_label
+ return Label::Str.new(self.get_string)
+ end
+
+ def get_question
+ name = self.get_name
+ type, klass = self.get_unpack("nn")
+ return name, Resource.get_class(type, klass)
+ end
+
+ def get_rr
+ name = self.get_name
+ type, klass, ttl = self.get_unpack('nnN')
+ typeclass = Resource.get_class(type, klass)
+ res = self.get_length16 do
+ begin
+ typeclass.decode_rdata self
+ rescue => e
+ raise DecodeError, e.message, e.backtrace
+ end
+ end
+ res.instance_variable_set :@ttl, ttl
+ return name, ttl, res
+ end
+ end
+ end
+
+ ##
+ # SvcParams for service binding RRs. [RFC9460]
+
+ class SvcParams
+ include Enumerable
+
+ ##
+ # Create a list of SvcParams with the given initial content.
+ #
+ # +params+ has to be an enumerable of +SvcParam+s.
+ # If its content has +SvcParam+s with the duplicate key,
+ # the one appears last takes precedence.
+
+ def initialize(params = [])
+ @params = {}
+
+ params.each do |param|
+ add param
+ end
+ end
+
+ ##
+ # Get SvcParam for the given +key+ in this list.
+
+ def [](key)
+ @params[canonical_key(key)]
+ end
+
+ ##
+ # Get the number of SvcParams in this list.
+
+ def count
+ @params.count
+ end
+
+ ##
+ # Get whether this list is empty.
+
+ def empty?
+ @params.empty?
+ end
+
+ ##
+ # Add the SvcParam +param+ to this list, overwriting the existing one with the same key.
+
+ def add(param)
+ @params[param.class.key_number] = param
+ end
+
+ ##
+ # Remove the +SvcParam+ with the given +key+ and return it.
+
+ def delete(key)
+ @params.delete(canonical_key(key))
+ end
+
+ ##
+ # Enumerate the +SvcParam+s in this list.
+
+ def each(&block)
+ return enum_for(:each) unless block
+ @params.each_value(&block)
+ end
+
+ def encode(msg) # :nodoc:
+ @params.keys.sort.each do |key|
+ msg.put_pack('n', key)
+ msg.put_length16 do
+ @params.fetch(key).encode(msg)
+ end
+ end
+ end
+
+ def self.decode(msg) # :nodoc:
+ params = msg.get_list do
+ key, = msg.get_unpack('n')
+ msg.get_length16 do
+ SvcParam::ClassHash[key].decode(msg)
+ end
+ end
+
+ return self.new(params)
+ end
+
+ private
+
+ def canonical_key(key) # :nodoc:
+ case key
+ when Integer
+ key
+ when /\Akey(\d+)\z/
+ Integer($1)
+ when Symbol
+ SvcParam::ClassHash[key].key_number
+ else
+ raise TypeError, 'key must be either String or Symbol'
+ end
+ end
+ end
+
+
+ ##
+ # Base class for SvcParam. [RFC9460]
+
+ class SvcParam
+
+ ##
+ # Get the presentation name of the SvcParamKey.
+
+ def self.key_name
+ const_get(:KeyName)
+ end
+
+ ##
+ # Get the registered number of the SvcParamKey.
+
+ def self.key_number
+ const_get(:KeyNumber)
+ end
+
+ ClassHash = Hash.new do |h, key| # :nodoc:
+ case key
+ when Integer
+ Generic.create(key)
+ when /\Akey(?<key>\d+)\z/
+ Generic.create(key.to_int)
+ when Symbol
+ raise KeyError, "unknown key #{key}"
+ else
+ raise TypeError, 'key must be either String or Symbol'
+ end
+ end
+
+ ##
+ # Generic SvcParam abstract class.
+
+ class Generic < SvcParam
+
+ ##
+ # SvcParamValue in wire-format byte string.
+
+ attr_reader :value
+
+ ##
+ # Create generic SvcParam
+
+ def initialize(value)
+ @value = value
+ end
+
+ def encode(msg) # :nodoc:
+ msg.put_bytes(@value)
+ end
+
+ def self.decode(msg) # :nodoc:
+ return self.new(msg.get_bytes)
+ end
+
+ def self.create(key_number)
+ c = Class.new(Generic)
+ key_name = :"key#{key_number}"
+ c.const_set(:KeyName, key_name)
+ c.const_set(:KeyNumber, key_number)
+ self.const_set(:"Key#{key_number}", c)
+ ClassHash[key_name] = ClassHash[key_number] = c
+ return c
+ end
+ end
+
+ ##
+ # "mandatory" SvcParam -- Mandatory keys in service binding RR
+
+ class Mandatory < SvcParam
+ KeyName = :mandatory
+ KeyNumber = 0
+ ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
+
+ ##
+ # Mandatory keys.
+
+ attr_reader :keys
+
+ ##
+ # Initialize "mandatory" ScvParam.
+
+ def initialize(keys)
+ @keys = keys.map(&:to_int)
+ end
+
+ def encode(msg) # :nodoc:
+ @keys.sort.each do |key|
+ msg.put_pack('n', key)
+ end
+ end
+
+ def self.decode(msg) # :nodoc:
+ keys = msg.get_list { msg.get_unpack('n')[0] }
+ return self.new(keys)
+ end
+ end
+
+ ##
+ # "alpn" SvcParam -- Additional supported protocols
+
+ class ALPN < SvcParam
+ KeyName = :alpn
+ KeyNumber = 1
+ ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
+
+ ##
+ # Supported protocol IDs.
+
+ attr_reader :protocol_ids
+
+ ##
+ # Initialize "alpn" ScvParam.
+
+ def initialize(protocol_ids)
+ @protocol_ids = protocol_ids.map(&:to_str)
+ end
+
+ def encode(msg) # :nodoc:
+ msg.put_string_list(@protocol_ids)
+ end
+
+ def self.decode(msg) # :nodoc:
+ return self.new(msg.get_string_list)
+ end
+ end
+
+ ##
+ # "no-default-alpn" SvcParam -- No support for default protocol
+
+ class NoDefaultALPN < SvcParam
+ KeyName = :'no-default-alpn'
+ KeyNumber = 2
+ ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
+
+ def encode(msg) # :nodoc:
+ # no payload
+ end
+
+ def self.decode(msg) # :nodoc:
+ return self.new
+ end
+ end
+
+ ##
+ # "port" SvcParam -- Port for alternative endpoint
+
+ class Port < SvcParam
+ KeyName = :port
+ KeyNumber = 3
+ ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
+
+ ##
+ # Port number.
+
+ attr_reader :port
+
+ ##
+ # Initialize "port" ScvParam.
+
+ def initialize(port)
+ @port = port.to_int
+ end
+
+ def encode(msg) # :nodoc:
+ msg.put_pack('n', @port)
+ end
+
+ def self.decode(msg) # :nodoc:
+ port, = msg.get_unpack('n')
+ return self.new(port)
+ end
+ end
+
+ ##
+ # "ipv4hint" SvcParam -- IPv4 address hints
+
+ class IPv4Hint < SvcParam
+ KeyName = :ipv4hint
+ KeyNumber = 4
+ ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
+
+ ##
+ # Set of IPv4 addresses.
+
+ attr_reader :addresses
+
+ ##
+ # Initialize "ipv4hint" ScvParam.
+
+ def initialize(addresses)
+ @addresses = addresses.map {|address| IPv4.create(address) }
+ end
+
+ def encode(msg) # :nodoc:
+ @addresses.each do |address|
+ msg.put_bytes(address.address)
+ end
+ end
+
+ def self.decode(msg) # :nodoc:
+ addresses = msg.get_list { IPv4.new(msg.get_bytes(4)) }
+ return self.new(addresses)
+ end
+ end
+
+ ##
+ # "ipv6hint" SvcParam -- IPv6 address hints
+
+ class IPv6Hint < SvcParam
+ KeyName = :ipv6hint
+ KeyNumber = 6
+ ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
+
+ ##
+ # Set of IPv6 addresses.
+
+ attr_reader :addresses
+
+ ##
+ # Initialize "ipv6hint" ScvParam.
+
+ def initialize(addresses)
+ @addresses = addresses.map {|address| IPv6.create(address) }
+ end
+
+ def encode(msg) # :nodoc:
+ @addresses.each do |address|
+ msg.put_bytes(address.address)
+ end
+ end
+
+ def self.decode(msg) # :nodoc:
+ addresses = msg.get_list { IPv6.new(msg.get_bytes(16)) }
+ return self.new(addresses)
+ end
+ end
+
+ ##
+ # "dohpath" SvcParam -- DNS over HTTPS path template [RFC9461]
+
+ class DoHPath < SvcParam
+ KeyName = :dohpath
+ KeyNumber = 7
+ ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
+
+ ##
+ # URI template for DoH queries.
+
+ attr_reader :template
+
+ ##
+ # Initialize "dohpath" ScvParam.
+
+ def initialize(template)
+ @template = template.encode('utf-8')
+ end
+
+ def encode(msg) # :nodoc:
+ msg.put_bytes(@template)
+ end
+
+ def self.decode(msg) # :nodoc:
+ template = msg.get_bytes.force_encoding('utf-8')
+ return self.new(template)
+ end
+ end
+ end
+
+ ##
+ # A DNS query abstract class.
+
+ class Query
+ def encode_rdata(msg) # :nodoc:
+ raise EncodeError.new("#{self.class} is query.")
+ end
+
+ def self.decode_rdata(msg) # :nodoc:
+ raise DecodeError.new("#{self.class} is query.")
+ end
+ end
+
+ ##
+ # A DNS resource abstract class.
+
+ class Resource < Query
+
+ ##
+ # Remaining Time To Live for this Resource.
+
+ attr_reader :ttl
+
+ ClassHash = {} # :nodoc:
+
+ def encode_rdata(msg) # :nodoc:
+ raise NotImplementedError.new
+ end
+
+ def self.decode_rdata(msg) # :nodoc:
+ raise NotImplementedError.new
+ end
+
+ def ==(other) # :nodoc:
+ return false unless self.class == other.class
+ s_ivars = self.instance_variables
+ s_ivars.sort!
+ s_ivars.delete :@ttl
+ o_ivars = other.instance_variables
+ o_ivars.sort!
+ o_ivars.delete :@ttl
+ return s_ivars == o_ivars &&
+ s_ivars.collect {|name| self.instance_variable_get name} ==
+ o_ivars.collect {|name| other.instance_variable_get name}
+ end
+
+ def eql?(other) # :nodoc:
+ return self == other
+ end
+
+ def hash # :nodoc:
+ h = 0
+ vars = self.instance_variables
+ vars.delete :@ttl
+ vars.each {|name|
+ h ^= self.instance_variable_get(name).hash
+ }
+ return h
+ end
+
+ def self.get_class(type_value, class_value) # :nodoc:
+ return ClassHash[[type_value, class_value]] ||
+ Generic.create(type_value, class_value)
+ end
+
+ ##
+ # A generic resource abstract class.
+
+ class Generic < Resource
+
+ ##
+ # Creates a new generic resource.
+
+ def initialize(data)
+ @data = data
+ end
+
+ ##
+ # Data for this generic resource.
+
+ attr_reader :data
+
+ def encode_rdata(msg) # :nodoc:
+ msg.put_bytes(data)
+ end
+
+ def self.decode_rdata(msg) # :nodoc:
+ return self.new(msg.get_bytes)
+ end
+
+ def self.create(type_value, class_value) # :nodoc:
+ c = Class.new(Generic)
+ c.const_set(:TypeValue, type_value)
+ c.const_set(:ClassValue, class_value)
+ Generic.const_set("Type#{type_value}_Class#{class_value}", c)
+ ClassHash[[type_value, class_value]] = c
+ return c
+ end
+ end
+
+ ##
+ # Domain Name resource abstract class.
+
+ class DomainName < Resource
+
+ ##
+ # Creates a new DomainName from +name+.
+
+ def initialize(name)
+ @name = name
+ end
+
+ ##
+ # The name of this DomainName.
+
+ attr_reader :name
+
+ def encode_rdata(msg) # :nodoc:
+ msg.put_name(@name)
+ end
+
+ def self.decode_rdata(msg) # :nodoc:
+ return self.new(msg.get_name)
+ end
+ end
+
+ # Standard (class generic) RRs
+
+ ClassValue = nil # :nodoc:
+
+ ##
+ # An authoritative name server.
+
+ class NS < DomainName
+ TypeValue = 2 # :nodoc:
+ end
+
+ ##
+ # The canonical name for an alias.
+
+ class CNAME < DomainName
+ TypeValue = 5 # :nodoc:
+ end
+
+ ##
+ # Start Of Authority resource.
+
+ class SOA < Resource
+
+ TypeValue = 6 # :nodoc:
+
+ ##
+ # Creates a new SOA record. See the attr documentation for the
+ # details of each argument.
+
+ def initialize(mname, rname, serial, refresh, retry_, expire, minimum)
+ @mname = mname
+ @rname = rname
+ @serial = serial
+ @refresh = refresh
+ @retry = retry_
+ @expire = expire
+ @minimum = minimum
+ end
+
+ ##
+ # Name of the host where the master zone file for this zone resides.
+
+ attr_reader :mname
+
+ ##
+ # The person responsible for this domain name.
+
+ attr_reader :rname
+
+ ##
+ # The version number of the zone file.
+
+ attr_reader :serial
+
+ ##
+ # How often, in seconds, a secondary name server is to check for
+ # updates from the primary name server.
+
+ attr_reader :refresh
+
+ ##
+ # How often, in seconds, a secondary name server is to retry after a
+ # failure to check for a refresh.
+
+ attr_reader :retry
+
+ ##
+ # Time in seconds that a secondary name server is to use the data
+ # before refreshing from the primary name server.
+
+ attr_reader :expire
+
+ ##
+ # The minimum number of seconds to be used for TTL values in RRs.
+
+ attr_reader :minimum
+
+ def encode_rdata(msg) # :nodoc:
+ msg.put_name(@mname)
+ msg.put_name(@rname)
+ msg.put_pack('NNNNN', @serial, @refresh, @retry, @expire, @minimum)
+ end
+
+ def self.decode_rdata(msg) # :nodoc:
+ mname = msg.get_name
+ rname = msg.get_name
+ serial, refresh, retry_, expire, minimum = msg.get_unpack('NNNNN')
+ return self.new(
+ mname, rname, serial, refresh, retry_, expire, minimum)
+ end
+ end
+
+ ##
+ # A Pointer to another DNS name.
+
+ class PTR < DomainName
+ TypeValue = 12 # :nodoc:
+ end
+
+ ##
+ # Host Information resource.
+
+ class HINFO < Resource
+
+ TypeValue = 13 # :nodoc:
+
+ ##
+ # Creates a new HINFO running +os+ on +cpu+.
+
+ def initialize(cpu, os)
+ @cpu = cpu
+ @os = os
+ end
+
+ ##
+ # CPU architecture for this resource.
+
+ attr_reader :cpu
+
+ ##
+ # Operating system for this resource.
+
+ attr_reader :os
+
+ def encode_rdata(msg) # :nodoc:
+ msg.put_string(@cpu)
+ msg.put_string(@os)
+ end
+
+ def self.decode_rdata(msg) # :nodoc:
+ cpu = msg.get_string
+ os = msg.get_string
+ return self.new(cpu, os)
+ end
+ end
+
+ ##
+ # Mailing list or mailbox information.
+
+ class MINFO < Resource
+
+ TypeValue = 14 # :nodoc:
+
+ def initialize(rmailbx, emailbx)
+ @rmailbx = rmailbx
+ @emailbx = emailbx
+ end
+
+ ##
+ # Domain name responsible for this mail list or mailbox.
+
+ attr_reader :rmailbx
+
+ ##
+ # Mailbox to use for error messages related to the mail list or mailbox.
+
+ attr_reader :emailbx
+
+ def encode_rdata(msg) # :nodoc:
+ msg.put_name(@rmailbx)
+ msg.put_name(@emailbx)
+ end
+
+ def self.decode_rdata(msg) # :nodoc:
+ rmailbx = msg.get_string
+ emailbx = msg.get_string
+ return self.new(rmailbx, emailbx)
+ end
+ end
+
+ ##
+ # Mail Exchanger resource.
+
+ class MX < Resource
+
+ TypeValue= 15 # :nodoc:
+
+ ##
+ # Creates a new MX record with +preference+, accepting mail at
+ # +exchange+.
+
+ def initialize(preference, exchange)
+ @preference = preference
+ @exchange = exchange
+ end
+
+ ##
+ # The preference for this MX.
+
+ attr_reader :preference
+
+ ##
+ # The host of this MX.
+
+ attr_reader :exchange
+
+ def encode_rdata(msg) # :nodoc:
+ msg.put_pack('n', @preference)
+ msg.put_name(@exchange)
+ end
+
+ def self.decode_rdata(msg) # :nodoc:
+ preference, = msg.get_unpack('n')
+ exchange = msg.get_name
+ return self.new(preference, exchange)
+ end
+ end
+
+ ##
+ # Unstructured text resource.
+
+ class TXT < Resource
+
+ TypeValue = 16 # :nodoc:
+
+ def initialize(first_string, *rest_strings)
+ @strings = [first_string, *rest_strings]
+ end
+
+ ##
+ # Returns an Array of Strings for this TXT record.
+
+ attr_reader :strings
+
+ ##
+ # Returns the concatenated string from +strings+.
+
+ def data
+ @strings.join("")
+ end
+
+ def encode_rdata(msg) # :nodoc:
+ msg.put_string_list(@strings)
+ end
+
+ def self.decode_rdata(msg) # :nodoc:
+ strings = msg.get_string_list
+ return self.new(*strings)
+ end
+ end
+
+ ##
+ # Location resource
+
+ class LOC < Resource
+
+ TypeValue = 29 # :nodoc:
+
+ def initialize(version, ssize, hprecision, vprecision, latitude, longitude, altitude)
+ @version = version
+ @ssize = Gem::Resolv::LOC::Size.create(ssize)
+ @hprecision = Gem::Resolv::LOC::Size.create(hprecision)
+ @vprecision = Gem::Resolv::LOC::Size.create(vprecision)
+ @latitude = Gem::Resolv::LOC::Coord.create(latitude)
+ @longitude = Gem::Resolv::LOC::Coord.create(longitude)
+ @altitude = Gem::Resolv::LOC::Alt.create(altitude)
+ end
+
+ ##
+ # Returns the version value for this LOC record which should always be 00
+
+ attr_reader :version
+
+ ##
+ # The spherical size of this LOC
+ # in meters using scientific notation as 2 integers of XeY
+
+ attr_reader :ssize
+
+ ##
+ # The horizontal precision using ssize type values
+ # in meters using scientific notation as 2 integers of XeY
+ # for precision use value/2 e.g. 2m = +/-1m
+
+ attr_reader :hprecision
+
+ ##
+ # The vertical precision using ssize type values
+ # in meters using scientific notation as 2 integers of XeY
+ # for precision use value/2 e.g. 2m = +/-1m
+
+ attr_reader :vprecision
+
+ ##
+ # The latitude for this LOC where 2**31 is the equator
+ # in thousandths of an arc second as an unsigned 32bit integer
+
+ attr_reader :latitude
+
+ ##
+ # The longitude for this LOC where 2**31 is the prime meridian
+ # in thousandths of an arc second as an unsigned 32bit integer
+
+ attr_reader :longitude
+
+ ##
+ # The altitude of the LOC above a reference sphere whose surface sits 100km below the WGS84 spheroid
+ # in centimeters as an unsigned 32bit integer
+
+ attr_reader :altitude
+
+
+ def encode_rdata(msg) # :nodoc:
+ msg.put_bytes(@version)
+ msg.put_bytes(@ssize.scalar)
+ msg.put_bytes(@hprecision.scalar)
+ msg.put_bytes(@vprecision.scalar)
+ msg.put_bytes(@latitude.coordinates)
+ msg.put_bytes(@longitude.coordinates)
+ msg.put_bytes(@altitude.altitude)
+ end
+
+ def self.decode_rdata(msg) # :nodoc:
+ version = msg.get_bytes(1)
+ ssize = msg.get_bytes(1)
+ hprecision = msg.get_bytes(1)
+ vprecision = msg.get_bytes(1)
+ latitude = msg.get_bytes(4)
+ longitude = msg.get_bytes(4)
+ altitude = msg.get_bytes(4)
+ return self.new(
+ version,
+ Gem::Resolv::LOC::Size.new(ssize),
+ Gem::Resolv::LOC::Size.new(hprecision),
+ Gem::Resolv::LOC::Size.new(vprecision),
+ Gem::Resolv::LOC::Coord.new(latitude,"lat"),
+ Gem::Resolv::LOC::Coord.new(longitude,"lon"),
+ Gem::Resolv::LOC::Alt.new(altitude)
+ )
+ end
+ end
+
+ ##
+ # A Query type requesting any RR.
+
+ class ANY < Query
+ TypeValue = 255 # :nodoc:
+ end
+
+ ##
+ # CAA resource record defined in RFC 8659
+ #
+ # These records identify certificate authority allowed to issue
+ # certificates for the given domain.
+
+ class CAA < Resource
+ TypeValue = 257
+
+ ##
+ # Creates a new CAA for +flags+, +tag+ and +value+.
+
+ def initialize(flags, tag, value)
+ unless (0..255) === flags
+ raise ArgumentError.new('flags must be an Integer between 0 and 255')
+ end
+ unless (1..15) === tag.bytesize
+ raise ArgumentError.new('length of tag must be between 1 and 15')
+ end
+
+ @flags = flags
+ @tag = tag
+ @value = value
+ end
+
+ ##
+ # Flags for this proprty:
+ # - Bit 0 : 0 = not critical, 1 = critical
+
+ attr_reader :flags
+
+ ##
+ # Property tag ("issue", "issuewild", "iodef"...).
+
+ attr_reader :tag
+
+ ##
+ # Property value.
+
+ attr_reader :value
+
+ ##
+ # Whether the critical flag is set on this property.
+
+ def critical?
+ flags & 0x80 != 0
+ end
+
+ def encode_rdata(msg) # :nodoc:
+ msg.put_pack('C', @flags)
+ msg.put_string(@tag)
+ msg.put_bytes(@value)
+ end
+
+ def self.decode_rdata(msg) # :nodoc:
+ flags, = msg.get_unpack('C')
+ tag = msg.get_string
+ value = msg.get_bytes
+ self.new flags, tag, value
+ end
+ end
+
+ ClassInsensitiveTypes = [ # :nodoc:
+ NS, CNAME, SOA, PTR, HINFO, MINFO, MX, TXT, LOC, ANY, CAA
+ ]
+
+ ##
+ # module IN contains ARPA Internet specific RRs.
+
+ module IN
+
+ ClassValue = 1 # :nodoc:
+
+ ClassInsensitiveTypes.each {|s|
+ c = Class.new(s)
+ c.const_set(:TypeValue, s::TypeValue)
+ c.const_set(:ClassValue, ClassValue)
+ ClassHash[[s::TypeValue, ClassValue]] = c
+ self.const_set(s.name.sub(/.*::/, ''), c)
+ }
+
+ ##
+ # IPv4 Address resource
+
+ class A < Resource
+ TypeValue = 1
+ ClassValue = IN::ClassValue
+ ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
+
+ ##
+ # Creates a new A for +address+.
+
+ def initialize(address)
+ @address = IPv4.create(address)
+ end
+
+ ##
+ # The Gem::Resolv::IPv4 address for this A.
+
+ attr_reader :address
+
+ def encode_rdata(msg) # :nodoc:
+ msg.put_bytes(@address.address)
+ end
+
+ def self.decode_rdata(msg) # :nodoc:
+ return self.new(IPv4.new(msg.get_bytes(4)))
+ end
+ end
+
+ ##
+ # Well Known Service resource.
+
+ class WKS < Resource
+ TypeValue = 11
+ ClassValue = IN::ClassValue
+ ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
+
+ def initialize(address, protocol, bitmap)
+ @address = IPv4.create(address)
+ @protocol = protocol
+ @bitmap = bitmap
+ end
+
+ ##
+ # The host these services run on.
+
+ attr_reader :address
+
+ ##
+ # IP protocol number for these services.
+
+ attr_reader :protocol
+
+ ##
+ # A bit map of enabled services on this host.
+ #
+ # If protocol is 6 (TCP) then the 26th bit corresponds to the SMTP
+ # service (port 25). If this bit is set, then an SMTP server should
+ # be listening on TCP port 25; if zero, SMTP service is not
+ # supported.
+
+ attr_reader :bitmap
+
+ def encode_rdata(msg) # :nodoc:
+ msg.put_bytes(@address.address)
+ msg.put_pack("n", @protocol)
+ msg.put_bytes(@bitmap)
+ end
+
+ def self.decode_rdata(msg) # :nodoc:
+ address = IPv4.new(msg.get_bytes(4))
+ protocol, = msg.get_unpack("n")
+ bitmap = msg.get_bytes
+ return self.new(address, protocol, bitmap)
+ end
+ end
+
+ ##
+ # An IPv6 address record.
+
+ class AAAA < Resource
+ TypeValue = 28
+ ClassValue = IN::ClassValue
+ ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
+
+ ##
+ # Creates a new AAAA for +address+.
+
+ def initialize(address)
+ @address = IPv6.create(address)
+ end
+
+ ##
+ # The Gem::Resolv::IPv6 address for this AAAA.
+
+ attr_reader :address
+
+ def encode_rdata(msg) # :nodoc:
+ msg.put_bytes(@address.address)
+ end
+
+ def self.decode_rdata(msg) # :nodoc:
+ return self.new(IPv6.new(msg.get_bytes(16)))
+ end
+ end
+
+ ##
+ # SRV resource record defined in RFC 2782
+ #
+ # These records identify the hostname and port that a service is
+ # available at.
+
+ class SRV < Resource
+ TypeValue = 33
+ ClassValue = IN::ClassValue
+ ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
+
+ # Create a SRV resource record.
+ #
+ # See the documentation for #priority, #weight, #port and #target
+ # for +priority+, +weight+, +port and +target+ respectively.
+
+ def initialize(priority, weight, port, target)
+ @priority = priority.to_int
+ @weight = weight.to_int
+ @port = port.to_int
+ @target = Name.create(target)
+ end
+
+ # The priority of this target host.
+ #
+ # A client MUST attempt to contact the target host with the
+ # lowest-numbered priority it can reach; target hosts with the same
+ # priority SHOULD be tried in an order defined by the weight field.
+ # The range is 0-65535. Note that it is not widely implemented and
+ # should be set to zero.
+
+ attr_reader :priority
+
+ # A server selection mechanism.
+ #
+ # The weight field specifies a relative weight for entries with the
+ # same priority. Larger weights SHOULD be given a proportionately
+ # higher probability of being selected. The range of this number is
+ # 0-65535. Domain administrators SHOULD use Weight 0 when there
+ # isn't any server selection to do, to make the RR easier to read
+ # for humans (less noisy). Note that it is not widely implemented
+ # and should be set to zero.
+
+ attr_reader :weight
+
+ # The port on this target host of this service.
+ #
+ # The range is 0-65535.
+
+ attr_reader :port
+
+ # The domain name of the target host.
+ #
+ # A target of "." means that the service is decidedly not available
+ # at this domain.
+
+ attr_reader :target
+
+ def encode_rdata(msg) # :nodoc:
+ msg.put_pack("n", @priority)
+ msg.put_pack("n", @weight)
+ msg.put_pack("n", @port)
+ msg.put_name(@target, compress: false)
+ end
+
+ def self.decode_rdata(msg) # :nodoc:
+ priority, = msg.get_unpack("n")
+ weight, = msg.get_unpack("n")
+ port, = msg.get_unpack("n")
+ target = msg.get_name
+ return self.new(priority, weight, port, target)
+ end
+ end
+
+ ##
+ # Common implementation for SVCB-compatible resource records.
+
+ class ServiceBinding
+
+ ##
+ # Create a service binding resource record.
+
+ def initialize(priority, target, params = [])
+ @priority = priority.to_int
+ @target = Name.create(target)
+ @params = SvcParams.new(params)
+ end
+
+ ##
+ # The priority of this target host.
+ #
+ # The range is 0-65535.
+ # If set to 0, this RR is in AliasMode. Otherwise, it is in ServiceMode.
+
+ attr_reader :priority
+
+ ##
+ # The domain name of the target host.
+
+ attr_reader :target
+
+ ##
+ # The service parameters for the target host.
+
+ attr_reader :params
+
+ ##
+ # Whether this RR is in AliasMode.
+
+ def alias_mode?
+ self.priority == 0
+ end
+
+ ##
+ # Whether this RR is in ServiceMode.
+
+ def service_mode?
+ !alias_mode?
+ end
+
+ def encode_rdata(msg) # :nodoc:
+ msg.put_pack("n", @priority)
+ msg.put_name(@target, compress: false)
+ @params.encode(msg)
+ end
+
+ def self.decode_rdata(msg) # :nodoc:
+ priority, = msg.get_unpack("n")
+ target = msg.get_name
+ params = SvcParams.decode(msg)
+ return self.new(priority, target, params)
+ end
+ end
+
+ ##
+ # SVCB resource record [RFC9460]
+
+ class SVCB < ServiceBinding
+ TypeValue = 64
+ ClassValue = IN::ClassValue
+ ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
+ end
+
+ ##
+ # HTTPS resource record [RFC9460]
+
+ class HTTPS < ServiceBinding
+ TypeValue = 65
+ ClassValue = IN::ClassValue
+ ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
+ end
+ end
+ end
+ end
+
+ ##
+ # A Gem::Resolv::DNS IPv4 address.
+
+ class IPv4
+
+ ##
+ # Regular expression IPv4 addresses must match.
+
+ Regex256 = /0
+ |1(?:[0-9][0-9]?)?
+ |2(?:[0-4][0-9]?|5[0-5]?|[6-9])?
+ |[3-9][0-9]?/x
+ Regex = /\A(#{Regex256})\.(#{Regex256})\.(#{Regex256})\.(#{Regex256})\z/
+
+ def self.create(arg)
+ case arg
+ when IPv4
+ return arg
+ when Regex
+ if (0..255) === (a = $1.to_i) &&
+ (0..255) === (b = $2.to_i) &&
+ (0..255) === (c = $3.to_i) &&
+ (0..255) === (d = $4.to_i)
+ return self.new([a, b, c, d].pack("CCCC"))
+ else
+ raise ArgumentError.new("IPv4 address with invalid value: " + arg)
+ end
+ else
+ raise ArgumentError.new("cannot interpret as IPv4 address: #{arg.inspect}")
+ end
+ end
+
+ def initialize(address) # :nodoc:
+ unless address.kind_of?(String)
+ raise ArgumentError, 'IPv4 address must be a string'
+ end
+ unless address.length == 4
+ raise ArgumentError, "IPv4 address expects 4 bytes but #{address.length} bytes"
+ end
+ @address = address
+ end
+
+ ##
+ # A String representation of this IPv4 address.
+
+ ##
+ # The raw IPv4 address as a String.
+
+ attr_reader :address
+
+ def to_s # :nodoc:
+ return sprintf("%d.%d.%d.%d", *@address.unpack("CCCC"))
+ end
+
+ def inspect # :nodoc:
+ return "#<#{self.class} #{self}>"
+ end
+
+ ##
+ # Turns this IPv4 address into a Gem::Resolv::DNS::Name.
+
+ def to_name
+ return DNS::Name.create(
+ '%d.%d.%d.%d.in-addr.arpa.' % @address.unpack('CCCC').reverse)
+ end
+
+ def ==(other) # :nodoc:
+ return @address == other.address
+ end
+
+ def eql?(other) # :nodoc:
+ return self == other
+ end
+
+ def hash # :nodoc:
+ return @address.hash
+ end
+ end
+
+ ##
+ # A Gem::Resolv::DNS IPv6 address.
+
+ class IPv6
+
+ ##
+ # IPv6 address format a:b:c:d:e:f:g:h
+ Regex_8Hex = /\A
+ (?:[0-9A-Fa-f]{1,4}:){7}
+ [0-9A-Fa-f]{1,4}
+ \z/x
+
+ ##
+ # Compressed IPv6 address format a::b
+
+ Regex_CompressedHex = /\A
+ ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::
+ ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)
+ \z/x
+
+ ##
+ # IPv4 mapped IPv6 address format a:b:c:d:e:f:w.x.y.z
+
+ Regex_6Hex4Dec = /\A
+ ((?:[0-9A-Fa-f]{1,4}:){6,6})
+ (\d+)\.(\d+)\.(\d+)\.(\d+)
+ \z/x
+
+ ##
+ # Compressed IPv4 mapped IPv6 address format a::b:w.x.y.z
+
+ Regex_CompressedHex4Dec = /\A
+ ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::
+ ((?:[0-9A-Fa-f]{1,4}:)*)
+ (\d+)\.(\d+)\.(\d+)\.(\d+)
+ \z/x
+
+ ##
+ # IPv6 link local address format fe80:b:c:d:e:f:g:h%em1
+ Regex_8HexLinkLocal = /\A
+ [Ff][Ee]80
+ (?::[0-9A-Fa-f]{1,4}){7}
+ %[-0-9A-Za-z._~]+
+ \z/x
+
+ ##
+ # Compressed IPv6 link local address format fe80::b%em1
+
+ Regex_CompressedHexLinkLocal = /\A
+ [Ff][Ee]80:
+ (?:
+ ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::
+ ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)
+ |
+ :((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)
+ )?
+ :[0-9A-Fa-f]{1,4}%[-0-9A-Za-z._~]+
+ \z/x
+
+ ##
+ # A composite IPv6 address Regexp.
+
+ Regex = /
+ (?:#{Regex_8Hex}) |
+ (?:#{Regex_CompressedHex}) |
+ (?:#{Regex_6Hex4Dec}) |
+ (?:#{Regex_CompressedHex4Dec}) |
+ (?:#{Regex_8HexLinkLocal}) |
+ (?:#{Regex_CompressedHexLinkLocal})
+ /x
+
+ ##
+ # Creates a new IPv6 address from +arg+ which may be:
+ #
+ # IPv6:: returns +arg+.
+ # String:: +arg+ must match one of the IPv6::Regex* constants
+
+ def self.create(arg)
+ case arg
+ when IPv6
+ return arg
+ when String
+ address = ''.b
+ if Regex_8Hex =~ arg
+ arg.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')}
+ elsif Regex_CompressedHex =~ arg
+ prefix = $1
+ suffix = $2
+ a1 = ''.b
+ a2 = ''.b
+ prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')}
+ suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')}
+ omitlen = 16 - a1.length - a2.length
+ address << a1 << "\0" * omitlen << a2
+ elsif Regex_6Hex4Dec =~ arg
+ prefix, a, b, c, d = $1, $2.to_i, $3.to_i, $4.to_i, $5.to_i
+ if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d
+ prefix.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')}
+ address << [a, b, c, d].pack('CCCC')
+ else
+ raise ArgumentError.new("not numeric IPv6 address: " + arg)
+ end
+ elsif Regex_CompressedHex4Dec =~ arg
+ prefix, suffix, a, b, c, d = $1, $2, $3.to_i, $4.to_i, $5.to_i, $6.to_i
+ if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d
+ a1 = ''.b
+ a2 = ''.b
+ prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')}
+ suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')}
+ omitlen = 12 - a1.length - a2.length
+ address << a1 << "\0" * omitlen << a2 << [a, b, c, d].pack('CCCC')
+ else
+ raise ArgumentError.new("not numeric IPv6 address: " + arg)
+ end
+ else
+ raise ArgumentError.new("not numeric IPv6 address: " + arg)
+ end
+ return IPv6.new(address)
+ else
+ raise ArgumentError.new("cannot interpret as IPv6 address: #{arg.inspect}")
+ end
+ end
+
+ def initialize(address) # :nodoc:
+ unless address.kind_of?(String) && address.length == 16
+ raise ArgumentError.new('IPv6 address must be 16 bytes')
+ end
+ @address = address
+ end
+
+ ##
+ # The raw IPv6 address as a String.
+
+ attr_reader :address
+
+ def to_s # :nodoc:
+ sprintf("%x:%x:%x:%x:%x:%x:%x:%x", *@address.unpack("nnnnnnnn")).sub(/(^|:)0(:0)+(:|$)/, '::')
+ end
+
+ def inspect # :nodoc:
+ return "#<#{self.class} #{self}>"
+ end
+
+ ##
+ # Turns this IPv6 address into a Gem::Resolv::DNS::Name.
+ #--
+ # ip6.arpa should be searched too. [RFC3152]
+
+ def to_name
+ return DNS::Name.new(
+ @address.unpack("H32")[0].split(//).reverse + ['ip6', 'arpa'])
+ end
+
+ def ==(other) # :nodoc:
+ return @address == other.address
+ end
+
+ def eql?(other) # :nodoc:
+ return self == other
+ end
+
+ def hash # :nodoc:
+ return @address.hash
+ end
+ end
+
+ ##
+ # Gem::Resolv::MDNS is a one-shot Multicast DNS (mDNS) resolver. It blindly
+ # makes queries to the mDNS addresses without understanding anything about
+ # multicast ports.
+ #
+ # Information taken form the following places:
+ #
+ # * RFC 6762
+
+ class MDNS < DNS
+
+ ##
+ # Default mDNS Port
+
+ Port = 5353
+
+ ##
+ # Default IPv4 mDNS address
+
+ AddressV4 = '224.0.0.251'
+
+ ##
+ # Default IPv6 mDNS address
+
+ AddressV6 = 'ff02::fb'
+
+ ##
+ # Default mDNS addresses
+
+ Addresses = [
+ [AddressV4, Port],
+ [AddressV6, Port],
+ ]
+
+ ##
+ # Creates a new one-shot Multicast DNS (mDNS) resolver.
+ #
+ # +config_info+ can be:
+ #
+ # nil::
+ # Uses the default mDNS addresses
+ #
+ # Hash::
+ # Must contain :nameserver or :nameserver_port like
+ # Gem::Resolv::DNS#initialize.
+
+ def initialize(config_info=nil)
+ if config_info then
+ super({ nameserver_port: Addresses }.merge(config_info))
+ else
+ super(nameserver_port: Addresses)
+ end
+ end
+
+ ##
+ # Iterates over all IP addresses for +name+ retrieved from the mDNS
+ # resolver, provided name ends with "local". If the name does not end in
+ # "local" no records will be returned.
+ #
+ # +name+ can be a Gem::Resolv::DNS::Name or a String. Retrieved addresses will
+ # be a Gem::Resolv::IPv4 or Gem::Resolv::IPv6
+
+ def each_address(name)
+ name = Gem::Resolv::DNS::Name.create(name)
+
+ return unless name[-1].to_s == 'local'
+
+ super(name)
+ end
+
+ def make_udp_requester # :nodoc:
+ nameserver_port = @config.nameserver_port
+ Requester::MDNSOneShot.new(*nameserver_port)
+ end
+
+ end
+
+ module LOC
+
+ ##
+ # A Gem::Resolv::LOC::Size
+
+ class Size
+
+ Regex = /^(\d+\.*\d*)[m]$/
+
+ ##
+ # Creates a new LOC::Size from +arg+ which may be:
+ #
+ # LOC::Size:: returns +arg+.
+ # String:: +arg+ must match the LOC::Size::Regex constant
+
+ def self.create(arg)
+ case arg
+ when Size
+ return arg
+ when String
+ scalar = ''
+ if Regex =~ arg
+ scalar = [(($1.to_f*(1e2)).to_i.to_s[0].to_i*(2**4)+(($1.to_f*(1e2)).to_i.to_s.length-1))].pack("C")
+ else
+ raise ArgumentError.new("not a properly formed Size string: " + arg)
+ end
+ return Size.new(scalar)
+ else
+ raise ArgumentError.new("cannot interpret as Size: #{arg.inspect}")
+ end
+ end
+
+ def initialize(scalar)
+ @scalar = scalar
+ end
+
+ ##
+ # The raw size
+
+ attr_reader :scalar
+
+ def to_s # :nodoc:
+ s = @scalar.unpack("H2").join.to_s
+ return ((s[0].to_i)*(10**(s[1].to_i-2))).to_s << "m"
+ end
+
+ def inspect # :nodoc:
+ return "#<#{self.class} #{self}>"
+ end
+
+ def ==(other) # :nodoc:
+ return @scalar == other.scalar
+ end
+
+ def eql?(other) # :nodoc:
+ return self == other
+ end
+
+ def hash # :nodoc:
+ return @scalar.hash
+ end
+
+ end
+
+ ##
+ # A Gem::Resolv::LOC::Coord
+
+ class Coord
+
+ Regex = /^(\d+)\s(\d+)\s(\d+\.\d+)\s([NESW])$/
+
+ ##
+ # Creates a new LOC::Coord from +arg+ which may be:
+ #
+ # LOC::Coord:: returns +arg+.
+ # String:: +arg+ must match the LOC::Coord::Regex constant
+
+ def self.create(arg)
+ case arg
+ when Coord
+ return arg
+ when String
+ coordinates = ''
+ if Regex =~ arg && $1.to_f < 180
+ m = $~
+ hemi = (m[4][/[NE]/]) || (m[4][/[SW]/]) ? 1 : -1
+ coordinates = [ ((m[1].to_i*(36e5)) + (m[2].to_i*(6e4)) +
+ (m[3].to_f*(1e3))) * hemi+(2**31) ].pack("N")
+ orientation = m[4][/[NS]/] ? 'lat' : 'lon'
+ else
+ raise ArgumentError.new("not a properly formed Coord string: " + arg)
+ end
+ return Coord.new(coordinates,orientation)
+ else
+ raise ArgumentError.new("cannot interpret as Coord: #{arg.inspect}")
+ end
+ end
+
+ def initialize(coordinates,orientation)
+ unless coordinates.kind_of?(String)
+ raise ArgumentError.new("Coord must be a 32bit unsigned integer in hex format: #{coordinates.inspect}")
+ end
+ unless orientation.kind_of?(String) && orientation[/^lon$|^lat$/]
+ raise ArgumentError.new('Coord expects orientation to be a String argument of "lat" or "lon"')
+ end
+ @coordinates = coordinates
+ @orientation = orientation
+ end
+
+ ##
+ # The raw coordinates
+
+ attr_reader :coordinates
+
+ ## The orientation of the hemisphere as 'lat' or 'lon'
+
+ attr_reader :orientation
+
+ def to_s # :nodoc:
+ c = @coordinates.unpack("N").join.to_i
+ val = (c - (2**31)).abs
+ fracsecs = (val % 1e3).to_i.to_s
+ val = val / 1e3
+ secs = (val % 60).to_i.to_s
+ val = val / 60
+ mins = (val % 60).to_i.to_s
+ degs = (val / 60).to_i.to_s
+ posi = (c >= 2**31)
+ case posi
+ when true
+ hemi = @orientation[/^lat$/] ? "N" : "E"
+ else
+ hemi = @orientation[/^lon$/] ? "W" : "S"
+ end
+ return degs << " " << mins << " " << secs << "." << fracsecs << " " << hemi
+ end
+
+ def inspect # :nodoc:
+ return "#<#{self.class} #{self}>"
+ end
+
+ def ==(other) # :nodoc:
+ return @coordinates == other.coordinates
+ end
+
+ def eql?(other) # :nodoc:
+ return self == other
+ end
+
+ def hash # :nodoc:
+ return @coordinates.hash
+ end
+
+ end
+
+ ##
+ # A Gem::Resolv::LOC::Alt
+
+ class Alt
+
+ Regex = /^([+-]*\d+\.*\d*)[m]$/
+
+ ##
+ # Creates a new LOC::Alt from +arg+ which may be:
+ #
+ # LOC::Alt:: returns +arg+.
+ # String:: +arg+ must match the LOC::Alt::Regex constant
+
+ def self.create(arg)
+ case arg
+ when Alt
+ return arg
+ when String
+ altitude = ''
+ if Regex =~ arg
+ altitude = [($1.to_f*(1e2))+(1e7)].pack("N")
+ else
+ raise ArgumentError.new("not a properly formed Alt string: " + arg)
+ end
+ return Alt.new(altitude)
+ else
+ raise ArgumentError.new("cannot interpret as Alt: #{arg.inspect}")
+ end
+ end
+
+ def initialize(altitude)
+ @altitude = altitude
+ end
+
+ ##
+ # The raw altitude
+
+ attr_reader :altitude
+
+ def to_s # :nodoc:
+ a = @altitude.unpack("N").join.to_i
+ return ((a.to_f/1e2)-1e5).to_s + "m"
+ end
+
+ def inspect # :nodoc:
+ return "#<#{self.class} #{self}>"
+ end
+
+ def ==(other) # :nodoc:
+ return @altitude == other.altitude
+ end
+
+ def eql?(other) # :nodoc:
+ return self == other
+ end
+
+ def hash # :nodoc:
+ return @altitude.hash
+ end
+
+ end
+
+ end
+
+ ##
+ # Default resolver to use for Gem::Resolv class methods.
+
+ DefaultResolver = self.new
+
+ ##
+ # Replaces the resolvers in the default resolver with +new_resolvers+. This
+ # allows resolvers to be changed for resolv-replace.
+
+ def DefaultResolver.replace_resolvers new_resolvers
+ @resolvers = new_resolvers
+ end
+
+ ##
+ # Address Regexp to use for matching IP addresses.
+
+ AddressRegex = /(?:#{IPv4::Regex})|(?:#{IPv6::Regex})/
+
+end
+
diff --git a/lib/rubygems/vendor/timeout/.document b/lib/rubygems/vendor/timeout/.document
new file mode 100644
index 0000000000..0c43bbd6b3
--- /dev/null
+++ b/lib/rubygems/vendor/timeout/.document
@@ -0,0 +1 @@
+# Vendored files do not need to be documented
diff --git a/lib/rubygems/vendor/timeout/lib/timeout.rb b/lib/rubygems/vendor/timeout/lib/timeout.rb
new file mode 100644
index 0000000000..df97d64ca0
--- /dev/null
+++ b/lib/rubygems/vendor/timeout/lib/timeout.rb
@@ -0,0 +1,199 @@
+# frozen_string_literal: true
+# Timeout long-running blocks
+#
+# == Synopsis
+#
+# require 'rubygems/vendor/timeout/lib/timeout'
+# status = Gem::Timeout::timeout(5) {
+# # Something that should be interrupted if it takes more than 5 seconds...
+# }
+#
+# == Description
+#
+# Gem::Timeout provides a way to auto-terminate a potentially long-running
+# operation if it hasn't finished in a fixed amount of time.
+#
+# Previous versions didn't use a module for namespacing, however
+# #timeout is provided for backwards compatibility. You
+# should prefer Gem::Timeout.timeout instead.
+#
+# == Copyright
+#
+# Copyright:: (C) 2000 Network Applied Communication Laboratory, Inc.
+# Copyright:: (C) 2000 Information-technology Promotion Agency, Japan
+
+module Gem::Timeout
+ VERSION = "0.4.1"
+
+ # Internal error raised to when a timeout is triggered.
+ class ExitException < Exception
+ def exception(*)
+ self
+ end
+ end
+
+ # Raised by Gem::Timeout.timeout when the block times out.
+ class Error < RuntimeError
+ def self.handle_timeout(message)
+ exc = ExitException.new(message)
+
+ begin
+ yield exc
+ rescue ExitException => e
+ raise new(message) if exc.equal?(e)
+ raise
+ end
+ end
+ end
+
+ # :stopdoc:
+ CONDVAR = ConditionVariable.new
+ QUEUE = Queue.new
+ QUEUE_MUTEX = Mutex.new
+ TIMEOUT_THREAD_MUTEX = Mutex.new
+ @timeout_thread = nil
+ private_constant :CONDVAR, :QUEUE, :QUEUE_MUTEX, :TIMEOUT_THREAD_MUTEX
+
+ class Request
+ attr_reader :deadline
+
+ def initialize(thread, timeout, exception_class, message)
+ @thread = thread
+ @deadline = GET_TIME.call(Process::CLOCK_MONOTONIC) + timeout
+ @exception_class = exception_class
+ @message = message
+
+ @mutex = Mutex.new
+ @done = false # protected by @mutex
+ end
+
+ def done?
+ @mutex.synchronize do
+ @done
+ end
+ end
+
+ def expired?(now)
+ now >= @deadline
+ end
+
+ def interrupt
+ @mutex.synchronize do
+ unless @done
+ @thread.raise @exception_class, @message
+ @done = true
+ end
+ end
+ end
+
+ def finished
+ @mutex.synchronize do
+ @done = true
+ end
+ end
+ end
+ private_constant :Request
+
+ def self.create_timeout_thread
+ watcher = Thread.new do
+ requests = []
+ while true
+ until QUEUE.empty? and !requests.empty? # wait to have at least one request
+ req = QUEUE.pop
+ requests << req unless req.done?
+ end
+ closest_deadline = requests.min_by(&:deadline).deadline
+
+ now = 0.0
+ QUEUE_MUTEX.synchronize do
+ while (now = GET_TIME.call(Process::CLOCK_MONOTONIC)) < closest_deadline and QUEUE.empty?
+ CONDVAR.wait(QUEUE_MUTEX, closest_deadline - now)
+ end
+ end
+
+ requests.each do |req|
+ req.interrupt if req.expired?(now)
+ end
+ requests.reject!(&:done?)
+ end
+ end
+ ThreadGroup::Default.add(watcher) unless watcher.group.enclosed?
+ watcher.name = "Gem::Timeout stdlib thread"
+ watcher.thread_variable_set(:"\0__detached_thread__", true)
+ watcher
+ end
+ private_class_method :create_timeout_thread
+
+ def self.ensure_timeout_thread_created
+ unless @timeout_thread and @timeout_thread.alive?
+ TIMEOUT_THREAD_MUTEX.synchronize do
+ unless @timeout_thread and @timeout_thread.alive?
+ @timeout_thread = create_timeout_thread
+ end
+ end
+ end
+ end
+
+ # We keep a private reference so that time mocking libraries won't break
+ # Gem::Timeout.
+ GET_TIME = Process.method(:clock_gettime)
+ private_constant :GET_TIME
+
+ # :startdoc:
+
+ # Perform an operation in a block, raising an error if it takes longer than
+ # +sec+ seconds to complete.
+ #
+ # +sec+:: Number of seconds to wait for the block to terminate. Any number
+ # may be used, including Floats to specify fractional seconds. A
+ # value of 0 or +nil+ will execute the block without any timeout.
+ # +klass+:: Exception Class to raise if the block fails to terminate
+ # in +sec+ seconds. Omitting will use the default, Gem::Timeout::Error
+ # +message+:: Error message to raise with Exception Class.
+ # Omitting will use the default, "execution expired"
+ #
+ # Returns the result of the block *if* the block completed before
+ # +sec+ seconds, otherwise throws an exception, based on the value of +klass+.
+ #
+ # The exception thrown to terminate the given block cannot be rescued inside
+ # the block unless +klass+ is given explicitly. However, the block can use
+ # ensure to prevent the handling of the exception. For that reason, this
+ # method cannot be relied on to enforce timeouts for untrusted blocks.
+ #
+ # If a scheduler is defined, it will be used to handle the timeout by invoking
+ # Scheduler#timeout_after.
+ #
+ # Note that this is both a method of module Gem::Timeout, so you can <tt>include
+ # Gem::Timeout</tt> into your classes so they have a #timeout method, as well as
+ # a module method, so you can call it directly as Gem::Timeout.timeout().
+ def timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+
+ return yield(sec) if sec == nil or sec.zero?
+
+ message ||= "execution expired"
+
+ if Fiber.respond_to?(:current_scheduler) && (scheduler = Fiber.current_scheduler)&.respond_to?(:timeout_after)
+ return scheduler.timeout_after(sec, klass || Error, message, &block)
+ end
+
+ Gem::Timeout.ensure_timeout_thread_created
+ perform = Proc.new do |exc|
+ request = Request.new(Thread.current, sec, exc, message)
+ QUEUE_MUTEX.synchronize do
+ QUEUE << request
+ CONDVAR.signal
+ end
+ begin
+ return yield(sec)
+ ensure
+ request.finished
+ end
+ end
+
+ if klass
+ perform.call(klass)
+ else
+ Error.handle_timeout(message, &perform)
+ end
+ end
+ module_function :timeout
+end
diff --git a/lib/rubygems/vendor/tsort/.document b/lib/rubygems/vendor/tsort/.document
new file mode 100644
index 0000000000..0c43bbd6b3
--- /dev/null
+++ b/lib/rubygems/vendor/tsort/.document
@@ -0,0 +1 @@
+# Vendored files do not need to be documented
diff --git a/lib/rubygems/vendor/tsort/lib/tsort.rb b/lib/rubygems/vendor/tsort/lib/tsort.rb
new file mode 100644
index 0000000000..9dd7c09521
--- /dev/null
+++ b/lib/rubygems/vendor/tsort/lib/tsort.rb
@@ -0,0 +1,455 @@
+# frozen_string_literal: true
+
+#--
+# tsort.rb - provides a module for topological sorting and strongly connected components.
+#++
+#
+
+#
+# Gem::TSort implements topological sorting using Tarjan's algorithm for
+# strongly connected components.
+#
+# Gem::TSort is designed to be able to be used with any object which can be
+# interpreted as a directed graph.
+#
+# Gem::TSort requires two methods to interpret an object as a graph,
+# tsort_each_node and tsort_each_child.
+#
+# * tsort_each_node is used to iterate for all nodes over a graph.
+# * tsort_each_child is used to iterate for child nodes of a given node.
+#
+# The equality of nodes are defined by eql? and hash since
+# Gem::TSort uses Hash internally.
+#
+# == A Simple Example
+#
+# The following example demonstrates how to mix the Gem::TSort module into an
+# existing class (in this case, Hash). Here, we're treating each key in
+# the hash as a node in the graph, and so we simply alias the required
+# #tsort_each_node method to Hash's #each_key method. For each key in the
+# hash, the associated value is an array of the node's child nodes. This
+# choice in turn leads to our implementation of the required #tsort_each_child
+# method, which fetches the array of child nodes and then iterates over that
+# array using the user-supplied block.
+#
+# require 'rubygems/vendor/tsort/lib/tsort'
+#
+# class Hash
+# include Gem::TSort
+# alias tsort_each_node each_key
+# def tsort_each_child(node, &block)
+# fetch(node).each(&block)
+# end
+# end
+#
+# {1=>[2, 3], 2=>[3], 3=>[], 4=>[]}.tsort
+# #=> [3, 2, 1, 4]
+#
+# {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}.strongly_connected_components
+# #=> [[4], [2, 3], [1]]
+#
+# == A More Realistic Example
+#
+# A very simple `make' like tool can be implemented as follows:
+#
+# require 'rubygems/vendor/tsort/lib/tsort'
+#
+# class Make
+# def initialize
+# @dep = {}
+# @dep.default = []
+# end
+#
+# def rule(outputs, inputs=[], &block)
+# triple = [outputs, inputs, block]
+# outputs.each {|f| @dep[f] = [triple]}
+# @dep[triple] = inputs
+# end
+#
+# def build(target)
+# each_strongly_connected_component_from(target) {|ns|
+# if ns.length != 1
+# fs = ns.delete_if {|n| Array === n}
+# raise Gem::TSort::Cyclic.new("cyclic dependencies: #{fs.join ', '}")
+# end
+# n = ns.first
+# if Array === n
+# outputs, inputs, block = n
+# inputs_time = inputs.map {|f| File.mtime f}.max
+# begin
+# outputs_time = outputs.map {|f| File.mtime f}.min
+# rescue Errno::ENOENT
+# outputs_time = nil
+# end
+# if outputs_time == nil ||
+# inputs_time != nil && outputs_time <= inputs_time
+# sleep 1 if inputs_time != nil && inputs_time.to_i == Time.now.to_i
+# block.call
+# end
+# end
+# }
+# end
+#
+# def tsort_each_child(node, &block)
+# @dep[node].each(&block)
+# end
+# include Gem::TSort
+# end
+#
+# def command(arg)
+# print arg, "\n"
+# system arg
+# end
+#
+# m = Make.new
+# m.rule(%w[t1]) { command 'date > t1' }
+# m.rule(%w[t2]) { command 'date > t2' }
+# m.rule(%w[t3]) { command 'date > t3' }
+# m.rule(%w[t4], %w[t1 t3]) { command 'cat t1 t3 > t4' }
+# m.rule(%w[t5], %w[t4 t2]) { command 'cat t4 t2 > t5' }
+# m.build('t5')
+#
+# == Bugs
+#
+# * 'tsort.rb' is wrong name because this library uses
+# Tarjan's algorithm for strongly connected components.
+# Although 'strongly_connected_components.rb' is correct but too long.
+#
+# == References
+#
+# R. E. Tarjan, "Depth First Search and Linear Graph Algorithms",
+# <em>SIAM Journal on Computing</em>, Vol. 1, No. 2, pp. 146-160, June 1972.
+#
+
+module Gem::TSort
+
+ VERSION = "0.2.0"
+
+ class Cyclic < StandardError
+ end
+
+ # Returns a topologically sorted array of nodes.
+ # The array is sorted from children to parents, i.e.
+ # the first element has no child and the last node has no parent.
+ #
+ # If there is a cycle, Gem::TSort::Cyclic is raised.
+ #
+ # class G
+ # include Gem::TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # p graph.tsort #=> [4, 2, 3, 1]
+ #
+ # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
+ # p graph.tsort # raises Gem::TSort::Cyclic
+ #
+ def tsort
+ each_node = method(:tsort_each_node)
+ each_child = method(:tsort_each_child)
+ Gem::TSort.tsort(each_node, each_child)
+ end
+
+ # Returns a topologically sorted array of nodes.
+ # The array is sorted from children to parents, i.e.
+ # the first element has no child and the last node has no parent.
+ #
+ # The graph is represented by _each_node_ and _each_child_.
+ # _each_node_ should have +call+ method which yields for each node in the graph.
+ # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
+ #
+ # If there is a cycle, Gem::TSort::Cyclic is raised.
+ #
+ # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # p Gem::TSort.tsort(each_node, each_child) #=> [4, 2, 3, 1]
+ #
+ # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # p Gem::TSort.tsort(each_node, each_child) # raises Gem::TSort::Cyclic
+ #
+ def self.tsort(each_node, each_child)
+ tsort_each(each_node, each_child).to_a
+ end
+
+ # The iterator version of the #tsort method.
+ # <tt><em>obj</em>.tsort_each</tt> is similar to <tt><em>obj</em>.tsort.each</tt>, but
+ # modification of _obj_ during the iteration may lead to unexpected results.
+ #
+ # #tsort_each returns +nil+.
+ # If there is a cycle, Gem::TSort::Cyclic is raised.
+ #
+ # class G
+ # include Gem::TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # graph.tsort_each {|n| p n }
+ # #=> 4
+ # # 2
+ # # 3
+ # # 1
+ #
+ def tsort_each(&block) # :yields: node
+ each_node = method(:tsort_each_node)
+ each_child = method(:tsort_each_child)
+ Gem::TSort.tsort_each(each_node, each_child, &block)
+ end
+
+ # The iterator version of the Gem::TSort.tsort method.
+ #
+ # The graph is represented by _each_node_ and _each_child_.
+ # _each_node_ should have +call+ method which yields for each node in the graph.
+ # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
+ #
+ # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # Gem::TSort.tsort_each(each_node, each_child) {|n| p n }
+ # #=> 4
+ # # 2
+ # # 3
+ # # 1
+ #
+ def self.tsort_each(each_node, each_child) # :yields: node
+ return to_enum(__method__, each_node, each_child) unless block_given?
+
+ each_strongly_connected_component(each_node, each_child) {|component|
+ if component.size == 1
+ yield component.first
+ else
+ raise Cyclic.new("topological sort failed: #{component.inspect}")
+ end
+ }
+ end
+
+ # Returns strongly connected components as an array of arrays of nodes.
+ # The array is sorted from children to parents.
+ # Each elements of the array represents a strongly connected component.
+ #
+ # class G
+ # include Gem::TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # p graph.strongly_connected_components #=> [[4], [2], [3], [1]]
+ #
+ # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
+ # p graph.strongly_connected_components #=> [[4], [2, 3], [1]]
+ #
+ def strongly_connected_components
+ each_node = method(:tsort_each_node)
+ each_child = method(:tsort_each_child)
+ Gem::TSort.strongly_connected_components(each_node, each_child)
+ end
+
+ # Returns strongly connected components as an array of arrays of nodes.
+ # The array is sorted from children to parents.
+ # Each elements of the array represents a strongly connected component.
+ #
+ # The graph is represented by _each_node_ and _each_child_.
+ # _each_node_ should have +call+ method which yields for each node in the graph.
+ # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
+ #
+ # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # p Gem::TSort.strongly_connected_components(each_node, each_child)
+ # #=> [[4], [2], [3], [1]]
+ #
+ # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # p Gem::TSort.strongly_connected_components(each_node, each_child)
+ # #=> [[4], [2, 3], [1]]
+ #
+ def self.strongly_connected_components(each_node, each_child)
+ each_strongly_connected_component(each_node, each_child).to_a
+ end
+
+ # The iterator version of the #strongly_connected_components method.
+ # <tt><em>obj</em>.each_strongly_connected_component</tt> is similar to
+ # <tt><em>obj</em>.strongly_connected_components.each</tt>, but
+ # modification of _obj_ during the iteration may lead to unexpected results.
+ #
+ # #each_strongly_connected_component returns +nil+.
+ #
+ # class G
+ # include Gem::TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # graph.each_strongly_connected_component {|scc| p scc }
+ # #=> [4]
+ # # [2]
+ # # [3]
+ # # [1]
+ #
+ # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
+ # graph.each_strongly_connected_component {|scc| p scc }
+ # #=> [4]
+ # # [2, 3]
+ # # [1]
+ #
+ def each_strongly_connected_component(&block) # :yields: nodes
+ each_node = method(:tsort_each_node)
+ each_child = method(:tsort_each_child)
+ Gem::TSort.each_strongly_connected_component(each_node, each_child, &block)
+ end
+
+ # The iterator version of the Gem::TSort.strongly_connected_components method.
+ #
+ # The graph is represented by _each_node_ and _each_child_.
+ # _each_node_ should have +call+ method which yields for each node in the graph.
+ # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
+ #
+ # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # Gem::TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc }
+ # #=> [4]
+ # # [2]
+ # # [3]
+ # # [1]
+ #
+ # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # Gem::TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc }
+ # #=> [4]
+ # # [2, 3]
+ # # [1]
+ #
+ def self.each_strongly_connected_component(each_node, each_child) # :yields: nodes
+ return to_enum(__method__, each_node, each_child) unless block_given?
+
+ id_map = {}
+ stack = []
+ each_node.call {|node|
+ unless id_map.include? node
+ each_strongly_connected_component_from(node, each_child, id_map, stack) {|c|
+ yield c
+ }
+ end
+ }
+ nil
+ end
+
+ # Iterates over strongly connected component in the subgraph reachable from
+ # _node_.
+ #
+ # Return value is unspecified.
+ #
+ # #each_strongly_connected_component_from doesn't call #tsort_each_node.
+ #
+ # class G
+ # include Gem::TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # graph.each_strongly_connected_component_from(2) {|scc| p scc }
+ # #=> [4]
+ # # [2]
+ #
+ # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
+ # graph.each_strongly_connected_component_from(2) {|scc| p scc }
+ # #=> [4]
+ # # [2, 3]
+ #
+ def each_strongly_connected_component_from(node, id_map={}, stack=[], &block) # :yields: nodes
+ Gem::TSort.each_strongly_connected_component_from(node, method(:tsort_each_child), id_map, stack, &block)
+ end
+
+ # Iterates over strongly connected components in a graph.
+ # The graph is represented by _node_ and _each_child_.
+ #
+ # _node_ is the first node.
+ # _each_child_ should have +call+ method which takes a node argument
+ # and yields for each child node.
+ #
+ # Return value is unspecified.
+ #
+ # #Gem::TSort.each_strongly_connected_component_from is a class method and
+ # it doesn't need a class to represent a graph which includes Gem::TSort.
+ #
+ # graph = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+ # each_child = lambda {|n, &b| graph[n].each(&b) }
+ # Gem::TSort.each_strongly_connected_component_from(1, each_child) {|scc|
+ # p scc
+ # }
+ # #=> [4]
+ # # [2, 3]
+ # # [1]
+ #
+ def self.each_strongly_connected_component_from(node, each_child, id_map={}, stack=[]) # :yields: nodes
+ return to_enum(__method__, node, each_child, id_map, stack) unless block_given?
+
+ minimum_id = node_id = id_map[node] = id_map.size
+ stack_length = stack.length
+ stack << node
+
+ each_child.call(node) {|child|
+ if id_map.include? child
+ child_id = id_map[child]
+ minimum_id = child_id if child_id && child_id < minimum_id
+ else
+ sub_minimum_id =
+ each_strongly_connected_component_from(child, each_child, id_map, stack) {|c|
+ yield c
+ }
+ minimum_id = sub_minimum_id if sub_minimum_id < minimum_id
+ end
+ }
+
+ if node_id == minimum_id
+ component = stack.slice!(stack_length .. -1)
+ component.each {|n| id_map[n] = nil}
+ yield component
+ end
+
+ minimum_id
+ end
+
+ # Should be implemented by a extended class.
+ #
+ # #tsort_each_node is used to iterate for all nodes over a graph.
+ #
+ def tsort_each_node # :yields: node
+ raise NotImplementedError.new
+ end
+
+ # Should be implemented by a extended class.
+ #
+ # #tsort_each_child is used to iterate for child nodes of _node_.
+ #
+ def tsort_each_child(node) # :yields: child
+ raise NotImplementedError.new
+ end
+end
diff --git a/lib/rubygems/vendor/uri/.document b/lib/rubygems/vendor/uri/.document
new file mode 100644
index 0000000000..0c43bbd6b3
--- /dev/null
+++ b/lib/rubygems/vendor/uri/.document
@@ -0,0 +1 @@
+# Vendored files do not need to be documented
diff --git a/lib/rubygems/vendor/uri/lib/uri.rb b/lib/rubygems/vendor/uri/lib/uri.rb
new file mode 100644
index 0000000000..f1ccc167cc
--- /dev/null
+++ b/lib/rubygems/vendor/uri/lib/uri.rb
@@ -0,0 +1,104 @@
+# frozen_string_literal: false
+# Gem::URI is a module providing classes to handle Uniform Resource Identifiers
+# (RFC2396[http://tools.ietf.org/html/rfc2396]).
+#
+# == Features
+#
+# * Uniform way of handling URIs.
+# * Flexibility to introduce custom Gem::URI schemes.
+# * Flexibility to have an alternate Gem::URI::Parser (or just different patterns
+# and regexp's).
+#
+# == Basic example
+#
+# require 'rubygems/vendor/uri/lib/uri'
+#
+# uri = Gem::URI("http://foo.com/posts?id=30&limit=5#time=1305298413")
+# #=> #<Gem::URI::HTTP http://foo.com/posts?id=30&limit=5#time=1305298413>
+#
+# uri.scheme #=> "http"
+# uri.host #=> "foo.com"
+# uri.path #=> "/posts"
+# uri.query #=> "id=30&limit=5"
+# uri.fragment #=> "time=1305298413"
+#
+# uri.to_s #=> "http://foo.com/posts?id=30&limit=5#time=1305298413"
+#
+# == Adding custom URIs
+#
+# module Gem::URI
+# class RSYNC < Generic
+# DEFAULT_PORT = 873
+# end
+# register_scheme 'RSYNC', RSYNC
+# end
+# #=> Gem::URI::RSYNC
+#
+# Gem::URI.scheme_list
+# #=> {"FILE"=>Gem::URI::File, "FTP"=>Gem::URI::FTP, "HTTP"=>Gem::URI::HTTP,
+# # "HTTPS"=>Gem::URI::HTTPS, "LDAP"=>Gem::URI::LDAP, "LDAPS"=>Gem::URI::LDAPS,
+# # "MAILTO"=>Gem::URI::MailTo, "RSYNC"=>Gem::URI::RSYNC}
+#
+# uri = Gem::URI("rsync://rsync.foo.com")
+# #=> #<Gem::URI::RSYNC rsync://rsync.foo.com>
+#
+# == RFC References
+#
+# A good place to view an RFC spec is http://www.ietf.org/rfc.html.
+#
+# Here is a list of all related RFC's:
+# - RFC822[http://tools.ietf.org/html/rfc822]
+# - RFC1738[http://tools.ietf.org/html/rfc1738]
+# - RFC2255[http://tools.ietf.org/html/rfc2255]
+# - RFC2368[http://tools.ietf.org/html/rfc2368]
+# - RFC2373[http://tools.ietf.org/html/rfc2373]
+# - RFC2396[http://tools.ietf.org/html/rfc2396]
+# - RFC2732[http://tools.ietf.org/html/rfc2732]
+# - RFC3986[http://tools.ietf.org/html/rfc3986]
+#
+# == Class tree
+#
+# - Gem::URI::Generic (in uri/generic.rb)
+# - Gem::URI::File - (in uri/file.rb)
+# - Gem::URI::FTP - (in uri/ftp.rb)
+# - Gem::URI::HTTP - (in uri/http.rb)
+# - Gem::URI::HTTPS - (in uri/https.rb)
+# - Gem::URI::LDAP - (in uri/ldap.rb)
+# - Gem::URI::LDAPS - (in uri/ldaps.rb)
+# - Gem::URI::MailTo - (in uri/mailto.rb)
+# - Gem::URI::Parser - (in uri/common.rb)
+# - Gem::URI::REGEXP - (in uri/common.rb)
+# - Gem::URI::REGEXP::PATTERN - (in uri/common.rb)
+# - Gem::URI::Util - (in uri/common.rb)
+# - Gem::URI::Error - (in uri/common.rb)
+# - Gem::URI::InvalidURIError - (in uri/common.rb)
+# - Gem::URI::InvalidComponentError - (in uri/common.rb)
+# - Gem::URI::BadURIError - (in uri/common.rb)
+#
+# == Copyright Info
+#
+# Author:: Akira Yamada <akira@ruby-lang.org>
+# Documentation::
+# Akira Yamada <akira@ruby-lang.org>
+# Dmitry V. Sabanin <sdmitry@lrn.ru>
+# Vincent Batts <vbatts@hashbangbash.com>
+# License::
+# Copyright (c) 2001 akira yamada <akira@ruby-lang.org>
+# You can redistribute it and/or modify it under the same term as Ruby.
+#
+
+module Gem::URI
+end
+
+require_relative 'uri/version'
+require_relative 'uri/common'
+require_relative 'uri/generic'
+require_relative 'uri/file'
+require_relative 'uri/ftp'
+require_relative 'uri/http'
+require_relative 'uri/https'
+require_relative 'uri/ldap'
+require_relative 'uri/ldaps'
+require_relative 'uri/mailto'
+require_relative 'uri/ws'
+require_relative 'uri/wss'
diff --git a/lib/rubygems/vendor/uri/lib/uri/common.rb b/lib/rubygems/vendor/uri/lib/uri/common.rb
new file mode 100644
index 0000000000..921fb9dd28
--- /dev/null
+++ b/lib/rubygems/vendor/uri/lib/uri/common.rb
@@ -0,0 +1,853 @@
+# frozen_string_literal: true
+#--
+# = uri/common.rb
+#
+# Author:: Akira Yamada <akira@ruby-lang.org>
+# License::
+# You can redistribute it and/or modify it under the same term as Ruby.
+#
+# See Gem::URI for general documentation
+#
+
+require_relative "rfc2396_parser"
+require_relative "rfc3986_parser"
+
+module Gem::URI
+ include RFC2396_REGEXP
+
+ REGEXP = RFC2396_REGEXP
+ Parser = RFC2396_Parser
+ RFC3986_PARSER = RFC3986_Parser.new
+ Ractor.make_shareable(RFC3986_PARSER) if defined?(Ractor)
+
+ # Gem::URI::Parser.new
+ DEFAULT_PARSER = Parser.new
+ DEFAULT_PARSER.pattern.each_pair do |sym, str|
+ unless REGEXP::PATTERN.const_defined?(sym)
+ REGEXP::PATTERN.const_set(sym, str)
+ end
+ end
+ DEFAULT_PARSER.regexp.each_pair do |sym, str|
+ const_set(sym, str)
+ end
+ Ractor.make_shareable(DEFAULT_PARSER) if defined?(Ractor)
+
+ module Util # :nodoc:
+ def make_components_hash(klass, array_hash)
+ tmp = {}
+ if array_hash.kind_of?(Array) &&
+ array_hash.size == klass.component.size - 1
+ klass.component[1..-1].each_index do |i|
+ begin
+ tmp[klass.component[i + 1]] = array_hash[i].clone
+ rescue TypeError
+ tmp[klass.component[i + 1]] = array_hash[i]
+ end
+ end
+
+ elsif array_hash.kind_of?(Hash)
+ array_hash.each do |key, value|
+ begin
+ tmp[key] = value.clone
+ rescue TypeError
+ tmp[key] = value
+ end
+ end
+ else
+ raise ArgumentError,
+ "expected Array of or Hash of components of #{klass} (#{klass.component[1..-1].join(', ')})"
+ end
+ tmp[:scheme] = klass.to_s.sub(/\A.*::/, '').downcase
+
+ return tmp
+ end
+ module_function :make_components_hash
+ end
+
+ module Schemes
+ end
+ private_constant :Schemes
+
+ # Registers the given +klass+ as the class to be instantiated
+ # when parsing a \Gem::URI with the given +scheme+:
+ #
+ # Gem::URI.register_scheme('MS_SEARCH', Gem::URI::Generic) # => Gem::URI::Generic
+ # Gem::URI.scheme_list['MS_SEARCH'] # => Gem::URI::Generic
+ #
+ # Note that after calling String#upcase on +scheme+, it must be a valid
+ # constant name.
+ def self.register_scheme(scheme, klass)
+ Schemes.const_set(scheme.to_s.upcase, klass)
+ end
+
+ # Returns a hash of the defined schemes:
+ #
+ # Gem::URI.scheme_list
+ # # =>
+ # {"MAILTO"=>Gem::URI::MailTo,
+ # "LDAPS"=>Gem::URI::LDAPS,
+ # "WS"=>Gem::URI::WS,
+ # "HTTP"=>Gem::URI::HTTP,
+ # "HTTPS"=>Gem::URI::HTTPS,
+ # "LDAP"=>Gem::URI::LDAP,
+ # "FILE"=>Gem::URI::File,
+ # "FTP"=>Gem::URI::FTP}
+ #
+ # Related: Gem::URI.register_scheme.
+ def self.scheme_list
+ Schemes.constants.map { |name|
+ [name.to_s.upcase, Schemes.const_get(name)]
+ }.to_h
+ end
+
+ INITIAL_SCHEMES = scheme_list
+ private_constant :INITIAL_SCHEMES
+ Ractor.make_shareable(INITIAL_SCHEMES) if defined?(Ractor)
+
+ # Returns a new object constructed from the given +scheme+, +arguments+,
+ # and +default+:
+ #
+ # - The new object is an instance of <tt>Gem::URI.scheme_list[scheme.upcase]</tt>.
+ # - The object is initialized by calling the class initializer
+ # using +scheme+ and +arguments+.
+ # See Gem::URI::Generic.new.
+ #
+ # Examples:
+ #
+ # values = ['john.doe', 'www.example.com', '123', nil, '/forum/questions/', nil, 'tag=networking&order=newest', 'top']
+ # Gem::URI.for('https', *values)
+ # # => #<Gem::URI::HTTPS https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top>
+ # Gem::URI.for('foo', *values, default: Gem::URI::HTTP)
+ # # => #<Gem::URI::HTTP foo://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top>
+ #
+ def self.for(scheme, *arguments, default: Generic)
+ const_name = scheme.to_s.upcase
+
+ uri_class = INITIAL_SCHEMES[const_name]
+ uri_class ||= if /\A[A-Z]\w*\z/.match?(const_name) && Schemes.const_defined?(const_name, false)
+ Schemes.const_get(const_name, false)
+ end
+ uri_class ||= default
+
+ return uri_class.new(scheme, *arguments)
+ end
+
+ #
+ # Base class for all Gem::URI exceptions.
+ #
+ class Error < StandardError; end
+ #
+ # Not a Gem::URI.
+ #
+ class InvalidURIError < Error; end
+ #
+ # Not a Gem::URI component.
+ #
+ class InvalidComponentError < Error; end
+ #
+ # Gem::URI is valid, bad usage is not.
+ #
+ class BadURIError < Error; end
+
+ # Returns a 9-element array representing the parts of the \Gem::URI
+ # formed from the string +uri+;
+ # each array element is a string or +nil+:
+ #
+ # names = %w[scheme userinfo host port registry path opaque query fragment]
+ # values = Gem::URI.split('https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top')
+ # names.zip(values)
+ # # =>
+ # [["scheme", "https"],
+ # ["userinfo", "john.doe"],
+ # ["host", "www.example.com"],
+ # ["port", "123"],
+ # ["registry", nil],
+ # ["path", "/forum/questions/"],
+ # ["opaque", nil],
+ # ["query", "tag=networking&order=newest"],
+ # ["fragment", "top"]]
+ #
+ def self.split(uri)
+ RFC3986_PARSER.split(uri)
+ end
+
+ # Returns a new \Gem::URI object constructed from the given string +uri+:
+ #
+ # Gem::URI.parse('https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top')
+ # # => #<Gem::URI::HTTPS https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top>
+ # Gem::URI.parse('http://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top')
+ # # => #<Gem::URI::HTTP http://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top>
+ #
+ # It's recommended to first ::escape string +uri+
+ # if it may contain invalid Gem::URI characters.
+ #
+ def self.parse(uri)
+ RFC3986_PARSER.parse(uri)
+ end
+
+ # Merges the given Gem::URI strings +str+
+ # per {RFC 2396}[https://www.rfc-editor.org/rfc/rfc2396.html].
+ #
+ # Each string in +str+ is converted to an
+ # {RFC3986 Gem::URI}[https://www.rfc-editor.org/rfc/rfc3986.html] before being merged.
+ #
+ # Examples:
+ #
+ # Gem::URI.join("http://example.com/","main.rbx")
+ # # => #<Gem::URI::HTTP http://example.com/main.rbx>
+ #
+ # Gem::URI.join('http://example.com', 'foo')
+ # # => #<Gem::URI::HTTP http://example.com/foo>
+ #
+ # Gem::URI.join('http://example.com', '/foo', '/bar')
+ # # => #<Gem::URI::HTTP http://example.com/bar>
+ #
+ # Gem::URI.join('http://example.com', '/foo', 'bar')
+ # # => #<Gem::URI::HTTP http://example.com/bar>
+ #
+ # Gem::URI.join('http://example.com', '/foo/', 'bar')
+ # # => #<Gem::URI::HTTP http://example.com/foo/bar>
+ #
+ def self.join(*str)
+ RFC3986_PARSER.join(*str)
+ end
+
+ #
+ # == Synopsis
+ #
+ # Gem::URI::extract(str[, schemes][,&blk])
+ #
+ # == Args
+ #
+ # +str+::
+ # String to extract URIs from.
+ # +schemes+::
+ # Limit Gem::URI matching to specific schemes.
+ #
+ # == Description
+ #
+ # Extracts URIs from a string. If block given, iterates through all matched URIs.
+ # Returns nil if block given or array with matches.
+ #
+ # == Usage
+ #
+ # require "rubygems/vendor/uri/lib/uri"
+ #
+ # Gem::URI.extract("text here http://foo.example.org/bla and here mailto:test@example.com and here also.")
+ # # => ["http://foo.example.com/bla", "mailto:test@example.com"]
+ #
+ def self.extract(str, schemes = nil, &block) # :nodoc:
+ warn "Gem::URI.extract is obsolete", uplevel: 1 if $VERBOSE
+ DEFAULT_PARSER.extract(str, schemes, &block)
+ end
+
+ #
+ # == Synopsis
+ #
+ # Gem::URI::regexp([match_schemes])
+ #
+ # == Args
+ #
+ # +match_schemes+::
+ # Array of schemes. If given, resulting regexp matches to URIs
+ # whose scheme is one of the match_schemes.
+ #
+ # == Description
+ #
+ # Returns a Regexp object which matches to Gem::URI-like strings.
+ # The Regexp object returned by this method includes arbitrary
+ # number of capture group (parentheses). Never rely on its number.
+ #
+ # == Usage
+ #
+ # require 'rubygems/vendor/uri/lib/uri'
+ #
+ # # extract first Gem::URI from html_string
+ # html_string.slice(Gem::URI.regexp)
+ #
+ # # remove ftp URIs
+ # html_string.sub(Gem::URI.regexp(['ftp']), '')
+ #
+ # # You should not rely on the number of parentheses
+ # html_string.scan(Gem::URI.regexp) do |*matches|
+ # p $&
+ # end
+ #
+ def self.regexp(schemes = nil)# :nodoc:
+ warn "Gem::URI.regexp is obsolete", uplevel: 1 if $VERBOSE
+ DEFAULT_PARSER.make_regexp(schemes)
+ end
+
+ TBLENCWWWCOMP_ = {} # :nodoc:
+ 256.times do |i|
+ TBLENCWWWCOMP_[-i.chr] = -('%%%02X' % i)
+ end
+ TBLENCURICOMP_ = TBLENCWWWCOMP_.dup.freeze
+ TBLENCWWWCOMP_[' '] = '+'
+ TBLENCWWWCOMP_.freeze
+ TBLDECWWWCOMP_ = {} # :nodoc:
+ 256.times do |i|
+ h, l = i>>4, i&15
+ TBLDECWWWCOMP_[-('%%%X%X' % [h, l])] = -i.chr
+ TBLDECWWWCOMP_[-('%%%x%X' % [h, l])] = -i.chr
+ TBLDECWWWCOMP_[-('%%%X%x' % [h, l])] = -i.chr
+ TBLDECWWWCOMP_[-('%%%x%x' % [h, l])] = -i.chr
+ end
+ TBLDECWWWCOMP_['+'] = ' '
+ TBLDECWWWCOMP_.freeze
+
+ # Returns a URL-encoded string derived from the given string +str+.
+ #
+ # The returned string:
+ #
+ # - Preserves:
+ #
+ # - Characters <tt>'*'</tt>, <tt>'.'</tt>, <tt>'-'</tt>, and <tt>'_'</tt>.
+ # - Character in ranges <tt>'a'..'z'</tt>, <tt>'A'..'Z'</tt>,
+ # and <tt>'0'..'9'</tt>.
+ #
+ # Example:
+ #
+ # Gem::URI.encode_www_form_component('*.-_azAZ09')
+ # # => "*.-_azAZ09"
+ #
+ # - Converts:
+ #
+ # - Character <tt>' '</tt> to character <tt>'+'</tt>.
+ # - Any other character to "percent notation";
+ # the percent notation for character <i>c</i> is <tt>'%%%X' % c.ord</tt>.
+ #
+ # Example:
+ #
+ # Gem::URI.encode_www_form_component('Here are some punctuation characters: ,;?:')
+ # # => "Here+are+some+punctuation+characters%3A+%2C%3B%3F%3A"
+ #
+ # Encoding:
+ #
+ # - If +str+ has encoding Encoding::ASCII_8BIT, argument +enc+ is ignored.
+ # - Otherwise +str+ is converted first to Encoding::UTF_8
+ # (with suitable character replacements),
+ # and then to encoding +enc+.
+ #
+ # In either case, the returned string has forced encoding Encoding::US_ASCII.
+ #
+ # Related: Gem::URI.encode_uri_component (encodes <tt>' '</tt> as <tt>'%20'</tt>).
+ def self.encode_www_form_component(str, enc=nil)
+ _encode_uri_component(/[^*\-.0-9A-Z_a-z]/, TBLENCWWWCOMP_, str, enc)
+ end
+
+ # Returns a string decoded from the given \URL-encoded string +str+.
+ #
+ # The given string is first encoded as Encoding::ASCII-8BIT (using String#b),
+ # then decoded (as below), and finally force-encoded to the given encoding +enc+.
+ #
+ # The returned string:
+ #
+ # - Preserves:
+ #
+ # - Characters <tt>'*'</tt>, <tt>'.'</tt>, <tt>'-'</tt>, and <tt>'_'</tt>.
+ # - Character in ranges <tt>'a'..'z'</tt>, <tt>'A'..'Z'</tt>,
+ # and <tt>'0'..'9'</tt>.
+ #
+ # Example:
+ #
+ # Gem::URI.decode_www_form_component('*.-_azAZ09')
+ # # => "*.-_azAZ09"
+ #
+ # - Converts:
+ #
+ # - Character <tt>'+'</tt> to character <tt>' '</tt>.
+ # - Each "percent notation" to an ASCII character.
+ #
+ # Example:
+ #
+ # Gem::URI.decode_www_form_component('Here+are+some+punctuation+characters%3A+%2C%3B%3F%3A')
+ # # => "Here are some punctuation characters: ,;?:"
+ #
+ # Related: Gem::URI.decode_uri_component (preserves <tt>'+'</tt>).
+ def self.decode_www_form_component(str, enc=Encoding::UTF_8)
+ _decode_uri_component(/\+|%\h\h/, str, enc)
+ end
+
+ # Like Gem::URI.encode_www_form_component, except that <tt>' '</tt> (space)
+ # is encoded as <tt>'%20'</tt> (instead of <tt>'+'</tt>).
+ def self.encode_uri_component(str, enc=nil)
+ _encode_uri_component(/[^*\-.0-9A-Z_a-z]/, TBLENCURICOMP_, str, enc)
+ end
+
+ # Like Gem::URI.decode_www_form_component, except that <tt>'+'</tt> is preserved.
+ def self.decode_uri_component(str, enc=Encoding::UTF_8)
+ _decode_uri_component(/%\h\h/, str, enc)
+ end
+
+ def self._encode_uri_component(regexp, table, str, enc)
+ str = str.to_s.dup
+ if str.encoding != Encoding::ASCII_8BIT
+ if enc && enc != Encoding::ASCII_8BIT
+ str.encode!(Encoding::UTF_8, invalid: :replace, undef: :replace)
+ str.encode!(enc, fallback: ->(x){"&##{x.ord};"})
+ end
+ str.force_encoding(Encoding::ASCII_8BIT)
+ end
+ str.gsub!(regexp, table)
+ str.force_encoding(Encoding::US_ASCII)
+ end
+ private_class_method :_encode_uri_component
+
+ def self._decode_uri_component(regexp, str, enc)
+ raise ArgumentError, "invalid %-encoding (#{str})" if /%(?!\h\h)/.match?(str)
+ str.b.gsub(regexp, TBLDECWWWCOMP_).force_encoding(enc)
+ end
+ private_class_method :_decode_uri_component
+
+ # Returns a URL-encoded string derived from the given
+ # {Enumerable}[https://docs.ruby-lang.org/en/master/Enumerable.html#module-Enumerable-label-Enumerable+in+Ruby+Classes]
+ # +enum+.
+ #
+ # The result is suitable for use as form data
+ # for an \HTTP request whose <tt>Content-Type</tt> is
+ # <tt>'application/x-www-form-urlencoded'</tt>.
+ #
+ # The returned string consists of the elements of +enum+,
+ # each converted to one or more URL-encoded strings,
+ # and all joined with character <tt>'&'</tt>.
+ #
+ # Simple examples:
+ #
+ # Gem::URI.encode_www_form([['foo', 0], ['bar', 1], ['baz', 2]])
+ # # => "foo=0&bar=1&baz=2"
+ # Gem::URI.encode_www_form({foo: 0, bar: 1, baz: 2})
+ # # => "foo=0&bar=1&baz=2"
+ #
+ # The returned string is formed using method Gem::URI.encode_www_form_component,
+ # which converts certain characters:
+ #
+ # Gem::URI.encode_www_form('f#o': '/', 'b-r': '$', 'b z': '@')
+ # # => "f%23o=%2F&b-r=%24&b+z=%40"
+ #
+ # When +enum+ is Array-like, each element +ele+ is converted to a field:
+ #
+ # - If +ele+ is an array of two or more elements,
+ # the field is formed from its first two elements
+ # (and any additional elements are ignored):
+ #
+ # name = Gem::URI.encode_www_form_component(ele[0], enc)
+ # value = Gem::URI.encode_www_form_component(ele[1], enc)
+ # "#{name}=#{value}"
+ #
+ # Examples:
+ #
+ # Gem::URI.encode_www_form([%w[foo bar], %w[baz bat bah]])
+ # # => "foo=bar&baz=bat"
+ # Gem::URI.encode_www_form([['foo', 0], ['bar', :baz, 'bat']])
+ # # => "foo=0&bar=baz"
+ #
+ # - If +ele+ is an array of one element,
+ # the field is formed from <tt>ele[0]</tt>:
+ #
+ # Gem::URI.encode_www_form_component(ele[0])
+ #
+ # Example:
+ #
+ # Gem::URI.encode_www_form([['foo'], [:bar], [0]])
+ # # => "foo&bar&0"
+ #
+ # - Otherwise the field is formed from +ele+:
+ #
+ # Gem::URI.encode_www_form_component(ele)
+ #
+ # Example:
+ #
+ # Gem::URI.encode_www_form(['foo', :bar, 0])
+ # # => "foo&bar&0"
+ #
+ # The elements of an Array-like +enum+ may be mixture:
+ #
+ # Gem::URI.encode_www_form([['foo', 0], ['bar', 1, 2], ['baz'], :bat])
+ # # => "foo=0&bar=1&baz&bat"
+ #
+ # When +enum+ is Hash-like,
+ # each +key+/+value+ pair is converted to one or more fields:
+ #
+ # - If +value+ is
+ # {Array-convertible}[https://docs.ruby-lang.org/en/master/implicit_conversion_rdoc.html#label-Array-Convertible+Objects],
+ # each element +ele+ in +value+ is paired with +key+ to form a field:
+ #
+ # name = Gem::URI.encode_www_form_component(key, enc)
+ # value = Gem::URI.encode_www_form_component(ele, enc)
+ # "#{name}=#{value}"
+ #
+ # Example:
+ #
+ # Gem::URI.encode_www_form({foo: [:bar, 1], baz: [:bat, :bam, 2]})
+ # # => "foo=bar&foo=1&baz=bat&baz=bam&baz=2"
+ #
+ # - Otherwise, +key+ and +value+ are paired to form a field:
+ #
+ # name = Gem::URI.encode_www_form_component(key, enc)
+ # value = Gem::URI.encode_www_form_component(value, enc)
+ # "#{name}=#{value}"
+ #
+ # Example:
+ #
+ # Gem::URI.encode_www_form({foo: 0, bar: 1, baz: 2})
+ # # => "foo=0&bar=1&baz=2"
+ #
+ # The elements of a Hash-like +enum+ may be mixture:
+ #
+ # Gem::URI.encode_www_form({foo: [0, 1], bar: 2})
+ # # => "foo=0&foo=1&bar=2"
+ #
+ def self.encode_www_form(enum, enc=nil)
+ enum.map do |k,v|
+ if v.nil?
+ encode_www_form_component(k, enc)
+ elsif v.respond_to?(:to_ary)
+ v.to_ary.map do |w|
+ str = encode_www_form_component(k, enc)
+ unless w.nil?
+ str << '='
+ str << encode_www_form_component(w, enc)
+ end
+ end.join('&')
+ else
+ str = encode_www_form_component(k, enc)
+ str << '='
+ str << encode_www_form_component(v, enc)
+ end
+ end.join('&')
+ end
+
+ # Returns name/value pairs derived from the given string +str+,
+ # which must be an ASCII string.
+ #
+ # The method may be used to decode the body of Net::HTTPResponse object +res+
+ # for which <tt>res['Content-Type']</tt> is <tt>'application/x-www-form-urlencoded'</tt>.
+ #
+ # The returned data is an array of 2-element subarrays;
+ # each subarray is a name/value pair (both are strings).
+ # Each returned string has encoding +enc+,
+ # and has had invalid characters removed via
+ # {String#scrub}[https://docs.ruby-lang.org/en/master/String.html#method-i-scrub].
+ #
+ # A simple example:
+ #
+ # Gem::URI.decode_www_form('foo=0&bar=1&baz')
+ # # => [["foo", "0"], ["bar", "1"], ["baz", ""]]
+ #
+ # The returned strings have certain conversions,
+ # similar to those performed in Gem::URI.decode_www_form_component:
+ #
+ # Gem::URI.decode_www_form('f%23o=%2F&b-r=%24&b+z=%40')
+ # # => [["f#o", "/"], ["b-r", "$"], ["b z", "@"]]
+ #
+ # The given string may contain consecutive separators:
+ #
+ # Gem::URI.decode_www_form('foo=0&&bar=1&&baz=2')
+ # # => [["foo", "0"], ["", ""], ["bar", "1"], ["", ""], ["baz", "2"]]
+ #
+ # A different separator may be specified:
+ #
+ # Gem::URI.decode_www_form('foo=0--bar=1--baz', separator: '--')
+ # # => [["foo", "0"], ["bar", "1"], ["baz", ""]]
+ #
+ def self.decode_www_form(str, enc=Encoding::UTF_8, separator: '&', use__charset_: false, isindex: false)
+ raise ArgumentError, "the input of #{self.name}.#{__method__} must be ASCII only string" unless str.ascii_only?
+ ary = []
+ return ary if str.empty?
+ enc = Encoding.find(enc)
+ str.b.each_line(separator) do |string|
+ string.chomp!(separator)
+ key, sep, val = string.partition('=')
+ if isindex
+ if sep.empty?
+ val = key
+ key = +''
+ end
+ isindex = false
+ end
+
+ if use__charset_ and key == '_charset_' and e = get_encoding(val)
+ enc = e
+ use__charset_ = false
+ end
+
+ key.gsub!(/\+|%\h\h/, TBLDECWWWCOMP_)
+ if val
+ val.gsub!(/\+|%\h\h/, TBLDECWWWCOMP_)
+ else
+ val = +''
+ end
+
+ ary << [key, val]
+ end
+ ary.each do |k, v|
+ k.force_encoding(enc)
+ k.scrub!
+ v.force_encoding(enc)
+ v.scrub!
+ end
+ ary
+ end
+
+ private
+=begin command for WEB_ENCODINGS_
+ curl https://encoding.spec.whatwg.org/encodings.json|
+ ruby -rjson -e 'H={}
+ h={
+ "shift_jis"=>"Windows-31J",
+ "euc-jp"=>"cp51932",
+ "iso-2022-jp"=>"cp50221",
+ "x-mac-cyrillic"=>"macCyrillic",
+ }
+ JSON($<.read).map{|x|x["encodings"]}.flatten.each{|x|
+ Encoding.find(n=h.fetch(n=x["name"].downcase,n))rescue next
+ x["labels"].each{|y|H[y]=n}
+ }
+ puts "{"
+ H.each{|k,v|puts %[ #{k.dump}=>#{v.dump},]}
+ puts "}"
+'
+=end
+ WEB_ENCODINGS_ = {
+ "unicode-1-1-utf-8"=>"utf-8",
+ "utf-8"=>"utf-8",
+ "utf8"=>"utf-8",
+ "866"=>"ibm866",
+ "cp866"=>"ibm866",
+ "csibm866"=>"ibm866",
+ "ibm866"=>"ibm866",
+ "csisolatin2"=>"iso-8859-2",
+ "iso-8859-2"=>"iso-8859-2",
+ "iso-ir-101"=>"iso-8859-2",
+ "iso8859-2"=>"iso-8859-2",
+ "iso88592"=>"iso-8859-2",
+ "iso_8859-2"=>"iso-8859-2",
+ "iso_8859-2:1987"=>"iso-8859-2",
+ "l2"=>"iso-8859-2",
+ "latin2"=>"iso-8859-2",
+ "csisolatin3"=>"iso-8859-3",
+ "iso-8859-3"=>"iso-8859-3",
+ "iso-ir-109"=>"iso-8859-3",
+ "iso8859-3"=>"iso-8859-3",
+ "iso88593"=>"iso-8859-3",
+ "iso_8859-3"=>"iso-8859-3",
+ "iso_8859-3:1988"=>"iso-8859-3",
+ "l3"=>"iso-8859-3",
+ "latin3"=>"iso-8859-3",
+ "csisolatin4"=>"iso-8859-4",
+ "iso-8859-4"=>"iso-8859-4",
+ "iso-ir-110"=>"iso-8859-4",
+ "iso8859-4"=>"iso-8859-4",
+ "iso88594"=>"iso-8859-4",
+ "iso_8859-4"=>"iso-8859-4",
+ "iso_8859-4:1988"=>"iso-8859-4",
+ "l4"=>"iso-8859-4",
+ "latin4"=>"iso-8859-4",
+ "csisolatincyrillic"=>"iso-8859-5",
+ "cyrillic"=>"iso-8859-5",
+ "iso-8859-5"=>"iso-8859-5",
+ "iso-ir-144"=>"iso-8859-5",
+ "iso8859-5"=>"iso-8859-5",
+ "iso88595"=>"iso-8859-5",
+ "iso_8859-5"=>"iso-8859-5",
+ "iso_8859-5:1988"=>"iso-8859-5",
+ "arabic"=>"iso-8859-6",
+ "asmo-708"=>"iso-8859-6",
+ "csiso88596e"=>"iso-8859-6",
+ "csiso88596i"=>"iso-8859-6",
+ "csisolatinarabic"=>"iso-8859-6",
+ "ecma-114"=>"iso-8859-6",
+ "iso-8859-6"=>"iso-8859-6",
+ "iso-8859-6-e"=>"iso-8859-6",
+ "iso-8859-6-i"=>"iso-8859-6",
+ "iso-ir-127"=>"iso-8859-6",
+ "iso8859-6"=>"iso-8859-6",
+ "iso88596"=>"iso-8859-6",
+ "iso_8859-6"=>"iso-8859-6",
+ "iso_8859-6:1987"=>"iso-8859-6",
+ "csisolatingreek"=>"iso-8859-7",
+ "ecma-118"=>"iso-8859-7",
+ "elot_928"=>"iso-8859-7",
+ "greek"=>"iso-8859-7",
+ "greek8"=>"iso-8859-7",
+ "iso-8859-7"=>"iso-8859-7",
+ "iso-ir-126"=>"iso-8859-7",
+ "iso8859-7"=>"iso-8859-7",
+ "iso88597"=>"iso-8859-7",
+ "iso_8859-7"=>"iso-8859-7",
+ "iso_8859-7:1987"=>"iso-8859-7",
+ "sun_eu_greek"=>"iso-8859-7",
+ "csiso88598e"=>"iso-8859-8",
+ "csisolatinhebrew"=>"iso-8859-8",
+ "hebrew"=>"iso-8859-8",
+ "iso-8859-8"=>"iso-8859-8",
+ "iso-8859-8-e"=>"iso-8859-8",
+ "iso-ir-138"=>"iso-8859-8",
+ "iso8859-8"=>"iso-8859-8",
+ "iso88598"=>"iso-8859-8",
+ "iso_8859-8"=>"iso-8859-8",
+ "iso_8859-8:1988"=>"iso-8859-8",
+ "visual"=>"iso-8859-8",
+ "csisolatin6"=>"iso-8859-10",
+ "iso-8859-10"=>"iso-8859-10",
+ "iso-ir-157"=>"iso-8859-10",
+ "iso8859-10"=>"iso-8859-10",
+ "iso885910"=>"iso-8859-10",
+ "l6"=>"iso-8859-10",
+ "latin6"=>"iso-8859-10",
+ "iso-8859-13"=>"iso-8859-13",
+ "iso8859-13"=>"iso-8859-13",
+ "iso885913"=>"iso-8859-13",
+ "iso-8859-14"=>"iso-8859-14",
+ "iso8859-14"=>"iso-8859-14",
+ "iso885914"=>"iso-8859-14",
+ "csisolatin9"=>"iso-8859-15",
+ "iso-8859-15"=>"iso-8859-15",
+ "iso8859-15"=>"iso-8859-15",
+ "iso885915"=>"iso-8859-15",
+ "iso_8859-15"=>"iso-8859-15",
+ "l9"=>"iso-8859-15",
+ "iso-8859-16"=>"iso-8859-16",
+ "cskoi8r"=>"koi8-r",
+ "koi"=>"koi8-r",
+ "koi8"=>"koi8-r",
+ "koi8-r"=>"koi8-r",
+ "koi8_r"=>"koi8-r",
+ "koi8-ru"=>"koi8-u",
+ "koi8-u"=>"koi8-u",
+ "dos-874"=>"windows-874",
+ "iso-8859-11"=>"windows-874",
+ "iso8859-11"=>"windows-874",
+ "iso885911"=>"windows-874",
+ "tis-620"=>"windows-874",
+ "windows-874"=>"windows-874",
+ "cp1250"=>"windows-1250",
+ "windows-1250"=>"windows-1250",
+ "x-cp1250"=>"windows-1250",
+ "cp1251"=>"windows-1251",
+ "windows-1251"=>"windows-1251",
+ "x-cp1251"=>"windows-1251",
+ "ansi_x3.4-1968"=>"windows-1252",
+ "ascii"=>"windows-1252",
+ "cp1252"=>"windows-1252",
+ "cp819"=>"windows-1252",
+ "csisolatin1"=>"windows-1252",
+ "ibm819"=>"windows-1252",
+ "iso-8859-1"=>"windows-1252",
+ "iso-ir-100"=>"windows-1252",
+ "iso8859-1"=>"windows-1252",
+ "iso88591"=>"windows-1252",
+ "iso_8859-1"=>"windows-1252",
+ "iso_8859-1:1987"=>"windows-1252",
+ "l1"=>"windows-1252",
+ "latin1"=>"windows-1252",
+ "us-ascii"=>"windows-1252",
+ "windows-1252"=>"windows-1252",
+ "x-cp1252"=>"windows-1252",
+ "cp1253"=>"windows-1253",
+ "windows-1253"=>"windows-1253",
+ "x-cp1253"=>"windows-1253",
+ "cp1254"=>"windows-1254",
+ "csisolatin5"=>"windows-1254",
+ "iso-8859-9"=>"windows-1254",
+ "iso-ir-148"=>"windows-1254",
+ "iso8859-9"=>"windows-1254",
+ "iso88599"=>"windows-1254",
+ "iso_8859-9"=>"windows-1254",
+ "iso_8859-9:1989"=>"windows-1254",
+ "l5"=>"windows-1254",
+ "latin5"=>"windows-1254",
+ "windows-1254"=>"windows-1254",
+ "x-cp1254"=>"windows-1254",
+ "cp1255"=>"windows-1255",
+ "windows-1255"=>"windows-1255",
+ "x-cp1255"=>"windows-1255",
+ "cp1256"=>"windows-1256",
+ "windows-1256"=>"windows-1256",
+ "x-cp1256"=>"windows-1256",
+ "cp1257"=>"windows-1257",
+ "windows-1257"=>"windows-1257",
+ "x-cp1257"=>"windows-1257",
+ "cp1258"=>"windows-1258",
+ "windows-1258"=>"windows-1258",
+ "x-cp1258"=>"windows-1258",
+ "x-mac-cyrillic"=>"macCyrillic",
+ "x-mac-ukrainian"=>"macCyrillic",
+ "chinese"=>"gbk",
+ "csgb2312"=>"gbk",
+ "csiso58gb231280"=>"gbk",
+ "gb2312"=>"gbk",
+ "gb_2312"=>"gbk",
+ "gb_2312-80"=>"gbk",
+ "gbk"=>"gbk",
+ "iso-ir-58"=>"gbk",
+ "x-gbk"=>"gbk",
+ "gb18030"=>"gb18030",
+ "big5"=>"big5",
+ "big5-hkscs"=>"big5",
+ "cn-big5"=>"big5",
+ "csbig5"=>"big5",
+ "x-x-big5"=>"big5",
+ "cseucpkdfmtjapanese"=>"cp51932",
+ "euc-jp"=>"cp51932",
+ "x-euc-jp"=>"cp51932",
+ "csiso2022jp"=>"cp50221",
+ "iso-2022-jp"=>"cp50221",
+ "csshiftjis"=>"Windows-31J",
+ "ms932"=>"Windows-31J",
+ "ms_kanji"=>"Windows-31J",
+ "shift-jis"=>"Windows-31J",
+ "shift_jis"=>"Windows-31J",
+ "sjis"=>"Windows-31J",
+ "windows-31j"=>"Windows-31J",
+ "x-sjis"=>"Windows-31J",
+ "cseuckr"=>"euc-kr",
+ "csksc56011987"=>"euc-kr",
+ "euc-kr"=>"euc-kr",
+ "iso-ir-149"=>"euc-kr",
+ "korean"=>"euc-kr",
+ "ks_c_5601-1987"=>"euc-kr",
+ "ks_c_5601-1989"=>"euc-kr",
+ "ksc5601"=>"euc-kr",
+ "ksc_5601"=>"euc-kr",
+ "windows-949"=>"euc-kr",
+ "utf-16be"=>"utf-16be",
+ "utf-16"=>"utf-16le",
+ "utf-16le"=>"utf-16le",
+ } # :nodoc:
+ Ractor.make_shareable(WEB_ENCODINGS_) if defined?(Ractor)
+
+ # :nodoc:
+ # return encoding or nil
+ # http://encoding.spec.whatwg.org/#concept-encoding-get
+ def self.get_encoding(label)
+ Encoding.find(WEB_ENCODINGS_[label.to_str.strip.downcase]) rescue nil
+ end
+end # module Gem::URI
+
+module Gem
+
+ #
+ # Returns a \Gem::URI object derived from the given +uri+,
+ # which may be a \Gem::URI string or an existing \Gem::URI object:
+ #
+ # # Returns a new Gem::URI.
+ # uri = Gem::URI('http://github.com/ruby/ruby')
+ # # => #<Gem::URI::HTTP http://github.com/ruby/ruby>
+ # # Returns the given Gem::URI.
+ # Gem::URI(uri)
+ # # => #<Gem::URI::HTTP http://github.com/ruby/ruby>
+ #
+ def URI(uri)
+ if uri.is_a?(Gem::URI::Generic)
+ uri
+ elsif uri = String.try_convert(uri)
+ Gem::URI.parse(uri)
+ else
+ raise ArgumentError,
+ "bad argument (expected Gem::URI object or Gem::URI string)"
+ end
+ end
+ module_function :URI
+end
diff --git a/lib/rubygems/vendor/uri/lib/uri/file.rb b/lib/rubygems/vendor/uri/lib/uri/file.rb
new file mode 100644
index 0000000000..d419b26055
--- /dev/null
+++ b/lib/rubygems/vendor/uri/lib/uri/file.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+require_relative 'generic'
+
+module Gem::URI
+
+ #
+ # The "file" Gem::URI is defined by RFC8089.
+ #
+ class File < Generic
+ # A Default port of nil for Gem::URI::File.
+ DEFAULT_PORT = nil
+
+ #
+ # An Array of the available components for Gem::URI::File.
+ #
+ COMPONENT = [
+ :scheme,
+ :host,
+ :path
+ ].freeze
+
+ #
+ # == Description
+ #
+ # Creates a new Gem::URI::File object from components, with syntax checking.
+ #
+ # The components accepted are +host+ and +path+.
+ #
+ # The components should be provided either as an Array, or as a Hash
+ # with keys formed by preceding the component names with a colon.
+ #
+ # If an Array is used, the components must be passed in the
+ # order <code>[host, path]</code>.
+ #
+ # A path from e.g. the File class should be escaped before
+ # being passed.
+ #
+ # Examples:
+ #
+ # require 'rubygems/vendor/uri/lib/uri'
+ #
+ # uri1 = Gem::URI::File.build(['host.example.com', '/path/file.zip'])
+ # uri1.to_s # => "file://host.example.com/path/file.zip"
+ #
+ # uri2 = Gem::URI::File.build({:host => 'host.example.com',
+ # :path => '/ruby/src'})
+ # uri2.to_s # => "file://host.example.com/ruby/src"
+ #
+ # uri3 = Gem::URI::File.build({:path => Gem::URI::escape('/path/my file.txt')})
+ # uri3.to_s # => "file:///path/my%20file.txt"
+ #
+ def self.build(args)
+ tmp = Util::make_components_hash(self, args)
+ super(tmp)
+ end
+
+ # Protected setter for the host component +v+.
+ #
+ # See also Gem::URI::Generic.host=.
+ #
+ def set_host(v)
+ v = "" if v.nil? || v == "localhost"
+ @host = v
+ end
+
+ # do nothing
+ def set_port(v)
+ end
+
+ # raise InvalidURIError
+ def check_userinfo(user)
+ raise Gem::URI::InvalidURIError, "can not set userinfo for file Gem::URI"
+ end
+
+ # raise InvalidURIError
+ def check_user(user)
+ raise Gem::URI::InvalidURIError, "can not set user for file Gem::URI"
+ end
+
+ # raise InvalidURIError
+ def check_password(user)
+ raise Gem::URI::InvalidURIError, "can not set password for file Gem::URI"
+ end
+
+ # do nothing
+ def set_userinfo(v)
+ end
+
+ # do nothing
+ def set_user(v)
+ end
+
+ # do nothing
+ def set_password(v)
+ end
+ end
+
+ register_scheme 'FILE', File
+end
diff --git a/lib/rubygems/vendor/uri/lib/uri/ftp.rb b/lib/rubygems/vendor/uri/lib/uri/ftp.rb
new file mode 100644
index 0000000000..100498ffb2
--- /dev/null
+++ b/lib/rubygems/vendor/uri/lib/uri/ftp.rb
@@ -0,0 +1,267 @@
+# frozen_string_literal: false
+# = uri/ftp.rb
+#
+# Author:: Akira Yamada <akira@ruby-lang.org>
+# License:: You can redistribute it and/or modify it under the same term as Ruby.
+#
+# See Gem::URI for general documentation
+#
+
+require_relative 'generic'
+
+module Gem::URI
+
+ #
+ # FTP Gem::URI syntax is defined by RFC1738 section 3.2.
+ #
+ # This class will be redesigned because of difference of implementations;
+ # the structure of its path. draft-hoffman-ftp-uri-04 is a draft but it
+ # is a good summary about the de facto spec.
+ # http://tools.ietf.org/html/draft-hoffman-ftp-uri-04
+ #
+ class FTP < Generic
+ # A Default port of 21 for Gem::URI::FTP.
+ DEFAULT_PORT = 21
+
+ #
+ # An Array of the available components for Gem::URI::FTP.
+ #
+ COMPONENT = [
+ :scheme,
+ :userinfo, :host, :port,
+ :path, :typecode
+ ].freeze
+
+ #
+ # Typecode is "a", "i", or "d".
+ #
+ # * "a" indicates a text file (the FTP command was ASCII)
+ # * "i" indicates a binary file (FTP command IMAGE)
+ # * "d" indicates the contents of a directory should be displayed
+ #
+ TYPECODE = ['a', 'i', 'd'].freeze
+
+ # Typecode prefix ";type=".
+ TYPECODE_PREFIX = ';type='.freeze
+
+ def self.new2(user, password, host, port, path,
+ typecode = nil, arg_check = true) # :nodoc:
+ # Do not use this method! Not tested. [Bug #7301]
+ # This methods remains just for compatibility,
+ # Keep it undocumented until the active maintainer is assigned.
+ typecode = nil if typecode.size == 0
+ if typecode && !TYPECODE.include?(typecode)
+ raise ArgumentError,
+ "bad typecode is specified: #{typecode}"
+ end
+
+ # do escape
+
+ self.new('ftp',
+ [user, password],
+ host, port, nil,
+ typecode ? path + TYPECODE_PREFIX + typecode : path,
+ nil, nil, nil, arg_check)
+ end
+
+ #
+ # == Description
+ #
+ # Creates a new Gem::URI::FTP object from components, with syntax checking.
+ #
+ # The components accepted are +userinfo+, +host+, +port+, +path+, and
+ # +typecode+.
+ #
+ # The components should be provided either as an Array, or as a Hash
+ # with keys formed by preceding the component names with a colon.
+ #
+ # If an Array is used, the components must be passed in the
+ # order <code>[userinfo, host, port, path, typecode]</code>.
+ #
+ # If the path supplied is absolute, it will be escaped in order to
+ # make it absolute in the Gem::URI.
+ #
+ # Examples:
+ #
+ # require 'rubygems/vendor/uri/lib/uri'
+ #
+ # uri1 = Gem::URI::FTP.build(['user:password', 'ftp.example.com', nil,
+ # '/path/file.zip', 'i'])
+ # uri1.to_s # => "ftp://user:password@ftp.example.com/%2Fpath/file.zip;type=i"
+ #
+ # uri2 = Gem::URI::FTP.build({:host => 'ftp.example.com',
+ # :path => 'ruby/src'})
+ # uri2.to_s # => "ftp://ftp.example.com/ruby/src"
+ #
+ def self.build(args)
+
+ # Fix the incoming path to be generic URL syntax
+ # FTP path -> URL path
+ # foo/bar /foo/bar
+ # /foo/bar /%2Ffoo/bar
+ #
+ if args.kind_of?(Array)
+ args[3] = '/' + args[3].sub(/^\//, '%2F')
+ else
+ args[:path] = '/' + args[:path].sub(/^\//, '%2F')
+ end
+
+ tmp = Util::make_components_hash(self, args)
+
+ if tmp[:typecode]
+ if tmp[:typecode].size == 1
+ tmp[:typecode] = TYPECODE_PREFIX + tmp[:typecode]
+ end
+ tmp[:path] << tmp[:typecode]
+ end
+
+ return super(tmp)
+ end
+
+ #
+ # == Description
+ #
+ # Creates a new Gem::URI::FTP object from generic URL components with no
+ # syntax checking.
+ #
+ # Unlike build(), this method does not escape the path component as
+ # required by RFC1738; instead it is treated as per RFC2396.
+ #
+ # Arguments are +scheme+, +userinfo+, +host+, +port+, +registry+, +path+,
+ # +opaque+, +query+, and +fragment+, in that order.
+ #
+ def initialize(scheme,
+ userinfo, host, port, registry,
+ path, opaque,
+ query,
+ fragment,
+ parser = nil,
+ arg_check = false)
+ raise InvalidURIError unless path
+ path = path.sub(/^\//,'')
+ path.sub!(/^%2F/,'/')
+ super(scheme, userinfo, host, port, registry, path, opaque,
+ query, fragment, parser, arg_check)
+ @typecode = nil
+ if tmp = @path.index(TYPECODE_PREFIX)
+ typecode = @path[tmp + TYPECODE_PREFIX.size..-1]
+ @path = @path[0..tmp - 1]
+
+ if arg_check
+ self.typecode = typecode
+ else
+ self.set_typecode(typecode)
+ end
+ end
+ end
+
+ # typecode accessor.
+ #
+ # See Gem::URI::FTP::COMPONENT.
+ attr_reader :typecode
+
+ # Validates typecode +v+,
+ # returns +true+ or +false+.
+ #
+ def check_typecode(v)
+ if TYPECODE.include?(v)
+ return true
+ else
+ raise InvalidComponentError,
+ "bad typecode(expected #{TYPECODE.join(', ')}): #{v}"
+ end
+ end
+ private :check_typecode
+
+ # Private setter for the typecode +v+.
+ #
+ # See also Gem::URI::FTP.typecode=.
+ #
+ def set_typecode(v)
+ @typecode = v
+ end
+ protected :set_typecode
+
+ #
+ # == Args
+ #
+ # +v+::
+ # String
+ #
+ # == Description
+ #
+ # Public setter for the typecode +v+
+ # (with validation).
+ #
+ # See also Gem::URI::FTP.check_typecode.
+ #
+ # == Usage
+ #
+ # require 'rubygems/vendor/uri/lib/uri'
+ #
+ # uri = Gem::URI.parse("ftp://john@ftp.example.com/my_file.img")
+ # #=> #<Gem::URI::FTP ftp://john@ftp.example.com/my_file.img>
+ # uri.typecode = "i"
+ # uri
+ # #=> #<Gem::URI::FTP ftp://john@ftp.example.com/my_file.img;type=i>
+ #
+ def typecode=(typecode)
+ check_typecode(typecode)
+ set_typecode(typecode)
+ typecode
+ end
+
+ def merge(oth) # :nodoc:
+ tmp = super(oth)
+ if self != tmp
+ tmp.set_typecode(oth.typecode)
+ end
+
+ return tmp
+ end
+
+ # Returns the path from an FTP Gem::URI.
+ #
+ # RFC 1738 specifically states that the path for an FTP Gem::URI does not
+ # include the / which separates the Gem::URI path from the Gem::URI host. Example:
+ #
+ # <code>ftp://ftp.example.com/pub/ruby</code>
+ #
+ # The above Gem::URI indicates that the client should connect to
+ # ftp.example.com then cd to pub/ruby from the initial login directory.
+ #
+ # If you want to cd to an absolute directory, you must include an
+ # escaped / (%2F) in the path. Example:
+ #
+ # <code>ftp://ftp.example.com/%2Fpub/ruby</code>
+ #
+ # This method will then return "/pub/ruby".
+ #
+ def path
+ return @path.sub(/^\//,'').sub(/^%2F/,'/')
+ end
+
+ # Private setter for the path of the Gem::URI::FTP.
+ def set_path(v)
+ super("/" + v.sub(/^\//, "%2F"))
+ end
+ protected :set_path
+
+ # Returns a String representation of the Gem::URI::FTP.
+ def to_s
+ save_path = nil
+ if @typecode
+ save_path = @path
+ @path = @path + TYPECODE_PREFIX + @typecode
+ end
+ str = super
+ if @typecode
+ @path = save_path
+ end
+
+ return str
+ end
+ end
+
+ register_scheme 'FTP', FTP
+end
diff --git a/lib/rubygems/vendor/uri/lib/uri/generic.rb b/lib/rubygems/vendor/uri/lib/uri/generic.rb
new file mode 100644
index 0000000000..72c52aa8ee
--- /dev/null
+++ b/lib/rubygems/vendor/uri/lib/uri/generic.rb
@@ -0,0 +1,1588 @@
+# frozen_string_literal: true
+
+# = uri/generic.rb
+#
+# Author:: Akira Yamada <akira@ruby-lang.org>
+# License:: You can redistribute it and/or modify it under the same term as Ruby.
+#
+# See Gem::URI for general documentation
+#
+
+require_relative 'common'
+autoload :IPSocket, 'socket'
+autoload :IPAddr, 'ipaddr'
+
+module Gem::URI
+
+ #
+ # Base class for all Gem::URI classes.
+ # Implements generic Gem::URI syntax as per RFC 2396.
+ #
+ class Generic
+ include Gem::URI
+
+ #
+ # A Default port of nil for Gem::URI::Generic.
+ #
+ DEFAULT_PORT = nil
+
+ #
+ # Returns default port.
+ #
+ def self.default_port
+ self::DEFAULT_PORT
+ end
+
+ #
+ # Returns default port.
+ #
+ def default_port
+ self.class.default_port
+ end
+
+ #
+ # An Array of the available components for Gem::URI::Generic.
+ #
+ COMPONENT = [
+ :scheme,
+ :userinfo, :host, :port, :registry,
+ :path, :opaque,
+ :query,
+ :fragment
+ ].freeze
+
+ #
+ # Components of the Gem::URI in the order.
+ #
+ def self.component
+ self::COMPONENT
+ end
+
+ USE_REGISTRY = false # :nodoc:
+
+ def self.use_registry # :nodoc:
+ self::USE_REGISTRY
+ end
+
+ #
+ # == Synopsis
+ #
+ # See ::new.
+ #
+ # == Description
+ #
+ # At first, tries to create a new Gem::URI::Generic instance using
+ # Gem::URI::Generic::build. But, if exception Gem::URI::InvalidComponentError is raised,
+ # then it does Gem::URI::Escape.escape all Gem::URI components and tries again.
+ #
+ def self.build2(args)
+ begin
+ return self.build(args)
+ rescue InvalidComponentError
+ if args.kind_of?(Array)
+ return self.build(args.collect{|x|
+ if x.is_a?(String)
+ DEFAULT_PARSER.escape(x)
+ else
+ x
+ end
+ })
+ elsif args.kind_of?(Hash)
+ tmp = {}
+ args.each do |key, value|
+ tmp[key] = if value
+ DEFAULT_PARSER.escape(value)
+ else
+ value
+ end
+ end
+ return self.build(tmp)
+ end
+ end
+ end
+
+ #
+ # == Synopsis
+ #
+ # See ::new.
+ #
+ # == Description
+ #
+ # Creates a new Gem::URI::Generic instance from components of Gem::URI::Generic
+ # with check. Components are: scheme, userinfo, host, port, registry, path,
+ # opaque, query, and fragment. You can provide arguments either by an Array or a Hash.
+ # See ::new for hash keys to use or for order of array items.
+ #
+ def self.build(args)
+ if args.kind_of?(Array) &&
+ args.size == ::Gem::URI::Generic::COMPONENT.size
+ tmp = args.dup
+ elsif args.kind_of?(Hash)
+ tmp = ::Gem::URI::Generic::COMPONENT.collect do |c|
+ if args.include?(c)
+ args[c]
+ else
+ nil
+ end
+ end
+ else
+ component = self.class.component rescue ::Gem::URI::Generic::COMPONENT
+ raise ArgumentError,
+ "expected Array of or Hash of components of #{self.class} (#{component.join(', ')})"
+ end
+
+ tmp << nil
+ tmp << true
+ return self.new(*tmp)
+ end
+
+ #
+ # == Args
+ #
+ # +scheme+::
+ # Protocol scheme, i.e. 'http','ftp','mailto' and so on.
+ # +userinfo+::
+ # User name and password, i.e. 'sdmitry:bla'.
+ # +host+::
+ # Server host name.
+ # +port+::
+ # Server port.
+ # +registry+::
+ # Registry of naming authorities.
+ # +path+::
+ # Path on server.
+ # +opaque+::
+ # Opaque part.
+ # +query+::
+ # Query data.
+ # +fragment+::
+ # Part of the Gem::URI after '#' character.
+ # +parser+::
+ # Parser for internal use [Gem::URI::DEFAULT_PARSER by default].
+ # +arg_check+::
+ # Check arguments [false by default].
+ #
+ # == Description
+ #
+ # Creates a new Gem::URI::Generic instance from ``generic'' components without check.
+ #
+ def initialize(scheme,
+ userinfo, host, port, registry,
+ path, opaque,
+ query,
+ fragment,
+ parser = DEFAULT_PARSER,
+ arg_check = false)
+ @scheme = nil
+ @user = nil
+ @password = nil
+ @host = nil
+ @port = nil
+ @path = nil
+ @query = nil
+ @opaque = nil
+ @fragment = nil
+ @parser = parser == DEFAULT_PARSER ? nil : parser
+
+ if arg_check
+ self.scheme = scheme
+ self.userinfo = userinfo
+ self.hostname = host
+ self.port = port
+ self.path = path
+ self.query = query
+ self.opaque = opaque
+ self.fragment = fragment
+ else
+ self.set_scheme(scheme)
+ self.set_userinfo(userinfo)
+ self.set_host(host)
+ self.set_port(port)
+ self.set_path(path)
+ self.query = query
+ self.set_opaque(opaque)
+ self.fragment=(fragment)
+ end
+ if registry
+ raise InvalidURIError,
+ "the scheme #{@scheme} does not accept registry part: #{registry} (or bad hostname?)"
+ end
+
+ @scheme&.freeze
+ self.set_path('') if !@path && !@opaque # (see RFC2396 Section 5.2)
+ self.set_port(self.default_port) if self.default_port && !@port
+ end
+
+ #
+ # Returns the scheme component of the Gem::URI.
+ #
+ # Gem::URI("http://foo/bar/baz").scheme #=> "http"
+ #
+ attr_reader :scheme
+
+ # Returns the host component of the Gem::URI.
+ #
+ # Gem::URI("http://foo/bar/baz").host #=> "foo"
+ #
+ # It returns nil if no host component exists.
+ #
+ # Gem::URI("mailto:foo@example.org").host #=> nil
+ #
+ # The component does not contain the port number.
+ #
+ # Gem::URI("http://foo:8080/bar/baz").host #=> "foo"
+ #
+ # Since IPv6 addresses are wrapped with brackets in URIs,
+ # this method returns IPv6 addresses wrapped with brackets.
+ # This form is not appropriate to pass to socket methods such as TCPSocket.open.
+ # If unwrapped host names are required, use the #hostname method.
+ #
+ # Gem::URI("http://[::1]/bar/baz").host #=> "[::1]"
+ # Gem::URI("http://[::1]/bar/baz").hostname #=> "::1"
+ #
+ attr_reader :host
+
+ # Returns the port component of the Gem::URI.
+ #
+ # Gem::URI("http://foo/bar/baz").port #=> 80
+ # Gem::URI("http://foo:8080/bar/baz").port #=> 8080
+ #
+ attr_reader :port
+
+ def registry # :nodoc:
+ nil
+ end
+
+ # Returns the path component of the Gem::URI.
+ #
+ # Gem::URI("http://foo/bar/baz").path #=> "/bar/baz"
+ #
+ attr_reader :path
+
+ # Returns the query component of the Gem::URI.
+ #
+ # Gem::URI("http://foo/bar/baz?search=FooBar").query #=> "search=FooBar"
+ #
+ attr_reader :query
+
+ # Returns the opaque part of the Gem::URI.
+ #
+ # Gem::URI("mailto:foo@example.org").opaque #=> "foo@example.org"
+ # Gem::URI("http://foo/bar/baz").opaque #=> nil
+ #
+ # The portion of the path that does not make use of the slash '/'.
+ # The path typically refers to an absolute path or an opaque part.
+ # (See RFC2396 Section 3 and 5.2.)
+ #
+ attr_reader :opaque
+
+ # Returns the fragment component of the Gem::URI.
+ #
+ # Gem::URI("http://foo/bar/baz?search=FooBar#ponies").fragment #=> "ponies"
+ #
+ attr_reader :fragment
+
+ # Returns the parser to be used.
+ #
+ # Unless a Gem::URI::Parser is defined, DEFAULT_PARSER is used.
+ #
+ def parser
+ if !defined?(@parser) || !@parser
+ DEFAULT_PARSER
+ else
+ @parser || DEFAULT_PARSER
+ end
+ end
+
+ # Replaces self by other Gem::URI object.
+ #
+ def replace!(oth)
+ if self.class != oth.class
+ raise ArgumentError, "expected #{self.class} object"
+ end
+
+ component.each do |c|
+ self.__send__("#{c}=", oth.__send__(c))
+ end
+ end
+ private :replace!
+
+ #
+ # Components of the Gem::URI in the order.
+ #
+ def component
+ self.class.component
+ end
+
+ #
+ # Checks the scheme +v+ component against the Gem::URI::Parser Regexp for :SCHEME.
+ #
+ def check_scheme(v)
+ if v && parser.regexp[:SCHEME] !~ v
+ raise InvalidComponentError,
+ "bad component(expected scheme component): #{v}"
+ end
+
+ return true
+ end
+ private :check_scheme
+
+ # Protected setter for the scheme component +v+.
+ #
+ # See also Gem::URI::Generic.scheme=.
+ #
+ def set_scheme(v)
+ @scheme = v&.downcase
+ end
+ protected :set_scheme
+
+ #
+ # == Args
+ #
+ # +v+::
+ # String
+ #
+ # == Description
+ #
+ # Public setter for the scheme component +v+
+ # (with validation).
+ #
+ # See also Gem::URI::Generic.check_scheme.
+ #
+ # == Usage
+ #
+ # require 'rubygems/vendor/uri/lib/uri'
+ #
+ # uri = Gem::URI.parse("http://my.example.com")
+ # uri.scheme = "https"
+ # uri.to_s #=> "https://my.example.com"
+ #
+ def scheme=(v)
+ check_scheme(v)
+ set_scheme(v)
+ v
+ end
+
+ #
+ # Checks the +user+ and +password+.
+ #
+ # If +password+ is not provided, then +user+ is
+ # split, using Gem::URI::Generic.split_userinfo, to
+ # pull +user+ and +password.
+ #
+ # See also Gem::URI::Generic.check_user, Gem::URI::Generic.check_password.
+ #
+ def check_userinfo(user, password = nil)
+ if !password
+ user, password = split_userinfo(user)
+ end
+ check_user(user)
+ check_password(password, user)
+
+ return true
+ end
+ private :check_userinfo
+
+ #
+ # Checks the user +v+ component for RFC2396 compliance
+ # and against the Gem::URI::Parser Regexp for :USERINFO.
+ #
+ # Can not have a registry or opaque component defined,
+ # with a user component defined.
+ #
+ def check_user(v)
+ if @opaque
+ raise InvalidURIError,
+ "can not set user with opaque"
+ end
+
+ return v unless v
+
+ if parser.regexp[:USERINFO] !~ v
+ raise InvalidComponentError,
+ "bad component(expected userinfo component or user component): #{v}"
+ end
+
+ return true
+ end
+ private :check_user
+
+ #
+ # Checks the password +v+ component for RFC2396 compliance
+ # and against the Gem::URI::Parser Regexp for :USERINFO.
+ #
+ # Can not have a registry or opaque component defined,
+ # with a user component defined.
+ #
+ def check_password(v, user = @user)
+ if @opaque
+ raise InvalidURIError,
+ "can not set password with opaque"
+ end
+ return v unless v
+
+ if !user
+ raise InvalidURIError,
+ "password component depends user component"
+ end
+
+ if parser.regexp[:USERINFO] !~ v
+ raise InvalidComponentError,
+ "bad password component"
+ end
+
+ return true
+ end
+ private :check_password
+
+ #
+ # Sets userinfo, argument is string like 'name:pass'.
+ #
+ def userinfo=(userinfo)
+ if userinfo.nil?
+ return nil
+ end
+ check_userinfo(*userinfo)
+ set_userinfo(*userinfo)
+ # returns userinfo
+ end
+
+ #
+ # == Args
+ #
+ # +v+::
+ # String
+ #
+ # == Description
+ #
+ # Public setter for the +user+ component
+ # (with validation).
+ #
+ # See also Gem::URI::Generic.check_user.
+ #
+ # == Usage
+ #
+ # require 'rubygems/vendor/uri/lib/uri'
+ #
+ # uri = Gem::URI.parse("http://john:S3nsit1ve@my.example.com")
+ # uri.user = "sam"
+ # uri.to_s #=> "http://sam:V3ry_S3nsit1ve@my.example.com"
+ #
+ def user=(user)
+ check_user(user)
+ set_user(user)
+ # returns user
+ end
+
+ #
+ # == Args
+ #
+ # +v+::
+ # String
+ #
+ # == Description
+ #
+ # Public setter for the +password+ component
+ # (with validation).
+ #
+ # See also Gem::URI::Generic.check_password.
+ #
+ # == Usage
+ #
+ # require 'rubygems/vendor/uri/lib/uri'
+ #
+ # uri = Gem::URI.parse("http://john:S3nsit1ve@my.example.com")
+ # uri.password = "V3ry_S3nsit1ve"
+ # uri.to_s #=> "http://john:V3ry_S3nsit1ve@my.example.com"
+ #
+ def password=(password)
+ check_password(password)
+ set_password(password)
+ # returns password
+ end
+
+ # Protected setter for the +user+ component, and +password+ if available
+ # (with validation).
+ #
+ # See also Gem::URI::Generic.userinfo=.
+ #
+ def set_userinfo(user, password = nil)
+ unless password
+ user, password = split_userinfo(user)
+ end
+ @user = user
+ @password = password if password
+
+ [@user, @password]
+ end
+ protected :set_userinfo
+
+ # Protected setter for the user component +v+.
+ #
+ # See also Gem::URI::Generic.user=.
+ #
+ def set_user(v)
+ set_userinfo(v, @password)
+ v
+ end
+ protected :set_user
+
+ # Protected setter for the password component +v+.
+ #
+ # See also Gem::URI::Generic.password=.
+ #
+ def set_password(v)
+ @password = v
+ # returns v
+ end
+ protected :set_password
+
+ # Returns the userinfo +ui+ as <code>[user, password]</code>
+ # if properly formatted as 'user:password'.
+ def split_userinfo(ui)
+ return nil, nil unless ui
+ user, password = ui.split(':', 2)
+
+ return user, password
+ end
+ private :split_userinfo
+
+ # Escapes 'user:password' +v+ based on RFC 1738 section 3.1.
+ def escape_userpass(v)
+ parser.escape(v, /[@:\/]/o) # RFC 1738 section 3.1 #/
+ end
+ private :escape_userpass
+
+ # Returns the userinfo, either as 'user' or 'user:password'.
+ def userinfo
+ if @user.nil?
+ nil
+ elsif @password.nil?
+ @user
+ else
+ @user + ':' + @password
+ end
+ end
+
+ # Returns the user component (without Gem::URI decoding).
+ def user
+ @user
+ end
+
+ # Returns the password component (without Gem::URI decoding).
+ def password
+ @password
+ end
+
+ # Returns the user component after Gem::URI decoding.
+ def decoded_user
+ Gem::URI.decode_uri_component(@user) if @user
+ end
+
+ # Returns the password component after Gem::URI decoding.
+ def decoded_password
+ Gem::URI.decode_uri_component(@password) if @password
+ end
+
+ #
+ # Checks the host +v+ component for RFC2396 compliance
+ # and against the Gem::URI::Parser Regexp for :HOST.
+ #
+ # Can not have a registry or opaque component defined,
+ # with a host component defined.
+ #
+ def check_host(v)
+ return v unless v
+
+ if @opaque
+ raise InvalidURIError,
+ "can not set host with registry or opaque"
+ elsif parser.regexp[:HOST] !~ v
+ raise InvalidComponentError,
+ "bad component(expected host component): #{v}"
+ end
+
+ return true
+ end
+ private :check_host
+
+ # Protected setter for the host component +v+.
+ #
+ # See also Gem::URI::Generic.host=.
+ #
+ def set_host(v)
+ @host = v
+ end
+ protected :set_host
+
+ #
+ # == Args
+ #
+ # +v+::
+ # String
+ #
+ # == Description
+ #
+ # Public setter for the host component +v+
+ # (with validation).
+ #
+ # See also Gem::URI::Generic.check_host.
+ #
+ # == Usage
+ #
+ # require 'rubygems/vendor/uri/lib/uri'
+ #
+ # uri = Gem::URI.parse("http://my.example.com")
+ # uri.host = "foo.com"
+ # uri.to_s #=> "http://foo.com"
+ #
+ def host=(v)
+ check_host(v)
+ set_host(v)
+ v
+ end
+
+ # Extract the host part of the Gem::URI and unwrap brackets for IPv6 addresses.
+ #
+ # This method is the same as Gem::URI::Generic#host except
+ # brackets for IPv6 (and future IP) addresses are removed.
+ #
+ # uri = Gem::URI("http://[::1]/bar")
+ # uri.hostname #=> "::1"
+ # uri.host #=> "[::1]"
+ #
+ def hostname
+ v = self.host
+ v&.start_with?('[') && v.end_with?(']') ? v[1..-2] : v
+ end
+
+ # Sets the host part of the Gem::URI as the argument with brackets for IPv6 addresses.
+ #
+ # This method is the same as Gem::URI::Generic#host= except
+ # the argument can be a bare IPv6 address.
+ #
+ # uri = Gem::URI("http://foo/bar")
+ # uri.hostname = "::1"
+ # uri.to_s #=> "http://[::1]/bar"
+ #
+ # If the argument seems to be an IPv6 address,
+ # it is wrapped with brackets.
+ #
+ def hostname=(v)
+ v = "[#{v}]" if !(v&.start_with?('[') && v&.end_with?(']')) && v&.index(':')
+ self.host = v
+ end
+
+ #
+ # Checks the port +v+ component for RFC2396 compliance
+ # and against the Gem::URI::Parser Regexp for :PORT.
+ #
+ # Can not have a registry or opaque component defined,
+ # with a port component defined.
+ #
+ def check_port(v)
+ return v unless v
+
+ if @opaque
+ raise InvalidURIError,
+ "can not set port with registry or opaque"
+ elsif !v.kind_of?(Integer) && parser.regexp[:PORT] !~ v
+ raise InvalidComponentError,
+ "bad component(expected port component): #{v.inspect}"
+ end
+
+ return true
+ end
+ private :check_port
+
+ # Protected setter for the port component +v+.
+ #
+ # See also Gem::URI::Generic.port=.
+ #
+ def set_port(v)
+ v = v.empty? ? nil : v.to_i unless !v || v.kind_of?(Integer)
+ @port = v
+ end
+ protected :set_port
+
+ #
+ # == Args
+ #
+ # +v+::
+ # String
+ #
+ # == Description
+ #
+ # Public setter for the port component +v+
+ # (with validation).
+ #
+ # See also Gem::URI::Generic.check_port.
+ #
+ # == Usage
+ #
+ # require 'rubygems/vendor/uri/lib/uri'
+ #
+ # uri = Gem::URI.parse("http://my.example.com")
+ # uri.port = 8080
+ # uri.to_s #=> "http://my.example.com:8080"
+ #
+ def port=(v)
+ check_port(v)
+ set_port(v)
+ port
+ end
+
+ def check_registry(v) # :nodoc:
+ raise InvalidURIError, "can not set registry"
+ end
+ private :check_registry
+
+ def set_registry(v) #:nodoc:
+ raise InvalidURIError, "can not set registry"
+ end
+ protected :set_registry
+
+ def registry=(v)
+ raise InvalidURIError, "can not set registry"
+ end
+
+ #
+ # Checks the path +v+ component for RFC2396 compliance
+ # and against the Gem::URI::Parser Regexp
+ # for :ABS_PATH and :REL_PATH.
+ #
+ # Can not have a opaque component defined,
+ # with a path component defined.
+ #
+ def check_path(v)
+ # raise if both hier and opaque are not nil, because:
+ # absoluteURI = scheme ":" ( hier_part | opaque_part )
+ # hier_part = ( net_path | abs_path ) [ "?" query ]
+ if v && @opaque
+ raise InvalidURIError,
+ "path conflicts with opaque"
+ end
+
+ # If scheme is ftp, path may be relative.
+ # See RFC 1738 section 3.2.2, and RFC 2396.
+ if @scheme && @scheme != "ftp"
+ if v && v != '' && parser.regexp[:ABS_PATH] !~ v
+ raise InvalidComponentError,
+ "bad component(expected absolute path component): #{v}"
+ end
+ else
+ if v && v != '' && parser.regexp[:ABS_PATH] !~ v &&
+ parser.regexp[:REL_PATH] !~ v
+ raise InvalidComponentError,
+ "bad component(expected relative path component): #{v}"
+ end
+ end
+
+ return true
+ end
+ private :check_path
+
+ # Protected setter for the path component +v+.
+ #
+ # See also Gem::URI::Generic.path=.
+ #
+ def set_path(v)
+ @path = v
+ end
+ protected :set_path
+
+ #
+ # == Args
+ #
+ # +v+::
+ # String
+ #
+ # == Description
+ #
+ # Public setter for the path component +v+
+ # (with validation).
+ #
+ # See also Gem::URI::Generic.check_path.
+ #
+ # == Usage
+ #
+ # require 'rubygems/vendor/uri/lib/uri'
+ #
+ # uri = Gem::URI.parse("http://my.example.com/pub/files")
+ # uri.path = "/faq/"
+ # uri.to_s #=> "http://my.example.com/faq/"
+ #
+ def path=(v)
+ check_path(v)
+ set_path(v)
+ v
+ end
+
+ #
+ # == Args
+ #
+ # +v+::
+ # String
+ #
+ # == Description
+ #
+ # Public setter for the query component +v+.
+ #
+ # == Usage
+ #
+ # require 'rubygems/vendor/uri/lib/uri'
+ #
+ # uri = Gem::URI.parse("http://my.example.com/?id=25")
+ # uri.query = "id=1"
+ # uri.to_s #=> "http://my.example.com/?id=1"
+ #
+ def query=(v)
+ return @query = nil unless v
+ raise InvalidURIError, "query conflicts with opaque" if @opaque
+
+ x = v.to_str
+ v = x.dup if x.equal? v
+ v.encode!(Encoding::UTF_8) rescue nil
+ v.delete!("\t\r\n")
+ v.force_encoding(Encoding::ASCII_8BIT)
+ raise InvalidURIError, "invalid percent escape: #{$1}" if /(%\H\H)/n.match(v)
+ v.gsub!(/(?!%\h\h|[!$-&(-;=?-_a-~])./n.freeze){'%%%02X' % $&.ord}
+ v.force_encoding(Encoding::US_ASCII)
+ @query = v
+ end
+
+ #
+ # Checks the opaque +v+ component for RFC2396 compliance and
+ # against the Gem::URI::Parser Regexp for :OPAQUE.
+ #
+ # Can not have a host, port, user, or path component defined,
+ # with an opaque component defined.
+ #
+ def check_opaque(v)
+ return v unless v
+
+ # raise if both hier and opaque are not nil, because:
+ # absoluteURI = scheme ":" ( hier_part | opaque_part )
+ # hier_part = ( net_path | abs_path ) [ "?" query ]
+ if @host || @port || @user || @path # userinfo = @user + ':' + @password
+ raise InvalidURIError,
+ "can not set opaque with host, port, userinfo or path"
+ elsif v && parser.regexp[:OPAQUE] !~ v
+ raise InvalidComponentError,
+ "bad component(expected opaque component): #{v}"
+ end
+
+ return true
+ end
+ private :check_opaque
+
+ # Protected setter for the opaque component +v+.
+ #
+ # See also Gem::URI::Generic.opaque=.
+ #
+ def set_opaque(v)
+ @opaque = v
+ end
+ protected :set_opaque
+
+ #
+ # == Args
+ #
+ # +v+::
+ # String
+ #
+ # == Description
+ #
+ # Public setter for the opaque component +v+
+ # (with validation).
+ #
+ # See also Gem::URI::Generic.check_opaque.
+ #
+ def opaque=(v)
+ check_opaque(v)
+ set_opaque(v)
+ v
+ end
+
+ #
+ # Checks the fragment +v+ component against the Gem::URI::Parser Regexp for :FRAGMENT.
+ #
+ #
+ # == Args
+ #
+ # +v+::
+ # String
+ #
+ # == Description
+ #
+ # Public setter for the fragment component +v+
+ # (with validation).
+ #
+ # == Usage
+ #
+ # require 'rubygems/vendor/uri/lib/uri'
+ #
+ # uri = Gem::URI.parse("http://my.example.com/?id=25#time=1305212049")
+ # uri.fragment = "time=1305212086"
+ # uri.to_s #=> "http://my.example.com/?id=25#time=1305212086"
+ #
+ def fragment=(v)
+ return @fragment = nil unless v
+
+ x = v.to_str
+ v = x.dup if x.equal? v
+ v.encode!(Encoding::UTF_8) rescue nil
+ v.delete!("\t\r\n")
+ v.force_encoding(Encoding::ASCII_8BIT)
+ v.gsub!(/(?!%\h\h|[!-~])./n){'%%%02X' % $&.ord}
+ v.force_encoding(Encoding::US_ASCII)
+ @fragment = v
+ end
+
+ #
+ # Returns true if Gem::URI is hierarchical.
+ #
+ # == Description
+ #
+ # Gem::URI has components listed in order of decreasing significance from left to right,
+ # see RFC3986 https://tools.ietf.org/html/rfc3986 1.2.3.
+ #
+ # == Usage
+ #
+ # require 'rubygems/vendor/uri/lib/uri'
+ #
+ # uri = Gem::URI.parse("http://my.example.com/")
+ # uri.hierarchical?
+ # #=> true
+ # uri = Gem::URI.parse("mailto:joe@example.com")
+ # uri.hierarchical?
+ # #=> false
+ #
+ def hierarchical?
+ if @path
+ true
+ else
+ false
+ end
+ end
+
+ #
+ # Returns true if Gem::URI has a scheme (e.g. http:// or https://) specified.
+ #
+ def absolute?
+ if @scheme
+ true
+ else
+ false
+ end
+ end
+ alias absolute absolute?
+
+ #
+ # Returns true if Gem::URI does not have a scheme (e.g. http:// or https://) specified.
+ #
+ def relative?
+ !absolute?
+ end
+
+ #
+ # Returns an Array of the path split on '/'.
+ #
+ def split_path(path)
+ path.split("/", -1)
+ end
+ private :split_path
+
+ #
+ # Merges a base path +base+, with relative path +rel+,
+ # returns a modified base path.
+ #
+ def merge_path(base, rel)
+
+ # RFC2396, Section 5.2, 5)
+ # RFC2396, Section 5.2, 6)
+ base_path = split_path(base)
+ rel_path = split_path(rel)
+
+ # RFC2396, Section 5.2, 6), a)
+ base_path << '' if base_path.last == '..'
+ while i = base_path.index('..')
+ base_path.slice!(i - 1, 2)
+ end
+
+ if (first = rel_path.first) and first.empty?
+ base_path.clear
+ rel_path.shift
+ end
+
+ # RFC2396, Section 5.2, 6), c)
+ # RFC2396, Section 5.2, 6), d)
+ rel_path.push('') if rel_path.last == '.' || rel_path.last == '..'
+ rel_path.delete('.')
+
+ # RFC2396, Section 5.2, 6), e)
+ tmp = []
+ rel_path.each do |x|
+ if x == '..' &&
+ !(tmp.empty? || tmp.last == '..')
+ tmp.pop
+ else
+ tmp << x
+ end
+ end
+
+ add_trailer_slash = !tmp.empty?
+ if base_path.empty?
+ base_path = [''] # keep '/' for root directory
+ elsif add_trailer_slash
+ base_path.pop
+ end
+ while x = tmp.shift
+ if x == '..'
+ # RFC2396, Section 4
+ # a .. or . in an absolute path has no special meaning
+ base_path.pop if base_path.size > 1
+ else
+ # if x == '..'
+ # valid absolute (but abnormal) path "/../..."
+ # else
+ # valid absolute path
+ # end
+ base_path << x
+ tmp.each {|t| base_path << t}
+ add_trailer_slash = false
+ break
+ end
+ end
+ base_path.push('') if add_trailer_slash
+
+ return base_path.join('/')
+ end
+ private :merge_path
+
+ #
+ # == Args
+ #
+ # +oth+::
+ # Gem::URI or String
+ #
+ # == Description
+ #
+ # Destructive form of #merge.
+ #
+ # == Usage
+ #
+ # require 'rubygems/vendor/uri/lib/uri'
+ #
+ # uri = Gem::URI.parse("http://my.example.com")
+ # uri.merge!("/main.rbx?page=1")
+ # uri.to_s # => "http://my.example.com/main.rbx?page=1"
+ #
+ def merge!(oth)
+ t = merge(oth)
+ if self == t
+ nil
+ else
+ replace!(t)
+ self
+ end
+ end
+
+ #
+ # == Args
+ #
+ # +oth+::
+ # Gem::URI or String
+ #
+ # == Description
+ #
+ # Merges two URIs.
+ #
+ # == Usage
+ #
+ # require 'rubygems/vendor/uri/lib/uri'
+ #
+ # uri = Gem::URI.parse("http://my.example.com")
+ # uri.merge("/main.rbx?page=1")
+ # # => "http://my.example.com/main.rbx?page=1"
+ #
+ def merge(oth)
+ rel = parser.__send__(:convert_to_uri, oth)
+
+ if rel.absolute?
+ #raise BadURIError, "both Gem::URI are absolute" if absolute?
+ # hmm... should return oth for usability?
+ return rel
+ end
+
+ unless self.absolute?
+ raise BadURIError, "both Gem::URI are relative"
+ end
+
+ base = self.dup
+
+ authority = rel.userinfo || rel.host || rel.port
+
+ # RFC2396, Section 5.2, 2)
+ if (rel.path.nil? || rel.path.empty?) && !authority && !rel.query
+ base.fragment=(rel.fragment) if rel.fragment
+ return base
+ end
+
+ base.query = nil
+ base.fragment=(nil)
+
+ # RFC2396, Section 5.2, 4)
+ if !authority
+ base.set_path(merge_path(base.path, rel.path)) if base.path && rel.path
+ else
+ # RFC2396, Section 5.2, 4)
+ base.set_path(rel.path) if rel.path
+ end
+
+ # RFC2396, Section 5.2, 7)
+ base.set_userinfo(rel.userinfo) if rel.userinfo
+ base.set_host(rel.host) if rel.host
+ base.set_port(rel.port) if rel.port
+ base.query = rel.query if rel.query
+ base.fragment=(rel.fragment) if rel.fragment
+
+ return base
+ end # merge
+ alias + merge
+
+ # :stopdoc:
+ def route_from_path(src, dst)
+ case dst
+ when src
+ # RFC2396, Section 4.2
+ return ''
+ when %r{(?:\A|/)\.\.?(?:/|\z)}
+ # dst has abnormal absolute path,
+ # like "/./", "/../", "/x/../", ...
+ return dst.dup
+ end
+
+ src_path = src.scan(%r{[^/]*/})
+ dst_path = dst.scan(%r{[^/]*/?})
+
+ # discard same parts
+ while !dst_path.empty? && dst_path.first == src_path.first
+ src_path.shift
+ dst_path.shift
+ end
+
+ tmp = dst_path.join
+
+ # calculate
+ if src_path.empty?
+ if tmp.empty?
+ return './'
+ elsif dst_path.first.include?(':') # (see RFC2396 Section 5)
+ return './' + tmp
+ else
+ return tmp
+ end
+ end
+
+ return '../' * src_path.size + tmp
+ end
+ private :route_from_path
+ # :startdoc:
+
+ # :stopdoc:
+ def route_from0(oth)
+ oth = parser.__send__(:convert_to_uri, oth)
+ if self.relative?
+ raise BadURIError,
+ "relative Gem::URI: #{self}"
+ end
+ if oth.relative?
+ raise BadURIError,
+ "relative Gem::URI: #{oth}"
+ end
+
+ if self.scheme != oth.scheme
+ return self, self.dup
+ end
+ rel = Gem::URI::Generic.new(nil, # it is relative Gem::URI
+ self.userinfo, self.host, self.port,
+ nil, self.path, self.opaque,
+ self.query, self.fragment, parser)
+
+ if rel.userinfo != oth.userinfo ||
+ rel.host.to_s.downcase != oth.host.to_s.downcase ||
+ rel.port != oth.port
+
+ if self.userinfo.nil? && self.host.nil?
+ return self, self.dup
+ end
+
+ rel.set_port(nil) if rel.port == oth.default_port
+ return rel, rel
+ end
+ rel.set_userinfo(nil)
+ rel.set_host(nil)
+ rel.set_port(nil)
+
+ if rel.path && rel.path == oth.path
+ rel.set_path('')
+ rel.query = nil if rel.query == oth.query
+ return rel, rel
+ elsif rel.opaque && rel.opaque == oth.opaque
+ rel.set_opaque('')
+ rel.query = nil if rel.query == oth.query
+ return rel, rel
+ end
+
+ # you can modify `rel', but can not `oth'.
+ return oth, rel
+ end
+ private :route_from0
+ # :startdoc:
+
+ #
+ # == Args
+ #
+ # +oth+::
+ # Gem::URI or String
+ #
+ # == Description
+ #
+ # Calculates relative path from oth to self.
+ #
+ # == Usage
+ #
+ # require 'rubygems/vendor/uri/lib/uri'
+ #
+ # uri = Gem::URI.parse('http://my.example.com/main.rbx?page=1')
+ # uri.route_from('http://my.example.com')
+ # #=> #<Gem::URI::Generic /main.rbx?page=1>
+ #
+ def route_from(oth)
+ # you can modify `rel', but can not `oth'.
+ begin
+ oth, rel = route_from0(oth)
+ rescue
+ raise $!.class, $!.message
+ end
+ if oth == rel
+ return rel
+ end
+
+ rel.set_path(route_from_path(oth.path, self.path))
+ if rel.path == './' && self.query
+ # "./?foo" -> "?foo"
+ rel.set_path('')
+ end
+
+ return rel
+ end
+
+ alias - route_from
+
+ #
+ # == Args
+ #
+ # +oth+::
+ # Gem::URI or String
+ #
+ # == Description
+ #
+ # Calculates relative path to oth from self.
+ #
+ # == Usage
+ #
+ # require 'rubygems/vendor/uri/lib/uri'
+ #
+ # uri = Gem::URI.parse('http://my.example.com')
+ # uri.route_to('http://my.example.com/main.rbx?page=1')
+ # #=> #<Gem::URI::Generic /main.rbx?page=1>
+ #
+ def route_to(oth)
+ parser.__send__(:convert_to_uri, oth).route_from(self)
+ end
+
+ #
+ # Returns normalized Gem::URI.
+ #
+ # require 'rubygems/vendor/uri/lib/uri'
+ #
+ # Gem::URI("HTTP://my.EXAMPLE.com").normalize
+ # #=> #<Gem::URI::HTTP http://my.example.com/>
+ #
+ # Normalization here means:
+ #
+ # * scheme and host are converted to lowercase,
+ # * an empty path component is set to "/".
+ #
+ def normalize
+ uri = dup
+ uri.normalize!
+ uri
+ end
+
+ #
+ # Destructive version of #normalize.
+ #
+ def normalize!
+ if path&.empty?
+ set_path('/')
+ end
+ if scheme && scheme != scheme.downcase
+ set_scheme(self.scheme.downcase)
+ end
+ if host && host != host.downcase
+ set_host(self.host.downcase)
+ end
+ end
+
+ #
+ # Constructs String from Gem::URI.
+ #
+ def to_s
+ str = ''.dup
+ if @scheme
+ str << @scheme
+ str << ':'
+ end
+
+ if @opaque
+ str << @opaque
+ else
+ if @host || %w[file postgres].include?(@scheme)
+ str << '//'
+ end
+ if self.userinfo
+ str << self.userinfo
+ str << '@'
+ end
+ if @host
+ str << @host
+ end
+ if @port && @port != self.default_port
+ str << ':'
+ str << @port.to_s
+ end
+ str << @path
+ if @query
+ str << '?'
+ str << @query
+ end
+ end
+ if @fragment
+ str << '#'
+ str << @fragment
+ end
+ str
+ end
+ alias to_str to_s
+
+ #
+ # Compares two URIs.
+ #
+ def ==(oth)
+ if self.class == oth.class
+ self.normalize.component_ary == oth.normalize.component_ary
+ else
+ false
+ end
+ end
+
+ def hash
+ self.component_ary.hash
+ end
+
+ def eql?(oth)
+ self.class == oth.class &&
+ parser == oth.parser &&
+ self.component_ary.eql?(oth.component_ary)
+ end
+
+=begin
+
+--- Gem::URI::Generic#===(oth)
+
+=end
+# def ===(oth)
+# raise NotImplementedError
+# end
+
+=begin
+=end
+
+
+ # Returns an Array of the components defined from the COMPONENT Array.
+ def component_ary
+ component.collect do |x|
+ self.__send__(x)
+ end
+ end
+ protected :component_ary
+
+ # == Args
+ #
+ # +components+::
+ # Multiple Symbol arguments defined in Gem::URI::HTTP.
+ #
+ # == Description
+ #
+ # Selects specified components from Gem::URI.
+ #
+ # == Usage
+ #
+ # require 'rubygems/vendor/uri/lib/uri'
+ #
+ # uri = Gem::URI.parse('http://myuser:mypass@my.example.com/test.rbx')
+ # uri.select(:userinfo, :host, :path)
+ # # => ["myuser:mypass", "my.example.com", "/test.rbx"]
+ #
+ def select(*components)
+ components.collect do |c|
+ if component.include?(c)
+ self.__send__(c)
+ else
+ raise ArgumentError,
+ "expected of components of #{self.class} (#{self.class.component.join(', ')})"
+ end
+ end
+ end
+
+ def inspect
+ "#<#{self.class} #{self}>"
+ end
+
+ #
+ # == Args
+ #
+ # +v+::
+ # Gem::URI or String
+ #
+ # == Description
+ #
+ # Attempts to parse other Gem::URI +oth+,
+ # returns [parsed_oth, self].
+ #
+ # == Usage
+ #
+ # require 'rubygems/vendor/uri/lib/uri'
+ #
+ # uri = Gem::URI.parse("http://my.example.com")
+ # uri.coerce("http://foo.com")
+ # #=> [#<Gem::URI::HTTP http://foo.com>, #<Gem::URI::HTTP http://my.example.com>]
+ #
+ def coerce(oth)
+ case oth
+ when String
+ oth = parser.parse(oth)
+ else
+ super
+ end
+
+ return oth, self
+ end
+
+ # Returns a proxy Gem::URI.
+ # The proxy Gem::URI is obtained from environment variables such as http_proxy,
+ # ftp_proxy, no_proxy, etc.
+ # If there is no proper proxy, nil is returned.
+ #
+ # If the optional parameter +env+ is specified, it is used instead of ENV.
+ #
+ # Note that capitalized variables (HTTP_PROXY, FTP_PROXY, NO_PROXY, etc.)
+ # are examined, too.
+ #
+ # But http_proxy and HTTP_PROXY is treated specially under CGI environment.
+ # It's because HTTP_PROXY may be set by Proxy: header.
+ # So HTTP_PROXY is not used.
+ # http_proxy is not used too if the variable is case insensitive.
+ # CGI_HTTP_PROXY can be used instead.
+ def find_proxy(env=ENV)
+ raise BadURIError, "relative Gem::URI: #{self}" if self.relative?
+ name = self.scheme.downcase + '_proxy'
+ proxy_uri = nil
+ if name == 'http_proxy' && env.include?('REQUEST_METHOD') # CGI?
+ # HTTP_PROXY conflicts with *_proxy for proxy settings and
+ # HTTP_* for header information in CGI.
+ # So it should be careful to use it.
+ pairs = env.reject {|k, v| /\Ahttp_proxy\z/i !~ k }
+ case pairs.length
+ when 0 # no proxy setting anyway.
+ proxy_uri = nil
+ when 1
+ k, _ = pairs.shift
+ if k == 'http_proxy' && env[k.upcase] == nil
+ # http_proxy is safe to use because ENV is case sensitive.
+ proxy_uri = env[name]
+ else
+ proxy_uri = nil
+ end
+ else # http_proxy is safe to use because ENV is case sensitive.
+ proxy_uri = env.to_hash[name]
+ end
+ if !proxy_uri
+ # Use CGI_HTTP_PROXY. cf. libwww-perl.
+ proxy_uri = env["CGI_#{name.upcase}"]
+ end
+ elsif name == 'http_proxy'
+ if RUBY_ENGINE == 'jruby' && p_addr = ENV_JAVA['http.proxyHost']
+ p_port = ENV_JAVA['http.proxyPort']
+ if p_user = ENV_JAVA['http.proxyUser']
+ p_pass = ENV_JAVA['http.proxyPass']
+ proxy_uri = "http://#{p_user}:#{p_pass}@#{p_addr}:#{p_port}"
+ else
+ proxy_uri = "http://#{p_addr}:#{p_port}"
+ end
+ else
+ unless proxy_uri = env[name]
+ if proxy_uri = env[name.upcase]
+ warn 'The environment variable HTTP_PROXY is discouraged. Use http_proxy.', uplevel: 1
+ end
+ end
+ end
+ else
+ proxy_uri = env[name] || env[name.upcase]
+ end
+
+ if proxy_uri.nil? || proxy_uri.empty?
+ return nil
+ end
+
+ if self.hostname
+ begin
+ addr = IPSocket.getaddress(self.hostname)
+ return nil if /\A127\.|\A::1\z/ =~ addr
+ rescue SocketError
+ end
+ end
+
+ name = 'no_proxy'
+ if no_proxy = env[name] || env[name.upcase]
+ return nil unless Gem::URI::Generic.use_proxy?(self.hostname, addr, self.port, no_proxy)
+ end
+ Gem::URI.parse(proxy_uri)
+ end
+
+ def self.use_proxy?(hostname, addr, port, no_proxy) # :nodoc:
+ hostname = hostname.downcase
+ dothostname = ".#{hostname}"
+ no_proxy.scan(/([^:,\s]+)(?::(\d+))?/) {|p_host, p_port|
+ if !p_port || port == p_port.to_i
+ if p_host.start_with?('.')
+ return false if hostname.end_with?(p_host.downcase)
+ else
+ return false if dothostname.end_with?(".#{p_host.downcase}")
+ end
+ if addr
+ begin
+ return false if IPAddr.new(p_host).include?(addr)
+ rescue IPAddr::InvalidAddressError
+ next
+ end
+ end
+ end
+ }
+ true
+ end
+ end
+end
diff --git a/lib/rubygems/vendor/uri/lib/uri/http.rb b/lib/rubygems/vendor/uri/lib/uri/http.rb
new file mode 100644
index 0000000000..bef43490a3
--- /dev/null
+++ b/lib/rubygems/vendor/uri/lib/uri/http.rb
@@ -0,0 +1,125 @@
+# frozen_string_literal: false
+# = uri/http.rb
+#
+# Author:: Akira Yamada <akira@ruby-lang.org>
+# License:: You can redistribute it and/or modify it under the same term as Ruby.
+#
+# See Gem::URI for general documentation
+#
+
+require_relative 'generic'
+
+module Gem::URI
+
+ #
+ # The syntax of HTTP URIs is defined in RFC1738 section 3.3.
+ #
+ # Note that the Ruby Gem::URI library allows HTTP URLs containing usernames and
+ # passwords. This is not legal as per the RFC, but used to be
+ # supported in Internet Explorer 5 and 6, before the MS04-004 security
+ # update. See <URL:http://support.microsoft.com/kb/834489>.
+ #
+ class HTTP < Generic
+ # A Default port of 80 for Gem::URI::HTTP.
+ DEFAULT_PORT = 80
+
+ # An Array of the available components for Gem::URI::HTTP.
+ COMPONENT = %i[
+ scheme
+ userinfo host port
+ path
+ query
+ fragment
+ ].freeze
+
+ #
+ # == Description
+ #
+ # Creates a new Gem::URI::HTTP object from components, with syntax checking.
+ #
+ # The components accepted are userinfo, host, port, path, query, and
+ # fragment.
+ #
+ # The components should be provided either as an Array, or as a Hash
+ # with keys formed by preceding the component names with a colon.
+ #
+ # If an Array is used, the components must be passed in the
+ # order <code>[userinfo, host, port, path, query, fragment]</code>.
+ #
+ # Example:
+ #
+ # uri = Gem::URI::HTTP.build(host: 'www.example.com', path: '/foo/bar')
+ #
+ # uri = Gem::URI::HTTP.build([nil, "www.example.com", nil, "/path",
+ # "query", 'fragment'])
+ #
+ # Currently, if passed userinfo components this method generates
+ # invalid HTTP URIs as per RFC 1738.
+ #
+ def self.build(args)
+ tmp = Util.make_components_hash(self, args)
+ super(tmp)
+ end
+
+ #
+ # == Description
+ #
+ # Returns the full path for an HTTP request, as required by Net::HTTP::Get.
+ #
+ # If the Gem::URI contains a query, the full path is Gem::URI#path + '?' + Gem::URI#query.
+ # Otherwise, the path is simply Gem::URI#path.
+ #
+ # Example:
+ #
+ # uri = Gem::URI::HTTP.build(path: '/foo/bar', query: 'test=true')
+ # uri.request_uri # => "/foo/bar?test=true"
+ #
+ def request_uri
+ return unless @path
+
+ url = @query ? "#@path?#@query" : @path.dup
+ url.start_with?(?/.freeze) ? url : ?/ + url
+ end
+
+ #
+ # == Description
+ #
+ # Returns the authority for an HTTP uri, as defined in
+ # https://datatracker.ietf.org/doc/html/rfc3986/#section-3.2.
+ #
+ #
+ # Example:
+ #
+ # Gem::URI::HTTP.build(host: 'www.example.com', path: '/foo/bar').authority #=> "www.example.com"
+ # Gem::URI::HTTP.build(host: 'www.example.com', port: 8000, path: '/foo/bar').authority #=> "www.example.com:8000"
+ # Gem::URI::HTTP.build(host: 'www.example.com', port: 80, path: '/foo/bar').authority #=> "www.example.com"
+ #
+ def authority
+ if port == default_port
+ host
+ else
+ "#{host}:#{port}"
+ end
+ end
+
+ #
+ # == Description
+ #
+ # Returns the origin for an HTTP uri, as defined in
+ # https://datatracker.ietf.org/doc/html/rfc6454.
+ #
+ #
+ # Example:
+ #
+ # Gem::URI::HTTP.build(host: 'www.example.com', path: '/foo/bar').origin #=> "http://www.example.com"
+ # Gem::URI::HTTP.build(host: 'www.example.com', port: 8000, path: '/foo/bar').origin #=> "http://www.example.com:8000"
+ # Gem::URI::HTTP.build(host: 'www.example.com', port: 80, path: '/foo/bar').origin #=> "http://www.example.com"
+ # Gem::URI::HTTPS.build(host: 'www.example.com', path: '/foo/bar').origin #=> "https://www.example.com"
+ #
+ def origin
+ "#{scheme}://#{authority}"
+ end
+ end
+
+ register_scheme 'HTTP', HTTP
+end
diff --git a/lib/rubygems/vendor/uri/lib/uri/https.rb b/lib/rubygems/vendor/uri/lib/uri/https.rb
new file mode 100644
index 0000000000..6e8e732e1d
--- /dev/null
+++ b/lib/rubygems/vendor/uri/lib/uri/https.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: false
+# = uri/https.rb
+#
+# Author:: Akira Yamada <akira@ruby-lang.org>
+# License:: You can redistribute it and/or modify it under the same term as Ruby.
+#
+# See Gem::URI for general documentation
+#
+
+require_relative 'http'
+
+module Gem::URI
+
+ # The default port for HTTPS URIs is 443, and the scheme is 'https:' rather
+ # than 'http:'. Other than that, HTTPS URIs are identical to HTTP URIs;
+ # see Gem::URI::HTTP.
+ class HTTPS < HTTP
+ # A Default port of 443 for Gem::URI::HTTPS
+ DEFAULT_PORT = 443
+ end
+
+ register_scheme 'HTTPS', HTTPS
+end
diff --git a/lib/rubygems/vendor/uri/lib/uri/ldap.rb b/lib/rubygems/vendor/uri/lib/uri/ldap.rb
new file mode 100644
index 0000000000..1a08b5ab7e
--- /dev/null
+++ b/lib/rubygems/vendor/uri/lib/uri/ldap.rb
@@ -0,0 +1,261 @@
+# frozen_string_literal: false
+# = uri/ldap.rb
+#
+# Author::
+# Takaaki Tateishi <ttate@jaist.ac.jp>
+# Akira Yamada <akira@ruby-lang.org>
+# License::
+# Gem::URI::LDAP is copyrighted free software by Takaaki Tateishi and Akira Yamada.
+# You can redistribute it and/or modify it under the same term as Ruby.
+#
+# See Gem::URI for general documentation
+#
+
+require_relative 'generic'
+
+module Gem::URI
+
+ #
+ # LDAP Gem::URI SCHEMA (described in RFC2255).
+ #--
+ # ldap://<host>/<dn>[?<attrs>[?<scope>[?<filter>[?<extensions>]]]]
+ #++
+ class LDAP < Generic
+
+ # A Default port of 389 for Gem::URI::LDAP.
+ DEFAULT_PORT = 389
+
+ # An Array of the available components for Gem::URI::LDAP.
+ COMPONENT = [
+ :scheme,
+ :host, :port,
+ :dn,
+ :attributes,
+ :scope,
+ :filter,
+ :extensions,
+ ].freeze
+
+ # Scopes available for the starting point.
+ #
+ # * SCOPE_BASE - the Base DN
+ # * SCOPE_ONE - one level under the Base DN, not including the base DN and
+ # not including any entries under this
+ # * SCOPE_SUB - subtrees, all entries at all levels
+ #
+ SCOPE = [
+ SCOPE_ONE = 'one',
+ SCOPE_SUB = 'sub',
+ SCOPE_BASE = 'base',
+ ].freeze
+
+ #
+ # == Description
+ #
+ # Creates a new Gem::URI::LDAP object from components, with syntax checking.
+ #
+ # The components accepted are host, port, dn, attributes,
+ # scope, filter, and extensions.
+ #
+ # The components should be provided either as an Array, or as a Hash
+ # with keys formed by preceding the component names with a colon.
+ #
+ # If an Array is used, the components must be passed in the
+ # order <code>[host, port, dn, attributes, scope, filter, extensions]</code>.
+ #
+ # Example:
+ #
+ # uri = Gem::URI::LDAP.build({:host => 'ldap.example.com',
+ # :dn => '/dc=example'})
+ #
+ # uri = Gem::URI::LDAP.build(["ldap.example.com", nil,
+ # "/dc=example;dc=com", "query", nil, nil, nil])
+ #
+ def self.build(args)
+ tmp = Util::make_components_hash(self, args)
+
+ if tmp[:dn]
+ tmp[:path] = tmp[:dn]
+ end
+
+ query = []
+ [:extensions, :filter, :scope, :attributes].collect do |x|
+ next if !tmp[x] && query.size == 0
+ query.unshift(tmp[x])
+ end
+
+ tmp[:query] = query.join('?')
+
+ return super(tmp)
+ end
+
+ #
+ # == Description
+ #
+ # Creates a new Gem::URI::LDAP object from generic Gem::URI components as per
+ # RFC 2396. No LDAP-specific syntax checking is performed.
+ #
+ # Arguments are +scheme+, +userinfo+, +host+, +port+, +registry+, +path+,
+ # +opaque+, +query+, and +fragment+, in that order.
+ #
+ # Example:
+ #
+ # uri = Gem::URI::LDAP.new("ldap", nil, "ldap.example.com", nil, nil,
+ # "/dc=example;dc=com", nil, "query", nil)
+ #
+ # See also Gem::URI::Generic.new.
+ #
+ def initialize(*arg)
+ super(*arg)
+
+ if @fragment
+ raise InvalidURIError, 'bad LDAP URL'
+ end
+
+ parse_dn
+ parse_query
+ end
+
+ # Private method to cleanup +dn+ from using the +path+ component attribute.
+ def parse_dn
+ raise InvalidURIError, 'bad LDAP URL' unless @path
+ @dn = @path[1..-1]
+ end
+ private :parse_dn
+
+ # Private method to cleanup +attributes+, +scope+, +filter+, and +extensions+
+ # from using the +query+ component attribute.
+ def parse_query
+ @attributes = nil
+ @scope = nil
+ @filter = nil
+ @extensions = nil
+
+ if @query
+ attrs, scope, filter, extensions = @query.split('?')
+
+ @attributes = attrs if attrs && attrs.size > 0
+ @scope = scope if scope && scope.size > 0
+ @filter = filter if filter && filter.size > 0
+ @extensions = extensions if extensions && extensions.size > 0
+ end
+ end
+ private :parse_query
+
+ # Private method to assemble +query+ from +attributes+, +scope+, +filter+, and +extensions+.
+ def build_path_query
+ @path = '/' + @dn
+
+ query = []
+ [@extensions, @filter, @scope, @attributes].each do |x|
+ next if !x && query.size == 0
+ query.unshift(x)
+ end
+ @query = query.join('?')
+ end
+ private :build_path_query
+
+ # Returns dn.
+ def dn
+ @dn
+ end
+
+ # Private setter for dn +val+.
+ def set_dn(val)
+ @dn = val
+ build_path_query
+ @dn
+ end
+ protected :set_dn
+
+ # Setter for dn +val+.
+ def dn=(val)
+ set_dn(val)
+ val
+ end
+
+ # Returns attributes.
+ def attributes
+ @attributes
+ end
+
+ # Private setter for attributes +val+.
+ def set_attributes(val)
+ @attributes = val
+ build_path_query
+ @attributes
+ end
+ protected :set_attributes
+
+ # Setter for attributes +val+.
+ def attributes=(val)
+ set_attributes(val)
+ val
+ end
+
+ # Returns scope.
+ def scope
+ @scope
+ end
+
+ # Private setter for scope +val+.
+ def set_scope(val)
+ @scope = val
+ build_path_query
+ @scope
+ end
+ protected :set_scope
+
+ # Setter for scope +val+.
+ def scope=(val)
+ set_scope(val)
+ val
+ end
+
+ # Returns filter.
+ def filter
+ @filter
+ end
+
+ # Private setter for filter +val+.
+ def set_filter(val)
+ @filter = val
+ build_path_query
+ @filter
+ end
+ protected :set_filter
+
+ # Setter for filter +val+.
+ def filter=(val)
+ set_filter(val)
+ val
+ end
+
+ # Returns extensions.
+ def extensions
+ @extensions
+ end
+
+ # Private setter for extensions +val+.
+ def set_extensions(val)
+ @extensions = val
+ build_path_query
+ @extensions
+ end
+ protected :set_extensions
+
+ # Setter for extensions +val+.
+ def extensions=(val)
+ set_extensions(val)
+ val
+ end
+
+ # Checks if Gem::URI has a path.
+ # For Gem::URI::LDAP this will return +false+.
+ def hierarchical?
+ false
+ end
+ end
+
+ register_scheme 'LDAP', LDAP
+end
diff --git a/lib/rubygems/vendor/uri/lib/uri/ldaps.rb b/lib/rubygems/vendor/uri/lib/uri/ldaps.rb
new file mode 100644
index 0000000000..b7a5b50e27
--- /dev/null
+++ b/lib/rubygems/vendor/uri/lib/uri/ldaps.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: false
+# = uri/ldap.rb
+#
+# License:: You can redistribute it and/or modify it under the same term as Ruby.
+#
+# See Gem::URI for general documentation
+#
+
+require_relative 'ldap'
+
+module Gem::URI
+
+ # The default port for LDAPS URIs is 636, and the scheme is 'ldaps:' rather
+ # than 'ldap:'. Other than that, LDAPS URIs are identical to LDAP URIs;
+ # see Gem::URI::LDAP.
+ class LDAPS < LDAP
+ # A Default port of 636 for Gem::URI::LDAPS
+ DEFAULT_PORT = 636
+ end
+
+ register_scheme 'LDAPS', LDAPS
+end
diff --git a/lib/rubygems/vendor/uri/lib/uri/mailto.rb b/lib/rubygems/vendor/uri/lib/uri/mailto.rb
new file mode 100644
index 0000000000..7ae544d194
--- /dev/null
+++ b/lib/rubygems/vendor/uri/lib/uri/mailto.rb
@@ -0,0 +1,293 @@
+# frozen_string_literal: false
+# = uri/mailto.rb
+#
+# Author:: Akira Yamada <akira@ruby-lang.org>
+# License:: You can redistribute it and/or modify it under the same term as Ruby.
+#
+# See Gem::URI for general documentation
+#
+
+require_relative 'generic'
+
+module Gem::URI
+
+ #
+ # RFC6068, the mailto URL scheme.
+ #
+ class MailTo < Generic
+ include RFC2396_REGEXP
+
+ # A Default port of nil for Gem::URI::MailTo.
+ DEFAULT_PORT = nil
+
+ # An Array of the available components for Gem::URI::MailTo.
+ COMPONENT = [ :scheme, :to, :headers ].freeze
+
+ # :stopdoc:
+ # "hname" and "hvalue" are encodings of an RFC 822 header name and
+ # value, respectively. As with "to", all URL reserved characters must
+ # be encoded.
+ #
+ # "#mailbox" is as specified in RFC 822 [RFC822]. This means that it
+ # consists of zero or more comma-separated mail addresses, possibly
+ # including "phrase" and "comment" components. Note that all URL
+ # reserved characters in "to" must be encoded: in particular,
+ # parentheses, commas, and the percent sign ("%"), which commonly occur
+ # in the "mailbox" syntax.
+ #
+ # Within mailto URLs, the characters "?", "=", "&" are reserved.
+
+ # ; RFC 6068
+ # hfields = "?" hfield *( "&" hfield )
+ # hfield = hfname "=" hfvalue
+ # hfname = *qchar
+ # hfvalue = *qchar
+ # qchar = unreserved / pct-encoded / some-delims
+ # some-delims = "!" / "$" / "'" / "(" / ")" / "*"
+ # / "+" / "," / ";" / ":" / "@"
+ #
+ # ; RFC3986
+ # unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
+ # pct-encoded = "%" HEXDIG HEXDIG
+ HEADER_REGEXP = /\A(?<hfield>(?:%\h\h|[!$'-.0-;@-Z_a-z~])*=(?:%\h\h|[!$'-.0-;@-Z_a-z~])*)(?:&\g<hfield>)*\z/
+ # practical regexp for email address
+ # https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address
+ EMAIL_REGEXP = /\A[a-zA-Z0-9.!\#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\z/
+ # :startdoc:
+
+ #
+ # == Description
+ #
+ # Creates a new Gem::URI::MailTo object from components, with syntax checking.
+ #
+ # Components can be provided as an Array or Hash. If an Array is used,
+ # the components must be supplied as <code>[to, headers]</code>.
+ #
+ # If a Hash is used, the keys are the component names preceded by colons.
+ #
+ # The headers can be supplied as a pre-encoded string, such as
+ # <code>"subject=subscribe&cc=address"</code>, or as an Array of Arrays
+ # like <code>[['subject', 'subscribe'], ['cc', 'address']]</code>.
+ #
+ # Examples:
+ #
+ # require 'rubygems/vendor/uri/lib/uri'
+ #
+ # m1 = Gem::URI::MailTo.build(['joe@example.com', 'subject=Ruby'])
+ # m1.to_s # => "mailto:joe@example.com?subject=Ruby"
+ #
+ # m2 = Gem::URI::MailTo.build(['john@example.com', [['Subject', 'Ruby'], ['Cc', 'jack@example.com']]])
+ # m2.to_s # => "mailto:john@example.com?Subject=Ruby&Cc=jack@example.com"
+ #
+ # m3 = Gem::URI::MailTo.build({:to => 'listman@example.com', :headers => [['subject', 'subscribe']]})
+ # m3.to_s # => "mailto:listman@example.com?subject=subscribe"
+ #
+ def self.build(args)
+ tmp = Util.make_components_hash(self, args)
+
+ case tmp[:to]
+ when Array
+ tmp[:opaque] = tmp[:to].join(',')
+ when String
+ tmp[:opaque] = tmp[:to].dup
+ else
+ tmp[:opaque] = ''
+ end
+
+ if tmp[:headers]
+ query =
+ case tmp[:headers]
+ when Array
+ tmp[:headers].collect { |x|
+ if x.kind_of?(Array)
+ x[0] + '=' + x[1..-1].join
+ else
+ x.to_s
+ end
+ }.join('&')
+ when Hash
+ tmp[:headers].collect { |h,v|
+ h + '=' + v
+ }.join('&')
+ else
+ tmp[:headers].to_s
+ end
+ unless query.empty?
+ tmp[:opaque] << '?' << query
+ end
+ end
+
+ super(tmp)
+ end
+
+ #
+ # == Description
+ #
+ # Creates a new Gem::URI::MailTo object from generic URL components with
+ # no syntax checking.
+ #
+ # This method is usually called from Gem::URI::parse, which checks
+ # the validity of each component.
+ #
+ def initialize(*arg)
+ super(*arg)
+
+ @to = nil
+ @headers = []
+
+ # The RFC3986 parser does not normally populate opaque
+ @opaque = "?#{@query}" if @query && !@opaque
+
+ unless @opaque
+ raise InvalidComponentError,
+ "missing opaque part for mailto URL"
+ end
+ to, header = @opaque.split('?', 2)
+ # allow semicolon as a addr-spec separator
+ # http://support.microsoft.com/kb/820868
+ unless /\A(?:[^@,;]+@[^@,;]+(?:\z|[,;]))*\z/ =~ to
+ raise InvalidComponentError,
+ "unrecognised opaque part for mailtoURL: #{@opaque}"
+ end
+
+ if arg[10] # arg_check
+ self.to = to
+ self.headers = header
+ else
+ set_to(to)
+ set_headers(header)
+ end
+ end
+
+ # The primary e-mail address of the URL, as a String.
+ attr_reader :to
+
+ # E-mail headers set by the URL, as an Array of Arrays.
+ attr_reader :headers
+
+ # Checks the to +v+ component.
+ def check_to(v)
+ return true unless v
+ return true if v.size == 0
+
+ v.split(/[,;]/).each do |addr|
+ # check url safety as path-rootless
+ if /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*\z/ !~ addr
+ raise InvalidComponentError,
+ "an address in 'to' is invalid as Gem::URI #{addr.dump}"
+ end
+
+ # check addr-spec
+ # don't s/\+/ /g
+ addr.gsub!(/%\h\h/, Gem::URI::TBLDECWWWCOMP_)
+ if EMAIL_REGEXP !~ addr
+ raise InvalidComponentError,
+ "an address in 'to' is invalid as uri-escaped addr-spec #{addr.dump}"
+ end
+ end
+
+ true
+ end
+ private :check_to
+
+ # Private setter for to +v+.
+ def set_to(v)
+ @to = v
+ end
+ protected :set_to
+
+ # Setter for to +v+.
+ def to=(v)
+ check_to(v)
+ set_to(v)
+ v
+ end
+
+ # Checks the headers +v+ component against either
+ # * HEADER_REGEXP
+ def check_headers(v)
+ return true unless v
+ return true if v.size == 0
+ if HEADER_REGEXP !~ v
+ raise InvalidComponentError,
+ "bad component(expected opaque component): #{v}"
+ end
+
+ true
+ end
+ private :check_headers
+
+ # Private setter for headers +v+.
+ def set_headers(v)
+ @headers = []
+ if v
+ v.split('&').each do |x|
+ @headers << x.split(/=/, 2)
+ end
+ end
+ end
+ protected :set_headers
+
+ # Setter for headers +v+.
+ def headers=(v)
+ check_headers(v)
+ set_headers(v)
+ v
+ end
+
+ # Constructs String from Gem::URI.
+ def to_s
+ @scheme + ':' +
+ if @to
+ @to
+ else
+ ''
+ end +
+ if @headers.size > 0
+ '?' + @headers.collect{|x| x.join('=')}.join('&')
+ else
+ ''
+ end +
+ if @fragment
+ '#' + @fragment
+ else
+ ''
+ end
+ end
+
+ # Returns the RFC822 e-mail text equivalent of the URL, as a String.
+ #
+ # Example:
+ #
+ # require 'rubygems/vendor/uri/lib/uri'
+ #
+ # uri = Gem::URI.parse("mailto:ruby-list@ruby-lang.org?Subject=subscribe&cc=myaddr")
+ # uri.to_mailtext
+ # # => "To: ruby-list@ruby-lang.org\nSubject: subscribe\nCc: myaddr\n\n\n"
+ #
+ def to_mailtext
+ to = Gem::URI.decode_www_form_component(@to)
+ head = ''
+ body = ''
+ @headers.each do |x|
+ case x[0]
+ when 'body'
+ body = Gem::URI.decode_www_form_component(x[1])
+ when 'to'
+ to << ', ' + Gem::URI.decode_www_form_component(x[1])
+ else
+ head << Gem::URI.decode_www_form_component(x[0]).capitalize + ': ' +
+ Gem::URI.decode_www_form_component(x[1]) + "\n"
+ end
+ end
+
+ "To: #{to}
+#{head}
+#{body}
+"
+ end
+ alias to_rfc822text to_mailtext
+ end
+
+ register_scheme 'MAILTO', MailTo
+end
diff --git a/lib/rubygems/vendor/uri/lib/uri/rfc2396_parser.rb b/lib/rubygems/vendor/uri/lib/uri/rfc2396_parser.rb
new file mode 100644
index 0000000000..735a269f2c
--- /dev/null
+++ b/lib/rubygems/vendor/uri/lib/uri/rfc2396_parser.rb
@@ -0,0 +1,539 @@
+# frozen_string_literal: false
+#--
+# = uri/common.rb
+#
+# Author:: Akira Yamada <akira@ruby-lang.org>
+# License::
+# You can redistribute it and/or modify it under the same term as Ruby.
+#
+# See Gem::URI for general documentation
+#
+
+module Gem::URI
+ #
+ # Includes Gem::URI::REGEXP::PATTERN
+ #
+ module RFC2396_REGEXP
+ #
+ # Patterns used to parse Gem::URI's
+ #
+ module PATTERN
+ # :stopdoc:
+
+ # RFC 2396 (Gem::URI Generic Syntax)
+ # RFC 2732 (IPv6 Literal Addresses in URL's)
+ # RFC 2373 (IPv6 Addressing Architecture)
+
+ # alpha = lowalpha | upalpha
+ ALPHA = "a-zA-Z"
+ # alphanum = alpha | digit
+ ALNUM = "#{ALPHA}\\d"
+
+ # hex = digit | "A" | "B" | "C" | "D" | "E" | "F" |
+ # "a" | "b" | "c" | "d" | "e" | "f"
+ HEX = "a-fA-F\\d"
+ # escaped = "%" hex hex
+ ESCAPED = "%[#{HEX}]{2}"
+ # mark = "-" | "_" | "." | "!" | "~" | "*" | "'" |
+ # "(" | ")"
+ # unreserved = alphanum | mark
+ UNRESERVED = "\\-_.!~*'()#{ALNUM}"
+ # reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
+ # "$" | ","
+ # reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
+ # "$" | "," | "[" | "]" (RFC 2732)
+ RESERVED = ";/?:@&=+$,\\[\\]"
+
+ # domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
+ DOMLABEL = "(?:[#{ALNUM}](?:[-#{ALNUM}]*[#{ALNUM}])?)"
+ # toplabel = alpha | alpha *( alphanum | "-" ) alphanum
+ TOPLABEL = "(?:[#{ALPHA}](?:[-#{ALNUM}]*[#{ALNUM}])?)"
+ # hostname = *( domainlabel "." ) toplabel [ "." ]
+ HOSTNAME = "(?:#{DOMLABEL}\\.)*#{TOPLABEL}\\.?"
+
+ # :startdoc:
+ end # PATTERN
+
+ # :startdoc:
+ end # REGEXP
+
+ # Class that parses String's into Gem::URI's.
+ #
+ # It contains a Hash set of patterns and Regexp's that match and validate.
+ #
+ class RFC2396_Parser
+ include RFC2396_REGEXP
+
+ #
+ # == Synopsis
+ #
+ # Gem::URI::Parser.new([opts])
+ #
+ # == Args
+ #
+ # The constructor accepts a hash as options for parser.
+ # Keys of options are pattern names of Gem::URI components
+ # and values of options are pattern strings.
+ # The constructor generates set of regexps for parsing URIs.
+ #
+ # You can use the following keys:
+ #
+ # * :ESCAPED (Gem::URI::PATTERN::ESCAPED in default)
+ # * :UNRESERVED (Gem::URI::PATTERN::UNRESERVED in default)
+ # * :DOMLABEL (Gem::URI::PATTERN::DOMLABEL in default)
+ # * :TOPLABEL (Gem::URI::PATTERN::TOPLABEL in default)
+ # * :HOSTNAME (Gem::URI::PATTERN::HOSTNAME in default)
+ #
+ # == Examples
+ #
+ # p = Gem::URI::Parser.new(:ESCAPED => "(?:%[a-fA-F0-9]{2}|%u[a-fA-F0-9]{4})")
+ # u = p.parse("http://example.jp/%uABCD") #=> #<Gem::URI::HTTP http://example.jp/%uABCD>
+ # Gem::URI.parse(u.to_s) #=> raises Gem::URI::InvalidURIError
+ #
+ # s = "http://example.com/ABCD"
+ # u1 = p.parse(s) #=> #<Gem::URI::HTTP http://example.com/ABCD>
+ # u2 = Gem::URI.parse(s) #=> #<Gem::URI::HTTP http://example.com/ABCD>
+ # u1 == u2 #=> true
+ # u1.eql?(u2) #=> false
+ #
+ def initialize(opts = {})
+ @pattern = initialize_pattern(opts)
+ @pattern.each_value(&:freeze)
+ @pattern.freeze
+
+ @regexp = initialize_regexp(@pattern)
+ @regexp.each_value(&:freeze)
+ @regexp.freeze
+ end
+
+ # The Hash of patterns.
+ #
+ # See also Gem::URI::Parser.initialize_pattern.
+ attr_reader :pattern
+
+ # The Hash of Regexp.
+ #
+ # See also Gem::URI::Parser.initialize_regexp.
+ attr_reader :regexp
+
+ # Returns a split Gem::URI against +regexp[:ABS_URI]+.
+ def split(uri)
+ case uri
+ when ''
+ # null uri
+
+ when @regexp[:ABS_URI]
+ scheme, opaque, userinfo, host, port,
+ registry, path, query, fragment = $~[1..-1]
+
+ # Gem::URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
+
+ # absoluteURI = scheme ":" ( hier_part | opaque_part )
+ # hier_part = ( net_path | abs_path ) [ "?" query ]
+ # opaque_part = uric_no_slash *uric
+
+ # abs_path = "/" path_segments
+ # net_path = "//" authority [ abs_path ]
+
+ # authority = server | reg_name
+ # server = [ [ userinfo "@" ] hostport ]
+
+ if !scheme
+ raise InvalidURIError,
+ "bad Gem::URI(absolute but no scheme): #{uri}"
+ end
+ if !opaque && (!path && (!host && !registry))
+ raise InvalidURIError,
+ "bad Gem::URI(absolute but no path): #{uri}"
+ end
+
+ when @regexp[:REL_URI]
+ scheme = nil
+ opaque = nil
+
+ userinfo, host, port, registry,
+ rel_segment, abs_path, query, fragment = $~[1..-1]
+ if rel_segment && abs_path
+ path = rel_segment + abs_path
+ elsif rel_segment
+ path = rel_segment
+ elsif abs_path
+ path = abs_path
+ end
+
+ # Gem::URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
+
+ # relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ]
+
+ # net_path = "//" authority [ abs_path ]
+ # abs_path = "/" path_segments
+ # rel_path = rel_segment [ abs_path ]
+
+ # authority = server | reg_name
+ # server = [ [ userinfo "@" ] hostport ]
+
+ else
+ raise InvalidURIError, "bad Gem::URI(is not Gem::URI?): #{uri}"
+ end
+
+ path = '' if !path && !opaque # (see RFC2396 Section 5.2)
+ ret = [
+ scheme,
+ userinfo, host, port, # X
+ registry, # X
+ path, # Y
+ opaque, # Y
+ query,
+ fragment
+ ]
+ return ret
+ end
+
+ #
+ # == Args
+ #
+ # +uri+::
+ # String
+ #
+ # == Description
+ #
+ # Parses +uri+ and constructs either matching Gem::URI scheme object
+ # (File, FTP, HTTP, HTTPS, LDAP, LDAPS, or MailTo) or Gem::URI::Generic.
+ #
+ # == Usage
+ #
+ # p = Gem::URI::Parser.new
+ # p.parse("ldap://ldap.example.com/dc=example?user=john")
+ # #=> #<Gem::URI::LDAP ldap://ldap.example.com/dc=example?user=john>
+ #
+ def parse(uri)
+ Gem::URI.for(*self.split(uri), self)
+ end
+
+ #
+ # == Args
+ #
+ # +uris+::
+ # an Array of Strings
+ #
+ # == Description
+ #
+ # Attempts to parse and merge a set of URIs.
+ #
+ def join(*uris)
+ uris[0] = convert_to_uri(uris[0])
+ uris.inject :merge
+ end
+
+ #
+ # :call-seq:
+ # extract( str )
+ # extract( str, schemes )
+ # extract( str, schemes ) {|item| block }
+ #
+ # == Args
+ #
+ # +str+::
+ # String to search
+ # +schemes+::
+ # Patterns to apply to +str+
+ #
+ # == Description
+ #
+ # Attempts to parse and merge a set of URIs.
+ # If no +block+ given, then returns the result,
+ # else it calls +block+ for each element in result.
+ #
+ # See also Gem::URI::Parser.make_regexp.
+ #
+ def extract(str, schemes = nil)
+ if block_given?
+ str.scan(make_regexp(schemes)) { yield $& }
+ nil
+ else
+ result = []
+ str.scan(make_regexp(schemes)) { result.push $& }
+ result
+ end
+ end
+
+ # Returns Regexp that is default +self.regexp[:ABS_URI_REF]+,
+ # unless +schemes+ is provided. Then it is a Regexp.union with +self.pattern[:X_ABS_URI]+.
+ def make_regexp(schemes = nil)
+ unless schemes
+ @regexp[:ABS_URI_REF]
+ else
+ /(?=#{Regexp.union(*schemes)}:)#{@pattern[:X_ABS_URI]}/x
+ end
+ end
+
+ #
+ # :call-seq:
+ # escape( str )
+ # escape( str, unsafe )
+ #
+ # == Args
+ #
+ # +str+::
+ # String to make safe
+ # +unsafe+::
+ # Regexp to apply. Defaults to +self.regexp[:UNSAFE]+
+ #
+ # == Description
+ #
+ # Constructs a safe String from +str+, removing unsafe characters,
+ # replacing them with codes.
+ #
+ def escape(str, unsafe = @regexp[:UNSAFE])
+ unless unsafe.kind_of?(Regexp)
+ # perhaps unsafe is String object
+ unsafe = Regexp.new("[#{Regexp.quote(unsafe)}]", false)
+ end
+ str.gsub(unsafe) do
+ us = $&
+ tmp = ''
+ us.each_byte do |uc|
+ tmp << sprintf('%%%02X', uc)
+ end
+ tmp
+ end.force_encoding(Encoding::US_ASCII)
+ end
+
+ #
+ # :call-seq:
+ # unescape( str )
+ # unescape( str, escaped )
+ #
+ # == Args
+ #
+ # +str+::
+ # String to remove escapes from
+ # +escaped+::
+ # Regexp to apply. Defaults to +self.regexp[:ESCAPED]+
+ #
+ # == Description
+ #
+ # Removes escapes from +str+.
+ #
+ def unescape(str, escaped = @regexp[:ESCAPED])
+ enc = str.encoding
+ enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
+ str.gsub(escaped) { [$&[1, 2]].pack('H2').force_encoding(enc) }
+ end
+
+ @@to_s = Kernel.instance_method(:to_s)
+ if @@to_s.respond_to?(:bind_call)
+ def inspect
+ @@to_s.bind_call(self)
+ end
+ else
+ def inspect
+ @@to_s.bind(self).call
+ end
+ end
+
+ private
+
+ # Constructs the default Hash of patterns.
+ def initialize_pattern(opts = {})
+ ret = {}
+ ret[:ESCAPED] = escaped = (opts.delete(:ESCAPED) || PATTERN::ESCAPED)
+ ret[:UNRESERVED] = unreserved = opts.delete(:UNRESERVED) || PATTERN::UNRESERVED
+ ret[:RESERVED] = reserved = opts.delete(:RESERVED) || PATTERN::RESERVED
+ ret[:DOMLABEL] = opts.delete(:DOMLABEL) || PATTERN::DOMLABEL
+ ret[:TOPLABEL] = opts.delete(:TOPLABEL) || PATTERN::TOPLABEL
+ ret[:HOSTNAME] = hostname = opts.delete(:HOSTNAME)
+
+ # RFC 2396 (Gem::URI Generic Syntax)
+ # RFC 2732 (IPv6 Literal Addresses in URL's)
+ # RFC 2373 (IPv6 Addressing Architecture)
+
+ # uric = reserved | unreserved | escaped
+ ret[:URIC] = uric = "(?:[#{unreserved}#{reserved}]|#{escaped})"
+ # uric_no_slash = unreserved | escaped | ";" | "?" | ":" | "@" |
+ # "&" | "=" | "+" | "$" | ","
+ ret[:URIC_NO_SLASH] = uric_no_slash = "(?:[#{unreserved};?:@&=+$,]|#{escaped})"
+ # query = *uric
+ ret[:QUERY] = query = "#{uric}*"
+ # fragment = *uric
+ ret[:FRAGMENT] = fragment = "#{uric}*"
+
+ # hostname = *( domainlabel "." ) toplabel [ "." ]
+ # reg-name = *( unreserved / pct-encoded / sub-delims ) # RFC3986
+ unless hostname
+ ret[:HOSTNAME] = hostname = "(?:[a-zA-Z0-9\\-.]|%\\h\\h)+"
+ end
+
+ # RFC 2373, APPENDIX B:
+ # IPv6address = hexpart [ ":" IPv4address ]
+ # IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
+ # hexpart = hexseq | hexseq "::" [ hexseq ] | "::" [ hexseq ]
+ # hexseq = hex4 *( ":" hex4)
+ # hex4 = 1*4HEXDIG
+ #
+ # XXX: This definition has a flaw. "::" + IPv4address must be
+ # allowed too. Here is a replacement.
+ #
+ # IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
+ ret[:IPV4ADDR] = ipv4addr = "\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"
+ # hex4 = 1*4HEXDIG
+ hex4 = "[#{PATTERN::HEX}]{1,4}"
+ # lastpart = hex4 | IPv4address
+ lastpart = "(?:#{hex4}|#{ipv4addr})"
+ # hexseq1 = *( hex4 ":" ) hex4
+ hexseq1 = "(?:#{hex4}:)*#{hex4}"
+ # hexseq2 = *( hex4 ":" ) lastpart
+ hexseq2 = "(?:#{hex4}:)*#{lastpart}"
+ # IPv6address = hexseq2 | [ hexseq1 ] "::" [ hexseq2 ]
+ ret[:IPV6ADDR] = ipv6addr = "(?:#{hexseq2}|(?:#{hexseq1})?::(?:#{hexseq2})?)"
+
+ # IPv6prefix = ( hexseq1 | [ hexseq1 ] "::" [ hexseq1 ] ) "/" 1*2DIGIT
+ # unused
+
+ # ipv6reference = "[" IPv6address "]" (RFC 2732)
+ ret[:IPV6REF] = ipv6ref = "\\[#{ipv6addr}\\]"
+
+ # host = hostname | IPv4address
+ # host = hostname | IPv4address | IPv6reference (RFC 2732)
+ ret[:HOST] = host = "(?:#{hostname}|#{ipv4addr}|#{ipv6ref})"
+ # port = *digit
+ ret[:PORT] = port = '\d*'
+ # hostport = host [ ":" port ]
+ ret[:HOSTPORT] = hostport = "#{host}(?::#{port})?"
+
+ # userinfo = *( unreserved | escaped |
+ # ";" | ":" | "&" | "=" | "+" | "$" | "," )
+ ret[:USERINFO] = userinfo = "(?:[#{unreserved};:&=+$,]|#{escaped})*"
+
+ # pchar = unreserved | escaped |
+ # ":" | "@" | "&" | "=" | "+" | "$" | ","
+ pchar = "(?:[#{unreserved}:@&=+$,]|#{escaped})"
+ # param = *pchar
+ param = "#{pchar}*"
+ # segment = *pchar *( ";" param )
+ segment = "#{pchar}*(?:;#{param})*"
+ # path_segments = segment *( "/" segment )
+ ret[:PATH_SEGMENTS] = path_segments = "#{segment}(?:/#{segment})*"
+
+ # server = [ [ userinfo "@" ] hostport ]
+ server = "(?:#{userinfo}@)?#{hostport}"
+ # reg_name = 1*( unreserved | escaped | "$" | "," |
+ # ";" | ":" | "@" | "&" | "=" | "+" )
+ ret[:REG_NAME] = reg_name = "(?:[#{unreserved}$,;:@&=+]|#{escaped})+"
+ # authority = server | reg_name
+ authority = "(?:#{server}|#{reg_name})"
+
+ # rel_segment = 1*( unreserved | escaped |
+ # ";" | "@" | "&" | "=" | "+" | "$" | "," )
+ ret[:REL_SEGMENT] = rel_segment = "(?:[#{unreserved};@&=+$,]|#{escaped})+"
+
+ # scheme = alpha *( alpha | digit | "+" | "-" | "." )
+ ret[:SCHEME] = scheme = "[#{PATTERN::ALPHA}][\\-+.#{PATTERN::ALPHA}\\d]*"
+
+ # abs_path = "/" path_segments
+ ret[:ABS_PATH] = abs_path = "/#{path_segments}"
+ # rel_path = rel_segment [ abs_path ]
+ ret[:REL_PATH] = rel_path = "#{rel_segment}(?:#{abs_path})?"
+ # net_path = "//" authority [ abs_path ]
+ ret[:NET_PATH] = net_path = "//#{authority}(?:#{abs_path})?"
+
+ # hier_part = ( net_path | abs_path ) [ "?" query ]
+ ret[:HIER_PART] = hier_part = "(?:#{net_path}|#{abs_path})(?:\\?(?:#{query}))?"
+ # opaque_part = uric_no_slash *uric
+ ret[:OPAQUE_PART] = opaque_part = "#{uric_no_slash}#{uric}*"
+
+ # absoluteURI = scheme ":" ( hier_part | opaque_part )
+ ret[:ABS_URI] = abs_uri = "#{scheme}:(?:#{hier_part}|#{opaque_part})"
+ # relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ]
+ ret[:REL_URI] = rel_uri = "(?:#{net_path}|#{abs_path}|#{rel_path})(?:\\?#{query})?"
+
+ # Gem::URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
+ ret[:URI_REF] = "(?:#{abs_uri}|#{rel_uri})?(?:##{fragment})?"
+
+ ret[:X_ABS_URI] = "
+ (#{scheme}): (?# 1: scheme)
+ (?:
+ (#{opaque_part}) (?# 2: opaque)
+ |
+ (?:(?:
+ //(?:
+ (?:(?:(#{userinfo})@)? (?# 3: userinfo)
+ (?:(#{host})(?::(\\d*))?))? (?# 4: host, 5: port)
+ |
+ (#{reg_name}) (?# 6: registry)
+ )
+ |
+ (?!//)) (?# XXX: '//' is the mark for hostport)
+ (#{abs_path})? (?# 7: path)
+ )(?:\\?(#{query}))? (?# 8: query)
+ )
+ (?:\\#(#{fragment}))? (?# 9: fragment)
+ "
+
+ ret[:X_REL_URI] = "
+ (?:
+ (?:
+ //
+ (?:
+ (?:(#{userinfo})@)? (?# 1: userinfo)
+ (#{host})?(?::(\\d*))? (?# 2: host, 3: port)
+ |
+ (#{reg_name}) (?# 4: registry)
+ )
+ )
+ |
+ (#{rel_segment}) (?# 5: rel_segment)
+ )?
+ (#{abs_path})? (?# 6: abs_path)
+ (?:\\?(#{query}))? (?# 7: query)
+ (?:\\#(#{fragment}))? (?# 8: fragment)
+ "
+
+ ret
+ end
+
+ # Constructs the default Hash of Regexp's.
+ def initialize_regexp(pattern)
+ ret = {}
+
+ # for Gem::URI::split
+ ret[:ABS_URI] = Regexp.new('\A\s*+' + pattern[:X_ABS_URI] + '\s*\z', Regexp::EXTENDED)
+ ret[:REL_URI] = Regexp.new('\A\s*+' + pattern[:X_REL_URI] + '\s*\z', Regexp::EXTENDED)
+
+ # for Gem::URI::extract
+ ret[:URI_REF] = Regexp.new(pattern[:URI_REF])
+ ret[:ABS_URI_REF] = Regexp.new(pattern[:X_ABS_URI], Regexp::EXTENDED)
+ ret[:REL_URI_REF] = Regexp.new(pattern[:X_REL_URI], Regexp::EXTENDED)
+
+ # for Gem::URI::escape/unescape
+ ret[:ESCAPED] = Regexp.new(pattern[:ESCAPED])
+ ret[:UNSAFE] = Regexp.new("[^#{pattern[:UNRESERVED]}#{pattern[:RESERVED]}]")
+
+ # for Generic#initialize
+ ret[:SCHEME] = Regexp.new("\\A#{pattern[:SCHEME]}\\z")
+ ret[:USERINFO] = Regexp.new("\\A#{pattern[:USERINFO]}\\z")
+ ret[:HOST] = Regexp.new("\\A#{pattern[:HOST]}\\z")
+ ret[:PORT] = Regexp.new("\\A#{pattern[:PORT]}\\z")
+ ret[:OPAQUE] = Regexp.new("\\A#{pattern[:OPAQUE_PART]}\\z")
+ ret[:REGISTRY] = Regexp.new("\\A#{pattern[:REG_NAME]}\\z")
+ ret[:ABS_PATH] = Regexp.new("\\A#{pattern[:ABS_PATH]}\\z")
+ ret[:REL_PATH] = Regexp.new("\\A#{pattern[:REL_PATH]}\\z")
+ ret[:QUERY] = Regexp.new("\\A#{pattern[:QUERY]}\\z")
+ ret[:FRAGMENT] = Regexp.new("\\A#{pattern[:FRAGMENT]}\\z")
+
+ ret
+ end
+
+ def convert_to_uri(uri)
+ if uri.is_a?(Gem::URI::Generic)
+ uri
+ elsif uri = String.try_convert(uri)
+ parse(uri)
+ else
+ raise ArgumentError,
+ "bad argument (expected Gem::URI object or Gem::URI string)"
+ end
+ end
+
+ end # class Parser
+end # module Gem::URI
diff --git a/lib/rubygems/vendor/uri/lib/uri/rfc3986_parser.rb b/lib/rubygems/vendor/uri/lib/uri/rfc3986_parser.rb
new file mode 100644
index 0000000000..728bb55674
--- /dev/null
+++ b/lib/rubygems/vendor/uri/lib/uri/rfc3986_parser.rb
@@ -0,0 +1,183 @@
+# frozen_string_literal: true
+module Gem::URI
+ class RFC3986_Parser # :nodoc:
+ # Gem::URI defined in RFC3986
+ HOST = %r[
+ (?<IP-literal>\[(?:
+ (?<IPv6address>
+ (?:\h{1,4}:){6}
+ (?<ls32>\h{1,4}:\h{1,4}
+ | (?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)
+ \.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>)
+ )
+ | ::(?:\h{1,4}:){5}\g<ls32>
+ | \h{1,4}?::(?:\h{1,4}:){4}\g<ls32>
+ | (?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>
+ | (?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>
+ | (?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>
+ | (?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>
+ | (?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}
+ | (?:(?:\h{1,4}:){,6}\h{1,4})?::
+ )
+ | (?<IPvFuture>v\h++\.[!$&-.0-9:;=A-Z_a-z~]++)
+ )\])
+ | \g<IPv4address>
+ | (?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*+)
+ ]x
+
+ USERINFO = /(?:%\h\h|[!$&-.0-9:;=A-Z_a-z~])*+/
+
+ SCHEME = %r[[A-Za-z][+\-.0-9A-Za-z]*+].source
+ SEG = %r[(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/])].source
+ SEG_NC = %r[(?:%\h\h|[!$&-.0-9;=@A-Z_a-z~])].source
+ FRAGMENT = %r[(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/?])*+].source
+
+ RFC3986_URI = %r[\A
+ (?<seg>#{SEG}){0}
+ (?<Gem::URI>
+ (?<scheme>#{SCHEME}):
+ (?<hier-part>//
+ (?<authority>
+ (?:(?<userinfo>#{USERINFO.source})@)?
+ (?<host>#{HOST.source.delete(" \n")})
+ (?::(?<port>\d*+))?
+ )
+ (?<path-abempty>(?:/\g<seg>*+)?)
+ | (?<path-absolute>/((?!/)\g<seg>++)?)
+ | (?<path-rootless>(?!/)\g<seg>++)
+ | (?<path-empty>)
+ )
+ (?:\?(?<query>[^\#]*+))?
+ (?:\#(?<fragment>#{FRAGMENT}))?
+ )\z]x
+
+ RFC3986_relative_ref = %r[\A
+ (?<seg>#{SEG}){0}
+ (?<relative-ref>
+ (?<relative-part>//
+ (?<authority>
+ (?:(?<userinfo>#{USERINFO.source})@)?
+ (?<host>#{HOST.source.delete(" \n")}(?<!/))?
+ (?::(?<port>\d*+))?
+ )
+ (?<path-abempty>(?:/\g<seg>*+)?)
+ | (?<path-absolute>/\g<seg>*+)
+ | (?<path-noscheme>#{SEG_NC}++(?:/\g<seg>*+)?)
+ | (?<path-empty>)
+ )
+ (?:\?(?<query>[^#]*+))?
+ (?:\#(?<fragment>#{FRAGMENT}))?
+ )\z]x
+ attr_reader :regexp
+
+ def initialize
+ @regexp = default_regexp.each_value(&:freeze).freeze
+ end
+
+ def split(uri) #:nodoc:
+ begin
+ uri = uri.to_str
+ rescue NoMethodError
+ raise InvalidURIError, "bad Gem::URI(is not Gem::URI?): #{uri.inspect}"
+ end
+ uri.ascii_only? or
+ raise InvalidURIError, "Gem::URI must be ascii only #{uri.dump}"
+ if m = RFC3986_URI.match(uri)
+ query = m["query"]
+ scheme = m["scheme"]
+ opaque = m["path-rootless"]
+ if opaque
+ opaque << "?#{query}" if query
+ [ scheme,
+ nil, # userinfo
+ nil, # host
+ nil, # port
+ nil, # registry
+ nil, # path
+ opaque,
+ nil, # query
+ m["fragment"]
+ ]
+ else # normal
+ [ scheme,
+ m["userinfo"],
+ m["host"],
+ m["port"],
+ nil, # registry
+ (m["path-abempty"] ||
+ m["path-absolute"] ||
+ m["path-empty"]),
+ nil, # opaque
+ query,
+ m["fragment"]
+ ]
+ end
+ elsif m = RFC3986_relative_ref.match(uri)
+ [ nil, # scheme
+ m["userinfo"],
+ m["host"],
+ m["port"],
+ nil, # registry,
+ (m["path-abempty"] ||
+ m["path-absolute"] ||
+ m["path-noscheme"] ||
+ m["path-empty"]),
+ nil, # opaque
+ m["query"],
+ m["fragment"]
+ ]
+ else
+ raise InvalidURIError, "bad Gem::URI(is not Gem::URI?): #{uri.inspect}"
+ end
+ end
+
+ def parse(uri) # :nodoc:
+ Gem::URI.for(*self.split(uri), self)
+ end
+
+
+ def join(*uris) # :nodoc:
+ uris[0] = convert_to_uri(uris[0])
+ uris.inject :merge
+ end
+
+ @@to_s = Kernel.instance_method(:to_s)
+ if @@to_s.respond_to?(:bind_call)
+ def inspect
+ @@to_s.bind_call(self)
+ end
+ else
+ def inspect
+ @@to_s.bind(self).call
+ end
+ end
+
+ private
+
+ def default_regexp # :nodoc:
+ {
+ SCHEME: %r[\A#{SCHEME}\z]o,
+ USERINFO: %r[\A#{USERINFO}\z]o,
+ HOST: %r[\A#{HOST}\z]o,
+ ABS_PATH: %r[\A/#{SEG}*+\z]o,
+ REL_PATH: %r[\A(?!/)#{SEG}++\z]o,
+ QUERY: %r[\A(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/?])*+\z],
+ FRAGMENT: %r[\A#{FRAGMENT}\z]o,
+ OPAQUE: %r[\A(?:[^/].*)?\z],
+ PORT: /\A[\x09\x0a\x0c\x0d ]*+\d*[\x09\x0a\x0c\x0d ]*\z/,
+ }
+ end
+
+ def convert_to_uri(uri)
+ if uri.is_a?(Gem::URI::Generic)
+ uri
+ elsif uri = String.try_convert(uri)
+ parse(uri)
+ else
+ raise ArgumentError,
+ "bad argument (expected Gem::URI object or Gem::URI string)"
+ end
+ end
+
+ end # class Parser
+end # module Gem::URI
diff --git a/lib/rubygems/vendor/uri/lib/uri/version.rb b/lib/rubygems/vendor/uri/lib/uri/version.rb
new file mode 100644
index 0000000000..3c80c334d4
--- /dev/null
+++ b/lib/rubygems/vendor/uri/lib/uri/version.rb
@@ -0,0 +1,6 @@
+module Gem::URI
+ # :stopdoc:
+ VERSION_CODE = '001300'.freeze
+ VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.').freeze
+ # :startdoc:
+end
diff --git a/lib/rubygems/vendor/uri/lib/uri/ws.rb b/lib/rubygems/vendor/uri/lib/uri/ws.rb
new file mode 100644
index 0000000000..0dd2a7a1bb
--- /dev/null
+++ b/lib/rubygems/vendor/uri/lib/uri/ws.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: false
+# = uri/ws.rb
+#
+# Author:: Matt Muller <mamuller@amazon.com>
+# License:: You can redistribute it and/or modify it under the same term as Ruby.
+#
+# See Gem::URI for general documentation
+#
+
+require_relative 'generic'
+
+module Gem::URI
+
+ #
+ # The syntax of WS URIs is defined in RFC6455 section 3.
+ #
+ # Note that the Ruby Gem::URI library allows WS URLs containing usernames and
+ # passwords. This is not legal as per the RFC, but used to be
+ # supported in Internet Explorer 5 and 6, before the MS04-004 security
+ # update. See <URL:http://support.microsoft.com/kb/834489>.
+ #
+ class WS < Generic
+ # A Default port of 80 for Gem::URI::WS.
+ DEFAULT_PORT = 80
+
+ # An Array of the available components for Gem::URI::WS.
+ COMPONENT = %i[
+ scheme
+ userinfo host port
+ path
+ query
+ ].freeze
+
+ #
+ # == Description
+ #
+ # Creates a new Gem::URI::WS object from components, with syntax checking.
+ #
+ # The components accepted are userinfo, host, port, path, and query.
+ #
+ # The components should be provided either as an Array, or as a Hash
+ # with keys formed by preceding the component names with a colon.
+ #
+ # If an Array is used, the components must be passed in the
+ # order <code>[userinfo, host, port, path, query]</code>.
+ #
+ # Example:
+ #
+ # uri = Gem::URI::WS.build(host: 'www.example.com', path: '/foo/bar')
+ #
+ # uri = Gem::URI::WS.build([nil, "www.example.com", nil, "/path", "query"])
+ #
+ # Currently, if passed userinfo components this method generates
+ # invalid WS URIs as per RFC 1738.
+ #
+ def self.build(args)
+ tmp = Util.make_components_hash(self, args)
+ super(tmp)
+ end
+
+ #
+ # == Description
+ #
+ # Returns the full path for a WS Gem::URI, as required by Net::HTTP::Get.
+ #
+ # If the Gem::URI contains a query, the full path is Gem::URI#path + '?' + Gem::URI#query.
+ # Otherwise, the path is simply Gem::URI#path.
+ #
+ # Example:
+ #
+ # uri = Gem::URI::WS.build(path: '/foo/bar', query: 'test=true')
+ # uri.request_uri # => "/foo/bar?test=true"
+ #
+ def request_uri
+ return unless @path
+
+ url = @query ? "#@path?#@query" : @path.dup
+ url.start_with?(?/.freeze) ? url : ?/ + url
+ end
+ end
+
+ register_scheme 'WS', WS
+end
diff --git a/lib/rubygems/vendor/uri/lib/uri/wss.rb b/lib/rubygems/vendor/uri/lib/uri/wss.rb
new file mode 100644
index 0000000000..0b91d334bb
--- /dev/null
+++ b/lib/rubygems/vendor/uri/lib/uri/wss.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: false
+# = uri/wss.rb
+#
+# Author:: Matt Muller <mamuller@amazon.com>
+# License:: You can redistribute it and/or modify it under the same term as Ruby.
+#
+# See Gem::URI for general documentation
+#
+
+require_relative 'ws'
+
+module Gem::URI
+
+ # The default port for WSS URIs is 443, and the scheme is 'wss:' rather
+ # than 'ws:'. Other than that, WSS URIs are identical to WS URIs;
+ # see Gem::URI::WS.
+ class WSS < WS
+ # A Default port of 443 for Gem::URI::WSS
+ DEFAULT_PORT = 443
+ end
+
+ register_scheme 'WSS', WSS
+end
diff --git a/lib/rubygems/vendored_molinillo.rb b/lib/rubygems/vendored_molinillo.rb
new file mode 100644
index 0000000000..45906c0e5c
--- /dev/null
+++ b/lib/rubygems/vendored_molinillo.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+require_relative "vendor/molinillo/lib/molinillo"
diff --git a/lib/rubygems/vendored_net_http.rb b/lib/rubygems/vendored_net_http.rb
new file mode 100644
index 0000000000..a84c52a947
--- /dev/null
+++ b/lib/rubygems/vendored_net_http.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+# Ruby 3.3 and RubyGems 3.5 is already load Gem::Timeout from lib/rubygems/net/http.rb
+# We should avoid to load it again
+require_relative "vendor/net-http/lib/net/http" unless defined?(Gem::Net::HTTP)
diff --git a/lib/rubygems/vendored_optparse.rb b/lib/rubygems/vendored_optparse.rb
new file mode 100644
index 0000000000..a5611d32f0
--- /dev/null
+++ b/lib/rubygems/vendored_optparse.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+require_relative "vendor/optparse/lib/optparse"
diff --git a/lib/rubygems/vendored_timeout.rb b/lib/rubygems/vendored_timeout.rb
new file mode 100644
index 0000000000..45541928e6
--- /dev/null
+++ b/lib/rubygems/vendored_timeout.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+# Ruby 3.3 and RubyGems 3.5 is already load Gem::Timeout from lib/rubygems/timeout.rb
+# We should avoid to load it again
+require_relative "vendor/timeout/lib/timeout" unless defined?(Gem::Timeout)
diff --git a/lib/rubygems/vendored_tsort.rb b/lib/rubygems/vendored_tsort.rb
new file mode 100644
index 0000000000..c3d815650d
--- /dev/null
+++ b/lib/rubygems/vendored_tsort.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+require_relative "vendor/tsort/lib/tsort"
diff --git a/lib/rubygems/version.rb b/lib/rubygems/version.rb
index 3d8e11b515..e174d8ad95 100644
--- a/lib/rubygems/version.rb
+++ b/lib/rubygems/version.rb
@@ -131,7 +131,7 @@ require_relative "deprecate"
#
# == Preventing Version Catastrophe:
#
-# From: http://blog.zenspider.com/2008/10/rubygems-howto-preventing-cata.html
+# From: https://www.zenspider.com/ruby/2008/10/rubygems-how-to-preventing-catastrophe.html
#
# Let's say you're depending on the fnord gem version 2.y.z. If you
# specify your dependency as ">= 2.0.0" then, you're good, right? What
@@ -156,13 +156,13 @@ class Gem::Version
include Comparable
VERSION_PATTERN = '[0-9]+(?>\.[0-9a-zA-Z]+)*(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?' # :nodoc:
- ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})?\s*\z/.freeze # :nodoc:
+ ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})?\s*\z/ # :nodoc:
##
# A string representation of this Version.
def version
- @version.dup
+ @version
end
alias_method :to_s, :version
@@ -173,7 +173,7 @@ class Gem::Version
def self.correct?(version)
nil_versions_are_discouraged! if version.nil?
- !!(version.to_s =~ ANCHORED_VERSION_PATTERN)
+ ANCHORED_VERSION_PATTERN.match?(version.to_s)
end
##
@@ -201,7 +201,7 @@ class Gem::Version
@@release = {}
def self.new(version) # :nodoc:
- return super unless Gem::Version == self
+ return super unless self == Gem::Version
@@all[version] ||= super
end
@@ -224,9 +224,17 @@ class Gem::Version
end
# If version is an empty string convert it to 0
- version = 0 if version.is_a?(String) && version =~ /\A\s*\Z/
+ version = 0 if version.is_a?(String) && /\A\s*\Z/.match?(version)
+
+ @version = version.to_s
- @version = version.to_s.strip.gsub("-",".pre.")
+ # optimization to avoid allocation when given an integer, since we know
+ # it's to_s won't have any spaces or dashes
+ unless version.is_a?(Integer)
+ @version = @version.strip
+ @version.gsub!("-",".pre.")
+ end
+ @version = -@version
@segments = nil
end
@@ -252,7 +260,7 @@ class Gem::Version
# same precision. Version "1.0" is not the same as version "1".
def eql?(other)
- self.class === other && @version == other._version
+ self.class === other && @version == other.version
end
def hash # :nodoc:
@@ -284,7 +292,7 @@ class Gem::Version
end
def yaml_initialize(tag, map) # :nodoc:
- @version = map["version"]
+ @version = -map["version"]
@segments = nil
@hash = nil
end
@@ -302,7 +310,7 @@ class Gem::Version
def prerelease?
unless instance_variable_defined? :@prerelease
- @prerelease = !(@version =~ /[a-zA-Z]/).nil?
+ @prerelease = /[a-zA-Z]/.match?(version)
end
@prerelease
end
@@ -354,7 +362,7 @@ class Gem::Version
return self <=> self.class.new(other) if (String === other) && self.class.correct?(other)
return unless Gem::Version === other
- return 0 if @version == other._version || canonical_segments == other.canonical_segments
+ return 0 if @version == other.version || canonical_segments == other.canonical_segments
lhsegments = canonical_segments
rhsegments = other.canonical_segments
@@ -380,39 +388,37 @@ class Gem::Version
0
end
+ # remove trailing zeros segments before first letter or at the end of the version
def canonical_segments
- @canonical_segments ||=
- _split_segments.map! do |segments|
- segments.reverse_each.drop_while {|s| s == 0 }.reverse
- end.reduce(&:concat)
+ @canonical_segments ||= begin
+ # remove trailing 0 segments, using dot or letter as anchor
+ # may leave a trailing dot which will be ignored by partition_segments
+ canonical_version = @version.sub(/(?<=[a-zA-Z.])[.0]+\z/, "")
+ # remove 0 segments before the first letter in a prerelease version
+ canonical_version.sub!(/(?<=\.|\A)[0.]+(?=[a-zA-Z])/, "") if prerelease?
+ partition_segments(canonical_version)
+ end
end
def freeze
prerelease?
+ _segments
canonical_segments
super
end
protected
- def _version
- @version
- end
-
def _segments
# segments is lazy so it can pick up version values that come from
# old marshaled versions, which don't go through marshal_load.
# since this version object is cached in @@all, its @segments should be frozen
-
- @segments ||= @version.scan(/[0-9]+|[a-z]+/i).map do |s|
- /^\d+$/.match?(s) ? s.to_i : s
- end.freeze
+ @segments ||= partition_segments(@version)
end
- def _split_segments
- string_start = _segments.index {|s| s.is_a?(String) }
- string_segments = segments
- numeric_segments = string_segments.slice!(0, string_start || string_segments.size)
- [numeric_segments, string_segments]
+ def partition_segments(ver)
+ ver.scan(/\d+|[a-z]+/i).map! do |s|
+ /\A\d/.match?(s) ? s.to_i : -s
+ end.freeze
end
end
diff --git a/lib/rubygems/webauthn_listener.rb b/lib/rubygems/webauthn_listener.rb
deleted file mode 100644
index 22f7ea2011..0000000000
--- a/lib/rubygems/webauthn_listener.rb
+++ /dev/null
@@ -1,92 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "webauthn_listener/response"
-
-##
-# The WebauthnListener class retrieves an OTP after a user successfully WebAuthns with the Gem host.
-# An instance opens a socket using the TCPServer instance given and listens for a request from the Gem host.
-# The request should be a GET request to the root path and contains the OTP code in the form
-# of a query parameter `code`. The listener will return the code which will be used as the OTP for
-# API requests.
-#
-# Types of responses sent by the listener after receiving a request:
-# - 200 OK: OTP code was successfully retrieved
-# - 204 No Content: If the request was an OPTIONS request
-# - 400 Bad Request: If the request did not contain a query parameter `code`
-# - 404 Not Found: The request was not to the root path
-# - 405 Method Not Allowed: OTP code was not retrieved because the request was not a GET/OPTIONS request
-#
-# Example usage:
-#
-# server = TCPServer.new(0)
-# otp = Gem::WebauthnListener.wait_for_otp_code("https://rubygems.example", server)
-#
-
-class Gem::WebauthnListener
- attr_reader :host
-
- def initialize(host)
- @host = host
- end
-
- def self.wait_for_otp_code(host, server)
- new(host).fetch_otp_from_connection(server)
- end
-
- def fetch_otp_from_connection(server)
- loop do
- socket = server.accept
- request_line = socket.gets
-
- method, req_uri, _protocol = request_line.split(" ")
- req_uri = URI.parse(req_uri)
-
- responder = SocketResponder.new(socket)
-
- unless root_path?(req_uri)
- responder.send(NotFoundResponse.for(host))
- raise Gem::WebauthnVerificationError, "Page at #{req_uri.path} not found."
- end
-
- case method.upcase
- when "OPTIONS"
- responder.send(NoContentResponse.for(host))
- next # will be GET
- when "GET"
- if otp = parse_otp_from_uri(req_uri)
- responder.send(OkResponse.for(host))
- return otp
- end
- responder.send(BadRequestResponse.for(host))
- raise Gem::WebauthnVerificationError, "Did not receive OTP from #{host}."
- else
- responder.send(MethodNotAllowedResponse.for(host))
- raise Gem::WebauthnVerificationError, "Invalid HTTP method #{method.upcase} received."
- end
- end
- end
-
- private
-
- def root_path?(uri)
- uri.path == "/"
- end
-
- def parse_otp_from_uri(uri)
- require "cgi"
-
- return if uri.query.nil?
- CGI.parse(uri.query).dig("code", 0)
- end
-
- class SocketResponder
- def initialize(socket)
- @socket = socket
- end
-
- def send(response)
- @socket.print response.to_s
- @socket.close
- end
- end
-end
diff --git a/lib/rubygems/webauthn_listener/response.rb b/lib/rubygems/webauthn_listener/response.rb
deleted file mode 100644
index baa769c4ae..0000000000
--- a/lib/rubygems/webauthn_listener/response.rb
+++ /dev/null
@@ -1,161 +0,0 @@
-# frozen_string_literal: true
-
-##
-# The WebauthnListener Response class is used by the WebauthnListener to create
-# responses to be sent to the Gem host. It creates a Net::HTTPResponse instance
-# when initialized and can be converted to the appropriate format to be sent by a socket using `to_s`.
-# Net::HTTPResponse instances cannot be directly sent over a socket.
-#
-# Types of response classes:
-# - OkResponse
-# - NoContentResponse
-# - BadRequestResponse
-# - NotFoundResponse
-# - MethodNotAllowedResponse
-#
-# Example usage:
-#
-# server = TCPServer.new(0)
-# socket = server.accept
-#
-# response = OkResponse.for("https://rubygems.example")
-# socket.print response.to_s
-# socket.close
-#
-
-class Gem::WebauthnListener
- class Response
- attr_reader :http_response
-
- def self.for(host)
- new(host)
- end
-
- def initialize(host)
- @host = host
-
- build_http_response
- end
-
- def to_s
- status_line = "HTTP/#{@http_response.http_version} #{@http_response.code} #{@http_response.message}\r\n"
- headers = @http_response.to_hash.map {|header, value| "#{header}: #{value.join(", ")}\r\n" }.join + "\r\n"
- body = @http_response.body ? "#{@http_response.body}\n" : ""
-
- status_line + headers + body
- end
-
- private
-
- # Must be implemented in subclasses
- def code
- raise NotImplementedError
- end
-
- def reason_phrase
- raise NotImplementedError
- end
-
- def body; end
-
- def build_http_response
- response_class = Net::HTTPResponse::CODE_TO_OBJ[code.to_s]
- @http_response = response_class.new("1.1", code, reason_phrase)
- @http_response.instance_variable_set(:@read, true)
-
- add_connection_header
- add_access_control_headers
- add_body
- end
-
- def add_connection_header
- @http_response["connection"] = "close"
- end
-
- def add_access_control_headers
- @http_response["access-control-allow-origin"] = @host
- @http_response["access-control-allow-methods"] = "POST"
- @http_response["access-control-allow-headers"] = %w[Content-Type Authorization x-csrf-token]
- end
-
- def add_body
- return unless body
- @http_response["content-type"] = "text/plain"
- @http_response["content-length"] = body.bytesize
- @http_response.instance_variable_set(:@body, body)
- end
- end
-
- class OkResponse < Response
- private
-
- def code
- 200
- end
-
- def reason_phrase
- "OK"
- end
-
- def body
- "success"
- end
- end
-
- class NoContentResponse < Response
- private
-
- def code
- 204
- end
-
- def reason_phrase
- "No Content"
- end
- end
-
- class BadRequestResponse < Response
- private
-
- def code
- 400
- end
-
- def reason_phrase
- "Bad Request"
- end
-
- def body
- "missing code parameter"
- end
- end
-
- class NotFoundResponse < Response
- private
-
- def code
- 404
- end
-
- def reason_phrase
- "Not Found"
- end
- end
-
- class MethodNotAllowedResponse < Response
- private
-
- def code
- 405
- end
-
- def reason_phrase
- "Method Not Allowed"
- end
-
- def add_access_control_headers
- super
- @http_response["allow"] = %w[GET OPTIONS]
- end
- end
-end
diff --git a/lib/rubygems/yaml_serializer.rb b/lib/rubygems/yaml_serializer.rb
index a447709c74..128becc1ce 100644
--- a/lib/rubygems/yaml_serializer.rb
+++ b/lib/rubygems/yaml_serializer.rb
@@ -17,7 +17,11 @@ module Gem
if v.is_a?(Hash)
yaml << dump_hash(v).gsub(/^(?!$)/, " ") # indent all non-empty lines
elsif v.is_a?(Array) # Expected to be array of strings
- yaml << "\n- " << v.map {|s| s.to_s.gsub(/\s+/, " ").inspect }.join("\n- ") << "\n"
+ if v.empty?
+ yaml << " []\n"
+ else
+ yaml << "\n- " << v.map {|s| s.to_s.gsub(/\s+/, " ").inspect }.join("\n- ") << "\n"
+ end
else
yaml << " " << v.to_s.gsub(/\s+/, " ").inspect << "\n"
end
@@ -32,7 +36,7 @@ module Gem
(.*) # value
\1 # matching closing quote
$
- /xo.freeze
+ /xo
HASH_REGEX = /
^
@@ -44,18 +48,20 @@ module Gem
(.*) # value
\3 # matching closing quote
$
- /xo.freeze
+ /xo
def load(str)
res = {}
stack = [res]
last_hash = nil
last_empty_key = nil
- str.split(/\r?\n/).each do |line|
+ str.split(/\r?\n/) do |line|
if match = HASH_REGEX.match(line)
indent, key, quote, val = match.captures
- key = convert_to_backward_compatible_key(key)
- depth = indent.scan(/ /).length
+ val = strip_comment(val)
+
+ convert_to_backward_compatible_key!(key)
+ depth = indent.size / 2
if quote.empty? && val.empty?
new_hash = {}
stack[depth][key] = new_hash
@@ -63,10 +69,13 @@ module Gem
last_empty_key = key
last_hash = stack[depth]
else
+ val = [] if val == "[]" # empty array
stack[depth][key] = val
end
elsif match = ARRAY_REGEX.match(line)
_, val = match.captures
+ val = strip_comment(val)
+
last_hash[last_empty_key] = [] unless last_hash[last_empty_key].is_a?(Array)
last_hash[last_empty_key].push(val)
@@ -75,15 +84,22 @@ module Gem
res
end
+ def strip_comment(val)
+ if val.include?("#") && !val.start_with?("#")
+ val.split("#", 2).first.strip
+ else
+ val
+ end
+ end
+
# for settings' keys
- def convert_to_backward_compatible_key(key)
- key = "#{key}/" if key =~ /https?:/i && key !~ %r{/\Z}
- key = key.gsub(".", "__") if key.include?(".")
- key
+ def convert_to_backward_compatible_key!(key)
+ key << "/" if /https?:/i.match?(key) && !%r{/\Z}.match?(key)
+ key.gsub!(".", "__")
end
class << self
- private :dump_hash, :convert_to_backward_compatible_key
+ private :dump_hash, :convert_to_backward_compatible_key!
end
end
end
diff --git a/lib/securerandom.gemspec b/lib/securerandom.gemspec
index f42ddbcc97..0b023486a6 100644
--- a/lib/securerandom.gemspec
+++ b/lib/securerandom.gemspec
@@ -20,8 +20,13 @@ Gem::Specification.new do |spec|
spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = spec.homepage
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
+ # Specify which files should be added to the gem when it is released.
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
+ spec.files = Dir.chdir(__dir__) do
+ `git ls-files -z`.split("\x0").reject do |f|
+ (File.expand_path(f) == __FILE__) ||
+ f.start_with?(*%w[bin/ test/ spec/ features/ docs/ rakelib/ .document .git Gemfile Rakefile])
+ end
end
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
diff --git a/lib/securerandom.rb b/lib/securerandom.rb
index 9faa09e4e0..81757f3100 100644
--- a/lib/securerandom.rb
+++ b/lib/securerandom.rb
@@ -40,19 +40,27 @@ require 'random/formatter'
module SecureRandom
- VERSION = "0.2.2"
+ # The version
+ VERSION = "0.3.1"
class << self
+ # Returns a random binary string containing +size+ bytes.
+ #
+ # See Random.bytes
def bytes(n)
return gen_random(n)
end
private
+ # :stopdoc:
+
+ # Implementation using OpenSSL
def gen_random_openssl(n)
return OpenSSL::Random.random_bytes(n)
end
+ # Implementation using system random device
def gen_random_urandom(n)
ret = Random.urandom(n)
unless ret
@@ -78,6 +86,9 @@ module SecureRandom
end
end
+ # :startdoc:
+
+ # Generate random data bytes for Random::Formatter
public :gen_random
end
end
diff --git a/lib/set.rb b/lib/set.rb
index 5ac37dbf3c..a0954a31d1 100644
--- a/lib/set.rb
+++ b/lib/set.rb
@@ -3,7 +3,7 @@
#
# set.rb - defines the Set class
#
-# Copyright (c) 2002-2020 Akinori MUSHA <knu@iDaemons.org>
+# Copyright (c) 2002-2023 Akinori MUSHA <knu@iDaemons.org>
#
# Documentation by Akinori MUSHA and Gavin Sinclair.
#
@@ -216,7 +216,7 @@
# has been modified while an element in the set.
#
class Set
- VERSION = "1.0.3"
+ VERSION = "1.1.0"
include Enumerable
@@ -286,18 +286,10 @@ class Set
@hash = orig.instance_variable_get(:@hash).dup
end
- if Kernel.instance_method(:initialize_clone).arity != 1
- # Clone internal hash.
- def initialize_clone(orig, **options)
- super
- @hash = orig.instance_variable_get(:@hash).clone(**options)
- end
- else
- # Clone internal hash.
- def initialize_clone(orig)
- super
- @hash = orig.instance_variable_get(:@hash).clone
- end
+ # Clone internal hash.
+ def initialize_clone(orig, **options)
+ super
+ @hash = orig.instance_variable_get(:@hash).clone(**options)
end
def freeze # :nodoc:
@@ -389,7 +381,7 @@ class Set
# Equivalent to Set#flatten, but replaces the receiver with the
# result in place. Returns nil if no modifications were made.
def flatten!
- replace(flatten()) if any? { |e| e.is_a?(Set) }
+ replace(flatten()) if any?(Set)
end
# Returns true if the set contains the given object.
@@ -409,7 +401,7 @@ class Set
when set.instance_of?(self.class) && @hash.respond_to?(:>=)
@hash >= set.instance_variable_get(:@hash)
when set.is_a?(Set)
- size >= set.size && set.all? { |o| include?(o) }
+ size >= set.size && set.all?(self)
else
raise ArgumentError, "value must be a set"
end
@@ -422,7 +414,7 @@ class Set
when set.instance_of?(self.class) && @hash.respond_to?(:>)
@hash > set.instance_variable_get(:@hash)
when set.is_a?(Set)
- size > set.size && set.all? { |o| include?(o) }
+ size > set.size && set.all?(self)
else
raise ArgumentError, "value must be a set"
end
@@ -435,7 +427,7 @@ class Set
when set.instance_of?(self.class) && @hash.respond_to?(:<=)
@hash <= set.instance_variable_get(:@hash)
when set.is_a?(Set)
- size <= set.size && all? { |o| set.include?(o) }
+ size <= set.size && all?(set)
else
raise ArgumentError, "value must be a set"
end
@@ -448,7 +440,7 @@ class Set
when set.instance_of?(self.class) && @hash.respond_to?(:<)
@hash < set.instance_variable_get(:@hash)
when set.is_a?(Set)
- size < set.size && all? { |o| set.include?(o) }
+ size < set.size && all?(set)
else
raise ArgumentError, "value must be a set"
end
@@ -479,12 +471,12 @@ class Set
case set
when Set
if size < set.size
- any? { |o| set.include?(o) }
+ any?(set)
else
- set.any? { |o| include?(o) }
+ set.any?(self)
end
when Enumerable
- set.any? { |o| include?(o) }
+ set.any?(self)
else
raise ArgumentError, "value must be enumerable"
end
diff --git a/lib/set/set.gemspec b/lib/set/set.gemspec
index c415fac99c..2ebef6985d 100644
--- a/lib/set/set.gemspec
+++ b/lib/set/set.gemspec
@@ -15,7 +15,7 @@ Gem::Specification.new do |spec|
spec.description = %q{Provides a class to deal with collections of unordered, unique values}
spec.homepage = "https://github.com/ruby/set"
spec.licenses = ["Ruby", "BSD-2-Clause"]
- spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
+ spec.required_ruby_version = Gem::Requirement.new(">= 3.0.0")
spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = spec.homepage
diff --git a/lib/shellwords.rb b/lib/shellwords.rb
index 5866601b17..067432e374 100644
--- a/lib/shellwords.rb
+++ b/lib/shellwords.rb
@@ -68,8 +68,7 @@
# 1: {IEEE Std 1003.1-2008, 2016 Edition, the Shell & Utilities volume}[http://pubs.opengroup.org/onlinepubs/9699919799/utilities/contents.html]
module Shellwords
-
- VERSION = "0.1.0"
+ VERSION = "0.2.0"
# Splits a string into an array of tokens in the same way the UNIX
# Bourne shell does.
diff --git a/lib/singleton.rb b/lib/singleton.rb
index 7ed17a769c..6da939124e 100644
--- a/lib/singleton.rb
+++ b/lib/singleton.rb
@@ -92,7 +92,7 @@
# p a.strip # => nil
#
module Singleton
- VERSION = "0.1.1"
+ VERSION = "0.2.0"
# Raises a TypeError to prevent cloning.
def clone
diff --git a/lib/syntax_suggest/api.rb b/lib/syntax_suggest/api.rb
index 74e53c2563..65660ec5e5 100644
--- a/lib/syntax_suggest/api.rb
+++ b/lib/syntax_suggest/api.rb
@@ -5,9 +5,28 @@ require_relative "version"
require "tmpdir"
require "stringio"
require "pathname"
-require "ripper"
require "timeout"
+# We need Ripper loaded for `Prism.lex_compat` even if we're using Prism
+# for lexing and parsing
+require "ripper"
+
+# Prism is the new parser, replacing Ripper
+#
+# We need to "dual boot" both for now because syntax_suggest
+# supports older rubies that do not ship with syntax suggest.
+#
+# We also need the ability to control loading of this library
+# so we can test that both modes work correctly in CI.
+if (value = ENV["SYNTAX_SUGGEST_DISABLE_PRISM"])
+ warn "Skipping loading prism due to SYNTAX_SUGGEST_DISABLE_PRISM=#{value}"
+else
+ begin
+ require "prism"
+ rescue LoadError
+ end
+end
+
module SyntaxSuggest
# Used to indicate a default value that cannot
# be confused with another input.
@@ -16,6 +35,14 @@ module SyntaxSuggest
class Error < StandardError; end
TIMEOUT_DEFAULT = ENV.fetch("SYNTAX_SUGGEST_TIMEOUT", 1).to_i
+ # SyntaxSuggest.use_prism_parser? [Private]
+ #
+ # Tells us if the prism parser is available for use
+ # or if we should fallback to `Ripper`
+ def self.use_prism_parser?
+ defined?(Prism)
+ end
+
# SyntaxSuggest.handle_error [Public]
#
# Takes a `SyntaxError` exception, uses the
@@ -129,11 +156,20 @@ module SyntaxSuggest
# SyntaxSuggest.invalid? [Private]
#
# Opposite of `SyntaxSuggest.valid?`
- def self.invalid?(source)
- source = source.join if source.is_a?(Array)
- source = source.to_s
+ if defined?(Prism)
+ def self.invalid?(source)
+ source = source.join if source.is_a?(Array)
+ source = source.to_s
- Ripper.new(source).tap(&:parse).error?
+ Prism.parse(source).failure?
+ end
+ else
+ def self.invalid?(source)
+ source = source.join if source.is_a?(Array)
+ source = source.to_s
+
+ Ripper.new(source).tap(&:parse).error?
+ end
end
# SyntaxSuggest.valid? [Private]
@@ -191,7 +227,6 @@ require_relative "lex_all"
require_relative "code_line"
require_relative "code_block"
require_relative "block_expand"
-require_relative "ripper_errors"
require_relative "priority_queue"
require_relative "unvisited_lines"
require_relative "around_block_scan"
diff --git a/lib/syntax_suggest/around_block_scan.rb b/lib/syntax_suggest/around_block_scan.rb
index ce00431b3a..dd9af729c5 100644
--- a/lib/syntax_suggest/around_block_scan.rb
+++ b/lib/syntax_suggest/around_block_scan.rb
@@ -118,7 +118,7 @@ module SyntaxSuggest
end
# Scanning is intentionally conservative because
- # we have no way of rolling back an agressive block (at this time)
+ # we have no way of rolling back an aggressive block (at this time)
#
# If a block was stopped for some trivial reason, (like an empty line)
# but the next line would have caused it to be balanced then we
@@ -224,7 +224,7 @@ module SyntaxSuggest
@scanner.lines
end
- # Managable rspec errors
+ # Manageable rspec errors
def inspect
"#<#{self.class}:0x0000123843lol >"
end
diff --git a/lib/syntax_suggest/block_expand.rb b/lib/syntax_suggest/block_expand.rb
index e9b486c720..2751ae2a64 100644
--- a/lib/syntax_suggest/block_expand.rb
+++ b/lib/syntax_suggest/block_expand.rb
@@ -157,7 +157,7 @@ module SyntaxSuggest
end
end
- # Managable rspec errors
+ # Manageable rspec errors
def inspect
"#<SyntaxSuggest::CodeBlock:0x0000123843lol >"
end
diff --git a/lib/syntax_suggest/capture_code_context.rb b/lib/syntax_suggest/capture_code_context.rb
index 6dc7047176..1f232cfae3 100644
--- a/lib/syntax_suggest/capture_code_context.rb
+++ b/lib/syntax_suggest/capture_code_context.rb
@@ -26,7 +26,7 @@ module SyntaxSuggest
# they can't add extra data that's not present.
#
# In the case of known ambiguious cases, this class adds context
- # back to the ambiguitiy so the programmer has full information.
+ # back to the ambiguity so the programmer has full information.
#
# Beyond handling these ambiguities, it also captures surrounding
# code context information:
diff --git a/lib/syntax_suggest/clean_document.rb b/lib/syntax_suggest/clean_document.rb
index 2c26061bfc..2790ccae86 100644
--- a/lib/syntax_suggest/clean_document.rb
+++ b/lib/syntax_suggest/clean_document.rb
@@ -47,9 +47,9 @@ module SyntaxSuggest
# ## Heredocs
#
# A heredoc is an way of defining a multi-line string. They can cause many
- # problems. If left as a single line, Ripper would try to parse the contents
+ # problems. If left as a single line, the parser would try to parse the contents
# as ruby code rather than as a string. Even without this problem, we still
- # hit an issue with indentation
+ # hit an issue with indentation:
#
# 1 foo = <<~HEREDOC
# 2 "Be yourself; everyone else is already taken.""
@@ -224,7 +224,7 @@ module SyntaxSuggest
#
def join_consecutive!
consecutive_groups = @document.select(&:ignore_newline_not_beg?).map do |code_line|
- take_while_including(code_line.index..-1) do |line|
+ take_while_including(code_line.index..) do |line|
line.ignore_newline_not_beg?
end
end
@@ -245,7 +245,7 @@ module SyntaxSuggest
# expect(lines[1].to_s).to eq("")
def join_trailing_slash!
trailing_groups = @document.select(&:trailing_slash?).map do |code_line|
- take_while_including(code_line.index..-1) { |x| x.trailing_slash? }
+ take_while_including(code_line.index..) { |x| x.trailing_slash? }
end
join_groups(trailing_groups)
self
@@ -267,7 +267,7 @@ module SyntaxSuggest
groups.each do |lines|
line = lines.first
- # Handle the case of multiple groups in a a row
+ # Handle the case of multiple groups in a row
# if one is already replaced, move on
next if @document[line.index].empty?
@@ -279,7 +279,7 @@ module SyntaxSuggest
)
# Hide the rest of the lines
- lines[1..-1].each do |line|
+ lines[1..].each do |line|
# The above lines already have newlines in them, if add more
# then there will be double newline, use an empty line instead
@document[line.index] = CodeLine.new(line: "", index: line.index, lex: [])
@@ -293,7 +293,7 @@ module SyntaxSuggest
# Like `take_while` except when it stops
# iterating, it also returns the line
# that caused it to stop
- def take_while_including(range = 0..-1)
+ def take_while_including(range = 0..)
take_next_and_stop = false
@document[range].take_while do |line|
next if take_next_and_stop
diff --git a/lib/syntax_suggest/code_block.rb b/lib/syntax_suggest/code_block.rb
index 61e7986da4..d842890300 100644
--- a/lib/syntax_suggest/code_block.rb
+++ b/lib/syntax_suggest/code_block.rb
@@ -81,7 +81,7 @@ module SyntaxSuggest
# lines then the result cannot be invalid
#
# That means there's no reason to re-check all
- # lines with ripper (which is expensive).
+ # lines with the parser (which is expensive).
# Benchmark in commit message
@valid = if lines.all? { |l| l.hidden? || l.empty? }
true
diff --git a/lib/syntax_suggest/code_frontier.rb b/lib/syntax_suggest/code_frontier.rb
index 8e93b32514..0f870d0df0 100644
--- a/lib/syntax_suggest/code_frontier.rb
+++ b/lib/syntax_suggest/code_frontier.rb
@@ -117,7 +117,7 @@ module SyntaxSuggest
if ENV["SYNTAX_SUGGEST_DEBUG"]
puts "```"
- puts @queue.peek.to_s
+ puts @queue.peek
puts "```"
puts " @frontier indent: #{frontier_indent}"
puts " @unvisited indent: #{unvisited_indent}"
diff --git a/lib/syntax_suggest/code_line.rb b/lib/syntax_suggest/code_line.rb
index a20f34afa4..58197e95d0 100644
--- a/lib/syntax_suggest/code_line.rb
+++ b/lib/syntax_suggest/code_line.rb
@@ -180,12 +180,19 @@ module SyntaxSuggest
# EOM
# expect(lines.first.trailing_slash?).to eq(true)
#
- def trailing_slash?
- last = @lex.last
- return false unless last
- return false unless last.type == :on_sp
+ if SyntaxSuggest.use_prism_parser?
+ def trailing_slash?
+ last = @lex.last
+ last&.type == :on_tstring_end
+ end
+ else
+ def trailing_slash?
+ last = @lex.last
+ return false unless last
+ return false unless last.type == :on_sp
- last.token == TRAILING_SLASH
+ last.token == TRAILING_SLASH
+ end
end
# Endless method detection
diff --git a/lib/syntax_suggest/code_search.rb b/lib/syntax_suggest/code_search.rb
index 2a86dfea90..7628dcd131 100644
--- a/lib/syntax_suggest/code_search.rb
+++ b/lib/syntax_suggest/code_search.rb
@@ -43,7 +43,7 @@ module SyntaxSuggest
def initialize(source, record_dir: DEFAULT_VALUE)
record_dir = if record_dir == DEFAULT_VALUE
- ENV["SYNTAX_SUGGEST_RECORD_DIR"] || ENV["SYNTAX_SUGGEST_DEBUG"] ? "tmp" : nil
+ (ENV["SYNTAX_SUGGEST_RECORD_DIR"] || ENV["SYNTAX_SUGGEST_DEBUG"]) ? "tmp" : nil
else
record_dir
end
@@ -73,7 +73,7 @@ module SyntaxSuggest
if ENV["SYNTAX_SUGGEST_DEBUG"]
puts "\n\n==== #{filename} ===="
puts "\n```#{block.starts_at}..#{block.ends_at}"
- puts block.to_s
+ puts block
puts "```"
puts " block indent: #{block.current_indent}"
end
diff --git a/lib/syntax_suggest/core_ext.rb b/lib/syntax_suggest/core_ext.rb
index e0fd62b81c..c299627bb7 100644
--- a/lib/syntax_suggest/core_ext.rb
+++ b/lib/syntax_suggest/core_ext.rb
@@ -21,7 +21,7 @@ if SyntaxError.method_defined?(:detailed_message)
attr_reader :string
end
- # SyntaxSuggest.record_dir [Private]
+ # SyntaxSuggest.module_for_detailed_message [Private]
#
# Used to monkeypatch SyntaxError via Module.prepend
def self.module_for_detailed_message
diff --git a/lib/syntax_suggest/display_invalid_blocks.rb b/lib/syntax_suggest/display_invalid_blocks.rb
index 32ec0021a3..5e79b3a262 100644
--- a/lib/syntax_suggest/display_invalid_blocks.rb
+++ b/lib/syntax_suggest/display_invalid_blocks.rb
@@ -14,7 +14,7 @@ module SyntaxSuggest
@filename = filename
@code_lines = code_lines
- @terminal = terminal == DEFAULT_VALUE ? io.isatty : terminal
+ @terminal = (terminal == DEFAULT_VALUE) ? io.isatty : terminal
end
def document_ok?
diff --git a/lib/syntax_suggest/explain_syntax.rb b/lib/syntax_suggest/explain_syntax.rb
index 142ed2e269..0d80c4d869 100644
--- a/lib/syntax_suggest/explain_syntax.rb
+++ b/lib/syntax_suggest/explain_syntax.rb
@@ -2,7 +2,21 @@
require_relative "left_right_lex_count"
+if !SyntaxSuggest.use_prism_parser?
+ require_relative "ripper_errors"
+end
+
module SyntaxSuggest
+ class GetParseErrors
+ def self.errors(source)
+ if SyntaxSuggest.use_prism_parser?
+ Prism.parse(source).errors.map(&:message)
+ else
+ RipperErrors.new(source).call.errors
+ end
+ end
+ end
+
# Explains syntax errors based on their source
#
# example:
@@ -15,8 +29,8 @@ module SyntaxSuggest
# # => "Unmatched keyword, missing `end' ?"
#
# When the error cannot be determined by lexical counting
- # then ripper is run against the input and the raw ripper
- # errors returned.
+ # then the parser is run against the input and the raw
+ # errors are returned.
#
# Example:
#
@@ -91,10 +105,10 @@ module SyntaxSuggest
# Returns an array of syntax error messages
#
# If no missing pairs are found it falls back
- # on the original ripper error messages
+ # on the original error messages
def errors
if missing.empty?
- return RipperErrors.new(@code_lines.map(&:original).join).call.errors
+ return GetParseErrors.errors(@code_lines.map(&:original).join).uniq
end
missing.map { |miss| why(miss) }
diff --git a/lib/syntax_suggest/lex_all.rb b/lib/syntax_suggest/lex_all.rb
index 132cba9f5d..c16fbb52d3 100644
--- a/lib/syntax_suggest/lex_all.rb
+++ b/lib/syntax_suggest/lex_all.rb
@@ -3,34 +3,53 @@
module SyntaxSuggest
# Ripper.lex is not guaranteed to lex the entire source document
#
- # lex = LexAll.new(source: source)
- # lex.each do |value|
- # puts value.line
- # end
+ # This class guarantees the whole document is lex-ed by iteratively
+ # lexing the document where ripper stopped.
+ #
+ # Prism likely doesn't have the same problem. Once ripper support is removed
+ # we can likely reduce the complexity here if not remove the whole concept.
+ #
+ # Example usage:
+ #
+ # lex = LexAll.new(source: source)
+ # lex.each do |value|
+ # puts value.line
+ # end
class LexAll
include Enumerable
def initialize(source:, source_lines: nil)
- @lex = Ripper::Lexer.new(source, "-", 1).parse.sort_by(&:pos)
- lineno = @lex.last.pos.first + 1
+ @lex = self.class.lex(source, 1)
+ lineno = @lex.last[0][0] + 1
source_lines ||= source.lines
last_lineno = source_lines.length
until lineno >= last_lineno
- lines = source_lines[lineno..-1]
+ lines = source_lines[lineno..]
@lex.concat(
- Ripper::Lexer.new(lines.join, "-", lineno + 1).parse.sort_by(&:pos)
+ self.class.lex(lines.join, lineno + 1)
)
- lineno = @lex.last.pos.first + 1
+
+ lineno = @lex.last[0].first + 1
end
last_lex = nil
@lex.map! { |elem|
- last_lex = LexValue.new(elem.pos.first, elem.event, elem.tok, elem.state, last_lex)
+ last_lex = LexValue.new(elem[0].first, elem[1], elem[2], elem[3], last_lex)
}
end
+ if SyntaxSuggest.use_prism_parser?
+ def self.lex(source, line_number)
+ Prism.lex_compat(source, line: line_number).value.sort_by { |values| values[0] }
+ end
+ else
+ def self.lex(source, line_number)
+ Ripper::Lexer.new(source, "-", line_number).parse.sort_by(&:pos)
+ end
+ end
+
def to_a
@lex
end
diff --git a/lib/syntax_suggest/parse_blocks_from_indent_line.rb b/lib/syntax_suggest/parse_blocks_from_indent_line.rb
index 241ed6acb4..39dfca55d2 100644
--- a/lib/syntax_suggest/parse_blocks_from_indent_line.rb
+++ b/lib/syntax_suggest/parse_blocks_from_indent_line.rb
@@ -8,7 +8,7 @@ module SyntaxSuggest
# grabbing one that contains only an "end". In this example:
#
# def dog
- # begonn # mispelled `begin`
+ # begonn # misspelled `begin`
# puts "bark"
# end
# end
diff --git a/lib/syntax_suggest/pathname_from_message.rb b/lib/syntax_suggest/pathname_from_message.rb
index b6fe1617be..ab90227427 100644
--- a/lib/syntax_suggest/pathname_from_message.rb
+++ b/lib/syntax_suggest/pathname_from_message.rb
@@ -13,7 +13,7 @@ module SyntaxSuggest
# # => "/tmp/scratch.rb"
#
class PathnameFromMessage
- EVAL_RE = /^\(eval\):\d+/
+ EVAL_RE = /^\(eval.*\):\d+/
STREAMING_RE = /^-:\d+/
attr_reader :name
diff --git a/lib/syntax_suggest/ripper_errors.rb b/lib/syntax_suggest/ripper_errors.rb
index 48eb206e48..4e2bc90948 100644
--- a/lib/syntax_suggest/ripper_errors.rb
+++ b/lib/syntax_suggest/ripper_errors.rb
@@ -1,7 +1,10 @@
# frozen_string_literal: true
module SyntaxSuggest
- # Capture parse errors from ripper
+ # Capture parse errors from Ripper
+ #
+ # Prism returns the errors with their messages, but Ripper
+ # does not. To get them we must make a custom subclass.
#
# Example:
#
diff --git a/lib/syntax_suggest/scan_history.rb b/lib/syntax_suggest/scan_history.rb
index d15597c440..dc36e6ba2e 100644
--- a/lib/syntax_suggest/scan_history.rb
+++ b/lib/syntax_suggest/scan_history.rb
@@ -118,7 +118,7 @@ module SyntaxSuggest
# Returns an array of all the CodeLines that exist after
# the currently scanned block
private def after_lines
- @code_lines[@after_index.next..-1] || []
+ @code_lines[@after_index.next..] || []
end
private def current
diff --git a/lib/syntax_suggest/syntax_suggest.gemspec b/lib/syntax_suggest/syntax_suggest.gemspec
index 0e611c13d0..756a85bf63 100644
--- a/lib/syntax_suggest/syntax_suggest.gemspec
+++ b/lib/syntax_suggest/syntax_suggest.gemspec
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
spec.description = 'When you get an "unexpected end" in your syntax this gem helps you find it'
spec.homepage = "https://github.com/ruby/syntax_suggest.git"
spec.license = "MIT"
- spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
+ spec.required_ruby_version = Gem::Requirement.new(">= 3.0.0")
spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = "https://github.com/ruby/syntax_suggest.git"
diff --git a/lib/syntax_suggest/version.rb b/lib/syntax_suggest/version.rb
index ac8c2f62e5..4320adb218 100644
--- a/lib/syntax_suggest/version.rb
+++ b/lib/syntax_suggest/version.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
module SyntaxSuggest
- VERSION = "1.1.0"
+ VERSION = "2.0.0"
end
diff --git a/lib/tempfile.gemspec b/lib/tempfile.gemspec
index a3cb11296d..0b362b0a86 100644
--- a/lib/tempfile.gemspec
+++ b/lib/tempfile.gemspec
@@ -22,8 +22,12 @@ Gem::Specification.new do |spec|
# Specify which files should be added to the gem when it is released.
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
- `git ls-files -z 2>#{IO::NULL}`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
+ gemspec = File.basename(__FILE__)
+ spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL, exception: false) do |ls|
+ ls.readlines("\x0", chomp: true).reject do |f|
+ (f == gemspec) ||
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git Gemfile])
+ end
end
spec.require_paths = ["lib"]
end
diff --git a/lib/tempfile.rb b/lib/tempfile.rb
index d53d8c7196..1d7b80a74d 100644
--- a/lib/tempfile.rb
+++ b/lib/tempfile.rb
@@ -88,7 +88,8 @@ require 'tmpdir'
# mutex.
class Tempfile < DelegateClass(File)
- VERSION = "0.1.3"
+ # The version
+ VERSION = "0.2.1"
# Creates a file in the underlying file system;
# returns a new \Tempfile object based on that file.
@@ -152,26 +153,50 @@ class Tempfile < DelegateClass(File)
@unlinked = false
@mode = mode|File::RDWR|File::CREAT|File::EXCL
+ @finalizer_obj = Object.new
+ tmpfile = nil
::Dir::Tmpname.create(basename, tmpdir, **options) do |tmpname, n, opts|
opts[:perm] = 0600
- @tmpfile = File.open(tmpname, @mode, **opts)
+ tmpfile = File.open(tmpname, @mode, **opts)
@opts = opts.freeze
end
- ObjectSpace.define_finalizer(self, Remover.new(@tmpfile))
+ ObjectSpace.define_finalizer(@finalizer_obj, Remover.new(tmpfile.path))
+ ObjectSpace.define_finalizer(self, Closer.new(tmpfile))
- super(@tmpfile)
+ super(tmpfile)
+ end
+
+ def initialize_dup(other) # :nodoc:
+ initialize_copy_iv(other)
+ super(other)
+ ObjectSpace.define_finalizer(self, Closer.new(__getobj__))
+ end
+
+ def initialize_clone(other) # :nodoc:
+ initialize_copy_iv(other)
+ super(other)
+ ObjectSpace.define_finalizer(self, Closer.new(__getobj__))
+ end
+
+ private def initialize_copy_iv(other) # :nodoc:
+ @unlinked = other.unlinked
+ @mode = other.mode
+ @opts = other.opts
+ @finalizer_obj = other.finalizer_obj
end
# Opens or reopens the file with mode "r+".
def open
_close
+ ObjectSpace.undefine_finalizer(self)
mode = @mode & ~(File::CREAT|File::EXCL)
- @tmpfile = File.open(@tmpfile.path, mode, **@opts)
- __setobj__(@tmpfile)
+ __setobj__(File.open(__getobj__.path, mode, **@opts))
+ ObjectSpace.define_finalizer(self, Closer.new(__getobj__))
+ __getobj__
end
def _close # :nodoc:
- @tmpfile.close
+ __getobj__.close
end
protected :_close
@@ -228,13 +253,13 @@ class Tempfile < DelegateClass(File)
def unlink
return if @unlinked
begin
- File.unlink(@tmpfile.path)
+ File.unlink(__getobj__.path)
rescue Errno::ENOENT
rescue Errno::EACCES
# may not be able to unlink on Windows; just ignore
return
end
- ObjectSpace.undefine_finalizer(self)
+ ObjectSpace.undefine_finalizer(@finalizer_obj)
@unlinked = true
end
alias delete unlink
@@ -242,43 +267,57 @@ class Tempfile < DelegateClass(File)
# Returns the full path name of the temporary file.
# This will be nil if #unlink has been called.
def path
- @unlinked ? nil : @tmpfile.path
+ @unlinked ? nil : __getobj__.path
end
# Returns the size of the temporary file. As a side effect, the IO
# buffer is flushed before determining the size.
def size
- if !@tmpfile.closed?
- @tmpfile.size # File#size calls rb_io_flush_raw()
+ if !__getobj__.closed?
+ __getobj__.size # File#size calls rb_io_flush_raw()
else
- File.size(@tmpfile.path)
+ File.size(__getobj__.path)
end
end
alias length size
# :stopdoc:
def inspect
- if @tmpfile.closed?
+ if __getobj__.closed?
"#<#{self.class}:#{path} (closed)>"
else
"#<#{self.class}:#{path}>"
end
end
+ alias to_s inspect
- class Remover # :nodoc:
+ protected
+
+ attr_reader :unlinked, :mode, :opts, :finalizer_obj
+
+ class Closer # :nodoc:
def initialize(tmpfile)
- @pid = Process.pid
@tmpfile = tmpfile
end
def call(*args)
+ @tmpfile.close
+ end
+ end
+
+ class Remover # :nodoc:
+ def initialize(path)
+ @pid = Process.pid
+ @path = path
+ end
+
+ def call(*args)
return if @pid != Process.pid
- $stderr.puts "removing #{@tmpfile.path}..." if $DEBUG
+ $stderr.puts "removing #{@path}..." if $DEBUG
- @tmpfile.close
begin
- File.unlink(@tmpfile.path)
+ File.unlink(@path)
rescue Errno::ENOENT
end
diff --git a/lib/time.gemspec b/lib/time.gemspec
index a9349a253f..4b9f9e1218 100644
--- a/lib/time.gemspec
+++ b/lib/time.gemspec
@@ -20,8 +20,12 @@ Gem::Specification.new do |spec|
spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = spec.homepage
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
+ srcdir, gemspec = File.split(__FILE__)
+ spec.files = Dir.chdir(srcdir) do
+ `git ls-files -z`.split("\x0").reject { |f|
+ f == gemspec or
+ f.start_with?(".git", "bin/", "test/", "rakelib/", "Gemfile", "Rakefile")
+ }
end
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
diff --git a/lib/time.rb b/lib/time.rb
index c572e76d7d..b565130b50 100644
--- a/lib/time.rb
+++ b/lib/time.rb
@@ -26,7 +26,7 @@ require 'date'
class Time
- VERSION = "0.2.2"
+ VERSION = "0.3.0"
class << Time
@@ -391,6 +391,8 @@ class Time
# heuristic to detect the format of the input string, you provide
# a second argument that describes the format of the string.
#
+ # Raises `ArgumentError` if the date or format is invalid.
+ #
# If a block is given, the year described in +date+ is converted by the
# block. For example:
#
@@ -455,7 +457,7 @@ class Time
#
def strptime(date, format, now=self.now)
d = Date._strptime(date, format)
- raise ArgumentError, "invalid date or strptime format - `#{date}' `#{format}'" unless d
+ raise ArgumentError, "invalid date or strptime format - '#{date}' '#{format}'" unless d
if seconds = d[:seconds]
if sec_fraction = d[:sec_fraction]
usec = sec_fraction * 1000000
diff --git a/lib/timeout.gemspec b/lib/timeout.gemspec
index 23c6e5fe95..5449494e07 100644
--- a/lib/timeout.gemspec
+++ b/lib/timeout.gemspec
@@ -20,6 +20,9 @@ Gem::Specification.new do |spec|
spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = spec.homepage
+ spec.metadata["changelog_uri"] = spec.homepage + "/releases"
+
+ spec.required_ruby_version = '>= 2.6.0'
spec.files = Dir.chdir(__dir__) do
`git ls-files -z`.split("\x0").reject do |f|
diff --git a/lib/timeout.rb b/lib/timeout.rb
index e1107765ab..c67a748856 100644
--- a/lib/timeout.rb
+++ b/lib/timeout.rb
@@ -23,29 +23,27 @@
# Copyright:: (C) 2000 Information-technology Promotion Agency, Japan
module Timeout
- VERSION = "0.3.2"
+ # The version
+ VERSION = "0.4.1"
+
+ # Internal error raised to when a timeout is triggered.
+ class ExitException < Exception
+ def exception(*) # :nodoc:
+ self
+ end
+ end
# Raised by Timeout.timeout when the block times out.
class Error < RuntimeError
- attr_reader :thread
+ def self.handle_timeout(message) # :nodoc:
+ exc = ExitException.new(message)
- def self.catch(*args)
- exc = new(*args)
- exc.instance_variable_set(:@thread, Thread.current)
- exc.instance_variable_set(:@catch_value, exc)
- ::Kernel.catch(exc) {yield exc}
- end
-
- def exception(*)
- # TODO: use Fiber.current to see if self can be thrown
- if self.thread == Thread.current
- bt = caller
- begin
- throw(@catch_value, bt)
- rescue UncaughtThrowError
- end
+ begin
+ yield exc
+ rescue ExitException => e
+ raise new(message) if exc.equal?(e)
+ raise
end
- super
end
end
@@ -195,8 +193,7 @@ module Timeout
if klass
perform.call(klass)
else
- backtrace = Error.catch(&perform)
- raise Error, message, backtrace
+ Error.handle_timeout(message, &perform)
end
end
module_function :timeout
diff --git a/lib/tmpdir.gemspec b/lib/tmpdir.gemspec
index a480b9c018..4c96e5984b 100644
--- a/lib/tmpdir.gemspec
+++ b/lib/tmpdir.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "tmpdir"
- spec.version = "0.1.3"
+ spec.version = "0.2.0"
spec.authors = ["Yukihiro Matsumoto"]
spec.email = ["matz@ruby-lang.org"]
@@ -15,8 +15,12 @@ Gem::Specification.new do |spec|
# Specify which files should be added to the gem when it is released.
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
- `git ls-files -z 2>#{IO::NULL}`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
+ gemspec = File.basename(__FILE__)
+ spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL, exception: false) do |ls|
+ ls.readlines("\x0", chomp: true).reject do |f|
+ (f == gemspec) ||
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git Gemfile])
+ end
end
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
diff --git a/lib/tmpdir.rb b/lib/tmpdir.rb
index 55920a4a74..fe3e0e19d1 100644
--- a/lib/tmpdir.rb
+++ b/lib/tmpdir.rb
@@ -13,15 +13,23 @@ end
class Dir
- @@systmpdir ||= defined?(Etc.systmpdir) ? Etc.systmpdir : '/tmp'
+ # Class variables are inaccessible from non-main Ractor.
+ # And instance variables too, in Ruby 3.0.
+
+ # System-wide temporary directory path
+ SYSTMPDIR = (defined?(Etc.systmpdir) ? Etc.systmpdir.freeze : '/tmp')
+ private_constant :SYSTMPDIR
##
# Returns the operating system's temporary file path.
+ #
+ # require 'tmpdir'
+ # Dir.tmpdir # => "/tmp"
def self.tmpdir
- ['TMPDIR', 'TMP', 'TEMP', ['system temporary path', @@systmpdir], ['/tmp']*2, ['.']*2].find do |name, dir|
+ ['TMPDIR', 'TMP', 'TEMP', ['system temporary path', SYSTMPDIR], ['/tmp']*2, ['.']*2].find do |name, dir|
unless dir
- next if !(dir = ENV[name]) or dir.empty?
+ next if !(dir = ENV[name] rescue next) or dir.empty?
end
dir = File.expand_path(dir)
stat = File.stat(dir) rescue next
@@ -40,6 +48,11 @@ class Dir
# Dir.mktmpdir creates a temporary directory.
#
+ # require 'tmpdir'
+ # Dir.mktmpdir {|dir|
+ # # use the directory
+ # }
+ #
# The directory is created with 0700 permission.
# Application should not change the permission to make the temporary directory accessible from other users.
#
@@ -94,9 +107,10 @@ class Dir
yield path.dup
ensure
unless base
- stat = File.stat(File.dirname(path))
+ base = File.dirname(path)
+ stat = File.stat(base)
if stat.world_writable? and !stat.sticky?
- raise ArgumentError, "parent directory is world writable but not sticky"
+ raise ArgumentError, "parent directory is world writable but not sticky: #{base}"
end
end
FileUtils.remove_entry path
@@ -118,16 +132,17 @@ class Dir
UNUSABLE_CHARS = "^,-.0-9A-Z_a-z~"
# Dedicated random number generator
- RANDOM = Random.new
+ RANDOM = Object.new
class << RANDOM # :nodoc:
# Maximum random number
MAX = 36**6 # < 0x100000000
# Returns new random string upto 6 bytes
def next
- rand(MAX).to_s(36)
+ (::Random.urandom(4).unpack1("L")%MAX).to_s(36)
end
end
+ RANDOM.freeze
private_constant :RANDOM
# Generates and yields random names to create a temporary name
@@ -152,7 +167,7 @@ class Dir
n ||= 0
n += 1
retry if !max_try or n < max_try
- raise "cannot generate temporary name using `#{basename}' under `#{tmpdir}'"
+ raise "cannot generate temporary name using '#{basename}' under '#{tmpdir}'"
end
path
end
diff --git a/lib/tsort.rb b/lib/tsort.rb
index 3a2a91a7b4..dbaed45415 100644
--- a/lib/tsort.rb
+++ b/lib/tsort.rb
@@ -123,7 +123,7 @@
module TSort
- VERSION = "0.1.1"
+ VERSION = "0.2.0"
class Cyclic < StandardError
end
diff --git a/lib/un.rb b/lib/un.rb
index 632960bf53..8fb3c61a93 100644
--- a/lib/un.rb
+++ b/lib/un.rb
@@ -30,7 +30,6 @@ require "fileutils"
require "optparse"
module FileUtils
-# @fileutils_label = ""
@fileutils_output = $stdout
end
@@ -413,7 +412,7 @@ end
module UN # :nodoc:
- VERSION = "0.2.1"
+ VERSION = "0.3.0"
module_function
def help(argv, output: $stdout)
diff --git a/lib/uri.rb b/lib/uri.rb
index 59a7c4ad28..dfdb052a79 100644
--- a/lib/uri.rb
+++ b/lib/uri.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: false
# URI is a module providing classes to handle Uniform Resource Identifiers
-# (RFC2396[http://tools.ietf.org/html/rfc2396]).
+# (RFC2396[https://www.rfc-editor.org/rfc/rfc2396]).
#
# == Features
#
@@ -47,14 +47,14 @@
# A good place to view an RFC spec is http://www.ietf.org/rfc.html.
#
# Here is a list of all related RFC's:
-# - RFC822[http://tools.ietf.org/html/rfc822]
-# - RFC1738[http://tools.ietf.org/html/rfc1738]
-# - RFC2255[http://tools.ietf.org/html/rfc2255]
-# - RFC2368[http://tools.ietf.org/html/rfc2368]
-# - RFC2373[http://tools.ietf.org/html/rfc2373]
-# - RFC2396[http://tools.ietf.org/html/rfc2396]
-# - RFC2732[http://tools.ietf.org/html/rfc2732]
-# - RFC3986[http://tools.ietf.org/html/rfc3986]
+# - RFC822[https://www.rfc-editor.org/rfc/rfc822]
+# - RFC1738[https://www.rfc-editor.org/rfc/rfc1738]
+# - RFC2255[https://www.rfc-editor.org/rfc/rfc2255]
+# - RFC2368[https://www.rfc-editor.org/rfc/rfc2368]
+# - RFC2373[https://www.rfc-editor.org/rfc/rfc2373]
+# - RFC2396[https://www.rfc-editor.org/rfc/rfc2396]
+# - RFC2732[https://www.rfc-editor.org/rfc/rfc2732]
+# - RFC3986[https://www.rfc-editor.org/rfc/rfc3986]
#
# == Class tree
#
diff --git a/lib/uri/ftp.rb b/lib/uri/ftp.rb
index abad613c33..1c75e242ba 100644
--- a/lib/uri/ftp.rb
+++ b/lib/uri/ftp.rb
@@ -17,7 +17,7 @@ module URI
# This class will be redesigned because of difference of implementations;
# the structure of its path. draft-hoffman-ftp-uri-04 is a draft but it
# is a good summary about the de facto spec.
- # http://tools.ietf.org/html/draft-hoffman-ftp-uri-04
+ # https://datatracker.ietf.org/doc/html/draft-hoffman-ftp-uri-04
#
class FTP < Generic
# A Default port of 21 for URI::FTP.
diff --git a/lib/uri/generic.rb b/lib/uri/generic.rb
index 69698c4e2d..bdd366661e 100644
--- a/lib/uri/generic.rb
+++ b/lib/uri/generic.rb
@@ -945,7 +945,7 @@ module URI
# == Description
#
# URI has components listed in order of decreasing significance from left to right,
- # see RFC3986 https://tools.ietf.org/html/rfc3986 1.2.3.
+ # see RFC3986 https://www.rfc-editor.org/rfc/rfc3986 1.2.3.
#
# == Usage
#
@@ -1364,6 +1364,9 @@ module URI
str << ':'
str << @port.to_s
end
+ if (@host || @port) && !@path.empty? && !@path.start_with?('/')
+ str << '/'
+ end
str << @path
if @query
str << '?'
@@ -1376,6 +1379,7 @@ module URI
end
str
end
+ alias to_str to_s
#
# Compares two URIs.
@@ -1398,19 +1402,6 @@ module URI
self.component_ary.eql?(oth.component_ary)
end
-=begin
-
---- URI::Generic#===(oth)
-
-=end
-# def ===(oth)
-# raise NotImplementedError
-# end
-
-=begin
-=end
-
-
# Returns an Array of the components defined from the COMPONENT Array.
def component_ary
component.collect do |x|
diff --git a/lib/uri/http.rb b/lib/uri/http.rb
index 306daf1965..900b132c8c 100644
--- a/lib/uri/http.rb
+++ b/lib/uri/http.rb
@@ -85,7 +85,7 @@ module URI
# == Description
#
# Returns the authority for an HTTP uri, as defined in
- # https://datatracker.ietf.org/doc/html/rfc3986/#section-3.2.
+ # https://www.rfc-editor.org/rfc/rfc3986#section-3.2.
#
#
# Example:
@@ -106,7 +106,7 @@ module URI
# == Description
#
# Returns the origin for an HTTP uri, as defined in
- # https://datatracker.ietf.org/doc/html/rfc6454.
+ # https://www.rfc-editor.org/rfc/rfc6454.
#
#
# Example:
diff --git a/lib/uri/rfc2396_parser.rb b/lib/uri/rfc2396_parser.rb
index 76a8f99fd4..00c66cf042 100644
--- a/lib/uri/rfc2396_parser.rb
+++ b/lib/uri/rfc2396_parser.rb
@@ -497,8 +497,8 @@ module URI
ret = {}
# for URI::split
- ret[:ABS_URI] = Regexp.new('\A\s*' + pattern[:X_ABS_URI] + '\s*\z', Regexp::EXTENDED)
- ret[:REL_URI] = Regexp.new('\A\s*' + pattern[:X_REL_URI] + '\s*\z', Regexp::EXTENDED)
+ ret[:ABS_URI] = Regexp.new('\A\s*+' + pattern[:X_ABS_URI] + '\s*\z', Regexp::EXTENDED)
+ ret[:REL_URI] = Regexp.new('\A\s*+' + pattern[:X_REL_URI] + '\s*\z', Regexp::EXTENDED)
# for URI::extract
ret[:URI_REF] = Regexp.new(pattern[:URI_REF])
diff --git a/lib/uri/rfc3986_parser.rb b/lib/uri/rfc3986_parser.rb
index dd24a409ea..092a1ac89d 100644
--- a/lib/uri/rfc3986_parser.rb
+++ b/lib/uri/rfc3986_parser.rb
@@ -1,9 +1,73 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module URI
class RFC3986_Parser # :nodoc:
# URI defined in RFC3986
- RFC3986_URI = /\A(?<URI>(?<scheme>[A-Za-z][+\-.0-9A-Za-z]*+):(?<hier-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*+))(?::(?<port>\d*+))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g<segment>)*+)?)|(?<path-rootless>\g<segment-nz>(?:\/\g<segment>)*+)|(?<path-empty>))(?:\?(?<query>[^#]*+))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/
- RFC3986_relative_ref = /\A(?<relative-ref>(?<relative-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])++))?(?::(?<port>\d*+))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g<segment>)*+)?)|(?<path-noscheme>(?<segment-nz-nc>(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])++)(?:\/\g<segment>)*+)|(?<path-empty>))(?:\?(?<query>[^#]*+))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/
+ HOST = %r[
+ (?<IP-literal>\[(?:
+ (?<IPv6address>
+ (?:\h{1,4}:){6}
+ (?<ls32>\h{1,4}:\h{1,4}
+ | (?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)
+ \.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>)
+ )
+ | ::(?:\h{1,4}:){5}\g<ls32>
+ | \h{1,4}?::(?:\h{1,4}:){4}\g<ls32>
+ | (?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>
+ | (?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>
+ | (?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>
+ | (?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>
+ | (?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}
+ | (?:(?:\h{1,4}:){,6}\h{1,4})?::
+ )
+ | (?<IPvFuture>v\h++\.[!$&-.0-9:;=A-Z_a-z~]++)
+ )\])
+ | \g<IPv4address>
+ | (?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*+)
+ ]x
+
+ USERINFO = /(?:%\h\h|[!$&-.0-9:;=A-Z_a-z~])*+/
+
+ SCHEME = %r[[A-Za-z][+\-.0-9A-Za-z]*+].source
+ SEG = %r[(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/])].source
+ SEG_NC = %r[(?:%\h\h|[!$&-.0-9;=@A-Z_a-z~])].source
+ FRAGMENT = %r[(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/?])*+].source
+
+ RFC3986_URI = %r[\A
+ (?<seg>#{SEG}){0}
+ (?<URI>
+ (?<scheme>#{SCHEME}):
+ (?<hier-part>//
+ (?<authority>
+ (?:(?<userinfo>#{USERINFO.source})@)?
+ (?<host>#{HOST.source.delete(" \n")})
+ (?::(?<port>\d*+))?
+ )
+ (?<path-abempty>(?:/\g<seg>*+)?)
+ | (?<path-absolute>/((?!/)\g<seg>++)?)
+ | (?<path-rootless>(?!/)\g<seg>++)
+ | (?<path-empty>)
+ )
+ (?:\?(?<query>[^\#]*+))?
+ (?:\#(?<fragment>#{FRAGMENT}))?
+ )\z]x
+
+ RFC3986_relative_ref = %r[\A
+ (?<seg>#{SEG}){0}
+ (?<relative-ref>
+ (?<relative-part>//
+ (?<authority>
+ (?:(?<userinfo>#{USERINFO.source})@)?
+ (?<host>#{HOST.source.delete(" \n")}(?<!/))?
+ (?::(?<port>\d*+))?
+ )
+ (?<path-abempty>(?:/\g<seg>*+)?)
+ | (?<path-absolute>/\g<seg>*+)
+ | (?<path-noscheme>#{SEG_NC}++(?:/\g<seg>*+)?)
+ | (?<path-empty>)
+ )
+ (?:\?(?<query>[^#]*+))?
+ (?:\#(?<fragment>#{FRAGMENT}))?
+ )\z]x
attr_reader :regexp
def initialize
@@ -19,9 +83,9 @@ module URI
uri.ascii_only? or
raise InvalidURIError, "URI must be ascii only #{uri.dump}"
if m = RFC3986_URI.match(uri)
- query = m["query".freeze]
- scheme = m["scheme".freeze]
- opaque = m["path-rootless".freeze]
+ query = m["query"]
+ scheme = m["scheme"]
+ opaque = m["path-rootless"]
if opaque
opaque << "?#{query}" if query
[ scheme,
@@ -32,35 +96,35 @@ module URI
nil, # path
opaque,
nil, # query
- m["fragment".freeze]
+ m["fragment"]
]
else # normal
[ scheme,
- m["userinfo".freeze],
- m["host".freeze],
- m["port".freeze],
+ m["userinfo"],
+ m["host"],
+ m["port"],
nil, # registry
- (m["path-abempty".freeze] ||
- m["path-absolute".freeze] ||
- m["path-empty".freeze]),
+ (m["path-abempty"] ||
+ m["path-absolute"] ||
+ m["path-empty"]),
nil, # opaque
query,
- m["fragment".freeze]
+ m["fragment"]
]
end
elsif m = RFC3986_relative_ref.match(uri)
[ nil, # scheme
- m["userinfo".freeze],
- m["host".freeze],
- m["port".freeze],
+ m["userinfo"],
+ m["host"],
+ m["port"],
nil, # registry,
- (m["path-abempty".freeze] ||
- m["path-absolute".freeze] ||
- m["path-noscheme".freeze] ||
- m["path-empty".freeze]),
+ (m["path-abempty"] ||
+ m["path-absolute"] ||
+ m["path-noscheme"] ||
+ m["path-empty"]),
nil, # opaque
- m["query".freeze],
- m["fragment".freeze]
+ m["query"],
+ m["fragment"]
]
else
raise InvalidURIError, "bad URI(is not URI?): #{uri.inspect}"
@@ -92,15 +156,15 @@ module URI
def default_regexp # :nodoc:
{
- SCHEME: /\A[A-Za-z][A-Za-z0-9+\-.]*\z/,
- USERINFO: /\A(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*\z/,
- HOST: /\A(?:(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{,4}::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*))\z/,
- ABS_PATH: /\A\/(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*(?:\/(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*)*\z/,
- REL_PATH: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+(?:\/(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*)*\z/,
- QUERY: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/,
- FRAGMENT: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/,
- OPAQUE: /\A(?:[^\/].*)?\z/,
- PORT: /\A[\x09\x0a\x0c\x0d ]*\d*[\x09\x0a\x0c\x0d ]*\z/,
+ SCHEME: %r[\A#{SCHEME}\z]o,
+ USERINFO: %r[\A#{USERINFO}\z]o,
+ HOST: %r[\A#{HOST}\z]o,
+ ABS_PATH: %r[\A/#{SEG}*+\z]o,
+ REL_PATH: %r[\A(?!/)#{SEG}++\z]o,
+ QUERY: %r[\A(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/?])*+\z],
+ FRAGMENT: %r[\A#{FRAGMENT}\z]o,
+ OPAQUE: %r[\A(?:[^/].*)?\z],
+ PORT: /\A[\x09\x0a\x0c\x0d ]*+\d*[\x09\x0a\x0c\x0d ]*\z/,
}
end
diff --git a/lib/uri/version.rb b/lib/uri/version.rb
index 7497a7d31a..2dafa57d59 100644
--- a/lib/uri/version.rb
+++ b/lib/uri/version.rb
@@ -1,6 +1,6 @@
module URI
# :stopdoc:
- VERSION_CODE = '001201'.freeze
+ VERSION_CODE = '001300'.freeze
VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.').freeze
# :startdoc:
end
diff --git a/lib/weakref.rb b/lib/weakref.rb
index 2bbadf68f9..a8da39a26a 100644
--- a/lib/weakref.rb
+++ b/lib/weakref.rb
@@ -17,7 +17,7 @@ require "delegate"
#
class WeakRef < Delegator
- VERSION = "0.1.2"
+ VERSION = "0.1.3"
##
# RefError is raised when a referenced object has been recycled by the
diff --git a/lib/yaml.rb b/lib/yaml.rb
index 3b2a80f1df..b2669899dd 100644
--- a/lib/yaml.rb
+++ b/lib/yaml.rb
@@ -66,5 +66,5 @@ YAML = Psych # :nodoc:
#
# Syck can also be found on github: https://github.com/ruby/syck
module YAML
- LOADER_VERSION = "0.2.1"
+ LOADER_VERSION = "0.3.0"
end
diff --git a/lib/yaml/store.rb b/lib/yaml/store.rb
index f8650b942f..f24e4ad332 100644
--- a/lib/yaml/store.rb
+++ b/lib/yaml/store.rb
@@ -3,7 +3,11 @@
# YAML::Store
#
require 'yaml'
-require 'pstore'
+
+begin
+ require 'pstore'
+rescue LoadError
+end
# YAML::Store provides the same functionality as PStore, except it uses YAML
# to dump objects instead of Marshal.
@@ -65,7 +69,7 @@ class YAML::Store < PStore
end
def load(content)
- table = YAML.unsafe_load(content)
+ table = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(content) : YAML.load(content)
if table == false
{}
else
@@ -83,4 +87,4 @@ class YAML::Store < PStore
def empty_marshal_checksum
CHECKSUM_ALGO.digest(empty_marshal_data)
end
-end
+end if defined?(::PStore)
diff --git a/libexec/bundle b/libexec/bundle
index 90c62627f8..92387bc2cf 100755
--- a/libexec/bundle
+++ b/libexec/bundle
@@ -15,15 +15,6 @@ end
require "bundler"
-if Gem.rubygems_version < Gem::Version.new("3.2.3") && Gem.ruby_version < Gem::Version.new("2.7.a") && !ENV["BUNDLER_NO_OLD_RUBYGEMS_WARNING"]
- Bundler.ui.warn \
- "Your RubyGems version (#{Gem::VERSION}) has a bug that prevents " \
- "`required_ruby_version` from working for Bundler. Any scripts that use " \
- "`gem install bundler` will break as soon as Bundler drops support for " \
- "your Ruby version. Please upgrade RubyGems to avoid future breakage " \
- "and silence this warning by running `gem update --system 3.2.3`"
-end
-
require "bundler/friendly_errors"
Bundler.with_friendly_errors do
@@ -34,5 +25,5 @@ Bundler.with_friendly_errors do
help_flag_used = ARGV.any? {|a| help_flags.include? a }
args = help_flag_used ? Bundler::CLI.reformatted_help_args(ARGV) : ARGV
- Bundler::CLI.start(args, :debug => true)
+ Bundler::CLI.start(args, debug: true)
end
diff --git a/libexec/racc b/libexec/racc
deleted file mode 100755
index 4507d04962..0000000000
--- a/libexec/racc
+++ /dev/null
@@ -1,320 +0,0 @@
-#!/usr/bin/env ruby
-#
-#
-#
-# Copyright (c) 1999-2006 Minero Aoki
-#
-# This program is free software.
-# You can distribute/modify this program under the same terms of ruby.
-# see the file "COPYING".
-
-require 'racc/static'
-require 'optparse'
-
-def main
- output = nil
- debug_parser = false
- make_logfile = false
- logfilename = nil
- make_executable = false
- rubypath = nil
- embed_runtime = false
- debug_flags = Racc::DebugFlags.new
- line_convert = true
- line_convert_all = false
- omit_action_call = true
- superclass = nil
- check_only = false
- verbose = false
- profiler = RaccProfiler.new(false)
-
- parser = OptionParser.new
- parser.banner = "Usage: #{File.basename($0)} [options] <input>"
- parser.on('-o', '--output-file=PATH',
- 'output file name [<input>.tab.rb]') {|name|
- output = name
- }
- parser.on('-t', '--debug', 'Outputs debugging parser.') {|fl|
- debug_parser = fl
- }
- parser.on('-g', 'Equivalent to -t (obsolete).') {|fl|
- $stderr.puts "racc -g is obsolete. Use racc -t instead." if $VERBOSE
- debug_parser = fl
- }
- parser.on('-v', '--verbose',
- 'Creates <filename>.output log file.') {|fl|
- make_logfile = fl
- }
- parser.on('-O', '--log-file=PATH',
- 'Log file name [<input>.output]') {|path|
- make_logfile = true
- logfilename = path
- }
- parser.on('-e', '--executable [RUBYPATH]', 'Makes executable parser.') {|path|
- make_executable = true
- rubypath = (path == 'ruby' ? nil : path)
- }
- parser.on('-E', '--embedded', "Embeds Racc runtime in output.") {
- embed_runtime = true
- }
- parser.on('--line-convert-all', 'Converts line numbers of user codes.') {
- line_convert_all = true
- }
- parser.on('-l', '--no-line-convert', 'Never convert line numbers.') {
- line_convert = false
- line_convert_all = false
- }
- parser.on('-a', '--no-omit-actions', 'Never omit actions.') {
- omit_action_call = false
- }
- parser.on('--superclass=CLASSNAME',
- 'Uses CLASSNAME instead of Racc::Parser.') {|name|
- superclass = name
- }
- parser.on('-C', '--check-only', 'Checks syntax and quit immediately.') {|fl|
- check_only = fl
- }
- parser.on('-S', '--output-status', 'Outputs internal status time to time.') {
- verbose = true
- }
- parser.on('-P', 'Enables generator profile') {
- profiler = RaccProfiler.new(true)
- }
- parser.on('-D flags', "Flags for Racc debugging (do not use).") {|flags|
- debug_flags = Racc::DebugFlags.parse_option_string(flags)
- }
- #parser.on('--no-extensions', 'Run Racc without any Ruby extension.') {
- # Racc.const_set :Racc_No_Extensions, true
- #}
- parser.on('--version', 'Prints version and quit.') {
- puts "racc version #{Racc::Version}"
- exit 0
- }
- parser.on('--runtime-version', 'Prints runtime version and quit.') {
- printf "racc runtime version %s; %s\n",
- Racc::Parser::Racc_Runtime_Version,
- if Racc::Parser.racc_runtime_type == 'ruby'
- sprintf('ruby core version %s',
- Racc::Parser::Racc_Runtime_Core_Version_R)
- else
- sprintf('c core version %s',
- Racc::Parser::Racc_Runtime_Core_Version_C)
- end
- exit 0
- }
- parser.on('--copyright', 'Prints copyright and quit.') {
- puts Racc::Copyright
- exit 0
- }
- parser.on('--help', 'Prints this message and quit.') {
- puts parser.help
- exit 1
- }
- begin
- parser.parse!
- rescue OptionParser::ParseError => err
- $stderr.puts err.message
- $stderr.puts parser.help
- exit 1
- end
- if ARGV.empty?
- $stderr.puts 'no input'
- exit 1
- end
- if ARGV.size > 1
- $stderr.puts 'too many input'
- exit 1
- end
- input = ARGV[0]
-
- begin
- $stderr.puts 'Parsing grammar file...' if verbose
- result = profiler.section('parse') {
- parser = Racc::GrammarFileParser.new(debug_flags)
- parser.parse(File.read(input), File.basename(input))
- }
- if check_only
- $stderr.puts 'syntax ok'
- exit 0
- end
-
- $stderr.puts 'Generating LALR states...' if verbose
- states = profiler.section('nfa') {
- Racc::States.new(result.grammar).nfa
- }
-
- $stderr.puts "Resolving #{states.size} states..." if verbose
- profiler.section('dfa') {
- states.dfa
- }
-
- $stderr.puts 'Creating parser file...' if verbose
- params = result.params.dup
- # Overwrites parameters given by a grammar file with command line options.
- params.superclass = superclass if superclass
- params.omit_action_call = true if omit_action_call
- # From command line option
- if make_executable
- params.make_executable = true
- params.interpreter = rubypath
- end
- params.debug_parser = debug_parser
- params.convert_line = line_convert
- params.convert_line_all = line_convert_all
- params.embed_runtime = embed_runtime
- profiler.section('generation') {
- generator = Racc::ParserFileGenerator.new(states, params)
- generator.generate_parser_file(output || make_filename(input, '.tab.rb'))
- }
-
- if make_logfile
- profiler.section('logging') {
- $stderr.puts 'Creating log file...' if verbose
- logfilename ||= make_filename(output || File.basename(input), '.output')
- File.open(logfilename, 'w') {|f|
- Racc::LogFileGenerator.new(states, debug_flags).output f
- }
- }
- end
- if debug_flags.status_logging
- log_useless states.grammar
- log_conflict states
- else
- has_useless = report_useless states.grammar
- has_conflicts = report_conflict states
- if has_useless || has_conflicts
- preamble = make_logfile ? 'C' : 'Turn on logging with "-v" and c'
- $stderr.puts %Q{#{preamble}heck ".output" file for details}
- end
- end
-
- profiler.report
- rescue Racc::Error, Errno::ENOENT, Errno::EPERM => err
- raise if $DEBUG or debug_flags.any?
- lineno = err.message.slice(/\A\d+:/).to_s
- $stderr.puts "#{File.basename $0}: #{input}:#{lineno} #{err.message.strip}"
- exit 1
- end
-end
-
-def make_filename(path, suffix)
- path.sub(/(?:\..*?)?\z/, suffix)
-end
-
-LIST_LIMIT = 10
-def report_list(enum, label)
- c = enum.count
- if c > 0
- $stderr.puts "#{c} #{label}:"
- enum.first(LIST_LIMIT).each do |item|
- $stderr.puts " #{yield item}"
- end
- $stderr.puts " ..." if c > LIST_LIMIT
- end
-end
-
-# @return [Boolean] if anything was reported
-def report_conflict(states)
- if states.should_report_srconflict?
- reported = true
- $stderr.puts "#{states.n_srconflicts} shift/reduce conflicts"
- end
- if states.rrconflict_exist?
- reported = true
- $stderr.puts "#{states.n_rrconflicts} reduce/reduce conflicts"
- end
- reported
-end
-
-def log_conflict(states)
- logging('w') {|f|
- f.puts "ex#{states.grammar.n_expected_srconflicts}"
- if states.should_report_srconflict?
- f.puts "sr#{states.n_srconflicts}"
- end
- if states.rrconflict_exist?
- f.puts "rr#{states.n_rrconflicts}"
- end
- }
-end
-
-# @return [Boolean] if anything was reported
-def report_useless(grammar)
- reported = report_list(grammar.each_useless_nonterminal, 'useless nonterminals', &:to_s)
-
- reported ||= report_list(grammar.each_useless_rule, 'useless rules') { |r| "##{r.ident} (#{r.target})" }
-
- if grammar.start.useless?
- $stderr.puts 'fatal: start symbol does not derive any sentence'
- reported = true
- end
- reported
-end
-
-def log_useless(grammar)
- logging('a') {|f|
- if grammar.useless_nonterminal_exist?
- f.puts "un#{grammar.n_useless_nonterminals}"
- end
- if grammar.useless_rule_exist?
- f.puts "ur#{grammar.n_useless_rules}"
- end
- }
-end
-
-def logging(mode, &block)
- File.open("log/#{File.basename(ARGV[0])}", mode, &block)
-end
-
-class RaccProfiler
- def initialize(really)
- @really = really
- @log = []
- unless ::Process.respond_to?(:times)
- # Ruby 1.6
- @class = ::Time
- else
- @class = ::Process
- end
- end
-
- def section(name)
- if @really
- t1 = @class.times.utime
- result = yield
- t2 = @class.times.utime
- @log.push [name, t2 - t1]
- result
- else
- yield
- end
- end
-
- def report
- return unless @really
- f = $stderr
- total = cumulative_time()
- f.puts '--task-----------+--sec------+---%-'
- @log.each do |name, time|
- f.printf "%-19s %s %3d%%\n", name, pjust(time,4,4), (time/total*100).to_i
- end
- f.puts '-----------------+-----------+-----'
- f.printf "%-20s%s\n", 'total', pjust(total,4,4)
- end
-
- private
-
- def cumulative_time
- t = @log.inject(0) {|sum, (name, time)| sum + time }
- t == 0 ? 0.01 : t
- end
-
- def pjust(num, i, j)
- m = /(\d+)(\.\d+)?/.match(num.to_s)
- str = m[1].rjust(i)
- str.concat m[2].ljust(j+1)[0,j+1] if m[2]
- str
- end
-end
-
-main
diff --git a/load.c b/load.c
index da81becfc1..1309d566b9 100644
--- a/load.c
+++ b/load.c
@@ -8,8 +8,9 @@
#include "internal/dir.h"
#include "internal/error.h"
#include "internal/file.h"
+#include "internal/hash.h"
#include "internal/load.h"
-#include "internal/parse.h"
+#include "internal/ruby_parser.h"
#include "internal/thread.h"
#include "internal/variable.h"
#include "iseq.h"
@@ -18,12 +19,27 @@
#include "ruby/encoding.h"
#include "ruby/util.h"
-static VALUE ruby_dln_librefs;
+static VALUE ruby_dln_libmap;
#define IS_RBEXT(e) (strcmp((e), ".rb") == 0)
#define IS_SOEXT(e) (strcmp((e), ".so") == 0 || strcmp((e), ".o") == 0)
#define IS_DLEXT(e) (strcmp((e), DLEXT) == 0)
+#if SIZEOF_VALUE <= SIZEOF_LONG
+# define SVALUE2NUM(x) LONG2NUM((long)(x))
+# define NUM2SVALUE(x) (SIGNED_VALUE)NUM2LONG(x)
+#elif SIZEOF_VALUE <= SIZEOF_LONG_LONG
+# define SVALUE2NUM(x) LL2NUM((LONG_LONG)(x))
+# define NUM2SVALUE(x) (SIGNED_VALUE)NUM2LL(x)
+#else
+# error Need integer for VALUE
+#endif
+
+enum {
+ loadable_ext_rb = (0+ /* .rb extension is the first in both tables */
+ 1) /* offset by rb_find_file_ext() */
+};
+
static const char *const loadable_ext[] = {
".rb", DLEXT,
0
@@ -237,9 +253,9 @@ features_index_add_single_callback(st_data_t *key, st_data_t *value, st_data_t r
rb_darray_set(feature_indexes, top^0, FIX2LONG(this_feature_index));
rb_darray_set(feature_indexes, top^1, FIX2LONG(offset));
- assert(rb_darray_size(feature_indexes) == 2);
+ RUBY_ASSERT(rb_darray_size(feature_indexes) == 2);
// assert feature_indexes does not look like a special const
- assert(!SPECIAL_CONST_P((VALUE)feature_indexes));
+ RUBY_ASSERT(!SPECIAL_CONST_P((VALUE)feature_indexes));
*value = (st_data_t)feature_indexes;
}
@@ -355,6 +371,13 @@ loaded_features_index_clear_i(st_data_t key, st_data_t val, st_data_t arg)
return ST_DELETE;
}
+void
+rb_free_loaded_features_index(rb_vm_t *vm)
+{
+ st_foreach(vm->loaded_features_index, loaded_features_index_clear_i, 0);
+ st_free_table(vm->loaded_features_index);
+}
+
static st_table *
get_loaded_features_index(rb_vm_t *vm)
{
@@ -475,6 +498,12 @@ loaded_feature_path_i(st_data_t v, st_data_t b, st_data_t f)
return ST_STOP;
}
+/*
+ * Returns the type of already provided feature.
+ * 'r': ruby script (".rb")
+ * 's': shared object (".so"/"."DLEXT)
+ * 'u': unsuffixed
+ */
static int
rb_feature_p(rb_vm_t *vm, const char *feature, const char *ext, int rb, int expanded, const char **fn)
{
@@ -689,6 +718,19 @@ rb_provide(const char *feature)
NORETURN(static void load_failed(VALUE));
+static inline VALUE
+realpath_internal_cached(VALUE hash, VALUE path)
+{
+ VALUE ret = rb_hash_aref(hash, path);
+ if(RTEST(ret)) {
+ return ret;
+ }
+
+ VALUE realpath = rb_realpath_internal(Qnil, path, 1);
+ rb_hash_aset(hash, rb_fstring(path), rb_fstring(realpath));
+ return realpath;
+}
+
static inline void
load_iseq_eval(rb_execution_context_t *ec, VALUE fname)
{
@@ -697,13 +739,40 @@ load_iseq_eval(rb_execution_context_t *ec, VALUE fname)
if (!iseq) {
rb_execution_context_t *ec = GET_EC();
VALUE v = rb_vm_push_frame_fname(ec, fname);
- rb_ast_t *ast;
- VALUE parser = rb_parser_new();
- rb_parser_set_context(parser, NULL, FALSE);
- ast = (rb_ast_t *)rb_parser_load_file(parser, fname);
- iseq = rb_iseq_new_top(&ast->body, rb_fstring_lit("<top (required)>"),
- fname, rb_realpath_internal(Qnil, fname, 1), NULL);
- rb_ast_dispose(ast);
+
+ rb_thread_t *th = rb_ec_thread_ptr(ec);
+ VALUE realpath_map = get_loaded_features_realpath_map(th->vm);
+
+ if (*rb_ruby_prism_ptr()) {
+ pm_parse_result_t result = { 0 };
+ result.options.line = 1;
+
+ VALUE error = pm_load_parse_file(&result, fname);
+
+ if (error == Qnil) {
+ iseq = pm_iseq_new_top(&result.node, rb_fstring_lit("<top (required)>"), fname, realpath_internal_cached(realpath_map, fname), NULL);
+ pm_parse_result_free(&result);
+ }
+ else {
+ rb_vm_pop_frame(ec);
+ RB_GC_GUARD(v);
+ pm_parse_result_free(&result);
+ rb_exc_raise(error);
+ }
+ }
+ else {
+ rb_ast_t *ast;
+ VALUE vast;
+ VALUE parser = rb_parser_new();
+ rb_parser_set_context(parser, NULL, FALSE);
+ vast = rb_parser_load_file(parser, fname);
+ ast = rb_ruby_ast_data_get(vast);
+
+ iseq = rb_iseq_new_top(vast, rb_fstring_lit("<top (required)>"),
+ fname, realpath_internal_cached(realpath_map, fname), NULL);
+ rb_ast_dispose(ast);
+ }
+
rb_vm_pop_frame(ec);
RB_GC_GUARD(v);
}
@@ -820,9 +889,8 @@ rb_load_protect(VALUE fname, int wrap, int *pstate)
* LoadError will be raised.
*
* If the optional _wrap_ parameter is +true+, the loaded script will
- * be executed under an anonymous module, protecting the calling
- * program's global namespace. If the optional _wrap_ parameter is a
- * module, the loaded script will be executed under the given module.
+ * be executed under an anonymous module. If the optional _wrap_ parameter
+ * is a module, the loaded script will be executed under the given module.
* In no circumstance will any local variables in the loaded file be
* propagated to the loading environment.
*/
@@ -907,6 +975,7 @@ load_unlock(rb_vm_t *vm, const char *ftptr, int done)
}
}
+static VALUE rb_require_string_internal(VALUE fname, bool resurrect);
/*
* call-seq:
@@ -921,15 +990,14 @@ load_unlock(rb_vm_t *vm, const char *ftptr, int done)
* If the filename starts with './' or '../', resolution is based on Dir.pwd.
*
* If the filename has the extension ".rb", it is loaded as a source file; if
- * the extension is ".so", ".o", or ".dll", or the default shared library
- * extension on the current platform, Ruby loads the shared library as a
- * Ruby extension. Otherwise, Ruby tries adding ".rb", ".so", and so on
- * to the name until found. If the file named cannot be found, a LoadError
- * will be raised.
+ * the extension is ".so", ".o", or the default shared library extension on
+ * the current platform, Ruby loads the shared library as a Ruby extension.
+ * Otherwise, Ruby tries adding ".rb", ".so", and so on to the name until
+ * found. If the file named cannot be found, a LoadError will be raised.
*
- * For Ruby extensions the filename given may use any shared library
- * extension. For example, on Linux the socket extension is "socket.so" and
- * <code>require 'socket.dll'</code> will load the socket extension.
+ * For Ruby extensions the filename given may use ".so" or ".o". For example,
+ * on macOS the socket extension is "socket.bundle" and
+ * <code>require 'socket.so'</code> will load the socket extension.
*
* The absolute path of the loaded file is added to
* <code>$LOADED_FEATURES</code> (<code>$"</code>). A file will not be
@@ -969,7 +1037,7 @@ rb_f_require_relative(VALUE obj, VALUE fname)
rb_loaderror("cannot infer basepath");
}
base = rb_file_dirname(base);
- return rb_require_string(rb_file_absolute_path(fname, base));
+ return rb_require_string_internal(rb_file_absolute_path(fname, base), false);
}
typedef int (*feature_func)(rb_vm_t *vm, const char *feature, const char *ext, int rb, int expanded, const char **fn);
@@ -979,7 +1047,7 @@ search_required(rb_vm_t *vm, VALUE fname, volatile VALUE *path, feature_func rb_
{
VALUE tmp;
char *ext, *ftptr;
- int type, ft = 0;
+ int ft = 0;
const char *loading;
*path = 0;
@@ -1031,11 +1099,11 @@ search_required(rb_vm_t *vm, VALUE fname, volatile VALUE *path, feature_func rb_
return 'r';
}
tmp = fname;
- type = rb_find_file_ext(&tmp, ft == 's' ? ruby_ext : loadable_ext);
+ const unsigned int type = rb_find_file_ext(&tmp, ft == 's' ? ruby_ext : loadable_ext);
// Check if it's a statically linked extension when
// not already a feature and not found as a dynamic library.
- if (!ft && type != 1 && vm->static_ext_inits) {
+ if (!ft && type != loadable_ext_rb && vm->static_ext_inits) {
VALUE lookup_name = tmp;
// Append ".so" if not already present so for example "etc" can find "etc.so".
// We always register statically linked extensions with a ".so" extension.
@@ -1063,13 +1131,13 @@ search_required(rb_vm_t *vm, VALUE fname, volatile VALUE *path, feature_func rb_
goto feature_present;
}
/* fall through */
- case 1:
+ case loadable_ext_rb:
ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
- if (rb_feature_p(vm, ftptr, ext, !--type, TRUE, &loading) && !loading)
+ if (rb_feature_p(vm, ftptr, ext, type == loadable_ext_rb, TRUE, &loading) && !loading)
break;
*path = tmp;
}
- return type ? 's' : 'r';
+ return type > loadable_ext_rb ? 's' : 'r';
feature_present:
if (loading) *path = rb_filesystem_str_new_cstr(loading);
@@ -1167,8 +1235,10 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa
rb_thread_t *th = rb_ec_thread_ptr(ec);
volatile const struct {
VALUE wrapper, self, errinfo;
+ rb_execution_context_t *ec;
} saved = {
th->top_wrapper, th->top_self, ec->errinfo,
+ ec,
};
enum ruby_tag_type state;
char *volatile ftptr = 0;
@@ -1180,7 +1250,6 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa
volatile bool reset_ext_config = false;
struct rb_ext_config prev_ext_config;
- fname = rb_get_path(fname);
path = rb_str_encode_ospath(fname);
RUBY_DTRACE_HOOK(REQUIRE_ENTRY, RSTRING_PTR(fname));
saved_path = path;
@@ -1189,7 +1258,7 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa
ec->errinfo = Qnil; /* ensure */
th->top_wrapper = 0;
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
- long handle;
+ VALUE handle;
int found;
RUBY_DTRACE_HOOK(FIND_REQUIRE_ENTRY, RSTRING_PTR(fname));
@@ -1208,7 +1277,7 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa
result = TAG_RETURN;
}
else if (RTEST(rb_hash_aref(realpaths,
- realpath = rb_realpath_internal(Qnil, path, 1)))) {
+ realpath = realpath_internal_cached(realpath_map, path)))) {
result = 0;
}
else {
@@ -1220,9 +1289,9 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa
case 's':
reset_ext_config = true;
ext_config_push(th, &prev_ext_config);
- handle = (long)rb_vm_call_cfunc(rb_vm_top_self(), load_ext,
- path, VM_BLOCK_HANDLER_NONE, path);
- rb_ary_push(ruby_dln_librefs, LONG2NUM(handle));
+ handle = rb_vm_call_cfunc(rb_vm_top_self(), load_ext,
+ path, VM_BLOCK_HANDLER_NONE, path);
+ rb_hash_aset(ruby_dln_libmap, path, SVALUE2NUM((SIGNED_VALUE)handle));
break;
}
result = TAG_RETURN;
@@ -1231,6 +1300,7 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa
}
EC_POP_TAG();
+ ec = saved.ec;
rb_thread_t *th2 = rb_ec_thread_ptr(ec);
th2->top_self = saved.self;
th2->top_wrapper = saved.wrapper;
@@ -1268,7 +1338,6 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa
if (real) {
real = rb_fstring(real);
rb_hash_aset(realpaths, real, Qtrue);
- rb_hash_aset(realpath_map, path, real);
}
}
ec->errinfo = saved.errinfo;
@@ -1306,6 +1375,12 @@ ruby_require_internal(const char *fname, unsigned int len)
VALUE
rb_require_string(VALUE fname)
{
+ return rb_require_string_internal(FilePathValue(fname), false);
+}
+
+static VALUE
+rb_require_string_internal(VALUE fname, bool resurrect)
+{
rb_execution_context_t *ec = GET_EC();
int result = require_internal(ec, fname, 1, RTEST(ruby_verbose));
@@ -1313,6 +1388,7 @@ rb_require_string(VALUE fname)
EC_JUMP_TAG(ec, result);
}
if (result < 0) {
+ if (resurrect) fname = rb_str_resurrect(fname);
load_failed(fname);
}
@@ -1322,7 +1398,9 @@ rb_require_string(VALUE fname)
VALUE
rb_require(const char *fname)
{
- return rb_require_string(rb_str_new_cstr(fname));
+ struct RString fake;
+ VALUE str = rb_setup_fake_str(&fake, fname, strlen(fname), 0);
+ return rb_require_string_internal(str, true);
}
static int
@@ -1456,10 +1534,22 @@ rb_f_autoload(VALUE obj, VALUE sym, VALUE file)
* autoload?(name, inherit=true) -> String or nil
*
* Returns _filename_ to be loaded if _name_ is registered as
- * +autoload+.
+ * +autoload+ in the current namespace or one of its ancestors.
*
* autoload(:B, "b")
* autoload?(:B) #=> "b"
+ *
+ * module C
+ * autoload(:D, "d")
+ * autoload?(:D) #=> "d"
+ * autoload?(:B) #=> nil
+ * end
+ *
+ * class E
+ * autoload(:F, "f")
+ * autoload?(:F) #=> "f"
+ * autoload?(:B) #=> "b"
+ * end
*/
static VALUE
@@ -1473,6 +1563,37 @@ rb_f_autoload_p(int argc, VALUE *argv, VALUE obj)
return rb_mod_autoload_p(argc, argv, klass);
}
+void *
+rb_ext_resolve_symbol(const char* fname, const char* symbol)
+{
+ VALUE handle;
+ VALUE resolved;
+ VALUE path;
+ char *ext;
+ VALUE fname_str = rb_str_new_cstr(fname);
+
+ resolved = rb_resolve_feature_path((VALUE)NULL, fname_str);
+ if (NIL_P(resolved)) {
+ ext = strrchr(fname, '.');
+ if (!ext || !IS_SOEXT(ext)) {
+ rb_str_cat_cstr(fname_str, ".so");
+ }
+ if (rb_feature_p(GET_VM(), fname, 0, FALSE, FALSE, 0)) {
+ return dln_symbol(NULL, symbol);
+ }
+ return NULL;
+ }
+ if (RARRAY_LEN(resolved) != 2 || rb_ary_entry(resolved, 0) != ID2SYM(rb_intern("so"))) {
+ return NULL;
+ }
+ path = rb_ary_entry(resolved, 1);
+ handle = rb_hash_lookup(ruby_dln_libmap, path);
+ if (NIL_P(handle)) {
+ return NULL;
+ }
+ return dln_symbol((void *)NUM2SVALUE(handle), symbol);
+}
+
void
Init_load(void)
{
@@ -1507,6 +1628,6 @@ Init_load(void)
rb_define_global_function("autoload", rb_f_autoload, 2);
rb_define_global_function("autoload?", rb_f_autoload_p, -1);
- ruby_dln_librefs = rb_ary_hidden_new(0);
- rb_gc_register_mark_object(ruby_dln_librefs);
+ ruby_dln_libmap = rb_hash_new_with_size(0);
+ rb_vm_register_global_object(ruby_dln_libmap);
}
diff --git a/main.c b/main.c
index 072dc56dd5..cbb294ba72 100644
--- a/main.c
+++ b/main.c
@@ -20,6 +20,7 @@
#undef RUBY_EXPORT
#include "ruby.h"
#include "vm_debug.h"
+#include "internal/sanitizers.h"
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
@@ -57,3 +58,14 @@ main(int argc, char **argv)
ruby_sysinit(&argc, &argv);
return rb_main(argc, argv);
}
+
+#ifdef RUBY_ASAN_ENABLED
+/* Compile in the ASAN options Ruby needs, rather than relying on environment variables, so
+ * that even tests which fork ruby with a clean environment will run ASAN with the right
+ * settings */
+const char *
+__asan_default_options(void)
+{
+ return "use_sigaltstack=0:detect_leaks=0";
+}
+#endif
diff --git a/man/irb.1 b/man/irb.1
index c589c99c78..93ef9b8f66 100644
--- a/man/irb.1
+++ b/man/irb.1
@@ -140,6 +140,13 @@ Use autocompletion.
Don't use autocompletion.
.Pp
.Pp
+.It Fl -regexp-completor
+Use regexp based completion.
+.Pp
+.It Fl -type-completor
+Use type based completion.
+.Pp
+.Pp
.It Fl -verbose
Show details.
.Pp
diff --git a/man/ruby.1 b/man/ruby.1
index 6c9f5fb0d1..3cfe605ce9 100644
--- a/man/ruby.1
+++ b/man/ruby.1
@@ -25,6 +25,7 @@
.Op Fl - Ns Bro Cm enable Ns | Ns Cm disable Brc Ns - Ns Ar FEATURE
.Op Fl -dump Ns = Ns Ar target
.Op Fl -verbose
+.Op Fl -crash-report Ns = Ns Ar template
.Op Fl -
.Op Ar program_file
.Op Ar argument ...
@@ -100,10 +101,10 @@ different character encodings, without dependence on Unicode.
.It Sy "Bignums"
With built-in bignums, you can for example calculate factorial(400).
.Pp
-.It Sy "Reflection and domain specific languages"
+.It Sy "Reflection and domain-specific languages"
Class is also an instance of the Class class. Definition of classes and methods
is an expression just as 1+1 is. So your programs can even write and modify programs.
-Thus you can write your application in your own programming language on top of Ruby.
+Thus, you can write your application in your own programming language on top of Ruby.
.Pp
.It Sy "Exception handling"
As in Java(tm).
@@ -271,6 +272,11 @@ In auto-split mode, Ruby executes
.Dl $F = $_.split
at beginning of each loop.
.Pp
+.It Fl -backtrace-limit Ns = Ns Ar num
+Limits the maximum length of backtraces to
+.Ar num
+lines (default -1, meaning no limit).
+.Pp
.It Fl c
Causes Ruby to check the syntax of the script and exit without
executing. If there are no syntax errors, Ruby will print
@@ -449,7 +455,7 @@ Enable compiler debug mode (same as
.It Sy parsetree
Print a textual representation of the Ruby AST for the program.
.It Sy parsetree_with_comment
-Print a textual representation of the Ruby AST for the program, but with each node annoted with the associated Ruby source code.
+Print a textual representation of the Ruby AST for the program, but with each node annotated with the associated Ruby source code.
.It Sy insns
Print a list of disassembled bytecode instructions.
.It Sy insns_without_opt
@@ -464,6 +470,12 @@ variable to true.
If this switch is given, and no script arguments (script file or
.Fl e
options) are present, Ruby quits immediately.
+.Pp
+.It Fl -crash-report Ns = Ns Ar template
+Sets the template of path name to save crash report.
+See
+.Ev RUBY_CRASH_REPORT
+environment variable for details.
.El
.Pp
.Sh ENVIRONMENT
@@ -525,7 +537,7 @@ malloc family of C standard library calls (
.Xr calloc 3 ,
and
.Xr realloc 3 ) .
-In this documentatation, the "heap" refers to the Ruby object heap
+In this documentation, the "heap" refers to the Ruby object heap
of fixed-sized slots, while "malloc" refers to auxiliary
allocations commonly referred to as the "process heap".
Thus there are at least two possible ways to trigger GC:
@@ -552,9 +564,9 @@ the following 11 environment variables:
.It Ev RUBY_GC_HEAP_INIT_SLOTS
Initial allocation slots. Applies to all slot sizes. Introduced in Ruby 2.1, default: 10000.
.Pp
-.It Ev RUBY_GC_HEAP_INIT_SIZE_%d_SLOTS
-Initial allocation of slots in a specific size pool.
-The available size pools can be found in `GC.stat_heap`.
+.It Ev RUBY_GC_HEAP_%d_INIT_SLOTS
+Initial allocation of slots in a specific heap.
+The available heaps can be found in the keys of `GC.stat_heap`.
Introduced in Ruby 3.3.
.Pp
.It Ev RUBY_GC_HEAP_FREE_SLOTS
@@ -640,6 +652,61 @@ Machine stack size used at fiber creation.
default: 262144 or 524288
.Pp
.El
+.Sh CRASH REPORT ENVIRONMENT
+.Pp
+.Bl -tag -compact -width "RUBY_CRASH_REPORT"
+.It Ev RUBY_CRASH_REPORT
+The template of path name to save crash report.
+default: none
+.El
+.Ss Naming crash report files
+The template can contain
+.Li \fB%\fP
+specifiers which are substituted by the following values when a crash
+report file is created:
+.Pp
+.Bl -hang -compact -width "%NNN"
+.It Li \fB%%\fP
+A single
+.Li \fB%\fP
+character.
+.It Li \fB%e\fP
+Basename of executable.
+.It Li \fB%E\fP
+Pathname of executable,
+with slashes (\fB/\fP) replaced by exclamation marks (\fB!\fP).
+.It Li \fB%f\fP
+Basename of the program name,
+.Li "$0" .
+.It Li \fB%F\fP
+Pathname of the program name,
+.Li "$0",
+with slashes (\fB/\fP) replaced by exclamation marks (\fB!\fP).
+.It Li \fB%p\fP
+PID of dumped process.
+.It Li \fB%t\fP
+Time of dump, expressed as seconds since the
+Epoch, 1970-01-01 00:00:00 +0000 (UTC).
+.It Li \fB%NNN\fP
+A character code in octal.
+.El
+.Pp
+A single
+.Li \fB%\fP
+at the end of the template is dropped from the core filename, as is
+the combination of a
+.Li \fB%\fP
+followed by any character other than those listed above. All other
+characters in the template become a literal part of the core filename.
+The template may include \(aq/\(aq characters, which are interpreted
+as delimiters for directory names.
+.Ss Piping crash reports to a program
+If the first character of this file is a pipe symbol (\fB|\fP),
+then the remainder of the line is interpreted as the command-line for
+a program (or script) that is to be executed.
+.Pp
+The pipe template is split on spaces into an argument list before the
+template parameters are expanded.
.Sh SEE ALSO
.Bl -hang -compact -width "https://www.ruby-toolbox.com/"
.It Lk https://www.ruby-lang.org/
@@ -667,5 +734,5 @@ Ruby is designed and implemented by
.An Yukihiro Matsumoto Aq matz@netlab.jp .
.Pp
See
-.Aq Lk https://bugs.ruby-lang.org/projects/ruby/wiki/Contributors
+.Aq Lk https://github.com/ruby/ruby/graphs/contributors
for contributors to Ruby.
diff --git a/marshal.c b/marshal.c
index 712a40347f..e26c600ca2 100644
--- a/marshal.c
+++ b/marshal.c
@@ -173,7 +173,7 @@ struct dump_arg {
st_table *data;
st_table *compat_tbl;
st_table *encodings;
- unsigned long num_entries;
+ st_index_t num_entries;
};
struct dump_call_arg {
@@ -228,19 +228,24 @@ static void
free_dump_arg(void *ptr)
{
clear_dump_arg(ptr);
- xfree(ptr);
}
static size_t
memsize_dump_arg(const void *ptr)
{
- return sizeof(struct dump_arg);
+ const struct dump_arg *p = (struct dump_arg *)ptr;
+ size_t memsize = 0;
+ if (p->symbols) memsize += rb_st_memsize(p->symbols);
+ if (p->data) memsize += rb_st_memsize(p->data);
+ if (p->compat_tbl) memsize += rb_st_memsize(p->compat_tbl);
+ if (p->encodings) memsize += rb_st_memsize(p->encodings);
+ return memsize;
}
static const rb_data_type_t dump_arg_data = {
"dump_arg",
{mark_dump_arg, free_dump_arg, memsize_dump_arg,},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_EMBEDDABLE
};
static VALUE
@@ -461,7 +466,7 @@ w_float(double d, struct dump_arg *arg)
memcpy(buf + len, p, digs);
len += digs;
}
- xfree(p);
+ free(p);
w_bytes(buf, len, arg);
}
}
@@ -528,7 +533,7 @@ hash_each(VALUE key, VALUE value, VALUE v)
static void
w_extended(VALUE klass, struct dump_arg *arg, int check)
{
- if (check && FL_TEST(klass, FL_SINGLETON)) {
+ if (check && RCLASS_SINGLETON_P(klass)) {
VALUE origin = RCLASS_ORIGIN(klass);
if (SINGLETON_DUMP_UNABLE_P(klass) ||
(origin != klass && SINGLETON_DUMP_UNABLE_P(origin))) {
@@ -605,20 +610,18 @@ struct w_ivar_arg {
};
static int
-w_obj_each(st_data_t key, st_data_t val, st_data_t a)
+w_obj_each(ID id, VALUE value, st_data_t a)
{
- ID id = (ID)key;
- VALUE value = (VALUE)val;
struct w_ivar_arg *ivarg = (struct w_ivar_arg *)a;
struct dump_call_arg *arg = ivarg->dump;
if (to_be_skipped_id(id)) {
if (id == s_encoding_short) {
- rb_warn("instance variable `"name_s_encoding_short"' on class %"PRIsVALUE" is not dumped",
+ rb_warn("instance variable '"name_s_encoding_short"' on class %"PRIsVALUE" is not dumped",
CLASS_OF(arg->obj));
}
if (id == s_ruby2_keywords_flag) {
- rb_warn("instance variable `"name_s_ruby2_keywords_flag"' on class %"PRIsVALUE" is not dumped",
+ rb_warn("instance variable '"name_s_ruby2_keywords_flag"' on class %"PRIsVALUE" is not dumped",
CLASS_OF(arg->obj));
}
return ST_CONTINUE;
@@ -630,9 +633,8 @@ w_obj_each(st_data_t key, st_data_t val, st_data_t a)
}
static int
-obj_count_ivars(st_data_t key, st_data_t val, st_data_t a)
+obj_count_ivars(ID id, VALUE val, st_data_t a)
{
- ID id = (ID)key;
if (!to_be_skipped_id(id) && UNLIKELY(!++*(st_index_t *)a)) {
rb_raise(rb_eRuntimeError, "too many instance variables");
}
@@ -1272,19 +1274,24 @@ static void
free_load_arg(void *ptr)
{
clear_load_arg(ptr);
- xfree(ptr);
}
static size_t
memsize_load_arg(const void *ptr)
{
- return sizeof(struct load_arg);
+ const struct load_arg *p = (struct load_arg *)ptr;
+ size_t memsize = 0;
+ if (p->symbols) memsize += rb_st_memsize(p->symbols);
+ if (p->data) memsize += rb_st_memsize(p->data);
+ if (p->partial_objects) memsize += rb_st_memsize(p->partial_objects);
+ if (p->compat_tbl) memsize += rb_st_memsize(p->compat_tbl);
+ return memsize;
}
static const rb_data_type_t load_arg_data = {
"load_arg",
{mark_load_arg, free_load_arg, memsize_load_arg,},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_EMBEDDABLE
};
#define r_entry(v, arg) r_entry0((v), (arg)->data->num_entries, (arg))
@@ -1673,10 +1680,9 @@ r_leave(VALUE v, struct load_arg *arg, bool partial)
}
static int
-copy_ivar_i(st_data_t key, st_data_t val, st_data_t arg)
+copy_ivar_i(ID vid, VALUE value, st_data_t arg)
{
- VALUE obj = (VALUE)arg, value = (VALUE)val;
- ID vid = (ID)key;
+ VALUE obj = (VALUE)arg;
if (!rb_ivar_defined(obj, vid))
rb_ivar_set(obj, vid, value);
@@ -1790,7 +1796,7 @@ append_extmod(VALUE obj, VALUE extmod)
#define prohibit_ivar(type, str) do { \
if (!ivp || !*ivp) break; \
rb_raise(rb_eTypeError, \
- "can't override instance variable of "type" `%"PRIsVALUE"'", \
+ "can't override instance variable of "type" '%"PRIsVALUE"'", \
(str)); \
} while (0)
@@ -1803,20 +1809,6 @@ r_object0(struct load_arg *arg, bool partial, int *ivp, VALUE extmod)
return r_object_for(arg, partial, ivp, extmod, type);
}
-static int
-r_move_ivar(st_data_t k, st_data_t v, st_data_t d)
-{
- ID key = (ID)k;
- VALUE value = (VALUE)v;
- VALUE dest = (VALUE)d;
-
- if (rb_is_instance_id(key)) {
- rb_ivar_set(dest, key, value);
- return ST_DELETE;
- }
- return ST_CONTINUE;
-}
-
static VALUE
r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int type)
{
@@ -1897,7 +1889,7 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int typ
goto type_hash;
}
v = r_object_for(arg, partial, 0, extmod, type);
- if (rb_special_const_p(v) || RB_TYPE_P(v, T_OBJECT) || RB_TYPE_P(v, T_CLASS)) {
+ if (RB_SPECIAL_CONST_P(v) || RB_TYPE_P(v, T_OBJECT) || RB_TYPE_P(v, T_CLASS)) {
goto format_error;
}
if (RB_TYPE_P(v, T_MODULE) || !RTEST(rb_class_inherited_p(c, RBASIC(v)->klass))) {
@@ -2034,7 +2026,7 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int typ
rb_str_set_len(str, dst - ptr);
}
VALUE regexp = rb_reg_new_str(str, options);
- rb_ivar_foreach(str, r_move_ivar, regexp);
+ r_copy_ivar(regexp, str);
v = r_entry0(regexp, idx, arg);
v = r_leave(v, arg, partial);
@@ -2141,7 +2133,7 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int typ
st_data_t d;
if (!rb_obj_respond_to(klass, s_load, TRUE)) {
- rb_raise(rb_eTypeError, "class %"PRIsVALUE" needs to have method `_load'",
+ rb_raise(rb_eTypeError, "class %"PRIsVALUE" needs to have method '_load'",
name);
}
data = r_string(arg);
@@ -2177,7 +2169,7 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int typ
append_extmod(v, extmod);
}
if (!rb_obj_respond_to(v, s_mload, TRUE)) {
- rb_raise(rb_eTypeError, "instance of %"PRIsVALUE" needs to have method `marshal_load'",
+ rb_raise(rb_eTypeError, "instance of %"PRIsVALUE" needs to have method 'marshal_load'",
name);
}
v = r_entry(v, arg);
@@ -2223,7 +2215,7 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int typ
v = r_entry(v, arg);
if (!rb_obj_respond_to(v, s_load_data, TRUE)) {
rb_raise(rb_eTypeError,
- "class %"PRIsVALUE" needs to have instance method `_load_data'",
+ "class %"PRIsVALUE" needs to have instance method '_load_data'",
name);
}
r = r_object0(arg, partial, 0, extmod);
@@ -2302,10 +2294,8 @@ r_object(struct load_arg *arg)
static void
clear_load_arg(struct load_arg *arg)
{
- if (arg->buf) {
- xfree(arg->buf);
- arg->buf = 0;
- }
+ xfree(arg->buf);
+ arg->buf = NULL;
arg->buflen = 0;
arg->offset = 0;
arg->readable = 0;
@@ -2525,6 +2515,20 @@ Init_marshal(void)
rb_define_const(rb_mMarshal, "MINOR_VERSION", INT2FIX(MARSHAL_MINOR));
}
+static int
+free_compat_i(st_data_t key, st_data_t value, st_data_t _)
+{
+ xfree((marshal_compat_t *)value);
+ return ST_CONTINUE;
+}
+
+static void
+free_compat_allocator_table(void *data)
+{
+ st_foreach(data, free_compat_i, 0);
+ st_free_table(data);
+}
+
static st_table *
compat_allocator_table(void)
{
@@ -2533,8 +2537,8 @@ compat_allocator_table(void)
#undef RUBY_UNTYPED_DATA_WARNING
#define RUBY_UNTYPED_DATA_WARNING 0
compat_allocator_tbl_wrapper =
- Data_Wrap_Struct(0, mark_marshal_compat_t, 0, compat_allocator_tbl);
- rb_gc_register_mark_object(compat_allocator_tbl_wrapper);
+ Data_Wrap_Struct(0, mark_marshal_compat_t, free_compat_allocator_table, compat_allocator_tbl);
+ rb_vm_register_global_object(compat_allocator_tbl_wrapper);
return compat_allocator_tbl;
}
diff --git a/math.c b/math.c
index 51a9eac8b4..2394fe9f58 100644
--- a/math.c
+++ b/math.c
@@ -474,7 +474,6 @@ math_exp(VALUE unused_obj, VALUE x)
# define M_LN10 2.30258509299404568401799145468436421
#endif
-static double math_log1(VALUE x);
FUNC_MINIMIZED(static VALUE math_log(int, const VALUE *, VALUE));
/*
@@ -509,20 +508,6 @@ math_log(int argc, const VALUE *argv, VALUE unused_obj)
return rb_math_log(argc, argv);
}
-VALUE
-rb_math_log(int argc, const VALUE *argv)
-{
- VALUE x, base;
- double d;
-
- rb_scan_args(argc, argv, "11", &x, &base);
- d = math_log1(x);
- if (argc == 2) {
- d /= math_log1(base);
- }
- return DBL2NUM(d);
-}
-
static double
get_double_rshift(VALUE x, size_t *pnumbits)
{
@@ -541,16 +526,51 @@ get_double_rshift(VALUE x, size_t *pnumbits)
}
static double
-math_log1(VALUE x)
+math_log_split(VALUE x, size_t *numbits)
{
- size_t numbits;
- double d = get_double_rshift(x, &numbits);
+ double d = get_double_rshift(x, numbits);
domain_check_min(d, 0.0, "log");
- /* check for pole error */
- if (d == 0.0) return -HUGE_VAL;
+ return d;
+}
+
+#if defined(log2) || defined(HAVE_LOG2)
+# define log_intermediate log2
+#else
+# define log_intermediate log10
+double log2(double x);
+#endif
+
+VALUE
+rb_math_log(int argc, const VALUE *argv)
+{
+ VALUE x, base;
+ double d;
+ size_t numbits;
- return log(d) + numbits * M_LN2; /* log(d * 2 ** numbits) */
+ argc = rb_scan_args(argc, argv, "11", &x, &base);
+ d = math_log_split(x, &numbits);
+ if (argc == 2) {
+ size_t numbits_2;
+ double b = math_log_split(base, &numbits_2);
+ /* check for pole error */
+ if (d == 0.0) {
+ // Already DomainError if b < 0.0
+ return b ? DBL2NUM(-HUGE_VAL) : DBL2NUM(NAN);
+ }
+ else if (b == 0.0) {
+ return DBL2NUM(-0.0);
+ }
+ d = log_intermediate(d) / log_intermediate(b);
+ d += (numbits - numbits_2) / log2(b);
+ }
+ else {
+ /* check for pole error */
+ if (d == 0.0) return DBL2NUM(-HUGE_VAL);
+ d = log(d);
+ d += numbits * M_LN2;
+ }
+ return DBL2NUM(d);
}
#ifndef log2
@@ -711,7 +731,7 @@ rb_math_sqrt(VALUE x)
* cbrt(1.0) # => 1.0
* cbrt(0.0) # => 0.0
* cbrt(1.0) # => 1.0
- cbrt(2.0) # => 1.2599210498948732
+ * cbrt(2.0) # => 1.2599210498948732
* cbrt(8.0) # => 2.0
* cbrt(27.0) # => 3.0
* cbrt(INFINITY) # => Infinity
diff --git a/memory_view.c b/memory_view.c
index 3fb79202f9..519aad2ca1 100644
--- a/memory_view.c
+++ b/memory_view.c
@@ -845,9 +845,7 @@ rb_memory_view_release(rb_memory_view_t* view)
if (rv) {
unregister_exported_object(view->obj);
view->obj = Qnil;
- if (view->item_desc.components) {
- xfree((void *)view->item_desc.components);
- }
+ xfree((void *)view->item_desc.components);
}
return rv;
}
@@ -865,7 +863,7 @@ Init_MemoryView(void)
VALUE obj = TypedData_Wrap_Struct(
0, &rb_memory_view_exported_object_registry_data_type,
exported_object_table);
- rb_gc_register_mark_object(obj);
+ rb_vm_register_global_object(obj);
rb_memory_view_exported_object_registry = obj;
id_memory_view = rb_intern_const("__memory_view__");
diff --git a/method.h b/method.h
index d33ab5053c..fdc7c263d1 100644
--- a/method.h
+++ b/method.h
@@ -101,8 +101,9 @@ static inline void
METHOD_ENTRY_FLAGS_COPY(rb_method_entry_t *dst, const rb_method_entry_t *src)
{
dst->flags =
- (dst->flags & ~(IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2)) |
- (src->flags & (IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2));
+ (dst->flags & ~(IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2
+ |IMEMO_FL_USER3)) |
+ (src->flags & (IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2|IMEMO_FL_USER3));
}
typedef enum {
@@ -135,8 +136,9 @@ typedef struct rb_method_iseq_struct {
rb_cref_t * cref; /*!< class reference, should be marked */
} rb_method_iseq_t; /* check rb_add_method_iseq() when modify the fields */
+typedef VALUE (*rb_cfunc_t)(ANYARGS);
typedef struct rb_method_cfunc_struct {
- VALUE (*func)(ANYARGS);
+ rb_cfunc_t func;
VALUE (*invoker)(VALUE recv, int argc, const VALUE *argv, VALUE (*func)(ANYARGS));
int argc;
} rb_method_cfunc_t;
@@ -152,7 +154,6 @@ typedef struct rb_method_alias_struct {
typedef struct rb_method_refined_struct {
struct rb_method_entry_struct * orig_me;
- VALUE owner;
} rb_method_refined_t;
typedef struct rb_method_bmethod_struct {
@@ -178,9 +179,9 @@ typedef struct rb_method_optimized {
struct rb_method_definition_struct {
BITFIELD(rb_method_type_t, type, VM_METHOD_TYPE_MINIMUM_BITS);
unsigned int iseq_overload: 1;
- int alias_count : 27;
- int complemented_count : 28;
unsigned int no_redef_warning: 1;
+ unsigned int aliased : 1;
+ int reference_count : 28;
union {
rb_method_iseq_t iseq;
@@ -199,7 +200,7 @@ struct rb_method_definition_struct {
struct rb_id_table;
typedef struct rb_method_definition_struct rb_method_definition_t;
-STATIC_ASSERT(sizeof_method_def, offsetof(rb_method_definition_t, body)==8);
+STATIC_ASSERT(sizeof_method_def, offsetof(rb_method_definition_t, body) <= 8);
#define UNDEFINED_METHOD_ENTRY_P(me) (!(me) || !(me)->def || (me)->def->type == VM_METHOD_TYPE_UNDEF)
#define UNDEFINED_REFINED_METHOD_P(def) \
@@ -213,7 +214,7 @@ void rb_add_method_optimized(VALUE klass, ID mid, enum method_optimized_type, un
void rb_add_refined_method_entry(VALUE refined_class, ID mid);
rb_method_entry_t *rb_method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *, rb_method_visibility_t noex);
-rb_method_entry_t *rb_method_entry_create(ID called_id, VALUE klass, rb_method_visibility_t visi, const rb_method_definition_t *def);
+rb_method_entry_t *rb_method_entry_create(ID called_id, VALUE klass, rb_method_visibility_t visi, rb_method_definition_t *def);
const rb_method_entry_t *rb_method_entry_at(VALUE obj, ID id);
@@ -249,6 +250,6 @@ void rb_scope_visibility_set(rb_method_visibility_t);
VALUE rb_unnamed_parameters(int arity);
void rb_clear_method_cache(VALUE klass_or_module, ID mid);
-void rb_clear_method_cache_all(void);
+void rb_clear_all_refinement_method_cache(void);
#endif /* RUBY_METHOD_H */
diff --git a/mini_builtin.c b/mini_builtin.c
index 1818f18cba..b38642e89b 100644
--- a/mini_builtin.c
+++ b/mini_builtin.c
@@ -12,36 +12,36 @@
static struct st_table *loaded_builtin_table;
#endif
-rb_ast_t *rb_builtin_ast(const char *feature_name, VALUE *name_str);
+VALUE rb_builtin_vast(const char *feature_name, VALUE *name_str);
static const rb_iseq_t *
builtin_iseq_load(const char *feature_name, const struct rb_builtin_function *table)
{
VALUE name_str = 0;
- rb_ast_t *ast = rb_builtin_ast(feature_name, &name_str);
+ rb_ast_t *ast;
+ VALUE vast = rb_builtin_vast(feature_name, &name_str);
rb_vm_t *vm = GET_VM();
- if (!ast) {
+ if (NIL_P(vast)) {
rb_fatal("builtin_iseq_load: can not find %s; "
"probably miniprelude.c is out of date",
feature_name);
}
vm->builtin_function_table = table;
- vm->builtin_inline_index = 0;
static const rb_compile_option_t optimization = {
- TRUE, /* int inline_const_cache; */
- TRUE, /* int peephole_optimization; */
- FALSE,/* int tailcall_optimization; */
- TRUE, /* int specialized_instruction; */
- TRUE, /* int operands_unification; */
- TRUE, /* int instructions_unification; */
- TRUE, /* int stack_caching; */
- TRUE, /* int frozen_string_literal; */
- FALSE, /* int debug_frozen_string_literal; */
- FALSE, /* unsigned int coverage_enabled; */
- 0, /* int debug_level; */
+ .inline_const_cache = TRUE,
+ .peephole_optimization = TRUE,
+ .tailcall_optimization = FALSE,
+ .specialized_instruction = TRUE,
+ .operands_unification = TRUE,
+ .instructions_unification = TRUE,
+ .frozen_string_literal = TRUE,
+ .debug_frozen_string_literal = FALSE,
+ .coverage_enabled = FALSE,
+ .debug_level = 0,
};
- const rb_iseq_t *iseq = rb_iseq_new_with_opt(&ast->body, name_str, name_str, Qnil, 0, NULL, 0, ISEQ_TYPE_TOP, &optimization);
+ ast = rb_ruby_ast_data_get(vast);
+ const rb_iseq_t *iseq = rb_iseq_new_with_opt(vast, name_str, name_str, Qnil, 0, NULL, 0, ISEQ_TYPE_TOP, &optimization, Qnil);
GET_VM()->builtin_function_table = NULL;
rb_ast_dispose(ast);
@@ -53,7 +53,7 @@ builtin_iseq_load(const char *feature_name, const struct rb_builtin_function *ta
#ifndef INCLUDED_BY_BUILTIN_C
st_insert(loaded_builtin_table, (st_data_t)feature_name, (st_data_t)iseq);
- rb_gc_register_mark_object((VALUE)iseq);
+ rb_vm_register_global_object((VALUE)iseq);
#endif
return iseq;
@@ -79,6 +79,7 @@ each_builtin_i(st_data_t key, st_data_t val, st_data_t dmy)
return ST_CONTINUE;
}
+/* :nodoc: */
static VALUE
each_builtin(VALUE self)
{
diff --git a/miniinit.c b/miniinit.c
index 2a14a0d1c5..09608c1113 100644
--- a/miniinit.c
+++ b/miniinit.c
@@ -48,4 +48,17 @@ Init_enc(void)
rb_encdb_alias("ASCII", "US-ASCII");
}
+/* miniruby does not support dynamic loading. */
+void
+Init_ext(void)
+{
+}
+
#include "mini_builtin.c"
+
+void
+rb_free_loaded_builtin_table(void)
+{
+ if (loaded_builtin_table)
+ st_free_table(loaded_builtin_table);
+}
diff --git a/misc/call_fuzzer.rb b/misc/call_fuzzer.rb
new file mode 100644
index 0000000000..c3f9f90490
--- /dev/null
+++ b/misc/call_fuzzer.rb
@@ -0,0 +1,372 @@
+require 'optparse'
+require 'set'
+
+# Number of iterations to test
+num_iters = 10_000
+
+# Parse the command-line options
+OptionParser.new do |opts|
+ opts.on("--num-iters=N") do |n|
+ num_iters = n.to_i
+ end
+end.parse!
+
+# Format large numbers with comma separators for readability
+def format_number(pad, number)
+ s = number.to_s
+ i = s.index('.') || s.size
+ s.insert(i -= 3, ',') while i > 3
+ s.rjust(pad, ' ')
+end
+
+# Wrap an integer to pass as argument
+# We use this so we can have some object arguments
+class IntWrapper
+ def initialize(v)
+ # Force the object to have a random shape
+ if rand() < 50
+ @v0 = 1
+ end
+ if rand() < 50
+ @v1 = 1
+ end
+ if rand() < 50
+ @v2 = 1
+ end
+ if rand() < 50
+ @v3 = 1
+ end
+ if rand() < 50
+ @v4 = 1
+ end
+ if rand() < 50
+ @v5 = 1
+ end
+ if rand() < 50
+ @v6 = 1
+ end
+
+ @value = v
+ end
+
+ attr_reader :value
+end
+
+# Generate a random argument value, integer or string or object
+def sample_arg()
+ c = ['int', 'string', 'object'].sample()
+
+ if c == 'int'
+ return rand(0...100)
+ end
+
+ if c == 'string'
+ return 'f' * rand(0...100)
+ end
+
+ if c == 'object'
+ return IntWrapper.new(rand(0...100))
+ end
+
+ raise "should not get here"
+end
+
+# Evaluate the value of an argument with respect to the checksum
+def arg_val(arg)
+ if arg.kind_of? Integer
+ return arg
+ end
+
+ if arg.kind_of? String
+ return arg.length
+ end
+
+ if arg.kind_of? Object
+ return arg.value
+ end
+
+ raise "unknown arg type"
+end
+
+# List of parameters/arguments for a method
+class ParamList
+ def initialize()
+ self.sample_params()
+ self.sample_args()
+ end
+
+ # Sample/generate a random set of parameters for a method
+ def sample_params()
+ # Choose how many positional arguments to use, and how many are optional
+ num_pargs = rand(10)
+ @opt_parg_idx = rand(num_pargs)
+ @num_opt_pargs = rand(num_pargs + 1 - @opt_parg_idx)
+ @num_pargs_req = num_pargs - @num_opt_pargs
+ @pargs = (0...num_pargs).map do |i|
+ {
+ :name => "p#{i}",
+ :optional => (i >= @opt_parg_idx && i < @opt_parg_idx + @num_opt_pargs)
+ }
+ end
+
+ # Choose how many kwargs to use, and how many are optional
+ num_kwargs = rand(10)
+ @kwargs = (0...num_kwargs).map do |i|
+ {
+ :name => "k#{i}",
+ :optional => rand() < 0.5
+ }
+ end
+
+ # Choose whether to have rest parameters or not
+ @has_rest = @num_opt_pargs == 0 && rand() < 0.5
+ @has_kwrest = rand() < 0.25
+
+ # Choose whether to have a named block parameter or not
+ @has_block_param = rand() < 0.25
+ end
+
+ # Sample/generate a random set of arguments corresponding to the parameters
+ def sample_args()
+ # Choose how many positional args to pass
+ num_pargs_passed = rand(@num_pargs_req..@pargs.size)
+
+ # How many optional arguments will be filled
+ opt_pargs_filled = num_pargs_passed - @num_pargs_req
+
+ @pargs.each_with_index do |parg, i|
+ if parg[:optional]
+ parg[:default] = rand(100)
+ end
+
+ if !parg[:optional] || i < @opt_parg_idx + opt_pargs_filled
+ parg[:argval] = rand(100)
+ end
+ end
+
+ @kwargs.each_with_index do |kwarg, i|
+ if kwarg[:optional]
+ kwarg[:default] = rand(100)
+ end
+
+ if !kwarg[:optional] || rand() < 0.5
+ kwarg[:argval] = rand(100)
+ end
+ end
+
+ # Randomly pass a block or not
+ @block_arg = nil
+ if rand() < 0.5
+ @block_arg = rand(100)
+ end
+ end
+
+ # Compute the expected checksum of arguments ahead of time
+ def compute_checksum()
+ checksum = 0
+
+ @pargs.each_with_index do |arg, i|
+ value = (arg.key? :argval)? arg[:argval]:arg[:default]
+ checksum += (i+1) * arg_val(value)
+ end
+
+ @kwargs.each_with_index do |arg, i|
+ value = (arg.key? :argval)? arg[:argval]:arg[:default]
+ checksum += (i+1) * arg_val(value)
+ end
+
+ if @block_arg
+ if @has_block_param
+ checksum += arg_val(@block_arg)
+ end
+
+ checksum += arg_val(@block_arg)
+ end
+
+ checksum
+ end
+
+ # Generate code for the method signature and method body
+ def gen_method_str()
+ m_str = "def m("
+
+ @pargs.each do |arg|
+ if !m_str.end_with?("(")
+ m_str += ", "
+ end
+
+ m_str += arg[:name]
+
+ # If this has a default value
+ if arg[:optional]
+ m_str += " = #{arg[:default]}"
+ end
+ end
+
+ if @has_rest
+ if !m_str.end_with?("(")
+ m_str += ", "
+ end
+ m_str += "*rest"
+ end
+
+ @kwargs.each do |arg|
+ if !m_str.end_with?("(")
+ m_str += ", "
+ end
+
+ m_str += "#{arg[:name]}:"
+
+ # If this has a default value
+ if arg[:optional]
+ m_str += " #{arg[:default]}"
+ end
+ end
+
+ if @has_kwrest
+ if !m_str.end_with?("(")
+ m_str += ", "
+ end
+ m_str += "**kwrest"
+ end
+
+ if @has_block_param
+ if !m_str.end_with?("(")
+ m_str += ", "
+ end
+
+ m_str += "&block"
+ end
+
+ m_str += ")\n"
+
+ # Add some useless locals
+ rand(0...16).times do |i|
+ m_str += "local#{i} = #{i}\n"
+ end
+
+ # Add some useless if statements
+ @pargs.each_with_index do |arg, i|
+ if rand() < 50
+ m_str += "if #{arg[:name]} > 4; end\n"
+ end
+ end
+
+ m_str += "checksum = 0\n"
+
+ @pargs.each_with_index do |arg, i|
+ m_str += "checksum += #{i+1} * arg_val(#{arg[:name]})\n"
+ end
+
+ @kwargs.each_with_index do |arg, i|
+ m_str += "checksum += #{i+1} * arg_val(#{arg[:name]})\n"
+ end
+
+ if @has_block_param
+ m_str += "if block; r = block.call; checksum += arg_val(r); end\n"
+ end
+
+ m_str += "if block_given?; r = yield; checksum += arg_val(r); end\n"
+
+ if @has_rest
+ m_str += "raise 'rest is not array' unless rest.kind_of?(Array)\n"
+ m_str += "raise 'rest size not integer' unless rest.size.kind_of?(Integer)\n"
+ end
+
+ if @has_kwrest
+ m_str += "raise 'kwrest is not a hash' unless kwrest.kind_of?(Hash)\n"
+ m_str += "raise 'kwrest size not integer' unless kwrest.size.kind_of?(Integer)\n"
+ end
+
+ m_str += "checksum\n"
+ m_str += "end"
+
+ m_str
+ end
+
+ # Generate code to call into the method and pass the arguments
+ def gen_call_str()
+ c_str = "m("
+
+ @pargs.each_with_index do |arg, i|
+ if !arg.key? :argval
+ next
+ end
+
+ if !c_str.end_with?("(")
+ c_str += ", "
+ end
+
+ c_str += "#{arg[:argval]}"
+ end
+
+ @kwargs.each_with_index do |arg, i|
+ if !arg.key? :argval
+ next
+ end
+
+ if !c_str.end_with?("(")
+ c_str += ", "
+ end
+
+ c_str += "#{arg[:name]}: #{arg[:argval]}"
+ end
+
+ c_str += ")"
+
+ # Randomly pass a block or not
+ if @block_arg
+ c_str += " { #{@block_arg} }"
+ end
+
+ c_str
+ end
+end
+
+iseqs_compiled_start = RubyVM::YJIT.runtime_stats[:compiled_iseq_entry]
+start_time = Time.now.to_f
+
+num_iters.times do |i|
+ puts "Iteration #{i}"
+
+ lst = ParamList.new()
+ m_str = lst.gen_method_str()
+ c_str = lst.gen_call_str()
+ checksum = lst.compute_checksum()
+
+ f = Object.new
+
+ # Define the method on f
+ puts "Defining"
+ p m_str
+ f.instance_eval(m_str)
+ #puts RubyVM::InstructionSequence.disasm(f.method(:m))
+ #exit 0
+
+ puts "Calling"
+ c_str = "f.#{c_str}"
+ p c_str
+ r = eval(c_str)
+ puts "checksum=#{r}"
+
+ if r != checksum
+ raise "return value #{r} doesn't match checksum #{checksum}"
+ end
+
+ puts ""
+end
+
+# Make sure that YJIT actually compiled the tests we ran
+# Should be run with --yjit-call-threshold=1
+iseqs_compiled_end = RubyVM::YJIT.runtime_stats[:compiled_iseq_entry]
+if iseqs_compiled_end - iseqs_compiled_start < num_iters
+ raise "YJIT did not compile enough ISEQs"
+end
+
+puts "Code region size: #{ format_number(0, RubyVM::YJIT.runtime_stats[:code_region_size]) }"
+
+end_time = Time.now.to_f
+itrs_per_sec = num_iters / (end_time - start_time)
+itrs_per_hour = 3600 * itrs_per_sec
+puts "#{'%.1f' % itrs_per_sec} iterations/s"
+puts "#{format_number(0, itrs_per_hour.round)} iterations/hour"
diff --git a/misc/call_fuzzer.sh b/misc/call_fuzzer.sh
new file mode 100755
index 0000000000..cf4ec76fe8
--- /dev/null
+++ b/misc/call_fuzzer.sh
@@ -0,0 +1,13 @@
+# Stop at first error
+set -e
+
+# TODO
+# TODO: boost --num-iters to 1M+ for actual test
+# TODO
+export NUM_ITERS=25000
+
+# Enable code GC so we don't stop compiling when we hit the code size limit
+ruby --yjit-call-threshold=1 --yjit-code-gc misc/call_fuzzer.rb --num-iters=$NUM_ITERS
+
+# Do another pass with --verify-ctx
+ruby --yjit-call-threshold=1 --yjit-code-gc --yjit-verify-ctx misc/call_fuzzer.rb --num-iters=$NUM_ITERS
diff --git a/misc/gdb.py b/misc/gdb.py
index 85507fb540..6034a389bb 100644
--- a/misc/gdb.py
+++ b/misc/gdb.py
@@ -1,8 +1,18 @@
+import argparse
import textwrap
-# Usage:
-# cfp: Dump the current cfp
-# cfp 1: Dump the caller cfp
+# usage: [-h] [-a | --all | --no-all] [-s STACK_SIZE] [uplevel]
+#
+# Dump a control frame
+#
+# positional arguments:
+# uplevel CFP offset from the stack top
+#
+# options:
+# -h, --help show this help message and exit
+# -a, --all, --no-all dump all frames
+# -s STACK_SIZE, --stack-size STACK_SIZE
+# override stack_size (useful for JIT frames)
class CFP(gdb.Command):
FRAME_MAGICS = [
# frame types
@@ -35,44 +45,77 @@ class CFP(gdb.Command):
def __init__(self):
super(CFP, self).__init__('cfp', gdb.COMMAND_USER)
- def invoke(self, offset, from_tty):
- if not offset:
- offset = '0'
- cfp = f'(ruby_current_ec->cfp + ({offset}))'
-
+ self.parser = argparse.ArgumentParser(description='Dump a control frame')
+ self.parser.add_argument('uplevel', type=int, nargs='?', default=0, help='CFP offset from the stack top')
+ self.parser.add_argument('-a', '--all', action=argparse.BooleanOptionalAction, help='dump all frames')
+ self.parser.add_argument('-s', '--stack-size', type=int, help='override stack_size (useful for JIT frames)')
+
+ def invoke(self, args, from_tty):
+ try:
+ args = self.parser.parse_args(args.split())
+ except SystemExit:
+ return
+ cfp = f'(ruby_current_ec->cfp + ({args.uplevel}))'
end_cfp = self.get_int('ruby_current_ec->vm_stack + ruby_current_ec->vm_stack_size')
- cfp_count = int((end_cfp - self.get_int('ruby_current_ec->cfp')) / self.get_int('sizeof(rb_control_frame_t)'))
- print('CFP (count={}, addr=0x{:x}):'.format(cfp_count, self.get_int(cfp)))
+ cfp_index = int((end_cfp - self.get_int(cfp) - 1) / self.get_int('sizeof(rb_control_frame_t)'))
+
+ if args.all:
+ cfp_count = int((end_cfp - self.get_int('ruby_current_ec->cfp')) / self.get_int('sizeof(rb_control_frame_t)')) - 1 # exclude dummy CFP
+ for i in range(cfp_count):
+ print('-' * 80)
+ self.invoke(str(cfp_count - i - 1), from_tty)
+ return
+
+ print('CFP (addr=0x{:x}, index={}):'.format(self.get_int(cfp), cfp_index))
gdb.execute(f'p *({cfp})')
print()
if self.get_int(f'{cfp}->iseq'):
local_size = self.get_int(f'{cfp}->iseq->body->local_table_size - {cfp}->iseq->body->param.size')
param_size = self.get_int(f'{cfp}->iseq->body->param.size')
- print(f'Params (size={param_size}):')
- for i in range(-3 - local_size - param_size, -3 - local_size):
- self.print_stack(cfp, i, self.rp(cfp, i))
- print()
- print(f'Locals (size={local_size}):')
- for i in range(-3 - local_size, -3):
- self.print_stack(cfp, i, self.rp(cfp, i))
- print()
+ if local_size:
+ print(f'Params (size={param_size}):')
+ for i in range(-3 - local_size - param_size, -3 - local_size):
+ self.print_stack(cfp, i, self.rp(cfp, i))
+ print()
+
+ if param_size:
+ print(f'Locals (size={local_size}):')
+ for i in range(-3 - local_size, -3):
+ self.print_stack(cfp, i, self.rp(cfp, i))
+ print()
print('Env:')
- self.print_stack(cfp, -3, self.rp(cfp, -3))
- self.print_stack(cfp, -2, self.specval(cfp, -2))
- self.print_stack(cfp, -1, self.frame_types(cfp, -1))
+ self.print_env(cfp, -3, self.rp_env(cfp, -3))
+ self.print_env(cfp, -2, self.specval(cfp, -2))
+ self.print_env(cfp, -1, self.frame_types(cfp, -1))
print()
- stack_size = int((self.get_int(f'{cfp}->sp') - self.get_int(f'{cfp}->__bp__')) / 8)
- print(f'Stack (size={stack_size}):')
- for i in range(0, stack_size):
- self.print_stack(cfp, i, self.rp(cfp, i))
- print(self.regs(cfp, stack_size))
+ # We can't calculate BP for the first frame.
+ # vm_base_ptr doesn't work for C frames either.
+ if cfp_index > 0 and self.get_int(f'{cfp}->iseq'):
+ if args.stack_size is not None:
+ stack_size = args.stack_size
+ else:
+ stack_size = int((self.get_int(f'{cfp}->sp') - self.get_int(f'vm_base_ptr({cfp})')) / 8)
+ print(f'Stack (size={stack_size}):')
+ for i in range(0, stack_size):
+ self.print_stack(cfp, i, self.rp(cfp, i))
+ print(self.regs(cfp, stack_size))
+
+ def print_env(self, cfp, bp_index, content):
+ ep_index = bp_index + 1
+ address = self.get_int(f'((rb_control_frame_t *){cfp})->ep + {ep_index}')
+ value = self.get_env(cfp, bp_index)
+ regs = self.regs(cfp, bp_index)
+ if content:
+ content = textwrap.indent(content, ' ' * 3).lstrip() # Leave the regs column empty
+ content = f'{content} '
+ print('{:2} 0x{:x} [{}] {}(0x{:x})'.format(regs, address, bp_index, content, value))
def print_stack(self, cfp, bp_index, content):
- address = self.get_int(f'{cfp}->__bp__ + {bp_index}')
+ address = self.get_int(f'vm_base_ptr({cfp}) + {bp_index}')
value = self.get_value(cfp, bp_index)
regs = self.regs(cfp, bp_index)
if content:
@@ -81,9 +124,9 @@ class CFP(gdb.Command):
print('{:2} 0x{:x} [{}] {}(0x{:x})'.format(regs, address, bp_index, content, value))
def regs(self, cfp, bp_index):
- address = self.get_int(f'{cfp}->__bp__ + {bp_index}')
+ address = self.get_int(f'vm_base_ptr({cfp}) + {bp_index}')
regs = []
- for reg, field in { 'EP': 'ep', 'BP': '__bp__', 'SP': 'sp' }.items():
+ for reg, field in { 'EP': 'ep', 'SP': 'sp' }.items():
if address == self.get_int(f'{cfp}->{field}'):
regs.append(reg)
return ' '.join(regs)
@@ -92,9 +135,13 @@ class CFP(gdb.Command):
value = self.get_value(cfp, bp_index)
return self.get_string(f'rp {value}').rstrip()
+ def rp_env(self, cfp, bp_index):
+ value = self.get_env(cfp, bp_index)
+ return self.get_string(f'rp {value}').rstrip()
+
# specval: block_handler or previous EP
def specval(self, cfp, bp_index):
- value = self.get_value(cfp, bp_index)
+ value = self.get_env(cfp, bp_index)
if value == 0:
return 'VM_BLOCK_HANDLER_NONE'
if value == self.get_int('rb_block_param_proxy'):
@@ -103,7 +150,7 @@ class CFP(gdb.Command):
def frame_types(self, cfp, bp_index):
types = []
- value = self.get_value(cfp, bp_index)
+ value = self.get_env(cfp, bp_index)
magic_mask = self.get_int('VM_FRAME_MAGIC_MASK')
for magic in self.FRAME_MAGICS:
@@ -118,8 +165,12 @@ class CFP(gdb.Command):
return ' | '.join(types)
+ def get_env(self, cfp, bp_index):
+ ep_index = bp_index + 1
+ return self.get_int(f'((rb_control_frame_t *){cfp})->ep[{ep_index}]')
+
def get_value(self, cfp, bp_index):
- return self.get_int(f'{cfp}->__bp__[{bp_index}]')
+ return self.get_int(f'vm_base_ptr({cfp})[{bp_index}]')
def get_int(self, expr):
return int(self.get_string(f'printf "%ld", ({expr})'))
diff --git a/misc/lldb_cruby.py b/misc/lldb_cruby.py
index 95e03c6209..400ccb45b9 100755
--- a/misc/lldb_cruby.py
+++ b/misc/lldb_cruby.py
@@ -197,18 +197,16 @@ def string2cstr(rstring):
flags = rstring.GetValueForExpressionPath(".basic->flags").unsigned
if flags & RUBY_T_MASK != RUBY_T_STRING:
raise TypeError("not a string")
+ clen = int(rstring.GetValueForExpressionPath(".len").value, 0)
if flags & RUBY_FL_USER1:
cptr = int(rstring.GetValueForExpressionPath(".as.heap.ptr").value, 0)
- clen = int(rstring.GetValueForExpressionPath(".as.heap.len").value, 0)
else:
cptr = int(rstring.GetValueForExpressionPath(".as.embed.ary").location, 0)
- clen = int(rstring.GetValueForExpressionPath(".as.embed.len").value, 0)
return cptr, clen
def output_string(debugger, result, rstring):
cptr, clen = string2cstr(rstring)
- expr = "print *(const char (*)[%d])%0#x" % (clen, cptr)
- append_command_output(debugger, expr, result)
+ append_expression(debugger, "*(const char (*)[%d])%0#x" % (clen, cptr), result)
def fixnum_p(x):
return x & RUBY_FIXNUM_FLAG != 0
@@ -227,6 +225,9 @@ def append_command_output(debugger, command, result):
result.write(output1)
result.write(output2)
+def append_expression(debugger, expression, result):
+ append_command_output(debugger, "expression " + expression, result)
+
def lldb_rp(debugger, command, result, internal_dict):
if not ('RUBY_Qfalse' in globals()):
lldb_init(debugger)
@@ -258,13 +259,13 @@ def lldb_inspect(debugger, target, result, val):
elif fixnum_p(num):
print(num >> 1, file=result)
elif flonum_p(num):
- append_command_output(debugger, "print rb_float_value(%0#x)" % val.GetValueAsUnsigned(), result)
+ append_expression(debugger, "rb_float_value(%0#x)" % val.GetValueAsUnsigned(), result)
elif static_sym_p(num):
if num < 128:
print("T_SYMBOL: %c" % num, file=result)
else:
print("T_SYMBOL: (%x)" % num, file=result)
- append_command_output(debugger, "p rb_id2name(%0#x)" % (num >> 8), result)
+ append_expression(debugger, "rb_id2name(%0#x)" % (num >> 8), result)
elif num & RUBY_IMMEDIATE_MASK:
print('immediate(%x)' % num, file=result)
else:
@@ -292,13 +293,13 @@ def lldb_inspect(debugger, target, result, val):
print('T_NIL: %s%s' % (flaginfo, val.Dereference()), file=result)
elif flType == RUBY_T_OBJECT:
result.write('T_OBJECT: %s' % flaginfo)
- append_command_output(debugger, "print *(struct RObject*)%0#x" % val.GetValueAsUnsigned(), result)
+ append_expression(debugger, "*(struct RObject*)%0#x" % val.GetValueAsUnsigned(), result)
elif flType == RUBY_T_CLASS or flType == RUBY_T_MODULE or flType == RUBY_T_ICLASS:
result.write('T_%s: %s' % ('CLASS' if flType == RUBY_T_CLASS else 'MODULE' if flType == RUBY_T_MODULE else 'ICLASS', flaginfo))
- append_command_output(debugger, "print *(struct RClass*)%0#x" % val.GetValueAsUnsigned(), result)
+ append_expression(debugger, "*(struct RClass*)%0#x" % val.GetValueAsUnsigned(), result)
tRClass = target.FindFirstType("struct RClass")
if not val.Cast(tRClass).GetChildMemberWithName("ptr").IsValid():
- append_command_output(debugger, "print *(struct rb_classext_struct*)%0#x" % (val.GetValueAsUnsigned() + tRClass.GetByteSize()), result)
+ append_expression(debugger, "*(struct rb_classext_struct*)%0#x" % (val.GetValueAsUnsigned() + tRClass.GetByteSize()), result)
elif flType == RUBY_T_STRING:
result.write('T_STRING: %s' % flaginfo)
encidx = ((flags & RUBY_ENCODING_MASK)>>RUBY_ENCODING_SHIFT)
@@ -312,12 +313,12 @@ def lldb_inspect(debugger, target, result, val):
if len == 0:
result.write("(empty)\n")
else:
- append_command_output(debugger, "print *(const char (*)[%d])%0#x" % (len, ptr), result)
+ append_expression(debugger, "*(const char (*)[%d])%0#x" % (len, ptr), result)
elif flType == RUBY_T_SYMBOL:
result.write('T_SYMBOL: %s' % flaginfo)
tRSymbol = target.FindFirstType("struct RSymbol").GetPointerType()
val = val.Cast(tRSymbol)
- append_command_output(debugger, 'print (ID)%0#x ' % val.GetValueForExpressionPath("->id").GetValueAsUnsigned(), result)
+ append_expression(debugger, '(ID)%0#x ' % val.GetValueForExpressionPath("->id").GetValueAsUnsigned(), result)
tRString = target.FindFirstType("struct RString").GetPointerType()
output_string(debugger, result, val.GetValueForExpressionPath("->fstr").Cast(tRString))
elif flType == RUBY_T_ARRAY:
@@ -343,12 +344,12 @@ def lldb_inspect(debugger, target, result, val):
else:
result.write("\n")
if ptr.GetValueAsSigned() == 0:
- append_command_output(debugger, "expression -fx -- ((struct RArray*)%0#x)->as.ary" % val.GetValueAsUnsigned(), result)
+ append_expression(debugger, "-fx -- ((struct RArray*)%0#x)->as.ary" % val.GetValueAsUnsigned(), result)
else:
- append_command_output(debugger, "expression -Z %d -fx -- (const VALUE*)%0#x" % (len, ptr.GetValueAsUnsigned()), result)
+ append_expression(debugger, "-Z %d -fx -- (const VALUE*)%0#x" % (len, ptr.GetValueAsUnsigned()), result)
elif flType == RUBY_T_HASH:
result.write("T_HASH: %s" % flaginfo)
- append_command_output(debugger, "p *(struct RHash *) %0#x" % val.GetValueAsUnsigned(), result)
+ append_expression(debugger, "*(struct RHash *) %0#x" % val.GetValueAsUnsigned(), result)
elif flType == RUBY_T_BIGNUM:
tRBignum = target.FindFirstType("struct RBignum").GetPointerType()
val = val.Cast(tRBignum)
@@ -356,15 +357,15 @@ def lldb_inspect(debugger, target, result, val):
if flags & RUBY_FL_USER2:
len = ((flags & (RUBY_FL_USER3|RUBY_FL_USER4|RUBY_FL_USER5)) >> (RUBY_FL_USHIFT+3))
print("T_BIGNUM: sign=%s len=%d (embed)" % (sign, len), file=result)
- append_command_output(debugger, "print ((struct RBignum *) %0#x)->as.ary" % val.GetValueAsUnsigned(), result)
+ append_expression(debugger, "((struct RBignum *) %0#x)->as.ary" % val.GetValueAsUnsigned(), result)
else:
len = val.GetValueForExpressionPath("->as.heap.len").GetValueAsSigned()
print("T_BIGNUM: sign=%s len=%d" % (sign, len), file=result)
print(val.Dereference(), file=result)
- append_command_output(debugger, "expression -Z %x -fx -- (const BDIGIT*)((struct RBignum*)%d)->as.heap.digits" % (len, val.GetValueAsUnsigned()), result)
- # append_command_output(debugger, "x ((struct RBignum *) %0#x)->as.heap.digits / %d" % (val.GetValueAsUnsigned(), len), result)
+ append_expression(debugger, "-Z %x -fx -- (const BDIGIT*)((struct RBignum*)%d)->as.heap.digits" % (len, val.GetValueAsUnsigned()), result)
+ # append_expression(debugger, "((struct RBignum *) %0#x)->as.heap.digits / %d" % (val.GetValueAsUnsigned(), len), result)
elif flType == RUBY_T_FLOAT:
- append_command_output(debugger, "print ((struct RFloat *)%d)->float_value" % val.GetValueAsUnsigned(), result)
+ append_expression(debugger, "((struct RFloat *)%d)->float_value" % val.GetValueAsUnsigned(), result)
elif flType == RUBY_T_RATIONAL:
tRRational = target.FindFirstType("struct RRational").GetPointerType()
val = val.Cast(tRRational)
@@ -397,39 +398,39 @@ def lldb_inspect(debugger, target, result, val):
flag = val.GetValueForExpressionPath("->typed_flag")
if flag.GetValueAsUnsigned() == 1:
print("T_DATA: %s" % val.GetValueForExpressionPath("->type->wrap_struct_name"), file=result)
- append_command_output(debugger, "p *(struct RTypedData *) %0#x" % val.GetValueAsUnsigned(), result)
+ append_expression(debugger, "*(struct RTypedData *) %0#x" % val.GetValueAsUnsigned(), result)
else:
print("T_DATA:", file=result)
- append_command_output(debugger, "p *(struct RData *) %0#x" % val.GetValueAsUnsigned(), result)
+ append_expression(debugger, "*(struct RData *) %0#x" % val.GetValueAsUnsigned(), result)
elif flType == RUBY_T_NODE:
tRTypedData = target.FindFirstType("struct RNode").GetPointerType()
nd_type = (flags & RUBY_NODE_TYPEMASK) >> RUBY_NODE_TYPESHIFT
- append_command_output(debugger, "p (node_type) %d" % nd_type, result)
+ append_expression(debugger, "(node_type) %d" % nd_type, result)
val = val.Cast(tRTypedData)
- append_command_output(debugger, "p *(struct RNode *) %0#x" % val.GetValueAsUnsigned(), result)
+ append_expression(debugger, "*(struct RNode *) %0#x" % val.GetValueAsUnsigned(), result)
elif flType == RUBY_T_MOVED:
tRTypedData = target.FindFirstType("struct RMoved").GetPointerType()
val = val.Cast(tRTypedData)
- append_command_output(debugger, "p *(struct RMoved *) %0#x" % val.GetValueAsUnsigned(), result)
+ append_expression(debugger, "*(struct RMoved *) %0#x" % val.GetValueAsUnsigned(), result)
elif flType == RUBY_T_MATCH:
tRTypedData = target.FindFirstType("struct RMatch").GetPointerType()
val = val.Cast(tRTypedData)
- append_command_output(debugger, "p *(struct RMatch *) %0#x" % val.GetValueAsUnsigned(), result)
+ append_expression(debugger, "*(struct RMatch *) %0#x" % val.GetValueAsUnsigned(), result)
elif flType == RUBY_T_IMEMO:
# I'm not sure how to get IMEMO_MASK out of lldb. It's not in globals()
imemo_type = (flags >> RUBY_FL_USHIFT) & 0x0F # IMEMO_MASK
print("T_IMEMO: ", file=result)
- append_command_output(debugger, "p (enum imemo_type) %d" % imemo_type, result)
- append_command_output(debugger, "p *(struct MEMO *) %0#x" % val.GetValueAsUnsigned(), result)
+ append_expression(debugger, "(enum imemo_type) %d" % imemo_type, result)
+ append_expression(debugger, "*(struct MEMO *) %0#x" % val.GetValueAsUnsigned(), result)
elif flType == RUBY_T_STRUCT:
tRTypedData = target.FindFirstType("struct RStruct").GetPointerType()
val = val.Cast(tRTypedData)
- append_command_output(debugger, "p *(struct RStruct *) %0#x" % val.GetValueAsUnsigned(), result)
+ append_expression(debugger, "*(struct RStruct *) %0#x" % val.GetValueAsUnsigned(), result)
elif flType == RUBY_T_ZOMBIE:
tRZombie = target.FindFirstType("struct RZombie").GetPointerType()
val = val.Cast(tRZombie)
- append_command_output(debugger, "p *(struct RZombie *) %0#x" % val.GetValueAsUnsigned(), result)
+ append_expression(debugger, "*(struct RZombie *) %0#x" % val.GetValueAsUnsigned(), result)
else:
print("Not-handled type %0#x" % flType, file=result)
print(val, file=result)
diff --git a/misc/lldb_rb/commands/heap_page_command.py b/misc/lldb_rb/commands/heap_page_command.py
index edb74a415b..b56a3eae4e 100644
--- a/misc/lldb_rb/commands/heap_page_command.py
+++ b/misc/lldb_rb/commands/heap_page_command.py
@@ -14,8 +14,8 @@ class HeapPageCommand(RbBaseCommand):
page = self._get_page(self.frame.EvaluateExpression(command))
page.Cast(self.t_heap_page_ptr)
- self._append_command_output(debugger, "p (struct heap_page *) %0#x" % page.GetValueAsUnsigned(), result)
- self._append_command_output(debugger, "p *(struct heap_page *) %0#x" % page.GetValueAsUnsigned(), result)
+ self._append_expression(debugger, "(struct heap_page *) %0#x" % page.GetValueAsUnsigned(), result)
+ self._append_expression(debugger, "*(struct heap_page *) %0#x" % page.GetValueAsUnsigned(), result)
def _get_page(self, val):
addr = val.GetValueAsUnsigned()
diff --git a/misc/lldb_rb/commands/rp_command.py b/misc/lldb_rb/commands/rp_command.py
index 9da9688b96..06b2516d50 100644
--- a/misc/lldb_rb/commands/rp_command.py
+++ b/misc/lldb_rb/commands/rp_command.py
@@ -13,4 +13,3 @@ class RbID2StrCommand(RbBaseCommand):
val = self.frame.EvaluateExpression(command)
inspector = RbInspector(debugger, result, self.ruby_globals)
inspector.inspect(val)
-
diff --git a/misc/lldb_rb/lldb_interface.py b/misc/lldb_rb/lldb_interface.py
index 893064db90..785a54b3e3 100644
--- a/misc/lldb_rb/lldb_interface.py
+++ b/misc/lldb_rb/lldb_interface.py
@@ -5,4 +5,3 @@ class LLDBInterface:
self.process = self.target.GetProcess()
self.thread = self.process.GetSelectedThread()
self.frame = self.thread.GetSelectedFrame()
-
diff --git a/misc/lldb_rb/rb_base_command.py b/misc/lldb_rb/rb_base_command.py
index d786a010b6..70a5addd6d 100644
--- a/misc/lldb_rb/rb_base_command.py
+++ b/misc/lldb_rb/rb_base_command.py
@@ -43,6 +43,7 @@ class RbBaseCommand(LLDBInterface):
self.internal_dict = _internal_dict
def __call__(self, debugger, command, exe_ctx, result):
+ self.ruby_globals = RbBaseCommand.lldb_init(debugger)
self.build_environment(debugger)
self.call(debugger, command, exe_ctx, result)
@@ -54,4 +55,3 @@ class RbBaseCommand(LLDBInterface):
def get_long_help(self):
return self.__class__.help_string
-
diff --git a/misc/lldb_rb/rb_heap_structs.py b/misc/lldb_rb/rb_heap_structs.py
index 0428b7fc3f..86b38dbbbd 100644
--- a/misc/lldb_rb/rb_heap_structs.py
+++ b/misc/lldb_rb/rb_heap_structs.py
@@ -108,11 +108,15 @@ class RbObject(LLDBInterface):
else:
return False
+ def as_type(self, type_name):
+ return self.val.Cast(self.tRValue.GetPointerType()).GetValueForExpressionPath("->as."+type_name)
+
def ary_ptr(self):
+ rval = self.as_type("array")
if self.flags & self.ruby_globals["RUBY_FL_USER1"]:
- ptr = self.val.GetValueForExpressionPath("->as.ary")
+ ptr = rval.GetValueForExpressionPath("->as.ary")
else:
- ptr = self.val.GetValueForExpressionPath("->as.heap.ptr")
+ ptr = rval.GetValueForExpressionPath("->as.heap.ptr")
return ptr
def ary_len(self):
@@ -122,19 +126,18 @@ class RbObject(LLDBInterface):
self.flUser7 | self.flUser8 | self.flUser9)
) >> (self.flUshift + 3))
else:
- len = self.val.GetValueForExpressionPath("->as.heap.len")
+ rval = self.as_type("array")
+ len = rval.GetValueForExpressionPath("->as.heap.len").GetValueAsSigned()
return len
def bignum_len(self):
- if self.flags & flUser2:
+ if self.flags & self.flUser2:
len = ((self.flags &
(self.flUser3 | self.flUser4 | self.flUser5)
) >> (self.flUshift + 3))
else:
- len = self.val.GetValueForExpressionPath("->as.heap.len")
+ len = (self.as_type("bignum").GetValueForExpressionPath("->as.heap.len").
+ GetValueAsUnsigned())
return len
-
-
-
diff --git a/misc/lldb_rb/utils.py b/misc/lldb_rb/utils.py
index b6cfe2fe77..86b5bdda2d 100644
--- a/misc/lldb_rb/utils.py
+++ b/misc/lldb_rb/utils.py
@@ -16,25 +16,26 @@ class RbInspector(LLDBInterface):
self.result.write(output1)
self.result.write(output2)
+ def _append_expression(self, expression):
+ self._append_command_output("expression " + expression)
+
def string2cstr(self, rstring):
"""Returns the pointer to the C-string in the given String object"""
if rstring.TypeIsPointerType():
rstring = rstring.Dereference()
flags = rstring.GetValueForExpressionPath(".basic->flags").unsigned
+ clen = int(rstring.GetValueForExpressionPath(".len").value, 0)
if flags & self.ruby_globals["RUBY_FL_USER1"]:
cptr = int(rstring.GetValueForExpressionPath(".as.heap.ptr").value, 0)
- clen = int(rstring.GetValueForExpressionPath(".as.heap.len").value, 0)
else:
cptr = int(rstring.GetValueForExpressionPath(".as.embed.ary").location, 0)
- clen = int(rstring.GetValueForExpressionPath(".as.embed.len").value, 0)
return cptr, clen
def output_string(self, rstring):
cptr, clen = self.string2cstr(rstring)
- expr = "print *(const char (*)[%d])%0#x" % (clen, cptr)
- self._append_command_output(expr)
+ self._append_expression("*(const char (*)[%d])%0#x" % (clen, cptr))
def fixnum_p(self, x):
return x & self.ruby_globals["RUBY_FIXNUM_FLAG"] != 0
@@ -50,7 +51,7 @@ class RbInspector(LLDBInterface):
def generic_inspect(self, val, rtype):
tRType = self.target.FindFirstType("struct %s" % rtype).GetPointerType()
val = val.Cast(tRType)
- self._append_command_output("p *(struct %s *) %0#x" % (rtype, val.GetValueAsUnsigned()))
+ self._append_expression("*(struct %s *) %0#x" % (rtype, val.GetValueAsUnsigned()))
def inspect(self, val):
rbTrue = self.ruby_globals["RUBY_Qtrue"]
@@ -71,13 +72,13 @@ class RbInspector(LLDBInterface):
elif self.fixnum_p(num):
print(num >> 1, file=self.result)
elif self.flonum_p(num):
- self._append_command_output("print rb_float_value(%0#x)" % val.GetValueAsUnsigned())
+ self._append_expression("rb_float_value(%0#x)" % val.GetValueAsUnsigned())
elif self.static_sym_p(num):
if num < 128:
print("T_SYMBOL: %c" % num, file=self.result)
else:
print("T_SYMBOL: (%x)" % num, file=self.result)
- self._append_command_output("p rb_id2name(%0#x)" % (num >> 8))
+ self._append_expression("rb_id2name(%0#x)" % (num >> 8))
elif num & rbImmediateMask:
print('immediate(%x)' % num, file=self.result)
@@ -99,7 +100,7 @@ class RbInspector(LLDBInterface):
elif rval.is_type("RUBY_T_OBJECT"):
self.result.write('T_OBJECT: %s' % flaginfo)
- self._append_command_output("print *(struct RObject*)%0#x" % val.GetValueAsUnsigned())
+ self._append_expression("*(struct RObject*)%0#x" % val.GetValueAsUnsigned())
elif (rval.is_type("RUBY_T_CLASS") or
rval.is_type("RUBY_T_MODULE") or
@@ -107,10 +108,10 @@ class RbInspector(LLDBInterface):
self.result.write('T_%s: %s' % (rval.type_name.split('_')[-1], flaginfo))
tRClass = self.target.FindFirstType("struct RClass")
- self._append_command_output("print *(struct RClass*)%0#x" % val.GetValueAsUnsigned())
+ self._append_expression("*(struct RClass*)%0#x" % val.GetValueAsUnsigned())
if not val.Cast(tRClass).GetChildMemberWithName("ptr").IsValid():
- self._append_command_output(
- "print *(struct rb_classext_struct*)%0#x" %
+ self._append_expression(
+ "*(struct rb_classext_struct*)%0#x" %
(val.GetValueAsUnsigned() + tRClass.GetByteSize())
)
@@ -118,6 +119,10 @@ class RbInspector(LLDBInterface):
self.result.write('T_STRING: %s' % flaginfo)
tRString = self.target.FindFirstType("struct RString").GetPointerType()
+ chilled = self.ruby_globals["RUBY_FL_USER3"]
+ if (rval.flags & chilled) != 0:
+ self.result.write("[CHILLED] ")
+
rb_enc_mask = self.ruby_globals["RUBY_ENCODING_MASK"]
rb_enc_shift = self.ruby_globals["RUBY_ENCODING_SHIFT"]
encidx = ((rval.flags & rb_enc_mask) >> rb_enc_shift)
@@ -134,7 +139,7 @@ class RbInspector(LLDBInterface):
if len == 0:
self.result.write("(empty)\n")
else:
- self._append_command_output("print *(const char (*)[%d])%0#x" % (len, ptr))
+ self._append_expression("*(const char (*)[%d])%0#x" % (len, ptr))
elif rval.is_type("RUBY_T_SYMBOL"):
self.result.write('T_SYMBOL: %s' % flaginfo)
@@ -142,12 +147,10 @@ class RbInspector(LLDBInterface):
tRString = self.target.FindFirstType("struct RString").GetPointerType()
val = val.Cast(tRSymbol)
- self._append_command_output(
- 'print (ID)%0#x ' % val.GetValueForExpressionPath("->id").GetValueAsUnsigned())
+ self._append_expression('(ID)%0#x ' % val.GetValueForExpressionPath("->id").GetValueAsUnsigned())
self.output_string(val.GetValueForExpressionPath("->fstr").Cast(tRString))
elif rval.is_type("RUBY_T_ARRAY"):
- tRArray = self.target.FindFirstType("struct RArray").GetPointerType()
len = rval.ary_len()
ptr = rval.ary_ptr()
@@ -166,19 +169,20 @@ class RbInspector(LLDBInterface):
else:
self.result.write("\n")
if ptr.GetValueAsSigned() == 0:
- self._append_command_output(
- "expression -fx -- ((struct RArray*)%0#x)->as.ary" % val.GetValueAsUnsigned())
+ self._append_expression("-fx -- ((struct RArray*)%0#x)->as.ary" % val.GetValueAsUnsigned())
else:
- self._append_command_output(
- "expression -Z %d -fx -- (const VALUE*)%0#x" % (len, ptr.GetValueAsUnsigned()))
+ self._append_expression("-Z %d -fx -- (const VALUE*)%0#x" % (len, ptr.GetValueAsUnsigned()))
elif rval.is_type("RUBY_T_HASH"):
self.result.write("T_HASH: %s" % flaginfo)
- self._append_command_output("p *(struct RHash *) %0#x" % val.GetValueAsUnsigned())
+ ptr = val.GetValueAsUnsigned()
+ self._append_expression("*(struct RHash *) %0#x" % ptr)
+ if rval.flags & self.ruby_globals["RUBY_FL_USER3"]:
+ self._append_expression("*(struct st_table *) (%0#x + sizeof(struct RHash))" % ptr)
+ else:
+ self._append_expression("*(struct ar_table *) (%0#x + sizeof(struct RHash))" % ptr)
elif rval.is_type("RUBY_T_BIGNUM"):
- tRBignum = self.target.FindFirstType("struct RBignum").GetPointerType()
-
sign = '-'
if (rval.flags & self.ruby_globals["RUBY_FL_USER1"]) != 0:
sign = '+'
@@ -186,17 +190,16 @@ class RbInspector(LLDBInterface):
if rval.flags & self.ruby_globals["RUBY_FL_USER2"]:
print("T_BIGNUM: sign=%s len=%d (embed)" % (sign, len), file=self.result)
- self._append_command_output("print ((struct RBignum *) %0#x)->as.ary"
+ self._append_expression("((struct RBignum *) %0#x)->as.ary"
% val.GetValueAsUnsigned())
else:
print("T_BIGNUM: sign=%s len=%d" % (sign, len), file=self.result)
- print(val.Dereference(), file=self.result)
- self._append_command_output(
- "expression -Z %x -fx -- (const BDIGIT*)((struct RBignum*)%d)->as.heap.digits" %
- (len, val.GetValueAsUnsigned()))
+ print(rval.as_type("bignum"), file=self.result)
+ self._append_expression("-Z %d -fx -- ((struct RBignum*)%d)->as.heap.digits" %
+ (len, val.GetValueAsUnsigned()))
elif rval.is_type("RUBY_T_FLOAT"):
- self._append_command_output("print ((struct RFloat *)%d)->float_value"
+ self._append_expression("((struct RFloat *)%d)->float_value"
% val.GetValueAsUnsigned())
elif rval.is_type("RUBY_T_RATIONAL"):
@@ -237,12 +240,10 @@ class RbInspector(LLDBInterface):
print("T_DATA: %s" %
val.GetValueForExpressionPath("->type->wrap_struct_name"),
file=self.result)
- self._append_command_output(
- "p *(struct RTypedData *) %0#x" % val.GetValueAsUnsigned())
+ self._append_expression("*(struct RTypedData *) %0#x" % val.GetValueAsUnsigned())
else:
print("T_DATA:", file=self.result)
- self._append_command_output(
- "p *(struct RData *) %0#x" % val.GetValueAsUnsigned())
+ self._append_expression("*(struct RData *) %0#x" % val.GetValueAsUnsigned())
elif rval.is_type("RUBY_T_NODE"):
tRNode = self.target.FindFirstType("struct RNode").GetPointerType()
@@ -252,16 +253,226 @@ class RbInspector(LLDBInterface):
nd_type = (rval.flags & rbNodeTypeMask) >> rbNodeTypeShift
val = val.Cast(tRNode)
- self._append_command_output("p (node_type) %d" % nd_type)
- self._append_command_output("p *(struct RNode *) %0#x" % val.GetValueAsUnsigned())
+ self._append_expression("(node_type) %d" % nd_type)
+
+ if nd_type == self.ruby_globals["NODE_SCOPE"]:
+ self._append_expression("*(struct RNode_SCOPE *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_BLOCK"]:
+ self._append_expression("*(struct RNode_BLOCK *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_IF"]:
+ self._append_expression("*(struct RNode_IF *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_UNLESS"]:
+ self._append_expression("*(struct RNode_UNLESS *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_CASE"]:
+ self._append_expression("*(struct RNode_CASE *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_CASE2"]:
+ self._append_expression("*(struct RNode_CASE2 *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_CASE3"]:
+ self._append_expression("*(struct RNode_CASE3 *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_WHEN"]:
+ self._append_expression("*(struct RNode_WHEN *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_IN"]:
+ self._append_expression("*(struct RNode_IN *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_WHILE"]:
+ self._append_expression("*(struct RNode_WHILE *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_UNTIL"]:
+ self._append_expression("*(struct RNode_UNTIL *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_ITER"]:
+ self._append_expression("*(struct RNode_ITER *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_FOR"]:
+ self._append_expression("*(struct RNode_FOR *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_FOR_MASGN"]:
+ self._append_expression("*(struct RNode_FOR_MASGN *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_BREAK"]:
+ self._append_expression("*(struct RNode_BREAK *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_NEXT"]:
+ self._append_expression("*(struct RNode_NEXT *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_REDO"]:
+ self._append_expression("*(struct RNode_REDO *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_RETRY"]:
+ self._append_expression("*(struct RNode_RETRY *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_BEGIN"]:
+ self._append_expression("*(struct RNode_BEGIN *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_RESCUE"]:
+ self._append_expression("*(struct RNode_RESCUE *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_RESBODY"]:
+ self._append_expression("*(struct RNode_RESBODY *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_ENSURE"]:
+ self._append_expression("*(struct RNode_ENSURE *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_AND"]:
+ self._append_expression("*(struct RNode_AND *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_OR"]:
+ self._append_expression("*(struct RNode_OR *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_MASGN"]:
+ self._append_expression("*(struct RNode_MASGN *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_LASGN"]:
+ self._append_expression("*(struct RNode_LASGN *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_DASGN"]:
+ self._append_expression("*(struct RNode_DASGN *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_GASGN"]:
+ self._append_expression("*(struct RNode_GASGN *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_IASGN"]:
+ self._append_expression("*(struct RNode_IASGN *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_CDECL"]:
+ self._append_expression("*(struct RNode_CDECL *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_CVASGN"]:
+ self._append_expression("*(struct RNode_CVASGN *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_OP_ASGN1"]:
+ self._append_expression("*(struct RNode_OP_ASGN1 *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_OP_ASGN2"]:
+ self._append_expression("*(struct RNode_OP_ASGN2 *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_OP_ASGN_AND"]:
+ self._append_expression("*(struct RNode_OP_ASGN_AND *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_OP_ASGN_OR"]:
+ self._append_expression("*(struct RNode_OP_ASGN_OR *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_OP_CDECL"]:
+ self._append_expression("*(struct RNode_OP_CDECL *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_CALL"]:
+ self._append_expression("*(struct RNode_CALL *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_OPCALL"]:
+ self._append_expression("*(struct RNode_OPCALL *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_FCALL"]:
+ self._append_expression("*(struct RNode_FCALL *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_VCALL"]:
+ self._append_expression("*(struct RNode_VCALL *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_QCALL"]:
+ self._append_expression("*(struct RNode_QCALL *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_SUPER"]:
+ self._append_expression("*(struct RNode_SUPER *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_ZSUPER"]:
+ self._append_expression("*(struct RNode_ZSUPER *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_LIST"]:
+ self._append_expression("*(struct RNode_LIST *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_ZLIST"]:
+ self._append_expression("*(struct RNode_ZLIST *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_HASH"]:
+ self._append_expression("*(struct RNode_HASH *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_RETURN"]:
+ self._append_expression("*(struct RNode_RETURN *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_YIELD"]:
+ self._append_expression("*(struct RNode_YIELD *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_LVAR"]:
+ self._append_expression("*(struct RNode_LVAR *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_DVAR"]:
+ self._append_expression("*(struct RNode_DVAR *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_GVAR"]:
+ self._append_expression("*(struct RNode_GVAR *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_CONST"]:
+ self._append_expression("*(struct RNode_CONST *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_CVAR"]:
+ self._append_expression("*(struct RNode_CVAR *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_NTH_REF"]:
+ self._append_expression("*(struct RNode_NTH_REF *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_BACK_REF"]:
+ self._append_expression("*(struct RNode_BACK_REF *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_MATCH"]:
+ self._append_expression("*(struct RNode_MATCH *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_MATCH2"]:
+ self._append_expression("*(struct RNode_MATCH2 *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_MATCH3"]:
+ self._append_expression("*(struct RNode_MATCH3 *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_STR"]:
+ self._append_expression("*(struct RNode_STR *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_DSTR"]:
+ self._append_expression("*(struct RNode_DSTR *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_XSTR"]:
+ self._append_expression("*(struct RNode_XSTR *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_DXSTR"]:
+ self._append_expression("*(struct RNode_DXSTR *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_EVSTR"]:
+ self._append_expression("*(struct RNode_EVSTR *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_REGX"]:
+ self._append_expression("*(struct RNode_REGX *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_DREGX"]:
+ self._append_expression("*(struct RNode_DREGX *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_ONCE"]:
+ self._append_expression("*(struct RNode_ONCE *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_ARGS"]:
+ self._append_expression("*(struct RNode_ARGS *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_ARGS_AUX"]:
+ self._append_expression("*(struct RNode_ARGS_AUX *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_OPT_ARG"]:
+ self._append_expression("*(struct RNode_OPT_ARG *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_KW_ARG"]:
+ self._append_expression("*(struct RNode_KW_ARG *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_POSTARG"]:
+ self._append_expression("*(struct RNode_POSTARG *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_ARGSCAT"]:
+ self._append_expression("*(struct RNode_ARGSCAT *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_ARGSPUSH"]:
+ self._append_expression("*(struct RNode_ARGSPUSH *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_SPLAT"]:
+ self._append_expression("*(struct RNode_SPLAT *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_DEFN"]:
+ self._append_expression("*(struct RNode_DEFN *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_DEFS"]:
+ self._append_expression("*(struct RNode_DEFS *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_ALIAS"]:
+ self._append_expression("*(struct RNode_ALIAS *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_VALIAS"]:
+ self._append_expression("*(struct RNode_VALIAS *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_UNDEF"]:
+ self._append_expression("*(struct RNode_UNDEF *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_CLASS"]:
+ self._append_expression("*(struct RNode_CLASS *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_MODULE"]:
+ self._append_expression("*(struct RNode_MODULE *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_SCLASS"]:
+ self._append_expression("*(struct RNode_SCLASS *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_COLON2"]:
+ self._append_expression("*(struct RNode_COLON2 *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_COLON3"]:
+ self._append_expression("*(struct RNode_COLON3 *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_DOT2"]:
+ self._append_expression("*(struct RNode_DOT2 *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_DOT3"]:
+ self._append_expression("*(struct RNode_DOT3 *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_FLIP2"]:
+ self._append_expression("*(struct RNode_FLIP2 *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_FLIP3"]:
+ self._append_expression("*(struct RNode_FLIP3 *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_SELF"]:
+ self._append_expression("*(struct RNode_SELF *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_NIL"]:
+ self._append_expression("*(struct RNode_NIL *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_TRUE"]:
+ self._append_expression("*(struct RNode_TRUE *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_FALSE"]:
+ self._append_expression("*(struct RNode_FALSE *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_ERRINFO"]:
+ self._append_expression("*(struct RNode_ERRINFO *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_DEFINED"]:
+ self._append_expression("*(struct RNode_DEFINED *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_POSTEXE"]:
+ self._append_expression("*(struct RNode_POSTEXE *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_DSYM"]:
+ self._append_expression("*(struct RNode_DSYM *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_ATTRASGN"]:
+ self._append_expression("*(struct RNode_ATTRASGN *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_LAMBDA"]:
+ self._append_expression("*(struct RNode_LAMBDA *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_ARYPTN"]:
+ self._append_expression("*(struct RNode_ARYPTN *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_HSHPTN"]:
+ self._append_expression("*(struct RNode_HSHPTN *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_FNDPTN"]:
+ self._append_expression("*(struct RNode_FNDPTN *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_ERROR"]:
+ self._append_expression("*(struct RNode_ERROR *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_LINE"]:
+ self._append_expression("*(struct RNode_LINE *) %0#x" % val.GetValueAsUnsigned())
+ elif nd_type == self.ruby_globals["NODE_FILE"]:
+ self._append_expression("*(struct RNode_FILE *) %0#x" % val.GetValueAsUnsigned())
+ else:
+ self._append_expression("*(struct RNode *) %0#x" % val.GetValueAsUnsigned())
elif rval.is_type("RUBY_T_IMEMO"):
imemo_type = ((rval.flags >> self.ruby_globals["RUBY_FL_USHIFT"])
& IMEMO_MASK)
print("T_IMEMO: ", file=self.result)
- self._append_command_output("p (enum imemo_type) %d" % imemo_type)
- self._append_command_output("p *(struct MEMO *) %0#x" % val.GetValueAsUnsigned())
+ self._append_expression("(enum imemo_type) %d" % imemo_type)
+ self._append_expression("*(struct MEMO *) %0#x" % val.GetValueAsUnsigned())
elif rval.is_type("RUBY_T_FILE"):
self.generic_inspect(val, "RFile")
@@ -281,4 +492,3 @@ class RbInspector(LLDBInterface):
else:
print("Not-handled type %0#x" % rval.type, file=self.result)
print(val, file=self.result)
-
diff --git a/misc/lldb_yjit.py b/misc/lldb_yjit.py
deleted file mode 100644
index cc37b990ea..0000000000
--- a/misc/lldb_yjit.py
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env python
-#coding: utf-8
-#
-# Usage: run `command script import -r misc/lldb_yjit.py` on LLDB
-#
-
-from __future__ import print_function
-import lldb
-import os
-import shlex
-
-def list_comments(debugger, command, result, internal_dict):
- target = debugger.GetSelectedTarget()
- process = target.GetProcess()
- thread = process.GetSelectedThread()
- frame = thread.GetSelectedFrame()
-
- # Get the different types we need
- rb_darray_meta_t = target.FindFirstType("rb_darray_meta_t")
- codeblock_t = target.FindFirstType("codeblock_t")
- yjit_comment = target.FindFirstType("yjit_comment")
-
- # Get the global variables we need
- comments = target.FindFirstGlobalVariable("yjit_code_comments")
- cb = target.FindFirstGlobalVariable("cb").Cast(codeblock_t.GetPointerType())
-
- # Get the address of the memory block we're using
- mem_addr = cb.GetChildMemberWithName("mem_block").GetValueAsUnsigned()
-
- # Find the size of the darray comment list
- meta = comments.Cast(rb_darray_meta_t.GetPointerType())
- size = meta.GetChildMemberWithName("size").GetValueAsUnsigned()
-
- # Get the address of the block following the metadata header
- t_offset = comments.GetValueAsUnsigned() + rb_darray_meta_t.GetByteSize()
-
- # Loop through each comment and print
- for t in range(0, size):
- addr = lldb.SBAddress(t_offset + (t * yjit_comment.GetByteSize()), target)
- comment = target.CreateValueFromAddress("yjit_comment", addr, yjit_comment)
- string = comment.GetChildMemberWithName("comment")
- comment_offset = mem_addr + comment.GetChildMemberWithName("offset").GetValueAsUnsigned()
- print("%0#x %s" % (comment_offset, string.GetSummary()), file = result)
-
-
-def __lldb_init_module(debugger, internal_dict):
- debugger.HandleCommand("command script add -f lldb_yjit.list_comments lc")
diff --git a/misc/yjit_perf.py b/misc/yjit_perf.py
new file mode 100755
index 0000000000..61434e5eb4
--- /dev/null
+++ b/misc/yjit_perf.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python3
+import os
+import sys
+from collections import Counter, defaultdict
+import os.path
+
+# Aggregating cycles per symbol and dso
+total_cycles = 0
+category_cycles = Counter()
+detailed_category_cycles = defaultdict(Counter)
+categories = set()
+
+def truncate_symbol(symbol, max_length=50):
+ """ Truncate the symbol name to a maximum length """
+ return symbol if len(symbol) <= max_length else symbol[:max_length-3] + '...'
+
+def categorize_symbol(dso, symbol):
+ """ Categorize the symbol based on the defined criteria """
+ if dso == 'sqlite3_native.so':
+ return '[sqlite3]'
+ elif 'SHA256' in symbol:
+ return '[sha256]'
+ elif symbol.startswith('[JIT] gen_send'):
+ return '[JIT send]'
+ elif symbol.startswith('[JIT]'):
+ return '[JIT code]'
+ elif '::' in symbol or symbol.startswith('yjit::') or symbol.startswith('_ZN4yjit'):
+ return '[YJIT compile]'
+ elif symbol.startswith('rb_vm_') or symbol.startswith('vm_') or symbol in {
+ "rb_call0", "callable_method_entry_or_negative", "invoke_block_from_c_bh",
+ "rb_funcallv_scope", "setup_parameters_complex", "rb_yield"}:
+ return '[interpreter]'
+ elif symbol.startswith('rb_hash_') or symbol.startswith('hash_'):
+ return '[rb_hash_*]'
+ elif symbol.startswith('rb_ary_') or symbol.startswith('ary_'):
+ return '[rb_ary_*]'
+ elif symbol.startswith('rb_str_') or symbol.startswith('str_'):
+ return '[rb_str_*]'
+ elif symbol.startswith('rb_sym') or symbol.startswith('sym_'):
+ return '[rb_sym_*]'
+ elif symbol.startswith('rb_st_') or symbol.startswith('st_'):
+ return '[rb_st_*]'
+ elif symbol.startswith('rb_ivar_') or 'shape' in symbol:
+ return '[ivars]'
+ elif 'match' in symbol or symbol.startswith('rb_reg') or symbol.startswith('onig'):
+ return '[regexp]'
+ elif 'alloc' in symbol or 'free' in symbol or 'gc' in symbol:
+ return '[GC]'
+ elif 'pthread' in symbol and 'lock' in symbol:
+ return '[pthread lock]'
+ else:
+ return symbol # Return the symbol itself for uncategorized symbols
+
+def process_event(event):
+ global total_cycles, category_cycles, detailed_category_cycles, categories
+
+ full_dso = event.get("dso", "Unknown_dso")
+ dso = os.path.basename(full_dso)
+ symbol = event.get("symbol", "[unknown]")
+ cycles = event["sample"]["period"]
+ total_cycles += cycles
+
+ category = categorize_symbol(dso, symbol)
+ category_cycles[category] += cycles
+ detailed_category_cycles[category][(dso, symbol)] += cycles
+
+ if category.startswith('[') and category.endswith(']'):
+ categories.add(category)
+
+def trace_end():
+ if total_cycles == 0:
+ return
+
+ print("Aggregated Event Data:")
+ print("{:<20} {:<50} {:>20} {:>15}".format("[dso]", "[symbol or category]", "[top-most cycle ratio]", "[num cycles]"))
+
+ for category, cycles in category_cycles.most_common():
+ ratio = (cycles / total_cycles) * 100
+ dsos = {dso for dso, _ in detailed_category_cycles[category]}
+ dso_display = next(iter(dsos)) if len(dsos) == 1 else "Multiple DSOs"
+ print("{:<20} {:<50} {:>20.2f}% {:>15}".format(dso_display, truncate_symbol(category), ratio, cycles))
+
+ # Category breakdown
+ for category in categories:
+ symbols = detailed_category_cycles[category]
+ category_total = sum(symbols.values())
+ category_ratio = (category_total / total_cycles) * 100
+ print(f"\nCategory: {category} ({category_ratio:.2f}%)")
+ print("{:<20} {:<50} {:>20} {:>15}".format("[dso]", "[symbol]", "[top-most cycle ratio]", "[num cycles]"))
+ for (dso, symbol), cycles in symbols.most_common():
+ symbol_ratio = (cycles / category_total) * 100
+ print("{:<20} {:<50} {:>20.2f}% {:>15}".format(dso, truncate_symbol(symbol), symbol_ratio, cycles))
+
+# There are two ways to use this script:
+# 1) perf script -s misc/yjit_perf.py -- native interface
+# 2) perf script > perf.txt && misc/yjit_perf.py perf.txt -- hack, which doesn't require perf with Python support
+#
+# In both cases, __name__ is "__main__". The following code implements (2) when sys.argv is 2.
+if __name__ == "__main__" and len(sys.argv) == 2:
+ if len(sys.argv) != 2:
+ print("Usage: yjit_perf.py <filename>")
+ sys.exit(1)
+
+ with open(sys.argv[1], "r") as file:
+ for line in file:
+ # [Example]
+ # ruby 78207 3482.848465: 1212775 cpu_core/cycles:P/: 5c0333f682e1 [JIT] getlocal_WC_0+0x0 (/tmp/perf-78207.map)
+ row = line.split(maxsplit=6)
+
+ period = row[3] # "1212775"
+ symbol, dso = row[6].split(" (") # "[JIT] getlocal_WC_0+0x0", "/tmp/perf-78207.map)\n"
+ symbol = symbol.split("+")[0] # "[JIT] getlocal_WC_0"
+ dso = dso.split(")")[0] # "/tmp/perf-78207.map"
+
+ process_event({"dso": dso, "symbol": symbol, "sample": {"period": int(period)}})
+ trace_end()
diff --git a/missing/dtoa.c b/missing/dtoa.c
index b7a8302875..bce2cb22a1 100644
--- a/missing/dtoa.c
+++ b/missing/dtoa.c
@@ -183,7 +183,10 @@
#undef Long
#undef ULong
+#include <assert.h>
#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
#if (INT_MAX >> 30) && !(INT_MAX >> 31)
#define Long int
@@ -195,7 +198,7 @@
#error No 32bit integer
#endif
-#if HAVE_LONG_LONG
+#if defined(HAVE_LONG_LONG) && (HAVE_LONG_LONG)
#define Llong LONG_LONG
#else
#define NO_LONG_LONG
@@ -221,12 +224,12 @@
#ifdef MALLOC
extern void *MALLOC(size_t);
#else
-#define MALLOC xmalloc
+#define MALLOC malloc
#endif
#ifdef FREE
extern void FREE(void*);
#else
-#define FREE xfree
+#define FREE free
#endif
#ifndef NO_SANITIZE
#define NO_SANITIZE(x, y) y
@@ -502,7 +505,7 @@ extern double rnd_prod(double, double), rnd_quot(double, double);
#endif
#ifndef ATOMIC_PTR_CAS
-#define ATOMIC_PTR_CAS(var, old, new) ((var) = (new), (old))
+#define ATOMIC_PTR_CAS(var, old, new) ((var) = (new), (void *)(old))
#endif
#ifndef LIKELY
#define LIKELY(x) (x)
diff --git a/missing/explicit_bzero.c b/missing/explicit_bzero.c
index 1220e5f9ad..59417e158e 100644
--- a/missing/explicit_bzero.c
+++ b/missing/explicit_bzero.c
@@ -1,12 +1,9 @@
#ifndef __STDC_WANT_LIB_EXT1__
-#define __STDC_WANT_LIB_EXT1__ 1
+#define __STDC_WANT_LIB_EXT1__ 1 /* for memset_s() */
#endif
#include "ruby/missing.h"
#include <string.h>
-#ifdef HAVE_MEMSET_S
-# include <string.h>
-#endif
#ifdef _WIN32
#include <windows.h>
diff --git a/missing/procstat_vm.c b/missing/procstat_vm.c
index 76fd8f61ba..155ee355d1 100644
--- a/missing/procstat_vm.c
+++ b/missing/procstat_vm.c
@@ -6,7 +6,7 @@
# define KVME_TYPE_MGTDEVICE 8
# endif
void
-procstat_vm(struct procstat *procstat, struct kinfo_proc *kipp)
+procstat_vm(struct procstat *procstat, struct kinfo_proc *kipp, FILE *errout)
{
struct kinfo_vmentry *freep, *kve;
int ptrwidth;
@@ -17,7 +17,7 @@ procstat_vm(struct procstat *procstat, struct kinfo_proc *kipp)
#else
ptrwidth = 2*sizeof(void *) + 2;
#endif
- fprintf(stderr, "%*s %*s %3s %4s %4s %3s %3s %4s %-2s %-s\n",
+ fprintf(errout, "%*s %*s %3s %4s %4s %3s %3s %4s %-2s %-s\n",
ptrwidth, "START", ptrwidth, "END", "PRT", "RES",
"P""RES", "REF", "SHD", "FL", "TP", "PATH");
@@ -30,20 +30,20 @@ procstat_vm(struct procstat *procstat, struct kinfo_proc *kipp)
return;
for (i = 0; i < cnt; i++) {
kve = &freep[i];
- fprintf(stderr, "%#*jx ", ptrwidth, (uintmax_t)kve->kve_start);
- fprintf(stderr, "%#*jx ", ptrwidth, (uintmax_t)kve->kve_end);
- fprintf(stderr, "%s", kve->kve_protection & KVME_PROT_READ ? "r" : "-");
- fprintf(stderr, "%s", kve->kve_protection & KVME_PROT_WRITE ? "w" : "-");
- fprintf(stderr, "%s ", kve->kve_protection & KVME_PROT_EXEC ? "x" : "-");
- fprintf(stderr, "%4d ", kve->kve_resident);
- fprintf(stderr, "%4d ", kve->kve_private_resident);
- fprintf(stderr, "%3d ", kve->kve_ref_count);
- fprintf(stderr, "%3d ", kve->kve_shadow_count);
- fprintf(stderr, "%-1s", kve->kve_flags & KVME_FLAG_COW ? "C" : "-");
- fprintf(stderr, "%-1s", kve->kve_flags & KVME_FLAG_NEEDS_COPY ? "N" :
+ fprintf(errout, "%#*jx ", ptrwidth, (uintmax_t)kve->kve_start);
+ fprintf(errout, "%#*jx ", ptrwidth, (uintmax_t)kve->kve_end);
+ fprintf(errout, "%s", kve->kve_protection & KVME_PROT_READ ? "r" : "-");
+ fprintf(errout, "%s", kve->kve_protection & KVME_PROT_WRITE ? "w" : "-");
+ fprintf(errout, "%s ", kve->kve_protection & KVME_PROT_EXEC ? "x" : "-");
+ fprintf(errout, "%4d ", kve->kve_resident);
+ fprintf(errout, "%4d ", kve->kve_private_resident);
+ fprintf(errout, "%3d ", kve->kve_ref_count);
+ fprintf(errout, "%3d ", kve->kve_shadow_count);
+ fprintf(errout, "%-1s", kve->kve_flags & KVME_FLAG_COW ? "C" : "-");
+ fprintf(errout, "%-1s", kve->kve_flags & KVME_FLAG_NEEDS_COPY ? "N" :
"-");
- fprintf(stderr, "%-1s", kve->kve_flags & KVME_FLAG_SUPER ? "S" : "-");
- fprintf(stderr, "%-1s ", kve->kve_flags & KVME_FLAG_GROWS_UP ? "U" :
+ fprintf(errout, "%-1s", kve->kve_flags & KVME_FLAG_SUPER ? "S" : "-");
+ fprintf(errout, "%-1s ", kve->kve_flags & KVME_FLAG_GROWS_UP ? "U" :
kve->kve_flags & KVME_FLAG_GROWS_DOWN ? "D" : "-");
switch (kve->kve_type) {
case KVME_TYPE_NONE:
@@ -78,8 +78,8 @@ procstat_vm(struct procstat *procstat, struct kinfo_proc *kipp)
str = "??";
break;
}
- fprintf(stderr, "%-2s ", str);
- fprintf(stderr, "%-s\n", kve->kve_path);
+ fprintf(errout, "%-2s ", str);
+ fprintf(errout, "%-s\n", kve->kve_path);
}
free(freep);
}
diff --git a/missing/setproctitle.c b/missing/setproctitle.c
index 811829c060..f90886671c 100644
--- a/missing/setproctitle.c
+++ b/missing/setproctitle.c
@@ -80,10 +80,20 @@ static char **argv1_addr = NULL;
#endif /* HAVE_SETPROCTITLE */
+#if defined(SPT_TYPE) && SPT_TYPE == SPT_REUSEARGV
+# define ALLOCATE_ENVIRON 1
+#else
+# define ALLOCATE_ENVIRON 0
+#endif
+
+#if ALLOCATE_ENVIRON
+static char **orig_environ = NULL;
+#endif
+
void
compat_init_setproctitle(int argc, char *argv[])
{
-#if defined(SPT_TYPE) && SPT_TYPE == SPT_REUSEARGV
+#if ALLOCATE_ENVIRON
extern char **environ;
char *lastargv = NULL;
char *lastenvp = NULL;
@@ -100,9 +110,10 @@ compat_init_setproctitle(int argc, char *argv[])
return;
/* Fail if we can't allocate room for the new environment */
- for (i = 0; envp[i] != NULL; i++)
- ;
- if ((environ = calloc(i + 1, sizeof(*environ))) == NULL) {
+ for (i = 0; envp[i] != NULL; i++);
+
+ orig_environ = environ = xcalloc(i + 1, sizeof(*environ));
+ if (environ == NULL) {
environ = envp; /* put it back */
return;
}
@@ -134,7 +145,29 @@ compat_init_setproctitle(int argc, char *argv[])
#endif /* SPT_REUSEARGV */
}
+void
+ruby_free_proctitle(void)
+{
+#if ALLOCATE_ENVIRON
+ extern char **environ;
+
+ if (!orig_environ) return; /* environ is allocated by OS */
+
+ /* ruby_setenv could allocate a new environ, so we need to free orig_environ
+ * in that case. */
+ if (environ != orig_environ) {
+ for (int i = 0; orig_environ[i] != NULL; i++) {
+ xfree(orig_environ[i]);
+ }
+
+ xfree(orig_environ);
+ orig_environ = NULL;
+ }
+#endif
+}
+
#ifndef HAVE_SETPROCTITLE
+
void
setproctitle(const char *fmt, ...)
{
diff --git a/node.c b/node.c
index 027b00ed06..2efcd6eba8 100644
--- a/node.c
+++ b/node.c
@@ -9,1215 +9,123 @@
**********************************************************************/
-#include "internal.h"
-#include "internal/hash.h"
-#include "internal/variable.h"
-#include "ruby/ruby.h"
-#include "vm_core.h"
-
-#define NODE_BUF_DEFAULT_LEN 16
-
-#define A(str) rb_str_cat2(buf, (str))
-#define AR(str) rb_str_concat(buf, (str))
-
-#define A_INDENT add_indent(buf, indent)
-#define D_INDENT rb_str_cat2(indent, next_indent)
-#define D_DEDENT rb_str_resize(indent, RSTRING_LEN(indent) - 4)
-#define A_ID(id) add_id(buf, (id))
-#define A_INT(val) rb_str_catf(buf, "%d", (val))
-#define A_LONG(val) rb_str_catf(buf, "%ld", (val))
-#define A_LIT(lit) AR(rb_dump_literal(lit))
-#define A_NODE_HEADER(node, term) \
- rb_str_catf(buf, "@ %s (id: %d, line: %d, location: (%d,%d)-(%d,%d))%s"term, \
- ruby_node_name(nd_type(node)), nd_node_id(node), nd_line(node), \
- nd_first_lineno(node), nd_first_column(node), \
- nd_last_lineno(node), nd_last_column(node), \
- (node->flags & NODE_FL_NEWLINE ? "*" : ""))
-#define A_FIELD_HEADER(len, name, term) \
- rb_str_catf(buf, "+- %.*s:"term, (len), (name))
-#define D_FIELD_HEADER(len, name, term) (A_INDENT, A_FIELD_HEADER(len, name, term))
-
-#define D_NULL_NODE (A_INDENT, A("(null node)\n"))
-#define D_NODE_HEADER(node) (A_INDENT, A_NODE_HEADER(node, "\n"))
-
-#define COMPOUND_FIELD(len, name) \
- FIELD_BLOCK((D_FIELD_HEADER((len), (name), "\n"), D_INDENT), D_DEDENT)
-
-#define COMPOUND_FIELD1(name, ann) \
- COMPOUND_FIELD(FIELD_NAME_LEN(name, ann), \
- FIELD_NAME_DESC(name, ann))
-
-#define FIELD_NAME_DESC(name, ann) name " (" ann ")"
-#define FIELD_NAME_LEN(name, ann) (int)( \
- comment ? \
- rb_strlen_lit(FIELD_NAME_DESC(name, ann)) : \
- rb_strlen_lit(name))
-#define SIMPLE_FIELD(len, name) \
- FIELD_BLOCK(D_FIELD_HEADER((len), (name), " "), A("\n"))
-
-#define FIELD_BLOCK(init, reset) \
- for (init, field_flag = 1; \
- field_flag; /* should be optimized away */ \
- reset, field_flag = 0)
-
-#define SIMPLE_FIELD1(name, ann) SIMPLE_FIELD(FIELD_NAME_LEN(name, ann), FIELD_NAME_DESC(name, ann))
-#define F_CUSTOM1(name, ann) SIMPLE_FIELD1(#name, ann)
-#define F_ID(name, ann) SIMPLE_FIELD1(#name, ann) A_ID(node->name)
-#define F_GENTRY(name, ann) SIMPLE_FIELD1(#name, ann) A_ID(node->name)
-#define F_INT(name, ann) SIMPLE_FIELD1(#name, ann) A_INT(node->name)
-#define F_LONG(name, ann) SIMPLE_FIELD1(#name, ann) A_LONG(node->name)
-#define F_LIT(name, ann) SIMPLE_FIELD1(#name, ann) A_LIT(node->name)
-#define F_MSG(name, ann, desc) SIMPLE_FIELD1(#name, ann) A(desc)
+#ifdef UNIVERSAL_PARSER
+#include <stddef.h>
+#include "node.h"
+#include "rubyparser.h"
+#endif
-#define F_NODE(name, ann) \
- COMPOUND_FIELD1(#name, ann) {dump_node(buf, indent, comment, node->name);}
-
-#define ANN(ann) \
- if (comment) { \
- A_INDENT; A("| # " ann "\n"); \
- }
-
-#define LAST_NODE (next_indent = " ")
+#include "internal/variable.h"
-VALUE
-rb_dump_literal(VALUE lit)
-{
- if (!RB_SPECIAL_CONST_P(lit)) {
- VALUE str;
- switch (RB_BUILTIN_TYPE(lit)) {
- case T_CLASS: case T_MODULE: case T_ICLASS:
- str = rb_class_path(lit);
- if (FL_TEST(lit, FL_SINGLETON)) {
- str = rb_sprintf("<%"PRIsVALUE">", str);
- }
- return str;
- default:
- break;
- }
- }
- return rb_inspect(lit);
-}
+#define NODE_BUF_DEFAULT_SIZE (sizeof(struct RNode) * 16)
static void
-add_indent(VALUE buf, VALUE indent)
+init_node_buffer_elem(node_buffer_elem_t *nbe, size_t allocated, void *xmalloc(size_t))
{
- AR(indent);
+ nbe->allocated = allocated;
+ nbe->used = 0;
+ nbe->len = 0;
+ nbe->nodes = xmalloc(allocated / sizeof(struct RNode) * sizeof(struct RNode *)); /* All node requires at least RNode */
}
static void
-add_id(VALUE buf, ID id)
+init_node_buffer_list(node_buffer_list_t *nb, node_buffer_elem_t *head, void *xmalloc(size_t))
{
- if (id == 0) {
- A("(null)");
- }
- else {
- VALUE str = rb_id2str(id);
- if (str) {
- A(":"); AR(str);
- }
- else {
- rb_str_catf(buf, "(internal variable: 0x%"PRIsVALUE")", id);
- }
- }
+ init_node_buffer_elem(head, NODE_BUF_DEFAULT_SIZE, xmalloc);
+ nb->head = nb->last = head;
+ nb->head->next = NULL;
}
-struct add_option_arg {
- VALUE buf, indent;
- st_index_t count;
-};
-
-static void dump_node(VALUE, VALUE, int, const NODE *);
-static const char default_indent[] = "| ";
+#ifdef UNIVERSAL_PARSER
+#define ruby_xmalloc config->malloc
+#endif
-static void
-dump_array(VALUE buf, VALUE indent, int comment, const NODE *node)
+#ifdef UNIVERSAL_PARSER
+static node_buffer_t *
+rb_node_buffer_new(const rb_parser_config_t *config)
+#else
+static node_buffer_t *
+rb_node_buffer_new(void)
+#endif
{
- int field_flag;
- const char *next_indent = default_indent;
- F_LONG(nd_alen, "length");
- F_NODE(nd_head, "element");
- while (node->nd_next && nd_type_p(node->nd_next, NODE_LIST)) {
- node = node->nd_next;
- F_NODE(nd_head, "element");
- }
- LAST_NODE;
- F_NODE(nd_next, "next element");
+ const size_t bucket_size = offsetof(node_buffer_elem_t, buf) + NODE_BUF_DEFAULT_SIZE;
+ const size_t alloc_size = sizeof(node_buffer_t) + (bucket_size);
+ STATIC_ASSERT(
+ integer_overflow,
+ offsetof(node_buffer_elem_t, buf) + NODE_BUF_DEFAULT_SIZE
+ > sizeof(node_buffer_t) + sizeof(node_buffer_elem_t));
+ node_buffer_t *nb = ruby_xmalloc(alloc_size);
+ init_node_buffer_list(&nb->buffer_list, (node_buffer_elem_t*)&nb[1], ruby_xmalloc);
+ nb->local_tables = 0;
+ nb->tokens = 0;
+ return nb;
}
-static void
-dump_node(VALUE buf, VALUE indent, int comment, const NODE * node)
-{
- int field_flag;
- int i;
- const char *next_indent = default_indent;
- enum node_type type;
-
- if (!node) {
- D_NULL_NODE;
- return;
- }
-
- D_NODE_HEADER(node);
-
- type = nd_type(node);
- switch (type) {
- case NODE_BLOCK:
- ANN("statement sequence");
- ANN("format: [nd_head]; ...; [nd_next]");
- ANN("example: foo; bar");
- i = 0;
- do {
- A_INDENT;
- rb_str_catf(buf, "+- nd_head (%s%d):\n",
- comment ? "statement #" : "", ++i);
- if (!node->nd_next) LAST_NODE;
- D_INDENT;
- dump_node(buf, indent, comment, node->nd_head);
- D_DEDENT;
- } while (node->nd_next &&
- nd_type_p(node->nd_next, NODE_BLOCK) &&
- (node = node->nd_next, 1));
- if (node->nd_next) {
- LAST_NODE;
- F_NODE(nd_next, "next block");
- }
- return;
-
- case NODE_IF:
- ANN("if statement");
- ANN("format: if [nd_cond] then [nd_body] else [nd_else] end");
- ANN("example: if x == 1 then foo else bar end");
- F_NODE(nd_cond, "condition expr");
- F_NODE(nd_body, "then clause");
- LAST_NODE;
- F_NODE(nd_else, "else clause");
- return;
-
- case NODE_UNLESS:
- ANN("unless statement");
- ANN("format: unless [nd_cond] then [nd_body] else [nd_else] end");
- ANN("example: unless x == 1 then foo else bar end");
- F_NODE(nd_cond, "condition expr");
- F_NODE(nd_body, "then clause");
- LAST_NODE;
- F_NODE(nd_else, "else clause");
- return;
-
- case NODE_CASE:
- ANN("case statement");
- ANN("format: case [nd_head]; [nd_body]; end");
- ANN("example: case x; when 1; foo; when 2; bar; else baz; end");
- F_NODE(nd_head, "case expr");
- LAST_NODE;
- F_NODE(nd_body, "when clauses");
- return;
- case NODE_CASE2:
- ANN("case statement with no head");
- ANN("format: case; [nd_body]; end");
- ANN("example: case; when 1; foo; when 2; bar; else baz; end");
- F_NODE(nd_head, "case expr");
- LAST_NODE;
- F_NODE(nd_body, "when clauses");
- return;
- case NODE_CASE3:
- ANN("case statement (pattern matching)");
- ANN("format: case [nd_head]; [nd_body]; end");
- ANN("example: case x; in 1; foo; in 2; bar; else baz; end");
- F_NODE(nd_head, "case expr");
- LAST_NODE;
- F_NODE(nd_body, "in clauses");
- return;
-
- case NODE_WHEN:
- ANN("when clause");
- ANN("format: when [nd_head]; [nd_body]; (when or else) [nd_next]");
- ANN("example: case x; when 1; foo; when 2; bar; else baz; end");
- F_NODE(nd_head, "when value");
- F_NODE(nd_body, "when body");
- LAST_NODE;
- F_NODE(nd_next, "next when clause");
- return;
-
- case NODE_IN:
- ANN("in clause");
- ANN("format: in [nd_head]; [nd_body]; (in or else) [nd_next]");
- ANN("example: case x; in 1; foo; in 2; bar; else baz; end");
- F_NODE(nd_head, "in pattern");
- F_NODE(nd_body, "in body");
- LAST_NODE;
- F_NODE(nd_next, "next in clause");
- return;
-
- case NODE_WHILE:
- ANN("while statement");
- ANN("format: while [nd_cond]; [nd_body]; end");
- ANN("example: while x == 1; foo; end");
- goto loop;
- case NODE_UNTIL:
- ANN("until statement");
- ANN("format: until [nd_cond]; [nd_body]; end");
- ANN("example: until x == 1; foo; end");
- loop:
- F_CUSTOM1(nd_state, "begin-end-while?") {
- A_INT((int)node->nd_state);
- A((node->nd_state == 1) ? " (while-end)" : " (begin-end-while)");
- }
- F_NODE(nd_cond, "condition");
- LAST_NODE;
- F_NODE(nd_body, "body");
- return;
-
- case NODE_ITER:
- ANN("method call with block");
- ANN("format: [nd_iter] { [nd_body] }");
- ANN("example: 3.times { foo }");
- goto iter;
- case NODE_FOR:
- ANN("for statement");
- ANN("format: for * in [nd_iter] do [nd_body] end");
- ANN("example: for i in 1..3 do foo end");
- iter:
- F_NODE(nd_iter, "iteration receiver");
- LAST_NODE;
- F_NODE(nd_body, "body");
- return;
-
- case NODE_FOR_MASGN:
- ANN("vars of for statement with masgn");
- ANN("format: for [nd_var] in ... do ... end");
- ANN("example: for x, y in 1..3 do foo end");
- LAST_NODE;
- F_NODE(nd_var, "var");
- return;
-
- case NODE_BREAK:
- ANN("break statement");
- ANN("format: break [nd_stts]");
- ANN("example: break 1");
- goto jump;
- case NODE_NEXT:
- ANN("next statement");
- ANN("format: next [nd_stts]");
- ANN("example: next 1");
- goto jump;
- case NODE_RETURN:
- ANN("return statement");
- ANN("format: return [nd_stts]");
- ANN("example: return 1");
- jump:
- LAST_NODE;
- F_NODE(nd_stts, "value");
- return;
-
- case NODE_REDO:
- ANN("redo statement");
- ANN("format: redo");
- ANN("example: redo");
- return;
-
- case NODE_RETRY:
- ANN("retry statement");
- ANN("format: retry");
- ANN("example: retry");
- return;
-
- case NODE_BEGIN:
- ANN("begin statement");
- ANN("format: begin; [nd_body]; end");
- ANN("example: begin; 1; end");
- LAST_NODE;
- F_NODE(nd_body, "body");
- return;
-
- case NODE_RESCUE:
- ANN("rescue clause");
- ANN("format: begin; [nd_body]; (rescue) [nd_resq]; else [nd_else]; end");
- ANN("example: begin; foo; rescue; bar; else; baz; end");
- F_NODE(nd_head, "body");
- F_NODE(nd_resq, "rescue clause list");
- LAST_NODE;
- F_NODE(nd_else, "rescue else clause");
- return;
-
- case NODE_RESBODY:
- ANN("rescue clause (cont'd)");
- ANN("format: rescue [nd_args]; [nd_body]; (rescue) [nd_head]");
- ANN("example: begin; foo; rescue; bar; else; baz; end");
- F_NODE(nd_args, "rescue exceptions");
- F_NODE(nd_body, "rescue clause");
- LAST_NODE;
- F_NODE(nd_head, "next rescue clause");
- return;
-
- case NODE_ENSURE:
- ANN("ensure clause");
- ANN("format: begin; [nd_head]; ensure; [nd_ensr]; end");
- ANN("example: begin; foo; ensure; bar; end");
- F_NODE(nd_head, "body");
- LAST_NODE;
- F_NODE(nd_ensr, "ensure clause");
- return;
-
- case NODE_AND:
- ANN("&& operator");
- ANN("format: [nd_1st] && [nd_2nd]");
- ANN("example: foo && bar");
- goto andor;
- case NODE_OR:
- ANN("|| operator");
- ANN("format: [nd_1st] || [nd_2nd]");
- ANN("example: foo || bar");
- andor:
- while (1) {
- F_NODE(nd_1st, "left expr");
- if (!node->nd_2nd || !nd_type_p(node->nd_2nd, type))
- break;
- node = node->nd_2nd;
- }
- LAST_NODE;
- F_NODE(nd_2nd, "right expr");
- return;
-
- case NODE_MASGN:
- ANN("multiple assignment");
- ANN("format: [nd_head], [nd_args] = [nd_value]");
- ANN("example: a, b = foo");
- F_NODE(nd_value, "rhsn");
- F_NODE(nd_head, "lhsn");
- if (NODE_NAMED_REST_P(node->nd_args)) {
- LAST_NODE;
- F_NODE(nd_args, "splatn");
- }
- else {
- F_MSG(nd_args, "splatn", "NODE_SPECIAL_NO_NAME_REST (rest argument without name)");
- }
- return;
-
- case NODE_LASGN:
- ANN("local variable assignment");
- ANN("format: [nd_vid](lvar) = [nd_value]");
- ANN("example: x = foo");
- F_ID(nd_vid, "local variable");
- if (NODE_REQUIRED_KEYWORD_P(node)) {
- F_MSG(nd_value, "rvalue", "NODE_SPECIAL_REQUIRED_KEYWORD (required keyword argument)");
- }
- else {
- LAST_NODE;
- F_NODE(nd_value, "rvalue");
- }
- return;
- case NODE_DASGN:
- ANN("dynamic variable assignment");
- ANN("format: [nd_vid](dvar) = [nd_value]");
- ANN("example: x = nil; 1.times { x = foo }");
- ANN("example: 1.times { x = foo }");
- F_ID(nd_vid, "local variable");
- if (NODE_REQUIRED_KEYWORD_P(node)) {
- F_MSG(nd_value, "rvalue", "NODE_SPECIAL_REQUIRED_KEYWORD (required keyword argument)");
- }
- else {
- LAST_NODE;
- F_NODE(nd_value, "rvalue");
- }
- return;
- case NODE_IASGN:
- ANN("instance variable assignment");
- ANN("format: [nd_vid](ivar) = [nd_value]");
- ANN("example: @x = foo");
- F_ID(nd_vid, "instance variable");
- LAST_NODE;
- F_NODE(nd_value, "rvalue");
- return;
- case NODE_CVASGN:
- ANN("class variable assignment");
- ANN("format: [nd_vid](cvar) = [nd_value]");
- ANN("example: @@x = foo");
- F_ID(nd_vid, "class variable");
- LAST_NODE;
- F_NODE(nd_value, "rvalue");
- return;
- case NODE_GASGN:
- ANN("global variable assignment");
- ANN("format: [nd_entry](gvar) = [nd_value]");
- ANN("example: $x = foo");
- F_GENTRY(nd_entry, "global variable");
- LAST_NODE;
- F_NODE(nd_value, "rvalue");
- return;
-
- case NODE_CDECL:
- ANN("constant declaration");
- ANN("format: [nd_else]::[nd_vid](constant) = [nd_value]");
- ANN("example: X = foo");
- if (node->nd_vid) {
- F_ID(nd_vid, "constant");
- F_MSG(nd_else, "extension", "not used");
- }
- else {
- F_MSG(nd_vid, "constant", "0 (see extension field)");
- F_NODE(nd_else, "extension");
- }
- LAST_NODE;
- F_NODE(nd_value, "rvalue");
- return;
-
- case NODE_OP_ASGN1:
- ANN("array assignment with operator");
- ANN("format: [nd_recv] [ [nd_args->nd_head] ] [nd_mid]= [nd_args->nd_body]");
- ANN("example: ary[1] += foo");
- F_NODE(nd_recv, "receiver");
- F_ID(nd_mid, "operator");
- F_NODE(nd_args->nd_head, "index");
- LAST_NODE;
- F_NODE(nd_args->nd_body, "rvalue");
- return;
-
- case NODE_OP_ASGN2:
- ANN("attr assignment with operator");
- ANN("format: [nd_recv].[attr] [nd_next->nd_mid]= [nd_value]");
- ANN(" where [attr]: [nd_next->nd_vid]");
- ANN("example: struct.field += foo");
- F_NODE(nd_recv, "receiver");
- F_CUSTOM1(nd_next->nd_vid, "attr") {
- if (node->nd_next->nd_aid) A("? ");
- A_ID(node->nd_next->nd_vid);
- }
- F_ID(nd_next->nd_mid, "operator");
- LAST_NODE;
- F_NODE(nd_value, "rvalue");
- return;
-
- case NODE_OP_ASGN_AND:
- ANN("assignment with && operator");
- ANN("format: [nd_head] &&= [nd_value]");
- ANN("example: foo &&= bar");
- goto asgn_andor;
- case NODE_OP_ASGN_OR:
- ANN("assignment with || operator");
- ANN("format: [nd_head] ||= [nd_value]");
- ANN("example: foo ||= bar");
- asgn_andor:
- F_NODE(nd_head, "variable");
- LAST_NODE;
- F_NODE(nd_value, "rvalue");
- return;
-
- case NODE_OP_CDECL:
- ANN("constant declaration with operator");
- ANN("format: [nd_head](constant) [nd_aid]= [nd_value]");
- ANN("example: A::B ||= 1");
- F_NODE(nd_head, "constant");
- F_ID(nd_aid, "operator");
- LAST_NODE;
- F_NODE(nd_value, "rvalue");
- return;
-
- case NODE_CALL:
- ANN("method invocation");
- ANN("format: [nd_recv].[nd_mid]([nd_args])");
- ANN("example: obj.foo(1)");
- F_ID(nd_mid, "method id");
- F_NODE(nd_recv, "receiver");
- LAST_NODE;
- F_NODE(nd_args, "arguments");
- return;
-
- case NODE_OPCALL:
- ANN("method invocation");
- ANN("format: [nd_recv] [nd_mid] [nd_args]");
- ANN("example: foo + bar");
- F_ID(nd_mid, "method id");
- F_NODE(nd_recv, "receiver");
- LAST_NODE;
- F_NODE(nd_args, "arguments");
- return;
-
- case NODE_FCALL:
- ANN("function call");
- ANN("format: [nd_mid]([nd_args])");
- ANN("example: foo(1)");
- F_ID(nd_mid, "method id");
- LAST_NODE;
- F_NODE(nd_args, "arguments");
- return;
-
- case NODE_VCALL:
- ANN("function call with no argument");
- ANN("format: [nd_mid]");
- ANN("example: foo");
- F_ID(nd_mid, "method id");
- return;
-
- case NODE_QCALL:
- ANN("safe method invocation");
- ANN("format: [nd_recv]&.[nd_mid]([nd_args])");
- ANN("example: obj&.foo(1)");
- F_ID(nd_mid, "method id");
- F_NODE(nd_recv, "receiver");
- LAST_NODE;
- F_NODE(nd_args, "arguments");
- return;
-
- case NODE_SUPER:
- ANN("super invocation");
- ANN("format: super [nd_args]");
- ANN("example: super 1");
- LAST_NODE;
- F_NODE(nd_args, "arguments");
- return;
-
- case NODE_ZSUPER:
- ANN("super invocation with no argument");
- ANN("format: super");
- ANN("example: super");
- return;
-
- case NODE_LIST:
- ANN("list constructor");
- ANN("format: [ [nd_head], [nd_next].. ] (length: [nd_alen])");
- ANN("example: [1, 2, 3]");
- goto ary;
- case NODE_VALUES:
- ANN("return arguments");
- ANN("format: [ [nd_head], [nd_next].. ] (length: [nd_alen])");
- ANN("example: return 1, 2, 3");
- ary:
- dump_array(buf, indent, comment, node);
- return;
-
- case NODE_ZLIST:
- ANN("empty list constructor");
- ANN("format: []");
- ANN("example: []");
- return;
-
- case NODE_HASH:
- if (!node->nd_brace) {
- ANN("keyword arguments");
- ANN("format: nd_head");
- ANN("example: a: 1, b: 2");
- }
- else {
- ANN("hash constructor");
- ANN("format: { [nd_head] }");
- ANN("example: { 1 => 2, 3 => 4 }");
- }
- F_CUSTOM1(nd_brace, "keyword arguments or hash literal") {
- switch (node->nd_brace) {
- case 0: A("0 (keyword argument)"); break;
- case 1: A("1 (hash literal)"); break;
- }
- }
- LAST_NODE;
- F_NODE(nd_head, "contents");
- return;
-
- case NODE_YIELD:
- ANN("yield invocation");
- ANN("format: yield [nd_head]");
- ANN("example: yield 1");
- LAST_NODE;
- F_NODE(nd_head, "arguments");
- return;
-
- case NODE_LVAR:
- ANN("local variable reference");
- ANN("format: [nd_vid](lvar)");
- ANN("example: x");
- F_ID(nd_vid, "local variable");
- return;
- case NODE_DVAR:
- ANN("dynamic variable reference");
- ANN("format: [nd_vid](dvar)");
- ANN("example: 1.times { x = 1; x }");
- F_ID(nd_vid, "local variable");
- return;
- case NODE_IVAR:
- ANN("instance variable reference");
- ANN("format: [nd_vid](ivar)");
- ANN("example: @x");
- F_ID(nd_vid, "instance variable");
- return;
- case NODE_CONST:
- ANN("constant reference");
- ANN("format: [nd_vid](constant)");
- ANN("example: X");
- F_ID(nd_vid, "constant");
- return;
- case NODE_CVAR:
- ANN("class variable reference");
- ANN("format: [nd_vid](cvar)");
- ANN("example: @@x");
- F_ID(nd_vid, "class variable");
- return;
-
- case NODE_GVAR:
- ANN("global variable reference");
- ANN("format: [nd_entry](gvar)");
- ANN("example: $x");
- F_GENTRY(nd_entry, "global variable");
- return;
-
- case NODE_NTH_REF:
- ANN("nth special variable reference");
- ANN("format: $[nd_nth]");
- ANN("example: $1, $2, ..");
- F_CUSTOM1(nd_nth, "variable") { A("$"); A_LONG(node->nd_nth); }
- return;
-
- case NODE_BACK_REF:
- ANN("back special variable reference");
- ANN("format: $[nd_nth]");
- ANN("example: $&, $`, $', $+");
- F_CUSTOM1(nd_nth, "variable") {
- char name[3] = "$ ";
- name[1] = (char)node->nd_nth;
- A(name);
- }
- return;
-
- case NODE_MATCH:
- ANN("match expression (against $_ implicitly)");
- ANN("format: [nd_lit] (in condition)");
- ANN("example: if /foo/; foo; end");
- F_LIT(nd_lit, "regexp");
- return;
-
- case NODE_MATCH2:
- ANN("match expression (regexp first)");
- ANN("format: [nd_recv] =~ [nd_value]");
- ANN("example: /foo/ =~ 'foo'");
- F_NODE(nd_recv, "regexp (receiver)");
- if (!node->nd_args) LAST_NODE;
- F_NODE(nd_value, "string (argument)");
- if (node->nd_args) {
- LAST_NODE;
- F_NODE(nd_args, "named captures");
- }
- return;
-
- case NODE_MATCH3:
- ANN("match expression (regexp second)");
- ANN("format: [nd_recv] =~ [nd_value]");
- ANN("example: 'foo' =~ /foo/");
- F_NODE(nd_recv, "string (receiver)");
- LAST_NODE;
- F_NODE(nd_value, "regexp (argument)");
- return;
-
- case NODE_LIT:
- ANN("literal");
- ANN("format: [nd_lit]");
- ANN("example: 1, /foo/");
- goto lit;
- case NODE_STR:
- ANN("string literal");
- ANN("format: [nd_lit]");
- ANN("example: 'foo'");
- goto lit;
- case NODE_XSTR:
- ANN("xstring literal");
- ANN("format: [nd_lit]");
- ANN("example: `foo`");
- lit:
- F_LIT(nd_lit, "literal");
- return;
-
- case NODE_ONCE:
- ANN("once evaluation");
- ANN("format: [nd_body]");
- ANN("example: /foo#{ bar }baz/o");
- LAST_NODE;
- F_NODE(nd_body, "body");
- return;
- case NODE_DSTR:
- ANN("string literal with interpolation");
- ANN("format: [nd_lit]");
- ANN("example: \"foo#{ bar }baz\"");
- goto dlit;
- case NODE_DXSTR:
- ANN("xstring literal with interpolation");
- ANN("format: [nd_lit]");
- ANN("example: `foo#{ bar }baz`");
- goto dlit;
- case NODE_DREGX:
- ANN("regexp literal with interpolation");
- ANN("format: [nd_lit]");
- ANN("example: /foo#{ bar }baz/");
- goto dlit;
- case NODE_DSYM:
- ANN("symbol literal with interpolation");
- ANN("format: [nd_lit]");
- ANN("example: :\"foo#{ bar }baz\"");
- dlit:
- F_LIT(nd_lit, "preceding string");
- if (!node->nd_next) return;
- F_NODE(nd_next->nd_head, "interpolation");
- LAST_NODE;
- F_NODE(nd_next->nd_next, "tailing strings");
- return;
-
- case NODE_EVSTR:
- ANN("interpolation expression");
- ANN("format: \"..#{ [nd_lit] }..\"");
- ANN("example: \"foo#{ bar }baz\"");
- LAST_NODE;
- F_NODE(nd_body, "body");
- return;
-
- case NODE_ARGSCAT:
- ANN("splat argument following arguments");
- ANN("format: ..(*[nd_head], [nd_body..])");
- ANN("example: foo(*ary, post_arg1, post_arg2)");
- F_NODE(nd_head, "preceding array");
- LAST_NODE;
- F_NODE(nd_body, "following array");
- return;
-
- case NODE_ARGSPUSH:
- ANN("splat argument following one argument");
- ANN("format: ..(*[nd_head], [nd_body])");
- ANN("example: foo(*ary, post_arg)");
- F_NODE(nd_head, "preceding array");
- LAST_NODE;
- F_NODE(nd_body, "following element");
- return;
-
- case NODE_SPLAT:
- ANN("splat argument");
- ANN("format: *[nd_head]");
- ANN("example: foo(*ary)");
- LAST_NODE;
- F_NODE(nd_head, "splat'ed array");
- return;
-
- case NODE_BLOCK_PASS:
- ANN("arguments with block argument");
- ANN("format: ..([nd_head], &[nd_body])");
- ANN("example: foo(x, &blk)");
- F_NODE(nd_head, "other arguments");
- LAST_NODE;
- F_NODE(nd_body, "block argument");
- return;
-
- case NODE_DEFN:
- ANN("method definition");
- ANN("format: def [nd_mid] [nd_defn]; end");
- ANN("example: def foo; bar; end");
- F_ID(nd_mid, "method name");
- LAST_NODE;
- F_NODE(nd_defn, "method definition");
- return;
-
- case NODE_DEFS:
- ANN("singleton method definition");
- ANN("format: def [nd_recv].[nd_mid] [nd_defn]; end");
- ANN("example: def obj.foo; bar; end");
- F_NODE(nd_recv, "receiver");
- F_ID(nd_mid, "method name");
- LAST_NODE;
- F_NODE(nd_defn, "method definition");
- return;
-
- case NODE_ALIAS:
- ANN("method alias statement");
- ANN("format: alias [nd_1st] [nd_2nd]");
- ANN("example: alias bar foo");
- F_NODE(nd_1st, "new name");
- LAST_NODE;
- F_NODE(nd_2nd, "old name");
- return;
-
- case NODE_VALIAS:
- ANN("global variable alias statement");
- ANN("format: alias [nd_alias](gvar) [nd_orig](gvar)");
- ANN("example: alias $y $x");
- F_ID(nd_alias, "new name");
- F_ID(nd_orig, "old name");
- return;
-
- case NODE_UNDEF:
- ANN("method undef statement");
- ANN("format: undef [nd_undef]");
- ANN("example: undef foo");
- LAST_NODE;
- F_NODE(nd_undef, "old name");
- return;
-
- case NODE_CLASS:
- ANN("class definition");
- ANN("format: class [nd_cpath] < [nd_super]; [nd_body]; end");
- ANN("example: class C2 < C; ..; end");
- F_NODE(nd_cpath, "class path");
- F_NODE(nd_super, "superclass");
- LAST_NODE;
- F_NODE(nd_body, "class definition");
- return;
-
- case NODE_MODULE:
- ANN("module definition");
- ANN("format: module [nd_cpath]; [nd_body]; end");
- ANN("example: module M; ..; end");
- F_NODE(nd_cpath, "module path");
- LAST_NODE;
- F_NODE(nd_body, "module definition");
- return;
-
- case NODE_SCLASS:
- ANN("singleton class definition");
- ANN("format: class << [nd_recv]; [nd_body]; end");
- ANN("example: class << obj; ..; end");
- F_NODE(nd_recv, "receiver");
- LAST_NODE;
- F_NODE(nd_body, "singleton class definition");
- return;
-
- case NODE_COLON2:
- ANN("scoped constant reference");
- ANN("format: [nd_head]::[nd_mid]");
- ANN("example: M::C");
- F_ID(nd_mid, "constant name");
- LAST_NODE;
- F_NODE(nd_head, "receiver");
- return;
-
- case NODE_COLON3:
- ANN("top-level constant reference");
- ANN("format: ::[nd_mid]");
- ANN("example: ::Object");
- F_ID(nd_mid, "constant name");
- return;
-
- case NODE_DOT2:
- ANN("range constructor (incl.)");
- ANN("format: [nd_beg]..[nd_end]");
- ANN("example: 1..5");
- goto dot;
- case NODE_DOT3:
- ANN("range constructor (excl.)");
- ANN("format: [nd_beg]...[nd_end]");
- ANN("example: 1...5");
- goto dot;
- case NODE_FLIP2:
- ANN("flip-flop condition (incl.)");
- ANN("format: [nd_beg]..[nd_end]");
- ANN("example: if (x==1)..(x==5); foo; end");
- goto dot;
- case NODE_FLIP3:
- ANN("flip-flop condition (excl.)");
- ANN("format: [nd_beg]...[nd_end]");
- ANN("example: if (x==1)...(x==5); foo; end");
- dot:
- F_NODE(nd_beg, "begin");
- LAST_NODE;
- F_NODE(nd_end, "end");
- return;
-
- case NODE_SELF:
- ANN("self");
- ANN("format: self");
- ANN("example: self");
- return;
-
- case NODE_NIL:
- ANN("nil");
- ANN("format: nil");
- ANN("example: nil");
- return;
-
- case NODE_TRUE:
- ANN("true");
- ANN("format: true");
- ANN("example: true");
- return;
-
- case NODE_FALSE:
- ANN("false");
- ANN("format: false");
- ANN("example: false");
- return;
-
- case NODE_ERRINFO:
- ANN("virtual reference to $!");
- ANN("format: rescue => id");
- ANN("example: rescue => id");
- return;
-
- case NODE_DEFINED:
- ANN("defined? expression");
- ANN("format: defined?([nd_head])");
- ANN("example: defined?(foo)");
- F_NODE(nd_head, "expr");
- return;
-
- case NODE_POSTEXE:
- ANN("post-execution");
- ANN("format: END { [nd_body] }");
- ANN("example: END { foo }");
- LAST_NODE;
- F_NODE(nd_body, "END clause");
- return;
-
- case NODE_ATTRASGN:
- ANN("attr assignment");
- ANN("format: [nd_recv].[nd_mid] = [nd_args]");
- ANN("example: struct.field = foo");
- F_NODE(nd_recv, "receiver");
- F_ID(nd_mid, "method name");
- LAST_NODE;
- F_NODE(nd_args, "arguments");
- return;
-
- case NODE_LAMBDA:
- ANN("lambda expression");
- ANN("format: -> [nd_body]");
- ANN("example: -> { foo }");
- LAST_NODE;
- F_NODE(nd_body, "lambda clause");
- return;
-
- case NODE_OPT_ARG:
- ANN("optional arguments");
- ANN("format: def method_name([nd_body=some], [nd_next..])");
- ANN("example: def foo(a, b=1, c); end");
- F_NODE(nd_body, "body");
- LAST_NODE;
- F_NODE(nd_next, "next");
- return;
-
- case NODE_KW_ARG:
- ANN("keyword arguments");
- ANN("format: def method_name([nd_body=some], [nd_next..])");
- ANN("example: def foo(a:1, b:2); end");
- F_NODE(nd_body, "body");
- LAST_NODE;
- F_NODE(nd_next, "next");
- return;
-
- case NODE_POSTARG:
- ANN("post arguments");
- ANN("format: *[nd_1st], [nd_2nd..] = ..");
- ANN("example: a, *rest, z = foo");
- if (NODE_NAMED_REST_P(node->nd_1st)) {
- F_NODE(nd_1st, "rest argument");
- }
- else {
- F_MSG(nd_1st, "rest argument", "NODE_SPECIAL_NO_NAME_REST (rest argument without name)");
- }
- LAST_NODE;
- F_NODE(nd_2nd, "post arguments");
- return;
-
- case NODE_ARGS:
- ANN("method parameters");
- ANN("format: def method_name(.., [nd_ainfo->nd_optargs], *[nd_ainfo->rest_arg], [nd_ainfo->first_post_arg], .., [nd_ainfo->kw_args], **[nd_ainfo->kw_rest_arg], &[nd_ainfo->block_arg])");
- ANN("example: def foo(a, b, opt1=1, opt2=2, *rest, y, z, kw: 1, **kwrest, &blk); end");
- F_INT(nd_ainfo->pre_args_num, "count of mandatory (pre-)arguments");
- F_NODE(nd_ainfo->pre_init, "initialization of (pre-)arguments");
- F_INT(nd_ainfo->post_args_num, "count of mandatory post-arguments");
- F_NODE(nd_ainfo->post_init, "initialization of post-arguments");
- F_ID(nd_ainfo->first_post_arg, "first post argument");
- F_CUSTOM1(nd_ainfo->rest_arg, "rest argument") {
- if (node->nd_ainfo->rest_arg == NODE_SPECIAL_EXCESSIVE_COMMA) {
- A("1 (excessed comma)");
- }
- else {
- A_ID(node->nd_ainfo->rest_arg);
- }
- }
- F_ID(nd_ainfo->block_arg, "block argument");
- F_NODE(nd_ainfo->opt_args, "optional arguments");
- F_NODE(nd_ainfo->kw_args, "keyword arguments");
- LAST_NODE;
- F_NODE(nd_ainfo->kw_rest_arg, "keyword rest argument");
- return;
-
- case NODE_SCOPE:
- ANN("new scope");
- ANN("format: [nd_tbl]: local table, [nd_args]: arguments, [nd_body]: body");
- F_CUSTOM1(nd_tbl, "local table") {
- rb_ast_id_table_t *tbl = node->nd_tbl;
- int i;
- int size = tbl ? tbl->size : 0;
- if (size == 0) A("(empty)");
- for (i = 0; i < size; i++) {
- A_ID(tbl->ids[i]); if (i < size - 1) A(",");
- }
- }
- F_NODE(nd_args, "arguments");
- LAST_NODE;
- F_NODE(nd_body, "body");
- return;
-
- case NODE_ARYPTN:
- ANN("array pattern");
- ANN("format: [nd_pconst]([pre_args], ..., *[rest_arg], [post_args], ...)");
- F_NODE(nd_pconst, "constant");
- F_NODE(nd_apinfo->pre_args, "pre arguments");
- if (NODE_NAMED_REST_P(node->nd_apinfo->rest_arg)) {
- F_NODE(nd_apinfo->rest_arg, "rest argument");
- }
- else {
- F_MSG(nd_apinfo->rest_arg, "rest argument", "NODE_SPECIAL_NO_NAME_REST (rest argument without name)");
- }
- LAST_NODE;
- F_NODE(nd_apinfo->post_args, "post arguments");
- return;
-
- case NODE_FNDPTN:
- ANN("find pattern");
- ANN("format: [nd_pconst](*[pre_rest_arg], args, ..., *[post_rest_arg])");
- F_NODE(nd_pconst, "constant");
- if (NODE_NAMED_REST_P(node->nd_fpinfo->pre_rest_arg)) {
- F_NODE(nd_fpinfo->pre_rest_arg, "pre rest argument");
- }
- else {
- F_MSG(nd_fpinfo->pre_rest_arg, "pre rest argument", "NODE_SPECIAL_NO_NAME_REST (rest argument without name)");
- }
- F_NODE(nd_fpinfo->args, "arguments");
-
- LAST_NODE;
- if (NODE_NAMED_REST_P(node->nd_fpinfo->post_rest_arg)) {
- F_NODE(nd_fpinfo->post_rest_arg, "post rest argument");
- }
- else {
- F_MSG(nd_fpinfo->post_rest_arg, "post rest argument", "NODE_SPECIAL_NO_NAME_REST (rest argument without name)");
- }
- return;
-
- case NODE_HSHPTN:
- ANN("hash pattern");
- ANN("format: [nd_pconst]([nd_pkwargs], ..., **[nd_pkwrestarg])");
- F_NODE(nd_pconst, "constant");
- F_NODE(nd_pkwargs, "keyword arguments");
- LAST_NODE;
- if (node->nd_pkwrestarg == NODE_SPECIAL_NO_REST_KEYWORD) {
- F_MSG(nd_pkwrestarg, "keyword rest argument", "NODE_SPECIAL_NO_REST_KEYWORD (**nil)");
- }
- else {
- F_NODE(nd_pkwrestarg, "keyword rest argument");
- }
- return;
- case NODE_ERROR:
- ANN("Broken input recovered by Error Tolerant mode");
- return;
-
- case NODE_ARGS_AUX:
- case NODE_LAST:
- break;
- }
-
- rb_bug("dump_node: unknown node: %s", ruby_node_name(nd_type(node)));
-}
+#ifdef UNIVERSAL_PARSER
+#undef ruby_xmalloc
+#define ruby_xmalloc ast->config->malloc
+#undef xfree
+#define xfree ast->config->free
+#define rb_xmalloc_mul_add ast->config->xmalloc_mul_add
+#define ruby_xrealloc(var,size) (ast->config->realloc_n((void *)var, 1, size))
+#endif
-VALUE
-rb_parser_dump_tree(const NODE *node, int comment)
-{
- VALUE buf = rb_str_new_cstr(
- "###########################################################\n"
- "## Do NOT use this node dump for any purpose other than ##\n"
- "## debug and research. Compatibility is not guaranteed. ##\n"
- "###########################################################\n\n"
- );
- dump_node(buf, rb_str_new_cstr("# "), comment, node);
- return buf;
-}
+typedef void node_itr_t(rb_ast_t *ast, void *ctx, NODE *node);
+static void iterate_node_values(rb_ast_t *ast, node_buffer_list_t *nb, node_itr_t * func, void *ctx);
-/* Setup NODE structure.
- * NODE is not an object managed by GC, but it imitates an object
- * so that it can work with `RB_TYPE_P(obj, T_NODE)`.
- * This dirty hack is needed because Ripper jumbles NODEs and other type
- * objects.
- */
void
-rb_node_init(NODE *n, enum node_type type, VALUE a0, VALUE a1, VALUE a2)
+rb_node_init(NODE *n, enum node_type type)
{
- n->flags = T_NODE;
- nd_init_type(n, type);
- n->u1.value = a0;
- n->u2.value = a1;
- n->u3.value = a2;
- n->nd_loc.beg_pos.lineno = 0;
- n->nd_loc.beg_pos.column = 0;
- n->nd_loc.end_pos.lineno = 0;
- n->nd_loc.end_pos.column = 0;
- n->node_id = -1;
+ RNODE(n)->flags = 0;
+ nd_init_type(RNODE(n), type);
+ RNODE(n)->nd_loc.beg_pos.lineno = 0;
+ RNODE(n)->nd_loc.beg_pos.column = 0;
+ RNODE(n)->nd_loc.end_pos.lineno = 0;
+ RNODE(n)->nd_loc.end_pos.column = 0;
+ RNODE(n)->node_id = -1;
}
const char *
-ruby_node_name(int node)
+rb_node_name(int node)
{
switch (node) {
#include "node_name.inc"
default:
- rb_bug("unknown node: %d", node);
return 0;
}
}
-typedef struct node_buffer_elem_struct {
- struct node_buffer_elem_struct *next;
- long len;
- NODE buf[FLEX_ARY_LEN];
-} node_buffer_elem_t;
-
-typedef struct {
- long idx, len;
- node_buffer_elem_t *head;
- node_buffer_elem_t *last;
-} node_buffer_list_t;
-
-struct node_buffer_struct {
- node_buffer_list_t unmarkable;
- node_buffer_list_t markable;
- struct rb_ast_local_table_link *local_tables;
- VALUE mark_hash;
- // - id (sequence number)
- // - token_type
- // - text of token
- // - location info
- // Array, whose entry is array
- VALUE tokens;
-};
-
-static void
-init_node_buffer_list(node_buffer_list_t * nb, node_buffer_elem_t *head)
+#ifdef UNIVERSAL_PARSER
+const char *
+ruby_node_name(int node)
{
- nb->idx = 0;
- nb->len = NODE_BUF_DEFAULT_LEN;
- nb->head = nb->last = head;
- nb->head->len = nb->len;
- nb->head->next = NULL;
+ return rb_node_name(node);
}
-
-static node_buffer_t *
-rb_node_buffer_new(void)
+#else
+const char *
+ruby_node_name(int node)
{
- const size_t bucket_size = offsetof(node_buffer_elem_t, buf) + NODE_BUF_DEFAULT_LEN * sizeof(NODE);
- const size_t alloc_size = sizeof(node_buffer_t) + (bucket_size * 2);
- STATIC_ASSERT(
- integer_overflow,
- offsetof(node_buffer_elem_t, buf) + NODE_BUF_DEFAULT_LEN * sizeof(NODE)
- > sizeof(node_buffer_t) + 2 * sizeof(node_buffer_elem_t));
- node_buffer_t *nb = ruby_xmalloc(alloc_size);
- init_node_buffer_list(&nb->unmarkable, (node_buffer_elem_t*)&nb[1]);
- init_node_buffer_list(&nb->markable, (node_buffer_elem_t*)((size_t)nb->unmarkable.head + bucket_size));
- nb->local_tables = 0;
- nb->mark_hash = Qnil;
- nb->tokens = Qnil;
- return nb;
+ const char *name = rb_node_name(node);
+
+ if (!name) rb_bug("unknown node: %d", node);
+ return name;
}
+#endif
static void
-node_buffer_list_free(node_buffer_list_t * nb)
+node_buffer_list_free(rb_ast_t *ast, node_buffer_list_t * nb)
{
node_buffer_elem_t *nbe = nb->head;
-
while (nbe != nb->last) {
void *buf = nbe;
+ xfree(nbe->nodes);
nbe = nbe->next;
xfree(buf);
}
+
+ /* The last node_buffer_elem_t is allocated in the node_buffer_t, so we
+ * only need to free the nodes. */
+ xfree(nbe->nodes);
}
struct rb_ast_local_table_link {
@@ -1229,10 +137,88 @@ struct rb_ast_local_table_link {
};
static void
-rb_node_buffer_free(node_buffer_t *nb)
+parser_string_free(rb_ast_t *ast, rb_parser_string_t *str)
+{
+ if (!str) return;
+ xfree(str->ptr);
+ xfree(str);
+}
+
+static void
+parser_ast_token_free(rb_ast_t *ast, rb_parser_ast_token_t *token)
+{
+ if (!token) return;
+ parser_string_free(ast, token->str);
+ xfree(token);
+}
+
+static void
+parser_tokens_free(rb_ast_t *ast, rb_parser_ary_t *tokens)
+{
+ for (long i = 0; i < tokens->len; i++) {
+ parser_ast_token_free(ast, tokens->data[i]);
+ }
+ xfree(tokens->data);
+ xfree(tokens);
+}
+
+static void
+free_ast_value(rb_ast_t *ast, void *ctx, NODE *node)
+{
+ switch (nd_type(node)) {
+ case NODE_STR:
+ parser_string_free(ast, RNODE_STR(node)->string);
+ break;
+ case NODE_DSTR:
+ parser_string_free(ast, RNODE_DSTR(node)->string);
+ break;
+ case NODE_XSTR:
+ parser_string_free(ast, RNODE_XSTR(node)->string);
+ break;
+ case NODE_DXSTR:
+ parser_string_free(ast, RNODE_DXSTR(node)->string);
+ break;
+ case NODE_SYM:
+ parser_string_free(ast, RNODE_SYM(node)->string);
+ break;
+ case NODE_REGX:
+ case NODE_MATCH:
+ parser_string_free(ast, RNODE_REGX(node)->string);
+ break;
+ case NODE_DSYM:
+ parser_string_free(ast, RNODE_DSYM(node)->string);
+ break;
+ case NODE_DREGX:
+ parser_string_free(ast, RNODE_DREGX(node)->string);
+ break;
+ case NODE_FILE:
+ parser_string_free(ast, RNODE_FILE(node)->path);
+ break;
+ case NODE_INTEGER:
+ xfree(RNODE_INTEGER(node)->val);
+ break;
+ case NODE_FLOAT:
+ xfree(RNODE_FLOAT(node)->val);
+ break;
+ case NODE_RATIONAL:
+ xfree(RNODE_RATIONAL(node)->val);
+ break;
+ case NODE_IMAGINARY:
+ xfree(RNODE_IMAGINARY(node)->val);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+rb_node_buffer_free(rb_ast_t *ast, node_buffer_t *nb)
{
- node_buffer_list_free(&nb->unmarkable);
- node_buffer_list_free(&nb->markable);
+ if (nb && nb->tokens) {
+ parser_tokens_free(ast, nb->tokens);
+ }
+ iterate_node_values(ast, &nb->buffer_list, free_ast_value, NULL);
+ node_buffer_list_free(ast, &nb->buffer_list);
struct rb_ast_local_table_link *local_table = nb->local_tables;
while (local_table) {
struct rb_ast_local_table_link *next_table = local_table->next;
@@ -1242,61 +228,39 @@ rb_node_buffer_free(node_buffer_t *nb)
xfree(nb);
}
+#define buf_add_offset(nbe, offset) ((char *)(nbe->buf) + (offset))
+
static NODE *
-ast_newnode_in_bucket(node_buffer_list_t *nb)
+ast_newnode_in_bucket(rb_ast_t *ast, node_buffer_list_t *nb, size_t size, size_t alignment)
{
- if (nb->idx >= nb->len) {
- long n = nb->len * 2;
+ size_t padding;
+ NODE *ptr;
+
+ padding = alignment - (size_t)buf_add_offset(nb->head, nb->head->used) % alignment;
+ padding = padding == alignment ? 0 : padding;
+
+ if (nb->head->used + size + padding > nb->head->allocated) {
+ size_t n = nb->head->allocated * 2;
node_buffer_elem_t *nbe;
- nbe = rb_xmalloc_mul_add(n, sizeof(NODE), offsetof(node_buffer_elem_t, buf));
- nbe->len = n;
- nb->idx = 0;
- nb->len = n;
+ nbe = rb_xmalloc_mul_add(n, sizeof(char *), offsetof(node_buffer_elem_t, buf));
+ init_node_buffer_elem(nbe, n, ruby_xmalloc);
nbe->next = nb->head;
nb->head = nbe;
+ padding = 0; /* malloc returns aligned address then no need to add padding */
}
- return &nb->head->buf[nb->idx++];
-}
-RBIMPL_ATTR_PURE()
-static bool
-nodetype_markable_p(enum node_type type)
-{
- switch (type) {
- case NODE_MATCH:
- case NODE_LIT:
- case NODE_STR:
- case NODE_XSTR:
- case NODE_DSTR:
- case NODE_DXSTR:
- case NODE_DREGX:
- case NODE_DSYM:
- case NODE_ARGS:
- case NODE_ARYPTN:
- case NODE_FNDPTN:
- return true;
- default:
- return false;
- }
+ ptr = (NODE *)buf_add_offset(nb->head, nb->head->used + padding);
+ nb->head->used += (size + padding);
+ nb->head->nodes[nb->head->len++] = ptr;
+ return ptr;
}
NODE *
-rb_ast_newnode(rb_ast_t *ast, enum node_type type)
+rb_ast_newnode(rb_ast_t *ast, enum node_type type, size_t size, size_t alignment)
{
node_buffer_t *nb = ast->node_buffer;
- node_buffer_list_t *bucket =
- (nodetype_markable_p(type) ? &nb->markable : &nb->unmarkable);
- return ast_newnode_in_bucket(bucket);
-}
-
-void
-rb_ast_node_type_change(NODE *n, enum node_type type)
-{
- enum node_type old_type = nd_type(n);
- if (nodetype_markable_p(old_type) != nodetype_markable_p(type)) {
- rb_bug("node type changed: %s -> %s",
- ruby_node_name(old_type), ruby_node_name(type));
- }
+ node_buffer_list_t *bucket = &nb->buffer_list;
+ return ast_newnode_in_bucket(ast, bucket, size, alignment);
}
rb_ast_id_table_t *
@@ -1331,131 +295,63 @@ rb_ast_delete_node(rb_ast_t *ast, NODE *n)
/* should we implement freelist? */
}
+#ifdef UNIVERSAL_PARSER
+rb_ast_t *
+rb_ast_new(const rb_parser_config_t *config)
+{
+ node_buffer_t *nb = rb_node_buffer_new(config);
+ rb_ast_t *ast = (rb_ast_t *)config->calloc(1, sizeof(rb_ast_t));
+ ast->config = config;
+ ast->node_buffer = nb;
+ return ast;
+}
+#else
rb_ast_t *
rb_ast_new(void)
{
node_buffer_t *nb = rb_node_buffer_new();
- rb_ast_t *ast = (rb_ast_t *)rb_imemo_new(imemo_ast, 0, 0, 0, (VALUE)nb);
+ rb_ast_t *ast = ruby_xcalloc(1, sizeof(rb_ast_t));
+ ast->node_buffer = nb;
return ast;
}
-
-typedef void node_itr_t(void *ctx, NODE * node);
+#endif
static void
-iterate_buffer_elements(node_buffer_elem_t *nbe, long len, node_itr_t *func, void *ctx)
+iterate_buffer_elements(rb_ast_t *ast, node_buffer_elem_t *nbe, long len, node_itr_t *func, void *ctx)
{
long cursor;
for (cursor = 0; cursor < len; cursor++) {
- func(ctx, &nbe->buf[cursor]);
+ func(ast, ctx, nbe->nodes[cursor]);
}
}
static void
-iterate_node_values(node_buffer_list_t *nb, node_itr_t * func, void *ctx)
+iterate_node_values(rb_ast_t *ast, node_buffer_list_t *nb, node_itr_t * func, void *ctx)
{
node_buffer_elem_t *nbe = nb->head;
- /* iterate over the head first because it's not full */
- iterate_buffer_elements(nbe, nb->idx, func, ctx);
-
- nbe = nbe->next;
while (nbe) {
- iterate_buffer_elements(nbe, nbe->len, func, ctx);
+ iterate_buffer_elements(ast, nbe, nbe->len, func, ctx);
nbe = nbe->next;
}
}
static void
-mark_ast_value(void *ctx, NODE * node)
+script_lines_free(rb_ast_t *ast, rb_parser_ary_t *script_lines)
{
- switch (nd_type(node)) {
- case NODE_ARGS:
- {
- struct rb_args_info *args = node->nd_ainfo;
- rb_gc_mark_movable(args->imemo);
- break;
- }
- case NODE_MATCH:
- case NODE_LIT:
- case NODE_STR:
- case NODE_XSTR:
- case NODE_DSTR:
- case NODE_DXSTR:
- case NODE_DREGX:
- case NODE_DSYM:
- rb_gc_mark_movable(node->nd_lit);
- break;
- case NODE_ARYPTN:
- case NODE_FNDPTN:
- rb_gc_mark_movable(node->nd_rval);
- break;
- default:
- rb_bug("unreachable node %s", ruby_node_name(nd_type(node)));
- }
-}
-
-static void
-update_ast_value(void *ctx, NODE * node)
-{
- switch (nd_type(node)) {
- case NODE_ARGS:
- {
- struct rb_args_info *args = node->nd_ainfo;
- args->imemo = rb_gc_location(args->imemo);
- break;
- }
- case NODE_MATCH:
- case NODE_LIT:
- case NODE_STR:
- case NODE_XSTR:
- case NODE_DSTR:
- case NODE_DXSTR:
- case NODE_DREGX:
- case NODE_DSYM:
- node->nd_lit = rb_gc_location(node->nd_lit);
- break;
- case NODE_ARYPTN:
- case NODE_FNDPTN:
- node->nd_rval = rb_gc_location(node->nd_rval);
- break;
- default:
- rb_bug("unreachable");
- }
-}
-
-void
-rb_ast_update_references(rb_ast_t *ast)
-{
- if (ast->node_buffer) {
- node_buffer_t *nb = ast->node_buffer;
-
- iterate_node_values(&nb->markable, update_ast_value, NULL);
+ if (!script_lines) return;
+ for (long i = 0; i < script_lines->len; i++) {
+ parser_string_free(ast, (rb_parser_string_t *)script_lines->data[i]);
}
-}
-
-void
-rb_ast_mark(rb_ast_t *ast)
-{
- if (ast->node_buffer) {
- rb_gc_mark(ast->node_buffer->mark_hash);
- rb_gc_mark(ast->node_buffer->tokens);
- }
- if (ast->body.compile_option) rb_gc_mark(ast->body.compile_option);
- if (ast->node_buffer) {
- node_buffer_t *nb = ast->node_buffer;
-
- iterate_node_values(&nb->markable, mark_ast_value, NULL);
- }
- if (ast->body.script_lines) rb_gc_mark(ast->body.script_lines);
+ xfree(script_lines->data);
+ xfree(script_lines);
}
void
rb_ast_free(rb_ast_t *ast)
{
- if (ast->node_buffer) {
- rb_node_buffer_free(ast->node_buffer);
- ast->node_buffer = 0;
- }
+ rb_ast_dispose(ast);
+ xfree(ast);
}
static size_t
@@ -1464,8 +360,8 @@ buffer_list_size(node_buffer_list_t *nb)
size_t size = 0;
node_buffer_elem_t *nbe = nb->head;
while (nbe != nb->last) {
+ size += offsetof(node_buffer_elem_t, buf) + nbe->used;
nbe = nbe->next;
- size += offsetof(node_buffer_elem_t, buf) + nb->len * sizeof(NODE);
}
return size;
}
@@ -1473,40 +369,61 @@ buffer_list_size(node_buffer_list_t *nb)
size_t
rb_ast_memsize(const rb_ast_t *ast)
{
- size_t size = 0;
+ size_t size = sizeof(rb_ast_t);
node_buffer_t *nb = ast->node_buffer;
+ rb_parser_ary_t *tokens = NULL;
+ struct rb_ast_local_table_link *link = NULL;
+ rb_parser_ary_t *script_lines = ast->body.script_lines;
+
+ long i;
if (nb) {
- size += sizeof(node_buffer_t) + offsetof(node_buffer_elem_t, buf) + NODE_BUF_DEFAULT_LEN * sizeof(NODE);
- size += buffer_list_size(&nb->unmarkable);
- size += buffer_list_size(&nb->markable);
+ size += sizeof(node_buffer_t);
+ size += buffer_list_size(&nb->buffer_list);
+ link = nb->local_tables;
+ tokens = nb->tokens;
+ }
+
+ while (link) {
+ size += sizeof(struct rb_ast_local_table_link);
+ size += link->size * sizeof(ID);
+ link = link->next;
+ }
+
+ if (tokens) {
+ size += sizeof(rb_parser_ary_t);
+ for (i = 0; i < tokens->len; i++) {
+ size += sizeof(rb_parser_ast_token_t);
+ rb_parser_ast_token_t *token = tokens->data[i];
+ size += sizeof(rb_parser_string_t);
+ size += token->str->len + 1;
+ }
}
+
+ if (script_lines) {
+ size += sizeof(rb_parser_ary_t);
+ for (i = 0; i < script_lines->len; i++) {
+ size += sizeof(rb_parser_string_t);
+ size += ((rb_parser_string_t *)script_lines->data[i])->len + 1;
+ }
+ }
+
return size;
}
void
rb_ast_dispose(rb_ast_t *ast)
{
- rb_ast_free(ast);
-}
-
-void
-rb_ast_add_mark_object(rb_ast_t *ast, VALUE obj)
-{
- if (NIL_P(ast->node_buffer->mark_hash)) {
- RB_OBJ_WRITE(ast, &ast->node_buffer->mark_hash, rb_ident_hash_new());
+ if (ast && ast->node_buffer) {
+ script_lines_free(ast, ast->body.script_lines);
+ ast->body.script_lines = NULL;
+ rb_node_buffer_free(ast, ast->node_buffer);
+ ast->node_buffer = 0;
}
- rb_hash_aset(ast->node_buffer->mark_hash, obj, Qtrue);
}
VALUE
-rb_ast_tokens(rb_ast_t *ast)
-{
- return ast->node_buffer->tokens;
-}
-
-void
-rb_ast_set_tokens(rb_ast_t *ast, VALUE tokens)
+rb_node_set_type(NODE *n, enum node_type t)
{
- RB_OBJ_WRITE(ast, &ast->node_buffer->tokens, tokens);
+ return nd_init_type(n, t);
}
diff --git a/node.h b/node.h
index befb1328fb..1e411f211b 100644
--- a/node.h
+++ b/node.h
@@ -11,504 +11,101 @@
**********************************************************************/
-#include "internal/compilers.h"
-
-#if defined(__cplusplus)
-extern "C" {
-#if 0
-} /* satisfy cc-mode */
-#endif
-#endif
-
-enum node_type {
- NODE_SCOPE,
- NODE_BLOCK,
- NODE_IF,
- NODE_UNLESS,
- NODE_CASE,
- NODE_CASE2,
- NODE_CASE3,
- NODE_WHEN,
- NODE_IN,
- NODE_WHILE,
- NODE_UNTIL,
- NODE_ITER,
- NODE_FOR,
- NODE_FOR_MASGN,
- NODE_BREAK,
- NODE_NEXT,
- NODE_REDO,
- NODE_RETRY,
- NODE_BEGIN,
- NODE_RESCUE,
- NODE_RESBODY,
- NODE_ENSURE,
- NODE_AND,
- NODE_OR,
- NODE_MASGN,
- NODE_LASGN,
- NODE_DASGN,
- NODE_GASGN,
- NODE_IASGN,
- NODE_CDECL,
- NODE_CVASGN,
- NODE_OP_ASGN1,
- NODE_OP_ASGN2,
- NODE_OP_ASGN_AND,
- NODE_OP_ASGN_OR,
- NODE_OP_CDECL,
- NODE_CALL,
- NODE_OPCALL,
- NODE_FCALL,
- NODE_VCALL,
- NODE_QCALL,
- NODE_SUPER,
- NODE_ZSUPER,
- NODE_LIST,
- NODE_ZLIST,
- NODE_VALUES,
- NODE_HASH,
- NODE_RETURN,
- NODE_YIELD,
- NODE_LVAR,
- NODE_DVAR,
- NODE_GVAR,
- NODE_IVAR,
- NODE_CONST,
- NODE_CVAR,
- NODE_NTH_REF,
- NODE_BACK_REF,
- NODE_MATCH,
- NODE_MATCH2,
- NODE_MATCH3,
- NODE_LIT,
- NODE_STR,
- NODE_DSTR,
- NODE_XSTR,
- NODE_DXSTR,
- NODE_EVSTR,
- NODE_DREGX,
- NODE_ONCE,
- NODE_ARGS,
- NODE_ARGS_AUX,
- NODE_OPT_ARG,
- NODE_KW_ARG,
- NODE_POSTARG,
- NODE_ARGSCAT,
- NODE_ARGSPUSH,
- NODE_SPLAT,
- NODE_BLOCK_PASS,
- NODE_DEFN,
- NODE_DEFS,
- NODE_ALIAS,
- NODE_VALIAS,
- NODE_UNDEF,
- NODE_CLASS,
- NODE_MODULE,
- NODE_SCLASS,
- NODE_COLON2,
- NODE_COLON3,
- NODE_DOT2,
- NODE_DOT3,
- NODE_FLIP2,
- NODE_FLIP3,
- NODE_SELF,
- NODE_NIL,
- NODE_TRUE,
- NODE_FALSE,
- NODE_ERRINFO,
- NODE_DEFINED,
- NODE_POSTEXE,
- NODE_DSYM,
- NODE_ATTRASGN,
- NODE_LAMBDA,
- NODE_ARYPTN,
- NODE_HSHPTN,
- NODE_FNDPTN,
- NODE_ERROR,
- NODE_LAST
+#include <stdbool.h>
+#include "rubyparser.h"
+#include "ruby/backward/2/attributes.h"
+
+typedef void (*bug_report_func)(const char *fmt, ...) RUBYPARSER_ATTRIBUTE_FORMAT(1, 2);
+typedef struct node_buffer_elem_struct {
+ struct node_buffer_elem_struct *next;
+ long len; /* Length of nodes */
+ size_t allocated; /* Total memory size of allocated buf */
+ size_t used; /* Current usage of buf */
+ NODE **nodes; /* Array of node pointers */
+ NODE *buf[FLEX_ARY_LEN];
+} node_buffer_elem_t;
+
+typedef struct {
+ node_buffer_elem_t *head;
+ node_buffer_elem_t *last;
+} node_buffer_list_t;
+
+struct node_buffer_struct {
+ node_buffer_list_t buffer_list;
+ struct rb_ast_local_table_link *local_tables;
+ // - id (sequence number)
+ // - token_type
+ // - text of token
+ // - location info
+ // Array, whose entry is array
+ rb_parser_ary_t *tokens;
};
-typedef struct rb_code_position_struct {
- int lineno;
- int column;
-} rb_code_position_t;
-
-typedef struct rb_code_location_struct {
- rb_code_position_t beg_pos;
- rb_code_position_t end_pos;
-} rb_code_location_t;
-
-static inline rb_code_location_t
-code_loc_gen(const rb_code_location_t *loc1, const rb_code_location_t *loc2)
-{
- rb_code_location_t loc;
- loc.beg_pos = loc1->beg_pos;
- loc.end_pos = loc2->end_pos;
- return loc;
-}
+RUBY_SYMBOL_EXPORT_BEGIN
-typedef struct rb_ast_id_table {
- int size;
- ID ids[FLEX_ARY_LEN];
-} rb_ast_id_table_t;
+#ifdef UNIVERSAL_PARSER
+rb_ast_t *rb_ast_new(const rb_parser_config_t *config);
+#else
+rb_ast_t *rb_ast_new(void);
+#endif
+size_t rb_ast_memsize(const rb_ast_t*);
+void rb_ast_dispose(rb_ast_t*);
+const char *ruby_node_name(int node);
+void rb_node_init(NODE *n, enum node_type type);
-typedef struct RNode {
- VALUE flags;
- union {
- struct RNode *node;
- ID id;
- VALUE value;
- rb_ast_id_table_t *tbl;
- } u1;
- union {
- struct RNode *node;
- ID id;
- long argc;
- VALUE value;
- } u2;
- union {
- struct RNode *node;
- ID id;
- long state;
- struct rb_args_info *args;
- struct rb_ary_pattern_info *apinfo;
- struct rb_fnd_pattern_info *fpinfo;
- VALUE value;
- } u3;
- rb_code_location_t nd_loc;
- int node_id;
-} NODE;
+void rb_ast_update_references(rb_ast_t*);
+void rb_ast_free(rb_ast_t*);
+NODE *rb_ast_newnode(rb_ast_t*, enum node_type type, size_t size, size_t alignment);
+void rb_ast_delete_node(rb_ast_t*, NODE *n);
+rb_ast_id_table_t *rb_ast_new_local_table(rb_ast_t*, int);
+rb_ast_id_table_t *rb_ast_resize_latest_local_table(rb_ast_t*, int);
-#define RNODE(obj) ((struct RNode *)(obj))
+VALUE rb_parser_dump_tree(const NODE *node, int comment);
-/* FL : 0..4: T_TYPES, 5: KEEP_WB, 6: PROMOTED, 7: FINALIZE, 8: UNUSED, 9: UNUSED, 10: EXIVAR, 11: FREEZE */
-/* NODE_FL: 0..4: T_TYPES, 5: KEEP_WB, 6: PROMOTED, 7: NODE_FL_NEWLINE,
- * 8..14: nd_type,
- * 15..: nd_line
- */
-#define NODE_FL_NEWLINE (((VALUE)1)<<7)
+const struct kwtable *rb_reserved_word(const char *, unsigned int);
-#define NODE_TYPESHIFT 8
-#define NODE_TYPEMASK (((VALUE)0x7f)<<NODE_TYPESHIFT)
+struct parser_params;
+PRINTF_ARGS(void rb_parser_printf(struct parser_params *parser, const char *fmt, ...), 2, 3);
+VALUE rb_node_set_type(NODE *n, enum node_type t);
-#define nd_type(n) ((int) (((n)->flags & NODE_TYPEMASK)>>NODE_TYPESHIFT))
-#define nd_set_type(n,t) \
- rb_node_set_type(n, t)
-#define nd_init_type(n,t) \
- (n)->flags=(((n)->flags&~NODE_TYPEMASK)|((((unsigned long)(t))<<NODE_TYPESHIFT)&NODE_TYPEMASK))
+RUBY_SYMBOL_EXPORT_END
#define NODE_LSHIFT (NODE_TYPESHIFT+7)
#define NODE_LMASK (((SIGNED_VALUE)1<<(sizeof(VALUE)*CHAR_BIT-NODE_LSHIFT))-1)
+
#define nd_line(n) (int)(((SIGNED_VALUE)(n)->flags)>>NODE_LSHIFT)
#define nd_set_line(n,l) \
(n)->flags=(((n)->flags&~((VALUE)(-1)<<NODE_LSHIFT))|((VALUE)((l)&NODE_LMASK)<<NODE_LSHIFT))
-#define nd_first_column(n) ((int)((n)->nd_loc.beg_pos.column))
-#define nd_set_first_column(n, v) ((n)->nd_loc.beg_pos.column = (v))
-#define nd_first_lineno(n) ((int)((n)->nd_loc.beg_pos.lineno))
-#define nd_set_first_lineno(n, v) ((n)->nd_loc.beg_pos.lineno = (v))
-#define nd_first_loc(n) ((n)->nd_loc.beg_pos)
-#define nd_set_first_loc(n, v) (nd_first_loc(n) = (v))
-
-#define nd_last_column(n) ((int)((n)->nd_loc.end_pos.column))
-#define nd_set_last_column(n, v) ((n)->nd_loc.end_pos.column = (v))
-#define nd_last_lineno(n) ((int)((n)->nd_loc.end_pos.lineno))
-#define nd_set_last_lineno(n, v) ((n)->nd_loc.end_pos.lineno = (v))
-#define nd_last_loc(n) ((n)->nd_loc.end_pos)
-#define nd_set_last_loc(n, v) (nd_last_loc(n) = (v))
-#define nd_node_id(n) ((n)->node_id)
-#define nd_set_node_id(n,id) ((n)->node_id = (id))
-
-#define nd_head u1.node
-#define nd_alen u2.argc
-#define nd_next u3.node
-
-#define nd_cond u1.node
-#define nd_body u2.node
-#define nd_else u3.node
-
-#define nd_resq u2.node
-#define nd_ensr u3.node
-
-#define nd_1st u1.node
-#define nd_2nd u2.node
-
-#define nd_stts u1.node
-
-#define nd_entry u3.id
-#define nd_vid u1.id
-
-#define nd_var u1.node
-#define nd_iter u3.node
-
-#define nd_value u2.node
-#define nd_aid u3.id
-
-#define nd_lit u1.value
-
-#define nd_recv u1.node
-#define nd_mid u2.id
-#define nd_args u3.node
-#define nd_ainfo u3.args
-
-#define nd_defn u3.node
-
-#define nd_cpath u1.node
-#define nd_super u3.node
-
-#define nd_beg u1.node
-#define nd_end u2.node
-#define nd_state u3.state
-
-#define nd_nth u2.argc
-
-#define nd_alias u1.id
-#define nd_orig u2.id
-#define nd_undef u2.node
-
-#define nd_brace u2.argc
-
-#define nd_pconst u1.node
-#define nd_pkwargs u2.node
-#define nd_pkwrestarg u3.node
-
-#define nd_apinfo u3.apinfo
-
-#define nd_fpinfo u3.fpinfo
-
-// for NODE_SCOPE
-#define nd_tbl u1.tbl
-
-// for NODE_ARGS_AUX
-#define nd_pid u1.id
-#define nd_plen u2.argc
-#define nd_cflag u2.id
-
-// for ripper
-#define nd_cval u3.value
-#define nd_rval u2.value
-#define nd_tag u1.id
-
-#define NEW_NODE(t,a0,a1,a2,loc) rb_node_newnode((t),(VALUE)(a0),(VALUE)(a1),(VALUE)(a2),loc)
-#define NEW_NODE_WITH_LOCALS(t,a1,a2,loc) node_newnode_with_locals(p, (t),(VALUE)(a1),(VALUE)(a2),loc)
-
-#define NEW_DEFN(i,a,d,loc) NEW_NODE(NODE_DEFN,0,i,NEW_SCOPE(a,d,loc),loc)
-#define NEW_DEFS(r,i,a,d,loc) NEW_NODE(NODE_DEFS,r,i,NEW_SCOPE(a,d,loc),loc)
-#define NEW_SCOPE(a,b,loc) NEW_NODE_WITH_LOCALS(NODE_SCOPE,b,a,loc)
-#define NEW_BLOCK(a,loc) NEW_NODE(NODE_BLOCK,a,0,0,loc)
-#define NEW_IF(c,t,e,loc) NEW_NODE(NODE_IF,c,t,e,loc)
-#define NEW_UNLESS(c,t,e,loc) NEW_NODE(NODE_UNLESS,c,t,e,loc)
-#define NEW_CASE(h,b,loc) NEW_NODE(NODE_CASE,h,b,0,loc)
-#define NEW_CASE2(b,loc) NEW_NODE(NODE_CASE2,0,b,0,loc)
-#define NEW_CASE3(h,b,loc) NEW_NODE(NODE_CASE3,h,b,0,loc)
-#define NEW_WHEN(c,t,e,loc) NEW_NODE(NODE_WHEN,c,t,e,loc)
-#define NEW_IN(c,t,e,loc) NEW_NODE(NODE_IN,c,t,e,loc)
-#define NEW_WHILE(c,b,n,loc) NEW_NODE(NODE_WHILE,c,b,n,loc)
-#define NEW_UNTIL(c,b,n,loc) NEW_NODE(NODE_UNTIL,c,b,n,loc)
-#define NEW_FOR(i,b,loc) NEW_NODE(NODE_FOR,0,b,i,loc)
-#define NEW_FOR_MASGN(v,loc) NEW_NODE(NODE_FOR_MASGN,v,0,0,loc)
-#define NEW_ITER(a,b,loc) NEW_NODE(NODE_ITER,0,NEW_SCOPE(a,b,loc),0,loc)
-#define NEW_LAMBDA(a,b,loc) NEW_NODE(NODE_LAMBDA,0,NEW_SCOPE(a,b,loc),0,loc)
-#define NEW_BREAK(s,loc) NEW_NODE(NODE_BREAK,s,0,0,loc)
-#define NEW_NEXT(s,loc) NEW_NODE(NODE_NEXT,s,0,0,loc)
-#define NEW_REDO(loc) NEW_NODE(NODE_REDO,0,0,0,loc)
-#define NEW_RETRY(loc) NEW_NODE(NODE_RETRY,0,0,0,loc)
-#define NEW_BEGIN(b,loc) NEW_NODE(NODE_BEGIN,0,b,0,loc)
-#define NEW_RESCUE(b,res,e,loc) NEW_NODE(NODE_RESCUE,b,res,e,loc)
-#define NEW_RESBODY(a,ex,n,loc) NEW_NODE(NODE_RESBODY,n,ex,a,loc)
-#define NEW_ENSURE(b,en,loc) NEW_NODE(NODE_ENSURE,b,0,en,loc)
-#define NEW_RETURN(s,loc) NEW_NODE(NODE_RETURN,s,0,0,loc)
-#define NEW_YIELD(a,loc) NEW_NODE(NODE_YIELD,a,0,0,loc)
-#define NEW_LIST(a,loc) NEW_NODE(NODE_LIST,a,1,0,loc)
-#define NEW_ZLIST(loc) NEW_NODE(NODE_ZLIST,0,0,0,loc)
-#define NEW_HASH(a,loc) NEW_NODE(NODE_HASH,a,0,0,loc)
-#define NEW_MASGN(l,r,loc) NEW_NODE(NODE_MASGN,l,0,r,loc)
-#define NEW_GASGN(v,val,loc) NEW_NODE(NODE_GASGN,v,val,v,loc)
-#define NEW_LASGN(v,val,loc) NEW_NODE(NODE_LASGN,v,val,0,loc)
-#define NEW_DASGN(v,val,loc) NEW_NODE(NODE_DASGN,v,val,0,loc)
-#define NEW_IASGN(v,val,loc) NEW_NODE(NODE_IASGN,v,val,0,loc)
-#define NEW_CDECL(v,val,path,loc) NEW_NODE(NODE_CDECL,v,val,path,loc)
-#define NEW_CVASGN(v,val,loc) NEW_NODE(NODE_CVASGN,v,val,0,loc)
-#define NEW_OP_ASGN1(p,id,a,loc) NEW_NODE(NODE_OP_ASGN1,p,id,a,loc)
-#define NEW_OP_ASGN2(r,t,i,o,val,loc) NEW_NODE(NODE_OP_ASGN2,r,val,NEW_OP_ASGN22(i,o,t,loc),loc)
-#define NEW_OP_ASGN22(i,o,t,loc) NEW_NODE(NODE_OP_ASGN2,i,o,t,loc)
-#define NEW_OP_ASGN_OR(i,val,loc) NEW_NODE(NODE_OP_ASGN_OR,i,val,0,loc)
-#define NEW_OP_ASGN_AND(i,val,loc) NEW_NODE(NODE_OP_ASGN_AND,i,val,0,loc)
-#define NEW_OP_CDECL(v,op,val,loc) NEW_NODE(NODE_OP_CDECL,v,val,op,loc)
-#define NEW_GVAR(v,loc) NEW_NODE(NODE_GVAR,v,0,v,loc)
-#define NEW_LVAR(v,loc) NEW_NODE(NODE_LVAR,v,0,0,loc)
-#define NEW_DVAR(v,loc) NEW_NODE(NODE_DVAR,v,0,0,loc)
-#define NEW_IVAR(v,loc) NEW_NODE(NODE_IVAR,v,0,0,loc)
-#define NEW_CONST(v,loc) NEW_NODE(NODE_CONST,v,0,0,loc)
-#define NEW_CVAR(v,loc) NEW_NODE(NODE_CVAR,v,0,0,loc)
-#define NEW_NTH_REF(n,loc) NEW_NODE(NODE_NTH_REF,0,n,0,loc)
-#define NEW_BACK_REF(n,loc) NEW_NODE(NODE_BACK_REF,0,n,0,loc)
-#define NEW_MATCH(c,loc) NEW_NODE(NODE_MATCH,c,0,0,loc)
-#define NEW_MATCH2(n1,n2,loc) NEW_NODE(NODE_MATCH2,n1,n2,0,loc)
-#define NEW_MATCH3(r,n2,loc) NEW_NODE(NODE_MATCH3,r,n2,0,loc)
-#define NEW_LIT(l,loc) NEW_NODE(NODE_LIT,l,0,0,loc)
-#define NEW_STR(s,loc) NEW_NODE(NODE_STR,s,0,0,loc)
-#define NEW_DSTR(s,loc) NEW_NODE(NODE_DSTR,s,1,0,loc)
-#define NEW_XSTR(s,loc) NEW_NODE(NODE_XSTR,s,0,0,loc)
-#define NEW_DXSTR(s,loc) NEW_NODE(NODE_DXSTR,s,0,0,loc)
-#define NEW_DSYM(s,loc) NEW_NODE(NODE_DSYM,s,0,0,loc)
-#define NEW_EVSTR(n,loc) NEW_NODE(NODE_EVSTR,0,(n),0,loc)
-#define NEW_CALL(r,m,a,loc) NEW_NODE(NODE_CALL,r,m,a,loc)
-#define NEW_OPCALL(r,m,a,loc) NEW_NODE(NODE_OPCALL,r,m,a,loc)
-#define NEW_FCALL(m,a,loc) NEW_NODE(NODE_FCALL,0,m,a,loc)
-#define NEW_VCALL(m,loc) NEW_NODE(NODE_VCALL,0,m,0,loc)
-#define NEW_SUPER(a,loc) NEW_NODE(NODE_SUPER,0,0,a,loc)
-#define NEW_ZSUPER(loc) NEW_NODE(NODE_ZSUPER,0,0,0,loc)
-#define NEW_ARGS_AUX(r,b,loc) NEW_NODE(NODE_ARGS_AUX,r,b,0,loc)
-#define NEW_OPT_ARG(i,v,loc) NEW_NODE(NODE_OPT_ARG,i,v,0,loc)
-#define NEW_KW_ARG(i,v,loc) NEW_NODE(NODE_KW_ARG,i,v,0,loc)
-#define NEW_POSTARG(i,v,loc) NEW_NODE(NODE_POSTARG,i,v,0,loc)
-#define NEW_ARGSCAT(a,b,loc) NEW_NODE(NODE_ARGSCAT,a,b,0,loc)
-#define NEW_ARGSPUSH(a,b,loc) NEW_NODE(NODE_ARGSPUSH,a,b,0,loc)
-#define NEW_SPLAT(a,loc) NEW_NODE(NODE_SPLAT,a,0,0,loc)
-#define NEW_BLOCK_PASS(b,loc) NEW_NODE(NODE_BLOCK_PASS,0,b,0,loc)
-#define NEW_ALIAS(n,o,loc) NEW_NODE(NODE_ALIAS,n,o,0,loc)
-#define NEW_VALIAS(n,o,loc) NEW_NODE(NODE_VALIAS,n,o,0,loc)
-#define NEW_UNDEF(i,loc) NEW_NODE(NODE_UNDEF,0,i,0,loc)
-#define NEW_CLASS(n,b,s,loc) NEW_NODE(NODE_CLASS,n,NEW_SCOPE(0,b,loc),(s),loc)
-#define NEW_SCLASS(r,b,loc) NEW_NODE(NODE_SCLASS,r,NEW_SCOPE(0,b,loc),0,loc)
-#define NEW_MODULE(n,b,loc) NEW_NODE(NODE_MODULE,n,NEW_SCOPE(0,b,loc),0,loc)
-#define NEW_COLON2(c,i,loc) NEW_NODE(NODE_COLON2,c,i,0,loc)
-#define NEW_COLON3(i,loc) NEW_NODE(NODE_COLON3,0,i,0,loc)
-#define NEW_DOT2(b,e,loc) NEW_NODE(NODE_DOT2,b,e,0,loc)
-#define NEW_DOT3(b,e,loc) NEW_NODE(NODE_DOT3,b,e,0,loc)
-#define NEW_SELF(loc) NEW_NODE(NODE_SELF,0,0,1,loc)
-#define NEW_NIL(loc) NEW_NODE(NODE_NIL,0,0,0,loc)
-#define NEW_TRUE(loc) NEW_NODE(NODE_TRUE,0,0,0,loc)
-#define NEW_FALSE(loc) NEW_NODE(NODE_FALSE,0,0,0,loc)
-#define NEW_ERRINFO(loc) NEW_NODE(NODE_ERRINFO,0,0,0,loc)
-#define NEW_DEFINED(e,loc) NEW_NODE(NODE_DEFINED,e,0,0,loc)
-#define NEW_POSTEXE(b,loc) NEW_NODE(NODE_POSTEXE,0,b,0,loc)
-#define NEW_ATTRASGN(r,m,a,loc) NEW_NODE(NODE_ATTRASGN,r,m,a,loc)
-#define NEW_ERROR(loc) NEW_NODE(NODE_ERROR,0,0,0,loc)
#define NODE_SPECIAL_REQUIRED_KEYWORD ((NODE *)-1)
-#define NODE_REQUIRED_KEYWORD_P(node) ((node)->nd_value == NODE_SPECIAL_REQUIRED_KEYWORD)
+#define NODE_REQUIRED_KEYWORD_P(node) ((node) == NODE_SPECIAL_REQUIRED_KEYWORD)
#define NODE_SPECIAL_NO_NAME_REST ((NODE *)-1)
#define NODE_NAMED_REST_P(node) ((node) != NODE_SPECIAL_NO_NAME_REST)
#define NODE_SPECIAL_EXCESSIVE_COMMA ((ID)1)
#define NODE_SPECIAL_NO_REST_KEYWORD ((NODE *)-1)
-VALUE rb_node_case_when_optimizable_literal(const NODE *const node);
-
-RUBY_SYMBOL_EXPORT_BEGIN
-
-typedef struct node_buffer_struct node_buffer_t;
-/* T_IMEMO/ast */
-typedef struct rb_ast_body_struct {
- const NODE *root;
- VALUE compile_option;
- VALUE script_lines;
- // script_lines is either:
- // - a Fixnum that represents the line count of the original source, or
- // - an Array that contains the lines of the original source
-} rb_ast_body_t;
-typedef struct rb_ast_struct {
- VALUE flags;
- node_buffer_t *node_buffer;
- rb_ast_body_t body;
-} rb_ast_t;
-rb_ast_t *rb_ast_new(void);
-void rb_ast_mark(rb_ast_t*);
-void rb_ast_update_references(rb_ast_t*);
-void rb_ast_dispose(rb_ast_t*);
-void rb_ast_free(rb_ast_t*);
-size_t rb_ast_memsize(const rb_ast_t*);
-void rb_ast_add_mark_object(rb_ast_t*, VALUE);
-void rb_ast_set_tokens(rb_ast_t*, VALUE);
-VALUE rb_ast_tokens(rb_ast_t *ast);
-NODE *rb_ast_newnode(rb_ast_t*, enum node_type type);
-void rb_ast_delete_node(rb_ast_t*, NODE *n);
-rb_ast_id_table_t *rb_ast_new_local_table(rb_ast_t*, int);
-rb_ast_id_table_t *rb_ast_resize_latest_local_table(rb_ast_t*, int);
-
-VALUE rb_parser_new(void);
-VALUE rb_parser_end_seen_p(VALUE);
-VALUE rb_parser_encoding(VALUE);
-VALUE rb_parser_set_yydebug(VALUE, VALUE);
-VALUE rb_parser_dump_tree(const NODE *node, int comment);
-void rb_parser_set_options(VALUE, int, int, int, int);
-
-rb_ast_t *rb_parser_compile_string(VALUE, const char*, VALUE, int);
-rb_ast_t *rb_parser_compile_string_path(VALUE vparser, VALUE fname, VALUE src, int line);
-rb_ast_t *rb_parser_compile_file_path(VALUE vparser, VALUE fname, VALUE input, int line);
-rb_ast_t *rb_parser_compile_generic(VALUE vparser, VALUE (*lex_gets)(VALUE, int), VALUE fname, VALUE input, int line);
-
-void rb_node_init(NODE *n, enum node_type type, VALUE a0, VALUE a1, VALUE a2);
-const char *ruby_node_name(int node);
-
-const struct kwtable *rb_reserved_word(const char *, unsigned int);
-
-struct rb_args_info {
- NODE *pre_init;
- NODE *post_init;
-
- int pre_args_num; /* count of mandatory pre-arguments */
- int post_args_num; /* count of mandatory post-arguments */
-
- ID first_post_arg;
-
- ID rest_arg;
- ID block_arg;
-
- NODE *kw_args;
- NODE *kw_rest_arg;
-
- NODE *opt_args;
- unsigned int no_kwarg: 1;
- unsigned int ruby2_keywords: 1;
- unsigned int forwarding: 1;
-
- VALUE imemo;
-};
-
-struct rb_ary_pattern_info {
- NODE *pre_args;
- NODE *rest_arg;
- NODE *post_args;
-};
-
-struct rb_fnd_pattern_info {
- NODE *pre_rest_arg;
- NODE *args;
- NODE *post_rest_arg;
-};
-
-struct parser_params;
-void *rb_parser_malloc(struct parser_params *, size_t);
-void *rb_parser_realloc(struct parser_params *, void *, size_t);
-void *rb_parser_calloc(struct parser_params *, size_t, size_t);
-void rb_parser_free(struct parser_params *, void *);
-PRINTF_ARGS(void rb_parser_printf(struct parser_params *parser, const char *fmt, ...), 2, 3);
-void rb_ast_node_type_change(NODE *n, enum node_type type);
-
-RUBY_SYMBOL_EXPORT_END
+#define nd_code_loc(n) (&RNODE(n)->nd_loc)
+#define nd_first_column(n) ((int)(RNODE(n)->nd_loc.beg_pos.column))
+#define nd_set_first_column(n, v) (RNODE(n)->nd_loc.beg_pos.column = (v))
+#define nd_first_lineno(n) ((int)(RNODE(n)->nd_loc.beg_pos.lineno))
+#define nd_set_first_lineno(n, v) (RNODE(n)->nd_loc.beg_pos.lineno = (v))
+#define nd_first_loc(n) (RNODE(n)->nd_loc.beg_pos)
+#define nd_set_first_loc(n, v) (nd_first_loc(n) = (v))
-static inline VALUE
-rb_node_set_type(NODE *n, enum node_type t)
-{
-#if RUBY_DEBUG
- rb_ast_node_type_change(n, t);
-#endif
- return nd_init_type(n, t);
-}
+#define nd_last_column(n) ((int)(RNODE(n)->nd_loc.end_pos.column))
+#define nd_set_last_column(n, v) (RNODE(n)->nd_loc.end_pos.column = (v))
+#define nd_last_lineno(n) ((int)(RNODE(n)->nd_loc.end_pos.lineno))
+#define nd_set_last_lineno(n, v) (RNODE(n)->nd_loc.end_pos.lineno = (v))
+#define nd_last_loc(n) (RNODE(n)->nd_loc.end_pos)
+#define nd_set_last_loc(n, v) (nd_last_loc(n) = (v))
+#define nd_node_id(n) (RNODE(n)->node_id)
+#define nd_set_node_id(n,id) (RNODE(n)->node_id = (id))
static inline bool
nd_type_p(const NODE *n, enum node_type t)
{
return (enum node_type)nd_type(n) == t;
}
-#if defined(__cplusplus)
-#if 0
-{ /* satisfy cc-mode */
-#endif
-} /* extern "C" { */
-#endif
#endif /* RUBY_NODE_H */
diff --git a/node_dump.c b/node_dump.c
new file mode 100644
index 0000000000..37abea8441
--- /dev/null
+++ b/node_dump.c
@@ -0,0 +1,1206 @@
+/**********************************************************************
+
+ node_dump.c - dump ruby node tree
+
+ $Author: mame $
+ created at: 09/12/06 21:23:44 JST
+
+ Copyright (C) 2009 Yusuke Endoh
+
+**********************************************************************/
+
+#include "internal.h"
+#include "internal/class.h"
+#include "internal/hash.h"
+#include "internal/ruby_parser.h"
+#include "internal/variable.h"
+#include "ruby/ruby.h"
+#include "vm_core.h"
+
+#define A(str) rb_str_cat2(buf, (str))
+#define AR(str) rb_str_concat(buf, (str))
+
+#define A_INDENT add_indent(buf, indent)
+#define D_INDENT rb_str_cat2(indent, next_indent)
+#define D_DEDENT rb_str_resize(indent, RSTRING_LEN(indent) - 4)
+#define A_ID(id) add_id(buf, (id))
+#define A_INT(val) rb_str_catf(buf, "%d", (val))
+#define A_LONG(val) rb_str_catf(buf, "%ld", (val))
+#define A_LIT(lit) AR(rb_dump_literal(lit))
+#define A_NODE_HEADER(node, term) \
+ rb_str_catf(buf, "@ %s (id: %d, line: %d, location: (%d,%d)-(%d,%d))%s"term, \
+ ruby_node_name(nd_type(node)), nd_node_id(node), nd_line(node), \
+ nd_first_lineno(node), nd_first_column(node), \
+ nd_last_lineno(node), nd_last_column(node), \
+ (nd_fl_newline(node) ? "*" : ""))
+#define A_FIELD_HEADER(len, name, term) \
+ rb_str_catf(buf, "+- %.*s:"term, (len), (name))
+#define D_FIELD_HEADER(len, name, term) (A_INDENT, A_FIELD_HEADER(len, name, term))
+
+#define D_NULL_NODE (A_INDENT, A("(null node)\n"))
+#define D_NODE_HEADER(node) (A_INDENT, A_NODE_HEADER(node, "\n"))
+
+#define COMPOUND_FIELD(len, name) \
+ FIELD_BLOCK((D_FIELD_HEADER((len), (name), "\n"), D_INDENT), D_DEDENT)
+
+#define COMPOUND_FIELD1(name, ann) \
+ COMPOUND_FIELD(FIELD_NAME_LEN(name, ann), \
+ FIELD_NAME_DESC(name, ann))
+
+#define FIELD_NAME_DESC(name, ann) name " (" ann ")"
+#define FIELD_NAME_LEN(name, ann) (int)( \
+ comment ? \
+ rb_strlen_lit(FIELD_NAME_DESC(name, ann)) : \
+ rb_strlen_lit(name))
+#define SIMPLE_FIELD(len, name) \
+ FIELD_BLOCK(D_FIELD_HEADER((len), (name), " "), A("\n"))
+
+#define FIELD_BLOCK(init, reset) \
+ for (init, field_flag = 1; \
+ field_flag; /* should be optimized away */ \
+ reset, field_flag = 0)
+
+#define A_SHAREABILITY(shareability) \
+ switch (shareability) { \
+ case rb_parser_shareable_none: \
+ rb_str_cat_cstr(buf, "none"); \
+ break; \
+ case rb_parser_shareable_literal: \
+ rb_str_cat_cstr(buf, "literal"); \
+ break; \
+ case rb_parser_shareable_copy: \
+ rb_str_cat_cstr(buf, "experimental_copy"); \
+ break; \
+ case rb_parser_shareable_everything: \
+ rb_str_cat_cstr(buf, "experimental_everything"); \
+ break; \
+ }
+
+#define SIMPLE_FIELD1(name, ann) SIMPLE_FIELD(FIELD_NAME_LEN(name, ann), FIELD_NAME_DESC(name, ann))
+#define F_CUSTOM1(name, ann) SIMPLE_FIELD1(#name, ann)
+#define F_ID(name, type, ann) SIMPLE_FIELD1(#name, ann) A_ID(type(node)->name)
+#define F_INT(name, type, ann) SIMPLE_FIELD1(#name, ann) A_INT(type(node)->name)
+#define F_LONG(name, type, ann) SIMPLE_FIELD1(#name, ann) A_LONG(type(node)->name)
+#define F_LIT(name, type, ann) SIMPLE_FIELD1(#name, ann) A_LIT(type(node)->name)
+#define F_VALUE(name, val, ann) SIMPLE_FIELD1(#name, ann) A_LIT(val)
+#define F_MSG(name, ann, desc) SIMPLE_FIELD1(#name, ann) A(desc)
+#define F_SHAREABILITY(name, type, ann) SIMPLE_FIELD1(#name, ann) A_SHAREABILITY(type(node)->name)
+
+#define F_NODE(name, type, ann) \
+ COMPOUND_FIELD1(#name, ann) {dump_node(buf, indent, comment, RNODE(type(node)->name));}
+
+#define F_NODE2(name, n, ann) \
+ COMPOUND_FIELD1(#name, ann) {dump_node(buf, indent, comment, n);}
+
+#define ANN(ann) \
+ if (comment) { \
+ A_INDENT; A("| # " ann "\n"); \
+ }
+
+#define LAST_NODE (next_indent = " ")
+
+VALUE
+rb_dump_literal(VALUE lit)
+{
+ if (!RB_SPECIAL_CONST_P(lit)) {
+ VALUE str;
+ switch (RB_BUILTIN_TYPE(lit)) {
+ case T_CLASS: case T_MODULE: case T_ICLASS:
+ str = rb_class_path(lit);
+ if (RCLASS_SINGLETON_P(lit)) {
+ str = rb_sprintf("<%"PRIsVALUE">", str);
+ }
+ return str;
+ default:
+ break;
+ }
+ }
+ return rb_inspect(lit);
+}
+
+static void
+add_indent(VALUE buf, VALUE indent)
+{
+ AR(indent);
+}
+
+static void
+add_id(VALUE buf, ID id)
+{
+ if (id == 0) {
+ A("(null)");
+ }
+ else {
+ VALUE str = rb_id2str(id);
+ if (str) {
+ A(":"); AR(str);
+ }
+ else {
+ rb_str_catf(buf, "(internal variable: 0x%"PRIsVALUE")", id);
+ }
+ }
+}
+
+struct add_option_arg {
+ VALUE buf, indent;
+ st_index_t count;
+};
+
+static void dump_node(VALUE, VALUE, int, const NODE *);
+static const char default_indent[] = "| ";
+
+static void
+dump_array(VALUE buf, VALUE indent, int comment, const NODE *node)
+{
+ int field_flag;
+ const char *next_indent = default_indent;
+ F_LONG(as.nd_alen, RNODE_LIST, "length");
+ F_NODE(nd_head, RNODE_LIST, "element");
+ while (RNODE_LIST(node)->nd_next && nd_type_p(RNODE_LIST(node)->nd_next, NODE_LIST)) {
+ node = RNODE_LIST(node)->nd_next;
+ F_NODE(nd_head, RNODE_LIST, "element");
+ }
+ LAST_NODE;
+ F_NODE(nd_next, RNODE_LIST, "next element");
+}
+
+static void
+dump_node(VALUE buf, VALUE indent, int comment, const NODE * node)
+{
+ int field_flag;
+ int i;
+ const char *next_indent = default_indent;
+ enum node_type type;
+
+ if (!node) {
+ D_NULL_NODE;
+ return;
+ }
+
+ D_NODE_HEADER(node);
+
+ type = nd_type(node);
+ switch (type) {
+ case NODE_BLOCK:
+ ANN("statement sequence");
+ ANN("format: [nd_head]; ...; [nd_next]");
+ ANN("example: foo; bar");
+ i = 0;
+ do {
+ A_INDENT;
+ rb_str_catf(buf, "+- nd_head (%s%d):\n",
+ comment ? "statement #" : "", ++i);
+ if (!RNODE_BLOCK(node)->nd_next) LAST_NODE;
+ D_INDENT;
+ dump_node(buf, indent, comment, RNODE_BLOCK(node)->nd_head);
+ D_DEDENT;
+ } while (RNODE_BLOCK(node)->nd_next &&
+ nd_type_p(RNODE_BLOCK(node)->nd_next, NODE_BLOCK) &&
+ (node = RNODE_BLOCK(node)->nd_next, 1));
+ if (RNODE_BLOCK(node)->nd_next) {
+ LAST_NODE;
+ F_NODE(nd_next, RNODE_BLOCK, "next block");
+ }
+ return;
+
+ case NODE_IF:
+ ANN("if statement");
+ ANN("format: if [nd_cond] then [nd_body] else [nd_else] end");
+ ANN("example: if x == 1 then foo else bar end");
+ F_NODE(nd_cond, RNODE_IF, "condition expr");
+ F_NODE(nd_body, RNODE_IF, "then clause");
+ LAST_NODE;
+ F_NODE(nd_else, RNODE_IF, "else clause");
+ return;
+
+ case NODE_UNLESS:
+ ANN("unless statement");
+ ANN("format: unless [nd_cond] then [nd_body] else [nd_else] end");
+ ANN("example: unless x == 1 then foo else bar end");
+ F_NODE(nd_cond, RNODE_UNLESS, "condition expr");
+ F_NODE(nd_body, RNODE_UNLESS, "then clause");
+ LAST_NODE;
+ F_NODE(nd_else, RNODE_UNLESS, "else clause");
+ return;
+
+ case NODE_CASE:
+ ANN("case statement");
+ ANN("format: case [nd_head]; [nd_body]; end");
+ ANN("example: case x; when 1; foo; when 2; bar; else baz; end");
+ F_NODE(nd_head, RNODE_CASE, "case expr");
+ LAST_NODE;
+ F_NODE(nd_body, RNODE_CASE, "when clauses");
+ return;
+ case NODE_CASE2:
+ ANN("case statement with no head");
+ ANN("format: case; [nd_body]; end");
+ ANN("example: case; when 1; foo; when 2; bar; else baz; end");
+ F_NODE(nd_head, RNODE_CASE2, "case expr");
+ LAST_NODE;
+ F_NODE(nd_body, RNODE_CASE2, "when clauses");
+ return;
+ case NODE_CASE3:
+ ANN("case statement (pattern matching)");
+ ANN("format: case [nd_head]; [nd_body]; end");
+ ANN("example: case x; in 1; foo; in 2; bar; else baz; end");
+ F_NODE(nd_head, RNODE_CASE3, "case expr");
+ LAST_NODE;
+ F_NODE(nd_body, RNODE_CASE3, "in clauses");
+ return;
+
+ case NODE_WHEN:
+ ANN("when clause");
+ ANN("format: when [nd_head]; [nd_body]; (when or else) [nd_next]");
+ ANN("example: case x; when 1; foo; when 2; bar; else baz; end");
+ F_NODE(nd_head, RNODE_WHEN, "when value");
+ F_NODE(nd_body, RNODE_WHEN, "when body");
+ LAST_NODE;
+ F_NODE(nd_next, RNODE_WHEN, "next when clause");
+ return;
+
+ case NODE_IN:
+ ANN("in clause");
+ ANN("format: in [nd_head]; [nd_body]; (in or else) [nd_next]");
+ ANN("example: case x; in 1; foo; in 2; bar; else baz; end");
+ F_NODE(nd_head, RNODE_IN, "in pattern");
+ F_NODE(nd_body, RNODE_IN, "in body");
+ LAST_NODE;
+ F_NODE(nd_next, RNODE_IN, "next in clause");
+ return;
+
+ case NODE_WHILE:
+ ANN("while statement");
+ ANN("format: while [nd_cond]; [nd_body]; end");
+ ANN("example: while x == 1; foo; end");
+ goto loop;
+ case NODE_UNTIL:
+ ANN("until statement");
+ ANN("format: until [nd_cond]; [nd_body]; end");
+ ANN("example: until x == 1; foo; end");
+ loop:
+ F_CUSTOM1(nd_state, "begin-end-while?") {
+ A_INT((int)RNODE_WHILE(node)->nd_state);
+ A((RNODE_WHILE(node)->nd_state == 1) ? " (while-end)" : " (begin-end-while)");
+ }
+ F_NODE(nd_cond, RNODE_WHILE, "condition");
+ LAST_NODE;
+ F_NODE(nd_body, RNODE_WHILE, "body");
+ return;
+
+ case NODE_ITER:
+ ANN("method call with block");
+ ANN("format: [nd_iter] { [nd_body] }");
+ ANN("example: 3.times { foo }");
+ goto iter;
+ case NODE_FOR:
+ ANN("for statement");
+ ANN("format: for * in [nd_iter] do [nd_body] end");
+ ANN("example: for i in 1..3 do foo end");
+ iter:
+ F_NODE(nd_iter, RNODE_ITER, "iteration receiver");
+ LAST_NODE;
+ F_NODE(nd_body, RNODE_ITER, "body");
+ return;
+
+ case NODE_FOR_MASGN:
+ ANN("vars of for statement with masgn");
+ ANN("format: for [nd_var] in ... do ... end");
+ ANN("example: for x, y in 1..3 do foo end");
+ LAST_NODE;
+ F_NODE(nd_var, RNODE_FOR_MASGN, "var");
+ return;
+
+ case NODE_BREAK:
+ ANN("break statement");
+ ANN("format: break [nd_stts]");
+ ANN("example: break 1");
+ LAST_NODE;
+ F_NODE(nd_stts, RNODE_BREAK, "value");
+ return;
+ case NODE_NEXT:
+ ANN("next statement");
+ ANN("format: next [nd_stts]");
+ ANN("example: next 1");
+ LAST_NODE;
+ F_NODE(nd_stts, RNODE_NEXT, "value");
+ return;
+ case NODE_RETURN:
+ ANN("return statement");
+ ANN("format: return [nd_stts]");
+ ANN("example: return 1");
+ LAST_NODE;
+ F_NODE(nd_stts, RNODE_RETURN, "value");
+ return;
+
+ case NODE_REDO:
+ ANN("redo statement");
+ ANN("format: redo");
+ ANN("example: redo");
+ return;
+
+ case NODE_RETRY:
+ ANN("retry statement");
+ ANN("format: retry");
+ ANN("example: retry");
+ return;
+
+ case NODE_BEGIN:
+ ANN("begin statement");
+ ANN("format: begin; [nd_body]; end");
+ ANN("example: begin; 1; end");
+ LAST_NODE;
+ F_NODE(nd_body, RNODE_BEGIN, "body");
+ return;
+
+ case NODE_RESCUE:
+ ANN("rescue clause");
+ ANN("format: begin; [nd_body]; (rescue) [nd_resq]; else [nd_else]; end");
+ ANN("example: begin; foo; rescue; bar; else; baz; end");
+ F_NODE(nd_head, RNODE_RESCUE, "body");
+ F_NODE(nd_resq, RNODE_RESCUE, "rescue clause list");
+ LAST_NODE;
+ F_NODE(nd_else, RNODE_RESCUE, "rescue else clause");
+ return;
+
+ case NODE_RESBODY:
+ ANN("rescue clause (cont'd)");
+ ANN("format: rescue [nd_args]; [nd_body]; (rescue) [nd_head]");
+ ANN("example: begin; foo; rescue; bar; else; baz; end");
+ F_NODE(nd_args, RNODE_RESBODY, "rescue exceptions");
+ F_NODE(nd_body, RNODE_RESBODY, "rescue clause");
+ LAST_NODE;
+ F_NODE(nd_next, RNODE_RESBODY, "next rescue clause");
+ return;
+
+ case NODE_ENSURE:
+ ANN("ensure clause");
+ ANN("format: begin; [nd_head]; ensure; [nd_ensr]; end");
+ ANN("example: begin; foo; ensure; bar; end");
+ F_NODE(nd_head, RNODE_ENSURE, "body");
+ LAST_NODE;
+ F_NODE(nd_ensr, RNODE_ENSURE, "ensure clause");
+ return;
+
+ case NODE_AND:
+ ANN("&& operator");
+ ANN("format: [nd_1st] && [nd_2nd]");
+ ANN("example: foo && bar");
+ goto andor;
+ case NODE_OR:
+ ANN("|| operator");
+ ANN("format: [nd_1st] || [nd_2nd]");
+ ANN("example: foo || bar");
+ andor:
+ while (1) {
+ F_NODE(nd_1st, RNODE_AND, "left expr");
+ if (!RNODE_AND(node)->nd_2nd || !nd_type_p(RNODE_AND(node)->nd_2nd, type))
+ break;
+ node = RNODE_AND(node)->nd_2nd;
+ }
+ LAST_NODE;
+ F_NODE(nd_2nd, RNODE_AND, "right expr");
+ return;
+
+ case NODE_MASGN:
+ ANN("multiple assignment");
+ ANN("format: [nd_head], [nd_args] = [nd_value]");
+ ANN("example: a, b = foo");
+ F_NODE(nd_value, RNODE_MASGN, "rhsn");
+ F_NODE(nd_head, RNODE_MASGN, "lhsn");
+ if (NODE_NAMED_REST_P(RNODE_MASGN(node)->nd_args)) {
+ LAST_NODE;
+ F_NODE(nd_args, RNODE_MASGN, "splatn");
+ }
+ else {
+ F_MSG(nd_args, "splatn", "NODE_SPECIAL_NO_NAME_REST (rest argument without name)");
+ }
+ return;
+
+ case NODE_LASGN:
+ ANN("local variable assignment");
+ ANN("format: [nd_vid](lvar) = [nd_value]");
+ ANN("example: x = foo");
+ F_ID(nd_vid, RNODE_LASGN, "local variable");
+ if (NODE_REQUIRED_KEYWORD_P(RNODE_LASGN(node)->nd_value)) {
+ F_MSG(nd_value, "rvalue", "NODE_SPECIAL_REQUIRED_KEYWORD (required keyword argument)");
+ }
+ else {
+ LAST_NODE;
+ F_NODE(nd_value, RNODE_LASGN, "rvalue");
+ }
+ return;
+ case NODE_DASGN:
+ ANN("dynamic variable assignment");
+ ANN("format: [nd_vid](dvar) = [nd_value]");
+ ANN("example: x = nil; 1.times { x = foo }");
+ ANN("example: 1.times { x = foo }");
+ F_ID(nd_vid, RNODE_DASGN, "local variable");
+ if (NODE_REQUIRED_KEYWORD_P(RNODE_DASGN(node)->nd_value)) {
+ F_MSG(nd_value, "rvalue", "NODE_SPECIAL_REQUIRED_KEYWORD (required keyword argument)");
+ }
+ else {
+ LAST_NODE;
+ F_NODE(nd_value, RNODE_DASGN, "rvalue");
+ }
+ return;
+ case NODE_IASGN:
+ ANN("instance variable assignment");
+ ANN("format: [nd_vid](ivar) = [nd_value]");
+ ANN("example: @x = foo");
+ F_ID(nd_vid, RNODE_IASGN, "instance variable");
+ LAST_NODE;
+ F_NODE(nd_value, RNODE_IASGN, "rvalue");
+ return;
+ case NODE_CVASGN:
+ ANN("class variable assignment");
+ ANN("format: [nd_vid](cvar) = [nd_value]");
+ ANN("example: @@x = foo");
+ F_ID(nd_vid, RNODE_CVASGN, "class variable");
+ LAST_NODE;
+ F_NODE(nd_value, RNODE_CVASGN, "rvalue");
+ return;
+ case NODE_GASGN:
+ ANN("global variable assignment");
+ ANN("format: [nd_vid](gvar) = [nd_value]");
+ ANN("example: $x = foo");
+ F_ID(nd_vid, RNODE_GASGN, "global variable");
+ LAST_NODE;
+ F_NODE(nd_value, RNODE_GASGN, "rvalue");
+ return;
+
+ case NODE_CDECL:
+ ANN("constant declaration");
+ ANN("format: [nd_else]::[nd_vid](constant) = [nd_value]");
+ ANN("example: X = foo");
+ if (RNODE_CDECL(node)->nd_vid) {
+ F_ID(nd_vid, RNODE_CDECL, "constant");
+ F_MSG(nd_else, "extension", "not used");
+ }
+ else {
+ F_MSG(nd_vid, "constant", "0 (see extension field)");
+ F_NODE(nd_else, RNODE_CDECL, "extension");
+ }
+ F_SHAREABILITY(shareability, RNODE_CDECL, "shareability");
+ LAST_NODE;
+ F_NODE(nd_value, RNODE_CDECL, "rvalue");
+ return;
+
+ case NODE_OP_ASGN1:
+ ANN("array assignment with operator");
+ ANN("format: [nd_recv] [ [nd_index] ] [nd_mid]= [nd_rvalue]");
+ ANN("example: ary[1] += foo");
+ F_NODE(nd_recv, RNODE_OP_ASGN1, "receiver");
+ F_ID(nd_mid, RNODE_OP_ASGN1, "operator");
+ F_NODE(nd_index, RNODE_OP_ASGN1, "index");
+ LAST_NODE;
+ F_NODE(nd_rvalue, RNODE_OP_ASGN1, "rvalue");
+ return;
+
+ case NODE_OP_ASGN2:
+ ANN("attr assignment with operator");
+ ANN("format: [nd_recv].[nd_vid] [nd_mid]= [nd_value]");
+ ANN("example: struct.field += foo");
+ F_NODE(nd_recv, RNODE_OP_ASGN2, "receiver");
+ F_CUSTOM1(nd_vid, "attr") {
+ if (RNODE_OP_ASGN2(node)->nd_aid) A("? ");
+ A_ID(RNODE_OP_ASGN2(node)->nd_vid);
+ }
+ F_ID(nd_mid, RNODE_OP_ASGN2, "operator");
+ LAST_NODE;
+ F_NODE(nd_value, RNODE_OP_ASGN2, "rvalue");
+ return;
+
+ case NODE_OP_ASGN_AND:
+ ANN("assignment with && operator");
+ ANN("format: [nd_head] &&= [nd_value]");
+ ANN("example: foo &&= bar");
+ goto asgn_andor;
+ case NODE_OP_ASGN_OR:
+ ANN("assignment with || operator");
+ ANN("format: [nd_head] ||= [nd_value]");
+ ANN("example: foo ||= bar");
+ asgn_andor:
+ F_NODE(nd_head, RNODE_OP_ASGN_AND, "variable");
+ LAST_NODE;
+ F_NODE(nd_value, RNODE_OP_ASGN_AND, "rvalue");
+ return;
+
+ case NODE_OP_CDECL:
+ ANN("constant declaration with operator");
+ ANN("format: [nd_head](constant) [nd_aid]= [nd_value]");
+ ANN("example: A::B ||= 1");
+ F_NODE(nd_head, RNODE_OP_CDECL, "constant");
+ F_ID(nd_aid, RNODE_OP_CDECL, "operator");
+ F_SHAREABILITY(shareability, RNODE_OP_CDECL, "shareability");
+ LAST_NODE;
+ F_NODE(nd_value, RNODE_OP_CDECL, "rvalue");
+ return;
+
+ case NODE_CALL:
+ ANN("method invocation");
+ ANN("format: [nd_recv].[nd_mid]([nd_args])");
+ ANN("example: obj.foo(1)");
+ F_ID(nd_mid, RNODE_CALL, "method id");
+ F_NODE(nd_recv, RNODE_CALL, "receiver");
+ LAST_NODE;
+ F_NODE(nd_args, RNODE_CALL, "arguments");
+ return;
+
+ case NODE_OPCALL:
+ ANN("method invocation");
+ ANN("format: [nd_recv] [nd_mid] [nd_args]");
+ ANN("example: foo + bar");
+ F_ID(nd_mid, RNODE_OPCALL, "method id");
+ F_NODE(nd_recv, RNODE_OPCALL, "receiver");
+ LAST_NODE;
+ F_NODE(nd_args, RNODE_OPCALL, "arguments");
+ return;
+
+ case NODE_FCALL:
+ ANN("function call");
+ ANN("format: [nd_mid]([nd_args])");
+ ANN("example: foo(1)");
+ F_ID(nd_mid, RNODE_FCALL, "method id");
+ LAST_NODE;
+ F_NODE(nd_args, RNODE_FCALL, "arguments");
+ return;
+
+ case NODE_VCALL:
+ ANN("function call with no argument");
+ ANN("format: [nd_mid]");
+ ANN("example: foo");
+ F_ID(nd_mid, RNODE_VCALL, "method id");
+ return;
+
+ case NODE_QCALL:
+ ANN("safe method invocation");
+ ANN("format: [nd_recv]&.[nd_mid]([nd_args])");
+ ANN("example: obj&.foo(1)");
+ F_ID(nd_mid, RNODE_QCALL, "method id");
+ F_NODE(nd_recv, RNODE_QCALL, "receiver");
+ LAST_NODE;
+ F_NODE(nd_args, RNODE_QCALL, "arguments");
+ return;
+
+ case NODE_SUPER:
+ ANN("super invocation");
+ ANN("format: super [nd_args]");
+ ANN("example: super 1");
+ LAST_NODE;
+ F_NODE(nd_args, RNODE_SUPER, "arguments");
+ return;
+
+ case NODE_ZSUPER:
+ ANN("super invocation with no argument");
+ ANN("format: super");
+ ANN("example: super");
+ return;
+
+ case NODE_LIST:
+ ANN("list constructor");
+ ANN("format: [ [nd_head], [nd_next].. ] (length: [nd_alen])");
+ ANN("example: [1, 2, 3]");
+ dump_array(buf, indent, comment, node);
+ return;
+
+ case NODE_ZLIST:
+ ANN("empty list constructor");
+ ANN("format: []");
+ ANN("example: []");
+ return;
+
+ case NODE_HASH:
+ if (!RNODE_HASH(node)->nd_brace) {
+ ANN("keyword arguments");
+ ANN("format: [nd_head]");
+ ANN("example: a: 1, b: 2");
+ }
+ else {
+ ANN("hash constructor");
+ ANN("format: { [nd_head] }");
+ ANN("example: { 1 => 2, 3 => 4 }");
+ }
+ F_CUSTOM1(nd_brace, "keyword arguments or hash literal") {
+ switch (RNODE_HASH(node)->nd_brace) {
+ case 0: A("0 (keyword argument)"); break;
+ case 1: A("1 (hash literal)"); break;
+ }
+ }
+ LAST_NODE;
+ F_NODE(nd_head, RNODE_HASH, "contents");
+ return;
+
+ case NODE_YIELD:
+ ANN("yield invocation");
+ ANN("format: yield [nd_head]");
+ ANN("example: yield 1");
+ LAST_NODE;
+ F_NODE(nd_head, RNODE_YIELD, "arguments");
+ return;
+
+ case NODE_LVAR:
+ ANN("local variable reference");
+ ANN("format: [nd_vid](lvar)");
+ ANN("example: x");
+ F_ID(nd_vid, RNODE_LVAR, "local variable");
+ return;
+ case NODE_DVAR:
+ ANN("dynamic variable reference");
+ ANN("format: [nd_vid](dvar)");
+ ANN("example: 1.times { x = 1; x }");
+ F_ID(nd_vid, RNODE_DVAR, "local variable");
+ return;
+ case NODE_IVAR:
+ ANN("instance variable reference");
+ ANN("format: [nd_vid](ivar)");
+ ANN("example: @x");
+ F_ID(nd_vid, RNODE_IVAR, "instance variable");
+ return;
+ case NODE_CONST:
+ ANN("constant reference");
+ ANN("format: [nd_vid](constant)");
+ ANN("example: X");
+ F_ID(nd_vid, RNODE_CONST, "constant");
+ return;
+ case NODE_CVAR:
+ ANN("class variable reference");
+ ANN("format: [nd_vid](cvar)");
+ ANN("example: @@x");
+ F_ID(nd_vid, RNODE_CVAR, "class variable");
+ return;
+
+ case NODE_GVAR:
+ ANN("global variable reference");
+ ANN("format: [nd_vid](gvar)");
+ ANN("example: $x");
+ F_ID(nd_vid, RNODE_GVAR, "global variable");
+ return;
+
+ case NODE_NTH_REF:
+ ANN("nth special variable reference");
+ ANN("format: $[nd_nth]");
+ ANN("example: $1, $2, ..");
+ F_CUSTOM1(nd_nth, "variable") { A("$"); A_LONG(RNODE_NTH_REF(node)->nd_nth); }
+ return;
+
+ case NODE_BACK_REF:
+ ANN("back special variable reference");
+ ANN("format: $[nd_nth]");
+ ANN("example: $&, $`, $', $+");
+ F_CUSTOM1(nd_nth, "variable") {
+ char name[3] = "$ ";
+ name[1] = (char)RNODE_BACK_REF(node)->nd_nth;
+ A(name);
+ }
+ return;
+
+ case NODE_MATCH:
+ ANN("match expression (against $_ implicitly)");
+ ANN("format: [nd_lit] (in condition)");
+ ANN("example: if /foo/; foo; end");
+ LAST_NODE;
+ F_VALUE(string, rb_node_regx_string_val(node), "string");
+ return;
+
+ case NODE_MATCH2:
+ ANN("match expression (regexp first)");
+ ANN("format: [nd_recv] =~ [nd_value]");
+ ANN("example: /foo/ =~ 'foo'");
+ F_NODE(nd_recv, RNODE_MATCH2, "regexp (receiver)");
+ if (!RNODE_MATCH2(node)->nd_args) LAST_NODE;
+ F_NODE(nd_value, RNODE_MATCH2, "string (argument)");
+ if (RNODE_MATCH2(node)->nd_args) {
+ LAST_NODE;
+ F_NODE(nd_args, RNODE_MATCH2, "named captures");
+ }
+ return;
+
+ case NODE_MATCH3:
+ ANN("match expression (regexp second)");
+ ANN("format: [nd_recv] =~ [nd_value]");
+ ANN("example: 'foo' =~ /foo/");
+ F_NODE(nd_recv, RNODE_MATCH3, "string (receiver)");
+ LAST_NODE;
+ F_NODE(nd_value, RNODE_MATCH3, "regexp (argument)");
+ return;
+
+ case NODE_STR:
+ ANN("string literal");
+ ANN("format: [nd_lit]");
+ ANN("example: 'foo'");
+ goto str;
+ case NODE_XSTR:
+ ANN("xstring literal");
+ ANN("format: [nd_lit]");
+ ANN("example: `foo`");
+ str:
+ F_VALUE(string, rb_node_str_string_val(node), "literal");
+ return;
+
+ case NODE_INTEGER:
+ ANN("integer literal");
+ ANN("format: [val]");
+ ANN("example: 1");
+ F_VALUE(val, rb_node_integer_literal_val(node), "val");
+ return;
+
+ case NODE_FLOAT:
+ ANN("float literal");
+ ANN("format: [val]");
+ ANN("example: 1.2");
+ F_VALUE(val, rb_node_float_literal_val(node), "val");
+ return;
+
+ case NODE_RATIONAL:
+ ANN("rational number literal");
+ ANN("format: [val]");
+ ANN("example: 1r");
+ F_VALUE(val, rb_node_rational_literal_val(node), "val");
+ return;
+
+ case NODE_IMAGINARY:
+ ANN("complex number literal");
+ ANN("format: [val]");
+ ANN("example: 1i");
+ F_VALUE(val, rb_node_imaginary_literal_val(node), "val");
+ return;
+
+ case NODE_REGX:
+ ANN("regexp literal");
+ ANN("format: [string]");
+ ANN("example: /foo/");
+ LAST_NODE;
+ F_VALUE(string, rb_node_regx_string_val(node), "string");
+ return;
+
+ case NODE_ONCE:
+ ANN("once evaluation");
+ ANN("format: [nd_body]");
+ ANN("example: /foo#{ bar }baz/o");
+ LAST_NODE;
+ F_NODE(nd_body, RNODE_ONCE, "body");
+ return;
+
+ case NODE_DSTR:
+ ANN("string literal with interpolation");
+ ANN("format: [nd_lit]");
+ ANN("example: \"foo#{ bar }baz\"");
+ goto dlit;
+ case NODE_DXSTR:
+ ANN("xstring literal with interpolation");
+ ANN("format: [nd_lit]");
+ ANN("example: `foo#{ bar }baz`");
+ goto dlit;
+ case NODE_DREGX:
+ ANN("regexp literal with interpolation");
+ ANN("format: [nd_lit]");
+ ANN("example: /foo#{ bar }baz/");
+ goto dlit;
+ case NODE_DSYM:
+ ANN("symbol literal with interpolation");
+ ANN("format: [nd_lit]");
+ ANN("example: :\"foo#{ bar }baz\"");
+ dlit:
+ F_VALUE(string, rb_node_dstr_string_val(node), "preceding string");
+ if (!RNODE_DSTR(node)->nd_next) return;
+ F_NODE(nd_next->nd_head, RNODE_DSTR, "interpolation");
+ LAST_NODE;
+ F_NODE(nd_next->nd_next, RNODE_DSTR, "tailing strings");
+ return;
+
+ case NODE_SYM:
+ ANN("symbol literal");
+ ANN("format: [string]");
+ ANN("example: :foo");
+ F_VALUE(string, rb_node_sym_string_val(node), "string");
+ return;
+
+ case NODE_EVSTR:
+ ANN("interpolation expression");
+ ANN("format: \"..#{ [nd_body] }..\"");
+ ANN("example: \"foo#{ bar }baz\"");
+ LAST_NODE;
+ F_NODE(nd_body, RNODE_EVSTR, "body");
+ return;
+
+ case NODE_ARGSCAT:
+ ANN("splat argument following arguments");
+ ANN("format: ..(*[nd_head], [nd_body..])");
+ ANN("example: foo(*ary, post_arg1, post_arg2)");
+ F_NODE(nd_head, RNODE_ARGSCAT, "preceding array");
+ LAST_NODE;
+ F_NODE(nd_body, RNODE_ARGSCAT, "following array");
+ return;
+
+ case NODE_ARGSPUSH:
+ ANN("splat argument following one argument");
+ ANN("format: ..(*[nd_head], [nd_body])");
+ ANN("example: foo(*ary, post_arg)");
+ F_NODE(nd_head, RNODE_ARGSPUSH, "preceding array");
+ LAST_NODE;
+ F_NODE(nd_body, RNODE_ARGSPUSH, "following element");
+ return;
+
+ case NODE_SPLAT:
+ ANN("splat argument");
+ ANN("format: *[nd_head]");
+ ANN("example: foo(*ary)");
+ LAST_NODE;
+ F_NODE(nd_head, RNODE_SPLAT, "splat'ed array");
+ return;
+
+ case NODE_BLOCK_PASS:
+ ANN("arguments with block argument");
+ ANN("format: ..([nd_head], &[nd_body])");
+ ANN("example: foo(x, &blk)");
+ F_NODE(nd_head, RNODE_BLOCK_PASS, "other arguments");
+ LAST_NODE;
+ F_NODE(nd_body, RNODE_BLOCK_PASS, "block argument");
+ return;
+
+ case NODE_DEFN:
+ ANN("method definition");
+ ANN("format: def [nd_mid] [nd_defn]; end");
+ ANN("example: def foo; bar; end");
+ F_ID(nd_mid, RNODE_DEFN, "method name");
+ LAST_NODE;
+ F_NODE(nd_defn, RNODE_DEFN, "method definition");
+ return;
+
+ case NODE_DEFS:
+ ANN("singleton method definition");
+ ANN("format: def [nd_recv].[nd_mid] [nd_defn]; end");
+ ANN("example: def obj.foo; bar; end");
+ F_NODE(nd_recv, RNODE_DEFS, "receiver");
+ F_ID(nd_mid, RNODE_DEFS, "method name");
+ LAST_NODE;
+ F_NODE(nd_defn, RNODE_DEFS, "method definition");
+ return;
+
+ case NODE_ALIAS:
+ ANN("method alias statement");
+ ANN("format: alias [nd_1st] [nd_2nd]");
+ ANN("example: alias bar foo");
+ F_NODE(nd_1st, RNODE_ALIAS, "new name");
+ LAST_NODE;
+ F_NODE(nd_2nd, RNODE_ALIAS, "old name");
+ return;
+
+ case NODE_VALIAS:
+ ANN("global variable alias statement");
+ ANN("format: alias [nd_alias](gvar) [nd_orig](gvar)");
+ ANN("example: alias $y $x");
+ F_ID(nd_alias, RNODE_VALIAS, "new name");
+ F_ID(nd_orig, RNODE_VALIAS, "old name");
+ return;
+
+ case NODE_UNDEF:
+ ANN("method undef statement");
+ ANN("format: undef [nd_undef]");
+ ANN("example: undef foo");
+ LAST_NODE;
+ F_NODE(nd_undef, RNODE_UNDEF, "old name");
+ return;
+
+ case NODE_CLASS:
+ ANN("class definition");
+ ANN("format: class [nd_cpath] < [nd_super]; [nd_body]; end");
+ ANN("example: class C2 < C; ..; end");
+ F_NODE(nd_cpath, RNODE_CLASS, "class path");
+ F_NODE(nd_super, RNODE_CLASS, "superclass");
+ LAST_NODE;
+ F_NODE(nd_body, RNODE_CLASS, "class definition");
+ return;
+
+ case NODE_MODULE:
+ ANN("module definition");
+ ANN("format: module [nd_cpath]; [nd_body]; end");
+ ANN("example: module M; ..; end");
+ F_NODE(nd_cpath, RNODE_MODULE, "module path");
+ LAST_NODE;
+ F_NODE(nd_body, RNODE_MODULE, "module definition");
+ return;
+
+ case NODE_SCLASS:
+ ANN("singleton class definition");
+ ANN("format: class << [nd_recv]; [nd_body]; end");
+ ANN("example: class << obj; ..; end");
+ F_NODE(nd_recv, RNODE_SCLASS, "receiver");
+ LAST_NODE;
+ F_NODE(nd_body, RNODE_SCLASS, "singleton class definition");
+ return;
+
+ case NODE_COLON2:
+ ANN("scoped constant reference");
+ ANN("format: [nd_head]::[nd_mid]");
+ ANN("example: M::C");
+ F_ID(nd_mid, RNODE_COLON2, "constant name");
+ LAST_NODE;
+ F_NODE(nd_head, RNODE_COLON2, "receiver");
+ return;
+
+ case NODE_COLON3:
+ ANN("top-level constant reference");
+ ANN("format: ::[nd_mid]");
+ ANN("example: ::Object");
+ F_ID(nd_mid, RNODE_COLON3, "constant name");
+ return;
+
+ case NODE_DOT2:
+ ANN("range constructor (incl.)");
+ ANN("format: [nd_beg]..[nd_end]");
+ ANN("example: 1..5");
+ goto dot;
+ case NODE_DOT3:
+ ANN("range constructor (excl.)");
+ ANN("format: [nd_beg]...[nd_end]");
+ ANN("example: 1...5");
+ goto dot;
+ case NODE_FLIP2:
+ ANN("flip-flop condition (incl.)");
+ ANN("format: [nd_beg]..[nd_end]");
+ ANN("example: if (x==1)..(x==5); foo; end");
+ goto dot;
+ case NODE_FLIP3:
+ ANN("flip-flop condition (excl.)");
+ ANN("format: [nd_beg]...[nd_end]");
+ ANN("example: if (x==1)...(x==5); foo; end");
+ dot:
+ F_NODE(nd_beg, RNODE_DOT2, "begin");
+ LAST_NODE;
+ F_NODE(nd_end, RNODE_DOT2, "end");
+ return;
+
+ case NODE_SELF:
+ ANN("self");
+ ANN("format: self");
+ ANN("example: self");
+ F_CUSTOM1(nd_state, "nd_state") {
+ A_INT((int)RNODE_SELF(node)->nd_state);
+ }
+ return;
+
+ case NODE_NIL:
+ ANN("nil");
+ ANN("format: nil");
+ ANN("example: nil");
+ return;
+
+ case NODE_TRUE:
+ ANN("true");
+ ANN("format: true");
+ ANN("example: true");
+ return;
+
+ case NODE_FALSE:
+ ANN("false");
+ ANN("format: false");
+ ANN("example: false");
+ return;
+
+ case NODE_ERRINFO:
+ ANN("virtual reference to $!");
+ ANN("format: rescue => id");
+ ANN("example: rescue => id");
+ return;
+
+ case NODE_DEFINED:
+ ANN("defined? expression");
+ ANN("format: defined?([nd_head])");
+ ANN("example: defined?(foo)");
+ F_NODE(nd_head, RNODE_DEFINED, "expr");
+ return;
+
+ case NODE_POSTEXE:
+ ANN("post-execution");
+ ANN("format: END { [nd_body] }");
+ ANN("example: END { foo }");
+ LAST_NODE;
+ F_NODE(nd_body, RNODE_POSTEXE, "END clause");
+ return;
+
+ case NODE_ATTRASGN:
+ ANN("attr assignment");
+ ANN("format: [nd_recv].[nd_mid] = [nd_args]");
+ ANN("example: struct.field = foo");
+ F_NODE(nd_recv, RNODE_ATTRASGN, "receiver");
+ F_ID(nd_mid, RNODE_ATTRASGN, "method name");
+ LAST_NODE;
+ F_NODE(nd_args, RNODE_ATTRASGN, "arguments");
+ return;
+
+ case NODE_LAMBDA:
+ ANN("lambda expression");
+ ANN("format: -> [nd_body]");
+ ANN("example: -> { foo }");
+ LAST_NODE;
+ F_NODE(nd_body, RNODE_LAMBDA, "lambda clause");
+ return;
+
+ case NODE_OPT_ARG:
+ ANN("optional arguments");
+ ANN("format: def method_name([nd_body=some], [nd_next..])");
+ ANN("example: def foo(a, b=1, c); end");
+ F_NODE(nd_body, RNODE_OPT_ARG, "body");
+ LAST_NODE;
+ F_NODE(nd_next, RNODE_OPT_ARG, "next");
+ return;
+
+ case NODE_KW_ARG:
+ ANN("keyword arguments");
+ ANN("format: def method_name([nd_body=some], [nd_next..])");
+ ANN("example: def foo(a:1, b:2); end");
+ F_NODE(nd_body, RNODE_KW_ARG, "body");
+ LAST_NODE;
+ F_NODE(nd_next, RNODE_KW_ARG, "next");
+ return;
+
+ case NODE_POSTARG:
+ ANN("post arguments");
+ ANN("format: *[nd_1st], [nd_2nd..] = ..");
+ ANN("example: a, *rest, z = foo");
+ if (NODE_NAMED_REST_P(RNODE_POSTARG(node)->nd_1st)) {
+ F_NODE(nd_1st, RNODE_POSTARG, "rest argument");
+ }
+ else {
+ F_MSG(nd_1st, "rest argument", "NODE_SPECIAL_NO_NAME_REST (rest argument without name)");
+ }
+ LAST_NODE;
+ F_NODE(nd_2nd, RNODE_POSTARG, "post arguments");
+ return;
+
+ case NODE_ARGS:
+ ANN("method parameters");
+ ANN("format: def method_name(.., [nd_ainfo.nd_optargs], *[nd_ainfo.rest_arg], [nd_ainfo.first_post_arg], .., [nd_ainfo.kw_args], **[nd_ainfo.kw_rest_arg], &[nd_ainfo.block_arg])");
+ ANN("example: def foo(a, b, opt1=1, opt2=2, *rest, y, z, kw: 1, **kwrest, &blk); end");
+ F_INT(nd_ainfo.pre_args_num, RNODE_ARGS, "count of mandatory (pre-)arguments");
+ F_NODE(nd_ainfo.pre_init, RNODE_ARGS, "initialization of (pre-)arguments");
+ F_INT(nd_ainfo.post_args_num, RNODE_ARGS, "count of mandatory post-arguments");
+ F_NODE(nd_ainfo.post_init, RNODE_ARGS, "initialization of post-arguments");
+ F_ID(nd_ainfo.first_post_arg, RNODE_ARGS, "first post argument");
+ F_CUSTOM1(nd_ainfo.rest_arg, "rest argument") {
+ if (RNODE_ARGS(node)->nd_ainfo.rest_arg == NODE_SPECIAL_EXCESSIVE_COMMA) {
+ A("1 (excessed comma)");
+ }
+ else {
+ A_ID(RNODE_ARGS(node)->nd_ainfo.rest_arg);
+ }
+ }
+ F_ID(nd_ainfo.block_arg, RNODE_ARGS, "block argument");
+ F_NODE(nd_ainfo.opt_args, RNODE_ARGS, "optional arguments");
+ F_NODE(nd_ainfo.kw_args, RNODE_ARGS, "keyword arguments");
+ LAST_NODE;
+ F_NODE(nd_ainfo.kw_rest_arg, RNODE_ARGS, "keyword rest argument");
+ return;
+
+ case NODE_SCOPE:
+ ANN("new scope");
+ ANN("format: [nd_tbl]: local table, [nd_args]: arguments, [nd_body]: body");
+ F_CUSTOM1(nd_tbl, "local table") {
+ rb_ast_id_table_t *tbl = RNODE_SCOPE(node)->nd_tbl;
+ int i;
+ int size = tbl ? tbl->size : 0;
+ if (size == 0) A("(empty)");
+ for (i = 0; i < size; i++) {
+ A_ID(tbl->ids[i]); if (i < size - 1) A(",");
+ }
+ }
+ F_NODE(nd_args, RNODE_SCOPE, "arguments");
+ LAST_NODE;
+ F_NODE(nd_body, RNODE_SCOPE, "body");
+ return;
+
+ case NODE_ARYPTN:
+ ANN("array pattern");
+ ANN("format: [nd_pconst]([pre_args], ..., *[rest_arg], [post_args], ...)");
+ F_NODE(nd_pconst, RNODE_ARYPTN, "constant");
+ F_NODE(pre_args, RNODE_ARYPTN, "pre arguments");
+ if (NODE_NAMED_REST_P(RNODE_ARYPTN(node)->rest_arg)) {
+ F_NODE(rest_arg, RNODE_ARYPTN, "rest argument");
+ }
+ else {
+ F_MSG(rest_arg, "rest argument", "NODE_SPECIAL_NO_NAME_REST (rest argument without name)");
+ }
+ LAST_NODE;
+ F_NODE(post_args, RNODE_ARYPTN, "post arguments");
+ return;
+
+ case NODE_FNDPTN:
+ ANN("find pattern");
+ ANN("format: [nd_pconst](*[pre_rest_arg], args, ..., *[post_rest_arg])");
+ F_NODE(nd_pconst, RNODE_FNDPTN, "constant");
+ if (NODE_NAMED_REST_P(RNODE_FNDPTN(node)->pre_rest_arg)) {
+ F_NODE(pre_rest_arg, RNODE_FNDPTN, "pre rest argument");
+ }
+ else {
+ F_MSG(pre_rest_arg, "pre rest argument", "NODE_SPECIAL_NO_NAME_REST (rest argument without name)");
+ }
+ F_NODE(args, RNODE_FNDPTN, "arguments");
+
+ LAST_NODE;
+ if (NODE_NAMED_REST_P(RNODE_FNDPTN(node)->post_rest_arg)) {
+ F_NODE(post_rest_arg, RNODE_FNDPTN, "post rest argument");
+ }
+ else {
+ F_MSG(post_rest_arg, "post rest argument", "NODE_SPECIAL_NO_NAME_REST (rest argument without name)");
+ }
+ return;
+
+ case NODE_HSHPTN:
+ ANN("hash pattern");
+ ANN("format: [nd_pconst]([nd_pkwargs], ..., **[nd_pkwrestarg])");
+ F_NODE(nd_pconst, RNODE_HSHPTN, "constant");
+ F_NODE(nd_pkwargs, RNODE_HSHPTN, "keyword arguments");
+ LAST_NODE;
+ if (RNODE_HSHPTN(node)->nd_pkwrestarg == NODE_SPECIAL_NO_REST_KEYWORD) {
+ F_MSG(nd_pkwrestarg, "keyword rest argument", "NODE_SPECIAL_NO_REST_KEYWORD (**nil)");
+ }
+ else {
+ F_NODE(nd_pkwrestarg, RNODE_HSHPTN, "keyword rest argument");
+ }
+ return;
+
+ case NODE_LINE:
+ ANN("line");
+ ANN("format: [lineno]");
+ ANN("example: __LINE__");
+ return;
+
+ case NODE_FILE:
+ ANN("line");
+ ANN("format: [path]");
+ ANN("example: __FILE__");
+ F_VALUE(path, rb_node_file_path_val(node), "path");
+ return;
+
+ case NODE_ENCODING:
+ ANN("encoding");
+ ANN("format: [enc]");
+ ANN("example: __ENCODING__");
+ F_VALUE(enc, rb_node_encoding_val(node), "enc");
+ return;
+
+ case NODE_ERROR:
+ ANN("Broken input recovered by Error Tolerant mode");
+ return;
+
+ case NODE_ARGS_AUX:
+ case NODE_LAST:
+ break;
+ }
+
+ rb_bug("dump_node: unknown node: %s", ruby_node_name(nd_type(node)));
+}
+
+VALUE
+rb_parser_dump_tree(const NODE *node, int comment)
+{
+ VALUE buf = rb_str_new_cstr(
+ "###########################################################\n"
+ "## Do NOT use this node dump for any purpose other than ##\n"
+ "## debug and research. Compatibility is not guaranteed. ##\n"
+ "###########################################################\n\n"
+ );
+ dump_node(buf, rb_str_new_cstr("# "), comment, node);
+ return buf;
+}
diff --git a/numeric.c b/numeric.c
index 8cad7ff670..4db0834ae3 100644
--- a/numeric.c
+++ b/numeric.c
@@ -671,7 +671,7 @@ num_div(VALUE x, VALUE y)
* Of the Core and Standard Library classes,
* only Rational uses this implementation.
*
- * For \Rational +r+ and real number +n+, these expressions are equivalent:
+ * For Rational +r+ and real number +n+, these expressions are equivalent:
*
* r % n
* r-n*(r/n).floor
@@ -836,7 +836,7 @@ int_zero_p(VALUE num)
if (FIXNUM_P(num)) {
return FIXNUM_ZERO_P(num);
}
- assert(RB_BIGNUM_TYPE_P(num));
+ RUBY_ASSERT(RB_BIGNUM_TYPE_P(num));
return rb_bigzero_p(num);
}
@@ -862,6 +862,8 @@ rb_int_zero_p(VALUE num)
* Of the Core and Standard Library classes,
* Integer, Float, Rational, and Complex use this implementation.
*
+ * Related: #zero?
+ *
*/
static VALUE
@@ -948,7 +950,7 @@ num_negative_p(VALUE num)
* So you should know its esoteric system. See following:
*
* - https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
- * - https://github.com/rdp/ruby_tutorials_core/wiki/Ruby-Talk-FAQ#floats_imprecise
+ * - https://github.com/rdp/ruby_tutorials_core/wiki/Ruby-Talk-FAQ#-why-are-rubys-floats-imprecise
* - https://en.wikipedia.org/wiki/Floating_point#Accuracy_problems
*
* You can create a \Float object explicitly with:
@@ -963,7 +965,10 @@ num_negative_p(VALUE num)
*
* First, what's elsewhere. \Class \Float:
*
- * - Inherits from {class Numeric}[rdoc-ref:Numeric@What-27s+Here].
+ * - Inherits from
+ * {class Numeric}[rdoc-ref:Numeric@What-27s+Here]
+ * and {class Object}[rdoc-ref:Object@What-27s+Here].
+ * - Includes {module Comparable}[rdoc-ref:Comparable@What-27s+Here].
*
* Here, class \Float provides methods for:
*
@@ -999,10 +1004,10 @@ num_negative_p(VALUE num)
* - #/: Returns the quotient of +self+ and the given value.
* - #ceil: Returns the smallest number greater than or equal to +self+.
* - #coerce: Returns a 2-element array containing the given value converted to a \Float
- and +self+
+ * and +self+
* - #divmod: Returns a 2-element array containing the quotient and remainder
* results of dividing +self+ by the given value.
- * - #fdiv: Returns the Float result of dividing +self+ by the given value.
+ * - #fdiv: Returns the \Float result of dividing +self+ by the given value.
* - #floor: Returns the greatest number smaller than or equal to +self+.
* - #next_float: Returns the next-larger representable \Float.
* - #prev_float: Returns the next-smaller representable \Float.
@@ -1078,7 +1083,7 @@ flo_to_s(VALUE flt)
s = sign ? rb_usascii_str_new_cstr("-") : rb_usascii_str_new(0, 0);
if ((digs = (int)(e - p)) >= (int)sizeof(buf)) digs = (int)sizeof(buf) - 1;
memcpy(buf, p, digs);
- xfree(p);
+ free(p);
if (decpt > 0) {
if (decpt < digs) {
memmove(buf + decpt + 1, buf + decpt, digs - decpt);
@@ -1550,8 +1555,8 @@ rb_float_pow(VALUE x, VALUE y)
* 1.eql?(Rational(1, 1)) # => false
* 1.eql?(Complex(1, 0)) # => false
*
- * \Method +eql?+ is different from +==+ in that +eql?+ requires matching types,
- * while +==+ does not.
+ * \Method +eql?+ is different from <tt>==</tt> in that +eql?+ requires matching types,
+ * while <tt>==</tt> does not.
*
*/
@@ -1683,12 +1688,12 @@ rb_dbl_cmp(double a, double b)
* Examples:
*
* 2.0 <=> 2 # => 0
- 2.0 <=> 2.0 # => 0
- 2.0 <=> Rational(2, 1) # => 0
- 2.0 <=> Complex(2, 0) # => 0
- 2.0 <=> 1.9 # => 1
- 2.0 <=> 2.1 # => -1
- 2.0 <=> 'foo' # => nil
+ * 2.0 <=> 2.0 # => 0
+ * 2.0 <=> Rational(2, 1) # => 0
+ * 2.0 <=> Complex(2, 0) # => 0
+ * 2.0 <=> 1.9 # => 1
+ * 2.0 <=> 2.1 # => -1
+ * 2.0 <=> 'foo' # => nil
*
* This is the basis for the tests in the Comparable module.
*
@@ -2335,7 +2340,7 @@ int_half_p_half_down(VALUE num, VALUE n, VALUE f)
}
/*
- * Assumes num is an Integer, ndigits <= 0
+ * Assumes num is an \Integer, ndigits <= 0
*/
static VALUE
rb_int_round(VALUE num, int ndigits, enum ruby_num_rounding_mode mode)
@@ -2449,7 +2454,7 @@ rb_int_truncate(VALUE num, int ndigits)
/*
* call-seq:
- * round(ndigits = 0, half: :up]) -> integer or float
+ * round(ndigits = 0, half: :up) -> integer or float
*
* Returns +self+ rounded to the nearest value with
* a precision of +ndigits+ decimal digits.
@@ -2834,7 +2839,7 @@ ruby_num_interval_step_size(VALUE from, VALUE to, VALUE step, int excl)
}
if (RTEST(rb_funcall(from, cmp, 1, to))) return INT2FIX(0);
result = rb_funcall(rb_funcall(to, '-', 1, from), id_div, 1, step);
- if (!excl || RTEST(rb_funcall(rb_funcall(from, '+', 1, rb_funcall(result, '*', 1, step)), cmp, 1, to))) {
+ if (!excl || RTEST(rb_funcall(to, cmp, 1, rb_funcall(from, '+', 1, rb_funcall(result, '*', 1, step))))) {
result = rb_funcall(result, '+', 1, INT2FIX(1));
}
return result;
@@ -2946,88 +2951,88 @@ num_step_size(VALUE from, VALUE args, VALUE eobj)
* step(by: , to: nil) {|n| ... } -> self
* step(by: , to: nil) -> enumerator
*
- * Generates a sequence of numbers; with a block given, traverses the sequence.
+ * Generates a sequence of numbers; with a block given, traverses the sequence.
*
- * Of the Core and Standard Library classes,
- * Integer, Float, and Rational use this implementation.
- *
- * A quick example:
- *
- * squares = []
- * 1.step(by: 2, to: 10) {|i| squares.push(i*i) }
- * squares # => [1, 9, 25, 49, 81]
- *
- * The generated sequence:
- *
- * - Begins with +self+.
- * - Continues at intervals of +step+ (which may not be zero).
- * - Ends with the last number that is within or equal to +limit+;
- * that is, less than or equal to +limit+ if +step+ is positive,
- * greater than or equal to +limit+ if +step+ is negative.
- * If +limit+ is not given, the sequence is of infinite length.
- *
- * If a block is given, calls the block with each number in the sequence;
- * returns +self+. If no block is given, returns an Enumerator::ArithmeticSequence.
- *
- * <b>Keyword Arguments</b>
- *
- * With keyword arguments +by+ and +to+,
- * their values (or defaults) determine the step and limit:
- *
- * # Both keywords given.
- * squares = []
- * 4.step(by: 2, to: 10) {|i| squares.push(i*i) } # => 4
- * squares # => [16, 36, 64, 100]
- * cubes = []
- * 3.step(by: -1.5, to: -3) {|i| cubes.push(i*i*i) } # => 3
- * cubes # => [27.0, 3.375, 0.0, -3.375, -27.0]
- * squares = []
- * 1.2.step(by: 0.2, to: 2.0) {|f| squares.push(f*f) }
- * squares # => [1.44, 1.9599999999999997, 2.5600000000000005, 3.24, 4.0]
- *
- * squares = []
- * Rational(6/5).step(by: 0.2, to: 2.0) {|r| squares.push(r*r) }
- * squares # => [1.0, 1.44, 1.9599999999999997, 2.5600000000000005, 3.24, 4.0]
- *
- * # Only keyword to given.
- * squares = []
- * 4.step(to: 10) {|i| squares.push(i*i) } # => 4
- * squares # => [16, 25, 36, 49, 64, 81, 100]
- * # Only by given.
- *
- * # Only keyword by given
- * squares = []
- * 4.step(by:2) {|i| squares.push(i*i); break if i > 10 }
- * squares # => [16, 36, 64, 100, 144]
- *
- * # No block given.
- * e = 3.step(by: -1.5, to: -3) # => (3.step(by: -1.5, to: -3))
- * e.class # => Enumerator::ArithmeticSequence
- *
- * <b>Positional Arguments</b>
- *
- * With optional positional arguments +limit+ and +step+,
- * their values (or defaults) determine the step and limit:
- *
- * squares = []
- * 4.step(10, 2) {|i| squares.push(i*i) } # => 4
- * squares # => [16, 36, 64, 100]
- * squares = []
- * 4.step(10) {|i| squares.push(i*i) }
- * squares # => [16, 25, 36, 49, 64, 81, 100]
- * squares = []
- * 4.step {|i| squares.push(i*i); break if i > 10 } # => nil
- * squares # => [16, 25, 36, 49, 64, 81, 100, 121]
+ * Of the Core and Standard Library classes,
+ * Integer, Float, and Rational use this implementation.
+ *
+ * A quick example:
+ *
+ * squares = []
+ * 1.step(by: 2, to: 10) {|i| squares.push(i*i) }
+ * squares # => [1, 9, 25, 49, 81]
+ *
+ * The generated sequence:
+ *
+ * - Begins with +self+.
+ * - Continues at intervals of +by+ (which may not be zero).
+ * - Ends with the last number that is within or equal to +to+;
+ * that is, less than or equal to +to+ if +by+ is positive,
+ * greater than or equal to +to+ if +by+ is negative.
+ * If +to+ is +nil+, the sequence is of infinite length.
+ *
+ * If a block is given, calls the block with each number in the sequence;
+ * returns +self+. If no block is given, returns an Enumerator::ArithmeticSequence.
+ *
+ * <b>Keyword Arguments</b>
+ *
+ * With keyword arguments +by+ and +to+,
+ * their values (or defaults) determine the step and limit:
+ *
+ * # Both keywords given.
+ * squares = []
+ * 4.step(by: 2, to: 10) {|i| squares.push(i*i) } # => 4
+ * squares # => [16, 36, 64, 100]
+ * cubes = []
+ * 3.step(by: -1.5, to: -3) {|i| cubes.push(i*i*i) } # => 3
+ * cubes # => [27.0, 3.375, 0.0, -3.375, -27.0]
+ * squares = []
+ * 1.2.step(by: 0.2, to: 2.0) {|f| squares.push(f*f) }
+ * squares # => [1.44, 1.9599999999999997, 2.5600000000000005, 3.24, 4.0]
+ *
+ * squares = []
+ * Rational(6/5).step(by: 0.2, to: 2.0) {|r| squares.push(r*r) }
+ * squares # => [1.0, 1.44, 1.9599999999999997, 2.5600000000000005, 3.24, 4.0]
+ *
+ * # Only keyword to given.
+ * squares = []
+ * 4.step(to: 10) {|i| squares.push(i*i) } # => 4
+ * squares # => [16, 25, 36, 49, 64, 81, 100]
+ * # Only by given.
+ *
+ * # Only keyword by given
+ * squares = []
+ * 4.step(by:2) {|i| squares.push(i*i); break if i > 10 }
+ * squares # => [16, 36, 64, 100, 144]
+ *
+ * # No block given.
+ * e = 3.step(by: -1.5, to: -3) # => (3.step(by: -1.5, to: -3))
+ * e.class # => Enumerator::ArithmeticSequence
+ *
+ * <b>Positional Arguments</b>
+ *
+ * With optional positional arguments +to+ and +by+,
+ * their values (or defaults) determine the step and limit:
+ *
+ * squares = []
+ * 4.step(10, 2) {|i| squares.push(i*i) } # => 4
+ * squares # => [16, 36, 64, 100]
+ * squares = []
+ * 4.step(10) {|i| squares.push(i*i) }
+ * squares # => [16, 25, 36, 49, 64, 81, 100]
+ * squares = []
+ * 4.step {|i| squares.push(i*i); break if i > 10 } # => nil
+ * squares # => [16, 25, 36, 49, 64, 81, 100, 121]
*
* <b>Implementation Notes</b>
*
- * If all the arguments are integers, the loop operates using an integer
- * counter.
+ * If all the arguments are integers, the loop operates using an integer
+ * counter.
*
- * If any of the arguments are floating point numbers, all are converted
- * to floats, and the loop is executed
- * <i>floor(n + n*Float::EPSILON) + 1</i> times,
- * where <i>n = (limit - self)/step</i>.
+ * If any of the arguments are floating point numbers, all are converted
+ * to floats, and the loop is executed
+ * <i>floor(n + n*Float::EPSILON) + 1</i> times,
+ * where <i>n = (limit - self)/step</i>.
*
*/
@@ -3210,7 +3215,7 @@ rb_num2ulong(VALUE val)
void
rb_out_of_int(SIGNED_VALUE num)
{
- rb_raise(rb_eRangeError, "integer %"PRIdVALUE " too %s to convert to `int'",
+ rb_raise(rb_eRangeError, "integer %"PRIdVALUE " too %s to convert to 'int'",
num, num < 0 ? "small" : "big");
}
@@ -3229,12 +3234,12 @@ check_uint(unsigned long num, int sign)
if (sign) {
/* minus */
if (num < (unsigned long)INT_MIN)
- rb_raise(rb_eRangeError, "integer %ld too small to convert to `unsigned int'", (long)num);
+ rb_raise(rb_eRangeError, "integer %ld too small to convert to 'unsigned int'", (long)num);
}
else {
/* plus */
if (UINT_MAX < num)
- rb_raise(rb_eRangeError, "integer %lu too big to convert to `unsigned int'", num);
+ rb_raise(rb_eRangeError, "integer %lu too big to convert to 'unsigned int'", num);
}
}
@@ -3309,7 +3314,7 @@ NORETURN(static void rb_out_of_short(SIGNED_VALUE num));
static void
rb_out_of_short(SIGNED_VALUE num)
{
- rb_raise(rb_eRangeError, "integer %"PRIdVALUE " too %s to convert to `short'",
+ rb_raise(rb_eRangeError, "integer %"PRIdVALUE " too %s to convert to 'short'",
num, num < 0 ? "small" : "big");
}
@@ -3327,12 +3332,12 @@ check_ushort(unsigned long num, int sign)
if (sign) {
/* minus */
if (num < (unsigned long)SHRT_MIN)
- rb_raise(rb_eRangeError, "integer %ld too small to convert to `unsigned short'", (long)num);
+ rb_raise(rb_eRangeError, "integer %ld too small to convert to 'unsigned short'", (long)num);
}
else {
/* plus */
if (USHRT_MAX < num)
- rb_raise(rb_eRangeError, "integer %lu too big to convert to `unsigned short'", num);
+ rb_raise(rb_eRangeError, "integer %lu too big to convert to 'unsigned short'", num);
}
}
@@ -3488,7 +3493,10 @@ rb_num2ull(VALUE val)
*
* First, what's elsewhere. \Class \Integer:
*
- * - Inherits from {class Numeric}[rdoc-ref:Numeric@What-27s+Here].
+ * - Inherits from
+ * {class Numeric}[rdoc-ref:Numeric@What-27s+Here]
+ * and {class Object}[rdoc-ref:Object@What-27s+Here].
+ * - Includes {module Comparable}[rdoc-ref:Comparable@What-27s+Here].
*
* Here, class \Integer provides methods for:
*
@@ -3568,7 +3576,7 @@ rb_int_odd_p(VALUE num)
return RBOOL(num & 2);
}
else {
- assert(RB_BIGNUM_TYPE_P(num));
+ RUBY_ASSERT(RB_BIGNUM_TYPE_P(num));
return rb_big_odd_p(num);
}
}
@@ -3580,7 +3588,7 @@ int_even_p(VALUE num)
return RBOOL((num & 2) == 0);
}
else {
- assert(RB_BIGNUM_TYPE_P(num));
+ RUBY_ASSERT(RB_BIGNUM_TYPE_P(num));
return rb_big_even_p(num);
}
}
@@ -3837,7 +3845,7 @@ rb_int_uminus(VALUE num)
return fix_uminus(num);
}
else {
- assert(RB_BIGNUM_TYPE_P(num));
+ RUBY_ASSERT(RB_BIGNUM_TYPE_P(num));
return rb_big_uminus(num);
}
}
@@ -4096,7 +4104,13 @@ static double
fix_fdiv_double(VALUE x, VALUE y)
{
if (FIXNUM_P(y)) {
- return double_div_double(FIX2LONG(x), FIX2LONG(y));
+ long iy = FIX2LONG(y);
+#if SIZEOF_LONG * CHAR_BIT > DBL_MANT_DIG
+ if ((iy < 0 ? -iy : iy) >= (1L << DBL_MANT_DIG)) {
+ return rb_big_fdiv_double(rb_int2big(FIX2LONG(x)), rb_int2big(iy));
+ }
+#endif
+ return double_div_double(FIX2LONG(x), iy);
}
else if (RB_BIGNUM_TYPE_P(y)) {
return rb_big_fdiv_double(rb_int2big(FIX2LONG(x)), y);
@@ -4114,7 +4128,7 @@ rb_int_fdiv_double(VALUE x, VALUE y)
{
if (RB_INTEGER_TYPE_P(y) && !FIXNUM_ZERO_P(y)) {
VALUE gcd = rb_gcd(x, y);
- if (!FIXNUM_ZERO_P(gcd)) {
+ if (!FIXNUM_ZERO_P(gcd) && gcd != INT2FIX(1)) {
x = rb_int_idiv(x, gcd);
y = rb_int_idiv(y, gcd);
}
@@ -4236,14 +4250,14 @@ fix_idiv(VALUE x, VALUE y)
* Performs integer division; returns the integer result of dividing +self+
* by +numeric+:
*
- * 4.div(3) # => 1
- * 4.div(-3) # => -2
- * -4.div(3) # => -2
- * -4.div(-3) # => 1
- * 4.div(3.0) # => 1
- * 4.div(Rational(3, 1)) # => 1
+ * 4.div(3) # => 1
+ * 4.div(-3) # => -2
+ * -4.div(3) # => -2
+ * -4.div(-3) # => 1
+ * 4.div(3.0) # => 1
+ * 4.div(Rational(3, 1)) # => 1
*
- * Raises an exception if +numeric+ does not have method +div+.
+ * Raises an exception if +numeric+ does not have method +div+.
*
*/
@@ -4347,7 +4361,7 @@ int_remainder(VALUE x, VALUE y)
if (FIXNUM_P(x)) {
if (FIXNUM_P(y)) {
VALUE z = fix_mod(x, y);
- assert(FIXNUM_P(z));
+ RUBY_ASSERT(FIXNUM_P(z));
if (z != INT2FIX(0) && (SIGNED_VALUE)(x ^ y) < 0)
z = fix_minus(z, y);
return z;
@@ -4697,7 +4711,7 @@ rb_int_cmp(VALUE x, VALUE y)
return rb_big_cmp(x, y);
}
else {
- rb_raise(rb_eNotImpError, "need to define `<=>' in %s", rb_obj_classname(x));
+ rb_raise(rb_eNotImpError, "need to define '<=>' in %s", rb_obj_classname(x));
}
}
@@ -5157,7 +5171,7 @@ fix_rshift(long val, unsigned long i)
*
*/
-static VALUE
+VALUE
rb_int_rshift(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
@@ -5339,7 +5353,7 @@ int_aref(int const argc, VALUE * const argv, VALUE const num)
* 1.to_f # => 1.0
* -1.to_f # => -1.0
*
- * If the value of +self+ does not fit in a \Float,
+ * If the value of +self+ does not fit in a Float,
* the result is infinity:
*
* (10**400).to_f # => Infinity
@@ -5432,7 +5446,7 @@ rb_fix_digits(VALUE fix, long base)
VALUE digits;
long x = FIX2LONG(fix);
- assert(x >= 0);
+ RUBY_ASSERT(x >= 0);
if (base < 2)
rb_raise(rb_eArgError, "invalid radix %ld", base);
@@ -5441,11 +5455,12 @@ rb_fix_digits(VALUE fix, long base)
return rb_ary_new_from_args(1, INT2FIX(0));
digits = rb_ary_new();
- while (x > 0) {
+ while (x >= base) {
long q = x % base;
rb_ary_push(digits, LONG2NUM(q));
x /= base;
}
+ rb_ary_push(digits, LONG2NUM(x));
return digits;
}
@@ -5455,7 +5470,7 @@ rb_int_digits_bigbase(VALUE num, VALUE base)
{
VALUE digits, bases;
- assert(!rb_num_negative_p(num));
+ RUBY_ASSERT(!rb_num_negative_p(num));
if (RB_BIGNUM_TYPE_P(base))
base = rb_big_norm(base);
@@ -5655,53 +5670,7 @@ int_downto(VALUE from, VALUE to)
static VALUE
int_dotimes_size(VALUE num, VALUE args, VALUE eobj)
{
- if (FIXNUM_P(num)) {
- if (NUM2LONG(num) <= 0) return INT2FIX(0);
- }
- else {
- if (RTEST(rb_funcall(num, '<', 1, INT2FIX(0)))) return INT2FIX(0);
- }
- return num;
-}
-
-/*
- * call-seq:
- * times {|i| ... } -> self
- * times -> enumerator
- *
- * Calls the given block +self+ times with each integer in <tt>(0..self-1)</tt>:
- *
- * a = []
- * 5.times {|i| a.push(i) } # => 5
- * a # => [0, 1, 2, 3, 4]
- *
- * With no block given, returns an Enumerator.
- *
- */
-
-static VALUE
-int_dotimes(VALUE num)
-{
- RETURN_SIZED_ENUMERATOR(num, 0, 0, int_dotimes_size);
-
- if (FIXNUM_P(num)) {
- long i, end;
-
- end = FIX2LONG(num);
- for (i=0; i<end; i++) {
- rb_yield_1(LONG2FIX(i));
- }
- }
- else {
- VALUE i = INT2FIX(0);
-
- for (;;) {
- if (!RTEST(int_le(i, num))) break;
- rb_yield(i);
- i = rb_int_plus(i, INT2FIX(1));
- }
- }
- return num;
+ return int_neg_p(num) ? INT2FIX(0) : num;
}
/*
@@ -6032,9 +6001,9 @@ int_s_try_convert(VALUE self, VALUE num)
/*
* Document-class: Numeric
*
- * Numeric is the class from which all higher-level numeric classes should inherit.
+ * \Numeric is the class from which all higher-level numeric classes should inherit.
*
- * Numeric allows instantiation of heap-allocated objects. Other core numeric classes such as
+ * \Numeric allows instantiation of heap-allocated objects. Other core numeric classes such as
* Integer are implemented as immediates, which means that each Integer is a single immutable
* object which is always passed by value.
*
@@ -6048,9 +6017,9 @@ int_s_try_convert(VALUE self, VALUE num)
* 1.dup #=> 1
* 1.object_id == 1.dup.object_id #=> true
*
- * For this reason, Numeric should be used when defining other numeric classes.
+ * For this reason, \Numeric should be used when defining other numeric classes.
*
- * Classes which inherit from Numeric must implement +coerce+, which returns a two-member
+ * Classes which inherit from \Numeric must implement +coerce+, which returns a two-member
* Array containing an object that has been coerced into an instance of the new class
* and +self+ (see #coerce).
*
@@ -6243,7 +6212,6 @@ Init_Numeric(void)
rb_define_method(rb_cInteger, "nobits?", int_nobits_p, 1);
rb_define_method(rb_cInteger, "upto", int_upto, 1);
rb_define_method(rb_cInteger, "downto", int_downto, 1);
- rb_define_method(rb_cInteger, "times", int_dotimes, 0);
rb_define_method(rb_cInteger, "succ", int_succ, 0);
rb_define_method(rb_cInteger, "next", int_succ, 0);
rb_define_method(rb_cInteger, "pred", int_pred, 0);
@@ -6286,19 +6254,25 @@ Init_Numeric(void)
rb_define_method(rb_cInteger, "digits", rb_int_digits, -1);
- rb_fix_to_s_static[0] = rb_fstring_literal("0");
- rb_fix_to_s_static[1] = rb_fstring_literal("1");
- rb_fix_to_s_static[2] = rb_fstring_literal("2");
- rb_fix_to_s_static[3] = rb_fstring_literal("3");
- rb_fix_to_s_static[4] = rb_fstring_literal("4");
- rb_fix_to_s_static[5] = rb_fstring_literal("5");
- rb_fix_to_s_static[6] = rb_fstring_literal("6");
- rb_fix_to_s_static[7] = rb_fstring_literal("7");
- rb_fix_to_s_static[8] = rb_fstring_literal("8");
- rb_fix_to_s_static[9] = rb_fstring_literal("9");
- for(int i = 0; i < 10; i++) {
- rb_gc_register_mark_object(rb_fix_to_s_static[i]);
- }
+#define fix_to_s_static(n) do { \
+ VALUE lit = rb_fstring_literal(#n); \
+ rb_fix_to_s_static[n] = lit; \
+ rb_vm_register_global_object(lit); \
+ RB_GC_GUARD(lit); \
+ } while (0)
+
+ fix_to_s_static(0);
+ fix_to_s_static(1);
+ fix_to_s_static(2);
+ fix_to_s_static(3);
+ fix_to_s_static(4);
+ fix_to_s_static(5);
+ fix_to_s_static(6);
+ fix_to_s_static(7);
+ fix_to_s_static(8);
+ fix_to_s_static(9);
+
+#undef fix_to_s_static
rb_cFloat = rb_define_class("Float", rb_cNumeric);
diff --git a/numeric.rb b/numeric.rb
index 92768200ad..4dc406fd23 100644
--- a/numeric.rb
+++ b/numeric.rb
@@ -1,62 +1,56 @@
class Numeric
- #
+
# call-seq:
- # num.real? -> true or false
+ # real? -> true or false
#
- # Returns +true+ if +num+ is a real number (i.e. not Complex).
+ # Returns +true+ if +self+ is a real number (i.e. not Complex).
#
def real?
true
end
- #
# call-seq:
- # num.real -> self
+ # real -> self
#
- # Returns self.
+ # Returns +self+.
#
def real
self
end
- #
# call-seq:
- # num.integer? -> true or false
+ # integer? -> true or false
#
- # Returns +true+ if +num+ is an Integer.
+ # Returns +true+ if +self+ is an Integer.
#
- # 1.0.integer? #=> false
- # 1.integer? #=> true
+ # 1.0.integer? # => false
+ # 1.integer? # => true
#
def integer?
false
end
- #
# call-seq:
- # num.finite? -> true or false
+ # finite? -> true or false
#
- # Returns +true+ if +num+ is a finite number, otherwise returns +false+.
+ # Returns +true+ if +self+ is a finite number, +false+ otherwise.
#
def finite?
true
end
- #
# call-seq:
- # num.infinite? -> -1, 1, or nil
+ # infinite? -> -1, 1, or nil
#
- # Returns +nil+, -1, or 1 depending on whether the value is
- # finite, <code>-Infinity</code>, or <code>+Infinity</code>.
+ # Returns +nil+, -1, or 1 depending on whether +self+ is
+ # finite, <tt>-Infinity</tt>, or <tt>+Infinity</tt>.
#
def infinite?
nil
end
- #
# call-seq:
- # num.imag -> 0
- # num.imaginary -> 0
+ # imag -> 0
#
# Returns zero.
#
@@ -66,12 +60,10 @@ class Numeric
alias imag imaginary
- #
# call-seq:
- # num.conj -> self
- # num.conjugate -> self
+ # conj -> self
#
- # Returns self.
+ # Returns +self+.
#
def conjugate
self
@@ -82,39 +74,41 @@ end
class Integer
# call-seq:
- # -int -> integer
+ # -int -> integer
#
- # Returns +int+, negated.
+ # Returns +self+, negated.
def -@
Primitive.attr! :leaf
Primitive.cexpr! 'rb_int_uminus(self)'
end
# call-seq:
- # ~int -> integer
+ # ~int -> integer
#
- # One's complement: returns a number where each bit is flipped.
+ # One's complement:
+ # returns the value of +self+ with each bit inverted.
#
- # Inverts the bits in an Integer. As integers are conceptually of
- # infinite length, the result acts as if it had an infinite number of
- # one bits to the left. In hex representations, this is displayed
- # as two periods to the left of the digits.
+ # Because an integer value is conceptually of infinite length,
+ # the result acts as if it had an infinite number of
+ # one bits to the left.
+ # In hex representations, this is displayed
+ # as two periods to the left of the digits:
+ #
+ # sprintf("%X", ~0x1122334455) # => "..FEEDDCCBBAA"
#
- # sprintf("%X", ~0x1122334455) #=> "..FEEDDCCBBAA"
def ~
Primitive.attr! :leaf
Primitive.cexpr! 'rb_int_comp(self)'
end
# call-seq:
- # int.abs -> integer
- # int.magnitude -> integer
+ # abs -> integer
#
- # Returns the absolute value of +int+.
+ # Returns the absolute value of +self+.
#
- # (-12345).abs #=> 12345
- # -12345.abs #=> 12345
- # 12345.abs #=> 12345
+ # (-12345).abs # => 12345
+ # -12345.abs # => 12345
+ # 12345.abs # => 12345
#
def abs
Primitive.attr! :leaf
@@ -122,64 +116,65 @@ class Integer
end
# call-seq:
- # int.bit_length -> integer
- #
- # Returns the number of bits of the value of +int+.
+ # bit_length -> integer
#
- # "Number of bits" means the bit position of the highest bit
- # which is different from the sign bit
+ # Returns the number of bits of the value of +self+,
+ # which is the bit position of the highest-order bit
+ # that is different from the sign bit
# (where the least significant bit has bit position 1).
- # If there is no such bit (zero or minus one), zero is returned.
- #
- # I.e. this method returns <i>ceil(log2(int < 0 ? -int : int+1))</i>.
- #
- # (-2**1000-1).bit_length #=> 1001
- # (-2**1000).bit_length #=> 1000
- # (-2**1000+1).bit_length #=> 1000
- # (-2**12-1).bit_length #=> 13
- # (-2**12).bit_length #=> 12
- # (-2**12+1).bit_length #=> 12
- # -0x101.bit_length #=> 9
- # -0x100.bit_length #=> 8
- # -0xff.bit_length #=> 8
- # -2.bit_length #=> 1
- # -1.bit_length #=> 0
- # 0.bit_length #=> 0
- # 1.bit_length #=> 1
- # 0xff.bit_length #=> 8
- # 0x100.bit_length #=> 9
- # (2**12-1).bit_length #=> 12
- # (2**12).bit_length #=> 13
- # (2**12+1).bit_length #=> 13
- # (2**1000-1).bit_length #=> 1000
- # (2**1000).bit_length #=> 1001
- # (2**1000+1).bit_length #=> 1001
- #
- # This method can be used to detect overflow in Array#pack as follows:
- #
- # if n.bit_length < 32
- # [n].pack("l") # no overflow
- # else
- # raise "overflow"
- # end
+ # If there is no such bit (zero or minus one), returns zero.
+ #
+ # This method returns <tt>ceil(log2(self < 0 ? -self : self + 1))</tt>>.
+ #
+ # (-2**1000-1).bit_length # => 1001
+ # (-2**1000).bit_length # => 1000
+ # (-2**1000+1).bit_length # => 1000
+ # (-2**12-1).bit_length # => 13
+ # (-2**12).bit_length # => 12
+ # (-2**12+1).bit_length # => 12
+ # -0x101.bit_length # => 9
+ # -0x100.bit_length # => 8
+ # -0xff.bit_length # => 8
+ # -2.bit_length # => 1
+ # -1.bit_length # => 0
+ # 0.bit_length # => 0
+ # 1.bit_length # => 1
+ # 0xff.bit_length # => 8
+ # 0x100.bit_length # => 9
+ # (2**12-1).bit_length # => 12
+ # (2**12).bit_length # => 13
+ # (2**12+1).bit_length # => 13
+ # (2**1000-1).bit_length # => 1000
+ # (2**1000).bit_length # => 1001
+ # (2**1000+1).bit_length # => 1001
+ #
+ # For \Integer _n_,
+ # this method can be used to detect overflow in Array#pack:
+ #
+ # if n.bit_length < 32
+ # [n].pack('l') # No overflow.
+ # else
+ # raise 'Overflow'
+ # end
+ #
def bit_length
Primitive.attr! :leaf
Primitive.cexpr! 'rb_int_bit_length(self)'
end
# call-seq:
- # int.even? -> true or false
+ # even? -> true or false
#
- # Returns +true+ if +int+ is an even number.
+ # Returns +true+ if +self+ is an even number, +false+ otherwise.
def even?
Primitive.attr! :leaf
Primitive.cexpr! 'rb_int_even_p(self)'
end
# call-seq:
- # int.integer? -> true
+ # integer? -> true
#
- # Since +int+ is already an Integer, this always returns +true+.
+ # Since +self+ is already an \Integer, always returns +true+.
def integer?
true
end
@@ -187,131 +182,146 @@ class Integer
alias magnitude abs
# call-seq:
- # int.odd? -> true or false
+ # odd? -> true or false
#
- # Returns +true+ if +int+ is an odd number.
+ # Returns +true+ if +self+ is an odd number, +false+ otherwise.
def odd?
Primitive.attr! :leaf
Primitive.cexpr! 'rb_int_odd_p(self)'
end
# call-seq:
- # int.ord -> self
- #
- # Returns the +int+ itself.
+ # ord -> self
#
- # 97.ord #=> 97
- #
- # This method is intended for compatibility to character literals
- # in Ruby 1.9.
- #
- # For example, <code>?a.ord</code> returns 97 both in 1.8 and 1.9.
+ # Returns +self+;
+ # intended for compatibility to character literals in Ruby 1.9.
def ord
self
end
# call-seq:
- # int.size -> int
+ # size -> integer
#
- # Returns the number of bytes in the machine representation of +int+
- # (machine dependent).
+ # Returns the number of bytes in the machine representation of +self+;
+ # the value is system-dependent:
#
- # 1.size #=> 8
- # -1.size #=> 8
- # 2147483647.size #=> 8
- # (256**10 - 1).size #=> 10
- # (256**20 - 1).size #=> 20
- # (256**40 - 1).size #=> 40
+ # 1.size # => 8
+ # -1.size # => 8
+ # 2147483647.size # => 8
+ # (256**10 - 1).size # => 10
+ # (256**20 - 1).size # => 20
+ # (256**40 - 1).size # => 40
#
def size
Primitive.attr! :leaf
Primitive.cexpr! 'rb_int_size(self)'
end
+ # call-seq:
+ # times {|i| ... } -> self
+ # times -> enumerator
+ #
+ # Calls the given block +self+ times with each integer in <tt>(0..self-1)</tt>:
+ #
+ # a = []
+ # 5.times {|i| a.push(i) } # => 5
+ # a # => [0, 1, 2, 3, 4]
+ #
+ # With no block given, returns an Enumerator.
+ def times
+ Primitive.attr! :inline_block
+ unless defined?(yield)
+ return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 0, 0, int_dotimes_size)'
+ end
+ i = 0
+ while i < self
+ yield i
+ i = i.succ
+ end
+ self
+ end
+
# call-seq:
- # int.to_i -> integer
+ # to_i -> self
#
- # Since +int+ is already an Integer, returns +self+.
+ # Returns +self+ (which is already an \Integer).
def to_i
self
end
# call-seq:
- # int.to_int -> integer
+ # to_int -> self
#
- # Since +int+ is already an Integer, returns +self+.
+ # Returns +self+ (which is already an \Integer).
def to_int
self
end
# call-seq:
- # int.zero? -> true or false
+ # zero? -> true or false
#
- # Returns +true+ if +int+ has a zero value.
+ # Returns +true+ if +self+ has a zero value, +false+ otherwise.
def zero?
Primitive.attr! :leaf
Primitive.cexpr! 'rb_int_zero_p(self)'
end
# call-seq:
- # ceildiv(other) -> integer
+ # ceildiv(numeric) -> integer
#
- # Returns the result of division +self+ by +other+. The result is rounded up to the nearest integer.
+ # Returns the result of division +self+ by +numeric+.
+ # rounded up to the nearest integer.
#
- # 3.ceildiv(3) # => 1
- # 4.ceildiv(3) # => 2
+ # 3.ceildiv(3) # => 1
+ # 4.ceildiv(3) # => 2
#
- # 4.ceildiv(-3) # => -1
- # -4.ceildiv(3) # => -1
+ # 4.ceildiv(-3) # => -1
+ # -4.ceildiv(3) # => -1
# -4.ceildiv(-3) # => 2
#
# 3.ceildiv(1.2) # => 3
+ #
def ceildiv(other)
-div(0 - other)
end
#
# call-seq:
- # int.numerator -> self
+ # numerator -> self
#
- # Returns self.
+ # Returns +self+.
#
def numerator
self
end
- #
# call-seq:
- # int.denominator -> 1
- #
- # Returns 1.
+ # denominator -> 1
#
+ # Returns +1+.
def denominator
1
end
end
class Float
- #
+
# call-seq:
- # float.to_f -> self
- #
- # Since +float+ is already a Float, returns +self+.
+ # to_f -> self
#
+ # Returns +self+ (which is already a \Float).
def to_f
self
end
- #
# call-seq:
- # float.abs -> float
- # float.magnitude -> float
+ # float.abs -> float
#
- # Returns the absolute value of +float+.
+ # Returns the absolute value of +self+:
#
- # (-34.56).abs #=> 34.56
- # -34.56.abs #=> 34.56
- # 34.56.abs #=> 34.56
+ # (-34.56).abs # => 34.56
+ # -34.56.abs # => 34.56
+ # 34.56.abs # => 34.56
#
def abs
Primitive.attr! :leaf
@@ -323,45 +333,38 @@ class Float
Primitive.cexpr! 'rb_float_abs(self)'
end
- #
# call-seq:
- # -float -> float
+ # -float -> float
#
- # Returns +float+, negated.
+ # Returns +self+, negated.
#
def -@
Primitive.attr! :leaf
Primitive.cexpr! 'rb_float_uminus(self)'
end
- #
# call-seq:
- # float.zero? -> true or false
- #
- # Returns +true+ if +float+ is 0.0.
+ # zero? -> true or false
#
+ # Returns +true+ if +self+ is 0.0, +false+ otherwise.
def zero?
Primitive.attr! :leaf
Primitive.cexpr! 'RBOOL(FLOAT_ZERO_P(self))'
end
- #
# call-seq:
- # float.positive? -> true or false
- #
- # Returns +true+ if +float+ is greater than 0.
+ # positive? -> true or false
#
+ # Returns +true+ if +self+ is greater than 0, +false+ otherwise.
def positive?
Primitive.attr! :leaf
Primitive.cexpr! 'RBOOL(RFLOAT_VALUE(self) > 0.0)'
end
- #
# call-seq:
- # float.negative? -> true or false
- #
- # Returns +true+ if +float+ is less than 0.
+ # negative? -> true or false
#
+ # Returns +true+ if +self+ is less than 0, +false+ otherwise.
def negative?
Primitive.attr! :leaf
Primitive.cexpr! 'RBOOL(RFLOAT_VALUE(self) < 0.0)'
diff --git a/object.c b/object.c
index 94e85ed316..0a8ce4dc0f 100644
--- a/object.c
+++ b/object.c
@@ -31,6 +31,7 @@
#include "internal/object.h"
#include "internal/struct.h"
#include "internal/string.h"
+#include "internal/st.h"
#include "internal/symbol.h"
#include "internal/variable.h"
#include "variable.h"
@@ -41,6 +42,7 @@
#include "ruby/assert.h"
#include "builtin.h"
#include "shape.h"
+#include "yjit.h"
/* Flags of RObject
*
@@ -92,6 +94,12 @@ static VALUE rb_cFalseClass_to_s;
/*! \endcond */
+size_t
+rb_obj_embedded_size(uint32_t numiv)
+{
+ return offsetof(struct RObject, as.ary) + (sizeof(VALUE) * numiv);
+}
+
VALUE
rb_obj_hide(VALUE obj)
{
@@ -111,9 +119,40 @@ rb_obj_reveal(VALUE obj, VALUE klass)
}
VALUE
+rb_class_allocate_instance(VALUE klass)
+{
+ uint32_t index_tbl_num_entries = RCLASS_EXT(klass)->max_iv_count;
+
+ size_t size = rb_obj_embedded_size(index_tbl_num_entries);
+ if (!rb_gc_size_allocatable_p(size)) {
+ size = sizeof(struct RObject);
+ }
+
+ NEWOBJ_OF(o, struct RObject, klass,
+ T_OBJECT | ROBJECT_EMBED | (RGENGC_WB_PROTECTED_OBJECT ? FL_WB_PROTECTED : 0), size, 0);
+ VALUE obj = (VALUE)o;
+
+ RUBY_ASSERT(rb_shape_get_shape(obj)->type == SHAPE_ROOT);
+
+ // Set the shape to the specific T_OBJECT shape.
+ ROBJECT_SET_SHAPE_ID(obj, (shape_id_t)(rb_gc_size_pool_id_for_size(size) + FIRST_T_OBJECT_SHAPE_ID));
+
+#if RUBY_DEBUG
+ RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
+ VALUE *ptr = ROBJECT_IVPTR(obj);
+ for (size_t i = 0; i < ROBJECT_IV_CAPACITY(obj); i++) {
+ ptr[i] = Qundef;
+ }
+#endif
+
+ return obj;
+}
+
+VALUE
rb_obj_setup(VALUE obj, VALUE klass, VALUE type)
{
- RBASIC(obj)->flags = type;
+ VALUE ignored_flags = RUBY_FL_PROMOTED | RUBY_FL_SEEN_OBJ_ID;
+ RBASIC(obj)->flags = (type & ~ignored_flags) | (RBASIC(obj)->flags & ignored_flags);
RBASIC_SET_CLASS(obj, klass);
return obj;
}
@@ -248,7 +287,7 @@ VALUE
rb_class_real(VALUE cl)
{
while (cl &&
- ((RBASIC(cl)->flags & FL_SINGLETON) || BUILTIN_TYPE(cl) == T_ICLASS)) {
+ (RCLASS_SINGLETON_P(cl) || BUILTIN_TYPE(cl) == T_ICLASS)) {
cl = RCLASS_SUPER(cl);
}
return cl;
@@ -292,13 +331,10 @@ rb_obj_copy_ivar(VALUE dest, VALUE obj)
RUBY_ASSERT(BUILTIN_TYPE(dest) == BUILTIN_TYPE(obj));
rb_shape_t * src_shape = rb_shape_get_shape(obj);
- if (rb_shape_id(src_shape) == OBJ_TOO_COMPLEX_SHAPE_ID) {
- st_table * table = rb_st_init_numtable_with_size(rb_st_table_size(ROBJECT_IV_HASH(obj)));
-
- rb_ivar_foreach(obj, rb_obj_evacuate_ivs_to_hash_table, (st_data_t)table);
- rb_shape_set_too_complex(dest);
-
- ROBJECT(dest)->as.heap.ivptr = (VALUE *)table;
+ if (rb_shape_obj_too_complex(obj)) {
+ // obj is TOO_COMPLEX so we can copy its iv_hash
+ st_table *table = st_copy(ROBJECT_IV_HASH(obj));
+ rb_obj_convert_to_too_complex(dest, table);
return;
}
@@ -326,9 +362,16 @@ rb_obj_copy_ivar(VALUE dest, VALUE obj)
RUBY_ASSERT(initial_shape->type == SHAPE_T_OBJECT);
shape_to_set_on_dest = rb_shape_rebuild_shape(initial_shape, src_shape);
+ if (UNLIKELY(rb_shape_id(shape_to_set_on_dest) == OBJ_TOO_COMPLEX_SHAPE_ID)) {
+ st_table * table = rb_st_init_numtable_with_size(src_num_ivs);
+ rb_obj_copy_ivs_to_hash_table(obj, table);
+ rb_obj_convert_to_too_complex(dest, table);
+
+ return;
+ }
}
- RUBY_ASSERT(src_num_ivs <= shape_to_set_on_dest->capacity);
+ RUBY_ASSERT(src_num_ivs <= shape_to_set_on_dest->capacity || rb_shape_id(shape_to_set_on_dest) == OBJ_TOO_COMPLEX_SHAPE_ID);
if (initial_shape->capacity < shape_to_set_on_dest->capacity) {
rb_ensure_iv_list_size(dest, initial_shape->capacity, shape_to_set_on_dest->capacity);
dest_buf = ROBJECT_IVPTR(dest);
@@ -353,10 +396,8 @@ init_copy(VALUE dest, VALUE obj)
RBASIC(dest)->flags &= ~(T_MASK|FL_EXIVAR);
// Copies the shape id from obj to dest
RBASIC(dest)->flags |= RBASIC(obj)->flags & (T_MASK|FL_EXIVAR);
- rb_copy_wb_protected_attribute(dest, obj);
+ rb_gc_copy_attributes(dest, obj);
rb_copy_generic_ivar(dest, obj);
- rb_gc_copy_finalizer(dest, obj);
-
if (RB_TYPE_P(obj, T_OBJECT)) {
rb_obj_copy_ivar(dest, obj);
}
@@ -442,17 +483,14 @@ immutable_obj_clone(VALUE obj, VALUE kwfreeze)
return obj;
}
-static VALUE
-mutable_obj_clone(VALUE obj, VALUE kwfreeze)
+VALUE
+rb_obj_clone_setup(VALUE obj, VALUE clone, VALUE kwfreeze)
{
- VALUE clone, singleton;
VALUE argv[2];
- clone = rb_obj_alloc(rb_obj_class(obj));
-
- singleton = rb_singleton_class_clone_and_attach(obj, clone);
+ VALUE singleton = rb_singleton_class_clone_and_attach(obj, clone);
RBASIC_SET_CLASS(clone, singleton);
- if (FL_TEST(singleton, FL_SINGLETON)) {
+ if (RCLASS_SINGLETON_P(singleton)) {
rb_singleton_class_attached(singleton, clone);
}
@@ -462,15 +500,24 @@ mutable_obj_clone(VALUE obj, VALUE kwfreeze)
case Qnil:
rb_funcall(clone, id_init_clone, 1, obj);
RBASIC(clone)->flags |= RBASIC(obj)->flags & FL_FREEZE;
- if (RB_OBJ_FROZEN(obj)) {
- rb_shape_transition_shape_frozen(clone);
+ if (CHILLED_STRING_P(obj)) {
+ STR_CHILL_RAW(clone);
+ }
+ else if (RB_OBJ_FROZEN(obj)) {
+ rb_shape_t * next_shape = rb_shape_transition_shape_frozen(clone);
+ if (!rb_shape_obj_too_complex(clone) && next_shape->type == SHAPE_OBJ_TOO_COMPLEX) {
+ rb_evict_ivars_to_hash(clone);
+ }
+ else {
+ rb_shape_set_shape(clone, next_shape);
+ }
}
break;
case Qtrue: {
static VALUE freeze_true_hash;
if (!freeze_true_hash) {
freeze_true_hash = rb_hash_new();
- rb_gc_register_mark_object(freeze_true_hash);
+ rb_vm_register_global_object(freeze_true_hash);
rb_hash_aset(freeze_true_hash, ID2SYM(idFreeze), Qtrue);
rb_obj_freeze(freeze_true_hash);
}
@@ -479,14 +526,22 @@ mutable_obj_clone(VALUE obj, VALUE kwfreeze)
argv[1] = freeze_true_hash;
rb_funcallv_kw(clone, id_init_clone, 2, argv, RB_PASS_KEYWORDS);
RBASIC(clone)->flags |= FL_FREEZE;
- rb_shape_transition_shape_frozen(clone);
+ rb_shape_t * next_shape = rb_shape_transition_shape_frozen(clone);
+ // If we're out of shapes, but we want to freeze, then we need to
+ // evacuate this clone to a hash
+ if (!rb_shape_obj_too_complex(clone) && next_shape->type == SHAPE_OBJ_TOO_COMPLEX) {
+ rb_evict_ivars_to_hash(clone);
+ }
+ else {
+ rb_shape_set_shape(clone, next_shape);
+ }
break;
}
case Qfalse: {
static VALUE freeze_false_hash;
if (!freeze_false_hash) {
freeze_false_hash = rb_hash_new();
- rb_gc_register_mark_object(freeze_false_hash);
+ rb_vm_register_global_object(freeze_false_hash);
rb_hash_aset(freeze_false_hash, ID2SYM(idFreeze), Qfalse);
rb_obj_freeze(freeze_false_hash);
}
@@ -503,6 +558,13 @@ mutable_obj_clone(VALUE obj, VALUE kwfreeze)
return clone;
}
+static VALUE
+mutable_obj_clone(VALUE obj, VALUE kwfreeze)
+{
+ VALUE clone = rb_obj_alloc(rb_obj_class(obj));
+ return rb_obj_clone_setup(obj, clone, kwfreeze);
+}
+
VALUE
rb_obj_clone(VALUE obj)
{
@@ -510,6 +572,15 @@ rb_obj_clone(VALUE obj)
return mutable_obj_clone(obj, Qnil);
}
+VALUE
+rb_obj_dup_setup(VALUE obj, VALUE dup)
+{
+ init_copy(dup, obj);
+ rb_funcall(dup, id_init_dup, 1, obj);
+
+ return dup;
+}
+
/*
* call-seq:
* obj.dup -> an_object
@@ -558,10 +629,7 @@ rb_obj_dup(VALUE obj)
return obj;
}
dup = rb_obj_alloc(rb_obj_class(obj));
- init_copy(dup, obj);
- rb_funcall(dup, id_init_dup, 1, obj);
-
- return dup;
+ return rb_obj_dup_setup(obj, dup);
}
/*
@@ -590,9 +658,9 @@ rb_obj_size(VALUE self, VALUE args, VALUE obj)
/**
* :nodoc:
*--
- * Default implementation of \c #initialize_copy
- * \param[in,out] obj the receiver being initialized
- * \param[in] orig the object to be copied from.
+ * Default implementation of `#initialize_copy`
+ * @param[in,out] obj the receiver being initialized
+ * @param[in] orig the object to be copied from.
*++
*/
VALUE
@@ -606,13 +674,13 @@ rb_obj_init_copy(VALUE obj, VALUE orig)
return obj;
}
-/*!
+/**
* :nodoc:
*--
- * Default implementation of \c #initialize_dup
+ * Default implementation of `#initialize_dup`
*
- * \param[in,out] obj the receiver being initialized
- * \param[in] orig the object to be dup from.
+ * @param[in,out] obj the receiver being initialized
+ * @param[in] orig the object to be dup from.
*++
**/
VALUE
@@ -622,14 +690,14 @@ rb_obj_init_dup_clone(VALUE obj, VALUE orig)
return obj;
}
-/*!
+/**
* :nodoc:
*--
- * Default implementation of \c #initialize_clone
+ * Default implementation of `#initialize_clone`
*
- * \param[in] The number of arguments
- * \param[in] The array of arguments
- * \param[in] obj the receiver being initialized
+ * @param[in] The number of arguments
+ * @param[in] The array of arguments
+ * @param[in] obj the receiver being initialized
*++
**/
static VALUE
@@ -683,10 +751,8 @@ rb_inspect(VALUE obj)
}
static int
-inspect_i(st_data_t k, st_data_t v, st_data_t a)
+inspect_i(ID id, VALUE value, st_data_t a)
{
- ID id = (ID)k;
- VALUE value = (VALUE)v;
VALUE str = (VALUE)a;
/* need not to show internal data */
@@ -803,7 +869,7 @@ rb_obj_is_instance_of(VALUE obj, VALUE c)
return RBOOL(rb_obj_class(obj) == c);
}
-// Returns whether c is a proper (c != cl) subclass of cl
+// Returns whether c is a proper (c != cl) superclass of cl
// Both c and cl must be T_CLASS
static VALUE
class_search_class_ancestor(VALUE cl, VALUE c)
@@ -816,7 +882,7 @@ class_search_class_ancestor(VALUE cl, VALUE c)
VALUE *classes = RCLASS_SUPERCLASSES(cl);
// If c's inheritance chain is longer, it cannot be an ancestor
- // We are checking for a proper subclass so don't check if they are equal
+ // We are checking for a proper superclass so don't check if they are equal
if (cl_depth <= c_depth)
return Qfalse;
@@ -1679,7 +1745,7 @@ rb_mod_to_s(VALUE klass)
ID id_defined_at;
VALUE refined_class, defined_at;
- if (FL_TEST(klass, FL_SINGLETON)) {
+ if (RCLASS_SINGLETON_P(klass)) {
VALUE s = rb_usascii_str_new2("#<Class:");
VALUE v = RCLASS_ATTACHED_OBJECT(klass);
@@ -2055,7 +2121,7 @@ class_get_alloc_func(VALUE klass)
if (RCLASS_SUPER(klass) == 0 && klass != rb_cBasicObject) {
rb_raise(rb_eTypeError, "can't instantiate uninitialized class");
}
- if (FL_TEST(klass, FL_SINGLETON)) {
+ if (RCLASS_SINGLETON_P(klass)) {
rb_raise(rb_eTypeError, "can't create instance of singleton class");
}
allocator = rb_get_alloc_func(klass);
@@ -2145,12 +2211,12 @@ rb_class_new_instance(int argc, const VALUE *argv, VALUE klass)
* BasicObject.superclass #=> nil
*
*--
- * Returns the superclass of \a klass. Equivalent to \c Class\#superclass in Ruby.
+ * Returns the superclass of `klass`. Equivalent to `Class#superclass` in Ruby.
*
* It skips modules.
- * \param[in] klass a Class object
- * \return the superclass, or \c Qnil if \a klass does not have a parent class.
- * \sa rb_class_get_superclass
+ * @param[in] klass a Class object
+ * @return the superclass, or `Qnil` if `klass` does not have a parent class.
+ * @sa rb_class_get_superclass
*++
*/
@@ -2165,6 +2231,10 @@ rb_class_superclass(VALUE klass)
if (klass == rb_cBasicObject) return Qnil;
rb_raise(rb_eTypeError, "uninitialized class");
}
+
+ if (!RCLASS_SUPERCLASS_DEPTH(klass)) {
+ return Qnil;
+ }
else {
super = RCLASS_SUPERCLASSES(klass)[RCLASS_SUPERCLASS_DEPTH(klass) - 1];
RUBY_ASSERT(RB_TYPE_P(klass, T_CLASS));
@@ -2178,10 +2248,10 @@ rb_class_get_superclass(VALUE klass)
return RCLASS(klass)->super;
}
-static const char bad_instance_name[] = "`%1$s' is not allowed as an instance variable name";
-static const char bad_class_name[] = "`%1$s' is not allowed as a class variable name";
+static const char bad_instance_name[] = "'%1$s' is not allowed as an instance variable name";
+static const char bad_class_name[] = "'%1$s' is not allowed as a class variable name";
static const char bad_const_name[] = "wrong constant name %1$s";
-static const char bad_attr_name[] = "invalid attribute name `%1$s'";
+static const char bad_attr_name[] = "invalid attribute name '%1$s'";
#define wrong_constant_name bad_const_name
/*! \private */
@@ -3012,7 +3082,7 @@ rb_mod_cvar_defined(VALUE obj, VALUE iv)
static VALUE
rb_mod_singleton_p(VALUE klass)
{
- return RBOOL(RB_TYPE_P(klass, T_CLASS) && FL_TEST(klass, FL_SINGLETON));
+ return RBOOL(RCLASS_SINGLETON_P(klass));
}
/*! \private */
@@ -3166,14 +3236,22 @@ ALWAYS_INLINE(static VALUE rb_to_integer_with_id_exception(VALUE val, const char
static inline VALUE
rb_to_integer_with_id_exception(VALUE val, const char *method, ID mid, int raise)
{
+ // We need to pop the lazily pushed frame when not raising an exception.
+ rb_control_frame_t *current_cfp;
VALUE v;
if (RB_INTEGER_TYPE_P(val)) return val;
+ current_cfp = GET_EC()->cfp;
+ rb_yjit_lazy_push_frame(GET_EC()->cfp->pc);
v = try_to_int(val, mid, raise);
- if (!raise && NIL_P(v)) return Qnil;
+ if (!raise && NIL_P(v)) {
+ GET_EC()->cfp = current_cfp;
+ return Qnil;
+ }
if (!RB_INTEGER_TYPE_P(v)) {
conversion_mismatch(val, "Integer", method, v);
}
+ GET_EC()->cfp = current_cfp;
return v;
}
#define rb_to_integer(val, method, mid) \
@@ -3308,121 +3386,17 @@ rb_opts_exception_p(VALUE opts, int default_value)
return default_value;
}
-#define opts_exception_p(opts) rb_opts_exception_p((opts), TRUE)
-
-/*
- * call-seq:
- * Integer(object, base = 0, exception: true) -> integer or nil
- *
- * Returns an integer converted from +object+.
- *
- * Tries to convert +object+ to an integer
- * using +to_int+ first and +to_i+ second;
- * see below for exceptions.
- *
- * With a non-zero +base+, +object+ must be a string or convertible
- * to a string.
- *
- * ==== numeric objects
- *
- * With integer argument +object+ given, returns +object+:
- *
- * Integer(1) # => 1
- * Integer(-1) # => -1
- *
- * With floating-point argument +object+ given,
- * returns +object+ truncated to an integer:
- *
- * Integer(1.9) # => 1 # Rounds toward zero.
- * Integer(-1.9) # => -1 # Rounds toward zero.
- *
- * ==== string objects
- *
- * With string argument +object+ and zero +base+ given,
- * returns +object+ converted to an integer in base 10:
- *
- * Integer('100') # => 100
- * Integer('-100') # => -100
- *
- * With +base+ zero, string +object+ may contain leading characters
- * to specify the actual base (radix indicator):
- *
- * Integer('0100') # => 64 # Leading '0' specifies base 8.
- * Integer('0b100') # => 4 # Leading '0b', specifies base 2.
- * Integer('0x100') # => 256 # Leading '0x' specifies base 16.
- *
- * With a positive +base+ (in range 2..36) given, returns +object+
- * converted to an integer in the given base:
- *
- * Integer('100', 2) # => 4
- * Integer('100', 8) # => 64
- * Integer('-100', 16) # => -256
- *
- * With a negative +base+ (in range -36..-2) given, returns +object+
- * converted to an integer in the radix indicator if exists or
- * +-base+:
- *
- * Integer('0x100', -2) # => 256
- * Integer('100', -2) # => 4
- * Integer('0b100', -8) # => 4
- * Integer('100', -8) # => 64
- * Integer('0o100', -10) # => 64
- * Integer('100', -10) # => 100
- *
- * +base+ -1 is equal the -10 case.
- *
- * When converting strings, surrounding whitespace and embedded underscores
- * are allowed and ignored:
- *
- * Integer(' 100 ') # => 100
- * Integer('-1_0_0', 16) # => -256
- *
- * ==== other classes
- *
- * Examples with +object+ of various other classes:
- *
- * Integer(Rational(9, 10)) # => 0 # Rounds toward zero.
- * Integer(Complex(2, 0)) # => 2 # Imaginary part must be zero.
- * Integer(Time.now) # => 1650974042
- *
- * ==== keywords
- *
- * With optional keyword argument +exception+ given as +true+ (the default):
- *
- * - Raises TypeError if +object+ does not respond to +to_int+ or +to_i+.
- * - Raises TypeError if +object+ is +nil+.
- * - Raise ArgumentError if +object+ is an invalid string.
- *
- * With +exception+ given as +false+, an exception of any kind is suppressed
- * and +nil+ is returned.
- *
- */
-
static VALUE
-rb_f_integer(int argc, VALUE *argv, VALUE obj)
+rb_f_integer1(rb_execution_context_t *ec, VALUE obj, VALUE arg)
{
- VALUE arg = Qnil, opts = Qnil;
- int base = 0;
-
- if (argc > 1) {
- int narg = 1;
- VALUE vbase = rb_check_to_int(argv[1]);
- if (!NIL_P(vbase)) {
- base = NUM2INT(vbase);
- narg = 2;
- }
- if (argc > narg) {
- VALUE hash = rb_check_hash_type(argv[argc-1]);
- if (!NIL_P(hash)) {
- opts = rb_extract_keywords(&hash);
- if (!hash) --argc;
- }
- }
- }
- rb_check_arity(argc, 1, 2);
- arg = argv[0];
+ return rb_convert_to_integer(arg, 0, TRUE);
+}
- return rb_convert_to_integer(arg, base, opts_exception_p(opts));
+static VALUE
+rb_f_integer(rb_execution_context_t *ec, VALUE obj, VALUE arg, VALUE base, VALUE exception)
+{
+ int exc = rb_bool_expected(exception, "exception", TRUE);
+ return rb_convert_to_integer(arg, NUM2INT(base), exc);
}
static double
@@ -4023,6 +3997,12 @@ f_sprintf(int c, const VALUE *v, VALUE _)
return rb_f_sprintf(c, v);
}
+static VALUE
+rb_f_loop_size(VALUE self, VALUE args, VALUE eobj)
+{
+ return DBL2NUM(HUGE_VAL);
+}
+
/*
* Document-class: Class
*
@@ -4121,7 +4101,7 @@ f_sprintf(int c, const VALUE *v, VALUE _)
* end
*
* def respond_to_missing?(name, include_private = false)
- * DELEGATE.include?(name) or super
+ * DELEGATE.include?(name)
* end
* end
*
@@ -4240,7 +4220,7 @@ f_sprintf(int c, const VALUE *v, VALUE _)
* and frozen state.
* - #define_singleton_method: Defines a singleton method in +self+
* for the given symbol method-name and block or proc.
- * - #display: Prints +self+ to the given \IO stream or <tt>$stdout</tt>.
+ * - #display: Prints +self+ to the given IO stream or <tt>$stdout</tt>.
* - #dup: Returns a shallow unfrozen copy of +self+.
* - #enum_for (aliased as #to_enum): Returns an Enumerator for +self+
* using the using the given method, arguments, and block.
@@ -4471,15 +4451,13 @@ InitVM_Object(void)
rb_define_global_function("sprintf", f_sprintf, -1);
rb_define_global_function("format", f_sprintf, -1);
- rb_define_global_function("Integer", rb_f_integer, -1);
-
rb_define_global_function("String", rb_f_string, 1);
rb_define_global_function("Array", rb_f_array, 1);
rb_define_global_function("Hash", rb_f_hash, 1);
rb_cNilClass = rb_define_class("NilClass", rb_cObject);
rb_cNilClass_to_s = rb_fstring_enc_lit("", rb_usascii_encoding());
- rb_gc_register_mark_object(rb_cNilClass_to_s);
+ rb_vm_register_global_object(rb_cNilClass_to_s);
rb_define_method(rb_cNilClass, "to_s", rb_nil_to_s, 0);
rb_define_method(rb_cNilClass, "to_a", nil_to_a, 0);
rb_define_method(rb_cNilClass, "to_h", nil_to_h, 0);
@@ -4508,6 +4486,7 @@ InitVM_Object(void)
rb_define_method(rb_cModule, "included_modules", rb_mod_included_modules, 0); /* in class.c */
rb_define_method(rb_cModule, "include?", rb_mod_include_p, 1); /* in class.c */
rb_define_method(rb_cModule, "name", rb_mod_name, 0); /* in variable.c */
+ rb_define_method(rb_cModule, "set_temporary_name", rb_mod_set_temporary_name, 1); /* in variable.c */
rb_define_method(rb_cModule, "ancestors", rb_mod_ancestors, 0); /* in class.c */
rb_define_method(rb_cModule, "attr", rb_mod_attr, -1);
@@ -4564,7 +4543,7 @@ InitVM_Object(void)
rb_cTrueClass = rb_define_class("TrueClass", rb_cObject);
rb_cTrueClass_to_s = rb_fstring_enc_lit("true", rb_usascii_encoding());
- rb_gc_register_mark_object(rb_cTrueClass_to_s);
+ rb_vm_register_global_object(rb_cTrueClass_to_s);
rb_define_method(rb_cTrueClass, "to_s", rb_true_to_s, 0);
rb_define_alias(rb_cTrueClass, "inspect", "to_s");
rb_define_method(rb_cTrueClass, "&", true_and, 1);
@@ -4576,7 +4555,7 @@ InitVM_Object(void)
rb_cFalseClass = rb_define_class("FalseClass", rb_cObject);
rb_cFalseClass_to_s = rb_fstring_enc_lit("false", rb_usascii_encoding());
- rb_gc_register_mark_object(rb_cFalseClass_to_s);
+ rb_vm_register_global_object(rb_cFalseClass_to_s);
rb_define_method(rb_cFalseClass, "to_s", rb_false_to_s, 0);
rb_define_alias(rb_cFalseClass, "inspect", "to_s");
rb_define_method(rb_cFalseClass, "&", false_and, 1);
diff --git a/pack.c b/pack.c
index d5b78b6060..4fdaf7fd89 100644
--- a/pack.c
+++ b/pack.c
@@ -36,10 +36,11 @@
*/
#ifdef HAVE_TRUE_LONG_LONG
static const char natstr[] = "sSiIlLqQjJ";
+# define endstr natstr
#else
static const char natstr[] = "sSiIlLjJ";
-#endif
static const char endstr[] = "sSiIlLqQjJ";
+#endif
#ifdef HAVE_TRUE_LONG_LONG
/* It is intentional to use long long instead of LONG_LONG. */
diff --git a/pack.rb b/pack.rb
index d505eaee35..b6e29c3eab 100644
--- a/pack.rb
+++ b/pack.rb
@@ -17,6 +17,7 @@ class String
# returns that array.
# See {Packed Data}[rdoc-ref:packed_data.rdoc].
def unpack(fmt, offset: 0)
+ Primitive.attr! :use_block
Primitive.pack_unpack(fmt, offset)
end
diff --git a/parse.y b/parse.y
index b331174a1a..d86c915b3f 100644
--- a/parse.y
+++ b/parse.y
@@ -19,16 +19,28 @@
#define YYDEBUG 1
#define YYERROR_VERBOSE 1
#define YYSTACK_USE_ALLOCA 0
-#define YYLTYPE rb_code_location_t
-#define YYLTYPE_IS_DECLARED 1
+
+/* For Ripper */
+#ifdef RUBY_EXTCONF_H
+# include RUBY_EXTCONF_H
+#endif
#include "ruby/internal/config.h"
-#include <ctype.h>
#include <errno.h>
-#include <stdio.h>
-struct lex_context;
+#ifdef UNIVERSAL_PARSER
+
+#include "internal/ruby_parser.h"
+#include "parser_node.h"
+#include "universal_parser.c"
+
+#ifdef RIPPER
+#define STATIC_ID2SYM p->config->static_id2sym
+#define rb_str_coderange_scan_restartable p->config->str_coderange_scan_restartable
+#endif
+
+#else
#include "internal.h"
#include "internal/compile.h"
@@ -37,16 +49,17 @@ struct lex_context;
#include "internal/encoding.h"
#include "internal/error.h"
#include "internal/hash.h"
-#include "internal/imemo.h"
#include "internal/io.h"
#include "internal/numeric.h"
#include "internal/parse.h"
#include "internal/rational.h"
#include "internal/re.h"
+#include "internal/ruby_parser.h"
#include "internal/symbol.h"
#include "internal/thread.h"
#include "internal/variable.h"
#include "node.h"
+#include "parser_node.h"
#include "probes.h"
#include "regenc.h"
#include "ruby/encoding.h"
@@ -57,11 +70,239 @@ struct lex_context;
#include "ruby/ractor.h"
#include "symbol.h"
-enum shareability {
- shareable_none,
- shareable_literal,
- shareable_copy,
- shareable_everything,
+#ifndef RIPPER
+static VALUE
+syntax_error_new(void)
+{
+ return rb_class_new_instance(0, 0, rb_eSyntaxError);
+}
+#endif
+
+static NODE *reg_named_capture_assign(struct parser_params* p, VALUE regexp, const YYLTYPE *loc);
+
+#define compile_callback rb_suppress_tracing
+#endif /* !UNIVERSAL_PARSER */
+
+static int rb_parser_string_hash_cmp(rb_parser_string_t *str1, rb_parser_string_t *str2);
+static rb_parser_string_t *rb_parser_string_deep_copy(struct parser_params *p, const rb_parser_string_t *original);
+
+static int
+node_integer_cmp(rb_node_integer_t *n1, rb_node_integer_t *n2)
+{
+ return (n1->minus != n2->minus ||
+ n1->base != n2->base ||
+ strcmp(n1->val, n2->val));
+}
+
+static int
+node_float_cmp(rb_node_float_t *n1, rb_node_float_t *n2)
+{
+ return (n1->minus != n2->minus ||
+ strcmp(n1->val, n2->val));
+}
+
+static int
+node_rational_cmp(rb_node_rational_t *n1, rb_node_rational_t *n2)
+{
+ return (n1->minus != n2->minus ||
+ n1->base != n2->base ||
+ n1->seen_point != n2->seen_point ||
+ strcmp(n1->val, n2->val));
+}
+
+static int
+node_imaginary_cmp(rb_node_imaginary_t *n1, rb_node_imaginary_t *n2)
+{
+ return (n1->minus != n2->minus ||
+ n1->base != n2->base ||
+ n1->seen_point != n2->seen_point ||
+ n1->type != n2->type ||
+ strcmp(n1->val, n2->val));
+}
+
+static int
+rb_parser_regx_hash_cmp(rb_node_regx_t *n1, rb_node_regx_t *n2)
+{
+ return (n1->options != n2->options ||
+ rb_parser_string_hash_cmp(n1->string, n2->string));
+}
+
+static st_index_t rb_parser_str_hash(rb_parser_string_t *str);
+static st_index_t rb_char_p_hash(const char *c);
+
+static int
+literal_cmp(st_data_t val, st_data_t lit)
+{
+ if (val == lit) return 0;
+
+ NODE *node_val = RNODE(val);
+ NODE *node_lit = RNODE(lit);
+ enum node_type type_val = nd_type(node_val);
+ enum node_type type_lit = nd_type(node_lit);
+
+ if (type_val != type_lit) {
+ return -1;
+ }
+
+ switch (type_lit) {
+ case NODE_INTEGER:
+ return node_integer_cmp(RNODE_INTEGER(node_val), RNODE_INTEGER(node_lit));
+ case NODE_FLOAT:
+ return node_float_cmp(RNODE_FLOAT(node_val), RNODE_FLOAT(node_lit));
+ case NODE_RATIONAL:
+ return node_rational_cmp(RNODE_RATIONAL(node_val), RNODE_RATIONAL(node_lit));
+ case NODE_IMAGINARY:
+ return node_imaginary_cmp(RNODE_IMAGINARY(node_val), RNODE_IMAGINARY(node_lit));
+ case NODE_STR:
+ return rb_parser_string_hash_cmp(RNODE_STR(node_val)->string, RNODE_STR(node_lit)->string);
+ case NODE_SYM:
+ return rb_parser_string_hash_cmp(RNODE_SYM(node_val)->string, RNODE_SYM(node_lit)->string);
+ case NODE_REGX:
+ return rb_parser_regx_hash_cmp(RNODE_REGX(node_val), RNODE_REGX(node_lit));
+ case NODE_LINE:
+ return node_val->nd_loc.beg_pos.lineno != node_lit->nd_loc.beg_pos.lineno;
+ case NODE_FILE:
+ return rb_parser_string_hash_cmp(RNODE_FILE(node_val)->path, RNODE_FILE(node_lit)->path);
+ case NODE_ENCODING:
+ return RNODE_ENCODING(node_val)->enc != RNODE_ENCODING(node_lit)->enc;
+ default:
+#ifdef UNIVERSAL_PARSER
+ abort();
+#else
+ rb_bug("unexpected node: %s, %s", ruby_node_name(type_val), ruby_node_name(type_lit));
+#endif
+ }
+}
+
+static st_index_t
+literal_hash(st_data_t a)
+{
+ NODE *node = (NODE *)a;
+ enum node_type type = nd_type(node);
+
+ switch (type) {
+ case NODE_INTEGER:
+ return rb_char_p_hash(RNODE_INTEGER(node)->val);
+ case NODE_FLOAT:
+ return rb_char_p_hash(RNODE_FLOAT(node)->val);
+ case NODE_RATIONAL:
+ return rb_char_p_hash(RNODE_RATIONAL(node)->val);
+ case NODE_IMAGINARY:
+ return rb_char_p_hash(RNODE_IMAGINARY(node)->val);
+ case NODE_STR:
+ return rb_parser_str_hash(RNODE_STR(node)->string);
+ case NODE_SYM:
+ return rb_parser_str_hash(RNODE_SYM(node)->string);
+ case NODE_REGX:
+ return rb_parser_str_hash(RNODE_REGX(node)->string);
+ case NODE_LINE:
+ return (st_index_t)node->nd_loc.beg_pos.lineno;
+ case NODE_FILE:
+ return rb_parser_str_hash(RNODE_FILE(node)->path);
+ case NODE_ENCODING:
+ return (st_index_t)RNODE_ENCODING(node)->enc;
+ default:
+#ifdef UNIVERSAL_PARSER
+ abort();
+#else
+ rb_bug("unexpected node: %s", ruby_node_name(type));
+#endif
+ }
+}
+
+static inline int
+parse_isascii(int c)
+{
+ return '\0' <= c && c <= '\x7f';
+}
+
+#undef ISASCII
+#define ISASCII parse_isascii
+
+static inline int
+parse_isspace(int c)
+{
+ return c == ' ' || ('\t' <= c && c <= '\r');
+}
+
+#undef ISSPACE
+#define ISSPACE parse_isspace
+
+static inline int
+parse_iscntrl(int c)
+{
+ return ('\0' <= c && c < ' ') || c == '\x7f';
+}
+
+#undef ISCNTRL
+#define ISCNTRL(c) parse_iscntrl(c)
+
+static inline int
+parse_isupper(int c)
+{
+ return 'A' <= c && c <= 'Z';
+}
+
+static inline int
+parse_islower(int c)
+{
+ return 'a' <= c && c <= 'z';
+}
+
+static inline int
+parse_isalpha(int c)
+{
+ return parse_isupper(c) || parse_islower(c);
+}
+
+#undef ISALPHA
+#define ISALPHA(c) parse_isalpha(c)
+
+static inline int
+parse_isdigit(int c)
+{
+ return '0' <= c && c <= '9';
+}
+
+#undef ISDIGIT
+#define ISDIGIT(c) parse_isdigit(c)
+
+static inline int
+parse_isalnum(int c)
+{
+ return parse_isalpha(c) || parse_isdigit(c);
+}
+
+#undef ISALNUM
+#define ISALNUM(c) parse_isalnum(c)
+
+static inline int
+parse_isxdigit(int c)
+{
+ return parse_isdigit(c) || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f');
+}
+
+#undef ISXDIGIT
+#define ISXDIGIT(c) parse_isxdigit(c)
+
+#include "parser_st.h"
+
+#undef STRCASECMP
+#define STRCASECMP rb_parser_st_locale_insensitive_strcasecmp
+
+#undef STRNCASECMP
+#define STRNCASECMP rb_parser_st_locale_insensitive_strncasecmp
+
+#ifdef RIPPER
+VALUE rb_ripper_none;
+#include "ripper_init.h"
+#endif
+
+enum rescue_context {
+ before_rescue,
+ after_rescue,
+ after_else,
+ after_ensure,
};
struct lex_context {
@@ -70,9 +311,13 @@ struct lex_context {
unsigned int in_argdef: 1;
unsigned int in_def: 1;
unsigned int in_class: 1;
- BITFIELD(enum shareability, shareable_constant_value, 2);
+ BITFIELD(enum rb_parser_shareability, shareable_constant_value, 2);
+ BITFIELD(enum rescue_context, in_rescue, 2);
};
+typedef struct RNode_DEF_TEMP rb_node_def_temp_t;
+typedef struct RNode_EXITS rb_node_exits_t;
+
#if defined(__GNUC__) && !defined(__clang__)
// Suppress "parameter passing for argument of type 'struct
// lex_context' changed" notes. `struct lex_context` is file scope,
@@ -87,8 +332,6 @@ RBIMPL_WARNING_POP()
#define NO_LEX_CTXT (struct lex_context){0}
-#define AREF(ary, i) RARRAY_AREF(ary, i)
-
#ifndef WARN_PAST_SCOPE
# define WARN_PAST_SCOPE 0
#endif
@@ -97,10 +340,6 @@ RBIMPL_WARNING_POP()
#define yydebug (p->debug) /* disable the global variable definition */
-#define YYMALLOC(size) rb_parser_malloc(p, (size))
-#define YYREALLOC(ptr, size) rb_parser_realloc(p, (ptr), (size))
-#define YYCALLOC(nelem, size) rb_parser_calloc(p, (nelem), (size))
-#define YYFREE(ptr) rb_parser_free(p, (ptr))
#define YYFPRINTF(out, ...) rb_parser_printf(p, __VA_ARGS__)
#define YY_LOCATION_PRINT(File, loc, p) \
rb_parser_printf(p, "%d.%d-%d.%d", \
@@ -141,44 +380,6 @@ RBIMPL_WARNING_POP()
{p->ruby_sourceline, (int)(p->lex.pcur - p->lex.pbeg)}, \
}
-enum lex_state_bits {
- EXPR_BEG_bit, /* ignore newline, +/- is a sign. */
- EXPR_END_bit, /* newline significant, +/- is an operator. */
- EXPR_ENDARG_bit, /* ditto, and unbound braces. */
- EXPR_ENDFN_bit, /* ditto, and unbound braces. */
- EXPR_ARG_bit, /* newline significant, +/- is an operator. */
- EXPR_CMDARG_bit, /* newline significant, +/- is an operator. */
- EXPR_MID_bit, /* newline significant, +/- is an operator. */
- EXPR_FNAME_bit, /* ignore newline, no reserved words. */
- EXPR_DOT_bit, /* right after `.', `&.' or `::', no reserved words. */
- EXPR_CLASS_bit, /* immediate after `class', no here document. */
- EXPR_LABEL_bit, /* flag bit, label is allowed. */
- EXPR_LABELED_bit, /* flag bit, just after a label. */
- EXPR_FITEM_bit, /* symbol literal as FNAME. */
- EXPR_MAX_STATE
-};
-/* examine combinations */
-enum lex_state_e {
-#define DEF_EXPR(n) EXPR_##n = (1 << EXPR_##n##_bit)
- DEF_EXPR(BEG),
- DEF_EXPR(END),
- DEF_EXPR(ENDARG),
- DEF_EXPR(ENDFN),
- DEF_EXPR(ARG),
- DEF_EXPR(CMDARG),
- DEF_EXPR(MID),
- DEF_EXPR(FNAME),
- DEF_EXPR(DOT),
- DEF_EXPR(CLASS),
- DEF_EXPR(LABEL),
- DEF_EXPR(LABELED),
- DEF_EXPR(FITEM),
- EXPR_VALUE = EXPR_BEG,
- EXPR_BEG_ANY = (EXPR_BEG | EXPR_MID | EXPR_CLASS),
- EXPR_ARG_ANY = (EXPR_ARG | EXPR_CMDARG),
- EXPR_END_ANY = (EXPR_END | EXPR_ENDARG | EXPR_ENDFN),
- EXPR_NONE = 0
-};
#define IS_lex_state_for(x, ls) ((x) & (ls))
#define IS_lex_state_all_for(x, ls) (((x) & (ls)) == (ls))
#define IS_lex_state(ls) IS_lex_state_for(p->lex.state, (ls))
@@ -227,11 +428,10 @@ struct local_vars {
struct vtable *past;
# endif
struct local_vars *prev;
-# ifndef RIPPER
struct {
NODE *outer, *inner, *current;
} numparam;
-# endif
+ NODE *it;
};
enum {
@@ -240,18 +440,6 @@ enum {
NUMPARAM_MAX = 9,
};
-#define NUMPARAM_ID_P(id) numparam_id_p(id)
-#define NUMPARAM_ID_TO_IDX(id) (unsigned int)(((id) >> ID_SCOPE_SHIFT) - (tNUMPARAM_1 - 1))
-#define NUMPARAM_IDX_TO_ID(idx) TOKEN2LOCALID((tNUMPARAM_1 - 1 + (idx)))
-static int
-numparam_id_p(ID id)
-{
- if (!is_local_id(id) || id < (tNUMPARAM_1 << ID_SCOPE_SHIFT)) return 0;
- unsigned int idx = NUMPARAM_ID_TO_IDX(id);
- return idx > 0 && idx <= NUMPARAM_MAX;
-}
-static void numparam_name(struct parser_params *p, ID id);
-
#define DVARS_INHERIT ((void*)1)
#define DVARS_TOPSCOPE NULL
#define DVARS_TERMINAL_P(tbl) ((tbl) == DVARS_INHERIT || (tbl) == DVARS_TOPSCOPE)
@@ -264,7 +452,24 @@ typedef struct token_info {
struct token_info *next;
} token_info;
-typedef struct rb_strterm_struct rb_strterm_t;
+typedef struct end_expect_token_locations {
+ const rb_code_position_t *pos;
+ struct end_expect_token_locations *prev;
+} end_expect_token_locations_t;
+
+typedef struct parser_string_buffer_elem {
+ struct parser_string_buffer_elem *next;
+ long len; /* Total length of allocated buf */
+ long used; /* Current usage of buf */
+ rb_parser_string_t *buf[FLEX_ARY_LEN];
+} parser_string_buffer_elem_t;
+
+typedef struct parser_string_buffer {
+ parser_string_buffer_elem_t *head;
+ parser_string_buffer_elem_t *last;
+} parser_string_buffer_t;
+
+#define AFTER_HEREDOC_WITHOUT_TERMINTOR ((rb_parser_string_t *)1)
/*
Structure of Lexer Buffer:
@@ -276,25 +481,20 @@ typedef struct rb_strterm_struct rb_strterm_t;
token
*/
struct parser_params {
- rb_imemo_tmpbuf_t *heap;
-
YYSTYPE *lval;
YYLTYPE *yylloc;
struct {
rb_strterm_t *strterm;
- VALUE (*gets)(struct parser_params*,VALUE);
- VALUE input;
- VALUE lastline;
- VALUE nextline;
+ VALUE (*gets)(struct parser_params*,rb_parser_input_data,int);
+ rb_parser_input_data input;
+ parser_string_buffer_t string_buffer;
+ rb_parser_string_t *lastline;
+ rb_parser_string_t *nextline;
const char *pbeg;
const char *pcur;
const char *pend;
const char *ptok;
- union {
- long ptr;
- VALUE (*call)(VALUE, int);
- } gets_;
enum lex_state_e state;
/* track the nest level of any parens "()[]{}" */
int paren_nest;
@@ -307,7 +507,6 @@ struct parser_params {
stack_type cmdarg_stack;
int tokidx;
int toksiz;
- int tokline;
int heredoc_end;
int heredoc_indent;
int heredoc_line_indent;
@@ -318,17 +517,17 @@ struct parser_params {
int line_count;
int ruby_sourceline; /* current line no. */
const char *ruby_sourcefile; /* current source file */
- VALUE ruby_sourcefile_string;
+ rb_parser_string_t *ruby_sourcefile_string;
rb_encoding *enc;
token_info *token_info;
- VALUE case_labels;
- VALUE compile_option;
+ st_table *case_labels;
+ rb_node_exits_t *exits;
VALUE debug_buffer;
VALUE debug_output;
struct {
- VALUE token;
+ rb_parser_string_t *token;
int beg_line;
int beg_col;
int end_line;
@@ -341,9 +540,20 @@ struct parser_params {
int node_id;
int max_numparam;
+ ID it_id;
struct lex_context ctxt;
+ NODE *eval_tree_begin;
+ NODE *eval_tree;
+ const struct rb_iseq_struct *parent_iseq;
+
+#ifdef UNIVERSAL_PARSER
+ const rb_parser_config_t *config;
+#endif
+ /* compile_option */
+ signed int frozen_string_literal:2; /* -1: not specified, 0: false, 1: true */
+
unsigned int command_start:1;
unsigned int eofp: 1;
unsigned int ruby__end__seen: 1;
@@ -364,45 +574,142 @@ struct parser_params {
unsigned int do_loop: 1;
unsigned int do_chomp: 1;
unsigned int do_split: 1;
- unsigned int keep_script_lines: 1;
unsigned int error_tolerant: 1;
unsigned int keep_tokens: 1;
- NODE *eval_tree_begin;
- NODE *eval_tree;
VALUE error_buffer;
- VALUE debug_lines;
- const struct rb_iseq_struct *parent_iseq;
- /* store specific keyword locations to generate dummy end token */
- VALUE end_expect_token_locations;
+ rb_parser_ary_t *debug_lines;
+ /*
+ * Store specific keyword locations to generate dummy end token.
+ * Refer to the tail of list element.
+ */
+ end_expect_token_locations_t *end_expect_token_locations;
/* id for terms */
int token_id;
/* Array for term tokens */
- VALUE tokens;
+ rb_parser_ary_t *tokens;
#else
/* Ripper only */
VALUE value;
VALUE result;
VALUE parsing_thread;
+ VALUE s_value; /* Token VALUE */
+ VALUE s_lvalue; /* VALUE generated by rule action (reduce) */
+ VALUE s_value_stack;
#endif
};
+#define NUMPARAM_ID_P(id) numparam_id_p(p, id)
+#define NUMPARAM_ID_TO_IDX(id) (unsigned int)(((id) >> ID_SCOPE_SHIFT) - (tNUMPARAM_1 - 1))
+#define NUMPARAM_IDX_TO_ID(idx) TOKEN2LOCALID((tNUMPARAM_1 - 1 + (idx)))
+static int
+numparam_id_p(struct parser_params *p, ID id)
+{
+ if (!is_local_id(id) || id < (tNUMPARAM_1 << ID_SCOPE_SHIFT)) return 0;
+ unsigned int idx = NUMPARAM_ID_TO_IDX(id);
+ return idx > 0 && idx <= NUMPARAM_MAX;
+}
+static void numparam_name(struct parser_params *p, ID id);
+
+#ifdef RIPPER
+static void
+after_shift(struct parser_params *p)
+{
+ if (p->debug) {
+ rb_parser_printf(p, "after-shift: %+"PRIsVALUE"\n", p->s_value);
+ }
+ rb_ary_push(p->s_value_stack, p->s_value);
+ p->s_value = Qnil;
+}
+
+static void
+before_reduce(int len, struct parser_params *p)
+{
+ // Initialize $$ with $1.
+ if (len) p->s_lvalue = rb_ary_entry(p->s_value_stack, -len);
+}
+
+static void
+after_reduce(int len, struct parser_params *p)
+{
+ for (int i = 0; i < len; i++) {
+ if (p->debug) {
+ rb_parser_printf(p, "after-reduce pop: %+"PRIsVALUE"\n", rb_ary_entry(p->s_value_stack, -1));
+ }
+ rb_ary_pop(p->s_value_stack);
+ }
+ if (p->debug) {
+ rb_parser_printf(p, "after-reduce push: %+"PRIsVALUE"\n", p->s_lvalue);
+ }
+ rb_ary_push(p->s_value_stack, p->s_lvalue);
+ p->s_lvalue = Qnil;
+}
+
+static void
+after_shift_error_token(struct parser_params *p)
+{
+ if (p->debug) {
+ rb_parser_printf(p, "after-shift-error-token:\n");
+ }
+ rb_ary_push(p->s_value_stack, Qnil);
+}
+
+static void
+after_pop_stack(int len, struct parser_params *p)
+{
+ for (int i = 0; i < len; i++) {
+ if (p->debug) {
+ rb_parser_printf(p, "after-pop-stack pop: %+"PRIsVALUE"\n", rb_ary_entry(p->s_value_stack, -1));
+ }
+ rb_ary_pop(p->s_value_stack);
+ }
+}
+#else
+static void
+after_shift(struct parser_params *p)
+{
+}
+
+static void
+before_reduce(int len, struct parser_params *p)
+{
+}
+
+static void
+after_reduce(int len, struct parser_params *p)
+{
+}
+
+static void
+after_shift_error_token(struct parser_params *p)
+{
+}
+
+static void
+after_pop_stack(int len, struct parser_params *p)
+{
+}
+#endif
+
#define intern_cstr(n,l,en) rb_intern3(n,l,en)
+#define STRING_NEW0() rb_parser_encoding_string_new(p,0,0,p->enc)
+
#define STR_NEW(ptr,len) rb_enc_str_new((ptr),(len),p->enc)
#define STR_NEW0() rb_enc_str_new(0,0,p->enc)
#define STR_NEW2(ptr) rb_enc_str_new((ptr),strlen(ptr),p->enc)
-#define STR_NEW3(ptr,len,e,func) parser_str_new((ptr),(len),(e),(func),p->enc)
+#define STR_NEW3(ptr,len,e,func) parser_str_new(p, (ptr),(len),(e),(func),p->enc)
#define TOK_INTERN() intern_cstr(tok(p), toklen(p), p->enc)
+#define VALID_SYMNAME_P(s, l, enc, type) (rb_enc_symname_type(s, l, enc, (1U<<(type))) == (int)(type))
-static st_table *
-push_pvtbl(struct parser_params *p)
+#ifndef RIPPER
+static inline bool
+end_with_newline_p(struct parser_params *p, VALUE str)
{
- st_table *tbl = p->pvtbl;
- p->pvtbl = st_init_numtable();
- return tbl;
+ return RSTRING_LEN(str) > 0 && RSTRING_END(str)[-1] == '\n';
}
+#endif
static void
pop_pvtbl(struct parser_params *p, st_table *tbl)
@@ -411,14 +718,6 @@ pop_pvtbl(struct parser_params *p, st_table *tbl)
p->pvtbl = tbl;
}
-static st_table *
-push_pktbl(struct parser_params *p)
-{
- st_table *tbl = p->pktbl;
- p->pktbl = 0;
- return tbl;
-}
-
static void
pop_pktbl(struct parser_params *p, st_table *tbl)
{
@@ -426,6 +725,59 @@ pop_pktbl(struct parser_params *p, st_table *tbl)
p->pktbl = tbl;
}
+#define STRING_BUF_DEFAULT_LEN 16
+
+static void
+string_buffer_init(struct parser_params *p)
+{
+ parser_string_buffer_t *buf = &p->lex.string_buffer;
+ const size_t size = offsetof(parser_string_buffer_elem_t, buf) + sizeof(rb_parser_string_t *) * STRING_BUF_DEFAULT_LEN;
+
+ buf->head = buf->last = xmalloc(size);
+ buf->head->len = STRING_BUF_DEFAULT_LEN;
+ buf->head->used = 0;
+ buf->head->next = NULL;
+}
+
+static void
+string_buffer_append(struct parser_params *p, rb_parser_string_t *str)
+{
+ parser_string_buffer_t *buf = &p->lex.string_buffer;
+
+ if (buf->head->used >= buf->head->len) {
+ parser_string_buffer_elem_t *elem;
+ long n = buf->head->len * 2;
+ const size_t size = offsetof(parser_string_buffer_elem_t, buf) + sizeof(rb_parser_string_t *) * n;
+
+ elem = xmalloc(size);
+ elem->len = n;
+ elem->used = 0;
+ elem->next = NULL;
+ buf->last->next = elem;
+ buf->last = elem;
+ }
+ buf->last->buf[buf->last->used++] = str;
+}
+
+static void rb_parser_string_free(rb_parser_t *p, rb_parser_string_t *str);
+
+static void
+string_buffer_free(struct parser_params *p)
+{
+ parser_string_buffer_elem_t *elem = p->lex.string_buffer.head;
+
+ while (elem) {
+ parser_string_buffer_elem_t *next_elem = elem->next;
+
+ for (long i = 0; i < elem->used; i++) {
+ rb_parser_string_free(p, elem->buf[i]);
+ }
+
+ xfree(elem);
+ elem = next_elem;
+ }
+}
+
#ifndef RIPPER
static void flush_debug_buffer(struct parser_params *p, VALUE out, VALUE str);
@@ -433,8 +785,15 @@ static void
debug_end_expect_token_locations(struct parser_params *p, const char *name)
{
if(p->debug) {
- VALUE mesg = rb_sprintf("%s: ", name);
- rb_str_catf(mesg, " %"PRIsVALUE"\n", p->end_expect_token_locations);
+ VALUE mesg = rb_sprintf("%s: [", name);
+ int i = 0;
+ for (end_expect_token_locations_t *loc = p->end_expect_token_locations; loc; loc = loc->prev) {
+ if (i > 0)
+ rb_str_cat_cstr(mesg, ", ");
+ rb_str_catf(mesg, "[%d, %d]", loc->pos->lineno, loc->pos->column);
+ i++;
+ }
+ rb_str_cat_cstr(mesg, "]\n");
flush_debug_buffer(p, p->debug_output, mesg);
}
}
@@ -442,197 +801,215 @@ debug_end_expect_token_locations(struct parser_params *p, const char *name)
static void
push_end_expect_token_locations(struct parser_params *p, const rb_code_position_t *pos)
{
- if(NIL_P(p->end_expect_token_locations)) return;
- rb_ary_push(p->end_expect_token_locations, rb_ary_new_from_args(2, INT2NUM(pos->lineno), INT2NUM(pos->column)));
+ if(!p->error_tolerant) return;
+
+ end_expect_token_locations_t *locations;
+ locations = ALLOC(end_expect_token_locations_t);
+ locations->pos = pos;
+ locations->prev = p->end_expect_token_locations;
+ p->end_expect_token_locations = locations;
+
debug_end_expect_token_locations(p, "push_end_expect_token_locations");
}
static void
pop_end_expect_token_locations(struct parser_params *p)
{
- if(NIL_P(p->end_expect_token_locations)) return;
- rb_ary_pop(p->end_expect_token_locations);
+ if(!p->end_expect_token_locations) return;
+
+ end_expect_token_locations_t *locations = p->end_expect_token_locations->prev;
+ ruby_sized_xfree(p->end_expect_token_locations, sizeof(end_expect_token_locations_t));
+ p->end_expect_token_locations = locations;
+
debug_end_expect_token_locations(p, "pop_end_expect_token_locations");
}
-static VALUE
+static end_expect_token_locations_t *
peek_end_expect_token_locations(struct parser_params *p)
{
- if(NIL_P(p->end_expect_token_locations)) return Qnil;
- return rb_ary_last(0, 0, p->end_expect_token_locations);
+ return p->end_expect_token_locations;
}
-static ID
-parser_token2id(enum yytokentype tok)
+static const char *
+parser_token2char(struct parser_params *p, enum yytokentype tok)
{
switch ((int) tok) {
-#define TOKEN2ID(tok) case tok: return rb_intern(#tok);
-#define TOKEN2ID2(tok, name) case tok: return rb_intern(name);
- TOKEN2ID2(' ', "words_sep")
- TOKEN2ID2('!', "!")
- TOKEN2ID2('%', "%");
- TOKEN2ID2('&', "&");
- TOKEN2ID2('*', "*");
- TOKEN2ID2('+', "+");
- TOKEN2ID2('-', "-");
- TOKEN2ID2('/', "/");
- TOKEN2ID2('<', "<");
- TOKEN2ID2('=', "=");
- TOKEN2ID2('>', ">");
- TOKEN2ID2('?', "?");
- TOKEN2ID2('^', "^");
- TOKEN2ID2('|', "|");
- TOKEN2ID2('~', "~");
- TOKEN2ID2(':', ":");
- TOKEN2ID2(',', ",");
- TOKEN2ID2('.', ".");
- TOKEN2ID2(';', ";");
- TOKEN2ID2('`', "`");
- TOKEN2ID2('\n', "nl");
- TOKEN2ID2('{', "{");
- TOKEN2ID2('}', "}");
- TOKEN2ID2('[', "[");
- TOKEN2ID2(']', "]");
- TOKEN2ID2('(', "(");
- TOKEN2ID2(')', ")");
- TOKEN2ID2('\\', "backslash");
- TOKEN2ID(keyword_class);
- TOKEN2ID(keyword_module);
- TOKEN2ID(keyword_def);
- TOKEN2ID(keyword_undef);
- TOKEN2ID(keyword_begin);
- TOKEN2ID(keyword_rescue);
- TOKEN2ID(keyword_ensure);
- TOKEN2ID(keyword_end);
- TOKEN2ID(keyword_if);
- TOKEN2ID(keyword_unless);
- TOKEN2ID(keyword_then);
- TOKEN2ID(keyword_elsif);
- TOKEN2ID(keyword_else);
- TOKEN2ID(keyword_case);
- TOKEN2ID(keyword_when);
- TOKEN2ID(keyword_while);
- TOKEN2ID(keyword_until);
- TOKEN2ID(keyword_for);
- TOKEN2ID(keyword_break);
- TOKEN2ID(keyword_next);
- TOKEN2ID(keyword_redo);
- TOKEN2ID(keyword_retry);
- TOKEN2ID(keyword_in);
- TOKEN2ID(keyword_do);
- TOKEN2ID(keyword_do_cond);
- TOKEN2ID(keyword_do_block);
- TOKEN2ID(keyword_do_LAMBDA);
- TOKEN2ID(keyword_return);
- TOKEN2ID(keyword_yield);
- TOKEN2ID(keyword_super);
- TOKEN2ID(keyword_self);
- TOKEN2ID(keyword_nil);
- TOKEN2ID(keyword_true);
- TOKEN2ID(keyword_false);
- TOKEN2ID(keyword_and);
- TOKEN2ID(keyword_or);
- TOKEN2ID(keyword_not);
- TOKEN2ID(modifier_if);
- TOKEN2ID(modifier_unless);
- TOKEN2ID(modifier_while);
- TOKEN2ID(modifier_until);
- TOKEN2ID(modifier_rescue);
- TOKEN2ID(keyword_alias);
- TOKEN2ID(keyword_defined);
- TOKEN2ID(keyword_BEGIN);
- TOKEN2ID(keyword_END);
- TOKEN2ID(keyword__LINE__);
- TOKEN2ID(keyword__FILE__);
- TOKEN2ID(keyword__ENCODING__);
- TOKEN2ID(tIDENTIFIER);
- TOKEN2ID(tFID);
- TOKEN2ID(tGVAR);
- TOKEN2ID(tIVAR);
- TOKEN2ID(tCONSTANT);
- TOKEN2ID(tCVAR);
- TOKEN2ID(tLABEL);
- TOKEN2ID(tINTEGER);
- TOKEN2ID(tFLOAT);
- TOKEN2ID(tRATIONAL);
- TOKEN2ID(tIMAGINARY);
- TOKEN2ID(tCHAR);
- TOKEN2ID(tNTH_REF);
- TOKEN2ID(tBACK_REF);
- TOKEN2ID(tSTRING_CONTENT);
- TOKEN2ID(tREGEXP_END);
- TOKEN2ID(tDUMNY_END);
- TOKEN2ID(tSP);
- TOKEN2ID(tUPLUS);
- TOKEN2ID(tUMINUS);
- TOKEN2ID(tPOW);
- TOKEN2ID(tCMP);
- TOKEN2ID(tEQ);
- TOKEN2ID(tEQQ);
- TOKEN2ID(tNEQ);
- TOKEN2ID(tGEQ);
- TOKEN2ID(tLEQ);
- TOKEN2ID(tANDOP);
- TOKEN2ID(tOROP);
- TOKEN2ID(tMATCH);
- TOKEN2ID(tNMATCH);
- TOKEN2ID(tDOT2);
- TOKEN2ID(tDOT3);
- TOKEN2ID(tBDOT2);
- TOKEN2ID(tBDOT3);
- TOKEN2ID(tAREF);
- TOKEN2ID(tASET);
- TOKEN2ID(tLSHFT);
- TOKEN2ID(tRSHFT);
- TOKEN2ID(tANDDOT);
- TOKEN2ID(tCOLON2);
- TOKEN2ID(tCOLON3);
- TOKEN2ID(tOP_ASGN);
- TOKEN2ID(tASSOC);
- TOKEN2ID(tLPAREN);
- TOKEN2ID(tLPAREN_ARG);
- TOKEN2ID(tRPAREN);
- TOKEN2ID(tLBRACK);
- TOKEN2ID(tLBRACE);
- TOKEN2ID(tLBRACE_ARG);
- TOKEN2ID(tSTAR);
- TOKEN2ID(tDSTAR);
- TOKEN2ID(tAMPER);
- TOKEN2ID(tLAMBDA);
- TOKEN2ID(tSYMBEG);
- TOKEN2ID(tSTRING_BEG);
- TOKEN2ID(tXSTRING_BEG);
- TOKEN2ID(tREGEXP_BEG);
- TOKEN2ID(tWORDS_BEG);
- TOKEN2ID(tQWORDS_BEG);
- TOKEN2ID(tSYMBOLS_BEG);
- TOKEN2ID(tQSYMBOLS_BEG);
- TOKEN2ID(tSTRING_END);
- TOKEN2ID(tSTRING_DEND);
- TOKEN2ID(tSTRING_DBEG);
- TOKEN2ID(tSTRING_DVAR);
- TOKEN2ID(tLAMBEG);
- TOKEN2ID(tLABEL_END);
- TOKEN2ID(tIGNORED_NL);
- TOKEN2ID(tCOMMENT);
- TOKEN2ID(tEMBDOC_BEG);
- TOKEN2ID(tEMBDOC);
- TOKEN2ID(tEMBDOC_END);
- TOKEN2ID(tHEREDOC_BEG);
- TOKEN2ID(tHEREDOC_END);
- TOKEN2ID(k__END__);
- TOKEN2ID(tLOWEST);
- TOKEN2ID(tUMINUS_NUM);
- TOKEN2ID(tLAST_TOKEN);
-#undef TOKEN2ID
-#undef TOKEN2ID2
+#define TOKEN2CHAR(tok) case tok: return (#tok);
+#define TOKEN2CHAR2(tok, name) case tok: return (name);
+ TOKEN2CHAR2(' ', "word_sep");
+ TOKEN2CHAR2('!', "!")
+ TOKEN2CHAR2('%', "%");
+ TOKEN2CHAR2('&', "&");
+ TOKEN2CHAR2('*', "*");
+ TOKEN2CHAR2('+', "+");
+ TOKEN2CHAR2('-', "-");
+ TOKEN2CHAR2('/', "/");
+ TOKEN2CHAR2('<', "<");
+ TOKEN2CHAR2('=', "=");
+ TOKEN2CHAR2('>', ">");
+ TOKEN2CHAR2('?', "?");
+ TOKEN2CHAR2('^', "^");
+ TOKEN2CHAR2('|', "|");
+ TOKEN2CHAR2('~', "~");
+ TOKEN2CHAR2(':', ":");
+ TOKEN2CHAR2(',', ",");
+ TOKEN2CHAR2('.', ".");
+ TOKEN2CHAR2(';', ";");
+ TOKEN2CHAR2('`', "`");
+ TOKEN2CHAR2('\n', "nl");
+ TOKEN2CHAR2('{', "\"{\"");
+ TOKEN2CHAR2('}', "\"}\"");
+ TOKEN2CHAR2('[', "\"[\"");
+ TOKEN2CHAR2(']', "\"]\"");
+ TOKEN2CHAR2('(', "\"(\"");
+ TOKEN2CHAR2(')', "\")\"");
+ TOKEN2CHAR2('\\', "backslash");
+ TOKEN2CHAR(keyword_class);
+ TOKEN2CHAR(keyword_module);
+ TOKEN2CHAR(keyword_def);
+ TOKEN2CHAR(keyword_undef);
+ TOKEN2CHAR(keyword_begin);
+ TOKEN2CHAR(keyword_rescue);
+ TOKEN2CHAR(keyword_ensure);
+ TOKEN2CHAR(keyword_end);
+ TOKEN2CHAR(keyword_if);
+ TOKEN2CHAR(keyword_unless);
+ TOKEN2CHAR(keyword_then);
+ TOKEN2CHAR(keyword_elsif);
+ TOKEN2CHAR(keyword_else);
+ TOKEN2CHAR(keyword_case);
+ TOKEN2CHAR(keyword_when);
+ TOKEN2CHAR(keyword_while);
+ TOKEN2CHAR(keyword_until);
+ TOKEN2CHAR(keyword_for);
+ TOKEN2CHAR(keyword_break);
+ TOKEN2CHAR(keyword_next);
+ TOKEN2CHAR(keyword_redo);
+ TOKEN2CHAR(keyword_retry);
+ TOKEN2CHAR(keyword_in);
+ TOKEN2CHAR(keyword_do);
+ TOKEN2CHAR(keyword_do_cond);
+ TOKEN2CHAR(keyword_do_block);
+ TOKEN2CHAR(keyword_do_LAMBDA);
+ TOKEN2CHAR(keyword_return);
+ TOKEN2CHAR(keyword_yield);
+ TOKEN2CHAR(keyword_super);
+ TOKEN2CHAR(keyword_self);
+ TOKEN2CHAR(keyword_nil);
+ TOKEN2CHAR(keyword_true);
+ TOKEN2CHAR(keyword_false);
+ TOKEN2CHAR(keyword_and);
+ TOKEN2CHAR(keyword_or);
+ TOKEN2CHAR(keyword_not);
+ TOKEN2CHAR(modifier_if);
+ TOKEN2CHAR(modifier_unless);
+ TOKEN2CHAR(modifier_while);
+ TOKEN2CHAR(modifier_until);
+ TOKEN2CHAR(modifier_rescue);
+ TOKEN2CHAR(keyword_alias);
+ TOKEN2CHAR(keyword_defined);
+ TOKEN2CHAR(keyword_BEGIN);
+ TOKEN2CHAR(keyword_END);
+ TOKEN2CHAR(keyword__LINE__);
+ TOKEN2CHAR(keyword__FILE__);
+ TOKEN2CHAR(keyword__ENCODING__);
+ TOKEN2CHAR(tIDENTIFIER);
+ TOKEN2CHAR(tFID);
+ TOKEN2CHAR(tGVAR);
+ TOKEN2CHAR(tIVAR);
+ TOKEN2CHAR(tCONSTANT);
+ TOKEN2CHAR(tCVAR);
+ TOKEN2CHAR(tLABEL);
+ TOKEN2CHAR(tINTEGER);
+ TOKEN2CHAR(tFLOAT);
+ TOKEN2CHAR(tRATIONAL);
+ TOKEN2CHAR(tIMAGINARY);
+ TOKEN2CHAR(tCHAR);
+ TOKEN2CHAR(tNTH_REF);
+ TOKEN2CHAR(tBACK_REF);
+ TOKEN2CHAR(tSTRING_CONTENT);
+ TOKEN2CHAR(tREGEXP_END);
+ TOKEN2CHAR(tDUMNY_END);
+ TOKEN2CHAR(tSP);
+ TOKEN2CHAR(tUPLUS);
+ TOKEN2CHAR(tUMINUS);
+ TOKEN2CHAR(tPOW);
+ TOKEN2CHAR(tCMP);
+ TOKEN2CHAR(tEQ);
+ TOKEN2CHAR(tEQQ);
+ TOKEN2CHAR(tNEQ);
+ TOKEN2CHAR(tGEQ);
+ TOKEN2CHAR(tLEQ);
+ TOKEN2CHAR(tANDOP);
+ TOKEN2CHAR(tOROP);
+ TOKEN2CHAR(tMATCH);
+ TOKEN2CHAR(tNMATCH);
+ TOKEN2CHAR(tDOT2);
+ TOKEN2CHAR(tDOT3);
+ TOKEN2CHAR(tBDOT2);
+ TOKEN2CHAR(tBDOT3);
+ TOKEN2CHAR(tAREF);
+ TOKEN2CHAR(tASET);
+ TOKEN2CHAR(tLSHFT);
+ TOKEN2CHAR(tRSHFT);
+ TOKEN2CHAR(tANDDOT);
+ TOKEN2CHAR(tCOLON2);
+ TOKEN2CHAR(tCOLON3);
+ TOKEN2CHAR(tOP_ASGN);
+ TOKEN2CHAR(tASSOC);
+ TOKEN2CHAR(tLPAREN);
+ TOKEN2CHAR(tLPAREN_ARG);
+ TOKEN2CHAR(tRPAREN);
+ TOKEN2CHAR(tLBRACK);
+ TOKEN2CHAR(tLBRACE);
+ TOKEN2CHAR(tLBRACE_ARG);
+ TOKEN2CHAR(tSTAR);
+ TOKEN2CHAR(tDSTAR);
+ TOKEN2CHAR(tAMPER);
+ TOKEN2CHAR(tLAMBDA);
+ TOKEN2CHAR(tSYMBEG);
+ TOKEN2CHAR(tSTRING_BEG);
+ TOKEN2CHAR(tXSTRING_BEG);
+ TOKEN2CHAR(tREGEXP_BEG);
+ TOKEN2CHAR(tWORDS_BEG);
+ TOKEN2CHAR(tQWORDS_BEG);
+ TOKEN2CHAR(tSYMBOLS_BEG);
+ TOKEN2CHAR(tQSYMBOLS_BEG);
+ TOKEN2CHAR(tSTRING_END);
+ TOKEN2CHAR(tSTRING_DEND);
+ TOKEN2CHAR(tSTRING_DBEG);
+ TOKEN2CHAR(tSTRING_DVAR);
+ TOKEN2CHAR(tLAMBEG);
+ TOKEN2CHAR(tLABEL_END);
+ TOKEN2CHAR(tIGNORED_NL);
+ TOKEN2CHAR(tCOMMENT);
+ TOKEN2CHAR(tEMBDOC_BEG);
+ TOKEN2CHAR(tEMBDOC);
+ TOKEN2CHAR(tEMBDOC_END);
+ TOKEN2CHAR(tHEREDOC_BEG);
+ TOKEN2CHAR(tHEREDOC_END);
+ TOKEN2CHAR(k__END__);
+ TOKEN2CHAR(tLOWEST);
+ TOKEN2CHAR(tUMINUS_NUM);
+ TOKEN2CHAR(tLAST_TOKEN);
+#undef TOKEN2CHAR
+#undef TOKEN2CHAR2
}
rb_bug("parser_token2id: unknown token %d", tok);
UNREACHABLE_RETURN(0);
}
+#else
+static void
+push_end_expect_token_locations(struct parser_params *p, const rb_code_position_t *pos)
+{
+}
+static void
+pop_end_expect_token_locations(struct parser_params *p)
+{
+}
#endif
RBIMPL_ATTR_NONNULL((1, 2, 3))
@@ -644,8 +1021,10 @@ static int parser_yyerror0(struct parser_params*, const char*);
#define yyerror(yylloc, p, msg) parser_yyerror(p, yylloc, msg)
#define token_flush(ptr) ((ptr)->lex.ptok = (ptr)->lex.pcur)
#define lex_goto_eol(p) ((p)->lex.pcur = (p)->lex.pend)
-#define lex_eol_p(p) ((p)->lex.pcur >= (p)->lex.pend)
-#define lex_eol_n_p(p,n) ((p)->lex.pcur+(n) >= (p)->lex.pend)
+#define lex_eol_p(p) lex_eol_n_p(p, 0)
+#define lex_eol_n_p(p,n) lex_eol_ptr_n_p(p, (p)->lex.pcur, n)
+#define lex_eol_ptr_p(p,ptr) lex_eol_ptr_n_p(p,ptr,0)
+#define lex_eol_ptr_n_p(p,ptr,n) ((ptr)+(n) >= (p)->lex.pend)
static void token_info_setup(token_info *ptinfo, const char *ptr, const rb_code_location_t *loc);
static void token_info_push(struct parser_params*, const char *token, const rb_code_location_t *loc);
@@ -661,44 +1040,290 @@ static void token_info_drop(struct parser_params *p, const char *token, rb_code_
#define token_column ((int)(p->lex.ptok - p->lex.pbeg))
-#define CALL_Q_P(q) ((q) == TOKEN2VAL(tANDDOT))
-#define NODE_CALL_Q(q) (CALL_Q_P(q) ? NODE_QCALL : NODE_CALL)
-#define NEW_QCALL(q,r,m,a,loc) NEW_NODE(NODE_CALL_Q(q),r,m,a,loc)
+#define CALL_Q_P(q) ((q) == tANDDOT)
+#define NEW_QCALL(q,r,m,a,loc) (CALL_Q_P(q) ? NEW_QCALL0(r,m,a,loc) : NEW_CALL(r,m,a,loc))
#define lambda_beginning_p() (p->lex.lpar_beg == p->lex.paren_nest)
static enum yytokentype yylex(YYSTYPE*, YYLTYPE*, struct parser_params*);
-#ifndef RIPPER
static inline void
rb_discard_node(struct parser_params *p, NODE *n)
{
rb_ast_delete_node(p->ast, n);
}
-#endif
-#ifdef RIPPER
-static inline VALUE
-add_mark_object(struct parser_params *p, VALUE obj)
+static rb_node_scope_t *rb_node_scope_new(struct parser_params *p, rb_node_args_t *nd_args, NODE *nd_body, const YYLTYPE *loc);
+static rb_node_scope_t *rb_node_scope_new2(struct parser_params *p, rb_ast_id_table_t *nd_tbl, rb_node_args_t *nd_args, NODE *nd_body, const YYLTYPE *loc);
+static rb_node_block_t *rb_node_block_new(struct parser_params *p, NODE *nd_head, const YYLTYPE *loc);
+static rb_node_if_t *rb_node_if_new(struct parser_params *p, NODE *nd_cond, NODE *nd_body, NODE *nd_else, const YYLTYPE *loc);
+static rb_node_unless_t *rb_node_unless_new(struct parser_params *p, NODE *nd_cond, NODE *nd_body, NODE *nd_else, const YYLTYPE *loc);
+static rb_node_case_t *rb_node_case_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, const YYLTYPE *loc);
+static rb_node_case2_t *rb_node_case2_new(struct parser_params *p, NODE *nd_body, const YYLTYPE *loc);
+static rb_node_case3_t *rb_node_case3_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, const YYLTYPE *loc);
+static rb_node_when_t *rb_node_when_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, NODE *nd_next, const YYLTYPE *loc);
+static rb_node_in_t *rb_node_in_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, NODE *nd_next, const YYLTYPE *loc);
+static rb_node_while_t *rb_node_while_new(struct parser_params *p, NODE *nd_cond, NODE *nd_body, long nd_state, const YYLTYPE *loc);
+static rb_node_until_t *rb_node_until_new(struct parser_params *p, NODE *nd_cond, NODE *nd_body, long nd_state, const YYLTYPE *loc);
+static rb_node_iter_t *rb_node_iter_new(struct parser_params *p, rb_node_args_t *nd_args, NODE *nd_body, const YYLTYPE *loc);
+static rb_node_for_t *rb_node_for_new(struct parser_params *p, NODE *nd_iter, NODE *nd_body, const YYLTYPE *loc);
+static rb_node_for_masgn_t *rb_node_for_masgn_new(struct parser_params *p, NODE *nd_var, const YYLTYPE *loc);
+static rb_node_retry_t *rb_node_retry_new(struct parser_params *p, const YYLTYPE *loc);
+static rb_node_begin_t *rb_node_begin_new(struct parser_params *p, NODE *nd_body, const YYLTYPE *loc);
+static rb_node_rescue_t *rb_node_rescue_new(struct parser_params *p, NODE *nd_head, NODE *nd_resq, NODE *nd_else, const YYLTYPE *loc);
+static rb_node_resbody_t *rb_node_resbody_new(struct parser_params *p, NODE *nd_args, NODE *nd_body, NODE *nd_next, const YYLTYPE *loc);
+static rb_node_ensure_t *rb_node_ensure_new(struct parser_params *p, NODE *nd_head, NODE *nd_ensr, const YYLTYPE *loc);
+static rb_node_and_t *rb_node_and_new(struct parser_params *p, NODE *nd_1st, NODE *nd_2nd, const YYLTYPE *loc);
+static rb_node_or_t *rb_node_or_new(struct parser_params *p, NODE *nd_1st, NODE *nd_2nd, const YYLTYPE *loc);
+static rb_node_masgn_t *rb_node_masgn_new(struct parser_params *p, NODE *nd_head, NODE *nd_args, const YYLTYPE *loc);
+static rb_node_lasgn_t *rb_node_lasgn_new(struct parser_params *p, ID nd_vid, NODE *nd_value, const YYLTYPE *loc);
+static rb_node_dasgn_t *rb_node_dasgn_new(struct parser_params *p, ID nd_vid, NODE *nd_value, const YYLTYPE *loc);
+static rb_node_gasgn_t *rb_node_gasgn_new(struct parser_params *p, ID nd_vid, NODE *nd_value, const YYLTYPE *loc);
+static rb_node_iasgn_t *rb_node_iasgn_new(struct parser_params *p, ID nd_vid, NODE *nd_value, const YYLTYPE *loc);
+static rb_node_cdecl_t *rb_node_cdecl_new(struct parser_params *p, ID nd_vid, NODE *nd_value, NODE *nd_else, enum rb_parser_shareability shareability, const YYLTYPE *loc);
+static rb_node_cvasgn_t *rb_node_cvasgn_new(struct parser_params *p, ID nd_vid, NODE *nd_value, const YYLTYPE *loc);
+static rb_node_op_asgn1_t *rb_node_op_asgn1_new(struct parser_params *p, NODE *nd_recv, ID nd_mid, NODE *index, NODE *rvalue, const YYLTYPE *loc);
+static rb_node_op_asgn2_t *rb_node_op_asgn2_new(struct parser_params *p, NODE *nd_recv, NODE *nd_value, ID nd_vid, ID nd_mid, bool nd_aid, const YYLTYPE *loc);
+static rb_node_op_asgn_or_t *rb_node_op_asgn_or_new(struct parser_params *p, NODE *nd_head, NODE *nd_value, const YYLTYPE *loc);
+static rb_node_op_asgn_and_t *rb_node_op_asgn_and_new(struct parser_params *p, NODE *nd_head, NODE *nd_value, const YYLTYPE *loc);
+static rb_node_op_cdecl_t *rb_node_op_cdecl_new(struct parser_params *p, NODE *nd_head, NODE *nd_value, ID nd_aid, enum rb_parser_shareability shareability, const YYLTYPE *loc);
+static rb_node_call_t *rb_node_call_new(struct parser_params *p, NODE *nd_recv, ID nd_mid, NODE *nd_args, const YYLTYPE *loc);
+static rb_node_opcall_t *rb_node_opcall_new(struct parser_params *p, NODE *nd_recv, ID nd_mid, NODE *nd_args, const YYLTYPE *loc);
+static rb_node_fcall_t *rb_node_fcall_new(struct parser_params *p, ID nd_mid, NODE *nd_args, const YYLTYPE *loc);
+static rb_node_vcall_t *rb_node_vcall_new(struct parser_params *p, ID nd_mid, const YYLTYPE *loc);
+static rb_node_qcall_t *rb_node_qcall_new(struct parser_params *p, NODE *nd_recv, ID nd_mid, NODE *nd_args, const YYLTYPE *loc);
+static rb_node_super_t *rb_node_super_new(struct parser_params *p, NODE *nd_args, const YYLTYPE *loc);
+static rb_node_zsuper_t * rb_node_zsuper_new(struct parser_params *p, const YYLTYPE *loc);
+static rb_node_list_t *rb_node_list_new(struct parser_params *p, NODE *nd_head, const YYLTYPE *loc);
+static rb_node_list_t *rb_node_list_new2(struct parser_params *p, NODE *nd_head, long nd_alen, NODE *nd_next, const YYLTYPE *loc);
+static rb_node_zlist_t *rb_node_zlist_new(struct parser_params *p, const YYLTYPE *loc);
+static rb_node_hash_t *rb_node_hash_new(struct parser_params *p, NODE *nd_head, const YYLTYPE *loc);
+static rb_node_return_t *rb_node_return_new(struct parser_params *p, NODE *nd_stts, const YYLTYPE *loc);
+static rb_node_yield_t *rb_node_yield_new(struct parser_params *p, NODE *nd_head, const YYLTYPE *loc);
+static rb_node_lvar_t *rb_node_lvar_new(struct parser_params *p, ID nd_vid, const YYLTYPE *loc);
+static rb_node_dvar_t *rb_node_dvar_new(struct parser_params *p, ID nd_vid, const YYLTYPE *loc);
+static rb_node_gvar_t *rb_node_gvar_new(struct parser_params *p, ID nd_vid, const YYLTYPE *loc);
+static rb_node_ivar_t *rb_node_ivar_new(struct parser_params *p, ID nd_vid, const YYLTYPE *loc);
+static rb_node_const_t *rb_node_const_new(struct parser_params *p, ID nd_vid, const YYLTYPE *loc);
+static rb_node_cvar_t *rb_node_cvar_new(struct parser_params *p, ID nd_vid, const YYLTYPE *loc);
+static rb_node_nth_ref_t *rb_node_nth_ref_new(struct parser_params *p, long nd_nth, const YYLTYPE *loc);
+static rb_node_back_ref_t *rb_node_back_ref_new(struct parser_params *p, long nd_nth, const YYLTYPE *loc);
+static rb_node_match2_t *rb_node_match2_new(struct parser_params *p, NODE *nd_recv, NODE *nd_value, const YYLTYPE *loc);
+static rb_node_match3_t *rb_node_match3_new(struct parser_params *p, NODE *nd_recv, NODE *nd_value, const YYLTYPE *loc);
+static rb_node_integer_t * rb_node_integer_new(struct parser_params *p, char* val, int base, const YYLTYPE *loc);
+static rb_node_float_t * rb_node_float_new(struct parser_params *p, char* val, const YYLTYPE *loc);
+static rb_node_rational_t * rb_node_rational_new(struct parser_params *p, char* val, int base, int seen_point, const YYLTYPE *loc);
+static rb_node_imaginary_t * rb_node_imaginary_new(struct parser_params *p, char* val, int base, int seen_point, enum rb_numeric_type, const YYLTYPE *loc);
+static rb_node_str_t *rb_node_str_new(struct parser_params *p, rb_parser_string_t *string, const YYLTYPE *loc);
+static rb_node_dstr_t *rb_node_dstr_new0(struct parser_params *p, rb_parser_string_t *string, long nd_alen, NODE *nd_next, const YYLTYPE *loc);
+static rb_node_dstr_t *rb_node_dstr_new(struct parser_params *p, rb_parser_string_t *string, const YYLTYPE *loc);
+static rb_node_xstr_t *rb_node_xstr_new(struct parser_params *p, rb_parser_string_t *string, const YYLTYPE *loc);
+static rb_node_dxstr_t *rb_node_dxstr_new(struct parser_params *p, rb_parser_string_t *string, long nd_alen, NODE *nd_next, const YYLTYPE *loc);
+static rb_node_evstr_t *rb_node_evstr_new(struct parser_params *p, NODE *nd_body, const YYLTYPE *loc);
+static rb_node_regx_t *rb_node_regx_new(struct parser_params *p, rb_parser_string_t *string, int options, const YYLTYPE *loc);
+static rb_node_once_t *rb_node_once_new(struct parser_params *p, NODE *nd_body, const YYLTYPE *loc);
+static rb_node_args_t *rb_node_args_new(struct parser_params *p, const YYLTYPE *loc);
+static rb_node_args_aux_t *rb_node_args_aux_new(struct parser_params *p, ID nd_pid, int nd_plen, const YYLTYPE *loc);
+static rb_node_opt_arg_t *rb_node_opt_arg_new(struct parser_params *p, NODE *nd_body, const YYLTYPE *loc);
+static rb_node_kw_arg_t *rb_node_kw_arg_new(struct parser_params *p, NODE *nd_body, const YYLTYPE *loc);
+static rb_node_postarg_t *rb_node_postarg_new(struct parser_params *p, NODE *nd_1st, NODE *nd_2nd, const YYLTYPE *loc);
+static rb_node_argscat_t *rb_node_argscat_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, const YYLTYPE *loc);
+static rb_node_argspush_t *rb_node_argspush_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, const YYLTYPE *loc);
+static rb_node_splat_t *rb_node_splat_new(struct parser_params *p, NODE *nd_head, const YYLTYPE *loc);
+static rb_node_block_pass_t *rb_node_block_pass_new(struct parser_params *p, NODE *nd_body, const YYLTYPE *loc);
+static rb_node_defn_t *rb_node_defn_new(struct parser_params *p, ID nd_mid, NODE *nd_defn, const YYLTYPE *loc);
+static rb_node_defs_t *rb_node_defs_new(struct parser_params *p, NODE *nd_recv, ID nd_mid, NODE *nd_defn, const YYLTYPE *loc);
+static rb_node_alias_t *rb_node_alias_new(struct parser_params *p, NODE *nd_1st, NODE *nd_2nd, const YYLTYPE *loc);
+static rb_node_valias_t *rb_node_valias_new(struct parser_params *p, ID nd_alias, ID nd_orig, const YYLTYPE *loc);
+static rb_node_undef_t *rb_node_undef_new(struct parser_params *p, NODE *nd_undef, const YYLTYPE *loc);
+static rb_node_class_t *rb_node_class_new(struct parser_params *p, NODE *nd_cpath, NODE *nd_body, NODE *nd_super, const YYLTYPE *loc);
+static rb_node_module_t *rb_node_module_new(struct parser_params *p, NODE *nd_cpath, NODE *nd_body, const YYLTYPE *loc);
+static rb_node_sclass_t *rb_node_sclass_new(struct parser_params *p, NODE *nd_recv, NODE *nd_body, const YYLTYPE *loc);
+static rb_node_colon2_t *rb_node_colon2_new(struct parser_params *p, NODE *nd_head, ID nd_mid, const YYLTYPE *loc);
+static rb_node_colon3_t *rb_node_colon3_new(struct parser_params *p, ID nd_mid, const YYLTYPE *loc);
+static rb_node_dot2_t *rb_node_dot2_new(struct parser_params *p, NODE *nd_beg, NODE *nd_end, const YYLTYPE *loc);
+static rb_node_dot3_t *rb_node_dot3_new(struct parser_params *p, NODE *nd_beg, NODE *nd_end, const YYLTYPE *loc);
+static rb_node_self_t *rb_node_self_new(struct parser_params *p, const YYLTYPE *loc);
+static rb_node_nil_t *rb_node_nil_new(struct parser_params *p, const YYLTYPE *loc);
+static rb_node_true_t *rb_node_true_new(struct parser_params *p, const YYLTYPE *loc);
+static rb_node_false_t *rb_node_false_new(struct parser_params *p, const YYLTYPE *loc);
+static rb_node_errinfo_t *rb_node_errinfo_new(struct parser_params *p, const YYLTYPE *loc);
+static rb_node_defined_t *rb_node_defined_new(struct parser_params *p, NODE *nd_head, const YYLTYPE *loc);
+static rb_node_postexe_t *rb_node_postexe_new(struct parser_params *p, NODE *nd_body, const YYLTYPE *loc);
+static rb_node_sym_t *rb_node_sym_new(struct parser_params *p, VALUE str, const YYLTYPE *loc);
+static rb_node_dsym_t *rb_node_dsym_new(struct parser_params *p, rb_parser_string_t *string, long nd_alen, NODE *nd_next, const YYLTYPE *loc);
+static rb_node_attrasgn_t *rb_node_attrasgn_new(struct parser_params *p, NODE *nd_recv, ID nd_mid, NODE *nd_args, const YYLTYPE *loc);
+static rb_node_lambda_t *rb_node_lambda_new(struct parser_params *p, rb_node_args_t *nd_args, NODE *nd_body, const YYLTYPE *loc);
+static rb_node_aryptn_t *rb_node_aryptn_new(struct parser_params *p, NODE *pre_args, NODE *rest_arg, NODE *post_args, const YYLTYPE *loc);
+static rb_node_hshptn_t *rb_node_hshptn_new(struct parser_params *p, NODE *nd_pconst, NODE *nd_pkwargs, NODE *nd_pkwrestarg, const YYLTYPE *loc);
+static rb_node_fndptn_t *rb_node_fndptn_new(struct parser_params *p, NODE *pre_rest_arg, NODE *args, NODE *post_rest_arg, const YYLTYPE *loc);
+static rb_node_line_t *rb_node_line_new(struct parser_params *p, const YYLTYPE *loc);
+static rb_node_file_t *rb_node_file_new(struct parser_params *p, rb_parser_string_t *str, const YYLTYPE *loc);
+static rb_node_error_t *rb_node_error_new(struct parser_params *p, const YYLTYPE *loc);
+
+#define NEW_SCOPE(a,b,loc) (NODE *)rb_node_scope_new(p,a,b,loc)
+#define NEW_SCOPE2(t,a,b,loc) (NODE *)rb_node_scope_new2(p,t,a,b,loc)
+#define NEW_BLOCK(a,loc) (NODE *)rb_node_block_new(p,a,loc)
+#define NEW_IF(c,t,e,loc) (NODE *)rb_node_if_new(p,c,t,e,loc)
+#define NEW_UNLESS(c,t,e,loc) (NODE *)rb_node_unless_new(p,c,t,e,loc)
+#define NEW_CASE(h,b,loc) (NODE *)rb_node_case_new(p,h,b,loc)
+#define NEW_CASE2(b,loc) (NODE *)rb_node_case2_new(p,b,loc)
+#define NEW_CASE3(h,b,loc) (NODE *)rb_node_case3_new(p,h,b,loc)
+#define NEW_WHEN(c,t,e,loc) (NODE *)rb_node_when_new(p,c,t,e,loc)
+#define NEW_IN(c,t,e,loc) (NODE *)rb_node_in_new(p,c,t,e,loc)
+#define NEW_WHILE(c,b,n,loc) (NODE *)rb_node_while_new(p,c,b,n,loc)
+#define NEW_UNTIL(c,b,n,loc) (NODE *)rb_node_until_new(p,c,b,n,loc)
+#define NEW_ITER(a,b,loc) (NODE *)rb_node_iter_new(p,a,b,loc)
+#define NEW_FOR(i,b,loc) (NODE *)rb_node_for_new(p,i,b,loc)
+#define NEW_FOR_MASGN(v,loc) (NODE *)rb_node_for_masgn_new(p,v,loc)
+#define NEW_RETRY(loc) (NODE *)rb_node_retry_new(p,loc)
+#define NEW_BEGIN(b,loc) (NODE *)rb_node_begin_new(p,b,loc)
+#define NEW_RESCUE(b,res,e,loc) (NODE *)rb_node_rescue_new(p,b,res,e,loc)
+#define NEW_RESBODY(a,ex,n,loc) (NODE *)rb_node_resbody_new(p,a,ex,n,loc)
+#define NEW_ENSURE(b,en,loc) (NODE *)rb_node_ensure_new(p,b,en,loc)
+#define NEW_AND(f,s,loc) (NODE *)rb_node_and_new(p,f,s,loc)
+#define NEW_OR(f,s,loc) (NODE *)rb_node_or_new(p,f,s,loc)
+#define NEW_MASGN(l,r,loc) rb_node_masgn_new(p,l,r,loc)
+#define NEW_LASGN(v,val,loc) (NODE *)rb_node_lasgn_new(p,v,val,loc)
+#define NEW_DASGN(v,val,loc) (NODE *)rb_node_dasgn_new(p,v,val,loc)
+#define NEW_GASGN(v,val,loc) (NODE *)rb_node_gasgn_new(p,v,val,loc)
+#define NEW_IASGN(v,val,loc) (NODE *)rb_node_iasgn_new(p,v,val,loc)
+#define NEW_CDECL(v,val,path,share,loc) (NODE *)rb_node_cdecl_new(p,v,val,path,share,loc)
+#define NEW_CVASGN(v,val,loc) (NODE *)rb_node_cvasgn_new(p,v,val,loc)
+#define NEW_OP_ASGN1(r,id,idx,rval,loc) (NODE *)rb_node_op_asgn1_new(p,r,id,idx,rval,loc)
+#define NEW_OP_ASGN2(r,t,i,o,val,loc) (NODE *)rb_node_op_asgn2_new(p,r,val,i,o,t,loc)
+#define NEW_OP_ASGN_OR(i,val,loc) (NODE *)rb_node_op_asgn_or_new(p,i,val,loc)
+#define NEW_OP_ASGN_AND(i,val,loc) (NODE *)rb_node_op_asgn_and_new(p,i,val,loc)
+#define NEW_OP_CDECL(v,op,val,share,loc) (NODE *)rb_node_op_cdecl_new(p,v,val,op,share,loc)
+#define NEW_CALL(r,m,a,loc) (NODE *)rb_node_call_new(p,r,m,a,loc)
+#define NEW_OPCALL(r,m,a,loc) (NODE *)rb_node_opcall_new(p,r,m,a,loc)
+#define NEW_FCALL(m,a,loc) rb_node_fcall_new(p,m,a,loc)
+#define NEW_VCALL(m,loc) (NODE *)rb_node_vcall_new(p,m,loc)
+#define NEW_QCALL0(r,m,a,loc) (NODE *)rb_node_qcall_new(p,r,m,a,loc)
+#define NEW_SUPER(a,loc) (NODE *)rb_node_super_new(p,a,loc)
+#define NEW_ZSUPER(loc) (NODE *)rb_node_zsuper_new(p,loc)
+#define NEW_LIST(a,loc) (NODE *)rb_node_list_new(p,a,loc)
+#define NEW_LIST2(h,l,n,loc) (NODE *)rb_node_list_new2(p,h,l,n,loc)
+#define NEW_ZLIST(loc) (NODE *)rb_node_zlist_new(p,loc)
+#define NEW_HASH(a,loc) (NODE *)rb_node_hash_new(p,a,loc)
+#define NEW_RETURN(s,loc) (NODE *)rb_node_return_new(p,s,loc)
+#define NEW_YIELD(a,loc) (NODE *)rb_node_yield_new(p,a,loc)
+#define NEW_LVAR(v,loc) (NODE *)rb_node_lvar_new(p,v,loc)
+#define NEW_DVAR(v,loc) (NODE *)rb_node_dvar_new(p,v,loc)
+#define NEW_GVAR(v,loc) (NODE *)rb_node_gvar_new(p,v,loc)
+#define NEW_IVAR(v,loc) (NODE *)rb_node_ivar_new(p,v,loc)
+#define NEW_CONST(v,loc) (NODE *)rb_node_const_new(p,v,loc)
+#define NEW_CVAR(v,loc) (NODE *)rb_node_cvar_new(p,v,loc)
+#define NEW_NTH_REF(n,loc) (NODE *)rb_node_nth_ref_new(p,n,loc)
+#define NEW_BACK_REF(n,loc) (NODE *)rb_node_back_ref_new(p,n,loc)
+#define NEW_MATCH2(n1,n2,loc) (NODE *)rb_node_match2_new(p,n1,n2,loc)
+#define NEW_MATCH3(r,n2,loc) (NODE *)rb_node_match3_new(p,r,n2,loc)
+#define NEW_INTEGER(val, base,loc) (NODE *)rb_node_integer_new(p,val,base,loc)
+#define NEW_FLOAT(val,loc) (NODE *)rb_node_float_new(p,val,loc)
+#define NEW_RATIONAL(val,base,seen_point,loc) (NODE *)rb_node_rational_new(p,val,base,seen_point,loc)
+#define NEW_IMAGINARY(val,base,seen_point,numeric_type,loc) (NODE *)rb_node_imaginary_new(p,val,base,seen_point,numeric_type,loc)
+#define NEW_STR(s,loc) (NODE *)rb_node_str_new(p,s,loc)
+#define NEW_DSTR0(s,l,n,loc) (NODE *)rb_node_dstr_new0(p,s,l,n,loc)
+#define NEW_DSTR(s,loc) (NODE *)rb_node_dstr_new(p,s,loc)
+#define NEW_XSTR(s,loc) (NODE *)rb_node_xstr_new(p,s,loc)
+#define NEW_DXSTR(s,l,n,loc) (NODE *)rb_node_dxstr_new(p,s,l,n,loc)
+#define NEW_EVSTR(n,loc) (NODE *)rb_node_evstr_new(p,n,loc)
+#define NEW_REGX(str,opts,loc) (NODE *)rb_node_regx_new(p,str,opts,loc)
+#define NEW_ONCE(b,loc) (NODE *)rb_node_once_new(p,b,loc)
+#define NEW_ARGS(loc) rb_node_args_new(p,loc)
+#define NEW_ARGS_AUX(r,b,loc) rb_node_args_aux_new(p,r,b,loc)
+#define NEW_OPT_ARG(v,loc) rb_node_opt_arg_new(p,v,loc)
+#define NEW_KW_ARG(v,loc) rb_node_kw_arg_new(p,v,loc)
+#define NEW_POSTARG(i,v,loc) (NODE *)rb_node_postarg_new(p,i,v,loc)
+#define NEW_ARGSCAT(a,b,loc) (NODE *)rb_node_argscat_new(p,a,b,loc)
+#define NEW_ARGSPUSH(a,b,loc) (NODE *)rb_node_argspush_new(p,a,b,loc)
+#define NEW_SPLAT(a,loc) (NODE *)rb_node_splat_new(p,a,loc)
+#define NEW_BLOCK_PASS(b,loc) rb_node_block_pass_new(p,b,loc)
+#define NEW_DEFN(i,s,loc) (NODE *)rb_node_defn_new(p,i,s,loc)
+#define NEW_DEFS(r,i,s,loc) (NODE *)rb_node_defs_new(p,r,i,s,loc)
+#define NEW_ALIAS(n,o,loc) (NODE *)rb_node_alias_new(p,n,o,loc)
+#define NEW_VALIAS(n,o,loc) (NODE *)rb_node_valias_new(p,n,o,loc)
+#define NEW_UNDEF(i,loc) (NODE *)rb_node_undef_new(p,i,loc)
+#define NEW_CLASS(n,b,s,loc) (NODE *)rb_node_class_new(p,n,b,s,loc)
+#define NEW_MODULE(n,b,loc) (NODE *)rb_node_module_new(p,n,b,loc)
+#define NEW_SCLASS(r,b,loc) (NODE *)rb_node_sclass_new(p,r,b,loc)
+#define NEW_COLON2(c,i,loc) (NODE *)rb_node_colon2_new(p,c,i,loc)
+#define NEW_COLON3(i,loc) (NODE *)rb_node_colon3_new(p,i,loc)
+#define NEW_DOT2(b,e,loc) (NODE *)rb_node_dot2_new(p,b,e,loc)
+#define NEW_DOT3(b,e,loc) (NODE *)rb_node_dot3_new(p,b,e,loc)
+#define NEW_SELF(loc) (NODE *)rb_node_self_new(p,loc)
+#define NEW_NIL(loc) (NODE *)rb_node_nil_new(p,loc)
+#define NEW_TRUE(loc) (NODE *)rb_node_true_new(p,loc)
+#define NEW_FALSE(loc) (NODE *)rb_node_false_new(p,loc)
+#define NEW_ERRINFO(loc) (NODE *)rb_node_errinfo_new(p,loc)
+#define NEW_DEFINED(e,loc) (NODE *)rb_node_defined_new(p,e,loc)
+#define NEW_POSTEXE(b,loc) (NODE *)rb_node_postexe_new(p,b,loc)
+#define NEW_SYM(str,loc) (NODE *)rb_node_sym_new(p,str,loc)
+#define NEW_DSYM(s,l,n,loc) (NODE *)rb_node_dsym_new(p,s,l,n,loc)
+#define NEW_ATTRASGN(r,m,a,loc) (NODE *)rb_node_attrasgn_new(p,r,m,a,loc)
+#define NEW_LAMBDA(a,b,loc) (NODE *)rb_node_lambda_new(p,a,b,loc)
+#define NEW_ARYPTN(pre,r,post,loc) (NODE *)rb_node_aryptn_new(p,pre,r,post,loc)
+#define NEW_HSHPTN(c,kw,kwrest,loc) (NODE *)rb_node_hshptn_new(p,c,kw,kwrest,loc)
+#define NEW_FNDPTN(pre,a,post,loc) (NODE *)rb_node_fndptn_new(p,pre,a,post,loc)
+#define NEW_LINE(loc) (NODE *)rb_node_line_new(p,loc)
+#define NEW_FILE(str,loc) (NODE *)rb_node_file_new(p,str,loc)
+#define NEW_ENCODING(loc) (NODE *)rb_node_encoding_new(p,loc)
+#define NEW_ERROR(loc) (NODE *)rb_node_error_new(p,loc)
+
+enum internal_node_type {
+ NODE_INTERNAL_ONLY = NODE_LAST,
+ NODE_DEF_TEMP,
+ NODE_EXITS,
+ NODE_INTERNAL_LAST
+};
+
+static const char *
+parser_node_name(int node)
{
- if (!SPECIAL_CONST_P(obj)
- && !RB_TYPE_P(obj, T_NODE) /* Ripper jumbles NODE objects and other objects... */
- ) {
- rb_ast_add_mark_object(p->ast, obj);
+ switch (node) {
+ case NODE_DEF_TEMP:
+ return "NODE_DEF_TEMP";
+ case NODE_EXITS:
+ return "NODE_EXITS";
+ default:
+ return ruby_node_name(node);
}
- return obj;
}
-#else
-static NODE* node_newnode_with_locals(struct parser_params *, enum node_type, VALUE, VALUE, const rb_code_location_t*);
-#endif
-static NODE* node_newnode(struct parser_params *, enum node_type, VALUE, VALUE, VALUE, const rb_code_location_t*);
-#define rb_node_newnode(type, a1, a2, a3, loc) node_newnode(p, (type), (a1), (a2), (a3), (loc))
+/* This node is parse.y internal */
+struct RNode_DEF_TEMP {
+ NODE node;
+
+ /* for NODE_DEFN/NODE_DEFS */
+
+ struct RNode *nd_def;
+ ID nd_mid;
+
+ struct {
+ ID cur_arg;
+ int max_numparam;
+ NODE *numparam_save;
+ struct lex_context ctxt;
+ } save;
+};
+
+#define RNODE_DEF_TEMP(node) ((struct RNode_DEF_TEMP *)(node))
+
+static rb_node_break_t *rb_node_break_new(struct parser_params *p, NODE *nd_stts, const YYLTYPE *loc);
+static rb_node_next_t *rb_node_next_new(struct parser_params *p, NODE *nd_stts, const YYLTYPE *loc);
+static rb_node_redo_t *rb_node_redo_new(struct parser_params *p, const YYLTYPE *loc);
+static rb_node_def_temp_t *rb_node_def_temp_new(struct parser_params *p, const YYLTYPE *loc);
+static rb_node_def_temp_t *def_head_save(struct parser_params *p, rb_node_def_temp_t *n);
-/* Make a new temporal node, which should not be appeared in the
+#define NEW_BREAK(s,loc) (NODE *)rb_node_break_new(p,s,loc)
+#define NEW_NEXT(s,loc) (NODE *)rb_node_next_new(p,s,loc)
+#define NEW_REDO(loc) (NODE *)rb_node_redo_new(p,loc)
+#define NEW_DEF_TEMP(loc) rb_node_def_temp_new(p,loc)
+
+/* Make a new internal node, which should not be appeared in the
* result AST and does not have node_id and location. */
-static NODE* node_new_temporal(struct parser_params *p, enum node_type type, VALUE a0, VALUE a1, VALUE a2);
-#define NODE_NEW_TEMPORAL(t,a0,a1,a2) node_new_temporal(p, (t),(VALUE)(a0),(VALUE)(a1),(VALUE)(a2))
+static NODE* node_new_internal(struct parser_params *p, enum node_type type, size_t size, size_t alignment);
+#define NODE_NEW_INTERNAL(ndtype, type) (type *)node_new_internal(p, (enum node_type)(ndtype), sizeof(type), RUBY_ALIGNOF(type))
static NODE *nd_set_loc(NODE *nd, const YYLTYPE *loc);
@@ -714,11 +1339,10 @@ static void
anddot_multiple_assignment_check(struct parser_params* p, const YYLTYPE *loc, ID id)
{
if (id == tANDDOT) {
- yyerror1(loc, "&. inside multiple assignment destination");
+ yyerror1(loc, "&. inside multiple assignment destination");
}
}
-#ifndef RIPPER
static inline void
set_line_body(NODE *body, int line)
{
@@ -730,7 +1354,33 @@ set_line_body(NODE *body, int line)
}
}
+static void
+set_embraced_location(NODE *node, const rb_code_location_t *beg, const rb_code_location_t *end)
+{
+ RNODE_ITER(node)->nd_body->nd_loc = code_loc_gen(beg, end);
+ nd_set_line(node, beg->end_pos.lineno);
+}
+
+static NODE *
+last_expr_node(NODE *expr)
+{
+ while (expr) {
+ if (nd_type_p(expr, NODE_BLOCK)) {
+ expr = RNODE_BLOCK(RNODE_BLOCK(expr)->nd_end)->nd_head;
+ }
+ else if (nd_type_p(expr, NODE_BEGIN) && RNODE_BEGIN(expr)->nd_body) {
+ expr = RNODE_BEGIN(expr)->nd_body;
+ }
+ else {
+ break;
+ }
+ }
+ return expr;
+}
+
+#ifndef RIPPER
#define yyparse ruby_yyparse
+#endif
static NODE* cond(struct parser_params *p, NODE *node, const YYLTYPE *loc);
static NODE* method_cond(struct parser_params *p, NODE *node, const YYLTYPE *loc);
@@ -746,7 +1396,6 @@ static void fixpos(NODE*,NODE*);
static int value_expr_gen(struct parser_params*,NODE*);
static void void_expr(struct parser_params*,NODE*);
static NODE *remove_begin(NODE*);
-static NODE *remove_begin_all(NODE*);
#define value_expr(node) value_expr_gen(p, (node))
static NODE *void_stmts(struct parser_params*,NODE*);
static void reduce_nodes(struct parser_params*,NODE**);
@@ -761,6 +1410,7 @@ static NODE *rest_arg_append(struct parser_params *p, NODE *args, NODE *rest_arg
static NODE *literal_concat(struct parser_params*,NODE*,NODE*,const YYLTYPE*);
static NODE *new_evstr(struct parser_params*,NODE*,const YYLTYPE*);
static NODE *new_dstr(struct parser_params*,NODE*,const YYLTYPE*);
+static NODE *str2dstr(struct parser_params*,NODE*);
static NODE *evstr2dstr(struct parser_params*,NODE*);
static NODE *splat_array(NODE*);
static void mark_lvar_used(struct parser_params *p, NODE *rhs);
@@ -769,11 +1419,11 @@ static NODE *call_bin_op(struct parser_params*,NODE*,ID,NODE*,const YYLTYPE*,con
static NODE *call_uni_op(struct parser_params*,NODE*,ID,const YYLTYPE*,const YYLTYPE*);
static NODE *new_qcall(struct parser_params* p, ID atype, NODE *recv, ID mid, NODE *args, const YYLTYPE *op_loc, const YYLTYPE *loc);
static NODE *new_command_qcall(struct parser_params* p, ID atype, NODE *recv, ID mid, NODE *args, NODE *block, const YYLTYPE *op_loc, const YYLTYPE *loc);
-static NODE *method_add_block(struct parser_params*p, NODE *m, NODE *b, const YYLTYPE *loc) {b->nd_iter = m; b->nd_loc = *loc; return b;}
+static NODE *method_add_block(struct parser_params*p, NODE *m, NODE *b, const YYLTYPE *loc) {RNODE_ITER(b)->nd_iter = m; b->nd_loc = *loc; return b;}
static bool args_info_empty_p(struct rb_args_info *args);
-static NODE *new_args(struct parser_params*,NODE*,NODE*,ID,NODE*,NODE*,const YYLTYPE*);
-static NODE *new_args_tail(struct parser_params*,NODE*,ID,ID,const YYLTYPE*);
+static rb_node_args_t *new_args(struct parser_params*,rb_node_args_aux_t*,rb_node_opt_arg_t*,ID,rb_node_args_aux_t*,rb_node_args_t*,const YYLTYPE*);
+static rb_node_args_t *new_args_tail(struct parser_params*,rb_node_kw_arg_t*,ID,ID,const YYLTYPE*);
static NODE *new_array_pattern(struct parser_params *p, NODE *constant, NODE *pre_arg, NODE *aryptn, const YYLTYPE *loc);
static NODE *new_array_pattern_tail(struct parser_params *p, NODE *pre_args, int has_rest, NODE *rest_arg, NODE *post_args, const YYLTYPE *loc);
static NODE *new_find_pattern(struct parser_params *p, NODE *constant, NODE *fndptn, const YYLTYPE *loc);
@@ -781,12 +1431,12 @@ static NODE *new_find_pattern_tail(struct parser_params *p, NODE *pre_rest_arg,
static NODE *new_hash_pattern(struct parser_params *p, NODE *constant, NODE *hshptn, const YYLTYPE *loc);
static NODE *new_hash_pattern_tail(struct parser_params *p, NODE *kw_args, ID kw_rest_arg, const YYLTYPE *loc);
-static NODE *new_kw_arg(struct parser_params *p, NODE *k, const YYLTYPE *loc);
-static NODE *args_with_numbered(struct parser_params*,NODE*,int);
+static rb_node_kw_arg_t *new_kw_arg(struct parser_params *p, NODE *k, const YYLTYPE *loc);
+static rb_node_args_t *args_with_numbered(struct parser_params*,rb_node_args_t*,int,ID);
-static VALUE negate_lit(struct parser_params*, VALUE);
+static NODE* negate_lit(struct parser_params*, NODE*);
static NODE *ret_args(struct parser_params*,NODE*);
-static NODE *arg_blk_pass(NODE*,NODE*);
+static NODE *arg_blk_pass(NODE*,rb_node_block_pass_t*);
static NODE *new_yield(struct parser_params*,NODE*,const YYLTYPE*);
static NODE *dsym_node(struct parser_params*,NODE*,const YYLTYPE*);
@@ -807,8 +1457,8 @@ static NODE *new_bodystmt(struct parser_params *p, NODE *head, NODE *rescue, NOD
static NODE *const_decl(struct parser_params *p, NODE* path, const YYLTYPE *loc);
-static NODE *opt_arg_append(NODE*, NODE*);
-static NODE *kwd_append(NODE*, NODE*);
+static rb_node_opt_arg_t *opt_arg_append(rb_node_opt_arg_t*, rb_node_opt_arg_t*);
+static rb_node_kw_arg_t *kwd_append(rb_node_kw_arg_t*, rb_node_kw_arg_t*);
static NODE *new_hash(struct parser_params *p, NODE *hash, const YYLTYPE *loc);
static NODE *new_unique_key_hash(struct parser_params *p, NODE *hash, const YYLTYPE *loc);
@@ -827,92 +1477,34 @@ static NODE *match_op(struct parser_params*,NODE*,NODE*,const YYLTYPE*,const YYL
static rb_ast_id_table_t *local_tbl(struct parser_params*);
-static VALUE reg_compile(struct parser_params*, VALUE, int);
-static void reg_fragment_setenc(struct parser_params*, VALUE, int);
-static int reg_fragment_check(struct parser_params*, VALUE, int);
-static NODE *reg_named_capture_assign(struct parser_params* p, VALUE regexp, const YYLTYPE *loc);
+static VALUE reg_compile(struct parser_params*, rb_parser_string_t*, int);
+static void reg_fragment_setenc(struct parser_params*, rb_parser_string_t*, int);
+#define reg_fragment_check rb_parser_reg_fragment_check
+int reg_fragment_check(struct parser_params*, rb_parser_string_t*, int);
-static int literal_concat0(struct parser_params *p, VALUE head, VALUE tail);
+static int literal_concat0(struct parser_params *p, rb_parser_string_t *head, rb_parser_string_t *tail);
static NODE *heredoc_dedent(struct parser_params*,NODE*);
static void check_literal_when(struct parser_params *p, NODE *args, const YYLTYPE *loc);
-#define get_id(id) (id)
-#define get_value(val) (val)
-#define get_num(num) (num)
-#else /* RIPPER */
-#define NODE_RIPPER NODE_CDECL
-#define NEW_RIPPER(a,b,c,loc) (VALUE)NEW_CDECL(a,b,c,loc)
-#define NODE_RIPPER2 NODE_OP_CDECL
-#define NEW_RIPPER2(a,b,c,loc) (VALUE)NEW_OP_CDECL(a,c,b,loc)
-
-static inline int ripper_is_node_yylval(VALUE n);
-
-static inline VALUE
-ripper_new_yylval(struct parser_params *p, ID a, VALUE b, VALUE c)
-{
- if (ripper_is_node_yylval(c)) c = RNODE(c)->nd_cval;
- add_mark_object(p, b);
- add_mark_object(p, c);
- return NEW_RIPPER(a, b, c, &NULL_LOC);
-}
-
-static inline VALUE
-ripper_new_yylval2(struct parser_params *p, VALUE a, VALUE b, VALUE c)
-{
- add_mark_object(p, a);
- add_mark_object(p, b);
- add_mark_object(p, c);
- return NEW_RIPPER2(a, b, c, &NULL_LOC);
-}
-
-static inline int
-ripper_is_node_yylval(VALUE n)
-{
- return RB_TYPE_P(n, T_NODE) && nd_type_p(RNODE(n), NODE_RIPPER);
-}
-
-#define value_expr(node) ((void)(node))
-#define remove_begin(node) (node)
-#define void_stmts(p,x) (x)
-#define rb_dvar_defined(id, base) 0
-#define rb_local_defined(id, base) 0
-static ID ripper_get_id(VALUE);
-#define get_id(id) ripper_get_id(id)
-static VALUE ripper_get_value(VALUE);
-#define get_value(val) ripper_get_value(val)
-#define get_num(num) (int)get_id(num)
-static VALUE assignable(struct parser_params*,VALUE);
-static int id_is_var(struct parser_params *p, ID id);
-
-#define method_cond(p,node,loc) (node)
-#define call_bin_op(p, recv,id,arg1,op_loc,loc) dispatch3(binary, (recv), STATIC_ID2SYM(id), (arg1))
-#define match_op(p,node1,node2,op_loc,loc) call_bin_op(0, (node1), idEqTilde, (node2), op_loc, loc)
-#define call_uni_op(p, recv,id,op_loc,loc) dispatch2(unary, STATIC_ID2SYM(id), (recv))
-#define logop(p,id,node1,node2,op_loc,loc) call_bin_op(0, (node1), (id), (node2), op_loc, loc)
-
-#define new_nil(loc) Qnil
-
-static VALUE new_regexp(struct parser_params *, VALUE, VALUE, const YYLTYPE *);
-
-static VALUE const_decl(struct parser_params *p, VALUE path);
-
+#ifdef RIPPER
static VALUE var_field(struct parser_params *p, VALUE a);
-static VALUE assign_error(struct parser_params *p, const char *mesg, VALUE a);
-
-static VALUE parser_reg_compile(struct parser_params*, VALUE, int, VALUE *);
-
+#define get_value(idx) (rb_ary_entry(p->s_value_stack, idx))
+#define set_value(val) (p->s_lvalue = val)
+static VALUE defs(struct parser_params *p, VALUE head, VALUE args, VALUE bodystmt);
static VALUE backref_error(struct parser_params*, NODE *, VALUE);
-#endif /* !RIPPER */
-
-/* forward declaration */
-typedef struct rb_strterm_heredoc_struct rb_strterm_heredoc_t;
+static VALUE ripper_assignable(struct parser_params *p, ID id, VALUE lhs);
+static VALUE ripper_const_decl(struct parser_params *p, VALUE path);
+static VALUE ripper_heredoc_dedent(struct parser_params *p, int indent, VALUE array);
+static VALUE assign_error(struct parser_params *p, const char *mesg, VALUE a);
+static int id_is_var(struct parser_params *p, ID id);
+#endif
RUBY_SYMBOL_EXPORT_BEGIN
VALUE rb_parser_reg_compile(struct parser_params* p, VALUE str, int options);
-int rb_reg_fragment_setenc(struct parser_params*, VALUE, int);
+int rb_reg_fragment_setenc(struct parser_params*, rb_parser_string_t *, int);
enum lex_state_e rb_parser_trace_lex_state(struct parser_params *, enum lex_state_e, enum lex_state_e, int);
-VALUE rb_parser_lex_state_name(enum lex_state_e state);
+VALUE rb_parser_lex_state_name(struct parser_params *p, enum lex_state_e state);
void rb_parser_show_bitstack(struct parser_params *, stack_type, const char *, int);
PRINTF_ARGS(void rb_parser_fatal(struct parser_params *p, const char *fmt, ...), 2, 3);
YYLTYPE *rb_parser_set_location_from_strterm_heredoc(struct parser_params *p, rb_strterm_heredoc_t *here, YYLTYPE *yylloc);
@@ -921,15 +1513,12 @@ YYLTYPE *rb_parser_set_location_of_heredoc_end(struct parser_params *p, YYLTYPE
YYLTYPE *rb_parser_set_location_of_dummy_end(struct parser_params *p, YYLTYPE *yylloc);
YYLTYPE *rb_parser_set_location_of_none(struct parser_params *p, YYLTYPE *yylloc);
YYLTYPE *rb_parser_set_location(struct parser_params *p, YYLTYPE *yylloc);
+void ruby_show_error_line(struct parser_params *p, VALUE errbuf, const YYLTYPE *yylloc, int lineno, rb_parser_string_t *str);
RUBY_SYMBOL_EXPORT_END
static void error_duplicate_pattern_variable(struct parser_params *p, ID id, const YYLTYPE *loc);
static void error_duplicate_pattern_key(struct parser_params *p, ID id, const YYLTYPE *loc);
-#ifndef RIPPER
static ID formal_argument(struct parser_params*, ID);
-#else
-static ID formal_argument(struct parser_params*, VALUE);
-#endif
static ID shadowing_lvar(struct parser_params*,ID);
static void new_bv(struct parser_params*,ID);
@@ -939,19 +1528,20 @@ static void local_var(struct parser_params*, ID);
static void arg_var(struct parser_params*, ID);
static int local_id(struct parser_params *p, ID id);
static int local_id_ref(struct parser_params*, ID, ID **);
-#ifndef RIPPER
-static ID internal_id(struct parser_params*);
+#define internal_id rb_parser_internal_id
+ID internal_id(struct parser_params*);
static NODE *new_args_forward_call(struct parser_params*, NODE*, const YYLTYPE*, const YYLTYPE*);
-#endif
static int check_forwarding_args(struct parser_params*);
static void add_forwarding_args(struct parser_params *p);
+static void forwarding_arg_check(struct parser_params *p, ID arg, ID all, const char *var);
static const struct vtable *dyna_push(struct parser_params *);
static void dyna_pop(struct parser_params*, const struct vtable *);
static int dyna_in_block(struct parser_params*);
#define dyna_var(p, id) local_var(p, id)
static int dvar_defined(struct parser_params*, ID);
-static int dvar_defined_ref(struct parser_params*, ID, ID**);
+#define dvar_defined_ref rb_parser_dvar_defined_ref
+int dvar_defined_ref(struct parser_params*, ID, ID**);
static int dvar_curr(struct parser_params*,ID);
static int lvar_defined(struct parser_params*, ID);
@@ -959,18 +1549,17 @@ static int lvar_defined(struct parser_params*, ID);
static NODE *numparam_push(struct parser_params *p);
static void numparam_pop(struct parser_params *p, NODE *prev_inner);
-#ifdef RIPPER
-# define METHOD_NOT idNOT
-#else
-# define METHOD_NOT '!'
-#endif
+#define METHOD_NOT '!'
#define idFWD_REST '*'
#define idFWD_KWREST idPow /* Use simple "**", as tDSTAR is "**arg" */
#define idFWD_BLOCK '&'
#define idFWD_ALL idDot3
-#define FORWARD_ARGS_WITH_RUBY2_KEYWORDS
+#define arg_FWD_BLOCK idFWD_BLOCK
+#define RE_ONIG_OPTION_IGNORECASE 1
+#define RE_ONIG_OPTION_EXTEND (RE_ONIG_OPTION_IGNORECASE<<1)
+#define RE_ONIG_OPTION_MULTILINE (RE_ONIG_OPTION_EXTEND<<1)
#define RE_OPTION_ONCE (1<<16)
#define RE_OPTION_ENCODING_SHIFT 8
#define RE_OPTION_ENCODING(e) (((e)&0xff)<<RE_OPTION_ENCODING_SHIFT)
@@ -979,71 +1568,8 @@ static void numparam_pop(struct parser_params *p, NODE *prev_inner);
#define RE_OPTION_MASK 0xff
#define RE_OPTION_ARG_ENCODING_NONE 32
-/* structs for managing terminator of string literal and heredocment */
-typedef struct rb_strterm_literal_struct {
- union {
- VALUE dummy;
- long nest;
- } u0;
- union {
- VALUE dummy;
- long func; /* STR_FUNC_* (e.g., STR_FUNC_ESCAPE and STR_FUNC_EXPAND) */
- } u1;
- union {
- VALUE dummy;
- long paren; /* '(' of `%q(...)` */
- } u2;
- union {
- VALUE dummy;
- long term; /* ')' of `%q(...)` */
- } u3;
-} rb_strterm_literal_t;
-
-#define HERETERM_LENGTH_BITS ((SIZEOF_VALUE - 1) * CHAR_BIT - 1)
-
-struct rb_strterm_heredoc_struct {
- VALUE lastline; /* the string of line that contains `<<"END"` */
- long offset; /* the column of END in `<<"END"` */
- int sourceline; /* lineno of the line that contains `<<"END"` */
- unsigned length /* the length of END in `<<"END"` */
-#if HERETERM_LENGTH_BITS < SIZEOF_INT * CHAR_BIT
- : HERETERM_LENGTH_BITS
-# define HERETERM_LENGTH_MAX ((1U << HERETERM_LENGTH_BITS) - 1)
-#else
-# define HERETERM_LENGTH_MAX UINT_MAX
-#endif
- ;
-#if HERETERM_LENGTH_BITS < SIZEOF_INT * CHAR_BIT
- unsigned quote: 1;
- unsigned func: 8;
-#else
- uint8_t quote;
- uint8_t func;
-#endif
-};
-STATIC_ASSERT(rb_strterm_heredoc_t, sizeof(rb_strterm_heredoc_t) <= 4 * SIZEOF_VALUE);
-
-#define STRTERM_HEREDOC IMEMO_FL_USER0
-
-struct rb_strterm_struct {
- VALUE flags;
- union {
- rb_strterm_literal_t literal;
- rb_strterm_heredoc_t heredoc;
- } u;
-};
-
-#ifndef RIPPER
-void
-rb_strterm_mark(VALUE obj)
-{
- rb_strterm_t *strterm = (rb_strterm_t*)obj;
- if (RBASIC(obj)->flags & STRTERM_HEREDOC) {
- rb_strterm_heredoc_t *heredoc = &strterm->u.heredoc;
- rb_gc_mark(heredoc->lastline);
- }
-}
-#endif
+#define CHECK_LITERAL_WHEN (st_table *)1
+#define CASE_LABELS_ENABLED_P(case_labels) (case_labels && case_labels != CHECK_LITERAL_WHEN)
#define yytnamerr(yyres, yystr) (YYSIZE_T)rb_yytnamerr(p, yyres, yystr)
size_t rb_yytnamerr(struct parser_params *p, char *yyres, const char *yystr);
@@ -1060,12 +1586,11 @@ size_t rb_yytnamerr(struct parser_params *p, char *yyres, const char *yystr);
/****** Ripper *******/
#ifdef RIPPER
-#define RIPPER_VERSION "0.1.0"
-static inline VALUE intern_sym(const char *name);
+#include "eventids1.h"
+#include "eventids2.h"
-#include "eventids1.c"
-#include "eventids2.c"
+extern const struct ripper_parser_ids ripper_parser_ids;
static VALUE ripper_dispatch0(struct parser_params*,ID);
static VALUE ripper_dispatch1(struct parser_params*,ID,VALUE);
@@ -1074,7 +1599,7 @@ static VALUE ripper_dispatch3(struct parser_params*,ID,VALUE,VALUE,VALUE);
static VALUE ripper_dispatch4(struct parser_params*,ID,VALUE,VALUE,VALUE,VALUE);
static VALUE ripper_dispatch5(struct parser_params*,ID,VALUE,VALUE,VALUE,VALUE,VALUE);
static VALUE ripper_dispatch7(struct parser_params*,ID,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE);
-static void ripper_error(struct parser_params *p);
+void ripper_error(struct parser_params *p);
#define dispatch0(n) ripper_dispatch0(p, TOKEN_PASTE(ripper_id_, n))
#define dispatch1(n,a) ripper_dispatch1(p, TOKEN_PASTE(ripper_id_, n), (a))
@@ -1086,44 +1611,23 @@ static void ripper_error(struct parser_params *p);
#define yyparse ripper_yyparse
-#define ID2VAL(id) STATIC_ID2SYM(id)
-#define TOKEN2VAL(t) ID2VAL(TOKEN2ID(t))
-#define KWD2EID(t, v) ripper_new_yylval(p, keyword_##t, get_value(v), 0)
-
-#define params_new(pars, opts, rest, pars2, kws, kwrest, blk) \
- dispatch7(params, (pars), (opts), (rest), (pars2), (kws), (kwrest), (blk))
-
-#define escape_Qundef(x) ((x)==Qundef ? Qnil : (x))
+static void ripper_formal_argument(struct parser_params *p, ID id, VALUE lhs);
-static inline VALUE
-new_args(struct parser_params *p, VALUE pre_args, VALUE opt_args, VALUE rest_arg, VALUE post_args, VALUE tail, YYLTYPE *loc)
-{
- NODE *t = (NODE *)tail;
- VALUE kw_args = t->u1.value, kw_rest_arg = t->u2.value, block = t->u3.value;
- return params_new(pre_args, opt_args, rest_arg, post_args, kw_args, kw_rest_arg, escape_Qundef(block));
-}
-
-static inline VALUE
-new_args_tail(struct parser_params *p, VALUE kw_args, VALUE kw_rest_arg, VALUE block, YYLTYPE *loc)
-{
- NODE *t = rb_node_newnode(NODE_ARGS_AUX, kw_args, kw_rest_arg, block, &NULL_LOC);
- add_mark_object(p, kw_args);
- add_mark_object(p, kw_rest_arg);
- add_mark_object(p, block);
- return (VALUE)t;
-}
-
-static inline VALUE
-args_with_numbered(struct parser_params *p, VALUE args, int max_numparam)
+static VALUE
+ripper_new_args(struct parser_params *p, VALUE pre_args, VALUE opt_args, VALUE rest_arg, VALUE post_args, VALUE tail)
{
- return args;
+ VALUE kw_args = rb_ary_entry(tail, 0);
+ VALUE kw_rest_arg = rb_ary_entry(tail, 1);
+ VALUE block = rb_ary_entry(tail, 2);
+ return dispatch7(params, pre_args, opt_args, rest_arg, post_args, kw_args, kw_rest_arg, block);
}
static VALUE
-new_array_pattern(struct parser_params *p, VALUE constant, VALUE pre_arg, VALUE aryptn, const YYLTYPE *loc)
+ripper_new_array_pattern(struct parser_params *p, VALUE constant, VALUE pre_arg, VALUE aryptn)
{
- NODE *t = (NODE *)aryptn;
- VALUE pre_args = t->u1.value, rest_arg = t->u2.value, post_args = t->u3.value;
+ VALUE pre_args = rb_ary_entry(aryptn, 0);
+ VALUE rest_arg = rb_ary_entry(aryptn, 1);
+ VALUE post_args = rb_ary_entry(aryptn, 2);
if (!NIL_P(pre_arg)) {
if (!NIL_P(pre_args)) {
@@ -1137,71 +1641,61 @@ new_array_pattern(struct parser_params *p, VALUE constant, VALUE pre_arg, VALUE
}
static VALUE
-new_array_pattern_tail(struct parser_params *p, VALUE pre_args, VALUE has_rest, VALUE rest_arg, VALUE post_args, const YYLTYPE *loc)
+ripper_new_array_pattern_tail(struct parser_params *p, VALUE pre_args, VALUE rest_arg, VALUE post_args)
{
- return ripper_new_yylval2(p, pre_args, rest_arg, post_args);
+ return rb_ary_new_from_args(3, pre_args, rest_arg, post_args);
}
static VALUE
-new_find_pattern(struct parser_params *p, VALUE constant, VALUE fndptn, const YYLTYPE *loc)
+ripper_new_hash_pattern(struct parser_params *p, VALUE constant, VALUE hshptn)
{
- NODE *t = (NODE *)fndptn;
- VALUE pre_rest_arg = t->u1.value, args = t->u2.value, post_rest_arg = t->u3.value;
+ VALUE kw_args = rb_ary_entry(hshptn, 0);
+ VALUE kw_rest_arg = rb_ary_entry(hshptn, 1);
- return dispatch4(fndptn, constant, pre_rest_arg, args, post_rest_arg);
+ return dispatch3(hshptn, constant, kw_args, kw_rest_arg);
}
static VALUE
-new_find_pattern_tail(struct parser_params *p, VALUE pre_rest_arg, VALUE args, VALUE post_rest_arg, const YYLTYPE *loc)
+ripper_new_hash_pattern_tail(struct parser_params *p, VALUE kw_args, VALUE kw_rest_arg)
{
- return ripper_new_yylval2(p, pre_rest_arg, args, post_rest_arg);
+ if (kw_rest_arg) {
+ kw_rest_arg = dispatch1(var_field, kw_rest_arg);
+ }
+ else {
+ kw_rest_arg = Qnil;
+ }
+ return rb_ary_new_from_args(2, kw_args, kw_rest_arg);
}
-#define new_hash(p,h,l) rb_ary_new_from_args(0)
-
static VALUE
-new_unique_key_hash(struct parser_params *p, VALUE ary, const YYLTYPE *loc)
+ripper_new_find_pattern(struct parser_params *p, VALUE constant, VALUE fndptn)
{
- return ary;
-}
+ VALUE pre_rest_arg = rb_ary_entry(fndptn, 0);
+ VALUE args = rb_ary_entry(fndptn, 1);
+ VALUE post_rest_arg = rb_ary_entry(fndptn, 2);
-static VALUE
-new_hash_pattern(struct parser_params *p, VALUE constant, VALUE hshptn, const YYLTYPE *loc)
-{
- NODE *t = (NODE *)hshptn;
- VALUE kw_args = t->u1.value, kw_rest_arg = t->u2.value;
- return dispatch3(hshptn, constant, kw_args, kw_rest_arg);
+ return dispatch4(fndptn, constant, pre_rest_arg, args, post_rest_arg);
}
static VALUE
-new_hash_pattern_tail(struct parser_params *p, VALUE kw_args, VALUE kw_rest_arg, const YYLTYPE *loc)
+ripper_new_find_pattern_tail(struct parser_params *p, VALUE pre_rest_arg, VALUE args, VALUE post_rest_arg)
{
- if (kw_rest_arg) {
- kw_rest_arg = dispatch1(var_field, kw_rest_arg);
- }
- else {
- kw_rest_arg = Qnil;
- }
- return ripper_new_yylval2(p, kw_args, kw_rest_arg, Qnil);
+ return rb_ary_new_from_args(3, pre_rest_arg, args, post_rest_arg);
}
-#define new_defined(p,expr,loc) dispatch1(defined, (expr))
-
-static VALUE heredoc_dedent(struct parser_params*,VALUE);
+#define ID2VAL(id) STATIC_ID2SYM(id)
+#define TOKEN2VAL(t) ID2VAL(TOKEN2ID(t))
+#endif /* RIPPER */
-#else
-#define ID2VAL(id) (id)
-#define TOKEN2VAL(t) ID2VAL(t)
#define KWD2EID(t, v) keyword_##t
static NODE *
-set_defun_body(struct parser_params *p, NODE *n, NODE *args, NODE *body, const YYLTYPE *loc)
+new_scope_body(struct parser_params *p, rb_node_args_t *args, NODE *body, const YYLTYPE *loc)
{
body = remove_begin(body);
reduce_nodes(p, &body);
- n->nd_defn = NEW_SCOPE(args, body, loc);
- n->nd_loc = *loc;
- nd_set_line(n->nd_defn, loc->end_pos.lineno);
+ NODE *n = NEW_SCOPE(args, body, loc);
+ nd_set_line(n, loc->end_pos.lineno);
set_line_body(body, loc->beg_pos.lineno);
return n;
}
@@ -1216,27 +1710,35 @@ rescued_expr(struct parser_params *p, NODE *arg, NODE *rescue,
return NEW_RESCUE(arg, rescue, 0, &loc);
}
-#endif /* RIPPER */
+static NODE *add_block_exit(struct parser_params *p, NODE *node);
+static rb_node_exits_t *init_block_exit(struct parser_params *p);
+static rb_node_exits_t *allow_block_exit(struct parser_params *p);
+static void restore_block_exit(struct parser_params *p, rb_node_exits_t *exits);
+static void clear_block_exit(struct parser_params *p, bool error);
static void
-restore_defun(struct parser_params *p, NODE *name)
+next_rescue_context(struct lex_context *next, const struct lex_context *outer, enum rescue_context def)
{
- NODE *save = name->nd_next;
- YYSTYPE c = {.val = save->nd_cval};
- p->cur_arg = name->nd_vid;
- p->ctxt.in_def = c.ctxt.in_def;
- p->ctxt.shareable_constant_value = c.ctxt.shareable_constant_value;
- p->max_numparam = (int)save->nd_nth;
- numparam_pop(p, save->nd_head);
+ next->in_rescue = outer->in_rescue == after_rescue ? after_rescue : def;
}
static void
-endless_method_name(struct parser_params *p, NODE *defn, const YYLTYPE *loc)
+restore_defun(struct parser_params *p, rb_node_def_temp_t *temp)
+{
+ /* See: def_name action */
+ struct lex_context ctxt = temp->save.ctxt;
+ p->cur_arg = temp->save.cur_arg;
+ p->ctxt.in_def = ctxt.in_def;
+ p->ctxt.shareable_constant_value = ctxt.shareable_constant_value;
+ p->ctxt.in_rescue = ctxt.in_rescue;
+ p->max_numparam = temp->save.max_numparam;
+ numparam_pop(p, temp->save.numparam_save);
+ clear_block_exit(p, true);
+}
+
+static void
+endless_method_name(struct parser_params *p, ID mid, const YYLTYPE *loc)
{
-#ifdef RIPPER
- defn = defn->nd_defn;
-#endif
- ID mid = defn->nd_mid;
if (is_attrset_id(mid)) {
yyerror1(loc, "setter method cannot be defined in an endless method definition");
}
@@ -1253,13 +1755,21 @@ endless_method_name(struct parser_params *p, NODE *defn, const YYLTYPE *loc)
} \
} while (0)
+#define begin_definition(k, loc_beg, loc_end) \
+ do { \
+ if (!(p->ctxt.in_class = (k)[0] != 0)) { \
+ p->ctxt.in_def = 0; \
+ } \
+ else if (p->ctxt.in_def) { \
+ YYLTYPE loc = code_loc_gen(loc_beg, loc_end); \
+ yyerror1(&loc, k " definition in method body"); \
+ } \
+ local_push(p, 0); \
+ } while (0)
+
#ifndef RIPPER
-# define Qnone 0
-# define Qnull 0
# define ifndef_ripper(x) (x)
#else
-# define Qnone Qnil
-# define Qnull Qundef
# define ifndef_ripper(x)
#endif
@@ -1284,14 +1794,13 @@ endless_method_name(struct parser_params *p, NODE *defn, const YYLTYPE *loc)
# define rb_warning3L(l,fmt,a,b,c) WARNING_CALL(WARNING_ARGS_L(l, fmt, 4), (a), (b), (c))
# define rb_warning4L(l,fmt,a,b,c,d) WARNING_CALL(WARNING_ARGS_L(l, fmt, 5), (a), (b), (c), (d))
#ifdef RIPPER
-static ID id_warn, id_warning, id_gets, id_assoc;
+extern const ID id_warn, id_warning, id_gets, id_assoc;
# define ERR_MESG() STR_NEW2(mesg) /* to bypass Ripper DSL */
# define WARN_S_L(s,l) STR_NEW(s,l)
# define WARN_S(s) STR_NEW2(s)
# define WARN_I(i) INT2NUM(i)
# define WARN_ID(i) rb_id2str(i)
-# define WARN_IVAL(i) i
-# define PRIsWARN "s"
+# define PRIsWARN PRIsVALUE
# define rb_warn0L_experimental(l,fmt) WARN_CALL(WARN_ARGS_L(l, fmt, 1))
# define WARN_ARGS(fmt,n) p->value, id_warn, n, rb_usascii_str_new_lit(fmt)
# define WARN_ARGS_L(l,fmt,n) WARN_ARGS(fmt,n)
@@ -1307,14 +1816,12 @@ static ID id_warn, id_warning, id_gets, id_assoc;
# else
# define WARNING_CALL rb_funcall
# endif
-PRINTF_ARGS(static void ripper_compile_error(struct parser_params*, const char *fmt, ...), 2, 3);
# define compile_error ripper_compile_error
#else
# define WARN_S_L(s,l) s
# define WARN_S(s) s
# define WARN_I(i) i
# define WARN_ID(i) rb_id2name(i)
-# define WARN_IVAL(i) NUM2INT(i)
# define PRIsWARN PRIsVALUE
# define WARN_ARGS(fmt,n) WARN_ARGS_L(p->ruby_sourceline,fmt,n)
# define WARN_ARGS_L(l,fmt,n) p->ruby_sourcefile, (l), (fmt)
@@ -1323,125 +1830,998 @@ PRINTF_ARGS(static void ripper_compile_error(struct parser_params*, const char *
# define WARNING_ARGS(fmt,n) WARN_ARGS(fmt,n)
# define WARNING_ARGS_L(l,fmt,n) WARN_ARGS_L(l,fmt,n)
# define WARNING_CALL rb_compile_warning
-PRINTF_ARGS(static void parser_compile_error(struct parser_params*, const char *fmt, ...), 2, 3);
-# define compile_error parser_compile_error
+PRINTF_ARGS(static void parser_compile_error(struct parser_params*, const rb_code_location_t *loc, const char *fmt, ...), 3, 4);
+# define compile_error(p, ...) parser_compile_error(p, NULL, __VA_ARGS__)
#endif
+struct RNode_EXITS {
+ NODE node;
+
+ NODE *nd_chain; /* Assume NODE_BREAK, NODE_NEXT, NODE_REDO have nd_chain here */
+ NODE *nd_end;
+};
+
+#define RNODE_EXITS(node) ((rb_node_exits_t*)(node))
+
+static NODE *
+add_block_exit(struct parser_params *p, NODE *node)
+{
+ if (!node) {
+ compile_error(p, "unexpected null node");
+ return 0;
+ }
+ switch (nd_type(node)) {
+ case NODE_BREAK: case NODE_NEXT: case NODE_REDO: break;
+ default:
+ compile_error(p, "unexpected node: %s", parser_node_name(nd_type(node)));
+ return node;
+ }
+ if (!p->ctxt.in_defined) {
+ rb_node_exits_t *exits = p->exits;
+ if (exits) {
+ RNODE_EXITS(exits->nd_end)->nd_chain = node;
+ exits->nd_end = node;
+ }
+ }
+ return node;
+}
+
+static rb_node_exits_t *
+init_block_exit(struct parser_params *p)
+{
+ rb_node_exits_t *old = p->exits;
+ rb_node_exits_t *exits = NODE_NEW_INTERNAL(NODE_EXITS, rb_node_exits_t);
+ exits->nd_chain = 0;
+ exits->nd_end = RNODE(exits);
+ p->exits = exits;
+ return old;
+}
+
+static rb_node_exits_t *
+allow_block_exit(struct parser_params *p)
+{
+ rb_node_exits_t *exits = p->exits;
+ p->exits = 0;
+ return exits;
+}
+
+static void
+restore_block_exit(struct parser_params *p, rb_node_exits_t *exits)
+{
+ p->exits = exits;
+}
+
+static void
+clear_block_exit(struct parser_params *p, bool error)
+{
+ rb_node_exits_t *exits = p->exits;
+ if (!exits) return;
+ if (error && !compile_for_eval) {
+ for (NODE *e = RNODE(exits); (e = RNODE_EXITS(e)->nd_chain) != 0; ) {
+ switch (nd_type(e)) {
+ case NODE_BREAK:
+ yyerror1(&e->nd_loc, "Invalid break");
+ break;
+ case NODE_NEXT:
+ yyerror1(&e->nd_loc, "Invalid next");
+ break;
+ case NODE_REDO:
+ yyerror1(&e->nd_loc, "Invalid redo");
+ break;
+ default:
+ yyerror1(&e->nd_loc, "unexpected node");
+ goto end_checks; /* no nd_chain */
+ }
+ }
+ end_checks:;
+ }
+ exits->nd_end = RNODE(exits);
+ exits->nd_chain = 0;
+}
+
#define WARN_EOL(tok) \
(looking_at_eol_p(p) ? \
- (void)rb_warning0("`" tok "' at the end of line without an expression") : \
+ (void)rb_warning0("'" tok "' at the end of line without an expression") : \
(void)0)
static int looking_at_eol_p(struct parser_params *p);
+
+static NODE *
+get_nd_value(struct parser_params *p, NODE *node)
+{
+ switch (nd_type(node)) {
+ case NODE_GASGN:
+ return RNODE_GASGN(node)->nd_value;
+ case NODE_IASGN:
+ return RNODE_IASGN(node)->nd_value;
+ case NODE_LASGN:
+ return RNODE_LASGN(node)->nd_value;
+ case NODE_DASGN:
+ return RNODE_DASGN(node)->nd_value;
+ case NODE_MASGN:
+ return RNODE_MASGN(node)->nd_value;
+ case NODE_CVASGN:
+ return RNODE_CVASGN(node)->nd_value;
+ case NODE_CDECL:
+ return RNODE_CDECL(node)->nd_value;
+ default:
+ compile_error(p, "unexpected node: %s", parser_node_name(nd_type(node)));
+ return 0;
+ }
+}
+
+static void
+set_nd_value(struct parser_params *p, NODE *node, NODE *rhs)
+{
+ switch (nd_type(node)) {
+ case NODE_CDECL:
+ RNODE_CDECL(node)->nd_value = rhs;
+ break;
+ case NODE_GASGN:
+ RNODE_GASGN(node)->nd_value = rhs;
+ break;
+ case NODE_IASGN:
+ RNODE_IASGN(node)->nd_value = rhs;
+ break;
+ case NODE_LASGN:
+ RNODE_LASGN(node)->nd_value = rhs;
+ break;
+ case NODE_DASGN:
+ RNODE_DASGN(node)->nd_value = rhs;
+ break;
+ case NODE_MASGN:
+ RNODE_MASGN(node)->nd_value = rhs;
+ break;
+ case NODE_CVASGN:
+ RNODE_CVASGN(node)->nd_value = rhs;
+ break;
+ default:
+ compile_error(p, "unexpected node: %s", parser_node_name(nd_type(node)));
+ break;
+ }
+}
+
+static ID
+get_nd_vid(struct parser_params *p, NODE *node)
+{
+ switch (nd_type(node)) {
+ case NODE_CDECL:
+ return RNODE_CDECL(node)->nd_vid;
+ case NODE_GASGN:
+ return RNODE_GASGN(node)->nd_vid;
+ case NODE_IASGN:
+ return RNODE_IASGN(node)->nd_vid;
+ case NODE_LASGN:
+ return RNODE_LASGN(node)->nd_vid;
+ case NODE_DASGN:
+ return RNODE_DASGN(node)->nd_vid;
+ case NODE_CVASGN:
+ return RNODE_CVASGN(node)->nd_vid;
+ default:
+ compile_error(p, "unexpected node: %s", parser_node_name(nd_type(node)));
+ return 0;
+ }
+}
+
+static NODE *
+get_nd_args(struct parser_params *p, NODE *node)
+{
+ switch (nd_type(node)) {
+ case NODE_CALL:
+ return RNODE_CALL(node)->nd_args;
+ case NODE_OPCALL:
+ return RNODE_OPCALL(node)->nd_args;
+ case NODE_FCALL:
+ return RNODE_FCALL(node)->nd_args;
+ case NODE_QCALL:
+ return RNODE_QCALL(node)->nd_args;
+ case NODE_SUPER:
+ return RNODE_SUPER(node)->nd_args;
+ case NODE_VCALL:
+ case NODE_ZSUPER:
+ case NODE_YIELD:
+ case NODE_RETURN:
+ case NODE_BREAK:
+ case NODE_NEXT:
+ return 0;
+ default:
+ compile_error(p, "unexpected node: %s", parser_node_name(nd_type(node)));
+ return 0;
+ }
+}
+
+static st_index_t
+djb2(const uint8_t *str, size_t len)
+{
+ st_index_t hash = 5381;
+
+ for (size_t i = 0; i < len; i++) {
+ hash = ((hash << 5) + hash) + str[i];
+ }
+
+ return hash;
+}
+
+static st_index_t
+parser_memhash(const void *ptr, long len)
+{
+ return djb2(ptr, len);
+}
+
+#define PARSER_STRING_PTR(str) (str->ptr)
+#define PARSER_STRING_LEN(str) (str->len)
+#define PARSER_STRING_END(str) (&str->ptr[str->len])
+#define STRING_SIZE(str) ((size_t)str->len + 1)
+#define STRING_TERM_LEN(str) (1)
+#define STRING_TERM_FILL(str) (str->ptr[str->len] = '\0')
+#define PARSER_STRING_RESIZE_CAPA_TERM(p,str,capacity,termlen) do {\
+ SIZED_REALLOC_N(str->ptr, char, (size_t)total + termlen, STRING_SIZE(str)); \
+ str->len = total; \
+} while (0)
+#define STRING_SET_LEN(str, n) do { \
+ (str)->len = (n); \
+} while (0)
+#define PARSER_STRING_GETMEM(str, ptrvar, lenvar) \
+ ((ptrvar) = str->ptr, \
+ (lenvar) = str->len)
+
+static inline bool
+parser_string_end_with_newline_p(struct parser_params *p, rb_parser_string_t *str)
+{
+ return PARSER_STRING_LEN(str) > 0 && PARSER_STRING_END(str)[-1] == '\n';
+}
+
+static rb_parser_string_t *
+rb_parser_string_new(rb_parser_t *p, const char *ptr, long len)
+{
+ rb_parser_string_t *str;
+
+ if (len < 0) {
+ rb_bug("negative string size (or size too big): %ld", len);
+ }
+
+ str = xcalloc(1, sizeof(rb_parser_string_t));
+ str->ptr = xcalloc(len + 1, sizeof(char));
+
+ if (ptr) {
+ memcpy(PARSER_STRING_PTR(str), ptr, len);
+ }
+ STRING_SET_LEN(str, len);
+ STRING_TERM_FILL(str);
+ return str;
+}
+
+static rb_parser_string_t *
+rb_parser_encoding_string_new(rb_parser_t *p, const char *ptr, long len, rb_encoding *enc)
+{
+ rb_parser_string_t *str = rb_parser_string_new(p, ptr, len);
+ str->coderange = RB_PARSER_ENC_CODERANGE_UNKNOWN;
+ str->enc = enc;
+ return str;
+}
+
+#ifndef RIPPER
+static bool
+zero_filled(const char *s, int n)
+{
+ for (; n > 0; --n) {
+ if (*s++) return false;
+ }
+ return true;
+}
+
+static bool
+str_null_char(rb_parser_t *p, const char *s, long len, const int minlen, rb_encoding *enc)
+{
+ const char *e = s + len;
+
+ for (; s + minlen <= e; s += rb_enc_mbclen(s, e, enc)) {
+ if (zero_filled(s, minlen)) return true;
+ }
+ return false;
+}
+
+static bool
+cstr_null_check(rb_parser_t *p, const char *s, long len, rb_encoding *enc, int *w)
+{
+ const int minlen = rb_enc_mbminlen(enc);
+
+ if (minlen > 1) {
+ *w = 1;
+ return str_null_char(p, s, len, minlen, enc);
+ }
+ else {
+ *w = 0;
+ return (!s || memchr(s, 0, len));
+ }
+}
+
+rb_parser_string_t *
+rb_str_to_parser_string(rb_parser_t *p, VALUE str)
+{
+ /* Type check */
+ return rb_parser_encoding_string_new(p, RSTRING_PTR(str), RSTRING_LEN(str), rb_enc_get(str));
+}
+#endif
+
+static void
+rb_parser_string_free(rb_parser_t *p, rb_parser_string_t *str)
+{
+ if (!str) return;
+ xfree(PARSER_STRING_PTR(str));
+ xfree(str);
+}
+
+static st_index_t
+rb_parser_str_hash(rb_parser_string_t *str)
+{
+ return parser_memhash((const void *)PARSER_STRING_PTR(str), PARSER_STRING_LEN(str));
+}
+
+static st_index_t
+rb_char_p_hash(const char *c)
+{
+ return parser_memhash((const void *)c, strlen(c));
+}
+
+static size_t
+rb_parser_str_capacity(rb_parser_string_t *str, const int termlen)
+{
+ return PARSER_STRING_LEN(str);
+}
+
+#ifndef RIPPER
+static char *
+rb_parser_string_end(rb_parser_string_t *str)
+{
+ return &str->ptr[str->len];
+}
+#endif
+
+static void
+rb_parser_string_set_encoding(rb_parser_string_t *str, rb_encoding *enc)
+{
+ str->enc = enc;
+}
+
+static rb_encoding *
+rb_parser_str_get_encoding(rb_parser_string_t *str)
+{
+ return str->enc;
+}
+
+#ifndef RIPPER
+static bool
+PARSER_ENCODING_IS_ASCII8BIT(struct parser_params *p, rb_parser_string_t *str)
+{
+ return rb_parser_str_get_encoding(str) == rb_ascii8bit_encoding();
+}
+#endif
+
+static int
+PARSER_ENC_CODERANGE(rb_parser_string_t *str)
+{
+ return str->coderange;
+}
+
+static void
+PARSER_ENC_CODERANGE_SET(rb_parser_string_t *str, int coderange)
+{
+ str->coderange = coderange;
+}
+
+static void
+PARSER_ENCODING_CODERANGE_SET(rb_parser_string_t *str, rb_encoding *enc, enum rb_parser_string_coderange_type cr)
+{
+ rb_parser_string_set_encoding(str, enc);
+ PARSER_ENC_CODERANGE_SET(str, cr);
+}
+
+static void
+PARSER_ENC_CODERANGE_CLEAR(rb_parser_string_t *str)
+{
+ str->coderange = RB_PARSER_ENC_CODERANGE_UNKNOWN;
+}
+
+static bool
+PARSER_ENC_CODERANGE_ASCIIONLY(rb_parser_string_t *str)
+{
+ return PARSER_ENC_CODERANGE(str) == RB_PARSER_ENC_CODERANGE_7BIT;
+}
+
+static bool
+PARSER_ENC_CODERANGE_CLEAN_P(int cr)
+{
+ return cr == RB_PARSER_ENC_CODERANGE_7BIT || cr == RB_PARSER_ENC_CODERANGE_VALID;
+}
+
+static const char *
+rb_parser_search_nonascii(const char *p, const char *e)
+{
+ const char *s = p;
+
+ for (; s < e; s++) {
+ if (*s & 0x80) return s;
+ }
+
+ return NULL;
+}
+
+static int
+rb_parser_coderange_scan(struct parser_params *p, const char *ptr, long len, rb_encoding *enc)
+{
+ const char *e = ptr + len;
+
+ if (enc == rb_ascii8bit_encoding()) {
+ /* enc is ASCII-8BIT. ASCII-8BIT string never be broken. */
+ ptr = rb_parser_search_nonascii(ptr, e);
+ return ptr ? RB_PARSER_ENC_CODERANGE_VALID : RB_PARSER_ENC_CODERANGE_7BIT;
+ }
+
+ /* parser string encoding is always asciicompat */
+ ptr = rb_parser_search_nonascii(ptr, e);
+ if (!ptr) return RB_PARSER_ENC_CODERANGE_7BIT;
+ for (;;) {
+ int ret = rb_enc_precise_mbclen(ptr, e, enc);
+ if (!MBCLEN_CHARFOUND_P(ret)) return RB_PARSER_ENC_CODERANGE_BROKEN;
+ ptr += MBCLEN_CHARFOUND_LEN(ret);
+ if (ptr == e) break;
+ ptr = rb_parser_search_nonascii(ptr, e);
+ if (!ptr) break;
+ }
+
+ return RB_PARSER_ENC_CODERANGE_VALID;
+}
+
+static int
+rb_parser_enc_coderange_scan(struct parser_params *p, rb_parser_string_t *str, rb_encoding *enc)
+{
+ return rb_parser_coderange_scan(p, PARSER_STRING_PTR(str), PARSER_STRING_LEN(str), enc);
+}
+
+static int
+rb_parser_enc_str_coderange(struct parser_params *p, rb_parser_string_t *str)
+{
+ int cr = PARSER_ENC_CODERANGE(str);
+
+ if (cr == RB_PARSER_ENC_CODERANGE_UNKNOWN) {
+ cr = rb_parser_enc_coderange_scan(p, str, rb_parser_str_get_encoding(str));
+ PARSER_ENC_CODERANGE_SET(str, cr);
+ }
+
+ return cr;
+}
+
+static rb_parser_string_t *
+rb_parser_enc_associate(struct parser_params *p, rb_parser_string_t *str, rb_encoding *enc)
+{
+ if (rb_parser_str_get_encoding(str) == enc)
+ return str;
+ if (!PARSER_ENC_CODERANGE_ASCIIONLY(str) ||
+ !rb_enc_asciicompat(enc)) {
+ PARSER_ENC_CODERANGE_CLEAR(str);
+ }
+ rb_parser_string_set_encoding(str, enc);
+ return str;
+}
+
+static bool
+rb_parser_is_ascii_string(struct parser_params *p, rb_parser_string_t *str)
+{
+ return rb_parser_enc_str_coderange(p, str) == RB_PARSER_ENC_CODERANGE_7BIT;
+}
+
+static int
+rb_parser_enc_str_asciionly_p(struct parser_params *p, rb_parser_string_t *str)
+{
+ rb_encoding *enc = rb_parser_str_get_encoding(str);
+
+ if (!rb_enc_asciicompat(enc))
+ return FALSE;
+ else if (rb_parser_is_ascii_string(p, str))
+ return TRUE;
+ return FALSE;
+}
+
+static rb_encoding *
+rb_parser_enc_compatible_latter(struct parser_params *p, rb_parser_string_t *str1, rb_parser_string_t *str2, rb_encoding *enc1, rb_encoding *enc2)
+{
+ int cr1, cr2;
+
+ if (PARSER_STRING_LEN(str2) == 0)
+ return enc1;
+ if (PARSER_STRING_LEN(str1) == 0)
+ return (rb_enc_asciicompat(enc1) && rb_parser_enc_str_asciionly_p(p, str2)) ? enc1 : enc2;
+ if (!rb_enc_asciicompat(enc1) || !rb_enc_asciicompat(enc2)) {
+ return 0;
+ }
+
+ cr1 = rb_parser_enc_str_coderange(p, str1);
+ cr2 = rb_parser_enc_str_coderange(p, str2);
+
+ if (cr1 != cr2) {
+ if (cr1 == RB_PARSER_ENC_CODERANGE_7BIT) return enc2;
+ if (cr2 == RB_PARSER_ENC_CODERANGE_7BIT) return enc1;
+ }
+
+ if (cr2 == RB_PARSER_ENC_CODERANGE_7BIT) {
+ return enc1;
+ }
+
+ if (cr1 == RB_PARSER_ENC_CODERANGE_7BIT) {
+ return enc2;
+ }
+
+ return 0;
+}
+
+static rb_encoding *
+rb_parser_enc_compatible(struct parser_params *p, rb_parser_string_t *str1, rb_parser_string_t *str2)
+{
+ rb_encoding *enc1 = rb_parser_str_get_encoding(str1);
+ rb_encoding *enc2 = rb_parser_str_get_encoding(str2);
+
+ if (enc1 == NULL || enc2 == NULL)
+ return 0;
+
+ if (enc1 == enc2) {
+ return enc1;
+ }
+
+ return rb_parser_enc_compatible_latter(p, str1, str2, enc1, enc2);
+}
+
+static void
+rb_parser_str_modify(rb_parser_string_t *str)
+{
+ PARSER_ENC_CODERANGE_CLEAR(str);
+}
+
+static void
+rb_parser_str_set_len(struct parser_params *p, rb_parser_string_t *str, long len)
+{
+ long capa;
+ const int termlen = STRING_TERM_LEN(str);
+
+ if (len > (capa = (long)(rb_parser_str_capacity(str, termlen))) || len < 0) {
+ rb_bug("probable buffer overflow: %ld for %ld", len, capa);
+ }
+
+ int cr = PARSER_ENC_CODERANGE(str);
+ if (cr == RB_PARSER_ENC_CODERANGE_UNKNOWN) {
+ /* Leave unknown. */
+ }
+ else if (len > PARSER_STRING_LEN(str)) {
+ PARSER_ENC_CODERANGE_SET(str, RB_PARSER_ENC_CODERANGE_UNKNOWN);
+ }
+ else if (len < PARSER_STRING_LEN(str)) {
+ if (cr != RB_PARSER_ENC_CODERANGE_7BIT) {
+ /* ASCII-only string is keeping after truncated. Valid
+ * and broken may be invalid or valid, leave unknown. */
+ PARSER_ENC_CODERANGE_SET(str, RB_PARSER_ENC_CODERANGE_UNKNOWN);
+ }
+ }
+
+ STRING_SET_LEN(str, len);
+ STRING_TERM_FILL(str);
+}
+
+static rb_parser_string_t *
+rb_parser_str_buf_cat(struct parser_params *p, rb_parser_string_t *str, const char *ptr, long len)
+{
+ rb_parser_str_modify(str);
+ if (len == 0) return 0;
+
+ long total, olen, off = -1;
+ char *sptr;
+ const int termlen = STRING_TERM_LEN(str);
+
+ PARSER_STRING_GETMEM(str, sptr, olen);
+ if (ptr >= sptr && ptr <= sptr + olen) {
+ off = ptr - sptr;
+ }
+
+ if (olen > LONG_MAX - len) {
+ compile_error(p, "string sizes too big");
+ return 0;
+ }
+ total = olen + len;
+ PARSER_STRING_RESIZE_CAPA_TERM(p, str, total, termlen);
+ sptr = PARSER_STRING_PTR(str);
+ if (off != -1) {
+ ptr = sptr + off;
+ }
+ memcpy(sptr + olen, ptr, len);
+ STRING_SET_LEN(str, total);
+ STRING_TERM_FILL(str);
+
+ return str;
+}
+
+static rb_parser_string_t *
+rb_parser_enc_cr_str_buf_cat(struct parser_params *p, rb_parser_string_t *str, const char *ptr, long len,
+ rb_encoding *ptr_enc, int ptr_cr, int *ptr_cr_ret)
+{
+ int str_cr, res_cr;
+ rb_encoding *str_enc, *res_enc;
+
+ str_enc = rb_parser_str_get_encoding(str);
+ str_cr = PARSER_STRING_LEN(str) ? PARSER_ENC_CODERANGE(str) : RB_PARSER_ENC_CODERANGE_7BIT;
+
+ if (str_enc == ptr_enc) {
+ if (str_cr != RB_PARSER_ENC_CODERANGE_UNKNOWN && ptr_cr == RB_PARSER_ENC_CODERANGE_UNKNOWN) {
+ ptr_cr = rb_parser_coderange_scan(p, ptr, len, ptr_enc);
+ }
+ }
+ else {
+ /* parser string encoding is always asciicompat */
+ if (ptr_cr == RB_PARSER_ENC_CODERANGE_UNKNOWN) {
+ ptr_cr = rb_parser_coderange_scan(p, ptr, len, ptr_enc);
+ }
+ if (str_cr == RB_PARSER_ENC_CODERANGE_UNKNOWN) {
+ if (str_enc == rb_ascii8bit_encoding() || ptr_cr != RB_PARSER_ENC_CODERANGE_7BIT) {
+ str_cr = rb_parser_enc_str_coderange(p, str);
+ }
+ }
+ }
+ if (ptr_cr_ret)
+ *ptr_cr_ret = ptr_cr;
+
+ if (str_enc != ptr_enc &&
+ str_cr != RB_PARSER_ENC_CODERANGE_7BIT &&
+ ptr_cr != RB_PARSER_ENC_CODERANGE_7BIT) {
+ goto incompatible;
+ }
+
+ if (str_cr == RB_PARSER_ENC_CODERANGE_UNKNOWN) {
+ res_enc = str_enc;
+ res_cr = RB_PARSER_ENC_CODERANGE_UNKNOWN;
+ }
+ else if (str_cr == RB_PARSER_ENC_CODERANGE_7BIT) {
+ if (ptr_cr == RB_PARSER_ENC_CODERANGE_7BIT) {
+ res_enc = str_enc;
+ res_cr = RB_PARSER_ENC_CODERANGE_7BIT;
+ }
+ else {
+ res_enc = ptr_enc;
+ res_cr = ptr_cr;
+ }
+ }
+ else if (str_cr == RB_PARSER_ENC_CODERANGE_VALID) {
+ res_enc = str_enc;
+ if (PARSER_ENC_CODERANGE_CLEAN_P(ptr_cr))
+ res_cr = str_cr;
+ else
+ res_cr = ptr_cr;
+ }
+ else { /* str_cr == RB_PARSER_ENC_CODERANGE_BROKEN */
+ res_enc = str_enc;
+ res_cr = str_cr;
+ if (0 < len) res_cr = RB_PARSER_ENC_CODERANGE_UNKNOWN;
+ }
+
+ if (len < 0) {
+ compile_error(p, "negative string size (or size too big)");
+ }
+ rb_parser_str_buf_cat(p, str, ptr, len);
+ PARSER_ENCODING_CODERANGE_SET(str, res_enc, res_cr);
+ return str;
+
+ incompatible:
+ compile_error(p, "incompatible character encodings: %s and %s",
+ rb_enc_name(str_enc), rb_enc_name(ptr_enc));
+ UNREACHABLE_RETURN(0);
+
+}
+
+static rb_parser_string_t *
+rb_parser_enc_str_buf_cat(struct parser_params *p, rb_parser_string_t *str, const char *ptr, long len,
+ rb_encoding *ptr_enc)
+{
+ return rb_parser_enc_cr_str_buf_cat(p, str, ptr, len, ptr_enc, RB_PARSER_ENC_CODERANGE_UNKNOWN, NULL);
+}
+
+static rb_parser_string_t *
+rb_parser_str_buf_append(struct parser_params *p, rb_parser_string_t *str, rb_parser_string_t *str2)
+{
+ int str2_cr = rb_parser_enc_str_coderange(p, str2);
+
+ rb_parser_enc_cr_str_buf_cat(p, str, PARSER_STRING_PTR(str2), PARSER_STRING_LEN(str2),
+ rb_parser_str_get_encoding(str2), str2_cr, &str2_cr);
+
+ PARSER_ENC_CODERANGE_SET(str2, str2_cr);
+
+ return str;
+}
+
+static rb_parser_string_t *
+rb_parser_str_resize(struct parser_params *p, rb_parser_string_t *str, long len)
+{
+ if (len < 0) {
+ rb_bug("negative string size (or size too big)");
+ }
+
+ long slen = PARSER_STRING_LEN(str);
+
+ if (slen > len && PARSER_ENC_CODERANGE(str) != RB_PARSER_ENC_CODERANGE_7BIT) {
+ PARSER_ENC_CODERANGE_CLEAR(str);
+ }
+
+ {
+ long capa;
+ const int termlen = STRING_TERM_LEN(str);
+
+ if ((capa = slen) < len) {
+ SIZED_REALLOC_N(str->ptr, char, (size_t)len + termlen, STRING_SIZE(str));
+ }
+ else if (len == slen) return str;
+ STRING_SET_LEN(str, len);
+ STRING_TERM_FILL(str);
+ }
+ return str;
+}
+
+# define PARSER_ENC_STRING_GETMEM(str, ptrvar, lenvar, encvar) \
+ ((ptrvar) = str->ptr, \
+ (lenvar) = str->len, \
+ (encvar) = str->enc)
+
+static int
+rb_parser_string_hash_cmp(rb_parser_string_t *str1, rb_parser_string_t *str2)
+{
+ long len1, len2;
+ const char *ptr1, *ptr2;
+ rb_encoding *enc1, *enc2;
+
+ PARSER_ENC_STRING_GETMEM(str1, ptr1, len1, enc1);
+ PARSER_ENC_STRING_GETMEM(str2, ptr2, len2, enc2);
+
+ return (len1 != len2 ||
+ enc1 != enc2 ||
+ memcmp(ptr1, ptr2, len1) != 0);
+}
+
+#ifndef RIPPER
+static void
+rb_parser_ary_extend(rb_parser_t *p, rb_parser_ary_t *ary, long len)
+{
+ long i;
+ if (ary->capa < len) {
+ ary->capa = len;
+ ary->data = (rb_parser_ary_data *)xrealloc(ary->data, sizeof(rb_parser_ary_data) * len);
+ for (i = ary->len; i < len; i++) {
+ ary->data[i] = 0;
+ }
+ }
+}
+
+/*
+ * Do not call this directly.
+ * Use rb_parser_ary_new_capa_for_script_line() or rb_parser_ary_new_capa_for_ast_token() instead.
+ */
+static rb_parser_ary_t *
+parser_ary_new_capa(rb_parser_t *p, long len)
+{
+ if (len < 0) {
+ rb_bug("negative array size (or size too big): %ld", len);
+ }
+ rb_parser_ary_t *ary = xcalloc(1, sizeof(rb_parser_ary_t));
+ ary->len = 0;
+ ary->capa = len;
+ if (0 < len) {
+ ary->data = (rb_parser_ary_data *)xcalloc(len, sizeof(rb_parser_ary_data));
+ }
+ else {
+ ary->data = NULL;
+ }
+ return ary;
+}
+
+static rb_parser_ary_t *
+rb_parser_ary_new_capa_for_script_line(rb_parser_t *p, long len)
+{
+ rb_parser_ary_t *ary = parser_ary_new_capa(p, len);
+ ary->data_type = PARSER_ARY_DATA_SCRIPT_LINE;
+ return ary;
+}
+
+static rb_parser_ary_t *
+rb_parser_ary_new_capa_for_ast_token(rb_parser_t *p, long len)
+{
+ rb_parser_ary_t *ary = parser_ary_new_capa(p, len);
+ ary->data_type = PARSER_ARY_DATA_AST_TOKEN;
+ return ary;
+}
+
+/*
+ * Do not call this directly.
+ * Use rb_parser_ary_push_script_line() or rb_parser_ary_push_ast_token() instead.
+ */
+static rb_parser_ary_t *
+parser_ary_push(rb_parser_t *p, rb_parser_ary_t *ary, rb_parser_ary_data val)
+{
+ if (ary->len == ary->capa) {
+ rb_parser_ary_extend(p, ary, ary->len == 0 ? 1 : ary->len * 2);
+ }
+ ary->data[ary->len++] = val;
+ return ary;
+}
+
+static rb_parser_ary_t *
+rb_parser_ary_push_ast_token(rb_parser_t *p, rb_parser_ary_t *ary, rb_parser_ast_token_t *val)
+{
+ if (ary->data_type != PARSER_ARY_DATA_AST_TOKEN) {
+ rb_bug("unexpected rb_parser_ary_data_type: %d", ary->data_type);
+ }
+ return parser_ary_push(p, ary, val);
+}
+
+static rb_parser_ary_t *
+rb_parser_ary_push_script_line(rb_parser_t *p, rb_parser_ary_t *ary, rb_parser_string_t *val)
+{
+ if (ary->data_type != PARSER_ARY_DATA_SCRIPT_LINE) {
+ rb_bug("unexpected rb_parser_ary_data_type: %d", ary->data_type);
+ }
+ return parser_ary_push(p, ary, val);
+}
+
+static void
+rb_parser_ast_token_free(rb_parser_t *p, rb_parser_ast_token_t *token)
+{
+ if (!token) return;
+ rb_parser_string_free(p, token->str);
+ xfree(token);
+}
+
+static void
+rb_parser_ary_free(rb_parser_t *p, rb_parser_ary_t *ary)
+{
+ void (*free_func)(rb_parser_t *, rb_parser_ary_data) = NULL;
+ switch (ary->data_type) {
+ case PARSER_ARY_DATA_AST_TOKEN:
+ free_func = (void (*)(rb_parser_t *, rb_parser_ary_data))rb_parser_ast_token_free;
+ break;
+ case PARSER_ARY_DATA_SCRIPT_LINE:
+ free_func = (void (*)(rb_parser_t *, rb_parser_ary_data))rb_parser_string_free;
+ break;
+ default:
+ rb_bug("unexpected rb_parser_ary_data_type: %d", ary->data_type);
+ break;
+ }
+ for (long i = 0; i < ary->len; i++) {
+ free_func(p, ary->data[i]);
+ }
+ xfree(ary);
+}
+
+#endif /* !RIPPER */
%}
%expect 0
%define api.pure
%define parse.error verbose
%printer {
-#ifndef RIPPER
- if ($$) {
- rb_parser_printf(p, "%s", ruby_node_name(nd_type($$)));
+ if ((NODE *)$$ == (NODE *)-1) {
+ rb_parser_printf(p, "NODE_SPECIAL");
}
-#else
-#endif
-} <node>
+ else if ($$) {
+ rb_parser_printf(p, "%s", parser_node_name(nd_type(RNODE($$))));
+ }
+} <node> <node_fcall> <node_args> <node_args_aux> <node_opt_arg>
+ <node_kw_arg> <node_block_pass> <node_masgn> <node_def_temp> <node_exits>
%printer {
-#ifndef RIPPER
rb_parser_printf(p, "%"PRIsVALUE, rb_id2str($$));
-#else
- rb_parser_printf(p, "%"PRIsVALUE, RNODE($$)->nd_rval);
-#endif
-} tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR tLABEL tOP_ASGN
+} <id>
%printer {
-#ifndef RIPPER
- rb_parser_printf(p, "%+"PRIsVALUE, $$->nd_lit);
-#else
- rb_parser_printf(p, "%+"PRIsVALUE, get_value($$));
-#endif
+ switch (nd_type(RNODE($$))) {
+ case NODE_INTEGER:
+ rb_parser_printf(p, "%+"PRIsVALUE, rb_node_integer_literal_val($$));
+ break;
+ case NODE_FLOAT:
+ rb_parser_printf(p, "%+"PRIsVALUE, rb_node_float_literal_val($$));
+ break;
+ case NODE_RATIONAL:
+ rb_parser_printf(p, "%+"PRIsVALUE, rb_node_rational_literal_val($$));
+ break;
+ case NODE_IMAGINARY:
+ rb_parser_printf(p, "%+"PRIsVALUE, rb_node_imaginary_literal_val($$));
+ break;
+ default:
+ break;
+ }
} tINTEGER tFLOAT tRATIONAL tIMAGINARY tSTRING_CONTENT tCHAR
%printer {
-#ifndef RIPPER
- rb_parser_printf(p, "$%ld", $$->nd_nth);
-#else
- rb_parser_printf(p, "%"PRIsVALUE, $$);
-#endif
+ rb_parser_printf(p, "$%ld", RNODE_NTH_REF($$)->nd_nth);
} tNTH_REF
%printer {
-#ifndef RIPPER
- rb_parser_printf(p, "$%c", (int)$$->nd_nth);
-#else
- rb_parser_printf(p, "%"PRIsVALUE, $$);
-#endif
+ rb_parser_printf(p, "$%c", (int)RNODE_BACK_REF($$)->nd_nth);
} tBACK_REF
+%destructor {
+ if (CASE_LABELS_ENABLED_P($$)) st_free_table($$);
+} <labels>
+
%lex-param {struct parser_params *p}
%parse-param {struct parser_params *p}
%initial-action
{
RUBY_SET_YYLLOC_OF_NONE(@$);
};
+%after-shift after_shift
+%before-reduce before_reduce
+%after-reduce after_reduce
+%after-shift-error-token after_shift_error_token
+%after-pop-stack after_pop_stack
%union {
- VALUE val;
NODE *node;
+ rb_node_fcall_t *node_fcall;
+ rb_node_args_t *node_args;
+ rb_node_args_aux_t *node_args_aux;
+ rb_node_opt_arg_t *node_opt_arg;
+ rb_node_kw_arg_t *node_kw_arg;
+ rb_node_block_pass_t *node_block_pass;
+ rb_node_masgn_t *node_masgn;
+ rb_node_def_temp_t *node_def_temp;
+ rb_node_exits_t *node_exits;
ID id;
int num;
st_table *tbl;
+ st_table *labels;
const struct vtable *vars;
struct rb_strterm_struct *strterm;
struct lex_context ctxt;
}
%token <id>
- keyword_class "`class'"
- keyword_module "`module'"
- keyword_def "`def'"
- keyword_undef "`undef'"
- keyword_begin "`begin'"
- keyword_rescue "`rescue'"
- keyword_ensure "`ensure'"
- keyword_end "`end'"
- keyword_if "`if'"
- keyword_unless "`unless'"
- keyword_then "`then'"
- keyword_elsif "`elsif'"
- keyword_else "`else'"
- keyword_case "`case'"
- keyword_when "`when'"
- keyword_while "`while'"
- keyword_until "`until'"
- keyword_for "`for'"
- keyword_break "`break'"
- keyword_next "`next'"
- keyword_redo "`redo'"
- keyword_retry "`retry'"
- keyword_in "`in'"
- keyword_do "`do'"
- keyword_do_cond "`do' for condition"
- keyword_do_block "`do' for block"
- keyword_do_LAMBDA "`do' for lambda"
- keyword_return "`return'"
- keyword_yield "`yield'"
- keyword_super "`super'"
- keyword_self "`self'"
- keyword_nil "`nil'"
- keyword_true "`true'"
- keyword_false "`false'"
- keyword_and "`and'"
- keyword_or "`or'"
- keyword_not "`not'"
- modifier_if "`if' modifier"
- modifier_unless "`unless' modifier"
- modifier_while "`while' modifier"
- modifier_until "`until' modifier"
- modifier_rescue "`rescue' modifier"
- keyword_alias "`alias'"
- keyword_defined "`defined?'"
- keyword_BEGIN "`BEGIN'"
- keyword_END "`END'"
- keyword__LINE__ "`__LINE__'"
- keyword__FILE__ "`__FILE__'"
- keyword__ENCODING__ "`__ENCODING__'"
+ keyword_class "'class'"
+ keyword_module "'module'"
+ keyword_def "'def'"
+ keyword_undef "'undef'"
+ keyword_begin "'begin'"
+ keyword_rescue "'rescue'"
+ keyword_ensure "'ensure'"
+ keyword_end "'end'"
+ keyword_if "'if'"
+ keyword_unless "'unless'"
+ keyword_then "'then'"
+ keyword_elsif "'elsif'"
+ keyword_else "'else'"
+ keyword_case "'case'"
+ keyword_when "'when'"
+ keyword_while "'while'"
+ keyword_until "'until'"
+ keyword_for "'for'"
+ keyword_break "'break'"
+ keyword_next "'next'"
+ keyword_redo "'redo'"
+ keyword_retry "'retry'"
+ keyword_in "'in'"
+ keyword_do "'do'"
+ keyword_do_cond "'do' for condition"
+ keyword_do_block "'do' for block"
+ keyword_do_LAMBDA "'do' for lambda"
+ keyword_return "'return'"
+ keyword_yield "'yield'"
+ keyword_super "'super'"
+ keyword_self "'self'"
+ keyword_nil "'nil'"
+ keyword_true "'true'"
+ keyword_false "'false'"
+ keyword_and "'and'"
+ keyword_or "'or'"
+ keyword_not "'not'"
+ modifier_if "'if' modifier"
+ modifier_unless "'unless' modifier"
+ modifier_while "'while' modifier"
+ modifier_until "'until' modifier"
+ modifier_rescue "'rescue' modifier"
+ keyword_alias "'alias'"
+ keyword_defined "'defined?'"
+ keyword_BEGIN "'BEGIN'"
+ keyword_END "'END'"
+ keyword__LINE__ "'__LINE__'"
+ keyword__FILE__ "'__FILE__'"
+ keyword__ENCODING__ "'__ENCODING__'"
%token <id> tIDENTIFIER "local variable or method"
%token <id> tFID "method"
@@ -1464,26 +2844,38 @@ static int looking_at_eol_p(struct parser_params *p);
%type <node> singleton strings string string1 xstring regexp
%type <node> string_contents xstring_contents regexp_contents string_content
%type <node> words symbols symbol_list qwords qsymbols word_list qword_list qsym_list word
-%type <node> literal numeric simple_numeric ssym dsym symbol cpath def_name defn_head defs_head
+%type <node> literal numeric simple_numeric ssym dsym symbol cpath
+%type <node_def_temp> defn_head defs_head k_def
+%type <node_exits> block_open k_while k_until k_for allow_exits
%type <node> top_compstmt top_stmts top_stmt begin_block endless_arg endless_command
%type <node> bodystmt compstmt stmts stmt_or_begin stmt expr arg primary command command_call method_call
-%type <node> expr_value expr_value_do arg_value primary_value fcall rel_expr
+%type <node> expr_value expr_value_do arg_value primary_value rel_expr
+%type <node_fcall> fcall
%type <node> if_tail opt_else case_body case_args cases opt_rescue exc_list exc_var opt_ensure
-%type <node> args call_args opt_call_args
-%type <node> paren_args opt_paren_args args_tail opt_args_tail block_args_tail opt_block_args_tail
-%type <node> command_args aref_args opt_block_arg block_arg var_ref var_lhs
+%type <node> args arg_splat call_args opt_call_args
+%type <node> paren_args opt_paren_args
+%type <node_args> args_tail opt_args_tail block_args_tail opt_block_args_tail
+%type <node> command_args aref_args
+%type <node_block_pass> opt_block_arg block_arg
+%type <node> var_ref var_lhs
%type <node> command_rhs arg_rhs
%type <node> command_asgn mrhs mrhs_arg superclass block_call block_command
-%type <node> f_block_optarg f_block_opt
-%type <node> f_arglist f_opt_paren_args f_paren_args f_args f_arg f_arg_item
-%type <node> f_optarg f_marg f_marg_list f_margs f_rest_marg
+%type <node_opt_arg> f_block_optarg f_block_opt
+%type <node_args> f_arglist f_opt_paren_args f_paren_args f_args
+%type <node_args_aux> f_arg f_arg_item
+%type <node_opt_arg> f_optarg
+%type <node> f_marg f_marg_list f_rest_marg
+%type <node_masgn> f_margs
%type <node> assoc_list assocs assoc undef_list backref string_dvar for_var
-%type <node> block_param opt_block_param block_param_def f_opt
-%type <node> f_kwarg f_kw f_block_kwarg f_block_kw
-%type <node> bv_decls opt_bv_decl bvar
-%type <node> lambda f_larglist lambda_body brace_body do_body
+%type <node_args> block_param opt_block_param block_param_def
+%type <node_opt_arg> f_opt
+%type <node_kw_arg> f_kwarg f_kw f_block_kwarg f_block_kw
+%type <id> bv_decls opt_bv_decl bvar
+%type <node> lambda lambda_body brace_body do_body
+%type <node_args> f_larglist
%type <node> brace_block cmd_brace_block do_block lhs none fitem
-%type <node> mlhs mlhs_head mlhs_basic mlhs_item mlhs_node mlhs_post mlhs_inner
+%type <node> mlhs_head mlhs_item mlhs_node mlhs_post
+%type <node_masgn> mlhs mlhs_basic mlhs_inner
%type <node> p_case_body p_cases p_top_expr p_top_expr_body
%type <node> p_expr p_as p_alt p_expr_basic p_find
%type <node> p_args p_args_head p_args_tail p_args_post p_arg p_rest
@@ -1493,10 +2885,16 @@ static int looking_at_eol_p(struct parser_params *p);
%type <id> cname fname op f_rest_arg f_block_arg opt_f_block_arg f_norm_arg f_bad_arg
%type <id> f_kwrest f_label f_arg_asgn call_op call_op2 reswords relop dot_or_colon
%type <id> p_kwrest p_kwnorest p_any_kwrest p_kw_label
-%type <id> f_no_kwarg f_any_kwrest args_forward excessed_comma nonlocal_var
- %type <ctxt> lex_ctxt /* keep <ctxt> in ripper */
+%type <id> f_no_kwarg f_any_kwrest args_forward excessed_comma nonlocal_var def_name
+%type <ctxt> lex_ctxt begin_defined k_class k_module k_END k_rescue k_ensure after_rescue
+%type <ctxt> p_in_kwarg
+%type <tbl> p_lparen p_lbracket p_pktbl p_pvtbl
+%type <num> max_numparam
+%type <node> numparam
+%type <id> it_id
%token END_OF_INPUT 0 "end-of-input"
%token <id> '.'
+
/* escaped chars, should be ignored otherwise */
%token <id> '\\' "backslash"
%token tSP "escaped space"
@@ -1585,34 +2983,44 @@ static int looking_at_eol_p(struct parser_params *p);
%token tLAST_TOKEN
+/*
+ * parameterizing rules
+ */
+%rule words(begin, word_list): begin ' '+ word_list tSTRING_END
+ {
+ $$ = make_list($3, &@$);
+ /*% ripper: array!($:3) %*/
+ }
+ ;
+
%%
program : {
SET_LEX_STATE(EXPR_BEG);
local_push(p, ifndef_ripper(1)+0);
+ /* jumps are possible in the top-level loop. */
+ if (!ifndef_ripper(p->do_loop) + 0) init_block_exit(p);
}
top_compstmt
{
- /*%%%*/
if ($2 && !compile_for_eval) {
NODE *node = $2;
/* last expression should not be void */
if (nd_type_p(node, NODE_BLOCK)) {
- while (node->nd_next) {
- node = node->nd_next;
+ while (RNODE_BLOCK(node)->nd_next) {
+ node = RNODE_BLOCK(node)->nd_next;
}
- node = node->nd_head;
+ node = RNODE_BLOCK(node)->nd_head;
}
node = remove_begin(node);
void_expr(p, node);
}
p->eval_tree = NEW_SCOPE(0, block_append(p, p->eval_tree, $2), &@$);
- /*% %*/
- /*% ripper[final]: program!($2) %*/
+ /*% ripper[final]: program!($:2) %*/
local_pop(p);
}
;
-top_compstmt : top_stmts opt_terms
+top_compstmt : top_stmts terms?
{
$$ = void_stmts(p, $1);
}
@@ -1620,68 +3028,76 @@ top_compstmt : top_stmts opt_terms
top_stmts : none
{
- /*%%%*/
$$ = NEW_BEGIN(0, &@$);
- /*% %*/
/*% ripper: stmts_add!(stmts_new!, void_stmt!) %*/
}
| top_stmt
{
- /*%%%*/
$$ = newline_node($1);
- /*% %*/
- /*% ripper: stmts_add!(stmts_new!, $1) %*/
+ /*% ripper: stmts_add!(stmts_new!, $:1) %*/
}
| top_stmts terms top_stmt
{
- /*%%%*/
$$ = block_append(p, $1, newline_node($3));
- /*% %*/
- /*% ripper: stmts_add!($1, $3) %*/
+ /*% ripper: stmts_add!($:1, $:3) %*/
}
;
top_stmt : stmt
+ {
+ clear_block_exit(p, true);
+ $$ = $1;
+ }
| keyword_BEGIN begin_block
{
$$ = $2;
+ /*% ripper: get_value($:2); %*/
}
;
-begin_block : '{' top_compstmt '}'
+block_open : '{' {$$ = init_block_exit(p);};
+
+begin_block : block_open top_compstmt '}'
{
- /*%%%*/
+ restore_block_exit(p, $block_open);
p->eval_tree_begin = block_append(p, p->eval_tree_begin,
NEW_BEGIN($2, &@$));
$$ = NEW_BEGIN(0, &@$);
- /*% %*/
- /*% ripper: BEGIN!($2) %*/
+ /*% ripper: BEGIN!($:2) %*/
}
;
-bodystmt : compstmt
+bodystmt : compstmt[body]
+ lex_ctxt[ctxt]
opt_rescue
- k_else {if (!$2) {yyerror1(&@3, "else without rescue is useless");}}
- compstmt
+ k_else
+ {
+ if (!$opt_rescue) yyerror1(&@k_else, "else without rescue is useless");
+ next_rescue_context(&p->ctxt, &$ctxt, after_else);
+ }
+ compstmt[elsebody]
+ {
+ next_rescue_context(&p->ctxt, &$ctxt, after_ensure);
+ }
opt_ensure
{
- /*%%%*/
- $$ = new_bodystmt(p, $1, $2, $5, $6, &@$);
- /*% %*/
- /*% ripper: bodystmt!(escape_Qundef($1), escape_Qundef($2), escape_Qundef($5), escape_Qundef($6)) %*/
+ $$ = new_bodystmt(p, $body, $opt_rescue, $elsebody, $opt_ensure, &@$);
+ /*% ripper: bodystmt!($:body, $:opt_rescue, $:elsebody, $:opt_ensure) %*/
}
- | compstmt
+ | compstmt[body]
+ lex_ctxt[ctxt]
opt_rescue
+ {
+ next_rescue_context(&p->ctxt, &$ctxt, after_ensure);
+ }
opt_ensure
{
- /*%%%*/
- $$ = new_bodystmt(p, $1, $2, 0, $3, &@$);
- /*% %*/
- /*% ripper: bodystmt!(escape_Qundef($1), escape_Qundef($2), Qnil, escape_Qundef($3)) %*/
+ $$ = new_bodystmt(p, $body, $opt_rescue, 0, $opt_ensure, &@$);
+ /*% ripper: bodystmt!($:body, $:opt_rescue, Qnil, $:opt_ensure) %*/
}
;
-compstmt : stmts opt_terms
+compstmt : stmts terms?
{
$$ = void_stmts(p, $1);
}
@@ -1689,24 +3105,18 @@ compstmt : stmts opt_terms
stmts : none
{
- /*%%%*/
$$ = NEW_BEGIN(0, &@$);
- /*% %*/
/*% ripper: stmts_add!(stmts_new!, void_stmt!) %*/
}
| stmt_or_begin
{
- /*%%%*/
$$ = newline_node($1);
- /*% %*/
- /*% ripper: stmts_add!(stmts_new!, $1) %*/
+ /*% ripper: stmts_add!(stmts_new!, $:1) %*/
}
| stmts terms stmt_or_begin
{
- /*%%%*/
$$ = block_append(p, $1, newline_node($3));
- /*% %*/
- /*% ripper: stmts_add!($1, $3) %*/
+ /*% ripper: stmts_add!($:1, $:3) %*/
}
;
@@ -1724,245 +3134,227 @@ stmt_or_begin : stmt
}
;
+allow_exits : {$$ = allow_block_exit(p);};
+
+k_END : keyword_END lex_ctxt
+ {
+ $$ = $2;
+ p->ctxt.in_rescue = before_rescue;
+ /*% ripper: get_value($:2); %*/
+ };
+
stmt : keyword_alias fitem {SET_LEX_STATE(EXPR_FNAME|EXPR_FITEM);} fitem
{
- /*%%%*/
$$ = NEW_ALIAS($2, $4, &@$);
- /*% %*/
- /*% ripper: alias!($2, $4) %*/
+ /*% ripper: alias!($:2, $:4) %*/
}
| keyword_alias tGVAR tGVAR
{
- /*%%%*/
$$ = NEW_VALIAS($2, $3, &@$);
- /*% %*/
- /*% ripper: var_alias!($2, $3) %*/
+ /*% ripper: var_alias!($:2, $:3) %*/
}
| keyword_alias tGVAR tBACK_REF
{
- /*%%%*/
char buf[2];
buf[0] = '$';
- buf[1] = (char)$3->nd_nth;
+ buf[1] = (char)RNODE_BACK_REF($3)->nd_nth;
$$ = NEW_VALIAS($2, rb_intern2(buf, 2), &@$);
- /*% %*/
- /*% ripper: var_alias!($2, $3) %*/
+ /*% ripper: var_alias!($:2, $:3) %*/
}
| keyword_alias tGVAR tNTH_REF
{
static const char mesg[] = "can't make alias for the number variables";
/*%%%*/
yyerror1(&@3, mesg);
- $$ = NEW_BEGIN(0, &@$);
/*% %*/
- /*% ripper[error]: alias_error!(ERR_MESG(), $3) %*/
+ $$ = NEW_ERROR(&@$);
+ /*% ripper[error]: alias_error!(ERR_MESG(), $:3) %*/
}
| keyword_undef undef_list
{
- /*%%%*/
$$ = $2;
- /*% %*/
- /*% ripper: undef!($2) %*/
+ /*% ripper: undef!($:2) %*/
}
| stmt modifier_if expr_value
{
- /*%%%*/
$$ = new_if(p, $3, remove_begin($1), 0, &@$);
fixpos($$, $3);
- /*% %*/
- /*% ripper: if_mod!($3, $1) %*/
+ /*% ripper: if_mod!($:3, $:1) %*/
}
| stmt modifier_unless expr_value
{
- /*%%%*/
$$ = new_unless(p, $3, remove_begin($1), 0, &@$);
fixpos($$, $3);
- /*% %*/
- /*% ripper: unless_mod!($3, $1) %*/
+ /*% ripper: unless_mod!($:3, $:1) %*/
}
| stmt modifier_while expr_value
{
- /*%%%*/
+ clear_block_exit(p, false);
if ($1 && nd_type_p($1, NODE_BEGIN)) {
- $$ = NEW_WHILE(cond(p, $3, &@3), $1->nd_body, 0, &@$);
+ $$ = NEW_WHILE(cond(p, $3, &@3), RNODE_BEGIN($1)->nd_body, 0, &@$);
}
else {
$$ = NEW_WHILE(cond(p, $3, &@3), $1, 1, &@$);
}
- /*% %*/
- /*% ripper: while_mod!($3, $1) %*/
+ /*% ripper: while_mod!($:3, $:1) %*/
}
| stmt modifier_until expr_value
{
- /*%%%*/
+ clear_block_exit(p, false);
if ($1 && nd_type_p($1, NODE_BEGIN)) {
- $$ = NEW_UNTIL(cond(p, $3, &@3), $1->nd_body, 0, &@$);
+ $$ = NEW_UNTIL(cond(p, $3, &@3), RNODE_BEGIN($1)->nd_body, 0, &@$);
}
else {
$$ = NEW_UNTIL(cond(p, $3, &@3), $1, 1, &@$);
}
- /*% %*/
- /*% ripper: until_mod!($3, $1) %*/
+ /*% ripper: until_mod!($:3, $:1) %*/
}
- | stmt modifier_rescue stmt
+ | stmt modifier_rescue after_rescue stmt
{
- /*%%%*/
+ p->ctxt.in_rescue = $3.in_rescue;
NODE *resq;
- YYLTYPE loc = code_loc_gen(&@2, &@3);
- resq = NEW_RESBODY(0, remove_begin($3), 0, &loc);
+ YYLTYPE loc = code_loc_gen(&@2, &@4);
+ resq = NEW_RESBODY(0, remove_begin($4), 0, &loc);
$$ = NEW_RESCUE(remove_begin($1), resq, 0, &@$);
- /*% %*/
- /*% ripper: rescue_mod!($1, $3) %*/
+ /*% ripper: rescue_mod!($:1, $:4) %*/
}
- | keyword_END '{' compstmt '}'
+ | k_END allow_exits '{' compstmt '}'
{
if (p->ctxt.in_def) {
rb_warn0("END in method; use at_exit");
}
- /*%%%*/
+ restore_block_exit(p, $allow_exits);
+ p->ctxt = $k_END;
{
- NODE *scope = NEW_NODE(
- NODE_SCOPE, 0 /* tbl */, $3 /* body */, 0 /* args */, &@$);
+ NODE *scope = NEW_SCOPE2(0 /* tbl */, 0 /* args */, $compstmt /* body */, &@$);
$$ = NEW_POSTEXE(scope, &@$);
}
- /*% %*/
- /*% ripper: END!($3) %*/
+ /*% ripper: END!($:compstmt) %*/
}
| command_asgn
| mlhs '=' lex_ctxt command_call
{
- /*%%%*/
value_expr($4);
- $$ = node_assign(p, $1, $4, $3, &@$);
- /*% %*/
- /*% ripper: massign!($1, $4) %*/
+ $$ = node_assign(p, (NODE *)$1, $4, $3, &@$);
+ /*% ripper: massign!($:1, $:4) %*/
}
| lhs '=' lex_ctxt mrhs
{
- /*%%%*/
$$ = node_assign(p, $1, $4, $3, &@$);
- /*% %*/
- /*% ripper: assign!($1, $4) %*/
+ /*% ripper: assign!($:1, $:4) %*/
}
- | mlhs '=' lex_ctxt mrhs_arg modifier_rescue stmt
+ | mlhs '=' lex_ctxt mrhs_arg modifier_rescue
+ after_rescue stmt[resbody]
{
- /*%%%*/
- YYLTYPE loc = code_loc_gen(&@5, &@6);
- $$ = node_assign(p, $1, NEW_RESCUE($4, NEW_RESBODY(0, remove_begin($6), 0, &loc), 0, &@$), $3, &@$);
- /*% %*/
- /*% ripper: massign!($1, rescue_mod!($4, $6)) %*/
+ p->ctxt.in_rescue = $3.in_rescue;
+ YYLTYPE loc = code_loc_gen(&@modifier_rescue, &@resbody);
+ $resbody = NEW_RESBODY(0, remove_begin($resbody), 0, &loc);
+ loc.beg_pos = @mrhs_arg.beg_pos;
+ $mrhs_arg = NEW_RESCUE($mrhs_arg, $resbody, 0, &loc);
+ $$ = node_assign(p, (NODE *)$mlhs, $mrhs_arg, $lex_ctxt, &@$);
+ /*% ripper: massign!($:1, rescue_mod!($:4, $:7)) %*/
}
| mlhs '=' lex_ctxt mrhs_arg
{
- /*%%%*/
- $$ = node_assign(p, $1, $4, $3, &@$);
- /*% %*/
- /*% ripper: massign!($1, $4) %*/
+ $$ = node_assign(p, (NODE *)$1, $4, $3, &@$);
+ /*% ripper: massign!($:1, $:4) %*/
}
| expr
| error
{
(void)yynerrs;
- /*%%%*/
$$ = NEW_ERROR(&@$);
- /*% %*/
}
;
command_asgn : lhs '=' lex_ctxt command_rhs
{
- /*%%%*/
$$ = node_assign(p, $1, $4, $3, &@$);
- /*% %*/
- /*% ripper: assign!($1, $4) %*/
+ /*% ripper: assign!($:1, $:4) %*/
}
| var_lhs tOP_ASGN lex_ctxt command_rhs
{
- /*%%%*/
$$ = new_op_assign(p, $1, $2, $4, $3, &@$);
- /*% %*/
- /*% ripper: opassign!($1, $2, $4) %*/
+ /*% ripper: opassign!($:1, $:2, $:4) %*/
}
| primary_value '[' opt_call_args rbracket tOP_ASGN lex_ctxt command_rhs
{
- /*%%%*/
$$ = new_ary_op_assign(p, $1, $3, $5, $7, &@3, &@$);
- /*% %*/
- /*% ripper: opassign!(aref_field!($1, escape_Qundef($3)), $5, $7) %*/
+ /*% ripper: opassign!(aref_field!($:1, $:3), $:5, $:7) %*/
}
| primary_value call_op tIDENTIFIER tOP_ASGN lex_ctxt command_rhs
{
- /*%%%*/
$$ = new_attr_op_assign(p, $1, $2, $3, $4, $6, &@$);
- /*% %*/
- /*% ripper: opassign!(field!($1, $2, $3), $4, $6) %*/
+ /*% ripper: opassign!(field!($:1, $:2, $:3), $:4, $:6) %*/
}
| primary_value call_op tCONSTANT tOP_ASGN lex_ctxt command_rhs
{
- /*%%%*/
$$ = new_attr_op_assign(p, $1, $2, $3, $4, $6, &@$);
- /*% %*/
- /*% ripper: opassign!(field!($1, $2, $3), $4, $6) %*/
+ /*% ripper: opassign!(field!($:1, $:2, $:3), $:4, $:6) %*/
}
| primary_value tCOLON2 tCONSTANT tOP_ASGN lex_ctxt command_rhs
{
- /*%%%*/
YYLTYPE loc = code_loc_gen(&@1, &@3);
$$ = new_const_op_assign(p, NEW_COLON2($1, $3, &loc), $4, $6, $5, &@$);
- /*% %*/
- /*% ripper: opassign!(const_path_field!($1, $3), $4, $6) %*/
+ /*% ripper: opassign!(const_path_field!($:1, $:3), $:4, $:6) %*/
}
| primary_value tCOLON2 tIDENTIFIER tOP_ASGN lex_ctxt command_rhs
{
- /*%%%*/
- $$ = new_attr_op_assign(p, $1, ID2VAL(idCOLON2), $3, $4, $6, &@$);
- /*% %*/
- /*% ripper: opassign!(field!($1, ID2VAL(idCOLON2), $3), $4, $6) %*/
+ $$ = new_attr_op_assign(p, $1, idCOLON2, $3, $4, $6, &@$);
+ /*% ripper: opassign!(field!($:1, $:2, $:3), $:4, $:6) %*/
}
- | defn_head f_opt_paren_args '=' endless_command
+ | defn_head[head] f_opt_paren_args[args] '=' endless_command[bodystmt]
{
- endless_method_name(p, $<node>1, &@1);
- restore_defun(p, $<node>1->nd_defn);
+ endless_method_name(p, $head->nd_mid, &@head);
+ restore_defun(p, $head);
+ $bodystmt = new_scope_body(p, $args, $bodystmt, &@$);
+ ($$ = $head->nd_def)->nd_loc = @$;
+ RNODE_DEFN($$)->nd_defn = $bodystmt;
/*%%%*/
- $$ = set_defun_body(p, $1, $2, $4, &@$);
- /*% %*/
- /*% ripper: def!(get_value($1), $2, bodystmt!($4, Qnil, Qnil, Qnil)) %*/
+ /*%
+ VALUE val = dispatch4(bodystmt, get_value($:bodystmt), Qnil, Qnil, Qnil);
+ val = dispatch3(def, get_value($:head), get_value($:args), val);
+ set_value(val);
+ %*/
local_pop(p);
}
- | defs_head f_opt_paren_args '=' endless_command
+ | defs_head[head] f_opt_paren_args[args] '=' endless_command[bodystmt]
{
- endless_method_name(p, $<node>1, &@1);
- restore_defun(p, $<node>1->nd_defn);
+ endless_method_name(p, $head->nd_mid, &@head);
+ restore_defun(p, $head);
+ $bodystmt = new_scope_body(p, $args, $bodystmt, &@$);
+ ($$ = $head->nd_def)->nd_loc = @$;
+ RNODE_DEFS($$)->nd_defn = $bodystmt;
/*%%%*/
- $$ = set_defun_body(p, $1, $2, $4, &@$);
/*%
- $1 = get_value($1);
+ VALUE val = dispatch4(bodystmt, get_value($:bodystmt), Qnil, Qnil, Qnil);
+ val = defs(p, get_value($:head), get_value($:args), val);
+ set_value(val);
%*/
- /*% ripper: defs!(AREF($1, 0), AREF($1, 1), AREF($1, 2), $2, bodystmt!($4, Qnil, Qnil, Qnil)) %*/
local_pop(p);
}
| backref tOP_ASGN lex_ctxt command_rhs
{
/*%%%*/
rb_backref_error(p, $1);
- $$ = NEW_BEGIN(0, &@$);
/*% %*/
- /*% ripper[error]: backref_error(p, RNODE($1), assign!(var_field(p, $1), $4)) %*/
+ $$ = NEW_ERROR(&@$);
+ /*% ripper[error]: backref_error(p, RNODE($:1), assign!(var_field(p, get_value($:1)), $:4)) %*/
}
;
endless_command : command
- | endless_command modifier_rescue arg
+ | endless_command modifier_rescue after_rescue arg
{
- /*%%%*/
- $$ = rescued_expr(p, $1, $3, &@1, &@2, &@3);
- /*% %*/
- /*% ripper: rescue_mod!($1, $3) %*/
+ p->ctxt.in_rescue = $3.in_rescue;
+ $$ = rescued_expr(p, $1, $4, &@1, &@2, &@4);
+ /*% ripper: rescue_mod!($:1, $:4) %*/
}
- | keyword_not opt_nl endless_command
+ | keyword_not '\n'? endless_command
{
$$ = call_uni_op(p, method_cond(p, $3, &@3), METHOD_NOT, &@1, &@$);
+ /*% ripper: unary!(ID2VAL(idNOT), $:3) %*/
}
;
@@ -1971,14 +3363,13 @@ command_rhs : command_call %prec tOP_ASGN
value_expr($1);
$$ = $1;
}
- | command_call modifier_rescue stmt
+ | command_call modifier_rescue after_rescue stmt
{
- /*%%%*/
- YYLTYPE loc = code_loc_gen(&@2, &@3);
+ p->ctxt.in_rescue = $3.in_rescue;
+ YYLTYPE loc = code_loc_gen(&@2, &@4);
value_expr($1);
- $$ = NEW_RESCUE($1, NEW_RESBODY(0, remove_begin($3), 0, &loc), 0, &@$);
- /*% %*/
- /*% ripper: rescue_mod!($1, $3) %*/
+ $$ = NEW_RESCUE($1, NEW_RESBODY(0, remove_begin($4), 0, &loc), 0, &@$);
+ /*% ripper: rescue_mod!($:1, $:4) %*/
}
| command_asgn
;
@@ -1987,94 +3378,70 @@ expr : command_call
| expr keyword_and expr
{
$$ = logop(p, idAND, $1, $3, &@2, &@$);
+ /*% ripper: binary!($:1, ID2VAL(idAND), $:3) %*/
}
| expr keyword_or expr
{
$$ = logop(p, idOR, $1, $3, &@2, &@$);
+ /*% ripper: binary!($:1, ID2VAL(idOR), $:3) %*/
}
- | keyword_not opt_nl expr
+ | keyword_not '\n'? expr
{
$$ = call_uni_op(p, method_cond(p, $3, &@3), METHOD_NOT, &@1, &@$);
+ /*% ripper: unary!(ID2VAL(idNOT), $:3) %*/
}
| '!' command_call
{
$$ = call_uni_op(p, method_cond(p, $2, &@2), '!', &@1, &@$);
+ /*% ripper: unary!(ID2VAL('\'!\''), $:2) %*/
}
| arg tASSOC
{
- value_expr($1);
- SET_LEX_STATE(EXPR_BEG|EXPR_LABEL);
- p->command_start = FALSE;
- $<ctxt>2 = p->ctxt;
- p->ctxt.in_kwarg = 1;
- $<tbl>$ = push_pvtbl(p);
- }
- {
- $<tbl>$ = push_pktbl(p);
+ value_expr($arg);
}
- p_top_expr_body
+ p_in_kwarg[ctxt] p_pvtbl p_pktbl
+ p_top_expr_body[body]
{
- pop_pktbl(p, $<tbl>4);
- pop_pvtbl(p, $<tbl>3);
- p->ctxt.in_kwarg = $<ctxt>2.in_kwarg;
- /*%%%*/
- $$ = NEW_CASE3($1, NEW_IN($5, 0, 0, &@5), &@$);
- /*% %*/
- /*% ripper: case!($1, in!($5, Qnil, Qnil)) %*/
+ pop_pktbl(p, $p_pktbl);
+ pop_pvtbl(p, $p_pvtbl);
+ p->ctxt.in_kwarg = $ctxt.in_kwarg;
+ $$ = NEW_CASE3($arg, NEW_IN($body, 0, 0, &@body), &@$);
+ /*% ripper: case!($:arg, in!($:body, Qnil, Qnil)) %*/
}
| arg keyword_in
{
- value_expr($1);
- SET_LEX_STATE(EXPR_BEG|EXPR_LABEL);
- p->command_start = FALSE;
- $<ctxt>2 = p->ctxt;
- p->ctxt.in_kwarg = 1;
- $<tbl>$ = push_pvtbl(p);
+ value_expr($arg);
}
+ p_in_kwarg[ctxt] p_pvtbl p_pktbl
+ p_top_expr_body[body]
{
- $<tbl>$ = push_pktbl(p);
- }
- p_top_expr_body
- {
- pop_pktbl(p, $<tbl>4);
- pop_pvtbl(p, $<tbl>3);
- p->ctxt.in_kwarg = $<ctxt>2.in_kwarg;
- /*%%%*/
- $$ = NEW_CASE3($1, NEW_IN($5, NEW_TRUE(&@5), NEW_FALSE(&@5), &@5), &@$);
- /*% %*/
- /*% ripper: case!($1, in!($5, Qnil, Qnil)) %*/
+ pop_pktbl(p, $p_pktbl);
+ pop_pvtbl(p, $p_pvtbl);
+ p->ctxt.in_kwarg = $ctxt.in_kwarg;
+ $$ = NEW_CASE3($arg, NEW_IN($body, NEW_TRUE(&@body), NEW_FALSE(&@body), &@body), &@$);
+ /*% ripper: case!($:arg, in!($:body, Qnil, Qnil)) %*/
}
| arg %prec tLBRACE_ARG
;
def_name : fname
{
- ID fname = get_id($1);
- ID cur_arg = p->cur_arg;
- YYSTYPE c = {.ctxt = p->ctxt};
+ ID fname = $1;
numparam_name(p, fname);
- NODE *save =
- NODE_NEW_TEMPORAL(NODE_SELF,
- /*head*/numparam_push(p),
- /*nth*/p->max_numparam,
- /*cval*/c.val);
local_push(p, 0);
p->cur_arg = 0;
p->ctxt.in_def = 1;
- $<node>$ = NEW_NODE(NODE_SELF, /*vid*/cur_arg, /*mid*/fname, /*args*/save, &@$);
- /*%%%*/
- /*%
- $$ = NEW_RIPPER(fname, get_value($1), $$, &NULL_LOC);
- %*/
+ p->ctxt.in_rescue = before_rescue;
+ $$ = $1;
}
;
defn_head : k_def def_name
{
- $$ = $2;
- /*%%%*/
- $$ = NEW_NODE(NODE_DEFN, 0, $$->nd_mid, $$, &@$);
- /*% %*/
+ $$ = def_head_save(p, $k_def);
+ $$->nd_mid = $def_name;
+ $$->nd_def = NEW_DEFN($def_name, 0, &@$);
+ /*% ripper: get_value($:def_name); %*/
}
;
@@ -2086,13 +3453,12 @@ defs_head : k_def singleton dot_or_colon
def_name
{
SET_LEX_STATE(EXPR_ENDFN|EXPR_LABEL); /* force for args */
- $$ = $5;
+ $$ = def_head_save(p, $k_def);
+ $$->nd_mid = $def_name;
+ $$->nd_def = NEW_DEFS($singleton, $def_name, 0, &@$);
/*%%%*/
- $$ = NEW_NODE(NODE_DEFS, $2, $$->nd_mid, $$, &@$);
/*%
- VALUE ary = rb_ary_new_from_args(3, $2, $3, get_value($$));
- add_mark_object(p, ary);
- $<node>$->nd_rval = ary;
+ set_value(rb_ary_new_from_args(3, get_value($:singleton), get_value($:dot_or_colon), get_value($:def_name)));
%*/
}
;
@@ -2104,15 +3470,14 @@ expr_value : expr
}
| error
{
- /*%%%*/
$$ = NEW_ERROR(&@$);
- /*% %*/
}
;
expr_value_do : {COND_PUSH(1);} expr_value do {COND_POP();}
{
$$ = $2;
+ /*% ripper: get_value($:2); %*/
}
;
@@ -2123,385 +3488,300 @@ command_call : command
block_command : block_call
| block_call call_op2 operation2 command_args
{
- /*%%%*/
$$ = new_qcall(p, $2, $1, $3, $4, &@3, &@$);
- /*% %*/
- /*% ripper: method_add_arg!(call!($1, $2, $3), $4) %*/
+ /*% ripper: method_add_arg!(call!($:1, $:2, $:3), $:4) %*/
}
;
cmd_brace_block : tLBRACE_ARG brace_body '}'
{
$$ = $2;
- /*%%%*/
- $$->nd_body->nd_loc = code_loc_gen(&@1, &@3);
- nd_set_line($$, @1.end_pos.lineno);
- /*% %*/
+ set_embraced_location($$, &@1, &@3);
+ /*% ripper: get_value($:2); %*/
}
;
fcall : operation
{
- /*%%%*/
$$ = NEW_FCALL($1, 0, &@$);
- nd_set_line($$, p->tokline);
- /*% %*/
- /*% ripper: $1 %*/
+ /*% ripper: get_value($:1); %*/
}
;
command : fcall command_args %prec tLOWEST
{
- /*%%%*/
$1->nd_args = $2;
nd_set_last_loc($1, @2.end_pos);
- $$ = $1;
- /*% %*/
- /*% ripper: command!($1, $2) %*/
+ $$ = (NODE *)$1;
+ /*% ripper: command!($:1, $:2) %*/
}
| fcall command_args cmd_brace_block
{
- /*%%%*/
block_dup_check(p, $2, $3);
$1->nd_args = $2;
- $$ = method_add_block(p, $1, $3, &@$);
- fixpos($$, $1);
+ $$ = method_add_block(p, (NODE *)$1, $3, &@$);
+ fixpos($$, RNODE($1));
nd_set_last_loc($1, @2.end_pos);
- /*% %*/
- /*% ripper: method_add_block!(command!($1, $2), $3) %*/
+ /*% ripper: method_add_block!(command!($:1, $:2), $:3) %*/
}
| primary_value call_op operation2 command_args %prec tLOWEST
{
- /*%%%*/
- $$ = new_command_qcall(p, $2, $1, $3, $4, Qnull, &@3, &@$);
- /*% %*/
- /*% ripper: command_call!($1, $2, $3, $4) %*/
+ $$ = new_command_qcall(p, $2, $1, $3, $4, 0, &@3, &@$);
+ /*% ripper: command_call!($:1, $:2, $:3, $:4) %*/
}
| primary_value call_op operation2 command_args cmd_brace_block
{
- /*%%%*/
$$ = new_command_qcall(p, $2, $1, $3, $4, $5, &@3, &@$);
- /*% %*/
- /*% ripper: method_add_block!(command_call!($1, $2, $3, $4), $5) %*/
+ /*% ripper: method_add_block!(command_call!($:1, $:2, $:3, $:4), $:5) %*/
}
| primary_value tCOLON2 operation2 command_args %prec tLOWEST
{
- /*%%%*/
- $$ = new_command_qcall(p, ID2VAL(idCOLON2), $1, $3, $4, Qnull, &@3, &@$);
- /*% %*/
- /*% ripper: command_call!($1, ID2VAL(idCOLON2), $3, $4) %*/
+ $$ = new_command_qcall(p, idCOLON2, $1, $3, $4, 0, &@3, &@$);
+ /*% ripper: command_call!($:1, $:2, $:3, $:4) %*/
}
| primary_value tCOLON2 operation2 command_args cmd_brace_block
{
- /*%%%*/
- $$ = new_command_qcall(p, ID2VAL(idCOLON2), $1, $3, $4, $5, &@3, &@$);
- /*% %*/
- /*% ripper: method_add_block!(command_call!($1, ID2VAL(idCOLON2), $3, $4), $5) %*/
+ $$ = new_command_qcall(p, idCOLON2, $1, $3, $4, $5, &@3, &@$);
+ /*% ripper: method_add_block!(command_call!($:1, $:2, $:3, $:4), $:5) %*/
+ }
+ | primary_value tCOLON2 tCONSTANT '{' brace_body '}'
+ {
+ set_embraced_location($5, &@4, &@6);
+ $$ = new_command_qcall(p, idCOLON2, $1, $3, 0, $5, &@3, &@$);
+ /*% ripper: method_add_block!(command_call!($:1, $:2, $:3, Qnil), $:5) %*/
}
| keyword_super command_args
{
- /*%%%*/
$$ = NEW_SUPER($2, &@$);
fixpos($$, $2);
- /*% %*/
- /*% ripper: super!($2) %*/
+ /*% ripper: super!($:2) %*/
}
- | keyword_yield command_args
+ | k_yield command_args
{
- /*%%%*/
$$ = new_yield(p, $2, &@$);
fixpos($$, $2);
- /*% %*/
- /*% ripper: yield!($2) %*/
+ /*% ripper: yield!($:2) %*/
}
| k_return call_args
{
- /*%%%*/
$$ = NEW_RETURN(ret_args(p, $2), &@$);
- /*% %*/
- /*% ripper: return!($2) %*/
+ /*% ripper: return!($:2) %*/
}
| keyword_break call_args
{
- /*%%%*/
- $$ = NEW_BREAK(ret_args(p, $2), &@$);
- /*% %*/
- /*% ripper: break!($2) %*/
+ NODE *args = 0;
+ args = ret_args(p, $2);
+ $<node>$ = add_block_exit(p, NEW_BREAK(args, &@$));
+ /*% ripper: break!($:2) %*/
}
| keyword_next call_args
{
- /*%%%*/
- $$ = NEW_NEXT(ret_args(p, $2), &@$);
- /*% %*/
- /*% ripper: next!($2) %*/
+ NODE *args = 0;
+ args = ret_args(p, $2);
+ $<node>$ = add_block_exit(p, NEW_NEXT(args, &@$));
+ /*% ripper: next!($:2) %*/
}
;
mlhs : mlhs_basic
| tLPAREN mlhs_inner rparen
{
- /*%%%*/
$$ = $2;
- /*% %*/
- /*% ripper: mlhs_paren!($2) %*/
+ /*% ripper: mlhs_paren!($:2) %*/
}
;
mlhs_inner : mlhs_basic
| tLPAREN mlhs_inner rparen
{
- /*%%%*/
- $$ = NEW_MASGN(NEW_LIST($2, &@$), 0, &@$);
- /*% %*/
- /*% ripper: mlhs_paren!($2) %*/
+ $$ = NEW_MASGN(NEW_LIST((NODE *)$2, &@$), 0, &@$);
+ /*% ripper: mlhs_paren!($:2) %*/
}
;
mlhs_basic : mlhs_head
{
- /*%%%*/
$$ = NEW_MASGN($1, 0, &@$);
- /*% %*/
- /*% ripper: $1 %*/
+ /*% ripper: get_value($:1) %*/
}
| mlhs_head mlhs_item
{
- /*%%%*/
- $$ = NEW_MASGN(list_append(p, $1,$2), 0, &@$);
- /*% %*/
- /*% ripper: mlhs_add!($1, $2) %*/
+ $$ = NEW_MASGN(list_append(p, $1, $2), 0, &@$);
+ /*% ripper: mlhs_add!($:1, $:2) %*/
}
| mlhs_head tSTAR mlhs_node
{
- /*%%%*/
$$ = NEW_MASGN($1, $3, &@$);
- /*% %*/
- /*% ripper: mlhs_add_star!($1, $3) %*/
+ /*% ripper: mlhs_add_star!($:1, $:3) %*/
}
| mlhs_head tSTAR mlhs_node ',' mlhs_post
{
- /*%%%*/
$$ = NEW_MASGN($1, NEW_POSTARG($3,$5,&@$), &@$);
- /*% %*/
- /*% ripper: mlhs_add_post!(mlhs_add_star!($1, $3), $5) %*/
+ /*% ripper: mlhs_add_post!(mlhs_add_star!($:1, $:3), $:5) %*/
}
| mlhs_head tSTAR
{
- /*%%%*/
$$ = NEW_MASGN($1, NODE_SPECIAL_NO_NAME_REST, &@$);
- /*% %*/
- /*% ripper: mlhs_add_star!($1, Qnil) %*/
+ /*% ripper: mlhs_add_star!($:1, Qnil) %*/
}
| mlhs_head tSTAR ',' mlhs_post
{
- /*%%%*/
$$ = NEW_MASGN($1, NEW_POSTARG(NODE_SPECIAL_NO_NAME_REST, $4, &@$), &@$);
- /*% %*/
- /*% ripper: mlhs_add_post!(mlhs_add_star!($1, Qnil), $4) %*/
+ /*% ripper: mlhs_add_post!(mlhs_add_star!($:1, Qnil), $:4) %*/
}
| tSTAR mlhs_node
{
- /*%%%*/
$$ = NEW_MASGN(0, $2, &@$);
- /*% %*/
- /*% ripper: mlhs_add_star!(mlhs_new!, $2) %*/
+ /*% ripper: mlhs_add_star!(mlhs_new!, $:2) %*/
}
| tSTAR mlhs_node ',' mlhs_post
{
- /*%%%*/
$$ = NEW_MASGN(0, NEW_POSTARG($2,$4,&@$), &@$);
- /*% %*/
- /*% ripper: mlhs_add_post!(mlhs_add_star!(mlhs_new!, $2), $4) %*/
+ /*% ripper: mlhs_add_post!(mlhs_add_star!(mlhs_new!, $:2), $:4) %*/
}
| tSTAR
{
- /*%%%*/
$$ = NEW_MASGN(0, NODE_SPECIAL_NO_NAME_REST, &@$);
- /*% %*/
/*% ripper: mlhs_add_star!(mlhs_new!, Qnil) %*/
}
| tSTAR ',' mlhs_post
{
- /*%%%*/
$$ = NEW_MASGN(0, NEW_POSTARG(NODE_SPECIAL_NO_NAME_REST, $3, &@$), &@$);
- /*% %*/
- /*% ripper: mlhs_add_post!(mlhs_add_star!(mlhs_new!, Qnil), $3) %*/
+ /*% ripper: mlhs_add_post!(mlhs_add_star!(mlhs_new!, Qnil), $:3) %*/
}
;
mlhs_item : mlhs_node
| tLPAREN mlhs_inner rparen
{
- /*%%%*/
- $$ = $2;
- /*% %*/
- /*% ripper: mlhs_paren!($2) %*/
+ $$ = (NODE *)$2;
+ /*% ripper: mlhs_paren!($:2) %*/
}
;
mlhs_head : mlhs_item ','
{
- /*%%%*/
$$ = NEW_LIST($1, &@1);
- /*% %*/
- /*% ripper: mlhs_add!(mlhs_new!, $1) %*/
+ /*% ripper: mlhs_add!(mlhs_new!, $:1) %*/
}
| mlhs_head mlhs_item ','
{
- /*%%%*/
$$ = list_append(p, $1, $2);
- /*% %*/
- /*% ripper: mlhs_add!($1, $2) %*/
+ /*% ripper: mlhs_add!($:1, $:2) %*/
}
;
mlhs_post : mlhs_item
{
- /*%%%*/
$$ = NEW_LIST($1, &@$);
- /*% %*/
- /*% ripper: mlhs_add!(mlhs_new!, $1) %*/
+ /*% ripper: mlhs_add!(mlhs_new!, $:1) %*/
}
| mlhs_post ',' mlhs_item
{
- /*%%%*/
$$ = list_append(p, $1, $3);
- /*% %*/
- /*% ripper: mlhs_add!($1, $3) %*/
+ /*% ripper: mlhs_add!($:1, $:3) %*/
}
;
mlhs_node : user_variable
{
- /*%%%*/
$$ = assignable(p, $1, 0, &@$);
- /*% %*/
- /*% ripper: assignable(p, var_field(p, $1)) %*/
+ /*% ripper: ripper_assignable(p, $1, var_field(p, get_value($:1))) %*/
}
| keyword_variable
{
- /*%%%*/
$$ = assignable(p, $1, 0, &@$);
- /*% %*/
- /*% ripper: assignable(p, var_field(p, $1)) %*/
+ /*% ripper: ripper_assignable(p, $1, var_field(p, get_value($:1))) %*/
}
| primary_value '[' opt_call_args rbracket
{
- /*%%%*/
$$ = aryset(p, $1, $3, &@$);
- /*% %*/
- /*% ripper: aref_field!($1, escape_Qundef($3)) %*/
+ /*% ripper: aref_field!($:1, $:3) %*/
}
| primary_value call_op tIDENTIFIER
{
anddot_multiple_assignment_check(p, &@2, $2);
- /*%%%*/
$$ = attrset(p, $1, $2, $3, &@$);
- /*% %*/
- /*% ripper: field!($1, $2, $3) %*/
+ /*% ripper: field!($:1, $:2, $:3) %*/
}
| primary_value tCOLON2 tIDENTIFIER
{
- /*%%%*/
$$ = attrset(p, $1, idCOLON2, $3, &@$);
- /*% %*/
- /*% ripper: const_path_field!($1, $3) %*/
+ /*% ripper: const_path_field!($:1, $:3) %*/
}
| primary_value call_op tCONSTANT
{
anddot_multiple_assignment_check(p, &@2, $2);
- /*%%%*/
$$ = attrset(p, $1, $2, $3, &@$);
- /*% %*/
- /*% ripper: field!($1, $2, $3) %*/
+ /*% ripper: field!($:1, $:2, $:3) %*/
}
| primary_value tCOLON2 tCONSTANT
{
- /*%%%*/
$$ = const_decl(p, NEW_COLON2($1, $3, &@$), &@$);
- /*% %*/
- /*% ripper: const_decl(p, const_path_field!($1, $3)) %*/
+ /*% ripper: ripper_const_decl(p, const_path_field!($:1, $:3)) %*/
}
| tCOLON3 tCONSTANT
{
- /*%%%*/
$$ = const_decl(p, NEW_COLON3($2, &@$), &@$);
- /*% %*/
- /*% ripper: const_decl(p, top_const_field!($2)) %*/
+ /*% ripper: ripper_const_decl(p, top_const_field!($:2)) %*/
}
| backref
{
/*%%%*/
rb_backref_error(p, $1);
- $$ = NEW_BEGIN(0, &@$);
/*% %*/
- /*% ripper[error]: backref_error(p, RNODE($1), var_field(p, $1)) %*/
+ $$ = NEW_ERROR(&@$);
+ /*% ripper[error]: backref_error(p, $1, var_field(p, get_value($:1))) %*/
}
;
lhs : user_variable
{
- /*%%%*/
$$ = assignable(p, $1, 0, &@$);
- /*% %*/
- /*% ripper: assignable(p, var_field(p, $1)) %*/
+ /*% ripper: ripper_assignable(p, $1, var_field(p, get_value($:1))) %*/
}
| keyword_variable
{
- /*%%%*/
$$ = assignable(p, $1, 0, &@$);
- /*% %*/
- /*% ripper: assignable(p, var_field(p, $1)) %*/
+ /*% ripper: ripper_assignable(p, $1, var_field(p, get_value($:1))) %*/
}
| primary_value '[' opt_call_args rbracket
{
- /*%%%*/
$$ = aryset(p, $1, $3, &@$);
- /*% %*/
- /*% ripper: aref_field!($1, escape_Qundef($3)) %*/
+ /*% ripper: aref_field!($:1, $:3) %*/
}
| primary_value call_op tIDENTIFIER
{
- /*%%%*/
$$ = attrset(p, $1, $2, $3, &@$);
- /*% %*/
- /*% ripper: field!($1, $2, $3) %*/
+ /*% ripper: field!($:1, $:2, $:3) %*/
}
| primary_value tCOLON2 tIDENTIFIER
{
- /*%%%*/
$$ = attrset(p, $1, idCOLON2, $3, &@$);
- /*% %*/
- /*% ripper: field!($1, ID2VAL(idCOLON2), $3) %*/
+ /*% ripper: field!($:1, $:2, $:3) %*/
}
| primary_value call_op tCONSTANT
{
- /*%%%*/
$$ = attrset(p, $1, $2, $3, &@$);
- /*% %*/
- /*% ripper: field!($1, $2, $3) %*/
+ /*% ripper: field!($:1, $:2, $:3) %*/
}
| primary_value tCOLON2 tCONSTANT
{
- /*%%%*/
$$ = const_decl(p, NEW_COLON2($1, $3, &@$), &@$);
- /*% %*/
- /*% ripper: const_decl(p, const_path_field!($1, $3)) %*/
+ /*% ripper: ripper_const_decl(p, const_path_field!($:1, $:3)) %*/
}
| tCOLON3 tCONSTANT
{
- /*%%%*/
$$ = const_decl(p, NEW_COLON3($2, &@$), &@$);
- /*% %*/
- /*% ripper: const_decl(p, top_const_field!($2)) %*/
+ /*% ripper: ripper_const_decl(p, top_const_field!($:2)) %*/
}
| backref
{
/*%%%*/
rb_backref_error(p, $1);
- $$ = NEW_BEGIN(0, &@$);
/*% %*/
- /*% ripper[error]: backref_error(p, RNODE($1), var_field(p, $1)) %*/
+ $$ = NEW_ERROR(&@$);
+ /*% ripper[error]: backref_error(p, $1, var_field(p, get_value($:1))) %*/
}
;
@@ -2511,31 +3791,25 @@ cname : tIDENTIFIER
/*%%%*/
yyerror1(&@1, mesg);
/*% %*/
- /*% ripper[error]: class_name_error!(ERR_MESG(), $1) %*/
+ /*% ripper[error]: class_name_error!(ERR_MESG(), $:1) %*/
}
| tCONSTANT
;
cpath : tCOLON3 cname
{
- /*%%%*/
$$ = NEW_COLON3($2, &@$);
- /*% %*/
- /*% ripper: top_const_ref!($2) %*/
+ /*% ripper: top_const_ref!($:2) %*/
}
| cname
{
- /*%%%*/
- $$ = NEW_COLON2(0, $$, &@$);
- /*% %*/
- /*% ripper: const_ref!($1) %*/
+ $$ = NEW_COLON2(0, $1, &@$);
+ /*% ripper: const_ref!($:1) %*/
}
| primary_value tCOLON2 cname
{
- /*%%%*/
$$ = NEW_COLON2($1, $3, &@$);
- /*% %*/
- /*% ripper: const_path_ref!($1, $3) %*/
+ /*% ripper: const_path_ref!($:1, $:3) %*/
}
;
@@ -2552,61 +3826,55 @@ fname : tIDENTIFIER
fitem : fname
{
- /*%%%*/
- $$ = NEW_LIT(ID2SYM($1), &@$);
- /*% %*/
- /*% ripper: symbol_literal!($1) %*/
+ $$ = NEW_SYM(rb_id2str($1), &@$);
+ /*% ripper: symbol_literal!($:1) %*/
}
| symbol
;
undef_list : fitem
{
- /*%%%*/
$$ = NEW_UNDEF($1, &@$);
- /*% %*/
- /*% ripper: rb_ary_new3(1, get_value($1)) %*/
+ /*% ripper: rb_ary_new3(1, get_value($:1)) %*/
}
| undef_list ',' {SET_LEX_STATE(EXPR_FNAME|EXPR_FITEM);} fitem
{
- /*%%%*/
NODE *undef = NEW_UNDEF($4, &@4);
$$ = block_append(p, $1, undef);
- /*% %*/
- /*% ripper: rb_ary_push($1, get_value($4)) %*/
- }
- ;
-
-op : '|' { ifndef_ripper($$ = '|'); }
- | '^' { ifndef_ripper($$ = '^'); }
- | '&' { ifndef_ripper($$ = '&'); }
- | tCMP { ifndef_ripper($$ = tCMP); }
- | tEQ { ifndef_ripper($$ = tEQ); }
- | tEQQ { ifndef_ripper($$ = tEQQ); }
- | tMATCH { ifndef_ripper($$ = tMATCH); }
- | tNMATCH { ifndef_ripper($$ = tNMATCH); }
- | '>' { ifndef_ripper($$ = '>'); }
- | tGEQ { ifndef_ripper($$ = tGEQ); }
- | '<' { ifndef_ripper($$ = '<'); }
- | tLEQ { ifndef_ripper($$ = tLEQ); }
- | tNEQ { ifndef_ripper($$ = tNEQ); }
- | tLSHFT { ifndef_ripper($$ = tLSHFT); }
- | tRSHFT { ifndef_ripper($$ = tRSHFT); }
- | '+' { ifndef_ripper($$ = '+'); }
- | '-' { ifndef_ripper($$ = '-'); }
- | '*' { ifndef_ripper($$ = '*'); }
- | tSTAR { ifndef_ripper($$ = '*'); }
- | '/' { ifndef_ripper($$ = '/'); }
- | '%' { ifndef_ripper($$ = '%'); }
- | tPOW { ifndef_ripper($$ = tPOW); }
- | tDSTAR { ifndef_ripper($$ = tDSTAR); }
- | '!' { ifndef_ripper($$ = '!'); }
- | '~' { ifndef_ripper($$ = '~'); }
- | tUPLUS { ifndef_ripper($$ = tUPLUS); }
- | tUMINUS { ifndef_ripper($$ = tUMINUS); }
- | tAREF { ifndef_ripper($$ = tAREF); }
- | tASET { ifndef_ripper($$ = tASET); }
- | '`' { ifndef_ripper($$ = '`'); }
+ /*% ripper: rb_ary_push(get_value($:1), get_value($:4)) %*/
+ }
+ ;
+
+op : '|' { $$ = '|'; }
+ | '^' { $$ = '^'; }
+ | '&' { $$ = '&'; }
+ | tCMP { $$ = tCMP; }
+ | tEQ { $$ = tEQ; }
+ | tEQQ { $$ = tEQQ; }
+ | tMATCH { $$ = tMATCH; }
+ | tNMATCH { $$ = tNMATCH; }
+ | '>' { $$ = '>'; }
+ | tGEQ { $$ = tGEQ; }
+ | '<' { $$ = '<'; }
+ | tLEQ { $$ = tLEQ; }
+ | tNEQ { $$ = tNEQ; }
+ | tLSHFT { $$ = tLSHFT; }
+ | tRSHFT { $$ = tRSHFT; }
+ | '+' { $$ = '+'; }
+ | '-' { $$ = '-'; }
+ | '*' { $$ = '*'; }
+ | tSTAR { $$ = '*'; }
+ | '/' { $$ = '/'; }
+ | '%' { $$ = '%'; }
+ | tPOW { $$ = tPOW; }
+ | tDSTAR { $$ = tDSTAR; }
+ | '!' { $$ = '!'; }
+ | '~' { $$ = '~'; }
+ | tUPLUS { $$ = tUPLUS; }
+ | tUMINUS { $$ = tUMINUS; }
+ | tAREF { $$ = tAREF; }
+ | tASET { $$ = tASET; }
+ | '`' { $$ = '`'; }
;
reswords : keyword__LINE__ | keyword__FILE__ | keyword__ENCODING__
@@ -2625,251 +3893,259 @@ reswords : keyword__LINE__ | keyword__FILE__ | keyword__ENCODING__
arg : lhs '=' lex_ctxt arg_rhs
{
- /*%%%*/
$$ = node_assign(p, $1, $4, $3, &@$);
- /*% %*/
- /*% ripper: assign!($1, $4) %*/
+ /*% ripper: assign!($:1, $:4) %*/
}
| var_lhs tOP_ASGN lex_ctxt arg_rhs
{
- /*%%%*/
$$ = new_op_assign(p, $1, $2, $4, $3, &@$);
- /*% %*/
- /*% ripper: opassign!($1, $2, $4) %*/
+ /*% ripper: opassign!($:1, $:2, $:4) %*/
}
| primary_value '[' opt_call_args rbracket tOP_ASGN lex_ctxt arg_rhs
{
- /*%%%*/
$$ = new_ary_op_assign(p, $1, $3, $5, $7, &@3, &@$);
- /*% %*/
- /*% ripper: opassign!(aref_field!($1, escape_Qundef($3)), $5, $7) %*/
+ /*% ripper: opassign!(aref_field!($:1, $:3), $:5, $:7) %*/
}
| primary_value call_op tIDENTIFIER tOP_ASGN lex_ctxt arg_rhs
{
- /*%%%*/
$$ = new_attr_op_assign(p, $1, $2, $3, $4, $6, &@$);
- /*% %*/
- /*% ripper: opassign!(field!($1, $2, $3), $4, $6) %*/
+ /*% ripper: opassign!(field!($:1, $:2, $:3), $:4, $:6) %*/
}
| primary_value call_op tCONSTANT tOP_ASGN lex_ctxt arg_rhs
{
- /*%%%*/
$$ = new_attr_op_assign(p, $1, $2, $3, $4, $6, &@$);
- /*% %*/
- /*% ripper: opassign!(field!($1, $2, $3), $4, $6) %*/
+ /*% ripper: opassign!(field!($:1, $:2, $:3), $:4, $:6) %*/
}
| primary_value tCOLON2 tIDENTIFIER tOP_ASGN lex_ctxt arg_rhs
{
- /*%%%*/
- $$ = new_attr_op_assign(p, $1, ID2VAL(idCOLON2), $3, $4, $6, &@$);
- /*% %*/
- /*% ripper: opassign!(field!($1, ID2VAL(idCOLON2), $3), $4, $6) %*/
+ $$ = new_attr_op_assign(p, $1, idCOLON2, $3, $4, $6, &@$);
+ /*% ripper: opassign!(field!($:1, $:2, $:3), $:4, $:6) %*/
}
| primary_value tCOLON2 tCONSTANT tOP_ASGN lex_ctxt arg_rhs
{
- /*%%%*/
YYLTYPE loc = code_loc_gen(&@1, &@3);
$$ = new_const_op_assign(p, NEW_COLON2($1, $3, &loc), $4, $6, $5, &@$);
- /*% %*/
- /*% ripper: opassign!(const_path_field!($1, $3), $4, $6) %*/
+ /*% ripper: opassign!(const_path_field!($:1, $:3), $:4, $:6) %*/
}
| tCOLON3 tCONSTANT tOP_ASGN lex_ctxt arg_rhs
{
- /*%%%*/
YYLTYPE loc = code_loc_gen(&@1, &@2);
$$ = new_const_op_assign(p, NEW_COLON3($2, &loc), $3, $5, $4, &@$);
- /*% %*/
- /*% ripper: opassign!(top_const_field!($2), $3, $5) %*/
+ /*% ripper: opassign!(top_const_field!($:2), $:3, $:5) %*/
}
| backref tOP_ASGN lex_ctxt arg_rhs
{
- /*%%%*/
rb_backref_error(p, $1);
- $$ = NEW_BEGIN(0, &@$);
+ /*%%%*/
+ $$ = NEW_ERROR(&@$);
/*% %*/
- /*% ripper[error]: backref_error(p, RNODE($1), opassign!(var_field(p, $1), $2, $4)) %*/
+ /*% ripper[error]: backref_error(p, RNODE($:1), opassign!(var_field(p, get_value($:1)), $:2, $:4)) %*/
}
| arg tDOT2 arg
{
- /*%%%*/
value_expr($1);
value_expr($3);
$$ = NEW_DOT2($1, $3, &@$);
- /*% %*/
- /*% ripper: dot2!($1, $3) %*/
+ /*% ripper: dot2!($:1, $:3) %*/
}
| arg tDOT3 arg
{
- /*%%%*/
value_expr($1);
value_expr($3);
$$ = NEW_DOT3($1, $3, &@$);
- /*% %*/
- /*% ripper: dot3!($1, $3) %*/
+ /*% ripper: dot3!($:1, $:3) %*/
}
| arg tDOT2
{
- /*%%%*/
value_expr($1);
$$ = NEW_DOT2($1, new_nil_at(p, &@2.end_pos), &@$);
- /*% %*/
- /*% ripper: dot2!($1, Qnil) %*/
+ /*% ripper: dot2!($:1, Qnil) %*/
}
| arg tDOT3
{
- /*%%%*/
value_expr($1);
$$ = NEW_DOT3($1, new_nil_at(p, &@2.end_pos), &@$);
- /*% %*/
- /*% ripper: dot3!($1, Qnil) %*/
+ /*% ripper: dot3!($:1, Qnil) %*/
}
| tBDOT2 arg
{
- /*%%%*/
value_expr($2);
$$ = NEW_DOT2(new_nil_at(p, &@1.beg_pos), $2, &@$);
- /*% %*/
- /*% ripper: dot2!(Qnil, $2) %*/
+ /*% ripper: dot2!(Qnil, $:2) %*/
}
| tBDOT3 arg
{
- /*%%%*/
value_expr($2);
$$ = NEW_DOT3(new_nil_at(p, &@1.beg_pos), $2, &@$);
- /*% %*/
- /*% ripper: dot3!(Qnil, $2) %*/
+ /*% ripper: dot3!(Qnil, $:2) %*/
}
| arg '+' arg
{
$$ = call_bin_op(p, $1, '+', $3, &@2, &@$);
+ /*% ripper: binary!($:1, ID2VAL('\'+\''), $:3) %*/
}
| arg '-' arg
{
$$ = call_bin_op(p, $1, '-', $3, &@2, &@$);
+ /*% ripper: binary!($:1, ID2VAL('\'-\''), $:3) %*/
}
| arg '*' arg
{
$$ = call_bin_op(p, $1, '*', $3, &@2, &@$);
+ /*% ripper: binary!($:1, ID2VAL('\'*\''), $:3) %*/
}
| arg '/' arg
{
$$ = call_bin_op(p, $1, '/', $3, &@2, &@$);
+ /*% ripper: binary!($:1, ID2VAL('\'/\''), $:3) %*/
}
| arg '%' arg
{
$$ = call_bin_op(p, $1, '%', $3, &@2, &@$);
+ /*% ripper: binary!($:1, ID2VAL('\'%\''), $:3) %*/
}
| arg tPOW arg
{
$$ = call_bin_op(p, $1, idPow, $3, &@2, &@$);
+ /*% ripper: binary!($:1, ID2VAL(idPow), $:3) %*/
}
| tUMINUS_NUM simple_numeric tPOW arg
{
$$ = call_uni_op(p, call_bin_op(p, $2, idPow, $4, &@2, &@$), idUMinus, &@1, &@$);
+ /*%%%*/
+ /*%
+ VALUE val = dispatch3(binary, get_value($:2), ID2VAL(idPow), get_value($:4));
+ val = dispatch2(unary, ID2VAL(idUMinus), val);
+ set_value(val);
+ %*/
}
| tUPLUS arg
{
$$ = call_uni_op(p, $2, idUPlus, &@1, &@$);
+ /*% ripper: unary!(ID2VAL(idUPlus), $:2) %*/
}
| tUMINUS arg
{
$$ = call_uni_op(p, $2, idUMinus, &@1, &@$);
+ /*% ripper: unary!(ID2VAL(idUMinus), $:2) %*/
}
| arg '|' arg
{
$$ = call_bin_op(p, $1, '|', $3, &@2, &@$);
+ /*% ripper: binary!($:1, ID2VAL('\'|\''), $:3) %*/
}
| arg '^' arg
{
$$ = call_bin_op(p, $1, '^', $3, &@2, &@$);
+ /*% ripper: binary!($:1, ID2VAL('\'^\''), $:3) %*/
}
| arg '&' arg
{
$$ = call_bin_op(p, $1, '&', $3, &@2, &@$);
+ /*% ripper: binary!($:1, ID2VAL('\'&\''), $:3) %*/
}
| arg tCMP arg
{
$$ = call_bin_op(p, $1, idCmp, $3, &@2, &@$);
+ /*% ripper: binary!($:1, ID2VAL(idCmp), $:3) %*/
}
| rel_expr %prec tCMP
| arg tEQ arg
{
$$ = call_bin_op(p, $1, idEq, $3, &@2, &@$);
+ /*% ripper: binary!($:1, ID2VAL(idEq), $:3) %*/
}
| arg tEQQ arg
{
$$ = call_bin_op(p, $1, idEqq, $3, &@2, &@$);
+ /*% ripper: binary!($:1, ID2VAL(idEqq), $:3) %*/
}
| arg tNEQ arg
{
$$ = call_bin_op(p, $1, idNeq, $3, &@2, &@$);
+ /*% ripper: binary!($:1, ID2VAL(idNeq), $:3) %*/
}
| arg tMATCH arg
{
$$ = match_op(p, $1, $3, &@2, &@$);
+ /*% ripper: binary!($:1, ID2VAL(idEqTilde), $:3) %*/
}
| arg tNMATCH arg
{
$$ = call_bin_op(p, $1, idNeqTilde, $3, &@2, &@$);
+ /*% ripper: binary!($:1, ID2VAL(idNeqTilde), $:3) %*/
}
| '!' arg
{
$$ = call_uni_op(p, method_cond(p, $2, &@2), '!', &@1, &@$);
+ /*% ripper: unary!(ID2VAL('\'!\''), $:2) %*/
}
| '~' arg
{
$$ = call_uni_op(p, $2, '~', &@1, &@$);
+ /*% ripper: unary!(ID2VAL('\'~\''), $:2) %*/
}
| arg tLSHFT arg
{
$$ = call_bin_op(p, $1, idLTLT, $3, &@2, &@$);
+ /*% ripper: binary!($:1, ID2VAL(idLTLT), $:3) %*/
}
| arg tRSHFT arg
{
$$ = call_bin_op(p, $1, idGTGT, $3, &@2, &@$);
+ /*% ripper: binary!($:1, ID2VAL(idGTGT), $:3) %*/
}
| arg tANDOP arg
{
$$ = logop(p, idANDOP, $1, $3, &@2, &@$);
+ /*% ripper: binary!($:1, ID2VAL(idANDOP), $:3) %*/
}
| arg tOROP arg
{
$$ = logop(p, idOROP, $1, $3, &@2, &@$);
+ /*% ripper: binary!($:1, ID2VAL(idOROP), $:3) %*/
}
- | keyword_defined opt_nl {p->ctxt.in_defined = 1;} arg
+ | keyword_defined '\n'? begin_defined arg
{
- p->ctxt.in_defined = 0;
+ p->ctxt.in_defined = $3.in_defined;
$$ = new_defined(p, $4, &@$);
+ /*% ripper: defined!($:4) %*/
}
- | arg '?' arg opt_nl ':' arg
+ | arg '?' arg '\n'? ':' arg
{
- /*%%%*/
value_expr($1);
$$ = new_if(p, $1, $3, $6, &@$);
fixpos($$, $1);
- /*% %*/
- /*% ripper: ifop!($1, $3, $6) %*/
+ /*% ripper: ifop!($:1, $:3, $:6) %*/
}
- | defn_head f_opt_paren_args '=' endless_arg
+ | defn_head[head] f_opt_paren_args[args] '=' endless_arg[bodystmt]
{
- endless_method_name(p, $<node>1, &@1);
- restore_defun(p, $<node>1->nd_defn);
+ endless_method_name(p, $head->nd_mid, &@head);
+ restore_defun(p, $head);
+ $bodystmt = new_scope_body(p, $args, $bodystmt, &@$);
+ ($$ = $head->nd_def)->nd_loc = @$;
+ RNODE_DEFN($$)->nd_defn = $bodystmt;
/*%%%*/
- $$ = set_defun_body(p, $1, $2, $4, &@$);
- /*% %*/
- /*% ripper: def!(get_value($1), $2, bodystmt!($4, Qnil, Qnil, Qnil)) %*/
+ /*%
+ VALUE val = dispatch4(bodystmt, get_value($:bodystmt), Qnil, Qnil, Qnil);
+ val = dispatch3(def, get_value($:head), get_value($:args), val);
+ set_value(val);
+ %*/
local_pop(p);
}
- | defs_head f_opt_paren_args '=' endless_arg
+ | defs_head[head] f_opt_paren_args[args] '=' endless_arg[bodystmt]
{
- endless_method_name(p, $<node>1, &@1);
- restore_defun(p, $<node>1->nd_defn);
+ endless_method_name(p, $head->nd_mid, &@head);
+ restore_defun(p, $head);
+ $bodystmt = new_scope_body(p, $args, $bodystmt, &@$);
+ ($$ = $head->nd_def)->nd_loc = @$;
+ RNODE_DEFS($$)->nd_defn = $bodystmt;
/*%%%*/
- $$ = set_defun_body(p, $1, $2, $4, &@$);
/*%
- $1 = get_value($1);
+ VALUE val = dispatch4(bodystmt, get_value($:bodystmt), Qnil, Qnil, Qnil);
+ val = defs(p, get_value($:head), get_value($:args), val);
+ set_value(val);
%*/
- /*% ripper: defs!(AREF($1, 0), AREF($1, 1), AREF($1, 2), $2, bodystmt!($4, Qnil, Qnil, Qnil)) %*/
local_pop(p);
}
| primary
@@ -2879,16 +4155,16 @@ arg : lhs '=' lex_ctxt arg_rhs
;
endless_arg : arg %prec modifier_rescue
- | endless_arg modifier_rescue arg
+ | endless_arg modifier_rescue after_rescue arg
{
- /*%%%*/
- $$ = rescued_expr(p, $1, $3, &@1, &@2, &@3);
- /*% %*/
- /*% ripper: rescue_mod!($1, $3) %*/
+ p->ctxt.in_rescue = $3.in_rescue;
+ $$ = rescued_expr(p, $1, $4, &@1, &@2, &@4);
+ /*% ripper: rescue_mod!($:1, $:4) %*/
}
- | keyword_not opt_nl endless_arg
+ | keyword_not '\n'? endless_arg
{
$$ = call_uni_op(p, method_cond(p, $3, &@3), METHOD_NOT, &@1, &@$);
+ /*% ripper: unary!(ID2VAL(idNOT), $:3) %*/
}
;
@@ -2901,11 +4177,13 @@ relop : '>' {$$ = '>';}
rel_expr : arg relop arg %prec '>'
{
$$ = call_bin_op(p, $1, $2, $3, &@2, &@$);
+ /*% ripper: binary!($:1, ID2VAL($2), $:3) %*/
}
| rel_expr relop arg %prec '>'
{
rb_warning1("comparison '%s' after comparison", WARN_ID($2));
$$ = call_bin_op(p, $1, $2, $3, &@2, &@$);
+ /*% ripper: binary!($:1, ID2VAL($2), $:3) %*/
}
;
@@ -2915,6 +4193,20 @@ lex_ctxt : none
}
;
+begin_defined : lex_ctxt
+ {
+ p->ctxt.in_defined = 1;
+ $$ = $1;
+ }
+ ;
+
+after_rescue : lex_ctxt
+ {
+ p->ctxt.in_rescue = after_rescue;
+ $$ = $1;
+ }
+ ;
+
arg_value : arg
{
value_expr($1);
@@ -2929,17 +4221,13 @@ aref_args : none
}
| args ',' assocs trailer
{
- /*%%%*/
$$ = $3 ? arg_append(p, $1, new_hash(p, $3, &@3), &@$) : $1;
- /*% %*/
- /*% ripper: args_add!($1, bare_assoc_hash!($3)) %*/
+ /*% ripper: args_add!($:1, bare_assoc_hash!($:3)) %*/
}
| assocs trailer
{
- /*%%%*/
$$ = $1 ? NEW_LIST(new_hash(p, $1, &@1), &@$) : 0;
- /*% %*/
- /*% ripper: args_add!(args_new!, bare_assoc_hash!($1)) %*/
+ /*% ripper: args_add!(args_new!, bare_assoc_hash!($:1)) %*/
}
;
@@ -2948,45 +4236,38 @@ arg_rhs : arg %prec tOP_ASGN
value_expr($1);
$$ = $1;
}
- | arg modifier_rescue arg
+ | arg modifier_rescue after_rescue arg
{
- /*%%%*/
+ p->ctxt.in_rescue = $3.in_rescue;
value_expr($1);
- $$ = rescued_expr(p, $1, $3, &@1, &@2, &@3);
- /*% %*/
- /*% ripper: rescue_mod!($1, $3) %*/
+ $$ = rescued_expr(p, $1, $4, &@1, &@2, &@4);
+ /*% ripper: rescue_mod!($:1, $:4) %*/
}
;
paren_args : '(' opt_call_args rparen
{
- /*%%%*/
$$ = $2;
- /*% %*/
- /*% ripper: arg_paren!(escape_Qundef($2)) %*/
+ /*% ripper: arg_paren!($:2) %*/
}
| '(' args ',' args_forward rparen
{
if (!check_forwarding_args(p)) {
- $$ = Qnone;
+ $$ = 0;
}
else {
- /*%%%*/
$$ = new_args_forward_call(p, $2, &@4, &@$);
- /*% %*/
- /*% ripper: arg_paren!(args_add!($2, $4)) %*/
+ /*% ripper: arg_paren!(args_add!($:2, $:4)) %*/
}
}
| '(' args_forward rparen
{
if (!check_forwarding_args(p)) {
- $$ = Qnone;
+ $$ = 0;
}
else {
- /*%%%*/
$$ = new_args_forward_call(p, 0, &@2, &@$);
- /*% %*/
- /*% ripper: arg_paren!($2) %*/
+ /*% ripper: arg_paren!($:2) %*/
}
}
;
@@ -3003,53 +4284,41 @@ opt_call_args : none
}
| args ',' assocs ','
{
- /*%%%*/
$$ = $3 ? arg_append(p, $1, new_hash(p, $3, &@3), &@$) : $1;
- /*% %*/
- /*% ripper: args_add!($1, bare_assoc_hash!($3)) %*/
+ /*% ripper: args_add!($:1, bare_assoc_hash!($:3)) %*/
}
| assocs ','
{
- /*%%%*/
$$ = $1 ? NEW_LIST(new_hash(p, $1, &@1), &@1) : 0;
- /*% %*/
- /*% ripper: args_add!(args_new!, bare_assoc_hash!($1)) %*/
+ /*% ripper: args_add!(args_new!, bare_assoc_hash!($:1)) %*/
}
;
call_args : command
{
- /*%%%*/
value_expr($1);
$$ = NEW_LIST($1, &@$);
- /*% %*/
- /*% ripper: args_add!(args_new!, $1) %*/
+ /*% ripper: args_add!(args_new!, $:1) %*/
}
| args opt_block_arg
{
- /*%%%*/
$$ = arg_blk_pass($1, $2);
- /*% %*/
- /*% ripper: args_add_block!($1, $2) %*/
+ /*% ripper: args_add_block!($:1, $:2) %*/
}
| assocs opt_block_arg
{
- /*%%%*/
$$ = $1 ? NEW_LIST(new_hash(p, $1, &@1), &@1) : 0;
$$ = arg_blk_pass($$, $2);
- /*% %*/
- /*% ripper: args_add_block!(args_add!(args_new!, bare_assoc_hash!($1)), $2) %*/
+ /*% ripper: args_add_block!(args_add!(args_new!, bare_assoc_hash!($:1)), $:2) %*/
}
| args ',' assocs opt_block_arg
{
- /*%%%*/
$$ = $3 ? arg_append(p, $1, new_hash(p, $3, &@3), &@$) : $1;
$$ = arg_blk_pass($$, $4);
- /*% %*/
- /*% ripper: args_add_block!(args_add!($1, bare_assoc_hash!($3)), $4) %*/
+ /*% ripper: args_add_block!(args_add!($:1, bare_assoc_hash!($:3)), $:4) %*/
}
| block_arg
- /*% ripper[brace]: args_add_block!(args_new!, $1) %*/
+ /*% ripper: args_add_block!(args_new!, $:1) %*/
;
command_args : {
@@ -3086,24 +4355,19 @@ command_args : {
CMDARG_POP();
if (lookahead) CMDARG_PUSH(0);
$$ = $2;
+ /*% ripper: get_value($:2); %*/
}
;
block_arg : tAMPER arg_value
{
- /*%%%*/
$$ = NEW_BLOCK_PASS($2, &@$);
- /*% %*/
- /*% ripper: $2 %*/
+ /*% ripper: get_value($:2) %*/
}
| tAMPER
{
- if (!local_id(p, idFWD_BLOCK)) {
- compile_error(p, "no anonymous block parameter");
- }
- /*%%%*/
+ forwarding_arg_check(p, idFWD_BLOCK, idFWD_ALL, "block");
$$ = NEW_BLOCK_PASS(NEW_LVAR(idFWD_BLOCK, &@1), &@$);
- /*% %*/
/*% ripper: Qnil %*/
}
;
@@ -3111,63 +4375,49 @@ block_arg : tAMPER arg_value
opt_block_arg : ',' block_arg
{
$$ = $2;
+ /*% ripper: get_value($:2); %*/
}
| none
{
$$ = 0;
+ /*% ripper: Qfalse %*/
}
;
/* value */
args : arg_value
{
- /*%%%*/
$$ = NEW_LIST($1, &@$);
- /*% %*/
- /*% ripper: args_add!(args_new!, $1) %*/
+ /*% ripper: args_add!(args_new!, $:1) %*/
}
- | tSTAR arg_value
+ | arg_splat
{
- /*%%%*/
- $$ = NEW_SPLAT($2, &@$);
- /*% %*/
- /*% ripper: args_add_star!(args_new!, $2) %*/
- }
- | tSTAR
- {
- if (!local_id(p, idFWD_REST) ||
- local_id(p, idFWD_ALL)) {
- compile_error(p, "no anonymous rest parameter");
- }
- /*%%%*/
- $$ = NEW_SPLAT(NEW_LVAR(idFWD_REST, &@1), &@$);
- /*% %*/
- /*% ripper: args_add_star!(args_new!, Qnil) %*/
+ $$ = NEW_SPLAT($arg_splat, &@$);
+ /*% ripper: args_add_star!(args_new!, $:arg_splat) %*/
}
| args ',' arg_value
{
- /*%%%*/
$$ = last_arg_append(p, $1, $3, &@$);
- /*% %*/
- /*% ripper: args_add!($1, $3) %*/
+ /*% ripper: args_add!($:1, $:3) %*/
}
- | args ',' tSTAR arg_value
+ | args ',' arg_splat
{
- /*%%%*/
- $$ = rest_arg_append(p, $1, $4, &@$);
- /*% %*/
- /*% ripper: args_add_star!($1, $4) %*/
+ $$ = rest_arg_append(p, $1, $3, &@$);
+ /*% ripper: args_add_star!($:1, $:3) %*/
}
- | args ',' tSTAR
+ ;
+
+/* value */
+arg_splat : tSTAR arg_value
{
- if (!local_id(p, idFWD_REST) ||
- local_id(p, idFWD_ALL)) {
- compile_error(p, "no anonymous rest parameter");
- }
- /*%%%*/
- $$ = rest_arg_append(p, $1, NEW_LVAR(idFWD_REST, &@3), &@$);
- /*% %*/
- /*% ripper: args_add_star!($1, Qnil) %*/
+ $$ = $2;
+ /*% ripper: get_value($:2); %*/
+ }
+ | tSTAR /* none */
+ {
+ forwarding_arg_check(p, idFWD_REST, idFWD_ALL, "rest");
+ $$ = NEW_LVAR(idFWD_REST, &@1);
+ /*% ripper: Qnil %*/
}
;
@@ -3179,24 +4429,18 @@ mrhs_arg : mrhs
/* value */
mrhs : args ',' arg_value
{
- /*%%%*/
$$ = last_arg_append(p, $1, $3, &@$);
- /*% %*/
- /*% ripper: mrhs_add!(mrhs_new_from_args!($1), $3) %*/
+ /*% ripper: mrhs_add!(mrhs_new_from_args!($:1), $:3) %*/
}
| args ',' tSTAR arg_value
{
- /*%%%*/
$$ = rest_arg_append(p, $1, $4, &@$);
- /*% %*/
- /*% ripper: mrhs_add_star!(mrhs_new_from_args!($1), $4) %*/
+ /*% ripper: mrhs_add_star!(mrhs_new_from_args!($:1), $:4) %*/
}
| tSTAR arg_value
{
- /*%%%*/
$$ = NEW_SPLAT($2, &@$);
- /*% %*/
- /*% ripper: mrhs_add_star!(mrhs_new!, $2) %*/
+ /*% ripper: mrhs_add_star!(mrhs_new!, $:2) %*/
}
;
@@ -3212,10 +4456,8 @@ primary : literal
| backref
| tFID
{
- /*%%%*/
- $$ = NEW_FCALL($1, 0, &@$);
- /*% %*/
- /*% ripper: method_add_arg!(fcall!($1), args_new!) %*/
+ $$ = (NODE *)NEW_FCALL($1, 0, &@$);
+ /*% ripper: method_add_arg!(fcall!($:1), args_new!) %*/
}
| k_begin
{
@@ -3225,121 +4467,91 @@ primary : literal
k_end
{
CMDARG_POP();
- /*%%%*/
set_line_body($3, @1.end_pos.lineno);
$$ = NEW_BEGIN($3, &@$);
nd_set_line($$, @1.end_pos.lineno);
- /*% %*/
- /*% ripper: begin!($3) %*/
- }
- | tLPAREN_ARG {SET_LEX_STATE(EXPR_ENDARG);} rparen
- {
- /*%%%*/
- $$ = NEW_BEGIN(0, &@$);
- /*% %*/
- /*% ripper: paren!(0) %*/
+ /*% ripper: begin!($:3) %*/
}
- | tLPAREN_ARG stmt {SET_LEX_STATE(EXPR_ENDARG);} rparen
+ | tLPAREN_ARG compstmt {SET_LEX_STATE(EXPR_ENDARG);} ')'
{
- /*%%%*/
- if (nd_type_p($2, NODE_SELF)) $2->nd_state = 0;
+ if (nd_type_p($2, NODE_SELF)) RNODE_SELF($2)->nd_state = 0;
$$ = $2;
- /*% %*/
- /*% ripper: paren!($2) %*/
+ /*% ripper: paren!($:2) %*/
}
| tLPAREN compstmt ')'
{
- /*%%%*/
- if (nd_type_p($2, NODE_SELF)) $2->nd_state = 0;
- $$ = $2;
- /*% %*/
- /*% ripper: paren!($2) %*/
+ if (nd_type_p($2, NODE_SELF)) RNODE_SELF($2)->nd_state = 0;
+ $$ = NEW_BLOCK($2, &@$);
+ /*% ripper: paren!($:2) %*/
}
| primary_value tCOLON2 tCONSTANT
{
- /*%%%*/
$$ = NEW_COLON2($1, $3, &@$);
- /*% %*/
- /*% ripper: const_path_ref!($1, $3) %*/
+ /*% ripper: const_path_ref!($:1, $:3) %*/
}
| tCOLON3 tCONSTANT
{
- /*%%%*/
$$ = NEW_COLON3($2, &@$);
- /*% %*/
- /*% ripper: top_const_ref!($2) %*/
+ /*% ripper: top_const_ref!($:2) %*/
}
| tLBRACK aref_args ']'
{
- /*%%%*/
$$ = make_list($2, &@$);
- /*% %*/
- /*% ripper: array!(escape_Qundef($2)) %*/
+ /*% ripper: array!($:2) %*/
}
| tLBRACE assoc_list '}'
{
- /*%%%*/
$$ = new_hash(p, $2, &@$);
- $$->nd_brace = TRUE;
- /*% %*/
- /*% ripper: hash!(escape_Qundef($2)) %*/
+ RNODE_HASH($$)->nd_brace = TRUE;
+ /*% ripper: hash!($:2) %*/
}
| k_return
{
- /*%%%*/
$$ = NEW_RETURN(0, &@$);
- /*% %*/
/*% ripper: return0! %*/
}
- | keyword_yield '(' call_args rparen
+ | k_yield '(' call_args rparen
{
- /*%%%*/
$$ = new_yield(p, $3, &@$);
- /*% %*/
- /*% ripper: yield!(paren!($3)) %*/
+ /*% ripper: yield!(paren!($:3)) %*/
}
- | keyword_yield '(' rparen
+ | k_yield '(' rparen
{
- /*%%%*/
$$ = NEW_YIELD(0, &@$);
- /*% %*/
/*% ripper: yield!(paren!(args_new!)) %*/
}
- | keyword_yield
+ | k_yield
{
- /*%%%*/
$$ = NEW_YIELD(0, &@$);
- /*% %*/
/*% ripper: yield0! %*/
}
- | keyword_defined opt_nl '(' {p->ctxt.in_defined = 1;} expr rparen
+ | keyword_defined '\n'? '(' begin_defined expr rparen
{
- p->ctxt.in_defined = 0;
+ p->ctxt.in_defined = $4.in_defined;
$$ = new_defined(p, $5, &@$);
+ /*% ripper: defined!($:5) %*/
}
| keyword_not '(' expr rparen
{
$$ = call_uni_op(p, method_cond(p, $3, &@3), METHOD_NOT, &@1, &@$);
+ /*% ripper: unary!(ID2VAL(idNOT), $:3) %*/
}
| keyword_not '(' rparen
{
$$ = call_uni_op(p, method_cond(p, new_nil(&@2), &@2), METHOD_NOT, &@1, &@$);
+ /*% ripper: unary!(ID2VAL(idNOT), Qnil) %*/
}
| fcall brace_block
{
- /*%%%*/
- $$ = method_add_block(p, $1, $2, &@$);
- /*% %*/
- /*% ripper: method_add_block!(method_add_arg!(fcall!($1), args_new!), $2) %*/
+ $$ = method_add_block(p, (NODE *)$1, $2, &@$);
+ /*% ripper: method_add_block!(method_add_arg!(fcall!($:1), args_new!), $:2) %*/
}
| method_call
| method_call brace_block
{
- /*%%%*/
- block_dup_check(p, $1->nd_args, $2);
+ block_dup_check(p, get_nd_args(p, $1), $2);
$$ = method_add_block(p, $1, $2, &@$);
- /*% %*/
- /*% ripper: method_add_block!($1, $2) %*/
+ /*% ripper: method_add_block!($:1, $:2) %*/
}
| lambda
| k_if expr_value then
@@ -3347,88 +4559,76 @@ primary : literal
if_tail
k_end
{
- /*%%%*/
$$ = new_if(p, $2, $4, $5, &@$);
fixpos($$, $2);
- /*% %*/
- /*% ripper: if!($2, $4, escape_Qundef($5)) %*/
+ /*% ripper: if!($:2, $:4, $:5) %*/
}
| k_unless expr_value then
compstmt
opt_else
k_end
{
- /*%%%*/
$$ = new_unless(p, $2, $4, $5, &@$);
fixpos($$, $2);
- /*% %*/
- /*% ripper: unless!($2, $4, escape_Qundef($5)) %*/
+ /*% ripper: unless!($:2, $:4, $:5) %*/
}
| k_while expr_value_do
compstmt
k_end
{
- /*%%%*/
+ restore_block_exit(p, $1);
$$ = NEW_WHILE(cond(p, $2, &@2), $3, 1, &@$);
fixpos($$, $2);
- /*% %*/
- /*% ripper: while!($2, $3) %*/
+ /*% ripper: while!($:2, $:3) %*/
}
| k_until expr_value_do
compstmt
k_end
{
- /*%%%*/
+ restore_block_exit(p, $1);
$$ = NEW_UNTIL(cond(p, $2, &@2), $3, 1, &@$);
fixpos($$, $2);
- /*% %*/
- /*% ripper: until!($2, $3) %*/
+ /*% ripper: until!($:2, $:3) %*/
}
- | k_case expr_value opt_terms
+ | k_case expr_value terms?
{
- $<val>$ = p->case_labels;
- p->case_labels = Qnil;
- }
+ $$ = p->case_labels;
+ p->case_labels = CHECK_LITERAL_WHEN;
+ }<labels>
case_body
k_end
{
- if (RTEST(p->case_labels)) rb_hash_clear(p->case_labels);
- p->case_labels = $<val>4;
- /*%%%*/
+ if (CASE_LABELS_ENABLED_P(p->case_labels)) st_free_table(p->case_labels);
+ p->case_labels = $<labels>4;
$$ = NEW_CASE($2, $5, &@$);
fixpos($$, $2);
- /*% %*/
- /*% ripper: case!($2, $5) %*/
+ /*% ripper: case!($:2, $:5) %*/
}
- | k_case opt_terms
+ | k_case terms?
{
- $<val>$ = p->case_labels;
+ $$ = p->case_labels;
p->case_labels = 0;
- }
+ }<labels>
case_body
k_end
{
- if (RTEST(p->case_labels)) rb_hash_clear(p->case_labels);
- p->case_labels = $<val>3;
- /*%%%*/
+ if (p->case_labels) st_free_table(p->case_labels);
+ p->case_labels = $<labels>3;
$$ = NEW_CASE2($4, &@$);
- /*% %*/
- /*% ripper: case!(Qnil, $4) %*/
+ /*% ripper: case!(Qnil, $:4) %*/
}
- | k_case expr_value opt_terms
+ | k_case expr_value terms?
p_case_body
k_end
{
- /*%%%*/
$$ = NEW_CASE3($2, $4, &@$);
- /*% %*/
- /*% ripper: case!($2, $4) %*/
+ /*% ripper: case!($:2, $:4) %*/
}
| k_for for_var keyword_in expr_value_do
compstmt
k_end
{
- /*%%%*/
+ restore_block_exit(p, $1);
/*
* for a, b, c in e
* #=>
@@ -3439,15 +4639,16 @@ primary : literal
* e.each{|x| a, = x}
*/
ID id = internal_id(p);
- NODE *m = NEW_ARGS_AUX(0, 0, &NULL_LOC);
- NODE *args, *scope, *internal_var = NEW_DVAR(id, &@2);
+ rb_node_args_aux_t *m = NEW_ARGS_AUX(0, 0, &NULL_LOC);
+ rb_node_args_t *args;
+ NODE *scope, *internal_var = NEW_DVAR(id, &@2);
rb_ast_id_table_t *tbl = rb_ast_new_local_table(p->ast, 1);
tbl->ids[0] = id; /* internal id */
switch (nd_type($2)) {
case NODE_LASGN:
case NODE_DASGN: /* e.each {|internal_var| a = internal_var; ... } */
- $2->nd_value = internal_var;
+ set_nd_value(p, $2, internal_var);
id = 0;
m->nd_plen = 1;
m->nd_next = $2;
@@ -3456,146 +4657,121 @@ primary : literal
m->nd_next = node_assign(p, $2, NEW_FOR_MASGN(internal_var, &@2), NO_LEX_CTXT, &@2);
break;
default: /* e.each {|*internal_var| @a, B, c[1], d.attr = internal_val; ... } */
- m->nd_next = node_assign(p, NEW_MASGN(NEW_LIST($2, &@2), 0, &@2), internal_var, NO_LEX_CTXT, &@2);
+ m->nd_next = node_assign(p, (NODE *)NEW_MASGN(NEW_LIST($2, &@2), 0, &@2), internal_var, NO_LEX_CTXT, &@2);
}
/* {|*internal_id| <m> = internal_id; ... } */
args = new_args(p, m, 0, id, 0, new_args_tail(p, 0, 0, 0, &@2), &@2);
- scope = NEW_NODE(NODE_SCOPE, tbl, $5, args, &@$);
+ scope = NEW_SCOPE2(tbl, args, $5, &@$);
$$ = NEW_FOR($4, scope, &@$);
fixpos($$, $2);
- /*% %*/
- /*% ripper: for!($2, $4, $5) %*/
+ /*% ripper: for!($:2, $:4, $:5) %*/
}
| k_class cpath superclass
{
- if (p->ctxt.in_def) {
- YYLTYPE loc = code_loc_gen(&@1, &@2);
- yyerror1(&loc, "class definition in method body");
- }
- p->ctxt.in_class = 1;
- local_push(p, 0);
+ begin_definition("class", &@k_class, &@cpath);
}
bodystmt
k_end
{
- /*%%%*/
- $$ = NEW_CLASS($2, $5, $3, &@$);
- nd_set_line($$->nd_body, @6.end_pos.lineno);
- set_line_body($5, @3.end_pos.lineno);
- nd_set_line($$, @3.end_pos.lineno);
- /*% %*/
- /*% ripper: class!($2, $3, $5) %*/
+ $$ = NEW_CLASS($cpath, $bodystmt, $superclass, &@$);
+ nd_set_line(RNODE_CLASS($$)->nd_body, @k_end.end_pos.lineno);
+ set_line_body($bodystmt, @superclass.end_pos.lineno);
+ nd_set_line($$, @superclass.end_pos.lineno);
+ /*% ripper: class!($:cpath, $:superclass, $:bodystmt) %*/
local_pop(p);
- p->ctxt.in_class = $<ctxt>1.in_class;
- p->ctxt.shareable_constant_value = $<ctxt>1.shareable_constant_value;
+ p->ctxt.in_class = $k_class.in_class;
+ p->ctxt.shareable_constant_value = $k_class.shareable_constant_value;
}
- | k_class tLSHFT expr
+ | k_class tLSHFT expr_value
{
- p->ctxt.in_def = 0;
- p->ctxt.in_class = 0;
- local_push(p, 0);
+ begin_definition("", &@k_class, &@tLSHFT);
}
term
bodystmt
k_end
{
- /*%%%*/
- $$ = NEW_SCLASS($3, $6, &@$);
- nd_set_line($$->nd_body, @7.end_pos.lineno);
- set_line_body($6, nd_line($3));
- fixpos($$, $3);
- /*% %*/
- /*% ripper: sclass!($3, $6) %*/
+ $$ = NEW_SCLASS($expr_value, $bodystmt, &@$);
+ nd_set_line(RNODE_SCLASS($$)->nd_body, @k_end.end_pos.lineno);
+ set_line_body($bodystmt, nd_line($expr_value));
+ fixpos($$, $expr_value);
+ /*% ripper: sclass!($:expr_value, $:bodystmt) %*/
local_pop(p);
- p->ctxt.in_def = $<ctxt>1.in_def;
- p->ctxt.in_class = $<ctxt>1.in_class;
- p->ctxt.shareable_constant_value = $<ctxt>1.shareable_constant_value;
+ p->ctxt.in_def = $k_class.in_def;
+ p->ctxt.in_class = $k_class.in_class;
+ p->ctxt.shareable_constant_value = $k_class.shareable_constant_value;
}
| k_module cpath
{
- if (p->ctxt.in_def) {
- YYLTYPE loc = code_loc_gen(&@1, &@2);
- yyerror1(&loc, "module definition in method body");
- }
- p->ctxt.in_class = 1;
- local_push(p, 0);
+ begin_definition("module", &@k_module, &@cpath);
}
bodystmt
k_end
{
- /*%%%*/
- $$ = NEW_MODULE($2, $4, &@$);
- nd_set_line($$->nd_body, @5.end_pos.lineno);
- set_line_body($4, @2.end_pos.lineno);
- nd_set_line($$, @2.end_pos.lineno);
- /*% %*/
- /*% ripper: module!($2, $4) %*/
+ $$ = NEW_MODULE($cpath, $bodystmt, &@$);
+ nd_set_line(RNODE_MODULE($$)->nd_body, @k_end.end_pos.lineno);
+ set_line_body($bodystmt, @cpath.end_pos.lineno);
+ nd_set_line($$, @cpath.end_pos.lineno);
+ /*% ripper: module!($:cpath, $:bodystmt) %*/
local_pop(p);
- p->ctxt.in_class = $<ctxt>1.in_class;
- p->ctxt.shareable_constant_value = $<ctxt>1.shareable_constant_value;
+ p->ctxt.in_class = $k_module.in_class;
+ p->ctxt.shareable_constant_value = $k_module.shareable_constant_value;
}
- | defn_head
- f_arglist
+ | defn_head[head]
+ f_arglist[args]
{
- /*%%%*/
- push_end_expect_token_locations(p, &@1.beg_pos);
- /*% %*/
+ push_end_expect_token_locations(p, &@head.beg_pos);
}
bodystmt
k_end
{
- restore_defun(p, $<node>1->nd_defn);
- /*%%%*/
- $$ = set_defun_body(p, $1, $2, $4, &@$);
- /*% %*/
- /*% ripper: def!(get_value($1), $2, $4) %*/
+ restore_defun(p, $head);
+ $bodystmt = new_scope_body(p, $args, $bodystmt, &@$);
+ ($$ = $head->nd_def)->nd_loc = @$;
+ RNODE_DEFN($$)->nd_defn = $bodystmt;
+ /*% ripper: def!($:head, $:args, $:bodystmt) %*/
local_pop(p);
}
- | defs_head
- f_arglist
+ | defs_head[head]
+ f_arglist[args]
{
- /*%%%*/
- push_end_expect_token_locations(p, &@1.beg_pos);
- /*% %*/
+ push_end_expect_token_locations(p, &@head.beg_pos);
}
bodystmt
k_end
{
- restore_defun(p, $<node>1->nd_defn);
- /*%%%*/
- $$ = set_defun_body(p, $1, $2, $4, &@$);
- /*%
- $1 = get_value($1);
- %*/
- /*% ripper: defs!(AREF($1, 0), AREF($1, 1), AREF($1, 2), $2, $4) %*/
+ restore_defun(p, $head);
+ $bodystmt = new_scope_body(p, $args, $bodystmt, &@$);
+ ($$ = $head->nd_def)->nd_loc = @$;
+ RNODE_DEFS($$)->nd_defn = $bodystmt;
+ /*% ripper: defs(p, get_value($:head), get_value($:args), get_value($:bodystmt)) %*/
local_pop(p);
}
| keyword_break
{
- /*%%%*/
- $$ = NEW_BREAK(0, &@$);
- /*% %*/
+ $<node>$ = add_block_exit(p, NEW_BREAK(0, &@$));
/*% ripper: break!(args_new!) %*/
}
| keyword_next
{
- /*%%%*/
- $$ = NEW_NEXT(0, &@$);
- /*% %*/
+ $<node>$ = add_block_exit(p, NEW_NEXT(0, &@$));
/*% ripper: next!(args_new!) %*/
}
| keyword_redo
{
- /*%%%*/
- $$ = NEW_REDO(&@$);
- /*% %*/
+ $<node>$ = add_block_exit(p, NEW_REDO(&@$));
/*% ripper: redo! %*/
}
| keyword_retry
{
- /*%%%*/
+ if (!p->ctxt.in_defined) {
+ switch (p->ctxt.in_rescue) {
+ case before_rescue: yyerror1(&@1, "Invalid retry without rescue"); break;
+ case after_rescue: /* ok */ break;
+ case after_else: yyerror1(&@1, "Invalid retry after else"); break;
+ case after_ensure: yyerror1(&@1, "Invalid retry after ensure"); break;
+ }
+ }
$$ = NEW_RETRY(&@$);
- /*% %*/
/*% ripper: retry! %*/
}
;
@@ -3610,9 +4786,7 @@ primary_value : primary
k_begin : keyword_begin
{
token_info_push(p, "begin", &@$);
- /*%%%*/
push_end_expect_token_locations(p, &@1.beg_pos);
- /*% %*/
}
;
@@ -3630,80 +4804,70 @@ k_if : keyword_if
p->token_info->nonspc = 0;
}
}
- /*%%%*/
push_end_expect_token_locations(p, &@1.beg_pos);
- /*% %*/
}
;
k_unless : keyword_unless
{
token_info_push(p, "unless", &@$);
- /*%%%*/
push_end_expect_token_locations(p, &@1.beg_pos);
- /*% %*/
}
;
-k_while : keyword_while
+k_while : keyword_while allow_exits
{
+ $$ = $allow_exits;
token_info_push(p, "while", &@$);
- /*%%%*/
push_end_expect_token_locations(p, &@1.beg_pos);
- /*% %*/
}
;
-k_until : keyword_until
+k_until : keyword_until allow_exits
{
+ $$ = $allow_exits;
token_info_push(p, "until", &@$);
- /*%%%*/
push_end_expect_token_locations(p, &@1.beg_pos);
- /*% %*/
}
;
k_case : keyword_case
{
token_info_push(p, "case", &@$);
- /*%%%*/
push_end_expect_token_locations(p, &@1.beg_pos);
- /*% %*/
}
;
-k_for : keyword_for
+k_for : keyword_for allow_exits
{
+ $$ = $allow_exits;
token_info_push(p, "for", &@$);
- /*%%%*/
push_end_expect_token_locations(p, &@1.beg_pos);
- /*% %*/
}
;
k_class : keyword_class
{
token_info_push(p, "class", &@$);
- $<ctxt>$ = p->ctxt;
- /*%%%*/
+ $$ = p->ctxt;
+ p->ctxt.in_rescue = before_rescue;
push_end_expect_token_locations(p, &@1.beg_pos);
- /*% %*/
}
;
k_module : keyword_module
{
token_info_push(p, "module", &@$);
- $<ctxt>$ = p->ctxt;
- /*%%%*/
+ $$ = p->ctxt;
+ p->ctxt.in_rescue = before_rescue;
push_end_expect_token_locations(p, &@1.beg_pos);
- /*% %*/
}
;
k_def : keyword_def
{
token_info_push(p, "def", &@$);
+ $$ = NEW_DEF_TEMP(&@$);
p->ctxt.in_argdef = 1;
}
;
@@ -3711,31 +4875,29 @@ k_def : keyword_def
k_do : keyword_do
{
token_info_push(p, "do", &@$);
- /*%%%*/
push_end_expect_token_locations(p, &@1.beg_pos);
- /*% %*/
-
}
;
k_do_block : keyword_do_block
{
token_info_push(p, "do", &@$);
- /*%%%*/
push_end_expect_token_locations(p, &@1.beg_pos);
- /*% %*/
}
;
k_rescue : keyword_rescue
{
token_info_warn(p, "rescue", p->token_info, 1, &@$);
+ $$ = p->ctxt;
+ p->ctxt.in_rescue = after_rescue;
}
;
k_ensure : keyword_ensure
{
token_info_warn(p, "ensure", p->token_info, 1, &@$);
+ $$ = p->ctxt;
}
;
@@ -3770,9 +4932,7 @@ k_elsif : keyword_elsif
k_end : keyword_end
{
token_info_pop(p, "end", &@$);
- /*%%%*/
pop_end_expect_token_locations(p);
- /*% %*/
}
| tDUMNY_END
{
@@ -3787,6 +4947,13 @@ k_return : keyword_return
}
;
+k_yield : keyword_yield
+ {
+ if (!p->ctxt.in_defined && !p->ctxt.in_def && !compile_for_eval)
+ yyerror1(&@1, "Invalid yield");
+ }
+ ;
+
then : term
| keyword_then
| term keyword_then
@@ -3801,21 +4968,17 @@ if_tail : opt_else
compstmt
if_tail
{
- /*%%%*/
$$ = new_if(p, $2, $4, $5, &@$);
fixpos($$, $2);
- /*% %*/
- /*% ripper: elsif!($2, $4, escape_Qundef($5)) %*/
+ /*% ripper: elsif!($:2, $:4, $:5) %*/
}
;
opt_else : none
| k_else compstmt
{
- /*%%%*/
$$ = $2;
- /*% %*/
- /*% ripper: else!($2) %*/
+ /*% ripper: else!($:2) %*/
}
;
@@ -3825,93 +4988,75 @@ for_var : lhs
f_marg : f_norm_arg
{
- /*%%%*/
$$ = assignable(p, $1, 0, &@$);
mark_lvar_used(p, $$);
- /*% %*/
- /*% ripper: assignable(p, $1) %*/
+ /*% ripper: ripper_assignable(p, $1, get_value($:1)) %*/
}
| tLPAREN f_margs rparen
{
- /*%%%*/
- $$ = $2;
- /*% %*/
- /*% ripper: mlhs_paren!($2) %*/
+ $$ = (NODE *)$2;
+ /*% ripper: mlhs_paren!($:2) %*/
}
;
f_marg_list : f_marg
{
- /*%%%*/
$$ = NEW_LIST($1, &@$);
- /*% %*/
- /*% ripper: mlhs_add!(mlhs_new!, $1) %*/
+ /*% ripper: mlhs_add!(mlhs_new!, $:1) %*/
}
| f_marg_list ',' f_marg
{
- /*%%%*/
$$ = list_append(p, $1, $3);
- /*% %*/
- /*% ripper: mlhs_add!($1, $3) %*/
+ /*% ripper: mlhs_add!($:1, $:3) %*/
}
;
f_margs : f_marg_list
{
- /*%%%*/
$$ = NEW_MASGN($1, 0, &@$);
- /*% %*/
- /*% ripper: $1 %*/
+ /*% ripper: get_value($:1) %*/
}
| f_marg_list ',' f_rest_marg
{
- /*%%%*/
$$ = NEW_MASGN($1, $3, &@$);
- /*% %*/
- /*% ripper: mlhs_add_star!($1, $3) %*/
+ /*% ripper: mlhs_add_star!($:1, $:3) %*/
}
| f_marg_list ',' f_rest_marg ',' f_marg_list
{
- /*%%%*/
$$ = NEW_MASGN($1, NEW_POSTARG($3, $5, &@$), &@$);
- /*% %*/
- /*% ripper: mlhs_add_post!(mlhs_add_star!($1, $3), $5) %*/
+ /*% ripper: mlhs_add_post!(mlhs_add_star!($:1, $:3), $:5) %*/
}
| f_rest_marg
{
- /*%%%*/
$$ = NEW_MASGN(0, $1, &@$);
- /*% %*/
- /*% ripper: mlhs_add_star!(mlhs_new!, $1) %*/
+ /*% ripper: mlhs_add_star!(mlhs_new!, $:1) %*/
}
| f_rest_marg ',' f_marg_list
{
- /*%%%*/
$$ = NEW_MASGN(0, NEW_POSTARG($1, $3, &@$), &@$);
- /*% %*/
- /*% ripper: mlhs_add_post!(mlhs_add_star!(mlhs_new!, $1), $3) %*/
+ /*% ripper: mlhs_add_post!(mlhs_add_star!(mlhs_new!, $:1), $:3) %*/
}
;
f_rest_marg : tSTAR f_norm_arg
{
- /*%%%*/
$$ = assignable(p, $2, 0, &@$);
mark_lvar_used(p, $$);
- /*% %*/
- /*% ripper: assignable(p, $2) %*/
+ /*% ripper: ripper_assignable(p, $2, get_value($:2)) %*/
}
| tSTAR
{
- /*%%%*/
$$ = NODE_SPECIAL_NO_NAME_REST;
- /*% %*/
/*% ripper: Qnil %*/
}
;
f_any_kwrest : f_kwrest
- | f_no_kwarg {$$ = ID2VAL(idNil);}
+ | f_no_kwarg
+ {
+ $$ = idNil;
+ /*% ripper: ID2VAL(idNil) %*/
+ }
;
f_eq : {p->ctxt.in_argdef = 0;} '=';
@@ -3919,101 +5064,120 @@ f_eq : {p->ctxt.in_argdef = 0;} '=';
block_args_tail : f_block_kwarg ',' f_kwrest opt_f_block_arg
{
$$ = new_args_tail(p, $1, $3, $4, &@3);
+ /*% ripper: rb_ary_new_from_args(3, get_value($:1), get_value($:3), get_value($:4)); %*/
}
| f_block_kwarg opt_f_block_arg
{
- $$ = new_args_tail(p, $1, Qnone, $2, &@1);
+ $$ = new_args_tail(p, $1, 0, $2, &@1);
+ /*% ripper: rb_ary_new_from_args(3, get_value($:1), Qnil, get_value($:2)); %*/
}
| f_any_kwrest opt_f_block_arg
{
- $$ = new_args_tail(p, Qnone, $1, $2, &@1);
+ $$ = new_args_tail(p, 0, $1, $2, &@1);
+ /*% ripper: rb_ary_new_from_args(3, Qnil, get_value($:1), get_value($:2)); %*/
}
| f_block_arg
{
- $$ = new_args_tail(p, Qnone, Qnone, $1, &@1);
+ $$ = new_args_tail(p, 0, 0, $1, &@1);
+ /*% ripper: rb_ary_new_from_args(3, Qnil, Qnil, get_value($:1)); %*/
}
;
opt_block_args_tail : ',' block_args_tail
{
$$ = $2;
+ /*% ripper: get_value($:2); %*/
}
| /* none */
{
- $$ = new_args_tail(p, Qnone, Qnone, Qnone, &@0);
+ $$ = new_args_tail(p, 0, 0, 0, &@0);
+ /*% ripper: rb_ary_new_from_args(3, Qnil, Qnil, Qnil); %*/
}
;
excessed_comma : ','
{
/* magic number for rest_id in iseq_set_arguments() */
- /*%%%*/
$$ = NODE_SPECIAL_EXCESSIVE_COMMA;
- /*% %*/
/*% ripper: excessed_comma! %*/
}
;
block_param : f_arg ',' f_block_optarg ',' f_rest_arg opt_block_args_tail
{
- $$ = new_args(p, $1, $3, $5, Qnone, $6, &@$);
+ $$ = new_args(p, $1, $3, $5, 0, $6, &@$);
+ /*% ripper: ripper_new_args(p, get_value($:1), get_value($:3), get_value($:5), Qnil, get_value($:6)) %*/
}
| f_arg ',' f_block_optarg ',' f_rest_arg ',' f_arg opt_block_args_tail
{
$$ = new_args(p, $1, $3, $5, $7, $8, &@$);
+ /*% ripper: ripper_new_args(p, get_value($:1), get_value($:3), get_value($:5), get_value($:7), get_value($:8)) %*/
}
| f_arg ',' f_block_optarg opt_block_args_tail
{
- $$ = new_args(p, $1, $3, Qnone, Qnone, $4, &@$);
+ $$ = new_args(p, $1, $3, 0, 0, $4, &@$);
+ /*% ripper: ripper_new_args(p, get_value($:1), get_value($:3), Qnil, Qnil, get_value($:4)) %*/
}
| f_arg ',' f_block_optarg ',' f_arg opt_block_args_tail
{
- $$ = new_args(p, $1, $3, Qnone, $5, $6, &@$);
+ $$ = new_args(p, $1, $3, 0, $5, $6, &@$);
+ /*% ripper: ripper_new_args(p, get_value($:1), get_value($:3), Qnil, get_value($:5), get_value($:6)) %*/
}
| f_arg ',' f_rest_arg opt_block_args_tail
{
- $$ = new_args(p, $1, Qnone, $3, Qnone, $4, &@$);
+ $$ = new_args(p, $1, 0, $3, 0, $4, &@$);
+ /*% ripper: ripper_new_args(p, get_value($:1), Qnil, get_value($:3), Qnil, get_value($:4)) %*/
}
| f_arg excessed_comma
{
- $$ = new_args_tail(p, Qnone, Qnone, Qnone, &@2);
- $$ = new_args(p, $1, Qnone, $2, Qnone, $$, &@$);
+ $$ = new_args_tail(p, 0, 0, 0, &@2);
+ $$ = new_args(p, $1, 0, $2, 0, $$, &@$);
+ /*% ripper: ripper_new_args(p, get_value($:1), Qnil, get_value($:2), Qnil, rb_ary_new_from_args(3, Qnil, Qnil, Qnil)) %*/
}
| f_arg ',' f_rest_arg ',' f_arg opt_block_args_tail
{
- $$ = new_args(p, $1, Qnone, $3, $5, $6, &@$);
+ $$ = new_args(p, $1, 0, $3, $5, $6, &@$);
+ /*% ripper: ripper_new_args(p, get_value($:1), Qnil, get_value($:3), get_value($:5), get_value($:6)) %*/
}
| f_arg opt_block_args_tail
{
- $$ = new_args(p, $1, Qnone, Qnone, Qnone, $2, &@$);
+ $$ = new_args(p, $1, 0, 0, 0, $2, &@$);
+ /*% ripper: ripper_new_args(p, get_value($:1), Qnil, Qnil, Qnil, get_value($:2)) %*/
}
| f_block_optarg ',' f_rest_arg opt_block_args_tail
{
- $$ = new_args(p, Qnone, $1, $3, Qnone, $4, &@$);
+ $$ = new_args(p, 0, $1, $3, 0, $4, &@$);
+ /*% ripper: ripper_new_args(p, Qnil, get_value($:1), get_value($:3), Qnil, get_value($:4)) %*/
}
| f_block_optarg ',' f_rest_arg ',' f_arg opt_block_args_tail
{
- $$ = new_args(p, Qnone, $1, $3, $5, $6, &@$);
+ $$ = new_args(p, 0, $1, $3, $5, $6, &@$);
+ /*% ripper: ripper_new_args(p, Qnil, get_value($:1), get_value($:3), get_value($:5), get_value($:6)) %*/
}
| f_block_optarg opt_block_args_tail
{
- $$ = new_args(p, Qnone, $1, Qnone, Qnone, $2, &@$);
+ $$ = new_args(p, 0, $1, 0, 0, $2, &@$);
+ /*% ripper: ripper_new_args(p, Qnil, get_value($:1), Qnil, Qnil, get_value($:2)) %*/
}
| f_block_optarg ',' f_arg opt_block_args_tail
{
- $$ = new_args(p, Qnone, $1, Qnone, $3, $4, &@$);
+ $$ = new_args(p, 0, $1, 0, $3, $4, &@$);
+ /*% ripper: ripper_new_args(p, Qnil, get_value($:1), Qnil, get_value($:3), get_value($:4)) %*/
}
| f_rest_arg opt_block_args_tail
{
- $$ = new_args(p, Qnone, Qnone, $1, Qnone, $2, &@$);
+ $$ = new_args(p, 0, 0, $1, 0, $2, &@$);
+ /*% ripper: ripper_new_args(p, Qnil, Qnil, get_value($:1), Qnil, get_value($:2)) %*/
}
| f_rest_arg ',' f_arg opt_block_args_tail
{
- $$ = new_args(p, Qnone, Qnone, $1, $3, $4, &@$);
+ $$ = new_args(p, 0, 0, $1, $3, $4, &@$);
+ /*% ripper: ripper_new_args(p, Qnil, Qnil, get_value($:1), get_value($:3), get_value($:4)) %*/
}
| block_args_tail
{
- $$ = new_args(p, Qnone, Qnone, Qnone, Qnone, $1, &@$);
+ $$ = new_args(p, 0, 0, 0, 0, $1, &@$);
+ /*% ripper: ripper_new_args(p, Qnil, Qnil, Qnil, Qnil, get_value($:1)) %*/
}
;
@@ -4029,47 +5193,47 @@ block_param_def : '|' opt_bv_decl '|'
p->cur_arg = 0;
p->max_numparam = ORDINAL_PARAM;
p->ctxt.in_argdef = 0;
- /*%%%*/
$$ = 0;
- /*% %*/
- /*% ripper: block_var!(params!(Qnil,Qnil,Qnil,Qnil,Qnil,Qnil,Qnil), escape_Qundef($2)) %*/
+ /*%%%*/
+ /*%
+ VALUE val = dispatch7(params, Qnil,Qnil,Qnil,Qnil,Qnil,Qnil,Qnil);
+ val = dispatch2(block_var, val, get_value($:2));
+ set_value(val);
+ %*/
}
| '|' block_param opt_bv_decl '|'
{
p->cur_arg = 0;
p->max_numparam = ORDINAL_PARAM;
p->ctxt.in_argdef = 0;
- /*%%%*/
$$ = $2;
- /*% %*/
- /*% ripper: block_var!(escape_Qundef($2), escape_Qundef($3)) %*/
+ /*% ripper: block_var!($:2, $:3) %*/
}
;
-opt_bv_decl : opt_nl
+opt_bv_decl : '\n'?
{
$$ = 0;
+ /*% ripper: Qfalse %*/
}
- | opt_nl ';' bv_decls opt_nl
+ | '\n'? ';' bv_decls '\n'?
{
- /*%%%*/
$$ = 0;
- /*% %*/
- /*% ripper: $3 %*/
+ /*% ripper: get_value($:3) %*/
}
;
bv_decls : bvar
- /*% ripper[brace]: rb_ary_new3(1, get_value($1)) %*/
+ /*% ripper[brace]: rb_ary_new3(1, get_value($:1)) %*/
| bv_decls ',' bvar
- /*% ripper[brace]: rb_ary_push($1, get_value($3)) %*/
+ /*% ripper[brace]: rb_ary_push(get_value($:1), get_value($:3)) %*/
;
bvar : tIDENTIFIER
{
- new_bv(p, get_id($1));
- /*% ripper: get_value($1) %*/
+ new_bv(p, $1);
+ /*% ripper: get_value($:1) %*/
}
| f_bad_arg
{
@@ -4077,62 +5241,70 @@ bvar : tIDENTIFIER
}
;
-lambda : tLAMBDA
+max_numparam : {
+ $$ = p->max_numparam;
+ p->max_numparam = 0;
+ }
+ ;
+
+numparam : {
+ $$ = numparam_push(p);
+ }
+ ;
+
+it_id : {
+ $$ = p->it_id;
+ p->it_id = 0;
+ }
+ ;
+
+lambda : tLAMBDA[dyna]
{
token_info_push(p, "->", &@1);
- $<vars>1 = dyna_push(p);
+ $<vars>dyna = dyna_push(p);
$<num>$ = p->lex.lpar_beg;
p->lex.lpar_beg = p->lex.paren_nest;
- }
- {
- $<num>$ = p->max_numparam;
- p->max_numparam = 0;
- }
- {
- $<node>$ = numparam_push(p);
- }
- f_larglist
+ }[lpar]
+ max_numparam numparam it_id allow_exits
+ f_larglist[args]
{
CMDARG_PUSH(0);
}
- lambda_body
+ lambda_body[body]
{
int max_numparam = p->max_numparam;
- p->lex.lpar_beg = $<num>2;
- p->max_numparam = $<num>3;
+ ID it_id = p->it_id;
+ p->lex.lpar_beg = $<num>lpar;
+ p->max_numparam = $max_numparam;
+ p->it_id = $it_id;
+ restore_block_exit(p, $allow_exits);
CMDARG_POP();
- $5 = args_with_numbered(p, $5, max_numparam);
- /*%%%*/
+ $args = args_with_numbered(p, $args, max_numparam, it_id);
{
- YYLTYPE loc = code_loc_gen(&@5, &@7);
- $$ = NEW_LAMBDA($5, $7, &loc);
- nd_set_line($$->nd_body, @7.end_pos.lineno);
- nd_set_line($$, @5.end_pos.lineno);
+ YYLTYPE loc = code_loc_gen(&@args, &@body);
+ $$ = NEW_LAMBDA($args, $body, &loc);
+ nd_set_line(RNODE_LAMBDA($$)->nd_body, @body.end_pos.lineno);
+ nd_set_line($$, @args.end_pos.lineno);
nd_set_first_loc($$, @1.beg_pos);
}
- /*% %*/
- /*% ripper: lambda!($5, $7) %*/
- numparam_pop(p, $<node>4);
- dyna_pop(p, $<vars>1);
+ /*% ripper: lambda!($:args, $:body) %*/
+ numparam_pop(p, $numparam);
+ dyna_pop(p, $<vars>dyna);
}
;
f_larglist : '(' f_args opt_bv_decl ')'
{
p->ctxt.in_argdef = 0;
- /*%%%*/
$$ = $2;
p->max_numparam = ORDINAL_PARAM;
- /*% %*/
- /*% ripper: paren!($2) %*/
+ /*% ripper: paren!($:2) %*/
}
| f_args
{
p->ctxt.in_argdef = 0;
- /*%%%*/
- if (!args_info_empty_p($1->nd_ainfo))
+ if (!args_info_empty_p(&$1->nd_ainfo))
p->max_numparam = ORDINAL_PARAM;
- /*% %*/
$$ = $1;
}
;
@@ -4141,231 +5313,183 @@ lambda_body : tLAMBEG compstmt '}'
{
token_info_pop(p, "}", &@3);
$$ = $2;
+ /*% ripper: get_value($:2); %*/
}
| keyword_do_LAMBDA
{
- /*%%%*/
push_end_expect_token_locations(p, &@1.beg_pos);
- /*% %*/
}
bodystmt k_end
{
$$ = $3;
+ /*% ripper: get_value($:3); %*/
}
;
do_block : k_do_block do_body k_end
{
$$ = $2;
- /*%%%*/
- $$->nd_body->nd_loc = code_loc_gen(&@1, &@3);
- nd_set_line($$, @1.end_pos.lineno);
- /*% %*/
+ set_embraced_location($$, &@1, &@3);
+ /*% ripper: get_value($:2); %*/
}
;
block_call : command do_block
{
- /*%%%*/
if (nd_type_p($1, NODE_YIELD)) {
compile_error(p, "block given to yield");
}
else {
- block_dup_check(p, $1->nd_args, $2);
+ block_dup_check(p, get_nd_args(p, $1), $2);
}
$$ = method_add_block(p, $1, $2, &@$);
fixpos($$, $1);
- /*% %*/
- /*% ripper: method_add_block!($1, $2) %*/
+ /*% ripper: method_add_block!($:1, $:2) %*/
}
| block_call call_op2 operation2 opt_paren_args
{
- /*%%%*/
$$ = new_qcall(p, $2, $1, $3, $4, &@3, &@$);
- /*% %*/
- /*% ripper: opt_event(:method_add_arg!, call!($1, $2, $3), $4) %*/
+ /*% ripper: opt_event(:method_add_arg!, call!($:1, $:2, $:3), $:4) %*/
}
| block_call call_op2 operation2 opt_paren_args brace_block
{
- /*%%%*/
$$ = new_command_qcall(p, $2, $1, $3, $4, $5, &@3, &@$);
- /*% %*/
- /*% ripper: opt_event(:method_add_block!, command_call!($1, $2, $3, $4), $5) %*/
+ /*% ripper: opt_event(:method_add_block!, command_call!($:1, $:2, $:3, $:4), $:5) %*/
}
| block_call call_op2 operation2 command_args do_block
{
- /*%%%*/
$$ = new_command_qcall(p, $2, $1, $3, $4, $5, &@3, &@$);
- /*% %*/
- /*% ripper: method_add_block!(command_call!($1, $2, $3, $4), $5) %*/
+ /*% ripper: method_add_block!(command_call!($:1, $:2, $:3, $:4), $:5) %*/
}
;
method_call : fcall paren_args
{
- /*%%%*/
- $$ = $1;
- $$->nd_args = $2;
+ $1->nd_args = $2;
+ $$ = (NODE *)$1;
nd_set_last_loc($1, @2.end_pos);
- /*% %*/
- /*% ripper: method_add_arg!(fcall!($1), $2) %*/
+ /*% ripper: method_add_arg!(fcall!($:1), $:2) %*/
}
| primary_value call_op operation2 opt_paren_args
{
- /*%%%*/
$$ = new_qcall(p, $2, $1, $3, $4, &@3, &@$);
nd_set_line($$, @3.end_pos.lineno);
- /*% %*/
- /*% ripper: opt_event(:method_add_arg!, call!($1, $2, $3), $4) %*/
+ /*% ripper: opt_event(:method_add_arg!, call!($:1, $:2, $:3), $:4) %*/
}
| primary_value tCOLON2 operation2 paren_args
{
- /*%%%*/
- $$ = new_qcall(p, ID2VAL(idCOLON2), $1, $3, $4, &@3, &@$);
+ $$ = new_qcall(p, idCOLON2, $1, $3, $4, &@3, &@$);
nd_set_line($$, @3.end_pos.lineno);
- /*% %*/
- /*% ripper: method_add_arg!(call!($1, ID2VAL(idCOLON2), $3), $4) %*/
+ /*% ripper: method_add_arg!(call!($:1, $:2, $:3), $:4) %*/
}
| primary_value tCOLON2 operation3
{
- /*%%%*/
- $$ = new_qcall(p, ID2VAL(idCOLON2), $1, $3, Qnull, &@3, &@$);
- /*% %*/
- /*% ripper: call!($1, ID2VAL(idCOLON2), $3) %*/
+ $$ = new_qcall(p, idCOLON2, $1, $3, 0, &@3, &@$);
+ /*% ripper: call!($:1, $:2, $:3) %*/
}
| primary_value call_op paren_args
{
- /*%%%*/
- $$ = new_qcall(p, $2, $1, ID2VAL(idCall), $3, &@2, &@$);
+ $$ = new_qcall(p, $2, $1, idCall, $3, &@2, &@$);
nd_set_line($$, @2.end_pos.lineno);
- /*% %*/
- /*% ripper: method_add_arg!(call!($1, $2, ID2VAL(idCall)), $3) %*/
+ /*% ripper: method_add_arg!(call!($:1, $:2, ID2VAL(idCall)), $:3) %*/
}
| primary_value tCOLON2 paren_args
{
- /*%%%*/
- $$ = new_qcall(p, ID2VAL(idCOLON2), $1, ID2VAL(idCall), $3, &@2, &@$);
+ $$ = new_qcall(p, idCOLON2, $1, idCall, $3, &@2, &@$);
nd_set_line($$, @2.end_pos.lineno);
- /*% %*/
- /*% ripper: method_add_arg!(call!($1, ID2VAL(idCOLON2), ID2VAL(idCall)), $3) %*/
+ /*% ripper: method_add_arg!(call!($:1, $:2, ID2VAL(idCall)), $:3) %*/
}
| keyword_super paren_args
{
- /*%%%*/
$$ = NEW_SUPER($2, &@$);
- /*% %*/
- /*% ripper: super!($2) %*/
+ /*% ripper: super!($:2) %*/
}
| keyword_super
{
- /*%%%*/
$$ = NEW_ZSUPER(&@$);
- /*% %*/
/*% ripper: zsuper! %*/
}
| primary_value '[' opt_call_args rbracket
{
- /*%%%*/
$$ = NEW_CALL($1, tAREF, $3, &@$);
fixpos($$, $1);
- /*% %*/
- /*% ripper: aref!($1, escape_Qundef($3)) %*/
+ /*% ripper: aref!($:1, $:3) %*/
}
;
brace_block : '{' brace_body '}'
{
$$ = $2;
- /*%%%*/
- $$->nd_body->nd_loc = code_loc_gen(&@1, &@3);
- nd_set_line($$, @1.end_pos.lineno);
- /*% %*/
+ set_embraced_location($$, &@1, &@3);
+ /*% ripper: get_value($:2); %*/
}
| k_do do_body k_end
{
$$ = $2;
- /*%%%*/
- $$->nd_body->nd_loc = code_loc_gen(&@1, &@3);
- nd_set_line($$, @1.end_pos.lineno);
- /*% %*/
+ set_embraced_location($$, &@1, &@3);
+ /*% ripper: get_value($:2); %*/
}
;
-brace_body : {$<vars>$ = dyna_push(p);}
- {
- $<num>$ = p->max_numparam;
- p->max_numparam = 0;
- }
- {
- $<node>$ = numparam_push(p);
- }
- opt_block_param compstmt
+brace_body : {$<vars>$ = dyna_push(p);}[dyna]
+ max_numparam numparam it_id allow_exits
+ opt_block_param[args] compstmt
{
int max_numparam = p->max_numparam;
- p->max_numparam = $<num>2;
- $4 = args_with_numbered(p, $4, max_numparam);
- /*%%%*/
- $$ = NEW_ITER($4, $5, &@$);
- /*% %*/
- /*% ripper: brace_block!(escape_Qundef($4), $5) %*/
- numparam_pop(p, $<node>3);
- dyna_pop(p, $<vars>1);
+ ID it_id = p->it_id;
+ p->max_numparam = $max_numparam;
+ p->it_id = $it_id;
+ $args = args_with_numbered(p, $args, max_numparam, it_id);
+ $$ = NEW_ITER($args, $compstmt, &@$);
+ /*% ripper: brace_block!($:args, $:compstmt) %*/
+ restore_block_exit(p, $allow_exits);
+ numparam_pop(p, $numparam);
+ dyna_pop(p, $<vars>dyna);
}
;
-do_body : {$<vars>$ = dyna_push(p);}
- {
- $<num>$ = p->max_numparam;
- p->max_numparam = 0;
- }
- {
- $<node>$ = numparam_push(p);
+do_body : {
+ $<vars>$ = dyna_push(p);
CMDARG_PUSH(0);
- }
- opt_block_param bodystmt
+ }[dyna]
+ max_numparam numparam it_id allow_exits
+ opt_block_param[args] bodystmt
{
int max_numparam = p->max_numparam;
- p->max_numparam = $<num>2;
- $4 = args_with_numbered(p, $4, max_numparam);
- /*%%%*/
- $$ = NEW_ITER($4, $5, &@$);
- /*% %*/
- /*% ripper: do_block!(escape_Qundef($4), $5) %*/
+ ID it_id = p->it_id;
+ p->max_numparam = $max_numparam;
+ p->it_id = $<id>it_id;
+ $args = args_with_numbered(p, $args, max_numparam, it_id);
+ $$ = NEW_ITER($args, $bodystmt, &@$);
+ /*% ripper: do_block!($:args, $:bodystmt) %*/
CMDARG_POP();
- numparam_pop(p, $<node>3);
- dyna_pop(p, $<vars>1);
+ restore_block_exit(p, $allow_exits);
+ numparam_pop(p, $numparam);
+ dyna_pop(p, $<vars>dyna);
}
;
case_args : arg_value
{
- /*%%%*/
check_literal_when(p, $1, &@1);
$$ = NEW_LIST($1, &@$);
- /*% %*/
- /*% ripper: args_add!(args_new!, $1) %*/
+ /*% ripper: args_add!(args_new!, $:1) %*/
}
| tSTAR arg_value
{
- /*%%%*/
$$ = NEW_SPLAT($2, &@$);
- /*% %*/
- /*% ripper: args_add_star!(args_new!, $2) %*/
+ /*% ripper: args_add_star!(args_new!, $:2) %*/
}
| case_args ',' arg_value
{
- /*%%%*/
check_literal_when(p, $3, &@3);
$$ = last_arg_append(p, $1, $3, &@$);
- /*% %*/
- /*% ripper: args_add!($1, $3) %*/
+ /*% ripper: args_add!($:1, $:3) %*/
}
| case_args ',' tSTAR arg_value
{
- /*%%%*/
$$ = rest_arg_append(p, $1, $4, &@$);
- /*% %*/
- /*% ripper: args_add_star!($1, $4) %*/
+ /*% ripper: args_add_star!($:1, $:4) %*/
}
;
@@ -4373,11 +5497,9 @@ case_body : k_when case_args then
compstmt
cases
{
- /*%%%*/
$$ = NEW_WHEN($2, $4, $5, &@$);
fixpos($$, $2);
- /*% %*/
- /*% ripper: when!($2, $4, escape_Qundef($5)) %*/
+ /*% ripper: when!($:2, $:4, $:5) %*/
}
;
@@ -4385,30 +5507,30 @@ cases : opt_else
| case_body
;
-p_case_body : keyword_in
- {
+p_pvtbl : {$$ = p->pvtbl; p->pvtbl = st_init_numtable();};
+p_pktbl : {$$ = p->pktbl; p->pktbl = 0;};
+
+p_in_kwarg : {
+ $$ = p->ctxt;
SET_LEX_STATE(EXPR_BEG|EXPR_LABEL);
p->command_start = FALSE;
- $<ctxt>1 = p->ctxt;
p->ctxt.in_kwarg = 1;
- $<tbl>$ = push_pvtbl(p);
- }
- {
- $<tbl>$ = push_pktbl(p);
}
- p_top_expr then
+ ;
+
+p_case_body : keyword_in
+ p_in_kwarg[ctxt] p_pvtbl p_pktbl
+ p_top_expr[expr] then
{
- pop_pktbl(p, $<tbl>3);
- pop_pvtbl(p, $<tbl>2);
- p->ctxt.in_kwarg = $<ctxt>1.in_kwarg;
+ pop_pktbl(p, $p_pktbl);
+ pop_pvtbl(p, $p_pvtbl);
+ p->ctxt.in_kwarg = $ctxt.in_kwarg;
}
compstmt
- p_cases
+ p_cases[cases]
{
- /*%%%*/
- $$ = NEW_IN($4, $7, $8, &@$);
- /*% %*/
- /*% ripper: in!($4, $7, escape_Qundef($8)) %*/
+ $$ = NEW_IN($expr, $compstmt, $cases, &@$);
+ /*% ripper: in!($:expr, $:compstmt, $:cases) %*/
}
;
@@ -4419,47 +5541,45 @@ p_cases : opt_else
p_top_expr : p_top_expr_body
| p_top_expr_body modifier_if expr_value
{
- /*%%%*/
$$ = new_if(p, $3, $1, 0, &@$);
fixpos($$, $3);
- /*% %*/
- /*% ripper: if_mod!($3, $1) %*/
+ /*% ripper: if_mod!($:3, $:1) %*/
}
| p_top_expr_body modifier_unless expr_value
{
- /*%%%*/
$$ = new_unless(p, $3, $1, 0, &@$);
fixpos($$, $3);
- /*% %*/
- /*% ripper: unless_mod!($3, $1) %*/
+ /*% ripper: unless_mod!($:3, $:1) %*/
}
;
p_top_expr_body : p_expr
| p_expr ','
{
- $$ = new_array_pattern_tail(p, Qnone, 1, Qnone, Qnone, &@$);
- $$ = new_array_pattern(p, Qnone, get_value($1), $$, &@$);
+ $$ = new_array_pattern_tail(p, 0, 1, 0, 0, &@$);
+ $$ = new_array_pattern(p, 0, $1, $$, &@$);
+ /*% ripper: ripper_new_array_pattern(p, Qnil, get_value($:1), rb_ary_new()); %*/
}
| p_expr ',' p_args
{
- $$ = new_array_pattern(p, Qnone, get_value($1), $3, &@$);
- /*%%%*/
+ $$ = new_array_pattern(p, 0, $1, $3, &@$);
nd_set_first_loc($$, @1.beg_pos);
- /*%
- %*/
+ /*% ripper: ripper_new_array_pattern(p, Qnil, get_value($:1), get_value($:3)); %*/
}
| p_find
{
- $$ = new_find_pattern(p, Qnone, $1, &@$);
+ $$ = new_find_pattern(p, 0, $1, &@$);
+ /*% ripper: ripper_new_find_pattern(p, Qnil, get_value($:1)); %*/
}
| p_args_tail
{
- $$ = new_array_pattern(p, Qnone, Qnone, $1, &@$);
+ $$ = new_array_pattern(p, 0, 0, $1, &@$);
+ /*% ripper: ripper_new_array_pattern(p, Qnil, Qnil, get_value($:1)); %*/
}
| p_kwargs
{
- $$ = new_hash_pattern(p, Qnone, $1, &@$);
+ $$ = new_hash_pattern(p, 0, $1, &@$);
+ /*% ripper: ripper_new_hash_pattern(p, Qnil, get_value($:1)); %*/
}
;
@@ -4468,161 +5588,180 @@ p_expr : p_as
p_as : p_expr tASSOC p_variable
{
- /*%%%*/
NODE *n = NEW_LIST($1, &@$);
n = list_append(p, n, $3);
$$ = new_hash(p, n, &@$);
- /*% %*/
- /*% ripper: binary!($1, STATIC_ID2SYM((id_assoc)), $3) %*/
+ /*% ripper: binary!($:1, ID2VAL((id_assoc)), $:3) %*/
}
| p_alt
;
p_alt : p_alt '|' p_expr_basic
{
- /*%%%*/
- $$ = NEW_NODE(NODE_OR, $1, $3, 0, &@$);
- /*% %*/
- /*% ripper: binary!($1, STATIC_ID2SYM(idOr), $3) %*/
+ $$ = NEW_OR($1, $3, &@$);
+ /*% ripper: binary!($:1, ID2VAL(idOr), $:3) %*/
}
| p_expr_basic
;
-p_lparen : '(' {$<tbl>$ = push_pktbl(p);};
-p_lbracket : '[' {$<tbl>$ = push_pktbl(p);};
+p_lparen : '(' p_pktbl
+ {
+ $$ = $2;
+ /*% ripper: get_value($:2); %*/
+ }
+ ;
+
+p_lbracket : '[' p_pktbl
+ {
+ $$ = $2;
+ /*% ripper: get_value($:2); %*/
+ }
+ ;
p_expr_basic : p_value
| p_variable
- | p_const p_lparen p_args rparen
+ | p_const p_lparen[p_pktbl] p_args rparen
{
- pop_pktbl(p, $<tbl>2);
- $$ = new_array_pattern(p, $1, Qnone, $3, &@$);
- /*%%%*/
- nd_set_first_loc($$, @1.beg_pos);
- /*%
- %*/
+ pop_pktbl(p, $p_pktbl);
+ $$ = new_array_pattern(p, $p_const, 0, $p_args, &@$);
+ nd_set_first_loc($$, @p_const.beg_pos);
+ /*% ripper: ripper_new_array_pattern(p, get_value($:p_const), Qnil, get_value($:p_args)); %*/
}
- | p_const p_lparen p_find rparen
+ | p_const p_lparen[p_pktbl] p_find rparen
{
- pop_pktbl(p, $<tbl>2);
- $$ = new_find_pattern(p, $1, $3, &@$);
- /*%%%*/
- nd_set_first_loc($$, @1.beg_pos);
- /*%
- %*/
+ pop_pktbl(p, $p_pktbl);
+ $$ = new_find_pattern(p, $p_const, $p_find, &@$);
+ nd_set_first_loc($$, @p_const.beg_pos);
+ /*% ripper: ripper_new_find_pattern(p, get_value($:p_const), get_value($:p_find)); %*/
}
- | p_const p_lparen p_kwargs rparen
+ | p_const p_lparen[p_pktbl] p_kwargs rparen
{
- pop_pktbl(p, $<tbl>2);
- $$ = new_hash_pattern(p, $1, $3, &@$);
- /*%%%*/
- nd_set_first_loc($$, @1.beg_pos);
- /*%
- %*/
+ pop_pktbl(p, $p_pktbl);
+ $$ = new_hash_pattern(p, $p_const, $p_kwargs, &@$);
+ nd_set_first_loc($$, @p_const.beg_pos);
+ /*% ripper: ripper_new_hash_pattern(p, get_value($:p_const), get_value($:p_kwargs)); %*/
}
| p_const '(' rparen
{
- $$ = new_array_pattern_tail(p, Qnone, 0, Qnone, Qnone, &@$);
- $$ = new_array_pattern(p, $1, Qnone, $$, &@$);
+ $$ = new_array_pattern_tail(p, 0, 0, 0, 0, &@$);
+ $$ = new_array_pattern(p, $p_const, 0, $$, &@$);
+ /*% ripper: ripper_new_array_pattern(p, get_value($:p_const), Qnil, rb_ary_new()); %*/
}
- | p_const p_lbracket p_args rbracket
+ | p_const p_lbracket[p_pktbl] p_args rbracket
{
- pop_pktbl(p, $<tbl>2);
- $$ = new_array_pattern(p, $1, Qnone, $3, &@$);
- /*%%%*/
- nd_set_first_loc($$, @1.beg_pos);
- /*%
- %*/
+ pop_pktbl(p, $p_pktbl);
+ $$ = new_array_pattern(p, $p_const, 0, $p_args, &@$);
+ nd_set_first_loc($$, @p_const.beg_pos);
+ /*% ripper: ripper_new_array_pattern(p, get_value($:p_const), Qnil, get_value($:p_args)); %*/
}
- | p_const p_lbracket p_find rbracket
+ | p_const p_lbracket[p_pktbl] p_find rbracket
{
- pop_pktbl(p, $<tbl>2);
- $$ = new_find_pattern(p, $1, $3, &@$);
- /*%%%*/
- nd_set_first_loc($$, @1.beg_pos);
- /*%
- %*/
+ pop_pktbl(p, $p_pktbl);
+ $$ = new_find_pattern(p, $p_const, $p_find, &@$);
+ nd_set_first_loc($$, @p_const.beg_pos);
+ /*% ripper: ripper_new_find_pattern(p, get_value($:p_const), get_value($:p_find)); %*/
}
- | p_const p_lbracket p_kwargs rbracket
+ | p_const p_lbracket[p_pktbl] p_kwargs rbracket
{
- pop_pktbl(p, $<tbl>2);
- $$ = new_hash_pattern(p, $1, $3, &@$);
- /*%%%*/
- nd_set_first_loc($$, @1.beg_pos);
- /*%
- %*/
+ pop_pktbl(p, $p_pktbl);
+ $$ = new_hash_pattern(p, $p_const, $p_kwargs, &@$);
+ nd_set_first_loc($$, @p_const.beg_pos);
+ /*% ripper: ripper_new_hash_pattern(p, get_value($:p_const), get_value($:p_kwargs)); %*/
}
| p_const '[' rbracket
{
- $$ = new_array_pattern_tail(p, Qnone, 0, Qnone, Qnone, &@$);
- $$ = new_array_pattern(p, $1, Qnone, $$, &@$);
+ $$ = new_array_pattern_tail(p, 0, 0, 0, 0, &@$);
+ $$ = new_array_pattern(p, $1, 0, $$, &@$);
+ /*% ripper: ripper_new_array_pattern(p, get_value($:1), Qnil, rb_ary_new()); %*/
}
| tLBRACK p_args rbracket
{
- $$ = new_array_pattern(p, Qnone, Qnone, $2, &@$);
+ $$ = new_array_pattern(p, 0, 0, $p_args, &@$);
+ /*% ripper: ripper_new_array_pattern(p, Qnil, Qnil, get_value($:p_args)); %*/
}
| tLBRACK p_find rbracket
{
- $$ = new_find_pattern(p, Qnone, $2, &@$);
+ $$ = new_find_pattern(p, 0, $p_find, &@$);
+ /*% ripper: ripper_new_find_pattern(p, Qnil, get_value($:p_find)); %*/
}
| tLBRACK rbracket
{
- $$ = new_array_pattern_tail(p, Qnone, 0, Qnone, Qnone, &@$);
- $$ = new_array_pattern(p, Qnone, Qnone, $$, &@$);
+ $$ = new_array_pattern_tail(p, 0, 0, 0, 0, &@$);
+ $$ = new_array_pattern(p, 0, 0, $$, &@$);
+ /*% ripper: ripper_new_array_pattern(p, Qnil, Qnil, rb_ary_new()); %*/
}
- | tLBRACE
+ | tLBRACE p_pktbl lex_ctxt[ctxt]
{
- $<tbl>$ = push_pktbl(p);
- $<ctxt>1 = p->ctxt;
p->ctxt.in_kwarg = 0;
}
p_kwargs rbrace
{
- pop_pktbl(p, $<tbl>2);
- p->ctxt.in_kwarg = $<ctxt>1.in_kwarg;
- $$ = new_hash_pattern(p, Qnone, $3, &@$);
+ pop_pktbl(p, $p_pktbl);
+ p->ctxt.in_kwarg = $ctxt.in_kwarg;
+ $$ = new_hash_pattern(p, 0, $p_kwargs, &@$);
+ /*% ripper: ripper_new_hash_pattern(p, Qnil, get_value($:p_kwargs)); %*/
}
| tLBRACE rbrace
{
- $$ = new_hash_pattern_tail(p, Qnone, 0, &@$);
- $$ = new_hash_pattern(p, Qnone, $$, &@$);
+ $$ = new_hash_pattern_tail(p, 0, 0, &@$);
+ $$ = new_hash_pattern(p, 0, $$, &@$);
+ /*%%%*/
+ /*%
+ VALUE val = ripper_new_hash_pattern_tail(p, Qnil, 0);
+ val = ripper_new_hash_pattern(p, Qnil, val);
+ set_value(val);
+ %*/
}
- | tLPAREN {$<tbl>$ = push_pktbl(p);} p_expr rparen
+ | tLPAREN p_pktbl p_expr rparen
{
- pop_pktbl(p, $<tbl>2);
- $$ = $3;
+ pop_pktbl(p, $p_pktbl);
+ $$ = $p_expr;
+ /*% ripper: get_value($:p_expr); %*/
}
;
p_args : p_expr
{
- /*%%%*/
NODE *pre_args = NEW_LIST($1, &@$);
- $$ = new_array_pattern_tail(p, pre_args, 0, Qnone, Qnone, &@$);
+ $$ = new_array_pattern_tail(p, pre_args, 0, 0, 0, &@$);
+ /*%%%*/
/*%
- $$ = new_array_pattern_tail(p, rb_ary_new_from_args(1, get_value($1)), 0, Qnone, Qnone, &@$);
+ VALUE ary = rb_ary_new_from_args(1, get_value($:1));
+ set_value(rb_ary_new_from_args(3, ary, Qnil, Qnil));
%*/
}
| p_args_head
{
- $$ = new_array_pattern_tail(p, $1, 1, Qnone, Qnone, &@$);
+ $$ = new_array_pattern_tail(p, $1, 1, 0, 0, &@$);
+ /*%%%*/
+ /*%
+ set_value(rb_ary_new_from_args(3, get_value($:1), Qnil, Qnil));
+ %*/
}
| p_args_head p_arg
{
+ $$ = new_array_pattern_tail(p, list_concat($1, $2), 0, 0, 0, &@$);
/*%%%*/
- $$ = new_array_pattern_tail(p, list_concat($1, $2), 0, Qnone, Qnone, &@$);
/*%
- VALUE pre_args = rb_ary_concat($1, get_value($2));
- $$ = new_array_pattern_tail(p, pre_args, 0, Qnone, Qnone, &@$);
+ VALUE pre_args = rb_ary_concat(get_value($:1), get_value($:2));
+ set_value(rb_ary_new_from_args(3, pre_args, Qnil, Qnil));
%*/
}
| p_args_head p_rest
{
- $$ = new_array_pattern_tail(p, $1, 1, $2, Qnone, &@$);
+ $$ = new_array_pattern_tail(p, $1, 1, $2, 0, &@$);
+ /*%%%*/
+ /*%
+ set_value(rb_ary_new_from_args(3, get_value($:1), get_value($:2), Qnil));
+ %*/
}
| p_args_head p_rest ',' p_args_post
{
$$ = new_array_pattern_tail(p, $1, 1, $2, $4, &@$);
+ /*%%%*/
+ /*%
+ set_value(rb_ary_new_from_args(3, get_value($:1), get_value($:2), get_value($:4)));
+ %*/
}
| p_args_tail
;
@@ -4633,43 +5772,40 @@ p_args_head : p_arg ','
}
| p_args_head p_arg ','
{
- /*%%%*/
$$ = list_concat($1, $2);
- /*% %*/
- /*% ripper: rb_ary_concat($1, get_value($2)) %*/
+ /*% ripper: rb_ary_concat(get_value($:1), get_value($:2)) %*/
}
;
p_args_tail : p_rest
{
- $$ = new_array_pattern_tail(p, Qnone, 1, $1, Qnone, &@$);
+ $$ = new_array_pattern_tail(p, 0, 1, $1, 0, &@$);
+ /*% ripper: ripper_new_array_pattern_tail(p, Qnil, get_value($:1), Qnil); %*/
}
| p_rest ',' p_args_post
{
- $$ = new_array_pattern_tail(p, Qnone, 1, $1, $3, &@$);
+ $$ = new_array_pattern_tail(p, 0, 1, $1, $3, &@$);
+ /*% ripper: ripper_new_array_pattern_tail(p, Qnil, get_value($:1), get_value($:3)); %*/
}
;
p_find : p_rest ',' p_args_post ',' p_rest
{
$$ = new_find_pattern_tail(p, $1, $3, $5, &@$);
+ /*% ripper: ripper_new_find_pattern_tail(p, get_value($:1), get_value($:3), get_value($:5)) %*/
}
;
p_rest : tSTAR tIDENTIFIER
{
- /*%%%*/
error_duplicate_pattern_variable(p, $2, &@2);
$$ = assignable(p, $2, 0, &@$);
- /*% %*/
- /*% ripper: assignable(p, var_field(p, $2)) %*/
+ /*% ripper: ripper_assignable(p, $2, var_field(p, get_value($:2))) %*/
}
| tSTAR
{
- /*%%%*/
$$ = 0;
- /*% %*/
/*% ripper: var_field(p, Qnil) %*/
}
;
@@ -4677,70 +5813,64 @@ p_rest : tSTAR tIDENTIFIER
p_args_post : p_arg
| p_args_post ',' p_arg
{
- /*%%%*/
$$ = list_concat($1, $3);
- /*% %*/
- /*% ripper: rb_ary_concat($1, get_value($3)) %*/
+ /*% ripper: rb_ary_concat(get_value($:1), get_value($:3)) %*/
}
;
p_arg : p_expr
{
- /*%%%*/
$$ = NEW_LIST($1, &@$);
- /*% %*/
- /*% ripper: rb_ary_new_from_args(1, get_value($1)) %*/
+ /*% ripper: rb_ary_new_from_args(1, get_value($:1)) %*/
}
;
p_kwargs : p_kwarg ',' p_any_kwrest
{
$$ = new_hash_pattern_tail(p, new_unique_key_hash(p, $1, &@$), $3, &@$);
+ /*% ripper: ripper_new_hash_pattern_tail(p, get_value($:1), get_value($:3)) %*/
}
| p_kwarg
{
$$ = new_hash_pattern_tail(p, new_unique_key_hash(p, $1, &@$), 0, &@$);
+ /*% ripper: ripper_new_hash_pattern_tail(p, get_value($:1), 0) %*/
}
| p_kwarg ','
{
$$ = new_hash_pattern_tail(p, new_unique_key_hash(p, $1, &@$), 0, &@$);
+ /*% ripper: ripper_new_hash_pattern_tail(p, get_value($:1), 0) %*/
}
| p_any_kwrest
{
- $$ = new_hash_pattern_tail(p, new_hash(p, Qnone, &@$), $1, &@$);
+ $$ = new_hash_pattern_tail(p, new_hash(p, 0, &@$), $1, &@$);
+ /*% ripper: ripper_new_hash_pattern_tail(p, rb_ary_new(), get_value($:1)) %*/
}
;
p_kwarg : p_kw
- /*% ripper[brace]: rb_ary_new_from_args(1, $1) %*/
+ /*% ripper[brace]: rb_ary_new_from_args(1, get_value($:1)) %*/
| p_kwarg ',' p_kw
{
- /*%%%*/
$$ = list_concat($1, $3);
- /*% %*/
- /*% ripper: rb_ary_push($1, $3) %*/
+ /*% ripper: rb_ary_push(get_value($:1), get_value($:3)) %*/
}
;
p_kw : p_kw_label p_expr
{
- error_duplicate_pattern_key(p, get_id($1), &@1);
- /*%%%*/
- $$ = list_append(p, NEW_LIST(NEW_LIT(ID2SYM($1), &@1), &@$), $2);
- /*% %*/
- /*% ripper: rb_ary_new_from_args(2, get_value($1), get_value($2)) %*/
+ error_duplicate_pattern_key(p, $1, &@1);
+ $$ = list_append(p, NEW_LIST(NEW_SYM(rb_id2str($1), &@1), &@$), $2);
+ /*% ripper: rb_ary_new_from_args(2, get_value($:1), get_value($:2)) %*/
}
| p_kw_label
{
- error_duplicate_pattern_key(p, get_id($1), &@1);
- if ($1 && !is_local_id(get_id($1))) {
+ error_duplicate_pattern_key(p, $1, &@1);
+ if ($1 && !is_local_id($1)) {
yyerror1(&@1, "key must be valid as local variables");
}
- error_duplicate_pattern_variable(p, get_id($1), &@1);
- /*%%%*/
- $$ = list_append(p, NEW_LIST(NEW_LIT(ID2SYM($1), &@$), &@$), assignable(p, $1, 0, &@$));
- /*% %*/
- /*% ripper: rb_ary_new_from_args(2, get_value(assignable(p, $1)), Qnil) %*/
+ error_duplicate_pattern_variable(p, $1, &@1);
+ $$ = list_append(p, NEW_LIST(NEW_SYM(rb_id2str($1), &@$), &@$), assignable(p, $1, 0, &@$));
+ /*% ripper: rb_ary_new_from_args(2, ripper_assignable(p, $1, get_value($:1)), Qnil) %*/
}
;
@@ -4748,33 +5878,27 @@ p_kw_label : tLABEL
| tSTRING_BEG string_contents tLABEL_END
{
YYLTYPE loc = code_loc_gen(&@1, &@3);
- /*%%%*/
if (!$2 || nd_type_p($2, NODE_STR)) {
NODE *node = dsym_node(p, $2, &loc);
- $$ = SYM2ID(node->nd_lit);
- }
- /*%
- if (ripper_is_node_yylval($2) && RNODE($2)->nd_cval) {
- VALUE label = RNODE($2)->nd_cval;
- VALUE rval = RNODE($2)->nd_rval;
- $$ = ripper_new_yylval(p, rb_intern_str(label), rval, label);
- RNODE($$)->nd_loc = loc;
+ $$ = rb_sym2id(rb_node_sym_string_val(node));
}
- %*/
else {
yyerror1(&loc, "symbol literal with interpolation is not allowed");
- $$ = 0;
+ $$ = rb_intern_str(STR_NEW0());
}
+ /*% ripper: get_value($:2); %*/
}
;
p_kwrest : kwrest_mark tIDENTIFIER
{
$$ = $2;
+ /*% ripper: get_value($:2); %*/
}
| kwrest_mark
{
$$ = 0;
+ /*% ripper: 0; %*/
}
;
@@ -4785,62 +5909,54 @@ p_kwnorest : kwrest_mark keyword_nil
;
p_any_kwrest : p_kwrest
- | p_kwnorest {$$ = ID2VAL(idNil);}
+ | p_kwnorest
+ {
+ $$ = idNil;
+ /*% ripper: ID2VAL(idNil) %*/
+ }
;
p_value : p_primitive
| p_primitive tDOT2 p_primitive
{
- /*%%%*/
value_expr($1);
value_expr($3);
$$ = NEW_DOT2($1, $3, &@$);
- /*% %*/
- /*% ripper: dot2!($1, $3) %*/
+ /*% ripper: dot2!($:1, $:3) %*/
}
| p_primitive tDOT3 p_primitive
{
- /*%%%*/
value_expr($1);
value_expr($3);
$$ = NEW_DOT3($1, $3, &@$);
- /*% %*/
- /*% ripper: dot3!($1, $3) %*/
+ /*% ripper: dot3!($:1, $:3) %*/
}
| p_primitive tDOT2
{
- /*%%%*/
value_expr($1);
$$ = NEW_DOT2($1, new_nil_at(p, &@2.end_pos), &@$);
- /*% %*/
- /*% ripper: dot2!($1, Qnil) %*/
+ /*% ripper: dot2!($:1, Qnil) %*/
}
| p_primitive tDOT3
{
- /*%%%*/
value_expr($1);
$$ = NEW_DOT3($1, new_nil_at(p, &@2.end_pos), &@$);
- /*% %*/
- /*% ripper: dot3!($1, Qnil) %*/
+ /*% ripper: dot3!($:1, Qnil) %*/
}
| p_var_ref
| p_expr_ref
| p_const
| tBDOT2 p_primitive
{
- /*%%%*/
value_expr($2);
$$ = NEW_DOT2(new_nil_at(p, &@1.beg_pos), $2, &@$);
- /*% %*/
- /*% ripper: dot2!(Qnil, $2) %*/
+ /*% ripper: dot2!(Qnil, $:2) %*/
}
| tBDOT3 p_primitive
{
- /*%%%*/
value_expr($2);
$$ = NEW_DOT3(new_nil_at(p, &@1.beg_pos), $2, &@$);
- /*% %*/
- /*% ripper: dot3!(Qnil, $2) %*/
+ /*% ripper: dot3!(Qnil, $:2) %*/
}
;
@@ -4854,73 +5970,60 @@ p_primitive : literal
| qsymbols
| keyword_variable
{
- /*%%%*/
- if (!($$ = gettable(p, $1, &@$))) $$ = NEW_BEGIN(0, &@$);
- /*% %*/
- /*% ripper: var_ref!($1) %*/
+ if (!($$ = gettable(p, $1, &@$))) $$ = NEW_ERROR(&@$);
+ /*% ripper: var_ref!($:1) %*/
}
| lambda
;
p_variable : tIDENTIFIER
{
- /*%%%*/
error_duplicate_pattern_variable(p, $1, &@1);
$$ = assignable(p, $1, 0, &@$);
- /*% %*/
- /*% ripper: assignable(p, var_field(p, $1)) %*/
+ /*% ripper: ripper_assignable(p, $1, var_field(p, get_value($:1))) %*/
}
;
p_var_ref : '^' tIDENTIFIER
{
- /*%%%*/
NODE *n = gettable(p, $2, &@$);
- if (!(nd_type_p(n, NODE_LVAR) || nd_type_p(n, NODE_DVAR))) {
+ if (!n) {
+ n = NEW_ERROR(&@$);
+ }
+ else if (!(nd_type_p(n, NODE_LVAR) || nd_type_p(n, NODE_DVAR))) {
compile_error(p, "%"PRIsVALUE": no such local variable", rb_id2str($2));
}
$$ = n;
- /*% %*/
- /*% ripper: var_ref!($2) %*/
+ /*% ripper: var_ref!($:2) %*/
}
| '^' nonlocal_var
{
- /*%%%*/
- if (!($$ = gettable(p, $2, &@$))) $$ = NEW_BEGIN(0, &@$);
- /*% %*/
- /*% ripper: var_ref!($2) %*/
+ if (!($$ = gettable(p, $2, &@$))) $$ = NEW_ERROR(&@$);
+ /*% ripper: var_ref!($:2) %*/
}
;
p_expr_ref : '^' tLPAREN expr_value rparen
{
- /*%%%*/
- $$ = NEW_BEGIN($3, &@$);
- /*% %*/
- /*% ripper: begin!($3) %*/
+ $$ = NEW_BLOCK($3, &@$);
+ /*% ripper: begin!($:3) %*/
}
;
p_const : tCOLON3 cname
{
- /*%%%*/
$$ = NEW_COLON3($2, &@$);
- /*% %*/
- /*% ripper: top_const_ref!($2) %*/
+ /*% ripper: top_const_ref!($:2) %*/
}
| p_const tCOLON2 cname
{
- /*%%%*/
$$ = NEW_COLON2($1, $3, &@$);
- /*% %*/
- /*% ripper: const_path_ref!($1, $3) %*/
+ /*% ripper: const_path_ref!($:1, $:3) %*/
}
| tCONSTANT
{
- /*%%%*/
$$ = gettable(p, $1, &@$);
- /*% %*/
- /*% ripper: var_ref!($1) %*/
+ /*% ripper: var_ref!($:1) %*/
}
;
@@ -4928,11 +6031,13 @@ opt_rescue : k_rescue exc_list exc_var then
compstmt
opt_rescue
{
- /*%%%*/
- $$ = NEW_RESBODY($2,
- $3 ? block_append(p, node_assign(p, $3, NEW_ERRINFO(&@3), NO_LEX_CTXT, &@3), $5) : $5,
- $6, &@$);
-
+ NODE *body = $5;
+ if ($3) {
+ NODE *err = NEW_ERRINFO(&@3);
+ err = node_assign(p, $3, err, NO_LEX_CTXT, &@3);
+ body = block_append(p, err, body);
+ }
+ $$ = NEW_RESBODY($2, body, $6, &@$);
if ($2) {
fixpos($$, $2);
}
@@ -4942,25 +6047,19 @@ opt_rescue : k_rescue exc_list exc_var then
else {
fixpos($$, $5);
}
- /*% %*/
- /*% ripper: rescue!(escape_Qundef($2), escape_Qundef($3), escape_Qundef($5), escape_Qundef($6)) %*/
+ /*% ripper: rescue!($:2, $:3, $:5, $:6) %*/
}
| none
;
exc_list : arg_value
{
- /*%%%*/
$$ = NEW_LIST($1, &@$);
- /*% %*/
- /*% ripper: rb_ary_new3(1, get_value($1)) %*/
+ /*% ripper: rb_ary_new3(1, get_value($:1)) %*/
}
| mrhs
{
- /*%%%*/
if (!($$ = splat_array($1))) $$ = $1;
- /*% %*/
- /*% ripper: $1 %*/
}
| none
;
@@ -4968,16 +6067,16 @@ exc_list : arg_value
exc_var : tASSOC lhs
{
$$ = $2;
+ /*% ripper: get_value($:2); %*/
}
| none
;
opt_ensure : k_ensure compstmt
{
- /*%%%*/
+ p->ctxt.in_rescue = $1.in_rescue;
$$ = $2;
- /*% %*/
- /*% ripper: ensure!($2) %*/
+ /*% ripper: ensure!($:2) %*/
}
| none
;
@@ -4988,18 +6087,15 @@ literal : numeric
strings : string
{
- /*%%%*/
NODE *node = $1;
if (!node) {
- node = NEW_STR(STR_NEW0(), &@$);
- RB_OBJ_WRITTEN(p->ast, Qnil, node->nd_lit);
+ node = NEW_STR(STRING_NEW0(), &@$);
}
else {
node = evstr2dstr(p, node);
}
$$ = node;
- /*% %*/
- /*% ripper: $1 %*/
+ /*% ripper: get_value($:1); %*/
}
;
@@ -5007,212 +6103,151 @@ string : tCHAR
| string1
| string string1
{
- /*%%%*/
$$ = literal_concat(p, $1, $2, &@$);
- /*% %*/
- /*% ripper: string_concat!($1, $2) %*/
+ /*% ripper: string_concat!($:1, $:2) %*/
}
;
string1 : tSTRING_BEG string_contents tSTRING_END
{
/*%%%*/
+ /*%
+ int indent = p->heredoc_indent;
+ %*/
$$ = heredoc_dedent(p, $2);
if ($$) nd_set_loc($$, &@$);
- /*% %*/
- /*% ripper: string_literal!(heredoc_dedent(p, $2)) %*/
+ /*%%%*/
+ /*%
+ VALUE val = dispatch1(string_literal, ripper_heredoc_dedent(p, indent, get_value($:2)));
+ set_value(val);
+ %*/
}
;
xstring : tXSTRING_BEG xstring_contents tSTRING_END
{
/*%%%*/
+ /*%
+ int indent = p->heredoc_indent;
+ %*/
$$ = new_xstring(p, heredoc_dedent(p, $2), &@$);
- /*% %*/
- /*% ripper: xstring_literal!(heredoc_dedent(p, $2)) %*/
+ /*%%%*/
+ /*%
+ VALUE val = dispatch1(xstring_literal, ripper_heredoc_dedent(p, indent, get_value($:2)));
+ set_value(val);
+ %*/
}
;
regexp : tREGEXP_BEG regexp_contents tREGEXP_END
{
$$ = new_regexp(p, $2, $3, &@$);
+ /*% ripper: regexp_literal!($:2, $:3) %*/
}
;
-words_sep : ' ' {}
- | words_sep ' '
- ;
-
-words : tWORDS_BEG words_sep word_list tSTRING_END
- {
- /*%%%*/
- $$ = make_list($3, &@$);
- /*% %*/
- /*% ripper: array!($3) %*/
- }
+words : words(tWORDS_BEG, word_list) <node>
;
word_list : /* none */
{
- /*%%%*/
$$ = 0;
- /*% %*/
/*% ripper: words_new! %*/
}
- | word_list word words_sep
+ | word_list word ' '+
{
- /*%%%*/
$$ = list_append(p, $1, evstr2dstr(p, $2));
- /*% %*/
- /*% ripper: words_add!($1, $2) %*/
+ /*% ripper: words_add!($:1, $:2) %*/
}
;
word : string_content
- /*% ripper[brace]: word_add!(word_new!, $1) %*/
+ /*% ripper[brace]: word_add!(word_new!, $:1) %*/
| word string_content
{
- /*%%%*/
$$ = literal_concat(p, $1, $2, &@$);
- /*% %*/
- /*% ripper: word_add!($1, $2) %*/
+ /*% ripper: word_add!($:1, $:2) %*/
}
;
-symbols : tSYMBOLS_BEG words_sep symbol_list tSTRING_END
- {
- /*%%%*/
- $$ = make_list($3, &@$);
- /*% %*/
- /*% ripper: array!($3) %*/
- }
+symbols : words(tSYMBOLS_BEG, symbol_list) <node>
;
symbol_list : /* none */
{
- /*%%%*/
$$ = 0;
- /*% %*/
/*% ripper: symbols_new! %*/
}
- | symbol_list word words_sep
+ | symbol_list word ' '+
{
- /*%%%*/
$$ = symbol_append(p, $1, evstr2dstr(p, $2));
- /*% %*/
- /*% ripper: symbols_add!($1, $2) %*/
+ /*% ripper: symbols_add!($:1, $:2) %*/
}
;
-qwords : tQWORDS_BEG words_sep qword_list tSTRING_END
- {
- /*%%%*/
- $$ = make_list($3, &@$);
- /*% %*/
- /*% ripper: array!($3) %*/
- }
+qwords : words(tQWORDS_BEG, qword_list) <node>
;
-qsymbols : tQSYMBOLS_BEG words_sep qsym_list tSTRING_END
- {
- /*%%%*/
- $$ = make_list($3, &@$);
- /*% %*/
- /*% ripper: array!($3) %*/
- }
+qsymbols : words(tQSYMBOLS_BEG, qsym_list) <node>
;
qword_list : /* none */
{
- /*%%%*/
$$ = 0;
- /*% %*/
/*% ripper: qwords_new! %*/
}
- | qword_list tSTRING_CONTENT words_sep
+ | qword_list tSTRING_CONTENT ' '+
{
- /*%%%*/
$$ = list_append(p, $1, $2);
- /*% %*/
- /*% ripper: qwords_add!($1, $2) %*/
+ /*% ripper: qwords_add!($:1, $:2) %*/
}
;
qsym_list : /* none */
{
- /*%%%*/
$$ = 0;
- /*% %*/
/*% ripper: qsymbols_new! %*/
}
- | qsym_list tSTRING_CONTENT words_sep
+ | qsym_list tSTRING_CONTENT ' '+
{
- /*%%%*/
$$ = symbol_append(p, $1, $2);
- /*% %*/
- /*% ripper: qsymbols_add!($1, $2) %*/
+ /*% ripper: qsymbols_add!($:1, $:2) %*/
}
;
string_contents : /* none */
{
- /*%%%*/
$$ = 0;
+ /*%%%*/
/*% %*/
/*% ripper: string_content! %*/
- /*%%%*/
- /*%
- $$ = ripper_new_yylval(p, 0, $$, 0);
- %*/
}
| string_contents string_content
{
- /*%%%*/
$$ = literal_concat(p, $1, $2, &@$);
- /*% %*/
- /*% ripper: string_add!($1, $2) %*/
- /*%%%*/
- /*%
- if (ripper_is_node_yylval($1) && ripper_is_node_yylval($2) &&
- !RNODE($1)->nd_cval) {
- RNODE($1)->nd_cval = RNODE($2)->nd_cval;
- RNODE($1)->nd_rval = add_mark_object(p, $$);
- $$ = $1;
- }
- %*/
+ /*% ripper: string_add!($:1, $:2) %*/
}
;
xstring_contents: /* none */
{
- /*%%%*/
$$ = 0;
- /*% %*/
/*% ripper: xstring_new! %*/
}
| xstring_contents string_content
{
- /*%%%*/
$$ = literal_concat(p, $1, $2, &@$);
- /*% %*/
- /*% ripper: xstring_add!($1, $2) %*/
+ /*% ripper: xstring_add!($:1, $:2) %*/
}
;
regexp_contents: /* none */
{
- /*%%%*/
$$ = 0;
- /*% %*/
/*% ripper: regexp_new! %*/
- /*%%%*/
- /*%
- $$ = ripper_new_yylval(p, 0, $$, 0);
- %*/
}
| regexp_contents string_content
{
- /*%%%*/
NODE *head = $1, *tail = $2;
if (!head) {
$$ = tail;
@@ -5223,36 +6258,22 @@ regexp_contents: /* none */
else {
switch (nd_type(head)) {
case NODE_STR:
- nd_set_type(head, NODE_DSTR);
+ head = str2dstr(p, head);
break;
case NODE_DSTR:
break;
default:
- head = list_append(p, NEW_DSTR(Qnil, &@$), head);
+ head = list_append(p, NEW_DSTR(0, &@$), head);
break;
}
$$ = list_append(p, head, tail);
}
- /*%
- VALUE s1 = 1, s2 = 0, n1 = $1, n2 = $2;
- if (ripper_is_node_yylval(n1)) {
- s1 = RNODE(n1)->nd_cval;
- n1 = RNODE(n1)->nd_rval;
- }
- if (ripper_is_node_yylval(n2)) {
- s2 = RNODE(n2)->nd_cval;
- n2 = RNODE(n2)->nd_rval;
- }
- $$ = dispatch2(regexp_add, n1, n2);
- if (!s1 && s2) {
- $$ = ripper_new_yylval(p, 0, $$, s2);
- }
- %*/
+ /*% ripper: regexp_add!($:1, $:2) %*/
}
;
string_content : tSTRING_CONTENT
- /*% ripper[brace]: ripper_new_yylval(p, 0, get_value($1), $1) %*/
+ /*% ripper[brace]: get_value($:1); %*/
| tSTRING_DVAR
{
/* need to backup p->lex.strterm so that a string literal `%&foo,#$&,bar&` can be parsed */
@@ -5263,71 +6284,51 @@ string_content : tSTRING_CONTENT
string_dvar
{
p->lex.strterm = $<strterm>2;
- /*%%%*/
$$ = NEW_EVSTR($3, &@$);
nd_set_line($$, @3.end_pos.lineno);
- /*% %*/
- /*% ripper: string_dvar!($3) %*/
+ /*% ripper: string_dvar!($:3) %*/
}
- | tSTRING_DBEG
+ | tSTRING_DBEG[term]
{
CMDARG_PUSH(0);
COND_PUSH(0);
- }
- {
/* need to backup p->lex.strterm so that a string literal `%!foo,#{ !0 },bar!` can be parsed */
- $<strterm>$ = p->lex.strterm;
+ $<strterm>term = p->lex.strterm;
p->lex.strterm = 0;
- }
- {
$<num>$ = p->lex.state;
SET_LEX_STATE(EXPR_BEG);
- }
+ }[state]
{
$<num>$ = p->lex.brace_nest;
p->lex.brace_nest = 0;
- }
+ }[brace]
{
$<num>$ = p->heredoc_indent;
p->heredoc_indent = 0;
- }
- compstmt tSTRING_DEND
+ }[indent]
+ compstmt string_dend
{
COND_POP();
CMDARG_POP();
- p->lex.strterm = $<strterm>3;
- SET_LEX_STATE($<num>4);
- p->lex.brace_nest = $<num>5;
- p->heredoc_indent = $<num>6;
+ p->lex.strterm = $<strterm>term;
+ SET_LEX_STATE($<num>state);
+ p->lex.brace_nest = $<num>brace;
+ p->heredoc_indent = $<num>indent;
p->heredoc_line_indent = -1;
- /*%%%*/
- if ($7) $7->flags &= ~NODE_FL_NEWLINE;
- $$ = new_evstr(p, $7, &@$);
- /*% %*/
- /*% ripper: string_embexpr!($7) %*/
+ if ($compstmt) nd_unset_fl_newline($compstmt);
+ $$ = new_evstr(p, $compstmt, &@$);
+ /*% ripper: string_embexpr!($:compstmt) %*/
}
;
-string_dvar : tGVAR
- {
- /*%%%*/
- $$ = NEW_GVAR($1, &@$);
- /*% %*/
- /*% ripper: var_ref!($1) %*/
- }
- | tIVAR
+string_dend : tSTRING_DEND
+ | END_OF_INPUT
+ ;
+
+string_dvar : nonlocal_var
{
- /*%%%*/
- $$ = NEW_IVAR($1, &@$);
- /*% %*/
- /*% ripper: var_ref!($1) %*/
- }
- | tCVAR
- {
- /*%%%*/
- $$ = NEW_CVAR($1, &@$);
- /*% %*/
- /*% ripper: var_ref!($1) %*/
+ if (!($$ = gettable(p, $1, &@$))) $$ = NEW_ERROR(&@$);
+ /*% ripper: var_ref!($:1) %*/
}
| backref
;
@@ -5339,10 +6340,16 @@ symbol : ssym
ssym : tSYMBEG sym
{
SET_LEX_STATE(EXPR_END);
- /*%%%*/
- $$ = NEW_LIT(ID2SYM($2), &@$);
- /*% %*/
- /*% ripper: symbol_literal!(symbol!($2)) %*/
+ VALUE str = rb_id2str($2);
+ /*
+ * TODO:
+ * set_yylval_noname sets invalid id to yylval.
+ * This branch can be removed once yylval is changed to
+ * hold lexed string.
+ */
+ if (!str) str = STR_NEW0();
+ $$ = NEW_SYM(str, &@$);
+ /*% ripper: symbol_literal!(symbol!($:2)) %*/
}
;
@@ -5353,21 +6360,17 @@ sym : fname
dsym : tSYMBEG string_contents tSTRING_END
{
SET_LEX_STATE(EXPR_END);
- /*%%%*/
$$ = dsym_node(p, $2, &@$);
- /*% %*/
- /*% ripper: dyna_symbol!($2) %*/
+ /*% ripper: dyna_symbol!($:2) %*/
}
;
numeric : simple_numeric
| tUMINUS_NUM simple_numeric %prec tLOWEST
{
- /*%%%*/
$$ = $2;
- RB_OBJ_WRITE(p->ast, &$$->nd_lit, negate_lit(p, $$->nd_lit));
- /*% %*/
- /*% ripper: unary!(ID2VAL(idUMinus), $2) %*/
+ negate_lit(p, $$);
+ /*% ripper: unary!(ID2VAL(idUMinus), $:2) %*/
}
;
@@ -5398,39 +6401,35 @@ keyword_variable: keyword_nil {$$ = KWD2EID(nil, $1);}
var_ref : user_variable
{
+ if (!($$ = gettable(p, $1, &@$))) $$ = NEW_ERROR(&@$);
/*%%%*/
- if (!($$ = gettable(p, $1, &@$))) $$ = NEW_BEGIN(0, &@$);
/*%
- if (id_is_var(p, get_id($1))) {
- $$ = dispatch1(var_ref, $1);
+ if (id_is_var(p, $1)) {
+ VALUE val = dispatch1(var_ref, get_value($:1));
+ set_value(val);
}
else {
- $$ = dispatch1(vcall, $1);
+ VALUE val = dispatch1(vcall, get_value($:1));
+ set_value(val);
}
%*/
}
| keyword_variable
{
- /*%%%*/
- if (!($$ = gettable(p, $1, &@$))) $$ = NEW_BEGIN(0, &@$);
- /*% %*/
- /*% ripper: var_ref!($1) %*/
+ if (!($$ = gettable(p, $1, &@$))) $$ = NEW_ERROR(&@$);
+ /*% ripper: var_ref!($:1) %*/
}
;
var_lhs : user_variable
{
- /*%%%*/
$$ = assignable(p, $1, 0, &@$);
- /*% %*/
- /*% ripper: assignable(p, var_field(p, $1)) %*/
+ /*% ripper: ripper_assignable(p, $1, var_field(p, get_value($:1))) %*/
}
| keyword_variable
{
- /*%%%*/
$$ = assignable(p, $1, 0, &@$);
- /*% %*/
- /*% ripper: assignable(p, var_field(p, $1)) %*/
+ /*% ripper: ripper_assignable(p, $1, var_field(p, get_value($:1))) %*/
}
;
@@ -5446,12 +6445,11 @@ superclass : '<'
expr_value term
{
$$ = $3;
+ /*% ripper: get_value($:3); %*/
}
| /* none */
{
- /*%%%*/
$$ = 0;
- /*% %*/
/*% ripper: Qnil %*/
}
;
@@ -5460,17 +6458,16 @@ f_opt_paren_args: f_paren_args
| none
{
p->ctxt.in_argdef = 0;
- $$ = new_args_tail(p, Qnone, Qnone, Qnone, &@0);
- $$ = new_args(p, Qnone, Qnone, Qnone, Qnone, $$, &@0);
+ $$ = new_args_tail(p, 0, 0, 0, &@0);
+ $$ = new_args(p, 0, 0, 0, 0, $$, &@0);
+ /*% ripper: ripper_new_args(p, Qnil, Qnil, Qnil, Qnil, rb_ary_new_from_args(3, Qnil, Qnil, Qnil)) %*/
}
;
f_paren_args : '(' f_args rparen
{
- /*%%%*/
$$ = $2;
- /*% %*/
- /*% ripper: paren!($2) %*/
+ /*% ripper: paren!($:2) %*/
SET_LEX_STATE(EXPR_BEG);
p->command_start = TRUE;
p->ctxt.in_argdef = 0;
@@ -5491,117 +6488,136 @@ f_arglist : f_paren_args
$$ = $2;
SET_LEX_STATE(EXPR_BEG);
p->command_start = TRUE;
+ /*% ripper: get_value($:2); %*/
}
;
args_tail : f_kwarg ',' f_kwrest opt_f_block_arg
{
$$ = new_args_tail(p, $1, $3, $4, &@3);
+ /*% ripper: rb_ary_new_from_args(3, get_value($:1), get_value($:3), get_value($:4)); %*/
}
| f_kwarg opt_f_block_arg
{
- $$ = new_args_tail(p, $1, Qnone, $2, &@1);
+ $$ = new_args_tail(p, $1, 0, $2, &@1);
+ /*% ripper: rb_ary_new_from_args(3, get_value($:1), Qnil, get_value($:2)); %*/
}
| f_any_kwrest opt_f_block_arg
{
- $$ = new_args_tail(p, Qnone, $1, $2, &@1);
+ $$ = new_args_tail(p, 0, $1, $2, &@1);
+ /*% ripper: rb_ary_new_from_args(3, Qnil, get_value($:1), get_value($:2)); %*/
}
| f_block_arg
{
- $$ = new_args_tail(p, Qnone, Qnone, $1, &@1);
+ $$ = new_args_tail(p, 0, 0, $1, &@1);
+ /*% ripper: rb_ary_new_from_args(3, Qnil, Qnil, get_value($:1)); %*/
}
| args_forward
{
add_forwarding_args(p);
- $$ = new_args_tail(p, Qnone, $1, ID2VAL(idFWD_BLOCK), &@1);
- /*%%%*/
- ($$->nd_ainfo)->forwarding = 1;
- /*% %*/
+ $$ = new_args_tail(p, 0, $1, arg_FWD_BLOCK, &@1);
+ $$->nd_ainfo.forwarding = 1;
+ /*% ripper: rb_ary_new_from_args(3, Qnil, get_value($:1), Qnil); %*/
}
;
opt_args_tail : ',' args_tail
{
$$ = $2;
+ /*% ripper: get_value($:2); %*/
}
| /* none */
{
- $$ = new_args_tail(p, Qnone, Qnone, Qnone, &@0);
+ $$ = new_args_tail(p, 0, 0, 0, &@0);
+ /*% ripper: rb_ary_new_from_args(3, Qnil, Qnil, Qnil); %*/
}
;
f_args : f_arg ',' f_optarg ',' f_rest_arg opt_args_tail
{
- $$ = new_args(p, $1, $3, $5, Qnone, $6, &@$);
+ $$ = new_args(p, $1, $3, $5, 0, $6, &@$);
+ /*% ripper: ripper_new_args(p, get_value($:1), get_value($:3), get_value($:5), Qnil, get_value($:6)) %*/
}
| f_arg ',' f_optarg ',' f_rest_arg ',' f_arg opt_args_tail
{
$$ = new_args(p, $1, $3, $5, $7, $8, &@$);
+ /*% ripper: ripper_new_args(p, get_value($:1), get_value($:3), get_value($:5), get_value($:7), get_value($:8)) %*/
}
| f_arg ',' f_optarg opt_args_tail
{
- $$ = new_args(p, $1, $3, Qnone, Qnone, $4, &@$);
+ $$ = new_args(p, $1, $3, 0, 0, $4, &@$);
+ /*% ripper: ripper_new_args(p, get_value($:1), get_value($:3), Qnil, Qnil, get_value($:4)) %*/
}
| f_arg ',' f_optarg ',' f_arg opt_args_tail
{
- $$ = new_args(p, $1, $3, Qnone, $5, $6, &@$);
+ $$ = new_args(p, $1, $3, 0, $5, $6, &@$);
+ /*% ripper: ripper_new_args(p, get_value($:1), get_value($:3), Qnil, get_value($:5), get_value($:6)) %*/
}
| f_arg ',' f_rest_arg opt_args_tail
{
- $$ = new_args(p, $1, Qnone, $3, Qnone, $4, &@$);
+ $$ = new_args(p, $1, 0, $3, 0, $4, &@$);
+ /*% ripper: ripper_new_args(p, get_value($:1), Qnil, get_value($:3), Qnil, get_value($:4)) %*/
}
| f_arg ',' f_rest_arg ',' f_arg opt_args_tail
{
- $$ = new_args(p, $1, Qnone, $3, $5, $6, &@$);
+ $$ = new_args(p, $1, 0, $3, $5, $6, &@$);
+ /*% ripper: ripper_new_args(p, get_value($:1), Qnil, get_value($:3), get_value($:5), get_value($:6)) %*/
}
| f_arg opt_args_tail
{
- $$ = new_args(p, $1, Qnone, Qnone, Qnone, $2, &@$);
+ $$ = new_args(p, $1, 0, 0, 0, $2, &@$);
+ /*% ripper: ripper_new_args(p, get_value($:1), Qnil, Qnil, Qnil, get_value($:2)) %*/
}
| f_optarg ',' f_rest_arg opt_args_tail
{
- $$ = new_args(p, Qnone, $1, $3, Qnone, $4, &@$);
+ $$ = new_args(p, 0, $1, $3, 0, $4, &@$);
+ /*% ripper: ripper_new_args(p, Qnil, get_value($:1), get_value($:3), Qnil, get_value($:4)) %*/
}
| f_optarg ',' f_rest_arg ',' f_arg opt_args_tail
{
- $$ = new_args(p, Qnone, $1, $3, $5, $6, &@$);
+ $$ = new_args(p, 0, $1, $3, $5, $6, &@$);
+ /*% ripper: ripper_new_args(p, Qnil, get_value($:1), get_value($:3), get_value($:5), get_value($:6)) %*/
}
| f_optarg opt_args_tail
{
- $$ = new_args(p, Qnone, $1, Qnone, Qnone, $2, &@$);
+ $$ = new_args(p, 0, $1, 0, 0, $2, &@$);
+ /*% ripper: ripper_new_args(p, Qnil, get_value($:1), Qnil, Qnil, get_value($:2)) %*/
}
| f_optarg ',' f_arg opt_args_tail
{
- $$ = new_args(p, Qnone, $1, Qnone, $3, $4, &@$);
+ $$ = new_args(p, 0, $1, 0, $3, $4, &@$);
+ /*% ripper: ripper_new_args(p, Qnil, get_value($:1), Qnil, get_value($:3), get_value($:4)) %*/
}
| f_rest_arg opt_args_tail
{
- $$ = new_args(p, Qnone, Qnone, $1, Qnone, $2, &@$);
+ $$ = new_args(p, 0, 0, $1, 0, $2, &@$);
+ /*% ripper: ripper_new_args(p, Qnil, Qnil, get_value($:1), Qnil, get_value($:2)) %*/
}
| f_rest_arg ',' f_arg opt_args_tail
{
- $$ = new_args(p, Qnone, Qnone, $1, $3, $4, &@$);
+ $$ = new_args(p, 0, 0, $1, $3, $4, &@$);
+ /*% ripper: ripper_new_args(p, Qnil, Qnil, get_value($:1), get_value($:3), get_value($:4)) %*/
}
| args_tail
{
- $$ = new_args(p, Qnone, Qnone, Qnone, Qnone, $1, &@$);
+ $$ = new_args(p, 0, 0, 0, 0, $1, &@$);
+ /*% ripper: ripper_new_args(p, Qnil, Qnil, Qnil, Qnil, get_value($:1)) %*/
}
| /* none */
{
- $$ = new_args_tail(p, Qnone, Qnone, Qnone, &@0);
- $$ = new_args(p, Qnone, Qnone, Qnone, Qnone, $$, &@0);
+ $$ = new_args_tail(p, 0, 0, 0, &@0);
+ $$ = new_args(p, 0, 0, 0, 0, $$, &@0);
+ /*% ripper: ripper_new_args(p, Qnil, Qnil, Qnil, Qnil, rb_ary_new_from_args(3, Qnil, Qnil, Qnil)) %*/
}
;
args_forward : tBDOT3
{
- /*%%%*/
#ifdef FORWARD_ARGS_WITH_RUBY2_KEYWORDS
$$ = 0;
#else
$$ = idFWD_KWREST;
#endif
- /*% %*/
/*% ripper: args_forward! %*/
}
;
@@ -5611,36 +6627,36 @@ f_bad_arg : tCONSTANT
static const char mesg[] = "formal argument cannot be a constant";
/*%%%*/
yyerror1(&@1, mesg);
- $$ = 0;
/*% %*/
- /*% ripper[error]: param_error!(ERR_MESG(), $1) %*/
+ $$ = 0;
+ /*% ripper[error]: param_error!(ERR_MESG(), $:1) %*/
}
| tIVAR
{
static const char mesg[] = "formal argument cannot be an instance variable";
/*%%%*/
yyerror1(&@1, mesg);
- $$ = 0;
/*% %*/
- /*% ripper[error]: param_error!(ERR_MESG(), $1) %*/
+ $$ = 0;
+ /*% ripper[error]: param_error!(ERR_MESG(), $:1) %*/
}
| tGVAR
{
static const char mesg[] = "formal argument cannot be a global variable";
/*%%%*/
yyerror1(&@1, mesg);
- $$ = 0;
/*% %*/
- /*% ripper[error]: param_error!(ERR_MESG(), $1) %*/
+ $$ = 0;
+ /*% ripper[error]: param_error!(ERR_MESG(), $:1) %*/
}
| tCVAR
{
static const char mesg[] = "formal argument cannot be a class variable";
/*%%%*/
yyerror1(&@1, mesg);
- $$ = 0;
/*% %*/
- /*% ripper[error]: param_error!(ERR_MESG(), $1) %*/
+ $$ = 0;
+ /*% ripper[error]: param_error!(ERR_MESG(), $:1) %*/
}
;
@@ -5650,12 +6666,16 @@ f_norm_arg : f_bad_arg
formal_argument(p, $1);
p->max_numparam = ORDINAL_PARAM;
$$ = $1;
+ /*%%%*/
+ /*%
+ ripper_formal_argument(p, $1, get_value($:1));
+ %*/
}
;
f_arg_asgn : f_norm_arg
{
- ID id = get_id($1);
+ ID id = $1;
arg_var(p, id);
p->cur_arg = id;
$$ = $1;
@@ -5665,14 +6685,11 @@ f_arg_asgn : f_norm_arg
f_arg_item : f_arg_asgn
{
p->cur_arg = 0;
- /*%%%*/
$$ = NEW_ARGS_AUX($1, 1, &NULL_LOC);
- /*% %*/
- /*% ripper: get_value($1) %*/
+ /*% ripper: get_value($:1) %*/
}
| tLPAREN f_margs rparen
{
- /*%%%*/
ID tid = internal_id(p);
YYLTYPE loc;
loc.beg_pos = @2.beg_pos;
@@ -5685,23 +6702,20 @@ f_arg_item : f_arg_asgn
$2->nd_value = NEW_LVAR(tid, &loc);
}
$$ = NEW_ARGS_AUX(tid, 1, &NULL_LOC);
- $$->nd_next = $2;
- /*% %*/
- /*% ripper: mlhs_paren!($2) %*/
+ $$->nd_next = (NODE *)$2;
+ /*% ripper: mlhs_paren!($:2) %*/
}
;
f_arg : f_arg_item
- /*% ripper[brace]: rb_ary_new3(1, get_value($1)) %*/
+ /*% ripper[brace]: rb_ary_new3(1, get_value($:1)) %*/
| f_arg ',' f_arg_item
{
- /*%%%*/
$$ = $1;
$$->nd_plen++;
$$->nd_next = block_append(p, $$->nd_next, $3->nd_next);
- rb_discard_node(p, $3);
- /*% %*/
- /*% ripper: rb_ary_push($1, get_value($3)) %*/
+ rb_discard_node(p, (NODE *)$3);
+ /*% ripper: rb_ary_push(get_value($:1), get_value($:3)) %*/
}
;
@@ -5709,10 +6723,14 @@ f_arg : f_arg_item
f_label : tLABEL
{
arg_var(p, formal_argument(p, $1));
- p->cur_arg = get_id($1);
+ p->cur_arg = $1;
p->max_numparam = ORDINAL_PARAM;
p->ctxt.in_argdef = 0;
$$ = $1;
+ /*%%%*/
+ /*%
+ ripper_formal_argument(p, $1, get_value($:1));
+ %*/
}
;
@@ -5720,70 +6738,54 @@ f_kw : f_label arg_value
{
p->cur_arg = 0;
p->ctxt.in_argdef = 1;
- /*%%%*/
$$ = new_kw_arg(p, assignable(p, $1, $2, &@$), &@$);
- /*% %*/
- /*% ripper: rb_assoc_new(get_value(assignable(p, $1)), get_value($2)) %*/
+ /*% ripper: rb_assoc_new(ripper_assignable(p, $1, get_value($:1)), get_value($:2)) %*/
}
| f_label
{
p->cur_arg = 0;
p->ctxt.in_argdef = 1;
- /*%%%*/
$$ = new_kw_arg(p, assignable(p, $1, NODE_SPECIAL_REQUIRED_KEYWORD, &@$), &@$);
- /*% %*/
- /*% ripper: rb_assoc_new(get_value(assignable(p, $1)), 0) %*/
+ /*% ripper: rb_assoc_new(ripper_assignable(p, $1, get_value($:1)), 0) %*/
}
;
f_block_kw : f_label primary_value
{
p->ctxt.in_argdef = 1;
- /*%%%*/
$$ = new_kw_arg(p, assignable(p, $1, $2, &@$), &@$);
- /*% %*/
- /*% ripper: rb_assoc_new(get_value(assignable(p, $1)), get_value($2)) %*/
+ /*% ripper: rb_assoc_new(ripper_assignable(p, $1, get_value($:1)), get_value($:2)) %*/
}
| f_label
{
p->ctxt.in_argdef = 1;
- /*%%%*/
$$ = new_kw_arg(p, assignable(p, $1, NODE_SPECIAL_REQUIRED_KEYWORD, &@$), &@$);
- /*% %*/
- /*% ripper: rb_assoc_new(get_value(assignable(p, $1)), 0) %*/
+ /*% ripper: rb_assoc_new(ripper_assignable(p, $1, get_value($:1)), 0) %*/
}
;
f_block_kwarg : f_block_kw
{
- /*%%%*/
$$ = $1;
- /*% %*/
- /*% ripper: rb_ary_new3(1, get_value($1)) %*/
+ /*% ripper: rb_ary_new3(1, get_value($:1)) %*/
}
| f_block_kwarg ',' f_block_kw
{
- /*%%%*/
$$ = kwd_append($1, $3);
- /*% %*/
- /*% ripper: rb_ary_push($1, get_value($3)) %*/
+ /*% ripper: rb_ary_push(get_value($:1), get_value($:3)) %*/
}
;
f_kwarg : f_kw
{
- /*%%%*/
$$ = $1;
- /*% %*/
- /*% ripper: rb_ary_new3(1, get_value($1)) %*/
+ /*% ripper: rb_ary_new3(1, get_value($:1)) %*/
}
| f_kwarg ',' f_kw
{
- /*%%%*/
$$ = kwd_append($1, $3);
- /*% %*/
- /*% ripper: rb_ary_push($1, get_value($3)) %*/
+ /*% ripper: rb_ary_push(get_value($:1), get_value($:3)) %*/
}
;
@@ -5793,26 +6795,20 @@ kwrest_mark : tPOW
f_no_kwarg : p_kwnorest
{
- /*%%%*/
- /*% %*/
/*% ripper: nokw_param!(Qnil) %*/
}
;
f_kwrest : kwrest_mark tIDENTIFIER
{
- arg_var(p, shadowing_lvar(p, get_id($2)));
- /*%%%*/
+ arg_var(p, shadowing_lvar(p, $2));
$$ = $2;
- /*% %*/
- /*% ripper: kwrest_param!($2) %*/
+ /*% ripper: kwrest_param!($:2) %*/
}
| kwrest_mark
{
arg_var(p, idFWD_KWREST);
- /*%%%*/
$$ = idFWD_KWREST;
- /*% %*/
/*% ripper: kwrest_param!(Qnil) %*/
}
;
@@ -5821,10 +6817,8 @@ f_opt : f_arg_asgn f_eq arg_value
{
p->cur_arg = 0;
p->ctxt.in_argdef = 1;
- /*%%%*/
- $$ = NEW_OPT_ARG(0, assignable(p, $1, $3, &@$), &@$);
- /*% %*/
- /*% ripper: rb_assoc_new(get_value(assignable(p, $1)), get_value($3)) %*/
+ $$ = NEW_OPT_ARG(assignable(p, $1, $3, &@$), &@$);
+ /*% ripper: rb_assoc_new(ripper_assignable(p, $1, get_value($:1)), get_value($:3)) %*/
}
;
@@ -5832,42 +6826,32 @@ f_block_opt : f_arg_asgn f_eq primary_value
{
p->cur_arg = 0;
p->ctxt.in_argdef = 1;
- /*%%%*/
- $$ = NEW_OPT_ARG(0, assignable(p, $1, $3, &@$), &@$);
- /*% %*/
- /*% ripper: rb_assoc_new(get_value(assignable(p, $1)), get_value($3)) %*/
+ $$ = NEW_OPT_ARG(assignable(p, $1, $3, &@$), &@$);
+ /*% ripper: rb_assoc_new(ripper_assignable(p, $1, get_value($:1)), get_value($:3)) %*/
}
;
f_block_optarg : f_block_opt
{
- /*%%%*/
$$ = $1;
- /*% %*/
- /*% ripper: rb_ary_new3(1, get_value($1)) %*/
+ /*% ripper: rb_ary_new3(1, get_value($:1)) %*/
}
| f_block_optarg ',' f_block_opt
{
- /*%%%*/
$$ = opt_arg_append($1, $3);
- /*% %*/
- /*% ripper: rb_ary_push($1, get_value($3)) %*/
+ /*% ripper: rb_ary_push(get_value($:1), get_value($:3)) %*/
}
;
f_optarg : f_opt
{
- /*%%%*/
$$ = $1;
- /*% %*/
- /*% ripper: rb_ary_new3(1, get_value($1)) %*/
+ /*% ripper: rb_ary_new3(1, get_value($:1)) %*/
}
| f_optarg ',' f_opt
{
- /*%%%*/
$$ = opt_arg_append($1, $3);
- /*% %*/
- /*% ripper: rb_ary_push($1, get_value($3)) %*/
+ /*% ripper: rb_ary_push(get_value($:1), get_value($:3)) %*/
}
;
@@ -5877,18 +6861,14 @@ restarg_mark : '*'
f_rest_arg : restarg_mark tIDENTIFIER
{
- arg_var(p, shadowing_lvar(p, get_id($2)));
- /*%%%*/
+ arg_var(p, shadowing_lvar(p, $2));
$$ = $2;
- /*% %*/
- /*% ripper: rest_param!($2) %*/
+ /*% ripper: rest_param!($:2) %*/
}
| restarg_mark
{
arg_var(p, idFWD_REST);
- /*%%%*/
$$ = idFWD_REST;
- /*% %*/
/*% ripper: rest_param!(Qnil) %*/
}
;
@@ -5899,18 +6879,14 @@ blkarg_mark : '&'
f_block_arg : blkarg_mark tIDENTIFIER
{
- arg_var(p, shadowing_lvar(p, get_id($2)));
- /*%%%*/
+ arg_var(p, shadowing_lvar(p, $2));
$$ = $2;
- /*% %*/
- /*% ripper: blockarg!($2) %*/
+ /*% ripper: blockarg!($:2) %*/
}
| blkarg_mark
{
arg_var(p, idFWD_BLOCK);
- /*%%%*/
$$ = idFWD_BLOCK;
- /*% %*/
/*% ripper: blockarg!(Qnil) %*/
}
;
@@ -5918,10 +6894,12 @@ f_block_arg : blkarg_mark tIDENTIFIER
opt_f_block_arg : ',' f_block_arg
{
$$ = $2;
+ /*% ripper: get_value($:2); %*/
}
| none
{
- $$ = Qnull;
+ $$ = 0;
+ /*% ripper: Qnil; %*/
}
;
@@ -5932,125 +6910,104 @@ singleton : var_ref
}
| '(' {SET_LEX_STATE(EXPR_BEG);} expr rparen
{
- /*%%%*/
- switch (nd_type($3)) {
+ NODE *expr = last_expr_node($3);
+ switch (nd_type(expr)) {
case NODE_STR:
case NODE_DSTR:
case NODE_XSTR:
case NODE_DXSTR:
+ case NODE_REGX:
case NODE_DREGX:
- case NODE_LIT:
+ case NODE_SYM:
+ case NODE_LINE:
+ case NODE_FILE:
+ case NODE_ENCODING:
+ case NODE_INTEGER:
+ case NODE_FLOAT:
+ case NODE_RATIONAL:
+ case NODE_IMAGINARY:
+ case NODE_DSYM:
case NODE_LIST:
case NODE_ZLIST:
- yyerror1(&@3, "can't define singleton method for literals");
+ yyerror1(&expr->nd_loc, "can't define singleton method for literals");
break;
default:
value_expr($3);
break;
}
$$ = $3;
- /*% %*/
- /*% ripper: paren!($3) %*/
+ /*% ripper: paren!($:3) %*/
}
;
assoc_list : none
| assocs trailer
{
- /*%%%*/
$$ = $1;
- /*% %*/
- /*% ripper: assoclist_from_args!($1) %*/
+ /*% ripper: assoclist_from_args!($:1) %*/
}
;
assocs : assoc
- /*% ripper[brace]: rb_ary_new3(1, get_value($1)) %*/
+ /*% ripper[brace]: rb_ary_new3(1, get_value($:1)) %*/
| assocs ',' assoc
{
- /*%%%*/
NODE *assocs = $1;
NODE *tail = $3;
if (!assocs) {
assocs = tail;
}
else if (tail) {
- if (assocs->nd_head &&
- !tail->nd_head && nd_type_p(tail->nd_next, NODE_LIST) &&
- nd_type_p(tail->nd_next->nd_head, NODE_HASH)) {
- /* DSTAR */
- tail = tail->nd_next->nd_head->nd_head;
+ if (RNODE_LIST(assocs)->nd_head) {
+ NODE *n = RNODE_LIST(tail)->nd_next;
+ if (!RNODE_LIST(tail)->nd_head && nd_type_p(n, NODE_LIST) &&
+ nd_type_p((n = RNODE_LIST(n)->nd_head), NODE_HASH)) {
+ /* DSTAR */
+ tail = RNODE_HASH(n)->nd_head;
+ }
+ }
+ if (tail) {
+ assocs = list_concat(assocs, tail);
}
- assocs = list_concat(assocs, tail);
}
$$ = assocs;
- /*% %*/
- /*% ripper: rb_ary_push($1, get_value($3)) %*/
+ /*% ripper: rb_ary_push(get_value($:1), get_value($:3)) %*/
}
;
assoc : arg_value tASSOC arg_value
{
- /*%%%*/
- if (nd_type_p($1, NODE_STR)) {
- nd_set_type($1, NODE_LIT);
- RB_OBJ_WRITE(p->ast, &$1->nd_lit, rb_fstring($1->nd_lit));
- }
$$ = list_append(p, NEW_LIST($1, &@$), $3);
- /*% %*/
- /*% ripper: assoc_new!($1, $3) %*/
+ /*% ripper: assoc_new!($:1, $:3) %*/
}
| tLABEL arg_value
{
- /*%%%*/
- $$ = list_append(p, NEW_LIST(NEW_LIT(ID2SYM($1), &@1), &@$), $2);
- /*% %*/
- /*% ripper: assoc_new!($1, $2) %*/
+ $$ = list_append(p, NEW_LIST(NEW_SYM(rb_id2str($1), &@1), &@$), $2);
+ /*% ripper: assoc_new!($:1, $:2) %*/
}
| tLABEL
{
- /*%%%*/
NODE *val = gettable(p, $1, &@$);
- if (!val) val = NEW_BEGIN(0, &@$);
- $$ = list_append(p, NEW_LIST(NEW_LIT(ID2SYM($1), &@1), &@$), val);
- /*% %*/
- /*% ripper: assoc_new!($1, Qnil) %*/
+ if (!val) val = NEW_ERROR(&@$);
+ $$ = list_append(p, NEW_LIST(NEW_SYM(rb_id2str($1), &@1), &@$), val);
+ /*% ripper: assoc_new!($:1, Qnil) %*/
}
| tSTRING_BEG string_contents tLABEL_END arg_value
{
- /*%%%*/
YYLTYPE loc = code_loc_gen(&@1, &@3);
$$ = list_append(p, NEW_LIST(dsym_node(p, $2, &loc), &loc), $4);
- /*% %*/
- /*% ripper: assoc_new!(dyna_symbol!($2), $4) %*/
+ /*% ripper: assoc_new!(dyna_symbol!($:2), $:4) %*/
}
| tDSTAR arg_value
{
- /*%%%*/
- if (nd_type_p($2, NODE_HASH) &&
- !($2->nd_head && $2->nd_head->nd_alen)) {
- static VALUE empty_hash;
- if (!empty_hash) {
- empty_hash = rb_obj_freeze(rb_hash_new());
- rb_gc_register_mark_object(empty_hash);
- }
- $$ = list_append(p, NEW_LIST(0, &@$), NEW_LIT(empty_hash, &@$));
- }
- else
- $$ = list_append(p, NEW_LIST(0, &@$), $2);
- /*% %*/
- /*% ripper: assoc_splat!($2) %*/
+ $$ = list_append(p, NEW_LIST(0, &@$), $2);
+ /*% ripper: assoc_splat!($:2) %*/
}
| tDSTAR
{
- if (!local_id(p, idFWD_KWREST) ||
- local_id(p, idFWD_ALL)) {
- compile_error(p, "no anonymous keyword rest parameter");
- }
- /*%%%*/
+ forwarding_arg_check(p, idFWD_KWREST, idFWD_ALL, "keyword rest");
$$ = list_append(p, NEW_LIST(0, &@$),
NEW_LVAR(idFWD_KWREST, &@$));
- /*% %*/
/*% ripper: assoc_splat!(Qnil) %*/
}
;
@@ -6081,24 +7038,16 @@ call_op2 : call_op
| tCOLON2
;
-opt_terms : /* none */
- | terms
- ;
-
-opt_nl : /* none */
- | '\n'
+rparen : '\n'? ')'
;
-rparen : opt_nl ')'
+rbracket : '\n'? ']'
;
-rbracket : opt_nl ']'
+rbrace : '\n'? '}'
;
-rbrace : opt_nl '}'
- ;
-
-trailer : opt_nl
+trailer : '\n'?
| ','
;
@@ -6116,7 +7065,11 @@ terms : term
none : /* none */
{
- $$ = Qnull;
+ $$ = 0;
+ /*%%%*/
+ /*%
+ set_value(rb_ripper_none);
+ %*/
}
;
%%
@@ -6132,43 +7085,35 @@ static enum yytokentype parse_string(struct parser_params*,rb_strterm_literal_t*
static enum yytokentype here_document(struct parser_params*,rb_strterm_heredoc_t*);
#ifndef RIPPER
+#define set_parser_s_value(x) (void)(x)
+#else
+#define set_parser_s_value(x) (p->s_value = (x))
+#endif
+
# define set_yylval_node(x) { \
YYLTYPE _cur_loc; \
rb_parser_set_location(p, &_cur_loc); \
yylval.node = (x); \
+ set_parser_s_value(STR_NEW(p->lex.ptok, p->lex.pcur-p->lex.ptok)); \
}
# define set_yylval_str(x) \
do { \
- set_yylval_node(NEW_STR(x, &_cur_loc)); \
- RB_OBJ_WRITTEN(p->ast, Qnil, x); \
-} while(0)
-# define set_yylval_literal(x) \
-do { \
- set_yylval_node(NEW_LIT(x, &_cur_loc)); \
- RB_OBJ_WRITTEN(p->ast, Qnil, x); \
+ set_yylval_node(NEW_STR(rb_str_to_parser_string(p, x), &_cur_loc)); \
+ set_parser_s_value(x); \
} while(0)
-# define set_yylval_num(x) (yylval.num = (x))
-# define set_yylval_id(x) (yylval.id = (x))
-# define set_yylval_name(x) (yylval.id = (x))
+# define set_yylval_num(x) { \
+ yylval.num = (x); \
+ set_parser_s_value(x); \
+}
+# define set_yylval_id(x) (yylval.id = (x))
+# define set_yylval_name(x) { \
+ (yylval.id = (x)); \
+ set_parser_s_value(ID2SYM(x)); \
+}
# define yylval_id() (yylval.id)
-#else
-static inline VALUE
-ripper_yylval_id(struct parser_params *p, ID x)
-{
- return ripper_new_yylval(p, x, ID2SYM(x), 0);
-}
-# define set_yylval_str(x) (yylval.val = add_mark_object(p, (x)))
-# define set_yylval_num(x) (yylval.val = ripper_new_yylval(p, (x), 0, 0))
-# define set_yylval_id(x) (void)(x)
-# define set_yylval_name(x) (void)(yylval.val = ripper_yylval_id(p, x))
-# define set_yylval_literal(x) add_mark_object(p, (x))
-# define set_yylval_node(x) (yylval.val = ripper_new_yylval(p, 0, 0, STR_NEW(p->lex.ptok, p->lex.pcur-p->lex.ptok)))
-# define yylval_id() yylval.id
-# define _cur_loc NULL_LOC /* dummy */
-#endif
#define set_yylval_noname() set_yylval_id(keyword_nil)
-#define has_delayed_token(p) (!NIL_P(p->delayed.token))
+#define has_delayed_token(p) (p->delayed.token != NULL)
#ifndef RIPPER
#define literal_flush(p, ptr) ((p)->lex.ptok = (ptr))
@@ -6186,35 +7131,100 @@ parser_has_token(struct parser_params *p)
return pcur > ptok;
}
-static VALUE
-code_loc_to_ary(const rb_code_location_t *loc)
+static const char *
+escaped_char(int c)
{
- VALUE ary = rb_ary_new_from_args(4,
- INT2NUM(loc->beg_pos.lineno), INT2NUM(loc->beg_pos.column),
- INT2NUM(loc->end_pos.lineno), INT2NUM(loc->end_pos.column));
- rb_obj_freeze(ary);
-
- return ary;
+ switch (c) {
+ case '"': return "\\\"";
+ case '\\': return "\\\\";
+ case '\0': return "\\0";
+ case '\n': return "\\n";
+ case '\r': return "\\r";
+ case '\t': return "\\t";
+ case '\f': return "\\f";
+ case '\013': return "\\v";
+ case '\010': return "\\b";
+ case '\007': return "\\a";
+ case '\033': return "\\e";
+ case '\x7f': return "\\c?";
+ }
+ return NULL;
}
-static void
-parser_append_tokens(struct parser_params *p, VALUE str, enum yytokentype t, int line)
+static rb_parser_string_t *
+rb_parser_str_escape(struct parser_params *p, rb_parser_string_t *str)
{
- VALUE ary;
- int token_id;
+ rb_encoding *enc = p->enc;
+ const char *ptr = str->ptr;
+ const char *pend = ptr + str->len;
+ const char *prev = ptr;
+ char charbuf[5] = {'\\', 'x', 0, 0, 0};
+ rb_parser_string_t * result = rb_parser_string_new(p, 0, 0);
+ int asciicompat = rb_enc_asciicompat(enc);
+
+ while (ptr < pend) {
+ unsigned int c;
+ const char *cc;
+ int n = rb_enc_precise_mbclen(ptr, pend, enc);
+ if (!MBCLEN_CHARFOUND_P(n)) {
+ if (ptr > prev) rb_parser_str_buf_cat(p, result, prev, ptr - prev);
+ n = rb_enc_mbminlen(enc);
+ if (pend < ptr + n)
+ n = (int)(pend - ptr);
+ while (n--) {
+ c = *ptr & 0xf0 >> 4;
+ charbuf[2] = (c < 10) ? '0' + c : 'A' + c - 10;
+ c = *ptr & 0x0f;
+ charbuf[3] = (c < 10) ? '0' + c : 'A' + c - 10;
+ rb_parser_str_buf_cat(p, result, charbuf, 4);
+ prev = ++ptr;
+ }
+ continue;
+ }
+ n = MBCLEN_CHARFOUND_LEN(n);
+ c = rb_enc_mbc_to_codepoint(ptr, pend, enc);
+ ptr += n;
+ cc = escaped_char(c);
+ if (cc) {
+ if (ptr - n > prev) rb_parser_str_buf_cat(p, result, prev, ptr - n - prev);
+ rb_parser_str_buf_cat(p, result, cc, strlen(cc));
+ prev = ptr;
+ }
+ else if (asciicompat && rb_enc_isascii(c, enc) && ISPRINT(c)) {
+ }
+ else {
+ if (ptr - n > prev) {
+ rb_parser_str_buf_cat(p, result, prev, ptr - n - prev);
+ prev = ptr - n;
+ }
+ rb_parser_str_buf_cat(p, result, prev, ptr - prev);
+ prev = ptr;
+ }
+ }
+ if (ptr > prev) rb_parser_str_buf_cat(p, result, prev, ptr - prev);
- ary = rb_ary_new2(4);
- token_id = p->token_id;
- rb_ary_push(ary, INT2FIX(token_id));
- rb_ary_push(ary, ID2SYM(parser_token2id(t)));
- rb_ary_push(ary, str);
- rb_ary_push(ary, code_loc_to_ary(p->yylloc));
- rb_obj_freeze(ary);
- rb_ary_push(p->tokens, ary);
+ return result;
+}
+
+static void
+parser_append_tokens(struct parser_params *p, rb_parser_string_t *str, enum yytokentype t, int line)
+{
+ rb_parser_ast_token_t *token = xcalloc(1, sizeof(rb_parser_ast_token_t));
+ token->id = p->token_id;
+ token->type_name = parser_token2char(p, t);
+ token->str = str;
+ token->loc.beg_pos = p->yylloc->beg_pos;
+ token->loc.end_pos = p->yylloc->end_pos;
+ rb_parser_ary_push_ast_token(p, p->tokens, token);
p->token_id++;
if (p->debug) {
- rb_parser_printf(p, "Append tokens (line: %d) %"PRIsVALUE"\n", line, ary);
+ rb_parser_string_t *str_escaped = rb_parser_str_escape(p, str);
+ rb_parser_printf(p, "Append tokens (line: %d) [%d, :%s, \"%s\", [%d, %d, %d, %d]]\n",
+ line, token->id, token->type_name, str_escaped->ptr,
+ token->loc.beg_pos.lineno, token->loc.beg_pos.column,
+ token->loc.end_pos.lineno, token->loc.end_pos.column);
+ rb_parser_string_free(p, str_escaped);
}
}
@@ -6228,7 +7238,7 @@ parser_dispatch_scan_event(struct parser_params *p, enum yytokentype t, int line
RUBY_SET_YYLLOC(*p->yylloc);
if (p->keep_tokens) {
- VALUE str = STR_NEW(p->lex.ptok, p->lex.pcur - p->lex.ptok);
+ rb_parser_string_t *str = rb_parser_encoding_string_new(p, p->lex.ptok, p->lex.pcur - p->lex.ptok, p->enc);
parser_append_tokens(p, str, t, line);
}
@@ -6239,9 +7249,6 @@ parser_dispatch_scan_event(struct parser_params *p, enum yytokentype t, int line
static void
parser_dispatch_delayed_token(struct parser_params *p, enum yytokentype t, int line)
{
- int saved_line = p->ruby_sourceline;
- const char *saved_tokp = p->lex.ptok;
-
debug_token_line(p, "parser_dispatch_delayed_token", line);
if (!has_delayed_token(p)) return;
@@ -6249,27 +7256,17 @@ parser_dispatch_delayed_token(struct parser_params *p, enum yytokentype t, int l
RUBY_SET_YYLLOC_OF_DELAYED_TOKEN(*p->yylloc);
if (p->keep_tokens) {
- p->ruby_sourceline = p->delayed.beg_line;
- p->lex.ptok = p->lex.pbeg + p->delayed.beg_col;
+ /* p->delayed.token is freed by rb_parser_tokens_free */
parser_append_tokens(p, p->delayed.token, t, line);
- p->ruby_sourceline = saved_line;
- p->lex.ptok = saved_tokp;
+ } else {
+ rb_parser_string_free(p, p->delayed.token);
}
- p->delayed.token = Qnil;
+ p->delayed.token = NULL;
}
#else
#define literal_flush(p, ptr) ((void)(ptr))
-#define yylval_rval (*(RB_TYPE_P(yylval.val, T_NODE) ? &yylval.node->nd_rval : &yylval.val))
-
-static inline VALUE
-intern_sym(const char *name)
-{
- ID id = rb_intern_const(name);
- return ID2SYM(id);
-}
-
static int
ripper_has_scan_event(struct parser_params *p)
{
@@ -6291,21 +7288,27 @@ static void
ripper_dispatch_scan_event(struct parser_params *p, enum yytokentype t)
{
if (!ripper_has_scan_event(p)) return;
- add_mark_object(p, yylval_rval = ripper_scan_event_val(p, t));
+
+ set_parser_s_value(ripper_scan_event_val(p, t));
}
#define dispatch_scan_event(p, t) ripper_dispatch_scan_event(p, t)
static void
ripper_dispatch_delayed_token(struct parser_params *p, enum yytokentype t)
{
+ /* save and adjust the location to delayed token for callbacks */
int saved_line = p->ruby_sourceline;
const char *saved_tokp = p->lex.ptok;
+ VALUE s_value, str;
if (!has_delayed_token(p)) return;
p->ruby_sourceline = p->delayed.beg_line;
p->lex.ptok = p->lex.pbeg + p->delayed.beg_col;
- add_mark_object(p, yylval_rval = ripper_dispatch1(p, ripper_token2eventid(t), p->delayed.token));
- p->delayed.token = Qnil;
+ str = rb_str_new_mutable_parser_string(p->delayed.token);
+ rb_parser_string_free(p, p->delayed.token);
+ s_value = ripper_dispatch1(p, ripper_token2eventid(t), str);
+ set_parser_s_value(s_value);
+ p->delayed.token = NULL;
p->ruby_sourceline = saved_line;
p->lex.ptok = saved_tokp;
}
@@ -6313,7 +7316,7 @@ ripper_dispatch_delayed_token(struct parser_params *p, enum yytokentype t)
#endif /* RIPPER */
static inline int
-is_identchar(const char *ptr, const char *MAYBE_UNUSED(ptr_end), rb_encoding *enc)
+is_identchar(struct parser_params *p, const char *ptr, const char *MAYBE_UNUSED(ptr_end), rb_encoding *enc)
{
return rb_enc_isalnum((unsigned char)*ptr, enc) || *ptr == '_' || !ISASCII(*ptr);
}
@@ -6321,7 +7324,7 @@ is_identchar(const char *ptr, const char *MAYBE_UNUSED(ptr_end), rb_encoding *en
static inline int
parser_is_identchar(struct parser_params *p)
{
- return !(p)->eofp && is_identchar(p->lex.pcur-1, p->lex.pend, p->enc);
+ return !(p)->eofp && is_identchar(p, p->lex.pcur-1, p->lex.pend, p->enc);
}
static inline int
@@ -6424,12 +7427,10 @@ parser_precise_mbclen(struct parser_params *p, const char *ptr)
}
#ifndef RIPPER
-static void ruby_show_error_line(VALUE errbuf, const YYLTYPE *yylloc, int lineno, VALUE str);
-
static inline void
parser_show_error_line(struct parser_params *p, const YYLTYPE *yylloc)
{
- VALUE str;
+ rb_parser_string_t *str;
int lineno = p->ruby_sourceline;
if (!yylloc) {
return;
@@ -6440,11 +7441,11 @@ parser_show_error_line(struct parser_params *p, const YYLTYPE *yylloc)
else {
return;
}
- ruby_show_error_line(p->error_buffer, yylloc, lineno, str);
+ ruby_show_error_line(p, p->error_buffer, yylloc, lineno, str);
}
static int
-parser_yyerror(struct parser_params *p, const YYLTYPE *yylloc, const char *msg)
+parser_yyerror(struct parser_params *p, const rb_code_location_t *yylloc, const char *msg)
{
#if 0
YYLTYPE current;
@@ -6457,7 +7458,7 @@ parser_yyerror(struct parser_params *p, const YYLTYPE *yylloc, const char *msg)
yylloc = 0;
}
#endif
- compile_error(p, "%s", msg);
+ parser_compile_error(p, yylloc, "%s", msg);
parser_show_error_line(p, yylloc);
return 0;
}
@@ -6469,8 +7470,8 @@ parser_yyerror0(struct parser_params *p, const char *msg)
return parser_yyerror(p, RUBY_SET_YYLLOC(current), msg);
}
-static void
-ruby_show_error_line(VALUE errbuf, const YYLTYPE *yylloc, int lineno, VALUE str)
+void
+ruby_show_error_line(struct parser_params *p, VALUE errbuf, const YYLTYPE *yylloc, int lineno, rb_parser_string_t *str)
{
VALUE mesg;
const int max_line_margin = 30;
@@ -6478,13 +7479,13 @@ ruby_show_error_line(VALUE errbuf, const YYLTYPE *yylloc, int lineno, VALUE str)
const char *pre = "", *post = "", *pend;
const char *code = "", *caret = "";
const char *lim;
- const char *const pbeg = RSTRING_PTR(str);
+ const char *const pbeg = PARSER_STRING_PTR(str);
char *buf;
long len;
int i;
if (!yylloc) return;
- pend = RSTRING_END(str);
+ pend = rb_parser_string_end(str);
if (pend > pbeg && pend[-1] == '\n') {
if (--pend > pbeg && pend[-1] == '\r') --pend;
}
@@ -6505,11 +7506,11 @@ ruby_show_error_line(VALUE errbuf, const YYLTYPE *yylloc, int lineno, VALUE str)
len = ptr_end - ptr;
if (len > 4) {
if (ptr > pbeg) {
- ptr = rb_enc_prev_char(pbeg, ptr, pt, rb_enc_get(str));
+ ptr = rb_enc_prev_char(pbeg, ptr, pt, rb_parser_str_get_encoding(str));
if (ptr > pbeg) pre = "...";
}
if (ptr_end < pend) {
- ptr_end = rb_enc_prev_char(pt, ptr_end, pend, rb_enc_get(str));
+ ptr_end = rb_enc_prev_char(pt, ptr_end, pend, rb_parser_str_get_encoding(str));
if (ptr_end < pend) post = "...";
}
}
@@ -6528,7 +7529,7 @@ ruby_show_error_line(VALUE errbuf, const YYLTYPE *yylloc, int lineno, VALUE str)
rb_str_cat_cstr(mesg, "\n");
}
else {
- mesg = rb_enc_str_new(0, 0, rb_enc_get(str));
+ mesg = rb_enc_str_new(0, 0, rb_parser_str_get_encoding(str));
}
if (!errbuf && rb_stderr_tty_p()) {
#define CSI_BEGIN "\033["
@@ -6574,6 +7575,7 @@ ruby_show_error_line(VALUE errbuf, const YYLTYPE *yylloc, int lineno, VALUE str)
if (!errbuf) rb_write_error_str(mesg);
}
#else
+
static int
parser_yyerror(struct parser_params *p, const YYLTYPE *yylloc, const char *msg)
{
@@ -6607,7 +7609,6 @@ parser_show_error_line(struct parser_params *p, const YYLTYPE *yylloc)
}
#endif /* !RIPPER */
-#ifndef RIPPER
static int
vtable_size(const struct vtable *tbl)
{
@@ -6618,7 +7619,6 @@ vtable_size(const struct vtable *tbl)
return 0;
}
}
-#endif
static struct vtable *
vtable_alloc_gen(struct parser_params *p, int line, struct vtable *prev)
@@ -6677,7 +7677,6 @@ vtable_add_gen(struct parser_params *p, int line, const char *name,
}
#define vtable_add(tbl, id) vtable_add_gen(p, __LINE__, #tbl, tbl, id)
-#ifndef RIPPER
static void
vtable_pop_gen(struct parser_params *p, int line, const char *name,
struct vtable *tbl, int n)
@@ -6693,7 +7692,6 @@ vtable_pop_gen(struct parser_params *p, int line, const char *name,
tbl->pos -= n;
}
#define vtable_pop(tbl, n) vtable_pop_gen(p, __LINE__, #tbl, tbl, n)
-#endif
static int
vtable_included(const struct vtable * tbl, ID id)
@@ -6712,60 +7710,29 @@ vtable_included(const struct vtable * tbl, ID id)
static void parser_prepare(struct parser_params *p);
-#ifndef RIPPER
-static NODE *parser_append_options(struct parser_params *p, NODE *node);
-
-static VALUE
-debug_lines(VALUE fname)
-{
- ID script_lines;
- CONST_ID(script_lines, "SCRIPT_LINES__");
- if (rb_const_defined_at(rb_cObject, script_lines)) {
- VALUE hash = rb_const_get_at(rb_cObject, script_lines);
- if (RB_TYPE_P(hash, T_HASH)) {
- VALUE lines = rb_ary_new();
- rb_hash_aset(hash, fname, lines);
- return lines;
- }
- }
- return 0;
-}
-
static int
e_option_supplied(struct parser_params *p)
{
return strcmp(p->ruby_sourcefile, "-e") == 0;
}
+#ifndef RIPPER
+static NODE *parser_append_options(struct parser_params *p, NODE *node);
+
static VALUE
yycompile0(VALUE arg)
{
int n;
NODE *tree;
struct parser_params *p = (struct parser_params *)arg;
- VALUE cov = Qfalse;
-
- if (!compile_for_eval && !NIL_P(p->ruby_sourcefile_string)) {
- p->debug_lines = debug_lines(p->ruby_sourcefile_string);
- if (p->debug_lines && p->ruby_sourceline > 0) {
- VALUE str = rb_default_rs;
- n = p->ruby_sourceline;
- do {
- rb_ary_push(p->debug_lines, str);
- } while (--n);
- }
+ int cov = FALSE;
- if (!e_option_supplied(p)) {
- cov = Qtrue;
- }
+ if (!compile_for_eval && p->ruby_sourcefile_string && !e_option_supplied(p)) {
+ cov = TRUE;
}
- if (p->keep_script_lines || ruby_vm_keep_script_lines) {
- if (!p->debug_lines) {
- p->debug_lines = rb_ary_new();
- }
-
- RB_OBJ_WRITE(p->ast, &p->ast->body.script_lines, p->debug_lines);
+ if (p->debug_lines) {
+ p->ast->body.script_lines = p->debug_lines;
}
parser_prepare(p);
@@ -6776,14 +7743,16 @@ yycompile0(VALUE arg)
RUBY_DTRACE_PARSE_HOOK(BEGIN);
n = yyparse(p);
RUBY_DTRACE_PARSE_HOOK(END);
+
p->debug_lines = 0;
+ xfree(p->lex.strterm);
p->lex.strterm = 0;
p->lex.pcur = p->lex.pbeg = p->lex.pend = 0;
if (n || p->error_p) {
VALUE mesg = p->error_buffer;
if (!mesg) {
- mesg = rb_class_new_instance(0, 0, rb_eSyntaxError);
+ mesg = syntax_error_new();
}
if (!p->error_tolerant) {
rb_set_errinfo(mesg);
@@ -6795,45 +7764,64 @@ yycompile0(VALUE arg)
tree = NEW_NIL(&NULL_LOC);
}
else {
- VALUE opt = p->compile_option;
- VALUE tokens = p->tokens;
+ rb_parser_ary_t *tokens = p->tokens;
NODE *prelude;
- NODE *body = parser_append_options(p, tree->nd_body);
- if (!opt) opt = rb_obj_hide(rb_ident_hash_new());
- rb_hash_aset(opt, rb_sym_intern_ascii_cstr("coverage_enabled"), cov);
+ NODE *body = parser_append_options(p, RNODE_SCOPE(tree)->nd_body);
prelude = block_append(p, p->eval_tree_begin, body);
- tree->nd_body = prelude;
- RB_OBJ_WRITE(p->ast, &p->ast->body.compile_option, opt);
+ RNODE_SCOPE(tree)->nd_body = prelude;
+ p->ast->body.frozen_string_literal = p->frozen_string_literal;
+ p->ast->body.coverage_enabled = cov;
if (p->keep_tokens) {
- rb_obj_freeze(tokens);
- rb_ast_set_tokens(p->ast, tokens);
+ p->ast->node_buffer->tokens = tokens;
+ p->tokens = NULL;
}
}
p->ast->body.root = tree;
- if (!p->ast->body.script_lines) p->ast->body.script_lines = INT2FIX(p->line_count);
+ p->ast->body.line_count = p->line_count;
return TRUE;
}
+static void
+set_arg_error(struct parser_params *p, const char *err)
+{
+ VALUE excargs[3];
+
+ excargs[0] = rb_eArgError;
+ excargs[1] = rb_str_new_cstr(err);
+ excargs[2] = rb_make_backtrace();
+ rb_set_errinfo(rb_make_exception(3, excargs));
+}
+
static rb_ast_t *
-yycompile(VALUE vparser, struct parser_params *p, VALUE fname, int line)
+yycompile(struct parser_params *p, const char *fname_ptr, long fname_len, rb_encoding *fname_enc, int line)
{
rb_ast_t *ast;
- if (NIL_P(fname)) {
- p->ruby_sourcefile_string = Qnil;
+ if (!fname_ptr) {
+ p->ruby_sourcefile_string = NULL;
p->ruby_sourcefile = "(none)";
}
else {
- p->ruby_sourcefile_string = rb_fstring(fname);
- p->ruby_sourcefile = StringValueCStr(fname);
+ int w;
+ if (cstr_null_check(p, fname_ptr, fname_len, fname_enc, &w)) {
+ if (w) {
+ set_arg_error(p, "string contains null char");
+ }
+ else {
+ set_arg_error(p, "string contains null byte");
+ }
+ return rb_ast_new();
+ }
+
+ p->ruby_sourcefile_string = rb_parser_encoding_string_new(p, fname_ptr, fname_len, fname_enc);
+ p->ruby_sourcefile = fname_ptr;
}
p->ruby_sourceline = line - 1;
p->lvtbl = NULL;
p->ast = ast = rb_ast_new();
- rb_suppress_tracing(yycompile0, (VALUE)p);
+ compile_callback(yycompile0, (VALUE)p);
p->ast = 0;
- RB_GC_GUARD(vparser); /* prohibit tail call optimization */
while (p->lvtbl) {
local_pop(p);
@@ -6844,7 +7832,7 @@ yycompile(VALUE vparser, struct parser_params *p, VALUE fname, int line)
#endif /* !RIPPER */
static rb_encoding *
-must_be_ascii_compatible(VALUE s)
+must_be_ascii_compatible(struct parser_params *p, VALUE s)
{
rb_encoding *enc = rb_enc_get(s);
if (!rb_enc_asciicompat(enc)) {
@@ -6853,109 +7841,29 @@ must_be_ascii_compatible(VALUE s)
return enc;
}
-static VALUE
-lex_get_str(struct parser_params *p, VALUE s)
-{
- char *beg, *end, *start;
- long len;
-
- beg = RSTRING_PTR(s);
- len = RSTRING_LEN(s);
- start = beg;
- if (p->lex.gets_.ptr) {
- if (len == p->lex.gets_.ptr) return Qnil;
- beg += p->lex.gets_.ptr;
- len -= p->lex.gets_.ptr;
- }
- end = memchr(beg, '\n', len);
- if (end) len = ++end - beg;
- p->lex.gets_.ptr += len;
- return rb_str_subseq(s, beg - start, len);
-}
-
-static VALUE
+static rb_parser_string_t *
lex_getline(struct parser_params *p)
{
- VALUE line = (*p->lex.gets)(p, p->lex.input);
- if (NIL_P(line)) return line;
- must_be_ascii_compatible(line);
- if (RB_OBJ_FROZEN(line)) line = rb_str_dup(line); // needed for RubyVM::AST.of because script_lines in iseq is deep-frozen
+ rb_parser_string_t *str;
+ VALUE line = (*p->lex.gets)(p, p->lex.input, p->line_count);
+ if (NIL_P(line)) return 0;
+ must_be_ascii_compatible(p, line);
p->line_count++;
- return line;
+ str = rb_str_to_parser_string(p, line);
+ string_buffer_append(p, str);
+ return str;
}
-static const rb_data_type_t parser_data_type;
-
#ifndef RIPPER
-static rb_ast_t*
-parser_compile_string(VALUE vparser, VALUE fname, VALUE s, int line)
-{
- struct parser_params *p;
-
- TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
-
- p->lex.gets = lex_get_str;
- p->lex.gets_.ptr = 0;
- p->lex.input = rb_str_new_frozen(s);
- p->lex.pbeg = p->lex.pcur = p->lex.pend = 0;
-
- return yycompile(vparser, p, fname, line);
-}
-
-rb_ast_t*
-rb_parser_compile_string(VALUE vparser, const char *f, VALUE s, int line)
-{
- return rb_parser_compile_string_path(vparser, rb_filesystem_str_new_cstr(f), s, line);
-}
-
rb_ast_t*
-rb_parser_compile_string_path(VALUE vparser, VALUE f, VALUE s, int line)
+rb_parser_compile(rb_parser_t *p, rb_parser_lex_gets_func *gets,
+ const char *fname_ptr, long fname_len, rb_encoding *fname_enc, rb_parser_input_data input, int line)
{
- must_be_ascii_compatible(s);
- return parser_compile_string(vparser, f, s, line);
-}
-
-VALUE rb_io_gets_internal(VALUE io);
-
-static VALUE
-lex_io_gets(struct parser_params *p, VALUE io)
-{
- return rb_io_gets_internal(io);
-}
-
-rb_ast_t*
-rb_parser_compile_file_path(VALUE vparser, VALUE fname, VALUE file, int start)
-{
- struct parser_params *p;
-
- TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
-
- p->lex.gets = lex_io_gets;
- p->lex.input = file;
- p->lex.pbeg = p->lex.pcur = p->lex.pend = 0;
-
- return yycompile(vparser, p, fname, start);
-}
-
-static VALUE
-lex_generic_gets(struct parser_params *p, VALUE input)
-{
- return (*p->lex.gets_.call)(input, p->line_count);
-}
-
-rb_ast_t*
-rb_parser_compile_generic(VALUE vparser, VALUE (*lex_gets)(VALUE, int), VALUE fname, VALUE input, int start)
-{
- struct parser_params *p;
-
- TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
-
- p->lex.gets = lex_generic_gets;
- p->lex.gets_.call = lex_gets;
+ p->lex.gets = gets;
p->lex.input = input;
p->lex.pbeg = p->lex.pcur = p->lex.pend = 0;
- return yycompile(vparser, p, fname, start);
+ return yycompile(p, fname_ptr, fname_len, fname_enc, line);
}
#endif /* !RIPPER */
@@ -6982,7 +7890,7 @@ enum string_type {
};
static VALUE
-parser_str_new(const char *ptr, long len, rb_encoding *enc, int func, rb_encoding *enc0)
+parser_str_new(struct parser_params *p, const char *ptr, long len, rb_encoding *enc, int func, rb_encoding *enc0)
{
VALUE str;
@@ -6990,7 +7898,7 @@ parser_str_new(const char *ptr, long len, rb_encoding *enc, int func, rb_encodin
if (!(func & STR_FUNC_REGEXP) && rb_enc_asciicompat(enc)) {
if (is_ascii_string(str)) {
}
- else if (rb_is_usascii_enc(enc0) && enc != rb_utf8_encoding()) {
+ else if (rb_is_usascii_enc((void *)enc0) && enc != rb_utf8_encoding()) {
rb_enc_associate(str, rb_ascii8bit_encoding());
}
}
@@ -6998,6 +7906,30 @@ parser_str_new(const char *ptr, long len, rb_encoding *enc, int func, rb_encodin
return str;
}
+static int
+strterm_is_heredoc(rb_strterm_t *strterm)
+{
+ return strterm->heredoc;
+}
+
+static rb_strterm_t *
+new_strterm(struct parser_params *p, int func, int term, int paren)
+{
+ rb_strterm_t *strterm = ZALLOC(rb_strterm_t);
+ strterm->u.literal.func = func;
+ strterm->u.literal.term = term;
+ strterm->u.literal.paren = paren;
+ return strterm;
+}
+
+static rb_strterm_t *
+new_heredoc(struct parser_params *p)
+{
+ rb_strterm_t *strterm = ZALLOC(rb_strterm_t);
+ strterm->heredoc = true;
+ return strterm;
+}
+
#define peek(p,c) peek_n(p, (c), 0)
#define peek_n(p,c,n) (!lex_eol_n_p(p, n) && (c) == (unsigned char)(p)->lex.pcur[n])
#define peekc(p) peekc_n(p, 0)
@@ -7011,13 +7943,21 @@ add_delayed_token(struct parser_params *p, const char *tok, const char *end, int
#endif
if (tok < end) {
+ if (has_delayed_token(p)) {
+ bool next_line = parser_string_end_with_newline_p(p, p->delayed.token);
+ int end_line = (next_line ? 1 : 0) + p->delayed.end_line;
+ int end_col = (next_line ? 0 : p->delayed.end_col);
+ if (end_line != p->ruby_sourceline || end_col != tok - p->lex.pbeg) {
+ dispatch_delayed_token(p, tSTRING_CONTENT);
+ }
+ }
if (!has_delayed_token(p)) {
- p->delayed.token = rb_str_buf_new(end - tok);
- rb_enc_associate(p->delayed.token, p->enc);
+ p->delayed.token = rb_parser_string_new(p, 0, 0);
+ rb_parser_enc_associate(p, p->delayed.token, p->enc);
p->delayed.beg_line = p->ruby_sourceline;
p->delayed.beg_col = rb_long2int(tok - p->lex.pbeg);
}
- rb_str_buf_cat(p->delayed.token, tok, end - tok);
+ rb_parser_str_buf_cat(p, p->delayed.token, tok, end - tok);
p->delayed.end_line = p->ruby_sourceline;
p->delayed.end_col = rb_long2int(end - p->lex.pbeg);
p->lex.ptok = end;
@@ -7025,27 +7965,27 @@ add_delayed_token(struct parser_params *p, const char *tok, const char *end, int
}
static void
-set_lastline(struct parser_params *p, VALUE v)
+set_lastline(struct parser_params *p, rb_parser_string_t *str)
{
- p->lex.pbeg = p->lex.pcur = RSTRING_PTR(v);
- p->lex.pend = p->lex.pcur + RSTRING_LEN(v);
- p->lex.lastline = v;
+ p->lex.pbeg = p->lex.pcur = PARSER_STRING_PTR(str);
+ p->lex.pend = p->lex.pcur + PARSER_STRING_LEN(str);
+ p->lex.lastline = str;
}
static int
nextline(struct parser_params *p, int set_encoding)
{
- VALUE v = p->lex.nextline;
+ rb_parser_string_t *str = p->lex.nextline;
p->lex.nextline = 0;
- if (!v) {
+ if (!str) {
if (p->eofp)
return -1;
- if (p->lex.pend > p->lex.pbeg && *(p->lex.pend-1) != '\n') {
+ if (!lex_eol_ptr_p(p, p->lex.pbeg) && *(p->lex.pend-1) != '\n') {
goto end_of_input;
}
- if (!p->lex.input || NIL_P(v = lex_getline(p))) {
+ if (!p->lex.input || !(str = lex_getline(p))) {
end_of_input:
p->eofp = 1;
lex_goto_eol(p);
@@ -7053,13 +7993,14 @@ nextline(struct parser_params *p, int set_encoding)
}
#ifndef RIPPER
if (p->debug_lines) {
- if (set_encoding) rb_enc_associate(v, p->enc);
- rb_ary_push(p->debug_lines, v);
+ if (set_encoding) rb_parser_enc_associate(p, str, p->enc);
+ rb_parser_string_t *copy = rb_parser_string_deep_copy(p, str);
+ rb_parser_ary_push_script_line(p, p->debug_lines, copy);
}
#endif
p->cr_seen = FALSE;
}
- else if (NIL_P(v)) {
+ else if (str == AFTER_HEREDOC_WITHOUT_TERMINTOR) {
/* after here-document without terminator */
goto end_of_input;
}
@@ -7069,7 +8010,7 @@ nextline(struct parser_params *p, int set_encoding)
p->heredoc_end = 0;
}
p->ruby_sourceline++;
- set_lastline(p, v);
+ set_lastline(p, str);
token_flush(p);
return 0;
}
@@ -7089,7 +8030,7 @@ nextc0(struct parser_params *p, int set_encoding)
{
int c;
- if (UNLIKELY((p->lex.pcur == p->lex.pend) || p->eofp || RTEST(p->lex.nextline))) {
+ if (UNLIKELY(lex_eol_p(p) || p->eofp || p->lex.nextline > AFTER_HEREDOC_WITHOUT_TERMINTOR)) {
if (nextline(p, set_encoding)) return -1;
}
c = (unsigned char)*p->lex.pcur++;
@@ -7105,6 +8046,7 @@ static void
pushback(struct parser_params *p, int c)
{
if (c == -1) return;
+ p->eofp = 0;
p->lex.pcur--;
if (p->lex.pcur > p->lex.pbeg && p->lex.pcur[0] == '\n' && p->lex.pcur[-1] == '\r') {
p->lex.pcur--;
@@ -7121,7 +8063,7 @@ static int
looking_at_eol_p(struct parser_params *p)
{
const char *ptr = p->lex.pcur;
- while (ptr < p->lex.pend) {
+ while (!lex_eol_ptr_p(p, ptr)) {
int c = (unsigned char)*ptr++;
int eol = (c == '\n' || c == '#');
if (eol || !ISSPACE(c)) {
@@ -7135,7 +8077,6 @@ static char*
newtok(struct parser_params *p)
{
p->tokidx = 0;
- p->tokline = p->ruby_sourceline;
if (!p->tokenbuf) {
p->toksiz = 60;
p->tokenbuf = ALLOC_N(char, 60);
@@ -7174,10 +8115,10 @@ tok_hex(struct parser_params *p, size_t *numlen)
{
int c;
- c = scan_hex(p->lex.pcur, 2, numlen);
+ c = (int)ruby_scan_hex(p->lex.pcur, 2, numlen);
if (!*numlen) {
yyerror0("invalid hex escape");
- token_flush(p);
+ dispatch_scan_event(p, tSTRING_CONTENT);
return 0;
}
p->lex.pcur += *numlen;
@@ -7214,18 +8155,18 @@ escaped_control_code(int c)
}
#define WARN_SPACE_CHAR(c, prefix) \
- rb_warn1("invalid character syntax; use "prefix"\\%c", WARN_I(c2))
+ rb_warn1("invalid character syntax; use "prefix"\\%c", WARN_I(c))
static int
tokadd_codepoint(struct parser_params *p, rb_encoding **encp,
int regexp_literal, int wide)
{
size_t numlen;
- int codepoint = scan_hex(p->lex.pcur, wide ? p->lex.pend - p->lex.pcur : 4, &numlen);
+ int codepoint = (int)ruby_scan_hex(p->lex.pcur, wide ? p->lex.pend - p->lex.pcur : 4, &numlen);
p->lex.pcur += numlen;
if (p->lex.strterm == NULL ||
- (p->lex.strterm->flags & STRTERM_HEREDOC) ||
- (p->lex.strterm->u.literal.u1.func != str_regexp)) {
+ strterm_is_heredoc(p->lex.strterm) ||
+ (p->lex.strterm->u.literal.func != str_regexp)) {
if (wide ? (numlen == 0 || numlen > 6) : (numlen < 4)) {
literal_flush(p, p->lex.pcur);
yyerror0("invalid Unicode escape");
@@ -7262,6 +8203,18 @@ tokadd_codepoint(struct parser_params *p, rb_encoding **encp,
return TRUE;
}
+static int tokadd_mbchar(struct parser_params *p, int c);
+
+static int
+tokskip_mbchar(struct parser_params *p)
+{
+ int len = parser_precise_mbclen(p, p->lex.pcur-1);
+ if (len > 0) {
+ p->lex.pcur += len - 1;
+ }
+ return len;
+}
+
/* return value is for ?\u3042 */
static void
tokadd_utf8(struct parser_params *p, rb_encoding **encp,
@@ -7279,44 +8232,71 @@ tokadd_utf8(struct parser_params *p, rb_encoding **encp,
if (regexp_literal) { tokadd(p, '\\'); tokadd(p, 'u'); }
if (peek(p, open_brace)) { /* handle \u{...} form */
- const char *second = NULL;
- int c, last = nextc(p);
- if (p->lex.pcur >= p->lex.pend) goto unterminated;
- while (ISSPACE(c = *p->lex.pcur) && ++p->lex.pcur < p->lex.pend);
- while (c != close_brace) {
- if (c == term) goto unterminated;
- if (second == multiple_codepoints)
- second = p->lex.pcur;
- if (regexp_literal) tokadd(p, last);
- if (!tokadd_codepoint(p, encp, regexp_literal, TRUE)) {
- break;
- }
- while (ISSPACE(c = *p->lex.pcur)) {
- if (++p->lex.pcur >= p->lex.pend) goto unterminated;
- last = c;
+ if (regexp_literal && p->lex.strterm->u.literal.func == str_regexp) {
+ /*
+ * Skip parsing validation code and copy bytes as-is until term or
+ * closing brace, in order to correctly handle extended regexps where
+ * invalid unicode escapes are allowed in comments. The regexp parser
+ * does its own validation and will catch any issues.
+ */
+ tokadd(p, open_brace);
+ while (!lex_eol_ptr_p(p, ++p->lex.pcur)) {
+ int c = peekc(p);
+ if (c == close_brace) {
+ tokadd(p, c);
+ ++p->lex.pcur;
+ break;
+ }
+ else if (c == term) {
+ break;
+ }
+ if (c == '\\' && !lex_eol_n_p(p, 1)) {
+ tokadd(p, c);
+ c = *++p->lex.pcur;
+ }
+ tokadd_mbchar(p, c);
}
- if (term == -1 && !second)
- second = multiple_codepoints;
}
+ else {
+ const char *second = NULL;
+ int c, last = nextc(p);
+ if (lex_eol_p(p)) goto unterminated;
+ while (ISSPACE(c = peekc(p)) && !lex_eol_ptr_p(p, ++p->lex.pcur));
+ while (c != close_brace) {
+ if (c == term) goto unterminated;
+ if (second == multiple_codepoints)
+ second = p->lex.pcur;
+ if (regexp_literal) tokadd(p, last);
+ if (!tokadd_codepoint(p, encp, regexp_literal, TRUE)) {
+ break;
+ }
+ while (ISSPACE(c = peekc(p))) {
+ if (lex_eol_ptr_p(p, ++p->lex.pcur)) goto unterminated;
+ last = c;
+ }
+ if (term == -1 && !second)
+ second = multiple_codepoints;
+ }
- if (c != close_brace) {
- unterminated:
- token_flush(p);
- yyerror0("unterminated Unicode escape");
- return;
- }
- if (second && second != multiple_codepoints) {
- const char *pcur = p->lex.pcur;
- p->lex.pcur = second;
- dispatch_scan_event(p, tSTRING_CONTENT);
- token_flush(p);
- p->lex.pcur = pcur;
- yyerror0(multiple_codepoints);
- token_flush(p);
- }
+ if (c != close_brace) {
+ unterminated:
+ token_flush(p);
+ yyerror0("unterminated Unicode escape");
+ return;
+ }
+ if (second && second != multiple_codepoints) {
+ const char *pcur = p->lex.pcur;
+ p->lex.pcur = second;
+ dispatch_scan_event(p, tSTRING_CONTENT);
+ token_flush(p);
+ p->lex.pcur = pcur;
+ yyerror0(multiple_codepoints);
+ token_flush(p);
+ }
- if (regexp_literal) tokadd(p, close_brace);
- nextc(p);
+ if (regexp_literal) tokadd(p, close_brace);
+ nextc(p);
+ }
}
else { /* handle \uxxxx form */
if (!tokadd_codepoint(p, encp, regexp_literal, FALSE)) {
@@ -7330,7 +8310,7 @@ tokadd_utf8(struct parser_params *p, rb_encoding **encp,
#define ESCAPE_META 2
static int
-read_escape(struct parser_params *p, int flags, rb_encoding **encp)
+read_escape(struct parser_params *p, int flags)
{
int c;
size_t numlen;
@@ -7363,7 +8343,7 @@ read_escape(struct parser_params *p, int flags, rb_encoding **encp)
case '0': case '1': case '2': case '3': /* octal constant */
case '4': case '5': case '6': case '7':
pushback(p, c);
- c = scan_oct(p->lex.pcur, 3, &numlen);
+ c = (int)ruby_scan_oct(p->lex.pcur, 3, &numlen);
p->lex.pcur += numlen;
return c;
@@ -7389,7 +8369,7 @@ read_escape(struct parser_params *p, int flags, rb_encoding **encp)
nextc(p);
goto eof;
}
- return read_escape(p, flags|ESCAPE_META, encp) | 0x80;
+ return read_escape(p, flags|ESCAPE_META) | 0x80;
}
else if (c == -1 || !ISASCII(c)) goto eof;
else {
@@ -7418,11 +8398,15 @@ read_escape(struct parser_params *p, int flags, rb_encoding **encp)
nextc(p);
goto eof;
}
- c = read_escape(p, flags|ESCAPE_CONTROL, encp);
+ c = read_escape(p, flags|ESCAPE_CONTROL);
}
else if (c == '?')
return 0177;
- else if (c == -1 || !ISASCII(c)) goto eof;
+ else if (c == -1) goto eof;
+ else if (!ISASCII(c)) {
+ tokskip_mbchar(p);
+ goto eof;
+ }
else {
int c2 = escaped_control_code(c);
if (c2) {
@@ -7450,7 +8434,7 @@ read_escape(struct parser_params *p, int flags, rb_encoding **encp)
eof:
case -1:
yyerror0("Invalid escape character syntax");
- token_flush(p);
+ dispatch_scan_event(p, tSTRING_CONTENT);
return '\0';
default:
@@ -7466,7 +8450,7 @@ tokaddmbc(struct parser_params *p, int c, rb_encoding *enc)
}
static int
-tokadd_escape(struct parser_params *p, rb_encoding **encp)
+tokadd_escape(struct parser_params *p)
{
int c;
size_t numlen;
@@ -7507,6 +8491,61 @@ tokadd_escape(struct parser_params *p, rb_encoding **encp)
}
static int
+char_to_option(int c)
+{
+ int val;
+
+ switch (c) {
+ case 'i':
+ val = RE_ONIG_OPTION_IGNORECASE;
+ break;
+ case 'x':
+ val = RE_ONIG_OPTION_EXTEND;
+ break;
+ case 'm':
+ val = RE_ONIG_OPTION_MULTILINE;
+ break;
+ default:
+ val = 0;
+ break;
+ }
+ return val;
+}
+
+#define ARG_ENCODING_FIXED 16
+#define ARG_ENCODING_NONE 32
+#define ENC_ASCII8BIT 1
+#define ENC_EUC_JP 2
+#define ENC_Windows_31J 3
+#define ENC_UTF8 4
+
+static int
+char_to_option_kcode(int c, int *option, int *kcode)
+{
+ *option = 0;
+
+ switch (c) {
+ case 'n':
+ *kcode = ENC_ASCII8BIT;
+ return (*option = ARG_ENCODING_NONE);
+ case 'e':
+ *kcode = ENC_EUC_JP;
+ break;
+ case 's':
+ *kcode = ENC_Windows_31J;
+ break;
+ case 'u':
+ *kcode = ENC_UTF8;
+ break;
+ default:
+ *kcode = -1;
+ return (*option = char_to_option(c));
+ }
+ *option = ARG_ENCODING_FIXED;
+ return 1;
+}
+
+static int
regx_options(struct parser_params *p)
{
int kcode = 0;
@@ -7519,9 +8558,9 @@ regx_options(struct parser_params *p)
if (c == 'o') {
options |= RE_OPTION_ONCE;
}
- else if (rb_char_to_option_kcode(c, &opt, &kc)) {
+ else if (char_to_option_kcode(c, &opt, &kc)) {
if (kc >= 0) {
- if (kc != rb_ascii8bit_encindex()) kcode = c;
+ if (kc != ENC_ASCII8BIT) kcode = c;
kopt = opt;
}
else {
@@ -7612,6 +8651,13 @@ parser_mixed_escape(struct parser_params *p, const char *beg, rb_encoding *enc1,
p->lex.pcur = pos;
}
+static inline char
+nibble_char_upper(unsigned int c)
+{
+ c &= 0xf;
+ return c + (c < 10 ? '0' : 'A' - 10);
+}
+
static int
tokadd_string(struct parser_params *p,
int func, int term, int paren, long *nest,
@@ -7650,8 +8696,8 @@ tokadd_string(struct parser_params *p,
}
--*nest;
}
- else if ((func & STR_FUNC_EXPAND) && c == '#' && p->lex.pcur < p->lex.pend) {
- int c2 = *p->lex.pcur;
+ else if ((func & STR_FUNC_EXPAND) && c == '#' && !lex_eol_p(p)) {
+ unsigned char c2 = *p->lex.pcur;
if (c2 == '$' || c2 == '@' || c2 == '{') {
pushback(p, c);
break;
@@ -7699,14 +8745,13 @@ tokadd_string(struct parser_params *p,
case 'C':
case 'M': {
pushback(p, c);
- c = read_escape(p, 0, enc);
+ c = read_escape(p, 0);
- int i;
- char escbuf[5];
- snprintf(escbuf, sizeof(escbuf), "\\x%02X", c);
- for (i = 0; i < 4; i++) {
- tokadd(p, escbuf[i]);
- }
+ char *t = tokspace(p, rb_strlen_lit("\\x00"));
+ *t++ = '\\';
+ *t++ = 'x';
+ *t++ = nibble_char_upper(c >> 4);
+ *t++ = nibble_char_upper(c);
continue;
}
}
@@ -7716,7 +8761,7 @@ tokadd_string(struct parser_params *p,
continue;
}
pushback(p, c);
- if ((c = tokadd_escape(p, enc)) < 0)
+ if ((c = tokadd_escape(p)) < 0)
return -1;
if (*enc && *enc != *encp) {
mixed_escape(p->lex.ptok+2, *enc, *encp);
@@ -7726,7 +8771,7 @@ tokadd_string(struct parser_params *p,
else if (func & STR_FUNC_EXPAND) {
pushback(p, c);
if (func & STR_FUNC_ESCAPE) tokadd(p, '\\');
- c = read_escape(p, 0, enc);
+ c = read_escape(p, 0);
}
else if ((func & STR_FUNC_QWORDS) && ISSPACE(c)) {
/* ignore backslashed spaces in %w */
@@ -7773,45 +8818,15 @@ tokadd_string(struct parser_params *p,
return c;
}
-static inline rb_strterm_t *
-new_strterm(VALUE v1, VALUE v2, VALUE v3, VALUE v0)
-{
- return (rb_strterm_t*)rb_imemo_new(imemo_parser_strterm, v1, v2, v3, v0);
-}
-
-/* imemo_parser_strterm for literal */
-#define NEW_STRTERM(func, term, paren) \
- new_strterm((VALUE)(func), (VALUE)(paren), (VALUE)(term), 0)
+#define NEW_STRTERM(func, term, paren) new_strterm(p, func, term, paren)
-#ifdef RIPPER
static void
flush_string_content(struct parser_params *p, rb_encoding *enc)
{
- VALUE content = yylval.val;
- if (!ripper_is_node_yylval(content))
- content = ripper_new_yylval(p, 0, 0, content);
if (has_delayed_token(p)) {
ptrdiff_t len = p->lex.pcur - p->lex.ptok;
if (len > 0) {
- rb_enc_str_buf_cat(p->delayed.token, p->lex.ptok, len, enc);
- }
- dispatch_delayed_token(p, tSTRING_CONTENT);
- p->lex.ptok = p->lex.pcur;
- RNODE(content)->nd_rval = yylval.val;
- }
- dispatch_scan_event(p, tSTRING_CONTENT);
- if (yylval.val != content)
- RNODE(content)->nd_rval = yylval.val;
- yylval.val = content;
-}
-#else
-static void
-flush_string_content(struct parser_params *p, rb_encoding *enc)
-{
- if (has_delayed_token(p)) {
- ptrdiff_t len = p->lex.pcur - p->lex.ptok;
- if (len > 0) {
- rb_enc_str_buf_cat(p->delayed.token, p->lex.ptok, len, enc);
+ rb_parser_enc_str_buf_cat(p, p->delayed.token, p->lex.ptok, len, enc);
p->delayed.end_line = p->ruby_sourceline;
p->delayed.end_col = rb_long2int(p->lex.pcur - p->lex.pbeg);
}
@@ -7820,9 +8835,8 @@ flush_string_content(struct parser_params *p, rb_encoding *enc)
}
dispatch_scan_event(p, tSTRING_CONTENT);
}
-#endif
-RUBY_FUNC_EXPORTED const unsigned int ruby_global_name_punct_bits[(0x7e - 0x20 + 31) / 32];
+RUBY_FUNC_EXPORTED const uint_least32_t ruby_global_name_punct_bits[(0x7e - 0x20 + 31) / 32];
/* this can be shared with ripper, since it's independent from struct
* parser_params. */
#ifndef RIPPER
@@ -7834,7 +8848,7 @@ RUBY_FUNC_EXPORTED const unsigned int ruby_global_name_punct_bits[(0x7e - 0x20 +
BIT(':', idx) | BIT('<', idx) | BIT('>', idx) | BIT('\"', idx) | \
BIT('&', idx) | BIT('`', idx) | BIT('\'', idx) | BIT('+', idx) | \
BIT('0', idx))
-const unsigned int ruby_global_name_punct_bits[] = {
+const uint_least32_t ruby_global_name_punct_bits[] = {
SPECIAL_PUNCT(0),
SPECIAL_PUNCT(1),
SPECIAL_PUNCT(2),
@@ -7849,12 +8863,12 @@ parser_peek_variable_name(struct parser_params *p)
int c;
const char *ptr = p->lex.pcur;
- if (ptr + 1 >= p->lex.pend) return 0;
+ if (lex_eol_ptr_n_p(p, ptr, 1)) return 0;
c = *ptr++;
switch (c) {
case '$':
if ((c = *ptr) == '-') {
- if (++ptr >= p->lex.pend) return 0;
+ if (lex_eol_ptr_p(p, ++ptr)) return 0;
c = *ptr;
}
else if (is_global_name_punct(c) || ISDIGIT(c)) {
@@ -7863,7 +8877,7 @@ parser_peek_variable_name(struct parser_params *p)
break;
case '@':
if ((c = *ptr) == '@') {
- if (++ptr >= p->lex.pend) return 0;
+ if (lex_eol_ptr_p(p, ++ptr)) return 0;
c = *ptr;
}
break;
@@ -7892,6 +8906,7 @@ parser_peek_variable_name(struct parser_params *p)
static inline enum yytokentype
parser_string_term(struct parser_params *p, int func)
{
+ xfree(p->lex.strterm);
p->lex.strterm = 0;
if (func & STR_FUNC_REGEXP) {
set_yylval_num(regx_options(p));
@@ -7911,9 +8926,9 @@ parser_string_term(struct parser_params *p, int func)
static enum yytokentype
parse_string(struct parser_params *p, rb_strterm_literal_t *quote)
{
- int func = (int)quote->u1.func;
- int term = (int)quote->u3.term;
- int paren = (int)quote->u2.paren;
+ int func = quote->func;
+ int term = quote->term;
+ int paren = quote->paren;
int c, space = 0;
rb_encoding *enc = p->enc;
rb_encoding *base_enc = 0;
@@ -7922,6 +8937,7 @@ parse_string(struct parser_params *p, rb_strterm_literal_t *quote)
if (func & STR_FUNC_TERM) {
if (func & STR_FUNC_QWORDS) nextc(p); /* delayed term */
SET_LEX_STATE(EXPR_END);
+ xfree(p->lex.strterm);
p->lex.strterm = 0;
return func & STR_FUNC_REGEXP ? tREGEXP_END : tSTRING_END;
}
@@ -7931,12 +8947,12 @@ parse_string(struct parser_params *p, rb_strterm_literal_t *quote)
space = 1;
}
if (func & STR_FUNC_LIST) {
- quote->u1.func &= ~STR_FUNC_LIST;
+ quote->func &= ~STR_FUNC_LIST;
space = 1;
}
- if (c == term && !quote->u0.nest) {
+ if (c == term && !quote->nest) {
if (func & STR_FUNC_QWORDS) {
- quote->u1.func |= STR_FUNC_TERM;
+ quote->func |= STR_FUNC_TERM;
pushback(p, c); /* dispatch the term at tSTRING_END */
add_delayed_token(p, p->lex.ptok, p->lex.pcur, __LINE__);
return ' ';
@@ -7950,24 +8966,25 @@ parse_string(struct parser_params *p, rb_strterm_literal_t *quote)
}
newtok(p);
if ((func & STR_FUNC_EXPAND) && c == '#') {
- int t = parser_peek_variable_name(p);
+ enum yytokentype t = parser_peek_variable_name(p);
if (t) return t;
tokadd(p, '#');
c = nextc(p);
}
pushback(p, c);
- if (tokadd_string(p, func, term, paren, &quote->u0.nest,
+ if (tokadd_string(p, func, term, paren, &quote->nest,
&enc, &base_enc) == -1) {
if (p->eofp) {
#ifndef RIPPER
# define unterminated_literal(mesg) yyerror0(mesg)
#else
-# define unterminated_literal(mesg) compile_error(p, mesg)
+# define unterminated_literal(mesg) compile_error(p, mesg)
#endif
literal_flush(p, p->lex.pcur);
if (func & STR_FUNC_QWORDS) {
/* no content to add, bailing out here */
unterminated_literal("unterminated list meets end of file");
+ xfree(p->lex.strterm);
p->lex.strterm = 0;
return tSTRING_END;
}
@@ -7977,7 +8994,7 @@ parse_string(struct parser_params *p, rb_strterm_literal_t *quote)
else {
unterminated_literal("unterminated string meets end of file");
}
- quote->u1.func |= STR_FUNC_TERM;
+ quote->func |= STR_FUNC_TERM;
}
}
@@ -8058,14 +9075,14 @@ heredoc_identifier(struct parser_params *p)
dispatch_scan_event(p, tHEREDOC_BEG);
lex_goto_eol(p);
- p->lex.strterm = new_strterm(0, 0, 0, p->lex.lastline);
- p->lex.strterm->flags |= STRTERM_HEREDOC;
+ p->lex.strterm = new_heredoc(p);
rb_strterm_heredoc_t *here = &p->lex.strterm->u.heredoc;
here->offset = offset;
here->sourceline = p->ruby_sourceline;
- here->length = (int)len;
+ here->length = (unsigned)len;
here->quote = quote;
here->func = func;
+ here->lastline = p->lex.lastline;
token_flush(p);
p->heredoc_indent = indent;
@@ -8076,29 +9093,28 @@ heredoc_identifier(struct parser_params *p)
static void
heredoc_restore(struct parser_params *p, rb_strterm_heredoc_t *here)
{
- VALUE line;
+ rb_parser_string_t *line;
+ rb_strterm_t *term = p->lex.strterm;
p->lex.strterm = 0;
line = here->lastline;
p->lex.lastline = line;
- p->lex.pbeg = RSTRING_PTR(line);
- p->lex.pend = p->lex.pbeg + RSTRING_LEN(line);
+ p->lex.pbeg = PARSER_STRING_PTR(line);
+ p->lex.pend = p->lex.pbeg + PARSER_STRING_LEN(line);
p->lex.pcur = p->lex.pbeg + here->offset + here->length + here->quote;
p->lex.ptok = p->lex.pbeg + here->offset - here->quote;
p->heredoc_end = p->ruby_sourceline;
p->ruby_sourceline = (int)here->sourceline;
- if (p->eofp) p->lex.nextline = Qnil;
+ if (p->eofp) p->lex.nextline = AFTER_HEREDOC_WITHOUT_TERMINTOR;
p->eofp = 0;
+ xfree(term);
}
static int
-dedent_string(VALUE string, int width)
+dedent_string_column(const char *str, long len, int width)
{
- char *str;
- long len;
int i, col = 0;
- RSTRING_GETMEM(string, str, len);
for (i = 0; i < len && col < width; i++) {
if (str[i] == ' ') {
col++;
@@ -8112,35 +9128,50 @@ dedent_string(VALUE string, int width)
break;
}
}
+
+ return i;
+}
+
+static int
+dedent_string(struct parser_params *p, rb_parser_string_t *string, int width)
+{
+ char *str;
+ long len;
+ int i;
+
+ len = PARSER_STRING_LEN(string);
+ str = PARSER_STRING_PTR(string);
+
+ i = dedent_string_column(str, len, width);
if (!i) return 0;
- rb_str_modify(string);
- str = RSTRING_PTR(string);
- if (RSTRING_LEN(string) != len)
- rb_fatal("literal string changed: %+"PRIsVALUE, string);
+
+ rb_parser_str_modify(string);
+ str = PARSER_STRING_PTR(string);
+ if (PARSER_STRING_LEN(string) != len)
+ rb_fatal("literal string changed: %s", PARSER_STRING_PTR(string));
MEMMOVE(str, str + i, char, len - i);
- rb_str_set_len(string, len - i);
+ rb_parser_str_set_len(p, string, len - i);
return i;
}
-#ifndef RIPPER
static NODE *
heredoc_dedent(struct parser_params *p, NODE *root)
{
NODE *node, *str_node, *prev_node;
int indent = p->heredoc_indent;
- VALUE prev_lit = 0;
+ rb_parser_string_t *prev_lit = 0;
if (indent <= 0) return root;
p->heredoc_indent = 0;
if (!root) return root;
prev_node = node = str_node = root;
- if (nd_type_p(root, NODE_LIST)) str_node = root->nd_head;
+ if (nd_type_p(root, NODE_LIST)) str_node = RNODE_LIST(root)->nd_head;
while (str_node) {
- VALUE lit = str_node->nd_lit;
- if (str_node->flags & NODE_FL_NEWLINE) {
- dedent_string(lit, indent);
+ rb_parser_string_t *lit = RNODE_STR(str_node)->string;
+ if (nd_fl_newline(str_node)) {
+ dedent_string(p, lit, indent);
}
if (!prev_lit) {
prev_lit = lit;
@@ -8149,22 +9180,22 @@ heredoc_dedent(struct parser_params *p, NODE *root)
return 0;
}
else {
- NODE *end = node->nd_end;
- node = prev_node->nd_next = node->nd_next;
+ NODE *end = RNODE_LIST(node)->as.nd_end;
+ node = RNODE_LIST(prev_node)->nd_next = RNODE_LIST(node)->nd_next;
if (!node) {
if (nd_type_p(prev_node, NODE_DSTR))
nd_set_type(prev_node, NODE_STR);
break;
}
- node->nd_end = end;
+ RNODE_LIST(node)->as.nd_end = end;
goto next_str;
}
str_node = 0;
- while ((node = (prev_node = node)->nd_next) != 0) {
+ while ((nd_type_p(node, NODE_LIST) || nd_type_p(node, NODE_DSTR)) && (node = RNODE_LIST(prev_node = node)->nd_next) != 0) {
next_str:
if (!nd_type_p(node, NODE_LIST)) break;
- if ((str_node = node->nd_head) != 0) {
+ if ((str_node = RNODE_LIST(node)->nd_head) != 0) {
enum node_type type = nd_type(str_node);
if (type == NODE_STR || type == NODE_DSTR) break;
prev_lit = 0;
@@ -8174,37 +9205,16 @@ heredoc_dedent(struct parser_params *p, NODE *root)
}
return root;
}
-#else /* RIPPER */
+
+#ifdef RIPPER
static VALUE
-heredoc_dedent(struct parser_params *p, VALUE array)
+ripper_heredoc_dedent(struct parser_params *p, int indent, VALUE array)
{
- int indent = p->heredoc_indent;
-
if (indent <= 0) return array;
p->heredoc_indent = 0;
dispatch2(heredoc_dedent, array, INT2NUM(indent));
return array;
}
-
-/*
- * call-seq:
- * Ripper.dedent_string(input, width) -> Integer
- *
- * USE OF RIPPER LIBRARY ONLY.
- *
- * Strips up to +width+ leading whitespaces from +input+,
- * and returns the stripped column width.
- */
-static VALUE
-parser_dedent_string(VALUE self, VALUE input, VALUE width)
-{
- int wid, col;
-
- StringValue(input);
- wid = NUM2UINT(width);
- col = dedent_string(input, wid);
- return INT2NUM(col);
-}
#endif
static int
@@ -8229,7 +9239,7 @@ static int
word_match_p(struct parser_params *p, const char *word, long len)
{
if (strncmp(p->lex.pcur, word, len)) return 0;
- if (p->lex.pcur + len == p->lex.pend) return 1;
+ if (lex_eol_n_p(p, len)) return 1;
int c = (unsigned char)p->lex.pcur[len];
if (ISSPACE(c)) return 1;
switch (c) {
@@ -8273,27 +9283,41 @@ number_literal_suffix(struct parser_params *p, int mask)
}
static enum yytokentype
-set_number_literal(struct parser_params *p, VALUE v,
- enum yytokentype type, int suffix)
+set_number_literal(struct parser_params *p, enum yytokentype type, int suffix, int base, int seen_point)
{
- if (suffix & NUM_SUFFIX_I) {
- v = rb_complex_raw(INT2FIX(0), v);
- type = tIMAGINARY;
+ enum rb_numeric_type numeric_type = integer_literal;
+
+ if (type == tFLOAT) {
+ numeric_type = float_literal;
}
- set_yylval_literal(v);
- SET_LEX_STATE(EXPR_END);
- return type;
-}
-static enum yytokentype
-set_integer_literal(struct parser_params *p, VALUE v, int suffix)
-{
- enum yytokentype type = tINTEGER;
if (suffix & NUM_SUFFIX_R) {
- v = rb_rational_raw1(v);
type = tRATIONAL;
+ numeric_type = rational_literal;
+ }
+ if (suffix & NUM_SUFFIX_I) {
+ type = tIMAGINARY;
+ }
+
+ switch (type) {
+ case tINTEGER:
+ set_yylval_node(NEW_INTEGER(strdup(tok(p)), base, &_cur_loc));
+ break;
+ case tFLOAT:
+ set_yylval_node(NEW_FLOAT(strdup(tok(p)), &_cur_loc));
+ break;
+ case tRATIONAL:
+ set_yylval_node(NEW_RATIONAL(strdup(tok(p)), base, seen_point, &_cur_loc));
+ break;
+ case tIMAGINARY:
+ set_yylval_node(NEW_IMAGINARY(strdup(tok(p)), base, seen_point, numeric_type, &_cur_loc));
+ (void)numeric_type; /* for ripper */
+ break;
+ default:
+ rb_bug("unexpected token: %d", type);
}
- return set_number_literal(p, v, type, suffix);
+ SET_LEX_STATE(EXPR_END);
+ return type;
}
#ifdef RIPPER
@@ -8319,7 +9343,7 @@ parser_dispatch_heredoc_end(struct parser_params *p, int line)
dispatch_delayed_token(p, tSTRING_CONTENT);
if (p->keep_tokens) {
- VALUE str = STR_NEW(p->lex.ptok, p->lex.pend - p->lex.ptok);
+ rb_parser_string_t *str = rb_parser_encoding_string_new(p, p->lex.ptok, p->lex.pend - p->lex.ptok, p->enc);
RUBY_SET_YYLLOC_OF_HEREDOC_END(*p->yylloc);
parser_append_tokens(p, str, tHEREDOC_END, line);
}
@@ -8340,8 +9364,11 @@ here_document(struct parser_params *p, rb_strterm_heredoc_t *here)
rb_encoding *enc = p->enc;
rb_encoding *base_enc = 0;
int bol;
+#ifdef RIPPER
+ VALUE s_value;
+#endif
- eos = RSTRING_PTR(here->lastline) + here->offset;
+ eos = PARSER_STRING_PTR(here->lastline) + here->offset;
len = here->length;
indent = (func = here->func) & STR_FUNC_INDENT;
@@ -8362,7 +9389,7 @@ here_document(struct parser_params *p, rb_strterm_heredoc_t *here)
enc = rb_ascii8bit_encoding();
}
}
- rb_enc_str_buf_cat(p->delayed.token, p->lex.ptok, len, enc);
+ rb_parser_enc_str_buf_cat(p, p->delayed.token, p->lex.ptok, len, enc);
}
dispatch_delayed_token(p, tSTRING_CONTENT);
}
@@ -8372,7 +9399,6 @@ here_document(struct parser_params *p, rb_strterm_heredoc_t *here)
compile_error(p, "can't find string \"%.*s\" anywhere before EOF",
(int)len, eos);
token_flush(p);
- p->lex.strterm = 0;
SET_LEX_STATE(EXPR_END);
return tSTRING_END;
}
@@ -8392,14 +9418,13 @@ here_document(struct parser_params *p, rb_strterm_heredoc_t *here)
restore:
heredoc_restore(p, &p->lex.strterm->u.heredoc);
token_flush(p);
- p->lex.strterm = 0;
SET_LEX_STATE(EXPR_END);
return tSTRING_END;
}
if (!(func & STR_FUNC_EXPAND)) {
do {
- ptr = RSTRING_PTR(p->lex.lastline);
+ ptr = PARSER_STRING_PTR(p->lex.lastline);
ptr_end = p->lex.pend;
if (ptr_end > ptr) {
switch (ptr_end[-1]) {
@@ -8424,7 +9449,7 @@ here_document(struct parser_params *p, rb_strterm_heredoc_t *here)
rb_str_cat(str, ptr, ptr_end - ptr);
else
str = STR_NEW(ptr, ptr_end - ptr);
- if (ptr_end < p->lex.pend) rb_str_cat(str, "\n", 1);
+ if (!lex_eol_ptr_p(p, ptr_end)) rb_str_cat(str, "\n", 1);
lex_goto_eol(p);
if (p->heredoc_indent > 0) {
goto flush_str;
@@ -8441,7 +9466,7 @@ here_document(struct parser_params *p, rb_strterm_heredoc_t *here)
/* int mb = ENC_CODERANGE_7BIT, *mbp = &mb;*/
newtok(p);
if (c == '#') {
- int t = parser_peek_variable_name(p);
+ enum yytokentype t = parser_peek_variable_name(p);
if (p->heredoc_line_indent != -1) {
if (p->heredoc_indent > p->heredoc_line_indent) {
p->heredoc_indent = p->heredoc_line_indent;
@@ -8466,7 +9491,7 @@ here_document(struct parser_params *p, rb_strterm_heredoc_t *here)
flush_str:
set_yylval_str(str);
#ifndef RIPPER
- if (bol) yylval.node->flags |= NODE_FL_NEWLINE;
+ if (bol) nd_set_fl_newline(yylval.node);
#endif
flush_string_content(p, enc);
return tSTRING_CONTENT;
@@ -8482,16 +9507,20 @@ here_document(struct parser_params *p, rb_strterm_heredoc_t *here)
str = STR_NEW3(tok(p), toklen(p), enc, func);
}
dispatch_heredoc_end(p);
-#ifdef RIPPER
- str = ripper_new_yylval(p, ripper_token2eventid(tSTRING_CONTENT),
- yylval.val, str);
-#endif
heredoc_restore(p, &p->lex.strterm->u.heredoc);
token_flush(p);
p->lex.strterm = NEW_STRTERM(func | STR_FUNC_TERM, 0, 0);
+#ifdef RIPPER
+ /* Preserve s_value for set_yylval_str */
+ s_value = p->s_value;
+#endif
set_yylval_str(str);
+#ifdef RIPPER
+ set_parser_s_value(s_value);
+#endif
+
#ifndef RIPPER
- if (bol) yylval.node->flags |= NODE_FL_NEWLINE;
+ if (bol) nd_set_fl_newline(yylval.node);
#endif
return tSTRING_CONTENT;
}
@@ -8503,10 +9532,10 @@ arg_ambiguous(struct parser_params *p, char c)
{
#ifndef RIPPER
if (c == '/') {
- rb_warning1("ambiguity between regexp and two divisions: wrap regexp in parentheses or add a space after `%c' operator", WARN_I(c));
+ rb_warning1("ambiguity between regexp and two divisions: wrap regexp in parentheses or add a space after '%c' operator", WARN_I(c));
}
else {
- rb_warning1("ambiguous first argument; put parentheses or a space even after `%c' operator", WARN_I(c));
+ rb_warning1("ambiguous first argument; put parentheses or a space even after '%c' operator", WARN_I(c));
}
#else
dispatch1(arg_ambiguous, rb_usascii_str_new(&c, 1));
@@ -8515,22 +9544,12 @@ arg_ambiguous(struct parser_params *p, char c)
}
static ID
-#ifndef RIPPER
-formal_argument(struct parser_params *p, ID lhs)
-#else
-formal_argument(struct parser_params *p, VALUE lhs)
-#endif
+formal_argument(struct parser_params *p, ID id)
{
- ID id = get_id(lhs);
-
switch (id_type(id)) {
case ID_LOCAL:
break;
-#ifndef RIPPER
-# define ERR(mesg) yyerror0(mesg)
-#else
-# define ERR(mesg) (dispatch2(param_error, WARN_S(mesg), lhs), ripper_error(p))
-#endif
+#define ERR(mesg) yyerror0(mesg)
case ID_CONST:
ERR("formal argument cannot be a constant");
return 0;
@@ -8549,9 +9568,47 @@ formal_argument(struct parser_params *p, VALUE lhs)
#undef ERR
}
shadowing_lvar(p, id);
- return lhs;
+
+/*
+ * Workaround for Prism::ParseTest#test_filepath for "unparser/corpus/literal/def.txt"
+ *
+ * See the discussion on https://github.com/ruby/ruby/pull/9923
+ */
+#ifndef RIPPER
+ return id;
+#else
+ return 0;
+#endif
}
+#ifdef RIPPER
+static void
+ripper_formal_argument(struct parser_params *p, ID id, VALUE lhs)
+{
+ switch (id_type(id)) {
+ case ID_LOCAL:
+ break;
+#define ERR(mesg) (dispatch2(param_error, WARN_S(mesg), lhs), ripper_error(p))
+ case ID_CONST:
+ ERR("formal argument cannot be a constant");
+ return;
+ case ID_INSTANCE:
+ ERR("formal argument cannot be an instance variable");
+ return;
+ case ID_GLOBAL:
+ ERR("formal argument cannot be a global variable");
+ return;
+ case ID_CLASS:
+ ERR("formal argument cannot be a class variable");
+ return;
+ default:
+ ERR("formal argument must be local variable");
+ return;
+#undef ERR
+ }
+}
+#endif
+
static int
lvar_defined(struct parser_params *p, ID id)
{
@@ -8582,17 +9639,29 @@ parser_encode_length(struct parser_params *p, const char *name, long len)
static void
parser_set_encode(struct parser_params *p, const char *name)
{
- int idx = rb_enc_find_index(name);
rb_encoding *enc;
VALUE excargs[3];
+ int idx = 0;
+ const char *wrong = 0;
+ switch (*name) {
+ case 'e': case 'E': wrong = "external"; break;
+ case 'i': case 'I': wrong = "internal"; break;
+ case 'f': case 'F': wrong = "filesystem"; break;
+ case 'l': case 'L': wrong = "locale"; break;
+ }
+ if (wrong && STRCASECMP(name, wrong) == 0) goto unknown;
+ idx = rb_enc_find_index(name);
if (idx < 0) {
+ unknown:
excargs[1] = rb_sprintf("unknown encoding name: %s", name);
error:
excargs[0] = rb_eArgError;
excargs[2] = rb_make_backtrace();
- rb_ary_unshift(excargs[2], rb_sprintf("%"PRIsVALUE":%d", p->ruby_sourcefile_string, p->ruby_sourceline));
- rb_exc_raise(rb_make_exception(3, excargs));
+ rb_ary_unshift(excargs[2], rb_sprintf("%"PRIsVALUE":%d", rb_str_new_mutable_parser_string(p->ruby_sourcefile_string), p->ruby_sourceline));
+ VALUE exc = rb_make_exception(3, excargs);
+ ruby_show_error_line(p, exc, &(YYLTYPE)RUBY_INIT_YYLLOC(), p->ruby_sourceline, p->lex.lastline);
+ rb_exc_raise(exc);
}
enc = rb_enc_from_index(idx);
if (!rb_enc_asciicompat(enc)) {
@@ -8602,25 +9671,19 @@ parser_set_encode(struct parser_params *p, const char *name)
p->enc = enc;
#ifndef RIPPER
if (p->debug_lines) {
- VALUE lines = p->debug_lines;
- long i, n = RARRAY_LEN(lines);
- for (i = 0; i < n; ++i) {
- rb_enc_associate_index(RARRAY_AREF(lines, i), idx);
+ long i;
+ for (i = 0; i < p->debug_lines->len; i++) {
+ rb_parser_enc_associate(p, p->debug_lines->data[i], enc);
}
}
#endif
}
-static int
+static bool
comment_at_top(struct parser_params *p)
{
- const char *ptr = p->lex.pbeg, *ptr_end = p->lex.pcur - 1;
- if (p->line_count != (p->has_shebang ? 2 : 1)) return 0;
- while (ptr < ptr_end) {
- if (!ISSPACE(*ptr)) return 0;
- ptr++;
- }
- return 1;
+ if (p->token_seen) return false;
+ return (p->line_count == (p->has_shebang ? 2 : 1));
}
typedef long (*rb_magic_comment_length_t)(struct parser_params *p, const char *name, long len);
@@ -8670,22 +9733,19 @@ parser_set_token_info(struct parser_params *p, const char *name, const char *val
}
static void
-parser_set_compile_option_flag(struct parser_params *p, const char *name, const char *val)
+parser_set_frozen_string_literal(struct parser_params *p, const char *name, const char *val)
{
int b;
if (p->token_seen) {
- rb_warning1("`%s' is ignored after any tokens", WARN_S(name));
+ rb_warning1("'%s' is ignored after any tokens", WARN_S(name));
return;
}
b = parser_get_bool(p, name, val);
if (b < 0) return;
- if (!p->compile_option)
- p->compile_option = rb_obj_hide(rb_ident_hash_new());
- rb_hash_aset(p->compile_option, ID2SYM(rb_intern(name)),
- RBOOL(b));
+ p->frozen_string_literal = b;
}
static void
@@ -8694,30 +9754,30 @@ parser_set_shareable_constant_value(struct parser_params *p, const char *name, c
for (const char *s = p->lex.pbeg, *e = p->lex.pcur; s < e; ++s) {
if (*s == ' ' || *s == '\t') continue;
if (*s == '#') break;
- rb_warning1("`%s' is ignored unless in comment-only line", WARN_S(name));
+ rb_warning1("'%s' is ignored unless in comment-only line", WARN_S(name));
return;
}
switch (*val) {
case 'n': case 'N':
if (STRCASECMP(val, "none") == 0) {
- p->ctxt.shareable_constant_value = shareable_none;
+ p->ctxt.shareable_constant_value = rb_parser_shareable_none;
return;
}
break;
case 'l': case 'L':
if (STRCASECMP(val, "literal") == 0) {
- p->ctxt.shareable_constant_value = shareable_literal;
+ p->ctxt.shareable_constant_value = rb_parser_shareable_literal;
return;
}
break;
case 'e': case 'E':
if (STRCASECMP(val, "experimental_copy") == 0) {
- p->ctxt.shareable_constant_value = shareable_copy;
+ p->ctxt.shareable_constant_value = rb_parser_shareable_copy;
return;
}
if (STRCASECMP(val, "experimental_everything") == 0) {
- p->ctxt.shareable_constant_value = shareable_everything;
+ p->ctxt.shareable_constant_value = rb_parser_shareable_everything;
return;
}
break;
@@ -8743,7 +9803,7 @@ struct magic_comment {
static const struct magic_comment magic_comments[] = {
{"coding", magic_comment_encoding, parser_encode_length},
{"encoding", magic_comment_encoding, parser_encode_length},
- {"frozen_string_literal", parser_set_compile_option_flag},
+ {"frozen_string_literal", parser_set_frozen_string_literal},
{"shareable_constant_value", parser_set_shareable_constant_value},
{"warn_indent", parser_set_token_info},
# if WARN_PAST_SCOPE
@@ -8837,6 +9897,7 @@ parser_magic_comment(struct parser_params *p, const char *str, long len)
do str++; while (--len > 0 && ISSPACE(*str));
if (!len) break;
+ const char *tok_beg = str;
if (*str == '"') {
for (vbeg = ++str; --len > 0 && *str != '"'; str++) {
if (*str == '\\') {
@@ -8854,6 +9915,7 @@ parser_magic_comment(struct parser_params *p, const char *str, long len)
for (vbeg = str; len > 0 && *str != '"' && *str != ';' && !ISSPACE(*str); --len, str++);
vend = str;
}
+ const char *tok_end = str;
if (indicator) {
while (len > 0 && (*str == ';' || ISSPACE(*str))) --len, str++;
}
@@ -8875,6 +9937,8 @@ parser_magic_comment(struct parser_params *p, const char *str, long len)
n = (*mc->length)(p, vbeg, n);
}
str_copy(val, vbeg, n);
+ p->lex.ptok = tok_beg;
+ p->lex.pcur = tok_end;
(*mc->func)(p, mc->name, RSTRING_PTR(val));
break;
}
@@ -8928,6 +9992,8 @@ set_file_encoding(struct parser_params *p, const char *str, const char *send)
beg = str;
while ((*str == '-' || *str == '_' || ISALNUM(*str)) && ++str < send);
s = rb_str_new(beg, parser_encode_length(p, beg, str - beg));
+ p->lex.ptok = beg;
+ p->lex.pcur = str;
parser_set_encode(p, RSTRING_PTR(s));
rb_str_resize(s, 0);
}
@@ -8942,30 +10008,31 @@ parser_prepare(struct parser_params *p)
if (peek(p, '!')) p->has_shebang = 1;
break;
case 0xef: /* UTF-8 BOM marker */
- if (p->lex.pend - p->lex.pcur >= 2 &&
+ if (!lex_eol_n_p(p, 2) &&
(unsigned char)p->lex.pcur[0] == 0xbb &&
(unsigned char)p->lex.pcur[1] == 0xbf) {
p->enc = rb_utf8_encoding();
p->lex.pcur += 2;
#ifndef RIPPER
if (p->debug_lines) {
- rb_enc_associate(p->lex.lastline, p->enc);
+ rb_parser_string_set_encoding(p->lex.lastline, p->enc);
}
#endif
p->lex.pbeg = p->lex.pcur;
+ token_flush(p);
return;
}
break;
- case EOF:
+ case -1: /* end of script. */
return;
}
pushback(p, c);
- p->enc = rb_enc_get(p->lex.lastline);
+ p->enc = rb_parser_str_get_encoding(p->lex.lastline);
}
#ifndef RIPPER
#define ambiguous_operator(tok, op, syn) ( \
- rb_warning0("`"op"' after local variable or literal is interpreted as binary operator"), \
+ rb_warning0("'"op"' after local variable or literal is interpreted as binary operator"), \
rb_warning0("even though it seems like "syn""))
#else
#define ambiguous_operator(tok, op, syn) \
@@ -8977,24 +10044,13 @@ parser_prepare(struct parser_params *p)
(ambiguous_operator(tok, op, syn), 0)), \
(enum yytokentype)(tok))
-static VALUE
-parse_rational(struct parser_params *p, char *str, int len, int seen_point)
-{
- VALUE v;
- char *point = &str[seen_point];
- size_t fraclen = len-seen_point-1;
- memmove(point, point+1, fraclen+1);
- v = rb_cstr_to_inum(str, 10, FALSE);
- return rb_rational_new(v, rb_int_positive_pow(10, fraclen));
-}
-
static enum yytokentype
no_digits(struct parser_params *p)
{
yyerror0("numeric literal without digits");
if (peek(p, '_')) nextc(p);
/* dummy 0, for tUMINUS_NUM at numeric */
- return set_integer_literal(p, INT2FIX(0), 0);
+ return set_number_literal(p, tINTEGER, 0, 10, 0);
}
static enum yytokentype
@@ -9035,7 +10091,7 @@ parse_numeric(struct parser_params *p, int c)
}
else if (nondigit) goto trailing_uc;
suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
- return set_integer_literal(p, rb_cstr_to_inum(tok(p), 16, FALSE), suffix);
+ return set_number_literal(p, tINTEGER, suffix, 16, 0);
}
if (c == 'b' || c == 'B') {
/* binary */
@@ -9059,7 +10115,7 @@ parse_numeric(struct parser_params *p, int c)
}
else if (nondigit) goto trailing_uc;
suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
- return set_integer_literal(p, rb_cstr_to_inum(tok(p), 2, FALSE), suffix);
+ return set_number_literal(p, tINTEGER, suffix, 2, 0);
}
if (c == 'd' || c == 'D') {
/* decimal */
@@ -9083,7 +10139,7 @@ parse_numeric(struct parser_params *p, int c)
}
else if (nondigit) goto trailing_uc;
suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
- return set_integer_literal(p, rb_cstr_to_inum(tok(p), 10, FALSE), suffix);
+ return set_number_literal(p, tINTEGER, suffix, 10, 0);
}
if (c == '_') {
/* 0_0 */
@@ -9093,6 +10149,7 @@ parse_numeric(struct parser_params *p, int c)
/* prefixed octal */
c = nextc(p);
if (c == -1 || c == '_' || !ISDIGIT(c)) {
+ tokfix(p);
return no_digits(p);
}
}
@@ -9115,7 +10172,7 @@ parse_numeric(struct parser_params *p, int c)
tokfix(p);
if (nondigit) goto trailing_uc;
suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
- return set_integer_literal(p, rb_cstr_to_inum(tok(p), 8, FALSE), suffix);
+ return set_number_literal(p, tINTEGER, suffix, 8, 0);
}
if (nondigit) {
pushback(p, c);
@@ -9131,8 +10188,9 @@ parse_numeric(struct parser_params *p, int c)
}
else {
pushback(p, c);
+ tokfix(p);
suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
- return set_integer_literal(p, INT2FIX(0), suffix);
+ return set_number_literal(p, tINTEGER, suffix, 10, 0);
}
}
@@ -9178,6 +10236,7 @@ parse_numeric(struct parser_params *p, int c)
c = nextc(p);
if (c != '-' && c != '+' && !ISDIGIT(c)) {
pushback(p, c);
+ c = nondigit;
nondigit = 0;
goto decode_num;
}
@@ -9205,31 +10264,28 @@ parse_numeric(struct parser_params *p, int c)
trailing_uc:
literal_flush(p, p->lex.pcur - 1);
YYLTYPE loc = RUBY_INIT_YYLLOC();
- compile_error(p, "trailing `%c' in number", nondigit);
+ compile_error(p, "trailing '%c' in number", nondigit);
parser_show_error_line(p, &loc);
}
tokfix(p);
if (is_float) {
enum yytokentype type = tFLOAT;
- VALUE v;
suffix = number_literal_suffix(p, seen_e ? NUM_SUFFIX_I : NUM_SUFFIX_ALL);
if (suffix & NUM_SUFFIX_R) {
type = tRATIONAL;
- v = parse_rational(p, tok(p), toklen(p), seen_point);
}
else {
- double d = strtod(tok(p), 0);
+ strtod(tok(p), 0);
if (errno == ERANGE) {
rb_warning1("Float %s out of range", WARN_S(tok(p)));
errno = 0;
}
- v = DBL2NUM(d);
}
- return set_number_literal(p, v, type, suffix);
+ return set_number_literal(p, type, suffix, 0, seen_point);
}
suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
- return set_integer_literal(p, rb_cstr_to_inum(tok(p), 10, FALSE), suffix);
+ return set_number_literal(p, tINTEGER, suffix, 10, 0);
}
static enum yytokentype
@@ -9266,16 +10322,16 @@ parse_qmark(struct parser_params *p, int space_seen)
if (tokadd_mbchar(p, c) == -1) return 0;
}
else if ((rb_enc_isalnum(c, p->enc) || c == '_') &&
- p->lex.pcur < p->lex.pend && is_identchar(p->lex.pcur, p->lex.pend, p->enc)) {
+ !lex_eol_p(p) && is_identchar(p, p->lex.pcur, p->lex.pend, p->enc)) {
if (space_seen) {
const char *start = p->lex.pcur - 1, *ptr = start;
do {
int n = parser_precise_mbclen(p, ptr);
if (n < 0) return -1;
ptr += n;
- } while (ptr < p->lex.pend && is_identchar(ptr, p->lex.pend, p->enc));
- rb_warn2("`?' just followed by `%.*s' is interpreted as" \
- " a conditional operator, put a space after `?'",
+ } while (!lex_eol_ptr_p(p, ptr) && is_identchar(p, ptr, p->lex.pend, p->enc));
+ rb_warn2("'?' just followed by '%.*s' is interpreted as" \
+ " a conditional operator, put a space after '?'",
WARN_I((int)(ptr - start)), WARN_S_L(start, (ptr - start)));
}
goto ternary;
@@ -9286,12 +10342,12 @@ parse_qmark(struct parser_params *p, int space_seen)
enc = rb_utf8_encoding();
tokadd_utf8(p, &enc, -1, 0, 0);
}
- else if (!lex_eol_p(p) && !(c = *p->lex.pcur, ISASCII(c))) {
+ else if (!ISASCII(c = peekc(p))) {
nextc(p);
if (tokadd_mbchar(p, c) == -1) return 0;
}
else {
- c = read_escape(p, 0, &enc);
+ c = read_escape(p, 0);
tokadd(p, c);
}
}
@@ -9438,7 +10494,7 @@ parse_numvar(struct parser_params *p)
if (overflow || n > nth_ref_max) {
/* compile_error()? */
- rb_warn1("`%s' is too big for a number variable, always nil", WARN_S(tok(p)));
+ rb_warn1("'%s' is too big for a number variable, always nil", WARN_S(tok(p)));
return 0; /* $0 is $PROGRAM_NAME, not NTH_REF */
}
else {
@@ -9467,22 +10523,22 @@ parse_gvar(struct parser_params *p, const enum lex_state_e last_state)
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 */
+ 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);
goto gvar;
@@ -9503,10 +10559,10 @@ parse_gvar(struct parser_params *p, const enum lex_state_e last_state)
set_yylval_name(TOK_INTERN());
return tGVAR;
- case '&': /* $&: last match */
- case '`': /* $`: string before last match */
- case '\'': /* $': string after last match */
- case '+': /* $+: string matches last paren. */
+ case '&': /* $&: last match */
+ case '`': /* $`: string before last match */
+ case '\'': /* $': string after last match */
+ case '+': /* $+: string matches last paren. */
if (IS_lex_state_for(last_state, EXPR_FNAME)) {
tokadd(p, '$');
tokadd(p, c);
@@ -9534,11 +10590,11 @@ parse_gvar(struct parser_params *p, const enum lex_state_e last_state)
if (!parser_is_identchar(p)) {
YYLTYPE loc = RUBY_INIT_YYLLOC();
if (c == -1 || ISSPACE(c)) {
- compile_error(p, "`$' without identifiers is not allowed as a global variable name");
+ compile_error(p, "'$' without identifiers is not allowed as a global variable name");
}
else {
pushback(p, c);
- compile_error(p, "`$%c' is not allowed as a global variable name", c);
+ compile_error(p, "'$%c' is not allowed as a global variable name", c);
}
parser_show_error_line(p, &loc);
set_yylval_noname();
@@ -9551,11 +10607,16 @@ parse_gvar(struct parser_params *p, const enum lex_state_e last_state)
if (tokadd_ident(p, c)) return 0;
SET_LEX_STATE(EXPR_END);
- tokenize_ident(p);
+ if (VALID_SYMNAME_P(tok(p), toklen(p), p->enc, ID_GLOBAL)) {
+ tokenize_ident(p);
+ }
+ else {
+ compile_error(p, "'%.*s' is not allowed as a global variable name", toklen(p), tok(p));
+ set_yylval_noname();
+ }
return tGVAR;
}
-#ifndef RIPPER
static bool
parser_numbered_param(struct parser_params *p, int n)
{
@@ -9577,7 +10638,6 @@ parser_numbered_param(struct parser_params *p, int n)
}
return true;
}
-#endif
static enum yytokentype
parse_atmark(struct parser_params *p, const enum lex_state_e last_state)
@@ -9600,10 +10660,10 @@ parse_atmark(struct parser_params *p, const enum lex_state_e last_state)
pushback(p, c);
RUBY_SET_YYLLOC(loc);
if (result == tIVAR) {
- compile_error(p, "`@' without identifiers is not allowed as an instance variable name");
+ compile_error(p, "'@' without identifiers is not allowed as an instance variable name");
}
else {
- compile_error(p, "`@@' without identifiers is not allowed as a class variable name");
+ compile_error(p, "'@@' without identifiers is not allowed as a class variable name");
}
parser_show_error_line(p, &loc);
set_yylval_noname();
@@ -9614,10 +10674,10 @@ parse_atmark(struct parser_params *p, const enum lex_state_e last_state)
pushback(p, c);
RUBY_SET_YYLLOC(loc);
if (result == tIVAR) {
- compile_error(p, "`@%c' is not allowed as an instance variable name", c);
+ compile_error(p, "'@%c' is not allowed as an instance variable name", c);
}
else {
- compile_error(p, "`@@%c' is not allowed as a class variable name", c);
+ compile_error(p, "'@@%c' is not allowed as a class variable name", c);
}
parser_show_error_line(p, &loc);
set_yylval_noname();
@@ -9634,13 +10694,13 @@ static enum yytokentype
parse_ident(struct parser_params *p, int c, int cmd_state)
{
enum yytokentype result;
- int mb = ENC_CODERANGE_7BIT;
+ bool is_ascii = true;
const enum lex_state_e last_state = p->lex.state;
ID ident;
int enforce_keyword_end = 0;
do {
- if (!ISASCII(c)) mb = ENC_CODERANGE_UNKNOWN;
+ if (!ISASCII(c)) is_ascii = false;
if (tokadd_mbchar(p, c) == -1) return 0;
c = nextc(p);
} while (parser_is_identchar(p));
@@ -9669,14 +10729,14 @@ parse_ident(struct parser_params *p, int c, int cmd_state)
}
#ifndef RIPPER
- if (!NIL_P(peek_end_expect_token_locations(p))) {
- VALUE end_loc;
+ if (peek_end_expect_token_locations(p)) {
+ const rb_code_position_t *end_pos;
int lineno, column;
int beg_pos = (int)(p->lex.ptok - p->lex.pbeg);
- end_loc = peek_end_expect_token_locations(p);
- lineno = NUM2INT(rb_ary_entry(end_loc, 0));
- column = NUM2INT(rb_ary_entry(end_loc, 1));
+ end_pos = peek_end_expect_token_locations(p)->pos;
+ lineno = end_pos->lineno;
+ column = end_pos->column;
if (p->debug) {
rb_parser_printf(p, "enforce_keyword_end check. current: (%d, %d), peek: (%d, %d)\n",
@@ -9694,7 +10754,7 @@ parse_ident(struct parser_params *p, int c, int cmd_state)
}
#endif
- if (mb == ENC_CODERANGE_7BIT && (!IS_lex_state(EXPR_DOT) || enforce_keyword_end)) {
+ if (is_ascii && (!IS_lex_state(EXPR_DOT) || enforce_keyword_end)) {
const struct kwtable *kw;
/* See if it is a reserved word. */
@@ -9777,7 +10837,7 @@ parser_yylex(struct parser_params *p)
int token_seen = p->token_seen;
if (p->lex.strterm) {
- if (p->lex.strterm->flags & STRTERM_HEREDOC) {
+ if (strterm_is_heredoc(p->lex.strterm)) {
token_flush(p);
return here_document(p, &p->lex.strterm->u.heredoc);
}
@@ -9799,9 +10859,9 @@ parser_yylex(struct parser_params *p)
case '\004': /* ^D */
case '\032': /* ^Z */
case -1: /* end of script. */
- p->eofp = 1;
+ p->eofp = 1;
#ifndef RIPPER
- if (!NIL_P(p->end_expect_token_locations) && RARRAY_LEN(p->end_expect_token_locations) > 0) {
+ if (p->end_expect_token_locations) {
pop_end_expect_token_locations(p);
RUBY_SET_YYLLOC_OF_DUMMY_END(*p->yylloc);
return tDUMNY_END;
@@ -9840,19 +10900,21 @@ parser_yylex(struct parser_params *p)
case '#': /* it's a comment */
p->token_seen = token_seen;
+ const char *const pcur = p->lex.pcur, *const ptok = p->lex.ptok;
/* no magic_comment in shebang line */
if (!parser_magic_comment(p, p->lex.pcur, p->lex.pend - p->lex.pcur)) {
if (comment_at_top(p)) {
set_file_encoding(p, p->lex.pcur, p->lex.pend);
}
}
+ p->lex.pcur = pcur, p->lex.ptok = ptok;
lex_goto_eol(p);
dispatch_scan_event(p, tCOMMENT);
fallthru = TRUE;
/* fall through */
case '\n':
p->token_seen = token_seen;
- VALUE prevline = p->lex.lastline;
+ rb_parser_string_t *prevline = p->lex.lastline;
c = (IS_lex_state(EXPR_BEG|EXPR_CLASS|EXPR_FNAME|EXPR_DOT) &&
!IS_lex_state(EXPR_LABELED));
if (c || IS_lex_state_all(EXPR_ARG|EXPR_LABELED)) {
@@ -9914,7 +10976,7 @@ parser_yylex(struct parser_params *p)
}
pushback(p, c);
if (IS_SPCARG(c)) {
- rb_warning0("`**' interpreted as argument prefix");
+ rb_warning0("'**' interpreted as argument prefix");
c = tDSTAR;
}
else if (IS_BEG()) {
@@ -9932,7 +10994,7 @@ parser_yylex(struct parser_params *p)
}
pushback(p, c);
if (IS_SPCARG(c)) {
- rb_warning0("`*' interpreted as argument prefix");
+ rb_warning0("'*' interpreted as argument prefix");
c = tSTAR;
}
else if (IS_BEG()) {
@@ -10018,7 +11080,7 @@ parser_yylex(struct parser_params *p)
!IS_lex_state(EXPR_DOT | EXPR_CLASS) &&
!IS_END() &&
(!IS_ARG() || IS_lex_state(EXPR_LABELED) || space_seen)) {
- int token = heredoc_identifier(p);
+ enum yytokentype token = heredoc_identifier(p);
if (token) return token < 0 ? 0 : token;
}
if (IS_AFTER_OPERATOR()) {
@@ -10121,8 +11183,8 @@ parser_yylex(struct parser_params *p)
if ((c != ':') ||
(c = peekc_n(p, 1)) == -1 ||
!(c == '\'' || c == '"' ||
- is_identchar((p->lex.pcur+1), p->lex.pend, p->enc))) {
- rb_warning0("`&' interpreted as argument prefix");
+ is_identchar(p, (p->lex.pcur+1), p->lex.pend, p->enc))) {
+ rb_warning0("'&' interpreted as argument prefix");
}
c = tAMPER;
}
@@ -10473,7 +11535,7 @@ parser_yylex(struct parser_params *p)
default:
if (!parser_is_identchar(p)) {
- compile_error(p, "Invalid char `\\x%02X' in expression", c);
+ compile_error(p, "Invalid char '\\x%02X' in expression", c);
token_flush(p);
goto retry;
}
@@ -10491,7 +11553,7 @@ yylex(YYSTYPE *lval, YYLTYPE *yylloc, struct parser_params *p)
enum yytokentype t;
p->lval = lval;
- lval->val = Qundef;
+ lval->node = 0;
p->yylloc = yylloc;
t = parser_yylex(p);
@@ -10507,30 +11569,1170 @@ yylex(YYSTYPE *lval, YYLTYPE *yylloc, struct parser_params *p)
#define LVAR_USED ((ID)1 << (sizeof(ID) * CHAR_BIT - 1))
static NODE*
-node_new_temporal(struct parser_params *p, enum node_type type, VALUE a0, VALUE a1, VALUE a2)
+node_new_internal(struct parser_params *p, enum node_type type, size_t size, size_t alignment)
{
- NODE *n = rb_ast_newnode(p->ast, type);
+ NODE *n = rb_ast_newnode(p->ast, type, size, alignment);
- rb_node_init(n, type, a0, a1, a2);
+ rb_node_init(n, type);
return n;
}
+static NODE *
+nd_set_loc(NODE *nd, const YYLTYPE *loc)
+{
+ nd->nd_loc = *loc;
+ nd_set_line(nd, loc->beg_pos.lineno);
+ return nd;
+}
+
static NODE*
-node_newnode(struct parser_params *p, enum node_type type, VALUE a0, VALUE a1, VALUE a2, const rb_code_location_t *loc)
+node_newnode(struct parser_params *p, enum node_type type, size_t size, size_t alignment, const rb_code_location_t *loc)
{
- NODE *n = node_new_temporal(p, type, a0, a1, a2);
+ NODE *n = node_new_internal(p, type, size, alignment);
nd_set_loc(n, loc);
nd_set_node_id(n, parser_get_node_id(p));
return n;
}
-static NODE *
-nd_set_loc(NODE *nd, const YYLTYPE *loc)
+#define NODE_NEWNODE(node_type, type, loc) (type *)(node_newnode(p, node_type, sizeof(type), RUBY_ALIGNOF(type), loc))
+
+static rb_node_scope_t *
+rb_node_scope_new(struct parser_params *p, rb_node_args_t *nd_args, NODE *nd_body, const YYLTYPE *loc)
{
- nd->nd_loc = *loc;
- nd_set_line(nd, loc->beg_pos.lineno);
- return nd;
+ rb_ast_id_table_t *nd_tbl;
+ nd_tbl = local_tbl(p);
+ rb_node_scope_t *n = NODE_NEWNODE(NODE_SCOPE, rb_node_scope_t, loc);
+ n->nd_tbl = nd_tbl;
+ n->nd_body = nd_body;
+ n->nd_args = nd_args;
+
+ return n;
+}
+
+static rb_node_scope_t *
+rb_node_scope_new2(struct parser_params *p, rb_ast_id_table_t *nd_tbl, rb_node_args_t *nd_args, NODE *nd_body, const YYLTYPE *loc)
+{
+ rb_node_scope_t *n = NODE_NEWNODE(NODE_SCOPE, rb_node_scope_t, loc);
+ n->nd_tbl = nd_tbl;
+ n->nd_body = nd_body;
+ n->nd_args = nd_args;
+
+ return n;
+}
+
+static rb_node_defn_t *
+rb_node_defn_new(struct parser_params *p, ID nd_mid, NODE *nd_defn, const YYLTYPE *loc)
+{
+ rb_node_defn_t *n = NODE_NEWNODE(NODE_DEFN, rb_node_defn_t, loc);
+ n->nd_mid = nd_mid;
+ n->nd_defn = nd_defn;
+
+ return n;
+}
+
+static rb_node_defs_t *
+rb_node_defs_new(struct parser_params *p, NODE *nd_recv, ID nd_mid, NODE *nd_defn, const YYLTYPE *loc)
+{
+ rb_node_defs_t *n = NODE_NEWNODE(NODE_DEFS, rb_node_defs_t, loc);
+ n->nd_recv = nd_recv;
+ n->nd_mid = nd_mid;
+ n->nd_defn = nd_defn;
+
+ return n;
+}
+
+static rb_node_block_t *
+rb_node_block_new(struct parser_params *p, NODE *nd_head, const YYLTYPE *loc)
+{
+ rb_node_block_t *n = NODE_NEWNODE(NODE_BLOCK, rb_node_block_t, loc);
+ n->nd_head = nd_head;
+ n->nd_end = (NODE *)n;
+ n->nd_next = 0;
+
+ return n;
+}
+
+static rb_node_for_t *
+rb_node_for_new(struct parser_params *p, NODE *nd_iter, NODE *nd_body, const YYLTYPE *loc)
+{
+ rb_node_for_t *n = NODE_NEWNODE(NODE_FOR, rb_node_for_t, loc);
+ n->nd_body = nd_body;
+ n->nd_iter = nd_iter;
+
+ return n;
+}
+
+static rb_node_for_masgn_t *
+rb_node_for_masgn_new(struct parser_params *p, NODE *nd_var, const YYLTYPE *loc)
+{
+ rb_node_for_masgn_t *n = NODE_NEWNODE(NODE_FOR_MASGN, rb_node_for_masgn_t, loc);
+ n->nd_var = nd_var;
+
+ return n;
+}
+
+static rb_node_retry_t *
+rb_node_retry_new(struct parser_params *p, const YYLTYPE *loc)
+{
+ rb_node_retry_t *n = NODE_NEWNODE(NODE_RETRY, rb_node_retry_t, loc);
+
+ return n;
+}
+
+static rb_node_begin_t *
+rb_node_begin_new(struct parser_params *p, NODE *nd_body, const YYLTYPE *loc)
+{
+ rb_node_begin_t *n = NODE_NEWNODE(NODE_BEGIN, rb_node_begin_t, loc);
+ n->nd_body = nd_body;
+
+ return n;
+}
+
+static rb_node_rescue_t *
+rb_node_rescue_new(struct parser_params *p, NODE *nd_head, NODE *nd_resq, NODE *nd_else, const YYLTYPE *loc)
+{
+ rb_node_rescue_t *n = NODE_NEWNODE(NODE_RESCUE, rb_node_rescue_t, loc);
+ n->nd_head = nd_head;
+ n->nd_resq = nd_resq;
+ n->nd_else = nd_else;
+
+ return n;
+}
+
+static rb_node_resbody_t *
+rb_node_resbody_new(struct parser_params *p, NODE *nd_args, NODE *nd_body, NODE *nd_next, const YYLTYPE *loc)
+{
+ rb_node_resbody_t *n = NODE_NEWNODE(NODE_RESBODY, rb_node_resbody_t, loc);
+ n->nd_args = nd_args;
+ n->nd_body = nd_body;
+ n->nd_next = nd_next;
+
+ return n;
+}
+
+static rb_node_ensure_t *
+rb_node_ensure_new(struct parser_params *p, NODE *nd_head, NODE *nd_ensr, const YYLTYPE *loc)
+{
+ rb_node_ensure_t *n = NODE_NEWNODE(NODE_ENSURE, rb_node_ensure_t, loc);
+ n->nd_head = nd_head;
+ n->nd_ensr = nd_ensr;
+
+ return n;
+}
+
+static rb_node_and_t *
+rb_node_and_new(struct parser_params *p, NODE *nd_1st, NODE *nd_2nd, const YYLTYPE *loc)
+{
+ rb_node_and_t *n = NODE_NEWNODE(NODE_AND, rb_node_and_t, loc);
+ n->nd_1st = nd_1st;
+ n->nd_2nd = nd_2nd;
+
+ return n;
+}
+
+static rb_node_or_t *
+rb_node_or_new(struct parser_params *p, NODE *nd_1st, NODE *nd_2nd, const YYLTYPE *loc)
+{
+ rb_node_or_t *n = NODE_NEWNODE(NODE_OR, rb_node_or_t, loc);
+ n->nd_1st = nd_1st;
+ n->nd_2nd = nd_2nd;
+
+ return n;
+}
+
+static rb_node_return_t *
+rb_node_return_new(struct parser_params *p, NODE *nd_stts, const YYLTYPE *loc)
+{
+ rb_node_return_t *n = NODE_NEWNODE(NODE_RETURN, rb_node_return_t, loc);
+ n->nd_stts = nd_stts;
+ return n;
+}
+
+static rb_node_yield_t *
+rb_node_yield_new(struct parser_params *p, NODE *nd_head, const YYLTYPE *loc)
+{
+ rb_node_yield_t *n = NODE_NEWNODE(NODE_YIELD, rb_node_yield_t, loc);
+ n->nd_head = nd_head;
+
+ return n;
+}
+
+static rb_node_if_t *
+rb_node_if_new(struct parser_params *p, NODE *nd_cond, NODE *nd_body, NODE *nd_else, const YYLTYPE *loc)
+{
+ rb_node_if_t *n = NODE_NEWNODE(NODE_IF, rb_node_if_t, loc);
+ n->nd_cond = nd_cond;
+ n->nd_body = nd_body;
+ n->nd_else = nd_else;
+
+ return n;
+}
+
+static rb_node_unless_t *
+rb_node_unless_new(struct parser_params *p, NODE *nd_cond, NODE *nd_body, NODE *nd_else, const YYLTYPE *loc)
+{
+ rb_node_unless_t *n = NODE_NEWNODE(NODE_UNLESS, rb_node_unless_t, loc);
+ n->nd_cond = nd_cond;
+ n->nd_body = nd_body;
+ n->nd_else = nd_else;
+
+ return n;
+}
+
+static rb_node_class_t *
+rb_node_class_new(struct parser_params *p, NODE *nd_cpath, NODE *nd_body, NODE *nd_super, const YYLTYPE *loc)
+{
+ /* Keep the order of node creation */
+ NODE *scope = NEW_SCOPE(0, nd_body, loc);
+ rb_node_class_t *n = NODE_NEWNODE(NODE_CLASS, rb_node_class_t, loc);
+ n->nd_cpath = nd_cpath;
+ n->nd_body = scope;
+ n->nd_super = nd_super;
+
+ return n;
+}
+
+static rb_node_sclass_t *
+rb_node_sclass_new(struct parser_params *p, NODE *nd_recv, NODE *nd_body, const YYLTYPE *loc)
+{
+ /* Keep the order of node creation */
+ NODE *scope = NEW_SCOPE(0, nd_body, loc);
+ rb_node_sclass_t *n = NODE_NEWNODE(NODE_SCLASS, rb_node_sclass_t, loc);
+ n->nd_recv = nd_recv;
+ n->nd_body = scope;
+
+ return n;
+}
+
+static rb_node_module_t *
+rb_node_module_new(struct parser_params *p, NODE *nd_cpath, NODE *nd_body, const YYLTYPE *loc)
+{
+ /* Keep the order of node creation */
+ NODE *scope = NEW_SCOPE(0, nd_body, loc);
+ rb_node_module_t *n = NODE_NEWNODE(NODE_MODULE, rb_node_module_t, loc);
+ n->nd_cpath = nd_cpath;
+ n->nd_body = scope;
+
+ return n;
+}
+
+static rb_node_iter_t *
+rb_node_iter_new(struct parser_params *p, rb_node_args_t *nd_args, NODE *nd_body, const YYLTYPE *loc)
+{
+ /* Keep the order of node creation */
+ NODE *scope = NEW_SCOPE(nd_args, nd_body, loc);
+ rb_node_iter_t *n = NODE_NEWNODE(NODE_ITER, rb_node_iter_t, loc);
+ n->nd_body = scope;
+ n->nd_iter = 0;
+
+ return n;
+}
+
+static rb_node_lambda_t *
+rb_node_lambda_new(struct parser_params *p, rb_node_args_t *nd_args, NODE *nd_body, const YYLTYPE *loc)
+{
+ /* Keep the order of node creation */
+ NODE *scope = NEW_SCOPE(nd_args, nd_body, loc);
+ rb_node_lambda_t *n = NODE_NEWNODE(NODE_LAMBDA, rb_node_lambda_t, loc);
+ n->nd_body = scope;
+
+ return n;
+}
+
+static rb_node_case_t *
+rb_node_case_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, const YYLTYPE *loc)
+{
+ rb_node_case_t *n = NODE_NEWNODE(NODE_CASE, rb_node_case_t, loc);
+ n->nd_head = nd_head;
+ n->nd_body = nd_body;
+
+ return n;
+}
+
+static rb_node_case2_t *
+rb_node_case2_new(struct parser_params *p, NODE *nd_body, const YYLTYPE *loc)
+{
+ rb_node_case2_t *n = NODE_NEWNODE(NODE_CASE2, rb_node_case2_t, loc);
+ n->nd_head = 0;
+ n->nd_body = nd_body;
+
+ return n;
+}
+
+static rb_node_case3_t *
+rb_node_case3_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, const YYLTYPE *loc)
+{
+ rb_node_case3_t *n = NODE_NEWNODE(NODE_CASE3, rb_node_case3_t, loc);
+ n->nd_head = nd_head;
+ n->nd_body = nd_body;
+
+ return n;
+}
+
+static rb_node_when_t *
+rb_node_when_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, NODE *nd_next, const YYLTYPE *loc)
+{
+ rb_node_when_t *n = NODE_NEWNODE(NODE_WHEN, rb_node_when_t, loc);
+ n->nd_head = nd_head;
+ n->nd_body = nd_body;
+ n->nd_next = nd_next;
+
+ return n;
+}
+
+static rb_node_in_t *
+rb_node_in_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, NODE *nd_next, const YYLTYPE *loc)
+{
+ rb_node_in_t *n = NODE_NEWNODE(NODE_IN, rb_node_in_t, loc);
+ n->nd_head = nd_head;
+ n->nd_body = nd_body;
+ n->nd_next = nd_next;
+
+ return n;
+}
+
+static rb_node_while_t *
+rb_node_while_new(struct parser_params *p, NODE *nd_cond, NODE *nd_body, long nd_state, const YYLTYPE *loc)
+{
+ rb_node_while_t *n = NODE_NEWNODE(NODE_WHILE, rb_node_while_t, loc);
+ n->nd_cond = nd_cond;
+ n->nd_body = nd_body;
+ n->nd_state = nd_state;
+
+ return n;
+}
+
+static rb_node_until_t *
+rb_node_until_new(struct parser_params *p, NODE *nd_cond, NODE *nd_body, long nd_state, const YYLTYPE *loc)
+{
+ rb_node_until_t *n = NODE_NEWNODE(NODE_UNTIL, rb_node_until_t, loc);
+ n->nd_cond = nd_cond;
+ n->nd_body = nd_body;
+ n->nd_state = nd_state;
+
+ return n;
+}
+
+static rb_node_colon2_t *
+rb_node_colon2_new(struct parser_params *p, NODE *nd_head, ID nd_mid, const YYLTYPE *loc)
+{
+ rb_node_colon2_t *n = NODE_NEWNODE(NODE_COLON2, rb_node_colon2_t, loc);
+ n->nd_head = nd_head;
+ n->nd_mid = nd_mid;
+
+ return n;
+}
+
+static rb_node_colon3_t *
+rb_node_colon3_new(struct parser_params *p, ID nd_mid, const YYLTYPE *loc)
+{
+ rb_node_colon3_t *n = NODE_NEWNODE(NODE_COLON3, rb_node_colon3_t, loc);
+ n->nd_mid = nd_mid;
+
+ return n;
+}
+
+static rb_node_dot2_t *
+rb_node_dot2_new(struct parser_params *p, NODE *nd_beg, NODE *nd_end, const YYLTYPE *loc)
+{
+ rb_node_dot2_t *n = NODE_NEWNODE(NODE_DOT2, rb_node_dot2_t, loc);
+ n->nd_beg = nd_beg;
+ n->nd_end = nd_end;
+
+ return n;
+}
+
+static rb_node_dot3_t *
+rb_node_dot3_new(struct parser_params *p, NODE *nd_beg, NODE *nd_end, const YYLTYPE *loc)
+{
+ rb_node_dot3_t *n = NODE_NEWNODE(NODE_DOT3, rb_node_dot3_t, loc);
+ n->nd_beg = nd_beg;
+ n->nd_end = nd_end;
+
+ return n;
+}
+
+static rb_node_self_t *
+rb_node_self_new(struct parser_params *p, const YYLTYPE *loc)
+{
+ rb_node_self_t *n = NODE_NEWNODE(NODE_SELF, rb_node_self_t, loc);
+ n->nd_state = 1;
+
+ return n;
+}
+
+static rb_node_nil_t *
+rb_node_nil_new(struct parser_params *p, const YYLTYPE *loc)
+{
+ rb_node_nil_t *n = NODE_NEWNODE(NODE_NIL, rb_node_nil_t, loc);
+
+ return n;
+}
+
+static rb_node_true_t *
+rb_node_true_new(struct parser_params *p, const YYLTYPE *loc)
+{
+ rb_node_true_t *n = NODE_NEWNODE(NODE_TRUE, rb_node_true_t, loc);
+
+ return n;
+}
+
+static rb_node_false_t *
+rb_node_false_new(struct parser_params *p, const YYLTYPE *loc)
+{
+ rb_node_false_t *n = NODE_NEWNODE(NODE_FALSE, rb_node_false_t, loc);
+
+ return n;
+}
+
+static rb_node_super_t *
+rb_node_super_new(struct parser_params *p, NODE *nd_args, const YYLTYPE *loc)
+{
+ rb_node_super_t *n = NODE_NEWNODE(NODE_SUPER, rb_node_super_t, loc);
+ n->nd_args = nd_args;
+
+ return n;
+}
+
+static rb_node_zsuper_t *
+rb_node_zsuper_new(struct parser_params *p, const YYLTYPE *loc)
+{
+ rb_node_zsuper_t *n = NODE_NEWNODE(NODE_ZSUPER, rb_node_zsuper_t, loc);
+
+ return n;
+}
+
+static rb_node_match2_t *
+rb_node_match2_new(struct parser_params *p, NODE *nd_recv, NODE *nd_value, const YYLTYPE *loc)
+{
+ rb_node_match2_t *n = NODE_NEWNODE(NODE_MATCH2, rb_node_match2_t, loc);
+ n->nd_recv = nd_recv;
+ n->nd_value = nd_value;
+ n->nd_args = 0;
+
+ return n;
+}
+
+static rb_node_match3_t *
+rb_node_match3_new(struct parser_params *p, NODE *nd_recv, NODE *nd_value, const YYLTYPE *loc)
+{
+ rb_node_match3_t *n = NODE_NEWNODE(NODE_MATCH3, rb_node_match3_t, loc);
+ n->nd_recv = nd_recv;
+ n->nd_value = nd_value;
+
+ return n;
+}
+
+/* TODO: Use union for NODE_LIST2 */
+static rb_node_list_t *
+rb_node_list_new(struct parser_params *p, NODE *nd_head, const YYLTYPE *loc)
+{
+ rb_node_list_t *n = NODE_NEWNODE(NODE_LIST, rb_node_list_t, loc);
+ n->nd_head = nd_head;
+ n->as.nd_alen = 1;
+ n->nd_next = 0;
+
+ return n;
+}
+
+static rb_node_list_t *
+rb_node_list_new2(struct parser_params *p, NODE *nd_head, long nd_alen, NODE *nd_next, const YYLTYPE *loc)
+{
+ rb_node_list_t *n = NODE_NEWNODE(NODE_LIST, rb_node_list_t, loc);
+ n->nd_head = nd_head;
+ n->as.nd_alen = nd_alen;
+ n->nd_next = nd_next;
+
+ return n;
+}
+
+static rb_node_zlist_t *
+rb_node_zlist_new(struct parser_params *p, const YYLTYPE *loc)
+{
+ rb_node_zlist_t *n = NODE_NEWNODE(NODE_ZLIST, rb_node_zlist_t, loc);
+
+ return n;
+}
+
+static rb_node_hash_t *
+rb_node_hash_new(struct parser_params *p, NODE *nd_head, const YYLTYPE *loc)
+{
+ rb_node_hash_t *n = NODE_NEWNODE(NODE_HASH, rb_node_hash_t, loc);
+ n->nd_head = nd_head;
+ n->nd_brace = 0;
+
+ return n;
+}
+
+static rb_node_masgn_t *
+rb_node_masgn_new(struct parser_params *p, NODE *nd_head, NODE *nd_args, const YYLTYPE *loc)
+{
+ rb_node_masgn_t *n = NODE_NEWNODE(NODE_MASGN, rb_node_masgn_t, loc);
+ n->nd_head = nd_head;
+ n->nd_value = 0;
+ n->nd_args = nd_args;
+
+ return n;
+}
+
+static rb_node_gasgn_t *
+rb_node_gasgn_new(struct parser_params *p, ID nd_vid, NODE *nd_value, const YYLTYPE *loc)
+{
+ rb_node_gasgn_t *n = NODE_NEWNODE(NODE_GASGN, rb_node_gasgn_t, loc);
+ n->nd_vid = nd_vid;
+ n->nd_value = nd_value;
+
+ return n;
+}
+
+static rb_node_lasgn_t *
+rb_node_lasgn_new(struct parser_params *p, ID nd_vid, NODE *nd_value, const YYLTYPE *loc)
+{
+ rb_node_lasgn_t *n = NODE_NEWNODE(NODE_LASGN, rb_node_lasgn_t, loc);
+ n->nd_vid = nd_vid;
+ n->nd_value = nd_value;
+
+ return n;
+}
+
+static rb_node_dasgn_t *
+rb_node_dasgn_new(struct parser_params *p, ID nd_vid, NODE *nd_value, const YYLTYPE *loc)
+{
+ rb_node_dasgn_t *n = NODE_NEWNODE(NODE_DASGN, rb_node_dasgn_t, loc);
+ n->nd_vid = nd_vid;
+ n->nd_value = nd_value;
+
+ return n;
+}
+
+static rb_node_iasgn_t *
+rb_node_iasgn_new(struct parser_params *p, ID nd_vid, NODE *nd_value, const YYLTYPE *loc)
+{
+ rb_node_iasgn_t *n = NODE_NEWNODE(NODE_IASGN, rb_node_iasgn_t, loc);
+ n->nd_vid = nd_vid;
+ n->nd_value = nd_value;
+
+ return n;
+}
+
+static rb_node_cvasgn_t *
+rb_node_cvasgn_new(struct parser_params *p, ID nd_vid, NODE *nd_value, const YYLTYPE *loc)
+{
+ rb_node_cvasgn_t *n = NODE_NEWNODE(NODE_CVASGN, rb_node_cvasgn_t, loc);
+ n->nd_vid = nd_vid;
+ n->nd_value = nd_value;
+
+ return n;
+}
+
+static rb_node_op_asgn1_t *
+rb_node_op_asgn1_new(struct parser_params *p, NODE *nd_recv, ID nd_mid, NODE *index, NODE *rvalue, const YYLTYPE *loc)
+{
+ rb_node_op_asgn1_t *n = NODE_NEWNODE(NODE_OP_ASGN1, rb_node_op_asgn1_t, loc);
+ n->nd_recv = nd_recv;
+ n->nd_mid = nd_mid;
+ n->nd_index = index;
+ n->nd_rvalue = rvalue;
+
+ return n;
+}
+
+static rb_node_op_asgn2_t *
+rb_node_op_asgn2_new(struct parser_params *p, NODE *nd_recv, NODE *nd_value, ID nd_vid, ID nd_mid, bool nd_aid, const YYLTYPE *loc)
+{
+ rb_node_op_asgn2_t *n = NODE_NEWNODE(NODE_OP_ASGN2, rb_node_op_asgn2_t, loc);
+ n->nd_recv = nd_recv;
+ n->nd_value = nd_value;
+ n->nd_vid = nd_vid;
+ n->nd_mid = nd_mid;
+ n->nd_aid = nd_aid;
+
+ return n;
+}
+
+static rb_node_op_asgn_or_t *
+rb_node_op_asgn_or_new(struct parser_params *p, NODE *nd_head, NODE *nd_value, const YYLTYPE *loc)
+{
+ rb_node_op_asgn_or_t *n = NODE_NEWNODE(NODE_OP_ASGN_OR, rb_node_op_asgn_or_t, loc);
+ n->nd_head = nd_head;
+ n->nd_value = nd_value;
+
+ return n;
+}
+
+static rb_node_op_asgn_and_t *
+rb_node_op_asgn_and_new(struct parser_params *p, NODE *nd_head, NODE *nd_value, const YYLTYPE *loc)
+{
+ rb_node_op_asgn_and_t *n = NODE_NEWNODE(NODE_OP_ASGN_AND, rb_node_op_asgn_and_t, loc);
+ n->nd_head = nd_head;
+ n->nd_value = nd_value;
+
+ return n;
+}
+
+static rb_node_gvar_t *
+rb_node_gvar_new(struct parser_params *p, ID nd_vid, const YYLTYPE *loc)
+{
+ rb_node_gvar_t *n = NODE_NEWNODE(NODE_GVAR, rb_node_gvar_t, loc);
+ n->nd_vid = nd_vid;
+
+ return n;
+}
+
+static rb_node_lvar_t *
+rb_node_lvar_new(struct parser_params *p, ID nd_vid, const YYLTYPE *loc)
+{
+ rb_node_lvar_t *n = NODE_NEWNODE(NODE_LVAR, rb_node_lvar_t, loc);
+ n->nd_vid = nd_vid;
+
+ return n;
+}
+
+static rb_node_dvar_t *
+rb_node_dvar_new(struct parser_params *p, ID nd_vid, const YYLTYPE *loc)
+{
+ rb_node_dvar_t *n = NODE_NEWNODE(NODE_DVAR, rb_node_dvar_t, loc);
+ n->nd_vid = nd_vid;
+
+ return n;
+}
+
+static rb_node_ivar_t *
+rb_node_ivar_new(struct parser_params *p, ID nd_vid, const YYLTYPE *loc)
+{
+ rb_node_ivar_t *n = NODE_NEWNODE(NODE_IVAR, rb_node_ivar_t, loc);
+ n->nd_vid = nd_vid;
+
+ return n;
+}
+
+static rb_node_const_t *
+rb_node_const_new(struct parser_params *p, ID nd_vid, const YYLTYPE *loc)
+{
+ rb_node_const_t *n = NODE_NEWNODE(NODE_CONST, rb_node_const_t, loc);
+ n->nd_vid = nd_vid;
+
+ return n;
+}
+
+static rb_node_cvar_t *
+rb_node_cvar_new(struct parser_params *p, ID nd_vid, const YYLTYPE *loc)
+{
+ rb_node_cvar_t *n = NODE_NEWNODE(NODE_CVAR, rb_node_cvar_t, loc);
+ n->nd_vid = nd_vid;
+
+ return n;
+}
+
+static rb_node_nth_ref_t *
+rb_node_nth_ref_new(struct parser_params *p, long nd_nth, const YYLTYPE *loc)
+{
+ rb_node_nth_ref_t *n = NODE_NEWNODE(NODE_NTH_REF, rb_node_nth_ref_t, loc);
+ n->nd_nth = nd_nth;
+
+ return n;
+}
+
+static rb_node_back_ref_t *
+rb_node_back_ref_new(struct parser_params *p, long nd_nth, const YYLTYPE *loc)
+{
+ rb_node_back_ref_t *n = NODE_NEWNODE(NODE_BACK_REF, rb_node_back_ref_t, loc);
+ n->nd_nth = nd_nth;
+
+ return n;
+}
+
+static rb_node_integer_t *
+rb_node_integer_new(struct parser_params *p, char* val, int base, const YYLTYPE *loc)
+{
+ rb_node_integer_t *n = NODE_NEWNODE(NODE_INTEGER, rb_node_integer_t, loc);
+ n->val = val;
+ n->minus = FALSE;
+ n->base = base;
+
+ return n;
+}
+
+static rb_node_float_t *
+rb_node_float_new(struct parser_params *p, char* val, const YYLTYPE *loc)
+{
+ rb_node_float_t *n = NODE_NEWNODE(NODE_FLOAT, rb_node_float_t, loc);
+ n->val = val;
+ n->minus = FALSE;
+
+ return n;
+}
+
+static rb_node_rational_t *
+rb_node_rational_new(struct parser_params *p, char* val, int base, int seen_point, const YYLTYPE *loc)
+{
+ rb_node_rational_t *n = NODE_NEWNODE(NODE_RATIONAL, rb_node_rational_t, loc);
+ n->val = val;
+ n->minus = FALSE;
+ n->base = base;
+ n->seen_point = seen_point;
+
+ return n;
+}
+
+static rb_node_imaginary_t *
+rb_node_imaginary_new(struct parser_params *p, char* val, int base, int seen_point, enum rb_numeric_type numeric_type, const YYLTYPE *loc)
+{
+ rb_node_imaginary_t *n = NODE_NEWNODE(NODE_IMAGINARY, rb_node_imaginary_t, loc);
+ n->val = val;
+ n->minus = FALSE;
+ n->base = base;
+ n->seen_point = seen_point;
+ n->type = numeric_type;
+
+ return n;
+}
+
+static rb_node_str_t *
+rb_node_str_new(struct parser_params *p, rb_parser_string_t *string, const YYLTYPE *loc)
+{
+ rb_node_str_t *n = NODE_NEWNODE(NODE_STR, rb_node_str_t, loc);
+ n->string = string;
+
+ return n;
+}
+
+/* TODO; Use union for NODE_DSTR2 */
+static rb_node_dstr_t *
+rb_node_dstr_new0(struct parser_params *p, rb_parser_string_t *string, long nd_alen, NODE *nd_next, const YYLTYPE *loc)
+{
+ rb_node_dstr_t *n = NODE_NEWNODE(NODE_DSTR, rb_node_dstr_t, loc);
+ n->string = string;
+ n->as.nd_alen = nd_alen;
+ n->nd_next = (rb_node_list_t *)nd_next;
+
+ return n;
+}
+
+static rb_node_dstr_t *
+rb_node_dstr_new(struct parser_params *p, rb_parser_string_t *string, const YYLTYPE *loc)
+{
+ return rb_node_dstr_new0(p, string, 1, 0, loc);
+}
+
+static rb_node_xstr_t *
+rb_node_xstr_new(struct parser_params *p, rb_parser_string_t *string, const YYLTYPE *loc)
+{
+ rb_node_xstr_t *n = NODE_NEWNODE(NODE_XSTR, rb_node_xstr_t, loc);
+ n->string = string;
+
+ return n;
+}
+
+static rb_node_dxstr_t *
+rb_node_dxstr_new(struct parser_params *p, rb_parser_string_t *string, long nd_alen, NODE *nd_next, const YYLTYPE *loc)
+{
+ rb_node_dxstr_t *n = NODE_NEWNODE(NODE_DXSTR, rb_node_dxstr_t, loc);
+ n->string = string;
+ n->nd_alen = nd_alen;
+ n->nd_next = (rb_node_list_t *)nd_next;
+
+ return n;
+}
+
+static rb_node_sym_t *
+rb_node_sym_new(struct parser_params *p, VALUE str, const YYLTYPE *loc)
+{
+ rb_node_sym_t *n = NODE_NEWNODE(NODE_SYM, rb_node_sym_t, loc);
+ n->string = rb_str_to_parser_string(p, str);
+
+ return n;
+}
+
+static rb_node_dsym_t *
+rb_node_dsym_new(struct parser_params *p, rb_parser_string_t *string, long nd_alen, NODE *nd_next, const YYLTYPE *loc)
+{
+ rb_node_dsym_t *n = NODE_NEWNODE(NODE_DSYM, rb_node_dsym_t, loc);
+ n->string = string;
+ n->nd_alen = nd_alen;
+ n->nd_next = (rb_node_list_t *)nd_next;
+
+ return n;
+}
+
+static rb_node_evstr_t *
+rb_node_evstr_new(struct parser_params *p, NODE *nd_body, const YYLTYPE *loc)
+{
+ rb_node_evstr_t *n = NODE_NEWNODE(NODE_EVSTR, rb_node_evstr_t, loc);
+ n->nd_body = nd_body;
+
+ return n;
+}
+
+static rb_node_regx_t *
+rb_node_regx_new(struct parser_params *p, rb_parser_string_t *string, int options, const YYLTYPE *loc)
+{
+ rb_node_regx_t *n = NODE_NEWNODE(NODE_REGX, rb_node_regx_t, loc);
+ n->string = string;
+ n->options = options & RE_OPTION_MASK;
+
+ return n;
+}
+
+static rb_node_call_t *
+rb_node_call_new(struct parser_params *p, NODE *nd_recv, ID nd_mid, NODE *nd_args, const YYLTYPE *loc)
+{
+ rb_node_call_t *n = NODE_NEWNODE(NODE_CALL, rb_node_call_t, loc);
+ n->nd_recv = nd_recv;
+ n->nd_mid = nd_mid;
+ n->nd_args = nd_args;
+
+ return n;
+}
+
+static rb_node_opcall_t *
+rb_node_opcall_new(struct parser_params *p, NODE *nd_recv, ID nd_mid, NODE *nd_args, const YYLTYPE *loc)
+{
+ rb_node_opcall_t *n = NODE_NEWNODE(NODE_OPCALL, rb_node_opcall_t, loc);
+ n->nd_recv = nd_recv;
+ n->nd_mid = nd_mid;
+ n->nd_args = nd_args;
+
+ return n;
+}
+
+static rb_node_fcall_t *
+rb_node_fcall_new(struct parser_params *p, ID nd_mid, NODE *nd_args, const YYLTYPE *loc)
+{
+ rb_node_fcall_t *n = NODE_NEWNODE(NODE_FCALL, rb_node_fcall_t, loc);
+ n->nd_mid = nd_mid;
+ n->nd_args = nd_args;
+
+ return n;
+}
+
+static rb_node_qcall_t *
+rb_node_qcall_new(struct parser_params *p, NODE *nd_recv, ID nd_mid, NODE *nd_args, const YYLTYPE *loc)
+{
+ rb_node_qcall_t *n = NODE_NEWNODE(NODE_QCALL, rb_node_qcall_t, loc);
+ n->nd_recv = nd_recv;
+ n->nd_mid = nd_mid;
+ n->nd_args = nd_args;
+
+ return n;
+}
+
+static rb_node_vcall_t *
+rb_node_vcall_new(struct parser_params *p, ID nd_mid, const YYLTYPE *loc)
+{
+ rb_node_vcall_t *n = NODE_NEWNODE(NODE_VCALL, rb_node_vcall_t, loc);
+ n->nd_mid = nd_mid;
+
+ return n;
+}
+
+static rb_node_once_t *
+rb_node_once_new(struct parser_params *p, NODE *nd_body, const YYLTYPE *loc)
+{
+ rb_node_once_t *n = NODE_NEWNODE(NODE_ONCE, rb_node_once_t, loc);
+ n->nd_body = nd_body;
+
+ return n;
+}
+
+static rb_node_args_t *
+rb_node_args_new(struct parser_params *p, const YYLTYPE *loc)
+{
+ rb_node_args_t *n = NODE_NEWNODE(NODE_ARGS, rb_node_args_t, loc);
+ MEMZERO(&n->nd_ainfo, struct rb_args_info, 1);
+
+ return n;
+}
+
+static rb_node_args_aux_t *
+rb_node_args_aux_new(struct parser_params *p, ID nd_pid, int nd_plen, const YYLTYPE *loc)
+{
+ rb_node_args_aux_t *n = NODE_NEWNODE(NODE_ARGS_AUX, rb_node_args_aux_t, loc);
+ n->nd_pid = nd_pid;
+ n->nd_plen = nd_plen;
+ n->nd_next = 0;
+
+ return n;
+}
+
+static rb_node_opt_arg_t *
+rb_node_opt_arg_new(struct parser_params *p, NODE *nd_body, const YYLTYPE *loc)
+{
+ rb_node_opt_arg_t *n = NODE_NEWNODE(NODE_OPT_ARG, rb_node_opt_arg_t, loc);
+ n->nd_body = nd_body;
+ n->nd_next = 0;
+
+ return n;
+}
+
+static rb_node_kw_arg_t *
+rb_node_kw_arg_new(struct parser_params *p, NODE *nd_body, const YYLTYPE *loc)
+{
+ rb_node_kw_arg_t *n = NODE_NEWNODE(NODE_KW_ARG, rb_node_kw_arg_t, loc);
+ n->nd_body = nd_body;
+ n->nd_next = 0;
+
+ return n;
+}
+
+static rb_node_postarg_t *
+rb_node_postarg_new(struct parser_params *p, NODE *nd_1st, NODE *nd_2nd, const YYLTYPE *loc)
+{
+ rb_node_postarg_t *n = NODE_NEWNODE(NODE_POSTARG, rb_node_postarg_t, loc);
+ n->nd_1st = nd_1st;
+ n->nd_2nd = nd_2nd;
+
+ return n;
+}
+
+static rb_node_argscat_t *
+rb_node_argscat_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, const YYLTYPE *loc)
+{
+ rb_node_argscat_t *n = NODE_NEWNODE(NODE_ARGSCAT, rb_node_argscat_t, loc);
+ n->nd_head = nd_head;
+ n->nd_body = nd_body;
+
+ return n;
+}
+
+static rb_node_argspush_t *
+rb_node_argspush_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, const YYLTYPE *loc)
+{
+ rb_node_argspush_t *n = NODE_NEWNODE(NODE_ARGSPUSH, rb_node_argspush_t, loc);
+ n->nd_head = nd_head;
+ n->nd_body = nd_body;
+
+ return n;
+}
+
+static rb_node_splat_t *
+rb_node_splat_new(struct parser_params *p, NODE *nd_head, const YYLTYPE *loc)
+{
+ rb_node_splat_t *n = NODE_NEWNODE(NODE_SPLAT, rb_node_splat_t, loc);
+ n->nd_head = nd_head;
+
+ return n;
+}
+
+static rb_node_block_pass_t *
+rb_node_block_pass_new(struct parser_params *p, NODE *nd_body, const YYLTYPE *loc)
+{
+ rb_node_block_pass_t *n = NODE_NEWNODE(NODE_BLOCK_PASS, rb_node_block_pass_t, loc);
+ n->nd_head = 0;
+ n->nd_body = nd_body;
+
+ return n;
+}
+
+static rb_node_alias_t *
+rb_node_alias_new(struct parser_params *p, NODE *nd_1st, NODE *nd_2nd, const YYLTYPE *loc)
+{
+ rb_node_alias_t *n = NODE_NEWNODE(NODE_ALIAS, rb_node_alias_t, loc);
+ n->nd_1st = nd_1st;
+ n->nd_2nd = nd_2nd;
+
+ return n;
+}
+
+static rb_node_valias_t *
+rb_node_valias_new(struct parser_params *p, ID nd_alias, ID nd_orig, const YYLTYPE *loc)
+{
+ rb_node_valias_t *n = NODE_NEWNODE(NODE_VALIAS, rb_node_valias_t, loc);
+ n->nd_alias = nd_alias;
+ n->nd_orig = nd_orig;
+
+ return n;
+}
+
+static rb_node_undef_t *
+rb_node_undef_new(struct parser_params *p, NODE *nd_undef, const YYLTYPE *loc)
+{
+ rb_node_undef_t *n = NODE_NEWNODE(NODE_UNDEF, rb_node_undef_t, loc);
+ n->nd_undef = nd_undef;
+
+ return n;
+}
+
+static rb_node_errinfo_t *
+rb_node_errinfo_new(struct parser_params *p, const YYLTYPE *loc)
+{
+ rb_node_errinfo_t *n = NODE_NEWNODE(NODE_ERRINFO, rb_node_errinfo_t, loc);
+
+ return n;
+}
+
+static rb_node_defined_t *
+rb_node_defined_new(struct parser_params *p, NODE *nd_head, const YYLTYPE *loc)
+{
+ rb_node_defined_t *n = NODE_NEWNODE(NODE_DEFINED, rb_node_defined_t, loc);
+ n->nd_head = nd_head;
+
+ return n;
+}
+
+static rb_node_postexe_t *
+rb_node_postexe_new(struct parser_params *p, NODE *nd_body, const YYLTYPE *loc)
+{
+ rb_node_postexe_t *n = NODE_NEWNODE(NODE_POSTEXE, rb_node_postexe_t, loc);
+ n->nd_body = nd_body;
+
+ return n;
+}
+
+static rb_node_attrasgn_t *
+rb_node_attrasgn_new(struct parser_params *p, NODE *nd_recv, ID nd_mid, NODE *nd_args, const YYLTYPE *loc)
+{
+ rb_node_attrasgn_t *n = NODE_NEWNODE(NODE_ATTRASGN, rb_node_attrasgn_t, loc);
+ n->nd_recv = nd_recv;
+ n->nd_mid = nd_mid;
+ n->nd_args = nd_args;
+
+ return n;
+}
+
+static rb_node_aryptn_t *
+rb_node_aryptn_new(struct parser_params *p, NODE *pre_args, NODE *rest_arg, NODE *post_args, const YYLTYPE *loc)
+{
+ rb_node_aryptn_t *n = NODE_NEWNODE(NODE_ARYPTN, rb_node_aryptn_t, loc);
+ n->nd_pconst = 0;
+ n->pre_args = pre_args;
+ n->rest_arg = rest_arg;
+ n->post_args = post_args;
+
+ return n;
+}
+
+static rb_node_hshptn_t *
+rb_node_hshptn_new(struct parser_params *p, NODE *nd_pconst, NODE *nd_pkwargs, NODE *nd_pkwrestarg, const YYLTYPE *loc)
+{
+ rb_node_hshptn_t *n = NODE_NEWNODE(NODE_HSHPTN, rb_node_hshptn_t, loc);
+ n->nd_pconst = nd_pconst;
+ n->nd_pkwargs = nd_pkwargs;
+ n->nd_pkwrestarg = nd_pkwrestarg;
+
+ return n;
+}
+
+static rb_node_fndptn_t *
+rb_node_fndptn_new(struct parser_params *p, NODE *pre_rest_arg, NODE *args, NODE *post_rest_arg, const YYLTYPE *loc)
+{
+ rb_node_fndptn_t *n = NODE_NEWNODE(NODE_FNDPTN, rb_node_fndptn_t, loc);
+ n->nd_pconst = 0;
+ n->pre_rest_arg = pre_rest_arg;
+ n->args = args;
+ n->post_rest_arg = post_rest_arg;
+
+ return n;
+}
+
+static rb_node_line_t *
+rb_node_line_new(struct parser_params *p, const YYLTYPE *loc)
+{
+ rb_node_line_t *n = NODE_NEWNODE(NODE_LINE, rb_node_line_t, loc);
+
+ return n;
+}
+
+static rb_node_file_t *
+rb_node_file_new(struct parser_params *p, rb_parser_string_t *str, const YYLTYPE *loc)
+{
+ rb_node_file_t *n = NODE_NEWNODE(NODE_FILE, rb_node_file_t, loc);
+ n->path = str;
+
+ return n;
+}
+
+static rb_node_encoding_t *
+rb_node_encoding_new(struct parser_params *p, const YYLTYPE *loc)
+{
+ rb_node_encoding_t *n = NODE_NEWNODE(NODE_ENCODING, rb_node_encoding_t, loc);
+ n->enc = p->enc;
+
+ return n;
+}
+
+static rb_node_cdecl_t *
+rb_node_cdecl_new(struct parser_params *p, ID nd_vid, NODE *nd_value, NODE *nd_else, enum rb_parser_shareability shareability, const YYLTYPE *loc)
+{
+ rb_node_cdecl_t *n = NODE_NEWNODE(NODE_CDECL, rb_node_cdecl_t, loc);
+ n->nd_vid = nd_vid;
+ n->nd_value = nd_value;
+ n->nd_else = nd_else;
+ n->shareability = shareability;
+
+ return n;
+}
+
+static rb_node_op_cdecl_t *
+rb_node_op_cdecl_new(struct parser_params *p, NODE *nd_head, NODE *nd_value, ID nd_aid, enum rb_parser_shareability shareability, const YYLTYPE *loc)
+{
+ rb_node_op_cdecl_t *n = NODE_NEWNODE(NODE_OP_CDECL, rb_node_op_cdecl_t, loc);
+ n->nd_head = nd_head;
+ n->nd_value = nd_value;
+ n->nd_aid = nd_aid;
+ n->shareability = shareability;
+
+ return n;
+}
+
+static rb_node_error_t *
+rb_node_error_new(struct parser_params *p, const YYLTYPE *loc)
+{
+ rb_node_error_t *n = NODE_NEWNODE(NODE_ERROR, rb_node_error_t, loc);
+
+ return n;
+}
+
+static rb_node_break_t *
+rb_node_break_new(struct parser_params *p, NODE *nd_stts, const YYLTYPE *loc)
+{
+ rb_node_break_t *n = NODE_NEWNODE(NODE_BREAK, rb_node_break_t, loc);
+ n->nd_stts = nd_stts;
+ n->nd_chain = 0;
+
+ return n;
+}
+
+static rb_node_next_t *
+rb_node_next_new(struct parser_params *p, NODE *nd_stts, const YYLTYPE *loc)
+{
+ rb_node_next_t *n = NODE_NEWNODE(NODE_NEXT, rb_node_next_t, loc);
+ n->nd_stts = nd_stts;
+ n->nd_chain = 0;
+
+ return n;
+}
+
+static rb_node_redo_t *
+rb_node_redo_new(struct parser_params *p, const YYLTYPE *loc)
+{
+ rb_node_redo_t *n = NODE_NEWNODE(NODE_REDO, rb_node_redo_t, loc);
+ n->nd_chain = 0;
+
+ return n;
+}
+
+static rb_node_def_temp_t *
+rb_node_def_temp_new(struct parser_params *p, const YYLTYPE *loc)
+{
+ rb_node_def_temp_t *n = NODE_NEWNODE((enum node_type)NODE_DEF_TEMP, rb_node_def_temp_t, loc);
+ n->save.cur_arg = p->cur_arg;
+ n->save.numparam_save = 0;
+ n->save.max_numparam = 0;
+ n->save.ctxt = p->ctxt;
+ n->nd_def = 0;
+ n->nd_mid = 0;
+
+ return n;
+}
+
+static rb_node_def_temp_t *
+def_head_save(struct parser_params *p, rb_node_def_temp_t *n)
+{
+ n->save.numparam_save = numparam_push(p);
+ n->save.max_numparam = p->max_numparam;
+ return n;
}
#ifndef RIPPER
@@ -10545,13 +12747,14 @@ nodeline(NODE *node)
{
return nd_line(node);
}
+#endif
static NODE*
newline_node(NODE *node)
{
if (node) {
node = remove_begin(node);
- node->flags |= NODE_FL_NEWLINE;
+ nd_set_fl_newline(node);
}
return node;
}
@@ -10564,18 +12767,6 @@ fixpos(NODE *node, NODE *orig)
nd_set_line(node, nd_line(orig));
}
-static void
-parser_warning(struct parser_params *p, NODE *node, const char *mesg)
-{
- rb_compile_warning(p->ruby_sourcefile, nd_line(node), "%s", mesg);
-}
-
-static void
-parser_warn(struct parser_params *p, NODE *node, const char *mesg)
-{
- rb_compile_warn(p->ruby_sourcefile, nd_line(node), "%s", mesg);
-}
-
static NODE*
block_append(struct parser_params *p, NODE *head, NODE *tail)
{
@@ -10585,34 +12776,23 @@ block_append(struct parser_params *p, NODE *head, NODE *tail)
if (h == 0) return tail;
switch (nd_type(h)) {
- case NODE_LIT:
- case NODE_STR:
- case NODE_SELF:
- case NODE_TRUE:
- case NODE_FALSE:
- case NODE_NIL:
- parser_warning(p, h, "unused literal ignored");
- return tail;
default:
h = end = NEW_BLOCK(head, &head->nd_loc);
- end->nd_end = end;
head = end;
break;
case NODE_BLOCK:
- end = h->nd_end;
+ end = RNODE_BLOCK(h)->nd_end;
break;
}
- nd = end->nd_head;
+ nd = RNODE_BLOCK(end)->nd_head;
switch (nd_type(nd)) {
case NODE_RETURN:
case NODE_BREAK:
case NODE_NEXT:
case NODE_REDO:
case NODE_RETRY:
- if (RTEST(ruby_verbose)) {
- parser_warning(p, tail, "statement not reached");
- }
+ rb_warning0L(nd_line(tail), "statement not reached");
break;
default:
@@ -10621,10 +12801,9 @@ block_append(struct parser_params *p, NODE *head, NODE *tail)
if (!nd_type_p(tail, NODE_BLOCK)) {
tail = NEW_BLOCK(tail, &tail->nd_loc);
- tail->nd_end = tail;
}
- end->nd_next = tail;
- h->nd_end = tail->nd_end;
+ RNODE_BLOCK(end)->nd_next = tail;
+ RNODE_BLOCK(h)->nd_end = RNODE_BLOCK(tail)->nd_end;
nd_set_last_loc(head, nd_last_loc(tail));
return head;
}
@@ -10636,16 +12815,16 @@ list_append(struct parser_params *p, NODE *list, NODE *item)
NODE *last;
if (list == 0) return NEW_LIST(item, &item->nd_loc);
- if (list->nd_next) {
- last = list->nd_next->nd_end;
+ if (RNODE_LIST(list)->nd_next) {
+ last = RNODE_LIST(RNODE_LIST(list)->nd_next)->as.nd_end;
}
else {
last = list;
}
- list->nd_alen += 1;
- last->nd_next = NEW_LIST(item, &item->nd_loc);
- list->nd_next->nd_end = last->nd_next;
+ RNODE_LIST(list)->as.nd_alen += 1;
+ RNODE_LIST(last)->nd_next = NEW_LIST(item, &item->nd_loc);
+ RNODE_LIST(RNODE_LIST(list)->nd_next)->as.nd_end = RNODE_LIST(last)->nd_next;
nd_set_last_loc(list, nd_last_loc(item));
@@ -10658,20 +12837,20 @@ list_concat(NODE *head, NODE *tail)
{
NODE *last;
- if (head->nd_next) {
- last = head->nd_next->nd_end;
+ if (RNODE_LIST(head)->nd_next) {
+ last = RNODE_LIST(RNODE_LIST(head)->nd_next)->as.nd_end;
}
else {
last = head;
}
- head->nd_alen += tail->nd_alen;
- last->nd_next = tail;
- if (tail->nd_next) {
- head->nd_next->nd_end = tail->nd_next->nd_end;
+ RNODE_LIST(head)->as.nd_alen += RNODE_LIST(tail)->as.nd_alen;
+ RNODE_LIST(last)->nd_next = tail;
+ if (RNODE_LIST(tail)->nd_next) {
+ RNODE_LIST(RNODE_LIST(head)->nd_next)->as.nd_end = RNODE_LIST(RNODE_LIST(tail)->nd_next)->as.nd_end;
}
else {
- head->nd_next->nd_end = tail;
+ RNODE_LIST(RNODE_LIST(head)->nd_next)->as.nd_end = tail;
}
nd_set_last_loc(head, nd_last_loc(tail));
@@ -10680,40 +12859,51 @@ list_concat(NODE *head, NODE *tail)
}
static int
-literal_concat0(struct parser_params *p, VALUE head, VALUE tail)
+literal_concat0(struct parser_params *p, rb_parser_string_t *head, rb_parser_string_t *tail)
{
- if (NIL_P(tail)) return 1;
- if (!rb_enc_compatible(head, tail)) {
+ if (!tail) return 1;
+ if (!rb_parser_enc_compatible(p, head, tail)) {
compile_error(p, "string literal encodings differ (%s / %s)",
- rb_enc_name(rb_enc_get(head)),
- rb_enc_name(rb_enc_get(tail)));
- rb_str_resize(head, 0);
- rb_str_resize(tail, 0);
+ rb_enc_name(rb_parser_str_get_encoding(head)),
+ rb_enc_name(rb_parser_str_get_encoding(tail)));
+ rb_parser_str_resize(p, head, 0);
+ rb_parser_str_resize(p, tail, 0);
return 0;
}
- rb_str_buf_append(head, tail);
+ rb_parser_str_buf_append(p, head, tail);
return 1;
}
-static VALUE
-string_literal_head(enum node_type htype, NODE *head)
+static rb_parser_string_t *
+string_literal_head(struct parser_params *p, enum node_type htype, NODE *head)
{
- if (htype != NODE_DSTR) return Qfalse;
- if (head->nd_next) {
- head = head->nd_next->nd_end->nd_head;
- if (!head || !nd_type_p(head, NODE_STR)) return Qfalse;
+ if (htype != NODE_DSTR) return NULL;
+ if (RNODE_DSTR(head)->nd_next) {
+ head = RNODE_LIST(RNODE_LIST(RNODE_DSTR(head)->nd_next)->as.nd_end)->nd_head;
+ if (!head || !nd_type_p(head, NODE_STR)) return NULL;
}
- const VALUE lit = head->nd_lit;
- ASSUME(lit != Qfalse);
+ rb_parser_string_t *lit = RNODE_DSTR(head)->string;
+ ASSUME(lit);
return lit;
}
+static rb_parser_string_t *
+rb_parser_string_deep_copy(struct parser_params *p, const rb_parser_string_t *orig)
+{
+ rb_parser_string_t *copy;
+ if (!orig) return NULL;
+ copy = rb_parser_string_new(p, PARSER_STRING_PTR(orig), PARSER_STRING_LEN(orig));
+ copy->coderange = orig->coderange;
+ copy->enc = orig->enc;
+ return copy;
+}
+
/* concat two string literals */
static NODE *
literal_concat(struct parser_params *p, NODE *head, NODE *tail, const YYLTYPE *loc)
{
enum node_type htype;
- VALUE lit;
+ rb_parser_string_t *lit;
if (!head) return tail;
if (!tail) return head;
@@ -10726,7 +12916,7 @@ literal_concat(struct parser_params *p, NODE *head, NODE *tail, const YYLTYPE *l
if (p->heredoc_indent > 0) {
switch (htype) {
case NODE_STR:
- nd_set_type(head, NODE_DSTR);
+ head = str2dstr(p, head);
case NODE_DSTR:
return list_append(p, head, tail);
default:
@@ -10735,14 +12925,14 @@ literal_concat(struct parser_params *p, NODE *head, NODE *tail, const YYLTYPE *l
}
switch (nd_type(tail)) {
case NODE_STR:
- if ((lit = string_literal_head(htype, head)) != Qfalse) {
+ if ((lit = string_literal_head(p, htype, head)) != false) {
htype = NODE_STR;
}
else {
- lit = head->nd_lit;
+ lit = RNODE_DSTR(head)->string;
}
if (htype == NODE_STR) {
- if (!literal_concat0(p, lit, tail->nd_lit)) {
+ if (!literal_concat0(p, lit, RNODE_STR(tail)->string)) {
error:
rb_discard_node(p, head);
rb_discard_node(p, tail);
@@ -10757,39 +12947,43 @@ literal_concat(struct parser_params *p, NODE *head, NODE *tail, const YYLTYPE *l
case NODE_DSTR:
if (htype == NODE_STR) {
- if (!literal_concat0(p, head->nd_lit, tail->nd_lit))
+ if (!literal_concat0(p, RNODE_STR(head)->string, RNODE_DSTR(tail)->string))
goto error;
- tail->nd_lit = head->nd_lit;
+ rb_parser_string_free(p, RNODE_DSTR(tail)->string);
+ RNODE_DSTR(tail)->string = RNODE_STR(head)->string;
+ RNODE_STR(head)->string = NULL;
rb_discard_node(p, head);
head = tail;
}
- else if (NIL_P(tail->nd_lit)) {
+ else if (!RNODE_DSTR(tail)->string) {
append:
- head->nd_alen += tail->nd_alen - 1;
- if (!head->nd_next) {
- head->nd_next = tail->nd_next;
+ RNODE_DSTR(head)->as.nd_alen += RNODE_DSTR(tail)->as.nd_alen - 1;
+ if (!RNODE_DSTR(head)->nd_next) {
+ RNODE_DSTR(head)->nd_next = RNODE_DSTR(tail)->nd_next;
}
- else if (tail->nd_next) {
- head->nd_next->nd_end->nd_next = tail->nd_next;
- head->nd_next->nd_end = tail->nd_next->nd_end;
+ else if (RNODE_DSTR(tail)->nd_next) {
+ RNODE_DSTR(RNODE_DSTR(RNODE_DSTR(head)->nd_next)->as.nd_end)->nd_next = RNODE_DSTR(tail)->nd_next;
+ RNODE_DSTR(RNODE_DSTR(head)->nd_next)->as.nd_end = RNODE_DSTR(RNODE_DSTR(tail)->nd_next)->as.nd_end;
}
rb_discard_node(p, tail);
}
- else if ((lit = string_literal_head(htype, head)) != Qfalse) {
- if (!literal_concat0(p, lit, tail->nd_lit))
+ else if ((lit = string_literal_head(p, htype, head)) != false) {
+ if (!literal_concat0(p, lit, RNODE_DSTR(tail)->string))
goto error;
- tail->nd_lit = Qnil;
+ rb_parser_string_free(p, RNODE_DSTR(tail)->string);
+ RNODE_DSTR(tail)->string = 0;
goto append;
}
else {
- list_concat(head, NEW_NODE(NODE_LIST, NEW_STR(tail->nd_lit, loc), tail->nd_alen, tail->nd_next, loc));
+ list_concat(head, NEW_LIST2(NEW_STR(RNODE_DSTR(tail)->string, loc), RNODE_DSTR(tail)->as.nd_alen, (NODE *)RNODE_DSTR(tail)->nd_next, loc));
+ RNODE_DSTR(tail)->string = 0;
}
break;
case NODE_EVSTR:
if (htype == NODE_STR) {
- nd_set_type(head, NODE_DSTR);
- head->nd_alen = 1;
+ head = str2dstr(p, head);
+ RNODE_DSTR(head)->as.nd_alen = 1;
}
list_append(p, head, tail);
break;
@@ -10797,6 +12991,40 @@ literal_concat(struct parser_params *p, NODE *head, NODE *tail, const YYLTYPE *l
return head;
}
+static void
+nd_copy_flag(NODE *new_node, NODE *old_node)
+{
+ if (nd_fl_newline(old_node)) nd_set_fl_newline(new_node);
+ nd_set_line(new_node, nd_line(old_node));
+ new_node->nd_loc = old_node->nd_loc;
+ new_node->node_id = old_node->node_id;
+}
+
+static NODE *
+str2dstr(struct parser_params *p, NODE *node)
+{
+ NODE *new_node = (NODE *)NODE_NEW_INTERNAL(NODE_DSTR, rb_node_dstr_t);
+ nd_copy_flag(new_node, node);
+ RNODE_DSTR(new_node)->string = RNODE_STR(node)->string;
+ RNODE_DSTR(new_node)->as.nd_alen = 0;
+ RNODE_DSTR(new_node)->nd_next = 0;
+ RNODE_STR(node)->string = 0;
+
+ return new_node;
+}
+
+static NODE *
+str2regx(struct parser_params *p, NODE *node, int options)
+{
+ NODE *new_node = (NODE *)NODE_NEW_INTERNAL(NODE_REGX, rb_node_regx_t);
+ nd_copy_flag(new_node, node);
+ RNODE_REGX(new_node)->string = RNODE_STR(node)->string;
+ RNODE_REGX(new_node)->options = options;
+ RNODE_STR(node)->string = 0;
+
+ return new_node;
+}
+
static NODE *
evstr2dstr(struct parser_params *p, NODE *node)
{
@@ -10814,8 +13042,7 @@ new_evstr(struct parser_params *p, NODE *node, const YYLTYPE *loc)
if (node) {
switch (nd_type(node)) {
case NODE_STR:
- nd_set_type(node, NODE_DSTR);
- return node;
+ return str2dstr(p, node);
case NODE_DSTR:
break;
case NODE_EVSTR:
@@ -10828,9 +13055,7 @@ new_evstr(struct parser_params *p, NODE *node, const YYLTYPE *loc)
static NODE *
new_dstr(struct parser_params *p, NODE *node, const YYLTYPE *loc)
{
- VALUE lit = STR_NEW0();
- NODE *dstr = NEW_DSTR(lit, loc);
- RB_OBJ_WRITTEN(p->ast, Qnil, lit);
+ NODE *dstr = NEW_DSTR(STRING_NEW0(), loc);
return list_append(p, dstr, node);
}
@@ -10875,7 +13100,15 @@ new_command_qcall(struct parser_params* p, ID atype, NODE *recv, ID mid, NODE *a
return ret;
}
-#define nd_once_body(node) (nd_type_p((node), NODE_ONCE) ? (node)->nd_body : node)
+#define nd_once_body(node) (nd_type_p((node), NODE_ONCE) ? RNODE_ONCE(node)->nd_body : node)
+
+static NODE*
+last_expr_once_body(NODE *node)
+{
+ if (!node) return 0;
+ return nd_once_body(node);
+}
+
static NODE*
match_op(struct parser_params *p, NODE *node1, NODE *node2, const YYLTYPE *op_loc, const YYLTYPE *loc)
{
@@ -10884,7 +13117,8 @@ match_op(struct parser_params *p, NODE *node1, NODE *node2, const YYLTYPE *op_lo
value_expr(node1);
value_expr(node2);
- if (node1 && (n = nd_once_body(node1)) != 0) {
+
+ if ((n = last_expr_once_body(node1)) != 0) {
switch (nd_type(n)) {
case NODE_DREGX:
{
@@ -10893,24 +13127,23 @@ match_op(struct parser_params *p, NODE *node1, NODE *node2, const YYLTYPE *op_lo
return match;
}
- case NODE_LIT:
- if (RB_TYPE_P(n->nd_lit, T_REGEXP)) {
- const VALUE lit = n->nd_lit;
- NODE *match = NEW_MATCH2(node1, node2, loc);
- match->nd_args = reg_named_capture_assign(p, lit, loc);
- nd_set_line(match, line);
- return match;
+ case NODE_REGX:
+ {
+ const VALUE lit = rb_node_regx_string_val(n);
+ if (!NIL_P(lit)) {
+ NODE *match = NEW_MATCH2(node1, node2, loc);
+ RNODE_MATCH2(match)->nd_args = reg_named_capture_assign(p, lit, loc);
+ nd_set_line(match, line);
+ return match;
+ }
}
}
}
- if (node2 && (n = nd_once_body(node2)) != 0) {
+ if ((n = last_expr_once_body(node2)) != 0) {
NODE *match3;
switch (nd_type(n)) {
- case NODE_LIT:
- if (!RB_TYPE_P(n->nd_lit, T_REGEXP)) break;
- /* fallthru */
case NODE_DREGX:
match3 = NEW_MATCH3(node2, node1, loc);
return match3;
@@ -10953,6 +13186,34 @@ numparam_nested_p(struct parser_params *p)
return 0;
}
+static int
+numparam_used_p(struct parser_params *p)
+{
+ NODE *numparam = p->lvtbl->numparam.current;
+ if (numparam) {
+ compile_error(p, "numbered parameter is already used in\n"
+ "%s:%d: current block here",
+ p->ruby_sourcefile, nd_line(numparam));
+ parser_show_error_line(p, &numparam->nd_loc);
+ return 1;
+ }
+ return 0;
+}
+
+static int
+it_used_p(struct parser_params *p)
+{
+ NODE *it = p->lvtbl->it;
+ if (it) {
+ compile_error(p, "'it' is already used in\n"
+ "%s:%d: current block here",
+ p->ruby_sourcefile, nd_line(it));
+ parser_show_error_line(p, &it->nd_loc);
+ return 1;
+ }
+ return 0;
+}
+
static NODE*
gettable(struct parser_params *p, ID id, const YYLTYPE *loc)
{
@@ -10969,27 +13230,26 @@ gettable(struct parser_params *p, ID id, const YYLTYPE *loc)
return NEW_FALSE(loc);
case keyword__FILE__:
{
- VALUE file = p->ruby_sourcefile_string;
- if (NIL_P(file))
- file = rb_str_new(0, 0);
- else
- file = rb_str_dup(file);
- node = NEW_STR(file, loc);
- RB_OBJ_WRITTEN(p->ast, Qnil, file);
+ rb_parser_string_t *file;
+ if (p->ruby_sourcefile_string) {
+ file = rb_parser_string_deep_copy(p, p->ruby_sourcefile_string);
+ }
+ else {
+ file = STRING_NEW0();
+ }
+ node = NEW_FILE(file, loc);
}
return node;
case keyword__LINE__:
- return NEW_LIT(INT2FIX(p->tokline), loc);
+ return NEW_LINE(loc);
case keyword__ENCODING__:
- node = NEW_LIT(rb_enc_from_encoding(p->enc), loc);
- RB_OBJ_WRITTEN(p->ast, Qnil, node->nd_lit);
- return node;
+ return NEW_ENCODING(loc);
}
switch (id_type(id)) {
case ID_LOCAL:
if (dyna_in_block(p) && dvar_defined_ref(p, id, &vidp)) {
- if (NUMPARAM_ID_P(id) && numparam_nested_p(p)) return 0;
+ if (NUMPARAM_ID_P(id) && (numparam_nested_p(p) || it_used_p(p))) return 0;
if (id == p->cur_arg) {
compile_error(p, "circular argument reference - %"PRIsWARN, rb_id2str(id));
return 0;
@@ -11009,7 +13269,7 @@ gettable(struct parser_params *p, ID id, const YYLTYPE *loc)
}
if (dyna_in_block(p) && NUMPARAM_ID_P(id) &&
parser_numbered_param(p, NUMPARAM_ID_TO_IDX(id))) {
- if (numparam_nested_p(p)) return 0;
+ if (numparam_nested_p(p) || it_used_p(p)) return 0;
node = NEW_DVAR(id, loc);
struct local_vars *local = p->lvtbl;
if (!local->numparam.current) local->numparam.current = node;
@@ -11021,6 +13281,20 @@ gettable(struct parser_params *p, ID id, const YYLTYPE *loc)
}
# endif
/* method call without arguments */
+ if (dyna_in_block(p) && id == rb_intern("it") && !(DVARS_TERMINAL_P(p->lvtbl->args) || DVARS_TERMINAL_P(p->lvtbl->args->prev))) {
+ if (numparam_used_p(p)) return 0;
+ if (p->max_numparam == ORDINAL_PARAM) {
+ compile_error(p, "ordinary parameter is defined");
+ return 0;
+ }
+ if (!p->it_id) {
+ p->it_id = internal_id(p);
+ vtable_add(p->lvtbl->args, p->it_id);
+ }
+ NODE *node = NEW_DVAR(p->it_id, loc);
+ if (!p->lvtbl->it) p->lvtbl->it = node;
+ return node;
+ }
return NEW_VCALL(id, loc);
case ID_GLOBAL:
return NEW_GVAR(id, loc);
@@ -11035,26 +13309,27 @@ gettable(struct parser_params *p, ID id, const YYLTYPE *loc)
return 0;
}
-static NODE *
-opt_arg_append(NODE *opt_list, NODE *opt)
+static rb_node_opt_arg_t *
+opt_arg_append(rb_node_opt_arg_t *opt_list, rb_node_opt_arg_t *opt)
{
- NODE *opts = opt_list;
- opts->nd_loc.end_pos = opt->nd_loc.end_pos;
+ rb_node_opt_arg_t *opts = opt_list;
+ RNODE(opts)->nd_loc.end_pos = RNODE(opt)->nd_loc.end_pos;
while (opts->nd_next) {
opts = opts->nd_next;
- opts->nd_loc.end_pos = opt->nd_loc.end_pos;
+ RNODE(opts)->nd_loc.end_pos = RNODE(opt)->nd_loc.end_pos;
}
opts->nd_next = opt;
return opt_list;
}
-static NODE *
-kwd_append(NODE *kwlist, NODE *kw)
+static rb_node_kw_arg_t *
+kwd_append(rb_node_kw_arg_t *kwlist, rb_node_kw_arg_t *kw)
{
if (kwlist) {
- opt_arg_append(kwlist, kw);
+ /* Assume rb_node_kw_arg_t and rb_node_opt_arg_t has same structure */
+ opt_arg_append(RNODE_OPT_ARG(kwlist), RNODE_OPT_ARG(kw));
}
return kwlist;
}
@@ -11062,7 +13337,34 @@ kwd_append(NODE *kwlist, NODE *kw)
static NODE *
new_defined(struct parser_params *p, NODE *expr, const YYLTYPE *loc)
{
- return NEW_DEFINED(remove_begin_all(expr), loc);
+ NODE *n = expr;
+ while (n) {
+ if (nd_type_p(n, NODE_BEGIN)) {
+ n = RNODE_BEGIN(n)->nd_body;
+ }
+ else if (nd_type_p(n, NODE_BLOCK) && RNODE_BLOCK(n)->nd_end == n) {
+ n = RNODE_BLOCK(n)->nd_head;
+ }
+ else {
+ break;
+ }
+ }
+ return NEW_DEFINED(n, loc);
+}
+
+static NODE*
+str_to_sym_node(struct parser_params *p, NODE *node, const YYLTYPE *loc)
+{
+ VALUE lit;
+ rb_parser_string_t *str = RNODE_STR(node)->string;
+ if (rb_parser_enc_str_coderange(p, str) == RB_PARSER_ENC_CODERANGE_BROKEN) {
+ yyerror1(loc, "invalid symbol");
+ lit = STR_NEW0();
+ }
+ else {
+ lit = rb_str_new_parser_string(str);
+ }
+ return NEW_SYM(lit, loc);
}
static NODE*
@@ -11074,11 +13376,10 @@ symbol_append(struct parser_params *p, NODE *symbols, NODE *symbol)
nd_set_type(symbol, NODE_DSYM);
break;
case NODE_STR:
- nd_set_type(symbol, NODE_LIT);
- RB_OBJ_WRITTEN(p->ast, Qnil, symbol->nd_lit = rb_str_intern(symbol->nd_lit));
+ symbol = str_to_sym_node(p, symbol, &RNODE(symbol)->nd_loc);
break;
default:
- compile_error(p, "unexpected node as symbol: %s", ruby_node_name(type));
+ compile_error(p, "unexpected node as symbol: %s", parser_node_name(type));
}
return list_append(p, symbols, symbol);
}
@@ -11086,84 +13387,80 @@ symbol_append(struct parser_params *p, NODE *symbols, NODE *symbol)
static NODE *
new_regexp(struct parser_params *p, NODE *node, int options, const YYLTYPE *loc)
{
- NODE *list, *prev;
- VALUE lit;
+ struct RNode_LIST *list;
+ NODE *prev;
if (!node) {
- node = NEW_LIT(reg_compile(p, STR_NEW0(), options), loc);
- RB_OBJ_WRITTEN(p->ast, Qnil, node->nd_lit);
+ /* Check string is valid regex */
+ rb_parser_string_t *str = STRING_NEW0();
+ reg_compile(p, str, options);
+ node = NEW_REGX(str, options, loc);
return node;
}
switch (nd_type(node)) {
case NODE_STR:
{
- VALUE src = node->nd_lit;
- nd_set_type(node, NODE_LIT);
- nd_set_loc(node, loc);
- RB_OBJ_WRITTEN(p->ast, Qnil, node->nd_lit = reg_compile(p, src, options));
+ /* Check string is valid regex */
+ reg_compile(p, RNODE_STR(node)->string, options);
+ node = str2regx(p, node, options);
}
break;
default:
- lit = STR_NEW0();
- node = NEW_NODE(NODE_DSTR, lit, 1, NEW_LIST(node, loc), loc);
- RB_OBJ_WRITTEN(p->ast, Qnil, lit);
+ node = NEW_DSTR0(STRING_NEW0(), 1, NEW_LIST(node, loc), loc);
/* fall through */
case NODE_DSTR:
nd_set_type(node, NODE_DREGX);
nd_set_loc(node, loc);
- node->nd_cflag = options & RE_OPTION_MASK;
- if (!NIL_P(node->nd_lit)) reg_fragment_check(p, node->nd_lit, options);
- for (list = (prev = node)->nd_next; list; list = list->nd_next) {
+ RNODE_DREGX(node)->nd_cflag = options & RE_OPTION_MASK;
+ if (RNODE_DREGX(node)->string) reg_fragment_check(p, RNODE_DREGX(node)->string, options);
+ for (list = RNODE_DREGX(prev = node)->nd_next; list; list = RNODE_LIST(list->nd_next)) {
NODE *frag = list->nd_head;
enum node_type type = nd_type(frag);
- if (type == NODE_STR || (type == NODE_DSTR && !frag->nd_next)) {
- VALUE tail = frag->nd_lit;
- if (reg_fragment_check(p, tail, options) && prev && !NIL_P(prev->nd_lit)) {
- VALUE lit = prev == node ? prev->nd_lit : prev->nd_head->nd_lit;
+ if (type == NODE_STR || (type == NODE_DSTR && !RNODE_DSTR(frag)->nd_next)) {
+ rb_parser_string_t *tail = RNODE_STR(frag)->string;
+ if (reg_fragment_check(p, tail, options) && prev && RNODE_DREGX(prev)->string) {
+ rb_parser_string_t *lit = prev == node ? RNODE_DREGX(prev)->string : RNODE_STR(RNODE_LIST(prev)->nd_head)->string;
if (!literal_concat0(p, lit, tail)) {
return NEW_NIL(loc); /* dummy node on error */
}
- rb_str_resize(tail, 0);
- prev->nd_next = list->nd_next;
+ rb_parser_str_resize(p, tail, 0);
+ RNODE_LIST(prev)->nd_next = list->nd_next;
rb_discard_node(p, list->nd_head);
- rb_discard_node(p, list);
- list = prev;
+ rb_discard_node(p, (NODE *)list);
+ list = RNODE_LIST(prev);
}
else {
- prev = list;
+ prev = (NODE *)list;
}
}
else {
prev = 0;
}
}
- if (!node->nd_next) {
- VALUE src = node->nd_lit;
- nd_set_type(node, NODE_LIT);
- RB_OBJ_WRITTEN(p->ast, Qnil, node->nd_lit = reg_compile(p, src, options));
+ if (!RNODE_DREGX(node)->nd_next) {
+ /* Check string is valid regex */
+ reg_compile(p, RNODE_DREGX(node)->string, options);
}
if (options & RE_OPTION_ONCE) {
- node = NEW_NODE(NODE_ONCE, 0, node, 0, loc);
+ node = NEW_ONCE(node, loc);
}
break;
}
return node;
}
-static NODE *
+static rb_node_kw_arg_t *
new_kw_arg(struct parser_params *p, NODE *k, const YYLTYPE *loc)
{
if (!k) return 0;
- return NEW_KW_ARG(0, (k), loc);
+ return NEW_KW_ARG((k), loc);
}
static NODE *
new_xstring(struct parser_params *p, NODE *node, const YYLTYPE *loc)
{
if (!node) {
- VALUE lit = STR_NEW0();
- NODE *xstr = NEW_XSTR(lit, loc);
- RB_OBJ_WRITTEN(p->ast, Qnil, lit);
+ NODE *xstr = NEW_XSTR(STRING_NEW0(), loc);
return xstr;
}
switch (nd_type(node)) {
@@ -11176,40 +13473,42 @@ new_xstring(struct parser_params *p, NODE *node, const YYLTYPE *loc)
nd_set_loc(node, loc);
break;
default:
- node = NEW_NODE(NODE_DXSTR, Qnil, 1, NEW_LIST(node, loc), loc);
+ node = NEW_DXSTR(0, 1, NEW_LIST(node, loc), loc);
break;
}
return node;
}
+static const
+struct st_hash_type literal_type = {
+ literal_cmp,
+ literal_hash,
+};
+
+static int nd_type_st_key_enable_p(NODE *node);
+
static void
check_literal_when(struct parser_params *p, NODE *arg, const YYLTYPE *loc)
{
- VALUE lit;
-
+ /* See https://bugs.ruby-lang.org/issues/20331 for discussion about what is warned. */
if (!arg || !p->case_labels) return;
+ if (!nd_type_st_key_enable_p(arg)) return;
- lit = rb_node_case_when_optimizable_literal(arg);
- if (UNDEF_P(lit)) return;
- if (nd_type_p(arg, NODE_STR)) {
- RB_OBJ_WRITTEN(p->ast, Qnil, arg->nd_lit = lit);
- }
-
- if (NIL_P(p->case_labels)) {
- p->case_labels = rb_obj_hide(rb_hash_new());
+ if (p->case_labels == CHECK_LITERAL_WHEN) {
+ p->case_labels = st_init_table(&literal_type);
}
else {
- VALUE line = rb_hash_lookup(p->case_labels, lit);
- if (!NIL_P(line)) {
- rb_warning1("duplicated `when' clause with line %d is ignored",
- WARN_IVAL(line));
+ st_data_t line;
+ if (st_lookup(p->case_labels, (st_data_t)arg, &line)) {
+ rb_warning1("duplicated 'when' clause with line %d is ignored",
+ WARN_I((int)line));
return;
}
}
- rb_hash_aset(p->case_labels, lit, INT2NUM(p->ruby_sourceline));
+ st_insert(p->case_labels, (st_data_t)arg, (st_data_t)p->ruby_sourceline);
}
-#else /* !RIPPER */
+#ifdef RIPPER
static int
id_is_var(struct parser_params *p, ID id)
{
@@ -11229,26 +13528,7 @@ id_is_var(struct parser_params *p, ID id)
compile_error(p, "identifier %"PRIsVALUE" is not valid to get", rb_id2str(id));
return 0;
}
-
-static VALUE
-new_regexp(struct parser_params *p, VALUE re, VALUE opt, const YYLTYPE *loc)
-{
- VALUE src = 0, err;
- int options = 0;
- if (ripper_is_node_yylval(re)) {
- src = RNODE(re)->nd_cval;
- re = RNODE(re)->nd_rval;
- }
- if (ripper_is_node_yylval(opt)) {
- options = (int)RNODE(opt)->nd_tag;
- opt = RNODE(opt)->nd_rval;
- }
- if (src && NIL_P(parser_reg_compile(p, src, options, &err))) {
- compile_error(p, "%"PRIsVALUE, err);
- }
- return dispatch2(regexp_literal, re, opt);
-}
-#endif /* !RIPPER */
+#endif
static inline enum lex_state_e
parser_set_lex_state(struct parser_params *p, enum lex_state_e ls, int line)
@@ -11260,6 +13540,20 @@ parser_set_lex_state(struct parser_params *p, enum lex_state_e ls, int line)
}
#ifndef RIPPER
+static void
+flush_debug_buffer(struct parser_params *p, VALUE out, VALUE str)
+{
+ VALUE mesg = p->debug_buffer;
+
+ if (!NIL_P(mesg) && RSTRING_LEN(mesg)) {
+ p->debug_buffer = Qnil;
+ rb_io_puts(1, &mesg, out);
+ }
+ if (!NIL_P(str) && RSTRING_LEN(str)) {
+ rb_io_write(p->debug_output, str);
+ }
+}
+
static const char rb_parser_lex_state_names[][8] = {
"BEG", "END", "ENDARG", "ENDFN", "ARG",
"CMDARG", "MID", "FNAME", "DOT", "CLASS",
@@ -11267,7 +13561,7 @@ static const char rb_parser_lex_state_names[][8] = {
};
static VALUE
-append_lex_state_name(enum lex_state_e state, VALUE buf)
+append_lex_state_name(struct parser_params *p, enum lex_state_e state, VALUE buf)
{
int i, sep = 0;
unsigned int mask = 1;
@@ -11288,42 +13582,28 @@ append_lex_state_name(enum lex_state_e state, VALUE buf)
return buf;
}
-static void
-flush_debug_buffer(struct parser_params *p, VALUE out, VALUE str)
-{
- VALUE mesg = p->debug_buffer;
-
- if (!NIL_P(mesg) && RSTRING_LEN(mesg)) {
- p->debug_buffer = Qnil;
- rb_io_puts(1, &mesg, out);
- }
- if (!NIL_P(str) && RSTRING_LEN(str)) {
- rb_io_write(p->debug_output, str);
- }
-}
-
enum lex_state_e
rb_parser_trace_lex_state(struct parser_params *p, enum lex_state_e from,
enum lex_state_e to, int line)
{
VALUE mesg;
mesg = rb_str_new_cstr("lex_state: ");
- append_lex_state_name(from, mesg);
+ append_lex_state_name(p, from, mesg);
rb_str_cat_cstr(mesg, " -> ");
- append_lex_state_name(to, mesg);
+ append_lex_state_name(p, to, mesg);
rb_str_catf(mesg, " at line %d\n", line);
flush_debug_buffer(p, p->debug_output, mesg);
return to;
}
VALUE
-rb_parser_lex_state_name(enum lex_state_e state)
+rb_parser_lex_state_name(struct parser_params *p, enum lex_state_e state)
{
- return rb_fstring(append_lex_state_name(state, rb_str_new(0, 0)));
+ return rb_str_to_interned_str(append_lex_state_name(p, state, rb_str_new(0, 0)));
}
static void
-append_bitstack_value(stack_type stack, VALUE mesg)
+append_bitstack_value(struct parser_params *p, stack_type stack, VALUE mesg)
{
if (stack == 0) {
rb_str_cat_cstr(mesg, "0");
@@ -11340,7 +13620,7 @@ rb_parser_show_bitstack(struct parser_params *p, stack_type stack,
const char *name, int line)
{
VALUE mesg = rb_sprintf("%s: ", name);
- append_bitstack_value(stack, mesg);
+ append_bitstack_value(p, stack, mesg);
rb_str_catf(mesg, " at line %d\n", line);
flush_debug_buffer(p, p->debug_output, mesg);
}
@@ -11358,13 +13638,13 @@ rb_parser_fatal(struct parser_params *p, const char *fmt, ...)
RB_GC_GUARD(mesg);
mesg = rb_str_new(0, 0);
- append_lex_state_name(p->lex.state, mesg);
+ append_lex_state_name(p, p->lex.state, mesg);
compile_error(p, "lex.state: %"PRIsVALUE, mesg);
rb_str_resize(mesg, 0);
- append_bitstack_value(p->cond_stack, mesg);
+ append_bitstack_value(p, p->cond_stack, mesg);
compile_error(p, "cond_stack: %"PRIsVALUE, mesg);
rb_str_resize(mesg, 0);
- append_bitstack_value(p->cmdarg_stack, mesg);
+ append_bitstack_value(p, p->cmdarg_stack, mesg);
compile_error(p, "cmdarg_stack: %"PRIsVALUE, mesg);
if (p->debug_output == rb_ractor_stdout())
p->debug_output = rb_ractor_stderr();
@@ -11498,7 +13778,6 @@ assignable0(struct parser_params *p, ID id, const char **err)
return -1;
}
-#ifndef RIPPER
static NODE*
assignable(struct parser_params *p, ID id, NODE *val, const YYLTYPE *loc)
{
@@ -11509,25 +13788,28 @@ assignable(struct parser_params *p, ID id, NODE *val, const YYLTYPE *loc)
case NODE_LASGN: return NEW_LASGN(id, val, loc);
case NODE_GASGN: return NEW_GASGN(id, val, loc);
case NODE_IASGN: return NEW_IASGN(id, val, loc);
- case NODE_CDECL: return NEW_CDECL(id, val, 0, loc);
+ case NODE_CDECL: return NEW_CDECL(id, val, 0, p->ctxt.shareable_constant_value, loc);
case NODE_CVASGN: return NEW_CVASGN(id, val, loc);
}
+/* TODO: FIXME */
+#ifndef RIPPER
if (err) yyerror1(loc, err);
- return NEW_BEGIN(0, loc);
+#endif
+ return NEW_ERROR(loc);
}
-#else
+#ifdef RIPPER
static VALUE
-assignable(struct parser_params *p, VALUE lhs)
+ripper_assignable(struct parser_params *p, ID id, VALUE lhs)
{
const char *err = 0;
- assignable0(p, get_id(lhs), &err);
+ assignable0(p, id, &err);
if (err) lhs = assign_error(p, err, lhs);
return lhs;
}
#endif
static int
-is_private_local_id(ID name)
+is_private_local_id(struct parser_params *p, ID name)
{
VALUE s;
if (name == idUScore) return 1;
@@ -11542,7 +13824,7 @@ shadowing_lvar_0(struct parser_params *p, ID name)
{
if (dyna_in_block(p)) {
if (dvar_curr(p, name)) {
- if (is_private_local_id(name)) return 1;
+ if (is_private_local_id(p, name)) return 1;
yyerror0("duplicated argument name");
}
else if (dvar_defined(p, name) || local_id(p, name)) {
@@ -11555,7 +13837,7 @@ shadowing_lvar_0(struct parser_params *p, ID name)
}
else {
if (local_id(p, name)) {
- if (is_private_local_id(name)) return 1;
+ if (is_private_local_id(p, name)) return 1;
yyerror0("duplicated argument name");
}
}
@@ -11580,12 +13862,44 @@ new_bv(struct parser_params *p, ID name)
}
if (!shadowing_lvar_0(p, name)) return;
dyna_var(p, name);
+ ID *vidp = 0;
+ if (dvar_defined_ref(p, name, &vidp)) {
+ if (vidp) *vidp |= LVAR_USED;
+ }
+}
+
+static void
+aryset_check(struct parser_params *p, NODE *args)
+{
+ NODE *block = 0, *kwds = 0;
+ if (args && nd_type_p(args, NODE_BLOCK_PASS)) {
+ block = RNODE_BLOCK_PASS(args)->nd_body;
+ args = RNODE_BLOCK_PASS(args)->nd_head;
+ }
+ if (args && nd_type_p(args, NODE_ARGSCAT)) {
+ args = RNODE_ARGSCAT(args)->nd_body;
+ }
+ if (args && nd_type_p(args, NODE_ARGSPUSH)) {
+ kwds = RNODE_ARGSPUSH(args)->nd_body;
+ }
+ else {
+ for (NODE *next = args; next && nd_type_p(next, NODE_LIST);
+ next = RNODE_LIST(next)->nd_next) {
+ kwds = RNODE_LIST(next)->nd_head;
+ }
+ }
+ if (kwds && nd_type_p(kwds, NODE_HASH) && !RNODE_HASH(kwds)->nd_brace) {
+ yyerror1(&kwds->nd_loc, "keyword arg given in index");
+ }
+ if (block) {
+ yyerror1(&block->nd_loc, "block arg given in index");
+ }
}
-#ifndef RIPPER
static NODE *
aryset(struct parser_params *p, NODE *recv, NODE *idx, const YYLTYPE *loc)
{
+ aryset_check(p, idx);
return NEW_ATTRASGN(recv, tASET, idx, loc);
}
@@ -11609,24 +13923,42 @@ rb_backref_error(struct parser_params *p, NODE *node)
{
switch (nd_type(node)) {
case NODE_NTH_REF:
- compile_error(p, "Can't set variable $%ld", node->nd_nth);
+ compile_error(p, "Can't set variable $%ld", RNODE_NTH_REF(node)->nd_nth);
break;
case NODE_BACK_REF:
- compile_error(p, "Can't set variable $%c", (int)node->nd_nth);
+ compile_error(p, "Can't set variable $%c", (int)RNODE_BACK_REF(node)->nd_nth);
break;
}
}
-#else
+
+#ifdef RIPPER
+static VALUE
+defs(struct parser_params *p, VALUE head, VALUE args, VALUE bodystmt)
+{
+ return dispatch5(defs,
+ rb_ary_entry(head, 0), /* nd_recv */
+ rb_ary_entry(head, 1), /* dot_or_colon */
+ rb_ary_entry(head, 2), /* nd_mid */
+ args,
+ bodystmt);
+}
+
static VALUE
-backref_error(struct parser_params *p, NODE *ref, VALUE expr)
+backref_error(struct parser_params *p, NODE *node, VALUE expr)
{
VALUE mesg = rb_str_new_cstr("Can't set variable ");
- rb_str_append(mesg, ref->nd_cval);
+ switch (nd_type(node)) {
+ case NODE_NTH_REF:
+ rb_str_catf(mesg, "$%ld", RNODE_NTH_REF(node)->nd_nth);
+ break;
+ case NODE_BACK_REF:
+ rb_str_catf(mesg, "$%c", (int)RNODE_BACK_REF(node)->nd_nth);
+ break;
+ }
return dispatch2(assign_error, mesg, expr);
}
#endif
-#ifndef RIPPER
static NODE *
arg_append(struct parser_params *p, NODE *node1, NODE *node2, const YYLTYPE *loc)
{
@@ -11635,18 +13967,18 @@ arg_append(struct parser_params *p, NODE *node1, NODE *node2, const YYLTYPE *loc
case NODE_LIST:
return list_append(p, node1, node2);
case NODE_BLOCK_PASS:
- node1->nd_head = arg_append(p, node1->nd_head, node2, loc);
- node1->nd_loc.end_pos = node1->nd_head->nd_loc.end_pos;
+ RNODE_BLOCK_PASS(node1)->nd_head = arg_append(p, RNODE_BLOCK_PASS(node1)->nd_head, node2, loc);
+ node1->nd_loc.end_pos = RNODE_BLOCK_PASS(node1)->nd_head->nd_loc.end_pos;
return node1;
case NODE_ARGSPUSH:
- node1->nd_body = list_append(p, NEW_LIST(node1->nd_body, &node1->nd_body->nd_loc), node2);
- node1->nd_loc.end_pos = node1->nd_body->nd_loc.end_pos;
+ RNODE_ARGSPUSH(node1)->nd_body = list_append(p, NEW_LIST(RNODE_ARGSPUSH(node1)->nd_body, &RNODE_ARGSPUSH(node1)->nd_body->nd_loc), node2);
+ node1->nd_loc.end_pos = RNODE_ARGSPUSH(node1)->nd_body->nd_loc.end_pos;
nd_set_type(node1, NODE_ARGSCAT);
return node1;
case NODE_ARGSCAT:
- if (!nd_type_p(node1->nd_body, NODE_LIST)) break;
- node1->nd_body = list_append(p, node1->nd_body, node2);
- node1->nd_loc.end_pos = node1->nd_body->nd_loc.end_pos;
+ if (!nd_type_p(RNODE_ARGSCAT(node1)->nd_body, NODE_LIST)) break;
+ RNODE_ARGSCAT(node1)->nd_body = list_append(p, RNODE_ARGSCAT(node1)->nd_body, node2);
+ node1->nd_loc.end_pos = RNODE_ARGSCAT(node1)->nd_body->nd_loc.end_pos;
return node1;
}
return NEW_ARGSPUSH(node1, node2, loc);
@@ -11658,20 +13990,20 @@ arg_concat(struct parser_params *p, NODE *node1, NODE *node2, const YYLTYPE *loc
if (!node2) return node1;
switch (nd_type(node1)) {
case NODE_BLOCK_PASS:
- if (node1->nd_head)
- node1->nd_head = arg_concat(p, node1->nd_head, node2, loc);
+ if (RNODE_BLOCK_PASS(node1)->nd_head)
+ RNODE_BLOCK_PASS(node1)->nd_head = arg_concat(p, RNODE_BLOCK_PASS(node1)->nd_head, node2, loc);
else
- node1->nd_head = NEW_LIST(node2, loc);
+ RNODE_LIST(node1)->nd_head = NEW_LIST(node2, loc);
return node1;
case NODE_ARGSPUSH:
if (!nd_type_p(node2, NODE_LIST)) break;
- node1->nd_body = list_concat(NEW_LIST(node1->nd_body, loc), node2);
+ RNODE_ARGSPUSH(node1)->nd_body = list_concat(NEW_LIST(RNODE_ARGSPUSH(node1)->nd_body, loc), node2);
nd_set_type(node1, NODE_ARGSCAT);
return node1;
case NODE_ARGSCAT:
if (!nd_type_p(node2, NODE_LIST) ||
- !nd_type_p(node1->nd_body, NODE_LIST)) break;
- node1->nd_body = list_concat(node1->nd_body, node2);
+ !nd_type_p(RNODE_ARGSCAT(node1)->nd_body, NODE_LIST)) break;
+ RNODE_ARGSCAT(node1)->nd_body = list_concat(RNODE_ARGSCAT(node1)->nd_body, node2);
return node1;
}
return NEW_ARGSCAT(node1, node2, loc);
@@ -11700,7 +14032,7 @@ rest_arg_append(struct parser_params *p, NODE *args, NODE *rest_arg, const YYLTY
static NODE *
splat_array(NODE* node)
{
- if (nd_type_p(node, NODE_SPLAT)) node = node->nd_head;
+ if (nd_type_p(node, NODE_SPLAT)) node = RNODE_SPLAT(node)->nd_head;
if (nd_type_p(node, NODE_LIST)) return node;
return 0;
}
@@ -11712,12 +14044,12 @@ mark_lvar_used(struct parser_params *p, NODE *rhs)
if (!rhs) return;
switch (nd_type(rhs)) {
case NODE_LASGN:
- if (local_id_ref(p, rhs->nd_vid, &vidp)) {
+ if (local_id_ref(p, RNODE_LASGN(rhs)->nd_vid, &vidp)) {
if (vidp) *vidp |= LVAR_USED;
}
break;
case NODE_DASGN:
- if (dvar_defined_ref(p, rhs->nd_vid, &vidp)) {
+ if (dvar_defined_ref(p, RNODE_DASGN(rhs)->nd_vid, &vidp)) {
if (vidp) *vidp |= LVAR_USED;
}
break;
@@ -11731,252 +14063,8 @@ mark_lvar_used(struct parser_params *p, NODE *rhs)
}
}
-static NODE *
-const_decl_path(struct parser_params *p, NODE **dest)
-{
- NODE *n = *dest;
- if (!nd_type_p(n, NODE_CALL)) {
- const YYLTYPE *loc = &n->nd_loc;
- VALUE path;
- if (n->nd_vid) {
- path = rb_id2str(n->nd_vid);
- }
- else {
- n = n->nd_else;
- path = rb_ary_new();
- for (; n && nd_type_p(n, NODE_COLON2); n = n->nd_head) {
- rb_ary_push(path, rb_id2str(n->nd_mid));
- }
- if (n && nd_type_p(n, NODE_CONST)) {
- // Const::Name
- rb_ary_push(path, rb_id2str(n->nd_vid));
- }
- else if (n && nd_type_p(n, NODE_COLON3)) {
- // ::Const::Name
- rb_ary_push(path, rb_str_new(0, 0));
- }
- else {
- // expression::Name
- rb_ary_push(path, rb_str_new_cstr("..."));
- }
- path = rb_ary_join(rb_ary_reverse(path), rb_str_new_cstr("::"));
- path = rb_fstring(path);
- }
- *dest = n = NEW_LIT(path, loc);
- RB_OBJ_WRITTEN(p->ast, Qnil, n->nd_lit);
- }
- return n;
-}
-
-extern VALUE rb_mRubyVMFrozenCore;
-
-static NODE *
-make_shareable_node(struct parser_params *p, NODE *value, bool copy, const YYLTYPE *loc)
-{
- NODE *fcore = NEW_LIT(rb_mRubyVMFrozenCore, loc);
-
- if (copy) {
- return NEW_CALL(fcore, rb_intern("make_shareable_copy"),
- NEW_LIST(value, loc), loc);
- }
- else {
- return NEW_CALL(fcore, rb_intern("make_shareable"),
- NEW_LIST(value, loc), loc);
- }
-}
-
-static NODE *
-ensure_shareable_node(struct parser_params *p, NODE **dest, NODE *value, const YYLTYPE *loc)
-{
- NODE *fcore = NEW_LIT(rb_mRubyVMFrozenCore, loc);
- NODE *args = NEW_LIST(value, loc);
- args = list_append(p, args, const_decl_path(p, dest));
- return NEW_CALL(fcore, rb_intern("ensure_shareable"), args, loc);
-}
-
static int is_static_content(NODE *node);
-static VALUE
-shareable_literal_value(NODE *node)
-{
- if (!node) return Qnil;
- enum node_type type = nd_type(node);
- switch (type) {
- case NODE_TRUE:
- return Qtrue;
- case NODE_FALSE:
- return Qfalse;
- case NODE_NIL:
- return Qnil;
- case NODE_LIT:
- return node->nd_lit;
- default:
- return Qundef;
- }
-}
-
-#ifndef SHAREABLE_BARE_EXPRESSION
-#define SHAREABLE_BARE_EXPRESSION 1
-#endif
-
-static NODE *
-shareable_literal_constant(struct parser_params *p, enum shareability shareable,
- NODE **dest, NODE *value, const YYLTYPE *loc, size_t level)
-{
-# define shareable_literal_constant_next(n) \
- shareable_literal_constant(p, shareable, dest, (n), &(n)->nd_loc, level+1)
- VALUE lit = Qnil;
-
- if (!value) return 0;
- enum node_type type = nd_type(value);
- switch (type) {
- case NODE_TRUE:
- case NODE_FALSE:
- case NODE_NIL:
- case NODE_LIT:
- return value;
-
- case NODE_DSTR:
- if (shareable == shareable_literal) {
- value = NEW_CALL(value, idUMinus, 0, loc);
- }
- return value;
-
- case NODE_STR:
- lit = rb_fstring(value->nd_lit);
- nd_set_type(value, NODE_LIT);
- RB_OBJ_WRITE(p->ast, &value->nd_lit, lit);
- return value;
-
- case NODE_ZLIST:
- lit = rb_ary_new();
- OBJ_FREEZE_RAW(lit);
- NODE *n = NEW_LIT(lit, loc);
- RB_OBJ_WRITTEN(p->ast, Qnil, n->nd_lit);
- return n;
-
- case NODE_LIST:
- lit = rb_ary_new();
- for (NODE *n = value; n; n = n->nd_next) {
- NODE *elt = n->nd_head;
- if (elt) {
- elt = shareable_literal_constant_next(elt);
- if (elt) {
- n->nd_head = elt;
- }
- else if (RTEST(lit)) {
- rb_ary_clear(lit);
- lit = Qfalse;
- }
- }
- if (RTEST(lit)) {
- VALUE e = shareable_literal_value(elt);
- if (!UNDEF_P(e)) {
- rb_ary_push(lit, e);
- }
- else {
- rb_ary_clear(lit);
- lit = Qnil; /* make shareable at runtime */
- }
- }
- }
- break;
-
- case NODE_HASH:
- if (!value->nd_brace) return 0;
- lit = rb_hash_new();
- for (NODE *n = value->nd_head; n; n = n->nd_next->nd_next) {
- NODE *key = n->nd_head;
- NODE *val = n->nd_next->nd_head;
- if (key) {
- key = shareable_literal_constant_next(key);
- if (key) {
- n->nd_head = key;
- }
- else if (RTEST(lit)) {
- rb_hash_clear(lit);
- lit = Qfalse;
- }
- }
- if (val) {
- val = shareable_literal_constant_next(val);
- if (val) {
- n->nd_next->nd_head = val;
- }
- else if (RTEST(lit)) {
- rb_hash_clear(lit);
- lit = Qfalse;
- }
- }
- if (RTEST(lit)) {
- VALUE k = shareable_literal_value(key);
- VALUE v = shareable_literal_value(val);
- if (!UNDEF_P(k) && !UNDEF_P(v)) {
- rb_hash_aset(lit, k, v);
- }
- else {
- rb_hash_clear(lit);
- lit = Qnil; /* make shareable at runtime */
- }
- }
- }
- break;
-
- default:
- if (shareable == shareable_literal &&
- (SHAREABLE_BARE_EXPRESSION || level > 0)) {
- return ensure_shareable_node(p, dest, value, loc);
- }
- return 0;
- }
-
- /* Array or Hash */
- if (!lit) return 0;
- if (NIL_P(lit)) {
- // if shareable_literal, all elements should have been ensured
- // as shareable
- value = make_shareable_node(p, value, false, loc);
- }
- else {
- value = NEW_LIT(rb_ractor_make_shareable(lit), loc);
- RB_OBJ_WRITTEN(p->ast, Qnil, value->nd_lit);
- }
-
- return value;
-# undef shareable_literal_constant_next
-}
-
-static NODE *
-shareable_constant_value(struct parser_params *p, enum shareability shareable,
- NODE *lhs, NODE *value, const YYLTYPE *loc)
-{
- if (!value) return 0;
- switch (shareable) {
- case shareable_none:
- return value;
-
- case shareable_literal:
- {
- NODE *lit = shareable_literal_constant(p, shareable, &lhs, value, loc, 0);
- if (lit) return lit;
- return value;
- }
- break;
-
- case shareable_copy:
- case shareable_everything:
- {
- NODE *lit = shareable_literal_constant(p, shareable, &lhs, value, loc, 0);
- if (lit) return lit;
- return make_shareable_node(p, value, shareable == shareable_copy, loc);
- }
- break;
-
- default:
- UNREACHABLE_RETURN(0);
- }
-}
-
static NODE *
node_assign(struct parser_params *p, NODE *lhs, NODE *rhs, struct lex_context ctxt, const YYLTYPE *loc)
{
@@ -11984,21 +14072,18 @@ node_assign(struct parser_params *p, NODE *lhs, NODE *rhs, struct lex_context ct
switch (nd_type(lhs)) {
case NODE_CDECL:
- rhs = shareable_constant_value(p, ctxt.shareable_constant_value, lhs, rhs, loc);
- /* fallthru */
-
case NODE_GASGN:
case NODE_IASGN:
case NODE_LASGN:
case NODE_DASGN:
case NODE_MASGN:
case NODE_CVASGN:
- lhs->nd_value = rhs;
+ set_nd_value(p, lhs, rhs);
nd_set_loc(lhs, loc);
break;
case NODE_ATTRASGN:
- lhs->nd_args = arg_append(p, lhs->nd_args, rhs, loc);
+ RNODE_ATTRASGN(lhs)->nd_args = arg_append(p, RNODE_ATTRASGN(lhs)->nd_args, rhs, loc);
nd_set_loc(lhs, loc);
break;
@@ -12020,52 +14105,81 @@ value_expr_check(struct parser_params *p, NODE *node)
}
while (node) {
switch (nd_type(node)) {
+ case NODE_ENSURE:
+ vn = RNODE_ENSURE(node)->nd_head;
+ node = RNODE_ENSURE(node)->nd_ensr;
+ /* nd_ensr should not be NULL, check it out next */
+ if (vn && (vn = value_expr_check(p, vn))) {
+ goto found;
+ }
+ break;
+
+ case NODE_RESCUE:
+ /* void only if all children are void */
+ vn = RNODE_RESCUE(node)->nd_head;
+ if (!vn || !(vn = value_expr_check(p, vn))) return NULL;
+ if (!void_node) void_node = vn;
+ for (NODE *r = RNODE_RESCUE(node)->nd_resq; r; r = RNODE_RESBODY(r)->nd_next) {
+ if (!nd_type_p(r, NODE_RESBODY)) {
+ compile_error(p, "unexpected node");
+ return NULL;
+ }
+ if (!(vn = value_expr_check(p, RNODE_RESBODY(r)->nd_body))) {
+ void_node = 0;
+ break;
+ }
+ if (!void_node) void_node = vn;
+ }
+ node = RNODE_RESCUE(node)->nd_else;
+ if (!node) return void_node;
+ break;
+
case NODE_RETURN:
case NODE_BREAK:
case NODE_NEXT:
case NODE_REDO:
case NODE_RETRY:
- return void_node ? void_node : node;
+ goto found;
case NODE_CASE3:
- if (!node->nd_body || !nd_type_p(node->nd_body, NODE_IN)) {
+ if (!RNODE_CASE3(node)->nd_body || !nd_type_p(RNODE_CASE3(node)->nd_body, NODE_IN)) {
compile_error(p, "unexpected node");
return NULL;
}
- if (node->nd_body->nd_body) {
+ if (RNODE_IN(RNODE_CASE3(node)->nd_body)->nd_body) {
return NULL;
}
- /* single line pattern matching */
- return void_node ? void_node : node;
+ /* single line pattern matching with "=>" operator */
+ goto found;
case NODE_BLOCK:
- while (node->nd_next) {
- node = node->nd_next;
+ while (RNODE_BLOCK(node)->nd_next) {
+ node = RNODE_BLOCK(node)->nd_next;
}
- node = node->nd_head;
+ node = RNODE_BLOCK(node)->nd_head;
break;
case NODE_BEGIN:
- node = node->nd_body;
+ node = RNODE_BEGIN(node)->nd_body;
break;
case NODE_IF:
case NODE_UNLESS:
- if (!node->nd_body) {
+ if (!RNODE_IF(node)->nd_body) {
return NULL;
}
- else if (!node->nd_else) {
+ else if (!RNODE_IF(node)->nd_else) {
return NULL;
}
- vn = value_expr_check(p, node->nd_body);
+ vn = value_expr_check(p, RNODE_IF(node)->nd_body);
if (!vn) return NULL;
if (!void_node) void_node = vn;
- node = node->nd_else;
+ node = RNODE_IF(node)->nd_else;
break;
case NODE_AND:
case NODE_OR:
- node = node->nd_1st;
+ node = RNODE_AND(node)->nd_1st;
break;
case NODE_LASGN:
@@ -12080,6 +14194,10 @@ value_expr_check(struct parser_params *p, NODE *node)
}
return NULL;
+
+ found:
+ /* return the first found node */
+ return void_node ? void_node : node;
}
static int
@@ -12093,6 +14211,7 @@ value_expr_gen(struct parser_params *p, NODE *node)
}
return TRUE;
}
+
static void
void_expr(struct parser_params *p, NODE *node)
{
@@ -12103,7 +14222,7 @@ void_expr(struct parser_params *p, NODE *node)
if (!node || !(node = nd_once_body(node))) return;
switch (nd_type(node)) {
case NODE_OPCALL:
- switch (node->nd_mid) {
+ switch (RNODE_OPCALL(node)->nd_mid) {
case '+':
case '-':
case '*':
@@ -12122,7 +14241,7 @@ void_expr(struct parser_params *p, NODE *node)
case tLEQ:
case tEQ:
case tNEQ:
- useless = rb_id2name(node->nd_mid);
+ useless = rb_id2name(RNODE_OPCALL(node)->nd_mid);
break;
}
break;
@@ -12139,9 +14258,17 @@ void_expr(struct parser_params *p, NODE *node)
case NODE_CONST:
useless = "a constant";
break;
- case NODE_LIT:
+ case NODE_SYM:
+ case NODE_LINE:
+ case NODE_FILE:
+ case NODE_ENCODING:
+ case NODE_INTEGER:
+ case NODE_FLOAT:
+ case NODE_RATIONAL:
+ case NODE_IMAGINARY:
case NODE_STR:
case NODE_DSTR:
+ case NODE_REGX:
case NODE_DREGX:
useless = "a literal";
break;
@@ -12185,9 +14312,9 @@ void_stmts(struct parser_params *p, NODE *node)
if (!node) return n;
if (!nd_type_p(node, NODE_BLOCK)) return n;
- while (node->nd_next) {
- void_expr(p, node->nd_head);
- node = node->nd_next;
+ while (RNODE_BLOCK(node)->nd_next) {
+ void_expr(p, RNODE_BLOCK(node)->nd_head);
+ node = RNODE_BLOCK(node)->nd_next;
}
return n;
}
@@ -12196,18 +14323,8 @@ static NODE *
remove_begin(NODE *node)
{
NODE **n = &node, *n1 = node;
- while (n1 && nd_type_p(n1, NODE_BEGIN) && n1->nd_body) {
- *n = n1 = n1->nd_body;
- }
- return node;
-}
-
-static NODE *
-remove_begin_all(NODE *node)
-{
- NODE **n = &node, *n1 = node;
- while (n1 && nd_type_p(n1, NODE_BEGIN)) {
- *n = n1 = n1->nd_body;
+ while (n1 && nd_type_p(n1, NODE_BEGIN) && RNODE_BEGIN(n1)->nd_body) {
+ *n = n1 = RNODE_BEGIN(n1)->nd_body;
}
return node;
}
@@ -12221,54 +14338,55 @@ reduce_nodes(struct parser_params *p, NODE **body)
*body = NEW_NIL(&NULL_LOC);
return;
}
-#define subnodes(n1, n2) \
- ((!node->n1) ? (node->n2 ? (body = &node->n2, 1) : 0) : \
- (!node->n2) ? (body = &node->n1, 1) : \
- (reduce_nodes(p, &node->n1), body = &node->n2, 1))
+#define subnodes(type, n1, n2) \
+ ((!type(node)->n1) ? (type(node)->n2 ? (body = &type(node)->n2, 1) : 0) : \
+ (!type(node)->n2) ? (body = &type(node)->n1, 1) : \
+ (reduce_nodes(p, &type(node)->n1), body = &type(node)->n2, 1))
while (node) {
- int newline = (int)(node->flags & NODE_FL_NEWLINE);
+ int newline = (int)(nd_fl_newline(node));
switch (nd_type(node)) {
end:
case NODE_NIL:
*body = 0;
return;
case NODE_RETURN:
- *body = node = node->nd_stts;
- if (newline && node) node->flags |= NODE_FL_NEWLINE;
+ *body = node = RNODE_RETURN(node)->nd_stts;
+ if (newline && node) nd_set_fl_newline(node);
continue;
case NODE_BEGIN:
- *body = node = node->nd_body;
- if (newline && node) node->flags |= NODE_FL_NEWLINE;
+ *body = node = RNODE_BEGIN(node)->nd_body;
+ if (newline && node) nd_set_fl_newline(node);
continue;
case NODE_BLOCK:
- body = &node->nd_end->nd_head;
+ body = &RNODE_BLOCK(RNODE_BLOCK(node)->nd_end)->nd_head;
break;
case NODE_IF:
case NODE_UNLESS:
- if (subnodes(nd_body, nd_else)) break;
+ if (subnodes(RNODE_IF, nd_body, nd_else)) break;
return;
case NODE_CASE:
- body = &node->nd_body;
+ body = &RNODE_CASE(node)->nd_body;
break;
case NODE_WHEN:
- if (!subnodes(nd_body, nd_next)) goto end;
+ if (!subnodes(RNODE_WHEN, nd_body, nd_next)) goto end;
break;
case NODE_ENSURE:
- if (!subnodes(nd_head, nd_resq)) goto end;
+ body = &RNODE_ENSURE(node)->nd_head;
break;
case NODE_RESCUE:
- if (node->nd_else) {
- body = &node->nd_resq;
+ newline = 0; // RESBODY should not be a NEWLINE
+ if (RNODE_RESCUE(node)->nd_else) {
+ body = &RNODE_RESCUE(node)->nd_resq;
break;
}
- if (!subnodes(nd_head, nd_resq)) goto end;
+ if (!subnodes(RNODE_RESCUE, nd_head, nd_resq)) goto end;
break;
default:
return;
}
node = *body;
- if (newline && node) node->flags |= NODE_FL_NEWLINE;
+ if (newline && node) nd_set_fl_newline(node);
}
#undef subnodes
@@ -12280,12 +14398,20 @@ is_static_content(NODE *node)
if (!node) return 1;
switch (nd_type(node)) {
case NODE_HASH:
- if (!(node = node->nd_head)) break;
+ if (!(node = RNODE_HASH(node)->nd_head)) break;
case NODE_LIST:
do {
- if (!is_static_content(node->nd_head)) return 0;
- } while ((node = node->nd_next) != 0);
- case NODE_LIT:
+ if (!is_static_content(RNODE_LIST(node)->nd_head)) return 0;
+ } while ((node = RNODE_LIST(node)->nd_next) != 0);
+ case NODE_SYM:
+ case NODE_REGX:
+ case NODE_LINE:
+ case NODE_FILE:
+ case NODE_ENCODING:
+ case NODE_INTEGER:
+ case NODE_FLOAT:
+ case NODE_RATIONAL:
+ case NODE_IMAGINARY:
case NODE_STR:
case NODE_NIL:
case NODE_TRUE:
@@ -12307,16 +14433,18 @@ assign_in_cond(struct parser_params *p, NODE *node)
case NODE_DASGN:
case NODE_GASGN:
case NODE_IASGN:
+ case NODE_CVASGN:
+ case NODE_CDECL:
break;
default:
return 0;
}
- if (!node->nd_value) return 1;
- if (is_static_content(node->nd_value)) {
+ if (!get_nd_value(p, node)) return 1;
+ if (is_static_content(get_nd_value(p, node))) {
/* reports always */
- parser_warn(p, node->nd_value, "found `= literal' in conditional, should be ==");
+ rb_warn0L(nd_line(get_nd_value(p, node)), "found '= literal' in conditional, should be ==");
}
return 1;
}
@@ -12327,14 +14455,15 @@ enum cond_type {
COND_IN_FF
};
-#define SWITCH_BY_COND_TYPE(t, w, arg) \
+#define SWITCH_BY_COND_TYPE(t, w, arg) do { \
switch (t) { \
case COND_IN_OP: break; \
case COND_IN_COND: rb_##w##0(arg "literal in condition"); break; \
case COND_IN_FF: rb_##w##0(arg "literal in flip-flop"); break; \
- }
+ } \
+} while (0)
-static NODE *cond0(struct parser_params*,NODE*,enum cond_type,const YYLTYPE*);
+static NODE *cond0(struct parser_params*,NODE*,enum cond_type,const YYLTYPE*,bool);
static NODE*
range_op(struct parser_params *p, NODE *node, const YYLTYPE *loc)
@@ -12345,67 +14474,87 @@ range_op(struct parser_params *p, NODE *node, const YYLTYPE *loc)
type = nd_type(node);
value_expr(node);
- if (type == NODE_LIT && FIXNUM_P(node->nd_lit)) {
- if (!e_option_supplied(p)) parser_warn(p, node, "integer literal in flip-flop");
+ if (type == NODE_INTEGER) {
+ if (!e_option_supplied(p)) rb_warn0L(nd_line(node), "integer literal in flip-flop");
ID lineno = rb_intern("$.");
return NEW_CALL(node, tEQ, NEW_LIST(NEW_GVAR(lineno, loc), loc), loc);
}
- return cond0(p, node, COND_IN_FF, loc);
+ return cond0(p, node, COND_IN_FF, loc, true);
}
static NODE*
-cond0(struct parser_params *p, NODE *node, enum cond_type type, const YYLTYPE *loc)
+cond0(struct parser_params *p, NODE *node, enum cond_type type, const YYLTYPE *loc, bool top)
{
if (node == 0) return 0;
if (!(node = nd_once_body(node))) return 0;
assign_in_cond(p, node);
switch (nd_type(node)) {
+ case NODE_BEGIN:
+ RNODE_BEGIN(node)->nd_body = cond0(p, RNODE_BEGIN(node)->nd_body, type, loc, top);
+ break;
+
case NODE_DSTR:
case NODE_EVSTR:
case NODE_STR:
- SWITCH_BY_COND_TYPE(type, warn, "string ")
+ case NODE_FILE:
+ SWITCH_BY_COND_TYPE(type, warn, "string ");
+ break;
+
+ case NODE_REGX:
+ if (!e_option_supplied(p)) SWITCH_BY_COND_TYPE(type, warn, "regex ");
+ nd_set_type(node, NODE_MATCH);
break;
case NODE_DREGX:
- if (!e_option_supplied(p)) SWITCH_BY_COND_TYPE(type, warning, "regex ")
+ if (!e_option_supplied(p)) SWITCH_BY_COND_TYPE(type, warning, "regex ");
return NEW_MATCH2(node, NEW_GVAR(idLASTLINE, loc), loc);
+ case NODE_BLOCK:
+ {
+ NODE *end = RNODE_BLOCK(node)->nd_end;
+ NODE **expr = &RNODE_BLOCK(end)->nd_head;
+ if (top) top = node == end;
+ *expr = cond0(p, *expr, type, loc, top);
+ }
+ break;
+
case NODE_AND:
case NODE_OR:
- node->nd_1st = cond0(p, node->nd_1st, COND_IN_COND, loc);
- node->nd_2nd = cond0(p, node->nd_2nd, COND_IN_COND, loc);
+ RNODE_AND(node)->nd_1st = cond0(p, RNODE_AND(node)->nd_1st, COND_IN_COND, loc, true);
+ RNODE_AND(node)->nd_2nd = cond0(p, RNODE_AND(node)->nd_2nd, COND_IN_COND, loc, true);
break;
case NODE_DOT2:
case NODE_DOT3:
- node->nd_beg = range_op(p, node->nd_beg, loc);
- node->nd_end = range_op(p, node->nd_end, loc);
+ if (!top) break;
+ RNODE_DOT2(node)->nd_beg = range_op(p, RNODE_DOT2(node)->nd_beg, loc);
+ RNODE_DOT2(node)->nd_end = range_op(p, RNODE_DOT2(node)->nd_end, loc);
if (nd_type_p(node, NODE_DOT2)) nd_set_type(node,NODE_FLIP2);
else if (nd_type_p(node, NODE_DOT3)) nd_set_type(node, NODE_FLIP3);
break;
+ case NODE_SYM:
case NODE_DSYM:
- warn_symbol:
- SWITCH_BY_COND_TYPE(type, warning, "symbol ")
+ SWITCH_BY_COND_TYPE(type, warning, "symbol ");
+ break;
+
+ case NODE_LINE:
+ SWITCH_BY_COND_TYPE(type, warning, "");
+ break;
+
+ case NODE_ENCODING:
+ SWITCH_BY_COND_TYPE(type, warning, "");
+ break;
+
+ case NODE_INTEGER:
+ case NODE_FLOAT:
+ case NODE_RATIONAL:
+ case NODE_IMAGINARY:
+ SWITCH_BY_COND_TYPE(type, warning, "");
break;
- case NODE_LIT:
- if (RB_TYPE_P(node->nd_lit, T_REGEXP)) {
- if (!e_option_supplied(p)) SWITCH_BY_COND_TYPE(type, warn, "regex ")
- nd_set_type(node, NODE_MATCH);
- }
- else if (node->nd_lit == Qtrue ||
- node->nd_lit == Qfalse) {
- /* booleans are OK, e.g., while true */
- }
- else if (SYMBOL_P(node->nd_lit)) {
- goto warn_symbol;
- }
- else {
- SWITCH_BY_COND_TYPE(type, warning, "")
- }
default:
break;
}
@@ -12416,14 +14565,14 @@ static NODE*
cond(struct parser_params *p, NODE *node, const YYLTYPE *loc)
{
if (node == 0) return 0;
- return cond0(p, node, COND_IN_COND, loc);
+ return cond0(p, node, COND_IN_COND, loc, true);
}
static NODE*
method_cond(struct parser_params *p, NODE *node, const YYLTYPE *loc)
{
if (node == 0) return 0;
- return cond0(p, node, COND_IN_OP, loc);
+ return cond0(p, node, COND_IN_OP, loc, true);
}
static NODE*
@@ -12437,7 +14586,7 @@ static NODE*
new_if(struct parser_params *p, NODE *cc, NODE *left, NODE *right, const YYLTYPE *loc)
{
if (!cc) return right;
- cc = cond0(p, cc, COND_IN_COND, loc);
+ cc = cond0(p, cc, COND_IN_COND, loc, true);
return newline_node(NEW_IF(cc, left, right, loc));
}
@@ -12445,10 +14594,12 @@ static NODE*
new_unless(struct parser_params *p, NODE *cc, NODE *left, NODE *right, const YYLTYPE *loc)
{
if (!cc) return right;
- cc = cond0(p, cc, COND_IN_COND, loc);
+ cc = cond0(p, cc, COND_IN_COND, loc, true);
return newline_node(NEW_UNLESS(cc, left, right, loc));
}
+#define NEW_AND_OR(type, f, s, loc) (type == NODE_AND ? NEW_AND(f,s,loc) : NEW_OR(f,s,loc))
+
static NODE*
logop(struct parser_params *p, ID id, NODE *left, NODE *right,
const YYLTYPE *op_loc, const YYLTYPE *loc)
@@ -12458,19 +14609,21 @@ logop(struct parser_params *p, ID id, NODE *left, NODE *right,
value_expr(left);
if (left && nd_type_p(left, type)) {
NODE *node = left, *second;
- while ((second = node->nd_2nd) != 0 && nd_type_p(second, type)) {
+ while ((second = RNODE_AND(node)->nd_2nd) != 0 && nd_type_p(second, type)) {
node = second;
}
- node->nd_2nd = NEW_NODE(type, second, right, 0, loc);
- nd_set_line(node->nd_2nd, op_loc->beg_pos.lineno);
+ RNODE_AND(node)->nd_2nd = NEW_AND_OR(type, second, right, loc);
+ nd_set_line(RNODE_AND(node)->nd_2nd, op_loc->beg_pos.lineno);
left->nd_loc.end_pos = loc->end_pos;
return left;
}
- op = NEW_NODE(type, left, right, 0, loc);
+ op = NEW_AND_OR(type, left, right, loc);
nd_set_line(op, op_loc->beg_pos.lineno);
return op;
}
+#undef NEW_AND_OR
+
static void
no_blockarg(struct parser_params *p, NODE *node)
{
@@ -12484,13 +14637,8 @@ ret_args(struct parser_params *p, NODE *node)
{
if (node) {
no_blockarg(p, node);
- if (nd_type_p(node, NODE_LIST)) {
- if (node->nd_next == 0) {
- node = node->nd_head;
- }
- else {
- nd_set_type(node, NODE_VALUES);
- }
+ if (nd_type_p(node, NODE_LIST) && !RNODE_LIST(node)->nd_next) {
+ node = RNODE_LIST(node)->nd_head;
}
}
return node;
@@ -12504,53 +14652,35 @@ new_yield(struct parser_params *p, NODE *node, const YYLTYPE *loc)
return NEW_YIELD(node, loc);
}
-static VALUE
-negate_lit(struct parser_params *p, VALUE lit)
+static NODE*
+negate_lit(struct parser_params *p, NODE* node)
{
- if (FIXNUM_P(lit)) {
- return LONG2FIX(-FIX2LONG(lit));
- }
- if (SPECIAL_CONST_P(lit)) {
-#if USE_FLONUM
- if (FLONUM_P(lit)) {
- return DBL2NUM(-RFLOAT_VALUE(lit));
- }
-#endif
- goto unknown;
- }
- switch (BUILTIN_TYPE(lit)) {
- case T_BIGNUM:
- BIGNUM_NEGATE(lit);
- lit = rb_big_norm(lit);
+ switch (nd_type(node)) {
+ case NODE_INTEGER:
+ RNODE_INTEGER(node)->minus = TRUE;
break;
- case T_RATIONAL:
- RATIONAL_SET_NUM(lit, negate_lit(p, RRATIONAL(lit)->num));
+ case NODE_FLOAT:
+ RNODE_FLOAT(node)->minus = TRUE;
break;
- case T_COMPLEX:
- RCOMPLEX_SET_REAL(lit, negate_lit(p, RCOMPLEX(lit)->real));
- RCOMPLEX_SET_IMAG(lit, negate_lit(p, RCOMPLEX(lit)->imag));
+ case NODE_RATIONAL:
+ RNODE_RATIONAL(node)->minus = TRUE;
break;
- case T_FLOAT:
- lit = DBL2NUM(-RFLOAT_VALUE(lit));
- break;
- unknown:
- default:
- rb_parser_fatal(p, "unknown literal type (%s) passed to negate_lit",
- rb_builtin_class_name(lit));
+ case NODE_IMAGINARY:
+ RNODE_IMAGINARY(node)->minus = TRUE;
break;
}
- return lit;
+ return node;
}
static NODE *
-arg_blk_pass(NODE *node1, NODE *node2)
+arg_blk_pass(NODE *node1, rb_node_block_pass_t *node2)
{
if (node2) {
- if (!node1) return node2;
+ if (!node1) return (NODE *)node2;
node2->nd_head = node1;
nd_set_first_lineno(node2, nd_first_lineno(node1));
nd_set_first_column(node2, nd_first_column(node1));
- return node2;
+ return (NODE *)node2;
}
return node1;
}
@@ -12568,24 +14698,23 @@ args_info_empty_p(struct rb_args_info *args)
return true;
}
-static NODE*
-new_args(struct parser_params *p, NODE *pre_args, NODE *opt_args, ID rest_arg, NODE *post_args, NODE *tail, const YYLTYPE *loc)
+static rb_node_args_t *
+new_args(struct parser_params *p, rb_node_args_aux_t *pre_args, rb_node_opt_arg_t *opt_args, ID rest_arg, rb_node_args_aux_t *post_args, rb_node_args_t *tail, const YYLTYPE *loc)
{
- int saved_line = p->ruby_sourceline;
- struct rb_args_info *args = tail->nd_ainfo;
+ struct rb_args_info *args = &tail->nd_ainfo;
if (args->forwarding) {
if (rest_arg) {
- yyerror1(&tail->nd_loc, "... after rest argument");
+ yyerror1(&RNODE(tail)->nd_loc, "... after rest argument");
return tail;
}
rest_arg = idFWD_REST;
}
- args->pre_args_num = pre_args ? rb_long2int(pre_args->nd_plen) : 0;
+ args->pre_args_num = pre_args ? pre_args->nd_plen : 0;
args->pre_init = pre_args ? pre_args->nd_next : 0;
- args->post_args_num = post_args ? rb_long2int(post_args->nd_plen) : 0;
+ args->post_args_num = post_args ? post_args->nd_plen : 0;
args->post_init = post_args ? post_args->nd_next : 0;
args->first_post_arg = post_args ? post_args->nd_pid : 0;
@@ -12599,23 +14728,16 @@ new_args(struct parser_params *p, NODE *pre_args, NODE *opt_args, ID rest_arg, N
args->ruby2_keywords = 0;
#endif
- p->ruby_sourceline = saved_line;
- nd_set_loc(tail, loc);
+ nd_set_loc(RNODE(tail), loc);
return tail;
}
-static NODE*
-new_args_tail(struct parser_params *p, NODE *kw_args, ID kw_rest_arg, ID block, const YYLTYPE *kw_rest_loc)
+static rb_node_args_t *
+new_args_tail(struct parser_params *p, rb_node_kw_arg_t *kw_args, ID kw_rest_arg, ID block, const YYLTYPE *kw_rest_loc)
{
- int saved_line = p->ruby_sourceline;
- NODE *node;
- VALUE tmpbuf = rb_imemo_tmpbuf_auto_free_pointer();
- struct rb_args_info *args = ZALLOC(struct rb_args_info);
- rb_imemo_tmpbuf_set_ptr(tmpbuf, args);
- args->imemo = tmpbuf;
- node = NEW_NODE(NODE_ARGS, 0, 0, args, &NULL_LOC);
- RB_OBJ_WRITTEN(p->ast, Qnil, tmpbuf);
+ rb_node_args_t *node = NEW_ARGS(&NULL_LOC);
+ struct rb_args_info *args = &node->nd_ainfo;
if (p->error_p) return node;
args->block_arg = block;
@@ -12630,21 +14752,21 @@ new_args_tail(struct parser_params *p, NODE *kw_args, ID kw_rest_arg, ID block,
*/
ID kw_bits = internal_id(p), *required_kw_vars, *kw_vars;
struct vtable *vtargs = p->lvtbl->args;
- NODE *kwn = kw_args;
+ rb_node_kw_arg_t *kwn = kw_args;
if (block) block = vtargs->tbl[vtargs->pos-1];
vtable_pop(vtargs, !!block + !!kw_rest_arg);
required_kw_vars = kw_vars = &vtargs->tbl[vtargs->pos];
while (kwn) {
- if (!NODE_REQUIRED_KEYWORD_P(kwn->nd_body))
+ if (!NODE_REQUIRED_KEYWORD_P(get_nd_value(p, kwn->nd_body)))
--kw_vars;
--required_kw_vars;
kwn = kwn->nd_next;
}
for (kwn = kw_args; kwn; kwn = kwn->nd_next) {
- ID vid = kwn->nd_body->nd_vid;
- if (NODE_REQUIRED_KEYWORD_P(kwn->nd_body)) {
+ ID vid = get_nd_vid(p, kwn->nd_body);
+ if (NODE_REQUIRED_KEYWORD_P(get_nd_value(p, kwn->nd_body))) {
*required_kw_vars++ = vid;
}
else {
@@ -12657,7 +14779,6 @@ new_args_tail(struct parser_params *p, NODE *kw_args, ID kw_rest_arg, ID block,
if (block) arg_var(p, block);
args->kw_rest_arg = NEW_DVAR(kw_rest_arg, kw_rest_loc);
- args->kw_rest_arg->nd_cflag = kw_bits;
}
else if (kw_rest_arg == idNil) {
args->no_kwarg = 1;
@@ -12666,20 +14787,19 @@ new_args_tail(struct parser_params *p, NODE *kw_args, ID kw_rest_arg, ID block,
args->kw_rest_arg = NEW_DVAR(kw_rest_arg, kw_rest_loc);
}
- p->ruby_sourceline = saved_line;
return node;
}
-static NODE *
-args_with_numbered(struct parser_params *p, NODE *args, int max_numparam)
+static rb_node_args_t *
+args_with_numbered(struct parser_params *p, rb_node_args_t *args, int max_numparam, ID it_id)
{
- if (max_numparam > NO_PARAM) {
+ if (max_numparam > NO_PARAM || it_id) {
if (!args) {
YYLTYPE loc = RUBY_INIT_YYLLOC();
args = new_args_tail(p, 0, 0, 0, 0);
- nd_set_loc(args, &loc);
+ nd_set_loc(RNODE(args), &loc);
}
- args->nd_ainfo->pre_args_num = max_numparam;
+ args->nd_ainfo.pre_args_num = it_id ? 1 : max_numparam;
}
return args;
}
@@ -12687,17 +14807,15 @@ args_with_numbered(struct parser_params *p, NODE *args, int max_numparam)
static NODE*
new_array_pattern(struct parser_params *p, NODE *constant, NODE *pre_arg, NODE *aryptn, const YYLTYPE *loc)
{
- struct rb_ary_pattern_info *apinfo = aryptn->nd_apinfo;
-
- aryptn->nd_pconst = constant;
+ RNODE_ARYPTN(aryptn)->nd_pconst = constant;
if (pre_arg) {
NODE *pre_args = NEW_LIST(pre_arg, loc);
- if (apinfo->pre_args) {
- apinfo->pre_args = list_concat(pre_args, apinfo->pre_args);
+ if (RNODE_ARYPTN(aryptn)->pre_args) {
+ RNODE_ARYPTN(aryptn)->pre_args = list_concat(pre_args, RNODE_ARYPTN(aryptn)->pre_args);
}
else {
- apinfo->pre_args = pre_args;
+ RNODE_ARYPTN(aryptn)->pre_args = pre_args;
}
}
return aryptn;
@@ -12706,33 +14824,21 @@ new_array_pattern(struct parser_params *p, NODE *constant, NODE *pre_arg, NODE *
static NODE*
new_array_pattern_tail(struct parser_params *p, NODE *pre_args, int has_rest, NODE *rest_arg, NODE *post_args, const YYLTYPE *loc)
{
- int saved_line = p->ruby_sourceline;
- NODE *node;
- VALUE tmpbuf = rb_imemo_tmpbuf_auto_free_pointer();
- struct rb_ary_pattern_info *apinfo = ZALLOC(struct rb_ary_pattern_info);
- rb_imemo_tmpbuf_set_ptr(tmpbuf, apinfo);
- node = NEW_NODE(NODE_ARYPTN, 0, tmpbuf, apinfo, loc);
- RB_OBJ_WRITTEN(p->ast, Qnil, tmpbuf);
-
- apinfo->pre_args = pre_args;
-
if (has_rest) {
- apinfo->rest_arg = rest_arg ? rest_arg : NODE_SPECIAL_NO_NAME_REST;
+ rest_arg = rest_arg ? rest_arg : NODE_SPECIAL_NO_NAME_REST;
}
else {
- apinfo->rest_arg = NULL;
+ rest_arg = NULL;
}
+ NODE *node = NEW_ARYPTN(pre_args, rest_arg, post_args, loc);
- apinfo->post_args = post_args;
-
- p->ruby_sourceline = saved_line;
return node;
}
static NODE*
new_find_pattern(struct parser_params *p, NODE *constant, NODE *fndptn, const YYLTYPE *loc)
{
- fndptn->nd_pconst = constant;
+ RNODE_FNDPTN(fndptn)->nd_pconst = constant;
return fndptn;
}
@@ -12740,33 +14846,23 @@ new_find_pattern(struct parser_params *p, NODE *constant, NODE *fndptn, const YY
static NODE*
new_find_pattern_tail(struct parser_params *p, NODE *pre_rest_arg, NODE *args, NODE *post_rest_arg, const YYLTYPE *loc)
{
- int saved_line = p->ruby_sourceline;
- NODE *node;
- VALUE tmpbuf = rb_imemo_tmpbuf_auto_free_pointer();
- struct rb_fnd_pattern_info *fpinfo = ZALLOC(struct rb_fnd_pattern_info);
- rb_imemo_tmpbuf_set_ptr(tmpbuf, fpinfo);
- node = NEW_NODE(NODE_FNDPTN, 0, tmpbuf, fpinfo, loc);
- RB_OBJ_WRITTEN(p->ast, Qnil, tmpbuf);
+ pre_rest_arg = pre_rest_arg ? pre_rest_arg : NODE_SPECIAL_NO_NAME_REST;
+ post_rest_arg = post_rest_arg ? post_rest_arg : NODE_SPECIAL_NO_NAME_REST;
+ NODE *node = NEW_FNDPTN(pre_rest_arg, args, post_rest_arg, loc);
- fpinfo->pre_rest_arg = pre_rest_arg ? pre_rest_arg : NODE_SPECIAL_NO_NAME_REST;
- fpinfo->args = args;
- fpinfo->post_rest_arg = post_rest_arg ? post_rest_arg : NODE_SPECIAL_NO_NAME_REST;
-
- p->ruby_sourceline = saved_line;
return node;
}
static NODE*
new_hash_pattern(struct parser_params *p, NODE *constant, NODE *hshptn, const YYLTYPE *loc)
{
- hshptn->nd_pconst = constant;
+ RNODE_HSHPTN(hshptn)->nd_pconst = constant;
return hshptn;
}
static NODE*
new_hash_pattern_tail(struct parser_params *p, NODE *kw_args, ID kw_rest_arg, const YYLTYPE *loc)
{
- int saved_line = p->ruby_sourceline;
NODE *node, *kw_rest_arg_node;
if (kw_rest_arg == idNil) {
@@ -12779,19 +14875,16 @@ new_hash_pattern_tail(struct parser_params *p, NODE *kw_args, ID kw_rest_arg, co
kw_rest_arg_node = NULL;
}
- node = NEW_NODE(NODE_HSHPTN, 0, kw_args, kw_rest_arg_node, loc);
+ node = NEW_HSHPTN(0, kw_args, kw_rest_arg_node, loc);
- p->ruby_sourceline = saved_line;
return node;
}
static NODE*
dsym_node(struct parser_params *p, NODE *node, const YYLTYPE *loc)
{
- VALUE lit;
-
if (!node) {
- return NEW_LIT(ID2SYM(idNULL), loc);
+ return NEW_SYM(STR_NEW0(), loc);
}
switch (nd_type(node)) {
@@ -12800,117 +14893,108 @@ dsym_node(struct parser_params *p, NODE *node, const YYLTYPE *loc)
nd_set_loc(node, loc);
break;
case NODE_STR:
- lit = node->nd_lit;
- RB_OBJ_WRITTEN(p->ast, Qnil, node->nd_lit = ID2SYM(rb_intern_str(lit)));
- nd_set_type(node, NODE_LIT);
- nd_set_loc(node, loc);
+ node = str_to_sym_node(p, node, loc);
break;
default:
- node = NEW_NODE(NODE_DSYM, Qnil, 1, NEW_LIST(node, loc), loc);
+ node = NEW_DSYM(0, 1, NEW_LIST(node, loc), loc);
break;
}
return node;
}
static int
-append_literal_keys(st_data_t k, st_data_t v, st_data_t h)
-{
- NODE *node = (NODE *)v;
- NODE **result = (NODE **)h;
- node->nd_alen = 2;
- node->nd_next->nd_end = node->nd_next;
- node->nd_next->nd_next = 0;
- if (*result)
- list_concat(*result, node);
- else
- *result = node;
- return ST_CONTINUE;
-}
-
-static bool
-hash_literal_key_p(VALUE k)
+nd_type_st_key_enable_p(NODE *node)
{
- switch (OBJ_BUILTIN_TYPE(k)) {
- case T_NODE:
- return false;
- default:
+ switch (nd_type(node)) {
+ case NODE_INTEGER:
+ case NODE_FLOAT:
+ case NODE_RATIONAL:
+ case NODE_IMAGINARY:
+ case NODE_STR:
+ case NODE_SYM:
+ case NODE_REGX:
+ case NODE_LINE:
+ case NODE_FILE:
+ case NODE_ENCODING:
return true;
+ default:
+ return false;
}
}
-static int
-literal_cmp(VALUE val, VALUE lit)
+static VALUE
+nd_value(struct parser_params *p, NODE *node)
{
- if (val == lit) return 0;
- if (!hash_literal_key_p(val) || !hash_literal_key_p(lit)) return -1;
- return rb_iseq_cdhash_cmp(val, lit);
+ switch (nd_type(node)) {
+ case NODE_STR:
+ return rb_node_str_string_val(node);
+ case NODE_INTEGER:
+ return rb_node_integer_literal_val(node);
+ case NODE_FLOAT:
+ return rb_node_float_literal_val(node);
+ case NODE_RATIONAL:
+ return rb_node_rational_literal_val(node);
+ case NODE_IMAGINARY:
+ return rb_node_imaginary_literal_val(node);
+ case NODE_SYM:
+ return rb_node_sym_string_val(node);
+ case NODE_REGX:
+ return rb_node_regx_string_val(node);
+ case NODE_LINE:
+ return rb_node_line_lineno_val(node);
+ case NODE_ENCODING:
+ return rb_node_encoding_val(node);
+ case NODE_FILE:
+ return rb_node_file_path_val(node);
+ default:
+ rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
+ UNREACHABLE_RETURN(0);
+ }
}
-static st_index_t
-literal_hash(VALUE a)
-{
- if (!hash_literal_key_p(a)) return (st_index_t)a;
- return rb_iseq_cdhash_hash(a);
-}
+static void
+warn_duplicate_keys(struct parser_params *p, NODE *hash)
+{
+ /* See https://bugs.ruby-lang.org/issues/20331 for discussion about what is warned. */
+ st_table *literal_keys = st_init_table_with_size(&literal_type, RNODE_LIST(hash)->as.nd_alen / 2);
+ while (hash && RNODE_LIST(hash)->nd_next) {
+ NODE *head = RNODE_LIST(hash)->nd_head;
+ NODE *value = RNODE_LIST(hash)->nd_next;
+ NODE *next = RNODE_LIST(value)->nd_next;
+ st_data_t key;
+ st_data_t data;
-static const struct st_hash_type literal_type = {
- literal_cmp,
- literal_hash,
-};
+ /* keyword splat, e.g. {k: 1, **z, k: 2} */
+ if (!head) {
+ head = value;
+ }
-static NODE *
-remove_duplicate_keys(struct parser_params *p, NODE *hash)
-{
- st_table *literal_keys = st_init_table_with_size(&literal_type, hash->nd_alen / 2);
- NODE *result = 0;
- NODE *last_expr = 0;
- rb_code_location_t loc = hash->nd_loc;
- while (hash && hash->nd_head && hash->nd_next) {
- NODE *head = hash->nd_head;
- NODE *value = hash->nd_next;
- NODE *next = value->nd_next;
- st_data_t key = (st_data_t)head;
- st_data_t data;
- value->nd_next = 0;
- if (nd_type_p(head, NODE_LIT) &&
- st_delete(literal_keys, (key = (st_data_t)head->nd_lit, &key), &data)) {
- NODE *dup_value = ((NODE *)data)->nd_next;
- rb_compile_warn(p->ruby_sourcefile, nd_line((NODE *)data),
- "key %+"PRIsVALUE" is duplicated and overwritten on line %d",
- head->nd_lit, nd_line(head));
- if (dup_value == last_expr) {
- value->nd_head = block_append(p, dup_value->nd_head, value->nd_head);
- }
- else {
- last_expr->nd_head = block_append(p, dup_value->nd_head, last_expr->nd_head);
+ if (nd_type_st_key_enable_p(head)) {
+ key = (st_data_t)head;
+
+ if (st_delete(literal_keys, &key, &data)) {
+ rb_warn2L(nd_line((NODE *)data),
+ "key %+"PRIsWARN" is duplicated and overwritten on line %d",
+ nd_value(p, head), WARN_I(nd_line(head)));
}
+ st_insert(literal_keys, (st_data_t)key, (st_data_t)hash);
}
- st_insert(literal_keys, (st_data_t)key, (st_data_t)hash);
- last_expr = nd_type_p(head, NODE_LIT) ? value : head;
hash = next;
}
- st_foreach(literal_keys, append_literal_keys, (st_data_t)&result);
st_free_table(literal_keys);
- if (hash) {
- if (!result) result = hash;
- else list_concat(result, hash);
- }
- result->nd_loc = loc;
- return result;
}
static NODE *
new_hash(struct parser_params *p, NODE *hash, const YYLTYPE *loc)
{
- if (hash) hash = remove_duplicate_keys(p, hash);
+ if (hash) warn_duplicate_keys(p, hash);
return NEW_HASH(hash, loc);
}
-#endif
static void
error_duplicate_pattern_variable(struct parser_params *p, ID id, const YYLTYPE *loc)
{
- if (is_private_local_id(id)) {
+ if (is_private_local_id(p, id)) {
return;
}
if (st_is_member(p->pvtbl, id)) {
@@ -12934,69 +15018,39 @@ error_duplicate_pattern_key(struct parser_params *p, VALUE key, const YYLTYPE *l
st_insert(p->pktbl, (st_data_t)key, 0);
}
-#ifndef RIPPER
static NODE *
new_unique_key_hash(struct parser_params *p, NODE *hash, const YYLTYPE *loc)
{
return NEW_HASH(hash, loc);
}
-#endif /* !RIPPER */
-#ifndef RIPPER
static NODE *
new_op_assign(struct parser_params *p, NODE *lhs, ID op, NODE *rhs, struct lex_context ctxt, const YYLTYPE *loc)
{
NODE *asgn;
if (lhs) {
- ID vid = lhs->nd_vid;
+ ID vid = get_nd_vid(p, lhs);
YYLTYPE lhs_loc = lhs->nd_loc;
- int shareable = ctxt.shareable_constant_value;
- if (shareable) {
- switch (nd_type(lhs)) {
- case NODE_CDECL:
- case NODE_COLON2:
- case NODE_COLON3:
- break;
- default:
- shareable = 0;
- break;
- }
- }
if (op == tOROP) {
- rhs = shareable_constant_value(p, shareable, lhs, rhs, &rhs->nd_loc);
- lhs->nd_value = rhs;
+ set_nd_value(p, lhs, rhs);
nd_set_loc(lhs, loc);
asgn = NEW_OP_ASGN_OR(gettable(p, vid, &lhs_loc), lhs, loc);
- if (is_notop_id(vid)) {
- switch (id_type(vid)) {
- case ID_GLOBAL:
- case ID_INSTANCE:
- case ID_CLASS:
- asgn->nd_aid = vid;
- }
- }
}
else if (op == tANDOP) {
- if (shareable) {
- rhs = shareable_constant_value(p, shareable, lhs, rhs, &rhs->nd_loc);
- }
- lhs->nd_value = rhs;
+ set_nd_value(p, lhs, rhs);
nd_set_loc(lhs, loc);
asgn = NEW_OP_ASGN_AND(gettable(p, vid, &lhs_loc), lhs, loc);
}
else {
asgn = lhs;
rhs = NEW_CALL(gettable(p, vid, &lhs_loc), op, NEW_LIST(rhs, &rhs->nd_loc), loc);
- if (shareable) {
- rhs = shareable_constant_value(p, shareable, lhs, rhs, &rhs->nd_loc);
- }
- asgn->nd_value = rhs;
+ set_nd_value(p, asgn, rhs);
nd_set_loc(asgn, loc);
}
}
else {
- asgn = NEW_BEGIN(0, loc);
+ asgn = NEW_ERROR(loc);
}
return asgn;
}
@@ -13007,14 +15061,9 @@ new_ary_op_assign(struct parser_params *p, NODE *ary,
{
NODE *asgn;
+ aryset_check(p, args);
args = make_list(args, args_loc);
- if (nd_type_p(args, NODE_BLOCK_PASS)) {
- args = NEW_ARGSCAT(args, rhs, loc);
- }
- else {
- args = arg_concat(p, args, rhs, loc);
- }
- asgn = NEW_OP_ASGN1(ary, op, args, loc);
+ asgn = NEW_OP_ASGN1(ary, op, args, rhs, loc);
fixpos(asgn, ary);
return asgn;
}
@@ -13036,11 +15085,10 @@ new_const_op_assign(struct parser_params *p, NODE *lhs, ID op, NODE *rhs, struct
NODE *asgn;
if (lhs) {
- rhs = shareable_constant_value(p, ctxt.shareable_constant_value, lhs, rhs, loc);
- asgn = NEW_OP_CDECL(lhs, op, rhs, loc);
+ asgn = NEW_OP_CDECL(lhs, op, rhs, ctxt.shareable_constant_value, loc);
}
else {
- asgn = NEW_BEGIN(0, loc);
+ asgn = NEW_ERROR(loc);
}
fixpos(asgn, lhs);
return asgn;
@@ -13052,11 +15100,11 @@ const_decl(struct parser_params *p, NODE *path, const YYLTYPE *loc)
if (p->ctxt.in_def) {
yyerror1(loc, "dynamic constant assignment");
}
- return NEW_CDECL(0, 0, (path), loc);
+ return NEW_CDECL(0, 0, (path), p->ctxt.shareable_constant_value, loc);
}
-#else
+#ifdef RIPPER
static VALUE
-const_decl(struct parser_params *p, VALUE path)
+ripper_const_decl(struct parser_params *p, VALUE path)
{
if (p->ctxt.in_def) {
path = assign_error(p, "dynamic constant assignment", path);
@@ -13075,11 +15123,10 @@ assign_error(struct parser_params *p, const char *mesg, VALUE a)
static VALUE
var_field(struct parser_params *p, VALUE a)
{
- return ripper_new_yylval(p, get_id(a), dispatch1(var_field, a), 0);
+ return dispatch1(var_field, a);
}
#endif
-#ifndef RIPPER
static NODE *
new_bodystmt(struct parser_params *p, NODE *head, NODE *rescue, NODE *rescue_else, NODE *ensure, const YYLTYPE *loc)
{
@@ -13100,7 +15147,6 @@ new_bodystmt(struct parser_params *p, NODE *head, NODE *rescue, NODE *rescue_els
fixpos(result, head);
return result;
}
-#endif
static void
warn_unused_var(struct parser_params *p, struct local_vars *local)
@@ -13117,7 +15163,7 @@ warn_unused_var(struct parser_params *p, struct local_vars *local)
ID *u = local->used->tbl;
for (int i = 0; i < cnt; ++i) {
if (!v[i] || (u[i] & LVAR_USED)) continue;
- if (is_private_local_id(v[i])) continue;
+ if (is_private_local_id(p, v[i])) continue;
rb_warn1L((int)u[i], "assigned but unused variable - %"PRIsWARN, rb_id2str(v[i]));
}
#endif
@@ -13137,10 +15183,11 @@ local_push(struct parser_params *p, int toplevel_scope)
#ifndef RIPPER
if (toplevel_scope && compile_for_eval) warn_unused_vars = 0;
if (toplevel_scope && e_option_supplied(p)) warn_unused_vars = 0;
+#endif
local->numparam.outer = 0;
local->numparam.inner = 0;
local->numparam.current = 0;
-#endif
+ local->it = 0;
local->used = warn_unused_vars ? vtable_alloc(0) : 0;
# if WARN_PAST_SCOPE
@@ -13152,29 +15199,45 @@ local_push(struct parser_params *p, int toplevel_scope)
}
static void
+vtable_chain_free(struct parser_params *p, struct vtable *table)
+{
+ while (!DVARS_TERMINAL_P(table)) {
+ struct vtable *cur_table = table;
+ table = cur_table->prev;
+ vtable_free(cur_table);
+ }
+}
+
+static void
+local_free(struct parser_params *p, struct local_vars *local)
+{
+ vtable_chain_free(p, local->used);
+
+# if WARN_PAST_SCOPE
+ vtable_chain_free(p, local->past);
+# endif
+
+ vtable_chain_free(p, local->args);
+ vtable_chain_free(p, local->vars);
+
+ ruby_sized_xfree(local, sizeof(struct local_vars));
+}
+
+static void
local_pop(struct parser_params *p)
{
struct local_vars *local = p->lvtbl->prev;
if (p->lvtbl->used) {
warn_unused_var(p, p->lvtbl);
- vtable_free(p->lvtbl->used);
- }
-# if WARN_PAST_SCOPE
- while (p->lvtbl->past) {
- struct vtable *past = p->lvtbl->past;
- p->lvtbl->past = past->prev;
- vtable_free(past);
}
-# endif
- vtable_free(p->lvtbl->args);
- vtable_free(p->lvtbl->vars);
+
+ local_free(p, p->lvtbl);
+ p->lvtbl = local;
+
CMDARG_POP();
COND_POP();
- ruby_sized_xfree(p->lvtbl, sizeof(*p->lvtbl));
- p->lvtbl = local;
}
-#ifndef RIPPER
static rb_ast_id_table_t *
local_tbl(struct parser_params *p)
{
@@ -13201,19 +15264,6 @@ local_tbl(struct parser_params *p)
return tbl;
}
-static NODE*
-node_newnode_with_locals(struct parser_params *p, enum node_type type, VALUE a1, VALUE a2, const rb_code_location_t *loc)
-{
- rb_ast_id_table_t *a0;
- NODE *n;
-
- a0 = local_tbl(p);
- n = NEW_NODE(type, a0, a1, a2, loc);
- return n;
-}
-
-#endif
-
static void
numparam_name(struct parser_params *p, ID id)
{
@@ -13239,6 +15289,14 @@ local_var(struct parser_params *p, ID id)
}
}
+#ifndef RIPPER
+int
+rb_parser_local_defined(struct parser_params *p, ID id, const struct rb_iseq_struct *iseq)
+{
+ return rb_local_defined(id, iseq);
+}
+#endif
+
static int
local_id_ref(struct parser_params *p, ID id, ID **vidrefp)
{
@@ -13255,7 +15313,7 @@ local_id_ref(struct parser_params *p, ID id, ID **vidrefp)
}
if (vars && vars->prev == DVARS_INHERIT) {
- return rb_local_defined(id, p->parent_iseq);
+ return rb_parser_local_defined(p, id, p->parent_iseq);
}
else if (vtable_included(args, id)) {
return 1;
@@ -13292,7 +15350,40 @@ add_forwarding_args(struct parser_params *p)
arg_var(p, idFWD_ALL);
}
-#ifndef RIPPER
+static void
+forwarding_arg_check(struct parser_params *p, ID arg, ID all, const char *var)
+{
+ bool conflict = false;
+
+ struct vtable *vars, *args;
+
+ vars = p->lvtbl->vars;
+ args = p->lvtbl->args;
+
+ while (vars && !DVARS_TERMINAL_P(vars->prev)) {
+ conflict |= (vtable_included(args, arg) && !(all && vtable_included(args, all)));
+ vars = vars->prev;
+ args = args->prev;
+ }
+
+ bool found = false;
+ if (vars && vars->prev == DVARS_INHERIT && !found) {
+ found = (rb_parser_local_defined(p, arg, p->parent_iseq) &&
+ !(all && rb_parser_local_defined(p, all, p->parent_iseq)));
+ }
+ else {
+ found = (vtable_included(args, arg) &&
+ !(all && vtable_included(args, all)));
+ }
+
+ if (!found) {
+ compile_error(p, "no anonymous %s parameter", var);
+ }
+ else if (conflict) {
+ compile_error(p, "anonymous %s parameter is also used within block", var);
+ }
+}
+
static NODE *
new_args_forward_call(struct parser_params *p, NODE *leading, const YYLTYPE *loc, const YYLTYPE *argsloc)
{
@@ -13300,19 +15391,17 @@ new_args_forward_call(struct parser_params *p, NODE *leading, const YYLTYPE *loc
#ifndef FORWARD_ARGS_WITH_RUBY2_KEYWORDS
NODE *kwrest = list_append(p, NEW_LIST(0, loc), NEW_LVAR(idFWD_KWREST, loc));
#endif
- NODE *block = NEW_BLOCK_PASS(NEW_LVAR(idFWD_BLOCK, loc), loc);
+ rb_node_block_pass_t *block = NEW_BLOCK_PASS(NEW_LVAR(idFWD_BLOCK, loc), loc);
NODE *args = leading ? rest_arg_append(p, leading, rest, argsloc) : NEW_SPLAT(rest, loc);
#ifndef FORWARD_ARGS_WITH_RUBY2_KEYWORDS
args = arg_append(p, args, new_hash(p, kwrest, loc), loc);
#endif
return arg_blk_pass(args, block);
}
-#endif
static NODE *
numparam_push(struct parser_params *p)
{
-#ifndef RIPPER
struct local_vars *local = p->lvtbl;
NODE *inner = local->numparam.inner;
if (!local->numparam.outer) {
@@ -13320,16 +15409,13 @@ numparam_push(struct parser_params *p)
}
local->numparam.inner = 0;
local->numparam.current = 0;
+ local->it = 0;
return inner;
-#else
- return 0;
-#endif
}
static void
numparam_pop(struct parser_params *p, NODE *prev_inner)
{
-#ifndef RIPPER
struct local_vars *local = p->lvtbl;
if (prev_inner) {
/* prefer first one */
@@ -13348,7 +15434,7 @@ numparam_pop(struct parser_params *p, NODE *prev_inner)
/* no numbered parameter */
local->numparam.current = 0;
}
-#endif
+ local->it = 0;
}
static const struct vtable *
@@ -13411,7 +15497,8 @@ dyna_in_block(struct parser_params *p)
return !DVARS_TERMINAL_P(p->lvtbl->vars) && p->lvtbl->vars->prev != DVARS_TOPSCOPE;
}
-static int
+#ifndef RIPPER
+int
dvar_defined_ref(struct parser_params *p, ID id, ID **vidrefp)
{
struct vtable *vars, *args, *used;
@@ -13441,6 +15528,7 @@ dvar_defined_ref(struct parser_params *p, ID id, ID **vidrefp)
return 0;
}
+#endif
static int
dvar_defined(struct parser_params *p, ID id)
@@ -13456,43 +15544,83 @@ dvar_curr(struct parser_params *p, ID id)
}
static void
-reg_fragment_enc_error(struct parser_params* p, VALUE str, int c)
+reg_fragment_enc_error(struct parser_params* p, rb_parser_string_t *str, int c)
{
compile_error(p,
"regexp encoding option '%c' differs from source encoding '%s'",
- c, rb_enc_name(rb_enc_get(str)));
+ c, rb_enc_name(rb_parser_str_get_encoding(str)));
}
#ifndef RIPPER
+static rb_encoding *
+find_enc(struct parser_params* p, const char *name)
+{
+ int idx = rb_enc_find_index(name);
+ if (idx < 0) {
+ rb_bug("unknown encoding name: %s", name);
+ }
+
+ return rb_enc_from_index(idx);
+}
+
+static rb_encoding *
+kcode_to_enc(struct parser_params* p, int kcode)
+{
+ rb_encoding *enc;
+
+ switch (kcode) {
+ case ENC_ASCII8BIT:
+ enc = rb_ascii8bit_encoding();
+ break;
+ case ENC_EUC_JP:
+ enc = find_enc(p, "EUC-JP");
+ break;
+ case ENC_Windows_31J:
+ enc = find_enc(p, "Windows-31J");
+ break;
+ case ENC_UTF8:
+ enc = rb_utf8_encoding();
+ break;
+ default:
+ enc = NULL;
+ break;
+ }
+
+ return enc;
+}
+
int
-rb_reg_fragment_setenc(struct parser_params* p, VALUE str, int options)
+rb_reg_fragment_setenc(struct parser_params* p, rb_parser_string_t *str, int options)
{
int c = RE_OPTION_ENCODING_IDX(options);
if (c) {
int opt, idx;
- rb_char_to_option_kcode(c, &opt, &idx);
- if (idx != ENCODING_GET(str) &&
- !is_ascii_string(str)) {
+ rb_encoding *enc;
+
+ char_to_option_kcode(c, &opt, &idx);
+ enc = kcode_to_enc(p, idx);
+ if (enc != rb_parser_str_get_encoding(str) &&
+ !rb_parser_is_ascii_string(p, str)) {
goto error;
}
- ENCODING_SET(str, idx);
+ rb_parser_string_set_encoding(str, enc);
}
else if (RE_OPTION_ENCODING_NONE(options)) {
- if (!ENCODING_IS_ASCII8BIT(str) &&
- !is_ascii_string(str)) {
+ if (!PARSER_ENCODING_IS_ASCII8BIT(p, str) &&
+ !rb_parser_is_ascii_string(p, str)) {
c = 'n';
goto error;
}
- rb_enc_associate(str, rb_ascii8bit_encoding());
+ rb_parser_enc_associate(p, str, rb_ascii8bit_encoding());
}
else if (rb_is_usascii_enc(p->enc)) {
- if (!is_ascii_string(str)) {
+ if (!rb_parser_is_ascii_string(p, str)) {
/* raise in re.c */
- rb_enc_associate(str, rb_usascii_encoding());
+ rb_parser_enc_associate(p, str, rb_usascii_encoding());
}
else {
- rb_enc_associate(str, rb_ascii8bit_encoding());
+ rb_parser_enc_associate(p, str, rb_ascii8bit_encoding());
}
}
return 0;
@@ -13500,20 +15628,24 @@ rb_reg_fragment_setenc(struct parser_params* p, VALUE str, int options)
error:
return c;
}
+#endif
static void
-reg_fragment_setenc(struct parser_params* p, VALUE str, int options)
+reg_fragment_setenc(struct parser_params* p, rb_parser_string_t *str, int options)
{
int c = rb_reg_fragment_setenc(p, str, options);
if (c) reg_fragment_enc_error(p, str, c);
}
-static int
-reg_fragment_check(struct parser_params* p, VALUE str, int options)
+#ifndef RIPPER
+int
+reg_fragment_check(struct parser_params* p, rb_parser_string_t *str, int options)
{
- VALUE err;
+ VALUE err, str2;
reg_fragment_setenc(p, str, options);
- err = rb_reg_check_preprocess(str);
+ /* TODO */
+ str2 = rb_str_new_parser_string(str);
+ err = rb_reg_check_preprocess(str2);
if (err != Qnil) {
err = rb_obj_as_string(err);
compile_error(p, "%"PRIsVALUE, err);
@@ -13521,7 +15653,9 @@ reg_fragment_check(struct parser_params* p, VALUE str, int options)
}
return 1;
}
+#endif
+#ifndef UNIVERSAL_PARSER
typedef struct {
struct parser_params* parser;
rb_encoding *enc;
@@ -13538,23 +15672,8 @@ reg_named_capture_assign_iter(const OnigUChar *name, const OnigUChar *name_end,
rb_encoding *enc = arg->enc;
long len = name_end - name;
const char *s = (const char *)name;
- ID var;
- NODE *node, *succ;
- if (!len) return ST_CONTINUE;
- if (rb_enc_symname_type(s, len, enc, (1U<<ID_LOCAL)) != ID_LOCAL)
- return ST_CONTINUE;
-
- var = intern_cstr(s, len, enc);
- if (len < MAX_WORD_LENGTH && rb_reserved_word(s, (int)len)) {
- if (!lvar_defined(p, var)) return ST_CONTINUE;
- }
- node = node_assign(p, assignable(p, var, 0, arg->loc), NEW_LIT(ID2SYM(var), arg->loc), NO_LEX_CTXT, arg->loc);
- succ = arg->succ_block;
- if (!succ) succ = NEW_BEGIN(0, arg->loc);
- succ = block_append(p, succ, node);
- arg->succ_block = succ;
- return ST_CONTINUE;
+ return rb_reg_named_capture_assign_iter_impl(p, s, len, enc, &arg->succ_block, arg->loc);
}
static NODE *
@@ -13569,24 +15688,54 @@ reg_named_capture_assign(struct parser_params* p, VALUE regexp, const YYLTYPE *l
onig_foreach_name(RREGEXP_PTR(regexp), reg_named_capture_assign_iter, &arg);
if (!arg.succ_block) return 0;
- return arg.succ_block->nd_next;
+ return RNODE_BLOCK(arg.succ_block)->nd_next;
}
+#endif
+
+#ifndef RIPPER
+int
+rb_reg_named_capture_assign_iter_impl(struct parser_params *p, const char *s, long len,
+ rb_encoding *enc, NODE **succ_block, const rb_code_location_t *loc)
+{
+ ID var;
+ NODE *node, *succ;
+
+ if (!len) return ST_CONTINUE;
+ if (!VALID_SYMNAME_P(s, len, enc, ID_LOCAL))
+ return ST_CONTINUE;
+
+ var = intern_cstr(s, len, enc);
+ if (len < MAX_WORD_LENGTH && rb_reserved_word(s, (int)len)) {
+ if (!lvar_defined(p, var)) return ST_CONTINUE;
+ }
+ node = node_assign(p, assignable(p, var, 0, loc), NEW_SYM(rb_id2str(var), loc), NO_LEX_CTXT, loc);
+ succ = *succ_block;
+ if (!succ) succ = NEW_ERROR(loc);
+ succ = block_append(p, succ, node);
+ *succ_block = succ;
+ return ST_CONTINUE;
+}
+#endif
static VALUE
-parser_reg_compile(struct parser_params* p, VALUE str, int options)
+parser_reg_compile(struct parser_params* p, rb_parser_string_t *str, int options)
{
+ VALUE str2;
reg_fragment_setenc(p, str, options);
- return rb_parser_reg_compile(p, str, options);
+ str2 = rb_str_new_parser_string(str);
+ return rb_parser_reg_compile(p, str2, options);
}
+#ifndef RIPPER
VALUE
rb_parser_reg_compile(struct parser_params* p, VALUE str, int options)
{
return rb_reg_compile(str, options & RE_OPTION_MASK, p->ruby_sourcefile, p->ruby_sourceline);
}
+#endif
static VALUE
-reg_compile(struct parser_params* p, VALUE str, int options)
+reg_compile(struct parser_params* p, rb_parser_string_t *str, int options)
{
VALUE re;
VALUE err;
@@ -13601,30 +15750,11 @@ reg_compile(struct parser_params* p, VALUE str, int options)
}
return re;
}
-#else
-static VALUE
-parser_reg_compile(struct parser_params* p, VALUE str, int options, VALUE *errmsg)
-{
- VALUE err = rb_errinfo();
- VALUE re;
- str = ripper_is_node_yylval(str) ? RNODE(str)->nd_cval : str;
- int c = rb_reg_fragment_setenc(p, str, options);
- if (c) reg_fragment_enc_error(p, str, c);
- re = rb_parser_reg_compile(p, str, options);
- if (NIL_P(re)) {
- *errmsg = rb_attr_get(rb_errinfo(), idMesg);
- rb_set_errinfo(err);
- }
- return re;
-}
-#endif
#ifndef RIPPER
void
-rb_parser_set_options(VALUE vparser, int print, int loop, int chomp, int split)
+rb_ruby_parser_set_options(struct parser_params *p, int print, int loop, int chomp, int split)
{
- struct parser_params *p;
- TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
p->do_print = print;
p->do_loop = loop;
p->do_chomp = chomp;
@@ -13638,7 +15768,7 @@ parser_append_options(struct parser_params *p, NODE *node)
const YYLTYPE *const LOC = &default_location;
if (p->do_print) {
- NODE *print = NEW_FCALL(rb_intern("print"),
+ NODE *print = (NODE *)NEW_FCALL(rb_intern("print"),
NEW_LIST(NEW_GVAR(idLASTLINE, LOC), LOC),
LOC);
node = block_append(p, node, print);
@@ -13658,12 +15788,12 @@ parser_append_options(struct parser_params *p, NODE *node)
node = block_append(p, split, node);
}
if (p->do_chomp) {
- NODE *chomp = NEW_LIT(ID2SYM(rb_intern("chomp")), LOC);
+ NODE *chomp = NEW_SYM(rb_str_new_cstr("chomp"), LOC);
chomp = list_append(p, NEW_LIST(chomp, LOC), NEW_TRUE(LOC));
irs = list_append(p, irs, NEW_HASH(chomp, LOC));
}
- node = NEW_WHILE(NEW_FCALL(idGets, irs, LOC), node, 1, LOC);
+ node = NEW_WHILE((NODE *)NEW_FCALL(idGets, irs, LOC), node, 1, LOC);
}
return node;
@@ -13677,7 +15807,7 @@ rb_init_parse(void)
(void)nodeline;
}
-static ID
+ID
internal_id(struct parser_params *p)
{
return rb_make_temporary_id(vtable_size(p->lvtbl->args) + vtable_size(p->lvtbl->vars));
@@ -13689,74 +15819,80 @@ parser_initialize(struct parser_params *p)
{
/* note: we rely on TypedData_Make_Struct to set most fields to 0 */
p->command_start = TRUE;
- p->ruby_sourcefile_string = Qnil;
+ p->ruby_sourcefile_string = NULL;
p->lex.lpar_beg = -1; /* make lambda_beginning_p() == FALSE at first */
+ string_buffer_init(p);
p->node_id = 0;
- p->delayed.token = Qnil;
-#ifdef RIPPER
- p->result = Qnil;
- p->parsing_thread = Qnil;
-#else
+ p->delayed.token = NULL;
+ p->frozen_string_literal = -1; /* not specified */
+#ifndef RIPPER
p->error_buffer = Qfalse;
- p->end_expect_token_locations = Qnil;
+ p->end_expect_token_locations = NULL;
p->token_id = 0;
- p->tokens = Qnil;
+ p->tokens = NULL;
+#else
+ p->result = Qnil;
+ p->parsing_thread = Qnil;
+ p->s_value = Qnil;
+ p->s_lvalue = Qnil;
+ p->s_value_stack = rb_ary_new();
#endif
p->debug_buffer = Qnil;
p->debug_output = rb_ractor_stdout();
p->enc = rb_utf8_encoding();
+ p->exits = 0;
}
#ifdef RIPPER
-#define parser_mark ripper_parser_mark
-#define parser_free ripper_parser_free
+#define rb_ruby_parser_mark ripper_parser_mark
+#define rb_ruby_parser_free ripper_parser_free
+#define rb_ruby_parser_memsize ripper_parser_memsize
#endif
-static void
-parser_mark(void *ptr)
+void
+rb_ruby_parser_mark(void *ptr)
{
struct parser_params *p = (struct parser_params*)ptr;
- rb_gc_mark(p->lex.input);
- rb_gc_mark(p->lex.lastline);
- rb_gc_mark(p->lex.nextline);
- rb_gc_mark(p->ruby_sourcefile_string);
- rb_gc_mark((VALUE)p->lex.strterm);
- rb_gc_mark((VALUE)p->ast);
- rb_gc_mark(p->case_labels);
- rb_gc_mark(p->delayed.token);
#ifndef RIPPER
- rb_gc_mark(p->debug_lines);
- rb_gc_mark(p->compile_option);
rb_gc_mark(p->error_buffer);
- rb_gc_mark(p->end_expect_token_locations);
- rb_gc_mark(p->tokens);
#else
rb_gc_mark(p->value);
rb_gc_mark(p->result);
rb_gc_mark(p->parsing_thread);
+ rb_gc_mark(p->s_value);
+ rb_gc_mark(p->s_lvalue);
+ rb_gc_mark(p->s_value_stack);
#endif
rb_gc_mark(p->debug_buffer);
rb_gc_mark(p->debug_output);
-#ifdef YYMALLOC
- rb_gc_mark((VALUE)p->heap);
-#endif
}
-static void
-parser_free(void *ptr)
+void
+rb_ruby_parser_free(void *ptr)
{
struct parser_params *p = (struct parser_params*)ptr;
struct local_vars *local, *prev;
+#ifndef RIPPER
+ if (p->tokens) {
+ rb_parser_ary_free(p, p->tokens);
+ }
+#endif
+
if (p->tokenbuf) {
ruby_sized_xfree(p->tokenbuf, p->toksiz);
}
+
+ if (p->ruby_sourcefile_string) {
+ rb_parser_string_free(p, p->ruby_sourcefile_string);
+ }
+
for (local = p->lvtbl; local; local = prev) {
- if (local->vars) xfree(local->vars);
prev = local->prev;
- xfree(local);
+ local_free(p, local);
}
+
{
token_info *ptinfo;
while ((ptinfo = p->token_info) != 0) {
@@ -13764,11 +15900,21 @@ parser_free(void *ptr)
xfree(ptinfo);
}
}
+ string_buffer_free(p);
+
+ if (p->pvtbl) {
+ st_free_table(p->pvtbl);
+ }
+
+ if (CASE_LABELS_ENABLED_P(p->case_labels)) {
+ st_free_table(p->case_labels);
+ }
+
xfree(ptr);
}
-static size_t
-parser_memsize(const void *ptr)
+size_t
+rb_ruby_parser_memsize(const void *ptr)
{
struct parser_params *p = (struct parser_params*)ptr;
struct local_vars *local;
@@ -13782,20 +15928,6 @@ parser_memsize(const void *ptr)
return size;
}
-static const rb_data_type_t parser_data_type = {
-#ifndef RIPPER
- "parser",
-#else
- "ripper",
-#endif
- {
- parser_mark,
- parser_free,
- parser_memsize,
- },
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
-};
-
#ifndef RIPPER
#undef rb_reserved_word
@@ -13805,245 +15937,260 @@ rb_reserved_word(const char *str, unsigned int len)
return reserved_word(str, len);
}
-VALUE
-rb_parser_new(void)
+#ifdef UNIVERSAL_PARSER
+rb_parser_t *
+rb_ruby_parser_allocate(const rb_parser_config_t *config)
{
- struct parser_params *p;
- VALUE parser = TypedData_Make_Struct(0, struct parser_params,
- &parser_data_type, p);
+ /* parser_initialize expects fields to be set to 0 */
+ rb_parser_t *p = (rb_parser_t *)config->calloc(1, sizeof(rb_parser_t));
+ p->config = config;
+ return p;
+}
+
+rb_parser_t *
+rb_ruby_parser_new(const rb_parser_config_t *config)
+{
+ /* parser_initialize expects fields to be set to 0 */
+ rb_parser_t *p = rb_ruby_parser_allocate(config);
parser_initialize(p);
- return parser;
+ return p;
+}
+#else
+rb_parser_t *
+rb_ruby_parser_allocate(void)
+{
+ /* parser_initialize expects fields to be set to 0 */
+ rb_parser_t *p = (rb_parser_t *)ruby_xcalloc(1, sizeof(rb_parser_t));
+ return p;
}
-VALUE
-rb_parser_set_context(VALUE vparser, const struct rb_iseq_struct *base, int main)
+rb_parser_t *
+rb_ruby_parser_new(void)
{
- struct parser_params *p;
+ /* parser_initialize expects fields to be set to 0 */
+ rb_parser_t *p = rb_ruby_parser_allocate();
+ parser_initialize(p);
+ return p;
+}
+#endif
- TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
+rb_parser_t *
+rb_ruby_parser_set_context(rb_parser_t *p, const struct rb_iseq_struct *base, int main)
+{
p->error_buffer = main ? Qfalse : Qnil;
p->parent_iseq = base;
- return vparser;
+ return p;
}
void
-rb_parser_keep_script_lines(VALUE vparser)
+rb_ruby_parser_set_script_lines(rb_parser_t *p)
{
- struct parser_params *p;
-
- TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
- p->keep_script_lines = 1;
+ p->debug_lines = rb_parser_ary_new_capa_for_script_line(p, 10);
}
void
-rb_parser_error_tolerant(VALUE vparser)
+rb_ruby_parser_error_tolerant(rb_parser_t *p)
{
- struct parser_params *p;
-
- TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
p->error_tolerant = 1;
- p->end_expect_token_locations = rb_ary_new();
}
void
-rb_parser_keep_tokens(VALUE vparser)
+rb_ruby_parser_keep_tokens(rb_parser_t *p)
{
- struct parser_params *p;
-
- TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
p->keep_tokens = 1;
- p->tokens = rb_ary_new();
+ p->tokens = rb_parser_ary_new_capa_for_ast_token(p, 10);
}
-#endif
+rb_encoding *
+rb_ruby_parser_encoding(rb_parser_t *p)
+{
+ return p->enc;
+}
-#ifdef RIPPER
-#define rb_parser_end_seen_p ripper_parser_end_seen_p
-#define rb_parser_encoding ripper_parser_encoding
-#define rb_parser_get_yydebug ripper_parser_get_yydebug
-#define rb_parser_set_yydebug ripper_parser_set_yydebug
-#define rb_parser_get_debug_output ripper_parser_get_debug_output
-#define rb_parser_set_debug_output ripper_parser_set_debug_output
-static VALUE ripper_parser_end_seen_p(VALUE vparser);
-static VALUE ripper_parser_encoding(VALUE vparser);
-static VALUE ripper_parser_get_yydebug(VALUE self);
-static VALUE ripper_parser_set_yydebug(VALUE self, VALUE flag);
-static VALUE ripper_parser_get_debug_output(VALUE self);
-static VALUE ripper_parser_set_debug_output(VALUE self, VALUE output);
+int
+rb_ruby_parser_end_seen_p(rb_parser_t *p)
+{
+ return p->ruby__end__seen;
+}
-/*
- * call-seq:
- * ripper.error? -> Boolean
- *
- * Return true if parsed source has errors.
- */
-static VALUE
-ripper_error_p(VALUE vparser)
+int
+rb_ruby_parser_set_yydebug(rb_parser_t *p, int flag)
{
- struct parser_params *p;
+ p->debug = flag;
+ return flag;
+}
+#endif /* !RIPPER */
- TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
- return RBOOL(p->error_p);
+#ifdef RIPPER
+int
+rb_ruby_parser_get_yydebug(rb_parser_t *p)
+{
+ return p->debug;
}
-#endif
-/*
- * call-seq:
- * ripper.end_seen? -> Boolean
- *
- * Return true if parsed source ended by +\_\_END\_\_+.
- */
-VALUE
-rb_parser_end_seen_p(VALUE vparser)
+void
+rb_ruby_parser_set_value(rb_parser_t *p, VALUE value)
{
- struct parser_params *p;
+ p->value = value;
+}
- TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
- return RBOOL(p->ruby__end__seen);
+int
+rb_ruby_parser_error_p(rb_parser_t *p)
+{
+ return p->error_p;
}
-/*
- * call-seq:
- * ripper.encoding -> encoding
- *
- * Return encoding of the source.
- */
VALUE
-rb_parser_encoding(VALUE vparser)
+rb_ruby_parser_debug_output(rb_parser_t *p)
{
- struct parser_params *p;
+ return p->debug_output;
+}
- TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
- return rb_enc_from_encoding(p->enc);
+void
+rb_ruby_parser_set_debug_output(rb_parser_t *p, VALUE output)
+{
+ p->debug_output = output;
}
-#ifdef RIPPER
-/*
- * call-seq:
- * ripper.yydebug -> true or false
- *
- * Get yydebug.
- */
VALUE
-rb_parser_get_yydebug(VALUE self)
+rb_ruby_parser_parsing_thread(rb_parser_t *p)
{
- struct parser_params *p;
-
- TypedData_Get_Struct(self, struct parser_params, &parser_data_type, p);
- return RBOOL(p->debug);
+ return p->parsing_thread;
}
-#endif
-/*
- * call-seq:
- * ripper.yydebug = flag
- *
- * Set yydebug.
- */
-VALUE
-rb_parser_set_yydebug(VALUE self, VALUE flag)
+void
+rb_ruby_parser_set_parsing_thread(rb_parser_t *p, VALUE parsing_thread)
{
- struct parser_params *p;
+ p->parsing_thread = parsing_thread;
+}
- TypedData_Get_Struct(self, struct parser_params, &parser_data_type, p);
- p->debug = RTEST(flag);
- return flag;
+void
+rb_ruby_parser_ripper_initialize(rb_parser_t *p, rb_parser_lex_gets_func *gets, rb_parser_input_data input, VALUE sourcefile_string, const char *sourcefile, int sourceline)
+{
+ p->lex.gets = gets;
+ p->lex.input = input;
+ p->eofp = 0;
+ p->ruby_sourcefile_string = rb_str_to_parser_string(p, sourcefile_string);
+ p->ruby_sourcefile = sourcefile;
+ p->ruby_sourceline = sourceline;
}
-/*
- * call-seq:
- * ripper.debug_output -> obj
- *
- * Get debug output.
- */
VALUE
-rb_parser_get_debug_output(VALUE self)
+rb_ruby_parser_result(rb_parser_t *p)
{
- struct parser_params *p;
+ return p->result;
+}
- TypedData_Get_Struct(self, struct parser_params, &parser_data_type, p);
- return p->debug_output;
+rb_encoding *
+rb_ruby_parser_enc(rb_parser_t *p)
+{
+ return p->enc;
}
-/*
- * call-seq:
- * ripper.debug_output = obj
- *
- * Set debug output.
- */
VALUE
-rb_parser_set_debug_output(VALUE self, VALUE output)
+rb_ruby_parser_ruby_sourcefile_string(rb_parser_t *p)
+{
+ return rb_str_new_parser_string(p->ruby_sourcefile_string);
+}
+
+int
+rb_ruby_parser_ruby_sourceline(rb_parser_t *p)
{
- struct parser_params *p;
+ return p->ruby_sourceline;
+}
- TypedData_Get_Struct(self, struct parser_params, &parser_data_type, p);
- return p->debug_output = output;
+int
+rb_ruby_parser_lex_state(rb_parser_t *p)
+{
+ return p->lex.state;
}
-#ifndef RIPPER
-#ifdef YYMALLOC
-#define HEAPCNT(n, size) ((n) * (size) / sizeof(YYSTYPE))
-/* Keep the order; NEWHEAP then xmalloc and ADD2HEAP to get rid of
- * potential memory leak */
-#define NEWHEAP() rb_imemo_tmpbuf_parser_heap(0, p->heap, 0)
-#define ADD2HEAP(new, cnt, ptr) ((p->heap = (new))->ptr = (ptr), \
- (new)->cnt = (cnt), (ptr))
+void
+rb_ruby_ripper_parse0(rb_parser_t *p)
+{
+ parser_prepare(p);
+ p->ast = rb_ast_new();
+ ripper_yyparse((void*)p);
+ rb_ast_dispose(p->ast);
+ p->ast = 0;
+ p->eval_tree = 0;
+ p->eval_tree_begin = 0;
+}
-void *
-rb_parser_malloc(struct parser_params *p, size_t size)
+int
+rb_ruby_ripper_dedent_string(rb_parser_t *p, VALUE string, int width)
{
- size_t cnt = HEAPCNT(1, size);
- rb_imemo_tmpbuf_t *n = NEWHEAP();
- void *ptr = xmalloc(size);
+ char *str;
+ long len;
+ int i;
+
+ RSTRING_GETMEM(string, str, len);
+ i = dedent_string_column(str, len, width);
+ if (!i) return 0;
- return ADD2HEAP(n, cnt, ptr);
+ rb_str_modify(string);
+ str = RSTRING_PTR(string);
+ if (RSTRING_LEN(string) != len)
+ rb_fatal("literal string changed: %+"PRIsVALUE, string);
+ MEMMOVE(str, str + i, char, len - i);
+ rb_str_set_len(string, len - i);
+ return i;
}
-void *
-rb_parser_calloc(struct parser_params *p, size_t nelem, size_t size)
+int
+rb_ruby_ripper_initialized_p(rb_parser_t *p)
{
- size_t cnt = HEAPCNT(nelem, size);
- rb_imemo_tmpbuf_t *n = NEWHEAP();
- void *ptr = xcalloc(nelem, size);
+ return p->lex.input != 0;
+}
- return ADD2HEAP(n, cnt, ptr);
+void
+rb_ruby_ripper_parser_initialize(rb_parser_t *p)
+{
+ parser_initialize(p);
}
-void *
-rb_parser_realloc(struct parser_params *p, void *ptr, size_t size)
+long
+rb_ruby_ripper_column(rb_parser_t *p)
{
- rb_imemo_tmpbuf_t *n;
- size_t cnt = HEAPCNT(1, size);
+ return p->lex.ptok - p->lex.pbeg;
+}
- if (ptr && (n = p->heap) != NULL) {
- do {
- if (n->ptr == ptr) {
- n->ptr = ptr = xrealloc(ptr, size);
- if (n->cnt) n->cnt = cnt;
- return ptr;
- }
- } while ((n = n->next) != NULL);
- }
- n = NEWHEAP();
- ptr = xrealloc(ptr, size);
- return ADD2HEAP(n, cnt, ptr);
+long
+rb_ruby_ripper_token_len(rb_parser_t *p)
+{
+ return p->lex.pcur - p->lex.ptok;
}
-void
-rb_parser_free(struct parser_params *p, void *ptr)
+rb_parser_string_t *
+rb_ruby_ripper_lex_lastline(rb_parser_t *p)
{
- rb_imemo_tmpbuf_t **prev = &p->heap, *n;
+ return p->lex.lastline;
+}
- while ((n = *prev) != NULL) {
- if (n->ptr == ptr) {
- *prev = n->next;
- break;
- }
- prev = &n->next;
- }
+VALUE
+rb_ruby_ripper_lex_state_name(struct parser_params *p, int state)
+{
+ return rb_parser_lex_state_name(p, (enum lex_state_e)state);
+}
+
+#ifdef UNIVERSAL_PARSER
+rb_parser_t *
+rb_ripper_parser_params_allocate(const rb_parser_config_t *config)
+{
+ rb_parser_t *p = (rb_parser_t *)config->calloc(1, sizeof(rb_parser_t));
+ p->config = config;
+ return p;
}
#endif
+struct parser_params*
+rb_ruby_ripper_parser_allocate(void)
+{
+ return (struct parser_params *)ruby_xcalloc(1, sizeof(struct parser_params));
+}
+#endif /* RIPPER */
+
+#ifndef RIPPER
void
rb_parser_printf(struct parser_params *p, const char *fmt, ...)
{
@@ -14054,25 +16201,34 @@ rb_parser_printf(struct parser_params *p, const char *fmt, ...)
va_start(ap, fmt);
rb_str_vcatf(mesg, fmt, ap);
va_end(ap);
- if (RSTRING_END(mesg)[-1] == '\n') {
+ if (end_with_newline_p(p, mesg)) {
rb_io_write(p->debug_output, mesg);
p->debug_buffer = Qnil;
}
}
static void
-parser_compile_error(struct parser_params *p, const char *fmt, ...)
+parser_compile_error(struct parser_params *p, const rb_code_location_t *loc, const char *fmt, ...)
{
va_list ap;
+ int lineno, column;
+
+ if (loc) {
+ lineno = loc->end_pos.lineno;
+ column = loc->end_pos.column;
+ }
+ else {
+ lineno = p->ruby_sourceline;
+ column = rb_long2int(p->lex.pcur - p->lex.pbeg);
+ }
rb_io_flush(p->debug_output);
p->error_p = 1;
va_start(ap, fmt);
p->error_buffer =
rb_syntax_error_append(p->error_buffer,
- p->ruby_sourcefile_string,
- p->ruby_sourceline,
- rb_long2int(p->lex.pcur - p->lex.pbeg),
+ p->ruby_sourcefile_string ? rb_str_new_parser_string(p->ruby_sourcefile_string) : Qnil,
+ lineno, column,
p->enc, fmt, ap);
va_end(ap);
}
@@ -14100,31 +16256,30 @@ rb_yytnamerr(struct parser_params *p, char *yyres, const char *yystr)
while (*++yyp) {
switch (*yyp) {
- case '`':
+ case '\'':
if (!bquote) {
- bquote = count_char(yyp+1, '`') + 1;
+ bquote = count_char(yyp+1, '\'') + 1;
if (yyres) memcpy(&yyres[yyn], yyp, bquote);
yyn += bquote;
yyp += bquote - 1;
break;
}
- goto default_char;
-
- case '\'':
- if (bquote && count_char(yyp+1, '\'') + 1 == bquote) {
- if (yyres) memcpy(yyres + yyn, yyp, bquote);
- yyn += bquote;
- yyp += bquote - 1;
- bquote = 0;
- break;
- }
- if (yyp[1] && yyp[1] != '\'' && yyp[2] == '\'') {
- if (yyres) memcpy(yyres + yyn, yyp, 3);
- yyn += 3;
- yyp += 2;
- break;
+ else {
+ if (bquote && count_char(yyp+1, '\'') + 1 == bquote) {
+ if (yyres) memcpy(yyres + yyn, yyp, bquote);
+ yyn += bquote;
+ yyp += bquote - 1;
+ bquote = 0;
+ break;
+ }
+ if (yyp[1] && yyp[1] != '\'' && yyp[2] == '\'') {
+ if (yyres) memcpy(yyres + yyn, yyp, 3);
+ yyn += 3;
+ yyp += 2;
+ break;
+ }
+ goto do_not_strip_quotes;
}
- goto do_not_strip_quotes;
case ',':
goto do_not_strip_quotes;
@@ -14133,7 +16288,6 @@ rb_yytnamerr(struct parser_params *p, char *yyres, const char *yystr)
if (*++yyp != '\\')
goto do_not_strip_quotes;
/* Fall through. */
- default_char:
default:
if (yyres)
yyres[yyn] = *yyp;
@@ -14157,46 +16311,7 @@ rb_yytnamerr(struct parser_params *p, char *yyres, const char *yystr)
#endif
#ifdef RIPPER
-#ifdef RIPPER_DEBUG
-/* :nodoc: */
-static VALUE
-ripper_validate_object(VALUE self, VALUE x)
-{
- if (x == Qfalse) return x;
- if (x == Qtrue) return x;
- if (NIL_P(x)) return x;
- if (UNDEF_P(x))
- rb_raise(rb_eArgError, "Qundef given");
- if (FIXNUM_P(x)) return x;
- if (SYMBOL_P(x)) return x;
- switch (BUILTIN_TYPE(x)) {
- case T_STRING:
- case T_OBJECT:
- case T_ARRAY:
- case T_BIGNUM:
- case T_FLOAT:
- case T_COMPLEX:
- case T_RATIONAL:
- break;
- case T_NODE:
- if (!nd_type_p((NODE *)x, NODE_RIPPER)) {
- rb_raise(rb_eArgError, "NODE given: %p", (void *)x);
- }
- x = ((NODE *)x)->nd_rval;
- break;
- default:
- rb_raise(rb_eArgError, "wrong type of ruby object: %p (%s)",
- (void *)x, rb_obj_classname(x));
- }
- if (!RBASIC_CLASS(x)) {
- rb_raise(rb_eArgError, "hidden ruby object: %p (%s)",
- (void *)x, rb_builtin_type_name(TYPE(x)));
- }
- return x;
-}
-#endif
-
-#define validate(x) ((x) = get_value(x))
+#define validate(x) ((x) = (x) == rb_ripper_none ? Qnil : x)
static VALUE
ripper_dispatch0(struct parser_params *p, ID mid)
@@ -14262,377 +16377,21 @@ ripper_dispatch7(struct parser_params *p, ID mid, VALUE a, VALUE b, VALUE c, VAL
return rb_funcall(p->value, mid, 7, a, b, c, d, e, f, g);
}
-static ID
-ripper_get_id(VALUE v)
-{
- NODE *nd;
- if (!RB_TYPE_P(v, T_NODE)) return 0;
- nd = (NODE *)v;
- if (!nd_type_p(nd, NODE_RIPPER)) return 0;
- return nd->nd_vid;
-}
-
-static VALUE
-ripper_get_value(VALUE v)
-{
- NODE *nd;
- if (UNDEF_P(v)) return Qnil;
- if (!RB_TYPE_P(v, T_NODE)) return v;
- nd = (NODE *)v;
- if (!nd_type_p(nd, NODE_RIPPER)) return Qnil;
- return nd->nd_rval;
-}
-
-static void
+void
ripper_error(struct parser_params *p)
{
p->error_p = TRUE;
}
-static void
-ripper_compile_error(struct parser_params *p, const char *fmt, ...)
-{
- VALUE str;
- va_list args;
-
- va_start(args, fmt);
- str = rb_vsprintf(fmt, args);
- va_end(args);
- rb_funcall(p->value, rb_intern("compile_error"), 1, str);
- ripper_error(p);
-}
-
-static VALUE
-ripper_lex_get_generic(struct parser_params *p, VALUE src)
-{
- VALUE line = rb_funcallv_public(src, id_gets, 0, 0);
- if (!NIL_P(line) && !RB_TYPE_P(line, T_STRING)) {
- rb_raise(rb_eTypeError,
- "gets returned %"PRIsVALUE" (expected String or nil)",
- rb_obj_class(line));
- }
- return line;
-}
-
-static VALUE
-ripper_lex_io_get(struct parser_params *p, VALUE src)
-{
- return rb_io_gets(src);
-}
-
-static VALUE
-ripper_s_allocate(VALUE klass)
-{
- struct parser_params *p;
- VALUE self = TypedData_Make_Struct(klass, struct parser_params,
- &parser_data_type, p);
- p->value = self;
- return self;
-}
-
-#define ripper_initialized_p(r) ((r)->lex.input != 0)
-
-/*
- * call-seq:
- * Ripper.new(src, filename="(ripper)", lineno=1) -> ripper
- *
- * Create a new Ripper object.
- * _src_ must be a String, an IO, or an Object which has #gets method.
- *
- * This method does not starts parsing.
- * See also Ripper#parse and Ripper.parse.
- */
-static VALUE
-ripper_initialize(int argc, VALUE *argv, VALUE self)
-{
- struct parser_params *p;
- VALUE src, fname, lineno;
-
- TypedData_Get_Struct(self, struct parser_params, &parser_data_type, p);
- rb_scan_args(argc, argv, "12", &src, &fname, &lineno);
- if (RB_TYPE_P(src, T_FILE)) {
- p->lex.gets = ripper_lex_io_get;
- }
- else if (rb_respond_to(src, id_gets)) {
- p->lex.gets = ripper_lex_get_generic;
- }
- else {
- StringValue(src);
- p->lex.gets = lex_get_str;
- }
- p->lex.input = src;
- p->eofp = 0;
- if (NIL_P(fname)) {
- fname = STR_NEW2("(ripper)");
- OBJ_FREEZE(fname);
- }
- else {
- StringValueCStr(fname);
- fname = rb_str_new_frozen(fname);
- }
- parser_initialize(p);
-
- p->ruby_sourcefile_string = fname;
- p->ruby_sourcefile = RSTRING_PTR(fname);
- p->ruby_sourceline = NIL_P(lineno) ? 0 : NUM2INT(lineno) - 1;
-
- return Qnil;
-}
-
-static VALUE
-ripper_parse0(VALUE parser_v)
-{
- struct parser_params *p;
-
- TypedData_Get_Struct(parser_v, struct parser_params, &parser_data_type, p);
- parser_prepare(p);
- p->ast = rb_ast_new();
- ripper_yyparse((void*)p);
- rb_ast_dispose(p->ast);
- p->ast = 0;
- return p->result;
-}
-
-static VALUE
-ripper_ensure(VALUE parser_v)
-{
- struct parser_params *p;
-
- TypedData_Get_Struct(parser_v, struct parser_params, &parser_data_type, p);
- p->parsing_thread = Qnil;
- return Qnil;
-}
-
-/*
- * call-seq:
- * ripper.parse
- *
- * Start parsing and returns the value of the root action.
- */
-static VALUE
-ripper_parse(VALUE self)
-{
- struct parser_params *p;
-
- TypedData_Get_Struct(self, struct parser_params, &parser_data_type, p);
- if (!ripper_initialized_p(p)) {
- rb_raise(rb_eArgError, "method called for uninitialized object");
- }
- if (!NIL_P(p->parsing_thread)) {
- if (p->parsing_thread == rb_thread_current())
- rb_raise(rb_eArgError, "Ripper#parse is not reentrant");
- else
- rb_raise(rb_eArgError, "Ripper#parse is not multithread-safe");
- }
- p->parsing_thread = rb_thread_current();
- rb_ensure(ripper_parse0, self, ripper_ensure, self);
-
- return p->result;
-}
-
-/*
- * call-seq:
- * ripper.column -> Integer
- *
- * Return column number of current parsing line.
- * This number starts from 0.
- */
-static VALUE
-ripper_column(VALUE self)
-{
- struct parser_params *p;
- long col;
-
- TypedData_Get_Struct(self, struct parser_params, &parser_data_type, p);
- if (!ripper_initialized_p(p)) {
- rb_raise(rb_eArgError, "method called for uninitialized object");
- }
- if (NIL_P(p->parsing_thread)) return Qnil;
- col = p->lex.ptok - p->lex.pbeg;
- return LONG2NUM(col);
-}
-
-/*
- * call-seq:
- * ripper.filename -> String
- *
- * Return current parsing filename.
- */
-static VALUE
-ripper_filename(VALUE self)
-{
- struct parser_params *p;
-
- TypedData_Get_Struct(self, struct parser_params, &parser_data_type, p);
- if (!ripper_initialized_p(p)) {
- rb_raise(rb_eArgError, "method called for uninitialized object");
- }
- return p->ruby_sourcefile_string;
-}
-
-/*
- * call-seq:
- * ripper.lineno -> Integer
- *
- * Return line number of current parsing line.
- * This number starts from 1.
- */
-static VALUE
-ripper_lineno(VALUE self)
-{
- struct parser_params *p;
-
- TypedData_Get_Struct(self, struct parser_params, &parser_data_type, p);
- if (!ripper_initialized_p(p)) {
- rb_raise(rb_eArgError, "method called for uninitialized object");
- }
- if (NIL_P(p->parsing_thread)) return Qnil;
- return INT2NUM(p->ruby_sourceline);
-}
-
-/*
- * call-seq:
- * ripper.state -> Integer
- *
- * Return scanner state of current token.
- */
-static VALUE
-ripper_state(VALUE self)
-{
- struct parser_params *p;
-
- TypedData_Get_Struct(self, struct parser_params, &parser_data_type, p);
- if (!ripper_initialized_p(p)) {
- rb_raise(rb_eArgError, "method called for uninitialized object");
- }
- if (NIL_P(p->parsing_thread)) return Qnil;
- return INT2NUM(p->lex.state);
-}
-
-/*
- * call-seq:
- * ripper.token -> String
- *
- * Return the current token string.
- */
-static VALUE
-ripper_token(VALUE self)
-{
- struct parser_params *p;
- long pos, len;
-
- TypedData_Get_Struct(self, struct parser_params, &parser_data_type, p);
- if (!ripper_initialized_p(p)) {
- rb_raise(rb_eArgError, "method called for uninitialized object");
- }
- if (NIL_P(p->parsing_thread)) return Qnil;
- pos = p->lex.ptok - p->lex.pbeg;
- len = p->lex.pcur - p->lex.ptok;
- return rb_str_subseq(p->lex.lastline, pos, len);
-}
-
-#ifdef RIPPER_DEBUG
-/* :nodoc: */
-static VALUE
-ripper_assert_Qundef(VALUE self, VALUE obj, VALUE msg)
-{
- StringValue(msg);
- if (UNDEF_P(obj)) {
- rb_raise(rb_eArgError, "%"PRIsVALUE, msg);
- }
- return Qnil;
-}
-
-/* :nodoc: */
-static VALUE
-ripper_value(VALUE self, VALUE obj)
-{
- return ULONG2NUM(obj);
-}
-#endif
-
-/*
- * call-seq:
- * Ripper.lex_state_name(integer) -> string
- *
- * Returns a string representation of lex_state.
- */
-static VALUE
-ripper_lex_state_name(VALUE self, VALUE state)
-{
- return rb_parser_lex_state_name(NUM2INT(state));
-}
-
-void
-Init_ripper(void)
+VALUE
+ripper_value(struct parser_params *p)
{
- ripper_init_eventids1();
- ripper_init_eventids2();
- id_warn = rb_intern_const("warn");
- id_warning = rb_intern_const("warning");
- id_gets = rb_intern_const("gets");
- id_assoc = rb_intern_const("=>");
-
(void)yystpcpy; /* may not used in newer bison */
- InitVM(ripper);
+ return p->value;
}
-void
-InitVM_ripper(void)
-{
- VALUE Ripper;
-
- Ripper = rb_define_class("Ripper", rb_cObject);
- /* version of Ripper */
- rb_define_const(Ripper, "Version", rb_usascii_str_new2(RIPPER_VERSION));
- rb_define_alloc_func(Ripper, ripper_s_allocate);
- rb_define_method(Ripper, "initialize", ripper_initialize, -1);
- rb_define_method(Ripper, "parse", ripper_parse, 0);
- rb_define_method(Ripper, "column", ripper_column, 0);
- rb_define_method(Ripper, "filename", ripper_filename, 0);
- rb_define_method(Ripper, "lineno", ripper_lineno, 0);
- rb_define_method(Ripper, "state", ripper_state, 0);
- rb_define_method(Ripper, "token", ripper_token, 0);
- rb_define_method(Ripper, "end_seen?", rb_parser_end_seen_p, 0);
- rb_define_method(Ripper, "encoding", rb_parser_encoding, 0);
- rb_define_method(Ripper, "yydebug", rb_parser_get_yydebug, 0);
- rb_define_method(Ripper, "yydebug=", rb_parser_set_yydebug, 1);
- rb_define_method(Ripper, "debug_output", rb_parser_get_debug_output, 0);
- rb_define_method(Ripper, "debug_output=", rb_parser_set_debug_output, 1);
- rb_define_method(Ripper, "error?", ripper_error_p, 0);
-#ifdef RIPPER_DEBUG
- rb_define_method(Ripper, "assert_Qundef", ripper_assert_Qundef, 2);
- rb_define_method(Ripper, "rawVALUE", ripper_value, 1);
- rb_define_method(Ripper, "validate_object", ripper_validate_object, 1);
-#endif
-
- rb_define_singleton_method(Ripper, "dedent_string", parser_dedent_string, 2);
- rb_define_private_method(Ripper, "dedent_string", parser_dedent_string, 2);
-
- rb_define_singleton_method(Ripper, "lex_state_name", ripper_lex_state_name, 1);
-
-<% @exprs.each do |expr, desc| -%>
- /* <%=desc%> */
- rb_define_const(Ripper, "<%=expr%>", INT2NUM(<%=expr%>));
-<% end %>
- ripper_init_eventids1_table(Ripper);
- ripper_init_eventids2_table(Ripper);
-
-# if 0
- /* Hack to let RDoc document SCRIPT_LINES__ */
-
- /*
- * When a Hash is assigned to +SCRIPT_LINES__+ the contents of files loaded
- * after the assignment will be added as an Array of lines with the file
- * name as the key.
- */
- rb_define_global_const("SCRIPT_LINES__", Qnil);
-#endif
-
-}
#endif /* RIPPER */
-
/*
* Local variables:
* mode: c
diff --git a/parser_bits.h b/parser_bits.h
new file mode 100644
index 0000000000..ca7535280e
--- /dev/null
+++ b/parser_bits.h
@@ -0,0 +1,564 @@
+#ifndef INTERNAL_BITS2_H /*-*-C-*-vi:se ft=c:*/
+#define INTERNAL_BITS2_H
+/**
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @brief Internal header for bitwise integer algorithms.
+ * @see Henry S. Warren Jr., "Hacker's Delight" (2nd ed.), 2013.
+ * @see SEI CERT C Coding Standard INT32-C. "Ensure that operations on
+ * signed integers do not result in overflow"
+ * @see https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
+ * @see https://clang.llvm.org/docs/LanguageExtensions.html#builtin-rotateleft
+ * @see https://clang.llvm.org/docs/LanguageExtensions.html#builtin-rotateright
+ * @see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/byteswap-uint64-byteswap-ulong-byteswap-ushort
+ * @see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/rotl-rotl64-rotr-rotr64
+ * @see https://docs.microsoft.com/en-us/cpp/intrinsics/bitscanforward-bitscanforward64
+ * @see https://docs.microsoft.com/en-us/cpp/intrinsics/bitscanreverse-bitscanreverse64
+ * @see https://docs.microsoft.com/en-us/cpp/intrinsics/lzcnt16-lzcnt-lzcnt64
+ * @see https://docs.microsoft.com/en-us/cpp/intrinsics/popcnt16-popcnt-popcnt64
+ * @see https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_lzcnt_u32
+ * @see https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_tzcnt_u32
+ * @see https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_rotl64
+ * @see https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_rotr64
+ * @see https://stackoverflow.com/a/776523
+ */
+#include "ruby/internal/config.h"
+#include <limits.h> /* for CHAR_BITS */
+#include <stdint.h> /* for uintptr_t */
+#include "internal/compilers.h" /* for MSC_VERSION_SINCE */
+
+#if MSC_VERSION_SINCE(1310)
+# include <stdlib.h> /* for _byteswap_uint64 */
+#endif
+
+#if defined(HAVE_X86INTRIN_H)
+# include <x86intrin.h> /* for _lzcnt_u64 */
+#elif MSC_VERSION_SINCE(1310)
+# include <intrin.h> /* for the following intrinsics */
+#endif
+
+#if defined(_MSC_VER) && defined(__AVX__)
+# pragma intrinsic(__popcnt)
+# pragma intrinsic(__popcnt64)
+#endif
+
+#if defined(_MSC_VER) && defined(__AVX2__)
+# pragma intrinsic(__lzcnt)
+# pragma intrinsic(__lzcnt64)
+#endif
+
+#if MSC_VERSION_SINCE(1310)
+# pragma intrinsic(_rotl)
+# pragma intrinsic(_rotr)
+# ifdef _WIN64
+# pragma intrinsic(_rotl64)
+# pragma intrinsic(_rotr64)
+# endif
+#endif
+
+#if MSC_VERSION_SINCE(1400)
+# pragma intrinsic(_BitScanForward)
+# pragma intrinsic(_BitScanReverse)
+# ifdef _WIN64
+# pragma intrinsic(_BitScanForward64)
+# pragma intrinsic(_BitScanReverse64)
+# endif
+#endif
+
+#include "parser_value.h" /* for VALUE */
+#include "internal/static_assert.h" /* for STATIC_ASSERT */
+
+/* The most significant bit of the lower part of half-long integer.
+ * If sizeof(long) == 4, this is 0x8000.
+ * If sizeof(long) == 8, this is 0x80000000.
+ */
+#define HALF_LONG_MSB ((SIGNED_VALUE)1<<((SIZEOF_LONG*CHAR_BIT-1)/2))
+
+#define SIGNED_INTEGER_TYPE_P(T) (0 > ((T)0)-1)
+
+#define SIGNED_INTEGER_MIN(T) \
+ ((sizeof(T) == sizeof(int8_t)) ? ((T)INT8_MIN) : \
+ ((sizeof(T) == sizeof(int16_t)) ? ((T)INT16_MIN) : \
+ ((sizeof(T) == sizeof(int32_t)) ? ((T)INT32_MIN) : \
+ ((sizeof(T) == sizeof(int64_t)) ? ((T)INT64_MIN) : \
+ 0))))
+
+#define SIGNED_INTEGER_MAX(T) ((T)(SIGNED_INTEGER_MIN(T) ^ ((T)~(T)0)))
+
+#define UNSIGNED_INTEGER_MAX(T) ((T)~(T)0)
+
+#if __has_builtin(__builtin_mul_overflow_p)
+# define MUL_OVERFLOW_P(a, b) \
+ __builtin_mul_overflow_p((a), (b), (__typeof__(a * b))0)
+#elif __has_builtin(__builtin_mul_overflow)
+# define MUL_OVERFLOW_P(a, b) \
+ __extension__ ({ __typeof__(a) c; __builtin_mul_overflow((a), (b), &c); })
+#endif
+
+#define MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, min, max) ( \
+ (a) == 0 ? 0 : \
+ (a) == -1 ? (b) < -(max) : \
+ (a) > 0 ? \
+ ((b) > 0 ? (max) / (a) < (b) : (min) / (a) > (b)) : \
+ ((b) > 0 ? (min) / (a) < (b) : (max) / (a) > (b)))
+
+#if __has_builtin(__builtin_mul_overflow_p)
+/* __builtin_mul_overflow_p can take bitfield */
+/* and GCC permits bitfields for integers other than int */
+# define MUL_OVERFLOW_FIXNUM_P(a, b) \
+ __extension__ ({ \
+ struct { long fixnum : sizeof(long) * CHAR_BIT - 1; } c = { 0 }; \
+ __builtin_mul_overflow_p((a), (b), c.fixnum); \
+ })
+#else
+# define MUL_OVERFLOW_FIXNUM_P(a, b) \
+ MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, FIXNUM_MIN, FIXNUM_MAX)
+#endif
+
+#ifdef MUL_OVERFLOW_P
+# define MUL_OVERFLOW_LONG_LONG_P(a, b) MUL_OVERFLOW_P(a, b)
+# define MUL_OVERFLOW_LONG_P(a, b) MUL_OVERFLOW_P(a, b)
+# define MUL_OVERFLOW_INT_P(a, b) MUL_OVERFLOW_P(a, b)
+#else
+# define MUL_OVERFLOW_LONG_LONG_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, LLONG_MIN, LLONG_MAX)
+# define MUL_OVERFLOW_LONG_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, LONG_MIN, LONG_MAX)
+# define MUL_OVERFLOW_INT_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, INT_MIN, INT_MAX)
+#endif
+
+#ifdef HAVE_UINT128_T
+# define bit_length(x) \
+ (unsigned int) \
+ (sizeof(x) <= sizeof(int32_t) ? 32 - nlz_int32((uint32_t)(x)) : \
+ sizeof(x) <= sizeof(int64_t) ? 64 - nlz_int64((uint64_t)(x)) : \
+ 128 - nlz_int128((uint128_t)(x)))
+#else
+# define bit_length(x) \
+ (unsigned int) \
+ (sizeof(x) <= sizeof(int32_t) ? 32 - nlz_int32((uint32_t)(x)) : \
+ 64 - nlz_int64((uint64_t)(x)))
+#endif
+
+#ifndef swap16
+# define swap16 ruby_swap16
+#endif
+
+#ifndef swap32
+# define swap32 ruby_swap32
+#endif
+
+#ifndef swap64
+# define swap64 ruby_swap64
+#endif
+
+static inline uint16_t ruby_swap16(uint16_t);
+static inline uint32_t ruby_swap32(uint32_t);
+static inline uint64_t ruby_swap64(uint64_t);
+static inline unsigned nlz_int(unsigned x);
+static inline unsigned nlz_long(unsigned long x);
+static inline unsigned nlz_long_long(unsigned long long x);
+static inline unsigned nlz_intptr(uintptr_t x);
+static inline unsigned nlz_int32(uint32_t x);
+static inline unsigned nlz_int64(uint64_t x);
+#ifdef HAVE_UINT128_T
+static inline unsigned nlz_int128(uint128_t x);
+#endif
+static inline unsigned rb_popcount32(uint32_t x);
+static inline unsigned rb_popcount64(uint64_t x);
+static inline unsigned rb_popcount_intptr(uintptr_t x);
+static inline int ntz_int32(uint32_t x);
+static inline int ntz_int64(uint64_t x);
+static inline int ntz_intptr(uintptr_t x);
+static inline VALUE RUBY_BIT_ROTL(VALUE, int);
+static inline VALUE RUBY_BIT_ROTR(VALUE, int);
+
+static inline uint16_t
+ruby_swap16(uint16_t x)
+{
+#if __has_builtin(__builtin_bswap16)
+ return __builtin_bswap16(x);
+
+#elif MSC_VERSION_SINCE(1310)
+ return _byteswap_ushort(x);
+
+#else
+ return (x << 8) | (x >> 8);
+
+#endif
+}
+
+static inline uint32_t
+ruby_swap32(uint32_t x)
+{
+#if __has_builtin(__builtin_bswap32)
+ return __builtin_bswap32(x);
+
+#elif MSC_VERSION_SINCE(1310)
+ return _byteswap_ulong(x);
+
+#else
+ x = ((x & 0x0000FFFF) << 16) | ((x & 0xFFFF0000) >> 16);
+ x = ((x & 0x00FF00FF) << 8) | ((x & 0xFF00FF00) >> 8);
+ return x;
+
+#endif
+}
+
+static inline uint64_t
+ruby_swap64(uint64_t x)
+{
+#if __has_builtin(__builtin_bswap64)
+ return __builtin_bswap64(x);
+
+#elif MSC_VERSION_SINCE(1310)
+ return _byteswap_uint64(x);
+
+#else
+ x = ((x & 0x00000000FFFFFFFFULL) << 32) | ((x & 0xFFFFFFFF00000000ULL) >> 32);
+ x = ((x & 0x0000FFFF0000FFFFULL) << 16) | ((x & 0xFFFF0000FFFF0000ULL) >> 16);
+ x = ((x & 0x00FF00FF00FF00FFULL) << 8) | ((x & 0xFF00FF00FF00FF00ULL) >> 8);
+ return x;
+
+#endif
+}
+
+static inline unsigned int
+nlz_int32(uint32_t x)
+{
+#if defined(_MSC_VER) && defined(__AVX2__)
+ /* Note: It seems there is no such thing like __LZCNT__ predefined in MSVC.
+ * AMD CPUs have had this instruction for decades (since K10) but for
+ * Intel, Haswell is the oldest one. We need to use __AVX2__ for maximum
+ * safety. */
+ return (unsigned int)__lzcnt(x);
+
+#elif defined(__x86_64__) && defined(__LZCNT__)
+ return (unsigned int)_lzcnt_u32(x);
+
+#elif MSC_VERSION_SINCE(1400) /* &&! defined(__AVX2__) */
+ unsigned long r;
+ return _BitScanReverse(&r, x) ? (31 - (int)r) : 32;
+
+#elif __has_builtin(__builtin_clz)
+ STATIC_ASSERT(sizeof_int, sizeof(int) * CHAR_BIT == 32);
+ return x ? (unsigned int)__builtin_clz(x) : 32;
+
+#else
+ uint32_t y;
+ unsigned n = 32;
+ y = x >> 16; if (y) {n -= 16; x = y;}
+ y = x >> 8; if (y) {n -= 8; x = y;}
+ y = x >> 4; if (y) {n -= 4; x = y;}
+ y = x >> 2; if (y) {n -= 2; x = y;}
+ y = x >> 1; if (y) {return n - 2;}
+ return (unsigned int)(n - x);
+#endif
+}
+
+static inline unsigned int
+nlz_int64(uint64_t x)
+{
+#if defined(_MSC_VER) && defined(__AVX2__)
+ return (unsigned int)__lzcnt64(x);
+
+#elif defined(__x86_64__) && defined(__LZCNT__)
+ return (unsigned int)_lzcnt_u64(x);
+
+#elif defined(_WIN64) && MSC_VERSION_SINCE(1400) /* &&! defined(__AVX2__) */
+ unsigned long r;
+ return _BitScanReverse64(&r, x) ? (63u - (unsigned int)r) : 64;
+
+#elif __has_builtin(__builtin_clzl)
+ if (x == 0) {
+ return 64;
+ }
+ else if (sizeof(long) * CHAR_BIT == 64) {
+ return (unsigned int)__builtin_clzl((unsigned long)x);
+ }
+ else if (sizeof(long long) * CHAR_BIT == 64) {
+ return (unsigned int)__builtin_clzll((unsigned long long)x);
+ }
+ else {
+ /* :FIXME: Is there a way to make this branch a compile-time error? */
+ UNREACHABLE_RETURN(~0);
+ }
+
+#else
+ uint64_t y;
+ unsigned int n = 64;
+ y = x >> 32; if (y) {n -= 32; x = y;}
+ y = x >> 16; if (y) {n -= 16; x = y;}
+ y = x >> 8; if (y) {n -= 8; x = y;}
+ y = x >> 4; if (y) {n -= 4; x = y;}
+ y = x >> 2; if (y) {n -= 2; x = y;}
+ y = x >> 1; if (y) {return n - 2;}
+ return (unsigned int)(n - x);
+
+#endif
+}
+
+#ifdef HAVE_UINT128_T
+static inline unsigned int
+nlz_int128(uint128_t x)
+{
+ uint64_t y = (uint64_t)(x >> 64);
+
+ if (x == 0) {
+ return 128;
+ }
+ else if (y == 0) {
+ return (unsigned int)nlz_int64(x) + 64;
+ }
+ else {
+ return (unsigned int)nlz_int64(y);
+ }
+}
+#endif
+
+static inline unsigned int
+nlz_int(unsigned int x)
+{
+ if (sizeof(unsigned int) * CHAR_BIT == 32) {
+ return nlz_int32((uint32_t)x);
+ }
+ else if (sizeof(unsigned int) * CHAR_BIT == 64) {
+ return nlz_int64((uint64_t)x);
+ }
+ else {
+ UNREACHABLE_RETURN(~0);
+ }
+}
+
+static inline unsigned int
+nlz_long(unsigned long x)
+{
+ if (sizeof(unsigned long) * CHAR_BIT == 32) {
+ return nlz_int32((uint32_t)x);
+ }
+ else if (sizeof(unsigned long) * CHAR_BIT == 64) {
+ return nlz_int64((uint64_t)x);
+ }
+ else {
+ UNREACHABLE_RETURN(~0);
+ }
+}
+
+static inline unsigned int
+nlz_long_long(unsigned long long x)
+{
+ if (sizeof(unsigned long long) * CHAR_BIT == 64) {
+ return nlz_int64((uint64_t)x);
+ }
+#ifdef HAVE_UINT128_T
+ else if (sizeof(unsigned long long) * CHAR_BIT == 128) {
+ return nlz_int128((uint128_t)x);
+ }
+#endif
+ else {
+ UNREACHABLE_RETURN(~0);
+ }
+}
+
+static inline unsigned int
+nlz_intptr(uintptr_t x)
+{
+ if (sizeof(uintptr_t) == sizeof(unsigned int)) {
+ return nlz_int((unsigned int)x);
+ }
+ if (sizeof(uintptr_t) == sizeof(unsigned long)) {
+ return nlz_long((unsigned long)x);
+ }
+ if (sizeof(uintptr_t) == sizeof(unsigned long long)) {
+ return nlz_long_long((unsigned long long)x);
+ }
+ else {
+ UNREACHABLE_RETURN(~0);
+ }
+}
+
+static inline unsigned int
+rb_popcount32(uint32_t x)
+{
+#if defined(_MSC_VER) && defined(__AVX__)
+ /* Note: CPUs since Nehalem and Barcelona have had this instruction so SSE
+ * 4.2 should suffice, but it seems there is no such thing like __SSE_4_2__
+ * predefined macro in MSVC. They do have __AVX__ so use it instead. */
+ return (unsigned int)__popcnt(x);
+
+#elif __has_builtin(__builtin_popcount)
+ STATIC_ASSERT(sizeof_int, sizeof(int) * CHAR_BIT >= 32);
+ return (unsigned int)__builtin_popcount(x);
+
+#else
+ x = (x & 0x55555555) + (x >> 1 & 0x55555555);
+ x = (x & 0x33333333) + (x >> 2 & 0x33333333);
+ x = (x & 0x0f0f0f0f) + (x >> 4 & 0x0f0f0f0f);
+ x = (x & 0x001f001f) + (x >> 8 & 0x001f001f);
+ x = (x & 0x0000003f) + (x >>16 & 0x0000003f);
+ return (unsigned int)x;
+
+#endif
+}
+
+static inline unsigned int
+rb_popcount64(uint64_t x)
+{
+#if defined(_MSC_VER) && defined(__AVX__)
+ return (unsigned int)__popcnt64(x);
+
+#elif __has_builtin(__builtin_popcount)
+ if (sizeof(long) * CHAR_BIT == 64) {
+ return (unsigned int)__builtin_popcountl((unsigned long)x);
+ }
+ else if (sizeof(long long) * CHAR_BIT == 64) {
+ return (unsigned int)__builtin_popcountll((unsigned long long)x);
+ }
+ else {
+ /* :FIXME: Is there a way to make this branch a compile-time error? */
+ UNREACHABLE_RETURN(~0);
+ }
+
+#else
+ x = (x & 0x5555555555555555) + (x >> 1 & 0x5555555555555555);
+ x = (x & 0x3333333333333333) + (x >> 2 & 0x3333333333333333);
+ x = (x & 0x0707070707070707) + (x >> 4 & 0x0707070707070707);
+ x = (x & 0x001f001f001f001f) + (x >> 8 & 0x001f001f001f001f);
+ x = (x & 0x0000003f0000003f) + (x >>16 & 0x0000003f0000003f);
+ x = (x & 0x000000000000007f) + (x >>32 & 0x000000000000007f);
+ return (unsigned int)x;
+
+#endif
+}
+
+static inline unsigned int
+rb_popcount_intptr(uintptr_t x)
+{
+ if (sizeof(uintptr_t) * CHAR_BIT == 64) {
+ return rb_popcount64((uint64_t)x);
+ }
+ else if (sizeof(uintptr_t) * CHAR_BIT == 32) {
+ return rb_popcount32((uint32_t)x);
+ }
+ else {
+ UNREACHABLE_RETURN(~0);
+ }
+}
+
+static inline int
+ntz_int32(uint32_t x)
+{
+#if defined(__x86_64__) && defined(__BMI__)
+ return (unsigned)_tzcnt_u32(x);
+
+#elif MSC_VERSION_SINCE(1400)
+ /* :FIXME: Is there any way to issue TZCNT instead of BSF, apart from using
+ * assembly? Because issuing LZCNT seems possible (see nlz.h). */
+ unsigned long r;
+ return _BitScanForward(&r, x) ? (int)r : 32;
+
+#elif __has_builtin(__builtin_ctz)
+ STATIC_ASSERT(sizeof_int, sizeof(int) * CHAR_BIT == 32);
+ return x ? (unsigned)__builtin_ctz(x) : 32;
+
+#else
+ return rb_popcount32((~x) & (x-1));
+
+#endif
+}
+
+static inline int
+ntz_int64(uint64_t x)
+{
+#if defined(__x86_64__) && defined(__BMI__)
+ return (unsigned)_tzcnt_u64(x);
+
+#elif defined(_WIN64) && MSC_VERSION_SINCE(1400)
+ unsigned long r;
+ return _BitScanForward64(&r, x) ? (int)r : 64;
+
+#elif __has_builtin(__builtin_ctzl)
+ if (x == 0) {
+ return 64;
+ }
+ else if (sizeof(long) * CHAR_BIT == 64) {
+ return (unsigned)__builtin_ctzl((unsigned long)x);
+ }
+ else if (sizeof(long long) * CHAR_BIT == 64) {
+ return (unsigned)__builtin_ctzll((unsigned long long)x);
+ }
+ else {
+ /* :FIXME: Is there a way to make this branch a compile-time error? */
+ UNREACHABLE_RETURN(~0);
+ }
+
+#else
+ return rb_popcount64((~x) & (x-1));
+
+#endif
+}
+
+static inline int
+ntz_intptr(uintptr_t x)
+{
+ if (sizeof(uintptr_t) * CHAR_BIT == 64) {
+ return ntz_int64((uint64_t)x);
+ }
+ else if (sizeof(uintptr_t) * CHAR_BIT == 32) {
+ return ntz_int32((uint32_t)x);
+ }
+ else {
+ UNREACHABLE_RETURN(~0);
+ }
+}
+
+static inline VALUE
+RUBY_BIT_ROTL(VALUE v, int n)
+{
+#if __has_builtin(__builtin_rotateleft32) && (SIZEOF_VALUE * CHAR_BIT == 32)
+ return __builtin_rotateleft32(v, n);
+
+#elif __has_builtin(__builtin_rotateleft64) && (SIZEOF_VALUE * CHAR_BIT == 64)
+ return __builtin_rotateleft64(v, n);
+
+#elif MSC_VERSION_SINCE(1310) && (SIZEOF_VALUE * CHAR_BIT == 32)
+ return _rotl(v, n);
+
+#elif MSC_VERSION_SINCE(1310) && (SIZEOF_VALUE * CHAR_BIT == 64)
+ return _rotl64(v, n);
+
+#elif defined(_lrotl) && (SIZEOF_VALUE == SIZEOF_LONG)
+ return _lrotl(v, n);
+
+#else
+ const int m = (sizeof(VALUE) * CHAR_BIT) - 1;
+ return (v << (n & m)) | (v >> (-n & m));
+#endif
+}
+
+static inline VALUE
+RUBY_BIT_ROTR(VALUE v, int n)
+{
+#if __has_builtin(__builtin_rotateright32) && (SIZEOF_VALUE * CHAR_BIT == 32)
+ return __builtin_rotateright32(v, n);
+
+#elif __has_builtin(__builtin_rotateright64) && (SIZEOF_VALUE * CHAR_BIT == 64)
+ return __builtin_rotateright64(v, n);
+
+#elif MSC_VERSION_SINCE(1310) && (SIZEOF_VALUE * CHAR_BIT == 32)
+ return _rotr(v, n);
+
+#elif MSC_VERSION_SINCE(1310) && (SIZEOF_VALUE * CHAR_BIT == 64)
+ return _rotr64(v, n);
+
+#elif defined(_lrotr) && (SIZEOF_VALUE == SIZEOF_LONG)
+ return _lrotr(v, n);
+
+#else
+ const int m = (sizeof(VALUE) * CHAR_BIT) - 1;
+ return (v << (-n & m)) | (v >> (n & m));
+#endif
+}
+
+#endif /* INTERNAL_BITS2_H */
diff --git a/parser_node.h b/parser_node.h
new file mode 100644
index 0000000000..2955720676
--- /dev/null
+++ b/parser_node.h
@@ -0,0 +1,32 @@
+#ifndef RUBY_PARSER_NODE_H
+#define RUBY_PARSER_NODE_H 1
+/*
+ * This is a header file used by only "parse.y"
+ */
+#include "rubyparser.h"
+#include "internal/compilers.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#if 0
+} /* satisfy cc-mode */
+#endif
+#endif
+
+static inline rb_code_location_t
+code_loc_gen(const rb_code_location_t *loc1, const rb_code_location_t *loc2)
+{
+ rb_code_location_t loc;
+ loc.beg_pos = loc1->beg_pos;
+ loc.end_pos = loc2->end_pos;
+ return loc;
+}
+
+#if defined(__cplusplus)
+#if 0
+{ /* satisfy cc-mode */
+#endif
+} /* extern "C" { */
+#endif
+
+#endif /* RUBY_PARSER_NODE_H */
diff --git a/parser_st.c b/parser_st.c
new file mode 100644
index 0000000000..17f669e763
--- /dev/null
+++ b/parser_st.c
@@ -0,0 +1,164 @@
+#include "parser_st.h"
+#include "parser_bits.h"
+
+#ifndef TRUE
+# define TRUE 1
+#endif
+
+#ifndef FALSE
+# define FALSE 0
+#endif
+
+#undef NOT_RUBY
+#undef RUBY
+#undef RUBY_EXPORT
+
+#undef MEMCPY
+#define MEMCPY(p1,p2,type,n) nonempty_memcpy((p1), (p2), (sizeof(type) * (n)))
+/* The multiplication should not overflow since this macro is used
+ * only with the already allocated size. */
+static inline void *
+nonempty_memcpy(void *dest, const void *src, size_t n)
+{
+ if (n) {
+ return memcpy(dest, src, n);
+ }
+ else {
+ return dest;
+ }
+}
+
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <string.h>
+#include <assert.h>
+
+#ifdef __GNUC__
+#define PREFETCH(addr, write_p) __builtin_prefetch(addr, write_p)
+#define EXPECT(expr, val) __builtin_expect(expr, val)
+#define ATTRIBUTE_UNUSED __attribute__((unused))
+#else
+#define PREFETCH(addr, write_p)
+#define EXPECT(expr, val) (expr)
+#define ATTRIBUTE_UNUSED
+#endif
+
+
+#define st_index_t parser_st_index_t
+#define st_hash_t parser_st_hash_t
+#define st_data_t parser_st_data_t
+#define st_hash_type parser_st_hash_type
+#define st_table parser_st_table
+#define st_table_entry parser_st_table_entry
+#define st_update_callback_func parser_st_update_callback_func
+#define st_foreach_check_callback_func parser_st_foreach_check_callback_func
+#define st_foreach_callback_func parser_st_foreach_callback_func
+#define st_retval parser_st_retval
+
+#define ST_CONTINUE ST2_CONTINUE
+#define ST_STOP ST2_STOP
+#define ST_DELETE ST2_DELETE
+#define ST_CHECK ST2_CHECK
+#define ST_REPLACE ST2_REPLACE
+
+#undef st_numcmp
+#define st_numcmp rb_parser_st_numcmp
+#undef st_numhash
+#define st_numhash rb_parser_st_numhash
+#undef st_free_table
+#define st_free_table rb_parser_st_free_table
+#define rb_st_hash_start rb_parser_st_hash_start
+#undef st_delete
+#define st_delete rb_parser_st_delete
+#undef st_foreach
+#define st_foreach rb_parser_st_foreach
+#undef st_init_numtable
+#define st_init_numtable rb_parser_st_init_numtable
+#undef st_init_table_with_size
+#define st_init_table_with_size rb_parser_st_init_table_with_size
+#undef st_init_existing_table_with_size
+#define st_init_existing_table_with_size rb_parser_st_init_existing_table_with_size
+#undef st_insert
+#define st_insert rb_parser_st_insert
+#undef st_lookup
+#define st_lookup rb_parser_st_lookup
+
+#undef st_table_size
+#define st_table_size rb_parser_st_table_size
+#undef st_clear
+#define st_clear rb_parser_st_clear
+#undef st_init_strtable
+#define st_init_strtable rb_parser_st_init_strtable
+#undef st_init_table
+#define st_init_table rb_parser_st_init_table
+#undef st_init_strcasetable
+#define st_init_strcasetable rb_parser_st_init_strcasetable
+#undef st_init_strtable_with_size
+#define st_init_strtable_with_size rb_parser_st_init_strtable_with_size
+#undef st_init_numtable_with_size
+#define st_init_numtable_with_size rb_parser_st_init_numtable_with_size
+#undef st_init_strcasetable_with_size
+#define st_init_strcasetable_with_size rb_parser_st_init_strcasetable_with_size
+#undef st_memsize
+#define st_memsize rb_parser_st_memsize
+#undef st_get_key
+#define st_get_key rb_parser_st_get_key
+#undef st_add_direct
+#define st_add_direct rb_parser_st_add_direct
+#define rb_st_add_direct_with_hash rb_parser_st_add_direct_with_hash
+#undef st_insert2
+#define st_insert2 rb_parser_st_insert2
+#undef st_replace
+#define st_replace rb_parser_st_replace
+#undef st_copy
+#define st_copy rb_parser_st_copy
+#undef st_delete_safe
+#define st_delete_safe rb_parser_st_delete_safe
+#undef st_shift
+#define st_shift rb_parser_st_shift
+#undef st_cleanup_safe
+#define st_cleanup_safe rb_parser_st_cleanup_safe
+#undef st_update
+#define st_update rb_parser_st_update
+#undef st_foreach_with_replace
+#define st_foreach_with_replace rb_parser_st_foreach_with_replace
+#undef st_foreach_check
+#define st_foreach_check rb_parser_st_foreach_check
+#undef st_keys
+#define st_keys rb_parser_st_keys
+#undef st_keys_check
+#define st_keys_check rb_parser_st_keys_check
+#undef st_values
+#define st_values rb_parser_st_values
+#undef st_values_check
+#define st_values_check rb_parser_st_values_check
+#undef st_hash
+#define st_hash rb_parser_st_hash
+#undef st_hash_uint32
+#define st_hash_uint32 rb_parser_st_hash_uint32
+#undef st_hash_uint
+#define st_hash_uint rb_parser_st_hash_uint
+#undef st_hash_end
+#define st_hash_end rb_parser_st_hash_end
+#undef st_locale_insensitive_strcasecmp
+#define st_locale_insensitive_strcasecmp rb_parser_st_locale_insensitive_strcasecmp
+#undef st_locale_insensitive_strncasecmp
+#define st_locale_insensitive_strncasecmp rb_parser_st_locale_insensitive_strncasecmp
+
+#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+/* GCC warns about unknown sanitizer, which is annoying. */
+# undef NO_SANITIZE
+# define NO_SANITIZE(x, y) \
+ _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-Wattributes\"") \
+ __attribute__((__no_sanitize__(x))) y; \
+ _Pragma("GCC diagnostic pop")
+#endif
+
+#ifndef NO_SANITIZE
+# define NO_SANITIZE(x, y) y
+#endif
+
+#include "st.c"
diff --git a/parser_st.h b/parser_st.h
new file mode 100644
index 0000000000..877b1e9051
--- /dev/null
+++ b/parser_st.h
@@ -0,0 +1,162 @@
+/* This is a public domain general purpose hash table package
+ originally written by Peter Moore @ UCB.
+
+ The hash table data structures were redesigned and the package was
+ rewritten by Vladimir Makarov <vmakarov@redhat.com>. */
+
+#ifndef RUBY_ST2_H
+#define RUBY_ST2_H 1
+
+#if defined(__cplusplus)
+extern "C" {
+#if 0
+} /* satisfy cc-mode */
+#endif
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include "ruby/config.h"
+#include "ruby/backward/2/long_long.h"
+#include "ruby/defines.h"
+
+RUBY_SYMBOL_EXPORT_BEGIN
+
+#if SIZEOF_LONG == SIZEOF_VOIDP
+typedef unsigned long parser_st_data_t;
+#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
+typedef unsigned LONG_LONG parser_st_data_t;
+#else
+# error ---->> parser_st.c requires sizeof(void*) == sizeof(long) or sizeof(LONG_LONG) to be compiled. <<----
+#endif
+#define ST2_DATA_T_DEFINED
+
+#ifndef CHAR_BIT
+# ifdef HAVE_LIMITS_H
+# include <limits.h>
+# else
+# define CHAR_BIT 8
+# endif
+#endif
+#ifndef _
+# define _(args) args
+#endif
+#ifndef ANYARGS
+# ifdef __cplusplus
+# define ANYARGS ...
+# else
+# define ANYARGS
+# endif
+#endif
+
+typedef struct parser_st_table parser_st_table;
+
+typedef parser_st_data_t parser_st_index_t;
+
+/* Maximal value of unsigned integer type parser_st_index_t. */
+#define MAX_ST2_INDEX_VAL (~(parser_st_index_t) 0)
+
+typedef int parser_st_compare_func(parser_st_data_t, parser_st_data_t);
+typedef parser_st_index_t parser_st_hash_func(parser_st_data_t);
+
+typedef char st_check_for_sizeof_parser_st_index_t[SIZEOF_VOIDP == (int)sizeof(parser_st_index_t) ? 1 : -1];
+#define SIZEOF_ST_INDEX_T SIZEOF_VOIDP
+
+struct parser_st_hash_type {
+ int (*compare)(parser_st_data_t, parser_st_data_t); /* parser_st_compare_func* */
+ parser_st_index_t (*hash)(parser_st_data_t); /* parser_st_hash_func* */
+};
+
+#define ST_INDEX_BITS (SIZEOF_ST_INDEX_T * CHAR_BIT)
+
+#if defined(HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR) && defined(HAVE_BUILTIN___BUILTIN_TYPES_COMPATIBLE_P)
+# define ST2_DATA_COMPATIBLE_P(type) \
+ __builtin_choose_expr(__builtin_types_compatible_p(type, parser_st_data_t), 1, 0)
+#else
+# define ST2_DATA_COMPATIBLE_P(type) 0
+#endif
+
+typedef struct parser_st_table_entry parser_st_table_entry;
+
+struct parser_st_table_entry; /* defined in parser_st.c */
+
+struct parser_st_table {
+ /* Cached features of the table -- see st.c for more details. */
+ unsigned char entry_power, bin_power, size_ind;
+ /* How many times the table was rebuilt. */
+ unsigned int rebuilds_num;
+ const struct parser_st_hash_type *type;
+ /* Number of entries currently in the table. */
+ parser_st_index_t num_entries;
+ /* Array of bins used for access by keys. */
+ parser_st_index_t *bins;
+ /* Start and bound index of entries in array entries.
+ entries_starts and entries_bound are in interval
+ [0,allocated_entries]. */
+ parser_st_index_t entries_start, entries_bound;
+ /* Array of size 2^entry_power. */
+ parser_st_table_entry *entries;
+};
+
+#define parser_st_is_member(table,key) rb_parser_st_lookup((table),(key),(parser_st_data_t *)0)
+
+enum parser_st_retval {ST2_CONTINUE, ST2_STOP, ST2_DELETE, ST2_CHECK, ST2_REPLACE};
+
+size_t rb_parser_st_table_size(const struct parser_st_table *tbl);
+parser_st_table *rb_parser_st_init_table(const struct parser_st_hash_type *);
+parser_st_table *rb_parser_st_init_table_with_size(const struct parser_st_hash_type *, parser_st_index_t);
+parser_st_table *rb_parser_st_init_existing_table_with_size(parser_st_table *, const struct parser_st_hash_type *, parser_st_index_t);
+parser_st_table *rb_parser_st_init_numtable(void);
+parser_st_table *rb_parser_st_init_numtable_with_size(parser_st_index_t);
+parser_st_table *rb_parser_st_init_strtable(void);
+parser_st_table *rb_parser_st_init_strtable_with_size(parser_st_index_t);
+parser_st_table *rb_parser_st_init_strcasetable(void);
+parser_st_table *rb_parser_st_init_strcasetable_with_size(parser_st_index_t);
+int rb_parser_st_delete(parser_st_table *, parser_st_data_t *, parser_st_data_t *); /* returns 0:notfound 1:deleted */
+int rb_parser_st_delete_safe(parser_st_table *, parser_st_data_t *, parser_st_data_t *, parser_st_data_t);
+int rb_parser_st_shift(parser_st_table *, parser_st_data_t *, parser_st_data_t *); /* returns 0:notfound 1:deleted */
+int rb_parser_st_insert(parser_st_table *, parser_st_data_t, parser_st_data_t);
+int rb_parser_st_insert2(parser_st_table *, parser_st_data_t, parser_st_data_t, parser_st_data_t (*)(parser_st_data_t));
+int rb_parser_st_lookup(parser_st_table *, parser_st_data_t, parser_st_data_t *);
+int rb_parser_st_get_key(parser_st_table *, parser_st_data_t, parser_st_data_t *);
+typedef int parser_st_update_callback_func(parser_st_data_t *key, parser_st_data_t *value, parser_st_data_t arg, int existing);
+/* *key may be altered, but must equal to the old key, i.e., the
+ * results of hash() are same and compare() returns 0, otherwise the
+ * behavior is undefined */
+int rb_parser_st_update(parser_st_table *table, parser_st_data_t key, parser_st_update_callback_func *func, parser_st_data_t arg);
+typedef int parser_st_foreach_callback_func(parser_st_data_t, parser_st_data_t, parser_st_data_t);
+typedef int parser_st_foreach_check_callback_func(parser_st_data_t, parser_st_data_t, parser_st_data_t, int);
+int rb_parser_st_foreach_with_replace(parser_st_table *tab, parser_st_foreach_check_callback_func *func, parser_st_update_callback_func *replace, parser_st_data_t arg);
+int rb_parser_st_foreach(parser_st_table *, parser_st_foreach_callback_func *, parser_st_data_t);
+int rb_parser_st_foreach_check(parser_st_table *, parser_st_foreach_check_callback_func *, parser_st_data_t, parser_st_data_t);
+parser_st_index_t rb_parser_st_keys(parser_st_table *table, parser_st_data_t *keys, parser_st_index_t size);
+parser_st_index_t rb_parser_st_keys_check(parser_st_table *table, parser_st_data_t *keys, parser_st_index_t size, parser_st_data_t never);
+parser_st_index_t rb_parser_st_values(parser_st_table *table, parser_st_data_t *values, parser_st_index_t size);
+parser_st_index_t rb_parser_st_values_check(parser_st_table *table, parser_st_data_t *values, parser_st_index_t size, parser_st_data_t never);
+void rb_parser_st_add_direct(parser_st_table *, parser_st_data_t, parser_st_data_t);
+void rb_parser_st_free_table(parser_st_table *);
+void rb_parser_st_cleanup_safe(parser_st_table *, parser_st_data_t);
+void rb_parser_st_clear(parser_st_table *);
+parser_st_table *rb_parser_st_replace(parser_st_table *, parser_st_table *);
+parser_st_table *rb_parser_st_copy(parser_st_table *);
+CONSTFUNC(int rb_parser_st_numcmp(parser_st_data_t, parser_st_data_t));
+CONSTFUNC(parser_st_index_t rb_parser_st_numhash(parser_st_data_t));
+PUREFUNC(int rb_parser_st_locale_insensitive_strcasecmp(const char *s1, const char *s2));
+PUREFUNC(int rb_parser_st_locale_insensitive_strncasecmp(const char *s1, const char *s2, size_t n));
+PUREFUNC(size_t rb_parser_st_memsize(const parser_st_table *));
+PUREFUNC(parser_st_index_t rb_parser_st_hash(const void *ptr, size_t len, parser_st_index_t h));
+CONSTFUNC(parser_st_index_t rb_parser_st_hash_uint32(parser_st_index_t h, uint32_t i));
+CONSTFUNC(parser_st_index_t rb_parser_st_hash_uint(parser_st_index_t h, parser_st_index_t i));
+CONSTFUNC(parser_st_index_t rb_parser_st_hash_end(parser_st_index_t h));
+CONSTFUNC(parser_st_index_t rb_parser_st_hash_start(parser_st_index_t h));
+
+RUBY_SYMBOL_EXPORT_END
+
+#if defined(__cplusplus)
+#if 0
+{ /* satisfy cc-mode */
+#endif
+} /* extern "C" { */
+#endif
+
+#endif /* RUBY_ST2_H */
diff --git a/parser_value.h b/parser_value.h
new file mode 100644
index 0000000000..4fe444e82f
--- /dev/null
+++ b/parser_value.h
@@ -0,0 +1,106 @@
+#ifndef EXTERNAL_VALUE_H
+#define EXTERNAL_VALUE_H
+
+#include "ruby/config.h"
+
+#if defined(__DOXYGEN__)
+
+/**
+ * Type that represents a Ruby object. It is an unsigned integer of some kind,
+ * depending on platforms.
+ *
+ * ```CXX
+ * VALUE value = rb_eval_string("ARGF.readlines.map.with_index");
+ * ```
+ *
+ * @warning ::VALUE is not a pointer.
+ * @warning ::VALUE can be wider than `long`.
+ */
+typedef uintptr_t VALUE;
+
+/**
+ * Type that represents a Ruby identifier such as a variable name.
+ *
+ * ```CXX
+ * ID method = rb_intern("method");
+ * VALUE result = rb_funcall(obj, method, 0);
+ * ```
+ *
+ * @note ::rb_cSymbol is a Ruby-level data type for the same thing.
+ */
+typedef uintptr_t ID;
+
+/**
+ * A signed integer type that has the same width with ::VALUE.
+ *
+ * @internal
+ *
+ * @shyouhei wonders: is it guaranteed that `uintptr_t` and `intptr_t` are the
+ * same width? As far as I read ISO/IEC 9899:2018 section 7.20.1.4 paragraph 1
+ * no such description is given... or defined elsewhere?
+ */
+typedef intptr_t SIGNED_VALUE;
+
+/**
+ * Identical to `sizeof(VALUE)`, except it is a macro that can also be used
+ * inside of preprocessor directives such as `#if`. Handy on occasions.
+ */
+#define SIZEOF_VALUE SIZEOF_UINTPTR_T
+
+/**
+ * @private
+ *
+ * A compile-time constant of type ::VALUE whose value is 0.
+ */
+#define RBIMPL_VALUE_NULL UINTPTR_C(0)
+
+/**
+ * @private
+ *
+ * A compile-time constant of type ::VALUE whose value is 1.
+ */
+#define RBIMPL_VALUE_ONE UINTPTR_C(1)
+
+/**
+ * @private
+ *
+ * Maximum possible value that a ::VALUE can take.
+ */
+#define RBIMPL_VALUE_FULL UINTPTR_MAX
+
+#elif defined HAVE_UINTPTR_T && 0
+typedef uintptr_t VALUE;
+typedef uintptr_t ID;
+# define SIGNED_VALUE intptr_t
+# define SIZEOF_VALUE SIZEOF_UINTPTR_T
+# undef PRI_VALUE_PREFIX
+# define RBIMPL_VALUE_NULL UINTPTR_C(0)
+# define RBIMPL_VALUE_ONE UINTPTR_C(1)
+# define RBIMPL_VALUE_FULL UINTPTR_MAX
+
+#elif SIZEOF_LONG == SIZEOF_VOIDP
+typedef unsigned long VALUE;
+typedef unsigned long ID;
+# define SIGNED_VALUE long
+# define SIZEOF_VALUE SIZEOF_LONG
+# define PRI_VALUE_PREFIX "l"
+# define RBIMPL_VALUE_NULL 0UL
+# define RBIMPL_VALUE_ONE 1UL
+# define RBIMPL_VALUE_FULL ULONG_MAX
+
+#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
+typedef unsigned LONG_LONG VALUE;
+typedef unsigned LONG_LONG ID;
+# define SIGNED_VALUE LONG_LONG
+# define LONG_LONG_VALUE 1
+# define SIZEOF_VALUE SIZEOF_LONG_LONG
+# define PRI_VALUE_PREFIX PRI_LL_PREFIX
+# define RBIMPL_VALUE_NULL 0ULL
+# define RBIMPL_VALUE_ONE 1ULL
+# define RBIMPL_VALUE_FULL ULLONG_MAX
+
+#else
+# error ---->> ruby requires sizeof(void*) == sizeof(long) or sizeof(LONG_LONG) to be compiled. <<----
+#endif
+
+#endif /* EXTERNAL_VALUE_H */
diff --git a/prism/api_pack.c b/prism/api_pack.c
new file mode 100644
index 0000000000..98509ae65c
--- /dev/null
+++ b/prism/api_pack.c
@@ -0,0 +1,276 @@
+#include "prism/extension.h"
+
+#ifdef PRISM_EXCLUDE_PACK
+
+void
+Init_prism_pack(void) {}
+
+#else
+
+static VALUE rb_cPrism;
+static VALUE rb_cPrismPack;
+static VALUE rb_cPrismPackDirective;
+static VALUE rb_cPrismPackFormat;
+
+static VALUE v3_2_0_symbol;
+static VALUE pack_symbol;
+static VALUE unpack_symbol;
+
+#if SIZEOF_UINT64_T == SIZEOF_LONG_LONG
+# define UINT64T2NUM(x) ULL2NUM(x)
+# define NUM2UINT64T(x) (uint64_t)NUM2ULL(x)
+#elif SIZEOF_UINT64_T == SIZEOF_LONG
+# define UINT64T2NUM(x) ULONG2NUM(x)
+# define NUM2UINT64T(x) (uint64_t)NUM2ULONG(x)
+#else
+// error No uint64_t conversion
+#endif
+
+static VALUE
+pack_type_to_symbol(pm_pack_type type) {
+ switch (type) {
+ case PM_PACK_SPACE:
+ return ID2SYM(rb_intern("SPACE"));
+ case PM_PACK_COMMENT:
+ return ID2SYM(rb_intern("COMMENT"));
+ case PM_PACK_INTEGER:
+ return ID2SYM(rb_intern("INTEGER"));
+ case PM_PACK_UTF8:
+ return ID2SYM(rb_intern("UTF8"));
+ case PM_PACK_BER:
+ return ID2SYM(rb_intern("BER"));
+ case PM_PACK_FLOAT:
+ return ID2SYM(rb_intern("FLOAT"));
+ case PM_PACK_STRING_SPACE_PADDED:
+ return ID2SYM(rb_intern("STRING_SPACE_PADDED"));
+ case PM_PACK_STRING_NULL_PADDED:
+ return ID2SYM(rb_intern("STRING_NULL_PADDED"));
+ case PM_PACK_STRING_NULL_TERMINATED:
+ return ID2SYM(rb_intern("STRING_NULL_TERMINATED"));
+ case PM_PACK_STRING_MSB:
+ return ID2SYM(rb_intern("STRING_MSB"));
+ case PM_PACK_STRING_LSB:
+ return ID2SYM(rb_intern("STRING_LSB"));
+ case PM_PACK_STRING_HEX_HIGH:
+ return ID2SYM(rb_intern("STRING_HEX_HIGH"));
+ case PM_PACK_STRING_HEX_LOW:
+ return ID2SYM(rb_intern("STRING_HEX_LOW"));
+ case PM_PACK_STRING_UU:
+ return ID2SYM(rb_intern("STRING_UU"));
+ case PM_PACK_STRING_MIME:
+ return ID2SYM(rb_intern("STRING_MIME"));
+ case PM_PACK_STRING_BASE64:
+ return ID2SYM(rb_intern("STRING_BASE64"));
+ case PM_PACK_STRING_FIXED:
+ return ID2SYM(rb_intern("STRING_FIXED"));
+ case PM_PACK_STRING_POINTER:
+ return ID2SYM(rb_intern("STRING_POINTER"));
+ case PM_PACK_MOVE:
+ return ID2SYM(rb_intern("MOVE"));
+ case PM_PACK_BACK:
+ return ID2SYM(rb_intern("BACK"));
+ case PM_PACK_NULL:
+ return ID2SYM(rb_intern("NULL"));
+ default:
+ return Qnil;
+ }
+}
+
+static VALUE
+pack_signed_to_symbol(pm_pack_signed signed_type) {
+ switch (signed_type) {
+ case PM_PACK_UNSIGNED:
+ return ID2SYM(rb_intern("UNSIGNED"));
+ case PM_PACK_SIGNED:
+ return ID2SYM(rb_intern("SIGNED"));
+ case PM_PACK_SIGNED_NA:
+ return ID2SYM(rb_intern("SIGNED_NA"));
+ default:
+ return Qnil;
+ }
+}
+
+static VALUE
+pack_endian_to_symbol(pm_pack_endian endian) {
+ switch (endian) {
+ case PM_PACK_AGNOSTIC_ENDIAN:
+ return ID2SYM(rb_intern("AGNOSTIC_ENDIAN"));
+ case PM_PACK_LITTLE_ENDIAN:
+ return ID2SYM(rb_intern("LITTLE_ENDIAN"));
+ case PM_PACK_BIG_ENDIAN:
+ return ID2SYM(rb_intern("BIG_ENDIAN"));
+ case PM_PACK_NATIVE_ENDIAN:
+ return ID2SYM(rb_intern("NATIVE_ENDIAN"));
+ case PM_PACK_ENDIAN_NA:
+ return ID2SYM(rb_intern("ENDIAN_NA"));
+ default:
+ return Qnil;
+ }
+}
+
+static VALUE
+pack_size_to_symbol(pm_pack_size size) {
+ switch (size) {
+ case PM_PACK_SIZE_SHORT:
+ return ID2SYM(rb_intern("SIZE_SHORT"));
+ case PM_PACK_SIZE_INT:
+ return ID2SYM(rb_intern("SIZE_INT"));
+ case PM_PACK_SIZE_LONG:
+ return ID2SYM(rb_intern("SIZE_LONG"));
+ case PM_PACK_SIZE_LONG_LONG:
+ return ID2SYM(rb_intern("SIZE_LONG_LONG"));
+ case PM_PACK_SIZE_8:
+ return ID2SYM(rb_intern("SIZE_8"));
+ case PM_PACK_SIZE_16:
+ return ID2SYM(rb_intern("SIZE_16"));
+ case PM_PACK_SIZE_32:
+ return ID2SYM(rb_intern("SIZE_32"));
+ case PM_PACK_SIZE_64:
+ return ID2SYM(rb_intern("SIZE_64"));
+ case PM_PACK_SIZE_P:
+ return ID2SYM(rb_intern("SIZE_P"));
+ case PM_PACK_SIZE_NA:
+ return ID2SYM(rb_intern("SIZE_NA"));
+ default:
+ return Qnil;
+ }
+}
+
+static VALUE
+pack_length_type_to_symbol(pm_pack_length_type length_type) {
+ switch (length_type) {
+ case PM_PACK_LENGTH_FIXED:
+ return ID2SYM(rb_intern("LENGTH_FIXED"));
+ case PM_PACK_LENGTH_MAX:
+ return ID2SYM(rb_intern("LENGTH_MAX"));
+ case PM_PACK_LENGTH_RELATIVE:
+ return ID2SYM(rb_intern("LENGTH_RELATIVE"));
+ case PM_PACK_LENGTH_NA:
+ return ID2SYM(rb_intern("LENGTH_NA"));
+ default:
+ return Qnil;
+ }
+}
+
+static VALUE
+pack_encoding_to_ruby(pm_pack_encoding encoding) {
+ int index;
+ switch (encoding) {
+ case PM_PACK_ENCODING_ASCII_8BIT:
+ index = rb_ascii8bit_encindex();
+ break;
+ case PM_PACK_ENCODING_US_ASCII:
+ index = rb_usascii_encindex();
+ break;
+ case PM_PACK_ENCODING_UTF_8:
+ index = rb_utf8_encindex();
+ break;
+ default:
+ return Qnil;
+ }
+ return rb_enc_from_encoding(rb_enc_from_index(index));
+}
+
+/**
+ * call-seq:
+ * Pack::parse(version, variant, source) -> Format
+ *
+ * Parse the given source and return a format object.
+ */
+static VALUE
+pack_parse(VALUE self, VALUE version_symbol, VALUE variant_symbol, VALUE format_string) {
+ if (version_symbol != v3_2_0_symbol) {
+ rb_raise(rb_eArgError, "invalid version");
+ }
+
+ pm_pack_variant variant;
+ if (variant_symbol == pack_symbol) {
+ variant = PM_PACK_VARIANT_PACK;
+ } else if (variant_symbol == unpack_symbol) {
+ variant = PM_PACK_VARIANT_UNPACK;
+ } else {
+ rb_raise(rb_eArgError, "invalid variant");
+ }
+
+ StringValue(format_string);
+
+ const char *format = RSTRING_PTR(format_string);
+ const char *format_end = format + RSTRING_LEN(format_string);
+ pm_pack_encoding encoding = PM_PACK_ENCODING_START;
+
+ VALUE directives_array = rb_ary_new();
+
+ while (format < format_end) {
+ pm_pack_type type;
+ pm_pack_signed signed_type;
+ pm_pack_endian endian;
+ pm_pack_size size;
+ pm_pack_length_type length_type;
+ uint64_t length;
+
+ const char *directive_start = format;
+
+ pm_pack_result parse_result = pm_pack_parse(variant, &format, format_end, &type, &signed_type, &endian,
+ &size, &length_type, &length, &encoding);
+
+ const char *directive_end = format;
+
+ switch (parse_result) {
+ case PM_PACK_OK:
+ break;
+ case PM_PACK_ERROR_UNSUPPORTED_DIRECTIVE:
+ rb_raise(rb_eArgError, "unsupported directive");
+ case PM_PACK_ERROR_UNKNOWN_DIRECTIVE:
+ rb_raise(rb_eArgError, "unsupported directive");
+ case PM_PACK_ERROR_LENGTH_TOO_BIG:
+ rb_raise(rb_eRangeError, "pack length too big");
+ case PM_PACK_ERROR_BANG_NOT_ALLOWED:
+ rb_raise(rb_eRangeError, "bang not allowed");
+ case PM_PACK_ERROR_DOUBLE_ENDIAN:
+ rb_raise(rb_eRangeError, "double endian");
+ default:
+ rb_bug("parse result");
+ }
+
+ if (type == PM_PACK_END) {
+ break;
+ }
+
+ VALUE directive_args[9] = {
+ version_symbol,
+ variant_symbol,
+ rb_usascii_str_new(directive_start, directive_end - directive_start),
+ pack_type_to_symbol(type),
+ pack_signed_to_symbol(signed_type),
+ pack_endian_to_symbol(endian),
+ pack_size_to_symbol(size),
+ pack_length_type_to_symbol(length_type),
+ UINT64T2NUM(length)
+ };
+
+ rb_ary_push(directives_array, rb_class_new_instance(9, directive_args, rb_cPrismPackDirective));
+ }
+
+ VALUE format_args[2];
+ format_args[0] = directives_array;
+ format_args[1] = pack_encoding_to_ruby(encoding);
+ return rb_class_new_instance(2, format_args, rb_cPrismPackFormat);
+}
+
+/**
+ * The function that gets called when Ruby initializes the prism extension.
+ */
+void
+Init_prism_pack(void) {
+ rb_cPrism = rb_define_module("Prism");
+ rb_cPrismPack = rb_define_module_under(rb_cPrism, "Pack");
+ rb_cPrismPackDirective = rb_define_class_under(rb_cPrismPack, "Directive", rb_cObject);
+ rb_cPrismPackFormat = rb_define_class_under(rb_cPrismPack, "Format", rb_cObject);
+ rb_define_singleton_method(rb_cPrismPack, "parse", pack_parse, 3);
+
+ v3_2_0_symbol = ID2SYM(rb_intern("v3_2_0"));
+ pack_symbol = ID2SYM(rb_intern("pack"));
+ unpack_symbol = ID2SYM(rb_intern("unpack"));
+}
+
+#endif
diff --git a/prism/config.yml b/prism/config.yml
new file mode 100644
index 0000000000..56c95e4e55
--- /dev/null
+++ b/prism/config.yml
@@ -0,0 +1,3689 @@
+errors:
+ - ALIAS_ARGUMENT
+ - ALIAS_ARGUMENT_NUMBERED_REFERENCE
+ - AMPAMPEQ_MULTI_ASSIGN
+ - ARGUMENT_AFTER_BLOCK
+ - ARGUMENT_AFTER_FORWARDING_ELLIPSES
+ - ARGUMENT_BARE_HASH
+ - ARGUMENT_BLOCK_FORWARDING
+ - ARGUMENT_BLOCK_MULTI
+ - ARGUMENT_FORMAL_CLASS
+ - ARGUMENT_FORMAL_CONSTANT
+ - ARGUMENT_FORMAL_GLOBAL
+ - ARGUMENT_FORMAL_IVAR
+ - ARGUMENT_FORWARDING_UNBOUND
+ - ARGUMENT_IN
+ - ARGUMENT_NO_FORWARDING_AMP
+ - ARGUMENT_NO_FORWARDING_ELLIPSES
+ - ARGUMENT_NO_FORWARDING_STAR
+ - ARGUMENT_NO_FORWARDING_STAR_STAR
+ - ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT
+ - ARGUMENT_SPLAT_AFTER_SPLAT
+ - ARGUMENT_TERM_PAREN
+ - ARGUMENT_UNEXPECTED_BLOCK
+ - ARRAY_ELEMENT
+ - ARRAY_EXPRESSION
+ - ARRAY_EXPRESSION_AFTER_STAR
+ - ARRAY_SEPARATOR
+ - ARRAY_TERM
+ - BEGIN_LONELY_ELSE
+ - BEGIN_TERM
+ - BEGIN_UPCASE_BRACE
+ - BEGIN_UPCASE_TERM
+ - BEGIN_UPCASE_TOPLEVEL
+ - BLOCK_PARAM_LOCAL_VARIABLE
+ - BLOCK_PARAM_PIPE_TERM
+ - BLOCK_TERM_BRACE
+ - BLOCK_TERM_END
+ - CANNOT_PARSE_EXPRESSION
+ - CANNOT_PARSE_STRING_PART
+ - CASE_EXPRESSION_AFTER_CASE
+ - CASE_EXPRESSION_AFTER_WHEN
+ - CASE_MATCH_MISSING_PREDICATE
+ - CASE_MISSING_CONDITIONS
+ - CASE_TERM
+ - CLASS_IN_METHOD
+ - CLASS_NAME
+ - CLASS_SUPERCLASS
+ - CLASS_TERM
+ - CLASS_UNEXPECTED_END
+ - CLASS_VARIABLE_BARE
+ - CONDITIONAL_ELSIF_PREDICATE
+ - CONDITIONAL_IF_PREDICATE
+ - CONDITIONAL_PREDICATE_TERM
+ - CONDITIONAL_TERM
+ - CONDITIONAL_TERM_ELSE
+ - CONDITIONAL_UNLESS_PREDICATE
+ - CONDITIONAL_UNTIL_PREDICATE
+ - CONDITIONAL_WHILE_PREDICATE
+ - CONSTANT_PATH_COLON_COLON_CONSTANT
+ - DEF_ENDLESS
+ - DEF_ENDLESS_SETTER
+ - DEF_NAME
+ - DEF_PARAMS_TERM
+ - DEF_PARAMS_TERM_PAREN
+ - DEF_RECEIVER
+ - DEF_RECEIVER_TERM
+ - DEF_TERM
+ - DEFINED_EXPRESSION
+ - EMBDOC_TERM
+ - EMBEXPR_END
+ - EMBVAR_INVALID
+ - END_UPCASE_BRACE
+ - END_UPCASE_TERM
+ - ESCAPE_INVALID_CONTROL
+ - ESCAPE_INVALID_CONTROL_REPEAT
+ - ESCAPE_INVALID_HEXADECIMAL
+ - ESCAPE_INVALID_META
+ - ESCAPE_INVALID_META_REPEAT
+ - ESCAPE_INVALID_UNICODE
+ - ESCAPE_INVALID_UNICODE_CM_FLAGS
+ - ESCAPE_INVALID_UNICODE_LITERAL
+ - ESCAPE_INVALID_UNICODE_LONG
+ - ESCAPE_INVALID_UNICODE_TERM
+ - EXPECT_ARGUMENT
+ - EXPECT_EOL_AFTER_STATEMENT
+ - EXPECT_EXPRESSION_AFTER_AMPAMPEQ
+ - EXPECT_EXPRESSION_AFTER_COMMA
+ - EXPECT_EXPRESSION_AFTER_EQUAL
+ - EXPECT_EXPRESSION_AFTER_LESS_LESS
+ - EXPECT_EXPRESSION_AFTER_LPAREN
+ - EXPECT_EXPRESSION_AFTER_OPERATOR
+ - EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ
+ - EXPECT_EXPRESSION_AFTER_QUESTION
+ - EXPECT_EXPRESSION_AFTER_SPLAT
+ - EXPECT_EXPRESSION_AFTER_SPLAT_HASH
+ - EXPECT_EXPRESSION_AFTER_STAR
+ - EXPECT_IDENT_REQ_PARAMETER
+ - EXPECT_LPAREN_REQ_PARAMETER
+ - EXPECT_MESSAGE
+ - EXPECT_RBRACKET
+ - EXPECT_RPAREN
+ - EXPECT_RPAREN_AFTER_MULTI
+ - EXPECT_RPAREN_REQ_PARAMETER
+ - EXPECT_STRING_CONTENT
+ - EXPECT_WHEN_DELIMITER
+ - EXPRESSION_BARE_HASH
+ - EXPRESSION_NOT_WRITABLE
+ - EXPRESSION_NOT_WRITABLE_ENCODING
+ - EXPRESSION_NOT_WRITABLE_FALSE
+ - EXPRESSION_NOT_WRITABLE_FILE
+ - EXPRESSION_NOT_WRITABLE_LINE
+ - EXPRESSION_NOT_WRITABLE_NIL
+ - EXPRESSION_NOT_WRITABLE_SELF
+ - EXPRESSION_NOT_WRITABLE_TRUE
+ - FLOAT_PARSE
+ - FOR_COLLECTION
+ - FOR_IN
+ - FOR_INDEX
+ - FOR_TERM
+ - GLOBAL_VARIABLE_BARE
+ - HASH_EXPRESSION_AFTER_LABEL
+ - HASH_KEY
+ - HASH_ROCKET
+ - HASH_TERM
+ - HASH_VALUE
+ - HEREDOC_TERM
+ - INCOMPLETE_QUESTION_MARK
+ - INCOMPLETE_VARIABLE_CLASS
+ - INCOMPLETE_VARIABLE_CLASS_3_3_0
+ - INCOMPLETE_VARIABLE_INSTANCE
+ - INCOMPLETE_VARIABLE_INSTANCE_3_3_0
+ - INSTANCE_VARIABLE_BARE
+ - INVALID_BLOCK_EXIT
+ - INVALID_CHARACTER
+ - INVALID_ENCODING_MAGIC_COMMENT
+ - INVALID_FLOAT_EXPONENT
+ - INVALID_LOCAL_VARIABLE_READ
+ - INVALID_LOCAL_VARIABLE_WRITE
+ - INVALID_MULTIBYTE_CHAR
+ - INVALID_MULTIBYTE_CHARACTER
+ - INVALID_MULTIBYTE_ESCAPE
+ - INVALID_NUMBER_BINARY
+ - INVALID_NUMBER_DECIMAL
+ - INVALID_NUMBER_HEXADECIMAL
+ - INVALID_NUMBER_OCTAL
+ - INVALID_NUMBER_UNDERSCORE
+ - INVALID_PERCENT
+ - INVALID_PRINTABLE_CHARACTER
+ - INVALID_RETRY_AFTER_ELSE
+ - INVALID_RETRY_AFTER_ENSURE
+ - INVALID_RETRY_WITHOUT_RESCUE
+ - INVALID_VARIABLE_GLOBAL
+ - INVALID_VARIABLE_GLOBAL_3_3_0
+ - INVALID_YIELD
+ - IT_NOT_ALLOWED_NUMBERED
+ - IT_NOT_ALLOWED_ORDINARY
+ - LAMBDA_OPEN
+ - LAMBDA_TERM_BRACE
+ - LAMBDA_TERM_END
+ - LIST_I_LOWER_ELEMENT
+ - LIST_I_LOWER_TERM
+ - LIST_I_UPPER_ELEMENT
+ - LIST_I_UPPER_TERM
+ - LIST_W_LOWER_ELEMENT
+ - LIST_W_LOWER_TERM
+ - LIST_W_UPPER_ELEMENT
+ - LIST_W_UPPER_TERM
+ - MALLOC_FAILED
+ - MIXED_ENCODING
+ - MODULE_IN_METHOD
+ - MODULE_NAME
+ - MODULE_TERM
+ - MULTI_ASSIGN_MULTI_SPLATS
+ - MULTI_ASSIGN_UNEXPECTED_REST
+ - NO_LOCAL_VARIABLE
+ - NOT_EXPRESSION
+ - NUMBER_LITERAL_UNDERSCORE
+ - NUMBERED_PARAMETER_IT
+ - NUMBERED_PARAMETER_ORDINARY
+ - NUMBERED_PARAMETER_OUTER_SCOPE
+ - OPERATOR_MULTI_ASSIGN
+ - OPERATOR_WRITE_ARGUMENTS
+ - OPERATOR_WRITE_BLOCK
+ - PARAMETER_ASSOC_SPLAT_MULTI
+ - PARAMETER_BLOCK_MULTI
+ - PARAMETER_CIRCULAR
+ - PARAMETER_METHOD_NAME
+ - PARAMETER_NAME_DUPLICATED
+ - PARAMETER_NO_DEFAULT
+ - PARAMETER_NO_DEFAULT_KW
+ - PARAMETER_NUMBERED_RESERVED
+ - PARAMETER_ORDER
+ - PARAMETER_SPLAT_MULTI
+ - PARAMETER_STAR
+ - PARAMETER_UNEXPECTED_FWD
+ - PARAMETER_WILD_LOOSE_COMMA
+ - PATTERN_CAPTURE_DUPLICATE
+ - PATTERN_EXPRESSION_AFTER_BRACKET
+ - PATTERN_EXPRESSION_AFTER_COMMA
+ - PATTERN_EXPRESSION_AFTER_HROCKET
+ - PATTERN_EXPRESSION_AFTER_IN
+ - PATTERN_EXPRESSION_AFTER_KEY
+ - PATTERN_EXPRESSION_AFTER_PAREN
+ - PATTERN_EXPRESSION_AFTER_PIN
+ - PATTERN_EXPRESSION_AFTER_PIPE
+ - PATTERN_EXPRESSION_AFTER_RANGE
+ - PATTERN_EXPRESSION_AFTER_REST
+ - PATTERN_HASH_KEY
+ - PATTERN_HASH_KEY_DUPLICATE
+ - PATTERN_HASH_KEY_LABEL
+ - PATTERN_HASH_KEY_LOCALS
+ - PATTERN_IDENT_AFTER_HROCKET
+ - PATTERN_LABEL_AFTER_COMMA
+ - PATTERN_REST
+ - PATTERN_TERM_BRACE
+ - PATTERN_TERM_BRACKET
+ - PATTERN_TERM_PAREN
+ - PIPEPIPEEQ_MULTI_ASSIGN
+ - REGEXP_ENCODING_OPTION_MISMATCH
+ - REGEXP_INCOMPAT_CHAR_ENCODING
+ - REGEXP_INVALID_UNICODE_RANGE
+ - REGEXP_NON_ESCAPED_MBC
+ - REGEXP_TERM
+ - REGEXP_UNKNOWN_OPTIONS
+ - REGEXP_UTF8_CHAR_NON_UTF8_REGEXP
+ - RESCUE_EXPRESSION
+ - RESCUE_MODIFIER_VALUE
+ - RESCUE_TERM
+ - RESCUE_VARIABLE
+ - RETURN_INVALID
+ - SCRIPT_NOT_FOUND
+ - SINGLETON_FOR_LITERALS
+ - STATEMENT_ALIAS
+ - STATEMENT_POSTEXE_END
+ - STATEMENT_PREEXE_BEGIN
+ - STATEMENT_UNDEF
+ - STRING_CONCATENATION
+ - STRING_INTERPOLATED_TERM
+ - STRING_LITERAL_EOF
+ - STRING_LITERAL_TERM
+ - SYMBOL_INVALID
+ - SYMBOL_TERM_DYNAMIC
+ - SYMBOL_TERM_INTERPOLATED
+ - TERNARY_COLON
+ - TERNARY_EXPRESSION_FALSE
+ - TERNARY_EXPRESSION_TRUE
+ - UNARY_RECEIVER
+ - UNDEF_ARGUMENT
+ - UNEXPECTED_BLOCK_ARGUMENT
+ - UNEXPECTED_TOKEN_CLOSE_CONTEXT
+ - UNEXPECTED_TOKEN_IGNORE
+ - UNTIL_TERM
+ - VOID_EXPRESSION
+ - WHILE_TERM
+ - WRITE_TARGET_IN_METHOD
+ - WRITE_TARGET_READONLY
+ - WRITE_TARGET_UNEXPECTED
+ - XSTRING_TERM
+warnings:
+ - AMBIGUOUS_FIRST_ARGUMENT_MINUS
+ - AMBIGUOUS_FIRST_ARGUMENT_PLUS
+ - AMBIGUOUS_PREFIX_AMPERSAND
+ - AMBIGUOUS_PREFIX_STAR
+ - AMBIGUOUS_PREFIX_STAR_STAR
+ - AMBIGUOUS_SLASH
+ - COMPARISON_AFTER_COMPARISON
+ - DOT_DOT_DOT_EOL
+ - EQUAL_IN_CONDITIONAL
+ - EQUAL_IN_CONDITIONAL_3_3_0
+ - END_IN_METHOD
+ - DUPLICATED_HASH_KEY
+ - DUPLICATED_WHEN_CLAUSE
+ - FLOAT_OUT_OF_RANGE
+ - IGNORED_FROZEN_STRING_LITERAL
+ - INTEGER_IN_FLIP_FLOP
+ - INVALID_CHARACTER
+ - INVALID_NUMBERED_REFERENCE
+ - INVALID_SHAREABLE_CONSTANT_VALUE
+ - KEYWORD_EOL
+ - LITERAL_IN_CONDITION_DEFAULT
+ - LITERAL_IN_CONDITION_VERBOSE
+ - SHEBANG_CARRIAGE_RETURN
+ - UNEXPECTED_CARRIAGE_RETURN
+ - UNREACHABLE_STATEMENT
+ - UNUSED_LOCAL_VARIABLE
+ - VOID_STATEMENT
+tokens:
+ - name: EOF
+ value: 1
+ comment: final token in the file
+ - name: MISSING
+ comment: "a token that was expected but not found"
+ - name: NOT_PROVIDED
+ comment: "a token that was not present but it is okay"
+ - name: AMPERSAND
+ comment: "&"
+ - name: AMPERSAND_AMPERSAND
+ comment: "&&"
+ - name: AMPERSAND_AMPERSAND_EQUAL
+ comment: "&&="
+ - name: AMPERSAND_DOT
+ comment: "&."
+ - name: AMPERSAND_EQUAL
+ comment: "&="
+ - name: BACKTICK
+ comment: "`"
+ - name: BACK_REFERENCE
+ comment: "a back reference"
+ - name: BANG
+ comment: "! or !@"
+ - name: BANG_EQUAL
+ comment: "!="
+ - name: BANG_TILDE
+ comment: "!~"
+ - name: BRACE_LEFT
+ comment: "{"
+ - name: BRACE_RIGHT
+ comment: "}"
+ - name: BRACKET_LEFT
+ comment: "["
+ - name: BRACKET_LEFT_ARRAY
+ comment: "[ for the beginning of an array"
+ - name: BRACKET_LEFT_RIGHT
+ comment: "[]"
+ - name: BRACKET_LEFT_RIGHT_EQUAL
+ comment: "[]="
+ - name: BRACKET_RIGHT
+ comment: "]"
+ - name: CARET
+ comment: "^"
+ - name: CARET_EQUAL
+ comment: "^="
+ - name: CHARACTER_LITERAL
+ comment: "a character literal"
+ - name: CLASS_VARIABLE
+ comment: "a class variable"
+ - name: COLON
+ comment: ":"
+ - name: COLON_COLON
+ comment: "::"
+ - name: COMMA
+ comment: ","
+ - name: COMMENT
+ comment: "a comment"
+ - name: CONSTANT
+ comment: "a constant"
+ - name: DOT
+ comment: "the . call operator"
+ - name: DOT_DOT
+ comment: "the .. range operator"
+ - name: DOT_DOT_DOT
+ comment: "the ... range operator or forwarding parameter"
+ - name: EMBDOC_BEGIN
+ comment: "=begin"
+ - name: EMBDOC_END
+ comment: "=end"
+ - name: EMBDOC_LINE
+ comment: "a line inside of embedded documentation"
+ - name: EMBEXPR_BEGIN
+ comment: "#{"
+ - name: EMBEXPR_END
+ comment: "}"
+ - name: EMBVAR
+ comment: "#"
+ - name: EQUAL
+ comment: "="
+ - name: EQUAL_EQUAL
+ comment: "=="
+ - name: EQUAL_EQUAL_EQUAL
+ comment: "==="
+ - name: EQUAL_GREATER
+ comment: "=>"
+ - name: EQUAL_TILDE
+ comment: "=~"
+ - name: FLOAT
+ comment: "a floating point number"
+ - name: FLOAT_IMAGINARY
+ comment: "a floating pointer number with an imaginary suffix"
+ - name: FLOAT_RATIONAL
+ comment: "a floating pointer number with a rational suffix"
+ - name: FLOAT_RATIONAL_IMAGINARY
+ comment: "a floating pointer number with a rational and imaginary suffix"
+ - name: GLOBAL_VARIABLE
+ comment: "a global variable"
+ - name: GREATER
+ comment: ">"
+ - name: GREATER_EQUAL
+ comment: ">="
+ - name: GREATER_GREATER
+ comment: ">>"
+ - name: GREATER_GREATER_EQUAL
+ comment: ">>="
+ - name: HEREDOC_END
+ comment: "the end of a heredoc"
+ - name: HEREDOC_START
+ comment: "the start of a heredoc"
+ - name: IDENTIFIER
+ comment: "an identifier"
+ - name: IGNORED_NEWLINE
+ comment: "an ignored newline"
+ - name: INSTANCE_VARIABLE
+ comment: "an instance variable"
+ - name: INTEGER
+ comment: "an integer (any base)"
+ - name: INTEGER_IMAGINARY
+ comment: "an integer with an imaginary suffix"
+ - name: INTEGER_RATIONAL
+ comment: "an integer with a rational suffix"
+ - name: INTEGER_RATIONAL_IMAGINARY
+ comment: "an integer with a rational and imaginary suffix"
+ - name: KEYWORD_ALIAS
+ comment: "alias"
+ - name: KEYWORD_AND
+ comment: "and"
+ - name: KEYWORD_BEGIN
+ comment: "begin"
+ - name: KEYWORD_BEGIN_UPCASE
+ comment: "BEGIN"
+ - name: KEYWORD_BREAK
+ comment: "break"
+ - name: KEYWORD_CASE
+ comment: "case"
+ - name: KEYWORD_CLASS
+ comment: "class"
+ - name: KEYWORD_DEF
+ comment: "def"
+ - name: KEYWORD_DEFINED
+ comment: "defined?"
+ - name: KEYWORD_DO
+ comment: "do"
+ - name: KEYWORD_DO_LOOP
+ comment: "do keyword for a predicate in a while, until, or for loop"
+ - name: KEYWORD_ELSE
+ comment: "else"
+ - name: KEYWORD_ELSIF
+ comment: "elsif"
+ - name: KEYWORD_END
+ comment: "end"
+ - name: KEYWORD_END_UPCASE
+ comment: "END"
+ - name: KEYWORD_ENSURE
+ comment: "ensure"
+ - name: KEYWORD_FALSE
+ comment: "false"
+ - name: KEYWORD_FOR
+ comment: "for"
+ - name: KEYWORD_IF
+ comment: "if"
+ - name: KEYWORD_IF_MODIFIER
+ comment: "if in the modifier form"
+ - name: KEYWORD_IN
+ comment: "in"
+ - name: KEYWORD_MODULE
+ comment: "module"
+ - name: KEYWORD_NEXT
+ comment: "next"
+ - name: KEYWORD_NIL
+ comment: "nil"
+ - name: KEYWORD_NOT
+ comment: "not"
+ - name: KEYWORD_OR
+ comment: "or"
+ - name: KEYWORD_REDO
+ comment: "redo"
+ - name: KEYWORD_RESCUE
+ comment: "rescue"
+ - name: KEYWORD_RESCUE_MODIFIER
+ comment: "rescue in the modifier form"
+ - name: KEYWORD_RETRY
+ comment: "retry"
+ - name: KEYWORD_RETURN
+ comment: "return"
+ - name: KEYWORD_SELF
+ comment: "self"
+ - name: KEYWORD_SUPER
+ comment: "super"
+ - name: KEYWORD_THEN
+ comment: "then"
+ - name: KEYWORD_TRUE
+ comment: "true"
+ - name: KEYWORD_UNDEF
+ comment: "undef"
+ - name: KEYWORD_UNLESS
+ comment: "unless"
+ - name: KEYWORD_UNLESS_MODIFIER
+ comment: "unless in the modifier form"
+ - name: KEYWORD_UNTIL
+ comment: "until"
+ - name: KEYWORD_UNTIL_MODIFIER
+ comment: "until in the modifier form"
+ - name: KEYWORD_WHEN
+ comment: "when"
+ - name: KEYWORD_WHILE
+ comment: "while"
+ - name: KEYWORD_WHILE_MODIFIER
+ comment: "while in the modifier form"
+ - name: KEYWORD_YIELD
+ comment: "yield"
+ - name: KEYWORD___ENCODING__
+ comment: "__ENCODING__"
+ - name: KEYWORD___FILE__
+ comment: "__FILE__"
+ - name: KEYWORD___LINE__
+ comment: "__LINE__"
+ - name: LABEL
+ comment: "a label"
+ - name: LABEL_END
+ comment: "the end of a label"
+ - name: LAMBDA_BEGIN
+ comment: "{"
+ - name: LESS
+ comment: "<"
+ - name: LESS_EQUAL
+ comment: "<="
+ - name: LESS_EQUAL_GREATER
+ comment: "<=>"
+ - name: LESS_LESS
+ comment: "<<"
+ - name: LESS_LESS_EQUAL
+ comment: "<<="
+ - name: METHOD_NAME
+ comment: "a method name"
+ - name: MINUS
+ comment: "-"
+ - name: MINUS_EQUAL
+ comment: "-="
+ - name: MINUS_GREATER
+ comment: "->"
+ - name: NEWLINE
+ comment: "a newline character outside of other tokens"
+ - name: NUMBERED_REFERENCE
+ comment: "a numbered reference to a capture group in the previous regular expression match"
+ - name: PARENTHESIS_LEFT
+ comment: "("
+ - name: PARENTHESIS_LEFT_PARENTHESES
+ comment: "( for a parentheses node"
+ - name: PARENTHESIS_RIGHT
+ comment: ")"
+ - name: PERCENT
+ comment: "%"
+ - name: PERCENT_EQUAL
+ comment: "%="
+ - name: PERCENT_LOWER_I
+ comment: "%i"
+ - name: PERCENT_LOWER_W
+ comment: "%w"
+ - name: PERCENT_LOWER_X
+ comment: "%x"
+ - name: PERCENT_UPPER_I
+ comment: "%I"
+ - name: PERCENT_UPPER_W
+ comment: "%W"
+ - name: PIPE
+ comment: "|"
+ - name: PIPE_EQUAL
+ comment: "|="
+ - name: PIPE_PIPE
+ comment: "||"
+ - name: PIPE_PIPE_EQUAL
+ comment: "||="
+ - name: PLUS
+ comment: "+"
+ - name: PLUS_EQUAL
+ comment: "+="
+ - name: QUESTION_MARK
+ comment: "?"
+ - name: REGEXP_BEGIN
+ comment: "the beginning of a regular expression"
+ - name: REGEXP_END
+ comment: "the end of a regular expression"
+ - name: SEMICOLON
+ comment: ";"
+ - name: SLASH
+ comment: "/"
+ - name: SLASH_EQUAL
+ comment: "/="
+ - name: STAR
+ comment: "*"
+ - name: STAR_EQUAL
+ comment: "*="
+ - name: STAR_STAR
+ comment: "**"
+ - name: STAR_STAR_EQUAL
+ comment: "**="
+ - name: STRING_BEGIN
+ comment: "the beginning of a string"
+ - name: STRING_CONTENT
+ comment: "the contents of a string"
+ - name: STRING_END
+ comment: "the end of a string"
+ - name: SYMBOL_BEGIN
+ comment: "the beginning of a symbol"
+ - name: TILDE
+ comment: "~ or ~@"
+ - name: UAMPERSAND
+ comment: "unary &"
+ - name: UCOLON_COLON
+ comment: "unary ::"
+ - name: UDOT_DOT
+ comment: "unary .. operator"
+ - name: UDOT_DOT_DOT
+ comment: "unary ... operator"
+ - name: UMINUS
+ comment: "-@"
+ - name: UMINUS_NUM
+ comment: "-@ for a number"
+ - name: UPLUS
+ comment: "+@"
+ - name: USTAR
+ comment: "unary *"
+ - name: USTAR_STAR
+ comment: "unary **"
+ - name: WORDS_SEP
+ comment: "a separator between words in a list"
+ - name: __END__
+ comment: "marker for the point in the file at which the parser should stop"
+flags:
+ - name: ArgumentsNodeFlags
+ values:
+ - name: CONTAINS_KEYWORD_SPLAT
+ comment: "if arguments contain keyword splat"
+ comment: Flags for arguments nodes.
+ - name: ArrayNodeFlags
+ values:
+ - name: CONTAINS_SPLAT
+ comment: "if array contains splat nodes"
+ comment: Flags for array nodes.
+ - name: CallNodeFlags
+ values:
+ - name: SAFE_NAVIGATION
+ comment: "&. operator"
+ - name: VARIABLE_CALL
+ comment: "a call that could have been a local variable"
+ - name: ATTRIBUTE_WRITE
+ comment: "a call that is an attribute write, so the value being written should be returned"
+ - name: IGNORE_VISIBILITY
+ comment: "a call that ignores method visibility"
+ comment: Flags for call nodes.
+ - name: EncodingFlags
+ values:
+ - name: FORCED_UTF8_ENCODING
+ comment: "internal bytes forced the encoding to UTF-8"
+ - name: FORCED_BINARY_ENCODING
+ comment: "internal bytes forced the encoding to binary"
+ comment: Flags for nodes that have unescaped content.
+ - name: IntegerBaseFlags
+ values:
+ - name: BINARY
+ comment: "0b prefix"
+ - name: DECIMAL
+ comment: "0d or no prefix"
+ - name: OCTAL
+ comment: "0o or 0 prefix"
+ - name: HEXADECIMAL
+ comment: "0x prefix"
+ comment: Flags for integer nodes that correspond to the base of the integer.
+ - name: InterpolatedStringNodeFlags
+ values:
+ - name: FROZEN
+ comment: "frozen by virtue of a `frozen_string_literal: true` comment or `--enable-frozen-string-literal`; only for adjacent string literals like `'a' 'b'`"
+ - name: MUTABLE
+ comment: "mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal`; only for adjacent string literals like `'a' 'b'`"
+ comment: Flags for interpolated string nodes that indicated mutability if they are also marked as literals.
+ - name: KeywordHashNodeFlags
+ values:
+ - name: SYMBOL_KEYS
+ comment: "a keyword hash which only has `AssocNode` elements all with symbol keys, which means the elements can be treated as keyword arguments"
+ comment: Flags for keyword hash nodes.
+ - name: LoopFlags
+ values:
+ - name: BEGIN_MODIFIER
+ comment: "a loop after a begin statement, so the body is executed first before the condition"
+ comment: Flags for while and until loop nodes.
+ - name: ParameterFlags
+ values:
+ - name: REPEATED_PARAMETER
+ comment: "a parameter name that has been repeated in the method signature"
+ comment: Flags for parameter nodes.
+ - name: RangeFlags
+ values:
+ - name: EXCLUDE_END
+ comment: "... operator"
+ comment: Flags for range and flip-flop nodes.
+ - name: RegularExpressionFlags
+ values:
+ - name: IGNORE_CASE
+ comment: "i - ignores the case of characters when matching"
+ - name: EXTENDED
+ comment: "x - ignores whitespace and allows comments in regular expressions"
+ - name: MULTI_LINE
+ comment: "m - allows $ to match the end of lines within strings"
+ - name: ONCE
+ comment: "o - only interpolates values into the regular expression once"
+ - name: EUC_JP
+ comment: "e - forces the EUC-JP encoding"
+ - name: ASCII_8BIT
+ comment: "n - forces the ASCII-8BIT encoding"
+ - name: WINDOWS_31J
+ comment: "s - forces the Windows-31J encoding"
+ - name: UTF_8
+ comment: "u - forces the UTF-8 encoding"
+ - name: FORCED_UTF8_ENCODING
+ comment: "internal bytes forced the encoding to UTF-8"
+ - name: FORCED_BINARY_ENCODING
+ comment: "internal bytes forced the encoding to binary"
+ - name: FORCED_US_ASCII_ENCODING
+ comment: "internal bytes forced the encoding to US-ASCII"
+ comment: Flags for regular expression and match last line nodes.
+ - name: ReturnNodeFlags
+ values:
+ - name: REDUNDANT
+ comment: "a return statement that is redundant because it is the last statement in a method"
+ comment: Flags for return nodes.
+ - name: ShareableConstantNodeFlags
+ values:
+ - name: LITERAL
+ comment: "constant writes that should be modified with shareable constant value literal"
+ - name: EXPERIMENTAL_EVERYTHING
+ comment: "constant writes that should be modified with shareable constant value experimental everything"
+ - name: EXPERIMENTAL_COPY
+ comment: "constant writes that should be modified with shareable constant value experimental copy"
+ comment: Flags for shareable constant nodes.
+ - name: StringFlags
+ values:
+ - name: FORCED_UTF8_ENCODING
+ comment: "internal bytes forced the encoding to UTF-8"
+ - name: FORCED_BINARY_ENCODING
+ comment: "internal bytes forced the encoding to binary"
+ - name: FROZEN
+ comment: "frozen by virtue of a `frozen_string_literal: true` comment or `--enable-frozen-string-literal`"
+ - name: MUTABLE
+ comment: "mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal`"
+ comment: Flags for string nodes.
+ - name: SymbolFlags
+ values:
+ - name: FORCED_UTF8_ENCODING
+ comment: "internal bytes forced the encoding to UTF-8"
+ - name: FORCED_BINARY_ENCODING
+ comment: "internal bytes forced the encoding to binary"
+ - name: FORCED_US_ASCII_ENCODING
+ comment: "internal bytes forced the encoding to US-ASCII"
+ comment: Flags for symbol nodes.
+nodes:
+ - name: AliasGlobalVariableNode
+ fields:
+ - name: new_name
+ type: node
+ comment: |
+ Represents the new name of the global variable that can be used after aliasing. This can be either a global variable, a back reference, or a numbered reference.
+
+ alias $foo $bar
+ ^^^^
+ - name: old_name
+ type: node
+ comment: |
+ Represents the old name of the global variable that could be used before aliasing. This can be either a global variable, a back reference, or a numbered reference.
+
+ alias $foo $bar
+ ^^^^
+ - name: keyword_loc
+ type: location
+ comment: |
+ The location of the `alias` keyword.
+
+ alias $foo $bar
+ ^^^^^
+ comment: |
+ Represents the use of the `alias` keyword to alias a global variable.
+
+ alias $foo $bar
+ ^^^^^^^^^^^^^^^
+ - name: AliasMethodNode
+ fields:
+ - name: new_name
+ type: node
+ - name: old_name
+ type: node
+ - name: keyword_loc
+ type: location
+ comment: |
+ Represents the use of the `alias` keyword to alias a method.
+
+ alias foo bar
+ ^^^^^^^^^^^^^
+ - name: AlternationPatternNode
+ fields:
+ - name: left
+ type: node
+ - name: right
+ type: node
+ - name: operator_loc
+ type: location
+ comment: |
+ Represents an alternation pattern in pattern matching.
+
+ foo => bar | baz
+ ^^^^^^^^^
+ - name: AndNode
+ fields:
+ - name: left
+ type: node
+ comment: |
+ Represents the left side of the expression. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
+
+ left and right
+ ^^^^
+
+ 1 && 2
+ ^
+ - name: right
+ type: node
+ comment: |
+ Represents the right side of the expression. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
+
+ left && right
+ ^^^^^
+
+ 1 and 2
+ ^
+ - name: operator_loc
+ type: location
+ comment: |
+ The location of the `and` keyword or the `&&` operator.
+
+ left and right
+ ^^^
+ comment: |
+ Represents the use of the `&&` operator or the `and` keyword.
+
+ left and right
+ ^^^^^^^^^^^^^^
+ - name: ArgumentsNode
+ fields:
+ - name: flags
+ type: flags
+ kind: ArgumentsNodeFlags
+ - name: arguments
+ type: node[]
+ comment: |
+ Represents a set of arguments to a method or a keyword.
+
+ return foo, bar, baz
+ ^^^^^^^^^^^^^
+ - name: ArrayNode
+ fields:
+ - name: flags
+ type: flags
+ kind: ArrayNodeFlags
+ - name: elements
+ type: node[]
+ comment: Represent the list of zero or more [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression) within the array.
+ - name: opening_loc
+ type: location?
+ comment: |
+ Represents the optional source location for the opening token.
+
+ [1,2,3] # "["
+ %w[foo bar baz] # "%w["
+ %I(apple orange banana) # "%I("
+ foo = 1, 2, 3 # nil
+ - name: closing_loc
+ type: location?
+ comment: |
+ Represents the optional source location for the closing token.
+
+ [1,2,3] # "]"
+ %w[foo bar baz] # "]"
+ %I(apple orange banana) # ")"
+ foo = 1, 2, 3 # nil
+ comment: |
+ Represents an array literal. This can be a regular array using brackets or a special array using % like %w or %i.
+
+ [1, 2, 3]
+ ^^^^^^^^^
+ - name: ArrayPatternNode
+ fields:
+ - name: constant
+ type: node?
+ - name: requireds
+ type: node[]
+ - name: rest
+ type: node?
+ - name: posts
+ type: node[]
+ - name: opening_loc
+ type: location?
+ - name: closing_loc
+ type: location?
+ comment: |
+ Represents an array pattern in pattern matching.
+
+ foo in 1, 2
+ ^^^^^^^^^^^
+
+ foo in [1, 2]
+ ^^^^^^^^^^^^^
+
+ foo in *1
+ ^^^^^^^^^
+
+ foo in Bar[]
+ ^^^^^^^^^^^^
+
+ foo in Bar[1, 2, 3]
+ ^^^^^^^^^^^^^^^^^^^
+ - name: AssocNode
+ fields:
+ - name: key
+ type: node
+ comment: |
+ The key of the association. This can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
+
+ { a: b }
+ ^
+
+ { foo => bar }
+ ^^^
+
+ { def a; end => 1 }
+ ^^^^^^^^^^
+ - name: value
+ type: node
+ comment: |
+ The value of the association, if present. This can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
+
+ { foo => bar }
+ ^^^
+
+ { x: 1 }
+ ^
+ - name: operator_loc
+ type: location?
+ comment: |
+ The location of the `=>` operator, if present.
+
+ { foo => bar }
+ ^^
+ comment: |
+ Represents a hash key/value pair.
+
+ { a => b }
+ ^^^^^^
+ - name: AssocSplatNode
+ fields:
+ - name: value
+ type: node?
+ comment: |
+ The value to be splatted, if present. Will be missing when keyword rest argument forwarding is used.
+
+ { **foo }
+ ^^^
+ - name: operator_loc
+ type: location
+ comment: |
+ The location of the `**` operator.
+
+ { **x }
+ ^^
+ comment: |
+ Represents a splat in a hash literal.
+
+ { **foo }
+ ^^^^^
+ - name: BackReferenceReadNode
+ fields:
+ - name: name
+ type: constant
+ comment: |
+ The name of the back-reference variable, including the leading `$`.
+
+ $& # name `:$&`
+
+ $+ # name `:$+`
+ comment: |
+ Represents reading a reference to a field in the previous match.
+
+ $'
+ ^^
+ - name: BeginNode
+ fields:
+ - name: begin_keyword_loc
+ type: location?
+ - name: statements
+ type: node?
+ kind: StatementsNode
+ - name: rescue_clause
+ type: node?
+ kind: RescueNode
+ - name: else_clause
+ type: node?
+ kind: ElseNode
+ - name: ensure_clause
+ type: node?
+ kind: EnsureNode
+ - name: end_keyword_loc
+ type: location?
+ newline: false
+ comment: |
+ Represents a begin statement.
+
+ begin
+ foo
+ end
+ ^^^^^
+ - name: BlockArgumentNode
+ fields:
+ - name: expression
+ type: node?
+ - name: operator_loc
+ type: location
+ comment: |
+ Represents block method arguments.
+
+ bar(&args)
+ ^^^^^^^^^^
+ - name: BlockLocalVariableNode
+ fields:
+ - name: flags
+ type: flags
+ kind: ParameterFlags
+ - name: name
+ type: constant
+ comment: |
+ Represents a block local variable.
+
+ a { |; b| }
+ ^
+ - name: BlockNode
+ fields:
+ - name: locals
+ type: constant[]
+ - name: parameters
+ type: node?
+ - name: body
+ type: node?
+ - name: opening_loc
+ type: location
+ - name: closing_loc
+ type: location
+ comment: |
+ Represents a block of ruby code.
+
+ [1, 2, 3].each { |i| puts x }
+ ^^^^^^^^^^^^^^
+ - name: BlockParameterNode
+ fields:
+ - name: flags
+ type: flags
+ kind: ParameterFlags
+ - name: name
+ type: constant?
+ - name: name_loc
+ type: location?
+ - name: operator_loc
+ type: location
+ comment: |
+ Represents a block parameter to a method, block, or lambda definition.
+
+ def a(&b)
+ ^^
+ end
+ - name: BlockParametersNode
+ fields:
+ - name: parameters
+ type: node?
+ kind: ParametersNode
+ - name: locals
+ type: node[]
+ kind: BlockLocalVariableNode
+ - name: opening_loc
+ type: location?
+ - name: closing_loc
+ type: location?
+ comment: |
+ Represents a block's parameters declaration.
+
+ -> (a, b = 1; local) { }
+ ^^^^^^^^^^^^^^^^^
+
+ foo do |a, b = 1; local|
+ ^^^^^^^^^^^^^^^^^
+ end
+ - name: BreakNode
+ fields:
+ - name: arguments
+ type: node?
+ kind: ArgumentsNode
+ comment: |
+ The arguments to the break statement, if present. These can be any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
+
+ break foo
+ ^^^
+ - name: keyword_loc
+ type: location
+ comment: |
+ The location of the `break` keyword.
+
+ break foo
+ ^^^^^
+ comment: |
+ Represents the use of the `break` keyword.
+
+ break foo
+ ^^^^^^^^^
+ - name: CallAndWriteNode
+ fields:
+ - name: flags
+ type: flags
+ kind: CallNodeFlags
+ - name: receiver
+ type: node?
+ - name: call_operator_loc
+ type: location?
+ - name: message_loc
+ type: location?
+ - name: read_name
+ type: constant
+ - name: write_name
+ type: constant
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ comment: |
+ Represents the use of the `&&=` operator on a call.
+
+ foo.bar &&= value
+ ^^^^^^^^^^^^^^^^^
+ - name: CallNode
+ fields:
+ - name: flags
+ type: flags
+ kind: CallNodeFlags
+ - name: receiver
+ type: node?
+ comment: |
+ The object that the method is being called on. This can be either `nil` or any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
+
+ foo.bar
+ ^^^
+
+ +foo
+ ^^^
+
+ foo + bar
+ ^^^
+ - name: call_operator_loc
+ type: location?
+ - name: name
+ type: constant
+ - name: message_loc
+ type: location?
+ - name: opening_loc
+ type: location?
+ - name: arguments
+ type: node?
+ kind: ArgumentsNode
+ - name: closing_loc
+ type: location?
+ - name: block
+ type: node?
+ comment: |
+ Represents a method call, in all of the various forms that can take.
+
+ foo
+ ^^^
+
+ foo()
+ ^^^^^
+
+ +foo
+ ^^^^
+
+ foo + bar
+ ^^^^^^^^^
+
+ foo.bar
+ ^^^^^^^
+
+ foo&.bar
+ ^^^^^^^^
+ - name: CallOperatorWriteNode
+ fields:
+ - name: flags
+ type: flags
+ kind: CallNodeFlags
+ - name: receiver
+ type: node?
+ - name: call_operator_loc
+ type: location?
+ - name: message_loc
+ type: location?
+ - name: read_name
+ type: constant
+ - name: write_name
+ type: constant
+ - name: operator
+ type: constant
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ comment: |
+ Represents the use of an assignment operator on a call.
+
+ foo.bar += baz
+ ^^^^^^^^^^^^^^
+ - name: CallOrWriteNode
+ fields:
+ - name: flags
+ type: flags
+ kind: CallNodeFlags
+ - name: receiver
+ type: node?
+ - name: call_operator_loc
+ type: location?
+ - name: message_loc
+ type: location?
+ - name: read_name
+ type: constant
+ - name: write_name
+ type: constant
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ comment: |
+ Represents the use of the `||=` operator on a call.
+
+ foo.bar ||= value
+ ^^^^^^^^^^^^^^^^^
+ - name: CallTargetNode
+ fields:
+ - name: flags
+ type: flags
+ kind: CallNodeFlags
+ - name: receiver
+ type: node
+ - name: call_operator_loc
+ type: location
+ - name: name
+ type: constant
+ - name: message_loc
+ type: location
+ comment: |
+ Represents assigning to a method call.
+
+ foo.bar, = 1
+ ^^^^^^^
+
+ begin
+ rescue => foo.bar
+ ^^^^^^^
+ end
+
+ for foo.bar in baz do end
+ ^^^^^^^
+ - name: CapturePatternNode
+ fields:
+ - name: value
+ type: node
+ - name: target
+ type: node
+ - name: operator_loc
+ type: location
+ comment: |
+ Represents assigning to a local variable in pattern matching.
+
+ foo => [bar => baz]
+ ^^^^^^^^^^^^
+ - name: CaseMatchNode
+ fields:
+ - name: predicate
+ type: node?
+ - name: conditions
+ type: node[]
+ - name: consequent
+ type: node?
+ kind: ElseNode
+ - name: case_keyword_loc
+ type: location
+ - name: end_keyword_loc
+ type: location
+ comment: |
+ Represents the use of a case statement for pattern matching.
+
+ case true
+ in false
+ end
+ ^^^^^^^^^
+ - name: CaseNode
+ fields:
+ - name: predicate
+ type: node?
+ - name: conditions
+ type: node[]
+ - name: consequent
+ type: node?
+ kind: ElseNode
+ - name: case_keyword_loc
+ type: location
+ - name: end_keyword_loc
+ type: location
+ comment: |
+ Represents the use of a case statement.
+
+ case true
+ when false
+ end
+ ^^^^^^^^^^
+ - name: ClassNode
+ fields:
+ - name: locals
+ type: constant[]
+ - name: class_keyword_loc
+ type: location
+ - name: constant_path
+ type: node
+ - name: inheritance_operator_loc
+ type: location?
+ - name: superclass
+ type: node?
+ - name: body
+ type: node?
+ - name: end_keyword_loc
+ type: location
+ - name: name
+ type: constant
+ comment: |
+ Represents a class declaration involving the `class` keyword.
+
+ class Foo end
+ ^^^^^^^^^^^^^
+ - name: ClassVariableAndWriteNode
+ fields:
+ - name: name
+ type: constant
+ - name: name_loc
+ type: location
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ comment: |
+ Represents the use of the `&&=` operator for assignment to a class variable.
+
+ @@target &&= value
+ ^^^^^^^^^^^^^^^^^^
+ - name: ClassVariableOperatorWriteNode
+ fields:
+ - name: name
+ type: constant
+ - name: name_loc
+ type: location
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ - name: operator
+ type: constant
+ comment: |
+ Represents assigning to a class variable using an operator that isn't `=`.
+
+ @@target += value
+ ^^^^^^^^^^^^^^^^^
+ - name: ClassVariableOrWriteNode
+ fields:
+ - name: name
+ type: constant
+ - name: name_loc
+ type: location
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ comment: |
+ Represents the use of the `||=` operator for assignment to a class variable.
+
+ @@target ||= value
+ ^^^^^^^^^^^^^^^^^^
+ - name: ClassVariableReadNode
+ fields:
+ - name: name
+ type: constant
+ comment: |
+ The name of the class variable, which is a `@@` followed by an [identifier](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#identifiers).
+
+ @@abc # name `:@@abc`
+
+ @@_test # name `:@@_test`
+ comment: |
+ Represents referencing a class variable.
+
+ @@foo
+ ^^^^^
+ - name: ClassVariableTargetNode
+ fields:
+ - name: name
+ type: constant
+ comment: |
+ Represents writing to a class variable in a context that doesn't have an explicit value.
+
+ @@foo, @@bar = baz
+ ^^^^^ ^^^^^
+ - name: ClassVariableWriteNode
+ fields:
+ - name: name
+ type: constant
+ comment: |
+ The name of the class variable, which is a `@@` followed by an [identifier](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#identifiers).
+
+ @@abc = 123 # name `@@abc`
+
+ @@_test = :test # name `@@_test`
+ - name: name_loc
+ type: location
+ comment: |
+ The location of the variable name.
+
+ @@foo = :bar
+ ^^^^^
+ - name: value
+ type: node
+ comment: |
+ The value to write to the class variable. This can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
+
+ @@foo = :bar
+ ^^^^
+
+ @@_xyz = 123
+ ^^^
+ - name: operator_loc
+ type: location
+ comment: |
+ The location of the `=` operator.
+
+ @@foo = :bar
+ ^
+ comment: |
+ Represents writing to a class variable.
+
+ @@foo = 1
+ ^^^^^^^^^
+ - name: ConstantAndWriteNode
+ fields:
+ - name: name
+ type: constant
+ - name: name_loc
+ type: location
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ comment: |
+ Represents the use of the `&&=` operator for assignment to a constant.
+
+ Target &&= value
+ ^^^^^^^^^^^^^^^^
+ - name: ConstantOperatorWriteNode
+ fields:
+ - name: name
+ type: constant
+ - name: name_loc
+ type: location
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ - name: operator
+ type: constant
+ comment: |
+ Represents assigning to a constant using an operator that isn't `=`.
+
+ Target += value
+ ^^^^^^^^^^^^^^^
+ - name: ConstantOrWriteNode
+ fields:
+ - name: name
+ type: constant
+ - name: name_loc
+ type: location
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ comment: |
+ Represents the use of the `||=` operator for assignment to a constant.
+
+ Target ||= value
+ ^^^^^^^^^^^^^^^^
+ - name: ConstantPathAndWriteNode
+ fields:
+ - name: target
+ type: node
+ kind: ConstantPathNode
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ comment: |
+ Represents the use of the `&&=` operator for assignment to a constant path.
+
+ Parent::Child &&= value
+ ^^^^^^^^^^^^^^^^^^^^^^^
+ - name: ConstantPathNode
+ fields:
+ - name: parent
+ type: node?
+ comment: |
+ The left-hand node of the path, if present. It can be `nil` or any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). It will be `nil` when the constant lookup is at the root of the module tree.
+
+ Foo::Bar
+ ^^^
+
+ self::Test
+ ^^^^
+
+ a.b::C
+ ^^^
+ - name: child
+ type: node
+ kind:
+ - ConstantReadNode
+ - MissingNode
+ comment: |
+ The right-hand node of the path. Always a `ConstantReadNode` in a
+ valid Ruby syntax tree.
+
+ ::Foo
+ ^^^
+
+ self::Test
+ ^^^^
+
+ a.b::C
+ ^
+ - name: delimiter_loc
+ type: location
+ comment: |
+ The location of the `::` delimiter.
+
+ ::Foo
+ ^^
+
+ One::Two
+ ^^
+ comment: |
+ Represents accessing a constant through a path of `::` operators.
+
+ Foo::Bar
+ ^^^^^^^^
+ - name: ConstantPathOperatorWriteNode
+ fields:
+ - name: target
+ type: node
+ kind: ConstantPathNode
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ - name: operator
+ type: constant
+ comment: |
+ Represents assigning to a constant path using an operator that isn't `=`.
+
+ Parent::Child += value
+ ^^^^^^^^^^^^^^^^^^^^^^
+ - name: ConstantPathOrWriteNode
+ fields:
+ - name: target
+ type: node
+ kind: ConstantPathNode
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ comment: |
+ Represents the use of the `||=` operator for assignment to a constant path.
+
+ Parent::Child ||= value
+ ^^^^^^^^^^^^^^^^^^^^^^^
+ - name: ConstantPathTargetNode
+ fields:
+ - name: parent
+ type: node?
+ - name: child
+ type: node
+ kind:
+ - ConstantReadNode
+ - MissingNode
+ - name: delimiter_loc
+ type: location
+ comment: |
+ Represents writing to a constant path in a context that doesn't have an explicit value.
+
+ Foo::Foo, Bar::Bar = baz
+ ^^^^^^^^ ^^^^^^^^
+ - name: ConstantPathWriteNode
+ fields:
+ - name: target
+ type: node
+ kind: ConstantPathNode
+ comment: |
+ A node representing the constant path being written to.
+
+ Foo::Bar = 1
+ ^^^^^^^^
+
+ ::Foo = :abc
+ ^^^^^
+ - name: operator_loc
+ type: location
+ comment: |
+ The location of the `=` operator.
+
+ ::ABC = 123
+ ^
+ - name: value
+ type: node
+ comment: |
+ The value to write to the constant path. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
+
+ FOO::BAR = :abc
+ ^^^^
+ comment: |
+ Represents writing to a constant path.
+
+ ::Foo = 1
+ ^^^^^^^^^
+
+ Foo::Bar = 1
+ ^^^^^^^^^^^^
+
+ ::Foo::Bar = 1
+ ^^^^^^^^^^^^^^
+ - name: ConstantReadNode
+ fields:
+ - name: name
+ type: constant
+ comment: |
+ The name of the [constant](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#constants).
+
+ X # name `:X`
+
+ SOME_CONSTANT # name `:SOME_CONSTANT`
+ comment: |
+ Represents referencing a constant.
+
+ Foo
+ ^^^
+ - name: ConstantTargetNode
+ fields:
+ - name: name
+ type: constant
+ comment: |
+ Represents writing to a constant in a context that doesn't have an explicit value.
+
+ Foo, Bar = baz
+ ^^^ ^^^
+ - name: ConstantWriteNode
+ fields:
+ - name: name
+ type: constant
+ comment: |
+ The name of the [constant](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#constants).
+
+ Foo = :bar # name `:Foo`
+
+ XYZ = 1 # name `:XYZ`
+ - name: name_loc
+ type: location
+ comment: |
+ The location of the constant name.
+
+ FOO = 1
+ ^^^
+ - name: value
+ type: node
+ comment: |
+ The value to write to the constant. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
+
+ FOO = :bar
+ ^^^^
+
+ MyClass = Class.new
+ ^^^^^^^^^
+ - name: operator_loc
+ type: location
+ comment: |
+ The location of the `=` operator.
+
+ FOO = :bar
+ ^
+ comment: |
+ Represents writing to a constant.
+
+ Foo = 1
+ ^^^^^^^
+ - name: DefNode
+ fields:
+ - name: name
+ type: constant
+ - name: name_loc
+ type: location
+ - name: receiver
+ type: node?
+ - name: parameters
+ type: node?
+ kind: ParametersNode
+ - name: body
+ type: node?
+ - name: locals
+ type: constant[]
+ - name: def_keyword_loc
+ type: location
+ - name: operator_loc
+ type: location?
+ - name: lparen_loc
+ type: location?
+ - name: rparen_loc
+ type: location?
+ - name: equal_loc
+ type: location?
+ - name: end_keyword_loc
+ type: location?
+ comment: |
+ Represents a method definition.
+
+ def method
+ end
+ ^^^^^^^^^^
+ - name: DefinedNode
+ fields:
+ - name: lparen_loc
+ type: location?
+ - name: value
+ type: node
+ - name: rparen_loc
+ type: location?
+ - name: keyword_loc
+ type: location
+ comment: |
+ Represents the use of the `defined?` keyword.
+
+ defined?(a)
+ ^^^^^^^^^^^
+ - name: ElseNode
+ fields:
+ - name: else_keyword_loc
+ type: location
+ - name: statements
+ type: node?
+ kind: StatementsNode
+ - name: end_keyword_loc
+ type: location?
+ comment: |
+ Represents an `else` clause in a `case`, `if`, or `unless` statement.
+
+ if a then b else c end
+ ^^^^^^^^^^
+ - name: EmbeddedStatementsNode
+ fields:
+ - name: opening_loc
+ type: location
+ - name: statements
+ type: node?
+ kind: StatementsNode
+ - name: closing_loc
+ type: location
+ comment: |
+ Represents an interpolated set of statements.
+
+ "foo #{bar}"
+ ^^^^^^
+ - name: EmbeddedVariableNode
+ fields:
+ - name: operator_loc
+ type: location
+ - name: variable
+ type: node
+ comment: |
+ Represents an interpolated variable.
+
+ "foo #@bar"
+ ^^^^^
+ - name: EnsureNode
+ fields:
+ - name: ensure_keyword_loc
+ type: location
+ - name: statements
+ type: node?
+ kind: StatementsNode
+ - name: end_keyword_loc
+ type: location
+ comment: |
+ Represents an `ensure` clause in a `begin` statement.
+
+ begin
+ foo
+ ensure
+ ^^^^^^
+ bar
+ end
+ - name: FalseNode
+ comment: |
+ Represents the use of the literal `false` keyword.
+
+ false
+ ^^^^^
+ - name: FindPatternNode
+ fields:
+ - name: constant
+ type: node?
+ - name: left
+ type: node
+ - name: requireds
+ type: node[]
+ - name: right
+ type: node
+ - name: opening_loc
+ type: location?
+ - name: closing_loc
+ type: location?
+ comment: |
+ Represents a find pattern in pattern matching.
+
+ foo in *bar, baz, *qux
+ ^^^^^^^^^^^^^^^
+
+ foo in [*bar, baz, *qux]
+ ^^^^^^^^^^^^^^^^^
+
+ foo in Foo(*bar, baz, *qux)
+ ^^^^^^^^^^^^^^^^^^^^
+ - name: FlipFlopNode
+ fields:
+ - name: flags
+ type: flags
+ kind: RangeFlags
+ - name: left
+ type: node?
+ - name: right
+ type: node?
+ - name: operator_loc
+ type: location
+ comment: |
+ Represents the use of the `..` or `...` operators to create flip flops.
+
+ baz if foo .. bar
+ ^^^^^^^^^^
+ - name: FloatNode
+ fields:
+ - name: value
+ type: double
+ comment: The value of the floating point number as a Float.
+ comment: |
+ Represents a floating point number literal.
+
+ 1.0
+ ^^^
+ - name: ForNode
+ fields:
+ - name: index
+ type: node
+ - name: collection
+ type: node
+ - name: statements
+ type: node?
+ kind: StatementsNode
+ - name: for_keyword_loc
+ type: location
+ - name: in_keyword_loc
+ type: location
+ - name: do_keyword_loc
+ type: location?
+ - name: end_keyword_loc
+ type: location
+ comment: |
+ Represents the use of the `for` keyword.
+
+ for i in a end
+ ^^^^^^^^^^^^^^
+ - name: ForwardingArgumentsNode
+ comment: |
+ Represents forwarding all arguments to this method to another method.
+
+ def foo(...)
+ bar(...)
+ ^^^
+ end
+ - name: ForwardingParameterNode
+ comment: |
+ Represents the use of the forwarding parameter in a method, block, or lambda declaration.
+
+ def foo(...)
+ ^^^
+ end
+ - name: ForwardingSuperNode
+ fields:
+ - name: block
+ type: node?
+ kind: BlockNode
+ comment: |
+ Represents the use of the `super` keyword without parentheses or arguments.
+
+ super
+ ^^^^^
+ - name: GlobalVariableAndWriteNode
+ fields:
+ - name: name
+ type: constant
+ - name: name_loc
+ type: location
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ comment: |
+ Represents the use of the `&&=` operator for assignment to a global variable.
+
+ $target &&= value
+ ^^^^^^^^^^^^^^^^^
+ - name: GlobalVariableOperatorWriteNode
+ fields:
+ - name: name
+ type: constant
+ - name: name_loc
+ type: location
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ - name: operator
+ type: constant
+ comment: |
+ Represents assigning to a global variable using an operator that isn't `=`.
+
+ $target += value
+ ^^^^^^^^^^^^^^^^
+ - name: GlobalVariableOrWriteNode
+ fields:
+ - name: name
+ type: constant
+ - name: name_loc
+ type: location
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ comment: |
+ Represents the use of the `||=` operator for assignment to a global variable.
+
+ $target ||= value
+ ^^^^^^^^^^^^^^^^^
+ - name: GlobalVariableReadNode
+ fields:
+ - name: name
+ type: constant
+ comment: |
+ The name of the global variable, which is a `$` followed by an [identifier](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#identifier). Alternatively, it can be one of the special global variables designated by a symbol.
+
+ $foo # name `:$foo`
+
+ $_Test # name `:$_Test`
+ comment: |
+ Represents referencing a global variable.
+
+ $foo
+ ^^^^
+ - name: GlobalVariableTargetNode
+ fields:
+ - name: name
+ type: constant
+ comment: |
+ Represents writing to a global variable in a context that doesn't have an explicit value.
+
+ $foo, $bar = baz
+ ^^^^ ^^^^
+ - name: GlobalVariableWriteNode
+ fields:
+ - name: name
+ type: constant
+ comment: |
+ The name of the global variable, which is a `$` followed by an [identifier](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#identifier). Alternatively, it can be one of the special global variables designated by a symbol.
+
+ $foo = :bar # name `:$foo`
+
+ $_Test = 123 # name `:$_Test`
+ - name: name_loc
+ type: location
+ comment: |
+ The location of the global variable's name.
+
+ $foo = :bar
+ ^^^^
+ - name: value
+ type: node
+ comment: |
+ The value to write to the global variable. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
+
+ $foo = :bar
+ ^^^^
+
+ $-xyz = 123
+ ^^^
+ - name: operator_loc
+ type: location
+ comment: |
+ The location of the `=` operator.
+
+ $foo = :bar
+ ^
+ comment: |
+ Represents writing to a global variable.
+
+ $foo = 1
+ ^^^^^^^^
+ - name: HashNode
+ fields:
+ - name: opening_loc
+ type: location
+ comment: |
+ The location of the opening brace.
+
+ { a => b }
+ ^
+ - name: elements
+ type: node[]
+ kind:
+ - AssocNode
+ - AssocSplatNode
+ comment: |
+ The elements of the hash. These can be either `AssocNode`s or `AssocSplatNode`s.
+
+ { a: b }
+ ^^^^
+
+ { **foo }
+ ^^^^^
+ - name: closing_loc
+ type: location
+ comment: |
+ The location of the closing brace.
+
+ { a => b }
+ ^
+ comment: |
+ Represents a hash literal.
+
+ { a => b }
+ ^^^^^^^^^^
+ - name: HashPatternNode
+ fields:
+ - name: constant
+ type: node?
+ - name: elements
+ type: node[]
+ kind: AssocNode
+ - name: rest
+ type: node?
+ kind:
+ - AssocSplatNode
+ - NoKeywordsParameterNode
+ - name: opening_loc
+ type: location?
+ - name: closing_loc
+ type: location?
+ comment: |
+ Represents a hash pattern in pattern matching.
+
+ foo => { a: 1, b: 2 }
+ ^^^^^^^^^^^^^^
+
+ foo => { a: 1, b: 2, **c }
+ ^^^^^^^^^^^^^^^^^^^
+ - name: IfNode
+ fields:
+ - name: if_keyword_loc
+ type: location?
+ comment: |
+ The location of the `if` keyword if present.
+
+ bar if foo
+ ^^
+
+ The `if_keyword_loc` field will be `nil` when the `IfNode` represents a ternary expression.
+ - name: predicate
+ type: node
+ comment: |
+ The node for the condition the `IfNode` is testing.
+
+ if foo
+ ^^^
+ bar
+ end
+
+ bar if foo
+ ^^^
+
+ foo ? bar : baz
+ ^^^
+ - name: then_keyword_loc
+ type: location?
+ comment: |
+ The location of the `then` keyword (if present) or the `?` in a ternary expression, `nil` otherwise.
+
+ if foo then bar end
+ ^^^^
+
+ a ? b : c
+ ^
+ - name: statements
+ type: node?
+ kind: StatementsNode
+ comment: |
+ Represents the body of statements that will be executed when the predicate is evaluated as truthy. Will be `nil` when no body is provided.
+
+ if foo
+ bar
+ ^^^
+ baz
+ ^^^
+ end
+ - name: consequent
+ type: node?
+ comment: |
+ Represents an `ElseNode` or an `IfNode` when there is an `else` or an `elsif` in the `if` statement.
+
+ if foo
+ bar
+ elsif baz
+ ^^^^^^^^^
+ qux
+ ^^^
+ end
+ ^^^
+
+ if foo then bar else baz end
+ ^^^^^^^^^^^^
+ - name: end_keyword_loc
+ type: location?
+ comment: |
+ The location of the `end` keyword if present, `nil` otherwise.
+
+ if foo
+ bar
+ end
+ ^^^
+ newline: predicate
+ comment: |
+ Represents the use of the `if` keyword, either in the block form or the modifier form, or a ternary expression.
+
+ bar if foo
+ ^^^^^^^^^^
+
+ if foo then bar end
+ ^^^^^^^^^^^^^^^^^^^
+
+ foo ? bar : baz
+ ^^^^^^^^^^^^^^^
+ - name: ImaginaryNode
+ fields:
+ - name: numeric
+ type: node
+ kind:
+ - FloatNode
+ - IntegerNode
+ - RationalNode
+ comment: |
+ Represents an imaginary number literal.
+
+ 1.0i
+ ^^^^
+ - name: ImplicitNode
+ fields:
+ - name: value
+ type: node
+ comment: |
+ Represents a node that is implicitly being added to the tree but doesn't correspond directly to a node in the source.
+
+ { foo: }
+ ^^^^
+
+ { Foo: }
+ ^^^^
+
+ foo in { bar: }
+ ^^^^
+ - name: ImplicitRestNode
+ comment: |
+ Represents using a trailing comma to indicate an implicit rest parameter.
+
+ foo { |bar,| }
+ ^
+
+ foo in [bar,]
+ ^
+
+ for foo, in bar do end
+ ^
+
+ foo, = bar
+ ^
+ - name: InNode
+ fields:
+ - name: pattern
+ type: node
+ - name: statements
+ type: node?
+ kind: StatementsNode
+ - name: in_loc
+ type: location
+ - name: then_loc
+ type: location?
+ comment: |
+ Represents the use of the `in` keyword in a case statement.
+
+ case a; in b then c end
+ ^^^^^^^^^^^
+ - name: IndexAndWriteNode
+ fields:
+ - name: flags
+ type: flags
+ kind: CallNodeFlags
+ - name: receiver
+ type: node?
+ - name: call_operator_loc
+ type: location?
+ - name: opening_loc
+ type: location
+ - name: arguments
+ type: node?
+ kind: ArgumentsNode
+ - name: closing_loc
+ type: location
+ - name: block
+ type: node?
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ comment: |
+ Represents the use of the `&&=` operator on a call to the `[]` method.
+
+ foo.bar[baz] &&= value
+ ^^^^^^^^^^^^^^^^^^^^^^
+ - name: IndexOperatorWriteNode
+ fields:
+ - name: flags
+ type: flags
+ kind: CallNodeFlags
+ - name: receiver
+ type: node?
+ - name: call_operator_loc
+ type: location?
+ - name: opening_loc
+ type: location
+ - name: arguments
+ type: node?
+ kind: ArgumentsNode
+ - name: closing_loc
+ type: location
+ - name: block
+ type: node?
+ - name: operator
+ type: constant
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ comment: |
+ Represents the use of an assignment operator on a call to `[]`.
+
+ foo.bar[baz] += value
+ ^^^^^^^^^^^^^^^^^^^^^
+ - name: IndexOrWriteNode
+ fields:
+ - name: flags
+ type: flags
+ kind: CallNodeFlags
+ - name: receiver
+ type: node?
+ - name: call_operator_loc
+ type: location?
+ - name: opening_loc
+ type: location
+ - name: arguments
+ type: node?
+ kind: ArgumentsNode
+ - name: closing_loc
+ type: location
+ - name: block
+ type: node?
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ comment: |
+ Represents the use of the `||=` operator on a call to `[]`.
+
+ foo.bar[baz] ||= value
+ ^^^^^^^^^^^^^^^^^^^^^^
+ - name: IndexTargetNode
+ fields:
+ - name: flags
+ type: flags
+ kind: CallNodeFlags
+ - name: receiver
+ type: node
+ - name: opening_loc
+ type: location
+ - name: arguments
+ type: node?
+ kind: ArgumentsNode
+ - name: closing_loc
+ type: location
+ - name: block
+ type: node?
+ comment: |
+ Represents assigning to an index.
+
+ foo[bar], = 1
+ ^^^^^^^^
+
+ begin
+ rescue => foo[bar]
+ ^^^^^^^^
+ end
+
+ for foo[bar] in baz do end
+ ^^^^^^^^
+ - name: InstanceVariableAndWriteNode
+ fields:
+ - name: name
+ type: constant
+ - name: name_loc
+ type: location
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ comment: |
+ Represents the use of the `&&=` operator for assignment to an instance variable.
+
+ @target &&= value
+ ^^^^^^^^^^^^^^^^^
+ - name: InstanceVariableOperatorWriteNode
+ fields:
+ - name: name
+ type: constant
+ - name: name_loc
+ type: location
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ - name: operator
+ type: constant
+ comment: |
+ Represents assigning to an instance variable using an operator that isn't `=`.
+
+ @target += value
+ ^^^^^^^^^^^^^^^^
+ - name: InstanceVariableOrWriteNode
+ fields:
+ - name: name
+ type: constant
+ - name: name_loc
+ type: location
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ comment: |
+ Represents the use of the `||=` operator for assignment to an instance variable.
+
+ @target ||= value
+ ^^^^^^^^^^^^^^^^^
+ - name: InstanceVariableReadNode
+ fields:
+ - name: name
+ type: constant
+ comment: |
+ The name of the instance variable, which is a `@` followed by an [identifier](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#identifiers).
+
+ @x # name `:@x`
+
+ @_test # name `:@_test`
+ comment: |
+ Represents referencing an instance variable.
+
+ @foo
+ ^^^^
+ - name: InstanceVariableTargetNode
+ fields:
+ - name: name
+ type: constant
+ comment: |
+ Represents writing to an instance variable in a context that doesn't have an explicit value.
+
+ @foo, @bar = baz
+ ^^^^ ^^^^
+ - name: InstanceVariableWriteNode
+ fields:
+ - name: name
+ type: constant
+ comment: |
+ The name of the instance variable, which is a `@` followed by an [identifier](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#identifiers).
+
+ @x = :y # name `:@x`
+
+ @_foo = "bar" # name `@_foo`
+ - name: name_loc
+ type: location
+ comment: |
+ The location of the variable name.
+
+ @_x = 1
+ ^^^
+ - name: value
+ type: node
+ comment: |
+ The value to write to the instance variable. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
+
+ @foo = :bar
+ ^^^^
+
+ @_x = 1234
+ ^^^^
+ - name: operator_loc
+ type: location
+ comment: |
+ The location of the `=` operator.
+
+ @x = y
+ ^
+ comment: |
+ Represents writing to an instance variable.
+
+ @foo = 1
+ ^^^^^^^^
+ - name: IntegerNode
+ fields:
+ - name: flags
+ type: flags
+ kind: IntegerBaseFlags
+ - name: value
+ type: integer
+ comment: The value of the integer literal as a number.
+ comment: |
+ Represents an integer number literal.
+
+ 1
+ ^
+ - name: InterpolatedMatchLastLineNode
+ fields:
+ - name: flags
+ type: flags
+ kind: RegularExpressionFlags
+ - name: opening_loc
+ type: location
+ - name: parts
+ type: node[]
+ kind:
+ - StringNode
+ - EmbeddedStatementsNode
+ - EmbeddedVariableNode
+ - name: closing_loc
+ type: location
+ newline: parts
+ comment: |
+ Represents a regular expression literal that contains interpolation that is being used in the predicate of a conditional to implicitly match against the last line read by an IO object.
+
+ if /foo #{bar} baz/ then end
+ ^^^^^^^^^^^^^^^^
+ - name: InterpolatedRegularExpressionNode
+ fields:
+ - name: flags
+ type: flags
+ kind: RegularExpressionFlags
+ - name: opening_loc
+ type: location
+ - name: parts
+ type: node[]
+ kind:
+ - StringNode
+ - EmbeddedStatementsNode
+ - EmbeddedVariableNode
+ - name: closing_loc
+ type: location
+ newline: parts
+ comment: |
+ Represents a regular expression literal that contains interpolation.
+
+ /foo #{bar} baz/
+ ^^^^^^^^^^^^^^^^
+ - name: InterpolatedStringNode
+ fields:
+ - name: flags
+ type: flags
+ kind: InterpolatedStringNodeFlags
+ - name: opening_loc
+ type: location?
+ - name: parts
+ type: node[]
+ kind:
+ - StringNode
+ - EmbeddedStatementsNode
+ - EmbeddedVariableNode
+ - InterpolatedStringNode # `"a" "#{b}"`
+ - name: closing_loc
+ type: location?
+ newline: parts
+ comment: |
+ Represents a string literal that contains interpolation.
+
+ "foo #{bar} baz"
+ ^^^^^^^^^^^^^^^^
+ - name: InterpolatedSymbolNode
+ fields:
+ - name: opening_loc
+ type: location?
+ - name: parts
+ type: node[]
+ kind:
+ - StringNode
+ - EmbeddedStatementsNode
+ - EmbeddedVariableNode
+ - name: closing_loc
+ type: location?
+ newline: parts
+ comment: |
+ Represents a symbol literal that contains interpolation.
+
+ :"foo #{bar} baz"
+ ^^^^^^^^^^^^^^^^^
+ - name: InterpolatedXStringNode
+ fields:
+ - name: opening_loc
+ type: location
+ - name: parts
+ type: node[]
+ kind:
+ - StringNode
+ - EmbeddedStatementsNode
+ - EmbeddedVariableNode
+ - name: closing_loc
+ type: location
+ newline: parts
+ comment: |
+ Represents an xstring literal that contains interpolation.
+
+ `foo #{bar} baz`
+ ^^^^^^^^^^^^^^^^
+ - name: ItParametersNode
+ comment: |
+ Represents an implicit set of parameters through the use of the `it` keyword within a block or lambda.
+
+ -> { it + it }
+ ^^^^^^^^^^^^^^
+ - name: KeywordHashNode
+ fields:
+ - name: flags
+ type: flags
+ kind: KeywordHashNodeFlags
+ - name: elements
+ type: node[]
+ kind:
+ - AssocNode
+ - AssocSplatNode
+ comment: |
+ Represents a hash literal without opening and closing braces.
+
+ foo(a: b)
+ ^^^^
+ - name: KeywordRestParameterNode
+ fields:
+ - name: flags
+ type: flags
+ kind: ParameterFlags
+ - name: name
+ type: constant?
+ - name: name_loc
+ type: location?
+ - name: operator_loc
+ type: location
+ comment: |
+ Represents a keyword rest parameter to a method, block, or lambda definition.
+
+ def a(**b)
+ ^^^
+ end
+ - name: LambdaNode
+ fields:
+ - name: locals
+ type: constant[]
+ - name: operator_loc
+ type: location
+ - name: opening_loc
+ type: location
+ - name: closing_loc
+ type: location
+ - name: parameters
+ type: node?
+ - name: body
+ type: node?
+ comment: |
+ Represents using a lambda literal (not the lambda method call).
+
+ ->(value) { value * 2 }
+ ^^^^^^^^^^^^^^^^^^^^^^^
+ - name: LocalVariableAndWriteNode
+ fields:
+ - name: name_loc
+ type: location
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ - name: name
+ type: constant
+ - name: depth
+ type: uint32
+ comment: |
+ Represents the use of the `&&=` operator for assignment to a local variable.
+
+ target &&= value
+ ^^^^^^^^^^^^^^^^
+ - name: LocalVariableOperatorWriteNode
+ fields:
+ - name: name_loc
+ type: location
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ - name: name
+ type: constant
+ - name: operator
+ type: constant
+ - name: depth
+ type: uint32
+ comment: |
+ Represents assigning to a local variable using an operator that isn't `=`.
+
+ target += value
+ ^^^^^^^^^^^^^^^
+ - name: LocalVariableOrWriteNode
+ fields:
+ - name: name_loc
+ type: location
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ - name: name
+ type: constant
+ - name: depth
+ type: uint32
+ comment: |
+ Represents the use of the `||=` operator for assignment to a local variable.
+
+ target ||= value
+ ^^^^^^^^^^^^^^^^
+ - name: LocalVariableReadNode
+ fields:
+ - name: name
+ type: constant
+ comment: |
+ The name of the local variable, which is an [identifier](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#identifiers).
+
+ x # name `:x`
+
+ _Test # name `:_Test`
+
+ Note that this can also be an underscore followed by a number for the default block parameters.
+
+ _1 # name `:_1`
+
+ Finally, for the default `it` block parameter, the name is `0it`. This is to distinguish it from an `it` local variable that is explicitly declared.
+
+ it # name `:0it`
+
+ - name: depth
+ type: uint32
+ comment: |
+ The number of visible scopes that should be searched to find the origin of this local variable.
+
+ foo = 1; foo # depth 0
+
+ bar = 2; tap { bar } # depth 1
+
+ The specific rules for calculating the depth may differ from individual Ruby implementations, as they are not specified by the language. For more information, see [the Prism documentation](https://github.com/ruby/prism/blob/main/docs/local_variable_depth.md).
+ comment: |
+ Represents reading a local variable. Note that this requires that a local variable of the same name has already been written to in the same scope, otherwise it is parsed as a method call.
+
+ foo
+ ^^^
+ - name: LocalVariableTargetNode
+ fields:
+ - name: name
+ type: constant
+ - name: depth
+ type: uint32
+ comment: |
+ Represents writing to a local variable in a context that doesn't have an explicit value.
+
+ foo, bar = baz
+ ^^^ ^^^
+ - name: LocalVariableWriteNode
+ fields:
+ - name: name
+ type: constant
+ comment: |
+ The name of the local variable, which is an [identifier](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#identifiers).
+
+ foo = :bar # name `:foo`
+
+ abc = 123 # name `:abc`
+ - name: depth
+ type: uint32
+ comment: |
+ The number of semantic scopes we have to traverse to find the declaration of this variable.
+
+ foo = 1 # depth 0
+
+ tap { foo = 1 } # depth 1
+
+ The specific rules for calculating the depth may differ from individual Ruby implementations, as they are not specified by the language. For more information, see [the Prism documentation](https://github.com/ruby/prism/blob/main/docs/local_variable_depth.md).
+ - name: name_loc
+ type: location
+ comment: |
+ The location of the variable name.
+
+ foo = :bar
+ ^^^
+ - name: value
+ type: node
+ comment: |
+ The value to write to the local variable. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
+
+ foo = :bar
+ ^^^^
+
+ abc = 1234
+ ^^^^
+
+ Note that since the name of a local variable is known before the value is parsed, it is valid for a local variable to appear within the value of its own write.
+
+ foo = foo
+ - name: operator_loc
+ type: location
+ comment: |
+ The location of the `=` operator.
+
+ x = :y
+ ^
+ comment: |
+ Represents writing to a local variable.
+
+ foo = 1
+ ^^^^^^^
+ - name: MatchLastLineNode
+ fields:
+ - name: flags
+ type: flags
+ kind: RegularExpressionFlags
+ - name: opening_loc
+ type: location
+ - name: content_loc
+ type: location
+ - name: closing_loc
+ type: location
+ - name: unescaped
+ type: string
+ comment: |
+ Represents a regular expression literal used in the predicate of a conditional to implicitly match against the last line read by an IO object.
+
+ if /foo/i then end
+ ^^^^^^
+ - name: MatchPredicateNode
+ fields:
+ - name: value
+ type: node
+ - name: pattern
+ type: node
+ - name: operator_loc
+ type: location
+ comment: |
+ Represents the use of the modifier `in` operator.
+
+ foo in bar
+ ^^^^^^^^^^
+ - name: MatchRequiredNode
+ fields:
+ - name: value
+ type: node
+ - name: pattern
+ type: node
+ - name: operator_loc
+ type: location
+ comment: |
+ Represents the use of the `=>` operator.
+
+ foo => bar
+ ^^^^^^^^^^
+ - name: MatchWriteNode
+ fields:
+ - name: call
+ type: node
+ kind: CallNode
+ - name: targets
+ type: node[]
+ kind: LocalVariableTargetNode
+ comment: |
+ Represents writing local variables using a regular expression match with named capture groups.
+
+ /(?<foo>bar)/ =~ baz
+ ^^^^^^^^^^^^^^^^^^^^
+ - name: MissingNode
+ comment: |
+ Represents a node that is missing from the source and results in a syntax error.
+ - name: ModuleNode
+ fields:
+ - name: locals
+ type: constant[]
+ - name: module_keyword_loc
+ type: location
+ - name: constant_path
+ type: node
+ - name: body
+ type: node?
+ - name: end_keyword_loc
+ type: location
+ - name: name
+ type: constant
+ comment: |
+ Represents a module declaration involving the `module` keyword.
+
+ module Foo end
+ ^^^^^^^^^^^^^^
+ - name: MultiTargetNode
+ fields:
+ - name: lefts
+ type: node[]
+ kind:
+ - LocalVariableTargetNode
+ - InstanceVariableTargetNode
+ - ClassVariableTargetNode
+ - GlobalVariableTargetNode
+ - ConstantTargetNode
+ - ConstantPathTargetNode
+ - CallTargetNode
+ - IndexTargetNode
+ - MultiTargetNode
+ - RequiredParameterNode
+ - BackReferenceReadNode # On parsing error of `$',`
+ - NumberedReferenceReadNode # On parsing error of `$1,`
+ - name: rest
+ type: node?
+ - name: rights
+ type: node[]
+ kind:
+ - LocalVariableTargetNode
+ - InstanceVariableTargetNode
+ - ClassVariableTargetNode
+ - GlobalVariableTargetNode
+ - ConstantTargetNode
+ - ConstantPathTargetNode
+ - CallTargetNode
+ - IndexTargetNode
+ - MultiTargetNode
+ - RequiredParameterNode
+ - BackReferenceReadNode # On parsing error of `*,$'`
+ - name: lparen_loc
+ type: location?
+ - name: rparen_loc
+ type: location?
+ comment: |
+ Represents a multi-target expression.
+
+ a, (b, c) = 1, 2, 3
+ ^^^^^^
+ - name: MultiWriteNode
+ fields:
+ - name: lefts
+ type: node[]
+ kind:
+ - LocalVariableTargetNode
+ - InstanceVariableTargetNode
+ - ClassVariableTargetNode
+ - GlobalVariableTargetNode
+ - ConstantTargetNode
+ - ConstantPathTargetNode
+ - CallTargetNode
+ - IndexTargetNode
+ - MultiTargetNode
+ - name: rest
+ type: node?
+ - name: rights
+ type: node[]
+ kind:
+ - LocalVariableTargetNode
+ - InstanceVariableTargetNode
+ - ClassVariableTargetNode
+ - GlobalVariableTargetNode
+ - ConstantTargetNode
+ - ConstantPathTargetNode
+ - CallTargetNode
+ - IndexTargetNode
+ - MultiTargetNode
+ - name: lparen_loc
+ type: location?
+ - name: rparen_loc
+ type: location?
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ comment: |
+ Represents a write to a multi-target expression.
+
+ a, b, c = 1, 2, 3
+ ^^^^^^^^^^^^^^^^^
+ - name: NextNode
+ fields:
+ - name: arguments
+ type: node?
+ kind: ArgumentsNode
+ - name: keyword_loc
+ type: location
+ comment: |
+ Represents the use of the `next` keyword.
+
+ next 1
+ ^^^^^^
+ - name: NilNode
+ comment: |
+ Represents the use of the `nil` keyword.
+
+ nil
+ ^^^
+ - name: NoKeywordsParameterNode
+ fields:
+ - name: operator_loc
+ type: location
+ - name: keyword_loc
+ type: location
+ comment: |
+ Represents the use of `**nil` inside method arguments.
+
+ def a(**nil)
+ ^^^^^
+ end
+ - name: NumberedParametersNode
+ fields:
+ - name: maximum
+ type: uint8
+ comment: |
+ Represents an implicit set of parameters through the use of numbered parameters within a block or lambda.
+
+ -> { _1 + _2 }
+ ^^^^^^^^^^^^^^
+ - name: NumberedReferenceReadNode
+ fields:
+ - name: number
+ type: uint32
+ comment: |
+ The (1-indexed, from the left) number of the capture group. Numbered references that are too large result in this value being `0`.
+
+ $1 # number `1`
+
+ $5432 # number `5432`
+
+ $4294967296 # number `0`
+ comment: |
+ Represents reading a numbered reference to a capture in the previous match.
+
+ $1
+ ^^
+ - name: OptionalKeywordParameterNode
+ fields:
+ - name: flags
+ type: flags
+ kind: ParameterFlags
+ - name: name
+ type: constant
+ - name: name_loc
+ type: location
+ - name: value
+ type: node
+ comment: |
+ Represents an optional keyword parameter to a method, block, or lambda definition.
+
+ def a(b: 1)
+ ^^^^
+ end
+ - name: OptionalParameterNode
+ fields:
+ - name: flags
+ type: flags
+ kind: ParameterFlags
+ - name: name
+ type: constant
+ - name: name_loc
+ type: location
+ - name: operator_loc
+ type: location
+ - name: value
+ type: node
+ comment: |
+ Represents an optional parameter to a method, block, or lambda definition.
+
+ def a(b = 1)
+ ^^^^^
+ end
+ - name: OrNode
+ fields:
+ - name: left
+ type: node
+ comment: |
+ Represents the left side of the expression. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
+
+ left or right
+ ^^^^
+
+ 1 || 2
+ ^
+ - name: right
+ type: node
+ comment: |
+ Represents the right side of the expression. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
+
+ left || right
+ ^^^^^
+
+ 1 or 2
+ ^
+ - name: operator_loc
+ type: location
+ comment: |
+ The location of the `or` keyword or the `||` operator.
+
+ left or right
+ ^^
+ comment: |
+ Represents the use of the `||` operator or the `or` keyword.
+
+ left or right
+ ^^^^^^^^^^^^^
+ - name: ParametersNode
+ fields:
+ - name: requireds
+ type: node[]
+ kind:
+ - RequiredParameterNode
+ - MultiTargetNode
+ - name: optionals
+ type: node[]
+ kind: OptionalParameterNode
+ - name: rest
+ type: node?
+ kind:
+ - RestParameterNode
+ - ImplicitRestNode # Only in block parameters
+ - name: posts
+ type: node[]
+ kind:
+ - RequiredParameterNode
+ - MultiTargetNode
+ # On parsing error of `f(**kwargs, ...)` or `f(**nil, ...)`, the keyword_rest value is moved here:
+ - KeywordRestParameterNode
+ - NoKeywordsParameterNode
+ - name: keywords
+ type: node[]
+ kind:
+ - RequiredKeywordParameterNode
+ - OptionalKeywordParameterNode
+ - name: keyword_rest
+ type: node?
+ kind:
+ - KeywordRestParameterNode
+ - ForwardingParameterNode
+ - NoKeywordsParameterNode
+ - name: block
+ type: node?
+ kind: BlockParameterNode
+ comment: |
+ Represents the list of parameters on a method, block, or lambda definition.
+
+ def a(b, c, d)
+ ^^^^^^^
+ end
+ - name: ParenthesesNode
+ fields:
+ - name: body
+ type: node?
+ - name: opening_loc
+ type: location
+ - name: closing_loc
+ type: location
+ newline: false
+ comment: |
+ Represents a parenthesized expression
+
+ (10 + 34)
+ ^^^^^^^^^
+ - name: PinnedExpressionNode
+ fields:
+ - name: expression
+ type: node
+ - name: operator_loc
+ type: location
+ - name: lparen_loc
+ type: location
+ - name: rparen_loc
+ type: location
+ comment: |
+ Represents the use of the `^` operator for pinning an expression in a pattern matching expression.
+
+ foo in ^(bar)
+ ^^^^^^
+ - name: PinnedVariableNode
+ fields:
+ - name: variable
+ type: node
+ - name: operator_loc
+ type: location
+ comment: |
+ Represents the use of the `^` operator for pinning a variable in a pattern matching expression.
+
+ foo in ^bar
+ ^^^^
+ - name: PostExecutionNode
+ fields:
+ - name: statements
+ type: node?
+ kind: StatementsNode
+ - name: keyword_loc
+ type: location
+ - name: opening_loc
+ type: location
+ - name: closing_loc
+ type: location
+ comment: |
+ Represents the use of the `END` keyword.
+
+ END { foo }
+ ^^^^^^^^^^^
+ - name: PreExecutionNode
+ fields:
+ - name: statements
+ type: node?
+ kind: StatementsNode
+ - name: keyword_loc
+ type: location
+ - name: opening_loc
+ type: location
+ - name: closing_loc
+ type: location
+ comment: |
+ Represents the use of the `BEGIN` keyword.
+
+ BEGIN { foo }
+ ^^^^^^^^^^^^^
+ - name: ProgramNode
+ fields:
+ - name: locals
+ type: constant[]
+ - name: statements
+ type: node
+ kind: StatementsNode
+ comment: The top level node of any parse tree.
+ - name: RangeNode
+ fields:
+ - name: flags
+ type: flags
+ kind: RangeFlags
+ - name: left
+ type: node?
+ comment: |
+ The left-hand side of the range, if present. It can be either `nil` or any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
+
+ 1...
+ ^
+
+ hello...goodbye
+ ^^^^^
+ - name: right
+ type: node?
+ comment: |
+ The right-hand side of the range, if present. It can be either `nil` or any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
+
+ ..5
+ ^
+
+ 1...foo
+ ^^^
+ If neither right-hand or left-hand side was included, this will be a MissingNode.
+ - name: operator_loc
+ type: location
+ comment: |
+ The location of the `..` or `...` operator.
+ comment: |
+ Represents the use of the `..` or `...` operators.
+
+ 1..2
+ ^^^^
+
+ c if a =~ /left/ ... b =~ /right/
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ - name: RationalNode
+ fields:
+ - name: numeric
+ type: node
+ comment: |
+ Represents a rational number literal.
+
+ 1.0r
+ ^^^^
+ - name: RedoNode
+ comment: |
+ Represents the use of the `redo` keyword.
+
+ redo
+ ^^^^
+ - name: RegularExpressionNode
+ fields:
+ - name: flags
+ type: flags
+ kind: RegularExpressionFlags
+ - name: opening_loc
+ type: location
+ - name: content_loc
+ type: location
+ - name: closing_loc
+ type: location
+ - name: unescaped
+ type: string
+ comment: |
+ Represents a regular expression literal with no interpolation.
+
+ /foo/i
+ ^^^^^^
+ - name: RequiredKeywordParameterNode
+ fields:
+ - name: flags
+ type: flags
+ kind: ParameterFlags
+ - name: name
+ type: constant
+ - name: name_loc
+ type: location
+ comment: |
+ Represents a required keyword parameter to a method, block, or lambda definition.
+
+ def a(b: )
+ ^^
+ end
+ - name: RequiredParameterNode
+ fields:
+ - name: flags
+ type: flags
+ kind: ParameterFlags
+ - name: name
+ type: constant
+ comment: |
+ Represents a required parameter to a method, block, or lambda definition.
+
+ def a(b)
+ ^
+ end
+ - name: RescueModifierNode
+ fields:
+ - name: expression
+ type: node
+ - name: keyword_loc
+ type: location
+ - name: rescue_expression
+ type: node
+ newline: expression
+ comment: |
+ Represents an expression modified with a rescue.
+
+ foo rescue nil
+ ^^^^^^^^^^^^^^
+ - name: RescueNode
+ fields:
+ - name: keyword_loc
+ type: location
+ - name: exceptions
+ type: node[]
+ - name: operator_loc
+ type: location?
+ - name: reference
+ type: node?
+ - name: statements
+ type: node?
+ kind: StatementsNode
+ - name: consequent
+ type: node?
+ kind: RescueNode
+ comment: |
+ Represents a rescue statement.
+
+ begin
+ rescue Foo, *splat, Bar => ex
+ foo
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ end
+
+ `Foo, *splat, Bar` are in the `exceptions` field. `ex` is in the `exception` field.
+ - name: RestParameterNode
+ fields:
+ - name: flags
+ type: flags
+ kind: ParameterFlags
+ - name: name
+ type: constant?
+ - name: name_loc
+ type: location?
+ - name: operator_loc
+ type: location
+ comment: |
+ Represents a rest parameter to a method, block, or lambda definition.
+
+ def a(*b)
+ ^^
+ end
+ - name: RetryNode
+ comment: |
+ Represents the use of the `retry` keyword.
+
+ retry
+ ^^^^^
+ - name: ReturnNode
+ fields:
+ - name: flags
+ type: flags
+ kind: ReturnNodeFlags
+ - name: keyword_loc
+ type: location
+ - name: arguments
+ type: node?
+ kind: ArgumentsNode
+ comment: |
+ Represents the use of the `return` keyword.
+
+ return 1
+ ^^^^^^^^
+ - name: SelfNode
+ comment: |
+ Represents the `self` keyword.
+
+ self
+ ^^^^
+ - name: ShareableConstantNode
+ fields:
+ - name: flags
+ type: flags
+ kind: ShareableConstantNodeFlags
+ - name: write
+ type: node
+ kind:
+ - ConstantWriteNode
+ - ConstantAndWriteNode
+ - ConstantOrWriteNode
+ - ConstantOperatorWriteNode
+ - ConstantPathWriteNode
+ - ConstantPathAndWriteNode
+ - ConstantPathOrWriteNode
+ - ConstantPathOperatorWriteNode
+ comment: The constant write that should be modified with the shareability state.
+ comment: |
+ This node wraps a constant write to indicate that when the value is written, it should have its shareability state modified.
+
+ # shareable_constant_value: literal
+ C = { a: 1 }
+ ^^^^^^^^^^^^
+ - name: SingletonClassNode
+ fields:
+ - name: locals
+ type: constant[]
+ - name: class_keyword_loc
+ type: location
+ - name: operator_loc
+ type: location
+ - name: expression
+ type: node
+ - name: body
+ type: node?
+ - name: end_keyword_loc
+ type: location
+ comment: |
+ Represents a singleton class declaration involving the `class` keyword.
+
+ class << self end
+ ^^^^^^^^^^^^^^^^^
+ - name: SourceEncodingNode
+ comment: |
+ Represents the use of the `__ENCODING__` keyword.
+
+ __ENCODING__
+ ^^^^^^^^^^^^
+ - name: SourceFileNode
+ fields:
+ - name: flags
+ type: flags
+ kind: StringFlags
+ - name: filepath
+ type: string
+ comment: Represents the file path being parsed. This corresponds directly to the `filepath` option given to the various `Prism::parse*` APIs.
+ comment: |
+ Represents the use of the `__FILE__` keyword.
+
+ __FILE__
+ ^^^^^^^^
+ - name: SourceLineNode
+ comment: |
+ Represents the use of the `__LINE__` keyword.
+
+ __LINE__
+ ^^^^^^^^
+ - name: SplatNode
+ fields:
+ - name: operator_loc
+ type: location
+ - name: expression
+ type: node?
+ comment: |
+ Represents the use of the splat operator.
+
+ [*a]
+ ^^
+ - name: StatementsNode
+ fields:
+ - name: body
+ type: node[]
+ comment: |
+ Represents a set of statements contained within some scope.
+
+ foo; bar; baz
+ ^^^^^^^^^^^^^
+ - name: StringNode
+ fields:
+ - name: flags
+ type: flags
+ kind: StringFlags
+ - name: opening_loc
+ type: location?
+ - name: content_loc
+ type: location
+ - name: closing_loc
+ type: location?
+ - name: unescaped
+ type: string
+ comment: |
+ Represents a string literal, a string contained within a `%w` list, or plain string content within an interpolated string.
+
+ "foo"
+ ^^^^^
+
+ %w[foo]
+ ^^^
+
+ "foo #{bar} baz"
+ ^^^^ ^^^^
+ - name: SuperNode
+ fields:
+ - name: keyword_loc
+ type: location
+ - name: lparen_loc
+ type: location?
+ - name: arguments
+ type: node?
+ kind: ArgumentsNode
+ - name: rparen_loc
+ type: location?
+ - name: block
+ type: node?
+ comment: |
+ Represents the use of the `super` keyword with parentheses or arguments.
+
+ super()
+ ^^^^^^^
+
+ super foo, bar
+ ^^^^^^^^^^^^^^
+ - name: SymbolNode
+ fields:
+ - name: flags
+ type: flags
+ kind: SymbolFlags
+ - name: opening_loc
+ type: location?
+ - name: value_loc
+ type: location?
+ - name: closing_loc
+ type: location?
+ - name: unescaped
+ type: string
+ comment: |
+ Represents a symbol literal or a symbol contained within a `%i` list.
+
+ :foo
+ ^^^^
+
+ %i[foo]
+ ^^^
+ - name: TrueNode
+ comment: |
+ Represents the use of the literal `true` keyword.
+
+ true
+ ^^^^
+ - name: UndefNode
+ fields:
+ - name: names
+ type: node[]
+ kind:
+ - SymbolNode
+ - InterpolatedSymbolNode
+ - name: keyword_loc
+ type: location
+ comment: |
+ Represents the use of the `undef` keyword.
+
+ undef :foo, :bar, :baz
+ ^^^^^^^^^^^^^^^^^^^^^^
+ - name: UnlessNode
+ fields:
+ - name: keyword_loc
+ type: location
+ comment: |
+ The location of the `unless` keyword.
+
+ unless cond then bar end
+ ^^^^^^
+
+ bar unless cond
+ ^^^^^^
+ - name: predicate
+ type: node
+ comment: |
+ The condition to be evaluated for the unless expression. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
+
+ unless cond then bar end
+ ^^^^
+
+ bar unless cond
+ ^^^^
+ - name: then_keyword_loc
+ type: location?
+ comment:
+ The location of the `then` keyword, if present.
+
+ unless cond then bar end
+ ^^^^
+ - name: statements
+ type: node?
+ kind: StatementsNode
+ comment: |
+ The body of statements that will executed if the unless condition is
+ falsey. Will be `nil` if no body is provided.
+
+ unless cond then bar end
+ ^^^
+ - name: consequent
+ type: node?
+ kind: ElseNode
+ comment: |
+ The else clause of the unless expression, if present.
+
+ unless cond then bar else baz end
+ ^^^^^^^^
+ - name: end_keyword_loc
+ type: location?
+ comment: |
+ The location of the `end` keyword, if present.
+
+ unless cond then bar end
+ ^^^
+ newline: predicate
+ comment: |
+ Represents the use of the `unless` keyword, either in the block form or the modifier form.
+
+ bar unless foo
+ ^^^^^^^^^^^^^^
+
+ unless foo then bar end
+ ^^^^^^^^^^^^^^^^^^^^^^^
+ - name: UntilNode
+ fields:
+ - name: flags
+ type: flags
+ kind: LoopFlags
+ - name: keyword_loc
+ type: location
+ - name: closing_loc
+ type: location?
+ - name: predicate
+ type: node
+ - name: statements
+ type: node?
+ kind: StatementsNode
+ newline: predicate
+ comment: |
+ Represents the use of the `until` keyword, either in the block form or the modifier form.
+
+ bar until foo
+ ^^^^^^^^^^^^^
+
+ until foo do bar end
+ ^^^^^^^^^^^^^^^^^^^^
+ - name: WhenNode
+ fields:
+ - name: keyword_loc
+ type: location
+ - name: conditions
+ type: node[]
+ - name: then_keyword_loc
+ type: location?
+ - name: statements
+ type: node?
+ kind: StatementsNode
+ comment: |
+ Represents the use of the `when` keyword within a case statement.
+
+ case true
+ when true
+ ^^^^^^^^^
+ end
+ - name: WhileNode
+ fields:
+ - name: flags
+ type: flags
+ kind: LoopFlags
+ - name: keyword_loc
+ type: location
+ - name: closing_loc
+ type: location?
+ - name: predicate
+ type: node
+ - name: statements
+ type: node?
+ kind: StatementsNode
+ newline: predicate
+ comment: |
+ Represents the use of the `while` keyword, either in the block form or the modifier form.
+
+ bar while foo
+ ^^^^^^^^^^^^^
+
+ while foo do bar end
+ ^^^^^^^^^^^^^^^^^^^^
+ - name: XStringNode
+ fields:
+ - name: flags
+ type: flags
+ kind: EncodingFlags
+ - name: opening_loc
+ type: location
+ - name: content_loc
+ type: location
+ - name: closing_loc
+ type: location
+ - name: unescaped
+ type: string
+ comment: |
+ Represents an xstring literal with no interpolation.
+
+ `foo`
+ ^^^^^
+ - name: YieldNode
+ fields:
+ - name: keyword_loc
+ type: location
+ - name: lparen_loc
+ type: location?
+ - name: arguments
+ type: node?
+ kind: ArgumentsNode
+ - name: rparen_loc
+ type: location?
+ comment: |
+ Represents the use of the `yield` keyword.
+
+ yield 1
+ ^^^^^^^
diff --git a/prism/defines.h b/prism/defines.h
new file mode 100644
index 0000000000..849ca6d051
--- /dev/null
+++ b/prism/defines.h
@@ -0,0 +1,206 @@
+/**
+ * @file defines.h
+ *
+ * Macro definitions used throughout the prism library.
+ *
+ * This file should be included first by any *.h or *.c in prism for consistency
+ * and to ensure that the macros are defined before they are used.
+ */
+#ifndef PRISM_DEFINES_H
+#define PRISM_DEFINES_H
+
+#include <ctype.h>
+#include <limits.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * We want to be able to use the PRI* macros for printing out integers, but on
+ * some platforms they aren't included unless this is already defined.
+ */
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
+/**
+ * By default, we compile with -fvisibility=hidden. When this is enabled, we
+ * need to mark certain functions as being publically-visible. This macro does
+ * that in a compiler-agnostic way.
+ */
+#ifndef PRISM_EXPORTED_FUNCTION
+# ifdef PRISM_EXPORT_SYMBOLS
+# ifdef _WIN32
+# define PRISM_EXPORTED_FUNCTION __declspec(dllexport) extern
+# else
+# define PRISM_EXPORTED_FUNCTION __attribute__((__visibility__("default"))) extern
+# endif
+# else
+# define PRISM_EXPORTED_FUNCTION
+# endif
+#endif
+
+/**
+ * Certain compilers support specifying that a function accepts variadic
+ * parameters that look like printf format strings to provide a better developer
+ * experience when someone is using the function. This macro does that in a
+ * compiler-agnostic way.
+ */
+#if defined(__GNUC__)
+# if defined(__MINGW_PRINTF_FORMAT)
+# define PRISM_ATTRIBUTE_FORMAT(string_index, argument_index) __attribute__((format(__MINGW_PRINTF_FORMAT, string_index, argument_index)))
+# else
+# define PRISM_ATTRIBUTE_FORMAT(string_index, argument_index) __attribute__((format(printf, string_index, argument_index)))
+# endif
+#elif defined(__clang__)
+# define PRISM_ATTRIBUTE_FORMAT(string_index, argument_index) __attribute__((__format__(__printf__, string_index, argument_index)))
+#else
+# define PRISM_ATTRIBUTE_FORMAT(string_index, argument_index)
+#endif
+
+/**
+ * GCC will warn if you specify a function or parameter that is unused at
+ * runtime. This macro allows you to mark a function or parameter as unused in a
+ * compiler-agnostic way.
+ */
+#if defined(__GNUC__)
+# define PRISM_ATTRIBUTE_UNUSED __attribute__((unused))
+#else
+# define PRISM_ATTRIBUTE_UNUSED
+#endif
+
+/**
+ * Old Visual Studio versions do not support the inline keyword, so we need to
+ * define it to be __inline.
+ */
+#if defined(_MSC_VER) && !defined(inline)
+# define inline __inline
+#endif
+
+/**
+ * Old Visual Studio versions before 2015 do not implement sprintf, but instead
+ * implement _snprintf. We standard that here.
+ */
+#if !defined(snprintf) && defined(_MSC_VER) && (_MSC_VER < 1900)
+# define snprintf _snprintf
+#endif
+
+/**
+ * A simple utility macro to concatenate two tokens together, necessary when one
+ * of the tokens is itself a macro.
+ */
+#define PM_CONCATENATE(left, right) left ## right
+
+/**
+ * We want to be able to use static assertions, but they weren't standardized
+ * until C11. As such, we polyfill it here by making a hacky typedef that will
+ * fail to compile due to a negative array size if the condition is false.
+ */
+#if defined(_Static_assert)
+# define PM_STATIC_ASSERT(line, condition, message) _Static_assert(condition, message)
+#else
+# define PM_STATIC_ASSERT(line, condition, message) typedef char PM_CONCATENATE(static_assert_, line)[(condition) ? 1 : -1]
+#endif
+
+/**
+ * In general, libc for embedded systems does not support memory-mapped files.
+ * If the target platform is POSIX or Windows, we can map a file in memory and
+ * read it in a more efficient manner.
+ */
+#ifdef _WIN32
+# define PRISM_HAS_MMAP
+#else
+# include <unistd.h>
+# ifdef _POSIX_MAPPED_FILES
+# define PRISM_HAS_MMAP
+# endif
+#endif
+
+/**
+ * isinf on Windows is defined as accepting a float, but on POSIX systems it
+ * accepts a float, a double, or a long double. We want to mirror this behavior
+ * on windows.
+ */
+#ifdef _WIN32
+# include <float.h>
+# undef isinf
+# define isinf(x) (sizeof(x) == sizeof(float) ? !_finitef(x) : !_finite(x))
+#endif
+
+/**
+ * If you build prism with a custom allocator, configure it with
+ * "-D PRISM_XALLOCATOR" to use your own allocator that defines xmalloc,
+ * xrealloc, xcalloc, and xfree.
+ *
+ * For example, your `prism_xallocator.h` file could look like this:
+ *
+ * ```
+ * #ifndef PRISM_XALLOCATOR_H
+ * #define PRISM_XALLOCATOR_H
+ * #define xmalloc my_malloc
+ * #define xrealloc my_realloc
+ * #define xcalloc my_calloc
+ * #define xfree my_free
+ * #endif
+ * ```
+ */
+#ifdef PRISM_XALLOCATOR
+ #include "prism_xallocator.h"
+#else
+ #ifndef xmalloc
+ /**
+ * The malloc function that should be used. This can be overridden with
+ * the PRISM_XALLOCATOR define.
+ */
+ #define xmalloc malloc
+ #endif
+
+ #ifndef xrealloc
+ /**
+ * The realloc function that should be used. This can be overridden with
+ * the PRISM_XALLOCATOR define.
+ */
+ #define xrealloc realloc
+ #endif
+
+ #ifndef xcalloc
+ /**
+ * The calloc function that should be used. This can be overridden with
+ * the PRISM_XALLOCATOR define.
+ */
+ #define xcalloc calloc
+ #endif
+
+ #ifndef xfree
+ /**
+ * The free function that should be used. This can be overridden with the
+ * PRISM_XALLOCATOR define.
+ */
+ #define xfree free
+ #endif
+#endif
+
+/**
+ * If PRISM_BUILD_MINIMAL is defined, then we're going to define every possible
+ * switch that will turn off certain features of prism.
+ */
+#ifdef PRISM_BUILD_MINIMAL
+ /** Exclude the serialization API. */
+ #define PRISM_EXCLUDE_SERIALIZATION
+
+ /** Exclude the JSON serialization API. */
+ #define PRISM_EXCLUDE_JSON
+
+ /** Exclude the Array#pack parser API. */
+ #define PRISM_EXCLUDE_PACK
+
+ /** Exclude the prettyprint API. */
+ #define PRISM_EXCLUDE_PRETTYPRINT
+
+ /** Exclude the full set of encodings, using the minimal only. */
+ #define PRISM_ENCODING_EXCLUDE_FULL
+#endif
+
+#endif
diff --git a/prism/encoding.c b/prism/encoding.c
new file mode 100644
index 0000000000..a4aeed104f
--- /dev/null
+++ b/prism/encoding.c
@@ -0,0 +1,5235 @@
+#include "prism/encoding.h"
+
+typedef uint32_t pm_unicode_codepoint_t;
+
+#define UNICODE_ALPHA_CODEPOINTS_LENGTH 1450
+static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEPOINTS_LENGTH] = {
+ 0x100, 0x2C1,
+ 0x2C6, 0x2D1,
+ 0x2E0, 0x2E4,
+ 0x2EC, 0x2EC,
+ 0x2EE, 0x2EE,
+ 0x345, 0x345,
+ 0x370, 0x374,
+ 0x376, 0x377,
+ 0x37A, 0x37D,
+ 0x37F, 0x37F,
+ 0x386, 0x386,
+ 0x388, 0x38A,
+ 0x38C, 0x38C,
+ 0x38E, 0x3A1,
+ 0x3A3, 0x3F5,
+ 0x3F7, 0x481,
+ 0x48A, 0x52F,
+ 0x531, 0x556,
+ 0x559, 0x559,
+ 0x560, 0x588,
+ 0x5B0, 0x5BD,
+ 0x5BF, 0x5BF,
+ 0x5C1, 0x5C2,
+ 0x5C4, 0x5C5,
+ 0x5C7, 0x5C7,
+ 0x5D0, 0x5EA,
+ 0x5EF, 0x5F2,
+ 0x610, 0x61A,
+ 0x620, 0x657,
+ 0x659, 0x65F,
+ 0x66E, 0x6D3,
+ 0x6D5, 0x6DC,
+ 0x6E1, 0x6E8,
+ 0x6ED, 0x6EF,
+ 0x6FA, 0x6FC,
+ 0x6FF, 0x6FF,
+ 0x710, 0x73F,
+ 0x74D, 0x7B1,
+ 0x7CA, 0x7EA,
+ 0x7F4, 0x7F5,
+ 0x7FA, 0x7FA,
+ 0x800, 0x817,
+ 0x81A, 0x82C,
+ 0x840, 0x858,
+ 0x860, 0x86A,
+ 0x870, 0x887,
+ 0x889, 0x88E,
+ 0x8A0, 0x8C9,
+ 0x8D4, 0x8DF,
+ 0x8E3, 0x8E9,
+ 0x8F0, 0x93B,
+ 0x93D, 0x94C,
+ 0x94E, 0x950,
+ 0x955, 0x963,
+ 0x971, 0x983,
+ 0x985, 0x98C,
+ 0x98F, 0x990,
+ 0x993, 0x9A8,
+ 0x9AA, 0x9B0,
+ 0x9B2, 0x9B2,
+ 0x9B6, 0x9B9,
+ 0x9BD, 0x9C4,
+ 0x9C7, 0x9C8,
+ 0x9CB, 0x9CC,
+ 0x9CE, 0x9CE,
+ 0x9D7, 0x9D7,
+ 0x9DC, 0x9DD,
+ 0x9DF, 0x9E3,
+ 0x9F0, 0x9F1,
+ 0x9FC, 0x9FC,
+ 0xA01, 0xA03,
+ 0xA05, 0xA0A,
+ 0xA0F, 0xA10,
+ 0xA13, 0xA28,
+ 0xA2A, 0xA30,
+ 0xA32, 0xA33,
+ 0xA35, 0xA36,
+ 0xA38, 0xA39,
+ 0xA3E, 0xA42,
+ 0xA47, 0xA48,
+ 0xA4B, 0xA4C,
+ 0xA51, 0xA51,
+ 0xA59, 0xA5C,
+ 0xA5E, 0xA5E,
+ 0xA70, 0xA75,
+ 0xA81, 0xA83,
+ 0xA85, 0xA8D,
+ 0xA8F, 0xA91,
+ 0xA93, 0xAA8,
+ 0xAAA, 0xAB0,
+ 0xAB2, 0xAB3,
+ 0xAB5, 0xAB9,
+ 0xABD, 0xAC5,
+ 0xAC7, 0xAC9,
+ 0xACB, 0xACC,
+ 0xAD0, 0xAD0,
+ 0xAE0, 0xAE3,
+ 0xAF9, 0xAFC,
+ 0xB01, 0xB03,
+ 0xB05, 0xB0C,
+ 0xB0F, 0xB10,
+ 0xB13, 0xB28,
+ 0xB2A, 0xB30,
+ 0xB32, 0xB33,
+ 0xB35, 0xB39,
+ 0xB3D, 0xB44,
+ 0xB47, 0xB48,
+ 0xB4B, 0xB4C,
+ 0xB56, 0xB57,
+ 0xB5C, 0xB5D,
+ 0xB5F, 0xB63,
+ 0xB71, 0xB71,
+ 0xB82, 0xB83,
+ 0xB85, 0xB8A,
+ 0xB8E, 0xB90,
+ 0xB92, 0xB95,
+ 0xB99, 0xB9A,
+ 0xB9C, 0xB9C,
+ 0xB9E, 0xB9F,
+ 0xBA3, 0xBA4,
+ 0xBA8, 0xBAA,
+ 0xBAE, 0xBB9,
+ 0xBBE, 0xBC2,
+ 0xBC6, 0xBC8,
+ 0xBCA, 0xBCC,
+ 0xBD0, 0xBD0,
+ 0xBD7, 0xBD7,
+ 0xC00, 0xC0C,
+ 0xC0E, 0xC10,
+ 0xC12, 0xC28,
+ 0xC2A, 0xC39,
+ 0xC3D, 0xC44,
+ 0xC46, 0xC48,
+ 0xC4A, 0xC4C,
+ 0xC55, 0xC56,
+ 0xC58, 0xC5A,
+ 0xC5D, 0xC5D,
+ 0xC60, 0xC63,
+ 0xC80, 0xC83,
+ 0xC85, 0xC8C,
+ 0xC8E, 0xC90,
+ 0xC92, 0xCA8,
+ 0xCAA, 0xCB3,
+ 0xCB5, 0xCB9,
+ 0xCBD, 0xCC4,
+ 0xCC6, 0xCC8,
+ 0xCCA, 0xCCC,
+ 0xCD5, 0xCD6,
+ 0xCDD, 0xCDE,
+ 0xCE0, 0xCE3,
+ 0xCF1, 0xCF3,
+ 0xD00, 0xD0C,
+ 0xD0E, 0xD10,
+ 0xD12, 0xD3A,
+ 0xD3D, 0xD44,
+ 0xD46, 0xD48,
+ 0xD4A, 0xD4C,
+ 0xD4E, 0xD4E,
+ 0xD54, 0xD57,
+ 0xD5F, 0xD63,
+ 0xD7A, 0xD7F,
+ 0xD81, 0xD83,
+ 0xD85, 0xD96,
+ 0xD9A, 0xDB1,
+ 0xDB3, 0xDBB,
+ 0xDBD, 0xDBD,
+ 0xDC0, 0xDC6,
+ 0xDCF, 0xDD4,
+ 0xDD6, 0xDD6,
+ 0xDD8, 0xDDF,
+ 0xDF2, 0xDF3,
+ 0xE01, 0xE3A,
+ 0xE40, 0xE46,
+ 0xE4D, 0xE4D,
+ 0xE81, 0xE82,
+ 0xE84, 0xE84,
+ 0xE86, 0xE8A,
+ 0xE8C, 0xEA3,
+ 0xEA5, 0xEA5,
+ 0xEA7, 0xEB9,
+ 0xEBB, 0xEBD,
+ 0xEC0, 0xEC4,
+ 0xEC6, 0xEC6,
+ 0xECD, 0xECD,
+ 0xEDC, 0xEDF,
+ 0xF00, 0xF00,
+ 0xF40, 0xF47,
+ 0xF49, 0xF6C,
+ 0xF71, 0xF83,
+ 0xF88, 0xF97,
+ 0xF99, 0xFBC,
+ 0x1000, 0x1036,
+ 0x1038, 0x1038,
+ 0x103B, 0x103F,
+ 0x1050, 0x108F,
+ 0x109A, 0x109D,
+ 0x10A0, 0x10C5,
+ 0x10C7, 0x10C7,
+ 0x10CD, 0x10CD,
+ 0x10D0, 0x10FA,
+ 0x10FC, 0x1248,
+ 0x124A, 0x124D,
+ 0x1250, 0x1256,
+ 0x1258, 0x1258,
+ 0x125A, 0x125D,
+ 0x1260, 0x1288,
+ 0x128A, 0x128D,
+ 0x1290, 0x12B0,
+ 0x12B2, 0x12B5,
+ 0x12B8, 0x12BE,
+ 0x12C0, 0x12C0,
+ 0x12C2, 0x12C5,
+ 0x12C8, 0x12D6,
+ 0x12D8, 0x1310,
+ 0x1312, 0x1315,
+ 0x1318, 0x135A,
+ 0x1380, 0x138F,
+ 0x13A0, 0x13F5,
+ 0x13F8, 0x13FD,
+ 0x1401, 0x166C,
+ 0x166F, 0x167F,
+ 0x1681, 0x169A,
+ 0x16A0, 0x16EA,
+ 0x16EE, 0x16F8,
+ 0x1700, 0x1713,
+ 0x171F, 0x1733,
+ 0x1740, 0x1753,
+ 0x1760, 0x176C,
+ 0x176E, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17B3,
+ 0x17B6, 0x17C8,
+ 0x17D7, 0x17D7,
+ 0x17DC, 0x17DC,
+ 0x1820, 0x1878,
+ 0x1880, 0x18AA,
+ 0x18B0, 0x18F5,
+ 0x1900, 0x191E,
+ 0x1920, 0x192B,
+ 0x1930, 0x1938,
+ 0x1950, 0x196D,
+ 0x1970, 0x1974,
+ 0x1980, 0x19AB,
+ 0x19B0, 0x19C9,
+ 0x1A00, 0x1A1B,
+ 0x1A20, 0x1A5E,
+ 0x1A61, 0x1A74,
+ 0x1AA7, 0x1AA7,
+ 0x1ABF, 0x1AC0,
+ 0x1ACC, 0x1ACE,
+ 0x1B00, 0x1B33,
+ 0x1B35, 0x1B43,
+ 0x1B45, 0x1B4C,
+ 0x1B80, 0x1BA9,
+ 0x1BAC, 0x1BAF,
+ 0x1BBA, 0x1BE5,
+ 0x1BE7, 0x1BF1,
+ 0x1C00, 0x1C36,
+ 0x1C4D, 0x1C4F,
+ 0x1C5A, 0x1C7D,
+ 0x1C80, 0x1C88,
+ 0x1C90, 0x1CBA,
+ 0x1CBD, 0x1CBF,
+ 0x1CE9, 0x1CEC,
+ 0x1CEE, 0x1CF3,
+ 0x1CF5, 0x1CF6,
+ 0x1CFA, 0x1CFA,
+ 0x1D00, 0x1DBF,
+ 0x1DE7, 0x1DF4,
+ 0x1E00, 0x1F15,
+ 0x1F18, 0x1F1D,
+ 0x1F20, 0x1F45,
+ 0x1F48, 0x1F4D,
+ 0x1F50, 0x1F57,
+ 0x1F59, 0x1F59,
+ 0x1F5B, 0x1F5B,
+ 0x1F5D, 0x1F5D,
+ 0x1F5F, 0x1F7D,
+ 0x1F80, 0x1FB4,
+ 0x1FB6, 0x1FBC,
+ 0x1FBE, 0x1FBE,
+ 0x1FC2, 0x1FC4,
+ 0x1FC6, 0x1FCC,
+ 0x1FD0, 0x1FD3,
+ 0x1FD6, 0x1FDB,
+ 0x1FE0, 0x1FEC,
+ 0x1FF2, 0x1FF4,
+ 0x1FF6, 0x1FFC,
+ 0x2071, 0x2071,
+ 0x207F, 0x207F,
+ 0x2090, 0x209C,
+ 0x2102, 0x2102,
+ 0x2107, 0x2107,
+ 0x210A, 0x2113,
+ 0x2115, 0x2115,
+ 0x2119, 0x211D,
+ 0x2124, 0x2124,
+ 0x2126, 0x2126,
+ 0x2128, 0x2128,
+ 0x212A, 0x212D,
+ 0x212F, 0x2139,
+ 0x213C, 0x213F,
+ 0x2145, 0x2149,
+ 0x214E, 0x214E,
+ 0x2160, 0x2188,
+ 0x24B6, 0x24E9,
+ 0x2C00, 0x2CE4,
+ 0x2CEB, 0x2CEE,
+ 0x2CF2, 0x2CF3,
+ 0x2D00, 0x2D25,
+ 0x2D27, 0x2D27,
+ 0x2D2D, 0x2D2D,
+ 0x2D30, 0x2D67,
+ 0x2D6F, 0x2D6F,
+ 0x2D80, 0x2D96,
+ 0x2DA0, 0x2DA6,
+ 0x2DA8, 0x2DAE,
+ 0x2DB0, 0x2DB6,
+ 0x2DB8, 0x2DBE,
+ 0x2DC0, 0x2DC6,
+ 0x2DC8, 0x2DCE,
+ 0x2DD0, 0x2DD6,
+ 0x2DD8, 0x2DDE,
+ 0x2DE0, 0x2DFF,
+ 0x2E2F, 0x2E2F,
+ 0x3005, 0x3007,
+ 0x3021, 0x3029,
+ 0x3031, 0x3035,
+ 0x3038, 0x303C,
+ 0x3041, 0x3096,
+ 0x309D, 0x309F,
+ 0x30A1, 0x30FA,
+ 0x30FC, 0x30FF,
+ 0x3105, 0x312F,
+ 0x3131, 0x318E,
+ 0x31A0, 0x31BF,
+ 0x31F0, 0x31FF,
+ 0x3400, 0x4DBF,
+ 0x4E00, 0xA48C,
+ 0xA4D0, 0xA4FD,
+ 0xA500, 0xA60C,
+ 0xA610, 0xA61F,
+ 0xA62A, 0xA62B,
+ 0xA640, 0xA66E,
+ 0xA674, 0xA67B,
+ 0xA67F, 0xA6EF,
+ 0xA717, 0xA71F,
+ 0xA722, 0xA788,
+ 0xA78B, 0xA7CA,
+ 0xA7D0, 0xA7D1,
+ 0xA7D3, 0xA7D3,
+ 0xA7D5, 0xA7D9,
+ 0xA7F2, 0xA805,
+ 0xA807, 0xA827,
+ 0xA840, 0xA873,
+ 0xA880, 0xA8C3,
+ 0xA8C5, 0xA8C5,
+ 0xA8F2, 0xA8F7,
+ 0xA8FB, 0xA8FB,
+ 0xA8FD, 0xA8FF,
+ 0xA90A, 0xA92A,
+ 0xA930, 0xA952,
+ 0xA960, 0xA97C,
+ 0xA980, 0xA9B2,
+ 0xA9B4, 0xA9BF,
+ 0xA9CF, 0xA9CF,
+ 0xA9E0, 0xA9EF,
+ 0xA9FA, 0xA9FE,
+ 0xAA00, 0xAA36,
+ 0xAA40, 0xAA4D,
+ 0xAA60, 0xAA76,
+ 0xAA7A, 0xAABE,
+ 0xAAC0, 0xAAC0,
+ 0xAAC2, 0xAAC2,
+ 0xAADB, 0xAADD,
+ 0xAAE0, 0xAAEF,
+ 0xAAF2, 0xAAF5,
+ 0xAB01, 0xAB06,
+ 0xAB09, 0xAB0E,
+ 0xAB11, 0xAB16,
+ 0xAB20, 0xAB26,
+ 0xAB28, 0xAB2E,
+ 0xAB30, 0xAB5A,
+ 0xAB5C, 0xAB69,
+ 0xAB70, 0xABEA,
+ 0xAC00, 0xD7A3,
+ 0xD7B0, 0xD7C6,
+ 0xD7CB, 0xD7FB,
+ 0xF900, 0xFA6D,
+ 0xFA70, 0xFAD9,
+ 0xFB00, 0xFB06,
+ 0xFB13, 0xFB17,
+ 0xFB1D, 0xFB28,
+ 0xFB2A, 0xFB36,
+ 0xFB38, 0xFB3C,
+ 0xFB3E, 0xFB3E,
+ 0xFB40, 0xFB41,
+ 0xFB43, 0xFB44,
+ 0xFB46, 0xFBB1,
+ 0xFBD3, 0xFD3D,
+ 0xFD50, 0xFD8F,
+ 0xFD92, 0xFDC7,
+ 0xFDF0, 0xFDFB,
+ 0xFE70, 0xFE74,
+ 0xFE76, 0xFEFC,
+ 0xFF21, 0xFF3A,
+ 0xFF41, 0xFF5A,
+ 0xFF66, 0xFFBE,
+ 0xFFC2, 0xFFC7,
+ 0xFFCA, 0xFFCF,
+ 0xFFD2, 0xFFD7,
+ 0xFFDA, 0xFFDC,
+ 0x10000, 0x1000B,
+ 0x1000D, 0x10026,
+ 0x10028, 0x1003A,
+ 0x1003C, 0x1003D,
+ 0x1003F, 0x1004D,
+ 0x10050, 0x1005D,
+ 0x10080, 0x100FA,
+ 0x10140, 0x10174,
+ 0x10280, 0x1029C,
+ 0x102A0, 0x102D0,
+ 0x10300, 0x1031F,
+ 0x1032D, 0x1034A,
+ 0x10350, 0x1037A,
+ 0x10380, 0x1039D,
+ 0x103A0, 0x103C3,
+ 0x103C8, 0x103CF,
+ 0x103D1, 0x103D5,
+ 0x10400, 0x1049D,
+ 0x104B0, 0x104D3,
+ 0x104D8, 0x104FB,
+ 0x10500, 0x10527,
+ 0x10530, 0x10563,
+ 0x10570, 0x1057A,
+ 0x1057C, 0x1058A,
+ 0x1058C, 0x10592,
+ 0x10594, 0x10595,
+ 0x10597, 0x105A1,
+ 0x105A3, 0x105B1,
+ 0x105B3, 0x105B9,
+ 0x105BB, 0x105BC,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+ 0x10780, 0x10785,
+ 0x10787, 0x107B0,
+ 0x107B2, 0x107BA,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080A, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083C, 0x1083C,
+ 0x1083F, 0x10855,
+ 0x10860, 0x10876,
+ 0x10880, 0x1089E,
+ 0x108E0, 0x108F2,
+ 0x108F4, 0x108F5,
+ 0x10900, 0x10915,
+ 0x10920, 0x10939,
+ 0x10980, 0x109B7,
+ 0x109BE, 0x109BF,
+ 0x10A00, 0x10A03,
+ 0x10A05, 0x10A06,
+ 0x10A0C, 0x10A13,
+ 0x10A15, 0x10A17,
+ 0x10A19, 0x10A35,
+ 0x10A60, 0x10A7C,
+ 0x10A80, 0x10A9C,
+ 0x10AC0, 0x10AC7,
+ 0x10AC9, 0x10AE4,
+ 0x10B00, 0x10B35,
+ 0x10B40, 0x10B55,
+ 0x10B60, 0x10B72,
+ 0x10B80, 0x10B91,
+ 0x10C00, 0x10C48,
+ 0x10C80, 0x10CB2,
+ 0x10CC0, 0x10CF2,
+ 0x10D00, 0x10D27,
+ 0x10E80, 0x10EA9,
+ 0x10EAB, 0x10EAC,
+ 0x10EB0, 0x10EB1,
+ 0x10F00, 0x10F1C,
+ 0x10F27, 0x10F27,
+ 0x10F30, 0x10F45,
+ 0x10F70, 0x10F81,
+ 0x10FB0, 0x10FC4,
+ 0x10FE0, 0x10FF6,
+ 0x11000, 0x11045,
+ 0x11071, 0x11075,
+ 0x11080, 0x110B8,
+ 0x110C2, 0x110C2,
+ 0x110D0, 0x110E8,
+ 0x11100, 0x11132,
+ 0x11144, 0x11147,
+ 0x11150, 0x11172,
+ 0x11176, 0x11176,
+ 0x11180, 0x111BF,
+ 0x111C1, 0x111C4,
+ 0x111CE, 0x111CF,
+ 0x111DA, 0x111DA,
+ 0x111DC, 0x111DC,
+ 0x11200, 0x11211,
+ 0x11213, 0x11234,
+ 0x11237, 0x11237,
+ 0x1123E, 0x11241,
+ 0x11280, 0x11286,
+ 0x11288, 0x11288,
+ 0x1128A, 0x1128D,
+ 0x1128F, 0x1129D,
+ 0x1129F, 0x112A8,
+ 0x112B0, 0x112E8,
+ 0x11300, 0x11303,
+ 0x11305, 0x1130C,
+ 0x1130F, 0x11310,
+ 0x11313, 0x11328,
+ 0x1132A, 0x11330,
+ 0x11332, 0x11333,
+ 0x11335, 0x11339,
+ 0x1133D, 0x11344,
+ 0x11347, 0x11348,
+ 0x1134B, 0x1134C,
+ 0x11350, 0x11350,
+ 0x11357, 0x11357,
+ 0x1135D, 0x11363,
+ 0x11400, 0x11441,
+ 0x11443, 0x11445,
+ 0x11447, 0x1144A,
+ 0x1145F, 0x11461,
+ 0x11480, 0x114C1,
+ 0x114C4, 0x114C5,
+ 0x114C7, 0x114C7,
+ 0x11580, 0x115B5,
+ 0x115B8, 0x115BE,
+ 0x115D8, 0x115DD,
+ 0x11600, 0x1163E,
+ 0x11640, 0x11640,
+ 0x11644, 0x11644,
+ 0x11680, 0x116B5,
+ 0x116B8, 0x116B8,
+ 0x11700, 0x1171A,
+ 0x1171D, 0x1172A,
+ 0x11740, 0x11746,
+ 0x11800, 0x11838,
+ 0x118A0, 0x118DF,
+ 0x118FF, 0x11906,
+ 0x11909, 0x11909,
+ 0x1190C, 0x11913,
+ 0x11915, 0x11916,
+ 0x11918, 0x11935,
+ 0x11937, 0x11938,
+ 0x1193B, 0x1193C,
+ 0x1193F, 0x11942,
+ 0x119A0, 0x119A7,
+ 0x119AA, 0x119D7,
+ 0x119DA, 0x119DF,
+ 0x119E1, 0x119E1,
+ 0x119E3, 0x119E4,
+ 0x11A00, 0x11A32,
+ 0x11A35, 0x11A3E,
+ 0x11A50, 0x11A97,
+ 0x11A9D, 0x11A9D,
+ 0x11AB0, 0x11AF8,
+ 0x11C00, 0x11C08,
+ 0x11C0A, 0x11C36,
+ 0x11C38, 0x11C3E,
+ 0x11C40, 0x11C40,
+ 0x11C72, 0x11C8F,
+ 0x11C92, 0x11CA7,
+ 0x11CA9, 0x11CB6,
+ 0x11D00, 0x11D06,
+ 0x11D08, 0x11D09,
+ 0x11D0B, 0x11D36,
+ 0x11D3A, 0x11D3A,
+ 0x11D3C, 0x11D3D,
+ 0x11D3F, 0x11D41,
+ 0x11D43, 0x11D43,
+ 0x11D46, 0x11D47,
+ 0x11D60, 0x11D65,
+ 0x11D67, 0x11D68,
+ 0x11D6A, 0x11D8E,
+ 0x11D90, 0x11D91,
+ 0x11D93, 0x11D96,
+ 0x11D98, 0x11D98,
+ 0x11EE0, 0x11EF6,
+ 0x11F00, 0x11F10,
+ 0x11F12, 0x11F3A,
+ 0x11F3E, 0x11F40,
+ 0x11FB0, 0x11FB0,
+ 0x12000, 0x12399,
+ 0x12400, 0x1246E,
+ 0x12480, 0x12543,
+ 0x12F90, 0x12FF0,
+ 0x13000, 0x1342F,
+ 0x13441, 0x13446,
+ 0x14400, 0x14646,
+ 0x16800, 0x16A38,
+ 0x16A40, 0x16A5E,
+ 0x16A70, 0x16ABE,
+ 0x16AD0, 0x16AED,
+ 0x16B00, 0x16B2F,
+ 0x16B40, 0x16B43,
+ 0x16B63, 0x16B77,
+ 0x16B7D, 0x16B8F,
+ 0x16E40, 0x16E7F,
+ 0x16F00, 0x16F4A,
+ 0x16F4F, 0x16F87,
+ 0x16F8F, 0x16F9F,
+ 0x16FE0, 0x16FE1,
+ 0x16FE3, 0x16FE3,
+ 0x16FF0, 0x16FF1,
+ 0x17000, 0x187F7,
+ 0x18800, 0x18CD5,
+ 0x18D00, 0x18D08,
+ 0x1AFF0, 0x1AFF3,
+ 0x1AFF5, 0x1AFFB,
+ 0x1AFFD, 0x1AFFE,
+ 0x1B000, 0x1B122,
+ 0x1B132, 0x1B132,
+ 0x1B150, 0x1B152,
+ 0x1B155, 0x1B155,
+ 0x1B164, 0x1B167,
+ 0x1B170, 0x1B2FB,
+ 0x1BC00, 0x1BC6A,
+ 0x1BC70, 0x1BC7C,
+ 0x1BC80, 0x1BC88,
+ 0x1BC90, 0x1BC99,
+ 0x1BC9E, 0x1BC9E,
+ 0x1D400, 0x1D454,
+ 0x1D456, 0x1D49C,
+ 0x1D49E, 0x1D49F,
+ 0x1D4A2, 0x1D4A2,
+ 0x1D4A5, 0x1D4A6,
+ 0x1D4A9, 0x1D4AC,
+ 0x1D4AE, 0x1D4B9,
+ 0x1D4BB, 0x1D4BB,
+ 0x1D4BD, 0x1D4C3,
+ 0x1D4C5, 0x1D505,
+ 0x1D507, 0x1D50A,
+ 0x1D50D, 0x1D514,
+ 0x1D516, 0x1D51C,
+ 0x1D51E, 0x1D539,
+ 0x1D53B, 0x1D53E,
+ 0x1D540, 0x1D544,
+ 0x1D546, 0x1D546,
+ 0x1D54A, 0x1D550,
+ 0x1D552, 0x1D6A5,
+ 0x1D6A8, 0x1D6C0,
+ 0x1D6C2, 0x1D6DA,
+ 0x1D6DC, 0x1D6FA,
+ 0x1D6FC, 0x1D714,
+ 0x1D716, 0x1D734,
+ 0x1D736, 0x1D74E,
+ 0x1D750, 0x1D76E,
+ 0x1D770, 0x1D788,
+ 0x1D78A, 0x1D7A8,
+ 0x1D7AA, 0x1D7C2,
+ 0x1D7C4, 0x1D7CB,
+ 0x1DF00, 0x1DF1E,
+ 0x1DF25, 0x1DF2A,
+ 0x1E000, 0x1E006,
+ 0x1E008, 0x1E018,
+ 0x1E01B, 0x1E021,
+ 0x1E023, 0x1E024,
+ 0x1E026, 0x1E02A,
+ 0x1E030, 0x1E06D,
+ 0x1E08F, 0x1E08F,
+ 0x1E100, 0x1E12C,
+ 0x1E137, 0x1E13D,
+ 0x1E14E, 0x1E14E,
+ 0x1E290, 0x1E2AD,
+ 0x1E2C0, 0x1E2EB,
+ 0x1E4D0, 0x1E4EB,
+ 0x1E7E0, 0x1E7E6,
+ 0x1E7E8, 0x1E7EB,
+ 0x1E7ED, 0x1E7EE,
+ 0x1E7F0, 0x1E7FE,
+ 0x1E800, 0x1E8C4,
+ 0x1E900, 0x1E943,
+ 0x1E947, 0x1E947,
+ 0x1E94B, 0x1E94B,
+ 0x1EE00, 0x1EE03,
+ 0x1EE05, 0x1EE1F,
+ 0x1EE21, 0x1EE22,
+ 0x1EE24, 0x1EE24,
+ 0x1EE27, 0x1EE27,
+ 0x1EE29, 0x1EE32,
+ 0x1EE34, 0x1EE37,
+ 0x1EE39, 0x1EE39,
+ 0x1EE3B, 0x1EE3B,
+ 0x1EE42, 0x1EE42,
+ 0x1EE47, 0x1EE47,
+ 0x1EE49, 0x1EE49,
+ 0x1EE4B, 0x1EE4B,
+ 0x1EE4D, 0x1EE4F,
+ 0x1EE51, 0x1EE52,
+ 0x1EE54, 0x1EE54,
+ 0x1EE57, 0x1EE57,
+ 0x1EE59, 0x1EE59,
+ 0x1EE5B, 0x1EE5B,
+ 0x1EE5D, 0x1EE5D,
+ 0x1EE5F, 0x1EE5F,
+ 0x1EE61, 0x1EE62,
+ 0x1EE64, 0x1EE64,
+ 0x1EE67, 0x1EE6A,
+ 0x1EE6C, 0x1EE72,
+ 0x1EE74, 0x1EE77,
+ 0x1EE79, 0x1EE7C,
+ 0x1EE7E, 0x1EE7E,
+ 0x1EE80, 0x1EE89,
+ 0x1EE8B, 0x1EE9B,
+ 0x1EEA1, 0x1EEA3,
+ 0x1EEA5, 0x1EEA9,
+ 0x1EEAB, 0x1EEBB,
+ 0x1F130, 0x1F149,
+ 0x1F150, 0x1F169,
+ 0x1F170, 0x1F189,
+ 0x20000, 0x2A6DF,
+ 0x2A700, 0x2B739,
+ 0x2B740, 0x2B81D,
+ 0x2B820, 0x2CEA1,
+ 0x2CEB0, 0x2EBE0,
+ 0x2F800, 0x2FA1D,
+ 0x30000, 0x3134A,
+ 0x31350, 0x323AF,
+};
+
+#define UNICODE_ALNUM_CODEPOINTS_LENGTH 1528
+static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEPOINTS_LENGTH] = {
+ 0x100, 0x2C1,
+ 0x2C6, 0x2D1,
+ 0x2E0, 0x2E4,
+ 0x2EC, 0x2EC,
+ 0x2EE, 0x2EE,
+ 0x345, 0x345,
+ 0x370, 0x374,
+ 0x376, 0x377,
+ 0x37A, 0x37D,
+ 0x37F, 0x37F,
+ 0x386, 0x386,
+ 0x388, 0x38A,
+ 0x38C, 0x38C,
+ 0x38E, 0x3A1,
+ 0x3A3, 0x3F5,
+ 0x3F7, 0x481,
+ 0x48A, 0x52F,
+ 0x531, 0x556,
+ 0x559, 0x559,
+ 0x560, 0x588,
+ 0x5B0, 0x5BD,
+ 0x5BF, 0x5BF,
+ 0x5C1, 0x5C2,
+ 0x5C4, 0x5C5,
+ 0x5C7, 0x5C7,
+ 0x5D0, 0x5EA,
+ 0x5EF, 0x5F2,
+ 0x610, 0x61A,
+ 0x620, 0x657,
+ 0x659, 0x669,
+ 0x66E, 0x6D3,
+ 0x6D5, 0x6DC,
+ 0x6E1, 0x6E8,
+ 0x6ED, 0x6FC,
+ 0x6FF, 0x6FF,
+ 0x710, 0x73F,
+ 0x74D, 0x7B1,
+ 0x7C0, 0x7EA,
+ 0x7F4, 0x7F5,
+ 0x7FA, 0x7FA,
+ 0x800, 0x817,
+ 0x81A, 0x82C,
+ 0x840, 0x858,
+ 0x860, 0x86A,
+ 0x870, 0x887,
+ 0x889, 0x88E,
+ 0x8A0, 0x8C9,
+ 0x8D4, 0x8DF,
+ 0x8E3, 0x8E9,
+ 0x8F0, 0x93B,
+ 0x93D, 0x94C,
+ 0x94E, 0x950,
+ 0x955, 0x963,
+ 0x966, 0x96F,
+ 0x971, 0x983,
+ 0x985, 0x98C,
+ 0x98F, 0x990,
+ 0x993, 0x9A8,
+ 0x9AA, 0x9B0,
+ 0x9B2, 0x9B2,
+ 0x9B6, 0x9B9,
+ 0x9BD, 0x9C4,
+ 0x9C7, 0x9C8,
+ 0x9CB, 0x9CC,
+ 0x9CE, 0x9CE,
+ 0x9D7, 0x9D7,
+ 0x9DC, 0x9DD,
+ 0x9DF, 0x9E3,
+ 0x9E6, 0x9F1,
+ 0x9FC, 0x9FC,
+ 0xA01, 0xA03,
+ 0xA05, 0xA0A,
+ 0xA0F, 0xA10,
+ 0xA13, 0xA28,
+ 0xA2A, 0xA30,
+ 0xA32, 0xA33,
+ 0xA35, 0xA36,
+ 0xA38, 0xA39,
+ 0xA3E, 0xA42,
+ 0xA47, 0xA48,
+ 0xA4B, 0xA4C,
+ 0xA51, 0xA51,
+ 0xA59, 0xA5C,
+ 0xA5E, 0xA5E,
+ 0xA66, 0xA75,
+ 0xA81, 0xA83,
+ 0xA85, 0xA8D,
+ 0xA8F, 0xA91,
+ 0xA93, 0xAA8,
+ 0xAAA, 0xAB0,
+ 0xAB2, 0xAB3,
+ 0xAB5, 0xAB9,
+ 0xABD, 0xAC5,
+ 0xAC7, 0xAC9,
+ 0xACB, 0xACC,
+ 0xAD0, 0xAD0,
+ 0xAE0, 0xAE3,
+ 0xAE6, 0xAEF,
+ 0xAF9, 0xAFC,
+ 0xB01, 0xB03,
+ 0xB05, 0xB0C,
+ 0xB0F, 0xB10,
+ 0xB13, 0xB28,
+ 0xB2A, 0xB30,
+ 0xB32, 0xB33,
+ 0xB35, 0xB39,
+ 0xB3D, 0xB44,
+ 0xB47, 0xB48,
+ 0xB4B, 0xB4C,
+ 0xB56, 0xB57,
+ 0xB5C, 0xB5D,
+ 0xB5F, 0xB63,
+ 0xB66, 0xB6F,
+ 0xB71, 0xB71,
+ 0xB82, 0xB83,
+ 0xB85, 0xB8A,
+ 0xB8E, 0xB90,
+ 0xB92, 0xB95,
+ 0xB99, 0xB9A,
+ 0xB9C, 0xB9C,
+ 0xB9E, 0xB9F,
+ 0xBA3, 0xBA4,
+ 0xBA8, 0xBAA,
+ 0xBAE, 0xBB9,
+ 0xBBE, 0xBC2,
+ 0xBC6, 0xBC8,
+ 0xBCA, 0xBCC,
+ 0xBD0, 0xBD0,
+ 0xBD7, 0xBD7,
+ 0xBE6, 0xBEF,
+ 0xC00, 0xC0C,
+ 0xC0E, 0xC10,
+ 0xC12, 0xC28,
+ 0xC2A, 0xC39,
+ 0xC3D, 0xC44,
+ 0xC46, 0xC48,
+ 0xC4A, 0xC4C,
+ 0xC55, 0xC56,
+ 0xC58, 0xC5A,
+ 0xC5D, 0xC5D,
+ 0xC60, 0xC63,
+ 0xC66, 0xC6F,
+ 0xC80, 0xC83,
+ 0xC85, 0xC8C,
+ 0xC8E, 0xC90,
+ 0xC92, 0xCA8,
+ 0xCAA, 0xCB3,
+ 0xCB5, 0xCB9,
+ 0xCBD, 0xCC4,
+ 0xCC6, 0xCC8,
+ 0xCCA, 0xCCC,
+ 0xCD5, 0xCD6,
+ 0xCDD, 0xCDE,
+ 0xCE0, 0xCE3,
+ 0xCE6, 0xCEF,
+ 0xCF1, 0xCF3,
+ 0xD00, 0xD0C,
+ 0xD0E, 0xD10,
+ 0xD12, 0xD3A,
+ 0xD3D, 0xD44,
+ 0xD46, 0xD48,
+ 0xD4A, 0xD4C,
+ 0xD4E, 0xD4E,
+ 0xD54, 0xD57,
+ 0xD5F, 0xD63,
+ 0xD66, 0xD6F,
+ 0xD7A, 0xD7F,
+ 0xD81, 0xD83,
+ 0xD85, 0xD96,
+ 0xD9A, 0xDB1,
+ 0xDB3, 0xDBB,
+ 0xDBD, 0xDBD,
+ 0xDC0, 0xDC6,
+ 0xDCF, 0xDD4,
+ 0xDD6, 0xDD6,
+ 0xDD8, 0xDDF,
+ 0xDE6, 0xDEF,
+ 0xDF2, 0xDF3,
+ 0xE01, 0xE3A,
+ 0xE40, 0xE46,
+ 0xE4D, 0xE4D,
+ 0xE50, 0xE59,
+ 0xE81, 0xE82,
+ 0xE84, 0xE84,
+ 0xE86, 0xE8A,
+ 0xE8C, 0xEA3,
+ 0xEA5, 0xEA5,
+ 0xEA7, 0xEB9,
+ 0xEBB, 0xEBD,
+ 0xEC0, 0xEC4,
+ 0xEC6, 0xEC6,
+ 0xECD, 0xECD,
+ 0xED0, 0xED9,
+ 0xEDC, 0xEDF,
+ 0xF00, 0xF00,
+ 0xF20, 0xF29,
+ 0xF40, 0xF47,
+ 0xF49, 0xF6C,
+ 0xF71, 0xF83,
+ 0xF88, 0xF97,
+ 0xF99, 0xFBC,
+ 0x1000, 0x1036,
+ 0x1038, 0x1038,
+ 0x103B, 0x1049,
+ 0x1050, 0x109D,
+ 0x10A0, 0x10C5,
+ 0x10C7, 0x10C7,
+ 0x10CD, 0x10CD,
+ 0x10D0, 0x10FA,
+ 0x10FC, 0x1248,
+ 0x124A, 0x124D,
+ 0x1250, 0x1256,
+ 0x1258, 0x1258,
+ 0x125A, 0x125D,
+ 0x1260, 0x1288,
+ 0x128A, 0x128D,
+ 0x1290, 0x12B0,
+ 0x12B2, 0x12B5,
+ 0x12B8, 0x12BE,
+ 0x12C0, 0x12C0,
+ 0x12C2, 0x12C5,
+ 0x12C8, 0x12D6,
+ 0x12D8, 0x1310,
+ 0x1312, 0x1315,
+ 0x1318, 0x135A,
+ 0x1380, 0x138F,
+ 0x13A0, 0x13F5,
+ 0x13F8, 0x13FD,
+ 0x1401, 0x166C,
+ 0x166F, 0x167F,
+ 0x1681, 0x169A,
+ 0x16A0, 0x16EA,
+ 0x16EE, 0x16F8,
+ 0x1700, 0x1713,
+ 0x171F, 0x1733,
+ 0x1740, 0x1753,
+ 0x1760, 0x176C,
+ 0x176E, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17B3,
+ 0x17B6, 0x17C8,
+ 0x17D7, 0x17D7,
+ 0x17DC, 0x17DC,
+ 0x17E0, 0x17E9,
+ 0x1810, 0x1819,
+ 0x1820, 0x1878,
+ 0x1880, 0x18AA,
+ 0x18B0, 0x18F5,
+ 0x1900, 0x191E,
+ 0x1920, 0x192B,
+ 0x1930, 0x1938,
+ 0x1946, 0x196D,
+ 0x1970, 0x1974,
+ 0x1980, 0x19AB,
+ 0x19B0, 0x19C9,
+ 0x19D0, 0x19D9,
+ 0x1A00, 0x1A1B,
+ 0x1A20, 0x1A5E,
+ 0x1A61, 0x1A74,
+ 0x1A80, 0x1A89,
+ 0x1A90, 0x1A99,
+ 0x1AA7, 0x1AA7,
+ 0x1ABF, 0x1AC0,
+ 0x1ACC, 0x1ACE,
+ 0x1B00, 0x1B33,
+ 0x1B35, 0x1B43,
+ 0x1B45, 0x1B4C,
+ 0x1B50, 0x1B59,
+ 0x1B80, 0x1BA9,
+ 0x1BAC, 0x1BE5,
+ 0x1BE7, 0x1BF1,
+ 0x1C00, 0x1C36,
+ 0x1C40, 0x1C49,
+ 0x1C4D, 0x1C7D,
+ 0x1C80, 0x1C88,
+ 0x1C90, 0x1CBA,
+ 0x1CBD, 0x1CBF,
+ 0x1CE9, 0x1CEC,
+ 0x1CEE, 0x1CF3,
+ 0x1CF5, 0x1CF6,
+ 0x1CFA, 0x1CFA,
+ 0x1D00, 0x1DBF,
+ 0x1DE7, 0x1DF4,
+ 0x1E00, 0x1F15,
+ 0x1F18, 0x1F1D,
+ 0x1F20, 0x1F45,
+ 0x1F48, 0x1F4D,
+ 0x1F50, 0x1F57,
+ 0x1F59, 0x1F59,
+ 0x1F5B, 0x1F5B,
+ 0x1F5D, 0x1F5D,
+ 0x1F5F, 0x1F7D,
+ 0x1F80, 0x1FB4,
+ 0x1FB6, 0x1FBC,
+ 0x1FBE, 0x1FBE,
+ 0x1FC2, 0x1FC4,
+ 0x1FC6, 0x1FCC,
+ 0x1FD0, 0x1FD3,
+ 0x1FD6, 0x1FDB,
+ 0x1FE0, 0x1FEC,
+ 0x1FF2, 0x1FF4,
+ 0x1FF6, 0x1FFC,
+ 0x2071, 0x2071,
+ 0x207F, 0x207F,
+ 0x2090, 0x209C,
+ 0x2102, 0x2102,
+ 0x2107, 0x2107,
+ 0x210A, 0x2113,
+ 0x2115, 0x2115,
+ 0x2119, 0x211D,
+ 0x2124, 0x2124,
+ 0x2126, 0x2126,
+ 0x2128, 0x2128,
+ 0x212A, 0x212D,
+ 0x212F, 0x2139,
+ 0x213C, 0x213F,
+ 0x2145, 0x2149,
+ 0x214E, 0x214E,
+ 0x2160, 0x2188,
+ 0x24B6, 0x24E9,
+ 0x2C00, 0x2CE4,
+ 0x2CEB, 0x2CEE,
+ 0x2CF2, 0x2CF3,
+ 0x2D00, 0x2D25,
+ 0x2D27, 0x2D27,
+ 0x2D2D, 0x2D2D,
+ 0x2D30, 0x2D67,
+ 0x2D6F, 0x2D6F,
+ 0x2D80, 0x2D96,
+ 0x2DA0, 0x2DA6,
+ 0x2DA8, 0x2DAE,
+ 0x2DB0, 0x2DB6,
+ 0x2DB8, 0x2DBE,
+ 0x2DC0, 0x2DC6,
+ 0x2DC8, 0x2DCE,
+ 0x2DD0, 0x2DD6,
+ 0x2DD8, 0x2DDE,
+ 0x2DE0, 0x2DFF,
+ 0x2E2F, 0x2E2F,
+ 0x3005, 0x3007,
+ 0x3021, 0x3029,
+ 0x3031, 0x3035,
+ 0x3038, 0x303C,
+ 0x3041, 0x3096,
+ 0x309D, 0x309F,
+ 0x30A1, 0x30FA,
+ 0x30FC, 0x30FF,
+ 0x3105, 0x312F,
+ 0x3131, 0x318E,
+ 0x31A0, 0x31BF,
+ 0x31F0, 0x31FF,
+ 0x3400, 0x4DBF,
+ 0x4E00, 0xA48C,
+ 0xA4D0, 0xA4FD,
+ 0xA500, 0xA60C,
+ 0xA610, 0xA62B,
+ 0xA640, 0xA66E,
+ 0xA674, 0xA67B,
+ 0xA67F, 0xA6EF,
+ 0xA717, 0xA71F,
+ 0xA722, 0xA788,
+ 0xA78B, 0xA7CA,
+ 0xA7D0, 0xA7D1,
+ 0xA7D3, 0xA7D3,
+ 0xA7D5, 0xA7D9,
+ 0xA7F2, 0xA805,
+ 0xA807, 0xA827,
+ 0xA840, 0xA873,
+ 0xA880, 0xA8C3,
+ 0xA8C5, 0xA8C5,
+ 0xA8D0, 0xA8D9,
+ 0xA8F2, 0xA8F7,
+ 0xA8FB, 0xA8FB,
+ 0xA8FD, 0xA92A,
+ 0xA930, 0xA952,
+ 0xA960, 0xA97C,
+ 0xA980, 0xA9B2,
+ 0xA9B4, 0xA9BF,
+ 0xA9CF, 0xA9D9,
+ 0xA9E0, 0xA9FE,
+ 0xAA00, 0xAA36,
+ 0xAA40, 0xAA4D,
+ 0xAA50, 0xAA59,
+ 0xAA60, 0xAA76,
+ 0xAA7A, 0xAABE,
+ 0xAAC0, 0xAAC0,
+ 0xAAC2, 0xAAC2,
+ 0xAADB, 0xAADD,
+ 0xAAE0, 0xAAEF,
+ 0xAAF2, 0xAAF5,
+ 0xAB01, 0xAB06,
+ 0xAB09, 0xAB0E,
+ 0xAB11, 0xAB16,
+ 0xAB20, 0xAB26,
+ 0xAB28, 0xAB2E,
+ 0xAB30, 0xAB5A,
+ 0xAB5C, 0xAB69,
+ 0xAB70, 0xABEA,
+ 0xABF0, 0xABF9,
+ 0xAC00, 0xD7A3,
+ 0xD7B0, 0xD7C6,
+ 0xD7CB, 0xD7FB,
+ 0xF900, 0xFA6D,
+ 0xFA70, 0xFAD9,
+ 0xFB00, 0xFB06,
+ 0xFB13, 0xFB17,
+ 0xFB1D, 0xFB28,
+ 0xFB2A, 0xFB36,
+ 0xFB38, 0xFB3C,
+ 0xFB3E, 0xFB3E,
+ 0xFB40, 0xFB41,
+ 0xFB43, 0xFB44,
+ 0xFB46, 0xFBB1,
+ 0xFBD3, 0xFD3D,
+ 0xFD50, 0xFD8F,
+ 0xFD92, 0xFDC7,
+ 0xFDF0, 0xFDFB,
+ 0xFE70, 0xFE74,
+ 0xFE76, 0xFEFC,
+ 0xFF10, 0xFF19,
+ 0xFF21, 0xFF3A,
+ 0xFF41, 0xFF5A,
+ 0xFF66, 0xFFBE,
+ 0xFFC2, 0xFFC7,
+ 0xFFCA, 0xFFCF,
+ 0xFFD2, 0xFFD7,
+ 0xFFDA, 0xFFDC,
+ 0x10000, 0x1000B,
+ 0x1000D, 0x10026,
+ 0x10028, 0x1003A,
+ 0x1003C, 0x1003D,
+ 0x1003F, 0x1004D,
+ 0x10050, 0x1005D,
+ 0x10080, 0x100FA,
+ 0x10140, 0x10174,
+ 0x10280, 0x1029C,
+ 0x102A0, 0x102D0,
+ 0x10300, 0x1031F,
+ 0x1032D, 0x1034A,
+ 0x10350, 0x1037A,
+ 0x10380, 0x1039D,
+ 0x103A0, 0x103C3,
+ 0x103C8, 0x103CF,
+ 0x103D1, 0x103D5,
+ 0x10400, 0x1049D,
+ 0x104A0, 0x104A9,
+ 0x104B0, 0x104D3,
+ 0x104D8, 0x104FB,
+ 0x10500, 0x10527,
+ 0x10530, 0x10563,
+ 0x10570, 0x1057A,
+ 0x1057C, 0x1058A,
+ 0x1058C, 0x10592,
+ 0x10594, 0x10595,
+ 0x10597, 0x105A1,
+ 0x105A3, 0x105B1,
+ 0x105B3, 0x105B9,
+ 0x105BB, 0x105BC,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+ 0x10780, 0x10785,
+ 0x10787, 0x107B0,
+ 0x107B2, 0x107BA,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080A, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083C, 0x1083C,
+ 0x1083F, 0x10855,
+ 0x10860, 0x10876,
+ 0x10880, 0x1089E,
+ 0x108E0, 0x108F2,
+ 0x108F4, 0x108F5,
+ 0x10900, 0x10915,
+ 0x10920, 0x10939,
+ 0x10980, 0x109B7,
+ 0x109BE, 0x109BF,
+ 0x10A00, 0x10A03,
+ 0x10A05, 0x10A06,
+ 0x10A0C, 0x10A13,
+ 0x10A15, 0x10A17,
+ 0x10A19, 0x10A35,
+ 0x10A60, 0x10A7C,
+ 0x10A80, 0x10A9C,
+ 0x10AC0, 0x10AC7,
+ 0x10AC9, 0x10AE4,
+ 0x10B00, 0x10B35,
+ 0x10B40, 0x10B55,
+ 0x10B60, 0x10B72,
+ 0x10B80, 0x10B91,
+ 0x10C00, 0x10C48,
+ 0x10C80, 0x10CB2,
+ 0x10CC0, 0x10CF2,
+ 0x10D00, 0x10D27,
+ 0x10D30, 0x10D39,
+ 0x10E80, 0x10EA9,
+ 0x10EAB, 0x10EAC,
+ 0x10EB0, 0x10EB1,
+ 0x10F00, 0x10F1C,
+ 0x10F27, 0x10F27,
+ 0x10F30, 0x10F45,
+ 0x10F70, 0x10F81,
+ 0x10FB0, 0x10FC4,
+ 0x10FE0, 0x10FF6,
+ 0x11000, 0x11045,
+ 0x11066, 0x1106F,
+ 0x11071, 0x11075,
+ 0x11080, 0x110B8,
+ 0x110C2, 0x110C2,
+ 0x110D0, 0x110E8,
+ 0x110F0, 0x110F9,
+ 0x11100, 0x11132,
+ 0x11136, 0x1113F,
+ 0x11144, 0x11147,
+ 0x11150, 0x11172,
+ 0x11176, 0x11176,
+ 0x11180, 0x111BF,
+ 0x111C1, 0x111C4,
+ 0x111CE, 0x111DA,
+ 0x111DC, 0x111DC,
+ 0x11200, 0x11211,
+ 0x11213, 0x11234,
+ 0x11237, 0x11237,
+ 0x1123E, 0x11241,
+ 0x11280, 0x11286,
+ 0x11288, 0x11288,
+ 0x1128A, 0x1128D,
+ 0x1128F, 0x1129D,
+ 0x1129F, 0x112A8,
+ 0x112B0, 0x112E8,
+ 0x112F0, 0x112F9,
+ 0x11300, 0x11303,
+ 0x11305, 0x1130C,
+ 0x1130F, 0x11310,
+ 0x11313, 0x11328,
+ 0x1132A, 0x11330,
+ 0x11332, 0x11333,
+ 0x11335, 0x11339,
+ 0x1133D, 0x11344,
+ 0x11347, 0x11348,
+ 0x1134B, 0x1134C,
+ 0x11350, 0x11350,
+ 0x11357, 0x11357,
+ 0x1135D, 0x11363,
+ 0x11400, 0x11441,
+ 0x11443, 0x11445,
+ 0x11447, 0x1144A,
+ 0x11450, 0x11459,
+ 0x1145F, 0x11461,
+ 0x11480, 0x114C1,
+ 0x114C4, 0x114C5,
+ 0x114C7, 0x114C7,
+ 0x114D0, 0x114D9,
+ 0x11580, 0x115B5,
+ 0x115B8, 0x115BE,
+ 0x115D8, 0x115DD,
+ 0x11600, 0x1163E,
+ 0x11640, 0x11640,
+ 0x11644, 0x11644,
+ 0x11650, 0x11659,
+ 0x11680, 0x116B5,
+ 0x116B8, 0x116B8,
+ 0x116C0, 0x116C9,
+ 0x11700, 0x1171A,
+ 0x1171D, 0x1172A,
+ 0x11730, 0x11739,
+ 0x11740, 0x11746,
+ 0x11800, 0x11838,
+ 0x118A0, 0x118E9,
+ 0x118FF, 0x11906,
+ 0x11909, 0x11909,
+ 0x1190C, 0x11913,
+ 0x11915, 0x11916,
+ 0x11918, 0x11935,
+ 0x11937, 0x11938,
+ 0x1193B, 0x1193C,
+ 0x1193F, 0x11942,
+ 0x11950, 0x11959,
+ 0x119A0, 0x119A7,
+ 0x119AA, 0x119D7,
+ 0x119DA, 0x119DF,
+ 0x119E1, 0x119E1,
+ 0x119E3, 0x119E4,
+ 0x11A00, 0x11A32,
+ 0x11A35, 0x11A3E,
+ 0x11A50, 0x11A97,
+ 0x11A9D, 0x11A9D,
+ 0x11AB0, 0x11AF8,
+ 0x11C00, 0x11C08,
+ 0x11C0A, 0x11C36,
+ 0x11C38, 0x11C3E,
+ 0x11C40, 0x11C40,
+ 0x11C50, 0x11C59,
+ 0x11C72, 0x11C8F,
+ 0x11C92, 0x11CA7,
+ 0x11CA9, 0x11CB6,
+ 0x11D00, 0x11D06,
+ 0x11D08, 0x11D09,
+ 0x11D0B, 0x11D36,
+ 0x11D3A, 0x11D3A,
+ 0x11D3C, 0x11D3D,
+ 0x11D3F, 0x11D41,
+ 0x11D43, 0x11D43,
+ 0x11D46, 0x11D47,
+ 0x11D50, 0x11D59,
+ 0x11D60, 0x11D65,
+ 0x11D67, 0x11D68,
+ 0x11D6A, 0x11D8E,
+ 0x11D90, 0x11D91,
+ 0x11D93, 0x11D96,
+ 0x11D98, 0x11D98,
+ 0x11DA0, 0x11DA9,
+ 0x11EE0, 0x11EF6,
+ 0x11F00, 0x11F10,
+ 0x11F12, 0x11F3A,
+ 0x11F3E, 0x11F40,
+ 0x11F50, 0x11F59,
+ 0x11FB0, 0x11FB0,
+ 0x12000, 0x12399,
+ 0x12400, 0x1246E,
+ 0x12480, 0x12543,
+ 0x12F90, 0x12FF0,
+ 0x13000, 0x1342F,
+ 0x13441, 0x13446,
+ 0x14400, 0x14646,
+ 0x16800, 0x16A38,
+ 0x16A40, 0x16A5E,
+ 0x16A60, 0x16A69,
+ 0x16A70, 0x16ABE,
+ 0x16AC0, 0x16AC9,
+ 0x16AD0, 0x16AED,
+ 0x16B00, 0x16B2F,
+ 0x16B40, 0x16B43,
+ 0x16B50, 0x16B59,
+ 0x16B63, 0x16B77,
+ 0x16B7D, 0x16B8F,
+ 0x16E40, 0x16E7F,
+ 0x16F00, 0x16F4A,
+ 0x16F4F, 0x16F87,
+ 0x16F8F, 0x16F9F,
+ 0x16FE0, 0x16FE1,
+ 0x16FE3, 0x16FE3,
+ 0x16FF0, 0x16FF1,
+ 0x17000, 0x187F7,
+ 0x18800, 0x18CD5,
+ 0x18D00, 0x18D08,
+ 0x1AFF0, 0x1AFF3,
+ 0x1AFF5, 0x1AFFB,
+ 0x1AFFD, 0x1AFFE,
+ 0x1B000, 0x1B122,
+ 0x1B132, 0x1B132,
+ 0x1B150, 0x1B152,
+ 0x1B155, 0x1B155,
+ 0x1B164, 0x1B167,
+ 0x1B170, 0x1B2FB,
+ 0x1BC00, 0x1BC6A,
+ 0x1BC70, 0x1BC7C,
+ 0x1BC80, 0x1BC88,
+ 0x1BC90, 0x1BC99,
+ 0x1BC9E, 0x1BC9E,
+ 0x1D400, 0x1D454,
+ 0x1D456, 0x1D49C,
+ 0x1D49E, 0x1D49F,
+ 0x1D4A2, 0x1D4A2,
+ 0x1D4A5, 0x1D4A6,
+ 0x1D4A9, 0x1D4AC,
+ 0x1D4AE, 0x1D4B9,
+ 0x1D4BB, 0x1D4BB,
+ 0x1D4BD, 0x1D4C3,
+ 0x1D4C5, 0x1D505,
+ 0x1D507, 0x1D50A,
+ 0x1D50D, 0x1D514,
+ 0x1D516, 0x1D51C,
+ 0x1D51E, 0x1D539,
+ 0x1D53B, 0x1D53E,
+ 0x1D540, 0x1D544,
+ 0x1D546, 0x1D546,
+ 0x1D54A, 0x1D550,
+ 0x1D552, 0x1D6A5,
+ 0x1D6A8, 0x1D6C0,
+ 0x1D6C2, 0x1D6DA,
+ 0x1D6DC, 0x1D6FA,
+ 0x1D6FC, 0x1D714,
+ 0x1D716, 0x1D734,
+ 0x1D736, 0x1D74E,
+ 0x1D750, 0x1D76E,
+ 0x1D770, 0x1D788,
+ 0x1D78A, 0x1D7A8,
+ 0x1D7AA, 0x1D7C2,
+ 0x1D7C4, 0x1D7CB,
+ 0x1D7CE, 0x1D7FF,
+ 0x1DF00, 0x1DF1E,
+ 0x1DF25, 0x1DF2A,
+ 0x1E000, 0x1E006,
+ 0x1E008, 0x1E018,
+ 0x1E01B, 0x1E021,
+ 0x1E023, 0x1E024,
+ 0x1E026, 0x1E02A,
+ 0x1E030, 0x1E06D,
+ 0x1E08F, 0x1E08F,
+ 0x1E100, 0x1E12C,
+ 0x1E137, 0x1E13D,
+ 0x1E140, 0x1E149,
+ 0x1E14E, 0x1E14E,
+ 0x1E290, 0x1E2AD,
+ 0x1E2C0, 0x1E2EB,
+ 0x1E2F0, 0x1E2F9,
+ 0x1E4D0, 0x1E4EB,
+ 0x1E4F0, 0x1E4F9,
+ 0x1E7E0, 0x1E7E6,
+ 0x1E7E8, 0x1E7EB,
+ 0x1E7ED, 0x1E7EE,
+ 0x1E7F0, 0x1E7FE,
+ 0x1E800, 0x1E8C4,
+ 0x1E900, 0x1E943,
+ 0x1E947, 0x1E947,
+ 0x1E94B, 0x1E94B,
+ 0x1E950, 0x1E959,
+ 0x1EE00, 0x1EE03,
+ 0x1EE05, 0x1EE1F,
+ 0x1EE21, 0x1EE22,
+ 0x1EE24, 0x1EE24,
+ 0x1EE27, 0x1EE27,
+ 0x1EE29, 0x1EE32,
+ 0x1EE34, 0x1EE37,
+ 0x1EE39, 0x1EE39,
+ 0x1EE3B, 0x1EE3B,
+ 0x1EE42, 0x1EE42,
+ 0x1EE47, 0x1EE47,
+ 0x1EE49, 0x1EE49,
+ 0x1EE4B, 0x1EE4B,
+ 0x1EE4D, 0x1EE4F,
+ 0x1EE51, 0x1EE52,
+ 0x1EE54, 0x1EE54,
+ 0x1EE57, 0x1EE57,
+ 0x1EE59, 0x1EE59,
+ 0x1EE5B, 0x1EE5B,
+ 0x1EE5D, 0x1EE5D,
+ 0x1EE5F, 0x1EE5F,
+ 0x1EE61, 0x1EE62,
+ 0x1EE64, 0x1EE64,
+ 0x1EE67, 0x1EE6A,
+ 0x1EE6C, 0x1EE72,
+ 0x1EE74, 0x1EE77,
+ 0x1EE79, 0x1EE7C,
+ 0x1EE7E, 0x1EE7E,
+ 0x1EE80, 0x1EE89,
+ 0x1EE8B, 0x1EE9B,
+ 0x1EEA1, 0x1EEA3,
+ 0x1EEA5, 0x1EEA9,
+ 0x1EEAB, 0x1EEBB,
+ 0x1F130, 0x1F149,
+ 0x1F150, 0x1F169,
+ 0x1F170, 0x1F189,
+ 0x1FBF0, 0x1FBF9,
+ 0x20000, 0x2A6DF,
+ 0x2A700, 0x2B739,
+ 0x2B740, 0x2B81D,
+ 0x2B820, 0x2CEA1,
+ 0x2CEB0, 0x2EBE0,
+ 0x2F800, 0x2FA1D,
+ 0x30000, 0x3134A,
+ 0x31350, 0x323AF,
+};
+
+#define UNICODE_ISUPPER_CODEPOINTS_LENGTH 1302
+static const pm_unicode_codepoint_t unicode_isupper_codepoints[UNICODE_ISUPPER_CODEPOINTS_LENGTH] = {
+ 0x100, 0x100,
+ 0x102, 0x102,
+ 0x104, 0x104,
+ 0x106, 0x106,
+ 0x108, 0x108,
+ 0x10A, 0x10A,
+ 0x10C, 0x10C,
+ 0x10E, 0x10E,
+ 0x110, 0x110,
+ 0x112, 0x112,
+ 0x114, 0x114,
+ 0x116, 0x116,
+ 0x118, 0x118,
+ 0x11A, 0x11A,
+ 0x11C, 0x11C,
+ 0x11E, 0x11E,
+ 0x120, 0x120,
+ 0x122, 0x122,
+ 0x124, 0x124,
+ 0x126, 0x126,
+ 0x128, 0x128,
+ 0x12A, 0x12A,
+ 0x12C, 0x12C,
+ 0x12E, 0x12E,
+ 0x130, 0x130,
+ 0x132, 0x132,
+ 0x134, 0x134,
+ 0x136, 0x136,
+ 0x139, 0x139,
+ 0x13B, 0x13B,
+ 0x13D, 0x13D,
+ 0x13F, 0x13F,
+ 0x141, 0x141,
+ 0x143, 0x143,
+ 0x145, 0x145,
+ 0x147, 0x147,
+ 0x14A, 0x14A,
+ 0x14C, 0x14C,
+ 0x14E, 0x14E,
+ 0x150, 0x150,
+ 0x152, 0x152,
+ 0x154, 0x154,
+ 0x156, 0x156,
+ 0x158, 0x158,
+ 0x15A, 0x15A,
+ 0x15C, 0x15C,
+ 0x15E, 0x15E,
+ 0x160, 0x160,
+ 0x162, 0x162,
+ 0x164, 0x164,
+ 0x166, 0x166,
+ 0x168, 0x168,
+ 0x16A, 0x16A,
+ 0x16C, 0x16C,
+ 0x16E, 0x16E,
+ 0x170, 0x170,
+ 0x172, 0x172,
+ 0x174, 0x174,
+ 0x176, 0x176,
+ 0x178, 0x179,
+ 0x17B, 0x17B,
+ 0x17D, 0x17D,
+ 0x181, 0x182,
+ 0x184, 0x184,
+ 0x186, 0x187,
+ 0x189, 0x18B,
+ 0x18E, 0x191,
+ 0x193, 0x194,
+ 0x196, 0x198,
+ 0x19C, 0x19D,
+ 0x19F, 0x1A0,
+ 0x1A2, 0x1A2,
+ 0x1A4, 0x1A4,
+ 0x1A6, 0x1A7,
+ 0x1A9, 0x1A9,
+ 0x1AC, 0x1AC,
+ 0x1AE, 0x1AF,
+ 0x1B1, 0x1B3,
+ 0x1B5, 0x1B5,
+ 0x1B7, 0x1B8,
+ 0x1BC, 0x1BC,
+ 0x1C4, 0x1C5,
+ 0x1C7, 0x1C8,
+ 0x1CA, 0x1CB,
+ 0x1CD, 0x1CD,
+ 0x1CF, 0x1CF,
+ 0x1D1, 0x1D1,
+ 0x1D3, 0x1D3,
+ 0x1D5, 0x1D5,
+ 0x1D7, 0x1D7,
+ 0x1D9, 0x1D9,
+ 0x1DB, 0x1DB,
+ 0x1DE, 0x1DE,
+ 0x1E0, 0x1E0,
+ 0x1E2, 0x1E2,
+ 0x1E4, 0x1E4,
+ 0x1E6, 0x1E6,
+ 0x1E8, 0x1E8,
+ 0x1EA, 0x1EA,
+ 0x1EC, 0x1EC,
+ 0x1EE, 0x1EE,
+ 0x1F1, 0x1F2,
+ 0x1F4, 0x1F4,
+ 0x1F6, 0x1F8,
+ 0x1FA, 0x1FA,
+ 0x1FC, 0x1FC,
+ 0x1FE, 0x1FE,
+ 0x200, 0x200,
+ 0x202, 0x202,
+ 0x204, 0x204,
+ 0x206, 0x206,
+ 0x208, 0x208,
+ 0x20A, 0x20A,
+ 0x20C, 0x20C,
+ 0x20E, 0x20E,
+ 0x210, 0x210,
+ 0x212, 0x212,
+ 0x214, 0x214,
+ 0x216, 0x216,
+ 0x218, 0x218,
+ 0x21A, 0x21A,
+ 0x21C, 0x21C,
+ 0x21E, 0x21E,
+ 0x220, 0x220,
+ 0x222, 0x222,
+ 0x224, 0x224,
+ 0x226, 0x226,
+ 0x228, 0x228,
+ 0x22A, 0x22A,
+ 0x22C, 0x22C,
+ 0x22E, 0x22E,
+ 0x230, 0x230,
+ 0x232, 0x232,
+ 0x23A, 0x23B,
+ 0x23D, 0x23E,
+ 0x241, 0x241,
+ 0x243, 0x246,
+ 0x248, 0x248,
+ 0x24A, 0x24A,
+ 0x24C, 0x24C,
+ 0x24E, 0x24E,
+ 0x370, 0x370,
+ 0x372, 0x372,
+ 0x376, 0x376,
+ 0x37F, 0x37F,
+ 0x386, 0x386,
+ 0x388, 0x38A,
+ 0x38C, 0x38C,
+ 0x38E, 0x38F,
+ 0x391, 0x3A1,
+ 0x3A3, 0x3AB,
+ 0x3CF, 0x3CF,
+ 0x3D2, 0x3D4,
+ 0x3D8, 0x3D8,
+ 0x3DA, 0x3DA,
+ 0x3DC, 0x3DC,
+ 0x3DE, 0x3DE,
+ 0x3E0, 0x3E0,
+ 0x3E2, 0x3E2,
+ 0x3E4, 0x3E4,
+ 0x3E6, 0x3E6,
+ 0x3E8, 0x3E8,
+ 0x3EA, 0x3EA,
+ 0x3EC, 0x3EC,
+ 0x3EE, 0x3EE,
+ 0x3F4, 0x3F4,
+ 0x3F7, 0x3F7,
+ 0x3F9, 0x3FA,
+ 0x3FD, 0x42F,
+ 0x460, 0x460,
+ 0x462, 0x462,
+ 0x464, 0x464,
+ 0x466, 0x466,
+ 0x468, 0x468,
+ 0x46A, 0x46A,
+ 0x46C, 0x46C,
+ 0x46E, 0x46E,
+ 0x470, 0x470,
+ 0x472, 0x472,
+ 0x474, 0x474,
+ 0x476, 0x476,
+ 0x478, 0x478,
+ 0x47A, 0x47A,
+ 0x47C, 0x47C,
+ 0x47E, 0x47E,
+ 0x480, 0x480,
+ 0x48A, 0x48A,
+ 0x48C, 0x48C,
+ 0x48E, 0x48E,
+ 0x490, 0x490,
+ 0x492, 0x492,
+ 0x494, 0x494,
+ 0x496, 0x496,
+ 0x498, 0x498,
+ 0x49A, 0x49A,
+ 0x49C, 0x49C,
+ 0x49E, 0x49E,
+ 0x4A0, 0x4A0,
+ 0x4A2, 0x4A2,
+ 0x4A4, 0x4A4,
+ 0x4A6, 0x4A6,
+ 0x4A8, 0x4A8,
+ 0x4AA, 0x4AA,
+ 0x4AC, 0x4AC,
+ 0x4AE, 0x4AE,
+ 0x4B0, 0x4B0,
+ 0x4B2, 0x4B2,
+ 0x4B4, 0x4B4,
+ 0x4B6, 0x4B6,
+ 0x4B8, 0x4B8,
+ 0x4BA, 0x4BA,
+ 0x4BC, 0x4BC,
+ 0x4BE, 0x4BE,
+ 0x4C0, 0x4C1,
+ 0x4C3, 0x4C3,
+ 0x4C5, 0x4C5,
+ 0x4C7, 0x4C7,
+ 0x4C9, 0x4C9,
+ 0x4CB, 0x4CB,
+ 0x4CD, 0x4CD,
+ 0x4D0, 0x4D0,
+ 0x4D2, 0x4D2,
+ 0x4D4, 0x4D4,
+ 0x4D6, 0x4D6,
+ 0x4D8, 0x4D8,
+ 0x4DA, 0x4DA,
+ 0x4DC, 0x4DC,
+ 0x4DE, 0x4DE,
+ 0x4E0, 0x4E0,
+ 0x4E2, 0x4E2,
+ 0x4E4, 0x4E4,
+ 0x4E6, 0x4E6,
+ 0x4E8, 0x4E8,
+ 0x4EA, 0x4EA,
+ 0x4EC, 0x4EC,
+ 0x4EE, 0x4EE,
+ 0x4F0, 0x4F0,
+ 0x4F2, 0x4F2,
+ 0x4F4, 0x4F4,
+ 0x4F6, 0x4F6,
+ 0x4F8, 0x4F8,
+ 0x4FA, 0x4FA,
+ 0x4FC, 0x4FC,
+ 0x4FE, 0x4FE,
+ 0x500, 0x500,
+ 0x502, 0x502,
+ 0x504, 0x504,
+ 0x506, 0x506,
+ 0x508, 0x508,
+ 0x50A, 0x50A,
+ 0x50C, 0x50C,
+ 0x50E, 0x50E,
+ 0x510, 0x510,
+ 0x512, 0x512,
+ 0x514, 0x514,
+ 0x516, 0x516,
+ 0x518, 0x518,
+ 0x51A, 0x51A,
+ 0x51C, 0x51C,
+ 0x51E, 0x51E,
+ 0x520, 0x520,
+ 0x522, 0x522,
+ 0x524, 0x524,
+ 0x526, 0x526,
+ 0x528, 0x528,
+ 0x52A, 0x52A,
+ 0x52C, 0x52C,
+ 0x52E, 0x52E,
+ 0x531, 0x556,
+ 0x10A0, 0x10C5,
+ 0x10C7, 0x10C7,
+ 0x10CD, 0x10CD,
+ 0x13A0, 0x13F5,
+ 0x1C90, 0x1CBA,
+ 0x1CBD, 0x1CBF,
+ 0x1E00, 0x1E00,
+ 0x1E02, 0x1E02,
+ 0x1E04, 0x1E04,
+ 0x1E06, 0x1E06,
+ 0x1E08, 0x1E08,
+ 0x1E0A, 0x1E0A,
+ 0x1E0C, 0x1E0C,
+ 0x1E0E, 0x1E0E,
+ 0x1E10, 0x1E10,
+ 0x1E12, 0x1E12,
+ 0x1E14, 0x1E14,
+ 0x1E16, 0x1E16,
+ 0x1E18, 0x1E18,
+ 0x1E1A, 0x1E1A,
+ 0x1E1C, 0x1E1C,
+ 0x1E1E, 0x1E1E,
+ 0x1E20, 0x1E20,
+ 0x1E22, 0x1E22,
+ 0x1E24, 0x1E24,
+ 0x1E26, 0x1E26,
+ 0x1E28, 0x1E28,
+ 0x1E2A, 0x1E2A,
+ 0x1E2C, 0x1E2C,
+ 0x1E2E, 0x1E2E,
+ 0x1E30, 0x1E30,
+ 0x1E32, 0x1E32,
+ 0x1E34, 0x1E34,
+ 0x1E36, 0x1E36,
+ 0x1E38, 0x1E38,
+ 0x1E3A, 0x1E3A,
+ 0x1E3C, 0x1E3C,
+ 0x1E3E, 0x1E3E,
+ 0x1E40, 0x1E40,
+ 0x1E42, 0x1E42,
+ 0x1E44, 0x1E44,
+ 0x1E46, 0x1E46,
+ 0x1E48, 0x1E48,
+ 0x1E4A, 0x1E4A,
+ 0x1E4C, 0x1E4C,
+ 0x1E4E, 0x1E4E,
+ 0x1E50, 0x1E50,
+ 0x1E52, 0x1E52,
+ 0x1E54, 0x1E54,
+ 0x1E56, 0x1E56,
+ 0x1E58, 0x1E58,
+ 0x1E5A, 0x1E5A,
+ 0x1E5C, 0x1E5C,
+ 0x1E5E, 0x1E5E,
+ 0x1E60, 0x1E60,
+ 0x1E62, 0x1E62,
+ 0x1E64, 0x1E64,
+ 0x1E66, 0x1E66,
+ 0x1E68, 0x1E68,
+ 0x1E6A, 0x1E6A,
+ 0x1E6C, 0x1E6C,
+ 0x1E6E, 0x1E6E,
+ 0x1E70, 0x1E70,
+ 0x1E72, 0x1E72,
+ 0x1E74, 0x1E74,
+ 0x1E76, 0x1E76,
+ 0x1E78, 0x1E78,
+ 0x1E7A, 0x1E7A,
+ 0x1E7C, 0x1E7C,
+ 0x1E7E, 0x1E7E,
+ 0x1E80, 0x1E80,
+ 0x1E82, 0x1E82,
+ 0x1E84, 0x1E84,
+ 0x1E86, 0x1E86,
+ 0x1E88, 0x1E88,
+ 0x1E8A, 0x1E8A,
+ 0x1E8C, 0x1E8C,
+ 0x1E8E, 0x1E8E,
+ 0x1E90, 0x1E90,
+ 0x1E92, 0x1E92,
+ 0x1E94, 0x1E94,
+ 0x1E9E, 0x1E9E,
+ 0x1EA0, 0x1EA0,
+ 0x1EA2, 0x1EA2,
+ 0x1EA4, 0x1EA4,
+ 0x1EA6, 0x1EA6,
+ 0x1EA8, 0x1EA8,
+ 0x1EAA, 0x1EAA,
+ 0x1EAC, 0x1EAC,
+ 0x1EAE, 0x1EAE,
+ 0x1EB0, 0x1EB0,
+ 0x1EB2, 0x1EB2,
+ 0x1EB4, 0x1EB4,
+ 0x1EB6, 0x1EB6,
+ 0x1EB8, 0x1EB8,
+ 0x1EBA, 0x1EBA,
+ 0x1EBC, 0x1EBC,
+ 0x1EBE, 0x1EBE,
+ 0x1EC0, 0x1EC0,
+ 0x1EC2, 0x1EC2,
+ 0x1EC4, 0x1EC4,
+ 0x1EC6, 0x1EC6,
+ 0x1EC8, 0x1EC8,
+ 0x1ECA, 0x1ECA,
+ 0x1ECC, 0x1ECC,
+ 0x1ECE, 0x1ECE,
+ 0x1ED0, 0x1ED0,
+ 0x1ED2, 0x1ED2,
+ 0x1ED4, 0x1ED4,
+ 0x1ED6, 0x1ED6,
+ 0x1ED8, 0x1ED8,
+ 0x1EDA, 0x1EDA,
+ 0x1EDC, 0x1EDC,
+ 0x1EDE, 0x1EDE,
+ 0x1EE0, 0x1EE0,
+ 0x1EE2, 0x1EE2,
+ 0x1EE4, 0x1EE4,
+ 0x1EE6, 0x1EE6,
+ 0x1EE8, 0x1EE8,
+ 0x1EEA, 0x1EEA,
+ 0x1EEC, 0x1EEC,
+ 0x1EEE, 0x1EEE,
+ 0x1EF0, 0x1EF0,
+ 0x1EF2, 0x1EF2,
+ 0x1EF4, 0x1EF4,
+ 0x1EF6, 0x1EF6,
+ 0x1EF8, 0x1EF8,
+ 0x1EFA, 0x1EFA,
+ 0x1EFC, 0x1EFC,
+ 0x1EFE, 0x1EFE,
+ 0x1F08, 0x1F0F,
+ 0x1F18, 0x1F1D,
+ 0x1F28, 0x1F2F,
+ 0x1F38, 0x1F3F,
+ 0x1F48, 0x1F4D,
+ 0x1F59, 0x1F59,
+ 0x1F5B, 0x1F5B,
+ 0x1F5D, 0x1F5D,
+ 0x1F5F, 0x1F5F,
+ 0x1F68, 0x1F6F,
+ 0x1F88, 0x1F8F,
+ 0x1F98, 0x1F9F,
+ 0x1FA8, 0x1FAF,
+ 0x1FB8, 0x1FBC,
+ 0x1FC8, 0x1FCC,
+ 0x1FD8, 0x1FDB,
+ 0x1FE8, 0x1FEC,
+ 0x1FF8, 0x1FFC,
+ 0x2102, 0x2102,
+ 0x2107, 0x2107,
+ 0x210B, 0x210D,
+ 0x2110, 0x2112,
+ 0x2115, 0x2115,
+ 0x2119, 0x211D,
+ 0x2124, 0x2124,
+ 0x2126, 0x2126,
+ 0x2128, 0x2128,
+ 0x212A, 0x212D,
+ 0x2130, 0x2133,
+ 0x213E, 0x213F,
+ 0x2145, 0x2145,
+ 0x2160, 0x216F,
+ 0x2183, 0x2183,
+ 0x24B6, 0x24CF,
+ 0x2C00, 0x2C2F,
+ 0x2C60, 0x2C60,
+ 0x2C62, 0x2C64,
+ 0x2C67, 0x2C67,
+ 0x2C69, 0x2C69,
+ 0x2C6B, 0x2C6B,
+ 0x2C6D, 0x2C70,
+ 0x2C72, 0x2C72,
+ 0x2C75, 0x2C75,
+ 0x2C7E, 0x2C80,
+ 0x2C82, 0x2C82,
+ 0x2C84, 0x2C84,
+ 0x2C86, 0x2C86,
+ 0x2C88, 0x2C88,
+ 0x2C8A, 0x2C8A,
+ 0x2C8C, 0x2C8C,
+ 0x2C8E, 0x2C8E,
+ 0x2C90, 0x2C90,
+ 0x2C92, 0x2C92,
+ 0x2C94, 0x2C94,
+ 0x2C96, 0x2C96,
+ 0x2C98, 0x2C98,
+ 0x2C9A, 0x2C9A,
+ 0x2C9C, 0x2C9C,
+ 0x2C9E, 0x2C9E,
+ 0x2CA0, 0x2CA0,
+ 0x2CA2, 0x2CA2,
+ 0x2CA4, 0x2CA4,
+ 0x2CA6, 0x2CA6,
+ 0x2CA8, 0x2CA8,
+ 0x2CAA, 0x2CAA,
+ 0x2CAC, 0x2CAC,
+ 0x2CAE, 0x2CAE,
+ 0x2CB0, 0x2CB0,
+ 0x2CB2, 0x2CB2,
+ 0x2CB4, 0x2CB4,
+ 0x2CB6, 0x2CB6,
+ 0x2CB8, 0x2CB8,
+ 0x2CBA, 0x2CBA,
+ 0x2CBC, 0x2CBC,
+ 0x2CBE, 0x2CBE,
+ 0x2CC0, 0x2CC0,
+ 0x2CC2, 0x2CC2,
+ 0x2CC4, 0x2CC4,
+ 0x2CC6, 0x2CC6,
+ 0x2CC8, 0x2CC8,
+ 0x2CCA, 0x2CCA,
+ 0x2CCC, 0x2CCC,
+ 0x2CCE, 0x2CCE,
+ 0x2CD0, 0x2CD0,
+ 0x2CD2, 0x2CD2,
+ 0x2CD4, 0x2CD4,
+ 0x2CD6, 0x2CD6,
+ 0x2CD8, 0x2CD8,
+ 0x2CDA, 0x2CDA,
+ 0x2CDC, 0x2CDC,
+ 0x2CDE, 0x2CDE,
+ 0x2CE0, 0x2CE0,
+ 0x2CE2, 0x2CE2,
+ 0x2CEB, 0x2CEB,
+ 0x2CED, 0x2CED,
+ 0x2CF2, 0x2CF2,
+ 0xA640, 0xA640,
+ 0xA642, 0xA642,
+ 0xA644, 0xA644,
+ 0xA646, 0xA646,
+ 0xA648, 0xA648,
+ 0xA64A, 0xA64A,
+ 0xA64C, 0xA64C,
+ 0xA64E, 0xA64E,
+ 0xA650, 0xA650,
+ 0xA652, 0xA652,
+ 0xA654, 0xA654,
+ 0xA656, 0xA656,
+ 0xA658, 0xA658,
+ 0xA65A, 0xA65A,
+ 0xA65C, 0xA65C,
+ 0xA65E, 0xA65E,
+ 0xA660, 0xA660,
+ 0xA662, 0xA662,
+ 0xA664, 0xA664,
+ 0xA666, 0xA666,
+ 0xA668, 0xA668,
+ 0xA66A, 0xA66A,
+ 0xA66C, 0xA66C,
+ 0xA680, 0xA680,
+ 0xA682, 0xA682,
+ 0xA684, 0xA684,
+ 0xA686, 0xA686,
+ 0xA688, 0xA688,
+ 0xA68A, 0xA68A,
+ 0xA68C, 0xA68C,
+ 0xA68E, 0xA68E,
+ 0xA690, 0xA690,
+ 0xA692, 0xA692,
+ 0xA694, 0xA694,
+ 0xA696, 0xA696,
+ 0xA698, 0xA698,
+ 0xA69A, 0xA69A,
+ 0xA722, 0xA722,
+ 0xA724, 0xA724,
+ 0xA726, 0xA726,
+ 0xA728, 0xA728,
+ 0xA72A, 0xA72A,
+ 0xA72C, 0xA72C,
+ 0xA72E, 0xA72E,
+ 0xA732, 0xA732,
+ 0xA734, 0xA734,
+ 0xA736, 0xA736,
+ 0xA738, 0xA738,
+ 0xA73A, 0xA73A,
+ 0xA73C, 0xA73C,
+ 0xA73E, 0xA73E,
+ 0xA740, 0xA740,
+ 0xA742, 0xA742,
+ 0xA744, 0xA744,
+ 0xA746, 0xA746,
+ 0xA748, 0xA748,
+ 0xA74A, 0xA74A,
+ 0xA74C, 0xA74C,
+ 0xA74E, 0xA74E,
+ 0xA750, 0xA750,
+ 0xA752, 0xA752,
+ 0xA754, 0xA754,
+ 0xA756, 0xA756,
+ 0xA758, 0xA758,
+ 0xA75A, 0xA75A,
+ 0xA75C, 0xA75C,
+ 0xA75E, 0xA75E,
+ 0xA760, 0xA760,
+ 0xA762, 0xA762,
+ 0xA764, 0xA764,
+ 0xA766, 0xA766,
+ 0xA768, 0xA768,
+ 0xA76A, 0xA76A,
+ 0xA76C, 0xA76C,
+ 0xA76E, 0xA76E,
+ 0xA779, 0xA779,
+ 0xA77B, 0xA77B,
+ 0xA77D, 0xA77E,
+ 0xA780, 0xA780,
+ 0xA782, 0xA782,
+ 0xA784, 0xA784,
+ 0xA786, 0xA786,
+ 0xA78B, 0xA78B,
+ 0xA78D, 0xA78D,
+ 0xA790, 0xA790,
+ 0xA792, 0xA792,
+ 0xA796, 0xA796,
+ 0xA798, 0xA798,
+ 0xA79A, 0xA79A,
+ 0xA79C, 0xA79C,
+ 0xA79E, 0xA79E,
+ 0xA7A0, 0xA7A0,
+ 0xA7A2, 0xA7A2,
+ 0xA7A4, 0xA7A4,
+ 0xA7A6, 0xA7A6,
+ 0xA7A8, 0xA7A8,
+ 0xA7AA, 0xA7AE,
+ 0xA7B0, 0xA7B4,
+ 0xA7B6, 0xA7B6,
+ 0xA7B8, 0xA7B8,
+ 0xA7BA, 0xA7BA,
+ 0xA7BC, 0xA7BC,
+ 0xA7BE, 0xA7BE,
+ 0xA7C0, 0xA7C0,
+ 0xA7C2, 0xA7C2,
+ 0xA7C4, 0xA7C7,
+ 0xA7C9, 0xA7C9,
+ 0xA7D0, 0xA7D0,
+ 0xA7D6, 0xA7D6,
+ 0xA7D8, 0xA7D8,
+ 0xA7F5, 0xA7F5,
+ 0xFF21, 0xFF3A,
+ 0x10400, 0x10427,
+ 0x104B0, 0x104D3,
+ 0x10570, 0x1057A,
+ 0x1057C, 0x1058A,
+ 0x1058C, 0x10592,
+ 0x10594, 0x10595,
+ 0x10C80, 0x10CB2,
+ 0x118A0, 0x118BF,
+ 0x16E40, 0x16E5F,
+ 0x1D400, 0x1D419,
+ 0x1D434, 0x1D44D,
+ 0x1D468, 0x1D481,
+ 0x1D49C, 0x1D49C,
+ 0x1D49E, 0x1D49F,
+ 0x1D4A2, 0x1D4A2,
+ 0x1D4A5, 0x1D4A6,
+ 0x1D4A9, 0x1D4AC,
+ 0x1D4AE, 0x1D4B5,
+ 0x1D4D0, 0x1D4E9,
+ 0x1D504, 0x1D505,
+ 0x1D507, 0x1D50A,
+ 0x1D50D, 0x1D514,
+ 0x1D516, 0x1D51C,
+ 0x1D538, 0x1D539,
+ 0x1D53B, 0x1D53E,
+ 0x1D540, 0x1D544,
+ 0x1D546, 0x1D546,
+ 0x1D54A, 0x1D550,
+ 0x1D56C, 0x1D585,
+ 0x1D5A0, 0x1D5B9,
+ 0x1D5D4, 0x1D5ED,
+ 0x1D608, 0x1D621,
+ 0x1D63C, 0x1D655,
+ 0x1D670, 0x1D689,
+ 0x1D6A8, 0x1D6C0,
+ 0x1D6E2, 0x1D6FA,
+ 0x1D71C, 0x1D734,
+ 0x1D756, 0x1D76E,
+ 0x1D790, 0x1D7A8,
+ 0x1D7CA, 0x1D7CA,
+ 0x1E900, 0x1E921,
+ 0x1F130, 0x1F149,
+ 0x1F150, 0x1F169,
+ 0x1F170, 0x1F189,
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding unicode codepoint. Note that
+ * this table is different from other encodings where we used a lookup table
+ * because the indices of those tables are the byte representations, not the
+ * codepoints themselves.
+ */
+const uint8_t pm_encoding_unicode_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Bx
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx
+ 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, // Fx
+};
+
+/**
+ * Binary search through the given list of codepoints to see if the given
+ * codepoint is in the list.
+ */
+static bool
+pm_unicode_codepoint_match(pm_unicode_codepoint_t codepoint, const pm_unicode_codepoint_t *codepoints, size_t size) {
+ size_t start = 0;
+ size_t end = size;
+
+ while (start < end) {
+ size_t middle = start + (end - start) / 2;
+ if ((middle % 2) != 0) middle--;
+
+ if (codepoint >= codepoints[middle] && codepoint <= codepoints[middle + 1]) {
+ return true;
+ }
+
+ if (codepoint < codepoints[middle]) {
+ end = middle;
+ } else {
+ start = middle + 2;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * A state transition table for decoding UTF-8.
+ *
+ * Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
+ *
+ * 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.
+ */
+static const uint8_t pm_utf_8_dfa[] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
+ 8,8,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, // c0..df
+ 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
+ 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
+ 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2
+ 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4
+ 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6
+ 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8
+};
+
+/**
+ * Given a pointer to a string and the number of bytes remaining in the string,
+ * decode the next UTF-8 codepoint and return it. The number of bytes consumed
+ * is returned in the width out parameter.
+ */
+static pm_unicode_codepoint_t
+pm_utf_8_codepoint(const uint8_t *b, ptrdiff_t n, size_t *width) {
+ assert(n >= 0);
+
+ size_t maximum = (n > 4) ? 4 : ((size_t) n);
+ uint32_t codepoint;
+ uint32_t state = 0;
+
+ for (size_t index = 0; index < maximum; index++) {
+ uint32_t byte = b[index];
+ uint32_t type = pm_utf_8_dfa[byte];
+
+ codepoint = (state != 0) ?
+ (byte & 0x3fu) | (codepoint << 6) :
+ (0xffu >> type) & (byte);
+
+ state = pm_utf_8_dfa[256 + (state * 16) + type];
+ if (state == 0) {
+ *width = index + 1;
+ return (pm_unicode_codepoint_t) codepoint;
+ }
+ }
+
+ *width = 0;
+ return 0;
+}
+
+/**
+ * Return the size of the next character in the UTF-8 encoding.
+ */
+size_t
+pm_encoding_utf_8_char_width(const uint8_t *b, ptrdiff_t n) {
+ assert(n >= 0);
+
+ size_t maximum = (n > 4) ? 4 : ((size_t) n);
+ uint32_t state = 0;
+
+ for (size_t index = 0; index < maximum; index++) {
+ state = pm_utf_8_dfa[256 + (state * 16) + pm_utf_8_dfa[b[index]]];
+ if (state == 0) return index + 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Return the size of the next character in the UTF-8 encoding if it is an
+ * alphabetical character.
+ */
+size_t
+pm_encoding_utf_8_alpha_char(const uint8_t *b, ptrdiff_t n) {
+ if (*b < 0x80) {
+ return (pm_encoding_unicode_table[*b] & PRISM_ENCODING_ALPHABETIC_BIT) ? 1 : 0;
+ }
+
+ size_t width;
+ pm_unicode_codepoint_t codepoint = pm_utf_8_codepoint(b, n, &width);
+
+ if (codepoint <= 0xFF) {
+ return (pm_encoding_unicode_table[(uint8_t) codepoint] & PRISM_ENCODING_ALPHABETIC_BIT) ? width : 0;
+ } else {
+ return pm_unicode_codepoint_match(codepoint, unicode_alpha_codepoints, UNICODE_ALPHA_CODEPOINTS_LENGTH) ? width : 0;
+ }
+}
+
+/**
+ * Return the size of the next character in the UTF-8 encoding if it is an
+ * alphanumeric character.
+ */
+size_t
+pm_encoding_utf_8_alnum_char(const uint8_t *b, ptrdiff_t n) {
+ if (*b < 0x80) {
+ return (pm_encoding_unicode_table[*b] & (PRISM_ENCODING_ALPHANUMERIC_BIT)) ? 1 : 0;
+ }
+
+ size_t width;
+ pm_unicode_codepoint_t codepoint = pm_utf_8_codepoint(b, n, &width);
+
+ if (codepoint <= 0xFF) {
+ return (pm_encoding_unicode_table[(uint8_t) codepoint] & (PRISM_ENCODING_ALPHANUMERIC_BIT)) ? width : 0;
+ } else {
+ return pm_unicode_codepoint_match(codepoint, unicode_alnum_codepoints, UNICODE_ALNUM_CODEPOINTS_LENGTH) ? width : 0;
+ }
+}
+
+/**
+ * Return true if the next character in the UTF-8 encoding if it is an uppercase
+ * character.
+ */
+bool
+pm_encoding_utf_8_isupper_char(const uint8_t *b, ptrdiff_t n) {
+ if (*b < 0x80) {
+ return (pm_encoding_unicode_table[*b] & PRISM_ENCODING_UPPERCASE_BIT) ? true : false;
+ }
+
+ size_t width;
+ pm_unicode_codepoint_t codepoint = pm_utf_8_codepoint(b, n, &width);
+
+ if (codepoint <= 0xFF) {
+ return (pm_encoding_unicode_table[(uint8_t) codepoint] & PRISM_ENCODING_UPPERCASE_BIT) ? true : false;
+ } else {
+ return pm_unicode_codepoint_match(codepoint, unicode_isupper_codepoints, UNICODE_ISUPPER_CODEPOINTS_LENGTH) ? true : false;
+ }
+}
+
+#ifndef PRISM_ENCODING_EXCLUDE_FULL
+
+static pm_unicode_codepoint_t
+pm_cesu_8_codepoint(const uint8_t *b, ptrdiff_t n, size_t *width) {
+ if (b[0] < 0x80) {
+ *width = 1;
+ return (pm_unicode_codepoint_t) b[0];
+ }
+
+ if (n > 1 && b[0] >= 0xC2 && b[0] <= 0xDF && b[1] >= 0x80 && b[1] <= 0xBF) {
+ *width = 2;
+
+ // 110xxxxx 10xxxxxx
+ return (pm_unicode_codepoint_t) (((b[0] & 0x1F) << 6) | (b[1] & 0x3F));
+ }
+
+ if (n > 5 && b[0] == 0xED && b[1] >= 0xA0 && b[1] <= 0xAF && b[2] >= 0x80 && b[2] <= 0xBF && b[3] == 0xED && b[4] >= 0xB0 && b[4] <= 0xBF && b[5] >= 0x80 && b[5] <= 0xBF) {
+ *width = 6;
+
+ // 11101101 1010xxxx 10xxxxxx 11101101 1011xxxx 10xxxxxx
+ return (pm_unicode_codepoint_t) (0x10000 + (((b[1] & 0xF) << 16) | ((b[2] & 0x3F) << 10) | ((b[4] & 0xF) << 6) | (b[5] & 0x3F)));
+ }
+
+ if (n > 2 && b[0] == 0xED && b[1] >= 0xA0 && b[1] <= 0xBF) {
+ *width = 3;
+
+ // 11101101 1010xxxx 10xxxxx
+ return (pm_unicode_codepoint_t) (0x10000 + (((b[0] & 0x03) << 16) | ((b[1] & 0x3F) << 10) | (b[2] & 0x3F)));
+ }
+
+ if (n > 2 && ((b[0] == 0xE0 && b[1] >= 0xA0) || (b[0] >= 0xE1 && b[0] <= 0xEF && b[1] >= 0x80)) && b[1] <= 0xBF && b[2] >= 0x80 && b[2] <= 0xBF) {
+ *width = 3;
+
+ // 1110xxxx 10xxxxxx 10xxxxx
+ return (pm_unicode_codepoint_t) (((b[0] & 0xF) << 12) | ((b[1] & 0x3F) << 6) | (b[2] & 0x3F));
+ }
+
+ *width = 0;
+ return 0;
+}
+
+static size_t
+pm_encoding_cesu_8_char_width(const uint8_t *b, ptrdiff_t n) {
+ size_t width;
+ pm_cesu_8_codepoint(b, n, &width);
+ return width;
+}
+
+static size_t
+pm_encoding_cesu_8_alpha_char(const uint8_t *b, ptrdiff_t n) {
+ if (*b < 0x80) {
+ return (pm_encoding_unicode_table[*b] & PRISM_ENCODING_ALPHABETIC_BIT) ? 1 : 0;
+ }
+
+ size_t width;
+ pm_unicode_codepoint_t codepoint = pm_cesu_8_codepoint(b, n, &width);
+
+ if (codepoint <= 0xFF) {
+ return (pm_encoding_unicode_table[(uint8_t) codepoint] & PRISM_ENCODING_ALPHABETIC_BIT) ? width : 0;
+ } else {
+ return pm_unicode_codepoint_match(codepoint, unicode_alpha_codepoints, UNICODE_ALPHA_CODEPOINTS_LENGTH) ? width : 0;
+ }
+}
+
+static size_t
+pm_encoding_cesu_8_alnum_char(const uint8_t *b, ptrdiff_t n) {
+ if (*b < 0x80) {
+ return (pm_encoding_unicode_table[*b] & (PRISM_ENCODING_ALPHANUMERIC_BIT)) ? 1 : 0;
+ }
+
+ size_t width;
+ pm_unicode_codepoint_t codepoint = pm_cesu_8_codepoint(b, n, &width);
+
+ if (codepoint <= 0xFF) {
+ return (pm_encoding_unicode_table[(uint8_t) codepoint] & (PRISM_ENCODING_ALPHANUMERIC_BIT)) ? width : 0;
+ } else {
+ return pm_unicode_codepoint_match(codepoint, unicode_alnum_codepoints, UNICODE_ALNUM_CODEPOINTS_LENGTH) ? width : 0;
+ }
+}
+
+static bool
+pm_encoding_cesu_8_isupper_char(const uint8_t *b, ptrdiff_t n) {
+ if (*b < 0x80) {
+ return (pm_encoding_unicode_table[*b] & PRISM_ENCODING_UPPERCASE_BIT) ? true : false;
+ }
+
+ size_t width;
+ pm_unicode_codepoint_t codepoint = pm_cesu_8_codepoint(b, n, &width);
+
+ if (codepoint <= 0xFF) {
+ return (pm_encoding_unicode_table[(uint8_t) codepoint] & PRISM_ENCODING_UPPERCASE_BIT) ? true : false;
+ } else {
+ return pm_unicode_codepoint_match(codepoint, unicode_isupper_codepoints, UNICODE_ISUPPER_CODEPOINTS_LENGTH) ? true : false;
+ }
+}
+
+#endif
+
+#undef UNICODE_ALPHA_CODEPOINTS_LENGTH
+#undef UNICODE_ALNUM_CODEPOINTS_LENGTH
+#undef UNICODE_ISUPPER_CODEPOINTS_LENGTH
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding US-ASCII character.
+ */
+static const uint8_t pm_encoding_ascii_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+#ifndef PRISM_ENCODING_EXCLUDE_FULL
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding CP850 character.
+ */
+static const uint8_t pm_encoding_cp850_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding CP852 character.
+ */
+static const uint8_t pm_encoding_cp852_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding CP855 character.
+ */
+static const uint8_t pm_encoding_cp855_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding GB1988 character.
+ */
+static const uint8_t pm_encoding_gb1988_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding IBM437 character.
+ */
+static const uint8_t pm_encoding_ibm437_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding IBM720 character.
+ */
+static const uint8_t pm_encoding_ibm720_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding IBM737 character.
+ */
+static const uint8_t pm_encoding_ibm737_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding IBM775 character.
+ */
+static const uint8_t pm_encoding_ibm775_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding IBM852 character.
+ */
+static const uint8_t pm_encoding_ibm852_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding IBM855 character.
+ */
+static const uint8_t pm_encoding_ibm855_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding IBM857 character.
+ */
+static const uint8_t pm_encoding_ibm857_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding IBM860 character.
+ */
+static const uint8_t pm_encoding_ibm860_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding IBM861 character.
+ */
+static const uint8_t pm_encoding_ibm861_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding IBM862 character.
+ */
+static const uint8_t pm_encoding_ibm862_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding IBM863 character.
+ */
+static const uint8_t pm_encoding_ibm863_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding IBM864 character.
+ */
+static const uint8_t pm_encoding_ibm864_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding IBM865 character.
+ */
+static const uint8_t pm_encoding_ibm865_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding IBM866 character.
+ */
+static const uint8_t pm_encoding_ibm866_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding IBM869 character.
+ */
+static const uint8_t pm_encoding_ibm869_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding ISO-8859-1 character.
+ */
+static const uint8_t pm_encoding_iso_8859_1_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Bx
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx
+ 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding ISO-8859-2 character.
+ */
+static const uint8_t pm_encoding_iso_8859_2_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 7, 0, 7, 0, 7, 7, 0, 0, 7, 7, 7, 7, 0, 7, 7, // Ax
+ 0, 3, 0, 3, 0, 3, 3, 0, 0, 3, 3, 3, 3, 0, 3, 3, // Bx
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx
+ 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding ISO-8859-3 character.
+ */
+static const uint8_t pm_encoding_iso_8859_3_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 7, 0, 0, 0, 0, 7, 0, 0, 7, 7, 7, 7, 0, 0, 7, // Ax
+ 0, 3, 0, 0, 0, 3, 3, 0, 0, 3, 3, 3, 3, 0, 0, 3, // Bx
+ 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx
+ 0, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding ISO-8859-4 character.
+ */
+static const uint8_t pm_encoding_iso_8859_4_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 7, 3, 7, 0, 7, 7, 0, 0, 7, 7, 7, 7, 0, 7, 0, // Ax
+ 0, 3, 0, 3, 0, 3, 3, 0, 0, 3, 3, 3, 3, 7, 3, 3, // Bx
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx
+ 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding ISO-8859-5 character.
+ */
+static const uint8_t pm_encoding_iso_8859_5_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, // Ax
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Bx
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Dx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding ISO-8859-6 character.
+ */
+static const uint8_t pm_encoding_iso_8859_6_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Cx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // Dx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding ISO-8859-7 character.
+ */
+static const uint8_t pm_encoding_iso_8859_7_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 7, 0, 7, 7, 7, 0, 7, 0, 7, 7, // Bx
+ 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx
+ 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, 3, 3, 3, // Dx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding ISO-8859-8 character.
+ */
+static const uint8_t pm_encoding_iso_8859_8_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding ISO-8859-9 character.
+ */
+static const uint8_t pm_encoding_iso_8859_9_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Bx
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx
+ 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding ISO-8859-10 character.
+ */
+static const uint8_t pm_encoding_iso_8859_10_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 0, 7, 7, // Ax
+ 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 0, 3, 3, // Bx
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, // Dx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding ISO-8859-11 character.
+ */
+static const uint8_t pm_encoding_iso_8859_11_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ax
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Bx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Cx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 3, // Dx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding ISO-8859-13 character.
+ */
+static const uint8_t pm_encoding_iso_8859_13_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 7, 0, 0, 0, 0, 7, // Ax
+ 0, 0, 0, 0, 0, 3, 0, 0, 3, 0, 3, 0, 0, 0, 0, 3, // Bx
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx
+ 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding ISO-8859-14 character.
+ */
+static const uint8_t pm_encoding_iso_8859_14_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 7, 3, 0, 7, 3, 7, 0, 7, 0, 7, 3, 7, 0, 0, 7, // Ax
+ 7, 3, 7, 3, 7, 3, 0, 7, 3, 3, 3, 7, 3, 7, 3, 3, // Bx
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, // Dx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding ISO-8859-15 character.
+ */
+static const uint8_t pm_encoding_iso_8859_15_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 7, 0, 3, 0, 3, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 7, 3, 0, 0, 3, 0, 3, 0, 7, 3, 7, 0, // Bx
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx
+ 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding ISO-8859-16 character.
+ */
+static const uint8_t pm_encoding_iso_8859_16_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 7, 3, 7, 0, 0, 7, 0, 3, 0, 7, 0, 7, 0, 3, 7, // Ax
+ 0, 0, 7, 3, 7, 0, 0, 0, 3, 3, 3, 0, 7, 3, 7, 3, // Bx
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, // Dx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding KOI8-R character.
+ */
+static const uint8_t pm_encoding_koi8_r_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Cx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Dx
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Ex
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding KOI8-U character.
+ */
+static const uint8_t pm_encoding_koi8_u_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 3, 3, 0, 3, 3, 0, 0, 0, 0, 0, 3, 0, 0, // Ax
+ 0, 0, 0, 7, 7, 0, 7, 7, 0, 0, 0, 0, 0, 7, 0, 0, // Bx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Cx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Dx
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Ex
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding macCentEuro character.
+ */
+static const uint8_t pm_encoding_mac_cent_euro_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding macCroatian character.
+ */
+static const uint8_t pm_encoding_mac_croatian_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+ /**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding macCyrillic character.
+ */
+static const uint8_t pm_encoding_mac_cyrillic_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding macGreek character.
+ */
+static const uint8_t pm_encoding_mac_greek_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding macIceland character.
+ */
+static const uint8_t pm_encoding_mac_iceland_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding macRoman character.
+ */
+static const uint8_t pm_encoding_mac_roman_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding macRomania character.
+ */
+static const uint8_t pm_encoding_mac_romania_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding macThai character.
+ */
+static const uint8_t pm_encoding_mac_thai_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding TIS-620 character.
+ */
+static const uint8_t pm_encoding_tis_620_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ax
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Bx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Cx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 3, // Dx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding macTurkish character.
+ */
+static const uint8_t pm_encoding_mac_turkish_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding macUkraine character.
+ */
+static const uint8_t pm_encoding_mac_ukraine_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding windows-1250 character.
+ */
+static const uint8_t pm_encoding_windows_1250_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 7, 7, 7, 7, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 3, 3, 3, // 9x
+ 0, 0, 0, 7, 0, 7, 0, 0, 0, 0, 7, 0, 0, 0, 0, 7, // Ax
+ 0, 0, 0, 3, 0, 3, 0, 0, 0, 3, 3, 0, 7, 0, 3, 3, // Bx
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, // Dx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding windows-1251 character.
+ */
+static const uint8_t pm_encoding_windows_1251_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 7, 7, 0, 3, 0, 0, 0, 0, 0, 0, 7, 0, 7, 7, 7, 7, // 8x
+ 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 3, 3, 3, // 9x
+ 0, 7, 3, 7, 0, 7, 0, 0, 7, 0, 7, 0, 0, 0, 0, 7, // Ax
+ 0, 0, 7, 3, 3, 3, 0, 0, 3, 0, 3, 0, 3, 7, 3, 3, // Bx
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Dx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding windows-1252 character.
+ */
+static const uint8_t pm_encoding_windows_1252_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 7, 0, 7, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 3, 7, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Bx
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, // Dx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding windows-1253 character.
+ */
+static const uint8_t pm_encoding_windows_1253_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 3, 7, 0, 7, 7, 7, 0, 7, 0, 7, 7, // Bx
+ 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx
+ 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, 3, 3, 3, // Dx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding windows-1254 character.
+ */
+static const uint8_t pm_encoding_windows_1254_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 7, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 0, 7, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Bx
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx
+ 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding windows-1255 character.
+ */
+static const uint8_t pm_encoding_windows_1255_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding windows-1256 character.
+ */
+static const uint8_t pm_encoding_windows_1256_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Cx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // Dx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding windows-1257 character.
+ */
+static const uint8_t pm_encoding_windows_1257_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 7, 0, 0, 0, 0, 7, // Ax
+ 0, 0, 0, 0, 0, 3, 0, 0, 3, 0, 3, 0, 0, 0, 0, 3, // Bx
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx
+ 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex
+ 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding windows-1258 character.
+ */
+static const uint8_t pm_encoding_windows_1258_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+/**
+ * Each element of the following table contains a bitfield that indicates a
+ * piece of information about the corresponding windows-874 character.
+ */
+static const uint8_t pm_encoding_windows_874_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+#define PRISM_ENCODING_TABLE(name) \
+ static size_t pm_encoding_ ##name ## _alpha_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n) { \
+ return (pm_encoding_ ##name ## _table[*b] & PRISM_ENCODING_ALPHABETIC_BIT); \
+ } \
+ static size_t pm_encoding_ ##name ## _alnum_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n) { \
+ return (pm_encoding_ ##name ## _table[*b] & PRISM_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; \
+ } \
+ static bool pm_encoding_ ##name ## _isupper_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n) { \
+ return (pm_encoding_ ##name ## _table[*b] & PRISM_ENCODING_UPPERCASE_BIT); \
+ }
+
+PRISM_ENCODING_TABLE(cp850)
+PRISM_ENCODING_TABLE(cp852)
+PRISM_ENCODING_TABLE(cp855)
+PRISM_ENCODING_TABLE(gb1988)
+PRISM_ENCODING_TABLE(ibm437)
+PRISM_ENCODING_TABLE(ibm720)
+PRISM_ENCODING_TABLE(ibm737)
+PRISM_ENCODING_TABLE(ibm775)
+PRISM_ENCODING_TABLE(ibm852)
+PRISM_ENCODING_TABLE(ibm855)
+PRISM_ENCODING_TABLE(ibm857)
+PRISM_ENCODING_TABLE(ibm860)
+PRISM_ENCODING_TABLE(ibm861)
+PRISM_ENCODING_TABLE(ibm862)
+PRISM_ENCODING_TABLE(ibm863)
+PRISM_ENCODING_TABLE(ibm864)
+PRISM_ENCODING_TABLE(ibm865)
+PRISM_ENCODING_TABLE(ibm866)
+PRISM_ENCODING_TABLE(ibm869)
+PRISM_ENCODING_TABLE(iso_8859_1)
+PRISM_ENCODING_TABLE(iso_8859_2)
+PRISM_ENCODING_TABLE(iso_8859_3)
+PRISM_ENCODING_TABLE(iso_8859_4)
+PRISM_ENCODING_TABLE(iso_8859_5)
+PRISM_ENCODING_TABLE(iso_8859_6)
+PRISM_ENCODING_TABLE(iso_8859_7)
+PRISM_ENCODING_TABLE(iso_8859_8)
+PRISM_ENCODING_TABLE(iso_8859_9)
+PRISM_ENCODING_TABLE(iso_8859_10)
+PRISM_ENCODING_TABLE(iso_8859_11)
+PRISM_ENCODING_TABLE(iso_8859_13)
+PRISM_ENCODING_TABLE(iso_8859_14)
+PRISM_ENCODING_TABLE(iso_8859_15)
+PRISM_ENCODING_TABLE(iso_8859_16)
+PRISM_ENCODING_TABLE(koi8_r)
+PRISM_ENCODING_TABLE(koi8_u)
+PRISM_ENCODING_TABLE(mac_cent_euro)
+PRISM_ENCODING_TABLE(mac_croatian)
+PRISM_ENCODING_TABLE(mac_cyrillic)
+PRISM_ENCODING_TABLE(mac_greek)
+PRISM_ENCODING_TABLE(mac_iceland)
+PRISM_ENCODING_TABLE(mac_roman)
+PRISM_ENCODING_TABLE(mac_romania)
+PRISM_ENCODING_TABLE(mac_thai)
+PRISM_ENCODING_TABLE(mac_turkish)
+PRISM_ENCODING_TABLE(mac_ukraine)
+PRISM_ENCODING_TABLE(tis_620)
+PRISM_ENCODING_TABLE(windows_1250)
+PRISM_ENCODING_TABLE(windows_1251)
+PRISM_ENCODING_TABLE(windows_1252)
+PRISM_ENCODING_TABLE(windows_1253)
+PRISM_ENCODING_TABLE(windows_1254)
+PRISM_ENCODING_TABLE(windows_1255)
+PRISM_ENCODING_TABLE(windows_1256)
+PRISM_ENCODING_TABLE(windows_1257)
+PRISM_ENCODING_TABLE(windows_1258)
+PRISM_ENCODING_TABLE(windows_874)
+
+#undef PRISM_ENCODING_TABLE
+#endif
+
+/**
+ * Returns the size of the next character in the ASCII encoding. This basically
+ * means that if the top bit is not set, the character is 1 byte long.
+ */
+static size_t
+pm_encoding_ascii_char_width(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n) {
+ return *b < 0x80 ? 1 : 0;
+}
+
+/**
+ * Return the size of the next character in the ASCII encoding if it is an
+ * alphabetical character.
+ */
+static size_t
+pm_encoding_ascii_alpha_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n) {
+ return (pm_encoding_ascii_table[*b] & PRISM_ENCODING_ALPHABETIC_BIT);
+}
+
+/**
+ * Certain encodings are equivalent to ASCII below 0x80, so it works for our
+ * purposes to have a function here that first checks the bounds and then falls
+ * back to checking the ASCII lookup table.
+ */
+static size_t
+pm_encoding_ascii_alpha_char_7bit(const uint8_t *b, ptrdiff_t n) {
+ return (*b < 0x80) ? pm_encoding_ascii_alpha_char(b, n) : 0;
+}
+
+/**
+ * Return the size of the next character in the ASCII encoding if it is an
+ * alphanumeric character.
+ */
+static size_t
+pm_encoding_ascii_alnum_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n) {
+ return (pm_encoding_ascii_table[*b] & PRISM_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0;
+}
+
+/**
+ * Certain encodings are equivalent to ASCII below 0x80, so it works for our
+ * purposes to have a function here that first checks the bounds and then falls
+ * back to checking the ASCII lookup table.
+ */
+static size_t
+pm_encoding_ascii_alnum_char_7bit(const uint8_t *b, ptrdiff_t n) {
+ return (*b < 0x80) ? pm_encoding_ascii_alnum_char(b, n) : 0;
+}
+
+/**
+ * Return true if the next character in the ASCII encoding if it is an uppercase
+ * character.
+ */
+static bool
+pm_encoding_ascii_isupper_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n) {
+ return (pm_encoding_ascii_table[*b] & PRISM_ENCODING_UPPERCASE_BIT);
+}
+
+/**
+ * For a lot of encodings the default is that they are a single byte long no
+ * matter what the codepoint, so this function is shared between them.
+ */
+static size_t
+pm_encoding_single_char_width(PRISM_ATTRIBUTE_UNUSED const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n) {
+ return 1;
+}
+
+/**
+ * Returns the size of the next character in the EUC-JP encoding, or 0 if a
+ * character cannot be decoded from the given bytes.
+ */
+static size_t
+pm_encoding_euc_jp_char_width(const uint8_t *b, ptrdiff_t n) {
+ // These are the single byte characters.
+ if (*b < 0x80) {
+ return 1;
+ }
+
+ // These are the double byte characters.
+ if ((n > 1) && ((b[0] == 0x8E) || (b[0] >= 0xA1 && b[0] <= 0xFE)) && (b[1] >= 0xA1 && b[1] <= 0xFE)) {
+ return 2;
+ }
+
+ // These are the triple byte characters.
+ if ((n > 2) && (b[0] == 0x8F) && (b[1] >= 0xA1 && b[2] <= 0xFE) && (b[2] >= 0xA1 && b[2] <= 0xFE)) {
+ return 3;
+ }
+
+ return 0;
+}
+
+/**
+ * Returns the size of the next character in the EUC-JP encoding if it is an
+ * uppercase character.
+ */
+static bool
+pm_encoding_euc_jp_isupper_char(const uint8_t *b, ptrdiff_t n) {
+ size_t width = pm_encoding_euc_jp_char_width(b, n);
+
+ if (width == 1) {
+ return pm_encoding_ascii_isupper_char(b, n);
+ } else if (width == 2) {
+ return (
+ (b[0] == 0xA3 && b[1] >= 0xC1 && b[1] <= 0xDA) ||
+ (b[0] == 0xA6 && b[1] >= 0xA1 && b[1] <= 0xB8) ||
+ (b[0] == 0xA7 && b[1] >= 0xA1 && b[1] <= 0xC1)
+ );
+ } else {
+ return false;
+ }
+}
+
+/**
+ * Returns the size of the next character in the Shift_JIS encoding, or 0 if a
+ * character cannot be decoded from the given bytes.
+ */
+static size_t
+pm_encoding_shift_jis_char_width(const uint8_t *b, ptrdiff_t n) {
+ // These are the single byte characters.
+ if (b[0] < 0x80 || (b[0] >= 0xA1 && b[0] <= 0xDF)) {
+ return 1;
+ }
+
+ // These are the double byte characters.
+ if ((n > 1) && ((b[0] >= 0x81 && b[0] <= 0x9F) || (b[0] >= 0xE0 && b[0] <= 0xFC)) && (b[1] >= 0x40 && b[1] <= 0xFC && b[1] != 0x7F)) {
+ return 2;
+ }
+
+ return 0;
+}
+
+/**
+ * Returns the size of the next character in the Shift_JIS encoding if it is an
+ * alphanumeric character.
+ */
+static size_t
+pm_encoding_shift_jis_alnum_char(const uint8_t *b, ptrdiff_t n) {
+ size_t width = pm_encoding_shift_jis_char_width(b, n);
+ return width == 1 ? ((b[0] >= 0x80) || pm_encoding_ascii_alnum_char(b, n)) : width;
+}
+
+/**
+ * Returns the size of the next character in the Shift_JIS encoding if it is an
+ * alphabetical character.
+ */
+static size_t
+pm_encoding_shift_jis_alpha_char(const uint8_t *b, ptrdiff_t n) {
+ size_t width = pm_encoding_shift_jis_char_width(b, n);
+ return width == 1 ? ((b[0] >= 0x80) || pm_encoding_ascii_alpha_char(b, n)) : width;
+}
+
+/**
+ * Returns the size of the next character in the Shift_JIS encoding if it is an
+ * uppercase character.
+ */
+static bool
+pm_encoding_shift_jis_isupper_char(const uint8_t *b, ptrdiff_t n) {
+ size_t width = pm_encoding_shift_jis_char_width(b, n);
+
+ if (width == 1) {
+ return pm_encoding_ascii_isupper_char(b, n);
+ } else if (width == 2) {
+ return (
+ ((b[0] == 0x82) && (b[1] >= 0x60 && b[1] <= 0x79)) ||
+ ((b[0] == 0x83) && (b[1] >= 0x9F && b[1] <= 0xB6)) ||
+ ((b[0] == 0x84) && (b[1] >= 0x40 && b[1] <= 0x60))
+ );
+ } else {
+ return width;
+ }
+}
+
+#ifndef PRISM_ENCODING_EXCLUDE_FULL
+
+/**
+ * Certain encodings are equivalent to ASCII below 0x80, so it works for our
+ * purposes to have a function here that first checks the bounds and then falls
+ * back to checking the ASCII lookup table.
+ */
+static bool
+pm_encoding_ascii_isupper_char_7bit(const uint8_t *b, ptrdiff_t n) {
+ return (*b < 0x80) && pm_encoding_ascii_isupper_char(b, n);
+}
+
+/**
+ * Returns the size of the next character in the Big5 encoding, or 0 if a
+ * character cannot be decoded from the given bytes.
+ */
+static size_t
+pm_encoding_big5_char_width(const uint8_t *b, ptrdiff_t n) {
+ // These are the single byte characters.
+ if (*b < 0x80) {
+ return 1;
+ }
+
+ // These are the double byte characters.
+ if ((n > 1) && (b[0] >= 0xA1 && b[0] <= 0xFE) && ((b[1] >= 0x40 && b[1] <= 0x7E) || (b[1] >= 0xA1 && b[1] <= 0xFE))) {
+ return 2;
+ }
+
+ return 0;
+}
+
+/**
+ * Returns the size of the next character in the CP949 encoding, or 0 if a
+ * character cannot be decoded from the given bytes.
+ */
+static size_t
+pm_encoding_cp949_char_width(const uint8_t *b, ptrdiff_t n) {
+ // These are the single byte characters
+ if (*b <= 0x80) {
+ return 1;
+ }
+
+ // These are the double byte characters
+ if ((n > 1) && (b[0] >= 0x81 && b[0] <= 0xFE) && ((b[1] >= 0x41 && b[1] <= 0x5A) || (b[1] >= 0x61 && b[1] <= 0x7A) || (b[1] >= 0x81 && b[1] <= 0xFE))) {
+ return 2;
+ }
+
+ return 0;
+}
+
+/**
+ * Returns the size of the next character in the Emacs MULE encoding, or 0 if a
+ * character cannot be decoded from the given bytes.
+ */
+static size_t
+pm_encoding_emacs_mule_char_width(const uint8_t *b, ptrdiff_t n) {
+ // These are the 1 byte characters.
+ if (*b < 0x80) {
+ return 1;
+ }
+
+ // These are the 2 byte characters.
+ if ((n > 1) && (b[0] >= 0x81 && b[0] <= 0x8F) && (b[1] >= 0xA0)) {
+ return 2;
+ }
+
+ // These are the 3 byte characters.
+ if (
+ (n > 2) &&
+ (
+ ((b[0] >= 0x90 && b[0] <= 0x99) && (b[1] >= 0xA0)) ||
+ ((b[0] == 0x9A || b[0] == 0x9B) && (b[1] >= 0xE0 && b[1] <= 0xEF))
+ ) &&
+ (b[2] >= 0xA0)
+ ) {
+ return 3;
+ }
+
+ // These are the 4 byte characters.
+ if (
+ (n > 3) &&
+ (
+ ((b[0] == 0x9C) && (b[1] >= 0xF0) && (b[1] <= 0xF4)) ||
+ ((b[0] == 0x9D) && (b[1] >= 0xF5) && (b[1] <= 0xFE))
+ ) &&
+ (b[2] >= 0xA0) && (b[3] >= 0xA0)
+ ) {
+ return 4;
+ }
+
+ return 0;
+}
+
+/**
+ * Returns the size of the next character in the EUC-KR encoding, or 0 if a
+ * character cannot be decoded from the given bytes.
+ */
+static size_t
+pm_encoding_euc_kr_char_width(const uint8_t *b, ptrdiff_t n) {
+ // These are the single byte characters.
+ if (*b < 0x80) {
+ return 1;
+ }
+
+ // These are the double byte characters.
+ if ((n > 1) && (b[0] >= 0xA1 && b[0] <= 0xFE) && (b[1] >= 0xA1 && b[1] <= 0xFE)) {
+ return 2;
+ }
+
+ return 0;
+}
+
+/**
+ * Returns the size of the next character in the EUC-TW encoding, or 0 if a
+ * character cannot be decoded from the given bytes.
+ */
+static size_t
+pm_encoding_euc_tw_char_width(const uint8_t *b, ptrdiff_t n) {
+ // These are the single byte characters.
+ if (*b < 0x80) {
+ return 1;
+ }
+
+ // These are the double byte characters.
+ if ((n > 1) && (b[0] >= 0xA1) && (b[0] <= 0xFE) && (b[1] >= 0xA1) && (b[1] <= 0xFE)) {
+ return 2;
+ }
+
+ // These are the quadruple byte characters.
+ if ((n > 3) && (b[0] == 0x8E) && (b[1] >= 0xA1) && (b[1] <= 0xB0) && (b[2] >= 0xA1) && (b[2] <= 0xFE) && (b[3] >= 0xA1) && (b[3] <= 0xFE)) {
+ return 4;
+ }
+
+ return 0;
+}
+
+/**
+ * Returns the size of the next character in the GB18030 encoding, or 0 if a
+ * character cannot be decoded from the given bytes.
+ */
+static size_t
+pm_encoding_gb18030_char_width(const uint8_t *b, ptrdiff_t n) {
+ // These are the 1 byte characters.
+ if (*b < 0x80) {
+ return 1;
+ }
+
+ // These are the 2 byte characters.
+ if ((n > 1) && (b[0] >= 0x81 && b[0] <= 0xFE) && (b[1] >= 0x40 && b[1] <= 0xFE && b[1] != 0x7F)) {
+ return 2;
+ }
+
+ // These are the 4 byte characters.
+ if ((n > 3) && ((b[0] >= 0x81 && b[0] <= 0xFE) && (b[1] >= 0x30 && b[1] <= 0x39) && (b[2] >= 0x81 && b[2] <= 0xFE) && (b[3] >= 0x30 && b[3] <= 0x39))) {
+ return 4;
+ }
+
+ return 0;
+}
+
+/**
+ * Returns the size of the next character in the GBK encoding, or 0 if a
+ * character cannot be decoded from the given bytes.
+ */
+static size_t
+pm_encoding_gbk_char_width(const uint8_t *b, ptrdiff_t n) {
+ // These are the single byte characters.
+ if (*b <= 0x80) {
+ return 1;
+ }
+
+ // These are the double byte characters.
+ if (
+ (n > 1) &&
+ (
+ ((b[0] >= 0xA1 && b[0] <= 0xA9) && (b[1] >= 0xA1 && b[1] <= 0xFE)) || // GBK/1
+ ((b[0] >= 0xB0 && b[0] <= 0xF7) && (b[1] >= 0xA1 && b[1] <= 0xFE)) || // GBK/2
+ ((b[0] >= 0x81 && b[0] <= 0xA0) && (b[1] >= 0x40 && b[1] <= 0xFE) && (b[1] != 0x7F)) || // GBK/3
+ ((b[0] >= 0xAA && b[0] <= 0xFE) && (b[1] >= 0x40 && b[1] <= 0xA0) && (b[1] != 0x7F)) || // GBK/4
+ ((b[0] >= 0xA8 && b[0] <= 0xA9) && (b[1] >= 0x40 && b[1] <= 0xA0) && (b[1] != 0x7F)) || // GBK/5
+ ((b[0] >= 0xAA && b[0] <= 0xAF) && (b[1] >= 0xA1 && b[1] <= 0xFE)) || // user-defined 1
+ ((b[0] >= 0xF8 && b[0] <= 0xFE) && (b[1] >= 0xA1 && b[1] <= 0xFE)) || // user-defined 2
+ ((b[0] >= 0xA1 && b[0] <= 0xA7) && (b[1] >= 0x40 && b[1] <= 0xA0) && (b[1] != 0x7F)) // user-defined 3
+ )
+ ) {
+ return 2;
+ }
+
+ return 0;
+}
+
+#endif
+
+/**
+ * This is the table of all of the encodings that prism supports.
+ */
+const pm_encoding_t pm_encodings[] = {
+ [PM_ENCODING_UTF_8] = {
+ .name = "UTF-8",
+ .char_width = pm_encoding_utf_8_char_width,
+ .alnum_char = pm_encoding_utf_8_alnum_char,
+ .alpha_char = pm_encoding_utf_8_alpha_char,
+ .isupper_char = pm_encoding_utf_8_isupper_char,
+ .multibyte = true
+ },
+ [PM_ENCODING_US_ASCII] = {
+ .name = "US-ASCII",
+ .char_width = pm_encoding_ascii_char_width,
+ .alnum_char = pm_encoding_ascii_alnum_char,
+ .alpha_char = pm_encoding_ascii_alpha_char,
+ .isupper_char = pm_encoding_ascii_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_ASCII_8BIT] = {
+ .name = "ASCII-8BIT",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_ascii_alnum_char,
+ .alpha_char = pm_encoding_ascii_alpha_char,
+ .isupper_char = pm_encoding_ascii_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_EUC_JP] = {
+ .name = "EUC-JP",
+ .char_width = pm_encoding_euc_jp_char_width,
+ .alnum_char = pm_encoding_ascii_alnum_char_7bit,
+ .alpha_char = pm_encoding_ascii_alpha_char_7bit,
+ .isupper_char = pm_encoding_euc_jp_isupper_char,
+ .multibyte = true
+ },
+ [PM_ENCODING_WINDOWS_31J] = {
+ .name = "Windows-31J",
+ .char_width = pm_encoding_shift_jis_char_width,
+ .alnum_char = pm_encoding_shift_jis_alnum_char,
+ .alpha_char = pm_encoding_shift_jis_alpha_char,
+ .isupper_char = pm_encoding_shift_jis_isupper_char,
+ .multibyte = true
+ },
+
+#ifndef PRISM_ENCODING_EXCLUDE_FULL
+ [PM_ENCODING_BIG5] = {
+ .name = "Big5",
+ .char_width = pm_encoding_big5_char_width,
+ .alnum_char = pm_encoding_ascii_alnum_char_7bit,
+ .alpha_char = pm_encoding_ascii_alpha_char_7bit,
+ .isupper_char = pm_encoding_ascii_isupper_char_7bit,
+ .multibyte = true
+ },
+ [PM_ENCODING_BIG5_HKSCS] = {
+ .name = "Big5-HKSCS",
+ .char_width = pm_encoding_big5_char_width,
+ .alnum_char = pm_encoding_ascii_alnum_char_7bit,
+ .alpha_char = pm_encoding_ascii_alpha_char_7bit,
+ .isupper_char = pm_encoding_ascii_isupper_char_7bit,
+ .multibyte = true
+ },
+ [PM_ENCODING_BIG5_UAO] = {
+ .name = "Big5-UAO",
+ .char_width = pm_encoding_big5_char_width,
+ .alnum_char = pm_encoding_ascii_alnum_char_7bit,
+ .alpha_char = pm_encoding_ascii_alpha_char_7bit,
+ .isupper_char = pm_encoding_ascii_isupper_char_7bit,
+ .multibyte = true
+ },
+ [PM_ENCODING_CESU_8] = {
+ .name = "CESU-8",
+ .char_width = pm_encoding_cesu_8_char_width,
+ .alnum_char = pm_encoding_cesu_8_alnum_char,
+ .alpha_char = pm_encoding_cesu_8_alpha_char,
+ .isupper_char = pm_encoding_cesu_8_isupper_char,
+ .multibyte = true
+ },
+ [PM_ENCODING_CP51932] = {
+ .name = "CP51932",
+ .char_width = pm_encoding_euc_jp_char_width,
+ .alnum_char = pm_encoding_ascii_alnum_char_7bit,
+ .alpha_char = pm_encoding_ascii_alpha_char_7bit,
+ .isupper_char = pm_encoding_euc_jp_isupper_char,
+ .multibyte = true
+ },
+ [PM_ENCODING_CP850] = {
+ .name = "CP850",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_cp850_alnum_char,
+ .alpha_char = pm_encoding_cp850_alpha_char,
+ .isupper_char = pm_encoding_cp850_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_CP852] = {
+ .name = "CP852",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_cp852_alnum_char,
+ .alpha_char = pm_encoding_cp852_alpha_char,
+ .isupper_char = pm_encoding_cp852_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_CP855] = {
+ .name = "CP855",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_cp855_alnum_char,
+ .alpha_char = pm_encoding_cp855_alpha_char,
+ .isupper_char = pm_encoding_cp855_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_CP949] = {
+ .name = "CP949",
+ .char_width = pm_encoding_cp949_char_width,
+ .alnum_char = pm_encoding_ascii_alnum_char_7bit,
+ .alpha_char = pm_encoding_ascii_alpha_char_7bit,
+ .isupper_char = pm_encoding_ascii_isupper_char_7bit,
+ .multibyte = true
+ },
+ [PM_ENCODING_CP950] = {
+ .name = "CP950",
+ .char_width = pm_encoding_big5_char_width,
+ .alnum_char = pm_encoding_ascii_alnum_char_7bit,
+ .alpha_char = pm_encoding_ascii_alpha_char_7bit,
+ .isupper_char = pm_encoding_ascii_isupper_char_7bit,
+ .multibyte = true
+ },
+ [PM_ENCODING_CP951] = {
+ .name = "CP951",
+ .char_width = pm_encoding_big5_char_width,
+ .alnum_char = pm_encoding_ascii_alnum_char_7bit,
+ .alpha_char = pm_encoding_ascii_alpha_char_7bit,
+ .isupper_char = pm_encoding_ascii_isupper_char_7bit,
+ .multibyte = true
+ },
+ [PM_ENCODING_EMACS_MULE] = {
+ .name = "Emacs-Mule",
+ .char_width = pm_encoding_emacs_mule_char_width,
+ .alnum_char = pm_encoding_ascii_alnum_char_7bit,
+ .alpha_char = pm_encoding_ascii_alpha_char_7bit,
+ .isupper_char = pm_encoding_ascii_isupper_char_7bit,
+ .multibyte = true
+ },
+ [PM_ENCODING_EUC_JP_MS] = {
+ .name = "eucJP-ms",
+ .char_width = pm_encoding_euc_jp_char_width,
+ .alnum_char = pm_encoding_ascii_alnum_char_7bit,
+ .alpha_char = pm_encoding_ascii_alpha_char_7bit,
+ .isupper_char = pm_encoding_euc_jp_isupper_char,
+ .multibyte = true
+ },
+ [PM_ENCODING_EUC_JIS_2004] = {
+ .name = "EUC-JIS-2004",
+ .char_width = pm_encoding_euc_jp_char_width,
+ .alnum_char = pm_encoding_ascii_alnum_char_7bit,
+ .alpha_char = pm_encoding_ascii_alpha_char_7bit,
+ .isupper_char = pm_encoding_euc_jp_isupper_char,
+ .multibyte = true
+ },
+ [PM_ENCODING_EUC_KR] = {
+ .name = "EUC-KR",
+ .char_width = pm_encoding_euc_kr_char_width,
+ .alnum_char = pm_encoding_ascii_alnum_char_7bit,
+ .alpha_char = pm_encoding_ascii_alpha_char_7bit,
+ .isupper_char = pm_encoding_ascii_isupper_char_7bit,
+ .multibyte = true
+ },
+ [PM_ENCODING_EUC_TW] = {
+ .name = "EUC-TW",
+ .char_width = pm_encoding_euc_tw_char_width,
+ .alnum_char = pm_encoding_ascii_alnum_char_7bit,
+ .alpha_char = pm_encoding_ascii_alpha_char_7bit,
+ .isupper_char = pm_encoding_ascii_isupper_char_7bit,
+ .multibyte = true
+ },
+ [PM_ENCODING_GB12345] = {
+ .name = "GB12345",
+ .char_width = pm_encoding_euc_kr_char_width,
+ .alnum_char = pm_encoding_ascii_alnum_char_7bit,
+ .alpha_char = pm_encoding_ascii_alpha_char_7bit,
+ .isupper_char = pm_encoding_ascii_isupper_char_7bit,
+ .multibyte = true
+ },
+ [PM_ENCODING_GB18030] = {
+ .name = "GB18030",
+ .char_width = pm_encoding_gb18030_char_width,
+ .alnum_char = pm_encoding_ascii_alnum_char_7bit,
+ .alpha_char = pm_encoding_ascii_alpha_char_7bit,
+ .isupper_char = pm_encoding_ascii_isupper_char_7bit,
+ .multibyte = true
+ },
+ [PM_ENCODING_GB1988] = {
+ .name = "GB1988",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_gb1988_alnum_char,
+ .alpha_char = pm_encoding_gb1988_alpha_char,
+ .isupper_char = pm_encoding_gb1988_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_GB2312] = {
+ .name = "GB2312",
+ .char_width = pm_encoding_euc_kr_char_width,
+ .alnum_char = pm_encoding_ascii_alnum_char_7bit,
+ .alpha_char = pm_encoding_ascii_alpha_char_7bit,
+ .isupper_char = pm_encoding_ascii_isupper_char_7bit,
+ .multibyte = true
+ },
+ [PM_ENCODING_GBK] = {
+ .name = "GBK",
+ .char_width = pm_encoding_gbk_char_width,
+ .alnum_char = pm_encoding_ascii_alnum_char_7bit,
+ .alpha_char = pm_encoding_ascii_alpha_char_7bit,
+ .isupper_char = pm_encoding_ascii_isupper_char_7bit,
+ .multibyte = true
+ },
+ [PM_ENCODING_IBM437] = {
+ .name = "IBM437",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_ibm437_alnum_char,
+ .alpha_char = pm_encoding_ibm437_alpha_char,
+ .isupper_char = pm_encoding_ibm437_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_IBM720] = {
+ .name = "IBM720",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_ibm720_alnum_char,
+ .alpha_char = pm_encoding_ibm720_alpha_char,
+ .isupper_char = pm_encoding_ibm720_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_IBM737] = {
+ .name = "IBM737",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_ibm737_alnum_char,
+ .alpha_char = pm_encoding_ibm737_alpha_char,
+ .isupper_char = pm_encoding_ibm737_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_IBM775] = {
+ .name = "IBM775",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_ibm775_alnum_char,
+ .alpha_char = pm_encoding_ibm775_alpha_char,
+ .isupper_char = pm_encoding_ibm775_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_IBM852] = {
+ .name = "IBM852",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_ibm852_alnum_char,
+ .alpha_char = pm_encoding_ibm852_alpha_char,
+ .isupper_char = pm_encoding_ibm852_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_IBM855] = {
+ .name = "IBM855",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_ibm855_alnum_char,
+ .alpha_char = pm_encoding_ibm855_alpha_char,
+ .isupper_char = pm_encoding_ibm855_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_IBM857] = {
+ .name = "IBM857",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_ibm857_alnum_char,
+ .alpha_char = pm_encoding_ibm857_alpha_char,
+ .isupper_char = pm_encoding_ibm857_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_IBM860] = {
+ .name = "IBM860",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_ibm860_alnum_char,
+ .alpha_char = pm_encoding_ibm860_alpha_char,
+ .isupper_char = pm_encoding_ibm860_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_IBM861] = {
+ .name = "IBM861",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_ibm861_alnum_char,
+ .alpha_char = pm_encoding_ibm861_alpha_char,
+ .isupper_char = pm_encoding_ibm861_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_IBM862] = {
+ .name = "IBM862",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_ibm862_alnum_char,
+ .alpha_char = pm_encoding_ibm862_alpha_char,
+ .isupper_char = pm_encoding_ibm862_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_IBM863] = {
+ .name = "IBM863",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_ibm863_alnum_char,
+ .alpha_char = pm_encoding_ibm863_alpha_char,
+ .isupper_char = pm_encoding_ibm863_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_IBM864] = {
+ .name = "IBM864",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_ibm864_alnum_char,
+ .alpha_char = pm_encoding_ibm864_alpha_char,
+ .isupper_char = pm_encoding_ibm864_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_IBM865] = {
+ .name = "IBM865",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_ibm865_alnum_char,
+ .alpha_char = pm_encoding_ibm865_alpha_char,
+ .isupper_char = pm_encoding_ibm865_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_IBM866] = {
+ .name = "IBM866",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_ibm866_alnum_char,
+ .alpha_char = pm_encoding_ibm866_alpha_char,
+ .isupper_char = pm_encoding_ibm866_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_IBM869] = {
+ .name = "IBM869",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_ibm869_alnum_char,
+ .alpha_char = pm_encoding_ibm869_alpha_char,
+ .isupper_char = pm_encoding_ibm869_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_ISO_8859_1] = {
+ .name = "ISO-8859-1",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_iso_8859_1_alnum_char,
+ .alpha_char = pm_encoding_iso_8859_1_alpha_char,
+ .isupper_char = pm_encoding_iso_8859_1_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_ISO_8859_2] = {
+ .name = "ISO-8859-2",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_iso_8859_2_alnum_char,
+ .alpha_char = pm_encoding_iso_8859_2_alpha_char,
+ .isupper_char = pm_encoding_iso_8859_2_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_ISO_8859_3] = {
+ .name = "ISO-8859-3",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_iso_8859_3_alnum_char,
+ .alpha_char = pm_encoding_iso_8859_3_alpha_char,
+ .isupper_char = pm_encoding_iso_8859_3_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_ISO_8859_4] = {
+ .name = "ISO-8859-4",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_iso_8859_4_alnum_char,
+ .alpha_char = pm_encoding_iso_8859_4_alpha_char,
+ .isupper_char = pm_encoding_iso_8859_4_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_ISO_8859_5] = {
+ .name = "ISO-8859-5",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_iso_8859_5_alnum_char,
+ .alpha_char = pm_encoding_iso_8859_5_alpha_char,
+ .isupper_char = pm_encoding_iso_8859_5_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_ISO_8859_6] = {
+ .name = "ISO-8859-6",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_iso_8859_6_alnum_char,
+ .alpha_char = pm_encoding_iso_8859_6_alpha_char,
+ .isupper_char = pm_encoding_iso_8859_6_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_ISO_8859_7] = {
+ .name = "ISO-8859-7",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_iso_8859_7_alnum_char,
+ .alpha_char = pm_encoding_iso_8859_7_alpha_char,
+ .isupper_char = pm_encoding_iso_8859_7_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_ISO_8859_8] = {
+ .name = "ISO-8859-8",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_iso_8859_8_alnum_char,
+ .alpha_char = pm_encoding_iso_8859_8_alpha_char,
+ .isupper_char = pm_encoding_iso_8859_8_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_ISO_8859_9] = {
+ .name = "ISO-8859-9",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_iso_8859_9_alnum_char,
+ .alpha_char = pm_encoding_iso_8859_9_alpha_char,
+ .isupper_char = pm_encoding_iso_8859_9_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_ISO_8859_10] = {
+ .name = "ISO-8859-10",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_iso_8859_10_alnum_char,
+ .alpha_char = pm_encoding_iso_8859_10_alpha_char,
+ .isupper_char = pm_encoding_iso_8859_10_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_ISO_8859_11] = {
+ .name = "ISO-8859-11",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_iso_8859_11_alnum_char,
+ .alpha_char = pm_encoding_iso_8859_11_alpha_char,
+ .isupper_char = pm_encoding_iso_8859_11_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_ISO_8859_13] = {
+ .name = "ISO-8859-13",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_iso_8859_13_alnum_char,
+ .alpha_char = pm_encoding_iso_8859_13_alpha_char,
+ .isupper_char = pm_encoding_iso_8859_13_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_ISO_8859_14] = {
+ .name = "ISO-8859-14",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_iso_8859_14_alnum_char,
+ .alpha_char = pm_encoding_iso_8859_14_alpha_char,
+ .isupper_char = pm_encoding_iso_8859_14_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_ISO_8859_15] = {
+ .name = "ISO-8859-15",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_iso_8859_15_alnum_char,
+ .alpha_char = pm_encoding_iso_8859_15_alpha_char,
+ .isupper_char = pm_encoding_iso_8859_15_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_ISO_8859_16] = {
+ .name = "ISO-8859-16",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_iso_8859_16_alnum_char,
+ .alpha_char = pm_encoding_iso_8859_16_alpha_char,
+ .isupper_char = pm_encoding_iso_8859_16_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_KOI8_R] = {
+ .name = "KOI8-R",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_koi8_r_alnum_char,
+ .alpha_char = pm_encoding_koi8_r_alpha_char,
+ .isupper_char = pm_encoding_koi8_r_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_KOI8_U] = {
+ .name = "KOI8-U",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_koi8_u_alnum_char,
+ .alpha_char = pm_encoding_koi8_u_alpha_char,
+ .isupper_char = pm_encoding_koi8_u_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_MAC_CENT_EURO] = {
+ .name = "macCentEuro",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_mac_cent_euro_alnum_char,
+ .alpha_char = pm_encoding_mac_cent_euro_alpha_char,
+ .isupper_char = pm_encoding_mac_cent_euro_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_MAC_CROATIAN] = {
+ .name = "macCroatian",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_mac_croatian_alnum_char,
+ .alpha_char = pm_encoding_mac_croatian_alpha_char,
+ .isupper_char = pm_encoding_mac_croatian_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_MAC_CYRILLIC] = {
+ .name = "macCyrillic",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_mac_cyrillic_alnum_char,
+ .alpha_char = pm_encoding_mac_cyrillic_alpha_char,
+ .isupper_char = pm_encoding_mac_cyrillic_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_MAC_GREEK] = {
+ .name = "macGreek",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_mac_greek_alnum_char,
+ .alpha_char = pm_encoding_mac_greek_alpha_char,
+ .isupper_char = pm_encoding_mac_greek_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_MAC_ICELAND] = {
+ .name = "macIceland",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_mac_iceland_alnum_char,
+ .alpha_char = pm_encoding_mac_iceland_alpha_char,
+ .isupper_char = pm_encoding_mac_iceland_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_MAC_JAPANESE] = {
+ .name = "MacJapanese",
+ .char_width = pm_encoding_shift_jis_char_width,
+ .alnum_char = pm_encoding_shift_jis_alnum_char,
+ .alpha_char = pm_encoding_shift_jis_alpha_char,
+ .isupper_char = pm_encoding_shift_jis_isupper_char,
+ .multibyte = true
+ },
+ [PM_ENCODING_MAC_ROMAN] = {
+ .name = "macRoman",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_mac_roman_alnum_char,
+ .alpha_char = pm_encoding_mac_roman_alpha_char,
+ .isupper_char = pm_encoding_mac_roman_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_MAC_ROMANIA] = {
+ .name = "macRomania",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_mac_romania_alnum_char,
+ .alpha_char = pm_encoding_mac_romania_alpha_char,
+ .isupper_char = pm_encoding_mac_romania_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_MAC_THAI] = {
+ .name = "macThai",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_mac_thai_alnum_char,
+ .alpha_char = pm_encoding_mac_thai_alpha_char,
+ .isupper_char = pm_encoding_mac_thai_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_MAC_TURKISH] = {
+ .name = "macTurkish",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_mac_turkish_alnum_char,
+ .alpha_char = pm_encoding_mac_turkish_alpha_char,
+ .isupper_char = pm_encoding_mac_turkish_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_MAC_UKRAINE] = {
+ .name = "macUkraine",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_mac_ukraine_alnum_char,
+ .alpha_char = pm_encoding_mac_ukraine_alpha_char,
+ .isupper_char = pm_encoding_mac_ukraine_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_SHIFT_JIS] = {
+ .name = "Shift_JIS",
+ .char_width = pm_encoding_shift_jis_char_width,
+ .alnum_char = pm_encoding_shift_jis_alnum_char,
+ .alpha_char = pm_encoding_shift_jis_alpha_char,
+ .isupper_char = pm_encoding_shift_jis_isupper_char,
+ .multibyte = true
+ },
+ [PM_ENCODING_SJIS_DOCOMO] = {
+ .name = "SJIS-DoCoMo",
+ .char_width = pm_encoding_shift_jis_char_width,
+ .alnum_char = pm_encoding_shift_jis_alnum_char,
+ .alpha_char = pm_encoding_shift_jis_alpha_char,
+ .isupper_char = pm_encoding_shift_jis_isupper_char,
+ .multibyte = true
+ },
+ [PM_ENCODING_SJIS_KDDI] = {
+ .name = "SJIS-KDDI",
+ .char_width = pm_encoding_shift_jis_char_width,
+ .alnum_char = pm_encoding_shift_jis_alnum_char,
+ .alpha_char = pm_encoding_shift_jis_alpha_char,
+ .isupper_char = pm_encoding_shift_jis_isupper_char,
+ .multibyte = true
+ },
+ [PM_ENCODING_SJIS_SOFTBANK] = {
+ .name = "SJIS-SoftBank",
+ .char_width = pm_encoding_shift_jis_char_width,
+ .alnum_char = pm_encoding_shift_jis_alnum_char,
+ .alpha_char = pm_encoding_shift_jis_alpha_char,
+ .isupper_char = pm_encoding_shift_jis_isupper_char,
+ .multibyte = true
+ },
+ [PM_ENCODING_STATELESS_ISO_2022_JP] = {
+ .name = "stateless-ISO-2022-JP",
+ .char_width = pm_encoding_emacs_mule_char_width,
+ .alnum_char = pm_encoding_ascii_alnum_char_7bit,
+ .alpha_char = pm_encoding_ascii_alpha_char_7bit,
+ .isupper_char = pm_encoding_ascii_isupper_char_7bit,
+ .multibyte = true
+ },
+ [PM_ENCODING_STATELESS_ISO_2022_JP_KDDI] = {
+ .name = "stateless-ISO-2022-JP-KDDI",
+ .char_width = pm_encoding_emacs_mule_char_width,
+ .alnum_char = pm_encoding_ascii_alnum_char_7bit,
+ .alpha_char = pm_encoding_ascii_alpha_char_7bit,
+ .isupper_char = pm_encoding_ascii_isupper_char_7bit,
+ .multibyte = true
+ },
+ [PM_ENCODING_TIS_620] = {
+ .name = "TIS-620",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_tis_620_alnum_char,
+ .alpha_char = pm_encoding_tis_620_alpha_char,
+ .isupper_char = pm_encoding_tis_620_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_UTF8_MAC] = {
+ .name = "UTF8-MAC",
+ .char_width = pm_encoding_utf_8_char_width,
+ .alnum_char = pm_encoding_utf_8_alnum_char,
+ .alpha_char = pm_encoding_utf_8_alpha_char,
+ .isupper_char = pm_encoding_utf_8_isupper_char,
+ .multibyte = true
+ },
+ [PM_ENCODING_UTF8_DOCOMO] = {
+ .name = "UTF8-DoCoMo",
+ .char_width = pm_encoding_utf_8_char_width,
+ .alnum_char = pm_encoding_utf_8_alnum_char,
+ .alpha_char = pm_encoding_utf_8_alpha_char,
+ .isupper_char = pm_encoding_utf_8_isupper_char,
+ .multibyte = true
+ },
+ [PM_ENCODING_UTF8_KDDI] = {
+ .name = "UTF8-KDDI",
+ .char_width = pm_encoding_utf_8_char_width,
+ .alnum_char = pm_encoding_utf_8_alnum_char,
+ .alpha_char = pm_encoding_utf_8_alpha_char,
+ .isupper_char = pm_encoding_utf_8_isupper_char,
+ .multibyte = true
+ },
+ [PM_ENCODING_UTF8_SOFTBANK] = {
+ .name = "UTF8-SoftBank",
+ .char_width = pm_encoding_utf_8_char_width,
+ .alnum_char = pm_encoding_utf_8_alnum_char,
+ .alpha_char = pm_encoding_utf_8_alpha_char,
+ .isupper_char = pm_encoding_utf_8_isupper_char,
+ .multibyte = true
+ },
+ [PM_ENCODING_WINDOWS_1250] = {
+ .name = "Windows-1250",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_windows_1250_alnum_char,
+ .alpha_char = pm_encoding_windows_1250_alpha_char,
+ .isupper_char = pm_encoding_windows_1250_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_WINDOWS_1251] = {
+ .name = "Windows-1251",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_windows_1251_alnum_char,
+ .alpha_char = pm_encoding_windows_1251_alpha_char,
+ .isupper_char = pm_encoding_windows_1251_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_WINDOWS_1252] = {
+ .name = "Windows-1252",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_windows_1252_alnum_char,
+ .alpha_char = pm_encoding_windows_1252_alpha_char,
+ .isupper_char = pm_encoding_windows_1252_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_WINDOWS_1253] = {
+ .name = "Windows-1253",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_windows_1253_alnum_char,
+ .alpha_char = pm_encoding_windows_1253_alpha_char,
+ .isupper_char = pm_encoding_windows_1253_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_WINDOWS_1254] = {
+ .name = "Windows-1254",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_windows_1254_alnum_char,
+ .alpha_char = pm_encoding_windows_1254_alpha_char,
+ .isupper_char = pm_encoding_windows_1254_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_WINDOWS_1255] = {
+ .name = "Windows-1255",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_windows_1255_alnum_char,
+ .alpha_char = pm_encoding_windows_1255_alpha_char,
+ .isupper_char = pm_encoding_windows_1255_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_WINDOWS_1256] = {
+ .name = "Windows-1256",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_windows_1256_alnum_char,
+ .alpha_char = pm_encoding_windows_1256_alpha_char,
+ .isupper_char = pm_encoding_windows_1256_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_WINDOWS_1257] = {
+ .name = "Windows-1257",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_windows_1257_alnum_char,
+ .alpha_char = pm_encoding_windows_1257_alpha_char,
+ .isupper_char = pm_encoding_windows_1257_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_WINDOWS_1258] = {
+ .name = "Windows-1258",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_windows_1258_alnum_char,
+ .alpha_char = pm_encoding_windows_1258_alpha_char,
+ .isupper_char = pm_encoding_windows_1258_isupper_char,
+ .multibyte = false
+ },
+ [PM_ENCODING_WINDOWS_874] = {
+ .name = "Windows-874",
+ .char_width = pm_encoding_single_char_width,
+ .alnum_char = pm_encoding_windows_874_alnum_char,
+ .alpha_char = pm_encoding_windows_874_alpha_char,
+ .isupper_char = pm_encoding_windows_874_isupper_char,
+ .multibyte = false
+ }
+#endif
+};
+
+/**
+ * Parse the given name of an encoding and return a pointer to the corresponding
+ * encoding struct if one can be found, otherwise return NULL.
+ */
+const pm_encoding_t *
+pm_encoding_find(const uint8_t *start, const uint8_t *end) {
+ size_t width = (size_t) (end - start);
+
+ // First, we're going to check for UTF-8. This is the most common encoding.
+ // UTF-8 can contain extra information at the end about the platform it is
+ // encoded on, such as UTF-8-MAC or UTF-8-UNIX. We'll ignore those suffixes.
+ if ((start + 5 <= end) && (pm_strncasecmp(start, (const uint8_t *) "UTF-8", 5) == 0)) {
+#ifndef PRISM_ENCODING_EXCLUDE_FULL
+ // We need to explicitly handle UTF-8-HFS, as that one needs to switch
+ // over to being UTF8-MAC.
+ if (width == 9 && (pm_strncasecmp(start + 5, (const uint8_t *) "-HFS", 4) == 0)) {
+ return &pm_encodings[PM_ENCODING_UTF8_MAC];
+ }
+#endif
+
+ // Otherwise we'll return the default UTF-8 encoding.
+ return PM_ENCODING_UTF_8_ENTRY;
+ }
+
+ // Next, we're going to loop through each of the encodings that we handle
+ // explicitly. If we found one that we understand, we'll use that value.
+#define ENCODING1(name, encoding) if (width == sizeof(name) - 1 && pm_strncasecmp(start, (const uint8_t *) name, width) == 0) return &pm_encodings[encoding];
+#define ENCODING2(name1, name2, encoding) ENCODING1(name1, encoding) ENCODING1(name2, encoding)
+
+ if (width >= 3) {
+ switch (*start) {
+ case 'A': case 'a':
+ ENCODING1("ASCII", PM_ENCODING_US_ASCII);
+ ENCODING1("ASCII-8BIT", PM_ENCODING_ASCII_8BIT);
+ ENCODING1("ANSI_X3.4-1968", PM_ENCODING_US_ASCII);
+ break;
+ case 'B': case 'b':
+ ENCODING1("BINARY", PM_ENCODING_ASCII_8BIT);
+#ifndef PRISM_ENCODING_EXCLUDE_FULL
+ ENCODING1("Big5", PM_ENCODING_BIG5);
+ ENCODING2("Big5-HKSCS", "Big5-HKSCS:2008", PM_ENCODING_BIG5_HKSCS);
+ ENCODING1("Big5-UAO", PM_ENCODING_BIG5_UAO);
+#endif
+ break;
+ case 'C': case 'c':
+ ENCODING1("CP65001", PM_ENCODING_UTF_8);
+ ENCODING2("CP932", "csWindows31J", PM_ENCODING_WINDOWS_31J);
+#ifndef PRISM_ENCODING_EXCLUDE_FULL
+ ENCODING1("CESU-8", PM_ENCODING_CESU_8);
+ ENCODING1("CP437", PM_ENCODING_IBM437);
+ ENCODING1("CP720", PM_ENCODING_IBM720);
+ ENCODING1("CP737", PM_ENCODING_IBM737);
+ ENCODING1("CP775", PM_ENCODING_IBM775);
+ ENCODING1("CP850", PM_ENCODING_CP850);
+ ENCODING1("CP852", PM_ENCODING_CP852);
+ ENCODING1("CP855", PM_ENCODING_CP855);
+ ENCODING1("CP857", PM_ENCODING_IBM857);
+ ENCODING1("CP860", PM_ENCODING_IBM860);
+ ENCODING1("CP861", PM_ENCODING_IBM861);
+ ENCODING1("CP862", PM_ENCODING_IBM862);
+ ENCODING1("CP864", PM_ENCODING_IBM864);
+ ENCODING1("CP865", PM_ENCODING_IBM865);
+ ENCODING1("CP866", PM_ENCODING_IBM866);
+ ENCODING1("CP869", PM_ENCODING_IBM869);
+ ENCODING1("CP874", PM_ENCODING_WINDOWS_874);
+ ENCODING1("CP878", PM_ENCODING_KOI8_R);
+ ENCODING1("CP863", PM_ENCODING_IBM863);
+ ENCODING1("CP936", PM_ENCODING_GBK);
+ ENCODING1("CP949", PM_ENCODING_CP949);
+ ENCODING1("CP950", PM_ENCODING_CP950);
+ ENCODING1("CP951", PM_ENCODING_CP951);
+ ENCODING1("CP1250", PM_ENCODING_WINDOWS_1250);
+ ENCODING1("CP1251", PM_ENCODING_WINDOWS_1251);
+ ENCODING1("CP1252", PM_ENCODING_WINDOWS_1252);
+ ENCODING1("CP1253", PM_ENCODING_WINDOWS_1253);
+ ENCODING1("CP1254", PM_ENCODING_WINDOWS_1254);
+ ENCODING1("CP1255", PM_ENCODING_WINDOWS_1255);
+ ENCODING1("CP1256", PM_ENCODING_WINDOWS_1256);
+ ENCODING1("CP1257", PM_ENCODING_WINDOWS_1257);
+ ENCODING1("CP1258", PM_ENCODING_WINDOWS_1258);
+ ENCODING1("CP51932", PM_ENCODING_CP51932);
+#endif
+ break;
+ case 'E': case 'e':
+ ENCODING2("EUC-JP", "eucJP", PM_ENCODING_EUC_JP);
+#ifndef PRISM_ENCODING_EXCLUDE_FULL
+ ENCODING2("eucJP-ms", "euc-jp-ms", PM_ENCODING_EUC_JP_MS);
+ ENCODING2("EUC-JIS-2004", "EUC-JISX0213", PM_ENCODING_EUC_JIS_2004);
+ ENCODING2("EUC-KR", "eucKR", PM_ENCODING_EUC_KR);
+ ENCODING2("EUC-CN", "eucCN", PM_ENCODING_GB2312);
+ ENCODING2("EUC-TW", "eucTW", PM_ENCODING_EUC_TW);
+ ENCODING1("Emacs-Mule", PM_ENCODING_EMACS_MULE);
+#endif
+ break;
+ case 'G': case 'g':
+#ifndef PRISM_ENCODING_EXCLUDE_FULL
+ ENCODING1("GBK", PM_ENCODING_GBK);
+ ENCODING1("GB12345", PM_ENCODING_GB12345);
+ ENCODING1("GB18030", PM_ENCODING_GB18030);
+ ENCODING1("GB1988", PM_ENCODING_GB1988);
+ ENCODING1("GB2312", PM_ENCODING_GB2312);
+#endif
+ break;
+ case 'I': case 'i':
+#ifndef PRISM_ENCODING_EXCLUDE_FULL
+ ENCODING1("IBM437", PM_ENCODING_IBM437);
+ ENCODING1("IBM720", PM_ENCODING_IBM720);
+ ENCODING1("IBM737", PM_ENCODING_IBM737);
+ ENCODING1("IBM775", PM_ENCODING_IBM775);
+ ENCODING1("IBM850", PM_ENCODING_CP850);
+ ENCODING1("IBM852", PM_ENCODING_IBM852);
+ ENCODING1("IBM855", PM_ENCODING_IBM855);
+ ENCODING1("IBM857", PM_ENCODING_IBM857);
+ ENCODING1("IBM860", PM_ENCODING_IBM860);
+ ENCODING1("IBM861", PM_ENCODING_IBM861);
+ ENCODING1("IBM862", PM_ENCODING_IBM862);
+ ENCODING1("IBM863", PM_ENCODING_IBM863);
+ ENCODING1("IBM864", PM_ENCODING_IBM864);
+ ENCODING1("IBM865", PM_ENCODING_IBM865);
+ ENCODING1("IBM866", PM_ENCODING_IBM866);
+ ENCODING1("IBM869", PM_ENCODING_IBM869);
+ ENCODING2("ISO-8859-1", "ISO8859-1", PM_ENCODING_ISO_8859_1);
+ ENCODING2("ISO-8859-2", "ISO8859-2", PM_ENCODING_ISO_8859_2);
+ ENCODING2("ISO-8859-3", "ISO8859-3", PM_ENCODING_ISO_8859_3);
+ ENCODING2("ISO-8859-4", "ISO8859-4", PM_ENCODING_ISO_8859_4);
+ ENCODING2("ISO-8859-5", "ISO8859-5", PM_ENCODING_ISO_8859_5);
+ ENCODING2("ISO-8859-6", "ISO8859-6", PM_ENCODING_ISO_8859_6);
+ ENCODING2("ISO-8859-7", "ISO8859-7", PM_ENCODING_ISO_8859_7);
+ ENCODING2("ISO-8859-8", "ISO8859-8", PM_ENCODING_ISO_8859_8);
+ ENCODING2("ISO-8859-9", "ISO8859-9", PM_ENCODING_ISO_8859_9);
+ ENCODING2("ISO-8859-10", "ISO8859-10", PM_ENCODING_ISO_8859_10);
+ ENCODING2("ISO-8859-11", "ISO8859-11", PM_ENCODING_ISO_8859_11);
+ ENCODING2("ISO-8859-13", "ISO8859-13", PM_ENCODING_ISO_8859_13);
+ ENCODING2("ISO-8859-14", "ISO8859-14", PM_ENCODING_ISO_8859_14);
+ ENCODING2("ISO-8859-15", "ISO8859-15", PM_ENCODING_ISO_8859_15);
+ ENCODING2("ISO-8859-16", "ISO8859-16", PM_ENCODING_ISO_8859_16);
+#endif
+ break;
+ case 'K': case 'k':
+#ifndef PRISM_ENCODING_EXCLUDE_FULL
+ ENCODING1("KOI8-R", PM_ENCODING_KOI8_R);
+ ENCODING1("KOI8-U", PM_ENCODING_KOI8_U);
+#endif
+ break;
+ case 'M': case 'm':
+#ifndef PRISM_ENCODING_EXCLUDE_FULL
+ ENCODING1("macCentEuro", PM_ENCODING_MAC_CENT_EURO);
+ ENCODING1("macCroatian", PM_ENCODING_MAC_CROATIAN);
+ ENCODING1("macCyrillic", PM_ENCODING_MAC_CYRILLIC);
+ ENCODING1("macGreek", PM_ENCODING_MAC_GREEK);
+ ENCODING1("macIceland", PM_ENCODING_MAC_ICELAND);
+ ENCODING1("MacJapanese", PM_ENCODING_MAC_JAPANESE);
+ ENCODING1("MacJapan", PM_ENCODING_MAC_JAPANESE);
+ ENCODING1("macRoman", PM_ENCODING_MAC_ROMAN);
+ ENCODING1("macRomania", PM_ENCODING_MAC_ROMANIA);
+ ENCODING1("macThai", PM_ENCODING_MAC_THAI);
+ ENCODING1("macTurkish", PM_ENCODING_MAC_TURKISH);
+ ENCODING1("macUkraine", PM_ENCODING_MAC_UKRAINE);
+#endif
+ break;
+ case 'P': case 'p':
+ ENCODING1("PCK", PM_ENCODING_WINDOWS_31J);
+ break;
+ case 'S': case 's':
+ ENCODING1("SJIS", PM_ENCODING_WINDOWS_31J);
+#ifndef PRISM_ENCODING_EXCLUDE_FULL
+ ENCODING1("Shift_JIS", PM_ENCODING_SHIFT_JIS);
+ ENCODING1("SJIS-DoCoMo", PM_ENCODING_SJIS_DOCOMO);
+ ENCODING1("SJIS-KDDI", PM_ENCODING_SJIS_KDDI);
+ ENCODING1("SJIS-SoftBank", PM_ENCODING_SJIS_SOFTBANK);
+ ENCODING1("stateless-ISO-2022-JP", PM_ENCODING_STATELESS_ISO_2022_JP);
+ ENCODING1("stateless-ISO-2022-JP-KDDI", PM_ENCODING_STATELESS_ISO_2022_JP_KDDI);
+#endif
+ break;
+ case 'T': case 't':
+#ifndef PRISM_ENCODING_EXCLUDE_FULL
+ ENCODING1("TIS-620", PM_ENCODING_TIS_620);
+#endif
+ break;
+ case 'U': case 'u':
+ ENCODING1("US-ASCII", PM_ENCODING_US_ASCII);
+#ifndef PRISM_ENCODING_EXCLUDE_FULL
+ ENCODING2("UTF8-MAC", "UTF-8-HFS", PM_ENCODING_UTF8_MAC);
+ ENCODING1("UTF8-DoCoMo", PM_ENCODING_UTF8_DOCOMO);
+ ENCODING1("UTF8-KDDI", PM_ENCODING_UTF8_KDDI);
+ ENCODING1("UTF8-SoftBank", PM_ENCODING_UTF8_SOFTBANK);
+#endif
+ break;
+ case 'W': case 'w':
+ ENCODING1("Windows-31J", PM_ENCODING_WINDOWS_31J);
+#ifndef PRISM_ENCODING_EXCLUDE_FULL
+ ENCODING1("Windows-874", PM_ENCODING_WINDOWS_874);
+ ENCODING1("Windows-1250", PM_ENCODING_WINDOWS_1250);
+ ENCODING1("Windows-1251", PM_ENCODING_WINDOWS_1251);
+ ENCODING1("Windows-1252", PM_ENCODING_WINDOWS_1252);
+ ENCODING1("Windows-1253", PM_ENCODING_WINDOWS_1253);
+ ENCODING1("Windows-1254", PM_ENCODING_WINDOWS_1254);
+ ENCODING1("Windows-1255", PM_ENCODING_WINDOWS_1255);
+ ENCODING1("Windows-1256", PM_ENCODING_WINDOWS_1256);
+ ENCODING1("Windows-1257", PM_ENCODING_WINDOWS_1257);
+ ENCODING1("Windows-1258", PM_ENCODING_WINDOWS_1258);
+#endif
+ break;
+ case '6':
+ ENCODING1("646", PM_ENCODING_US_ASCII);
+ break;
+ }
+ }
+
+#undef ENCODING2
+#undef ENCODING1
+
+ // If we didn't match any encodings, return NULL.
+ return NULL;
+}
diff --git a/prism/encoding.h b/prism/encoding.h
new file mode 100644
index 0000000000..5f7724821f
--- /dev/null
+++ b/prism/encoding.h
@@ -0,0 +1,283 @@
+/**
+ * @file encoding.h
+ *
+ * The encoding interface and implementations used by the parser.
+ */
+#ifndef PRISM_ENCODING_H
+#define PRISM_ENCODING_H
+
+#include "prism/defines.h"
+#include "prism/util/pm_strncasecmp.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/**
+ * This struct defines the functions necessary to implement the encoding
+ * interface so we can determine how many bytes the subsequent character takes.
+ * Each callback should return the number of bytes, or 0 if the next bytes are
+ * invalid for the encoding and type.
+ */
+typedef struct {
+ /**
+ * Return the number of bytes that the next character takes if it is valid
+ * in the encoding. Does not read more than n bytes. It is assumed that n is
+ * at least 1.
+ */
+ size_t (*char_width)(const uint8_t *b, ptrdiff_t n);
+
+ /**
+ * Return the number of bytes that the next character takes if it is valid
+ * in the encoding and is alphabetical. Does not read more than n bytes. It
+ * is assumed that n is at least 1.
+ */
+ size_t (*alpha_char)(const uint8_t *b, ptrdiff_t n);
+
+ /**
+ * Return the number of bytes that the next character takes if it is valid
+ * in the encoding and is alphanumeric. Does not read more than n bytes. It
+ * is assumed that n is at least 1.
+ */
+ size_t (*alnum_char)(const uint8_t *b, ptrdiff_t n);
+
+ /**
+ * Return true if the next character is valid in the encoding and is an
+ * uppercase character. Does not read more than n bytes. It is assumed that
+ * n is at least 1.
+ */
+ bool (*isupper_char)(const uint8_t *b, ptrdiff_t n);
+
+ /**
+ * The name of the encoding. This should correspond to a value that can be
+ * passed to Encoding.find in Ruby.
+ */
+ const char *name;
+
+ /**
+ * Return true if the encoding is a multibyte encoding.
+ */
+ bool multibyte;
+} pm_encoding_t;
+
+/**
+ * All of the lookup tables use the first bit of each embedded byte to indicate
+ * whether the codepoint is alphabetical.
+ */
+#define PRISM_ENCODING_ALPHABETIC_BIT 1 << 0
+
+/**
+ * All of the lookup tables use the second bit of each embedded byte to indicate
+ * whether the codepoint is alphanumeric.
+ */
+#define PRISM_ENCODING_ALPHANUMERIC_BIT 1 << 1
+
+/**
+ * All of the lookup tables use the third bit of each embedded byte to indicate
+ * whether the codepoint is uppercase.
+ */
+#define PRISM_ENCODING_UPPERCASE_BIT 1 << 2
+
+/**
+ * Return the size of the next character in the UTF-8 encoding.
+ *
+ * @param b The bytes to read.
+ * @param n The number of bytes that can be read.
+ * @returns The number of bytes that the next character takes if it is valid in
+ * the encoding, or 0 if it is not.
+ */
+size_t pm_encoding_utf_8_char_width(const uint8_t *b, ptrdiff_t n);
+
+/**
+ * Return the size of the next character in the UTF-8 encoding if it is an
+ * alphabetical character.
+ *
+ * @param b The bytes to read.
+ * @param n The number of bytes that can be read.
+ * @returns The number of bytes that the next character takes if it is valid in
+ * the encoding, or 0 if it is not.
+ */
+size_t pm_encoding_utf_8_alpha_char(const uint8_t *b, ptrdiff_t n);
+
+/**
+ * Return the size of the next character in the UTF-8 encoding if it is an
+ * alphanumeric character.
+ *
+ * @param b The bytes to read.
+ * @param n The number of bytes that can be read.
+ * @returns The number of bytes that the next character takes if it is valid in
+ * the encoding, or 0 if it is not.
+ */
+size_t pm_encoding_utf_8_alnum_char(const uint8_t *b, ptrdiff_t n);
+
+/**
+ * Return true if the next character in the UTF-8 encoding if it is an uppercase
+ * character.
+ *
+ * @param b The bytes to read.
+ * @param n The number of bytes that can be read.
+ * @returns True if the next character is valid in the encoding and is an
+ * uppercase character, or false if it is not.
+ */
+bool pm_encoding_utf_8_isupper_char(const uint8_t *b, ptrdiff_t n);
+
+/**
+ * This lookup table is referenced in both the UTF-8 encoding file and the
+ * parser directly in order to speed up the default encoding processing. It is
+ * used to indicate whether a character is alphabetical, alphanumeric, or
+ * uppercase in unicode mappings.
+ */
+extern const uint8_t pm_encoding_unicode_table[256];
+
+/**
+ * These are all of the encodings that prism supports.
+ */
+typedef enum {
+ PM_ENCODING_UTF_8 = 0,
+ PM_ENCODING_US_ASCII,
+ PM_ENCODING_ASCII_8BIT,
+ PM_ENCODING_EUC_JP,
+ PM_ENCODING_WINDOWS_31J,
+
+// We optionally support excluding the full set of encodings to only support the
+// minimum necessary to process Ruby code without encoding comments.
+#ifndef PRISM_ENCODING_EXCLUDE_FULL
+ PM_ENCODING_BIG5,
+ PM_ENCODING_BIG5_HKSCS,
+ PM_ENCODING_BIG5_UAO,
+ PM_ENCODING_CESU_8,
+ PM_ENCODING_CP51932,
+ PM_ENCODING_CP850,
+ PM_ENCODING_CP852,
+ PM_ENCODING_CP855,
+ PM_ENCODING_CP949,
+ PM_ENCODING_CP950,
+ PM_ENCODING_CP951,
+ PM_ENCODING_EMACS_MULE,
+ PM_ENCODING_EUC_JP_MS,
+ PM_ENCODING_EUC_JIS_2004,
+ PM_ENCODING_EUC_KR,
+ PM_ENCODING_EUC_TW,
+ PM_ENCODING_GB12345,
+ PM_ENCODING_GB18030,
+ PM_ENCODING_GB1988,
+ PM_ENCODING_GB2312,
+ PM_ENCODING_GBK,
+ PM_ENCODING_IBM437,
+ PM_ENCODING_IBM720,
+ PM_ENCODING_IBM737,
+ PM_ENCODING_IBM775,
+ PM_ENCODING_IBM852,
+ PM_ENCODING_IBM855,
+ PM_ENCODING_IBM857,
+ PM_ENCODING_IBM860,
+ PM_ENCODING_IBM861,
+ PM_ENCODING_IBM862,
+ PM_ENCODING_IBM863,
+ PM_ENCODING_IBM864,
+ PM_ENCODING_IBM865,
+ PM_ENCODING_IBM866,
+ PM_ENCODING_IBM869,
+ PM_ENCODING_ISO_8859_1,
+ PM_ENCODING_ISO_8859_2,
+ PM_ENCODING_ISO_8859_3,
+ PM_ENCODING_ISO_8859_4,
+ PM_ENCODING_ISO_8859_5,
+ PM_ENCODING_ISO_8859_6,
+ PM_ENCODING_ISO_8859_7,
+ PM_ENCODING_ISO_8859_8,
+ PM_ENCODING_ISO_8859_9,
+ PM_ENCODING_ISO_8859_10,
+ PM_ENCODING_ISO_8859_11,
+ PM_ENCODING_ISO_8859_13,
+ PM_ENCODING_ISO_8859_14,
+ PM_ENCODING_ISO_8859_15,
+ PM_ENCODING_ISO_8859_16,
+ PM_ENCODING_KOI8_R,
+ PM_ENCODING_KOI8_U,
+ PM_ENCODING_MAC_CENT_EURO,
+ PM_ENCODING_MAC_CROATIAN,
+ PM_ENCODING_MAC_CYRILLIC,
+ PM_ENCODING_MAC_GREEK,
+ PM_ENCODING_MAC_ICELAND,
+ PM_ENCODING_MAC_JAPANESE,
+ PM_ENCODING_MAC_ROMAN,
+ PM_ENCODING_MAC_ROMANIA,
+ PM_ENCODING_MAC_THAI,
+ PM_ENCODING_MAC_TURKISH,
+ PM_ENCODING_MAC_UKRAINE,
+ PM_ENCODING_SHIFT_JIS,
+ PM_ENCODING_SJIS_DOCOMO,
+ PM_ENCODING_SJIS_KDDI,
+ PM_ENCODING_SJIS_SOFTBANK,
+ PM_ENCODING_STATELESS_ISO_2022_JP,
+ PM_ENCODING_STATELESS_ISO_2022_JP_KDDI,
+ PM_ENCODING_TIS_620,
+ PM_ENCODING_UTF8_MAC,
+ PM_ENCODING_UTF8_DOCOMO,
+ PM_ENCODING_UTF8_KDDI,
+ PM_ENCODING_UTF8_SOFTBANK,
+ PM_ENCODING_WINDOWS_1250,
+ PM_ENCODING_WINDOWS_1251,
+ PM_ENCODING_WINDOWS_1252,
+ PM_ENCODING_WINDOWS_1253,
+ PM_ENCODING_WINDOWS_1254,
+ PM_ENCODING_WINDOWS_1255,
+ PM_ENCODING_WINDOWS_1256,
+ PM_ENCODING_WINDOWS_1257,
+ PM_ENCODING_WINDOWS_1258,
+ PM_ENCODING_WINDOWS_874,
+#endif
+
+ PM_ENCODING_MAXIMUM
+} pm_encoding_type_t;
+
+/**
+ * This is the table of all of the encodings that prism supports.
+ */
+extern const pm_encoding_t pm_encodings[PM_ENCODING_MAXIMUM];
+
+/**
+ * This is the default UTF-8 encoding. We need a reference to it to quickly
+ * create parsers.
+ */
+#define PM_ENCODING_UTF_8_ENTRY (&pm_encodings[PM_ENCODING_UTF_8])
+
+/**
+ * This is the US-ASCII encoding. We need a reference to it to be able to
+ * compare against it when a string is being created because it could possibly
+ * need to fall back to ASCII-8BIT.
+ */
+#define PM_ENCODING_US_ASCII_ENTRY (&pm_encodings[PM_ENCODING_US_ASCII])
+
+/**
+ * This is the ASCII-8BIT encoding. We need a reference to it so that pm_strpbrk
+ * can compare against it because invalid multibyte characters are not a thing
+ * in this encoding. It is also needed for handling Regexp encoding flags.
+ */
+#define PM_ENCODING_ASCII_8BIT_ENTRY (&pm_encodings[PM_ENCODING_ASCII_8BIT])
+
+/**
+ * This is the EUC-JP encoding. We need a reference to it to quickly process
+ * regular expression modifiers.
+ */
+#define PM_ENCODING_EUC_JP_ENTRY (&pm_encodings[PM_ENCODING_EUC_JP])
+
+/**
+ * This is the Windows-31J encoding. We need a reference to it to quickly
+ * process regular expression modifiers.
+ */
+#define PM_ENCODING_WINDOWS_31J_ENTRY (&pm_encodings[PM_ENCODING_WINDOWS_31J])
+
+/**
+ * Parse the given name of an encoding and return a pointer to the corresponding
+ * encoding struct if one can be found, otherwise return NULL.
+ *
+ * @param start A pointer to the first byte of the name.
+ * @param end A pointer to the last byte of the name.
+ * @returns A pointer to the encoding struct if one is found, otherwise NULL.
+ */
+const pm_encoding_t * pm_encoding_find(const uint8_t *start, const uint8_t *end);
+
+#endif
diff --git a/prism/extension.c b/prism/extension.c
new file mode 100644
index 0000000000..7b3f894478
--- /dev/null
+++ b/prism/extension.c
@@ -0,0 +1,1433 @@
+#include "prism/extension.h"
+
+#ifdef _WIN32
+#include <ruby/win32.h>
+#endif
+
+// NOTE: this file should contain only bindings. All non-trivial logic should be
+// in libprism so it can be shared its the various callers.
+
+VALUE rb_cPrism;
+VALUE rb_cPrismNode;
+VALUE rb_cPrismSource;
+VALUE rb_cPrismToken;
+VALUE rb_cPrismLocation;
+
+VALUE rb_cPrismComment;
+VALUE rb_cPrismInlineComment;
+VALUE rb_cPrismEmbDocComment;
+VALUE rb_cPrismMagicComment;
+VALUE rb_cPrismParseError;
+VALUE rb_cPrismParseWarning;
+VALUE rb_cPrismResult;
+VALUE rb_cPrismParseResult;
+VALUE rb_cPrismParseLexResult;
+
+VALUE rb_cPrismDebugEncoding;
+
+ID rb_option_id_command_line;
+ID rb_option_id_encoding;
+ID rb_option_id_filepath;
+ID rb_option_id_frozen_string_literal;
+ID rb_option_id_line;
+ID rb_option_id_scopes;
+ID rb_option_id_version;
+
+/******************************************************************************/
+/* IO of Ruby code */
+/******************************************************************************/
+
+/**
+ * Check if the given VALUE is a string. If it's nil, then return NULL. If it's
+ * not a string, then raise a type error. Otherwise return the VALUE as a C
+ * string.
+ */
+static const char *
+check_string(VALUE value) {
+ // If the value is nil, then we don't need to do anything.
+ if (NIL_P(value)) {
+ return NULL;
+ }
+
+ // Check if the value is a string. If it's not, then raise a type error.
+ if (!RB_TYPE_P(value, T_STRING)) {
+ rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected String)", rb_obj_class(value));
+ }
+
+ // Otherwise, return the value as a C string.
+ return RSTRING_PTR(value);
+}
+
+/**
+ * Load the contents and size of the given string into the given pm_string_t.
+ */
+static void
+input_load_string(pm_string_t *input, VALUE string) {
+ // Check if the string is a string. If it's not, then raise a type error.
+ if (!RB_TYPE_P(string, T_STRING)) {
+ rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected String)", rb_obj_class(string));
+ }
+
+ pm_string_constant_init(input, RSTRING_PTR(string), RSTRING_LEN(string));
+}
+
+/******************************************************************************/
+/* Building C options from Ruby options */
+/******************************************************************************/
+
+/**
+ * Build the scopes associated with the provided Ruby keyword value.
+ */
+static void
+build_options_scopes(pm_options_t *options, VALUE scopes) {
+ // Check if the value is an array. If it's not, then raise a type error.
+ if (!RB_TYPE_P(scopes, T_ARRAY)) {
+ rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array)", rb_obj_class(scopes));
+ }
+
+ // Initialize the scopes array.
+ size_t scopes_count = RARRAY_LEN(scopes);
+ if (!pm_options_scopes_init(options, scopes_count)) {
+ rb_raise(rb_eNoMemError, "failed to allocate memory");
+ }
+
+ // Iterate over the scopes and add them to the options.
+ for (size_t scope_index = 0; scope_index < scopes_count; scope_index++) {
+ VALUE scope = rb_ary_entry(scopes, scope_index);
+
+ // Check that the scope is an array. If it's not, then raise a type
+ // error.
+ if (!RB_TYPE_P(scope, T_ARRAY)) {
+ rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array)", rb_obj_class(scope));
+ }
+
+ // Initialize the scope array.
+ size_t locals_count = RARRAY_LEN(scope);
+ pm_options_scope_t *options_scope = &options->scopes[scope_index];
+ if (!pm_options_scope_init(options_scope, locals_count)) {
+ rb_raise(rb_eNoMemError, "failed to allocate memory");
+ }
+
+ // Iterate over the locals and add them to the scope.
+ for (size_t local_index = 0; local_index < locals_count; local_index++) {
+ VALUE local = rb_ary_entry(scope, local_index);
+
+ // Check that the local is a symbol. If it's not, then raise a
+ // type error.
+ if (!RB_TYPE_P(local, T_SYMBOL)) {
+ rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Symbol)", rb_obj_class(local));
+ }
+
+ // Add the local to the scope.
+ pm_string_t *scope_local = &options_scope->locals[local_index];
+ const char *name = rb_id2name(SYM2ID(local));
+ pm_string_constant_init(scope_local, name, strlen(name));
+ }
+ }
+}
+
+/**
+ * An iterator function that is called for each key-value in the keywords hash.
+ */
+static int
+build_options_i(VALUE key, VALUE value, VALUE argument) {
+ pm_options_t *options = (pm_options_t *) argument;
+ ID key_id = SYM2ID(key);
+
+ if (key_id == rb_option_id_filepath) {
+ if (!NIL_P(value)) pm_options_filepath_set(options, check_string(value));
+ } else if (key_id == rb_option_id_encoding) {
+ if (!NIL_P(value)) pm_options_encoding_set(options, rb_enc_name(rb_to_encoding(value)));
+ } else if (key_id == rb_option_id_line) {
+ if (!NIL_P(value)) pm_options_line_set(options, NUM2INT(value));
+ } else if (key_id == rb_option_id_frozen_string_literal) {
+ if (!NIL_P(value)) pm_options_frozen_string_literal_set(options, RTEST(value));
+ } else if (key_id == rb_option_id_version) {
+ if (!NIL_P(value)) {
+ const char *version = check_string(value);
+
+ if (!pm_options_version_set(options, version, RSTRING_LEN(value))) {
+ rb_raise(rb_eArgError, "invalid version: %" PRIsVALUE, value);
+ }
+ }
+ } else if (key_id == rb_option_id_scopes) {
+ if (!NIL_P(value)) build_options_scopes(options, value);
+ } else if (key_id == rb_option_id_command_line) {
+ if (!NIL_P(value)) {
+ const char *string = check_string(value);
+ uint8_t command_line = 0;
+
+ for (size_t index = 0; index < strlen(string); index++) {
+ switch (string[index]) {
+ case 'a': command_line |= PM_OPTIONS_COMMAND_LINE_A; break;
+ case 'e': command_line |= PM_OPTIONS_COMMAND_LINE_E; break;
+ case 'l': command_line |= PM_OPTIONS_COMMAND_LINE_L; break;
+ case 'n': command_line |= PM_OPTIONS_COMMAND_LINE_N; break;
+ case 'p': command_line |= PM_OPTIONS_COMMAND_LINE_P; break;
+ case 'x': command_line |= PM_OPTIONS_COMMAND_LINE_X; break;
+ default: rb_raise(rb_eArgError, "invalid command line flag: '%c'", string[index]); break;
+ }
+ }
+
+ pm_options_command_line_set(options, command_line);
+ }
+ } else {
+ rb_raise(rb_eArgError, "unknown keyword: %" PRIsVALUE, key);
+ }
+
+ return ST_CONTINUE;
+}
+
+/**
+ * We need a struct here to pass through rb_protect and it has to be a single
+ * value. Because the sizeof(VALUE) == sizeof(void *), we're going to pass this
+ * through as an opaque pointer and cast it on both sides.
+ */
+struct build_options_data {
+ pm_options_t *options;
+ VALUE keywords;
+};
+
+/**
+ * Build the set of options from the given keywords. Note that this can raise a
+ * Ruby error if the options are not valid.
+ */
+static VALUE
+build_options(VALUE argument) {
+ struct build_options_data *data = (struct build_options_data *) argument;
+ rb_hash_foreach(data->keywords, build_options_i, (VALUE) data->options);
+ return Qnil;
+}
+
+/**
+ * Extract the options from the given keyword arguments.
+ */
+static void
+extract_options(pm_options_t *options, VALUE filepath, VALUE keywords) {
+ options->line = 1; // default
+ if (!NIL_P(keywords)) {
+ struct build_options_data data = { .options = options, .keywords = keywords };
+ struct build_options_data *argument = &data;
+
+ int state = 0;
+ rb_protect(build_options, (VALUE) argument, &state);
+
+ if (state != 0) {
+ pm_options_free(options);
+ rb_jump_tag(state);
+ }
+ }
+
+ if (!NIL_P(filepath)) {
+ if (!RB_TYPE_P(filepath, T_STRING)) {
+ pm_options_free(options);
+ rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected String)", rb_obj_class(filepath));
+ }
+
+ pm_options_filepath_set(options, RSTRING_PTR(filepath));
+ }
+}
+
+/**
+ * Read options for methods that look like (source, **options).
+ */
+static void
+string_options(int argc, VALUE *argv, pm_string_t *input, pm_options_t *options) {
+ VALUE string;
+ VALUE keywords;
+ rb_scan_args(argc, argv, "1:", &string, &keywords);
+
+ extract_options(options, Qnil, keywords);
+ input_load_string(input, string);
+}
+
+/**
+ * Read options for methods that look like (filepath, **options).
+ */
+static void
+file_options(int argc, VALUE *argv, pm_string_t *input, pm_options_t *options) {
+ VALUE filepath;
+ VALUE keywords;
+ rb_scan_args(argc, argv, "1:", &filepath, &keywords);
+
+ Check_Type(filepath, T_STRING);
+
+ extract_options(options, filepath, keywords);
+
+ const char * string_source = (const char *) pm_string_source(&options->filepath);
+
+ if (!pm_string_file_init(input, string_source)) {
+ pm_options_free(options);
+
+#ifdef _WIN32
+ int e = rb_w32_map_errno(GetLastError());
+#else
+ int e = errno;
+#endif
+
+ rb_syserr_fail(e, string_source);
+ }
+}
+
+#ifndef PRISM_EXCLUDE_SERIALIZATION
+
+/******************************************************************************/
+/* Serializing the AST */
+/******************************************************************************/
+
+/**
+ * Dump the AST corresponding to the given input to a string.
+ */
+static VALUE
+dump_input(pm_string_t *input, const pm_options_t *options) {
+ pm_buffer_t buffer;
+ if (!pm_buffer_init(&buffer)) {
+ rb_raise(rb_eNoMemError, "failed to allocate memory");
+ }
+
+ pm_parser_t parser;
+ pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
+
+ pm_node_t *node = pm_parse(&parser);
+ pm_serialize(&parser, node, &buffer);
+
+ VALUE result = rb_str_new(pm_buffer_value(&buffer), pm_buffer_length(&buffer));
+ pm_node_destroy(&parser, node);
+ pm_buffer_free(&buffer);
+ pm_parser_free(&parser);
+
+ return result;
+}
+
+/**
+ * call-seq:
+ * Prism::dump(source, **options) -> String
+ *
+ * Dump the AST corresponding to the given string to a string. For supported
+ * options, see Prism::parse.
+ */
+static VALUE
+dump(int argc, VALUE *argv, VALUE self) {
+ pm_string_t input;
+ pm_options_t options = { 0 };
+ string_options(argc, argv, &input, &options);
+
+#ifdef PRISM_BUILD_DEBUG
+ size_t length = pm_string_length(&input);
+ char* dup = xmalloc(length);
+ memcpy(dup, pm_string_source(&input), length);
+ pm_string_constant_init(&input, dup, length);
+#endif
+
+ VALUE value = dump_input(&input, &options);
+
+#ifdef PRISM_BUILD_DEBUG
+ xfree(dup);
+#endif
+
+ pm_string_free(&input);
+ pm_options_free(&options);
+
+ return value;
+}
+
+/**
+ * call-seq:
+ * Prism::dump_file(filepath, **options) -> String
+ *
+ * Dump the AST corresponding to the given file to a string. For supported
+ * options, see Prism::parse.
+ */
+static VALUE
+dump_file(int argc, VALUE *argv, VALUE self) {
+ pm_string_t input;
+ pm_options_t options = { 0 };
+
+ file_options(argc, argv, &input, &options);
+
+ VALUE value = dump_input(&input, &options);
+ pm_string_free(&input);
+ pm_options_free(&options);
+
+ return value;
+}
+
+#endif
+
+/******************************************************************************/
+/* Extracting values for the parse result */
+/******************************************************************************/
+
+/**
+ * Extract the comments out of the parser into an array.
+ */
+static VALUE
+parser_comments(pm_parser_t *parser, VALUE source) {
+ VALUE comments = rb_ary_new();
+
+ for (pm_comment_t *comment = (pm_comment_t *) parser->comment_list.head; comment != NULL; comment = (pm_comment_t *) comment->node.next) {
+ VALUE location_argv[] = {
+ source,
+ LONG2FIX(comment->location.start - parser->start),
+ LONG2FIX(comment->location.end - comment->location.start)
+ };
+
+ VALUE type = (comment->type == PM_COMMENT_EMBDOC) ? rb_cPrismEmbDocComment : rb_cPrismInlineComment;
+ VALUE comment_argv[] = { rb_class_new_instance(3, location_argv, rb_cPrismLocation) };
+ rb_ary_push(comments, rb_class_new_instance(1, comment_argv, type));
+ }
+
+ return comments;
+}
+
+/**
+ * Extract the magic comments out of the parser into an array.
+ */
+static VALUE
+parser_magic_comments(pm_parser_t *parser, VALUE source) {
+ VALUE magic_comments = rb_ary_new();
+
+ for (pm_magic_comment_t *magic_comment = (pm_magic_comment_t *) parser->magic_comment_list.head; magic_comment != NULL; magic_comment = (pm_magic_comment_t *) magic_comment->node.next) {
+ VALUE key_loc_argv[] = {
+ source,
+ LONG2FIX(magic_comment->key_start - parser->start),
+ LONG2FIX(magic_comment->key_length)
+ };
+
+ VALUE value_loc_argv[] = {
+ source,
+ LONG2FIX(magic_comment->value_start - parser->start),
+ LONG2FIX(magic_comment->value_length)
+ };
+
+ VALUE magic_comment_argv[] = {
+ rb_class_new_instance(3, key_loc_argv, rb_cPrismLocation),
+ rb_class_new_instance(3, value_loc_argv, rb_cPrismLocation)
+ };
+
+ rb_ary_push(magic_comments, rb_class_new_instance(2, magic_comment_argv, rb_cPrismMagicComment));
+ }
+
+ return magic_comments;
+}
+
+/**
+ * Extract out the data location from the parser into a Location instance if one
+ * exists.
+ */
+static VALUE
+parser_data_loc(const pm_parser_t *parser, VALUE source) {
+ if (parser->data_loc.end == NULL) {
+ return Qnil;
+ } else {
+ VALUE argv[] = {
+ source,
+ LONG2FIX(parser->data_loc.start - parser->start),
+ LONG2FIX(parser->data_loc.end - parser->data_loc.start)
+ };
+
+ return rb_class_new_instance(3, argv, rb_cPrismLocation);
+ }
+}
+
+/**
+ * Extract the errors out of the parser into an array.
+ */
+static VALUE
+parser_errors(pm_parser_t *parser, rb_encoding *encoding, VALUE source) {
+ VALUE errors = rb_ary_new();
+ pm_diagnostic_t *error;
+
+ for (error = (pm_diagnostic_t *) parser->error_list.head; error != NULL; error = (pm_diagnostic_t *) error->node.next) {
+ VALUE location_argv[] = {
+ source,
+ LONG2FIX(error->location.start - parser->start),
+ LONG2FIX(error->location.end - error->location.start)
+ };
+
+ VALUE level = Qnil;
+ switch (error->level) {
+ case PM_ERROR_LEVEL_SYNTAX:
+ level = ID2SYM(rb_intern("syntax"));
+ break;
+ case PM_ERROR_LEVEL_ARGUMENT:
+ level = ID2SYM(rb_intern("argument"));
+ break;
+ case PM_ERROR_LEVEL_LOAD:
+ level = ID2SYM(rb_intern("load"));
+ break;
+ default:
+ rb_raise(rb_eRuntimeError, "Unknown level: %" PRIu8, error->level);
+ }
+
+ VALUE error_argv[] = {
+ ID2SYM(rb_intern(pm_diagnostic_id_human(error->diag_id))),
+ rb_enc_str_new_cstr(error->message, encoding),
+ rb_class_new_instance(3, location_argv, rb_cPrismLocation),
+ level
+ };
+
+ rb_ary_push(errors, rb_class_new_instance(4, error_argv, rb_cPrismParseError));
+ }
+
+ return errors;
+}
+
+/**
+ * Extract the warnings out of the parser into an array.
+ */
+static VALUE
+parser_warnings(pm_parser_t *parser, rb_encoding *encoding, VALUE source) {
+ VALUE warnings = rb_ary_new();
+ pm_diagnostic_t *warning;
+
+ for (warning = (pm_diagnostic_t *) parser->warning_list.head; warning != NULL; warning = (pm_diagnostic_t *) warning->node.next) {
+ VALUE location_argv[] = {
+ source,
+ LONG2FIX(warning->location.start - parser->start),
+ LONG2FIX(warning->location.end - warning->location.start)
+ };
+
+ VALUE level = Qnil;
+ switch (warning->level) {
+ case PM_WARNING_LEVEL_DEFAULT:
+ level = ID2SYM(rb_intern("default"));
+ break;
+ case PM_WARNING_LEVEL_VERBOSE:
+ level = ID2SYM(rb_intern("verbose"));
+ break;
+ default:
+ rb_raise(rb_eRuntimeError, "Unknown level: %" PRIu8, warning->level);
+ }
+
+ VALUE warning_argv[] = {
+ ID2SYM(rb_intern(pm_diagnostic_id_human(warning->diag_id))),
+ rb_enc_str_new_cstr(warning->message, encoding),
+ rb_class_new_instance(3, location_argv, rb_cPrismLocation),
+ level
+ };
+
+ rb_ary_push(warnings, rb_class_new_instance(4, warning_argv, rb_cPrismParseWarning));
+ }
+
+ return warnings;
+}
+
+/**
+ * Create a new parse result from the given parser, value, encoding, and source.
+ */
+static VALUE
+parse_result_create(VALUE class, pm_parser_t *parser, VALUE value, rb_encoding *encoding, VALUE source) {
+ VALUE result_argv[] = {
+ value,
+ parser_comments(parser, source),
+ parser_magic_comments(parser, source),
+ parser_data_loc(parser, source),
+ parser_errors(parser, encoding, source),
+ parser_warnings(parser, encoding, source),
+ source
+ };
+
+ return rb_class_new_instance(7, result_argv, class);
+}
+
+/******************************************************************************/
+/* Lexing Ruby code */
+/******************************************************************************/
+
+/**
+ * This struct gets stored in the parser and passed in to the lex callback any
+ * time a new token is found. We use it to store the necessary information to
+ * initialize a Token instance.
+ */
+typedef struct {
+ VALUE source;
+ VALUE tokens;
+ rb_encoding *encoding;
+} parse_lex_data_t;
+
+/**
+ * This is passed as a callback to the parser. It gets called every time a new
+ * token is found. Once found, we initialize a new instance of Token and push it
+ * onto the tokens array.
+ */
+static void
+parse_lex_token(void *data, pm_parser_t *parser, pm_token_t *token) {
+ parse_lex_data_t *parse_lex_data = (parse_lex_data_t *) parser->lex_callback->data;
+
+ VALUE yields = rb_ary_new_capa(2);
+ rb_ary_push(yields, pm_token_new(parser, token, parse_lex_data->encoding, parse_lex_data->source));
+ rb_ary_push(yields, INT2FIX(parser->lex_state));
+
+ rb_ary_push(parse_lex_data->tokens, yields);
+}
+
+/**
+ * This is called whenever the encoding changes based on the magic comment at
+ * the top of the file. We use it to update the encoding that we are using to
+ * create tokens.
+ */
+static void
+parse_lex_encoding_changed_callback(pm_parser_t *parser) {
+ parse_lex_data_t *parse_lex_data = (parse_lex_data_t *) parser->lex_callback->data;
+ parse_lex_data->encoding = rb_enc_find(parser->encoding->name);
+
+ // Since the encoding changed, we need to go back and change the encoding of
+ // the tokens that were already lexed. This is only going to end up being
+ // one or two tokens, since the encoding can only change at the top of the
+ // file.
+ VALUE tokens = parse_lex_data->tokens;
+ for (long index = 0; index < RARRAY_LEN(tokens); index++) {
+ VALUE yields = rb_ary_entry(tokens, index);
+ VALUE token = rb_ary_entry(yields, 0);
+
+ VALUE value = rb_ivar_get(token, rb_intern("@value"));
+ rb_enc_associate(value, parse_lex_data->encoding);
+ ENC_CODERANGE_CLEAR(value);
+ }
+}
+
+/**
+ * Parse the given input and return a ParseResult containing just the tokens or
+ * the nodes and tokens.
+ */
+static VALUE
+parse_lex_input(pm_string_t *input, const pm_options_t *options, bool return_nodes) {
+ pm_parser_t parser;
+ pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
+ pm_parser_register_encoding_changed_callback(&parser, parse_lex_encoding_changed_callback);
+
+ VALUE source_string = rb_str_new((const char *) pm_string_source(input), pm_string_length(input));
+ VALUE offsets = rb_ary_new();
+ VALUE source_argv[] = { source_string, LONG2NUM(parser.start_line), offsets };
+ VALUE source = rb_class_new_instance(3, source_argv, rb_cPrismSource);
+
+ parse_lex_data_t parse_lex_data = {
+ .source = source,
+ .tokens = rb_ary_new(),
+ .encoding = rb_utf8_encoding()
+ };
+
+ parse_lex_data_t *data = &parse_lex_data;
+ pm_lex_callback_t lex_callback = (pm_lex_callback_t) {
+ .data = (void *) data,
+ .callback = parse_lex_token,
+ };
+
+ parser.lex_callback = &lex_callback;
+ pm_node_t *node = pm_parse(&parser);
+
+ // Here we need to update the Source object to have the correct
+ // encoding for the source string and the correct newline offsets.
+ // We do it here because we've already created the Source object and given
+ // it over to all of the tokens, and both of these are only set after pm_parse().
+ rb_encoding *encoding = rb_enc_find(parser.encoding->name);
+ rb_enc_associate(source_string, encoding);
+
+ for (size_t index = 0; index < parser.newline_list.size; index++) {
+ rb_ary_push(offsets, ULONG2NUM(parser.newline_list.offsets[index]));
+ }
+
+ VALUE value;
+ if (return_nodes) {
+ value = rb_ary_new_capa(2);
+ rb_ary_push(value, pm_ast_new(&parser, node, parse_lex_data.encoding, source));
+ rb_ary_push(value, parse_lex_data.tokens);
+ } else {
+ value = parse_lex_data.tokens;
+ }
+
+ VALUE result = parse_result_create(rb_cPrismParseLexResult, &parser, value, parse_lex_data.encoding, source);
+ pm_node_destroy(&parser, node);
+ pm_parser_free(&parser);
+
+ return result;
+}
+
+/**
+ * call-seq:
+ * Prism::lex(source, **options) -> Array
+ *
+ * Return an array of Token instances corresponding to the given string. For
+ * supported options, see Prism::parse.
+ */
+static VALUE
+lex(int argc, VALUE *argv, VALUE self) {
+ pm_string_t input;
+ pm_options_t options = { 0 };
+ string_options(argc, argv, &input, &options);
+
+ VALUE result = parse_lex_input(&input, &options, false);
+ pm_string_free(&input);
+ pm_options_free(&options);
+
+ return result;
+}
+
+/**
+ * call-seq:
+ * Prism::lex_file(filepath, **options) -> Array
+ *
+ * Return an array of Token instances corresponding to the given file. For
+ * supported options, see Prism::parse.
+ */
+static VALUE
+lex_file(int argc, VALUE *argv, VALUE self) {
+ pm_string_t input;
+ pm_options_t options = { 0 };
+
+ file_options(argc, argv, &input, &options);
+
+ VALUE value = parse_lex_input(&input, &options, false);
+ pm_string_free(&input);
+ pm_options_free(&options);
+
+ return value;
+}
+
+/******************************************************************************/
+/* Parsing Ruby code */
+/******************************************************************************/
+
+/**
+ * Parse the given input and return a ParseResult instance.
+ */
+static VALUE
+parse_input(pm_string_t *input, const pm_options_t *options) {
+ pm_parser_t parser;
+ pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
+
+ pm_node_t *node = pm_parse(&parser);
+ rb_encoding *encoding = rb_enc_find(parser.encoding->name);
+
+ VALUE source = pm_source_new(&parser, encoding);
+ VALUE value = pm_ast_new(&parser, node, encoding, source);
+ VALUE result = parse_result_create(rb_cPrismParseResult, &parser, value, encoding, source) ;
+
+ pm_node_destroy(&parser, node);
+ pm_parser_free(&parser);
+
+ return result;
+}
+
+/**
+ * call-seq:
+ * Prism::parse(source, **options) -> ParseResult
+ *
+ * Parse the given string and return a ParseResult instance. The options that
+ * are supported are:
+ *
+ * * `command_line` - either nil or a string of the various options that were
+ * set on the command line. Valid values are combinations of "a", "l",
+ * "n", "p", and "x".
+ * * `encoding` - the encoding of the source being parsed. This should be an
+ * encoding or nil.
+ * * `filepath` - the filepath of the source being parsed. This should be a
+ * string or nil.
+ * * `frozen_string_literal` - whether or not the frozen string literal pragma
+ * has been set. This should be a boolean or nil.
+ * * `line` - the line number that the parse starts on. This should be an
+ * integer or nil. Note that this is 1-indexed.
+ * * `scopes` - the locals that are in scope surrounding the code that is being
+ * parsed. This should be an array of arrays of symbols or nil. Scopes are
+ * ordered from the outermost scope to the innermost one.
+ * * `version` - the version of Ruby syntax that prism should used to parse Ruby
+ * code. By default prism assumes you want to parse with the latest version
+ * of Ruby syntax (which you can trigger with `nil` or `"latest"`). You
+ * may also restrict the syntax to a specific version of Ruby. The
+ * supported values are `"3.3.0"` and `"3.4.0"`.
+ */
+static VALUE
+parse(int argc, VALUE *argv, VALUE self) {
+ pm_string_t input;
+ pm_options_t options = { 0 };
+ string_options(argc, argv, &input, &options);
+
+#ifdef PRISM_BUILD_DEBUG
+ size_t length = pm_string_length(&input);
+ char* dup = xmalloc(length);
+ memcpy(dup, pm_string_source(&input), length);
+ pm_string_constant_init(&input, dup, length);
+#endif
+
+ VALUE value = parse_input(&input, &options);
+
+#ifdef PRISM_BUILD_DEBUG
+ xfree(dup);
+#endif
+
+ pm_string_free(&input);
+ pm_options_free(&options);
+ return value;
+}
+
+/**
+ * An implementation of fgets that is suitable for use with Ruby IO objects.
+ */
+static char *
+parse_stream_fgets(char *string, int size, void *stream) {
+ RUBY_ASSERT(size > 0);
+
+ VALUE line = rb_funcall((VALUE) stream, rb_intern("gets"), 1, INT2FIX(size - 1));
+ if (NIL_P(line)) {
+ return NULL;
+ }
+
+ const char *cstr = StringValueCStr(line);
+ size_t length = strlen(cstr);
+
+ memcpy(string, cstr, length);
+ string[length] = '\0';
+
+ return string;
+}
+
+/**
+ * call-seq:
+ * Prism::parse_stream(stream, **options) -> ParseResult
+ *
+ * Parse the given object that responds to `gets` and return a ParseResult
+ * instance. The options that are supported are the same as Prism::parse.
+ */
+static VALUE
+parse_stream(int argc, VALUE *argv, VALUE self) {
+ VALUE stream;
+ VALUE keywords;
+ rb_scan_args(argc, argv, "1:", &stream, &keywords);
+
+ pm_options_t options = { 0 };
+ extract_options(&options, Qnil, keywords);
+
+ pm_parser_t parser;
+ pm_buffer_t buffer;
+
+ pm_node_t *node = pm_parse_stream(&parser, &buffer, (void *) stream, parse_stream_fgets, &options);
+ rb_encoding *encoding = rb_enc_find(parser.encoding->name);
+
+ VALUE source = pm_source_new(&parser, encoding);
+ VALUE value = pm_ast_new(&parser, node, encoding, source);
+ VALUE result = parse_result_create(rb_cPrismParseResult, &parser, value, encoding, source);
+
+ pm_node_destroy(&parser, node);
+ pm_buffer_free(&buffer);
+ pm_parser_free(&parser);
+
+ return result;
+}
+
+/**
+ * call-seq:
+ * Prism::parse_file(filepath, **options) -> ParseResult
+ *
+ * Parse the given file and return a ParseResult instance. For supported
+ * options, see Prism::parse.
+ */
+static VALUE
+parse_file(int argc, VALUE *argv, VALUE self) {
+ pm_string_t input;
+ pm_options_t options = { 0 };
+
+ file_options(argc, argv, &input, &options);
+
+ VALUE value = parse_input(&input, &options);
+ pm_string_free(&input);
+ pm_options_free(&options);
+
+ return value;
+}
+
+/**
+ * Parse the given input and return an array of Comment objects.
+ */
+static VALUE
+parse_input_comments(pm_string_t *input, const pm_options_t *options) {
+ pm_parser_t parser;
+ pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
+
+ pm_node_t *node = pm_parse(&parser);
+ rb_encoding *encoding = rb_enc_find(parser.encoding->name);
+
+ VALUE source = pm_source_new(&parser, encoding);
+ VALUE comments = parser_comments(&parser, source);
+
+ pm_node_destroy(&parser, node);
+ pm_parser_free(&parser);
+
+ return comments;
+}
+
+/**
+ * call-seq:
+ * Prism::parse_comments(source, **options) -> Array
+ *
+ * Parse the given string and return an array of Comment objects. For supported
+ * options, see Prism::parse.
+ */
+static VALUE
+parse_comments(int argc, VALUE *argv, VALUE self) {
+ pm_string_t input;
+ pm_options_t options = { 0 };
+ string_options(argc, argv, &input, &options);
+
+ VALUE result = parse_input_comments(&input, &options);
+ pm_string_free(&input);
+ pm_options_free(&options);
+
+ return result;
+}
+
+/**
+ * call-seq:
+ * Prism::parse_file_comments(filepath, **options) -> Array
+ *
+ * Parse the given file and return an array of Comment objects. For supported
+ * options, see Prism::parse.
+ */
+static VALUE
+parse_file_comments(int argc, VALUE *argv, VALUE self) {
+ pm_string_t input;
+ pm_options_t options = { 0 };
+
+ file_options(argc, argv, &input, &options);
+
+ VALUE value = parse_input_comments(&input, &options);
+ pm_string_free(&input);
+ pm_options_free(&options);
+
+ return value;
+}
+
+/**
+ * call-seq:
+ * Prism::parse_lex(source, **options) -> ParseResult
+ *
+ * Parse the given string and return a ParseResult instance that contains a
+ * 2-element array, where the first element is the AST and the second element is
+ * an array of Token instances.
+ *
+ * This API is only meant to be used in the case where you need both the AST and
+ * the tokens. If you only need one or the other, use either Prism::parse or
+ * Prism::lex.
+ *
+ * For supported options, see Prism::parse.
+ */
+static VALUE
+parse_lex(int argc, VALUE *argv, VALUE self) {
+ pm_string_t input;
+ pm_options_t options = { 0 };
+ string_options(argc, argv, &input, &options);
+
+ VALUE value = parse_lex_input(&input, &options, true);
+ pm_string_free(&input);
+ pm_options_free(&options);
+
+ return value;
+}
+
+/**
+ * call-seq:
+ * Prism::parse_lex_file(filepath, **options) -> ParseResult
+ *
+ * Parse the given file and return a ParseResult instance that contains a
+ * 2-element array, where the first element is the AST and the second element is
+ * an array of Token instances.
+ *
+ * This API is only meant to be used in the case where you need both the AST and
+ * the tokens. If you only need one or the other, use either Prism::parse_file
+ * or Prism::lex_file.
+ *
+ * For supported options, see Prism::parse.
+ */
+static VALUE
+parse_lex_file(int argc, VALUE *argv, VALUE self) {
+ pm_string_t input;
+ pm_options_t options = { 0 };
+
+ file_options(argc, argv, &input, &options);
+
+ VALUE value = parse_lex_input(&input, &options, true);
+ pm_string_free(&input);
+ pm_options_free(&options);
+
+ return value;
+}
+
+/**
+ * Parse the given input and return true if it parses without errors.
+ */
+static VALUE
+parse_input_success_p(pm_string_t *input, const pm_options_t *options) {
+ pm_parser_t parser;
+ pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
+
+ pm_node_t *node = pm_parse(&parser);
+ pm_node_destroy(&parser, node);
+
+ VALUE result = parser.error_list.size == 0 ? Qtrue : Qfalse;
+ pm_parser_free(&parser);
+
+ return result;
+}
+
+/**
+ * call-seq:
+ * Prism::parse_success?(source, **options) -> bool
+ *
+ * Parse the given string and return true if it parses without errors. For
+ * supported options, see Prism::parse.
+ */
+static VALUE
+parse_success_p(int argc, VALUE *argv, VALUE self) {
+ pm_string_t input;
+ pm_options_t options = { 0 };
+ string_options(argc, argv, &input, &options);
+
+ VALUE result = parse_input_success_p(&input, &options);
+ pm_string_free(&input);
+ pm_options_free(&options);
+
+ return result;
+}
+
+/**
+ * call-seq:
+ * Prism::parse_failure?(source, **options) -> bool
+ *
+ * Parse the given string and return true if it parses with errors. For
+ * supported options, see Prism::parse.
+ */
+static VALUE
+parse_failure_p(int argc, VALUE *argv, VALUE self) {
+ return RTEST(parse_success_p(argc, argv, self)) ? Qfalse : Qtrue;
+}
+
+/**
+ * call-seq:
+ * Prism::parse_file_success?(filepath, **options) -> bool
+ *
+ * Parse the given file and return true if it parses without errors. For
+ * supported options, see Prism::parse.
+ */
+static VALUE
+parse_file_success_p(int argc, VALUE *argv, VALUE self) {
+ pm_string_t input;
+ pm_options_t options = { 0 };
+
+ file_options(argc, argv, &input, &options);
+
+ VALUE result = parse_input_success_p(&input, &options);
+ pm_string_free(&input);
+ pm_options_free(&options);
+
+ return result;
+}
+
+/**
+ * call-seq:
+ * Prism::parse_file_failure?(filepath, **options) -> bool
+ *
+ * Parse the given file and return true if it parses with errors. For
+ * supported options, see Prism::parse.
+ */
+static VALUE
+parse_file_failure_p(int argc, VALUE *argv, VALUE self) {
+ return RTEST(parse_file_success_p(argc, argv, self)) ? Qfalse : Qtrue;
+}
+
+/******************************************************************************/
+/* Utility functions exposed to make testing easier */
+/******************************************************************************/
+
+/**
+ * call-seq:
+ * Debug::named_captures(source) -> Array
+ *
+ * Returns an array of strings corresponding to the named capture groups in the
+ * given source string. If prism was unable to parse the regular expression,
+ * this function returns nil.
+ */
+static VALUE
+named_captures(VALUE self, VALUE source) {
+ pm_string_list_t string_list = { 0 };
+
+ if (!pm_regexp_named_capture_group_names((const uint8_t *) RSTRING_PTR(source), RSTRING_LEN(source), &string_list, false, PM_ENCODING_UTF_8_ENTRY)) {
+ pm_string_list_free(&string_list);
+ return Qnil;
+ }
+
+ VALUE names = rb_ary_new();
+ for (size_t index = 0; index < string_list.length; index++) {
+ const pm_string_t *string = &string_list.strings[index];
+ rb_ary_push(names, rb_str_new((const char *) pm_string_source(string), pm_string_length(string)));
+ }
+
+ pm_string_list_free(&string_list);
+ return names;
+}
+
+/**
+ * call-seq:
+ * Debug::integer_parse(source) -> [Integer, String]
+ *
+ * Parses the given source string and returns the integer it represents, as well
+ * as a decimal string representation.
+ */
+static VALUE
+integer_parse(VALUE self, VALUE source) {
+ const uint8_t *start = (const uint8_t *) RSTRING_PTR(source);
+ size_t length = RSTRING_LEN(source);
+
+ pm_integer_t integer = { 0 };
+ pm_integer_parse(&integer, PM_INTEGER_BASE_UNKNOWN, start, start + length);
+
+ pm_buffer_t buffer = { 0 };
+ pm_integer_string(&buffer, &integer);
+
+ VALUE string = rb_str_new(pm_buffer_value(&buffer), pm_buffer_length(&buffer));
+ pm_buffer_free(&buffer);
+
+ VALUE result = rb_ary_new_capa(2);
+ rb_ary_push(result, pm_integer_new(&integer));
+ rb_ary_push(result, string);
+ pm_integer_free(&integer);
+
+ return result;
+}
+
+/**
+ * call-seq:
+ * Debug::memsize(source) -> { length: xx, memsize: xx, node_count: xx }
+ *
+ * Return a hash of information about the given source string's memory usage.
+ */
+static VALUE
+memsize(VALUE self, VALUE string) {
+ pm_parser_t parser;
+ size_t length = RSTRING_LEN(string);
+ pm_parser_init(&parser, (const uint8_t *) RSTRING_PTR(string), length, NULL);
+
+ pm_node_t *node = pm_parse(&parser);
+ pm_memsize_t memsize;
+ pm_node_memsize(node, &memsize);
+
+ pm_node_destroy(&parser, node);
+ pm_parser_free(&parser);
+
+ VALUE result = rb_hash_new();
+ rb_hash_aset(result, ID2SYM(rb_intern("length")), INT2FIX(length));
+ rb_hash_aset(result, ID2SYM(rb_intern("memsize")), INT2FIX(memsize.memsize));
+ rb_hash_aset(result, ID2SYM(rb_intern("node_count")), INT2FIX(memsize.node_count));
+ return result;
+}
+
+/**
+ * call-seq:
+ * Debug::profile_file(filepath) -> nil
+ *
+ * Parse the file, but do nothing with the result. This is used to profile the
+ * parser for memory and speed.
+ */
+static VALUE
+profile_file(VALUE self, VALUE filepath) {
+ pm_string_t input;
+
+ const char *checked = check_string(filepath);
+ Check_Type(filepath, T_STRING);
+
+ if (!pm_string_mapped_init(&input, checked)) {
+#ifdef _WIN32
+ int e = rb_w32_map_errno(GetLastError());
+#else
+ int e = errno;
+#endif
+
+ rb_syserr_fail(e, checked);
+ }
+
+ pm_options_t options = { 0 };
+ pm_options_filepath_set(&options, checked);
+
+ pm_parser_t parser;
+ pm_parser_init(&parser, pm_string_source(&input), pm_string_length(&input), &options);
+
+ pm_node_t *node = pm_parse(&parser);
+ pm_node_destroy(&parser, node);
+ pm_parser_free(&parser);
+ pm_options_free(&options);
+ pm_string_free(&input);
+
+ return Qnil;
+}
+
+#ifndef PRISM_EXCLUDE_PRETTYPRINT
+
+/**
+ * call-seq:
+ * Debug::inspect_node(source) -> inspected
+ *
+ * Inspect the AST that represents the given source using the prism pretty print
+ * as opposed to the Ruby implementation.
+ */
+static VALUE
+inspect_node(VALUE self, VALUE source) {
+ pm_string_t input;
+ input_load_string(&input, source);
+
+ pm_parser_t parser;
+ pm_parser_init(&parser, pm_string_source(&input), pm_string_length(&input), NULL);
+
+ pm_node_t *node = pm_parse(&parser);
+ pm_buffer_t buffer = { 0 };
+
+ pm_prettyprint(&buffer, &parser, node);
+
+ rb_encoding *encoding = rb_enc_find(parser.encoding->name);
+ VALUE string = rb_enc_str_new(pm_buffer_value(&buffer), pm_buffer_length(&buffer), encoding);
+
+ pm_buffer_free(&buffer);
+ pm_node_destroy(&parser, node);
+ pm_parser_free(&parser);
+
+ return string;
+}
+
+#endif
+
+/**
+ * call-seq:
+ * Debug::format_errors(source, colorize) -> String
+ *
+ * Format the errors that are found when parsing the given source string.
+ */
+static VALUE
+format_errors(VALUE self, VALUE source, VALUE colorize) {
+ pm_string_t input;
+ input_load_string(&input, source);
+
+ pm_parser_t parser;
+ pm_parser_init(&parser, pm_string_source(&input), pm_string_length(&input), NULL);
+
+ pm_node_t *node = pm_parse(&parser);
+ pm_buffer_t buffer = { 0 };
+
+ pm_parser_errors_format(&parser, &parser.error_list, &buffer, RTEST(colorize), true);
+
+ rb_encoding *encoding = rb_enc_find(parser.encoding->name);
+ VALUE result = rb_enc_str_new(pm_buffer_value(&buffer), pm_buffer_length(&buffer), encoding);
+
+ pm_buffer_free(&buffer);
+ pm_node_destroy(&parser, node);
+ pm_parser_free(&parser);
+ pm_string_free(&input);
+
+ return result;
+}
+
+/**
+ * call-seq:
+ * Debug::static_inspect(source) -> String
+ *
+ * Inspect the node as it would be inspected by the warnings used in static
+ * literal sets.
+ */
+static VALUE
+static_inspect(int argc, VALUE *argv, VALUE self) {
+ pm_string_t input;
+ pm_options_t options = { 0 };
+ string_options(argc, argv, &input, &options);
+
+ pm_parser_t parser;
+ pm_parser_init(&parser, pm_string_source(&input), pm_string_length(&input), &options);
+
+ pm_node_t *program = pm_parse(&parser);
+ pm_node_t *node = ((pm_program_node_t *) program)->statements->body.nodes[0];
+
+ pm_buffer_t buffer = { 0 };
+ pm_static_literal_inspect(&buffer, &parser.newline_list, parser.start_line, parser.encoding->name, node);
+
+ rb_encoding *encoding = rb_enc_find(parser.encoding->name);
+ VALUE result = rb_enc_str_new(pm_buffer_value(&buffer), pm_buffer_length(&buffer), encoding);
+
+ pm_buffer_free(&buffer);
+ pm_node_destroy(&parser, program);
+ pm_parser_free(&parser);
+ pm_string_free(&input);
+ pm_options_free(&options);
+
+ return result;
+}
+
+/**
+ * call-seq: Debug::Encoding.all -> Array[Debug::Encoding]
+ *
+ * Return an array of all of the encodings that prism knows about.
+ */
+static VALUE
+encoding_all(VALUE self) {
+ VALUE encodings = rb_ary_new();
+
+ for (size_t index = 0; index < PM_ENCODING_MAXIMUM; index++) {
+ const pm_encoding_t *encoding = &pm_encodings[index];
+
+ VALUE encoding_argv[] = { rb_str_new_cstr(encoding->name), encoding->multibyte ? Qtrue : Qfalse };
+ rb_ary_push(encodings, rb_class_new_instance(2, encoding_argv, rb_cPrismDebugEncoding));
+ }
+
+ return encodings;
+}
+
+static const pm_encoding_t *
+encoding_find(VALUE name) {
+ const uint8_t *source = (const uint8_t *) RSTRING_PTR(name);
+ size_t length = RSTRING_LEN(name);
+
+ const pm_encoding_t *encoding = pm_encoding_find(source, source + length);
+ if (encoding == NULL) { rb_raise(rb_eArgError, "Unknown encoding: %s", source); }
+
+ return encoding;
+}
+
+/**
+ * call-seq: Debug::Encoding.width(source) -> Integer
+ *
+ * Returns the width of the first character in the given string if it is valid
+ * in the encoding. If it is not, this function returns 0.
+ */
+static VALUE
+encoding_char_width(VALUE self, VALUE name, VALUE value) {
+ return ULONG2NUM(encoding_find(name)->char_width((const uint8_t *) RSTRING_PTR(value), RSTRING_LEN(value)));
+}
+
+/**
+ * call-seq: Debug::Encoding.alnum?(source) -> true | false
+ *
+ * Returns true if the first character in the given string is an alphanumeric
+ * character in the encoding.
+ */
+static VALUE
+encoding_alnum_char(VALUE self, VALUE name, VALUE value) {
+ return encoding_find(name)->alnum_char((const uint8_t *) RSTRING_PTR(value), RSTRING_LEN(value)) > 0 ? Qtrue : Qfalse;
+}
+
+/**
+ * call-seq: Debug::Encoding.alpha?(source) -> true | false
+ *
+ * Returns true if the first character in the given string is an alphabetic
+ * character in the encoding.
+ */
+static VALUE
+encoding_alpha_char(VALUE self, VALUE name, VALUE value) {
+ return encoding_find(name)->alpha_char((const uint8_t *) RSTRING_PTR(value), RSTRING_LEN(value)) > 0 ? Qtrue : Qfalse;
+}
+
+/**
+ * call-seq: Debug::Encoding.upper?(source) -> true | false
+ *
+ * Returns true if the first character in the given string is an uppercase
+ * character in the encoding.
+ */
+static VALUE
+encoding_isupper_char(VALUE self, VALUE name, VALUE value) {
+ return encoding_find(name)->isupper_char((const uint8_t *) RSTRING_PTR(value), RSTRING_LEN(value)) ? Qtrue : Qfalse;
+}
+
+/******************************************************************************/
+/* Initialization of the extension */
+/******************************************************************************/
+
+/**
+ * The init function that Ruby calls when loading this extension.
+ */
+RUBY_FUNC_EXPORTED void
+Init_prism(void) {
+ // Make sure that the prism library version matches the expected version.
+ // Otherwise something was compiled incorrectly.
+ if (strcmp(pm_version(), EXPECTED_PRISM_VERSION) != 0) {
+ rb_raise(
+ rb_eRuntimeError,
+ "The prism library version (%s) does not match the expected version (%s)",
+ pm_version(),
+ EXPECTED_PRISM_VERSION
+ );
+ }
+
+ // Grab up references to all of the constants that we're going to need to
+ // reference throughout this extension.
+ rb_cPrism = rb_define_module("Prism");
+ rb_cPrismNode = rb_define_class_under(rb_cPrism, "Node", rb_cObject);
+ rb_cPrismSource = rb_define_class_under(rb_cPrism, "Source", rb_cObject);
+ rb_cPrismToken = rb_define_class_under(rb_cPrism, "Token", rb_cObject);
+ rb_cPrismLocation = rb_define_class_under(rb_cPrism, "Location", rb_cObject);
+ rb_cPrismComment = rb_define_class_under(rb_cPrism, "Comment", rb_cObject);
+ rb_cPrismInlineComment = rb_define_class_under(rb_cPrism, "InlineComment", rb_cPrismComment);
+ rb_cPrismEmbDocComment = rb_define_class_under(rb_cPrism, "EmbDocComment", rb_cPrismComment);
+ rb_cPrismMagicComment = rb_define_class_under(rb_cPrism, "MagicComment", rb_cObject);
+ rb_cPrismParseError = rb_define_class_under(rb_cPrism, "ParseError", rb_cObject);
+ rb_cPrismParseWarning = rb_define_class_under(rb_cPrism, "ParseWarning", rb_cObject);
+
+ rb_cPrismResult = rb_define_class_under(rb_cPrism, "Result", rb_cObject);
+ rb_cPrismParseResult = rb_define_class_under(rb_cPrism, "ParseResult", rb_cPrismResult);
+ rb_cPrismParseLexResult = rb_define_class_under(rb_cPrism, "ParseLexResult", rb_cPrismResult);
+
+ // Intern all of the options that we support so that we don't have to do it
+ // every time we parse.
+ rb_option_id_command_line = rb_intern_const("command_line");
+ rb_option_id_encoding = rb_intern_const("encoding");
+ rb_option_id_filepath = rb_intern_const("filepath");
+ rb_option_id_frozen_string_literal = rb_intern_const("frozen_string_literal");
+ rb_option_id_line = rb_intern_const("line");
+ rb_option_id_scopes = rb_intern_const("scopes");
+ rb_option_id_version = rb_intern_const("version");
+
+ /**
+ * The version of the prism library.
+ */
+ rb_define_const(rb_cPrism, "VERSION", rb_str_new2(EXPECTED_PRISM_VERSION));
+
+ // First, the functions that have to do with lexing and parsing.
+ rb_define_singleton_method(rb_cPrism, "lex", lex, -1);
+ rb_define_singleton_method(rb_cPrism, "lex_file", lex_file, -1);
+ rb_define_singleton_method(rb_cPrism, "parse", parse, -1);
+ rb_define_singleton_method(rb_cPrism, "parse_stream", parse_stream, -1);
+ rb_define_singleton_method(rb_cPrism, "parse_file", parse_file, -1);
+ rb_define_singleton_method(rb_cPrism, "parse_comments", parse_comments, -1);
+ rb_define_singleton_method(rb_cPrism, "parse_file_comments", parse_file_comments, -1);
+ rb_define_singleton_method(rb_cPrism, "parse_lex", parse_lex, -1);
+ rb_define_singleton_method(rb_cPrism, "parse_lex_file", parse_lex_file, -1);
+ rb_define_singleton_method(rb_cPrism, "parse_success?", parse_success_p, -1);
+ rb_define_singleton_method(rb_cPrism, "parse_failure?", parse_failure_p, -1);
+ rb_define_singleton_method(rb_cPrism, "parse_file_success?", parse_file_success_p, -1);
+ rb_define_singleton_method(rb_cPrism, "parse_file_failure?", parse_file_failure_p, -1);
+
+#ifndef PRISM_EXCLUDE_SERIALIZATION
+ rb_define_singleton_method(rb_cPrism, "dump", dump, -1);
+ rb_define_singleton_method(rb_cPrism, "dump_file", dump_file, -1);
+#endif
+
+ // Next, the functions that will be called by the parser to perform various
+ // internal tasks. We expose these to make them easier to test.
+ VALUE rb_cPrismDebug = rb_define_module_under(rb_cPrism, "Debug");
+ rb_define_singleton_method(rb_cPrismDebug, "named_captures", named_captures, 1);
+ rb_define_singleton_method(rb_cPrismDebug, "integer_parse", integer_parse, 1);
+ rb_define_singleton_method(rb_cPrismDebug, "memsize", memsize, 1);
+ rb_define_singleton_method(rb_cPrismDebug, "profile_file", profile_file, 1);
+ rb_define_singleton_method(rb_cPrismDebug, "format_errors", format_errors, 2);
+ rb_define_singleton_method(rb_cPrismDebug, "static_inspect", static_inspect, -1);
+
+#ifndef PRISM_EXCLUDE_PRETTYPRINT
+ rb_define_singleton_method(rb_cPrismDebug, "inspect_node", inspect_node, 1);
+#endif
+
+ // Next, define the functions that are exposed through the private
+ // Debug::Encoding class.
+ rb_cPrismDebugEncoding = rb_define_class_under(rb_cPrismDebug, "Encoding", rb_cObject);
+ rb_define_singleton_method(rb_cPrismDebugEncoding, "all", encoding_all, 0);
+ rb_define_singleton_method(rb_cPrismDebugEncoding, "_width", encoding_char_width, 2);
+ rb_define_singleton_method(rb_cPrismDebugEncoding, "_alnum?", encoding_alnum_char, 2);
+ rb_define_singleton_method(rb_cPrismDebugEncoding, "_alpha?", encoding_alpha_char, 2);
+ rb_define_singleton_method(rb_cPrismDebugEncoding, "_upper?", encoding_isupper_char, 2);
+
+ // Next, initialize the other APIs.
+ Init_prism_api_node();
+ Init_prism_pack();
+}
diff --git a/prism/extension.h b/prism/extension.h
new file mode 100644
index 0000000000..9134959783
--- /dev/null
+++ b/prism/extension.h
@@ -0,0 +1,19 @@
+#ifndef PRISM_EXT_NODE_H
+#define PRISM_EXT_NODE_H
+
+#define EXPECTED_PRISM_VERSION "0.27.0"
+
+#include <ruby.h>
+#include <ruby/encoding.h>
+#include "prism.h"
+
+VALUE pm_source_new(const pm_parser_t *parser, rb_encoding *encoding);
+VALUE pm_token_new(const pm_parser_t *parser, const pm_token_t *token, rb_encoding *encoding, VALUE source);
+VALUE pm_ast_new(const pm_parser_t *parser, const pm_node_t *node, rb_encoding *encoding, VALUE source);
+VALUE pm_integer_new(const pm_integer_t *integer);
+
+void Init_prism_api_node(void);
+void Init_prism_pack(void);
+PRISM_EXPORTED_FUNCTION void Init_prism(void);
+
+#endif
diff --git a/prism/node.h b/prism/node.h
new file mode 100644
index 0000000000..8736e59a94
--- /dev/null
+++ b/prism/node.h
@@ -0,0 +1,150 @@
+/**
+ * @file node.h
+ *
+ * Functions related to nodes in the AST.
+ */
+#ifndef PRISM_NODE_H
+#define PRISM_NODE_H
+
+#include "prism/defines.h"
+#include "prism/parser.h"
+#include "prism/util/pm_buffer.h"
+
+/**
+ * Loop through each node in the node list, writing each node to the given
+ * pm_node_t pointer.
+ */
+#define PM_NODE_LIST_FOREACH(list, index, node) \
+ for (size_t index = 0; index < (list)->size && ((node) = (list)->nodes[index]); index++)
+
+/**
+ * Append a new node onto the end of the node list.
+ *
+ * @param list The list to append to.
+ * @param node The node to append.
+ */
+void pm_node_list_append(pm_node_list_t *list, pm_node_t *node);
+
+/**
+ * Prepend a new node onto the beginning of the node list.
+ *
+ * @param list The list to prepend to.
+ * @param node The node to prepend.
+ */
+void pm_node_list_prepend(pm_node_list_t *list, pm_node_t *node);
+
+/**
+ * Concatenate the given node list onto the end of the other node list.
+ *
+ * @param list The list to concatenate onto.
+ * @param other The list to concatenate.
+ */
+void pm_node_list_concat(pm_node_list_t *list, pm_node_list_t *other);
+
+/**
+ * Free the internal memory associated with the given node list.
+ *
+ * @param list The list to free.
+ */
+void pm_node_list_free(pm_node_list_t *list);
+
+/**
+ * Deallocate a node and all of its children.
+ *
+ * @param parser The parser that owns the node.
+ * @param node The node to deallocate.
+ */
+PRISM_EXPORTED_FUNCTION void pm_node_destroy(pm_parser_t *parser, struct pm_node *node);
+
+/**
+ * This struct stores the information gathered by the pm_node_memsize function.
+ * It contains both the memory footprint and additionally metadata about the
+ * shape of the tree.
+ */
+typedef struct {
+ /** The total memory footprint of the node and all of its children. */
+ size_t memsize;
+
+ /** The number of children the node has. */
+ size_t node_count;
+} pm_memsize_t;
+
+/**
+ * Calculates the memory footprint of a given node.
+ *
+ * @param node The node to calculate the memory footprint of.
+ * @param memsize The memory footprint of the node and all of its children.
+ */
+PRISM_EXPORTED_FUNCTION void pm_node_memsize(pm_node_t *node, pm_memsize_t *memsize);
+
+/**
+ * Returns a string representation of the given node type.
+ *
+ * @param node_type The node type to convert to a string.
+ * @return A string representation of the given node type.
+ */
+PRISM_EXPORTED_FUNCTION const char * pm_node_type_to_str(pm_node_type_t node_type);
+
+/**
+ * Visit each of the nodes in this subtree using the given visitor callback. The
+ * callback function will be called for each node in the subtree. If it returns
+ * false, then that node's children will not be visited. If it returns true,
+ * then the children will be visited. The data parameter is treated as an opaque
+ * pointer and is passed to the visitor callback for consumers to use as they
+ * see fit.
+ *
+ * As an example:
+ *
+ * ```c
+ * #include "prism.h"
+ *
+ * bool visit(const pm_node_t *node, void *data) {
+ * size_t *indent = (size_t *) data;
+ * for (size_t i = 0; i < *indent * 2; i++) putc(' ', stdout);
+ * printf("%s\n", pm_node_type_to_str(node->type));
+ *
+ * size_t next_indent = *indent + 1;
+ * size_t *next_data = &next_indent;
+ * pm_visit_child_nodes(node, visit, next_data);
+ *
+ * return false;
+ * }
+ *
+ * int main(void) {
+ * const char *source = "1 + 2; 3 + 4";
+ * size_t size = strlen(source);
+ *
+ * pm_parser_t parser;
+ * pm_options_t options = { 0 };
+ * pm_parser_init(&parser, (const uint8_t *) source, size, &options);
+ *
+ * size_t indent = 0;
+ * pm_node_t *node = pm_parse(&parser);
+ *
+ * size_t *data = &indent;
+ * pm_visit_node(node, visit, data);
+ *
+ * pm_node_destroy(&parser, node);
+ * pm_parser_free(&parser);
+ * return EXIT_SUCCESS;
+ * }
+ * ```
+ *
+ * @param node The root node to start visiting from.
+ * @param visitor The callback to call for each node in the subtree.
+ * @param data An opaque pointer that is passed to the visitor callback.
+ */
+PRISM_EXPORTED_FUNCTION void pm_visit_node(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data);
+
+/**
+ * Visit the children of the given node with the given callback. This is the
+ * default behavior for walking the tree that is called from pm_visit_node if
+ * the callback returns true.
+ *
+ * @param node The node to visit the children of.
+ * @param visitor The callback to call for each child node.
+ * @param data An opaque pointer that is passed to the visitor callback.
+ */
+PRISM_EXPORTED_FUNCTION void pm_visit_child_nodes(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data);
+
+#endif
diff --git a/prism/options.c b/prism/options.c
new file mode 100644
index 0000000000..4d0d6dbc49
--- /dev/null
+++ b/prism/options.c
@@ -0,0 +1,248 @@
+#include "prism/options.h"
+
+/**
+ * Set the filepath option on the given options struct.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_options_filepath_set(pm_options_t *options, const char *filepath) {
+ pm_string_constant_init(&options->filepath, filepath, strlen(filepath));
+}
+
+/**
+ * Set the encoding option on the given options struct.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_options_encoding_set(pm_options_t *options, const char *encoding) {
+ pm_string_constant_init(&options->encoding, encoding, strlen(encoding));
+}
+
+/**
+ * Set the line option on the given options struct.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_options_line_set(pm_options_t *options, int32_t line) {
+ options->line = line;
+}
+
+/**
+ * Set the frozen string literal option on the given options struct.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_options_frozen_string_literal_set(pm_options_t *options, bool frozen_string_literal) {
+ options->frozen_string_literal = frozen_string_literal ? PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED : PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED;
+}
+
+/**
+ * Sets the command line option on the given options struct.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_options_command_line_set(pm_options_t *options, uint8_t command_line) {
+ options->command_line = command_line;
+}
+
+/**
+ * Set the version option on the given options struct by parsing the given
+ * string. If the string contains an invalid option, this returns false.
+ * Otherwise, it returns true.
+ */
+PRISM_EXPORTED_FUNCTION bool
+pm_options_version_set(pm_options_t *options, const char *version, size_t length) {
+ switch (length) {
+ case 0:
+ if (version == NULL) {
+ options->version = PM_OPTIONS_VERSION_LATEST;
+ return true;
+ }
+
+ return false;
+ case 5:
+ assert(version != NULL);
+
+ if (strncmp(version, "3.3.0", length) == 0) {
+ options->version = PM_OPTIONS_VERSION_CRUBY_3_3_0;
+ return true;
+ }
+
+ if (strncmp(version, "3.4.0", length) == 0) {
+ options->version = PM_OPTIONS_VERSION_LATEST;
+ return true;
+ }
+
+ return false;
+ case 6:
+ assert(version != NULL);
+
+ if (strncmp(version, "latest", length) == 0) {
+ options->version = PM_OPTIONS_VERSION_LATEST;
+ return true;
+ }
+
+ return false;
+ default:
+ return false;
+ }
+}
+
+// For some reason, GCC analyzer thinks we're leaking allocated scopes and
+// locals here, even though we definitely aren't. This is a false positive.
+// Ideally we wouldn't need to suppress this.
+#if defined(__GNUC__) && (__GNUC__ >= 10)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak"
+#endif
+
+/**
+ * Allocate and zero out the scopes array on the given options struct.
+ */
+PRISM_EXPORTED_FUNCTION bool
+pm_options_scopes_init(pm_options_t *options, size_t scopes_count) {
+ options->scopes_count = scopes_count;
+ options->scopes = xcalloc(scopes_count, sizeof(pm_options_scope_t));
+ return options->scopes != NULL;
+}
+
+/**
+ * Return a pointer to the scope at the given index within the given options.
+ */
+PRISM_EXPORTED_FUNCTION const pm_options_scope_t *
+pm_options_scope_get(const pm_options_t *options, size_t index) {
+ return &options->scopes[index];
+}
+
+/**
+ * Create a new options scope struct. This will hold a set of locals that are in
+ * scope surrounding the code that is being parsed.
+ */
+PRISM_EXPORTED_FUNCTION bool
+pm_options_scope_init(pm_options_scope_t *scope, size_t locals_count) {
+ scope->locals_count = locals_count;
+ scope->locals = xcalloc(locals_count, sizeof(pm_string_t));
+ return scope->locals != NULL;
+}
+
+/**
+ * Return a pointer to the local at the given index within the given scope.
+ */
+PRISM_EXPORTED_FUNCTION const pm_string_t *
+pm_options_scope_local_get(const pm_options_scope_t *scope, size_t index) {
+ return &scope->locals[index];
+}
+
+/**
+ * Free the internal memory associated with the options.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_options_free(pm_options_t *options) {
+ pm_string_free(&options->filepath);
+ pm_string_free(&options->encoding);
+
+ for (size_t scope_index = 0; scope_index < options->scopes_count; scope_index++) {
+ pm_options_scope_t *scope = &options->scopes[scope_index];
+
+ for (size_t local_index = 0; local_index < scope->locals_count; local_index++) {
+ pm_string_free(&scope->locals[local_index]);
+ }
+
+ xfree(scope->locals);
+ }
+
+ xfree(options->scopes);
+}
+
+/**
+ * Read a 32-bit unsigned integer from a pointer. This function is used to read
+ * the options that are passed into the parser from the Ruby implementation. It
+ * handles aligned and unaligned reads.
+ */
+static uint32_t
+pm_options_read_u32(const char *data) {
+ if (((uintptr_t) data) % sizeof(uint32_t) == 0) {
+ return *((uint32_t *) data);
+ } else {
+ uint32_t value;
+ memcpy(&value, data, sizeof(uint32_t));
+ return value;
+ }
+}
+
+/**
+ * Read a 32-bit signed integer from a pointer. This function is used to read
+ * the options that are passed into the parser from the Ruby implementation. It
+ * handles aligned and unaligned reads.
+ */
+static int32_t
+pm_options_read_s32(const char *data) {
+ if (((uintptr_t) data) % sizeof(int32_t) == 0) {
+ return *((int32_t *) data);
+ } else {
+ int32_t value;
+ memcpy(&value, data, sizeof(int32_t));
+ return value;
+ }
+}
+
+/**
+ * Deserialize an options struct from the given binary string. This is used to
+ * pass options to the parser from an FFI call so that consumers of the library
+ * from an FFI perspective don't have to worry about the structure of our
+ * options structs. Since the source of these calls will be from Ruby
+ * implementation internals we assume it is from a trusted source.
+ */
+void
+pm_options_read(pm_options_t *options, const char *data) {
+ options->line = 1; // default
+ if (data == NULL) return;
+
+ uint32_t filepath_length = pm_options_read_u32(data);
+ data += 4;
+
+ if (filepath_length > 0) {
+ pm_string_constant_init(&options->filepath, data, filepath_length);
+ data += filepath_length;
+ }
+
+ options->line = pm_options_read_s32(data);
+ data += 4;
+
+ uint32_t encoding_length = pm_options_read_u32(data);
+ data += 4;
+
+ if (encoding_length > 0) {
+ pm_string_constant_init(&options->encoding, data, encoding_length);
+ data += encoding_length;
+ }
+
+ options->frozen_string_literal = (int8_t) *data++;
+ options->command_line = (uint8_t) *data++;
+ options->version = (pm_options_version_t) *data++;
+
+ uint32_t scopes_count = pm_options_read_u32(data);
+ data += 4;
+
+ if (scopes_count > 0) {
+ if (!pm_options_scopes_init(options, scopes_count)) return;
+
+ for (size_t scope_index = 0; scope_index < scopes_count; scope_index++) {
+ uint32_t locals_count = pm_options_read_u32(data);
+ data += 4;
+
+ pm_options_scope_t *scope = &options->scopes[scope_index];
+ if (!pm_options_scope_init(scope, locals_count)) {
+ pm_options_free(options);
+ return;
+ }
+
+ for (size_t local_index = 0; local_index < locals_count; local_index++) {
+ uint32_t local_length = pm_options_read_u32(data);
+ data += 4;
+
+ pm_string_constant_init(&scope->locals[local_index], data, local_length);
+ data += local_length;
+ }
+ }
+ }
+}
+
+#if defined(__GNUC__) && (__GNUC__ >= 10)
+#pragma GCC diagnostic pop
+#endif
diff --git a/prism/options.h b/prism/options.h
new file mode 100644
index 0000000000..d0b46a0864
--- /dev/null
+++ b/prism/options.h
@@ -0,0 +1,305 @@
+/**
+ * @file options.h
+ *
+ * The options that can be passed to parsing.
+ */
+#ifndef PRISM_OPTIONS_H
+#define PRISM_OPTIONS_H
+
+#include "prism/defines.h"
+#include "prism/util/pm_string.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/**
+ * String literals should be made frozen.
+ */
+#define PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED ((int8_t) -1)
+
+/**
+ * String literals may be frozen or mutable depending on the implementation
+ * default.
+ */
+#define PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET ((int8_t) 0)
+
+/**
+ * String literals should be made mutable.
+ */
+#define PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED ((int8_t) 1)
+
+/**
+ * A scope of locals surrounding the code that is being parsed.
+ */
+typedef struct pm_options_scope {
+ /** The number of locals in the scope. */
+ size_t locals_count;
+
+ /** The names of the locals in the scope. */
+ pm_string_t *locals;
+} pm_options_scope_t;
+
+/**
+ * The version of Ruby syntax that we should be parsing with. This is used to
+ * allow consumers to specify which behavior they want in case they need to
+ * parse in the same way as a specific version of CRuby would have.
+ */
+typedef enum {
+ /** The current version of prism. */
+ PM_OPTIONS_VERSION_LATEST = 0,
+
+ /** The vendored version of prism in CRuby 3.3.0. */
+ PM_OPTIONS_VERSION_CRUBY_3_3_0 = 1
+} pm_options_version_t;
+
+/**
+ * The options that can be passed to the parser.
+ */
+typedef struct {
+ /** The name of the file that is currently being parsed. */
+ pm_string_t filepath;
+
+ /**
+ * The line within the file that the parse starts on. This value is
+ * 1-indexed.
+ */
+ int32_t line;
+
+ /**
+ * The name of the encoding that the source file is in. Note that this must
+ * correspond to a name that can be found with Encoding.find in Ruby.
+ */
+ pm_string_t encoding;
+
+ /**
+ * The number of scopes surrounding the code that is being parsed.
+ */
+ size_t scopes_count;
+
+ /**
+ * The scopes surrounding the code that is being parsed. For most parses
+ * this will be NULL, but for evals it will be the locals that are in scope
+ * surrounding the eval. Scopes are ordered from the outermost scope to the
+ * innermost one.
+ */
+ pm_options_scope_t *scopes;
+
+ /**
+ * The version of prism that we should be parsing with. This is used to
+ * allow consumers to specify which behavior they want in case they need to
+ * parse exactly as a specific version of CRuby.
+ */
+ pm_options_version_t version;
+
+ /** A bitset of the various options that were set on the command line. */
+ uint8_t command_line;
+
+ /**
+ * Whether or not the frozen string literal option has been set.
+ * May be:
+ * - PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED
+ * - PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED
+ * - PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET
+ */
+ int8_t frozen_string_literal;
+} pm_options_t;
+
+/**
+ * A bit representing whether or not the command line -a option was set. -a
+ * splits the input line $_ into $F.
+ */
+static const uint8_t PM_OPTIONS_COMMAND_LINE_A = 0x1;
+
+/**
+ * A bit representing whether or not the command line -e option was set. -e
+ * allow the user to specify a script to be executed. This is necessary for
+ * prism to know because certain warnings are not generated when -e is used.
+ */
+static const uint8_t PM_OPTIONS_COMMAND_LINE_E = 0x2;
+
+/**
+ * A bit representing whether or not the command line -l option was set. -l
+ * chomps the input line by default.
+ */
+static const uint8_t PM_OPTIONS_COMMAND_LINE_L = 0x4;
+
+/**
+ * A bit representing whether or not the command line -n option was set. -n
+ * wraps the script in a while gets loop.
+ */
+static const uint8_t PM_OPTIONS_COMMAND_LINE_N = 0x8;
+
+/**
+ * A bit representing whether or not the command line -p option was set. -p
+ * prints the value of $_ at the end of each loop.
+ */
+static const uint8_t PM_OPTIONS_COMMAND_LINE_P = 0x10;
+
+/**
+ * A bit representing whether or not the command line -x option was set. -x
+ * searches the input file for a shebang that matches the current Ruby engine.
+ */
+static const uint8_t PM_OPTIONS_COMMAND_LINE_X = 0x20;
+
+/**
+ * Set the filepath option on the given options struct.
+ *
+ * @param options The options struct to set the filepath on.
+ * @param filepath The filepath to set.
+ */
+PRISM_EXPORTED_FUNCTION void pm_options_filepath_set(pm_options_t *options, const char *filepath);
+
+/**
+ * Set the line option on the given options struct.
+ *
+ * @param options The options struct to set the line on.
+ * @param line The line to set.
+ */
+PRISM_EXPORTED_FUNCTION void pm_options_line_set(pm_options_t *options, int32_t line);
+
+/**
+ * Set the encoding option on the given options struct.
+ *
+ * @param options The options struct to set the encoding on.
+ * @param encoding The encoding to set.
+ */
+PRISM_EXPORTED_FUNCTION void pm_options_encoding_set(pm_options_t *options, const char *encoding);
+
+/**
+ * Set the frozen string literal option on the given options struct.
+ *
+ * @param options The options struct to set the frozen string literal value on.
+ * @param frozen_string_literal The frozen string literal value to set.
+ */
+PRISM_EXPORTED_FUNCTION void pm_options_frozen_string_literal_set(pm_options_t *options, bool frozen_string_literal);
+
+/**
+ * Sets the command line option on the given options struct.
+ *
+ * @param options The options struct to set the command line option on.
+ * @param command_line The command_line value to set.
+ */
+PRISM_EXPORTED_FUNCTION void pm_options_command_line_set(pm_options_t *options, uint8_t command_line);
+
+/**
+ * Set the version option on the given options struct by parsing the given
+ * string. If the string contains an invalid option, this returns false.
+ * Otherwise, it returns true.
+ *
+ * @param options The options struct to set the version on.
+ * @param version The version to set.
+ * @param length The length of the version string.
+ * @return Whether or not the version was parsed successfully.
+ */
+PRISM_EXPORTED_FUNCTION bool pm_options_version_set(pm_options_t *options, const char *version, size_t length);
+
+/**
+ * Allocate and zero out the scopes array on the given options struct.
+ *
+ * @param options The options struct to initialize the scopes array on.
+ * @param scopes_count The number of scopes to allocate.
+ * @return Whether or not the scopes array was initialized successfully.
+ */
+PRISM_EXPORTED_FUNCTION bool pm_options_scopes_init(pm_options_t *options, size_t scopes_count);
+
+/**
+ * Return a pointer to the scope at the given index within the given options.
+ *
+ * @param options The options struct to get the scope from.
+ * @param index The index of the scope to get.
+ * @return A pointer to the scope at the given index.
+ */
+PRISM_EXPORTED_FUNCTION const pm_options_scope_t * pm_options_scope_get(const pm_options_t *options, size_t index);
+
+/**
+ * Create a new options scope struct. This will hold a set of locals that are in
+ * scope surrounding the code that is being parsed.
+ *
+ * @param scope The scope struct to initialize.
+ * @param locals_count The number of locals to allocate.
+ * @return Whether or not the scope was initialized successfully.
+ */
+PRISM_EXPORTED_FUNCTION bool pm_options_scope_init(pm_options_scope_t *scope, size_t locals_count);
+
+/**
+ * Return a pointer to the local at the given index within the given scope.
+ *
+ * @param scope The scope struct to get the local from.
+ * @param index The index of the local to get.
+ * @return A pointer to the local at the given index.
+ */
+PRISM_EXPORTED_FUNCTION const pm_string_t * pm_options_scope_local_get(const pm_options_scope_t *scope, size_t index);
+
+/**
+ * Free the internal memory associated with the options.
+ *
+ * @param options The options struct whose internal memory should be freed.
+ */
+PRISM_EXPORTED_FUNCTION void pm_options_free(pm_options_t *options);
+
+/**
+ * Deserialize an options struct from the given binary string. This is used to
+ * pass options to the parser from an FFI call so that consumers of the library
+ * from an FFI perspective don't have to worry about the structure of our
+ * options structs. Since the source of these calls will be from Ruby
+ * implementation internals we assume it is from a trusted source.
+ *
+ * `data` is assumed to be a valid pointer pointing to well-formed data. The
+ * layout of this data should be the same every time, and is described below:
+ *
+ * | # bytes | field |
+ * | ------- | -------------------------- |
+ * | `4` | the length of the filepath |
+ * | ... | the filepath bytes |
+ * | `4` | the line number |
+ * | `4` | the length the encoding |
+ * | ... | the encoding bytes |
+ * | `1` | frozen string literal |
+ * | `1` | -p command line option |
+ * | `1` | -n command line option |
+ * | `1` | -l command line option |
+ * | `1` | -a command line option |
+ * | `1` | the version |
+ * | `4` | the number of scopes |
+ * | ... | the scopes |
+ *
+ * The version field is an enum, so it should be one of the following values:
+ *
+ * | value | version |
+ * | ----- | ------------------------- |
+ * | `0` | use the latest version of prism |
+ * | `1` | use the version of prism that is vendored in CRuby 3.3.0 |
+ *
+ * Each scope is laid out as follows:
+ *
+ * | # bytes | field |
+ * | ------- | -------------------------- |
+ * | `4` | the number of locals |
+ * | ... | the locals |
+ *
+ * Each local is laid out as follows:
+ *
+ * | # bytes | field |
+ * | ------- | -------------------------- |
+ * | `4` | the length of the local |
+ * | ... | the local bytes |
+ *
+ * Some additional things to note about this layout:
+ *
+ * * The filepath can have a length of 0, in which case we'll consider it an
+ * empty string.
+ * * The line number should be 0-indexed.
+ * * The encoding can have a length of 0, in which case we'll use the default
+ * encoding (UTF-8). If it's not 0, it should correspond to a name of an
+ * encoding that can be passed to `Encoding.find` in Ruby.
+ * * The frozen string literal and suppress warnings fields are booleans, so
+ * their values should be either 0 or 1.
+ * * The number of scopes can be 0.
+ *
+ * @param options The options struct to deserialize into.
+ * @param data The binary string to deserialize from.
+ */
+void pm_options_read(pm_options_t *options, const char *data);
+
+#endif
diff --git a/prism/pack.c b/prism/pack.c
new file mode 100644
index 0000000000..1388ca8a3b
--- /dev/null
+++ b/prism/pack.c
@@ -0,0 +1,509 @@
+#include "prism/pack.h"
+
+// We optionally support parsing String#pack templates. For systems that don't
+// want or need this functionality, it can be turned off with the
+// PRISM_EXCLUDE_PACK define.
+#ifdef PRISM_EXCLUDE_PACK
+
+void pm_pack_parse(void) {}
+
+#else
+
+#include <stdbool.h>
+#include <errno.h>
+
+static uintmax_t
+strtoumaxc(const char **format) {
+ uintmax_t value = 0;
+ while (**format >= '0' && **format <= '9') {
+ if (value > UINTMAX_MAX / 10) {
+ errno = ERANGE;
+ }
+ value = value * 10 + ((uintmax_t) (**format - '0'));
+ (*format)++;
+ }
+ return value;
+}
+
+PRISM_EXPORTED_FUNCTION pm_pack_result
+pm_pack_parse(
+ pm_pack_variant variant,
+ const char **format,
+ const char *format_end,
+ pm_pack_type *type,
+ pm_pack_signed *signed_type,
+ pm_pack_endian *endian,
+ pm_pack_size *size,
+ pm_pack_length_type *length_type,
+ uint64_t *length,
+ pm_pack_encoding *encoding
+) {
+ if (*encoding == PM_PACK_ENCODING_START) {
+ *encoding = PM_PACK_ENCODING_US_ASCII;
+ }
+
+ if (*format == format_end) {
+ *type = PM_PACK_END;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_ENDIAN_NA;
+ *size = PM_PACK_SIZE_NA;
+ *length_type = PM_PACK_LENGTH_NA;
+ return PM_PACK_OK;
+ }
+
+ *length_type = PM_PACK_LENGTH_FIXED;
+ *length = 1;
+ bool length_changed_allowed = true;
+
+ char directive = **format;
+ (*format)++;
+ switch (directive) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\v':
+ case '\f':
+ case '\r':
+ *type = PM_PACK_SPACE;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_ENDIAN_NA;
+ *size = PM_PACK_SIZE_NA;
+ *length_type = PM_PACK_LENGTH_NA;
+ *length = 0;
+ return PM_PACK_OK;
+ case '#':
+ while ((*format < format_end) && (**format != '\n')) {
+ (*format)++;
+ }
+ *type = PM_PACK_COMMENT;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_ENDIAN_NA;
+ *size = PM_PACK_SIZE_NA;
+ *length_type = PM_PACK_LENGTH_NA;
+ *length = 0;
+ return PM_PACK_OK;
+ case 'C':
+ *type = PM_PACK_INTEGER;
+ *signed_type = PM_PACK_UNSIGNED;
+ *endian = PM_PACK_AGNOSTIC_ENDIAN;
+ *size = PM_PACK_SIZE_8;
+ break;
+ case 'S':
+ *type = PM_PACK_INTEGER;
+ *signed_type = PM_PACK_UNSIGNED;
+ *endian = PM_PACK_NATIVE_ENDIAN;
+ *size = PM_PACK_SIZE_16;
+ break;
+ case 'L':
+ *type = PM_PACK_INTEGER;
+ *signed_type = PM_PACK_UNSIGNED;
+ *endian = PM_PACK_NATIVE_ENDIAN;
+ *size = PM_PACK_SIZE_32;
+ break;
+ case 'Q':
+ *type = PM_PACK_INTEGER;
+ *signed_type = PM_PACK_UNSIGNED;
+ *endian = PM_PACK_NATIVE_ENDIAN;
+ *size = PM_PACK_SIZE_64;
+ break;
+ case 'J':
+ *type = PM_PACK_INTEGER;
+ *signed_type = PM_PACK_UNSIGNED;
+ *endian = PM_PACK_NATIVE_ENDIAN;
+ *size = PM_PACK_SIZE_P;
+ break;
+ case 'c':
+ *type = PM_PACK_INTEGER;
+ *signed_type = PM_PACK_SIGNED;
+ *endian = PM_PACK_AGNOSTIC_ENDIAN;
+ *size = PM_PACK_SIZE_8;
+ break;
+ case 's':
+ *type = PM_PACK_INTEGER;
+ *signed_type = PM_PACK_SIGNED;
+ *endian = PM_PACK_NATIVE_ENDIAN;
+ *size = PM_PACK_SIZE_16;
+ break;
+ case 'l':
+ *type = PM_PACK_INTEGER;
+ *signed_type = PM_PACK_SIGNED;
+ *endian = PM_PACK_NATIVE_ENDIAN;
+ *size = PM_PACK_SIZE_32;
+ break;
+ case 'q':
+ *type = PM_PACK_INTEGER;
+ *signed_type = PM_PACK_SIGNED;
+ *endian = PM_PACK_NATIVE_ENDIAN;
+ *size = PM_PACK_SIZE_64;
+ break;
+ case 'j':
+ *type = PM_PACK_INTEGER;
+ *signed_type = PM_PACK_SIGNED;
+ *endian = PM_PACK_NATIVE_ENDIAN;
+ *size = PM_PACK_SIZE_P;
+ break;
+ case 'I':
+ *type = PM_PACK_INTEGER;
+ *signed_type = PM_PACK_UNSIGNED;
+ *endian = PM_PACK_NATIVE_ENDIAN;
+ *size = PM_PACK_SIZE_INT;
+ break;
+ case 'i':
+ *type = PM_PACK_INTEGER;
+ *signed_type = PM_PACK_SIGNED;
+ *endian = PM_PACK_NATIVE_ENDIAN;
+ *size = PM_PACK_SIZE_INT;
+ break;
+ case 'n':
+ *type = PM_PACK_INTEGER;
+ *signed_type = PM_PACK_UNSIGNED;
+ *endian = PM_PACK_BIG_ENDIAN;
+ *size = PM_PACK_SIZE_16;
+ length_changed_allowed = false;
+ break;
+ case 'N':
+ *type = PM_PACK_INTEGER;
+ *signed_type = PM_PACK_UNSIGNED;
+ *endian = PM_PACK_BIG_ENDIAN;
+ *size = PM_PACK_SIZE_32;
+ length_changed_allowed = false;
+ break;
+ case 'v':
+ *type = PM_PACK_INTEGER;
+ *signed_type = PM_PACK_UNSIGNED;
+ *endian = PM_PACK_LITTLE_ENDIAN;
+ *size = PM_PACK_SIZE_16;
+ length_changed_allowed = false;
+ break;
+ case 'V':
+ *type = PM_PACK_INTEGER;
+ *signed_type = PM_PACK_UNSIGNED;
+ *endian = PM_PACK_LITTLE_ENDIAN;
+ *size = PM_PACK_SIZE_32;
+ length_changed_allowed = false;
+ break;
+ case 'U':
+ *type = PM_PACK_UTF8;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_ENDIAN_NA;
+ *size = PM_PACK_SIZE_NA;
+ break;
+ case 'w':
+ *type = PM_PACK_BER;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_ENDIAN_NA;
+ *size = PM_PACK_SIZE_NA;
+ break;
+ case 'D':
+ case 'd':
+ *type = PM_PACK_FLOAT;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_NATIVE_ENDIAN;
+ *size = PM_PACK_SIZE_64;
+ break;
+ case 'F':
+ case 'f':
+ *type = PM_PACK_FLOAT;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_NATIVE_ENDIAN;
+ *size = PM_PACK_SIZE_32;
+ break;
+ case 'E':
+ *type = PM_PACK_FLOAT;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_LITTLE_ENDIAN;
+ *size = PM_PACK_SIZE_64;
+ break;
+ case 'e':
+ *type = PM_PACK_FLOAT;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_LITTLE_ENDIAN;
+ *size = PM_PACK_SIZE_32;
+ break;
+ case 'G':
+ *type = PM_PACK_FLOAT;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_BIG_ENDIAN;
+ *size = PM_PACK_SIZE_64;
+ break;
+ case 'g':
+ *type = PM_PACK_FLOAT;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_BIG_ENDIAN;
+ *size = PM_PACK_SIZE_32;
+ break;
+ case 'A':
+ *type = PM_PACK_STRING_SPACE_PADDED;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_ENDIAN_NA;
+ *size = PM_PACK_SIZE_NA;
+ break;
+ case 'a':
+ *type = PM_PACK_STRING_NULL_PADDED;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_ENDIAN_NA;
+ *size = PM_PACK_SIZE_NA;
+ break;
+ case 'Z':
+ *type = PM_PACK_STRING_NULL_TERMINATED;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_ENDIAN_NA;
+ *size = PM_PACK_SIZE_NA;
+ break;
+ case 'B':
+ *type = PM_PACK_STRING_MSB;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_ENDIAN_NA;
+ *size = PM_PACK_SIZE_NA;
+ break;
+ case 'b':
+ *type = PM_PACK_STRING_LSB;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_ENDIAN_NA;
+ *size = PM_PACK_SIZE_NA;
+ break;
+ case 'H':
+ *type = PM_PACK_STRING_HEX_HIGH;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_ENDIAN_NA;
+ *size = PM_PACK_SIZE_NA;
+ break;
+ case 'h':
+ *type = PM_PACK_STRING_HEX_LOW;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_ENDIAN_NA;
+ *size = PM_PACK_SIZE_NA;
+ break;
+ case 'u':
+ *type = PM_PACK_STRING_UU;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_ENDIAN_NA;
+ *size = PM_PACK_SIZE_NA;
+ break;
+ case 'M':
+ *type = PM_PACK_STRING_MIME;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_ENDIAN_NA;
+ *size = PM_PACK_SIZE_NA;
+ break;
+ case 'm':
+ *type = PM_PACK_STRING_BASE64;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_ENDIAN_NA;
+ *size = PM_PACK_SIZE_NA;
+ break;
+ case 'P':
+ *type = PM_PACK_STRING_FIXED;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_ENDIAN_NA;
+ *size = PM_PACK_SIZE_NA;
+ break;
+ case 'p':
+ *type = PM_PACK_STRING_POINTER;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_ENDIAN_NA;
+ *size = PM_PACK_SIZE_NA;
+ break;
+ case '@':
+ *type = PM_PACK_MOVE;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_ENDIAN_NA;
+ *size = PM_PACK_SIZE_NA;
+ break;
+ case 'X':
+ *type = PM_PACK_BACK;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_ENDIAN_NA;
+ *size = PM_PACK_SIZE_NA;
+ break;
+ case 'x':
+ *type = PM_PACK_NULL;
+ *signed_type = PM_PACK_SIGNED_NA;
+ *endian = PM_PACK_ENDIAN_NA;
+ *size = PM_PACK_SIZE_NA;
+ break;
+ case '%':
+ return PM_PACK_ERROR_UNSUPPORTED_DIRECTIVE;
+ default:
+ return PM_PACK_ERROR_UNKNOWN_DIRECTIVE;
+ }
+
+ bool explicit_endian = false;
+
+ while (*format < format_end) {
+ switch (**format) {
+ case '_':
+ case '!':
+ (*format)++;
+ if (*type != PM_PACK_INTEGER || !length_changed_allowed) {
+ return PM_PACK_ERROR_BANG_NOT_ALLOWED;
+ }
+ switch (*size) {
+ case PM_PACK_SIZE_SHORT:
+ case PM_PACK_SIZE_INT:
+ case PM_PACK_SIZE_LONG:
+ case PM_PACK_SIZE_LONG_LONG:
+ break;
+ case PM_PACK_SIZE_16:
+ *size = PM_PACK_SIZE_SHORT;
+ break;
+ case PM_PACK_SIZE_32:
+ *size = PM_PACK_SIZE_LONG;
+ break;
+ case PM_PACK_SIZE_64:
+ *size = PM_PACK_SIZE_LONG_LONG;
+ break;
+ case PM_PACK_SIZE_P:
+ break;
+ default:
+ return PM_PACK_ERROR_BANG_NOT_ALLOWED;
+ }
+ break;
+ case '<':
+ (*format)++;
+ if (explicit_endian) {
+ return PM_PACK_ERROR_DOUBLE_ENDIAN;
+ }
+ *endian = PM_PACK_LITTLE_ENDIAN;
+ explicit_endian = true;
+ break;
+ case '>':
+ (*format)++;
+ if (explicit_endian) {
+ return PM_PACK_ERROR_DOUBLE_ENDIAN;
+ }
+ *endian = PM_PACK_BIG_ENDIAN;
+ explicit_endian = true;
+ break;
+ default:
+ goto exit_modifier_loop;
+ }
+ }
+
+exit_modifier_loop:
+
+ if (variant == PM_PACK_VARIANT_UNPACK && *type == PM_PACK_MOVE) {
+ *length = 0;
+ }
+
+ if (*format < format_end) {
+ if (**format == '*') {
+ switch (*type) {
+ case PM_PACK_NULL:
+ case PM_PACK_BACK:
+ switch (variant) {
+ case PM_PACK_VARIANT_PACK:
+ *length_type = PM_PACK_LENGTH_FIXED;
+ break;
+ case PM_PACK_VARIANT_UNPACK:
+ *length_type = PM_PACK_LENGTH_MAX;
+ break;
+ }
+ *length = 0;
+ break;
+
+ case PM_PACK_MOVE:
+ switch (variant) {
+ case PM_PACK_VARIANT_PACK:
+ *length_type = PM_PACK_LENGTH_FIXED;
+ break;
+ case PM_PACK_VARIANT_UNPACK:
+ *length_type = PM_PACK_LENGTH_RELATIVE;
+ break;
+ }
+ *length = 0;
+ break;
+
+ case PM_PACK_STRING_UU:
+ *length_type = PM_PACK_LENGTH_FIXED;
+ *length = 0;
+ break;
+
+ case PM_PACK_STRING_FIXED:
+ switch (variant) {
+ case PM_PACK_VARIANT_PACK:
+ *length_type = PM_PACK_LENGTH_FIXED;
+ *length = 1;
+ break;
+ case PM_PACK_VARIANT_UNPACK:
+ *length_type = PM_PACK_LENGTH_MAX;
+ *length = 0;
+ break;
+ }
+ break;
+
+ case PM_PACK_STRING_MIME:
+ case PM_PACK_STRING_BASE64:
+ *length_type = PM_PACK_LENGTH_FIXED;
+ *length = 1;
+ break;
+
+ default:
+ *length_type = PM_PACK_LENGTH_MAX;
+ *length = 0;
+ break;
+ }
+
+ (*format)++;
+ } else if (**format >= '0' && **format <= '9') {
+ errno = 0;
+ *length_type = PM_PACK_LENGTH_FIXED;
+ #if UINTMAX_MAX < UINT64_MAX
+ #error "prism's design assumes uintmax_t is at least as large as uint64_t"
+ #endif
+ uintmax_t length_max = strtoumaxc(format);
+ if (errno || length_max > UINT64_MAX) {
+ return PM_PACK_ERROR_LENGTH_TOO_BIG;
+ }
+ *length = (uint64_t) length_max;
+ }
+ }
+
+ switch (*type) {
+ case PM_PACK_UTF8:
+ /* if encoding is US-ASCII, upgrade to UTF-8 */
+ if (*encoding == PM_PACK_ENCODING_US_ASCII) {
+ *encoding = PM_PACK_ENCODING_UTF_8;
+ }
+ break;
+ case PM_PACK_STRING_MIME:
+ case PM_PACK_STRING_BASE64:
+ case PM_PACK_STRING_UU:
+ /* keep US-ASCII (do nothing) */
+ break;
+ default:
+ /* fall back to BINARY */
+ *encoding = PM_PACK_ENCODING_ASCII_8BIT;
+ break;
+ }
+
+ return PM_PACK_OK;
+}
+
+PRISM_EXPORTED_FUNCTION size_t
+pm_size_to_native(pm_pack_size size) {
+ switch (size) {
+ case PM_PACK_SIZE_SHORT:
+ return sizeof(short);
+ case PM_PACK_SIZE_INT:
+ return sizeof(int);
+ case PM_PACK_SIZE_LONG:
+ return sizeof(long);
+ case PM_PACK_SIZE_LONG_LONG:
+ return sizeof(long long);
+ case PM_PACK_SIZE_8:
+ return 1;
+ case PM_PACK_SIZE_16:
+ return 2;
+ case PM_PACK_SIZE_32:
+ return 4;
+ case PM_PACK_SIZE_64:
+ return 8;
+ case PM_PACK_SIZE_P:
+ return sizeof(void *);
+ default:
+ return 0;
+ }
+}
+
+#endif
diff --git a/prism/pack.h b/prism/pack.h
new file mode 100644
index 0000000000..0b0b4b19cc
--- /dev/null
+++ b/prism/pack.h
@@ -0,0 +1,163 @@
+/**
+ * @file pack.h
+ *
+ * A pack template string parser.
+ */
+#ifndef PRISM_PACK_H
+#define PRISM_PACK_H
+
+#include "prism/defines.h"
+
+// We optionally support parsing String#pack templates. For systems that don't
+// want or need this functionality, it can be turned off with the
+// PRISM_EXCLUDE_PACK define.
+#ifdef PRISM_EXCLUDE_PACK
+
+void pm_pack_parse(void);
+
+#else
+
+#include <stdint.h>
+#include <stdlib.h>
+
+/** The version of the pack template language that we are parsing. */
+typedef enum pm_pack_version {
+ PM_PACK_VERSION_3_2_0
+} pm_pack_version;
+
+/** The type of pack template we are parsing. */
+typedef enum pm_pack_variant {
+ PM_PACK_VARIANT_PACK,
+ PM_PACK_VARIANT_UNPACK
+} pm_pack_variant;
+
+/** A directive within the pack template. */
+typedef enum pm_pack_type {
+ PM_PACK_SPACE,
+ PM_PACK_COMMENT,
+ PM_PACK_INTEGER,
+ PM_PACK_UTF8,
+ PM_PACK_BER,
+ PM_PACK_FLOAT,
+ PM_PACK_STRING_SPACE_PADDED,
+ PM_PACK_STRING_NULL_PADDED,
+ PM_PACK_STRING_NULL_TERMINATED,
+ PM_PACK_STRING_MSB,
+ PM_PACK_STRING_LSB,
+ PM_PACK_STRING_HEX_HIGH,
+ PM_PACK_STRING_HEX_LOW,
+ PM_PACK_STRING_UU,
+ PM_PACK_STRING_MIME,
+ PM_PACK_STRING_BASE64,
+ PM_PACK_STRING_FIXED,
+ PM_PACK_STRING_POINTER,
+ PM_PACK_MOVE,
+ PM_PACK_BACK,
+ PM_PACK_NULL,
+ PM_PACK_END
+} pm_pack_type;
+
+/** The signness of a pack directive. */
+typedef enum pm_pack_signed {
+ PM_PACK_UNSIGNED,
+ PM_PACK_SIGNED,
+ PM_PACK_SIGNED_NA
+} pm_pack_signed;
+
+/** The endianness of a pack directive. */
+typedef enum pm_pack_endian {
+ PM_PACK_AGNOSTIC_ENDIAN,
+ PM_PACK_LITTLE_ENDIAN, // aka 'VAX', or 'V'
+ PM_PACK_BIG_ENDIAN, // aka 'network', or 'N'
+ PM_PACK_NATIVE_ENDIAN,
+ PM_PACK_ENDIAN_NA
+} pm_pack_endian;
+
+/** The size of an integer pack directive. */
+typedef enum pm_pack_size {
+ PM_PACK_SIZE_SHORT,
+ PM_PACK_SIZE_INT,
+ PM_PACK_SIZE_LONG,
+ PM_PACK_SIZE_LONG_LONG,
+ PM_PACK_SIZE_8,
+ PM_PACK_SIZE_16,
+ PM_PACK_SIZE_32,
+ PM_PACK_SIZE_64,
+ PM_PACK_SIZE_P,
+ PM_PACK_SIZE_NA
+} pm_pack_size;
+
+/** The type of length of a pack directive. */
+typedef enum pm_pack_length_type {
+ PM_PACK_LENGTH_FIXED,
+ PM_PACK_LENGTH_MAX,
+ PM_PACK_LENGTH_RELATIVE, // special case for unpack @*
+ PM_PACK_LENGTH_NA
+} pm_pack_length_type;
+
+/** The type of encoding for a pack template string. */
+typedef enum pm_pack_encoding {
+ PM_PACK_ENCODING_START,
+ PM_PACK_ENCODING_ASCII_8BIT,
+ PM_PACK_ENCODING_US_ASCII,
+ PM_PACK_ENCODING_UTF_8
+} pm_pack_encoding;
+
+/** The result of parsing a pack template. */
+typedef enum pm_pack_result {
+ PM_PACK_OK,
+ PM_PACK_ERROR_UNSUPPORTED_DIRECTIVE,
+ PM_PACK_ERROR_UNKNOWN_DIRECTIVE,
+ PM_PACK_ERROR_LENGTH_TOO_BIG,
+ PM_PACK_ERROR_BANG_NOT_ALLOWED,
+ PM_PACK_ERROR_DOUBLE_ENDIAN
+} pm_pack_result;
+
+/**
+ * Parse a single directive from a pack or unpack format string.
+ *
+ * @param variant (in) pack or unpack
+ * @param format (in, out) the start of the next directive to parse on calling,
+ * and advanced beyond the parsed directive on return, or as much of it as
+ * was consumed until an error was encountered
+ * @param format_end (in) the end of the format string
+ * @param type (out) the type of the directive
+ * @param signed_type (out) whether the value is signed
+ * @param endian (out) the endianness of the value
+ * @param size (out) the size of the value
+ * @param length_type (out) what kind of length is specified
+ * @param length (out) the length of the directive
+ * @param encoding (in, out) takes the current encoding of the string which
+ * would result from parsing the whole format string, and returns a possibly
+ * changed directive - the encoding should be `PM_PACK_ENCODING_START` when
+ * pm_pack_parse is called for the first directive in a format string
+ *
+ * @return `PM_PACK_OK` on success or `PM_PACK_ERROR_*` on error
+ * @note Consult Ruby documentation for the meaning of directives.
+ */
+PRISM_EXPORTED_FUNCTION pm_pack_result
+pm_pack_parse(
+ pm_pack_variant variant,
+ const char **format,
+ const char *format_end,
+ pm_pack_type *type,
+ pm_pack_signed *signed_type,
+ pm_pack_endian *endian,
+ pm_pack_size *size,
+ pm_pack_length_type *length_type,
+ uint64_t *length,
+ pm_pack_encoding *encoding
+);
+
+/**
+ * Prism abstracts sizes away from the native system - this converts an abstract
+ * size to a native size.
+ *
+ * @param size The abstract size to convert.
+ * @return The native size.
+ */
+PRISM_EXPORTED_FUNCTION size_t pm_size_to_native(pm_pack_size size);
+
+#endif
+
+#endif
diff --git a/prism/parser.h b/prism/parser.h
new file mode 100644
index 0000000000..8054e332f7
--- /dev/null
+++ b/prism/parser.h
@@ -0,0 +1,891 @@
+/**
+ * @file parser.h
+ *
+ * The parser used to parse Ruby source.
+ */
+#ifndef PRISM_PARSER_H
+#define PRISM_PARSER_H
+
+#include "prism/defines.h"
+#include "prism/ast.h"
+#include "prism/encoding.h"
+#include "prism/options.h"
+#include "prism/static_literals.h"
+#include "prism/util/pm_constant_pool.h"
+#include "prism/util/pm_list.h"
+#include "prism/util/pm_newline_list.h"
+#include "prism/util/pm_string.h"
+
+#include <stdbool.h>
+
+/**
+ * This enum provides various bits that represent different kinds of states that
+ * the lexer can track. This is used to determine which kind of token to return
+ * based on the context of the parser.
+ */
+typedef enum {
+ PM_LEX_STATE_BIT_BEG,
+ PM_LEX_STATE_BIT_END,
+ PM_LEX_STATE_BIT_ENDARG,
+ PM_LEX_STATE_BIT_ENDFN,
+ PM_LEX_STATE_BIT_ARG,
+ PM_LEX_STATE_BIT_CMDARG,
+ PM_LEX_STATE_BIT_MID,
+ PM_LEX_STATE_BIT_FNAME,
+ PM_LEX_STATE_BIT_DOT,
+ PM_LEX_STATE_BIT_CLASS,
+ PM_LEX_STATE_BIT_LABEL,
+ PM_LEX_STATE_BIT_LABELED,
+ PM_LEX_STATE_BIT_FITEM
+} pm_lex_state_bit_t;
+
+/**
+ * This enum combines the various bits from the above enum into individual
+ * values that represent the various states of the lexer.
+ */
+typedef enum {
+ PM_LEX_STATE_NONE = 0,
+ PM_LEX_STATE_BEG = (1 << PM_LEX_STATE_BIT_BEG),
+ PM_LEX_STATE_END = (1 << PM_LEX_STATE_BIT_END),
+ PM_LEX_STATE_ENDARG = (1 << PM_LEX_STATE_BIT_ENDARG),
+ PM_LEX_STATE_ENDFN = (1 << PM_LEX_STATE_BIT_ENDFN),
+ PM_LEX_STATE_ARG = (1 << PM_LEX_STATE_BIT_ARG),
+ PM_LEX_STATE_CMDARG = (1 << PM_LEX_STATE_BIT_CMDARG),
+ PM_LEX_STATE_MID = (1 << PM_LEX_STATE_BIT_MID),
+ PM_LEX_STATE_FNAME = (1 << PM_LEX_STATE_BIT_FNAME),
+ PM_LEX_STATE_DOT = (1 << PM_LEX_STATE_BIT_DOT),
+ PM_LEX_STATE_CLASS = (1 << PM_LEX_STATE_BIT_CLASS),
+ PM_LEX_STATE_LABEL = (1 << PM_LEX_STATE_BIT_LABEL),
+ PM_LEX_STATE_LABELED = (1 << PM_LEX_STATE_BIT_LABELED),
+ PM_LEX_STATE_FITEM = (1 << PM_LEX_STATE_BIT_FITEM),
+ PM_LEX_STATE_BEG_ANY = PM_LEX_STATE_BEG | PM_LEX_STATE_MID | PM_LEX_STATE_CLASS,
+ PM_LEX_STATE_ARG_ANY = PM_LEX_STATE_ARG | PM_LEX_STATE_CMDARG,
+ PM_LEX_STATE_END_ANY = PM_LEX_STATE_END | PM_LEX_STATE_ENDARG | PM_LEX_STATE_ENDFN
+} pm_lex_state_t;
+
+/**
+ * The type of quote that a heredoc uses.
+ */
+typedef enum {
+ PM_HEREDOC_QUOTE_NONE,
+ PM_HEREDOC_QUOTE_SINGLE = '\'',
+ PM_HEREDOC_QUOTE_DOUBLE = '"',
+ PM_HEREDOC_QUOTE_BACKTICK = '`',
+} pm_heredoc_quote_t;
+
+/**
+ * The type of indentation that a heredoc uses.
+ */
+typedef enum {
+ PM_HEREDOC_INDENT_NONE,
+ PM_HEREDOC_INDENT_DASH,
+ PM_HEREDOC_INDENT_TILDE,
+} pm_heredoc_indent_t;
+
+/**
+ * When lexing Ruby source, the lexer has a small amount of state to tell which
+ * kind of token it is currently lexing. For example, when we find the start of
+ * a string, the first token that we return is a TOKEN_STRING_BEGIN token. After
+ * that the lexer is now in the PM_LEX_STRING mode, and will return tokens that
+ * are found as part of a string.
+ */
+typedef struct pm_lex_mode {
+ /** The type of this lex mode. */
+ enum {
+ /** This state is used when any given token is being lexed. */
+ PM_LEX_DEFAULT,
+
+ /**
+ * This state is used when we're lexing as normal but inside an embedded
+ * expression of a string.
+ */
+ PM_LEX_EMBEXPR,
+
+ /**
+ * This state is used when we're lexing a variable that is embedded
+ * directly inside of a string with the # shorthand.
+ */
+ PM_LEX_EMBVAR,
+
+ /** This state is used when you are inside the content of a heredoc. */
+ PM_LEX_HEREDOC,
+
+ /**
+ * This state is used when we are lexing a list of tokens, as in a %w
+ * word list literal or a %i symbol list literal.
+ */
+ PM_LEX_LIST,
+
+ /**
+ * This state is used when a regular expression has been begun and we
+ * are looking for the terminator.
+ */
+ PM_LEX_REGEXP,
+
+ /**
+ * This state is used when we are lexing a string or a string-like
+ * token, as in string content with either quote or an xstring.
+ */
+ PM_LEX_STRING
+ } mode;
+
+ /** The data associated with this type of lex mode. */
+ union {
+ struct {
+ /** This keeps track of the nesting level of the list. */
+ size_t nesting;
+
+ /** Whether or not interpolation is allowed in this list. */
+ bool interpolation;
+
+ /**
+ * When lexing a list, it takes into account balancing the
+ * terminator if the terminator is one of (), [], {}, or <>.
+ */
+ uint8_t incrementor;
+
+ /** This is the terminator of the list literal. */
+ uint8_t terminator;
+
+ /**
+ * This is the character set that should be used to delimit the
+ * tokens within the list.
+ */
+ uint8_t breakpoints[11];
+ } list;
+
+ struct {
+ /**
+ * This keeps track of the nesting level of the regular expression.
+ */
+ size_t nesting;
+
+ /**
+ * When lexing a regular expression, it takes into account balancing
+ * the terminator if the terminator is one of (), [], {}, or <>.
+ */
+ uint8_t incrementor;
+
+ /** This is the terminator of the regular expression. */
+ uint8_t terminator;
+
+ /**
+ * This is the character set that should be used to delimit the
+ * tokens within the regular expression.
+ */
+ uint8_t breakpoints[7];
+ } regexp;
+
+ struct {
+ /** This keeps track of the nesting level of the string. */
+ size_t nesting;
+
+ /** Whether or not interpolation is allowed in this string. */
+ bool interpolation;
+
+ /**
+ * Whether or not at the end of the string we should allow a :,
+ * which would indicate this was a dynamic symbol instead of a
+ * string.
+ */
+ bool label_allowed;
+
+ /**
+ * When lexing a string, it takes into account balancing the
+ * terminator if the terminator is one of (), [], {}, or <>.
+ */
+ uint8_t incrementor;
+
+ /**
+ * This is the terminator of the string. It is typically either a
+ * single or double quote.
+ */
+ uint8_t terminator;
+
+ /**
+ * This is the character set that should be used to delimit the
+ * tokens within the string.
+ */
+ uint8_t breakpoints[7];
+ } string;
+
+ struct {
+ /** A pointer to the start of the heredoc identifier. */
+ const uint8_t *ident_start;
+
+ /** The length of the heredoc identifier. */
+ size_t ident_length;
+
+ /** The type of quote that the heredoc uses. */
+ pm_heredoc_quote_t quote;
+
+ /** The type of indentation that the heredoc uses. */
+ pm_heredoc_indent_t indent;
+
+ /**
+ * This is the pointer to the character where lexing should resume
+ * once the heredoc has been completely processed.
+ */
+ const uint8_t *next_start;
+
+ /**
+ * This is used to track the amount of common whitespace on each
+ * line so that we know how much to dedent each line in the case of
+ * a tilde heredoc.
+ */
+ size_t common_whitespace;
+
+ /** True if the previous token ended with a line continuation. */
+ bool line_continuation;
+ } heredoc;
+ } as;
+
+ /** The previous lex state so that it knows how to pop. */
+ struct pm_lex_mode *prev;
+} pm_lex_mode_t;
+
+/**
+ * We pre-allocate a certain number of lex states in order to avoid having to
+ * call malloc too many times while parsing. You really shouldn't need more than
+ * this because you only really nest deeply when doing string interpolation.
+ */
+#define PM_LEX_STACK_SIZE 4
+
+/**
+ * The parser used to parse Ruby source.
+ */
+typedef struct pm_parser pm_parser_t;
+
+/**
+ * While parsing, we keep track of a stack of contexts. This is helpful for
+ * error recovery so that we can pop back to a previous context when we hit a
+ * token that is understood by a parent context but not by the current context.
+ */
+typedef enum {
+ /** a null context, used for returning a value from a function */
+ PM_CONTEXT_NONE = 0,
+
+ /** a begin statement */
+ PM_CONTEXT_BEGIN,
+
+ /** an ensure statement with an explicit begin */
+ PM_CONTEXT_BEGIN_ENSURE,
+
+ /** a rescue else statement with an explicit begin */
+ PM_CONTEXT_BEGIN_ELSE,
+
+ /** a rescue statement with an explicit begin */
+ PM_CONTEXT_BEGIN_RESCUE,
+
+ /** expressions in block arguments using braces */
+ PM_CONTEXT_BLOCK_BRACES,
+
+ /** expressions in block arguments using do..end */
+ PM_CONTEXT_BLOCK_KEYWORDS,
+
+ /** an ensure statement within a do..end block */
+ PM_CONTEXT_BLOCK_ENSURE,
+
+ /** a rescue else statement within a do..end block */
+ PM_CONTEXT_BLOCK_ELSE,
+
+ /** a rescue statement within a do..end block */
+ PM_CONTEXT_BLOCK_RESCUE,
+
+ /** a case when statements */
+ PM_CONTEXT_CASE_WHEN,
+
+ /** a case in statements */
+ PM_CONTEXT_CASE_IN,
+
+ /** a class declaration */
+ PM_CONTEXT_CLASS,
+
+ /** an ensure statement within a class statement */
+ PM_CONTEXT_CLASS_ENSURE,
+
+ /** a rescue else statement within a class statement */
+ PM_CONTEXT_CLASS_ELSE,
+
+ /** a rescue statement within a class statement */
+ PM_CONTEXT_CLASS_RESCUE,
+
+ /** a method definition */
+ PM_CONTEXT_DEF,
+
+ /** an ensure statement within a method definition */
+ PM_CONTEXT_DEF_ENSURE,
+
+ /** a rescue else statement within a method definition */
+ PM_CONTEXT_DEF_ELSE,
+
+ /** a rescue statement within a method definition */
+ PM_CONTEXT_DEF_RESCUE,
+
+ /** a method definition's parameters */
+ PM_CONTEXT_DEF_PARAMS,
+
+ /** a defined? expression */
+ PM_CONTEXT_DEFINED,
+
+ /** a method definition's default parameter */
+ PM_CONTEXT_DEFAULT_PARAMS,
+
+ /** an else clause */
+ PM_CONTEXT_ELSE,
+
+ /** an elsif clause */
+ PM_CONTEXT_ELSIF,
+
+ /** an interpolated expression */
+ PM_CONTEXT_EMBEXPR,
+
+ /** a for loop */
+ PM_CONTEXT_FOR,
+
+ /** a for loop's index */
+ PM_CONTEXT_FOR_INDEX,
+
+ /** an if statement */
+ PM_CONTEXT_IF,
+
+ /** a lambda expression with braces */
+ PM_CONTEXT_LAMBDA_BRACES,
+
+ /** a lambda expression with do..end */
+ PM_CONTEXT_LAMBDA_DO_END,
+
+ /** an ensure statement within a lambda expression */
+ PM_CONTEXT_LAMBDA_ENSURE,
+
+ /** a rescue else statement within a lambda expression */
+ PM_CONTEXT_LAMBDA_ELSE,
+
+ /** a rescue statement within a lambda expression */
+ PM_CONTEXT_LAMBDA_RESCUE,
+
+ /** the top level context */
+ PM_CONTEXT_MAIN,
+
+ /** a module declaration */
+ PM_CONTEXT_MODULE,
+
+ /** an ensure statement within a module statement */
+ PM_CONTEXT_MODULE_ENSURE,
+
+ /** a rescue else statement within a module statement */
+ PM_CONTEXT_MODULE_ELSE,
+
+ /** a rescue statement within a module statement */
+ PM_CONTEXT_MODULE_RESCUE,
+
+ /** a parenthesized expression */
+ PM_CONTEXT_PARENS,
+
+ /** an END block */
+ PM_CONTEXT_POSTEXE,
+
+ /** a predicate inside an if/elsif/unless statement */
+ PM_CONTEXT_PREDICATE,
+
+ /** a BEGIN block */
+ PM_CONTEXT_PREEXE,
+
+ /** a modifier rescue clause */
+ PM_CONTEXT_RESCUE_MODIFIER,
+
+ /** a singleton class definition */
+ PM_CONTEXT_SCLASS,
+
+ /** an ensure statement with a singleton class */
+ PM_CONTEXT_SCLASS_ENSURE,
+
+ /** a rescue else statement with a singleton class */
+ PM_CONTEXT_SCLASS_ELSE,
+
+ /** a rescue statement with a singleton class */
+ PM_CONTEXT_SCLASS_RESCUE,
+
+ /** a ternary expression */
+ PM_CONTEXT_TERNARY,
+
+ /** an unless statement */
+ PM_CONTEXT_UNLESS,
+
+ /** an until statement */
+ PM_CONTEXT_UNTIL,
+
+ /** a while statement */
+ PM_CONTEXT_WHILE,
+} pm_context_t;
+
+/** This is a node in a linked list of contexts. */
+typedef struct pm_context_node {
+ /** The context that this node represents. */
+ pm_context_t context;
+
+ /** A pointer to the previous context in the linked list. */
+ struct pm_context_node *prev;
+} pm_context_node_t;
+
+/** This is the type of a comment that we've found while parsing. */
+typedef enum {
+ PM_COMMENT_INLINE,
+ PM_COMMENT_EMBDOC
+} pm_comment_type_t;
+
+/**
+ * This is a node in the linked list of comments that we've found while parsing.
+ *
+ * @extends pm_list_node_t
+ */
+typedef struct pm_comment {
+ /** The embedded base node. */
+ pm_list_node_t node;
+
+ /** The location of the comment in the source. */
+ pm_location_t location;
+
+ /** The type of comment that we've found. */
+ pm_comment_type_t type;
+} pm_comment_t;
+
+/**
+ * This is a node in the linked list of magic comments that we've found while
+ * parsing.
+ *
+ * @extends pm_list_node_t
+ */
+typedef struct {
+ /** The embedded base node. */
+ pm_list_node_t node;
+
+ /** A pointer to the start of the key in the source. */
+ const uint8_t *key_start;
+
+ /** A pointer to the start of the value in the source. */
+ const uint8_t *value_start;
+
+ /** The length of the key in the source. */
+ uint32_t key_length;
+
+ /** The length of the value in the source. */
+ uint32_t value_length;
+} pm_magic_comment_t;
+
+/**
+ * When the encoding that is being used to parse the source is changed by prism,
+ * we provide the ability here to call out to a user-defined function.
+ */
+typedef void (*pm_encoding_changed_callback_t)(pm_parser_t *parser);
+
+/**
+ * When you are lexing through a file, the lexer needs all of the information
+ * that the parser additionally provides (for example, the local table). So if
+ * you want to properly lex Ruby, you need to actually lex it in the context of
+ * the parser. In order to provide this functionality, we optionally allow a
+ * struct to be attached to the parser that calls back out to a user-provided
+ * callback when each token is lexed.
+ */
+typedef struct {
+ /**
+ * This opaque pointer is used to provide whatever information the user
+ * deemed necessary to the callback. In our case we use it to pass the array
+ * that the tokens get appended into.
+ */
+ void *data;
+
+ /**
+ * This is the callback that is called when a token is lexed. It is passed
+ * the opaque data pointer, the parser, and the token that was lexed.
+ */
+ void (*callback)(void *data, pm_parser_t *parser, pm_token_t *token);
+} pm_lex_callback_t;
+
+/** The type of shareable constant value that can be set. */
+typedef uint8_t pm_shareable_constant_value_t;
+static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_NONE = 0x0;
+static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_LITERAL = 0x1;
+static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_EVERYTHING = 0x2;
+static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_COPY = 0x4;
+
+/**
+ * This tracks an individual local variable in a certain lexical context, as
+ * well as the number of times is it read.
+ */
+typedef struct {
+ /** The name of the local variable. */
+ pm_constant_id_t name;
+
+ /** The location of the local variable in the source. */
+ pm_location_t location;
+
+ /** The index of the local variable in the local table. */
+ uint32_t index;
+
+ /** The number of times the local variable is read. */
+ uint32_t reads;
+
+ /** The hash of the local variable. */
+ uint32_t hash;
+} pm_local_t;
+
+/**
+ * This is a set of local variables in a certain lexical context (method, class,
+ * module, etc.). We need to track how many times these variables are read in
+ * order to warn if they only get written.
+ */
+typedef struct pm_locals {
+ /** The number of local variables in the set. */
+ uint32_t size;
+
+ /** The capacity of the local variables set. */
+ uint32_t capacity;
+
+ /** The nullable allocated memory for the local variables in the set. */
+ pm_local_t *locals;
+} pm_locals_t;
+
+/**
+ * This struct represents a node in a linked list of scopes. Some scopes can see
+ * into their parent scopes, while others cannot.
+ */
+typedef struct pm_scope {
+ /** A pointer to the previous scope in the linked list. */
+ struct pm_scope *previous;
+
+ /** The IDs of the locals in the given scope. */
+ pm_locals_t locals;
+
+ /**
+ * This is a bitfield that indicates the parameters that are being used in
+ * this scope. It is a combination of the PM_SCOPE_PARAMS_* constants. There
+ * are three different kinds of parameters that can be used in a scope:
+ *
+ * - Ordinary parameters (e.g., def foo(bar); end)
+ * - Numbered parameters (e.g., def foo; _1; end)
+ * - The it parameter (e.g., def foo; it; end)
+ *
+ * If ordinary parameters are being used, then certain parameters can be
+ * forwarded to another method/structure. Those are indicated by four
+ * additional bits in the params field. For example, some combinations of:
+ *
+ * - def foo(*); end
+ * - def foo(**); end
+ * - def foo(&); end
+ * - def foo(...); end
+ */
+ uint8_t parameters;
+
+ /**
+ * An integer indicating the number of numbered parameters on this scope.
+ * This is necessary to determine if child blocks are allowed to use
+ * numbered parameters, and to pass information to consumers of the AST
+ * about how many numbered parameters exist.
+ */
+ int8_t numbered_parameters;
+
+ /**
+ * The current state of constant shareability for this scope. This is
+ * changed by magic shareable_constant_value comments.
+ */
+ pm_shareable_constant_value_t shareable_constant;
+
+ /**
+ * A boolean indicating whether or not this scope can see into its parent.
+ * If closed is true, then the scope cannot see into its parent.
+ */
+ bool closed;
+} pm_scope_t;
+
+static const uint8_t PM_SCOPE_PARAMETERS_NONE = 0x0;
+static const uint8_t PM_SCOPE_PARAMETERS_ORDINARY = 0x1;
+static const uint8_t PM_SCOPE_PARAMETERS_NUMBERED = 0x2;
+static const uint8_t PM_SCOPE_PARAMETERS_IT = 0x4;
+static const uint8_t PM_SCOPE_PARAMETERS_TYPE_MASK = 0x7;
+
+static const uint8_t PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS = 0x8;
+static const uint8_t PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS = 0x10;
+static const uint8_t PM_SCOPE_PARAMETERS_FORWARDING_BLOCK = 0x20;
+static const uint8_t PM_SCOPE_PARAMETERS_FORWARDING_ALL = 0x40;
+
+static const int8_t PM_SCOPE_NUMBERED_PARAMETERS_DISALLOWED = -1;
+static const int8_t PM_SCOPE_NUMBERED_PARAMETERS_NONE = 0;
+
+/**
+ * A struct that represents a stack of boolean values.
+ */
+typedef uint32_t pm_state_stack_t;
+
+/**
+ * This struct represents the overall parser. It contains a reference to the
+ * source file, as well as pointers that indicate where in the source it's
+ * currently parsing. It also contains the most recent and current token that
+ * it's considering.
+ */
+struct pm_parser {
+ /** The current state of the lexer. */
+ pm_lex_state_t lex_state;
+
+ /** Tracks the current nesting of (), [], and {}. */
+ int enclosure_nesting;
+
+ /**
+ * Used to temporarily track the nesting of enclosures to determine if a {
+ * is the beginning of a lambda following the parameters of a lambda.
+ */
+ int lambda_enclosure_nesting;
+
+ /**
+ * Used to track the nesting of braces to ensure we get the correct value
+ * when we are interpolating blocks with braces.
+ */
+ int brace_nesting;
+
+ /**
+ * The stack used to determine if a do keyword belongs to the predicate of a
+ * while, until, or for loop.
+ */
+ pm_state_stack_t do_loop_stack;
+
+ /**
+ * The stack used to determine if a do keyword belongs to the beginning of a
+ * block.
+ */
+ pm_state_stack_t accepts_block_stack;
+
+ /** A stack of lex modes. */
+ struct {
+ /** The current mode of the lexer. */
+ pm_lex_mode_t *current;
+
+ /** The stack of lexer modes. */
+ pm_lex_mode_t stack[PM_LEX_STACK_SIZE];
+
+ /** The current index into the lexer mode stack. */
+ size_t index;
+ } lex_modes;
+
+ /** The pointer to the start of the source. */
+ const uint8_t *start;
+
+ /** The pointer to the end of the source. */
+ const uint8_t *end;
+
+ /** The previous token we were considering. */
+ pm_token_t previous;
+
+ /** The current token we're considering. */
+ pm_token_t current;
+
+ /**
+ * This is a special field set on the parser when we need the parser to jump
+ * to a specific location when lexing the next token, as opposed to just
+ * using the end of the previous token. Normally this is NULL.
+ */
+ const uint8_t *next_start;
+
+ /**
+ * This field indicates the end of a heredoc whose identifier was found on
+ * the current line. If another heredoc is found on the same line, then this
+ * will be moved forward to the end of that heredoc. If no heredocs are
+ * found on a line then this is NULL.
+ */
+ const uint8_t *heredoc_end;
+
+ /** The list of comments that have been found while parsing. */
+ pm_list_t comment_list;
+
+ /** The list of magic comments that have been found while parsing. */
+ pm_list_t magic_comment_list;
+
+ /**
+ * An optional location that represents the location of the __END__ marker
+ * and the rest of the content of the file. This content is loaded into the
+ * DATA constant when the file being parsed is the main file being executed.
+ */
+ pm_location_t data_loc;
+
+ /** The list of warnings that have been found while parsing. */
+ pm_list_t warning_list;
+
+ /** The list of errors that have been found while parsing. */
+ pm_list_t error_list;
+
+ /** The current local scope. */
+ pm_scope_t *current_scope;
+
+ /** The current parsing context. */
+ pm_context_node_t *current_context;
+
+ /**
+ * The hash keys for the hash that is currently being parsed. This is not
+ * usually necessary because it can pass it down the various call chains,
+ * but in the event that you're parsing a hash that is being directly
+ * pushed into another hash with **, we need to share the hash keys so that
+ * we can warn for the nested hash as well.
+ */
+ pm_static_literals_t *current_hash_keys;
+
+ /**
+ * The encoding functions for the current file is attached to the parser as
+ * it's parsing so that it can change with a magic comment.
+ */
+ const pm_encoding_t *encoding;
+
+ /**
+ * When the encoding that is being used to parse the source is changed by
+ * prism, we provide the ability here to call out to a user-defined
+ * function.
+ */
+ pm_encoding_changed_callback_t encoding_changed_callback;
+
+ /**
+ * This pointer indicates where a comment must start if it is to be
+ * considered an encoding comment.
+ */
+ const uint8_t *encoding_comment_start;
+
+ /**
+ * This is an optional callback that can be attached to the parser that will
+ * be called whenever a new token is lexed by the parser.
+ */
+ pm_lex_callback_t *lex_callback;
+
+ /**
+ * This is the path of the file being parsed. We use the filepath when
+ * constructing SourceFileNodes.
+ */
+ pm_string_t filepath;
+
+ /**
+ * This constant pool keeps all of the constants defined throughout the file
+ * so that we can reference them later.
+ */
+ pm_constant_pool_t constant_pool;
+
+ /** This is the list of newline offsets in the source file. */
+ pm_newline_list_t newline_list;
+
+ /**
+ * We want to add a flag to integer nodes that indicates their base. We only
+ * want to parse these once, but we don't have space on the token itself to
+ * communicate this information. So we store it here and pass it through
+ * when we find tokens that we need it for.
+ */
+ pm_node_flags_t integer_base;
+
+ /**
+ * This string is used to pass information from the lexer to the parser. It
+ * is particularly necessary because of escape sequences.
+ */
+ pm_string_t current_string;
+
+ /**
+ * The line number at the start of the parse. This will be used to offset
+ * the line numbers of all of the locations.
+ */
+ int32_t start_line;
+
+ /**
+ * When a string-like expression is being lexed, any byte or escape sequence
+ * that resolves to a value whose top bit is set (i.e., >= 0x80) will
+ * explicitly set the encoding to the same encoding as the source.
+ * Alternatively, if a unicode escape sequence is used (e.g., \\u{80}) that
+ * resolves to a value whose top bit is set, then the encoding will be
+ * explicitly set to UTF-8.
+ *
+ * The _next_ time this happens, if the encoding that is about to become the
+ * explicitly set encoding does not match the previously set explicit
+ * encoding, a mixed encoding error will be emitted.
+ *
+ * When the expression is finished being lexed, the explicit encoding
+ * controls the encoding of the expression. For the most part this means
+ * that the expression will either be encoded in the source encoding or
+ * UTF-8. This holds for all encodings except US-ASCII. If the source is
+ * US-ASCII and an explicit encoding was set that was _not_ UTF-8, then the
+ * expression will be encoded as ASCII-8BIT.
+ *
+ * Note that if the expression is a list, different elements within the same
+ * list can have different encodings, so this will get reset between each
+ * element. Furthermore all of this only applies to lists that support
+ * interpolation, because otherwise escapes that could change the encoding
+ * are ignored.
+ *
+ * At first glance, it may make more sense for this to live on the lexer
+ * mode, but we need it here to communicate back to the parser for character
+ * literals that do not push a new lexer mode.
+ */
+ const pm_encoding_t *explicit_encoding;
+
+ /**
+ * When parsing block exits (e.g., break, next, redo), we need to validate
+ * that they are in correct contexts. For the most part we can do this by
+ * looking at our parent contexts. However, modifier while and until
+ * expressions can change that context to make block exits valid. In these
+ * cases, we need to keep track of the block exits and then validate them
+ * after the expression has been parsed.
+ *
+ * We use a pointer here because we don't want to keep a whole list attached
+ * since this will only be used in the context of begin/end expressions.
+ */
+ pm_node_list_t *current_block_exits;
+
+ /** The version of prism that we should use to parse. */
+ pm_options_version_t version;
+
+ /** The command line flags given from the options. */
+ uint8_t command_line;
+
+ /**
+ * Whether or not we have found a frozen_string_literal magic comment with
+ * a true or false value.
+ * May be:
+ * - PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED
+ * - PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED
+ * - PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET
+ */
+ int8_t frozen_string_literal;
+
+ /**
+ * Whether or not we are parsing an eval string. This impacts whether or not
+ * we should evaluate if block exits/yields are valid.
+ */
+ bool parsing_eval;
+
+ /** Whether or not we're at the beginning of a command. */
+ bool command_start;
+
+ /** Whether or not we're currently recovering from a syntax error. */
+ bool recovering;
+
+ /**
+ * Whether or not the encoding has been changed by a magic comment. We use
+ * this to provide a fast path for the lexer instead of going through the
+ * function pointer.
+ */
+ bool encoding_changed;
+
+ /**
+ * This flag indicates that we are currently parsing a pattern matching
+ * expression and impacts that calculation of newlines.
+ */
+ bool pattern_matching_newlines;
+
+ /** This flag indicates that we are currently parsing a keyword argument. */
+ bool in_keyword_arg;
+
+ /**
+ * Whether or not the parser has seen a token that has semantic meaning
+ * (i.e., a token that is not a comment or whitespace).
+ */
+ bool semantic_token_seen;
+
+ /**
+ * True if the current regular expression being lexed contains only ASCII
+ * characters.
+ */
+ bool current_regular_expression_ascii_only;
+};
+
+#endif
diff --git a/prism/prettyprint.h b/prism/prettyprint.h
new file mode 100644
index 0000000000..5a52b2b6b8
--- /dev/null
+++ b/prism/prettyprint.h
@@ -0,0 +1,34 @@
+/**
+ * @file prettyprint.h
+ *
+ * An AST node pretty-printer.
+ */
+#ifndef PRISM_PRETTYPRINT_H
+#define PRISM_PRETTYPRINT_H
+
+#include "prism/defines.h"
+
+#ifdef PRISM_EXCLUDE_PRETTYPRINT
+
+void pm_prettyprint(void);
+
+#else
+
+#include <stdio.h>
+
+#include "prism/ast.h"
+#include "prism/parser.h"
+#include "prism/util/pm_buffer.h"
+
+/**
+ * Pretty-prints the AST represented by the given node to the given buffer.
+ *
+ * @param output_buffer The buffer to write the pretty-printed AST to.
+ * @param parser The parser that parsed the AST.
+ * @param node The root node of the AST to pretty-print.
+ */
+PRISM_EXPORTED_FUNCTION void pm_prettyprint(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm_node_t *node);
+
+#endif
+
+#endif
diff --git a/prism/prism.c b/prism/prism.c
new file mode 100644
index 0000000000..2815723ebd
--- /dev/null
+++ b/prism/prism.c
@@ -0,0 +1,21437 @@
+#include "prism.h"
+
+/**
+ * The prism version and the serialization format.
+ */
+const char *
+pm_version(void) {
+ return PRISM_VERSION;
+}
+
+/**
+ * In heredocs, tabs automatically complete up to the next 8 spaces. This is
+ * defined in CRuby as TAB_WIDTH.
+ */
+#define PM_TAB_WHITESPACE_SIZE 8
+
+#ifndef PM_DEBUG_LOGGING
+/**
+ * Debugging logging will provide you with additional debugging functions as
+ * well as automatically replace some functions with their debugging
+ * counterparts.
+ */
+#define PM_DEBUG_LOGGING 0
+#endif
+
+#if PM_DEBUG_LOGGING
+
+/******************************************************************************/
+/* Debugging */
+/******************************************************************************/
+
+PRISM_ATTRIBUTE_UNUSED static const char *
+debug_context(pm_context_t context) {
+ switch (context) {
+ case PM_CONTEXT_BEGIN: return "BEGIN";
+ case PM_CONTEXT_BEGIN_ENSURE: return "BEGIN_ENSURE";
+ case PM_CONTEXT_BEGIN_ELSE: return "BEGIN_ELSE";
+ case PM_CONTEXT_BEGIN_RESCUE: return "BEGIN_RESCUE";
+ case PM_CONTEXT_BLOCK_BRACES: return "BLOCK_BRACES";
+ case PM_CONTEXT_BLOCK_KEYWORDS: return "BLOCK_KEYWORDS";
+ case PM_CONTEXT_BLOCK_ENSURE: return "BLOCK_ENSURE";
+ case PM_CONTEXT_BLOCK_ELSE: return "BLOCK_ELSE";
+ case PM_CONTEXT_BLOCK_RESCUE: return "BLOCK_RESCUE";
+ case PM_CONTEXT_CASE_IN: return "CASE_IN";
+ case PM_CONTEXT_CASE_WHEN: return "CASE_WHEN";
+ case PM_CONTEXT_CLASS: return "CLASS";
+ case PM_CONTEXT_CLASS_ELSE: return "CLASS_ELSE";
+ case PM_CONTEXT_CLASS_ENSURE: return "CLASS_ENSURE";
+ case PM_CONTEXT_CLASS_RESCUE: return "CLASS_RESCUE";
+ case PM_CONTEXT_DEF: return "DEF";
+ case PM_CONTEXT_DEF_PARAMS: return "DEF_PARAMS";
+ case PM_CONTEXT_DEF_ENSURE: return "DEF_ENSURE";
+ case PM_CONTEXT_DEF_ELSE: return "DEF_ELSE";
+ case PM_CONTEXT_DEF_RESCUE: return "DEF_RESCUE";
+ case PM_CONTEXT_DEFAULT_PARAMS: return "DEFAULT_PARAMS";
+ case PM_CONTEXT_DEFINED: return "DEFINED";
+ case PM_CONTEXT_ELSE: return "ELSE";
+ case PM_CONTEXT_ELSIF: return "ELSIF";
+ case PM_CONTEXT_EMBEXPR: return "EMBEXPR";
+ case PM_CONTEXT_FOR_INDEX: return "FOR_INDEX";
+ case PM_CONTEXT_FOR: return "FOR";
+ case PM_CONTEXT_IF: return "IF";
+ case PM_CONTEXT_LAMBDA_BRACES: return "LAMBDA_BRACES";
+ case PM_CONTEXT_LAMBDA_DO_END: return "LAMBDA_DO_END";
+ case PM_CONTEXT_LAMBDA_ENSURE: return "LAMBDA_ENSURE";
+ case PM_CONTEXT_LAMBDA_ELSE: return "LAMBDA_ELSE";
+ case PM_CONTEXT_LAMBDA_RESCUE: return "LAMBDA_RESCUE";
+ case PM_CONTEXT_MAIN: return "MAIN";
+ case PM_CONTEXT_MODULE: return "MODULE";
+ case PM_CONTEXT_MODULE_ELSE: return "MODULE_ELSE";
+ case PM_CONTEXT_MODULE_ENSURE: return "MODULE_ENSURE";
+ case PM_CONTEXT_MODULE_RESCUE: return "MODULE_RESCUE";
+ case PM_CONTEXT_NONE: return "NONE";
+ case PM_CONTEXT_PARENS: return "PARENS";
+ case PM_CONTEXT_POSTEXE: return "POSTEXE";
+ case PM_CONTEXT_PREDICATE: return "PREDICATE";
+ case PM_CONTEXT_PREEXE: return "PREEXE";
+ case PM_CONTEXT_RESCUE_MODIFIER: return "RESCUE_MODIFIER";
+ case PM_CONTEXT_SCLASS: return "SCLASS";
+ case PM_CONTEXT_SCLASS_ENSURE: return "SCLASS_ENSURE";
+ case PM_CONTEXT_SCLASS_ELSE: return "SCLASS_ELSE";
+ case PM_CONTEXT_SCLASS_RESCUE: return "SCLASS_RESCUE";
+ case PM_CONTEXT_TERNARY: return "TERNARY";
+ case PM_CONTEXT_UNLESS: return "UNLESS";
+ case PM_CONTEXT_UNTIL: return "UNTIL";
+ case PM_CONTEXT_WHILE: return "WHILE";
+ }
+ return NULL;
+}
+
+PRISM_ATTRIBUTE_UNUSED static void
+debug_contexts(pm_parser_t *parser) {
+ pm_context_node_t *context_node = parser->current_context;
+ fprintf(stderr, "CONTEXTS: ");
+
+ if (context_node != NULL) {
+ while (context_node != NULL) {
+ fprintf(stderr, "%s", debug_context(context_node->context));
+ context_node = context_node->prev;
+ if (context_node != NULL) {
+ fprintf(stderr, " <- ");
+ }
+ }
+ } else {
+ fprintf(stderr, "NONE");
+ }
+
+ fprintf(stderr, "\n");
+}
+
+PRISM_ATTRIBUTE_UNUSED static void
+debug_node(const pm_parser_t *parser, const pm_node_t *node) {
+ pm_buffer_t output_buffer = { 0 };
+ pm_prettyprint(&output_buffer, parser, node);
+
+ fprintf(stderr, "%.*s", (int) output_buffer.length, output_buffer.value);
+ pm_buffer_free(&output_buffer);
+}
+
+PRISM_ATTRIBUTE_UNUSED static void
+debug_lex_mode(pm_parser_t *parser) {
+ pm_lex_mode_t *lex_mode = parser->lex_modes.current;
+ bool first = true;
+
+ while (lex_mode != NULL) {
+ if (first) {
+ first = false;
+ } else {
+ fprintf(stderr, " <- ");
+ }
+
+ switch (lex_mode->mode) {
+ case PM_LEX_DEFAULT: fprintf(stderr, "DEFAULT"); break;
+ case PM_LEX_EMBEXPR: fprintf(stderr, "EMBEXPR"); break;
+ case PM_LEX_EMBVAR: fprintf(stderr, "EMBVAR"); break;
+ case PM_LEX_HEREDOC: fprintf(stderr, "HEREDOC"); break;
+ case PM_LEX_LIST: fprintf(stderr, "LIST (terminator=%c, interpolation=%d)", lex_mode->as.list.terminator, lex_mode->as.list.interpolation); break;
+ case PM_LEX_REGEXP: fprintf(stderr, "REGEXP (terminator=%c)", lex_mode->as.regexp.terminator); break;
+ case PM_LEX_STRING: fprintf(stderr, "STRING (terminator=%c, interpolation=%d)", lex_mode->as.string.terminator, lex_mode->as.string.interpolation); break;
+ }
+
+ lex_mode = lex_mode->prev;
+ }
+
+ fprintf(stderr, "\n");
+}
+
+PRISM_ATTRIBUTE_UNUSED static void
+debug_state(pm_parser_t *parser) {
+ fprintf(stderr, "STATE: ");
+ bool first = true;
+
+ if (parser->lex_state == PM_LEX_STATE_NONE) {
+ fprintf(stderr, "NONE\n");
+ return;
+ }
+
+#define CHECK_STATE(state) \
+ if (parser->lex_state & state) { \
+ if (!first) fprintf(stderr, "|"); \
+ fprintf(stderr, "%s", #state); \
+ first = false; \
+ }
+
+ CHECK_STATE(PM_LEX_STATE_BEG)
+ CHECK_STATE(PM_LEX_STATE_END)
+ CHECK_STATE(PM_LEX_STATE_ENDARG)
+ CHECK_STATE(PM_LEX_STATE_ENDFN)
+ CHECK_STATE(PM_LEX_STATE_ARG)
+ CHECK_STATE(PM_LEX_STATE_CMDARG)
+ CHECK_STATE(PM_LEX_STATE_MID)
+ CHECK_STATE(PM_LEX_STATE_FNAME)
+ CHECK_STATE(PM_LEX_STATE_DOT)
+ CHECK_STATE(PM_LEX_STATE_CLASS)
+ CHECK_STATE(PM_LEX_STATE_LABEL)
+ CHECK_STATE(PM_LEX_STATE_LABELED)
+ CHECK_STATE(PM_LEX_STATE_FITEM)
+
+#undef CHECK_STATE
+
+ fprintf(stderr, "\n");
+}
+
+PRISM_ATTRIBUTE_UNUSED static void
+debug_token(pm_token_t * token) {
+ fprintf(stderr, "%s: \"%.*s\"\n", pm_token_type_human(token->type), (int) (token->end - token->start), token->start);
+}
+
+#endif
+
+// Macros for min/max.
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#define MAX(a,b) (((a)>(b))?(a):(b))
+
+/******************************************************************************/
+/* Lex mode manipulations */
+/******************************************************************************/
+
+/**
+ * Returns the incrementor character that should be used to increment the
+ * nesting count if one is possible.
+ */
+static inline uint8_t
+lex_mode_incrementor(const uint8_t start) {
+ switch (start) {
+ case '(':
+ case '[':
+ case '{':
+ case '<':
+ return start;
+ default:
+ return '\0';
+ }
+}
+
+/**
+ * Returns the matching character that should be used to terminate a list
+ * beginning with the given character.
+ */
+static inline uint8_t
+lex_mode_terminator(const uint8_t start) {
+ switch (start) {
+ case '(':
+ return ')';
+ case '[':
+ return ']';
+ case '{':
+ return '}';
+ case '<':
+ return '>';
+ default:
+ return start;
+ }
+}
+
+/**
+ * Push a new lex state onto the stack. If we're still within the pre-allocated
+ * space of the lex state stack, then we'll just use a new slot. Otherwise we'll
+ * allocate a new pointer and use that.
+ */
+static bool
+lex_mode_push(pm_parser_t *parser, pm_lex_mode_t lex_mode) {
+ lex_mode.prev = parser->lex_modes.current;
+ parser->lex_modes.index++;
+
+ if (parser->lex_modes.index > PM_LEX_STACK_SIZE - 1) {
+ parser->lex_modes.current = (pm_lex_mode_t *) xmalloc(sizeof(pm_lex_mode_t));
+ if (parser->lex_modes.current == NULL) return false;
+
+ *parser->lex_modes.current = lex_mode;
+ } else {
+ parser->lex_modes.stack[parser->lex_modes.index] = lex_mode;
+ parser->lex_modes.current = &parser->lex_modes.stack[parser->lex_modes.index];
+ }
+
+ return true;
+}
+
+/**
+ * Push on a new list lex mode.
+ */
+static inline bool
+lex_mode_push_list(pm_parser_t *parser, bool interpolation, uint8_t delimiter) {
+ uint8_t incrementor = lex_mode_incrementor(delimiter);
+ uint8_t terminator = lex_mode_terminator(delimiter);
+
+ pm_lex_mode_t lex_mode = {
+ .mode = PM_LEX_LIST,
+ .as.list = {
+ .nesting = 0,
+ .interpolation = interpolation,
+ .incrementor = incrementor,
+ .terminator = terminator
+ }
+ };
+
+ // These are the places where we need to split up the content of the list.
+ // We'll use strpbrk to find the first of these characters.
+ uint8_t *breakpoints = lex_mode.as.list.breakpoints;
+ memcpy(breakpoints, "\\ \t\f\r\v\n\0\0\0", sizeof(lex_mode.as.list.breakpoints));
+ size_t index = 7;
+
+ // Now we'll add the terminator to the list of breakpoints. If the
+ // terminator is not already a NULL byte, add it to the list.
+ if (terminator != '\0') {
+ breakpoints[index++] = terminator;
+ }
+
+ // If interpolation is allowed, then we're going to check for the #
+ // character. Otherwise we'll only look for escapes and the terminator.
+ if (interpolation) {
+ breakpoints[index++] = '#';
+ }
+
+ // If there is an incrementor, then we'll check for that as well.
+ if (incrementor != '\0') {
+ breakpoints[index++] = incrementor;
+ }
+
+ parser->explicit_encoding = NULL;
+ return lex_mode_push(parser, lex_mode);
+}
+
+/**
+ * Push on a new list lex mode that is only used for compatibility. This is
+ * called when we're at the end of the file. We want the parser to be able to
+ * perform its normal error tolerance.
+ */
+static inline bool
+lex_mode_push_list_eof(pm_parser_t *parser) {
+ return lex_mode_push_list(parser, false, '\0');
+}
+
+/**
+ * Push on a new regexp lex mode.
+ */
+static inline bool
+lex_mode_push_regexp(pm_parser_t *parser, uint8_t incrementor, uint8_t terminator) {
+ pm_lex_mode_t lex_mode = {
+ .mode = PM_LEX_REGEXP,
+ .as.regexp = {
+ .nesting = 0,
+ .incrementor = incrementor,
+ .terminator = terminator
+ }
+ };
+
+ // These are the places where we need to split up the content of the
+ // regular expression. We'll use strpbrk to find the first of these
+ // characters.
+ uint8_t *breakpoints = lex_mode.as.regexp.breakpoints;
+ memcpy(breakpoints, "\r\n\\#\0\0", sizeof(lex_mode.as.regexp.breakpoints));
+ size_t index = 4;
+
+ // First we'll add the terminator.
+ if (terminator != '\0') {
+ breakpoints[index++] = terminator;
+ }
+
+ // Next, if there is an incrementor, then we'll check for that as well.
+ if (incrementor != '\0') {
+ breakpoints[index++] = incrementor;
+ }
+
+ return lex_mode_push(parser, lex_mode);
+}
+
+/**
+ * Push on a new string lex mode.
+ */
+static inline bool
+lex_mode_push_string(pm_parser_t *parser, bool interpolation, bool label_allowed, uint8_t incrementor, uint8_t terminator) {
+ pm_lex_mode_t lex_mode = {
+ .mode = PM_LEX_STRING,
+ .as.string = {
+ .nesting = 0,
+ .interpolation = interpolation,
+ .label_allowed = label_allowed,
+ .incrementor = incrementor,
+ .terminator = terminator
+ }
+ };
+
+ // These are the places where we need to split up the content of the
+ // string. We'll use strpbrk to find the first of these characters.
+ uint8_t *breakpoints = lex_mode.as.string.breakpoints;
+ memcpy(breakpoints, "\r\n\\\0\0\0", sizeof(lex_mode.as.string.breakpoints));
+ size_t index = 3;
+
+ // Now add in the terminator. If the terminator is not already a NULL byte,
+ // then we'll add it.
+ if (terminator != '\0') {
+ breakpoints[index++] = terminator;
+ }
+
+ // If interpolation is allowed, then we're going to check for the #
+ // character. Otherwise we'll only look for escapes and the terminator.
+ if (interpolation) {
+ breakpoints[index++] = '#';
+ }
+
+ // If we have an incrementor, then we'll add that in as a breakpoint as
+ // well.
+ if (incrementor != '\0') {
+ breakpoints[index++] = incrementor;
+ }
+
+ parser->explicit_encoding = NULL;
+ return lex_mode_push(parser, lex_mode);
+}
+
+/**
+ * Push on a new string lex mode that is only used for compatibility. This is
+ * called when we're at the end of the file. We want the parser to be able to
+ * perform its normal error tolerance.
+ */
+static inline bool
+lex_mode_push_string_eof(pm_parser_t *parser) {
+ return lex_mode_push_string(parser, false, false, '\0', '\0');
+}
+
+/**
+ * Pop the current lex state off the stack. If we're within the pre-allocated
+ * space of the lex state stack, then we'll just decrement the index. Otherwise
+ * we'll free the current pointer and use the previous pointer.
+ */
+static void
+lex_mode_pop(pm_parser_t *parser) {
+ if (parser->lex_modes.index == 0) {
+ parser->lex_modes.current->mode = PM_LEX_DEFAULT;
+ } else if (parser->lex_modes.index < PM_LEX_STACK_SIZE) {
+ parser->lex_modes.index--;
+ parser->lex_modes.current = &parser->lex_modes.stack[parser->lex_modes.index];
+ } else {
+ parser->lex_modes.index--;
+ pm_lex_mode_t *prev = parser->lex_modes.current->prev;
+ xfree(parser->lex_modes.current);
+ parser->lex_modes.current = prev;
+ }
+}
+
+/**
+ * This is the equivalent of IS_lex_state is CRuby.
+ */
+static inline bool
+lex_state_p(pm_parser_t *parser, pm_lex_state_t state) {
+ return parser->lex_state & state;
+}
+
+typedef enum {
+ PM_IGNORED_NEWLINE_NONE = 0,
+ PM_IGNORED_NEWLINE_ALL,
+ PM_IGNORED_NEWLINE_PATTERN
+} pm_ignored_newline_type_t;
+
+static inline pm_ignored_newline_type_t
+lex_state_ignored_p(pm_parser_t *parser) {
+ bool ignored = lex_state_p(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_CLASS | PM_LEX_STATE_FNAME | PM_LEX_STATE_DOT) && !lex_state_p(parser, PM_LEX_STATE_LABELED);
+
+ if (ignored) {
+ return PM_IGNORED_NEWLINE_ALL;
+ } else if ((parser->lex_state & ~((unsigned int) PM_LEX_STATE_LABEL)) == (PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED)) {
+ return PM_IGNORED_NEWLINE_PATTERN;
+ } else {
+ return PM_IGNORED_NEWLINE_NONE;
+ }
+}
+
+static inline bool
+lex_state_beg_p(pm_parser_t *parser) {
+ return lex_state_p(parser, PM_LEX_STATE_BEG_ANY) || ((parser->lex_state & (PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED)) == (PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED));
+}
+
+static inline bool
+lex_state_arg_labeled_p(pm_parser_t *parser) {
+ return (parser->lex_state & (PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED)) == (PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
+}
+
+static inline bool
+lex_state_arg_p(pm_parser_t *parser) {
+ return lex_state_p(parser, PM_LEX_STATE_ARG_ANY);
+}
+
+static inline bool
+lex_state_spcarg_p(pm_parser_t *parser, bool space_seen) {
+ if (parser->current.end >= parser->end) {
+ return false;
+ }
+ return lex_state_arg_p(parser) && space_seen && !pm_char_is_whitespace(*parser->current.end);
+}
+
+static inline bool
+lex_state_end_p(pm_parser_t *parser) {
+ return lex_state_p(parser, PM_LEX_STATE_END_ANY);
+}
+
+/**
+ * This is the equivalent of IS_AFTER_OPERATOR in CRuby.
+ */
+static inline bool
+lex_state_operator_p(pm_parser_t *parser) {
+ return lex_state_p(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_DOT);
+}
+
+/**
+ * Set the state of the lexer. This is defined as a function to be able to put a
+ * breakpoint in it.
+ */
+static inline void
+lex_state_set(pm_parser_t *parser, pm_lex_state_t state) {
+ parser->lex_state = state;
+}
+
+#if PM_DEBUG_LOGGING
+static inline void
+debug_lex_state_set(pm_parser_t *parser, pm_lex_state_t state, char const * caller_name, int line_number) {
+ fprintf(stderr, "Caller: %s:%d\nPrevious: ", caller_name, line_number);
+ debug_state(parser);
+ lex_state_set(parser, state);
+ fprintf(stderr, "Now: ");
+ debug_state(parser);
+ fprintf(stderr, "\n");
+}
+
+#define lex_state_set(parser, state) debug_lex_state_set(parser, state, __func__, __LINE__)
+#endif
+
+/******************************************************************************/
+/* Command-line macro helpers */
+/******************************************************************************/
+
+/** True if the parser has the given command-line option. */
+#define PM_PARSER_COMMAND_LINE_OPTION(parser, option) ((parser)->command_line & (option))
+
+/** True if the -a command line option was given. */
+#define PM_PARSER_COMMAND_LINE_OPTION_A(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_A)
+
+/** True if the -e command line option was given. */
+#define PM_PARSER_COMMAND_LINE_OPTION_E(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_E)
+
+/** True if the -l command line option was given. */
+#define PM_PARSER_COMMAND_LINE_OPTION_L(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_L)
+
+/** True if the -n command line option was given. */
+#define PM_PARSER_COMMAND_LINE_OPTION_N(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_N)
+
+/** True if the -p command line option was given. */
+#define PM_PARSER_COMMAND_LINE_OPTION_P(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_P)
+
+/** True if the -x command line option was given. */
+#define PM_PARSER_COMMAND_LINE_OPTION_X(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_X)
+
+/******************************************************************************/
+/* Diagnostic-related functions */
+/******************************************************************************/
+
+/**
+ * Append an error to the list of errors on the parser.
+ */
+static inline void
+pm_parser_err(pm_parser_t *parser, const uint8_t *start, const uint8_t *end, pm_diagnostic_id_t diag_id) {
+ pm_diagnostic_list_append(&parser->error_list, start, end, diag_id);
+}
+
+/**
+ * Append an error to the list of errors on the parser using a format string.
+ */
+#define PM_PARSER_ERR_FORMAT(parser, start, end, diag_id, ...) \
+ pm_diagnostic_list_append_format(&parser->error_list, start, end, diag_id, __VA_ARGS__)
+
+/**
+ * Append an error to the list of errors on the parser using the location of the
+ * current token.
+ */
+static inline void
+pm_parser_err_current(pm_parser_t *parser, pm_diagnostic_id_t diag_id) {
+ pm_parser_err(parser, parser->current.start, parser->current.end, diag_id);
+}
+
+/**
+ * Append an error to the list of errors on the parser using the given location
+ * using a format string.
+ */
+#define PM_PARSER_ERR_LOCATION_FORMAT(parser, location, diag_id, ...) \
+ PM_PARSER_ERR_FORMAT(parser, (location)->start, (location)->end, diag_id, __VA_ARGS__)
+
+/**
+ * Append an error to the list of errors on the parser using the location of the
+ * given node.
+ */
+static inline void
+pm_parser_err_node(pm_parser_t *parser, const pm_node_t *node, pm_diagnostic_id_t diag_id) {
+ pm_parser_err(parser, node->location.start, node->location.end, diag_id);
+}
+
+/**
+ * Append an error to the list of errors on the parser using the location of the
+ * given node and a format string.
+ */
+#define PM_PARSER_ERR_NODE_FORMAT(parser, node, diag_id, ...) \
+ PM_PARSER_ERR_FORMAT(parser, (node)->location.start, (node)->location.end, diag_id, __VA_ARGS__)
+
+/**
+ * Append an error to the list of errors on the parser using the location of the
+ * given node and a format string, and add on the content of the node.
+ */
+#define PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, diag_id) \
+ PM_PARSER_ERR_NODE_FORMAT(parser, node, diag_id, (int) ((node)->location.end - (node)->location.start), (const char *) (node)->location.start)
+
+/**
+ * Append an error to the list of errors on the parser using the location of the
+ * previous token.
+ */
+static inline void
+pm_parser_err_previous(pm_parser_t *parser, pm_diagnostic_id_t diag_id) {
+ pm_parser_err(parser, parser->previous.start, parser->previous.end, diag_id);
+}
+
+/**
+ * Append an error to the list of errors on the parser using the location of the
+ * given token.
+ */
+static inline void
+pm_parser_err_token(pm_parser_t *parser, const pm_token_t *token, pm_diagnostic_id_t diag_id) {
+ pm_parser_err(parser, token->start, token->end, diag_id);
+}
+
+/**
+ * Append an error to the list of errors on the parser using the location of the
+ * given token and a format string.
+ */
+#define PM_PARSER_ERR_TOKEN_FORMAT(parser, token, diag_id, ...) \
+ PM_PARSER_ERR_FORMAT(parser, (token).start, (token).end, diag_id, __VA_ARGS__)
+
+/**
+ * Append an error to the list of errors on the parser using the location of the
+ * given token and a format string, and add on the content of the token.
+ */
+#define PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, token, diag_id) \
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, token, diag_id, (int) ((token).end - (token).start), (const char *) (token).start)
+
+/**
+ * Append a warning to the list of warnings on the parser.
+ */
+static inline void
+pm_parser_warn(pm_parser_t *parser, const uint8_t *start, const uint8_t *end, pm_diagnostic_id_t diag_id) {
+ pm_diagnostic_list_append(&parser->warning_list, start, end, diag_id);
+}
+
+/**
+ * Append a warning to the list of warnings on the parser using the location of
+ * the given token.
+ */
+static inline void
+pm_parser_warn_token(pm_parser_t *parser, const pm_token_t *token, pm_diagnostic_id_t diag_id) {
+ pm_parser_warn(parser, token->start, token->end, diag_id);
+}
+
+/**
+ * Append a warning to the list of warnings on the parser using the location of
+ * the given node.
+ */
+static inline void
+pm_parser_warn_node(pm_parser_t *parser, const pm_node_t *node, pm_diagnostic_id_t diag_id) {
+ pm_parser_warn(parser, node->location.start, node->location.end, diag_id);
+}
+
+/**
+ * Append a warning to the list of warnings on the parser using a format string.
+ */
+#define PM_PARSER_WARN_FORMAT(parser, start, end, diag_id, ...) \
+ pm_diagnostic_list_append_format(&parser->warning_list, start, end, diag_id, __VA_ARGS__)
+
+/**
+ * Append a warning to the list of warnings on the parser using the location of
+ * the given token and a format string.
+ */
+#define PM_PARSER_WARN_TOKEN_FORMAT(parser, token, diag_id, ...) \
+ PM_PARSER_WARN_FORMAT(parser, (token).start, (token).end, diag_id, __VA_ARGS__)
+
+/**
+ * Append a warning to the list of warnings on the parser using the location of
+ * the given token and a format string, and add on the content of the token.
+ */
+#define PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, token, diag_id) \
+ PM_PARSER_WARN_TOKEN_FORMAT(parser, token, diag_id, (int) ((token).end - (token).start), (const char *) (token).start)
+
+/**
+ * Append a warning to the list of warnings on the parser using the location of
+ * the given node and a format string.
+ */
+#define PM_PARSER_WARN_NODE_FORMAT(parser, node, diag_id, ...) \
+ PM_PARSER_WARN_FORMAT(parser, (node)->location.start, (node)->location.end, diag_id, __VA_ARGS__)
+
+/******************************************************************************/
+/* Scope-related functions */
+/******************************************************************************/
+
+/**
+ * Allocate and initialize a new scope. Push it onto the scope stack.
+ */
+static bool
+pm_parser_scope_push(pm_parser_t *parser, bool closed) {
+ pm_scope_t *scope = (pm_scope_t *) xmalloc(sizeof(pm_scope_t));
+ if (scope == NULL) return false;
+
+ *scope = (pm_scope_t) {
+ .previous = parser->current_scope,
+ .locals = { 0 },
+ .parameters = PM_SCOPE_PARAMETERS_NONE,
+ .numbered_parameters = PM_SCOPE_NUMBERED_PARAMETERS_NONE,
+ .shareable_constant = (closed || parser->current_scope == NULL) ? PM_SCOPE_SHAREABLE_CONSTANT_NONE : parser->current_scope->shareable_constant,
+ .closed = closed
+ };
+
+ parser->current_scope = scope;
+ return true;
+}
+
+/**
+ * Determine if the current scope is at the top level. This means it is either
+ * the top-level scope or it is open to the top-level.
+ */
+static bool
+pm_parser_scope_toplevel_p(pm_parser_t *parser) {
+ pm_scope_t *scope = parser->current_scope;
+
+ do {
+ if (scope->previous == NULL) return true;
+ if (scope->closed) return false;
+ } while ((scope = scope->previous) != NULL);
+
+ assert(false && "unreachable");
+ return true;
+}
+
+/**
+ * Retrieve the scope at the given depth.
+ */
+static pm_scope_t *
+pm_parser_scope_find(pm_parser_t *parser, uint32_t depth) {
+ pm_scope_t *scope = parser->current_scope;
+
+ while (depth-- > 0) {
+ assert(scope != NULL);
+ scope = scope->previous;
+ }
+
+ return scope;
+}
+
+static void
+pm_parser_scope_forwarding_param_check(pm_parser_t *parser, const pm_token_t * token, const uint8_t mask, pm_diagnostic_id_t diag) {
+ pm_scope_t *scope = parser->current_scope;
+ while (scope) {
+ if (scope->parameters & mask) {
+ if (!scope->closed) {
+ pm_parser_err_token(parser, token, diag);
+ return;
+ }
+ return;
+ }
+ if (scope->closed) break;
+ scope = scope->previous;
+ }
+
+ pm_parser_err_token(parser, token, diag);
+}
+
+static inline void
+pm_parser_scope_forwarding_block_check(pm_parser_t *parser, const pm_token_t * token) {
+ pm_parser_scope_forwarding_param_check(parser, token, PM_SCOPE_PARAMETERS_FORWARDING_BLOCK, PM_ERR_ARGUMENT_NO_FORWARDING_AMP);
+}
+
+static inline void
+pm_parser_scope_forwarding_positionals_check(pm_parser_t *parser, const pm_token_t * token) {
+ pm_parser_scope_forwarding_param_check(parser, token, PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS, PM_ERR_ARGUMENT_NO_FORWARDING_STAR);
+}
+
+static inline void
+pm_parser_scope_forwarding_all_check(pm_parser_t *parser, const pm_token_t * token) {
+ pm_parser_scope_forwarding_param_check(parser, token, PM_SCOPE_PARAMETERS_FORWARDING_ALL, PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES);
+}
+
+static inline void
+pm_parser_scope_forwarding_keywords_check(pm_parser_t *parser, const pm_token_t * token) {
+ pm_parser_scope_forwarding_param_check(parser, token, PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS, PM_ERR_ARGUMENT_NO_FORWARDING_STAR_STAR);
+}
+
+/**
+ * Get the current state of constant shareability.
+ */
+static inline pm_shareable_constant_value_t
+pm_parser_scope_shareable_constant_get(pm_parser_t *parser) {
+ return parser->current_scope->shareable_constant;
+}
+
+/**
+ * Set the current state of constant shareability. We'll set it on all of the
+ * open scopes so that reads are quick.
+ */
+static void
+pm_parser_scope_shareable_constant_set(pm_parser_t *parser, pm_shareable_constant_value_t shareable_constant) {
+ pm_scope_t *scope = parser->current_scope;
+
+ do {
+ scope->shareable_constant = shareable_constant;
+ } while (!scope->closed && (scope = scope->previous) != NULL);
+}
+
+/******************************************************************************/
+/* Local variable-related functions */
+/******************************************************************************/
+
+/**
+ * The point at which the set of locals switches from being a list to a hash.
+ */
+#define PM_LOCALS_HASH_THRESHOLD 9
+
+static void
+pm_locals_free(pm_locals_t *locals) {
+ if (locals->capacity > 0) {
+ xfree(locals->locals);
+ }
+}
+
+/**
+ * Use as simple and fast a hash function as we can that still properly mixes
+ * the bits.
+ */
+static uint32_t
+pm_locals_hash(pm_constant_id_t name) {
+ name = ((name >> 16) ^ name) * 0x45d9f3b;
+ name = ((name >> 16) ^ name) * 0x45d9f3b;
+ name = (name >> 16) ^ name;
+ return name;
+}
+
+/**
+ * Resize the locals list to be twice its current size. If the next capacity is
+ * above the threshold for switching to a hash, then we'll switch to a hash.
+ */
+static void
+pm_locals_resize(pm_locals_t *locals) {
+ uint32_t next_capacity = locals->capacity == 0 ? 4 : (locals->capacity * 2);
+ assert(next_capacity > locals->capacity);
+
+ pm_local_t *next_locals = xcalloc(next_capacity, sizeof(pm_local_t));
+ if (next_locals == NULL) abort();
+
+ if (next_capacity < PM_LOCALS_HASH_THRESHOLD) {
+ if (locals->size > 0) {
+ memcpy(next_locals, locals->locals, locals->size * sizeof(pm_local_t));
+ }
+ } else {
+ // If we just switched from a list to a hash, then we need to fill in
+ // the hash values of all of the locals.
+ bool hash_needed = (locals->capacity <= PM_LOCALS_HASH_THRESHOLD);
+ uint32_t mask = next_capacity - 1;
+
+ for (uint32_t index = 0; index < locals->capacity; index++) {
+ pm_local_t *local = &locals->locals[index];
+
+ if (local->name != PM_CONSTANT_ID_UNSET) {
+ if (hash_needed) local->hash = pm_locals_hash(local->name);
+
+ uint32_t hash = local->hash;
+ while (next_locals[hash & mask].name != PM_CONSTANT_ID_UNSET) hash++;
+ next_locals[hash & mask] = *local;
+ }
+ }
+ }
+
+ pm_locals_free(locals);
+ locals->locals = next_locals;
+ locals->capacity = next_capacity;
+}
+
+/**
+ * Add a new local to the set of locals. This will automatically rehash the
+ * locals if the size is greater than 3/4 of the capacity.
+ *
+ * @param locals The set of locals to add to.
+ * @param name The name of the local.
+ * @param start The source location that represents the start of the local. This
+ * is used for the location of the warning in case this local is not read.
+ * @param end The source location that represents the end of the local. This is
+ * used for the location of the warning in case this local is not read.
+ * @param reads The initial number of reads for this local. Usually this is set
+ * to 0, but for some locals (like parameters) we want to initialize it with
+ * 1 so that we never warn on unused parameters.
+ * @return True if the local was added, and false if the local already exists.
+ */
+static bool
+pm_locals_write(pm_locals_t *locals, pm_constant_id_t name, const uint8_t *start, const uint8_t *end, uint32_t reads) {
+ if (locals->size >= (locals->capacity / 4 * 3)) {
+ pm_locals_resize(locals);
+ }
+
+ if (locals->capacity < PM_LOCALS_HASH_THRESHOLD) {
+ for (uint32_t index = 0; index < locals->capacity; index++) {
+ pm_local_t *local = &locals->locals[index];
+
+ if (local->name == PM_CONSTANT_ID_UNSET) {
+ *local = (pm_local_t) {
+ .name = name,
+ .location = { .start = start, .end = end },
+ .index = locals->size++,
+ .reads = reads,
+ .hash = 0
+ };
+ return true;
+ } else if (local->name == name) {
+ return false;
+ }
+ }
+ } else {
+ uint32_t mask = locals->capacity - 1;
+ uint32_t hash = pm_locals_hash(name);
+ uint32_t initial_hash = hash;
+
+ do {
+ pm_local_t *local = &locals->locals[hash & mask];
+
+ if (local->name == PM_CONSTANT_ID_UNSET) {
+ *local = (pm_local_t) {
+ .name = name,
+ .location = { .start = start, .end = end },
+ .index = locals->size++,
+ .reads = reads,
+ .hash = initial_hash
+ };
+ return true;
+ } else if (local->name == name) {
+ return false;
+ } else {
+ hash++;
+ }
+ } while ((hash & mask) != initial_hash);
+ }
+
+ assert(false && "unreachable");
+ return true;
+}
+
+/**
+ * Finds the index of a local variable in the locals set. If it is not found,
+ * this returns UINT32_MAX.
+ */
+static uint32_t
+pm_locals_find(pm_locals_t *locals, pm_constant_id_t name) {
+ if (locals->capacity < PM_LOCALS_HASH_THRESHOLD) {
+ for (uint32_t index = 0; index < locals->size; index++) {
+ pm_local_t *local = &locals->locals[index];
+ if (local->name == name) return index;
+ }
+ } else {
+ uint32_t mask = locals->capacity - 1;
+ uint32_t hash = pm_locals_hash(name);
+ uint32_t initial_hash = hash & mask;
+
+ do {
+ pm_local_t *local = &locals->locals[hash & mask];
+
+ if (local->name == PM_CONSTANT_ID_UNSET) {
+ return UINT32_MAX;
+ } else if (local->name == name) {
+ return hash & mask;
+ } else {
+ hash++;
+ }
+ } while ((hash & mask) != initial_hash);
+ }
+
+ return UINT32_MAX;
+}
+
+/**
+ * Called when a variable is read in a certain lexical context. Tracks the read
+ * by adding to the reads count.
+ */
+static void
+pm_locals_read(pm_locals_t *locals, pm_constant_id_t name) {
+ uint32_t index = pm_locals_find(locals, name);
+ assert(index != UINT32_MAX);
+
+ pm_local_t *local = &locals->locals[index];
+ assert(local->reads < UINT32_MAX);
+
+ local->reads++;
+}
+
+/**
+ * Called when a variable read is transformed into a variable write, because a
+ * write operator is found after the variable name.
+ */
+static void
+pm_locals_unread(pm_locals_t *locals, pm_constant_id_t name) {
+ uint32_t index = pm_locals_find(locals, name);
+ assert(index != UINT32_MAX);
+
+ pm_local_t *local = &locals->locals[index];
+ assert(local->reads > 0);
+
+ local->reads--;
+}
+
+/**
+ * Returns the current number of reads for a local variable.
+ */
+static uint32_t
+pm_locals_reads(pm_locals_t *locals, pm_constant_id_t name) {
+ uint32_t index = pm_locals_find(locals, name);
+ assert(index != UINT32_MAX);
+
+ return locals->locals[index].reads;
+}
+
+/**
+ * Write out the locals into the given list of constant ids in the correct
+ * order. This is used to set the list of locals on the nodes in the tree once
+ * we're sure no additional locals will be added to the set.
+ *
+ * This function is also responsible for warning when a local variable has been
+ * written but not read in certain contexts.
+ */
+static void
+pm_locals_order(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, pm_locals_t *locals, pm_constant_id_list_t *list, bool toplevel) {
+ pm_constant_id_list_init_capacity(list, locals->size);
+
+ // If we're still below the threshold for switching to a hash, then we only
+ // need to loop over the locals until we hit the size because the locals are
+ // stored in a list.
+ uint32_t capacity = locals->capacity < PM_LOCALS_HASH_THRESHOLD ? locals->size : locals->capacity;
+
+ // We will only warn for unused variables if we're not at the top level, or
+ // if we're parsing a file outside of eval or -e.
+ bool warn_unused = !toplevel || (!parser->parsing_eval && !PM_PARSER_COMMAND_LINE_OPTION_E(parser));
+
+ for (uint32_t index = 0; index < capacity; index++) {
+ pm_local_t *local = &locals->locals[index];
+
+ if (local->name != PM_CONSTANT_ID_UNSET) {
+ pm_constant_id_list_insert(list, (size_t) local->index, local->name);
+
+ if (warn_unused && local->reads == 0) {
+ pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, local->name);
+
+ if (constant->length >= 1 && *constant->start != '_') {
+ PM_PARSER_WARN_FORMAT(
+ parser,
+ local->location.start,
+ local->location.end,
+ PM_WARN_UNUSED_LOCAL_VARIABLE,
+ (int) constant->length,
+ (const char *) constant->start
+ );
+ }
+ }
+ }
+ }
+}
+
+/******************************************************************************/
+/* Node-related functions */
+/******************************************************************************/
+
+/**
+ * Retrieve the constant pool id for the given location.
+ */
+static inline pm_constant_id_t
+pm_parser_constant_id_location(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
+ return pm_constant_pool_insert_shared(&parser->constant_pool, start, (size_t) (end - start));
+}
+
+/**
+ * Retrieve the constant pool id for the given string.
+ */
+static inline pm_constant_id_t
+pm_parser_constant_id_owned(pm_parser_t *parser, uint8_t *start, size_t length) {
+ return pm_constant_pool_insert_owned(&parser->constant_pool, start, length);
+}
+
+/**
+ * Retrieve the constant pool id for the given static literal C string.
+ */
+static inline pm_constant_id_t
+pm_parser_constant_id_constant(pm_parser_t *parser, const char *start, size_t length) {
+ return pm_constant_pool_insert_constant(&parser->constant_pool, (const uint8_t *) start, length);
+}
+
+/**
+ * Retrieve the constant pool id for the given token.
+ */
+static inline pm_constant_id_t
+pm_parser_constant_id_token(pm_parser_t *parser, const pm_token_t *token) {
+ return pm_parser_constant_id_location(parser, token->start, token->end);
+}
+
+/**
+ * Retrieve the constant pool id for the given token. If the token is not
+ * provided, then return 0.
+ */
+static inline pm_constant_id_t
+pm_parser_optional_constant_id_token(pm_parser_t *parser, const pm_token_t *token) {
+ return token->type == PM_TOKEN_NOT_PROVIDED ? 0 : pm_parser_constant_id_token(parser, token);
+}
+
+/**
+ * Check whether or not the given node is value expression.
+ * If the node is value node, it returns NULL.
+ * If not, it returns the pointer to the node to be inspected as "void expression".
+ */
+static pm_node_t*
+pm_check_value_expression(pm_node_t *node) {
+ pm_node_t* void_node = NULL;
+
+ while (node != NULL) {
+ switch (PM_NODE_TYPE(node)) {
+ case PM_RETURN_NODE:
+ case PM_BREAK_NODE:
+ case PM_NEXT_NODE:
+ case PM_REDO_NODE:
+ case PM_RETRY_NODE:
+ case PM_MATCH_REQUIRED_NODE:
+ return void_node != NULL ? void_node : node;
+ case PM_MATCH_PREDICATE_NODE:
+ return NULL;
+ case PM_BEGIN_NODE: {
+ pm_begin_node_t *cast = (pm_begin_node_t *) node;
+ node = (pm_node_t *) cast->statements;
+ break;
+ }
+ case PM_PARENTHESES_NODE: {
+ pm_parentheses_node_t *cast = (pm_parentheses_node_t *) node;
+ node = (pm_node_t *) cast->body;
+ break;
+ }
+ case PM_STATEMENTS_NODE: {
+ pm_statements_node_t *cast = (pm_statements_node_t *) node;
+ node = cast->body.nodes[cast->body.size - 1];
+ break;
+ }
+ case PM_IF_NODE: {
+ pm_if_node_t *cast = (pm_if_node_t *) node;
+ if (cast->statements == NULL || cast->consequent == NULL) {
+ return NULL;
+ }
+ pm_node_t *vn = pm_check_value_expression((pm_node_t *) cast->statements);
+ if (vn == NULL) {
+ return NULL;
+ }
+ if (void_node == NULL) {
+ void_node = vn;
+ }
+ node = cast->consequent;
+ break;
+ }
+ case PM_UNLESS_NODE: {
+ pm_unless_node_t *cast = (pm_unless_node_t *) node;
+ if (cast->statements == NULL || cast->consequent == NULL) {
+ return NULL;
+ }
+ pm_node_t *vn = pm_check_value_expression((pm_node_t *) cast->statements);
+ if (vn == NULL) {
+ return NULL;
+ }
+ if (void_node == NULL) {
+ void_node = vn;
+ }
+ node = (pm_node_t *) cast->consequent;
+ break;
+ }
+ case PM_ELSE_NODE: {
+ pm_else_node_t *cast = (pm_else_node_t *) node;
+ node = (pm_node_t *) cast->statements;
+ break;
+ }
+ case PM_AND_NODE: {
+ pm_and_node_t *cast = (pm_and_node_t *) node;
+ node = cast->left;
+ break;
+ }
+ case PM_OR_NODE: {
+ pm_or_node_t *cast = (pm_or_node_t *) node;
+ node = cast->left;
+ break;
+ }
+ default:
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+static inline void
+pm_assert_value_expression(pm_parser_t *parser, pm_node_t *node) {
+ pm_node_t *void_node = pm_check_value_expression(node);
+ if (void_node != NULL) {
+ pm_parser_err_node(parser, void_node, PM_ERR_VOID_EXPRESSION);
+ }
+}
+
+/**
+ * Warn if the given node is a "void" statement.
+ */
+static void
+pm_void_statement_check(pm_parser_t *parser, const pm_node_t *node) {
+ const char *type = NULL;
+ int length = 0;
+
+ switch (PM_NODE_TYPE(node)) {
+ case PM_BACK_REFERENCE_READ_NODE:
+ case PM_CLASS_VARIABLE_READ_NODE:
+ case PM_GLOBAL_VARIABLE_READ_NODE:
+ case PM_INSTANCE_VARIABLE_READ_NODE:
+ case PM_LOCAL_VARIABLE_READ_NODE:
+ case PM_NUMBERED_REFERENCE_READ_NODE:
+ type = "a variable";
+ length = 10;
+ break;
+ case PM_CALL_NODE: {
+ const pm_call_node_t *cast = (const pm_call_node_t *) node;
+ if (cast->call_operator_loc.start != NULL || cast->message_loc.start == NULL) break;
+
+ const pm_constant_t *message = pm_constant_pool_id_to_constant(&parser->constant_pool, cast->name);
+ switch (message->length) {
+ case 1:
+ switch (message->start[0]) {
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '%':
+ case '|':
+ case '^':
+ case '&':
+ case '>':
+ case '<':
+ type = (const char *) message->start;
+ length = 1;
+ break;
+ }
+ break;
+ case 2:
+ switch (message->start[1]) {
+ case '=':
+ if (message->start[0] == '<' || message->start[0] == '>' || message->start[0] == '!' || message->start[0] == '=') {
+ type = (const char *) message->start;
+ length = 2;
+ }
+ break;
+ case '@':
+ if (message->start[0] == '+' || message->start[0] == '-') {
+ type = (const char *) message->start;
+ length = 2;
+ }
+ break;
+ case '*':
+ if (message->start[0] == '*') {
+ type = (const char *) message->start;
+ length = 2;
+ }
+ break;
+ }
+ break;
+ case 3:
+ if (memcmp(message->start, "<=>", 3) == 0) {
+ type = "<=>";
+ length = 3;
+ }
+ break;
+ }
+
+ break;
+ }
+ case PM_CONSTANT_PATH_NODE:
+ type = "::";
+ length = 2;
+ break;
+ case PM_CONSTANT_READ_NODE:
+ type = "a constant";
+ length = 10;
+ break;
+ case PM_DEFINED_NODE:
+ type = "defined?";
+ length = 8;
+ break;
+ case PM_FALSE_NODE:
+ type = "false";
+ length = 5;
+ break;
+ case PM_FLOAT_NODE:
+ case PM_IMAGINARY_NODE:
+ case PM_INTEGER_NODE:
+ case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
+ case PM_INTERPOLATED_STRING_NODE:
+ case PM_RATIONAL_NODE:
+ case PM_REGULAR_EXPRESSION_NODE:
+ case PM_SOURCE_ENCODING_NODE:
+ case PM_SOURCE_FILE_NODE:
+ case PM_SOURCE_LINE_NODE:
+ case PM_STRING_NODE:
+ case PM_SYMBOL_NODE:
+ type = "a literal";
+ length = 9;
+ break;
+ case PM_NIL_NODE:
+ type = "nil";
+ length = 3;
+ break;
+ case PM_RANGE_NODE: {
+ const pm_range_node_t *cast = (const pm_range_node_t *) node;
+
+ if (PM_NODE_FLAG_P(cast, PM_RANGE_FLAGS_EXCLUDE_END)) {
+ type = "...";
+ length = 3;
+ } else {
+ type = "..";
+ length = 2;
+ }
+
+ break;
+ }
+ case PM_SELF_NODE:
+ type = "self";
+ length = 4;
+ break;
+ case PM_TRUE_NODE:
+ type = "true";
+ length = 4;
+ break;
+ default:
+ break;
+ }
+
+ if (type != NULL) {
+ PM_PARSER_WARN_NODE_FORMAT(parser, node, PM_WARN_VOID_STATEMENT, length, type);
+ }
+}
+
+/**
+ * Warn if any of the statements that are not the last statement in the list are
+ * a "void" statement.
+ */
+static void
+pm_void_statements_check(pm_parser_t *parser, const pm_statements_node_t *node) {
+ assert(node->body.size > 0);
+ for (size_t index = 0; index < node->body.size - 1; index++) {
+ pm_void_statement_check(parser, node->body.nodes[index]);
+ }
+}
+
+/**
+ * When we're handling the predicate of a conditional, we need to know our
+ * context in order to determine the kind of warning we should deliver to the
+ * user.
+ */
+typedef enum {
+ PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL,
+ PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP,
+ PM_CONDITIONAL_PREDICATE_TYPE_NOT
+} pm_conditional_predicate_type_t;
+
+/**
+ * Add a warning to the parser if the predicate of a conditional is a literal.
+ */
+static void
+pm_parser_warn_conditional_predicate_literal(pm_parser_t *parser, pm_node_t *node, pm_conditional_predicate_type_t type, pm_diagnostic_id_t diag_id, const char *prefix) {
+ switch (type) {
+ case PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL:
+ PM_PARSER_WARN_NODE_FORMAT(parser, node, diag_id, prefix, "condition");
+ break;
+ case PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP:
+ PM_PARSER_WARN_NODE_FORMAT(parser, node, diag_id, prefix, "flip-flop");
+ break;
+ case PM_CONDITIONAL_PREDICATE_TYPE_NOT:
+ break;
+ }
+}
+
+/**
+ * Return true if the value being written within the predicate of a conditional
+ * is a literal value.
+ */
+static bool
+pm_conditional_predicate_warn_write_literal_p(const pm_node_t *node) {
+ switch (PM_NODE_TYPE(node)) {
+ case PM_ARRAY_NODE: {
+ const pm_array_node_t *cast = (const pm_array_node_t *) node;
+ const pm_node_t *element;
+
+ PM_NODE_LIST_FOREACH(&cast->elements, index, element) {
+ if (!pm_conditional_predicate_warn_write_literal_p(element)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ case PM_FALSE_NODE:
+ case PM_FLOAT_NODE:
+ case PM_IMAGINARY_NODE:
+ case PM_INTEGER_NODE:
+ case PM_NIL_NODE:
+ case PM_RATIONAL_NODE:
+ case PM_REGULAR_EXPRESSION_NODE:
+ case PM_SOURCE_ENCODING_NODE:
+ case PM_SOURCE_FILE_NODE:
+ case PM_SOURCE_LINE_NODE:
+ case PM_STRING_NODE:
+ case PM_SYMBOL_NODE:
+ case PM_TRUE_NODE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * Add a warning to the parser if the value that is being written inside of a
+ * predicate to a conditional is a literal.
+ */
+static inline void
+pm_conditional_predicate_warn_write_literal(pm_parser_t *parser, const pm_node_t *node) {
+ if (pm_conditional_predicate_warn_write_literal_p(node)) {
+ pm_parser_warn_node(parser, node, parser->version == PM_OPTIONS_VERSION_CRUBY_3_3_0 ? PM_WARN_EQUAL_IN_CONDITIONAL_3_3_0 : PM_WARN_EQUAL_IN_CONDITIONAL);
+ }
+}
+
+/**
+ * The predicate of conditional nodes can change what would otherwise be regular
+ * nodes into specialized nodes. For example:
+ *
+ * if foo .. bar => RangeNode becomes FlipFlopNode
+ * if foo and bar .. baz => RangeNode becomes FlipFlopNode
+ * if /foo/ => RegularExpressionNode becomes MatchLastLineNode
+ * if /foo #{bar}/ => InterpolatedRegularExpressionNode becomes InterpolatedMatchLastLineNode
+ *
+ * We also want to warn the user if they're using a static literal as a
+ * predicate or writing a static literal as the predicate.
+ */
+static void
+pm_conditional_predicate(pm_parser_t *parser, pm_node_t *node, pm_conditional_predicate_type_t type) {
+ switch (PM_NODE_TYPE(node)) {
+ case PM_AND_NODE: {
+ pm_and_node_t *cast = (pm_and_node_t *) node;
+ pm_conditional_predicate(parser, cast->left, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
+ pm_conditional_predicate(parser, cast->right, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
+ break;
+ }
+ case PM_OR_NODE: {
+ pm_or_node_t *cast = (pm_or_node_t *) node;
+ pm_conditional_predicate(parser, cast->left, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
+ pm_conditional_predicate(parser, cast->right, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
+ break;
+ }
+ case PM_PARENTHESES_NODE: {
+ pm_parentheses_node_t *cast = (pm_parentheses_node_t *) node;
+
+ if ((cast->body != NULL) && PM_NODE_TYPE_P(cast->body, PM_STATEMENTS_NODE)) {
+ pm_statements_node_t *statements = (pm_statements_node_t *) cast->body;
+ if (statements->body.size == 1) pm_conditional_predicate(parser, statements->body.nodes[0], type);
+ }
+
+ break;
+ }
+ case PM_BEGIN_NODE: {
+ pm_begin_node_t *cast = (pm_begin_node_t *) node;
+ if (cast->statements != NULL) {
+ pm_statements_node_t *statements = cast->statements;
+ if (statements->body.size == 1) pm_conditional_predicate(parser, statements->body.nodes[0], type);
+ }
+ break;
+ }
+ case PM_RANGE_NODE: {
+ pm_range_node_t *cast = (pm_range_node_t *) node;
+
+ if (cast->left != NULL) pm_conditional_predicate(parser, cast->left, PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP);
+ if (cast->right != NULL) pm_conditional_predicate(parser, cast->right, PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP);
+
+ // Here we change the range node into a flip flop node. We can do
+ // this since the nodes are exactly the same except for the type.
+ // We're only asserting against the size when we should probably
+ // assert against the entire layout, but we'll assume tests will
+ // catch this.
+ assert(sizeof(pm_range_node_t) == sizeof(pm_flip_flop_node_t));
+ node->type = PM_FLIP_FLOP_NODE;
+
+ break;
+ }
+ case PM_REGULAR_EXPRESSION_NODE:
+ // Here we change the regular expression node into a match last line
+ // node. We can do this since the nodes are exactly the same except
+ // for the type.
+ assert(sizeof(pm_regular_expression_node_t) == sizeof(pm_match_last_line_node_t));
+ node->type = PM_MATCH_LAST_LINE_NODE;
+
+ if (!PM_PARSER_COMMAND_LINE_OPTION_E(parser)) {
+ pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_DEFAULT, "regex ");
+ }
+
+ break;
+ case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
+ // Here we change the interpolated regular expression node into an
+ // interpolated match last line node. We can do this since the nodes
+ // are exactly the same except for the type.
+ assert(sizeof(pm_interpolated_regular_expression_node_t) == sizeof(pm_interpolated_match_last_line_node_t));
+ node->type = PM_INTERPOLATED_MATCH_LAST_LINE_NODE;
+
+ if (!PM_PARSER_COMMAND_LINE_OPTION_E(parser)) {
+ pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "regex ");
+ }
+
+ break;
+ case PM_INTEGER_NODE:
+ if (type == PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP) {
+ if (!PM_PARSER_COMMAND_LINE_OPTION_E(parser)) {
+ pm_parser_warn_node(parser, node, PM_WARN_INTEGER_IN_FLIP_FLOP);
+ }
+ } else {
+ pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "");
+ }
+ break;
+ case PM_STRING_NODE:
+ case PM_SOURCE_FILE_NODE:
+ case PM_INTERPOLATED_STRING_NODE:
+ pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_DEFAULT, "string ");
+ break;
+ case PM_SYMBOL_NODE:
+ case PM_INTERPOLATED_SYMBOL_NODE:
+ pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "symbol ");
+ break;
+ case PM_SOURCE_LINE_NODE:
+ case PM_SOURCE_ENCODING_NODE:
+ case PM_FLOAT_NODE:
+ case PM_RATIONAL_NODE:
+ case PM_IMAGINARY_NODE:
+ pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "");
+ break;
+ case PM_CLASS_VARIABLE_WRITE_NODE:
+ pm_conditional_predicate_warn_write_literal(parser, ((pm_class_variable_write_node_t *) node)->value);
+ break;
+ case PM_CONSTANT_WRITE_NODE:
+ pm_conditional_predicate_warn_write_literal(parser, ((pm_constant_write_node_t *) node)->value);
+ break;
+ case PM_GLOBAL_VARIABLE_WRITE_NODE:
+ pm_conditional_predicate_warn_write_literal(parser, ((pm_global_variable_write_node_t *) node)->value);
+ break;
+ case PM_INSTANCE_VARIABLE_WRITE_NODE:
+ pm_conditional_predicate_warn_write_literal(parser, ((pm_instance_variable_write_node_t *) node)->value);
+ break;
+ case PM_LOCAL_VARIABLE_WRITE_NODE:
+ pm_conditional_predicate_warn_write_literal(parser, ((pm_local_variable_write_node_t *) node)->value);
+ break;
+ case PM_MULTI_WRITE_NODE:
+ pm_conditional_predicate_warn_write_literal(parser, ((pm_multi_write_node_t *) node)->value);
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * In a lot of places in the tree you can have tokens that are not provided but
+ * that do not cause an error. For example, this happens in a method call
+ * without parentheses. In these cases we set the token to the "not provided" type.
+ * For example:
+ *
+ * pm_token_t token = not_provided(parser);
+ */
+static inline pm_token_t
+not_provided(pm_parser_t *parser) {
+ return (pm_token_t) { .type = PM_TOKEN_NOT_PROVIDED, .start = parser->start, .end = parser->start };
+}
+
+#define PM_LOCATION_NULL_VALUE(parser) ((pm_location_t) { .start = parser->start, .end = parser->start })
+#define PM_LOCATION_TOKEN_VALUE(token) ((pm_location_t) { .start = (token)->start, .end = (token)->end })
+#define PM_LOCATION_NODE_VALUE(node) ((pm_location_t) { .start = (node)->location.start, .end = (node)->location.end })
+#define PM_LOCATION_NODE_BASE_VALUE(node) ((pm_location_t) { .start = (node)->base.location.start, .end = (node)->base.location.end })
+#define PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE ((pm_location_t) { .start = NULL, .end = NULL })
+#define PM_OPTIONAL_LOCATION_TOKEN_VALUE(token) ((token)->type == PM_TOKEN_NOT_PROVIDED ? PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE : PM_LOCATION_TOKEN_VALUE(token))
+
+/**
+ * This is a special out parameter to the parse_arguments_list function that
+ * includes opening and closing parentheses in addition to the arguments since
+ * it's so common. It is handy to use when passing argument information to one
+ * of the call node creation functions.
+ */
+typedef struct {
+ /** The optional location of the opening parenthesis or bracket. */
+ pm_location_t opening_loc;
+
+ /** The lazily-allocated optional arguments node. */
+ pm_arguments_node_t *arguments;
+
+ /** The optional location of the closing parenthesis or bracket. */
+ pm_location_t closing_loc;
+
+ /** The optional block attached to the call. */
+ pm_node_t *block;
+
+ /** The flag indicating whether this arguments list has forwarding argument. */
+ bool has_forwarding;
+} pm_arguments_t;
+
+/**
+ * Retrieve the end location of a `pm_arguments_t` object.
+ */
+static inline const uint8_t *
+pm_arguments_end(pm_arguments_t *arguments) {
+ if (arguments->block != NULL) {
+ const uint8_t *end = arguments->block->location.end;
+ if (arguments->closing_loc.start != NULL && arguments->closing_loc.end > end) {
+ end = arguments->closing_loc.end;
+ }
+ return end;
+ }
+ if (arguments->closing_loc.start != NULL) {
+ return arguments->closing_loc.end;
+ }
+ if (arguments->arguments != NULL) {
+ return arguments->arguments->base.location.end;
+ }
+ return arguments->closing_loc.end;
+}
+
+/**
+ * Check that we're not about to attempt to attach a brace block to a call that
+ * has arguments without parentheses.
+ */
+static void
+pm_arguments_validate_block(pm_parser_t *parser, pm_arguments_t *arguments, pm_block_node_t *block) {
+ // First, check that we have arguments and that we don't have a closing
+ // location for them.
+ if (arguments->arguments == NULL || arguments->closing_loc.start != NULL) {
+ return;
+ }
+
+ // Next, check that we don't have a single parentheses argument. This would
+ // look like:
+ //
+ // foo (1) {}
+ //
+ // In this case, it's actually okay for the block to be attached to the
+ // call, even though it looks like it's attached to the argument.
+ if (arguments->arguments->arguments.size == 1 && PM_NODE_TYPE_P(arguments->arguments->arguments.nodes[0], PM_PARENTHESES_NODE)) {
+ return;
+ }
+
+ // If we didn't hit a case before this check, then at this point we need to
+ // add a syntax error.
+ pm_parser_err_node(parser, (pm_node_t *) block, PM_ERR_ARGUMENT_UNEXPECTED_BLOCK);
+}
+
+/******************************************************************************/
+/* Basic character checks */
+/******************************************************************************/
+
+/**
+ * This function is used extremely frequently to lex all of the identifiers in a
+ * source file, so it's important that it be as fast as possible. For this
+ * reason we have the encoding_changed boolean to check if we need to go through
+ * the function pointer or can just directly use the UTF-8 functions.
+ */
+static inline size_t
+char_is_identifier_start(const pm_parser_t *parser, const uint8_t *b) {
+ if (parser->encoding_changed) {
+ size_t width;
+ if ((width = parser->encoding->alpha_char(b, parser->end - b)) != 0) {
+ return width;
+ } else if (*b == '_') {
+ return 1;
+ } else if (*b >= 0x80) {
+ return parser->encoding->char_width(b, parser->end - b);
+ } else {
+ return 0;
+ }
+ } else if (*b < 0x80) {
+ return (pm_encoding_unicode_table[*b] & PRISM_ENCODING_ALPHABETIC_BIT ? 1 : 0) || (*b == '_');
+ } else {
+ return pm_encoding_utf_8_char_width(b, parser->end - b);
+ }
+}
+
+/**
+ * Similar to char_is_identifier but this function assumes that the encoding
+ * has not been changed.
+ */
+static inline size_t
+char_is_identifier_utf8(const uint8_t *b, const uint8_t *end) {
+ if (*b < 0x80) {
+ return (*b == '_') || (pm_encoding_unicode_table[*b] & PRISM_ENCODING_ALPHANUMERIC_BIT ? 1 : 0);
+ } else {
+ return pm_encoding_utf_8_char_width(b, end - b);
+ }
+}
+
+/**
+ * Like the above, this function is also used extremely frequently to lex all of
+ * the identifiers in a source file once the first character has been found. So
+ * it's important that it be as fast as possible.
+ */
+static inline size_t
+char_is_identifier(pm_parser_t *parser, const uint8_t *b) {
+ if (parser->encoding_changed) {
+ size_t width;
+ if ((width = parser->encoding->alnum_char(b, parser->end - b)) != 0) {
+ return width;
+ } else if (*b == '_') {
+ return 1;
+ } else if (*b >= 0x80) {
+ return parser->encoding->char_width(b, parser->end - b);
+ } else {
+ return 0;
+ }
+ }
+ return char_is_identifier_utf8(b, parser->end);
+}
+
+// Here we're defining a perfect hash for the characters that are allowed in
+// global names. This is used to quickly check the next character after a $ to
+// see if it's a valid character for a global name.
+#define BIT(c, idx) (((c) / 32 - 1 == idx) ? (1U << ((c) % 32)) : 0)
+#define PUNCT(idx) ( \
+ BIT('~', idx) | BIT('*', idx) | BIT('$', idx) | BIT('?', idx) | \
+ BIT('!', idx) | BIT('@', idx) | BIT('/', idx) | BIT('\\', idx) | \
+ BIT(';', idx) | BIT(',', idx) | BIT('.', idx) | BIT('=', idx) | \
+ BIT(':', idx) | BIT('<', idx) | BIT('>', idx) | BIT('\"', idx) | \
+ BIT('&', idx) | BIT('`', idx) | BIT('\'', idx) | BIT('+', idx) | \
+ BIT('0', idx))
+
+const unsigned int pm_global_name_punctuation_hash[(0x7e - 0x20 + 31) / 32] = { PUNCT(0), PUNCT(1), PUNCT(2) };
+
+#undef BIT
+#undef PUNCT
+
+static inline bool
+char_is_global_name_punctuation(const uint8_t b) {
+ const unsigned int i = (const unsigned int) b;
+ if (i <= 0x20 || 0x7e < i) return false;
+
+ return (pm_global_name_punctuation_hash[(i - 0x20) / 32] >> (i % 32)) & 1;
+}
+
+static inline bool
+token_is_setter_name(pm_token_t *token) {
+ return (
+ (token->type == PM_TOKEN_IDENTIFIER) &&
+ (token->end - token->start >= 2) &&
+ (token->end[-1] == '=')
+ );
+}
+
+/**
+ * Returns true if the given local variable is a keyword.
+ */
+static bool
+pm_local_is_keyword(const char *source, size_t length) {
+#define KEYWORD(name) if (memcmp(source, name, length) == 0) return true
+
+ switch (length) {
+ case 2:
+ switch (source[0]) {
+ case 'd': KEYWORD("do"); return false;
+ case 'i': KEYWORD("if"); KEYWORD("in"); return false;
+ case 'o': KEYWORD("or"); return false;
+ default: return false;
+ }
+ case 3:
+ switch (source[0]) {
+ case 'a': KEYWORD("and"); return false;
+ case 'd': KEYWORD("def"); return false;
+ case 'e': KEYWORD("end"); return false;
+ case 'f': KEYWORD("for"); return false;
+ case 'n': KEYWORD("nil"); KEYWORD("not"); return false;
+ default: return false;
+ }
+ case 4:
+ switch (source[0]) {
+ case 'c': KEYWORD("case"); return false;
+ case 'e': KEYWORD("else"); return false;
+ case 'n': KEYWORD("next"); return false;
+ case 'r': KEYWORD("redo"); return false;
+ case 's': KEYWORD("self"); return false;
+ case 't': KEYWORD("then"); KEYWORD("true"); return false;
+ case 'w': KEYWORD("when"); return false;
+ default: return false;
+ }
+ case 5:
+ switch (source[0]) {
+ case 'a': KEYWORD("alias"); return false;
+ case 'b': KEYWORD("begin"); KEYWORD("break"); return false;
+ case 'c': KEYWORD("class"); return false;
+ case 'e': KEYWORD("elsif"); return false;
+ case 'f': KEYWORD("false"); return false;
+ case 'r': KEYWORD("retry"); return false;
+ case 's': KEYWORD("super"); return false;
+ case 'u': KEYWORD("undef"); KEYWORD("until"); return false;
+ case 'w': KEYWORD("while"); return false;
+ case 'y': KEYWORD("yield"); return false;
+ default: return false;
+ }
+ case 6:
+ switch (source[0]) {
+ case 'e': KEYWORD("ensure"); return false;
+ case 'm': KEYWORD("module"); return false;
+ case 'r': KEYWORD("rescue"); KEYWORD("return"); return false;
+ case 'u': KEYWORD("unless"); return false;
+ default: return false;
+ }
+ case 8:
+ KEYWORD("__LINE__");
+ KEYWORD("__FILE__");
+ return false;
+ case 12:
+ KEYWORD("__ENCODING__");
+ return false;
+ default:
+ return false;
+ }
+
+#undef KEYWORD
+}
+
+/******************************************************************************/
+/* Node flag handling functions */
+/******************************************************************************/
+
+/**
+ * Set the given flag on the given node.
+ */
+static inline void
+pm_node_flag_set(pm_node_t *node, pm_node_flags_t flag) {
+ node->flags |= flag;
+}
+
+/**
+ * Remove the given flag from the given node.
+ */
+static inline void
+pm_node_flag_unset(pm_node_t *node, pm_node_flags_t flag) {
+ node->flags &= (pm_node_flags_t) ~flag;
+}
+
+/**
+ * Set the repeated parameter flag on the given node.
+ */
+static inline void
+pm_node_flag_set_repeated_parameter(pm_node_t *node) {
+ assert(PM_NODE_TYPE(node) == PM_BLOCK_LOCAL_VARIABLE_NODE ||
+ PM_NODE_TYPE(node) == PM_BLOCK_PARAMETER_NODE ||
+ PM_NODE_TYPE(node) == PM_KEYWORD_REST_PARAMETER_NODE ||
+ PM_NODE_TYPE(node) == PM_OPTIONAL_KEYWORD_PARAMETER_NODE ||
+ PM_NODE_TYPE(node) == PM_OPTIONAL_PARAMETER_NODE ||
+ PM_NODE_TYPE(node) == PM_REQUIRED_KEYWORD_PARAMETER_NODE ||
+ PM_NODE_TYPE(node) == PM_REQUIRED_PARAMETER_NODE ||
+ PM_NODE_TYPE(node) == PM_REST_PARAMETER_NODE);
+
+ pm_node_flag_set(node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER);
+}
+
+/******************************************************************************/
+/* Node creation functions */
+/******************************************************************************/
+
+/**
+ * When you have an encoding flag on a regular expression, it takes precedence
+ * over all of the previously set encoding flags. So we need to mask off any
+ * previously set encoding flags before setting the new one.
+ */
+#define PM_REGULAR_EXPRESSION_ENCODING_MASK ~(PM_REGULAR_EXPRESSION_FLAGS_EUC_JP | PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT | PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J | PM_REGULAR_EXPRESSION_FLAGS_UTF_8)
+
+/**
+ * Parse out the options for a regular expression.
+ */
+static inline pm_node_flags_t
+pm_regular_expression_flags_create(pm_parser_t *parser, const pm_token_t *closing) {
+ pm_node_flags_t flags = 0;
+
+ if (closing->type == PM_TOKEN_REGEXP_END) {
+ pm_buffer_t unknown_flags = { 0 };
+
+ for (const uint8_t *flag = closing->start + 1; flag < closing->end; flag++) {
+ switch (*flag) {
+ case 'i': flags |= PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE; break;
+ case 'm': flags |= PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE; break;
+ case 'x': flags |= PM_REGULAR_EXPRESSION_FLAGS_EXTENDED; break;
+ case 'o': flags |= PM_REGULAR_EXPRESSION_FLAGS_ONCE; break;
+
+ case 'e': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_EUC_JP); break;
+ case 'n': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT); break;
+ case 's': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J); break;
+ case 'u': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_UTF_8); break;
+
+ default: pm_buffer_append_byte(&unknown_flags, *flag);
+ }
+ }
+
+ size_t unknown_flags_length = pm_buffer_length(&unknown_flags);
+ if (unknown_flags_length != 0) {
+ const char *word = unknown_flags_length >= 2 ? "options" : "option";
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->previous, PM_ERR_REGEXP_UNKNOWN_OPTIONS, word, unknown_flags_length, pm_buffer_value(&unknown_flags));
+ }
+ pm_buffer_free(&unknown_flags);
+ }
+
+ return flags;
+}
+
+#undef PM_REGULAR_EXPRESSION_ENCODING_MASK
+
+static pm_statements_node_t *
+pm_statements_node_create(pm_parser_t *parser);
+
+static void
+pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node, pm_node_t *statement);
+
+static size_t
+pm_statements_node_body_length(pm_statements_node_t *node);
+
+/**
+ * This function is here to allow us a place to extend in the future when we
+ * implement our own arena allocation.
+ */
+static inline void *
+pm_alloc_node(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, size_t size) {
+ void *memory = xcalloc(1, size);
+ if (memory == NULL) {
+ fprintf(stderr, "Failed to allocate %d bytes\n", (int) size);
+ abort();
+ }
+ return memory;
+}
+
+#define PM_ALLOC_NODE(parser, type) (type *) pm_alloc_node(parser, sizeof(type))
+
+/**
+ * Allocate a new MissingNode node.
+ */
+static pm_missing_node_t *
+pm_missing_node_create(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
+ pm_missing_node_t *node = PM_ALLOC_NODE(parser, pm_missing_node_t);
+ *node = (pm_missing_node_t) {{ .type = PM_MISSING_NODE, .location = { .start = start, .end = end } }};
+ return node;
+}
+
+/**
+ * Allocate and initialize a new AliasGlobalVariableNode node.
+ */
+static pm_alias_global_variable_node_t *
+pm_alias_global_variable_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *new_name, pm_node_t *old_name) {
+ assert(keyword->type == PM_TOKEN_KEYWORD_ALIAS);
+ pm_alias_global_variable_node_t *node = PM_ALLOC_NODE(parser, pm_alias_global_variable_node_t);
+
+ *node = (pm_alias_global_variable_node_t) {
+ {
+ .type = PM_ALIAS_GLOBAL_VARIABLE_NODE,
+ .location = {
+ .start = keyword->start,
+ .end = old_name->location.end
+ },
+ },
+ .new_name = new_name,
+ .old_name = old_name,
+ .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new AliasMethodNode node.
+ */
+static pm_alias_method_node_t *
+pm_alias_method_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *new_name, pm_node_t *old_name) {
+ assert(keyword->type == PM_TOKEN_KEYWORD_ALIAS);
+ pm_alias_method_node_t *node = PM_ALLOC_NODE(parser, pm_alias_method_node_t);
+
+ *node = (pm_alias_method_node_t) {
+ {
+ .type = PM_ALIAS_METHOD_NODE,
+ .location = {
+ .start = keyword->start,
+ .end = old_name->location.end
+ },
+ },
+ .new_name = new_name,
+ .old_name = old_name,
+ .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate a new AlternationPatternNode node.
+ */
+static pm_alternation_pattern_node_t *
+pm_alternation_pattern_node_create(pm_parser_t *parser, pm_node_t *left, pm_node_t *right, const pm_token_t *operator) {
+ pm_alternation_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_alternation_pattern_node_t);
+
+ *node = (pm_alternation_pattern_node_t) {
+ {
+ .type = PM_ALTERNATION_PATTERN_NODE,
+ .location = {
+ .start = left->location.start,
+ .end = right->location.end
+ },
+ },
+ .left = left,
+ .right = right,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new and node.
+ */
+static pm_and_node_t *
+pm_and_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operator, pm_node_t *right) {
+ pm_assert_value_expression(parser, left);
+
+ pm_and_node_t *node = PM_ALLOC_NODE(parser, pm_and_node_t);
+
+ *node = (pm_and_node_t) {
+ {
+ .type = PM_AND_NODE,
+ .location = {
+ .start = left->location.start,
+ .end = right->location.end
+ },
+ },
+ .left = left,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .right = right
+ };
+
+ return node;
+}
+
+/**
+ * Allocate an initialize a new arguments node.
+ */
+static pm_arguments_node_t *
+pm_arguments_node_create(pm_parser_t *parser) {
+ pm_arguments_node_t *node = PM_ALLOC_NODE(parser, pm_arguments_node_t);
+
+ *node = (pm_arguments_node_t) {
+ {
+ .type = PM_ARGUMENTS_NODE,
+ .location = PM_LOCATION_NULL_VALUE(parser)
+ },
+ .arguments = { 0 }
+ };
+
+ return node;
+}
+
+/**
+ * Return the size of the given arguments node.
+ */
+static size_t
+pm_arguments_node_size(pm_arguments_node_t *node) {
+ return node->arguments.size;
+}
+
+/**
+ * Append an argument to an arguments node.
+ */
+static void
+pm_arguments_node_arguments_append(pm_arguments_node_t *node, pm_node_t *argument) {
+ if (pm_arguments_node_size(node) == 0) {
+ node->base.location.start = argument->location.start;
+ }
+
+ node->base.location.end = argument->location.end;
+ pm_node_list_append(&node->arguments, argument);
+}
+
+/**
+ * Allocate and initialize a new ArrayNode node.
+ */
+static pm_array_node_t *
+pm_array_node_create(pm_parser_t *parser, const pm_token_t *opening) {
+ pm_array_node_t *node = PM_ALLOC_NODE(parser, pm_array_node_t);
+
+ *node = (pm_array_node_t) {
+ {
+ .type = PM_ARRAY_NODE,
+ .flags = PM_NODE_FLAG_STATIC_LITERAL,
+ .location = PM_LOCATION_TOKEN_VALUE(opening)
+ },
+ .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening),
+ .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening),
+ .elements = { 0 }
+ };
+
+ return node;
+}
+
+/**
+ * Return the size of the given array node.
+ */
+static inline size_t
+pm_array_node_size(pm_array_node_t *node) {
+ return node->elements.size;
+}
+
+/**
+ * Append an argument to an array node.
+ */
+static inline void
+pm_array_node_elements_append(pm_array_node_t *node, pm_node_t *element) {
+ if (!node->elements.size && !node->opening_loc.start) {
+ node->base.location.start = element->location.start;
+ }
+
+ pm_node_list_append(&node->elements, element);
+ node->base.location.end = element->location.end;
+
+ // If the element is not a static literal, then the array is not a static
+ // literal. Turn that flag off.
+ if (PM_NODE_TYPE_P(element, PM_ARRAY_NODE) || PM_NODE_TYPE_P(element, PM_HASH_NODE) || PM_NODE_TYPE_P(element, PM_RANGE_NODE) || !PM_NODE_FLAG_P(element, PM_NODE_FLAG_STATIC_LITERAL)) {
+ pm_node_flag_unset((pm_node_t *)node, PM_NODE_FLAG_STATIC_LITERAL);
+ }
+
+ if (PM_NODE_TYPE_P(element, PM_SPLAT_NODE)) {
+ pm_node_flag_set((pm_node_t *)node, PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT);
+ }
+}
+
+/**
+ * Set the closing token and end location of an array node.
+ */
+static void
+pm_array_node_close_set(pm_array_node_t *node, const pm_token_t *closing) {
+ assert(closing->type == PM_TOKEN_BRACKET_RIGHT || closing->type == PM_TOKEN_STRING_END || closing->type == PM_TOKEN_MISSING || closing->type == PM_TOKEN_NOT_PROVIDED);
+ node->base.location.end = closing->end;
+ node->closing_loc = PM_LOCATION_TOKEN_VALUE(closing);
+}
+
+/**
+ * Allocate and initialize a new array pattern node. The node list given in the
+ * nodes parameter is guaranteed to have at least two nodes.
+ */
+static pm_array_pattern_node_t *
+pm_array_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *nodes) {
+ pm_array_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_array_pattern_node_t);
+
+ *node = (pm_array_pattern_node_t) {
+ {
+ .type = PM_ARRAY_PATTERN_NODE,
+ .location = {
+ .start = nodes->nodes[0]->location.start,
+ .end = nodes->nodes[nodes->size - 1]->location.end
+ },
+ },
+ .constant = NULL,
+ .rest = NULL,
+ .requireds = { 0 },
+ .posts = { 0 },
+ .opening_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
+ .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE
+ };
+
+ // For now we're going to just copy over each pointer manually. This could be
+ // much more efficient, as we could instead resize the node list.
+ bool found_rest = false;
+ pm_node_t *child;
+
+ PM_NODE_LIST_FOREACH(nodes, index, child) {
+ if (!found_rest && (PM_NODE_TYPE_P(child, PM_SPLAT_NODE) || PM_NODE_TYPE_P(child, PM_IMPLICIT_REST_NODE))) {
+ node->rest = child;
+ found_rest = true;
+ } else if (found_rest) {
+ pm_node_list_append(&node->posts, child);
+ } else {
+ pm_node_list_append(&node->requireds, child);
+ }
+ }
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new array pattern node from a single rest node.
+ */
+static pm_array_pattern_node_t *
+pm_array_pattern_node_rest_create(pm_parser_t *parser, pm_node_t *rest) {
+ pm_array_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_array_pattern_node_t);
+
+ *node = (pm_array_pattern_node_t) {
+ {
+ .type = PM_ARRAY_PATTERN_NODE,
+ .location = rest->location,
+ },
+ .constant = NULL,
+ .rest = rest,
+ .requireds = { 0 },
+ .posts = { 0 },
+ .opening_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
+ .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new array pattern node from a constant and opening
+ * and closing tokens.
+ */
+static pm_array_pattern_node_t *
+pm_array_pattern_node_constant_create(pm_parser_t *parser, pm_node_t *constant, const pm_token_t *opening, const pm_token_t *closing) {
+ pm_array_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_array_pattern_node_t);
+
+ *node = (pm_array_pattern_node_t) {
+ {
+ .type = PM_ARRAY_PATTERN_NODE,
+ .location = {
+ .start = constant->location.start,
+ .end = closing->end
+ },
+ },
+ .constant = constant,
+ .rest = NULL,
+ .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
+ .closing_loc = PM_LOCATION_TOKEN_VALUE(closing),
+ .requireds = { 0 },
+ .posts = { 0 }
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new array pattern node from an opening and closing
+ * token.
+ */
+static pm_array_pattern_node_t *
+pm_array_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) {
+ pm_array_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_array_pattern_node_t);
+
+ *node = (pm_array_pattern_node_t) {
+ {
+ .type = PM_ARRAY_PATTERN_NODE,
+ .location = {
+ .start = opening->start,
+ .end = closing->end
+ },
+ },
+ .constant = NULL,
+ .rest = NULL,
+ .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
+ .closing_loc = PM_LOCATION_TOKEN_VALUE(closing),
+ .requireds = { 0 },
+ .posts = { 0 }
+ };
+
+ return node;
+}
+
+static inline void
+pm_array_pattern_node_requireds_append(pm_array_pattern_node_t *node, pm_node_t *inner) {
+ pm_node_list_append(&node->requireds, inner);
+}
+
+/**
+ * Allocate and initialize a new assoc node.
+ */
+static pm_assoc_node_t *
+pm_assoc_node_create(pm_parser_t *parser, pm_node_t *key, const pm_token_t *operator, pm_node_t *value) {
+ pm_assoc_node_t *node = PM_ALLOC_NODE(parser, pm_assoc_node_t);
+ const uint8_t *end;
+
+ if (value != NULL && value->location.end > key->location.end) {
+ end = value->location.end;
+ } else if (operator->type != PM_TOKEN_NOT_PROVIDED) {
+ end = operator->end;
+ } else {
+ end = key->location.end;
+ }
+
+ // Hash string keys will be frozen, so we can mark them as frozen here so
+ // that the compiler picks them up and also when we check for static literal
+ // on the keys it gets factored in.
+ if (PM_NODE_TYPE_P(key, PM_STRING_NODE)) {
+ key->flags |= PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL;
+ }
+
+ // If the key and value of this assoc node are both static literals, then
+ // we can mark this node as a static literal.
+ pm_node_flags_t flags = 0;
+ if (value && !PM_NODE_TYPE_P(value, PM_ARRAY_NODE) && !PM_NODE_TYPE_P(value, PM_HASH_NODE) && !PM_NODE_TYPE_P(value, PM_RANGE_NODE)) {
+ flags = key->flags & value->flags & PM_NODE_FLAG_STATIC_LITERAL;
+ }
+
+ *node = (pm_assoc_node_t) {
+ {
+ .type = PM_ASSOC_NODE,
+ .flags = flags,
+ .location = {
+ .start = key->location.start,
+ .end = end
+ },
+ },
+ .key = key,
+ .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator),
+ .value = value
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new assoc splat node.
+ */
+static pm_assoc_splat_node_t *
+pm_assoc_splat_node_create(pm_parser_t *parser, pm_node_t *value, const pm_token_t *operator) {
+ assert(operator->type == PM_TOKEN_USTAR_STAR);
+ pm_assoc_splat_node_t *node = PM_ALLOC_NODE(parser, pm_assoc_splat_node_t);
+
+ *node = (pm_assoc_splat_node_t) {
+ {
+ .type = PM_ASSOC_SPLAT_NODE,
+ .location = {
+ .start = operator->start,
+ .end = value == NULL ? operator->end : value->location.end
+ },
+ },
+ .value = value,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate a new BackReferenceReadNode node.
+ */
+static pm_back_reference_read_node_t *
+pm_back_reference_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
+ assert(name->type == PM_TOKEN_BACK_REFERENCE);
+ pm_back_reference_read_node_t *node = PM_ALLOC_NODE(parser, pm_back_reference_read_node_t);
+
+ *node = (pm_back_reference_read_node_t) {
+ {
+ .type = PM_BACK_REFERENCE_READ_NODE,
+ .location = PM_LOCATION_TOKEN_VALUE(name),
+ },
+ .name = pm_parser_constant_id_token(parser, name)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize new a begin node.
+ */
+static pm_begin_node_t *
+pm_begin_node_create(pm_parser_t *parser, const pm_token_t *begin_keyword, pm_statements_node_t *statements) {
+ pm_begin_node_t *node = PM_ALLOC_NODE(parser, pm_begin_node_t);
+
+ *node = (pm_begin_node_t) {
+ {
+ .type = PM_BEGIN_NODE,
+ .location = {
+ .start = begin_keyword->start,
+ .end = statements == NULL ? begin_keyword->end : statements->base.location.end
+ },
+ },
+ .begin_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(begin_keyword),
+ .statements = statements,
+ .end_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE
+ };
+
+ return node;
+}
+
+/**
+ * Set the rescue clause, optionally start, and end location of a begin node.
+ */
+static void
+pm_begin_node_rescue_clause_set(pm_begin_node_t *node, pm_rescue_node_t *rescue_clause) {
+ // If the begin keyword doesn't exist, we set the start on the begin_node
+ if (!node->begin_keyword_loc.start) {
+ node->base.location.start = rescue_clause->base.location.start;
+ }
+ node->base.location.end = rescue_clause->base.location.end;
+ node->rescue_clause = rescue_clause;
+}
+
+/**
+ * Set the else clause and end location of a begin node.
+ */
+static void
+pm_begin_node_else_clause_set(pm_begin_node_t *node, pm_else_node_t *else_clause) {
+ node->base.location.end = else_clause->base.location.end;
+ node->else_clause = else_clause;
+}
+
+/**
+ * Set the ensure clause and end location of a begin node.
+ */
+static void
+pm_begin_node_ensure_clause_set(pm_begin_node_t *node, pm_ensure_node_t *ensure_clause) {
+ node->base.location.end = ensure_clause->base.location.end;
+ node->ensure_clause = ensure_clause;
+}
+
+/**
+ * Set the end keyword and end location of a begin node.
+ */
+static void
+pm_begin_node_end_keyword_set(pm_begin_node_t *node, const pm_token_t *end_keyword) {
+ assert(end_keyword->type == PM_TOKEN_KEYWORD_END || end_keyword->type == PM_TOKEN_MISSING);
+
+ node->base.location.end = end_keyword->end;
+ node->end_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(end_keyword);
+}
+
+/**
+ * Allocate and initialize a new BlockArgumentNode node.
+ */
+static pm_block_argument_node_t *
+pm_block_argument_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *expression) {
+ pm_block_argument_node_t *node = PM_ALLOC_NODE(parser, pm_block_argument_node_t);
+
+ *node = (pm_block_argument_node_t) {
+ {
+ .type = PM_BLOCK_ARGUMENT_NODE,
+ .location = {
+ .start = operator->start,
+ .end = expression == NULL ? operator->end : expression->location.end
+ },
+ },
+ .expression = expression,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new BlockNode node.
+ */
+static pm_block_node_t *
+pm_block_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const pm_token_t *opening, pm_node_t *parameters, pm_node_t *body, const pm_token_t *closing) {
+ pm_block_node_t *node = PM_ALLOC_NODE(parser, pm_block_node_t);
+
+ *node = (pm_block_node_t) {
+ {
+ .type = PM_BLOCK_NODE,
+ .location = { .start = opening->start, .end = closing->end },
+ },
+ .locals = *locals,
+ .parameters = parameters,
+ .body = body,
+ .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
+ .closing_loc = PM_LOCATION_TOKEN_VALUE(closing)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new BlockParameterNode node.
+ */
+static pm_block_parameter_node_t *
+pm_block_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, const pm_token_t *operator) {
+ assert(operator->type == PM_TOKEN_NOT_PROVIDED || operator->type == PM_TOKEN_UAMPERSAND || operator->type == PM_TOKEN_AMPERSAND);
+ pm_block_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_block_parameter_node_t);
+
+ *node = (pm_block_parameter_node_t) {
+ {
+ .type = PM_BLOCK_PARAMETER_NODE,
+ .location = {
+ .start = operator->start,
+ .end = (name->type == PM_TOKEN_NOT_PROVIDED ? operator->end : name->end)
+ },
+ },
+ .name = pm_parser_optional_constant_id_token(parser, name),
+ .name_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(name),
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new BlockParametersNode node.
+ */
+static pm_block_parameters_node_t *
+pm_block_parameters_node_create(pm_parser_t *parser, pm_parameters_node_t *parameters, const pm_token_t *opening) {
+ pm_block_parameters_node_t *node = PM_ALLOC_NODE(parser, pm_block_parameters_node_t);
+
+ const uint8_t *start;
+ if (opening->type != PM_TOKEN_NOT_PROVIDED) {
+ start = opening->start;
+ } else if (parameters != NULL) {
+ start = parameters->base.location.start;
+ } else {
+ start = NULL;
+ }
+
+ const uint8_t *end;
+ if (parameters != NULL) {
+ end = parameters->base.location.end;
+ } else if (opening->type != PM_TOKEN_NOT_PROVIDED) {
+ end = opening->end;
+ } else {
+ end = NULL;
+ }
+
+ *node = (pm_block_parameters_node_t) {
+ {
+ .type = PM_BLOCK_PARAMETERS_NODE,
+ .location = {
+ .start = start,
+ .end = end
+ }
+ },
+ .parameters = parameters,
+ .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening),
+ .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
+ .locals = { 0 }
+ };
+
+ return node;
+}
+
+/**
+ * Set the closing location of a BlockParametersNode node.
+ */
+static void
+pm_block_parameters_node_closing_set(pm_block_parameters_node_t *node, const pm_token_t *closing) {
+ assert(closing->type == PM_TOKEN_PIPE || closing->type == PM_TOKEN_PARENTHESIS_RIGHT || closing->type == PM_TOKEN_MISSING);
+
+ node->base.location.end = closing->end;
+ node->closing_loc = PM_LOCATION_TOKEN_VALUE(closing);
+}
+
+/**
+ * Allocate and initialize a new BlockLocalVariableNode node.
+ */
+static pm_block_local_variable_node_t *
+pm_block_local_variable_node_create(pm_parser_t *parser, const pm_token_t *name) {
+ pm_block_local_variable_node_t *node = PM_ALLOC_NODE(parser, pm_block_local_variable_node_t);
+
+ *node = (pm_block_local_variable_node_t) {
+ {
+ .type = PM_BLOCK_LOCAL_VARIABLE_NODE,
+ .location = PM_LOCATION_TOKEN_VALUE(name),
+ },
+ .name = pm_parser_constant_id_token(parser, name)
+ };
+
+ return node;
+}
+
+/**
+ * Append a new block-local variable to a BlockParametersNode node.
+ */
+static void
+pm_block_parameters_node_append_local(pm_block_parameters_node_t *node, const pm_block_local_variable_node_t *local) {
+ pm_node_list_append(&node->locals, (pm_node_t *) local);
+
+ if (node->base.location.start == NULL) node->base.location.start = local->base.location.start;
+ node->base.location.end = local->base.location.end;
+}
+
+/**
+ * Allocate and initialize a new BreakNode node.
+ */
+static pm_break_node_t *
+pm_break_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_node_t *arguments) {
+ assert(keyword->type == PM_TOKEN_KEYWORD_BREAK);
+ pm_break_node_t *node = PM_ALLOC_NODE(parser, pm_break_node_t);
+
+ *node = (pm_break_node_t) {
+ {
+ .type = PM_BREAK_NODE,
+ .location = {
+ .start = keyword->start,
+ .end = (arguments == NULL ? keyword->end : arguments->base.location.end)
+ },
+ },
+ .arguments = arguments,
+ .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword)
+ };
+
+ return node;
+}
+
+// There are certain flags that we want to use internally but don't want to
+// expose because they are not relevant beyond parsing. Therefore we'll define
+// them here and not define them in config.yml/a header file.
+static const pm_node_flags_t PM_CALL_NODE_FLAGS_COMPARISON = 0x10;
+static const pm_node_flags_t PM_CALL_NODE_FLAGS_INDEX = 0x20;
+
+/**
+ * Allocate and initialize a new CallNode node. This sets everything to NULL or
+ * PM_TOKEN_NOT_PROVIDED as appropriate such that its values can be overridden
+ * in the various specializations of this function.
+ */
+static pm_call_node_t *
+pm_call_node_create(pm_parser_t *parser, pm_node_flags_t flags) {
+ pm_call_node_t *node = PM_ALLOC_NODE(parser, pm_call_node_t);
+
+ *node = (pm_call_node_t) {
+ {
+ .type = PM_CALL_NODE,
+ .flags = flags,
+ .location = PM_LOCATION_NULL_VALUE(parser),
+ },
+ .receiver = NULL,
+ .call_operator_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
+ .message_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
+ .opening_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
+ .arguments = NULL,
+ .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
+ .block = NULL,
+ .name = 0
+ };
+
+ return node;
+}
+
+/**
+ * Returns the value that the ignore visibility flag should be set to for the
+ * given receiver.
+ */
+static inline pm_node_flags_t
+pm_call_node_ignore_visibility_flag(const pm_node_t *receiver) {
+ return PM_NODE_TYPE_P(receiver, PM_SELF_NODE) ? PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY : 0;
+}
+
+/**
+ * Allocate and initialize a new CallNode node from an aref or an aset
+ * expression.
+ */
+static pm_call_node_t *
+pm_call_node_aref_create(pm_parser_t *parser, pm_node_t *receiver, pm_arguments_t *arguments) {
+ pm_assert_value_expression(parser, receiver);
+
+ pm_node_flags_t flags = pm_call_node_ignore_visibility_flag(receiver);
+ if (arguments->block == NULL || PM_NODE_TYPE_P(arguments->block, PM_BLOCK_ARGUMENT_NODE)) {
+ flags |= PM_CALL_NODE_FLAGS_INDEX;
+ }
+
+ pm_call_node_t *node = pm_call_node_create(parser, flags);
+
+ node->base.location.start = receiver->location.start;
+ node->base.location.end = pm_arguments_end(arguments);
+
+ node->receiver = receiver;
+ node->message_loc.start = arguments->opening_loc.start;
+ node->message_loc.end = arguments->closing_loc.end;
+
+ node->opening_loc = arguments->opening_loc;
+ node->arguments = arguments->arguments;
+ node->closing_loc = arguments->closing_loc;
+ node->block = arguments->block;
+
+ node->name = pm_parser_constant_id_constant(parser, "[]", 2);
+ return node;
+}
+
+/**
+ * Allocate and initialize a new CallNode node from a binary expression.
+ */
+static pm_call_node_t *
+pm_call_node_binary_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *operator, pm_node_t *argument, pm_node_flags_t flags) {
+ pm_assert_value_expression(parser, receiver);
+ pm_assert_value_expression(parser, argument);
+
+ pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver) | flags);
+
+ node->base.location.start = MIN(receiver->location.start, argument->location.start);
+ node->base.location.end = MAX(receiver->location.end, argument->location.end);
+
+ node->receiver = receiver;
+ node->message_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator);
+
+ pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
+ pm_arguments_node_arguments_append(arguments, argument);
+ node->arguments = arguments;
+
+ node->name = pm_parser_constant_id_token(parser, operator);
+ return node;
+}
+
+/**
+ * Allocate and initialize a new CallNode node from a call expression.
+ */
+static pm_call_node_t *
+pm_call_node_call_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *operator, pm_token_t *message, pm_arguments_t *arguments) {
+ pm_assert_value_expression(parser, receiver);
+
+ pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver));
+
+ node->base.location.start = receiver->location.start;
+ const uint8_t *end = pm_arguments_end(arguments);
+ if (end == NULL) {
+ end = message->end;
+ }
+ node->base.location.end = end;
+
+ node->receiver = receiver;
+ node->call_operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator);
+ node->message_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(message);
+ node->opening_loc = arguments->opening_loc;
+ node->arguments = arguments->arguments;
+ node->closing_loc = arguments->closing_loc;
+ node->block = arguments->block;
+
+ if (operator->type == PM_TOKEN_AMPERSAND_DOT) {
+ pm_node_flag_set((pm_node_t *)node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION);
+ }
+
+ node->name = pm_parser_constant_id_token(parser, message);
+ return node;
+}
+
+/**
+ * Allocate and initialize a new synthesized CallNode node from a call expression.
+ */
+static pm_call_node_t *
+pm_call_node_call_synthesized_create(pm_parser_t *parser, pm_node_t *receiver, const char *message, pm_arguments_node_t *arguments) {
+ pm_call_node_t *node = pm_call_node_create(parser, 0);
+ node->base.location.start = parser->start;
+ node->base.location.end = parser->end;
+
+ node->receiver = receiver;
+ node->call_operator_loc = (pm_location_t) { .start = NULL, .end = NULL };
+ node->message_loc = (pm_location_t) { .start = NULL, .end = NULL };
+ node->arguments = arguments;
+
+ node->name = pm_parser_constant_id_constant(parser, message, strlen(message));
+ return node;
+}
+
+/**
+ * Allocate and initialize a new CallNode node from a call to a method name
+ * without a receiver that could not have been a local variable read.
+ */
+static pm_call_node_t *
+pm_call_node_fcall_create(pm_parser_t *parser, pm_token_t *message, pm_arguments_t *arguments) {
+ pm_call_node_t *node = pm_call_node_create(parser, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY);
+
+ node->base.location.start = message->start;
+ node->base.location.end = pm_arguments_end(arguments);
+
+ node->message_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(message);
+ node->opening_loc = arguments->opening_loc;
+ node->arguments = arguments->arguments;
+ node->closing_loc = arguments->closing_loc;
+ node->block = arguments->block;
+
+ node->name = pm_parser_constant_id_token(parser, message);
+ return node;
+}
+
+/**
+ * Allocate and initialize a new CallNode node from a synthesized call to a
+ * method name with the given arguments.
+ */
+static pm_call_node_t *
+pm_call_node_fcall_synthesized_create(pm_parser_t *parser, pm_arguments_node_t *arguments, pm_constant_id_t name) {
+ pm_call_node_t *node = pm_call_node_create(parser, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY);
+
+ node->base.location.start = parser->start;
+ node->base.location.end = parser->start;
+ node->arguments = arguments;
+
+ node->name = name;
+ return node;
+}
+
+/**
+ * Allocate and initialize a new CallNode node from a not expression.
+ */
+static pm_call_node_t *
+pm_call_node_not_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *message, pm_arguments_t *arguments) {
+ pm_assert_value_expression(parser, receiver);
+ if (receiver != NULL) pm_conditional_predicate(parser, receiver, PM_CONDITIONAL_PREDICATE_TYPE_NOT);
+
+ pm_call_node_t *node = pm_call_node_create(parser, receiver == NULL ? 0 : pm_call_node_ignore_visibility_flag(receiver));
+
+ node->base.location.start = message->start;
+ if (arguments->closing_loc.start != NULL) {
+ node->base.location.end = arguments->closing_loc.end;
+ } else {
+ assert(receiver != NULL);
+ node->base.location.end = receiver->location.end;
+ }
+
+ node->receiver = receiver;
+ node->message_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(message);
+ node->opening_loc = arguments->opening_loc;
+ node->arguments = arguments->arguments;
+ node->closing_loc = arguments->closing_loc;
+
+ node->name = pm_parser_constant_id_constant(parser, "!", 1);
+ return node;
+}
+
+/**
+ * Allocate and initialize a new CallNode node from a call shorthand expression.
+ */
+static pm_call_node_t *
+pm_call_node_shorthand_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *operator, pm_arguments_t *arguments) {
+ pm_assert_value_expression(parser, receiver);
+
+ pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver));
+
+ node->base.location.start = receiver->location.start;
+ node->base.location.end = pm_arguments_end(arguments);
+
+ node->receiver = receiver;
+ node->call_operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator);
+ node->opening_loc = arguments->opening_loc;
+ node->arguments = arguments->arguments;
+ node->closing_loc = arguments->closing_loc;
+ node->block = arguments->block;
+
+ if (operator->type == PM_TOKEN_AMPERSAND_DOT) {
+ pm_node_flag_set((pm_node_t *)node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION);
+ }
+
+ node->name = pm_parser_constant_id_constant(parser, "call", 4);
+ return node;
+}
+
+/**
+ * Allocate and initialize a new CallNode node from a unary operator expression.
+ */
+static pm_call_node_t *
+pm_call_node_unary_create(pm_parser_t *parser, pm_token_t *operator, pm_node_t *receiver, const char *name) {
+ pm_assert_value_expression(parser, receiver);
+
+ pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver));
+
+ node->base.location.start = operator->start;
+ node->base.location.end = receiver->location.end;
+
+ node->receiver = receiver;
+ node->message_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator);
+
+ node->name = pm_parser_constant_id_constant(parser, name, strlen(name));
+ return node;
+}
+
+/**
+ * Allocate and initialize a new CallNode node from a call to a method name
+ * without a receiver that could also have been a local variable read.
+ */
+static pm_call_node_t *
+pm_call_node_variable_call_create(pm_parser_t *parser, pm_token_t *message) {
+ pm_call_node_t *node = pm_call_node_create(parser, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY);
+
+ node->base.location = PM_LOCATION_TOKEN_VALUE(message);
+ node->message_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(message);
+
+ node->name = pm_parser_constant_id_token(parser, message);
+ return node;
+}
+
+/**
+ * Returns whether or not this call can be used on the left-hand side of an
+ * operator assignment.
+ */
+static inline bool
+pm_call_node_writable_p(const pm_parser_t *parser, const pm_call_node_t *node) {
+ return (
+ (node->message_loc.start != NULL) &&
+ (node->message_loc.end[-1] != '!') &&
+ (node->message_loc.end[-1] != '?') &&
+ char_is_identifier_start(parser, node->message_loc.start) &&
+ (node->opening_loc.start == NULL) &&
+ (node->arguments == NULL) &&
+ (node->block == NULL)
+ );
+}
+
+/**
+ * Initialize the read name by reading the write name and chopping off the '='.
+ */
+static void
+pm_call_write_read_name_init(pm_parser_t *parser, pm_constant_id_t *read_name, pm_constant_id_t *write_name) {
+ pm_constant_t *write_constant = pm_constant_pool_id_to_constant(&parser->constant_pool, *write_name);
+
+ if (write_constant->length > 0) {
+ size_t length = write_constant->length - 1;
+
+ void *memory = xmalloc(length);
+ memcpy(memory, write_constant->start, length);
+
+ *read_name = pm_constant_pool_insert_owned(&parser->constant_pool, (uint8_t *) memory, length);
+ } else {
+ // We can get here if the message was missing because of a syntax error.
+ *read_name = pm_parser_constant_id_constant(parser, "", 0);
+ }
+}
+
+/**
+ * Allocate and initialize a new CallAndWriteNode node.
+ */
+static pm_call_and_write_node_t *
+pm_call_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ assert(target->block == NULL);
+ assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
+ pm_call_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_call_and_write_node_t);
+
+ *node = (pm_call_and_write_node_t) {
+ {
+ .type = PM_CALL_AND_WRITE_NODE,
+ .flags = target->base.flags,
+ .location = {
+ .start = target->base.location.start,
+ .end = value->location.end
+ }
+ },
+ .receiver = target->receiver,
+ .call_operator_loc = target->call_operator_loc,
+ .message_loc = target->message_loc,
+ .read_name = 0,
+ .write_name = target->name,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value
+ };
+
+ pm_call_write_read_name_init(parser, &node->read_name, &node->write_name);
+
+ // Here we're going to free the target, since it is no longer necessary.
+ // However, we don't want to call `pm_node_destroy` because we want to keep
+ // around all of its children since we just reused them.
+ xfree(target);
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new IndexAndWriteNode node.
+ */
+static pm_index_and_write_node_t *
+pm_index_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
+ pm_index_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_index_and_write_node_t);
+
+ *node = (pm_index_and_write_node_t) {
+ {
+ .type = PM_INDEX_AND_WRITE_NODE,
+ .flags = target->base.flags,
+ .location = {
+ .start = target->base.location.start,
+ .end = value->location.end
+ }
+ },
+ .receiver = target->receiver,
+ .call_operator_loc = target->call_operator_loc,
+ .opening_loc = target->opening_loc,
+ .arguments = target->arguments,
+ .closing_loc = target->closing_loc,
+ .block = target->block,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value
+ };
+
+ // Here we're going to free the target, since it is no longer necessary.
+ // However, we don't want to call `pm_node_destroy` because we want to keep
+ // around all of its children since we just reused them.
+ xfree(target);
+
+ return node;
+}
+
+/**
+ * Allocate a new CallOperatorWriteNode node.
+ */
+static pm_call_operator_write_node_t *
+pm_call_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ assert(target->block == NULL);
+ pm_call_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_call_operator_write_node_t);
+
+ *node = (pm_call_operator_write_node_t) {
+ {
+ .type = PM_CALL_OPERATOR_WRITE_NODE,
+ .flags = target->base.flags,
+ .location = {
+ .start = target->base.location.start,
+ .end = value->location.end
+ }
+ },
+ .receiver = target->receiver,
+ .call_operator_loc = target->call_operator_loc,
+ .message_loc = target->message_loc,
+ .read_name = 0,
+ .write_name = target->name,
+ .operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1),
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value
+ };
+
+ pm_call_write_read_name_init(parser, &node->read_name, &node->write_name);
+
+ // Here we're going to free the target, since it is no longer necessary.
+ // However, we don't want to call `pm_node_destroy` because we want to keep
+ // around all of its children since we just reused them.
+ xfree(target);
+
+ return node;
+}
+
+/**
+ * Allocate a new IndexOperatorWriteNode node.
+ */
+static pm_index_operator_write_node_t *
+pm_index_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ pm_index_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_index_operator_write_node_t);
+
+ *node = (pm_index_operator_write_node_t) {
+ {
+ .type = PM_INDEX_OPERATOR_WRITE_NODE,
+ .flags = target->base.flags,
+ .location = {
+ .start = target->base.location.start,
+ .end = value->location.end
+ }
+ },
+ .receiver = target->receiver,
+ .call_operator_loc = target->call_operator_loc,
+ .opening_loc = target->opening_loc,
+ .arguments = target->arguments,
+ .closing_loc = target->closing_loc,
+ .block = target->block,
+ .operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1),
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value
+ };
+
+ // Here we're going to free the target, since it is no longer necessary.
+ // However, we don't want to call `pm_node_destroy` because we want to keep
+ // around all of its children since we just reused them.
+ xfree(target);
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new CallOrWriteNode node.
+ */
+static pm_call_or_write_node_t *
+pm_call_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ assert(target->block == NULL);
+ assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
+ pm_call_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_call_or_write_node_t);
+
+ *node = (pm_call_or_write_node_t) {
+ {
+ .type = PM_CALL_OR_WRITE_NODE,
+ .flags = target->base.flags,
+ .location = {
+ .start = target->base.location.start,
+ .end = value->location.end
+ }
+ },
+ .receiver = target->receiver,
+ .call_operator_loc = target->call_operator_loc,
+ .message_loc = target->message_loc,
+ .read_name = 0,
+ .write_name = target->name,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value
+ };
+
+ pm_call_write_read_name_init(parser, &node->read_name, &node->write_name);
+
+ // Here we're going to free the target, since it is no longer necessary.
+ // However, we don't want to call `pm_node_destroy` because we want to keep
+ // around all of its children since we just reused them.
+ xfree(target);
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new IndexOrWriteNode node.
+ */
+static pm_index_or_write_node_t *
+pm_index_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
+ pm_index_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_index_or_write_node_t);
+
+ *node = (pm_index_or_write_node_t) {
+ {
+ .type = PM_INDEX_OR_WRITE_NODE,
+ .flags = target->base.flags,
+ .location = {
+ .start = target->base.location.start,
+ .end = value->location.end
+ }
+ },
+ .receiver = target->receiver,
+ .call_operator_loc = target->call_operator_loc,
+ .opening_loc = target->opening_loc,
+ .arguments = target->arguments,
+ .closing_loc = target->closing_loc,
+ .block = target->block,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value
+ };
+
+ // Here we're going to free the target, since it is no longer necessary.
+ // However, we don't want to call `pm_node_destroy` because we want to keep
+ // around all of its children since we just reused them.
+ xfree(target);
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new CallTargetNode node from an existing call
+ * node.
+ */
+static pm_call_target_node_t *
+pm_call_target_node_create(pm_parser_t *parser, pm_call_node_t *target) {
+ pm_call_target_node_t *node = PM_ALLOC_NODE(parser, pm_call_target_node_t);
+
+ *node = (pm_call_target_node_t) {
+ {
+ .type = PM_CALL_TARGET_NODE,
+ .flags = target->base.flags,
+ .location = target->base.location
+ },
+ .receiver = target->receiver,
+ .call_operator_loc = target->call_operator_loc,
+ .name = target->name,
+ .message_loc = target->message_loc
+ };
+
+ // Here we're going to free the target, since it is no longer necessary.
+ // However, we don't want to call `pm_node_destroy` because we want to keep
+ // around all of its children since we just reused them.
+ xfree(target);
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new IndexTargetNode node from an existing call
+ * node.
+ */
+static pm_index_target_node_t *
+pm_index_target_node_create(pm_parser_t *parser, pm_call_node_t *target) {
+ pm_index_target_node_t *node = PM_ALLOC_NODE(parser, pm_index_target_node_t);
+ pm_node_flags_t flags = target->base.flags;
+
+ *node = (pm_index_target_node_t) {
+ {
+ .type = PM_INDEX_TARGET_NODE,
+ .flags = flags | PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE,
+ .location = target->base.location
+ },
+ .receiver = target->receiver,
+ .opening_loc = target->opening_loc,
+ .arguments = target->arguments,
+ .closing_loc = target->closing_loc,
+ .block = target->block
+ };
+
+ // Here we're going to free the target, since it is no longer necessary.
+ // However, we don't want to call `pm_node_destroy` because we want to keep
+ // around all of its children since we just reused them.
+ xfree(target);
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new CapturePatternNode node.
+ */
+static pm_capture_pattern_node_t *
+pm_capture_pattern_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t *target, const pm_token_t *operator) {
+ pm_capture_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_capture_pattern_node_t);
+
+ *node = (pm_capture_pattern_node_t) {
+ {
+ .type = PM_CAPTURE_PATTERN_NODE,
+ .location = {
+ .start = value->location.start,
+ .end = target->location.end
+ },
+ },
+ .value = value,
+ .target = target,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new CaseNode node.
+ */
+static pm_case_node_t *
+pm_case_node_create(pm_parser_t *parser, const pm_token_t *case_keyword, pm_node_t *predicate, const pm_token_t *end_keyword) {
+ pm_case_node_t *node = PM_ALLOC_NODE(parser, pm_case_node_t);
+
+ *node = (pm_case_node_t) {
+ {
+ .type = PM_CASE_NODE,
+ .location = {
+ .start = case_keyword->start,
+ .end = end_keyword->end
+ },
+ },
+ .predicate = predicate,
+ .consequent = NULL,
+ .case_keyword_loc = PM_LOCATION_TOKEN_VALUE(case_keyword),
+ .end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword),
+ .conditions = { 0 }
+ };
+
+ return node;
+}
+
+/**
+ * Append a new condition to a CaseNode node.
+ */
+static void
+pm_case_node_condition_append(pm_case_node_t *node, pm_node_t *condition) {
+ assert(PM_NODE_TYPE_P(condition, PM_WHEN_NODE));
+
+ pm_node_list_append(&node->conditions, condition);
+ node->base.location.end = condition->location.end;
+}
+
+/**
+ * Set the consequent of a CaseNode node.
+ */
+static void
+pm_case_node_consequent_set(pm_case_node_t *node, pm_else_node_t *consequent) {
+ node->consequent = consequent;
+ node->base.location.end = consequent->base.location.end;
+}
+
+/**
+ * Set the end location for a CaseNode node.
+ */
+static void
+pm_case_node_end_keyword_loc_set(pm_case_node_t *node, const pm_token_t *end_keyword) {
+ node->base.location.end = end_keyword->end;
+ node->end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword);
+}
+
+/**
+ * Allocate and initialize a new CaseMatchNode node.
+ */
+static pm_case_match_node_t *
+pm_case_match_node_create(pm_parser_t *parser, const pm_token_t *case_keyword, pm_node_t *predicate, const pm_token_t *end_keyword) {
+ pm_case_match_node_t *node = PM_ALLOC_NODE(parser, pm_case_match_node_t);
+
+ *node = (pm_case_match_node_t) {
+ {
+ .type = PM_CASE_MATCH_NODE,
+ .location = {
+ .start = case_keyword->start,
+ .end = end_keyword->end
+ },
+ },
+ .predicate = predicate,
+ .consequent = NULL,
+ .case_keyword_loc = PM_LOCATION_TOKEN_VALUE(case_keyword),
+ .end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword),
+ .conditions = { 0 }
+ };
+
+ return node;
+}
+
+/**
+ * Append a new condition to a CaseMatchNode node.
+ */
+static void
+pm_case_match_node_condition_append(pm_case_match_node_t *node, pm_node_t *condition) {
+ assert(PM_NODE_TYPE_P(condition, PM_IN_NODE));
+
+ pm_node_list_append(&node->conditions, condition);
+ node->base.location.end = condition->location.end;
+}
+
+/**
+ * Set the consequent of a CaseMatchNode node.
+ */
+static void
+pm_case_match_node_consequent_set(pm_case_match_node_t *node, pm_else_node_t *consequent) {
+ node->consequent = consequent;
+ node->base.location.end = consequent->base.location.end;
+}
+
+/**
+ * Set the end location for a CaseMatchNode node.
+ */
+static void
+pm_case_match_node_end_keyword_loc_set(pm_case_match_node_t *node, const pm_token_t *end_keyword) {
+ node->base.location.end = end_keyword->end;
+ node->end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword);
+}
+
+/**
+ * Allocate a new ClassNode node.
+ */
+static pm_class_node_t *
+pm_class_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const pm_token_t *class_keyword, pm_node_t *constant_path, const pm_token_t *name, const pm_token_t *inheritance_operator, pm_node_t *superclass, pm_node_t *body, const pm_token_t *end_keyword) {
+ pm_class_node_t *node = PM_ALLOC_NODE(parser, pm_class_node_t);
+
+ *node = (pm_class_node_t) {
+ {
+ .type = PM_CLASS_NODE,
+ .location = { .start = class_keyword->start, .end = end_keyword->end },
+ },
+ .locals = *locals,
+ .class_keyword_loc = PM_LOCATION_TOKEN_VALUE(class_keyword),
+ .constant_path = constant_path,
+ .inheritance_operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(inheritance_operator),
+ .superclass = superclass,
+ .body = body,
+ .end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword),
+ .name = pm_parser_constant_id_token(parser, name)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new ClassVariableAndWriteNode node.
+ */
+static pm_class_variable_and_write_node_t *
+pm_class_variable_and_write_node_create(pm_parser_t *parser, pm_class_variable_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
+ pm_class_variable_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_class_variable_and_write_node_t);
+
+ *node = (pm_class_variable_and_write_node_t) {
+ {
+ .type = PM_CLASS_VARIABLE_AND_WRITE_NODE,
+ .location = {
+ .start = target->base.location.start,
+ .end = value->location.end
+ }
+ },
+ .name = target->name,
+ .name_loc = target->base.location,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new ClassVariableOperatorWriteNode node.
+ */
+static pm_class_variable_operator_write_node_t *
+pm_class_variable_operator_write_node_create(pm_parser_t *parser, pm_class_variable_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ pm_class_variable_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_class_variable_operator_write_node_t);
+
+ *node = (pm_class_variable_operator_write_node_t) {
+ {
+ .type = PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE,
+ .location = {
+ .start = target->base.location.start,
+ .end = value->location.end
+ }
+ },
+ .name = target->name,
+ .name_loc = target->base.location,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value,
+ .operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new ClassVariableOrWriteNode node.
+ */
+static pm_class_variable_or_write_node_t *
+pm_class_variable_or_write_node_create(pm_parser_t *parser, pm_class_variable_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
+ pm_class_variable_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_class_variable_or_write_node_t);
+
+ *node = (pm_class_variable_or_write_node_t) {
+ {
+ .type = PM_CLASS_VARIABLE_OR_WRITE_NODE,
+ .location = {
+ .start = target->base.location.start,
+ .end = value->location.end
+ }
+ },
+ .name = target->name,
+ .name_loc = target->base.location,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new ClassVariableReadNode node.
+ */
+static pm_class_variable_read_node_t *
+pm_class_variable_read_node_create(pm_parser_t *parser, const pm_token_t *token) {
+ assert(token->type == PM_TOKEN_CLASS_VARIABLE);
+ pm_class_variable_read_node_t *node = PM_ALLOC_NODE(parser, pm_class_variable_read_node_t);
+
+ *node = (pm_class_variable_read_node_t) {
+ {
+ .type = PM_CLASS_VARIABLE_READ_NODE,
+ .location = PM_LOCATION_TOKEN_VALUE(token)
+ },
+ .name = pm_parser_constant_id_token(parser, token)
+ };
+
+ return node;
+}
+
+/**
+ * Initialize a new ClassVariableWriteNode node from a ClassVariableRead node.
+ */
+static pm_class_variable_write_node_t *
+pm_class_variable_write_node_create(pm_parser_t *parser, pm_class_variable_read_node_t *read_node, pm_token_t *operator, pm_node_t *value) {
+ pm_class_variable_write_node_t *node = PM_ALLOC_NODE(parser, pm_class_variable_write_node_t);
+
+ *node = (pm_class_variable_write_node_t) {
+ {
+ .type = PM_CLASS_VARIABLE_WRITE_NODE,
+ .location = {
+ .start = read_node->base.location.start,
+ .end = value->location.end
+ },
+ },
+ .name = read_node->name,
+ .name_loc = PM_LOCATION_NODE_VALUE((pm_node_t *) read_node),
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new ConstantPathAndWriteNode node.
+ */
+static pm_constant_path_and_write_node_t *
+pm_constant_path_and_write_node_create(pm_parser_t *parser, pm_constant_path_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
+ pm_constant_path_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_constant_path_and_write_node_t);
+
+ *node = (pm_constant_path_and_write_node_t) {
+ {
+ .type = PM_CONSTANT_PATH_AND_WRITE_NODE,
+ .location = {
+ .start = target->base.location.start,
+ .end = value->location.end
+ }
+ },
+ .target = target,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new ConstantPathOperatorWriteNode node.
+ */
+static pm_constant_path_operator_write_node_t *
+pm_constant_path_operator_write_node_create(pm_parser_t *parser, pm_constant_path_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ pm_constant_path_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_constant_path_operator_write_node_t);
+
+ *node = (pm_constant_path_operator_write_node_t) {
+ {
+ .type = PM_CONSTANT_PATH_OPERATOR_WRITE_NODE,
+ .location = {
+ .start = target->base.location.start,
+ .end = value->location.end
+ }
+ },
+ .target = target,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value,
+ .operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new ConstantPathOrWriteNode node.
+ */
+static pm_constant_path_or_write_node_t *
+pm_constant_path_or_write_node_create(pm_parser_t *parser, pm_constant_path_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
+ pm_constant_path_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_constant_path_or_write_node_t);
+
+ *node = (pm_constant_path_or_write_node_t) {
+ {
+ .type = PM_CONSTANT_PATH_OR_WRITE_NODE,
+ .location = {
+ .start = target->base.location.start,
+ .end = value->location.end
+ }
+ },
+ .target = target,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new ConstantPathNode node.
+ */
+static pm_constant_path_node_t *
+pm_constant_path_node_create(pm_parser_t *parser, pm_node_t *parent, const pm_token_t *delimiter, pm_node_t *child) {
+ pm_assert_value_expression(parser, parent);
+
+ pm_constant_path_node_t *node = PM_ALLOC_NODE(parser, pm_constant_path_node_t);
+
+ *node = (pm_constant_path_node_t) {
+ {
+ .type = PM_CONSTANT_PATH_NODE,
+ .location = {
+ .start = parent == NULL ? delimiter->start : parent->location.start,
+ .end = child->location.end
+ },
+ },
+ .parent = parent,
+ .child = child,
+ .delimiter_loc = PM_LOCATION_TOKEN_VALUE(delimiter)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate a new ConstantPathWriteNode node.
+ */
+static pm_constant_path_write_node_t *
+pm_constant_path_write_node_create(pm_parser_t *parser, pm_constant_path_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ pm_constant_path_write_node_t *node = PM_ALLOC_NODE(parser, pm_constant_path_write_node_t);
+
+ *node = (pm_constant_path_write_node_t) {
+ {
+ .type = PM_CONSTANT_PATH_WRITE_NODE,
+ .location = {
+ .start = target->base.location.start,
+ .end = value->location.end
+ },
+ },
+ .target = target,
+ .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator),
+ .value = value
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new ConstantAndWriteNode node.
+ */
+static pm_constant_and_write_node_t *
+pm_constant_and_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
+ pm_constant_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_constant_and_write_node_t);
+
+ *node = (pm_constant_and_write_node_t) {
+ {
+ .type = PM_CONSTANT_AND_WRITE_NODE,
+ .location = {
+ .start = target->base.location.start,
+ .end = value->location.end
+ }
+ },
+ .name = target->name,
+ .name_loc = target->base.location,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new ConstantOperatorWriteNode node.
+ */
+static pm_constant_operator_write_node_t *
+pm_constant_operator_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ pm_constant_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_constant_operator_write_node_t);
+
+ *node = (pm_constant_operator_write_node_t) {
+ {
+ .type = PM_CONSTANT_OPERATOR_WRITE_NODE,
+ .location = {
+ .start = target->base.location.start,
+ .end = value->location.end
+ }
+ },
+ .name = target->name,
+ .name_loc = target->base.location,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value,
+ .operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new ConstantOrWriteNode node.
+ */
+static pm_constant_or_write_node_t *
+pm_constant_or_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
+ pm_constant_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_constant_or_write_node_t);
+
+ *node = (pm_constant_or_write_node_t) {
+ {
+ .type = PM_CONSTANT_OR_WRITE_NODE,
+ .location = {
+ .start = target->base.location.start,
+ .end = value->location.end
+ }
+ },
+ .name = target->name,
+ .name_loc = target->base.location,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new ConstantReadNode node.
+ */
+static pm_constant_read_node_t *
+pm_constant_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
+ assert(name->type == PM_TOKEN_CONSTANT || name->type == PM_TOKEN_MISSING);
+ pm_constant_read_node_t *node = PM_ALLOC_NODE(parser, pm_constant_read_node_t);
+
+ *node = (pm_constant_read_node_t) {
+ {
+ .type = PM_CONSTANT_READ_NODE,
+ .location = PM_LOCATION_TOKEN_VALUE(name)
+ },
+ .name = pm_parser_constant_id_token(parser, name)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate a new ConstantWriteNode node.
+ */
+static pm_constant_write_node_t *
+pm_constant_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ pm_constant_write_node_t *node = PM_ALLOC_NODE(parser, pm_constant_write_node_t);
+
+ *node = (pm_constant_write_node_t) {
+ {
+ .type = PM_CONSTANT_WRITE_NODE,
+ .location = {
+ .start = target->base.location.start,
+ .end = value->location.end
+ }
+ },
+ .name = target->name,
+ .name_loc = target->base.location,
+ .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator),
+ .value = value
+ };
+
+ return node;
+}
+
+/**
+ * Check if the receiver of a `def` node is allowed.
+ */
+static void
+pm_def_node_receiver_check(pm_parser_t *parser, const pm_node_t *node) {
+ switch (PM_NODE_TYPE(node)) {
+ case PM_BEGIN_NODE: {
+ const pm_begin_node_t *cast = (pm_begin_node_t *) node;
+ if (cast->statements != NULL) pm_def_node_receiver_check(parser, (pm_node_t *) cast->statements);
+ break;
+ }
+ case PM_PARENTHESES_NODE: {
+ const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
+ if (cast->body != NULL) pm_def_node_receiver_check(parser, cast->body);
+ break;
+ }
+ case PM_STATEMENTS_NODE: {
+ const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
+ pm_def_node_receiver_check(parser, cast->body.nodes[cast->body.size - 1]);
+ break;
+ }
+ case PM_ARRAY_NODE:
+ case PM_FLOAT_NODE:
+ case PM_IMAGINARY_NODE:
+ case PM_INTEGER_NODE:
+ case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
+ case PM_INTERPOLATED_STRING_NODE:
+ case PM_INTERPOLATED_SYMBOL_NODE:
+ case PM_INTERPOLATED_X_STRING_NODE:
+ case PM_RATIONAL_NODE:
+ case PM_REGULAR_EXPRESSION_NODE:
+ case PM_SOURCE_ENCODING_NODE:
+ case PM_SOURCE_FILE_NODE:
+ case PM_SOURCE_LINE_NODE:
+ case PM_STRING_NODE:
+ case PM_SYMBOL_NODE:
+ case PM_X_STRING_NODE:
+ pm_parser_err_node(parser, node, PM_ERR_SINGLETON_FOR_LITERALS);
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * When a method body is created, we want to check if the last statement is a
+ * return or a statement that houses a return. If it is, then we want to mark
+ * that return as being redundant so that we can compile it differently but also
+ * so that we can indicate that to the user.
+ */
+static void
+pm_def_node_body_redundant_return(pm_node_t *node) {
+ switch (PM_NODE_TYPE(node)) {
+ case PM_RETURN_NODE:
+ node->flags |= PM_RETURN_NODE_FLAGS_REDUNDANT;
+ break;
+ case PM_BEGIN_NODE: {
+ pm_begin_node_t *cast = (pm_begin_node_t *) node;
+
+ if (cast->statements != NULL && cast->else_clause == NULL) {
+ pm_def_node_body_redundant_return((pm_node_t *) cast->statements);
+ }
+ break;
+ }
+ case PM_STATEMENTS_NODE: {
+ pm_statements_node_t *cast = (pm_statements_node_t *) node;
+
+ if (cast->body.size > 0) {
+ pm_def_node_body_redundant_return(cast->body.nodes[cast->body.size - 1]);
+ }
+ break;
+ }
+ case PM_IF_NODE: {
+ pm_if_node_t *cast = (pm_if_node_t *) node;
+
+ if (cast->statements != NULL) {
+ pm_def_node_body_redundant_return((pm_node_t *) cast->statements);
+ }
+
+ if (cast->consequent != NULL) {
+ pm_def_node_body_redundant_return(cast->consequent);
+ }
+ break;
+ }
+ case PM_UNLESS_NODE: {
+ pm_unless_node_t *cast = (pm_unless_node_t *) node;
+
+ if (cast->statements != NULL) {
+ pm_def_node_body_redundant_return((pm_node_t *) cast->statements);
+ }
+
+ if (cast->consequent != NULL) {
+ pm_def_node_body_redundant_return((pm_node_t *) cast->consequent);
+ }
+ break;
+ }
+ case PM_ELSE_NODE: {
+ pm_else_node_t *cast = (pm_else_node_t *) node;
+
+ if (cast->statements != NULL) {
+ pm_def_node_body_redundant_return((pm_node_t *) cast->statements);
+ }
+ break;
+ }
+ case PM_CASE_NODE: {
+ pm_case_node_t *cast = (pm_case_node_t *) node;
+ pm_node_t *condition;
+
+ PM_NODE_LIST_FOREACH(&cast->conditions, index, condition) {
+ pm_def_node_body_redundant_return(condition);
+ }
+
+ if (cast->consequent != NULL) {
+ pm_def_node_body_redundant_return((pm_node_t *) cast->consequent);
+ }
+ break;
+ }
+ case PM_WHEN_NODE: {
+ pm_when_node_t *cast = (pm_when_node_t *) node;
+
+ if (cast->statements != NULL) {
+ pm_def_node_body_redundant_return((pm_node_t *) cast->statements);
+ }
+ break;
+ }
+ case PM_CASE_MATCH_NODE: {
+ pm_case_match_node_t *cast = (pm_case_match_node_t *) node;
+ pm_node_t *condition;
+
+ PM_NODE_LIST_FOREACH(&cast->conditions, index, condition) {
+ pm_def_node_body_redundant_return(condition);
+ }
+
+ if (cast->consequent != NULL) {
+ pm_def_node_body_redundant_return((pm_node_t *) cast->consequent);
+ }
+ break;
+ }
+ case PM_IN_NODE: {
+ pm_in_node_t *cast = (pm_in_node_t *) node;
+
+ if (cast->statements != NULL) {
+ pm_def_node_body_redundant_return((pm_node_t *) cast->statements);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/**
+ * Allocate and initialize a new DefNode node.
+ */
+static pm_def_node_t *
+pm_def_node_create(
+ pm_parser_t *parser,
+ pm_constant_id_t name,
+ const pm_token_t *name_loc,
+ pm_node_t *receiver,
+ pm_parameters_node_t *parameters,
+ pm_node_t *body,
+ pm_constant_id_list_t *locals,
+ const pm_token_t *def_keyword,
+ const pm_token_t *operator,
+ const pm_token_t *lparen,
+ const pm_token_t *rparen,
+ const pm_token_t *equal,
+ const pm_token_t *end_keyword
+) {
+ pm_def_node_t *node = PM_ALLOC_NODE(parser, pm_def_node_t);
+ const uint8_t *end;
+
+ if (end_keyword->type == PM_TOKEN_NOT_PROVIDED) {
+ end = body->location.end;
+ } else {
+ end = end_keyword->end;
+ }
+
+ if ((receiver != NULL) && PM_NODE_TYPE_P(receiver, PM_PARENTHESES_NODE)) {
+ pm_def_node_receiver_check(parser, receiver);
+ }
+
+ if (body != NULL) {
+ pm_def_node_body_redundant_return(body);
+ }
+
+ *node = (pm_def_node_t) {
+ {
+ .type = PM_DEF_NODE,
+ .location = { .start = def_keyword->start, .end = end },
+ },
+ .name = name,
+ .name_loc = PM_LOCATION_TOKEN_VALUE(name_loc),
+ .receiver = receiver,
+ .parameters = parameters,
+ .body = body,
+ .locals = *locals,
+ .def_keyword_loc = PM_LOCATION_TOKEN_VALUE(def_keyword),
+ .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator),
+ .lparen_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(lparen),
+ .rparen_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(rparen),
+ .equal_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(equal),
+ .end_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(end_keyword)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate a new DefinedNode node.
+ */
+static pm_defined_node_t *
+pm_defined_node_create(pm_parser_t *parser, const pm_token_t *lparen, pm_node_t *value, const pm_token_t *rparen, const pm_location_t *keyword_loc) {
+ pm_defined_node_t *node = PM_ALLOC_NODE(parser, pm_defined_node_t);
+
+ *node = (pm_defined_node_t) {
+ {
+ .type = PM_DEFINED_NODE,
+ .location = {
+ .start = keyword_loc->start,
+ .end = (rparen->type == PM_TOKEN_NOT_PROVIDED ? value->location.end : rparen->end)
+ },
+ },
+ .lparen_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(lparen),
+ .value = value,
+ .rparen_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(rparen),
+ .keyword_loc = *keyword_loc
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new ElseNode node.
+ */
+static pm_else_node_t *
+pm_else_node_create(pm_parser_t *parser, const pm_token_t *else_keyword, pm_statements_node_t *statements, const pm_token_t *end_keyword) {
+ pm_else_node_t *node = PM_ALLOC_NODE(parser, pm_else_node_t);
+ const uint8_t *end = NULL;
+ if ((end_keyword->type == PM_TOKEN_NOT_PROVIDED) && (statements != NULL)) {
+ end = statements->base.location.end;
+ } else {
+ end = end_keyword->end;
+ }
+
+ *node = (pm_else_node_t) {
+ {
+ .type = PM_ELSE_NODE,
+ .location = {
+ .start = else_keyword->start,
+ .end = end,
+ },
+ },
+ .else_keyword_loc = PM_LOCATION_TOKEN_VALUE(else_keyword),
+ .statements = statements,
+ .end_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(end_keyword)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new EmbeddedStatementsNode node.
+ */
+static pm_embedded_statements_node_t *
+pm_embedded_statements_node_create(pm_parser_t *parser, const pm_token_t *opening, pm_statements_node_t *statements, const pm_token_t *closing) {
+ pm_embedded_statements_node_t *node = PM_ALLOC_NODE(parser, pm_embedded_statements_node_t);
+
+ *node = (pm_embedded_statements_node_t) {
+ {
+ .type = PM_EMBEDDED_STATEMENTS_NODE,
+ .location = {
+ .start = opening->start,
+ .end = closing->end
+ }
+ },
+ .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
+ .statements = statements,
+ .closing_loc = PM_LOCATION_TOKEN_VALUE(closing)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new EmbeddedVariableNode node.
+ */
+static pm_embedded_variable_node_t *
+pm_embedded_variable_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *variable) {
+ pm_embedded_variable_node_t *node = PM_ALLOC_NODE(parser, pm_embedded_variable_node_t);
+
+ *node = (pm_embedded_variable_node_t) {
+ {
+ .type = PM_EMBEDDED_VARIABLE_NODE,
+ .location = {
+ .start = operator->start,
+ .end = variable->location.end
+ }
+ },
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .variable = variable
+ };
+
+ return node;
+}
+
+/**
+ * Allocate a new EnsureNode node.
+ */
+static pm_ensure_node_t *
+pm_ensure_node_create(pm_parser_t *parser, const pm_token_t *ensure_keyword, pm_statements_node_t *statements, const pm_token_t *end_keyword) {
+ pm_ensure_node_t *node = PM_ALLOC_NODE(parser, pm_ensure_node_t);
+
+ *node = (pm_ensure_node_t) {
+ {
+ .type = PM_ENSURE_NODE,
+ .location = {
+ .start = ensure_keyword->start,
+ .end = end_keyword->end
+ },
+ },
+ .ensure_keyword_loc = PM_LOCATION_TOKEN_VALUE(ensure_keyword),
+ .statements = statements,
+ .end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new FalseNode node.
+ */
+static pm_false_node_t *
+pm_false_node_create(pm_parser_t *parser, const pm_token_t *token) {
+ assert(token->type == PM_TOKEN_KEYWORD_FALSE);
+ pm_false_node_t *node = PM_ALLOC_NODE(parser, pm_false_node_t);
+
+ *node = (pm_false_node_t) {{
+ .type = PM_FALSE_NODE,
+ .flags = PM_NODE_FLAG_STATIC_LITERAL,
+ .location = PM_LOCATION_TOKEN_VALUE(token)
+ }};
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new find pattern node. The node list given in the
+ * nodes parameter is guaranteed to have at least two nodes.
+ */
+static pm_find_pattern_node_t *
+pm_find_pattern_node_create(pm_parser_t *parser, pm_node_list_t *nodes) {
+ pm_find_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_find_pattern_node_t);
+
+ pm_node_t *left = nodes->nodes[0];
+ pm_node_t *right;
+
+ if (nodes->size == 1) {
+ right = (pm_node_t *) pm_missing_node_create(parser, left->location.end, left->location.end);
+ } else {
+ right = nodes->nodes[nodes->size - 1];
+ }
+
+ *node = (pm_find_pattern_node_t) {
+ {
+ .type = PM_FIND_PATTERN_NODE,
+ .location = {
+ .start = left->location.start,
+ .end = right->location.end,
+ },
+ },
+ .constant = NULL,
+ .left = left,
+ .right = right,
+ .requireds = { 0 },
+ .opening_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
+ .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE
+ };
+
+ // For now we're going to just copy over each pointer manually. This could be
+ // much more efficient, as we could instead resize the node list to only point
+ // to 1...-1.
+ for (size_t index = 1; index < nodes->size - 1; index++) {
+ pm_node_list_append(&node->requireds, nodes->nodes[index]);
+ }
+
+ return node;
+}
+
+/**
+ * Parse the value of a double, add appropriate errors if there is an issue, and
+ * return the value that should be saved on the PM_FLOAT_NODE node.
+ */
+static double
+pm_double_parse(pm_parser_t *parser, const pm_token_t *token) {
+ ptrdiff_t diff = token->end - token->start;
+ if (diff <= 0) return 0.0;
+
+ // First, get a buffer of the content.
+ size_t length = (size_t) diff;
+ char *buffer = xmalloc(sizeof(char) * (length + 1));
+ memcpy((void *) buffer, token->start, length);
+
+ // Next, determine if we need to replace the decimal point because of
+ // locale-specific options, and then normalize them if we have to.
+ char decimal_point = *localeconv()->decimal_point;
+ if (decimal_point != '.') {
+ for (size_t index = 0; index < length; index++) {
+ if (buffer[index] == '.') buffer[index] = decimal_point;
+ }
+ }
+
+ // Next, handle underscores by removing them from the buffer.
+ for (size_t index = 0; index < length; index++) {
+ if (buffer[index] == '_') {
+ memmove((void *) (buffer + index), (void *) (buffer + index + 1), length - index);
+ length--;
+ }
+ }
+
+ // Null-terminate the buffer so that strtod cannot read off the end.
+ buffer[length] = '\0';
+
+ // Now, call strtod to parse the value. Note that CRuby has their own
+ // version of strtod which avoids locales. We're okay using the locale-aware
+ // version because we've already validated through the parser that the token
+ // is in a valid format.
+ errno = 0;
+ char *eptr;
+ double value = strtod(buffer, &eptr);
+
+ // This should never happen, because we've already checked that the token
+ // is in a valid format. However it's good to be safe.
+ if ((eptr != buffer + length) || (errno != 0 && errno != ERANGE)) {
+ PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, (*token), PM_ERR_FLOAT_PARSE);
+ xfree((void *) buffer);
+ return 0.0;
+ }
+
+ // If errno is set, then it should only be ERANGE. At this point we need to
+ // check if it's infinity (it should be).
+ if (errno == ERANGE && isinf(value)) {
+ int warn_width;
+ const char *ellipsis;
+
+ if (length > 20) {
+ warn_width = 20;
+ ellipsis = "...";
+ } else {
+ warn_width = (int) length;
+ ellipsis = "";
+ }
+
+ pm_diagnostic_list_append_format(&parser->warning_list, token->start, token->end, PM_WARN_FLOAT_OUT_OF_RANGE, warn_width, (const char *) token->start, ellipsis);
+ value = (value < 0.0) ? -HUGE_VAL : HUGE_VAL;
+ }
+
+ // Finally we can free the buffer and return the value.
+ xfree((void *) buffer);
+ return value;
+}
+
+/**
+ * Allocate and initialize a new FloatNode node.
+ */
+static pm_float_node_t *
+pm_float_node_create(pm_parser_t *parser, const pm_token_t *token) {
+ assert(token->type == PM_TOKEN_FLOAT);
+ pm_float_node_t *node = PM_ALLOC_NODE(parser, pm_float_node_t);
+
+ *node = (pm_float_node_t) {
+ {
+ .type = PM_FLOAT_NODE,
+ .flags = PM_NODE_FLAG_STATIC_LITERAL,
+ .location = PM_LOCATION_TOKEN_VALUE(token)
+ },
+ .value = pm_double_parse(parser, token)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new FloatNode node from a FLOAT_IMAGINARY token.
+ */
+static pm_imaginary_node_t *
+pm_float_node_imaginary_create(pm_parser_t *parser, const pm_token_t *token) {
+ assert(token->type == PM_TOKEN_FLOAT_IMAGINARY);
+
+ pm_imaginary_node_t *node = PM_ALLOC_NODE(parser, pm_imaginary_node_t);
+ *node = (pm_imaginary_node_t) {
+ {
+ .type = PM_IMAGINARY_NODE,
+ .flags = PM_NODE_FLAG_STATIC_LITERAL,
+ .location = PM_LOCATION_TOKEN_VALUE(token)
+ },
+ .numeric = (pm_node_t *) pm_float_node_create(parser, &((pm_token_t) {
+ .type = PM_TOKEN_FLOAT,
+ .start = token->start,
+ .end = token->end - 1
+ }))
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new FloatNode node from a FLOAT_RATIONAL token.
+ */
+static pm_rational_node_t *
+pm_float_node_rational_create(pm_parser_t *parser, const pm_token_t *token) {
+ assert(token->type == PM_TOKEN_FLOAT_RATIONAL);
+
+ pm_rational_node_t *node = PM_ALLOC_NODE(parser, pm_rational_node_t);
+ *node = (pm_rational_node_t) {
+ {
+ .type = PM_RATIONAL_NODE,
+ .flags = PM_NODE_FLAG_STATIC_LITERAL,
+ .location = PM_LOCATION_TOKEN_VALUE(token)
+ },
+ .numeric = (pm_node_t *) pm_float_node_create(parser, &((pm_token_t) {
+ .type = PM_TOKEN_FLOAT,
+ .start = token->start,
+ .end = token->end - 1
+ }))
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new FloatNode node from a FLOAT_RATIONAL_IMAGINARY
+ * token.
+ */
+static pm_imaginary_node_t *
+pm_float_node_rational_imaginary_create(pm_parser_t *parser, const pm_token_t *token) {
+ assert(token->type == PM_TOKEN_FLOAT_RATIONAL_IMAGINARY);
+
+ pm_imaginary_node_t *node = PM_ALLOC_NODE(parser, pm_imaginary_node_t);
+ *node = (pm_imaginary_node_t) {
+ {
+ .type = PM_IMAGINARY_NODE,
+ .flags = PM_NODE_FLAG_STATIC_LITERAL,
+ .location = PM_LOCATION_TOKEN_VALUE(token)
+ },
+ .numeric = (pm_node_t *) pm_float_node_rational_create(parser, &((pm_token_t) {
+ .type = PM_TOKEN_FLOAT_RATIONAL,
+ .start = token->start,
+ .end = token->end - 1
+ }))
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new ForNode node.
+ */
+static pm_for_node_t *
+pm_for_node_create(
+ pm_parser_t *parser,
+ pm_node_t *index,
+ pm_node_t *collection,
+ pm_statements_node_t *statements,
+ const pm_token_t *for_keyword,
+ const pm_token_t *in_keyword,
+ const pm_token_t *do_keyword,
+ const pm_token_t *end_keyword
+) {
+ pm_for_node_t *node = PM_ALLOC_NODE(parser, pm_for_node_t);
+
+ *node = (pm_for_node_t) {
+ {
+ .type = PM_FOR_NODE,
+ .location = {
+ .start = for_keyword->start,
+ .end = end_keyword->end
+ },
+ },
+ .index = index,
+ .collection = collection,
+ .statements = statements,
+ .for_keyword_loc = PM_LOCATION_TOKEN_VALUE(for_keyword),
+ .in_keyword_loc = PM_LOCATION_TOKEN_VALUE(in_keyword),
+ .do_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(do_keyword),
+ .end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new ForwardingArgumentsNode node.
+ */
+static pm_forwarding_arguments_node_t *
+pm_forwarding_arguments_node_create(pm_parser_t *parser, const pm_token_t *token) {
+ assert(token->type == PM_TOKEN_UDOT_DOT_DOT);
+ pm_forwarding_arguments_node_t *node = PM_ALLOC_NODE(parser, pm_forwarding_arguments_node_t);
+ *node = (pm_forwarding_arguments_node_t) {{ .type = PM_FORWARDING_ARGUMENTS_NODE, .location = PM_LOCATION_TOKEN_VALUE(token) }};
+ return node;
+}
+
+/**
+ * Allocate and initialize a new ForwardingParameterNode node.
+ */
+static pm_forwarding_parameter_node_t *
+pm_forwarding_parameter_node_create(pm_parser_t *parser, const pm_token_t *token) {
+ assert(token->type == PM_TOKEN_UDOT_DOT_DOT);
+ pm_forwarding_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_forwarding_parameter_node_t);
+ *node = (pm_forwarding_parameter_node_t) {{ .type = PM_FORWARDING_PARAMETER_NODE, .location = PM_LOCATION_TOKEN_VALUE(token) }};
+ return node;
+}
+
+/**
+ * Allocate and initialize a new ForwardingSuper node.
+ */
+static pm_forwarding_super_node_t *
+pm_forwarding_super_node_create(pm_parser_t *parser, const pm_token_t *token, pm_arguments_t *arguments) {
+ assert(arguments->block == NULL || PM_NODE_TYPE_P(arguments->block, PM_BLOCK_NODE));
+ assert(token->type == PM_TOKEN_KEYWORD_SUPER);
+ pm_forwarding_super_node_t *node = PM_ALLOC_NODE(parser, pm_forwarding_super_node_t);
+
+ pm_block_node_t *block = NULL;
+ if (arguments->block != NULL) {
+ block = (pm_block_node_t *) arguments->block;
+ }
+
+ *node = (pm_forwarding_super_node_t) {
+ {
+ .type = PM_FORWARDING_SUPER_NODE,
+ .location = {
+ .start = token->start,
+ .end = block != NULL ? block->base.location.end : token->end
+ },
+ },
+ .block = block
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new hash pattern node from an opening and closing
+ * token.
+ */
+static pm_hash_pattern_node_t *
+pm_hash_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) {
+ pm_hash_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_hash_pattern_node_t);
+
+ *node = (pm_hash_pattern_node_t) {
+ {
+ .type = PM_HASH_PATTERN_NODE,
+ .location = {
+ .start = opening->start,
+ .end = closing->end
+ },
+ },
+ .constant = NULL,
+ .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
+ .closing_loc = PM_LOCATION_TOKEN_VALUE(closing),
+ .elements = { 0 },
+ .rest = NULL
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new hash pattern node.
+ */
+static pm_hash_pattern_node_t *
+pm_hash_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *elements, pm_node_t *rest) {
+ pm_hash_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_hash_pattern_node_t);
+
+ const uint8_t *start;
+ const uint8_t *end;
+
+ if (elements->size > 0) {
+ if (rest) {
+ start = elements->nodes[0]->location.start;
+ end = rest->location.end;
+ } else {
+ start = elements->nodes[0]->location.start;
+ end = elements->nodes[elements->size - 1]->location.end;
+ }
+ } else {
+ assert(rest != NULL);
+ start = rest->location.start;
+ end = rest->location.end;
+ }
+
+ *node = (pm_hash_pattern_node_t) {
+ {
+ .type = PM_HASH_PATTERN_NODE,
+ .location = {
+ .start = start,
+ .end = end
+ },
+ },
+ .constant = NULL,
+ .elements = { 0 },
+ .rest = rest,
+ .opening_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
+ .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE
+ };
+
+ pm_node_t *element;
+ PM_NODE_LIST_FOREACH(elements, index, element) {
+ pm_node_list_append(&node->elements, element);
+ }
+
+ return node;
+}
+
+/**
+ * Retrieve the name from a node that will become a global variable write node.
+ */
+static pm_constant_id_t
+pm_global_variable_write_name(pm_parser_t *parser, const pm_node_t *target) {
+ switch (PM_NODE_TYPE(target)) {
+ case PM_GLOBAL_VARIABLE_READ_NODE:
+ return ((pm_global_variable_read_node_t *) target)->name;
+ case PM_BACK_REFERENCE_READ_NODE:
+ return ((pm_back_reference_read_node_t *) target)->name;
+ case PM_NUMBERED_REFERENCE_READ_NODE:
+ // This will only ever happen in the event of a syntax error, but we
+ // still need to provide something for the node.
+ return pm_parser_constant_id_location(parser, target->location.start, target->location.end);
+ default:
+ assert(false && "unreachable");
+ return (pm_constant_id_t) -1;
+ }
+}
+
+/**
+ * Allocate and initialize a new GlobalVariableAndWriteNode node.
+ */
+static pm_global_variable_and_write_node_t *
+pm_global_variable_and_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
+ pm_global_variable_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_global_variable_and_write_node_t);
+
+ *node = (pm_global_variable_and_write_node_t) {
+ {
+ .type = PM_GLOBAL_VARIABLE_AND_WRITE_NODE,
+ .location = {
+ .start = target->location.start,
+ .end = value->location.end
+ }
+ },
+ .name = pm_global_variable_write_name(parser, target),
+ .name_loc = target->location,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new GlobalVariableOperatorWriteNode node.
+ */
+static pm_global_variable_operator_write_node_t *
+pm_global_variable_operator_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ pm_global_variable_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_global_variable_operator_write_node_t);
+
+ *node = (pm_global_variable_operator_write_node_t) {
+ {
+ .type = PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE,
+ .location = {
+ .start = target->location.start,
+ .end = value->location.end
+ }
+ },
+ .name = pm_global_variable_write_name(parser, target),
+ .name_loc = target->location,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value,
+ .operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new GlobalVariableOrWriteNode node.
+ */
+static pm_global_variable_or_write_node_t *
+pm_global_variable_or_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
+ pm_global_variable_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_global_variable_or_write_node_t);
+
+ *node = (pm_global_variable_or_write_node_t) {
+ {
+ .type = PM_GLOBAL_VARIABLE_OR_WRITE_NODE,
+ .location = {
+ .start = target->location.start,
+ .end = value->location.end
+ }
+ },
+ .name = pm_global_variable_write_name(parser, target),
+ .name_loc = target->location,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value
+ };
+
+ return node;
+}
+
+/**
+ * Allocate a new GlobalVariableReadNode node.
+ */
+static pm_global_variable_read_node_t *
+pm_global_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
+ pm_global_variable_read_node_t *node = PM_ALLOC_NODE(parser, pm_global_variable_read_node_t);
+
+ *node = (pm_global_variable_read_node_t) {
+ {
+ .type = PM_GLOBAL_VARIABLE_READ_NODE,
+ .location = PM_LOCATION_TOKEN_VALUE(name),
+ },
+ .name = pm_parser_constant_id_token(parser, name)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new synthesized GlobalVariableReadNode node.
+ */
+static pm_global_variable_read_node_t *
+pm_global_variable_read_node_synthesized_create(pm_parser_t *parser, pm_constant_id_t name) {
+ pm_global_variable_read_node_t *node = PM_ALLOC_NODE(parser, pm_global_variable_read_node_t);
+
+ *node = (pm_global_variable_read_node_t) {
+ {
+ .type = PM_GLOBAL_VARIABLE_READ_NODE,
+ .location = { .start = parser->start, .end = parser->start }
+ },
+ .name = name
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new GlobalVariableWriteNode node.
+ */
+static pm_global_variable_write_node_t *
+pm_global_variable_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ pm_global_variable_write_node_t *node = PM_ALLOC_NODE(parser, pm_global_variable_write_node_t);
+
+ *node = (pm_global_variable_write_node_t) {
+ {
+ .type = PM_GLOBAL_VARIABLE_WRITE_NODE,
+ .location = {
+ .start = target->location.start,
+ .end = value->location.end
+ },
+ },
+ .name = pm_global_variable_write_name(parser, target),
+ .name_loc = PM_LOCATION_NODE_VALUE(target),
+ .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator),
+ .value = value
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new synthesized GlobalVariableWriteNode node.
+ */
+static pm_global_variable_write_node_t *
+pm_global_variable_write_node_synthesized_create(pm_parser_t *parser, pm_constant_id_t name, pm_node_t *value) {
+ pm_global_variable_write_node_t *node = PM_ALLOC_NODE(parser, pm_global_variable_write_node_t);
+
+ *node = (pm_global_variable_write_node_t) {
+ {
+ .type = PM_GLOBAL_VARIABLE_WRITE_NODE,
+ .location = { .start = parser->start, .end = parser->start }
+ },
+ .name = name,
+ .name_loc = { .start = parser->start, .end = parser->start },
+ .operator_loc = { .start = parser->start, .end = parser->start },
+ .value = value
+ };
+
+ return node;
+}
+
+/**
+ * Allocate a new HashNode node.
+ */
+static pm_hash_node_t *
+pm_hash_node_create(pm_parser_t *parser, const pm_token_t *opening) {
+ assert(opening != NULL);
+ pm_hash_node_t *node = PM_ALLOC_NODE(parser, pm_hash_node_t);
+
+ *node = (pm_hash_node_t) {
+ {
+ .type = PM_HASH_NODE,
+ .flags = PM_NODE_FLAG_STATIC_LITERAL,
+ .location = PM_LOCATION_TOKEN_VALUE(opening)
+ },
+ .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
+ .closing_loc = PM_LOCATION_NULL_VALUE(parser),
+ .elements = { 0 }
+ };
+
+ return node;
+}
+
+/**
+ * Append a new element to a hash node.
+ */
+static inline void
+pm_hash_node_elements_append(pm_hash_node_t *hash, pm_node_t *element) {
+ pm_node_list_append(&hash->elements, element);
+
+ bool static_literal = PM_NODE_TYPE_P(element, PM_ASSOC_NODE);
+ if (static_literal) {
+ pm_assoc_node_t *assoc = (pm_assoc_node_t *) element;
+ static_literal = !PM_NODE_TYPE_P(assoc->key, PM_ARRAY_NODE) && !PM_NODE_TYPE_P(assoc->key, PM_HASH_NODE) && !PM_NODE_TYPE_P(assoc->key, PM_RANGE_NODE);
+ static_literal = static_literal && PM_NODE_FLAG_P(assoc->key, PM_NODE_FLAG_STATIC_LITERAL);
+ static_literal = static_literal && PM_NODE_FLAG_P(assoc, PM_NODE_FLAG_STATIC_LITERAL);
+ }
+
+ if (!static_literal) {
+ pm_node_flag_unset((pm_node_t *)hash, PM_NODE_FLAG_STATIC_LITERAL);
+ }
+}
+
+static inline void
+pm_hash_node_closing_loc_set(pm_hash_node_t *hash, pm_token_t *token) {
+ hash->base.location.end = token->end;
+ hash->closing_loc = PM_LOCATION_TOKEN_VALUE(token);
+}
+
+/**
+ * Allocate a new IfNode node.
+ */
+static pm_if_node_t *
+pm_if_node_create(pm_parser_t *parser,
+ const pm_token_t *if_keyword,
+ pm_node_t *predicate,
+ const pm_token_t *then_keyword,
+ pm_statements_node_t *statements,
+ pm_node_t *consequent,
+ const pm_token_t *end_keyword
+) {
+ pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
+ pm_if_node_t *node = PM_ALLOC_NODE(parser, pm_if_node_t);
+
+ const uint8_t *end;
+ if (end_keyword->type != PM_TOKEN_NOT_PROVIDED) {
+ end = end_keyword->end;
+ } else if (consequent != NULL) {
+ end = consequent->location.end;
+ } else if (pm_statements_node_body_length(statements) != 0) {
+ end = statements->base.location.end;
+ } else {
+ end = predicate->location.end;
+ }
+
+ *node = (pm_if_node_t) {
+ {
+ .type = PM_IF_NODE,
+ .flags = PM_NODE_FLAG_NEWLINE,
+ .location = {
+ .start = if_keyword->start,
+ .end = end
+ },
+ },
+ .if_keyword_loc = PM_LOCATION_TOKEN_VALUE(if_keyword),
+ .predicate = predicate,
+ .then_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(then_keyword),
+ .statements = statements,
+ .consequent = consequent,
+ .end_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(end_keyword)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize new IfNode node in the modifier form.
+ */
+static pm_if_node_t *
+pm_if_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_token_t *if_keyword, pm_node_t *predicate) {
+ pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
+ pm_if_node_t *node = PM_ALLOC_NODE(parser, pm_if_node_t);
+
+ pm_statements_node_t *statements = pm_statements_node_create(parser);
+ pm_statements_node_body_append(parser, statements, statement);
+
+ *node = (pm_if_node_t) {
+ {
+ .type = PM_IF_NODE,
+ .flags = PM_NODE_FLAG_NEWLINE,
+ .location = {
+ .start = statement->location.start,
+ .end = predicate->location.end
+ },
+ },
+ .if_keyword_loc = PM_LOCATION_TOKEN_VALUE(if_keyword),
+ .predicate = predicate,
+ .then_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
+ .statements = statements,
+ .consequent = NULL,
+ .end_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize an if node from a ternary expression.
+ */
+static pm_if_node_t *
+pm_if_node_ternary_create(pm_parser_t *parser, pm_node_t *predicate, const pm_token_t *qmark, pm_node_t *true_expression, const pm_token_t *colon, pm_node_t *false_expression) {
+ pm_assert_value_expression(parser, predicate);
+ pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
+
+ pm_statements_node_t *if_statements = pm_statements_node_create(parser);
+ pm_statements_node_body_append(parser, if_statements, true_expression);
+
+ pm_statements_node_t *else_statements = pm_statements_node_create(parser);
+ pm_statements_node_body_append(parser, else_statements, false_expression);
+
+ pm_token_t end_keyword = not_provided(parser);
+ pm_else_node_t *else_node = pm_else_node_create(parser, colon, else_statements, &end_keyword);
+
+ pm_if_node_t *node = PM_ALLOC_NODE(parser, pm_if_node_t);
+
+ *node = (pm_if_node_t) {
+ {
+ .type = PM_IF_NODE,
+ .flags = PM_NODE_FLAG_NEWLINE,
+ .location = {
+ .start = predicate->location.start,
+ .end = false_expression->location.end,
+ },
+ },
+ .if_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
+ .predicate = predicate,
+ .then_keyword_loc = PM_LOCATION_TOKEN_VALUE(qmark),
+ .statements = if_statements,
+ .consequent = (pm_node_t *)else_node,
+ .end_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE
+ };
+
+ return node;
+
+}
+
+static inline void
+pm_if_node_end_keyword_loc_set(pm_if_node_t *node, const pm_token_t *keyword) {
+ node->base.location.end = keyword->end;
+ node->end_keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword);
+}
+
+static inline void
+pm_else_node_end_keyword_loc_set(pm_else_node_t *node, const pm_token_t *keyword) {
+ node->base.location.end = keyword->end;
+ node->end_keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword);
+}
+
+/**
+ * Allocate and initialize a new ImplicitNode node.
+ */
+static pm_implicit_node_t *
+pm_implicit_node_create(pm_parser_t *parser, pm_node_t *value) {
+ pm_implicit_node_t *node = PM_ALLOC_NODE(parser, pm_implicit_node_t);
+
+ *node = (pm_implicit_node_t) {
+ {
+ .type = PM_IMPLICIT_NODE,
+ .location = value->location
+ },
+ .value = value
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new ImplicitRestNode node.
+ */
+static pm_implicit_rest_node_t *
+pm_implicit_rest_node_create(pm_parser_t *parser, const pm_token_t *token) {
+ assert(token->type == PM_TOKEN_COMMA);
+
+ pm_implicit_rest_node_t *node = PM_ALLOC_NODE(parser, pm_implicit_rest_node_t);
+
+ *node = (pm_implicit_rest_node_t) {
+ {
+ .type = PM_IMPLICIT_REST_NODE,
+ .location = PM_LOCATION_TOKEN_VALUE(token)
+ }
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new IntegerNode node.
+ */
+static pm_integer_node_t *
+pm_integer_node_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) {
+ assert(token->type == PM_TOKEN_INTEGER);
+ pm_integer_node_t *node = PM_ALLOC_NODE(parser, pm_integer_node_t);
+
+ *node = (pm_integer_node_t) {
+ {
+ .type = PM_INTEGER_NODE,
+ .flags = base | PM_NODE_FLAG_STATIC_LITERAL,
+ .location = PM_LOCATION_TOKEN_VALUE(token)
+ },
+ .value = { 0 }
+ };
+
+ pm_integer_base_t integer_base = PM_INTEGER_BASE_DECIMAL;
+ switch (base) {
+ case PM_INTEGER_BASE_FLAGS_BINARY: integer_base = PM_INTEGER_BASE_BINARY; break;
+ case PM_INTEGER_BASE_FLAGS_OCTAL: integer_base = PM_INTEGER_BASE_OCTAL; break;
+ case PM_INTEGER_BASE_FLAGS_DECIMAL: break;
+ case PM_INTEGER_BASE_FLAGS_HEXADECIMAL: integer_base = PM_INTEGER_BASE_HEXADECIMAL; break;
+ default: assert(false && "unreachable"); break;
+ }
+
+ pm_integer_parse(&node->value, integer_base, token->start, token->end);
+ return node;
+}
+
+/**
+ * Allocate and initialize a new IntegerNode node from an INTEGER_IMAGINARY
+ * token.
+ */
+static pm_imaginary_node_t *
+pm_integer_node_imaginary_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) {
+ assert(token->type == PM_TOKEN_INTEGER_IMAGINARY);
+
+ pm_imaginary_node_t *node = PM_ALLOC_NODE(parser, pm_imaginary_node_t);
+ *node = (pm_imaginary_node_t) {
+ {
+ .type = PM_IMAGINARY_NODE,
+ .flags = PM_NODE_FLAG_STATIC_LITERAL,
+ .location = PM_LOCATION_TOKEN_VALUE(token)
+ },
+ .numeric = (pm_node_t *) pm_integer_node_create(parser, base, &((pm_token_t) {
+ .type = PM_TOKEN_INTEGER,
+ .start = token->start,
+ .end = token->end - 1
+ }))
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new IntegerNode node from an INTEGER_RATIONAL
+ * token.
+ */
+static pm_rational_node_t *
+pm_integer_node_rational_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) {
+ assert(token->type == PM_TOKEN_INTEGER_RATIONAL);
+
+ pm_rational_node_t *node = PM_ALLOC_NODE(parser, pm_rational_node_t);
+ *node = (pm_rational_node_t) {
+ {
+ .type = PM_RATIONAL_NODE,
+ .flags = PM_NODE_FLAG_STATIC_LITERAL,
+ .location = PM_LOCATION_TOKEN_VALUE(token)
+ },
+ .numeric = (pm_node_t *) pm_integer_node_create(parser, base, &((pm_token_t) {
+ .type = PM_TOKEN_INTEGER,
+ .start = token->start,
+ .end = token->end - 1
+ }))
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new IntegerNode node from an
+ * INTEGER_RATIONAL_IMAGINARY token.
+ */
+static pm_imaginary_node_t *
+pm_integer_node_rational_imaginary_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) {
+ assert(token->type == PM_TOKEN_INTEGER_RATIONAL_IMAGINARY);
+
+ pm_imaginary_node_t *node = PM_ALLOC_NODE(parser, pm_imaginary_node_t);
+ *node = (pm_imaginary_node_t) {
+ {
+ .type = PM_IMAGINARY_NODE,
+ .flags = PM_NODE_FLAG_STATIC_LITERAL,
+ .location = PM_LOCATION_TOKEN_VALUE(token)
+ },
+ .numeric = (pm_node_t *) pm_integer_node_rational_create(parser, base, &((pm_token_t) {
+ .type = PM_TOKEN_INTEGER_RATIONAL,
+ .start = token->start,
+ .end = token->end - 1
+ }))
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new InNode node.
+ */
+static pm_in_node_t *
+pm_in_node_create(pm_parser_t *parser, pm_node_t *pattern, pm_statements_node_t *statements, const pm_token_t *in_keyword, const pm_token_t *then_keyword) {
+ pm_in_node_t *node = PM_ALLOC_NODE(parser, pm_in_node_t);
+
+ const uint8_t *end;
+ if (statements != NULL) {
+ end = statements->base.location.end;
+ } else if (then_keyword->type != PM_TOKEN_NOT_PROVIDED) {
+ end = then_keyword->end;
+ } else {
+ end = pattern->location.end;
+ }
+
+ *node = (pm_in_node_t) {
+ {
+ .type = PM_IN_NODE,
+ .location = {
+ .start = in_keyword->start,
+ .end = end
+ },
+ },
+ .pattern = pattern,
+ .statements = statements,
+ .in_loc = PM_LOCATION_TOKEN_VALUE(in_keyword),
+ .then_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(then_keyword)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new InstanceVariableAndWriteNode node.
+ */
+static pm_instance_variable_and_write_node_t *
+pm_instance_variable_and_write_node_create(pm_parser_t *parser, pm_instance_variable_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
+ pm_instance_variable_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_instance_variable_and_write_node_t);
+
+ *node = (pm_instance_variable_and_write_node_t) {
+ {
+ .type = PM_INSTANCE_VARIABLE_AND_WRITE_NODE,
+ .location = {
+ .start = target->base.location.start,
+ .end = value->location.end
+ }
+ },
+ .name = target->name,
+ .name_loc = target->base.location,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new InstanceVariableOperatorWriteNode node.
+ */
+static pm_instance_variable_operator_write_node_t *
+pm_instance_variable_operator_write_node_create(pm_parser_t *parser, pm_instance_variable_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ pm_instance_variable_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_instance_variable_operator_write_node_t);
+
+ *node = (pm_instance_variable_operator_write_node_t) {
+ {
+ .type = PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE,
+ .location = {
+ .start = target->base.location.start,
+ .end = value->location.end
+ }
+ },
+ .name = target->name,
+ .name_loc = target->base.location,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value,
+ .operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new InstanceVariableOrWriteNode node.
+ */
+static pm_instance_variable_or_write_node_t *
+pm_instance_variable_or_write_node_create(pm_parser_t *parser, pm_instance_variable_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
+ pm_instance_variable_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_instance_variable_or_write_node_t);
+
+ *node = (pm_instance_variable_or_write_node_t) {
+ {
+ .type = PM_INSTANCE_VARIABLE_OR_WRITE_NODE,
+ .location = {
+ .start = target->base.location.start,
+ .end = value->location.end
+ }
+ },
+ .name = target->name,
+ .name_loc = target->base.location,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new InstanceVariableReadNode node.
+ */
+static pm_instance_variable_read_node_t *
+pm_instance_variable_read_node_create(pm_parser_t *parser, const pm_token_t *token) {
+ assert(token->type == PM_TOKEN_INSTANCE_VARIABLE);
+ pm_instance_variable_read_node_t *node = PM_ALLOC_NODE(parser, pm_instance_variable_read_node_t);
+
+ *node = (pm_instance_variable_read_node_t) {
+ {
+ .type = PM_INSTANCE_VARIABLE_READ_NODE,
+ .location = PM_LOCATION_TOKEN_VALUE(token)
+ },
+ .name = pm_parser_constant_id_token(parser, token)
+ };
+
+ return node;
+}
+
+/**
+ * Initialize a new InstanceVariableWriteNode node from an InstanceVariableRead
+ * node.
+ */
+static pm_instance_variable_write_node_t *
+pm_instance_variable_write_node_create(pm_parser_t *parser, pm_instance_variable_read_node_t *read_node, pm_token_t *operator, pm_node_t *value) {
+ pm_instance_variable_write_node_t *node = PM_ALLOC_NODE(parser, pm_instance_variable_write_node_t);
+ *node = (pm_instance_variable_write_node_t) {
+ {
+ .type = PM_INSTANCE_VARIABLE_WRITE_NODE,
+ .location = {
+ .start = read_node->base.location.start,
+ .end = value->location.end
+ }
+ },
+ .name = read_node->name,
+ .name_loc = PM_LOCATION_NODE_BASE_VALUE(read_node),
+ .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator),
+ .value = value
+ };
+
+ return node;
+}
+
+/**
+ * Allocate a new InterpolatedRegularExpressionNode node.
+ */
+static pm_interpolated_regular_expression_node_t *
+pm_interpolated_regular_expression_node_create(pm_parser_t *parser, const pm_token_t *opening) {
+ pm_interpolated_regular_expression_node_t *node = PM_ALLOC_NODE(parser, pm_interpolated_regular_expression_node_t);
+
+ *node = (pm_interpolated_regular_expression_node_t) {
+ {
+ .type = PM_INTERPOLATED_REGULAR_EXPRESSION_NODE,
+ .flags = PM_NODE_FLAG_STATIC_LITERAL,
+ .location = {
+ .start = opening->start,
+ .end = NULL,
+ },
+ },
+ .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
+ .closing_loc = PM_LOCATION_TOKEN_VALUE(opening),
+ .parts = { 0 }
+ };
+
+ return node;
+}
+
+static inline void
+pm_interpolated_regular_expression_node_append(pm_interpolated_regular_expression_node_t *node, pm_node_t *part) {
+ if (node->base.location.start > part->location.start) {
+ node->base.location.start = part->location.start;
+ }
+ if (node->base.location.end < part->location.end) {
+ node->base.location.end = part->location.end;
+ }
+
+ if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
+ pm_node_flag_set(part, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN);
+ }
+
+ if (!PM_NODE_FLAG_P(part, PM_NODE_FLAG_STATIC_LITERAL)) {
+ pm_node_flag_unset((pm_node_t *) node, PM_NODE_FLAG_STATIC_LITERAL);
+ }
+
+ pm_node_list_append(&node->parts, part);
+}
+
+static inline void
+pm_interpolated_regular_expression_node_closing_set(pm_parser_t *parser, pm_interpolated_regular_expression_node_t *node, const pm_token_t *closing) {
+ node->closing_loc = PM_LOCATION_TOKEN_VALUE(closing);
+ node->base.location.end = closing->end;
+ pm_node_flag_set((pm_node_t *)node, pm_regular_expression_flags_create(parser, closing));
+}
+
+/**
+ * Append a part to an InterpolatedStringNode node.
+ */
+static inline void
+pm_interpolated_string_node_append(pm_parser_t *parser, pm_interpolated_string_node_t *node, pm_node_t *part) {
+ if (node->parts.size == 0 && node->opening_loc.start == NULL) {
+ node->base.location.start = part->location.start;
+ }
+
+ if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
+ pm_node_flag_set(part, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN);
+ }
+
+ if (!PM_NODE_FLAG_P(part, PM_NODE_FLAG_STATIC_LITERAL)) {
+ pm_node_flag_unset((pm_node_t *) node, PM_NODE_FLAG_STATIC_LITERAL | PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN | PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE);
+ }
+
+ pm_node_list_append(&node->parts, part);
+ node->base.location.end = MAX(node->base.location.end, part->location.end);
+
+ if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
+ switch (parser->frozen_string_literal) {
+ case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
+ pm_node_flag_set((pm_node_t *) node, PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE);
+ break;
+ case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
+ pm_node_flag_set((pm_node_t *) node, PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN);
+ break;
+ }
+ }
+}
+
+/**
+ * Allocate and initialize a new InterpolatedStringNode node.
+ */
+static pm_interpolated_string_node_t *
+pm_interpolated_string_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_node_list_t *parts, const pm_token_t *closing) {
+ pm_interpolated_string_node_t *node = PM_ALLOC_NODE(parser, pm_interpolated_string_node_t);
+
+ *node = (pm_interpolated_string_node_t) {
+ {
+ .type = PM_INTERPOLATED_STRING_NODE,
+ .flags = PM_NODE_FLAG_STATIC_LITERAL,
+ .location = {
+ .start = opening->start,
+ .end = closing->end,
+ },
+ },
+ .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening),
+ .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing),
+ .parts = { 0 }
+ };
+
+ if (parts != NULL) {
+ pm_node_t *part;
+ PM_NODE_LIST_FOREACH(parts, index, part) {
+ pm_interpolated_string_node_append(parser, node, part);
+ }
+ }
+
+ return node;
+}
+
+/**
+ * Set the closing token of the given InterpolatedStringNode node.
+ */
+static void
+pm_interpolated_string_node_closing_set(pm_interpolated_string_node_t *node, const pm_token_t *closing) {
+ node->closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing);
+ node->base.location.end = closing->end;
+}
+
+static void
+pm_interpolated_symbol_node_append(pm_interpolated_symbol_node_t *node, pm_node_t *part) {
+ if (node->parts.size == 0 && node->opening_loc.start == NULL) {
+ node->base.location.start = part->location.start;
+ }
+
+ if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
+ pm_node_flag_set(part, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN);
+ }
+
+ if (!PM_NODE_FLAG_P(part, PM_NODE_FLAG_STATIC_LITERAL)) {
+ pm_node_flag_unset((pm_node_t *) node, PM_NODE_FLAG_STATIC_LITERAL);
+ }
+
+ pm_node_list_append(&node->parts, part);
+ node->base.location.end = MAX(node->base.location.end, part->location.end);
+}
+
+static void
+pm_interpolated_symbol_node_closing_loc_set(pm_interpolated_symbol_node_t *node, const pm_token_t *closing) {
+ node->closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing);
+ node->base.location.end = closing->end;
+}
+
+/**
+ * Allocate and initialize a new InterpolatedSymbolNode node.
+ */
+static pm_interpolated_symbol_node_t *
+pm_interpolated_symbol_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_node_list_t *parts, const pm_token_t *closing) {
+ pm_interpolated_symbol_node_t *node = PM_ALLOC_NODE(parser, pm_interpolated_symbol_node_t);
+
+ *node = (pm_interpolated_symbol_node_t) {
+ {
+ .type = PM_INTERPOLATED_SYMBOL_NODE,
+ .flags = PM_NODE_FLAG_STATIC_LITERAL,
+ .location = {
+ .start = opening->start,
+ .end = closing->end,
+ },
+ },
+ .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening),
+ .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing),
+ .parts = { 0 }
+ };
+
+ if (parts != NULL) {
+ pm_node_t *part;
+ PM_NODE_LIST_FOREACH(parts, index, part) {
+ pm_interpolated_symbol_node_append(node, part);
+ }
+ }
+
+ return node;
+}
+
+/**
+ * Allocate a new InterpolatedXStringNode node.
+ */
+static pm_interpolated_x_string_node_t *
+pm_interpolated_xstring_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) {
+ pm_interpolated_x_string_node_t *node = PM_ALLOC_NODE(parser, pm_interpolated_x_string_node_t);
+
+ *node = (pm_interpolated_x_string_node_t) {
+ {
+ .type = PM_INTERPOLATED_X_STRING_NODE,
+ .location = {
+ .start = opening->start,
+ .end = closing->end
+ },
+ },
+ .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening),
+ .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing),
+ .parts = { 0 }
+ };
+
+ return node;
+}
+
+static inline void
+pm_interpolated_xstring_node_append(pm_interpolated_x_string_node_t *node, pm_node_t *part) {
+ if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
+ pm_node_flag_set(part, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN);
+ }
+
+ pm_node_list_append(&node->parts, part);
+ node->base.location.end = part->location.end;
+}
+
+static inline void
+pm_interpolated_xstring_node_closing_set(pm_interpolated_x_string_node_t *node, const pm_token_t *closing) {
+ node->closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing);
+ node->base.location.end = closing->end;
+}
+
+/**
+ * Allocate and initialize a new ItParametersNode node.
+ */
+static pm_it_parameters_node_t *
+pm_it_parameters_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) {
+ pm_it_parameters_node_t *node = PM_ALLOC_NODE(parser, pm_it_parameters_node_t);
+
+ *node = (pm_it_parameters_node_t) {
+ {
+ .type = PM_IT_PARAMETERS_NODE,
+ .location = {
+ .start = opening->start,
+ .end = closing->end
+ }
+ }
+ };
+
+ return node;
+}
+
+/**
+ * Allocate a new KeywordHashNode node.
+ */
+static pm_keyword_hash_node_t *
+pm_keyword_hash_node_create(pm_parser_t *parser) {
+ pm_keyword_hash_node_t *node = PM_ALLOC_NODE(parser, pm_keyword_hash_node_t);
+
+ *node = (pm_keyword_hash_node_t) {
+ .base = {
+ .type = PM_KEYWORD_HASH_NODE,
+ .location = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
+ .flags = PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS
+ },
+ .elements = { 0 }
+ };
+
+ return node;
+}
+
+/**
+ * Append an element to a KeywordHashNode node.
+ */
+static void
+pm_keyword_hash_node_elements_append(pm_keyword_hash_node_t *hash, pm_node_t *element) {
+ // If the element being added is not an AssocNode or does not have a symbol
+ // key, then we want to turn the SYMBOL_KEYS flag off.
+ if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE) || !PM_NODE_TYPE_P(((pm_assoc_node_t *) element)->key, PM_SYMBOL_NODE)) {
+ pm_node_flag_unset((pm_node_t *)hash, PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS);
+ }
+
+ pm_node_list_append(&hash->elements, element);
+ if (hash->base.location.start == NULL) {
+ hash->base.location.start = element->location.start;
+ }
+ hash->base.location.end = element->location.end;
+}
+
+/**
+ * Allocate and initialize a new RequiredKeywordParameterNode node.
+ */
+static pm_required_keyword_parameter_node_t *
+pm_required_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t *name) {
+ pm_required_keyword_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_required_keyword_parameter_node_t);
+
+ *node = (pm_required_keyword_parameter_node_t) {
+ {
+ .type = PM_REQUIRED_KEYWORD_PARAMETER_NODE,
+ .location = {
+ .start = name->start,
+ .end = name->end
+ },
+ },
+ .name = pm_parser_constant_id_location(parser, name->start, name->end - 1),
+ .name_loc = PM_LOCATION_TOKEN_VALUE(name),
+ };
+
+ return node;
+}
+
+/**
+ * Allocate a new OptionalKeywordParameterNode node.
+ */
+static pm_optional_keyword_parameter_node_t *
+pm_optional_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, pm_node_t *value) {
+ pm_optional_keyword_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_optional_keyword_parameter_node_t);
+
+ *node = (pm_optional_keyword_parameter_node_t) {
+ {
+ .type = PM_OPTIONAL_KEYWORD_PARAMETER_NODE,
+ .location = {
+ .start = name->start,
+ .end = value->location.end
+ },
+ },
+ .name = pm_parser_constant_id_location(parser, name->start, name->end - 1),
+ .name_loc = PM_LOCATION_TOKEN_VALUE(name),
+ .value = value
+ };
+
+ return node;
+}
+
+/**
+ * Allocate a new KeywordRestParameterNode node.
+ */
+static pm_keyword_rest_parameter_node_t *
+pm_keyword_rest_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *name) {
+ pm_keyword_rest_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_keyword_rest_parameter_node_t);
+
+ *node = (pm_keyword_rest_parameter_node_t) {
+ {
+ .type = PM_KEYWORD_REST_PARAMETER_NODE,
+ .location = {
+ .start = operator->start,
+ .end = (name->type == PM_TOKEN_NOT_PROVIDED ? operator->end : name->end)
+ },
+ },
+ .name = pm_parser_optional_constant_id_token(parser, name),
+ .name_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(name),
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate a new LambdaNode node.
+ */
+static pm_lambda_node_t *
+pm_lambda_node_create(
+ pm_parser_t *parser,
+ pm_constant_id_list_t *locals,
+ const pm_token_t *operator,
+ const pm_token_t *opening,
+ const pm_token_t *closing,
+ pm_node_t *parameters,
+ pm_node_t *body
+) {
+ pm_lambda_node_t *node = PM_ALLOC_NODE(parser, pm_lambda_node_t);
+
+ *node = (pm_lambda_node_t) {
+ {
+ .type = PM_LAMBDA_NODE,
+ .location = {
+ .start = operator->start,
+ .end = closing->end
+ },
+ },
+ .locals = *locals,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
+ .closing_loc = PM_LOCATION_TOKEN_VALUE(closing),
+ .parameters = parameters,
+ .body = body
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new LocalVariableAndWriteNode node.
+ */
+static pm_local_variable_and_write_node_t *
+pm_local_variable_and_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value, pm_constant_id_t name, uint32_t depth) {
+ assert(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_READ_NODE) || PM_NODE_TYPE_P(target, PM_CALL_NODE));
+ assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
+ pm_local_variable_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_local_variable_and_write_node_t);
+
+ *node = (pm_local_variable_and_write_node_t) {
+ {
+ .type = PM_LOCAL_VARIABLE_AND_WRITE_NODE,
+ .location = {
+ .start = target->location.start,
+ .end = value->location.end
+ }
+ },
+ .name_loc = target->location,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value,
+ .name = name,
+ .depth = depth
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new LocalVariableOperatorWriteNode node.
+ */
+static pm_local_variable_operator_write_node_t *
+pm_local_variable_operator_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value, pm_constant_id_t name, uint32_t depth) {
+ pm_local_variable_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_local_variable_operator_write_node_t);
+
+ *node = (pm_local_variable_operator_write_node_t) {
+ {
+ .type = PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE,
+ .location = {
+ .start = target->location.start,
+ .end = value->location.end
+ }
+ },
+ .name_loc = target->location,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value,
+ .name = name,
+ .operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1),
+ .depth = depth
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new LocalVariableOrWriteNode node.
+ */
+static pm_local_variable_or_write_node_t *
+pm_local_variable_or_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value, pm_constant_id_t name, uint32_t depth) {
+ assert(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_READ_NODE) || PM_NODE_TYPE_P(target, PM_CALL_NODE));
+ assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
+ pm_local_variable_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_local_variable_or_write_node_t);
+
+ *node = (pm_local_variable_or_write_node_t) {
+ {
+ .type = PM_LOCAL_VARIABLE_OR_WRITE_NODE,
+ .location = {
+ .start = target->location.start,
+ .end = value->location.end
+ }
+ },
+ .name_loc = target->location,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value,
+ .name = name,
+ .depth = depth
+ };
+
+ return node;
+}
+
+/**
+ * Allocate a new LocalVariableReadNode node with constant_id.
+ */
+static pm_local_variable_read_node_t *
+pm_local_variable_read_node_create_constant_id(pm_parser_t *parser, const pm_token_t *name, pm_constant_id_t name_id, uint32_t depth, bool missing) {
+ if (!missing) pm_locals_read(&pm_parser_scope_find(parser, depth)->locals, name_id);
+
+ pm_local_variable_read_node_t *node = PM_ALLOC_NODE(parser, pm_local_variable_read_node_t);
+
+ *node = (pm_local_variable_read_node_t) {
+ {
+ .type = PM_LOCAL_VARIABLE_READ_NODE,
+ .location = PM_LOCATION_TOKEN_VALUE(name)
+ },
+ .name = name_id,
+ .depth = depth
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new LocalVariableReadNode node.
+ */
+static pm_local_variable_read_node_t *
+pm_local_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name, uint32_t depth) {
+ pm_constant_id_t name_id = pm_parser_constant_id_token(parser, name);
+ return pm_local_variable_read_node_create_constant_id(parser, name, name_id, depth, false);
+}
+
+/**
+ * Allocate and initialize a new LocalVariableReadNode node for a missing local
+ * variable. (This will only happen when there is a syntax error.)
+ */
+static pm_local_variable_read_node_t *
+pm_local_variable_read_node_missing_create(pm_parser_t *parser, const pm_token_t *name, uint32_t depth) {
+ pm_constant_id_t name_id = pm_parser_constant_id_token(parser, name);
+ return pm_local_variable_read_node_create_constant_id(parser, name, name_id, depth, true);
+}
+
+/**
+ * Allocate and initialize a new LocalVariableWriteNode node.
+ */
+static pm_local_variable_write_node_t *
+pm_local_variable_write_node_create(pm_parser_t *parser, pm_constant_id_t name, uint32_t depth, pm_node_t *value, const pm_location_t *name_loc, const pm_token_t *operator) {
+ pm_local_variable_write_node_t *node = PM_ALLOC_NODE(parser, pm_local_variable_write_node_t);
+
+ *node = (pm_local_variable_write_node_t) {
+ {
+ .type = PM_LOCAL_VARIABLE_WRITE_NODE,
+ .location = {
+ .start = name_loc->start,
+ .end = value->location.end
+ }
+ },
+ .name = name,
+ .depth = depth,
+ .value = value,
+ .name_loc = *name_loc,
+ .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator)
+ };
+
+ return node;
+}
+
+/**
+ * Returns true if the given bounds comprise `it`.
+ */
+static inline bool
+pm_token_is_it(const uint8_t *start, const uint8_t *end) {
+ return (end - start == 2) && (start[0] == 'i') && (start[1] == 't');
+}
+
+/**
+ * Returns true if the given node is `it` default parameter.
+ */
+static inline bool
+pm_node_is_it(pm_parser_t *parser, pm_node_t *node) {
+ // Check if it's a local variable reference
+ if (node->type != PM_CALL_NODE) {
+ return false;
+ }
+
+ // Check if it's a variable call
+ pm_call_node_t *call_node = (pm_call_node_t *) node;
+ if (!PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
+ return false;
+ }
+
+ // Check if it's called `it`
+ pm_constant_id_t id = ((pm_call_node_t *)node)->name;
+ pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, id);
+ return pm_token_is_it(constant->start, constant->start + constant->length);
+}
+
+/**
+ * Returns true if the given bounds comprise a numbered parameter (i.e., they
+ * are of the form /^_\d$/).
+ */
+static inline bool
+pm_token_is_numbered_parameter(const uint8_t *start, const uint8_t *end) {
+ return (end - start == 2) && (start[0] == '_') && (start[1] != '0') && (pm_char_is_decimal_digit(start[1]));
+}
+
+/**
+ * Ensure the given bounds do not comprise a numbered parameter. If they do, add
+ * an appropriate error message to the parser.
+ */
+static inline void
+pm_refute_numbered_parameter(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
+ if (pm_token_is_numbered_parameter(start, end)) {
+ PM_PARSER_ERR_FORMAT(parser, start, end, PM_ERR_PARAMETER_NUMBERED_RESERVED, start);
+ }
+}
+
+/**
+ * Allocate and initialize a new LocalVariableTargetNode node with the given
+ * name and depth.
+ */
+static pm_local_variable_target_node_t *
+pm_local_variable_target_node_create(pm_parser_t *parser, const pm_location_t *location, pm_constant_id_t name, uint32_t depth) {
+ pm_refute_numbered_parameter(parser, location->start, location->end);
+ pm_local_variable_target_node_t *node = PM_ALLOC_NODE(parser, pm_local_variable_target_node_t);
+
+ *node = (pm_local_variable_target_node_t) {
+ {
+ .type = PM_LOCAL_VARIABLE_TARGET_NODE,
+ .location = *location
+ },
+ .name = name,
+ .depth = depth
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new MatchPredicateNode node.
+ */
+static pm_match_predicate_node_t *
+pm_match_predicate_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t *pattern, const pm_token_t *operator) {
+ pm_assert_value_expression(parser, value);
+
+ pm_match_predicate_node_t *node = PM_ALLOC_NODE(parser, pm_match_predicate_node_t);
+
+ *node = (pm_match_predicate_node_t) {
+ {
+ .type = PM_MATCH_PREDICATE_NODE,
+ .location = {
+ .start = value->location.start,
+ .end = pattern->location.end
+ }
+ },
+ .value = value,
+ .pattern = pattern,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new MatchRequiredNode node.
+ */
+static pm_match_required_node_t *
+pm_match_required_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t *pattern, const pm_token_t *operator) {
+ pm_assert_value_expression(parser, value);
+
+ pm_match_required_node_t *node = PM_ALLOC_NODE(parser, pm_match_required_node_t);
+
+ *node = (pm_match_required_node_t) {
+ {
+ .type = PM_MATCH_REQUIRED_NODE,
+ .location = {
+ .start = value->location.start,
+ .end = pattern->location.end
+ }
+ },
+ .value = value,
+ .pattern = pattern,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new MatchWriteNode node.
+ */
+static pm_match_write_node_t *
+pm_match_write_node_create(pm_parser_t *parser, pm_call_node_t *call) {
+ pm_match_write_node_t *node = PM_ALLOC_NODE(parser, pm_match_write_node_t);
+
+ *node = (pm_match_write_node_t) {
+ {
+ .type = PM_MATCH_WRITE_NODE,
+ .location = call->base.location
+ },
+ .call = call,
+ .targets = { 0 }
+ };
+
+ return node;
+}
+
+/**
+ * Allocate a new ModuleNode node.
+ */
+static pm_module_node_t *
+pm_module_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const pm_token_t *module_keyword, pm_node_t *constant_path, const pm_token_t *name, pm_node_t *body, const pm_token_t *end_keyword) {
+ pm_module_node_t *node = PM_ALLOC_NODE(parser, pm_module_node_t);
+
+ *node = (pm_module_node_t) {
+ {
+ .type = PM_MODULE_NODE,
+ .location = {
+ .start = module_keyword->start,
+ .end = end_keyword->end
+ }
+ },
+ .locals = (locals == NULL ? ((pm_constant_id_list_t) { .ids = NULL, .size = 0, .capacity = 0 }) : *locals),
+ .module_keyword_loc = PM_LOCATION_TOKEN_VALUE(module_keyword),
+ .constant_path = constant_path,
+ .body = body,
+ .end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword),
+ .name = pm_parser_constant_id_token(parser, name)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize new MultiTargetNode node.
+ */
+static pm_multi_target_node_t *
+pm_multi_target_node_create(pm_parser_t *parser) {
+ pm_multi_target_node_t *node = PM_ALLOC_NODE(parser, pm_multi_target_node_t);
+
+ *node = (pm_multi_target_node_t) {
+ {
+ .type = PM_MULTI_TARGET_NODE,
+ .location = { .start = NULL, .end = NULL }
+ },
+ .lefts = { 0 },
+ .rest = NULL,
+ .rights = { 0 },
+ .lparen_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
+ .rparen_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE
+ };
+
+ return node;
+}
+
+/**
+ * Append a target to a MultiTargetNode node.
+ */
+static void
+pm_multi_target_node_targets_append(pm_parser_t *parser, pm_multi_target_node_t *node, pm_node_t *target) {
+ if (PM_NODE_TYPE_P(target, PM_SPLAT_NODE)) {
+ if (node->rest == NULL) {
+ node->rest = target;
+ } else {
+ pm_parser_err_node(parser, target, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS);
+ pm_node_list_append(&node->rights, target);
+ }
+ } else if (PM_NODE_TYPE_P(target, PM_IMPLICIT_REST_NODE)) {
+ if (node->rest == NULL) {
+ node->rest = target;
+ } else {
+ PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->current, PM_ERR_MULTI_ASSIGN_UNEXPECTED_REST);
+ pm_node_list_append(&node->rights, target);
+ }
+ } else if (node->rest == NULL) {
+ pm_node_list_append(&node->lefts, target);
+ } else {
+ pm_node_list_append(&node->rights, target);
+ }
+
+ if (node->base.location.start == NULL || (node->base.location.start > target->location.start)) {
+ node->base.location.start = target->location.start;
+ }
+
+ if (node->base.location.end == NULL || (node->base.location.end < target->location.end)) {
+ node->base.location.end = target->location.end;
+ }
+}
+
+/**
+ * Set the opening of a MultiTargetNode node.
+ */
+static void
+pm_multi_target_node_opening_set(pm_multi_target_node_t *node, const pm_token_t *lparen) {
+ node->base.location.start = lparen->start;
+ node->lparen_loc = PM_LOCATION_TOKEN_VALUE(lparen);
+}
+
+/**
+ * Set the closing of a MultiTargetNode node.
+ */
+static void
+pm_multi_target_node_closing_set(pm_multi_target_node_t *node, const pm_token_t *rparen) {
+ node->base.location.end = rparen->end;
+ node->rparen_loc = PM_LOCATION_TOKEN_VALUE(rparen);
+}
+
+/**
+ * Allocate a new MultiWriteNode node.
+ */
+static pm_multi_write_node_t *
+pm_multi_write_node_create(pm_parser_t *parser, pm_multi_target_node_t *target, const pm_token_t *operator, pm_node_t *value) {
+ pm_multi_write_node_t *node = PM_ALLOC_NODE(parser, pm_multi_write_node_t);
+
+ *node = (pm_multi_write_node_t) {
+ {
+ .type = PM_MULTI_WRITE_NODE,
+ .location = {
+ .start = target->base.location.start,
+ .end = value->location.end
+ }
+ },
+ .lefts = target->lefts,
+ .rest = target->rest,
+ .rights = target->rights,
+ .lparen_loc = target->lparen_loc,
+ .rparen_loc = target->rparen_loc,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value
+ };
+
+ // Explicitly do not call pm_node_destroy here because we want to keep
+ // around all of the information within the MultiWriteNode node.
+ xfree(target);
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new NextNode node.
+ */
+static pm_next_node_t *
+pm_next_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_node_t *arguments) {
+ assert(keyword->type == PM_TOKEN_KEYWORD_NEXT);
+ pm_next_node_t *node = PM_ALLOC_NODE(parser, pm_next_node_t);
+
+ *node = (pm_next_node_t) {
+ {
+ .type = PM_NEXT_NODE,
+ .location = {
+ .start = keyword->start,
+ .end = (arguments == NULL ? keyword->end : arguments->base.location.end)
+ }
+ },
+ .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
+ .arguments = arguments
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new NilNode node.
+ */
+static pm_nil_node_t *
+pm_nil_node_create(pm_parser_t *parser, const pm_token_t *token) {
+ assert(token->type == PM_TOKEN_KEYWORD_NIL);
+ pm_nil_node_t *node = PM_ALLOC_NODE(parser, pm_nil_node_t);
+
+ *node = (pm_nil_node_t) {{
+ .type = PM_NIL_NODE,
+ .flags = PM_NODE_FLAG_STATIC_LITERAL,
+ .location = PM_LOCATION_TOKEN_VALUE(token)
+ }};
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new NoKeywordsParameterNode node.
+ */
+static pm_no_keywords_parameter_node_t *
+pm_no_keywords_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *keyword) {
+ assert(operator->type == PM_TOKEN_USTAR_STAR || operator->type == PM_TOKEN_STAR_STAR);
+ assert(keyword->type == PM_TOKEN_KEYWORD_NIL);
+ pm_no_keywords_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_no_keywords_parameter_node_t);
+
+ *node = (pm_no_keywords_parameter_node_t) {
+ {
+ .type = PM_NO_KEYWORDS_PARAMETER_NODE,
+ .location = {
+ .start = operator->start,
+ .end = keyword->end
+ }
+ },
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new NumberedParametersNode node.
+ */
+static pm_numbered_parameters_node_t *
+pm_numbered_parameters_node_create(pm_parser_t *parser, const pm_location_t *location, uint8_t maximum) {
+ pm_numbered_parameters_node_t *node = PM_ALLOC_NODE(parser, pm_numbered_parameters_node_t);
+
+ *node = (pm_numbered_parameters_node_t) {
+ {
+ .type = PM_NUMBERED_PARAMETERS_NODE,
+ .location = *location
+ },
+ .maximum = maximum
+ };
+
+ return node;
+}
+
+/**
+ * The maximum numbered reference value is defined as the maximum value that an
+ * integer can hold minus 1 bit for CRuby instruction sequence operand tagging.
+ */
+#define NTH_REF_MAX ((uint32_t) (INT_MAX >> 1))
+
+/**
+ * Parse the decimal number represented by the range of bytes. Returns
+ * 0 if the number fails to parse or if the number is greater than the maximum
+ * value representable by a numbered reference. This function assumes that the
+ * range of bytes has already been validated to contain only decimal digits.
+ */
+static uint32_t
+pm_numbered_reference_read_node_number(pm_parser_t *parser, const pm_token_t *token) {
+ const uint8_t *start = token->start + 1;
+ const uint8_t *end = token->end;
+
+ ptrdiff_t diff = end - start;
+ assert(diff > 0 && ((unsigned long) diff < SIZE_MAX));
+ size_t length = (size_t) diff;
+
+ char *digits = xcalloc(length + 1, sizeof(char));
+ memcpy(digits, start, length);
+ digits[length] = '\0';
+
+ char *endptr;
+ errno = 0;
+ unsigned long value = strtoul(digits, &endptr, 10);
+
+ if ((digits == endptr) || (*endptr != '\0') || (errno == ERANGE)) {
+ pm_parser_err(parser, start, end, PM_ERR_INVALID_NUMBER_DECIMAL);
+ value = 0;
+ }
+
+ xfree(digits);
+
+ if (value > NTH_REF_MAX) {
+ PM_PARSER_WARN_FORMAT(parser, start, end, PM_WARN_INVALID_NUMBERED_REFERENCE, (int) (length + 1), (const char *) token->start);
+ value = 0;
+ }
+
+ return (uint32_t) value;
+}
+
+#undef NTH_REF_MAX
+
+/**
+ * Allocate and initialize a new NthReferenceReadNode node.
+ */
+static pm_numbered_reference_read_node_t *
+pm_numbered_reference_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
+ assert(name->type == PM_TOKEN_NUMBERED_REFERENCE);
+ pm_numbered_reference_read_node_t *node = PM_ALLOC_NODE(parser, pm_numbered_reference_read_node_t);
+
+ *node = (pm_numbered_reference_read_node_t) {
+ {
+ .type = PM_NUMBERED_REFERENCE_READ_NODE,
+ .location = PM_LOCATION_TOKEN_VALUE(name),
+ },
+ .number = pm_numbered_reference_read_node_number(parser, name)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate a new OptionalParameterNode node.
+ */
+static pm_optional_parameter_node_t *
+pm_optional_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, const pm_token_t *operator, pm_node_t *value) {
+ pm_optional_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_optional_parameter_node_t);
+
+ *node = (pm_optional_parameter_node_t) {
+ {
+ .type = PM_OPTIONAL_PARAMETER_NODE,
+ .location = {
+ .start = name->start,
+ .end = value->location.end
+ }
+ },
+ .name = pm_parser_constant_id_token(parser, name),
+ .name_loc = PM_LOCATION_TOKEN_VALUE(name),
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .value = value
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new OrNode node.
+ */
+static pm_or_node_t *
+pm_or_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operator, pm_node_t *right) {
+ pm_assert_value_expression(parser, left);
+
+ pm_or_node_t *node = PM_ALLOC_NODE(parser, pm_or_node_t);
+
+ *node = (pm_or_node_t) {
+ {
+ .type = PM_OR_NODE,
+ .location = {
+ .start = left->location.start,
+ .end = right->location.end
+ }
+ },
+ .left = left,
+ .right = right,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new ParametersNode node.
+ */
+static pm_parameters_node_t *
+pm_parameters_node_create(pm_parser_t *parser) {
+ pm_parameters_node_t *node = PM_ALLOC_NODE(parser, pm_parameters_node_t);
+
+ *node = (pm_parameters_node_t) {
+ {
+ .type = PM_PARAMETERS_NODE,
+ .location = PM_LOCATION_TOKEN_VALUE(&parser->current)
+ },
+ .rest = NULL,
+ .keyword_rest = NULL,
+ .block = NULL,
+ .requireds = { 0 },
+ .optionals = { 0 },
+ .posts = { 0 },
+ .keywords = { 0 }
+ };
+
+ return node;
+}
+
+/**
+ * Set the location properly for the parameters node.
+ */
+static void
+pm_parameters_node_location_set(pm_parameters_node_t *params, pm_node_t *param) {
+ if (params->base.location.start == NULL) {
+ params->base.location.start = param->location.start;
+ } else {
+ params->base.location.start = params->base.location.start < param->location.start ? params->base.location.start : param->location.start;
+ }
+
+ if (params->base.location.end == NULL) {
+ params->base.location.end = param->location.end;
+ } else {
+ params->base.location.end = params->base.location.end > param->location.end ? params->base.location.end : param->location.end;
+ }
+}
+
+/**
+ * Append a required parameter to a ParametersNode node.
+ */
+static void
+pm_parameters_node_requireds_append(pm_parameters_node_t *params, pm_node_t *param) {
+ pm_parameters_node_location_set(params, param);
+ pm_node_list_append(&params->requireds, param);
+}
+
+/**
+ * Append an optional parameter to a ParametersNode node.
+ */
+static void
+pm_parameters_node_optionals_append(pm_parameters_node_t *params, pm_optional_parameter_node_t *param) {
+ pm_parameters_node_location_set(params, (pm_node_t *) param);
+ pm_node_list_append(&params->optionals, (pm_node_t *) param);
+}
+
+/**
+ * Append a post optional arguments parameter to a ParametersNode node.
+ */
+static void
+pm_parameters_node_posts_append(pm_parameters_node_t *params, pm_node_t *param) {
+ pm_parameters_node_location_set(params, param);
+ pm_node_list_append(&params->posts, param);
+}
+
+/**
+ * Set the rest parameter on a ParametersNode node.
+ */
+static void
+pm_parameters_node_rest_set(pm_parameters_node_t *params, pm_node_t *param) {
+ pm_parameters_node_location_set(params, param);
+ params->rest = param;
+}
+
+/**
+ * Append a keyword parameter to a ParametersNode node.
+ */
+static void
+pm_parameters_node_keywords_append(pm_parameters_node_t *params, pm_node_t *param) {
+ pm_parameters_node_location_set(params, param);
+ pm_node_list_append(&params->keywords, param);
+}
+
+/**
+ * Set the keyword rest parameter on a ParametersNode node.
+ */
+static void
+pm_parameters_node_keyword_rest_set(pm_parameters_node_t *params, pm_node_t *param) {
+ assert(params->keyword_rest == NULL);
+ pm_parameters_node_location_set(params, param);
+ params->keyword_rest = param;
+}
+
+/**
+ * Set the block parameter on a ParametersNode node.
+ */
+static void
+pm_parameters_node_block_set(pm_parameters_node_t *params, pm_block_parameter_node_t *param) {
+ assert(params->block == NULL);
+ pm_parameters_node_location_set(params, (pm_node_t *) param);
+ params->block = param;
+}
+
+/**
+ * Allocate a new ProgramNode node.
+ */
+static pm_program_node_t *
+pm_program_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, pm_statements_node_t *statements) {
+ pm_program_node_t *node = PM_ALLOC_NODE(parser, pm_program_node_t);
+
+ *node = (pm_program_node_t) {
+ {
+ .type = PM_PROGRAM_NODE,
+ .location = {
+ .start = statements == NULL ? parser->start : statements->base.location.start,
+ .end = statements == NULL ? parser->end : statements->base.location.end
+ }
+ },
+ .locals = *locals,
+ .statements = statements
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize new ParenthesesNode node.
+ */
+static pm_parentheses_node_t *
+pm_parentheses_node_create(pm_parser_t *parser, const pm_token_t *opening, pm_node_t *body, const pm_token_t *closing) {
+ pm_parentheses_node_t *node = PM_ALLOC_NODE(parser, pm_parentheses_node_t);
+
+ *node = (pm_parentheses_node_t) {
+ {
+ .type = PM_PARENTHESES_NODE,
+ .location = {
+ .start = opening->start,
+ .end = closing->end
+ }
+ },
+ .body = body,
+ .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
+ .closing_loc = PM_LOCATION_TOKEN_VALUE(closing)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new PinnedExpressionNode node.
+ */
+static pm_pinned_expression_node_t *
+pm_pinned_expression_node_create(pm_parser_t *parser, pm_node_t *expression, const pm_token_t *operator, const pm_token_t *lparen, const pm_token_t *rparen) {
+ pm_pinned_expression_node_t *node = PM_ALLOC_NODE(parser, pm_pinned_expression_node_t);
+
+ *node = (pm_pinned_expression_node_t) {
+ {
+ .type = PM_PINNED_EXPRESSION_NODE,
+ .location = {
+ .start = operator->start,
+ .end = rparen->end
+ }
+ },
+ .expression = expression,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .lparen_loc = PM_LOCATION_TOKEN_VALUE(lparen),
+ .rparen_loc = PM_LOCATION_TOKEN_VALUE(rparen)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new PinnedVariableNode node.
+ */
+static pm_pinned_variable_node_t *
+pm_pinned_variable_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *variable) {
+ pm_pinned_variable_node_t *node = PM_ALLOC_NODE(parser, pm_pinned_variable_node_t);
+
+ *node = (pm_pinned_variable_node_t) {
+ {
+ .type = PM_PINNED_VARIABLE_NODE,
+ .location = {
+ .start = operator->start,
+ .end = variable->location.end
+ }
+ },
+ .variable = variable,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new PostExecutionNode node.
+ */
+static pm_post_execution_node_t *
+pm_post_execution_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_token_t *opening, pm_statements_node_t *statements, const pm_token_t *closing) {
+ pm_post_execution_node_t *node = PM_ALLOC_NODE(parser, pm_post_execution_node_t);
+
+ *node = (pm_post_execution_node_t) {
+ {
+ .type = PM_POST_EXECUTION_NODE,
+ .location = {
+ .start = keyword->start,
+ .end = closing->end
+ }
+ },
+ .statements = statements,
+ .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
+ .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
+ .closing_loc = PM_LOCATION_TOKEN_VALUE(closing)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new PreExecutionNode node.
+ */
+static pm_pre_execution_node_t *
+pm_pre_execution_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_token_t *opening, pm_statements_node_t *statements, const pm_token_t *closing) {
+ pm_pre_execution_node_t *node = PM_ALLOC_NODE(parser, pm_pre_execution_node_t);
+
+ *node = (pm_pre_execution_node_t) {
+ {
+ .type = PM_PRE_EXECUTION_NODE,
+ .location = {
+ .start = keyword->start,
+ .end = closing->end
+ }
+ },
+ .statements = statements,
+ .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
+ .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
+ .closing_loc = PM_LOCATION_TOKEN_VALUE(closing)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize new RangeNode node.
+ */
+static pm_range_node_t *
+pm_range_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operator, pm_node_t *right) {
+ pm_assert_value_expression(parser, left);
+ pm_assert_value_expression(parser, right);
+
+ pm_range_node_t *node = PM_ALLOC_NODE(parser, pm_range_node_t);
+ pm_node_flags_t flags = 0;
+
+ // Indicate that this node is an exclusive range if the operator is `...`.
+ if (operator->type == PM_TOKEN_DOT_DOT_DOT || operator->type == PM_TOKEN_UDOT_DOT_DOT) {
+ flags |= PM_RANGE_FLAGS_EXCLUDE_END;
+ }
+
+ // Indicate that this node is a static literal (i.e., can be compiled with
+ // a putobject in CRuby) if the left and right are implicit nil, explicit
+ // nil, or integers.
+ if (
+ (left == NULL || PM_NODE_TYPE_P(left, PM_NIL_NODE) || PM_NODE_TYPE_P(left, PM_INTEGER_NODE)) &&
+ (right == NULL || PM_NODE_TYPE_P(right, PM_NIL_NODE) || PM_NODE_TYPE_P(right, PM_INTEGER_NODE))
+ ) {
+ flags |= PM_NODE_FLAG_STATIC_LITERAL;
+ }
+
+ *node = (pm_range_node_t) {
+ {
+ .type = PM_RANGE_NODE,
+ .flags = flags,
+ .location = {
+ .start = (left == NULL ? operator->start : left->location.start),
+ .end = (right == NULL ? operator->end : right->location.end)
+ }
+ },
+ .left = left,
+ .right = right,
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new RedoNode node.
+ */
+static pm_redo_node_t *
+pm_redo_node_create(pm_parser_t *parser, const pm_token_t *token) {
+ assert(token->type == PM_TOKEN_KEYWORD_REDO);
+ pm_redo_node_t *node = PM_ALLOC_NODE(parser, pm_redo_node_t);
+
+ *node = (pm_redo_node_t) {{ .type = PM_REDO_NODE, .location = PM_LOCATION_TOKEN_VALUE(token) }};
+ return node;
+}
+
+/**
+ * Allocate a new initialize a new RegularExpressionNode node with the given
+ * unescaped string.
+ */
+static pm_regular_expression_node_t *
+pm_regular_expression_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing, const pm_string_t *unescaped) {
+ pm_regular_expression_node_t *node = PM_ALLOC_NODE(parser, pm_regular_expression_node_t);
+
+ *node = (pm_regular_expression_node_t) {
+ {
+ .type = PM_REGULAR_EXPRESSION_NODE,
+ .flags = pm_regular_expression_flags_create(parser, closing) | PM_NODE_FLAG_STATIC_LITERAL,
+ .location = {
+ .start = MIN(opening->start, closing->start),
+ .end = MAX(opening->end, closing->end)
+ }
+ },
+ .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
+ .content_loc = PM_LOCATION_TOKEN_VALUE(content),
+ .closing_loc = PM_LOCATION_TOKEN_VALUE(closing),
+ .unescaped = *unescaped
+ };
+
+ return node;
+}
+
+/**
+ * Allocate a new initialize a new RegularExpressionNode node.
+ */
+static inline pm_regular_expression_node_t *
+pm_regular_expression_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
+ return pm_regular_expression_node_create_unescaped(parser, opening, content, closing, &PM_STRING_EMPTY);
+}
+
+/**
+ * Allocate a new RequiredParameterNode node.
+ */
+static pm_required_parameter_node_t *
+pm_required_parameter_node_create(pm_parser_t *parser, const pm_token_t *token) {
+ pm_required_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_required_parameter_node_t);
+
+ *node = (pm_required_parameter_node_t) {
+ {
+ .type = PM_REQUIRED_PARAMETER_NODE,
+ .location = PM_LOCATION_TOKEN_VALUE(token)
+ },
+ .name = pm_parser_constant_id_token(parser, token)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate a new RescueModifierNode node.
+ */
+static pm_rescue_modifier_node_t *
+pm_rescue_modifier_node_create(pm_parser_t *parser, pm_node_t *expression, const pm_token_t *keyword, pm_node_t *rescue_expression) {
+ pm_rescue_modifier_node_t *node = PM_ALLOC_NODE(parser, pm_rescue_modifier_node_t);
+
+ *node = (pm_rescue_modifier_node_t) {
+ {
+ .type = PM_RESCUE_MODIFIER_NODE,
+ .location = {
+ .start = expression->location.start,
+ .end = rescue_expression->location.end
+ }
+ },
+ .expression = expression,
+ .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
+ .rescue_expression = rescue_expression
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new RescueNode node.
+ */
+static pm_rescue_node_t *
+pm_rescue_node_create(pm_parser_t *parser, const pm_token_t *keyword) {
+ pm_rescue_node_t *node = PM_ALLOC_NODE(parser, pm_rescue_node_t);
+
+ *node = (pm_rescue_node_t) {
+ {
+ .type = PM_RESCUE_NODE,
+ .location = PM_LOCATION_TOKEN_VALUE(keyword)
+ },
+ .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
+ .operator_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
+ .reference = NULL,
+ .statements = NULL,
+ .consequent = NULL,
+ .exceptions = { 0 }
+ };
+
+ return node;
+}
+
+static inline void
+pm_rescue_node_operator_set(pm_rescue_node_t *node, const pm_token_t *operator) {
+ node->operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator);
+}
+
+/**
+ * Set the reference of a rescue node, and update the location of the node.
+ */
+static void
+pm_rescue_node_reference_set(pm_rescue_node_t *node, pm_node_t *reference) {
+ node->reference = reference;
+ node->base.location.end = reference->location.end;
+}
+
+/**
+ * Set the statements of a rescue node, and update the location of the node.
+ */
+static void
+pm_rescue_node_statements_set(pm_rescue_node_t *node, pm_statements_node_t *statements) {
+ node->statements = statements;
+ if (pm_statements_node_body_length(statements) > 0) {
+ node->base.location.end = statements->base.location.end;
+ }
+}
+
+/**
+ * Set the consequent of a rescue node, and update the location.
+ */
+static void
+pm_rescue_node_consequent_set(pm_rescue_node_t *node, pm_rescue_node_t *consequent) {
+ node->consequent = consequent;
+ node->base.location.end = consequent->base.location.end;
+}
+
+/**
+ * Append an exception node to a rescue node, and update the location.
+ */
+static void
+pm_rescue_node_exceptions_append(pm_rescue_node_t *node, pm_node_t *exception) {
+ pm_node_list_append(&node->exceptions, exception);
+ node->base.location.end = exception->location.end;
+}
+
+/**
+ * Allocate a new RestParameterNode node.
+ */
+static pm_rest_parameter_node_t *
+pm_rest_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *name) {
+ pm_rest_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_rest_parameter_node_t);
+
+ *node = (pm_rest_parameter_node_t) {
+ {
+ .type = PM_REST_PARAMETER_NODE,
+ .location = {
+ .start = operator->start,
+ .end = (name->type == PM_TOKEN_NOT_PROVIDED ? operator->end : name->end)
+ }
+ },
+ .name = pm_parser_optional_constant_id_token(parser, name),
+ .name_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(name),
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new RetryNode node.
+ */
+static pm_retry_node_t *
+pm_retry_node_create(pm_parser_t *parser, const pm_token_t *token) {
+ assert(token->type == PM_TOKEN_KEYWORD_RETRY);
+ pm_retry_node_t *node = PM_ALLOC_NODE(parser, pm_retry_node_t);
+
+ *node = (pm_retry_node_t) {{ .type = PM_RETRY_NODE, .location = PM_LOCATION_TOKEN_VALUE(token) }};
+ return node;
+}
+
+/**
+ * Allocate a new ReturnNode node.
+ */
+static pm_return_node_t *
+pm_return_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_node_t *arguments) {
+ pm_return_node_t *node = PM_ALLOC_NODE(parser, pm_return_node_t);
+
+ *node = (pm_return_node_t) {
+ {
+ .type = PM_RETURN_NODE,
+ .flags = 0,
+ .location = {
+ .start = keyword->start,
+ .end = (arguments == NULL ? keyword->end : arguments->base.location.end)
+ }
+ },
+ .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
+ .arguments = arguments
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new SelfNode node.
+ */
+static pm_self_node_t *
+pm_self_node_create(pm_parser_t *parser, const pm_token_t *token) {
+ assert(token->type == PM_TOKEN_KEYWORD_SELF);
+ pm_self_node_t *node = PM_ALLOC_NODE(parser, pm_self_node_t);
+
+ *node = (pm_self_node_t) {{
+ .type = PM_SELF_NODE,
+ .location = PM_LOCATION_TOKEN_VALUE(token)
+ }};
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new ShareableConstantNode node.
+ */
+static pm_shareable_constant_node_t *
+pm_shareable_constant_node_create(pm_parser_t *parser, pm_node_t *write, pm_shareable_constant_value_t value) {
+ pm_shareable_constant_node_t *node = PM_ALLOC_NODE(parser, pm_shareable_constant_node_t);
+
+ *node = (pm_shareable_constant_node_t) {
+ {
+ .type = PM_SHAREABLE_CONSTANT_NODE,
+ .flags = (pm_node_flags_t) value,
+ .location = PM_LOCATION_NODE_VALUE(write)
+ },
+ .write = write
+ };
+
+ return node;
+}
+
+/**
+ * Allocate a new SingletonClassNode node.
+ */
+static pm_singleton_class_node_t *
+pm_singleton_class_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const pm_token_t *class_keyword, const pm_token_t *operator, pm_node_t *expression, pm_node_t *body, const pm_token_t *end_keyword) {
+ pm_singleton_class_node_t *node = PM_ALLOC_NODE(parser, pm_singleton_class_node_t);
+
+ *node = (pm_singleton_class_node_t) {
+ {
+ .type = PM_SINGLETON_CLASS_NODE,
+ .location = {
+ .start = class_keyword->start,
+ .end = end_keyword->end
+ }
+ },
+ .locals = *locals,
+ .class_keyword_loc = PM_LOCATION_TOKEN_VALUE(class_keyword),
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .expression = expression,
+ .body = body,
+ .end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword)
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new SourceEncodingNode node.
+ */
+static pm_source_encoding_node_t *
+pm_source_encoding_node_create(pm_parser_t *parser, const pm_token_t *token) {
+ assert(token->type == PM_TOKEN_KEYWORD___ENCODING__);
+ pm_source_encoding_node_t *node = PM_ALLOC_NODE(parser, pm_source_encoding_node_t);
+
+ *node = (pm_source_encoding_node_t) {{
+ .type = PM_SOURCE_ENCODING_NODE,
+ .flags = PM_NODE_FLAG_STATIC_LITERAL,
+ .location = PM_LOCATION_TOKEN_VALUE(token)
+ }};
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new SourceFileNode node.
+ */
+static pm_source_file_node_t*
+pm_source_file_node_create(pm_parser_t *parser, const pm_token_t *file_keyword) {
+ pm_source_file_node_t *node = PM_ALLOC_NODE(parser, pm_source_file_node_t);
+ assert(file_keyword->type == PM_TOKEN_KEYWORD___FILE__);
+
+ pm_node_flags_t flags = 0;
+
+ switch (parser->frozen_string_literal) {
+ case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
+ flags |= PM_STRING_FLAGS_MUTABLE;
+ break;
+ case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
+ flags |= PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN;
+ break;
+ }
+
+ *node = (pm_source_file_node_t) {
+ {
+ .type = PM_SOURCE_FILE_NODE,
+ .flags = flags,
+ .location = PM_LOCATION_TOKEN_VALUE(file_keyword),
+ },
+ .filepath = parser->filepath
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new SourceLineNode node.
+ */
+static pm_source_line_node_t *
+pm_source_line_node_create(pm_parser_t *parser, const pm_token_t *token) {
+ assert(token->type == PM_TOKEN_KEYWORD___LINE__);
+ pm_source_line_node_t *node = PM_ALLOC_NODE(parser, pm_source_line_node_t);
+
+ *node = (pm_source_line_node_t) {{
+ .type = PM_SOURCE_LINE_NODE,
+ .flags = PM_NODE_FLAG_STATIC_LITERAL,
+ .location = PM_LOCATION_TOKEN_VALUE(token)
+ }};
+
+ return node;
+}
+
+/**
+ * Allocate a new SplatNode node.
+ */
+static pm_splat_node_t *
+pm_splat_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *expression) {
+ pm_splat_node_t *node = PM_ALLOC_NODE(parser, pm_splat_node_t);
+
+ *node = (pm_splat_node_t) {
+ {
+ .type = PM_SPLAT_NODE,
+ .location = {
+ .start = operator->start,
+ .end = (expression == NULL ? operator->end : expression->location.end)
+ }
+ },
+ .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
+ .expression = expression
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new StatementsNode node.
+ */
+static pm_statements_node_t *
+pm_statements_node_create(pm_parser_t *parser) {
+ pm_statements_node_t *node = PM_ALLOC_NODE(parser, pm_statements_node_t);
+
+ *node = (pm_statements_node_t) {
+ {
+ .type = PM_STATEMENTS_NODE,
+ .location = PM_LOCATION_NULL_VALUE(parser)
+ },
+ .body = { 0 }
+ };
+
+ return node;
+}
+
+/**
+ * Get the length of the given StatementsNode node's body.
+ */
+static size_t
+pm_statements_node_body_length(pm_statements_node_t *node) {
+ return node && node->body.size;
+}
+
+/**
+ * Set the location of the given StatementsNode.
+ */
+static void
+pm_statements_node_location_set(pm_statements_node_t *node, const uint8_t *start, const uint8_t *end) {
+ node->base.location = (pm_location_t) { .start = start, .end = end };
+}
+
+/**
+ * Update the location of the statements node based on the statement that is
+ * being added to the list.
+ */
+static inline void
+pm_statements_node_body_update(pm_statements_node_t *node, pm_node_t *statement) {
+ if (pm_statements_node_body_length(node) == 0 || statement->location.start < node->base.location.start) {
+ node->base.location.start = statement->location.start;
+ }
+
+ if (statement->location.end > node->base.location.end) {
+ node->base.location.end = statement->location.end;
+ }
+}
+
+/**
+ * Append a new node to the given StatementsNode node's body.
+ */
+static void
+pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node, pm_node_t *statement) {
+ pm_statements_node_body_update(node, statement);
+
+ if (node->body.size > 0) {
+ const pm_node_t *previous = node->body.nodes[node->body.size - 1];
+
+ switch (PM_NODE_TYPE(previous)) {
+ case PM_BREAK_NODE:
+ case PM_NEXT_NODE:
+ case PM_REDO_NODE:
+ case PM_RETRY_NODE:
+ case PM_RETURN_NODE:
+ pm_parser_warn_node(parser, previous, PM_WARN_UNREACHABLE_STATEMENT);
+ break;
+ default:
+ break;
+ }
+ }
+
+ pm_node_list_append(&node->body, statement);
+ pm_node_flag_set(statement, PM_NODE_FLAG_NEWLINE);
+}
+
+/**
+ * Prepend a new node to the given StatementsNode node's body.
+ */
+static void
+pm_statements_node_body_prepend(pm_statements_node_t *node, pm_node_t *statement) {
+ pm_statements_node_body_update(node, statement);
+ pm_node_list_prepend(&node->body, statement);
+ pm_node_flag_set(statement, PM_NODE_FLAG_NEWLINE);
+}
+
+/**
+ * Allocate a new StringNode node with the current string on the parser.
+ */
+static inline pm_string_node_t *
+pm_string_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing, const pm_string_t *string) {
+ pm_string_node_t *node = PM_ALLOC_NODE(parser, pm_string_node_t);
+ pm_node_flags_t flags = 0;
+
+ switch (parser->frozen_string_literal) {
+ case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
+ flags = PM_STRING_FLAGS_MUTABLE;
+ break;
+ case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
+ flags = PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN;
+ break;
+ }
+
+ *node = (pm_string_node_t) {
+ {
+ .type = PM_STRING_NODE,
+ .flags = flags,
+ .location = {
+ .start = (opening->type == PM_TOKEN_NOT_PROVIDED ? content->start : opening->start),
+ .end = (closing->type == PM_TOKEN_NOT_PROVIDED ? content->end : closing->end)
+ }
+ },
+ .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening),
+ .content_loc = PM_LOCATION_TOKEN_VALUE(content),
+ .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing),
+ .unescaped = *string
+ };
+
+ return node;
+}
+
+/**
+ * Allocate a new StringNode node.
+ */
+static pm_string_node_t *
+pm_string_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
+ return pm_string_node_create_unescaped(parser, opening, content, closing, &PM_STRING_EMPTY);
+}
+
+/**
+ * Allocate a new StringNode node and create it using the current string on the
+ * parser.
+ */
+static pm_string_node_t *
+pm_string_node_create_current_string(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
+ pm_string_node_t *node = pm_string_node_create_unescaped(parser, opening, content, closing, &parser->current_string);
+ parser->current_string = PM_STRING_EMPTY;
+ return node;
+}
+
+/**
+ * Allocate and initialize a new SuperNode node.
+ */
+static pm_super_node_t *
+pm_super_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_t *arguments) {
+ assert(keyword->type == PM_TOKEN_KEYWORD_SUPER);
+ pm_super_node_t *node = PM_ALLOC_NODE(parser, pm_super_node_t);
+
+ const uint8_t *end = pm_arguments_end(arguments);
+ if (end == NULL) {
+ assert(false && "unreachable");
+ }
+
+ *node = (pm_super_node_t) {
+ {
+ .type = PM_SUPER_NODE,
+ .location = {
+ .start = keyword->start,
+ .end = end,
+ }
+ },
+ .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
+ .lparen_loc = arguments->opening_loc,
+ .arguments = arguments->arguments,
+ .rparen_loc = arguments->closing_loc,
+ .block = arguments->block
+ };
+
+ return node;
+}
+
+/**
+ * Read through the contents of a string and check if it consists solely of US ASCII code points.
+ */
+static bool
+pm_ascii_only_p(const pm_string_t *contents) {
+ const size_t length = pm_string_length(contents);
+ const uint8_t *source = pm_string_source(contents);
+
+ for (size_t index = 0; index < length; index++) {
+ if (source[index] & 0x80) return false;
+ }
+
+ return true;
+}
+
+/**
+ * Ruby "downgrades" the encoding of Symbols to US-ASCII if the associated
+ * encoding is ASCII-compatible and the Symbol consists only of US-ASCII code
+ * points. Otherwise, the encoding may be explicitly set with an escape
+ * sequence.
+ */
+static inline pm_node_flags_t
+parse_symbol_encoding(const pm_parser_t *parser, const pm_string_t *contents) {
+ if (parser->explicit_encoding != NULL) {
+ // A Symbol may optionally have its encoding explicitly set. This will
+ // happen if an escape sequence results in a non-ASCII code point.
+ if (parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY) {
+ return PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING;
+ } else if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
+ return PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING;
+ }
+ } else if (pm_ascii_only_p(contents)) {
+ // Ruby stipulates that all source files must use an ASCII-compatible
+ // encoding. Thus, all symbols appearing in source are eligible for
+ // "downgrading" to US-ASCII.
+ return PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING;
+ }
+
+ return 0;
+}
+
+static pm_node_flags_t
+parse_and_validate_regular_expression_encoding_modifier(pm_parser_t *parser, const pm_string_t *source, bool ascii_only, pm_node_flags_t flags, char modifier, const pm_encoding_t *modifier_encoding) {
+ assert ((modifier == 'n' && modifier_encoding == PM_ENCODING_ASCII_8BIT_ENTRY) ||
+ (modifier == 'u' && modifier_encoding == PM_ENCODING_UTF_8_ENTRY) ||
+ (modifier == 'e' && modifier_encoding == PM_ENCODING_EUC_JP_ENTRY) ||
+ (modifier == 's' && modifier_encoding == PM_ENCODING_WINDOWS_31J_ENTRY));
+
+ // There's special validation logic used if a string does not contain any character escape sequences.
+ if (parser->explicit_encoding == NULL) {
+ // If an ASCII-only string without character escapes is used with an encoding modifier, then resulting Regexp
+ // has the modifier encoding, unless the ASCII-8BIT modifier is used, in which case the Regexp "downgrades" to
+ // the US-ASCII encoding.
+ if (ascii_only) {
+ return modifier == 'n' ? PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING : flags;
+ }
+
+ if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
+ if (!ascii_only) {
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_INVALID_MULTIBYTE_CHAR, parser->encoding->name);
+ }
+ } else if (parser->encoding != modifier_encoding) {
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_REGEXP_ENCODING_OPTION_MISMATCH, modifier, parser->encoding->name);
+
+ if (modifier == 'n' && !ascii_only) {
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_REGEXP_NON_ESCAPED_MBC, (int) pm_string_length(source), (const char *) pm_string_source(source));
+ }
+ }
+
+ return flags;
+ }
+
+ // TODO (nirvdrum 21-Feb-2024): To validate regexp sources with character escape sequences we need to know whether hex or Unicode escape sequences were used and Prism doesn't currently provide that data. We handle a subset of unambiguous cases in the meanwhile.
+ bool mixed_encoding = false;
+
+ if (mixed_encoding) {
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_INVALID_MULTIBYTE_ESCAPE, (int) pm_string_length(source), (const char *) pm_string_source(source));
+ } else if (modifier != 'n' && parser->explicit_encoding == PM_ENCODING_ASCII_8BIT_ENTRY) {
+ // TODO (nirvdrum 21-Feb-2024): Validate the content is valid in the modifier encoding. Do this on-demand so we don't pay the cost of computation unnecessarily.
+ bool valid_string_in_modifier_encoding = true;
+
+ if (!valid_string_in_modifier_encoding) {
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_INVALID_MULTIBYTE_ESCAPE, (int) pm_string_length(source), (const char *) pm_string_source(source));
+ }
+ } else if (modifier != 'u' && parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY) {
+ // TODO (nirvdrum 21-Feb-2024): There's currently no way to tell if the source used hex or Unicode character escapes from `explicit_encoding` alone. If the source encoding was already UTF-8, both character escape types would set `explicit_encoding` to UTF-8, but need to be processed differently. Skip for now.
+ if (parser->encoding != PM_ENCODING_UTF_8_ENTRY) {
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_REGEXP_INCOMPAT_CHAR_ENCODING, (int) pm_string_length(source), (const char *) pm_string_source(source));
+ }
+ }
+
+ // We've determined the encoding would naturally be EUC-JP and there is no need to force the encoding to anything else.
+ return flags;
+}
+
+/**
+ * Ruby "downgrades" the encoding of Regexps to US-ASCII if the associated encoding is ASCII-compatible and
+ * the unescaped representation of a Regexp source consists only of US-ASCII code points. This is true even
+ * when the Regexp is explicitly given an ASCII-8BIT encoding via the (/n) modifier. Otherwise, the encoding
+ * may be explicitly set with an escape sequence.
+ */
+static pm_node_flags_t
+parse_and_validate_regular_expression_encoding(pm_parser_t *parser, const pm_string_t *source, bool ascii_only, pm_node_flags_t flags) {
+ // TODO (nirvdrum 22-Feb-2024): CRuby reports a special Regexp-specific error for invalid Unicode ranges. We either need to scan again or modify the "invalid Unicode escape sequence" message we already report.
+ bool valid_unicode_range = true;
+ if (parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY && !valid_unicode_range) {
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_REGEXP_INVALID_UNICODE_RANGE, (int) pm_string_length(source), (const char *) pm_string_source(source));
+ return flags;
+ }
+
+ // US-ASCII strings do not admit multi-byte character literals. However, character escape sequences corresponding
+ // to multi-byte characters are allowed.
+ if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY && parser->explicit_encoding == NULL && !ascii_only) {
+ // CRuby will continue processing even though a SyntaxError has already been detected. It may result in the
+ // following error message appearing twice. We do the same for compatibility.
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_INVALID_MULTIBYTE_CHAR, parser->encoding->name);
+ }
+
+ /**
+ * Start checking modifier flags. We need to process these before considering any explicit encodings that may have
+ * been set by character literals. The order in which the encoding modifiers is checked does not matter. In the
+ * event that both an encoding modifier and an explicit encoding would result in the same encoding we do not set
+ * the corresponding "forced_<encoding>" flag. Instead, the caller should check the encoding modifier flag and
+ * determine the encoding that way.
+ */
+
+ if (flags & PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT) {
+ return parse_and_validate_regular_expression_encoding_modifier(parser, source, ascii_only, flags, 'n', PM_ENCODING_ASCII_8BIT_ENTRY);
+ }
+
+ if (flags & PM_REGULAR_EXPRESSION_FLAGS_UTF_8) {
+ return parse_and_validate_regular_expression_encoding_modifier(parser, source, ascii_only, flags, 'u', PM_ENCODING_UTF_8_ENTRY);
+ }
+
+ if (flags & PM_REGULAR_EXPRESSION_FLAGS_EUC_JP) {
+ return parse_and_validate_regular_expression_encoding_modifier(parser, source, ascii_only, flags, 'e', PM_ENCODING_EUC_JP_ENTRY);
+ }
+
+ if (flags & PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J) {
+ return parse_and_validate_regular_expression_encoding_modifier(parser, source, ascii_only, flags, 's', PM_ENCODING_WINDOWS_31J_ENTRY);
+ }
+
+ // At this point no encoding modifiers will be present on the regular expression as they would have already
+ // been processed. Ruby stipulates that all source files must use an ASCII-compatible encoding. Thus, all
+ // regular expressions without an encoding modifier appearing in source are eligible for "downgrading" to US-ASCII.
+ if (ascii_only) {
+ return PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING;
+ }
+
+ // A Regexp may optionally have its encoding explicitly set via a character escape sequence in the source string
+ // or by specifying a modifier.
+ //
+ // NB: an explicitly set encoding is ignored by Ruby if the Regexp consists of only US ASCII code points.
+ if (parser->explicit_encoding != NULL) {
+ if (parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY) {
+ return PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING;
+ } else if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
+ return PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Allocate and initialize a new SymbolNode node with the given unescaped
+ * string.
+ */
+static pm_symbol_node_t *
+pm_symbol_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *value, const pm_token_t *closing, const pm_string_t *unescaped, pm_node_flags_t flags) {
+ pm_symbol_node_t *node = PM_ALLOC_NODE(parser, pm_symbol_node_t);
+
+ *node = (pm_symbol_node_t) {
+ {
+ .type = PM_SYMBOL_NODE,
+ .flags = PM_NODE_FLAG_STATIC_LITERAL | flags,
+ .location = {
+ .start = (opening->type == PM_TOKEN_NOT_PROVIDED ? value->start : opening->start),
+ .end = (closing->type == PM_TOKEN_NOT_PROVIDED ? value->end : closing->end)
+ }
+ },
+ .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening),
+ .value_loc = PM_LOCATION_TOKEN_VALUE(value),
+ .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing),
+ .unescaped = *unescaped
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new SymbolNode node.
+ */
+static inline pm_symbol_node_t *
+pm_symbol_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *value, const pm_token_t *closing) {
+ return pm_symbol_node_create_unescaped(parser, opening, value, closing, &PM_STRING_EMPTY, 0);
+}
+
+/**
+ * Allocate and initialize a new SymbolNode node with the current string.
+ */
+static pm_symbol_node_t *
+pm_symbol_node_create_current_string(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *value, const pm_token_t *closing) {
+ pm_symbol_node_t *node = pm_symbol_node_create_unescaped(parser, opening, value, closing, &parser->current_string, parse_symbol_encoding(parser, &parser->current_string));
+ parser->current_string = PM_STRING_EMPTY;
+ return node;
+}
+
+/**
+ * Allocate and initialize a new SymbolNode node from a label.
+ */
+static pm_symbol_node_t *
+pm_symbol_node_label_create(pm_parser_t *parser, const pm_token_t *token) {
+ pm_symbol_node_t *node;
+
+ switch (token->type) {
+ case PM_TOKEN_LABEL: {
+ pm_token_t opening = not_provided(parser);
+ pm_token_t closing = { .type = PM_TOKEN_LABEL_END, .start = token->end - 1, .end = token->end };
+
+ pm_token_t label = { .type = PM_TOKEN_LABEL, .start = token->start, .end = token->end - 1 };
+ node = pm_symbol_node_create(parser, &opening, &label, &closing);
+
+ assert((label.end - label.start) >= 0);
+ pm_string_shared_init(&node->unescaped, label.start, label.end);
+ pm_node_flag_set((pm_node_t *) node, parse_symbol_encoding(parser, &node->unescaped));
+
+ break;
+ }
+ case PM_TOKEN_MISSING: {
+ pm_token_t opening = not_provided(parser);
+ pm_token_t closing = not_provided(parser);
+
+ pm_token_t label = { .type = PM_TOKEN_LABEL, .start = token->start, .end = token->end };
+ node = pm_symbol_node_create(parser, &opening, &label, &closing);
+ break;
+ }
+ default:
+ assert(false && "unreachable");
+ node = NULL;
+ break;
+ }
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new synthesized SymbolNode node.
+ */
+static pm_symbol_node_t *
+pm_symbol_node_synthesized_create(pm_parser_t *parser, const char *content) {
+ pm_symbol_node_t *node = PM_ALLOC_NODE(parser, pm_symbol_node_t);
+
+ *node = (pm_symbol_node_t) {
+ {
+ .type = PM_SYMBOL_NODE,
+ .flags = PM_NODE_FLAG_STATIC_LITERAL | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING,
+ .location = { .start = parser->start, .end = parser->start }
+ },
+ .value_loc = { .start = parser->start, .end = parser->start },
+ .unescaped = { 0 }
+ };
+
+ pm_string_constant_init(&node->unescaped, content, strlen(content));
+ return node;
+}
+
+/**
+ * Check if the given node is a label in a hash.
+ */
+static bool
+pm_symbol_node_label_p(pm_node_t *node) {
+ const uint8_t *end = NULL;
+
+ switch (PM_NODE_TYPE(node)) {
+ case PM_SYMBOL_NODE:
+ end = ((pm_symbol_node_t *) node)->closing_loc.end;
+ break;
+ case PM_INTERPOLATED_SYMBOL_NODE:
+ end = ((pm_interpolated_symbol_node_t *) node)->closing_loc.end;
+ break;
+ default:
+ return false;
+ }
+
+ return (end != NULL) && (end[-1] == ':');
+}
+
+/**
+ * Convert the given StringNode node to a SymbolNode node.
+ */
+static pm_symbol_node_t *
+pm_string_node_to_symbol_node(pm_parser_t *parser, pm_string_node_t *node, const pm_token_t *opening, const pm_token_t *closing) {
+ pm_symbol_node_t *new_node = PM_ALLOC_NODE(parser, pm_symbol_node_t);
+
+ *new_node = (pm_symbol_node_t) {
+ {
+ .type = PM_SYMBOL_NODE,
+ .flags = PM_NODE_FLAG_STATIC_LITERAL,
+ .location = {
+ .start = opening->start,
+ .end = closing->end
+ }
+ },
+ .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening),
+ .value_loc = node->content_loc,
+ .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing),
+ .unescaped = node->unescaped
+ };
+
+ pm_node_flag_set((pm_node_t *)new_node, parse_symbol_encoding(parser, &node->unescaped));
+
+ // We are explicitly _not_ using pm_node_destroy here because we don't want
+ // to trash the unescaped string. We could instead copy the string if we
+ // know that it is owned, but we're taking the fast path for now.
+ xfree(node);
+
+ return new_node;
+}
+
+/**
+ * Convert the given SymbolNode node to a StringNode node.
+ */
+static pm_string_node_t *
+pm_symbol_node_to_string_node(pm_parser_t *parser, pm_symbol_node_t *node) {
+ pm_string_node_t *new_node = PM_ALLOC_NODE(parser, pm_string_node_t);
+ pm_node_flags_t flags = 0;
+
+ switch (parser->frozen_string_literal) {
+ case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
+ flags = PM_STRING_FLAGS_MUTABLE;
+ break;
+ case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
+ flags = PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN;
+ break;
+ }
+
+ *new_node = (pm_string_node_t) {
+ {
+ .type = PM_STRING_NODE,
+ .flags = flags,
+ .location = node->base.location
+ },
+ .opening_loc = node->opening_loc,
+ .content_loc = node->value_loc,
+ .closing_loc = node->closing_loc,
+ .unescaped = node->unescaped
+ };
+
+ // We are explicitly _not_ using pm_node_destroy here because we don't want
+ // to trash the unescaped string. We could instead copy the string if we
+ // know that it is owned, but we're taking the fast path for now.
+ xfree(node);
+
+ return new_node;
+}
+
+/**
+ * Allocate and initialize a new TrueNode node.
+ */
+static pm_true_node_t *
+pm_true_node_create(pm_parser_t *parser, const pm_token_t *token) {
+ assert(token->type == PM_TOKEN_KEYWORD_TRUE);
+ pm_true_node_t *node = PM_ALLOC_NODE(parser, pm_true_node_t);
+
+ *node = (pm_true_node_t) {{
+ .type = PM_TRUE_NODE,
+ .flags = PM_NODE_FLAG_STATIC_LITERAL,
+ .location = PM_LOCATION_TOKEN_VALUE(token)
+ }};
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new synthesized TrueNode node.
+ */
+static pm_true_node_t *
+pm_true_node_synthesized_create(pm_parser_t *parser) {
+ pm_true_node_t *node = PM_ALLOC_NODE(parser, pm_true_node_t);
+
+ *node = (pm_true_node_t) {{
+ .type = PM_TRUE_NODE,
+ .flags = PM_NODE_FLAG_STATIC_LITERAL,
+ .location = { .start = parser->start, .end = parser->end }
+ }};
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new UndefNode node.
+ */
+static pm_undef_node_t *
+pm_undef_node_create(pm_parser_t *parser, const pm_token_t *token) {
+ assert(token->type == PM_TOKEN_KEYWORD_UNDEF);
+ pm_undef_node_t *node = PM_ALLOC_NODE(parser, pm_undef_node_t);
+
+ *node = (pm_undef_node_t) {
+ {
+ .type = PM_UNDEF_NODE,
+ .location = PM_LOCATION_TOKEN_VALUE(token),
+ },
+ .keyword_loc = PM_LOCATION_TOKEN_VALUE(token),
+ .names = { 0 }
+ };
+
+ return node;
+}
+
+/**
+ * Append a name to an undef node.
+ */
+static void
+pm_undef_node_append(pm_undef_node_t *node, pm_node_t *name) {
+ node->base.location.end = name->location.end;
+ pm_node_list_append(&node->names, name);
+}
+
+/**
+ * Allocate a new UnlessNode node.
+ */
+static pm_unless_node_t *
+pm_unless_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *predicate, const pm_token_t *then_keyword, pm_statements_node_t *statements) {
+ pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
+ pm_unless_node_t *node = PM_ALLOC_NODE(parser, pm_unless_node_t);
+
+ const uint8_t *end;
+ if (statements != NULL) {
+ end = statements->base.location.end;
+ } else {
+ end = predicate->location.end;
+ }
+
+ *node = (pm_unless_node_t) {
+ {
+ .type = PM_UNLESS_NODE,
+ .flags = PM_NODE_FLAG_NEWLINE,
+ .location = {
+ .start = keyword->start,
+ .end = end
+ },
+ },
+ .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
+ .predicate = predicate,
+ .then_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(then_keyword),
+ .statements = statements,
+ .consequent = NULL,
+ .end_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize new UnlessNode node in the modifier form.
+ */
+static pm_unless_node_t *
+pm_unless_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_token_t *unless_keyword, pm_node_t *predicate) {
+ pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
+ pm_unless_node_t *node = PM_ALLOC_NODE(parser, pm_unless_node_t);
+
+ pm_statements_node_t *statements = pm_statements_node_create(parser);
+ pm_statements_node_body_append(parser, statements, statement);
+
+ *node = (pm_unless_node_t) {
+ {
+ .type = PM_UNLESS_NODE,
+ .flags = PM_NODE_FLAG_NEWLINE,
+ .location = {
+ .start = statement->location.start,
+ .end = predicate->location.end
+ },
+ },
+ .keyword_loc = PM_LOCATION_TOKEN_VALUE(unless_keyword),
+ .predicate = predicate,
+ .then_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
+ .statements = statements,
+ .consequent = NULL,
+ .end_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE
+ };
+
+ return node;
+}
+
+static inline void
+pm_unless_node_end_keyword_loc_set(pm_unless_node_t *node, const pm_token_t *end_keyword) {
+ node->end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword);
+ node->base.location.end = end_keyword->end;
+}
+
+/**
+ * Allocate a new UntilNode node.
+ */
+static pm_until_node_t *
+pm_until_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_token_t *closing, pm_node_t *predicate, pm_statements_node_t *statements, pm_node_flags_t flags) {
+ pm_until_node_t *node = PM_ALLOC_NODE(parser, pm_until_node_t);
+ pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
+
+ *node = (pm_until_node_t) {
+ {
+ .type = PM_UNTIL_NODE,
+ .flags = flags,
+ .location = {
+ .start = keyword->start,
+ .end = closing->end,
+ },
+ },
+ .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
+ .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing),
+ .predicate = predicate,
+ .statements = statements
+ };
+
+ return node;
+}
+
+/**
+ * Allocate a new UntilNode node.
+ */
+static pm_until_node_t *
+pm_until_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *predicate, pm_statements_node_t *statements, pm_node_flags_t flags) {
+ pm_until_node_t *node = PM_ALLOC_NODE(parser, pm_until_node_t);
+ pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
+
+ *node = (pm_until_node_t) {
+ {
+ .type = PM_UNTIL_NODE,
+ .flags = flags,
+ .location = {
+ .start = statements->base.location.start,
+ .end = predicate->location.end,
+ },
+ },
+ .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
+ .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
+ .predicate = predicate,
+ .statements = statements
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new WhenNode node.
+ */
+static pm_when_node_t *
+pm_when_node_create(pm_parser_t *parser, const pm_token_t *keyword) {
+ pm_when_node_t *node = PM_ALLOC_NODE(parser, pm_when_node_t);
+
+ *node = (pm_when_node_t) {
+ {
+ .type = PM_WHEN_NODE,
+ .location = {
+ .start = keyword->start,
+ .end = NULL
+ }
+ },
+ .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
+ .statements = NULL,
+ .then_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
+ .conditions = { 0 }
+ };
+
+ return node;
+}
+
+/**
+ * Append a new condition to a when node.
+ */
+static void
+pm_when_node_conditions_append(pm_when_node_t *node, pm_node_t *condition) {
+ node->base.location.end = condition->location.end;
+ pm_node_list_append(&node->conditions, condition);
+}
+
+/**
+ * Set the location of the then keyword of a when node.
+ */
+static inline void
+pm_when_node_then_keyword_loc_set(pm_when_node_t *node, const pm_token_t *then_keyword) {
+ node->base.location.end = then_keyword->end;
+ node->then_keyword_loc = PM_LOCATION_TOKEN_VALUE(then_keyword);
+}
+
+/**
+ * Set the statements list of a when node.
+ */
+static void
+pm_when_node_statements_set(pm_when_node_t *node, pm_statements_node_t *statements) {
+ if (statements->base.location.end > node->base.location.end) {
+ node->base.location.end = statements->base.location.end;
+ }
+
+ node->statements = statements;
+}
+
+/**
+ * Allocate a new WhileNode node.
+ */
+static pm_while_node_t *
+pm_while_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_token_t *closing, pm_node_t *predicate, pm_statements_node_t *statements, pm_node_flags_t flags) {
+ pm_while_node_t *node = PM_ALLOC_NODE(parser, pm_while_node_t);
+ pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
+
+ *node = (pm_while_node_t) {
+ {
+ .type = PM_WHILE_NODE,
+ .flags = flags,
+ .location = {
+ .start = keyword->start,
+ .end = closing->end
+ },
+ },
+ .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
+ .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing),
+ .predicate = predicate,
+ .statements = statements
+ };
+
+ return node;
+}
+
+/**
+ * Allocate a new WhileNode node.
+ */
+static pm_while_node_t *
+pm_while_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *predicate, pm_statements_node_t *statements, pm_node_flags_t flags) {
+ pm_while_node_t *node = PM_ALLOC_NODE(parser, pm_while_node_t);
+ pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
+
+ *node = (pm_while_node_t) {
+ {
+ .type = PM_WHILE_NODE,
+ .flags = flags,
+ .location = {
+ .start = statements->base.location.start,
+ .end = predicate->location.end
+ },
+ },
+ .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
+ .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
+ .predicate = predicate,
+ .statements = statements
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new synthesized while loop.
+ */
+static pm_while_node_t *
+pm_while_node_synthesized_create(pm_parser_t *parser, pm_node_t *predicate, pm_statements_node_t *statements) {
+ pm_while_node_t *node = PM_ALLOC_NODE(parser, pm_while_node_t);
+
+ *node = (pm_while_node_t) {
+ {
+ .type = PM_WHILE_NODE,
+ .location = { .start = parser->start, .end = parser->start }
+ },
+ .keyword_loc = { .start = parser->start, .end = parser->start },
+ .closing_loc = { .start = parser->start, .end = parser->start },
+ .predicate = predicate,
+ .statements = statements
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new XStringNode node with the given unescaped
+ * string.
+ */
+static pm_x_string_node_t *
+pm_xstring_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing, const pm_string_t *unescaped) {
+ pm_x_string_node_t *node = PM_ALLOC_NODE(parser, pm_x_string_node_t);
+
+ *node = (pm_x_string_node_t) {
+ {
+ .type = PM_X_STRING_NODE,
+ .flags = PM_STRING_FLAGS_FROZEN,
+ .location = {
+ .start = opening->start,
+ .end = closing->end
+ },
+ },
+ .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
+ .content_loc = PM_LOCATION_TOKEN_VALUE(content),
+ .closing_loc = PM_LOCATION_TOKEN_VALUE(closing),
+ .unescaped = *unescaped
+ };
+
+ return node;
+}
+
+/**
+ * Allocate and initialize a new XStringNode node.
+ */
+static inline pm_x_string_node_t *
+pm_xstring_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
+ return pm_xstring_node_create_unescaped(parser, opening, content, closing, &PM_STRING_EMPTY);
+}
+
+/**
+ * Allocate a new YieldNode node.
+ */
+static pm_yield_node_t *
+pm_yield_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_location_t *lparen_loc, pm_arguments_node_t *arguments, const pm_location_t *rparen_loc) {
+ pm_yield_node_t *node = PM_ALLOC_NODE(parser, pm_yield_node_t);
+
+ const uint8_t *end;
+ if (rparen_loc->start != NULL) {
+ end = rparen_loc->end;
+ } else if (arguments != NULL) {
+ end = arguments->base.location.end;
+ } else if (lparen_loc->start != NULL) {
+ end = lparen_loc->end;
+ } else {
+ end = keyword->end;
+ }
+
+ *node = (pm_yield_node_t) {
+ {
+ .type = PM_YIELD_NODE,
+ .location = {
+ .start = keyword->start,
+ .end = end
+ },
+ },
+ .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
+ .lparen_loc = *lparen_loc,
+ .arguments = arguments,
+ .rparen_loc = *rparen_loc
+ };
+
+ return node;
+}
+
+#undef PM_ALLOC_NODE
+
+/**
+ * Check if any of the currently visible scopes contain a local variable
+ * described by the given constant id.
+ */
+static int
+pm_parser_local_depth_constant_id(pm_parser_t *parser, pm_constant_id_t constant_id) {
+ pm_scope_t *scope = parser->current_scope;
+ int depth = 0;
+
+ while (scope != NULL) {
+ if (pm_locals_find(&scope->locals, constant_id) != UINT32_MAX) return depth;
+ if (scope->closed) break;
+
+ scope = scope->previous;
+ depth++;
+ }
+
+ return -1;
+}
+
+/**
+ * Check if any of the currently visible scopes contain a local variable
+ * described by the given token. This function implicitly inserts a constant
+ * into the constant pool.
+ */
+static inline int
+pm_parser_local_depth(pm_parser_t *parser, pm_token_t *token) {
+ return pm_parser_local_depth_constant_id(parser, pm_parser_constant_id_token(parser, token));
+}
+
+/**
+ * Add a constant id to the local table of the current scope.
+ */
+static inline void
+pm_parser_local_add(pm_parser_t *parser, pm_constant_id_t constant_id, const uint8_t *start, const uint8_t *end, uint32_t reads) {
+ pm_locals_write(&parser->current_scope->locals, constant_id, start, end, reads);
+}
+
+/**
+ * Add a local variable from a location to the current scope.
+ */
+static pm_constant_id_t
+pm_parser_local_add_location(pm_parser_t *parser, const uint8_t *start, const uint8_t *end, uint32_t reads) {
+ pm_constant_id_t constant_id = pm_parser_constant_id_location(parser, start, end);
+ if (constant_id != 0) pm_parser_local_add(parser, constant_id, start, end, reads);
+ return constant_id;
+}
+
+/**
+ * Add a local variable from a token to the current scope.
+ */
+static inline pm_constant_id_t
+pm_parser_local_add_token(pm_parser_t *parser, pm_token_t *token, uint32_t reads) {
+ return pm_parser_local_add_location(parser, token->start, token->end, reads);
+}
+
+/**
+ * Add a local variable from an owned string to the current scope.
+ */
+static pm_constant_id_t
+pm_parser_local_add_owned(pm_parser_t *parser, uint8_t *start, size_t length) {
+ pm_constant_id_t constant_id = pm_parser_constant_id_owned(parser, start, length);
+ if (constant_id != 0) pm_parser_local_add(parser, constant_id, parser->start, parser->start, 1);
+ return constant_id;
+}
+
+/**
+ * Add a local variable from a constant string to the current scope.
+ */
+static pm_constant_id_t
+pm_parser_local_add_constant(pm_parser_t *parser, const char *start, size_t length) {
+ pm_constant_id_t constant_id = pm_parser_constant_id_constant(parser, start, length);
+ if (constant_id != 0) pm_parser_local_add(parser, constant_id, parser->start, parser->start, 1);
+ return constant_id;
+}
+
+/**
+ * Create a local variable read that is reading the implicit 'it' variable.
+ */
+static pm_local_variable_read_node_t *
+pm_local_variable_read_node_create_it(pm_parser_t *parser, const pm_token_t *name) {
+ if (parser->current_scope->parameters & PM_SCOPE_PARAMETERS_ORDINARY) {
+ pm_parser_err_token(parser, name, PM_ERR_IT_NOT_ALLOWED_ORDINARY);
+ return NULL;
+ }
+
+ if (parser->current_scope->parameters & PM_SCOPE_PARAMETERS_NUMBERED) {
+ pm_parser_err_token(parser, name, PM_ERR_IT_NOT_ALLOWED_NUMBERED);
+ return NULL;
+ }
+
+ parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_IT;
+
+ pm_constant_id_t name_id = pm_parser_constant_id_constant(parser, "0it", 3);
+ pm_parser_local_add(parser, name_id, name->start, name->end, 0);
+
+ return pm_local_variable_read_node_create_constant_id(parser, name, name_id, 0, false);
+}
+
+/**
+ * Convert a `it` variable call node to a node for `it` default parameter.
+ */
+static pm_node_t *
+pm_node_check_it(pm_parser_t *parser, pm_node_t *node) {
+ if (
+ (parser->version != PM_OPTIONS_VERSION_CRUBY_3_3_0) &&
+ !parser->current_scope->closed &&
+ (parser->current_scope->numbered_parameters != PM_SCOPE_NUMBERED_PARAMETERS_DISALLOWED) &&
+ pm_node_is_it(parser, node)
+ ) {
+ pm_local_variable_read_node_t *read = pm_local_variable_read_node_create_it(parser, &parser->previous);
+
+ if (read != NULL) {
+ pm_node_destroy(parser, node);
+ node = (pm_node_t *) read;
+ }
+ }
+
+ return node;
+}
+
+/**
+ * Add a parameter name to the current scope and check whether the name of the
+ * parameter is unique or not.
+ *
+ * Returns `true` if this is a duplicate parameter name, otherwise returns
+ * false.
+ */
+static bool
+pm_parser_parameter_name_check(pm_parser_t *parser, const pm_token_t *name) {
+ // We want to check whether the parameter name is a numbered parameter or
+ // not.
+ pm_refute_numbered_parameter(parser, name->start, name->end);
+
+ // Otherwise we'll fetch the constant id for the parameter name and check
+ // whether it's already in the current scope.
+ pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, name);
+
+ if (pm_locals_find(&parser->current_scope->locals, constant_id) != UINT32_MAX) {
+ // Add an error if the parameter doesn't start with _ and has been seen before
+ if ((name->start < name->end) && (*name->start != '_')) {
+ pm_parser_err_token(parser, name, PM_ERR_PARAMETER_NAME_DUPLICATED);
+ }
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Pop the current scope off the scope stack.
+ */
+static void
+pm_parser_scope_pop(pm_parser_t *parser) {
+ pm_scope_t *scope = parser->current_scope;
+ parser->current_scope = scope->previous;
+ pm_locals_free(&scope->locals);
+ xfree(scope);
+}
+
+/******************************************************************************/
+/* Stack helpers */
+/******************************************************************************/
+
+/**
+ * Pushes a value onto the stack.
+ */
+static inline void
+pm_state_stack_push(pm_state_stack_t *stack, bool value) {
+ *stack = (*stack << 1) | (value & 1);
+}
+
+/**
+ * Pops a value off the stack.
+ */
+static inline void
+pm_state_stack_pop(pm_state_stack_t *stack) {
+ *stack >>= 1;
+}
+
+/**
+ * Returns the value at the top of the stack.
+ */
+static inline bool
+pm_state_stack_p(const pm_state_stack_t *stack) {
+ return *stack & 1;
+}
+
+static inline void
+pm_accepts_block_stack_push(pm_parser_t *parser, bool value) {
+ // Use the negation of the value to prevent stack overflow.
+ pm_state_stack_push(&parser->accepts_block_stack, !value);
+}
+
+static inline void
+pm_accepts_block_stack_pop(pm_parser_t *parser) {
+ pm_state_stack_pop(&parser->accepts_block_stack);
+}
+
+static inline bool
+pm_accepts_block_stack_p(pm_parser_t *parser) {
+ return !pm_state_stack_p(&parser->accepts_block_stack);
+}
+
+static inline void
+pm_do_loop_stack_push(pm_parser_t *parser, bool value) {
+ pm_state_stack_push(&parser->do_loop_stack, value);
+}
+
+static inline void
+pm_do_loop_stack_pop(pm_parser_t *parser) {
+ pm_state_stack_pop(&parser->do_loop_stack);
+}
+
+static inline bool
+pm_do_loop_stack_p(pm_parser_t *parser) {
+ return pm_state_stack_p(&parser->do_loop_stack);
+}
+
+/******************************************************************************/
+/* Lexer check helpers */
+/******************************************************************************/
+
+/**
+ * Get the next character in the source starting from +cursor+. If that position
+ * is beyond the end of the source then return '\0'.
+ */
+static inline uint8_t
+peek_at(pm_parser_t *parser, const uint8_t *cursor) {
+ if (cursor < parser->end) {
+ return *cursor;
+ } else {
+ return '\0';
+ }
+}
+
+/**
+ * Get the next character in the source starting from parser->current.end and
+ * adding the given offset. If that position is beyond the end of the source
+ * then return '\0'.
+ */
+static inline uint8_t
+peek_offset(pm_parser_t *parser, ptrdiff_t offset) {
+ return peek_at(parser, parser->current.end + offset);
+}
+
+/**
+ * Get the next character in the source starting from parser->current.end. If
+ * that position is beyond the end of the source then return '\0'.
+ */
+static inline uint8_t
+peek(pm_parser_t *parser) {
+ return peek_at(parser, parser->current.end);
+}
+
+/**
+ * If the character to be read matches the given value, then returns true and
+ * advances the current pointer.
+ */
+static inline bool
+match(pm_parser_t *parser, uint8_t value) {
+ if (peek(parser) == value) {
+ parser->current.end++;
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Return the length of the line ending string starting at +cursor+, or 0 if it
+ * is not a line ending. This function is intended to be CRLF/LF agnostic.
+ */
+static inline size_t
+match_eol_at(pm_parser_t *parser, const uint8_t *cursor) {
+ if (peek_at(parser, cursor) == '\n') {
+ return 1;
+ }
+ if (peek_at(parser, cursor) == '\r' && peek_at(parser, cursor + 1) == '\n') {
+ return 2;
+ }
+ return 0;
+}
+
+/**
+ * Return the length of the line ending string starting at
+ * `parser->current.end + offset`, or 0 if it is not a line ending. This
+ * function is intended to be CRLF/LF agnostic.
+ */
+static inline size_t
+match_eol_offset(pm_parser_t *parser, ptrdiff_t offset) {
+ return match_eol_at(parser, parser->current.end + offset);
+}
+
+/**
+ * Return the length of the line ending string starting at parser->current.end,
+ * or 0 if it is not a line ending. This function is intended to be CRLF/LF
+ * agnostic.
+ */
+static inline size_t
+match_eol(pm_parser_t *parser) {
+ return match_eol_at(parser, parser->current.end);
+}
+
+/**
+ * Skip to the next newline character or NUL byte.
+ */
+static inline const uint8_t *
+next_newline(const uint8_t *cursor, ptrdiff_t length) {
+ assert(length >= 0);
+
+ // Note that it's okay for us to use memchr here to look for \n because none
+ // of the encodings that we support have \n as a component of a multi-byte
+ // character.
+ return memchr(cursor, '\n', (size_t) length);
+}
+
+/**
+ * Here we're going to check if this is a "magic" comment, and perform whatever
+ * actions are necessary for it here.
+ */
+static bool
+parser_lex_magic_comment_encoding_value(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
+ const pm_encoding_t *encoding = pm_encoding_find(start, end);
+
+ if (encoding != NULL) {
+ if (parser->encoding != encoding) {
+ parser->encoding = encoding;
+ if (parser->encoding_changed_callback != NULL) parser->encoding_changed_callback(parser);
+ }
+
+ parser->encoding_changed = (encoding != PM_ENCODING_UTF_8_ENTRY);
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Look for a specific pattern of "coding" and potentially set the encoding on
+ * the parser.
+ */
+static void
+parser_lex_magic_comment_encoding(pm_parser_t *parser) {
+ const uint8_t *cursor = parser->current.start + 1;
+ const uint8_t *end = parser->current.end;
+
+ bool separator = false;
+ while (true) {
+ if (end - cursor <= 6) return;
+ switch (cursor[6]) {
+ case 'C': case 'c': cursor += 6; continue;
+ case 'O': case 'o': cursor += 5; continue;
+ case 'D': case 'd': cursor += 4; continue;
+ case 'I': case 'i': cursor += 3; continue;
+ case 'N': case 'n': cursor += 2; continue;
+ case 'G': case 'g': cursor += 1; continue;
+ case '=': case ':':
+ separator = true;
+ cursor += 6;
+ break;
+ default:
+ cursor += 6;
+ if (pm_char_is_whitespace(*cursor)) break;
+ continue;
+ }
+ if (pm_strncasecmp(cursor - 6, (const uint8_t *) "coding", 6) == 0) break;
+ separator = false;
+ }
+
+ while (true) {
+ do {
+ if (++cursor >= end) return;
+ } while (pm_char_is_whitespace(*cursor));
+
+ if (separator) break;
+ if (*cursor != '=' && *cursor != ':') return;
+
+ separator = true;
+ cursor++;
+ }
+
+ const uint8_t *value_start = cursor;
+ while ((*cursor == '-' || *cursor == '_' || parser->encoding->alnum_char(cursor, 1)) && ++cursor < end);
+
+ if (!parser_lex_magic_comment_encoding_value(parser, value_start, cursor)) {
+ // If we were unable to parse the encoding value, then we've got an
+ // issue because we didn't understand the encoding that the user was
+ // trying to use. In this case we'll keep using the default encoding but
+ // add an error to the parser to indicate an unsuccessful parse.
+ pm_parser_err(parser, value_start, cursor, PM_ERR_INVALID_ENCODING_MAGIC_COMMENT);
+ }
+}
+
+/**
+ * Check if this is a magic comment that includes the frozen_string_literal
+ * pragma. If it does, set that field on the parser.
+ */
+static void
+parser_lex_magic_comment_frozen_string_literal_value(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
+ if ((start + 4 <= end) && pm_strncasecmp(start, (const uint8_t *) "true", 4) == 0) {
+ parser->frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED;
+ } else if ((start + 5 <= end) && pm_strncasecmp(start, (const uint8_t *) "false", 5) == 0) {
+ parser->frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED;
+ }
+}
+
+static inline bool
+pm_char_is_magic_comment_key_delimiter(const uint8_t b) {
+ return b == '\'' || b == '"' || b == ':' || b == ';';
+}
+
+/**
+ * Find an emacs magic comment marker (-*-) within the given bounds. If one is
+ * found, it returns a pointer to the start of the marker. Otherwise it returns
+ * NULL.
+ */
+static inline const uint8_t *
+parser_lex_magic_comment_emacs_marker(pm_parser_t *parser, const uint8_t *cursor, const uint8_t *end) {
+ while ((cursor + 3 <= end) && (cursor = pm_memchr(cursor, '-', (size_t) (end - cursor), parser->encoding_changed, parser->encoding)) != NULL) {
+ if (cursor + 3 <= end && cursor[1] == '*' && cursor[2] == '-') {
+ return cursor;
+ }
+ cursor++;
+ }
+ return NULL;
+}
+
+/**
+ * Parse the current token on the parser to see if it's a magic comment and
+ * potentially perform some action based on that. A regular expression that this
+ * function is effectively matching is:
+ *
+ * %r"([^\\s\'\":;]+)\\s*:\\s*(\"(?:\\\\.|[^\"])*\"|[^\"\\s;]+)[\\s;]*"
+ *
+ * It returns true if it consumes the entire comment. Otherwise it returns
+ * false.
+ */
+static inline bool
+parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) {
+ bool result = true;
+
+ const uint8_t *start = parser->current.start + 1;
+ const uint8_t *end = parser->current.end;
+ if (end - start <= 7) return false;
+
+ const uint8_t *cursor;
+ bool indicator = false;
+
+ if ((cursor = parser_lex_magic_comment_emacs_marker(parser, start, end)) != NULL) {
+ start = cursor + 3;
+
+ if ((cursor = parser_lex_magic_comment_emacs_marker(parser, start, end)) != NULL) {
+ end = cursor;
+ indicator = true;
+ } else {
+ // If we have a start marker but not an end marker, then we cannot
+ // have a magic comment.
+ return false;
+ }
+ }
+
+ cursor = start;
+ while (cursor < end) {
+ while (cursor < end && (pm_char_is_magic_comment_key_delimiter(*cursor) || pm_char_is_whitespace(*cursor))) cursor++;
+
+ const uint8_t *key_start = cursor;
+ while (cursor < end && (!pm_char_is_magic_comment_key_delimiter(*cursor) && !pm_char_is_whitespace(*cursor))) cursor++;
+
+ const uint8_t *key_end = cursor;
+ while (cursor < end && pm_char_is_whitespace(*cursor)) cursor++;
+ if (cursor == end) break;
+
+ if (*cursor == ':') {
+ cursor++;
+ } else {
+ if (!indicator) return false;
+ continue;
+ }
+
+ while (cursor < end && pm_char_is_whitespace(*cursor)) cursor++;
+ if (cursor == end) break;
+
+ const uint8_t *value_start;
+ const uint8_t *value_end;
+
+ if (*cursor == '"') {
+ value_start = ++cursor;
+ for (; cursor < end && *cursor != '"'; cursor++) {
+ if (*cursor == '\\' && (cursor + 1 < end)) cursor++;
+ }
+ value_end = cursor;
+ } else {
+ value_start = cursor;
+ while (cursor < end && *cursor != '"' && *cursor != ';' && !pm_char_is_whitespace(*cursor)) cursor++;
+ value_end = cursor;
+ }
+
+ if (indicator) {
+ while (cursor < end && (*cursor == ';' || pm_char_is_whitespace(*cursor))) cursor++;
+ } else {
+ while (cursor < end && pm_char_is_whitespace(*cursor)) cursor++;
+ if (cursor != end) return false;
+ }
+
+ // Here, we need to do some processing on the key to swap out dashes for
+ // underscores. We only need to do this if there _is_ a dash in the key.
+ pm_string_t key;
+ const size_t key_length = (size_t) (key_end - key_start);
+ const uint8_t *dash = pm_memchr(key_start, '-', (size_t) key_length, parser->encoding_changed, parser->encoding);
+
+ if (dash == NULL) {
+ pm_string_shared_init(&key, key_start, key_end);
+ } else {
+ size_t width = (size_t) (key_end - key_start);
+ uint8_t *buffer = xmalloc(width);
+ if (buffer == NULL) break;
+
+ memcpy(buffer, key_start, width);
+ buffer[dash - key_start] = '_';
+
+ while ((dash = pm_memchr(dash + 1, '-', (size_t) (key_end - dash - 1), parser->encoding_changed, parser->encoding)) != NULL) {
+ buffer[dash - key_start] = '_';
+ }
+
+ pm_string_owned_init(&key, buffer, width);
+ }
+
+ // Finally, we can start checking the key against the list of known
+ // magic comment keys, and potentially change state based on that.
+ const uint8_t *key_source = pm_string_source(&key);
+
+ // We only want to attempt to compare against encoding comments if it's
+ // the first line in the file (or the second in the case of a shebang).
+ if (parser->current.start == parser->encoding_comment_start) {
+ if (
+ (key_length == 8 && pm_strncasecmp(key_source, (const uint8_t *) "encoding", 8) == 0) ||
+ (key_length == 6 && pm_strncasecmp(key_source, (const uint8_t *) "coding", 6) == 0)
+ ) {
+ result = parser_lex_magic_comment_encoding_value(parser, value_start, value_end);
+ }
+ }
+
+ // We only want to handle frozen string literal comments if it's before
+ // any semantic tokens have been seen.
+ if (key_length == 21 && pm_strncasecmp(key_source, (const uint8_t *) "frozen_string_literal", 21) == 0) {
+ if (semantic_token_seen) {
+ pm_parser_warn_token(parser, &parser->current, PM_WARN_IGNORED_FROZEN_STRING_LITERAL);
+ } else {
+ parser_lex_magic_comment_frozen_string_literal_value(parser, value_start, value_end);
+ }
+ }
+
+ // If we have hit a ractor pragma, attempt to lex that.
+ uint32_t value_length = (uint32_t) (value_end - value_start);
+ if (key_length == 24 && pm_strncasecmp(key_source, (const uint8_t *) "shareable_constant_value", 24) == 0) {
+ if (value_length == 4 && pm_strncasecmp(value_start, (const uint8_t *) "none", 4) == 0) {
+ pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_NONE);
+ } else if (value_length == 7 && pm_strncasecmp(value_start, (const uint8_t *) "literal", 7) == 0) {
+ pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_LITERAL);
+ } else if (value_length == 23 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_everything", 23) == 0) {
+ pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_EVERYTHING);
+ } else if (value_length == 17 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_copy", 17) == 0) {
+ pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_COPY);
+ } else {
+ PM_PARSER_WARN_TOKEN_FORMAT(
+ parser,
+ parser->current,
+ PM_WARN_INVALID_SHAREABLE_CONSTANT_VALUE,
+ (int) value_length,
+ (const char *) value_start
+ );
+ }
+ }
+
+ // When we're done, we want to free the string in case we had to
+ // allocate memory for it.
+ pm_string_free(&key);
+
+ // Allocate a new magic comment node to append to the parser's list.
+ pm_magic_comment_t *magic_comment;
+ if ((magic_comment = (pm_magic_comment_t *) xcalloc(1, sizeof(pm_magic_comment_t))) != NULL) {
+ magic_comment->key_start = key_start;
+ magic_comment->value_start = value_start;
+ magic_comment->key_length = (uint32_t) key_length;
+ magic_comment->value_length = (uint32_t) (value_end - value_start);
+ pm_list_append(&parser->magic_comment_list, (pm_list_node_t *) magic_comment);
+ }
+ }
+
+ return result;
+}
+
+/******************************************************************************/
+/* Context manipulations */
+/******************************************************************************/
+
+static bool
+context_terminator(pm_context_t context, pm_token_t *token) {
+ switch (context) {
+ case PM_CONTEXT_MAIN:
+ case PM_CONTEXT_DEF_PARAMS:
+ case PM_CONTEXT_DEFINED:
+ case PM_CONTEXT_TERNARY:
+ case PM_CONTEXT_RESCUE_MODIFIER:
+ return token->type == PM_TOKEN_EOF;
+ case PM_CONTEXT_DEFAULT_PARAMS:
+ return token->type == PM_TOKEN_COMMA || token->type == PM_TOKEN_PARENTHESIS_RIGHT;
+ case PM_CONTEXT_PREEXE:
+ case PM_CONTEXT_POSTEXE:
+ return token->type == PM_TOKEN_BRACE_RIGHT;
+ case PM_CONTEXT_MODULE:
+ case PM_CONTEXT_CLASS:
+ case PM_CONTEXT_SCLASS:
+ case PM_CONTEXT_LAMBDA_DO_END:
+ case PM_CONTEXT_DEF:
+ case PM_CONTEXT_BLOCK_KEYWORDS:
+ return token->type == PM_TOKEN_KEYWORD_END || token->type == PM_TOKEN_KEYWORD_RESCUE || token->type == PM_TOKEN_KEYWORD_ENSURE;
+ case PM_CONTEXT_WHILE:
+ case PM_CONTEXT_UNTIL:
+ case PM_CONTEXT_ELSE:
+ case PM_CONTEXT_FOR:
+ case PM_CONTEXT_BEGIN_ENSURE:
+ case PM_CONTEXT_BLOCK_ENSURE:
+ case PM_CONTEXT_CLASS_ENSURE:
+ case PM_CONTEXT_DEF_ENSURE:
+ case PM_CONTEXT_LAMBDA_ENSURE:
+ case PM_CONTEXT_MODULE_ENSURE:
+ case PM_CONTEXT_SCLASS_ENSURE:
+ return token->type == PM_TOKEN_KEYWORD_END;
+ case PM_CONTEXT_FOR_INDEX:
+ return token->type == PM_TOKEN_KEYWORD_IN;
+ case PM_CONTEXT_CASE_WHEN:
+ return token->type == PM_TOKEN_KEYWORD_WHEN || token->type == PM_TOKEN_KEYWORD_END || token->type == PM_TOKEN_KEYWORD_ELSE;
+ case PM_CONTEXT_CASE_IN:
+ return token->type == PM_TOKEN_KEYWORD_IN || token->type == PM_TOKEN_KEYWORD_END || token->type == PM_TOKEN_KEYWORD_ELSE;
+ case PM_CONTEXT_IF:
+ case PM_CONTEXT_ELSIF:
+ return token->type == PM_TOKEN_KEYWORD_ELSE || token->type == PM_TOKEN_KEYWORD_ELSIF || token->type == PM_TOKEN_KEYWORD_END;
+ case PM_CONTEXT_UNLESS:
+ return token->type == PM_TOKEN_KEYWORD_ELSE || token->type == PM_TOKEN_KEYWORD_END;
+ case PM_CONTEXT_EMBEXPR:
+ return token->type == PM_TOKEN_EMBEXPR_END;
+ case PM_CONTEXT_BLOCK_BRACES:
+ return token->type == PM_TOKEN_BRACE_RIGHT;
+ case PM_CONTEXT_PARENS:
+ return token->type == PM_TOKEN_PARENTHESIS_RIGHT;
+ case PM_CONTEXT_BEGIN:
+ case PM_CONTEXT_BEGIN_RESCUE:
+ case PM_CONTEXT_BLOCK_RESCUE:
+ case PM_CONTEXT_CLASS_RESCUE:
+ case PM_CONTEXT_DEF_RESCUE:
+ case PM_CONTEXT_LAMBDA_RESCUE:
+ case PM_CONTEXT_MODULE_RESCUE:
+ case PM_CONTEXT_SCLASS_RESCUE:
+ return token->type == PM_TOKEN_KEYWORD_ENSURE || token->type == PM_TOKEN_KEYWORD_RESCUE || token->type == PM_TOKEN_KEYWORD_ELSE || token->type == PM_TOKEN_KEYWORD_END;
+ case PM_CONTEXT_BEGIN_ELSE:
+ case PM_CONTEXT_BLOCK_ELSE:
+ case PM_CONTEXT_CLASS_ELSE:
+ case PM_CONTEXT_DEF_ELSE:
+ case PM_CONTEXT_LAMBDA_ELSE:
+ case PM_CONTEXT_MODULE_ELSE:
+ case PM_CONTEXT_SCLASS_ELSE:
+ return token->type == PM_TOKEN_KEYWORD_ENSURE || token->type == PM_TOKEN_KEYWORD_END;
+ case PM_CONTEXT_LAMBDA_BRACES:
+ return token->type == PM_TOKEN_BRACE_RIGHT;
+ case PM_CONTEXT_PREDICATE:
+ return token->type == PM_TOKEN_KEYWORD_THEN || token->type == PM_TOKEN_NEWLINE || token->type == PM_TOKEN_SEMICOLON;
+ case PM_CONTEXT_NONE:
+ return false;
+ }
+
+ return false;
+}
+
+/**
+ * Returns the context that the given token is found to be terminating, or
+ * returns PM_CONTEXT_NONE.
+ */
+static pm_context_t
+context_recoverable(const pm_parser_t *parser, pm_token_t *token) {
+ pm_context_node_t *context_node = parser->current_context;
+
+ while (context_node != NULL) {
+ if (context_terminator(context_node->context, token)) return context_node->context;
+ context_node = context_node->prev;
+ }
+
+ return PM_CONTEXT_NONE;
+}
+
+static bool
+context_push(pm_parser_t *parser, pm_context_t context) {
+ pm_context_node_t *context_node = (pm_context_node_t *) xmalloc(sizeof(pm_context_node_t));
+ if (context_node == NULL) return false;
+
+ *context_node = (pm_context_node_t) { .context = context, .prev = NULL };
+
+ if (parser->current_context == NULL) {
+ parser->current_context = context_node;
+ } else {
+ context_node->prev = parser->current_context;
+ parser->current_context = context_node;
+ }
+
+ return true;
+}
+
+static void
+context_pop(pm_parser_t *parser) {
+ pm_context_node_t *prev = parser->current_context->prev;
+ xfree(parser->current_context);
+ parser->current_context = prev;
+}
+
+static bool
+context_p(const pm_parser_t *parser, pm_context_t context) {
+ pm_context_node_t *context_node = parser->current_context;
+
+ while (context_node != NULL) {
+ if (context_node->context == context) return true;
+ context_node = context_node->prev;
+ }
+
+ return false;
+}
+
+static bool
+context_def_p(const pm_parser_t *parser) {
+ pm_context_node_t *context_node = parser->current_context;
+
+ while (context_node != NULL) {
+ switch (context_node->context) {
+ case PM_CONTEXT_DEF:
+ case PM_CONTEXT_DEF_PARAMS:
+ case PM_CONTEXT_DEF_ENSURE:
+ case PM_CONTEXT_DEF_RESCUE:
+ case PM_CONTEXT_DEF_ELSE:
+ return true;
+ case PM_CONTEXT_CLASS:
+ case PM_CONTEXT_CLASS_ENSURE:
+ case PM_CONTEXT_CLASS_RESCUE:
+ case PM_CONTEXT_CLASS_ELSE:
+ case PM_CONTEXT_MODULE:
+ case PM_CONTEXT_MODULE_ENSURE:
+ case PM_CONTEXT_MODULE_RESCUE:
+ case PM_CONTEXT_MODULE_ELSE:
+ case PM_CONTEXT_SCLASS:
+ case PM_CONTEXT_SCLASS_ENSURE:
+ case PM_CONTEXT_SCLASS_RESCUE:
+ case PM_CONTEXT_SCLASS_ELSE:
+ return false;
+ default:
+ context_node = context_node->prev;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Returns a human readable string for the given context, used in error
+ * messages.
+ */
+static const char *
+context_human(pm_context_t context) {
+ switch (context) {
+ case PM_CONTEXT_NONE:
+ assert(false && "unreachable");
+ return "";
+ case PM_CONTEXT_BEGIN: return "begin statement";
+ case PM_CONTEXT_BLOCK_BRACES: return "'{'..'}' block";
+ case PM_CONTEXT_BLOCK_KEYWORDS: return "'do'..'end' block";
+ case PM_CONTEXT_CASE_WHEN: return "'when' clause";
+ case PM_CONTEXT_CASE_IN: return "'in' clause";
+ case PM_CONTEXT_CLASS: return "class definition";
+ case PM_CONTEXT_DEF: return "method definition";
+ case PM_CONTEXT_DEF_PARAMS: return "method parameters";
+ case PM_CONTEXT_DEFAULT_PARAMS: return "parameter default value";
+ case PM_CONTEXT_DEFINED: return "'defined?' expression";
+ case PM_CONTEXT_ELSE:
+ case PM_CONTEXT_BEGIN_ELSE:
+ case PM_CONTEXT_BLOCK_ELSE:
+ case PM_CONTEXT_CLASS_ELSE:
+ case PM_CONTEXT_DEF_ELSE:
+ case PM_CONTEXT_LAMBDA_ELSE:
+ case PM_CONTEXT_MODULE_ELSE:
+ case PM_CONTEXT_SCLASS_ELSE: return "'else' clause";
+ case PM_CONTEXT_ELSIF: return "'elsif' clause";
+ case PM_CONTEXT_EMBEXPR: return "embedded expression";
+ case PM_CONTEXT_BEGIN_ENSURE:
+ case PM_CONTEXT_BLOCK_ENSURE:
+ case PM_CONTEXT_CLASS_ENSURE:
+ case PM_CONTEXT_DEF_ENSURE:
+ case PM_CONTEXT_LAMBDA_ENSURE:
+ case PM_CONTEXT_MODULE_ENSURE:
+ case PM_CONTEXT_SCLASS_ENSURE: return "'ensure' clause";
+ case PM_CONTEXT_FOR: return "for loop";
+ case PM_CONTEXT_FOR_INDEX: return "for loop index";
+ case PM_CONTEXT_IF: return "if statement";
+ case PM_CONTEXT_LAMBDA_BRACES: return "'{'..'}' lambda block";
+ case PM_CONTEXT_LAMBDA_DO_END: return "'do'..'end' lambda block";
+ case PM_CONTEXT_MAIN: return "top level context";
+ case PM_CONTEXT_MODULE: return "module definition";
+ case PM_CONTEXT_PARENS: return "parentheses";
+ case PM_CONTEXT_POSTEXE: return "'END' block";
+ case PM_CONTEXT_PREDICATE: return "predicate";
+ case PM_CONTEXT_PREEXE: return "'BEGIN' block";
+ case PM_CONTEXT_BEGIN_RESCUE:
+ case PM_CONTEXT_BLOCK_RESCUE:
+ case PM_CONTEXT_CLASS_RESCUE:
+ case PM_CONTEXT_DEF_RESCUE:
+ case PM_CONTEXT_LAMBDA_RESCUE:
+ case PM_CONTEXT_MODULE_RESCUE:
+ case PM_CONTEXT_RESCUE_MODIFIER:
+ case PM_CONTEXT_SCLASS_RESCUE: return "'rescue' clause";
+ case PM_CONTEXT_SCLASS: return "singleton class definition";
+ case PM_CONTEXT_TERNARY: return "ternary expression";
+ case PM_CONTEXT_UNLESS: return "unless statement";
+ case PM_CONTEXT_UNTIL: return "until statement";
+ case PM_CONTEXT_WHILE: return "while statement";
+ }
+
+ assert(false && "unreachable");
+ return "";
+}
+
+/******************************************************************************/
+/* Specific token lexers */
+/******************************************************************************/
+
+static void
+pm_strspn_number_validate(pm_parser_t *parser, const uint8_t *invalid) {
+ if (invalid != NULL) {
+ pm_parser_err(parser, invalid, invalid + 1, PM_ERR_INVALID_NUMBER_UNDERSCORE);
+ }
+}
+
+static size_t
+pm_strspn_binary_number_validate(pm_parser_t *parser, const uint8_t *string) {
+ const uint8_t *invalid = NULL;
+ size_t length = pm_strspn_binary_number(string, parser->end - string, &invalid);
+ pm_strspn_number_validate(parser, invalid);
+ return length;
+}
+
+static size_t
+pm_strspn_octal_number_validate(pm_parser_t *parser, const uint8_t *string) {
+ const uint8_t *invalid = NULL;
+ size_t length = pm_strspn_octal_number(string, parser->end - string, &invalid);
+ pm_strspn_number_validate(parser, invalid);
+ return length;
+}
+
+static size_t
+pm_strspn_decimal_number_validate(pm_parser_t *parser, const uint8_t *string) {
+ const uint8_t *invalid = NULL;
+ size_t length = pm_strspn_decimal_number(string, parser->end - string, &invalid);
+ pm_strspn_number_validate(parser, invalid);
+ return length;
+}
+
+static size_t
+pm_strspn_hexadecimal_number_validate(pm_parser_t *parser, const uint8_t *string) {
+ const uint8_t *invalid = NULL;
+ size_t length = pm_strspn_hexadecimal_number(string, parser->end - string, &invalid);
+ pm_strspn_number_validate(parser, invalid);
+ return length;
+}
+
+static pm_token_type_t
+lex_optional_float_suffix(pm_parser_t *parser, bool* seen_e) {
+ pm_token_type_t type = PM_TOKEN_INTEGER;
+
+ // Here we're going to attempt to parse the optional decimal portion of a
+ // float. If it's not there, then it's okay and we'll just continue on.
+ if (peek(parser) == '.') {
+ if (pm_char_is_decimal_digit(peek_offset(parser, 1))) {
+ parser->current.end += 2;
+ parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
+ type = PM_TOKEN_FLOAT;
+ } else {
+ // If we had a . and then something else, then it's not a float
+ // suffix on a number it's a method call or something else.
+ return type;
+ }
+ }
+
+ // Here we're going to attempt to parse the optional exponent portion of a
+ // float. If it's not there, it's okay and we'll just continue on.
+ if ((peek(parser) == 'e') || (peek(parser) == 'E')) {
+ if ((peek_offset(parser, 1) == '+') || (peek_offset(parser, 1) == '-')) {
+ parser->current.end += 2;
+
+ if (pm_char_is_decimal_digit(peek(parser))) {
+ parser->current.end++;
+ parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
+ } else {
+ pm_parser_err_current(parser, PM_ERR_INVALID_FLOAT_EXPONENT);
+ }
+ } else if (pm_char_is_decimal_digit(peek_offset(parser, 1))) {
+ parser->current.end++;
+ parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
+ } else {
+ return type;
+ }
+
+ *seen_e = true;
+ type = PM_TOKEN_FLOAT;
+ }
+
+ return type;
+}
+
+static pm_token_type_t
+lex_numeric_prefix(pm_parser_t *parser, bool* seen_e) {
+ pm_token_type_t type = PM_TOKEN_INTEGER;
+ *seen_e = false;
+
+ if (peek_offset(parser, -1) == '0') {
+ switch (*parser->current.end) {
+ // 0d1111 is a decimal number
+ case 'd':
+ case 'D':
+ parser->current.end++;
+ if (pm_char_is_decimal_digit(peek(parser))) {
+ parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
+ } else {
+ match(parser, '_');
+ pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_DECIMAL);
+ }
+
+ break;
+
+ // 0b1111 is a binary number
+ case 'b':
+ case 'B':
+ parser->current.end++;
+ if (pm_char_is_binary_digit(peek(parser))) {
+ parser->current.end += pm_strspn_binary_number_validate(parser, parser->current.end);
+ } else {
+ match(parser, '_');
+ pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_BINARY);
+ }
+
+ parser->integer_base = PM_INTEGER_BASE_FLAGS_BINARY;
+ break;
+
+ // 0o1111 is an octal number
+ case 'o':
+ case 'O':
+ parser->current.end++;
+ if (pm_char_is_octal_digit(peek(parser))) {
+ parser->current.end += pm_strspn_octal_number_validate(parser, parser->current.end);
+ } else {
+ match(parser, '_');
+ pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_OCTAL);
+ }
+
+ parser->integer_base = PM_INTEGER_BASE_FLAGS_OCTAL;
+ break;
+
+ // 01111 is an octal number
+ case '_':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ parser->current.end += pm_strspn_octal_number_validate(parser, parser->current.end);
+ parser->integer_base = PM_INTEGER_BASE_FLAGS_OCTAL;
+ break;
+
+ // 0x1111 is a hexadecimal number
+ case 'x':
+ case 'X':
+ parser->current.end++;
+ if (pm_char_is_hexadecimal_digit(peek(parser))) {
+ parser->current.end += pm_strspn_hexadecimal_number_validate(parser, parser->current.end);
+ } else {
+ match(parser, '_');
+ pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_HEXADECIMAL);
+ }
+
+ parser->integer_base = PM_INTEGER_BASE_FLAGS_HEXADECIMAL;
+ break;
+
+ // 0.xxx is a float
+ case '.': {
+ type = lex_optional_float_suffix(parser, seen_e);
+ break;
+ }
+
+ // 0exxx is a float
+ case 'e':
+ case 'E': {
+ type = lex_optional_float_suffix(parser, seen_e);
+ break;
+ }
+ }
+ } else {
+ // If it didn't start with a 0, then we'll lex as far as we can into a
+ // decimal number.
+ parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
+
+ // Afterward, we'll lex as far as we can into an optional float suffix.
+ type = lex_optional_float_suffix(parser, seen_e);
+ }
+
+ return type;
+}
+
+static pm_token_type_t
+lex_numeric(pm_parser_t *parser) {
+ pm_token_type_t type = PM_TOKEN_INTEGER;
+ parser->integer_base = PM_INTEGER_BASE_FLAGS_DECIMAL;
+
+ if (parser->current.end < parser->end) {
+ bool seen_e = false;
+ type = lex_numeric_prefix(parser, &seen_e);
+
+ const uint8_t *end = parser->current.end;
+ pm_token_type_t suffix_type = type;
+
+ if (type == PM_TOKEN_INTEGER) {
+ if (match(parser, 'r')) {
+ suffix_type = PM_TOKEN_INTEGER_RATIONAL;
+
+ if (match(parser, 'i')) {
+ suffix_type = PM_TOKEN_INTEGER_RATIONAL_IMAGINARY;
+ }
+ } else if (match(parser, 'i')) {
+ suffix_type = PM_TOKEN_INTEGER_IMAGINARY;
+ }
+ } else {
+ if (!seen_e && match(parser, 'r')) {
+ suffix_type = PM_TOKEN_FLOAT_RATIONAL;
+
+ if (match(parser, 'i')) {
+ suffix_type = PM_TOKEN_FLOAT_RATIONAL_IMAGINARY;
+ }
+ } else if (match(parser, 'i')) {
+ suffix_type = PM_TOKEN_FLOAT_IMAGINARY;
+ }
+ }
+
+ const uint8_t b = peek(parser);
+ if (b != '\0' && (b >= 0x80 || ((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z')) || b == '_')) {
+ parser->current.end = end;
+ } else {
+ type = suffix_type;
+ }
+ }
+
+ return type;
+}
+
+static pm_token_type_t
+lex_global_variable(pm_parser_t *parser) {
+ if (parser->current.end >= parser->end) {
+ pm_parser_err_token(parser, &parser->current, PM_ERR_GLOBAL_VARIABLE_BARE);
+ return PM_TOKEN_GLOBAL_VARIABLE;
+ }
+
+ switch (*parser->current.end) {
+ 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
+ parser->current.end++;
+ return PM_TOKEN_GLOBAL_VARIABLE;
+
+ case '&': // $&: last match
+ case '`': // $`: string before last match
+ case '\'': // $': string after last match
+ case '+': // $+: string matches last paren.
+ parser->current.end++;
+ return lex_state_p(parser, PM_LEX_STATE_FNAME) ? PM_TOKEN_GLOBAL_VARIABLE : PM_TOKEN_BACK_REFERENCE;
+
+ case '0': {
+ parser->current.end++;
+ size_t width;
+
+ if (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0) {
+ do {
+ parser->current.end += width;
+ } while (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0);
+
+ // $0 isn't allowed to be followed by anything.
+ pm_diagnostic_id_t diag_id = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3_0 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3_0 : PM_ERR_INVALID_VARIABLE_GLOBAL;
+ PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->current, diag_id);
+ }
+
+ return PM_TOKEN_GLOBAL_VARIABLE;
+ }
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ parser->current.end += pm_strspn_decimal_digit(parser->current.end, parser->end - parser->current.end);
+ return lex_state_p(parser, PM_LEX_STATE_FNAME) ? PM_TOKEN_GLOBAL_VARIABLE : PM_TOKEN_NUMBERED_REFERENCE;
+
+ case '-':
+ parser->current.end++;
+ /* fallthrough */
+ default: {
+ size_t width;
+
+ if ((width = char_is_identifier(parser, parser->current.end)) > 0) {
+ do {
+ parser->current.end += width;
+ } while (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0);
+ } else if (pm_char_is_whitespace(peek(parser))) {
+ // If we get here, then we have a $ followed by whitespace,
+ // which is not allowed.
+ pm_parser_err_token(parser, &parser->current, PM_ERR_GLOBAL_VARIABLE_BARE);
+ } else {
+ // If we get here, then we have a $ followed by something that
+ // isn't recognized as a global variable.
+ pm_diagnostic_id_t diag_id = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3_0 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3_0 : PM_ERR_INVALID_VARIABLE_GLOBAL;
+ size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, diag_id, (int) ((parser->current.end + width) - parser->current.start), (const char *) parser->current.start);
+ }
+
+ return PM_TOKEN_GLOBAL_VARIABLE;
+ }
+ }
+}
+
+/**
+ * This function checks if the current token matches a keyword. If it does, it
+ * returns the token type. Otherwise, it returns PM_TOKEN_EOF. The arguments are as follows:
+ *
+ * * `parser` - the parser object
+ * * `current_start` - pointer to the start of the current token
+ * * `value` - the literal string that we're checking for
+ * * `vlen` - the length of the token
+ * * `state` - the state that we should transition to if the token matches
+ * * `type` - the expected token type
+ * * `modifier_type` - the expected modifier token type
+ */
+static inline pm_token_type_t
+lex_keyword(pm_parser_t *parser, const uint8_t *current_start, const char *value, size_t vlen, pm_lex_state_t state, pm_token_type_t type, pm_token_type_t modifier_type) {
+ if (memcmp(current_start, value, vlen) == 0) {
+ pm_lex_state_t last_state = parser->lex_state;
+
+ if (parser->lex_state & PM_LEX_STATE_FNAME) {
+ lex_state_set(parser, PM_LEX_STATE_ENDFN);
+ } else {
+ lex_state_set(parser, state);
+ if (state == PM_LEX_STATE_BEG) {
+ parser->command_start = true;
+ }
+
+ if ((modifier_type != PM_TOKEN_EOF) && !(last_state & (PM_LEX_STATE_BEG | PM_LEX_STATE_LABELED | PM_LEX_STATE_CLASS))) {
+ lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
+ return modifier_type;
+ }
+ }
+
+ return type;
+ }
+
+ return PM_TOKEN_EOF;
+}
+
+static pm_token_type_t
+lex_identifier(pm_parser_t *parser, bool previous_command_start) {
+ // Lex as far as we can into the current identifier.
+ size_t width;
+ const uint8_t *end = parser->end;
+ const uint8_t *current_start = parser->current.start;
+ const uint8_t *current_end = parser->current.end;
+ bool encoding_changed = parser->encoding_changed;
+
+ if (encoding_changed) {
+ while (current_end < end && (width = char_is_identifier(parser, current_end)) > 0) {
+ current_end += width;
+ }
+ } else {
+ while (current_end < end && (width = char_is_identifier_utf8(current_end, end)) > 0) {
+ current_end += width;
+ }
+ }
+ parser->current.end = current_end;
+
+ // Now cache the length of the identifier so that we can quickly compare it
+ // against known keywords.
+ width = (size_t) (current_end - current_start);
+
+ if (current_end < end) {
+ if (((current_end + 1 >= end) || (current_end[1] != '=')) && (match(parser, '!') || match(parser, '?'))) {
+ // First we'll attempt to extend the identifier by a ! or ?. Then we'll
+ // check if we're returning the defined? keyword or just an identifier.
+ width++;
+
+ if (
+ ((lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) &&
+ (peek(parser) == ':') && (peek_offset(parser, 1) != ':')
+ ) {
+ // If we're in a position where we can accept a : at the end of an
+ // identifier, then we'll optionally accept it.
+ lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
+ (void) match(parser, ':');
+ return PM_TOKEN_LABEL;
+ }
+
+ if (parser->lex_state != PM_LEX_STATE_DOT) {
+ if (width == 8 && (lex_keyword(parser, current_start, "defined?", width, PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_DEFINED, PM_TOKEN_EOF) != PM_TOKEN_EOF)) {
+ return PM_TOKEN_KEYWORD_DEFINED;
+ }
+ }
+
+ return PM_TOKEN_METHOD_NAME;
+ }
+
+ if (lex_state_p(parser, PM_LEX_STATE_FNAME) && peek_offset(parser, 1) != '~' && peek_offset(parser, 1) != '>' && (peek_offset(parser, 1) != '=' || peek_offset(parser, 2) == '>') && match(parser, '=')) {
+ // If we're in a position where we can accept a = at the end of an
+ // identifier, then we'll optionally accept it.
+ return PM_TOKEN_IDENTIFIER;
+ }
+
+ if (
+ ((lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) &&
+ peek(parser) == ':' && peek_offset(parser, 1) != ':'
+ ) {
+ // If we're in a position where we can accept a : at the end of an
+ // identifier, then we'll optionally accept it.
+ lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
+ (void) match(parser, ':');
+ return PM_TOKEN_LABEL;
+ }
+ }
+
+ if (parser->lex_state != PM_LEX_STATE_DOT) {
+ pm_token_type_t type;
+ switch (width) {
+ case 2:
+ if (lex_keyword(parser, current_start, "do", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_DO, PM_TOKEN_EOF) != PM_TOKEN_EOF) {
+ if (pm_do_loop_stack_p(parser)) {
+ return PM_TOKEN_KEYWORD_DO_LOOP;
+ }
+ return PM_TOKEN_KEYWORD_DO;
+ }
+
+ if ((type = lex_keyword(parser, current_start, "if", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_IF, PM_TOKEN_KEYWORD_IF_MODIFIER)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "in", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_IN, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "or", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_OR, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ break;
+ case 3:
+ if ((type = lex_keyword(parser, current_start, "and", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_AND, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "def", width, PM_LEX_STATE_FNAME, PM_TOKEN_KEYWORD_DEF, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "end", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_END, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "END", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_END_UPCASE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "for", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_FOR, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "nil", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_NIL, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "not", width, PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_NOT, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ break;
+ case 4:
+ if ((type = lex_keyword(parser, current_start, "case", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_CASE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "else", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "next", width, PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_NEXT, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "redo", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_REDO, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "self", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_SELF, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "then", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "true", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_TRUE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "when", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ break;
+ case 5:
+ if ((type = lex_keyword(parser, current_start, "alias", width, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM, PM_TOKEN_KEYWORD_ALIAS, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "begin", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_BEGIN, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "BEGIN", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_BEGIN_UPCASE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "break", width, PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_BREAK, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "class", width, PM_LEX_STATE_CLASS, PM_TOKEN_KEYWORD_CLASS, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "elsif", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ELSIF, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "false", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_FALSE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "retry", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_RETRY, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "super", width, PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_SUPER, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "undef", width, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM, PM_TOKEN_KEYWORD_UNDEF, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "until", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_UNTIL, PM_TOKEN_KEYWORD_UNTIL_MODIFIER)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "while", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_WHILE, PM_TOKEN_KEYWORD_WHILE_MODIFIER)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "yield", width, PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_YIELD, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ break;
+ case 6:
+ if ((type = lex_keyword(parser, current_start, "ensure", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "module", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_MODULE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "rescue", width, PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "return", width, PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_RETURN, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "unless", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_UNLESS, PM_TOKEN_KEYWORD_UNLESS_MODIFIER)) != PM_TOKEN_EOF) return type;
+ break;
+ case 8:
+ if ((type = lex_keyword(parser, current_start, "__LINE__", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD___LINE__, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ if ((type = lex_keyword(parser, current_start, "__FILE__", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD___FILE__, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ break;
+ case 12:
+ if ((type = lex_keyword(parser, current_start, "__ENCODING__", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD___ENCODING__, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
+ break;
+ }
+ }
+
+ if (encoding_changed) {
+ return parser->encoding->isupper_char(current_start, end - current_start) ? PM_TOKEN_CONSTANT : PM_TOKEN_IDENTIFIER;
+ }
+ return pm_encoding_utf_8_isupper_char(current_start, end - current_start) ? PM_TOKEN_CONSTANT : PM_TOKEN_IDENTIFIER;
+}
+
+/**
+ * Returns true if the current token that the parser is considering is at the
+ * beginning of a line or the beginning of the source.
+ */
+static bool
+current_token_starts_line(pm_parser_t *parser) {
+ return (parser->current.start == parser->start) || (parser->current.start[-1] == '\n');
+}
+
+/**
+ * When we hit a # while lexing something like a string, we need to potentially
+ * handle interpolation. This function performs that check. It returns a token
+ * type representing what it found. Those cases are:
+ *
+ * * PM_TOKEN_NOT_PROVIDED - No interpolation was found at this point. The
+ * caller should keep lexing.
+ * * PM_TOKEN_STRING_CONTENT - No interpolation was found at this point. The
+ * caller should return this token type.
+ * * PM_TOKEN_EMBEXPR_BEGIN - An embedded expression was found. The caller
+ * should return this token type.
+ * * PM_TOKEN_EMBVAR - An embedded variable was found. The caller should return
+ * this token type.
+ */
+static pm_token_type_t
+lex_interpolation(pm_parser_t *parser, const uint8_t *pound) {
+ // If there is no content following this #, then we're at the end of
+ // the string and we can safely return string content.
+ if (pound + 1 >= parser->end) {
+ parser->current.end = pound + 1;
+ return PM_TOKEN_STRING_CONTENT;
+ }
+
+ // Now we'll check against the character that follows the #. If it constitutes
+ // valid interplation, we'll handle that, otherwise we'll return
+ // PM_TOKEN_NOT_PROVIDED.
+ switch (pound[1]) {
+ case '@': {
+ // In this case we may have hit an embedded instance or class variable.
+ if (pound + 2 >= parser->end) {
+ parser->current.end = pound + 1;
+ return PM_TOKEN_STRING_CONTENT;
+ }
+
+ // If we're looking at a @ and there's another @, then we'll skip past the
+ // second @.
+ const uint8_t *variable = pound + 2;
+ if (*variable == '@' && pound + 3 < parser->end) variable++;
+
+ if (char_is_identifier_start(parser, variable)) {
+ // At this point we're sure that we've either hit an embedded instance
+ // or class variable. In this case we'll first need to check if we've
+ // already consumed content.
+ if (pound > parser->current.start) {
+ parser->current.end = pound;
+ return PM_TOKEN_STRING_CONTENT;
+ }
+
+ // Otherwise we need to return the embedded variable token
+ // and then switch to the embedded variable lex mode.
+ lex_mode_push(parser, (pm_lex_mode_t) { .mode = PM_LEX_EMBVAR });
+ parser->current.end = pound + 1;
+ return PM_TOKEN_EMBVAR;
+ }
+
+ // If we didn't get a valid interpolation, then this is just regular
+ // string content. This is like if we get "#@-". In this case the caller
+ // should keep lexing.
+ parser->current.end = pound + 1;
+ return PM_TOKEN_NOT_PROVIDED;
+ }
+ case '$':
+ // In this case we may have hit an embedded global variable. If there's
+ // not enough room, then we'll just return string content.
+ if (pound + 2 >= parser->end) {
+ parser->current.end = pound + 1;
+ return PM_TOKEN_STRING_CONTENT;
+ }
+
+ // This is the character that we're going to check to see if it is the
+ // start of an identifier that would indicate that this is a global
+ // variable.
+ const uint8_t *check = pound + 2;
+
+ if (pound[2] == '-') {
+ if (pound + 3 >= parser->end) {
+ parser->current.end = pound + 2;
+ return PM_TOKEN_STRING_CONTENT;
+ }
+
+ check++;
+ }
+
+ // If the character that we're going to check is the start of an
+ // identifier, or we don't have a - and the character is a decimal number
+ // or a global name punctuation character, then we've hit an embedded
+ // global variable.
+ if (
+ char_is_identifier_start(parser, check) ||
+ (pound[2] != '-' && (pm_char_is_decimal_digit(pound[2]) || char_is_global_name_punctuation(pound[2])))
+ ) {
+ // In this case we've hit an embedded global variable. First check to
+ // see if we've already consumed content. If we have, then we need to
+ // return that content as string content first.
+ if (pound > parser->current.start) {
+ parser->current.end = pound;
+ return PM_TOKEN_STRING_CONTENT;
+ }
+
+ // Otherwise, we need to return the embedded variable token and switch
+ // to the embedded variable lex mode.
+ lex_mode_push(parser, (pm_lex_mode_t) { .mode = PM_LEX_EMBVAR });
+ parser->current.end = pound + 1;
+ return PM_TOKEN_EMBVAR;
+ }
+
+ // In this case we've hit a #$ that does not indicate a global variable.
+ // In this case we'll continue lexing past it.
+ parser->current.end = pound + 1;
+ return PM_TOKEN_NOT_PROVIDED;
+ case '{':
+ // In this case it's the start of an embedded expression. If we have
+ // already consumed content, then we need to return that content as string
+ // content first.
+ if (pound > parser->current.start) {
+ parser->current.end = pound;
+ return PM_TOKEN_STRING_CONTENT;
+ }
+
+ parser->enclosure_nesting++;
+
+ // Otherwise we'll skip past the #{ and begin lexing the embedded
+ // expression.
+ lex_mode_push(parser, (pm_lex_mode_t) { .mode = PM_LEX_EMBEXPR });
+ parser->current.end = pound + 2;
+ parser->command_start = true;
+ pm_do_loop_stack_push(parser, false);
+ return PM_TOKEN_EMBEXPR_BEGIN;
+ default:
+ // In this case we've hit a # that doesn't constitute interpolation. We'll
+ // mark that by returning the not provided token type. This tells the
+ // consumer to keep lexing forward.
+ parser->current.end = pound + 1;
+ return PM_TOKEN_NOT_PROVIDED;
+ }
+}
+
+static const uint8_t PM_ESCAPE_FLAG_NONE = 0x0;
+static const uint8_t PM_ESCAPE_FLAG_CONTROL = 0x1;
+static const uint8_t PM_ESCAPE_FLAG_META = 0x2;
+static const uint8_t PM_ESCAPE_FLAG_SINGLE = 0x4;
+static const uint8_t PM_ESCAPE_FLAG_REGEXP = 0x8;
+
+/**
+ * This is a lookup table for whether or not an ASCII character is printable.
+ */
+static const bool ascii_printable_chars[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0
+};
+
+static inline bool
+char_is_ascii_printable(const uint8_t b) {
+ return (b < 0x80) && ascii_printable_chars[b];
+}
+
+/**
+ * Return the value that a hexadecimal digit character represents. For example,
+ * transform 'a' into 10, 'b' into 11, etc.
+ */
+static inline uint8_t
+escape_hexadecimal_digit(const uint8_t value) {
+ return (uint8_t) ((value <= '9') ? (value - '0') : (value & 0x7) + 9);
+}
+
+/**
+ * Scan the 4 digits of a Unicode escape into the value. Returns the number of
+ * digits scanned. This function assumes that the characters have already been
+ * validated.
+ */
+static inline uint32_t
+escape_unicode(const uint8_t *string, size_t length) {
+ uint32_t value = 0;
+ for (size_t index = 0; index < length; index++) {
+ if (index != 0) value <<= 4;
+ value |= escape_hexadecimal_digit(string[index]);
+ }
+ return value;
+}
+
+/**
+ * Escape a single character value based on the given flags.
+ */
+static inline uint8_t
+escape_byte(uint8_t value, const uint8_t flags) {
+ if (flags & PM_ESCAPE_FLAG_CONTROL) value &= 0x1f;
+ if (flags & PM_ESCAPE_FLAG_META) value |= 0x80;
+ return value;
+}
+
+/**
+ * Write a unicode codepoint to the given buffer.
+ */
+static inline void
+escape_write_unicode(pm_parser_t *parser, pm_buffer_t *buffer, const uint8_t flags, const uint8_t *start, const uint8_t *end, uint32_t value) {
+ // \u escape sequences in string-like structures implicitly change the
+ // encoding to UTF-8 if they are >= 0x80 or if they are used in a character
+ // literal.
+ if (value >= 0x80 || flags & PM_ESCAPE_FLAG_SINGLE) {
+ if (parser->explicit_encoding != NULL && parser->explicit_encoding != PM_ENCODING_UTF_8_ENTRY) {
+ PM_PARSER_ERR_FORMAT(parser, start, end, PM_ERR_MIXED_ENCODING, parser->explicit_encoding->name);
+ }
+
+ parser->explicit_encoding = PM_ENCODING_UTF_8_ENTRY;
+ }
+
+ if (value <= 0x7F) { // 0xxxxxxx
+ pm_buffer_append_byte(buffer, (uint8_t) value);
+ } else if (value <= 0x7FF) { // 110xxxxx 10xxxxxx
+ pm_buffer_append_byte(buffer, (uint8_t) (0xC0 | (value >> 6)));
+ pm_buffer_append_byte(buffer, (uint8_t) (0x80 | (value & 0x3F)));
+ } else if (value <= 0xFFFF) { // 1110xxxx 10xxxxxx 10xxxxxx
+ pm_buffer_append_byte(buffer, (uint8_t) (0xE0 | (value >> 12)));
+ pm_buffer_append_byte(buffer, (uint8_t) (0x80 | ((value >> 6) & 0x3F)));
+ pm_buffer_append_byte(buffer, (uint8_t) (0x80 | (value & 0x3F)));
+ } else if (value <= 0x10FFFF) { // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ pm_buffer_append_byte(buffer, (uint8_t) (0xF0 | (value >> 18)));
+ pm_buffer_append_byte(buffer, (uint8_t) (0x80 | ((value >> 12) & 0x3F)));
+ pm_buffer_append_byte(buffer, (uint8_t) (0x80 | ((value >> 6) & 0x3F)));
+ pm_buffer_append_byte(buffer, (uint8_t) (0x80 | (value & 0x3F)));
+ } else {
+ pm_parser_err(parser, start, end, PM_ERR_ESCAPE_INVALID_UNICODE);
+ pm_buffer_append_byte(buffer, 0xEF);
+ pm_buffer_append_byte(buffer, 0xBF);
+ pm_buffer_append_byte(buffer, 0xBD);
+ }
+}
+
+/**
+ * When you're writing a byte to the unescape buffer, if the byte is non-ASCII
+ * (i.e., the top bit is set) then it locks in the encoding.
+ */
+static inline void
+escape_write_byte_encoded(pm_parser_t *parser, pm_buffer_t *buffer, uint8_t byte) {
+ if (byte >= 0x80) {
+ if (parser->explicit_encoding != NULL && parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY && parser->encoding != PM_ENCODING_UTF_8_ENTRY) {
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_MIXED_ENCODING, parser->encoding->name);
+ }
+
+ parser->explicit_encoding = parser->encoding;
+ }
+
+ pm_buffer_append_byte(buffer, byte);
+}
+
+/**
+ * Write each byte of the given escaped character into the buffer.
+ */
+static inline void
+escape_write_escape_encoded(pm_parser_t *parser, pm_buffer_t *buffer) {
+ size_t width;
+ if (parser->encoding_changed) {
+ width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
+ } else {
+ width = pm_encoding_utf_8_char_width(parser->current.end, parser->end - parser->current.end);
+ }
+
+ // TODO: If the character is invalid in the given encoding, then we'll just
+ // push one byte into the buffer. This should actually be an error.
+ width = (width == 0) ? 1 : width;
+
+ for (size_t index = 0; index < width; index++) {
+ escape_write_byte_encoded(parser, buffer, *parser->current.end);
+ parser->current.end++;
+ }
+}
+
+/**
+ * The regular expression engine doesn't support the same escape sequences as
+ * Ruby does. So first we have to read the escape sequence, and then we have to
+ * format it like the regular expression engine expects it. For example, in Ruby
+ * if we have:
+ *
+ * /\M-\C-?/
+ *
+ * then the first byte is actually 255, so we have to rewrite this as:
+ *
+ * /\xFF/
+ *
+ * Note that in this case there is a literal \ byte in the regular expression
+ * source so that the regular expression engine will perform its own unescaping.
+ */
+static inline void
+escape_write_byte(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expression_buffer, uint8_t flags, uint8_t byte) {
+ if (flags & PM_ESCAPE_FLAG_REGEXP) {
+ pm_buffer_append_bytes(regular_expression_buffer, (const uint8_t *) "\\x", 2);
+
+ uint8_t byte1 = (uint8_t) ((byte >> 4) & 0xF);
+ uint8_t byte2 = (uint8_t) (byte & 0xF);
+
+ if (byte1 >= 0xA) {
+ pm_buffer_append_byte(regular_expression_buffer, (uint8_t) ((byte1 - 0xA) + 'A'));
+ } else {
+ pm_buffer_append_byte(regular_expression_buffer, (uint8_t) (byte1 + '0'));
+ }
+
+ if (byte2 >= 0xA) {
+ pm_buffer_append_byte(regular_expression_buffer, (uint8_t) (byte2 - 0xA + 'A'));
+ } else {
+ pm_buffer_append_byte(regular_expression_buffer, (uint8_t) (byte2 + '0'));
+ }
+ }
+
+ escape_write_byte_encoded(parser, buffer, byte);
+}
+
+/**
+ * Warn about using a space or a tab character in an escape, as opposed to using
+ * \\s or \\t. Note that we can quite copy the source because the warning
+ * message replaces \\c with \\C.
+ */
+static void
+escape_read_warn(pm_parser_t *parser, uint8_t flags, uint8_t flag, const char *type) {
+#define FLAG(value) ((value & PM_ESCAPE_FLAG_CONTROL) ? "\\C-" : (value & PM_ESCAPE_FLAG_META) ? "\\M-" : "")
+
+ PM_PARSER_WARN_TOKEN_FORMAT(
+ parser,
+ parser->current,
+ PM_WARN_INVALID_CHARACTER,
+ FLAG(flags),
+ FLAG(flag),
+ type
+ );
+
+#undef FLAG
+}
+
+/**
+ * Read the value of an escape into the buffer.
+ */
+static void
+escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expression_buffer, uint8_t flags) {
+ switch (peek(parser)) {
+ case '\\': {
+ parser->current.end++;
+ escape_write_byte_encoded(parser, buffer, escape_byte('\\', flags));
+ return;
+ }
+ case '\'': {
+ parser->current.end++;
+ escape_write_byte_encoded(parser, buffer, escape_byte('\'', flags));
+ return;
+ }
+ case 'a': {
+ parser->current.end++;
+ escape_write_byte_encoded(parser, buffer, escape_byte('\a', flags));
+ return;
+ }
+ case 'b': {
+ parser->current.end++;
+ escape_write_byte_encoded(parser, buffer, escape_byte('\b', flags));
+ return;
+ }
+ case 'e': {
+ parser->current.end++;
+ escape_write_byte_encoded(parser, buffer, escape_byte('\033', flags));
+ return;
+ }
+ case 'f': {
+ parser->current.end++;
+ escape_write_byte_encoded(parser, buffer, escape_byte('\f', flags));
+ return;
+ }
+ case 'n': {
+ parser->current.end++;
+ escape_write_byte_encoded(parser, buffer, escape_byte('\n', flags));
+ return;
+ }
+ case 'r': {
+ parser->current.end++;
+ escape_write_byte_encoded(parser, buffer, escape_byte('\r', flags));
+ return;
+ }
+ case 's': {
+ parser->current.end++;
+ escape_write_byte_encoded(parser, buffer, escape_byte(' ', flags));
+ return;
+ }
+ case 't': {
+ parser->current.end++;
+ escape_write_byte_encoded(parser, buffer, escape_byte('\t', flags));
+ return;
+ }
+ case 'v': {
+ parser->current.end++;
+ escape_write_byte_encoded(parser, buffer, escape_byte('\v', flags));
+ return;
+ }
+ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': {
+ uint8_t value = (uint8_t) (*parser->current.end - '0');
+ parser->current.end++;
+
+ if (pm_char_is_octal_digit(peek(parser))) {
+ value = ((uint8_t) (value << 3)) | ((uint8_t) (*parser->current.end - '0'));
+ parser->current.end++;
+
+ if (pm_char_is_octal_digit(peek(parser))) {
+ value = ((uint8_t) (value << 3)) | ((uint8_t) (*parser->current.end - '0'));
+ parser->current.end++;
+ }
+ }
+
+ escape_write_byte_encoded(parser, buffer, value);
+ return;
+ }
+ case 'x': {
+ const uint8_t *start = parser->current.end - 1;
+
+ parser->current.end++;
+ uint8_t byte = peek(parser);
+
+ if (pm_char_is_hexadecimal_digit(byte)) {
+ uint8_t value = escape_hexadecimal_digit(byte);
+ parser->current.end++;
+
+ byte = peek(parser);
+ if (pm_char_is_hexadecimal_digit(byte)) {
+ value = (uint8_t) ((value << 4) | escape_hexadecimal_digit(byte));
+ parser->current.end++;
+ }
+
+ if (flags & PM_ESCAPE_FLAG_REGEXP) {
+ pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
+ }
+
+ escape_write_byte_encoded(parser, buffer, value);
+ } else {
+ pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_HEXADECIMAL);
+ }
+
+ return;
+ }
+ case 'u': {
+ const uint8_t *start = parser->current.end - 1;
+ parser->current.end++;
+
+ if (
+ (parser->current.end + 4 <= parser->end) &&
+ pm_char_is_hexadecimal_digit(parser->current.end[0]) &&
+ pm_char_is_hexadecimal_digit(parser->current.end[1]) &&
+ pm_char_is_hexadecimal_digit(parser->current.end[2]) &&
+ pm_char_is_hexadecimal_digit(parser->current.end[3])
+ ) {
+ uint32_t value = escape_unicode(parser->current.end, 4);
+
+ if (flags & PM_ESCAPE_FLAG_REGEXP) {
+ pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end + 4 - start));
+ }
+ escape_write_unicode(parser, buffer, flags, start, parser->current.end + 4, value);
+
+ parser->current.end += 4;
+ } else if (peek(parser) == '{') {
+ const uint8_t *unicode_codepoints_start = parser->current.end - 2;
+
+ parser->current.end++;
+ parser->current.end += pm_strspn_whitespace(parser->current.end, parser->end - parser->current.end);
+
+ const uint8_t *extra_codepoints_start = NULL;
+ int codepoints_count = 0;
+
+ while ((parser->current.end < parser->end) && (*parser->current.end != '}')) {
+ const uint8_t *unicode_start = parser->current.end;
+ size_t hexadecimal_length = pm_strspn_hexadecimal_digit(parser->current.end, parser->end - parser->current.end);
+
+ if (hexadecimal_length > 6) {
+ // \u{nnnn} character literal allows only 1-6 hexadecimal digits
+ pm_parser_err(parser, unicode_start, unicode_start + hexadecimal_length, PM_ERR_ESCAPE_INVALID_UNICODE_LONG);
+ } else if (hexadecimal_length == 0) {
+ // there are not hexadecimal characters
+ pm_parser_err(parser, unicode_start, unicode_start + hexadecimal_length, PM_ERR_ESCAPE_INVALID_UNICODE);
+ return;
+ }
+
+ parser->current.end += hexadecimal_length;
+ codepoints_count++;
+ if (flags & PM_ESCAPE_FLAG_SINGLE && codepoints_count == 2) {
+ extra_codepoints_start = unicode_start;
+ }
+
+ uint32_t value = escape_unicode(unicode_start, hexadecimal_length);
+ escape_write_unicode(parser, buffer, flags, unicode_start, parser->current.end, value);
+
+ parser->current.end += pm_strspn_whitespace(parser->current.end, parser->end - parser->current.end);
+ }
+
+ // ?\u{nnnn} character literal should contain only one codepoint
+ // and cannot be like ?\u{nnnn mmmm}.
+ if (flags & PM_ESCAPE_FLAG_SINGLE && codepoints_count > 1) {
+ pm_parser_err(parser, extra_codepoints_start, parser->current.end - 1, PM_ERR_ESCAPE_INVALID_UNICODE_LITERAL);
+ }
+
+ if (peek(parser) == '}') {
+ parser->current.end++;
+ } else {
+ pm_parser_err(parser, unicode_codepoints_start, parser->current.end, PM_ERR_ESCAPE_INVALID_UNICODE_TERM);
+ }
+
+ if (flags & PM_ESCAPE_FLAG_REGEXP) {
+ pm_buffer_append_bytes(regular_expression_buffer, unicode_codepoints_start, (size_t) (parser->current.end - unicode_codepoints_start));
+ }
+ } else {
+ pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_UNICODE);
+ }
+
+ return;
+ }
+ case 'c': {
+ parser->current.end++;
+ if (parser->current.end == parser->end) {
+ pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
+ return;
+ }
+
+ uint8_t peeked = peek(parser);
+ switch (peeked) {
+ case '?': {
+ parser->current.end++;
+ escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(0x7f, flags));
+ return;
+ }
+ case '\\':
+ if (flags & PM_ESCAPE_FLAG_CONTROL) {
+ pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT);
+ return;
+ }
+ parser->current.end++;
+ escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_CONTROL);
+ return;
+ case ' ':
+ parser->current.end++;
+ escape_read_warn(parser, flags, PM_ESCAPE_FLAG_CONTROL, "\\s");
+ escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
+ return;
+ case '\t':
+ parser->current.end++;
+ escape_read_warn(parser, flags, 0, "\\t");
+ escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
+ return;
+ default: {
+ if (!char_is_ascii_printable(peeked)) {
+ pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
+ return;
+ }
+
+ parser->current.end++;
+ escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
+ return;
+ }
+ }
+ }
+ case 'C': {
+ parser->current.end++;
+ if (peek(parser) != '-') {
+ pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
+ return;
+ }
+
+ parser->current.end++;
+ if (parser->current.end == parser->end) {
+ pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
+ return;
+ }
+
+ uint8_t peeked = peek(parser);
+ switch (peeked) {
+ case '?': {
+ parser->current.end++;
+ escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(0x7f, flags));
+ return;
+ }
+ case '\\':
+ if (flags & PM_ESCAPE_FLAG_CONTROL) {
+ pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT);
+ return;
+ }
+ parser->current.end++;
+ escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_CONTROL);
+ return;
+ case ' ':
+ parser->current.end++;
+ escape_read_warn(parser, flags, PM_ESCAPE_FLAG_CONTROL, "\\s");
+ escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
+ return;
+ case '\t':
+ parser->current.end++;
+ escape_read_warn(parser, flags, 0, "\\t");
+ escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
+ return;
+ default: {
+ if (!char_is_ascii_printable(peeked)) {
+ pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
+ return;
+ }
+
+ parser->current.end++;
+ escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
+ return;
+ }
+ }
+ }
+ case 'M': {
+ parser->current.end++;
+ if (peek(parser) != '-') {
+ pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META);
+ return;
+ }
+
+ parser->current.end++;
+ if (parser->current.end == parser->end) {
+ pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META);
+ return;
+ }
+
+ uint8_t peeked = peek(parser);
+ switch (peeked) {
+ case '\\':
+ if (flags & PM_ESCAPE_FLAG_META) {
+ pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META_REPEAT);
+ return;
+ }
+ parser->current.end++;
+ escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_META);
+ return;
+ case ' ':
+ parser->current.end++;
+ escape_read_warn(parser, flags, PM_ESCAPE_FLAG_META, "\\s");
+ escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META));
+ return;
+ case '\t':
+ parser->current.end++;
+ escape_read_warn(parser, flags & ((uint8_t) ~PM_ESCAPE_FLAG_CONTROL), PM_ESCAPE_FLAG_META, "\\t");
+ escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META));
+ return;
+ default:
+ if (!char_is_ascii_printable(peeked)) {
+ pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META);
+ return;
+ }
+
+ parser->current.end++;
+ escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META));
+ return;
+ }
+ }
+ case '\r': {
+ if (peek_offset(parser, 1) == '\n') {
+ parser->current.end += 2;
+ escape_write_byte_encoded(parser, buffer, escape_byte('\n', flags));
+ return;
+ }
+ }
+ /* fallthrough */
+ default: {
+ if (parser->current.end < parser->end) {
+ escape_write_escape_encoded(parser, buffer);
+ }
+ return;
+ }
+ }
+}
+
+/**
+ * This function is responsible for lexing either a character literal or the ?
+ * operator. The supported character literals are described below.
+ *
+ * \\a bell, ASCII 07h (BEL)
+ * \\b backspace, ASCII 08h (BS)
+ * \t horizontal tab, ASCII 09h (TAB)
+ * \\n newline (line feed), ASCII 0Ah (LF)
+ * \v vertical tab, ASCII 0Bh (VT)
+ * \f form feed, ASCII 0Ch (FF)
+ * \r carriage return, ASCII 0Dh (CR)
+ * \\e escape, ASCII 1Bh (ESC)
+ * \s space, ASCII 20h (SPC)
+ * \\ backslash
+ * \nnn octal bit pattern, where nnn is 1-3 octal digits ([0-7])
+ * \xnn hexadecimal bit pattern, where nn is 1-2 hexadecimal digits ([0-9a-fA-F])
+ * \unnnn Unicode character, where nnnn is exactly 4 hexadecimal digits ([0-9a-fA-F])
+ * \u{nnnn ...} Unicode character(s), where each nnnn is 1-6 hexadecimal digits ([0-9a-fA-F])
+ * \cx or \C-x control character, where x is an ASCII printable character
+ * \M-x meta character, where x is an ASCII printable character
+ * \M-\C-x meta control character, where x is an ASCII printable character
+ * \M-\cx same as above
+ * \\c\M-x same as above
+ * \\c? or \C-? delete, ASCII 7Fh (DEL)
+ */
+static pm_token_type_t
+lex_question_mark(pm_parser_t *parser) {
+ if (lex_state_end_p(parser)) {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ return PM_TOKEN_QUESTION_MARK;
+ }
+
+ if (parser->current.end >= parser->end) {
+ pm_parser_err_current(parser, PM_ERR_INCOMPLETE_QUESTION_MARK);
+ pm_string_shared_init(&parser->current_string, parser->current.start + 1, parser->current.end);
+ return PM_TOKEN_CHARACTER_LITERAL;
+ }
+
+ if (pm_char_is_whitespace(*parser->current.end)) {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ return PM_TOKEN_QUESTION_MARK;
+ }
+
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+
+ if (match(parser, '\\')) {
+ lex_state_set(parser, PM_LEX_STATE_END);
+
+ pm_buffer_t buffer;
+ pm_buffer_init_capacity(&buffer, 3);
+
+ escape_read(parser, &buffer, NULL, PM_ESCAPE_FLAG_SINGLE);
+ pm_string_owned_init(&parser->current_string, (uint8_t *) buffer.value, buffer.length);
+
+ return PM_TOKEN_CHARACTER_LITERAL;
+ } else {
+ size_t encoding_width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
+
+ // Ternary operators can have a ? immediately followed by an identifier
+ // which starts with an underscore. We check for this case here.
+ if (
+ !(parser->encoding->alnum_char(parser->current.end, parser->end - parser->current.end) || peek(parser) == '_') ||
+ (
+ (parser->current.end + encoding_width >= parser->end) ||
+ !char_is_identifier(parser, parser->current.end + encoding_width)
+ )
+ ) {
+ lex_state_set(parser, PM_LEX_STATE_END);
+ parser->current.end += encoding_width;
+ pm_string_shared_init(&parser->current_string, parser->current.start + 1, parser->current.end);
+ return PM_TOKEN_CHARACTER_LITERAL;
+ }
+ }
+
+ return PM_TOKEN_QUESTION_MARK;
+}
+
+/**
+ * Lex a variable that starts with an @ sign (either an instance or class
+ * variable).
+ */
+static pm_token_type_t
+lex_at_variable(pm_parser_t *parser) {
+ pm_token_type_t type = match(parser, '@') ? PM_TOKEN_CLASS_VARIABLE : PM_TOKEN_INSTANCE_VARIABLE;
+ size_t width;
+
+ if (parser->current.end < parser->end && (width = char_is_identifier_start(parser, parser->current.end)) > 0) {
+ parser->current.end += width;
+
+ while (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0) {
+ parser->current.end += width;
+ }
+ } else if (parser->current.end < parser->end && pm_char_is_decimal_digit(*parser->current.end)) {
+ pm_diagnostic_id_t diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE;
+ if (parser->version == PM_OPTIONS_VERSION_CRUBY_3_3_0) {
+ diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3_0 : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3_0;
+ }
+
+ size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, diag_id, (int) ((parser->current.end + width) - parser->current.start), (const char *) parser->current.start);
+ } else {
+ pm_diagnostic_id_t diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_CLASS_VARIABLE_BARE : PM_ERR_INSTANCE_VARIABLE_BARE;
+ pm_parser_err_token(parser, &parser->current, diag_id);
+ }
+
+ // If we're lexing an embedded variable, then we need to pop back into the
+ // parent lex context.
+ if (parser->lex_modes.current->mode == PM_LEX_EMBVAR) {
+ lex_mode_pop(parser);
+ }
+
+ return type;
+}
+
+/**
+ * Optionally call out to the lex callback if one is provided.
+ */
+static inline void
+parser_lex_callback(pm_parser_t *parser) {
+ if (parser->lex_callback) {
+ parser->lex_callback->callback(parser->lex_callback->data, parser, &parser->current);
+ }
+}
+
+/**
+ * Return a new comment node of the specified type.
+ */
+static inline pm_comment_t *
+parser_comment(pm_parser_t *parser, pm_comment_type_t type) {
+ pm_comment_t *comment = (pm_comment_t *) xcalloc(1, sizeof(pm_comment_t));
+ if (comment == NULL) return NULL;
+
+ *comment = (pm_comment_t) {
+ .type = type,
+ .location = { parser->current.start, parser->current.end }
+ };
+
+ return comment;
+}
+
+/**
+ * Lex out embedded documentation, and return when we have either hit the end of
+ * the file or the end of the embedded documentation. This calls the callback
+ * manually because only the lexer should see these tokens, not the parser.
+ */
+static pm_token_type_t
+lex_embdoc(pm_parser_t *parser) {
+ // First, lex out the EMBDOC_BEGIN token.
+ const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
+
+ if (newline == NULL) {
+ parser->current.end = parser->end;
+ } else {
+ pm_newline_list_append(&parser->newline_list, newline);
+ parser->current.end = newline + 1;
+ }
+
+ parser->current.type = PM_TOKEN_EMBDOC_BEGIN;
+ parser_lex_callback(parser);
+
+ // Now, create a comment that is going to be attached to the parser.
+ pm_comment_t *comment = parser_comment(parser, PM_COMMENT_EMBDOC);
+ if (comment == NULL) return PM_TOKEN_EOF;
+
+ // Now, loop until we find the end of the embedded documentation or the end
+ // of the file.
+ while (parser->current.end + 4 <= parser->end) {
+ parser->current.start = parser->current.end;
+
+ // If we've hit the end of the embedded documentation then we'll return
+ // that token here.
+ if (
+ (memcmp(parser->current.end, "=end", 4) == 0) &&
+ (
+ (parser->current.end + 4 == parser->end) || // end of file
+ pm_char_is_whitespace(parser->current.end[4]) || // whitespace
+ (parser->current.end[4] == '\0') || // NUL or end of script
+ (parser->current.end[4] == '\004') || // ^D
+ (parser->current.end[4] == '\032') // ^Z
+ )
+ ) {
+ const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
+
+ if (newline == NULL) {
+ parser->current.end = parser->end;
+ } else {
+ pm_newline_list_append(&parser->newline_list, newline);
+ parser->current.end = newline + 1;
+ }
+
+ parser->current.type = PM_TOKEN_EMBDOC_END;
+ parser_lex_callback(parser);
+
+ comment->location.end = parser->current.end;
+ pm_list_append(&parser->comment_list, (pm_list_node_t *) comment);
+
+ return PM_TOKEN_EMBDOC_END;
+ }
+
+ // Otherwise, we'll parse until the end of the line and return a line of
+ // embedded documentation.
+ const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
+
+ if (newline == NULL) {
+ parser->current.end = parser->end;
+ } else {
+ pm_newline_list_append(&parser->newline_list, newline);
+ parser->current.end = newline + 1;
+ }
+
+ parser->current.type = PM_TOKEN_EMBDOC_LINE;
+ parser_lex_callback(parser);
+ }
+
+ pm_parser_err_current(parser, PM_ERR_EMBDOC_TERM);
+
+ comment->location.end = parser->current.end;
+ pm_list_append(&parser->comment_list, (pm_list_node_t *) comment);
+
+ return PM_TOKEN_EOF;
+}
+
+/**
+ * Set the current type to an ignored newline and then call the lex callback.
+ * This happens in a couple places depending on whether or not we have already
+ * lexed a comment.
+ */
+static inline void
+parser_lex_ignored_newline(pm_parser_t *parser) {
+ parser->current.type = PM_TOKEN_IGNORED_NEWLINE;
+ parser_lex_callback(parser);
+}
+
+/**
+ * This function will be called when a newline is encountered. In some newlines,
+ * we need to check if there is a heredoc or heredocs that we have already lexed
+ * the body of that we need to now skip past. That will be indicated by the
+ * heredoc_end field on the parser.
+ *
+ * If it is set, then we need to skip past the heredoc body and then clear the
+ * heredoc_end field.
+ */
+static inline void
+parser_flush_heredoc_end(pm_parser_t *parser) {
+ assert(parser->heredoc_end <= parser->end);
+ parser->next_start = parser->heredoc_end;
+ parser->heredoc_end = NULL;
+}
+
+/**
+ * Returns true if the parser has lexed the last token on the current line.
+*/
+static bool
+parser_end_of_line_p(const pm_parser_t *parser) {
+ const uint8_t *cursor = parser->current.end;
+
+ while (cursor < parser->end && *cursor != '\n' && *cursor != '#') {
+ if (!pm_char_is_inline_whitespace(*cursor++)) return false;
+ }
+
+ return true;
+}
+
+/**
+ * When we're lexing certain types (strings, symbols, lists, etc.) we have
+ * string content associated with the tokens. For example:
+ *
+ * "foo"
+ *
+ * In this case, the string content is foo. Since there is no escaping, there's
+ * no need to track additional information and the token can be returned as
+ * normal. However, if we have escape sequences:
+ *
+ * "foo\n"
+ *
+ * then the bytes in the string are "f", "o", "o", "\", "n", but we want to
+ * provide our consumers with the string content "f", "o", "o", "\n". In these
+ * cases, when we find the first escape sequence, we initialize a pm_buffer_t
+ * to keep track of the string content. Then in the parser, it will
+ * automatically attach the string content to the node that it belongs to.
+ */
+typedef struct {
+ /**
+ * The buffer that we're using to keep track of the string content. It will
+ * only be initialized if we receive an escape sequence.
+ */
+ pm_buffer_t buffer;
+
+ /**
+ * The cursor into the source string that points to how far we have
+ * currently copied into the buffer.
+ */
+ const uint8_t *cursor;
+} pm_token_buffer_t;
+
+/**
+ * In order to properly set a regular expression's encoding and to validate
+ * the byte sequence for the underlying encoding we must process any escape
+ * sequences. The unescaped byte sequence will be stored in `buffer` just like
+ * for other string-like types. However, we also need to store the regular
+ * expression's source string. That string may be different from what we see
+ * during lexing because some escape sequences rewrite the source.
+ *
+ * This value will only be initialized for regular expressions and only if we
+ * receive an escape sequence. It will contain the regular expression's source
+ * string's byte sequence.
+ */
+typedef struct {
+ /** The embedded base buffer. */
+ pm_token_buffer_t base;
+
+ /** The buffer holding the regexp source. */
+ pm_buffer_t regexp_buffer;
+} pm_regexp_token_buffer_t;
+
+/**
+ * Push the given byte into the token buffer.
+ */
+static inline void
+pm_token_buffer_push_byte(pm_token_buffer_t *token_buffer, uint8_t byte) {
+ pm_buffer_append_byte(&token_buffer->buffer, byte);
+}
+
+static inline void
+pm_regexp_token_buffer_push_byte(pm_regexp_token_buffer_t *token_buffer, uint8_t byte) {
+ pm_buffer_append_byte(&token_buffer->regexp_buffer, byte);
+}
+
+/**
+ * Return the width of the character at the end of the current token.
+ */
+static inline size_t
+parser_char_width(const pm_parser_t *parser) {
+ size_t width;
+ if (parser->encoding_changed) {
+ width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
+ } else {
+ width = pm_encoding_utf_8_char_width(parser->current.end, parser->end - parser->current.end);
+ }
+
+ // TODO: If the character is invalid in the given encoding, then we'll just
+ // push one byte into the buffer. This should actually be an error.
+ return (width == 0 ? 1 : width);
+}
+
+/**
+ * Push an escaped character into the token buffer.
+ */
+static void
+pm_token_buffer_push_escaped(pm_token_buffer_t *token_buffer, pm_parser_t *parser) {
+ size_t width = parser_char_width(parser);
+ pm_buffer_append_bytes(&token_buffer->buffer, parser->current.end, width);
+ parser->current.end += width;
+}
+
+static void
+pm_regexp_token_buffer_push_escaped(pm_regexp_token_buffer_t *token_buffer, pm_parser_t *parser) {
+ size_t width = parser_char_width(parser);
+ pm_buffer_append_bytes(&token_buffer->base.buffer, parser->current.end, width);
+ pm_buffer_append_bytes(&token_buffer->regexp_buffer, parser->current.end, width);
+ parser->current.end += width;
+}
+
+static bool
+pm_slice_ascii_only_p(const uint8_t *value, size_t length) {
+ for (size_t index = 0; index < length; index++) {
+ if (value[index] & 0x80) return false;
+ }
+
+ return true;
+}
+
+/**
+ * When we're about to return from lexing the current token and we know for sure
+ * that we have found an escape sequence, this function is called to copy the
+ * contents of the token buffer into the current string on the parser so that it
+ * can be attached to the correct node.
+ */
+static inline void
+pm_token_buffer_copy(pm_parser_t *parser, pm_token_buffer_t *token_buffer) {
+ pm_string_owned_init(&parser->current_string, (uint8_t *) pm_buffer_value(&token_buffer->buffer), pm_buffer_length(&token_buffer->buffer));
+}
+
+static inline void
+pm_regexp_token_buffer_copy(pm_parser_t *parser, pm_regexp_token_buffer_t *token_buffer) {
+ pm_string_owned_init(&parser->current_string, (uint8_t *) pm_buffer_value(&token_buffer->base.buffer), pm_buffer_length(&token_buffer->base.buffer));
+ parser->current_regular_expression_ascii_only = pm_slice_ascii_only_p((const uint8_t *) pm_buffer_value(&token_buffer->regexp_buffer), pm_buffer_length(&token_buffer->regexp_buffer));
+ pm_buffer_free(&token_buffer->regexp_buffer);
+}
+
+/**
+ * When we're about to return from lexing the current token, we need to flush
+ * all of the content that we have pushed into the buffer into the current
+ * string. If we haven't pushed anything into the buffer, this means that we
+ * never found an escape sequence, so we can directly reference the bounds of
+ * the current string. Either way, at the return of this function it is expected
+ * that parser->current_string is established in such a way that it can be
+ * attached to a node.
+ */
+static void
+pm_token_buffer_flush(pm_parser_t *parser, pm_token_buffer_t *token_buffer) {
+ if (token_buffer->cursor == NULL) {
+ pm_string_shared_init(&parser->current_string, parser->current.start, parser->current.end);
+ } else {
+ pm_buffer_append_bytes(&token_buffer->buffer, token_buffer->cursor, (size_t) (parser->current.end - token_buffer->cursor));
+ pm_token_buffer_copy(parser, token_buffer);
+ }
+}
+
+static void
+pm_regexp_token_buffer_flush(pm_parser_t *parser, pm_regexp_token_buffer_t *token_buffer) {
+ if (token_buffer->base.cursor == NULL) {
+ pm_string_shared_init(&parser->current_string, parser->current.start, parser->current.end);
+ parser->current_regular_expression_ascii_only = pm_slice_ascii_only_p(parser->current.start, (size_t) (parser->current.end - parser->current.start));
+ } else {
+ pm_buffer_append_bytes(&token_buffer->base.buffer, token_buffer->base.cursor, (size_t) (parser->current.end - token_buffer->base.cursor));
+ pm_buffer_append_bytes(&token_buffer->regexp_buffer, token_buffer->base.cursor, (size_t) (parser->current.end - token_buffer->base.cursor));
+ pm_regexp_token_buffer_copy(parser, token_buffer);
+ }
+}
+
+#define PM_TOKEN_BUFFER_DEFAULT_SIZE 16
+
+/**
+ * When we've found an escape sequence, we need to copy everything up to this
+ * point into the buffer because we're about to provide a string that has
+ * different content than a direct slice of the source.
+ *
+ * It is expected that the parser's current token end will be pointing at one
+ * byte past the backslash that starts the escape sequence.
+ */
+static void
+pm_token_buffer_escape(pm_parser_t *parser, pm_token_buffer_t *token_buffer) {
+ const uint8_t *start;
+ if (token_buffer->cursor == NULL) {
+ pm_buffer_init_capacity(&token_buffer->buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE);
+ start = parser->current.start;
+ } else {
+ start = token_buffer->cursor;
+ }
+
+ const uint8_t *end = parser->current.end - 1;
+ pm_buffer_append_bytes(&token_buffer->buffer, start, (size_t) (end - start));
+
+ token_buffer->cursor = end;
+}
+
+static void
+pm_regexp_token_buffer_escape(pm_parser_t *parser, pm_regexp_token_buffer_t *token_buffer) {
+ const uint8_t *start;
+ if (token_buffer->base.cursor == NULL) {
+ pm_buffer_init_capacity(&token_buffer->base.buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE);
+ pm_buffer_init_capacity(&token_buffer->regexp_buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE);
+ start = parser->current.start;
+ } else {
+ start = token_buffer->base.cursor;
+ }
+
+ const uint8_t *end = parser->current.end - 1;
+ pm_buffer_append_bytes(&token_buffer->base.buffer, start, (size_t) (end - start));
+ pm_buffer_append_bytes(&token_buffer->regexp_buffer, start, (size_t) (end - start));
+
+ token_buffer->base.cursor = end;
+}
+
+#undef PM_TOKEN_BUFFER_DEFAULT_SIZE
+
+/**
+ * Effectively the same thing as pm_strspn_inline_whitespace, but in the case of
+ * a tilde heredoc expands out tab characters to the nearest tab boundaries.
+ */
+static inline size_t
+pm_heredoc_strspn_inline_whitespace(pm_parser_t *parser, const uint8_t **cursor, pm_heredoc_indent_t indent) {
+ size_t whitespace = 0;
+
+ switch (indent) {
+ case PM_HEREDOC_INDENT_NONE:
+ // Do nothing, we can't match a terminator with
+ // indentation and there's no need to calculate common
+ // whitespace.
+ break;
+ case PM_HEREDOC_INDENT_DASH:
+ // Skip past inline whitespace.
+ *cursor += pm_strspn_inline_whitespace(*cursor, parser->end - *cursor);
+ break;
+ case PM_HEREDOC_INDENT_TILDE:
+ // Skip past inline whitespace and calculate common
+ // whitespace.
+ while (*cursor < parser->end && pm_char_is_inline_whitespace(**cursor)) {
+ if (**cursor == '\t') {
+ whitespace = (whitespace / PM_TAB_WHITESPACE_SIZE + 1) * PM_TAB_WHITESPACE_SIZE;
+ } else {
+ whitespace++;
+ }
+ (*cursor)++;
+ }
+
+ break;
+ }
+
+ return whitespace;
+}
+
+/**
+ * Lex past the delimiter of a percent literal. Handle newlines and heredocs
+ * appropriately.
+ */
+static uint8_t
+pm_lex_percent_delimiter(pm_parser_t *parser) {
+ size_t eol_length = match_eol(parser);
+
+ if (eol_length) {
+ if (parser->heredoc_end) {
+ // If we have already lexed a heredoc, then the newline has already
+ // been added to the list. In this case we want to just flush the
+ // heredoc end.
+ parser_flush_heredoc_end(parser);
+ } else {
+ // Otherwise, we'll add the newline to the list of newlines.
+ pm_newline_list_append(&parser->newline_list, parser->current.end + eol_length - 1);
+ }
+
+ const uint8_t delimiter = *parser->current.end;
+ parser->current.end += eol_length;
+
+ return delimiter;
+ }
+
+ return *parser->current.end++;
+}
+
+/**
+ * This is a convenience macro that will set the current token type, call the
+ * lex callback, and then return from the parser_lex function.
+ */
+#define LEX(token_type) parser->current.type = token_type; parser_lex_callback(parser); return
+
+/**
+ * Called when the parser requires a new token. The parser maintains a moving
+ * window of two tokens at a time: parser.previous and parser.current. This
+ * function will move the current token into the previous token and then
+ * lex a new token into the current token.
+ */
+static void
+parser_lex(pm_parser_t *parser) {
+ assert(parser->current.end <= parser->end);
+ parser->previous = parser->current;
+
+ // This value mirrors cmd_state from CRuby.
+ bool previous_command_start = parser->command_start;
+ parser->command_start = false;
+
+ // This is used to communicate to the newline lexing function that we've
+ // already seen a comment.
+ bool lexed_comment = false;
+
+ // Here we cache the current value of the semantic token seen flag. This is
+ // used to reset it in case we find a token that shouldn't flip this flag.
+ unsigned int semantic_token_seen = parser->semantic_token_seen;
+ parser->semantic_token_seen = true;
+
+ switch (parser->lex_modes.current->mode) {
+ case PM_LEX_DEFAULT:
+ case PM_LEX_EMBEXPR:
+ case PM_LEX_EMBVAR:
+
+ // We have a specific named label here because we are going to jump back to
+ // this location in the event that we have lexed a token that should not be
+ // returned to the parser. This includes comments, ignored newlines, and
+ // invalid tokens of some form.
+ lex_next_token: {
+ // If we have the special next_start pointer set, then we're going to jump
+ // to that location and start lexing from there.
+ if (parser->next_start != NULL) {
+ parser->current.end = parser->next_start;
+ parser->next_start = NULL;
+ }
+
+ // This value mirrors space_seen from CRuby. It tracks whether or not
+ // space has been eaten before the start of the next token.
+ bool space_seen = false;
+
+ // First, we're going to skip past any whitespace at the front of the next
+ // token.
+ bool chomping = true;
+ while (parser->current.end < parser->end && chomping) {
+ switch (*parser->current.end) {
+ case ' ':
+ case '\t':
+ case '\f':
+ case '\v':
+ parser->current.end++;
+ space_seen = true;
+ break;
+ case '\r':
+ if (match_eol_offset(parser, 1)) {
+ chomping = false;
+ } else {
+ pm_parser_warn(parser, parser->current.end, parser->current.end + 1, PM_WARN_UNEXPECTED_CARRIAGE_RETURN);
+ parser->current.end++;
+ space_seen = true;
+ }
+ break;
+ case '\\': {
+ size_t eol_length = match_eol_offset(parser, 1);
+ if (eol_length) {
+ if (parser->heredoc_end) {
+ parser->current.end = parser->heredoc_end;
+ parser->heredoc_end = NULL;
+ } else {
+ parser->current.end += eol_length + 1;
+ pm_newline_list_append(&parser->newline_list, parser->current.end - 1);
+ space_seen = true;
+ }
+ } else if (pm_char_is_inline_whitespace(*parser->current.end)) {
+ parser->current.end += 2;
+ } else {
+ chomping = false;
+ }
+
+ break;
+ }
+ default:
+ chomping = false;
+ break;
+ }
+ }
+
+ // Next, we'll set to start of this token to be the current end.
+ parser->current.start = parser->current.end;
+
+ // We'll check if we're at the end of the file. If we are, then we
+ // need to return the EOF token.
+ if (parser->current.end >= parser->end) {
+ LEX(PM_TOKEN_EOF);
+ }
+
+ // Finally, we'll check the current character to determine the next
+ // token.
+ switch (*parser->current.end++) {
+ case '\0': // NUL or end of script
+ case '\004': // ^D
+ case '\032': // ^Z
+ parser->current.end--;
+ LEX(PM_TOKEN_EOF);
+
+ case '#': { // comments
+ const uint8_t *ending = next_newline(parser->current.end, parser->end - parser->current.end);
+ parser->current.end = ending == NULL ? parser->end : ending;
+
+ // If we found a comment while lexing, then we're going to
+ // add it to the list of comments in the file and keep
+ // lexing.
+ pm_comment_t *comment = parser_comment(parser, PM_COMMENT_INLINE);
+ pm_list_append(&parser->comment_list, (pm_list_node_t *) comment);
+
+ if (ending) parser->current.end++;
+ parser->current.type = PM_TOKEN_COMMENT;
+ parser_lex_callback(parser);
+
+ // Here, parse the comment to see if it's a magic comment
+ // and potentially change state on the parser.
+ if (!parser_lex_magic_comment(parser, semantic_token_seen) && (parser->current.start == parser->encoding_comment_start)) {
+ ptrdiff_t length = parser->current.end - parser->current.start;
+
+ // If we didn't find a magic comment within the first
+ // pass and we're at the start of the file, then we need
+ // to do another pass to potentially find other patterns
+ // for encoding comments.
+ if (length >= 10) parser_lex_magic_comment_encoding(parser);
+ }
+
+ lexed_comment = true;
+ }
+ /* fallthrough */
+ case '\r':
+ case '\n': {
+ parser->semantic_token_seen = semantic_token_seen & 0x1;
+ size_t eol_length = match_eol_at(parser, parser->current.end - 1);
+
+ if (eol_length) {
+ // The only way you can have carriage returns in this
+ // particular loop is if you have a carriage return
+ // followed by a newline. In that case we'll just skip
+ // over the carriage return and continue lexing, in
+ // order to make it so that the newline token
+ // encapsulates both the carriage return and the
+ // newline. Note that we need to check that we haven't
+ // already lexed a comment here because that falls
+ // through into here as well.
+ if (!lexed_comment) {
+ parser->current.end += eol_length - 1; // skip CR
+ }
+
+ if (parser->heredoc_end == NULL) {
+ pm_newline_list_append(&parser->newline_list, parser->current.end - 1);
+ }
+ }
+
+ if (parser->heredoc_end) {
+ parser_flush_heredoc_end(parser);
+ }
+
+ // If this is an ignored newline, then we can continue lexing after
+ // calling the callback with the ignored newline token.
+ switch (lex_state_ignored_p(parser)) {
+ case PM_IGNORED_NEWLINE_NONE:
+ break;
+ case PM_IGNORED_NEWLINE_PATTERN:
+ if (parser->pattern_matching_newlines || parser->in_keyword_arg) {
+ if (!lexed_comment) parser_lex_ignored_newline(parser);
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ parser->command_start = true;
+ parser->current.type = PM_TOKEN_NEWLINE;
+ return;
+ }
+ /* fallthrough */
+ case PM_IGNORED_NEWLINE_ALL:
+ if (!lexed_comment) parser_lex_ignored_newline(parser);
+ lexed_comment = false;
+ goto lex_next_token;
+ }
+
+ // Here we need to look ahead and see if there is a call operator
+ // (either . or &.) that starts the next line. If there is, then this
+ // is going to become an ignored newline and we're going to instead
+ // return the call operator.
+ const uint8_t *next_content = parser->next_start == NULL ? parser->current.end : parser->next_start;
+ next_content += pm_strspn_inline_whitespace(next_content, parser->end - next_content);
+
+ if (next_content < parser->end) {
+ // If we hit a comment after a newline, then we're going to check
+ // if it's ignored or if it's followed by a method call ('.').
+ // If it is, then we're going to call the
+ // callback with an ignored newline and then continue lexing.
+ // Otherwise we'll return a regular newline.
+ if (next_content[0] == '#') {
+ // Here we look for a "." or "&." following a "\n".
+ const uint8_t *following = next_newline(next_content, parser->end - next_content);
+
+ while (following && (following + 1 < parser->end)) {
+ following++;
+ following += pm_strspn_inline_whitespace(following, parser->end - following);
+
+ // If this is not followed by a comment, then we can break out
+ // of this loop.
+ if (peek_at(parser, following) != '#') break;
+
+ // If there is a comment, then we need to find the end of the
+ // comment and continue searching from there.
+ following = next_newline(following, parser->end - following);
+ }
+
+ // If the lex state was ignored, or we hit a '.' or a '&.',
+ // we will lex the ignored newline
+ if (
+ lex_state_ignored_p(parser) ||
+ (following && (
+ (peek_at(parser, following) == '.') ||
+ (peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '.')
+ ))
+ ) {
+ if (!lexed_comment) parser_lex_ignored_newline(parser);
+ lexed_comment = false;
+ goto lex_next_token;
+ }
+ }
+
+ // If we hit a . after a newline, then we're in a call chain and
+ // we need to return the call operator.
+ if (next_content[0] == '.') {
+ // To match ripper, we need to emit an ignored newline even though
+ // it's a real newline in the case that we have a beginless range
+ // on a subsequent line.
+ if (peek_at(parser, next_content + 1) == '.') {
+ if (!lexed_comment) parser_lex_ignored_newline(parser);
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ parser->command_start = true;
+ parser->current.type = PM_TOKEN_NEWLINE;
+ return;
+ }
+
+ if (!lexed_comment) parser_lex_ignored_newline(parser);
+ lex_state_set(parser, PM_LEX_STATE_DOT);
+ parser->current.start = next_content;
+ parser->current.end = next_content + 1;
+ parser->next_start = NULL;
+ LEX(PM_TOKEN_DOT);
+ }
+
+ // If we hit a &. after a newline, then we're in a call chain and
+ // we need to return the call operator.
+ if (peek_at(parser, next_content) == '&' && peek_at(parser, next_content + 1) == '.') {
+ if (!lexed_comment) parser_lex_ignored_newline(parser);
+ lex_state_set(parser, PM_LEX_STATE_DOT);
+ parser->current.start = next_content;
+ parser->current.end = next_content + 2;
+ parser->next_start = NULL;
+ LEX(PM_TOKEN_AMPERSAND_DOT);
+ }
+ }
+
+ // At this point we know this is a regular newline, and we can set the
+ // necessary state and return the token.
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ parser->command_start = true;
+ parser->current.type = PM_TOKEN_NEWLINE;
+ if (!lexed_comment) parser_lex_callback(parser);
+ return;
+ }
+
+ // ,
+ case ',':
+ lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
+ LEX(PM_TOKEN_COMMA);
+
+ // (
+ case '(': {
+ pm_token_type_t type = PM_TOKEN_PARENTHESIS_LEFT;
+
+ if (space_seen && (lex_state_arg_p(parser) || parser->lex_state == (PM_LEX_STATE_END | PM_LEX_STATE_LABEL))) {
+ type = PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES;
+ }
+
+ parser->enclosure_nesting++;
+ lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
+ pm_do_loop_stack_push(parser, false);
+ LEX(type);
+ }
+
+ // )
+ case ')':
+ parser->enclosure_nesting--;
+ lex_state_set(parser, PM_LEX_STATE_ENDFN);
+ pm_do_loop_stack_pop(parser);
+ LEX(PM_TOKEN_PARENTHESIS_RIGHT);
+
+ // ;
+ case ';':
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ parser->command_start = true;
+ LEX(PM_TOKEN_SEMICOLON);
+
+ // [ [] []=
+ case '[':
+ parser->enclosure_nesting++;
+ pm_token_type_t type = PM_TOKEN_BRACKET_LEFT;
+
+ if (lex_state_operator_p(parser)) {
+ if (match(parser, ']')) {
+ parser->enclosure_nesting--;
+ lex_state_set(parser, PM_LEX_STATE_ARG);
+ LEX(match(parser, '=') ? PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL : PM_TOKEN_BRACKET_LEFT_RIGHT);
+ }
+
+ lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABEL);
+ LEX(type);
+ }
+
+ if (lex_state_beg_p(parser) || (lex_state_arg_p(parser) && (space_seen || lex_state_p(parser, PM_LEX_STATE_LABELED)))) {
+ type = PM_TOKEN_BRACKET_LEFT_ARRAY;
+ }
+
+ lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
+ pm_do_loop_stack_push(parser, false);
+ LEX(type);
+
+ // ]
+ case ']':
+ parser->enclosure_nesting--;
+ lex_state_set(parser, PM_LEX_STATE_END);
+ pm_do_loop_stack_pop(parser);
+ LEX(PM_TOKEN_BRACKET_RIGHT);
+
+ // {
+ case '{': {
+ pm_token_type_t type = PM_TOKEN_BRACE_LEFT;
+
+ if (parser->enclosure_nesting == parser->lambda_enclosure_nesting) {
+ // This { begins a lambda
+ parser->command_start = true;
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ type = PM_TOKEN_LAMBDA_BEGIN;
+ } else if (lex_state_p(parser, PM_LEX_STATE_LABELED)) {
+ // This { begins a hash literal
+ lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
+ } else if (lex_state_p(parser, PM_LEX_STATE_ARG_ANY | PM_LEX_STATE_END | PM_LEX_STATE_ENDFN)) {
+ // This { begins a block
+ parser->command_start = true;
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ } else if (lex_state_p(parser, PM_LEX_STATE_ENDARG)) {
+ // This { begins a block on a command
+ parser->command_start = true;
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ } else {
+ // This { begins a hash literal
+ lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
+ }
+
+ parser->enclosure_nesting++;
+ parser->brace_nesting++;
+ pm_do_loop_stack_push(parser, false);
+
+ LEX(type);
+ }
+
+ // }
+ case '}':
+ parser->enclosure_nesting--;
+ pm_do_loop_stack_pop(parser);
+
+ if ((parser->lex_modes.current->mode == PM_LEX_EMBEXPR) && (parser->brace_nesting == 0)) {
+ lex_mode_pop(parser);
+ LEX(PM_TOKEN_EMBEXPR_END);
+ }
+
+ parser->brace_nesting--;
+ lex_state_set(parser, PM_LEX_STATE_END);
+ LEX(PM_TOKEN_BRACE_RIGHT);
+
+ // * ** **= *=
+ case '*': {
+ if (match(parser, '*')) {
+ if (match(parser, '=')) {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ LEX(PM_TOKEN_STAR_STAR_EQUAL);
+ }
+
+ pm_token_type_t type = PM_TOKEN_STAR_STAR;
+
+ if (lex_state_spcarg_p(parser, space_seen)) {
+ pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_STAR_STAR);
+ type = PM_TOKEN_USTAR_STAR;
+ } else if (lex_state_beg_p(parser)) {
+ type = PM_TOKEN_USTAR_STAR;
+ }
+
+ if (lex_state_operator_p(parser)) {
+ lex_state_set(parser, PM_LEX_STATE_ARG);
+ } else {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ }
+
+ LEX(type);
+ }
+
+ if (match(parser, '=')) {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ LEX(PM_TOKEN_STAR_EQUAL);
+ }
+
+ pm_token_type_t type = PM_TOKEN_STAR;
+
+ if (lex_state_spcarg_p(parser, space_seen)) {
+ pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_STAR);
+ type = PM_TOKEN_USTAR;
+ } else if (lex_state_beg_p(parser)) {
+ type = PM_TOKEN_USTAR;
+ }
+
+ if (lex_state_operator_p(parser)) {
+ lex_state_set(parser, PM_LEX_STATE_ARG);
+ } else {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ }
+
+ LEX(type);
+ }
+
+ // ! != !~ !@
+ case '!':
+ if (lex_state_operator_p(parser)) {
+ lex_state_set(parser, PM_LEX_STATE_ARG);
+ if (match(parser, '@')) {
+ LEX(PM_TOKEN_BANG);
+ }
+ } else {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ }
+
+ if (match(parser, '=')) {
+ LEX(PM_TOKEN_BANG_EQUAL);
+ }
+
+ if (match(parser, '~')) {
+ LEX(PM_TOKEN_BANG_TILDE);
+ }
+
+ LEX(PM_TOKEN_BANG);
+
+ // = => =~ == === =begin
+ case '=':
+ if (
+ current_token_starts_line(parser) &&
+ (parser->current.end + 5 <= parser->end) &&
+ memcmp(parser->current.end, "begin", 5) == 0 &&
+ (pm_char_is_whitespace(peek_offset(parser, 5)) || (peek_offset(parser, 5) == '\0'))
+ ) {
+ pm_token_type_t type = lex_embdoc(parser);
+ if (type == PM_TOKEN_EOF) {
+ LEX(type);
+ }
+
+ goto lex_next_token;
+ }
+
+ if (lex_state_operator_p(parser)) {
+ lex_state_set(parser, PM_LEX_STATE_ARG);
+ } else {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ }
+
+ if (match(parser, '>')) {
+ LEX(PM_TOKEN_EQUAL_GREATER);
+ }
+
+ if (match(parser, '~')) {
+ LEX(PM_TOKEN_EQUAL_TILDE);
+ }
+
+ if (match(parser, '=')) {
+ LEX(match(parser, '=') ? PM_TOKEN_EQUAL_EQUAL_EQUAL : PM_TOKEN_EQUAL_EQUAL);
+ }
+
+ LEX(PM_TOKEN_EQUAL);
+
+ // < << <<= <= <=>
+ case '<':
+ if (match(parser, '<')) {
+ if (
+ !lex_state_p(parser, PM_LEX_STATE_DOT | PM_LEX_STATE_CLASS) &&
+ !lex_state_end_p(parser) &&
+ (!lex_state_p(parser, PM_LEX_STATE_ARG_ANY) || lex_state_p(parser, PM_LEX_STATE_LABELED) || space_seen)
+ ) {
+ const uint8_t *end = parser->current.end;
+
+ pm_heredoc_quote_t quote = PM_HEREDOC_QUOTE_NONE;
+ pm_heredoc_indent_t indent = PM_HEREDOC_INDENT_NONE;
+
+ if (match(parser, '-')) {
+ indent = PM_HEREDOC_INDENT_DASH;
+ }
+ else if (match(parser, '~')) {
+ indent = PM_HEREDOC_INDENT_TILDE;
+ }
+
+ if (match(parser, '`')) {
+ quote = PM_HEREDOC_QUOTE_BACKTICK;
+ }
+ else if (match(parser, '"')) {
+ quote = PM_HEREDOC_QUOTE_DOUBLE;
+ }
+ else if (match(parser, '\'')) {
+ quote = PM_HEREDOC_QUOTE_SINGLE;
+ }
+
+ const uint8_t *ident_start = parser->current.end;
+ size_t width = 0;
+
+ if (parser->current.end >= parser->end) {
+ parser->current.end = end;
+ } else if (quote == PM_HEREDOC_QUOTE_NONE && (width = char_is_identifier(parser, parser->current.end)) == 0) {
+ parser->current.end = end;
+ } else {
+ if (quote == PM_HEREDOC_QUOTE_NONE) {
+ parser->current.end += width;
+
+ while ((parser->current.end < parser->end) && (width = char_is_identifier(parser, parser->current.end))) {
+ parser->current.end += width;
+ }
+ } else {
+ // If we have quotes, then we're going to go until we find the
+ // end quote.
+ while ((parser->current.end < parser->end) && quote != (pm_heredoc_quote_t) (*parser->current.end)) {
+ parser->current.end++;
+ }
+ }
+
+ size_t ident_length = (size_t) (parser->current.end - ident_start);
+ if (quote != PM_HEREDOC_QUOTE_NONE && !match(parser, (uint8_t) quote)) {
+ // TODO: handle unterminated heredoc
+ }
+
+ parser->explicit_encoding = NULL;
+ lex_mode_push(parser, (pm_lex_mode_t) {
+ .mode = PM_LEX_HEREDOC,
+ .as.heredoc = {
+ .ident_start = ident_start,
+ .ident_length = ident_length,
+ .next_start = parser->current.end,
+ .quote = quote,
+ .indent = indent,
+ .common_whitespace = (size_t) -1,
+ .line_continuation = false
+ }
+ });
+
+ if (parser->heredoc_end == NULL) {
+ const uint8_t *body_start = next_newline(parser->current.end, parser->end - parser->current.end);
+
+ if (body_start == NULL) {
+ // If there is no newline after the heredoc identifier, then
+ // this is not a valid heredoc declaration. In this case we
+ // will add an error, but we will still return a heredoc
+ // start.
+ pm_parser_err_current(parser, PM_ERR_HEREDOC_TERM);
+ body_start = parser->end;
+ } else {
+ // Otherwise, we want to indicate that the body of the
+ // heredoc starts on the character after the next newline.
+ pm_newline_list_append(&parser->newline_list, body_start);
+ body_start++;
+ }
+
+ parser->next_start = body_start;
+ } else {
+ parser->next_start = parser->heredoc_end;
+ }
+
+ LEX(PM_TOKEN_HEREDOC_START);
+ }
+ }
+
+ if (match(parser, '=')) {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ LEX(PM_TOKEN_LESS_LESS_EQUAL);
+ }
+
+ if (lex_state_operator_p(parser)) {
+ lex_state_set(parser, PM_LEX_STATE_ARG);
+ } else {
+ if (lex_state_p(parser, PM_LEX_STATE_CLASS)) parser->command_start = true;
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ }
+
+ LEX(PM_TOKEN_LESS_LESS);
+ }
+
+ if (lex_state_operator_p(parser)) {
+ lex_state_set(parser, PM_LEX_STATE_ARG);
+ } else {
+ if (lex_state_p(parser, PM_LEX_STATE_CLASS)) parser->command_start = true;
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ }
+
+ if (match(parser, '=')) {
+ if (match(parser, '>')) {
+ LEX(PM_TOKEN_LESS_EQUAL_GREATER);
+ }
+
+ LEX(PM_TOKEN_LESS_EQUAL);
+ }
+
+ LEX(PM_TOKEN_LESS);
+
+ // > >> >>= >=
+ case '>':
+ if (match(parser, '>')) {
+ if (lex_state_operator_p(parser)) {
+ lex_state_set(parser, PM_LEX_STATE_ARG);
+ } else {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ }
+ LEX(match(parser, '=') ? PM_TOKEN_GREATER_GREATER_EQUAL : PM_TOKEN_GREATER_GREATER);
+ }
+
+ if (lex_state_operator_p(parser)) {
+ lex_state_set(parser, PM_LEX_STATE_ARG);
+ } else {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ }
+
+ LEX(match(parser, '=') ? PM_TOKEN_GREATER_EQUAL : PM_TOKEN_GREATER);
+
+ // double-quoted string literal
+ case '"': {
+ bool label_allowed = (lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser);
+ lex_mode_push_string(parser, true, label_allowed, '\0', '"');
+ LEX(PM_TOKEN_STRING_BEGIN);
+ }
+
+ // xstring literal
+ case '`': {
+ if (lex_state_p(parser, PM_LEX_STATE_FNAME)) {
+ lex_state_set(parser, PM_LEX_STATE_ENDFN);
+ LEX(PM_TOKEN_BACKTICK);
+ }
+
+ if (lex_state_p(parser, PM_LEX_STATE_DOT)) {
+ if (previous_command_start) {
+ lex_state_set(parser, PM_LEX_STATE_CMDARG);
+ } else {
+ lex_state_set(parser, PM_LEX_STATE_ARG);
+ }
+
+ LEX(PM_TOKEN_BACKTICK);
+ }
+
+ lex_mode_push_string(parser, true, false, '\0', '`');
+ LEX(PM_TOKEN_BACKTICK);
+ }
+
+ // single-quoted string literal
+ case '\'': {
+ bool label_allowed = (lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser);
+ lex_mode_push_string(parser, false, label_allowed, '\0', '\'');
+ LEX(PM_TOKEN_STRING_BEGIN);
+ }
+
+ // ? character literal
+ case '?':
+ LEX(lex_question_mark(parser));
+
+ // & && &&= &=
+ case '&': {
+ if (match(parser, '&')) {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+
+ if (match(parser, '=')) {
+ LEX(PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
+ }
+
+ LEX(PM_TOKEN_AMPERSAND_AMPERSAND);
+ }
+
+ if (match(parser, '=')) {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ LEX(PM_TOKEN_AMPERSAND_EQUAL);
+ }
+
+ if (match(parser, '.')) {
+ lex_state_set(parser, PM_LEX_STATE_DOT);
+ LEX(PM_TOKEN_AMPERSAND_DOT);
+ }
+
+ pm_token_type_t type = PM_TOKEN_AMPERSAND;
+ if (lex_state_spcarg_p(parser, space_seen)) {
+ pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_AMPERSAND);
+ type = PM_TOKEN_UAMPERSAND;
+ } else if (lex_state_beg_p(parser)) {
+ type = PM_TOKEN_UAMPERSAND;
+ }
+
+ if (lex_state_operator_p(parser)) {
+ lex_state_set(parser, PM_LEX_STATE_ARG);
+ } else {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ }
+
+ LEX(type);
+ }
+
+ // | || ||= |=
+ case '|':
+ if (match(parser, '|')) {
+ if (match(parser, '=')) {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ LEX(PM_TOKEN_PIPE_PIPE_EQUAL);
+ }
+
+ if (lex_state_p(parser, PM_LEX_STATE_BEG)) {
+ parser->current.end--;
+ LEX(PM_TOKEN_PIPE);
+ }
+
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ LEX(PM_TOKEN_PIPE_PIPE);
+ }
+
+ if (match(parser, '=')) {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ LEX(PM_TOKEN_PIPE_EQUAL);
+ }
+
+ if (lex_state_operator_p(parser)) {
+ lex_state_set(parser, PM_LEX_STATE_ARG);
+ } else {
+ lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
+ }
+
+ LEX(PM_TOKEN_PIPE);
+
+ // + += +@
+ case '+': {
+ if (lex_state_operator_p(parser)) {
+ lex_state_set(parser, PM_LEX_STATE_ARG);
+
+ if (match(parser, '@')) {
+ LEX(PM_TOKEN_UPLUS);
+ }
+
+ LEX(PM_TOKEN_PLUS);
+ }
+
+ if (match(parser, '=')) {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ LEX(PM_TOKEN_PLUS_EQUAL);
+ }
+
+ if (
+ lex_state_beg_p(parser) ||
+ (lex_state_spcarg_p(parser, space_seen) ? (pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS), true) : false)
+ ) {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+
+ if (pm_char_is_decimal_digit(peek(parser))) {
+ parser->current.end++;
+ pm_token_type_t type = lex_numeric(parser);
+ lex_state_set(parser, PM_LEX_STATE_END);
+ LEX(type);
+ }
+
+ LEX(PM_TOKEN_UPLUS);
+ }
+
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ LEX(PM_TOKEN_PLUS);
+ }
+
+ // - -= -@
+ case '-': {
+ if (lex_state_operator_p(parser)) {
+ lex_state_set(parser, PM_LEX_STATE_ARG);
+
+ if (match(parser, '@')) {
+ LEX(PM_TOKEN_UMINUS);
+ }
+
+ LEX(PM_TOKEN_MINUS);
+ }
+
+ if (match(parser, '=')) {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ LEX(PM_TOKEN_MINUS_EQUAL);
+ }
+
+ if (match(parser, '>')) {
+ lex_state_set(parser, PM_LEX_STATE_ENDFN);
+ LEX(PM_TOKEN_MINUS_GREATER);
+ }
+
+ bool spcarg = lex_state_spcarg_p(parser, space_seen);
+ bool is_beg = lex_state_beg_p(parser);
+ if (!is_beg && spcarg) {
+ pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_MINUS);
+ }
+
+ if (is_beg || spcarg) {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ LEX(pm_char_is_decimal_digit(peek(parser)) ? PM_TOKEN_UMINUS_NUM : PM_TOKEN_UMINUS);
+ }
+
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ LEX(PM_TOKEN_MINUS);
+ }
+
+ // . .. ...
+ case '.': {
+ bool beg_p = lex_state_beg_p(parser);
+
+ if (match(parser, '.')) {
+ if (match(parser, '.')) {
+ // If we're _not_ inside a range within default parameters
+ if (
+ !context_p(parser, PM_CONTEXT_DEFAULT_PARAMS) &&
+ context_p(parser, PM_CONTEXT_DEF_PARAMS)
+ ) {
+ if (lex_state_p(parser, PM_LEX_STATE_END)) {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ } else {
+ lex_state_set(parser, PM_LEX_STATE_ENDARG);
+ }
+ LEX(PM_TOKEN_UDOT_DOT_DOT);
+ }
+
+ if (parser->enclosure_nesting == 0 && parser_end_of_line_p(parser)) {
+ pm_parser_warn_token(parser, &parser->current, PM_WARN_DOT_DOT_DOT_EOL);
+ }
+
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ LEX(beg_p ? PM_TOKEN_UDOT_DOT_DOT : PM_TOKEN_DOT_DOT_DOT);
+ }
+
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ LEX(beg_p ? PM_TOKEN_UDOT_DOT : PM_TOKEN_DOT_DOT);
+ }
+
+ lex_state_set(parser, PM_LEX_STATE_DOT);
+ LEX(PM_TOKEN_DOT);
+ }
+
+ // integer
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': {
+ pm_token_type_t type = lex_numeric(parser);
+ lex_state_set(parser, PM_LEX_STATE_END);
+ LEX(type);
+ }
+
+ // :: symbol
+ case ':':
+ if (match(parser, ':')) {
+ if (lex_state_beg_p(parser) || lex_state_p(parser, PM_LEX_STATE_CLASS) || (lex_state_p(parser, PM_LEX_STATE_ARG_ANY) && space_seen)) {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ LEX(PM_TOKEN_UCOLON_COLON);
+ }
+
+ lex_state_set(parser, PM_LEX_STATE_DOT);
+ LEX(PM_TOKEN_COLON_COLON);
+ }
+
+ if (lex_state_end_p(parser) || pm_char_is_whitespace(peek(parser)) || peek(parser) == '#') {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ LEX(PM_TOKEN_COLON);
+ }
+
+ if (peek(parser) == '"' || peek(parser) == '\'') {
+ lex_mode_push_string(parser, peek(parser) == '"', false, '\0', *parser->current.end);
+ parser->current.end++;
+ }
+
+ lex_state_set(parser, PM_LEX_STATE_FNAME);
+ LEX(PM_TOKEN_SYMBOL_BEGIN);
+
+ // / /=
+ case '/':
+ if (lex_state_beg_p(parser)) {
+ lex_mode_push_regexp(parser, '\0', '/');
+ LEX(PM_TOKEN_REGEXP_BEGIN);
+ }
+
+ if (match(parser, '=')) {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ LEX(PM_TOKEN_SLASH_EQUAL);
+ }
+
+ if (lex_state_spcarg_p(parser, space_seen)) {
+ pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_SLASH);
+ lex_mode_push_regexp(parser, '\0', '/');
+ LEX(PM_TOKEN_REGEXP_BEGIN);
+ }
+
+ if (lex_state_operator_p(parser)) {
+ lex_state_set(parser, PM_LEX_STATE_ARG);
+ } else {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ }
+
+ LEX(PM_TOKEN_SLASH);
+
+ // ^ ^=
+ case '^':
+ if (lex_state_operator_p(parser)) {
+ lex_state_set(parser, PM_LEX_STATE_ARG);
+ } else {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ }
+ LEX(match(parser, '=') ? PM_TOKEN_CARET_EQUAL : PM_TOKEN_CARET);
+
+ // ~ ~@
+ case '~':
+ if (lex_state_operator_p(parser)) {
+ (void) match(parser, '@');
+ lex_state_set(parser, PM_LEX_STATE_ARG);
+ } else {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ }
+
+ LEX(PM_TOKEN_TILDE);
+
+ // % %= %i %I %q %Q %w %W
+ case '%': {
+ // If there is no subsequent character then we have an
+ // invalid token. We're going to say it's the percent
+ // operator because we don't want to move into the string
+ // lex mode unnecessarily.
+ if ((lex_state_beg_p(parser) || lex_state_arg_p(parser)) && (parser->current.end >= parser->end)) {
+ pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT);
+ LEX(PM_TOKEN_PERCENT);
+ }
+
+ if (!lex_state_beg_p(parser) && match(parser, '=')) {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ LEX(PM_TOKEN_PERCENT_EQUAL);
+ } else if (
+ lex_state_beg_p(parser) ||
+ (lex_state_p(parser, PM_LEX_STATE_FITEM) && (peek(parser) == 's')) ||
+ lex_state_spcarg_p(parser, space_seen)
+ ) {
+ if (!parser->encoding->alnum_char(parser->current.end, parser->end - parser->current.end)) {
+ if (*parser->current.end >= 0x80) {
+ pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT);
+ }
+
+ const uint8_t delimiter = pm_lex_percent_delimiter(parser);
+ lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
+
+ if (parser->current.end < parser->end) {
+ LEX(PM_TOKEN_STRING_BEGIN);
+ }
+ }
+
+ // Delimiters for %-literals cannot be alphanumeric. We
+ // validate that here.
+ uint8_t delimiter = peek_offset(parser, 1);
+ if (delimiter >= 0x80 || parser->encoding->alnum_char(&delimiter, 1)) {
+ pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT);
+ goto lex_next_token;
+ }
+
+ switch (peek(parser)) {
+ case 'i': {
+ parser->current.end++;
+
+ if (parser->current.end < parser->end) {
+ lex_mode_push_list(parser, false, pm_lex_percent_delimiter(parser));
+ } else {
+ lex_mode_push_list_eof(parser);
+ }
+
+ LEX(PM_TOKEN_PERCENT_LOWER_I);
+ }
+ case 'I': {
+ parser->current.end++;
+
+ if (parser->current.end < parser->end) {
+ lex_mode_push_list(parser, true, pm_lex_percent_delimiter(parser));
+ } else {
+ lex_mode_push_list_eof(parser);
+ }
+
+ LEX(PM_TOKEN_PERCENT_UPPER_I);
+ }
+ case 'r': {
+ parser->current.end++;
+
+ if (parser->current.end < parser->end) {
+ const uint8_t delimiter = pm_lex_percent_delimiter(parser);
+ lex_mode_push_regexp(parser, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
+ } else {
+ lex_mode_push_regexp(parser, '\0', '\0');
+ }
+
+ LEX(PM_TOKEN_REGEXP_BEGIN);
+ }
+ case 'q': {
+ parser->current.end++;
+
+ if (parser->current.end < parser->end) {
+ const uint8_t delimiter = pm_lex_percent_delimiter(parser);
+ lex_mode_push_string(parser, false, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
+ } else {
+ lex_mode_push_string_eof(parser);
+ }
+
+ LEX(PM_TOKEN_STRING_BEGIN);
+ }
+ case 'Q': {
+ parser->current.end++;
+
+ if (parser->current.end < parser->end) {
+ const uint8_t delimiter = pm_lex_percent_delimiter(parser);
+ lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
+ } else {
+ lex_mode_push_string_eof(parser);
+ }
+
+ LEX(PM_TOKEN_STRING_BEGIN);
+ }
+ case 's': {
+ parser->current.end++;
+
+ if (parser->current.end < parser->end) {
+ const uint8_t delimiter = pm_lex_percent_delimiter(parser);
+ lex_mode_push_string(parser, false, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
+ lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM);
+ } else {
+ lex_mode_push_string_eof(parser);
+ }
+
+ LEX(PM_TOKEN_SYMBOL_BEGIN);
+ }
+ case 'w': {
+ parser->current.end++;
+
+ if (parser->current.end < parser->end) {
+ lex_mode_push_list(parser, false, pm_lex_percent_delimiter(parser));
+ } else {
+ lex_mode_push_list_eof(parser);
+ }
+
+ LEX(PM_TOKEN_PERCENT_LOWER_W);
+ }
+ case 'W': {
+ parser->current.end++;
+
+ if (parser->current.end < parser->end) {
+ lex_mode_push_list(parser, true, pm_lex_percent_delimiter(parser));
+ } else {
+ lex_mode_push_list_eof(parser);
+ }
+
+ LEX(PM_TOKEN_PERCENT_UPPER_W);
+ }
+ case 'x': {
+ parser->current.end++;
+
+ if (parser->current.end < parser->end) {
+ const uint8_t delimiter = pm_lex_percent_delimiter(parser);
+ lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
+ } else {
+ lex_mode_push_string_eof(parser);
+ }
+
+ LEX(PM_TOKEN_PERCENT_LOWER_X);
+ }
+ default:
+ // If we get to this point, then we have a % that is completely
+ // unparsable. In this case we'll just drop it from the parser
+ // and skip past it and hope that the next token is something
+ // that we can parse.
+ pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT);
+ goto lex_next_token;
+ }
+ }
+
+ lex_state_set(parser, lex_state_operator_p(parser) ? PM_LEX_STATE_ARG : PM_LEX_STATE_BEG);
+ LEX(PM_TOKEN_PERCENT);
+ }
+
+ // global variable
+ case '$': {
+ pm_token_type_t type = lex_global_variable(parser);
+
+ // If we're lexing an embedded variable, then we need to pop back into
+ // the parent lex context.
+ if (parser->lex_modes.current->mode == PM_LEX_EMBVAR) {
+ lex_mode_pop(parser);
+ }
+
+ lex_state_set(parser, PM_LEX_STATE_END);
+ LEX(type);
+ }
+
+ // instance variable, class variable
+ case '@':
+ lex_state_set(parser, parser->lex_state & PM_LEX_STATE_FNAME ? PM_LEX_STATE_ENDFN : PM_LEX_STATE_END);
+ LEX(lex_at_variable(parser));
+
+ default: {
+ if (*parser->current.start != '_') {
+ size_t width = char_is_identifier_start(parser, parser->current.start);
+
+ // If this isn't the beginning of an identifier, then
+ // it's an invalid token as we've exhausted all of the
+ // other options. We'll skip past it and return the next
+ // token after adding an appropriate error message.
+ if (!width) {
+ if (*parser->current.start >= 0x80) {
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_INVALID_MULTIBYTE_CHARACTER, *parser->current.start);
+ } else if (*parser->current.start == '\\') {
+ switch (peek_at(parser, parser->current.start + 1)) {
+ case ' ':
+ parser->current.end++;
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped space");
+ break;
+ case '\f':
+ parser->current.end++;
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped form feed");
+ break;
+ case '\t':
+ parser->current.end++;
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped horizontal tab");
+ break;
+ case '\v':
+ parser->current.end++;
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped vertical tab");
+ break;
+ case '\r':
+ if (peek_at(parser, parser->current.start + 2) != '\n') {
+ parser->current.end++;
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped carriage return");
+ break;
+ }
+ /* fallthrough */
+ default:
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "backslash");
+ break;
+ }
+ } else if (char_is_ascii_printable(*parser->current.start)) {
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_INVALID_PRINTABLE_CHARACTER, *parser->current.start);
+ } else {
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_INVALID_CHARACTER, *parser->current.start);
+ }
+
+ goto lex_next_token;
+ }
+
+ parser->current.end = parser->current.start + width;
+ }
+
+ pm_token_type_t type = lex_identifier(parser, previous_command_start);
+
+ // If we've hit a __END__ and it was at the start of the
+ // line or the start of the file and it is followed by
+ // either a \n or a \r\n, then this is the last token of the
+ // file.
+ if (
+ ((parser->current.end - parser->current.start) == 7) &&
+ current_token_starts_line(parser) &&
+ (memcmp(parser->current.start, "__END__", 7) == 0) &&
+ (parser->current.end == parser->end || match_eol(parser))
+ ) {
+ // Since we know we're about to add an __END__ comment,
+ // we know we need to add all of the newlines to get the
+ // correct column information for it.
+ const uint8_t *cursor = parser->current.end;
+ while ((cursor = next_newline(cursor, parser->end - cursor)) != NULL) {
+ pm_newline_list_append(&parser->newline_list, cursor++);
+ }
+
+ parser->current.end = parser->end;
+ parser->current.type = PM_TOKEN___END__;
+ parser_lex_callback(parser);
+
+ parser->data_loc.start = parser->current.start;
+ parser->data_loc.end = parser->current.end;
+
+ LEX(PM_TOKEN_EOF);
+ }
+
+ pm_lex_state_t last_state = parser->lex_state;
+
+ if (type == PM_TOKEN_IDENTIFIER || type == PM_TOKEN_CONSTANT || type == PM_TOKEN_METHOD_NAME) {
+ if (lex_state_p(parser, PM_LEX_STATE_BEG_ANY | PM_LEX_STATE_ARG_ANY | PM_LEX_STATE_DOT)) {
+ if (previous_command_start) {
+ lex_state_set(parser, PM_LEX_STATE_CMDARG);
+ } else {
+ lex_state_set(parser, PM_LEX_STATE_ARG);
+ }
+ } else if (parser->lex_state == PM_LEX_STATE_FNAME) {
+ lex_state_set(parser, PM_LEX_STATE_ENDFN);
+ } else {
+ lex_state_set(parser, PM_LEX_STATE_END);
+ }
+ }
+
+ if (
+ !(last_state & (PM_LEX_STATE_DOT | PM_LEX_STATE_FNAME)) &&
+ (type == PM_TOKEN_IDENTIFIER) &&
+ ((pm_parser_local_depth(parser, &parser->current) != -1) ||
+ pm_token_is_numbered_parameter(parser->current.start, parser->current.end))
+ ) {
+ lex_state_set(parser, PM_LEX_STATE_END | PM_LEX_STATE_LABEL);
+ }
+
+ LEX(type);
+ }
+ }
+ }
+ case PM_LEX_LIST: {
+ if (parser->next_start != NULL) {
+ parser->current.end = parser->next_start;
+ parser->next_start = NULL;
+ }
+
+ // First we'll set the beginning of the token.
+ parser->current.start = parser->current.end;
+
+ // If there's any whitespace at the start of the list, then we're
+ // going to trim it off the beginning and create a new token.
+ size_t whitespace;
+
+ if (parser->heredoc_end) {
+ whitespace = pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end);
+ if (peek_offset(parser, (ptrdiff_t)whitespace) == '\n') {
+ whitespace += 1;
+ }
+ } else {
+ whitespace = pm_strspn_whitespace_newlines(parser->current.end, parser->end - parser->current.end, &parser->newline_list);
+ }
+
+ if (whitespace > 0) {
+ parser->current.end += whitespace;
+ if (peek_offset(parser, -1) == '\n') {
+ // mutates next_start
+ parser_flush_heredoc_end(parser);
+ }
+ LEX(PM_TOKEN_WORDS_SEP);
+ }
+
+ // We'll check if we're at the end of the file. If we are, then we
+ // need to return the EOF token.
+ if (parser->current.end >= parser->end) {
+ LEX(PM_TOKEN_EOF);
+ }
+
+ // Here we'll get a list of the places where strpbrk should break,
+ // and then find the first one.
+ pm_lex_mode_t *lex_mode = parser->lex_modes.current;
+ const uint8_t *breakpoints = lex_mode->as.list.breakpoints;
+ const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
+
+ // If we haven't found an escape yet, then this buffer will be
+ // unallocated since we can refer directly to the source string.
+ pm_token_buffer_t token_buffer = { 0 };
+
+ while (breakpoint != NULL) {
+ // If we hit whitespace, then we must have received content by
+ // now, so we can return an element of the list.
+ if (pm_char_is_whitespace(*breakpoint)) {
+ parser->current.end = breakpoint;
+ pm_token_buffer_flush(parser, &token_buffer);
+ LEX(PM_TOKEN_STRING_CONTENT);
+ }
+
+ // If we hit the terminator, we need to check which token to
+ // return.
+ if (*breakpoint == lex_mode->as.list.terminator) {
+ // If this terminator doesn't actually close the list, then
+ // we need to continue on past it.
+ if (lex_mode->as.list.nesting > 0) {
+ parser->current.end = breakpoint + 1;
+ breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
+ lex_mode->as.list.nesting--;
+ continue;
+ }
+
+ // If we've hit the terminator and we've already skipped
+ // past content, then we can return a list node.
+ if (breakpoint > parser->current.start) {
+ parser->current.end = breakpoint;
+ pm_token_buffer_flush(parser, &token_buffer);
+ LEX(PM_TOKEN_STRING_CONTENT);
+ }
+
+ // Otherwise, switch back to the default state and return
+ // the end of the list.
+ parser->current.end = breakpoint + 1;
+ lex_mode_pop(parser);
+ lex_state_set(parser, PM_LEX_STATE_END);
+ LEX(PM_TOKEN_STRING_END);
+ }
+
+ // If we hit a null byte, skip directly past it.
+ if (*breakpoint == '\0') {
+ breakpoint = pm_strpbrk(parser, breakpoint + 1, breakpoints, parser->end - (breakpoint + 1), true);
+ continue;
+ }
+
+ // If we hit escapes, then we need to treat the next token
+ // literally. In this case we'll skip past the next character
+ // and find the next breakpoint.
+ if (*breakpoint == '\\') {
+ parser->current.end = breakpoint + 1;
+
+ // If we've hit the end of the file, then break out of the
+ // loop by setting the breakpoint to NULL.
+ if (parser->current.end == parser->end) {
+ breakpoint = NULL;
+ continue;
+ }
+
+ pm_token_buffer_escape(parser, &token_buffer);
+ uint8_t peeked = peek(parser);
+
+ switch (peeked) {
+ case ' ':
+ case '\f':
+ case '\t':
+ case '\v':
+ case '\\':
+ pm_token_buffer_push_byte(&token_buffer, peeked);
+ parser->current.end++;
+ break;
+ case '\r':
+ parser->current.end++;
+ if (peek(parser) != '\n') {
+ pm_token_buffer_push_byte(&token_buffer, '\r');
+ break;
+ }
+ /* fallthrough */
+ case '\n':
+ pm_token_buffer_push_byte(&token_buffer, '\n');
+
+ if (parser->heredoc_end) {
+ // ... if we are on the same line as a heredoc,
+ // flush the heredoc and continue parsing after
+ // heredoc_end.
+ parser_flush_heredoc_end(parser);
+ pm_token_buffer_copy(parser, &token_buffer);
+ LEX(PM_TOKEN_STRING_CONTENT);
+ } else {
+ // ... else track the newline.
+ pm_newline_list_append(&parser->newline_list, parser->current.end);
+ }
+
+ parser->current.end++;
+ break;
+ default:
+ if (peeked == lex_mode->as.list.incrementor || peeked == lex_mode->as.list.terminator) {
+ pm_token_buffer_push_byte(&token_buffer, peeked);
+ parser->current.end++;
+ } else if (lex_mode->as.list.interpolation) {
+ escape_read(parser, &token_buffer.buffer, NULL, PM_ESCAPE_FLAG_NONE);
+ } else {
+ pm_token_buffer_push_byte(&token_buffer, '\\');
+ pm_token_buffer_push_escaped(&token_buffer, parser);
+ }
+
+ break;
+ }
+
+ token_buffer.cursor = parser->current.end;
+ breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
+ continue;
+ }
+
+ // If we hit a #, then we will attempt to lex interpolation.
+ if (*breakpoint == '#') {
+ pm_token_type_t type = lex_interpolation(parser, breakpoint);
+
+ if (type == PM_TOKEN_NOT_PROVIDED) {
+ // If we haven't returned at this point then we had something
+ // that looked like an interpolated class or instance variable
+ // like "#@" but wasn't actually. In this case we'll just skip
+ // to the next breakpoint.
+ breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
+ continue;
+ }
+
+ if (type == PM_TOKEN_STRING_CONTENT) {
+ pm_token_buffer_flush(parser, &token_buffer);
+ }
+
+ LEX(type);
+ }
+
+ // If we've hit the incrementor, then we need to skip past it
+ // and find the next breakpoint.
+ assert(*breakpoint == lex_mode->as.list.incrementor);
+ parser->current.end = breakpoint + 1;
+ breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
+ lex_mode->as.list.nesting++;
+ continue;
+ }
+
+ if (parser->current.end > parser->current.start) {
+ pm_token_buffer_flush(parser, &token_buffer);
+ LEX(PM_TOKEN_STRING_CONTENT);
+ }
+
+ // If we were unable to find a breakpoint, then this token hits the
+ // end of the file.
+ parser->current.end = parser->end;
+ pm_token_buffer_flush(parser, &token_buffer);
+ LEX(PM_TOKEN_STRING_CONTENT);
+ }
+ case PM_LEX_REGEXP: {
+ // First, we'll set to start of this token to be the current end.
+ if (parser->next_start == NULL) {
+ parser->current.start = parser->current.end;
+ } else {
+ parser->current.start = parser->next_start;
+ parser->current.end = parser->next_start;
+ parser->next_start = NULL;
+ }
+
+ // We'll check if we're at the end of the file. If we are, then we
+ // need to return the EOF token.
+ if (parser->current.end >= parser->end) {
+ LEX(PM_TOKEN_EOF);
+ }
+
+ // Get a reference to the current mode.
+ pm_lex_mode_t *lex_mode = parser->lex_modes.current;
+
+ // These are the places where we need to split up the content of the
+ // regular expression. We'll use strpbrk to find the first of these
+ // characters.
+ const uint8_t *breakpoints = lex_mode->as.regexp.breakpoints;
+ const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
+ pm_regexp_token_buffer_t token_buffer = { 0 };
+
+ while (breakpoint != NULL) {
+ // If we hit the terminator, we need to determine what kind of
+ // token to return.
+ if (*breakpoint == lex_mode->as.regexp.terminator) {
+ if (lex_mode->as.regexp.nesting > 0) {
+ parser->current.end = breakpoint + 1;
+ breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
+ lex_mode->as.regexp.nesting--;
+ continue;
+ }
+
+ // Here we've hit the terminator. If we have already consumed
+ // content then we need to return that content as string content
+ // first.
+ if (breakpoint > parser->current.start) {
+ parser->current.end = breakpoint;
+ pm_regexp_token_buffer_flush(parser, &token_buffer);
+ LEX(PM_TOKEN_STRING_CONTENT);
+ }
+
+ // Check here if we need to track the newline.
+ size_t eol_length = match_eol_at(parser, breakpoint);
+ if (eol_length) {
+ parser->current.end = breakpoint + eol_length;
+ pm_newline_list_append(&parser->newline_list, parser->current.end - 1);
+ } else {
+ parser->current.end = breakpoint + 1;
+ }
+
+ // Since we've hit the terminator of the regular expression,
+ // we now need to parse the options.
+ parser->current.end += pm_strspn_regexp_option(parser->current.end, parser->end - parser->current.end);
+
+ lex_mode_pop(parser);
+ lex_state_set(parser, PM_LEX_STATE_END);
+ LEX(PM_TOKEN_REGEXP_END);
+ }
+
+ // If we've hit the incrementor, then we need to skip past it
+ // and find the next breakpoint.
+ if (*breakpoint && *breakpoint == lex_mode->as.regexp.incrementor) {
+ parser->current.end = breakpoint + 1;
+ breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
+ lex_mode->as.regexp.nesting++;
+ continue;
+ }
+
+ switch (*breakpoint) {
+ case '\0':
+ // If we hit a null byte, skip directly past it.
+ parser->current.end = breakpoint + 1;
+ breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
+ break;
+ case '\r':
+ if (peek_at(parser, breakpoint + 1) != '\n') {
+ parser->current.end = breakpoint + 1;
+ breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
+ break;
+ }
+
+ breakpoint++;
+ parser->current.end = breakpoint;
+ pm_regexp_token_buffer_escape(parser, &token_buffer);
+ token_buffer.base.cursor = breakpoint;
+
+ /* fallthrough */
+ case '\n':
+ // If we've hit a newline, then we need to track that in
+ // the list of newlines.
+ if (parser->heredoc_end == NULL) {
+ pm_newline_list_append(&parser->newline_list, breakpoint);
+ parser->current.end = breakpoint + 1;
+ breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
+ break;
+ }
+
+ parser->current.end = breakpoint + 1;
+ parser_flush_heredoc_end(parser);
+ pm_regexp_token_buffer_flush(parser, &token_buffer);
+ LEX(PM_TOKEN_STRING_CONTENT);
+ case '\\': {
+ // If we hit escapes, then we need to treat the next
+ // token literally. In this case we'll skip past the
+ // next character and find the next breakpoint.
+ parser->current.end = breakpoint + 1;
+
+ // If we've hit the end of the file, then break out of
+ // the loop by setting the breakpoint to NULL.
+ if (parser->current.end == parser->end) {
+ breakpoint = NULL;
+ break;
+ }
+
+ pm_regexp_token_buffer_escape(parser, &token_buffer);
+ uint8_t peeked = peek(parser);
+
+ switch (peeked) {
+ case '\r':
+ parser->current.end++;
+ if (peek(parser) != '\n') {
+ if (lex_mode->as.regexp.terminator != '\r') {
+ pm_token_buffer_push_byte(&token_buffer.base, '\\');
+ }
+ pm_regexp_token_buffer_push_byte(&token_buffer, '\r');
+ pm_token_buffer_push_byte(&token_buffer.base, '\r');
+ break;
+ }
+ /* fallthrough */
+ case '\n':
+ if (parser->heredoc_end) {
+ // ... if we are on the same line as a heredoc,
+ // flush the heredoc and continue parsing after
+ // heredoc_end.
+ parser_flush_heredoc_end(parser);
+ pm_regexp_token_buffer_copy(parser, &token_buffer);
+ LEX(PM_TOKEN_STRING_CONTENT);
+ } else {
+ // ... else track the newline.
+ pm_newline_list_append(&parser->newline_list, parser->current.end);
+ }
+
+ parser->current.end++;
+ break;
+ case 'c':
+ case 'C':
+ case 'M':
+ case 'u':
+ case 'x':
+ escape_read(parser, &token_buffer.regexp_buffer, &token_buffer.base.buffer, PM_ESCAPE_FLAG_REGEXP);
+ break;
+ default:
+ if (lex_mode->as.regexp.terminator == peeked) {
+ // Some characters when they are used as the
+ // terminator also receive an escape. They are
+ // enumerated here.
+ switch (peeked) {
+ case '$': case ')': case '*': case '+':
+ case '.': case '>': case '?': case ']':
+ case '^': case '|': case '}':
+ pm_token_buffer_push_byte(&token_buffer.base, '\\');
+ break;
+ default:
+ break;
+ }
+
+ pm_regexp_token_buffer_push_byte(&token_buffer, peeked);
+ pm_token_buffer_push_byte(&token_buffer.base, peeked);
+ parser->current.end++;
+ break;
+ }
+
+ if (peeked < 0x80) pm_token_buffer_push_byte(&token_buffer.base, '\\');
+ pm_regexp_token_buffer_push_escaped(&token_buffer, parser);
+ break;
+ }
+
+ token_buffer.base.cursor = parser->current.end;
+ breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
+ break;
+ }
+ case '#': {
+ // If we hit a #, then we will attempt to lex
+ // interpolation.
+ pm_token_type_t type = lex_interpolation(parser, breakpoint);
+
+ if (type == PM_TOKEN_NOT_PROVIDED) {
+ // If we haven't returned at this point then we had
+ // something that looked like an interpolated class or
+ // instance variable like "#@" but wasn't actually. In
+ // this case we'll just skip to the next breakpoint.
+ breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
+ break;
+ }
+
+ if (type == PM_TOKEN_STRING_CONTENT) {
+ pm_regexp_token_buffer_flush(parser, &token_buffer);
+ }
+
+ LEX(type);
+ }
+ default:
+ assert(false && "unreachable");
+ break;
+ }
+ }
+
+ if (parser->current.end > parser->current.start) {
+ pm_regexp_token_buffer_flush(parser, &token_buffer);
+ LEX(PM_TOKEN_STRING_CONTENT);
+ }
+
+ // If we were unable to find a breakpoint, then this token hits the
+ // end of the file.
+ parser->current.end = parser->end;
+ pm_regexp_token_buffer_flush(parser, &token_buffer);
+ LEX(PM_TOKEN_STRING_CONTENT);
+ }
+ case PM_LEX_STRING: {
+ // First, we'll set to start of this token to be the current end.
+ if (parser->next_start == NULL) {
+ parser->current.start = parser->current.end;
+ } else {
+ parser->current.start = parser->next_start;
+ parser->current.end = parser->next_start;
+ parser->next_start = NULL;
+ }
+
+ // We'll check if we're at the end of the file. If we are, then we need to
+ // return the EOF token.
+ if (parser->current.end >= parser->end) {
+ LEX(PM_TOKEN_EOF);
+ }
+
+ // These are the places where we need to split up the content of the
+ // string. We'll use strpbrk to find the first of these characters.
+ pm_lex_mode_t *lex_mode = parser->lex_modes.current;
+ const uint8_t *breakpoints = lex_mode->as.string.breakpoints;
+ const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
+
+ // If we haven't found an escape yet, then this buffer will be
+ // unallocated since we can refer directly to the source string.
+ pm_token_buffer_t token_buffer = { 0 };
+
+ while (breakpoint != NULL) {
+ // If we hit the incrementor, then we'll increment then nesting and
+ // continue lexing.
+ if (lex_mode->as.string.incrementor != '\0' && *breakpoint == lex_mode->as.string.incrementor) {
+ lex_mode->as.string.nesting++;
+ parser->current.end = breakpoint + 1;
+ breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
+ continue;
+ }
+
+ // Note that we have to check the terminator here first because we could
+ // potentially be parsing a % string that has a # character as the
+ // terminator.
+ if (*breakpoint == lex_mode->as.string.terminator) {
+ // If this terminator doesn't actually close the string, then we need
+ // to continue on past it.
+ if (lex_mode->as.string.nesting > 0) {
+ parser->current.end = breakpoint + 1;
+ breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
+ lex_mode->as.string.nesting--;
+ continue;
+ }
+
+ // Here we've hit the terminator. If we have already consumed content
+ // then we need to return that content as string content first.
+ if (breakpoint > parser->current.start) {
+ parser->current.end = breakpoint;
+ pm_token_buffer_flush(parser, &token_buffer);
+ LEX(PM_TOKEN_STRING_CONTENT);
+ }
+
+ // Otherwise we need to switch back to the parent lex mode and
+ // return the end of the string.
+ size_t eol_length = match_eol_at(parser, breakpoint);
+ if (eol_length) {
+ parser->current.end = breakpoint + eol_length;
+ pm_newline_list_append(&parser->newline_list, parser->current.end - 1);
+ } else {
+ parser->current.end = breakpoint + 1;
+ }
+
+ if (lex_mode->as.string.label_allowed && (peek(parser) == ':') && (peek_offset(parser, 1) != ':')) {
+ parser->current.end++;
+ lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
+ lex_mode_pop(parser);
+ LEX(PM_TOKEN_LABEL_END);
+ }
+
+ lex_state_set(parser, PM_LEX_STATE_END);
+ lex_mode_pop(parser);
+ LEX(PM_TOKEN_STRING_END);
+ }
+
+ switch (*breakpoint) {
+ case '\0':
+ // Skip directly past the null character.
+ parser->current.end = breakpoint + 1;
+ breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
+ break;
+ case '\r':
+ if (peek_at(parser, breakpoint + 1) != '\n') {
+ parser->current.end = breakpoint + 1;
+ breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
+ break;
+ }
+
+ // If we hit a \r\n sequence, then we need to treat it
+ // as a newline.
+ breakpoint++;
+ parser->current.end = breakpoint;
+ pm_token_buffer_escape(parser, &token_buffer);
+ token_buffer.cursor = breakpoint;
+
+ /* fallthrough */
+ case '\n':
+ // When we hit a newline, we need to flush any potential
+ // heredocs. Note that this has to happen after we check
+ // for the terminator in case the terminator is a
+ // newline character.
+ if (parser->heredoc_end == NULL) {
+ pm_newline_list_append(&parser->newline_list, breakpoint);
+ parser->current.end = breakpoint + 1;
+ breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
+ break;
+ }
+
+ parser->current.end = breakpoint + 1;
+ parser_flush_heredoc_end(parser);
+ pm_token_buffer_flush(parser, &token_buffer);
+ LEX(PM_TOKEN_STRING_CONTENT);
+ case '\\': {
+ // Here we hit escapes.
+ parser->current.end = breakpoint + 1;
+
+ // If we've hit the end of the file, then break out of
+ // the loop by setting the breakpoint to NULL.
+ if (parser->current.end == parser->end) {
+ breakpoint = NULL;
+ continue;
+ }
+
+ pm_token_buffer_escape(parser, &token_buffer);
+ uint8_t peeked = peek(parser);
+
+ switch (peeked) {
+ case '\\':
+ pm_token_buffer_push_byte(&token_buffer, '\\');
+ parser->current.end++;
+ break;
+ case '\r':
+ parser->current.end++;
+ if (peek(parser) != '\n') {
+ if (!lex_mode->as.string.interpolation) {
+ pm_token_buffer_push_byte(&token_buffer, '\\');
+ }
+ pm_token_buffer_push_byte(&token_buffer, '\r');
+ break;
+ }
+ /* fallthrough */
+ case '\n':
+ if (!lex_mode->as.string.interpolation) {
+ pm_token_buffer_push_byte(&token_buffer, '\\');
+ pm_token_buffer_push_byte(&token_buffer, '\n');
+ }
+
+ if (parser->heredoc_end) {
+ // ... if we are on the same line as a heredoc,
+ // flush the heredoc and continue parsing after
+ // heredoc_end.
+ parser_flush_heredoc_end(parser);
+ pm_token_buffer_copy(parser, &token_buffer);
+ LEX(PM_TOKEN_STRING_CONTENT);
+ } else {
+ // ... else track the newline.
+ pm_newline_list_append(&parser->newline_list, parser->current.end);
+ }
+
+ parser->current.end++;
+ break;
+ default:
+ if (lex_mode->as.string.incrementor != '\0' && peeked == lex_mode->as.string.incrementor) {
+ pm_token_buffer_push_byte(&token_buffer, peeked);
+ parser->current.end++;
+ } else if (lex_mode->as.string.terminator != '\0' && peeked == lex_mode->as.string.terminator) {
+ pm_token_buffer_push_byte(&token_buffer, peeked);
+ parser->current.end++;
+ } else if (lex_mode->as.string.interpolation) {
+ escape_read(parser, &token_buffer.buffer, NULL, PM_ESCAPE_FLAG_NONE);
+ } else {
+ pm_token_buffer_push_byte(&token_buffer, '\\');
+ pm_token_buffer_push_escaped(&token_buffer, parser);
+ }
+
+ break;
+ }
+
+ token_buffer.cursor = parser->current.end;
+ breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
+ break;
+ }
+ case '#': {
+ pm_token_type_t type = lex_interpolation(parser, breakpoint);
+
+ if (type == PM_TOKEN_NOT_PROVIDED) {
+ // If we haven't returned at this point then we had something that
+ // looked like an interpolated class or instance variable like "#@"
+ // but wasn't actually. In this case we'll just skip to the next
+ // breakpoint.
+ breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
+ break;
+ }
+
+ if (type == PM_TOKEN_STRING_CONTENT) {
+ pm_token_buffer_flush(parser, &token_buffer);
+ }
+
+ LEX(type);
+ }
+ default:
+ assert(false && "unreachable");
+ }
+ }
+
+ if (parser->current.end > parser->current.start) {
+ pm_token_buffer_flush(parser, &token_buffer);
+ LEX(PM_TOKEN_STRING_CONTENT);
+ }
+
+ // If we've hit the end of the string, then this is an unterminated
+ // string. In that case we'll return a string content token.
+ parser->current.end = parser->end;
+ pm_token_buffer_flush(parser, &token_buffer);
+ LEX(PM_TOKEN_STRING_CONTENT);
+ }
+ case PM_LEX_HEREDOC: {
+ // First, we'll set to start of this token.
+ if (parser->next_start == NULL) {
+ parser->current.start = parser->current.end;
+ } else {
+ parser->current.start = parser->next_start;
+ parser->current.end = parser->next_start;
+ parser->heredoc_end = NULL;
+ parser->next_start = NULL;
+ }
+
+ // Now let's grab the information about the identifier off of the
+ // current lex mode.
+ pm_lex_mode_t *lex_mode = parser->lex_modes.current;
+
+ bool line_continuation = lex_mode->as.heredoc.line_continuation;
+ lex_mode->as.heredoc.line_continuation = false;
+
+ // We'll check if we're at the end of the file. If we are, then we
+ // will add an error (because we weren't able to find the
+ // terminator) but still continue parsing so that content after the
+ // declaration of the heredoc can be parsed.
+ if (parser->current.end >= parser->end) {
+ pm_parser_err_current(parser, PM_ERR_HEREDOC_TERM);
+ parser->next_start = lex_mode->as.heredoc.next_start;
+ parser->heredoc_end = parser->current.end;
+ lex_state_set(parser, PM_LEX_STATE_END);
+ LEX(PM_TOKEN_HEREDOC_END);
+ }
+
+ const uint8_t *ident_start = lex_mode->as.heredoc.ident_start;
+ size_t ident_length = lex_mode->as.heredoc.ident_length;
+
+ // If we are immediately following a newline and we have hit the
+ // terminator, then we need to return the ending of the heredoc.
+ if (!line_continuation && current_token_starts_line(parser)) {
+ const uint8_t *start = parser->current.start;
+ if (start + ident_length <= parser->end) {
+ const uint8_t *newline = next_newline(start, parser->end - start);
+ const uint8_t *ident_end = newline;
+ const uint8_t *terminator_end = newline;
+
+ if (newline == NULL) {
+ terminator_end = parser->end;
+ ident_end = parser->end;
+ } else {
+ terminator_end++;
+ if (newline[-1] == '\r') {
+ ident_end--; // Remove \r
+ }
+ }
+
+ const uint8_t *terminator_start = ident_end - ident_length;
+ const uint8_t *cursor = start;
+
+ if (
+ lex_mode->as.heredoc.indent == PM_HEREDOC_INDENT_DASH ||
+ lex_mode->as.heredoc.indent == PM_HEREDOC_INDENT_TILDE
+ ) {
+ while (cursor < terminator_start && pm_char_is_inline_whitespace(*cursor)) {
+ cursor++;
+ }
+ }
+
+ if (
+ (cursor == terminator_start) &&
+ (memcmp(terminator_start, ident_start, ident_length) == 0)
+ ) {
+ if (newline != NULL) {
+ pm_newline_list_append(&parser->newline_list, newline);
+ }
+
+ parser->current.end = terminator_end;
+ if (*lex_mode->as.heredoc.next_start == '\\') {
+ parser->next_start = NULL;
+ } else {
+ parser->next_start = lex_mode->as.heredoc.next_start;
+ parser->heredoc_end = parser->current.end;
+ }
+
+ lex_state_set(parser, PM_LEX_STATE_END);
+ LEX(PM_TOKEN_HEREDOC_END);
+ }
+ }
+
+ size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, lex_mode->as.heredoc.indent);
+ if (
+ lex_mode->as.heredoc.indent == PM_HEREDOC_INDENT_TILDE &&
+ (lex_mode->as.heredoc.common_whitespace > whitespace) &&
+ peek_at(parser, start) != '\n'
+ ) {
+ lex_mode->as.heredoc.common_whitespace = whitespace;
+ }
+ }
+
+ // Otherwise we'll be parsing string content. These are the places
+ // where we need to split up the content of the heredoc. We'll use
+ // strpbrk to find the first of these characters.
+ uint8_t breakpoints[] = "\r\n\\#";
+
+ pm_heredoc_quote_t quote = lex_mode->as.heredoc.quote;
+ if (quote == PM_HEREDOC_QUOTE_SINGLE) {
+ breakpoints[3] = '\0';
+ }
+
+ const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
+ pm_token_buffer_t token_buffer = { 0 };
+ bool was_line_continuation = false;
+
+ while (breakpoint != NULL) {
+ switch (*breakpoint) {
+ case '\0':
+ // Skip directly past the null character.
+ parser->current.end = breakpoint + 1;
+ breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
+ break;
+ case '\r':
+ parser->current.end = breakpoint + 1;
+
+ if (peek_at(parser, breakpoint + 1) != '\n') {
+ breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
+ break;
+ }
+
+ // If we hit a \r\n sequence, then we want to replace it
+ // with a single \n character in the final string.
+ breakpoint++;
+ pm_token_buffer_escape(parser, &token_buffer);
+ token_buffer.cursor = breakpoint;
+
+ /* fallthrough */
+ case '\n': {
+ if (parser->heredoc_end != NULL && (parser->heredoc_end > breakpoint)) {
+ parser_flush_heredoc_end(parser);
+ parser->current.end = breakpoint + 1;
+ pm_token_buffer_flush(parser, &token_buffer);
+ LEX(PM_TOKEN_STRING_CONTENT);
+ }
+
+ pm_newline_list_append(&parser->newline_list, breakpoint);
+
+ // If we have a - or ~ heredoc, then we can match after
+ // some leading whitespace.
+ const uint8_t *start = breakpoint + 1;
+
+ if (!was_line_continuation && (start + ident_length <= parser->end)) {
+ // We want to match the terminator starting from the end of the line in case
+ // there is whitespace in the ident such as <<-' DOC' or <<~' DOC'.
+ const uint8_t *newline = next_newline(start, parser->end - start);
+
+ if (newline == NULL) {
+ newline = parser->end;
+ } else if (newline[-1] == '\r') {
+ newline--; // Remove \r
+ }
+
+ // Start of a possible terminator.
+ const uint8_t *terminator_start = newline - ident_length;
+
+ // Cursor to check for the leading whitespace. We skip the
+ // leading whitespace if we have a - or ~ heredoc.
+ const uint8_t *cursor = start;
+
+ if (lex_mode->as.heredoc.indent == PM_HEREDOC_INDENT_DASH ||
+ lex_mode->as.heredoc.indent == PM_HEREDOC_INDENT_TILDE) {
+ while (cursor < terminator_start && pm_char_is_inline_whitespace(*cursor)) {
+ cursor++;
+ }
+ }
+
+ if (
+ cursor == terminator_start &&
+ (memcmp(terminator_start, ident_start, ident_length) == 0)
+ ) {
+ parser->current.end = breakpoint + 1;
+ pm_token_buffer_flush(parser, &token_buffer);
+ LEX(PM_TOKEN_STRING_CONTENT);
+ }
+ }
+
+ size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, lex_mode->as.heredoc.indent);
+
+ // If we have hit a newline that is followed by a valid
+ // terminator, then we need to return the content of the
+ // heredoc here as string content. Then, the next time a
+ // token is lexed, it will match again and return the
+ // end of the heredoc.
+ if (lex_mode->as.heredoc.indent == PM_HEREDOC_INDENT_TILDE) {
+ if ((lex_mode->as.heredoc.common_whitespace > whitespace) && peek_at(parser, start) != '\n') {
+ lex_mode->as.heredoc.common_whitespace = whitespace;
+ }
+
+ parser->current.end = breakpoint + 1;
+
+ if (!was_line_continuation) {
+ pm_token_buffer_flush(parser, &token_buffer);
+ LEX(PM_TOKEN_STRING_CONTENT);
+ }
+ }
+
+ // Otherwise we hit a newline and it wasn't followed by
+ // a terminator, so we can continue parsing.
+ parser->current.end = breakpoint + 1;
+ breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
+ break;
+ }
+ case '\\': {
+ // If we hit an escape, then we need to skip past
+ // however many characters the escape takes up. However
+ // it's important that if \n or \r\n are escaped, we
+ // stop looping before the newline and not after the
+ // newline so that we can still potentially find the
+ // terminator of the heredoc.
+ parser->current.end = breakpoint + 1;
+
+ // If we've hit the end of the file, then break out of
+ // the loop by setting the breakpoint to NULL.
+ if (parser->current.end == parser->end) {
+ breakpoint = NULL;
+ continue;
+ }
+
+ pm_token_buffer_escape(parser, &token_buffer);
+ uint8_t peeked = peek(parser);
+
+ if (quote == PM_HEREDOC_QUOTE_SINGLE) {
+ switch (peeked) {
+ case '\r':
+ parser->current.end++;
+ if (peek(parser) != '\n') {
+ pm_token_buffer_push_byte(&token_buffer, '\\');
+ pm_token_buffer_push_byte(&token_buffer, '\r');
+ break;
+ }
+ /* fallthrough */
+ case '\n':
+ pm_token_buffer_push_byte(&token_buffer, '\\');
+ pm_token_buffer_push_byte(&token_buffer, '\n');
+ token_buffer.cursor = parser->current.end + 1;
+ breakpoint = parser->current.end;
+ continue;
+ default:
+ pm_token_buffer_push_byte(&token_buffer, '\\');
+ pm_token_buffer_push_escaped(&token_buffer, parser);
+ break;
+ }
+ } else {
+ switch (peeked) {
+ case '\r':
+ parser->current.end++;
+ if (peek(parser) != '\n') {
+ pm_token_buffer_push_byte(&token_buffer, '\r');
+ break;
+ }
+ /* fallthrough */
+ case '\n':
+ // If we are in a tilde here, we should
+ // break out of the loop and return the
+ // string content.
+ if (lex_mode->as.heredoc.indent == PM_HEREDOC_INDENT_TILDE) {
+ const uint8_t *end = parser->current.end;
+ pm_newline_list_append(&parser->newline_list, end);
+
+ // Here we want the buffer to only
+ // include up to the backslash.
+ parser->current.end = breakpoint;
+ pm_token_buffer_flush(parser, &token_buffer);
+
+ // Now we can advance the end of the
+ // token past the newline.
+ parser->current.end = end + 1;
+ lex_mode->as.heredoc.line_continuation = true;
+ LEX(PM_TOKEN_STRING_CONTENT);
+ }
+
+ was_line_continuation = true;
+ token_buffer.cursor = parser->current.end + 1;
+ breakpoint = parser->current.end;
+ continue;
+ default:
+ escape_read(parser, &token_buffer.buffer, NULL, PM_ESCAPE_FLAG_NONE);
+ break;
+ }
+ }
+
+ token_buffer.cursor = parser->current.end;
+ breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
+ break;
+ }
+ case '#': {
+ pm_token_type_t type = lex_interpolation(parser, breakpoint);
+
+ if (type == PM_TOKEN_NOT_PROVIDED) {
+ // If we haven't returned at this point then we had
+ // something that looked like an interpolated class
+ // or instance variable like "#@" but wasn't
+ // actually. In this case we'll just skip to the
+ // next breakpoint.
+ breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
+ break;
+ }
+
+ if (type == PM_TOKEN_STRING_CONTENT) {
+ pm_token_buffer_flush(parser, &token_buffer);
+ }
+
+ LEX(type);
+ }
+ default:
+ assert(false && "unreachable");
+ }
+
+ was_line_continuation = false;
+ }
+
+ if (parser->current.end > parser->current.start) {
+ parser->current.end = parser->end;
+ pm_token_buffer_flush(parser, &token_buffer);
+ LEX(PM_TOKEN_STRING_CONTENT);
+ }
+
+ // If we've hit the end of the string, then this is an unterminated
+ // heredoc. In that case we'll return a string content token.
+ parser->current.end = parser->end;
+ pm_token_buffer_flush(parser, &token_buffer);
+ LEX(PM_TOKEN_STRING_CONTENT);
+ }
+ }
+
+ assert(false && "unreachable");
+}
+
+#undef LEX
+
+/******************************************************************************/
+/* Parse functions */
+/******************************************************************************/
+
+/**
+ * These are the various precedence rules. Because we are using a Pratt parser,
+ * they are named binding power to represent the manner in which nodes are bound
+ * together in the stack.
+ *
+ * We increment by 2 because we want to leave room for the infix operators to
+ * specify their associativity by adding or subtracting one.
+ */
+typedef enum {
+ PM_BINDING_POWER_UNSET = 0, // used to indicate this token cannot be used as an infix operator
+ PM_BINDING_POWER_STATEMENT = 2,
+ PM_BINDING_POWER_MODIFIER_RESCUE = 4, // rescue
+ PM_BINDING_POWER_MODIFIER = 6, // if unless until while
+ PM_BINDING_POWER_COMPOSITION = 8, // and or
+ PM_BINDING_POWER_NOT = 10, // not
+ PM_BINDING_POWER_MATCH = 12, // => in
+ PM_BINDING_POWER_DEFINED = 14, // defined?
+ PM_BINDING_POWER_MULTI_ASSIGNMENT = 16, // =
+ PM_BINDING_POWER_ASSIGNMENT = 18, // = += -= *= /= %= &= |= ^= &&= ||= <<= >>= **=
+ PM_BINDING_POWER_TERNARY = 20, // ?:
+ PM_BINDING_POWER_RANGE = 22, // .. ...
+ PM_BINDING_POWER_LOGICAL_OR = 24, // ||
+ PM_BINDING_POWER_LOGICAL_AND = 26, // &&
+ PM_BINDING_POWER_EQUALITY = 28, // <=> == === != =~ !~
+ PM_BINDING_POWER_COMPARISON = 30, // > >= < <=
+ PM_BINDING_POWER_BITWISE_OR = 32, // | ^
+ PM_BINDING_POWER_BITWISE_AND = 34, // &
+ PM_BINDING_POWER_SHIFT = 36, // << >>
+ PM_BINDING_POWER_TERM = 38, // + -
+ PM_BINDING_POWER_FACTOR = 40, // * / %
+ PM_BINDING_POWER_UMINUS = 42, // -@
+ PM_BINDING_POWER_EXPONENT = 44, // **
+ PM_BINDING_POWER_UNARY = 46, // ! ~ +@
+ PM_BINDING_POWER_INDEX = 48, // [] []=
+ PM_BINDING_POWER_CALL = 50, // :: .
+ PM_BINDING_POWER_MAX = 52
+} pm_binding_power_t;
+
+/**
+ * This struct represents a set of binding powers used for a given token. They
+ * are combined in this way to make it easier to represent associativity.
+ */
+typedef struct {
+ /** The left binding power. */
+ pm_binding_power_t left;
+
+ /** The right binding power. */
+ pm_binding_power_t right;
+
+ /** Whether or not this token can be used as a binary operator. */
+ bool binary;
+
+ /**
+ * Whether or not this token can be used as non-associative binary operator.
+ * Non-associative operators (e.g. in and =>) need special treatment in parse_expression.
+ */
+ bool nonassoc;
+} pm_binding_powers_t;
+
+#define BINDING_POWER_ASSIGNMENT { PM_BINDING_POWER_UNARY, PM_BINDING_POWER_ASSIGNMENT, true, false }
+#define LEFT_ASSOCIATIVE(precedence) { precedence, precedence + 1, true, false }
+#define RIGHT_ASSOCIATIVE(precedence) { precedence, precedence, true, false }
+#define NON_ASSOCIATIVE(precedence) { precedence, precedence + 1, true, true }
+#define RIGHT_ASSOCIATIVE_UNARY(precedence) { precedence, precedence, false, false }
+
+pm_binding_powers_t pm_binding_powers[PM_TOKEN_MAXIMUM] = {
+ // rescue
+ [PM_TOKEN_KEYWORD_RESCUE_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER_RESCUE),
+
+ // if unless until while
+ [PM_TOKEN_KEYWORD_IF_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
+ [PM_TOKEN_KEYWORD_UNLESS_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
+ [PM_TOKEN_KEYWORD_UNTIL_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
+ [PM_TOKEN_KEYWORD_WHILE_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
+
+ // and or
+ [PM_TOKEN_KEYWORD_AND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPOSITION),
+ [PM_TOKEN_KEYWORD_OR] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPOSITION),
+
+ // => in
+ [PM_TOKEN_EQUAL_GREATER] = NON_ASSOCIATIVE(PM_BINDING_POWER_MATCH),
+ [PM_TOKEN_KEYWORD_IN] = NON_ASSOCIATIVE(PM_BINDING_POWER_MATCH),
+
+ // &&= &= ^= = >>= <<= -= %= |= ||= += /= *= **=
+ [PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL] = BINDING_POWER_ASSIGNMENT,
+ [PM_TOKEN_AMPERSAND_EQUAL] = BINDING_POWER_ASSIGNMENT,
+ [PM_TOKEN_CARET_EQUAL] = BINDING_POWER_ASSIGNMENT,
+ [PM_TOKEN_EQUAL] = BINDING_POWER_ASSIGNMENT,
+ [PM_TOKEN_GREATER_GREATER_EQUAL] = BINDING_POWER_ASSIGNMENT,
+ [PM_TOKEN_LESS_LESS_EQUAL] = BINDING_POWER_ASSIGNMENT,
+ [PM_TOKEN_MINUS_EQUAL] = BINDING_POWER_ASSIGNMENT,
+ [PM_TOKEN_PERCENT_EQUAL] = BINDING_POWER_ASSIGNMENT,
+ [PM_TOKEN_PIPE_EQUAL] = BINDING_POWER_ASSIGNMENT,
+ [PM_TOKEN_PIPE_PIPE_EQUAL] = BINDING_POWER_ASSIGNMENT,
+ [PM_TOKEN_PLUS_EQUAL] = BINDING_POWER_ASSIGNMENT,
+ [PM_TOKEN_SLASH_EQUAL] = BINDING_POWER_ASSIGNMENT,
+ [PM_TOKEN_STAR_EQUAL] = BINDING_POWER_ASSIGNMENT,
+ [PM_TOKEN_STAR_STAR_EQUAL] = BINDING_POWER_ASSIGNMENT,
+
+ // ?:
+ [PM_TOKEN_QUESTION_MARK] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_TERNARY),
+
+ // .. ...
+ [PM_TOKEN_DOT_DOT] = NON_ASSOCIATIVE(PM_BINDING_POWER_RANGE),
+ [PM_TOKEN_DOT_DOT_DOT] = NON_ASSOCIATIVE(PM_BINDING_POWER_RANGE),
+ [PM_TOKEN_UDOT_DOT] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_LOGICAL_OR),
+ [PM_TOKEN_UDOT_DOT_DOT] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_LOGICAL_OR),
+
+ // ||
+ [PM_TOKEN_PIPE_PIPE] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_LOGICAL_OR),
+
+ // &&
+ [PM_TOKEN_AMPERSAND_AMPERSAND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_LOGICAL_AND),
+
+ // != !~ == === =~ <=>
+ [PM_TOKEN_BANG_EQUAL] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
+ [PM_TOKEN_BANG_TILDE] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
+ [PM_TOKEN_EQUAL_EQUAL] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
+ [PM_TOKEN_EQUAL_EQUAL_EQUAL] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
+ [PM_TOKEN_EQUAL_TILDE] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
+ [PM_TOKEN_LESS_EQUAL_GREATER] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
+
+ // > >= < <=
+ [PM_TOKEN_GREATER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
+ [PM_TOKEN_GREATER_EQUAL] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
+ [PM_TOKEN_LESS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
+ [PM_TOKEN_LESS_EQUAL] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
+
+ // ^ |
+ [PM_TOKEN_CARET] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_BITWISE_OR),
+ [PM_TOKEN_PIPE] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_BITWISE_OR),
+
+ // &
+ [PM_TOKEN_AMPERSAND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_BITWISE_AND),
+
+ // >> <<
+ [PM_TOKEN_GREATER_GREATER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_SHIFT),
+ [PM_TOKEN_LESS_LESS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_SHIFT),
+
+ // - +
+ [PM_TOKEN_MINUS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_TERM),
+ [PM_TOKEN_PLUS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_TERM),
+
+ // % / *
+ [PM_TOKEN_PERCENT] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
+ [PM_TOKEN_SLASH] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
+ [PM_TOKEN_STAR] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
+ [PM_TOKEN_USTAR] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
+
+ // -@
+ [PM_TOKEN_UMINUS] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UMINUS),
+ [PM_TOKEN_UMINUS_NUM] = { PM_BINDING_POWER_UMINUS, PM_BINDING_POWER_MAX, false, false },
+
+ // **
+ [PM_TOKEN_STAR_STAR] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_EXPONENT),
+ [PM_TOKEN_USTAR_STAR] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
+
+ // ! ~ +@
+ [PM_TOKEN_BANG] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
+ [PM_TOKEN_TILDE] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
+ [PM_TOKEN_UPLUS] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
+
+ // [
+ [PM_TOKEN_BRACKET_LEFT] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_INDEX),
+
+ // :: . &.
+ [PM_TOKEN_COLON_COLON] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_CALL),
+ [PM_TOKEN_DOT] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_CALL),
+ [PM_TOKEN_AMPERSAND_DOT] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_CALL)
+};
+
+#undef BINDING_POWER_ASSIGNMENT
+#undef LEFT_ASSOCIATIVE
+#undef RIGHT_ASSOCIATIVE
+#undef RIGHT_ASSOCIATIVE_UNARY
+
+/**
+ * Returns true if the current token is of the given type.
+ */
+static inline bool
+match1(const pm_parser_t *parser, pm_token_type_t type) {
+ return parser->current.type == type;
+}
+
+/**
+ * Returns true if the current token is of either of the given types.
+ */
+static inline bool
+match2(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2) {
+ return match1(parser, type1) || match1(parser, type2);
+}
+
+/**
+ * Returns true if the current token is any of the three given types.
+ */
+static inline bool
+match3(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3) {
+ return match1(parser, type1) || match1(parser, type2) || match1(parser, type3);
+}
+
+/**
+ * Returns true if the current token is any of the four given types.
+ */
+static inline bool
+match4(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3, pm_token_type_t type4) {
+ return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4);
+}
+
+/**
+ * Returns true if the current token is any of the six given types.
+ */
+static inline bool
+match6(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3, pm_token_type_t type4, pm_token_type_t type5, pm_token_type_t type6) {
+ return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4) || match1(parser, type5) || match1(parser, type6);
+}
+
+/**
+ * Returns true if the current token is any of the seven given types.
+ */
+static inline bool
+match7(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3, pm_token_type_t type4, pm_token_type_t type5, pm_token_type_t type6, pm_token_type_t type7) {
+ return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4) || match1(parser, type5) || match1(parser, type6) || match1(parser, type7);
+}
+
+/**
+ * Returns true if the current token is any of the eight given types.
+ */
+static inline bool
+match8(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3, pm_token_type_t type4, pm_token_type_t type5, pm_token_type_t type6, pm_token_type_t type7, pm_token_type_t type8) {
+ return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4) || match1(parser, type5) || match1(parser, type6) || match1(parser, type7) || match1(parser, type8);
+}
+
+/**
+ * If the current token is of the specified type, lex forward by one token and
+ * return true. Otherwise, return false. For example:
+ *
+ * if (accept1(parser, PM_TOKEN_COLON)) { ... }
+ */
+static bool
+accept1(pm_parser_t *parser, pm_token_type_t type) {
+ if (match1(parser, type)) {
+ parser_lex(parser);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * If the current token is either of the two given types, lex forward by one
+ * token and return true. Otherwise return false.
+ */
+static inline bool
+accept2(pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2) {
+ if (match2(parser, type1, type2)) {
+ parser_lex(parser);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * If the current token is any of the three given types, lex forward by one
+ * token and return true. Otherwise return false.
+ */
+static inline bool
+accept3(pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3) {
+ if (match3(parser, type1, type2, type3)) {
+ parser_lex(parser);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * This function indicates that the parser expects a token in a specific
+ * position. For example, if you're parsing a BEGIN block, you know that a { is
+ * expected immediately after the keyword. In that case you would call this
+ * function to indicate that that token should be found.
+ *
+ * If we didn't find the token that we were expecting, then we're going to add
+ * an error to the parser's list of errors (to indicate that the tree is not
+ * valid) and create an artificial token instead. This allows us to recover from
+ * the fact that the token isn't present and continue parsing.
+ */
+static void
+expect1(pm_parser_t *parser, pm_token_type_t type, pm_diagnostic_id_t diag_id) {
+ if (accept1(parser, type)) return;
+
+ const uint8_t *location = parser->previous.end;
+ pm_parser_err(parser, location, location, diag_id);
+
+ parser->previous.start = location;
+ parser->previous.type = PM_TOKEN_MISSING;
+}
+
+/**
+ * This function is the same as expect1, but it expects either of two token
+ * types.
+ */
+static void
+expect2(pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_diagnostic_id_t diag_id) {
+ if (accept2(parser, type1, type2)) return;
+
+ const uint8_t *location = parser->previous.end;
+ pm_parser_err(parser, location, location, diag_id);
+
+ parser->previous.start = location;
+ parser->previous.type = PM_TOKEN_MISSING;
+}
+
+/**
+ * This function is the same as expect2, but it expects one of three token types.
+ */
+static void
+expect3(pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3, pm_diagnostic_id_t diag_id) {
+ if (accept3(parser, type1, type2, type3)) return;
+
+ const uint8_t *location = parser->previous.end;
+ pm_parser_err(parser, location, location, diag_id);
+
+ parser->previous.start = location;
+ parser->previous.type = PM_TOKEN_MISSING;
+}
+
+static pm_node_t *
+parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool accepts_command_call, pm_diagnostic_id_t diag_id);
+
+/**
+ * This is a wrapper of parse_expression, which also checks whether the
+ * resulting node is a value expression.
+ */
+static pm_node_t *
+parse_value_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool accepts_command_call, pm_diagnostic_id_t diag_id) {
+ pm_node_t *node = parse_expression(parser, binding_power, accepts_command_call, diag_id);
+ pm_assert_value_expression(parser, node);
+ return node;
+}
+
+/**
+ * This function controls whether or not we will attempt to parse an expression
+ * beginning at the subsequent token. It is used when we are in a context where
+ * an expression is optional.
+ *
+ * For example, looking at a range object when we've already lexed the operator,
+ * we need to know if we should attempt to parse an expression on the right.
+ *
+ * For another example, if we've parsed an identifier or a method call and we do
+ * not have parentheses, then the next token may be the start of an argument or
+ * it may not.
+ *
+ * CRuby parsers that are generated would resolve this by using a lookahead and
+ * potentially backtracking. We attempt to do this by just looking at the next
+ * token and making a decision based on that. I am not sure if this is going to
+ * work in all cases, it may need to be refactored later. But it appears to work
+ * for now.
+ */
+static inline bool
+token_begins_expression_p(pm_token_type_t type) {
+ switch (type) {
+ case PM_TOKEN_EQUAL_GREATER:
+ case PM_TOKEN_KEYWORD_IN:
+ // We need to special case this because it is a binary operator that
+ // should not be marked as beginning an expression.
+ return false;
+ case PM_TOKEN_BRACE_RIGHT:
+ case PM_TOKEN_BRACKET_RIGHT:
+ case PM_TOKEN_COLON:
+ case PM_TOKEN_COMMA:
+ case PM_TOKEN_EMBEXPR_END:
+ case PM_TOKEN_EOF:
+ case PM_TOKEN_LAMBDA_BEGIN:
+ case PM_TOKEN_KEYWORD_DO:
+ case PM_TOKEN_KEYWORD_DO_LOOP:
+ case PM_TOKEN_KEYWORD_END:
+ case PM_TOKEN_KEYWORD_ELSE:
+ case PM_TOKEN_KEYWORD_ELSIF:
+ case PM_TOKEN_KEYWORD_ENSURE:
+ case PM_TOKEN_KEYWORD_THEN:
+ case PM_TOKEN_KEYWORD_RESCUE:
+ case PM_TOKEN_KEYWORD_WHEN:
+ case PM_TOKEN_NEWLINE:
+ case PM_TOKEN_PARENTHESIS_RIGHT:
+ case PM_TOKEN_SEMICOLON:
+ // The reason we need this short-circuit is because we're using the
+ // binding powers table to tell us if the subsequent token could
+ // potentially be the start of an expression. If there _is_ a binding
+ // power for one of these tokens, then we should remove it from this list
+ // and let it be handled by the default case below.
+ assert(pm_binding_powers[type].left == PM_BINDING_POWER_UNSET);
+ return false;
+ case PM_TOKEN_UAMPERSAND:
+ // This is a special case because this unary operator cannot appear
+ // as a general operator, it only appears in certain circumstances.
+ return false;
+ case PM_TOKEN_UCOLON_COLON:
+ case PM_TOKEN_UMINUS:
+ case PM_TOKEN_UMINUS_NUM:
+ case PM_TOKEN_UPLUS:
+ case PM_TOKEN_BANG:
+ case PM_TOKEN_TILDE:
+ case PM_TOKEN_UDOT_DOT:
+ case PM_TOKEN_UDOT_DOT_DOT:
+ // These unary tokens actually do have binding power associated with them
+ // so that we can correctly place them into the precedence order. But we
+ // want them to be marked as beginning an expression, so we need to
+ // special case them here.
+ return true;
+ default:
+ return pm_binding_powers[type].left == PM_BINDING_POWER_UNSET;
+ }
+}
+
+/**
+ * Parse an expression with the given binding power that may be optionally
+ * prefixed by the * operator.
+ */
+static pm_node_t *
+parse_starred_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool accepts_command_call, pm_diagnostic_id_t diag_id) {
+ if (accept1(parser, PM_TOKEN_USTAR)) {
+ pm_token_t operator = parser->previous;
+ pm_node_t *expression = parse_value_expression(parser, binding_power, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR);
+ return (pm_node_t *) pm_splat_node_create(parser, &operator, expression);
+ }
+
+ return parse_value_expression(parser, binding_power, accepts_command_call, diag_id);
+}
+
+/**
+ * Convert the name of a method into the corresponding write method name. For
+ * example, foo would be turned into foo=.
+ */
+static void
+parse_write_name(pm_parser_t *parser, pm_constant_id_t *name_field) {
+ // The method name needs to change. If we previously had
+ // foo, we now need foo=. In this case we'll allocate a new
+ // owned string, copy the previous method name in, and
+ // append an =.
+ pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, *name_field);
+ size_t length = constant->length;
+ uint8_t *name = xcalloc(length + 1, sizeof(uint8_t));
+ if (name == NULL) return;
+
+ memcpy(name, constant->start, length);
+ name[length] = '=';
+
+ // Now switch the name to the new string.
+ // This silences clang analyzer warning about leak of memory pointed by `name`.
+ // NOLINTNEXTLINE(clang-analyzer-*)
+ *name_field = pm_constant_pool_insert_owned(&parser->constant_pool, name, length + 1);
+}
+
+/**
+ * Convert the given node into a valid target node.
+ */
+static pm_node_t *
+parse_target(pm_parser_t *parser, pm_node_t *target) {
+ switch (PM_NODE_TYPE(target)) {
+ case PM_MISSING_NODE:
+ return target;
+ case PM_CLASS_VARIABLE_READ_NODE:
+ assert(sizeof(pm_class_variable_target_node_t) == sizeof(pm_class_variable_read_node_t));
+ target->type = PM_CLASS_VARIABLE_TARGET_NODE;
+ return target;
+ case PM_CONSTANT_PATH_NODE:
+ assert(sizeof(pm_constant_path_target_node_t) == sizeof(pm_constant_path_node_t));
+ target->type = PM_CONSTANT_PATH_TARGET_NODE;
+ return target;
+ case PM_CONSTANT_READ_NODE:
+ assert(sizeof(pm_constant_target_node_t) == sizeof(pm_constant_read_node_t));
+ target->type = PM_CONSTANT_TARGET_NODE;
+ return target;
+ case PM_BACK_REFERENCE_READ_NODE:
+ case PM_NUMBERED_REFERENCE_READ_NODE:
+ PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, target, PM_ERR_WRITE_TARGET_READONLY);
+ return target;
+ case PM_GLOBAL_VARIABLE_READ_NODE:
+ assert(sizeof(pm_global_variable_target_node_t) == sizeof(pm_global_variable_read_node_t));
+ target->type = PM_GLOBAL_VARIABLE_TARGET_NODE;
+ return target;
+ case PM_LOCAL_VARIABLE_READ_NODE: {
+ pm_refute_numbered_parameter(parser, target->location.start, target->location.end);
+
+ const pm_local_variable_read_node_t *cast = (const pm_local_variable_read_node_t *) target;
+ uint32_t name = cast->name;
+ uint32_t depth = cast->depth;
+ pm_locals_unread(&pm_parser_scope_find(parser, depth)->locals, name);
+
+ assert(sizeof(pm_local_variable_target_node_t) == sizeof(pm_local_variable_read_node_t));
+ target->type = PM_LOCAL_VARIABLE_TARGET_NODE;
+
+ return target;
+ }
+ case PM_INSTANCE_VARIABLE_READ_NODE:
+ assert(sizeof(pm_instance_variable_target_node_t) == sizeof(pm_instance_variable_read_node_t));
+ target->type = PM_INSTANCE_VARIABLE_TARGET_NODE;
+ return target;
+ case PM_MULTI_TARGET_NODE:
+ return target;
+ case PM_SPLAT_NODE: {
+ pm_splat_node_t *splat = (pm_splat_node_t *) target;
+
+ if (splat->expression != NULL) {
+ splat->expression = parse_target(parser, splat->expression);
+ }
+
+ return (pm_node_t *) splat;
+ }
+ case PM_CALL_NODE: {
+ pm_call_node_t *call = (pm_call_node_t *) target;
+
+ // If we have no arguments to the call node and we need this to be a
+ // target then this is either a method call or a local variable
+ // write.
+ if (
+ (call->message_loc.start != NULL) &&
+ (call->message_loc.end[-1] != '!') &&
+ (call->message_loc.end[-1] != '?') &&
+ (call->opening_loc.start == NULL) &&
+ (call->arguments == NULL) &&
+ (call->block == NULL)
+ ) {
+ if (call->receiver == NULL) {
+ // When we get here, we have a local variable write, because it
+ // was previously marked as a method call but now we have an =.
+ // This looks like:
+ //
+ // foo = 1
+ //
+ // When it was parsed in the prefix position, foo was seen as a
+ // method call with no receiver and no arguments. Now we have an
+ // =, so we know it's a local variable write.
+ const pm_location_t message_loc = call->message_loc;
+
+ pm_constant_id_t name = pm_parser_local_add_location(parser, message_loc.start, message_loc.end, 0);
+ pm_node_destroy(parser, target);
+
+ return (pm_node_t *) pm_local_variable_target_node_create(parser, &message_loc, name, 0);
+ }
+
+ if (*call->message_loc.start == '_' || parser->encoding->alnum_char(call->message_loc.start, call->message_loc.end - call->message_loc.start)) {
+ parse_write_name(parser, &call->name);
+ return (pm_node_t *) pm_call_target_node_create(parser, call);
+ }
+ }
+
+ // If there is no call operator and the message is "[]" then this is
+ // an aref expression, and we can transform it into an aset
+ // expression.
+ if (PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_INDEX)) {
+ return (pm_node_t *) pm_index_target_node_create(parser, call);
+ }
+ }
+ /* fallthrough */
+ default:
+ // In this case we have a node that we don't know how to convert
+ // into a target. We need to treat it as an error. For now, we'll
+ // mark it as an error and just skip right past it.
+ pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_UNEXPECTED);
+ return target;
+ }
+}
+
+/**
+ * Parse a write target and validate that it is in a valid position for
+ * assignment.
+ */
+static pm_node_t *
+parse_target_validate(pm_parser_t *parser, pm_node_t *target) {
+ pm_node_t *result = parse_target(parser, target);
+
+ // Ensure that we have one of an =, an 'in' in for indexes, and a ')' in parens after the targets.
+ if (
+ !match1(parser, PM_TOKEN_EQUAL) &&
+ !(context_p(parser, PM_CONTEXT_FOR_INDEX) && match1(parser, PM_TOKEN_KEYWORD_IN)) &&
+ !(context_p(parser, PM_CONTEXT_PARENS) && match1(parser, PM_TOKEN_PARENTHESIS_RIGHT))
+ ) {
+ pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
+ }
+
+ return result;
+}
+
+/**
+ * Potentially wrap a constant write node in a shareable constant node depending
+ * on the current state.
+ */
+static pm_node_t *
+parse_shareable_constant_write(pm_parser_t *parser, pm_node_t *write) {
+ pm_shareable_constant_value_t shareable_constant = pm_parser_scope_shareable_constant_get(parser);
+
+ if (shareable_constant != PM_SCOPE_SHAREABLE_CONSTANT_NONE) {
+ return (pm_node_t *) pm_shareable_constant_node_create(parser, write, shareable_constant);
+ }
+
+ return write;
+}
+
+/**
+ * Convert the given node into a valid write node.
+ */
+static pm_node_t *
+parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_node_t *value) {
+ switch (PM_NODE_TYPE(target)) {
+ case PM_MISSING_NODE:
+ pm_node_destroy(parser, value);
+ return target;
+ case PM_CLASS_VARIABLE_READ_NODE: {
+ pm_class_variable_write_node_t *node = pm_class_variable_write_node_create(parser, (pm_class_variable_read_node_t *) target, operator, value);
+ pm_node_destroy(parser, target);
+ return (pm_node_t *) node;
+ }
+ case PM_CONSTANT_PATH_NODE: {
+ pm_node_t *node = (pm_node_t *) pm_constant_path_write_node_create(parser, (pm_constant_path_node_t *) target, operator, value);
+ return parse_shareable_constant_write(parser, node);
+ }
+ case PM_CONSTANT_READ_NODE: {
+ pm_node_t *node = (pm_node_t *) pm_constant_write_node_create(parser, (pm_constant_read_node_t *) target, operator, value);
+ if (context_def_p(parser)) {
+ pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_IN_METHOD);
+ }
+ pm_node_destroy(parser, target);
+ return parse_shareable_constant_write(parser, node);
+ }
+ case PM_BACK_REFERENCE_READ_NODE:
+ case PM_NUMBERED_REFERENCE_READ_NODE:
+ PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, target, PM_ERR_WRITE_TARGET_READONLY);
+ /* fallthrough */
+ case PM_GLOBAL_VARIABLE_READ_NODE: {
+ pm_global_variable_write_node_t *node = pm_global_variable_write_node_create(parser, target, operator, value);
+ pm_node_destroy(parser, target);
+ return (pm_node_t *) node;
+ }
+ case PM_LOCAL_VARIABLE_READ_NODE: {
+ pm_refute_numbered_parameter(parser, target->location.start, target->location.end);
+ pm_local_variable_read_node_t *local_read = (pm_local_variable_read_node_t *) target;
+
+ pm_constant_id_t name = local_read->name;
+ uint32_t depth = local_read->depth;
+ pm_locals_unread(&pm_parser_scope_find(parser, depth)->locals, name);
+
+ pm_location_t name_loc = target->location;
+ pm_node_destroy(parser, target);
+
+ return (pm_node_t *) pm_local_variable_write_node_create(parser, name, depth, value, &name_loc, operator);
+ }
+ case PM_INSTANCE_VARIABLE_READ_NODE: {
+ pm_node_t *write_node = (pm_node_t *) pm_instance_variable_write_node_create(parser, (pm_instance_variable_read_node_t *) target, operator, value);
+ pm_node_destroy(parser, target);
+ return write_node;
+ }
+ case PM_MULTI_TARGET_NODE:
+ return (pm_node_t *) pm_multi_write_node_create(parser, (pm_multi_target_node_t *) target, operator, value);
+ case PM_SPLAT_NODE: {
+ pm_splat_node_t *splat = (pm_splat_node_t *) target;
+
+ if (splat->expression != NULL) {
+ splat->expression = parse_write(parser, splat->expression, operator, value);
+ }
+
+ pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser);
+ pm_multi_target_node_targets_append(parser, multi_target, (pm_node_t *) splat);
+
+ return (pm_node_t *) pm_multi_write_node_create(parser, multi_target, operator, value);
+ }
+ case PM_CALL_NODE: {
+ pm_call_node_t *call = (pm_call_node_t *) target;
+
+ // If we have no arguments to the call node and we need this to be a
+ // target then this is either a method call or a local variable
+ // write.
+ if (
+ (call->message_loc.start != NULL) &&
+ (call->message_loc.end[-1] != '!') &&
+ (call->message_loc.end[-1] != '?') &&
+ (call->opening_loc.start == NULL) &&
+ (call->arguments == NULL) &&
+ (call->block == NULL)
+ ) {
+ if (call->receiver == NULL) {
+ // When we get here, we have a local variable write, because it
+ // was previously marked as a method call but now we have an =.
+ // This looks like:
+ //
+ // foo = 1
+ //
+ // When it was parsed in the prefix position, foo was seen as a
+ // method call with no receiver and no arguments. Now we have an
+ // =, so we know it's a local variable write.
+ const pm_location_t message = call->message_loc;
+
+ pm_parser_local_add_location(parser, message.start, message.end, 0);
+ pm_node_destroy(parser, target);
+
+ pm_constant_id_t constant_id = pm_parser_constant_id_location(parser, message.start, message.end);
+ target = (pm_node_t *) pm_local_variable_write_node_create(parser, constant_id, 0, value, &message, operator);
+
+ pm_refute_numbered_parameter(parser, message.start, message.end);
+ return target;
+ }
+
+ if (char_is_identifier_start(parser, call->message_loc.start)) {
+ // When we get here, we have a method call, because it was
+ // previously marked as a method call but now we have an =. This
+ // looks like:
+ //
+ // foo.bar = 1
+ //
+ // When it was parsed in the prefix position, foo.bar was seen as a
+ // method call with no arguments. Now we have an =, so we know it's
+ // a method call with an argument. In this case we will create the
+ // arguments node, parse the argument, and add it to the list.
+ pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
+ call->arguments = arguments;
+
+ pm_arguments_node_arguments_append(arguments, value);
+ call->base.location.end = arguments->base.location.end;
+
+ parse_write_name(parser, &call->name);
+ pm_node_flag_set((pm_node_t *) call, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE);
+ return (pm_node_t *) call;
+ }
+ }
+
+ // If there is no call operator and the message is "[]" then this is
+ // an aref expression, and we can transform it into an aset
+ // expression.
+ if (PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_INDEX)) {
+ if (call->arguments == NULL) {
+ call->arguments = pm_arguments_node_create(parser);
+ }
+
+ pm_arguments_node_arguments_append(call->arguments, value);
+ target->location.end = value->location.end;
+
+ // Replace the name with "[]=".
+ call->name = pm_parser_constant_id_constant(parser, "[]=", 3);
+ pm_node_flag_set((pm_node_t *) call, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE);
+ return target;
+ }
+
+ // If there are arguments on the call node, then it can't be a method
+ // call ending with = or a local variable write, so it must be a
+ // syntax error. In this case we'll fall through to our default
+ // handling. We need to free the value that we parsed because there
+ // is no way for us to attach it to the tree at this point.
+ pm_node_destroy(parser, value);
+ }
+ /* fallthrough */
+ default:
+ // In this case we have a node that we don't know how to convert into a
+ // target. We need to treat it as an error. For now, we'll mark it as an
+ // error and just skip right past it.
+ pm_parser_err_token(parser, operator, PM_ERR_WRITE_TARGET_UNEXPECTED);
+ return target;
+ }
+}
+
+/**
+ * Certain expressions are not writable, but in order to provide a better
+ * experience we give a specific error message. In order to maintain as much
+ * information in the tree as possible, we replace them with local variable
+ * writes.
+ */
+static pm_node_t *
+parse_unwriteable_write(pm_parser_t *parser, pm_node_t *target, const pm_token_t *equals, pm_node_t *value) {
+ switch (PM_NODE_TYPE(target)) {
+ case PM_SOURCE_ENCODING_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING); break;
+ case PM_FALSE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE); break;
+ case PM_SOURCE_FILE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_FILE); break;
+ case PM_SOURCE_LINE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_LINE); break;
+ case PM_NIL_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_NIL); break;
+ case PM_SELF_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_SELF); break;
+ case PM_TRUE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE); break;
+ default: break;
+ }
+
+ pm_constant_id_t name = pm_parser_constant_id_location(parser, target->location.start, target->location.end);
+ pm_local_variable_write_node_t *result = pm_local_variable_write_node_create(parser, name, 0, value, &target->location, equals);
+
+ pm_node_destroy(parser, target);
+ return (pm_node_t *) result;
+}
+
+/**
+ * Parse a list of targets for assignment. This is used in the case of a for
+ * loop or a multi-assignment. For example, in the following code:
+ *
+ * for foo, bar in baz
+ * ^^^^^^^^
+ *
+ * The targets are `foo` and `bar`. This function will either return a single
+ * target node or a multi-target node.
+ */
+static pm_node_t *
+parse_targets(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t binding_power) {
+ bool has_rest = PM_NODE_TYPE_P(first_target, PM_SPLAT_NODE);
+
+ pm_multi_target_node_t *result = pm_multi_target_node_create(parser);
+ pm_multi_target_node_targets_append(parser, result, parse_target(parser, first_target));
+
+ while (accept1(parser, PM_TOKEN_COMMA)) {
+ if (accept1(parser, PM_TOKEN_USTAR)) {
+ // Here we have a splat operator. It can have a name or be
+ // anonymous. It can be the final target or be in the middle if
+ // there haven't been any others yet.
+ if (has_rest) {
+ pm_parser_err_previous(parser, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS);
+ }
+
+ pm_token_t star_operator = parser->previous;
+ pm_node_t *name = NULL;
+
+ if (token_begins_expression_p(parser->current.type)) {
+ name = parse_expression(parser, binding_power, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR);
+ name = parse_target(parser, name);
+ }
+
+ pm_node_t *splat = (pm_node_t *) pm_splat_node_create(parser, &star_operator, name);
+ pm_multi_target_node_targets_append(parser, result, splat);
+ has_rest = true;
+ } else if (token_begins_expression_p(parser->current.type)) {
+ pm_node_t *target = parse_expression(parser, binding_power, false, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA);
+ target = parse_target(parser, target);
+
+ pm_multi_target_node_targets_append(parser, result, target);
+ } else if (!match1(parser, PM_TOKEN_EOF)) {
+ // If we get here, then we have a trailing , in a multi target node.
+ // We'll add an implicit rest node to represent this.
+ pm_node_t *rest = (pm_node_t *) pm_implicit_rest_node_create(parser, &parser->previous);
+ pm_multi_target_node_targets_append(parser, result, rest);
+ break;
+ }
+ }
+
+ return (pm_node_t *) result;
+}
+
+/**
+ * Parse a list of targets and validate that it is in a valid position for
+ * assignment.
+ */
+static pm_node_t *
+parse_targets_validate(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t binding_power) {
+ pm_node_t *result = parse_targets(parser, first_target, binding_power);
+ accept1(parser, PM_TOKEN_NEWLINE);
+
+ // Ensure that we have either an = or a ) after the targets.
+ if (!match2(parser, PM_TOKEN_EQUAL, PM_TOKEN_PARENTHESIS_RIGHT)) {
+ pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
+ }
+
+ return result;
+}
+
+/**
+ * Parse a list of statements separated by newlines or semicolons.
+ */
+static pm_statements_node_t *
+parse_statements(pm_parser_t *parser, pm_context_t context) {
+ // First, skip past any optional terminators that might be at the beginning of
+ // the statements.
+ while (accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE));
+
+ // If we have a terminator, then we can just return NULL.
+ if (context_terminator(context, &parser->current)) return NULL;
+
+ pm_statements_node_t *statements = pm_statements_node_create(parser);
+
+ // At this point we know we have at least one statement, and that it
+ // immediately follows the current token.
+ context_push(parser, context);
+
+ while (true) {
+ pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, true, PM_ERR_CANNOT_PARSE_EXPRESSION);
+ pm_statements_node_body_append(parser, statements, node);
+
+ // If we're recovering from a syntax error, then we need to stop parsing the
+ // statements now.
+ if (parser->recovering) {
+ // If this is the level of context where the recovery has happened, then
+ // we can mark the parser as done recovering.
+ if (context_terminator(context, &parser->current)) parser->recovering = false;
+ break;
+ }
+
+ // If we have a terminator, then we will parse all consecutive terminators
+ // and then continue parsing the statements list.
+ if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
+ // If we have a terminator, then we will continue parsing the statements
+ // list.
+ while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
+ if (context_terminator(context, &parser->current)) break;
+
+ // Now we can continue parsing the list of statements.
+ continue;
+ }
+
+ // At this point we have a list of statements that are not terminated by a
+ // newline or semicolon. At this point we need to check if we're at the end
+ // of the statements list. If we are, then we should break out of the loop.
+ if (context_terminator(context, &parser->current)) break;
+
+ // At this point, we have a syntax error, because the statement was not
+ // terminated by a newline or semicolon, and we're not at the end of the
+ // statements list. Ideally we should scan forward to determine if we should
+ // insert a missing terminator or break out of parsing the statements list
+ // at this point.
+ //
+ // We don't have that yet, so instead we'll do a more naive approach. If we
+ // were unable to parse an expression, then we will skip past this token and
+ // continue parsing the statements list. Otherwise we'll add an error and
+ // continue parsing the statements list.
+ if (PM_NODE_TYPE_P(node, PM_MISSING_NODE)) {
+ parser_lex(parser);
+
+ while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
+ if (context_terminator(context, &parser->current)) break;
+ } else if (!accept1(parser, PM_TOKEN_NEWLINE)) {
+ // This is an inlined version of accept1 because the error that we
+ // want to add has varargs. If this happens again, we should
+ // probably extract a helper function.
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(parser->current.type));
+ parser->previous.start = parser->previous.end;
+ parser->previous.type = PM_TOKEN_MISSING;
+ }
+ }
+
+ context_pop(parser);
+ pm_void_statements_check(parser, statements);
+
+ return statements;
+}
+
+/**
+ * Add a node to a set of static literals that holds a set of hash keys. If the
+ * node is a duplicate, then add an appropriate warning.
+ */
+static void
+pm_hash_key_static_literals_add(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) {
+ const pm_node_t *duplicated = pm_static_literals_add(&parser->newline_list, parser->start_line, literals, node);
+
+ if (duplicated != NULL) {
+ pm_buffer_t buffer = { 0 };
+ pm_static_literal_inspect(&buffer, &parser->newline_list, parser->start_line, parser->encoding->name, duplicated);
+
+ pm_diagnostic_list_append_format(
+ &parser->warning_list,
+ duplicated->location.start,
+ duplicated->location.end,
+ PM_WARN_DUPLICATED_HASH_KEY,
+ (int) pm_buffer_length(&buffer),
+ pm_buffer_value(&buffer),
+ pm_newline_list_line_column(&parser->newline_list, node->location.start, parser->start_line).line
+ );
+
+ pm_buffer_free(&buffer);
+ }
+}
+
+/**
+ * Add a node to a set of static literals that holds a set of hash keys. If the
+ * node is a duplicate, then add an appropriate warning.
+ */
+static void
+pm_when_clause_static_literals_add(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) {
+ if (pm_static_literals_add(&parser->newline_list, parser->start_line, literals, node) != NULL) {
+ pm_diagnostic_list_append_format(
+ &parser->warning_list,
+ node->location.start,
+ node->location.end,
+ PM_WARN_DUPLICATED_WHEN_CLAUSE,
+ pm_newline_list_line_column(&parser->newline_list, node->location.start, parser->start_line).line
+ );
+ }
+}
+
+/**
+ * Parse all of the elements of a hash. Return true if a double splat was found.
+ */
+static bool
+parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) {
+ assert(PM_NODE_TYPE_P(node, PM_HASH_NODE) || PM_NODE_TYPE_P(node, PM_KEYWORD_HASH_NODE));
+ bool contains_keyword_splat = false;
+
+ while (true) {
+ pm_node_t *element;
+
+ switch (parser->current.type) {
+ case PM_TOKEN_USTAR_STAR: {
+ parser_lex(parser);
+ pm_token_t operator = parser->previous;
+ pm_node_t *value = NULL;
+
+ if (match1(parser, PM_TOKEN_BRACE_LEFT)) {
+ // If we're about to parse a nested hash that is being
+ // pushed into this hash directly with **, then we want the
+ // inner hash to share the static literals with the outer
+ // hash.
+ parser->current_hash_keys = literals;
+ value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH);
+ } else if (token_begins_expression_p(parser->current.type)) {
+ value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH);
+ } else {
+ pm_parser_scope_forwarding_keywords_check(parser, &operator);
+ }
+
+ element = (pm_node_t *) pm_assoc_splat_node_create(parser, value, &operator);
+ contains_keyword_splat = true;
+ break;
+ }
+ case PM_TOKEN_LABEL: {
+ pm_token_t label = parser->current;
+ parser_lex(parser);
+
+ pm_node_t *key = (pm_node_t *) pm_symbol_node_label_create(parser, &label);
+ pm_hash_key_static_literals_add(parser, literals, key);
+
+ pm_token_t operator = not_provided(parser);
+ pm_node_t *value = NULL;
+
+ if (token_begins_expression_p(parser->current.type)) {
+ value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_HASH_EXPRESSION_AFTER_LABEL);
+ } else {
+ if (parser->encoding->isupper_char(label.start, (label.end - 1) - label.start)) {
+ pm_token_t constant = { .type = PM_TOKEN_CONSTANT, .start = label.start, .end = label.end - 1 };
+ value = (pm_node_t *) pm_constant_read_node_create(parser, &constant);
+ } else {
+ int depth = -1;
+ pm_token_t identifier = { .type = PM_TOKEN_IDENTIFIER, .start = label.start, .end = label.end - 1 };
+
+ if (identifier.end[-1] == '!' || identifier.end[-1] == '?') {
+ PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, identifier, PM_ERR_INVALID_LOCAL_VARIABLE_READ);
+ } else {
+ depth = pm_parser_local_depth(parser, &identifier);
+ }
+
+ if (depth == -1) {
+ value = (pm_node_t *) pm_call_node_variable_call_create(parser, &identifier);
+ } else {
+ value = (pm_node_t *) pm_local_variable_read_node_create(parser, &identifier, (uint32_t) depth);
+ }
+ }
+
+ value->location.end++;
+ value = (pm_node_t *) pm_implicit_node_create(parser, value);
+ }
+
+ element = (pm_node_t *) pm_assoc_node_create(parser, key, &operator, value);
+ break;
+ }
+ default: {
+ pm_node_t *key = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_HASH_KEY);
+
+ // Hash keys that are strings are automatically frozen. We will
+ // mark that here.
+ if (PM_NODE_TYPE_P(key, PM_STRING_NODE)) {
+ pm_node_flag_set(key, PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL);
+ }
+
+ pm_hash_key_static_literals_add(parser, literals, key);
+
+ pm_token_t operator;
+ if (pm_symbol_node_label_p(key)) {
+ operator = not_provided(parser);
+ } else {
+ expect1(parser, PM_TOKEN_EQUAL_GREATER, PM_ERR_HASH_ROCKET);
+ operator = parser->previous;
+ }
+
+ pm_node_t *value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_HASH_VALUE);
+ element = (pm_node_t *) pm_assoc_node_create(parser, key, &operator, value);
+ break;
+ }
+ }
+
+ if (PM_NODE_TYPE_P(node, PM_HASH_NODE)) {
+ pm_hash_node_elements_append((pm_hash_node_t *) node, element);
+ } else {
+ pm_keyword_hash_node_elements_append((pm_keyword_hash_node_t *) node, element);
+ }
+
+ // If there's no comma after the element, then we're done.
+ if (!accept1(parser, PM_TOKEN_COMMA)) break;
+
+ // If the next element starts with a label or a **, then we know we have
+ // another element in the hash, so we'll continue parsing.
+ if (match2(parser, PM_TOKEN_USTAR_STAR, PM_TOKEN_LABEL)) continue;
+
+ // Otherwise we need to check if the subsequent token begins an expression.
+ // If it does, then we'll continue parsing.
+ if (token_begins_expression_p(parser->current.type)) continue;
+
+ // Otherwise by default we will exit out of this loop.
+ break;
+ }
+
+ return contains_keyword_splat;
+}
+
+/**
+ * Append an argument to a list of arguments.
+ */
+static inline void
+parse_arguments_append(pm_parser_t *parser, pm_arguments_t *arguments, pm_node_t *argument) {
+ if (arguments->arguments == NULL) {
+ arguments->arguments = pm_arguments_node_create(parser);
+ }
+
+ pm_arguments_node_arguments_append(arguments->arguments, argument);
+}
+
+/**
+ * Parse a list of arguments.
+ */
+static void
+parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_forwarding, pm_token_type_t terminator) {
+ pm_binding_power_t binding_power = pm_binding_powers[parser->current.type].left;
+
+ // First we need to check if the next token is one that could be the start of
+ // an argument. If it's not, then we can just return.
+ if (
+ match2(parser, terminator, PM_TOKEN_EOF) ||
+ (binding_power != PM_BINDING_POWER_UNSET && binding_power < PM_BINDING_POWER_RANGE) ||
+ context_terminator(parser->current_context->context, &parser->current)
+ ) {
+ return;
+ }
+
+ bool parsed_first_argument = false;
+ bool parsed_bare_hash = false;
+ bool parsed_block_argument = false;
+ bool parsed_forwarding_arguments = false;
+
+ while (!match1(parser, PM_TOKEN_EOF)) {
+ if (parsed_block_argument) {
+ pm_parser_err_current(parser, PM_ERR_ARGUMENT_AFTER_BLOCK);
+ }
+ if (parsed_forwarding_arguments) {
+ pm_parser_err_current(parser, PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES);
+ }
+
+ pm_node_t *argument = NULL;
+
+ switch (parser->current.type) {
+ case PM_TOKEN_USTAR_STAR:
+ case PM_TOKEN_LABEL: {
+ if (parsed_bare_hash) {
+ pm_parser_err_current(parser, PM_ERR_ARGUMENT_BARE_HASH);
+ }
+
+ pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser);
+ argument = (pm_node_t *) hash;
+
+ pm_static_literals_t hash_keys = { 0 };
+ bool contains_keyword_splat = parse_assocs(parser, &hash_keys, (pm_node_t *) hash);
+
+ parse_arguments_append(parser, arguments, argument);
+ if (contains_keyword_splat) {
+ pm_node_flag_set((pm_node_t *) arguments->arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT);
+ }
+
+ pm_static_literals_free(&hash_keys);
+ parsed_bare_hash = true;
+
+ break;
+ }
+ case PM_TOKEN_UAMPERSAND: {
+ parser_lex(parser);
+ pm_token_t operator = parser->previous;
+ pm_node_t *expression = NULL;
+
+ if (token_begins_expression_p(parser->current.type)) {
+ expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_EXPECT_ARGUMENT);
+ } else {
+ pm_parser_scope_forwarding_block_check(parser, &operator);
+ }
+
+ argument = (pm_node_t *) pm_block_argument_node_create(parser, &operator, expression);
+ if (parsed_block_argument) {
+ parse_arguments_append(parser, arguments, argument);
+ } else {
+ arguments->block = argument;
+ }
+
+ parsed_block_argument = true;
+ break;
+ }
+ case PM_TOKEN_USTAR: {
+ parser_lex(parser);
+ pm_token_t operator = parser->previous;
+
+ if (match4(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_COMMA, PM_TOKEN_SEMICOLON, PM_TOKEN_BRACKET_RIGHT)) {
+ pm_parser_scope_forwarding_positionals_check(parser, &operator);
+ argument = (pm_node_t *) pm_splat_node_create(parser, &operator, NULL);
+ } else {
+ pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT);
+
+ if (parsed_bare_hash) {
+ pm_parser_err(parser, operator.start, expression->location.end, PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT);
+ }
+
+ argument = (pm_node_t *) pm_splat_node_create(parser, &operator, expression);
+ }
+
+ parse_arguments_append(parser, arguments, argument);
+ break;
+ }
+ case PM_TOKEN_UDOT_DOT_DOT: {
+ if (accepts_forwarding) {
+ parser_lex(parser);
+
+ if (token_begins_expression_p(parser->current.type)) {
+ // If the token begins an expression then this ... was not actually
+ // argument forwarding but was instead a range.
+ pm_token_t operator = parser->previous;
+ pm_node_t *right = parse_expression(parser, PM_BINDING_POWER_RANGE, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
+ argument = (pm_node_t *) pm_range_node_create(parser, NULL, &operator, right);
+ } else {
+ pm_parser_scope_forwarding_all_check(parser, &parser->previous);
+ if (parsed_first_argument && terminator == PM_TOKEN_EOF) {
+ pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORWARDING_UNBOUND);
+ }
+
+ argument = (pm_node_t *) pm_forwarding_arguments_node_create(parser, &parser->previous);
+ parse_arguments_append(parser, arguments, argument);
+ arguments->has_forwarding = true;
+ parsed_forwarding_arguments = true;
+ break;
+ }
+ }
+ }
+ /* fallthrough */
+ default: {
+ if (argument == NULL) {
+ argument = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, !parsed_first_argument, PM_ERR_EXPECT_ARGUMENT);
+ }
+
+ bool contains_keyword_splat = false;
+ if (pm_symbol_node_label_p(argument) || accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
+ if (parsed_bare_hash) {
+ pm_parser_err_previous(parser, PM_ERR_ARGUMENT_BARE_HASH);
+ }
+
+ pm_token_t operator;
+ if (parser->previous.type == PM_TOKEN_EQUAL_GREATER) {
+ operator = parser->previous;
+ } else {
+ operator = not_provided(parser);
+ }
+
+ pm_keyword_hash_node_t *bare_hash = pm_keyword_hash_node_create(parser);
+
+ // Create the set of static literals for this hash.
+ pm_static_literals_t hash_keys = { 0 };
+ pm_hash_key_static_literals_add(parser, &hash_keys, argument);
+
+ // Finish parsing the one we are part way through.
+ pm_node_t *value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_HASH_VALUE);
+ argument = (pm_node_t *) pm_assoc_node_create(parser, argument, &operator, value);
+
+ pm_keyword_hash_node_elements_append(bare_hash, argument);
+ argument = (pm_node_t *) bare_hash;
+
+ // Then parse more if we have a comma
+ if (accept1(parser, PM_TOKEN_COMMA) && (
+ token_begins_expression_p(parser->current.type) ||
+ match2(parser, PM_TOKEN_USTAR_STAR, PM_TOKEN_LABEL)
+ )) {
+ contains_keyword_splat = parse_assocs(parser, &hash_keys, (pm_node_t *) bare_hash);
+ }
+
+ pm_static_literals_free(&hash_keys);
+ parsed_bare_hash = true;
+ } else if (accept1(parser, PM_TOKEN_KEYWORD_IN)) {
+ // TODO: Could we solve this with binding powers instead?
+ pm_parser_err_current(parser, PM_ERR_ARGUMENT_IN);
+ }
+
+ parse_arguments_append(parser, arguments, argument);
+ if (contains_keyword_splat) {
+ pm_node_flag_set((pm_node_t *)arguments->arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT);
+ }
+ break;
+ }
+ }
+
+ parsed_first_argument = true;
+
+ // If parsing the argument failed, we need to stop parsing arguments.
+ if (PM_NODE_TYPE_P(argument, PM_MISSING_NODE) || parser->recovering) break;
+
+ // If the terminator of these arguments is not EOF, then we have a specific
+ // token we're looking for. In that case we can accept a newline here
+ // because it is not functioning as a statement terminator.
+ if (terminator != PM_TOKEN_EOF) accept1(parser, PM_TOKEN_NEWLINE);
+
+ if (parser->previous.type == PM_TOKEN_COMMA && parsed_bare_hash) {
+ // If we previously were on a comma and we just parsed a bare hash, then
+ // we want to continue parsing arguments. This is because the comma was
+ // grabbed up by the hash parser.
+ } else {
+ // If there is no comma at the end of the argument list then we're done
+ // parsing arguments and can break out of this loop.
+ if (!accept1(parser, PM_TOKEN_COMMA)) break;
+ }
+
+ // If we hit the terminator, then that means we have a trailing comma so we
+ // can accept that output as well.
+ if (match1(parser, terminator)) break;
+ }
+}
+
+/**
+ * Required parameters on method, block, and lambda declarations can be
+ * destructured using parentheses. This looks like:
+ *
+ * def foo((bar, baz))
+ * end
+ *
+ *
+ * It can recurse infinitely down, and splats are allowed to group arguments.
+ */
+static pm_multi_target_node_t *
+parse_required_destructured_parameter(pm_parser_t *parser) {
+ expect1(parser, PM_TOKEN_PARENTHESIS_LEFT, PM_ERR_EXPECT_LPAREN_REQ_PARAMETER);
+
+ pm_multi_target_node_t *node = pm_multi_target_node_create(parser);
+ pm_multi_target_node_opening_set(node, &parser->previous);
+
+ do {
+ pm_node_t *param;
+
+ // If we get here then we have a trailing comma, which isn't allowed in
+ // the grammar. In other places, multi targets _do_ allow trailing
+ // commas, so here we'll assume this is a mistake of the user not
+ // knowing it's not allowed here.
+ if (node->lefts.size > 0 && match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
+ param = (pm_node_t *) pm_implicit_rest_node_create(parser, &parser->previous);
+ pm_multi_target_node_targets_append(parser, node, param);
+ pm_parser_err_current(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
+ break;
+ }
+
+ if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
+ param = (pm_node_t *) parse_required_destructured_parameter(parser);
+ } else if (accept1(parser, PM_TOKEN_USTAR)) {
+ pm_token_t star = parser->previous;
+ pm_node_t *value = NULL;
+
+ if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
+ pm_token_t name = parser->previous;
+ value = (pm_node_t *) pm_required_parameter_node_create(parser, &name);
+ if (pm_parser_parameter_name_check(parser, &name)) {
+ pm_node_flag_set_repeated_parameter(value);
+ }
+ pm_parser_local_add_token(parser, &name, 1);
+ }
+
+ param = (pm_node_t *) pm_splat_node_create(parser, &star, value);
+ } else {
+ expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_EXPECT_IDENT_REQ_PARAMETER);
+ pm_token_t name = parser->previous;
+
+ param = (pm_node_t *) pm_required_parameter_node_create(parser, &name);
+ if (pm_parser_parameter_name_check(parser, &name)) {
+ pm_node_flag_set_repeated_parameter(param);
+ }
+ pm_parser_local_add_token(parser, &name, 1);
+ }
+
+ pm_multi_target_node_targets_append(parser, node, param);
+ } while (accept1(parser, PM_TOKEN_COMMA));
+
+ accept1(parser, PM_TOKEN_NEWLINE);
+ expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN_REQ_PARAMETER);
+ pm_multi_target_node_closing_set(node, &parser->previous);
+
+ return node;
+}
+
+/**
+ * This represents the different order states we can be in when parsing
+ * method parameters.
+ */
+typedef enum {
+ PM_PARAMETERS_NO_CHANGE = 0, // Extra state for tokens that should not change the state
+ PM_PARAMETERS_ORDER_NOTHING_AFTER = 1,
+ PM_PARAMETERS_ORDER_KEYWORDS_REST,
+ PM_PARAMETERS_ORDER_KEYWORDS,
+ PM_PARAMETERS_ORDER_REST,
+ PM_PARAMETERS_ORDER_AFTER_OPTIONAL,
+ PM_PARAMETERS_ORDER_OPTIONAL,
+ PM_PARAMETERS_ORDER_NAMED,
+ PM_PARAMETERS_ORDER_NONE,
+
+} pm_parameters_order_t;
+
+/**
+ * This matches parameters tokens with parameters state.
+ */
+static pm_parameters_order_t parameters_ordering[PM_TOKEN_MAXIMUM] = {
+ [0] = PM_PARAMETERS_NO_CHANGE,
+ [PM_TOKEN_UAMPERSAND] = PM_PARAMETERS_ORDER_NOTHING_AFTER,
+ [PM_TOKEN_AMPERSAND] = PM_PARAMETERS_ORDER_NOTHING_AFTER,
+ [PM_TOKEN_UDOT_DOT_DOT] = PM_PARAMETERS_ORDER_NOTHING_AFTER,
+ [PM_TOKEN_IDENTIFIER] = PM_PARAMETERS_ORDER_NAMED,
+ [PM_TOKEN_PARENTHESIS_LEFT] = PM_PARAMETERS_ORDER_NAMED,
+ [PM_TOKEN_EQUAL] = PM_PARAMETERS_ORDER_OPTIONAL,
+ [PM_TOKEN_LABEL] = PM_PARAMETERS_ORDER_KEYWORDS,
+ [PM_TOKEN_USTAR] = PM_PARAMETERS_ORDER_AFTER_OPTIONAL,
+ [PM_TOKEN_STAR] = PM_PARAMETERS_ORDER_AFTER_OPTIONAL,
+ [PM_TOKEN_USTAR_STAR] = PM_PARAMETERS_ORDER_KEYWORDS_REST,
+ [PM_TOKEN_STAR_STAR] = PM_PARAMETERS_ORDER_KEYWORDS_REST
+};
+
+/**
+ * Check if current parameter follows valid parameters ordering. If not it adds
+ * an error to the list without stopping the parsing, otherwise sets the
+ * parameters state to the one corresponding to the current parameter.
+ */
+static void
+update_parameter_state(pm_parser_t *parser, pm_token_t *token, pm_parameters_order_t *current) {
+ pm_parameters_order_t state = parameters_ordering[token->type];
+ if (state == PM_PARAMETERS_NO_CHANGE) return;
+
+ // If we see another ordered argument after a optional argument
+ // we only continue parsing ordered arguments until we stop seeing ordered arguments.
+ if (*current == PM_PARAMETERS_ORDER_OPTIONAL && state == PM_PARAMETERS_ORDER_NAMED) {
+ *current = PM_PARAMETERS_ORDER_AFTER_OPTIONAL;
+ return;
+ } else if (*current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL && state == PM_PARAMETERS_ORDER_NAMED) {
+ return;
+ }
+
+ if (token->type == PM_TOKEN_USTAR && *current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL) {
+ pm_parser_err_token(parser, token, PM_ERR_PARAMETER_STAR);
+ }
+
+ if (*current == PM_PARAMETERS_ORDER_NOTHING_AFTER || state > *current) {
+ // We know what transition we failed on, so we can provide a better error here.
+ pm_parser_err_token(parser, token, PM_ERR_PARAMETER_ORDER);
+ } else if (state < *current) {
+ *current = state;
+ }
+}
+
+/**
+ * Parse a list of parameters on a method definition.
+ */
+static pm_parameters_node_t *
+parse_parameters(
+ pm_parser_t *parser,
+ pm_binding_power_t binding_power,
+ bool uses_parentheses,
+ bool allows_trailing_comma,
+ bool allows_forwarding_parameters
+) {
+ pm_parameters_node_t *params = pm_parameters_node_create(parser);
+ bool looping = true;
+
+ pm_do_loop_stack_push(parser, false);
+ pm_parameters_order_t order = PM_PARAMETERS_ORDER_NONE;
+
+ do {
+ switch (parser->current.type) {
+ case PM_TOKEN_PARENTHESIS_LEFT: {
+ update_parameter_state(parser, &parser->current, &order);
+ pm_node_t *param = (pm_node_t *) parse_required_destructured_parameter(parser);
+
+ if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) {
+ pm_parameters_node_requireds_append(params, param);
+ } else {
+ pm_parameters_node_posts_append(params, param);
+ }
+ break;
+ }
+ case PM_TOKEN_UAMPERSAND:
+ case PM_TOKEN_AMPERSAND: {
+ update_parameter_state(parser, &parser->current, &order);
+ parser_lex(parser);
+
+ pm_token_t operator = parser->previous;
+ pm_token_t name;
+
+ bool repeated = false;
+ if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
+ name = parser->previous;
+ repeated = pm_parser_parameter_name_check(parser, &name);
+ pm_parser_local_add_token(parser, &name, 1);
+ } else {
+ name = not_provided(parser);
+ parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK;
+ }
+
+ pm_block_parameter_node_t *param = pm_block_parameter_node_create(parser, &name, &operator);
+ if (repeated) {
+ pm_node_flag_set_repeated_parameter((pm_node_t *)param);
+ }
+ if (params->block == NULL) {
+ pm_parameters_node_block_set(params, param);
+ } else {
+ pm_parser_err_node(parser, (pm_node_t *) param, PM_ERR_PARAMETER_BLOCK_MULTI);
+ pm_parameters_node_posts_append(params, (pm_node_t *) param);
+ }
+
+ break;
+ }
+ case PM_TOKEN_UDOT_DOT_DOT: {
+ if (!allows_forwarding_parameters) {
+ pm_parser_err_current(parser, PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES);
+ }
+
+ if (order > PM_PARAMETERS_ORDER_NOTHING_AFTER) {
+ update_parameter_state(parser, &parser->current, &order);
+ parser_lex(parser);
+
+ parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_ALL;
+
+ pm_forwarding_parameter_node_t *param = pm_forwarding_parameter_node_create(parser, &parser->previous);
+ if (params->keyword_rest != NULL) {
+ // If we already have a keyword rest parameter, then we replace it with the
+ // forwarding parameter and move the keyword rest parameter to the posts list.
+ pm_node_t *keyword_rest = params->keyword_rest;
+ pm_parameters_node_posts_append(params, keyword_rest);
+ pm_parser_err_previous(parser, PM_ERR_PARAMETER_UNEXPECTED_FWD);
+ params->keyword_rest = NULL;
+ }
+ pm_parameters_node_keyword_rest_set(params, (pm_node_t *)param);
+ } else {
+ update_parameter_state(parser, &parser->current, &order);
+ parser_lex(parser);
+ }
+
+ break;
+ }
+ case PM_TOKEN_CLASS_VARIABLE:
+ case PM_TOKEN_IDENTIFIER:
+ case PM_TOKEN_CONSTANT:
+ case PM_TOKEN_INSTANCE_VARIABLE:
+ case PM_TOKEN_GLOBAL_VARIABLE:
+ case PM_TOKEN_METHOD_NAME: {
+ parser_lex(parser);
+ switch (parser->previous.type) {
+ case PM_TOKEN_CONSTANT:
+ pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_CONSTANT);
+ break;
+ case PM_TOKEN_INSTANCE_VARIABLE:
+ pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_IVAR);
+ break;
+ case PM_TOKEN_GLOBAL_VARIABLE:
+ pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_GLOBAL);
+ break;
+ case PM_TOKEN_CLASS_VARIABLE:
+ pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_CLASS);
+ break;
+ case PM_TOKEN_METHOD_NAME:
+ pm_parser_err_previous(parser, PM_ERR_PARAMETER_METHOD_NAME);
+ break;
+ default: break;
+ }
+
+ if (parser->current.type == PM_TOKEN_EQUAL) {
+ update_parameter_state(parser, &parser->current, &order);
+ } else {
+ update_parameter_state(parser, &parser->previous, &order);
+ }
+
+ pm_token_t name = parser->previous;
+ bool repeated = pm_parser_parameter_name_check(parser, &name);
+ pm_parser_local_add_token(parser, &name, 1);
+
+ if (accept1(parser, PM_TOKEN_EQUAL)) {
+ pm_token_t operator = parser->previous;
+ context_push(parser, PM_CONTEXT_DEFAULT_PARAMS);
+
+ pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &name);
+ uint32_t reads = pm_locals_reads(&parser->current_scope->locals, name_id);
+
+ pm_node_t *value = parse_value_expression(parser, binding_power, false, PM_ERR_PARAMETER_NO_DEFAULT);
+ pm_optional_parameter_node_t *param = pm_optional_parameter_node_create(parser, &name, &operator, value);
+
+ if (repeated) {
+ pm_node_flag_set_repeated_parameter((pm_node_t *)param);
+ }
+ pm_parameters_node_optionals_append(params, param);
+
+ // If the value of the parameter increased the number of
+ // reads of that parameter, then we need to warn that we
+ // have a circular definition.
+ if (pm_locals_reads(&parser->current_scope->locals, name_id) != reads) {
+ PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, name, PM_ERR_PARAMETER_CIRCULAR);
+ }
+
+ context_pop(parser);
+
+ // If parsing the value of the parameter resulted in error recovery,
+ // then we can put a missing node in its place and stop parsing the
+ // parameters entirely now.
+ if (parser->recovering) {
+ looping = false;
+ break;
+ }
+ } else if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) {
+ pm_required_parameter_node_t *param = pm_required_parameter_node_create(parser, &name);
+ if (repeated) {
+ pm_node_flag_set_repeated_parameter((pm_node_t *)param);
+ }
+ pm_parameters_node_requireds_append(params, (pm_node_t *) param);
+ } else {
+ pm_required_parameter_node_t *param = pm_required_parameter_node_create(parser, &name);
+ if (repeated) {
+ pm_node_flag_set_repeated_parameter((pm_node_t *)param);
+ }
+ pm_parameters_node_posts_append(params, (pm_node_t *) param);
+ }
+
+ break;
+ }
+ case PM_TOKEN_LABEL: {
+ if (!uses_parentheses) parser->in_keyword_arg = true;
+ update_parameter_state(parser, &parser->current, &order);
+ parser_lex(parser);
+
+ pm_token_t name = parser->previous;
+ pm_token_t local = name;
+ local.end -= 1;
+
+ bool repeated = pm_parser_parameter_name_check(parser, &local);
+ pm_parser_local_add_token(parser, &local, 1);
+
+ switch (parser->current.type) {
+ case PM_TOKEN_COMMA:
+ case PM_TOKEN_PARENTHESIS_RIGHT:
+ case PM_TOKEN_PIPE: {
+ pm_node_t *param = (pm_node_t *) pm_required_keyword_parameter_node_create(parser, &name);
+ if (repeated) {
+ pm_node_flag_set_repeated_parameter(param);
+ }
+ pm_parameters_node_keywords_append(params, param);
+ break;
+ }
+ case PM_TOKEN_SEMICOLON:
+ case PM_TOKEN_NEWLINE: {
+ if (uses_parentheses) {
+ looping = false;
+ break;
+ }
+
+ pm_node_t *param = (pm_node_t *) pm_required_keyword_parameter_node_create(parser, &name);
+ if (repeated) {
+ pm_node_flag_set_repeated_parameter(param);
+ }
+ pm_parameters_node_keywords_append(params, param);
+ break;
+ }
+ default: {
+ pm_node_t *param;
+
+ if (token_begins_expression_p(parser->current.type)) {
+ context_push(parser, PM_CONTEXT_DEFAULT_PARAMS);
+
+ pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &local);
+ uint32_t reads = pm_locals_reads(&parser->current_scope->locals, name_id);
+ pm_node_t *value = parse_value_expression(parser, binding_power, false, PM_ERR_PARAMETER_NO_DEFAULT_KW);
+
+ if (pm_locals_reads(&parser->current_scope->locals, name_id) != reads) {
+ PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, local, PM_ERR_PARAMETER_CIRCULAR);
+ }
+
+ context_pop(parser);
+ param = (pm_node_t *) pm_optional_keyword_parameter_node_create(parser, &name, value);
+ }
+ else {
+ param = (pm_node_t *) pm_required_keyword_parameter_node_create(parser, &name);
+ }
+
+ if (repeated) {
+ pm_node_flag_set_repeated_parameter(param);
+ }
+ pm_parameters_node_keywords_append(params, param);
+
+ // If parsing the value of the parameter resulted in error recovery,
+ // then we can put a missing node in its place and stop parsing the
+ // parameters entirely now.
+ if (parser->recovering) {
+ looping = false;
+ break;
+ }
+ }
+ }
+
+ parser->in_keyword_arg = false;
+ break;
+ }
+ case PM_TOKEN_USTAR:
+ case PM_TOKEN_STAR: {
+ update_parameter_state(parser, &parser->current, &order);
+ parser_lex(parser);
+
+ pm_token_t operator = parser->previous;
+ pm_token_t name;
+ bool repeated = false;
+ if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
+ name = parser->previous;
+ repeated = pm_parser_parameter_name_check(parser, &name);
+ pm_parser_local_add_token(parser, &name, 1);
+ } else {
+ name = not_provided(parser);
+ parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS;
+ }
+
+ pm_node_t *param = (pm_node_t *) pm_rest_parameter_node_create(parser, &operator, &name);
+ if (repeated) {
+ pm_node_flag_set_repeated_parameter(param);
+ }
+ if (params->rest == NULL) {
+ pm_parameters_node_rest_set(params, param);
+ } else {
+ pm_parser_err_node(parser, param, PM_ERR_PARAMETER_SPLAT_MULTI);
+ pm_parameters_node_posts_append(params, param);
+ }
+
+ break;
+ }
+ case PM_TOKEN_STAR_STAR:
+ case PM_TOKEN_USTAR_STAR: {
+ update_parameter_state(parser, &parser->current, &order);
+ parser_lex(parser);
+
+ pm_token_t operator = parser->previous;
+ pm_node_t *param;
+
+ if (accept1(parser, PM_TOKEN_KEYWORD_NIL)) {
+ param = (pm_node_t *) pm_no_keywords_parameter_node_create(parser, &operator, &parser->previous);
+ } else {
+ pm_token_t name;
+
+ bool repeated = false;
+ if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
+ name = parser->previous;
+ repeated = pm_parser_parameter_name_check(parser, &name);
+ pm_parser_local_add_token(parser, &name, 1);
+ } else {
+ name = not_provided(parser);
+ parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS;
+ }
+
+ param = (pm_node_t *) pm_keyword_rest_parameter_node_create(parser, &operator, &name);
+ if (repeated) {
+ pm_node_flag_set_repeated_parameter(param);
+ }
+ }
+
+ if (params->keyword_rest == NULL) {
+ pm_parameters_node_keyword_rest_set(params, param);
+ } else {
+ pm_parser_err_node(parser, param, PM_ERR_PARAMETER_ASSOC_SPLAT_MULTI);
+ pm_parameters_node_posts_append(params, param);
+ }
+
+ break;
+ }
+ default:
+ if (parser->previous.type == PM_TOKEN_COMMA) {
+ if (allows_trailing_comma) {
+ // If we get here, then we have a trailing comma in a
+ // block parameter list.
+ pm_node_t *param = (pm_node_t *) pm_implicit_rest_node_create(parser, &parser->previous);
+
+ if (params->rest == NULL) {
+ pm_parameters_node_rest_set(params, param);
+ } else {
+ pm_parser_err_node(parser, (pm_node_t *) param, PM_ERR_PARAMETER_SPLAT_MULTI);
+ pm_parameters_node_posts_append(params, (pm_node_t *) param);
+ }
+ } else {
+ pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
+ }
+ }
+
+ looping = false;
+ break;
+ }
+
+ if (looping && uses_parentheses) {
+ accept1(parser, PM_TOKEN_NEWLINE);
+ }
+ } while (looping && accept1(parser, PM_TOKEN_COMMA));
+
+ pm_do_loop_stack_pop(parser);
+
+ // If we don't have any parameters, return `NULL` instead of an empty `ParametersNode`.
+ if (params->base.location.start == params->base.location.end) {
+ pm_node_destroy(parser, (pm_node_t *) params);
+ return NULL;
+ }
+
+ return params;
+}
+
+typedef enum {
+ PM_RESCUES_BEGIN = 1,
+ PM_RESCUES_BLOCK,
+ PM_RESCUES_CLASS,
+ PM_RESCUES_DEF,
+ PM_RESCUES_LAMBDA,
+ PM_RESCUES_MODULE,
+ PM_RESCUES_SCLASS
+} pm_rescues_type_t;
+
+/**
+ * Parse any number of rescue clauses. This will form a linked list of if
+ * nodes pointing to each other from the top.
+ */
+static inline void
+parse_rescues(pm_parser_t *parser, pm_begin_node_t *parent_node, pm_rescues_type_t type) {
+ pm_rescue_node_t *current = NULL;
+
+ while (accept1(parser, PM_TOKEN_KEYWORD_RESCUE)) {
+ pm_rescue_node_t *rescue = pm_rescue_node_create(parser, &parser->previous);
+
+ switch (parser->current.type) {
+ case PM_TOKEN_EQUAL_GREATER: {
+ // Here we have an immediate => after the rescue keyword, in which case
+ // we're going to have an empty list of exceptions to rescue (which
+ // implies StandardError).
+ parser_lex(parser);
+ pm_rescue_node_operator_set(rescue, &parser->previous);
+
+ pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, false, PM_ERR_RESCUE_VARIABLE);
+ reference = parse_target(parser, reference);
+
+ pm_rescue_node_reference_set(rescue, reference);
+ break;
+ }
+ case PM_TOKEN_NEWLINE:
+ case PM_TOKEN_SEMICOLON:
+ case PM_TOKEN_KEYWORD_THEN:
+ // Here we have a terminator for the rescue keyword, in which case we're
+ // going to just continue on.
+ break;
+ default: {
+ if (token_begins_expression_p(parser->current.type) || match1(parser, PM_TOKEN_USTAR)) {
+ // Here we have something that could be an exception expression, so
+ // we'll attempt to parse it here and any others delimited by commas.
+
+ do {
+ pm_node_t *expression = parse_starred_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_RESCUE_EXPRESSION);
+ pm_rescue_node_exceptions_append(rescue, expression);
+
+ // If we hit a newline, then this is the end of the rescue expression. We
+ // can continue on to parse the statements.
+ if (match3(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_KEYWORD_THEN)) break;
+
+ // If we hit a `=>` then we're going to parse the exception variable. Once
+ // we've done that, we'll break out of the loop and parse the statements.
+ if (accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
+ pm_rescue_node_operator_set(rescue, &parser->previous);
+
+ pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, false, PM_ERR_RESCUE_VARIABLE);
+ reference = parse_target(parser, reference);
+
+ pm_rescue_node_reference_set(rescue, reference);
+ break;
+ }
+ } while (accept1(parser, PM_TOKEN_COMMA));
+ }
+ }
+ }
+
+ if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
+ accept1(parser, PM_TOKEN_KEYWORD_THEN);
+ } else {
+ expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_RESCUE_TERM);
+ }
+
+ if (!match3(parser, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_END)) {
+ pm_accepts_block_stack_push(parser, true);
+ pm_context_t context;
+
+ switch (type) {
+ case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_RESCUE; break;
+ case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_RESCUE; break;
+ case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_RESCUE; break;
+ case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_RESCUE; break;
+ case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_RESCUE; break;
+ case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_RESCUE; break;
+ case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_RESCUE; break;
+ default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_RESCUE; break;
+ }
+
+ pm_statements_node_t *statements = parse_statements(parser, context);
+ if (statements != NULL) pm_rescue_node_statements_set(rescue, statements);
+
+ pm_accepts_block_stack_pop(parser);
+ accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
+ }
+
+ if (current == NULL) {
+ pm_begin_node_rescue_clause_set(parent_node, rescue);
+ } else {
+ pm_rescue_node_consequent_set(current, rescue);
+ }
+
+ current = rescue;
+ }
+
+ // The end node locations on rescue nodes will not be set correctly
+ // since we won't know the end until we've found all consequent
+ // clauses. This sets the end location on all rescues once we know it.
+ if (current) {
+ const uint8_t *end_to_set = current->base.location.end;
+ current = parent_node->rescue_clause;
+ while (current) {
+ current->base.location.end = end_to_set;
+ current = current->consequent;
+ }
+ }
+
+ if (accept1(parser, PM_TOKEN_KEYWORD_ELSE)) {
+ pm_token_t else_keyword = parser->previous;
+ accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
+
+ pm_statements_node_t *else_statements = NULL;
+ if (!match2(parser, PM_TOKEN_KEYWORD_END, PM_TOKEN_KEYWORD_ENSURE)) {
+ pm_accepts_block_stack_push(parser, true);
+ pm_context_t context;
+
+ switch (type) {
+ case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_ELSE; break;
+ case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_ELSE; break;
+ case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_ELSE; break;
+ case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_ELSE; break;
+ case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_ELSE; break;
+ case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_ELSE; break;
+ case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_ELSE; break;
+ default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_RESCUE; break;
+ }
+
+ else_statements = parse_statements(parser, context);
+ pm_accepts_block_stack_pop(parser);
+
+ accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
+ }
+
+ pm_else_node_t *else_clause = pm_else_node_create(parser, &else_keyword, else_statements, &parser->current);
+ pm_begin_node_else_clause_set(parent_node, else_clause);
+ }
+
+ if (accept1(parser, PM_TOKEN_KEYWORD_ENSURE)) {
+ pm_token_t ensure_keyword = parser->previous;
+ accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
+
+ pm_statements_node_t *ensure_statements = NULL;
+ if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
+ pm_accepts_block_stack_push(parser, true);
+ pm_context_t context;
+
+ switch (type) {
+ case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_ENSURE; break;
+ case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_ENSURE; break;
+ case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_ENSURE; break;
+ case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_ENSURE; break;
+ case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_ENSURE; break;
+ case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_ENSURE; break;
+ case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_ENSURE; break;
+ default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_RESCUE; break;
+ }
+
+ ensure_statements = parse_statements(parser, context);
+ pm_accepts_block_stack_pop(parser);
+
+ accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
+ }
+
+ pm_ensure_node_t *ensure_clause = pm_ensure_node_create(parser, &ensure_keyword, ensure_statements, &parser->current);
+ pm_begin_node_ensure_clause_set(parent_node, ensure_clause);
+ }
+
+ if (parser->current.type == PM_TOKEN_KEYWORD_END) {
+ pm_begin_node_end_keyword_set(parent_node, &parser->current);
+ } else {
+ pm_token_t end_keyword = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
+ pm_begin_node_end_keyword_set(parent_node, &end_keyword);
+ }
+}
+
+/**
+ * Parse a set of rescue clauses with an implicit begin (for example when on a
+ * class, module, def, etc.).
+ */
+static pm_begin_node_t *
+parse_rescues_implicit_begin(pm_parser_t *parser, const uint8_t *start, pm_statements_node_t *statements, pm_rescues_type_t type) {
+ pm_token_t begin_keyword = not_provided(parser);
+ pm_begin_node_t *node = pm_begin_node_create(parser, &begin_keyword, statements);
+
+ parse_rescues(parser, node, type);
+ node->base.location.start = start;
+
+ return node;
+}
+
+/**
+ * Parse a list of parameters and local on a block definition.
+ */
+static pm_block_parameters_node_t *
+parse_block_parameters(
+ pm_parser_t *parser,
+ bool allows_trailing_comma,
+ const pm_token_t *opening,
+ bool is_lambda_literal
+) {
+ pm_parameters_node_t *parameters = NULL;
+ if (!match1(parser, PM_TOKEN_SEMICOLON)) {
+ parameters = parse_parameters(
+ parser,
+ is_lambda_literal ? PM_BINDING_POWER_DEFINED : PM_BINDING_POWER_INDEX,
+ false,
+ allows_trailing_comma,
+ false
+ );
+ }
+
+ pm_block_parameters_node_t *block_parameters = pm_block_parameters_node_create(parser, parameters, opening);
+ if ((opening->type != PM_TOKEN_NOT_PROVIDED)) {
+ accept1(parser, PM_TOKEN_NEWLINE);
+
+ if (accept1(parser, PM_TOKEN_SEMICOLON)) {
+ do {
+ switch (parser->current.type) {
+ case PM_TOKEN_CONSTANT:
+ pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_CONSTANT);
+ parser_lex(parser);
+ break;
+ case PM_TOKEN_INSTANCE_VARIABLE:
+ pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_IVAR);
+ parser_lex(parser);
+ break;
+ case PM_TOKEN_GLOBAL_VARIABLE:
+ pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_GLOBAL);
+ parser_lex(parser);
+ break;
+ case PM_TOKEN_CLASS_VARIABLE:
+ pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_CLASS);
+ parser_lex(parser);
+ break;
+ default:
+ expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_BLOCK_PARAM_LOCAL_VARIABLE);
+ break;
+ }
+
+ bool repeated = pm_parser_parameter_name_check(parser, &parser->previous);
+ pm_parser_local_add_token(parser, &parser->previous, 1);
+
+ pm_block_local_variable_node_t *local = pm_block_local_variable_node_create(parser, &parser->previous);
+ if (repeated) pm_node_flag_set_repeated_parameter((pm_node_t *) local);
+
+ pm_block_parameters_node_append_local(block_parameters, local);
+ } while (accept1(parser, PM_TOKEN_COMMA));
+ }
+ }
+
+ return block_parameters;
+}
+
+/**
+ * Return the node that should be used in the parameters field of a block-like
+ * (block or lambda) node, depending on the kind of parameters that were
+ * declared in the current scope.
+ */
+static pm_node_t *
+parse_blocklike_parameters(pm_parser_t *parser, pm_node_t *parameters, const pm_token_t *opening, const pm_token_t *closing) {
+ uint8_t masked = parser->current_scope->parameters & PM_SCOPE_PARAMETERS_TYPE_MASK;
+
+ if (masked == PM_SCOPE_PARAMETERS_NONE) {
+ assert(parameters == NULL);
+ return NULL;
+ } else if (masked == PM_SCOPE_PARAMETERS_ORDINARY) {
+ assert(parameters != NULL);
+ return parameters;
+ } else if (masked == PM_SCOPE_PARAMETERS_NUMBERED) {
+ assert(parameters == NULL);
+
+ int8_t maximum = parser->current_scope->numbered_parameters;
+ if (maximum > 0) {
+ const pm_location_t location = { .start = opening->start, .end = closing->end };
+ return (pm_node_t *) pm_numbered_parameters_node_create(parser, &location, (uint8_t) maximum);
+ }
+
+ return NULL;
+ } else if (masked == PM_SCOPE_PARAMETERS_IT) {
+ assert(parameters == NULL);
+ return (pm_node_t *) pm_it_parameters_node_create(parser, opening, closing);
+ } else {
+ assert(false && "unreachable");
+ return NULL;
+ }
+}
+
+/**
+ * Parse a block.
+ */
+static pm_block_node_t *
+parse_block(pm_parser_t *parser) {
+ pm_token_t opening = parser->previous;
+ accept1(parser, PM_TOKEN_NEWLINE);
+
+ pm_accepts_block_stack_push(parser, true);
+ pm_parser_scope_push(parser, false);
+
+ pm_block_parameters_node_t *block_parameters = NULL;
+
+ if (accept1(parser, PM_TOKEN_PIPE)) {
+ assert(parser->current_scope->parameters == PM_SCOPE_PARAMETERS_NONE);
+ parser->current_scope->parameters = PM_SCOPE_PARAMETERS_ORDINARY;
+
+ pm_token_t block_parameters_opening = parser->previous;
+ if (match1(parser, PM_TOKEN_PIPE)) {
+ block_parameters = pm_block_parameters_node_create(parser, NULL, &block_parameters_opening);
+ parser->command_start = true;
+ parser_lex(parser);
+ } else {
+ block_parameters = parse_block_parameters(parser, true, &block_parameters_opening, false);
+ accept1(parser, PM_TOKEN_NEWLINE);
+ parser->command_start = true;
+ expect1(parser, PM_TOKEN_PIPE, PM_ERR_BLOCK_PARAM_PIPE_TERM);
+ }
+
+ pm_block_parameters_node_closing_set(block_parameters, &parser->previous);
+ }
+
+ accept1(parser, PM_TOKEN_NEWLINE);
+ pm_node_t *statements = NULL;
+
+ if (opening.type == PM_TOKEN_BRACE_LEFT) {
+ if (!match1(parser, PM_TOKEN_BRACE_RIGHT)) {
+ statements = (pm_node_t *) parse_statements(parser, PM_CONTEXT_BLOCK_BRACES);
+ }
+
+ expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BLOCK_TERM_BRACE);
+ } else {
+ if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
+ if (!match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_ENSURE)) {
+ pm_accepts_block_stack_push(parser, true);
+ statements = (pm_node_t *) parse_statements(parser, PM_CONTEXT_BLOCK_KEYWORDS);
+ pm_accepts_block_stack_pop(parser);
+ }
+
+ if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
+ assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
+ statements = (pm_node_t *) parse_rescues_implicit_begin(parser, opening.start, (pm_statements_node_t *) statements, PM_RESCUES_BLOCK);
+ }
+ }
+
+ expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BLOCK_TERM_END);
+ }
+
+ pm_constant_id_list_t locals;
+ pm_locals_order(parser, &parser->current_scope->locals, &locals, pm_parser_scope_toplevel_p(parser));
+ pm_node_t *parameters = parse_blocklike_parameters(parser, (pm_node_t *) block_parameters, &opening, &parser->previous);
+
+ pm_parser_scope_pop(parser);
+ pm_accepts_block_stack_pop(parser);
+
+ return pm_block_node_create(parser, &locals, &opening, parameters, statements, &parser->previous);
+}
+
+/**
+ * Parse a list of arguments and their surrounding parentheses if they are
+ * present. It returns true if it found any pieces of arguments (parentheses,
+ * arguments, or blocks).
+ */
+static bool
+parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_block, bool accepts_command_call) {
+ bool found = false;
+
+ if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
+ found |= true;
+ arguments->opening_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous);
+
+ if (accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
+ arguments->closing_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous);
+ } else {
+ pm_accepts_block_stack_push(parser, true);
+ parse_arguments(parser, arguments, true, PM_TOKEN_PARENTHESIS_RIGHT);
+
+ if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_ARGUMENT_TERM_PAREN, pm_token_type_human(parser->current.type));
+ parser->previous.start = parser->previous.end;
+ parser->previous.type = PM_TOKEN_MISSING;
+ }
+
+ pm_accepts_block_stack_pop(parser);
+ arguments->closing_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous);
+ }
+ } else if (accepts_command_call && (token_begins_expression_p(parser->current.type) || match3(parser, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR, PM_TOKEN_UAMPERSAND)) && !match1(parser, PM_TOKEN_BRACE_LEFT)) {
+ found |= true;
+ pm_accepts_block_stack_push(parser, false);
+
+ // If we get here, then the subsequent token cannot be used as an infix
+ // operator. In this case we assume the subsequent token is part of an
+ // argument to this method call.
+ parse_arguments(parser, arguments, true, PM_TOKEN_EOF);
+
+ // If we have done with the arguments and still not consumed the comma,
+ // then we have a trailing comma where we need to check whether it is
+ // allowed or not.
+ if (parser->previous.type == PM_TOKEN_COMMA && !match1(parser, PM_TOKEN_SEMICOLON)) {
+ pm_parser_err_previous(parser, PM_ERR_EXPECT_ARGUMENT);
+ }
+
+ pm_accepts_block_stack_pop(parser);
+ }
+
+ // If we're at the end of the arguments, we can now check if there is a block
+ // node that starts with a {. If there is, then we can parse it and add it to
+ // the arguments.
+ if (accepts_block) {
+ pm_block_node_t *block = NULL;
+
+ if (accept1(parser, PM_TOKEN_BRACE_LEFT)) {
+ found |= true;
+ block = parse_block(parser);
+ pm_arguments_validate_block(parser, arguments, block);
+ } else if (pm_accepts_block_stack_p(parser) && accept1(parser, PM_TOKEN_KEYWORD_DO)) {
+ found |= true;
+ block = parse_block(parser);
+ }
+
+ if (block != NULL) {
+ if (arguments->block == NULL && !arguments->has_forwarding) {
+ arguments->block = (pm_node_t *) block;
+ } else {
+ if (arguments->has_forwarding) {
+ pm_parser_err_node(parser, (pm_node_t *) block, PM_ERR_ARGUMENT_BLOCK_FORWARDING);
+ } else {
+ pm_parser_err_node(parser, (pm_node_t *) block, PM_ERR_ARGUMENT_BLOCK_MULTI);
+ }
+ if (arguments->block != NULL) {
+ if (arguments->arguments == NULL) {
+ arguments->arguments = pm_arguments_node_create(parser);
+ }
+ pm_arguments_node_arguments_append(arguments->arguments, arguments->block);
+ }
+ arguments->block = (pm_node_t *) block;
+ }
+ }
+ }
+
+ return found;
+}
+
+/**
+ * Check that the block exit (next, break, redo) is allowed in the current
+ * context. If it isn't, add an error to the parser.
+ */
+static void
+parse_block_exit(pm_parser_t *parser, pm_node_t *node, const char *type) {
+ pm_context_node_t *context_node = parser->current_context;
+ bool through_expression = false;
+
+ while (context_node != NULL) {
+ switch (context_node->context) {
+ case PM_CONTEXT_BLOCK_BRACES:
+ case PM_CONTEXT_BLOCK_KEYWORDS:
+ case PM_CONTEXT_BLOCK_ELSE:
+ case PM_CONTEXT_BLOCK_ENSURE:
+ case PM_CONTEXT_BLOCK_RESCUE:
+ case PM_CONTEXT_DEFINED:
+ case PM_CONTEXT_FOR:
+ case PM_CONTEXT_LAMBDA_BRACES:
+ case PM_CONTEXT_LAMBDA_DO_END:
+ case PM_CONTEXT_LAMBDA_ELSE:
+ case PM_CONTEXT_LAMBDA_ENSURE:
+ case PM_CONTEXT_LAMBDA_RESCUE:
+ case PM_CONTEXT_POSTEXE:
+ case PM_CONTEXT_UNTIL:
+ case PM_CONTEXT_WHILE:
+ // These are the good cases. We're allowed to have a block exit
+ // in these contexts.
+ return;
+ case PM_CONTEXT_DEF:
+ case PM_CONTEXT_DEF_PARAMS:
+ case PM_CONTEXT_DEF_ELSE:
+ case PM_CONTEXT_DEF_ENSURE:
+ case PM_CONTEXT_DEF_RESCUE:
+ case PM_CONTEXT_MAIN:
+ case PM_CONTEXT_PREEXE:
+ case PM_CONTEXT_SCLASS:
+ case PM_CONTEXT_SCLASS_ELSE:
+ case PM_CONTEXT_SCLASS_ENSURE:
+ case PM_CONTEXT_SCLASS_RESCUE:
+ // These are the bad cases. We're not allowed to have a block
+ // exit in these contexts.
+
+ if (through_expression) {
+ // If we get here, then we're about to mark this block exit
+ // as invalid. However, it could later _become_ valid if we
+ // find a trailing while/until on the expression. In this
+ // case instead of adding the error here, we'll add the
+ // block exit to the list of exits for the expression, and
+ // the node parsing will handle validating it instead.
+ assert(parser->current_block_exits != NULL);
+ pm_node_list_append(parser->current_block_exits, node);
+ } else {
+ // Otherwise, if we haven't gone through an expression
+ // context, then this is just invalid and we'll add the
+ // error here.
+ PM_PARSER_ERR_NODE_FORMAT(parser, node, PM_ERR_INVALID_BLOCK_EXIT, type);
+ }
+
+ return;
+ case PM_CONTEXT_NONE:
+ // This case should never happen.
+ assert(false && "unreachable");
+ break;
+ case PM_CONTEXT_BEGIN:
+ case PM_CONTEXT_BEGIN_ELSE:
+ case PM_CONTEXT_BEGIN_ENSURE:
+ case PM_CONTEXT_BEGIN_RESCUE:
+ case PM_CONTEXT_CASE_IN:
+ case PM_CONTEXT_CASE_WHEN:
+ case PM_CONTEXT_CLASS:
+ case PM_CONTEXT_CLASS_ELSE:
+ case PM_CONTEXT_CLASS_ENSURE:
+ case PM_CONTEXT_CLASS_RESCUE:
+ case PM_CONTEXT_ELSE:
+ case PM_CONTEXT_ELSIF:
+ case PM_CONTEXT_IF:
+ case PM_CONTEXT_MODULE:
+ case PM_CONTEXT_MODULE_ELSE:
+ case PM_CONTEXT_MODULE_ENSURE:
+ case PM_CONTEXT_MODULE_RESCUE:
+ case PM_CONTEXT_PARENS:
+ case PM_CONTEXT_RESCUE_MODIFIER:
+ case PM_CONTEXT_TERNARY:
+ case PM_CONTEXT_UNLESS:
+ // If we got to an expression that could be modified by a
+ // trailing while/until, then we'll track that we have gotten
+ // here because we need to know it if this block exit is later
+ // marked as invalid.
+ through_expression = true;
+ break;
+ case PM_CONTEXT_EMBEXPR:
+ case PM_CONTEXT_DEFAULT_PARAMS:
+ case PM_CONTEXT_FOR_INDEX:
+ case PM_CONTEXT_PREDICATE:
+ // In these contexts we should continue walking up the list of
+ // contexts.
+ break;
+ }
+
+ context_node = context_node->prev;
+ }
+}
+
+/**
+ * When we hit an expression that could contain block exits, we need to stash
+ * the previous set and create a new one.
+ */
+static pm_node_list_t *
+push_block_exits(pm_parser_t *parser, pm_node_list_t *current_block_exits) {
+ pm_node_list_t *previous_block_exits = parser->current_block_exits;
+ parser->current_block_exits = current_block_exits;
+ return previous_block_exits;
+}
+
+/**
+ * Pop the current level of block exits from the parser, and add errors to the
+ * parser if any of them are deemed to be invalid.
+ */
+static void
+pop_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) {
+ if (match2(parser, PM_TOKEN_KEYWORD_WHILE_MODIFIER, PM_TOKEN_KEYWORD_UNTIL_MODIFIER)) {
+ // If we matched a trailing while/until, then all of the block exits in
+ // the contained list are valid. In this case we do not need to do
+ // anything.
+ } else if (previous_block_exits != NULL) {
+ // If we did not matching a trailing while/until, then all of the block
+ // exits contained in the list are invalid for this specific context.
+ // However, they could still become valid in a higher level context if
+ // there is another list above this one. In this case we'll push all of
+ // the block exits up to the previous list.
+ pm_node_list_concat(previous_block_exits, parser->current_block_exits);
+ } else {
+ // If we did not match a trailing while/until and this was the last
+ // chance to do so, then all of the block exits in the list are invalid
+ // and we need to add an error for each of them.
+ pm_node_t *block_exit;
+ PM_NODE_LIST_FOREACH(parser->current_block_exits, index, block_exit) {
+ const char *type;
+
+ switch (PM_NODE_TYPE(block_exit)) {
+ case PM_BREAK_NODE: type = "break"; break;
+ case PM_NEXT_NODE: type = "next"; break;
+ case PM_REDO_NODE: type = "redo"; break;
+ default: assert(false && "unreachable"); type = ""; break;
+ }
+
+ PM_PARSER_ERR_NODE_FORMAT(parser, block_exit, PM_ERR_INVALID_BLOCK_EXIT, type);
+ }
+ }
+
+ parser->current_block_exits = previous_block_exits;
+}
+
+static inline pm_node_t *
+parse_predicate(pm_parser_t *parser, pm_binding_power_t binding_power, pm_context_t context, pm_token_t *then_keyword) {
+ context_push(parser, PM_CONTEXT_PREDICATE);
+ pm_diagnostic_id_t error_id = context == PM_CONTEXT_IF ? PM_ERR_CONDITIONAL_IF_PREDICATE : PM_ERR_CONDITIONAL_UNLESS_PREDICATE;
+ pm_node_t *predicate = parse_value_expression(parser, binding_power, true, error_id);
+
+ // Predicates are closed by a term, a "then", or a term and then a "then".
+ bool predicate_closed = accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
+
+ if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
+ predicate_closed = true;
+ *then_keyword = parser->previous;
+ }
+
+ if (!predicate_closed) {
+ pm_parser_err_current(parser, PM_ERR_CONDITIONAL_PREDICATE_TERM);
+ }
+
+ context_pop(parser);
+ return predicate;
+}
+
+static inline pm_node_t *
+parse_conditional(pm_parser_t *parser, pm_context_t context) {
+ pm_node_list_t current_block_exits = { 0 };
+ pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
+
+ pm_token_t keyword = parser->previous;
+ pm_token_t then_keyword = not_provided(parser);
+
+ pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_MODIFIER, context, &then_keyword);
+ pm_statements_node_t *statements = NULL;
+
+ if (!match3(parser, PM_TOKEN_KEYWORD_ELSIF, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
+ pm_accepts_block_stack_push(parser, true);
+ statements = parse_statements(parser, context);
+ pm_accepts_block_stack_pop(parser);
+ accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
+ }
+
+ pm_token_t end_keyword = not_provided(parser);
+ pm_node_t *parent = NULL;
+
+ switch (context) {
+ case PM_CONTEXT_IF:
+ parent = (pm_node_t *) pm_if_node_create(parser, &keyword, predicate, &then_keyword, statements, NULL, &end_keyword);
+ break;
+ case PM_CONTEXT_UNLESS:
+ parent = (pm_node_t *) pm_unless_node_create(parser, &keyword, predicate, &then_keyword, statements);
+ break;
+ default:
+ assert(false && "unreachable");
+ break;
+ }
+
+ pm_node_t *current = parent;
+
+ // Parse any number of elsif clauses. This will form a linked list of if
+ // nodes pointing to each other from the top.
+ if (context == PM_CONTEXT_IF) {
+ while (match1(parser, PM_TOKEN_KEYWORD_ELSIF)) {
+ if (parser_end_of_line_p(parser)) {
+ PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, parser->current, PM_WARN_KEYWORD_EOL);
+ }
+
+ pm_token_t elsif_keyword = parser->current;
+ parser_lex(parser);
+
+ pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_MODIFIER, PM_CONTEXT_ELSIF, &then_keyword);
+ pm_accepts_block_stack_push(parser, true);
+
+ pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_ELSIF);
+ pm_accepts_block_stack_pop(parser);
+ accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
+
+ pm_node_t *elsif = (pm_node_t *) pm_if_node_create(parser, &elsif_keyword, predicate, &then_keyword, statements, NULL, &end_keyword);
+ ((pm_if_node_t *) current)->consequent = elsif;
+ current = elsif;
+ }
+ }
+
+ if (match1(parser, PM_TOKEN_KEYWORD_ELSE)) {
+ parser_lex(parser);
+ pm_token_t else_keyword = parser->previous;
+
+ pm_accepts_block_stack_push(parser, true);
+ pm_statements_node_t *else_statements = parse_statements(parser, PM_CONTEXT_ELSE);
+ pm_accepts_block_stack_pop(parser);
+
+ accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
+ expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM_ELSE);
+
+ pm_else_node_t *else_node = pm_else_node_create(parser, &else_keyword, else_statements, &parser->previous);
+
+ switch (context) {
+ case PM_CONTEXT_IF:
+ ((pm_if_node_t *) current)->consequent = (pm_node_t *) else_node;
+ break;
+ case PM_CONTEXT_UNLESS:
+ ((pm_unless_node_t *) parent)->consequent = else_node;
+ break;
+ default:
+ assert(false && "unreachable");
+ break;
+ }
+ } else {
+ // We should specialize this error message to refer to 'if' or 'unless'
+ // explicitly.
+ expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM);
+ }
+
+ // Set the appropriate end location for all of the nodes in the subtree.
+ switch (context) {
+ case PM_CONTEXT_IF: {
+ pm_node_t *current = parent;
+ bool recursing = true;
+
+ while (recursing) {
+ switch (PM_NODE_TYPE(current)) {
+ case PM_IF_NODE:
+ pm_if_node_end_keyword_loc_set((pm_if_node_t *) current, &parser->previous);
+ current = ((pm_if_node_t *) current)->consequent;
+ recursing = current != NULL;
+ break;
+ case PM_ELSE_NODE:
+ pm_else_node_end_keyword_loc_set((pm_else_node_t *) current, &parser->previous);
+ recursing = false;
+ break;
+ default: {
+ recursing = false;
+ break;
+ }
+ }
+ }
+ break;
+ }
+ case PM_CONTEXT_UNLESS:
+ pm_unless_node_end_keyword_loc_set((pm_unless_node_t *) parent, &parser->previous);
+ break;
+ default:
+ assert(false && "unreachable");
+ break;
+ }
+
+ pop_block_exits(parser, previous_block_exits);
+ pm_node_list_free(&current_block_exits);
+
+ return parent;
+}
+
+/**
+ * This macro allows you to define a case statement for all of the keywords.
+ * It's meant to be used in a switch statement.
+ */
+#define PM_CASE_KEYWORD PM_TOKEN_KEYWORD___ENCODING__: case PM_TOKEN_KEYWORD___FILE__: case PM_TOKEN_KEYWORD___LINE__: \
+ case PM_TOKEN_KEYWORD_ALIAS: case PM_TOKEN_KEYWORD_AND: case PM_TOKEN_KEYWORD_BEGIN: case PM_TOKEN_KEYWORD_BEGIN_UPCASE: \
+ case PM_TOKEN_KEYWORD_BREAK: case PM_TOKEN_KEYWORD_CASE: case PM_TOKEN_KEYWORD_CLASS: case PM_TOKEN_KEYWORD_DEF: \
+ case PM_TOKEN_KEYWORD_DEFINED: case PM_TOKEN_KEYWORD_DO: case PM_TOKEN_KEYWORD_DO_LOOP: case PM_TOKEN_KEYWORD_ELSE: \
+ case PM_TOKEN_KEYWORD_ELSIF: case PM_TOKEN_KEYWORD_END: case PM_TOKEN_KEYWORD_END_UPCASE: case PM_TOKEN_KEYWORD_ENSURE: \
+ case PM_TOKEN_KEYWORD_FALSE: case PM_TOKEN_KEYWORD_FOR: case PM_TOKEN_KEYWORD_IF: case PM_TOKEN_KEYWORD_IN: \
+ case PM_TOKEN_KEYWORD_MODULE: case PM_TOKEN_KEYWORD_NEXT: case PM_TOKEN_KEYWORD_NIL: case PM_TOKEN_KEYWORD_NOT: \
+ case PM_TOKEN_KEYWORD_OR: case PM_TOKEN_KEYWORD_REDO: case PM_TOKEN_KEYWORD_RESCUE: case PM_TOKEN_KEYWORD_RETRY: \
+ case PM_TOKEN_KEYWORD_RETURN: case PM_TOKEN_KEYWORD_SELF: case PM_TOKEN_KEYWORD_SUPER: case PM_TOKEN_KEYWORD_THEN: \
+ case PM_TOKEN_KEYWORD_TRUE: case PM_TOKEN_KEYWORD_UNDEF: case PM_TOKEN_KEYWORD_UNLESS: case PM_TOKEN_KEYWORD_UNTIL: \
+ case PM_TOKEN_KEYWORD_WHEN: case PM_TOKEN_KEYWORD_WHILE: case PM_TOKEN_KEYWORD_YIELD
+
+/**
+ * This macro allows you to define a case statement for all of the operators.
+ * It's meant to be used in a switch statement.
+ */
+#define PM_CASE_OPERATOR PM_TOKEN_AMPERSAND: case PM_TOKEN_BACKTICK: case PM_TOKEN_BANG_EQUAL: \
+ case PM_TOKEN_BANG_TILDE: case PM_TOKEN_BANG: case PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL: \
+ case PM_TOKEN_BRACKET_LEFT_RIGHT: case PM_TOKEN_CARET: case PM_TOKEN_EQUAL_EQUAL_EQUAL: case PM_TOKEN_EQUAL_EQUAL: \
+ case PM_TOKEN_EQUAL_TILDE: case PM_TOKEN_GREATER_EQUAL: case PM_TOKEN_GREATER_GREATER: case PM_TOKEN_GREATER: \
+ case PM_TOKEN_LESS_EQUAL_GREATER: case PM_TOKEN_LESS_EQUAL: case PM_TOKEN_LESS_LESS: case PM_TOKEN_LESS: \
+ case PM_TOKEN_MINUS: case PM_TOKEN_PERCENT: case PM_TOKEN_PIPE: case PM_TOKEN_PLUS: case PM_TOKEN_SLASH: \
+ case PM_TOKEN_STAR_STAR: case PM_TOKEN_STAR: case PM_TOKEN_TILDE: case PM_TOKEN_UAMPERSAND: case PM_TOKEN_UMINUS: \
+ case PM_TOKEN_UMINUS_NUM: case PM_TOKEN_UPLUS: case PM_TOKEN_USTAR: case PM_TOKEN_USTAR_STAR
+
+/**
+ * This macro allows you to define a case statement for all of the token types
+ * that represent the beginning of nodes that are "primitives" in a pattern
+ * matching expression.
+ */
+#define PM_CASE_PRIMITIVE PM_TOKEN_INTEGER: case PM_TOKEN_INTEGER_IMAGINARY: case PM_TOKEN_INTEGER_RATIONAL: \
+ case PM_TOKEN_INTEGER_RATIONAL_IMAGINARY: case PM_TOKEN_FLOAT: case PM_TOKEN_FLOAT_IMAGINARY: \
+ case PM_TOKEN_FLOAT_RATIONAL: case PM_TOKEN_FLOAT_RATIONAL_IMAGINARY: case PM_TOKEN_SYMBOL_BEGIN: \
+ case PM_TOKEN_REGEXP_BEGIN: case PM_TOKEN_BACKTICK: case PM_TOKEN_PERCENT_LOWER_X: case PM_TOKEN_PERCENT_LOWER_I: \
+ case PM_TOKEN_PERCENT_LOWER_W: case PM_TOKEN_PERCENT_UPPER_I: case PM_TOKEN_PERCENT_UPPER_W: \
+ case PM_TOKEN_STRING_BEGIN: case PM_TOKEN_KEYWORD_NIL: case PM_TOKEN_KEYWORD_SELF: case PM_TOKEN_KEYWORD_TRUE: \
+ case PM_TOKEN_KEYWORD_FALSE: case PM_TOKEN_KEYWORD___FILE__: case PM_TOKEN_KEYWORD___LINE__: \
+ case PM_TOKEN_KEYWORD___ENCODING__: case PM_TOKEN_MINUS_GREATER: case PM_TOKEN_HEREDOC_START: \
+ case PM_TOKEN_UMINUS_NUM: case PM_TOKEN_CHARACTER_LITERAL
+
+/**
+ * This macro allows you to define a case statement for all of the token types
+ * that could begin a parameter.
+ */
+#define PM_CASE_PARAMETER PM_TOKEN_UAMPERSAND: case PM_TOKEN_AMPERSAND: case PM_TOKEN_UDOT_DOT_DOT: \
+ case PM_TOKEN_IDENTIFIER: case PM_TOKEN_LABEL: case PM_TOKEN_USTAR: case PM_TOKEN_STAR: case PM_TOKEN_STAR_STAR: \
+ case PM_TOKEN_USTAR_STAR: case PM_TOKEN_CONSTANT: case PM_TOKEN_INSTANCE_VARIABLE: case PM_TOKEN_GLOBAL_VARIABLE: \
+ case PM_TOKEN_CLASS_VARIABLE
+
+/**
+ * This macro allows you to define a case statement for all of the nodes that
+ * can be transformed into write targets.
+ */
+#define PM_CASE_WRITABLE PM_CLASS_VARIABLE_READ_NODE: case PM_CONSTANT_PATH_NODE: \
+ case PM_CONSTANT_READ_NODE: case PM_GLOBAL_VARIABLE_READ_NODE: case PM_LOCAL_VARIABLE_READ_NODE: \
+ case PM_INSTANCE_VARIABLE_READ_NODE: case PM_MULTI_TARGET_NODE: case PM_BACK_REFERENCE_READ_NODE: \
+ case PM_NUMBERED_REFERENCE_READ_NODE
+
+// Assert here that the flags are the same so that we can safely switch the type
+// of the node without having to move the flags.
+PM_STATIC_ASSERT(__LINE__, ((int) PM_STRING_FLAGS_FORCED_UTF8_ENCODING) == ((int) PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING), "Expected the flags to match.");
+
+/**
+ * If the encoding was explicitly set through the lexing process, then we need
+ * to potentially mark the string's flags to indicate how to encode it.
+ */
+static inline pm_node_flags_t
+parse_unescaped_encoding(const pm_parser_t *parser) {
+ if (parser->explicit_encoding != NULL) {
+ if (parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY) {
+ // If the there's an explicit encoding and it's using a UTF-8 escape
+ // sequence, then mark the string as UTF-8.
+ return PM_STRING_FLAGS_FORCED_UTF8_ENCODING;
+ } else if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
+ // If there's a non-UTF-8 escape sequence being used, then the
+ // string uses the source encoding, unless the source is marked as
+ // US-ASCII. In that case the string is forced as ASCII-8BIT in
+ // order to keep the string valid.
+ return PM_STRING_FLAGS_FORCED_BINARY_ENCODING;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Parse a node that is part of a string. If the subsequent tokens cannot be
+ * parsed as a string part, then NULL is returned.
+ */
+static pm_node_t *
+parse_string_part(pm_parser_t *parser) {
+ switch (parser->current.type) {
+ // Here the lexer has returned to us plain string content. In this case
+ // we'll create a string node that has no opening or closing and return that
+ // as the part. These kinds of parts look like:
+ //
+ // "aaa #{bbb} #@ccc ddd"
+ // ^^^^ ^ ^^^^
+ case PM_TOKEN_STRING_CONTENT: {
+ pm_token_t opening = not_provided(parser);
+ pm_token_t closing = not_provided(parser);
+
+ pm_node_t *node = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &parser->current, &closing);
+ pm_node_flag_set(node, parse_unescaped_encoding(parser));
+
+ parser_lex(parser);
+ return node;
+ }
+ // Here the lexer has returned the beginning of an embedded expression. In
+ // that case we'll parse the inner statements and return that as the part.
+ // These kinds of parts look like:
+ //
+ // "aaa #{bbb} #@ccc ddd"
+ // ^^^^^^
+ case PM_TOKEN_EMBEXPR_BEGIN: {
+ pm_lex_state_t state = parser->lex_state;
+ int brace_nesting = parser->brace_nesting;
+
+ parser->brace_nesting = 0;
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ parser_lex(parser);
+
+ pm_token_t opening = parser->previous;
+ pm_statements_node_t *statements = NULL;
+
+ if (!match1(parser, PM_TOKEN_EMBEXPR_END)) {
+ pm_accepts_block_stack_push(parser, true);
+ statements = parse_statements(parser, PM_CONTEXT_EMBEXPR);
+ pm_accepts_block_stack_pop(parser);
+ }
+
+ parser->brace_nesting = brace_nesting;
+ lex_state_set(parser, state);
+
+ expect1(parser, PM_TOKEN_EMBEXPR_END, PM_ERR_EMBEXPR_END);
+ pm_token_t closing = parser->previous;
+
+ return (pm_node_t *) pm_embedded_statements_node_create(parser, &opening, statements, &closing);
+ }
+
+ // Here the lexer has returned the beginning of an embedded variable.
+ // In that case we'll parse the variable and create an appropriate node
+ // for it and then return that node. These kinds of parts look like:
+ //
+ // "aaa #{bbb} #@ccc ddd"
+ // ^^^^^
+ case PM_TOKEN_EMBVAR: {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ parser_lex(parser);
+
+ pm_token_t operator = parser->previous;
+ pm_node_t *variable;
+
+ switch (parser->current.type) {
+ // In this case a back reference is being interpolated. We'll
+ // create a global variable read node.
+ case PM_TOKEN_BACK_REFERENCE:
+ parser_lex(parser);
+ variable = (pm_node_t *) pm_back_reference_read_node_create(parser, &parser->previous);
+ break;
+ // In this case an nth reference is being interpolated. We'll
+ // create a global variable read node.
+ case PM_TOKEN_NUMBERED_REFERENCE:
+ parser_lex(parser);
+ variable = (pm_node_t *) pm_numbered_reference_read_node_create(parser, &parser->previous);
+ break;
+ // In this case a global variable is being interpolated. We'll
+ // create a global variable read node.
+ case PM_TOKEN_GLOBAL_VARIABLE:
+ parser_lex(parser);
+ variable = (pm_node_t *) pm_global_variable_read_node_create(parser, &parser->previous);
+ break;
+ // In this case an instance variable is being interpolated.
+ // We'll create an instance variable read node.
+ case PM_TOKEN_INSTANCE_VARIABLE:
+ parser_lex(parser);
+ variable = (pm_node_t *) pm_instance_variable_read_node_create(parser, &parser->previous);
+ break;
+ // In this case a class variable is being interpolated. We'll
+ // create a class variable read node.
+ case PM_TOKEN_CLASS_VARIABLE:
+ parser_lex(parser);
+ variable = (pm_node_t *) pm_class_variable_read_node_create(parser, &parser->previous);
+ break;
+ // We can hit here if we got an invalid token. In that case
+ // we'll not attempt to lex this token and instead just return a
+ // missing node.
+ default:
+ expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_EMBVAR_INVALID);
+ variable = (pm_node_t *) pm_missing_node_create(parser, parser->current.start, parser->current.end);
+ break;
+ }
+
+ return (pm_node_t *) pm_embedded_variable_node_create(parser, &operator, variable);
+ }
+ default:
+ parser_lex(parser);
+ pm_parser_err_previous(parser, PM_ERR_CANNOT_PARSE_STRING_PART);
+ return NULL;
+ }
+}
+
+/**
+ * When creating a symbol, unary operators that cannot be binary operators
+ * automatically drop trailing `@` characters. This happens at the parser level,
+ * such that `~@` is parsed as `~` and `!@` is parsed as `!`. We do that here.
+ */
+static const uint8_t *
+parse_operator_symbol_name(const pm_token_t *name) {
+ switch (name->type) {
+ case PM_TOKEN_TILDE:
+ case PM_TOKEN_BANG:
+ if (name->end[-1] == '@') return name->end - 1;
+ /* fallthrough */
+ default:
+ return name->end;
+ }
+}
+
+static pm_node_t *
+parse_operator_symbol(pm_parser_t *parser, const pm_token_t *opening, pm_lex_state_t next_state) {
+ pm_token_t closing = not_provided(parser);
+ pm_symbol_node_t *symbol = pm_symbol_node_create(parser, opening, &parser->current, &closing);
+
+ const uint8_t *end = parse_operator_symbol_name(&parser->current);
+
+ if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
+ parser_lex(parser);
+
+ pm_string_shared_init(&symbol->unescaped, parser->previous.start, end);
+ pm_node_flag_set((pm_node_t *) symbol, PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING);
+
+ return (pm_node_t *) symbol;
+}
+
+/**
+ * Parse a symbol node. This function will get called immediately after finding
+ * a symbol opening token. This handles parsing bare symbols and interpolated
+ * symbols.
+ */
+static pm_node_t *
+parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_state) {
+ const pm_token_t opening = parser->previous;
+
+ if (lex_mode->mode != PM_LEX_STRING) {
+ if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
+
+ switch (parser->current.type) {
+ case PM_CASE_OPERATOR:
+ return parse_operator_symbol(parser, &opening, next_state == PM_LEX_STATE_NONE ? PM_LEX_STATE_ENDFN : next_state);
+ case PM_TOKEN_IDENTIFIER:
+ case PM_TOKEN_CONSTANT:
+ case PM_TOKEN_INSTANCE_VARIABLE:
+ case PM_TOKEN_METHOD_NAME:
+ case PM_TOKEN_CLASS_VARIABLE:
+ case PM_TOKEN_GLOBAL_VARIABLE:
+ case PM_TOKEN_NUMBERED_REFERENCE:
+ case PM_TOKEN_BACK_REFERENCE:
+ case PM_CASE_KEYWORD:
+ parser_lex(parser);
+ break;
+ default:
+ expect2(parser, PM_TOKEN_IDENTIFIER, PM_TOKEN_METHOD_NAME, PM_ERR_SYMBOL_INVALID);
+ break;
+ }
+
+ pm_token_t closing = not_provided(parser);
+ pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &parser->previous, &closing);
+
+ pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end);
+ pm_node_flag_set((pm_node_t *) symbol, parse_symbol_encoding(parser, &symbol->unescaped));
+
+ return (pm_node_t *) symbol;
+ }
+
+ if (lex_mode->as.string.interpolation) {
+ // If we have the end of the symbol, then we can return an empty symbol.
+ if (match1(parser, PM_TOKEN_STRING_END)) {
+ if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
+ parser_lex(parser);
+
+ pm_token_t content = not_provided(parser);
+ pm_token_t closing = parser->previous;
+ return (pm_node_t *) pm_symbol_node_create(parser, &opening, &content, &closing);
+ }
+
+ // Now we can parse the first part of the symbol.
+ pm_node_t *part = parse_string_part(parser);
+
+ // If we got a string part, then it's possible that we could transform
+ // what looks like an interpolated symbol into a regular symbol.
+ if (part && PM_NODE_TYPE_P(part, PM_STRING_NODE) && match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
+ if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
+ expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_INTERPOLATED);
+
+ return (pm_node_t *) pm_string_node_to_symbol_node(parser, (pm_string_node_t *) part, &opening, &parser->previous);
+ }
+
+ pm_interpolated_symbol_node_t *symbol = pm_interpolated_symbol_node_create(parser, &opening, NULL, &opening);
+ if (part) pm_interpolated_symbol_node_append(symbol, part);
+
+ while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
+ if ((part = parse_string_part(parser)) != NULL) {
+ pm_interpolated_symbol_node_append(symbol, part);
+ }
+ }
+
+ if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
+ if (match1(parser, PM_TOKEN_EOF)) {
+ pm_parser_err_token(parser, &opening, PM_ERR_SYMBOL_TERM_INTERPOLATED);
+ } else {
+ expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_INTERPOLATED);
+ }
+
+ pm_interpolated_symbol_node_closing_loc_set(symbol, &parser->previous);
+ return (pm_node_t *) symbol;
+ }
+
+ pm_token_t content;
+ pm_string_t unescaped;
+
+ if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
+ content = parser->current;
+ unescaped = parser->current_string;
+ parser_lex(parser);
+
+ // If we have two string contents in a row, then the content of this
+ // symbol is split because of heredoc contents. This looks like:
+ //
+ // <<A; :'a
+ // A
+ // b'
+ //
+ // In this case, the best way we have to represent this is as an
+ // interpolated string node, so that's what we'll do here.
+ if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
+ pm_interpolated_symbol_node_t *symbol = pm_interpolated_symbol_node_create(parser, &opening, NULL, &opening);
+ pm_token_t bounds = not_provided(parser);
+
+ pm_node_t *part = (pm_node_t *) pm_string_node_create_unescaped(parser, &bounds, &content, &bounds, &unescaped);
+ pm_interpolated_symbol_node_append(symbol, part);
+
+ part = (pm_node_t *) pm_string_node_create_unescaped(parser, &bounds, &parser->current, &bounds, &parser->current_string);
+ pm_interpolated_symbol_node_append(symbol, part);
+
+ if (next_state != PM_LEX_STATE_NONE) {
+ lex_state_set(parser, next_state);
+ }
+
+ parser_lex(parser);
+ expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_DYNAMIC);
+
+ pm_interpolated_symbol_node_closing_loc_set(symbol, &parser->previous);
+ return (pm_node_t *) symbol;
+ }
+ } else {
+ content = (pm_token_t) { .type = PM_TOKEN_STRING_CONTENT, .start = parser->previous.end, .end = parser->previous.end };
+ pm_string_shared_init(&unescaped, content.start, content.end);
+ }
+
+ if (next_state != PM_LEX_STATE_NONE) {
+ lex_state_set(parser, next_state);
+ }
+
+ if (match1(parser, PM_TOKEN_EOF)) {
+ pm_parser_err_token(parser, &opening, PM_ERR_SYMBOL_TERM_DYNAMIC);
+ } else {
+ expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_DYNAMIC);
+ }
+
+ return (pm_node_t *) pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &unescaped));
+}
+
+/**
+ * Parse an argument to undef which can either be a bare word, a symbol, a
+ * constant, or an interpolated symbol.
+ */
+static inline pm_node_t *
+parse_undef_argument(pm_parser_t *parser) {
+ switch (parser->current.type) {
+ case PM_CASE_OPERATOR: {
+ const pm_token_t opening = not_provided(parser);
+ return parse_operator_symbol(parser, &opening, PM_LEX_STATE_NONE);
+ }
+ case PM_CASE_KEYWORD:
+ case PM_TOKEN_CONSTANT:
+ case PM_TOKEN_IDENTIFIER:
+ case PM_TOKEN_METHOD_NAME: {
+ parser_lex(parser);
+
+ pm_token_t opening = not_provided(parser);
+ pm_token_t closing = not_provided(parser);
+ pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &parser->previous, &closing);
+
+ pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end);
+ pm_node_flag_set((pm_node_t *) symbol, parse_symbol_encoding(parser, &symbol->unescaped));
+
+ return (pm_node_t *) symbol;
+ }
+ case PM_TOKEN_SYMBOL_BEGIN: {
+ pm_lex_mode_t lex_mode = *parser->lex_modes.current;
+ parser_lex(parser);
+
+ return parse_symbol(parser, &lex_mode, PM_LEX_STATE_NONE);
+ }
+ default:
+ pm_parser_err_current(parser, PM_ERR_UNDEF_ARGUMENT);
+ return (pm_node_t *) pm_missing_node_create(parser, parser->current.start, parser->current.end);
+ }
+}
+
+/**
+ * Parse an argument to alias which can either be a bare word, a symbol, an
+ * interpolated symbol or a global variable. If this is the first argument, then
+ * we need to set the lex state to PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM
+ * between the first and second arguments.
+ */
+static inline pm_node_t *
+parse_alias_argument(pm_parser_t *parser, bool first) {
+ switch (parser->current.type) {
+ case PM_CASE_OPERATOR: {
+ const pm_token_t opening = not_provided(parser);
+ return parse_operator_symbol(parser, &opening, first ? PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM : PM_LEX_STATE_NONE);
+ }
+ case PM_CASE_KEYWORD:
+ case PM_TOKEN_CONSTANT:
+ case PM_TOKEN_IDENTIFIER:
+ case PM_TOKEN_METHOD_NAME: {
+ if (first) lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM);
+ parser_lex(parser);
+
+ pm_token_t opening = not_provided(parser);
+ pm_token_t closing = not_provided(parser);
+ pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &parser->previous, &closing);
+
+ pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end);
+ pm_node_flag_set((pm_node_t *) symbol, parse_symbol_encoding(parser, &symbol->unescaped));
+
+ return (pm_node_t *) symbol;
+ }
+ case PM_TOKEN_SYMBOL_BEGIN: {
+ pm_lex_mode_t lex_mode = *parser->lex_modes.current;
+ parser_lex(parser);
+
+ return parse_symbol(parser, &lex_mode, first ? PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM : PM_LEX_STATE_NONE);
+ }
+ case PM_TOKEN_BACK_REFERENCE:
+ parser_lex(parser);
+ return (pm_node_t *) pm_back_reference_read_node_create(parser, &parser->previous);
+ case PM_TOKEN_NUMBERED_REFERENCE:
+ parser_lex(parser);
+ return (pm_node_t *) pm_numbered_reference_read_node_create(parser, &parser->previous);
+ case PM_TOKEN_GLOBAL_VARIABLE:
+ parser_lex(parser);
+ return (pm_node_t *) pm_global_variable_read_node_create(parser, &parser->previous);
+ default:
+ pm_parser_err_current(parser, PM_ERR_ALIAS_ARGUMENT);
+ return (pm_node_t *) pm_missing_node_create(parser, parser->current.start, parser->current.end);
+ }
+}
+
+/**
+ * Return true if any of the visible scopes to the current context are using
+ * numbered parameters.
+ */
+static bool
+outer_scope_using_numbered_parameters_p(pm_parser_t *parser) {
+ for (pm_scope_t *scope = parser->current_scope->previous; scope != NULL && !scope->closed; scope = scope->previous) {
+ if (scope->numbered_parameters > 0) return true;
+ }
+
+ return false;
+}
+
+/**
+ * These are the names of the various numbered parameters. We have them here so
+ * that when we insert them into the constant pool we can use a constant string
+ * and not have to allocate.
+ */
+static const char * const pm_numbered_parameter_names[] = {
+ "_1", "_2", "_3", "_4", "_5", "_6", "_7", "_8", "_9"
+};
+
+/**
+ * Parse an identifier into either a local variable read. If the local variable
+ * is not found, it returns NULL instead.
+ */
+static pm_local_variable_read_node_t *
+parse_variable(pm_parser_t *parser) {
+ int depth;
+ if ((depth = pm_parser_local_depth(parser, &parser->previous)) != -1) {
+ return pm_local_variable_read_node_create(parser, &parser->previous, (uint32_t) depth);
+ }
+
+ pm_scope_t *current_scope = parser->current_scope;
+ if (!current_scope->closed && current_scope->numbered_parameters != PM_SCOPE_NUMBERED_PARAMETERS_DISALLOWED && pm_token_is_numbered_parameter(parser->previous.start, parser->previous.end)) {
+ // Now that we know we have a numbered parameter, we need to check
+ // if it's allowed in this context. If it is, then we will create a
+ // local variable read. If it's not, then we'll create a normal call
+ // node but add an error.
+ if (current_scope->parameters & PM_SCOPE_PARAMETERS_ORDINARY) {
+ pm_parser_err_previous(parser, PM_ERR_NUMBERED_PARAMETER_ORDINARY);
+ } else if (current_scope->parameters & PM_SCOPE_PARAMETERS_IT) {
+ pm_parser_err_previous(parser, PM_ERR_NUMBERED_PARAMETER_IT);
+ } else if (outer_scope_using_numbered_parameters_p(parser)) {
+ pm_parser_err_previous(parser, PM_ERR_NUMBERED_PARAMETER_OUTER_SCOPE);
+ } else {
+ // Indicate that this scope is using numbered params so that child
+ // scopes cannot. We subtract the value for the character '0' to get
+ // the actual integer value of the number (only _1 through _9 are
+ // valid).
+ int8_t numbered_parameters = (int8_t) (parser->previous.start[1] - '0');
+ current_scope->parameters |= PM_SCOPE_PARAMETERS_NUMBERED;
+
+ if (numbered_parameters > current_scope->numbered_parameters) {
+ current_scope->numbered_parameters = numbered_parameters;
+ }
+
+ // When you use a numbered parameter, it implies the existence
+ // of all of the locals that exist before it. For example,
+ // referencing _2 means that _1 must exist. Therefore here we
+ // loop through all of the possibilities and add them into the
+ // constant pool.
+ for (int8_t numbered_param = 1; numbered_param <= numbered_parameters - 1; numbered_param++) {
+ pm_parser_local_add_constant(parser, pm_numbered_parameter_names[numbered_param - 1], 2);
+ }
+
+ // Finally we can create the local variable read node.
+ pm_constant_id_t name_id = pm_parser_local_add_constant(parser, pm_numbered_parameter_names[numbered_parameters - 1], 2);
+ return pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, 0, false);
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Parse an identifier into either a local variable read or a call.
+ */
+static pm_node_t *
+parse_variable_call(pm_parser_t *parser) {
+ pm_node_flags_t flags = 0;
+
+ if (!match1(parser, PM_TOKEN_PARENTHESIS_LEFT) && (parser->previous.end[-1] != '!') && (parser->previous.end[-1] != '?')) {
+ pm_local_variable_read_node_t *node = parse_variable(parser);
+ if (node != NULL) return (pm_node_t *) node;
+ flags |= PM_CALL_NODE_FLAGS_VARIABLE_CALL;
+ }
+
+ pm_call_node_t *node = pm_call_node_variable_call_create(parser, &parser->previous);
+ pm_node_flag_set((pm_node_t *)node, flags);
+
+ return (pm_node_t *) node;
+}
+
+/**
+ * Parse the method definition name based on the current token available on the
+ * parser. If it does not match a valid method definition name, then a missing
+ * token is returned.
+ */
+static inline pm_token_t
+parse_method_definition_name(pm_parser_t *parser) {
+ switch (parser->current.type) {
+ case PM_CASE_KEYWORD:
+ case PM_TOKEN_CONSTANT:
+ case PM_TOKEN_METHOD_NAME:
+ parser_lex(parser);
+ return parser->previous;
+ case PM_TOKEN_IDENTIFIER:
+ pm_refute_numbered_parameter(parser, parser->current.start, parser->current.end);
+ parser_lex(parser);
+ return parser->previous;
+ case PM_CASE_OPERATOR:
+ lex_state_set(parser, PM_LEX_STATE_ENDFN);
+ parser_lex(parser);
+ return parser->previous;
+ default:
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_DEF_NAME, pm_token_type_human(parser->current.type));
+ return (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->current.start, .end = parser->current.end };
+ }
+}
+
+static void
+parse_heredoc_dedent_string(pm_string_t *string, size_t common_whitespace) {
+ // Get a reference to the string struct that is being held by the string
+ // node. This is the value we're going to actually manipulate.
+ pm_string_ensure_owned(string);
+
+ // Now get the bounds of the existing string. We'll use this as a
+ // destination to move bytes into. We'll also use it for bounds checking
+ // since we don't require that these strings be null terminated.
+ size_t dest_length = pm_string_length(string);
+ const uint8_t *source_cursor = (uint8_t *) string->source;
+ const uint8_t *source_end = source_cursor + dest_length;
+
+ // We're going to move bytes backward in the string when we get leading
+ // whitespace, so we'll maintain a pointer to the current position in the
+ // string that we're writing to.
+ size_t trimmed_whitespace = 0;
+
+ // While we haven't reached the amount of common whitespace that we need to
+ // trim and we haven't reached the end of the string, we'll keep trimming
+ // whitespace. Trimming in this context means skipping over these bytes such
+ // that they aren't copied into the new string.
+ while ((source_cursor < source_end) && pm_char_is_inline_whitespace(*source_cursor) && trimmed_whitespace < common_whitespace) {
+ if (*source_cursor == '\t') {
+ trimmed_whitespace = (trimmed_whitespace / PM_TAB_WHITESPACE_SIZE + 1) * PM_TAB_WHITESPACE_SIZE;
+ if (trimmed_whitespace > common_whitespace) break;
+ } else {
+ trimmed_whitespace++;
+ }
+
+ source_cursor++;
+ dest_length--;
+ }
+
+ memmove((uint8_t *) string->source, source_cursor, (size_t) (source_end - source_cursor));
+ string->length = dest_length;
+}
+
+/**
+ * Take a heredoc node that is indented by a ~ and trim the leading whitespace.
+ */
+static void
+parse_heredoc_dedent(pm_parser_t *parser, pm_node_list_t *nodes, size_t common_whitespace) {
+ // The next node should be dedented if it's the first node in the list or if
+ // it follows a string node.
+ bool dedent_next = true;
+
+ // Iterate over all nodes, and trim whitespace accordingly. We're going to
+ // keep around two indices: a read and a write. If we end up trimming all of
+ // the whitespace from a node, then we'll drop it from the list entirely.
+ size_t write_index = 0;
+
+ pm_node_t *node;
+ PM_NODE_LIST_FOREACH(nodes, read_index, node) {
+ // We're not manipulating child nodes that aren't strings. In this case
+ // we'll skip past it and indicate that the subsequent node should not
+ // be dedented.
+ if (!PM_NODE_TYPE_P(node, PM_STRING_NODE)) {
+ nodes->nodes[write_index++] = node;
+ dedent_next = false;
+ continue;
+ }
+
+ pm_string_node_t *string_node = ((pm_string_node_t *) node);
+ if (dedent_next) {
+ parse_heredoc_dedent_string(&string_node->unescaped, common_whitespace);
+ }
+
+ if (string_node->unescaped.length == 0) {
+ pm_node_destroy(parser, node);
+ } else {
+ nodes->nodes[write_index++] = node;
+ }
+
+ // We always dedent the next node if it follows a string node.
+ dedent_next = true;
+ }
+
+ nodes->size = write_index;
+}
+
+static pm_node_t *
+parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, bool top_pattern, pm_diagnostic_id_t diag_id);
+
+/**
+ * Add the newly created local to the list of captures for this pattern matching
+ * expression. If it is duplicated from a previous local, then we'll need to add
+ * an error to the parser.
+ */
+static void
+parse_pattern_capture(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_constant_id_t capture, const pm_location_t *location) {
+ // Skip this capture if it starts with an underscore.
+ if (*location->start == '_') return;
+
+ if (pm_constant_id_list_includes(captures, capture)) {
+ pm_parser_err(parser, location->start, location->end, PM_ERR_PATTERN_CAPTURE_DUPLICATE);
+ } else {
+ pm_constant_id_list_append(captures, capture);
+ }
+}
+
+/**
+ * Accept any number of constants joined by :: delimiters.
+ */
+static pm_node_t *
+parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node_t *node) {
+ // Now, if there are any :: operators that follow, parse them as constant
+ // path nodes.
+ while (accept1(parser, PM_TOKEN_COLON_COLON)) {
+ pm_token_t delimiter = parser->previous;
+ expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
+
+ pm_node_t *child = (pm_node_t *) pm_constant_read_node_create(parser, &parser->previous);
+ node = (pm_node_t *) pm_constant_path_node_create(parser, node, &delimiter, child);
+ }
+
+ // If there is a [ or ( that follows, then this is part of a larger pattern
+ // expression. We'll parse the inner pattern here, then modify the returned
+ // inner pattern with our constant path attached.
+ if (!match2(parser, PM_TOKEN_BRACKET_LEFT, PM_TOKEN_PARENTHESIS_LEFT)) {
+ return node;
+ }
+
+ pm_token_t opening;
+ pm_token_t closing;
+ pm_node_t *inner = NULL;
+
+ if (accept1(parser, PM_TOKEN_BRACKET_LEFT)) {
+ opening = parser->previous;
+ accept1(parser, PM_TOKEN_NEWLINE);
+
+ if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
+ inner = parse_pattern(parser, captures, true, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET);
+ accept1(parser, PM_TOKEN_NEWLINE);
+ expect1(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET);
+ }
+
+ closing = parser->previous;
+ } else {
+ parser_lex(parser);
+ opening = parser->previous;
+ accept1(parser, PM_TOKEN_NEWLINE);
+
+ if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
+ inner = parse_pattern(parser, captures, true, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN);
+ accept1(parser, PM_TOKEN_NEWLINE);
+ expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN);
+ }
+
+ closing = parser->previous;
+ }
+
+ if (!inner) {
+ // If there was no inner pattern, then we have something like Foo() or
+ // Foo[]. In that case we'll create an array pattern with no requireds.
+ return (pm_node_t *) pm_array_pattern_node_constant_create(parser, node, &opening, &closing);
+ }
+
+ // Now that we have the inner pattern, check to see if it's an array, find,
+ // or hash pattern. If it is, then we'll attach our constant path to it if
+ // it doesn't already have a constant. If it's not one of those node types
+ // or it does have a constant, then we'll create an array pattern.
+ switch (PM_NODE_TYPE(inner)) {
+ case PM_ARRAY_PATTERN_NODE: {
+ pm_array_pattern_node_t *pattern_node = (pm_array_pattern_node_t *) inner;
+
+ if (pattern_node->constant == NULL && pattern_node->opening_loc.start == NULL) {
+ pattern_node->base.location.start = node->location.start;
+ pattern_node->base.location.end = closing.end;
+
+ pattern_node->constant = node;
+ pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening);
+ pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing);
+
+ return (pm_node_t *) pattern_node;
+ }
+
+ break;
+ }
+ case PM_FIND_PATTERN_NODE: {
+ pm_find_pattern_node_t *pattern_node = (pm_find_pattern_node_t *) inner;
+
+ if (pattern_node->constant == NULL && pattern_node->opening_loc.start == NULL) {
+ pattern_node->base.location.start = node->location.start;
+ pattern_node->base.location.end = closing.end;
+
+ pattern_node->constant = node;
+ pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening);
+ pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing);
+
+ return (pm_node_t *) pattern_node;
+ }
+
+ break;
+ }
+ case PM_HASH_PATTERN_NODE: {
+ pm_hash_pattern_node_t *pattern_node = (pm_hash_pattern_node_t *) inner;
+
+ if (pattern_node->constant == NULL && pattern_node->opening_loc.start == NULL) {
+ pattern_node->base.location.start = node->location.start;
+ pattern_node->base.location.end = closing.end;
+
+ pattern_node->constant = node;
+ pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening);
+ pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing);
+
+ return (pm_node_t *) pattern_node;
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ // If we got here, then we didn't return one of the inner patterns by
+ // attaching its constant. In this case we'll create an array pattern and
+ // attach our constant to it.
+ pm_array_pattern_node_t *pattern_node = pm_array_pattern_node_constant_create(parser, node, &opening, &closing);
+ pm_array_pattern_node_requireds_append(pattern_node, inner);
+ return (pm_node_t *) pattern_node;
+}
+
+/**
+ * Parse a rest pattern.
+ */
+static pm_splat_node_t *
+parse_pattern_rest(pm_parser_t *parser, pm_constant_id_list_t *captures) {
+ assert(parser->previous.type == PM_TOKEN_USTAR);
+ pm_token_t operator = parser->previous;
+ pm_node_t *name = NULL;
+
+ // Rest patterns don't necessarily have a name associated with them. So we
+ // will check for that here. If they do, then we'll add it to the local
+ // table since this pattern will cause it to become a local variable.
+ if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
+ pm_token_t identifier = parser->previous;
+ pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &identifier);
+
+ int depth;
+ if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
+ pm_parser_local_add(parser, constant_id, identifier.start, identifier.end, 0);
+ }
+
+ parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&identifier));
+ name = (pm_node_t *) pm_local_variable_target_node_create(
+ parser,
+ &PM_LOCATION_TOKEN_VALUE(&identifier),
+ constant_id,
+ (uint32_t) (depth == -1 ? 0 : depth)
+ );
+ }
+
+ // Finally we can return the created node.
+ return pm_splat_node_create(parser, &operator, name);
+}
+
+/**
+ * Parse a keyword rest node.
+ */
+static pm_node_t *
+parse_pattern_keyword_rest(pm_parser_t *parser, pm_constant_id_list_t *captures) {
+ assert(parser->current.type == PM_TOKEN_USTAR_STAR);
+ parser_lex(parser);
+
+ pm_token_t operator = parser->previous;
+ pm_node_t *value = NULL;
+
+ if (accept1(parser, PM_TOKEN_KEYWORD_NIL)) {
+ return (pm_node_t *) pm_no_keywords_parameter_node_create(parser, &operator, &parser->previous);
+ }
+
+ if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
+ pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
+
+ int depth;
+ if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
+ pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
+ }
+
+ parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous));
+ value = (pm_node_t *) pm_local_variable_target_node_create(
+ parser,
+ &PM_LOCATION_TOKEN_VALUE(&parser->previous),
+ constant_id,
+ (uint32_t) (depth == -1 ? 0 : depth)
+ );
+ }
+
+ return (pm_node_t *) pm_assoc_splat_node_create(parser, value, &operator);
+}
+
+/**
+ * Create an implicit node for the value of a hash pattern that has omitted the
+ * value. This will use an implicit local variable target.
+ */
+static pm_node_t *
+parse_pattern_hash_implicit_value(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_symbol_node_t *key) {
+ const pm_location_t *value_loc = &((pm_symbol_node_t *) key)->value_loc;
+ pm_constant_id_t constant_id = pm_parser_constant_id_location(parser, value_loc->start, value_loc->end);
+
+ int depth = -1;
+ if (value_loc->end[-1] == '!' || value_loc->end[-1] == '?') {
+ pm_parser_err(parser, key->base.location.start, key->base.location.end, PM_ERR_PATTERN_HASH_KEY_LOCALS);
+ PM_PARSER_ERR_LOCATION_FORMAT(parser, value_loc, PM_ERR_INVALID_LOCAL_VARIABLE_WRITE, (int) (value_loc->end - value_loc->start), (const char *) value_loc->start);
+ } else {
+ depth = pm_parser_local_depth_constant_id(parser, constant_id);
+ }
+
+ if (depth == -1) {
+ pm_parser_local_add(parser, constant_id, value_loc->start, value_loc->end, 0);
+ }
+
+ parse_pattern_capture(parser, captures, constant_id, value_loc);
+ pm_local_variable_target_node_t *target = pm_local_variable_target_node_create(
+ parser,
+ value_loc,
+ constant_id,
+ (uint32_t) (depth == -1 ? 0 : depth)
+ );
+
+ return (pm_node_t *) pm_implicit_node_create(parser, (pm_node_t *) target);
+}
+
+/**
+ * Add a node to the list of keys for a hash pattern, and if it is a duplicate
+ * then add an error to the parser.
+ */
+static void
+parse_pattern_hash_key(pm_parser_t *parser, pm_static_literals_t *keys, pm_node_t *node) {
+ if (pm_static_literals_add(&parser->newline_list, parser->start_line, keys, node) != NULL) {
+ pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_KEY_DUPLICATE);
+ }
+}
+
+/**
+ * Parse a hash pattern.
+ */
+static pm_hash_pattern_node_t *
+parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node_t *first_node) {
+ pm_node_list_t assocs = { 0 };
+ pm_static_literals_t keys = { 0 };
+ pm_node_t *rest = NULL;
+
+ switch (PM_NODE_TYPE(first_node)) {
+ case PM_ASSOC_SPLAT_NODE:
+ case PM_NO_KEYWORDS_PARAMETER_NODE:
+ rest = first_node;
+ break;
+ case PM_SYMBOL_NODE: {
+ if (pm_symbol_node_label_p(first_node)) {
+ parse_pattern_hash_key(parser, &keys, first_node);
+ pm_node_t *value;
+
+ if (match7(parser, PM_TOKEN_COMMA, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
+ // Otherwise, we will create an implicit local variable
+ // target for the value.
+ value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) first_node);
+ } else {
+ // Here we have a value for the first assoc in the list, so
+ // we will parse it now.
+ value = parse_pattern(parser, captures, false, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY);
+ }
+
+ pm_token_t operator = not_provided(parser);
+ pm_node_t *assoc = (pm_node_t *) pm_assoc_node_create(parser, first_node, &operator, value);
+
+ pm_node_list_append(&assocs, assoc);
+ break;
+ }
+ }
+ /* fallthrough */
+ default: {
+ // If we get anything else, then this is an error. For this we'll
+ // create a missing node for the value and create an assoc node for
+ // the first node in the list.
+ pm_parser_err_node(parser, first_node, PM_ERR_PATTERN_HASH_KEY_LABEL);
+
+ pm_token_t operator = not_provided(parser);
+ pm_node_t *value = (pm_node_t *) pm_missing_node_create(parser, first_node->location.start, first_node->location.end);
+ pm_node_t *assoc = (pm_node_t *) pm_assoc_node_create(parser, first_node, &operator, value);
+
+ pm_node_list_append(&assocs, assoc);
+ break;
+ }
+ }
+
+ // If there are any other assocs, then we'll parse them now.
+ while (accept1(parser, PM_TOKEN_COMMA)) {
+ // Here we need to break to support trailing commas.
+ if (match6(parser, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
+ break;
+ }
+
+ if (match1(parser, PM_TOKEN_USTAR_STAR)) {
+ pm_node_t *assoc = parse_pattern_keyword_rest(parser, captures);
+
+ if (rest == NULL) {
+ rest = assoc;
+ } else {
+ pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
+ pm_node_list_append(&assocs, assoc);
+ }
+ } else {
+ expect1(parser, PM_TOKEN_LABEL, PM_ERR_PATTERN_LABEL_AFTER_COMMA);
+ pm_node_t *key = (pm_node_t *) pm_symbol_node_label_create(parser, &parser->previous);
+
+ parse_pattern_hash_key(parser, &keys, key);
+ pm_node_t *value = NULL;
+
+ if (match7(parser, PM_TOKEN_COMMA, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
+ value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) key);
+ } else {
+ value = parse_pattern(parser, captures, false, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY);
+ }
+
+ pm_token_t operator = not_provided(parser);
+ pm_node_t *assoc = (pm_node_t *) pm_assoc_node_create(parser, key, &operator, value);
+
+ if (rest != NULL) {
+ pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
+ }
+
+ pm_node_list_append(&assocs, assoc);
+ }
+ }
+
+ pm_hash_pattern_node_t *node = pm_hash_pattern_node_node_list_create(parser, &assocs, rest);
+ xfree(assocs.nodes);
+
+ pm_static_literals_free(&keys);
+ return node;
+}
+
+/**
+ * Parse a pattern expression primitive.
+ */
+static pm_node_t *
+parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_diagnostic_id_t diag_id) {
+ switch (parser->current.type) {
+ case PM_TOKEN_IDENTIFIER:
+ case PM_TOKEN_METHOD_NAME: {
+ parser_lex(parser);
+ pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
+
+ int depth;
+ if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
+ pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
+ }
+
+ parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous));
+ return (pm_node_t *) pm_local_variable_target_node_create(
+ parser,
+ &PM_LOCATION_TOKEN_VALUE(&parser->previous),
+ constant_id,
+ (uint32_t) (depth == -1 ? 0 : depth)
+ );
+ }
+ case PM_TOKEN_BRACKET_LEFT_ARRAY: {
+ pm_token_t opening = parser->current;
+ parser_lex(parser);
+
+ if (accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
+ // If we have an empty array pattern, then we'll just return a new
+ // array pattern node.
+ return (pm_node_t *) pm_array_pattern_node_empty_create(parser, &opening, &parser->previous);
+ }
+
+ // Otherwise, we'll parse the inner pattern, then deal with it depending
+ // on the type it returns.
+ pm_node_t *inner = parse_pattern(parser, captures, true, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET);
+
+ accept1(parser, PM_TOKEN_NEWLINE);
+ expect1(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET);
+ pm_token_t closing = parser->previous;
+
+ switch (PM_NODE_TYPE(inner)) {
+ case PM_ARRAY_PATTERN_NODE: {
+ pm_array_pattern_node_t *pattern_node = (pm_array_pattern_node_t *) inner;
+ if (pattern_node->opening_loc.start == NULL) {
+ pattern_node->base.location.start = opening.start;
+ pattern_node->base.location.end = closing.end;
+
+ pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening);
+ pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing);
+
+ return (pm_node_t *) pattern_node;
+ }
+
+ break;
+ }
+ case PM_FIND_PATTERN_NODE: {
+ pm_find_pattern_node_t *pattern_node = (pm_find_pattern_node_t *) inner;
+ if (pattern_node->opening_loc.start == NULL) {
+ pattern_node->base.location.start = opening.start;
+ pattern_node->base.location.end = closing.end;
+
+ pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening);
+ pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing);
+
+ return (pm_node_t *) pattern_node;
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ pm_array_pattern_node_t *node = pm_array_pattern_node_empty_create(parser, &opening, &closing);
+ pm_array_pattern_node_requireds_append(node, inner);
+ return (pm_node_t *) node;
+ }
+ case PM_TOKEN_BRACE_LEFT: {
+ bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
+ parser->pattern_matching_newlines = false;
+
+ pm_hash_pattern_node_t *node;
+ pm_token_t opening = parser->current;
+ parser_lex(parser);
+
+ if (accept1(parser, PM_TOKEN_BRACE_RIGHT)) {
+ // If we have an empty hash pattern, then we'll just return a new hash
+ // pattern node.
+ node = pm_hash_pattern_node_empty_create(parser, &opening, &parser->previous);
+ } else {
+ pm_node_t *first_node;
+
+ switch (parser->current.type) {
+ case PM_TOKEN_LABEL:
+ parser_lex(parser);
+ first_node = (pm_node_t *) pm_symbol_node_label_create(parser, &parser->previous);
+ break;
+ case PM_TOKEN_USTAR_STAR:
+ first_node = parse_pattern_keyword_rest(parser, captures);
+ break;
+ case PM_TOKEN_STRING_BEGIN:
+ first_node = parse_expression(parser, PM_BINDING_POWER_MAX, false, PM_ERR_PATTERN_HASH_KEY);
+ break;
+ default: {
+ parser_lex(parser);
+ pm_parser_err_previous(parser, PM_ERR_PATTERN_HASH_KEY);
+
+ first_node = (pm_node_t *) pm_missing_node_create(parser, parser->previous.start, parser->previous.end);
+ break;
+ }
+ }
+
+ node = parse_pattern_hash(parser, captures, first_node);
+
+ accept1(parser, PM_TOKEN_NEWLINE);
+ expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_PATTERN_TERM_BRACE);
+ pm_token_t closing = parser->previous;
+
+ node->base.location.start = opening.start;
+ node->base.location.end = closing.end;
+
+ node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening);
+ node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing);
+ }
+
+ parser->pattern_matching_newlines = previous_pattern_matching_newlines;
+ return (pm_node_t *) node;
+ }
+ case PM_TOKEN_UDOT_DOT:
+ case PM_TOKEN_UDOT_DOT_DOT: {
+ pm_token_t operator = parser->current;
+ parser_lex(parser);
+
+ // Since we have a unary range operator, we need to parse the subsequent
+ // expression as the right side of the range.
+ switch (parser->current.type) {
+ case PM_CASE_PRIMITIVE: {
+ pm_node_t *right = parse_expression(parser, PM_BINDING_POWER_MAX, false, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE);
+ return (pm_node_t *) pm_range_node_create(parser, NULL, &operator, right);
+ }
+ default: {
+ pm_parser_err_token(parser, &operator, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE);
+ pm_node_t *right = (pm_node_t *) pm_missing_node_create(parser, operator.start, operator.end);
+ return (pm_node_t *) pm_range_node_create(parser, NULL, &operator, right);
+ }
+ }
+ }
+ case PM_CASE_PRIMITIVE: {
+ pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_MAX, false, diag_id);
+
+ // Now that we have a primitive, we need to check if it's part of a range.
+ if (accept2(parser, PM_TOKEN_DOT_DOT, PM_TOKEN_DOT_DOT_DOT)) {
+ pm_token_t operator = parser->previous;
+
+ // Now that we have the operator, we need to check if this is followed
+ // by another expression. If it is, then we will create a full range
+ // node. Otherwise, we'll create an endless range.
+ switch (parser->current.type) {
+ case PM_CASE_PRIMITIVE: {
+ pm_node_t *right = parse_expression(parser, PM_BINDING_POWER_MAX, false, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE);
+ return (pm_node_t *) pm_range_node_create(parser, node, &operator, right);
+ }
+ default:
+ return (pm_node_t *) pm_range_node_create(parser, node, &operator, NULL);
+ }
+ }
+
+ return node;
+ }
+ case PM_TOKEN_CARET: {
+ parser_lex(parser);
+ pm_token_t operator = parser->previous;
+
+ // At this point we have a pin operator. We need to check the subsequent
+ // expression to determine if it's a variable or an expression.
+ switch (parser->current.type) {
+ case PM_TOKEN_IDENTIFIER: {
+ parser_lex(parser);
+ pm_node_t *variable = (pm_node_t *) parse_variable(parser);
+
+ if (variable == NULL) {
+ if (
+ (parser->version != PM_OPTIONS_VERSION_CRUBY_3_3_0) &&
+ !parser->current_scope->closed &&
+ (parser->current_scope->numbered_parameters != PM_SCOPE_NUMBERED_PARAMETERS_DISALLOWED) &&
+ pm_token_is_it(parser->previous.start, parser->previous.end)
+ ) {
+ pm_local_variable_read_node_t *read = pm_local_variable_read_node_create_it(parser, &parser->previous);
+ if (read == NULL) read = pm_local_variable_read_node_create(parser, &parser->previous, 0);
+ variable = (pm_node_t *) read;
+ } else {
+ PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->previous, PM_ERR_NO_LOCAL_VARIABLE);
+ variable = (pm_node_t *) pm_local_variable_read_node_missing_create(parser, &parser->previous, 0);
+ }
+ }
+
+ return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable);
+ }
+ case PM_TOKEN_INSTANCE_VARIABLE: {
+ parser_lex(parser);
+ pm_node_t *variable = (pm_node_t *) pm_instance_variable_read_node_create(parser, &parser->previous);
+
+ return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable);
+ }
+ case PM_TOKEN_CLASS_VARIABLE: {
+ parser_lex(parser);
+ pm_node_t *variable = (pm_node_t *) pm_class_variable_read_node_create(parser, &parser->previous);
+
+ return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable);
+ }
+ case PM_TOKEN_GLOBAL_VARIABLE: {
+ parser_lex(parser);
+ pm_node_t *variable = (pm_node_t *) pm_global_variable_read_node_create(parser, &parser->previous);
+
+ return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable);
+ }
+ case PM_TOKEN_NUMBERED_REFERENCE: {
+ parser_lex(parser);
+ pm_node_t *variable = (pm_node_t *) pm_numbered_reference_read_node_create(parser, &parser->previous);
+
+ return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable);
+ }
+ case PM_TOKEN_BACK_REFERENCE: {
+ parser_lex(parser);
+ pm_node_t *variable = (pm_node_t *) pm_back_reference_read_node_create(parser, &parser->previous);
+
+ return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable);
+ }
+ case PM_TOKEN_PARENTHESIS_LEFT: {
+ bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
+ parser->pattern_matching_newlines = false;
+
+ pm_token_t lparen = parser->current;
+ parser_lex(parser);
+
+ pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_STATEMENT, true, PM_ERR_PATTERN_EXPRESSION_AFTER_PIN);
+ parser->pattern_matching_newlines = previous_pattern_matching_newlines;
+
+ accept1(parser, PM_TOKEN_NEWLINE);
+ expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN);
+ return (pm_node_t *) pm_pinned_expression_node_create(parser, expression, &operator, &lparen, &parser->previous);
+ }
+ default: {
+ // If we get here, then we have a pin operator followed by something
+ // not understood. We'll create a missing node and return that.
+ pm_parser_err_token(parser, &operator, PM_ERR_PATTERN_EXPRESSION_AFTER_PIN);
+ pm_node_t *variable = (pm_node_t *) pm_missing_node_create(parser, operator.start, operator.end);
+ return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable);
+ }
+ }
+ }
+ case PM_TOKEN_UCOLON_COLON: {
+ pm_token_t delimiter = parser->current;
+ parser_lex(parser);
+
+ expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
+ pm_node_t *child = (pm_node_t *) pm_constant_read_node_create(parser, &parser->previous);
+ pm_constant_path_node_t *node = pm_constant_path_node_create(parser, NULL, &delimiter, child);
+
+ return parse_pattern_constant_path(parser, captures, (pm_node_t *) node);
+ }
+ case PM_TOKEN_CONSTANT: {
+ pm_token_t constant = parser->current;
+ parser_lex(parser);
+
+ pm_node_t *node = (pm_node_t *) pm_constant_read_node_create(parser, &constant);
+ return parse_pattern_constant_path(parser, captures, node);
+ }
+ default:
+ pm_parser_err_current(parser, diag_id);
+ return (pm_node_t *) pm_missing_node_create(parser, parser->current.start, parser->current.end);
+ }
+}
+
+/**
+ * Parse any number of primitives joined by alternation and ended optionally by
+ * assignment.
+ */
+static pm_node_t *
+parse_pattern_primitives(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_diagnostic_id_t diag_id) {
+ pm_node_t *node = NULL;
+
+ do {
+ pm_token_t operator = parser->previous;
+
+ switch (parser->current.type) {
+ case PM_TOKEN_IDENTIFIER:
+ case PM_TOKEN_BRACKET_LEFT_ARRAY:
+ case PM_TOKEN_BRACE_LEFT:
+ case PM_TOKEN_CARET:
+ case PM_TOKEN_CONSTANT:
+ case PM_TOKEN_UCOLON_COLON:
+ case PM_TOKEN_UDOT_DOT:
+ case PM_TOKEN_UDOT_DOT_DOT:
+ case PM_CASE_PRIMITIVE: {
+ if (node == NULL) {
+ node = parse_pattern_primitive(parser, captures, diag_id);
+ } else {
+ pm_node_t *right = parse_pattern_primitive(parser, captures, PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE);
+ node = (pm_node_t *) pm_alternation_pattern_node_create(parser, node, right, &operator);
+ }
+
+ break;
+ }
+ case PM_TOKEN_PARENTHESIS_LEFT: {
+ pm_token_t opening = parser->current;
+ parser_lex(parser);
+
+ pm_node_t *body = parse_pattern(parser, captures, false, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN);
+ accept1(parser, PM_TOKEN_NEWLINE);
+ expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN);
+ pm_node_t *right = (pm_node_t *) pm_parentheses_node_create(parser, &opening, body, &parser->previous);
+
+ if (node == NULL) {
+ node = right;
+ } else {
+ node = (pm_node_t *) pm_alternation_pattern_node_create(parser, node, right, &operator);
+ }
+
+ break;
+ }
+ default: {
+ pm_parser_err_current(parser, diag_id);
+ pm_node_t *right = (pm_node_t *) pm_missing_node_create(parser, parser->current.start, parser->current.end);
+
+ if (node == NULL) {
+ node = right;
+ } else {
+ node = (pm_node_t *) pm_alternation_pattern_node_create(parser, node, right, &operator);
+ }
+
+ break;
+ }
+ }
+ } while (accept1(parser, PM_TOKEN_PIPE));
+
+ // If we have an =>, then we are assigning this pattern to a variable.
+ // In this case we should create an assignment node.
+ while (accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
+ pm_token_t operator = parser->previous;
+ expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_PATTERN_IDENT_AFTER_HROCKET);
+
+ pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
+ int depth;
+
+ if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
+ pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
+ }
+
+ parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous));
+ pm_node_t *target = (pm_node_t *) pm_local_variable_target_node_create(
+ parser,
+ &PM_LOCATION_TOKEN_VALUE(&parser->previous),
+ constant_id,
+ (uint32_t) (depth == -1 ? 0 : depth)
+ );
+
+ node = (pm_node_t *) pm_capture_pattern_node_create(parser, node, target, &operator);
+ }
+
+ return node;
+}
+
+/**
+ * Parse a pattern matching expression.
+ */
+static pm_node_t *
+parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, bool top_pattern, pm_diagnostic_id_t diag_id) {
+ pm_node_t *node = NULL;
+
+ bool leading_rest = false;
+ bool trailing_rest = false;
+
+ switch (parser->current.type) {
+ case PM_TOKEN_LABEL: {
+ parser_lex(parser);
+ pm_node_t *key = (pm_node_t *) pm_symbol_node_label_create(parser, &parser->previous);
+ return (pm_node_t *) parse_pattern_hash(parser, captures, key);
+ }
+ case PM_TOKEN_USTAR_STAR: {
+ node = parse_pattern_keyword_rest(parser, captures);
+ return (pm_node_t *) parse_pattern_hash(parser, captures, node);
+ }
+ case PM_TOKEN_USTAR: {
+ if (top_pattern) {
+ parser_lex(parser);
+ node = (pm_node_t *) parse_pattern_rest(parser, captures);
+ leading_rest = true;
+ break;
+ }
+ }
+ /* fallthrough */
+ default:
+ node = parse_pattern_primitives(parser, captures, diag_id);
+ break;
+ }
+
+ // If we got a dynamic label symbol, then we need to treat it like the
+ // beginning of a hash pattern.
+ if (pm_symbol_node_label_p(node)) {
+ return (pm_node_t *) parse_pattern_hash(parser, captures, node);
+ }
+
+ if (top_pattern && match1(parser, PM_TOKEN_COMMA)) {
+ // If we have a comma, then we are now parsing either an array pattern or a
+ // find pattern. We need to parse all of the patterns, put them into a big
+ // list, and then determine which type of node we have.
+ pm_node_list_t nodes = { 0 };
+ pm_node_list_append(&nodes, node);
+
+ // Gather up all of the patterns into the list.
+ while (accept1(parser, PM_TOKEN_COMMA)) {
+ // Break early here in case we have a trailing comma.
+ if (match6(parser, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_EOF)) {
+ node = (pm_node_t *) pm_implicit_rest_node_create(parser, &parser->previous);
+ pm_node_list_append(&nodes, node);
+ break;
+ }
+
+ if (accept1(parser, PM_TOKEN_USTAR)) {
+ node = (pm_node_t *) parse_pattern_rest(parser, captures);
+
+ // If we have already parsed a splat pattern, then this is an error. We
+ // will continue to parse the rest of the patterns, but we will indicate
+ // it as an error.
+ if (trailing_rest) {
+ pm_parser_err_previous(parser, PM_ERR_PATTERN_REST);
+ }
+
+ trailing_rest = true;
+ } else {
+ node = parse_pattern_primitives(parser, captures, PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA);
+ }
+
+ pm_node_list_append(&nodes, node);
+ }
+
+ // If the first pattern and the last pattern are rest patterns, then we will
+ // call this a find pattern, regardless of how many rest patterns are in
+ // between because we know we already added the appropriate errors.
+ // Otherwise we will create an array pattern.
+ if (PM_NODE_TYPE_P(nodes.nodes[0], PM_SPLAT_NODE) && PM_NODE_TYPE_P(nodes.nodes[nodes.size - 1], PM_SPLAT_NODE)) {
+ node = (pm_node_t *) pm_find_pattern_node_create(parser, &nodes);
+ } else {
+ node = (pm_node_t *) pm_array_pattern_node_node_list_create(parser, &nodes);
+ }
+
+ xfree(nodes.nodes);
+ } else if (leading_rest) {
+ // Otherwise, if we parsed a single splat pattern, then we know we have an
+ // array pattern, so we can go ahead and create that node.
+ node = (pm_node_t *) pm_array_pattern_node_rest_create(parser, node);
+ }
+
+ return node;
+}
+
+/**
+ * Incorporate a negative sign into a numeric node by subtracting 1 character
+ * from its start bounds. If it's a compound node, then we will recursively
+ * apply this function to its value.
+ */
+static inline void
+parse_negative_numeric(pm_node_t *node) {
+ switch (PM_NODE_TYPE(node)) {
+ case PM_INTEGER_NODE: {
+ pm_integer_node_t *cast = (pm_integer_node_t *) node;
+ cast->base.location.start--;
+ cast->value.negative = true;
+ break;
+ }
+ case PM_FLOAT_NODE: {
+ pm_float_node_t *cast = (pm_float_node_t *) node;
+ cast->base.location.start--;
+ cast->value = -cast->value;
+ break;
+ }
+ case PM_RATIONAL_NODE:
+ node->location.start--;
+ parse_negative_numeric(((pm_rational_node_t *) node)->numeric);
+ break;
+ case PM_IMAGINARY_NODE:
+ node->location.start--;
+ parse_negative_numeric(((pm_imaginary_node_t *) node)->numeric);
+ break;
+ default:
+ assert(false && "unreachable");
+ break;
+ }
+}
+
+/**
+ * Return a string content token at a particular location that is empty.
+ */
+static pm_token_t
+parse_strings_empty_content(const uint8_t *location) {
+ return (pm_token_t) { .type = PM_TOKEN_STRING_CONTENT, .start = location, .end = location };
+}
+
+/**
+ * Parse a set of strings that could be concatenated together.
+ */
+static inline pm_node_t *
+parse_strings(pm_parser_t *parser, pm_node_t *current) {
+ assert(parser->current.type == PM_TOKEN_STRING_BEGIN);
+
+ bool concating = false;
+ bool state_is_arg_labeled = lex_state_arg_labeled_p(parser);
+
+ while (match1(parser, PM_TOKEN_STRING_BEGIN)) {
+ pm_node_t *node = NULL;
+
+ // Here we have found a string literal. We'll parse it and add it to
+ // the list of strings.
+ const pm_lex_mode_t *lex_mode = parser->lex_modes.current;
+ assert(lex_mode->mode == PM_LEX_STRING);
+ bool lex_interpolation = lex_mode->as.string.interpolation;
+
+ pm_token_t opening = parser->current;
+ parser_lex(parser);
+
+ if (match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
+ expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_EOF);
+ // If we get here, then we have an end immediately after a
+ // start. In that case we'll create an empty content token and
+ // return an uninterpolated string.
+ pm_token_t content = parse_strings_empty_content(parser->previous.start);
+ pm_string_node_t *string = pm_string_node_create(parser, &opening, &content, &parser->previous);
+
+ pm_string_shared_init(&string->unescaped, content.start, content.end);
+ node = (pm_node_t *) string;
+ } else if (accept1(parser, PM_TOKEN_LABEL_END)) {
+ // If we get here, then we have an end of a label immediately
+ // after a start. In that case we'll create an empty symbol
+ // node.
+ pm_token_t content = parse_strings_empty_content(parser->previous.start);
+ pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &content, &parser->previous);
+
+ pm_string_shared_init(&symbol->unescaped, content.start, content.end);
+ node = (pm_node_t *) symbol;
+ } else if (!lex_interpolation) {
+ // If we don't accept interpolation then we expect the string to
+ // start with a single string content node.
+ pm_string_t unescaped;
+ pm_token_t content;
+ if (match1(parser, PM_TOKEN_EOF)) {
+ unescaped = PM_STRING_EMPTY;
+ content = not_provided(parser);
+ } else {
+ unescaped = parser->current_string;
+ expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_EXPECT_STRING_CONTENT);
+ content = parser->previous;
+ }
+
+ // It is unfortunately possible to have multiple string content
+ // nodes in a row in the case that there's heredoc content in
+ // the middle of the string, like this cursed example:
+ //
+ // <<-END+'b
+ // a
+ // END
+ // c'+'d'
+ //
+ // In that case we need to switch to an interpolated string to
+ // be able to contain all of the parts.
+ if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
+ pm_node_list_t parts = { 0 };
+
+ pm_token_t delimiters = not_provided(parser);
+ pm_node_t *part = (pm_node_t *) pm_string_node_create_unescaped(parser, &delimiters, &content, &delimiters, &unescaped);
+ pm_node_list_append(&parts, part);
+
+ do {
+ part = (pm_node_t *) pm_string_node_create_current_string(parser, &delimiters, &parser->current, &delimiters);
+ pm_node_list_append(&parts, part);
+ parser_lex(parser);
+ } while (match1(parser, PM_TOKEN_STRING_CONTENT));
+
+ expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_EOF);
+ node = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous);
+
+ pm_node_list_free(&parts);
+ } else if (accept1(parser, PM_TOKEN_LABEL_END) && !state_is_arg_labeled) {
+ node = (pm_node_t *) pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &unescaped));
+ } else if (match1(parser, PM_TOKEN_EOF)) {
+ pm_parser_err_token(parser, &opening, PM_ERR_STRING_LITERAL_EOF);
+ node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped);
+ } else if (accept1(parser, PM_TOKEN_STRING_END)) {
+ node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped);
+ } else {
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->previous, PM_ERR_STRING_LITERAL_TERM, pm_token_type_human(parser->previous.type));
+ parser->previous.start = parser->previous.end;
+ parser->previous.type = PM_TOKEN_MISSING;
+ node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped);
+ }
+ } else if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
+ // In this case we've hit string content so we know the string
+ // at least has something in it. We'll need to check if the
+ // following token is the end (in which case we can return a
+ // plain string) or if it's not then it has interpolation.
+ pm_token_t content = parser->current;
+ pm_string_t unescaped = parser->current_string;
+ parser_lex(parser);
+
+ if (match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
+ node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped);
+ pm_node_flag_set(node, parse_unescaped_encoding(parser));
+ expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_EOF);
+ } else if (accept1(parser, PM_TOKEN_LABEL_END)) {
+ node = (pm_node_t *) pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &unescaped));
+ } else {
+ // If we get here, then we have interpolation so we'll need
+ // to create a string or symbol node with interpolation.
+ pm_node_list_t parts = { 0 };
+ pm_token_t string_opening = not_provided(parser);
+ pm_token_t string_closing = not_provided(parser);
+
+ pm_node_t *part = (pm_node_t *) pm_string_node_create_unescaped(parser, &string_opening, &parser->previous, &string_closing, &unescaped);
+ pm_node_flag_set(part, parse_unescaped_encoding(parser));
+ pm_node_list_append(&parts, part);
+
+ while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) {
+ if ((part = parse_string_part(parser)) != NULL) {
+ pm_node_list_append(&parts, part);
+ }
+ }
+
+ if (accept1(parser, PM_TOKEN_LABEL_END) && !state_is_arg_labeled) {
+ node = (pm_node_t *) pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous);
+ } else if (match1(parser, PM_TOKEN_EOF)) {
+ pm_parser_err_token(parser, &opening, PM_ERR_STRING_INTERPOLATED_TERM);
+ node = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, &parts, &parser->current);
+ } else {
+ expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_INTERPOLATED_TERM);
+ node = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous);
+ }
+
+ pm_node_list_free(&parts);
+ }
+ } else {
+ // If we get here, then the first part of the string is not plain
+ // string content, in which case we need to parse the string as an
+ // interpolated string.
+ pm_node_list_t parts = { 0 };
+ pm_node_t *part;
+
+ while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) {
+ if ((part = parse_string_part(parser)) != NULL) {
+ pm_node_list_append(&parts, part);
+ }
+ }
+
+ if (accept1(parser, PM_TOKEN_LABEL_END)) {
+ node = (pm_node_t *) pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous);
+ } else if (match1(parser, PM_TOKEN_EOF)) {
+ pm_parser_err_token(parser, &opening, PM_ERR_STRING_INTERPOLATED_TERM);
+ node = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, &parts, &parser->current);
+ } else {
+ expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_INTERPOLATED_TERM);
+ node = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous);
+ }
+
+ pm_node_list_free(&parts);
+ }
+
+ if (current == NULL) {
+ // If the node we just parsed is a symbol node, then we can't
+ // concatenate it with anything else, so we can now return that
+ // node.
+ if (PM_NODE_TYPE_P(node, PM_SYMBOL_NODE) || PM_NODE_TYPE_P(node, PM_INTERPOLATED_SYMBOL_NODE)) {
+ return node;
+ }
+
+ // If we don't already have a node, then it's fine and we can just
+ // set the result to be the node we just parsed.
+ current = node;
+ } else {
+ // Otherwise we need to check the type of the node we just parsed.
+ // If it cannot be concatenated with the previous node, then we'll
+ // need to add a syntax error.
+ if (!PM_NODE_TYPE_P(node, PM_STRING_NODE) && !PM_NODE_TYPE_P(node, PM_INTERPOLATED_STRING_NODE)) {
+ pm_parser_err_node(parser, node, PM_ERR_STRING_CONCATENATION);
+ }
+
+ // If we haven't already created our container for concatenation,
+ // we'll do that now.
+ if (!concating) {
+ concating = true;
+ pm_token_t bounds = not_provided(parser);
+
+ pm_interpolated_string_node_t *container = pm_interpolated_string_node_create(parser, &bounds, NULL, &bounds);
+ pm_interpolated_string_node_append(parser, container, current);
+ current = (pm_node_t *) container;
+ }
+
+ pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, node);
+ }
+ }
+
+ return current;
+}
+
+/**
+ * Append an error to the error list on the parser using the given diagnostic
+ * ID. This function is a specialization that handles formatting the specific
+ * kind of error that is being appended.
+ */
+static void
+pm_parser_err_prefix(pm_parser_t *parser, pm_diagnostic_id_t diag_id) {
+ switch (diag_id) {
+ case PM_ERR_HASH_KEY: {
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->previous, diag_id, pm_token_type_human(parser->previous.type));
+ break;
+ }
+ case PM_ERR_UNARY_RECEIVER: {
+ const char *human = (parser->current.type == PM_TOKEN_EOF ? "end-of-input" : pm_token_type_human(parser->current.type));
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->previous, diag_id, human, parser->previous.start[0]);
+ break;
+ }
+ default:
+ pm_parser_err_previous(parser, diag_id);
+ break;
+ }
+}
+
+/**
+ * Ensures that the current retry token is valid in the current context.
+ */
+static void
+parse_retry(pm_parser_t *parser, const pm_node_t *node) {
+ pm_context_node_t *context_node = parser->current_context;
+
+ while (context_node != NULL) {
+ switch (context_node->context) {
+ case PM_CONTEXT_BEGIN_RESCUE:
+ case PM_CONTEXT_BLOCK_RESCUE:
+ case PM_CONTEXT_CLASS_RESCUE:
+ case PM_CONTEXT_DEF_RESCUE:
+ case PM_CONTEXT_LAMBDA_RESCUE:
+ case PM_CONTEXT_MODULE_RESCUE:
+ case PM_CONTEXT_SCLASS_RESCUE:
+ case PM_CONTEXT_DEFINED:
+ case PM_CONTEXT_RESCUE_MODIFIER:
+ // These are the good cases. We're allowed to have a retry here.
+ return;
+ case PM_CONTEXT_CLASS:
+ case PM_CONTEXT_DEF:
+ case PM_CONTEXT_DEF_PARAMS:
+ case PM_CONTEXT_MAIN:
+ case PM_CONTEXT_MODULE:
+ case PM_CONTEXT_PREEXE:
+ case PM_CONTEXT_SCLASS:
+ // These are the bad cases. We're not allowed to have a retry in
+ // these contexts.
+ pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_WITHOUT_RESCUE);
+ return;
+ case PM_CONTEXT_BEGIN_ELSE:
+ case PM_CONTEXT_BLOCK_ELSE:
+ case PM_CONTEXT_CLASS_ELSE:
+ case PM_CONTEXT_DEF_ELSE:
+ case PM_CONTEXT_LAMBDA_ELSE:
+ case PM_CONTEXT_MODULE_ELSE:
+ case PM_CONTEXT_SCLASS_ELSE:
+ // These are also bad cases, but with a more specific error
+ // message indicating the else.
+ pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_AFTER_ELSE);
+ return;
+ case PM_CONTEXT_BEGIN_ENSURE:
+ case PM_CONTEXT_BLOCK_ENSURE:
+ case PM_CONTEXT_CLASS_ENSURE:
+ case PM_CONTEXT_DEF_ENSURE:
+ case PM_CONTEXT_LAMBDA_ENSURE:
+ case PM_CONTEXT_MODULE_ENSURE:
+ case PM_CONTEXT_SCLASS_ENSURE:
+ // These are also bad cases, but with a more specific error
+ // message indicating the ensure.
+ pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_AFTER_ENSURE);
+ return;
+ case PM_CONTEXT_NONE:
+ // This case should never happen.
+ assert(false && "unreachable");
+ break;
+ case PM_CONTEXT_BEGIN:
+ case PM_CONTEXT_BLOCK_BRACES:
+ case PM_CONTEXT_BLOCK_KEYWORDS:
+ case PM_CONTEXT_CASE_IN:
+ case PM_CONTEXT_CASE_WHEN:
+ case PM_CONTEXT_DEFAULT_PARAMS:
+ case PM_CONTEXT_ELSE:
+ case PM_CONTEXT_ELSIF:
+ case PM_CONTEXT_EMBEXPR:
+ case PM_CONTEXT_FOR_INDEX:
+ case PM_CONTEXT_FOR:
+ case PM_CONTEXT_IF:
+ case PM_CONTEXT_LAMBDA_BRACES:
+ case PM_CONTEXT_LAMBDA_DO_END:
+ case PM_CONTEXT_PARENS:
+ case PM_CONTEXT_POSTEXE:
+ case PM_CONTEXT_PREDICATE:
+ case PM_CONTEXT_TERNARY:
+ case PM_CONTEXT_UNLESS:
+ case PM_CONTEXT_UNTIL:
+ case PM_CONTEXT_WHILE:
+ // In these contexts we should continue walking up the list of
+ // contexts.
+ break;
+ }
+
+ context_node = context_node->prev;
+ }
+}
+
+/**
+ * Ensures that the current yield token is valid in the current context.
+ */
+static void
+parse_yield(pm_parser_t *parser, const pm_node_t *node) {
+ pm_context_node_t *context_node = parser->current_context;
+
+ while (context_node != NULL) {
+ switch (context_node->context) {
+ case PM_CONTEXT_DEF:
+ case PM_CONTEXT_DEF_PARAMS:
+ case PM_CONTEXT_DEFINED:
+ case PM_CONTEXT_DEF_ENSURE:
+ case PM_CONTEXT_DEF_RESCUE:
+ case PM_CONTEXT_DEF_ELSE:
+ // These are the good cases. We're allowed to have a block exit
+ // in these contexts.
+ return;
+ case PM_CONTEXT_CLASS:
+ case PM_CONTEXT_CLASS_ENSURE:
+ case PM_CONTEXT_CLASS_RESCUE:
+ case PM_CONTEXT_CLASS_ELSE:
+ case PM_CONTEXT_MAIN:
+ case PM_CONTEXT_MODULE:
+ case PM_CONTEXT_MODULE_ENSURE:
+ case PM_CONTEXT_MODULE_RESCUE:
+ case PM_CONTEXT_MODULE_ELSE:
+ case PM_CONTEXT_SCLASS:
+ case PM_CONTEXT_SCLASS_RESCUE:
+ case PM_CONTEXT_SCLASS_ENSURE:
+ case PM_CONTEXT_SCLASS_ELSE:
+ // These are the bad cases. We're not allowed to have a retry in
+ // these contexts.
+ pm_parser_err_node(parser, node, PM_ERR_INVALID_YIELD);
+ return;
+ case PM_CONTEXT_NONE:
+ // This case should never happen.
+ assert(false && "unreachable");
+ break;
+ case PM_CONTEXT_BEGIN:
+ case PM_CONTEXT_BEGIN_ELSE:
+ case PM_CONTEXT_BEGIN_ENSURE:
+ case PM_CONTEXT_BEGIN_RESCUE:
+ case PM_CONTEXT_BLOCK_BRACES:
+ case PM_CONTEXT_BLOCK_KEYWORDS:
+ case PM_CONTEXT_BLOCK_ELSE:
+ case PM_CONTEXT_BLOCK_ENSURE:
+ case PM_CONTEXT_BLOCK_RESCUE:
+ case PM_CONTEXT_CASE_IN:
+ case PM_CONTEXT_CASE_WHEN:
+ case PM_CONTEXT_DEFAULT_PARAMS:
+ case PM_CONTEXT_ELSE:
+ case PM_CONTEXT_ELSIF:
+ case PM_CONTEXT_EMBEXPR:
+ case PM_CONTEXT_FOR_INDEX:
+ case PM_CONTEXT_FOR:
+ case PM_CONTEXT_IF:
+ case PM_CONTEXT_LAMBDA_BRACES:
+ case PM_CONTEXT_LAMBDA_DO_END:
+ case PM_CONTEXT_LAMBDA_ELSE:
+ case PM_CONTEXT_LAMBDA_ENSURE:
+ case PM_CONTEXT_LAMBDA_RESCUE:
+ case PM_CONTEXT_PARENS:
+ case PM_CONTEXT_POSTEXE:
+ case PM_CONTEXT_PREDICATE:
+ case PM_CONTEXT_PREEXE:
+ case PM_CONTEXT_RESCUE_MODIFIER:
+ case PM_CONTEXT_TERNARY:
+ case PM_CONTEXT_UNLESS:
+ case PM_CONTEXT_UNTIL:
+ case PM_CONTEXT_WHILE:
+ // In these contexts we should continue walking up the list of
+ // contexts.
+ break;
+ }
+
+ context_node = context_node->prev;
+ }
+}
+
+/**
+ * Parse an expression that begins with the previous node that we just lexed.
+ */
+static inline pm_node_t *
+parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, bool accepts_command_call, pm_diagnostic_id_t diag_id) {
+ switch (parser->current.type) {
+ case PM_TOKEN_BRACKET_LEFT_ARRAY: {
+ parser_lex(parser);
+
+ pm_array_node_t *array = pm_array_node_create(parser, &parser->previous);
+ pm_accepts_block_stack_push(parser, true);
+ bool parsed_bare_hash = false;
+
+ while (!match2(parser, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_EOF)) {
+ // Handle the case where we don't have a comma and we have a
+ // newline followed by a right bracket.
+ if (accept1(parser, PM_TOKEN_NEWLINE) && match1(parser, PM_TOKEN_BRACKET_RIGHT)) {
+ break;
+ }
+
+ if (pm_array_node_size(array) != 0) {
+ expect1(parser, PM_TOKEN_COMMA, PM_ERR_ARRAY_SEPARATOR);
+ }
+
+ // If we have a right bracket immediately following a comma,
+ // this is allowed since it's a trailing comma. In this case we
+ // can break out of the loop.
+ if (match1(parser, PM_TOKEN_BRACKET_RIGHT)) break;
+
+ pm_node_t *element;
+
+ if (accept1(parser, PM_TOKEN_USTAR)) {
+ pm_token_t operator = parser->previous;
+ pm_node_t *expression = NULL;
+
+ if (match3(parser, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_COMMA, PM_TOKEN_EOF)) {
+ pm_parser_scope_forwarding_positionals_check(parser, &operator);
+ } else {
+ expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_ARRAY_EXPRESSION_AFTER_STAR);
+ }
+
+ element = (pm_node_t *) pm_splat_node_create(parser, &operator, expression);
+ } else if (match2(parser, PM_TOKEN_LABEL, PM_TOKEN_USTAR_STAR)) {
+ if (parsed_bare_hash) {
+ pm_parser_err_current(parser, PM_ERR_EXPRESSION_BARE_HASH);
+ }
+
+ element = (pm_node_t *) pm_keyword_hash_node_create(parser);
+ pm_static_literals_t hash_keys = { 0 };
+
+ if (!match8(parser, PM_TOKEN_EOF, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_EOF, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_KEYWORD_DO, PM_TOKEN_PARENTHESIS_RIGHT)) {
+ parse_assocs(parser, &hash_keys, element);
+ }
+
+ pm_static_literals_free(&hash_keys);
+ parsed_bare_hash = true;
+ } else {
+ element = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_ARRAY_EXPRESSION);
+
+ if (pm_symbol_node_label_p(element) || accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
+ if (parsed_bare_hash) {
+ pm_parser_err_previous(parser, PM_ERR_EXPRESSION_BARE_HASH);
+ }
+
+ pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser);
+ pm_static_literals_t hash_keys = { 0 };
+ pm_hash_key_static_literals_add(parser, &hash_keys, element);
+
+ pm_token_t operator;
+ if (parser->previous.type == PM_TOKEN_EQUAL_GREATER) {
+ operator = parser->previous;
+ } else {
+ operator = not_provided(parser);
+ }
+
+ pm_node_t *value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_HASH_VALUE);
+ pm_node_t *assoc = (pm_node_t *) pm_assoc_node_create(parser, element, &operator, value);
+ pm_keyword_hash_node_elements_append(hash, assoc);
+
+ element = (pm_node_t *) hash;
+ if (accept1(parser, PM_TOKEN_COMMA) && !match1(parser, PM_TOKEN_BRACKET_RIGHT)) {
+ parse_assocs(parser, &hash_keys, element);
+ }
+
+ pm_static_literals_free(&hash_keys);
+ parsed_bare_hash = true;
+ }
+ }
+
+ pm_array_node_elements_append(array, element);
+ if (PM_NODE_TYPE_P(element, PM_MISSING_NODE)) break;
+ }
+
+ accept1(parser, PM_TOKEN_NEWLINE);
+ expect1(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_ARRAY_TERM);
+ pm_array_node_close_set(array, &parser->previous);
+ pm_accepts_block_stack_pop(parser);
+
+ return (pm_node_t *) array;
+ }
+ case PM_TOKEN_PARENTHESIS_LEFT:
+ case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES: {
+ pm_token_t opening = parser->current;
+
+ pm_node_list_t current_block_exits = { 0 };
+ pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
+
+ parser_lex(parser);
+ while (accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE));
+
+ // If this is the end of the file or we match a right parenthesis, then
+ // we have an empty parentheses node, and we can immediately return.
+ if (match2(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_EOF)) {
+ expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
+
+ pop_block_exits(parser, previous_block_exits);
+ pm_node_list_free(&current_block_exits);
+
+ return (pm_node_t *) pm_parentheses_node_create(parser, &opening, NULL, &parser->previous);
+ }
+
+ // Otherwise, we're going to parse the first statement in the list
+ // of statements within the parentheses.
+ pm_accepts_block_stack_push(parser, true);
+ context_push(parser, PM_CONTEXT_PARENS);
+ pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_STATEMENT, true, PM_ERR_CANNOT_PARSE_EXPRESSION);
+ context_pop(parser);
+
+ // Determine if this statement is followed by a terminator. In the
+ // case of a single statement, this is fine. But in the case of
+ // multiple statements it's required.
+ bool terminator_found = accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
+ if (terminator_found) {
+ while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
+ }
+
+ // If we hit a right parenthesis, then we're done parsing the
+ // parentheses node, and we can check which kind of node we should
+ // return.
+ if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
+ if (opening.type == PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES) {
+ lex_state_set(parser, PM_LEX_STATE_ENDARG);
+ }
+
+ parser_lex(parser);
+ pm_accepts_block_stack_pop(parser);
+
+ pop_block_exits(parser, previous_block_exits);
+ pm_node_list_free(&current_block_exits);
+
+ if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE) || PM_NODE_TYPE_P(statement, PM_SPLAT_NODE)) {
+ // If we have a single statement and are ending on a right
+ // parenthesis, then we need to check if this is possibly a
+ // multiple target node.
+ pm_multi_target_node_t *multi_target;
+
+ if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE) && ((pm_multi_target_node_t *) statement)->lparen_loc.start == NULL) {
+ multi_target = (pm_multi_target_node_t *) statement;
+ } else {
+ multi_target = pm_multi_target_node_create(parser);
+ pm_multi_target_node_targets_append(parser, multi_target, statement);
+ }
+
+ pm_location_t lparen_loc = PM_LOCATION_TOKEN_VALUE(&opening);
+ pm_location_t rparen_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous);
+
+ multi_target->lparen_loc = lparen_loc;
+ multi_target->rparen_loc = rparen_loc;
+ multi_target->base.location.start = lparen_loc.start;
+ multi_target->base.location.end = rparen_loc.end;
+
+ if (match1(parser, PM_TOKEN_COMMA)) {
+ if (binding_power == PM_BINDING_POWER_STATEMENT) {
+ return parse_targets_validate(parser, (pm_node_t *) multi_target, PM_BINDING_POWER_INDEX);
+ }
+ return (pm_node_t *) multi_target;
+ }
+
+ return parse_target_validate(parser, (pm_node_t *) multi_target);
+ }
+
+ // If we have a single statement and are ending on a right parenthesis
+ // and we didn't return a multiple assignment node, then we can return a
+ // regular parentheses node now.
+ pm_statements_node_t *statements = pm_statements_node_create(parser);
+ pm_statements_node_body_append(parser, statements, statement);
+
+ return (pm_node_t *) pm_parentheses_node_create(parser, &opening, (pm_node_t *) statements, &parser->previous);
+ }
+
+ // If we have more than one statement in the set of parentheses,
+ // then we are going to parse all of them as a list of statements.
+ // We'll do that here.
+ context_push(parser, PM_CONTEXT_PARENS);
+ pm_statements_node_t *statements = pm_statements_node_create(parser);
+ pm_statements_node_body_append(parser, statements, statement);
+
+ // If we didn't find a terminator and we didn't find a right
+ // parenthesis, then this is a syntax error.
+ if (!terminator_found) {
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(parser->current.type));
+ }
+
+ // Parse each statement within the parentheses.
+ while (true) {
+ pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, true, PM_ERR_CANNOT_PARSE_EXPRESSION);
+ pm_statements_node_body_append(parser, statements, node);
+
+ // If we're recovering from a syntax error, then we need to stop
+ // parsing the statements now.
+ if (parser->recovering) {
+ // If this is the level of context where the recovery has
+ // happened, then we can mark the parser as done recovering.
+ if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) parser->recovering = false;
+ break;
+ }
+
+ // If we couldn't parse an expression at all, then we need to
+ // bail out of the loop.
+ if (PM_NODE_TYPE_P(node, PM_MISSING_NODE)) break;
+
+ // If we successfully parsed a statement, then we are going to
+ // need terminator to delimit them.
+ if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
+ while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
+ if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) break;
+ } else if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
+ break;
+ } else {
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(parser->current.type));
+ }
+ }
+
+ context_pop(parser);
+ pm_accepts_block_stack_pop(parser);
+ expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
+
+ pop_block_exits(parser, previous_block_exits);
+ pm_node_list_free(&current_block_exits);
+
+ pm_void_statements_check(parser, statements);
+ return (pm_node_t *) pm_parentheses_node_create(parser, &opening, (pm_node_t *) statements, &parser->previous);
+ }
+ case PM_TOKEN_BRACE_LEFT: {
+ // If we were passed a current_hash_keys via the parser, then that
+ // means we're already parsing a hash and we want to share the set
+ // of hash keys with this inner hash we're about to parse for the
+ // sake of warnings. We'll set it to NULL after we grab it to make
+ // sure subsequent expressions don't use it. Effectively this is a
+ // way of getting around passing it to every call to
+ // parse_expression.
+ pm_static_literals_t *current_hash_keys = parser->current_hash_keys;
+ parser->current_hash_keys = NULL;
+
+ pm_accepts_block_stack_push(parser, true);
+ parser_lex(parser);
+
+ pm_hash_node_t *node = pm_hash_node_create(parser, &parser->previous);
+
+ if (!match2(parser, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_EOF)) {
+ if (current_hash_keys != NULL) {
+ parse_assocs(parser, current_hash_keys, (pm_node_t *) node);
+ } else {
+ pm_static_literals_t hash_keys = { 0 };
+ parse_assocs(parser, &hash_keys, (pm_node_t *) node);
+ pm_static_literals_free(&hash_keys);
+ }
+
+ accept1(parser, PM_TOKEN_NEWLINE);
+ }
+
+ pm_accepts_block_stack_pop(parser);
+ expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_HASH_TERM);
+ pm_hash_node_closing_loc_set(node, &parser->previous);
+
+ return (pm_node_t *) node;
+ }
+ case PM_TOKEN_CHARACTER_LITERAL: {
+ parser_lex(parser);
+
+ pm_token_t opening = parser->previous;
+ opening.type = PM_TOKEN_STRING_BEGIN;
+ opening.end = opening.start + 1;
+
+ pm_token_t content = parser->previous;
+ content.type = PM_TOKEN_STRING_CONTENT;
+ content.start = content.start + 1;
+
+ pm_token_t closing = not_provided(parser);
+ pm_node_t *node = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &content, &closing);
+ pm_node_flag_set(node, parse_unescaped_encoding(parser));
+
+ // Characters can be followed by strings in which case they are
+ // automatically concatenated.
+ if (match1(parser, PM_TOKEN_STRING_BEGIN)) {
+ return parse_strings(parser, node);
+ }
+
+ return node;
+ }
+ case PM_TOKEN_CLASS_VARIABLE: {
+ parser_lex(parser);
+ pm_node_t *node = (pm_node_t *) pm_class_variable_read_node_create(parser, &parser->previous);
+
+ if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
+ node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX);
+ }
+
+ return node;
+ }
+ case PM_TOKEN_CONSTANT: {
+ parser_lex(parser);
+ pm_token_t constant = parser->previous;
+
+ // If a constant is immediately followed by parentheses, then this is in
+ // fact a method call, not a constant read.
+ if (
+ match1(parser, PM_TOKEN_PARENTHESIS_LEFT) ||
+ (accepts_command_call && (token_begins_expression_p(parser->current.type) || match3(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR))) ||
+ (pm_accepts_block_stack_p(parser) && match1(parser, PM_TOKEN_KEYWORD_DO)) ||
+ match1(parser, PM_TOKEN_BRACE_LEFT)
+ ) {
+ pm_arguments_t arguments = { 0 };
+ parse_arguments_list(parser, &arguments, true, accepts_command_call);
+ return (pm_node_t *) pm_call_node_fcall_create(parser, &constant, &arguments);
+ }
+
+ pm_node_t *node = (pm_node_t *) pm_constant_read_node_create(parser, &parser->previous);
+
+ if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) {
+ // If we get here, then we have a comma immediately following a
+ // constant, so we're going to parse this as a multiple assignment.
+ node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX);
+ }
+
+ return node;
+ }
+ case PM_TOKEN_UCOLON_COLON: {
+ parser_lex(parser);
+
+ pm_token_t delimiter = parser->previous;
+ expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
+
+ pm_node_t *constant = (pm_node_t *) pm_constant_read_node_create(parser, &parser->previous);
+ pm_node_t *node = (pm_node_t *)pm_constant_path_node_create(parser, NULL, &delimiter, constant);
+
+ if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) {
+ node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX);
+ }
+
+ return node;
+ }
+ case PM_TOKEN_UDOT_DOT:
+ case PM_TOKEN_UDOT_DOT_DOT: {
+ pm_token_t operator = parser->current;
+ parser_lex(parser);
+
+ pm_node_t *right = parse_expression(parser, pm_binding_powers[operator.type].left, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
+ return (pm_node_t *) pm_range_node_create(parser, NULL, &operator, right);
+ }
+ case PM_TOKEN_FLOAT:
+ parser_lex(parser);
+ return (pm_node_t *) pm_float_node_create(parser, &parser->previous);
+ case PM_TOKEN_FLOAT_IMAGINARY:
+ parser_lex(parser);
+ return (pm_node_t *) pm_float_node_imaginary_create(parser, &parser->previous);
+ case PM_TOKEN_FLOAT_RATIONAL:
+ parser_lex(parser);
+ return (pm_node_t *) pm_float_node_rational_create(parser, &parser->previous);
+ case PM_TOKEN_FLOAT_RATIONAL_IMAGINARY:
+ parser_lex(parser);
+ return (pm_node_t *) pm_float_node_rational_imaginary_create(parser, &parser->previous);
+ case PM_TOKEN_NUMBERED_REFERENCE: {
+ parser_lex(parser);
+ pm_node_t *node = (pm_node_t *) pm_numbered_reference_read_node_create(parser, &parser->previous);
+
+ if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
+ node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX);
+ }
+
+ return node;
+ }
+ case PM_TOKEN_GLOBAL_VARIABLE: {
+ parser_lex(parser);
+ pm_node_t *node = (pm_node_t *) pm_global_variable_read_node_create(parser, &parser->previous);
+
+ if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
+ node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX);
+ }
+
+ return node;
+ }
+ case PM_TOKEN_BACK_REFERENCE: {
+ parser_lex(parser);
+ pm_node_t *node = (pm_node_t *) pm_back_reference_read_node_create(parser, &parser->previous);
+
+ if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
+ node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX);
+ }
+
+ return node;
+ }
+ case PM_TOKEN_IDENTIFIER:
+ case PM_TOKEN_METHOD_NAME: {
+ parser_lex(parser);
+ pm_token_t identifier = parser->previous;
+ pm_node_t *node = parse_variable_call(parser);
+
+ if (PM_NODE_TYPE_P(node, PM_CALL_NODE)) {
+ // If parse_variable_call returned with a call node, then we
+ // know the identifier is not in the local table. In that case
+ // we need to check if there are arguments following the
+ // identifier.
+ pm_call_node_t *call = (pm_call_node_t *) node;
+ pm_arguments_t arguments = { 0 };
+
+ if (parse_arguments_list(parser, &arguments, true, accepts_command_call)) {
+ // Since we found arguments, we need to turn off the
+ // variable call bit in the flags.
+ pm_node_flag_unset((pm_node_t *)call, PM_CALL_NODE_FLAGS_VARIABLE_CALL);
+
+ call->opening_loc = arguments.opening_loc;
+ call->arguments = arguments.arguments;
+ call->closing_loc = arguments.closing_loc;
+ call->block = arguments.block;
+
+ if (arguments.block != NULL) {
+ call->base.location.end = arguments.block->location.end;
+ } else if (arguments.closing_loc.start == NULL) {
+ if (arguments.arguments != NULL) {
+ call->base.location.end = arguments.arguments->base.location.end;
+ } else {
+ call->base.location.end = call->message_loc.end;
+ }
+ } else {
+ call->base.location.end = arguments.closing_loc.end;
+ }
+ }
+ } else {
+ // Otherwise, we know the identifier is in the local table. This
+ // can still be a method call if it is followed by arguments or
+ // a block, so we need to check for that here.
+ if (
+ (accepts_command_call && (token_begins_expression_p(parser->current.type) || match3(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR))) ||
+ (pm_accepts_block_stack_p(parser) && match1(parser, PM_TOKEN_KEYWORD_DO)) ||
+ match1(parser, PM_TOKEN_BRACE_LEFT)
+ ) {
+ pm_arguments_t arguments = { 0 };
+ parse_arguments_list(parser, &arguments, true, accepts_command_call);
+
+ pm_call_node_t *fcall = pm_call_node_fcall_create(parser, &identifier, &arguments);
+ pm_node_destroy(parser, node);
+ return (pm_node_t *) fcall;
+ }
+ }
+
+ if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) {
+ node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX);
+ } else {
+ // Check if `it` is not going to be assigned.
+ switch (parser->current.type) {
+ case PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL:
+ case PM_TOKEN_AMPERSAND_EQUAL:
+ case PM_TOKEN_CARET_EQUAL:
+ case PM_TOKEN_EQUAL:
+ case PM_TOKEN_GREATER_GREATER_EQUAL:
+ case PM_TOKEN_LESS_LESS_EQUAL:
+ case PM_TOKEN_MINUS_EQUAL:
+ case PM_TOKEN_PARENTHESIS_RIGHT:
+ case PM_TOKEN_PERCENT_EQUAL:
+ case PM_TOKEN_PIPE_EQUAL:
+ case PM_TOKEN_PIPE_PIPE_EQUAL:
+ case PM_TOKEN_PLUS_EQUAL:
+ case PM_TOKEN_SLASH_EQUAL:
+ case PM_TOKEN_STAR_EQUAL:
+ case PM_TOKEN_STAR_STAR_EQUAL:
+ break;
+ default:
+ // Once we know it's neither a method call nor an
+ // assignment, we can finally create `it` default
+ // parameter.
+ node = pm_node_check_it(parser, node);
+ }
+ }
+
+ return node;
+ }
+ case PM_TOKEN_HEREDOC_START: {
+ // Here we have found a heredoc. We'll parse it and add it to the
+ // list of strings.
+ pm_lex_mode_t *lex_mode = parser->lex_modes.current;
+ assert(lex_mode->mode == PM_LEX_HEREDOC);
+ pm_heredoc_quote_t quote = lex_mode->as.heredoc.quote;
+ pm_heredoc_indent_t indent = lex_mode->as.heredoc.indent;
+
+ parser_lex(parser);
+ pm_token_t opening = parser->previous;
+
+ pm_node_t *node;
+ pm_node_t *part;
+
+ if (match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
+ // If we get here, then we have an empty heredoc. We'll create
+ // an empty content token and return an empty string node.
+ lex_mode_pop(parser);
+ expect1(parser, PM_TOKEN_HEREDOC_END, PM_ERR_HEREDOC_TERM);
+ pm_token_t content = parse_strings_empty_content(parser->previous.start);
+
+ if (quote == PM_HEREDOC_QUOTE_BACKTICK) {
+ node = (pm_node_t *) pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY);
+ } else {
+ node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY);
+ }
+
+ node->location.end = opening.end;
+ } else if ((part = parse_string_part(parser)) == NULL) {
+ // If we get here, then we tried to find something in the
+ // heredoc but couldn't actually parse anything, so we'll just
+ // return a missing node.
+ //
+ // parse_string_part handles its own errors, so there is no need
+ // for us to add one here.
+ node = (pm_node_t *) pm_missing_node_create(parser, parser->previous.start, parser->previous.end);
+ } else if (PM_NODE_TYPE_P(part, PM_STRING_NODE) && match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
+ // If we get here, then the part that we parsed was plain string
+ // content and we're at the end of the heredoc, so we can return
+ // just a string node with the heredoc opening and closing as
+ // its opening and closing.
+ pm_node_flag_set(part, parse_unescaped_encoding(parser));
+ pm_string_node_t *cast = (pm_string_node_t *) part;
+
+ cast->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening);
+ cast->closing_loc = PM_LOCATION_TOKEN_VALUE(&parser->current);
+ cast->base.location = cast->opening_loc;
+
+ if (quote == PM_HEREDOC_QUOTE_BACKTICK) {
+ assert(sizeof(pm_string_node_t) == sizeof(pm_x_string_node_t));
+ cast->base.type = PM_X_STRING_NODE;
+ }
+
+ size_t common_whitespace = lex_mode->as.heredoc.common_whitespace;
+ if (indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
+ parse_heredoc_dedent_string(&cast->unescaped, common_whitespace);
+ }
+
+ node = (pm_node_t *) cast;
+ lex_mode_pop(parser);
+ expect1(parser, PM_TOKEN_HEREDOC_END, PM_ERR_HEREDOC_TERM);
+ } else {
+ // If we get here, then we have multiple parts in the heredoc,
+ // so we'll need to create an interpolated string node to hold
+ // them all.
+ pm_node_list_t parts = { 0 };
+ pm_node_list_append(&parts, part);
+
+ while (!match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
+ if ((part = parse_string_part(parser)) != NULL) {
+ pm_node_list_append(&parts, part);
+ }
+ }
+
+ size_t common_whitespace = lex_mode->as.heredoc.common_whitespace;
+
+ // Now that we have all of the parts, create the correct type of
+ // interpolated node.
+ if (quote == PM_HEREDOC_QUOTE_BACKTICK) {
+ pm_interpolated_x_string_node_t *cast = pm_interpolated_xstring_node_create(parser, &opening, &opening);
+ cast->parts = parts;
+
+ lex_mode_pop(parser);
+ expect1(parser, PM_TOKEN_HEREDOC_END, PM_ERR_HEREDOC_TERM);
+
+ pm_interpolated_xstring_node_closing_set(cast, &parser->previous);
+ cast->base.location = cast->opening_loc;
+ node = (pm_node_t *) cast;
+ } else {
+ pm_interpolated_string_node_t *cast = pm_interpolated_string_node_create(parser, &opening, &parts, &opening);
+ pm_node_list_free(&parts);
+
+ lex_mode_pop(parser);
+ expect1(parser, PM_TOKEN_HEREDOC_END, PM_ERR_HEREDOC_TERM);
+
+ pm_interpolated_string_node_closing_set(cast, &parser->previous);
+ cast->base.location = cast->opening_loc;
+ node = (pm_node_t *) cast;
+ }
+
+ // If this is a heredoc that is indented with a ~, then we need
+ // to dedent each line by the common leading whitespace.
+ if (indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
+ pm_node_list_t *nodes;
+ if (quote == PM_HEREDOC_QUOTE_BACKTICK) {
+ nodes = &((pm_interpolated_x_string_node_t *) node)->parts;
+ } else {
+ nodes = &((pm_interpolated_string_node_t *) node)->parts;
+ }
+
+ parse_heredoc_dedent(parser, nodes, common_whitespace);
+ }
+ }
+
+ if (match1(parser, PM_TOKEN_STRING_BEGIN)) {
+ return parse_strings(parser, node);
+ }
+
+ return node;
+ }
+ case PM_TOKEN_INSTANCE_VARIABLE: {
+ parser_lex(parser);
+ pm_node_t *node = (pm_node_t *) pm_instance_variable_read_node_create(parser, &parser->previous);
+
+ if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
+ node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX);
+ }
+
+ return node;
+ }
+ case PM_TOKEN_INTEGER: {
+ pm_node_flags_t base = parser->integer_base;
+ parser_lex(parser);
+ return (pm_node_t *) pm_integer_node_create(parser, base, &parser->previous);
+ }
+ case PM_TOKEN_INTEGER_IMAGINARY: {
+ pm_node_flags_t base = parser->integer_base;
+ parser_lex(parser);
+ return (pm_node_t *) pm_integer_node_imaginary_create(parser, base, &parser->previous);
+ }
+ case PM_TOKEN_INTEGER_RATIONAL: {
+ pm_node_flags_t base = parser->integer_base;
+ parser_lex(parser);
+ return (pm_node_t *) pm_integer_node_rational_create(parser, base, &parser->previous);
+ }
+ case PM_TOKEN_INTEGER_RATIONAL_IMAGINARY: {
+ pm_node_flags_t base = parser->integer_base;
+ parser_lex(parser);
+ return (pm_node_t *) pm_integer_node_rational_imaginary_create(parser, base, &parser->previous);
+ }
+ case PM_TOKEN_KEYWORD___ENCODING__:
+ parser_lex(parser);
+ return (pm_node_t *) pm_source_encoding_node_create(parser, &parser->previous);
+ case PM_TOKEN_KEYWORD___FILE__:
+ parser_lex(parser);
+ return (pm_node_t *) pm_source_file_node_create(parser, &parser->previous);
+ case PM_TOKEN_KEYWORD___LINE__:
+ parser_lex(parser);
+ return (pm_node_t *) pm_source_line_node_create(parser, &parser->previous);
+ case PM_TOKEN_KEYWORD_ALIAS: {
+ if (binding_power != PM_BINDING_POWER_STATEMENT) {
+ pm_parser_err_current(parser, PM_ERR_STATEMENT_ALIAS);
+ }
+
+ parser_lex(parser);
+ pm_token_t keyword = parser->previous;
+
+ pm_node_t *new_name = parse_alias_argument(parser, true);
+ pm_node_t *old_name = parse_alias_argument(parser, false);
+
+ switch (PM_NODE_TYPE(new_name)) {
+ case PM_BACK_REFERENCE_READ_NODE:
+ case PM_NUMBERED_REFERENCE_READ_NODE:
+ case PM_GLOBAL_VARIABLE_READ_NODE: {
+ if (PM_NODE_TYPE_P(old_name, PM_BACK_REFERENCE_READ_NODE) || PM_NODE_TYPE_P(old_name, PM_NUMBERED_REFERENCE_READ_NODE) || PM_NODE_TYPE_P(old_name, PM_GLOBAL_VARIABLE_READ_NODE)) {
+ if (PM_NODE_TYPE_P(old_name, PM_NUMBERED_REFERENCE_READ_NODE)) {
+ pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT_NUMBERED_REFERENCE);
+ }
+ } else {
+ pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT);
+ }
+
+ return (pm_node_t *) pm_alias_global_variable_node_create(parser, &keyword, new_name, old_name);
+ }
+ case PM_SYMBOL_NODE:
+ case PM_INTERPOLATED_SYMBOL_NODE: {
+ if (!PM_NODE_TYPE_P(old_name, PM_SYMBOL_NODE) && !PM_NODE_TYPE_P(old_name, PM_INTERPOLATED_SYMBOL_NODE)) {
+ pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT);
+ }
+ }
+ /* fallthrough */
+ default:
+ return (pm_node_t *) pm_alias_method_node_create(parser, &keyword, new_name, old_name);
+ }
+ }
+ case PM_TOKEN_KEYWORD_CASE: {
+ parser_lex(parser);
+ pm_token_t case_keyword = parser->previous;
+ pm_node_t *predicate = NULL;
+
+ pm_node_list_t current_block_exits = { 0 };
+ pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
+
+ if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
+ while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
+ predicate = NULL;
+ } else if (match3(parser, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_KEYWORD_IN, PM_TOKEN_KEYWORD_END)) {
+ predicate = NULL;
+ } else if (!token_begins_expression_p(parser->current.type)) {
+ predicate = NULL;
+ } else {
+ predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, true, PM_ERR_CASE_EXPRESSION_AFTER_CASE);
+ while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
+ }
+
+ if (accept1(parser, PM_TOKEN_KEYWORD_END)) {
+ pop_block_exits(parser, previous_block_exits);
+ pm_node_list_free(&current_block_exits);
+
+ pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
+ return (pm_node_t *) pm_case_node_create(parser, &case_keyword, predicate, &parser->previous);
+ }
+
+ // At this point we can create a case node, though we don't yet know
+ // if it is a case-in or case-when node.
+ pm_token_t end_keyword = not_provided(parser);
+ pm_node_t *node;
+
+ if (match1(parser, PM_TOKEN_KEYWORD_WHEN)) {
+ pm_case_node_t *case_node = pm_case_node_create(parser, &case_keyword, predicate, &end_keyword);
+ pm_static_literals_t literals = { 0 };
+
+ // At this point we've seen a when keyword, so we know this is a
+ // case-when node. We will continue to parse the when nodes
+ // until we hit the end of the list.
+ while (accept1(parser, PM_TOKEN_KEYWORD_WHEN)) {
+ pm_token_t when_keyword = parser->previous;
+ pm_when_node_t *when_node = pm_when_node_create(parser, &when_keyword);
+
+ do {
+ if (accept1(parser, PM_TOKEN_USTAR)) {
+ pm_token_t operator = parser->previous;
+ pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR);
+
+ pm_splat_node_t *splat_node = pm_splat_node_create(parser, &operator, expression);
+ pm_when_node_conditions_append(when_node, (pm_node_t *) splat_node);
+
+ if (PM_NODE_TYPE_P(expression, PM_MISSING_NODE)) break;
+ } else {
+ pm_node_t *condition = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_CASE_EXPRESSION_AFTER_WHEN);
+ pm_when_node_conditions_append(when_node, condition);
+
+ // If we found a missing node, then this is a syntax
+ // error and we should stop looping.
+ if (PM_NODE_TYPE_P(condition, PM_MISSING_NODE)) break;
+
+ // If this is a string node, then we need to mark it
+ // as frozen because when clause strings are frozen.
+ if (PM_NODE_TYPE_P(condition, PM_STRING_NODE)) {
+ pm_node_flag_set(condition, PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL);
+ }
+
+ pm_when_clause_static_literals_add(parser, &literals, condition);
+ }
+ } while (accept1(parser, PM_TOKEN_COMMA));
+
+ if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
+ if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
+ pm_when_node_then_keyword_loc_set(when_node, &parser->previous);
+ }
+ } else {
+ expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_EXPECT_WHEN_DELIMITER);
+ pm_when_node_then_keyword_loc_set(when_node, &parser->previous);
+ }
+
+ if (!match3(parser, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
+ pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_CASE_WHEN);
+ if (statements != NULL) {
+ pm_when_node_statements_set(when_node, statements);
+ }
+ }
+
+ pm_case_node_condition_append(case_node, (pm_node_t *) when_node);
+ }
+
+ // If we didn't parse any conditions (in or when) then we need
+ // to indicate that we have an error.
+ if (case_node->conditions.size == 0) {
+ pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
+ }
+
+ pm_static_literals_free(&literals);
+ node = (pm_node_t *) case_node;
+ } else {
+ pm_case_match_node_t *case_node = pm_case_match_node_create(parser, &case_keyword, predicate, &end_keyword);
+
+ // If this is a case-match node (i.e., it is a pattern matching
+ // case statement) then we must have a predicate.
+ if (predicate == NULL) {
+ pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MATCH_MISSING_PREDICATE);
+ }
+
+ // At this point we expect that we're parsing a case-in node. We
+ // will continue to parse the in nodes until we hit the end of
+ // the list.
+ while (match1(parser, PM_TOKEN_KEYWORD_IN)) {
+ bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
+ parser->pattern_matching_newlines = true;
+
+ lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
+ parser->command_start = false;
+ parser_lex(parser);
+
+ pm_token_t in_keyword = parser->previous;
+
+ pm_constant_id_list_t captures = { 0 };
+ pm_node_t *pattern = parse_pattern(parser, &captures, true, PM_ERR_PATTERN_EXPRESSION_AFTER_IN);
+
+ parser->pattern_matching_newlines = previous_pattern_matching_newlines;
+ pm_constant_id_list_free(&captures);
+
+ // Since we're in the top-level of the case-in node we need
+ // to check for guard clauses in the form of `if` or
+ // `unless` statements.
+ if (accept1(parser, PM_TOKEN_KEYWORD_IF_MODIFIER)) {
+ pm_token_t keyword = parser->previous;
+ pm_node_t *predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, true, PM_ERR_CONDITIONAL_IF_PREDICATE);
+ pattern = (pm_node_t *) pm_if_node_modifier_create(parser, pattern, &keyword, predicate);
+ } else if (accept1(parser, PM_TOKEN_KEYWORD_UNLESS_MODIFIER)) {
+ pm_token_t keyword = parser->previous;
+ pm_node_t *predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, true, PM_ERR_CONDITIONAL_UNLESS_PREDICATE);
+ pattern = (pm_node_t *) pm_unless_node_modifier_create(parser, pattern, &keyword, predicate);
+ }
+
+ // Now we need to check for the terminator of the in node's
+ // pattern. It can be a newline or semicolon optionally
+ // followed by a `then` keyword.
+ pm_token_t then_keyword;
+ if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
+ if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
+ then_keyword = parser->previous;
+ } else {
+ then_keyword = not_provided(parser);
+ }
+ } else {
+ expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_EXPECT_WHEN_DELIMITER);
+ then_keyword = parser->previous;
+ }
+
+ // Now we can actually parse the statements associated with
+ // the in node.
+ pm_statements_node_t *statements;
+ if (match3(parser, PM_TOKEN_KEYWORD_IN, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
+ statements = NULL;
+ } else {
+ statements = parse_statements(parser, PM_CONTEXT_CASE_IN);
+ }
+
+ // Now that we have the full pattern and statements, we can
+ // create the node and attach it to the case node.
+ pm_node_t *condition = (pm_node_t *) pm_in_node_create(parser, pattern, statements, &in_keyword, &then_keyword);
+ pm_case_match_node_condition_append(case_node, condition);
+ }
+
+ // If we didn't parse any conditions (in or when) then we need
+ // to indicate that we have an error.
+ if (case_node->conditions.size == 0) {
+ pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
+ }
+
+ node = (pm_node_t *) case_node;
+ }
+
+ accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
+ if (accept1(parser, PM_TOKEN_KEYWORD_ELSE)) {
+ pm_token_t else_keyword = parser->previous;
+ pm_else_node_t *else_node;
+
+ if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
+ else_node = pm_else_node_create(parser, &else_keyword, parse_statements(parser, PM_CONTEXT_ELSE), &parser->current);
+ } else {
+ else_node = pm_else_node_create(parser, &else_keyword, NULL, &parser->current);
+ }
+
+ if (PM_NODE_TYPE_P(node, PM_CASE_NODE)) {
+ pm_case_node_consequent_set((pm_case_node_t *) node, else_node);
+ } else {
+ pm_case_match_node_consequent_set((pm_case_match_node_t *) node, else_node);
+ }
+ }
+
+ expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CASE_TERM);
+ if (PM_NODE_TYPE_P(node, PM_CASE_NODE)) {
+ pm_case_node_end_keyword_loc_set((pm_case_node_t *) node, &parser->previous);
+ } else {
+ pm_case_match_node_end_keyword_loc_set((pm_case_match_node_t *) node, &parser->previous);
+ }
+
+ pop_block_exits(parser, previous_block_exits);
+ pm_node_list_free(&current_block_exits);
+
+ return node;
+ }
+ case PM_TOKEN_KEYWORD_BEGIN: {
+ parser_lex(parser);
+
+ pm_token_t begin_keyword = parser->previous;
+ accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
+
+ pm_node_list_t current_block_exits = { 0 };
+ pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
+ pm_statements_node_t *begin_statements = NULL;
+
+ if (!match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_END)) {
+ pm_accepts_block_stack_push(parser, true);
+ begin_statements = parse_statements(parser, PM_CONTEXT_BEGIN);
+ pm_accepts_block_stack_pop(parser);
+ accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
+ }
+
+ pm_begin_node_t *begin_node = pm_begin_node_create(parser, &begin_keyword, begin_statements);
+ parse_rescues(parser, begin_node, PM_RESCUES_BEGIN);
+
+ expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BEGIN_TERM);
+ begin_node->base.location.end = parser->previous.end;
+ pm_begin_node_end_keyword_set(begin_node, &parser->previous);
+
+ if ((begin_node->else_clause != NULL) && (begin_node->rescue_clause == NULL)) {
+ pm_parser_err_node(parser, (pm_node_t *) begin_node->else_clause, PM_ERR_BEGIN_LONELY_ELSE);
+ }
+
+ pop_block_exits(parser, previous_block_exits);
+ pm_node_list_free(&current_block_exits);
+
+ return (pm_node_t *) begin_node;
+ }
+ case PM_TOKEN_KEYWORD_BEGIN_UPCASE: {
+ if (binding_power != PM_BINDING_POWER_STATEMENT) {
+ pm_parser_err_current(parser, PM_ERR_STATEMENT_PREEXE_BEGIN);
+ }
+
+ parser_lex(parser);
+ pm_token_t keyword = parser->previous;
+
+ expect1(parser, PM_TOKEN_BRACE_LEFT, PM_ERR_BEGIN_UPCASE_BRACE);
+ pm_token_t opening = parser->previous;
+ pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_PREEXE);
+
+ expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BEGIN_UPCASE_TERM);
+ pm_context_t context = parser->current_context->context;
+ if ((context != PM_CONTEXT_MAIN) && (context != PM_CONTEXT_PREEXE)) {
+ pm_parser_err_token(parser, &keyword, PM_ERR_BEGIN_UPCASE_TOPLEVEL);
+ }
+ return (pm_node_t *) pm_pre_execution_node_create(parser, &keyword, &opening, statements, &parser->previous);
+ }
+ case PM_TOKEN_KEYWORD_BREAK:
+ case PM_TOKEN_KEYWORD_NEXT:
+ case PM_TOKEN_KEYWORD_RETURN: {
+ parser_lex(parser);
+
+ pm_token_t keyword = parser->previous;
+ pm_arguments_t arguments = { 0 };
+
+ if (
+ token_begins_expression_p(parser->current.type) ||
+ match2(parser, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR)
+ ) {
+ pm_binding_power_t binding_power = pm_binding_powers[parser->current.type].left;
+
+ if (binding_power == PM_BINDING_POWER_UNSET || binding_power >= PM_BINDING_POWER_RANGE) {
+ parse_arguments(parser, &arguments, false, PM_TOKEN_EOF);
+ }
+ }
+
+ switch (keyword.type) {
+ case PM_TOKEN_KEYWORD_BREAK: {
+ pm_node_t *node = (pm_node_t *) pm_break_node_create(parser, &keyword, arguments.arguments);
+ if (!parser->parsing_eval) parse_block_exit(parser, node, "break");
+ return node;
+ }
+ case PM_TOKEN_KEYWORD_NEXT: {
+ pm_node_t *node = (pm_node_t *) pm_next_node_create(parser, &keyword, arguments.arguments);
+ if (!parser->parsing_eval) parse_block_exit(parser, node, "next");
+ return node;
+ }
+ case PM_TOKEN_KEYWORD_RETURN: {
+ if (
+ (parser->current_context->context == PM_CONTEXT_CLASS) ||
+ (parser->current_context->context == PM_CONTEXT_MODULE)
+ ) {
+ pm_parser_err_previous(parser, PM_ERR_RETURN_INVALID);
+ }
+ return (pm_node_t *) pm_return_node_create(parser, &keyword, arguments.arguments);
+ }
+ default:
+ assert(false && "unreachable");
+ return (pm_node_t *) pm_missing_node_create(parser, parser->previous.start, parser->previous.end);
+ }
+ }
+ case PM_TOKEN_KEYWORD_SUPER: {
+ parser_lex(parser);
+
+ pm_token_t keyword = parser->previous;
+ pm_arguments_t arguments = { 0 };
+ parse_arguments_list(parser, &arguments, true, accepts_command_call);
+
+ if (
+ arguments.opening_loc.start == NULL &&
+ arguments.arguments == NULL &&
+ ((arguments.block == NULL) || PM_NODE_TYPE_P(arguments.block, PM_BLOCK_NODE))
+ ) {
+ return (pm_node_t *) pm_forwarding_super_node_create(parser, &keyword, &arguments);
+ }
+
+ return (pm_node_t *) pm_super_node_create(parser, &keyword, &arguments);
+ }
+ case PM_TOKEN_KEYWORD_YIELD: {
+ parser_lex(parser);
+
+ pm_token_t keyword = parser->previous;
+ pm_arguments_t arguments = { 0 };
+ parse_arguments_list(parser, &arguments, false, accepts_command_call);
+
+ // It's possible that we've parsed a block argument through our
+ // call to parse_arguments_list. If we found one, we should mark it
+ // as invalid and destroy it, as we don't have a place for it on the
+ // yield node.
+ if (arguments.block != NULL) {
+ pm_parser_err_node(parser, arguments.block, PM_ERR_UNEXPECTED_BLOCK_ARGUMENT);
+ pm_node_destroy(parser, arguments.block);
+ arguments.block = NULL;
+ }
+
+ pm_node_t *node = (pm_node_t *) pm_yield_node_create(parser, &keyword, &arguments.opening_loc, arguments.arguments, &arguments.closing_loc);
+ if (!parser->parsing_eval) parse_yield(parser, node);
+
+ return node;
+ }
+ case PM_TOKEN_KEYWORD_CLASS: {
+ parser_lex(parser);
+ pm_token_t class_keyword = parser->previous;
+ pm_do_loop_stack_push(parser, false);
+
+ if (accept1(parser, PM_TOKEN_LESS_LESS)) {
+ pm_token_t operator = parser->previous;
+ pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_NOT, true, PM_ERR_EXPECT_EXPRESSION_AFTER_LESS_LESS);
+
+ pm_parser_scope_push(parser, true);
+ accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
+
+ pm_node_t *statements = NULL;
+ if (!match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_END)) {
+ pm_accepts_block_stack_push(parser, true);
+ statements = (pm_node_t *) parse_statements(parser, PM_CONTEXT_SCLASS);
+ pm_accepts_block_stack_pop(parser);
+ }
+
+ if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
+ assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
+ statements = (pm_node_t *) parse_rescues_implicit_begin(parser, class_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_SCLASS);
+ }
+
+ expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM);
+
+ pm_constant_id_list_t locals;
+ pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
+
+ pm_parser_scope_pop(parser);
+ pm_do_loop_stack_pop(parser);
+
+ return (pm_node_t *) pm_singleton_class_node_create(parser, &locals, &class_keyword, &operator, expression, statements, &parser->previous);
+ }
+
+ pm_node_list_t current_block_exits = { 0 };
+ pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
+
+ pm_node_t *constant_path = parse_expression(parser, PM_BINDING_POWER_INDEX, false, PM_ERR_CLASS_NAME);
+ pm_token_t name = parser->previous;
+ if (name.type != PM_TOKEN_CONSTANT) {
+ pm_parser_err_token(parser, &name, PM_ERR_CLASS_NAME);
+ }
+
+ pm_token_t inheritance_operator;
+ pm_node_t *superclass;
+
+ if (match1(parser, PM_TOKEN_LESS)) {
+ inheritance_operator = parser->current;
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+
+ parser->command_start = true;
+ parser_lex(parser);
+
+ superclass = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, true, PM_ERR_CLASS_SUPERCLASS);
+ } else {
+ inheritance_operator = not_provided(parser);
+ superclass = NULL;
+ }
+
+ pm_parser_scope_push(parser, true);
+
+ if (inheritance_operator.type != PM_TOKEN_NOT_PROVIDED) {
+ expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CLASS_UNEXPECTED_END);
+ } else {
+ accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
+ }
+ pm_node_t *statements = NULL;
+
+ if (!match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_END)) {
+ pm_accepts_block_stack_push(parser, true);
+ statements = (pm_node_t *) parse_statements(parser, PM_CONTEXT_CLASS);
+ pm_accepts_block_stack_pop(parser);
+ }
+
+ if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
+ assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
+ statements = (pm_node_t *) parse_rescues_implicit_begin(parser, class_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_CLASS);
+ }
+
+ expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM);
+
+ if (context_def_p(parser)) {
+ pm_parser_err_token(parser, &class_keyword, PM_ERR_CLASS_IN_METHOD);
+ }
+
+ pm_constant_id_list_t locals;
+ pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
+
+ pm_parser_scope_pop(parser);
+ pm_do_loop_stack_pop(parser);
+
+ if (!PM_NODE_TYPE_P(constant_path, PM_CONSTANT_PATH_NODE) && !(PM_NODE_TYPE_P(constant_path, PM_CONSTANT_READ_NODE))) {
+ pm_parser_err_node(parser, constant_path, PM_ERR_CLASS_NAME);
+ }
+
+ pop_block_exits(parser, previous_block_exits);
+ pm_node_list_free(&current_block_exits);
+
+ return (pm_node_t *) pm_class_node_create(parser, &locals, &class_keyword, constant_path, &name, &inheritance_operator, superclass, statements, &parser->previous);
+ }
+ case PM_TOKEN_KEYWORD_DEF: {
+ pm_token_t def_keyword = parser->current;
+
+ pm_node_t *receiver = NULL;
+ pm_token_t operator = not_provided(parser);
+ pm_token_t name;
+
+ // This context is necessary for lexing `...` in a bare params
+ // correctly. It must be pushed before lexing the first param, so it
+ // is here.
+ context_push(parser, PM_CONTEXT_DEF_PARAMS);
+ parser_lex(parser);
+
+ switch (parser->current.type) {
+ case PM_CASE_OPERATOR:
+ pm_parser_scope_push(parser, true);
+ lex_state_set(parser, PM_LEX_STATE_ENDFN);
+ parser_lex(parser);
+
+ name = parser->previous;
+ break;
+ case PM_TOKEN_IDENTIFIER: {
+ parser_lex(parser);
+
+ if (match2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON)) {
+ receiver = parse_variable_call(parser);
+ receiver = pm_node_check_it(parser, receiver);
+
+ pm_parser_scope_push(parser, true);
+ lex_state_set(parser, PM_LEX_STATE_FNAME);
+ parser_lex(parser);
+
+ operator = parser->previous;
+ name = parse_method_definition_name(parser);
+ } else {
+ pm_refute_numbered_parameter(parser, parser->previous.start, parser->previous.end);
+ pm_parser_scope_push(parser, true);
+
+ name = parser->previous;
+ }
+
+ break;
+ }
+ case PM_TOKEN_CONSTANT:
+ case PM_TOKEN_INSTANCE_VARIABLE:
+ case PM_TOKEN_CLASS_VARIABLE:
+ case PM_TOKEN_GLOBAL_VARIABLE:
+ case PM_TOKEN_KEYWORD_NIL:
+ case PM_TOKEN_KEYWORD_SELF:
+ case PM_TOKEN_KEYWORD_TRUE:
+ case PM_TOKEN_KEYWORD_FALSE:
+ case PM_TOKEN_KEYWORD___FILE__:
+ case PM_TOKEN_KEYWORD___LINE__:
+ case PM_TOKEN_KEYWORD___ENCODING__: {
+ pm_parser_scope_push(parser, true);
+ parser_lex(parser);
+
+ pm_token_t identifier = parser->previous;
+
+ if (match2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON)) {
+ lex_state_set(parser, PM_LEX_STATE_FNAME);
+ parser_lex(parser);
+ operator = parser->previous;
+
+ switch (identifier.type) {
+ case PM_TOKEN_CONSTANT:
+ receiver = (pm_node_t *) pm_constant_read_node_create(parser, &identifier);
+ break;
+ case PM_TOKEN_INSTANCE_VARIABLE:
+ receiver = (pm_node_t *) pm_instance_variable_read_node_create(parser, &identifier);
+ break;
+ case PM_TOKEN_CLASS_VARIABLE:
+ receiver = (pm_node_t *) pm_class_variable_read_node_create(parser, &identifier);
+ break;
+ case PM_TOKEN_GLOBAL_VARIABLE:
+ receiver = (pm_node_t *) pm_global_variable_read_node_create(parser, &identifier);
+ break;
+ case PM_TOKEN_KEYWORD_NIL:
+ receiver = (pm_node_t *) pm_nil_node_create(parser, &identifier);
+ break;
+ case PM_TOKEN_KEYWORD_SELF:
+ receiver = (pm_node_t *) pm_self_node_create(parser, &identifier);
+ break;
+ case PM_TOKEN_KEYWORD_TRUE:
+ receiver = (pm_node_t *) pm_true_node_create(parser, &identifier);
+ break;
+ case PM_TOKEN_KEYWORD_FALSE:
+ receiver = (pm_node_t *) pm_false_node_create(parser, &identifier);
+ break;
+ case PM_TOKEN_KEYWORD___FILE__:
+ receiver = (pm_node_t *) pm_source_file_node_create(parser, &identifier);
+ break;
+ case PM_TOKEN_KEYWORD___LINE__:
+ receiver = (pm_node_t *) pm_source_line_node_create(parser, &identifier);
+ break;
+ case PM_TOKEN_KEYWORD___ENCODING__:
+ receiver = (pm_node_t *) pm_source_encoding_node_create(parser, &identifier);
+ break;
+ default:
+ break;
+ }
+
+ name = parse_method_definition_name(parser);
+ } else {
+ name = identifier;
+ }
+ break;
+ }
+ case PM_TOKEN_PARENTHESIS_LEFT: {
+ // The current context is `PM_CONTEXT_DEF_PARAMS`, however
+ // the inner expression of this parenthesis should not be
+ // processed under this context. Thus, the context is popped
+ // here.
+ context_pop(parser);
+ parser_lex(parser);
+
+ pm_token_t lparen = parser->previous;
+ pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_STATEMENT, true, PM_ERR_DEF_RECEIVER);
+
+ accept1(parser, PM_TOKEN_NEWLINE);
+ expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
+ pm_token_t rparen = parser->previous;
+
+ lex_state_set(parser, PM_LEX_STATE_FNAME);
+ expect2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON, PM_ERR_DEF_RECEIVER_TERM);
+
+ operator = parser->previous;
+ receiver = (pm_node_t *) pm_parentheses_node_create(parser, &lparen, expression, &rparen);
+
+ // To push `PM_CONTEXT_DEF_PARAMS` again is for the same
+ // reason as described the above.
+ pm_parser_scope_push(parser, true);
+ context_push(parser, PM_CONTEXT_DEF_PARAMS);
+ name = parse_method_definition_name(parser);
+ break;
+ }
+ default:
+ pm_parser_scope_push(parser, true);
+ name = parse_method_definition_name(parser);
+ break;
+ }
+
+ pm_token_t lparen;
+ pm_token_t rparen;
+ pm_parameters_node_t *params;
+
+ switch (parser->current.type) {
+ case PM_TOKEN_PARENTHESIS_LEFT: {
+ parser_lex(parser);
+ lparen = parser->previous;
+
+ if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
+ params = NULL;
+ } else {
+ params = parse_parameters(parser, PM_BINDING_POWER_DEFINED, true, false, true);
+ }
+
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ parser->command_start = true;
+
+ expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_DEF_PARAMS_TERM_PAREN);
+ rparen = parser->previous;
+ break;
+ }
+ case PM_CASE_PARAMETER: {
+ // If we're about to lex a label, we need to add the label
+ // state to make sure the next newline is ignored.
+ if (parser->current.type == PM_TOKEN_LABEL) {
+ lex_state_set(parser, parser->lex_state | PM_LEX_STATE_LABEL);
+ }
+
+ lparen = not_provided(parser);
+ rparen = not_provided(parser);
+ params = parse_parameters(parser, PM_BINDING_POWER_DEFINED, false, false, true);
+ break;
+ }
+ default: {
+ lparen = not_provided(parser);
+ rparen = not_provided(parser);
+ params = NULL;
+ break;
+ }
+ }
+
+ context_pop(parser);
+ pm_node_t *statements = NULL;
+ pm_token_t equal;
+ pm_token_t end_keyword;
+
+ if (accept1(parser, PM_TOKEN_EQUAL)) {
+ if (token_is_setter_name(&name)) {
+ pm_parser_err_token(parser, &name, PM_ERR_DEF_ENDLESS_SETTER);
+ }
+ equal = parser->previous;
+
+ context_push(parser, PM_CONTEXT_DEF);
+ pm_do_loop_stack_push(parser, false);
+ statements = (pm_node_t *) pm_statements_node_create(parser);
+
+ pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_DEFINED + 1, binding_power < PM_BINDING_POWER_COMPOSITION, PM_ERR_DEF_ENDLESS);
+
+ if (accept1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
+ context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
+
+ pm_token_t rescue_keyword = parser->previous;
+ pm_node_t *value = parse_expression(parser, binding_power, false, PM_ERR_RESCUE_MODIFIER_VALUE);
+ context_pop(parser);
+
+ statement = (pm_node_t *) pm_rescue_modifier_node_create(parser, statement, &rescue_keyword, value);
+ }
+
+ pm_statements_node_body_append(parser, (pm_statements_node_t *) statements, statement);
+ pm_do_loop_stack_pop(parser);
+ context_pop(parser);
+ end_keyword = not_provided(parser);
+ } else {
+ equal = not_provided(parser);
+
+ if (lparen.type == PM_TOKEN_NOT_PROVIDED) {
+ lex_state_set(parser, PM_LEX_STATE_BEG);
+ parser->command_start = true;
+ expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_DEF_PARAMS_TERM);
+ } else {
+ accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
+ }
+
+ pm_accepts_block_stack_push(parser, true);
+ pm_do_loop_stack_push(parser, false);
+
+ if (!match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_END)) {
+ pm_accepts_block_stack_push(parser, true);
+ statements = (pm_node_t *) parse_statements(parser, PM_CONTEXT_DEF);
+ pm_accepts_block_stack_pop(parser);
+ }
+
+ if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
+ assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
+ statements = (pm_node_t *) parse_rescues_implicit_begin(parser, def_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_DEF);
+ }
+
+ pm_accepts_block_stack_pop(parser);
+ pm_do_loop_stack_pop(parser);
+ expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_DEF_TERM);
+ end_keyword = parser->previous;
+ }
+
+ pm_constant_id_list_t locals;
+ pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
+ pm_parser_scope_pop(parser);
+
+ /**
+ * If the final character is @. As is the case when defining
+ * methods to override the unary operators, we should ignore
+ * the @ in the same way we do for symbols.
+ */
+ pm_constant_id_t name_id = pm_parser_constant_id_location(parser, name.start, parse_operator_symbol_name(&name));
+
+ return (pm_node_t *) pm_def_node_create(
+ parser,
+ name_id,
+ &name,
+ receiver,
+ params,
+ statements,
+ &locals,
+ &def_keyword,
+ &operator,
+ &lparen,
+ &rparen,
+ &equal,
+ &end_keyword
+ );
+ }
+ case PM_TOKEN_KEYWORD_DEFINED: {
+ parser_lex(parser);
+ pm_token_t keyword = parser->previous;
+
+ pm_token_t lparen;
+ pm_token_t rparen;
+ pm_node_t *expression;
+ context_push(parser, PM_CONTEXT_DEFINED);
+
+ if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
+ lparen = parser->previous;
+ expression = parse_expression(parser, PM_BINDING_POWER_COMPOSITION, true, PM_ERR_DEFINED_EXPRESSION);
+
+ if (parser->recovering) {
+ rparen = not_provided(parser);
+ } else {
+ accept1(parser, PM_TOKEN_NEWLINE);
+ expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
+ rparen = parser->previous;
+ }
+ } else {
+ lparen = not_provided(parser);
+ rparen = not_provided(parser);
+ expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_DEFINED_EXPRESSION);
+ }
+
+ context_pop(parser);
+ return (pm_node_t *) pm_defined_node_create(
+ parser,
+ &lparen,
+ expression,
+ &rparen,
+ &PM_LOCATION_TOKEN_VALUE(&keyword)
+ );
+ }
+ case PM_TOKEN_KEYWORD_END_UPCASE: {
+ if (binding_power != PM_BINDING_POWER_STATEMENT) {
+ pm_parser_err_current(parser, PM_ERR_STATEMENT_POSTEXE_END);
+ }
+
+ parser_lex(parser);
+ pm_token_t keyword = parser->previous;
+
+ if (context_def_p(parser)) {
+ pm_parser_warn_token(parser, &keyword, PM_WARN_END_IN_METHOD);
+ }
+
+ expect1(parser, PM_TOKEN_BRACE_LEFT, PM_ERR_END_UPCASE_BRACE);
+ pm_token_t opening = parser->previous;
+ pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_POSTEXE);
+
+ expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_END_UPCASE_TERM);
+ return (pm_node_t *) pm_post_execution_node_create(parser, &keyword, &opening, statements, &parser->previous);
+ }
+ case PM_TOKEN_KEYWORD_FALSE:
+ parser_lex(parser);
+ return (pm_node_t *)pm_false_node_create(parser, &parser->previous);
+ case PM_TOKEN_KEYWORD_FOR: {
+ parser_lex(parser);
+ pm_token_t for_keyword = parser->previous;
+ pm_node_t *index;
+
+ context_push(parser, PM_CONTEXT_FOR_INDEX);
+
+ // First, parse out the first index expression.
+ if (accept1(parser, PM_TOKEN_USTAR)) {
+ pm_token_t star_operator = parser->previous;
+ pm_node_t *name = NULL;
+
+ if (token_begins_expression_p(parser->current.type)) {
+ name = parse_expression(parser, PM_BINDING_POWER_INDEX, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR);
+ }
+
+ index = (pm_node_t *) pm_splat_node_create(parser, &star_operator, name);
+ } else if (token_begins_expression_p(parser->current.type)) {
+ index = parse_expression(parser, PM_BINDING_POWER_INDEX, false, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA);
+ } else {
+ pm_parser_err_token(parser, &for_keyword, PM_ERR_FOR_INDEX);
+ index = (pm_node_t *) pm_missing_node_create(parser, for_keyword.start, for_keyword.end);
+ }
+
+ // Now, if there are multiple index expressions, parse them out.
+ if (match1(parser, PM_TOKEN_COMMA)) {
+ index = parse_targets(parser, index, PM_BINDING_POWER_INDEX);
+ } else {
+ index = parse_target(parser, index);
+ }
+
+ context_pop(parser);
+ pm_do_loop_stack_push(parser, true);
+
+ expect1(parser, PM_TOKEN_KEYWORD_IN, PM_ERR_FOR_IN);
+ pm_token_t in_keyword = parser->previous;
+
+ pm_node_t *collection = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, true, PM_ERR_FOR_COLLECTION);
+ pm_do_loop_stack_pop(parser);
+
+ pm_token_t do_keyword;
+ if (accept1(parser, PM_TOKEN_KEYWORD_DO_LOOP)) {
+ do_keyword = parser->previous;
+ } else {
+ do_keyword = not_provided(parser);
+ }
+
+ accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE);
+ pm_statements_node_t *statements = NULL;
+
+ if (!accept1(parser, PM_TOKEN_KEYWORD_END)) {
+ statements = parse_statements(parser, PM_CONTEXT_FOR);
+ expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_FOR_TERM);
+ }
+
+ return (pm_node_t *) pm_for_node_create(parser, index, collection, statements, &for_keyword, &in_keyword, &do_keyword, &parser->previous);
+ }
+ case PM_TOKEN_KEYWORD_IF:
+ if (parser_end_of_line_p(parser)) {
+ PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, parser->current, PM_WARN_KEYWORD_EOL);
+ }
+
+ parser_lex(parser);
+ return parse_conditional(parser, PM_CONTEXT_IF);
+ case PM_TOKEN_KEYWORD_UNDEF: {
+ if (binding_power != PM_BINDING_POWER_STATEMENT) {
+ pm_parser_err_current(parser, PM_ERR_STATEMENT_UNDEF);
+ }
+
+ parser_lex(parser);
+ pm_undef_node_t *undef = pm_undef_node_create(parser, &parser->previous);
+ pm_node_t *name = parse_undef_argument(parser);
+
+ if (PM_NODE_TYPE_P(name, PM_MISSING_NODE)) {
+ pm_node_destroy(parser, name);
+ } else {
+ pm_undef_node_append(undef, name);
+
+ while (match1(parser, PM_TOKEN_COMMA)) {
+ lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM);
+ parser_lex(parser);
+ name = parse_undef_argument(parser);
+
+ if (PM_NODE_TYPE_P(name, PM_MISSING_NODE)) {
+ pm_node_destroy(parser, name);
+ break;
+ }
+
+ pm_undef_node_append(undef, name);
+ }
+ }
+
+ return (pm_node_t *) undef;
+ }
+ case PM_TOKEN_KEYWORD_NOT: {
+ parser_lex(parser);
+
+ pm_token_t message = parser->previous;
+ pm_arguments_t arguments = { 0 };
+ pm_node_t *receiver = NULL;
+
+ accept1(parser, PM_TOKEN_NEWLINE);
+
+ if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
+ arguments.opening_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous);
+
+ if (accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
+ arguments.closing_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous);
+ } else {
+ receiver = parse_expression(parser, PM_BINDING_POWER_COMPOSITION, true, PM_ERR_NOT_EXPRESSION);
+
+ if (!parser->recovering) {
+ accept1(parser, PM_TOKEN_NEWLINE);
+ expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
+ arguments.closing_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous);
+ }
+ }
+ } else {
+ receiver = parse_expression(parser, PM_BINDING_POWER_NOT, true, PM_ERR_NOT_EXPRESSION);
+ }
+
+ return (pm_node_t *) pm_call_node_not_create(parser, receiver, &message, &arguments);
+ }
+ case PM_TOKEN_KEYWORD_UNLESS:
+ parser_lex(parser);
+ return parse_conditional(parser, PM_CONTEXT_UNLESS);
+ case PM_TOKEN_KEYWORD_MODULE: {
+ pm_node_list_t current_block_exits = { 0 };
+ pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
+
+ parser_lex(parser);
+ pm_token_t module_keyword = parser->previous;
+
+ pm_node_t *constant_path = parse_expression(parser, PM_BINDING_POWER_INDEX, false, PM_ERR_MODULE_NAME);
+ pm_token_t name;
+
+ // If we can recover from a syntax error that occurred while parsing
+ // the name of the module, then we'll handle that here.
+ if (PM_NODE_TYPE_P(constant_path, PM_MISSING_NODE)) {
+ pop_block_exits(parser, previous_block_exits);
+ pm_node_list_free(&current_block_exits);
+
+ pm_token_t missing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
+ return (pm_node_t *) pm_module_node_create(parser, NULL, &module_keyword, constant_path, &missing, NULL, &missing);
+ }
+
+ while (accept1(parser, PM_TOKEN_COLON_COLON)) {
+ pm_token_t double_colon = parser->previous;
+
+ expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
+ pm_node_t *constant = (pm_node_t *) pm_constant_read_node_create(parser, &parser->previous);
+
+ constant_path = (pm_node_t *) pm_constant_path_node_create(parser, constant_path, &double_colon, constant);
+ }
+
+ // Here we retrieve the name of the module. If it wasn't a constant,
+ // then it's possible that `module foo` was passed, which is a
+ // syntax error. We handle that here as well.
+ name = parser->previous;
+ if (name.type != PM_TOKEN_CONSTANT) {
+ pm_parser_err_token(parser, &name, PM_ERR_MODULE_NAME);
+ }
+
+ pm_parser_scope_push(parser, true);
+ accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE);
+ pm_node_t *statements = NULL;
+
+ if (!match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_END)) {
+ pm_accepts_block_stack_push(parser, true);
+ statements = (pm_node_t *) parse_statements(parser, PM_CONTEXT_MODULE);
+ pm_accepts_block_stack_pop(parser);
+ }
+
+ if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
+ assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
+ statements = (pm_node_t *) parse_rescues_implicit_begin(parser, module_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_MODULE);
+ }
+
+ pm_constant_id_list_t locals;
+ pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
+
+ pm_parser_scope_pop(parser);
+ expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_MODULE_TERM);
+
+ if (context_def_p(parser)) {
+ pm_parser_err_token(parser, &module_keyword, PM_ERR_MODULE_IN_METHOD);
+ }
+
+ pop_block_exits(parser, previous_block_exits);
+ pm_node_list_free(&current_block_exits);
+
+ return (pm_node_t *) pm_module_node_create(parser, &locals, &module_keyword, constant_path, &name, statements, &parser->previous);
+ }
+ case PM_TOKEN_KEYWORD_NIL:
+ parser_lex(parser);
+ return (pm_node_t *) pm_nil_node_create(parser, &parser->previous);
+ case PM_TOKEN_KEYWORD_REDO: {
+ parser_lex(parser);
+
+ pm_node_t *node = (pm_node_t *) pm_redo_node_create(parser, &parser->previous);
+ if (!parser->parsing_eval) parse_block_exit(parser, node, "redo");
+
+ return node;
+ }
+ case PM_TOKEN_KEYWORD_RETRY: {
+ parser_lex(parser);
+
+ pm_node_t *node = (pm_node_t *) pm_retry_node_create(parser, &parser->previous);
+ parse_retry(parser, node);
+
+ return node;
+ }
+ case PM_TOKEN_KEYWORD_SELF:
+ parser_lex(parser);
+ return (pm_node_t *) pm_self_node_create(parser, &parser->previous);
+ case PM_TOKEN_KEYWORD_TRUE:
+ parser_lex(parser);
+ return (pm_node_t *) pm_true_node_create(parser, &parser->previous);
+ case PM_TOKEN_KEYWORD_UNTIL: {
+ pm_do_loop_stack_push(parser, true);
+ parser_lex(parser);
+ pm_token_t keyword = parser->previous;
+
+ pm_node_t *predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, true, PM_ERR_CONDITIONAL_UNTIL_PREDICATE);
+ pm_do_loop_stack_pop(parser);
+
+ expect3(parser, PM_TOKEN_KEYWORD_DO_LOOP, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CONDITIONAL_UNTIL_PREDICATE);
+ pm_statements_node_t *statements = NULL;
+
+ if (!accept1(parser, PM_TOKEN_KEYWORD_END)) {
+ pm_accepts_block_stack_push(parser, true);
+ statements = parse_statements(parser, PM_CONTEXT_UNTIL);
+ pm_accepts_block_stack_pop(parser);
+ accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
+ expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_UNTIL_TERM);
+ }
+
+ return (pm_node_t *) pm_until_node_create(parser, &keyword, &parser->previous, predicate, statements, 0);
+ }
+ case PM_TOKEN_KEYWORD_WHILE: {
+ pm_do_loop_stack_push(parser, true);
+ parser_lex(parser);
+ pm_token_t keyword = parser->previous;
+
+ pm_node_t *predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, true, PM_ERR_CONDITIONAL_WHILE_PREDICATE);
+ pm_do_loop_stack_pop(parser);
+
+ expect3(parser, PM_TOKEN_KEYWORD_DO_LOOP, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CONDITIONAL_WHILE_PREDICATE);
+ pm_statements_node_t *statements = NULL;
+
+ if (!accept1(parser, PM_TOKEN_KEYWORD_END)) {
+ pm_accepts_block_stack_push(parser, true);
+ statements = parse_statements(parser, PM_CONTEXT_WHILE);
+ pm_accepts_block_stack_pop(parser);
+ accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
+ expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_WHILE_TERM);
+ }
+
+ return (pm_node_t *) pm_while_node_create(parser, &keyword, &parser->previous, predicate, statements, 0);
+ }
+ case PM_TOKEN_PERCENT_LOWER_I: {
+ parser_lex(parser);
+ pm_token_t opening = parser->previous;
+ pm_array_node_t *array = pm_array_node_create(parser, &opening);
+
+ while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
+ accept1(parser, PM_TOKEN_WORDS_SEP);
+ if (match1(parser, PM_TOKEN_STRING_END)) break;
+
+ if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
+ pm_token_t opening = not_provided(parser);
+ pm_token_t closing = not_provided(parser);
+ pm_array_node_elements_append(array, (pm_node_t *) pm_symbol_node_create_current_string(parser, &opening, &parser->current, &closing));
+ }
+
+ expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_LOWER_ELEMENT);
+ }
+
+ pm_token_t closing = parser->current;
+ if (match1(parser, PM_TOKEN_EOF)) {
+ pm_parser_err_token(parser, &opening, PM_ERR_LIST_I_LOWER_TERM);
+ closing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
+ } else {
+ expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_I_LOWER_TERM);
+ }
+ pm_array_node_close_set(array, &closing);
+
+ return (pm_node_t *) array;
+ }
+ case PM_TOKEN_PERCENT_UPPER_I: {
+ parser_lex(parser);
+ pm_token_t opening = parser->previous;
+ pm_array_node_t *array = pm_array_node_create(parser, &opening);
+
+ // This is the current node that we are parsing that will be added to the
+ // list of elements.
+ pm_node_t *current = NULL;
+
+ while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
+ switch (parser->current.type) {
+ case PM_TOKEN_WORDS_SEP: {
+ if (current == NULL) {
+ // If we hit a separator before we have any content, then we don't
+ // need to do anything.
+ } else {
+ // If we hit a separator after we've hit content, then we need to
+ // append that content to the list and reset the current node.
+ pm_array_node_elements_append(array, current);
+ current = NULL;
+ }
+
+ parser_lex(parser);
+ break;
+ }
+ case PM_TOKEN_STRING_CONTENT: {
+ pm_token_t opening = not_provided(parser);
+ pm_token_t closing = not_provided(parser);
+
+ if (current == NULL) {
+ // If we hit content and the current node is NULL, then this is
+ // the first string content we've seen. In that case we're going
+ // to create a new string node and set that to the current.
+ current = (pm_node_t *) pm_symbol_node_create_current_string(parser, &opening, &parser->current, &closing);
+ parser_lex(parser);
+ } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) {
+ // If we hit string content and the current node is an
+ // interpolated string, then we need to append the string content
+ // to the list of child nodes.
+ pm_node_t *string = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &parser->current, &closing);
+ parser_lex(parser);
+
+ pm_interpolated_symbol_node_append((pm_interpolated_symbol_node_t *) current, string);
+ } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
+ // If we hit string content and the current node is a symbol node,
+ // then we need to convert the current node into an interpolated
+ // string and add the string content to the list of child nodes.
+ pm_symbol_node_t *cast = (pm_symbol_node_t *) current;
+ pm_token_t bounds = not_provided(parser);
+
+ pm_token_t content = { .type = PM_TOKEN_STRING_CONTENT, .start = cast->value_loc.start, .end = cast->value_loc.end };
+ pm_node_t *first_string = (pm_node_t *) pm_string_node_create_unescaped(parser, &bounds, &content, &bounds, &cast->unescaped);
+ pm_node_t *second_string = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &parser->previous, &closing);
+ parser_lex(parser);
+
+ pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing);
+ pm_interpolated_symbol_node_append(interpolated, first_string);
+ pm_interpolated_symbol_node_append(interpolated, second_string);
+
+ xfree(current);
+ current = (pm_node_t *) interpolated;
+ } else {
+ assert(false && "unreachable");
+ }
+
+ break;
+ }
+ case PM_TOKEN_EMBVAR: {
+ bool start_location_set = false;
+ if (current == NULL) {
+ // If we hit an embedded variable and the current node is NULL,
+ // then this is the start of a new string. We'll set the current
+ // node to a new interpolated string.
+ pm_token_t opening = not_provided(parser);
+ pm_token_t closing = not_provided(parser);
+ current = (pm_node_t *) pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing);
+ } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
+ // If we hit an embedded variable and the current node is a string
+ // node, then we'll convert the current into an interpolated
+ // string and add the string node to the list of parts.
+ pm_token_t opening = not_provided(parser);
+ pm_token_t closing = not_provided(parser);
+ pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing);
+
+ current = (pm_node_t *) pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current);
+ pm_interpolated_symbol_node_append(interpolated, current);
+ interpolated->base.location.start = current->location.start;
+ start_location_set = true;
+ current = (pm_node_t *) interpolated;
+ } else {
+ // If we hit an embedded variable and the current node is an
+ // interpolated string, then we'll just add the embedded variable.
+ }
+
+ pm_node_t *part = parse_string_part(parser);
+ pm_interpolated_symbol_node_append((pm_interpolated_symbol_node_t *) current, part);
+ if (!start_location_set) {
+ current->location.start = part->location.start;
+ }
+ break;
+ }
+ case PM_TOKEN_EMBEXPR_BEGIN: {
+ bool start_location_set = false;
+ if (current == NULL) {
+ // If we hit an embedded expression and the current node is NULL,
+ // then this is the start of a new string. We'll set the current
+ // node to a new interpolated string.
+ pm_token_t opening = not_provided(parser);
+ pm_token_t closing = not_provided(parser);
+ current = (pm_node_t *) pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing);
+ } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
+ // If we hit an embedded expression and the current node is a
+ // string node, then we'll convert the current into an
+ // interpolated string and add the string node to the list of
+ // parts.
+ pm_token_t opening = not_provided(parser);
+ pm_token_t closing = not_provided(parser);
+ pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing);
+
+ current = (pm_node_t *) pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current);
+ pm_interpolated_symbol_node_append(interpolated, current);
+ interpolated->base.location.start = current->location.start;
+ start_location_set = true;
+ current = (pm_node_t *) interpolated;
+ } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) {
+ // If we hit an embedded expression and the current node is an
+ // interpolated string, then we'll just continue on.
+ } else {
+ assert(false && "unreachable");
+ }
+
+ pm_node_t *part = parse_string_part(parser);
+ pm_interpolated_symbol_node_append((pm_interpolated_symbol_node_t *) current, part);
+ if (!start_location_set) {
+ current->location.start = part->location.start;
+ }
+ break;
+ }
+ default:
+ expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_UPPER_ELEMENT);
+ parser_lex(parser);
+ break;
+ }
+ }
+
+ // If we have a current node, then we need to append it to the list.
+ if (current) {
+ pm_array_node_elements_append(array, current);
+ }
+
+ pm_token_t closing = parser->current;
+ if (match1(parser, PM_TOKEN_EOF)) {
+ pm_parser_err_token(parser, &opening, PM_ERR_LIST_I_UPPER_TERM);
+ closing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
+ } else {
+ expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_I_UPPER_TERM);
+ }
+ pm_array_node_close_set(array, &closing);
+
+ return (pm_node_t *) array;
+ }
+ case PM_TOKEN_PERCENT_LOWER_W: {
+ parser_lex(parser);
+ pm_token_t opening = parser->previous;
+ pm_array_node_t *array = pm_array_node_create(parser, &opening);
+
+ // skip all leading whitespaces
+ accept1(parser, PM_TOKEN_WORDS_SEP);
+
+ while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
+ accept1(parser, PM_TOKEN_WORDS_SEP);
+ if (match1(parser, PM_TOKEN_STRING_END)) break;
+
+ if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
+ pm_token_t opening = not_provided(parser);
+ pm_token_t closing = not_provided(parser);
+
+ pm_node_t *string = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &parser->current, &closing);
+ pm_array_node_elements_append(array, string);
+ }
+
+ expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_LOWER_ELEMENT);
+ }
+
+ pm_token_t closing = parser->current;
+ if (match1(parser, PM_TOKEN_EOF)) {
+ pm_parser_err_token(parser, &opening, PM_ERR_LIST_W_LOWER_TERM);
+ closing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
+ } else {
+ expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_W_LOWER_TERM);
+ }
+
+ pm_array_node_close_set(array, &closing);
+ return (pm_node_t *) array;
+ }
+ case PM_TOKEN_PERCENT_UPPER_W: {
+ parser_lex(parser);
+ pm_token_t opening = parser->previous;
+ pm_array_node_t *array = pm_array_node_create(parser, &opening);
+
+ // This is the current node that we are parsing that will be added
+ // to the list of elements.
+ pm_node_t *current = NULL;
+
+ while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
+ switch (parser->current.type) {
+ case PM_TOKEN_WORDS_SEP: {
+ // Reset the explicit encoding if we hit a separator
+ // since each element can have its own encoding.
+ parser->explicit_encoding = NULL;
+
+ if (current == NULL) {
+ // If we hit a separator before we have any content,
+ // then we don't need to do anything.
+ } else {
+ // If we hit a separator after we've hit content,
+ // then we need to append that content to the list
+ // and reset the current node.
+ pm_array_node_elements_append(array, current);
+ current = NULL;
+ }
+
+ parser_lex(parser);
+ break;
+ }
+ case PM_TOKEN_STRING_CONTENT: {
+ pm_token_t opening = not_provided(parser);
+ pm_token_t closing = not_provided(parser);
+
+ pm_node_t *string = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &parser->current, &closing);
+ pm_node_flag_set(string, parse_unescaped_encoding(parser));
+ parser_lex(parser);
+
+ if (current == NULL) {
+ // If we hit content and the current node is NULL,
+ // then this is the first string content we've seen.
+ // In that case we're going to create a new string
+ // node and set that to the current.
+ current = string;
+ } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
+ // If we hit string content and the current node is
+ // an interpolated string, then we need to append
+ // the string content to the list of child nodes.
+ pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, string);
+ } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
+ // If we hit string content and the current node is
+ // a string node, then we need to convert the
+ // current node into an interpolated string and add
+ // the string content to the list of child nodes.
+ pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, &opening, NULL, &closing);
+ pm_interpolated_string_node_append(parser, interpolated, current);
+ pm_interpolated_string_node_append(parser, interpolated, string);
+ current = (pm_node_t *) interpolated;
+ } else {
+ assert(false && "unreachable");
+ }
+
+ break;
+ }
+ case PM_TOKEN_EMBVAR: {
+ if (current == NULL) {
+ // If we hit an embedded variable and the current
+ // node is NULL, then this is the start of a new
+ // string. We'll set the current node to a new
+ // interpolated string.
+ pm_token_t opening = not_provided(parser);
+ pm_token_t closing = not_provided(parser);
+ current = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, NULL, &closing);
+ } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
+ // If we hit an embedded variable and the current
+ // node is a string node, then we'll convert the
+ // current into an interpolated string and add the
+ // string node to the list of parts.
+ pm_token_t opening = not_provided(parser);
+ pm_token_t closing = not_provided(parser);
+ pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, &opening, NULL, &closing);
+ pm_interpolated_string_node_append(parser, interpolated, current);
+ current = (pm_node_t *) interpolated;
+ } else {
+ // If we hit an embedded variable and the current
+ // node is an interpolated string, then we'll just
+ // add the embedded variable.
+ }
+
+ pm_node_t *part = parse_string_part(parser);
+ pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, part);
+ break;
+ }
+ case PM_TOKEN_EMBEXPR_BEGIN: {
+ if (current == NULL) {
+ // If we hit an embedded expression and the current
+ // node is NULL, then this is the start of a new
+ // string. We'll set the current node to a new
+ // interpolated string.
+ pm_token_t opening = not_provided(parser);
+ pm_token_t closing = not_provided(parser);
+ current = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, NULL, &closing);
+ } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
+ // If we hit an embedded expression and the current
+ // node is a string node, then we'll convert the
+ // current into an interpolated string and add the
+ // string node to the list of parts.
+ pm_token_t opening = not_provided(parser);
+ pm_token_t closing = not_provided(parser);
+ pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, &opening, NULL, &closing);
+ pm_interpolated_string_node_append(parser, interpolated, current);
+ current = (pm_node_t *) interpolated;
+ } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
+ // If we hit an embedded expression and the current
+ // node is an interpolated string, then we'll just
+ // continue on.
+ } else {
+ assert(false && "unreachable");
+ }
+
+ pm_node_t *part = parse_string_part(parser);
+ pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, part);
+ break;
+ }
+ default:
+ expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_UPPER_ELEMENT);
+ parser_lex(parser);
+ break;
+ }
+ }
+
+ // If we have a current node, then we need to append it to the list.
+ if (current) {
+ pm_array_node_elements_append(array, current);
+ }
+
+ pm_token_t closing = parser->current;
+ if (match1(parser, PM_TOKEN_EOF)) {
+ pm_parser_err_token(parser, &opening, PM_ERR_LIST_W_UPPER_TERM);
+ closing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
+ } else {
+ expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_W_UPPER_TERM);
+ }
+
+ pm_array_node_close_set(array, &closing);
+ return (pm_node_t *) array;
+ }
+ case PM_TOKEN_REGEXP_BEGIN: {
+ pm_token_t opening = parser->current;
+ parser_lex(parser);
+
+ if (match1(parser, PM_TOKEN_REGEXP_END)) {
+ // If we get here, then we have an end immediately after a start. In
+ // that case we'll create an empty content token and return an
+ // uninterpolated regular expression.
+ pm_token_t content = (pm_token_t) {
+ .type = PM_TOKEN_STRING_CONTENT,
+ .start = parser->previous.end,
+ .end = parser->previous.end
+ };
+
+ parser_lex(parser);
+
+ pm_node_t *node = (pm_node_t *) pm_regular_expression_node_create(parser, &opening, &content, &parser->previous);
+ pm_node_flag_set(node, PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING);
+
+ return node;
+ }
+
+ pm_interpolated_regular_expression_node_t *interpolated;
+
+ if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
+ // In this case we've hit string content so we know the regular
+ // expression at least has something in it. We'll need to check if the
+ // following token is the end (in which case we can return a plain
+ // regular expression) or if it's not then it has interpolation.
+ pm_string_t unescaped = parser->current_string;
+ pm_token_t content = parser->current;
+ bool ascii_only = parser->current_regular_expression_ascii_only;
+ parser_lex(parser);
+
+ // If we hit an end, then we can create a regular expression node
+ // without interpolation, which can be represented more succinctly and
+ // more easily compiled.
+ if (accept1(parser, PM_TOKEN_REGEXP_END)) {
+ pm_node_t *node = (pm_node_t *) pm_regular_expression_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped);
+ pm_node_flag_set(node, parse_and_validate_regular_expression_encoding(parser, &unescaped, ascii_only, node->flags));
+ return node;
+ }
+
+ // If we get here, then we have interpolation so we'll need to create
+ // a regular expression node with interpolation.
+ interpolated = pm_interpolated_regular_expression_node_create(parser, &opening);
+
+ pm_token_t opening = not_provided(parser);
+ pm_token_t closing = not_provided(parser);
+ pm_node_t *part = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &parser->previous, &closing, &unescaped);
+ pm_interpolated_regular_expression_node_append(interpolated, part);
+ } else {
+ // If the first part of the body of the regular expression is not a
+ // string content, then we have interpolation and we need to create an
+ // interpolated regular expression node.
+ interpolated = pm_interpolated_regular_expression_node_create(parser, &opening);
+ }
+
+ // Now that we're here and we have interpolation, we'll parse all of the
+ // parts into the list.
+ pm_node_t *part;
+ while (!match2(parser, PM_TOKEN_REGEXP_END, PM_TOKEN_EOF)) {
+ if ((part = parse_string_part(parser)) != NULL) {
+ pm_interpolated_regular_expression_node_append(interpolated, part);
+ }
+ }
+
+ pm_token_t closing = parser->current;
+ if (match1(parser, PM_TOKEN_EOF)) {
+ pm_parser_err_token(parser, &opening, PM_ERR_REGEXP_TERM);
+ closing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
+ } else {
+ expect1(parser, PM_TOKEN_REGEXP_END, PM_ERR_REGEXP_TERM);
+ }
+
+ pm_interpolated_regular_expression_node_closing_set(parser, interpolated, &closing);
+ return (pm_node_t *) interpolated;
+ }
+ case PM_TOKEN_BACKTICK:
+ case PM_TOKEN_PERCENT_LOWER_X: {
+ parser_lex(parser);
+ pm_token_t opening = parser->previous;
+
+ // When we get here, we don't know if this string is going to have
+ // interpolation or not, even though it is allowed. Still, we want to be
+ // able to return a string node without interpolation if we can since
+ // it'll be faster.
+ if (match1(parser, PM_TOKEN_STRING_END)) {
+ // If we get here, then we have an end immediately after a start. In
+ // that case we'll create an empty content token and return an
+ // uninterpolated string.
+ pm_token_t content = (pm_token_t) {
+ .type = PM_TOKEN_STRING_CONTENT,
+ .start = parser->previous.end,
+ .end = parser->previous.end
+ };
+
+ parser_lex(parser);
+ return (pm_node_t *) pm_xstring_node_create(parser, &opening, &content, &parser->previous);
+ }
+
+ pm_interpolated_x_string_node_t *node;
+
+ if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
+ // In this case we've hit string content so we know the string
+ // at least has something in it. We'll need to check if the
+ // following token is the end (in which case we can return a
+ // plain string) or if it's not then it has interpolation.
+ pm_string_t unescaped = parser->current_string;
+ pm_token_t content = parser->current;
+ parser_lex(parser);
+
+ if (match1(parser, PM_TOKEN_STRING_END)) {
+ pm_node_t *node = (pm_node_t *) pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped);
+ pm_node_flag_set(node, parse_unescaped_encoding(parser));
+ parser_lex(parser);
+ return node;
+ }
+
+ // If we get here, then we have interpolation so we'll need to
+ // create a string node with interpolation.
+ node = pm_interpolated_xstring_node_create(parser, &opening, &opening);
+
+ pm_token_t opening = not_provided(parser);
+ pm_token_t closing = not_provided(parser);
+
+ pm_node_t *part = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &parser->previous, &closing, &unescaped);
+ pm_node_flag_set(part, parse_unescaped_encoding(parser));
+
+ pm_interpolated_xstring_node_append(node, part);
+ } else {
+ // If the first part of the body of the string is not a string
+ // content, then we have interpolation and we need to create an
+ // interpolated string node.
+ node = pm_interpolated_xstring_node_create(parser, &opening, &opening);
+ }
+
+ pm_node_t *part;
+ while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
+ if ((part = parse_string_part(parser)) != NULL) {
+ pm_interpolated_xstring_node_append(node, part);
+ }
+ }
+
+ pm_token_t closing = parser->current;
+ if (match1(parser, PM_TOKEN_EOF)) {
+ pm_parser_err_token(parser, &opening, PM_ERR_XSTRING_TERM);
+ closing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
+ } else {
+ expect1(parser, PM_TOKEN_STRING_END, PM_ERR_XSTRING_TERM);
+ }
+ pm_interpolated_xstring_node_closing_set(node, &closing);
+
+ return (pm_node_t *) node;
+ }
+ case PM_TOKEN_USTAR: {
+ parser_lex(parser);
+
+ // * operators at the beginning of expressions are only valid in the
+ // context of a multiple assignment. We enforce that here. We'll
+ // still lex past it though and create a missing node place.
+ if (binding_power != PM_BINDING_POWER_STATEMENT) {
+ pm_parser_err_prefix(parser, diag_id);
+ return (pm_node_t *) pm_missing_node_create(parser, parser->previous.start, parser->previous.end);
+ }
+
+ pm_token_t operator = parser->previous;
+ pm_node_t *name = NULL;
+
+ if (token_begins_expression_p(parser->current.type)) {
+ name = parse_expression(parser, PM_BINDING_POWER_INDEX, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR);
+ }
+
+ pm_node_t *splat = (pm_node_t *) pm_splat_node_create(parser, &operator, name);
+
+ if (match1(parser, PM_TOKEN_COMMA)) {
+ return parse_targets_validate(parser, splat, PM_BINDING_POWER_INDEX);
+ } else {
+ return parse_target_validate(parser, splat);
+ }
+ }
+ case PM_TOKEN_BANG: {
+ parser_lex(parser);
+
+ pm_token_t operator = parser->previous;
+ pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, binding_power < PM_BINDING_POWER_MATCH, PM_ERR_UNARY_RECEIVER);
+ pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "!");
+
+ pm_conditional_predicate(parser, receiver, PM_CONDITIONAL_PREDICATE_TYPE_NOT);
+ return (pm_node_t *) node;
+ }
+ case PM_TOKEN_TILDE: {
+ parser_lex(parser);
+
+ pm_token_t operator = parser->previous;
+ pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, false, PM_ERR_UNARY_RECEIVER);
+ pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "~");
+
+ return (pm_node_t *) node;
+ }
+ case PM_TOKEN_UMINUS: {
+ parser_lex(parser);
+
+ pm_token_t operator = parser->previous;
+ pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, false, PM_ERR_UNARY_RECEIVER);
+ pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "-@");
+
+ return (pm_node_t *) node;
+ }
+ case PM_TOKEN_UMINUS_NUM: {
+ parser_lex(parser);
+
+ pm_token_t operator = parser->previous;
+ pm_node_t *node = parse_expression(parser, pm_binding_powers[parser->previous.type].right, false, PM_ERR_UNARY_RECEIVER);
+
+ if (accept1(parser, PM_TOKEN_STAR_STAR)) {
+ pm_token_t exponent_operator = parser->previous;
+ pm_node_t *exponent = parse_expression(parser, pm_binding_powers[exponent_operator.type].right, false, PM_ERR_EXPECT_ARGUMENT);
+ node = (pm_node_t *) pm_call_node_binary_create(parser, node, &exponent_operator, exponent, 0);
+ node = (pm_node_t *) pm_call_node_unary_create(parser, &operator, node, "-@");
+ } else {
+ switch (PM_NODE_TYPE(node)) {
+ case PM_INTEGER_NODE:
+ case PM_FLOAT_NODE:
+ case PM_RATIONAL_NODE:
+ case PM_IMAGINARY_NODE:
+ parse_negative_numeric(node);
+ break;
+ default:
+ node = (pm_node_t *) pm_call_node_unary_create(parser, &operator, node, "-@");
+ break;
+ }
+ }
+
+ return node;
+ }
+ case PM_TOKEN_MINUS_GREATER: {
+ int previous_lambda_enclosure_nesting = parser->lambda_enclosure_nesting;
+ parser->lambda_enclosure_nesting = parser->enclosure_nesting;
+
+ pm_accepts_block_stack_push(parser, true);
+ parser_lex(parser);
+
+ pm_token_t operator = parser->previous;
+ pm_parser_scope_push(parser, false);
+
+ pm_block_parameters_node_t *block_parameters;
+
+ switch (parser->current.type) {
+ case PM_TOKEN_PARENTHESIS_LEFT: {
+ assert(parser->current_scope->parameters == PM_SCOPE_PARAMETERS_NONE);
+ parser->current_scope->parameters = PM_SCOPE_PARAMETERS_ORDINARY;
+
+ pm_token_t opening = parser->current;
+ parser_lex(parser);
+
+ if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
+ block_parameters = pm_block_parameters_node_create(parser, NULL, &opening);
+ } else {
+ block_parameters = parse_block_parameters(parser, false, &opening, true);
+ }
+
+ accept1(parser, PM_TOKEN_NEWLINE);
+ expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
+
+ pm_block_parameters_node_closing_set(block_parameters, &parser->previous);
+ break;
+ }
+ case PM_CASE_PARAMETER: {
+ assert(parser->current_scope->parameters == PM_SCOPE_PARAMETERS_NONE);
+ parser->current_scope->parameters = PM_SCOPE_PARAMETERS_ORDINARY;
+
+ pm_accepts_block_stack_push(parser, false);
+ pm_token_t opening = not_provided(parser);
+ block_parameters = parse_block_parameters(parser, false, &opening, true);
+ pm_accepts_block_stack_pop(parser);
+ break;
+ }
+ default: {
+ block_parameters = NULL;
+ break;
+ }
+ }
+
+ pm_token_t opening;
+ pm_node_t *body = NULL;
+ parser->lambda_enclosure_nesting = previous_lambda_enclosure_nesting;
+
+ if (accept1(parser, PM_TOKEN_LAMBDA_BEGIN)) {
+ opening = parser->previous;
+
+ if (!accept1(parser, PM_TOKEN_BRACE_RIGHT)) {
+ body = (pm_node_t *) parse_statements(parser, PM_CONTEXT_LAMBDA_BRACES);
+ expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_LAMBDA_TERM_BRACE);
+ }
+ } else {
+ expect1(parser, PM_TOKEN_KEYWORD_DO, PM_ERR_LAMBDA_OPEN);
+ opening = parser->previous;
+
+ if (!match3(parser, PM_TOKEN_KEYWORD_END, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
+ pm_accepts_block_stack_push(parser, true);
+ body = (pm_node_t *) parse_statements(parser, PM_CONTEXT_LAMBDA_DO_END);
+ pm_accepts_block_stack_pop(parser);
+ }
+
+ if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
+ assert(body == NULL || PM_NODE_TYPE_P(body, PM_STATEMENTS_NODE));
+ body = (pm_node_t *) parse_rescues_implicit_begin(parser, opening.start, (pm_statements_node_t *) body, PM_RESCUES_LAMBDA);
+ }
+
+ expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_LAMBDA_TERM_END);
+ }
+
+ pm_constant_id_list_t locals;
+ pm_locals_order(parser, &parser->current_scope->locals, &locals, pm_parser_scope_toplevel_p(parser));
+ pm_node_t *parameters = parse_blocklike_parameters(parser, (pm_node_t *) block_parameters, &operator, &parser->previous);
+
+ pm_parser_scope_pop(parser);
+ pm_accepts_block_stack_pop(parser);
+
+ return (pm_node_t *) pm_lambda_node_create(parser, &locals, &operator, &opening, &parser->previous, parameters, body);
+ }
+ case PM_TOKEN_UPLUS: {
+ parser_lex(parser);
+
+ pm_token_t operator = parser->previous;
+ pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, false, PM_ERR_UNARY_RECEIVER);
+ pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "+@");
+
+ return (pm_node_t *) node;
+ }
+ case PM_TOKEN_STRING_BEGIN:
+ return parse_strings(parser, NULL);
+ case PM_TOKEN_SYMBOL_BEGIN: {
+ pm_lex_mode_t lex_mode = *parser->lex_modes.current;
+ parser_lex(parser);
+
+ return parse_symbol(parser, &lex_mode, PM_LEX_STATE_END);
+ }
+ default: {
+ pm_context_t recoverable = context_recoverable(parser, &parser->current);
+
+ if (recoverable != PM_CONTEXT_NONE) {
+ parser->recovering = true;
+
+ // If the given error is not the generic one, then we'll add it
+ // here because it will provide more context in addition to the
+ // recoverable error that we will also add.
+ if (diag_id != PM_ERR_CANNOT_PARSE_EXPRESSION) {
+ pm_parser_err_prefix(parser, diag_id);
+ }
+
+ // If we get here, then we are assuming this token is closing a
+ // parent context, so we'll indicate that to the user so that
+ // they know how we behaved.
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT, pm_token_type_human(parser->current.type), context_human(recoverable));
+ } else if (diag_id == PM_ERR_CANNOT_PARSE_EXPRESSION) {
+ // We're going to make a special case here, because "cannot
+ // parse expression" is pretty generic, and we know here that we
+ // have an unexpected token.
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, pm_token_type_human(parser->current.type));
+ } else {
+ pm_parser_err_prefix(parser, diag_id);
+ }
+
+ return (pm_node_t *) pm_missing_node_create(parser, parser->previous.start, parser->previous.end);
+ }
+ }
+}
+
+/**
+ * Parse a value that is going to be written to some kind of variable or method
+ * call. We need to handle this separately because the rescue modifier is
+ * permitted on the end of the these expressions, which is a deviation from its
+ * normal binding power.
+ *
+ * Note that this will only be called after an operator write, as in &&=, ||=,
+ * or any of the binary operators that can be written to a variable.
+ */
+static pm_node_t *
+parse_assignment_value(pm_parser_t *parser, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power, bool accepts_command_call, pm_diagnostic_id_t diag_id) {
+ pm_node_t *value = parse_value_expression(parser, binding_power, previous_binding_power == PM_BINDING_POWER_ASSIGNMENT ? accepts_command_call : previous_binding_power < PM_BINDING_POWER_MATCH, diag_id);
+
+ // Contradicting binding powers, the right-hand-side value of the assignment
+ // allows the `rescue` modifier.
+ if (match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
+ context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
+
+ pm_token_t rescue = parser->current;
+ parser_lex(parser);
+
+ pm_node_t *right = parse_expression(parser, binding_power, false, PM_ERR_RESCUE_MODIFIER_VALUE);
+ context_pop(parser);
+
+ return (pm_node_t *) pm_rescue_modifier_node_create(parser, value, &rescue, right);
+ }
+
+ return value;
+}
+
+/**
+ * When a local variable write node is the value being written in a different
+ * write, the local variable is considered "used".
+ */
+static void
+parse_assignment_value_local(pm_parser_t *parser, const pm_node_t *node) {
+ switch (PM_NODE_TYPE(node)) {
+ case PM_BEGIN_NODE: {
+ const pm_begin_node_t *cast = (const pm_begin_node_t *) node;
+ if (cast->statements != NULL) parse_assignment_value_local(parser, (const pm_node_t *) cast->statements);
+ break;
+ }
+ case PM_LOCAL_VARIABLE_WRITE_NODE: {
+ const pm_local_variable_write_node_t *cast = (const pm_local_variable_write_node_t *) node;
+ pm_locals_read(&pm_parser_scope_find(parser, cast->depth)->locals, cast->name);
+ break;
+ }
+ case PM_PARENTHESES_NODE: {
+ const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
+ if (cast->body != NULL) parse_assignment_value_local(parser, cast->body);
+ break;
+ }
+ case PM_STATEMENTS_NODE: {
+ const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
+ const pm_node_t *statement;
+
+ PM_NODE_LIST_FOREACH(&cast->body, index, statement) {
+ parse_assignment_value_local(parser, statement);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/**
+ * Parse the value (or values, through an implicit array) that is going to be
+ * written to some kind of variable or method call. We need to handle this
+ * separately because the rescue modifier is permitted on the end of the these
+ * expressions, which is a deviation from its normal binding power.
+ *
+ * Additionally, if the value is a local variable write node (e.g., a = a = 1),
+ * the "a" is marked as being used so the parser should not warn on it.
+ *
+ * Note that this will only be called after an = operator, as that is the only
+ * operator that allows multiple values after it.
+ */
+static pm_node_t *
+parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power, bool accepts_command_call, pm_diagnostic_id_t diag_id) {
+ pm_node_t *value = parse_starred_expression(parser, binding_power, previous_binding_power == PM_BINDING_POWER_ASSIGNMENT ? accepts_command_call : previous_binding_power < PM_BINDING_POWER_MATCH, diag_id);
+ parse_assignment_value_local(parser, value);
+
+ bool single_value = true;
+ if (previous_binding_power == PM_BINDING_POWER_STATEMENT && (PM_NODE_TYPE_P(value, PM_SPLAT_NODE) || match1(parser, PM_TOKEN_COMMA))) {
+ single_value = false;
+
+ pm_token_t opening = not_provided(parser);
+ pm_array_node_t *array = pm_array_node_create(parser, &opening);
+
+ pm_array_node_elements_append(array, value);
+ value = (pm_node_t *) array;
+
+ while (accept1(parser, PM_TOKEN_COMMA)) {
+ pm_node_t *element = parse_starred_expression(parser, binding_power, false, PM_ERR_ARRAY_ELEMENT);
+
+ pm_array_node_elements_append(array, element);
+ if (PM_NODE_TYPE_P(element, PM_MISSING_NODE)) break;
+
+ parse_assignment_value_local(parser, element);
+ }
+ }
+
+ // Contradicting binding powers, the right-hand-side value of the assignment
+ // allows the `rescue` modifier.
+ if ((single_value || (binding_power == (PM_BINDING_POWER_MULTI_ASSIGNMENT + 1))) && match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
+ context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
+
+ pm_token_t rescue = parser->current;
+ parser_lex(parser);
+
+ bool accepts_command_call_inner = false;
+
+ // RHS can accept command call iff the value is a call with arguments
+ // but without parenthesis.
+ if (PM_NODE_TYPE_P(value, PM_CALL_NODE)) {
+ pm_call_node_t *call_node = (pm_call_node_t *) value;
+ if ((call_node->arguments != NULL) && (call_node->opening_loc.start == NULL)) {
+ accepts_command_call_inner = true;
+ }
+ }
+
+ pm_node_t *right = parse_expression(parser, binding_power, accepts_command_call_inner, PM_ERR_RESCUE_MODIFIER_VALUE);
+ context_pop(parser);
+
+ return (pm_node_t *) pm_rescue_modifier_node_create(parser, value, &rescue, right);
+ }
+
+ return value;
+}
+
+/**
+ * Ensure a call node that is about to become a call operator node does not
+ * have arguments or a block attached. If it does, then we'll need to add an
+ * error message and destroy the arguments/block. Ideally we would keep the node
+ * around so that consumers would still have access to it, but we don't have a
+ * great structure for that at the moment.
+ */
+static void
+parse_call_operator_write(pm_parser_t *parser, pm_call_node_t *call_node, const pm_token_t *operator) {
+ if (call_node->arguments != NULL) {
+ pm_parser_err_token(parser, operator, PM_ERR_OPERATOR_WRITE_ARGUMENTS);
+ pm_node_destroy(parser, (pm_node_t *) call_node->arguments);
+ call_node->arguments = NULL;
+ }
+
+ if (call_node->block != NULL) {
+ pm_parser_err_token(parser, operator, PM_ERR_OPERATOR_WRITE_BLOCK);
+ pm_node_destroy(parser, (pm_node_t *) call_node->block);
+ call_node->block = NULL;
+ }
+}
+
+/**
+ * Returns true if the name of the capture group is a valid local variable that
+ * can be written to.
+ */
+static bool
+parse_regular_expression_named_capture(pm_parser_t *parser, const uint8_t *source, size_t length) {
+ if (length == 0) {
+ return false;
+ }
+
+ // First ensure that it starts with a valid identifier starting character.
+ size_t width = char_is_identifier_start(parser, source);
+ if (!width) {
+ return false;
+ }
+
+ // Next, ensure that it's not an uppercase character.
+ if (parser->encoding_changed) {
+ if (parser->encoding->isupper_char(source, (ptrdiff_t) length)) return false;
+ } else {
+ if (pm_encoding_utf_8_isupper_char(source, (ptrdiff_t) length)) return false;
+ }
+
+ // Next, iterate through all of the bytes of the string to ensure that they
+ // are all valid identifier characters.
+ const uint8_t *cursor = source + width;
+ while (cursor < source + length && (width = char_is_identifier(parser, cursor))) {
+ cursor += width;
+ }
+
+ return cursor == source + length;
+}
+
+/**
+ * Potentially change a =~ with a regular expression with named captures into a
+ * match write node.
+ */
+static pm_node_t *
+parse_regular_expression_named_captures(pm_parser_t *parser, const pm_string_t *content, pm_call_node_t *call) {
+ pm_string_list_t named_captures = { 0 };
+ pm_node_t *result;
+
+ if (pm_regexp_named_capture_group_names(pm_string_source(content), pm_string_length(content), &named_captures, parser->encoding_changed, parser->encoding) && (named_captures.length > 0)) {
+ // Since we should not create a MatchWriteNode when all capture names
+ // are invalid, creating a MatchWriteNode is delaid here.
+ pm_match_write_node_t *match = NULL;
+ pm_constant_id_list_t names = { 0 };
+
+ for (size_t index = 0; index < named_captures.length; index++) {
+ pm_string_t *string = &named_captures.strings[index];
+
+ const uint8_t *source = pm_string_source(string);
+ size_t length = pm_string_length(string);
+
+ pm_location_t location;
+ pm_constant_id_t name;
+
+ // If the name of the capture group isn't a valid identifier, we do
+ // not add it to the local table.
+ if (!parse_regular_expression_named_capture(parser, source, length)) continue;
+
+ if (content->type == PM_STRING_SHARED) {
+ // If the unescaped string is a slice of the source, then we can
+ // copy the names directly. The pointers will line up.
+ location = (pm_location_t) { .start = source, .end = source + length };
+ name = pm_parser_constant_id_location(parser, location.start, location.end);
+ } else {
+ // Otherwise, the name is a slice of the malloc-ed owned string,
+ // in which case we need to copy it out into a new string.
+ location = call->receiver->location;
+
+ void *memory = xmalloc(length);
+ if (memory == NULL) abort();
+
+ memcpy(memory, source, length);
+ name = pm_parser_constant_id_owned(parser, (uint8_t *) memory, length);
+ }
+
+ if (name != 0) {
+ // We dont want to create duplicate targets if the capture name
+ // is duplicated.
+ if (pm_constant_id_list_includes(&names, name)) continue;
+ pm_constant_id_list_append(&names, name);
+
+ int depth;
+ if ((depth = pm_parser_local_depth_constant_id(parser, name)) == -1) {
+ // If the identifier is not already a local, then we'll add
+ // it to the local table unless it's a keyword.
+ if (pm_local_is_keyword((const char *) source, length)) continue;
+
+ pm_parser_local_add(parser, name, location.start, location.end, 0);
+ }
+
+ // Here we lazily create the MatchWriteNode since we know we're
+ // about to add a target.
+ if (match == NULL) match = pm_match_write_node_create(parser, call);
+
+ // Next, create the local variable target and add it to the
+ // list of targets for the match.
+ pm_node_t *target = (pm_node_t *) pm_local_variable_target_node_create(parser, &location, name, depth == -1 ? 0 : (uint32_t) depth);
+ pm_node_list_append(&match->targets, target);
+ }
+ }
+
+ if (match != NULL) {
+ result = (pm_node_t *) match;
+ } else {
+ result = (pm_node_t *) call;
+ }
+
+ pm_constant_id_list_free(&names);
+ } else {
+ result = (pm_node_t *) call;
+ }
+
+ pm_string_list_free(&named_captures);
+ return result;
+}
+
+static inline pm_node_t *
+parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power, bool accepts_command_call) {
+ pm_token_t token = parser->current;
+
+ switch (token.type) {
+ case PM_TOKEN_EQUAL: {
+ switch (PM_NODE_TYPE(node)) {
+ case PM_CALL_NODE: {
+ // If we have no arguments to the call node and we need this
+ // to be a target then this is either a method call or a
+ // local variable write. This _must_ happen before the value
+ // is parsed because it could be referenced in the value.
+ pm_call_node_t *call_node = (pm_call_node_t *) node;
+ if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
+ pm_parser_local_add_location(parser, call_node->message_loc.start, call_node->message_loc.end, 0);
+ }
+ }
+ /* fallthrough */
+ case PM_CASE_WRITABLE: {
+ parser_lex(parser);
+ pm_node_t *value = parse_assignment_values(parser, previous_binding_power, PM_NODE_TYPE_P(node, PM_MULTI_TARGET_NODE) ? PM_BINDING_POWER_MULTI_ASSIGNMENT + 1 : binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL);
+ return parse_write(parser, node, &token, value);
+ }
+ case PM_SPLAT_NODE: {
+ pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser);
+ pm_multi_target_node_targets_append(parser, multi_target, node);
+
+ parser_lex(parser);
+ pm_node_t *value = parse_assignment_values(parser, previous_binding_power, PM_BINDING_POWER_MULTI_ASSIGNMENT + 1, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL);
+ return parse_write(parser, (pm_node_t *) multi_target, &token, value);
+ }
+ case PM_SOURCE_ENCODING_NODE:
+ case PM_FALSE_NODE:
+ case PM_SOURCE_FILE_NODE:
+ case PM_SOURCE_LINE_NODE:
+ case PM_NIL_NODE:
+ case PM_SELF_NODE:
+ case PM_TRUE_NODE: {
+ // In these special cases, we have specific error messages
+ // and we will replace them with local variable writes.
+ parser_lex(parser);
+ pm_node_t *value = parse_assignment_values(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL);
+ return parse_unwriteable_write(parser, node, &token, value);
+ }
+ default:
+ // In this case we have an = sign, but we don't know what
+ // it's for. We need to treat it as an error. We'll mark it
+ // as an error and skip past it.
+ parser_lex(parser);
+ pm_parser_err_token(parser, &token, PM_ERR_EXPRESSION_NOT_WRITABLE);
+ return node;
+ }
+ }
+ case PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL: {
+ switch (PM_NODE_TYPE(node)) {
+ case PM_BACK_REFERENCE_READ_NODE:
+ case PM_NUMBERED_REFERENCE_READ_NODE:
+ PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
+ /* fallthrough */
+ case PM_GLOBAL_VARIABLE_READ_NODE: {
+ parser_lex(parser);
+
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ);
+ pm_node_t *result = (pm_node_t *) pm_global_variable_and_write_node_create(parser, node, &token, value);
+
+ pm_node_destroy(parser, node);
+ return result;
+ }
+ case PM_CLASS_VARIABLE_READ_NODE: {
+ parser_lex(parser);
+
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ);
+ pm_node_t *result = (pm_node_t *) pm_class_variable_and_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value);
+
+ pm_node_destroy(parser, node);
+ return result;
+ }
+ case PM_CONSTANT_PATH_NODE: {
+ parser_lex(parser);
+
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ);
+ pm_node_t *write = (pm_node_t *) pm_constant_path_and_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value);
+
+ return parse_shareable_constant_write(parser, write);
+ }
+ case PM_CONSTANT_READ_NODE: {
+ parser_lex(parser);
+
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ);
+ pm_node_t *write = (pm_node_t *) pm_constant_and_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value);
+
+ pm_node_destroy(parser, node);
+ return parse_shareable_constant_write(parser, write);
+ }
+ case PM_INSTANCE_VARIABLE_READ_NODE: {
+ parser_lex(parser);
+
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ);
+ pm_node_t *result = (pm_node_t *) pm_instance_variable_and_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value);
+
+ pm_node_destroy(parser, node);
+ return result;
+ }
+ case PM_LOCAL_VARIABLE_READ_NODE: {
+ pm_local_variable_read_node_t *cast = (pm_local_variable_read_node_t *) node;
+ parser_lex(parser);
+
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ);
+ pm_node_t *result = (pm_node_t *) pm_local_variable_and_write_node_create(parser, node, &token, value, cast->name, cast->depth);
+
+ pm_node_destroy(parser, node);
+ return result;
+ }
+ case PM_CALL_NODE: {
+ parser_lex(parser);
+ pm_call_node_t *cast = (pm_call_node_t *) node;
+
+ // If we have a vcall (a method with no arguments and no
+ // receiver that could have been a local variable) then we
+ // will transform it into a local variable write.
+ if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
+ pm_location_t *message_loc = &cast->message_loc;
+ pm_refute_numbered_parameter(parser, message_loc->start, message_loc->end);
+
+ pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end, 1);
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ);
+ pm_node_t *result = (pm_node_t *) pm_local_variable_and_write_node_create(parser, (pm_node_t *) cast, &token, value, constant_id, 0);
+
+ pm_node_destroy(parser, (pm_node_t *) cast);
+ return result;
+ }
+
+ // If there is no call operator and the message is "[]" then
+ // this is an aref expression, and we can transform it into
+ // an aset expression.
+ if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) {
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ);
+ return (pm_node_t *) pm_index_and_write_node_create(parser, cast, &token, value);
+ }
+
+ // If this node cannot be writable, then we have an error.
+ if (pm_call_node_writable_p(parser, cast)) {
+ parse_write_name(parser, &cast->name);
+ } else {
+ pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED);
+ }
+
+ parse_call_operator_write(parser, cast, &token);
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ);
+ return (pm_node_t *) pm_call_and_write_node_create(parser, cast, &token, value);
+ }
+ case PM_MULTI_WRITE_NODE: {
+ parser_lex(parser);
+ pm_parser_err_token(parser, &token, PM_ERR_AMPAMPEQ_MULTI_ASSIGN);
+ return node;
+ }
+ default:
+ parser_lex(parser);
+
+ // In this case we have an &&= sign, but we don't know what it's for.
+ // We need to treat it as an error. For now, we'll mark it as an error
+ // and just skip right past it.
+ pm_parser_err_token(parser, &token, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ);
+ return node;
+ }
+ }
+ case PM_TOKEN_PIPE_PIPE_EQUAL: {
+ switch (PM_NODE_TYPE(node)) {
+ case PM_BACK_REFERENCE_READ_NODE:
+ case PM_NUMBERED_REFERENCE_READ_NODE:
+ PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
+ /* fallthrough */
+ case PM_GLOBAL_VARIABLE_READ_NODE: {
+ parser_lex(parser);
+
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ);
+ pm_node_t *result = (pm_node_t *) pm_global_variable_or_write_node_create(parser, node, &token, value);
+
+ pm_node_destroy(parser, node);
+ return result;
+ }
+ case PM_CLASS_VARIABLE_READ_NODE: {
+ parser_lex(parser);
+
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ);
+ pm_node_t *result = (pm_node_t *) pm_class_variable_or_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value);
+
+ pm_node_destroy(parser, node);
+ return result;
+ }
+ case PM_CONSTANT_PATH_NODE: {
+ parser_lex(parser);
+
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ);
+ pm_node_t *write = (pm_node_t *) pm_constant_path_or_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value);
+
+ return parse_shareable_constant_write(parser, write);
+ }
+ case PM_CONSTANT_READ_NODE: {
+ parser_lex(parser);
+
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ);
+ pm_node_t *write = (pm_node_t *) pm_constant_or_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value);
+
+ pm_node_destroy(parser, node);
+ return parse_shareable_constant_write(parser, write);
+ }
+ case PM_INSTANCE_VARIABLE_READ_NODE: {
+ parser_lex(parser);
+
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ);
+ pm_node_t *result = (pm_node_t *) pm_instance_variable_or_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value);
+
+ pm_node_destroy(parser, node);
+ return result;
+ }
+ case PM_LOCAL_VARIABLE_READ_NODE: {
+ pm_local_variable_read_node_t *cast = (pm_local_variable_read_node_t *) node;
+ parser_lex(parser);
+
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ);
+ pm_node_t *result = (pm_node_t *) pm_local_variable_or_write_node_create(parser, node, &token, value, cast->name, cast->depth);
+
+ pm_node_destroy(parser, node);
+ return result;
+ }
+ case PM_CALL_NODE: {
+ parser_lex(parser);
+ pm_call_node_t *cast = (pm_call_node_t *) node;
+
+ // If we have a vcall (a method with no arguments and no
+ // receiver that could have been a local variable) then we
+ // will transform it into a local variable write.
+ if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
+ pm_location_t *message_loc = &cast->message_loc;
+ pm_refute_numbered_parameter(parser, message_loc->start, message_loc->end);
+
+ pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end, 1);
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ);
+ pm_node_t *result = (pm_node_t *) pm_local_variable_or_write_node_create(parser, (pm_node_t *) cast, &token, value, constant_id, 0);
+
+ pm_node_destroy(parser, (pm_node_t *) cast);
+ return result;
+ }
+
+ // If there is no call operator and the message is "[]" then
+ // this is an aref expression, and we can transform it into
+ // an aset expression.
+ if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) {
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ);
+ return (pm_node_t *) pm_index_or_write_node_create(parser, cast, &token, value);
+ }
+
+ // If this node cannot be writable, then we have an error.
+ if (pm_call_node_writable_p(parser, cast)) {
+ parse_write_name(parser, &cast->name);
+ } else {
+ pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED);
+ }
+
+ parse_call_operator_write(parser, cast, &token);
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ);
+ return (pm_node_t *) pm_call_or_write_node_create(parser, cast, &token, value);
+ }
+ case PM_MULTI_WRITE_NODE: {
+ parser_lex(parser);
+ pm_parser_err_token(parser, &token, PM_ERR_PIPEPIPEEQ_MULTI_ASSIGN);
+ return node;
+ }
+ default:
+ parser_lex(parser);
+
+ // In this case we have an ||= sign, but we don't know what it's for.
+ // We need to treat it as an error. For now, we'll mark it as an error
+ // and just skip right past it.
+ pm_parser_err_token(parser, &token, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ);
+ return node;
+ }
+ }
+ case PM_TOKEN_AMPERSAND_EQUAL:
+ case PM_TOKEN_CARET_EQUAL:
+ case PM_TOKEN_GREATER_GREATER_EQUAL:
+ case PM_TOKEN_LESS_LESS_EQUAL:
+ case PM_TOKEN_MINUS_EQUAL:
+ case PM_TOKEN_PERCENT_EQUAL:
+ case PM_TOKEN_PIPE_EQUAL:
+ case PM_TOKEN_PLUS_EQUAL:
+ case PM_TOKEN_SLASH_EQUAL:
+ case PM_TOKEN_STAR_EQUAL:
+ case PM_TOKEN_STAR_STAR_EQUAL: {
+ switch (PM_NODE_TYPE(node)) {
+ case PM_BACK_REFERENCE_READ_NODE:
+ case PM_NUMBERED_REFERENCE_READ_NODE:
+ PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
+ /* fallthrough */
+ case PM_GLOBAL_VARIABLE_READ_NODE: {
+ parser_lex(parser);
+
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
+ pm_node_t *result = (pm_node_t *) pm_global_variable_operator_write_node_create(parser, node, &token, value);
+
+ pm_node_destroy(parser, node);
+ return result;
+ }
+ case PM_CLASS_VARIABLE_READ_NODE: {
+ parser_lex(parser);
+
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
+ pm_node_t *result = (pm_node_t *) pm_class_variable_operator_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value);
+
+ pm_node_destroy(parser, node);
+ return result;
+ }
+ case PM_CONSTANT_PATH_NODE: {
+ parser_lex(parser);
+
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
+ pm_node_t *write = (pm_node_t *) pm_constant_path_operator_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value);
+
+ return parse_shareable_constant_write(parser, write);
+ }
+ case PM_CONSTANT_READ_NODE: {
+ parser_lex(parser);
+
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
+ pm_node_t *write = (pm_node_t *) pm_constant_operator_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value);
+
+ pm_node_destroy(parser, node);
+ return parse_shareable_constant_write(parser, write);
+ }
+ case PM_INSTANCE_VARIABLE_READ_NODE: {
+ parser_lex(parser);
+
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
+ pm_node_t *result = (pm_node_t *) pm_instance_variable_operator_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value);
+
+ pm_node_destroy(parser, node);
+ return result;
+ }
+ case PM_LOCAL_VARIABLE_READ_NODE: {
+ pm_local_variable_read_node_t *cast = (pm_local_variable_read_node_t *) node;
+ parser_lex(parser);
+
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
+ pm_node_t *result = (pm_node_t *) pm_local_variable_operator_write_node_create(parser, node, &token, value, cast->name, cast->depth);
+
+ pm_node_destroy(parser, node);
+ return result;
+ }
+ case PM_CALL_NODE: {
+ parser_lex(parser);
+ pm_call_node_t *cast = (pm_call_node_t *) node;
+
+ // If we have a vcall (a method with no arguments and no
+ // receiver that could have been a local variable) then we
+ // will transform it into a local variable write.
+ if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
+ pm_location_t *message_loc = &cast->message_loc;
+ pm_refute_numbered_parameter(parser, message_loc->start, message_loc->end);
+
+ pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end, 1);
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
+ pm_node_t *result = (pm_node_t *) pm_local_variable_operator_write_node_create(parser, (pm_node_t *) cast, &token, value, constant_id, 0);
+
+ pm_node_destroy(parser, (pm_node_t *) cast);
+ return result;
+ }
+
+ // If there is no call operator and the message is "[]" then
+ // this is an aref expression, and we can transform it into
+ // an aset expression.
+ if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) {
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
+ return (pm_node_t *) pm_index_operator_write_node_create(parser, cast, &token, value);
+ }
+
+ // If this node cannot be writable, then we have an error.
+ if (pm_call_node_writable_p(parser, cast)) {
+ parse_write_name(parser, &cast->name);
+ } else {
+ pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED);
+ }
+
+ parse_call_operator_write(parser, cast, &token);
+ pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
+ return (pm_node_t *) pm_call_operator_write_node_create(parser, cast, &token, value);
+ }
+ case PM_MULTI_WRITE_NODE: {
+ parser_lex(parser);
+ pm_parser_err_token(parser, &token, PM_ERR_OPERATOR_MULTI_ASSIGN);
+ return node;
+ }
+ default:
+ parser_lex(parser);
+
+ // In this case we have an operator but we don't know what it's for.
+ // We need to treat it as an error. For now, we'll mark it as an error
+ // and just skip right past it.
+ pm_parser_err_previous(parser, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
+ return node;
+ }
+ }
+ case PM_TOKEN_AMPERSAND_AMPERSAND:
+ case PM_TOKEN_KEYWORD_AND: {
+ parser_lex(parser);
+
+ pm_node_t *right = parse_expression(parser, binding_power, parser->previous.type == PM_TOKEN_KEYWORD_AND, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
+ return (pm_node_t *) pm_and_node_create(parser, node, &token, right);
+ }
+ case PM_TOKEN_KEYWORD_OR:
+ case PM_TOKEN_PIPE_PIPE: {
+ parser_lex(parser);
+
+ pm_node_t *right = parse_expression(parser, binding_power, parser->previous.type == PM_TOKEN_KEYWORD_OR, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
+ return (pm_node_t *) pm_or_node_create(parser, node, &token, right);
+ }
+ case PM_TOKEN_EQUAL_TILDE: {
+ // Note that we _must_ parse the value before adding the local
+ // variables in order to properly mirror the behavior of Ruby. For
+ // example,
+ //
+ // /(?<foo>bar)/ =~ foo
+ //
+ // In this case, `foo` should be a method call and not a local yet.
+ parser_lex(parser);
+ pm_node_t *argument = parse_expression(parser, binding_power, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
+
+ // By default, we're going to create a call node and then return it.
+ pm_call_node_t *call = pm_call_node_binary_create(parser, node, &token, argument, 0);
+ pm_node_t *result = (pm_node_t *) call;
+
+ // If the receiver of this =~ is a regular expression node, then we
+ // need to introduce local variables for it based on its named
+ // capture groups.
+ if (PM_NODE_TYPE_P(node, PM_INTERPOLATED_REGULAR_EXPRESSION_NODE)) {
+ // It's possible to have an interpolated regular expression node
+ // that only contains strings. This is because it can be split
+ // up by a heredoc. In this case we need to concat the unescaped
+ // strings together and then parse them as a regular expression.
+ pm_node_list_t *parts = &((pm_interpolated_regular_expression_node_t *) node)->parts;
+
+ bool interpolated = false;
+ size_t total_length = 0;
+
+ pm_node_t *part;
+ PM_NODE_LIST_FOREACH(parts, index, part) {
+ if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
+ total_length += pm_string_length(&((pm_string_node_t *) part)->unescaped);
+ } else {
+ interpolated = true;
+ break;
+ }
+ }
+
+ if (!interpolated && total_length > 0) {
+ void *memory = xmalloc(total_length);
+ if (!memory) abort();
+
+ uint8_t *cursor = memory;
+ PM_NODE_LIST_FOREACH(parts, index, part) {
+ pm_string_t *unescaped = &((pm_string_node_t *) part)->unescaped;
+ size_t length = pm_string_length(unescaped);
+
+ memcpy(cursor, pm_string_source(unescaped), length);
+ cursor += length;
+ }
+
+ pm_string_t owned;
+ pm_string_owned_init(&owned, (uint8_t *) memory, total_length);
+
+ result = parse_regular_expression_named_captures(parser, &owned, call);
+ pm_string_free(&owned);
+ }
+ } else if (PM_NODE_TYPE_P(node, PM_REGULAR_EXPRESSION_NODE)) {
+ // If we have a regular expression node, then we can just parse
+ // the named captures directly off the unescaped string.
+ const pm_string_t *content = &((pm_regular_expression_node_t *) node)->unescaped;
+ result = parse_regular_expression_named_captures(parser, content, call);
+ }
+
+ return result;
+ }
+ case PM_TOKEN_UAMPERSAND:
+ case PM_TOKEN_USTAR:
+ case PM_TOKEN_USTAR_STAR:
+ // The only times this will occur are when we are in an error state,
+ // but we'll put them in here so that errors can propagate.
+ case PM_TOKEN_BANG_EQUAL:
+ case PM_TOKEN_BANG_TILDE:
+ case PM_TOKEN_EQUAL_EQUAL:
+ case PM_TOKEN_EQUAL_EQUAL_EQUAL:
+ case PM_TOKEN_LESS_EQUAL_GREATER:
+ case PM_TOKEN_CARET:
+ case PM_TOKEN_PIPE:
+ case PM_TOKEN_AMPERSAND:
+ case PM_TOKEN_GREATER_GREATER:
+ case PM_TOKEN_LESS_LESS:
+ case PM_TOKEN_MINUS:
+ case PM_TOKEN_PLUS:
+ case PM_TOKEN_PERCENT:
+ case PM_TOKEN_SLASH:
+ case PM_TOKEN_STAR:
+ case PM_TOKEN_STAR_STAR: {
+ parser_lex(parser);
+ pm_node_t *argument = parse_expression(parser, binding_power, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
+ return (pm_node_t *) pm_call_node_binary_create(parser, node, &token, argument, 0);
+ }
+ case PM_TOKEN_GREATER:
+ case PM_TOKEN_GREATER_EQUAL:
+ case PM_TOKEN_LESS:
+ case PM_TOKEN_LESS_EQUAL: {
+ if (PM_NODE_TYPE_P(node, PM_CALL_NODE) && PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_COMPARISON)) {
+ PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, parser->current, PM_WARN_COMPARISON_AFTER_COMPARISON);
+ }
+
+ parser_lex(parser);
+ pm_node_t *argument = parse_expression(parser, binding_power, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
+ return (pm_node_t *) pm_call_node_binary_create(parser, node, &token, argument, PM_CALL_NODE_FLAGS_COMPARISON);
+ }
+ case PM_TOKEN_AMPERSAND_DOT:
+ case PM_TOKEN_DOT: {
+ parser_lex(parser);
+ pm_token_t operator = parser->previous;
+ pm_arguments_t arguments = { 0 };
+
+ // This if statement handles the foo.() syntax.
+ if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
+ parse_arguments_list(parser, &arguments, true, false);
+ return (pm_node_t *) pm_call_node_shorthand_create(parser, node, &operator, &arguments);
+ }
+
+ pm_token_t message;
+
+ switch (parser->current.type) {
+ case PM_CASE_OPERATOR:
+ case PM_CASE_KEYWORD:
+ case PM_TOKEN_CONSTANT:
+ case PM_TOKEN_IDENTIFIER:
+ case PM_TOKEN_METHOD_NAME: {
+ parser_lex(parser);
+ message = parser->previous;
+ break;
+ }
+ default: {
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_MESSAGE, pm_token_type_human(parser->current.type));
+ message = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
+ }
+ }
+
+ parse_arguments_list(parser, &arguments, true, accepts_command_call);
+ pm_call_node_t *call = pm_call_node_call_create(parser, node, &operator, &message, &arguments);
+
+ if (
+ (previous_binding_power == PM_BINDING_POWER_STATEMENT) &&
+ arguments.arguments == NULL &&
+ arguments.opening_loc.start == NULL &&
+ match1(parser, PM_TOKEN_COMMA)
+ ) {
+ return parse_targets_validate(parser, (pm_node_t *) call, PM_BINDING_POWER_INDEX);
+ } else {
+ return (pm_node_t *) call;
+ }
+ }
+ case PM_TOKEN_DOT_DOT:
+ case PM_TOKEN_DOT_DOT_DOT: {
+ parser_lex(parser);
+
+ pm_node_t *right = NULL;
+ if (token_begins_expression_p(parser->current.type)) {
+ right = parse_expression(parser, binding_power, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
+ }
+
+ return (pm_node_t *) pm_range_node_create(parser, node, &token, right);
+ }
+ case PM_TOKEN_KEYWORD_IF_MODIFIER: {
+ pm_token_t keyword = parser->current;
+ parser_lex(parser);
+
+ pm_node_t *predicate = parse_value_expression(parser, binding_power, true, PM_ERR_CONDITIONAL_IF_PREDICATE);
+ return (pm_node_t *) pm_if_node_modifier_create(parser, node, &keyword, predicate);
+ }
+ case PM_TOKEN_KEYWORD_UNLESS_MODIFIER: {
+ pm_token_t keyword = parser->current;
+ parser_lex(parser);
+
+ pm_node_t *predicate = parse_value_expression(parser, binding_power, true, PM_ERR_CONDITIONAL_UNLESS_PREDICATE);
+ return (pm_node_t *) pm_unless_node_modifier_create(parser, node, &keyword, predicate);
+ }
+ case PM_TOKEN_KEYWORD_UNTIL_MODIFIER: {
+ parser_lex(parser);
+ pm_statements_node_t *statements = pm_statements_node_create(parser);
+ pm_statements_node_body_append(parser, statements, node);
+
+ pm_node_t *predicate = parse_value_expression(parser, binding_power, true, PM_ERR_CONDITIONAL_UNTIL_PREDICATE);
+ return (pm_node_t *) pm_until_node_modifier_create(parser, &token, predicate, statements, PM_NODE_TYPE_P(node, PM_BEGIN_NODE) ? PM_LOOP_FLAGS_BEGIN_MODIFIER : 0);
+ }
+ case PM_TOKEN_KEYWORD_WHILE_MODIFIER: {
+ parser_lex(parser);
+ pm_statements_node_t *statements = pm_statements_node_create(parser);
+ pm_statements_node_body_append(parser, statements, node);
+
+ pm_node_t *predicate = parse_value_expression(parser, binding_power, true, PM_ERR_CONDITIONAL_WHILE_PREDICATE);
+ return (pm_node_t *) pm_while_node_modifier_create(parser, &token, predicate, statements, PM_NODE_TYPE_P(node, PM_BEGIN_NODE) ? PM_LOOP_FLAGS_BEGIN_MODIFIER : 0);
+ }
+ case PM_TOKEN_QUESTION_MARK: {
+ context_push(parser, PM_CONTEXT_TERNARY);
+ pm_node_list_t current_block_exits = { 0 };
+ pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
+
+ pm_token_t qmark = parser->current;
+ parser_lex(parser);
+
+ pm_node_t *true_expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_TERNARY_EXPRESSION_TRUE);
+
+ if (parser->recovering) {
+ // If parsing the true expression of this ternary resulted in a syntax
+ // error that we can recover from, then we're going to put missing nodes
+ // and tokens into the remaining places. We want to be sure to do this
+ // before the `expect` function call to make sure it doesn't
+ // accidentally move past a ':' token that occurs after the syntax
+ // error.
+ pm_token_t colon = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
+ pm_node_t *false_expression = (pm_node_t *) pm_missing_node_create(parser, colon.start, colon.end);
+
+ context_pop(parser);
+ pop_block_exits(parser, previous_block_exits);
+ pm_node_list_free(&current_block_exits);
+
+ return (pm_node_t *) pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression);
+ }
+
+ accept1(parser, PM_TOKEN_NEWLINE);
+ expect1(parser, PM_TOKEN_COLON, PM_ERR_TERNARY_COLON);
+
+ pm_token_t colon = parser->previous;
+ pm_node_t *false_expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_TERNARY_EXPRESSION_FALSE);
+
+ context_pop(parser);
+ pop_block_exits(parser, previous_block_exits);
+ pm_node_list_free(&current_block_exits);
+
+ return (pm_node_t *) pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression);
+ }
+ case PM_TOKEN_COLON_COLON: {
+ parser_lex(parser);
+ pm_token_t delimiter = parser->previous;
+
+ switch (parser->current.type) {
+ case PM_TOKEN_CONSTANT: {
+ parser_lex(parser);
+ pm_node_t *path;
+
+ if (
+ (parser->current.type == PM_TOKEN_PARENTHESIS_LEFT) ||
+ (token_begins_expression_p(parser->current.type) || match3(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR))
+ ) {
+ // If we have a constant immediately following a '::' operator, then
+ // this can either be a constant path or a method call, depending on
+ // what follows the constant.
+ //
+ // If we have parentheses, then this is a method call. That would
+ // look like Foo::Bar().
+ pm_token_t message = parser->previous;
+ pm_arguments_t arguments = { 0 };
+
+ parse_arguments_list(parser, &arguments, true, accepts_command_call);
+ path = (pm_node_t *) pm_call_node_call_create(parser, node, &delimiter, &message, &arguments);
+ } else {
+ // Otherwise, this is a constant path. That would look like Foo::Bar.
+ pm_node_t *child = (pm_node_t *) pm_constant_read_node_create(parser, &parser->previous);
+ path = (pm_node_t *)pm_constant_path_node_create(parser, node, &delimiter, child);
+ }
+
+ // If this is followed by a comma then it is a multiple assignment.
+ if (previous_binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
+ return parse_targets_validate(parser, path, PM_BINDING_POWER_INDEX);
+ }
+
+ return path;
+ }
+ case PM_CASE_OPERATOR:
+ case PM_CASE_KEYWORD:
+ case PM_TOKEN_IDENTIFIER:
+ case PM_TOKEN_METHOD_NAME: {
+ parser_lex(parser);
+ pm_token_t message = parser->previous;
+
+ // If we have an identifier following a '::' operator, then it is for
+ // sure a method call.
+ pm_arguments_t arguments = { 0 };
+ parse_arguments_list(parser, &arguments, true, accepts_command_call);
+ pm_call_node_t *call = pm_call_node_call_create(parser, node, &delimiter, &message, &arguments);
+
+ // If this is followed by a comma then it is a multiple assignment.
+ if (previous_binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
+ return parse_targets_validate(parser, (pm_node_t *) call, PM_BINDING_POWER_INDEX);
+ }
+
+ return (pm_node_t *) call;
+ }
+ case PM_TOKEN_PARENTHESIS_LEFT: {
+ // If we have a parenthesis following a '::' operator, then it is the
+ // method call shorthand. That would look like Foo::(bar).
+ pm_arguments_t arguments = { 0 };
+ parse_arguments_list(parser, &arguments, true, false);
+
+ return (pm_node_t *) pm_call_node_shorthand_create(parser, node, &delimiter, &arguments);
+ }
+ default: {
+ pm_parser_err_token(parser, &delimiter, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
+ pm_node_t *child = (pm_node_t *) pm_missing_node_create(parser, delimiter.start, delimiter.end);
+ return (pm_node_t *)pm_constant_path_node_create(parser, node, &delimiter, child);
+ }
+ }
+ }
+ case PM_TOKEN_KEYWORD_RESCUE_MODIFIER: {
+ context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
+ parser_lex(parser);
+ accept1(parser, PM_TOKEN_NEWLINE);
+
+ pm_node_t *value = parse_expression(parser, binding_power, true, PM_ERR_RESCUE_MODIFIER_VALUE);
+ context_pop(parser);
+
+ return (pm_node_t *) pm_rescue_modifier_node_create(parser, node, &token, value);
+ }
+ case PM_TOKEN_BRACKET_LEFT: {
+ parser_lex(parser);
+
+ pm_arguments_t arguments = { 0 };
+ arguments.opening_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous);
+
+ if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
+ pm_accepts_block_stack_push(parser, true);
+ parse_arguments(parser, &arguments, false, PM_TOKEN_BRACKET_RIGHT);
+ pm_accepts_block_stack_pop(parser);
+ expect1(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_EXPECT_RBRACKET);
+ }
+
+ arguments.closing_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous);
+
+ // If we have a comma after the closing bracket then this is a multiple
+ // assignment and we should parse the targets.
+ if (previous_binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
+ pm_call_node_t *aref = pm_call_node_aref_create(parser, node, &arguments);
+ return parse_targets_validate(parser, (pm_node_t *) aref, PM_BINDING_POWER_INDEX);
+ }
+
+ // If we're at the end of the arguments, we can now check if there is a
+ // block node that starts with a {. If there is, then we can parse it and
+ // add it to the arguments.
+ pm_block_node_t *block = NULL;
+ if (accept1(parser, PM_TOKEN_BRACE_LEFT)) {
+ block = parse_block(parser);
+ pm_arguments_validate_block(parser, &arguments, block);
+ } else if (pm_accepts_block_stack_p(parser) && accept1(parser, PM_TOKEN_KEYWORD_DO)) {
+ block = parse_block(parser);
+ }
+
+ if (block != NULL) {
+ if (arguments.block != NULL) {
+ pm_parser_err_node(parser, (pm_node_t *) block, PM_ERR_ARGUMENT_AFTER_BLOCK);
+ if (arguments.arguments == NULL) {
+ arguments.arguments = pm_arguments_node_create(parser);
+ }
+ pm_arguments_node_arguments_append(arguments.arguments, arguments.block);
+ }
+
+ arguments.block = (pm_node_t *) block;
+ }
+
+ return (pm_node_t *) pm_call_node_aref_create(parser, node, &arguments);
+ }
+ case PM_TOKEN_KEYWORD_IN: {
+ bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
+ parser->pattern_matching_newlines = true;
+
+ pm_token_t operator = parser->current;
+ parser->command_start = false;
+ lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
+ parser_lex(parser);
+
+ pm_constant_id_list_t captures = { 0 };
+ pm_node_t *pattern = parse_pattern(parser, &captures, true, PM_ERR_PATTERN_EXPRESSION_AFTER_IN);
+
+ parser->pattern_matching_newlines = previous_pattern_matching_newlines;
+ pm_constant_id_list_free(&captures);
+
+ return (pm_node_t *) pm_match_predicate_node_create(parser, node, pattern, &operator);
+ }
+ case PM_TOKEN_EQUAL_GREATER: {
+ bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
+ parser->pattern_matching_newlines = true;
+
+ pm_token_t operator = parser->current;
+ parser->command_start = false;
+ lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
+ parser_lex(parser);
+
+ pm_constant_id_list_t captures = { 0 };
+ pm_node_t *pattern = parse_pattern(parser, &captures, true, PM_ERR_PATTERN_EXPRESSION_AFTER_HROCKET);
+
+ parser->pattern_matching_newlines = previous_pattern_matching_newlines;
+ pm_constant_id_list_free(&captures);
+
+ return (pm_node_t *) pm_match_required_node_create(parser, node, pattern, &operator);
+ }
+ default:
+ assert(false && "unreachable");
+ return NULL;
+ }
+}
+
+/**
+ * Parse an expression at the given point of the parser using the given binding
+ * power to parse subsequent chains. If this function finds a syntax error, it
+ * will append the error message to the parser's error list.
+ *
+ * Consumers of this function should always check parser->recovering to
+ * determine if they need to perform additional cleanup.
+ */
+static pm_node_t *
+parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool accepts_command_call, pm_diagnostic_id_t diag_id) {
+ pm_node_t *node = parse_expression_prefix(parser, binding_power, accepts_command_call, diag_id);
+
+ switch (PM_NODE_TYPE(node)) {
+ case PM_MISSING_NODE:
+ // If we found a syntax error, then the type of node returned by
+ // parse_expression_prefix is going to be a missing node.
+ return node;
+ case PM_PRE_EXECUTION_NODE:
+ case PM_POST_EXECUTION_NODE:
+ case PM_ALIAS_GLOBAL_VARIABLE_NODE:
+ case PM_ALIAS_METHOD_NODE:
+ case PM_UNDEF_NODE:
+ // These expressions are statements, and cannot be followed by
+ // operators (except modifiers).
+ if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER) {
+ return node;
+ }
+ break;
+ case PM_RANGE_NODE:
+ // Range operators are non-associative, so that it does not
+ // associate with other range operators (i.e. `..1..` should be
+ // rejected). For this reason, we check such a case for unary ranges
+ // here, and if so, it returns the node immediately.
+ if ((((pm_range_node_t *) node)->left == NULL) && pm_binding_powers[parser->current.type].left >= PM_BINDING_POWER_RANGE) {
+ return node;
+ }
+ break;
+ default:
+ break;
+ }
+
+ // Otherwise we'll look and see if the next token can be parsed as an infix
+ // operator. If it can, then we'll parse it using parse_expression_infix.
+ pm_binding_powers_t current_binding_powers;
+ while (
+ current_binding_powers = pm_binding_powers[parser->current.type],
+ binding_power <= current_binding_powers.left &&
+ current_binding_powers.binary
+ ) {
+ node = parse_expression_infix(parser, node, binding_power, current_binding_powers.right, accepts_command_call);
+
+ if (current_binding_powers.nonassoc) {
+ bool endless_range_p = PM_NODE_TYPE_P(node, PM_RANGE_NODE) && ((pm_range_node_t *) node)->right == NULL;
+ pm_binding_power_t left = endless_range_p ? PM_BINDING_POWER_TERM : current_binding_powers.left;
+ if (
+ left <= pm_binding_powers[parser->current.type].left ||
+ // Exceptionally to operator precedences, '1.. & 2' is rejected.
+ // '1.. || 2' is also an exception, but it is handled by the lexer.
+ // (Here, parser->current is PM_TOKEN_PIPE, not PM_TOKEN_PIPE_PIPE).
+ (endless_range_p && match1(parser, PM_TOKEN_AMPERSAND))
+ ) {
+ break;
+ }
+ }
+
+ if (accepts_command_call) {
+ // A command-style method call is only accepted on method chains.
+ // Thus, we check whether the parsed node can continue method chains.
+ // The method chain can continue if the parsed node is one of the following five kinds:
+ // (1) index access: foo[1]
+ // (2) attribute access: foo.bar
+ // (3) method call with parenthesis: foo.bar(1)
+ // (4) method call with a block: foo.bar do end
+ // (5) constant path: foo::Bar
+ switch (node->type) {
+ case PM_CALL_NODE: {
+ pm_call_node_t *cast = (pm_call_node_t *)node;
+ if (
+ // (1) foo[1]
+ !(
+ cast->call_operator_loc.start == NULL &&
+ cast->message_loc.start != NULL &&
+ cast->message_loc.start[0] == '[' &&
+ cast->message_loc.end[-1] == ']'
+ ) &&
+ // (2) foo.bar
+ !(
+ cast->call_operator_loc.start != NULL &&
+ cast->arguments == NULL &&
+ cast->block == NULL &&
+ cast->opening_loc.start == NULL
+ ) &&
+ // (3) foo.bar(1)
+ !(
+ cast->call_operator_loc.start != NULL &&
+ cast->opening_loc.start != NULL
+ ) &&
+ // (4) foo.bar do end
+ !(
+ cast->block != NULL && PM_NODE_TYPE_P(cast->block, PM_BLOCK_NODE)
+ )
+ ) {
+ accepts_command_call = false;
+ }
+ break;
+ }
+ // (5) foo::Bar
+ case PM_CONSTANT_PATH_NODE:
+ break;
+ default:
+ accepts_command_call = false;
+ break;
+ }
+ }
+ }
+
+ return node;
+}
+
+/**
+ * ruby -p, ruby -n, ruby -a, and ruby -l options will mutate the AST. We
+ * perform that mutation here.
+ */
+static pm_statements_node_t *
+wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) {
+ if (PM_PARSER_COMMAND_LINE_OPTION_P(parser)) {
+ pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
+ pm_arguments_node_arguments_append(
+ arguments,
+ (pm_node_t *) pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2))
+ );
+
+ pm_statements_node_body_append(parser, statements, (pm_node_t *) pm_call_node_fcall_synthesized_create(
+ parser,
+ arguments,
+ pm_parser_constant_id_constant(parser, "print", 5)
+ ));
+ }
+
+ if (PM_PARSER_COMMAND_LINE_OPTION_N(parser)) {
+ if (PM_PARSER_COMMAND_LINE_OPTION_A(parser)) {
+ pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
+ pm_arguments_node_arguments_append(
+ arguments,
+ (pm_node_t *) pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$;", 2))
+ );
+
+ pm_global_variable_read_node_t *receiver = pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2));
+ pm_call_node_t *call = pm_call_node_call_synthesized_create(parser, (pm_node_t *) receiver, "split", arguments);
+
+ pm_global_variable_write_node_t *write = pm_global_variable_write_node_synthesized_create(
+ parser,
+ pm_parser_constant_id_constant(parser, "$F", 2),
+ (pm_node_t *) call
+ );
+
+ pm_statements_node_body_prepend(statements, (pm_node_t *) write);
+ }
+
+ pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
+ pm_arguments_node_arguments_append(
+ arguments,
+ (pm_node_t *) pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$/", 2))
+ );
+
+ if (PM_PARSER_COMMAND_LINE_OPTION_L(parser)) {
+ pm_keyword_hash_node_t *keywords = pm_keyword_hash_node_create(parser);
+ pm_keyword_hash_node_elements_append(keywords, (pm_node_t *) pm_assoc_node_create(
+ parser,
+ (pm_node_t *) pm_symbol_node_synthesized_create(parser, "chomp"),
+ &(pm_token_t) { .type = PM_TOKEN_NOT_PROVIDED, .start = parser->start, .end = parser->start },
+ (pm_node_t *) pm_true_node_synthesized_create(parser)
+ ));
+
+ pm_arguments_node_arguments_append(arguments, (pm_node_t *) keywords);
+ }
+
+ pm_statements_node_t *wrapped_statements = pm_statements_node_create(parser);
+ pm_statements_node_body_append(parser, wrapped_statements, (pm_node_t *) pm_while_node_synthesized_create(
+ parser,
+ (pm_node_t *) pm_call_node_fcall_synthesized_create(parser, arguments, pm_parser_constant_id_constant(parser, "gets", 4)),
+ statements
+ ));
+
+ statements = wrapped_statements;
+ }
+
+ return statements;
+}
+
+/**
+ * Parse the top-level program node.
+ */
+static pm_node_t *
+parse_program(pm_parser_t *parser) {
+ // If the current scope is NULL, then we want to push a new top level scope.
+ // The current scope could exist in the event that we are parsing an eval
+ // and the user has passed into scopes that already exist.
+ if (parser->current_scope == NULL) {
+ pm_parser_scope_push(parser, true);
+ }
+
+ parser_lex(parser);
+ pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_MAIN);
+
+ if (statements == NULL) {
+ statements = pm_statements_node_create(parser);
+ } else if (!parser->parsing_eval) {
+ // If we have statements, then the top-level statement should be
+ // explicitly checked as well. We have to do this here because
+ // everywhere else we check all but the last statement.
+ assert(statements->body.size > 0);
+ pm_void_statement_check(parser, statements->body.nodes[statements->body.size - 1]);
+ }
+
+ pm_constant_id_list_t locals;
+ pm_locals_order(parser, &parser->current_scope->locals, &locals, true);
+ pm_parser_scope_pop(parser);
+
+ // If this is an empty file, then we're still going to parse all of the
+ // statements in order to gather up all of the comments and such. Here we'll
+ // correct the location information.
+ if (pm_statements_node_body_length(statements) == 0) {
+ pm_statements_node_location_set(statements, parser->start, parser->start);
+ }
+
+ // At the top level, see if we need to wrap the statements in a program
+ // node with a while loop based on the options.
+ if (parser->command_line & (PM_OPTIONS_COMMAND_LINE_P | PM_OPTIONS_COMMAND_LINE_N)) {
+ statements = wrap_statements(parser, statements);
+ }
+
+ return (pm_node_t *) pm_program_node_create(parser, &locals, statements);
+}
+
+/******************************************************************************/
+/* External functions */
+/******************************************************************************/
+
+/**
+ * A vendored version of strnstr that is used to find a substring within a
+ * string with a given length. This function is used to search for the Ruby
+ * engine name within a shebang when the -x option is passed to Ruby.
+ *
+ * The only modification that we made here is that we don't do NULL byte checks
+ * because we know the little parameter will not have a NULL byte and we allow
+ * the big parameter to have them.
+ */
+static const char *
+pm_strnstr(const char *big, const char *little, size_t big_length) {
+ size_t little_length = strlen(little);
+
+ for (const char *big_end = big + big_length; big < big_end; big++) {
+ if (*big == *little && memcmp(big, little, little_length) == 0) return big;
+ }
+
+ return NULL;
+}
+
+/**
+ * Potentially warn the user if the shebang that has been found to include
+ * "ruby" has a carriage return at the end, as that can cause problems on some
+ * platforms.
+ */
+static void
+pm_parser_warn_shebang_carriage_return(pm_parser_t *parser, const uint8_t *start, size_t length) {
+ if (length > 2 && start[length - 1] == '\n' && start[length - 2] == '\r') {
+ pm_parser_warn(parser, start, start + length, PM_WARN_SHEBANG_CARRIAGE_RETURN);
+ }
+}
+
+/**
+ * Initialize a parser with the given start and end pointers.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm_options_t *options) {
+ assert(source != NULL);
+
+ *parser = (pm_parser_t) {
+ .lex_state = PM_LEX_STATE_BEG,
+ .enclosure_nesting = 0,
+ .lambda_enclosure_nesting = -1,
+ .brace_nesting = 0,
+ .do_loop_stack = 0,
+ .accepts_block_stack = 0,
+ .lex_modes = {
+ .index = 0,
+ .stack = {{ .mode = PM_LEX_DEFAULT }},
+ .current = &parser->lex_modes.stack[0],
+ },
+ .start = source,
+ .end = source + size,
+ .previous = { .type = PM_TOKEN_EOF, .start = source, .end = source },
+ .current = { .type = PM_TOKEN_EOF, .start = source, .end = source },
+ .next_start = NULL,
+ .heredoc_end = NULL,
+ .data_loc = { .start = NULL, .end = NULL },
+ .comment_list = { 0 },
+ .magic_comment_list = { 0 },
+ .warning_list = { 0 },
+ .error_list = { 0 },
+ .current_scope = NULL,
+ .current_context = NULL,
+ .encoding = PM_ENCODING_UTF_8_ENTRY,
+ .encoding_changed_callback = NULL,
+ .encoding_comment_start = source,
+ .lex_callback = NULL,
+ .filepath = { 0 },
+ .constant_pool = { 0 },
+ .newline_list = { 0 },
+ .integer_base = 0,
+ .current_string = PM_STRING_EMPTY,
+ .start_line = 1,
+ .explicit_encoding = NULL,
+ .command_line = 0,
+ .parsing_eval = false,
+ .command_start = true,
+ .recovering = false,
+ .encoding_changed = false,
+ .pattern_matching_newlines = false,
+ .in_keyword_arg = false,
+ .current_block_exits = NULL,
+ .semantic_token_seen = false,
+ .frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET,
+ .current_regular_expression_ascii_only = false
+ };
+
+ // Initialize the constant pool. We're going to completely guess as to the
+ // number of constants that we'll need based on the size of the input. The
+ // ratio we chose here is actually less arbitrary than you might think.
+ //
+ // We took ~50K Ruby files and measured the size of the file versus the
+ // number of constants that were found in those files. Then we found the
+ // average and standard deviation of the ratios of constants/bytesize. Then
+ // we added 1.34 standard deviations to the average to get a ratio that
+ // would fit 75% of the files (for a two-tailed distribution). This works
+ // because there was about a 0.77 correlation and the distribution was
+ // roughly normal.
+ //
+ // This ratio will need to change if we add more constants to the constant
+ // pool for another node type.
+ uint32_t constant_size = ((uint32_t) size) / 95;
+ pm_constant_pool_init(&parser->constant_pool, constant_size < 4 ? 4 : constant_size);
+
+ // Initialize the newline list. Similar to the constant pool, we're going to
+ // guess at the number of newlines that we'll need based on the size of the
+ // input.
+ size_t newline_size = size / 22;
+ pm_newline_list_init(&parser->newline_list, source, newline_size < 4 ? 4 : newline_size);
+
+ // If options were provided to this parse, establish them here.
+ if (options != NULL) {
+ // filepath option
+ parser->filepath = options->filepath;
+
+ // line option
+ parser->start_line = options->line;
+
+ // encoding option
+ size_t encoding_length = pm_string_length(&options->encoding);
+ if (encoding_length > 0) {
+ const uint8_t *encoding_source = pm_string_source(&options->encoding);
+ parser_lex_magic_comment_encoding_value(parser, encoding_source, encoding_source + encoding_length);
+ }
+
+ // frozen_string_literal option
+ parser->frozen_string_literal = options->frozen_string_literal;
+
+ // command_line option
+ parser->command_line = options->command_line;
+
+ // version option
+ parser->version = options->version;
+
+ // scopes option
+ parser->parsing_eval = options->scopes_count > 0;
+
+ for (size_t scope_index = 0; scope_index < options->scopes_count; scope_index++) {
+ const pm_options_scope_t *scope = pm_options_scope_get(options, scope_index);
+ pm_parser_scope_push(parser, scope_index == 0);
+
+ // Scopes given from the outside are not allowed to have numbered
+ // parameters.
+ parser->current_scope->numbered_parameters = PM_SCOPE_NUMBERED_PARAMETERS_DISALLOWED;
+
+ for (size_t local_index = 0; local_index < scope->locals_count; local_index++) {
+ const pm_string_t *local = pm_options_scope_local_get(scope, local_index);
+
+ const uint8_t *source = pm_string_source(local);
+ size_t length = pm_string_length(local);
+
+ void *allocated = xmalloc(length);
+ if (allocated == NULL) continue;
+
+ memcpy(allocated, source, length);
+ pm_parser_local_add_owned(parser, (uint8_t *) allocated, length);
+ }
+ }
+ }
+
+ pm_accepts_block_stack_push(parser, true);
+
+ // Skip past the UTF-8 BOM if it exists.
+ if (size >= 3 && source[0] == 0xef && source[1] == 0xbb && source[2] == 0xbf) {
+ parser->current.end += 3;
+ parser->encoding_comment_start += 3;
+
+ if (parser->encoding != PM_ENCODING_UTF_8_ENTRY) {
+ parser->encoding = PM_ENCODING_UTF_8_ENTRY;
+ if (parser->encoding_changed_callback != NULL) parser->encoding_changed_callback(parser);
+ }
+ }
+
+ // If the -x command line flag is set, or the first shebang of the file does
+ // not include "ruby", then we'll search for a shebang that does include
+ // "ruby" and start parsing from there.
+ bool search_shebang = PM_PARSER_COMMAND_LINE_OPTION_X(parser);
+
+ // If the first two bytes of the source are a shebang, then we'll indicate
+ // that the encoding comment is at the end of the shebang.
+ if (peek(parser) == '#' && peek_offset(parser, 1) == '!') {
+ const uint8_t *newline = next_newline(parser->start, parser->end - parser->start);
+ size_t length = (size_t) ((newline != NULL ? newline : parser->end) - parser->start);
+
+ if (pm_strnstr((const char *) parser->start, "ruby", length) != NULL) {
+ pm_parser_warn_shebang_carriage_return(parser, parser->start, length);
+ if (newline != NULL) parser->encoding_comment_start = newline + 1;
+ search_shebang = false;
+ } else {
+ search_shebang = true;
+ }
+ }
+
+ // Here we're going to find the first shebang that includes "ruby" and start
+ // parsing from there.
+ if (search_shebang) {
+ // If a shebang that includes "ruby" is not found, then we're going to a
+ // a load error to the list of errors on the parser.
+ bool found_shebang = false;
+
+ // This is going to point to the start of each line as we check it.
+ // We'll maintain a moving window looking at each line at they come.
+ const uint8_t *cursor = parser->start;
+
+ // The newline pointer points to the end of the current line that we're
+ // considering. If it is NULL, then we're at the end of the file.
+ const uint8_t *newline = next_newline(cursor, parser->end - cursor);
+
+ while (newline != NULL) {
+ pm_newline_list_append(&parser->newline_list, newline);
+
+ cursor = newline + 1;
+ newline = next_newline(cursor, parser->end - cursor);
+
+ size_t length = (size_t) ((newline != NULL ? newline : parser->end) - cursor);
+ if (length > 2 && cursor[0] == '#' && cursor[1] == '!') {
+ if (parser->newline_list.size == 1) {
+ pm_parser_warn_shebang_carriage_return(parser, cursor, length);
+ }
+
+ if (pm_strnstr((const char *) cursor, "ruby", length) != NULL) {
+ found_shebang = true;
+ parser->encoding_comment_start = newline + 1;
+ break;
+ }
+ }
+ }
+
+ if (found_shebang) {
+ parser->previous = (pm_token_t) { .type = PM_TOKEN_EOF, .start = cursor, .end = cursor };
+ parser->current = (pm_token_t) { .type = PM_TOKEN_EOF, .start = cursor, .end = cursor };
+ } else {
+ pm_parser_err(parser, parser->start, parser->start, PM_ERR_SCRIPT_NOT_FOUND);
+ pm_newline_list_clear(&parser->newline_list);
+ }
+ }
+
+ // The encoding comment can start after any amount of inline whitespace, so
+ // here we'll advance it to the first non-inline-whitespace character so
+ // that it is ready for future comparisons.
+ parser->encoding_comment_start += pm_strspn_inline_whitespace(parser->encoding_comment_start, parser->end - parser->encoding_comment_start);
+}
+
+/**
+ * Register a callback that will be called whenever prism changes the encoding
+ * it is using to parse based on the magic comment.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_parser_register_encoding_changed_callback(pm_parser_t *parser, pm_encoding_changed_callback_t callback) {
+ parser->encoding_changed_callback = callback;
+}
+
+/**
+ * Free all of the memory associated with the comment list.
+ */
+static inline void
+pm_comment_list_free(pm_list_t *list) {
+ pm_list_node_t *node, *next;
+
+ for (node = list->head; node != NULL; node = next) {
+ next = node->next;
+
+ pm_comment_t *comment = (pm_comment_t *) node;
+ xfree(comment);
+ }
+}
+
+/**
+ * Free all of the memory associated with the magic comment list.
+ */
+static inline void
+pm_magic_comment_list_free(pm_list_t *list) {
+ pm_list_node_t *node, *next;
+
+ for (node = list->head; node != NULL; node = next) {
+ next = node->next;
+
+ pm_magic_comment_t *magic_comment = (pm_magic_comment_t *) node;
+ xfree(magic_comment);
+ }
+}
+
+/**
+ * Free any memory associated with the given parser.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_parser_free(pm_parser_t *parser) {
+ pm_string_free(&parser->filepath);
+ pm_diagnostic_list_free(&parser->error_list);
+ pm_diagnostic_list_free(&parser->warning_list);
+ pm_comment_list_free(&parser->comment_list);
+ pm_magic_comment_list_free(&parser->magic_comment_list);
+ pm_constant_pool_free(&parser->constant_pool);
+ pm_newline_list_free(&parser->newline_list);
+
+ while (parser->current_scope != NULL) {
+ // Normally, popping the scope doesn't free the locals since it is
+ // assumed that ownership has transferred to the AST. However if we have
+ // scopes while we're freeing the parser, it's likely they came from
+ // eval scopes and we need to free them explicitly here.
+ pm_parser_scope_pop(parser);
+ }
+
+ while (parser->lex_modes.index >= PM_LEX_STACK_SIZE) {
+ lex_mode_pop(parser);
+ }
+}
+
+/**
+ * Parse the Ruby source associated with the given parser and return the tree.
+ */
+PRISM_EXPORTED_FUNCTION pm_node_t *
+pm_parse(pm_parser_t *parser) {
+ return parse_program(parser);
+}
+
+/**
+ * Read into the stream until the gets callback returns false. If the last read
+ * line from the stream matches an __END__ marker, then halt and return false,
+ * otherwise return true.
+ */
+static bool
+pm_parse_stream_read(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *fgets) {
+#define LINE_SIZE 4096
+ char line[LINE_SIZE];
+
+ while (fgets(line, LINE_SIZE, stream) != NULL) {
+ size_t length = strlen(line);
+
+ if (length == LINE_SIZE && line[length - 1] != '\n') {
+ // If we read a line that is the maximum size and it doesn't end
+ // with a newline, then we'll just append it to the buffer and
+ // continue reading.
+ pm_buffer_append_string(buffer, line, length);
+ continue;
+ }
+
+ // Append the line to the buffer.
+ pm_buffer_append_string(buffer, line, length);
+
+ // Check if the line matches the __END__ marker. If it does, then stop
+ // reading and return false. In most circumstances, this means we should
+ // stop reading from the stream so that the DATA constant can pick it
+ // up.
+ switch (length) {
+ case 7:
+ if (strncmp(line, "__END__", 7) == 0) return false;
+ break;
+ case 8:
+ if (strncmp(line, "__END__\n", 8) == 0) return false;
+ break;
+ case 9:
+ if (strncmp(line, "__END__\r\n", 9) == 0) return false;
+ break;
+ }
+ }
+
+ return true;
+#undef LINE_SIZE
+}
+
+/**
+ * Determine if there was an unterminated heredoc at the end of the input, which
+ * would mean the stream isn't finished and we should keep reading.
+ *
+ * For the other lex modes we can check if the lex mode has been closed, but for
+ * heredocs when we hit EOF we close the lex mode and then go back to parse the
+ * rest of the line after the heredoc declaration so that we get more of the
+ * syntax tree.
+ */
+static bool
+pm_parse_stream_unterminated_heredoc_p(pm_parser_t *parser) {
+ pm_diagnostic_t *diagnostic = (pm_diagnostic_t *) parser->error_list.head;
+
+ for (; diagnostic != NULL; diagnostic = (pm_diagnostic_t *) diagnostic->node.next) {
+ if (diagnostic->diag_id == PM_ERR_HEREDOC_TERM) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Parse a stream of Ruby source and return the tree.
+ *
+ * Prism is designed around having the entire source in memory at once, but you
+ * can stream stdin in to Ruby so we need to support a streaming API.
+ */
+PRISM_EXPORTED_FUNCTION pm_node_t *
+pm_parse_stream(pm_parser_t *parser, pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *fgets, const pm_options_t *options) {
+ pm_buffer_init(buffer);
+
+ bool eof = pm_parse_stream_read(buffer, stream, fgets);
+ pm_parser_init(parser, (const uint8_t *) pm_buffer_value(buffer), pm_buffer_length(buffer), options);
+ pm_node_t *node = pm_parse(parser);
+
+ while (!eof && parser->error_list.size > 0 && (parser->lex_modes.index > 0 || pm_parse_stream_unterminated_heredoc_p(parser))) {
+ pm_node_destroy(parser, node);
+ eof = pm_parse_stream_read(buffer, stream, fgets);
+
+ pm_parser_free(parser);
+ pm_parser_init(parser, (const uint8_t *) pm_buffer_value(buffer), pm_buffer_length(buffer), options);
+ node = pm_parse(parser);
+ }
+
+ return node;
+}
+
+/**
+ * Parse the source and return true if it parses without errors or warnings.
+ */
+PRISM_EXPORTED_FUNCTION bool
+pm_parse_success_p(const uint8_t *source, size_t size, const char *data) {
+ pm_options_t options = { 0 };
+ pm_options_read(&options, data);
+
+ pm_parser_t parser;
+ pm_parser_init(&parser, source, size, &options);
+
+ pm_node_t *node = pm_parse(&parser);
+ pm_node_destroy(&parser, node);
+
+ bool result = parser.error_list.size == 0;
+ pm_parser_free(&parser);
+ pm_options_free(&options);
+
+ return result;
+}
+
+#undef PM_CASE_KEYWORD
+#undef PM_CASE_OPERATOR
+#undef PM_CASE_WRITABLE
+#undef PM_STRING_EMPTY
+#undef PM_LOCATION_NODE_BASE_VALUE
+#undef PM_LOCATION_NODE_VALUE
+#undef PM_LOCATION_NULL_VALUE
+#undef PM_LOCATION_TOKEN_VALUE
+
+// We optionally support serializing to a binary string. For systems that don't
+// want or need this functionality, it can be turned off with the
+// PRISM_EXCLUDE_SERIALIZATION define.
+#ifndef PRISM_EXCLUDE_SERIALIZATION
+
+static inline void
+pm_serialize_header(pm_buffer_t *buffer) {
+ pm_buffer_append_string(buffer, "PRISM", 5);
+ pm_buffer_append_byte(buffer, PRISM_VERSION_MAJOR);
+ pm_buffer_append_byte(buffer, PRISM_VERSION_MINOR);
+ pm_buffer_append_byte(buffer, PRISM_VERSION_PATCH);
+ pm_buffer_append_byte(buffer, PRISM_SERIALIZE_ONLY_SEMANTICS_FIELDS ? 1 : 0);
+}
+
+/**
+ * Serialize the AST represented by the given node to the given buffer.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_serialize(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) {
+ pm_serialize_header(buffer);
+ pm_serialize_content(parser, node, buffer);
+ pm_buffer_append_byte(buffer, '\0');
+}
+
+/**
+ * Parse and serialize the AST represented by the given source to the given
+ * buffer.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_serialize_parse(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data) {
+ pm_options_t options = { 0 };
+ pm_options_read(&options, data);
+
+ pm_parser_t parser;
+ pm_parser_init(&parser, source, size, &options);
+
+ pm_node_t *node = pm_parse(&parser);
+
+ pm_serialize_header(buffer);
+ pm_serialize_content(&parser, node, buffer);
+ pm_buffer_append_byte(buffer, '\0');
+
+ pm_node_destroy(&parser, node);
+ pm_parser_free(&parser);
+ pm_options_free(&options);
+}
+
+/**
+ * Parse and serialize the AST represented by the source that is read out of the
+ * given stream into to the given buffer.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_serialize_parse_stream(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *fgets, const char *data) {
+ pm_parser_t parser;
+ pm_options_t options = { 0 };
+ pm_options_read(&options, data);
+
+ pm_buffer_t parser_buffer;
+ pm_node_t *node = pm_parse_stream(&parser, &parser_buffer, stream, fgets, &options);
+ pm_serialize_header(buffer);
+ pm_serialize_content(&parser, node, buffer);
+ pm_buffer_append_byte(buffer, '\0');
+
+ pm_node_destroy(&parser, node);
+ pm_buffer_free(&parser_buffer);
+ pm_parser_free(&parser);
+ pm_options_free(&options);
+}
+
+/**
+ * Parse and serialize the comments in the given source to the given buffer.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_serialize_parse_comments(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data) {
+ pm_options_t options = { 0 };
+ pm_options_read(&options, data);
+
+ pm_parser_t parser;
+ pm_parser_init(&parser, source, size, &options);
+
+ pm_node_t *node = pm_parse(&parser);
+ pm_serialize_header(buffer);
+ pm_serialize_encoding(parser.encoding, buffer);
+ pm_buffer_append_varsint(buffer, parser.start_line);
+ pm_serialize_comment_list(&parser, &parser.comment_list, buffer);
+
+ pm_node_destroy(&parser, node);
+ pm_parser_free(&parser);
+ pm_options_free(&options);
+}
+
+#endif
+
+/** An error that is going to be formatted into the output. */
+typedef struct {
+ /** A pointer to the diagnostic that was generated during parsing. */
+ pm_diagnostic_t *error;
+
+ /** The start line of the diagnostic message. */
+ int32_t line;
+
+ /** The column start of the diagnostic message. */
+ uint32_t column_start;
+
+ /** The column end of the diagnostic message. */
+ uint32_t column_end;
+} pm_error_t;
+
+/** The format that will be used to format the errors into the output. */
+typedef struct {
+ /** The prefix that will be used for line numbers. */
+ const char *number_prefix;
+
+ /** The prefix that will be used for blank lines. */
+ const char *blank_prefix;
+
+ /** The divider that will be used between sections of source code. */
+ const char *divider;
+
+ /** The length of the blank prefix. */
+ size_t blank_prefix_length;
+
+ /** The length of the divider. */
+ size_t divider_length;
+} pm_error_format_t;
+
+#define PM_COLOR_GRAY "\033[38;5;102m"
+#define PM_COLOR_RED "\033[1;31m"
+#define PM_COLOR_RESET "\033[m"
+
+static inline pm_error_t *
+pm_parser_errors_format_sort(const pm_parser_t *parser, const pm_list_t *error_list, const pm_newline_list_t *newline_list) {
+ pm_error_t *errors = xcalloc(error_list->size, sizeof(pm_error_t));
+ if (errors == NULL) return NULL;
+
+ int32_t start_line = parser->start_line;
+ for (pm_diagnostic_t *error = (pm_diagnostic_t *) error_list->head; error != NULL; error = (pm_diagnostic_t *) error->node.next) {
+ pm_line_column_t start = pm_newline_list_line_column(newline_list, error->location.start, start_line);
+ pm_line_column_t end = pm_newline_list_line_column(newline_list, error->location.end, start_line);
+
+ // We're going to insert this error into the array in sorted order. We
+ // do this by finding the first error that has a line number greater
+ // than the current error and then inserting the current error before
+ // that one.
+ size_t index = 0;
+ while (
+ (index < error_list->size) &&
+ (errors[index].error != NULL) &&
+ (
+ (errors[index].line < start.line) ||
+ ((errors[index].line == start.line) && (errors[index].column_start < start.column))
+ )
+ ) index++;
+
+ // Now we're going to shift all of the errors after this one down one
+ // index to make room for the new error.
+ if (index + 1 < error_list->size) {
+ memmove(&errors[index + 1], &errors[index], sizeof(pm_error_t) * (error_list->size - index - 1));
+ }
+
+ // Finally, we'll insert the error into the array.
+ uint32_t column_end;
+ if (start.line == end.line) {
+ column_end = end.column;
+ } else {
+ column_end = (uint32_t) (newline_list->offsets[start.line - start_line + 1] - newline_list->offsets[start.line - start_line] - 1);
+ }
+
+ // Ensure we have at least one column of error.
+ if (start.column == column_end) column_end++;
+
+ errors[index] = (pm_error_t) {
+ .error = error,
+ .line = start.line,
+ .column_start = start.column,
+ .column_end = column_end
+ };
+ }
+
+ return errors;
+}
+
+static inline void
+pm_parser_errors_format_line(const pm_parser_t *parser, const pm_newline_list_t *newline_list, const char *number_prefix, int32_t line, pm_buffer_t *buffer) {
+ int32_t line_delta = line - parser->start_line;
+ assert(line_delta >= 0);
+
+ size_t index = (size_t) line_delta;
+ assert(index < newline_list->size);
+
+ const uint8_t *start = &parser->start[newline_list->offsets[index]];
+ const uint8_t *end;
+
+ if (index >= newline_list->size - 1) {
+ end = parser->end;
+ } else {
+ end = &parser->start[newline_list->offsets[index + 1]];
+ }
+
+ pm_buffer_append_format(buffer, number_prefix, line);
+ pm_buffer_append_string(buffer, (const char *) start, (size_t) (end - start));
+
+ if (end == parser->end && end[-1] != '\n') {
+ pm_buffer_append_string(buffer, "\n", 1);
+ }
+}
+
+/**
+ * Format the errors on the parser into the given buffer.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_parser_errors_format(const pm_parser_t *parser, const pm_list_t *error_list, pm_buffer_t *buffer, bool colorize, bool inline_messages) {
+ assert(error_list->size != 0);
+
+ // First, we're going to sort all of the errors by line number using an
+ // insertion sort into a newly allocated array.
+ const int32_t start_line = parser->start_line;
+ const pm_newline_list_t *newline_list = &parser->newline_list;
+
+ pm_error_t *errors = pm_parser_errors_format_sort(parser, error_list, newline_list);
+ if (errors == NULL) return;
+
+ // Now we're going to determine how we're going to format line numbers and
+ // blank lines based on the maximum number of digits in the line numbers
+ // that are going to be displaid.
+ pm_error_format_t error_format;
+ int32_t first_line_number = errors[0].line;
+ int32_t last_line_number = errors[error_list->size - 1].line;
+
+ // If we have a maximum line number that is negative, then we're going to
+ // use the absolute value for comparison but multiple by 10 to additionally
+ // have a column for the negative sign.
+ if (first_line_number < 0) first_line_number = (-first_line_number) * 10;
+ if (last_line_number < 0) last_line_number = (-last_line_number) * 10;
+ int32_t max_line_number = first_line_number > last_line_number ? first_line_number : last_line_number;
+
+ if (max_line_number < 10) {
+ if (colorize) {
+ error_format = (pm_error_format_t) {
+ .number_prefix = PM_COLOR_GRAY "%1" PRIi32 " | " PM_COLOR_RESET,
+ .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
+ .divider = PM_COLOR_GRAY " ~~~~~" PM_COLOR_RESET "\n"
+ };
+ } else {
+ error_format = (pm_error_format_t) {
+ .number_prefix = "%1" PRIi32 " | ",
+ .blank_prefix = " | ",
+ .divider = " ~~~~~\n"
+ };
+ }
+ } else if (max_line_number < 100) {
+ if (colorize) {
+ error_format = (pm_error_format_t) {
+ .number_prefix = PM_COLOR_GRAY "%2" PRIi32 " | " PM_COLOR_RESET,
+ .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
+ .divider = PM_COLOR_GRAY " ~~~~~~" PM_COLOR_RESET "\n"
+ };
+ } else {
+ error_format = (pm_error_format_t) {
+ .number_prefix = "%2" PRIi32 " | ",
+ .blank_prefix = " | ",
+ .divider = " ~~~~~~\n"
+ };
+ }
+ } else if (max_line_number < 1000) {
+ if (colorize) {
+ error_format = (pm_error_format_t) {
+ .number_prefix = PM_COLOR_GRAY "%3" PRIi32 " | " PM_COLOR_RESET,
+ .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
+ .divider = PM_COLOR_GRAY " ~~~~~~~" PM_COLOR_RESET "\n"
+ };
+ } else {
+ error_format = (pm_error_format_t) {
+ .number_prefix = "%3" PRIi32 " | ",
+ .blank_prefix = " | ",
+ .divider = " ~~~~~~~\n"
+ };
+ }
+ } else if (max_line_number < 10000) {
+ if (colorize) {
+ error_format = (pm_error_format_t) {
+ .number_prefix = PM_COLOR_GRAY "%4" PRIi32 " | " PM_COLOR_RESET,
+ .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
+ .divider = PM_COLOR_GRAY " ~~~~~~~~" PM_COLOR_RESET "\n"
+ };
+ } else {
+ error_format = (pm_error_format_t) {
+ .number_prefix = "%4" PRIi32 " | ",
+ .blank_prefix = " | ",
+ .divider = " ~~~~~~~~\n"
+ };
+ }
+ } else {
+ if (colorize) {
+ error_format = (pm_error_format_t) {
+ .number_prefix = PM_COLOR_GRAY "%5" PRIi32 " | " PM_COLOR_RESET,
+ .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
+ .divider = PM_COLOR_GRAY " ~~~~~~~~" PM_COLOR_RESET "\n"
+ };
+ } else {
+ error_format = (pm_error_format_t) {
+ .number_prefix = "%5" PRIi32 " | ",
+ .blank_prefix = " | ",
+ .divider = " ~~~~~~~~\n"
+ };
+ }
+ }
+
+ error_format.blank_prefix_length = strlen(error_format.blank_prefix);
+ error_format.divider_length = strlen(error_format.divider);
+
+ // Now we're going to iterate through every error in our error list and
+ // display it. While we're iterating, we will display some padding lines of
+ // the source before the error to give some context. We'll be careful not to
+ // display the same line twice in case the errors are close enough in the
+ // source.
+ int32_t last_line = parser->start_line - 1;
+ const pm_encoding_t *encoding = parser->encoding;
+
+ for (size_t index = 0; index < error_list->size; index++) {
+ pm_error_t *error = &errors[index];
+
+ // Here we determine how many lines of padding of the source to display,
+ // based on the difference from the last line that was displaid.
+ if (error->line - last_line > 1) {
+ if (error->line - last_line > 2) {
+ if ((index != 0) && (error->line - last_line > 3)) {
+ pm_buffer_append_string(buffer, error_format.divider, error_format.divider_length);
+ }
+
+ pm_buffer_append_string(buffer, " ", 2);
+ pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, error->line - 2, buffer);
+ }
+
+ pm_buffer_append_string(buffer, " ", 2);
+ pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, error->line - 1, buffer);
+ }
+
+ // If this is the first error or we're on a new line, then we'll display
+ // the line that has the error in it.
+ if ((index == 0) || (error->line != last_line)) {
+ if (colorize) {
+ pm_buffer_append_string(buffer, PM_COLOR_RED "> " PM_COLOR_RESET, 12);
+ } else {
+ pm_buffer_append_string(buffer, "> ", 2);
+ }
+ pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, error->line, buffer);
+ }
+
+ const uint8_t *start = &parser->start[newline_list->offsets[error->line - start_line]];
+ if (start == parser->end) pm_buffer_append_byte(buffer, '\n');
+
+ // Now we'll display the actual error message. We'll do this by first
+ // putting the prefix to the line, then a bunch of blank spaces
+ // depending on the column, then as many carets as we need to display
+ // the width of the error, then the error message itself.
+ //
+ // Note that this doesn't take into account the width of the actual
+ // character when displaid in the terminal. For some east-asian
+ // languages or emoji, this means it can be thrown off pretty badly. We
+ // will need to solve this eventually.
+ pm_buffer_append_string(buffer, " ", 2);
+ pm_buffer_append_string(buffer, error_format.blank_prefix, error_format.blank_prefix_length);
+
+ size_t column = 0;
+ while (column < error->column_end) {
+ if (column < error->column_start) {
+ pm_buffer_append_byte(buffer, ' ');
+ } else {
+ const uint8_t caret = column == error->column_start ? '^' : '~';
+
+ if (colorize) {
+ pm_buffer_append_string(buffer, PM_COLOR_RED, 7);
+ pm_buffer_append_byte(buffer, caret);
+ pm_buffer_append_string(buffer, PM_COLOR_RESET, 3);
+ } else {
+ pm_buffer_append_byte(buffer, caret);
+ }
+ }
+
+ size_t char_width = encoding->char_width(start + column, parser->end - (start + column));
+ column += (char_width == 0 ? 1 : char_width);
+ }
+
+ if (inline_messages) {
+ pm_buffer_append_byte(buffer, ' ');
+ assert(error->error != NULL);
+
+ const char *message = error->error->message;
+ pm_buffer_append_string(buffer, message, strlen(message));
+ }
+
+ pm_buffer_append_byte(buffer, '\n');
+
+ // Here we determine how many lines of padding to display after the
+ // error, depending on where the next error is in source.
+ last_line = error->line;
+ int32_t next_line = (index == error_list->size - 1) ? (((int32_t) newline_list->size) + parser->start_line) : errors[index + 1].line;
+
+ if (next_line - last_line > 1) {
+ pm_buffer_append_string(buffer, " ", 2);
+ pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, ++last_line, buffer);
+ }
+
+ if (next_line - last_line > 1) {
+ pm_buffer_append_string(buffer, " ", 2);
+ pm_parser_errors_format_line(parser, newline_list, error_format.number_prefix, ++last_line, buffer);
+ }
+ }
+
+ // Finally, we'll free the array of errors that we allocated.
+ xfree(errors);
+}
+
+#undef PM_COLOR_GRAY
+#undef PM_COLOR_RED
+#undef PM_COLOR_RESET
diff --git a/prism/prism.h b/prism/prism.h
new file mode 100644
index 0000000000..59067c3021
--- /dev/null
+++ b/prism/prism.h
@@ -0,0 +1,347 @@
+/**
+ * @file prism.h
+ *
+ * The main header file for the prism parser.
+ */
+#ifndef PRISM_H
+#define PRISM_H
+
+#include "prism/defines.h"
+#include "prism/util/pm_buffer.h"
+#include "prism/util/pm_char.h"
+#include "prism/util/pm_integer.h"
+#include "prism/util/pm_memchr.h"
+#include "prism/util/pm_strncasecmp.h"
+#include "prism/util/pm_strpbrk.h"
+#include "prism/ast.h"
+#include "prism/diagnostic.h"
+#include "prism/node.h"
+#include "prism/options.h"
+#include "prism/pack.h"
+#include "prism/parser.h"
+#include "prism/prettyprint.h"
+#include "prism/regexp.h"
+#include "prism/static_literals.h"
+#include "prism/version.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <locale.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef _WIN32
+#include <strings.h>
+#endif
+
+/**
+ * The prism version and the serialization format.
+ *
+ * @returns The prism version as a constant string.
+ */
+PRISM_EXPORTED_FUNCTION const char * pm_version(void);
+
+/**
+ * Initialize a parser with the given start and end pointers.
+ *
+ * @param parser The parser to initialize.
+ * @param source The source to parse.
+ * @param size The size of the source.
+ * @param options The optional options to use when parsing.
+ */
+PRISM_EXPORTED_FUNCTION void pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm_options_t *options);
+
+/**
+ * Register a callback that will be called whenever prism changes the encoding
+ * it is using to parse based on the magic comment.
+ *
+ * @param parser The parser to register the callback with.
+ * @param callback The callback to register.
+ */
+PRISM_EXPORTED_FUNCTION void pm_parser_register_encoding_changed_callback(pm_parser_t *parser, pm_encoding_changed_callback_t callback);
+
+/**
+ * Free any memory associated with the given parser.
+ *
+ * @param parser The parser to free.
+ */
+PRISM_EXPORTED_FUNCTION void pm_parser_free(pm_parser_t *parser);
+
+/**
+ * Initiate the parser with the given parser.
+ *
+ * @param parser The parser to use.
+ * @return The AST representing the source.
+ */
+PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse(pm_parser_t *parser);
+
+/**
+ * This function is used in pm_parse_stream to retrieve a line of input from a
+ * stream. It closely mirrors that of fgets so that fgets can be used as the
+ * default implementation.
+ */
+typedef char * (pm_parse_stream_fgets_t)(char *string, int size, void *stream);
+
+/**
+ * Parse a stream of Ruby source and return the tree.
+ *
+ * @param parser The parser to use.
+ * @param buffer The buffer to use.
+ * @param stream The stream to parse.
+ * @param fgets The function to use to read from the stream.
+ * @param options The optional options to use when parsing.
+ * @return The AST representing the source.
+ */
+PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse_stream(pm_parser_t *parser, pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *fgets, const pm_options_t *options);
+
+// We optionally support serializing to a binary string. For systems that don't
+// want or need this functionality, it can be turned off with the
+// PRISM_EXCLUDE_SERIALIZATION define.
+#ifndef PRISM_EXCLUDE_SERIALIZATION
+
+/**
+ * Parse and serialize the AST represented by the source that is read out of the
+ * given stream into to the given buffer.
+ *
+ * @param buffer The buffer to serialize to.
+ * @param stream The stream to parse.
+ * @param fgets The function to use to read from the stream.
+ * @param data The optional data to pass to the parser.
+ */
+PRISM_EXPORTED_FUNCTION void pm_serialize_parse_stream(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *fgets, const char *data);
+
+/**
+ * Serialize the given list of comments to the given buffer.
+ *
+ * @param parser The parser to serialize.
+ * @param list The list of comments to serialize.
+ * @param buffer The buffer to serialize to.
+ */
+void pm_serialize_comment_list(pm_parser_t *parser, pm_list_t *list, pm_buffer_t *buffer);
+
+/**
+ * Serialize the name of the encoding to the buffer.
+ *
+ * @param encoding The encoding to serialize.
+ * @param buffer The buffer to serialize to.
+ */
+void pm_serialize_encoding(const pm_encoding_t *encoding, pm_buffer_t *buffer);
+
+/**
+ * Serialize the encoding, metadata, nodes, and constant pool.
+ *
+ * @param parser The parser to serialize.
+ * @param node The node to serialize.
+ * @param buffer The buffer to serialize to.
+ */
+void pm_serialize_content(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer);
+
+/**
+ * Serialize the AST represented by the given node to the given buffer.
+ *
+ * @param parser The parser to serialize.
+ * @param node The node to serialize.
+ * @param buffer The buffer to serialize to.
+ */
+PRISM_EXPORTED_FUNCTION void pm_serialize(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer);
+
+/**
+ * Parse the given source to the AST and dump the AST to the given buffer.
+ *
+ * @param buffer The buffer to serialize to.
+ * @param source The source to parse.
+ * @param size The size of the source.
+ * @param data The optional data to pass to the parser.
+ */
+PRISM_EXPORTED_FUNCTION void pm_serialize_parse(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data);
+
+/**
+ * Parse and serialize the comments in the given source to the given buffer.
+ *
+ * @param buffer The buffer to serialize to.
+ * @param source The source to parse.
+ * @param size The size of the source.
+ * @param data The optional data to pass to the parser.
+ */
+PRISM_EXPORTED_FUNCTION void pm_serialize_parse_comments(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data);
+
+/**
+ * Lex the given source and serialize to the given buffer.
+ *
+ * @param source The source to lex.
+ * @param size The size of the source.
+ * @param buffer The buffer to serialize to.
+ * @param data The optional data to pass to the lexer.
+ */
+PRISM_EXPORTED_FUNCTION void pm_serialize_lex(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data);
+
+/**
+ * Parse and serialize both the AST and the tokens represented by the given
+ * source to the given buffer.
+ *
+ * @param buffer The buffer to serialize to.
+ * @param source The source to parse.
+ * @param size The size of the source.
+ * @param data The optional data to pass to the parser.
+ */
+PRISM_EXPORTED_FUNCTION void pm_serialize_parse_lex(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data);
+
+#endif
+
+/**
+ * Parse the source and return true if it parses without errors or warnings.
+ *
+ * @param source The source to parse.
+ * @param size The size of the source.
+ * @param data The optional data to pass to the parser.
+ * @return True if the source parses without errors or warnings.
+ */
+PRISM_EXPORTED_FUNCTION bool pm_parse_success_p(const uint8_t *source, size_t size, const char *data);
+
+/**
+ * Returns a string representation of the given token type.
+ *
+ * @param token_type The token type to convert to a string.
+ * @return A string representation of the given token type.
+ */
+PRISM_EXPORTED_FUNCTION const char * pm_token_type_name(pm_token_type_t token_type);
+
+/**
+ * Returns the human name of the given token type.
+ *
+ * @param token_type The token type to convert to a human name.
+ * @return The human name of the given token type.
+ */
+const char * pm_token_type_human(pm_token_type_t token_type);
+
+/**
+ * Format the errors on the parser into the given buffer.
+ *
+ * @param parser The parser to format the errors for.
+ * @param error_list The list of errors to format.
+ * @param buffer The buffer to format the errors into.
+ * @param colorize Whether or not to colorize the errors with ANSI escape sequences.
+ * @param inline_messages Whether or not to inline the messages with the source.
+ */
+PRISM_EXPORTED_FUNCTION void pm_parser_errors_format(const pm_parser_t *parser, const pm_list_t *error_list, pm_buffer_t *buffer, bool colorize, bool inline_messages);
+
+// We optionally support dumping to JSON. For systems that don't want or need
+// this functionality, it can be turned off with the PRISM_EXCLUDE_JSON define.
+#ifndef PRISM_EXCLUDE_JSON
+
+/**
+ * Dump JSON to the given buffer.
+ *
+ * @param buffer The buffer to serialize to.
+ * @param parser The parser that parsed the node.
+ * @param node The node to serialize.
+ */
+PRISM_EXPORTED_FUNCTION void pm_dump_json(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_node_t *node);
+
+#endif
+
+/**
+ * @mainpage
+ *
+ * Prism is a parser for the Ruby programming language. It is designed to be
+ * portable, error tolerant, and maintainable. It is written in C99 and has no
+ * dependencies. It is currently being integrated into
+ * [CRuby](https://github.com/ruby/ruby),
+ * [JRuby](https://github.com/jruby/jruby),
+ * [TruffleRuby](https://github.com/oracle/truffleruby),
+ * [Sorbet](https://github.com/sorbet/sorbet), and
+ * [Syntax Tree](https://github.com/ruby-syntax-tree/syntax_tree).
+ *
+ * @section getting-started Getting started
+ *
+ * If you're vendoring this project and compiling it statically then as long as
+ * you have a C99 compiler you will be fine. If you're linking against it as
+ * shared library, then you should compile with `-fvisibility=hidden` and
+ * `-DPRISM_EXPORT_SYMBOLS` to tell prism to make only its public interface
+ * visible.
+ *
+ * @section parsing Parsing
+ *
+ * In order to parse Ruby code, the structures and functions that you're going
+ * to want to use and be aware of are:
+ *
+ * * `pm_parser_t` - the main parser structure
+ * * `pm_parser_init` - initialize a parser
+ * * `pm_parse` - parse and return the root node
+ * * `pm_node_destroy` - deallocate the root node returned by `pm_parse`
+ * * `pm_parser_free` - free the internal memory of the parser
+ *
+ * Putting all of this together would look something like:
+ *
+ * ```c
+ * void parse(const uint8_t *source, size_t length) {
+ * pm_parser_t parser;
+ * pm_parser_init(&parser, source, length, NULL);
+ *
+ * pm_node_t *root = pm_parse(&parser);
+ * printf("PARSED!\n");
+ *
+ * pm_node_destroy(&parser, root);
+ * pm_parser_free(&parser);
+ * }
+ * ```
+ *
+ * All of the nodes "inherit" from `pm_node_t` by embedding those structures as
+ * their first member. This means you can downcast and upcast any node in the
+ * tree to a `pm_node_t`.
+ *
+ * @section serializing Serializing
+ *
+ * Prism provides the ability to serialize the AST and its related metadata into
+ * a binary format. This format is designed to be portable to different
+ * languages and runtimes so that you only need to make one FFI call in order to
+ * parse Ruby code. The structures and functions that you're going to want to
+ * use and be aware of are:
+ *
+ * * `pm_buffer_t` - a small buffer object that will hold the serialized AST
+ * * `pm_buffer_free` - free the memory associated with the buffer
+ * * `pm_serialize` - serialize the AST into a buffer
+ * * `pm_serialize_parse` - parse and serialize the AST into a buffer
+ *
+ * Putting all of this together would look something like:
+ *
+ * ```c
+ * void serialize(const uint8_t *source, size_t length) {
+ * pm_buffer_t buffer = { 0 };
+ *
+ * pm_serialize_parse(&buffer, source, length, NULL);
+ * printf("SERIALIZED!\n");
+ *
+ * pm_buffer_free(&buffer);
+ * }
+ * ```
+ *
+ * @section inspecting Inspecting
+ *
+ * Prism provides the ability to inspect the AST by pretty-printing nodes. You
+ * can do this with the `pm_prettyprint` function, which you would use like:
+ *
+ * ```c
+ * void prettyprint(const uint8_t *source, size_t length) {
+ * pm_parser_t parser;
+ * pm_parser_init(&parser, source, length, NULL);
+ *
+ * pm_node_t *root = pm_parse(&parser);
+ * pm_buffer_t buffer = { 0 };
+ *
+ * pm_prettyprint(&buffer, &parser, root);
+ * printf("%*.s\n", (int) buffer.length, buffer.value);
+ *
+ * pm_buffer_free(&buffer);
+ * pm_node_destroy(&parser, root);
+ * pm_parser_free(&parser);
+ * }
+ * ```
+ */
+
+#endif
diff --git a/prism/regexp.c b/prism/regexp.c
new file mode 100644
index 0000000000..6e0fdd295c
--- /dev/null
+++ b/prism/regexp.c
@@ -0,0 +1,653 @@
+#include "prism/regexp.h"
+
+/**
+ * This is the parser that is going to handle parsing regular expressions.
+ */
+typedef struct {
+ /** A pointer to the start of the source that we are parsing. */
+ const uint8_t *start;
+
+ /** A pointer to the current position in the source. */
+ const uint8_t *cursor;
+
+ /** A pointer to the end of the source that we are parsing. */
+ const uint8_t *end;
+
+ /** A list of named captures that we've found. */
+ pm_string_list_t *named_captures;
+
+ /** Whether the encoding has changed from the default. */
+ bool encoding_changed;
+
+ /** The encoding of the source. */
+ const pm_encoding_t *encoding;
+} pm_regexp_parser_t;
+
+/**
+ * This initializes a new parser with the given source.
+ */
+static void
+pm_regexp_parser_init(pm_regexp_parser_t *parser, const uint8_t *start, const uint8_t *end, pm_string_list_t *named_captures, bool encoding_changed, const pm_encoding_t *encoding) {
+ *parser = (pm_regexp_parser_t) {
+ .start = start,
+ .cursor = start,
+ .end = end,
+ .named_captures = named_captures,
+ .encoding_changed = encoding_changed,
+ .encoding = encoding
+ };
+}
+
+/**
+ * This appends a new string to the list of named captures.
+ */
+static void
+pm_regexp_parser_named_capture(pm_regexp_parser_t *parser, const uint8_t *start, const uint8_t *end) {
+ pm_string_t string;
+ pm_string_shared_init(&string, start, end);
+ pm_string_list_append(parser->named_captures, &string);
+ pm_string_free(&string);
+}
+
+/**
+ * Returns true if the next character is the end of the source.
+ */
+static inline bool
+pm_regexp_char_is_eof(pm_regexp_parser_t *parser) {
+ return parser->cursor >= parser->end;
+}
+
+/**
+ * Optionally accept a char and consume it if it exists.
+ */
+static inline bool
+pm_regexp_char_accept(pm_regexp_parser_t *parser, uint8_t value) {
+ if (!pm_regexp_char_is_eof(parser) && *parser->cursor == value) {
+ parser->cursor++;
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Expect a character to be present and consume it.
+ */
+static inline bool
+pm_regexp_char_expect(pm_regexp_parser_t *parser, uint8_t value) {
+ if (!pm_regexp_char_is_eof(parser) && *parser->cursor == value) {
+ parser->cursor++;
+ return true;
+ }
+ return false;
+}
+
+/**
+ * This advances the current token to the next instance of the given character.
+ */
+static bool
+pm_regexp_char_find(pm_regexp_parser_t *parser, uint8_t value) {
+ if (pm_regexp_char_is_eof(parser)) {
+ return false;
+ }
+
+ const uint8_t *end = (const uint8_t *) pm_memchr(parser->cursor, value, (size_t) (parser->end - parser->cursor), parser->encoding_changed, parser->encoding);
+ if (end == NULL) {
+ return false;
+ }
+
+ parser->cursor = end + 1;
+ return true;
+}
+
+/**
+ * Range quantifiers are a special class of quantifiers that look like
+ *
+ * * {digit}
+ * * {digit,}
+ * * {digit,digit}
+ * * {,digit}
+ *
+ * Unfortunately, if there are any spaces in between, then this just becomes a
+ * regular character match expression and we have to backtrack. So when this
+ * function first starts running, we'll create a "save" point and then attempt
+ * to parse the quantifier. If it fails, we'll restore the save point and
+ * return.
+ *
+ * The properly track everything, we're going to build a little state machine.
+ * It looks something like the following:
+ *
+ * +-------+ +---------+ ------------+
+ * ---- lbrace ---> | start | ---- digit ---> | minimum | |
+ * +-------+ +---------+ <--- digit -+
+ * | | |
+ * +-------+ | | rbrace
+ * | comma | <----- comma +---- comma -------+ |
+ * +-------+ V V
+ * | +---------+ +---------+
+ * +-- digit --> | maximum | -- rbrace --> || final ||
+ * +---------+ +---------+
+ * | ^
+ * +- digit -+
+ *
+ * Note that by the time we've hit this function, the lbrace has already been
+ * consumed so we're in the start state.
+ */
+static bool
+pm_regexp_parse_range_quantifier(pm_regexp_parser_t *parser) {
+ const uint8_t *savepoint = parser->cursor;
+
+ enum {
+ PM_REGEXP_RANGE_QUANTIFIER_STATE_START,
+ PM_REGEXP_RANGE_QUANTIFIER_STATE_MINIMUM,
+ PM_REGEXP_RANGE_QUANTIFIER_STATE_MAXIMUM,
+ PM_REGEXP_RANGE_QUANTIFIER_STATE_COMMA
+ } state = PM_REGEXP_RANGE_QUANTIFIER_STATE_START;
+
+ while (1) {
+ switch (state) {
+ case PM_REGEXP_RANGE_QUANTIFIER_STATE_START:
+ switch (*parser->cursor) {
+ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
+ parser->cursor++;
+ state = PM_REGEXP_RANGE_QUANTIFIER_STATE_MINIMUM;
+ break;
+ case ',':
+ parser->cursor++;
+ state = PM_REGEXP_RANGE_QUANTIFIER_STATE_COMMA;
+ break;
+ default:
+ parser->cursor = savepoint;
+ return true;
+ }
+ break;
+ case PM_REGEXP_RANGE_QUANTIFIER_STATE_MINIMUM:
+ switch (*parser->cursor) {
+ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
+ parser->cursor++;
+ break;
+ case ',':
+ parser->cursor++;
+ state = PM_REGEXP_RANGE_QUANTIFIER_STATE_MAXIMUM;
+ break;
+ case '}':
+ parser->cursor++;
+ return true;
+ default:
+ parser->cursor = savepoint;
+ return true;
+ }
+ break;
+ case PM_REGEXP_RANGE_QUANTIFIER_STATE_COMMA:
+ switch (*parser->cursor) {
+ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
+ parser->cursor++;
+ state = PM_REGEXP_RANGE_QUANTIFIER_STATE_MAXIMUM;
+ break;
+ default:
+ parser->cursor = savepoint;
+ return true;
+ }
+ break;
+ case PM_REGEXP_RANGE_QUANTIFIER_STATE_MAXIMUM:
+ switch (*parser->cursor) {
+ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
+ parser->cursor++;
+ break;
+ case '}':
+ parser->cursor++;
+ return true;
+ default:
+ parser->cursor = savepoint;
+ return true;
+ }
+ break;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * quantifier : star-quantifier
+ * | plus-quantifier
+ * | optional-quantifier
+ * | range-quantifier
+ * | <empty>
+ * ;
+ */
+static bool
+pm_regexp_parse_quantifier(pm_regexp_parser_t *parser) {
+ if (pm_regexp_char_is_eof(parser)) return true;
+
+ switch (*parser->cursor) {
+ case '*':
+ case '+':
+ case '?':
+ parser->cursor++;
+ return true;
+ case '{':
+ parser->cursor++;
+ return pm_regexp_parse_range_quantifier(parser);
+ default:
+ // In this case there is no quantifier.
+ return true;
+ }
+}
+
+/**
+ * match-posix-class : '[' '[' ':' '^'? CHAR+ ':' ']' ']'
+ * ;
+ */
+static bool
+pm_regexp_parse_posix_class(pm_regexp_parser_t *parser) {
+ if (!pm_regexp_char_expect(parser, ':')) {
+ return false;
+ }
+
+ pm_regexp_char_accept(parser, '^');
+
+ return (
+ pm_regexp_char_find(parser, ':') &&
+ pm_regexp_char_expect(parser, ']') &&
+ pm_regexp_char_expect(parser, ']')
+ );
+}
+
+// Forward declaration because character sets can be nested.
+static bool
+pm_regexp_parse_lbracket(pm_regexp_parser_t *parser);
+
+/**
+ * match-char-set : '[' '^'? (match-range | match-char)* ']'
+ * ;
+ */
+static bool
+pm_regexp_parse_character_set(pm_regexp_parser_t *parser) {
+ pm_regexp_char_accept(parser, '^');
+
+ while (!pm_regexp_char_is_eof(parser) && *parser->cursor != ']') {
+ switch (*parser->cursor++) {
+ case '[':
+ pm_regexp_parse_lbracket(parser);
+ break;
+ case '\\':
+ if (!pm_regexp_char_is_eof(parser)) {
+ parser->cursor++;
+ }
+ break;
+ default:
+ // do nothing, we've already advanced the cursor
+ break;
+ }
+ }
+
+ return pm_regexp_char_expect(parser, ']');
+}
+
+/**
+ * A left bracket can either mean a POSIX class or a character set.
+ */
+static bool
+pm_regexp_parse_lbracket(pm_regexp_parser_t *parser) {
+ const uint8_t *reset = parser->cursor;
+
+ if ((parser->cursor + 2 < parser->end) && parser->cursor[0] == '[' && parser->cursor[1] == ':') {
+ parser->cursor++;
+ if (pm_regexp_parse_posix_class(parser)) return true;
+
+ parser->cursor = reset;
+ }
+
+ return pm_regexp_parse_character_set(parser);
+}
+
+// Forward declaration here since parsing groups needs to go back up the grammar
+// to parse expressions within them.
+static bool
+pm_regexp_parse_expression(pm_regexp_parser_t *parser);
+
+/**
+ * These are the states of the options that are configurable on the regular
+ * expression (or from within a group).
+ */
+typedef enum {
+ PM_REGEXP_OPTION_STATE_INVALID,
+ PM_REGEXP_OPTION_STATE_TOGGLEABLE,
+ PM_REGEXP_OPTION_STATE_ADDABLE,
+ PM_REGEXP_OPTION_STATE_ADDED,
+ PM_REGEXP_OPTION_STATE_REMOVED
+} pm_regexp_option_state_t;
+
+// These are the options that are configurable on the regular expression (or
+// from within a group).
+
+#define PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM 'a'
+#define PRISM_REGEXP_OPTION_STATE_SLOT_MAXIMUM 'x'
+#define PRISM_REGEXP_OPTION_STATE_SLOTS (PRISM_REGEXP_OPTION_STATE_SLOT_MAXIMUM - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM + 1)
+
+/**
+ * This is the set of options that are configurable on the regular expression.
+ */
+typedef struct {
+ /** The current state of each option. */
+ uint8_t values[PRISM_REGEXP_OPTION_STATE_SLOTS];
+} pm_regexp_options_t;
+
+/**
+ * Initialize a new set of options to their default values.
+ */
+static void
+pm_regexp_options_init(pm_regexp_options_t *options) {
+ memset(options, PM_REGEXP_OPTION_STATE_INVALID, sizeof(uint8_t) * PRISM_REGEXP_OPTION_STATE_SLOTS);
+ options->values['i' - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM] = PM_REGEXP_OPTION_STATE_TOGGLEABLE;
+ options->values['m' - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM] = PM_REGEXP_OPTION_STATE_TOGGLEABLE;
+ options->values['x' - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM] = PM_REGEXP_OPTION_STATE_TOGGLEABLE;
+ options->values['d' - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM] = PM_REGEXP_OPTION_STATE_ADDABLE;
+ options->values['a' - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM] = PM_REGEXP_OPTION_STATE_ADDABLE;
+ options->values['u' - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM] = PM_REGEXP_OPTION_STATE_ADDABLE;
+}
+
+/**
+ * Attempt to add the given option to the set of options. Returns true if it was
+ * added, false if it was already present.
+ */
+static bool
+pm_regexp_options_add(pm_regexp_options_t *options, uint8_t key) {
+ if (key >= PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM && key <= PRISM_REGEXP_OPTION_STATE_SLOT_MAXIMUM) {
+ key = (uint8_t) (key - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM);
+
+ switch (options->values[key]) {
+ case PM_REGEXP_OPTION_STATE_INVALID:
+ case PM_REGEXP_OPTION_STATE_REMOVED:
+ return false;
+ case PM_REGEXP_OPTION_STATE_TOGGLEABLE:
+ case PM_REGEXP_OPTION_STATE_ADDABLE:
+ options->values[key] = PM_REGEXP_OPTION_STATE_ADDED;
+ return true;
+ case PM_REGEXP_OPTION_STATE_ADDED:
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Attempt to remove the given option from the set of options. Returns true if
+ * it was removed, false if it was already absent.
+ */
+static bool
+pm_regexp_options_remove(pm_regexp_options_t *options, uint8_t key) {
+ if (key >= PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM && key <= PRISM_REGEXP_OPTION_STATE_SLOT_MAXIMUM) {
+ key = (uint8_t) (key - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM);
+
+ switch (options->values[key]) {
+ case PM_REGEXP_OPTION_STATE_INVALID:
+ case PM_REGEXP_OPTION_STATE_ADDABLE:
+ return false;
+ case PM_REGEXP_OPTION_STATE_TOGGLEABLE:
+ case PM_REGEXP_OPTION_STATE_ADDED:
+ case PM_REGEXP_OPTION_STATE_REMOVED:
+ options->values[key] = PM_REGEXP_OPTION_STATE_REMOVED;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Groups can have quite a few different patterns for syntax. They basically
+ * just wrap a set of expressions, but they can potentially have options after a
+ * question mark. If there _isn't_ a question mark, then it's just a set of
+ * expressions. If there _is_, then here are the options:
+ *
+ * * (?#...) - inline comments
+ * * (?:subexp) - non-capturing group
+ * * (?=subexp) - positive lookahead
+ * * (?!subexp) - negative lookahead
+ * * (?>subexp) - atomic group
+ * * (?~subexp) - absence operator
+ * * (?<=subexp) - positive lookbehind
+ * * (?<!subexp) - negative lookbehind
+ * * (?<name>subexp) - named capturing group
+ * * (?'name'subexp) - named capturing group
+ * * (?(cond)yes-subexp) - conditional expression
+ * * (?(cond)yes-subexp|no-subexp) - conditional expression
+ * * (?imxdau-imx) - turn on and off configuration
+ * * (?imxdau-imx:subexp) - turn on and off configuration for an expression
+ */
+static bool
+pm_regexp_parse_group(pm_regexp_parser_t *parser) {
+ // First, parse any options for the group.
+ if (pm_regexp_char_accept(parser, '?')) {
+ if (pm_regexp_char_is_eof(parser)) {
+ return false;
+ }
+ pm_regexp_options_t options;
+ pm_regexp_options_init(&options);
+
+ switch (*parser->cursor) {
+ case '#': { // inline comments
+ if (parser->encoding_changed && parser->encoding->multibyte) {
+ bool escaped = false;
+
+ // Here we're going to take a slow path and iterate through
+ // each multibyte character to find the close paren. We do
+ // this because \ can be a trailing byte in some encodings.
+ while (parser->cursor < parser->end) {
+ if (!escaped && *parser->cursor == ')') {
+ parser->cursor++;
+ return true;
+ }
+
+ size_t width = parser->encoding->char_width(parser->cursor, (ptrdiff_t) (parser->end - parser->cursor));
+ if (width == 0) return false;
+
+ escaped = (width == 1) && (*parser->cursor == '\\');
+ parser->cursor += width;
+ }
+
+ return false;
+ } else {
+ // Here we can take the fast path and use memchr to find the
+ // next ) because we are safe checking backward for \ since
+ // it cannot be a trailing character.
+ bool found = pm_regexp_char_find(parser, ')');
+
+ while (found && (parser->start <= parser->cursor - 2) && (*(parser->cursor - 2) == '\\')) {
+ found = pm_regexp_char_find(parser, ')');
+ }
+
+ return found;
+ }
+ }
+ case ':': // non-capturing group
+ case '=': // positive lookahead
+ case '!': // negative lookahead
+ case '>': // atomic group
+ case '~': // absence operator
+ parser->cursor++;
+ break;
+ case '<':
+ parser->cursor++;
+ if (pm_regexp_char_is_eof(parser)) {
+ return false;
+ }
+
+ switch (*parser->cursor) {
+ case '=': // positive lookbehind
+ case '!': // negative lookbehind
+ parser->cursor++;
+ break;
+ default: { // named capture group
+ const uint8_t *start = parser->cursor;
+ if (!pm_regexp_char_find(parser, '>')) {
+ return false;
+ }
+ pm_regexp_parser_named_capture(parser, start, parser->cursor - 1);
+ break;
+ }
+ }
+ break;
+ case '\'': { // named capture group
+ const uint8_t *start = ++parser->cursor;
+ if (!pm_regexp_char_find(parser, '\'')) {
+ return false;
+ }
+
+ pm_regexp_parser_named_capture(parser, start, parser->cursor - 1);
+ break;
+ }
+ case '(': // conditional expression
+ if (!pm_regexp_char_find(parser, ')')) {
+ return false;
+ }
+ break;
+ case 'i': case 'm': case 'x': case 'd': case 'a': case 'u': // options
+ while (!pm_regexp_char_is_eof(parser) && *parser->cursor != '-' && *parser->cursor != ':' && *parser->cursor != ')') {
+ if (!pm_regexp_options_add(&options, *parser->cursor)) {
+ return false;
+ }
+ parser->cursor++;
+ }
+
+ if (pm_regexp_char_is_eof(parser)) {
+ return false;
+ }
+
+ // If we hit a -, then we're done parsing options.
+ if (*parser->cursor != '-') break;
+
+ // Otherwise, fallthrough to the - case.
+ /* fallthrough */
+ case '-':
+ parser->cursor++;
+ while (!pm_regexp_char_is_eof(parser) && *parser->cursor != ':' && *parser->cursor != ')') {
+ if (!pm_regexp_options_remove(&options, *parser->cursor)) {
+ return false;
+ }
+ parser->cursor++;
+ }
+
+ if (pm_regexp_char_is_eof(parser)) {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+ }
+
+ // Now, parse the expressions within this group.
+ while (!pm_regexp_char_is_eof(parser) && *parser->cursor != ')') {
+ if (!pm_regexp_parse_expression(parser)) {
+ return false;
+ }
+ pm_regexp_char_accept(parser, '|');
+ }
+
+ // Finally, make sure we have a closing parenthesis.
+ return pm_regexp_char_expect(parser, ')');
+}
+
+/**
+ * item : anchor
+ * | match-posix-class
+ * | match-char-set
+ * | match-char-class
+ * | match-char-prop
+ * | match-char
+ * | match-any
+ * | group
+ * | quantified
+ * ;
+ */
+static bool
+pm_regexp_parse_item(pm_regexp_parser_t *parser) {
+ switch (*parser->cursor) {
+ case '^':
+ case '$':
+ parser->cursor++;
+ return true;
+ case '\\':
+ parser->cursor++;
+ if (!pm_regexp_char_is_eof(parser)) {
+ parser->cursor++;
+ }
+ return pm_regexp_parse_quantifier(parser);
+ case '(':
+ parser->cursor++;
+ return pm_regexp_parse_group(parser) && pm_regexp_parse_quantifier(parser);
+ case '[':
+ parser->cursor++;
+ return pm_regexp_parse_lbracket(parser) && pm_regexp_parse_quantifier(parser);
+ default: {
+ size_t width;
+ if (!parser->encoding_changed) {
+ width = pm_encoding_utf_8_char_width(parser->cursor, (ptrdiff_t) (parser->end - parser->cursor));
+ } else {
+ width = parser->encoding->char_width(parser->cursor, (ptrdiff_t) (parser->end - parser->cursor));
+ }
+
+ if (width == 0) return false; // TODO: add appropriate error
+ parser->cursor += width;
+
+ return pm_regexp_parse_quantifier(parser);
+ }
+ }
+}
+
+/**
+ * expression : item+
+ * ;
+ */
+static bool
+pm_regexp_parse_expression(pm_regexp_parser_t *parser) {
+ if (!pm_regexp_parse_item(parser)) {
+ return false;
+ }
+
+ while (!pm_regexp_char_is_eof(parser) && *parser->cursor != ')' && *parser->cursor != '|') {
+ if (!pm_regexp_parse_item(parser)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * pattern : EOF
+ * | expression EOF
+ * | expression '|' pattern
+ * ;
+ */
+static bool
+pm_regexp_parse_pattern(pm_regexp_parser_t *parser) {
+ return (
+ (
+ // Exit early if the pattern is empty.
+ pm_regexp_char_is_eof(parser) ||
+ // Parse the first expression in the pattern.
+ pm_regexp_parse_expression(parser)
+ ) &&
+ (
+ // Return now if we've parsed the entire pattern.
+ pm_regexp_char_is_eof(parser) ||
+ // Otherwise, we should have a pipe character.
+ (pm_regexp_char_expect(parser, '|') && pm_regexp_parse_pattern(parser))
+ )
+ );
+}
+
+/**
+ * Parse a regular expression and extract the names of all of the named capture
+ * groups.
+ */
+PRISM_EXPORTED_FUNCTION bool
+pm_regexp_named_capture_group_names(const uint8_t *source, size_t size, pm_string_list_t *named_captures, bool encoding_changed, const pm_encoding_t *encoding) {
+ pm_regexp_parser_t parser;
+ pm_regexp_parser_init(&parser, source, source + size, named_captures, encoding_changed, encoding);
+ return pm_regexp_parse_pattern(&parser);
+}
diff --git a/prism/regexp.h b/prism/regexp.h
new file mode 100644
index 0000000000..c5ceab11f9
--- /dev/null
+++ b/prism/regexp.h
@@ -0,0 +1,33 @@
+/**
+ * @file regexp.h
+ *
+ * A regular expression parser.
+ */
+#ifndef PRISM_REGEXP_H
+#define PRISM_REGEXP_H
+
+#include "prism/defines.h"
+#include "prism/parser.h"
+#include "prism/encoding.h"
+#include "prism/util/pm_memchr.h"
+#include "prism/util/pm_string_list.h"
+#include "prism/util/pm_string.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+
+/**
+ * Parse a regular expression and extract the names of all of the named capture
+ * groups.
+ *
+ * @param source The source code to parse.
+ * @param size The size of the source code.
+ * @param named_captures The list to add the names of the named capture groups.
+ * @param encoding_changed Whether or not the encoding changed from the default.
+ * @param encoding The encoding of the source code.
+ * @return Whether or not the parsing was successful.
+ */
+PRISM_EXPORTED_FUNCTION bool pm_regexp_named_capture_group_names(const uint8_t *source, size_t size, pm_string_list_t *named_captures, bool encoding_changed, const pm_encoding_t *encoding);
+
+#endif
diff --git a/prism/static_literals.c b/prism/static_literals.c
new file mode 100644
index 0000000000..08f61c81fa
--- /dev/null
+++ b/prism/static_literals.c
@@ -0,0 +1,638 @@
+#include "prism/static_literals.h"
+
+/**
+ * A small struct used for passing around a subset of the information that is
+ * stored on the parser. We use this to avoid having static literals explicitly
+ * depend on the parser struct.
+ */
+typedef struct {
+ /** The list of newline offsets to use to calculate line numbers. */
+ const pm_newline_list_t *newline_list;
+
+ /** The line number that the parser starts on. */
+ int32_t start_line;
+
+ /** The name of the encoding that the parser is using. */
+ const char *encoding_name;
+} pm_static_literals_metadata_t;
+
+static inline uint32_t
+murmur_scramble(uint32_t value) {
+ value *= 0xcc9e2d51;
+ value = (value << 15) | (value >> 17);
+ value *= 0x1b873593;
+ return value;
+}
+
+/**
+ * Murmur hash (https://en.wikipedia.org/wiki/MurmurHash) is a non-cryptographic
+ * general-purpose hash function. It is fast, which is what we care about in
+ * this case.
+ */
+static uint32_t
+murmur_hash(const uint8_t *key, size_t length) {
+ uint32_t hash = 0x9747b28c;
+ uint32_t segment;
+
+ for (size_t index = length >> 2; index; index--) {
+ memcpy(&segment, key, sizeof(uint32_t));
+ key += sizeof(uint32_t);
+ hash ^= murmur_scramble(segment);
+ hash = (hash << 13) | (hash >> 19);
+ hash = hash * 5 + 0xe6546b64;
+ }
+
+ segment = 0;
+ for (size_t index = length & 3; index; index--) {
+ segment <<= 8;
+ segment |= key[index - 1];
+ }
+
+ hash ^= murmur_scramble(segment);
+ hash ^= (uint32_t) length;
+ hash ^= hash >> 16;
+ hash *= 0x85ebca6b;
+ hash ^= hash >> 13;
+ hash *= 0xc2b2ae35;
+ hash ^= hash >> 16;
+ return hash;
+}
+
+/**
+ * Return the hash of the given node. It is important that nodes that have
+ * equivalent static literal values have the same hash. This is because we use
+ * these hashes to look for duplicates.
+ */
+static uint32_t
+node_hash(const pm_static_literals_metadata_t *metadata, const pm_node_t *node) {
+ switch (PM_NODE_TYPE(node)) {
+ case PM_INTEGER_NODE: {
+ // Integers hash their value.
+ const pm_integer_t *integer = &((const pm_integer_node_t *) node)->value;
+ uint32_t hash;
+ if (integer->values) {
+ hash = murmur_hash((const uint8_t *) integer->values, sizeof(uint32_t) * integer->length);
+ } else {
+ hash = murmur_hash((const uint8_t *) &integer->value, sizeof(uint32_t));
+ }
+
+ if (integer->negative) {
+ hash ^= murmur_scramble((uint32_t) 1);
+ }
+
+ return hash;
+ }
+ case PM_SOURCE_LINE_NODE: {
+ // Source lines hash their line number.
+ const pm_line_column_t line_column = pm_newline_list_line_column(metadata->newline_list, node->location.start, metadata->start_line);
+ const int32_t *value = &line_column.line;
+ return murmur_hash((const uint8_t *) value, sizeof(int32_t));
+ }
+ case PM_FLOAT_NODE: {
+ // Floats hash their value.
+ const double *value = &((const pm_float_node_t *) node)->value;
+ return murmur_hash((const uint8_t *) value, sizeof(double));
+ }
+ case PM_RATIONAL_NODE: {
+ // Rationals hash their numeric value. Because their numeric value
+ // is stored as a subnode, we hash that node and then mix in the
+ // fact that this is a rational node.
+ const pm_node_t *numeric = ((const pm_rational_node_t *) node)->numeric;
+ return node_hash(metadata, numeric) ^ murmur_scramble((uint32_t) node->type);
+ }
+ case PM_IMAGINARY_NODE: {
+ // Imaginaries hash their numeric value. Because their numeric value
+ // is stored as a subnode, we hash that node and then mix in the
+ // fact that this is an imaginary node.
+ const pm_node_t *numeric = ((const pm_imaginary_node_t *) node)->numeric;
+ return node_hash(metadata, numeric) ^ murmur_scramble((uint32_t) node->type);
+ }
+ case PM_STRING_NODE: {
+ // Strings hash their value and mix in their flags so that different
+ // encodings are not considered equal.
+ const pm_string_t *value = &((const pm_string_node_t *) node)->unescaped;
+
+ pm_node_flags_t flags = node->flags;
+ flags &= (PM_STRING_FLAGS_FORCED_BINARY_ENCODING | PM_STRING_FLAGS_FORCED_UTF8_ENCODING);
+
+ return murmur_hash(pm_string_source(value), pm_string_length(value) * sizeof(uint8_t)) ^ murmur_scramble((uint32_t) flags);
+ }
+ case PM_SOURCE_FILE_NODE: {
+ // Source files hash their value and mix in their flags so that
+ // different encodings are not considered equal.
+ const pm_string_t *value = &((const pm_source_file_node_t *) node)->filepath;
+ return murmur_hash(pm_string_source(value), pm_string_length(value) * sizeof(uint8_t));
+ }
+ case PM_REGULAR_EXPRESSION_NODE: {
+ // Regular expressions hash their value and mix in their flags so
+ // that different encodings are not considered equal.
+ const pm_string_t *value = &((const pm_regular_expression_node_t *) node)->unescaped;
+ return murmur_hash(pm_string_source(value), pm_string_length(value) * sizeof(uint8_t)) ^ murmur_scramble((uint32_t) node->flags);
+ }
+ case PM_SYMBOL_NODE: {
+ // Symbols hash their value and mix in their flags so that different
+ // encodings are not considered equal.
+ const pm_string_t *value = &((const pm_symbol_node_t *) node)->unescaped;
+ return murmur_hash(pm_string_source(value), pm_string_length(value) * sizeof(uint8_t)) ^ murmur_scramble((uint32_t) node->flags);
+ }
+ default:
+ assert(false && "unreachable");
+ return 0;
+ }
+}
+
+/**
+ * Insert a node into the node hash. It accepts the hash that should hold the
+ * new node, the parser that generated the node, the node to insert, and a
+ * comparison function. The comparison function is used for collision detection,
+ * and must be able to compare all node types that will be stored in this hash.
+ */
+static pm_node_t *
+pm_node_hash_insert(pm_node_hash_t *hash, const pm_static_literals_metadata_t *metadata, pm_node_t *node, int (*compare)(const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right)) {
+ // If we are out of space, we need to resize the hash. This will cause all
+ // of the nodes to be rehashed and reinserted into the new hash.
+ if (hash->size * 2 >= hash->capacity) {
+ // First, allocate space for the new node list.
+ uint32_t new_capacity = hash->capacity == 0 ? 4 : hash->capacity * 2;
+ pm_node_t **new_nodes = xcalloc(new_capacity, sizeof(pm_node_t *));
+ if (new_nodes == NULL) return NULL;
+
+ // It turns out to be more efficient to mask the hash value than to use
+ // the modulo operator. Because our capacities are always powers of two,
+ // we can use a bitwise AND to get the same result as the modulo
+ // operator.
+ uint32_t mask = new_capacity - 1;
+
+ // Now, rehash all of the nodes into the new list.
+ for (uint32_t index = 0; index < hash->capacity; index++) {
+ pm_node_t *node = hash->nodes[index];
+
+ if (node != NULL) {
+ uint32_t index = node_hash(metadata, node) & mask;
+ new_nodes[index] = node;
+ }
+ }
+
+ // Finally, free the old node list and update the hash.
+ xfree(hash->nodes);
+ hash->nodes = new_nodes;
+ hash->capacity = new_capacity;
+ }
+
+ // Now, insert the node into the hash.
+ uint32_t mask = hash->capacity - 1;
+ uint32_t index = node_hash(metadata, node) & mask;
+
+ // We use linear probing to resolve collisions. This means that if the
+ // current index is occupied, we will move to the next index and try again.
+ // We are guaranteed that this will eventually find an empty slot because we
+ // resize the hash when it gets too full.
+ while (hash->nodes[index] != NULL) {
+ if (compare(metadata, hash->nodes[index], node) == 0) break;
+ index = (index + 1) & mask;
+ }
+
+ // If the current index is occupied, we need to return the node that was
+ // already in the hash. Otherwise, we can just increment the size and insert
+ // the new node.
+ pm_node_t *result = hash->nodes[index];
+ if (result == NULL) hash->size++;
+
+ hash->nodes[index] = node;
+ return result;
+}
+
+/**
+ * Free the internal memory associated with the given node hash.
+ */
+static void
+pm_node_hash_free(pm_node_hash_t *hash) {
+ if (hash->capacity > 0) xfree(hash->nodes);
+}
+
+/**
+ * Compare two values that can be compared with a simple numeric comparison.
+ */
+#define PM_NUMERIC_COMPARISON(left, right) ((left < right) ? -1 : (left > right) ? 1 : 0)
+
+/**
+ * Return the integer value of the given node as an int64_t.
+ */
+static int64_t
+pm_int64_value(const pm_static_literals_metadata_t *metadata, const pm_node_t *node) {
+ switch (PM_NODE_TYPE(node)) {
+ case PM_INTEGER_NODE: {
+ const pm_integer_t *integer = &((const pm_integer_node_t *) node)->value;
+ if (integer->values) return integer->negative ? INT64_MIN : INT64_MAX;
+
+ int64_t value = (int64_t) integer->value;
+ return integer->negative ? -value : value;
+ }
+ case PM_SOURCE_LINE_NODE:
+ return (int64_t) pm_newline_list_line_column(metadata->newline_list, node->location.start, metadata->start_line).line;
+ default:
+ assert(false && "unreachable");
+ return 0;
+ }
+}
+
+/**
+ * A comparison function for comparing two IntegerNode or SourceLineNode
+ * instances.
+ */
+static int
+pm_compare_integer_nodes(const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) {
+ if (PM_NODE_TYPE_P(left, PM_SOURCE_LINE_NODE) || PM_NODE_TYPE_P(right, PM_SOURCE_LINE_NODE)) {
+ int64_t left_value = pm_int64_value(metadata, left);
+ int64_t right_value = pm_int64_value(metadata, right);
+ return PM_NUMERIC_COMPARISON(left_value, right_value);
+ }
+
+ const pm_integer_t *left_integer = &((const pm_integer_node_t *) left)->value;
+ const pm_integer_t *right_integer = &((const pm_integer_node_t *) right)->value;
+ return pm_integer_compare(left_integer, right_integer);
+}
+
+/**
+ * A comparison function for comparing two FloatNode instances.
+ */
+static int
+pm_compare_float_nodes(PRISM_ATTRIBUTE_UNUSED const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) {
+ const double left_value = ((const pm_float_node_t *) left)->value;
+ const double right_value = ((const pm_float_node_t *) right)->value;
+ return PM_NUMERIC_COMPARISON(left_value, right_value);
+}
+
+/**
+ * A comparison function for comparing two nodes that have attached numbers.
+ */
+static int
+pm_compare_number_nodes(const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) {
+ if (PM_NODE_TYPE(left) != PM_NODE_TYPE(right)) {
+ return PM_NUMERIC_COMPARISON(PM_NODE_TYPE(left), PM_NODE_TYPE(right));
+ }
+
+ switch (PM_NODE_TYPE(left)) {
+ case PM_IMAGINARY_NODE:
+ return pm_compare_number_nodes(metadata, ((const pm_imaginary_node_t *) left)->numeric, ((const pm_imaginary_node_t *) right)->numeric);
+ case PM_RATIONAL_NODE:
+ return pm_compare_number_nodes(metadata, ((const pm_rational_node_t *) left)->numeric, ((const pm_rational_node_t *) right)->numeric);
+ case PM_INTEGER_NODE:
+ return pm_compare_integer_nodes(metadata, left, right);
+ case PM_FLOAT_NODE:
+ return pm_compare_float_nodes(metadata, left, right);
+ default:
+ assert(false && "unreachable");
+ return 0;
+ }
+}
+
+/**
+ * Return a pointer to the string value of the given node.
+ */
+static const pm_string_t *
+pm_string_value(const pm_node_t *node) {
+ switch (PM_NODE_TYPE(node)) {
+ case PM_STRING_NODE:
+ return &((const pm_string_node_t *) node)->unescaped;
+ case PM_SOURCE_FILE_NODE:
+ return &((const pm_source_file_node_t *) node)->filepath;
+ case PM_SYMBOL_NODE:
+ return &((const pm_symbol_node_t *) node)->unescaped;
+ default:
+ assert(false && "unreachable");
+ return NULL;
+ }
+}
+
+/**
+ * A comparison function for comparing two nodes that have attached strings.
+ */
+static int
+pm_compare_string_nodes(PRISM_ATTRIBUTE_UNUSED const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) {
+ const pm_string_t *left_string = pm_string_value(left);
+ const pm_string_t *right_string = pm_string_value(right);
+ return pm_string_compare(left_string, right_string);
+}
+
+/**
+ * A comparison function for comparing two RegularExpressionNode instances.
+ */
+static int
+pm_compare_regular_expression_nodes(PRISM_ATTRIBUTE_UNUSED const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) {
+ const pm_regular_expression_node_t *left_regexp = (const pm_regular_expression_node_t *) left;
+ const pm_regular_expression_node_t *right_regexp = (const pm_regular_expression_node_t *) right;
+
+ int result = pm_string_compare(&left_regexp->unescaped, &right_regexp->unescaped);
+ if (result != 0) return result;
+
+ return PM_NUMERIC_COMPARISON(left_regexp->base.flags, right_regexp->base.flags);
+}
+
+#undef PM_NUMERIC_COMPARISON
+
+/**
+ * Add a node to the set of static literals.
+ */
+pm_node_t *
+pm_static_literals_add(const pm_newline_list_t *newline_list, int32_t start_line, pm_static_literals_t *literals, pm_node_t *node) {
+ switch (PM_NODE_TYPE(node)) {
+ case PM_INTEGER_NODE:
+ case PM_SOURCE_LINE_NODE:
+ return pm_node_hash_insert(
+ &literals->integer_nodes,
+ &(pm_static_literals_metadata_t) {
+ .newline_list = newline_list,
+ .start_line = start_line,
+ .encoding_name = NULL
+ },
+ node,
+ pm_compare_integer_nodes
+ );
+ case PM_FLOAT_NODE:
+ return pm_node_hash_insert(
+ &literals->float_nodes,
+ &(pm_static_literals_metadata_t) {
+ .newline_list = newline_list,
+ .start_line = start_line,
+ .encoding_name = NULL
+ },
+ node,
+ pm_compare_float_nodes
+ );
+ case PM_RATIONAL_NODE:
+ case PM_IMAGINARY_NODE:
+ return pm_node_hash_insert(
+ &literals->number_nodes,
+ &(pm_static_literals_metadata_t) {
+ .newline_list = newline_list,
+ .start_line = start_line,
+ .encoding_name = NULL
+ },
+ node,
+ pm_compare_number_nodes
+ );
+ case PM_STRING_NODE:
+ case PM_SOURCE_FILE_NODE:
+ return pm_node_hash_insert(
+ &literals->string_nodes,
+ &(pm_static_literals_metadata_t) {
+ .newline_list = newline_list,
+ .start_line = start_line,
+ .encoding_name = NULL
+ },
+ node,
+ pm_compare_string_nodes
+ );
+ case PM_REGULAR_EXPRESSION_NODE:
+ return pm_node_hash_insert(
+ &literals->regexp_nodes,
+ &(pm_static_literals_metadata_t) {
+ .newline_list = newline_list,
+ .start_line = start_line,
+ .encoding_name = NULL
+ },
+ node,
+ pm_compare_regular_expression_nodes
+ );
+ case PM_SYMBOL_NODE:
+ return pm_node_hash_insert(
+ &literals->symbol_nodes,
+ &(pm_static_literals_metadata_t) {
+ .newline_list = newline_list,
+ .start_line = start_line,
+ .encoding_name = NULL
+ },
+ node,
+ pm_compare_string_nodes
+ );
+ case PM_TRUE_NODE: {
+ pm_node_t *duplicated = literals->true_node;
+ literals->true_node = node;
+ return duplicated;
+ }
+ case PM_FALSE_NODE: {
+ pm_node_t *duplicated = literals->false_node;
+ literals->false_node = node;
+ return duplicated;
+ }
+ case PM_NIL_NODE: {
+ pm_node_t *duplicated = literals->nil_node;
+ literals->nil_node = node;
+ return duplicated;
+ }
+ case PM_SOURCE_ENCODING_NODE: {
+ pm_node_t *duplicated = literals->source_encoding_node;
+ literals->source_encoding_node = node;
+ return duplicated;
+ }
+ default:
+ return NULL;
+ }
+}
+
+/**
+ * Free the internal memory associated with the given static literals set.
+ */
+void
+pm_static_literals_free(pm_static_literals_t *literals) {
+ pm_node_hash_free(&literals->integer_nodes);
+ pm_node_hash_free(&literals->float_nodes);
+ pm_node_hash_free(&literals->number_nodes);
+ pm_node_hash_free(&literals->string_nodes);
+ pm_node_hash_free(&literals->regexp_nodes);
+ pm_node_hash_free(&literals->symbol_nodes);
+}
+
+/**
+ * A helper to determine if the given node is a static literal that is positive.
+ * This is used for formatting imaginary nodes.
+ */
+static bool
+pm_static_literal_positive_p(const pm_node_t *node) {
+ switch (PM_NODE_TYPE(node)) {
+ case PM_FLOAT_NODE:
+ return ((const pm_float_node_t *) node)->value > 0;
+ case PM_INTEGER_NODE:
+ return !((const pm_integer_node_t *) node)->value.negative;
+ case PM_RATIONAL_NODE:
+ return pm_static_literal_positive_p(((const pm_rational_node_t *) node)->numeric);
+ case PM_IMAGINARY_NODE:
+ return pm_static_literal_positive_p(((const pm_imaginary_node_t *) node)->numeric);
+ default:
+ assert(false && "unreachable");
+ return false;
+ }
+}
+
+/**
+ * Inspect a rational node that wraps a float node. This is going to be a
+ * poor-man's version of the Ruby `Rational#to_s` method, because we're not
+ * going to try to reduce the rational by finding the GCD. We'll leave that for
+ * a future improvement.
+ */
+static void
+pm_rational_inspect(pm_buffer_t *buffer, pm_rational_node_t *node) {
+ const uint8_t *start = node->base.location.start;
+ const uint8_t *end = node->base.location.end - 1; // r
+
+ while (start < end && *start == '0') start++; // 0.1 -> .1
+ while (end > start && end[-1] == '0') end--; // 1.0 -> 1.
+ size_t length = (size_t) (end - start);
+
+ const uint8_t *point = memchr(start, '.', length);
+ assert(point && "should have a decimal point");
+
+ uint8_t *digits = malloc(length - 1);
+ if (digits == NULL) return;
+
+ memcpy(digits, start, (unsigned long) (point - start));
+ memcpy(digits + (point - start), point + 1, (unsigned long) (end - point - 1));
+
+ pm_integer_t numerator = { 0 };
+ pm_integer_parse(&numerator, PM_INTEGER_BASE_DECIMAL, digits, digits + length - 1);
+
+ pm_buffer_append_byte(buffer, '(');
+ pm_integer_string(buffer, &numerator);
+ pm_buffer_append_string(buffer, "/1", 2);
+ for (size_t index = 0; index < (size_t) (end - point - 1); index++) pm_buffer_append_byte(buffer, '0');
+ pm_buffer_append_byte(buffer, ')');
+
+ pm_integer_free(&numerator);
+ free(digits);
+}
+
+/**
+ * Create a string-based representation of the given static literal.
+ */
+static inline void
+pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_metadata_t *metadata, const pm_node_t *node) {
+ switch (PM_NODE_TYPE(node)) {
+ case PM_FALSE_NODE:
+ pm_buffer_append_string(buffer, "false", 5);
+ break;
+ case PM_FLOAT_NODE: {
+ const double value = ((const pm_float_node_t *) node)->value;
+
+ if (isinf(value)) {
+ if (*node->location.start == '-') {
+ pm_buffer_append_byte(buffer, '-');
+ }
+ pm_buffer_append_string(buffer, "Infinity", 8);
+ } else if (value == 0.0) {
+ if (*node->location.start == '-') {
+ pm_buffer_append_byte(buffer, '-');
+ }
+ pm_buffer_append_string(buffer, "0.0", 3);
+ } else {
+ pm_buffer_append_format(buffer, "%g", value);
+
+ // %g will not insert a .0 for 1e100 (we'll get back 1e+100). So
+ // we check for the decimal point and add it in here if it's not
+ // present.
+ if (pm_buffer_index(buffer, '.') == SIZE_MAX) {
+ size_t exponent_index = pm_buffer_index(buffer, 'e');
+ size_t index = exponent_index == SIZE_MAX ? pm_buffer_length(buffer) : exponent_index;
+ pm_buffer_insert(buffer, index, ".0", 2);
+ }
+ }
+
+ break;
+ }
+ case PM_IMAGINARY_NODE: {
+ const pm_node_t *numeric = ((const pm_imaginary_node_t *) node)->numeric;
+ pm_buffer_append_string(buffer, "(0", 2);
+ if (pm_static_literal_positive_p(numeric)) pm_buffer_append_byte(buffer, '+');
+ pm_static_literal_inspect_node(buffer, metadata, numeric);
+ if (PM_NODE_TYPE_P(numeric, PM_RATIONAL_NODE)) pm_buffer_append_byte(buffer, '*');
+ pm_buffer_append_string(buffer, "i)", 2);
+ break;
+ }
+ case PM_INTEGER_NODE:
+ pm_integer_string(buffer, &((const pm_integer_node_t *) node)->value);
+ break;
+ case PM_NIL_NODE:
+ pm_buffer_append_string(buffer, "nil", 3);
+ break;
+ case PM_RATIONAL_NODE: {
+ const pm_node_t *numeric = ((const pm_rational_node_t *) node)->numeric;
+
+ switch (PM_NODE_TYPE(numeric)) {
+ case PM_INTEGER_NODE:
+ pm_buffer_append_byte(buffer, '(');
+ pm_static_literal_inspect_node(buffer, metadata, numeric);
+ pm_buffer_append_string(buffer, "/1)", 3);
+ break;
+ case PM_FLOAT_NODE:
+ pm_rational_inspect(buffer, (pm_rational_node_t *) node);
+ break;
+ default:
+ assert(false && "unreachable");
+ break;
+ }
+
+ break;
+ }
+ case PM_REGULAR_EXPRESSION_NODE: {
+ const pm_string_t *unescaped = &((const pm_regular_expression_node_t *) node)->unescaped;
+ pm_buffer_append_byte(buffer, '/');
+ pm_buffer_append_source(buffer, pm_string_source(unescaped), pm_string_length(unescaped), PM_BUFFER_ESCAPING_RUBY);
+ pm_buffer_append_byte(buffer, '/');
+
+ if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE)) pm_buffer_append_string(buffer, "m", 1);
+ if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE)) pm_buffer_append_string(buffer, "i", 1);
+ if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED)) pm_buffer_append_string(buffer, "x", 1);
+ if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT)) pm_buffer_append_string(buffer, "n", 1);
+
+ break;
+ }
+ case PM_SOURCE_ENCODING_NODE:
+ pm_buffer_append_format(buffer, "#<Encoding:%s>", metadata->encoding_name);
+ break;
+ case PM_SOURCE_FILE_NODE: {
+ const pm_string_t *filepath = &((const pm_source_file_node_t *) node)->filepath;
+ pm_buffer_append_byte(buffer, '"');
+ pm_buffer_append_source(buffer, pm_string_source(filepath), pm_string_length(filepath), PM_BUFFER_ESCAPING_RUBY);
+ pm_buffer_append_byte(buffer, '"');
+ break;
+ }
+ case PM_SOURCE_LINE_NODE:
+ pm_buffer_append_format(buffer, "%d", pm_newline_list_line_column(metadata->newline_list, node->location.start, metadata->start_line).line);
+ break;
+ case PM_STRING_NODE: {
+ const pm_string_t *unescaped = &((const pm_string_node_t *) node)->unescaped;
+ pm_buffer_append_byte(buffer, '"');
+ pm_buffer_append_source(buffer, pm_string_source(unescaped), pm_string_length(unescaped), PM_BUFFER_ESCAPING_RUBY);
+ pm_buffer_append_byte(buffer, '"');
+ break;
+ }
+ case PM_SYMBOL_NODE: {
+ const pm_string_t *unescaped = &((const pm_symbol_node_t *) node)->unescaped;
+ pm_buffer_append_byte(buffer, ':');
+ pm_buffer_append_source(buffer, pm_string_source(unescaped), pm_string_length(unescaped), PM_BUFFER_ESCAPING_RUBY);
+ break;
+ }
+ case PM_TRUE_NODE:
+ pm_buffer_append_string(buffer, "true", 4);
+ break;
+ default:
+ assert(false && "unreachable");
+ break;
+ }
+}
+
+/**
+ * Create a string-based representation of the given static literal.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_static_literal_inspect(pm_buffer_t *buffer, const pm_newline_list_t *newline_list, int32_t start_line, const char *encoding_name, const pm_node_t *node) {
+ pm_static_literal_inspect_node(
+ buffer,
+ &(pm_static_literals_metadata_t) {
+ .newline_list = newline_list,
+ .start_line = start_line,
+ .encoding_name = encoding_name
+ },
+ node
+ );
+}
diff --git a/prism/static_literals.h b/prism/static_literals.h
new file mode 100644
index 0000000000..72706054c1
--- /dev/null
+++ b/prism/static_literals.h
@@ -0,0 +1,120 @@
+/**
+ * @file static_literals.h
+ *
+ * A set of static literal nodes that can be checked for duplicates.
+ */
+#ifndef PRISM_STATIC_LITERALS_H
+#define PRISM_STATIC_LITERALS_H
+
+#include "prism/defines.h"
+#include "prism/ast.h"
+#include "prism/util/pm_newline_list.h"
+
+#include <assert.h>
+#include <stdbool.h>
+
+/**
+ * An internal hash table for a set of nodes.
+ */
+typedef struct {
+ /** The array of nodes in the hash table. */
+ pm_node_t **nodes;
+
+ /** The size of the hash table. */
+ uint32_t size;
+
+ /** The space that has been allocated in the hash table. */
+ uint32_t capacity;
+} pm_node_hash_t;
+
+/**
+ * Certain sets of nodes (hash keys and when clauses) check for duplicate nodes
+ * to alert the user of potential issues. To do this, we keep a set of the nodes
+ * that have been seen so far, and compare whenever we find a new node.
+ *
+ * We bucket the nodes based on their type to minimize the number of comparisons
+ * that need to be performed.
+ */
+typedef struct {
+ /**
+ * This is the set of IntegerNode and SourceLineNode instances.
+ */
+ pm_node_hash_t integer_nodes;
+
+ /**
+ * This is the set of FloatNode instances.
+ */
+ pm_node_hash_t float_nodes;
+
+ /**
+ * This is the set of RationalNode and ImaginaryNode instances.
+ */
+ pm_node_hash_t number_nodes;
+
+ /**
+ * This is the set of StringNode and SourceFileNode instances.
+ */
+ pm_node_hash_t string_nodes;
+
+ /**
+ * This is the set of RegularExpressionNode instances.
+ */
+ pm_node_hash_t regexp_nodes;
+
+ /**
+ * This is the set of SymbolNode instances.
+ */
+ pm_node_hash_t symbol_nodes;
+
+ /**
+ * A pointer to the last TrueNode instance that was inserted, or NULL.
+ */
+ pm_node_t *true_node;
+
+ /**
+ * A pointer to the last FalseNode instance that was inserted, or NULL.
+ */
+ pm_node_t *false_node;
+
+ /**
+ * A pointer to the last NilNode instance that was inserted, or NULL.
+ */
+ pm_node_t *nil_node;
+
+ /**
+ * A pointer to the last SourceEncodingNode instance that was inserted, or
+ * NULL.
+ */
+ pm_node_t *source_encoding_node;
+} pm_static_literals_t;
+
+/**
+ * Add a node to the set of static literals.
+ *
+ * @param newline_list The list of newline offsets to use to calculate lines.
+ * @param start_line The line number that the parser starts on.
+ * @param literals The set of static literals to add the node to.
+ * @param node The node to add to the set.
+ * @return A pointer to the node that is being overwritten, if there is one.
+ */
+pm_node_t * pm_static_literals_add(const pm_newline_list_t *newline_list, int32_t start_line, pm_static_literals_t *literals, pm_node_t *node);
+
+/**
+ * Free the internal memory associated with the given static literals set.
+ *
+ * @param literals The set of static literals to free.
+ */
+void pm_static_literals_free(pm_static_literals_t *literals);
+
+/**
+ * Create a string-based representation of the given static literal.
+ *
+ * @param buffer The buffer to write the string to.
+ * @param newline_list The list of newline offsets to use to calculate lines.
+ * @param start_line The line number that the parser starts on.
+ * @param encoding_name The name of the encoding of the source being parsed.
+ * @param node The node to create a string representation of.
+ */
+PRISM_EXPORTED_FUNCTION void pm_static_literal_inspect(pm_buffer_t *buffer, const pm_newline_list_t *newline_list, int32_t start_line, const char *encoding_name, const pm_node_t *node);
+
+#endif
diff --git a/prism/templates/ext/prism/api_node.c.erb b/prism/templates/ext/prism/api_node.c.erb
new file mode 100644
index 0000000000..419236ef78
--- /dev/null
+++ b/prism/templates/ext/prism/api_node.c.erb
@@ -0,0 +1,257 @@
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+#include "prism/extension.h"
+
+extern VALUE rb_cPrism;
+extern VALUE rb_cPrismNode;
+extern VALUE rb_cPrismSource;
+extern VALUE rb_cPrismToken;
+extern VALUE rb_cPrismLocation;
+
+<%- nodes.each do |node| -%>
+static VALUE rb_cPrism<%= node.name %>;
+<%- end -%>
+
+static VALUE
+pm_location_new(const pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
+ uint64_t value = ((((uint64_t) (start - parser->start)) << 32) | ((uint32_t) (end - start)));
+ return ULL2NUM(value);
+}
+
+VALUE
+pm_token_new(const pm_parser_t *parser, const pm_token_t *token, rb_encoding *encoding, VALUE source) {
+ ID type = rb_intern(pm_token_type_name(token->type));
+ VALUE location = pm_location_new(parser, token->start, token->end);
+
+ VALUE argv[] = {
+ source,
+ ID2SYM(type),
+ rb_enc_str_new((const char *) token->start, token->end - token->start, encoding),
+ location
+ };
+
+ return rb_class_new_instance(4, argv, rb_cPrismToken);
+}
+
+static VALUE
+pm_string_new(const pm_string_t *string, rb_encoding *encoding) {
+ return rb_enc_str_new((const char *) pm_string_source(string), pm_string_length(string), encoding);
+}
+
+VALUE
+pm_integer_new(const pm_integer_t *integer) {
+ VALUE result;
+ if (integer->values == NULL) {
+ result = UINT2NUM(integer->value);
+ } else {
+ VALUE string = rb_str_new(NULL, integer->length * 8);
+ unsigned char *bytes = (unsigned char *) RSTRING_PTR(string);
+
+ size_t offset = integer->length * 8;
+ for (size_t value_index = 0; value_index < integer->length; value_index++) {
+ uint32_t value = integer->values[value_index];
+
+ for (int index = 0; index < 8; index++) {
+ int byte = (value >> (4 * index)) & 0xf;
+ bytes[--offset] = byte < 10 ? byte + '0' : byte - 10 + 'a';
+ }
+ }
+
+ result = rb_funcall(string, rb_intern("to_i"), 1, UINT2NUM(16));
+ }
+
+ if (integer->negative) {
+ result = rb_funcall(result, rb_intern("-@"), 0);
+ }
+
+ return result;
+}
+
+// Create a Prism::Source object from the given parser, after pm_parse() was called.
+VALUE
+pm_source_new(const pm_parser_t *parser, rb_encoding *encoding) {
+ VALUE source_string = rb_enc_str_new((const char *) parser->start, parser->end - parser->start, encoding);
+
+ VALUE offsets = rb_ary_new_capa(parser->newline_list.size);
+ for (size_t index = 0; index < parser->newline_list.size; index++) {
+ rb_ary_push(offsets, ULONG2NUM(parser->newline_list.offsets[index]));
+ }
+
+ VALUE source_argv[] = { source_string, LONG2NUM(parser->start_line), offsets };
+ return rb_class_new_instance(3, source_argv, rb_cPrismSource);
+}
+
+typedef struct pm_node_stack_node {
+ struct pm_node_stack_node *prev;
+ const pm_node_t *visit;
+ bool visited;
+} pm_node_stack_node_t;
+
+static void
+pm_node_stack_push(pm_node_stack_node_t **stack, const pm_node_t *visit) {
+ pm_node_stack_node_t *node = xmalloc(sizeof(pm_node_stack_node_t));
+ node->prev = *stack;
+ node->visit = visit;
+ node->visited = false;
+ *stack = node;
+}
+
+static const pm_node_t *
+pm_node_stack_pop(pm_node_stack_node_t **stack) {
+ pm_node_stack_node_t *current = *stack;
+ const pm_node_t *visit = current->visit;
+
+ *stack = current->prev;
+ xfree(current);
+
+ return visit;
+}
+
+VALUE
+pm_ast_new(const pm_parser_t *parser, const pm_node_t *node, rb_encoding *encoding, VALUE source) {
+ VALUE constants = rb_ary_new_capa(parser->constant_pool.size);
+
+ for (uint32_t index = 0; index < parser->constant_pool.size; index++) {
+ pm_constant_t *constant = &parser->constant_pool.constants[index];
+ int state = 0;
+
+ VALUE string = rb_enc_str_new((const char *) constant->start, constant->length, encoding);
+ VALUE value = rb_protect(rb_str_intern, string, &state);
+
+ if (state != 0) {
+ value = ID2SYM(rb_intern_const("?"));
+ rb_set_errinfo(Qnil);
+ }
+
+ rb_ary_push(constants, value);
+ }
+
+ pm_node_stack_node_t *node_stack = NULL;
+ pm_node_stack_push(&node_stack, node);
+ VALUE value_stack = rb_ary_new();
+
+ while (node_stack != NULL) {
+ if (!node_stack->visited) {
+ if (node_stack->visit == NULL) {
+ pm_node_stack_pop(&node_stack);
+ rb_ary_push(value_stack, Qnil);
+ continue;
+ }
+
+ const pm_node_t *node = node_stack->visit;
+ node_stack->visited = true;
+
+ switch (PM_NODE_TYPE(node)) {
+ <%- nodes.each do |node| -%>
+ <%- if node.fields.any? { |field| [Prism::Template::NodeField, Prism::Template::OptionalNodeField, Prism::Template::NodeListField].include?(field.class) } -%>
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+ case <%= node.type %>: {
+ pm_<%= node.human %>_t *cast = (pm_<%= node.human %>_t *) node;
+ <%- node.fields.each do |field| -%>
+ <%- case field -%>
+ <%- when Prism::Template::NodeField, Prism::Template::OptionalNodeField -%>
+ pm_node_stack_push(&node_stack, (pm_node_t *) cast-><%= field.name %>);
+ <%- when Prism::Template::NodeListField -%>
+ for (size_t index = 0; index < cast-><%= field.name %>.size; index++) {
+ pm_node_stack_push(&node_stack, (pm_node_t *) cast-><%= field.name %>.nodes[index]);
+ }
+ <%- end -%>
+ <%- end -%>
+ break;
+ }
+ <%- end -%>
+ <%- end -%>
+ default:
+ break;
+ }
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+ } else {
+ const pm_node_t *node = pm_node_stack_pop(&node_stack);
+
+ switch (PM_NODE_TYPE(node)) {
+ <%- nodes.each do |node| -%>
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+ case <%= node.type %>: {
+ <%- if node.fields.any? { |field| ![Prism::Template::NodeField, Prism::Template::OptionalNodeField, Prism::Template::FlagsField].include?(field.class) } -%>
+ pm_<%= node.human %>_t *cast = (pm_<%= node.human %>_t *) node;
+ <%- end -%>
+ VALUE argv[<%= node.fields.length + 2 %>];
+
+ // source
+ argv[0] = source;
+ <%- node.fields.each.with_index(1) do |field, index| -%>
+
+ // <%= field.name %>
+ <%- case field -%>
+ <%- when Prism::Template::NodeField, Prism::Template::OptionalNodeField -%>
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+ argv[<%= index %>] = rb_ary_pop(value_stack);
+ <%- when Prism::Template::NodeListField -%>
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+ argv[<%= index %>] = rb_ary_new_capa(cast-><%= field.name %>.size);
+ for (size_t index = 0; index < cast-><%= field.name %>.size; index++) {
+ rb_ary_push(argv[<%= index %>], rb_ary_pop(value_stack));
+ }
+ <%- when Prism::Template::StringField -%>
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+ argv[<%= index %>] = pm_string_new(&cast-><%= field.name %>, encoding);
+ <%- when Prism::Template::ConstantField -%>
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+ assert(cast-><%= field.name %> != 0);
+ argv[<%= index %>] = RARRAY_AREF(constants, cast-><%= field.name %> - 1);
+ <%- when Prism::Template::OptionalConstantField -%>
+ argv[<%= index %>] = cast-><%= field.name %> == 0 ? Qnil : RARRAY_AREF(constants, cast-><%= field.name %> - 1);
+ <%- when Prism::Template::ConstantListField -%>
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+ argv[<%= index %>] = rb_ary_new_capa(cast-><%= field.name %>.size);
+ for (size_t index = 0; index < cast-><%= field.name %>.size; index++) {
+ assert(cast-><%= field.name %>.ids[index] != 0);
+ rb_ary_push(argv[<%= index %>], RARRAY_AREF(constants, cast-><%= field.name %>.ids[index] - 1));
+ }
+ <%- when Prism::Template::LocationField -%>
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+ argv[<%= index %>] = pm_location_new(parser, cast-><%= field.name %>.start, cast-><%= field.name %>.end);
+ <%- when Prism::Template::OptionalLocationField -%>
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+ argv[<%= index %>] = cast-><%= field.name %>.start == NULL ? Qnil : pm_location_new(parser, cast-><%= field.name %>.start, cast-><%= field.name %>.end);
+ <%- when Prism::Template::UInt8Field -%>
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+ argv[<%= index %>] = UINT2NUM(cast-><%= field.name %>);
+ <%- when Prism::Template::UInt32Field -%>
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+ argv[<%= index %>] = ULONG2NUM(cast-><%= field.name %>);
+ <%- when Prism::Template::FlagsField -%>
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+ argv[<%= index %>] = ULONG2NUM(node->flags & ~PM_NODE_FLAG_COMMON_MASK);
+ <%- when Prism::Template::IntegerField -%>
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+ argv[<%= index %>] = pm_integer_new(&cast-><%= field.name %>);
+ <%- when Prism::Template::DoubleField -%>
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+ argv[<%= index %>] = DBL2NUM(cast-><%= field.name %>);
+ <%- else -%>
+ <%- raise -%>
+ <%- end -%>
+ <%- end -%>
+
+ // location
+ argv[<%= node.fields.length + 1 %>] = pm_location_new(parser, node->location.start, node->location.end);
+
+ rb_ary_push(value_stack, rb_class_new_instance(<%= node.fields.length + 2 %>, argv, rb_cPrism<%= node.name %>));
+ break;
+ }
+ <%- end -%>
+ default:
+ rb_raise(rb_eRuntimeError, "unknown node type: %d", PM_NODE_TYPE(node));
+ }
+ }
+ }
+
+ return rb_ary_pop(value_stack);
+}
+
+void
+Init_prism_api_node(void) {
+ <%- nodes.each do |node| -%>
+ rb_cPrism<%= node.name %> = rb_define_class_under(rb_cPrism, "<%= node.name %>", rb_cPrismNode);
+ <%- end -%>
+}
diff --git a/prism/templates/include/prism/ast.h.erb b/prism/templates/include/prism/ast.h.erb
new file mode 100644
index 0000000000..0fe7905e40
--- /dev/null
+++ b/prism/templates/include/prism/ast.h.erb
@@ -0,0 +1,217 @@
+/**
+ * @file ast.h
+ *
+ * The abstract syntax tree.
+ */
+#ifndef PRISM_AST_H
+#define PRISM_AST_H
+
+#include "prism/defines.h"
+#include "prism/util/pm_constant_pool.h"
+#include "prism/util/pm_integer.h"
+#include "prism/util/pm_string.h"
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/**
+ * This enum represents every type of token in the Ruby source.
+ */
+typedef enum pm_token_type {
+<%- tokens.each do |token| -%>
+ /** <%= token.comment %> */
+ PM_TOKEN_<%= token.name %><%= " = #{token.value}" if token.value %>,
+
+<%- end -%>
+ /** The maximum token value. */
+ PM_TOKEN_MAXIMUM,
+} pm_token_type_t;
+
+/**
+ * This struct represents a token in the Ruby source. We use it to track both
+ * type and location information.
+ */
+typedef struct {
+ /** The type of the token. */
+ pm_token_type_t type;
+
+ /** A pointer to the start location of the token in the source. */
+ const uint8_t *start;
+
+ /** A pointer to the end location of the token in the source. */
+ const uint8_t *end;
+} pm_token_t;
+
+/**
+ * This represents a range of bytes in the source string to which a node or
+ * token corresponds.
+ */
+typedef struct {
+ /** A pointer to the start location of the range in the source. */
+ const uint8_t *start;
+
+ /** A pointer to the end location of the range in the source. */
+ const uint8_t *end;
+} pm_location_t;
+
+struct pm_node;
+
+/**
+ * A list of nodes in the source, most often used for lists of children.
+ */
+typedef struct pm_node_list {
+ /** The number of nodes in the list. */
+ size_t size;
+
+ /** The capacity of the list that has been allocated. */
+ size_t capacity;
+
+ /** The nodes in the list. */
+ struct pm_node **nodes;
+} pm_node_list_t;
+
+/**
+ * This enum represents every type of node in the Ruby syntax tree.
+ */
+enum pm_node_type {
+<%- nodes.each_with_index do |node, index| -%>
+ /** <%= node.name %> */
+ <%= node.type %> = <%= index + 1 %>,
+
+<%- end -%>
+ /** A special kind of node used for compilation. */
+ PM_SCOPE_NODE
+};
+
+/**
+ * This is the type of node embedded in the node struct. We explicitly control
+ * the size of it here to avoid having the variable-width enum.
+ */
+typedef uint16_t pm_node_type_t;
+
+/**
+ * These are the flags embedded in the node struct. We explicitly control the
+ * size of it here to avoid having the variable-width enum.
+ */
+typedef uint16_t pm_node_flags_t;
+
+/**
+ * We store the flags enum in every node in the tree. Some flags are common to
+ * all nodes (the ones listed below). Others are specific to certain node types.
+ */
+#define PM_NODE_FLAG_BITS (sizeof(pm_node_flags_t) * 8)
+
+static const pm_node_flags_t PM_NODE_FLAG_NEWLINE = (1 << (PM_NODE_FLAG_BITS - 1));
+static const pm_node_flags_t PM_NODE_FLAG_STATIC_LITERAL = (1 << (PM_NODE_FLAG_BITS - 2));
+static const pm_node_flags_t PM_NODE_FLAG_COMMON_MASK = (1 << (PM_NODE_FLAG_BITS - 1)) | (1 << (PM_NODE_FLAG_BITS - 2));
+
+/**
+ * Cast the type to an enum to allow the compiler to provide exhaustiveness
+ * checking.
+ */
+#define PM_NODE_TYPE(node) ((enum pm_node_type) (node)->type)
+
+/**
+ * Return true if the type of the given node matches the given type.
+ */
+#define PM_NODE_TYPE_P(node, type) (PM_NODE_TYPE(node) == (type))
+
+/**
+ * Return true if the given flag is set on the given node.
+ */
+#define PM_NODE_FLAG_P(node, flag) ((((pm_node_t *)(node))->flags & (flag)) != 0)
+
+/**
+ * This is the base structure that represents a node in the syntax tree. It is
+ * embedded into every node type.
+ */
+typedef struct pm_node {
+ /**
+ * This represents the type of the node. It somewhat maps to the nodes that
+ * existed in the original grammar and ripper, but it's not a 1:1 mapping.
+ */
+ pm_node_type_t type;
+
+ /**
+ * This represents any flags on the node. Some are common to all nodes, and
+ * some are specific to the type of node.
+ */
+ pm_node_flags_t flags;
+
+ /**
+ * This is the location of the node in the source. It's a range of bytes
+ * containing a start and an end.
+ */
+ pm_location_t location;
+} pm_node_t;
+<%- nodes.each do |node| -%>
+
+/**
+ * <%= node.name %>
+ *
+ * Type: <%= node.type %>
+<%- if (node_flags = node.fields.find { |field| field.is_a? Prism::Template::FlagsField }) -%>
+ * Flags:
+<%- found = flags.find { |flag| flag.name == node_flags.kind }.tap { |found| raise "Expected to find #{field.kind}" unless found } -%>
+<%- found.values.each do |value| -%>
+ * PM_<%= found.human.upcase %>_<%= value.name %>
+<%- end -%>
+<%- end -%>
+ *
+ * @extends pm_node_t
+ */
+typedef struct pm_<%= node.human %> {
+ /** The embedded base node. */
+ pm_node_t base;
+<%- node.fields.grep_v(Prism::Template::FlagsField).each do |field| -%>
+
+ /**
+ * <%= node.name %>#<%= field.name %>
+ <%- if field.comment -%>
+ *
+ <%- field.each_comment_line do |line| -%>
+ *<%= line %>
+ <%- end -%>
+ <%- end -%>
+ */
+ <%= case field
+ when Prism::Template::NodeField, Prism::Template::OptionalNodeField then "struct #{field.c_type} *#{field.name}"
+ when Prism::Template::NodeListField then "struct pm_node_list #{field.name}"
+ when Prism::Template::ConstantField, Prism::Template::OptionalConstantField then "pm_constant_id_t #{field.name}"
+ when Prism::Template::ConstantListField then "pm_constant_id_list_t #{field.name}"
+ when Prism::Template::StringField then "pm_string_t #{field.name}"
+ when Prism::Template::LocationField, Prism::Template::OptionalLocationField then "pm_location_t #{field.name}"
+ when Prism::Template::UInt8Field then "uint8_t #{field.name}"
+ when Prism::Template::UInt32Field then "uint32_t #{field.name}"
+ when Prism::Template::IntegerField then "pm_integer_t #{field.name}"
+ when Prism::Template::DoubleField then "double #{field.name}"
+ else raise field.class.name
+ end
+ %>;
+<%- end -%>
+} pm_<%= node.human %>_t;
+<%- end -%>
+<%- flags.each do |flag| -%>
+
+/**
+ * <%= flag.comment %>
+ */
+typedef enum pm_<%= flag.human %> {
+ <%- flag.values.each_with_index do |value, index| -%>
+<%= "\n" if index > 0 -%>
+ /** <%= value.comment %> */
+ PM_<%= flag.human.upcase %>_<%= value.name %> = <%= 1 << index %>,
+ <%- end -%>
+} pm_<%= flag.human %>_t;
+<%- end -%>
+
+/**
+ * When we're serializing to Java, we want to skip serializing the location
+ * fields as they won't be used by JRuby or TruffleRuby. This boolean allows us
+ * to specify that through the environment. It will never be true except for in
+ * those build systems.
+ */
+#define PRISM_SERIALIZE_ONLY_SEMANTICS_FIELDS <%= Prism::Template::SERIALIZE_ONLY_SEMANTICS_FIELDS %>
+
+#endif
diff --git a/prism/templates/include/prism/diagnostic.h.erb b/prism/templates/include/prism/diagnostic.h.erb
new file mode 100644
index 0000000000..07bbc8fae7
--- /dev/null
+++ b/prism/templates/include/prism/diagnostic.h.erb
@@ -0,0 +1,130 @@
+/**
+ * @file diagnostic.h
+ *
+ * A list of diagnostics generated during parsing.
+ */
+#ifndef PRISM_DIAGNOSTIC_H
+#define PRISM_DIAGNOSTIC_H
+
+#include "prism/ast.h"
+#include "prism/defines.h"
+#include "prism/util/pm_list.h"
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <assert.h>
+
+/**
+ * The diagnostic IDs of all of the diagnostics, used to communicate the types
+ * of errors between the parser and the user.
+ */
+typedef enum {
+ // These are the error diagnostics.
+ <%- errors.each do |error| -%>
+ PM_ERR_<%= error.name %>,
+ <%- end -%>
+
+ // These are the warning diagnostics.
+ <%- warnings.each do |warning| -%>
+ PM_WARN_<%= warning.name %>,
+ <%- end -%>
+} pm_diagnostic_id_t;
+
+/**
+ * This struct represents a diagnostic generated during parsing.
+ *
+ * @extends pm_list_node_t
+ */
+typedef struct {
+ /** The embedded base node. */
+ pm_list_node_t node;
+
+ /** The location of the diagnostic in the source. */
+ pm_location_t location;
+
+ /** The ID of the diagnostic. */
+ pm_diagnostic_id_t diag_id;
+
+ /** The message associated with the diagnostic. */
+ const char *message;
+
+ /**
+ * Whether or not the memory related to the message of this diagnostic is
+ * owned by this diagnostic. If it is, it needs to be freed when the
+ * diagnostic is freed.
+ */
+ bool owned;
+
+ /**
+ * The level of the diagnostic, see `pm_error_level_t` and
+ * `pm_warning_level_t` for possible values.
+ */
+ uint8_t level;
+} pm_diagnostic_t;
+
+/**
+ * The levels of errors generated during parsing.
+ */
+typedef enum {
+ /** For errors that should raise a syntax error. */
+ PM_ERROR_LEVEL_SYNTAX = 0,
+
+ /** For errors that should raise an argument error. */
+ PM_ERROR_LEVEL_ARGUMENT = 1,
+
+ /** For errors that should raise a load error. */
+ PM_ERROR_LEVEL_LOAD = 2
+} pm_error_level_t;
+
+/**
+ * The levels of warnings generated during parsing.
+ */
+typedef enum {
+ /** For warnings which should be emitted if $VERBOSE != nil. */
+ PM_WARNING_LEVEL_DEFAULT = 0,
+
+ /** For warnings which should be emitted if $VERBOSE == true. */
+ PM_WARNING_LEVEL_VERBOSE = 1
+} pm_warning_level_t;
+
+/**
+ * Get the human-readable name of the given diagnostic ID.
+ *
+ * @param diag_id The diagnostic ID.
+ * @return The human-readable name of the diagnostic ID.
+ */
+const char * pm_diagnostic_id_human(pm_diagnostic_id_t diag_id);
+
+/**
+ * Append a diagnostic to the given list of diagnostics that is using shared
+ * memory for its message.
+ *
+ * @param list The list to append to.
+ * @param start The start of the diagnostic.
+ * @param end The end of the diagnostic.
+ * @param diag_id The diagnostic ID.
+ * @return Whether the diagnostic was successfully appended.
+ */
+bool pm_diagnostic_list_append(pm_list_t *list, const uint8_t *start, const uint8_t *end, pm_diagnostic_id_t diag_id);
+
+/**
+ * Append a diagnostic to the given list of diagnostics that is using a format
+ * string for its message.
+ *
+ * @param list The list to append to.
+ * @param start The start of the diagnostic.
+ * @param end The end of the diagnostic.
+ * @param diag_id The diagnostic ID.
+ * @param ... The arguments to the format string for the message.
+ * @return Whether the diagnostic was successfully appended.
+ */
+bool pm_diagnostic_list_append_format(pm_list_t *list, const uint8_t *start, const uint8_t *end, pm_diagnostic_id_t diag_id, ...);
+
+/**
+ * Deallocate the internal state of the given diagnostic list.
+ *
+ * @param list The list to deallocate.
+ */
+void pm_diagnostic_list_free(pm_list_t *list);
+
+#endif
diff --git a/prism/templates/lib/prism/compiler.rb.erb b/prism/templates/lib/prism/compiler.rb.erb
new file mode 100644
index 0000000000..45ed88d8de
--- /dev/null
+++ b/prism/templates/lib/prism/compiler.rb.erb
@@ -0,0 +1,41 @@
+module Prism
+ # A compiler is a visitor that returns the value of each node as it visits.
+ # This is as opposed to a visitor which will only walk the tree. This can be
+ # useful when you are trying to compile a tree into a different format.
+ #
+ # For example, to build a representation of the tree as s-expressions, you
+ # could write:
+ #
+ # class SExpressions < Prism::Compiler
+ # def visit_arguments_node(node) = [:arguments, super]
+ # def visit_call_node(node) = [:call, super]
+ # def visit_integer_node(node) = [:integer]
+ # def visit_program_node(node) = [:program, super]
+ # end
+ #
+ # Prism.parse("1 + 2").value.accept(SExpressions.new)
+ # # => [:program, [[[:call, [[:integer], [:arguments, [[:integer]]]]]]]]
+ #
+ class Compiler < Visitor
+ # Visit an individual node.
+ def visit(node)
+ node&.accept(self)
+ end
+
+ # Visit a list of nodes.
+ def visit_all(nodes)
+ nodes.map { |node| node&.accept(self) }
+ end
+
+ # Visit the child nodes of the given node.
+ def visit_child_nodes(node)
+ node.compact_child_nodes.map { |node| node.accept(self) }
+ end
+
+ <%- nodes.each_with_index do |node, index| -%>
+<%= "\n" if index != 0 -%>
+ # Compile a <%= node.name %> node
+ alias visit_<%= node.human %> visit_child_nodes
+ <%- end -%>
+ end
+end
diff --git a/prism/templates/lib/prism/dispatcher.rb.erb b/prism/templates/lib/prism/dispatcher.rb.erb
new file mode 100644
index 0000000000..1370ca7636
--- /dev/null
+++ b/prism/templates/lib/prism/dispatcher.rb.erb
@@ -0,0 +1,89 @@
+module Prism
+ # The dispatcher class fires events for nodes that are found while walking an
+ # AST to all registered listeners. It's useful for performing different types
+ # of analysis on the AST while only having to walk the tree once.
+ #
+ # To use the dispatcher, you would first instantiate it and register listeners
+ # for the events you're interested in:
+ #
+ # class OctalListener
+ # def on_integer_node_enter(node)
+ # if node.octal? && !node.slice.start_with?("0o")
+ # warn("Octal integers should be written with the 0o prefix")
+ # end
+ # end
+ # end
+ #
+ # dispatcher = Dispatcher.new
+ # dispatcher.register(listener, :on_integer_node_enter)
+ #
+ # Then, you can walk any number of trees and dispatch events to the listeners:
+ #
+ # result = Prism.parse("001 + 002 + 003")
+ # dispatcher.dispatch(result.value)
+ #
+ # Optionally, you can also use `#dispatch_once` to dispatch enter and leave
+ # events for a single node without recursing further down the tree. This can
+ # be useful in circumstances where you want to reuse the listeners you already
+ # have registers but want to stop walking the tree at a certain point.
+ #
+ # integer = result.value.statements.body.first.receiver.receiver
+ # dispatcher.dispatch_once(integer)
+ #
+ class Dispatcher < Visitor
+ # attr_reader listeners: Hash[Symbol, Array[Listener]]
+ attr_reader :listeners
+
+ # Initialize a new dispatcher.
+ def initialize
+ @listeners = {}
+ end
+
+ # Register a listener for one or more events.
+ #
+ # def register: (Listener, *Symbol) -> void
+ def register(listener, *events)
+ events.each { |event| (listeners[event] ||= []) << listener }
+ end
+
+ # Walks `root` dispatching events to all registered listeners.
+ #
+ # def dispatch: (Node) -> void
+ alias dispatch visit
+
+ # Dispatches a single event for `node` to all registered listeners.
+ #
+ # def dispatch_once: (Node) -> void
+ def dispatch_once(node)
+ node.accept(DispatchOnce.new(listeners))
+ end
+ <%- nodes.each do |node| -%>
+
+ # Dispatch enter and leave events for <%= node.name %> nodes and continue
+ # walking the tree.
+ def visit_<%= node.human %>(node)
+ listeners[:on_<%= node.human %>_enter]&.each { |listener| listener.on_<%= node.human %>_enter(node) }
+ super
+ listeners[:on_<%= node.human %>_leave]&.each { |listener| listener.on_<%= node.human %>_leave(node) }
+ end
+ <%- end -%>
+
+ class DispatchOnce < Visitor # :nodoc:
+ attr_reader :listeners
+
+ def initialize(listeners)
+ @listeners = listeners
+ end
+ <%- nodes.each do |node| -%>
+
+ # Dispatch enter and leave events for <%= node.name %> nodes.
+ def visit_<%= node.human %>(node)
+ listeners[:on_<%= node.human %>_enter]&.each { |listener| listener.on_<%= node.human %>_enter(node) }
+ listeners[:on_<%= node.human %>_leave]&.each { |listener| listener.on_<%= node.human %>_leave(node) }
+ end
+ <%- end -%>
+ end
+
+ private_constant :DispatchOnce
+ end
+end
diff --git a/prism/templates/lib/prism/dot_visitor.rb.erb b/prism/templates/lib/prism/dot_visitor.rb.erb
new file mode 100644
index 0000000000..93c94b19ee
--- /dev/null
+++ b/prism/templates/lib/prism/dot_visitor.rb.erb
@@ -0,0 +1,186 @@
+require "cgi"
+
+module Prism
+ # This visitor provides the ability to call Node#to_dot, which converts a
+ # subtree into a graphviz dot graph.
+ class DotVisitor < Visitor
+ class Field # :nodoc:
+ attr_reader :name, :value, :port
+
+ def initialize(name, value, port)
+ @name = name
+ @value = value
+ @port = port
+ end
+
+ def to_dot
+ if port
+ "<tr><td align=\"left\" colspan=\"2\" port=\"#{name}\">#{name}</td></tr>"
+ else
+ "<tr><td align=\"left\">#{name}</td><td>#{CGI.escapeHTML(value || raise)}</td></tr>"
+ end
+ end
+ end
+
+ class Table # :nodoc:
+ attr_reader :name, :fields
+
+ def initialize(name)
+ @name = name
+ @fields = []
+ end
+
+ def field(name, value = nil, port: false)
+ fields << Field.new(name, value, port)
+ end
+
+ def to_dot
+ dot = <<~DOT
+ <table border="0" cellborder="1" cellspacing="0" cellpadding="4">
+ <tr><td colspan="2"><b>#{name}</b></td></tr>
+ DOT
+
+ if fields.any?
+ "#{dot} #{fields.map(&:to_dot).join("\n ")}\n</table>"
+ else
+ "#{dot}</table>"
+ end
+ end
+ end
+
+ class Digraph # :nodoc:
+ attr_reader :nodes, :waypoints, :edges
+
+ def initialize
+ @nodes = []
+ @waypoints = []
+ @edges = []
+ end
+
+ def node(value)
+ nodes << value
+ end
+
+ def waypoint(value)
+ waypoints << value
+ end
+
+ def edge(value)
+ edges << value
+ end
+
+ def to_dot
+ <<~DOT
+ digraph "Prism" {
+ node [
+ fontname=\"Courier New\"
+ shape=plain
+ style=filled
+ fillcolor=gray95
+ ];
+
+ #{nodes.map { |node| node.gsub(/\n/, "\n ") }.join("\n ")}
+ node [shape=point];
+ #{waypoints.join("\n ")}
+
+ #{edges.join("\n ")}
+ }
+ DOT
+ end
+ end
+
+ private_constant :Field, :Table, :Digraph
+
+ # The digraph that is being built.
+ attr_reader :digraph
+
+ # Initialize a new dot visitor.
+ def initialize
+ @digraph = Digraph.new
+ end
+
+ # Convert this visitor into a graphviz dot graph string.
+ def to_dot
+ digraph.to_dot
+ end
+ <%- nodes.each do |node| -%>
+
+ # Visit a <%= node.name %> node.
+ def visit_<%= node.human %>(node)
+ table = Table.new("<%= node.name %>")
+ id = node_id(node)
+ <%- node.fields.each do |field| -%>
+
+ # <%= field.name %>
+ <%- case field -%>
+ <%- when Prism::Template::NodeField -%>
+ table.field("<%= field.name %>", port: true)
+ digraph.edge("#{id}:<%= field.name %> -> #{node_id(node.<%= field.name %>)};")
+ <%- when Prism::Template::OptionalNodeField -%>
+ unless (<%= field.name %> = node.<%= field.name %>).nil?
+ table.field("<%= field.name %>", port: true)
+ digraph.edge("#{id}:<%= field.name %> -> #{node_id(<%= field.name %>)};")
+ end
+ <%- when Prism::Template::NodeListField -%>
+ if node.<%= field.name %>.any?
+ table.field("<%= field.name %>", port: true)
+
+ waypoint = "#{id}_<%= field.name %>"
+ digraph.waypoint("#{waypoint};")
+
+ digraph.edge("#{id}:<%= field.name %> -> #{waypoint};")
+ node.<%= field.name %>.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") }
+ else
+ table.field("<%= field.name %>", "[]")
+ end
+ <%- when Prism::Template::StringField, Prism::Template::ConstantField, Prism::Template::OptionalConstantField, Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::ConstantListField, Prism::Template::IntegerField, Prism::Template::DoubleField -%>
+ table.field("<%= field.name %>", node.<%= field.name %>.inspect)
+ <%- when Prism::Template::LocationField -%>
+ table.field("<%= field.name %>", location_inspect(node.<%= field.name %>))
+ <%- when Prism::Template::OptionalLocationField -%>
+ unless (<%= field.name %> = node.<%= field.name %>).nil?
+ table.field("<%= field.name %>", location_inspect(<%= field.name %>))
+ end
+ <%- when Prism::Template::FlagsField -%>
+ <%- flag = flags.find { |flag| flag.name == field.kind }.tap { |flag| raise "Expected to find #{field.kind}" unless flag } -%>
+ table.field("<%= field.name %>", <%= flag.human %>_inspect(node))
+ <%- else -%>
+ <%- raise -%>
+ <%- end -%>
+ <%- end -%>
+
+ digraph.nodes << <<~DOT
+ #{id} [
+ label=<#{table.to_dot.gsub(/\n/, "\n ")}>
+ ];
+ DOT
+
+ super
+ end
+ <%- end -%>
+
+ private
+
+ # Generate a unique node ID for a node throughout the digraph.
+ def node_id(node)
+ "Node_#{node.object_id}"
+ end
+
+ # Inspect a location to display the start and end line and column numbers.
+ def location_inspect(location)
+ "(#{location.start_line},#{location.start_column})-(#{location.end_line},#{location.end_column})"
+ end
+ <%- flags.each do |flag| -%>
+
+ # Inspect a node that has <%= flag.human %> flags to display the flags as a
+ # comma-separated list.
+ def <%= flag.human %>_inspect(node)
+ flags = [] #: Array[String]
+ <%- flag.values.each do |value| -%>
+ flags << "<%= value.name.downcase %>" if node.<%= value.name.downcase %>?
+ <%- end -%>
+ flags.join(", ")
+ end
+ <%- end -%>
+ end
+end
diff --git a/prism/templates/lib/prism/dsl.rb.erb b/prism/templates/lib/prism/dsl.rb.erb
new file mode 100644
index 0000000000..8dbb540952
--- /dev/null
+++ b/prism/templates/lib/prism/dsl.rb.erb
@@ -0,0 +1,49 @@
+module Prism
+ # The DSL module provides a set of methods that can be used to create prism
+ # nodes in a more concise manner. For example, instead of writing:
+ #
+ # source = Prism::Source.new("[1]")
+ #
+ # Prism::ArrayNode.new(
+ # [
+ # Prism::IntegerNode.new(
+ # Prism::IntegerBaseFlags::DECIMAL,
+ # 1,
+ # Prism::Location.new(source, 1, 1),
+ # source
+ # )
+ # ],
+ # Prism::Location.new(source, 0, 1),
+ # Prism::Location.new(source, 2, 1),
+ # source
+ # )
+ #
+ # you could instead write:
+ #
+ # source = Prism::Source.new("[1]")
+ #
+ # ArrayNode(
+ # IntegerNode(Prism::IntegerBaseFlags::DECIMAL, 1, Location(source, 1, 1)), source),
+ # Location(source, 0, 1),
+ # Location(source, 2, 1),
+ # source
+ # )
+ #
+ # This is mostly helpful in the context of writing tests, but can also be used
+ # to generate trees programmatically.
+ module DSL
+ private
+
+ # Create a new Location object
+ def Location(source = nil, start_offset = 0, length = 0)
+ Location.new(source, start_offset, length) # steep:ignore
+ end
+ <%- nodes.each do |node| -%>
+
+ # Create a new <%= node.name %> node
+ def <%= node.name %>(<%= (node.fields.map(&:name) + ["source = nil, location = Location()"]).join(", ") %>)
+ <%= node.name %>.new(<%= ["source", *node.fields.map(&:name), "location"].join(", ") %>)
+ end
+ <%- end -%>
+ end
+end
diff --git a/prism/templates/lib/prism/inspect_visitor.rb.erb b/prism/templates/lib/prism/inspect_visitor.rb.erb
new file mode 100644
index 0000000000..8e7902f0f1
--- /dev/null
+++ b/prism/templates/lib/prism/inspect_visitor.rb.erb
@@ -0,0 +1,137 @@
+module Prism
+ # This visitor is responsible for composing the strings that get returned by
+ # the various #inspect methods defined on each of the nodes.
+ class InspectVisitor < Visitor
+ # Most of the time, we can simply pass down the indent to the next node.
+ # However, when we are inside a list we want some extra special formatting
+ # when we hit an element in that list. In this case, we have a special
+ # command that replaces the subsequent indent with the given value.
+ class Replace # :nodoc:
+ attr_reader :value
+
+ def initialize(value)
+ @value = value
+ end
+ end
+
+ private_constant :Replace
+
+ # The current prefix string.
+ attr_reader :indent
+
+ # The list of commands that we need to execute in order to compose the
+ # final string.
+ attr_reader :commands
+
+ # Initializes a new instance of the InspectVisitor.
+ def initialize(indent = +"")
+ @indent = indent
+ @commands = []
+ end
+
+ # Compose an inspect string for the given node.
+ def self.compose(node)
+ visitor = new
+ node.accept(visitor)
+ visitor.compose
+ end
+
+ # Compose the final string.
+ def compose
+ buffer = +""
+ replace = nil
+
+ until commands.empty?
+ # @type var command: String | node | Replace
+ # @type var indent: String
+ command, indent = *commands.shift
+
+ case command
+ when String
+ buffer << (replace || indent)
+ buffer << command
+ replace = nil
+ when Node
+ visitor = InspectVisitor.new(indent)
+ command.accept(visitor)
+ @commands = [*visitor.commands, *@commands]
+ when Replace
+ replace = command.value
+ else
+ raise "Unknown command: #{command.inspect}"
+ end
+ end
+
+ buffer
+ end
+ <%- nodes.each do |node| -%>
+
+ # Inspect a <%= node.name %> node.
+ def visit_<%= node.human %>(node)
+ commands << [inspect_node(<%= node.name.inspect %>, node), indent]
+ <%- node.fields.each_with_index do |field, index| -%>
+ <%- pointer = index == node.fields.length - 1 ? "└── " : "├── " -%>
+ <%- preadd = index == node.fields.length - 1 ? " " : "│ " -%>
+ <%- case field -%>
+ <%- when Prism::Template::NodeListField -%>
+ commands << ["<%= pointer %><%= field.name %>: (length: #{(<%= field.name %> = node.<%= field.name %>).length})\n", indent]
+ if <%= field.name %>.any?
+ <%= field.name %>[0...-1].each do |child|
+ commands << [Replace.new("#{indent}<%= preadd %>├── "), indent]
+ commands << [child, "#{indent}<%= preadd %>│ "]
+ end
+ commands << [Replace.new("#{indent}<%= preadd %>└── "), indent]
+ commands << [<%= field.name %>[-1], "#{indent}<%= preadd %> "]
+ end
+ <%- when Prism::Template::NodeField -%>
+ commands << ["<%= pointer %><%= field.name %>:\n", indent]
+ commands << [node.<%= field.name %>, "#{indent}<%= preadd %>"]
+ <%- when Prism::Template::OptionalNodeField -%>
+ if (<%= field.name %> = node.<%= field.name %>).nil?
+ commands << ["<%= pointer %><%= field.name %>: ∅\n", indent]
+ else
+ commands << ["<%= pointer %><%= field.name %>:\n", indent]
+ commands << [<%= field.name %>, "#{indent}<%= preadd %>"]
+ end
+ <%- when Prism::Template::ConstantField, Prism::Template::ConstantListField, Prism::Template::StringField, Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::IntegerField, Prism::Template::DoubleField -%>
+ commands << ["<%= pointer %><%= field.name %>: #{node.<%= field.name %>.inspect}\n", indent]
+ <%- when Prism::Template::OptionalConstantField -%>
+ if (<%= field.name %> = node.<%= field.name %>).nil?
+ commands << ["<%= pointer %><%= field.name %>: ∅\n", indent]
+ else
+ commands << ["<%= pointer %><%= field.name %>: #{<%= field.name %>.inspect}\n", indent]
+ end
+ <%- when Prism::Template::FlagsField -%>
+ <%- flag = flags.find { |flag| flag.name == field.kind }.tap { |flag| raise unless flag } -%>
+ flags = [<%= flag.values.map { |value| "(\"#{value.name.downcase}\" if node.#{value.name.downcase}?)" }.join(", ") %>].compact
+ commands << ["<%= pointer %><%= field.name %>: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent]
+ <%- when Prism::Template::LocationField, Prism::Template::OptionalLocationField -%>
+ commands << ["<%= pointer %><%= field.name %>: #{inspect_location(node.<%= field.name %>)}\n", indent]
+ <%- end -%>
+ <%- end -%>
+ end
+ <%- end -%>
+
+ private
+
+ # Compose a header for the given node.
+ def inspect_node(name, node)
+ result = +"@ #{name} ("
+
+ location = node.location
+ result << "location: (#{location.start_line},#{location.start_column})-(#{location.end_line},#{location.end_column})"
+ result << ", newline: true" if node.newline?
+
+ result << ")\n"
+ end
+
+ # Compose a string representing the given inner location field.
+ def inspect_location(location)
+ if location
+ "(#{location.start_line},#{location.start_column})-(#{location.end_line},#{location.end_column}) = #{location.slice.inspect}"
+ else
+ "∅"
+ end
+ end
+ end
+end
diff --git a/prism/templates/lib/prism/mutation_compiler.rb.erb b/prism/templates/lib/prism/mutation_compiler.rb.erb
new file mode 100644
index 0000000000..565ee4e315
--- /dev/null
+++ b/prism/templates/lib/prism/mutation_compiler.rb.erb
@@ -0,0 +1,19 @@
+module Prism
+ # This visitor walks through the tree and copies each node as it is being
+ # visited. This is useful for consumers that want to mutate the tree, as you
+ # can change subtrees in place without effecting the rest of the tree.
+ class MutationCompiler < Compiler
+ <%- nodes.each_with_index do |node, index| -%>
+<%= "\n" if index != 0 -%>
+ # Copy a <%= node.name %> node
+ def visit_<%= node.human %>(node)
+ <%- fields = node.fields.select { |field| [Prism::Template::NodeField, Prism::Template::OptionalNodeField, Prism::Template::NodeListField].include?(field.class) } -%>
+ <%- if fields.any? -%>
+ node.copy(<%= fields.map { |field| "#{field.name}: #{field.is_a?(Prism::Template::NodeListField) ? "visit_all" : "visit"}(node.#{field.name})" }.join(", ") %>)
+ <%- else -%>
+ node.copy
+ <%- end -%>
+ end
+ <%- end -%>
+ end
+end
diff --git a/prism/templates/lib/prism/node.rb.erb b/prism/templates/lib/prism/node.rb.erb
new file mode 100644
index 0000000000..ddbe53e594
--- /dev/null
+++ b/prism/templates/lib/prism/node.rb.erb
@@ -0,0 +1,351 @@
+module Prism
+ # This represents a node in the tree. It is the parent class of all of the
+ # various node types.
+ class Node
+ # A pointer to the source that this node was created from.
+ attr_reader :source
+ private :source
+
+ # A Location instance that represents the location of this node in the
+ # source.
+ def location
+ location = @location
+ return location if location.is_a?(Location)
+ @location = Location.new(source, location >> 32, location & 0xFFFFFFFF)
+ end
+
+ # The start offset of the node in the source. This method is effectively a
+ # delegate method to the location object.
+ def start_offset
+ location = @location
+ location.is_a?(Location) ? location.start_offset : location >> 32
+ end
+
+ # The end offset of the node in the source. This method is effectively a
+ # delegate method to the location object.
+ def end_offset
+ location = @location
+ location.is_a?(Location) ? location.end_offset : ((location >> 32) + (location & 0xFFFFFFFF))
+ end
+
+ def newline? # :nodoc:
+ @newline ? true : false
+ end
+
+ def set_newline_flag(newline_marked) # :nodoc:
+ line = location.start_line
+ unless newline_marked[line]
+ newline_marked[line] = true
+ @newline = true
+ end
+ end
+
+ # Slice the location of the node from the source.
+ def slice
+ location.slice
+ end
+
+ # Slice the location of the node from the source, starting at the beginning
+ # of the line that the location starts on, ending at the end of the line
+ # that the location ends on.
+ def slice_lines
+ location.slice_lines
+ end
+
+ # Similar to inspect, but respects the current level of indentation given by
+ # the pretty print object.
+ def pretty_print(q)
+ q.seplist(inspect.chomp.each_line, -> { q.breakable }) do |line|
+ q.text(line.chomp)
+ end
+ q.current_group.break
+ end
+
+ # Convert this node into a graphviz dot graph string.
+ def to_dot
+ # @type self: node
+ DotVisitor.new.tap { |visitor| accept(visitor) }.to_dot
+ end
+
+ # Returns a list of the fields that exist for this node class. Fields
+ # describe the structure of the node. This kind of reflection is useful for
+ # things like recursively visiting each node _and_ field in the tree.
+ def self.fields
+ # This method should only be called on subclasses of Node, not Node
+ # itself.
+ raise NoMethodError, "undefined method `fields' for #{inspect}" if self == Node
+
+ Reflection.fields_for(self)
+ end
+
+ # --------------------------------------------------------------------------
+ # :section: Node interface
+ # These methods are effectively abstract methods that must be implemented by
+ # the various subclasses of Node. They are here to make it easier to work
+ # with typecheckers.
+ # --------------------------------------------------------------------------
+
+ # Accepts a visitor and calls back into the specialized visit function.
+ def accept(visitor)
+ raise NoMethodError, "undefined method `accept' for #{inspect}"
+ end
+
+ # Returns an array of child nodes, including `nil`s in the place of optional
+ # nodes that were not present.
+ def child_nodes
+ raise NoMethodError, "undefined method `child_nodes' for #{inspect}"
+ end
+
+ alias deconstruct child_nodes
+
+ # Returns an array of child nodes, excluding any `nil`s in the place of
+ # optional nodes that were not present.
+ def compact_child_nodes
+ raise NoMethodError, "undefined method `compact_child_nodes' for #{inspect}"
+ end
+
+ # Returns an array of child nodes and locations that could potentially have
+ # comments attached to them.
+ def comment_targets
+ raise NoMethodError, "undefined method `comment_targets' for #{inspect}"
+ end
+
+ # Returns a symbol symbolizing the type of node that this represents. This
+ # is particularly useful for case statements and array comparisons.
+ def type
+ raise NoMethodError, "undefined method `type' for #{inspect}"
+ end
+
+ # Returns a string representation of the node.
+ def inspect
+ raise NoMethodError, "undefined method `inspect' for #{inspect}"
+ end
+
+ # Returns the type of the node as a symbol.
+ def self.type
+ raise NoMethodError, "undefined method `type' for #{inspect}"
+ end
+ end
+ <%- nodes.each do |node| -%>
+
+ <%- node.each_comment_line do |line| -%>
+ #<%= line %>
+ <%- end -%>
+ class <%= node.name -%> < Node
+ # def initialize: (<%= (node.fields.map { |field| "#{field.rbs_class} #{field.name}" } + ["Location location"]).join(", ") %>) -> void
+ def initialize(source, <%= (node.fields.map(&:name) + ["location"]).join(", ") %>)
+ @source = source
+ @newline = false
+ @location = location
+ <%- node.fields.each do |field| -%>
+ <%- if Prism::Template::CHECK_FIELD_KIND && field.respond_to?(:check_field_kind) -%>
+ raise <%= field.name %>.inspect unless <%= field.check_field_kind %>
+ <%- end -%>
+ @<%= field.name %> = <%= field.name %>
+ <%- end -%>
+ end
+
+ # def accept: (Visitor visitor) -> void
+ def accept(visitor)
+ visitor.visit_<%= node.human %>(self)
+ end
+ <%- if node.newline == false -%>
+
+ def set_newline_flag(newline_marked) # :nodoc:
+ # Never mark <%= node.name %> with a newline flag, mark children instead
+ end
+ <%- elsif node.newline.is_a?(String) -%>
+
+ def set_newline_flag(newline_marked) # :nodoc:
+ <%- field = node.fields.find { |f| f.name == node.newline } or raise node.newline -%>
+ <%- case field -%>
+ <%- when Prism::Template::NodeField -%>
+ <%= field.name %>.set_newline_flag(newline_marked)
+ <%- when Prism::Template::NodeListField -%>
+ first = <%= field.name %>.first
+ first.set_newline_flag(newline_marked) if first
+ <%- else raise field.class.name -%>
+ <%- end -%>
+ end
+ <%- end -%>
+
+ # def child_nodes: () -> Array[nil | Node]
+ def child_nodes
+ [<%= node.fields.map { |field|
+ case field
+ when Prism::Template::NodeField, Prism::Template::OptionalNodeField then field.name
+ when Prism::Template::NodeListField then "*#{field.name}"
+ end
+ }.compact.join(", ") %>]
+ end
+
+ # def compact_child_nodes: () -> Array[Node]
+ def compact_child_nodes
+ <%- if node.fields.any? { |field| field.is_a?(Prism::Template::OptionalNodeField) } -%>
+ compact = [] #: Array[Prism::node]
+ <%- node.fields.each do |field| -%>
+ <%- case field -%>
+ <%- when Prism::Template::NodeField -%>
+ compact << <%= field.name %>
+ <%- when Prism::Template::OptionalNodeField -%>
+ compact << <%= field.name %> if <%= field.name %>
+ <%- when Prism::Template::NodeListField -%>
+ compact.concat(<%= field.name %>)
+ <%- end -%>
+ <%- end -%>
+ compact
+ <%- else -%>
+ [<%= node.fields.map { |field|
+ case field
+ when Prism::Template::NodeField then field.name
+ when Prism::Template::NodeListField then "*#{field.name}"
+ end
+ }.compact.join(", ") %>]
+ <%- end -%>
+ end
+
+ # def comment_targets: () -> Array[Node | Location]
+ def comment_targets
+ [<%= node.fields.map { |field|
+ case field
+ when Prism::Template::NodeField, Prism::Template::LocationField then field.name
+ when Prism::Template::OptionalNodeField, Prism::Template::NodeListField, Prism::Template::OptionalLocationField then "*#{field.name}"
+ end
+ }.compact.join(", ") %>] #: Array[Prism::node | Location]
+ end
+
+ # def copy: (<%= (node.fields.map { |field| "?#{field.name}: #{field.rbs_class}" } + ["?location: Location"]).join(", ") %>) -> <%= node.name %>
+ def copy(<%= (node.fields.map(&:name) + ["location"]).map { |field| "#{field}: self.#{field}" }.join(", ") %>)
+ <%= node.name %>.new(<%= ["source", *node.fields.map(&:name), "location"].join(", ") %>)
+ end
+
+ # def deconstruct: () -> Array[nil | Node]
+ alias deconstruct child_nodes
+
+ # def deconstruct_keys: (Array[Symbol] keys) -> { <%= (node.fields.map { |field| "#{field.name}: #{field.rbs_class}" } + ["location: Location"]).join(", ") %> }
+ def deconstruct_keys(keys)
+ { <%= (node.fields.map { |field| "#{field.name}: #{field.name}" } + ["location: location"]).join(", ") %> }
+ end
+ <%- node.fields.each do |field| -%>
+
+ <%- if field.comment.nil? -%>
+ # <%= "protected " if field.is_a?(Prism::Template::FlagsField) %>attr_reader <%= field.name %>: <%= field.rbs_class %>
+ <%- else -%>
+ <%- field.each_comment_line do |line| -%>
+ #<%= line %>
+ <%- end -%>
+ <%- end -%>
+ <%- case field -%>
+ <%- when Prism::Template::LocationField -%>
+ def <%= field.name %>
+ location = @<%= field.name %>
+ return location if location.is_a?(Location)
+ @<%= field.name %> = Location.new(source, location >> 32, location & 0xFFFFFFFF)
+ end
+ <%- when Prism::Template::OptionalLocationField -%>
+ def <%= field.name %>
+ location = @<%= field.name %>
+ case location
+ when nil
+ nil
+ when Location
+ location
+ else
+ @<%= field.name %> = Location.new(source, location >> 32, location & 0xFFFFFFFF)
+ end
+ end
+ <%- else -%>
+ attr_reader :<%= field.name -%><%= "\n protected :#{field.name}" if field.is_a?(Prism::Template::FlagsField) %>
+ <%- end -%>
+ <%- end -%>
+ <%- node.fields.each do |field| -%>
+ <%- case field -%>
+ <%- when Prism::Template::LocationField -%>
+ <%- raise unless field.name.end_with?("_loc") -%>
+ <%- next if node.fields.any? { |other| other.name == field.name.delete_suffix("_loc") } -%>
+
+ # def <%= field.name.delete_suffix("_loc") %>: () -> String
+ def <%= field.name.delete_suffix("_loc") %>
+ <%= field.name %>.slice
+ end
+ <%- when Prism::Template::OptionalLocationField -%>
+ <%- raise unless field.name.end_with?("_loc") -%>
+ <%- next if node.fields.any? { |other| other.name == field.name.delete_suffix("_loc") } -%>
+
+ # def <%= field.name.delete_suffix("_loc") %>: () -> String?
+ def <%= field.name.delete_suffix("_loc") %>
+ <%= field.name %>&.slice
+ end
+ <%- when Prism::Template::FlagsField -%>
+ <%- flags.find { |flag| flag.name == field.kind }.tap { |flag| raise "Expected to find #{field.kind}" unless flag }.values.each do |value| -%>
+
+ # def <%= value.name.downcase %>?: () -> bool
+ def <%= value.name.downcase %>?
+ <%= field.name %>.anybits?(<%= field.kind %>::<%= value.name %>)
+ end
+ <%- end -%>
+ <%- end -%>
+ <%- end -%>
+
+ # def inspect -> String
+ def inspect
+ InspectVisitor.compose(self)
+ end
+
+ # Sometimes you want to check an instance of a node against a list of
+ # classes to see what kind of behavior to perform. Usually this is done by
+ # calling `[cls1, cls2].include?(node.class)` or putting the node into a
+ # case statement and doing `case node; when cls1; when cls2; end`. Both of
+ # these approaches are relatively slow because of the constant lookups,
+ # method calls, and/or array allocations.
+ #
+ # Instead, you can call #type, which will return to you a symbol that you
+ # can use for comparison. This is faster than the other approaches because
+ # it uses a single integer comparison, but also because if you're on CRuby
+ # you can take advantage of the fact that case statements with all symbol
+ # keys will use a jump table.
+ #
+ # def type: () -> Symbol
+ def type
+ :<%= node.human %>
+ end
+
+ # Similar to #type, this method returns a symbol that you can use for
+ # splitting on the type of the node without having to do a long === chain.
+ # Note that like #type, it will still be slower than using == for a single
+ # class, but should be faster in a case statement or an array comparison.
+ #
+ # def self.type: () -> Symbol
+ def self.type
+ :<%= node.human %>
+ end
+
+ # Implements case-equality for the node. This is effectively == but without
+ # comparing the value of locations. Locations are checked only for presence.
+ def ===(other)
+ other.is_a?(<%= node.name %>)<%= " &&" if node.fields.any? %>
+ <%- node.fields.each_with_index do |field, index| -%>
+ <%- if field.is_a?(Prism::Template::LocationField) || field.is_a?(Prism::Template::OptionalLocationField) -%>
+ (<%= field.name %>.nil? == other.<%= field.name %>.nil?)<%= " &&" if index != node.fields.length - 1 %>
+ <%- elsif field.is_a?(Prism::Template::NodeListField) || field.is_a?(Prism::Template::ConstantListField) -%>
+ (<%= field.name %>.length == other.<%= field.name %>.length) &&
+ <%= field.name %>.zip(other.<%= field.name %>).all? { |left, right| left === right }<%= " &&" if index != node.fields.length - 1 %>
+ <%- else -%>
+ (<%= field.name %> === other.<%= field.name %>)<%= " &&" if index != node.fields.length - 1 %>
+ <%- end -%>
+ <%- end -%>
+ end
+ end
+ <%- end -%>
+ <%- flags.each_with_index do |flag, flag_index| -%>
+
+ # <%= flag.comment %>
+ module <%= flag.name %>
+ <%- flag.values.each_with_index do |value, index| -%>
+ # <%= value.comment %>
+ <%= value.name %> = 1 << <%= index %>
+<%= "\n" if value != flag.values.last -%>
+ <%- end -%>
+ end
+ <%- end -%>
+end
diff --git a/prism/templates/lib/prism/reflection.rb.erb b/prism/templates/lib/prism/reflection.rb.erb
new file mode 100644
index 0000000000..3c1d61c6c1
--- /dev/null
+++ b/prism/templates/lib/prism/reflection.rb.erb
@@ -0,0 +1,137 @@
+module Prism
+ # The Reflection module provides the ability to reflect on the structure of
+ # the syntax tree itself, as opposed to looking at a single syntax tree. This
+ # is useful in metaprogramming contexts.
+ module Reflection
+ # A field represents a single piece of data on a node. It is the base class
+ # for all other field types.
+ class Field
+ # The name of the field.
+ attr_reader :name
+
+ # Initializes the field with the given name.
+ def initialize(name)
+ @name = name
+ end
+ end
+
+ # A node field represents a single child node in the syntax tree. It
+ # resolves to a Prism::Node in Ruby.
+ class NodeField < Field
+ end
+
+ # An optional node field represents a single child node in the syntax tree
+ # that may or may not be present. It resolves to either a Prism::Node or nil
+ # in Ruby.
+ class OptionalNodeField < Field
+ end
+
+ # A node list field represents a list of child nodes in the syntax tree. It
+ # resolves to an array of Prism::Node instances in Ruby.
+ class NodeListField < Field
+ end
+
+ # A constant field represents a constant value on a node. Effectively, it
+ # represents an identifier found within the source. It resolves to a symbol
+ # in Ruby.
+ class ConstantField < Field
+ end
+
+ # An optional constant field represents a constant value on a node that may
+ # or may not be present. It resolves to either a symbol or nil in Ruby.
+ class OptionalConstantField < Field
+ end
+
+ # A constant list field represents a list of constant values on a node. It
+ # resolves to an array of symbols in Ruby.
+ class ConstantListField < Field
+ end
+
+ # A string field represents a string value on a node. It almost always
+ # represents the unescaped value of a string-like literal. It resolves to a
+ # string in Ruby.
+ class StringField < Field
+ end
+
+ # A location field represents the location of some part of the node in the
+ # source code. For example, the location of a keyword or an operator. It
+ # resolves to a Prism::Location in Ruby.
+ class LocationField < Field
+ end
+
+ # An optional location field represents the location of some part of the
+ # node in the source code that may or may not be present. It resolves to
+ # either a Prism::Location or nil in Ruby.
+ class OptionalLocationField < Field
+ end
+
+ # An integer field represents an integer value. It is used to represent the
+ # value of an integer literal, the depth of local variables, and the number
+ # of a numbered reference. It resolves to an Integer in Ruby.
+ class IntegerField < Field
+ end
+
+ # A float field represents a double-precision floating point value. It is
+ # used exclusively to represent the value of a floating point literal. It
+ # resolves to a Float in Ruby.
+ class FloatField < Field
+ end
+
+ # A flags field represents a bitset of flags on a node. It resolves to an
+ # integer in Ruby. Note that the flags cannot be accessed directly on the
+ # node because the integer is kept private. Instead, the various flags in
+ # the bitset should be accessed through their query methods.
+ class FlagsField < Field
+ # The names of the flags in the bitset.
+ attr_reader :flags
+
+ # Initializes the flags field with the given name and flags.
+ def initialize(name, flags)
+ super(name)
+ @flags = flags
+ end
+ end
+
+ # Returns the fields for the given node.
+ def self.fields_for(node)
+ case node.type
+ <%- nodes.each do |node| -%>
+ when :<%= node.human %>
+ [<%= node.fields.map { |field|
+ case field
+ when Prism::Template::NodeField
+ "NodeField.new(:#{field.name})"
+ when Prism::Template::OptionalNodeField
+ "OptionalNodeField.new(:#{field.name})"
+ when Prism::Template::NodeListField
+ "NodeListField.new(:#{field.name})"
+ when Prism::Template::ConstantField
+ "ConstantField.new(:#{field.name})"
+ when Prism::Template::OptionalConstantField
+ "OptionalConstantField.new(:#{field.name})"
+ when Prism::Template::ConstantListField
+ "ConstantListField.new(:#{field.name})"
+ when Prism::Template::StringField
+ "StringField.new(:#{field.name})"
+ when Prism::Template::LocationField
+ "LocationField.new(:#{field.name})"
+ when Prism::Template::OptionalLocationField
+ "OptionalLocationField.new(:#{field.name})"
+ when Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::IntegerField
+ "IntegerField.new(:#{field.name})"
+ when Prism::Template::DoubleField
+ "FloatField.new(:#{field.name})"
+ when Prism::Template::FlagsField
+ found = flags.find { |flag| flag.name == field.kind }.tap { |found| raise "Expected to find #{field.kind}" unless found }
+ "FlagsField.new(:#{field.name}, [#{found.values.map { |value| ":#{value.name.downcase}?" }.join(", ")}])"
+ else
+ raise field.class.name
+ end
+ }.join(", ") %>]
+ <%- end -%>
+ else
+ raise "Unknown node type: #{node.type.inspect}"
+ end
+ end
+ end
+end
diff --git a/prism/templates/lib/prism/serialize.rb.erb b/prism/templates/lib/prism/serialize.rb.erb
new file mode 100644
index 0000000000..c31a319e5f
--- /dev/null
+++ b/prism/templates/lib/prism/serialize.rb.erb
@@ -0,0 +1,404 @@
+require "stringio"
+require_relative "polyfill/string"
+
+module Prism
+ # A module responsible for deserializing parse results.
+ module Serialize
+ # The major version of prism that we are expecting to find in the serialized
+ # strings.
+ MAJOR_VERSION = 0
+
+ # The minor version of prism that we are expecting to find in the serialized
+ # strings.
+ MINOR_VERSION = 27
+
+ # The patch version of prism that we are expecting to find in the serialized
+ # strings.
+ PATCH_VERSION = 0
+
+ # Deserialize the AST represented by the given string into a parse result.
+ def self.load(input, serialized)
+ input = input.dup
+ source = Source.new(input)
+ loader = Loader.new(source, serialized)
+ result = loader.load_result
+
+ input.force_encoding(loader.encoding)
+ result
+ end
+
+ # Deserialize the tokens represented by the given string into a parse
+ # result.
+ def self.load_tokens(source, serialized)
+ Loader.new(source, serialized).load_tokens_result
+ end
+
+ class Loader # :nodoc:
+ if RUBY_ENGINE == "truffleruby"
+ # StringIO is synchronized and that adds a high overhead on TruffleRuby.
+ class FastStringIO # :nodoc:
+ attr_accessor :pos
+
+ def initialize(string)
+ @string = string
+ @pos = 0
+ end
+
+ def getbyte
+ byte = @string.getbyte(@pos)
+ @pos += 1
+ byte
+ end
+
+ def read(n)
+ slice = @string.byteslice(@pos, n)
+ @pos += n
+ slice
+ end
+
+ def eof?
+ @pos >= @string.bytesize
+ end
+ end
+ else
+ FastStringIO = ::StringIO
+ end
+ private_constant :FastStringIO
+
+ attr_reader :encoding, :input, :serialized, :io
+ attr_reader :constant_pool_offset, :constant_pool, :source
+ attr_reader :start_line
+
+ def initialize(source, serialized)
+ @encoding = Encoding::UTF_8
+
+ @input = source.source.dup
+ raise unless serialized.encoding == Encoding::BINARY
+ @serialized = serialized
+ @io = FastStringIO.new(serialized)
+
+ @constant_pool_offset = nil
+ @constant_pool = nil
+
+ @source = source
+ define_load_node_lambdas unless RUBY_ENGINE == "ruby"
+ end
+
+ def load_header
+ raise "Invalid serialization" if io.read(5) != "PRISM"
+ raise "Invalid serialization" if io.read(3).unpack("C3") != [MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION]
+ only_semantic_fields = io.getbyte
+ unless only_semantic_fields == 0
+ raise "Invalid serialization (location fields must be included but are not)"
+ end
+ end
+
+ def load_encoding
+ @encoding = Encoding.find(io.read(load_varuint))
+ @input = input.force_encoding(@encoding).freeze
+ @encoding
+ end
+
+ def load_start_line
+ source.instance_variable_set :@start_line, load_varsint
+ end
+
+ def load_line_offsets
+ source.instance_variable_set :@offsets, Array.new(load_varuint) { load_varuint }
+ end
+
+ def load_comments
+ Array.new(load_varuint) do
+ case load_varuint
+ when 0 then InlineComment.new(load_location_object)
+ when 1 then EmbDocComment.new(load_location_object)
+ end
+ end
+ end
+
+ DIAGNOSTIC_TYPES = [
+ <%- errors.each do |error| -%>
+ <%= error.name.downcase.to_sym.inspect %>,
+ <%- end -%>
+ <%- warnings.each do |warning| -%>
+ <%= warning.name.downcase.to_sym.inspect %>,
+ <%- end -%>
+ ].freeze
+
+ private_constant :DIAGNOSTIC_TYPES
+
+ def load_metadata
+ comments = load_comments
+ magic_comments = Array.new(load_varuint) { MagicComment.new(load_location_object, load_location_object) }
+ data_loc = load_optional_location_object
+ errors = Array.new(load_varuint) { ParseError.new(DIAGNOSTIC_TYPES[load_varuint], load_embedded_string, load_location_object, load_error_level) }
+ warnings = Array.new(load_varuint) { ParseWarning.new(DIAGNOSTIC_TYPES[load_varuint], load_embedded_string, load_location_object, load_warning_level) }
+ [comments, magic_comments, data_loc, errors, warnings]
+ end
+
+ def load_tokens
+ tokens = []
+ while type = TOKEN_TYPES.fetch(load_varuint)
+ start = load_varuint
+ length = load_varuint
+ lex_state = load_varuint
+ location = Location.new(@source, start, length)
+ tokens << [Token.new(source, type, location.slice, location), lex_state]
+ end
+
+ tokens
+ end
+
+ def load_tokens_result
+ tokens = load_tokens
+ encoding = load_encoding
+ load_start_line
+ load_line_offsets
+ comments, magic_comments, data_loc, errors, warnings = load_metadata
+ tokens.each { |token,| token.value.force_encoding(encoding) }
+
+ raise "Expected to consume all bytes while deserializing" unless @io.eof?
+ LexResult.new(tokens, comments, magic_comments, data_loc, errors, warnings, @source)
+ end
+
+ def load_nodes
+ load_header
+ load_encoding
+ load_start_line
+ load_line_offsets
+
+ comments, magic_comments, data_loc, errors, warnings = load_metadata
+
+ @constant_pool_offset = load_uint32
+ @constant_pool = Array.new(load_varuint, nil)
+
+ [load_node, comments, magic_comments, data_loc, errors, warnings]
+ end
+
+ def load_result
+ node, comments, magic_comments, data_loc, errors, warnings = load_nodes
+ ParseResult.new(node, comments, magic_comments, data_loc, errors, warnings, @source)
+ end
+
+ private
+
+ # variable-length integer using https://en.wikipedia.org/wiki/LEB128
+ # This is also what protobuf uses: https://protobuf.dev/programming-guides/encoding/#varints
+ def load_varuint
+ n = io.getbyte
+ if n < 128
+ n
+ else
+ n -= 128
+ shift = 0
+ while (b = io.getbyte) >= 128
+ n += (b - 128) << (shift += 7)
+ end
+ n + (b << (shift + 7))
+ end
+ end
+
+ def load_varsint
+ n = load_varuint
+ (n >> 1) ^ (-(n & 1))
+ end
+
+ def load_integer
+ negative = io.getbyte != 0
+ length = load_varuint
+
+ value = 0
+ length.times { |index| value |= (load_varuint << (index * 32)) }
+
+ value = -value if negative
+ value
+ end
+
+ def load_double
+ io.read(8).unpack1("D")
+ end
+
+ def load_uint32
+ io.read(4).unpack1("L")
+ end
+
+ def load_optional_node
+ if io.getbyte != 0
+ io.pos -= 1
+ load_node
+ end
+ end
+
+ def load_embedded_string
+ io.read(load_varuint).force_encoding(encoding)
+ end
+
+ def load_string
+ type = io.getbyte
+ case type
+ when 1
+ input.byteslice(load_varuint, load_varuint).force_encoding(encoding)
+ when 2
+ load_embedded_string
+ else
+ raise "Unknown serialized string type: #{type}"
+ end
+ end
+
+ def load_location
+ (load_varuint << 32) | load_varuint
+ end
+
+ def load_location_object
+ Location.new(source, load_varuint, load_varuint)
+ end
+
+ def load_optional_location
+ load_location if io.getbyte != 0
+ end
+
+ def load_optional_location_object
+ load_location_object if io.getbyte != 0
+ end
+
+ def load_constant(index)
+ constant = constant_pool[index]
+
+ unless constant
+ offset = constant_pool_offset + index * 8
+ start = @serialized.unpack1("L", offset: offset)
+ length = @serialized.unpack1("L", offset: offset + 4)
+
+ constant =
+ if start.nobits?(1 << 31)
+ input.byteslice(start, length).force_encoding(@encoding).to_sym
+ else
+ @serialized.byteslice(start & ((1 << 31) - 1), length).force_encoding(@encoding).to_sym
+ end
+
+ constant_pool[index] = constant
+ end
+
+ constant
+ end
+
+ def load_required_constant
+ load_constant(load_varuint - 1)
+ end
+
+ def load_optional_constant
+ index = load_varuint
+ load_constant(index - 1) if index != 0
+ end
+
+ def load_error_level
+ level = io.getbyte
+
+ case level
+ when 0
+ :syntax
+ when 1
+ :argument
+ when 2
+ :load
+ else
+ raise "Unknown level: #{level}"
+ end
+ end
+
+ def load_warning_level
+ level = io.getbyte
+
+ case level
+ when 0
+ :default
+ when 1
+ :verbose
+ else
+ raise "Unknown level: #{level}"
+ end
+ end
+
+ if RUBY_ENGINE == 'ruby'
+ def load_node
+ type = io.getbyte
+ location = load_location
+
+ case type
+ <%- nodes.each_with_index do |node, index| -%>
+ when <%= index + 1 %> then
+ <%- if node.needs_serialized_length? -%>
+ load_uint32
+ <%- end -%>
+ <%= node.name %>.new(
+ source, <%= (node.fields.map { |field|
+ case field
+ when Prism::Template::NodeField then "load_node"
+ when Prism::Template::OptionalNodeField then "load_optional_node"
+ when Prism::Template::StringField then "load_string"
+ when Prism::Template::NodeListField then "Array.new(load_varuint) { load_node }"
+ when Prism::Template::ConstantField then "load_required_constant"
+ when Prism::Template::OptionalConstantField then "load_optional_constant"
+ when Prism::Template::ConstantListField then "Array.new(load_varuint) { load_required_constant }"
+ when Prism::Template::LocationField then "load_location"
+ when Prism::Template::OptionalLocationField then "load_optional_location"
+ when Prism::Template::UInt8Field then "io.getbyte"
+ when Prism::Template::UInt32Field, Prism::Template::FlagsField then "load_varuint"
+ when Prism::Template::IntegerField then "load_integer"
+ when Prism::Template::DoubleField then "load_double"
+ else raise
+ end
+ } + ["location"]).join(", ") -%>)
+ <%- end -%>
+ end
+ end
+ else
+ def load_node
+ type = io.getbyte
+ @load_node_lambdas[type].call
+ end
+
+ def define_load_node_lambdas
+ @load_node_lambdas = [
+ nil,
+ <%- nodes.each do |node| -%>
+ -> {
+ location = load_location
+ <%- if node.needs_serialized_length? -%>
+ load_uint32
+ <%- end -%>
+ <%= node.name %>.new(
+ source, <%= (node.fields.map { |field|
+ case field
+ when Prism::Template::NodeField then "load_node"
+ when Prism::Template::OptionalNodeField then "load_optional_node"
+ when Prism::Template::StringField then "load_string"
+ when Prism::Template::NodeListField then "Array.new(load_varuint) { load_node }"
+ when Prism::Template::ConstantField then "load_required_constant"
+ when Prism::Template::OptionalConstantField then "load_optional_constant"
+ when Prism::Template::ConstantListField then "Array.new(load_varuint) { load_required_constant }"
+ when Prism::Template::LocationField then "load_location"
+ when Prism::Template::OptionalLocationField then "load_optional_location"
+ when Prism::Template::UInt8Field then "io.getbyte"
+ when Prism::Template::UInt32Field, Prism::Template::FlagsField then "load_varuint"
+ when Prism::Template::IntegerField then "load_integer"
+ when Prism::Template::DoubleField then "load_double"
+ else raise
+ end
+ } + ["location"]).join(", ") -%>)
+ },
+ <%- end -%>
+ ]
+ end
+ end
+ end
+
+ # The token types that can be indexed by their enum values.
+ TOKEN_TYPES = [
+ nil,
+ <%- tokens.each do |token| -%>
+ <%= token.name.to_sym.inspect %>,
+ <%- end -%>
+ ]
+ end
+end
diff --git a/prism/templates/lib/prism/visitor.rb.erb b/prism/templates/lib/prism/visitor.rb.erb
new file mode 100644
index 0000000000..4b30a1815b
--- /dev/null
+++ b/prism/templates/lib/prism/visitor.rb.erb
@@ -0,0 +1,53 @@
+module Prism
+ # A class that knows how to walk down the tree. None of the individual visit
+ # methods are implemented on this visitor, so it forces the consumer to
+ # implement each one that they need. For a default implementation that
+ # continues walking the tree, see the Visitor class.
+ class BasicVisitor
+ # Calls `accept` on the given node if it is not `nil`, which in turn should
+ # call back into this visitor by calling the appropriate `visit_*` method.
+ def visit(node)
+ # @type self: _Visitor
+ node&.accept(self)
+ end
+
+ # Visits each node in `nodes` by calling `accept` on each one.
+ def visit_all(nodes)
+ # @type self: _Visitor
+ nodes.each { |node| node&.accept(self) }
+ end
+
+ # Visits the child nodes of `node` by calling `accept` on each one.
+ def visit_child_nodes(node)
+ # @type self: _Visitor
+ node.compact_child_nodes.each { |node| node.accept(self) }
+ end
+ end
+
+ # A visitor is a class that provides a default implementation for every accept
+ # method defined on the nodes. This means it can walk a tree without the
+ # caller needing to define any special handling. This allows you to handle a
+ # subset of the tree, while still walking the whole tree.
+ #
+ # For example, to find all of the method calls that call the `foo` method, you
+ # could write:
+ #
+ # class FooCalls < Prism::Visitor
+ # def visit_call_node(node)
+ # if node.name == "foo"
+ # # Do something with the node
+ # end
+ #
+ # # Call super so that the visitor continues walking the tree
+ # super
+ # end
+ # end
+ #
+ class Visitor < BasicVisitor
+ <%- nodes.each_with_index do |node, index| -%>
+<%= "\n" if index != 0 -%>
+ # Visit a <%= node.name %> node
+ alias visit_<%= node.human %> visit_child_nodes
+ <%- end -%>
+ end
+end
diff --git a/prism/templates/src/diagnostic.c.erb b/prism/templates/src/diagnostic.c.erb
new file mode 100644
index 0000000000..839013714b
--- /dev/null
+++ b/prism/templates/src/diagnostic.c.erb
@@ -0,0 +1,487 @@
+#include "prism/diagnostic.h"
+
+#define PM_DIAGNOSTIC_ID_MAX <%= errors.length + warnings.length %>
+
+/** This struct holds the data for each diagnostic. */
+typedef struct {
+ /** The message associated with the diagnostic. */
+ const char* message;
+
+ /** The level associated with the diagnostic. */
+ uint8_t level;
+} pm_diagnostic_data_t;
+
+/**
+ * ## Message composition
+ *
+ * When composing an error message, use sentence fragments.
+ *
+ * Try describing the property of the code that caused the error, rather than
+ * the rule that is being violated. It may help to use a fragment that completes
+ * a sentence beginning, "the parser encountered (a) ...". If appropriate, add a
+ * description of the rule violation (or other helpful context) after a
+ * semicolon.
+ *
+ * For example:, instead of "control escape sequence cannot be doubled", prefer:
+ *
+ * > "invalid control escape sequence; control cannot be repeated"
+ *
+ * In some cases, where the failure is more general or syntax expectations are
+ * violated, it may make more sense to use a fragment that completes a sentence
+ * beginning, "the parser ...".
+ *
+ * For example:
+ *
+ * > "expected an expression after `(`"
+ * > "cannot parse the expression"
+ *
+ * ## Message style guide
+ *
+ * - Use articles like "a", "an", and "the" when appropriate.
+ * - e.g., prefer "cannot parse the expression" to "cannot parse expression".
+ * - Use the common name for tokens and nodes.
+ * - e.g., prefer "keyword splat" to "assoc splat"
+ * - e.g., prefer "embedded document" to "embdoc"
+ * - Do not capitalize the initial word of the message.
+ * - Use back ticks around token literals
+ * - e.g., "Expected a `=>` between the hash key and value"
+ * - Do not use `.` or other punctuation at the end of the message.
+ * - Do not use contractions like "can't". Prefer "cannot" to "can not".
+ * - For tokens that can have multiple meanings, reference the token and its meaning.
+ * - e.g., "`*` splat argument" is clearer and more complete than "splat argument" or "`*` argument"
+ *
+ * ## Error names (PM_ERR_*)
+ *
+ * - When appropriate, prefer node name to token name.
+ * - e.g., prefer "SPLAT" to "STAR" in the context of argument parsing.
+ * - Prefer token name to common name.
+ * - e.g., prefer "STAR" to "ASTERISK".
+ * - Try to order the words in the name from more general to more specific,
+ * - e.g., "INVALID_NUMBER_DECIMAL" is better than "DECIMAL_INVALID_NUMBER".
+ * - When in doubt, look for similar patterns and name them so that they are grouped when lexically
+ * sorted. See PM_ERR_ARGUMENT_NO_FORWARDING_* for an example.
+ *
+ * ## Level
+ *
+ * For errors, they are:
+ *
+ * * `PM_ERROR_LEVEL_SYNTAX` - Errors that should raise SyntaxError.
+ * * `PM_ERROR_LEVEL_ARGUMENT` - Errors that should raise ArgumentError.
+ * * `PM_ERROR_LEVEL_LOAD` - Errors that should raise LoadError.
+ *
+ * For warnings, they are:
+ *
+ * * `PM_WARNING_LEVEL_DEFAULT` - Warnings that appear for `ruby -c -e 'code'`.
+ * * `PM_WARNING_LEVEL_VERBOSE` - Warnings that appear with `-w`, as in `ruby -w -c -e 'code'`.
+ */
+static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = {
+ // Special error that can be replaced
+ [PM_ERR_CANNOT_PARSE_EXPRESSION] = { "cannot parse the expression", PM_ERROR_LEVEL_SYNTAX },
+
+ // Errors that should raise argument errors
+ [PM_ERR_INVALID_ENCODING_MAGIC_COMMENT] = { "unknown or invalid encoding in the magic comment", PM_ERROR_LEVEL_ARGUMENT },
+
+ // Errors that should raise load errors
+ [PM_ERR_SCRIPT_NOT_FOUND] = { "no Ruby script found in input", PM_ERROR_LEVEL_LOAD },
+
+ // Errors that should raise syntax errors
+ [PM_ERR_ALIAS_ARGUMENT] = { "invalid argument being passed to `alias`; expected a bare word, symbol, constant, or global variable", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ALIAS_ARGUMENT_NUMBERED_REFERENCE] = { "invalid argument being passed to `alias`; can't make alias for the number variables", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_AMPAMPEQ_MULTI_ASSIGN] = { "unexpected `&&=` in a multiple assignment", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ARGUMENT_AFTER_BLOCK] = { "unexpected argument after a block argument", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES] = { "unexpected argument after `...`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ARGUMENT_BARE_HASH] = { "unexpected bare hash argument", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ARGUMENT_BLOCK_FORWARDING] = { "both a block argument and a forwarding argument; only one block is allowed", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ARGUMENT_BLOCK_MULTI] = { "both block arg and actual block given; only one block is allowed", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ARGUMENT_FORMAL_CLASS] = { "invalid formal argument; formal argument cannot be a class variable", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ARGUMENT_FORMAL_CONSTANT] = { "invalid formal argument; formal argument cannot be a constant", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ARGUMENT_FORMAL_GLOBAL] = { "invalid formal argument; formal argument cannot be a global variable", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ARGUMENT_FORMAL_IVAR] = { "invalid formal argument; formal argument cannot be an instance variable", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ARGUMENT_FORWARDING_UNBOUND] = { "unexpected `...` in an non-parenthesized call", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ARGUMENT_IN] = { "unexpected `in` keyword in arguments", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ARGUMENT_NO_FORWARDING_AMP] = { "unexpected `&`; no anonymous block parameter", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES] = { "unexpected ... when the parent method is not forwarding", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ARGUMENT_NO_FORWARDING_STAR] = { "unexpected `*`; no anonymous rest parameter", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ARGUMENT_NO_FORWARDING_STAR_STAR] = { "unexpected `**`; no anonymous keyword rest parameter", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT] = { "unexpected `*` splat argument after a `**` keyword splat argument", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ARGUMENT_SPLAT_AFTER_SPLAT] = { "unexpected `*` splat argument after a `*` splat argument", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ARGUMENT_TERM_PAREN] = { "unexpected %s; expected a `)` to close the arguments", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ARGUMENT_UNEXPECTED_BLOCK] = { "unexpected `{` after a method call without parenthesis", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ARRAY_ELEMENT] = { "expected an element for the array", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ARRAY_EXPRESSION] = { "expected an expression for the array element", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ARRAY_EXPRESSION_AFTER_STAR] = { "expected an expression after `*` in the array", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ARRAY_SEPARATOR] = { "expected a `,` separator for the array elements", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ARRAY_TERM] = { "expected a `]` to close the array", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_BEGIN_LONELY_ELSE] = { "unexpected `else` in `begin` block; else without rescue is useless", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_BEGIN_TERM] = { "expected an `end` to close the `begin` statement", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_BEGIN_UPCASE_BRACE] = { "expected a `{` after `BEGIN`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_BEGIN_UPCASE_TERM] = { "expected a `}` to close the `BEGIN` statement", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_BEGIN_UPCASE_TOPLEVEL] = { "BEGIN is permitted only at toplevel", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_BLOCK_PARAM_LOCAL_VARIABLE] = { "expected a local variable name in the block parameters", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_BLOCK_PARAM_PIPE_TERM] = { "expected the block parameters to end with `|`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_BLOCK_TERM_BRACE] = { "expected a block beginning with `{` to end with `}`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_BLOCK_TERM_END] = { "expected a block beginning with `do` to end with `end`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_CANNOT_PARSE_STRING_PART] = { "cannot parse the string part", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_CASE_EXPRESSION_AFTER_CASE] = { "expected an expression after `case`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_CASE_EXPRESSION_AFTER_WHEN] = { "expected an expression after `when`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_CASE_MATCH_MISSING_PREDICATE] = { "expected a predicate for a case matching statement", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_CASE_MISSING_CONDITIONS] = { "expected a `when` or `in` clause after `case`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_CASE_TERM] = { "expected an `end` to close the `case` statement", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_CLASS_IN_METHOD] = { "unexpected class definition in method body", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_CLASS_NAME] = { "unexpected constant path after `class`; class/module name must be CONSTANT", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_CLASS_SUPERCLASS] = { "expected a superclass after `<`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_CLASS_TERM] = { "expected an `end` to close the `class` statement", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_CLASS_UNEXPECTED_END] = { "unexpected `end`, expecting ';' or '\\n'", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_CLASS_VARIABLE_BARE] = { "'@@' without identifiers is not allowed as a class variable name", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_CONDITIONAL_ELSIF_PREDICATE] = { "expected a predicate expression for the `elsif` statement", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_CONDITIONAL_IF_PREDICATE] = { "expected a predicate expression for the `if` statement", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_CONDITIONAL_PREDICATE_TERM] = { "expected `then` or `;` or '\\n'", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_CONDITIONAL_TERM] = { "expected an `end` to close the conditional clause", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_CONDITIONAL_TERM_ELSE] = { "expected an `end` to close the `else` clause", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_CONDITIONAL_UNLESS_PREDICATE] = { "expected a predicate expression for the `unless` statement", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_CONDITIONAL_UNTIL_PREDICATE] = { "expected a predicate expression for the `until` statement", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_CONDITIONAL_WHILE_PREDICATE] = { "expected a predicate expression for the `while` statement", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT] = { "expected a constant after the `::` operator", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_DEF_ENDLESS] = { "could not parse the endless method body", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_DEF_ENDLESS_SETTER] = { "invalid method name; a setter method cannot be defined in an endless method definition", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_DEF_NAME] = { "unexpected %s; expected a method name", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_DEF_PARAMS_TERM] = { "expected a delimiter to close the parameters", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_DEF_PARAMS_TERM_PAREN] = { "expected a `)` to close the parameters", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_DEF_RECEIVER] = { "expected a receiver for the method definition", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_DEF_RECEIVER_TERM] = { "expected a `.` or `::` after the receiver in a method definition", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_DEF_TERM] = { "expected an `end` to close the `def` statement", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_DEFINED_EXPRESSION] = { "expected an expression after `defined?`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EMBDOC_TERM] = { "embedded document meets end of file", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EMBEXPR_END] = { "expected a `}` to close the embedded expression", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EMBVAR_INVALID] = { "invalid embedded variable", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_END_UPCASE_BRACE] = { "expected a `{` after `END`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_END_UPCASE_TERM] = { "expected a `}` to close the `END` statement", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ESCAPE_INVALID_CONTROL] = { "invalid control escape sequence", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT] = { "invalid control escape sequence; control cannot be repeated", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ESCAPE_INVALID_HEXADECIMAL] = { "invalid hexadecimal escape sequence", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ESCAPE_INVALID_META] = { "invalid meta escape sequence", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ESCAPE_INVALID_META_REPEAT] = { "invalid meta escape sequence; meta cannot be repeated", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ESCAPE_INVALID_UNICODE] = { "invalid Unicode escape sequence", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ESCAPE_INVALID_UNICODE_CM_FLAGS] = { "invalid Unicode escape sequence; Unicode cannot be combined with control or meta flags", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ESCAPE_INVALID_UNICODE_LITERAL] = { "invalid Unicode escape sequence; multiple codepoints are not allowed in a character literal", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ESCAPE_INVALID_UNICODE_LONG] = { "invalid Unicode escape sequence; maximum length is 6 digits", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_ESCAPE_INVALID_UNICODE_TERM] = { "invalid Unicode escape sequence; needs closing `}`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPECT_ARGUMENT] = { "expected an argument", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPECT_EOL_AFTER_STATEMENT] = { "unexpected %s, expecting end-of-input", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ] = { "expected an expression after `&&=`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ] = { "expected an expression after `||=`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA] = { "expected an expression after `,`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL] = { "expected an expression after `=`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPECT_EXPRESSION_AFTER_LESS_LESS] = { "expected an expression after `<<`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPECT_EXPRESSION_AFTER_LPAREN] = { "expected an expression after `(`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR] = { "expected an expression after the operator", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT] = { "expected an expression after `*` splat in an argument", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH] = { "expected an expression after `**` in a hash", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPECT_EXPRESSION_AFTER_STAR] = { "expected an expression after `*`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPECT_IDENT_REQ_PARAMETER] = { "expected an identifier for the required parameter", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPECT_LPAREN_REQ_PARAMETER] = { "expected a `(` to start a required parameter", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPECT_MESSAGE] = { "unexpected %s; expecting a message to send to the receiver", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPECT_RBRACKET] = { "expected a matching `]`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPECT_RPAREN] = { "expected a matching `)`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPECT_RPAREN_AFTER_MULTI] = { "expected a `)` after multiple assignment", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPECT_RPAREN_REQ_PARAMETER] = { "expected a `)` to end a required parameter", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPECT_STRING_CONTENT] = { "expected string content after opening string delimiter", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPECT_WHEN_DELIMITER] = { "expected a delimiter after the predicates of a `when` clause", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPRESSION_BARE_HASH] = { "unexpected bare hash in expression", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPRESSION_NOT_WRITABLE] = { "unexpected '='; target cannot be written", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING] = { "Can't assign to __ENCODING__", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE] = { "Can't assign to false", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPRESSION_NOT_WRITABLE_FILE] = { "Can't assign to __FILE__", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPRESSION_NOT_WRITABLE_LINE] = { "Can't assign to __LINE__", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPRESSION_NOT_WRITABLE_NIL] = { "Can't assign to nil", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPRESSION_NOT_WRITABLE_SELF] = { "Can't change the value of self", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE] = { "Can't assign to true", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_FLOAT_PARSE] = { "could not parse the float '%.*s'", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_FOR_COLLECTION] = { "expected a collection after the `in` in a `for` statement", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_FOR_INDEX] = { "expected an index after `for`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_FOR_IN] = { "expected an `in` after the index in a `for` statement", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_FOR_TERM] = { "expected an `end` to close the `for` loop", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_GLOBAL_VARIABLE_BARE] = { "'$' without identifiers is not allowed as a global variable name", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_HASH_EXPRESSION_AFTER_LABEL] = { "expected an expression after the label in a hash", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_HASH_KEY] = { "unexpected %s, expecting '}' or a key in the hash literal", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_HASH_ROCKET] = { "expected a `=>` between the hash key and value", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_HASH_TERM] = { "expected a `}` to close the hash literal", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_HASH_VALUE] = { "expected a value in the hash literal", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_HEREDOC_TERM] = { "could not find a terminator for the heredoc", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INCOMPLETE_QUESTION_MARK] = { "incomplete expression at `?`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3_0] = { "`%.*s' is not allowed as a class variable name", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INCOMPLETE_VARIABLE_CLASS] = { "'%.*s' is not allowed as a class variable name", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3_0] = { "`%.*s' is not allowed as an instance variable name", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INCOMPLETE_VARIABLE_INSTANCE] = { "'%.*s' is not allowed as an instance variable name", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INSTANCE_VARIABLE_BARE] = { "'@' without identifiers is not allowed as an instance variable name", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_BLOCK_EXIT] = { "Invalid %s", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_FLOAT_EXPONENT] = { "invalid exponent", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_LOCAL_VARIABLE_READ] = { "identifier %.*s is not valid to get", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_LOCAL_VARIABLE_WRITE] = { "identifier %.*s is not valid to set", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_NUMBER_BINARY] = { "invalid binary number; numeric literal without digits", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_NUMBER_DECIMAL] = { "invalid decimal number; numeric literal without digits", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_NUMBER_HEXADECIMAL] = { "invalid hexadecimal number; numeric literal without digits", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_NUMBER_OCTAL] = { "invalid octal number; numeric literal without digits", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_NUMBER_UNDERSCORE] = { "invalid underscore placement in number", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_CHARACTER] = { "invalid character 0x%X", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_MULTIBYTE_CHAR] = { "invalid multibyte char (%s)", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_MULTIBYTE_CHARACTER] = { "invalid multibyte character 0x%X", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_MULTIBYTE_ESCAPE] = { "invalid multibyte escape: /%.*s/", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_PRINTABLE_CHARACTER] = { "invalid character `%c`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_PERCENT] = { "invalid `%` token", PM_ERROR_LEVEL_SYNTAX }, // TODO WHAT?
+ [PM_ERR_INVALID_RETRY_AFTER_ELSE] = { "Invalid retry after else", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_RETRY_AFTER_ENSURE] = { "Invalid retry after ensure", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_RETRY_WITHOUT_RESCUE] = { "Invalid retry without rescue", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_VARIABLE_GLOBAL_3_3_0] = { "`%.*s' is not allowed as a global variable name", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_VARIABLE_GLOBAL] = { "'%.*s' is not allowed as a global variable name", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_YIELD] = { "Invalid yield", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_IT_NOT_ALLOWED_NUMBERED] = { "`it` is not allowed when an numbered parameter is defined", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_IT_NOT_ALLOWED_ORDINARY] = { "`it` is not allowed when an ordinary parameter is defined", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_LAMBDA_OPEN] = { "expected a `do` keyword or a `{` to open the lambda block", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_LAMBDA_TERM_BRACE] = { "expected a lambda block beginning with `{` to end with `}`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_LAMBDA_TERM_END] = { "expected a lambda block beginning with `do` to end with `end`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_LIST_I_LOWER_ELEMENT] = { "expected a symbol in a `%i` list", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_LIST_I_LOWER_TERM] = { "expected a closing delimiter for the `%i` list", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_LIST_I_UPPER_ELEMENT] = { "expected a symbol in a `%I` list", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_LIST_I_UPPER_TERM] = { "expected a closing delimiter for the `%I` list", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_LIST_W_LOWER_ELEMENT] = { "expected a string in a `%w` list", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_LIST_W_LOWER_TERM] = { "expected a closing delimiter for the `%w` list", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_LIST_W_UPPER_ELEMENT] = { "expected a string in a `%W` list", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_LIST_W_UPPER_TERM] = { "expected a closing delimiter for the `%W` list", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_MALLOC_FAILED] = { "failed to allocate memory", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_MIXED_ENCODING] = { "UTF-8 mixed within %s source", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_MODULE_IN_METHOD] = { "unexpected module definition in method body", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_MODULE_NAME] = { "unexpected constant path after `module`; class/module name must be CONSTANT", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_MODULE_TERM] = { "expected an `end` to close the `module` statement", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_MULTI_ASSIGN_MULTI_SPLATS] = { "multiple splats in multiple assignment", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_MULTI_ASSIGN_UNEXPECTED_REST] = { "unexpected '%.*s' resulting in multiple splats in multiple assignment", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_NOT_EXPRESSION] = { "expected an expression after `not`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_NO_LOCAL_VARIABLE] = { "%.*s: no such local variable", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_NUMBER_LITERAL_UNDERSCORE] = { "number literal ending with a `_`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_NUMBERED_PARAMETER_IT] = { "numbered parameters are not allowed when an 'it' parameter is defined", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_NUMBERED_PARAMETER_ORDINARY] = { "numbered parameters are not allowed when an ordinary parameter is defined", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_NUMBERED_PARAMETER_OUTER_SCOPE] = { "numbered parameter is already used in outer scope", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_OPERATOR_MULTI_ASSIGN] = { "unexpected operator for a multiple assignment", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_OPERATOR_WRITE_ARGUMENTS] = { "unexpected operator after a call with arguments", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_OPERATOR_WRITE_BLOCK] = { "unexpected operator after a call with a block", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PARAMETER_ASSOC_SPLAT_MULTI] = { "unexpected multiple `**` splat parameters", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PARAMETER_BLOCK_MULTI] = { "multiple block parameters; only one block is allowed", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PARAMETER_CIRCULAR] = { "circular argument reference - %.*s", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PARAMETER_METHOD_NAME] = { "unexpected name for a parameter", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PARAMETER_NAME_DUPLICATED] = { "duplicated argument name", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PARAMETER_NO_DEFAULT] = { "expected a default value for the parameter", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PARAMETER_NO_DEFAULT_KW] = { "expected a default value for the keyword parameter", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PARAMETER_NUMBERED_RESERVED] = { "%.2s is reserved for numbered parameters", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PARAMETER_ORDER] = { "unexpected parameter order", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PARAMETER_SPLAT_MULTI] = { "unexpected multiple `*` splat parameters", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PARAMETER_STAR] = { "unexpected parameter `*`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PARAMETER_UNEXPECTED_FWD] = { "unexpected `...` in parameters", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PARAMETER_WILD_LOOSE_COMMA] = { "unexpected `,` in parameters", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PATTERN_CAPTURE_DUPLICATE] = { "duplicated variable name", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET] = { "expected a pattern expression after the `[` operator", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA] = { "expected a pattern expression after `,`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PATTERN_EXPRESSION_AFTER_HROCKET] = { "expected a pattern expression after `=>`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PATTERN_EXPRESSION_AFTER_IN] = { "expected a pattern expression after the `in` keyword", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PATTERN_EXPRESSION_AFTER_KEY] = { "expected a pattern expression after the key", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN] = { "expected a pattern expression after the `(` operator", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PATTERN_EXPRESSION_AFTER_PIN] = { "expected a pattern expression after the `^` pin operator", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE] = { "expected a pattern expression after the `|` operator", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE] = { "expected a pattern expression after the range operator", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PATTERN_EXPRESSION_AFTER_REST] = { "unexpected pattern expression after the `**` expression", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PATTERN_HASH_KEY] = { "expected a key in the hash pattern", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PATTERN_HASH_KEY_DUPLICATE] = { "duplicated key name", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PATTERN_HASH_KEY_LABEL] = { "expected a label as the key in the hash pattern", PM_ERROR_LEVEL_SYNTAX }, // TODO // THIS // AND // ABOVE // IS WEIRD
+ [PM_ERR_PATTERN_HASH_KEY_LOCALS] = { "key must be valid as local variables", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PATTERN_IDENT_AFTER_HROCKET] = { "expected an identifier after the `=>` operator", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PATTERN_LABEL_AFTER_COMMA] = { "expected a label after the `,` in the hash pattern", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PATTERN_REST] = { "unexpected rest pattern", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PATTERN_TERM_BRACE] = { "expected a `}` to close the pattern expression", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PATTERN_TERM_BRACKET] = { "expected a `]` to close the pattern expression", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PATTERN_TERM_PAREN] = { "expected a `)` to close the pattern expression", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PIPEPIPEEQ_MULTI_ASSIGN] = { "unexpected `||=` in a multiple assignment", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_REGEXP_ENCODING_OPTION_MISMATCH] = { "regexp encoding option '%c' differs from source encoding '%s'", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_REGEXP_INCOMPAT_CHAR_ENCODING] = { "incompatible character encoding: /%.*s/", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_REGEXP_NON_ESCAPED_MBC] = { "/.../n has a non escaped non ASCII character in non ASCII-8BIT script: /%.*s/", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_REGEXP_INVALID_UNICODE_RANGE] = { "invalid Unicode range: /%.*s/", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_REGEXP_UNKNOWN_OPTIONS] = { "unknown regexp %s: %.*s", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_REGEXP_TERM] = { "expected a closing delimiter for the regular expression", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_REGEXP_UTF8_CHAR_NON_UTF8_REGEXP] = { "UTF-8 character in non UTF-8 regexp: /%s/", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_RESCUE_EXPRESSION] = { "expected a rescued expression", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_RESCUE_MODIFIER_VALUE] = { "expected a value after the `rescue` modifier", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_RESCUE_TERM] = { "expected a closing delimiter for the `rescue` clause", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_RESCUE_VARIABLE] = { "expected an exception variable after `=>` in a rescue statement", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_RETURN_INVALID] = { "Invalid return in class/module body", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_SINGLETON_FOR_LITERALS] = { "cannot define singleton method for literals", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_STATEMENT_ALIAS] = { "unexpected an `alias` at a non-statement position", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_STATEMENT_POSTEXE_END] = { "unexpected an `END` at a non-statement position", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_STATEMENT_PREEXE_BEGIN] = { "unexpected a `BEGIN` at a non-statement position", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_STATEMENT_UNDEF] = { "unexpected an `undef` at a non-statement position", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_STRING_CONCATENATION] = { "expected a string for concatenation", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_STRING_INTERPOLATED_TERM] = { "expected a closing delimiter for the interpolated string", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_STRING_LITERAL_EOF] = { "unterminated string meets end of file", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_STRING_LITERAL_TERM] = { "unexpected %s, expected a string literal terminator", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_SYMBOL_INVALID] = { "invalid symbol", PM_ERROR_LEVEL_SYNTAX }, // TODO expected symbol? prism.c ~9719
+ [PM_ERR_SYMBOL_TERM_DYNAMIC] = { "expected a closing delimiter for the dynamic symbol", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_SYMBOL_TERM_INTERPOLATED] = { "expected a closing delimiter for the interpolated symbol", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_TERNARY_COLON] = { "expected a `:` after the true expression of a ternary operator", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_TERNARY_EXPRESSION_FALSE] = { "expected an expression after `:` in the ternary operator", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_TERNARY_EXPRESSION_TRUE] = { "expected an expression after `?` in the ternary operator", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_UNDEF_ARGUMENT] = { "invalid argument being passed to `undef`; expected a bare word, constant, or symbol argument", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_UNARY_RECEIVER] = { "unexpected %s, expected a receiver for unary `%c`", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_UNEXPECTED_BLOCK_ARGUMENT] = { "block argument should not be given", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT] = { "unexpected %s, assuming it is closing the parent %s", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_UNEXPECTED_TOKEN_IGNORE] = { "unexpected %s, ignoring it", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_UNTIL_TERM] = { "expected an `end` to close the `until` statement", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_VOID_EXPRESSION] = { "unexpected void value expression", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_WHILE_TERM] = { "expected an `end` to close the `while` statement", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_WRITE_TARGET_IN_METHOD] = { "dynamic constant assignment", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_WRITE_TARGET_READONLY] = { "Can't set variable %.*s", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_WRITE_TARGET_UNEXPECTED] = { "unexpected write target", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_XSTRING_TERM] = { "expected a closing delimiter for the `%x` or backtick string", PM_ERROR_LEVEL_SYNTAX },
+
+ // Warnings
+ [PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_MINUS] = { "ambiguous first argument; put parentheses or a space even after `-` operator", PM_WARNING_LEVEL_VERBOSE },
+ [PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS] = { "ambiguous first argument; put parentheses or a space even after `+` operator", PM_WARNING_LEVEL_VERBOSE },
+ [PM_WARN_AMBIGUOUS_PREFIX_AMPERSAND] = { "ambiguous `&` has been interpreted as an argument prefix", PM_WARNING_LEVEL_VERBOSE },
+ [PM_WARN_AMBIGUOUS_PREFIX_STAR] = { "ambiguous `*` has been interpreted as an argument prefix", PM_WARNING_LEVEL_VERBOSE },
+ [PM_WARN_AMBIGUOUS_PREFIX_STAR_STAR] = { "ambiguous `**` has been interpreted as an argument prefix", PM_WARNING_LEVEL_VERBOSE },
+ [PM_WARN_AMBIGUOUS_SLASH] = { "ambiguous `/`; wrap regexp in parentheses or add a space after `/` operator", PM_WARNING_LEVEL_VERBOSE },
+ [PM_WARN_COMPARISON_AFTER_COMPARISON] = { "comparison '%.*s' after comparison", PM_WARNING_LEVEL_VERBOSE },
+ [PM_WARN_DOT_DOT_DOT_EOL] = { "... at EOL, should be parenthesized?", PM_WARNING_LEVEL_DEFAULT },
+ [PM_WARN_DUPLICATED_HASH_KEY] = { "key %.*s is duplicated and overwritten on line %" PRIi32, PM_WARNING_LEVEL_DEFAULT },
+ [PM_WARN_DUPLICATED_WHEN_CLAUSE] = { "duplicated 'when' clause with line %" PRIi32 " is ignored", PM_WARNING_LEVEL_VERBOSE },
+ [PM_WARN_EQUAL_IN_CONDITIONAL_3_3_0] = { "found `= literal' in conditional, should be ==", PM_WARNING_LEVEL_DEFAULT },
+ [PM_WARN_EQUAL_IN_CONDITIONAL] = { "found '= literal' in conditional, should be ==", PM_WARNING_LEVEL_DEFAULT },
+ [PM_WARN_END_IN_METHOD] = { "END in method; use at_exit", PM_WARNING_LEVEL_DEFAULT },
+ [PM_WARN_FLOAT_OUT_OF_RANGE] = { "Float %.*s%s out of range", PM_WARNING_LEVEL_VERBOSE },
+ [PM_WARN_IGNORED_FROZEN_STRING_LITERAL] = { "'frozen_string_literal' is ignored after any tokens", PM_WARNING_LEVEL_VERBOSE },
+ [PM_WARN_INTEGER_IN_FLIP_FLOP] = { "integer literal in flip-flop", PM_WARNING_LEVEL_DEFAULT },
+ [PM_WARN_INVALID_CHARACTER] = { "invalid character syntax; use %s%s%s", PM_WARNING_LEVEL_DEFAULT },
+ [PM_WARN_INVALID_SHAREABLE_CONSTANT_VALUE] = { "invalid value for shareable_constant_value: %.*s", PM_WARNING_LEVEL_VERBOSE },
+ [PM_WARN_INVALID_NUMBERED_REFERENCE] = { "'%.*s' is too big for a number variable, always nil", PM_WARNING_LEVEL_DEFAULT },
+ [PM_WARN_KEYWORD_EOL] = { "`%.*s` at the end of line without an expression", PM_WARNING_LEVEL_VERBOSE },
+ [PM_WARN_LITERAL_IN_CONDITION_DEFAULT] = { "%sliteral in %s", PM_WARNING_LEVEL_DEFAULT },
+ [PM_WARN_LITERAL_IN_CONDITION_VERBOSE] = { "%sliteral in %s", PM_WARNING_LEVEL_VERBOSE },
+ [PM_WARN_SHEBANG_CARRIAGE_RETURN] = { "shebang line ending with \\r may cause problems", PM_WARNING_LEVEL_DEFAULT },
+ [PM_WARN_UNEXPECTED_CARRIAGE_RETURN] = { "encountered \\r in middle of line, treated as a mere space", PM_WARNING_LEVEL_DEFAULT },
+ [PM_WARN_UNREACHABLE_STATEMENT] = { "statement not reached", PM_WARNING_LEVEL_VERBOSE },
+ [PM_WARN_UNUSED_LOCAL_VARIABLE] = { "assigned but unused variable - %.*s", PM_WARNING_LEVEL_VERBOSE },
+ [PM_WARN_VOID_STATEMENT] = { "possibly useless use of %.*s in void context", PM_WARNING_LEVEL_VERBOSE }
+};
+
+/**
+ * Get the human-readable name of the given diagnostic ID.
+ */
+const char *
+pm_diagnostic_id_human(pm_diagnostic_id_t diag_id) {
+ switch (diag_id) {
+ <%- errors.each do |error| -%>
+ case PM_ERR_<%= error.name %>: return "<%= error.name.downcase %>";
+ <%- end -%>
+ <%- warnings.each do |warning| -%>
+ case PM_WARN_<%= warning.name %>: return "<%= warning.name.downcase %>";
+ <%- end -%>
+ }
+
+ assert(false && "unreachable");
+ return "";
+}
+
+static inline const char *
+pm_diagnostic_message(pm_diagnostic_id_t diag_id) {
+ assert(diag_id < PM_DIAGNOSTIC_ID_MAX);
+
+ const char *message = diagnostic_messages[diag_id].message;
+ assert(message);
+
+ return message;
+}
+
+static inline uint8_t
+pm_diagnostic_level(pm_diagnostic_id_t diag_id) {
+ assert(diag_id < PM_DIAGNOSTIC_ID_MAX);
+
+ return (uint8_t) diagnostic_messages[diag_id].level;
+}
+
+/**
+ * Append an error to the given list of diagnostic.
+ */
+bool
+pm_diagnostic_list_append(pm_list_t *list, const uint8_t *start, const uint8_t *end, pm_diagnostic_id_t diag_id) {
+ pm_diagnostic_t *diagnostic = (pm_diagnostic_t *) xcalloc(1, sizeof(pm_diagnostic_t));
+ if (diagnostic == NULL) return false;
+
+ *diagnostic = (pm_diagnostic_t) {
+ .location = { start, end },
+ .diag_id = diag_id,
+ .message = pm_diagnostic_message(diag_id),
+ .owned = false,
+ .level = pm_diagnostic_level(diag_id)
+ };
+
+ pm_list_append(list, (pm_list_node_t *) diagnostic);
+ return true;
+}
+
+/**
+ * Append a diagnostic to the given list of diagnostics that is using a format
+ * string for its message.
+ */
+bool
+pm_diagnostic_list_append_format(pm_list_t *list, const uint8_t *start, const uint8_t *end, pm_diagnostic_id_t diag_id, ...) {
+ va_list arguments;
+ va_start(arguments, diag_id);
+
+ const char *format = pm_diagnostic_message(diag_id);
+ int result = vsnprintf(NULL, 0, format, arguments);
+ va_end(arguments);
+
+ if (result < 0) {
+ return false;
+ }
+
+ pm_diagnostic_t *diagnostic = (pm_diagnostic_t *) xcalloc(1, sizeof(pm_diagnostic_t));
+ if (diagnostic == NULL) {
+ return false;
+ }
+
+ size_t length = (size_t) (result + 1);
+ char *message = (char *) xmalloc(length);
+ if (message == NULL) {
+ xfree(diagnostic);
+ return false;
+ }
+
+ va_start(arguments, diag_id);
+ vsnprintf(message, length, format, arguments);
+ va_end(arguments);
+
+ *diagnostic = (pm_diagnostic_t) {
+ .location = { start, end },
+ .diag_id = diag_id,
+ .message = message,
+ .owned = true,
+ .level = pm_diagnostic_level(diag_id)
+ };
+
+ pm_list_append(list, (pm_list_node_t *) diagnostic);
+ return true;
+}
+
+/**
+ * Deallocate the internal state of the given diagnostic list.
+ */
+void
+pm_diagnostic_list_free(pm_list_t *list) {
+ pm_diagnostic_t *node = (pm_diagnostic_t *) list->head;
+
+ while (node != NULL) {
+ pm_diagnostic_t *next = (pm_diagnostic_t *) node->node.next;
+
+ if (node->owned) xfree((void *) node->message);
+ xfree(node);
+
+ node = next;
+ }
+}
diff --git a/prism/templates/src/node.c.erb b/prism/templates/src/node.c.erb
new file mode 100644
index 0000000000..e1c35f5a45
--- /dev/null
+++ b/prism/templates/src/node.c.erb
@@ -0,0 +1,398 @@
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+#include "prism/node.h"
+
+static void
+pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize);
+
+/**
+ * Calculate the size of the node list in bytes.
+ */
+static size_t
+pm_node_list_memsize(pm_node_list_t *node_list, pm_memsize_t *memsize) {
+ pm_node_t *node;
+ PM_NODE_LIST_FOREACH(node_list, index, node) pm_node_memsize_node(node, memsize);
+ return sizeof(pm_node_list_t) + (node_list->capacity * sizeof(pm_node_t *));
+}
+
+/**
+ * Attempts to grow the node list to the next size. If there is already
+ * capacity in the list, this function does nothing. Otherwise it reallocates
+ * the list to be twice as large as it was before. If the reallocation fails,
+ * this function returns false, otherwise it returns true.
+ */
+static bool
+pm_node_list_grow(pm_node_list_t *list, size_t size) {
+ size_t requested_size = list->size + size;
+
+ // If the requested size caused overflow, return false.
+ if (requested_size < list->size) return false;
+
+ // If the requested size is within the existing capacity, return true.
+ if (requested_size < list->capacity) return true;
+
+ // Otherwise, reallocate the list to be twice as large as it was before.
+ size_t next_capacity = list->capacity == 0 ? 4 : list->capacity * 2;
+
+ // If multiplying by 2 caused overflow, return false.
+ if (next_capacity < list->capacity) return false;
+
+ // If we didn't get enough by doubling, keep doubling until we do.
+ while (requested_size > next_capacity) {
+ size_t double_capacity = next_capacity * 2;
+
+ // Ensure we didn't overflow by multiplying by 2.
+ if (double_capacity < next_capacity) return false;
+ next_capacity = double_capacity;
+ }
+
+ pm_node_t **nodes = (pm_node_t **) xrealloc(list->nodes, sizeof(pm_node_t *) * next_capacity);
+ if (nodes == NULL) return false;
+
+ list->nodes = nodes;
+ list->capacity = next_capacity;
+ return true;
+}
+
+/**
+ * Append a new node onto the end of the node list.
+ */
+void
+pm_node_list_append(pm_node_list_t *list, pm_node_t *node) {
+ if (pm_node_list_grow(list, 1)) {
+ list->nodes[list->size++] = node;
+ }
+}
+
+/**
+ * Prepend a new node onto the beginning of the node list.
+ */
+void
+pm_node_list_prepend(pm_node_list_t *list, pm_node_t *node) {
+ if (pm_node_list_grow(list, 1)) {
+ memmove(list->nodes + 1, list->nodes, list->size * sizeof(pm_node_t *));
+ list->nodes[0] = node;
+ list->size++;
+ }
+}
+
+/**
+ * Concatenate the given node list onto the end of the other node list.
+ */
+void
+pm_node_list_concat(pm_node_list_t *list, pm_node_list_t *other) {
+ if (other->size > 0 && pm_node_list_grow(list, other->size)) {
+ memcpy(list->nodes + list->size, other->nodes, other->size * sizeof(pm_node_t *));
+ list->size += other->size;
+ }
+}
+
+/**
+ * Free the internal memory associated with the given node list.
+ */
+void
+pm_node_list_free(pm_node_list_t *list) {
+ if (list->capacity > 0) {
+ xfree(list->nodes);
+ *list = (pm_node_list_t) { 0 };
+ }
+}
+
+PRISM_EXPORTED_FUNCTION void
+pm_node_destroy(pm_parser_t *parser, pm_node_t *node);
+
+/**
+ * Destroy the nodes that are contained within the given node list.
+ */
+static void
+pm_node_list_destroy(pm_parser_t *parser, pm_node_list_t *list) {
+ pm_node_t *node;
+ PM_NODE_LIST_FOREACH(list, index, node) pm_node_destroy(parser, node);
+ pm_node_list_free(list);
+}
+
+/**
+ * Deallocate the space for a pm_node_t. Similarly to pm_node_alloc, we're not
+ * using the parser argument, but it's there to allow for the future possibility
+ * of pre-allocating larger memory pools.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_node_destroy(pm_parser_t *parser, pm_node_t *node) {
+ switch (PM_NODE_TYPE(node)) {
+ <%- nodes.each do |node| -%>
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+ case <%= node.type %>: {
+ <%- if node.fields.any? { |field| ![Prism::Template::LocationField, Prism::Template::OptionalLocationField, Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::FlagsField, Prism::Template::ConstantField, Prism::Template::OptionalConstantField, Prism::Template::DoubleField].include?(field.class) } -%>
+ pm_<%= node.human %>_t *cast = (pm_<%= node.human %>_t *) node;
+ <%- end -%>
+ <%- node.fields.each do |field| -%>
+ <%- case field -%>
+ <%- when Prism::Template::LocationField, Prism::Template::OptionalLocationField, Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::FlagsField, Prism::Template::ConstantField, Prism::Template::OptionalConstantField, Prism::Template::DoubleField -%>
+ <%- when Prism::Template::NodeField -%>
+ pm_node_destroy(parser, (pm_node_t *)cast-><%= field.name %>);
+ <%- when Prism::Template::OptionalNodeField -%>
+ if (cast-><%= field.name %> != NULL) {
+ pm_node_destroy(parser, (pm_node_t *)cast-><%= field.name %>);
+ }
+ <%- when Prism::Template::StringField -%>
+ pm_string_free(&cast-><%= field.name %>);
+ <%- when Prism::Template::NodeListField -%>
+ pm_node_list_destroy(parser, &cast-><%= field.name %>);
+ <%- when Prism::Template::ConstantListField -%>
+ pm_constant_id_list_free(&cast-><%= field.name %>);
+ <%- when Prism::Template::IntegerField -%>
+ pm_integer_free(&cast-><%= field.name %>);
+ <%- else -%>
+ <%- raise -%>
+ <%- end -%>
+ <%- end -%>
+ break;
+ }
+ <%- end -%>
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+ default:
+ assert(false && "unreachable");
+ break;
+ }
+ xfree(node);
+}
+
+static void
+pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) {
+ memsize->node_count++;
+
+ switch (PM_NODE_TYPE(node)) {
+ // We do not calculate memsize of a ScopeNode
+ // as it should never be generated
+ case PM_SCOPE_NODE:
+ return;
+ <%- nodes.each do |node| -%>
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+ case <%= node.type %>: {
+ pm_<%= node.human %>_t *cast = (pm_<%= node.human %>_t *) node;
+ memsize->memsize += sizeof(*cast);
+ <%- node.fields.each do |field| -%>
+ <%- case field -%>
+ <%- when Prism::Template::ConstantField, Prism::Template::OptionalConstantField, Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::FlagsField, Prism::Template::LocationField, Prism::Template::OptionalLocationField, Prism::Template::DoubleField -%>
+ <%- when Prism::Template::NodeField -%>
+ pm_node_memsize_node((pm_node_t *)cast-><%= field.name %>, memsize);
+ <%- when Prism::Template::OptionalNodeField -%>
+ if (cast-><%= field.name %> != NULL) {
+ pm_node_memsize_node((pm_node_t *)cast-><%= field.name %>, memsize);
+ }
+ <%- when Prism::Template::StringField -%>
+ memsize->memsize += (pm_string_memsize(&cast-><%= field.name %>) - sizeof(pm_string_t));
+ <%- when Prism::Template::NodeListField -%>
+ memsize->memsize += (pm_node_list_memsize(&cast-><%= field.name %>, memsize) - sizeof(pm_node_list_t));
+ <%- when Prism::Template::ConstantListField -%>
+ memsize->memsize += (pm_constant_id_list_memsize(&cast-><%= field.name %>) - sizeof(pm_constant_id_list_t));
+ <%- when Prism::Template::IntegerField -%>
+ memsize->memsize += (pm_integer_memsize(&cast-><%= field.name %>) - sizeof(pm_integer_t));
+ <%- else -%>
+ <%- raise -%>
+ <%- end -%>
+ <%- end -%>
+ break;
+ }
+ <%- end -%>
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+ }
+}
+
+/**
+ * Calculates the memory footprint of a given node.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_node_memsize(pm_node_t *node, pm_memsize_t *memsize) {
+ *memsize = (pm_memsize_t) { .memsize = 0, .node_count = 0 };
+ pm_node_memsize_node(node, memsize);
+}
+
+/**
+ * Returns a string representation of the given node type.
+ */
+PRISM_EXPORTED_FUNCTION const char *
+pm_node_type_to_str(pm_node_type_t node_type)
+{
+ switch (node_type) {
+<%- nodes.each do |node| -%>
+ case <%= node.type %>:
+ return "<%= node.type %>";
+<%- end -%>
+ }
+ return "";
+}
+
+/**
+ * Visit each of the nodes in this subtree using the given visitor callback. The
+ * callback function will be called for each node in the subtree. If it returns
+ * false, then that node's children will not be visited. If it returns true,
+ * then the children will be visited. The data parameter is treated as an opaque
+ * pointer and is passed to the visitor callback for consumers to use as they
+ * see fit.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_visit_node(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data) {
+ if (visitor(node, data)) pm_visit_child_nodes(node, visitor, data);
+}
+
+/**
+ * Visit the children of the given node with the given callback. This is the
+ * default behavior for walking the tree that is called from pm_visit_node if
+ * the callback returns true.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_visit_child_nodes(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data) {
+ switch (PM_NODE_TYPE(node)) {
+ <%- nodes.each do |node| -%>
+ <%- if (fields = node.fields.select { |field| field.is_a?(Prism::Template::NodeField) || field.is_a?(Prism::Template::OptionalNodeField) || field.is_a?(Prism::Template::NodeListField) }).any? -%>
+ case <%= node.type %>: {
+ const pm_<%= node.human %>_t *cast = (const pm_<%= node.human %>_t *) node;
+ <%- fields.each do |field| -%>
+
+ // Visit the <%= field.name %> field
+ <%- case field -%>
+ <%- when Prism::Template::NodeField -%>
+ pm_visit_node((const pm_node_t *) cast-><%= field.name %>, visitor, data);
+ <%- when Prism::Template::OptionalNodeField -%>
+ if (cast-><%= field.name %> != NULL) {
+ pm_visit_node((const pm_node_t *) cast-><%= field.name %>, visitor, data);
+ }
+ <%- when Prism::Template::NodeListField -%>
+ const pm_node_list_t *<%= field.name %> = &cast-><%= field.name %>;
+ for (size_t index = 0; index < <%= field.name %>->size; index++) {
+ pm_visit_node(<%= field.name %>->nodes[index], visitor, data);
+ }
+ <%- end -%>
+ <%- end -%>
+
+ break;
+ }
+ <%- else -%>
+ case <%= node.type %>:
+ break;
+ <%- end -%>
+ <%- end -%>
+ case PM_SCOPE_NODE:
+ break;
+ }
+}
+
+// We optionally support dumping to JSON. For systems that don't want or need
+// this functionality, it can be turned off with the PRISM_EXCLUDE_JSON define.
+#ifndef PRISM_EXCLUDE_JSON
+
+static void
+pm_dump_json_constant(pm_buffer_t *buffer, const pm_parser_t *parser, pm_constant_id_t constant_id) {
+ const pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, constant_id);
+ pm_buffer_append_byte(buffer, '"');
+ pm_buffer_append_source(buffer, constant->start, constant->length, PM_BUFFER_ESCAPING_JSON);
+ pm_buffer_append_byte(buffer, '"');
+}
+
+static void
+pm_dump_json_location(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_location_t *location) {
+ uint32_t start = (uint32_t) (location->start - parser->start);
+ uint32_t end = (uint32_t) (location->end - parser->start);
+ pm_buffer_append_format(buffer, "{\"start\":%" PRIu32 ",\"end\":%" PRIu32 "}", start, end);
+}
+
+/**
+ * Dump JSON to the given buffer.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_dump_json(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_node_t *node) {
+ switch (PM_NODE_TYPE(node)) {
+ <%- nodes.each do |node| -%>
+ case <%= node.type %>: {
+ pm_buffer_append_string(buffer, "{\"type\":\"<%= node.name %>\",\"location\":", <%= node.name.bytesize + 22 %>);
+
+ const pm_<%= node.human %>_t *cast = (const pm_<%= node.human %>_t *) node;
+ pm_dump_json_location(buffer, parser, &cast->base.location);
+ <%- node.fields.each_with_index do |field, index| -%>
+
+ // Dump the <%= field.name %> field
+ pm_buffer_append_byte(buffer, ',');
+ pm_buffer_append_string(buffer, "\"<%= field.name %>\":", <%= field.name.bytesize + 3 %>);
+ <%- case field -%>
+ <%- when Prism::Template::NodeField -%>
+ pm_dump_json(buffer, parser, (const pm_node_t *) cast-><%= field.name %>);
+ <%- when Prism::Template::OptionalNodeField -%>
+ if (cast-><%= field.name %> != NULL) {
+ pm_dump_json(buffer, parser, (const pm_node_t *) cast-><%= field.name %>);
+ } else {
+ pm_buffer_append_string(buffer, "null", 4);
+ }
+ <%- when Prism::Template::NodeListField -%>
+ const pm_node_list_t *<%= field.name %> = &cast-><%= field.name %>;
+ pm_buffer_append_byte(buffer, '[');
+
+ for (size_t index = 0; index < <%= field.name %>->size; index++) {
+ if (index != 0) pm_buffer_append_byte(buffer, ',');
+ pm_dump_json(buffer, parser, <%= field.name %>->nodes[index]);
+ }
+ pm_buffer_append_byte(buffer, ']');
+ <%- when Prism::Template::StringField -%>
+ const pm_string_t *<%= field.name %> = &cast-><%= field.name %>;
+ pm_buffer_append_byte(buffer, '"');
+ pm_buffer_append_source(buffer, pm_string_source(<%= field.name %>), pm_string_length(<%= field.name %>), PM_BUFFER_ESCAPING_JSON);
+ pm_buffer_append_byte(buffer, '"');
+ <%- when Prism::Template::ConstantField -%>
+ pm_dump_json_constant(buffer, parser, cast-><%= field.name %>);
+ <%- when Prism::Template::OptionalConstantField -%>
+ if (cast-><%= field.name %> != PM_CONSTANT_ID_UNSET) {
+ pm_dump_json_constant(buffer, parser, cast-><%= field.name %>);
+ } else {
+ pm_buffer_append_string(buffer, "null", 4);
+ }
+ <%- when Prism::Template::ConstantListField -%>
+ const pm_constant_id_list_t *<%= field.name %> = &cast-><%= field.name %>;
+ pm_buffer_append_byte(buffer, '[');
+
+ for (size_t index = 0; index < <%= field.name %>->size; index++) {
+ if (index != 0) pm_buffer_append_byte(buffer, ',');
+ pm_dump_json_constant(buffer, parser, <%= field.name %>->ids[index]);
+ }
+ pm_buffer_append_byte(buffer, ']');
+ <%- when Prism::Template::LocationField -%>
+ pm_dump_json_location(buffer, parser, &cast-><%= field.name %>);
+ <%- when Prism::Template::OptionalLocationField -%>
+ if (cast-><%= field.name %>.start != NULL) {
+ pm_dump_json_location(buffer, parser, &cast-><%= field.name %>);
+ } else {
+ pm_buffer_append_string(buffer, "null", 4);
+ }
+ <%- when Prism::Template::UInt8Field -%>
+ pm_buffer_append_format(buffer, "%" PRIu8, cast-><%= field.name %>);
+ <%- when Prism::Template::UInt32Field -%>
+ pm_buffer_append_format(buffer, "%" PRIu32, cast-><%= field.name %>);
+ <%- when Prism::Template::FlagsField -%>
+ size_t flags = 0;
+ pm_buffer_append_byte(buffer, '[');
+ <%- found = flags.find { |flag| flag.name == field.kind }.tap { |found| raise "Expected to find #{field.kind}" unless found } -%>
+ <%- found.values.each_with_index do |value, index| -%>
+ if (PM_NODE_FLAG_P(cast, PM_<%= found.human.upcase %>_<%= value.name %>)) {
+ if (flags != 0) pm_buffer_append_byte(buffer, ',');
+ pm_buffer_append_string(buffer, "\"<%= value.name %>\"", <%= value.name.bytesize + 2 %>);
+ flags++;
+ }
+ <%- end -%>
+ pm_buffer_append_byte(buffer, ']');
+ <%- when Prism::Template::IntegerField -%>
+ pm_integer_string(buffer, &cast-><%= field.name %>);
+ <%- when Prism::Template::DoubleField -%>
+ pm_buffer_append_format(buffer, "%f", cast-><%= field.name %>);
+ <%- else -%>
+ <%- raise %>
+ <%- end -%>
+ <%- end -%>
+
+ pm_buffer_append_byte(buffer, '}');
+ break;
+ }
+ <%- end -%>
+ case PM_SCOPE_NODE:
+ break;
+ }
+}
+
+#endif
diff --git a/prism/templates/src/prettyprint.c.erb b/prism/templates/src/prettyprint.c.erb
new file mode 100644
index 0000000000..27f44cd996
--- /dev/null
+++ b/prism/templates/src/prettyprint.c.erb
@@ -0,0 +1,167 @@
+<%# encoding: ASCII -%>
+#include "prism/prettyprint.h"
+
+// We optionally support pretty printing nodes. For systems that don't want or
+// need this functionality, it can be turned off with the
+// PRISM_EXCLUDE_PRETTYPRINT define.
+#ifdef PRISM_EXCLUDE_PRETTYPRINT
+
+void pm_prettyprint(void) {}
+
+#else
+
+static inline void
+prettyprint_location(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm_location_t *location) {
+ pm_line_column_t start = pm_newline_list_line_column(&parser->newline_list, location->start, parser->start_line);
+ pm_line_column_t end = pm_newline_list_line_column(&parser->newline_list, location->end, parser->start_line);
+ pm_buffer_append_format(output_buffer, "(%" PRIi32 ",%" PRIu32 ")-(%" PRIi32 ",%" PRIu32 ")", start.line, start.column, end.line, end.column);
+}
+
+static inline void
+prettyprint_constant(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm_constant_id_t constant_id) {
+ pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, constant_id);
+ pm_buffer_append_format(output_buffer, ":%.*s", (int) constant->length, constant->start);
+}
+
+static void
+prettyprint_node(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm_node_t *node, pm_buffer_t *prefix_buffer) {
+ switch (PM_NODE_TYPE(node)) {
+ case PM_SCOPE_NODE:
+ // We do not need to print a ScopeNode as it's not part of the AST.
+ return;
+ <%- nodes.each do |node| -%>
+ case <%= node.type %>: {
+ <%- if node.fields.any? -%>
+ pm_<%= node.human %>_t *cast = (pm_<%= node.human %>_t *) node;
+ <%- end -%>
+ pm_buffer_append_string(output_buffer, "@ <%= node.name %> (location: ", <%= node.name.length + 14 %>);
+ prettyprint_location(output_buffer, parser, &node->location);
+ pm_buffer_append_string(output_buffer, ")\n", 2);
+ <%- node.fields.each_with_index do |field, index| -%>
+ <%- preadd = index == node.fields.length - 1 ? " " : "| " -%>
+
+ // <%= field.name %>
+ {
+ pm_buffer_concat(output_buffer, prefix_buffer);
+ pm_buffer_append_string(output_buffer, "+-- <%= field.name %>:", <%= 4 + field.name.length + 1 %>);
+ <%- case field -%>
+ <%- when Prism::Template::NodeField -%>
+ pm_buffer_append_byte(output_buffer, '\n');
+
+ size_t prefix_length = prefix_buffer->length;
+ pm_buffer_append_string(prefix_buffer, "<%= preadd %>", 4);
+ pm_buffer_concat(output_buffer, prefix_buffer);
+ prettyprint_node(output_buffer, parser, (pm_node_t *) cast-><%= field.name %>, prefix_buffer);
+ prefix_buffer->length = prefix_length;
+ <%- when Prism::Template::OptionalNodeField -%>
+ if (cast-><%= field.name %> == NULL) {
+ pm_buffer_append_string(output_buffer, " nil\n", 5);
+ } else {
+ pm_buffer_append_byte(output_buffer, '\n');
+
+ size_t prefix_length = prefix_buffer->length;
+ pm_buffer_append_string(prefix_buffer, "<%= preadd %>", 4);
+ pm_buffer_concat(output_buffer, prefix_buffer);
+ prettyprint_node(output_buffer, parser, (pm_node_t *) cast-><%= field.name %>, prefix_buffer);
+ prefix_buffer->length = prefix_length;
+ }
+ <%- when Prism::Template::StringField -%>
+ pm_buffer_append_string(output_buffer, " \"", 2);
+ pm_buffer_append_source(output_buffer, pm_string_source(&cast-><%= field.name %>), pm_string_length(&cast-><%= field.name %>), PM_BUFFER_ESCAPING_RUBY);
+ pm_buffer_append_string(output_buffer, "\"\n", 2);
+ <%- when Prism::Template::NodeListField -%>
+ pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast-><%= field.name %>.size));
+
+ size_t last_index = cast-><%= field.name %>.size;
+ for (uint32_t index = 0; index < last_index; index++) {
+ size_t prefix_length = prefix_buffer->length;
+ pm_buffer_append_string(prefix_buffer, "<%= preadd %>", 4);
+ pm_buffer_concat(output_buffer, prefix_buffer);
+ pm_buffer_append_string(output_buffer, "+-- ", 4);
+ pm_buffer_append_string(prefix_buffer, (index == last_index - 1) ? " " : "| ", 4);
+ prettyprint_node(output_buffer, parser, (pm_node_t *) cast-><%= field.name %>.nodes[index], prefix_buffer);
+ prefix_buffer->length = prefix_length;
+ }
+ <%- when Prism::Template::ConstantField -%>
+ pm_buffer_append_byte(output_buffer, ' ');
+ prettyprint_constant(output_buffer, parser, cast-><%= field.name %>);
+ pm_buffer_append_byte(output_buffer, '\n');
+ <%- when Prism::Template::OptionalConstantField -%>
+ if (cast-><%= field.name %> == 0) {
+ pm_buffer_append_string(output_buffer, " nil\n", 5);
+ } else {
+ pm_buffer_append_byte(output_buffer, ' ');
+ prettyprint_constant(output_buffer, parser, cast-><%= field.name %>);
+ pm_buffer_append_byte(output_buffer, '\n');
+ }
+ <%- when Prism::Template::ConstantListField -%>
+ pm_buffer_append_string(output_buffer, " [", 2);
+ for (uint32_t index = 0; index < cast-><%= field.name %>.size; index++) {
+ if (index != 0) pm_buffer_append_string(output_buffer, ", ", 2);
+ prettyprint_constant(output_buffer, parser, cast-><%= field.name %>.ids[index]);
+ }
+ pm_buffer_append_string(output_buffer, "]\n", 2);
+ <%- when Prism::Template::LocationField -%>
+ pm_location_t *location = &cast-><%= field.name %>;
+ pm_buffer_append_byte(output_buffer, ' ');
+ prettyprint_location(output_buffer, parser, location);
+ pm_buffer_append_string(output_buffer, " = \"", 4);
+ pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY);
+ pm_buffer_append_string(output_buffer, "\"\n", 2);
+ <%- when Prism::Template::OptionalLocationField -%>
+ pm_location_t *location = &cast-><%= field.name %>;
+ if (location->start == NULL) {
+ pm_buffer_append_string(output_buffer, " nil\n", 5);
+ } else {
+ pm_buffer_append_byte(output_buffer, ' ');
+ prettyprint_location(output_buffer, parser, location);
+ pm_buffer_append_string(output_buffer, " = \"", 4);
+ pm_buffer_append_source(output_buffer, location->start, (size_t) (location->end - location->start), PM_BUFFER_ESCAPING_RUBY);
+ pm_buffer_append_string(output_buffer, "\"\n", 2);
+ }
+ <%- when Prism::Template::UInt8Field -%>
+ pm_buffer_append_format(output_buffer, " %" PRIu8 "\n", cast-><%= field.name %>);
+ <%- when Prism::Template::UInt32Field -%>
+ pm_buffer_append_format(output_buffer, " %" PRIu32 "\n", cast-><%= field.name %>);
+ <%- when Prism::Template::FlagsField -%>
+ bool found = false;
+ <%- found = flags.find { |flag| flag.name == field.kind }.tap { |found| raise "Expected to find #{field.kind}" unless found } -%>
+ <%- found.values.each do |value| -%>
+ if (cast->base.<%= field.name %> & PM_<%= found.human.upcase %>_<%= value.name %>) {
+ if (found) pm_buffer_append_byte(output_buffer, ',');
+ pm_buffer_append_string(output_buffer, " <%= value.name.downcase %>", <%= value.name.bytesize + 1 %>);
+ found = true;
+ }
+ <%- end -%>
+ if (!found) pm_buffer_append_string(output_buffer, " nil", 4);
+ pm_buffer_append_byte(output_buffer, '\n');
+ <%- when Prism::Template::IntegerField -%>
+ const pm_integer_t *integer = &cast-><%= field.name %>;
+ pm_buffer_append_byte(output_buffer, ' ');
+ pm_integer_string(output_buffer, integer);
+ pm_buffer_append_byte(output_buffer, '\n');
+ <%- when Prism::Template::DoubleField -%>
+ pm_buffer_append_format(output_buffer, " %f\n", cast-><%= field.name %>);
+ <%- else -%>
+ <%- raise -%>
+ <%- end -%>
+ }
+ <%- end -%>
+
+ break;
+ }
+ <%- end -%>
+ }
+}
+
+/**
+ * Pretty-prints the AST represented by the given node to the given buffer.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_prettyprint(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm_node_t *node) {
+ pm_buffer_t prefix_buffer = { 0 };
+ prettyprint_node(output_buffer, parser, node, &prefix_buffer);
+ pm_buffer_free(&prefix_buffer);
+}
+
+#endif
diff --git a/prism/templates/src/serialize.c.erb b/prism/templates/src/serialize.c.erb
new file mode 100644
index 0000000000..97101e36d5
--- /dev/null
+++ b/prism/templates/src/serialize.c.erb
@@ -0,0 +1,402 @@
+#include "prism.h"
+
+// We optionally support serializing to a binary string. For systems that don't
+// want or need this functionality, it can be turned off with the
+// PRISM_EXCLUDE_SERIALIZATION define.
+#ifndef PRISM_EXCLUDE_SERIALIZATION
+
+#include <stdio.h>
+
+static inline uint32_t
+pm_ptrdifft_to_u32(ptrdiff_t value) {
+ assert(value >= 0 && ((unsigned long) value) < UINT32_MAX);
+ return (uint32_t) value;
+}
+
+static inline uint32_t
+pm_sizet_to_u32(size_t value) {
+ assert(value < UINT32_MAX);
+ return (uint32_t) value;
+}
+
+static void
+pm_serialize_location(const pm_parser_t *parser, const pm_location_t *location, pm_buffer_t *buffer) {
+ assert(location->start);
+ assert(location->end);
+ assert(location->start <= location->end);
+
+ pm_buffer_append_varuint(buffer, pm_ptrdifft_to_u32(location->start - parser->start));
+ pm_buffer_append_varuint(buffer, pm_ptrdifft_to_u32(location->end - location->start));
+}
+
+static void
+pm_serialize_string(const pm_parser_t *parser, const pm_string_t *string, pm_buffer_t *buffer) {
+ switch (string->type) {
+ case PM_STRING_SHARED: {
+ pm_buffer_append_byte(buffer, 1);
+ pm_buffer_append_varuint(buffer, pm_ptrdifft_to_u32(pm_string_source(string) - parser->start));
+ pm_buffer_append_varuint(buffer, pm_sizet_to_u32(pm_string_length(string)));
+ break;
+ }
+ case PM_STRING_OWNED:
+ case PM_STRING_CONSTANT: {
+ uint32_t length = pm_sizet_to_u32(pm_string_length(string));
+ pm_buffer_append_byte(buffer, 2);
+ pm_buffer_append_varuint(buffer, length);
+ pm_buffer_append_bytes(buffer, pm_string_source(string), length);
+ break;
+ }
+#ifdef PRISM_HAS_MMAP
+ case PM_STRING_MAPPED:
+ assert(false && "Cannot serialize mapped strings.");
+ break;
+#endif
+ }
+}
+
+static void
+pm_serialize_integer(const pm_integer_t *integer, pm_buffer_t *buffer) {
+ pm_buffer_append_byte(buffer, integer->negative ? 1 : 0);
+ if (integer->values == NULL) {
+ pm_buffer_append_varuint(buffer, pm_sizet_to_u32(1));
+ pm_buffer_append_varuint(buffer, integer->value);
+ } else {
+ pm_buffer_append_varuint(buffer, pm_sizet_to_u32(integer->length));
+ for (size_t i = 0; i < integer->length; i++) {
+ pm_buffer_append_varuint(buffer, integer->values[i]);
+ }
+ }
+}
+
+static void
+pm_serialize_node(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) {
+ pm_buffer_append_byte(buffer, (uint8_t) PM_NODE_TYPE(node));
+
+ size_t offset = buffer->length;
+
+ pm_serialize_location(parser, &node->location, buffer);
+
+ switch (PM_NODE_TYPE(node)) {
+ // We do not need to serialize a ScopeNode ever as
+ // it is not part of the AST
+ case PM_SCOPE_NODE:
+ return;
+ <%- nodes.each do |node| -%>
+ case <%= node.type %>: {
+ <%- if node.needs_serialized_length? -%>
+ // serialize length
+ // encoding of location u32s make us need to save this offset.
+ size_t length_offset = buffer->length;
+ pm_buffer_append_string(buffer, "\0\0\0\0", 4); /* consume 4 bytes, updated below */
+ <%- end -%>
+ <%- node.fields.each do |field| -%>
+ <%- case field -%>
+ <%- when Prism::Template::NodeField -%>
+ pm_serialize_node(parser, (pm_node_t *)((pm_<%= node.human %>_t *)node)-><%= field.name %>, buffer);
+ <%- when Prism::Template::OptionalNodeField -%>
+ if (((pm_<%= node.human %>_t *)node)-><%= field.name %> == NULL) {
+ pm_buffer_append_byte(buffer, 0);
+ } else {
+ pm_serialize_node(parser, (pm_node_t *)((pm_<%= node.human %>_t *)node)-><%= field.name %>, buffer);
+ }
+ <%- when Prism::Template::StringField -%>
+ pm_serialize_string(parser, &((pm_<%= node.human %>_t *)node)-><%= field.name %>, buffer);
+ <%- when Prism::Template::NodeListField -%>
+ uint32_t <%= field.name %>_size = pm_sizet_to_u32(((pm_<%= node.human %>_t *)node)-><%= field.name %>.size);
+ pm_buffer_append_varuint(buffer, <%= field.name %>_size);
+ for (uint32_t index = 0; index < <%= field.name %>_size; index++) {
+ pm_serialize_node(parser, (pm_node_t *) ((pm_<%= node.human %>_t *)node)-><%= field.name %>.nodes[index], buffer);
+ }
+ <%- when Prism::Template::ConstantField, Prism::Template::OptionalConstantField -%>
+ pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_<%= node.human %>_t *)node)-><%= field.name %>));
+ <%- when Prism::Template::ConstantListField -%>
+ uint32_t <%= field.name %>_size = pm_sizet_to_u32(((pm_<%= node.human %>_t *)node)-><%= field.name %>.size);
+ pm_buffer_append_varuint(buffer, <%= field.name %>_size);
+ for (uint32_t index = 0; index < <%= field.name %>_size; index++) {
+ pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_<%= node.human %>_t *)node)-><%= field.name %>.ids[index]));
+ }
+ <%- when Prism::Template::LocationField -%>
+ <%- if field.should_be_serialized? -%>
+ pm_serialize_location(parser, &((pm_<%= node.human %>_t *)node)-><%= field.name %>, buffer);
+ <%- end -%>
+ <%- when Prism::Template::OptionalLocationField -%>
+ <%- if field.should_be_serialized? -%>
+ if (((pm_<%= node.human %>_t *)node)-><%= field.name %>.start == NULL) {
+ pm_buffer_append_byte(buffer, 0);
+ } else {
+ pm_buffer_append_byte(buffer, 1);
+ pm_serialize_location(parser, &((pm_<%= node.human %>_t *)node)-><%= field.name %>, buffer);
+ }
+ <%- end -%>
+ <%- when Prism::Template::UInt8Field -%>
+ pm_buffer_append_byte(buffer, ((pm_<%= node.human %>_t *)node)-><%= field.name %>);
+ <%- when Prism::Template::UInt32Field -%>
+ pm_buffer_append_varuint(buffer, ((pm_<%= node.human %>_t *)node)-><%= field.name %>);
+ <%- when Prism::Template::FlagsField -%>
+ pm_buffer_append_varuint(buffer, (uint32_t)(node->flags & ~PM_NODE_FLAG_COMMON_MASK));
+ <%- when Prism::Template::IntegerField -%>
+ pm_serialize_integer(&((pm_<%= node.human %>_t *)node)-><%= field.name %>, buffer);
+ <%- when Prism::Template::DoubleField -%>
+ pm_buffer_append_double(buffer, ((pm_<%= node.human %>_t *)node)-><%= field.name %>);
+ <%- else -%>
+ <%- raise -%>
+ <%- end -%>
+ <%- end -%>
+ <%- if node.needs_serialized_length? -%>
+ // serialize length
+ uint32_t length = pm_sizet_to_u32(buffer->length - offset - sizeof(uint32_t));
+ memcpy(buffer->value + length_offset, &length, sizeof(uint32_t));
+ <%- end -%>
+ break;
+ }
+ <%- end -%>
+ }
+}
+
+static void
+pm_serialize_newline_list(pm_newline_list_t *list, pm_buffer_t *buffer) {
+ uint32_t size = pm_sizet_to_u32(list->size);
+ pm_buffer_append_varuint(buffer, size);
+
+ for (uint32_t i = 0; i < size; i++) {
+ uint32_t offset = pm_sizet_to_u32(list->offsets[i]);
+ pm_buffer_append_varuint(buffer, offset);
+ }
+}
+
+static void
+pm_serialize_comment(pm_parser_t *parser, pm_comment_t *comment, pm_buffer_t *buffer) {
+ // serialize type
+ pm_buffer_append_byte(buffer, (uint8_t) comment->type);
+
+ // serialize location
+ pm_serialize_location(parser, &comment->location, buffer);
+}
+
+/**
+ * Serialize the given list of comments to the given buffer.
+ */
+void
+pm_serialize_comment_list(pm_parser_t *parser, pm_list_t *list, pm_buffer_t *buffer) {
+ pm_buffer_append_varuint(buffer, pm_sizet_to_u32(pm_list_size(list)));
+
+ pm_comment_t *comment;
+ for (comment = (pm_comment_t *) list->head; comment != NULL; comment = (pm_comment_t *) comment->node.next) {
+ pm_serialize_comment(parser, comment, buffer);
+ }
+}
+
+static void
+pm_serialize_magic_comment(pm_parser_t *parser, pm_magic_comment_t *magic_comment, pm_buffer_t *buffer) {
+ // serialize key location
+ pm_buffer_append_varuint(buffer, pm_ptrdifft_to_u32(magic_comment->key_start - parser->start));
+ pm_buffer_append_varuint(buffer, pm_sizet_to_u32(magic_comment->key_length));
+
+ // serialize value location
+ pm_buffer_append_varuint(buffer, pm_ptrdifft_to_u32(magic_comment->value_start - parser->start));
+ pm_buffer_append_varuint(buffer, pm_sizet_to_u32(magic_comment->value_length));
+}
+
+static void
+pm_serialize_magic_comment_list(pm_parser_t *parser, pm_list_t *list, pm_buffer_t *buffer) {
+ pm_buffer_append_varuint(buffer, pm_sizet_to_u32(pm_list_size(list)));
+
+ pm_magic_comment_t *magic_comment;
+ for (magic_comment = (pm_magic_comment_t *) list->head; magic_comment != NULL; magic_comment = (pm_magic_comment_t *) magic_comment->node.next) {
+ pm_serialize_magic_comment(parser, magic_comment, buffer);
+ }
+}
+
+static void
+pm_serialize_data_loc(const pm_parser_t *parser, pm_buffer_t *buffer) {
+ if (parser->data_loc.end == NULL) {
+ pm_buffer_append_byte(buffer, 0);
+ } else {
+ pm_buffer_append_byte(buffer, 1);
+ pm_serialize_location(parser, &parser->data_loc, buffer);
+ }
+}
+
+static void
+pm_serialize_diagnostic(pm_parser_t *parser, pm_diagnostic_t *diagnostic, pm_buffer_t *buffer) {
+ // serialize the type
+ pm_buffer_append_varuint(buffer, (uint32_t) diagnostic->diag_id);
+
+ // serialize message
+ size_t message_length = strlen(diagnostic->message);
+ pm_buffer_append_varuint(buffer, pm_sizet_to_u32(message_length));
+ pm_buffer_append_string(buffer, diagnostic->message, message_length);
+
+ // serialize location
+ pm_serialize_location(parser, &diagnostic->location, buffer);
+
+ pm_buffer_append_byte(buffer, diagnostic->level);
+}
+
+static void
+pm_serialize_diagnostic_list(pm_parser_t *parser, pm_list_t *list, pm_buffer_t *buffer) {
+ pm_buffer_append_varuint(buffer, pm_sizet_to_u32(pm_list_size(list)));
+
+ pm_diagnostic_t *diagnostic;
+ for (diagnostic = (pm_diagnostic_t *) list->head; diagnostic != NULL; diagnostic = (pm_diagnostic_t *) diagnostic->node.next) {
+ pm_serialize_diagnostic(parser, diagnostic, buffer);
+ }
+}
+
+/**
+ * Serialize the name of the encoding to the buffer.
+ */
+void
+pm_serialize_encoding(const pm_encoding_t *encoding, pm_buffer_t *buffer) {
+ size_t encoding_length = strlen(encoding->name);
+ pm_buffer_append_varuint(buffer, pm_sizet_to_u32(encoding_length));
+ pm_buffer_append_string(buffer, encoding->name, encoding_length);
+}
+
+static void
+pm_serialize_metadata(pm_parser_t *parser, pm_buffer_t *buffer) {
+ pm_serialize_encoding(parser->encoding, buffer);
+ pm_buffer_append_varsint(buffer, parser->start_line);
+ pm_serialize_newline_list(&parser->newline_list, buffer);
+<%- unless Prism::Template::SERIALIZE_ONLY_SEMANTICS_FIELDS -%>
+ pm_serialize_comment_list(parser, &parser->comment_list, buffer);
+<%- end -%>
+ pm_serialize_magic_comment_list(parser, &parser->magic_comment_list, buffer);
+ pm_serialize_data_loc(parser, buffer);
+ pm_serialize_diagnostic_list(parser, &parser->error_list, buffer);
+ pm_serialize_diagnostic_list(parser, &parser->warning_list, buffer);
+}
+
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+/**
+ * Serialize the metadata, nodes, and constant pool.
+ */
+void
+pm_serialize_content(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) {
+ pm_serialize_metadata(parser, buffer);
+
+ // Here we're going to leave space for the offset of the constant pool in
+ // the buffer.
+ size_t offset = buffer->length;
+ pm_buffer_append_zeroes(buffer, 4);
+
+ // Next, encode the length of the constant pool.
+ pm_buffer_append_varuint(buffer, parser->constant_pool.size);
+
+ // Now we're going to serialize the content of the node.
+ pm_serialize_node(parser, node, buffer);
+
+ // Now we're going to serialize the offset of the constant pool back where
+ // we left space for it.
+ uint32_t length = pm_sizet_to_u32(buffer->length);
+ memcpy(buffer->value + offset, &length, sizeof(uint32_t));
+
+ // Now we're going to serialize the constant pool.
+ offset = buffer->length;
+ pm_buffer_append_zeroes(buffer, parser->constant_pool.size * 8);
+
+ for (uint32_t index = 0; index < parser->constant_pool.capacity; index++) {
+ pm_constant_pool_bucket_t *bucket = &parser->constant_pool.buckets[index];
+
+ // If we find a constant at this index, serialize it at the correct
+ // index in the buffer.
+ if (bucket->id != 0) {
+ pm_constant_t *constant = &parser->constant_pool.constants[bucket->id - 1];
+ size_t buffer_offset = offset + ((((size_t)bucket->id) - 1) * 8);
+
+ if (bucket->type == PM_CONSTANT_POOL_BUCKET_OWNED || bucket->type == PM_CONSTANT_POOL_BUCKET_CONSTANT) {
+ // Since this is an owned or constant constant, we are going to
+ // write its contents into the buffer after the constant pool.
+ // So effectively in place of the source offset, we have a
+ // buffer offset. We will add a leading 1 to indicate that this
+ // is a buffer offset.
+ uint32_t content_offset = pm_sizet_to_u32(buffer->length);
+ uint32_t owned_mask = (uint32_t) (1 << 31);
+
+ assert(content_offset < owned_mask);
+ content_offset |= owned_mask;
+
+ memcpy(buffer->value + buffer_offset, &content_offset, 4);
+ pm_buffer_append_bytes(buffer, constant->start, constant->length);
+ } else {
+ // Since this is a shared constant, we are going to write its
+ // source offset directly into the buffer.
+ uint32_t source_offset = pm_ptrdifft_to_u32(constant->start - parser->start);
+ memcpy(buffer->value + buffer_offset, &source_offset, 4);
+ }
+
+ // Now we can write the length of the constant into the buffer.
+ uint32_t constant_length = pm_sizet_to_u32(constant->length);
+ memcpy(buffer->value + buffer_offset + 4, &constant_length, 4);
+ }
+ }
+}
+
+static void
+serialize_token(void *data, pm_parser_t *parser, pm_token_t *token) {
+ pm_buffer_t *buffer = (pm_buffer_t *) data;
+
+ pm_buffer_append_varuint(buffer, token->type);
+ pm_buffer_append_varuint(buffer, pm_ptrdifft_to_u32(token->start - parser->start));
+ pm_buffer_append_varuint(buffer, pm_ptrdifft_to_u32(token->end - token->start));
+ pm_buffer_append_varuint(buffer, parser->lex_state);
+}
+
+/**
+ * Lex the given source and serialize to the given buffer.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_serialize_lex(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data) {
+ pm_options_t options = { 0 };
+ pm_options_read(&options, data);
+
+ pm_parser_t parser;
+ pm_parser_init(&parser, source, size, &options);
+
+ pm_lex_callback_t lex_callback = (pm_lex_callback_t) {
+ .data = (void *) buffer,
+ .callback = serialize_token,
+ };
+
+ parser.lex_callback = &lex_callback;
+ pm_node_t *node = pm_parse(&parser);
+
+ // Append 0 to mark end of tokens.
+ pm_buffer_append_byte(buffer, 0);
+
+ pm_serialize_metadata(&parser, buffer);
+
+ pm_node_destroy(&parser, node);
+ pm_parser_free(&parser);
+ pm_options_free(&options);
+}
+
+/**
+ * Parse and serialize both the AST and the tokens represented by the given
+ * source to the given buffer.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_serialize_parse_lex(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data) {
+ pm_options_t options = { 0 };
+ pm_options_read(&options, data);
+
+ pm_parser_t parser;
+ pm_parser_init(&parser, source, size, &options);
+
+ pm_lex_callback_t lex_callback = (pm_lex_callback_t) {
+ .data = (void *) buffer,
+ .callback = serialize_token,
+ };
+
+ parser.lex_callback = &lex_callback;
+ pm_node_t *node = pm_parse(&parser);
+
+ pm_buffer_append_byte(buffer, 0);
+ pm_serialize(&parser, node, buffer);
+
+ pm_node_destroy(&parser, node);
+ pm_parser_free(&parser);
+ pm_options_free(&options);
+}
+
+#endif
diff --git a/prism/templates/src/token_type.c.erb b/prism/templates/src/token_type.c.erb
new file mode 100644
index 0000000000..1aeecd72b2
--- /dev/null
+++ b/prism/templates/src/token_type.c.erb
@@ -0,0 +1,369 @@
+#include <string.h>
+
+#include "prism/ast.h"
+
+/**
+ * Returns a string representation of the given token type.
+ */
+PRISM_EXPORTED_FUNCTION const char *
+pm_token_type_name(pm_token_type_t token_type) {
+ switch (token_type) {
+<%- tokens.each do |token| -%>
+ case PM_TOKEN_<%= token.name %>:
+ return "<%= token.name %>";
+<%- end -%>
+ case PM_TOKEN_MAXIMUM:
+ assert(false && "unreachable");
+ return "";
+ }
+
+ // Provide a default, because some compilers can't determine that the above
+ // switch is exhaustive.
+ assert(false && "unreachable");
+ return "";
+}
+
+/**
+ * Returns the human name of the given token type.
+ */
+const char *
+pm_token_type_human(pm_token_type_t token_type) {
+ switch (token_type) {
+ case PM_TOKEN_EOF:
+ return "end of file";
+ case PM_TOKEN_MISSING:
+ return "missing token";
+ case PM_TOKEN_NOT_PROVIDED:
+ return "not provided token";
+ case PM_TOKEN_AMPERSAND:
+ return "'&'";
+ case PM_TOKEN_AMPERSAND_AMPERSAND:
+ return "'&&'";
+ case PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL:
+ return "'&&='";
+ case PM_TOKEN_AMPERSAND_DOT:
+ return "'&.'";
+ case PM_TOKEN_AMPERSAND_EQUAL:
+ return "'&='";
+ case PM_TOKEN_BACKTICK:
+ return "'`'";
+ case PM_TOKEN_BACK_REFERENCE:
+ return "back reference";
+ case PM_TOKEN_BANG:
+ return "'!'";
+ case PM_TOKEN_BANG_EQUAL:
+ return "'!='";
+ case PM_TOKEN_BANG_TILDE:
+ return "'!~'";
+ case PM_TOKEN_BRACE_LEFT:
+ return "'{'";
+ case PM_TOKEN_BRACE_RIGHT:
+ return "'}'";
+ case PM_TOKEN_BRACKET_LEFT:
+ return "'['";
+ case PM_TOKEN_BRACKET_LEFT_ARRAY:
+ return "'['";
+ case PM_TOKEN_BRACKET_LEFT_RIGHT:
+ return "'[]'";
+ case PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL:
+ return "'[]='";
+ case PM_TOKEN_BRACKET_RIGHT:
+ return "']'";
+ case PM_TOKEN_CARET:
+ return "'^'";
+ case PM_TOKEN_CARET_EQUAL:
+ return "'^='";
+ case PM_TOKEN_CHARACTER_LITERAL:
+ return "character literal";
+ case PM_TOKEN_CLASS_VARIABLE:
+ return "class variable";
+ case PM_TOKEN_COLON:
+ return "':'";
+ case PM_TOKEN_COLON_COLON:
+ return "'::'";
+ case PM_TOKEN_COMMA:
+ return "','";
+ case PM_TOKEN_COMMENT:
+ return "comment";
+ case PM_TOKEN_CONSTANT:
+ return "constant";
+ case PM_TOKEN_DOT:
+ return "'.'";
+ case PM_TOKEN_DOT_DOT:
+ return "'..'";
+ case PM_TOKEN_DOT_DOT_DOT:
+ return "'...'";
+ case PM_TOKEN_EMBDOC_BEGIN:
+ return "'=begin'";
+ case PM_TOKEN_EMBDOC_END:
+ return "'=end'";
+ case PM_TOKEN_EMBDOC_LINE:
+ return "embedded documentation line";
+ case PM_TOKEN_EMBEXPR_BEGIN:
+ return "'#{'";
+ case PM_TOKEN_EMBEXPR_END:
+ return "'}'";
+ case PM_TOKEN_EMBVAR:
+ return "'#'";
+ case PM_TOKEN_EQUAL:
+ return "'='";
+ case PM_TOKEN_EQUAL_EQUAL:
+ return "'=='";
+ case PM_TOKEN_EQUAL_EQUAL_EQUAL:
+ return "'==='";
+ case PM_TOKEN_EQUAL_GREATER:
+ return "'=>'";
+ case PM_TOKEN_EQUAL_TILDE:
+ return "'=~'";
+ case PM_TOKEN_FLOAT:
+ return "float";
+ case PM_TOKEN_FLOAT_IMAGINARY:
+ return "imaginary";
+ case PM_TOKEN_FLOAT_RATIONAL:
+ return "rational";
+ case PM_TOKEN_FLOAT_RATIONAL_IMAGINARY:
+ return "imaginary";
+ case PM_TOKEN_GLOBAL_VARIABLE:
+ return "global variable";
+ case PM_TOKEN_GREATER:
+ return "'>'";
+ case PM_TOKEN_GREATER_EQUAL:
+ return "'>='";
+ case PM_TOKEN_GREATER_GREATER:
+ return "'>>'";
+ case PM_TOKEN_GREATER_GREATER_EQUAL:
+ return "'>>='";
+ case PM_TOKEN_HEREDOC_END:
+ return "heredoc ending";
+ case PM_TOKEN_HEREDOC_START:
+ return "heredoc beginning";
+ case PM_TOKEN_IDENTIFIER:
+ return "local variable or method";
+ case PM_TOKEN_IGNORED_NEWLINE:
+ return "ignored newline";
+ case PM_TOKEN_INSTANCE_VARIABLE:
+ return "instance variable";
+ case PM_TOKEN_INTEGER:
+ return "integer";
+ case PM_TOKEN_INTEGER_IMAGINARY:
+ return "imaginary";
+ case PM_TOKEN_INTEGER_RATIONAL:
+ return "rational";
+ case PM_TOKEN_INTEGER_RATIONAL_IMAGINARY:
+ return "imaginary";
+ case PM_TOKEN_KEYWORD_ALIAS:
+ return "'alias'";
+ case PM_TOKEN_KEYWORD_AND:
+ return "'and'";
+ case PM_TOKEN_KEYWORD_BEGIN:
+ return "'begin'";
+ case PM_TOKEN_KEYWORD_BEGIN_UPCASE:
+ return "'BEGIN'";
+ case PM_TOKEN_KEYWORD_BREAK:
+ return "'break'";
+ case PM_TOKEN_KEYWORD_CASE:
+ return "'case'";
+ case PM_TOKEN_KEYWORD_CLASS:
+ return "'class'";
+ case PM_TOKEN_KEYWORD_DEF:
+ return "'def'";
+ case PM_TOKEN_KEYWORD_DEFINED:
+ return "'defined?'";
+ case PM_TOKEN_KEYWORD_DO:
+ return "'do'";
+ case PM_TOKEN_KEYWORD_DO_LOOP:
+ return "'do'";
+ case PM_TOKEN_KEYWORD_ELSE:
+ return "'else'";
+ case PM_TOKEN_KEYWORD_ELSIF:
+ return "'elsif'";
+ case PM_TOKEN_KEYWORD_END:
+ return "'end'";
+ case PM_TOKEN_KEYWORD_END_UPCASE:
+ return "'END'";
+ case PM_TOKEN_KEYWORD_ENSURE:
+ return "'ensure'";
+ case PM_TOKEN_KEYWORD_FALSE:
+ return "'false'";
+ case PM_TOKEN_KEYWORD_FOR:
+ return "'for'";
+ case PM_TOKEN_KEYWORD_IF:
+ return "'if'";
+ case PM_TOKEN_KEYWORD_IF_MODIFIER:
+ return "'if'";
+ case PM_TOKEN_KEYWORD_IN:
+ return "'in'";
+ case PM_TOKEN_KEYWORD_MODULE:
+ return "'module'";
+ case PM_TOKEN_KEYWORD_NEXT:
+ return "'next'";
+ case PM_TOKEN_KEYWORD_NIL:
+ return "'nil'";
+ case PM_TOKEN_KEYWORD_NOT:
+ return "'not'";
+ case PM_TOKEN_KEYWORD_OR:
+ return "'or'";
+ case PM_TOKEN_KEYWORD_REDO:
+ return "'redo'";
+ case PM_TOKEN_KEYWORD_RESCUE:
+ return "'rescue'";
+ case PM_TOKEN_KEYWORD_RESCUE_MODIFIER:
+ return "'rescue' modifier";
+ case PM_TOKEN_KEYWORD_RETRY:
+ return "'retry'";
+ case PM_TOKEN_KEYWORD_RETURN:
+ return "'return'";
+ case PM_TOKEN_KEYWORD_SELF:
+ return "'self'";
+ case PM_TOKEN_KEYWORD_SUPER:
+ return "'super'";
+ case PM_TOKEN_KEYWORD_THEN:
+ return "'then'";
+ case PM_TOKEN_KEYWORD_TRUE:
+ return "'true'";
+ case PM_TOKEN_KEYWORD_UNDEF:
+ return "'undef'";
+ case PM_TOKEN_KEYWORD_UNLESS:
+ return "'unless'";
+ case PM_TOKEN_KEYWORD_UNLESS_MODIFIER:
+ return "'unless'";
+ case PM_TOKEN_KEYWORD_UNTIL:
+ return "'until'";
+ case PM_TOKEN_KEYWORD_UNTIL_MODIFIER:
+ return "'until'";
+ case PM_TOKEN_KEYWORD_WHEN:
+ return "'when'";
+ case PM_TOKEN_KEYWORD_WHILE:
+ return "'while'";
+ case PM_TOKEN_KEYWORD_WHILE_MODIFIER:
+ return "'while'";
+ case PM_TOKEN_KEYWORD_YIELD:
+ return "'yield'";
+ case PM_TOKEN_KEYWORD___ENCODING__:
+ return "'__ENCODING__'";
+ case PM_TOKEN_KEYWORD___FILE__:
+ return "'__FILE__'";
+ case PM_TOKEN_KEYWORD___LINE__:
+ return "'__LINE__'";
+ case PM_TOKEN_LABEL:
+ return "label";
+ case PM_TOKEN_LABEL_END:
+ return "label terminator";
+ case PM_TOKEN_LAMBDA_BEGIN:
+ return "'{'";
+ case PM_TOKEN_LESS:
+ return "'<'";
+ case PM_TOKEN_LESS_EQUAL:
+ return "'<='";
+ case PM_TOKEN_LESS_EQUAL_GREATER:
+ return "'<=>'";
+ case PM_TOKEN_LESS_LESS:
+ return "'<<'";
+ case PM_TOKEN_LESS_LESS_EQUAL:
+ return "'<<='";
+ case PM_TOKEN_METHOD_NAME:
+ return "method name";
+ case PM_TOKEN_MINUS:
+ return "'-'";
+ case PM_TOKEN_MINUS_EQUAL:
+ return "'-='";
+ case PM_TOKEN_MINUS_GREATER:
+ return "'->'";
+ case PM_TOKEN_NEWLINE:
+ return "newline";
+ case PM_TOKEN_NUMBERED_REFERENCE:
+ return "numbered reference";
+ case PM_TOKEN_PARENTHESIS_LEFT:
+ return "'('";
+ case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES:
+ return "'('";
+ case PM_TOKEN_PARENTHESIS_RIGHT:
+ return "')'";
+ case PM_TOKEN_PERCENT:
+ return "'%'";
+ case PM_TOKEN_PERCENT_EQUAL:
+ return "'%='";
+ case PM_TOKEN_PERCENT_LOWER_I:
+ return "'%i'";
+ case PM_TOKEN_PERCENT_LOWER_W:
+ return "'%w'";
+ case PM_TOKEN_PERCENT_LOWER_X:
+ return "'%x'";
+ case PM_TOKEN_PERCENT_UPPER_I:
+ return "'%I'";
+ case PM_TOKEN_PERCENT_UPPER_W:
+ return "'%W'";
+ case PM_TOKEN_PIPE:
+ return "'|'";
+ case PM_TOKEN_PIPE_EQUAL:
+ return "'|='";
+ case PM_TOKEN_PIPE_PIPE:
+ return "'||'";
+ case PM_TOKEN_PIPE_PIPE_EQUAL:
+ return "'||='";
+ case PM_TOKEN_PLUS:
+ return "'+'";
+ case PM_TOKEN_PLUS_EQUAL:
+ return "'+='";
+ case PM_TOKEN_QUESTION_MARK:
+ return "'?'";
+ case PM_TOKEN_REGEXP_BEGIN:
+ return "regular expression beginning";
+ case PM_TOKEN_REGEXP_END:
+ return "regular expression ending";
+ case PM_TOKEN_SEMICOLON:
+ return "';'";
+ case PM_TOKEN_SLASH:
+ return "'/'";
+ case PM_TOKEN_SLASH_EQUAL:
+ return "'/='";
+ case PM_TOKEN_STAR:
+ return "'*'";
+ case PM_TOKEN_STAR_EQUAL:
+ return "'*='";
+ case PM_TOKEN_STAR_STAR:
+ return "'**'";
+ case PM_TOKEN_STAR_STAR_EQUAL:
+ return "'**='";
+ case PM_TOKEN_STRING_BEGIN:
+ return "string literal";
+ case PM_TOKEN_STRING_CONTENT:
+ return "string content";
+ case PM_TOKEN_STRING_END:
+ return "string ending";
+ case PM_TOKEN_SYMBOL_BEGIN:
+ return "symbol literal";
+ case PM_TOKEN_TILDE:
+ return "'~'";
+ case PM_TOKEN_UAMPERSAND:
+ return "'&'";
+ case PM_TOKEN_UCOLON_COLON:
+ return "'::'";
+ case PM_TOKEN_UDOT_DOT:
+ return "'..'";
+ case PM_TOKEN_UDOT_DOT_DOT:
+ return "'...'";
+ case PM_TOKEN_UMINUS:
+ return "'-'";
+ case PM_TOKEN_UMINUS_NUM:
+ return "'-'";
+ case PM_TOKEN_UPLUS:
+ return "'+'";
+ case PM_TOKEN_USTAR:
+ return "*";
+ case PM_TOKEN_USTAR_STAR:
+ return "'**'";
+ case PM_TOKEN_WORDS_SEP:
+ return "string separator";
+ case PM_TOKEN___END__:
+ return "'__END__'";
+ case PM_TOKEN_MAXIMUM:
+ assert(false && "unreachable");
+ return "";
+ }
+
+ // Provide a default, because some compilers can't determine that the above
+ // switch is exhaustive.
+ assert(false && "unreachable");
+ return "";
+}
diff --git a/prism/templates/template.rb b/prism/templates/template.rb
new file mode 100755
index 0000000000..6f79a85371
--- /dev/null
+++ b/prism/templates/template.rb
@@ -0,0 +1,662 @@
+#!/usr/bin/env ruby
+# typed: false
+
+require "erb"
+require "fileutils"
+require "yaml"
+
+module Prism
+ module Template
+ SERIALIZE_ONLY_SEMANTICS_FIELDS = ENV.fetch("PRISM_SERIALIZE_ONLY_SEMANTICS_FIELDS", false)
+ CHECK_FIELD_KIND = ENV.fetch("CHECK_FIELD_KIND", false)
+
+ JAVA_BACKEND = ENV["PRISM_JAVA_BACKEND"] || "truffleruby"
+ JAVA_STRING_TYPE = JAVA_BACKEND == "jruby" ? "org.jruby.RubySymbol" : "String"
+
+ class Error
+ attr_reader :name
+
+ def initialize(name)
+ @name = name
+ end
+ end
+
+ class Warning
+ attr_reader :name
+
+ def initialize(name)
+ @name = name
+ end
+ end
+
+ # This module contains methods for escaping characters in JavaDoc comments.
+ module JavaDoc
+ ESCAPES = {
+ "'" => "&#39;",
+ "\"" => "&quot;",
+ "@" => "&#64;",
+ "&" => "&amp;",
+ "<" => "&lt;",
+ ">" => "&gt;"
+ }.freeze
+
+ def self.escape(value)
+ value.gsub(/['&"<>@]/, ESCAPES)
+ end
+ end
+
+ # A comment attached to a field or node.
+ class ConfigComment
+ attr_reader :value
+
+ def initialize(value)
+ @value = value
+ end
+
+ def each_line(&block)
+ value.each_line { |line| yield line.prepend(" ").rstrip }
+ end
+
+ def each_java_line(&block)
+ ConfigComment.new(JavaDoc.escape(value)).each_line(&block)
+ end
+ end
+
+ # This represents a field on a node. It contains all of the necessary
+ # information to template out the code for that field.
+ class Field
+ attr_reader :name, :comment, :options
+
+ def initialize(name:, comment: nil, **options)
+ @name = name
+ @comment = comment
+ @options = options
+ end
+
+ def each_comment_line(&block)
+ ConfigComment.new(comment).each_line(&block) if comment
+ end
+
+ def each_comment_java_line(&block)
+ ConfigComment.new(comment).each_java_line(&block) if comment
+ end
+
+ def semantic_field?
+ true
+ end
+
+ def should_be_serialized?
+ SERIALIZE_ONLY_SEMANTICS_FIELDS ? semantic_field? : true
+ end
+ end
+
+ # Some node fields can be specialized if they point to a specific kind of
+ # node and not just a generic node.
+ class NodeKindField < Field
+ def c_type
+ if specific_kind
+ "pm_#{specific_kind.gsub(/(?<=.)[A-Z]/, "_\\0").downcase}"
+ else
+ "pm_node"
+ end
+ end
+
+ def ruby_type
+ specific_kind || "Node"
+ end
+
+ def java_type
+ specific_kind || "Node"
+ end
+
+ def java_cast
+ if specific_kind
+ "(Nodes.#{options[:kind]}) "
+ else
+ ""
+ end
+ end
+
+ def specific_kind
+ options[:kind] unless options[:kind].is_a?(Array)
+ end
+
+ def union_kind
+ options[:kind] if options[:kind].is_a?(Array)
+ end
+ end
+
+ # This represents a field on a node that is itself a node. We pass them as
+ # references and store them as references.
+ class NodeField < NodeKindField
+ def rbs_class
+ if specific_kind
+ specific_kind
+ elsif union_kind
+ union_kind.join(" | ")
+ else
+ "Prism::node"
+ end
+ end
+
+ def rbi_class
+ if specific_kind
+ "Prism::#{specific_kind}"
+ elsif union_kind
+ "T.any(#{union_kind.map { |kind| "Prism::#{kind}" }.join(", ")})"
+ else
+ "Prism::Node"
+ end
+ end
+
+ def check_field_kind
+ if union_kind
+ "[#{union_kind.join(', ')}].include?(#{name}.class)"
+ else
+ "#{name}.is_a?(#{ruby_type})"
+ end
+ end
+ end
+
+ # This represents a field on a node that is itself a node and can be
+ # optionally null. We pass them as references and store them as references.
+ class OptionalNodeField < NodeKindField
+ def rbs_class
+ if specific_kind
+ "#{specific_kind}?"
+ elsif union_kind
+ [*union_kind, "nil"].join(" | ")
+ else
+ "Prism::node?"
+ end
+ end
+
+ def rbi_class
+ if specific_kind
+ "T.nilable(Prism::#{specific_kind})"
+ elsif union_kind
+ "T.nilable(T.any(#{union_kind.map { |kind| "Prism::#{kind}" }.join(", ")}))"
+ else
+ "T.nilable(Prism::Node)"
+ end
+ end
+
+ def check_field_kind
+ if union_kind
+ "[#{union_kind.join(', ')}, NilClass].include?(#{name}.class)"
+ else
+ "#{name}.nil? || #{name}.is_a?(#{ruby_type})"
+ end
+ end
+ end
+
+ # This represents a field on a node that is a list of nodes. We pass them as
+ # references and store them directly on the struct.
+ class NodeListField < NodeKindField
+ def rbs_class
+ if specific_kind
+ "Array[#{specific_kind}]"
+ elsif union_kind
+ "Array[#{union_kind.join(" | ")}]"
+ else
+ "Array[Prism::node]"
+ end
+ end
+
+ def rbi_class
+ if specific_kind
+ "T::Array[Prism::#{specific_kind}]"
+ elsif union_kind
+ "T::Array[T.any(#{union_kind.map { |kind| "Prism::#{kind}" }.join(", ")})]"
+ else
+ "T::Array[Prism::Node]"
+ end
+ end
+
+ def java_type
+ "#{super}[]"
+ end
+
+ def check_field_kind
+ if union_kind
+ "#{name}.all? { |n| [#{union_kind.join(', ')}].include?(n.class) }"
+ else
+ "#{name}.all? { |n| n.is_a?(#{ruby_type}) }"
+ end
+ end
+ end
+
+ # This represents a field on a node that is the ID of a string interned
+ # through the parser's constant pool.
+ class ConstantField < Field
+ def rbs_class
+ "Symbol"
+ end
+
+ def rbi_class
+ "Symbol"
+ end
+
+ def java_type
+ JAVA_STRING_TYPE
+ end
+ end
+
+ # This represents a field on a node that is the ID of a string interned
+ # through the parser's constant pool and can be optionally null.
+ class OptionalConstantField < Field
+ def rbs_class
+ "Symbol?"
+ end
+
+ def rbi_class
+ "T.nilable(Symbol)"
+ end
+
+ def java_type
+ JAVA_STRING_TYPE
+ end
+ end
+
+ # This represents a field on a node that is a list of IDs that are associated
+ # with strings interned through the parser's constant pool.
+ class ConstantListField < Field
+ def rbs_class
+ "Array[Symbol]"
+ end
+
+ def rbi_class
+ "T::Array[Symbol]"
+ end
+
+ def java_type
+ "#{JAVA_STRING_TYPE}[]"
+ end
+ end
+
+ # This represents a field on a node that is a string.
+ class StringField < Field
+ def rbs_class
+ "String"
+ end
+
+ def rbi_class
+ "String"
+ end
+
+ def java_type
+ "byte[]"
+ end
+ end
+
+ # This represents a field on a node that is a location.
+ class LocationField < Field
+ def semantic_field?
+ false
+ end
+
+ def rbs_class
+ "Location"
+ end
+
+ def rbi_class
+ "Prism::Location"
+ end
+
+ def java_type
+ "Location"
+ end
+ end
+
+ # This represents a field on a node that is a location that is optional.
+ class OptionalLocationField < Field
+ def semantic_field?
+ false
+ end
+
+ def rbs_class
+ "Location?"
+ end
+
+ def rbi_class
+ "T.nilable(Prism::Location)"
+ end
+
+ def java_type
+ "Location"
+ end
+ end
+
+ # This represents an integer field.
+ class UInt8Field < Field
+ def rbs_class
+ "Integer"
+ end
+
+ def rbi_class
+ "Integer"
+ end
+
+ def java_type
+ "int"
+ end
+ end
+
+ # This represents an integer field.
+ class UInt32Field < Field
+ def rbs_class
+ "Integer"
+ end
+
+ def rbi_class
+ "Integer"
+ end
+
+ def java_type
+ "int"
+ end
+ end
+
+ # This represents a set of flags. It is very similar to the UInt32Field, but
+ # can be directly embedded into the flags field on the struct and provides
+ # convenient methods for checking if a flag is set.
+ class FlagsField < Field
+ def rbs_class
+ "Integer"
+ end
+
+ def rbi_class
+ "Integer"
+ end
+
+ def java_type
+ "short"
+ end
+
+ def kind
+ options.fetch(:kind)
+ end
+ end
+
+ # This represents an arbitrarily-sized integer. When it gets to Ruby it will
+ # be an Integer.
+ class IntegerField < Field
+ def rbs_class
+ "Integer"
+ end
+
+ def rbi_class
+ "Integer"
+ end
+
+ def java_type
+ "Object"
+ end
+ end
+
+ # This represents a double-precision floating point number. When it gets to
+ # Ruby it will be a Float.
+ class DoubleField < Field
+ def rbs_class
+ "Float"
+ end
+
+ def rbi_class
+ "Float"
+ end
+
+ def java_type
+ "double"
+ end
+ end
+
+ # This class represents a node in the tree, configured by the config.yml file
+ # in YAML format. It contains information about the name of the node and the
+ # various child nodes it contains.
+ class NodeType
+ attr_reader :name, :type, :human, :fields, :newline, :comment
+
+ def initialize(config)
+ @name = config.fetch("name")
+
+ type = @name.gsub(/(?<=.)[A-Z]/, "_\\0")
+ @type = "PM_#{type.upcase}"
+ @human = type.downcase
+
+ @fields =
+ config.fetch("fields", []).map do |field|
+ type = field_type_for(field.fetch("type"))
+
+ options = field.transform_keys(&:to_sym)
+ options.delete(:type)
+
+ # If/when we have documentation on every field, this should be changed
+ # to use fetch instead of delete.
+ comment = options.delete(:comment)
+
+ type.new(comment: comment, **options)
+ end
+
+ @newline = config.fetch("newline", true)
+ @comment = config.fetch("comment")
+ end
+
+ def each_comment_line(&block)
+ ConfigComment.new(comment).each_line(&block)
+ end
+
+ def each_comment_java_line(&block)
+ ConfigComment.new(comment).each_java_line(&block)
+ end
+
+ def semantic_fields
+ @semantic_fields ||= @fields.select(&:semantic_field?)
+ end
+
+ # Should emit serialized length of node so implementations can skip
+ # the node to enable lazy parsing.
+ def needs_serialized_length?
+ name == "DefNode"
+ end
+
+ private
+
+ def field_type_for(name)
+ case name
+ when "node" then NodeField
+ when "node?" then OptionalNodeField
+ when "node[]" then NodeListField
+ when "string" then StringField
+ when "constant" then ConstantField
+ when "constant?" then OptionalConstantField
+ when "constant[]" then ConstantListField
+ when "location" then LocationField
+ when "location?" then OptionalLocationField
+ when "uint8" then UInt8Field
+ when "uint32" then UInt32Field
+ when "flags" then FlagsField
+ when "integer" then IntegerField
+ when "double" then DoubleField
+ else raise("Unknown field type: #{name.inspect}")
+ end
+ end
+ end
+
+ # This represents a token in the lexer.
+ class Token
+ attr_reader :name, :value, :comment
+
+ def initialize(config)
+ @name = config.fetch("name")
+ @value = config["value"]
+ @comment = config.fetch("comment")
+ end
+ end
+
+ # Represents a set of flags that should be internally represented with an enum.
+ class Flags
+ # Represents an individual flag within a set of flags.
+ class Flag
+ attr_reader :name, :camelcase, :comment
+
+ def initialize(config)
+ @name = config.fetch("name")
+ @camelcase = @name.split("_").map(&:capitalize).join
+ @comment = config.fetch("comment")
+ end
+ end
+
+ attr_reader :name, :human, :values, :comment
+
+ def initialize(config)
+ @name = config.fetch("name")
+ @human = @name.gsub(/(?<=.)[A-Z]/, "_\\0").downcase
+ @values = config.fetch("values").map { |flag| Flag.new(flag) }
+ @comment = config.fetch("comment")
+ end
+ end
+
+ class << self
+ # This templates out a file using ERB with the given locals. The locals are
+ # derived from the config.yml file.
+ def render(name, write_to: nil)
+ filepath = "templates/#{name}.erb"
+ template = File.expand_path("../#{filepath}", __dir__)
+
+ erb = read_template(template)
+ extension = File.extname(filepath.gsub(".erb", ""))
+
+ heading =
+ case extension
+ when ".rb"
+ <<~HEADING
+ # frozen_string_literal: true
+
+ =begin
+ This file is generated by the templates/template.rb script and should not be
+ modified manually. See #{filepath}
+ if you are looking to modify the template
+ =end
+
+ HEADING
+ when ".rbs"
+ <<~HEADING
+ # This file is generated by the templates/template.rb script and should not be
+ # modified manually. See #{filepath}
+ # if you are looking to modify the template
+
+ HEADING
+ when ".rbi"
+ <<~HEADING
+ # typed: strict
+
+ =begin
+ This file is generated by the templates/template.rb script and should not be
+ modified manually. See #{filepath}
+ if you are looking to modify the template
+ =end
+
+ HEADING
+ else
+ <<~HEADING
+ /******************************************************************************/
+ /* This file is generated by the templates/template.rb script and should not */
+ /* be modified manually. See */
+ /* #{filepath + " " * (74 - filepath.size) } */
+ /* if you are looking to modify the */
+ /* template */
+ /******************************************************************************/
+
+ HEADING
+ end
+
+ write_to ||= File.expand_path("../#{name}", __dir__)
+ contents = heading + erb.result_with_hash(locals)
+
+ if (extension == ".c" || extension == ".h") && !contents.ascii_only?
+ # Enforce that we only have ASCII characters here. This is necessary
+ # for non-UTF-8 locales that only allow ASCII characters in C source
+ # files.
+ contents.each_line.with_index(1) do |line, line_number|
+ raise "Non-ASCII character on line #{line_number} of #{write_to}" unless line.ascii_only?
+ end
+ end
+
+ FileUtils.mkdir_p(File.dirname(write_to))
+ File.write(write_to, contents)
+ end
+
+ private
+
+ def read_template(filepath)
+ template = File.read(filepath, encoding: Encoding::UTF_8)
+ erb = erb(template)
+ erb.filename = filepath
+ erb
+ end
+
+ def erb(template)
+ ERB.new(template, trim_mode: "-")
+ end
+
+ def locals
+ @locals ||=
+ begin
+ config = YAML.load_file(File.expand_path("../config.yml", __dir__))
+
+ {
+ errors: config.fetch("errors").map { |name| Error.new(name) },
+ warnings: config.fetch("warnings").map { |name| Warning.new(name) },
+ nodes: config.fetch("nodes").map { |node| NodeType.new(node) }.sort_by(&:name),
+ tokens: config.fetch("tokens").map { |token| Token.new(token) },
+ flags: config.fetch("flags").map { |flags| Flags.new(flags) }
+ }
+ end
+ end
+ end
+
+ TEMPLATES = [
+ "ext/prism/api_node.c",
+ "include/prism/ast.h",
+ "include/prism/diagnostic.h",
+ "javascript/src/deserialize.js",
+ "javascript/src/nodes.js",
+ "javascript/src/visitor.js",
+ "java/org/prism/Loader.java",
+ "java/org/prism/Nodes.java",
+ "java/org/prism/AbstractNodeVisitor.java",
+ "lib/prism/compiler.rb",
+ "lib/prism/dispatcher.rb",
+ "lib/prism/dot_visitor.rb",
+ "lib/prism/dsl.rb",
+ "lib/prism/inspect_visitor.rb",
+ "lib/prism/mutation_compiler.rb",
+ "lib/prism/node.rb",
+ "lib/prism/reflection.rb",
+ "lib/prism/serialize.rb",
+ "lib/prism/visitor.rb",
+ "src/diagnostic.c",
+ "src/node.c",
+ "src/prettyprint.c",
+ "src/serialize.c",
+ "src/token_type.c",
+ "rbi/prism/node.rbi",
+ "rbi/prism/visitor.rbi",
+ "sig/prism.rbs",
+ "sig/prism/dsl.rbs",
+ "sig/prism/mutation_compiler.rbs",
+ "sig/prism/node.rbs",
+ "sig/prism/visitor.rbs",
+ "sig/prism/_private/dot_visitor.rbs"
+ ]
+ end
+end
+
+if __FILE__ == $0
+ if ARGV.empty?
+ Prism::Template::TEMPLATES.each { |filepath| Prism::Template.render(filepath) }
+ else # ruby/ruby
+ name, write_to = ARGV
+ Prism::Template.render(name, write_to: write_to)
+ end
+end
diff --git a/prism/util/pm_buffer.c b/prism/util/pm_buffer.c
new file mode 100644
index 0000000000..1e79e46718
--- /dev/null
+++ b/prism/util/pm_buffer.c
@@ -0,0 +1,317 @@
+#include "prism/util/pm_buffer.h"
+
+/**
+ * Return the size of the pm_buffer_t struct.
+ */
+size_t
+pm_buffer_sizeof(void) {
+ return sizeof(pm_buffer_t);
+}
+
+/**
+ * Initialize a pm_buffer_t with the given capacity.
+ */
+bool
+pm_buffer_init_capacity(pm_buffer_t *buffer, size_t capacity) {
+ buffer->length = 0;
+ buffer->capacity = capacity;
+
+ buffer->value = (char *) xmalloc(capacity);
+ return buffer->value != NULL;
+}
+
+/**
+ * Initialize a pm_buffer_t with its default values.
+ */
+bool
+pm_buffer_init(pm_buffer_t *buffer) {
+ return pm_buffer_init_capacity(buffer, 1024);
+}
+
+/**
+ * Return the value of the buffer.
+ */
+char *
+pm_buffer_value(const pm_buffer_t *buffer) {
+ return buffer->value;
+}
+
+/**
+ * Return the length of the buffer.
+ */
+size_t
+pm_buffer_length(const pm_buffer_t *buffer) {
+ return buffer->length;
+}
+
+/**
+ * Append the given amount of space to the buffer.
+ */
+static inline bool
+pm_buffer_append_length(pm_buffer_t *buffer, size_t length) {
+ size_t next_length = buffer->length + length;
+
+ if (next_length > buffer->capacity) {
+ if (buffer->capacity == 0) {
+ buffer->capacity = 1;
+ }
+
+ while (next_length > buffer->capacity) {
+ buffer->capacity *= 2;
+ }
+
+ buffer->value = xrealloc(buffer->value, buffer->capacity);
+ if (buffer->value == NULL) return false;
+ }
+
+ buffer->length = next_length;
+ return true;
+}
+
+/**
+ * Append a generic pointer to memory to the buffer.
+ */
+static inline void
+pm_buffer_append(pm_buffer_t *buffer, const void *source, size_t length) {
+ size_t cursor = buffer->length;
+ if (pm_buffer_append_length(buffer, length)) {
+ memcpy(buffer->value + cursor, source, length);
+ }
+}
+
+/**
+ * Append the given amount of space as zeroes to the buffer.
+ */
+void
+pm_buffer_append_zeroes(pm_buffer_t *buffer, size_t length) {
+ size_t cursor = buffer->length;
+ if (pm_buffer_append_length(buffer, length)) {
+ memset(buffer->value + cursor, 0, length);
+ }
+}
+
+/**
+ * Append a formatted string to the buffer.
+ */
+void
+pm_buffer_append_format(pm_buffer_t *buffer, const char *format, ...) {
+ va_list arguments;
+ va_start(arguments, format);
+ int result = vsnprintf(NULL, 0, format, arguments);
+ va_end(arguments);
+
+ if (result < 0) return;
+ size_t length = (size_t) (result + 1);
+
+ size_t cursor = buffer->length;
+ if (pm_buffer_append_length(buffer, length)) {
+ va_start(arguments, format);
+ vsnprintf(buffer->value + cursor, length, format, arguments);
+ va_end(arguments);
+ buffer->length--;
+ }
+}
+
+/**
+ * Append a string to the buffer.
+ */
+void
+pm_buffer_append_string(pm_buffer_t *buffer, const char *value, size_t length) {
+ pm_buffer_append(buffer, value, length);
+}
+
+/**
+ * Append a list of bytes to the buffer.
+ */
+void
+pm_buffer_append_bytes(pm_buffer_t *buffer, const uint8_t *value, size_t length) {
+ pm_buffer_append(buffer, (const char *) value, length);
+}
+
+/**
+ * Append a single byte to the buffer.
+ */
+void
+pm_buffer_append_byte(pm_buffer_t *buffer, uint8_t value) {
+ const void *source = &value;
+ pm_buffer_append(buffer, source, sizeof(uint8_t));
+}
+
+/**
+ * Append a 32-bit unsigned integer to the buffer as a variable-length integer.
+ */
+void
+pm_buffer_append_varuint(pm_buffer_t *buffer, uint32_t value) {
+ if (value < 128) {
+ pm_buffer_append_byte(buffer, (uint8_t) value);
+ } else {
+ uint32_t n = value;
+ while (n >= 128) {
+ pm_buffer_append_byte(buffer, (uint8_t) (n | 128));
+ n >>= 7;
+ }
+ pm_buffer_append_byte(buffer, (uint8_t) n);
+ }
+}
+
+/**
+ * Append a 32-bit signed integer to the buffer as a variable-length integer.
+ */
+void
+pm_buffer_append_varsint(pm_buffer_t *buffer, int32_t value) {
+ uint32_t unsigned_int = ((uint32_t)(value) << 1) ^ ((uint32_t)(value >> 31));
+ pm_buffer_append_varuint(buffer, unsigned_int);
+}
+
+/**
+ * Append a double to the buffer.
+ */
+void
+pm_buffer_append_double(pm_buffer_t *buffer, double value) {
+ const void *source = &value;
+ pm_buffer_append(buffer, source, sizeof(double));
+}
+
+/**
+ * Append a slice of source code to the buffer.
+ */
+void
+pm_buffer_append_source(pm_buffer_t *buffer, const uint8_t *source, size_t length, pm_buffer_escaping_t escaping) {
+ for (size_t index = 0; index < length; index++) {
+ const uint8_t byte = source[index];
+
+ if ((byte <= 0x06) || (byte >= 0x0E && byte <= 0x1F) || (byte >= 0x7F)) {
+ if (escaping == PM_BUFFER_ESCAPING_RUBY) {
+ pm_buffer_append_format(buffer, "\\x%02X", byte);
+ } else {
+ pm_buffer_append_format(buffer, "\\u%04X", byte);
+ }
+ } else {
+ switch (byte) {
+ case '\a':
+ if (escaping == PM_BUFFER_ESCAPING_RUBY) {
+ pm_buffer_append_string(buffer, "\\a", 2);
+ } else {
+ pm_buffer_append_format(buffer, "\\u%04X", byte);
+ }
+ break;
+ case '\b':
+ pm_buffer_append_string(buffer, "\\b", 2);
+ break;
+ case '\t':
+ pm_buffer_append_string(buffer, "\\t", 2);
+ break;
+ case '\n':
+ pm_buffer_append_string(buffer, "\\n", 2);
+ break;
+ case '\v':
+ if (escaping == PM_BUFFER_ESCAPING_RUBY) {
+ pm_buffer_append_string(buffer, "\\v", 2);
+ } else {
+ pm_buffer_append_format(buffer, "\\u%04X", byte);
+ }
+ break;
+ case '\f':
+ pm_buffer_append_string(buffer, "\\f", 2);
+ break;
+ case '\r':
+ pm_buffer_append_string(buffer, "\\r", 2);
+ break;
+ case '"':
+ pm_buffer_append_string(buffer, "\\\"", 2);
+ break;
+ case '#': {
+ if (escaping == PM_BUFFER_ESCAPING_RUBY && index + 1 < length) {
+ const uint8_t next_byte = source[index + 1];
+ if (next_byte == '{' || next_byte == '@' || next_byte == '$') {
+ pm_buffer_append_byte(buffer, '\\');
+ }
+ }
+
+ pm_buffer_append_byte(buffer, '#');
+ break;
+ }
+ case '\\':
+ pm_buffer_append_string(buffer, "\\\\", 2);
+ break;
+ default:
+ pm_buffer_append_byte(buffer, byte);
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * Prepend the given string to the buffer.
+ */
+void
+pm_buffer_prepend_string(pm_buffer_t *buffer, const char *value, size_t length) {
+ size_t cursor = buffer->length;
+ if (pm_buffer_append_length(buffer, length)) {
+ memmove(buffer->value + length, buffer->value, cursor);
+ memcpy(buffer->value, value, length);
+ }
+}
+
+/**
+ * Concatenate one buffer onto another.
+ */
+void
+pm_buffer_concat(pm_buffer_t *destination, const pm_buffer_t *source) {
+ if (source->length > 0) {
+ pm_buffer_append(destination, source->value, source->length);
+ }
+}
+
+/**
+ * Clear the buffer by reducing its size to 0. This does not free the allocated
+ * memory, but it does allow the buffer to be reused.
+ */
+void
+pm_buffer_clear(pm_buffer_t *buffer) {
+ buffer->length = 0;
+}
+
+/**
+ * Strip the whitespace from the end of the buffer.
+ */
+void
+pm_buffer_rstrip(pm_buffer_t *buffer) {
+ while (buffer->length > 0 && pm_char_is_whitespace((uint8_t) buffer->value[buffer->length - 1])) {
+ buffer->length--;
+ }
+}
+
+/**
+ * Checks if the buffer includes the given value.
+ */
+size_t
+pm_buffer_index(const pm_buffer_t *buffer, char value) {
+ const char *first = memchr(buffer->value, value, buffer->length);
+ return (first == NULL) ? SIZE_MAX : (size_t) (first - buffer->value);
+}
+
+/**
+ * Insert the given string into the buffer at the given index.
+ */
+void
+pm_buffer_insert(pm_buffer_t *buffer, size_t index, const char *value, size_t length) {
+ assert(index <= buffer->length);
+
+ if (index == buffer->length) {
+ pm_buffer_append_string(buffer, value, length);
+ } else {
+ pm_buffer_append_zeroes(buffer, length);
+ memmove(buffer->value + index + length, buffer->value + index, buffer->length - length - index);
+ memcpy(buffer->value + index, value, length);
+ }
+}
+
+/**
+ * Free the memory associated with the buffer.
+ */
+void
+pm_buffer_free(pm_buffer_t *buffer) {
+ xfree(buffer->value);
+}
diff --git a/prism/util/pm_buffer.h b/prism/util/pm_buffer.h
new file mode 100644
index 0000000000..58f7b506cf
--- /dev/null
+++ b/prism/util/pm_buffer.h
@@ -0,0 +1,218 @@
+/**
+ * @file pm_buffer.h
+ *
+ * A wrapper around a contiguous block of allocated memory.
+ */
+#ifndef PRISM_BUFFER_H
+#define PRISM_BUFFER_H
+
+#include "prism/defines.h"
+#include "prism/util/pm_char.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * A pm_buffer_t is a simple memory buffer that stores data in a contiguous
+ * block of memory.
+ */
+typedef struct {
+ /** The length of the buffer in bytes. */
+ size_t length;
+
+ /** The capacity of the buffer in bytes that has been allocated. */
+ size_t capacity;
+
+ /** A pointer to the start of the buffer. */
+ char *value;
+} pm_buffer_t;
+
+/**
+ * Return the size of the pm_buffer_t struct.
+ *
+ * @returns The size of the pm_buffer_t struct.
+ */
+PRISM_EXPORTED_FUNCTION size_t pm_buffer_sizeof(void);
+
+/**
+ * Initialize a pm_buffer_t with the given capacity.
+ *
+ * @param buffer The buffer to initialize.
+ * @param capacity The capacity of the buffer.
+ * @returns True if the buffer was initialized successfully, false otherwise.
+ */
+bool pm_buffer_init_capacity(pm_buffer_t *buffer, size_t capacity);
+
+/**
+ * Initialize a pm_buffer_t with its default values.
+ *
+ * @param buffer The buffer to initialize.
+ * @returns True if the buffer was initialized successfully, false otherwise.
+ */
+PRISM_EXPORTED_FUNCTION bool pm_buffer_init(pm_buffer_t *buffer);
+
+/**
+ * Return the value of the buffer.
+ *
+ * @param buffer The buffer to get the value of.
+ * @returns The value of the buffer.
+ */
+PRISM_EXPORTED_FUNCTION char * pm_buffer_value(const pm_buffer_t *buffer);
+
+/**
+ * Return the length of the buffer.
+ *
+ * @param buffer The buffer to get the length of.
+ * @returns The length of the buffer.
+ */
+PRISM_EXPORTED_FUNCTION size_t pm_buffer_length(const pm_buffer_t *buffer);
+
+/**
+ * Append the given amount of space as zeroes to the buffer.
+ *
+ * @param buffer The buffer to append to.
+ * @param length The amount of space to append and zero.
+ */
+void pm_buffer_append_zeroes(pm_buffer_t *buffer, size_t length);
+
+/**
+ * Append a formatted string to the buffer.
+ *
+ * @param buffer The buffer to append to.
+ * @param format The format string to append.
+ * @param ... The arguments to the format string.
+ */
+void pm_buffer_append_format(pm_buffer_t *buffer, const char *format, ...) PRISM_ATTRIBUTE_FORMAT(2, 3);
+
+/**
+ * Append a string to the buffer.
+ *
+ * @param buffer The buffer to append to.
+ * @param value The string to append.
+ * @param length The length of the string to append.
+ */
+void pm_buffer_append_string(pm_buffer_t *buffer, const char *value, size_t length);
+
+/**
+ * Append a list of bytes to the buffer.
+ *
+ * @param buffer The buffer to append to.
+ * @param value The bytes to append.
+ * @param length The length of the bytes to append.
+ */
+void pm_buffer_append_bytes(pm_buffer_t *buffer, const uint8_t *value, size_t length);
+
+/**
+ * Append a single byte to the buffer.
+ *
+ * @param buffer The buffer to append to.
+ * @param value The byte to append.
+ */
+void pm_buffer_append_byte(pm_buffer_t *buffer, uint8_t value);
+
+/**
+ * Append a 32-bit unsigned integer to the buffer as a variable-length integer.
+ *
+ * @param buffer The buffer to append to.
+ * @param value The integer to append.
+ */
+void pm_buffer_append_varuint(pm_buffer_t *buffer, uint32_t value);
+
+/**
+ * Append a 32-bit signed integer to the buffer as a variable-length integer.
+ *
+ * @param buffer The buffer to append to.
+ * @param value The integer to append.
+ */
+void pm_buffer_append_varsint(pm_buffer_t *buffer, int32_t value);
+
+/**
+ * Append a double to the buffer.
+ *
+ * @param buffer The buffer to append to.
+ * @param value The double to append.
+ */
+void pm_buffer_append_double(pm_buffer_t *buffer, double value);
+
+/**
+ * The different types of escaping that can be performed by the buffer when
+ * appending a slice of Ruby source code.
+ */
+typedef enum {
+ PM_BUFFER_ESCAPING_RUBY,
+ PM_BUFFER_ESCAPING_JSON
+} pm_buffer_escaping_t;
+
+/**
+ * Append a slice of source code to the buffer.
+ *
+ * @param buffer The buffer to append to.
+ * @param source The source code to append.
+ * @param length The length of the source code to append.
+ * @param escaping The type of escaping to perform.
+ */
+void pm_buffer_append_source(pm_buffer_t *buffer, const uint8_t *source, size_t length, pm_buffer_escaping_t escaping);
+
+/**
+ * Prepend the given string to the buffer.
+ *
+ * @param buffer The buffer to prepend to.
+ * @param value The string to prepend.
+ * @param length The length of the string to prepend.
+ */
+void pm_buffer_prepend_string(pm_buffer_t *buffer, const char *value, size_t length);
+
+/**
+ * Concatenate one buffer onto another.
+ *
+ * @param destination The buffer to concatenate onto.
+ * @param source The buffer to concatenate.
+ */
+void pm_buffer_concat(pm_buffer_t *destination, const pm_buffer_t *source);
+
+/**
+ * Clear the buffer by reducing its size to 0. This does not free the allocated
+ * memory, but it does allow the buffer to be reused.
+ *
+ * @param buffer The buffer to clear.
+ */
+void pm_buffer_clear(pm_buffer_t *buffer);
+
+/**
+ * Strip the whitespace from the end of the buffer.
+ *
+ * @param buffer The buffer to strip.
+ */
+void pm_buffer_rstrip(pm_buffer_t *buffer);
+
+/**
+ * Checks if the buffer includes the given value.
+ *
+ * @param buffer The buffer to check.
+ * @param value The value to check for.
+ * @returns The index of the first occurrence of the value in the buffer, or
+ * SIZE_MAX if the value is not found.
+ */
+size_t pm_buffer_index(const pm_buffer_t *buffer, char value);
+
+/**
+ * Insert the given string into the buffer at the given index.
+ *
+ * @param buffer The buffer to insert into.
+ * @param index The index to insert at.
+ * @param value The string to insert.
+ * @param length The length of the string to insert.
+ */
+void pm_buffer_insert(pm_buffer_t *buffer, size_t index, const char *value, size_t length);
+
+/**
+ * Free the memory associated with the buffer.
+ *
+ * @param buffer The buffer to free.
+ */
+PRISM_EXPORTED_FUNCTION void pm_buffer_free(pm_buffer_t *buffer);
+
+#endif
diff --git a/prism/util/pm_char.c b/prism/util/pm_char.c
new file mode 100644
index 0000000000..dce19abd1b
--- /dev/null
+++ b/prism/util/pm_char.c
@@ -0,0 +1,318 @@
+#include "prism/util/pm_char.h"
+
+#define PRISM_CHAR_BIT_WHITESPACE (1 << 0)
+#define PRISM_CHAR_BIT_INLINE_WHITESPACE (1 << 1)
+#define PRISM_CHAR_BIT_REGEXP_OPTION (1 << 2)
+
+#define PRISM_NUMBER_BIT_BINARY_DIGIT (1 << 0)
+#define PRISM_NUMBER_BIT_BINARY_NUMBER (1 << 1)
+#define PRISM_NUMBER_BIT_OCTAL_DIGIT (1 << 2)
+#define PRISM_NUMBER_BIT_OCTAL_NUMBER (1 << 3)
+#define PRISM_NUMBER_BIT_DECIMAL_DIGIT (1 << 4)
+#define PRISM_NUMBER_BIT_DECIMAL_NUMBER (1 << 5)
+#define PRISM_NUMBER_BIT_HEXADECIMAL_DIGIT (1 << 6)
+#define PRISM_NUMBER_BIT_HEXADECIMAL_NUMBER (1 << 7)
+
+static const uint8_t pm_byte_table[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 3, 3, 3, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 4x
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, // 5x
+ 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 6x
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx
+};
+
+static const uint8_t pm_number_table[256] = {
+ // 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1x
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 2x
+ 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 3x
+ 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 4x
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, // 5x
+ 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 6x
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 7x
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8x
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9x
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ax
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Bx
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Cx
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Dx
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ex
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Fx
+};
+
+/**
+ * Returns the number of characters at the start of the string that match the
+ * given kind. Disallows searching past the given maximum number of characters.
+ */
+static inline size_t
+pm_strspn_char_kind(const uint8_t *string, ptrdiff_t length, uint8_t kind) {
+ if (length <= 0) return 0;
+
+ size_t size = 0;
+ size_t maximum = (size_t) length;
+
+ while (size < maximum && (pm_byte_table[string[size]] & kind)) size++;
+ return size;
+}
+
+/**
+ * Returns the number of characters at the start of the string that are
+ * whitespace. Disallows searching past the given maximum number of characters.
+ */
+size_t
+pm_strspn_whitespace(const uint8_t *string, ptrdiff_t length) {
+ return pm_strspn_char_kind(string, length, PRISM_CHAR_BIT_WHITESPACE);
+}
+
+/**
+ * Returns the number of characters at the start of the string that are
+ * whitespace while also tracking the location of each newline. Disallows
+ * searching past the given maximum number of characters.
+ */
+size_t
+pm_strspn_whitespace_newlines(const uint8_t *string, ptrdiff_t length, pm_newline_list_t *newline_list) {
+ if (length <= 0) return 0;
+
+ size_t size = 0;
+ size_t maximum = (size_t) length;
+
+ while (size < maximum && (pm_byte_table[string[size]] & PRISM_CHAR_BIT_WHITESPACE)) {
+ if (string[size] == '\n') {
+ pm_newline_list_append(newline_list, string + size);
+ }
+
+ size++;
+ }
+
+ return size;
+}
+
+/**
+ * Returns the number of characters at the start of the string that are inline
+ * whitespace. Disallows searching past the given maximum number of characters.
+ */
+size_t
+pm_strspn_inline_whitespace(const uint8_t *string, ptrdiff_t length) {
+ return pm_strspn_char_kind(string, length, PRISM_CHAR_BIT_INLINE_WHITESPACE);
+}
+
+/**
+ * Returns the number of characters at the start of the string that are regexp
+ * options. Disallows searching past the given maximum number of characters.
+ */
+size_t
+pm_strspn_regexp_option(const uint8_t *string, ptrdiff_t length) {
+ return pm_strspn_char_kind(string, length, PRISM_CHAR_BIT_REGEXP_OPTION);
+}
+
+/**
+ * Returns true if the given character matches the given kind.
+ */
+static inline bool
+pm_char_is_char_kind(const uint8_t b, uint8_t kind) {
+ return (pm_byte_table[b] & kind) != 0;
+}
+
+/**
+ * Returns true if the given character is a whitespace character.
+ */
+bool
+pm_char_is_whitespace(const uint8_t b) {
+ return pm_char_is_char_kind(b, PRISM_CHAR_BIT_WHITESPACE);
+}
+
+/**
+ * Returns true if the given character is an inline whitespace character.
+ */
+bool
+pm_char_is_inline_whitespace(const uint8_t b) {
+ return pm_char_is_char_kind(b, PRISM_CHAR_BIT_INLINE_WHITESPACE);
+}
+
+/**
+ * Scan through the string and return the number of characters at the start of
+ * the string that match the given kind. Disallows searching past the given
+ * maximum number of characters.
+ */
+static inline size_t
+pm_strspn_number_kind(const uint8_t *string, ptrdiff_t length, uint8_t kind) {
+ if (length <= 0) return 0;
+
+ size_t size = 0;
+ size_t maximum = (size_t) length;
+
+ while (size < maximum && (pm_number_table[string[size]] & kind)) size++;
+ return size;
+}
+
+/**
+ * Scan through the string and return the number of characters at the start of
+ * the string that match the given kind. Disallows searching past the given
+ * maximum number of characters.
+ *
+ * Additionally, report the location of the last invalid underscore character
+ * found in the string through the out invalid parameter.
+ */
+static inline size_t
+pm_strspn_number_kind_underscores(const uint8_t *string, ptrdiff_t length, const uint8_t **invalid, uint8_t kind) {
+ if (length <= 0) return 0;
+
+ size_t size = 0;
+ size_t maximum = (size_t) length;
+
+ bool underscore = false;
+ while (size < maximum && (pm_number_table[string[size]] & kind)) {
+ if (string[size] == '_') {
+ if (underscore) *invalid = string + size;
+ underscore = true;
+ } else {
+ underscore = false;
+ }
+
+ size++;
+ }
+
+ if (string[size - 1] == '_') *invalid = string + size - 1;
+ return size;
+}
+
+/**
+ * Returns the number of characters at the start of the string that are binary
+ * digits or underscores. Disallows searching past the given maximum number of
+ * characters.
+ *
+ * If multiple underscores are found in a row or if an underscore is
+ * found at the end of the number, then the invalid pointer is set to the index
+ * of the first invalid underscore.
+ */
+size_t
+pm_strspn_binary_number(const uint8_t *string, ptrdiff_t length, const uint8_t **invalid) {
+ return pm_strspn_number_kind_underscores(string, length, invalid, PRISM_NUMBER_BIT_BINARY_NUMBER);
+}
+
+/**
+ * Returns the number of characters at the start of the string that are octal
+ * digits or underscores. Disallows searching past the given maximum number of
+ * characters.
+ *
+ * If multiple underscores are found in a row or if an underscore is
+ * found at the end of the number, then the invalid pointer is set to the index
+ * of the first invalid underscore.
+ */
+size_t
+pm_strspn_octal_number(const uint8_t *string, ptrdiff_t length, const uint8_t **invalid) {
+ return pm_strspn_number_kind_underscores(string, length, invalid, PRISM_NUMBER_BIT_OCTAL_NUMBER);
+}
+
+/**
+ * Returns the number of characters at the start of the string that are decimal
+ * digits. Disallows searching past the given maximum number of characters.
+ */
+size_t
+pm_strspn_decimal_digit(const uint8_t *string, ptrdiff_t length) {
+ return pm_strspn_number_kind(string, length, PRISM_NUMBER_BIT_DECIMAL_DIGIT);
+}
+
+/**
+ * Returns the number of characters at the start of the string that are decimal
+ * digits or underscores. Disallows searching past the given maximum number of
+ * characters.
+ *
+ * If multiple underscores are found in a row or if an underscore is
+ * found at the end of the number, then the invalid pointer is set to the index
+ * of the first invalid underscore
+ */
+size_t
+pm_strspn_decimal_number(const uint8_t *string, ptrdiff_t length, const uint8_t **invalid) {
+ return pm_strspn_number_kind_underscores(string, length, invalid, PRISM_NUMBER_BIT_DECIMAL_NUMBER);
+}
+
+/**
+ * Returns the number of characters at the start of the string that are
+ * hexadecimal digits. Disallows searching past the given maximum number of
+ * characters.
+ */
+size_t
+pm_strspn_hexadecimal_digit(const uint8_t *string, ptrdiff_t length) {
+ return pm_strspn_number_kind(string, length, PRISM_NUMBER_BIT_HEXADECIMAL_DIGIT);
+}
+
+/**
+ * Returns the number of characters at the start of the string that are
+ * hexadecimal digits or underscores. Disallows searching past the given maximum
+ * number of characters.
+ *
+ * If multiple underscores are found in a row or if an underscore is
+ * found at the end of the number, then the invalid pointer is set to the index
+ * of the first invalid underscore.
+ */
+size_t
+pm_strspn_hexadecimal_number(const uint8_t *string, ptrdiff_t length, const uint8_t **invalid) {
+ return pm_strspn_number_kind_underscores(string, length, invalid, PRISM_NUMBER_BIT_HEXADECIMAL_NUMBER);
+}
+
+/**
+ * Returns true if the given character matches the given kind.
+ */
+static inline bool
+pm_char_is_number_kind(const uint8_t b, uint8_t kind) {
+ return (pm_number_table[b] & kind) != 0;
+}
+
+/**
+ * Returns true if the given character is a binary digit.
+ */
+bool
+pm_char_is_binary_digit(const uint8_t b) {
+ return pm_char_is_number_kind(b, PRISM_NUMBER_BIT_BINARY_DIGIT);
+}
+
+/**
+ * Returns true if the given character is an octal digit.
+ */
+bool
+pm_char_is_octal_digit(const uint8_t b) {
+ return pm_char_is_number_kind(b, PRISM_NUMBER_BIT_OCTAL_DIGIT);
+}
+
+/**
+ * Returns true if the given character is a decimal digit.
+ */
+bool
+pm_char_is_decimal_digit(const uint8_t b) {
+ return pm_char_is_number_kind(b, PRISM_NUMBER_BIT_DECIMAL_DIGIT);
+}
+
+/**
+ * Returns true if the given character is a hexadecimal digit.
+ */
+bool
+pm_char_is_hexadecimal_digit(const uint8_t b) {
+ return pm_char_is_number_kind(b, PRISM_NUMBER_BIT_HEXADECIMAL_DIGIT);
+}
+
+#undef PRISM_CHAR_BIT_WHITESPACE
+#undef PRISM_CHAR_BIT_INLINE_WHITESPACE
+#undef PRISM_CHAR_BIT_REGEXP_OPTION
+
+#undef PRISM_NUMBER_BIT_BINARY_DIGIT
+#undef PRISM_NUMBER_BIT_BINARY_NUMBER
+#undef PRISM_NUMBER_BIT_OCTAL_DIGIT
+#undef PRISM_NUMBER_BIT_OCTAL_NUMBER
+#undef PRISM_NUMBER_BIT_DECIMAL_DIGIT
+#undef PRISM_NUMBER_BIT_DECIMAL_NUMBER
+#undef PRISM_NUMBER_BIT_HEXADECIMAL_NUMBER
+#undef PRISM_NUMBER_BIT_HEXADECIMAL_DIGIT
diff --git a/prism/util/pm_char.h b/prism/util/pm_char.h
new file mode 100644
index 0000000000..32f698a42b
--- /dev/null
+++ b/prism/util/pm_char.h
@@ -0,0 +1,205 @@
+/**
+ * @file pm_char.h
+ *
+ * Functions for working with characters and strings.
+ */
+#ifndef PRISM_CHAR_H
+#define PRISM_CHAR_H
+
+#include "prism/defines.h"
+#include "prism/util/pm_newline_list.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+
+/**
+ * Returns the number of characters at the start of the string that are
+ * whitespace. Disallows searching past the given maximum number of characters.
+ *
+ * @param string The string to search.
+ * @param length The maximum number of characters to search.
+ * @return The number of characters at the start of the string that are
+ * whitespace.
+ */
+size_t pm_strspn_whitespace(const uint8_t *string, ptrdiff_t length);
+
+/**
+ * Returns the number of characters at the start of the string that are
+ * whitespace while also tracking the location of each newline. Disallows
+ * searching past the given maximum number of characters.
+ *
+ * @param string The string to search.
+ * @param length The maximum number of characters to search.
+ * @param newline_list The list of newlines to populate.
+ * @return The number of characters at the start of the string that are
+ * whitespace.
+ */
+size_t
+pm_strspn_whitespace_newlines(const uint8_t *string, ptrdiff_t length, pm_newline_list_t *newline_list);
+
+/**
+ * Returns the number of characters at the start of the string that are inline
+ * whitespace. Disallows searching past the given maximum number of characters.
+ *
+ * @param string The string to search.
+ * @param length The maximum number of characters to search.
+ * @return The number of characters at the start of the string that are inline
+ * whitespace.
+ */
+size_t pm_strspn_inline_whitespace(const uint8_t *string, ptrdiff_t length);
+
+/**
+ * Returns the number of characters at the start of the string that are decimal
+ * digits. Disallows searching past the given maximum number of characters.
+ *
+ * @param string The string to search.
+ * @param length The maximum number of characters to search.
+ * @return The number of characters at the start of the string that are decimal
+ * digits.
+ */
+size_t pm_strspn_decimal_digit(const uint8_t *string, ptrdiff_t length);
+
+/**
+ * Returns the number of characters at the start of the string that are
+ * hexadecimal digits. Disallows searching past the given maximum number of
+ * characters.
+ *
+ * @param string The string to search.
+ * @param length The maximum number of characters to search.
+ * @return The number of characters at the start of the string that are
+ * hexadecimal digits.
+ */
+size_t pm_strspn_hexadecimal_digit(const uint8_t *string, ptrdiff_t length);
+
+/**
+ * Returns the number of characters at the start of the string that are octal
+ * digits or underscores. Disallows searching past the given maximum number of
+ * characters.
+ *
+ * If multiple underscores are found in a row or if an underscore is
+ * found at the end of the number, then the invalid pointer is set to the index
+ * of the first invalid underscore.
+ *
+ * @param string The string to search.
+ * @param length The maximum number of characters to search.
+ * @param invalid The pointer to set to the index of the first invalid
+ * underscore.
+ * @return The number of characters at the start of the string that are octal
+ * digits or underscores.
+ */
+size_t pm_strspn_octal_number(const uint8_t *string, ptrdiff_t length, const uint8_t **invalid);
+
+/**
+ * Returns the number of characters at the start of the string that are decimal
+ * digits or underscores. Disallows searching past the given maximum number of
+ * characters.
+ *
+ * If multiple underscores are found in a row or if an underscore is
+ * found at the end of the number, then the invalid pointer is set to the index
+ * of the first invalid underscore.
+ *
+ * @param string The string to search.
+ * @param length The maximum number of characters to search.
+ * @param invalid The pointer to set to the index of the first invalid
+ * underscore.
+ * @return The number of characters at the start of the string that are decimal
+ * digits or underscores.
+ */
+size_t pm_strspn_decimal_number(const uint8_t *string, ptrdiff_t length, const uint8_t **invalid);
+
+/**
+ * Returns the number of characters at the start of the string that are
+ * hexadecimal digits or underscores. Disallows searching past the given maximum
+ * number of characters.
+ *
+ * If multiple underscores are found in a row or if an underscore is
+ * found at the end of the number, then the invalid pointer is set to the index
+ * of the first invalid underscore.
+ *
+ * @param string The string to search.
+ * @param length The maximum number of characters to search.
+ * @param invalid The pointer to set to the index of the first invalid
+ * underscore.
+ * @return The number of characters at the start of the string that are
+ * hexadecimal digits or underscores.
+ */
+size_t pm_strspn_hexadecimal_number(const uint8_t *string, ptrdiff_t length, const uint8_t **invalid);
+
+/**
+ * Returns the number of characters at the start of the string that are regexp
+ * options. Disallows searching past the given maximum number of characters.
+ *
+ * @param string The string to search.
+ * @param length The maximum number of characters to search.
+ * @return The number of characters at the start of the string that are regexp
+ * options.
+ */
+size_t pm_strspn_regexp_option(const uint8_t *string, ptrdiff_t length);
+
+/**
+ * Returns the number of characters at the start of the string that are binary
+ * digits or underscores. Disallows searching past the given maximum number of
+ * characters.
+ *
+ * If multiple underscores are found in a row or if an underscore is
+ * found at the end of the number, then the invalid pointer is set to the index
+ * of the first invalid underscore.
+ *
+ * @param string The string to search.
+ * @param length The maximum number of characters to search.
+ * @param invalid The pointer to set to the index of the first invalid
+ * underscore.
+ * @return The number of characters at the start of the string that are binary
+ * digits or underscores.
+ */
+size_t pm_strspn_binary_number(const uint8_t *string, ptrdiff_t length, const uint8_t **invalid);
+
+/**
+ * Returns true if the given character is a whitespace character.
+ *
+ * @param b The character to check.
+ * @return True if the given character is a whitespace character.
+ */
+bool pm_char_is_whitespace(const uint8_t b);
+
+/**
+ * Returns true if the given character is an inline whitespace character.
+ *
+ * @param b The character to check.
+ * @return True if the given character is an inline whitespace character.
+ */
+bool pm_char_is_inline_whitespace(const uint8_t b);
+
+/**
+ * Returns true if the given character is a binary digit.
+ *
+ * @param b The character to check.
+ * @return True if the given character is a binary digit.
+ */
+bool pm_char_is_binary_digit(const uint8_t b);
+
+/**
+ * Returns true if the given character is an octal digit.
+ *
+ * @param b The character to check.
+ * @return True if the given character is an octal digit.
+ */
+bool pm_char_is_octal_digit(const uint8_t b);
+
+/**
+ * Returns true if the given character is a decimal digit.
+ *
+ * @param b The character to check.
+ * @return True if the given character is a decimal digit.
+ */
+bool pm_char_is_decimal_digit(const uint8_t b);
+
+/**
+ * Returns true if the given character is a hexadecimal digit.
+ *
+ * @param b The character to check.
+ * @return True if the given character is a hexadecimal digit.
+ */
+bool pm_char_is_hexadecimal_digit(const uint8_t b);
+
+#endif
diff --git a/prism/util/pm_constant_pool.c b/prism/util/pm_constant_pool.c
new file mode 100644
index 0000000000..2a3203f4c4
--- /dev/null
+++ b/prism/util/pm_constant_pool.c
@@ -0,0 +1,346 @@
+#include "prism/util/pm_constant_pool.h"
+
+/**
+ * Initialize a list of constant ids.
+ */
+void
+pm_constant_id_list_init(pm_constant_id_list_t *list) {
+ list->ids = NULL;
+ list->size = 0;
+ list->capacity = 0;
+}
+
+/**
+ * Initialize a list of constant ids with a given capacity.
+ */
+void
+pm_constant_id_list_init_capacity(pm_constant_id_list_t *list, size_t capacity) {
+ list->ids = xcalloc(capacity, sizeof(pm_constant_id_t));
+ if (list->ids == NULL) abort();
+
+ list->size = 0;
+ list->capacity = capacity;
+}
+
+/**
+ * Append a constant id to a list of constant ids. Returns false if any
+ * potential reallocations fail.
+ */
+bool
+pm_constant_id_list_append(pm_constant_id_list_t *list, pm_constant_id_t id) {
+ if (list->size >= list->capacity) {
+ list->capacity = list->capacity == 0 ? 8 : list->capacity * 2;
+ list->ids = (pm_constant_id_t *) xrealloc(list->ids, sizeof(pm_constant_id_t) * list->capacity);
+ if (list->ids == NULL) return false;
+ }
+
+ list->ids[list->size++] = id;
+ return true;
+}
+
+/**
+ * Insert a constant id into a list of constant ids at the specified index.
+ */
+void
+pm_constant_id_list_insert(pm_constant_id_list_t *list, size_t index, pm_constant_id_t id) {
+ assert(index < list->capacity);
+ assert(list->ids[index] == PM_CONSTANT_ID_UNSET);
+
+ list->ids[index] = id;
+ list->size++;
+}
+
+/**
+ * Checks if the current constant id list includes the given constant id.
+ */
+bool
+pm_constant_id_list_includes(pm_constant_id_list_t *list, pm_constant_id_t id) {
+ for (size_t index = 0; index < list->size; index++) {
+ if (list->ids[index] == id) return true;
+ }
+ return false;
+}
+
+/**
+ * Get the memory size of a list of constant ids.
+ */
+size_t
+pm_constant_id_list_memsize(pm_constant_id_list_t *list) {
+ return sizeof(pm_constant_id_list_t) + (list->capacity * sizeof(pm_constant_id_t));
+}
+
+/**
+ * Free the memory associated with a list of constant ids.
+ */
+void
+pm_constant_id_list_free(pm_constant_id_list_t *list) {
+ if (list->ids != NULL) {
+ xfree(list->ids);
+ }
+}
+
+/**
+ * A relatively simple hash function (djb2) that is used to hash strings. We are
+ * optimizing here for simplicity and speed.
+ */
+static inline uint32_t
+pm_constant_pool_hash(const uint8_t *start, size_t length) {
+ // This is a prime number used as the initial value for the hash function.
+ uint32_t value = 5381;
+
+ for (size_t index = 0; index < length; index++) {
+ value = ((value << 5) + value) + start[index];
+ }
+
+ return value;
+}
+
+/**
+ * https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
+ */
+static uint32_t
+next_power_of_two(uint32_t v) {
+ // Avoid underflow in subtraction on next line.
+ if (v == 0) {
+ // 1 is the nearest power of 2 to 0 (2^0)
+ return 1;
+ }
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v++;
+ return v;
+}
+
+#ifndef NDEBUG
+static bool
+is_power_of_two(uint32_t size) {
+ return (size & (size - 1)) == 0;
+}
+#endif
+
+/**
+ * Resize a constant pool to a given capacity.
+ */
+static inline bool
+pm_constant_pool_resize(pm_constant_pool_t *pool) {
+ assert(is_power_of_two(pool->capacity));
+
+ uint32_t next_capacity = pool->capacity * 2;
+ if (next_capacity < pool->capacity) return false;
+
+ const uint32_t mask = next_capacity - 1;
+ const size_t element_size = sizeof(pm_constant_pool_bucket_t) + sizeof(pm_constant_t);
+
+ void *next = xcalloc(next_capacity, element_size);
+ if (next == NULL) return false;
+
+ pm_constant_pool_bucket_t *next_buckets = next;
+ pm_constant_t *next_constants = (void *)(((char *) next) + next_capacity * sizeof(pm_constant_pool_bucket_t));
+
+ // For each bucket in the current constant pool, find the index in the
+ // next constant pool, and insert it.
+ for (uint32_t index = 0; index < pool->capacity; index++) {
+ pm_constant_pool_bucket_t *bucket = &pool->buckets[index];
+
+ // If an id is set on this constant, then we know we have content here.
+ // In this case we need to insert it into the next constant pool.
+ if (bucket->id != PM_CONSTANT_ID_UNSET) {
+ uint32_t next_index = bucket->hash & mask;
+
+ // This implements linear scanning to find the next available slot
+ // in case this index is already taken. We don't need to bother
+ // comparing the values since we know that the hash is unique.
+ while (next_buckets[next_index].id != PM_CONSTANT_ID_UNSET) {
+ next_index = (next_index + 1) & mask;
+ }
+
+ // Here we copy over the entire bucket, which includes the id so
+ // that they are consistent between resizes.
+ next_buckets[next_index] = *bucket;
+ }
+ }
+
+ // The constants are stable with respect to hash table resizes.
+ memcpy(next_constants, pool->constants, pool->size * sizeof(pm_constant_t));
+
+ // pool->constants and pool->buckets are allocated out of the same chunk
+ // of memory, with the buckets coming first.
+ xfree(pool->buckets);
+ pool->constants = next_constants;
+ pool->buckets = next_buckets;
+ pool->capacity = next_capacity;
+ return true;
+}
+
+/**
+ * Initialize a new constant pool with a given capacity.
+ */
+bool
+pm_constant_pool_init(pm_constant_pool_t *pool, uint32_t capacity) {
+ const uint32_t maximum = (~((uint32_t) 0));
+ if (capacity >= ((maximum / 2) + 1)) return false;
+
+ capacity = next_power_of_two(capacity);
+ const size_t element_size = sizeof(pm_constant_pool_bucket_t) + sizeof(pm_constant_t);
+ void *memory = xcalloc(capacity, element_size);
+ if (memory == NULL) return false;
+
+ pool->buckets = memory;
+ pool->constants = (void *)(((char *)memory) + capacity * sizeof(pm_constant_pool_bucket_t));
+ pool->size = 0;
+ pool->capacity = capacity;
+ return true;
+}
+
+/**
+ * Return a pointer to the constant indicated by the given constant id.
+ */
+pm_constant_t *
+pm_constant_pool_id_to_constant(const pm_constant_pool_t *pool, pm_constant_id_t constant_id) {
+ assert(constant_id != PM_CONSTANT_ID_UNSET && constant_id <= pool->size);
+ return &pool->constants[constant_id - 1];
+}
+
+/**
+ * Find a constant in a constant pool. Returns the id of the constant, or 0 if
+ * the constant is not found.
+ */
+pm_constant_id_t
+pm_constant_pool_find(const pm_constant_pool_t *pool, const uint8_t *start, size_t length) {
+ assert(is_power_of_two(pool->capacity));
+ const uint32_t mask = pool->capacity - 1;
+
+ uint32_t hash = pm_constant_pool_hash(start, length);
+ uint32_t index = hash & mask;
+ pm_constant_pool_bucket_t *bucket;
+
+ while (bucket = &pool->buckets[index], bucket->id != PM_CONSTANT_ID_UNSET) {
+ pm_constant_t *constant = &pool->constants[bucket->id - 1];
+ if ((constant->length == length) && memcmp(constant->start, start, length) == 0) {
+ return bucket->id;
+ }
+
+ index = (index + 1) & mask;
+ }
+
+ return PM_CONSTANT_ID_UNSET;
+}
+
+/**
+ * Insert a constant into a constant pool and return its index in the pool.
+ */
+static inline pm_constant_id_t
+pm_constant_pool_insert(pm_constant_pool_t *pool, const uint8_t *start, size_t length, pm_constant_pool_bucket_type_t type) {
+ if (pool->size >= (pool->capacity / 4 * 3)) {
+ if (!pm_constant_pool_resize(pool)) return PM_CONSTANT_ID_UNSET;
+ }
+
+ assert(is_power_of_two(pool->capacity));
+ const uint32_t mask = pool->capacity - 1;
+
+ uint32_t hash = pm_constant_pool_hash(start, length);
+ uint32_t index = hash & mask;
+ pm_constant_pool_bucket_t *bucket;
+
+ while (bucket = &pool->buckets[index], bucket->id != PM_CONSTANT_ID_UNSET) {
+ // If there is a collision, then we need to check if the content is the
+ // same as the content we are trying to insert. If it is, then we can
+ // return the id of the existing constant.
+ pm_constant_t *constant = &pool->constants[bucket->id - 1];
+
+ if ((constant->length == length) && memcmp(constant->start, start, length) == 0) {
+ // Since we have found a match, we need to check if this is
+ // attempting to insert a shared or an owned constant. We want to
+ // prefer shared constants since they don't require allocations.
+ if (type == PM_CONSTANT_POOL_BUCKET_OWNED) {
+ // If we're attempting to insert an owned constant and we have
+ // an existing constant, then either way we don't want the given
+ // memory. Either it's duplicated with the existing constant or
+ // it's not necessary because we have a shared version.
+ xfree((void *) start);
+ } else if (bucket->type == PM_CONSTANT_POOL_BUCKET_OWNED) {
+ // If we're attempting to insert a shared constant and the
+ // existing constant is owned, then we can free the owned
+ // constant and replace it with the shared constant.
+ xfree((void *) constant->start);
+ constant->start = start;
+ bucket->type = (unsigned int) (PM_CONSTANT_POOL_BUCKET_DEFAULT & 0x3);
+ }
+
+ return bucket->id;
+ }
+
+ index = (index + 1) & mask;
+ }
+
+ // IDs are allocated starting at 1, since the value 0 denotes a non-existent
+ // constant.
+ uint32_t id = ++pool->size;
+ assert(pool->size < ((uint32_t) (1 << 30)));
+
+ *bucket = (pm_constant_pool_bucket_t) {
+ .id = (unsigned int) (id & 0x3fffffff),
+ .type = (unsigned int) (type & 0x3),
+ .hash = hash
+ };
+
+ pool->constants[id - 1] = (pm_constant_t) {
+ .start = start,
+ .length = length,
+ };
+
+ return id;
+}
+
+/**
+ * Insert a constant into a constant pool. Returns the id of the constant, or
+ * PM_CONSTANT_ID_UNSET if any potential calls to resize fail.
+ */
+pm_constant_id_t
+pm_constant_pool_insert_shared(pm_constant_pool_t *pool, const uint8_t *start, size_t length) {
+ return pm_constant_pool_insert(pool, start, length, PM_CONSTANT_POOL_BUCKET_DEFAULT);
+}
+
+/**
+ * Insert a constant into a constant pool from memory that is now owned by the
+ * constant pool. Returns the id of the constant, or PM_CONSTANT_ID_UNSET if any
+ * potential calls to resize fail.
+ */
+pm_constant_id_t
+pm_constant_pool_insert_owned(pm_constant_pool_t *pool, uint8_t *start, size_t length) {
+ return pm_constant_pool_insert(pool, start, length, PM_CONSTANT_POOL_BUCKET_OWNED);
+}
+
+/**
+ * Insert a constant into a constant pool from memory that is constant. Returns
+ * the id of the constant, or PM_CONSTANT_ID_UNSET if any potential calls to
+ * resize fail.
+ */
+pm_constant_id_t
+pm_constant_pool_insert_constant(pm_constant_pool_t *pool, const uint8_t *start, size_t length) {
+ return pm_constant_pool_insert(pool, start, length, PM_CONSTANT_POOL_BUCKET_CONSTANT);
+}
+
+/**
+ * Free the memory associated with a constant pool.
+ */
+void
+pm_constant_pool_free(pm_constant_pool_t *pool) {
+ // For each constant in the current constant pool, free the contents if the
+ // contents are owned.
+ for (uint32_t index = 0; index < pool->capacity; index++) {
+ pm_constant_pool_bucket_t *bucket = &pool->buckets[index];
+
+ // If an id is set on this constant, then we know we have content here.
+ if (bucket->id != PM_CONSTANT_ID_UNSET && bucket->type == PM_CONSTANT_POOL_BUCKET_OWNED) {
+ pm_constant_t *constant = &pool->constants[bucket->id - 1];
+ xfree((void *) constant->start);
+ }
+ }
+
+ xfree(pool->buckets);
+}
diff --git a/prism/util/pm_constant_pool.h b/prism/util/pm_constant_pool.h
new file mode 100644
index 0000000000..0fe16858a0
--- /dev/null
+++ b/prism/util/pm_constant_pool.h
@@ -0,0 +1,226 @@
+/**
+ * @file pm_constant_pool.h
+ *
+ * A data structure that stores a set of strings.
+ *
+ * Each string is assigned a unique id, which can be used to compare strings for
+ * equality. This comparison ends up being much faster than strcmp, since it
+ * only requires a single integer comparison.
+ */
+#ifndef PRISM_CONSTANT_POOL_H
+#define PRISM_CONSTANT_POOL_H
+
+#include "prism/defines.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * When we allocate constants into the pool, we reserve 0 to mean that the slot
+ * is not yet filled. This constant is reused in other places to indicate the
+ * lack of a constant id.
+ */
+#define PM_CONSTANT_ID_UNSET 0
+
+/**
+ * A constant id is a unique identifier for a constant in the constant pool.
+ */
+typedef uint32_t pm_constant_id_t;
+
+/**
+ * A list of constant IDs. Usually used to represent a set of locals.
+ */
+typedef struct {
+ /** The number of constant ids in the list. */
+ size_t size;
+
+ /** The number of constant ids that have been allocated in the list. */
+ size_t capacity;
+
+ /** The constant ids in the list. */
+ pm_constant_id_t *ids;
+} pm_constant_id_list_t;
+
+/**
+ * Initialize a list of constant ids.
+ *
+ * @param list The list to initialize.
+ */
+void pm_constant_id_list_init(pm_constant_id_list_t *list);
+
+/**
+ * Initialize a list of constant ids with a given capacity.
+ *
+ * @param list The list to initialize.
+ * @param capacity The initial capacity of the list.
+ */
+void pm_constant_id_list_init_capacity(pm_constant_id_list_t *list, size_t capacity);
+
+/**
+ * Append a constant id to a list of constant ids. Returns false if any
+ * potential reallocations fail.
+ *
+ * @param list The list to append to.
+ * @param id The id to append.
+ * @return Whether the append succeeded.
+ */
+bool pm_constant_id_list_append(pm_constant_id_list_t *list, pm_constant_id_t id);
+
+/**
+ * Insert a constant id into a list of constant ids at the specified index.
+ *
+ * @param list The list to insert into.
+ * @param index The index at which to insert.
+ * @param id The id to insert.
+ */
+void pm_constant_id_list_insert(pm_constant_id_list_t *list, size_t index, pm_constant_id_t id);
+
+/**
+ * Checks if the current constant id list includes the given constant id.
+ *
+ * @param list The list to check.
+ * @param id The id to check for.
+ * @return Whether the list includes the given id.
+ */
+bool pm_constant_id_list_includes(pm_constant_id_list_t *list, pm_constant_id_t id);
+
+/**
+ * Get the memory size of a list of constant ids.
+ *
+ * @param list The list to get the memory size of.
+ * @return The memory size of the list.
+ */
+size_t pm_constant_id_list_memsize(pm_constant_id_list_t *list);
+
+/**
+ * Free the memory associated with a list of constant ids.
+ *
+ * @param list The list to free.
+ */
+void pm_constant_id_list_free(pm_constant_id_list_t *list);
+
+/**
+ * The type of bucket in the constant pool hash map. This determines how the
+ * bucket should be freed.
+ */
+typedef unsigned int pm_constant_pool_bucket_type_t;
+
+/** By default, each constant is a slice of the source. */
+static const pm_constant_pool_bucket_type_t PM_CONSTANT_POOL_BUCKET_DEFAULT = 0;
+
+/** An owned constant is one for which memory has been allocated. */
+static const pm_constant_pool_bucket_type_t PM_CONSTANT_POOL_BUCKET_OWNED = 1;
+
+/** A constant constant is known at compile time. */
+static const pm_constant_pool_bucket_type_t PM_CONSTANT_POOL_BUCKET_CONSTANT = 2;
+
+/** A bucket in the hash map. */
+typedef struct {
+ /** The incremental ID used for indexing back into the pool. */
+ unsigned int id: 30;
+
+ /** The type of the bucket, which determines how to free it. */
+ pm_constant_pool_bucket_type_t type: 2;
+
+ /** The hash of the bucket. */
+ uint32_t hash;
+} pm_constant_pool_bucket_t;
+
+/** A constant in the pool which effectively stores a string. */
+typedef struct {
+ /** A pointer to the start of the string. */
+ const uint8_t *start;
+
+ /** The length of the string. */
+ size_t length;
+} pm_constant_t;
+
+/** The overall constant pool, which stores constants found while parsing. */
+typedef struct {
+ /** The buckets in the hash map. */
+ pm_constant_pool_bucket_t *buckets;
+
+ /** The constants that are stored in the buckets. */
+ pm_constant_t *constants;
+
+ /** The number of buckets in the hash map. */
+ uint32_t size;
+
+ /** The number of buckets that have been allocated in the hash map. */
+ uint32_t capacity;
+} pm_constant_pool_t;
+
+/**
+ * Initialize a new constant pool with a given capacity.
+ *
+ * @param pool The pool to initialize.
+ * @param capacity The initial capacity of the pool.
+ * @return Whether the initialization succeeded.
+ */
+bool pm_constant_pool_init(pm_constant_pool_t *pool, uint32_t capacity);
+
+/**
+ * Return a pointer to the constant indicated by the given constant id.
+ *
+ * @param pool The pool to get the constant from.
+ * @param constant_id The id of the constant to get.
+ * @return A pointer to the constant.
+ */
+pm_constant_t * pm_constant_pool_id_to_constant(const pm_constant_pool_t *pool, pm_constant_id_t constant_id);
+
+/**
+ * Find a constant in a constant pool. Returns the id of the constant, or 0 if
+ * the constant is not found.
+ *
+ * @param pool The pool to find the constant in.
+ * @param start A pointer to the start of the constant.
+ * @param length The length of the constant.
+ * @return The id of the constant.
+ */
+pm_constant_id_t pm_constant_pool_find(const pm_constant_pool_t *pool, const uint8_t *start, size_t length);
+
+/**
+ * Insert a constant into a constant pool that is a slice of a source string.
+ * Returns the id of the constant, or 0 if any potential calls to resize fail.
+ *
+ * @param pool The pool to insert the constant into.
+ * @param start A pointer to the start of the constant.
+ * @param length The length of the constant.
+ * @return The id of the constant.
+ */
+pm_constant_id_t pm_constant_pool_insert_shared(pm_constant_pool_t *pool, const uint8_t *start, size_t length);
+
+/**
+ * Insert a constant into a constant pool from memory that is now owned by the
+ * constant pool. Returns the id of the constant, or 0 if any potential calls to
+ * resize fail.
+ *
+ * @param pool The pool to insert the constant into.
+ * @param start A pointer to the start of the constant.
+ * @param length The length of the constant.
+ * @return The id of the constant.
+ */
+pm_constant_id_t pm_constant_pool_insert_owned(pm_constant_pool_t *pool, uint8_t *start, size_t length);
+
+/**
+ * Insert a constant into a constant pool from memory that is constant. Returns
+ * the id of the constant, or 0 if any potential calls to resize fail.
+ *
+ * @param pool The pool to insert the constant into.
+ * @param start A pointer to the start of the constant.
+ * @param length The length of the constant.
+ * @return The id of the constant.
+ */
+pm_constant_id_t pm_constant_pool_insert_constant(pm_constant_pool_t *pool, const uint8_t *start, size_t length);
+
+/**
+ * Free the memory associated with a constant pool.
+ *
+ * @param pool The pool to free.
+ */
+void pm_constant_pool_free(pm_constant_pool_t *pool);
+
+#endif
diff --git a/prism/util/pm_integer.c b/prism/util/pm_integer.c
new file mode 100644
index 0000000000..e523bae90b
--- /dev/null
+++ b/prism/util/pm_integer.c
@@ -0,0 +1,642 @@
+#include "prism/util/pm_integer.h"
+
+/**
+ * Pull out the length and values from the integer, regardless of the form in
+ * which the length/values are stored.
+ */
+#define INTEGER_EXTRACT(integer, length_variable, values_variable) \
+ if ((integer)->values == NULL) { \
+ length_variable = 1; \
+ values_variable = &(integer)->value; \
+ } else { \
+ length_variable = (integer)->length; \
+ values_variable = (integer)->values; \
+ }
+
+/**
+ * Adds two positive pm_integer_t with the given base.
+ * Return pm_integer_t with values allocated. Not normalized.
+ */
+static void
+big_add(pm_integer_t *destination, pm_integer_t *left, pm_integer_t *right, uint64_t base) {
+ size_t left_length;
+ uint32_t *left_values;
+ INTEGER_EXTRACT(left, left_length, left_values)
+
+ size_t right_length;
+ uint32_t *right_values;
+ INTEGER_EXTRACT(right, right_length, right_values)
+
+ size_t length = left_length < right_length ? right_length : left_length;
+ uint32_t *values = (uint32_t *) xmalloc(sizeof(uint32_t) * (length + 1));
+ if (values == NULL) return;
+
+ uint64_t carry = 0;
+ for (size_t index = 0; index < length; index++) {
+ uint64_t sum = carry + (index < left_length ? left_values[index] : 0) + (index < right_length ? right_values[index] : 0);
+ values[index] = (uint32_t) (sum % base);
+ carry = sum / base;
+ }
+
+ if (carry > 0) {
+ values[length] = (uint32_t) carry;
+ length++;
+ }
+
+ *destination = (pm_integer_t) { 0, length, values, false };
+}
+
+/**
+ * Internal use for karatsuba_multiply. Calculates `a - b - c` with the given
+ * base. Assume a, b, c, a - b - c all to be poitive.
+ * Return pm_integer_t with values allocated. Not normalized.
+ */
+static void
+big_sub2(pm_integer_t *destination, pm_integer_t *a, pm_integer_t *b, pm_integer_t *c, uint64_t base) {
+ size_t a_length;
+ uint32_t *a_values;
+ INTEGER_EXTRACT(a, a_length, a_values)
+
+ size_t b_length;
+ uint32_t *b_values;
+ INTEGER_EXTRACT(b, b_length, b_values)
+
+ size_t c_length;
+ uint32_t *c_values;
+ INTEGER_EXTRACT(c, c_length, c_values)
+
+ uint32_t *values = (uint32_t*) xmalloc(sizeof(uint32_t) * a_length);
+ int64_t carry = 0;
+
+ for (size_t index = 0; index < a_length; index++) {
+ int64_t sub = (
+ carry +
+ a_values[index] -
+ (index < b_length ? b_values[index] : 0) -
+ (index < c_length ? c_values[index] : 0)
+ );
+
+ if (sub >= 0) {
+ values[index] = (uint32_t) sub;
+ carry = 0;
+ } else {
+ sub += 2 * (int64_t) base;
+ values[index] = (uint32_t) ((uint64_t) sub % base);
+ carry = sub / (int64_t) base - 2;
+ }
+ }
+
+ while (a_length > 1 && values[a_length - 1] == 0) a_length--;
+ *destination = (pm_integer_t) { 0, a_length, values, false };
+}
+
+/**
+ * Multiply two positive integers with the given base using karatsuba algorithm.
+ * Return pm_integer_t with values allocated. Not normalized.
+ */
+static void
+karatsuba_multiply(pm_integer_t *destination, pm_integer_t *left, pm_integer_t *right, uint64_t base) {
+ size_t left_length;
+ uint32_t *left_values;
+ INTEGER_EXTRACT(left, left_length, left_values)
+
+ size_t right_length;
+ uint32_t *right_values;
+ INTEGER_EXTRACT(right, right_length, right_values)
+
+ if (left_length > right_length) {
+ size_t temporary_length = left_length;
+ left_length = right_length;
+ right_length = temporary_length;
+
+ uint32_t *temporary_values = left_values;
+ left_values = right_values;
+ right_values = temporary_values;
+ }
+
+ if (left_length <= 10) {
+ size_t length = left_length + right_length;
+ uint32_t *values = (uint32_t *) xcalloc(length, sizeof(uint32_t));
+ if (values == NULL) return;
+
+ for (size_t left_index = 0; left_index < left_length; left_index++) {
+ uint32_t carry = 0;
+ for (size_t right_index = 0; right_index < right_length; right_index++) {
+ uint64_t product = (uint64_t) left_values[left_index] * right_values[right_index] + values[left_index + right_index] + carry;
+ values[left_index + right_index] = (uint32_t) (product % base);
+ carry = (uint32_t) (product / base);
+ }
+ values[left_index + right_length] = carry;
+ }
+
+ while (length > 1 && values[length - 1] == 0) length--;
+ *destination = (pm_integer_t) { 0, length, values, false };
+ return;
+ }
+
+ if (left_length * 2 <= right_length) {
+ uint32_t *values = (uint32_t *) xcalloc(left_length + right_length, sizeof(uint32_t));
+
+ for (size_t start_offset = 0; start_offset < right_length; start_offset += left_length) {
+ size_t end_offset = start_offset + left_length;
+ if (end_offset > right_length) end_offset = right_length;
+
+ pm_integer_t sliced_left = {
+ .value = 0,
+ .length = left_length,
+ .values = left_values,
+ .negative = false
+ };
+
+ pm_integer_t sliced_right = {
+ .value = 0,
+ .length = end_offset - start_offset,
+ .values = right_values + start_offset,
+ .negative = false
+ };
+
+ pm_integer_t product;
+ karatsuba_multiply(&product, &sliced_left, &sliced_right, base);
+
+ uint32_t carry = 0;
+ for (size_t index = 0; index < product.length; index++) {
+ uint64_t sum = (uint64_t) values[start_offset + index] + product.values[index] + carry;
+ values[start_offset + index] = (uint32_t) (sum % base);
+ carry = (uint32_t) (sum / base);
+ }
+
+ if (carry > 0) values[start_offset + product.length] += carry;
+ pm_integer_free(&product);
+ }
+
+ *destination = (pm_integer_t) { 0, left_length + right_length, values, false };
+ return;
+ }
+
+ size_t half = left_length / 2;
+ pm_integer_t x0 = { 0, half, left_values, false };
+ pm_integer_t x1 = { 0, left_length - half, left_values + half, false };
+ pm_integer_t y0 = { 0, half, right_values, false };
+ pm_integer_t y1 = { 0, right_length - half, right_values + half, false };
+
+ pm_integer_t z0 = { 0 };
+ karatsuba_multiply(&z0, &x0, &y0, base);
+
+ pm_integer_t z2 = { 0 };
+ karatsuba_multiply(&z2, &x1, &y1, base);
+
+ // For simplicity to avoid considering negative values,
+ // use `z1 = (x0 + x1) * (y0 + y1) - z0 - z2` instead of original karatsuba algorithm.
+ pm_integer_t x01 = { 0 };
+ big_add(&x01, &x0, &x1, base);
+
+ pm_integer_t y01 = { 0 };
+ big_add(&y01, &y0, &y1, base);
+
+ pm_integer_t xy = { 0 };
+ karatsuba_multiply(&xy, &x01, &y01, base);
+
+ pm_integer_t z1;
+ big_sub2(&z1, &xy, &z0, &z2, base);
+
+ size_t length = left_length + right_length;
+ uint32_t *values = (uint32_t*) xcalloc(length, sizeof(uint32_t));
+
+ assert(z0.values != NULL);
+ memcpy(values, z0.values, sizeof(uint32_t) * z0.length);
+
+ assert(z2.values != NULL);
+ memcpy(values + 2 * half, z2.values, sizeof(uint32_t) * z2.length);
+
+ uint32_t carry = 0;
+ for(size_t index = 0; index < z1.length; index++) {
+ uint64_t sum = (uint64_t) carry + values[index + half] + z1.values[index];
+ values[index + half] = (uint32_t) (sum % base);
+ carry = (uint32_t) (sum / base);
+ }
+
+ for(size_t index = half + z1.length; carry > 0; index++) {
+ uint64_t sum = (uint64_t) carry + values[index];
+ values[index] = (uint32_t) (sum % base);
+ carry = (uint32_t) (sum / base);
+ }
+
+ while (length > 1 && values[length - 1] == 0) length--;
+ pm_integer_free(&z0);
+ pm_integer_free(&z1);
+ pm_integer_free(&z2);
+ pm_integer_free(&x01);
+ pm_integer_free(&y01);
+ pm_integer_free(&xy);
+
+ *destination = (pm_integer_t) { 0, length, values, false };
+}
+
+/**
+ * The values of a hexadecimal digit, where the index is the ASCII character.
+ * Note that there's an odd exception here where _ is mapped to 0. This is
+ * because it's possible for us to end up trying to parse a number that has
+ * already had an error attached to it, and we want to provide _something_ to
+ * the user.
+ */
+static const int8_t pm_integer_parse_digit_values[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 1x
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 2x
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 3x
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 4x
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, // 5x
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 6x
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 7x
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 8x
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 9x
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Ax
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Bx
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Cx
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Dx
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Ex
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Fx
+};
+
+/**
+ * Return the value of a hexadecimal digit in a uint8_t.
+ */
+static uint8_t
+pm_integer_parse_digit(const uint8_t character) {
+ int8_t value = pm_integer_parse_digit_values[character];
+ assert(value != -1 && "invalid digit");
+
+ return (uint8_t) value;
+}
+
+/**
+ * Create a pm_integer_t from uint64_t with the given base. It is assumed that
+ * the memory for the pm_integer_t pointer has been zeroed.
+ */
+static void
+pm_integer_from_uint64(pm_integer_t *integer, uint64_t value, uint64_t base) {
+ if (value < base) {
+ integer->value = (uint32_t) value;
+ return;
+ }
+
+ size_t length = 0;
+ uint64_t length_value = value;
+ while (length_value > 0) {
+ length++;
+ length_value /= base;
+ }
+
+ uint32_t *values = (uint32_t *) xmalloc(sizeof(uint32_t) * length);
+ if (values == NULL) return;
+
+ for (size_t value_index = 0; value_index < length; value_index++) {
+ values[value_index] = (uint32_t) (value % base);
+ value /= base;
+ }
+
+ integer->length = length;
+ integer->values = values;
+}
+
+/**
+ * Normalize pm_integer_t.
+ * Heading zero values will be removed. If the integer fits into uint32_t,
+ * values is set to NULL, length is set to 0, and value field will be used.
+ */
+static void
+pm_integer_normalize(pm_integer_t *integer) {
+ if (integer->values == NULL) {
+ return;
+ }
+
+ while (integer->length > 1 && integer->values[integer->length - 1] == 0) {
+ integer->length--;
+ }
+
+ if (integer->length > 1) {
+ return;
+ }
+
+ uint32_t value = integer->values[0];
+ bool negative = integer->negative && value != 0;
+
+ pm_integer_free(integer);
+ *integer = (pm_integer_t) { .value = value, .length = 0, .values = NULL, .negative = negative };
+}
+
+/**
+ * Convert base of the integer.
+ * In practice, it converts 10**9 to 1<<32 or 1<<32 to 10**9.
+ */
+static void
+pm_integer_convert_base(pm_integer_t *destination, const pm_integer_t *source, uint64_t base_from, uint64_t base_to) {
+ size_t source_length;
+ const uint32_t *source_values;
+ INTEGER_EXTRACT(source, source_length, source_values)
+
+ size_t bigints_length = (source_length + 1) / 2;
+ assert(bigints_length > 0);
+
+ pm_integer_t *bigints = (pm_integer_t *) xcalloc(bigints_length, sizeof(pm_integer_t));
+ if (bigints == NULL) return;
+
+ for (size_t index = 0; index < source_length; index += 2) {
+ uint64_t value = source_values[index] + base_from * (index + 1 < source_length ? source_values[index + 1] : 0);
+ pm_integer_from_uint64(&bigints[index / 2], value, base_to);
+ }
+
+ pm_integer_t base = { 0 };
+ pm_integer_from_uint64(&base, base_from, base_to);
+
+ while (bigints_length > 1) {
+ pm_integer_t next_base;
+ karatsuba_multiply(&next_base, &base, &base, base_to);
+
+ pm_integer_free(&base);
+ base = next_base;
+
+ size_t next_length = (bigints_length + 1) / 2;
+ pm_integer_t *next_bigints = (pm_integer_t *) xcalloc(next_length, sizeof(pm_integer_t));
+
+ for (size_t bigints_index = 0; bigints_index < bigints_length; bigints_index += 2) {
+ if (bigints_index + 1 == bigints_length) {
+ next_bigints[bigints_index / 2] = bigints[bigints_index];
+ } else {
+ pm_integer_t multiplied = { 0 };
+ karatsuba_multiply(&multiplied, &base, &bigints[bigints_index + 1], base_to);
+
+ big_add(&next_bigints[bigints_index / 2], &bigints[bigints_index], &multiplied, base_to);
+ pm_integer_free(&bigints[bigints_index]);
+ pm_integer_free(&bigints[bigints_index + 1]);
+ pm_integer_free(&multiplied);
+ }
+ }
+
+ xfree(bigints);
+ bigints = next_bigints;
+ bigints_length = next_length;
+ }
+
+ *destination = bigints[0];
+ destination->negative = source->negative;
+ pm_integer_normalize(destination);
+
+ xfree(bigints);
+ pm_integer_free(&base);
+}
+
+#undef INTEGER_EXTRACT
+
+/**
+ * Convert digits to integer with the given power-of-two base.
+ */
+static void
+pm_integer_parse_powof2(pm_integer_t *integer, uint32_t base, const uint8_t *digits, size_t digits_length) {
+ size_t bit = 1;
+ while (base > (uint32_t) (1 << bit)) bit++;
+
+ size_t length = (digits_length * bit + 31) / 32;
+ uint32_t *values = (uint32_t *) xcalloc(length, sizeof(uint32_t));
+
+ for (size_t digit_index = 0; digit_index < digits_length; digit_index++) {
+ size_t bit_position = bit * (digits_length - digit_index - 1);
+ uint32_t value = digits[digit_index];
+
+ size_t index = bit_position / 32;
+ size_t shift = bit_position % 32;
+
+ values[index] |= value << shift;
+ if (32 - shift < bit) values[index + 1] |= value >> (32 - shift);
+ }
+
+ while (length > 1 && values[length - 1] == 0) length--;
+ *integer = (pm_integer_t) { .value = 0, .length = length, .values = values, .negative = false };
+ pm_integer_normalize(integer);
+}
+
+/**
+ * Convert decimal digits to pm_integer_t.
+ */
+static void
+pm_integer_parse_decimal(pm_integer_t *integer, const uint8_t *digits, size_t digits_length) {
+ const size_t batch = 9;
+ size_t length = (digits_length + batch - 1) / batch;
+
+ uint32_t *values = (uint32_t *) xcalloc(length, sizeof(uint32_t));
+ uint32_t value = 0;
+
+ for (size_t digits_index = 0; digits_index < digits_length; digits_index++) {
+ value = value * 10 + digits[digits_index];
+
+ size_t reverse_index = digits_length - digits_index - 1;
+ if (reverse_index % batch == 0) {
+ values[reverse_index / batch] = value;
+ value = 0;
+ }
+ }
+
+ // Convert base from 10**9 to 1<<32.
+ pm_integer_convert_base(integer, &((pm_integer_t) { .value = 0, .length = length, .values = values, .negative = false }), 1000000000, ((uint64_t) 1 << 32));
+ xfree(values);
+}
+
+/**
+ * Parse a large integer from a string that does not fit into uint32_t.
+ */
+static void
+pm_integer_parse_big(pm_integer_t *integer, uint32_t multiplier, const uint8_t *start, const uint8_t *end) {
+ // Allocate an array to store digits.
+ uint8_t *digits = xmalloc(sizeof(uint8_t) * (size_t) (end - start));
+ size_t digits_length = 0;
+
+ for (; start < end; start++) {
+ if (*start == '_') continue;
+ digits[digits_length++] = pm_integer_parse_digit(*start);
+ }
+
+ // Construct pm_integer_t from the digits.
+ if (multiplier == 10) {
+ pm_integer_parse_decimal(integer, digits, digits_length);
+ } else {
+ pm_integer_parse_powof2(integer, multiplier, digits, digits_length);
+ }
+
+ xfree(digits);
+}
+
+/**
+ * Parse an integer from a string. This assumes that the format of the integer
+ * has already been validated, as internal validation checks are not performed
+ * here.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_integer_parse(pm_integer_t *integer, pm_integer_base_t base, const uint8_t *start, const uint8_t *end) {
+ // Ignore unary +. Unary + is parsed differently and will not end up here.
+ // Instead, it will modify the parsed integer later.
+ if (*start == '+') start++;
+
+ // Determine the multiplier from the base, and skip past any prefixes.
+ uint32_t multiplier = 10;
+ switch (base) {
+ case PM_INTEGER_BASE_BINARY:
+ start += 2; // 0b
+ multiplier = 2;
+ break;
+ case PM_INTEGER_BASE_OCTAL:
+ start++; // 0
+ if (*start == '_' || *start == 'o' || *start == 'O') start++; // o
+ multiplier = 8;
+ break;
+ case PM_INTEGER_BASE_DECIMAL:
+ if (*start == '0' && (end - start) > 1) start += 2; // 0d
+ break;
+ case PM_INTEGER_BASE_HEXADECIMAL:
+ start += 2; // 0x
+ multiplier = 16;
+ break;
+ case PM_INTEGER_BASE_UNKNOWN:
+ if (*start == '0' && (end - start) > 1) {
+ switch (start[1]) {
+ case '_': start += 2; multiplier = 8; break;
+ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': start++; multiplier = 8; break;
+ case 'b': case 'B': start += 2; multiplier = 2; break;
+ case 'o': case 'O': start += 2; multiplier = 8; break;
+ case 'd': case 'D': start += 2; break;
+ case 'x': case 'X': start += 2; multiplier = 16; break;
+ default: assert(false && "unreachable"); break;
+ }
+ }
+ break;
+ }
+
+ // It's possible that we've consumed everything at this point if there is an
+ // invalid integer. If this is the case, we'll just return 0.
+ if (start >= end) return;
+
+ const uint8_t *cursor = start;
+ uint64_t value = (uint64_t) pm_integer_parse_digit(*cursor++);
+
+ for (; cursor < end; cursor++) {
+ if (*cursor == '_') continue;
+ value = value * multiplier + (uint64_t) pm_integer_parse_digit(*cursor);
+
+ if (value > UINT32_MAX) {
+ // If the integer is too large to fit into a single uint32_t, then
+ // we'll parse it as a big integer.
+ pm_integer_parse_big(integer, multiplier, start, end);
+ return;
+ }
+ }
+
+ integer->value = (uint32_t) value;
+}
+
+/**
+ * Return the memory size of the integer.
+ */
+size_t
+pm_integer_memsize(const pm_integer_t *integer) {
+ return sizeof(pm_integer_t) + integer->length * sizeof(uint32_t);
+}
+
+/**
+ * Compare two integers. This function returns -1 if the left integer is less
+ * than the right integer, 0 if they are equal, and 1 if the left integer is
+ * greater than the right integer.
+ */
+int
+pm_integer_compare(const pm_integer_t *left, const pm_integer_t *right) {
+ if (left->negative != right->negative) return left->negative ? -1 : 1;
+ int negative = left->negative ? -1 : 1;
+
+ if (left->values == NULL && right->values == NULL) {
+ if (left->value < right->value) return -1 * negative;
+ if (left->value > right->value) return 1 * negative;
+ return 0;
+ }
+
+ if (left->values == NULL || left->length < right->length) return -1 * negative;
+ if (right->values == NULL || left->length > right->length) return 1 * negative;
+
+ for (size_t index = 0; index < left->length; index++) {
+ size_t value_index = left->length - index - 1;
+ uint32_t left_value = left->values[value_index];
+ uint32_t right_value = right->values[value_index];
+
+ if (left_value < right_value) return -1 * negative;
+ if (left_value > right_value) return 1 * negative;
+ }
+
+ return 0;
+}
+
+/**
+ * Convert an integer to a decimal string.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_integer_string(pm_buffer_t *buffer, const pm_integer_t *integer) {
+ if (integer->negative) {
+ pm_buffer_append_byte(buffer, '-');
+ }
+
+ // If the integer fits into a single uint32_t, then we can just append the
+ // value directly to the buffer.
+ if (integer->values == NULL) {
+ pm_buffer_append_format(buffer, "%" PRIu32, integer->value);
+ return;
+ }
+
+ // If the integer is two uint32_t values, then we can | them together and
+ // append the result to the buffer.
+ if (integer->length == 2) {
+ const uint64_t value = ((uint64_t) integer->values[0]) | ((uint64_t) integer->values[1] << 32);
+ pm_buffer_append_format(buffer, "%" PRIu64, value);
+ return;
+ }
+
+ // Otherwise, first we'll convert the base from 1<<32 to 10**9.
+ pm_integer_t converted = { 0 };
+ pm_integer_convert_base(&converted, integer, (uint64_t) 1 << 32, 1000000000);
+
+ if (converted.values == NULL) {
+ pm_buffer_append_format(buffer, "%" PRIu32, converted.value);
+ pm_integer_free(&converted);
+ return;
+ }
+
+ // Allocate a buffer that we'll copy the decimal digits into.
+ size_t digits_length = converted.length * 9;
+ char *digits = xcalloc(digits_length, sizeof(char));
+ if (digits == NULL) return;
+
+ // Pack bigdecimal to digits.
+ for (size_t value_index = 0; value_index < converted.length; value_index++) {
+ uint32_t value = converted.values[value_index];
+
+ for (size_t digit_index = 0; digit_index < 9; digit_index++) {
+ digits[digits_length - 9 * value_index - digit_index - 1] = (char) ('0' + value % 10);
+ value /= 10;
+ }
+ }
+
+ size_t start_offset = 0;
+ while (start_offset < digits_length - 1 && digits[start_offset] == '0') start_offset++;
+
+ // Finally, append the string to the buffer and free the digits.
+ pm_buffer_append_string(buffer, digits + start_offset, digits_length - start_offset);
+ xfree(digits);
+ pm_integer_free(&converted);
+}
+
+/**
+ * Free the internal memory of an integer. This memory will only be allocated if
+ * the integer exceeds the size of a single uint32_t.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_integer_free(pm_integer_t *integer) {
+ if (integer->values) {
+ xfree(integer->values);
+ }
+}
diff --git a/prism/util/pm_integer.h b/prism/util/pm_integer.h
new file mode 100644
index 0000000000..7f172988b3
--- /dev/null
+++ b/prism/util/pm_integer.h
@@ -0,0 +1,119 @@
+/**
+ * @file pm_integer.h
+ *
+ * This module provides functions for working with arbitrary-sized integers.
+ */
+#ifndef PRISM_NUMBER_H
+#define PRISM_NUMBER_H
+
+#include "prism/defines.h"
+#include "prism/util/pm_buffer.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+/**
+ * A structure represents an arbitrary-sized integer.
+ */
+typedef struct {
+ /**
+ * Embedded value for small integer. This value is set to 0 if the value
+ * does not fit into uint32_t.
+ */
+ uint32_t value;
+
+ /**
+ * The number of allocated values. length is set to 0 if the integer fits
+ * into uint32_t.
+ */
+ size_t length;
+
+ /**
+ * List of 32-bit integers. Set to NULL if the integer fits into uint32_t.
+ */
+ uint32_t *values;
+
+ /**
+ * Whether or not the integer is negative. It is stored this way so that a
+ * zeroed pm_integer_t is always positive zero.
+ */
+ bool negative;
+} pm_integer_t;
+
+/**
+ * An enum controlling the base of an integer. It is expected that the base is
+ * already known before parsing the integer, even though it could be derived
+ * from the string itself.
+ */
+typedef enum {
+ /** The binary base, indicated by a 0b or 0B prefix. */
+ PM_INTEGER_BASE_BINARY,
+
+ /** The octal base, indicated by a 0, 0o, or 0O prefix. */
+ PM_INTEGER_BASE_OCTAL,
+
+ /** The decimal base, indicated by a 0d, 0D, or empty prefix. */
+ PM_INTEGER_BASE_DECIMAL,
+
+ /** The hexadecimal base, indicated by a 0x or 0X prefix. */
+ PM_INTEGER_BASE_HEXADECIMAL,
+
+ /**
+ * An unknown base, in which case pm_integer_parse will derive it based on
+ * the content of the string. This is less efficient and does more
+ * comparisons, so if callers know the base ahead of time, they should use
+ * that instead.
+ */
+ PM_INTEGER_BASE_UNKNOWN
+} pm_integer_base_t;
+
+/**
+ * Parse an integer from a string. This assumes that the format of the integer
+ * has already been validated, as internal validation checks are not performed
+ * here.
+ *
+ * @param integer The integer to parse into.
+ * @param base The base of the integer.
+ * @param start The start of the string.
+ * @param end The end of the string.
+ */
+PRISM_EXPORTED_FUNCTION void pm_integer_parse(pm_integer_t *integer, pm_integer_base_t base, const uint8_t *start, const uint8_t *end);
+
+/**
+ * Return the memory size of the integer.
+ *
+ * @param integer The integer to get the memory size of.
+ * @return The size of the memory associated with the integer.
+ */
+size_t pm_integer_memsize(const pm_integer_t *integer);
+
+/**
+ * Compare two integers. This function returns -1 if the left integer is less
+ * than the right integer, 0 if they are equal, and 1 if the left integer is
+ * greater than the right integer.
+ *
+ * @param left The left integer to compare.
+ * @param right The right integer to compare.
+ * @return The result of the comparison.
+ */
+int pm_integer_compare(const pm_integer_t *left, const pm_integer_t *right);
+
+/**
+ * Convert an integer to a decimal string.
+ *
+ * @param buffer The buffer to append the string to.
+ * @param integer The integer to convert to a string.
+ */
+PRISM_EXPORTED_FUNCTION void pm_integer_string(pm_buffer_t *buffer, const pm_integer_t *integer);
+
+/**
+ * Free the internal memory of an integer. This memory will only be allocated if
+ * the integer exceeds the size of a single node in the linked list.
+ *
+ * @param integer The integer to free.
+ */
+PRISM_EXPORTED_FUNCTION void pm_integer_free(pm_integer_t *integer);
+
+#endif
diff --git a/prism/util/pm_list.c b/prism/util/pm_list.c
new file mode 100644
index 0000000000..ad2294cd60
--- /dev/null
+++ b/prism/util/pm_list.c
@@ -0,0 +1,49 @@
+#include "prism/util/pm_list.h"
+
+/**
+ * Returns true if the given list is empty.
+ */
+PRISM_EXPORTED_FUNCTION bool
+pm_list_empty_p(pm_list_t *list) {
+ return list->head == NULL;
+}
+
+/**
+ * Returns the size of the list.
+ */
+PRISM_EXPORTED_FUNCTION size_t
+pm_list_size(pm_list_t *list) {
+ return list->size;
+}
+
+/**
+ * Append a node to the given list.
+ */
+void
+pm_list_append(pm_list_t *list, pm_list_node_t *node) {
+ if (list->head == NULL) {
+ list->head = node;
+ } else {
+ list->tail->next = node;
+ }
+
+ list->tail = node;
+ list->size++;
+}
+
+/**
+ * Deallocate the internal state of the given list.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_list_free(pm_list_t *list) {
+ pm_list_node_t *node = list->head;
+ pm_list_node_t *next;
+
+ while (node != NULL) {
+ next = node->next;
+ xfree(node);
+ node = next;
+ }
+
+ list->size = 0;
+}
diff --git a/prism/util/pm_list.h b/prism/util/pm_list.h
new file mode 100644
index 0000000000..3512dee979
--- /dev/null
+++ b/prism/util/pm_list.h
@@ -0,0 +1,97 @@
+/**
+ * @file pm_list.h
+ *
+ * An abstract linked list.
+ */
+#ifndef PRISM_LIST_H
+#define PRISM_LIST_H
+
+#include "prism/defines.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+/**
+ * This struct represents an abstract linked list that provides common
+ * functionality. It is meant to be used any time a linked list is necessary to
+ * store data.
+ *
+ * The linked list itself operates off a set of pointers. Because the pointers
+ * are not necessarily sequential, they can be of any size. We use this fact to
+ * allow the consumer of this linked list to extend the node struct to include
+ * any data they want. This is done by using the pm_list_node_t as the first
+ * member of the struct.
+ *
+ * For example, if we want to store a list of integers, we can do the following:
+ *
+ * ```c
+ * typedef struct {
+ * pm_list_node_t node;
+ * int value;
+ * } pm_int_node_t;
+ *
+ * pm_list_t list = { 0 };
+ * pm_int_node_t *node = xmalloc(sizeof(pm_int_node_t));
+ * node->value = 5;
+ *
+ * pm_list_append(&list, &node->node);
+ * ```
+ *
+ * The pm_list_t struct is used to represent the overall linked list. It
+ * contains a pointer to the head and tail of the list. This allows for easy
+ * iteration and appending of new nodes.
+ */
+typedef struct pm_list_node {
+ /** A pointer to the next node in the list. */
+ struct pm_list_node *next;
+} pm_list_node_t;
+
+/**
+ * This represents the overall linked list. It keeps a pointer to the head and
+ * tail so that iteration is easy and pushing new nodes is easy.
+ */
+typedef struct {
+ /** The size of the list. */
+ size_t size;
+
+ /** A pointer to the head of the list. */
+ pm_list_node_t *head;
+
+ /** A pointer to the tail of the list. */
+ pm_list_node_t *tail;
+} pm_list_t;
+
+/**
+ * Returns true if the given list is empty.
+ *
+ * @param list The list to check.
+ * @return True if the given list is empty, otherwise false.
+ */
+PRISM_EXPORTED_FUNCTION bool pm_list_empty_p(pm_list_t *list);
+
+/**
+ * Returns the size of the list.
+ *
+ * @param list The list to check.
+ * @return The size of the list.
+ */
+PRISM_EXPORTED_FUNCTION size_t pm_list_size(pm_list_t *list);
+
+/**
+ * Append a node to the given list.
+ *
+ * @param list The list to append to.
+ * @param node The node to append.
+ */
+void pm_list_append(pm_list_t *list, pm_list_node_t *node);
+
+/**
+ * Deallocate the internal state of the given list.
+ *
+ * @param list The list to free.
+ */
+PRISM_EXPORTED_FUNCTION void pm_list_free(pm_list_t *list);
+
+#endif
diff --git a/prism/util/pm_memchr.c b/prism/util/pm_memchr.c
new file mode 100644
index 0000000000..7ea20ace6d
--- /dev/null
+++ b/prism/util/pm_memchr.c
@@ -0,0 +1,35 @@
+#include "prism/util/pm_memchr.h"
+
+#define PRISM_MEMCHR_TRAILING_BYTE_MINIMUM 0x40
+
+/**
+ * We need to roll our own memchr to handle cases where the encoding changes and
+ * we need to search for a character in a buffer that could be the trailing byte
+ * of a multibyte character.
+ */
+void *
+pm_memchr(const void *memory, int character, size_t number, bool encoding_changed, const pm_encoding_t *encoding) {
+ if (encoding_changed && encoding->multibyte && character >= PRISM_MEMCHR_TRAILING_BYTE_MINIMUM) {
+ const uint8_t *source = (const uint8_t *) memory;
+ size_t index = 0;
+
+ while (index < number) {
+ if (source[index] == character) {
+ return (void *) (source + index);
+ }
+
+ size_t width = encoding->char_width(source + index, (ptrdiff_t) (number - index));
+ if (width == 0) {
+ return NULL;
+ }
+
+ index += width;
+ }
+
+ return NULL;
+ } else {
+ return memchr(memory, character, number);
+ }
+}
+
+#undef PRISM_MEMCHR_TRAILING_BYTE_MINIMUM
diff --git a/prism/util/pm_memchr.h b/prism/util/pm_memchr.h
new file mode 100644
index 0000000000..e0671eaed3
--- /dev/null
+++ b/prism/util/pm_memchr.h
@@ -0,0 +1,29 @@
+/**
+ * @file pm_memchr.h
+ *
+ * A custom memchr implementation.
+ */
+#ifndef PRISM_MEMCHR_H
+#define PRISM_MEMCHR_H
+
+#include "prism/defines.h"
+#include "prism/encoding.h"
+
+#include <stddef.h>
+
+/**
+ * We need to roll our own memchr to handle cases where the encoding changes and
+ * we need to search for a character in a buffer that could be the trailing byte
+ * of a multibyte character.
+ *
+ * @param source The source string.
+ * @param character The character to search for.
+ * @param number The maximum number of bytes to search.
+ * @param encoding_changed Whether the encoding changed.
+ * @param encoding A pointer to the encoding.
+ * @return A pointer to the first occurrence of the character in the source
+ * string, or NULL if no such character exists.
+ */
+void * pm_memchr(const void *source, int character, size_t number, bool encoding_changed, const pm_encoding_t *encoding);
+
+#endif
diff --git a/prism/util/pm_newline_list.c b/prism/util/pm_newline_list.c
new file mode 100644
index 0000000000..ce07ce8c8e
--- /dev/null
+++ b/prism/util/pm_newline_list.c
@@ -0,0 +1,96 @@
+#include "prism/util/pm_newline_list.h"
+
+/**
+ * Initialize a new newline list with the given capacity. Returns true if the
+ * allocation of the offsets succeeds, otherwise returns false.
+ */
+bool
+pm_newline_list_init(pm_newline_list_t *list, const uint8_t *start, size_t capacity) {
+ list->offsets = (size_t *) xcalloc(capacity, sizeof(size_t));
+ if (list->offsets == NULL) return false;
+
+ list->start = start;
+
+ // This is 1 instead of 0 because we want to include the first line of the
+ // file as having offset 0, which is set because of calloc.
+ list->size = 1;
+ list->capacity = capacity;
+
+ return true;
+}
+
+/**
+ * Clear out the newlines that have been appended to the list.
+ */
+void
+pm_newline_list_clear(pm_newline_list_t *list) {
+ list->size = 1;
+}
+
+/**
+ * Append a new offset to the newline list. Returns true if the reallocation of
+ * the offsets succeeds (if one was necessary), otherwise returns false.
+ */
+bool
+pm_newline_list_append(pm_newline_list_t *list, const uint8_t *cursor) {
+ if (list->size == list->capacity) {
+ size_t *original_offsets = list->offsets;
+
+ list->capacity = (list->capacity * 3) / 2;
+ list->offsets = (size_t *) xcalloc(list->capacity, sizeof(size_t));
+ if (list->offsets == NULL) return false;
+
+ memcpy(list->offsets, original_offsets, list->size * sizeof(size_t));
+ xfree(original_offsets);
+ }
+
+ assert(*cursor == '\n');
+ assert(cursor >= list->start);
+ size_t newline_offset = (size_t) (cursor - list->start + 1);
+
+ assert(list->size == 0 || newline_offset > list->offsets[list->size - 1]);
+ list->offsets[list->size++] = newline_offset;
+
+ return true;
+}
+
+/**
+ * Returns the line and column of the given offset. If the offset is not in the
+ * list, the line and column of the closest offset less than the given offset
+ * are returned.
+ */
+pm_line_column_t
+pm_newline_list_line_column(const pm_newline_list_t *list, const uint8_t *cursor, int32_t start_line) {
+ assert(cursor >= list->start);
+ size_t offset = (size_t) (cursor - list->start);
+
+ size_t left = 0;
+ size_t right = list->size - 1;
+
+ while (left <= right) {
+ size_t mid = left + (right - left) / 2;
+
+ if (list->offsets[mid] == offset) {
+ return ((pm_line_column_t) { ((int32_t) mid) + start_line, 0 });
+ }
+
+ if (list->offsets[mid] < offset) {
+ left = mid + 1;
+ } else {
+ right = mid - 1;
+ }
+ }
+
+ return ((pm_line_column_t) {
+ .line = ((int32_t) left) + start_line - 1,
+ .column = (uint32_t) (offset - list->offsets[left - 1])
+ });
+}
+
+/**
+ * Free the internal memory allocated for the newline list.
+ */
+void
+pm_newline_list_free(pm_newline_list_t *list) {
+ xfree(list->offsets);
+}
diff --git a/prism/util/pm_newline_list.h b/prism/util/pm_newline_list.h
new file mode 100644
index 0000000000..7ae9b6b3da
--- /dev/null
+++ b/prism/util/pm_newline_list.h
@@ -0,0 +1,102 @@
+/**
+ * @file pm_newline_list.h
+ *
+ * A list of byte offsets of newlines in a string.
+ *
+ * When compiling the syntax tree, it's necessary to know the line and column
+ * of many nodes. This is necessary to support things like error messages,
+ * tracepoints, etc.
+ *
+ * It's possible that we could store the start line, start column, end line, and
+ * end column on every node in addition to the offsets that we already store,
+ * but that would be quite a lot of memory overhead.
+ */
+#ifndef PRISM_NEWLINE_LIST_H
+#define PRISM_NEWLINE_LIST_H
+
+#include "prism/defines.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+/**
+ * A list of offsets of newlines in a string. The offsets are assumed to be
+ * sorted/inserted in ascending order.
+ */
+typedef struct {
+ /** A pointer to the start of the source string. */
+ const uint8_t *start;
+
+ /** The number of offsets in the list. */
+ size_t size;
+
+ /** The capacity of the list that has been allocated. */
+ size_t capacity;
+
+ /** The list of offsets. */
+ size_t *offsets;
+} pm_newline_list_t;
+
+/**
+ * A line and column in a string.
+ */
+typedef struct {
+ /** The line number. */
+ int32_t line;
+
+ /** The column number. */
+ uint32_t column;
+} pm_line_column_t;
+
+/**
+ * Initialize a new newline list with the given capacity. Returns true if the
+ * allocation of the offsets succeeds, otherwise returns false.
+ *
+ * @param list The list to initialize.
+ * @param start A pointer to the start of the source string.
+ * @param capacity The initial capacity of the list.
+ * @return True if the allocation of the offsets succeeds, otherwise false.
+ */
+bool pm_newline_list_init(pm_newline_list_t *list, const uint8_t *start, size_t capacity);
+
+/**
+ * Clear out the newlines that have been appended to the list.
+ *
+ * @param list The list to clear.
+ */
+void
+pm_newline_list_clear(pm_newline_list_t *list);
+
+/**
+ * Append a new offset to the newline list. Returns true if the reallocation of
+ * the offsets succeeds (if one was necessary), otherwise returns false.
+ *
+ * @param list The list to append to.
+ * @param cursor A pointer to the offset to append.
+ * @return True if the reallocation of the offsets succeeds (if one was
+ * necessary), otherwise false.
+ */
+bool pm_newline_list_append(pm_newline_list_t *list, const uint8_t *cursor);
+
+/**
+ * Returns the line and column of the given offset. If the offset is not in the
+ * list, the line and column of the closest offset less than the given offset
+ * are returned.
+ *
+ * @param list The list to search.
+ * @param cursor A pointer to the offset to search for.
+ * @param start_line The line to start counting from.
+ * @return The line and column of the given offset.
+ */
+pm_line_column_t pm_newline_list_line_column(const pm_newline_list_t *list, const uint8_t *cursor, int32_t start_line);
+
+/**
+ * Free the internal memory allocated for the newline list.
+ *
+ * @param list The list to free.
+ */
+void pm_newline_list_free(pm_newline_list_t *list);
+
+#endif
diff --git a/prism/util/pm_string.c b/prism/util/pm_string.c
new file mode 100644
index 0000000000..8342edc34e
--- /dev/null
+++ b/prism/util/pm_string.c
@@ -0,0 +1,332 @@
+#include "prism/util/pm_string.h"
+
+/**
+ * Returns the size of the pm_string_t struct. This is necessary to allocate the
+ * correct amount of memory in the FFI backend.
+ */
+PRISM_EXPORTED_FUNCTION size_t
+pm_string_sizeof(void) {
+ return sizeof(pm_string_t);
+}
+
+/**
+ * Initialize a shared string that is based on initial input.
+ */
+void
+pm_string_shared_init(pm_string_t *string, const uint8_t *start, const uint8_t *end) {
+ assert(start <= end);
+
+ *string = (pm_string_t) {
+ .type = PM_STRING_SHARED,
+ .source = start,
+ .length = (size_t) (end - start)
+ };
+}
+
+/**
+ * Initialize an owned string that is responsible for freeing allocated memory.
+ */
+void
+pm_string_owned_init(pm_string_t *string, uint8_t *source, size_t length) {
+ *string = (pm_string_t) {
+ .type = PM_STRING_OWNED,
+ .source = source,
+ .length = length
+ };
+}
+
+/**
+ * Initialize a constant string that doesn't own its memory source.
+ */
+void
+pm_string_constant_init(pm_string_t *string, const char *source, size_t length) {
+ *string = (pm_string_t) {
+ .type = PM_STRING_CONSTANT,
+ .source = (const uint8_t *) source,
+ .length = length
+ };
+}
+
+/**
+ * Read the file indicated by the filepath parameter into source and load its
+ * contents and size into the given `pm_string_t`. The given `pm_string_t`
+ * should be freed using `pm_string_free` when it is no longer used.
+ *
+ * We want to use demand paging as much as possible in order to avoid having to
+ * read the entire file into memory (which could be detrimental to performance
+ * for large files). This means that if we're on windows we'll use
+ * `MapViewOfFile`, on POSIX systems that have access to `mmap` we'll use
+ * `mmap`, and on other POSIX systems we'll use `read`.
+ */
+PRISM_EXPORTED_FUNCTION bool
+pm_string_mapped_init(pm_string_t *string, const char *filepath) {
+#ifdef _WIN32
+ // Open the file for reading.
+ HANDLE file = CreateFile(filepath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (file == INVALID_HANDLE_VALUE) {
+ return false;
+ }
+
+ // Get the file size.
+ DWORD file_size = GetFileSize(file, NULL);
+ if (file_size == INVALID_FILE_SIZE) {
+ CloseHandle(file);
+ return false;
+ }
+
+ // If the file is empty, then we don't need to do anything else, we'll set
+ // the source to a constant empty string and return.
+ if (file_size == 0) {
+ CloseHandle(file);
+ const uint8_t source[] = "";
+ *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 };
+ return true;
+ }
+
+ // Create a mapping of the file.
+ HANDLE mapping = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL);
+ if (mapping == NULL) {
+ CloseHandle(file);
+ return false;
+ }
+
+ // Map the file into memory.
+ uint8_t *source = (uint8_t *) MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
+ CloseHandle(mapping);
+ CloseHandle(file);
+
+ if (source == NULL) {
+ return false;
+ }
+
+ *string = (pm_string_t) { .type = PM_STRING_MAPPED, .source = source, .length = (size_t) file_size };
+ return true;
+#elif defined(_POSIX_MAPPED_FILES)
+ // Open the file for reading
+ int fd = open(filepath, O_RDONLY);
+ if (fd == -1) {
+ return false;
+ }
+
+ // Stat the file to get the file size
+ struct stat sb;
+ if (fstat(fd, &sb) == -1) {
+ close(fd);
+ return false;
+ }
+
+ // mmap the file descriptor to virtually get the contents
+ size_t size = (size_t) sb.st_size;
+ uint8_t *source = NULL;
+
+ if (size == 0) {
+ close(fd);
+ const uint8_t source[] = "";
+ *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 };
+ return true;
+ }
+
+ source = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (source == MAP_FAILED) {
+ return false;
+ }
+
+ close(fd);
+ *string = (pm_string_t) { .type = PM_STRING_MAPPED, .source = source, .length = size };
+ return true;
+#else
+ (void) string;
+ (void) filepath;
+ perror("pm_string_mapped_init is not implemented for this platform");
+ return false;
+#endif
+}
+
+/**
+ * Read the file indicated by the filepath parameter into source and load its
+ * contents and size into the given `pm_string_t`. The given `pm_string_t`
+ * should be freed using `pm_string_free` when it is no longer used.
+ */
+PRISM_EXPORTED_FUNCTION bool
+pm_string_file_init(pm_string_t *string, const char *filepath) {
+#ifdef _WIN32
+ // Open the file for reading.
+ HANDLE file = CreateFile(filepath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (file == INVALID_HANDLE_VALUE) {
+ return false;
+ }
+
+ // Get the file size.
+ DWORD file_size = GetFileSize(file, NULL);
+ if (file_size == INVALID_FILE_SIZE) {
+ CloseHandle(file);
+ return false;
+ }
+
+ // If the file is empty, then we don't need to do anything else, we'll set
+ // the source to a constant empty string and return.
+ if (file_size == 0) {
+ CloseHandle(file);
+ const uint8_t source[] = "";
+ *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 };
+ return true;
+ }
+
+ // Create a buffer to read the file into.
+ uint8_t *source = xmalloc(file_size);
+ if (source == NULL) {
+ CloseHandle(file);
+ return false;
+ }
+
+ // Read the contents of the file
+ DWORD bytes_read;
+ if (!ReadFile(file, source, file_size, &bytes_read, NULL)) {
+ CloseHandle(file);
+ return false;
+ }
+
+ // Check the number of bytes read
+ if (bytes_read != file_size) {
+ xfree(source);
+ CloseHandle(file);
+ return false;
+ }
+
+ CloseHandle(file);
+ *string = (pm_string_t) { .type = PM_STRING_OWNED, .source = source, .length = (size_t) file_size };
+ return true;
+#elif defined(_POSIX_MAPPED_FILES)
+ FILE *file = fopen(filepath, "rb");
+ if (file == NULL) {
+ return false;
+ }
+
+ fseek(file, 0, SEEK_END);
+ long file_size = ftell(file);
+
+ if (file_size == -1) {
+ fclose(file);
+ return false;
+ }
+
+ if (file_size == 0) {
+ fclose(file);
+ const uint8_t source[] = "";
+ *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 };
+ return true;
+ }
+
+ size_t length = (size_t) file_size;
+ uint8_t *source = xmalloc(length);
+ if (source == NULL) {
+ fclose(file);
+ return false;
+ }
+
+ fseek(file, 0, SEEK_SET);
+ size_t bytes_read = fread(source, length, 1, file);
+ fclose(file);
+
+ if (bytes_read != 1) {
+ xfree(source);
+ return false;
+ }
+
+ *string = (pm_string_t) { .type = PM_STRING_OWNED, .source = source, .length = length };
+ return true;
+#else
+ (void) string;
+ (void) filepath;
+ perror("pm_string_file_init is not implemented for this platform");
+ return false;
+#endif
+}
+
+/**
+ * Returns the memory size associated with the string.
+ */
+size_t
+pm_string_memsize(const pm_string_t *string) {
+ size_t size = sizeof(pm_string_t);
+ if (string->type == PM_STRING_OWNED) {
+ size += string->length;
+ }
+ return size;
+}
+
+/**
+ * Ensure the string is owned. If it is not, then reinitialize it as owned and
+ * copy over the previous source.
+ */
+void
+pm_string_ensure_owned(pm_string_t *string) {
+ if (string->type == PM_STRING_OWNED) return;
+
+ size_t length = pm_string_length(string);
+ const uint8_t *source = pm_string_source(string);
+
+ uint8_t *memory = xmalloc(length);
+ if (!memory) return;
+
+ pm_string_owned_init(string, memory, length);
+ memcpy((void *) string->source, source, length);
+}
+
+/**
+ * Compare the underlying lengths and bytes of two strings. Returns 0 if the
+ * strings are equal, a negative number if the left string is less than the
+ * right string, and a positive number if the left string is greater than the
+ * right string.
+ */
+int
+pm_string_compare(const pm_string_t *left, const pm_string_t *right) {
+ size_t left_length = pm_string_length(left);
+ size_t right_length = pm_string_length(right);
+
+ if (left_length < right_length) {
+ return -1;
+ } else if (left_length > right_length) {
+ return 1;
+ }
+
+ return memcmp(pm_string_source(left), pm_string_source(right), left_length);
+}
+
+/**
+ * Returns the length associated with the string.
+ */
+PRISM_EXPORTED_FUNCTION size_t
+pm_string_length(const pm_string_t *string) {
+ return string->length;
+}
+
+/**
+ * Returns the start pointer associated with the string.
+ */
+PRISM_EXPORTED_FUNCTION const uint8_t *
+pm_string_source(const pm_string_t *string) {
+ return string->source;
+}
+
+/**
+ * Free the associated memory of the given string.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_string_free(pm_string_t *string) {
+ void *memory = (void *) string->source;
+
+ if (string->type == PM_STRING_OWNED) {
+ xfree(memory);
+#ifdef PRISM_HAS_MMAP
+ } else if (string->type == PM_STRING_MAPPED && string->length) {
+#if defined(_WIN32)
+ UnmapViewOfFile(memory);
+#elif defined(_POSIX_MAPPED_FILES)
+ munmap(memory, string->length);
+#endif
+#endif /* PRISM_HAS_MMAP */
+ }
+}
diff --git a/prism/util/pm_string.h b/prism/util/pm_string.h
new file mode 100644
index 0000000000..a68e2a7c91
--- /dev/null
+++ b/prism/util/pm_string.h
@@ -0,0 +1,174 @@
+/**
+ * @file pm_string.h
+ *
+ * A generic string type that can have various ownership semantics.
+ */
+#ifndef PRISM_STRING_H
+#define PRISM_STRING_H
+
+#include "prism/defines.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+// The following headers are necessary to read files using demand paging.
+#ifdef _WIN32
+#include <windows.h>
+#elif defined(_POSIX_MAPPED_FILES)
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#endif
+
+/**
+ * A generic string type that can have various ownership semantics.
+ */
+typedef struct {
+ /** A pointer to the start of the string. */
+ const uint8_t *source;
+
+ /** The length of the string in bytes of memory. */
+ size_t length;
+
+ /** The type of the string. This field determines how the string should be freed. */
+ enum {
+ /** This string is a constant string, and should not be freed. */
+ PM_STRING_CONSTANT,
+
+ /** This is a slice of another string, and should not be freed. */
+ PM_STRING_SHARED,
+
+ /** This string owns its memory, and should be freed using `pm_string_free`. */
+ PM_STRING_OWNED,
+
+#ifdef PRISM_HAS_MMAP
+ /** This string is a memory-mapped file, and should be freed using `pm_string_free`. */
+ PM_STRING_MAPPED
+#endif
+ } type;
+} pm_string_t;
+
+/**
+ * Returns the size of the pm_string_t struct. This is necessary to allocate the
+ * correct amount of memory in the FFI backend.
+ *
+ * @return The size of the pm_string_t struct.
+ */
+PRISM_EXPORTED_FUNCTION size_t pm_string_sizeof(void);
+
+/**
+ * Defines an empty string. This is useful for initializing a string that will
+ * be filled in later.
+ */
+#define PM_STRING_EMPTY ((pm_string_t) { .type = PM_STRING_CONSTANT, .source = NULL, .length = 0 })
+
+/**
+ * Initialize a shared string that is based on initial input.
+ *
+ * @param string The string to initialize.
+ * @param start The start of the string.
+ * @param end The end of the string.
+ */
+void pm_string_shared_init(pm_string_t *string, const uint8_t *start, const uint8_t *end);
+
+/**
+ * Initialize an owned string that is responsible for freeing allocated memory.
+ *
+ * @param string The string to initialize.
+ * @param source The source of the string.
+ * @param length The length of the string.
+ */
+void pm_string_owned_init(pm_string_t *string, uint8_t *source, size_t length);
+
+/**
+ * Initialize a constant string that doesn't own its memory source.
+ *
+ * @param string The string to initialize.
+ * @param source The source of the string.
+ * @param length The length of the string.
+ */
+void pm_string_constant_init(pm_string_t *string, const char *source, size_t length);
+
+/**
+ * Read the file indicated by the filepath parameter into source and load its
+ * contents and size into the given `pm_string_t`. The given `pm_string_t`
+ * should be freed using `pm_string_free` when it is no longer used.
+ *
+ * We want to use demand paging as much as possible in order to avoid having to
+ * read the entire file into memory (which could be detrimental to performance
+ * for large files). This means that if we're on windows we'll use
+ * `MapViewOfFile`, on POSIX systems that have access to `mmap` we'll use
+ * `mmap`, and on other POSIX systems we'll use `read`.
+ *
+ * @param string The string to initialize.
+ * @param filepath The filepath to read.
+ * @return Whether or not the file was successfully mapped.
+ */
+PRISM_EXPORTED_FUNCTION bool pm_string_mapped_init(pm_string_t *string, const char *filepath);
+
+/**
+ * Read the file indicated by the filepath parameter into source and load its
+ * contents and size into the given `pm_string_t`. The given `pm_string_t`
+ * should be freed using `pm_string_free` when it is no longer used.
+ *
+ * @param string The string to initialize.
+ * @param filepath The filepath to read.
+ * @return Whether or not the file was successfully read.
+ */
+PRISM_EXPORTED_FUNCTION bool pm_string_file_init(pm_string_t *string, const char *filepath);
+
+/**
+ * Returns the memory size associated with the string.
+ *
+ * @param string The string to get the memory size of.
+ * @return The size of the memory associated with the string.
+ */
+size_t pm_string_memsize(const pm_string_t *string);
+
+/**
+ * Ensure the string is owned. If it is not, then reinitialize it as owned and
+ * copy over the previous source.
+ *
+ * @param string The string to ensure is owned.
+ */
+void pm_string_ensure_owned(pm_string_t *string);
+
+/**
+ * Compare the underlying lengths and bytes of two strings. Returns 0 if the
+ * strings are equal, a negative number if the left string is less than the
+ * right string, and a positive number if the left string is greater than the
+ * right string.
+ *
+ * @param left The left string to compare.
+ * @param right The right string to compare.
+ * @return The comparison result.
+ */
+int pm_string_compare(const pm_string_t *left, const pm_string_t *right);
+
+/**
+ * Returns the length associated with the string.
+ *
+ * @param string The string to get the length of.
+ * @return The length of the string.
+ */
+PRISM_EXPORTED_FUNCTION size_t pm_string_length(const pm_string_t *string);
+
+/**
+ * Returns the start pointer associated with the string.
+ *
+ * @param string The string to get the start pointer of.
+ * @return The start pointer of the string.
+ */
+PRISM_EXPORTED_FUNCTION const uint8_t * pm_string_source(const pm_string_t *string);
+
+/**
+ * Free the associated memory of the given string.
+ *
+ * @param string The string to free.
+ */
+PRISM_EXPORTED_FUNCTION void pm_string_free(pm_string_t *string);
+
+#endif
diff --git a/prism/util/pm_string_list.c b/prism/util/pm_string_list.c
new file mode 100644
index 0000000000..f6c2145987
--- /dev/null
+++ b/prism/util/pm_string_list.c
@@ -0,0 +1,28 @@
+#include "prism/util/pm_string_list.h"
+
+/**
+ * Append a pm_string_t to the given string list.
+ */
+void
+pm_string_list_append(pm_string_list_t *string_list, pm_string_t *string) {
+ if (string_list->length + 1 > string_list->capacity) {
+ if (string_list->capacity == 0) {
+ string_list->capacity = 1;
+ } else {
+ string_list->capacity *= 2;
+ }
+
+ string_list->strings = xrealloc(string_list->strings, string_list->capacity * sizeof(pm_string_t));
+ if (string_list->strings == NULL) abort();
+ }
+
+ string_list->strings[string_list->length++] = *string;
+}
+
+/**
+ * Free the memory associated with the string list
+ */
+void
+pm_string_list_free(pm_string_list_t *string_list) {
+ xfree(string_list->strings);
+}
diff --git a/prism/util/pm_string_list.h b/prism/util/pm_string_list.h
new file mode 100644
index 0000000000..0d406cc5d8
--- /dev/null
+++ b/prism/util/pm_string_list.h
@@ -0,0 +1,44 @@
+/**
+ * @file pm_string_list.h
+ *
+ * A list of strings.
+ */
+#ifndef PRISM_STRING_LIST_H
+#define PRISM_STRING_LIST_H
+
+#include "prism/defines.h"
+#include "prism/util/pm_string.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+/**
+ * A list of strings.
+ */
+typedef struct {
+ /** The length of the string list. */
+ size_t length;
+
+ /** The capacity of the string list that has been allocated. */
+ size_t capacity;
+
+ /** A pointer to the start of the string list. */
+ pm_string_t *strings;
+} pm_string_list_t;
+
+/**
+ * Append a pm_string_t to the given string list.
+ *
+ * @param string_list The string list to append to.
+ * @param string The string to append.
+ */
+void pm_string_list_append(pm_string_list_t *string_list, pm_string_t *string);
+
+/**
+ * Free the memory associated with the string list.
+ *
+ * @param string_list The string list to free.
+ */
+PRISM_EXPORTED_FUNCTION void pm_string_list_free(pm_string_list_t *string_list);
+
+#endif
diff --git a/prism/util/pm_strncasecmp.c b/prism/util/pm_strncasecmp.c
new file mode 100644
index 0000000000..2240bf8110
--- /dev/null
+++ b/prism/util/pm_strncasecmp.c
@@ -0,0 +1,24 @@
+#include "prism/util/pm_strncasecmp.h"
+
+/**
+ * Compare two strings, ignoring case, up to the given length. Returns 0 if the
+ * strings are equal, a negative number if string1 is less than string2, or a
+ * positive number if string1 is greater than string2.
+ *
+ * Note that this is effectively our own implementation of strncasecmp, but it's
+ * not available on all of the platforms we want to support so we're rolling it
+ * here.
+ */
+int
+pm_strncasecmp(const uint8_t *string1, const uint8_t *string2, size_t length) {
+ size_t offset = 0;
+ int difference = 0;
+
+ while (offset < length && string1[offset] != '\0') {
+ if (string2[offset] == '\0') return string1[offset];
+ if ((difference = tolower(string1[offset]) - tolower(string2[offset])) != 0) return difference;
+ offset++;
+ }
+
+ return difference;
+}
diff --git a/prism/util/pm_strncasecmp.h b/prism/util/pm_strncasecmp.h
new file mode 100644
index 0000000000..5cb88cb5eb
--- /dev/null
+++ b/prism/util/pm_strncasecmp.h
@@ -0,0 +1,32 @@
+/**
+ * @file pm_strncasecmp.h
+ *
+ * A custom strncasecmp implementation.
+ */
+#ifndef PRISM_STRNCASECMP_H
+#define PRISM_STRNCASECMP_H
+
+#include "prism/defines.h"
+
+#include <ctype.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/**
+ * Compare two strings, ignoring case, up to the given length. Returns 0 if the
+ * strings are equal, a negative number if string1 is less than string2, or a
+ * positive number if string1 is greater than string2.
+ *
+ * Note that this is effectively our own implementation of strncasecmp, but it's
+ * not available on all of the platforms we want to support so we're rolling it
+ * here.
+ *
+ * @param string1 The first string to compare.
+ * @param string2 The second string to compare
+ * @param length The maximum number of characters to compare.
+ * @return 0 if the strings are equal, a negative number if string1 is less than
+ * string2, or a positive number if string1 is greater than string2.
+ */
+int pm_strncasecmp(const uint8_t *string1, const uint8_t *string2, size_t length);
+
+#endif
diff --git a/prism/util/pm_strpbrk.c b/prism/util/pm_strpbrk.c
new file mode 100644
index 0000000000..6c8dea1836
--- /dev/null
+++ b/prism/util/pm_strpbrk.c
@@ -0,0 +1,180 @@
+#include "prism/util/pm_strpbrk.h"
+
+/**
+ * Add an invalid multibyte character error to the parser.
+ */
+static inline void
+pm_strpbrk_invalid_multibyte_character(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
+ pm_diagnostic_list_append_format(&parser->error_list, start, end, PM_ERR_INVALID_MULTIBYTE_CHARACTER, *start);
+}
+
+/**
+ * This is the default path.
+ */
+static inline const uint8_t *
+pm_strpbrk_utf8(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t maximum, bool validate) {
+ size_t index = 0;
+
+ while (index < maximum) {
+ if (strchr((const char *) charset, source[index]) != NULL) {
+ return source + index;
+ }
+
+ if (source[index] < 0x80) {
+ index++;
+ } else {
+ size_t width = pm_encoding_utf_8_char_width(source + index, (ptrdiff_t) (maximum - index));
+
+ if (width > 0) {
+ index += width;
+ } else if (!validate) {
+ index++;
+ } else {
+ // At this point we know we have an invalid multibyte character.
+ // We'll walk forward as far as we can until we find the next
+ // valid character so that we don't spam the user with a ton of
+ // the same kind of error.
+ const size_t start = index;
+
+ do {
+ index++;
+ } while (index < maximum && pm_encoding_utf_8_char_width(source + index, (ptrdiff_t) (maximum - index)) == 0);
+
+ pm_strpbrk_invalid_multibyte_character(parser, source + start, source + index);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * This is the path when the encoding is ASCII-8BIT.
+ */
+static inline const uint8_t *
+pm_strpbrk_ascii_8bit(const uint8_t *source, const uint8_t *charset, size_t maximum) {
+ size_t index = 0;
+
+ while (index < maximum) {
+ if (strchr((const char *) charset, source[index]) != NULL) {
+ return source + index;
+ }
+
+ index++;
+ }
+
+ return NULL;
+}
+
+/**
+ * This is the slow path that does care about the encoding.
+ */
+static inline const uint8_t *
+pm_strpbrk_multi_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t maximum, bool validate) {
+ size_t index = 0;
+
+ while (index < maximum) {
+ if (strchr((const char *) charset, source[index]) != NULL) {
+ return source + index;
+ }
+
+ if (source[index] < 0x80) {
+ index++;
+ } else {
+ size_t width = parser->encoding->char_width(source + index, (ptrdiff_t) (maximum - index));
+
+ if (width > 0) {
+ index += width;
+ } else if (!validate) {
+ index++;
+ } else {
+ // At this point we know we have an invalid multibyte character.
+ // We'll walk forward as far as we can until we find the next
+ // valid character so that we don't spam the user with a ton of
+ // the same kind of error.
+ const size_t start = index;
+
+ do {
+ index++;
+ } while (index < maximum && parser->encoding->char_width(source + index, (ptrdiff_t) (maximum - index)) == 0);
+
+ pm_strpbrk_invalid_multibyte_character(parser, source + start, source + index);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * This is the fast path that does not care about the encoding because we know
+ * the encoding only supports single-byte characters.
+ */
+static inline const uint8_t *
+pm_strpbrk_single_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t maximum, bool validate) {
+ size_t index = 0;
+
+ while (index < maximum) {
+ if (strchr((const char *) charset, source[index]) != NULL) {
+ return source + index;
+ }
+
+ if (source[index] < 0x80 || !validate) {
+ index++;
+ } else {
+ size_t width = parser->encoding->char_width(source + index, (ptrdiff_t) (maximum - index));
+
+ if (width > 0) {
+ index += width;
+ } else {
+ // At this point we know we have an invalid multibyte character.
+ // We'll walk forward as far as we can until we find the next
+ // valid character so that we don't spam the user with a ton of
+ // the same kind of error.
+ const size_t start = index;
+
+ do {
+ index++;
+ } while (index < maximum && parser->encoding->char_width(source + index, (ptrdiff_t) (maximum - index)) == 0);
+
+ pm_strpbrk_invalid_multibyte_character(parser, source + start, source + index);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Here we have rolled our own version of strpbrk. The standard library strpbrk
+ * has undefined behavior when the source string is not null-terminated. We want
+ * to support strings that are not null-terminated because pm_parse does not
+ * have the contract that the string is null-terminated. (This is desirable
+ * because it means the extension can call pm_parse with the result of a call to
+ * mmap).
+ *
+ * The standard library strpbrk also does not support passing a maximum length
+ * to search. We want to support this for the reason mentioned above, but we
+ * also don't want it to stop on null bytes. Ruby actually allows null bytes
+ * within strings, comments, regular expressions, etc. So we need to be able to
+ * skip past them.
+ *
+ * Finally, we want to support encodings wherein the charset could contain
+ * characters that are trailing bytes of multi-byte characters. For example, in
+ * Shift_JIS, the backslash character can be a trailing byte. In that case we
+ * need to take a slower path and iterate one multi-byte character at a time.
+ */
+const uint8_t *
+pm_strpbrk(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, ptrdiff_t length, bool validate) {
+ if (length <= 0) {
+ return NULL;
+ } else if (!parser->encoding_changed) {
+ return pm_strpbrk_utf8(parser, source, charset, (size_t) length, validate);
+ } else if (parser->encoding == PM_ENCODING_ASCII_8BIT_ENTRY) {
+ return pm_strpbrk_ascii_8bit(source, charset, (size_t) length);
+ } else if (parser->encoding->multibyte) {
+ return pm_strpbrk_multi_byte(parser, source, charset, (size_t) length, validate);
+ } else {
+ return pm_strpbrk_single_byte(parser, source, charset, (size_t) length, validate);
+ }
+}
diff --git a/prism/util/pm_strpbrk.h b/prism/util/pm_strpbrk.h
new file mode 100644
index 0000000000..f387bd5782
--- /dev/null
+++ b/prism/util/pm_strpbrk.h
@@ -0,0 +1,46 @@
+/**
+ * @file pm_strpbrk.h
+ *
+ * A custom strpbrk implementation.
+ */
+#ifndef PRISM_STRPBRK_H
+#define PRISM_STRPBRK_H
+
+#include "prism/defines.h"
+#include "prism/diagnostic.h"
+#include "prism/parser.h"
+
+#include <stddef.h>
+#include <string.h>
+
+/**
+ * Here we have rolled our own version of strpbrk. The standard library strpbrk
+ * has undefined behavior when the source string is not null-terminated. We want
+ * to support strings that are not null-terminated because pm_parse does not
+ * have the contract that the string is null-terminated. (This is desirable
+ * because it means the extension can call pm_parse with the result of a call to
+ * mmap).
+ *
+ * The standard library strpbrk also does not support passing a maximum length
+ * to search. We want to support this for the reason mentioned above, but we
+ * also don't want it to stop on null bytes. Ruby actually allows null bytes
+ * within strings, comments, regular expressions, etc. So we need to be able to
+ * skip past them.
+ *
+ * Finally, we want to support encodings wherein the charset could contain
+ * characters that are trailing bytes of multi-byte characters. For example, in
+ * Shift-JIS, the backslash character can be a trailing byte. In that case we
+ * need to take a slower path and iterate one multi-byte character at a time.
+ *
+ * @param parser The parser.
+ * @param source The source to search.
+ * @param charset The charset to search for.
+ * @param length The maximum number of bytes to search.
+ * @param validate Whether to validate that the source string is valid in the
+ * current encoding of the parser.
+ * @return A pointer to the first character in the source string that is in the
+ * charset, or NULL if no such character exists.
+ */
+const uint8_t * pm_strpbrk(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, ptrdiff_t length, bool validate);
+
+#endif
diff --git a/prism/version.h b/prism/version.h
new file mode 100644
index 0000000000..0d25b2883c
--- /dev/null
+++ b/prism/version.h
@@ -0,0 +1,29 @@
+/**
+ * @file version.h
+ *
+ * The version of the Prism library.
+ */
+#ifndef PRISM_VERSION_H
+#define PRISM_VERSION_H
+
+/**
+ * The major version of the Prism library as an int.
+ */
+#define PRISM_VERSION_MAJOR 0
+
+/**
+ * The minor version of the Prism library as an int.
+ */
+#define PRISM_VERSION_MINOR 27
+
+/**
+ * The patch version of the Prism library as an int.
+ */
+#define PRISM_VERSION_PATCH 0
+
+/**
+ * The version of the Prism library as a constant string.
+ */
+#define PRISM_VERSION "0.27.0"
+
+#endif
diff --git a/prism_compile.c b/prism_compile.c
new file mode 100644
index 0000000000..909e6b26da
--- /dev/null
+++ b/prism_compile.c
@@ -0,0 +1,9137 @@
+#include "prism.h"
+
+/******************************************************************************/
+/* These macros operate on pm_line_column_t structs as opposed to NODE*s. */
+/******************************************************************************/
+
+#define PUSH_ADJUST(seq, location, label) \
+ ADD_ELEM((seq), (LINK_ELEMENT *) new_adjust_body(iseq, (label), (int) (location).line))
+
+#define PUSH_ADJUST_RESTORE(seq, label) \
+ ADD_ELEM((seq), (LINK_ELEMENT *) new_adjust_body(iseq, (label), -1))
+
+#define PUSH_INSN(seq, location, insn) \
+ ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).column, BIN(insn), 0))
+
+#define PUSH_INSN1(seq, location, insn, op1) \
+ ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).column, BIN(insn), 1, (VALUE)(op1)))
+
+#define PUSH_INSN2(seq, location, insn, op1, op2) \
+ ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).column, BIN(insn), 2, (VALUE)(op1), (VALUE)(op2)))
+
+#define PUSH_INSN3(seq, location, insn, op1, op2, op3) \
+ ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).column, BIN(insn), 3, (VALUE)(op1), (VALUE)(op2), (VALUE)(op3)))
+
+#define PUSH_INSNL(seq, location, insn, label) \
+ (PUSH_INSN1(seq, location, insn, label), LABEL_REF(label))
+
+#define PUSH_LABEL(seq, label) \
+ ADD_ELEM((seq), (LINK_ELEMENT *) (label))
+
+#define PUSH_SEND_R(seq, location, id, argc, block, flag, keywords) \
+ ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_send(iseq, (int) (location).line, (int) (location).column, (id), (VALUE)(argc), (block), (VALUE)(flag), (keywords)))
+
+#define PUSH_SEND(seq, location, id, argc) \
+ PUSH_SEND_R((seq), location, (id), (argc), NULL, (VALUE)INT2FIX(0), NULL)
+
+#define PUSH_SEND_WITH_FLAG(seq, location, id, argc, flag) \
+ PUSH_SEND_R((seq), location, (id), (argc), NULL, (VALUE)(flag), NULL)
+
+#define PUSH_SEND_WITH_BLOCK(seq, location, id, argc, block) \
+ PUSH_SEND_R((seq), location, (id), (argc), (block), (VALUE)INT2FIX(0), NULL)
+
+#define PUSH_CALL(seq, location, id, argc) \
+ PUSH_SEND_R((seq), location, (id), (argc), NULL, (VALUE)INT2FIX(VM_CALL_FCALL), NULL)
+
+#define PUSH_CALL_WITH_BLOCK(seq, location, id, argc, block) \
+ PUSH_SEND_R((seq), location, (id), (argc), (block), (VALUE)INT2FIX(VM_CALL_FCALL), NULL)
+
+#define PUSH_TRACE(seq, event) \
+ ADD_ELEM((seq), (LINK_ELEMENT *) new_trace_body(iseq, (event), 0))
+
+#define PUSH_CATCH_ENTRY(type, ls, le, iseqv, lc) \
+ ADD_CATCH_ENTRY((type), (ls), (le), (iseqv), (lc))
+
+#define PUSH_SEQ(seq1, seq2) \
+ APPEND_LIST((seq1), (seq2))
+
+#define PUSH_SYNTHETIC_PUTNIL(seq, iseq) \
+ do { \
+ int lineno = ISEQ_COMPILE_DATA(iseq)->last_line; \
+ if (lineno == 0) lineno = FIX2INT(rb_iseq_first_lineno(iseq)); \
+ ADD_SYNTHETIC_INSN(seq, lineno, -1, putnil); \
+ } while (0)
+
+/******************************************************************************/
+/* These functions compile getlocal/setlocal instructions but operate on */
+/* prism locations instead of NODEs. */
+/******************************************************************************/
+
+static void
+pm_iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line_no, int column, int idx, int level)
+{
+ if (iseq_local_block_param_p(iseq, idx, level)) {
+ ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line_no, column, BIN(getblockparam), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
+ }
+ else {
+ ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line_no, column, BIN(getlocal), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
+ }
+ if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qfalse);
+}
+
+static void
+pm_iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line_no, int column, int idx, int level)
+{
+ if (iseq_local_block_param_p(iseq, idx, level)) {
+ ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line_no, column, BIN(setblockparam), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
+ }
+ else {
+ ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line_no, column, BIN(setlocal), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
+ }
+ if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qtrue);
+}
+
+#define PUSH_GETLOCAL(seq, location, idx, level) \
+ pm_iseq_add_getlocal(iseq, (seq), (int) (location).line, (int) (location).column, (idx), (level))
+
+#define PUSH_SETLOCAL(seq, location, idx, level) \
+ pm_iseq_add_setlocal(iseq, (seq), (int) (location).line, (int) (location).column, (idx), (level))
+
+/******************************************************************************/
+/* These are helper macros for the compiler. */
+/******************************************************************************/
+
+#define OLD_ISEQ NEW_ISEQ
+#undef NEW_ISEQ
+
+#define NEW_ISEQ(node, name, type, line_no) \
+ pm_new_child_iseq(iseq, (node), rb_fstring(name), 0, (type), (line_no))
+
+#define OLD_CHILD_ISEQ NEW_CHILD_ISEQ
+#undef NEW_CHILD_ISEQ
+
+#define NEW_CHILD_ISEQ(node, name, type, line_no) \
+ pm_new_child_iseq(iseq, (node), rb_fstring(name), iseq, (type), (line_no))
+
+#define PM_COMPILE(node) \
+ pm_compile_node(iseq, (node), ret, popped, scope_node)
+
+#define PM_COMPILE_INTO_ANCHOR(_ret, node) \
+ pm_compile_node(iseq, (node), _ret, popped, scope_node)
+
+#define PM_COMPILE_POPPED(node) \
+ pm_compile_node(iseq, (node), ret, true, scope_node)
+
+#define PM_COMPILE_NOT_POPPED(node) \
+ pm_compile_node(iseq, (node), ret, false, scope_node)
+
+#define PM_SPECIAL_CONSTANT_FLAG ((pm_constant_id_t)(1 << 31))
+#define PM_CONSTANT_AND ((pm_constant_id_t)(idAnd | PM_SPECIAL_CONSTANT_FLAG))
+#define PM_CONSTANT_DOT3 ((pm_constant_id_t)(idDot3 | PM_SPECIAL_CONSTANT_FLAG))
+#define PM_CONSTANT_MULT ((pm_constant_id_t)(idMULT | PM_SPECIAL_CONSTANT_FLAG))
+#define PM_CONSTANT_POW ((pm_constant_id_t)(idPow | PM_SPECIAL_CONSTANT_FLAG))
+
+#define PM_NODE_START_LINE_COLUMN(parser, node) \
+ pm_newline_list_line_column(&(parser)->newline_list, ((const pm_node_t *) (node))->location.start, (parser)->start_line)
+
+#define PM_NODE_END_LINE_COLUMN(parser, node) \
+ pm_newline_list_line_column(&(parser)->newline_list, ((const pm_node_t *) (node))->location.end, (parser)->start_line)
+
+#define PM_LOCATION_START_LINE_COLUMN(parser, location) \
+ pm_newline_list_line_column(&(parser)->newline_list, (location)->start, (parser)->start_line)
+
+static int
+pm_node_line_number(const pm_parser_t *parser, const pm_node_t *node)
+{
+ return (int) PM_NODE_START_LINE_COLUMN(parser, node).line;
+}
+
+static int
+pm_location_line_number(const pm_parser_t *parser, const pm_location_t *location) {
+ return (int) PM_LOCATION_START_LINE_COLUMN(parser, location).line;
+}
+
+/**
+ * Convert the value of an integer node into a Ruby Integer.
+ */
+static VALUE
+parse_integer(const pm_integer_node_t *node)
+{
+ const pm_integer_t *integer = &node->value;
+ VALUE result;
+
+ if (integer->values == NULL) {
+ result = UINT2NUM(integer->value);
+ }
+ else {
+ VALUE string = rb_str_new(NULL, integer->length * 8);
+ unsigned char *bytes = (unsigned char *) RSTRING_PTR(string);
+
+ size_t offset = integer->length * 8;
+ for (size_t value_index = 0; value_index < integer->length; value_index++) {
+ uint32_t value = integer->values[value_index];
+
+ for (int index = 0; index < 8; index++) {
+ int byte = (value >> (4 * index)) & 0xf;
+ bytes[--offset] = byte < 10 ? byte + '0' : byte - 10 + 'a';
+ }
+ }
+
+ result = rb_funcall(string, rb_intern("to_i"), 1, UINT2NUM(16));
+ }
+
+ if (integer->negative) {
+ result = rb_funcall(result, rb_intern("-@"), 0);
+ }
+
+ return result;
+}
+
+/**
+ * Convert the value of a float node into a Ruby Float.
+ */
+static VALUE
+parse_float(const pm_float_node_t *node)
+{
+ return DBL2NUM(node->value);
+}
+
+/**
+ * Convert the value of a rational node into a Ruby Rational. Rational nodes can
+ * either be wrapping an integer node or a float node. If it's an integer node,
+ * we can reuse our parsing. If it's not, then we'll parse the numerator and
+ * then parse the denominator and create the rational from those two values.
+ */
+static VALUE
+parse_rational(const pm_rational_node_t *node)
+{
+ VALUE result;
+
+ if (PM_NODE_TYPE_P(node->numeric, PM_FLOAT_NODE)) {
+ const uint8_t *start = node->base.location.start;
+ const uint8_t *end = node->base.location.end - 1;
+ size_t length = end - start;
+
+ char *buffer = malloc(length + 1);
+ memcpy(buffer, start, length);
+
+ buffer[length] = '\0';
+
+ char *decimal = memchr(buffer, '.', length);
+ RUBY_ASSERT(decimal);
+ size_t seen_decimal = decimal - buffer;
+ size_t fraclen = length - seen_decimal - 1;
+ memmove(decimal, decimal + 1, fraclen + 1);
+
+ VALUE numerator = rb_cstr_to_inum(buffer, 10, false);
+ result = rb_rational_new(numerator, rb_int_positive_pow(10, fraclen));
+
+ free(buffer);
+ }
+ else {
+ RUBY_ASSERT(PM_NODE_TYPE_P(node->numeric, PM_INTEGER_NODE));
+ VALUE numerator = parse_integer((const pm_integer_node_t *) node->numeric);
+ result = rb_rational_raw(numerator, INT2FIX(1));
+ }
+
+ return result;
+}
+
+/**
+ * Convert the value of an imaginary node into a Ruby Complex. Imaginary nodes
+ * can be wrapping an integer node, a float node, or a rational node. In all
+ * cases we will reuse parsing functions seen above to get the inner value, and
+ * then convert into an imaginary with rb_complex_raw.
+ */
+static VALUE
+parse_imaginary(const pm_imaginary_node_t *node)
+{
+ VALUE imaginary_part;
+ switch (PM_NODE_TYPE(node->numeric)) {
+ case PM_FLOAT_NODE: {
+ imaginary_part = parse_float((const pm_float_node_t *) node->numeric);
+ break;
+ }
+ case PM_INTEGER_NODE: {
+ imaginary_part = parse_integer((const pm_integer_node_t *) node->numeric);
+ break;
+ }
+ case PM_RATIONAL_NODE: {
+ imaginary_part = parse_rational((const pm_rational_node_t *) node->numeric);
+ break;
+ }
+ default:
+ rb_bug("Unexpected numeric type on imaginary number %s\n", pm_node_type_to_str(PM_NODE_TYPE(node->numeric)));
+ }
+
+ return rb_complex_raw(INT2FIX(0), imaginary_part);
+}
+
+static inline VALUE
+parse_string(const pm_scope_node_t *scope_node, const pm_string_t *string)
+{
+ return rb_enc_str_new((const char *) pm_string_source(string), pm_string_length(string), scope_node->encoding);
+}
+
+/**
+ * Certain strings can have their encoding differ from the parser's encoding due
+ * to bytes or escape sequences that have the top bit set. This function handles
+ * creating those strings based on the flags set on the owning node.
+ */
+static inline VALUE
+parse_string_encoded(const pm_scope_node_t *scope_node, const pm_node_t *node, const pm_string_t *string)
+{
+ rb_encoding *encoding;
+
+ if (node->flags & PM_ENCODING_FLAGS_FORCED_BINARY_ENCODING) {
+ encoding = rb_ascii8bit_encoding();
+ }
+ else if (node->flags & PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING) {
+ encoding = rb_utf8_encoding();
+ }
+ else {
+ encoding = scope_node->encoding;
+ }
+
+ return rb_enc_str_new((const char *) pm_string_source(string), pm_string_length(string), encoding);
+}
+
+static inline VALUE
+parse_static_literal_string(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_node_t *node, const pm_string_t *string)
+{
+ rb_encoding *encoding;
+
+ if (node->flags & PM_ENCODING_FLAGS_FORCED_BINARY_ENCODING) {
+ encoding = rb_ascii8bit_encoding();
+ }
+ else if (node->flags & PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING) {
+ encoding = rb_utf8_encoding();
+ }
+ else {
+ encoding = scope_node->encoding;
+ }
+
+ VALUE value = rb_enc_interned_str((const char *) pm_string_source(string), pm_string_length(string), encoding);
+ rb_enc_str_coderange(value);
+
+ if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
+ int line_number = pm_node_line_number(scope_node->parser, node);
+ VALUE debug_info = rb_ary_new_from_args(2, rb_iseq_path(iseq), INT2FIX(line_number));
+ value = rb_str_dup(value);
+ rb_ivar_set(value, id_debug_created_info, rb_obj_freeze(debug_info));
+ rb_str_freeze(value);
+ }
+
+ return value;
+}
+
+static inline ID
+parse_string_symbol(const pm_scope_node_t *scope_node, const pm_symbol_node_t *symbol)
+{
+ rb_encoding *encoding;
+ if (symbol->base.flags & PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING) {
+ encoding = rb_utf8_encoding();
+ }
+ else if (symbol->base.flags & PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING) {
+ encoding = rb_ascii8bit_encoding();
+ }
+ else if (symbol->base.flags & PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING) {
+ encoding = rb_usascii_encoding();
+ }
+ else {
+ encoding = scope_node->encoding;
+ }
+
+ return rb_intern3((const char *) pm_string_source(&symbol->unescaped), pm_string_length(&symbol->unescaped), encoding);
+}
+
+static int
+pm_optimizable_range_item_p(const pm_node_t *node)
+{
+ return (!node || PM_NODE_TYPE_P(node, PM_INTEGER_NODE) || PM_NODE_TYPE_P(node, PM_NIL_NODE));
+}
+
+static void pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node);
+
+static int
+pm_interpolated_node_compile(rb_iseq_t *iseq, const pm_node_list_t *parts, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
+{
+ int stack_size = 0;
+ size_t parts_size = parts->size;
+ bool interpolated = false;
+
+ if (parts_size > 0) {
+ VALUE current_string = Qnil;
+
+ for (size_t index = 0; index < parts_size; index++) {
+ const pm_node_t *part = parts->nodes[index];
+
+ if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
+ const pm_string_node_t *string_node = (const pm_string_node_t *) part;
+ VALUE string_value = parse_string_encoded(scope_node, (const pm_node_t *) string_node, &string_node->unescaped);
+
+ if (RTEST(current_string)) {
+ current_string = rb_str_concat(current_string, string_value);
+ }
+ else {
+ current_string = string_value;
+ }
+ }
+ else {
+ interpolated = true;
+
+ if (
+ PM_NODE_TYPE_P(part, PM_EMBEDDED_STATEMENTS_NODE) &&
+ ((const pm_embedded_statements_node_t *) part)->statements != NULL &&
+ ((const pm_embedded_statements_node_t *) part)->statements->body.size == 1 &&
+ PM_NODE_TYPE_P(((const pm_embedded_statements_node_t *) part)->statements->body.nodes[0], PM_STRING_NODE)
+ ) {
+ const pm_string_node_t *string_node = (const pm_string_node_t *) ((const pm_embedded_statements_node_t *) part)->statements->body.nodes[0];
+ VALUE string_value = parse_string_encoded(scope_node, (const pm_node_t *) string_node, &string_node->unescaped);
+
+ if (RTEST(current_string)) {
+ current_string = rb_str_concat(current_string, string_value);
+ }
+ else {
+ current_string = string_value;
+ }
+ }
+ else {
+ if (!RTEST(current_string)) {
+ current_string = rb_enc_str_new(NULL, 0, scope_node->encoding);
+ }
+
+ PUSH_INSN1(ret, *node_location, putobject, rb_fstring(current_string));
+ PM_COMPILE_NOT_POPPED(part);
+ PUSH_INSN(ret, *node_location, dup);
+ PUSH_INSN1(ret, *node_location, objtostring, new_callinfo(iseq, idTo_s, 0, VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE , NULL, FALSE));
+ PUSH_INSN(ret, *node_location, anytostring);
+
+ current_string = Qnil;
+ stack_size += 2;
+ }
+ }
+ }
+
+ if (RTEST(current_string)) {
+ current_string = rb_fstring(current_string);
+
+ if (stack_size == 0 && interpolated) {
+ PUSH_INSN1(ret, *node_location, putstring, current_string);
+ }
+ else {
+ PUSH_INSN1(ret, *node_location, putobject, current_string);
+ }
+
+ current_string = Qnil;
+ stack_size++;
+ }
+ }
+ else {
+ PUSH_INSN(ret, *node_location, putnil);
+ }
+
+ return stack_size;
+}
+
+static VALUE
+pm_static_literal_concat(const pm_node_list_t *nodes, const pm_scope_node_t *scope_node, bool top)
+{
+ VALUE current = Qnil;
+
+ for (size_t index = 0; index < nodes->size; index++) {
+ const pm_node_t *part = nodes->nodes[index];
+ VALUE string;
+
+ switch (PM_NODE_TYPE(part)) {
+ case PM_STRING_NODE:
+ string = parse_string_encoded(scope_node, part, &((const pm_string_node_t *) part)->unescaped);
+ break;
+ case PM_INTERPOLATED_STRING_NODE:
+ string = pm_static_literal_concat(&((const pm_interpolated_string_node_t *) part)->parts, scope_node, false);
+ break;
+ default:
+ RUBY_ASSERT(false && "unexpected node type in pm_static_literal_concat");
+ return Qnil;
+ }
+
+ if (current != Qnil) {
+ current = rb_str_concat(current, string);
+ }
+ else {
+ current = string;
+ }
+ }
+
+ return top ? rb_fstring(current) : current;
+}
+
+#define RE_OPTION_ENCODING_SHIFT 8
+#define RE_OPTION_ENCODING(encoding) (((encoding) & 0xFF) << RE_OPTION_ENCODING_SHIFT)
+#define ARG_ENCODING_NONE 32
+#define ARG_ENCODING_FIXED 16
+#define ENC_ASCII8BIT 1
+#define ENC_EUC_JP 2
+#define ENC_Windows_31J 3
+#define ENC_UTF8 4
+
+/**
+ * Check the prism flags of a regular expression-like node and return the flags
+ * that are expected by the CRuby VM.
+ */
+static int
+parse_regexp_flags(const pm_node_t *node)
+{
+ int flags = 0;
+
+ // Check "no encoding" first so that flags don't get clobbered
+ // We're calling `rb_char_to_option_kcode` in this case so that
+ // we don't need to have access to `ARG_ENCODING_NONE`
+ if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT)) {
+ flags |= ARG_ENCODING_NONE;
+ }
+
+ if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EUC_JP)) {
+ flags |= (ARG_ENCODING_FIXED | RE_OPTION_ENCODING(ENC_EUC_JP));
+ }
+
+ if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J)) {
+ flags |= (ARG_ENCODING_FIXED | RE_OPTION_ENCODING(ENC_Windows_31J));
+ }
+
+ if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_UTF_8)) {
+ flags |= (ARG_ENCODING_FIXED | RE_OPTION_ENCODING(ENC_UTF8));
+ }
+
+ if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE)) {
+ flags |= ONIG_OPTION_IGNORECASE;
+ }
+
+ if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE)) {
+ flags |= ONIG_OPTION_MULTILINE;
+ }
+
+ if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED)) {
+ flags |= ONIG_OPTION_EXTEND;
+ }
+
+ return flags;
+}
+
+#undef RE_OPTION_ENCODING_SHIFT
+#undef RE_OPTION_ENCODING
+#undef ARG_ENCODING_FIXED
+#undef ARG_ENCODING_NONE
+#undef ENC_ASCII8BIT
+#undef ENC_EUC_JP
+#undef ENC_Windows_31J
+#undef ENC_UTF8
+
+static rb_encoding *
+parse_regexp_encoding(const pm_scope_node_t *scope_node, const pm_node_t *node)
+{
+ if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT)) {
+ return rb_ascii8bit_encoding();
+ }
+ else if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_UTF_8)) {
+ return rb_utf8_encoding();
+ }
+ else if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EUC_JP)) {
+ return rb_enc_get_from_index(ENCINDEX_EUC_JP);
+ }
+ else if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J)) {
+ return rb_enc_get_from_index(ENCINDEX_Windows_31J);
+ }
+ else {
+ return scope_node->encoding;
+ }
+}
+
+/** Raise an error corresponding to the invalid regular expression. */
+static VALUE
+parse_regexp_error(rb_iseq_t *iseq, int32_t line_number, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ VALUE error = rb_syntax_error_append(Qnil, rb_iseq_path(iseq), line_number, -1, NULL, "%" PRIsVALUE, args);
+ va_end(args);
+ rb_exc_raise(error);
+}
+
+static VALUE
+parse_regexp(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_node_t *node, VALUE string)
+{
+ VALUE errinfo = rb_errinfo();
+
+ int32_t line_number = pm_node_line_number(scope_node->parser, node);
+ VALUE regexp = rb_reg_compile(string, parse_regexp_flags(node), (const char *) pm_string_source(&scope_node->parser->filepath), line_number);
+
+ if (NIL_P(regexp)) {
+ VALUE message = rb_attr_get(rb_errinfo(), idMesg);
+ rb_set_errinfo(errinfo);
+
+ parse_regexp_error(iseq, line_number, "%" PRIsVALUE, message);
+ return Qnil;
+ }
+
+ rb_obj_freeze(regexp);
+ return regexp;
+}
+
+static inline VALUE
+parse_regexp_literal(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_node_t *node, const pm_string_t *unescaped)
+{
+ VALUE string = rb_enc_str_new((const char *) pm_string_source(unescaped), pm_string_length(unescaped), parse_regexp_encoding(scope_node, node));
+ return parse_regexp(iseq, scope_node, node, string);
+}
+
+static inline VALUE
+parse_regexp_concat(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_node_t *node, const pm_node_list_t *parts)
+{
+ VALUE string = pm_static_literal_concat(parts, scope_node, false);
+ rb_enc_associate(string, parse_regexp_encoding(scope_node, node));
+ return parse_regexp(iseq, scope_node, node, string);
+}
+
+static void
+pm_compile_regexp_dynamic(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_list_t *parts, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
+{
+ int length = pm_interpolated_node_compile(iseq, parts, node_location, ret, popped, scope_node);
+ PUSH_INSN2(ret, *node_location, toregexp, INT2FIX(parse_regexp_flags(node) & 0xFF), INT2FIX(length));
+}
+
+static VALUE
+pm_source_file_value(const pm_source_file_node_t *node, const pm_scope_node_t *scope_node)
+{
+ const pm_string_t *filepath = &node->filepath;
+ size_t length = pm_string_length(filepath);
+
+ if (length > 0) {
+ rb_encoding *filepath_encoding = scope_node->filepath_encoding != NULL ? scope_node->filepath_encoding : rb_utf8_encoding();
+ return rb_enc_interned_str((const char *) pm_string_source(filepath), length, filepath_encoding);
+ }
+ else {
+ return rb_fstring_lit("<compiled>");
+ }
+}
+
+/**
+ * Return a static literal string, optionally with attached debugging
+ * information.
+ */
+static VALUE
+pm_static_literal_string(rb_iseq_t *iseq, VALUE string, int line_number)
+{
+ if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
+ VALUE debug_info = rb_ary_new_from_args(2, rb_iseq_path(iseq), INT2FIX(line_number));
+ rb_ivar_set(string, id_debug_created_info, rb_obj_freeze(debug_info));
+ return rb_str_freeze(string);
+ }
+ else {
+ return rb_fstring(string);
+ }
+}
+
+/**
+ * Certain nodes can be compiled literally. This function returns the literal
+ * value described by the given node. For example, an array node with all static
+ * literal values can be compiled into a literal array.
+ */
+static VALUE
+pm_static_literal_value(rb_iseq_t *iseq, const pm_node_t *node, const pm_scope_node_t *scope_node)
+{
+ // Every node that comes into this function should already be marked as
+ // static literal. If it's not, then we have a bug somewhere.
+ RUBY_ASSERT(PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL));
+
+ switch (PM_NODE_TYPE(node)) {
+ case PM_ARRAY_NODE: {
+ const pm_array_node_t *cast = (const pm_array_node_t *) node;
+ const pm_node_list_t *elements = &cast->elements;
+
+ VALUE value = rb_ary_hidden_new(elements->size);
+ for (size_t index = 0; index < elements->size; index++) {
+ rb_ary_push(value, pm_static_literal_value(iseq, elements->nodes[index], scope_node));
+ }
+
+ OBJ_FREEZE(value);
+ return value;
+ }
+ case PM_FALSE_NODE:
+ return Qfalse;
+ case PM_FLOAT_NODE:
+ return parse_float((const pm_float_node_t *) node);
+ case PM_HASH_NODE: {
+ const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
+ const pm_node_list_t *elements = &cast->elements;
+
+ VALUE array = rb_ary_hidden_new(elements->size * 2);
+ for (size_t index = 0; index < elements->size; index++) {
+ RUBY_ASSERT(PM_NODE_TYPE_P(elements->nodes[index], PM_ASSOC_NODE));
+ const pm_assoc_node_t *cast = (const pm_assoc_node_t *) elements->nodes[index];
+ VALUE pair[2] = { pm_static_literal_value(iseq, cast->key, scope_node), pm_static_literal_value(iseq, cast->value, scope_node) };
+ rb_ary_cat(array, pair, 2);
+ }
+
+ VALUE value = rb_hash_new_with_size(elements->size);
+ rb_hash_bulk_insert(RARRAY_LEN(array), RARRAY_CONST_PTR(array), value);
+
+ value = rb_obj_hide(value);
+ OBJ_FREEZE(value);
+ return value;
+ }
+ case PM_IMAGINARY_NODE:
+ return parse_imaginary((const pm_imaginary_node_t *) node);
+ case PM_INTEGER_NODE:
+ return parse_integer((const pm_integer_node_t *) node);
+ case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: {
+ const pm_interpolated_match_last_line_node_t *cast = (const pm_interpolated_match_last_line_node_t *) node;
+ return parse_regexp_concat(iseq, scope_node, (const pm_node_t *) cast, &cast->parts);
+ }
+ case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
+ const pm_interpolated_regular_expression_node_t *cast = (const pm_interpolated_regular_expression_node_t *) node;
+ return parse_regexp_concat(iseq, scope_node, (const pm_node_t *) cast, &cast->parts);
+ }
+ case PM_INTERPOLATED_STRING_NODE: {
+ VALUE string = pm_static_literal_concat(&((const pm_interpolated_string_node_t *) node)->parts, scope_node, false);
+ int line_number = pm_node_line_number(scope_node->parser, node);
+ return pm_static_literal_string(iseq, string, line_number);
+ }
+ case PM_INTERPOLATED_SYMBOL_NODE: {
+ const pm_interpolated_symbol_node_t *cast = (const pm_interpolated_symbol_node_t *) node;
+ VALUE string = pm_static_literal_concat(&cast->parts, scope_node, true);
+
+ return ID2SYM(rb_intern_str(string));
+ }
+ case PM_MATCH_LAST_LINE_NODE: {
+ const pm_match_last_line_node_t *cast = (const pm_match_last_line_node_t *) node;
+ return parse_regexp_literal(iseq, scope_node, (const pm_node_t *) cast, &cast->unescaped);
+ }
+ case PM_NIL_NODE:
+ return Qnil;
+ case PM_RATIONAL_NODE:
+ return parse_rational((const pm_rational_node_t *) node);
+ case PM_REGULAR_EXPRESSION_NODE: {
+ const pm_regular_expression_node_t *cast = (const pm_regular_expression_node_t *) node;
+ return parse_regexp_literal(iseq, scope_node, (const pm_node_t *) cast, &cast->unescaped);
+ }
+ case PM_SOURCE_ENCODING_NODE:
+ return rb_enc_from_encoding(scope_node->encoding);
+ case PM_SOURCE_FILE_NODE: {
+ const pm_source_file_node_t *cast = (const pm_source_file_node_t *) node;
+ return pm_source_file_value(cast, scope_node);
+ }
+ case PM_SOURCE_LINE_NODE:
+ return INT2FIX(pm_node_line_number(scope_node->parser, node));
+ case PM_STRING_NODE: {
+ const pm_string_node_t *cast = (const pm_string_node_t *) node;
+ return parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
+ }
+ case PM_SYMBOL_NODE:
+ return ID2SYM(parse_string_symbol(scope_node, (const pm_symbol_node_t *) node));
+ case PM_TRUE_NODE:
+ return Qtrue;
+ default:
+ rb_bug("Don't have a literal value for node type %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
+ return Qfalse;
+ }
+}
+
+/**
+ * A helper for converting a pm_location_t into a rb_code_location_t.
+ */
+static rb_code_location_t
+pm_code_location(const pm_scope_node_t *scope_node, const pm_node_t *node)
+{
+ const pm_line_column_t start_location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
+ const pm_line_column_t end_location = PM_NODE_END_LINE_COLUMN(scope_node->parser, node);
+
+ return (rb_code_location_t) {
+ .beg_pos = { .lineno = start_location.line, .column = start_location.column },
+ .end_pos = { .lineno = end_location.line, .column = end_location.column }
+ };
+}
+
+/**
+ * A macro for determining if we should go through the work of adding branch
+ * coverage to the current iseq. We check this manually each time because we
+ * want to avoid the overhead of creating rb_code_location_t objects.
+ */
+#define PM_BRANCH_COVERAGE_P(iseq) (ISEQ_COVERAGE(iseq) && ISEQ_BRANCH_COVERAGE(iseq))
+
+static void
+pm_compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_node_t *cond,
+ LABEL *then_label, LABEL *else_label, bool popped, pm_scope_node_t *scope_node);
+
+static void
+pm_compile_logical(rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_node_t *cond, LABEL *then_label, LABEL *else_label, bool popped, pm_scope_node_t *scope_node)
+{
+ const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, cond);
+
+ DECL_ANCHOR(seq);
+ INIT_ANCHOR(seq);
+
+ LABEL *label = NEW_LABEL(location.line);
+ if (!then_label) then_label = label;
+ else if (!else_label) else_label = label;
+
+ pm_compile_branch_condition(iseq, seq, cond, then_label, else_label, popped, scope_node);
+
+ if (LIST_INSN_SIZE_ONE(seq)) {
+ INSN *insn = (INSN *) ELEM_FIRST_INSN(FIRST_ELEMENT(seq));
+ if (insn->insn_id == BIN(jump) && (LABEL *)(insn->operands[0]) == label) return;
+ }
+
+ if (!label->refcnt) {
+ if (popped) PUSH_INSN(ret, location, putnil);
+ }
+ else {
+ PUSH_LABEL(seq, label);
+ }
+
+ PUSH_SEQ(ret, seq);
+ return;
+}
+
+static void
+pm_compile_flip_flop_bound(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
+{
+ const pm_line_column_t location = { .line = ISEQ_BODY(iseq)->location.first_lineno, .column = -1 };
+
+ if (PM_NODE_TYPE_P(node, PM_INTEGER_NODE)) {
+ PM_COMPILE_NOT_POPPED(node);
+ PUSH_INSN1(ret, location, getglobal, ID2SYM(rb_intern("$.")));
+ PUSH_SEND(ret, location, idEq, INT2FIX(1));
+ if (popped) PUSH_INSN(ret, location, pop);
+ }
+ else {
+ PM_COMPILE(node);
+ }
+}
+
+static void
+pm_compile_flip_flop(const pm_flip_flop_node_t *flip_flop_node, LABEL *else_label, LABEL *then_label, rb_iseq_t *iseq, const int lineno, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
+{
+ const pm_line_column_t location = { .line = ISEQ_BODY(iseq)->location.first_lineno, .column = -1 };
+ LABEL *lend = NEW_LABEL(location.line);
+
+ int again = !(flip_flop_node->base.flags & PM_RANGE_FLAGS_EXCLUDE_END);
+
+ rb_num_t count = ISEQ_FLIP_CNT_INCREMENT(ISEQ_BODY(iseq)->local_iseq) + VM_SVAR_FLIPFLOP_START;
+ VALUE key = INT2FIX(count);
+
+ PUSH_INSN2(ret, location, getspecial, key, INT2FIX(0));
+ PUSH_INSNL(ret, location, branchif, lend);
+
+ if (flip_flop_node->left) {
+ pm_compile_flip_flop_bound(iseq, flip_flop_node->left, ret, popped, scope_node);
+ }
+ else {
+ PUSH_INSN(ret, location, putnil);
+ }
+
+ PUSH_INSNL(ret, location, branchunless, else_label);
+ PUSH_INSN1(ret, location, putobject, Qtrue);
+ PUSH_INSN1(ret, location, setspecial, key);
+ if (!again) {
+ PUSH_INSNL(ret, location, jump, then_label);
+ }
+
+ PUSH_LABEL(ret, lend);
+ if (flip_flop_node->right) {
+ pm_compile_flip_flop_bound(iseq, flip_flop_node->right, ret, popped, scope_node);
+ }
+ else {
+ PUSH_INSN(ret, location, putnil);
+ }
+
+ PUSH_INSNL(ret, location, branchunless, then_label);
+ PUSH_INSN1(ret, location, putobject, Qfalse);
+ PUSH_INSN1(ret, location, setspecial, key);
+ PUSH_INSNL(ret, location, jump, then_label);
+}
+
+static void pm_compile_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition);
+
+static void
+pm_compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_node_t *cond, LABEL *then_label, LABEL *else_label, bool popped, pm_scope_node_t *scope_node)
+{
+ const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, cond);
+
+again:
+ switch (PM_NODE_TYPE(cond)) {
+ case PM_AND_NODE: {
+ const pm_and_node_t *cast = (const pm_and_node_t *) cond;
+ pm_compile_logical(iseq, ret, cast->left, NULL, else_label, popped, scope_node);
+
+ cond = cast->right;
+ goto again;
+ }
+ case PM_OR_NODE: {
+ const pm_or_node_t *cast = (const pm_or_node_t *) cond;
+ pm_compile_logical(iseq, ret, cast->left, then_label, NULL, popped, scope_node);
+
+ cond = cast->right;
+ goto again;
+ }
+ case PM_FALSE_NODE:
+ case PM_NIL_NODE:
+ PUSH_INSNL(ret, location, jump, else_label);
+ return;
+ case PM_FLOAT_NODE:
+ case PM_IMAGINARY_NODE:
+ case PM_INTEGER_NODE:
+ case PM_LAMBDA_NODE:
+ case PM_RATIONAL_NODE:
+ case PM_REGULAR_EXPRESSION_NODE:
+ case PM_STRING_NODE:
+ case PM_SYMBOL_NODE:
+ case PM_TRUE_NODE:
+ PUSH_INSNL(ret, location, jump, then_label);
+ return;
+ case PM_FLIP_FLOP_NODE:
+ pm_compile_flip_flop((const pm_flip_flop_node_t *) cond, else_label, then_label, iseq, location.line, ret, popped, scope_node);
+ return;
+ case PM_DEFINED_NODE: {
+ const pm_defined_node_t *cast = (const pm_defined_node_t *) cond;
+ pm_compile_defined_expr(iseq, cast->value, &location, ret, popped, scope_node, true);
+ break;
+ }
+ default: {
+ pm_compile_node(iseq, cond, ret, false, scope_node);
+ break;
+ }
+ }
+
+ PUSH_INSNL(ret, location, branchunless, else_label);
+ PUSH_INSNL(ret, location, jump, then_label);
+}
+
+/**
+ * Compile an if or unless node.
+ */
+static void
+pm_compile_conditional(rb_iseq_t *iseq, const pm_line_column_t *line_column, pm_node_type_t type, const pm_node_t *node, const pm_statements_node_t *statements, const pm_node_t *consequent, const pm_node_t *predicate, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
+{
+ const pm_line_column_t location = *line_column;
+ LABEL *then_label = NEW_LABEL(location.line);
+ LABEL *else_label = NEW_LABEL(location.line);
+ LABEL *end_label = NULL;
+
+ pm_compile_branch_condition(iseq, ret, predicate, then_label, else_label, false, scope_node);
+
+ rb_code_location_t conditional_location;
+ VALUE branches = Qfalse;
+
+ if (then_label->refcnt && else_label->refcnt && PM_BRANCH_COVERAGE_P(iseq)) {
+ conditional_location = pm_code_location(scope_node, node);
+ branches = decl_branch_base(iseq, PTR2NUM(node), &conditional_location, type == PM_IF_NODE ? "if" : "unless");
+ }
+
+ if (then_label->refcnt) {
+ PUSH_LABEL(ret, then_label);
+
+ DECL_ANCHOR(then_seq);
+ INIT_ANCHOR(then_seq);
+
+ if (statements != NULL) {
+ pm_compile_node(iseq, (const pm_node_t *) statements, then_seq, popped, scope_node);
+ }
+ else if (!popped) {
+ PUSH_SYNTHETIC_PUTNIL(then_seq, iseq);
+ }
+
+ if (else_label->refcnt) {
+ // Establish branch coverage for the then block.
+ if (PM_BRANCH_COVERAGE_P(iseq)) {
+ rb_code_location_t branch_location;
+
+ if (statements != NULL) {
+ branch_location = pm_code_location(scope_node, (const pm_node_t *) statements);
+ } else if (type == PM_IF_NODE) {
+ pm_line_column_t predicate_end = PM_NODE_END_LINE_COLUMN(scope_node->parser, predicate);
+ branch_location = (rb_code_location_t) {
+ .beg_pos = { .lineno = predicate_end.line, .column = predicate_end.column },
+ .end_pos = { .lineno = predicate_end.line, .column = predicate_end.column }
+ };
+ } else {
+ branch_location = conditional_location;
+ }
+
+ add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 0, type == PM_IF_NODE ? "then" : "else", branches);
+ }
+
+ end_label = NEW_LABEL(location.line);
+ PUSH_INSNL(then_seq, location, jump, end_label);
+ if (!popped) PUSH_INSN(then_seq, location, pop);
+ }
+
+ PUSH_SEQ(ret, then_seq);
+ }
+
+ if (else_label->refcnt) {
+ PUSH_LABEL(ret, else_label);
+
+ DECL_ANCHOR(else_seq);
+ INIT_ANCHOR(else_seq);
+
+ if (consequent != NULL) {
+ pm_compile_node(iseq, consequent, else_seq, popped, scope_node);
+ }
+ else if (!popped) {
+ PUSH_SYNTHETIC_PUTNIL(else_seq, iseq);
+ }
+
+ // Establish branch coverage for the else block.
+ if (then_label->refcnt && PM_BRANCH_COVERAGE_P(iseq)) {
+ rb_code_location_t branch_location;
+
+ if (consequent == NULL) {
+ branch_location = conditional_location;
+ } else if (PM_NODE_TYPE_P(consequent, PM_ELSE_NODE)) {
+ const pm_else_node_t *else_node = (const pm_else_node_t *) consequent;
+ branch_location = pm_code_location(scope_node, else_node->statements != NULL ? ((const pm_node_t *) else_node->statements) : (const pm_node_t *) else_node);
+ } else {
+ branch_location = pm_code_location(scope_node, (const pm_node_t *) consequent);
+ }
+
+ add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 1, type == PM_IF_NODE ? "else" : "then", branches);
+ }
+
+ PUSH_SEQ(ret, else_seq);
+ }
+
+ if (end_label) {
+ PUSH_LABEL(ret, end_label);
+ }
+
+ return;
+}
+
+/**
+ * Compile a while or until loop.
+ */
+static void
+pm_compile_loop(rb_iseq_t *iseq, const pm_line_column_t *line_column, pm_node_flags_t flags, enum pm_node_type type, const pm_node_t *node, const pm_statements_node_t *statements, const pm_node_t *predicate, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
+{
+ const pm_line_column_t location = *line_column;
+
+ LABEL *prev_start_label = ISEQ_COMPILE_DATA(iseq)->start_label;
+ LABEL *prev_end_label = ISEQ_COMPILE_DATA(iseq)->end_label;
+ LABEL *prev_redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label;
+
+ // TODO: Deal with ensures in here
+ LABEL *next_label = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(location.line); /* next */
+ LABEL *redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label = NEW_LABEL(location.line); /* redo */
+ LABEL *break_label = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(location.line); /* break */
+ LABEL *end_label = NEW_LABEL(location.line);
+ LABEL *adjust_label = NEW_LABEL(location.line);
+
+ LABEL *next_catch_label = NEW_LABEL(location.line);
+ LABEL *tmp_label = NULL;
+
+ // begin; end while true
+ if (flags & PM_LOOP_FLAGS_BEGIN_MODIFIER) {
+ tmp_label = NEW_LABEL(location.line);
+ PUSH_INSNL(ret, location, jump, tmp_label);
+ }
+ else {
+ // while true; end
+ PUSH_INSNL(ret, location, jump, next_label);
+ }
+
+ PUSH_LABEL(ret, adjust_label);
+ PUSH_INSN(ret, location, putnil);
+ PUSH_LABEL(ret, next_catch_label);
+ PUSH_INSN(ret, location, pop);
+ PUSH_INSNL(ret, location, jump, next_label);
+ if (tmp_label) PUSH_LABEL(ret, tmp_label);
+
+ PUSH_LABEL(ret, redo_label);
+
+ // Establish branch coverage for the loop.
+ if (PM_BRANCH_COVERAGE_P(iseq)) {
+ rb_code_location_t loop_location = pm_code_location(scope_node, node);
+ VALUE branches = decl_branch_base(iseq, PTR2NUM(node), &loop_location, type == PM_WHILE_NODE ? "while" : "until");
+
+ rb_code_location_t branch_location = statements != NULL ? pm_code_location(scope_node, (const pm_node_t *) statements) : loop_location;
+ add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 0, "body", branches);
+ }
+
+ if (statements != NULL) PM_COMPILE_POPPED((const pm_node_t *) statements);
+ PUSH_LABEL(ret, next_label);
+
+ if (type == PM_WHILE_NODE) {
+ pm_compile_branch_condition(iseq, ret, predicate, redo_label, end_label, popped, scope_node);
+ }
+ else if (type == PM_UNTIL_NODE) {
+ pm_compile_branch_condition(iseq, ret, predicate, end_label, redo_label, popped, scope_node);
+ }
+
+ PUSH_LABEL(ret, end_label);
+ PUSH_ADJUST_RESTORE(ret, adjust_label);
+ PUSH_INSN(ret, location, putnil);
+
+ PUSH_LABEL(ret, break_label);
+ if (popped) PUSH_INSN(ret, location, pop);
+
+ PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, redo_label, break_label, NULL, break_label);
+ PUSH_CATCH_ENTRY(CATCH_TYPE_NEXT, redo_label, break_label, NULL, next_catch_label);
+ PUSH_CATCH_ENTRY(CATCH_TYPE_REDO, redo_label, break_label, NULL, ISEQ_COMPILE_DATA(iseq)->redo_label);
+
+ ISEQ_COMPILE_DATA(iseq)->start_label = prev_start_label;
+ ISEQ_COMPILE_DATA(iseq)->end_label = prev_end_label;
+ ISEQ_COMPILE_DATA(iseq)->redo_label = prev_redo_label;
+ return;
+}
+
+// This recurses through scopes and finds the local index at any scope level
+// It also takes a pointer to depth, and increments depth appropriately
+// according to the depth of the local.
+static pm_local_index_t
+pm_lookup_local_index(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, pm_constant_id_t constant_id, int start_depth)
+{
+ pm_local_index_t lindex = { 0 };
+ st_data_t local_index;
+
+ int level;
+ for (level = 0; level < start_depth; level++) {
+ scope_node = scope_node->previous;
+ }
+
+ while (!st_lookup(scope_node->index_lookup_table, constant_id, &local_index)) {
+ level++;
+
+ if (scope_node->previous) {
+ scope_node = scope_node->previous;
+ }
+ else {
+ // We have recursed up all scope nodes
+ // and have not found the local yet
+ rb_bug("Local with constant_id %u does not exist", (unsigned int) constant_id);
+ }
+ }
+
+ lindex.level = level;
+ lindex.index = scope_node->local_table_for_iseq_size - (int) local_index;
+ return lindex;
+}
+
+// This returns the CRuby ID which maps to the pm_constant_id_t
+//
+// Constant_ids in prism are indexes of the constants in prism's constant pool.
+// We add a constants mapping on the scope_node which is a mapping from
+// these constant_id indexes to the CRuby IDs that they represent.
+// This helper method allows easy access to those IDs
+static ID
+pm_constant_id_lookup(const pm_scope_node_t *scope_node, pm_constant_id_t constant_id)
+{
+ if (constant_id < 1 || constant_id > scope_node->parser->constant_pool.size) {
+ rb_bug("constant_id out of range: %u", (unsigned int)constant_id);
+ }
+ return scope_node->constants[constant_id - 1];
+}
+
+static rb_iseq_t *
+pm_new_child_iseq(rb_iseq_t *iseq, pm_scope_node_t *node, VALUE name, const rb_iseq_t *parent, enum rb_iseq_type type, int line_no)
+{
+ debugs("[new_child_iseq]> ---------------------------------------\n");
+ int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth;
+ rb_iseq_t *ret_iseq = pm_iseq_new_with_opt(node, name,
+ rb_iseq_path(iseq), rb_iseq_realpath(iseq),
+ line_no, parent,
+ isolated_depth ? isolated_depth + 1 : 0,
+ type, ISEQ_COMPILE_DATA(iseq)->option);
+ debugs("[new_child_iseq]< ---------------------------------------\n");
+ return ret_iseq;
+}
+
+static int
+pm_compile_class_path(rb_iseq_t *iseq, const pm_node_t *node, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
+{
+ if (PM_NODE_TYPE_P(node, PM_CONSTANT_PATH_NODE)) {
+ const pm_node_t *parent = ((const pm_constant_path_node_t *) node)->parent;
+
+ if (parent) {
+ /* Bar::Foo */
+ PM_COMPILE(parent);
+ return VM_DEFINECLASS_FLAG_SCOPED;
+ }
+ else {
+ /* toplevel class ::Foo */
+ PUSH_INSN1(ret, *node_location, putobject, rb_cObject);
+ return VM_DEFINECLASS_FLAG_SCOPED;
+ }
+ }
+ else {
+ /* class at cbase Foo */
+ PUSH_INSN1(ret, *node_location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
+ return 0;
+ }
+}
+
+/**
+ * Compile either a call and write node or a call or write node. These look like
+ * method calls that are followed by a ||= or &&= operator.
+ */
+static void
+pm_compile_call_and_or_write_node(rb_iseq_t *iseq, bool and_node, const pm_node_t *receiver, const pm_node_t *value, pm_constant_id_t write_name, pm_constant_id_t read_name, bool safe_nav, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
+{
+ const pm_line_column_t location = *node_location;
+ LABEL *lfin = NEW_LABEL(location.line);
+ LABEL *lcfin = NEW_LABEL(location.line);
+ LABEL *lskip = NULL;
+
+ int flag = PM_NODE_TYPE_P(receiver, PM_SELF_NODE) ? VM_CALL_FCALL : 0;
+ ID id_read_name = pm_constant_id_lookup(scope_node, read_name);
+
+ PM_COMPILE_NOT_POPPED(receiver);
+ if (safe_nav) {
+ lskip = NEW_LABEL(location.line);
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSNL(ret, location, branchnil, lskip);
+ }
+
+ PUSH_INSN(ret, location, dup);
+ PUSH_SEND_WITH_FLAG(ret, location, id_read_name, INT2FIX(0), INT2FIX(flag));
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ if (and_node) {
+ PUSH_INSNL(ret, location, branchunless, lcfin);
+ }
+ else {
+ PUSH_INSNL(ret, location, branchif, lcfin);
+ }
+
+ if (!popped) PUSH_INSN(ret, location, pop);
+ PM_COMPILE_NOT_POPPED(value);
+
+ if (!popped) {
+ PUSH_INSN(ret, location, swap);
+ PUSH_INSN1(ret, location, topn, INT2FIX(1));
+ }
+
+ ID id_write_name = pm_constant_id_lookup(scope_node, write_name);
+ PUSH_SEND_WITH_FLAG(ret, location, id_write_name, INT2FIX(1), INT2FIX(flag));
+ PUSH_INSNL(ret, location, jump, lfin);
+
+ PUSH_LABEL(ret, lcfin);
+ if (!popped) PUSH_INSN(ret, location, swap);
+
+ PUSH_LABEL(ret, lfin);
+
+ if (lskip && popped) PUSH_LABEL(ret, lskip);
+ PUSH_INSN(ret, location, pop);
+ if (lskip && !popped) PUSH_LABEL(ret, lskip);
+}
+
+/**
+ * This function compiles a hash onto the stack. It is used to compile hash
+ * literals and keyword arguments. It is assumed that if we get here that the
+ * contents of the hash are not popped.
+ */
+static void
+pm_compile_hash_elements(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_list_t *elements, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node)
+{
+ const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
+
+ // If this element is not popped, then we need to create the hash on the
+ // stack. Neighboring plain assoc nodes should be grouped together (either
+ // by newhash or hash merge). Double splat nodes should be merged using the
+ // merge_kwd method call.
+ int assoc_length = 0;
+ bool made_hash = false;
+
+ for (size_t index = 0; index < elements->size; index++) {
+ const pm_node_t *element = elements->nodes[index];
+
+ switch (PM_NODE_TYPE(element)) {
+ case PM_ASSOC_NODE: {
+ // If this is a plain assoc node, then we can compile it directly
+ // and then add to the number of assoc nodes we've seen so far.
+ PM_COMPILE_NOT_POPPED(element);
+ assoc_length++;
+ break;
+ }
+ case PM_ASSOC_SPLAT_NODE: {
+ // If we are at a splat and we have already compiled some elements
+ // of the hash, then we need to either create the first hash or
+ // merge the current elements into the existing hash.
+ if (assoc_length > 0) {
+ if (!made_hash) {
+ PUSH_INSN1(ret, location, newhash, INT2FIX(assoc_length * 2));
+ PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ PUSH_INSN(ret, location, swap);
+ made_hash = true;
+ }
+ else {
+ // Here we are merging plain assoc nodes into the hash on
+ // the stack.
+ PUSH_SEND(ret, location, id_core_hash_merge_ptr, INT2FIX(assoc_length * 2 + 1));
+
+ // Since we already have a hash on the stack, we need to set
+ // up the method call for the next merge that will occur.
+ PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ PUSH_INSN(ret, location, swap);
+ }
+
+ assoc_length = 0;
+ }
+
+ // If this is the first time we've seen a splat, then we need to
+ // create a hash that we can merge into.
+ if (!made_hash) {
+ PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ PUSH_INSN1(ret, location, newhash, INT2FIX(0));
+ made_hash = true;
+ }
+
+ // Now compile the splat node itself and merge it into the hash.
+ PM_COMPILE_NOT_POPPED(element);
+ PUSH_SEND(ret, location, id_core_hash_merge_kwd, INT2FIX(2));
+
+ // We know that any subsequent elements will need to be merged in
+ // using one of the special core methods. So here we will put the
+ // receiver of the merge and then swap it with the hash that is
+ // going to be the first argument.
+ if (index != elements->size - 1) {
+ PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ PUSH_INSN(ret, location, swap);
+ }
+
+ break;
+ }
+ default:
+ RUBY_ASSERT("Invalid node type for hash" && false);
+ break;
+ }
+ }
+
+ if (!made_hash) {
+ // If we haven't already made the hash, then this means we only saw
+ // plain assoc nodes. In this case, we can just create the hash
+ // directly.
+ PUSH_INSN1(ret, location, newhash, INT2FIX(assoc_length * 2));
+ }
+ else if (assoc_length > 0) {
+ // If we have already made the hash, then we need to merge the remaining
+ // assoc nodes into the hash on the stack.
+ PUSH_SEND(ret, location, id_core_hash_merge_ptr, INT2FIX(assoc_length * 2 + 1));
+ }
+}
+
+// This is details. Users should call pm_setup_args() instead.
+static int
+pm_setup_args_core(const pm_arguments_node_t *arguments_node, const pm_node_t *block, int *flags, const bool has_regular_blockarg, struct rb_callinfo_kwarg **kw_arg, rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node, const pm_line_column_t *node_location)
+{
+ const pm_line_column_t location = *node_location;
+
+ int orig_argc = 0;
+ bool has_splat = false;
+ bool has_keyword_splat = false;
+
+ if (arguments_node == NULL) {
+ if (*flags & VM_CALL_FCALL) {
+ *flags |= VM_CALL_VCALL;
+ }
+ }
+ else {
+ const pm_node_list_t *arguments = &arguments_node->arguments;
+ has_keyword_splat = PM_NODE_FLAG_P(arguments_node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT);
+
+ // We count the number of elements post the splat node that are not keyword elements to
+ // eventually pass as an argument to newarray
+ int post_splat_counter = 0;
+ const pm_node_t *argument;
+
+ PM_NODE_LIST_FOREACH(arguments, index, argument) {
+ switch (PM_NODE_TYPE(argument)) {
+ // A keyword hash node contains all keyword arguments as AssocNodes and AssocSplatNodes
+ case PM_KEYWORD_HASH_NODE: {
+ const pm_keyword_hash_node_t *keyword_arg = (const pm_keyword_hash_node_t *) argument;
+ const pm_node_list_t *elements = &keyword_arg->elements;
+
+ if (has_keyword_splat || has_splat) {
+ *flags |= VM_CALL_KW_SPLAT;
+ has_keyword_splat = true;
+ pm_compile_hash_elements(iseq, argument, elements, ret, scope_node);
+ }
+ else {
+ // We need to first figure out if all elements of the
+ // KeywordHashNode are AssocNodes with symbol keys.
+ if (PM_NODE_FLAG_P(keyword_arg, PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS)) {
+ // If they are all symbol keys then we can pass them as
+ // keyword arguments. The first thing we need to do is
+ // deduplicate. We'll do this using the combination of a
+ // Ruby hash and a Ruby array.
+ VALUE stored_indices = rb_hash_new();
+ VALUE keyword_indices = rb_ary_new_capa(elements->size);
+
+ size_t size = 0;
+ for (size_t element_index = 0; element_index < elements->size; element_index++) {
+ const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[element_index];
+
+ // Retrieve the stored index from the hash for this
+ // keyword.
+ VALUE keyword = pm_static_literal_value(iseq, assoc->key, scope_node);
+ VALUE stored_index = rb_hash_aref(stored_indices, keyword);
+
+ // If this keyword was already seen in the hash,
+ // then mark the array at that index as false and
+ // decrement the keyword size.
+ if (!NIL_P(stored_index)) {
+ rb_ary_store(keyword_indices, NUM2LONG(stored_index), Qfalse);
+ size--;
+ }
+
+ // Store (and possibly overwrite) the index for this
+ // keyword in the hash, mark the array at that index
+ // as true, and increment the keyword size.
+ rb_hash_aset(stored_indices, keyword, ULONG2NUM(element_index));
+ rb_ary_store(keyword_indices, (long) element_index, Qtrue);
+ size++;
+ }
+
+ *kw_arg = rb_xmalloc_mul_add(size, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
+ *flags |= VM_CALL_KWARG;
+
+ VALUE *keywords = (*kw_arg)->keywords;
+ (*kw_arg)->references = 0;
+ (*kw_arg)->keyword_len = (int) size;
+
+ size_t keyword_index = 0;
+ for (size_t element_index = 0; element_index < elements->size; element_index++) {
+ const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[element_index];
+ bool popped = true;
+
+ if (rb_ary_entry(keyword_indices, (long) element_index) == Qtrue) {
+ keywords[keyword_index++] = pm_static_literal_value(iseq, assoc->key, scope_node);
+ popped = false;
+ }
+
+ PM_COMPILE(assoc->value);
+ }
+
+ RUBY_ASSERT(keyword_index == size);
+ }
+ else {
+ // If they aren't all symbol keys then we need to
+ // construct a new hash and pass that as an argument.
+ orig_argc++;
+ *flags |= VM_CALL_KW_SPLAT;
+
+ size_t size = elements->size;
+ if (size > 1) {
+ // A new hash will be created for the keyword
+ // arguments in this case, so mark the method as
+ // passing mutable keyword splat.
+ *flags |= VM_CALL_KW_SPLAT_MUT;
+ }
+
+ for (size_t element_index = 0; element_index < size; element_index++) {
+ const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[element_index];
+ PM_COMPILE_NOT_POPPED(assoc->key);
+ PM_COMPILE_NOT_POPPED(assoc->value);
+ }
+
+ PUSH_INSN1(ret, location, newhash, INT2FIX(size * 2));
+ }
+ }
+ break;
+ }
+ case PM_SPLAT_NODE: {
+ *flags |= VM_CALL_ARGS_SPLAT;
+ const pm_splat_node_t *splat_node = (const pm_splat_node_t *) argument;
+
+ if (splat_node->expression) {
+ PM_COMPILE_NOT_POPPED(splat_node->expression);
+ }
+ else {
+ pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_MULT, 0);
+ PUSH_GETLOCAL(ret, location, index.index, index.level);
+ }
+
+ bool first_splat = !has_splat;
+
+ if (first_splat) {
+ // If this is the first splat array seen and it's not the
+ // last parameter, we want splatarray to dup it.
+ //
+ // foo(a, *b, c)
+ // ^^
+ if (index + 1 < arguments->size || has_regular_blockarg) {
+ PUSH_INSN1(ret, location, splatarray, Qtrue);
+ *flags |= VM_CALL_ARGS_SPLAT_MUT;
+ }
+ // If this is the first spalt array seen and it's the last
+ // parameter, we don't want splatarray to dup it.
+ //
+ // foo(a, *b)
+ // ^^
+ else {
+ PUSH_INSN1(ret, location, splatarray, Qfalse);
+ }
+ }
+ else {
+ // If this is not the first splat array seen and it is also
+ // the last parameter, we don't want splatarray to dup it
+ // and we need to concat the array.
+ //
+ // foo(a, *b, *c)
+ // ^^
+ PUSH_INSN1(ret, location, splatarray, Qfalse);
+ PUSH_INSN(ret, location, concatarray);
+ }
+
+ has_splat = true;
+ post_splat_counter = 0;
+
+ break;
+ }
+ case PM_FORWARDING_ARGUMENTS_NODE: {
+ orig_argc += 2;
+ *flags |= VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_SPLAT_MUT | VM_CALL_ARGS_BLOCKARG | VM_CALL_KW_SPLAT;
+
+ // Forwarding arguments nodes are treated as foo(*, **, &)
+ // So foo(...) equals foo(*, **, &) and as such the local
+ // table for this method is known in advance
+ //
+ // Push the *
+ pm_local_index_t mult_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_MULT, 0);
+ PUSH_GETLOCAL(ret, location, mult_local.index, mult_local.level);
+ PUSH_INSN1(ret, location, splatarray, Qtrue);
+
+ // Push the **
+ pm_local_index_t pow_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_POW, 0);
+ PUSH_GETLOCAL(ret, location, pow_local.index, pow_local.level);
+
+ // Push the &
+ pm_local_index_t and_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_AND, 0);
+ PUSH_INSN2(ret, location, getblockparamproxy, INT2FIX(and_local.index + VM_ENV_DATA_SIZE - 1), INT2FIX(and_local.level));
+ PUSH_INSN(ret, location, splatkw);
+
+ break;
+ }
+ default: {
+ post_splat_counter++;
+ PM_COMPILE_NOT_POPPED(argument);
+
+ // If we have a splat and we've seen a splat, we need to process
+ // everything after the splat.
+ if (has_splat) {
+ // Stack items are turned into an array and concatenated in
+ // the following cases:
+ //
+ // If the next node is a splat:
+ //
+ // foo(*a, b, *c)
+ //
+ // If the next node is a kwarg or kwarg splat:
+ //
+ // foo(*a, b, c: :d)
+ // foo(*a, b, **c)
+ //
+ // If the next node is NULL (we have hit the end):
+ //
+ // foo(*a, b)
+ if (index == arguments->size - 1) {
+ RUBY_ASSERT(post_splat_counter > 0);
+ PUSH_INSN1(ret, location, pushtoarray, INT2FIX(post_splat_counter));
+ }
+ else {
+ pm_node_t *next_arg = arguments->nodes[index + 1];
+
+ switch (PM_NODE_TYPE(next_arg)) {
+ // A keyword hash node contains all keyword arguments as AssocNodes and AssocSplatNodes
+ case PM_KEYWORD_HASH_NODE: {
+ PUSH_INSN1(ret, location, newarray, INT2FIX(post_splat_counter));
+ PUSH_INSN(ret, location, concatarray);
+ break;
+ }
+ case PM_SPLAT_NODE: {
+ PUSH_INSN1(ret, location, newarray, INT2FIX(post_splat_counter));
+ PUSH_INSN(ret, location, concatarray);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ else {
+ orig_argc++;
+ }
+ }
+ }
+ }
+ }
+
+ if (has_splat) orig_argc++;
+ if (has_keyword_splat) orig_argc++;
+ return orig_argc;
+}
+
+// Compile the argument parts of a call
+static int
+pm_setup_args(const pm_arguments_node_t *arguments_node, const pm_node_t *block, int *flags, struct rb_callinfo_kwarg **kw_arg, rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node, const pm_line_column_t *node_location)
+{
+ if (block && PM_NODE_TYPE_P(block, PM_BLOCK_ARGUMENT_NODE)) {
+ // We compile the `&block_arg` expression first and stitch it later
+ // since the nature of the expression influences whether splat should
+ // duplicate the array.
+ bool regular_block_arg = true;
+ DECL_ANCHOR(block_arg);
+ INIT_ANCHOR(block_arg);
+ pm_compile_node(iseq, block, block_arg, false, scope_node);
+
+ *flags |= VM_CALL_ARGS_BLOCKARG;
+
+ if (LIST_INSN_SIZE_ONE(block_arg)) {
+ LINK_ELEMENT *elem = FIRST_ELEMENT(block_arg);
+ if (IS_INSN(elem)) {
+ INSN *iobj = (INSN *) elem;
+ if (iobj->insn_id == BIN(getblockparam)) {
+ iobj->insn_id = BIN(getblockparamproxy);
+ }
+ // Allow splat without duplication for simple one-instruction
+ // block arguments like `&arg`. It is known that this optimization
+ // can be too aggressive in some cases. See [Bug #16504].
+ regular_block_arg = false;
+ }
+ }
+
+ int argc = pm_setup_args_core(arguments_node, block, flags, regular_block_arg, kw_arg, iseq, ret, scope_node, node_location);
+ PUSH_SEQ(ret, block_arg);
+ return argc;
+ }
+
+ return pm_setup_args_core(arguments_node, block, flags, false, kw_arg, iseq, ret, scope_node, node_location);
+}
+
+/**
+ * Compile an index operator write node, which is a node that is writing a value
+ * using the [] and []= methods. It looks like:
+ *
+ * foo[bar] += baz
+ *
+ * This breaks down to caching the receiver and arguments on the stack, calling
+ * the [] method, calling the operator method with the result of the [] method,
+ * and then calling the []= method with the result of the operator method.
+ */
+static void
+pm_compile_index_operator_write_node(rb_iseq_t *iseq, const pm_index_operator_write_node_t *node, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
+{
+ const pm_line_column_t location = *node_location;
+ if (!popped) PUSH_INSN(ret, location, putnil);
+
+ PM_COMPILE_NOT_POPPED(node->receiver);
+
+ int boff = (node->block == NULL ? 0 : 1);
+ int flag = PM_NODE_TYPE_P(node->receiver, PM_SELF_NODE) ? VM_CALL_FCALL : 0;
+ struct rb_callinfo_kwarg *keywords = NULL;
+ int argc = pm_setup_args(node->arguments, node->block, &flag, &keywords, iseq, ret, scope_node, node_location);
+
+ if ((argc > 0 || boff) && (flag & VM_CALL_KW_SPLAT)) {
+ if (boff) {
+ PUSH_INSN(ret, location, splatkw);
+ }
+ else {
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSN(ret, location, splatkw);
+ PUSH_INSN(ret, location, pop);
+ }
+ }
+
+ int dup_argn = argc + 1 + boff;
+ int keyword_len = 0;
+
+ if (keywords) {
+ keyword_len = keywords->keyword_len;
+ dup_argn += keyword_len;
+ }
+
+ PUSH_INSN1(ret, location, dupn, INT2FIX(dup_argn));
+ PUSH_SEND_R(ret, location, idAREF, INT2FIX(argc), NULL, INT2FIX(flag & ~(VM_CALL_ARGS_SPLAT_MUT | VM_CALL_KW_SPLAT_MUT)), keywords);
+ PM_COMPILE_NOT_POPPED(node->value);
+
+ ID id_operator = pm_constant_id_lookup(scope_node, node->operator);
+ PUSH_SEND(ret, location, id_operator, INT2FIX(1));
+
+ if (!popped) {
+ PUSH_INSN1(ret, location, setn, INT2FIX(dup_argn + 1));
+ }
+ if (flag & VM_CALL_ARGS_SPLAT) {
+ if (flag & VM_CALL_KW_SPLAT) {
+ PUSH_INSN1(ret, location, topn, INT2FIX(2 + boff));
+
+ if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
+ PUSH_INSN1(ret, location, splatarray, Qtrue);
+ flag |= VM_CALL_ARGS_SPLAT_MUT;
+ }
+
+ PUSH_INSN(ret, location, swap);
+ PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
+ PUSH_INSN1(ret, location, setn, INT2FIX(2 + boff));
+ PUSH_INSN(ret, location, pop);
+ }
+ else {
+ if (boff > 0) {
+ PUSH_INSN1(ret, location, dupn, INT2FIX(3));
+ PUSH_INSN(ret, location, swap);
+ PUSH_INSN(ret, location, pop);
+ }
+ if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
+ PUSH_INSN(ret, location, swap);
+ PUSH_INSN1(ret, location, splatarray, Qtrue);
+ PUSH_INSN(ret, location, swap);
+ flag |= VM_CALL_ARGS_SPLAT_MUT;
+ }
+ PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
+ if (boff > 0) {
+ PUSH_INSN1(ret, location, setn, INT2FIX(3));
+ PUSH_INSN(ret, location, pop);
+ PUSH_INSN(ret, location, pop);
+ }
+ }
+
+ PUSH_SEND_R(ret, location, idASET, INT2FIX(argc), NULL, INT2FIX(flag), keywords);
+ }
+ else if (flag & VM_CALL_KW_SPLAT) {
+ if (boff > 0) {
+ PUSH_INSN1(ret, location, topn, INT2FIX(2));
+ PUSH_INSN(ret, location, swap);
+ PUSH_INSN1(ret, location, setn, INT2FIX(3));
+ PUSH_INSN(ret, location, pop);
+ }
+ PUSH_INSN(ret, location, swap);
+ PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
+ }
+ else if (keyword_len) {
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 2));
+ PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 1));
+ PUSH_INSN(ret, location, pop);
+ PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
+ }
+ else {
+ if (boff > 0) {
+ PUSH_INSN(ret, location, swap);
+ }
+ PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
+ }
+
+ PUSH_INSN(ret, location, pop);
+}
+
+/**
+ * Compile an index control flow write node, which is a node that is writing a
+ * value using the [] and []= methods and the &&= and ||= operators. It looks
+ * like:
+ *
+ * foo[bar] ||= baz
+ *
+ * This breaks down to caching the receiver and arguments on the stack, calling
+ * the [] method, checking the result and then changing control flow based on
+ * it. If the value would result in a write, then the value is written using the
+ * []= method.
+ */
+static void
+pm_compile_index_control_flow_write_node(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_t *receiver, const pm_arguments_node_t *arguments, const pm_node_t *block, const pm_node_t *value, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
+{
+ const pm_line_column_t location = *node_location;
+ if (!popped) PUSH_INSN(ret, location, putnil);
+ PM_COMPILE_NOT_POPPED(receiver);
+
+ int boff = (block == NULL ? 0 : 1);
+ int flag = PM_NODE_TYPE_P(receiver, PM_SELF_NODE) ? VM_CALL_FCALL : 0;
+ struct rb_callinfo_kwarg *keywords = NULL;
+ int argc = pm_setup_args(arguments, block, &flag, &keywords, iseq, ret, scope_node, node_location);
+
+ if ((argc > 0 || boff) && (flag & VM_CALL_KW_SPLAT)) {
+ if (boff) {
+ PUSH_INSN(ret, location, splatkw);
+ }
+ else {
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSN(ret, location, splatkw);
+ PUSH_INSN(ret, location, pop);
+ }
+ }
+
+ int dup_argn = argc + 1 + boff;
+ int keyword_len = 0;
+
+ if (keywords) {
+ keyword_len = keywords->keyword_len;
+ dup_argn += keyword_len;
+ }
+
+ PUSH_INSN1(ret, location, dupn, INT2FIX(dup_argn));
+ PUSH_SEND_R(ret, location, idAREF, INT2FIX(argc), NULL, INT2FIX(flag & ~(VM_CALL_ARGS_SPLAT_MUT | VM_CALL_KW_SPLAT_MUT)), keywords);
+
+ LABEL *label = NEW_LABEL(location.line);
+ LABEL *lfin = NEW_LABEL(location.line);
+
+ PUSH_INSN(ret, location, dup);
+ if (PM_NODE_TYPE_P(node, PM_INDEX_AND_WRITE_NODE)) {
+ PUSH_INSNL(ret, location, branchunless, label);
+ }
+ else {
+ PUSH_INSNL(ret, location, branchif, label);
+ }
+
+ PUSH_INSN(ret, location, pop);
+ PM_COMPILE_NOT_POPPED(value);
+
+ if (!popped) {
+ PUSH_INSN1(ret, location, setn, INT2FIX(dup_argn + 1));
+ }
+
+ if (flag & VM_CALL_ARGS_SPLAT) {
+ if (flag & VM_CALL_KW_SPLAT) {
+ PUSH_INSN1(ret, location, topn, INT2FIX(2 + boff));
+ if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
+ PUSH_INSN1(ret, location, splatarray, Qtrue);
+ flag |= VM_CALL_ARGS_SPLAT_MUT;
+ }
+
+ PUSH_INSN(ret, location, swap);
+ PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
+ PUSH_INSN1(ret, location, setn, INT2FIX(2 + boff));
+ PUSH_INSN(ret, location, pop);
+ }
+ else {
+ if (boff > 0) {
+ PUSH_INSN1(ret, location, dupn, INT2FIX(3));
+ PUSH_INSN(ret, location, swap);
+ PUSH_INSN(ret, location, pop);
+ }
+ if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
+ PUSH_INSN(ret, location, swap);
+ PUSH_INSN1(ret, location, splatarray, Qtrue);
+ PUSH_INSN(ret, location, swap);
+ flag |= VM_CALL_ARGS_SPLAT_MUT;
+ }
+ PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
+ if (boff > 0) {
+ PUSH_INSN1(ret, location, setn, INT2FIX(3));
+ PUSH_INSN(ret, location, pop);
+ PUSH_INSN(ret, location, pop);
+ }
+ }
+
+ PUSH_SEND_R(ret, location, idASET, INT2FIX(argc), NULL, INT2FIX(flag), keywords);
+ }
+ else if (flag & VM_CALL_KW_SPLAT) {
+ if (boff > 0) {
+ PUSH_INSN1(ret, location, topn, INT2FIX(2));
+ PUSH_INSN(ret, location, swap);
+ PUSH_INSN1(ret, location, setn, INT2FIX(3));
+ PUSH_INSN(ret, location, pop);
+ }
+
+ PUSH_INSN(ret, location, swap);
+ PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
+ }
+ else if (keyword_len) {
+ PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 1));
+ PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 0));
+ PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
+ }
+ else {
+ if (boff > 0) {
+ PUSH_INSN(ret, location, swap);
+ }
+ PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
+ }
+
+ PUSH_INSN(ret, location, pop);
+ PUSH_INSNL(ret, location, jump, lfin);
+ PUSH_LABEL(ret, label);
+ if (!popped) {
+ PUSH_INSN1(ret, location, setn, INT2FIX(dup_argn + 1));
+ }
+ PUSH_INSN1(ret, location, adjuststack, INT2FIX(dup_argn + 1));
+ PUSH_LABEL(ret, lfin);
+}
+
+// When we compile a pattern matching expression, we use the stack as a scratch
+// space to store lots of different values (consider it like we have a pattern
+// matching function and we need space for a bunch of different local
+// variables). The "base index" refers to the index on the stack where we
+// started compiling the pattern matching expression. These offsets from that
+// base index indicate the location of the various locals we need.
+#define PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE 0
+#define PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING 1
+#define PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P 2
+#define PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE 3
+#define PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY 4
+
+// A forward declaration because this is the recursive function that handles
+// compiling a pattern. It can be reentered by nesting patterns, as in the case
+// of arrays or hashes.
+static int pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *matched_label, LABEL *unmatched_label, bool in_single_pattern, bool in_alternation_pattern, bool use_deconstructed_cache, unsigned int base_index);
+
+/**
+ * This function generates the code to set up the error string and error_p
+ * locals depending on whether or not the pattern matched.
+ */
+static int
+pm_compile_pattern_generic_error(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, VALUE message, unsigned int base_index)
+{
+ const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
+ LABEL *match_succeeded_label = NEW_LABEL(location.line);
+
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSNL(ret, location, branchif, match_succeeded_label);
+
+ PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ PUSH_INSN1(ret, location, putobject, message);
+ PUSH_INSN1(ret, location, topn, INT2FIX(3));
+ PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(2));
+ PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
+
+ PUSH_INSN1(ret, location, putobject, Qfalse);
+ PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
+
+ PUSH_INSN(ret, location, pop);
+ PUSH_INSN(ret, location, pop);
+ PUSH_LABEL(ret, match_succeeded_label);
+
+ return COMPILE_OK;
+}
+
+/**
+ * This function generates the code to set up the error string and error_p
+ * locals depending on whether or not the pattern matched when the value needs
+ * to match a specific deconstructed length.
+ */
+static int
+pm_compile_pattern_length_error(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, VALUE message, VALUE length, unsigned int base_index)
+{
+ const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
+ LABEL *match_succeeded_label = NEW_LABEL(location.line);
+
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSNL(ret, location, branchif, match_succeeded_label);
+
+ PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ PUSH_INSN1(ret, location, putobject, message);
+ PUSH_INSN1(ret, location, topn, INT2FIX(3));
+ PUSH_INSN(ret, location, dup);
+ PUSH_SEND(ret, location, idLength, INT2FIX(0));
+ PUSH_INSN1(ret, location, putobject, length);
+ PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(4));
+ PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
+
+ PUSH_INSN1(ret, location, putobject, Qfalse);
+ PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
+
+ PUSH_INSN(ret, location, pop);
+ PUSH_INSN(ret, location, pop);
+ PUSH_LABEL(ret, match_succeeded_label);
+
+ return COMPILE_OK;
+}
+
+/**
+ * This function generates the code to set up the error string and error_p
+ * locals depending on whether or not the pattern matched when the value needs
+ * to pass a specific #=== method call.
+ */
+static int
+pm_compile_pattern_eqq_error(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, unsigned int base_index)
+{
+ const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
+ LABEL *match_succeeded_label = NEW_LABEL(location.line);
+
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSNL(ret, location, branchif, match_succeeded_label);
+
+ PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ PUSH_INSN1(ret, location, putobject, rb_fstring_lit("%p === %p does not return true"));
+ PUSH_INSN1(ret, location, topn, INT2FIX(3));
+ PUSH_INSN1(ret, location, topn, INT2FIX(5));
+ PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(3));
+ PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
+ PUSH_INSN1(ret, location, putobject, Qfalse);
+ PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
+ PUSH_INSN(ret, location, pop);
+ PUSH_INSN(ret, location, pop);
+
+ PUSH_LABEL(ret, match_succeeded_label);
+ PUSH_INSN1(ret, location, setn, INT2FIX(2));
+ PUSH_INSN(ret, location, pop);
+ PUSH_INSN(ret, location, pop);
+
+ return COMPILE_OK;
+}
+
+/**
+ * This is a variation on compiling a pattern matching expression that is used
+ * to have the pattern matching instructions fall through to immediately after
+ * the pattern if it passes. Otherwise it jumps to the given unmatched_label
+ * label.
+ */
+static int
+pm_compile_pattern_match(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *unmatched_label, bool in_single_pattern, bool in_alternation_pattern, bool use_deconstructed_cache, unsigned int base_index)
+{
+ LABEL *matched_label = NEW_LABEL(pm_node_line_number(scope_node->parser, node));
+ CHECK(pm_compile_pattern(iseq, scope_node, node, ret, matched_label, unmatched_label, in_single_pattern, in_alternation_pattern, use_deconstructed_cache, base_index));
+ PUSH_LABEL(ret, matched_label);
+ return COMPILE_OK;
+}
+
+/**
+ * This function compiles in the code necessary to call #deconstruct on the
+ * value to match against. It raises appropriate errors if the method does not
+ * exist or if it returns the wrong type.
+ */
+static int
+pm_compile_pattern_deconstruct(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *deconstruct_label, LABEL *match_failed_label, LABEL *deconstructed_label, LABEL *type_error_label, bool in_single_pattern, bool use_deconstructed_cache, unsigned int base_index)
+{
+ const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
+
+ if (use_deconstructed_cache) {
+ PUSH_INSN1(ret, location, topn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE));
+ PUSH_INSNL(ret, location, branchnil, deconstruct_label);
+
+ PUSH_INSN1(ret, location, topn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE));
+ PUSH_INSNL(ret, location, branchunless, match_failed_label);
+
+ PUSH_INSN(ret, location, pop);
+ PUSH_INSN1(ret, location, topn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE - 1));
+ PUSH_INSNL(ret, location, jump, deconstructed_label);
+ }
+ else {
+ PUSH_INSNL(ret, location, jump, deconstruct_label);
+ }
+
+ PUSH_LABEL(ret, deconstruct_label);
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSN1(ret, location, putobject, ID2SYM(rb_intern("deconstruct")));
+ PUSH_SEND(ret, location, idRespond_to, INT2FIX(1));
+
+ if (use_deconstructed_cache) {
+ PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE + 1));
+ }
+
+ if (in_single_pattern) {
+ CHECK(pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("%p does not respond to #deconstruct"), base_index + 1));
+ }
+
+ PUSH_INSNL(ret, location, branchunless, match_failed_label);
+ PUSH_SEND(ret, location, rb_intern("deconstruct"), INT2FIX(0));
+
+ if (use_deconstructed_cache) {
+ PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE));
+ }
+
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSN1(ret, location, checktype, INT2FIX(T_ARRAY));
+ PUSH_INSNL(ret, location, branchunless, type_error_label);
+ PUSH_LABEL(ret, deconstructed_label);
+
+ return COMPILE_OK;
+}
+
+/**
+ * This function compiles in the code necessary to match against the optional
+ * constant path that is attached to an array, find, or hash pattern.
+ */
+static int
+pm_compile_pattern_constant(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *match_failed_label, bool in_single_pattern, unsigned int base_index)
+{
+ const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
+
+ PUSH_INSN(ret, location, dup);
+ PM_COMPILE_NOT_POPPED(node);
+
+ if (in_single_pattern) {
+ PUSH_INSN1(ret, location, dupn, INT2FIX(2));
+ }
+ PUSH_INSN1(ret, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
+ if (in_single_pattern) {
+ CHECK(pm_compile_pattern_eqq_error(iseq, scope_node, node, ret, base_index + 3));
+ }
+ PUSH_INSNL(ret, location, branchunless, match_failed_label);
+ return COMPILE_OK;
+}
+
+/**
+ * When matching fails, an appropriate error must be raised. This function is
+ * responsible for compiling in those error raising instructions.
+ */
+static void
+pm_compile_pattern_error_handler(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *done_label, bool popped)
+{
+ const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
+ LABEL *key_error_label = NEW_LABEL(location.line);
+ LABEL *cleanup_label = NEW_LABEL(location.line);
+
+ struct rb_callinfo_kwarg *kw_arg = rb_xmalloc_mul_add(2, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
+ kw_arg->references = 0;
+ kw_arg->keyword_len = 2;
+ kw_arg->keywords[0] = ID2SYM(rb_intern("matchee"));
+ kw_arg->keywords[1] = ID2SYM(rb_intern("key"));
+
+ PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
+ PUSH_INSNL(ret, location, branchif, key_error_label);
+
+ PUSH_INSN1(ret, location, putobject, rb_eNoMatchingPatternError);
+ PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ PUSH_INSN1(ret, location, putobject, rb_fstring_lit("%p: %s"));
+ PUSH_INSN1(ret, location, topn, INT2FIX(4));
+ PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 6));
+ PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(3));
+ PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
+ PUSH_INSNL(ret, location, jump, cleanup_label);
+
+ PUSH_LABEL(ret, key_error_label);
+ PUSH_INSN1(ret, location, putobject, rb_eNoMatchingPatternKeyError);
+ PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ PUSH_INSN1(ret, location, putobject, rb_fstring_lit("%p: %s"));
+ PUSH_INSN1(ret, location, topn, INT2FIX(4));
+ PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 6));
+ PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(3));
+ PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE + 4));
+ PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY + 5));
+ PUSH_SEND_R(ret, location, rb_intern("new"), INT2FIX(1), NULL, INT2FIX(VM_CALL_KWARG), kw_arg);
+ PUSH_SEND(ret, location, id_core_raise, INT2FIX(1));
+ PUSH_LABEL(ret, cleanup_label);
+
+ PUSH_INSN1(ret, location, adjuststack, INT2FIX(7));
+ if (!popped) PUSH_INSN(ret, location, putnil);
+ PUSH_INSNL(ret, location, jump, done_label);
+ PUSH_INSN1(ret, location, dupn, INT2FIX(5));
+ if (popped) PUSH_INSN(ret, location, putnil);
+}
+
+/**
+ * Compile a pattern matching expression.
+ */
+static int
+pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *matched_label, LABEL *unmatched_label, bool in_single_pattern, bool in_alternation_pattern, bool use_deconstructed_cache, unsigned int base_index)
+{
+ const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
+
+ switch (PM_NODE_TYPE(node)) {
+ case PM_ARRAY_PATTERN_NODE: {
+ // Array patterns in pattern matching are triggered by using commas in
+ // a pattern or wrapping it in braces. They are represented by a
+ // ArrayPatternNode. This looks like:
+ //
+ // foo => [1, 2, 3]
+ //
+ // It can optionally have a splat in the middle of it, which can
+ // optionally have a name attached.
+ const pm_array_pattern_node_t *cast = (const pm_array_pattern_node_t *) node;
+
+ const size_t requireds_size = cast->requireds.size;
+ const size_t posts_size = cast->posts.size;
+ const size_t minimum_size = requireds_size + posts_size;
+
+ bool rest_named = false;
+ bool use_rest_size = false;
+
+ if (cast->rest != NULL) {
+ rest_named = (PM_NODE_TYPE_P(cast->rest, PM_SPLAT_NODE) && ((const pm_splat_node_t *) cast->rest)->expression != NULL);
+ use_rest_size = (rest_named || (!rest_named && posts_size > 0));
+ }
+
+ LABEL *match_failed_label = NEW_LABEL(location.line);
+ LABEL *type_error_label = NEW_LABEL(location.line);
+ LABEL *deconstruct_label = NEW_LABEL(location.line);
+ LABEL *deconstructed_label = NEW_LABEL(location.line);
+
+ if (use_rest_size) {
+ PUSH_INSN1(ret, location, putobject, INT2FIX(0));
+ PUSH_INSN(ret, location, swap);
+ base_index++;
+ }
+
+ if (cast->constant != NULL) {
+ CHECK(pm_compile_pattern_constant(iseq, scope_node, cast->constant, ret, match_failed_label, in_single_pattern, base_index));
+ }
+
+ CHECK(pm_compile_pattern_deconstruct(iseq, scope_node, node, ret, deconstruct_label, match_failed_label, deconstructed_label, type_error_label, in_single_pattern, use_deconstructed_cache, base_index));
+
+ PUSH_INSN(ret, location, dup);
+ PUSH_SEND(ret, location, idLength, INT2FIX(0));
+ PUSH_INSN1(ret, location, putobject, INT2FIX(minimum_size));
+ PUSH_SEND(ret, location, cast->rest == NULL ? idEq : idGE, INT2FIX(1));
+ if (in_single_pattern) {
+ VALUE message = cast->rest == NULL ? rb_fstring_lit("%p length mismatch (given %p, expected %p)") : rb_fstring_lit("%p length mismatch (given %p, expected %p+)");
+ CHECK(pm_compile_pattern_length_error(iseq, scope_node, node, ret, message, INT2FIX(minimum_size), base_index + 1));
+ }
+ PUSH_INSNL(ret, location, branchunless, match_failed_label);
+
+ for (size_t index = 0; index < requireds_size; index++) {
+ const pm_node_t *required = cast->requireds.nodes[index];
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSN1(ret, location, putobject, INT2FIX(index));
+ PUSH_SEND(ret, location, idAREF, INT2FIX(1));
+ CHECK(pm_compile_pattern_match(iseq, scope_node, required, ret, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 1));
+ }
+
+ if (cast->rest != NULL) {
+ if (rest_named) {
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSN1(ret, location, putobject, INT2FIX(requireds_size));
+ PUSH_INSN1(ret, location, topn, INT2FIX(1));
+ PUSH_SEND(ret, location, idLength, INT2FIX(0));
+ PUSH_INSN1(ret, location, putobject, INT2FIX(minimum_size));
+ PUSH_SEND(ret, location, idMINUS, INT2FIX(1));
+ PUSH_INSN1(ret, location, setn, INT2FIX(4));
+ PUSH_SEND(ret, location, idAREF, INT2FIX(2));
+ CHECK(pm_compile_pattern_match(iseq, scope_node, ((const pm_splat_node_t *) cast->rest)->expression, ret, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 1));
+ }
+ else if (posts_size > 0) {
+ PUSH_INSN(ret, location, dup);
+ PUSH_SEND(ret, location, idLength, INT2FIX(0));
+ PUSH_INSN1(ret, location, putobject, INT2FIX(minimum_size));
+ PUSH_SEND(ret, location, idMINUS, INT2FIX(1));
+ PUSH_INSN1(ret, location, setn, INT2FIX(2));
+ PUSH_INSN(ret, location, pop);
+ }
+ }
+
+ for (size_t index = 0; index < posts_size; index++) {
+ const pm_node_t *post = cast->posts.nodes[index];
+ PUSH_INSN(ret, location, dup);
+
+ PUSH_INSN1(ret, location, putobject, INT2FIX(requireds_size + index));
+ PUSH_INSN1(ret, location, topn, INT2FIX(3));
+ PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
+ PUSH_SEND(ret, location, idAREF, INT2FIX(1));
+ CHECK(pm_compile_pattern_match(iseq, scope_node, post, ret, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 1));
+ }
+
+ PUSH_INSN(ret, location, pop);
+ if (use_rest_size) {
+ PUSH_INSN(ret, location, pop);
+ }
+
+ PUSH_INSNL(ret, location, jump, matched_label);
+ PUSH_INSN(ret, location, putnil);
+ if (use_rest_size) {
+ PUSH_INSN(ret, location, putnil);
+ }
+
+ PUSH_LABEL(ret, type_error_label);
+ PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ PUSH_INSN1(ret, location, putobject, rb_eTypeError);
+ PUSH_INSN1(ret, location, putobject, rb_fstring_lit("deconstruct must return Array"));
+ PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
+ PUSH_INSN(ret, location, pop);
+
+ PUSH_LABEL(ret, match_failed_label);
+ PUSH_INSN(ret, location, pop);
+ if (use_rest_size) {
+ PUSH_INSN(ret, location, pop);
+ }
+
+ PUSH_INSNL(ret, location, jump, unmatched_label);
+ break;
+ }
+ case PM_FIND_PATTERN_NODE: {
+ // Find patterns in pattern matching are triggered by using commas in
+ // a pattern or wrapping it in braces and using a splat on both the left
+ // and right side of the pattern. This looks like:
+ //
+ // foo => [*, 1, 2, 3, *]
+ //
+ // There can be any number of requireds in the middle. The splats on
+ // both sides can optionally have names attached.
+ const pm_find_pattern_node_t *cast = (const pm_find_pattern_node_t *) node;
+ const size_t size = cast->requireds.size;
+
+ LABEL *match_failed_label = NEW_LABEL(location.line);
+ LABEL *type_error_label = NEW_LABEL(location.line);
+ LABEL *deconstruct_label = NEW_LABEL(location.line);
+ LABEL *deconstructed_label = NEW_LABEL(location.line);
+
+ if (cast->constant) {
+ CHECK(pm_compile_pattern_constant(iseq, scope_node, cast->constant, ret, match_failed_label, in_single_pattern, base_index));
+ }
+
+ CHECK(pm_compile_pattern_deconstruct(iseq, scope_node, node, ret, deconstruct_label, match_failed_label, deconstructed_label, type_error_label, in_single_pattern, use_deconstructed_cache, base_index));
+
+ PUSH_INSN(ret, location, dup);
+ PUSH_SEND(ret, location, idLength, INT2FIX(0));
+ PUSH_INSN1(ret, location, putobject, INT2FIX(size));
+ PUSH_SEND(ret, location, idGE, INT2FIX(1));
+ if (in_single_pattern) {
+ CHECK(pm_compile_pattern_length_error(iseq, scope_node, node, ret, rb_fstring_lit("%p length mismatch (given %p, expected %p+)"), INT2FIX(size), base_index + 1));
+ }
+ PUSH_INSNL(ret, location, branchunless, match_failed_label);
+
+ {
+ LABEL *while_begin_label = NEW_LABEL(location.line);
+ LABEL *next_loop_label = NEW_LABEL(location.line);
+ LABEL *find_succeeded_label = NEW_LABEL(location.line);
+ LABEL *find_failed_label = NEW_LABEL(location.line);
+
+ PUSH_INSN(ret, location, dup);
+ PUSH_SEND(ret, location, idLength, INT2FIX(0));
+
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSN1(ret, location, putobject, INT2FIX(size));
+ PUSH_SEND(ret, location, idMINUS, INT2FIX(1));
+ PUSH_INSN1(ret, location, putobject, INT2FIX(0));
+ PUSH_LABEL(ret, while_begin_label);
+
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSN1(ret, location, topn, INT2FIX(2));
+ PUSH_SEND(ret, location, idLE, INT2FIX(1));
+ PUSH_INSNL(ret, location, branchunless, find_failed_label);
+
+ for (size_t index = 0; index < size; index++) {
+ PUSH_INSN1(ret, location, topn, INT2FIX(3));
+ PUSH_INSN1(ret, location, topn, INT2FIX(1));
+
+ if (index != 0) {
+ PUSH_INSN1(ret, location, putobject, INT2FIX(index));
+ PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
+ }
+
+ PUSH_SEND(ret, location, idAREF, INT2FIX(1));
+ CHECK(pm_compile_pattern_match(iseq, scope_node, cast->requireds.nodes[index], ret, next_loop_label, in_single_pattern, in_alternation_pattern, false, base_index + 4));
+ }
+
+ RUBY_ASSERT(PM_NODE_TYPE_P(cast->left, PM_SPLAT_NODE));
+ const pm_splat_node_t *left = (const pm_splat_node_t *) cast->left;
+
+ if (left->expression != NULL) {
+ PUSH_INSN1(ret, location, topn, INT2FIX(3));
+ PUSH_INSN1(ret, location, putobject, INT2FIX(0));
+ PUSH_INSN1(ret, location, topn, INT2FIX(2));
+ PUSH_SEND(ret, location, idAREF, INT2FIX(2));
+ CHECK(pm_compile_pattern_match(iseq, scope_node, left->expression, ret, find_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 4));
+ }
+
+ RUBY_ASSERT(PM_NODE_TYPE_P(cast->right, PM_SPLAT_NODE));
+ const pm_splat_node_t *right = (const pm_splat_node_t *) cast->right;
+
+ if (right->expression != NULL) {
+ PUSH_INSN1(ret, location, topn, INT2FIX(3));
+ PUSH_INSN1(ret, location, topn, INT2FIX(1));
+ PUSH_INSN1(ret, location, putobject, INT2FIX(size));
+ PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
+ PUSH_INSN1(ret, location, topn, INT2FIX(3));
+ PUSH_SEND(ret, location, idAREF, INT2FIX(2));
+ pm_compile_pattern_match(iseq, scope_node, right->expression, ret, find_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 4);
+ }
+
+ PUSH_INSNL(ret, location, jump, find_succeeded_label);
+
+ PUSH_LABEL(ret, next_loop_label);
+ PUSH_INSN1(ret, location, putobject, INT2FIX(1));
+ PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
+ PUSH_INSNL(ret, location, jump, while_begin_label);
+
+ PUSH_LABEL(ret, find_failed_label);
+ PUSH_INSN1(ret, location, adjuststack, INT2FIX(3));
+ if (in_single_pattern) {
+ PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ PUSH_INSN1(ret, location, putobject, rb_fstring_lit("%p does not match to find pattern"));
+ PUSH_INSN1(ret, location, topn, INT2FIX(2));
+ PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(2));
+ PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
+
+ PUSH_INSN1(ret, location, putobject, Qfalse);
+ PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
+
+ PUSH_INSN(ret, location, pop);
+ PUSH_INSN(ret, location, pop);
+ }
+ PUSH_INSNL(ret, location, jump, match_failed_label);
+ PUSH_INSN1(ret, location, dupn, INT2FIX(3));
+
+ PUSH_LABEL(ret, find_succeeded_label);
+ PUSH_INSN1(ret, location, adjuststack, INT2FIX(3));
+ }
+
+ PUSH_INSN(ret, location, pop);
+ PUSH_INSNL(ret, location, jump, matched_label);
+ PUSH_INSN(ret, location, putnil);
+
+ PUSH_LABEL(ret, type_error_label);
+ PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ PUSH_INSN1(ret, location, putobject, rb_eTypeError);
+ PUSH_INSN1(ret, location, putobject, rb_fstring_lit("deconstruct must return Array"));
+ PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
+ PUSH_INSN(ret, location, pop);
+
+ PUSH_LABEL(ret, match_failed_label);
+ PUSH_INSN(ret, location, pop);
+ PUSH_INSNL(ret, location, jump, unmatched_label);
+
+ break;
+ }
+ case PM_HASH_PATTERN_NODE: {
+ // Hash patterns in pattern matching are triggered by using labels and
+ // values in a pattern or by using the ** operator. They are represented
+ // by the HashPatternNode. This looks like:
+ //
+ // foo => { a: 1, b: 2, **bar }
+ //
+ // It can optionally have an assoc splat in the middle of it, which can
+ // optionally have a name.
+ const pm_hash_pattern_node_t *cast = (const pm_hash_pattern_node_t *) node;
+
+ // We don't consider it a "rest" parameter if it's a ** that is unnamed.
+ bool has_rest = cast->rest != NULL && !(PM_NODE_TYPE_P(cast->rest, PM_ASSOC_SPLAT_NODE) && ((const pm_assoc_splat_node_t *) cast->rest)->value == NULL);
+ bool has_keys = cast->elements.size > 0 || cast->rest != NULL;
+
+ LABEL *match_failed_label = NEW_LABEL(location.line);
+ LABEL *type_error_label = NEW_LABEL(location.line);
+ VALUE keys = Qnil;
+
+ if (has_keys && !has_rest) {
+ keys = rb_ary_new_capa(cast->elements.size);
+
+ for (size_t index = 0; index < cast->elements.size; index++) {
+ const pm_node_t *element = cast->elements.nodes[index];
+ RUBY_ASSERT(PM_NODE_TYPE_P(element, PM_ASSOC_NODE));
+
+ const pm_node_t *key = ((const pm_assoc_node_t *) element)->key;
+ RUBY_ASSERT(PM_NODE_TYPE_P(key, PM_SYMBOL_NODE));
+
+ VALUE symbol = ID2SYM(parse_string_symbol(scope_node, (const pm_symbol_node_t *) key));
+ rb_ary_push(keys, symbol);
+ }
+ }
+
+ if (cast->constant) {
+ CHECK(pm_compile_pattern_constant(iseq, scope_node, cast->constant, ret, match_failed_label, in_single_pattern, base_index));
+ }
+
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSN1(ret, location, putobject, ID2SYM(rb_intern("deconstruct_keys")));
+ PUSH_SEND(ret, location, idRespond_to, INT2FIX(1));
+ if (in_single_pattern) {
+ CHECK(pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("%p does not respond to #deconstruct_keys"), base_index + 1));
+ }
+ PUSH_INSNL(ret, location, branchunless, match_failed_label);
+
+ if (NIL_P(keys)) {
+ PUSH_INSN(ret, location, putnil);
+ }
+ else {
+ PUSH_INSN1(ret, location, duparray, keys);
+ RB_OBJ_WRITTEN(iseq, Qundef, rb_obj_hide(keys));
+ }
+ PUSH_SEND(ret, location, rb_intern("deconstruct_keys"), INT2FIX(1));
+
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSN1(ret, location, checktype, INT2FIX(T_HASH));
+ PUSH_INSNL(ret, location, branchunless, type_error_label);
+
+ if (has_rest) {
+ PUSH_SEND(ret, location, rb_intern("dup"), INT2FIX(0));
+ }
+
+ if (has_keys) {
+ DECL_ANCHOR(match_values);
+ INIT_ANCHOR(match_values);
+
+ for (size_t index = 0; index < cast->elements.size; index++) {
+ const pm_node_t *element = cast->elements.nodes[index];
+ RUBY_ASSERT(PM_NODE_TYPE_P(element, PM_ASSOC_NODE));
+
+ const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
+ const pm_node_t *key = assoc->key;
+ RUBY_ASSERT(PM_NODE_TYPE_P(key, PM_SYMBOL_NODE));
+
+ VALUE symbol = ID2SYM(parse_string_symbol(scope_node, (const pm_symbol_node_t *) key));
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSN1(ret, location, putobject, symbol);
+ PUSH_SEND(ret, location, rb_intern("key?"), INT2FIX(1));
+
+ if (in_single_pattern) {
+ LABEL *match_succeeded_label = NEW_LABEL(location.line);
+
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSNL(ret, location, branchif, match_succeeded_label);
+
+ PUSH_INSN1(ret, location, putobject, rb_str_freeze(rb_sprintf("key not found: %+"PRIsVALUE, symbol)));
+ PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 2));
+ PUSH_INSN1(ret, location, putobject, Qtrue);
+ PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 3));
+ PUSH_INSN1(ret, location, topn, INT2FIX(3));
+ PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE + 4));
+ PUSH_INSN1(ret, location, putobject, symbol);
+ PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY + 5));
+
+ PUSH_INSN1(ret, location, adjuststack, INT2FIX(4));
+ PUSH_LABEL(ret, match_succeeded_label);
+ }
+
+ PUSH_INSNL(ret, location, branchunless, match_failed_label);
+ PUSH_INSN(match_values, location, dup);
+ PUSH_INSN1(match_values, location, putobject, symbol);
+ PUSH_SEND(match_values, location, has_rest ? rb_intern("delete") : idAREF, INT2FIX(1));
+
+ const pm_node_t *value = assoc->value;
+ if (PM_NODE_TYPE_P(value, PM_IMPLICIT_NODE)) {
+ value = ((const pm_implicit_node_t *) value)->value;
+ }
+
+ CHECK(pm_compile_pattern_match(iseq, scope_node, value, match_values, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 1));
+ }
+
+ PUSH_SEQ(ret, match_values);
+ }
+ else {
+ PUSH_INSN(ret, location, dup);
+ PUSH_SEND(ret, location, idEmptyP, INT2FIX(0));
+ if (in_single_pattern) {
+ CHECK(pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("%p is not empty"), base_index + 1));
+ }
+ PUSH_INSNL(ret, location, branchunless, match_failed_label);
+ }
+
+ if (has_rest) {
+ switch (PM_NODE_TYPE(cast->rest)) {
+ case PM_NO_KEYWORDS_PARAMETER_NODE: {
+ PUSH_INSN(ret, location, dup);
+ PUSH_SEND(ret, location, idEmptyP, INT2FIX(0));
+ if (in_single_pattern) {
+ pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("rest of %p is not empty"), base_index + 1);
+ }
+ PUSH_INSNL(ret, location, branchunless, match_failed_label);
+ break;
+ }
+ case PM_ASSOC_SPLAT_NODE: {
+ const pm_assoc_splat_node_t *splat = (const pm_assoc_splat_node_t *) cast->rest;
+ PUSH_INSN(ret, location, dup);
+ pm_compile_pattern_match(iseq, scope_node, splat->value, ret, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 1);
+ break;
+ }
+ default:
+ rb_bug("unreachable");
+ break;
+ }
+ }
+
+ PUSH_INSN(ret, location, pop);
+ PUSH_INSNL(ret, location, jump, matched_label);
+ PUSH_INSN(ret, location, putnil);
+
+ PUSH_LABEL(ret, type_error_label);
+ PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ PUSH_INSN1(ret, location, putobject, rb_eTypeError);
+ PUSH_INSN1(ret, location, putobject, rb_fstring_lit("deconstruct_keys must return Hash"));
+ PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
+ PUSH_INSN(ret, location, pop);
+
+ PUSH_LABEL(ret, match_failed_label);
+ PUSH_INSN(ret, location, pop);
+ PUSH_INSNL(ret, location, jump, unmatched_label);
+ break;
+ }
+ case PM_CAPTURE_PATTERN_NODE: {
+ // Capture patterns allow you to pattern match against an element in a
+ // pattern and also capture the value into a local variable. This looks
+ // like:
+ //
+ // [1] => [Integer => foo]
+ //
+ // In this case the `Integer => foo` will be represented by a
+ // CapturePatternNode, which has both a value (the pattern to match
+ // against) and a target (the place to write the variable into).
+ const pm_capture_pattern_node_t *cast = (const pm_capture_pattern_node_t *) node;
+
+ LABEL *match_failed_label = NEW_LABEL(location.line);
+
+ PUSH_INSN(ret, location, dup);
+ CHECK(pm_compile_pattern_match(iseq, scope_node, cast->value, ret, match_failed_label, in_single_pattern, in_alternation_pattern, use_deconstructed_cache, base_index + 1));
+ CHECK(pm_compile_pattern(iseq, scope_node, cast->target, ret, matched_label, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index));
+ PUSH_INSN(ret, location, putnil);
+
+ PUSH_LABEL(ret, match_failed_label);
+ PUSH_INSN(ret, location, pop);
+ PUSH_INSNL(ret, location, jump, unmatched_label);
+
+ break;
+ }
+ case PM_LOCAL_VARIABLE_TARGET_NODE: {
+ // Local variables can be targeted by placing identifiers in the place
+ // of a pattern. For example, foo in bar. This results in the value
+ // being matched being written to that local variable.
+ const pm_local_variable_target_node_t *cast = (const pm_local_variable_target_node_t *) node;
+ pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
+
+ // If this local variable is being written from within an alternation
+ // pattern, then it cannot actually be added to the local table since
+ // it's ambiguous which value should be used. So instead we indicate
+ // this with a compile error.
+ if (in_alternation_pattern) {
+ ID id = pm_constant_id_lookup(scope_node, cast->name);
+ const char *name = rb_id2name(id);
+
+ if (name && strlen(name) > 0 && name[0] != '_') {
+ COMPILE_ERROR(ERROR_ARGS "illegal variable in alternative pattern (%"PRIsVALUE")", rb_id2str(id));
+ return COMPILE_NG;
+ }
+ }
+
+ PUSH_SETLOCAL(ret, location, index.index, index.level);
+ PUSH_INSNL(ret, location, jump, matched_label);
+ break;
+ }
+ case PM_ALTERNATION_PATTERN_NODE: {
+ // Alternation patterns allow you to specify multiple patterns in a
+ // single expression using the | operator.
+ const pm_alternation_pattern_node_t *cast = (const pm_alternation_pattern_node_t *) node;
+
+ LABEL *matched_left_label = NEW_LABEL(location.line);
+ LABEL *unmatched_left_label = NEW_LABEL(location.line);
+
+ // First, we're going to attempt to match against the left pattern. If
+ // that pattern matches, then we'll skip matching the right pattern.
+ PUSH_INSN(ret, location, dup);
+ CHECK(pm_compile_pattern(iseq, scope_node, cast->left, ret, matched_left_label, unmatched_left_label, in_single_pattern, true, true, base_index + 1));
+
+ // If we get here, then we matched on the left pattern. In this case we
+ // should pop out the duplicate value that we preemptively added to
+ // match against the right pattern and then jump to the match label.
+ PUSH_LABEL(ret, matched_left_label);
+ PUSH_INSN(ret, location, pop);
+ PUSH_INSNL(ret, location, jump, matched_label);
+ PUSH_INSN(ret, location, putnil);
+
+ // If we get here, then we didn't match on the left pattern. In this
+ // case we attempt to match against the right pattern.
+ PUSH_LABEL(ret, unmatched_left_label);
+ CHECK(pm_compile_pattern(iseq, scope_node, cast->right, ret, matched_label, unmatched_label, in_single_pattern, true, true, base_index));
+ break;
+ }
+ case PM_PARENTHESES_NODE:
+ // Parentheses are allowed to wrap expressions in pattern matching and
+ // they do nothing since they can only wrap individual expressions and
+ // not groups. In this case we'll recurse back into this same function
+ // with the body of the parentheses.
+ return pm_compile_pattern(iseq, scope_node, ((const pm_parentheses_node_t *) node)->body, ret, matched_label, unmatched_label, in_single_pattern, in_alternation_pattern, use_deconstructed_cache, base_index);
+ case PM_PINNED_EXPRESSION_NODE:
+ // Pinned expressions are a way to match against the value of an
+ // expression that should be evaluated at runtime. This looks like:
+ // foo in ^(bar). To compile these, we compile the expression as if it
+ // were a literal value by falling through to the literal case.
+ node = ((const pm_pinned_expression_node_t *) node)->expression;
+ /* fallthrough */
+ case PM_ARRAY_NODE:
+ case PM_CLASS_VARIABLE_READ_NODE:
+ case PM_CONSTANT_PATH_NODE:
+ case PM_CONSTANT_READ_NODE:
+ case PM_FALSE_NODE:
+ case PM_FLOAT_NODE:
+ case PM_GLOBAL_VARIABLE_READ_NODE:
+ case PM_IMAGINARY_NODE:
+ case PM_INSTANCE_VARIABLE_READ_NODE:
+ case PM_INTEGER_NODE:
+ case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
+ case PM_INTERPOLATED_STRING_NODE:
+ case PM_INTERPOLATED_SYMBOL_NODE:
+ case PM_INTERPOLATED_X_STRING_NODE:
+ case PM_LAMBDA_NODE:
+ case PM_LOCAL_VARIABLE_READ_NODE:
+ case PM_NIL_NODE:
+ case PM_SOURCE_ENCODING_NODE:
+ case PM_SOURCE_FILE_NODE:
+ case PM_SOURCE_LINE_NODE:
+ case PM_RANGE_NODE:
+ case PM_RATIONAL_NODE:
+ case PM_REGULAR_EXPRESSION_NODE:
+ case PM_SELF_NODE:
+ case PM_STRING_NODE:
+ case PM_SYMBOL_NODE:
+ case PM_TRUE_NODE:
+ case PM_X_STRING_NODE: {
+ // These nodes are all simple patterns, which means we'll use the
+ // checkmatch instruction to match against them, which is effectively a
+ // VM-level === operator.
+ PM_COMPILE_NOT_POPPED(node);
+ if (in_single_pattern) {
+ PUSH_INSN1(ret, location, dupn, INT2FIX(2));
+ }
+
+ PUSH_INSN1(ret, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
+
+ if (in_single_pattern) {
+ pm_compile_pattern_eqq_error(iseq, scope_node, node, ret, base_index + 2);
+ }
+
+ PUSH_INSNL(ret, location, branchif, matched_label);
+ PUSH_INSNL(ret, location, jump, unmatched_label);
+ break;
+ }
+ case PM_PINNED_VARIABLE_NODE: {
+ // Pinned variables are a way to match against the value of a variable
+ // without it looking like you're trying to write to the variable. This
+ // looks like: foo in ^@bar. To compile these, we compile the variable
+ // that they hold.
+ const pm_pinned_variable_node_t *cast = (const pm_pinned_variable_node_t *) node;
+ CHECK(pm_compile_pattern(iseq, scope_node, cast->variable, ret, matched_label, unmatched_label, in_single_pattern, in_alternation_pattern, true, base_index));
+ break;
+ }
+ case PM_IF_NODE:
+ case PM_UNLESS_NODE: {
+ // If and unless nodes can show up here as guards on `in` clauses. This
+ // looks like:
+ //
+ // case foo
+ // in bar if baz?
+ // qux
+ // end
+ //
+ // Because we know they're in the modifier form and they can't have any
+ // variation on this pattern, we compile them differently (more simply)
+ // here than we would in the normal compilation path.
+ const pm_node_t *predicate;
+ const pm_node_t *statement;
+
+ if (PM_NODE_TYPE_P(node, PM_IF_NODE)) {
+ const pm_if_node_t *cast = (const pm_if_node_t *) node;
+ predicate = cast->predicate;
+
+ RUBY_ASSERT(cast->statements != NULL && cast->statements->body.size == 1);
+ statement = cast->statements->body.nodes[0];
+ }
+ else {
+ const pm_unless_node_t *cast = (const pm_unless_node_t *) node;
+ predicate = cast->predicate;
+
+ RUBY_ASSERT(cast->statements != NULL && cast->statements->body.size == 1);
+ statement = cast->statements->body.nodes[0];
+ }
+
+ CHECK(pm_compile_pattern_match(iseq, scope_node, statement, ret, unmatched_label, in_single_pattern, in_alternation_pattern, use_deconstructed_cache, base_index));
+ PM_COMPILE_NOT_POPPED(predicate);
+
+ if (in_single_pattern) {
+ LABEL *match_succeeded_label = NEW_LABEL(location.line);
+
+ PUSH_INSN(ret, location, dup);
+ if (PM_NODE_TYPE_P(node, PM_IF_NODE)) {
+ PUSH_INSNL(ret, location, branchif, match_succeeded_label);
+ }
+ else {
+ PUSH_INSNL(ret, location, branchunless, match_succeeded_label);
+ }
+
+ PUSH_INSN1(ret, location, putobject, rb_fstring_lit("guard clause does not return true"));
+ PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
+ PUSH_INSN1(ret, location, putobject, Qfalse);
+ PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
+
+ PUSH_INSN(ret, location, pop);
+ PUSH_INSN(ret, location, pop);
+
+ PUSH_LABEL(ret, match_succeeded_label);
+ }
+
+ if (PM_NODE_TYPE_P(node, PM_IF_NODE)) {
+ PUSH_INSNL(ret, location, branchunless, unmatched_label);
+ }
+ else {
+ PUSH_INSNL(ret, location, branchif, unmatched_label);
+ }
+
+ PUSH_INSNL(ret, location, jump, matched_label);
+ break;
+ }
+ default:
+ // If we get here, then we have a node type that should not be in this
+ // position. This would be a bug in the parser, because a different node
+ // type should never have been created in this position in the tree.
+ rb_bug("Unexpected node type in pattern matching expression: %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
+ break;
+ }
+
+ return COMPILE_OK;
+}
+
+#undef PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE
+#undef PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING
+#undef PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P
+#undef PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE
+#undef PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY
+
+// Generate a scope node from the given node.
+void
+pm_scope_node_init(const pm_node_t *node, pm_scope_node_t *scope, pm_scope_node_t *previous)
+{
+ // This is very important, otherwise the scope node could be seen as having
+ // certain flags set that _should not_ be set.
+ memset(scope, 0, sizeof(pm_scope_node_t));
+
+ scope->base.type = PM_SCOPE_NODE;
+ scope->base.location.start = node->location.start;
+ scope->base.location.end = node->location.end;
+
+ scope->previous = previous;
+ scope->ast_node = (pm_node_t *) node;
+
+ if (previous) {
+ scope->parser = previous->parser;
+ scope->encoding = previous->encoding;
+ scope->filepath_encoding = previous->filepath_encoding;
+ scope->constants = previous->constants;
+ }
+
+ switch (PM_NODE_TYPE(node)) {
+ case PM_BLOCK_NODE: {
+ const pm_block_node_t *cast = (const pm_block_node_t *) node;
+ scope->body = cast->body;
+ scope->locals = cast->locals;
+ scope->parameters = cast->parameters;
+ break;
+ }
+ case PM_CLASS_NODE: {
+ const pm_class_node_t *cast = (const pm_class_node_t *) node;
+ scope->body = cast->body;
+ scope->locals = cast->locals;
+ break;
+ }
+ case PM_DEF_NODE: {
+ const pm_def_node_t *cast = (const pm_def_node_t *) node;
+ scope->parameters = (pm_node_t *) cast->parameters;
+ scope->body = cast->body;
+ scope->locals = cast->locals;
+ break;
+ }
+ case PM_ENSURE_NODE: {
+ const pm_ensure_node_t *cast = (const pm_ensure_node_t *) node;
+ scope->body = (pm_node_t *) node;
+
+ if (cast->statements != NULL) {
+ scope->base.location.start = cast->statements->base.location.start;
+ scope->base.location.end = cast->statements->base.location.end;
+ }
+
+ break;
+ }
+ case PM_FOR_NODE: {
+ const pm_for_node_t *cast = (const pm_for_node_t *) node;
+ scope->body = (pm_node_t *) cast->statements;
+ break;
+ }
+ case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
+ RUBY_ASSERT(node->flags & PM_REGULAR_EXPRESSION_FLAGS_ONCE);
+ scope->body = (pm_node_t *) node;
+ break;
+ }
+ case PM_LAMBDA_NODE: {
+ const pm_lambda_node_t *cast = (const pm_lambda_node_t *) node;
+ scope->parameters = cast->parameters;
+ scope->body = cast->body;
+ scope->locals = cast->locals;
+
+ if (cast->parameters != NULL) {
+ scope->base.location.start = cast->parameters->location.start;
+ }
+ else {
+ scope->base.location.start = cast->operator_loc.end;
+ }
+ break;
+ }
+ case PM_MODULE_NODE: {
+ const pm_module_node_t *cast = (const pm_module_node_t *) node;
+ scope->body = cast->body;
+ scope->locals = cast->locals;
+ break;
+ }
+ case PM_POST_EXECUTION_NODE: {
+ const pm_post_execution_node_t *cast = (const pm_post_execution_node_t *) node;
+ scope->body = (pm_node_t *) cast->statements;
+ break;
+ }
+ case PM_PROGRAM_NODE: {
+ const pm_program_node_t *cast = (const pm_program_node_t *) node;
+ scope->body = (pm_node_t *) cast->statements;
+ scope->locals = cast->locals;
+ break;
+ }
+ case PM_RESCUE_NODE: {
+ const pm_rescue_node_t *cast = (const pm_rescue_node_t *) node;
+ scope->body = (pm_node_t *) cast->statements;
+ break;
+ }
+ case PM_RESCUE_MODIFIER_NODE: {
+ const pm_rescue_modifier_node_t *cast = (const pm_rescue_modifier_node_t *) node;
+ scope->body = (pm_node_t *) cast->rescue_expression;
+ break;
+ }
+ case PM_SINGLETON_CLASS_NODE: {
+ const pm_singleton_class_node_t *cast = (const pm_singleton_class_node_t *) node;
+ scope->body = cast->body;
+ scope->locals = cast->locals;
+ break;
+ }
+ case PM_STATEMENTS_NODE: {
+ const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
+ scope->body = (pm_node_t *) cast;
+ break;
+ }
+ default:
+ rb_bug("unreachable");
+ break;
+ }
+}
+
+void
+pm_scope_node_destroy(pm_scope_node_t *scope_node)
+{
+ if (scope_node->index_lookup_table) {
+ st_free_table(scope_node->index_lookup_table);
+ }
+}
+
+/**
+ * We need to put the label "retry_end_l" immediately after the last "send"
+ * instruction. This because vm_throw checks if the break cont is equal to the
+ * index of next insn of the "send". (Otherwise, it is considered
+ * "break from proc-closure". See "TAG_BREAK" handling in "vm_throw_start".)
+ *
+ * Normally, "send" instruction is at the last. However, qcall under branch
+ * coverage measurement adds some instructions after the "send".
+ *
+ * Note that "invokesuper" appears instead of "send".
+ */
+static void
+pm_compile_retry_end_label(rb_iseq_t *iseq, LINK_ANCHOR *const ret, LABEL *retry_end_l)
+{
+ INSN *iobj;
+ LINK_ELEMENT *last_elem = LAST_ELEMENT(ret);
+ iobj = IS_INSN(last_elem) ? (INSN*) last_elem : (INSN*) get_prev_insn((INSN*) last_elem);
+ while (INSN_OF(iobj) != BIN(send) && INSN_OF(iobj) != BIN(invokesuper)) {
+ iobj = (INSN*) get_prev_insn(iobj);
+ }
+ ELEM_INSERT_NEXT(&iobj->link, (LINK_ELEMENT*) retry_end_l);
+
+ // LINK_ANCHOR has a pointer to the last element, but
+ // ELEM_INSERT_NEXT does not update it even if we add an insn to the
+ // last of LINK_ANCHOR. So this updates it manually.
+ if (&iobj->link == LAST_ELEMENT(ret)) {
+ ret->last = (LINK_ELEMENT*) retry_end_l;
+ }
+}
+
+/**
+ * Compile a call node into the given iseq.
+ */
+static void
+pm_compile_call(rb_iseq_t *iseq, const pm_call_node_t *call_node, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, ID method_id, LABEL *start)
+{
+ const pm_location_t *message_loc = &call_node->message_loc;
+ if (message_loc->start == NULL) message_loc = &call_node->base.location;
+
+ const pm_line_column_t location = PM_LOCATION_START_LINE_COLUMN(scope_node->parser, message_loc);
+ LABEL *else_label = NEW_LABEL(location.line);
+ LABEL *end_label = NEW_LABEL(location.line);
+ LABEL *retry_end_l = NEW_LABEL(location.line);
+
+ VALUE branches = Qfalse;
+ rb_code_location_t code_location = { 0 };
+ int node_id = location.column;
+
+ if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
+ if (PM_BRANCH_COVERAGE_P(iseq)) {
+ const uint8_t *cursors[3] = {
+ call_node->closing_loc.end,
+ call_node->arguments == NULL ? NULL : call_node->arguments->base.location.end,
+ call_node->message_loc.end
+ };
+
+ const uint8_t *end_cursor = cursors[0];
+ end_cursor = (end_cursor == NULL || cursors[1] == NULL) ? cursors[1] : (end_cursor > cursors[1] ? end_cursor : cursors[1]);
+ end_cursor = (end_cursor == NULL || cursors[2] == NULL) ? cursors[2] : (end_cursor > cursors[2] ? end_cursor : cursors[2]);
+
+ const pm_line_column_t start_location = PM_NODE_START_LINE_COLUMN(scope_node->parser, call_node);
+ const pm_line_column_t end_location = pm_newline_list_line_column(&scope_node->parser->newline_list, end_cursor, scope_node->parser->start_line);
+
+ code_location = (rb_code_location_t) {
+ .beg_pos = { .lineno = start_location.line, .column = start_location.column },
+ .end_pos = { .lineno = end_location.line, .column = end_location.column }
+ };
+
+ branches = decl_branch_base(iseq, PTR2NUM(call_node), &code_location, "&.");
+ }
+
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSNL(ret, location, branchnil, else_label);
+
+ add_trace_branch_coverage(iseq, ret, &code_location, node_id, 0, "then", branches);
+ }
+
+ int flags = 0;
+ struct rb_callinfo_kwarg *kw_arg = NULL;
+
+ int orig_argc = pm_setup_args(call_node->arguments, call_node->block, &flags, &kw_arg, iseq, ret, scope_node, &location);
+ const rb_iseq_t *block_iseq = NULL;
+
+ if (call_node->block != NULL && PM_NODE_TYPE_P(call_node->block, PM_BLOCK_NODE)) {
+ // Scope associated with the block
+ pm_scope_node_t next_scope_node;
+ pm_scope_node_init(call_node->block, &next_scope_node, scope_node);
+
+ block_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, pm_node_line_number(scope_node->parser, call_node->block));
+ pm_scope_node_destroy(&next_scope_node);
+ ISEQ_COMPILE_DATA(iseq)->current_block = block_iseq;
+ }
+ else {
+ if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
+ flags |= VM_CALL_VCALL;
+ }
+
+ if (!flags) {
+ flags |= VM_CALL_ARGS_SIMPLE;
+ }
+ }
+
+ if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) {
+ flags |= VM_CALL_FCALL;
+ }
+
+ if (!popped && PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) {
+ if (flags & VM_CALL_ARGS_BLOCKARG) {
+ PUSH_INSN1(ret, location, topn, INT2FIX(1));
+ if (flags & VM_CALL_ARGS_SPLAT) {
+ PUSH_INSN1(ret, location, putobject, INT2FIX(-1));
+ PUSH_SEND_WITH_FLAG(ret, location, idAREF, INT2FIX(1), INT2FIX(0));
+ }
+ PUSH_INSN1(ret, location, setn, INT2FIX(orig_argc + 3));
+ PUSH_INSN(ret, location, pop);
+ }
+ else if (flags & VM_CALL_ARGS_SPLAT) {
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSN1(ret, location, putobject, INT2FIX(-1));
+ PUSH_SEND_WITH_FLAG(ret, location, idAREF, INT2FIX(1), INT2FIX(0));
+ PUSH_INSN1(ret, location, setn, INT2FIX(orig_argc + 2));
+ PUSH_INSN(ret, location, pop);
+ }
+ else {
+ PUSH_INSN1(ret, location, setn, INT2FIX(orig_argc + 1));
+ }
+ }
+
+ if ((flags & VM_CALL_KW_SPLAT) && (flags & VM_CALL_ARGS_BLOCKARG) && !(flags & VM_CALL_KW_SPLAT_MUT)) {
+ PUSH_INSN(ret, location, splatkw);
+ }
+
+ PUSH_SEND_R(ret, location, method_id, INT2FIX(orig_argc), block_iseq, INT2FIX(flags), kw_arg);
+
+ if (block_iseq && ISEQ_BODY(block_iseq)->catch_table) {
+ pm_compile_retry_end_label(iseq, ret, retry_end_l);
+ PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, start, retry_end_l, block_iseq, retry_end_l);
+ }
+
+ if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
+ PUSH_INSNL(ret, location, jump, end_label);
+ PUSH_LABEL(ret, else_label);
+ add_trace_branch_coverage(iseq, ret, &code_location, node_id, 1, "else", branches);
+ PUSH_LABEL(ret, end_label);
+ }
+
+ if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) && !popped) {
+ PUSH_INSN(ret, location, pop);
+ }
+
+ if (popped) PUSH_INSN(ret, location, pop);
+}
+
+static void
+pm_compile_defined_expr0(rb_iseq_t *iseq, const pm_node_t *node, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition, LABEL **lfinish, bool explicit_receiver)
+{
+ // in_condition is the same as compile.c's needstr
+ enum defined_type dtype = DEFINED_NOT_DEFINED;
+ const pm_line_column_t location = *node_location;
+
+ switch (PM_NODE_TYPE(node)) {
+ case PM_ARGUMENTS_NODE: {
+ const pm_arguments_node_t *cast = (const pm_arguments_node_t *) node;
+ const pm_node_list_t *arguments = &cast->arguments;
+ for (size_t idx = 0; idx < arguments->size; idx++) {
+ const pm_node_t *argument = arguments->nodes[idx];
+ pm_compile_defined_expr0(iseq, argument, node_location, ret, popped, scope_node, in_condition, lfinish, explicit_receiver);
+
+ if (!lfinish[1]) {
+ lfinish[1] = NEW_LABEL(location.line);
+ }
+ PUSH_INSNL(ret, location, branchunless, lfinish[1]);
+ }
+ dtype = DEFINED_TRUE;
+ break;
+ }
+ case PM_NIL_NODE:
+ dtype = DEFINED_NIL;
+ break;
+ case PM_PARENTHESES_NODE: {
+ const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
+
+ if (cast->body == NULL) {
+ // If we have empty parentheses, then we want to return "nil".
+ dtype = DEFINED_NIL;
+ }
+ else if (PM_NODE_TYPE_P(cast->body, PM_STATEMENTS_NODE) && ((const pm_statements_node_t *) cast->body)->body.size == 1) {
+ // If we have a parentheses node that is wrapping a single statement
+ // then we want to recurse down to that statement and compile it.
+ pm_compile_defined_expr0(iseq, ((const pm_statements_node_t *) cast->body)->body.nodes[0], node_location, ret, popped, scope_node, in_condition, lfinish, explicit_receiver);
+ return;
+ }
+ else {
+ // Otherwise, we have parentheses wrapping multiple statements, in
+ // which case this is defined as "expression".
+ dtype = DEFINED_EXPR;
+ }
+
+ break;
+ }
+ case PM_SELF_NODE:
+ dtype = DEFINED_SELF;
+ break;
+ case PM_TRUE_NODE:
+ dtype = DEFINED_TRUE;
+ break;
+ case PM_FALSE_NODE:
+ dtype = DEFINED_FALSE;
+ break;
+ case PM_ARRAY_NODE: {
+ const pm_array_node_t *cast = (const pm_array_node_t *) node;
+
+ if (!PM_NODE_FLAG_P(cast, PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT)) {
+ for (size_t index = 0; index < cast->elements.size; index++) {
+ pm_compile_defined_expr0(iseq, cast->elements.nodes[index], node_location, ret, popped, scope_node, true, lfinish, false);
+
+ if (!lfinish[1]) {
+ lfinish[1] = NEW_LABEL(location.line);
+ }
+
+ PUSH_INSNL(ret, location, branchunless, lfinish[1]);
+ }
+ }
+ }
+ case PM_AND_NODE:
+ case PM_BEGIN_NODE:
+ case PM_BREAK_NODE:
+ case PM_CASE_NODE:
+ case PM_CASE_MATCH_NODE:
+ case PM_CLASS_NODE:
+ case PM_DEF_NODE:
+ case PM_DEFINED_NODE:
+ case PM_FLOAT_NODE:
+ case PM_FOR_NODE:
+ case PM_HASH_NODE:
+ case PM_IF_NODE:
+ case PM_IMAGINARY_NODE:
+ case PM_INTEGER_NODE:
+ case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
+ case PM_INTERPOLATED_STRING_NODE:
+ case PM_INTERPOLATED_SYMBOL_NODE:
+ case PM_INTERPOLATED_X_STRING_NODE:
+ case PM_KEYWORD_HASH_NODE:
+ case PM_LAMBDA_NODE:
+ case PM_MATCH_PREDICATE_NODE:
+ case PM_MATCH_REQUIRED_NODE:
+ case PM_MATCH_WRITE_NODE:
+ case PM_MODULE_NODE:
+ case PM_NEXT_NODE:
+ case PM_OR_NODE:
+ case PM_RANGE_NODE:
+ case PM_RATIONAL_NODE:
+ case PM_REDO_NODE:
+ case PM_REGULAR_EXPRESSION_NODE:
+ case PM_RETRY_NODE:
+ case PM_RETURN_NODE:
+ case PM_SINGLETON_CLASS_NODE:
+ case PM_SOURCE_ENCODING_NODE:
+ case PM_SOURCE_FILE_NODE:
+ case PM_SOURCE_LINE_NODE:
+ case PM_STRING_NODE:
+ case PM_SYMBOL_NODE:
+ case PM_UNLESS_NODE:
+ case PM_UNTIL_NODE:
+ case PM_WHILE_NODE:
+ case PM_X_STRING_NODE:
+ dtype = DEFINED_EXPR;
+ break;
+ case PM_LOCAL_VARIABLE_READ_NODE:
+ dtype = DEFINED_LVAR;
+ break;
+
+#define PUSH_VAL(type) (in_condition ? Qtrue : rb_iseq_defined_string(type))
+
+ case PM_INSTANCE_VARIABLE_READ_NODE: {
+ const pm_instance_variable_read_node_t *cast = (const pm_instance_variable_read_node_t *) node;
+
+ ID name = pm_constant_id_lookup(scope_node, cast->name);
+ PUSH_INSN3(ret, location, definedivar, ID2SYM(name), get_ivar_ic_value(iseq, name), PUSH_VAL(DEFINED_IVAR));
+
+ return;
+ }
+ case PM_BACK_REFERENCE_READ_NODE: {
+ const char *char_ptr = (const char *) (node->location.start + 1);
+ ID backref_val = INT2FIX(rb_intern2(char_ptr, 1)) << 1 | 1;
+
+ PUSH_INSN(ret, location, putnil);
+ PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_REF), backref_val, PUSH_VAL(DEFINED_GVAR));
+
+ return;
+ }
+ case PM_NUMBERED_REFERENCE_READ_NODE: {
+ uint32_t reference_number = ((const pm_numbered_reference_read_node_t *) node)->number;
+
+ PUSH_INSN(ret, location, putnil);
+ PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_REF), INT2FIX(reference_number << 1), PUSH_VAL(DEFINED_GVAR));
+
+ return;
+ }
+ case PM_GLOBAL_VARIABLE_READ_NODE: {
+ const pm_global_variable_read_node_t *cast = (const pm_global_variable_read_node_t *) node;
+ VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
+
+ PUSH_INSN(ret, location, putnil);
+ PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_GVAR), name, PUSH_VAL(DEFINED_GVAR));
+
+ return;
+ }
+ case PM_CLASS_VARIABLE_READ_NODE: {
+ const pm_class_variable_read_node_t *cast = (const pm_class_variable_read_node_t *) node;
+ VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
+
+ PUSH_INSN(ret, location, putnil);
+ PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CVAR), name, PUSH_VAL(DEFINED_CVAR));
+
+ return;
+ }
+ case PM_CONSTANT_READ_NODE: {
+ const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node;
+ VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
+
+ PUSH_INSN(ret, location, putnil);
+ PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST), name, PUSH_VAL(DEFINED_CONST));
+
+ return;
+ }
+ case PM_CONSTANT_PATH_NODE: {
+ const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node;
+ VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, ((const pm_constant_read_node_t *) cast->child)->name));
+
+ if (cast->parent != NULL) {
+ if (!lfinish[1]) lfinish[1] = NEW_LABEL(location.line);
+ pm_compile_defined_expr0(iseq, cast->parent, node_location, ret, popped, scope_node, true, lfinish, false);
+
+ PUSH_INSNL(ret, location, branchunless, lfinish[1]);
+ PM_COMPILE(cast->parent);
+ }
+ else {
+ PUSH_INSN1(ret, location, putobject, rb_cObject);
+ }
+
+ PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST_FROM), name, PUSH_VAL(DEFINED_CONST));
+ return;
+ }
+ case PM_CALL_NODE: {
+ const pm_call_node_t *cast = ((const pm_call_node_t *) node);
+ ID method_id = pm_constant_id_lookup(scope_node, cast->name);
+
+ if (cast->receiver || cast->arguments) {
+ if (!lfinish[1]) lfinish[1] = NEW_LABEL(location.line);
+ if (!lfinish[2]) lfinish[2] = NEW_LABEL(location.line);
+ }
+
+ if (cast->arguments) {
+ pm_compile_defined_expr0(iseq, (const pm_node_t *) cast->arguments, node_location, ret, popped, scope_node, true, lfinish, false);
+ PUSH_INSNL(ret, location, branchunless, lfinish[1]);
+ }
+
+ if (cast->receiver) {
+ pm_compile_defined_expr0(iseq, cast->receiver, node_location, ret, popped, scope_node, true, lfinish, true);
+
+ if (PM_NODE_TYPE_P(cast->receiver, PM_CALL_NODE)) {
+ PUSH_INSNL(ret, location, branchunless, lfinish[2]);
+
+ const pm_call_node_t *receiver = (const pm_call_node_t *) cast->receiver;
+ ID method_id = pm_constant_id_lookup(scope_node, receiver->name);
+ pm_compile_call(iseq, receiver, ret, popped, scope_node, method_id, NULL);
+ }
+ else {
+ PUSH_INSNL(ret, location, branchunless, lfinish[1]);
+ PM_COMPILE(cast->receiver);
+ }
+
+ if (explicit_receiver) PUSH_INSN(ret, location, dup);
+ PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_METHOD), rb_id2sym(method_id), PUSH_VAL(DEFINED_METHOD));
+ }
+ else {
+ PUSH_INSN(ret, location, putself);
+ if (explicit_receiver) PUSH_INSN(ret, location, dup);
+ PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_FUNC), rb_id2sym(method_id), PUSH_VAL(DEFINED_METHOD));
+ }
+
+ return;
+ }
+ case PM_YIELD_NODE:
+ PUSH_INSN(ret, location, putnil);
+ PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_YIELD), 0, PUSH_VAL(DEFINED_YIELD));
+ return;
+ case PM_SUPER_NODE:
+ case PM_FORWARDING_SUPER_NODE:
+ PUSH_INSN(ret, location, putnil);
+ PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_ZSUPER), 0, PUSH_VAL(DEFINED_ZSUPER));
+ return;
+ case PM_CALL_AND_WRITE_NODE:
+ case PM_CALL_OPERATOR_WRITE_NODE:
+ case PM_CALL_OR_WRITE_NODE:
+
+ case PM_CONSTANT_WRITE_NODE:
+ case PM_CONSTANT_OPERATOR_WRITE_NODE:
+ case PM_CONSTANT_AND_WRITE_NODE:
+ case PM_CONSTANT_OR_WRITE_NODE:
+
+ case PM_CONSTANT_PATH_AND_WRITE_NODE:
+ case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE:
+ case PM_CONSTANT_PATH_OR_WRITE_NODE:
+ case PM_CONSTANT_PATH_WRITE_NODE:
+
+ case PM_GLOBAL_VARIABLE_WRITE_NODE:
+ case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE:
+ case PM_GLOBAL_VARIABLE_AND_WRITE_NODE:
+ case PM_GLOBAL_VARIABLE_OR_WRITE_NODE:
+
+ case PM_CLASS_VARIABLE_WRITE_NODE:
+ case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE:
+ case PM_CLASS_VARIABLE_AND_WRITE_NODE:
+ case PM_CLASS_VARIABLE_OR_WRITE_NODE:
+
+ case PM_INDEX_AND_WRITE_NODE:
+ case PM_INDEX_OPERATOR_WRITE_NODE:
+ case PM_INDEX_OR_WRITE_NODE:
+
+ case PM_INSTANCE_VARIABLE_WRITE_NODE:
+ case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE:
+ case PM_INSTANCE_VARIABLE_AND_WRITE_NODE:
+ case PM_INSTANCE_VARIABLE_OR_WRITE_NODE:
+
+ case PM_LOCAL_VARIABLE_WRITE_NODE:
+ case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE:
+ case PM_LOCAL_VARIABLE_AND_WRITE_NODE:
+ case PM_LOCAL_VARIABLE_OR_WRITE_NODE:
+
+ case PM_MULTI_WRITE_NODE:
+ dtype = DEFINED_ASGN;
+ break;
+ default:
+ rb_bug("Unsupported node %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
+ }
+
+ RUBY_ASSERT(dtype != DEFINED_NOT_DEFINED);
+ PUSH_INSN1(ret, location, putobject, PUSH_VAL(dtype));
+#undef PUSH_VAL
+}
+
+static void
+pm_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition, LABEL **lfinish, bool explicit_receiver)
+{
+ LINK_ELEMENT *lcur = ret->last;
+ pm_compile_defined_expr0(iseq, node, node_location, ret, popped, scope_node, in_condition, lfinish, false);
+
+ if (lfinish[1]) {
+ LABEL *lstart = NEW_LABEL(node_location->line);
+ LABEL *lend = NEW_LABEL(node_location->line);
+
+ struct rb_iseq_new_with_callback_callback_func *ifunc =
+ rb_iseq_new_with_callback_new_callback(build_defined_rescue_iseq, NULL);
+
+ const rb_iseq_t *rescue = new_child_iseq_with_callback(
+ iseq,
+ ifunc,
+ rb_str_concat(rb_str_new2("defined guard in "), ISEQ_BODY(iseq)->location.label),
+ iseq,
+ ISEQ_TYPE_RESCUE,
+ 0
+ );
+
+ lstart->rescued = LABEL_RESCUE_BEG;
+ lend->rescued = LABEL_RESCUE_END;
+
+ APPEND_LABEL(ret, lcur, lstart);
+ PUSH_LABEL(ret, lend);
+ PUSH_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lfinish[1]);
+ }
+}
+
+static void
+pm_compile_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition)
+{
+ LABEL *lfinish[3];
+ LINK_ELEMENT *last = ret->last;
+
+ lfinish[0] = NEW_LABEL(node_location->line);
+ lfinish[1] = 0;
+ lfinish[2] = 0;
+
+ if (!popped) {
+ pm_defined_expr(iseq, node, node_location, ret, popped, scope_node, in_condition, lfinish, false);
+ }
+
+ if (lfinish[1]) {
+ ELEM_INSERT_NEXT(last, &new_insn_body(iseq, node_location->line, node_location->column, BIN(putnil), 0)->link);
+ PUSH_INSN(ret, *node_location, swap);
+
+ if (lfinish[2]) PUSH_LABEL(ret, lfinish[2]);
+ PUSH_INSN(ret, *node_location, pop);
+ PUSH_LABEL(ret, lfinish[1]);
+
+ }
+
+ PUSH_LABEL(ret, lfinish[0]);
+}
+
+// This is exactly the same as add_ensure_iseq, except it compiled
+// the node as a Prism node, and not a CRuby node
+static void
+pm_add_ensure_iseq(LINK_ANCHOR *const ret, rb_iseq_t *iseq, int is_return, pm_scope_node_t *scope_node)
+{
+ RUBY_ASSERT(can_add_ensure_iseq(iseq));
+
+ struct iseq_compile_data_ensure_node_stack *enlp =
+ ISEQ_COMPILE_DATA(iseq)->ensure_node_stack;
+ struct iseq_compile_data_ensure_node_stack *prev_enlp = enlp;
+ DECL_ANCHOR(ensure);
+
+ INIT_ANCHOR(ensure);
+ while (enlp) {
+ if (enlp->erange != NULL) {
+ DECL_ANCHOR(ensure_part);
+ LABEL *lstart = NEW_LABEL(0);
+ LABEL *lend = NEW_LABEL(0);
+ INIT_ANCHOR(ensure_part);
+
+ add_ensure_range(iseq, enlp->erange, lstart, lend);
+
+ ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enlp->prev;
+ PUSH_LABEL(ensure_part, lstart);
+ bool popped = true;
+ PM_COMPILE_INTO_ANCHOR(ensure_part, (const pm_node_t *) enlp->ensure_node);
+ PUSH_LABEL(ensure_part, lend);
+ PUSH_SEQ(ensure, ensure_part);
+ }
+ else {
+ if (!is_return) {
+ break;
+ }
+ }
+ enlp = enlp->prev;
+ }
+ ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = prev_enlp;
+ PUSH_SEQ(ret, ensure);
+}
+
+struct pm_local_table_insert_ctx {
+ pm_scope_node_t *scope_node;
+ rb_ast_id_table_t *local_table_for_iseq;
+ int local_index;
+};
+
+static int
+pm_local_table_insert_func(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
+{
+ if (!existing) {
+ pm_constant_id_t constant_id = (pm_constant_id_t) *key;
+ struct pm_local_table_insert_ctx * ctx = (struct pm_local_table_insert_ctx *) arg;
+
+ pm_scope_node_t *scope_node = ctx->scope_node;
+ rb_ast_id_table_t *local_table_for_iseq = ctx->local_table_for_iseq;
+ int local_index = ctx->local_index;
+
+ ID local = pm_constant_id_lookup(scope_node, constant_id);
+ local_table_for_iseq->ids[local_index] = local;
+
+ *value = (st_data_t)local_index;
+
+ ctx->local_index++;
+ }
+
+ return ST_CONTINUE;
+}
+
+/**
+ * Insert a local into the local table for the iseq. This is used to create the
+ * local table in the correct order while compiling the scope. The locals being
+ * inserted are regular named locals, as opposed to special forwarding locals.
+ */
+static void
+pm_insert_local_index(pm_constant_id_t constant_id, int local_index, st_table *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq, pm_scope_node_t *scope_node)
+{
+ RUBY_ASSERT((constant_id & PM_SPECIAL_CONSTANT_FLAG) == 0);
+
+ ID local = pm_constant_id_lookup(scope_node, constant_id);
+ local_table_for_iseq->ids[local_index] = local;
+ st_insert(index_lookup_table, (st_data_t) constant_id, (st_data_t) local_index);
+}
+
+/**
+ * Insert a local into the local table for the iseq that is a special forwarding
+ * local variable.
+ */
+static void
+pm_insert_local_special(ID local_name, int local_index, st_table *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq)
+{
+ local_table_for_iseq->ids[local_index] = local_name;
+ st_insert(index_lookup_table, (st_data_t) (local_name | PM_SPECIAL_CONSTANT_FLAG), (st_data_t) local_index);
+}
+
+/**
+ * Compile the locals of a multi target node that is used as a positional
+ * parameter in a method, block, or lambda definition. Note that this doesn't
+ * actually add any instructions to the iseq. Instead, it adds locals to the
+ * local and index lookup tables and increments the local index as necessary.
+ */
+static int
+pm_compile_destructured_param_locals(const pm_multi_target_node_t *node, st_table *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq, pm_scope_node_t *scope_node, int local_index)
+{
+ for (size_t index = 0; index < node->lefts.size; index++) {
+ const pm_node_t *left = node->lefts.nodes[index];
+
+ if (PM_NODE_TYPE_P(left, PM_REQUIRED_PARAMETER_NODE)) {
+ if (!PM_NODE_FLAG_P(left, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
+ pm_insert_local_index(((const pm_required_parameter_node_t *) left)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
+ local_index++;
+ }
+ }
+ else {
+ RUBY_ASSERT(PM_NODE_TYPE_P(left, PM_MULTI_TARGET_NODE));
+ local_index = pm_compile_destructured_param_locals((const pm_multi_target_node_t *) left, index_lookup_table, local_table_for_iseq, scope_node, local_index);
+ }
+ }
+
+ if (node->rest != NULL && PM_NODE_TYPE_P(node->rest, PM_SPLAT_NODE)) {
+ const pm_splat_node_t *rest = (const pm_splat_node_t *) node->rest;
+
+ if (rest->expression != NULL) {
+ RUBY_ASSERT(PM_NODE_TYPE_P(rest->expression, PM_REQUIRED_PARAMETER_NODE));
+ pm_insert_local_index(((const pm_required_parameter_node_t *) rest->expression)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
+ local_index++;
+ }
+ }
+
+ for (size_t index = 0; index < node->rights.size; index++) {
+ const pm_node_t *right = node->rights.nodes[index];
+
+ if (PM_NODE_TYPE_P(right, PM_REQUIRED_PARAMETER_NODE)) {
+ pm_insert_local_index(((const pm_required_parameter_node_t *) right)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
+ local_index++;
+ }
+ else {
+ RUBY_ASSERT(PM_NODE_TYPE_P(right, PM_MULTI_TARGET_NODE));
+ local_index = pm_compile_destructured_param_locals((const pm_multi_target_node_t *) right, index_lookup_table, local_table_for_iseq, scope_node, local_index);
+ }
+ }
+
+ return local_index;
+}
+
+/**
+ * Compile a required parameter node that is part of a destructure that is used
+ * as a positional parameter in a method, block, or lambda definition.
+ */
+static inline void
+pm_compile_destructured_param_write(rb_iseq_t *iseq, const pm_required_parameter_node_t *node, LINK_ANCHOR *const ret, const pm_scope_node_t *scope_node)
+{
+ const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
+ pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, node->name, 0);
+ PUSH_SETLOCAL(ret, location, index.index, index.level);
+}
+
+/**
+ * Compile a multi target node that is used as a positional parameter in a
+ * method, block, or lambda definition. Note that this is effectively the same
+ * as a multi write, but with the added context that all of the targets
+ * contained in the write are required parameter nodes. With this context, we
+ * know they won't have any parent expressions so we build a separate code path
+ * for this simplified case.
+ */
+static void
+pm_compile_destructured_param_writes(rb_iseq_t *iseq, const pm_multi_target_node_t *node, LINK_ANCHOR *const ret, const pm_scope_node_t *scope_node)
+{
+ const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
+ bool has_rest = (node->rest && PM_NODE_TYPE_P(node->rest, PM_SPLAT_NODE) && (((const pm_splat_node_t *) node->rest)->expression) != NULL);
+ bool has_rights = node->rights.size > 0;
+
+ int flag = (has_rest || has_rights) ? 1 : 0;
+ PUSH_INSN2(ret, location, expandarray, INT2FIX(node->lefts.size), INT2FIX(flag));
+
+ for (size_t index = 0; index < node->lefts.size; index++) {
+ const pm_node_t *left = node->lefts.nodes[index];
+
+ if (PM_NODE_TYPE_P(left, PM_REQUIRED_PARAMETER_NODE)) {
+ pm_compile_destructured_param_write(iseq, (const pm_required_parameter_node_t *) left, ret, scope_node);
+ }
+ else {
+ RUBY_ASSERT(PM_NODE_TYPE_P(left, PM_MULTI_TARGET_NODE));
+ pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) left, ret, scope_node);
+ }
+ }
+
+ if (has_rest) {
+ if (has_rights) {
+ PUSH_INSN2(ret, location, expandarray, INT2FIX(node->rights.size), INT2FIX(3));
+ }
+
+ const pm_node_t *rest = ((const pm_splat_node_t *) node->rest)->expression;
+ RUBY_ASSERT(PM_NODE_TYPE_P(rest, PM_REQUIRED_PARAMETER_NODE));
+
+ pm_compile_destructured_param_write(iseq, (const pm_required_parameter_node_t *) rest, ret, scope_node);
+ }
+
+ if (has_rights) {
+ if (!has_rest) {
+ PUSH_INSN2(ret, location, expandarray, INT2FIX(node->rights.size), INT2FIX(2));
+ }
+
+ for (size_t index = 0; index < node->rights.size; index++) {
+ const pm_node_t *right = node->rights.nodes[index];
+
+ if (PM_NODE_TYPE_P(right, PM_REQUIRED_PARAMETER_NODE)) {
+ pm_compile_destructured_param_write(iseq, (const pm_required_parameter_node_t *) right, ret, scope_node);
+ }
+ else {
+ RUBY_ASSERT(PM_NODE_TYPE_P(right, PM_MULTI_TARGET_NODE));
+ pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) right, ret, scope_node);
+ }
+ }
+ }
+}
+
+/**
+ * This is a node in the multi target state linked list. It tracks the
+ * information for a particular target that necessarily has a parent expression.
+ */
+typedef struct pm_multi_target_state_node {
+ // The pointer to the topn instruction that will need to be modified after
+ // we know the total stack size of all of the targets.
+ INSN *topn;
+
+ // The index of the stack from the base of the entire multi target at which
+ // the parent expression is located.
+ size_t stack_index;
+
+ // The number of slots in the stack that this node occupies.
+ size_t stack_size;
+
+ // The position of the node in the list of targets.
+ size_t position;
+
+ // A pointer to the next node in this linked list.
+ struct pm_multi_target_state_node *next;
+} pm_multi_target_state_node_t;
+
+/**
+ * As we're compiling a multi target, we need to track additional information
+ * whenever there is a parent expression on the left hand side of the target.
+ * This is because we need to go back and tell the expression where to fetch its
+ * parent expression from the stack. We use a linked list of nodes to track this
+ * information.
+ */
+typedef struct {
+ // The total number of slots in the stack that this multi target occupies.
+ size_t stack_size;
+
+ // The position of the current node being compiled. This is forwarded to
+ // nodes when they are allocated.
+ size_t position;
+
+ // A pointer to the head of this linked list.
+ pm_multi_target_state_node_t *head;
+
+ // A pointer to the tail of this linked list.
+ pm_multi_target_state_node_t *tail;
+} pm_multi_target_state_t;
+
+/**
+ * Push a new state node onto the multi target state.
+ */
+static void
+pm_multi_target_state_push(pm_multi_target_state_t *state, INSN *topn, size_t stack_size)
+{
+ pm_multi_target_state_node_t *node = ALLOC(pm_multi_target_state_node_t);
+ node->topn = topn;
+ node->stack_index = state->stack_size + 1;
+ node->stack_size = stack_size;
+ node->position = state->position;
+ node->next = NULL;
+
+ if (state->head == NULL) {
+ state->head = node;
+ state->tail = node;
+ }
+ else {
+ state->tail->next = node;
+ state->tail = node;
+ }
+
+ state->stack_size += stack_size;
+}
+
+/**
+ * Walk through a multi target state's linked list and update the topn
+ * instructions that were inserted into the write sequence to make sure they can
+ * correctly retrieve their parent expressions.
+ */
+static void
+pm_multi_target_state_update(pm_multi_target_state_t *state)
+{
+ // If nothing was ever pushed onto the stack, then we don't need to do any
+ // kind of updates.
+ if (state->stack_size == 0) return;
+
+ pm_multi_target_state_node_t *current = state->head;
+ pm_multi_target_state_node_t *previous;
+
+ while (current != NULL) {
+ VALUE offset = INT2FIX(state->stack_size - current->stack_index + current->position);
+ current->topn->operands[0] = offset;
+
+ // stack_size will be > 1 in the case that we compiled an index target
+ // and it had arguments. In this case, we use multiple topn instructions
+ // to grab up all of the arguments as well, so those offsets need to be
+ // updated as well.
+ if (current->stack_size > 1) {
+ INSN *insn = current->topn;
+
+ for (size_t index = 1; index < current->stack_size; index += 1) {
+ LINK_ELEMENT *element = get_next_insn(insn);
+ RUBY_ASSERT(IS_INSN(element));
+
+ insn = (INSN *) element;
+ RUBY_ASSERT(insn->insn_id == BIN(topn));
+
+ insn->operands[0] = offset;
+ }
+ }
+
+ previous = current;
+ current = current->next;
+
+ free(previous);
+ }
+}
+
+static size_t
+pm_compile_multi_target_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const parents, LINK_ANCHOR *const writes, LINK_ANCHOR *const cleanup, pm_scope_node_t *scope_node, pm_multi_target_state_t *state);
+
+/**
+ * A target node represents an indirect write to a variable or a method call to
+ * a method ending in =. Compiling one of these nodes requires three sequences:
+ *
+ * * The first is to compile retrieving the parent expression if there is one.
+ * This could be the object that owns a constant or the receiver of a method
+ * call.
+ * * The second is to compile the writes to the targets. This could be writing
+ * to variables, or it could be performing method calls.
+ * * The third is to compile any cleanup that needs to happen, i.e., popping the
+ * appropriate number of values off the stack.
+ *
+ * When there is a parent expression and this target is part of a multi write, a
+ * topn instruction will be inserted into the write sequence. This is to move
+ * the parent expression to the top of the stack so that it can be used as the
+ * receiver of the method call or the owner of the constant. To facilitate this,
+ * we return a pointer to the topn instruction that was used to be later
+ * modified with the correct offset.
+ *
+ * These nodes can appear in a couple of places, but most commonly:
+ *
+ * * For loops - the index variable is a target node
+ * * Rescue clauses - the exception reference variable is a target node
+ * * Multi writes - the left hand side contains a list of target nodes
+ *
+ * For the comments with examples within this function, we'll use for loops as
+ * the containing node.
+ */
+static void
+pm_compile_target_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const parents, LINK_ANCHOR *const writes, LINK_ANCHOR *const cleanup, pm_scope_node_t *scope_node, pm_multi_target_state_t *state)
+{
+ const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
+
+ switch (PM_NODE_TYPE(node)) {
+ case PM_LOCAL_VARIABLE_TARGET_NODE: {
+ // Local variable targets have no parent expression, so they only need
+ // to compile the write.
+ //
+ // for i in []; end
+ //
+ const pm_local_variable_target_node_t *cast = (const pm_local_variable_target_node_t *) node;
+ pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
+
+ PUSH_SETLOCAL(writes, location, index.index, index.level);
+ break;
+ }
+ case PM_CLASS_VARIABLE_TARGET_NODE: {
+ // Class variable targets have no parent expression, so they only need
+ // to compile the write.
+ //
+ // for @@i in []; end
+ //
+ const pm_class_variable_target_node_t *cast = (const pm_class_variable_target_node_t *) node;
+ ID name = pm_constant_id_lookup(scope_node, cast->name);
+
+ PUSH_INSN2(writes, location, setclassvariable, ID2SYM(name), get_cvar_ic_value(iseq, name));
+ break;
+ }
+ case PM_CONSTANT_TARGET_NODE: {
+ // Constant targets have no parent expression, so they only need to
+ // compile the write.
+ //
+ // for I in []; end
+ //
+ const pm_constant_target_node_t *cast = (const pm_constant_target_node_t *) node;
+ ID name = pm_constant_id_lookup(scope_node, cast->name);
+
+ PUSH_INSN1(writes, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
+ PUSH_INSN1(writes, location, setconstant, ID2SYM(name));
+ break;
+ }
+ case PM_GLOBAL_VARIABLE_TARGET_NODE: {
+ // Global variable targets have no parent expression, so they only need
+ // to compile the write.
+ //
+ // for $i in []; end
+ //
+ const pm_global_variable_target_node_t *cast = (const pm_global_variable_target_node_t *) node;
+ ID name = pm_constant_id_lookup(scope_node, cast->name);
+
+ PUSH_INSN1(writes, location, setglobal, ID2SYM(name));
+ break;
+ }
+ case PM_INSTANCE_VARIABLE_TARGET_NODE: {
+ // Instance variable targets have no parent expression, so they only
+ // need to compile the write.
+ //
+ // for @i in []; end
+ //
+ const pm_instance_variable_target_node_t *cast = (const pm_instance_variable_target_node_t *) node;
+ ID name = pm_constant_id_lookup(scope_node, cast->name);
+
+ PUSH_INSN2(writes, location, setinstancevariable, ID2SYM(name), get_ivar_ic_value(iseq, name));
+ break;
+ }
+ case PM_CONSTANT_PATH_TARGET_NODE: {
+ // Constant path targets have a parent expression that is the object
+ // that owns the constant. This needs to be compiled first into the
+ // parents sequence. If no parent is found, then it represents using the
+ // unary :: operator to indicate a top-level constant. In that case we
+ // need to push Object onto the stack.
+ //
+ // for I::J in []; end
+ //
+ const pm_constant_path_target_node_t *cast = (const pm_constant_path_target_node_t *) node;
+ ID name = pm_constant_id_lookup(scope_node, ((const pm_constant_read_node_t *) cast->child)->name);
+
+ if (cast->parent != NULL) {
+ pm_compile_node(iseq, cast->parent, parents, false, scope_node);
+ }
+ else {
+ PUSH_INSN1(parents, location, putobject, rb_cObject);
+ }
+
+ if (state == NULL) {
+ PUSH_INSN(writes, location, swap);
+ }
+ else {
+ PUSH_INSN1(writes, location, topn, INT2FIX(1));
+ pm_multi_target_state_push(state, (INSN *) LAST_ELEMENT(writes), 1);
+ }
+
+ PUSH_INSN1(writes, location, setconstant, ID2SYM(name));
+
+ if (state != NULL) {
+ PUSH_INSN(cleanup, location, pop);
+ }
+
+ break;
+ }
+ case PM_CALL_TARGET_NODE: {
+ // Call targets have a parent expression that is the receiver of the
+ // method being called. This needs to be compiled first into the parents
+ // sequence. These nodes cannot have arguments, so the method call is
+ // compiled with a single argument which represents the value being
+ // written.
+ //
+ // for i.j in []; end
+ //
+ const pm_call_target_node_t *cast = (const pm_call_target_node_t *) node;
+ ID method_id = pm_constant_id_lookup(scope_node, cast->name);
+
+ pm_compile_node(iseq, cast->receiver, parents, false, scope_node);
+
+ if (state != NULL) {
+ PUSH_INSN1(writes, location, topn, INT2FIX(1));
+ pm_multi_target_state_push(state, (INSN *) LAST_ELEMENT(writes), 1);
+ PUSH_INSN(writes, location, swap);
+ }
+
+ int flags = VM_CALL_ARGS_SIMPLE;
+ if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) flags |= VM_CALL_FCALL;
+
+ PUSH_SEND_WITH_FLAG(writes, location, method_id, INT2FIX(1), INT2FIX(flags));
+ PUSH_INSN(writes, location, pop);
+
+ if (state != NULL) {
+ PUSH_INSN(cleanup, location, pop);
+ }
+
+ break;
+ }
+ case PM_INDEX_TARGET_NODE: {
+ // Index targets have a parent expression that is the receiver of the
+ // method being called and any additional arguments that are being
+ // passed along with the value being written. The receiver and arguments
+ // both need to be on the stack. Note that this is even more complicated
+ // by the fact that these nodes can hold a block using the unary &
+ // operator.
+ //
+ // for i[:j] in []; end
+ //
+ const pm_index_target_node_t *cast = (const pm_index_target_node_t *) node;
+
+ pm_compile_node(iseq, cast->receiver, parents, false, scope_node);
+
+ int flags = 0;
+ struct rb_callinfo_kwarg *kwargs = NULL;
+ int argc = pm_setup_args(cast->arguments, cast->block, &flags, &kwargs, iseq, parents, scope_node, &location);
+
+ if (state != NULL) {
+ PUSH_INSN1(writes, location, topn, INT2FIX(argc + 1));
+ pm_multi_target_state_push(state, (INSN *) LAST_ELEMENT(writes), argc + 1);
+
+ if (argc == 0) {
+ PUSH_INSN(writes, location, swap);
+ }
+ else {
+ for (int index = 0; index < argc; index++) {
+ PUSH_INSN1(writes, location, topn, INT2FIX(argc + 1));
+ }
+ PUSH_INSN1(writes, location, topn, INT2FIX(argc + 1));
+ }
+ }
+
+ // The argc that we're going to pass to the send instruction is the
+ // number of arguments + 1 for the value being written. If there's a
+ // splat, then we need to insert newarray and concatarray instructions
+ // after the arguments have been written.
+ int ci_argc = argc + 1;
+ if (flags & VM_CALL_ARGS_SPLAT) {
+ ci_argc--;
+ PUSH_INSN1(writes, location, newarray, INT2FIX(1));
+ PUSH_INSN(writes, location, concatarray);
+ }
+
+ PUSH_SEND_R(writes, location, idASET, INT2NUM(ci_argc), NULL, INT2FIX(flags), kwargs);
+ PUSH_INSN(writes, location, pop);
+
+ if (state != NULL) {
+ if (argc != 0) {
+ PUSH_INSN(writes, location, pop);
+ }
+
+ for (int index = 0; index < argc + 1; index++) {
+ PUSH_INSN(cleanup, location, pop);
+ }
+ }
+
+ break;
+ }
+ case PM_MULTI_TARGET_NODE: {
+ // Multi target nodes represent a set of writes to multiple variables.
+ // The parent expressions are the combined set of the parent expressions
+ // of its inner target nodes.
+ //
+ // for i, j in []; end
+ //
+ if (state != NULL) state->position--;
+ pm_compile_multi_target_node(iseq, node, parents, writes, cleanup, scope_node, state);
+ if (state != NULL) state->position++;
+ break;
+ }
+ default:
+ rb_bug("Unexpected node type: %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
+ break;
+ }
+}
+
+/**
+ * Compile a multi target or multi write node. It returns the number of values
+ * on the stack that correspond to the parent expressions of the various
+ * targets.
+ */
+static size_t
+pm_compile_multi_target_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const parents, LINK_ANCHOR *const writes, LINK_ANCHOR *const cleanup, pm_scope_node_t *scope_node, pm_multi_target_state_t *state)
+{
+ const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
+ const pm_node_list_t *lefts;
+ const pm_node_t *rest;
+ const pm_node_list_t *rights;
+
+ switch (PM_NODE_TYPE(node)) {
+ case PM_MULTI_TARGET_NODE: {
+ const pm_multi_target_node_t *cast = (const pm_multi_target_node_t *) node;
+ lefts = &cast->lefts;
+ rest = cast->rest;
+ rights = &cast->rights;
+ break;
+ }
+ case PM_MULTI_WRITE_NODE: {
+ const pm_multi_write_node_t *cast = (const pm_multi_write_node_t *) node;
+ lefts = &cast->lefts;
+ rest = cast->rest;
+ rights = &cast->rights;
+ break;
+ }
+ default:
+ rb_bug("Unsupported node %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
+ break;
+ }
+
+ bool has_rest = (rest != NULL) && PM_NODE_TYPE_P(rest, PM_SPLAT_NODE) && ((const pm_splat_node_t *) rest)->expression != NULL;
+ bool has_posts = rights->size > 0;
+
+ // The first instruction in the writes sequence is going to spread the
+ // top value of the stack onto the number of values that we're going to
+ // write.
+ PUSH_INSN2(writes, location, expandarray, INT2FIX(lefts->size), INT2FIX((has_rest || has_posts) ? 1 : 0));
+
+ // We need to keep track of some additional state information as we're
+ // going through the targets because we will need to revisit them once
+ // we know how many values are being pushed onto the stack.
+ pm_multi_target_state_t target_state = { 0 };
+ size_t base_position = state == NULL ? 0 : state->position;
+ size_t splat_position = has_rest ? 1 : 0;
+
+ // Next, we'll iterate through all of the leading targets.
+ for (size_t index = 0; index < lefts->size; index++) {
+ const pm_node_t *target = lefts->nodes[index];
+ target_state.position = lefts->size - index + splat_position + base_position;
+ pm_compile_target_node(iseq, target, parents, writes, cleanup, scope_node, &target_state);
+ }
+
+ // Next, we'll compile the rest target if there is one.
+ if (has_rest) {
+ const pm_node_t *target = ((const pm_splat_node_t *) rest)->expression;
+ target_state.position = 1 + rights->size + base_position;
+
+ if (has_posts) {
+ PUSH_INSN2(writes, location, expandarray, INT2FIX(rights->size), INT2FIX(3));
+ }
+
+ pm_compile_target_node(iseq, target, parents, writes, cleanup, scope_node, &target_state);
+ }
+
+ // Finally, we'll compile the trailing targets.
+ if (has_posts) {
+ if (!has_rest && rest != NULL) {
+ PUSH_INSN2(writes, location, expandarray, INT2FIX(rights->size), INT2FIX(2));
+ }
+
+ for (size_t index = 0; index < rights->size; index++) {
+ const pm_node_t *target = rights->nodes[index];
+ target_state.position = rights->size - index + base_position;
+ pm_compile_target_node(iseq, target, parents, writes, cleanup, scope_node, &target_state);
+ }
+ }
+
+ // Now, we need to go back and modify the topn instructions in order to
+ // ensure they can correctly retrieve the parent expressions.
+ pm_multi_target_state_update(&target_state);
+
+ if (state != NULL) state->stack_size += target_state.stack_size;
+
+ return target_state.stack_size;
+}
+
+/**
+ * When compiling a for loop, we need to write the iteration variable to
+ * whatever expression exists in the index slot. This function performs that
+ * compilation.
+ */
+static void
+pm_compile_for_node_index(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node)
+{
+ const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
+
+ switch (PM_NODE_TYPE(node)) {
+ case PM_LOCAL_VARIABLE_TARGET_NODE: {
+ // For local variables, all we have to do is retrieve the value and then
+ // compile the index node.
+ PUSH_GETLOCAL(ret, location, 1, 0);
+ pm_compile_target_node(iseq, node, ret, ret, ret, scope_node, NULL);
+ break;
+ }
+ case PM_CLASS_VARIABLE_TARGET_NODE:
+ case PM_CONSTANT_TARGET_NODE:
+ case PM_GLOBAL_VARIABLE_TARGET_NODE:
+ case PM_INSTANCE_VARIABLE_TARGET_NODE:
+ case PM_CONSTANT_PATH_TARGET_NODE:
+ case PM_CALL_TARGET_NODE:
+ case PM_INDEX_TARGET_NODE: {
+ // For other targets, we need to potentially compile the parent or
+ // owning expression of this target, then retrieve the value, expand it,
+ // and then compile the necessary writes.
+ DECL_ANCHOR(writes);
+ INIT_ANCHOR(writes);
+
+ DECL_ANCHOR(cleanup);
+ INIT_ANCHOR(cleanup);
+
+ pm_multi_target_state_t state = { 0 };
+ state.position = 1;
+ pm_compile_target_node(iseq, node, ret, writes, cleanup, scope_node, &state);
+
+ PUSH_GETLOCAL(ret, location, 1, 0);
+ PUSH_INSN2(ret, location, expandarray, INT2FIX(1), INT2FIX(0));
+
+ PUSH_SEQ(ret, writes);
+ PUSH_SEQ(ret, cleanup);
+
+ pm_multi_target_state_update(&state);
+ break;
+ }
+ case PM_MULTI_TARGET_NODE: {
+ DECL_ANCHOR(writes);
+ INIT_ANCHOR(writes);
+
+ DECL_ANCHOR(cleanup);
+ INIT_ANCHOR(cleanup);
+
+ pm_compile_target_node(iseq, node, ret, writes, cleanup, scope_node, NULL);
+
+ LABEL *not_single = NEW_LABEL(location.line);
+ LABEL *not_ary = NEW_LABEL(location.line);
+
+ // When there are multiple targets, we'll do a bunch of work to convert
+ // the value into an array before we expand it. Effectively we're trying
+ // to accomplish:
+ //
+ // (args.length == 1 && Array.try_convert(args[0])) || args
+ //
+ PUSH_GETLOCAL(ret, location, 1, 0);
+ PUSH_INSN(ret, location, dup);
+ PUSH_CALL(ret, location, idLength, INT2FIX(0));
+ PUSH_INSN1(ret, location, putobject, INT2FIX(1));
+ PUSH_CALL(ret, location, idEq, INT2FIX(1));
+ PUSH_INSNL(ret, location, branchunless, not_single);
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSN1(ret, location, putobject, INT2FIX(0));
+ PUSH_CALL(ret, location, idAREF, INT2FIX(1));
+ PUSH_INSN1(ret, location, putobject, rb_cArray);
+ PUSH_INSN(ret, location, swap);
+ PUSH_CALL(ret, location, rb_intern("try_convert"), INT2FIX(1));
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSNL(ret, location, branchunless, not_ary);
+ PUSH_INSN(ret, location, swap);
+
+ PUSH_LABEL(ret, not_ary);
+ PUSH_INSN(ret, location, pop);
+
+ PUSH_LABEL(ret, not_single);
+ PUSH_SEQ(ret, writes);
+ PUSH_SEQ(ret, cleanup);
+ break;
+ }
+ default:
+ rb_bug("Unexpected node type for index in for node: %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
+ break;
+ }
+}
+
+static void
+pm_compile_rescue(rb_iseq_t *iseq, const pm_begin_node_t *cast, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
+{
+ const pm_parser_t *parser = scope_node->parser;
+
+ LABEL *lstart = NEW_LABEL(node_location->line);
+ LABEL *lend = NEW_LABEL(node_location->line);
+ LABEL *lcont = NEW_LABEL(node_location->line);
+
+ pm_scope_node_t rescue_scope_node;
+ pm_scope_node_init((const pm_node_t *) cast->rescue_clause, &rescue_scope_node, scope_node);
+
+ rb_iseq_t *rescue_iseq = NEW_CHILD_ISEQ(
+ &rescue_scope_node,
+ rb_str_concat(rb_str_new2("rescue in "), ISEQ_BODY(iseq)->location.label),
+ ISEQ_TYPE_RESCUE,
+ pm_node_line_number(parser, (const pm_node_t *) cast->rescue_clause)
+ );
+
+ pm_scope_node_destroy(&rescue_scope_node);
+
+ lstart->rescued = LABEL_RESCUE_BEG;
+ lend->rescued = LABEL_RESCUE_END;
+ PUSH_LABEL(ret, lstart);
+
+ bool prev_in_rescue = ISEQ_COMPILE_DATA(iseq)->in_rescue;
+ ISEQ_COMPILE_DATA(iseq)->in_rescue = true;
+
+ if (cast->statements != NULL) {
+ PM_COMPILE_NOT_POPPED((const pm_node_t *) cast->statements);
+ }
+ else {
+ PUSH_INSN(ret, *node_location, putnil);
+ }
+
+ ISEQ_COMPILE_DATA(iseq)->in_rescue = prev_in_rescue;
+ PUSH_LABEL(ret, lend);
+
+ if (cast->else_clause != NULL) {
+ if (!popped) PUSH_INSN(ret, *node_location, pop);
+ PM_COMPILE((const pm_node_t *) cast->else_clause);
+ }
+
+ PUSH_INSN(ret, *node_location, nop);
+ PUSH_LABEL(ret, lcont);
+
+ if (popped) PUSH_INSN(ret, *node_location, pop);
+ PUSH_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue_iseq, lcont);
+ PUSH_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, NULL, lstart);
+}
+
+static void
+pm_compile_ensure(rb_iseq_t *iseq, const pm_begin_node_t *cast, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
+{
+ const pm_parser_t *parser = scope_node->parser;
+ const pm_statements_node_t *statements = cast->ensure_clause->statements;
+ const pm_line_column_t location = statements != NULL ? PM_NODE_START_LINE_COLUMN(parser, statements) : *node_location;
+
+ LABEL *estart = NEW_LABEL(location.line);
+ LABEL *eend = NEW_LABEL(location.line);
+ LABEL *econt = NEW_LABEL(location.line);
+
+ struct ensure_range er;
+ struct iseq_compile_data_ensure_node_stack enl;
+ struct ensure_range *erange;
+
+ er.begin = estart;
+ er.end = eend;
+ er.next = 0;
+ push_ensure_entry(iseq, &enl, &er, (void *) cast->ensure_clause);
+
+ PUSH_LABEL(ret, estart);
+ if (cast->rescue_clause) {
+ pm_compile_rescue(iseq, cast, &location, ret, popped, scope_node);
+ }
+ else {
+ if (cast->statements) {
+ PM_COMPILE((const pm_node_t *) cast->statements);
+ }
+ else if (!popped) {
+ PUSH_INSN(ret, *node_location, putnil);
+ }
+ }
+
+ PUSH_LABEL(ret, eend);
+ PUSH_LABEL(ret, econt);
+
+ pm_scope_node_t next_scope_node;
+ pm_scope_node_init((const pm_node_t *) cast->ensure_clause, &next_scope_node, scope_node);
+
+ rb_iseq_t *child_iseq = NEW_CHILD_ISEQ(
+ &next_scope_node,
+ rb_str_concat(rb_str_new2("ensure in "), ISEQ_BODY(iseq)->location.label),
+ ISEQ_TYPE_ENSURE,
+ location.line
+ );
+
+ pm_scope_node_destroy(&next_scope_node);
+ ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq;
+
+ erange = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->erange;
+ if (estart->link.next != &eend->link) {
+ while (erange) {
+ PUSH_CATCH_ENTRY(CATCH_TYPE_ENSURE, erange->begin, erange->end, child_iseq, econt);
+ erange = erange->next;
+ }
+ }
+ ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enl.prev;
+
+ // Compile the ensure entry
+ if (statements != NULL) {
+ PM_COMPILE((const pm_node_t *) statements);
+ if (!popped) PUSH_INSN(ret, *node_location, pop);
+ }
+}
+
+/**
+ * Returns true if the given call node can use the opt_str_uminus or
+ * opt_str_freeze instructions as an optimization with the current iseq options.
+ */
+static inline bool
+pm_opt_str_freeze_p(const rb_iseq_t *iseq, const pm_call_node_t *node)
+{
+ return (
+ !PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) &&
+ node->receiver != NULL &&
+ PM_NODE_TYPE_P(node->receiver, PM_STRING_NODE) &&
+ node->arguments == NULL &&
+ node->block == NULL &&
+ ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction
+ );
+}
+
+/**
+ * Returns true if the given call node can use the opt_aref_with optimization
+ * with the current iseq options.
+ */
+static inline bool
+pm_opt_aref_with_p(const rb_iseq_t *iseq, const pm_call_node_t *node)
+{
+ return (
+ !PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) &&
+ node->arguments != NULL &&
+ PM_NODE_TYPE_P((const pm_node_t *) node->arguments, PM_ARGUMENTS_NODE) &&
+ ((const pm_arguments_node_t *) node->arguments)->arguments.size == 1 &&
+ PM_NODE_TYPE_P(((const pm_arguments_node_t *) node->arguments)->arguments.nodes[0], PM_STRING_NODE) &&
+ node->block == NULL &&
+ !PM_NODE_FLAG_P(((const pm_arguments_node_t *) node->arguments)->arguments.nodes[0], PM_STRING_FLAGS_FROZEN) &&
+ ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction
+ );
+}
+
+/**
+ * Returns true if the given call node can use the opt_aset_with optimization
+ * with the current iseq options.
+ */
+static inline bool
+pm_opt_aset_with_p(const rb_iseq_t *iseq, const pm_call_node_t *node)
+{
+ return (
+ !PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) &&
+ node->arguments != NULL &&
+ PM_NODE_TYPE_P((const pm_node_t *) node->arguments, PM_ARGUMENTS_NODE) &&
+ ((const pm_arguments_node_t *) node->arguments)->arguments.size == 2 &&
+ PM_NODE_TYPE_P(((const pm_arguments_node_t *) node->arguments)->arguments.nodes[0], PM_STRING_NODE) &&
+ node->block == NULL &&
+ !PM_NODE_FLAG_P(((const pm_arguments_node_t *) node->arguments)->arguments.nodes[0], PM_STRING_FLAGS_FROZEN) &&
+ ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction
+ );
+}
+
+/**
+ * Compile the instructions necessary to read a constant, based on the options
+ * of the current iseq.
+ */
+static void
+pm_compile_constant_read(rb_iseq_t *iseq, VALUE name, const pm_location_t *name_loc, LINK_ANCHOR *const ret, const pm_scope_node_t *scope_node)
+{
+ const pm_line_column_t location = PM_LOCATION_START_LINE_COLUMN(scope_node->parser, name_loc);
+
+ if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
+ ISEQ_BODY(iseq)->ic_size++;
+ VALUE segments = rb_ary_new_from_args(1, name);
+ PUSH_INSN1(ret, location, opt_getconstant_path, segments);
+ }
+ else {
+ PUSH_INSN(ret, location, putnil);
+ PUSH_INSN1(ret, location, putobject, Qtrue);
+ PUSH_INSN1(ret, location, getconstant, name);
+ }
+}
+
+/**
+ * Returns a Ruby array of the parts of the constant path node if it is constant
+ * reads all of the way down. If it isn't, then Qnil is returned.
+ */
+static VALUE
+pm_constant_path_parts(const pm_node_t *node, const pm_scope_node_t *scope_node)
+{
+ VALUE parts = rb_ary_new();
+
+ while (true) {
+ switch (PM_NODE_TYPE(node)) {
+ case PM_CONSTANT_READ_NODE: {
+ const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node;
+ VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
+
+ rb_ary_unshift(parts, name);
+ return parts;
+ }
+ case PM_CONSTANT_PATH_NODE: {
+ const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node;
+ VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, ((const pm_constant_read_node_t *) cast->child)->name));
+
+ rb_ary_unshift(parts, name);
+ if (cast->parent == NULL) {
+ rb_ary_unshift(parts, ID2SYM(idNULL));
+ return parts;
+ }
+
+ node = cast->parent;
+ break;
+ }
+ default:
+ return Qnil;
+ }
+ }
+}
+
+/**
+ * Compile a constant path into two sequences of instructions, one for the
+ * owning expression if there is one (prefix) and one for the constant reads
+ * (body).
+ */
+static void
+pm_compile_constant_path(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const prefix, LINK_ANCHOR *const body, bool popped, pm_scope_node_t *scope_node)
+{
+ const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
+
+ switch (PM_NODE_TYPE(node)) {
+ case PM_CONSTANT_READ_NODE: {
+ const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node;
+ VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
+
+ PUSH_INSN1(body, location, putobject, Qtrue);
+ PUSH_INSN1(body, location, getconstant, name);
+ break;
+ }
+ case PM_CONSTANT_PATH_NODE: {
+ const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node;
+ VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, ((const pm_constant_read_node_t *) cast->child)->name));
+
+ if (cast->parent == NULL) {
+ PUSH_INSN(body, location, pop);
+ PUSH_INSN1(body, location, putobject, rb_cObject);
+ PUSH_INSN1(body, location, putobject, Qtrue);
+ PUSH_INSN1(body, location, getconstant, name);
+ }
+ else {
+ pm_compile_constant_path(iseq, cast->parent, prefix, body, false, scope_node);
+ PUSH_INSN1(body, location, putobject, Qfalse);
+ PUSH_INSN1(body, location, getconstant, name);
+ }
+ break;
+ }
+ default:
+ PM_COMPILE_INTO_ANCHOR(prefix, node);
+ break;
+ }
+}
+
+/**
+ * When we're compiling a case node, it's possible that we can speed it up using
+ * a dispatch hash, which will allow us to jump directly to the correct when
+ * clause body based on a hash lookup of the value. This can only happen when
+ * the conditions are literals that can be compiled into a hash key.
+ *
+ * This function accepts a dispatch hash and the condition of a when clause. It
+ * is responsible for compiling the condition into a hash key and then adding it
+ * to the dispatch hash.
+ *
+ * If the value can be successfully compiled into the hash, then this function
+ * returns the dispatch hash with the new key added. If the value cannot be
+ * compiled into the hash, then this function returns Qundef. In the case of
+ * Qundef, this function is signaling that the caller should abandon the
+ * optimization entirely.
+ */
+static VALUE
+pm_compile_case_node_dispatch(rb_iseq_t *iseq, VALUE dispatch, const pm_node_t *node, LABEL *label, const pm_scope_node_t *scope_node)
+{
+ VALUE key = Qundef;
+
+ switch (PM_NODE_TYPE(node)) {
+ case PM_FLOAT_NODE: {
+ key = pm_static_literal_value(iseq, node, scope_node);
+ double intptr;
+
+ if (modf(RFLOAT_VALUE(key), &intptr) == 0.0) {
+ key = (FIXABLE(intptr) ? LONG2FIX((long) intptr) : rb_dbl2big(intptr));
+ }
+
+ break;
+ }
+ case PM_FALSE_NODE:
+ case PM_INTEGER_NODE:
+ case PM_NIL_NODE:
+ case PM_SOURCE_FILE_NODE:
+ case PM_SOURCE_LINE_NODE:
+ case PM_SYMBOL_NODE:
+ case PM_TRUE_NODE:
+ key = pm_static_literal_value(iseq, node, scope_node);
+ break;
+ case PM_STRING_NODE: {
+ const pm_string_node_t *cast = (const pm_string_node_t *) node;
+ key = parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
+ break;
+ }
+ default:
+ return Qundef;
+ }
+
+ if (NIL_P(rb_hash_lookup(dispatch, key))) {
+ rb_hash_aset(dispatch, key, ((VALUE) label) | 1);
+ }
+
+ return dispatch;
+}
+
+/*
+ * Compiles a prism node into instruction sequences.
+ *
+ * iseq - The current instruction sequence object (used for locals)
+ * node - The prism node to compile
+ * ret - The linked list of instructions to append instructions onto
+ * popped - True if compiling something with no side effects, so instructions don't
+ * need to be added
+ * scope_node - Stores parser and local information
+ */
+static void
+pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
+{
+ const pm_parser_t *parser = scope_node->parser;
+ const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(parser, node);
+ int lineno = (int) location.line;
+
+ if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_NEWLINE) && ISEQ_COMPILE_DATA(iseq)->last_line != lineno) {
+ int event = RUBY_EVENT_LINE;
+
+ ISEQ_COMPILE_DATA(iseq)->last_line = lineno;
+ if (ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq)) {
+ event |= RUBY_EVENT_COVERAGE_LINE;
+ }
+ PUSH_TRACE(ret, event);
+ }
+
+ switch (PM_NODE_TYPE(node)) {
+ case PM_ALIAS_GLOBAL_VARIABLE_NODE: {
+ // alias $foo $bar
+ // ^^^^^^^^^^^^^^^
+ const pm_alias_global_variable_node_t *cast = (const pm_alias_global_variable_node_t *) node;
+ PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+
+ const pm_location_t *new_name_loc = &cast->new_name->location;
+ PUSH_INSN1(ret, location, putobject, ID2SYM(rb_intern3((const char *) new_name_loc->start, new_name_loc->end - new_name_loc->start, scope_node->encoding)));
+
+ const pm_location_t *old_name_loc = &cast->old_name->location;
+ PUSH_INSN1(ret, location, putobject, ID2SYM(rb_intern3((const char *) old_name_loc->start, old_name_loc->end - old_name_loc->start, scope_node->encoding)));
+
+ PUSH_SEND(ret, location, id_core_set_variable_alias, INT2FIX(2));
+ if (popped) PUSH_INSN(ret, location, pop);
+
+ return;
+ }
+ case PM_ALIAS_METHOD_NODE: {
+ // alias foo bar
+ // ^^^^^^^^^^^^^
+ const pm_alias_method_node_t *cast = (const pm_alias_method_node_t *) node;
+
+ PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
+ PM_COMPILE_NOT_POPPED(cast->new_name);
+ PM_COMPILE_NOT_POPPED(cast->old_name);
+
+ PUSH_SEND(ret, location, id_core_set_method_alias, INT2FIX(3));
+ if (popped) PUSH_INSN(ret, location, pop);
+
+ return;
+ }
+ case PM_AND_NODE: {
+ // a and b
+ // ^^^^^^^
+ const pm_and_node_t *cast = (const pm_and_node_t *) node;
+ LABEL *end_label = NEW_LABEL(lineno);
+
+ PM_COMPILE_NOT_POPPED(cast->left);
+ if (!popped) PUSH_INSN(ret, location, dup);
+ PUSH_INSNL(ret, location, branchunless, end_label);
+
+ if (!popped) PUSH_INSN(ret, location, pop);
+ PM_COMPILE(cast->right);
+ PUSH_LABEL(ret, end_label);
+
+ return;
+ }
+ case PM_ARGUMENTS_NODE:
+ // These are ArgumentsNodes that are not compiled directly by their
+ // parent call nodes, used in the cases of NextNodes, ReturnNodes, and
+ // BreakNodes. They can create an array like ArrayNode.
+ case PM_ARRAY_NODE: {
+ const pm_node_list_t *elements;
+
+ if (PM_NODE_TYPE(node) == PM_ARGUMENTS_NODE) {
+ // break foo
+ // ^^^
+ const pm_arguments_node_t *cast = (const pm_arguments_node_t *) node;
+ elements = &cast->arguments;
+
+ // If we are only returning a single element through one of the jump
+ // nodes, then we will only compile that node directly.
+ if (elements->size == 1) {
+ PM_COMPILE(elements->nodes[0]);
+ return;
+ }
+ }
+ else {
+ // [foo, bar, baz]
+ // ^^^^^^^^^^^^^^^
+ const pm_array_node_t *cast = (const pm_array_node_t *) node;
+ elements = &cast->elements;
+ }
+
+ // If every node in the array is static, then we can compile the entire
+ // array now instead of later.
+ if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
+ // We're only going to compile this node if it's not popped. If it
+ // is popped, then we know we don't need to do anything since it's
+ // statically known.
+ if (!popped) {
+ if (elements->size) {
+ VALUE value = pm_static_literal_value(iseq, node, scope_node);
+ PUSH_INSN1(ret, location, duparray, value);
+ }
+ else {
+ PUSH_INSN1(ret, location, newarray, INT2FIX(0));
+ }
+ }
+ }
+ else {
+ // Here since we know there are possible side-effects inside the
+ // array contents, we're going to build it entirely at runtime.
+ // We'll do this by pushing all of the elements onto the stack and
+ // then combining them with newarray.
+ //
+ // If this array is popped, then this serves only to ensure we enact
+ // all side-effects (like method calls) that are contained within
+ // the array contents.
+ //
+ // We treat all sequences of non-splat elements as their
+ // own arrays, followed by a newarray, and then continually
+ // concat the arrays with the SplatNode nodes.
+ int new_array_size = 0;
+
+ bool need_to_concat_array = false;
+ bool has_kw_splat = false;
+
+ for (size_t index = 0; index < elements->size; index++) {
+ const pm_node_t *element = elements->nodes[index];
+
+ if (PM_NODE_TYPE_P(element, PM_SPLAT_NODE)) {
+ const pm_splat_node_t *splat_element = (const pm_splat_node_t *) element;
+
+ // If we already have non-splat elements, we need to emit a
+ // newarray instruction.
+ if (new_array_size > 0) {
+ PUSH_INSN1(ret, location, newarray, INT2FIX(new_array_size));
+ new_array_size = 0;
+
+ // We don't want to emit a concat array in the case
+ // where we're seeing our first splat, and already have
+ // elements.
+ if (need_to_concat_array) PUSH_INSN(ret, location, concatarray);
+ }
+
+ if (splat_element->expression) {
+ PM_COMPILE_NOT_POPPED(splat_element->expression);
+ }
+ else {
+ pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_MULT, 0);
+ PUSH_GETLOCAL(ret, location, index.index, index.level);
+ }
+
+ if (index > 0) {
+ PUSH_INSN(ret, location, concatarray);
+ }
+ else {
+ // If this is the first element of the array then we
+ // need to splatarray the elements into the list.
+ PUSH_INSN1(ret, location, splatarray, Qtrue);
+ }
+
+ // Since we have now seen a splat and are concat-ing arrays,
+ // all subsequent splats will need to concat as well.
+ need_to_concat_array = true;
+ }
+ else if (PM_NODE_TYPE_P(element, PM_KEYWORD_HASH_NODE)) {
+ new_array_size++;
+ has_kw_splat = true;
+ pm_compile_hash_elements(iseq, element, &((const pm_keyword_hash_node_t *) element)->elements, ret, scope_node);
+ }
+ else {
+ new_array_size++;
+ PM_COMPILE_NOT_POPPED(element);
+ }
+ }
+
+ if (new_array_size) {
+ if (has_kw_splat) {
+ PUSH_INSN1(ret, location, newarraykwsplat, INT2FIX(new_array_size));
+ }
+ else {
+ PUSH_INSN1(ret, location, newarray, INT2FIX(new_array_size));
+ }
+
+ if (need_to_concat_array) PUSH_INSN(ret, location, concatarray);
+ }
+
+ if (popped) PUSH_INSN(ret, location, pop);
+ }
+ return;
+ }
+ case PM_ASSOC_NODE: {
+ // { foo: 1 }
+ // ^^^^^^
+ //
+ // foo(bar: 1)
+ // ^^^^^^
+ const pm_assoc_node_t *cast = (const pm_assoc_node_t *) node;
+
+ PM_COMPILE(cast->key);
+ PM_COMPILE(cast->value);
+
+ return;
+ }
+ case PM_ASSOC_SPLAT_NODE: {
+ // { **foo }
+ // ^^^^^
+ //
+ // def foo(**); bar(**); end
+ // ^^
+ const pm_assoc_splat_node_t *cast = (const pm_assoc_splat_node_t *) node;
+
+ if (cast->value != NULL) {
+ PM_COMPILE(cast->value);
+ }
+ else if (!popped) {
+ pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_POW, 0);
+ PUSH_GETLOCAL(ret, location, index.index, index.level);
+ }
+
+ return;
+ }
+ case PM_BACK_REFERENCE_READ_NODE: {
+ // $+
+ // ^^
+ if (!popped) {
+ // Since a back reference is `$<char>`, ruby represents the ID as the
+ // an rb_intern on the value after the `$`.
+ char *char_ptr = (char *)(node->location.start) + 1;
+ ID backref_val = INT2FIX(rb_intern2(char_ptr, 1)) << 1 | 1;
+ PUSH_INSN2(ret, location, getspecial, INT2FIX(1), backref_val);
+ }
+ return;
+ }
+ case PM_BEGIN_NODE: {
+ // begin end
+ // ^^^^^^^^^
+ const pm_begin_node_t *cast = (const pm_begin_node_t *) node;
+
+ if (cast->ensure_clause) {
+ // Compiling the ensure clause will compile the rescue clause (if
+ // there is one), which will compile the begin statements.
+ pm_compile_ensure(iseq, cast, &location, ret, popped, scope_node);
+ }
+ else if (cast->rescue_clause) {
+ // Compiling rescue will compile begin statements (if applicable).
+ pm_compile_rescue(iseq, cast, &location, ret, popped, scope_node);
+ }
+ else {
+ // If there is neither ensure or rescue, the just compile the
+ // statements.
+ if (cast->statements != NULL) {
+ PM_COMPILE((const pm_node_t *) cast->statements);
+ }
+ else if (!popped) {
+ PUSH_INSN(ret, location, putnil);
+ }
+ }
+ return;
+ }
+ case PM_BLOCK_ARGUMENT_NODE: {
+ // foo(&bar)
+ // ^^^^
+ const pm_block_argument_node_t *cast = (const pm_block_argument_node_t *) node;
+
+ if (cast->expression != NULL) {
+ PM_COMPILE(cast->expression);
+ }
+ else {
+ // If there's no expression, this must be block forwarding.
+ pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_AND, 0);
+ PUSH_INSN2(ret, location, getblockparamproxy, INT2FIX(local_index.index + VM_ENV_DATA_SIZE - 1), INT2FIX(local_index.level));
+ }
+ return;
+ }
+ case PM_BREAK_NODE: {
+ // break
+ // ^^^^^
+ //
+ // break foo
+ // ^^^^^^^^^
+ const pm_break_node_t *cast = (const pm_break_node_t *) node;
+ unsigned long throw_flag = 0;
+
+ if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
+ /* while/until */
+ LABEL *splabel = NEW_LABEL(0);
+ PUSH_LABEL(ret, splabel);
+ PUSH_ADJUST(ret, location, ISEQ_COMPILE_DATA(iseq)->redo_label);
+
+ if (cast->arguments != NULL) {
+ PM_COMPILE_NOT_POPPED((const pm_node_t *) cast->arguments);
+ }
+ else {
+ PUSH_INSN(ret, location, putnil);
+ }
+
+ pm_add_ensure_iseq(ret, iseq, 0, scope_node);
+ PUSH_INSNL(ret, location, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
+ PUSH_ADJUST_RESTORE(ret, splabel);
+ if (!popped) PUSH_INSN(ret, location, putnil);
+ }
+ else {
+ const rb_iseq_t *ip = iseq;
+
+ while (ip) {
+ if (!ISEQ_COMPILE_DATA(ip)) {
+ ip = 0;
+ break;
+ }
+
+ if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
+ throw_flag = VM_THROW_NO_ESCAPE_FLAG;
+ }
+ else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
+ throw_flag = 0;
+ }
+ else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
+ COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with break");
+ return;
+ }
+ else {
+ ip = ISEQ_BODY(ip)->parent_iseq;
+ continue;
+ }
+
+ /* escape from block */
+ if (cast->arguments != NULL) {
+ PM_COMPILE_NOT_POPPED((const pm_node_t *) cast->arguments);
+ }
+ else {
+ PUSH_INSN(ret, location, putnil);
+ }
+
+ PUSH_INSN1(ret, location, throw, INT2FIX(throw_flag | TAG_BREAK));
+ if (popped) PUSH_INSN(ret, location, pop);
+
+ return;
+ }
+
+ COMPILE_ERROR(ERROR_ARGS "Invalid break");
+ rb_bug("Invalid break");
+ }
+ return;
+ }
+ case PM_CALL_NODE: {
+ // foo
+ // ^^^
+ //
+ // foo.bar
+ // ^^^^^^^
+ //
+ // foo.bar() {}
+ // ^^^^^^^^^^^^
+ const pm_call_node_t *cast = (const pm_call_node_t *) node;
+ LABEL *start = NEW_LABEL(location.line);
+
+ if (cast->block) {
+ PUSH_LABEL(ret, start);
+ }
+
+ ID method_id = pm_constant_id_lookup(scope_node, cast->name);
+
+ switch (method_id) {
+ case idUMinus: {
+ if (pm_opt_str_freeze_p(iseq, cast)) {
+ VALUE value = parse_static_literal_string(iseq, scope_node, cast->receiver, &((const pm_string_node_t * ) cast->receiver)->unescaped);
+ PUSH_INSN2(ret, location, opt_str_uminus, value, new_callinfo(iseq, idUMinus, 0, 0, NULL, FALSE));
+ return;
+ }
+ break;
+ }
+ case idFreeze: {
+ if (pm_opt_str_freeze_p(iseq, cast)) {
+ VALUE value = parse_static_literal_string(iseq, scope_node, cast->receiver, &((const pm_string_node_t * ) cast->receiver)->unescaped);
+ PUSH_INSN2(ret, location, opt_str_freeze, value, new_callinfo(iseq, idFreeze, 0, 0, NULL, FALSE));
+ return;
+ }
+ break;
+ }
+ case idAREF: {
+ if (pm_opt_aref_with_p(iseq, cast)) {
+ const pm_string_node_t *string = (const pm_string_node_t *) ((const pm_arguments_node_t *) cast->arguments)->arguments.nodes[0];
+ VALUE value = parse_static_literal_string(iseq, scope_node, (const pm_node_t *) string, &string->unescaped);
+
+ PM_COMPILE_NOT_POPPED(cast->receiver);
+ PUSH_INSN2(ret, location, opt_aref_with, value, new_callinfo(iseq, idAREF, 1, 0, NULL, FALSE));
+
+ if (popped) {
+ PUSH_INSN(ret, location, pop);
+ }
+
+ return;
+ }
+ break;
+ }
+ case idASET: {
+ if (pm_opt_aset_with_p(iseq, cast)) {
+ const pm_string_node_t *string = (const pm_string_node_t *) ((const pm_arguments_node_t *) cast->arguments)->arguments.nodes[0];
+ VALUE value = parse_static_literal_string(iseq, scope_node, (const pm_node_t *) string, &string->unescaped);
+
+ PM_COMPILE_NOT_POPPED(cast->receiver);
+ PM_COMPILE_NOT_POPPED(((const pm_arguments_node_t *) cast->arguments)->arguments.nodes[1]);
+
+ if (!popped) {
+ PUSH_INSN(ret, location, swap);
+ PUSH_INSN1(ret, location, topn, INT2FIX(1));
+ }
+
+ PUSH_INSN2(ret, location, opt_aset_with, value, new_callinfo(iseq, idASET, 2, 0, NULL, FALSE));
+ PUSH_INSN(ret, location, pop);
+ return;
+ }
+ break;
+ }
+ }
+
+ if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) && !popped) {
+ PUSH_INSN(ret, location, putnil);
+ }
+
+ if (cast->receiver == NULL) {
+ PUSH_INSN(ret, location, putself);
+ }
+ else {
+ PM_COMPILE_NOT_POPPED(cast->receiver);
+ }
+
+ pm_compile_call(iseq, cast, ret, popped, scope_node, method_id, start);
+ return;
+ }
+ case PM_CALL_AND_WRITE_NODE: {
+ // foo.bar &&= baz
+ // ^^^^^^^^^^^^^^^
+ const pm_call_and_write_node_t *cast = (const pm_call_and_write_node_t *) node;
+ pm_compile_call_and_or_write_node(iseq, true, cast->receiver, cast->value, cast->write_name, cast->read_name, PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION), &location, ret, popped, scope_node);
+ return;
+ }
+ case PM_CALL_OR_WRITE_NODE: {
+ // foo.bar ||= baz
+ // ^^^^^^^^^^^^^^^
+ const pm_call_or_write_node_t *cast = (const pm_call_or_write_node_t *) node;
+ pm_compile_call_and_or_write_node(iseq, false, cast->receiver, cast->value, cast->write_name, cast->read_name, PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION), &location, ret, popped, scope_node);
+ return;
+ }
+ case PM_CALL_OPERATOR_WRITE_NODE: {
+ // foo.bar += baz
+ // ^^^^^^^^^^^^^^^
+ //
+ // Call operator writes occur when you have a call node on the left-hand
+ // side of a write operator that is not `=`. As an example,
+ // `foo.bar *= 1`. This breaks down to caching the receiver on the
+ // stack and then performing three method calls, one to read the value,
+ // one to compute the result, and one to write the result back to the
+ // receiver.
+ const pm_call_operator_write_node_t *cast = (const pm_call_operator_write_node_t *) node;
+ int flag = 0;
+
+ if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) {
+ flag = VM_CALL_FCALL;
+ }
+
+ PM_COMPILE_NOT_POPPED(cast->receiver);
+
+ LABEL *safe_label = NULL;
+ if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
+ safe_label = NEW_LABEL(location.line);
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSNL(ret, location, branchnil, safe_label);
+ }
+
+ PUSH_INSN(ret, location, dup);
+
+ ID id_read_name = pm_constant_id_lookup(scope_node, cast->read_name);
+ PUSH_SEND_WITH_FLAG(ret, location, id_read_name, INT2FIX(0), INT2FIX(flag));
+
+ PM_COMPILE_NOT_POPPED(cast->value);
+ ID id_operator = pm_constant_id_lookup(scope_node, cast->operator);
+ PUSH_SEND(ret, location, id_operator, INT2FIX(1));
+
+ if (!popped) {
+ PUSH_INSN(ret, location, swap);
+ PUSH_INSN1(ret, location, topn, INT2FIX(1));
+ }
+
+ ID id_write_name = pm_constant_id_lookup(scope_node, cast->write_name);
+ PUSH_SEND_WITH_FLAG(ret, location, id_write_name, INT2FIX(1), INT2FIX(flag));
+
+ if (safe_label != NULL && popped) PUSH_LABEL(ret, safe_label);
+ PUSH_INSN(ret, location, pop);
+ if (safe_label != NULL && !popped) PUSH_LABEL(ret, safe_label);
+
+ return;
+ }
+ case PM_CASE_NODE: {
+ // case foo; when bar; end
+ // ^^^^^^^^^^^^^^^^^^^^^^^
+ const pm_case_node_t *cast = (const pm_case_node_t *) node;
+ const pm_node_list_t *conditions = &cast->conditions;
+
+ // This is the anchor that we will compile the conditions of the various
+ // `when` nodes into. If a match is found, they will need to jump into
+ // the body_seq anchor to the correct spot.
+ DECL_ANCHOR(cond_seq);
+ INIT_ANCHOR(cond_seq);
+
+ // This is the anchor that we will compile the bodies of the various
+ // `when` nodes into. We'll make sure that the clauses that are compiled
+ // jump into the correct spots within this anchor.
+ DECL_ANCHOR(body_seq);
+ INIT_ANCHOR(body_seq);
+
+ // This is the label where all of the when clauses will jump to if they
+ // have matched and are done executing their bodies.
+ LABEL *end_label = NEW_LABEL(location.line);
+
+ // If we have a predicate on this case statement, then it's going to
+ // compare all of the various when clauses to the predicate. If we
+ // don't, then it's basically an if-elsif-else chain.
+ if (cast->predicate == NULL) {
+ // Establish branch coverage for the case node.
+ VALUE branches = Qfalse;
+ rb_code_location_t case_location = { 0 };
+ int branch_id = 0;
+
+ if (PM_BRANCH_COVERAGE_P(iseq)) {
+ case_location = pm_code_location(scope_node, (const pm_node_t *) cast);
+ branches = decl_branch_base(iseq, PTR2NUM(cast), &case_location, "case");
+ }
+
+ // Loop through each clauses in the case node and compile each of
+ // the conditions within them into cond_seq. If they match, they
+ // should jump into their respective bodies in body_seq.
+ for (size_t clause_index = 0; clause_index < conditions->size; clause_index++) {
+ const pm_when_node_t *clause = (const pm_when_node_t *) conditions->nodes[clause_index];
+ const pm_node_list_t *conditions = &clause->conditions;
+
+ int clause_lineno = pm_node_line_number(parser, (const pm_node_t *) clause);
+ LABEL *label = NEW_LABEL(clause_lineno);
+ PUSH_LABEL(body_seq, label);
+
+ // Establish branch coverage for the when clause.
+ if (PM_BRANCH_COVERAGE_P(iseq)) {
+ rb_code_location_t branch_location = pm_code_location(scope_node, clause->statements != NULL ? ((const pm_node_t *) clause->statements) : ((const pm_node_t *) clause));
+ add_trace_branch_coverage(iseq, body_seq, &branch_location, branch_location.beg_pos.column, branch_id++, "when", branches);
+ }
+
+ if (clause->statements != NULL) {
+ pm_compile_node(iseq, (const pm_node_t *) clause->statements, body_seq, popped, scope_node);
+ }
+ else if (!popped) {
+ PUSH_INSN(body_seq, location, putnil);
+ }
+
+ PUSH_INSNL(body_seq, location, jump, end_label);
+
+ // Compile each of the conditions for the when clause into the
+ // cond_seq. Each one should have a unique condition and should
+ // jump to the subsequent one if it doesn't match.
+ for (size_t condition_index = 0; condition_index < conditions->size; condition_index++) {
+ const pm_node_t *condition = conditions->nodes[condition_index];
+
+ if (PM_NODE_TYPE_P(condition, PM_SPLAT_NODE)) {
+ pm_line_column_t cond_location = PM_NODE_START_LINE_COLUMN(parser, condition);
+ PUSH_INSN(cond_seq, cond_location, putnil);
+ pm_compile_node(iseq, condition, cond_seq, false, scope_node);
+ PUSH_INSN1(cond_seq, cond_location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_WHEN | VM_CHECKMATCH_ARRAY));
+ PUSH_INSNL(cond_seq, cond_location, branchif, label);
+ }
+ else {
+ LABEL *next_label = NEW_LABEL(pm_node_line_number(parser, condition));
+ pm_compile_branch_condition(iseq, cond_seq, condition, label, next_label, false, scope_node);
+ PUSH_LABEL(cond_seq, next_label);
+ }
+ }
+ }
+
+ // Establish branch coverage for the else clause (implicit or
+ // explicit).
+ if (PM_BRANCH_COVERAGE_P(iseq)) {
+ rb_code_location_t branch_location;
+
+ if (cast->consequent == NULL) {
+ branch_location = case_location;
+ } else if (cast->consequent->statements == NULL) {
+ branch_location = pm_code_location(scope_node, (const pm_node_t *) cast->consequent);
+ } else {
+ branch_location = pm_code_location(scope_node, (const pm_node_t *) cast->consequent->statements);
+ }
+
+ add_trace_branch_coverage(iseq, cond_seq, &branch_location, branch_location.beg_pos.column, branch_id, "else", branches);
+ }
+
+ // Compile the consequent else clause if there is one.
+ if (cast->consequent != NULL) {
+ pm_compile_node(iseq, (const pm_node_t *) cast->consequent, cond_seq, popped, scope_node);
+ }
+ else if (!popped) {
+ PUSH_SYNTHETIC_PUTNIL(cond_seq, iseq);
+ }
+
+ // Finally, jump to the end label if none of the other conditions
+ // have matched.
+ PUSH_INSNL(cond_seq, location, jump, end_label);
+ PUSH_SEQ(ret, cond_seq);
+ }
+ else {
+ // Establish branch coverage for the case node.
+ VALUE branches = Qfalse;
+ rb_code_location_t case_location;
+ int branch_id = 0;
+
+ if (PM_BRANCH_COVERAGE_P(iseq)) {
+ case_location = pm_code_location(scope_node, (const pm_node_t *) cast);
+ branches = decl_branch_base(iseq, PTR2NUM(cast), &case_location, "case");
+ }
+
+ // This is the label where everything will fall into if none of the
+ // conditions matched.
+ LABEL *else_label = NEW_LABEL(location.line);
+
+ // It's possible for us to speed up the case node by using a
+ // dispatch hash. This is a hash that maps the conditions of the
+ // various when clauses to the labels of their bodies. If we can
+ // compile the conditions into a hash key, then we can use a hash
+ // lookup to jump directly to the correct when clause body.
+ VALUE dispatch = Qundef;
+ if (ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
+ dispatch = rb_hash_new();
+ RHASH_TBL_RAW(dispatch)->type = &cdhash_type;
+ }
+
+ // We're going to loop through each of the conditions in the case
+ // node and compile each of their contents into both the cond_seq
+ // and the body_seq. Each condition will use its own label to jump
+ // from its conditions into its body.
+ //
+ // Note that none of the code in the loop below should be adding
+ // anything to ret, as we're going to be laying out the entire case
+ // node instructions later.
+ for (size_t clause_index = 0; clause_index < conditions->size; clause_index++) {
+ const pm_when_node_t *clause = (const pm_when_node_t *) conditions->nodes[clause_index];
+ pm_line_column_t clause_location = PM_NODE_START_LINE_COLUMN(parser, (const pm_node_t *) clause);
+
+ const pm_node_list_t *conditions = &clause->conditions;
+ LABEL *label = NEW_LABEL(clause_location.line);
+
+ // Compile each of the conditions for the when clause into the
+ // cond_seq. Each one should have a unique comparison that then
+ // jumps into the body if it matches.
+ for (size_t condition_index = 0; condition_index < conditions->size; condition_index++) {
+ const pm_node_t *condition = conditions->nodes[condition_index];
+ const pm_line_column_t condition_location = PM_NODE_START_LINE_COLUMN(parser, condition);
+
+ // If we haven't already abandoned the optimization, then
+ // we're going to try to compile the condition into the
+ // dispatch hash.
+ if (dispatch != Qundef) {
+ dispatch = pm_compile_case_node_dispatch(iseq, dispatch, condition, label, scope_node);
+ }
+
+ if (PM_NODE_TYPE_P(condition, PM_SPLAT_NODE)) {
+ PUSH_INSN(cond_seq, condition_location, dup);
+ pm_compile_node(iseq, condition, cond_seq, false, scope_node);
+ PUSH_INSN1(cond_seq, condition_location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY));
+ }
+ else {
+ if (PM_NODE_TYPE_P(condition, PM_STRING_NODE)) {
+ const pm_string_node_t *string = (const pm_string_node_t *) condition;
+ VALUE value = parse_static_literal_string(iseq, scope_node, condition, &string->unescaped);
+ PUSH_INSN1(cond_seq, condition_location, putobject, value);
+ }
+ else {
+ pm_compile_node(iseq, condition, cond_seq, false, scope_node);
+ }
+
+ PUSH_INSN1(cond_seq, condition_location, topn, INT2FIX(1));
+ PUSH_SEND_WITH_FLAG(cond_seq, condition_location, idEqq, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE));
+ }
+
+ PUSH_INSNL(cond_seq, condition_location, branchif, label);
+ }
+
+ // Now, add the label to the body and compile the body of the
+ // when clause. This involves popping the predicate, compiling
+ // the statements to be executed, and then compiling a jump to
+ // the end of the case node.
+ PUSH_LABEL(body_seq, label);
+ PUSH_INSN(body_seq, clause_location, pop);
+
+ // Establish branch coverage for the when clause.
+ if (PM_BRANCH_COVERAGE_P(iseq)) {
+ rb_code_location_t branch_location = pm_code_location(scope_node, clause->statements != NULL ? ((const pm_node_t *) clause->statements) : ((const pm_node_t *) clause));
+ add_trace_branch_coverage(iseq, body_seq, &branch_location, branch_location.beg_pos.column, branch_id++, "when", branches);
+ }
+
+ if (clause->statements != NULL) {
+ pm_compile_node(iseq, (const pm_node_t *) clause->statements, body_seq, popped, scope_node);
+ }
+ else if (!popped) {
+ PUSH_INSN(body_seq, clause_location, putnil);
+ }
+
+ PUSH_INSNL(body_seq, clause_location, jump, end_label);
+ }
+
+ // Now that we have compiled the conditions and the bodies of the
+ // various when clauses, we can compile the predicate, lay out the
+ // conditions, compile the fallback consequent if there is one, and
+ // finally put in the bodies of the when clauses.
+ PM_COMPILE_NOT_POPPED(cast->predicate);
+
+ // If we have a dispatch hash, then we'll use it here to create the
+ // optimization.
+ if (dispatch != Qundef) {
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSN2(ret, location, opt_case_dispatch, dispatch, else_label);
+ LABEL_REF(else_label);
+ }
+
+ PUSH_SEQ(ret, cond_seq);
+
+ // Compile either the explicit else clause or an implicit else
+ // clause.
+ PUSH_LABEL(ret, else_label);
+
+ if (cast->consequent != NULL) {
+ pm_line_column_t else_location = PM_NODE_START_LINE_COLUMN(parser, cast->consequent->statements != NULL ? ((const pm_node_t *) cast->consequent->statements) : ((const pm_node_t *) cast->consequent));
+ PUSH_INSN(ret, else_location, pop);
+
+ // Establish branch coverage for the else clause.
+ if (PM_BRANCH_COVERAGE_P(iseq)) {
+ rb_code_location_t branch_location = pm_code_location(scope_node, cast->consequent->statements != NULL ? ((const pm_node_t *) cast->consequent->statements) : ((const pm_node_t *) cast->consequent));
+ add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, branch_id, "else", branches);
+ }
+
+ PM_COMPILE((const pm_node_t *) cast->consequent);
+ PUSH_INSNL(ret, else_location, jump, end_label);
+ }
+ else {
+ PUSH_INSN(ret, location, pop);
+
+ // Establish branch coverage for the implicit else clause.
+ if (PM_BRANCH_COVERAGE_P(iseq)) {
+ add_trace_branch_coverage(iseq, ret, &case_location, case_location.beg_pos.column, branch_id, "else", branches);
+ }
+
+ if (!popped) PUSH_INSN(ret, location, putnil);
+ PUSH_INSNL(ret, location, jump, end_label);
+ }
+ }
+
+ PUSH_SEQ(ret, body_seq);
+ PUSH_LABEL(ret, end_label);
+
+ return;
+ }
+ case PM_CASE_MATCH_NODE: {
+ // case foo; in bar; end
+ // ^^^^^^^^^^^^^^^^^^^^^
+ //
+ // If you use the `case` keyword to create a case match node, it will
+ // match against all of the `in` clauses until it finds one that
+ // matches. If it doesn't find one, it can optionally fall back to an
+ // `else` clause. If none is present and a match wasn't found, it will
+ // raise an appropriate error.
+ const pm_case_match_node_t *cast = (const pm_case_match_node_t *) node;
+
+ // This is the anchor that we will compile the bodies of the various
+ // `in` nodes into. We'll make sure that the patterns that are compiled
+ // jump into the correct spots within this anchor.
+ DECL_ANCHOR(body_seq);
+ INIT_ANCHOR(body_seq);
+
+ // This is the anchor that we will compile the patterns of the various
+ // `in` nodes into. If a match is found, they will need to jump into the
+ // body_seq anchor to the correct spot.
+ DECL_ANCHOR(cond_seq);
+ INIT_ANCHOR(cond_seq);
+
+ // This label is used to indicate the end of the entire node. It is
+ // jumped to after the entire stack is cleaned up.
+ LABEL *end_label = NEW_LABEL(location.line);
+
+ // This label is used as the fallback for the case match. If no match is
+ // found, then we jump to this label. This is either an `else` clause or
+ // an error handler.
+ LABEL *else_label = NEW_LABEL(location.line);
+
+ // We're going to use this to uniquely identify each branch so that we
+ // can track coverage information.
+ rb_code_location_t case_location;
+ VALUE branches = Qfalse;
+ int branch_id = 0;
+
+ if (PM_BRANCH_COVERAGE_P(iseq)) {
+ case_location = pm_code_location(scope_node, (const pm_node_t *) cast);
+ branches = decl_branch_base(iseq, PTR2NUM(cast), &case_location, "case");
+ }
+
+ // If there is only one pattern, then the behavior changes a bit. It
+ // effectively gets treated as a match required node (this is how it is
+ // represented in the other parser).
+ bool in_single_pattern = cast->consequent == NULL && cast->conditions.size == 1;
+
+ // First, we're going to push a bunch of stuff onto the stack that is
+ // going to serve as our scratch space.
+ if (in_single_pattern) {
+ PUSH_INSN(ret, location, putnil); // key error key
+ PUSH_INSN(ret, location, putnil); // key error matchee
+ PUSH_INSN1(ret, location, putobject, Qfalse); // key error?
+ PUSH_INSN(ret, location, putnil); // error string
+ }
+
+ // Now we're going to compile the value to match against.
+ PUSH_INSN(ret, location, putnil); // deconstruct cache
+ PM_COMPILE_NOT_POPPED(cast->predicate);
+
+ // Next, we'll loop through every in clause and compile its body into
+ // the body_seq anchor and its pattern into the cond_seq anchor. We'll
+ // make sure the pattern knows how to jump correctly into the body if it
+ // finds a match.
+ for (size_t index = 0; index < cast->conditions.size; index++) {
+ const pm_node_t *condition = cast->conditions.nodes[index];
+ RUBY_ASSERT(PM_NODE_TYPE_P(condition, PM_IN_NODE));
+
+ const pm_in_node_t *in_node = (const pm_in_node_t *) condition;
+ const pm_line_column_t in_location = PM_NODE_START_LINE_COLUMN(parser, in_node);
+ const pm_line_column_t pattern_location = PM_NODE_START_LINE_COLUMN(parser, in_node->pattern);
+
+ if (branch_id) {
+ PUSH_INSN(body_seq, in_location, putnil);
+ }
+
+ LABEL *body_label = NEW_LABEL(in_location.line);
+ PUSH_LABEL(body_seq, body_label);
+ PUSH_INSN1(body_seq, in_location, adjuststack, INT2FIX(in_single_pattern ? 6 : 2));
+
+ // Establish branch coverage for the in clause.
+ if (PM_BRANCH_COVERAGE_P(iseq)) {
+ rb_code_location_t branch_location = pm_code_location(scope_node, in_node->statements != NULL ? ((const pm_node_t *) in_node->statements) : ((const pm_node_t *) in_node));
+ add_trace_branch_coverage(iseq, body_seq, &branch_location, branch_location.beg_pos.column, branch_id++, "in", branches);
+ }
+
+ if (in_node->statements != NULL) {
+ PM_COMPILE_INTO_ANCHOR(body_seq, (const pm_node_t *) in_node->statements);
+ }
+ else if (!popped) {
+ PUSH_INSN(body_seq, in_location, putnil);
+ }
+
+ PUSH_INSNL(body_seq, in_location, jump, end_label);
+ LABEL *next_pattern_label = NEW_LABEL(pattern_location.line);
+
+ PUSH_INSN(cond_seq, pattern_location, dup);
+ pm_compile_pattern(iseq, scope_node, in_node->pattern, cond_seq, body_label, next_pattern_label, in_single_pattern, false, true, 2);
+ PUSH_LABEL(cond_seq, next_pattern_label);
+ LABEL_UNREMOVABLE(next_pattern_label);
+ }
+
+ if (cast->consequent != NULL) {
+ // If we have an `else` clause, then this becomes our fallback (and
+ // there is no need to compile in code to potentially raise an
+ // error).
+ const pm_else_node_t *else_node = (const pm_else_node_t *) cast->consequent;
+
+ PUSH_LABEL(cond_seq, else_label);
+ PUSH_INSN(cond_seq, location, pop);
+ PUSH_INSN(cond_seq, location, pop);
+
+ // Establish branch coverage for the else clause.
+ if (PM_BRANCH_COVERAGE_P(iseq)) {
+ rb_code_location_t branch_location = pm_code_location(scope_node, else_node->statements != NULL ? ((const pm_node_t *) else_node->statements) : ((const pm_node_t *) else_node));
+ add_trace_branch_coverage(iseq, cond_seq, &branch_location, branch_location.beg_pos.column, branch_id, "else", branches);
+ }
+
+ PM_COMPILE_INTO_ANCHOR(cond_seq, (const pm_node_t *) else_node);
+ PUSH_INSNL(cond_seq, location, jump, end_label);
+ PUSH_INSN(cond_seq, location, putnil);
+ if (popped) PUSH_INSN(cond_seq, location, putnil);
+ }
+ else {
+ // Otherwise, if we do not have an `else` clause, we will compile in
+ // the code to handle raising an appropriate error.
+ PUSH_LABEL(cond_seq, else_label);
+
+ // Establish branch coverage for the implicit else clause.
+ add_trace_branch_coverage(iseq, cond_seq, &case_location, case_location.beg_pos.column, branch_id, "else", branches);
+
+ if (in_single_pattern) {
+ pm_compile_pattern_error_handler(iseq, scope_node, node, cond_seq, end_label, popped);
+ }
+ else {
+ PUSH_INSN1(cond_seq, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ PUSH_INSN1(cond_seq, location, putobject, rb_eNoMatchingPatternError);
+ PUSH_INSN1(cond_seq, location, topn, INT2FIX(2));
+ PUSH_SEND(cond_seq, location, id_core_raise, INT2FIX(2));
+
+ PUSH_INSN1(cond_seq, location, adjuststack, INT2FIX(3));
+ if (!popped) PUSH_INSN(cond_seq, location, putnil);
+ PUSH_INSNL(cond_seq, location, jump, end_label);
+ PUSH_INSN1(cond_seq, location, dupn, INT2FIX(1));
+ if (popped) PUSH_INSN(cond_seq, location, putnil);
+ }
+ }
+
+ // At the end of all of this compilation, we will add the code for the
+ // conditions first, then the various bodies, then mark the end of the
+ // entire sequence with the end label.
+ PUSH_SEQ(ret, cond_seq);
+ PUSH_SEQ(ret, body_seq);
+ PUSH_LABEL(ret, end_label);
+
+ return;
+ }
+ case PM_CLASS_NODE: {
+ // class Foo; end
+ // ^^^^^^^^^^^^^^
+ const pm_class_node_t *cast = (const pm_class_node_t *) node;
+
+ ID class_id = pm_constant_id_lookup(scope_node, cast->name);
+ VALUE class_name = rb_str_freeze(rb_sprintf("<class:%"PRIsVALUE">", rb_id2str(class_id)));
+
+ pm_scope_node_t next_scope_node;
+ pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
+
+ const rb_iseq_t *class_iseq = NEW_CHILD_ISEQ(&next_scope_node, class_name, ISEQ_TYPE_CLASS, location.line);
+ pm_scope_node_destroy(&next_scope_node);
+
+ // TODO: Once we merge constant path nodes correctly, fix this flag
+ const int flags = VM_DEFINECLASS_TYPE_CLASS |
+ (cast->superclass ? VM_DEFINECLASS_FLAG_HAS_SUPERCLASS : 0) |
+ pm_compile_class_path(iseq, cast->constant_path, &location, ret, false, scope_node);
+
+ if (cast->superclass) {
+ PM_COMPILE_NOT_POPPED(cast->superclass);
+ }
+ else {
+ PUSH_INSN(ret, location, putnil);
+ }
+
+ PUSH_INSN3(ret, location, defineclass, ID2SYM(class_id), class_iseq, INT2FIX(flags));
+ RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)class_iseq);
+
+ if (popped) PUSH_INSN(ret, location, pop);
+ return;
+ }
+ case PM_CLASS_VARIABLE_AND_WRITE_NODE: {
+ // @@foo &&= bar
+ // ^^^^^^^^^^^^^
+ const pm_class_variable_and_write_node_t *cast = (const pm_class_variable_and_write_node_t *) node;
+ LABEL *end_label = NEW_LABEL(location.line);
+
+ ID name_id = pm_constant_id_lookup(scope_node, cast->name);
+ VALUE name = ID2SYM(name_id);
+
+ PUSH_INSN2(ret, location, getclassvariable, name, get_cvar_ic_value(iseq, name_id));
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ PUSH_INSNL(ret, location, branchunless, end_label);
+ if (!popped) PUSH_INSN(ret, location, pop);
+
+ PM_COMPILE_NOT_POPPED(cast->value);
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ PUSH_INSN2(ret, location, setclassvariable, name, get_cvar_ic_value(iseq, name_id));
+ PUSH_LABEL(ret, end_label);
+
+ return;
+ }
+ case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: {
+ // @@foo += bar
+ // ^^^^^^^^^^^^
+ const pm_class_variable_operator_write_node_t *cast = (const pm_class_variable_operator_write_node_t *) node;
+
+ ID name_id = pm_constant_id_lookup(scope_node, cast->name);
+ VALUE name = ID2SYM(name_id);
+
+ PUSH_INSN2(ret, location, getclassvariable, name, get_cvar_ic_value(iseq, name_id));
+ PM_COMPILE_NOT_POPPED(cast->value);
+
+ ID method_id = pm_constant_id_lookup(scope_node, cast->operator);
+ int flags = VM_CALL_ARGS_SIMPLE;
+ PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(flags));
+
+ if (!popped) PUSH_INSN(ret, location, dup);
+ PUSH_INSN2(ret, location, setclassvariable, name, get_cvar_ic_value(iseq, name_id));
+
+ return;
+ }
+ case PM_CLASS_VARIABLE_OR_WRITE_NODE: {
+ // @@foo ||= bar
+ // ^^^^^^^^^^^^^
+ const pm_class_variable_or_write_node_t *cast = (const pm_class_variable_or_write_node_t *) node;
+ LABEL *end_label = NEW_LABEL(location.line);
+ LABEL *start_label = NEW_LABEL(location.line);
+
+ ID name_id = pm_constant_id_lookup(scope_node, cast->name);
+ VALUE name = ID2SYM(name_id);
+
+ PUSH_INSN(ret, location, putnil);
+ PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CVAR), name, Qtrue);
+ PUSH_INSNL(ret, location, branchunless, start_label);
+
+ PUSH_INSN2(ret, location, getclassvariable, name, get_cvar_ic_value(iseq, name_id));
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ PUSH_INSNL(ret, location, branchif, end_label);
+ if (!popped) PUSH_INSN(ret, location, pop);
+
+ PUSH_LABEL(ret, start_label);
+ PM_COMPILE_NOT_POPPED(cast->value);
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ PUSH_INSN2(ret, location, setclassvariable, name, get_cvar_ic_value(iseq, name_id));
+ PUSH_LABEL(ret, end_label);
+
+ return;
+ }
+ case PM_CLASS_VARIABLE_READ_NODE: {
+ // @@foo
+ // ^^^^^
+ if (!popped) {
+ const pm_class_variable_read_node_t *cast = (const pm_class_variable_read_node_t *) node;
+ ID name = pm_constant_id_lookup(scope_node, cast->name);
+ PUSH_INSN2(ret, location, getclassvariable, ID2SYM(name), get_cvar_ic_value(iseq, name));
+ }
+ return;
+ }
+ case PM_CLASS_VARIABLE_WRITE_NODE: {
+ // @@foo = 1
+ // ^^^^^^^^^
+ const pm_class_variable_write_node_t *cast = (const pm_class_variable_write_node_t *) node;
+ PM_COMPILE_NOT_POPPED(cast->value);
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ ID name = pm_constant_id_lookup(scope_node, cast->name);
+ PUSH_INSN2(ret, location, setclassvariable, ID2SYM(name), get_cvar_ic_value(iseq, name));
+
+ return;
+ }
+ case PM_CONSTANT_PATH_NODE: {
+ // Foo::Bar
+ // ^^^^^^^^
+ VALUE parts;
+
+ if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache && ((parts = pm_constant_path_parts(node, scope_node)) != Qnil)) {
+ ISEQ_BODY(iseq)->ic_size++;
+ PUSH_INSN1(ret, location, opt_getconstant_path, parts);
+ }
+ else {
+ DECL_ANCHOR(prefix);
+ INIT_ANCHOR(prefix);
+
+ DECL_ANCHOR(body);
+ INIT_ANCHOR(body);
+
+ pm_compile_constant_path(iseq, node, prefix, body, popped, scope_node);
+ if (LIST_INSN_SIZE_ZERO(prefix)) {
+ PUSH_INSN(ret, location, putnil);
+ }
+ else {
+ PUSH_SEQ(ret, prefix);
+ }
+
+ PUSH_SEQ(ret, body);
+ }
+
+ if (popped) PUSH_INSN(ret, location, pop);
+ return;
+ }
+ case PM_CONSTANT_PATH_AND_WRITE_NODE: {
+ // Foo::Bar &&= baz
+ // ^^^^^^^^^^^^^^^^
+ const pm_constant_path_and_write_node_t *cast = (const pm_constant_path_and_write_node_t *) node;
+ const pm_constant_path_node_t *target = cast->target;
+
+ const pm_constant_read_node_t *child = (const pm_constant_read_node_t *) target->child;
+ VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, child->name));
+ LABEL *lfin = NEW_LABEL(location.line);
+
+ if (target->parent) {
+ PM_COMPILE_NOT_POPPED(target->parent);
+ }
+ else {
+ PUSH_INSN1(ret, location, putobject, rb_cObject);
+ }
+
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSN1(ret, location, putobject, Qtrue);
+ PUSH_INSN1(ret, location, getconstant, name);
+
+ if (!popped) PUSH_INSN(ret, location, dup);
+ PUSH_INSNL(ret, location, branchunless, lfin);
+
+ if (!popped) PUSH_INSN(ret, location, pop);
+ PM_COMPILE_NOT_POPPED(cast->value);
+
+ if (popped) {
+ PUSH_INSN1(ret, location, topn, INT2FIX(1));
+ }
+ else {
+ PUSH_INSN1(ret, location, dupn, INT2FIX(2));
+ PUSH_INSN(ret, location, swap);
+ }
+
+ PUSH_INSN1(ret, location, setconstant, name);
+ PUSH_LABEL(ret, lfin);
+
+ if (!popped) PUSH_INSN(ret, location, swap);
+ PUSH_INSN(ret, location, pop);
+
+ return;
+ }
+ case PM_CONSTANT_PATH_OR_WRITE_NODE: {
+ // Foo::Bar ||= baz
+ // ^^^^^^^^^^^^^^^^
+ const pm_constant_path_or_write_node_t *cast = (const pm_constant_path_or_write_node_t *) node;
+ const pm_constant_path_node_t *target = cast->target;
+
+ const pm_constant_read_node_t *child = (const pm_constant_read_node_t *) target->child;
+ VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, child->name));
+
+ LABEL *lassign = NEW_LABEL(location.line);
+ LABEL *lfin = NEW_LABEL(location.line);
+
+ if (target->parent) {
+ PM_COMPILE_NOT_POPPED(target->parent);
+ }
+ else {
+ PUSH_INSN1(ret, location, putobject, rb_cObject);
+ }
+
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST_FROM), name, Qtrue);
+ PUSH_INSNL(ret, location, branchunless, lassign);
+
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSN1(ret, location, putobject, Qtrue);
+ PUSH_INSN1(ret, location, getconstant, name);
+
+ if (!popped) PUSH_INSN(ret, location, dup);
+ PUSH_INSNL(ret, location, branchif, lfin);
+
+ if (!popped) PUSH_INSN(ret, location, pop);
+ PUSH_LABEL(ret, lassign);
+ PM_COMPILE_NOT_POPPED(cast->value);
+
+ if (popped) {
+ PUSH_INSN1(ret, location, topn, INT2FIX(1));
+ }
+ else {
+ PUSH_INSN1(ret, location, dupn, INT2FIX(2));
+ PUSH_INSN(ret, location, swap);
+ }
+
+ PUSH_INSN1(ret, location, setconstant, name);
+ PUSH_LABEL(ret, lfin);
+
+ if (!popped) PUSH_INSN(ret, location, swap);
+ PUSH_INSN(ret, location, pop);
+
+ return;
+ }
+ case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: {
+ // Foo::Bar += baz
+ // ^^^^^^^^^^^^^^^
+ const pm_constant_path_operator_write_node_t *cast = (const pm_constant_path_operator_write_node_t *) node;
+ const pm_constant_path_node_t *target = cast->target;
+ ID method_id = pm_constant_id_lookup(scope_node, cast->operator);
+
+ const pm_constant_read_node_t *child = (const pm_constant_read_node_t *) target->child;
+ VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, child->name));
+
+ if (target->parent) {
+ PM_COMPILE_NOT_POPPED(target->parent);
+ }
+ else {
+ PUSH_INSN1(ret, location, putobject, rb_cObject);
+ }
+
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSN1(ret, location, putobject, Qtrue);
+ PUSH_INSN1(ret, location, getconstant, name);
+
+ PM_COMPILE_NOT_POPPED(cast->value);
+ PUSH_CALL(ret, location, method_id, INT2FIX(1));
+ PUSH_INSN(ret, location, swap);
+
+ if (!popped) {
+ PUSH_INSN1(ret, location, topn, INT2FIX(1));
+ PUSH_INSN(ret, location, swap);
+ }
+
+ PUSH_INSN1(ret, location, setconstant, name);
+ return;
+ }
+ case PM_CONSTANT_PATH_WRITE_NODE: {
+ // Foo::Bar = 1
+ // ^^^^^^^^^^^^
+ const pm_constant_path_write_node_t *cast = (const pm_constant_path_write_node_t *) node;
+ const pm_constant_path_node_t *target = cast->target;
+
+ const pm_constant_read_node_t *child = (const pm_constant_read_node_t *) target->child;
+ VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, child->name));
+
+ if (target->parent) {
+ PM_COMPILE_NOT_POPPED((const pm_node_t *) target->parent);
+ }
+ else {
+ PUSH_INSN1(ret, location, putobject, rb_cObject);
+ }
+
+ PM_COMPILE_NOT_POPPED(cast->value);
+
+ if (!popped) {
+ PUSH_INSN(ret, location, swap);
+ PUSH_INSN1(ret, location, topn, INT2FIX(1));
+ }
+
+ PUSH_INSN(ret, location, swap);
+ PUSH_INSN1(ret, location, setconstant, name);
+
+ return;
+ }
+ case PM_CONSTANT_READ_NODE: {
+ // Foo
+ // ^^^
+ const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node;
+ VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
+
+ pm_compile_constant_read(iseq, name, &cast->base.location, ret, scope_node);
+ if (popped) PUSH_INSN(ret, location, pop);
+
+ return;
+ }
+ case PM_CONSTANT_AND_WRITE_NODE: {
+ // Foo &&= bar
+ // ^^^^^^^^^^^
+ const pm_constant_and_write_node_t *cast = (const pm_constant_and_write_node_t *) node;
+ VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
+ LABEL *end_label = NEW_LABEL(location.line);
+
+ pm_compile_constant_read(iseq, name, &cast->name_loc, ret, scope_node);
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ PUSH_INSNL(ret, location, branchunless, end_label);
+ if (!popped) PUSH_INSN(ret, location, pop);
+
+ PM_COMPILE_NOT_POPPED(cast->value);
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
+ PUSH_INSN1(ret, location, setconstant, name);
+ PUSH_LABEL(ret, end_label);
+
+ return;
+ }
+ case PM_CONSTANT_OR_WRITE_NODE: {
+ // Foo ||= bar
+ // ^^^^^^^^^^^
+ const pm_constant_or_write_node_t *cast = (const pm_constant_or_write_node_t *) node;
+ VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
+ LABEL *set_label = NEW_LABEL(location.line);
+ LABEL *end_label = NEW_LABEL(location.line);
+
+ PUSH_INSN(ret, location, putnil);
+ PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST), name, Qtrue);
+ PUSH_INSNL(ret, location, branchunless, set_label);
+
+ pm_compile_constant_read(iseq, name, &cast->name_loc, ret, scope_node);
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ PUSH_INSNL(ret, location, branchif, end_label);
+ if (!popped) PUSH_INSN(ret, location, pop);
+
+ PUSH_LABEL(ret, set_label);
+ PM_COMPILE_NOT_POPPED(cast->value);
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
+ PUSH_INSN1(ret, location, setconstant, name);
+ PUSH_LABEL(ret, end_label);
+
+ return;
+ }
+ case PM_CONSTANT_OPERATOR_WRITE_NODE: {
+ // Foo += bar
+ // ^^^^^^^^^^
+ const pm_constant_operator_write_node_t *cast = (const pm_constant_operator_write_node_t *) node;
+ VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
+ ID method_id = pm_constant_id_lookup(scope_node, cast->operator);
+
+ pm_compile_constant_read(iseq, name, &cast->name_loc, ret, scope_node);
+ PM_COMPILE_NOT_POPPED(cast->value);
+
+ PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
+ PUSH_INSN1(ret, location, setconstant, name);
+
+ return;
+ }
+ case PM_CONSTANT_WRITE_NODE: {
+ // Foo = 1
+ // ^^^^^^^
+ const pm_constant_write_node_t *cast = (const pm_constant_write_node_t *) node;
+ VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
+
+ PM_COMPILE_NOT_POPPED(cast->value);
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
+ PUSH_INSN1(ret, location, setconstant, name);
+
+ return;
+ }
+ case PM_DEF_NODE: {
+ // def foo; end
+ // ^^^^^^^^^^^^
+ //
+ // def self.foo; end
+ // ^^^^^^^^^^^^^^^^^
+ const pm_def_node_t *cast = (const pm_def_node_t *) node;
+ ID method_name = pm_constant_id_lookup(scope_node, cast->name);
+
+ pm_scope_node_t next_scope_node;
+ pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
+
+ rb_iseq_t *method_iseq = NEW_ISEQ(&next_scope_node, rb_id2str(method_name), ISEQ_TYPE_METHOD, location.line);
+ pm_scope_node_destroy(&next_scope_node);
+
+ if (cast->receiver) {
+ PM_COMPILE_NOT_POPPED(cast->receiver);
+ PUSH_INSN2(ret, location, definesmethod, ID2SYM(method_name), method_iseq);
+ }
+ else {
+ PUSH_INSN2(ret, location, definemethod, ID2SYM(method_name), method_iseq);
+ }
+ RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) method_iseq);
+
+ if (!popped) {
+ PUSH_INSN1(ret, location, putobject, ID2SYM(method_name));
+ }
+
+ return;
+ }
+ case PM_DEFINED_NODE: {
+ // defined?(a)
+ // ^^^^^^^^^^^
+ const pm_defined_node_t *cast = (const pm_defined_node_t *) node;
+ pm_compile_defined_expr(iseq, cast->value, &location, ret, popped, scope_node, false);
+ return;
+ }
+ case PM_EMBEDDED_STATEMENTS_NODE: {
+ // "foo #{bar}"
+ // ^^^^^^
+ const pm_embedded_statements_node_t *cast = (const pm_embedded_statements_node_t *) node;
+
+ if (cast->statements != NULL) {
+ PM_COMPILE((const pm_node_t *) (cast->statements));
+ }
+ else {
+ PUSH_INSN(ret, location, putnil);
+ }
+
+ if (popped) PUSH_INSN(ret, location, pop);
+ return;
+ }
+ case PM_EMBEDDED_VARIABLE_NODE: {
+ // "foo #@bar"
+ // ^^^^^
+ const pm_embedded_variable_node_t *cast = (const pm_embedded_variable_node_t *) node;
+ PM_COMPILE(cast->variable);
+ return;
+ }
+ case PM_FALSE_NODE: {
+ // false
+ // ^^^^^
+ if (!popped) {
+ PUSH_INSN1(ret, location, putobject, Qfalse);
+ }
+ return;
+ }
+ case PM_ENSURE_NODE: {
+ const pm_ensure_node_t *cast = (const pm_ensure_node_t *) node;
+
+ if (cast->statements != NULL) {
+ LABEL *start = NEW_LABEL(location.line);
+ LABEL *end = NEW_LABEL(location.line);
+ PUSH_LABEL(ret, start);
+
+ LABEL *prev_end_label = ISEQ_COMPILE_DATA(iseq)->end_label;
+ ISEQ_COMPILE_DATA(iseq)->end_label = end;
+
+ PM_COMPILE((const pm_node_t *) cast->statements);
+ ISEQ_COMPILE_DATA(iseq)->end_label = prev_end_label;
+ PUSH_LABEL(ret, end);
+ }
+
+ return;
+ }
+ case PM_ELSE_NODE: {
+ // if foo then bar else baz end
+ // ^^^^^^^^^^^^
+ const pm_else_node_t *cast = (const pm_else_node_t *) node;
+
+ if (cast->statements != NULL) {
+ PM_COMPILE((const pm_node_t *) cast->statements);
+ }
+ else if (!popped) {
+ PUSH_SYNTHETIC_PUTNIL(ret, iseq);
+ }
+
+ return;
+ }
+ case PM_FLIP_FLOP_NODE: {
+ // if foo .. bar; end
+ // ^^^^^^^^^^
+ const pm_flip_flop_node_t *cast = (const pm_flip_flop_node_t *) node;
+
+ LABEL *final_label = NEW_LABEL(location.line);
+ LABEL *then_label = NEW_LABEL(location.line);
+ LABEL *else_label = NEW_LABEL(location.line);
+
+ pm_compile_flip_flop(cast, else_label, then_label, iseq, location.line, ret, popped, scope_node);
+
+ PUSH_LABEL(ret, then_label);
+ PUSH_INSN1(ret, location, putobject, Qtrue);
+ PUSH_INSNL(ret, location, jump, final_label);
+ PUSH_LABEL(ret, else_label);
+ PUSH_INSN1(ret, location, putobject, Qfalse);
+ PUSH_LABEL(ret, final_label);
+
+ return;
+ }
+ case PM_FLOAT_NODE: {
+ // 1.0
+ // ^^^
+ if (!popped) {
+ PUSH_INSN1(ret, location, putobject, parse_float((const pm_float_node_t *) node));
+ }
+ return;
+ }
+ case PM_FOR_NODE: {
+ // for foo in bar do end
+ // ^^^^^^^^^^^^^^^^^^^^^
+ const pm_for_node_t *cast = (const pm_for_node_t *) node;
+
+ LABEL *retry_label = NEW_LABEL(location.line);
+ LABEL *retry_end_l = NEW_LABEL(location.line);
+
+ // First, compile the collection that we're going to be iterating over.
+ PUSH_LABEL(ret, retry_label);
+ PM_COMPILE_NOT_POPPED(cast->collection);
+
+ // Next, create the new scope that is going to contain the block that
+ // will be passed to the each method.
+ pm_scope_node_t next_scope_node;
+ pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
+
+ const rb_iseq_t *child_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, location.line);
+ pm_scope_node_destroy(&next_scope_node);
+
+ const rb_iseq_t *prev_block = ISEQ_COMPILE_DATA(iseq)->current_block;
+ ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq;
+
+ // Now, create the method call to each that will be used to iterate over
+ // the collection, and pass the newly created iseq as the block.
+ PUSH_SEND_WITH_BLOCK(ret, location, idEach, INT2FIX(0), child_iseq);
+ pm_compile_retry_end_label(iseq, ret, retry_end_l);
+
+ if (popped) PUSH_INSN(ret, location, pop);
+ ISEQ_COMPILE_DATA(iseq)->current_block = prev_block;
+ PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, child_iseq, retry_end_l);
+ return;
+ }
+ case PM_FORWARDING_ARGUMENTS_NODE: {
+ rb_bug("Cannot compile a ForwardingArgumentsNode directly\n");
+ return;
+ }
+ case PM_FORWARDING_SUPER_NODE: {
+ // super
+ // ^^^^^
+ //
+ // super {}
+ // ^^^^^^^^
+ const pm_forwarding_super_node_t *cast = (const pm_forwarding_super_node_t *) node;
+ const rb_iseq_t *block = NULL;
+
+ const rb_iseq_t *previous_block = NULL;
+ LABEL *retry_label = NULL;
+ LABEL *retry_end_l = NULL;
+
+ if (cast->block != NULL) {
+ previous_block = ISEQ_COMPILE_DATA(iseq)->current_block;
+ ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
+
+ retry_label = NEW_LABEL(location.line);
+ retry_end_l = NEW_LABEL(location.line);
+
+ PUSH_LABEL(ret, retry_label);
+ }
+
+ PUSH_INSN(ret, location, putself);
+ int flag = VM_CALL_ZSUPER | VM_CALL_SUPER | VM_CALL_FCALL;
+
+ if (cast->block != NULL) {
+ pm_scope_node_t next_scope_node;
+ pm_scope_node_init((const pm_node_t *) cast->block, &next_scope_node, scope_node);
+
+ ISEQ_COMPILE_DATA(iseq)->current_block = block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, location.line);
+ pm_scope_node_destroy(&next_scope_node);
+ RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) block);
+ }
+
+ DECL_ANCHOR(args);
+ INIT_ANCHOR(args);
+
+ struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
+ const rb_iseq_t *local_iseq = body->local_iseq;
+ const struct rb_iseq_constant_body *const local_body = ISEQ_BODY(local_iseq);
+
+ int argc = 0;
+ int depth = get_lvar_level(iseq);
+
+ if (local_body->param.flags.has_lead) {
+ /* required arguments */
+ for (int i = 0; i < local_body->param.lead_num; i++) {
+ int idx = local_body->local_table_size - i;
+ PUSH_GETLOCAL(args, location, idx, depth);
+ }
+ argc += local_body->param.lead_num;
+ }
+
+ if (local_body->param.flags.has_opt) {
+ /* optional arguments */
+ for (int j = 0; j < local_body->param.opt_num; j++) {
+ int idx = local_body->local_table_size - (argc + j);
+ PUSH_GETLOCAL(args, location, idx, depth);
+ }
+ argc += local_body->param.opt_num;
+ }
+
+ if (local_body->param.flags.has_rest) {
+ /* rest argument */
+ int idx = local_body->local_table_size - local_body->param.rest_start;
+ PUSH_GETLOCAL(args, location, idx, depth);
+ PUSH_INSN1(args, location, splatarray, Qfalse);
+
+ argc = local_body->param.rest_start + 1;
+ flag |= VM_CALL_ARGS_SPLAT;
+ }
+
+ if (local_body->param.flags.has_post) {
+ /* post arguments */
+ int post_len = local_body->param.post_num;
+ int post_start = local_body->param.post_start;
+
+ int j = 0;
+ for (; j < post_len; j++) {
+ int idx = local_body->local_table_size - (post_start + j);
+ PUSH_GETLOCAL(args, location, idx, depth);
+ }
+
+ if (local_body->param.flags.has_rest) {
+ // argc remains unchanged from rest branch
+ PUSH_INSN1(args, location, newarray, INT2FIX(j));
+ PUSH_INSN(args, location, concatarray);
+ }
+ else {
+ argc = post_len + post_start;
+ }
+ }
+
+ const struct rb_iseq_param_keyword *const local_keyword = local_body->param.keyword;
+ if (local_body->param.flags.has_kw) {
+ int local_size = local_body->local_table_size;
+ argc++;
+
+ PUSH_INSN1(args, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+
+ if (local_body->param.flags.has_kwrest) {
+ int idx = local_body->local_table_size - local_keyword->rest_start;
+ PUSH_GETLOCAL(args, location, idx, depth);
+ RUBY_ASSERT(local_keyword->num > 0);
+ PUSH_SEND(args, location, rb_intern("dup"), INT2FIX(0));
+ }
+ else {
+ PUSH_INSN1(args, location, newhash, INT2FIX(0));
+ }
+ int i = 0;
+ for (; i < local_keyword->num; ++i) {
+ ID id = local_keyword->table[i];
+ int idx = local_size - get_local_var_idx(local_iseq, id);
+ PUSH_INSN1(args, location, putobject, ID2SYM(id));
+ PUSH_GETLOCAL(args, location, idx, depth);
+ }
+
+ PUSH_SEND(args, location, id_core_hash_merge_ptr, INT2FIX(i * 2 + 1));
+ flag |= VM_CALL_KW_SPLAT| VM_CALL_KW_SPLAT_MUT;
+ }
+ else if (local_body->param.flags.has_kwrest) {
+ int idx = local_body->local_table_size - local_keyword->rest_start;
+ PUSH_GETLOCAL(args, location, idx, depth);
+ argc++;
+ flag |= VM_CALL_KW_SPLAT;
+ }
+
+ PUSH_SEQ(ret, args);
+ PUSH_INSN2(ret, location, invokesuper, new_callinfo(iseq, 0, argc, flag, NULL, block != NULL), block);
+
+ if (cast->block != NULL) {
+ pm_compile_retry_end_label(iseq, ret, retry_end_l);
+ PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, block, retry_end_l);
+ ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
+ }
+
+ if (popped) PUSH_INSN(ret, location, pop);
+ return;
+ }
+ case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: {
+ // $foo &&= bar
+ // ^^^^^^^^^^^^
+ const pm_global_variable_and_write_node_t *cast = (const pm_global_variable_and_write_node_t *) node;
+ LABEL *end_label = NEW_LABEL(location.line);
+
+ VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
+ PUSH_INSN1(ret, location, getglobal, name);
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ PUSH_INSNL(ret, location, branchunless, end_label);
+ if (!popped) PUSH_INSN(ret, location, pop);
+
+ PM_COMPILE_NOT_POPPED(cast->value);
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ PUSH_INSN1(ret, location, setglobal, name);
+ PUSH_LABEL(ret, end_label);
+
+ return;
+ }
+ case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: {
+ // $foo += bar
+ // ^^^^^^^^^^^
+ const pm_global_variable_operator_write_node_t *cast = (const pm_global_variable_operator_write_node_t *) node;
+
+ VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
+ PUSH_INSN1(ret, location, getglobal, name);
+ PM_COMPILE_NOT_POPPED(cast->value);
+
+ ID method_id = pm_constant_id_lookup(scope_node, cast->operator);
+ int flags = VM_CALL_ARGS_SIMPLE;
+ PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(flags));
+
+ if (!popped) PUSH_INSN(ret, location, dup);
+ PUSH_INSN1(ret, location, setglobal, name);
+
+ return;
+ }
+ case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: {
+ // $foo ||= bar
+ // ^^^^^^^^^^^^
+ const pm_global_variable_or_write_node_t *cast = (const pm_global_variable_or_write_node_t *) node;
+ LABEL *set_label = NEW_LABEL(location.line);
+ LABEL *end_label = NEW_LABEL(location.line);
+
+ PUSH_INSN(ret, location, putnil);
+ VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
+
+ PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_GVAR), name, Qtrue);
+ PUSH_INSNL(ret, location, branchunless, set_label);
+
+ PUSH_INSN1(ret, location, getglobal, name);
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ PUSH_INSNL(ret, location, branchif, end_label);
+ if (!popped) PUSH_INSN(ret, location, pop);
+
+ PUSH_LABEL(ret, set_label);
+ PM_COMPILE_NOT_POPPED(cast->value);
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ PUSH_INSN1(ret, location, setglobal, name);
+ PUSH_LABEL(ret, end_label);
+
+ return;
+ }
+ case PM_GLOBAL_VARIABLE_READ_NODE: {
+ // $foo
+ // ^^^^
+ const pm_global_variable_read_node_t *cast = (const pm_global_variable_read_node_t *) node;
+ VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
+
+ PUSH_INSN1(ret, location, getglobal, name);
+ if (popped) PUSH_INSN(ret, location, pop);
+
+ return;
+ }
+ case PM_GLOBAL_VARIABLE_WRITE_NODE: {
+ // $foo = 1
+ // ^^^^^^^^
+ const pm_global_variable_write_node_t *cast = (const pm_global_variable_write_node_t *) node;
+ PM_COMPILE_NOT_POPPED(cast->value);
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ ID name = pm_constant_id_lookup(scope_node, cast->name);
+ PUSH_INSN1(ret, location, setglobal, ID2SYM(name));
+
+ return;
+ }
+ case PM_HASH_NODE: {
+ // {}
+ // ^^
+ //
+ // If every node in the hash is static, then we can compile the entire
+ // hash now instead of later.
+ if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
+ // We're only going to compile this node if it's not popped. If it
+ // is popped, then we know we don't need to do anything since it's
+ // statically known.
+ if (!popped) {
+ VALUE value = pm_static_literal_value(iseq, node, scope_node);
+ PUSH_INSN1(ret, location, duphash, value);
+ RB_OBJ_WRITTEN(iseq, Qundef, value);
+ }
+ }
+ else {
+ // Here since we know there are possible side-effects inside the
+ // hash contents, we're going to build it entirely at runtime. We'll
+ // do this by pushing all of the key-value pairs onto the stack and
+ // then combining them with newhash.
+ //
+ // If this hash is popped, then this serves only to ensure we enact
+ // all side-effects (like method calls) that are contained within
+ // the hash contents.
+ const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
+ const pm_node_list_t *elements = &cast->elements;
+
+ if (popped) {
+ // If this hash is popped, then we can iterate through each
+ // element and compile it. The result of each compilation will
+ // only include the side effects of the element itself.
+ for (size_t index = 0; index < elements->size; index++) {
+ PM_COMPILE_POPPED(elements->nodes[index]);
+ }
+ }
+ else {
+ pm_compile_hash_elements(iseq, node, elements, ret, scope_node);
+ }
+ }
+
+ return;
+ }
+ case PM_IF_NODE: {
+ // if foo then bar end
+ // ^^^^^^^^^^^^^^^^^^^
+ //
+ // bar if foo
+ // ^^^^^^^^^^
+ //
+ // foo ? bar : baz
+ // ^^^^^^^^^^^^^^^
+ const pm_if_node_t *cast = (const pm_if_node_t *) node;
+ pm_compile_conditional(iseq, &location, PM_IF_NODE, (const pm_node_t *) cast, cast->statements, cast->consequent, cast->predicate, ret, popped, scope_node);
+ return;
+ }
+ case PM_IMAGINARY_NODE: {
+ // 1i
+ // ^^
+ if (!popped) {
+ PUSH_INSN1(ret, location, putobject, parse_imaginary((const pm_imaginary_node_t *) node));
+ }
+ return;
+ }
+ case PM_IMPLICIT_NODE: {
+ // Implicit nodes mark places in the syntax tree where explicit syntax
+ // was omitted, but implied. For example,
+ //
+ // { foo: }
+ //
+ // In this case a method call/local variable read is implied by virtue
+ // of the missing value. To compile these nodes, we simply compile the
+ // value that is implied, which is helpfully supplied by the parser.
+ const pm_implicit_node_t *cast = (const pm_implicit_node_t *) node;
+ PM_COMPILE(cast->value);
+ return;
+ }
+ case PM_IN_NODE: {
+ // In nodes are handled by the case match node directly, so we should
+ // never end up hitting them through this path.
+ rb_bug("Should not ever enter an in node directly");
+ return;
+ }
+ case PM_INDEX_OPERATOR_WRITE_NODE: {
+ // foo[bar] += baz
+ // ^^^^^^^^^^^^^^^
+ const pm_index_operator_write_node_t *cast = (const pm_index_operator_write_node_t *) node;
+ pm_compile_index_operator_write_node(iseq, cast, &location, ret, popped, scope_node);
+ return;
+ }
+ case PM_INDEX_AND_WRITE_NODE: {
+ // foo[bar] &&= baz
+ // ^^^^^^^^^^^^^^^^
+ const pm_index_and_write_node_t *cast = (const pm_index_and_write_node_t *) node;
+ pm_compile_index_control_flow_write_node(iseq, node, cast->receiver, cast->arguments, cast->block, cast->value, &location, ret, popped, scope_node);
+ return;
+ }
+ case PM_INDEX_OR_WRITE_NODE: {
+ // foo[bar] ||= baz
+ // ^^^^^^^^^^^^^^^^
+ const pm_index_or_write_node_t *cast = (const pm_index_or_write_node_t *) node;
+ pm_compile_index_control_flow_write_node(iseq, node, cast->receiver, cast->arguments, cast->block, cast->value, &location, ret, popped, scope_node);
+ return;
+ }
+ case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: {
+ // @foo &&= bar
+ // ^^^^^^^^^^^^
+ const pm_instance_variable_and_write_node_t *cast = (const pm_instance_variable_and_write_node_t *) node;
+ LABEL *end_label = NEW_LABEL(location.line);
+
+ ID name_id = pm_constant_id_lookup(scope_node, cast->name);
+ VALUE name = ID2SYM(name_id);
+
+ PUSH_INSN2(ret, location, getinstancevariable, name, get_ivar_ic_value(iseq, name_id));
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ PUSH_INSNL(ret, location, branchunless, end_label);
+ if (!popped) PUSH_INSN(ret, location, pop);
+
+ PM_COMPILE_NOT_POPPED(cast->value);
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ PUSH_INSN2(ret, location, setinstancevariable, name, get_ivar_ic_value(iseq, name_id));
+ PUSH_LABEL(ret, end_label);
+
+ return;
+ }
+ case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: {
+ // @foo += bar
+ // ^^^^^^^^^^^
+ const pm_instance_variable_operator_write_node_t *cast = (const pm_instance_variable_operator_write_node_t *) node;
+
+ ID name_id = pm_constant_id_lookup(scope_node, cast->name);
+ VALUE name = ID2SYM(name_id);
+
+ PUSH_INSN2(ret, location, getinstancevariable, name, get_ivar_ic_value(iseq, name_id));
+ PM_COMPILE_NOT_POPPED(cast->value);
+
+ ID method_id = pm_constant_id_lookup(scope_node, cast->operator);
+ int flags = VM_CALL_ARGS_SIMPLE;
+ PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(flags));
+
+ if (!popped) PUSH_INSN(ret, location, dup);
+ PUSH_INSN2(ret, location, setinstancevariable, name, get_ivar_ic_value(iseq, name_id));
+
+ return;
+ }
+ case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: {
+ // @foo ||= bar
+ // ^^^^^^^^^^^^
+ const pm_instance_variable_or_write_node_t *cast = (const pm_instance_variable_or_write_node_t *) node;
+ LABEL *end_label = NEW_LABEL(location.line);
+
+ ID name_id = pm_constant_id_lookup(scope_node, cast->name);
+ VALUE name = ID2SYM(name_id);
+
+ PUSH_INSN2(ret, location, getinstancevariable, name, get_ivar_ic_value(iseq, name_id));
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ PUSH_INSNL(ret, location, branchif, end_label);
+ if (!popped) PUSH_INSN(ret, location, pop);
+
+ PM_COMPILE_NOT_POPPED(cast->value);
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ PUSH_INSN2(ret, location, setinstancevariable, name, get_ivar_ic_value(iseq, name_id));
+ PUSH_LABEL(ret, end_label);
+
+ return;
+ }
+ case PM_INSTANCE_VARIABLE_READ_NODE: {
+ // @foo
+ // ^^^^
+ if (!popped) {
+ const pm_instance_variable_read_node_t *cast = (const pm_instance_variable_read_node_t *) node;
+ ID name = pm_constant_id_lookup(scope_node, cast->name);
+ PUSH_INSN2(ret, location, getinstancevariable, ID2SYM(name), get_ivar_ic_value(iseq, name));
+ }
+ return;
+ }
+ case PM_INSTANCE_VARIABLE_WRITE_NODE: {
+ // @foo = 1
+ // ^^^^^^^^
+ const pm_instance_variable_write_node_t *cast = (const pm_instance_variable_write_node_t *) node;
+ PM_COMPILE_NOT_POPPED(cast->value);
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ ID name = pm_constant_id_lookup(scope_node, cast->name);
+ PUSH_INSN2(ret, location, setinstancevariable, ID2SYM(name), get_ivar_ic_value(iseq, name));
+
+ return;
+ }
+ case PM_INTEGER_NODE: {
+ // 1
+ // ^
+ if (!popped) {
+ PUSH_INSN1(ret, location, putobject, parse_integer((const pm_integer_node_t *) node));
+ }
+ return;
+ }
+ case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: {
+ // if /foo #{bar}/ then end
+ // ^^^^^^^^^^^^
+ if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
+ if (!popped) {
+ VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
+ PUSH_INSN1(ret, location, putobject, regexp);
+ }
+ }
+ else {
+ pm_compile_regexp_dynamic(iseq, node, &((const pm_interpolated_match_last_line_node_t *) node)->parts, &location, ret, popped, scope_node);
+ }
+
+ PUSH_INSN1(ret, location, getglobal, rb_id2sym(idLASTLINE));
+ PUSH_SEND(ret, location, idEqTilde, INT2NUM(1));
+ if (popped) PUSH_INSN(ret, location, pop);
+
+ return;
+ }
+ case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
+ // /foo #{bar}/
+ // ^^^^^^^^^^^^
+ if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_ONCE)) {
+ const rb_iseq_t *prevblock = ISEQ_COMPILE_DATA(iseq)->current_block;
+ const rb_iseq_t *block_iseq = NULL;
+ int ise_index = ISEQ_BODY(iseq)->ise_size++;
+
+ pm_scope_node_t next_scope_node;
+ pm_scope_node_init(node, &next_scope_node, scope_node);
+
+ block_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, location.line);
+ pm_scope_node_destroy(&next_scope_node);
+
+ ISEQ_COMPILE_DATA(iseq)->current_block = block_iseq;
+ PUSH_INSN2(ret, location, once, block_iseq, INT2FIX(ise_index));
+ ISEQ_COMPILE_DATA(iseq)->current_block = prevblock;
+
+ if (popped) PUSH_INSN(ret, location, pop);
+ return;
+ }
+
+ if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
+ if (!popped) {
+ VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
+ PUSH_INSN1(ret, location, putobject, regexp);
+ }
+ }
+ else {
+ pm_compile_regexp_dynamic(iseq, node, &((const pm_interpolated_regular_expression_node_t *) node)->parts, &location, ret, popped, scope_node);
+ if (popped) PUSH_INSN(ret, location, pop);
+ }
+
+ return;
+ }
+ case PM_INTERPOLATED_STRING_NODE: {
+ // "foo #{bar}"
+ // ^^^^^^^^^^^^
+ if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
+ if (!popped) {
+ VALUE string = pm_static_literal_value(iseq, node, scope_node);
+
+ if (PM_NODE_FLAG_P(node, PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN)) {
+ PUSH_INSN1(ret, location, putobject, string);
+ }
+ else if (PM_NODE_FLAG_P(node, PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE)) {
+ PUSH_INSN1(ret, location, putstring, string);
+ }
+ else {
+ PUSH_INSN1(ret, location, putchilledstring, string);
+ }
+ }
+ }
+ else {
+ const pm_interpolated_string_node_t *cast = (const pm_interpolated_string_node_t *) node;
+ int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, popped, scope_node);
+ if (length > 1) PUSH_INSN1(ret, location, concatstrings, INT2FIX(length));
+ if (popped) PUSH_INSN(ret, location, pop);
+ }
+
+ return;
+ }
+ case PM_INTERPOLATED_SYMBOL_NODE: {
+ // :"foo #{bar}"
+ // ^^^^^^^^^^^^^
+ const pm_interpolated_symbol_node_t *cast = (const pm_interpolated_symbol_node_t *) node;
+
+ if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
+ if (!popped) {
+ VALUE symbol = pm_static_literal_value(iseq, node, scope_node);
+ PUSH_INSN1(ret, location, putobject, symbol);
+ }
+ }
+ else {
+ int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, popped, scope_node);
+ if (length > 1) {
+ PUSH_INSN1(ret, location, concatstrings, INT2FIX(length));
+ }
+
+ if (!popped) {
+ PUSH_INSN(ret, location, intern);
+ }
+ else {
+ PUSH_INSN(ret, location, pop);
+ }
+ }
+
+ return;
+ }
+ case PM_INTERPOLATED_X_STRING_NODE: {
+ // `foo #{bar}`
+ // ^^^^^^^^^^^^
+ const pm_interpolated_x_string_node_t *cast = (const pm_interpolated_x_string_node_t *) node;
+
+ PUSH_INSN(ret, location, putself);
+
+ int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, false, scope_node);
+ if (length > 1) PUSH_INSN1(ret, location, concatstrings, INT2FIX(length));
+
+ PUSH_SEND_WITH_FLAG(ret, location, idBackquote, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE));
+ if (popped) PUSH_INSN(ret, location, pop);
+
+ return;
+ }
+ case PM_KEYWORD_HASH_NODE: {
+ // foo(bar: baz)
+ // ^^^^^^^^
+ const pm_keyword_hash_node_t *cast = (const pm_keyword_hash_node_t *) node;
+ const pm_node_list_t *elements = &cast->elements;
+
+ const pm_node_t *element;
+ PM_NODE_LIST_FOREACH(elements, index, element) {
+ PM_COMPILE(element);
+ }
+
+ if (!popped) PUSH_INSN1(ret, location, newhash, INT2FIX(elements->size * 2));
+ return;
+ }
+ case PM_LAMBDA_NODE: {
+ // -> {}
+ // ^^^^^
+ const pm_lambda_node_t *cast = (const pm_lambda_node_t *) node;
+
+ pm_scope_node_t next_scope_node;
+ pm_scope_node_init(node, &next_scope_node, scope_node);
+
+ int opening_lineno = pm_location_line_number(parser, &cast->opening_loc);
+ const rb_iseq_t *block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, opening_lineno);
+ pm_scope_node_destroy(&next_scope_node);
+
+ VALUE argc = INT2FIX(0);
+ PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ PUSH_CALL_WITH_BLOCK(ret, location, idLambda, argc, block);
+ RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) block);
+
+ if (popped) PUSH_INSN(ret, location, pop);
+ return;
+ }
+ case PM_LOCAL_VARIABLE_AND_WRITE_NODE: {
+ // foo &&= bar
+ // ^^^^^^^^^^^
+ const pm_local_variable_and_write_node_t *cast = (const pm_local_variable_and_write_node_t *) node;
+ LABEL *end_label = NEW_LABEL(location.line);
+
+ pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
+ PUSH_GETLOCAL(ret, location, local_index.index, local_index.level);
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ PUSH_INSNL(ret, location, branchunless, end_label);
+ if (!popped) PUSH_INSN(ret, location, pop);
+
+ PM_COMPILE_NOT_POPPED(cast->value);
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ PUSH_SETLOCAL(ret, location, local_index.index, local_index.level);
+ PUSH_LABEL(ret, end_label);
+
+ return;
+ }
+ case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: {
+ // foo += bar
+ // ^^^^^^^^^^
+ const pm_local_variable_operator_write_node_t *cast = (const pm_local_variable_operator_write_node_t *) node;
+
+ pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
+ PUSH_GETLOCAL(ret, location, local_index.index, local_index.level);
+
+ PM_COMPILE_NOT_POPPED(cast->value);
+
+ ID method_id = pm_constant_id_lookup(scope_node, cast->operator);
+ PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
+
+ if (!popped) PUSH_INSN(ret, location, dup);
+ PUSH_SETLOCAL(ret, location, local_index.index, local_index.level);
+
+ return;
+ }
+ case PM_LOCAL_VARIABLE_OR_WRITE_NODE: {
+ // foo ||= bar
+ // ^^^^^^^^^^^
+ const pm_local_variable_or_write_node_t *cast = (const pm_local_variable_or_write_node_t *) node;
+
+ LABEL *set_label = NEW_LABEL(location.line);
+ LABEL *end_label = NEW_LABEL(location.line);
+
+ PUSH_INSN1(ret, location, putobject, Qtrue);
+ PUSH_INSNL(ret, location, branchunless, set_label);
+
+ pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
+ PUSH_GETLOCAL(ret, location, local_index.index, local_index.level);
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ PUSH_INSNL(ret, location, branchif, end_label);
+ if (!popped) PUSH_INSN(ret, location, pop);
+
+ PUSH_LABEL(ret, set_label);
+ PM_COMPILE_NOT_POPPED(cast->value);
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ PUSH_SETLOCAL(ret, location, local_index.index, local_index.level);
+ PUSH_LABEL(ret, end_label);
+
+ return;
+ }
+ case PM_LOCAL_VARIABLE_READ_NODE: {
+ // foo
+ // ^^^
+ const pm_local_variable_read_node_t *cast = (const pm_local_variable_read_node_t *) node;
+
+ if (!popped) {
+ pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
+ PUSH_GETLOCAL(ret, location, index.index, index.level);
+ }
+
+ return;
+ }
+ case PM_LOCAL_VARIABLE_WRITE_NODE: {
+ // foo = 1
+ // ^^^^^^^
+ const pm_local_variable_write_node_t *cast = (const pm_local_variable_write_node_t *) node;
+ PM_COMPILE_NOT_POPPED(cast->value);
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
+ PUSH_SETLOCAL(ret, location, index.index, index.level);
+ return;
+ }
+ case PM_MATCH_LAST_LINE_NODE: {
+ // if /foo/ then end
+ // ^^^^^
+ VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
+
+ PUSH_INSN1(ret, location, putobject, regexp);
+ PUSH_INSN2(ret, location, getspecial, INT2FIX(0), INT2FIX(0));
+ PUSH_SEND(ret, location, idEqTilde, INT2NUM(1));
+ if (popped) PUSH_INSN(ret, location, pop);
+
+ return;
+ }
+ case PM_MATCH_PREDICATE_NODE: {
+ // foo in bar
+ // ^^^^^^^^^^
+ const pm_match_predicate_node_t *cast = (const pm_match_predicate_node_t *) node;
+
+ // First, allocate some stack space for the cached return value of any
+ // calls to #deconstruct.
+ PUSH_INSN(ret, location, putnil);
+
+ // Next, compile the expression that we're going to match against.
+ PM_COMPILE_NOT_POPPED(cast->value);
+ PUSH_INSN(ret, location, dup);
+
+ // Now compile the pattern that is going to be used to match against the
+ // expression.
+ LABEL *matched_label = NEW_LABEL(location.line);
+ LABEL *unmatched_label = NEW_LABEL(location.line);
+ LABEL *done_label = NEW_LABEL(location.line);
+ pm_compile_pattern(iseq, scope_node, cast->pattern, ret, matched_label, unmatched_label, false, false, true, 2);
+
+ // If the pattern did not match, then compile the necessary instructions
+ // to handle pushing false onto the stack, then jump to the end.
+ PUSH_LABEL(ret, unmatched_label);
+ PUSH_INSN(ret, location, pop);
+ PUSH_INSN(ret, location, pop);
+
+ if (!popped) PUSH_INSN1(ret, location, putobject, Qfalse);
+ PUSH_INSNL(ret, location, jump, done_label);
+ PUSH_INSN(ret, location, putnil);
+
+ // If the pattern did match, then compile the necessary instructions to
+ // handle pushing true onto the stack, then jump to the end.
+ PUSH_LABEL(ret, matched_label);
+ PUSH_INSN1(ret, location, adjuststack, INT2FIX(2));
+ if (!popped) PUSH_INSN1(ret, location, putobject, Qtrue);
+ PUSH_INSNL(ret, location, jump, done_label);
+
+ PUSH_LABEL(ret, done_label);
+ return;
+ }
+ case PM_MATCH_REQUIRED_NODE: {
+ // foo => bar
+ // ^^^^^^^^^^
+ //
+ // A match required node represents pattern matching against a single
+ // pattern using the => operator. For example,
+ //
+ // foo => bar
+ //
+ // This is somewhat analogous to compiling a case match statement with a
+ // single pattern. In both cases, if the pattern fails it should
+ // immediately raise an error.
+ const pm_match_required_node_t *cast = (const pm_match_required_node_t *) node;
+
+ LABEL *matched_label = NEW_LABEL(location.line);
+ LABEL *unmatched_label = NEW_LABEL(location.line);
+ LABEL *done_label = NEW_LABEL(location.line);
+
+ // First, we're going to push a bunch of stuff onto the stack that is
+ // going to serve as our scratch space.
+ PUSH_INSN(ret, location, putnil); // key error key
+ PUSH_INSN(ret, location, putnil); // key error matchee
+ PUSH_INSN1(ret, location, putobject, Qfalse); // key error?
+ PUSH_INSN(ret, location, putnil); // error string
+ PUSH_INSN(ret, location, putnil); // deconstruct cache
+
+ // Next we're going to compile the value expression such that it's on
+ // the stack.
+ PM_COMPILE_NOT_POPPED(cast->value);
+
+ // Here we'll dup it so that it can be used for comparison, but also be
+ // used for error handling.
+ PUSH_INSN(ret, location, dup);
+
+ // Next we'll compile the pattern. We indicate to the pm_compile_pattern
+ // function that this is the only pattern that will be matched against
+ // through the in_single_pattern parameter. We also indicate that the
+ // value to compare against is 2 slots from the top of the stack (the
+ // base_index parameter).
+ pm_compile_pattern(iseq, scope_node, cast->pattern, ret, matched_label, unmatched_label, true, false, true, 2);
+
+ // If the pattern did not match the value, then we're going to compile
+ // in our error handler code. This will determine which error to raise
+ // and raise it.
+ PUSH_LABEL(ret, unmatched_label);
+ pm_compile_pattern_error_handler(iseq, scope_node, node, ret, done_label, popped);
+
+ // If the pattern did match, we'll clean up the values we've pushed onto
+ // the stack and then push nil onto the stack if it's not popped.
+ PUSH_LABEL(ret, matched_label);
+ PUSH_INSN1(ret, location, adjuststack, INT2FIX(6));
+ if (!popped) PUSH_INSN(ret, location, putnil);
+ PUSH_INSNL(ret, location, jump, done_label);
+
+ PUSH_LABEL(ret, done_label);
+ return;
+ }
+ case PM_MATCH_WRITE_NODE: {
+ // /(?<foo>foo)/ =~ bar
+ // ^^^^^^^^^^^^^^^^^^^^
+ //
+ // Match write nodes are specialized call nodes that have a regular
+ // expression with valid named capture groups on the left, the =~
+ // operator, and some value on the right. The nodes themselves simply
+ // wrap the call with the local variable targets that will be written
+ // when the call is executed.
+ const pm_match_write_node_t *cast = (const pm_match_write_node_t *) node;
+ LABEL *fail_label = NEW_LABEL(location.line);
+ LABEL *end_label = NEW_LABEL(location.line);
+
+ // First, we'll compile the call so that all of its instructions are
+ // present. Then we'll compile all of the local variable targets.
+ PM_COMPILE_NOT_POPPED((const pm_node_t *) cast->call);
+
+ // Now, check if the match was successful. If it was, then we'll
+ // continue on and assign local variables. Otherwise we'll skip over the
+ // assignment code.
+ PUSH_INSN1(ret, location, getglobal, rb_id2sym(idBACKREF));
+ PUSH_INSN(ret, location, dup);
+ PUSH_INSNL(ret, location, branchunless, fail_label);
+
+ // If there's only a single local variable target, we can skip some of
+ // the bookkeeping, so we'll put a special branch here.
+ size_t targets_count = cast->targets.size;
+
+ if (targets_count == 1) {
+ const pm_node_t *target = cast->targets.nodes[0];
+ RUBY_ASSERT(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_TARGET_NODE));
+
+ const pm_local_variable_target_node_t *local_target = (const pm_local_variable_target_node_t *) target;
+ pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, local_target->name, local_target->depth);
+
+ PUSH_INSN1(ret, location, putobject, rb_id2sym(pm_constant_id_lookup(scope_node, local_target->name)));
+ PUSH_SEND(ret, location, idAREF, INT2FIX(1));
+ PUSH_LABEL(ret, fail_label);
+ PUSH_SETLOCAL(ret, location, index.index, index.level);
+ if (popped) PUSH_INSN(ret, location, pop);
+ return;
+ }
+
+ DECL_ANCHOR(fail_anchor);
+ INIT_ANCHOR(fail_anchor);
+
+ // Otherwise there is more than one local variable target, so we'll need
+ // to do some bookkeeping.
+ for (size_t targets_index = 0; targets_index < targets_count; targets_index++) {
+ const pm_node_t *target = cast->targets.nodes[targets_index];
+ RUBY_ASSERT(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_TARGET_NODE));
+
+ const pm_local_variable_target_node_t *local_target = (const pm_local_variable_target_node_t *) target;
+ pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, local_target->name, local_target->depth);
+
+ if (((size_t) targets_index) < (targets_count - 1)) {
+ PUSH_INSN(ret, location, dup);
+ }
+ PUSH_INSN1(ret, location, putobject, rb_id2sym(pm_constant_id_lookup(scope_node, local_target->name)));
+ PUSH_SEND(ret, location, idAREF, INT2FIX(1));
+ PUSH_SETLOCAL(ret, location, index.index, index.level);
+
+ PUSH_INSN(fail_anchor, location, putnil);
+ PUSH_SETLOCAL(fail_anchor, location, index.index, index.level);
+ }
+
+ // Since we matched successfully, now we'll jump to the end.
+ PUSH_INSNL(ret, location, jump, end_label);
+
+ // In the case that the match failed, we'll loop through each local
+ // variable target and set all of them to `nil`.
+ PUSH_LABEL(ret, fail_label);
+ PUSH_INSN(ret, location, pop);
+ PUSH_SEQ(ret, fail_anchor);
+
+ // Finally, we can push the end label for either case.
+ PUSH_LABEL(ret, end_label);
+ if (popped) PUSH_INSN(ret, location, pop);
+ return;
+ }
+ case PM_MISSING_NODE: {
+ rb_bug("A pm_missing_node_t should not exist in prism's AST.");
+ return;
+ }
+ case PM_MODULE_NODE: {
+ // module Foo; end
+ // ^^^^^^^^^^^^^^^
+ const pm_module_node_t *cast = (const pm_module_node_t *) node;
+
+ ID module_id = pm_constant_id_lookup(scope_node, cast->name);
+ VALUE module_name = rb_str_freeze(rb_sprintf("<module:%"PRIsVALUE">", rb_id2str(module_id)));
+
+ pm_scope_node_t next_scope_node;
+ pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
+
+ const rb_iseq_t *module_iseq = NEW_CHILD_ISEQ(&next_scope_node, module_name, ISEQ_TYPE_CLASS, location.line);
+ pm_scope_node_destroy(&next_scope_node);
+
+ const int flags = VM_DEFINECLASS_TYPE_MODULE | pm_compile_class_path(iseq, cast->constant_path, &location, ret, false, scope_node);
+ PUSH_INSN(ret, location, putnil);
+ PUSH_INSN3(ret, location, defineclass, ID2SYM(module_id), module_iseq, INT2FIX(flags));
+ RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) module_iseq);
+
+ if (popped) PUSH_INSN(ret, location, pop);
+ return;
+ }
+ case PM_REQUIRED_PARAMETER_NODE: {
+ // def foo(bar); end
+ // ^^^
+ const pm_required_parameter_node_t *cast = (const pm_required_parameter_node_t *) node;
+ pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, 0);
+
+ PUSH_SETLOCAL(ret, location, index.index, index.level);
+ return;
+ }
+ case PM_MULTI_WRITE_NODE: {
+ // foo, bar = baz
+ // ^^^^^^^^^^^^^^
+ //
+ // A multi write node represents writing to multiple values using an =
+ // operator. Importantly these nodes are only parsed when the left-hand
+ // side of the operator has multiple targets. The right-hand side of the
+ // operator having multiple targets represents an implicit array
+ // instead.
+ const pm_multi_write_node_t *cast = (const pm_multi_write_node_t *) node;
+
+ DECL_ANCHOR(writes);
+ INIT_ANCHOR(writes);
+
+ DECL_ANCHOR(cleanup);
+ INIT_ANCHOR(cleanup);
+
+ pm_multi_target_state_t state = { 0 };
+ state.position = popped ? 0 : 1;
+ size_t stack_size = pm_compile_multi_target_node(iseq, node, ret, writes, cleanup, scope_node, &state);
+
+ PM_COMPILE_NOT_POPPED(cast->value);
+ if (!popped) PUSH_INSN(ret, location, dup);
+
+ PUSH_SEQ(ret, writes);
+ if (!popped && stack_size >= 1) {
+ // Make sure the value on the right-hand side of the = operator is
+ // being returned before we pop the parent expressions.
+ PUSH_INSN1(ret, location, setn, INT2FIX(stack_size));
+ }
+
+ PUSH_SEQ(ret, cleanup);
+ return;
+ }
+ case PM_NEXT_NODE: {
+ // next
+ // ^^^^
+ //
+ // next foo
+ // ^^^^^^^^
+ const pm_next_node_t *cast = (const pm_next_node_t *) node;
+
+ if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
+ LABEL *splabel = NEW_LABEL(0);
+ PUSH_LABEL(ret, splabel);
+
+ if (cast->arguments) {
+ PM_COMPILE_NOT_POPPED((const pm_node_t *) cast->arguments);
+ }
+ else {
+ PUSH_INSN(ret, location, putnil);
+ }
+ pm_add_ensure_iseq(ret, iseq, 0, scope_node);
+
+ PUSH_ADJUST(ret, location, ISEQ_COMPILE_DATA(iseq)->redo_label);
+ PUSH_INSNL(ret, location, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
+
+ PUSH_ADJUST_RESTORE(ret, splabel);
+ if (!popped) PUSH_INSN(ret, location, putnil);
+ }
+ else if (ISEQ_COMPILE_DATA(iseq)->end_label && can_add_ensure_iseq(iseq)) {
+ LABEL *splabel = NEW_LABEL(0);
+
+ PUSH_LABEL(ret, splabel);
+ PUSH_ADJUST(ret, location, ISEQ_COMPILE_DATA(iseq)->start_label);
+
+ if (cast->arguments != NULL) {
+ PM_COMPILE_NOT_POPPED((const pm_node_t *) cast->arguments);
+ }
+ else {
+ PUSH_INSN(ret, location, putnil);
+ }
+
+ pm_add_ensure_iseq(ret, iseq, 0, scope_node);
+ PUSH_INSNL(ret, location, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
+ PUSH_ADJUST_RESTORE(ret, splabel);
+ splabel->unremovable = FALSE;
+
+ if (!popped) PUSH_INSN(ret, location, putnil);
+ }
+ else {
+ const rb_iseq_t *ip = iseq;
+ unsigned long throw_flag = 0;
+
+ while (ip) {
+ if (!ISEQ_COMPILE_DATA(ip)) {
+ ip = 0;
+ break;
+ }
+
+ throw_flag = VM_THROW_NO_ESCAPE_FLAG;
+ if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
+ /* while loop */
+ break;
+ }
+ else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
+ break;
+ }
+ else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
+ COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with next");
+ return;
+ }
+
+ ip = ISEQ_BODY(ip)->parent_iseq;
+ }
+ if (ip != 0) {
+ if (cast->arguments) {
+ PM_COMPILE_NOT_POPPED((const pm_node_t *) cast->arguments);
+ }
+ else {
+ PUSH_INSN(ret, location, putnil);
+ }
+
+ PUSH_INSN1(ret, location, throw, INT2FIX(throw_flag | TAG_NEXT));
+ if (popped) PUSH_INSN(ret, location, pop);
+ }
+ else {
+ COMPILE_ERROR(ERROR_ARGS "Invalid next");
+ return;
+ }
+ }
+
+ return;
+ }
+ case PM_NIL_NODE: {
+ // nil
+ // ^^^
+ if (!popped) {
+ PUSH_INSN(ret, location, putnil);
+ }
+
+ return;
+ }
+ case PM_NO_KEYWORDS_PARAMETER_NODE: {
+ // def foo(**nil); end
+ // ^^^^^
+ ISEQ_BODY(iseq)->param.flags.accepts_no_kwarg = TRUE;
+ return;
+ }
+ case PM_NUMBERED_REFERENCE_READ_NODE: {
+ // $1
+ // ^^
+ if (!popped) {
+ uint32_t reference_number = ((const pm_numbered_reference_read_node_t *) node)->number;
+
+ if (reference_number > 0) {
+ PUSH_INSN2(ret, location, getspecial, INT2FIX(1), INT2FIX(reference_number << 1));
+ }
+ else {
+ PUSH_INSN(ret, location, putnil);
+ }
+ }
+
+ return;
+ }
+ case PM_OR_NODE: {
+ // a or b
+ // ^^^^^^
+ const pm_or_node_t *cast = (const pm_or_node_t *) node;
+
+ LABEL *end_label = NEW_LABEL(location.line);
+ PM_COMPILE_NOT_POPPED(cast->left);
+
+ if (!popped) PUSH_INSN(ret, location, dup);
+ PUSH_INSNL(ret, location, branchif, end_label);
+
+ if (!popped) PUSH_INSN(ret, location, pop);
+ PM_COMPILE(cast->right);
+ PUSH_LABEL(ret, end_label);
+
+ return;
+ }
+ case PM_OPTIONAL_PARAMETER_NODE: {
+ // def foo(bar = 1); end
+ // ^^^^^^^
+ const pm_optional_parameter_node_t *cast = (const pm_optional_parameter_node_t *) node;
+ PM_COMPILE_NOT_POPPED(cast->value);
+
+ pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, 0);
+ PUSH_SETLOCAL(ret, location, index.index, index.level);
+
+ return;
+ }
+ case PM_PARENTHESES_NODE: {
+ // ()
+ // ^^
+ //
+ // (1)
+ // ^^^
+ const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
+
+ if (cast->body != NULL) {
+ PM_COMPILE(cast->body);
+ }
+ else if (!popped) {
+ PUSH_INSN(ret, location, putnil);
+ }
+
+ return;
+ }
+ case PM_PRE_EXECUTION_NODE: {
+ // BEGIN {}
+ // ^^^^^^^^
+ const pm_pre_execution_node_t *cast = (const pm_pre_execution_node_t *) node;
+
+ LINK_ANCHOR *outer_pre = scope_node->pre_execution_anchor;
+ RUBY_ASSERT(outer_pre != NULL);
+
+ // BEGIN{} nodes can be nested, so here we're going to do the same thing
+ // that we did for the top-level compilation where we create two
+ // anchors and then join them in the correct order into the resulting
+ // anchor.
+ DECL_ANCHOR(inner_pre);
+ INIT_ANCHOR(inner_pre);
+ scope_node->pre_execution_anchor = inner_pre;
+
+ DECL_ANCHOR(inner_body);
+ INIT_ANCHOR(inner_body);
+
+ if (cast->statements != NULL) {
+ const pm_node_list_t *body = &cast->statements->body;
+
+ for (size_t index = 0; index < body->size; index++) {
+ pm_compile_node(iseq, body->nodes[index], inner_body, true, scope_node);
+ }
+ }
+
+ if (!popped) {
+ PUSH_INSN(inner_body, location, putnil);
+ }
+
+ // Now that everything has been compiled, join both anchors together
+ // into the correct outer pre execution anchor, and reset the value so
+ // that subsequent BEGIN{} nodes can be compiled correctly.
+ PUSH_SEQ(outer_pre, inner_pre);
+ PUSH_SEQ(outer_pre, inner_body);
+ scope_node->pre_execution_anchor = outer_pre;
+
+ return;
+ }
+ case PM_POST_EXECUTION_NODE: {
+ // END {}
+ // ^^^^^^
+ const rb_iseq_t *child_iseq;
+ const rb_iseq_t *prevblock = ISEQ_COMPILE_DATA(iseq)->current_block;
+
+ pm_scope_node_t next_scope_node;
+ pm_scope_node_init(node, &next_scope_node, scope_node);
+ child_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, lineno);
+ pm_scope_node_destroy(&next_scope_node);
+
+ ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq;
+
+ int is_index = ISEQ_BODY(iseq)->ise_size++;
+ PUSH_INSN2(ret, location, once, child_iseq, INT2FIX(is_index));
+ RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) child_iseq);
+ if (popped) PUSH_INSN(ret, location, pop);
+
+ ISEQ_COMPILE_DATA(iseq)->current_block = prevblock;
+
+ return;
+ }
+ case PM_RANGE_NODE: {
+ // 0..5
+ // ^^^^
+ const pm_range_node_t *cast = (const pm_range_node_t *) node;
+ bool exclude_end = PM_NODE_FLAG_P(cast, PM_RANGE_FLAGS_EXCLUDE_END);
+
+ if (pm_optimizable_range_item_p(cast->left) && pm_optimizable_range_item_p(cast->right)) {
+ if (!popped) {
+ const pm_node_t *left = cast->left;
+ const pm_node_t *right = cast->right;
+
+ VALUE val = rb_range_new(
+ (left && PM_NODE_TYPE_P(left, PM_INTEGER_NODE)) ? parse_integer((const pm_integer_node_t *) left) : Qnil,
+ (right && PM_NODE_TYPE_P(right, PM_INTEGER_NODE)) ? parse_integer((const pm_integer_node_t *) right) : Qnil,
+ exclude_end
+ );
+
+ PUSH_INSN1(ret, location, putobject, val);
+ }
+ }
+ else {
+ if (cast->left == NULL) {
+ PUSH_INSN(ret, location, putnil);
+ }
+ else {
+ PM_COMPILE(cast->left);
+ }
+
+ if (cast->right == NULL) {
+ PUSH_INSN(ret, location, putnil);
+ }
+ else {
+ PM_COMPILE(cast->right);
+ }
+
+ if (!popped) {
+ PUSH_INSN1(ret, location, newrange, INT2FIX(exclude_end ? 1 : 0));
+ }
+ }
+ return;
+ }
+ case PM_RATIONAL_NODE: {
+ // 1r
+ // ^^
+ if (!popped) {
+ PUSH_INSN1(ret, location, putobject, parse_rational((const pm_rational_node_t *) node));
+ }
+ return;
+ }
+ case PM_REDO_NODE: {
+ // redo
+ // ^^^^
+ if (ISEQ_COMPILE_DATA(iseq)->redo_label && can_add_ensure_iseq(iseq)) {
+ LABEL *splabel = NEW_LABEL(0);
+
+ PUSH_LABEL(ret, splabel);
+ PUSH_ADJUST(ret, location, ISEQ_COMPILE_DATA(iseq)->redo_label);
+ pm_add_ensure_iseq(ret, iseq, 0, scope_node);
+
+ PUSH_INSNL(ret, location, jump, ISEQ_COMPILE_DATA(iseq)->redo_label);
+ PUSH_ADJUST_RESTORE(ret, splabel);
+ if (!popped) PUSH_INSN(ret, location, putnil);
+ }
+ else if (ISEQ_BODY(iseq)->type != ISEQ_TYPE_EVAL && ISEQ_COMPILE_DATA(iseq)->start_label && can_add_ensure_iseq(iseq)) {
+ LABEL *splabel = NEW_LABEL(0);
+
+ PUSH_LABEL(ret, splabel);
+ pm_add_ensure_iseq(ret, iseq, 0, scope_node);
+ PUSH_ADJUST(ret, location, ISEQ_COMPILE_DATA(iseq)->start_label);
+
+ PUSH_INSNL(ret, location, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
+ PUSH_ADJUST_RESTORE(ret, splabel);
+ if (!popped) PUSH_INSN(ret, location, putnil);
+ }
+ else {
+ const rb_iseq_t *ip = iseq;
+
+ while (ip) {
+ if (!ISEQ_COMPILE_DATA(ip)) {
+ ip = 0;
+ break;
+ }
+
+ if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
+ break;
+ }
+ else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
+ break;
+ }
+ else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
+ COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with redo");
+ return;
+ }
+
+ ip = ISEQ_BODY(ip)->parent_iseq;
+ }
+
+ if (ip != 0) {
+ PUSH_INSN(ret, location, putnil);
+ PUSH_INSN1(ret, location, throw, INT2FIX(VM_THROW_NO_ESCAPE_FLAG | TAG_REDO));
+ if (popped) PUSH_INSN(ret, location, pop);
+ }
+ else {
+ COMPILE_ERROR(ERROR_ARGS "Invalid redo");
+ return;
+ }
+ }
+ return;
+ }
+ case PM_REGULAR_EXPRESSION_NODE: {
+ // /foo/
+ // ^^^^^
+ if (!popped) {
+ VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
+ PUSH_INSN1(ret, location, putobject, regexp);
+ }
+ return;
+ }
+ case PM_RESCUE_NODE: {
+ // begin; rescue; end
+ // ^^^^^^^
+ const pm_rescue_node_t *cast = (const pm_rescue_node_t *) node;
+ iseq_set_exception_local_table(iseq);
+
+ // First, establish the labels that we need to be able to jump to within
+ // this compilation block.
+ LABEL *exception_match_label = NEW_LABEL(location.line);
+ LABEL *rescue_end_label = NEW_LABEL(location.line);
+
+ // Next, compile each of the exceptions that we're going to be
+ // handling. For each one, we'll add instructions to check if the
+ // exception matches the raised one, and if it does then jump to the
+ // exception_match_label label. Otherwise it will fall through to the
+ // subsequent check. If there are no exceptions, we'll only check
+ // StandardError.
+ const pm_node_list_t *exceptions = &cast->exceptions;
+
+ if (exceptions->size > 0) {
+ for (size_t index = 0; index < exceptions->size; index++) {
+ PUSH_GETLOCAL(ret, location, LVAR_ERRINFO, 0);
+ PM_COMPILE(exceptions->nodes[index]);
+ int checkmatch_flags = VM_CHECKMATCH_TYPE_RESCUE;
+ if (PM_NODE_TYPE_P(exceptions->nodes[index], PM_SPLAT_NODE)) {
+ checkmatch_flags |= VM_CHECKMATCH_ARRAY;
+ }
+ PUSH_INSN1(ret, location, checkmatch, INT2FIX(checkmatch_flags));
+ PUSH_INSNL(ret, location, branchif, exception_match_label);
+ }
+ }
+ else {
+ PUSH_GETLOCAL(ret, location, LVAR_ERRINFO, 0);
+ PUSH_INSN1(ret, location, putobject, rb_eStandardError);
+ PUSH_INSN1(ret, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
+ PUSH_INSNL(ret, location, branchif, exception_match_label);
+ }
+
+ // If none of the exceptions that we are matching against matched, then
+ // we'll jump straight to the rescue_end_label label.
+ PUSH_INSNL(ret, location, jump, rescue_end_label);
+
+ // Here we have the exception_match_label, which is where the
+ // control-flow goes in the case that one of the exceptions matched.
+ // Here we will compile the instructions to handle the exception.
+ PUSH_LABEL(ret, exception_match_label);
+ PUSH_TRACE(ret, RUBY_EVENT_RESCUE);
+
+ // If we have a reference to the exception, then we'll compile the write
+ // into the instruction sequence. This can look quite different
+ // depending on the kind of write being performed.
+ if (cast->reference) {
+ DECL_ANCHOR(writes);
+ INIT_ANCHOR(writes);
+
+ DECL_ANCHOR(cleanup);
+ INIT_ANCHOR(cleanup);
+
+ pm_compile_target_node(iseq, cast->reference, ret, writes, cleanup, scope_node, NULL);
+ PUSH_GETLOCAL(ret, location, LVAR_ERRINFO, 0);
+
+ PUSH_SEQ(ret, writes);
+ PUSH_SEQ(ret, cleanup);
+ }
+
+ // If we have statements to execute, we'll compile them here. Otherwise
+ // we'll push nil onto the stack.
+ if (cast->statements) {
+ // We'll temporarily remove the end_label location from the iseq
+ // when compiling the statements so that next/redo statements
+ // inside the body will throw to the correct place instead of
+ // jumping straight to the end of this iseq
+ LABEL *prev_end = ISEQ_COMPILE_DATA(iseq)->end_label;
+ ISEQ_COMPILE_DATA(iseq)->end_label = NULL;
+
+ PM_COMPILE((const pm_node_t *) cast->statements);
+
+ // Now restore the end_label
+ ISEQ_COMPILE_DATA(iseq)->end_label = prev_end;
+ }
+ else {
+ PUSH_INSN(ret, location, putnil);
+ }
+
+ PUSH_INSN(ret, location, leave);
+
+ // Here we'll insert the rescue_end_label label, which is jumped to if
+ // none of the exceptions matched. It will cause the control-flow to
+ // either jump to the next rescue clause or it will fall through to the
+ // subsequent instruction returning the raised error.
+ PUSH_LABEL(ret, rescue_end_label);
+ if (cast->consequent) {
+ PM_COMPILE((const pm_node_t *) cast->consequent);
+ }
+ else {
+ PUSH_GETLOCAL(ret, location, 1, 0);
+ }
+
+ return;
+ }
+ case PM_RESCUE_MODIFIER_NODE: {
+ // foo rescue bar
+ // ^^^^^^^^^^^^^^
+ const pm_rescue_modifier_node_t *cast = (const pm_rescue_modifier_node_t *) node;
+
+ pm_scope_node_t rescue_scope_node;
+ pm_scope_node_init((const pm_node_t *) cast, &rescue_scope_node, scope_node);
+
+ rb_iseq_t *rescue_iseq = NEW_CHILD_ISEQ(
+ &rescue_scope_node,
+ rb_str_concat(rb_str_new2("rescue in "), ISEQ_BODY(iseq)->location.label),
+ ISEQ_TYPE_RESCUE,
+ pm_node_line_number(parser, cast->rescue_expression)
+ );
+
+ pm_scope_node_destroy(&rescue_scope_node);
+
+ LABEL *lstart = NEW_LABEL(location.line);
+ LABEL *lend = NEW_LABEL(location.line);
+ LABEL *lcont = NEW_LABEL(location.line);
+
+ lstart->rescued = LABEL_RESCUE_BEG;
+ lend->rescued = LABEL_RESCUE_END;
+ PUSH_LABEL(ret, lstart);
+ PM_COMPILE_NOT_POPPED(cast->expression);
+ PUSH_LABEL(ret, lend);
+ PUSH_INSN(ret, location, nop);
+ PUSH_LABEL(ret, lcont);
+ if (popped) PUSH_INSN(ret, location, pop);
+
+ PUSH_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue_iseq, lcont);
+ PUSH_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, NULL, lstart);
+ return;
+ }
+ case PM_RETURN_NODE: {
+ // return
+ // ^^^^^^
+ //
+ // return 1
+ // ^^^^^^^^
+ const pm_return_node_t *cast = (const pm_return_node_t *) node;
+ const pm_arguments_node_t *arguments = cast->arguments;
+
+ if (PM_NODE_FLAG_P(cast, PM_RETURN_NODE_FLAGS_REDUNDANT)) {
+ if (arguments) {
+ PM_COMPILE_NOT_POPPED((const pm_node_t *) arguments);
+ }
+ else {
+ PUSH_INSN(ret, location, putnil);
+ }
+ }
+ else {
+ enum rb_iseq_type type = ISEQ_BODY(iseq)->type;
+ LABEL *splabel = 0;
+
+ const rb_iseq_t *parent_iseq = iseq;
+ enum rb_iseq_type parent_type = ISEQ_BODY(parent_iseq)->type;
+ while (parent_type == ISEQ_TYPE_RESCUE || parent_type == ISEQ_TYPE_ENSURE) {
+ if (!(parent_iseq = ISEQ_BODY(parent_iseq)->parent_iseq)) break;
+ parent_type = ISEQ_BODY(parent_iseq)->type;
+ }
+
+ switch (parent_type) {
+ case ISEQ_TYPE_TOP:
+ case ISEQ_TYPE_MAIN:
+ if (arguments) {
+ rb_warn("argument of top-level return is ignored");
+ }
+ if (parent_iseq == iseq) {
+ type = ISEQ_TYPE_METHOD;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (type == ISEQ_TYPE_METHOD) {
+ splabel = NEW_LABEL(0);
+ PUSH_LABEL(ret, splabel);
+ PUSH_ADJUST(ret, location, 0);
+ }
+
+ if (arguments) {
+ PM_COMPILE_NOT_POPPED((const pm_node_t *) arguments);
+ }
+ else {
+ PUSH_INSN(ret, location, putnil);
+ }
+
+ if (type == ISEQ_TYPE_METHOD && can_add_ensure_iseq(iseq)) {
+ pm_add_ensure_iseq(ret, iseq, 1, scope_node);
+ PUSH_TRACE(ret, RUBY_EVENT_RETURN);
+ PUSH_INSN(ret, location, leave);
+ PUSH_ADJUST_RESTORE(ret, splabel);
+ if (!popped) PUSH_INSN(ret, location, putnil);
+ }
+ else {
+ PUSH_INSN1(ret, location, throw, INT2FIX(TAG_RETURN));
+ if (popped) PUSH_INSN(ret, location, pop);
+ }
+ }
+
+ return;
+ }
+ case PM_RETRY_NODE: {
+ // retry
+ // ^^^^^
+ if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_RESCUE) {
+ PUSH_INSN(ret, location, putnil);
+ PUSH_INSN1(ret, location, throw, INT2FIX(TAG_RETRY));
+ if (popped) PUSH_INSN(ret, location, pop);
+ }
+ else {
+ COMPILE_ERROR(ERROR_ARGS "Invalid retry");
+ return;
+ }
+ return;
+ }
+ case PM_SCOPE_NODE: {
+ pm_scope_node_t *scope_node = (pm_scope_node_t *) node;
+ pm_constant_id_list_t *locals = &scope_node->locals;
+
+ pm_parameters_node_t *parameters_node = NULL;
+ pm_node_list_t *keywords_list = NULL;
+ pm_node_list_t *optionals_list = NULL;
+ pm_node_list_t *posts_list = NULL;
+ pm_node_list_t *requireds_list = NULL;
+ pm_node_list_t *block_locals = NULL;
+ bool trailing_comma = false;
+
+ struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
+
+ if (PM_NODE_TYPE_P(scope_node->ast_node, PM_CLASS_NODE)) {
+ ADD_TRACE(ret, RUBY_EVENT_CLASS);
+ }
+
+ if (scope_node->parameters) {
+ switch (PM_NODE_TYPE(scope_node->parameters)) {
+ case PM_BLOCK_PARAMETERS_NODE: {
+ pm_block_parameters_node_t *cast = (pm_block_parameters_node_t *) scope_node->parameters;
+ parameters_node = cast->parameters;
+ block_locals = &cast->locals;
+
+ if (parameters_node) {
+ if (parameters_node->rest && PM_NODE_TYPE_P(parameters_node->rest, PM_IMPLICIT_REST_NODE)) {
+ trailing_comma = true;
+ }
+ }
+ break;
+ }
+ case PM_PARAMETERS_NODE: {
+ parameters_node = (pm_parameters_node_t *) scope_node->parameters;
+ break;
+ }
+ case PM_NUMBERED_PARAMETERS_NODE: {
+ uint32_t maximum = ((const pm_numbered_parameters_node_t *) scope_node->parameters)->maximum;
+ body->param.lead_num = maximum;
+ body->param.flags.ambiguous_param0 = maximum == 1;
+ break;
+ }
+ case PM_IT_PARAMETERS_NODE:
+ body->param.lead_num = 1;
+ body->param.flags.ambiguous_param0 = true;
+ break;
+ default:
+ rb_bug("Unexpected node type for parameters: %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
+ }
+ }
+
+ struct rb_iseq_param_keyword *keyword = NULL;
+
+ if (parameters_node) {
+ optionals_list = &parameters_node->optionals;
+ requireds_list = &parameters_node->requireds;
+ keywords_list = &parameters_node->keywords;
+ posts_list = &parameters_node->posts;
+ }
+ else if (scope_node->parameters && (PM_NODE_TYPE_P(scope_node->parameters, PM_NUMBERED_PARAMETERS_NODE) || PM_NODE_TYPE_P(scope_node->parameters, PM_IT_PARAMETERS_NODE))) {
+ body->param.opt_num = 0;
+ }
+ else {
+ body->param.lead_num = 0;
+ body->param.opt_num = 0;
+ }
+
+ //********STEP 1**********
+ // Goal: calculate the table size for the locals, accounting for
+ // hidden variables and multi target nodes
+ size_t locals_size = locals->size;
+
+ // Index lookup table buffer size is only the number of the locals
+ st_table *index_lookup_table = st_init_numtable();
+
+ int table_size = (int) locals_size;
+
+ // For nodes have a hidden iteration variable. We add that to the local
+ // table size here.
+ if (PM_NODE_TYPE_P(scope_node->ast_node, PM_FOR_NODE)) table_size++;
+
+ if (keywords_list && keywords_list->size) {
+ table_size++;
+ }
+
+ if (requireds_list) {
+ for (size_t i = 0; i < requireds_list->size; i++) {
+ // For each MultiTargetNode, we're going to have one
+ // additional anonymous local not represented in the locals table
+ // We want to account for this in our table size
+ pm_node_t *required = requireds_list->nodes[i];
+ if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
+ table_size++;
+ }
+ else if (PM_NODE_TYPE_P(required, PM_REQUIRED_PARAMETER_NODE)) {
+ if (PM_NODE_FLAG_P(required, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
+ table_size++;
+ }
+ }
+ }
+ }
+
+ // Ensure there is enough room in the local table for any
+ // parameters that have been repeated
+ // ex: def underscore_parameters(_, _ = 1, _ = 2); _; end
+ // ^^^^^^^^^^^^
+ if (optionals_list && optionals_list->size) {
+ for (size_t i = 0; i < optionals_list->size; i++) {
+ pm_node_t * node = optionals_list->nodes[i];
+ if (PM_NODE_FLAG_P(node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
+ table_size++;
+ }
+ }
+ }
+
+ // If we have an anonymous "rest" node, we'll need to increase the local
+ // table size to take it in to account.
+ // def m(foo, *, bar)
+ // ^
+ if (parameters_node) {
+ if (parameters_node->rest) {
+ if (!(PM_NODE_TYPE_P(parameters_node->rest, PM_IMPLICIT_REST_NODE))) {
+ if (!((const pm_rest_parameter_node_t *) parameters_node->rest)->name || PM_NODE_FLAG_P(parameters_node->rest, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
+ table_size++;
+ }
+ }
+ }
+
+ // def foo(_, **_); _; end
+ // ^^^
+ if (parameters_node->keyword_rest) {
+ // def foo(...); end
+ // ^^^
+ // When we have a `...` as the keyword_rest, it's a forwarding_parameter_node and
+ // we need to leave space for 4 locals: *, **, &, ...
+ if (PM_NODE_TYPE_P(parameters_node->keyword_rest, PM_FORWARDING_PARAMETER_NODE)) {
+ table_size += 4;
+ }
+ else {
+ const pm_keyword_rest_parameter_node_t *kw_rest = (const pm_keyword_rest_parameter_node_t *) parameters_node->keyword_rest;
+
+ // If it's anonymous or repeated, then we need to allocate stack space
+ if (!kw_rest->name || PM_NODE_FLAG_P(kw_rest, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
+ table_size++;
+ }
+ }
+ }
+ }
+
+ if (posts_list) {
+ for (size_t i = 0; i < posts_list->size; i++) {
+ // For each MultiTargetNode, we're going to have one
+ // additional anonymous local not represented in the locals table
+ // We want to account for this in our table size
+ pm_node_t *required = posts_list->nodes[i];
+ if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE) || PM_NODE_FLAG_P(required, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
+ table_size++;
+ }
+ }
+ }
+
+ if (keywords_list && keywords_list->size) {
+ for (size_t i = 0; i < keywords_list->size; i++) {
+ pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
+ if (PM_NODE_FLAG_P(keyword_parameter_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
+ table_size++;
+ }
+ }
+ }
+
+ if (parameters_node && parameters_node->block) {
+ const pm_block_parameter_node_t *block_node = (const pm_block_parameter_node_t *) parameters_node->block;
+
+ if (PM_NODE_FLAG_P(block_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER) || !block_node->name) {
+ table_size++;
+ }
+ }
+
+ // We can create local_table_for_iseq with the correct size
+ VALUE idtmp = 0;
+ rb_ast_id_table_t *local_table_for_iseq = ALLOCV(idtmp, sizeof(rb_ast_id_table_t) + table_size * sizeof(ID));
+ local_table_for_iseq->size = table_size;
+
+ //********END OF STEP 1**********
+
+ //********STEP 2**********
+ // Goal: populate iv index table as well as local table, keeping the
+ // layout of the local table consistent with the layout of the
+ // stack when calling the method
+ //
+ // Do a first pass on all of the parameters, setting their values in
+ // the local_table_for_iseq, _except_ for Multis who get a hidden
+ // variable in this step, and will get their names inserted in step 3
+
+ // local_index is a cursor that keeps track of the current
+ // index into local_table_for_iseq. The local table is actually a list,
+ // and the order of that list must match the order of the items pushed
+ // on the stack. We need to take in to account things pushed on the
+ // stack that _might not have a name_ (for example array destructuring).
+ // This index helps us know which item we're dealing with and also give
+ // those anonymous items temporary names (as below)
+ int local_index = 0;
+
+ // Here we figure out local table indices and insert them in to the
+ // index lookup table and local tables.
+ //
+ // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
+ // ^^^^^^^^^^^^^
+ if (requireds_list && requireds_list->size) {
+ for (size_t i = 0; i < requireds_list->size; i++, local_index++) {
+ ID local;
+
+ // For each MultiTargetNode, we're going to have one additional
+ // anonymous local not represented in the locals table. We want
+ // to account for this in our table size.
+ pm_node_t *required = requireds_list->nodes[i];
+
+ switch (PM_NODE_TYPE(required)) {
+ // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
+ // ^^^^^^^^^^
+ case PM_MULTI_TARGET_NODE: {
+ local = rb_make_temporary_id(local_index);
+ local_table_for_iseq->ids[local_index] = local;
+ break;
+ }
+ // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
+ // ^
+ case PM_REQUIRED_PARAMETER_NODE: {
+ const pm_required_parameter_node_t *param = (const pm_required_parameter_node_t *) required;
+
+ if (PM_NODE_FLAG_P(required, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
+ ID local = pm_constant_id_lookup(scope_node, param->name);
+ local_table_for_iseq->ids[local_index] = local;
+ }
+ else {
+ pm_insert_local_index(param->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
+ }
+
+ break;
+ }
+ default: {
+ rb_bug("Unsupported node in requireds in parameters %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
+ }
+ }
+ }
+
+ body->param.lead_num = (int) requireds_list->size;
+ body->param.flags.has_lead = true;
+ }
+
+ // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
+ // ^^^^^
+ if (optionals_list && optionals_list->size) {
+ body->param.opt_num = (int) optionals_list->size;
+ body->param.flags.has_opt = true;
+
+ for (size_t i = 0; i < optionals_list->size; i++, local_index++) {
+ pm_node_t * node = optionals_list->nodes[i];
+ pm_constant_id_t name = ((const pm_optional_parameter_node_t *) node)->name;
+
+ if (PM_NODE_FLAG_P(node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
+ ID local = pm_constant_id_lookup(scope_node, name);
+ local_table_for_iseq->ids[local_index] = local;
+ }
+ else {
+ pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
+ }
+ }
+ }
+
+ // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
+ // ^^
+ if (parameters_node && parameters_node->rest) {
+ body->param.rest_start = local_index;
+
+ // If there's a trailing comma, we'll have an implicit rest node,
+ // and we don't want it to impact the rest variables on param
+ if (!(PM_NODE_TYPE_P(parameters_node->rest, PM_IMPLICIT_REST_NODE))) {
+ body->param.flags.has_rest = true;
+ RUBY_ASSERT(body->param.rest_start != -1);
+
+ pm_constant_id_t name = ((const pm_rest_parameter_node_t *) parameters_node->rest)->name;
+
+ if (name) {
+ // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
+ // ^^
+ if (PM_NODE_FLAG_P(parameters_node->rest, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
+ ID local = pm_constant_id_lookup(scope_node, name);
+ local_table_for_iseq->ids[local_index] = local;
+ }
+ else {
+ pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
+ }
+ }
+ else {
+ // def foo(a, (b, *c, d), e = 1, *, g, (h, *i, j), k:, l: 1, **m, &n)
+ // ^
+ pm_insert_local_special(idMULT, local_index, index_lookup_table, local_table_for_iseq);
+ }
+
+ local_index++;
+ }
+ }
+
+ // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
+ // ^^^^^^^^^^^^^
+ if (posts_list && posts_list->size) {
+ body->param.post_num = (int) posts_list->size;
+ body->param.post_start = local_index;
+ body->param.flags.has_post = true;
+
+ for (size_t i = 0; i < posts_list->size; i++, local_index++) {
+ ID local;
+
+ // For each MultiTargetNode, we're going to have one additional
+ // anonymous local not represented in the locals table. We want
+ // to account for this in our table size.
+ const pm_node_t *post_node = posts_list->nodes[i];
+
+ switch (PM_NODE_TYPE(post_node)) {
+ // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
+ // ^^^^^^^^^^
+ case PM_MULTI_TARGET_NODE: {
+ local = rb_make_temporary_id(local_index);
+ local_table_for_iseq->ids[local_index] = local;
+ break;
+ }
+ // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
+ // ^
+ case PM_REQUIRED_PARAMETER_NODE: {
+ const pm_required_parameter_node_t *param = (const pm_required_parameter_node_t *) post_node;
+
+ if (PM_NODE_FLAG_P(param, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
+ ID local = pm_constant_id_lookup(scope_node, param->name);
+ local_table_for_iseq->ids[local_index] = local;
+ }
+ else {
+ pm_insert_local_index(param->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
+ }
+ break;
+ }
+ default: {
+ rb_bug("Unsupported node in posts in parameters %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
+ }
+ }
+ }
+ }
+
+ // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
+ // ^^^^^^^^
+ // Keywords create an internal variable on the parse tree
+ if (keywords_list && keywords_list->size) {
+ body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
+ keyword->num = (int) keywords_list->size;
+
+ body->param.flags.has_kw = true;
+ const VALUE default_values = rb_ary_hidden_new(1);
+ const VALUE complex_mark = rb_str_tmp_new(0);
+
+ ID *ids = xcalloc(keywords_list->size, sizeof(ID));
+
+ size_t kw_index = 0;
+
+ for (size_t i = 0; i < keywords_list->size; i++) {
+ pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
+ pm_constant_id_t name;
+
+ // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
+ // ^^
+ if (PM_NODE_TYPE_P(keyword_parameter_node, PM_REQUIRED_KEYWORD_PARAMETER_NODE)) {
+ name = ((const pm_required_keyword_parameter_node_t *) keyword_parameter_node)->name;
+ keyword->required_num++;
+ ID local = pm_constant_id_lookup(scope_node, name);
+
+ if (PM_NODE_FLAG_P(keyword_parameter_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
+ local_table_for_iseq->ids[local_index] = local;
+ }
+ else {
+ pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
+ }
+ local_index++;
+ ids[kw_index++] = local;
+ }
+ }
+
+ for (size_t i = 0; i < keywords_list->size; i++) {
+ pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
+ pm_constant_id_t name;
+
+ // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
+ // ^^^^
+ if (PM_NODE_TYPE_P(keyword_parameter_node, PM_OPTIONAL_KEYWORD_PARAMETER_NODE)) {
+ const pm_optional_keyword_parameter_node_t *cast = ((const pm_optional_keyword_parameter_node_t *) keyword_parameter_node);
+
+ pm_node_t *value = cast->value;
+ name = cast->name;
+
+ if (PM_NODE_FLAG_P(value, PM_NODE_FLAG_STATIC_LITERAL) && !(PM_NODE_TYPE_P(value, PM_ARRAY_NODE) || PM_NODE_TYPE_P(value, PM_HASH_NODE) || PM_NODE_TYPE_P(value, PM_RANGE_NODE))) {
+ rb_ary_push(default_values, pm_static_literal_value(iseq, value, scope_node));
+ }
+ else {
+ rb_ary_push(default_values, complex_mark);
+ }
+
+ ID local = pm_constant_id_lookup(scope_node, name);
+ if (PM_NODE_FLAG_P(keyword_parameter_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
+ local_table_for_iseq->ids[local_index] = local;
+ }
+ else {
+ pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
+ }
+ ids[kw_index++] = local;
+ local_index++;
+ }
+
+ }
+
+ keyword->bits_start = local_index;
+ keyword->table = ids;
+
+ VALUE *dvs = ALLOC_N(VALUE, RARRAY_LEN(default_values));
+
+ for (int i = 0; i < RARRAY_LEN(default_values); i++) {
+ VALUE dv = RARRAY_AREF(default_values, i);
+ if (dv == complex_mark) dv = Qundef;
+ if (!SPECIAL_CONST_P(dv)) {
+ RB_OBJ_WRITTEN(iseq, Qundef, dv);
+ }
+ dvs[i] = dv;
+ }
+
+ keyword->default_values = dvs;
+
+ // Hidden local for keyword arguments
+ ID local = rb_make_temporary_id(local_index);
+ local_table_for_iseq->ids[local_index] = local;
+ local_index++;
+ }
+
+ if (body->type == ISEQ_TYPE_BLOCK && local_index == 1 && requireds_list && requireds_list->size == 1 && !trailing_comma) {
+ body->param.flags.ambiguous_param0 = true;
+ }
+
+ if (parameters_node) {
+ // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
+ // ^^^
+ if (parameters_node->keyword_rest) {
+ switch (PM_NODE_TYPE(parameters_node->keyword_rest)) {
+ // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **nil, &n)
+ // ^^^^^
+ case PM_NO_KEYWORDS_PARAMETER_NODE: {
+ body->param.flags.accepts_no_kwarg = true;
+ break;
+ }
+ // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
+ // ^^^
+ case PM_KEYWORD_REST_PARAMETER_NODE: {
+ const pm_keyword_rest_parameter_node_t *kw_rest_node = (const pm_keyword_rest_parameter_node_t *) parameters_node->keyword_rest;
+ if (!body->param.flags.has_kw) {
+ body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
+ }
+
+ keyword->rest_start = local_index;
+ body->param.flags.has_kwrest = true;
+
+ pm_constant_id_t constant_id = kw_rest_node->name;
+ if (constant_id) {
+ if (PM_NODE_FLAG_P(kw_rest_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
+ ID local = pm_constant_id_lookup(scope_node, constant_id);
+ local_table_for_iseq->ids[local_index] = local;
+ }
+ else {
+ pm_insert_local_index(constant_id, local_index, index_lookup_table, local_table_for_iseq, scope_node);
+ }
+ }
+ else {
+ pm_insert_local_special(idPow, local_index, index_lookup_table, local_table_for_iseq);
+ }
+
+ local_index++;
+ break;
+ }
+ // def foo(...)
+ // ^^^
+ case PM_FORWARDING_PARAMETER_NODE: {
+ body->param.rest_start = local_index;
+ body->param.flags.has_rest = true;
+
+ // Add the leading *
+ pm_insert_local_special(idMULT, local_index++, index_lookup_table, local_table_for_iseq);
+
+ // Add the kwrest **
+ RUBY_ASSERT(!body->param.flags.has_kw);
+
+ // There are no keywords declared (in the text of the program)
+ // but the forwarding node implies we support kwrest (**)
+ body->param.flags.has_kw = false;
+ body->param.flags.has_kwrest = true;
+ body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
+
+ keyword->rest_start = local_index;
+
+ pm_insert_local_special(idPow, local_index++, index_lookup_table, local_table_for_iseq);
+
+ body->param.block_start = local_index;
+ body->param.flags.has_block = true;
+
+ pm_insert_local_special(idAnd, local_index++, index_lookup_table, local_table_for_iseq);
+ pm_insert_local_special(idDot3, local_index++, index_lookup_table, local_table_for_iseq);
+ break;
+ }
+ default: {
+ rb_bug("node type %s not expected as keyword_rest", pm_node_type_to_str(PM_NODE_TYPE(parameters_node->keyword_rest)));
+ }
+ }
+ }
+
+ // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
+ // ^^
+ if (parameters_node->block) {
+ body->param.block_start = local_index;
+ body->param.flags.has_block = true;
+
+ pm_constant_id_t name = ((const pm_block_parameter_node_t *) parameters_node->block)->name;
+
+ if (name) {
+ if (PM_NODE_FLAG_P(parameters_node->block, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
+ ID local = pm_constant_id_lookup(scope_node, name);
+ local_table_for_iseq->ids[local_index] = local;
+ }
+ else {
+ pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
+ }
+ }
+ else {
+ pm_insert_local_special(idAnd, local_index, index_lookup_table, local_table_for_iseq);
+ }
+
+ local_index++;
+ }
+ }
+
+ //********END OF STEP 2**********
+ // The local table is now consistent with expected
+ // stack layout
+
+ // If there's only one required element in the parameters
+ // CRuby needs to recognize it as an ambiguous parameter
+
+ //********STEP 3**********
+ // Goal: fill in the names of the parameters in MultiTargetNodes
+ //
+ // Go through requireds again to set the multis
+
+ if (requireds_list && requireds_list->size) {
+ for (size_t i = 0; i < requireds_list->size; i++) {
+ // For each MultiTargetNode, we're going to have one
+ // additional anonymous local not represented in the locals table
+ // We want to account for this in our table size
+ const pm_node_t *required = requireds_list->nodes[i];
+
+ if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
+ local_index = pm_compile_destructured_param_locals((const pm_multi_target_node_t *) required, index_lookup_table, local_table_for_iseq, scope_node, local_index);
+ }
+ }
+ }
+
+ // Go through posts again to set the multis
+ if (posts_list && posts_list->size) {
+ for (size_t i = 0; i < posts_list->size; i++) {
+ // For each MultiTargetNode, we're going to have one
+ // additional anonymous local not represented in the locals table
+ // We want to account for this in our table size
+ const pm_node_t *post = posts_list->nodes[i];
+
+ if (PM_NODE_TYPE_P(post, PM_MULTI_TARGET_NODE)) {
+ local_index = pm_compile_destructured_param_locals((const pm_multi_target_node_t *) post, index_lookup_table, local_table_for_iseq, scope_node, local_index);
+ }
+ }
+ }
+
+ // Set any anonymous locals for the for node
+ if (PM_NODE_TYPE_P(scope_node->ast_node, PM_FOR_NODE)) {
+ if (PM_NODE_TYPE_P(((const pm_for_node_t *) scope_node->ast_node)->index, PM_LOCAL_VARIABLE_TARGET_NODE)) {
+ body->param.lead_num++;
+ }
+ else {
+ body->param.rest_start = local_index;
+ body->param.flags.has_rest = true;
+ }
+
+ ID local = rb_make_temporary_id(local_index);
+ local_table_for_iseq->ids[local_index] = local;
+ local_index++;
+ }
+
+ // Fill in any NumberedParameters, if they exist
+ if (scope_node->parameters && PM_NODE_TYPE_P(scope_node->parameters, PM_NUMBERED_PARAMETERS_NODE)) {
+ int maximum = ((const pm_numbered_parameters_node_t *) scope_node->parameters)->maximum;
+ RUBY_ASSERT(0 < maximum && maximum <= 9);
+ for (int i = 0; i < maximum; i++, local_index++) {
+ const uint8_t param_name[] = { '_', '1' + i };
+ pm_constant_id_t constant_id = pm_constant_pool_find(&parser->constant_pool, param_name, 2);
+ RUBY_ASSERT(constant_id && "parser should fill in any gaps in numbered parameters");
+ pm_insert_local_index(constant_id, local_index, index_lookup_table, local_table_for_iseq, scope_node);
+ }
+ body->param.lead_num = maximum;
+ body->param.flags.has_lead = true;
+ }
+
+ // Fill in the it variable, if it exists
+ if (scope_node->parameters && PM_NODE_TYPE_P(scope_node->parameters, PM_IT_PARAMETERS_NODE)) {
+ const uint8_t param_name[] = { '0', 'i', 't' };
+ pm_constant_id_t constant_id = pm_constant_pool_find(&parser->constant_pool, param_name, 3);
+ RUBY_ASSERT(constant_id && "parser should have inserted 0it for 'it' local");
+
+ ID local = rb_make_temporary_id(local_index);
+ local_table_for_iseq->ids[local_index] = local;
+ st_insert(index_lookup_table, (st_data_t) constant_id, (st_data_t) local_index);
+ local_index++;
+ }
+
+ //********END OF STEP 3**********
+
+ //********STEP 4**********
+ // Goal: fill in the method body locals
+ // To be explicit, these are the non-parameter locals
+ // We fill in the block_locals, if they exist
+ // lambda { |x; y| y }
+ // ^
+ if (block_locals && block_locals->size) {
+ for (size_t i = 0; i < block_locals->size; i++, local_index++) {
+ pm_constant_id_t constant_id = ((const pm_block_local_variable_node_t *) block_locals->nodes[i])->name;
+ pm_insert_local_index(constant_id, local_index, index_lookup_table, local_table_for_iseq, scope_node);
+ }
+ }
+
+ // Fill in any locals we missed
+ if (scope_node->locals.size) {
+ for (size_t i = 0; i < scope_node->locals.size; i++) {
+ pm_constant_id_t constant_id = locals->ids[i];
+ if (constant_id) {
+ struct pm_local_table_insert_ctx ctx;
+ ctx.scope_node = scope_node;
+ ctx.local_table_for_iseq = local_table_for_iseq;
+ ctx.local_index = local_index;
+
+ st_update(index_lookup_table, (st_data_t)constant_id, pm_local_table_insert_func, (st_data_t)&ctx);
+
+ local_index = ctx.local_index;
+ }
+ }
+ }
+
+ //********END OF STEP 4**********
+
+ // We set the index_lookup_table on the scope node so we can
+ // refer to the parameters correctly
+ if (scope_node->index_lookup_table) {
+ st_free_table(scope_node->index_lookup_table);
+ }
+ scope_node->index_lookup_table = index_lookup_table;
+ iseq_calc_param_size(iseq);
+ iseq_set_local_table(iseq, local_table_for_iseq);
+ scope_node->local_table_for_iseq_size = local_table_for_iseq->size;
+
+ //********STEP 5************
+ // Goal: compile anything that needed to be compiled
+ if (optionals_list && optionals_list->size) {
+ LABEL **opt_table = (LABEL **) ALLOC_N(VALUE, optionals_list->size + 1);
+ LABEL *label;
+
+ // TODO: Should we make an api for NEW_LABEL where you can pass
+ // a pointer to the label it should fill out? We already
+ // have a list of labels allocated above so it seems wasteful
+ // to do the copies.
+ for (size_t i = 0; i < optionals_list->size; i++) {
+ label = NEW_LABEL(lineno);
+ opt_table[i] = label;
+ PUSH_LABEL(ret, label);
+ pm_node_t *optional_node = optionals_list->nodes[i];
+ PM_COMPILE_NOT_POPPED(optional_node);
+ }
+
+ // Set the last label
+ label = NEW_LABEL(lineno);
+ opt_table[optionals_list->size] = label;
+ PUSH_LABEL(ret, label);
+
+ body->param.opt_table = (const VALUE *) opt_table;
+ }
+
+ if (keywords_list && keywords_list->size) {
+ size_t optional_index = 0;
+ for (size_t i = 0; i < keywords_list->size; i++) {
+ pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
+ pm_constant_id_t name;
+
+ switch (PM_NODE_TYPE(keyword_parameter_node)) {
+ // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
+ // ^^^^
+ case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: {
+ const pm_optional_keyword_parameter_node_t *cast = ((const pm_optional_keyword_parameter_node_t *) keyword_parameter_node);
+
+ pm_node_t *value = cast->value;
+ name = cast->name;
+
+ if (!PM_NODE_FLAG_P(value, PM_NODE_FLAG_STATIC_LITERAL) || PM_NODE_TYPE_P(value, PM_ARRAY_NODE) || PM_NODE_TYPE_P(value, PM_HASH_NODE) || PM_NODE_TYPE_P(value, PM_RANGE_NODE)) {
+ LABEL *end_label = NEW_LABEL(location.line);
+
+ pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, name, 0);
+ int kw_bits_idx = table_size - body->param.keyword->bits_start;
+ PUSH_INSN2(ret, location, checkkeyword, INT2FIX(kw_bits_idx + VM_ENV_DATA_SIZE - 1), INT2FIX(optional_index));
+ PUSH_INSNL(ret, location, branchif, end_label);
+ PM_COMPILE(value);
+ PUSH_SETLOCAL(ret, location, index.index, index.level);
+ PUSH_LABEL(ret, end_label);
+ }
+ optional_index++;
+ break;
+ }
+ // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
+ // ^^
+ case PM_REQUIRED_KEYWORD_PARAMETER_NODE: {
+ break;
+ }
+ default: {
+ rb_bug("Unexpected keyword parameter node type %s", pm_node_type_to_str(PM_NODE_TYPE(keyword_parameter_node)));
+ }
+ }
+ }
+ }
+
+ if (requireds_list && requireds_list->size) {
+ for (size_t i = 0; i < requireds_list->size; i++) {
+ // For each MultiTargetNode, we're going to have one additional
+ // anonymous local not represented in the locals table. We want
+ // to account for this in our table size.
+ const pm_node_t *required = requireds_list->nodes[i];
+
+ if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
+ PUSH_GETLOCAL(ret, location, table_size - (int)i, 0);
+ pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) required, ret, scope_node);
+ }
+ }
+ }
+
+ if (posts_list && posts_list->size) {
+ for (size_t i = 0; i < posts_list->size; i++) {
+ // For each MultiTargetNode, we're going to have one additional
+ // anonymous local not represented in the locals table. We want
+ // to account for this in our table size.
+ const pm_node_t *post = posts_list->nodes[i];
+
+ if (PM_NODE_TYPE_P(post, PM_MULTI_TARGET_NODE)) {
+ PUSH_GETLOCAL(ret, location, table_size - body->param.post_start - (int) i, 0);
+ pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) post, ret, scope_node);
+ }
+ }
+ }
+
+ switch (body->type) {
+ case ISEQ_TYPE_BLOCK: {
+ LABEL *start = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(0);
+ LABEL *end = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(0);
+ const pm_line_column_t block_location = { .line = body->location.first_lineno, .column = -1 };
+
+ start->rescued = LABEL_RESCUE_BEG;
+ end->rescued = LABEL_RESCUE_END;
+
+ // For nodes automatically assign the iteration variable to whatever
+ // index variable. We need to handle that write here because it has
+ // to happen in the context of the block. Note that this happens
+ // before the B_CALL tracepoint event.
+ if (PM_NODE_TYPE_P(scope_node->ast_node, PM_FOR_NODE)) {
+ pm_compile_for_node_index(iseq, ((const pm_for_node_t *) scope_node->ast_node)->index, ret, scope_node);
+ }
+
+ PUSH_TRACE(ret, RUBY_EVENT_B_CALL);
+ PUSH_INSN(ret, block_location, nop);
+ PUSH_LABEL(ret, start);
+
+ if (scope_node->body != NULL) {
+ switch (PM_NODE_TYPE(scope_node->ast_node)) {
+ case PM_POST_EXECUTION_NODE: {
+ const pm_post_execution_node_t *cast = (const pm_post_execution_node_t *) scope_node->ast_node;
+ PUSH_INSN1(ret, block_location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+
+ // We create another ScopeNode from the statements within the PostExecutionNode
+ pm_scope_node_t next_scope_node;
+ pm_scope_node_init((const pm_node_t *) cast->statements, &next_scope_node, scope_node);
+
+ const rb_iseq_t *block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(body->parent_iseq), ISEQ_TYPE_BLOCK, location.line);
+ pm_scope_node_destroy(&next_scope_node);
+
+ PUSH_CALL_WITH_BLOCK(ret, block_location, id_core_set_postexe, INT2FIX(0), block);
+ break;
+ }
+ case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
+ const pm_interpolated_regular_expression_node_t *cast = (const pm_interpolated_regular_expression_node_t *) scope_node->ast_node;
+ pm_compile_regexp_dynamic(iseq, (const pm_node_t *) cast, &cast->parts, &location, ret, popped, scope_node);
+ break;
+ }
+ default:
+ pm_compile_node(iseq, scope_node->body, ret, popped, scope_node);
+ break;
+ }
+ }
+ else {
+ PUSH_INSN(ret, block_location, putnil);
+ }
+
+ PUSH_LABEL(ret, end);
+ PUSH_TRACE(ret, RUBY_EVENT_B_RETURN);
+ ISEQ_COMPILE_DATA(iseq)->last_line = body->location.code_location.end_pos.lineno;
+
+ /* wide range catch handler must put at last */
+ PUSH_CATCH_ENTRY(CATCH_TYPE_REDO, start, end, NULL, start);
+ PUSH_CATCH_ENTRY(CATCH_TYPE_NEXT, start, end, NULL, end);
+ break;
+ }
+ case ISEQ_TYPE_ENSURE: {
+ const pm_line_column_t statements_location = (scope_node->body != NULL ? PM_NODE_START_LINE_COLUMN(scope_node->parser, scope_node->body) : location);
+ iseq_set_exception_local_table(iseq);
+
+ if (scope_node->body != NULL) {
+ PM_COMPILE_POPPED((const pm_node_t *) scope_node->body);
+ }
+
+ PUSH_GETLOCAL(ret, statements_location, 1, 0);
+ PUSH_INSN1(ret, statements_location, throw, INT2FIX(0));
+ return;
+ }
+ case ISEQ_TYPE_METHOD: {
+ PUSH_TRACE(ret, RUBY_EVENT_CALL);
+ if (scope_node->body) {
+ PM_COMPILE((const pm_node_t *) scope_node->body);
+ }
+ else {
+ PUSH_INSN(ret, location, putnil);
+ }
+
+ PUSH_TRACE(ret, RUBY_EVENT_RETURN);
+ ISEQ_COMPILE_DATA(iseq)->last_line = body->location.code_location.end_pos.lineno;
+
+ break;
+ }
+ case ISEQ_TYPE_RESCUE: {
+ iseq_set_exception_local_table(iseq);
+ if (PM_NODE_TYPE_P(scope_node->ast_node, PM_RESCUE_MODIFIER_NODE)) {
+ LABEL *lab = NEW_LABEL(lineno);
+ LABEL *rescue_end = NEW_LABEL(lineno);
+ PUSH_GETLOCAL(ret, location, LVAR_ERRINFO, 0);
+ PUSH_INSN1(ret, location, putobject, rb_eStandardError);
+ PUSH_INSN1(ret, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
+ PUSH_INSNL(ret, location, branchif, lab);
+ PUSH_INSNL(ret, location, jump, rescue_end);
+ PUSH_LABEL(ret, lab);
+ PM_COMPILE((const pm_node_t *) scope_node->body);
+ PUSH_INSN(ret, location, leave);
+ PUSH_LABEL(ret, rescue_end);
+ PUSH_GETLOCAL(ret, location, LVAR_ERRINFO, 0);
+ }
+ else {
+ PM_COMPILE((const pm_node_t *) scope_node->ast_node);
+ }
+ PUSH_INSN1(ret, location, throw, INT2FIX(0));
+
+ return;
+ }
+ default:
+ if (scope_node->body) {
+ PM_COMPILE((const pm_node_t *) scope_node->body);
+ }
+ else {
+ PUSH_INSN(ret, location, putnil);
+ }
+ break;
+ }
+
+ if (PM_NODE_TYPE_P(scope_node->ast_node, PM_CLASS_NODE)) {
+ const pm_line_column_t end_location = PM_NODE_END_LINE_COLUMN(scope_node->parser, scope_node->ast_node);
+ ADD_TRACE(ret, RUBY_EVENT_END);
+ ISEQ_COMPILE_DATA(iseq)->last_line = end_location.line;
+ }
+
+ if (!PM_NODE_TYPE_P(scope_node->ast_node, PM_ENSURE_NODE)) {
+ const pm_line_column_t location = { .line = ISEQ_COMPILE_DATA(iseq)->last_line, .column = -1 };
+ PUSH_INSN(ret, location, leave);
+ }
+
+ return;
+ }
+ case PM_SELF_NODE: {
+ // self
+ // ^^^^
+ if (!popped) {
+ PUSH_INSN(ret, location, putself);
+ }
+ return;
+ }
+ case PM_SHAREABLE_CONSTANT_NODE: {
+ // A value that is being written to a constant that is being marked as
+ // shared depending on the current lexical context.
+ PM_COMPILE(((const pm_shareable_constant_node_t *) node)->write);
+ return;
+ }
+ case PM_SINGLETON_CLASS_NODE: {
+ // class << self; end
+ // ^^^^^^^^^^^^^^^^^^
+ const pm_singleton_class_node_t *cast = (const pm_singleton_class_node_t *) node;
+
+ pm_scope_node_t next_scope_node;
+ pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
+ const rb_iseq_t *child_iseq = NEW_ISEQ(&next_scope_node, rb_fstring_lit("singleton class"), ISEQ_TYPE_CLASS, location.line);
+ pm_scope_node_destroy(&next_scope_node);
+
+ PM_COMPILE_NOT_POPPED(cast->expression);
+ PUSH_INSN(ret, location, putnil);
+
+ ID singletonclass;
+ CONST_ID(singletonclass, "singletonclass");
+ PUSH_INSN3(ret, location, defineclass, ID2SYM(singletonclass), child_iseq, INT2FIX(VM_DEFINECLASS_TYPE_SINGLETON_CLASS));
+
+ if (popped) PUSH_INSN(ret, location, pop);
+ RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) child_iseq);
+
+ return;
+ }
+ case PM_SOURCE_ENCODING_NODE: {
+ // __ENCODING__
+ // ^^^^^^^^^^^^
+ if (!popped) {
+ VALUE value = pm_static_literal_value(iseq, node, scope_node);
+ PUSH_INSN1(ret, location, putobject, value);
+ }
+ return;
+ }
+ case PM_SOURCE_FILE_NODE: {
+ // __FILE__
+ // ^^^^^^^^
+ if (!popped) {
+ const pm_source_file_node_t *cast = (const pm_source_file_node_t *) node;
+ VALUE string = pm_source_file_value(cast, scope_node);
+
+ if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_FROZEN)) {
+ PUSH_INSN1(ret, location, putobject, string);
+ }
+ else if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_MUTABLE)) {
+ PUSH_INSN1(ret, location, putstring, string);
+ }
+ else {
+ PUSH_INSN1(ret, location, putchilledstring, string);
+ }
+ }
+ return;
+ }
+ case PM_SOURCE_LINE_NODE: {
+ // __LINE__
+ // ^^^^^^^^
+ if (!popped) {
+ VALUE value = pm_static_literal_value(iseq, node, scope_node);
+ PUSH_INSN1(ret, location, putobject, value);
+ }
+ return;
+ }
+ case PM_SPLAT_NODE: {
+ // foo(*bar)
+ // ^^^^
+ const pm_splat_node_t *cast = (const pm_splat_node_t *) node;
+ if (cast->expression) {
+ PM_COMPILE(cast->expression);
+ }
+
+ if (!popped) {
+ PUSH_INSN1(ret, location, splatarray, Qtrue);
+ }
+ return;
+ }
+ case PM_STATEMENTS_NODE: {
+ // A list of statements.
+ const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
+ const pm_node_list_t *body = &cast->body;
+
+ if (body->size > 0) {
+ for (size_t index = 0; index < body->size - 1; index++) {
+ PM_COMPILE_POPPED(body->nodes[index]);
+ }
+ PM_COMPILE(body->nodes[body->size - 1]);
+ }
+ else {
+ PUSH_INSN(ret, location, putnil);
+ }
+ return;
+ }
+ case PM_STRING_NODE: {
+ // "foo"
+ // ^^^^^
+ if (!popped) {
+ const pm_string_node_t *cast = (const pm_string_node_t *) node;
+ VALUE value = parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
+
+ if (PM_NODE_FLAG_P(node, PM_STRING_FLAGS_FROZEN)) {
+ PUSH_INSN1(ret, location, putobject, value);
+ }
+ else if (PM_NODE_FLAG_P(node, PM_STRING_FLAGS_MUTABLE)) {
+ PUSH_INSN1(ret, location, putstring, value);
+ }
+ else {
+ PUSH_INSN1(ret, location, putchilledstring, value);
+ }
+ }
+ return;
+ }
+ case PM_SUPER_NODE: {
+ // super(foo)
+ // ^^^^^^^^^^
+ const pm_super_node_t *cast = (const pm_super_node_t *) node;
+
+ DECL_ANCHOR(args);
+ INIT_ANCHOR(args);
+
+ LABEL *retry_label = NEW_LABEL(location.line);
+ LABEL *retry_end_l = NEW_LABEL(location.line);
+
+ const rb_iseq_t *previous_block = ISEQ_COMPILE_DATA(iseq)->current_block;
+ const rb_iseq_t *current_block;
+ ISEQ_COMPILE_DATA(iseq)->current_block = current_block = NULL;
+
+ PUSH_LABEL(ret, retry_label);
+ PUSH_INSN(ret, location, putself);
+
+ int flags = 0;
+ struct rb_callinfo_kwarg *keywords = NULL;
+ int argc = pm_setup_args(cast->arguments, cast->block, &flags, &keywords, iseq, ret, scope_node, &location);
+ flags |= VM_CALL_SUPER | VM_CALL_FCALL;
+
+ if (cast->block && PM_NODE_TYPE_P(cast->block, PM_BLOCK_NODE)) {
+ pm_scope_node_t next_scope_node;
+ pm_scope_node_init(cast->block, &next_scope_node, scope_node);
+
+ ISEQ_COMPILE_DATA(iseq)->current_block = current_block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, lineno);
+ pm_scope_node_destroy(&next_scope_node);
+ }
+
+ if ((flags & VM_CALL_ARGS_BLOCKARG) && (flags & VM_CALL_KW_SPLAT) && !(flags & VM_CALL_KW_SPLAT_MUT)) {
+ PUSH_INSN(args, location, splatkw);
+ }
+
+ PUSH_SEQ(ret, args);
+ PUSH_INSN2(ret, location, invokesuper, new_callinfo(iseq, 0, argc, flags, keywords, current_block != NULL), current_block);
+ pm_compile_retry_end_label(iseq, ret, retry_end_l);
+
+ if (popped) PUSH_INSN(ret, location, pop);
+ ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
+ PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, current_block, retry_end_l);
+
+ return;
+ }
+ case PM_SYMBOL_NODE: {
+ // :foo
+ // ^^^^
+ if (!popped) {
+ VALUE value = pm_static_literal_value(iseq, node, scope_node);
+ PUSH_INSN1(ret, location, putobject, value);
+ }
+ return;
+ }
+ case PM_TRUE_NODE: {
+ // true
+ // ^^^^
+ if (!popped) {
+ PUSH_INSN1(ret, location, putobject, Qtrue);
+ }
+ return;
+ }
+ case PM_UNDEF_NODE: {
+ // undef foo
+ // ^^^^^^^^^
+ const pm_undef_node_t *cast = (const pm_undef_node_t *) node;
+ const pm_node_list_t *names = &cast->names;
+
+ for (size_t index = 0; index < names->size; index++) {
+ PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
+
+ PM_COMPILE_NOT_POPPED(names->nodes[index]);
+ PUSH_SEND(ret, location, id_core_undef_method, INT2NUM(2));
+
+ if (index < names->size - 1) {
+ PUSH_INSN(ret, location, pop);
+ }
+ }
+
+ if (popped) PUSH_INSN(ret, location, pop);
+ return;
+ }
+ case PM_UNLESS_NODE: {
+ // unless foo; bar end
+ // ^^^^^^^^^^^^^^^^^^^
+ //
+ // bar unless foo
+ // ^^^^^^^^^^^^^^
+ const pm_unless_node_t *cast = (const pm_unless_node_t *) node;
+ const pm_statements_node_t *consequent = NULL;
+ if (cast->consequent != NULL) {
+ consequent = ((const pm_else_node_t *) cast->consequent)->statements;
+ }
+
+ pm_compile_conditional(iseq, &location, PM_UNLESS_NODE, (const pm_node_t *) cast, consequent, (const pm_node_t *) cast->statements, cast->predicate, ret, popped, scope_node);
+ return;
+ }
+ case PM_UNTIL_NODE: {
+ // until foo; bar end
+ // ^^^^^^^^^^^^^^^^^
+ //
+ // bar until foo
+ // ^^^^^^^^^^^^^
+ const pm_until_node_t *cast = (const pm_until_node_t *) node;
+ pm_compile_loop(iseq, &location, cast->base.flags, PM_UNTIL_NODE, (const pm_node_t *) cast, cast->statements, cast->predicate, ret, popped, scope_node);
+ return;
+ }
+ case PM_WHILE_NODE: {
+ // while foo; bar end
+ // ^^^^^^^^^^^^^^^^^^
+ //
+ // bar while foo
+ // ^^^^^^^^^^^^^
+ const pm_while_node_t *cast = (const pm_while_node_t *) node;
+ pm_compile_loop(iseq, &location, cast->base.flags, PM_WHILE_NODE, (const pm_node_t *) cast, cast->statements, cast->predicate, ret, popped, scope_node);
+ return;
+ }
+ case PM_X_STRING_NODE: {
+ // `foo`
+ // ^^^^^
+ const pm_x_string_node_t *cast = (const pm_x_string_node_t *) node;
+ VALUE value = parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
+
+ PUSH_INSN(ret, location, putself);
+ PUSH_INSN1(ret, location, putobject, value);
+ PUSH_SEND_WITH_FLAG(ret, location, idBackquote, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE));
+ if (popped) PUSH_INSN(ret, location, pop);
+
+ return;
+ }
+ case PM_YIELD_NODE: {
+ // yield
+ // ^^^^^
+ //
+ // yield 1
+ // ^^^^^^^
+ const pm_yield_node_t *cast = (const pm_yield_node_t *) node;
+
+ switch (ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->type) {
+ case ISEQ_TYPE_TOP:
+ case ISEQ_TYPE_MAIN:
+ case ISEQ_TYPE_CLASS:
+ COMPILE_ERROR(ERROR_ARGS "Invalid yield");
+ return;
+ default: /* valid */;
+ }
+
+ int argc = 0;
+ int flags = 0;
+ struct rb_callinfo_kwarg *keywords = NULL;
+
+ if (cast->arguments) {
+ argc = pm_setup_args(cast->arguments, NULL, &flags, &keywords, iseq, ret, scope_node, &location);
+ }
+
+ PUSH_INSN1(ret, location, invokeblock, new_callinfo(iseq, 0, argc, flags, keywords, FALSE));
+ if (popped) PUSH_INSN(ret, location, pop);
+
+ int level = 0;
+ for (const rb_iseq_t *tmp_iseq = iseq; tmp_iseq != ISEQ_BODY(iseq)->local_iseq; level++) {
+ tmp_iseq = ISEQ_BODY(tmp_iseq)->parent_iseq;
+ }
+
+ if (level > 0) access_outer_variables(iseq, level, rb_intern("yield"), true);
+ return;
+ }
+ default: {
+ rb_raise(rb_eNotImpError, "node type %s not implemented", pm_node_type_to_str(PM_NODE_TYPE(node)));
+ return;
+ }
+ }
+}
+
+/** True if the given iseq can have pre execution blocks. */
+static inline bool
+pm_iseq_pre_execution_p(rb_iseq_t *iseq)
+{
+ switch (ISEQ_BODY(iseq)->type) {
+ case ISEQ_TYPE_TOP:
+ case ISEQ_TYPE_EVAL:
+ case ISEQ_TYPE_MAIN:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * This is the main entry-point into the prism compiler. It accepts the iseq
+ * that it should be compiling instruction into and a pointer to the scope node
+ * that it should be compiling. It returns the established instruction sequence.
+ * Note that this function could raise Ruby errors if it encounters compilation
+ * errors or if there is a bug in the compiler.
+ */
+VALUE
+pm_iseq_compile_node(rb_iseq_t *iseq, pm_scope_node_t *node)
+{
+ DECL_ANCHOR(ret);
+ INIT_ANCHOR(ret);
+
+ if (pm_iseq_pre_execution_p(iseq)) {
+ // Because these ISEQs can have BEGIN{}, we're going to create two
+ // anchors to compile them, a "pre" and a "body". We'll mark the "pre"
+ // on the scope node so that when BEGIN{} is found, its contents will be
+ // added to the "pre" anchor.
+ DECL_ANCHOR(pre);
+ INIT_ANCHOR(pre);
+ node->pre_execution_anchor = pre;
+
+ // Now we'll compile the body as normal. We won't compile directly into
+ // the "ret" anchor yet because we want to add the "pre" anchor to the
+ // beginning of the "ret" anchor first.
+ DECL_ANCHOR(body);
+ INIT_ANCHOR(body);
+ pm_compile_node(iseq, (const pm_node_t *) node, body, false, node);
+
+ // Now we'll join both anchors together so that the content is in the
+ // correct order.
+ PUSH_SEQ(ret, pre);
+ PUSH_SEQ(ret, body);
+ }
+ else {
+ // In other circumstances, we can just compile the node directly into
+ // the "ret" anchor.
+ pm_compile_node(iseq, (const pm_node_t *) node, ret, false, node);
+ }
+
+ CHECK(iseq_setup_insn(iseq, ret));
+ return iseq_setup(iseq, ret);
+}
+
+/**
+ * Free the internal memory associated with a pm_parse_result_t struct.
+ * Importantly this does not free the struct itself.
+ */
+void
+pm_parse_result_free(pm_parse_result_t *result)
+{
+ if (result->parsed) {
+ pm_node_destroy(&result->parser, result->node.ast_node);
+ pm_scope_node_destroy(&result->node);
+ }
+
+ pm_parser_free(&result->parser);
+ pm_string_free(&result->input);
+ pm_options_free(&result->options);
+}
+
+/**
+ * Check if the given source slice is valid UTF-8. The location represents the
+ * location of the error, but the slice of the source will include the content
+ * of all of the lines that the error touches, so we need to check those parts
+ * as well.
+ */
+static bool
+pm_parse_process_error_utf8_p(const pm_parser_t *parser, const pm_location_t *location)
+{
+ const size_t start_line = pm_newline_list_line_column(&parser->newline_list, location->start, 1).line;
+ const size_t end_line = pm_newline_list_line_column(&parser->newline_list, location->end, 1).line;
+
+ const uint8_t *start = parser->start + parser->newline_list.offsets[start_line - 1];
+ const uint8_t *end = ((end_line == parser->newline_list.size) ? parser->end : (parser->start + parser->newline_list.offsets[end_line]));
+ size_t width;
+
+ while (start < end) {
+ if ((width = pm_encoding_utf_8_char_width(start, end - start)) == 0) return false;
+ start += width;
+ }
+
+ return true;
+}
+
+/**
+ * Generate an error object from the given parser that contains as much
+ * information as possible about the errors that were encountered.
+ */
+static VALUE
+pm_parse_process_error(const pm_parse_result_t *result)
+{
+ const pm_parser_t *parser = &result->parser;
+ const pm_diagnostic_t *head = (const pm_diagnostic_t *) parser->error_list.head;
+ bool valid_utf8 = true;
+
+ pm_buffer_t buffer = { 0 };
+ const pm_string_t *filepath = &parser->filepath;
+
+ for (const pm_diagnostic_t *error = head; error != NULL; error = (const pm_diagnostic_t *) error->node.next) {
+ switch (error->level) {
+ case PM_ERROR_LEVEL_SYNTAX:
+ // It is implicitly assumed that the error messages will be
+ // encodeable as UTF-8. Because of this, we can't include source
+ // examples that contain invalid byte sequences. So if any source
+ // examples include invalid UTF-8 byte sequences, we will skip
+ // showing source examples entirely.
+ if (valid_utf8 && !pm_parse_process_error_utf8_p(parser, &error->location)) {
+ valid_utf8 = false;
+ }
+ break;
+ case PM_ERROR_LEVEL_ARGUMENT: {
+ // Any errors with the level PM_ERROR_LEVEL_ARGUMENT take over as
+ // the only argument that gets raised. This is to allow priority
+ // messages that should be handled before anything else.
+ int32_t line_number = (int32_t) pm_location_line_number(parser, &error->location);
+
+ pm_buffer_append_format(
+ &buffer,
+ "%.*s:%" PRIi32 ": %s",
+ (int) pm_string_length(filepath),
+ pm_string_source(filepath),
+ line_number,
+ error->message
+ );
+
+ if (pm_parse_process_error_utf8_p(parser, &error->location)) {
+ pm_buffer_append_byte(&buffer, '\n');
+
+ pm_list_node_t *list_node = (pm_list_node_t *) error;
+ pm_list_t error_list = { .size = 1, .head = list_node, .tail = list_node };
+
+ pm_parser_errors_format(parser, &error_list, &buffer, rb_stderr_tty_p(), false);
+ }
+
+ VALUE value = rb_exc_new(rb_eArgError, pm_buffer_value(&buffer), pm_buffer_length(&buffer));
+ pm_buffer_free(&buffer);
+
+ return value;
+ }
+ case PM_ERROR_LEVEL_LOAD: {
+ // Load errors are much simpler, because they don't include any of
+ // the source in them. We create the error directly from the
+ // message.
+ VALUE message = rb_enc_str_new_cstr(error->message, rb_locale_encoding());
+ VALUE value = rb_exc_new3(rb_eLoadError, message);
+ rb_ivar_set(value, rb_intern_const("@path"), Qnil);
+ return value;
+ }
+ }
+ }
+
+ pm_buffer_append_format(
+ &buffer,
+ "%.*s:%" PRIi32 ": syntax error%s found\n",
+ (int) pm_string_length(filepath),
+ pm_string_source(filepath),
+ (int32_t) pm_location_line_number(parser, &head->location),
+ (parser->error_list.size > 1) ? "s" : ""
+ );
+
+ if (valid_utf8) {
+ pm_parser_errors_format(parser, &parser->error_list, &buffer, rb_stderr_tty_p(), true);
+ }
+ else {
+ for (const pm_diagnostic_t *error = head; error != NULL; error = (const pm_diagnostic_t *) error->node.next) {
+ if (error != head) pm_buffer_append_byte(&buffer, '\n');
+ pm_buffer_append_format(&buffer, "%.*s:%" PRIi32 ": %s", (int) pm_string_length(filepath), pm_string_source(filepath), (int32_t) pm_location_line_number(parser, &error->location), error->message);
+ }
+ }
+
+ VALUE error = rb_exc_new(rb_eSyntaxError, pm_buffer_value(&buffer), pm_buffer_length(&buffer));
+
+ rb_encoding *filepath_encoding = result->node.filepath_encoding != NULL ? result->node.filepath_encoding : rb_utf8_encoding();
+ VALUE path = rb_enc_str_new((const char *) pm_string_source(filepath), pm_string_length(filepath), filepath_encoding);
+
+ rb_ivar_set(error, rb_intern_const("@path"), path);
+ pm_buffer_free(&buffer);
+
+ return error;
+}
+
+/**
+ * Parse the parse result and raise a Ruby error if there are any syntax errors.
+ * It returns an error if one should be raised. It is assumed that the parse
+ * result object is zeroed out.
+ */
+static VALUE
+pm_parse_process(pm_parse_result_t *result, pm_node_t *node)
+{
+ pm_parser_t *parser = &result->parser;
+
+ // First, set up the scope node so that the AST node is attached and can be
+ // freed regardless of whether or we return an error.
+ pm_scope_node_t *scope_node = &result->node;
+ rb_encoding *filepath_encoding = scope_node->filepath_encoding;
+
+ pm_scope_node_init(node, scope_node, NULL);
+ scope_node->filepath_encoding = filepath_encoding;
+
+ // Emit all of the various warnings from the parse.
+ const pm_diagnostic_t *warning;
+ const char *warning_filepath = (const char *) pm_string_source(&parser->filepath);
+
+ for (warning = (const pm_diagnostic_t *) parser->warning_list.head; warning != NULL; warning = (const pm_diagnostic_t *) warning->node.next) {
+ int line = pm_location_line_number(parser, &warning->location);
+
+ if (warning->level == PM_WARNING_LEVEL_VERBOSE) {
+ rb_compile_warning(warning_filepath, line, "%s", warning->message);
+ }
+ else {
+ rb_compile_warn(warning_filepath, line, "%s", warning->message);
+ }
+ }
+
+ // If there are errors, raise an appropriate error and free the result.
+ if (parser->error_list.size > 0) {
+ VALUE error = pm_parse_process_error(result);
+
+ // TODO: We need to set the backtrace.
+ // rb_funcallv(error, rb_intern("set_backtrace"), 1, &path);
+ return error;
+ }
+
+ // Now set up the constant pool and intern all of the various constants into
+ // their corresponding IDs.
+ scope_node->encoding = rb_enc_find(parser->encoding->name);
+ if (!scope_node->encoding) rb_bug("Encoding not found %s!", parser->encoding->name);
+
+ scope_node->parser = parser;
+ scope_node->constants = calloc(parser->constant_pool.size, sizeof(ID));
+
+ for (uint32_t index = 0; index < parser->constant_pool.size; index++) {
+ pm_constant_t *constant = &parser->constant_pool.constants[index];
+ scope_node->constants[index] = rb_intern3((const char *) constant->start, constant->length, scope_node->encoding);
+ }
+
+ scope_node->index_lookup_table = st_init_numtable();
+ pm_constant_id_list_t *locals = &scope_node->locals;
+ for (size_t index = 0; index < locals->size; index++) {
+ st_insert(scope_node->index_lookup_table, locals->ids[index], index);
+ }
+
+ // If we got here, this is a success and we can return Qnil to indicate that
+ // no error should be raised.
+ result->parsed = true;
+ return Qnil;
+}
+
+/**
+ * Set the frozen_string_literal option based on the default value used by the
+ * CRuby compiler.
+ */
+static void
+pm_options_frozen_string_literal_init(pm_options_t *options)
+{
+ int frozen_string_literal = rb_iseq_opt_frozen_string_literal();
+
+ switch (frozen_string_literal) {
+ case ISEQ_FROZEN_STRING_LITERAL_UNSET:
+ break;
+ case ISEQ_FROZEN_STRING_LITERAL_DISABLED:
+ pm_options_frozen_string_literal_set(options, false);
+ break;
+ case ISEQ_FROZEN_STRING_LITERAL_ENABLED:
+ pm_options_frozen_string_literal_set(options, true);
+ break;
+ default:
+ rb_bug("pm_options_frozen_string_literal_init: invalid frozen_string_literal=%d", frozen_string_literal);
+ break;
+ }
+}
+
+/**
+ * Returns an array of ruby String objects that represent the lines of the
+ * source file that the given parser parsed.
+ */
+static inline VALUE
+pm_parse_file_script_lines(const pm_scope_node_t *scope_node, const pm_parser_t *parser)
+{
+ const pm_newline_list_t *newline_list = &parser->newline_list;
+ const char *start = (const char *) parser->start;
+ const char *end = (const char *) parser->end;
+
+ // If we end exactly on a newline, then there's no need to push on a final
+ // segment. If we don't, then we need to push on the last offset up to the
+ // end of the string.
+ size_t last_offset = newline_list->offsets[newline_list->size - 1];
+ bool last_push = start + last_offset != end;
+
+ // Create the ruby strings that represent the lines of the source.
+ VALUE lines = rb_ary_new_capa(newline_list->size - (last_push ? 0 : 1));
+
+ for (size_t index = 0; index < newline_list->size - 1; index++) {
+ size_t offset = newline_list->offsets[index];
+ size_t length = newline_list->offsets[index + 1] - offset;
+
+ rb_ary_push(lines, rb_enc_str_new(start + offset, length, scope_node->encoding));
+ }
+
+ // Push on the last line if we need to.
+ if (last_push) {
+ rb_ary_push(lines, rb_enc_str_new(start + last_offset, end - (start + last_offset), scope_node->encoding));
+ }
+
+ return lines;
+}
+
+/**
+ * Attempt to load the file into memory. Return a Ruby error if the file cannot
+ * be read.
+ */
+VALUE
+pm_load_file(pm_parse_result_t *result, VALUE filepath, bool load_error)
+{
+ if (!pm_string_mapped_init(&result->input, RSTRING_PTR(filepath))) {
+#ifdef _WIN32
+ int e = rb_w32_map_errno(GetLastError());
+#else
+ int e = errno;
+#endif
+
+ VALUE error;
+
+ if (load_error) {
+ VALUE message = rb_str_buf_new_cstr(strerror(e));
+ rb_str_cat2(message, " -- ");
+ rb_str_append(message, filepath);
+
+ error = rb_exc_new3(rb_eLoadError, message);
+ rb_ivar_set(error, rb_intern_const("@path"), filepath);
+ } else {
+ error = rb_syserr_new(e, RSTRING_PTR(filepath));
+ RB_GC_GUARD(filepath);
+ }
+
+ return error;
+ }
+
+ pm_options_frozen_string_literal_init(&result->options);
+ return Qnil;
+}
+
+/**
+ * Parse the given filepath and store the resulting scope node in the given
+ * parse result struct. It returns a Ruby error if the file cannot be read or
+ * if it cannot be parsed properly. It is assumed that the parse result object
+ * is zeroed out.
+ */
+VALUE
+pm_parse_file(pm_parse_result_t *result, VALUE filepath)
+{
+ pm_options_filepath_set(&result->options, RSTRING_PTR(filepath));
+ RB_GC_GUARD(filepath);
+
+ pm_parser_init(&result->parser, pm_string_source(&result->input), pm_string_length(&result->input), &result->options);
+ pm_node_t *node = pm_parse(&result->parser);
+
+ VALUE error = pm_parse_process(result, node);
+
+ // If we're parsing a filepath, then we need to potentially support the
+ // SCRIPT_LINES__ constant, which can be a hash that has an array of lines
+ // of every read file.
+ ID id_script_lines = rb_intern("SCRIPT_LINES__");
+
+ if (rb_const_defined_at(rb_cObject, id_script_lines)) {
+ VALUE script_lines = rb_const_get_at(rb_cObject, id_script_lines);
+
+ if (RB_TYPE_P(script_lines, T_HASH)) {
+ rb_hash_aset(script_lines, filepath, pm_parse_file_script_lines(&result->node, &result->parser));
+ }
+ }
+
+ return error;
+}
+
+/**
+ * Load and then parse the given filepath. It returns a Ruby error if the file
+ * cannot be read or if it cannot be parsed properly.
+ */
+VALUE
+pm_load_parse_file(pm_parse_result_t *result, VALUE filepath)
+{
+ VALUE error = pm_load_file(result, filepath, false);
+ if (NIL_P(error)) {
+ error = pm_parse_file(result, filepath);
+ }
+
+ return error;
+}
+
+/**
+ * Parse the given source that corresponds to the given filepath and store the
+ * resulting scope node in the given parse result struct. It is assumed that the
+ * parse result object is zeroed out. If the string fails to parse, then a Ruby
+ * error is returned.
+ */
+VALUE
+pm_parse_string(pm_parse_result_t *result, VALUE source, VALUE filepath)
+{
+ rb_encoding *encoding = rb_enc_get(source);
+ if (!rb_enc_asciicompat(encoding)) {
+ return rb_exc_new_cstr(rb_eArgError, "invalid source encoding");
+ }
+
+ pm_options_frozen_string_literal_init(&result->options);
+ pm_string_constant_init(&result->input, RSTRING_PTR(source), RSTRING_LEN(source));
+ pm_options_encoding_set(&result->options, rb_enc_name(encoding));
+
+ result->node.filepath_encoding = rb_enc_get(filepath);
+ pm_options_filepath_set(&result->options, RSTRING_PTR(filepath));
+ RB_GC_GUARD(filepath);
+
+ pm_parser_init(&result->parser, pm_string_source(&result->input), pm_string_length(&result->input), &result->options);
+ pm_node_t *node = pm_parse(&result->parser);
+
+ return pm_parse_process(result, node);
+}
+
+/**
+ * An implementation of fgets that is suitable for use with Ruby IO objects.
+ */
+static char *
+pm_parse_stdin_fgets(char *string, int size, void *stream)
+{
+ RUBY_ASSERT(size > 0);
+
+ VALUE line = rb_funcall((VALUE) stream, rb_intern("gets"), 1, INT2FIX(size - 1));
+ if (NIL_P(line)) {
+ return NULL;
+ }
+
+ const char *cstr = StringValueCStr(line);
+ size_t length = strlen(cstr);
+
+ memcpy(string, cstr, length);
+ string[length] = '\0';
+
+ return string;
+}
+
+/**
+ * Parse the source off STDIN and store the resulting scope node in the given
+ * parse result struct. It is assumed that the parse result object is zeroed
+ * out. If the stream fails to parse, then a Ruby error is returned.
+ */
+VALUE
+pm_parse_stdin(pm_parse_result_t *result)
+{
+ pm_options_frozen_string_literal_init(&result->options);
+
+ pm_buffer_t buffer;
+ pm_node_t *node = pm_parse_stream(&result->parser, &buffer, (void *) rb_stdin, pm_parse_stdin_fgets, &result->options);
+
+ // Copy the allocated buffer contents into the input string so that it gets
+ // freed. At this point we've handed over ownership, so we don't need to
+ // free the buffer itself.
+ pm_string_owned_init(&result->input, (uint8_t *) pm_buffer_value(&buffer), pm_buffer_length(&buffer));
+
+ return pm_parse_process(result, node);
+}
+
+#undef NEW_ISEQ
+#define NEW_ISEQ OLD_ISEQ
+
+#undef NEW_CHILD_ISEQ
+#define NEW_CHILD_ISEQ OLD_CHILD_ISEQ
diff --git a/prism_compile.h b/prism_compile.h
new file mode 100644
index 0000000000..0f82782ec0
--- /dev/null
+++ b/prism_compile.h
@@ -0,0 +1,88 @@
+#include "prism/prism.h"
+#include "ruby/encoding.h"
+
+/**
+ * the getlocal and setlocal instructions require two parameters. level is how
+ * many hops up the iseq stack one needs to go before finding the correct local
+ * table. The index is the index in that table where our variable is.
+ *
+ * Because these are always calculated and used together, we'll bind them
+ * together as a tuple.
+ */
+typedef struct pm_local_index_struct {
+ int index, level;
+} pm_local_index_t;
+
+// A declaration for the struct that lives in compile.c.
+struct iseq_link_anchor;
+
+// ScopeNodes are helper nodes, and will never be part of the AST. We manually
+// declare them here to avoid generating them.
+typedef struct pm_scope_node {
+ pm_node_t base;
+ struct pm_scope_node *previous;
+ pm_node_t *ast_node;
+ pm_node_t *parameters;
+ pm_node_t *body;
+ pm_constant_id_list_t locals;
+
+ const pm_parser_t *parser;
+ rb_encoding *encoding;
+
+ /**
+ * This is the encoding of the actual filepath object that will be used when
+ * a __FILE__ node is compiled or when the path has to be set on a syntax
+ * error.
+ */
+ rb_encoding *filepath_encoding;
+
+ // The size of the local table
+ // on the iseq which includes
+ // locals and hidden variables
+ int local_table_for_iseq_size;
+
+ ID *constants;
+ st_table *index_lookup_table;
+
+ /**
+ * This will only be set on the top-level scope node. It will contain all of
+ * the instructions pertaining to BEGIN{} nodes.
+ */
+ struct iseq_link_anchor *pre_execution_anchor;
+} pm_scope_node_t;
+
+void pm_scope_node_init(const pm_node_t *node, pm_scope_node_t *scope, pm_scope_node_t *previous);
+void pm_scope_node_destroy(pm_scope_node_t *scope_node);
+bool *rb_ruby_prism_ptr(void);
+
+typedef struct {
+ /** The parser that will do the actual parsing. */
+ pm_parser_t parser;
+
+ /** The options that will be passed to the parser. */
+ pm_options_t options;
+
+ /** The input that represents the source to be parsed. */
+ pm_string_t input;
+
+ /** The resulting scope node that will hold the generated AST. */
+ pm_scope_node_t node;
+
+ /** Whether or not this parse result has performed its parsing yet. */
+ bool parsed;
+} pm_parse_result_t;
+
+VALUE pm_load_file(pm_parse_result_t *result, VALUE filepath, bool load_error);
+VALUE pm_parse_file(pm_parse_result_t *result, VALUE filepath);
+VALUE pm_load_parse_file(pm_parse_result_t *result, VALUE filepath);
+VALUE pm_parse_string(pm_parse_result_t *result, VALUE source, VALUE filepath);
+VALUE pm_parse_stdin(pm_parse_result_t *result);
+void pm_parse_result_free(pm_parse_result_t *result);
+
+rb_iseq_t *pm_iseq_new(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent, enum rb_iseq_type);
+rb_iseq_t *pm_iseq_new_top(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent);
+rb_iseq_t *pm_iseq_new_main(pm_scope_node_t *node, VALUE path, VALUE realpath, const rb_iseq_t *parent, int opt);
+rb_iseq_t *pm_iseq_new_eval(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth);
+rb_iseq_t *pm_iseq_new_with_opt(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth, enum rb_iseq_type, const rb_compile_option_t*);
+
+VALUE pm_iseq_compile_node(rb_iseq_t *iseq, pm_scope_node_t *node);
diff --git a/prism_init.c b/prism_init.c
new file mode 100644
index 0000000000..ddc87fad85
--- /dev/null
+++ b/prism_init.c
@@ -0,0 +1,9 @@
+#include "prism/extension.h"
+
+void ruby_init_ext(const char *name, void (*init)(void));
+
+void
+Init_Prism(void)
+{
+ ruby_init_ext("prism/prism.so", Init_prism);
+}
diff --git a/proc.c b/proc.c
index 17c837d164..1a67a63663 100644
--- a/proc.c
+++ b/proc.c
@@ -23,15 +23,6 @@
#include "vm_core.h"
#include "yjit.h"
-#if !defined(__GNUC__) || __GNUC__ < 5 || defined(__MINGW32__)
-# define NO_CLOBBERED(v) (*(volatile VALUE *)&(v))
-#else
-# define NO_CLOBBERED(v) (v)
-#endif
-
-#define UPDATE_TYPED_REFERENCE(_type, _ref) *(_type*)&_ref = (_type)rb_gc_location((VALUE)_ref)
-#define UPDATE_REFERENCE(_ref) UPDATE_TYPED_REFERENCE(VALUE, _ref)
-
const rb_cref_t *rb_vm_cref_in_context(VALUE self, VALUE cbase);
struct METHOD {
@@ -60,23 +51,6 @@ static VALUE proc_binding(VALUE self);
#define IS_METHOD_PROC_IFUNC(ifunc) ((ifunc)->func == bmcall)
-/* :FIXME: The way procs are cloned has been historically different from the
- * way everything else are. @shyouhei is not sure for the intention though.
- */
-#undef CLONESETUP
-static inline void
-CLONESETUP(VALUE clone, VALUE obj)
-{
- RBIMPL_ASSERT_OR_ASSUME(! RB_SPECIAL_CONST_P(obj));
- RBIMPL_ASSERT_OR_ASSUME(! RB_SPECIAL_CONST_P(clone));
-
- const VALUE flags = RUBY_FL_PROMOTED0 | RUBY_FL_PROMOTED1 | RUBY_FL_FINALIZE;
- rb_obj_setup(clone, rb_singleton_class_clone(obj),
- RB_FL_TEST_RAW(obj, ~flags));
- rb_singleton_class_attached(RBASIC_CLASS(clone), clone);
- if (RB_FL_TEST(obj, RUBY_FL_EXIVAR)) rb_copy_generic_ivar(clone, obj);
-}
-
static void
block_mark_and_move(struct rb_block *block)
{
@@ -151,8 +125,15 @@ static VALUE
proc_clone(VALUE self)
{
VALUE procval = rb_proc_dup(self);
- CLONESETUP(procval, self);
- return procval;
+ return rb_obj_clone_setup(self, procval, Qnil);
+}
+
+/* :nodoc: */
+static VALUE
+proc_dup(VALUE self)
+{
+ VALUE procval = rb_proc_dup(self);
+ return rb_obj_dup_setup(self, procval);
}
/*
@@ -327,7 +308,7 @@ binding_dup(VALUE self)
rb_vm_block_copy(bindval, &dst->block, &src->block);
RB_OBJ_WRITE(bindval, &dst->pathobj, src->pathobj);
dst->first_lineno = src->first_lineno;
- return bindval;
+ return rb_obj_dup_setup(self, bindval);
}
/* :nodoc: */
@@ -335,8 +316,7 @@ static VALUE
binding_clone(VALUE self)
{
VALUE bindval = binding_dup(self);
- CLONESETUP(bindval, self);
- return bindval;
+ return rb_obj_clone_setup(self, bindval, Qnil);
}
VALUE
@@ -477,13 +457,13 @@ check_local_id(VALUE bindval, volatile VALUE *pname)
if (lid) {
if (!rb_is_local_id(lid)) {
- rb_name_err_raise("wrong local variable name `%1$s' for %2$s",
+ rb_name_err_raise("wrong local variable name '%1$s' for %2$s",
bindval, ID2SYM(lid));
}
}
else {
if (!rb_is_local_name(name)) {
- rb_name_err_raise("wrong local variable name `%1$s' for %2$s",
+ rb_name_err_raise("wrong local variable name '%1$s' for %2$s",
bindval, name);
}
return 0;
@@ -556,7 +536,7 @@ bind_local_variable_get(VALUE bindval, VALUE sym)
sym = ID2SYM(lid);
undefined:
- rb_name_err_raise("local variable `%1$s' is not defined for %2$s",
+ rb_name_err_raise("local variable '%1$s' is not defined for %2$s",
bindval, sym);
UNREACHABLE_RETURN(Qundef);
}
@@ -739,8 +719,13 @@ rb_vm_ifunc_new(rb_block_call_func_t func, const void *data, int min_argc, int m
arity.argc.min = min_argc;
arity.argc.max = max_argc;
rb_execution_context_t *ec = GET_EC();
- VALUE ret = rb_imemo_new(imemo_ifunc, (VALUE)func, (VALUE)data, arity.packed, (VALUE)rb_vm_svar_lep(ec, ec->cfp));
- return (struct vm_ifunc *)ret;
+
+ struct vm_ifunc *ifunc = IMEMO_NEW(struct vm_ifunc, imemo_ifunc, (VALUE)rb_vm_svar_lep(ec, ec->cfp));
+ ifunc->func = func;
+ ifunc->data = data;
+ ifunc->argc = arity.argc;
+
+ return ifunc;
}
VALUE
@@ -760,7 +745,7 @@ rb_func_lambda_new(rb_block_call_func_t func, VALUE val, int min_argc, int max_a
static const char proc_without_block[] = "tried to create Proc object without a block";
static VALUE
-proc_new(VALUE klass, int8_t is_lambda, int8_t kernel)
+proc_new(VALUE klass, int8_t is_lambda)
{
VALUE procval;
const rb_execution_context_t *ec = GET_EC();
@@ -793,16 +778,8 @@ proc_new(VALUE klass, int8_t is_lambda, int8_t kernel)
break;
case block_handler_type_ifunc:
- return rb_vm_make_proc_lambda(ec, VM_BH_TO_CAPT_BLOCK(block_handler), klass, is_lambda);
case block_handler_type_iseq:
- {
- const struct rb_captured_block *captured = VM_BH_TO_CAPT_BLOCK(block_handler);
- rb_control_frame_t *last_ruby_cfp = rb_vm_get_ruby_level_next_cfp(ec, cfp);
- if (is_lambda && last_ruby_cfp && vm_cfp_forwarded_bh_p(last_ruby_cfp, block_handler)) {
- is_lambda = false;
- }
- return rb_vm_make_proc_lambda(ec, captured, klass, is_lambda);
- }
+ return rb_vm_make_proc_lambda(ec, VM_BH_TO_CAPT_BLOCK(block_handler), klass, is_lambda);
}
VM_UNREACHABLE(proc_new);
return Qnil;
@@ -825,7 +802,7 @@ proc_new(VALUE klass, int8_t is_lambda, int8_t kernel)
static VALUE
rb_proc_s_new(int argc, VALUE *argv, VALUE klass)
{
- VALUE block = proc_new(klass, FALSE, FALSE);
+ VALUE block = proc_new(klass, FALSE);
rb_obj_call_init_kw(block, argc, argv, RB_PASS_CALLED_KEYWORDS);
return block;
@@ -834,7 +811,7 @@ rb_proc_s_new(int argc, VALUE *argv, VALUE klass)
VALUE
rb_block_proc(void)
{
- return proc_new(rb_cProc, FALSE, FALSE);
+ return proc_new(rb_cProc, FALSE);
}
/*
@@ -847,41 +824,44 @@ rb_block_proc(void)
static VALUE
f_proc(VALUE _)
{
- return proc_new(rb_cProc, FALSE, TRUE);
+ return proc_new(rb_cProc, FALSE);
}
VALUE
rb_block_lambda(void)
{
- return proc_new(rb_cProc, TRUE, FALSE);
+ return proc_new(rb_cProc, TRUE);
}
static void
-f_lambda_warn(void)
+f_lambda_filter_non_literal(void)
{
rb_control_frame_t *cfp = GET_EC()->cfp;
VALUE block_handler = rb_vm_frame_block_handler(cfp);
- if (block_handler != VM_BLOCK_HANDLER_NONE) {
- switch (vm_block_handler_type(block_handler)) {
- case block_handler_type_iseq:
- if (RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)->ep == VM_BH_TO_ISEQ_BLOCK(block_handler)->ep) {
- return;
- }
- break;
- case block_handler_type_symbol:
+ if (block_handler == VM_BLOCK_HANDLER_NONE) {
+ // no block erorr raised else where
+ return;
+ }
+
+ switch (vm_block_handler_type(block_handler)) {
+ case block_handler_type_iseq:
+ if (RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)->ep == VM_BH_TO_ISEQ_BLOCK(block_handler)->ep) {
+ return;
+ }
+ break;
+ case block_handler_type_symbol:
+ return;
+ case block_handler_type_proc:
+ if (rb_proc_lambda_p(VM_BH_TO_PROC(block_handler))) {
return;
- case block_handler_type_proc:
- if (rb_proc_lambda_p(VM_BH_TO_PROC(block_handler))) {
- return;
- }
- break;
- case block_handler_type_ifunc:
- break;
}
+ break;
+ case block_handler_type_ifunc:
+ break;
}
- rb_warn_deprecated("lambda without a literal block", "the proc without lambda");
+ rb_raise(rb_eArgError, "the lambda method requires a literal block");
}
/*
@@ -895,7 +875,7 @@ f_lambda_warn(void)
static VALUE
f_lambda(VALUE _)
{
- f_lambda_warn();
+ f_lambda_filter_non_literal();
return rb_block_lambda();
}
@@ -1482,7 +1462,7 @@ rb_sym_to_proc(VALUE sym)
if (!sym_proc_cache) {
sym_proc_cache = rb_ary_hidden_new(SYM_PROC_CACHE_SIZE * 2);
- rb_gc_register_mark_object(sym_proc_cache);
+ rb_vm_register_global_object(sym_proc_cache);
rb_ary_store(sym_proc_cache, SYM_PROC_CACHE_SIZE*2 - 1, Qnil);
}
@@ -1592,21 +1572,15 @@ bm_mark_and_move(void *ptr)
rb_gc_mark_and_move_ptr((rb_method_entry_t **)&data->me);
}
-static size_t
-bm_memsize(const void *ptr)
-{
- return sizeof(struct METHOD);
-}
-
static const rb_data_type_t method_data_type = {
"method",
{
bm_mark_and_move,
RUBY_TYPED_DEFAULT_FREE,
- bm_memsize,
+ NULL, // No external memory to report,
bm_mark_and_move,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
};
VALUE
@@ -1700,7 +1674,7 @@ mnew_internal(const rb_method_entry_t *me, VALUE klass, VALUE iclass,
method = TypedData_Make_Struct(mclass, struct METHOD, &method_data_type, data);
- if (obj == Qundef) {
+ if (UNDEF_P(obj)) {
RB_OBJ_WRITE(method, &data->recv, Qundef);
RB_OBJ_WRITE(method, &data->klass, Qundef);
}
@@ -1800,8 +1774,8 @@ method_eq(VALUE method, VALUE other)
return Qfalse;
Check_TypedStruct(method, &method_data_type);
- m1 = (struct METHOD *)DATA_PTR(method);
- m2 = (struct METHOD *)DATA_PTR(other);
+ m1 = (struct METHOD *)RTYPEDDATA_GET_DATA(method);
+ m2 = (struct METHOD *)RTYPEDDATA_GET_DATA(other);
klass1 = method_entry_defined_class(m1->me);
klass2 = method_entry_defined_class(m2->me);
@@ -1967,11 +1941,11 @@ method_owner(VALUE obj)
void
rb_method_name_error(VALUE klass, VALUE str)
{
-#define MSG(s) rb_fstring_lit("undefined method `%1$s' for"s" `%2$s'")
+#define MSG(s) rb_fstring_lit("undefined method '%1$s' for"s" '%2$s'")
VALUE c = klass;
VALUE s = Qundef;
- if (FL_TEST(c, FL_SINGLETON)) {
+ if (RCLASS_SINGLETON_P(c)) {
VALUE obj = RCLASS_ATTACHED_OBJECT(klass);
switch (BUILTIN_TYPE(obj)) {
@@ -2096,10 +2070,9 @@ rb_obj_singleton_method(VALUE obj, VALUE vid)
VALUE klass = rb_singleton_class_get(obj);
ID id = rb_check_id(&vid);
- if (NIL_P(klass)) {
- /* goto undef; */
- }
- else if (NIL_P(klass = RCLASS_ORIGIN(klass))) {
+ if (NIL_P(klass) ||
+ NIL_P(klass = RCLASS_ORIGIN(klass)) ||
+ !NIL_P(rb_special_singleton_class(obj))) {
/* goto undef; */
}
else if (! id) {
@@ -2123,7 +2096,7 @@ rb_obj_singleton_method(VALUE obj, VALUE vid)
}
/* undef: */
- rb_name_err_raise("undefined singleton method `%1$s' for `%2$s'",
+ rb_name_err_raise("undefined singleton method '%1$s' for '%2$s'",
obj, vid);
UNREACHABLE_RETURN(Qundef);
}
@@ -2218,10 +2191,10 @@ rb_mod_define_method_with_visibility(int argc, VALUE *argv, VALUE mod, const str
if (!id) id = rb_to_id(name);
if (is_method) {
- struct METHOD *method = (struct METHOD *)DATA_PTR(body);
+ struct METHOD *method = (struct METHOD *)RTYPEDDATA_GET_DATA(body);
if (method->me->owner != mod && !RB_TYPE_P(method->me->owner, T_MODULE) &&
!RTEST(rb_class_inherited_p(mod, method->me->owner))) {
- if (FL_TEST(method->me->owner, FL_SINGLETON)) {
+ if (RCLASS_SINGLETON_P(method->me->owner)) {
rb_raise(rb_eTypeError,
"can't bind singleton method to a different class");
}
@@ -2358,17 +2331,7 @@ rb_obj_define_method(int argc, VALUE *argv, VALUE obj)
static VALUE
top_define_method(int argc, VALUE *argv, VALUE obj)
{
- rb_thread_t *th = GET_THREAD();
- VALUE klass;
-
- klass = th->top_wrapper;
- if (klass) {
- rb_warning("main.define_method in the wrapped load is effective only in wrapper module");
- }
- else {
- klass = rb_cObject;
- }
- return rb_mod_define_method(argc, argv, klass);
+ return rb_mod_define_method(argc, argv, rb_top_main_class("define_method"));
}
/*
@@ -2396,7 +2359,25 @@ method_clone(VALUE self)
TypedData_Get_Struct(self, struct METHOD, &method_data_type, orig);
clone = TypedData_Make_Struct(CLASS_OF(self), struct METHOD, &method_data_type, data);
- CLONESETUP(clone, self);
+ rb_obj_clone_setup(self, clone, Qnil);
+ RB_OBJ_WRITE(clone, &data->recv, orig->recv);
+ RB_OBJ_WRITE(clone, &data->klass, orig->klass);
+ RB_OBJ_WRITE(clone, &data->iclass, orig->iclass);
+ RB_OBJ_WRITE(clone, &data->owner, orig->owner);
+ RB_OBJ_WRITE(clone, &data->me, rb_method_entry_clone(orig->me));
+ return clone;
+}
+
+/* :nodoc: */
+static VALUE
+method_dup(VALUE self)
+{
+ VALUE clone;
+ struct METHOD *orig, *data;
+
+ TypedData_Get_Struct(self, struct METHOD, &method_data_type, orig);
+ clone = TypedData_Make_Struct(CLASS_OF(self), struct METHOD, &method_data_type, data);
+ rb_obj_dup_setup(self, clone);
RB_OBJ_WRITE(clone, &data->recv, orig->recv);
RB_OBJ_WRITE(clone, &data->klass, orig->klass);
RB_OBJ_WRITE(clone, &data->iclass, orig->iclass);
@@ -2570,7 +2551,7 @@ convert_umethod_to_method_components(const struct METHOD *data, VALUE recv, VALU
if (!NIL_P(refined_class)) methclass = refined_class;
}
if (!RB_TYPE_P(methclass, T_MODULE) && !RTEST(rb_obj_is_kind_of(recv, methclass))) {
- if (FL_TEST(methclass, FL_SINGLETON)) {
+ if (RCLASS_SINGLETON_P(methclass)) {
rb_raise(rb_eTypeError,
"singleton method called for a different object");
}
@@ -3137,11 +3118,11 @@ method_inspect(VALUE method)
defined_class = RBASIC_CLASS(defined_class);
}
- if (data->recv == Qundef) {
+ if (UNDEF_P(data->recv)) {
// UnboundMethod
rb_str_buf_append(str, rb_inspect(defined_class));
}
- else if (FL_TEST(mklass, FL_SINGLETON)) {
+ else if (RCLASS_SINGLETON_P(mklass)) {
VALUE v = RCLASS_ATTACHED_OBJECT(mklass);
if (UNDEF_P(data->recv)) {
@@ -3161,7 +3142,7 @@ method_inspect(VALUE method)
}
else {
mklass = data->klass;
- if (FL_TEST(mklass, FL_SINGLETON)) {
+ if (RCLASS_SINGLETON_P(mklass)) {
VALUE v = RCLASS_ATTACHED_OBJECT(mklass);
if (!(RB_TYPE_P(v, T_CLASS) || RB_TYPE_P(v, T_MODULE))) {
do {
@@ -3421,9 +3402,15 @@ env_clone(const rb_env_t *env, const rb_cref_t *cref)
}
new_body = ALLOC_N(VALUE, env->env_size);
- MEMCPY(new_body, env->env, VALUE, env->env_size);
new_ep = &new_body[env->ep - env->env];
new_env = vm_env_new(new_ep, new_body, env->env_size, env->iseq);
+
+ /* The memcpy has to happen after the vm_env_new because it can trigger a
+ * GC compaction which can move the objects in the env. */
+ MEMCPY(new_body, env->env, VALUE, env->env_size);
+ /* VM_ENV_DATA_INDEX_ENV is set in vm_env_new but will get overwritten
+ * by the memcpy above. */
+ new_ep[VM_ENV_DATA_INDEX_ENV] = (VALUE)new_env;
RB_OBJ_WRITE(new_env, &new_ep[VM_ENV_DATA_INDEX_ME_CREF], (VALUE)cref);
VM_ASSERT(VM_ENV_ESCAPED_P(new_ep));
return new_env;
@@ -3480,7 +3467,7 @@ proc_binding(VALUE self)
env = VM_ENV_ENVVAL_PTR(block->as.captured.ep);
env = env_clone(env, method_cref(method));
/* set empty iseq */
- empty = rb_iseq_new(NULL, name, name, Qnil, 0, ISEQ_TYPE_TOP);
+ empty = rb_iseq_new(Qnil, name, name, Qnil, 0, ISEQ_TYPE_TOP);
RB_OBJ_WRITE(env, &env->iseq, empty);
break;
}
@@ -4264,7 +4251,7 @@ Init_Proc(void)
rb_define_method(rb_cProc, "to_proc", proc_to_proc, 0);
rb_define_method(rb_cProc, "arity", proc_arity, 0);
rb_define_method(rb_cProc, "clone", proc_clone, 0);
- rb_define_method(rb_cProc, "dup", rb_proc_dup, 0);
+ rb_define_method(rb_cProc, "dup", proc_dup, 0);
rb_define_method(rb_cProc, "hash", proc_hash, 0);
rb_define_method(rb_cProc, "to_s", proc_to_s, 0);
rb_define_alias(rb_cProc, "inspect", "to_s");
@@ -4300,6 +4287,7 @@ Init_Proc(void)
rb_define_method(rb_cMethod, "eql?", method_eq, 1);
rb_define_method(rb_cMethod, "hash", method_hash, 0);
rb_define_method(rb_cMethod, "clone", method_clone, 0);
+ rb_define_method(rb_cMethod, "dup", method_dup, 0);
rb_define_method(rb_cMethod, "call", rb_method_call_pass_called_kw, -1);
rb_define_method(rb_cMethod, "===", rb_method_call_pass_called_kw, -1);
rb_define_method(rb_cMethod, "curry", rb_method_curry, -1);
@@ -4330,6 +4318,7 @@ Init_Proc(void)
rb_define_method(rb_cUnboundMethod, "eql?", unbound_method_eq, 1);
rb_define_method(rb_cUnboundMethod, "hash", method_hash, 0);
rb_define_method(rb_cUnboundMethod, "clone", method_clone, 0);
+ rb_define_method(rb_cUnboundMethod, "dup", method_dup, 0);
rb_define_method(rb_cUnboundMethod, "arity", method_arity_m, 0);
rb_define_method(rb_cUnboundMethod, "inspect", method_inspect, 0);
rb_define_method(rb_cUnboundMethod, "to_s", method_inspect, 0);
diff --git a/process.c b/process.c
index b4435446f0..b1f9931f06 100644
--- a/process.c
+++ b/process.c
@@ -116,6 +116,7 @@ int initgroups(const char *, rb_gid_t);
#include "ruby/thread.h"
#include "ruby/util.h"
#include "vm_core.h"
+#include "vm_sync.h"
#include "ruby/ractor.h"
/* define system APIs */
@@ -517,12 +518,12 @@ clear_pid_cache(void)
/*
* call-seq:
- * Process.pid -> integer
+ * Process.pid -> integer
*
- * Returns the process id of this process. Not available on all
- * platforms.
+ * Returns the process ID of the current process:
+ *
+ * Process.pid # => 15668
*
- * Process.pid #=> 27415
*/
static VALUE
@@ -539,18 +540,19 @@ get_ppid(void)
/*
* call-seq:
- * Process.ppid -> integer
+ * Process.ppid -> integer
+ *
+ * Returns the process ID of the parent of the current process:
*
- * Returns the process id of the parent of this process. Returns
- * untrustworthy value on Win32/64. Not available on all platforms.
+ * puts "Pid is #{Process.pid}."
+ * fork { puts "Parent pid is #{Process.ppid}." }
*
- * puts "I am #{Process.pid}"
- * Process.fork { puts "Dad is #{Process.ppid}" }
+ * Output:
*
- * <em>produces:</em>
+ * Pid is 271290.
+ * Parent pid is 271290.
*
- * I am 27417
- * Dad is 27417
+ * May not return a trustworthy value on certain platforms.
*/
static VALUE
@@ -564,30 +566,20 @@ proc_get_ppid(VALUE _)
*
* Document-class: Process::Status
*
- * Process::Status encapsulates the information on the
- * status of a running or terminated system process. The built-in
- * variable <code>$?</code> is either +nil+ or a
- * Process::Status object.
- *
- * fork { exit 99 } #=> 26557
- * Process.wait #=> 26557
- * $?.class #=> Process::Status
- * $?.to_i #=> 25344
- * $? >> 8 #=> 99
- * $?.stopped? #=> false
- * $?.exited? #=> true
- * $?.exitstatus #=> 99
- *
- * Posix systems record information on processes using a 16-bit
- * integer. The lower bits record the process status (stopped,
- * exited, signaled) and the upper bits possibly contain additional
- * information (for example the program's return code in the case of
- * exited processes). Pre Ruby 1.8, these bits were exposed directly
- * to the Ruby program. Ruby now encapsulates these in a
- * Process::Status object. To maximize compatibility,
- * however, these objects retain a bit-oriented interface. In the
- * descriptions that follow, when we talk about the integer value of
- * _stat_, we're referring to this 16 bit value.
+ * A Process::Status contains information about a system process.
+ *
+ * Thread-local variable <tt>$?</tt> is initially +nil+.
+ * Some methods assign to it a Process::Status object
+ * that represents a system process (either running or terminated):
+ *
+ * `ruby -e "exit 99"`
+ * stat = $? # => #<Process::Status: pid 1262862 exit 99>
+ * stat.class # => Process::Status
+ * stat.to_i # => 25344
+ * stat.stopped? # => false
+ * stat.exited? # => true
+ * stat.exitstatus # => 99
+ *
*/
static VALUE rb_cProcessStatus;
@@ -601,17 +593,17 @@ struct rb_process_status {
static const rb_data_type_t rb_process_status_type = {
.wrap_struct_name = "Process::Status",
.function = {
+ .dmark = NULL,
.dfree = RUBY_DEFAULT_FREE,
+ .dsize = NULL,
},
- .data = NULL,
- .flags = RUBY_TYPED_FREE_IMMEDIATELY,
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE,
};
static VALUE
rb_process_status_allocate(VALUE klass)
{
- struct rb_process_status *data = NULL;
-
+ struct rb_process_status *data;
return TypedData_Make_Struct(klass, struct rb_process_status, &rb_process_status_type, data);
}
@@ -623,18 +615,25 @@ rb_last_status_get(void)
/*
* call-seq:
- * Process.last_status -> Process::Status or nil
+ * Process.last_status -> Process::Status or nil
+ *
+ * Returns a Process::Status object representing the most recently exited
+ * child process in the current thread, or +nil+ if none:
*
- * Returns the status of the last executed child process in the
- * current thread.
+ * Process.spawn('ruby', '-e', 'exit 13')
+ * Process.wait
+ * Process.last_status # => #<Process::Status: pid 14396 exit 13>
*
- * Process.wait Process.spawn("ruby", "-e", "exit 13")
- * Process.last_status #=> #<Process::Status: pid 4825 exit 13>
+ * Process.spawn('ruby', '-e', 'exit 14')
+ * Process.wait
+ * Process.last_status # => #<Process::Status: pid 4692 exit 14>
*
- * If no child process has ever been executed in the current
- * thread, this returns +nil+.
+ * Process.spawn('ruby', '-e', 'exit 15')
+ * # 'exit 15' has not been reaped by #wait.
+ * Process.last_status # => #<Process::Status: pid 4692 exit 14>
+ * Process.wait
+ * Process.last_status # => #<Process::Status: pid 1380 exit 15>
*
- * Process.last_status #=> nil
*/
static VALUE
proc_s_last_status(VALUE mod)
@@ -646,8 +645,7 @@ VALUE
rb_process_status_new(rb_pid_t pid, int status, int error)
{
VALUE last_status = rb_process_status_allocate(rb_cProcessStatus);
-
- struct rb_process_status *data = RTYPEDDATA_DATA(last_status);
+ struct rb_process_status *data = RTYPEDDATA_GET_DATA(last_status);
data->pid = pid;
data->status = status;
data->error = error;
@@ -660,7 +658,8 @@ static VALUE
process_status_dump(VALUE status)
{
VALUE dump = rb_class_new_instance(0, 0, rb_cObject);
- struct rb_process_status *data = RTYPEDDATA_DATA(status);
+ struct rb_process_status *data;
+ TypedData_Get_Struct(status, struct rb_process_status, &rb_process_status_type, data);
if (data->pid) {
rb_ivar_set(dump, id_status, INT2NUM(data->status));
rb_ivar_set(dump, id_pid, PIDT2NUM(data->pid));
@@ -685,36 +684,42 @@ rb_last_status_set(int status, rb_pid_t pid)
GET_THREAD()->last_status = rb_process_status_new(pid, status, 0);
}
+static void
+last_status_clear(rb_thread_t *th)
+{
+ th->last_status = Qnil;
+}
+
void
rb_last_status_clear(void)
{
- GET_THREAD()->last_status = Qnil;
+ last_status_clear(GET_THREAD());
}
static rb_pid_t
-pst_pid(VALUE pst)
+pst_pid(VALUE status)
{
- struct rb_process_status *data = RTYPEDDATA_DATA(pst);
+ struct rb_process_status *data;
+ TypedData_Get_Struct(status, struct rb_process_status, &rb_process_status_type, data);
return data->pid;
}
static int
-pst_status(VALUE pst)
+pst_status(VALUE status)
{
- struct rb_process_status *data = RTYPEDDATA_DATA(pst);
+ struct rb_process_status *data;
+ TypedData_Get_Struct(status, struct rb_process_status, &rb_process_status_type, data);
return data->status;
}
/*
* call-seq:
- * stat.to_i -> integer
+ * to_i -> integer
*
- * Returns the bits in _stat_ as an Integer. Poking
- * around in these bits is platform dependent.
+ * Returns the system-dependent integer status of +self+:
*
- * fork { exit 0xab } #=> 26566
- * Process.wait #=> 26566
- * sprintf('%04x', $?.to_i) #=> "ab00"
+ * `cat /nop`
+ * $?.to_i # => 256
*/
static VALUE
@@ -728,13 +733,13 @@ pst_to_i(VALUE self)
/*
* call-seq:
- * stat.pid -> integer
+ * pid -> integer
+ *
+ * Returns the process ID of the process:
*
- * Returns the process ID that this status object represents.
+ * system("false")
+ * $?.pid # => 1247002
*
- * fork { exit } #=> 26569
- * Process.wait #=> 26569
- * $?.pid #=> 26569
*/
static VALUE
@@ -790,12 +795,13 @@ pst_message_status(VALUE str, int status)
/*
* call-seq:
- * stat.to_s -> string
+ * to_s -> string
*
- * Show pid and exit status as a string.
+ * Returns a string representation of +self+:
+ *
+ * `cat /nop`
+ * $?.to_s # => "pid 1262141 exit 1"
*
- * system("false")
- * p $?.to_s #=> "pid 12766 exit 1"
*
*/
@@ -817,12 +823,12 @@ pst_to_s(VALUE st)
/*
* call-seq:
- * stat.inspect -> string
+ * inspect -> string
*
- * Override the inspection method.
+ * Returns a string representation of +self+:
*
* system("false")
- * p $?.inspect #=> "#<Process::Status: pid 12861 exit 1>"
+ * $?.inspect # => "#<Process::Status: pid 1303494 exit 1>"
*
*/
@@ -848,10 +854,15 @@ pst_inspect(VALUE st)
/*
* call-seq:
- * stat == other -> true or false
+ * stat == other -> true or false
+ *
+ * Returns whether the value of #to_i == +other+:
+ *
+ * `cat /nop`
+ * stat = $? # => #<Process::Status: pid 1170366 exit 1>
+ * sprintf('%x', stat.to_i) # => "100"
+ * stat == 0x100 # => true
*
- * Returns +true+ if the integer value of _stat_
- * equals <em>other</em>.
*/
static VALUE
@@ -864,20 +875,53 @@ pst_equal(VALUE st1, VALUE st2)
/*
* call-seq:
- * stat & num -> integer
+ * stat & mask -> integer
+ *
+ * This method is deprecated as #to_i value is system-specific; use
+ * predicate methods like #exited? or #stopped?, or getters like #exitstatus
+ * or #stopsig.
+ *
+ * Returns the logical AND of the value of #to_i with +mask+:
*
- * Logical AND of the bits in _stat_ with <em>num</em>.
+ * `cat /nop`
+ * stat = $? # => #<Process::Status: pid 1155508 exit 1>
+ * sprintf('%x', stat.to_i) # => "100"
+ * stat & 0x00 # => 0
*
- * fork { exit 0x37 }
- * Process.wait
- * sprintf('%04x', $?.to_i) #=> "3700"
- * sprintf('%04x', $? & 0x1e00) #=> "1600"
+ * ArgumentError is raised if +mask+ is negative.
*/
static VALUE
pst_bitand(VALUE st1, VALUE st2)
{
- int status = PST2INT(st1) & NUM2INT(st2);
+ int status = PST2INT(st1);
+ int mask = NUM2INT(st2);
+
+ if (mask < 0) {
+ rb_raise(rb_eArgError, "negative mask value: %d", mask);
+ }
+#define WARN_SUGGEST(suggest) \
+ rb_warn_deprecated_to_remove_at(3.5, "Process::Status#&", suggest)
+
+ switch (mask) {
+ case 0x80:
+ WARN_SUGGEST("Process::Status#coredump?");
+ break;
+ case 0x7f:
+ WARN_SUGGEST("Process::Status#signaled? or Process::Status#termsig");
+ break;
+ case 0xff:
+ WARN_SUGGEST("Process::Status#exited?, Process::Status#stopped? or Process::Status#coredump?");
+ break;
+ case 0xff00:
+ WARN_SUGGEST("Process::Status#exitstatus or Process::Status#stopsig");
+ break;
+ default:
+ WARN_SUGGEST("other Process::Status predicates");
+ break;
+ }
+#undef WARN_SUGGEST
+ status &= mask;
return INT2NUM(status);
}
@@ -885,20 +929,48 @@ pst_bitand(VALUE st1, VALUE st2)
/*
* call-seq:
- * stat >> num -> integer
+ * stat >> places -> integer
*
- * Shift the bits in _stat_ right <em>num</em> places.
+ * This method is deprecated as #to_i value is system-specific; use
+ * predicate methods like #exited? or #stopped?, or getters like #exitstatus
+ * or #stopsig.
*
- * fork { exit 99 } #=> 26563
- * Process.wait #=> 26563
- * $?.to_i #=> 25344
- * $? >> 8 #=> 99
+ * Returns the value of #to_i, shifted +places+ to the right:
+ *
+ * `cat /nop`
+ * stat = $? # => #<Process::Status: pid 1155508 exit 1>
+ * stat.to_i # => 256
+ * stat >> 1 # => 128
+ * stat >> 2 # => 64
+ *
+ * ArgumentError is raised if +places+ is negative.
*/
static VALUE
pst_rshift(VALUE st1, VALUE st2)
{
- int status = PST2INT(st1) >> NUM2INT(st2);
+ int status = PST2INT(st1);
+ int places = NUM2INT(st2);
+
+ if (places < 0) {
+ rb_raise(rb_eArgError, "negative shift value: %d", places);
+ }
+#define WARN_SUGGEST(suggest) \
+ rb_warn_deprecated_to_remove_at(3.5, "Process::Status#>>", suggest)
+
+ switch (places) {
+ case 7:
+ WARN_SUGGEST("Process::Status#coredump?");
+ break;
+ case 8:
+ WARN_SUGGEST("Process::Status#exitstatus or Process::Status#stopsig");
+ break;
+ default:
+ WARN_SUGGEST("other Process::Status attributes");
+ break;
+ }
+#undef WARN_SUGGEST
+ status >>= places;
return INT2NUM(status);
}
@@ -906,11 +978,11 @@ pst_rshift(VALUE st1, VALUE st2)
/*
* call-seq:
- * stat.stopped? -> true or false
+ * stopped? -> true or false
*
- * Returns +true+ if this process is stopped. This is only returned
- * if the corresponding #wait call had the Process::WUNTRACED flag
- * set.
+ * Returns +true+ if this process is stopped,
+ * and if the corresponding #wait call had the Process::WUNTRACED flag set,
+ * +false+ otherwise.
*/
static VALUE
@@ -924,10 +996,10 @@ pst_wifstopped(VALUE st)
/*
* call-seq:
- * stat.stopsig -> integer or nil
+ * stopsig -> integer or nil
*
- * Returns the number of the signal that caused _stat_ to stop
- * (or +nil+ if self is not stopped).
+ * Returns the number of the signal that caused the process to stop,
+ * or +nil+ if the process is not stopped.
*/
static VALUE
@@ -943,10 +1015,10 @@ pst_wstopsig(VALUE st)
/*
* call-seq:
- * stat.signaled? -> true or false
+ * signaled? -> true or false
*
- * Returns +true+ if _stat_ terminated because of
- * an uncaught signal.
+ * Returns +true+ if the process terminated because of an uncaught signal,
+ * +false+ otherwise.
*/
static VALUE
@@ -960,11 +1032,10 @@ pst_wifsignaled(VALUE st)
/*
* call-seq:
- * stat.termsig -> integer or nil
+ * termsig -> integer or nil
*
- * Returns the number of the signal that caused _stat_ to
- * terminate (or +nil+ if self was not terminated by an
- * uncaught signal).
+ * Returns the number of the signal that caused the process to terminate
+ * or +nil+ if the process was not terminated by an uncaught signal.
*/
static VALUE
@@ -980,11 +1051,11 @@ pst_wtermsig(VALUE st)
/*
* call-seq:
- * stat.exited? -> true or false
+ * exited? -> true or false
*
- * Returns +true+ if _stat_ exited normally (for
- * example using an <code>exit()</code> call or finishing the
- * program).
+ * Returns +true+ if the process exited normally
+ * (for example using an <code>exit()</code> call or finishing the
+ * program), +false+ if not.
*/
static VALUE
@@ -998,20 +1069,15 @@ pst_wifexited(VALUE st)
/*
* call-seq:
- * stat.exitstatus -> integer or nil
+ * exitstatus -> integer or nil
*
- * Returns the least significant eight bits of the return code of
- * _stat_. Only available if #exited? is +true+.
+ * Returns the least significant eight bits of the return code
+ * of the process if it has exited;
+ * +nil+ otherwise:
*
- * fork { } #=> 26572
- * Process.wait #=> 26572
- * $?.exited? #=> true
- * $?.exitstatus #=> 0
+ * `exit 99`
+ * $?.exitstatus # => 99
*
- * fork { exit 99 } #=> 26573
- * Process.wait #=> 26573
- * $?.exited? #=> true
- * $?.exitstatus #=> 99
*/
static VALUE
@@ -1027,10 +1093,14 @@ pst_wexitstatus(VALUE st)
/*
* call-seq:
- * stat.success? -> true, false or nil
+ * success? -> true, false, or nil
+ *
+ * Returns:
+ *
+ * - +true+ if the process has completed successfully and exited.
+ * - +false+ if the process has completed unsuccessfully and exited.
+ * - +nil+ if the process has not exited.
*
- * Returns +true+ if _stat_ is successful, +false+ if not.
- * Returns +nil+ if #exited? is not +true+.
*/
static VALUE
@@ -1046,10 +1116,12 @@ pst_success_p(VALUE st)
/*
* call-seq:
- * stat.coredump? -> true or false
+ * coredump? -> true or false
*
- * Returns +true+ if _stat_ generated a coredump
- * when it terminated. Not available on all platforms.
+ * Returns +true+ if the process generated a coredump
+ * when it terminated, +false+ if not.
+ *
+ * Not available on all platforms.
*/
static VALUE
@@ -1146,49 +1218,35 @@ rb_process_status_wait(rb_pid_t pid, int flags)
/*
* call-seq:
- * Process::Status.wait(pid=-1, flags=0) -> Process::Status
+ * Process::Status.wait(pid = -1, flags = 0) -> Process::Status
*
- * Waits for a child process to exit and returns a Process::Status object
- * containing information on that process. Which child it waits on
- * depends on the value of _pid_:
+ * Like Process.wait, but returns a Process::Status object
+ * (instead of an integer pid or nil);
+ * see Process.wait for the values of +pid+ and +flags+.
*
- * > 0:: Waits for the child whose process ID equals _pid_.
+ * If there are child processes,
+ * waits for a child process to exit and returns a Process::Status object
+ * containing information on that process;
+ * sets thread-local variable <tt>$?</tt>:
*
- * 0:: Waits for any child whose process group ID equals that of the
- * calling process.
+ * Process.spawn('cat /nop') # => 1155880
+ * Process::Status.wait # => #<Process::Status: pid 1155880 exit 1>
+ * $? # => #<Process::Status: pid 1155508 exit 1>
*
- * -1:: Waits for any child process (the default if no _pid_ is
- * given).
+ * If there is no child process,
+ * returns an "empty" Process::Status object
+ * that does not represent an actual process;
+ * does not set thread-local variable <tt>$?</tt>:
*
- * < -1:: Waits for any child whose process group ID equals the absolute
- * value of _pid_.
+ * Process::Status.wait # => #<Process::Status: pid -1 exit 0>
+ * $? # => #<Process::Status: pid 1155508 exit 1> # Unchanged.
*
- * The _flags_ argument may be a logical or of the flag values
- * Process::WNOHANG (do not block if no child available)
- * or Process::WUNTRACED (return stopped children that
- * haven't been reported). Not all flags are available on all
- * platforms, but a flag value of zero will work on all platforms.
+ * May invoke the scheduler hook Fiber::Scheduler#process_wait.
*
- * Returns +nil+ if there are no child processes.
* Not available on all platforms.
- *
- * May invoke the scheduler hook _process_wait_.
- *
- * fork { exit 99 } #=> 27429
- * Process::Status.wait #=> pid 27429 exit 99
- * $? #=> nil
- *
- * pid = fork { sleep 3 } #=> 27440
- * Time.now #=> 2008-03-08 19:56:16 +0900
- * Process::Status.wait(pid, Process::WNOHANG) #=> nil
- * Time.now #=> 2008-03-08 19:56:16 +0900
- * Process::Status.wait(pid, 0) #=> pid 27440 exit 99
- * Time.now #=> 2008-03-08 19:56:19 +0900
- *
- * This is an EXPERIMENTAL FEATURE.
*/
-VALUE
+static VALUE
rb_process_status_waitv(int argc, VALUE *argv, VALUE _)
{
rb_check_arity(argc, 0, 2);
@@ -1213,7 +1271,7 @@ rb_waitpid(rb_pid_t pid, int *st, int flags)
VALUE status = rb_process_status_wait(pid, flags);
if (NIL_P(status)) return 0;
- struct rb_process_status *data = RTYPEDDATA_DATA(status);
+ struct rb_process_status *data = rb_check_typeddata(status, &rb_process_status_type);
pid = data->pid;
if (st) *st = data->status;
@@ -1273,69 +1331,155 @@ proc_wait(int argc, VALUE *argv)
/*
* call-seq:
- * Process.wait() -> integer
- * Process.wait(pid=-1, flags=0) -> integer
- * Process.waitpid(pid=-1, flags=0) -> integer
- *
- * Waits for a child process to exit, returns its process id, and
- * sets <code>$?</code> to a Process::Status object
- * containing information on that process. Which child it waits on
- * depends on the value of _pid_:
- *
- * > 0:: Waits for the child whose process ID equals _pid_.
- *
- * 0:: Waits for any child whose process group ID equals that of the
- * calling process.
- *
- * -1:: Waits for any child process (the default if no _pid_ is
- * given).
- *
- * < -1:: Waits for any child whose process group ID equals the absolute
- * value of _pid_.
- *
- * The _flags_ argument may be a logical or of the flag values
- * Process::WNOHANG (do not block if no child available)
- * or Process::WUNTRACED (return stopped children that
- * haven't been reported). Not all flags are available on all
- * platforms, but a flag value of zero will work on all platforms.
- *
- * Calling this method raises a SystemCallError if there are no child
- * processes. Not available on all platforms.
- *
- * include Process
- * fork { exit 99 } #=> 27429
- * wait #=> 27429
- * $?.exitstatus #=> 99
- *
- * pid = fork { sleep 3 } #=> 27440
- * Time.now #=> 2008-03-08 19:56:16 +0900
- * waitpid(pid, Process::WNOHANG) #=> nil
- * Time.now #=> 2008-03-08 19:56:16 +0900
- * waitpid(pid, 0) #=> 27440
- * Time.now #=> 2008-03-08 19:56:19 +0900
+ * Process.wait(pid = -1, flags = 0) -> integer
+ *
+ * Waits for a suitable child process to exit, returns its process ID,
+ * and sets <tt>$?</tt> to a Process::Status object
+ * containing information on that process.
+ * Which child it waits for depends on the value of the given +pid+:
+ *
+ * - Positive integer: Waits for the child process whose process ID is +pid+:
+ *
+ * pid0 = Process.spawn('ruby', '-e', 'exit 13') # => 230866
+ * pid1 = Process.spawn('ruby', '-e', 'exit 14') # => 230891
+ * Process.wait(pid0) # => 230866
+ * $? # => #<Process::Status: pid 230866 exit 13>
+ * Process.wait(pid1) # => 230891
+ * $? # => #<Process::Status: pid 230891 exit 14>
+ * Process.wait(pid0) # Raises Errno::ECHILD
+ *
+ * - <tt>0</tt>: Waits for any child process whose group ID
+ * is the same as that of the current process:
+ *
+ * parent_pgpid = Process.getpgid(Process.pid)
+ * puts "Parent process group ID is #{parent_pgpid}."
+ * child0_pid = fork do
+ * puts "Child 0 pid is #{Process.pid}"
+ * child0_pgid = Process.getpgid(Process.pid)
+ * puts "Child 0 process group ID is #{child0_pgid} (same as parent's)."
+ * end
+ * child1_pid = fork do
+ * puts "Child 1 pid is #{Process.pid}"
+ * Process.setpgid(0, Process.pid)
+ * child1_pgid = Process.getpgid(Process.pid)
+ * puts "Child 1 process group ID is #{child1_pgid} (different from parent's)."
+ * end
+ * retrieved_pid = Process.wait(0)
+ * puts "Process.wait(0) returned pid #{retrieved_pid}, which is child 0 pid."
+ * begin
+ * Process.wait(0)
+ * rescue Errno::ECHILD => x
+ * puts "Raised #{x.class}, because child 1 process group ID differs from parent process group ID."
+ * end
+ *
+ * Output:
+ *
+ * Parent process group ID is 225764.
+ * Child 0 pid is 225788
+ * Child 0 process group ID is 225764 (same as parent's).
+ * Child 1 pid is 225789
+ * Child 1 process group ID is 225789 (different from parent's).
+ * Process.wait(0) returned pid 225788, which is child 0 pid.
+ * Raised Errno::ECHILD, because child 1 process group ID differs from parent process group ID.
+ *
+ * - <tt>-1</tt> (default): Waits for any child process:
+ *
+ * parent_pgpid = Process.getpgid(Process.pid)
+ * puts "Parent process group ID is #{parent_pgpid}."
+ * child0_pid = fork do
+ * puts "Child 0 pid is #{Process.pid}"
+ * child0_pgid = Process.getpgid(Process.pid)
+ * puts "Child 0 process group ID is #{child0_pgid} (same as parent's)."
+ * end
+ * child1_pid = fork do
+ * puts "Child 1 pid is #{Process.pid}"
+ * Process.setpgid(0, Process.pid)
+ * child1_pgid = Process.getpgid(Process.pid)
+ * puts "Child 1 process group ID is #{child1_pgid} (different from parent's)."
+ * sleep 3 # To force child 1 to exit later than child 0 exit.
+ * end
+ * child_pids = [child0_pid, child1_pid]
+ * retrieved_pid = Process.wait(-1)
+ * puts child_pids.include?(retrieved_pid)
+ * retrieved_pid = Process.wait(-1)
+ * puts child_pids.include?(retrieved_pid)
+ *
+ * Output:
+ *
+ * Parent process group ID is 228736.
+ * Child 0 pid is 228758
+ * Child 0 process group ID is 228736 (same as parent's).
+ * Child 1 pid is 228759
+ * Child 1 process group ID is 228759 (different from parent's).
+ * true
+ * true
+ *
+ * - Less than <tt>-1</tt>: Waits for any child whose process group ID is <tt>-pid</tt>:
+ *
+ * parent_pgpid = Process.getpgid(Process.pid)
+ * puts "Parent process group ID is #{parent_pgpid}."
+ * child0_pid = fork do
+ * puts "Child 0 pid is #{Process.pid}"
+ * child0_pgid = Process.getpgid(Process.pid)
+ * puts "Child 0 process group ID is #{child0_pgid} (same as parent's)."
+ * end
+ * child1_pid = fork do
+ * puts "Child 1 pid is #{Process.pid}"
+ * Process.setpgid(0, Process.pid)
+ * child1_pgid = Process.getpgid(Process.pid)
+ * puts "Child 1 process group ID is #{child1_pgid} (different from parent's)."
+ * end
+ * sleep 1
+ * retrieved_pid = Process.wait(-child1_pid)
+ * puts "Process.wait(-child1_pid) returned pid #{retrieved_pid}, which is child 1 pid."
+ * begin
+ * Process.wait(-child1_pid)
+ * rescue Errno::ECHILD => x
+ * puts "Raised #{x.class}, because there's no longer a child with process group id #{child1_pid}."
+ * end
+ *
+ * Output:
+ *
+ * Parent process group ID is 230083.
+ * Child 0 pid is 230108
+ * Child 0 process group ID is 230083 (same as parent's).
+ * Child 1 pid is 230109
+ * Child 1 process group ID is 230109 (different from parent's).
+ * Process.wait(-child1_pid) returned pid 230109, which is child 1 pid.
+ * Raised Errno::ECHILD, because there's no longer a child with process group id 230109.
+ *
+ * Argument +flags+ should be given as one of the following constants,
+ * or as the logical OR of both:
+ *
+ * - Process::WNOHANG: Does not block if no child process is available.
+ * - Process::WUNTRACED: May return a stopped child process, even if not yet reported.
+ *
+ * Not all flags are available on all platforms.
+ *
+ * Raises Errno::ECHILD if there is no suitable child process.
+ *
+ * Not available on all platforms.
+ *
+ * Process.waitpid is an alias for Process.wait.
*/
-
static VALUE
proc_m_wait(int c, VALUE *v, VALUE _)
{
return proc_wait(c, v);
}
-
/*
* call-seq:
- * Process.wait2(pid=-1, flags=0) -> [pid, status]
- * Process.waitpid2(pid=-1, flags=0) -> [pid, status]
- *
- * Waits for a child process to exit (see Process::waitpid for exact
- * semantics) and returns an array containing the process id and the
- * exit status (a Process::Status object) of that
- * child. Raises a SystemCallError if there are no child processes.
- *
- * Process.fork { exit 99 } #=> 27437
- * pid, status = Process.wait2
- * pid #=> 27437
- * status.exitstatus #=> 99
+ * Process.wait2(pid = -1, flags = 0) -> [pid, status]
+ *
+ * Like Process.waitpid, but returns an array
+ * containing the child process +pid+ and Process::Status +status+:
+ *
+ * pid = Process.spawn('ruby', '-e', 'exit 13') # => 309581
+ * Process.wait2(pid)
+ * # => [309581, #<Process::Status: pid 309581 exit 13>]
+ *
+ * Process.waitpid2 is an alias for Process.wait2.
*/
static VALUE
@@ -1349,22 +1493,17 @@ proc_wait2(int argc, VALUE *argv, VALUE _)
/*
* call-seq:
- * Process.waitall -> [ [pid1,status1], ...]
+ * Process.waitall -> array
*
- * Waits for all children, returning an array of
- * _pid_/_status_ pairs (where _status_ is a
- * Process::Status object).
+ * Waits for all children, returns an array of 2-element arrays;
+ * each subarray contains the integer pid and Process::Status status
+ * for one of the reaped child processes:
*
- * fork { sleep 0.2; exit 2 } #=> 27432
- * fork { sleep 0.1; exit 1 } #=> 27433
- * fork { exit 0 } #=> 27434
- * p Process.waitall
+ * pid0 = Process.spawn('ruby', '-e', 'exit 13') # => 325470
+ * pid1 = Process.spawn('ruby', '-e', 'exit 14') # => 325495
+ * Process.waitall
+ * # => [[325470, #<Process::Status: pid 325470 exit 13>], [325495, #<Process::Status: pid 325495 exit 14>]]
*
- * <em>produces</em>:
- *
- * [[30982, #<Process::Status: pid 30982 exit 0>],
- * [30979, #<Process::Status: pid 30979 exit 1>],
- * [30976, #<Process::Status: pid 30976 exit 2>]]
*/
static VALUE
@@ -1422,48 +1561,41 @@ rb_detach_process(rb_pid_t pid)
/*
* call-seq:
- * Process.detach(pid) -> thread
+ * Process.detach(pid) -> thread
+ *
+ * Avoids the potential for a child process to become a
+ * {zombie process}[https://en.wikipedia.org/wiki/Zombie_process].
+ * Process.detach prevents this by setting up a separate Ruby thread
+ * whose sole job is to reap the status of the process _pid_ when it terminates.
*
- * Some operating systems retain the status of terminated child
- * processes until the parent collects that status (normally using
- * some variant of <code>wait()</code>). If the parent never collects
- * this status, the child stays around as a <em>zombie</em> process.
- * Process::detach prevents this by setting up a separate Ruby thread
- * whose sole job is to reap the status of the process _pid_ when it
- * terminates. Use #detach only when you do not intend to explicitly
- * wait for the child to terminate.
+ * This method is needed only when the parent process will never wait
+ * for the child process.
*
- * The waiting thread returns the exit status of the detached process
- * when it terminates, so you can use Thread#join to
- * know the result. If specified _pid_ is not a valid child process
- * ID, the thread returns +nil+ immediately.
+ * This example does not reap the second child process;
+ * that process appears as a zombie in the process status (+ps+) output:
*
- * The waiting thread has #pid method which returns the pid.
+ * pid = Process.spawn('ruby', '-e', 'exit 13') # => 312691
+ * sleep(1)
+ * # Find zombies.
+ * system("ps -ho pid,state -p #{pid}")
*
- * In this first example, we don't reap the first child process, so
- * it appears as a zombie in the process status display.
+ * Output:
*
- * p1 = fork { sleep 0.1 }
- * p2 = fork { sleep 0.2 }
- * Process.waitpid(p2)
- * sleep 2
- * system("ps -ho pid,state -p #{p1}")
+ * 312716 Z
*
- * <em>produces:</em>
+ * This example also does not reap the second child process,
+ * but it does detach the process so that it does not become a zombie:
*
- * 27389 Z
+ * pid = Process.spawn('ruby', '-e', 'exit 13') # => 313213
+ * thread = Process.detach(pid)
+ * sleep(1)
+ * # => #<Process::Waiter:0x00007f038f48b838 run>
+ * system("ps -ho pid,state -p #{pid}") # Finds no zombies.
*
- * In the next example, Process::detach is used to reap
- * the child automatically.
+ * The waiting thread can return the pid of the detached child process:
*
- * p1 = fork { sleep 0.1 }
- * p2 = fork { sleep 0.2 }
- * Process.detach(p1)
- * Process.waitpid(p2)
- * sleep 2
- * system("ps -ho pid,state -p #{p1}")
+ * thread.join.pid # => 313262
*
- * <em>(produces no output)</em>
*/
static VALUE
@@ -1533,26 +1665,13 @@ before_exec(void)
before_exec_async_signal_safe();
}
-/* This function should be async-signal-safe. Actually it is. */
static void
-after_exec_async_signal_safe(void)
-{
-}
-
-static void
-after_exec_non_async_signal_safe(void)
+after_exec(void)
{
rb_thread_reset_timer_thread();
rb_thread_start_timer_thread();
}
-static void
-after_exec(void)
-{
- after_exec_async_signal_safe();
- after_exec_non_async_signal_safe();
-}
-
#if defined HAVE_WORKING_FORK || defined HAVE_DAEMON
static void
before_fork_ruby(void)
@@ -1563,12 +1682,15 @@ before_fork_ruby(void)
static void
after_fork_ruby(rb_pid_t pid)
{
- rb_threadptr_pending_interrupt_clear(GET_THREAD());
if (pid == 0) {
+ // child
clear_pid_cache();
rb_thread_atfork();
}
- after_exec();
+ else {
+ // parent
+ after_exec();
+ }
}
#endif
@@ -1716,7 +1838,7 @@ memsize_exec_arg(const void *ptr)
static const rb_data_type_t exec_arg_data_type = {
"exec_arg",
{mark_exec_arg, RUBY_TYPED_DEFAULT_FREE, memsize_exec_arg},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_EMBEDDABLE
};
#ifdef _WIN32
@@ -2359,12 +2481,12 @@ rb_check_argv(int argc, VALUE *argv)
}
prog = RARRAY_AREF(tmp, 0);
argv[0] = RARRAY_AREF(tmp, 1);
- SafeStringValue(prog);
+ StringValue(prog);
StringValueCStr(prog);
prog = rb_str_new_frozen(prog);
}
for (i = 0; i < argc; i++) {
- SafeStringValue(argv[i]);
+ StringValue(argv[i]);
argv[i] = rb_str_new_frozen(argv[i]);
StringValueCStr(argv[i]);
}
@@ -2660,6 +2782,7 @@ rb_execarg_setenv(VALUE execarg_obj, VALUE env)
struct rb_execarg *eargp = rb_execarg_get(execarg_obj);
env = !NIL_P(env) ? rb_check_exec_env(env, &eargp->path_env) : Qfalse;
eargp->env_modification = env;
+ RB_GC_GUARD(execarg_obj);
}
static int
@@ -2857,6 +2980,7 @@ execarg_parent_end(VALUE execarg_obj)
}
errno = err;
+ RB_GC_GUARD(execarg_obj);
return execarg_obj;
}
@@ -2922,77 +3046,91 @@ NORETURN(static VALUE f_exec(int c, const VALUE *a, VALUE _));
/*
* call-seq:
- * exec([env,] command... [,options])
+ * exec([env, ] command_line, options = {})
+ * exec([env, ] exe_path, *args, options = {})
+ *
+ * Replaces the current process by doing one of the following:
+ *
+ * - Passing string +command_line+ to the shell.
+ * - Invoking the executable at +exe_path+.
+ *
+ * This method has potential security vulnerabilities if called with untrusted input;
+ * see {Command Injection}[rdoc-ref:command_injection.rdoc].
+ *
+ * The new process is created using the
+ * {exec system call}[https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/execve.html];
+ * it may inherit some of its environment from the calling program
+ * (possibly including open file descriptors).
+ *
+ * Argument +env+, if given, is a hash that affects +ENV+ for the new process;
+ * see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
+ *
+ * Argument +options+ is a hash of options for the new process;
+ * see {Execution Options}[rdoc-ref:Process@Execution+Options].
+ *
+ * The first required argument is one of the following:
+ *
+ * - +command_line+ if it is a string,
+ * and if it begins with a shell reserved word or special built-in,
+ * or if it contains one or more meta characters.
+ * - +exe_path+ otherwise.
+ *
+ * <b>Argument +command_line+</b>
+ *
+ * \String argument +command_line+ is a command line to be passed to a shell;
+ * it must begin with a shell reserved word, begin with a special built-in,
+ * or contain meta characters:
*
- * Replaces the current process by running the given external _command_, which
- * can take one of the following forms:
+ * exec('if true; then echo "Foo"; fi') # Shell reserved word.
+ * exec('exit') # Built-in.
+ * exec('date > date.tmp') # Contains meta character.
*
- * [<code>exec(commandline)</code>]
- * command line string which is passed to the standard shell
- * [<code>exec(cmdname, arg1, ...)</code>]
- * command name and one or more arguments (no shell)
- * [<code>exec([cmdname, argv0], arg1, ...)</code>]
- * command name, +argv[0]+ and zero or more arguments (no shell)
+ * The command line may also contain arguments and options for the command:
*
- * In the first form, the string is taken as a command line that is subject to
- * shell expansion before being executed.
+ * exec('echo "Foo"')
*
- * The standard shell always means <code>"/bin/sh"</code> on Unix-like systems,
- * otherwise, <code>ENV["RUBYSHELL"]</code> or <code>ENV["COMSPEC"]</code> on
- * Windows and similar. The command is passed as an argument to the
- * <code>"-c"</code> switch to the shell, except in the case of +COMSPEC+.
+ * Output:
*
- * If the string from the first form (<code>exec("command")</code>) follows
- * these simple rules:
+ * Foo
*
- * * no meta characters,
- * * not starting with shell reserved word or special built-in,
+ * See {Execution Shell}[rdoc-ref:Process@Execution+Shell] for details about the shell.
*
- * Ruby invokes the command directly without shell.
+ * Raises an exception if the new process could not execute.
*
- * You can force shell invocation by adding ";" to the string (because ";" is
- * a meta character).
+ * <b>Argument +exe_path+</b>
*
- * Note that this behavior is observable by pid obtained
- * (return value of spawn() and IO#pid for IO.popen) is the pid of the invoked
- * command, not shell.
+ * Argument +exe_path+ is one of the following:
*
- * In the second form (<code>exec("command1", "arg1", ...)</code>), the first
- * is taken as a command name and the rest are passed as parameters to command
- * with no shell expansion.
+ * - The string path to an executable to be called.
+ * - A 2-element array containing the path to an executable
+ * and the string to be used as the name of the executing process.
*
- * In the third form (<code>exec(["command", "argv0"], "arg1", ...)</code>),
- * starting a two-element array at the beginning of the command, the first
- * element is the command to be executed, and the second argument is used as
- * the <code>argv[0]</code> value, which may show up in process listings.
+ * Example:
*
- * In order to execute the command, one of the <code>exec(2)</code> system
- * calls are used, so the running command may inherit some of the environment
- * of the original program (including open file descriptors).
+ * exec('/usr/bin/date')
*
- * This behavior is modified by the given +env+ and +options+ parameters. See
- * ::spawn for details.
+ * Output:
*
- * If the command fails to execute (typically Errno::ENOENT when
- * it was not found) a SystemCallError exception is raised.
+ * Sat Aug 26 09:38:00 AM CDT 2023
*
- * This method modifies process attributes according to given +options+ before
- * <code>exec(2)</code> system call. See ::spawn for more details about the
- * given +options+.
+ * Ruby invokes the executable directly.
+ * This form does not use the shell;
+ * see {Arguments args}[rdoc-ref:Process@Arguments+args] for caveats.
*
- * The modified attributes may be retained when <code>exec(2)</code> system
- * call fails.
+ * exec('doesnt_exist') # Raises Errno::ENOENT
*
- * For example, hard resource limits are not restorable.
+ * If one or more +args+ is given, each is an argument or option
+ * to be passed to the executable:
*
- * Consider to create a child process using ::spawn or Kernel#system if this
- * is not acceptable.
+ * exec('echo', 'C*')
+ * exec('echo', 'hello', 'world')
*
- * exec "echo *" # echoes list of files in current directory
- * # never get here
+ * Output:
*
- * exec "echo", "*" # echoes an asterisk
- * # never get here
+ * C*
+ * hello world
+ *
+ * Raises an exception if the new process could not execute.
*/
static VALUE
@@ -3002,9 +3140,13 @@ f_exec(int c, const VALUE *a, VALUE _)
UNREACHABLE_RETURN(Qnil);
}
-#define ERRMSG(str) do { if (errmsg && 0 < errmsg_buflen) strlcpy(errmsg, (str), errmsg_buflen); } while (0)
-#define ERRMSG1(str, a) do { if (errmsg && 0 < errmsg_buflen) snprintf(errmsg, errmsg_buflen, (str), (a)); } while (0)
-#define ERRMSG2(str, a, b) do { if (errmsg && 0 < errmsg_buflen) snprintf(errmsg, errmsg_buflen, (str), (a), (b)); } while (0)
+#define ERRMSG(str) \
+ ((errmsg && 0 < errmsg_buflen) ? \
+ (void)strlcpy(errmsg, (str), errmsg_buflen) : (void)0)
+
+#define ERRMSG_FMT(...) \
+ ((errmsg && 0 < errmsg_buflen) ? \
+ (void)snprintf(errmsg, errmsg_buflen, __VA_ARGS__) : (void)0)
static int fd_get_cloexec(int fd, char *errmsg, size_t errmsg_buflen);
static int fd_set_cloexec(int fd, char *errmsg, size_t errmsg_buflen);
@@ -3217,6 +3359,13 @@ run_exec_dup2(VALUE ary, VALUE tmpbuf, struct rb_execarg *sargp, char *errmsg, s
ERRMSG("dup");
goto fail;
}
+ // without this, kqueue timer_th.event_fd fails with a reserved FD did not have close-on-exec
+ // in #assert_close_on_exec because the FD_CLOEXEC is not dup'd by default
+ if (fd_get_cloexec(pairs[i].oldfd, errmsg, errmsg_buflen)) {
+ if (fd_set_cloexec(extra_fd, errmsg, errmsg_buflen)) {
+ goto fail;
+ }
+ }
rb_update_max_fd(extra_fd);
}
else {
@@ -4077,16 +4226,19 @@ rb_fork_ruby2(struct rb_process_status *status)
while (1) {
prefork();
- disable_child_handler_before_fork(&old);
+
before_fork_ruby();
- pid = rb_fork();
- err = errno;
- if (status) {
- status->pid = pid;
- status->error = err;
+ disable_child_handler_before_fork(&old);
+ {
+ pid = rb_fork();
+ err = errno;
+ if (status) {
+ status->pid = pid;
+ status->error = err;
+ }
}
- after_fork_ruby(pid);
disable_child_handler_fork_parent(&old); /* yes, bad name */
+ after_fork_ruby(pid);
if (pid >= 0) { /* fork succeed */
return pid;
@@ -4167,27 +4319,62 @@ rb_proc__fork(VALUE _obj)
/*
* call-seq:
- * Kernel.fork [{ block }] -> integer or nil
- * Process.fork [{ block }] -> integer or nil
- *
- * Creates a subprocess. If a block is specified, that block is run
- * in the subprocess, and the subprocess terminates with a status of
- * zero. Otherwise, the +fork+ call returns twice, once in the
- * parent, returning the process ID of the child, and once in the
- * child, returning _nil_. The child process can exit using
- * Kernel.exit! to avoid running any <code>at_exit</code>
- * functions. The parent process should use Process.wait to collect
- * the termination statuses of its children or use Process.detach to
- * register disinterest in their status; otherwise, the operating
- * system may accumulate zombie processes.
- *
- * The thread calling fork is the only thread in the created child process.
- * fork doesn't copy other threads.
- *
- * If fork is not usable, Process.respond_to?(:fork) returns false.
- *
- * Note that fork(2) is not available on some platforms like Windows and NetBSD 4.
- * Therefore you should use spawn() instead of fork().
+ * Process.fork { ... } -> integer or nil
+ * Process.fork -> integer or nil
+ *
+ * Creates a child process.
+ *
+ * With a block given, runs the block in the child process;
+ * on block exit, the child terminates with a status of zero:
+ *
+ * puts "Before the fork: #{Process.pid}"
+ * fork do
+ * puts "In the child process: #{Process.pid}"
+ * end # => 382141
+ * puts "After the fork: #{Process.pid}"
+ *
+ * Output:
+ *
+ * Before the fork: 420496
+ * After the fork: 420496
+ * In the child process: 420520
+ *
+ * With no block given, the +fork+ call returns twice:
+ *
+ * - Once in the parent process, returning the pid of the child process.
+ * - Once in the child process, returning +nil+.
+ *
+ * Example:
+ *
+ * puts "This is the first line before the fork (pid #{Process.pid})"
+ * puts fork
+ * puts "This is the second line after the fork (pid #{Process.pid})"
+ *
+ * Output:
+ *
+ * This is the first line before the fork (pid 420199)
+ * 420223
+ * This is the second line after the fork (pid 420199)
+ *
+ * This is the second line after the fork (pid 420223)
+ *
+ * In either case, the child process may exit using
+ * Kernel.exit! to avoid the call to Kernel#at_exit.
+ *
+ * To avoid zombie processes, the parent process should call either:
+ *
+ * - Process.wait, to collect the termination statuses of its children.
+ * - Process.detach, to register disinterest in their status.
+ *
+ * The thread calling +fork+ is the only thread in the created child process;
+ * +fork+ doesn't copy other threads.
+ *
+ * Note that method +fork+ is available on some platforms,
+ * but not on others:
+ *
+ * Process.respond_to?(:fork) # => true # Would be false on some.
+ *
+ * If not, you may use ::spawn instead of +fork+.
*/
static VALUE
@@ -4239,13 +4426,18 @@ exit_status_code(VALUE status)
NORETURN(static VALUE rb_f_exit_bang(int argc, VALUE *argv, VALUE obj));
/*
* call-seq:
- * Process.exit!(status=false)
+ * exit!(status = false)
+ * Process.exit!(status = false)
*
- * Exits the process immediately. No exit handlers are
- * run. <em>status</em> is returned to the underlying system as the
- * exit status.
+ * Exits the process immediately; no exit handlers are called.
+ * Returns exit status +status+ to the underlying operating system.
*
* Process.exit!(true)
+ *
+ * Values +true+ and +false+ for argument +status+
+ * indicate, respectively, success and failure;
+ * The meanings of integer values are system-dependent.
+ *
*/
static VALUE
@@ -4296,43 +4488,47 @@ rb_f_exit(int argc, const VALUE *argv)
NORETURN(static VALUE f_exit(int c, const VALUE *a, VALUE _));
/*
* call-seq:
- * exit(status=true)
- * Kernel::exit(status=true)
- * Process::exit(status=true)
- *
- * Initiates the termination of the Ruby script by raising the
- * SystemExit exception. This exception may be caught. The
- * optional parameter is used to return a status code to the invoking
- * environment.
- * +true+ and +FALSE+ of _status_ means success and failure
- * respectively. The interpretation of other integer values are
- * system dependent.
- *
- * begin
- * exit
- * puts "never get here"
- * rescue SystemExit
- * puts "rescued a SystemExit exception"
- * end
- * puts "after begin block"
- *
- * <em>produces:</em>
- *
- * rescued a SystemExit exception
- * after begin block
- *
- * Just prior to termination, Ruby executes any <code>at_exit</code>
- * functions (see Kernel::at_exit) and runs any object finalizers
- * (see ObjectSpace::define_finalizer).
- *
- * at_exit { puts "at_exit function" }
- * ObjectSpace.define_finalizer("string", proc { puts "in finalizer" })
- * exit
- *
- * <em>produces:</em>
- *
- * at_exit function
- * in finalizer
+ * exit(status = true)
+ * Process.exit(status = true)
+ *
+ * Initiates termination of the Ruby script by raising SystemExit;
+ * the exception may be caught.
+ * Returns exit status +status+ to the underlying operating system.
+ *
+ * Values +true+ and +false+ for argument +status+
+ * indicate, respectively, success and failure;
+ * The meanings of integer values are system-dependent.
+ *
+ * Example:
+ *
+ * begin
+ * exit
+ * puts 'Never get here.'
+ * rescue SystemExit
+ * puts 'Rescued a SystemExit exception.'
+ * end
+ * puts 'After begin block.'
+ *
+ * Output:
+ *
+ * Rescued a SystemExit exception.
+ * After begin block.
+ *
+ * Just prior to final termination,
+ * Ruby executes any at-exit procedures (see Kernel::at_exit)
+ * and any object finalizers (see ObjectSpace::define_finalizer).
+ *
+ * Example:
+ *
+ * at_exit { puts 'In at_exit function.' }
+ * ObjectSpace.define_finalizer('string', proc { puts 'In finalizer.' })
+ * exit
+ *
+ * Output:
+ *
+ * In at_exit function.
+ * In finalizer.
+ *
*/
static VALUE
@@ -4371,14 +4567,16 @@ NORETURN(static VALUE f_abort(int c, const VALUE *a, VALUE _));
/*
* call-seq:
- * abort
- * Kernel::abort([msg])
- * Process.abort([msg])
- *
- * Terminate execution immediately, effectively by calling
- * <code>Kernel.exit(false)</code>. If _msg_ is given, it is written
- * to STDERR prior to terminating. Otherwise, if an exception was raised,
- * print its message and backtrace.
+ * abort
+ * Process.abort(msg = nil)
+ *
+ * Terminates execution immediately, effectively by calling
+ * <tt>Kernel.exit(false)</tt>.
+ *
+ * If string argument +msg+ is given,
+ * it is written to STDERR prior to termination;
+ * otherwise, if an exception was raised,
+ * prints its message and backtrace.
*/
static VALUE
@@ -4484,11 +4682,16 @@ static VALUE
do_spawn_process(VALUE arg)
{
struct spawn_args *argp = (struct spawn_args *)arg;
+
rb_execarg_parent_start1(argp->execarg);
- return (VALUE)rb_spawn_process(DATA_PTR(argp->execarg),
+
+ return (VALUE)rb_spawn_process(rb_execarg_get(argp->execarg),
argp->errmsg.ptr, argp->errmsg.buflen);
}
+NOINLINE(static rb_pid_t
+ rb_execarg_spawn(VALUE execarg_obj, char *errmsg, size_t errmsg_buflen));
+
static rb_pid_t
rb_execarg_spawn(VALUE execarg_obj, char *errmsg, size_t errmsg_buflen)
{
@@ -4497,8 +4700,10 @@ rb_execarg_spawn(VALUE execarg_obj, char *errmsg, size_t errmsg_buflen)
args.execarg = execarg_obj;
args.errmsg.ptr = errmsg;
args.errmsg.buflen = errmsg_buflen;
- return (rb_pid_t)rb_ensure(do_spawn_process, (VALUE)&args,
- execarg_parent_end, execarg_obj);
+
+ rb_pid_t r = (rb_pid_t)rb_ensure(do_spawn_process, (VALUE)&args,
+ execarg_parent_end, execarg_obj);
+ return r;
}
static rb_pid_t
@@ -4524,68 +4729,133 @@ rb_spawn(int argc, const VALUE *argv)
/*
* call-seq:
- * system([env,] command... [,options], exception: false) -> true, false or nil
+ * system([env, ] command_line, options = {}, exception: false) -> true, false, or nil
+ * system([env, ] exe_path, *args, options = {}, exception: false) -> true, false, or nil
+ *
+ * Creates a new child process by doing one of the following
+ * in that process:
*
- * Executes _command..._ in a subshell.
- * _command..._ is one of following forms.
+ * - Passing string +command_line+ to the shell.
+ * - Invoking the executable at +exe_path+.
*
* This method has potential security vulnerabilities if called with untrusted input;
* see {Command Injection}[rdoc-ref:command_injection.rdoc].
*
- * [<code>commandline</code>]
- * command line string which is passed to the standard shell
- * [<code>cmdname, arg1, ...</code>]
- * command name and one or more arguments (no shell)
- * [<code>[cmdname, argv0], arg1, ...</code>]
- * command name, <code>argv[0]</code> and zero or more arguments (no shell)
+ * Returns:
+ *
+ * - +true+ if the command exits with status zero.
+ * - +false+ if the exit status is a non-zero integer.
+ * - +nil+ if the command could not execute.
+ *
+ * Raises an exception (instead of returning +false+ or +nil+)
+ * if keyword argument +exception+ is set to +true+.
+ *
+ * Assigns the command's error status to <tt>$?</tt>.
+ *
+ * The new process is created using the
+ * {system system call}[https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/system.html];
+ * it may inherit some of its environment from the calling program
+ * (possibly including open file descriptors).
*
- * system returns +true+ if the command gives zero exit status,
- * +false+ for non zero exit status.
- * Returns +nil+ if command execution fails.
- * An error status is available in <code>$?</code>.
+ * Argument +env+, if given, is a hash that affects +ENV+ for the new process;
+ * see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
*
- * If the <code>exception: true</code> argument is passed, the method
- * raises an exception instead of returning +false+ or +nil+.
+ * Argument +options+ is a hash of options for the new process;
+ * see {Execution Options}[rdoc-ref:Process@Execution+Options].
*
- * The arguments are processed in the same way as
- * for Kernel#spawn.
+ * The first required argument is one of the following:
*
- * The hash arguments, env and options, are same as #exec and #spawn.
- * See Kernel#spawn for details.
+ * - +command_line+ if it is a string,
+ * and if it begins with a shell reserved word or special built-in,
+ * or if it contains one or more meta characters.
+ * - +exe_path+ otherwise.
*
- * system("echo *")
- * system("echo", "*")
+ * <b>Argument +command_line+</b>
*
- * <em>produces:</em>
+ * \String argument +command_line+ is a command line to be passed to a shell;
+ * it must begin with a shell reserved word, begin with a special built-in,
+ * or contain meta characters:
*
- * config.h main.rb
- * *
+ * system('if true; then echo "Foo"; fi') # => true # Shell reserved word.
+ * system('exit') # => true # Built-in.
+ * system('date > /tmp/date.tmp') # => true # Contains meta character.
+ * system('date > /nop/date.tmp') # => false
+ * system('date > /nop/date.tmp', exception: true) # Raises RuntimeError.
*
- * Error handling:
+ * Assigns the command's error status to <tt>$?</tt>:
*
- * system("cat nonexistent.txt")
- * # => false
- * system("catt nonexistent.txt")
- * # => nil
+ * system('exit') # => true # Built-in.
+ * $? # => #<Process::Status: pid 640610 exit 0>
+ * system('date > /nop/date.tmp') # => false
+ * $? # => #<Process::Status: pid 640742 exit 2>
*
- * system("cat nonexistent.txt", exception: true)
- * # RuntimeError (Command failed with exit 1: cat)
- * system("catt nonexistent.txt", exception: true)
- * # Errno::ENOENT (No such file or directory - catt)
+ * The command line may also contain arguments and options for the command:
*
- * See Kernel#exec for the standard shell.
+ * system('echo "Foo"') # => true
+ *
+ * Output:
+ *
+ * Foo
+ *
+ * See {Execution Shell}[rdoc-ref:Process@Execution+Shell] for details about the shell.
+ *
+ * Raises an exception if the new process could not execute.
+ *
+ * <b>Argument +exe_path+</b>
+ *
+ * Argument +exe_path+ is one of the following:
+ *
+ * - The string path to an executable to be called.
+ * - A 2-element array containing the path to an executable
+ * and the string to be used as the name of the executing process.
+ *
+ * Example:
+ *
+ * system('/usr/bin/date') # => true # Path to date on Unix-style system.
+ * system('foo') # => nil # Command failed.
+ *
+ * Output:
+ *
+ * Mon Aug 28 11:43:10 AM CDT 2023
+ *
+ * Assigns the command's error status to <tt>$?</tt>:
+ *
+ * system('/usr/bin/date') # => true
+ * $? # => #<Process::Status: pid 645605 exit 0>
+ * system('foo') # => nil
+ * $? # => #<Process::Status: pid 645608 exit 127>
+ *
+ * Ruby invokes the executable directly.
+ * This form does not use the shell;
+ * see {Arguments args}[rdoc-ref:Process@Arguments+args] for caveats.
+ *
+ * system('doesnt_exist') # => nil
+ *
+ * If one or more +args+ is given, each is an argument or option
+ * to be passed to the executable:
+ *
+ * system('echo', 'C*') # => true
+ * system('echo', 'hello', 'world') # => true
+ *
+ * Output:
+ *
+ * C*
+ * hello world
+ *
+ * Raises an exception if the new process could not execute.
*/
static VALUE
rb_f_system(int argc, VALUE *argv, VALUE _)
{
+ rb_thread_t *th = GET_THREAD();
VALUE execarg_obj = rb_execarg_new(argc, argv, TRUE, TRUE);
struct rb_execarg *eargp = rb_execarg_get(execarg_obj);
struct rb_process_status status = {0};
eargp->status = &status;
- rb_last_status_clear();
+ last_status_clear(th);
// This function can set the thread's last status.
// May be different from waitpid_state.pid on exec failure.
@@ -4593,11 +4863,10 @@ rb_f_system(int argc, VALUE *argv, VALUE _)
if (pid > 0) {
VALUE status = rb_process_status_wait(pid, 0);
- struct rb_process_status *data = RTYPEDDATA_DATA(status);
-
+ struct rb_process_status *data = rb_check_typeddata(status, &rb_process_status_type);
// Set the last status:
rb_obj_freeze(status);
- GET_THREAD()->last_status = status;
+ th->last_status = status;
if (data->status == EXIT_SUCCESS) {
return Qtrue;
@@ -4640,271 +4909,105 @@ rb_f_system(int argc, VALUE *argv, VALUE _)
/*
* call-seq:
- * spawn([env,] command... [,options]) -> pid
- * Process.spawn([env,] command... [,options]) -> pid
- *
- * spawn executes specified command and return its pid.
- *
- * pid = spawn("tar xf ruby-2.0.0-p195.tar.bz2")
- * Process.wait pid
- *
- * pid = spawn(RbConfig.ruby, "-eputs'Hello, world!'")
- * Process.wait pid
- *
- * This method is similar to Kernel#system but it doesn't wait for the command
- * to finish.
- *
- * The parent process should
- * use Process.wait to collect
- * the termination status of its child or
- * use Process.detach to register
- * disinterest in their status;
- * otherwise, the operating system may accumulate zombie processes.
- *
- * spawn has bunch of options to specify process attributes:
- *
- * env: hash
- * name => val : set the environment variable
- * name => nil : unset the environment variable
- *
- * the keys and the values except for +nil+ must be strings.
- * command...:
- * commandline : command line string which is passed to the standard shell
- * cmdname, arg1, ... : command name and one or more arguments (This form does not use the shell. See below for caveats.)
- * [cmdname, argv0], arg1, ... : command name, argv[0] and zero or more arguments (no shell)
- * options: hash
- * clearing environment variables:
- * :unsetenv_others => true : clear environment variables except specified by env
- * :unsetenv_others => false : don't clear (default)
- * process group:
- * :pgroup => true or 0 : make a new process group
- * :pgroup => pgid : join the specified process group
- * :pgroup => nil : don't change the process group (default)
- * create new process group: Windows only
- * :new_pgroup => true : the new process is the root process of a new process group
- * :new_pgroup => false : don't create a new process group (default)
- * resource limit: resourcename is core, cpu, data, etc. See Process.setrlimit.
- * :rlimit_resourcename => limit
- * :rlimit_resourcename => [cur_limit, max_limit]
- * umask:
- * :umask => int
- * redirection:
- * key:
- * FD : single file descriptor in child process
- * [FD, FD, ...] : multiple file descriptor in child process
- * value:
- * FD : redirect to the file descriptor in parent process
- * string : redirect to file with open(string, "r" or "w")
- * [string] : redirect to file with open(string, File::RDONLY)
- * [string, open_mode] : redirect to file with open(string, open_mode, 0644)
- * [string, open_mode, perm] : redirect to file with open(string, open_mode, perm)
- * [:child, FD] : redirect to the redirected file descriptor
- * :close : close the file descriptor in child process
- * FD is one of follows
- * :in : the file descriptor 0 which is the standard input
- * :out : the file descriptor 1 which is the standard output
- * :err : the file descriptor 2 which is the standard error
- * integer : the file descriptor of specified the integer
- * io : the file descriptor specified as io.fileno
- * file descriptor inheritance: close non-redirected non-standard fds (3, 4, 5, ...) or not
- * :close_others => false : inherit
- * current directory:
- * :chdir => str
- *
- * The <code>cmdname, arg1, ...</code> form does not use the shell.
- * However, on different OSes, different things are provided as
- * built-in commands. An example of this is +'echo'+, which is a
- * built-in on Windows, but is a normal program on Linux and Mac OS X.
- * This means that <code>Process.spawn 'echo', '%Path%'</code> will
- * display the contents of the <tt>%Path%</tt> environment variable
- * on Windows, but <code>Process.spawn 'echo', '$PATH'</code> prints
- * the literal <tt>$PATH</tt>.
- *
- * If a hash is given as +env+, the environment is
- * updated by +env+ before <code>exec(2)</code> in the child process.
- * If a pair in +env+ has nil as the value, the variable is deleted.
- *
- * # set FOO as BAR and unset BAZ.
- * pid = spawn({"FOO"=>"BAR", "BAZ"=>nil}, command)
- *
- * If a hash is given as +options+,
- * it specifies
- * process group,
- * create new process group,
- * resource limit,
- * current directory,
- * umask and
- * redirects for the child process.
- * Also, it can be specified to clear environment variables.
- *
- * The <code>:unsetenv_others</code> key in +options+ specifies
- * to clear environment variables, other than specified by +env+.
- *
- * pid = spawn(command, :unsetenv_others=>true) # no environment variable
- * pid = spawn({"FOO"=>"BAR"}, command, :unsetenv_others=>true) # FOO only
- *
- * The <code>:pgroup</code> key in +options+ specifies a process group.
- * The corresponding value should be true, zero, a positive integer, or nil.
- * true and zero cause the process to be a process leader of a new process group.
- * A non-zero positive integer causes the process to join the provided process group.
- * The default value, nil, causes the process to remain in the same process group.
- *
- * pid = spawn(command, :pgroup=>true) # process leader
- * pid = spawn(command, :pgroup=>10) # belongs to the process group 10
- *
- * The <code>:new_pgroup</code> key in +options+ specifies to pass
- * +CREATE_NEW_PROCESS_GROUP+ flag to <code>CreateProcessW()</code> that is
- * Windows API. This option is only for Windows.
- * true means the new process is the root process of the new process group.
- * The new process has CTRL+C disabled. This flag is necessary for
- * <code>Process.kill(:SIGINT, pid)</code> on the subprocess.
- * :new_pgroup is false by default.
- *
- * pid = spawn(command, :new_pgroup=>true) # new process group
- * pid = spawn(command, :new_pgroup=>false) # same process group
- *
- * The <code>:rlimit_</code><em>foo</em> key specifies a resource limit.
- * <em>foo</em> should be one of resource types such as <code>core</code>.
- * The corresponding value should be an integer or an array which have one or
- * two integers: same as cur_limit and max_limit arguments for
- * Process.setrlimit.
- *
- * cur, max = Process.getrlimit(:CORE)
- * pid = spawn(command, :rlimit_core=>[0,max]) # disable core temporary.
- * pid = spawn(command, :rlimit_core=>max) # enable core dump
- * pid = spawn(command, :rlimit_core=>0) # never dump core.
- *
- * The <code>:umask</code> key in +options+ specifies the umask.
- *
- * pid = spawn(command, :umask=>077)
- *
- * The :in, :out, :err, an integer, an IO and an array key specifies a redirection.
- * The redirection maps a file descriptor in the child process.
- *
- * For example, stderr can be merged into stdout as follows:
- *
- * pid = spawn(command, :err=>:out)
- * pid = spawn(command, 2=>1)
- * pid = spawn(command, STDERR=>:out)
- * pid = spawn(command, STDERR=>STDOUT)
- *
- * The hash keys specifies a file descriptor in the child process
- * started by #spawn.
- * :err, 2 and STDERR specifies the standard error stream (stderr).
- *
- * The hash values specifies a file descriptor in the parent process
- * which invokes #spawn.
- * :out, 1 and STDOUT specifies the standard output stream (stdout).
- *
- * In the above example,
- * the standard output in the child process is not specified.
- * So it is inherited from the parent process.
- *
- * The standard input stream (stdin) can be specified by :in, 0 and STDIN.
- *
- * A filename can be specified as a hash value.
- *
- * pid = spawn(command, :in=>"/dev/null") # read mode
- * pid = spawn(command, :out=>"/dev/null") # write mode
- * pid = spawn(command, :err=>"log") # write mode
- * pid = spawn(command, [:out, :err]=>"/dev/null") # write mode
- * pid = spawn(command, 3=>"/dev/null") # read mode
+ * spawn([env, ] command_line, options = {}) -> pid
+ * spawn([env, ] exe_path, *args, options = {}) -> pid
+ *
+ * Creates a new child process by doing one of the following
+ * in that process:
+ *
+ * - Passing string +command_line+ to the shell.
+ * - Invoking the executable at +exe_path+.
*
- * For stdout and stderr (and combination of them),
- * it is opened in write mode.
- * Otherwise read mode is used.
+ * This method has potential security vulnerabilities if called with untrusted input;
+ * see {Command Injection}[rdoc-ref:command_injection.rdoc].
+ *
+ * Returns the process ID (pid) of the new process,
+ * without waiting for it to complete.
+ *
+ * To avoid zombie processes, the parent process should call either:
*
- * For specifying flags and permission of file creation explicitly,
- * an array is used instead.
+ * - Process.wait, to collect the termination statuses of its children.
+ * - Process.detach, to register disinterest in their status.
*
- * pid = spawn(command, :in=>["file"]) # read mode is assumed
- * pid = spawn(command, :in=>["file", "r"])
- * pid = spawn(command, :out=>["log", "w"]) # 0644 assumed
- * pid = spawn(command, :out=>["log", "w", 0600])
- * pid = spawn(command, :out=>["log", File::WRONLY|File::EXCL|File::CREAT, 0600])
+ * The new process is created using the
+ * {exec system call}[https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/execve.html];
+ * it may inherit some of its environment from the calling program
+ * (possibly including open file descriptors).
*
- * The array specifies a filename, flags and permission.
- * The flags can be a string or an integer.
- * If the flags is omitted or nil, File::RDONLY is assumed.
- * The permission should be an integer.
- * If the permission is omitted or nil, 0644 is assumed.
+ * Argument +env+, if given, is a hash that affects +ENV+ for the new process;
+ * see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
*
- * If an array of IOs and integers are specified as a hash key,
- * all the elements are redirected.
+ * Argument +options+ is a hash of options for the new process;
+ * see {Execution Options}[rdoc-ref:Process@Execution+Options].
*
- * # stdout and stderr is redirected to log file.
- * # The file "log" is opened just once.
- * pid = spawn(command, [:out, :err]=>["log", "w"])
+ * The first required argument is one of the following:
*
- * Another way to merge multiple file descriptors is [:child, fd].
- * \[:child, fd] means the file descriptor in the child process.
- * This is different from fd.
- * For example, :err=>:out means redirecting child stderr to parent stdout.
- * But :err=>[:child, :out] means redirecting child stderr to child stdout.
- * They differ if stdout is redirected in the child process as follows.
+ * - +command_line+ if it is a string,
+ * and if it begins with a shell reserved word or special built-in,
+ * or if it contains one or more meta characters.
+ * - +exe_path+ otherwise.
*
- * # stdout and stderr is redirected to log file.
- * # The file "log" is opened just once.
- * pid = spawn(command, :out=>["log", "w"], :err=>[:child, :out])
+ * <b>Argument +command_line+</b>
*
- * \[:child, :out] can be used to merge stderr into stdout in IO.popen.
- * In this case, IO.popen redirects stdout to a pipe in the child process
- * and [:child, :out] refers the redirected stdout.
+ * \String argument +command_line+ is a command line to be passed to a shell;
+ * it must begin with a shell reserved word, begin with a special built-in,
+ * or contain meta characters:
*
- * io = IO.popen(["sh", "-c", "echo out; echo err >&2", :err=>[:child, :out]])
- * p io.read #=> "out\nerr\n"
+ * spawn('if true; then echo "Foo"; fi') # => 798847 # Shell reserved word.
+ * Process.wait # => 798847
+ * spawn('exit') # => 798848 # Built-in.
+ * Process.wait # => 798848
+ * spawn('date > /tmp/date.tmp') # => 798879 # Contains meta character.
+ * Process.wait # => 798849
+ * spawn('date > /nop/date.tmp') # => 798882 # Issues error message.
+ * Process.wait # => 798882
*
- * The <code>:chdir</code> key in +options+ specifies the current directory.
+ * The command line may also contain arguments and options for the command:
*
- * pid = spawn(command, :chdir=>"/var/tmp")
+ * spawn('echo "Foo"') # => 799031
+ * Process.wait # => 799031
*
- * spawn closes all non-standard unspecified descriptors by default.
- * The "standard" descriptors are 0, 1 and 2.
- * This behavior is specified by :close_others option.
- * :close_others doesn't affect the standard descriptors which are
- * closed only if :close is specified explicitly.
+ * Output:
*
- * pid = spawn(command, :close_others=>true) # close 3,4,5,... (default)
- * pid = spawn(command, :close_others=>false) # don't close 3,4,5,...
+ * Foo
*
- * :close_others is false by default for spawn and IO.popen.
+ * See {Execution Shell}[rdoc-ref:Process@Execution+Shell] for details about the shell.
*
- * Note that fds which close-on-exec flag is already set are closed
- * regardless of :close_others option.
+ * Raises an exception if the new process could not execute.
*
- * So IO.pipe and spawn can be used as IO.popen.
+ * <b>Argument +exe_path+</b>
*
- * # similar to r = IO.popen(command)
- * r, w = IO.pipe
- * pid = spawn(command, :out=>w) # r, w is closed in the child process.
- * w.close
+ * Argument +exe_path+ is one of the following:
*
- * :close is specified as a hash value to close a fd individually.
+ * - The string path to an executable to be called.
+ * - A 2-element array containing the path to an executable to be called,
+ * and the string to be used as the name of the executing process.
*
- * f = open(foo)
- * system(command, f=>:close) # don't inherit f.
+ * spawn('/usr/bin/date') # Path to date on Unix-style system.
+ * Process.wait
*
- * If a file descriptor need to be inherited,
- * io=>io can be used.
+ * Output:
*
- * # valgrind has --log-fd option for log destination.
- * # log_w=>log_w indicates log_w.fileno inherits to child process.
- * log_r, log_w = IO.pipe
- * pid = spawn("valgrind", "--log-fd=#{log_w.fileno}", "echo", "a", log_w=>log_w)
- * log_w.close
- * p log_r.read
+ * Mon Aug 28 11:43:10 AM CDT 2023
*
- * It is also possible to exchange file descriptors.
+ * Ruby invokes the executable directly.
+ * This form does not use the shell;
+ * see {Arguments args}[rdoc-ref:Process@Arguments+args] for caveats.
*
- * pid = spawn(command, :out=>:err, :err=>:out)
+ * If one or more +args+ is given, each is an argument or option
+ * to be passed to the executable:
*
- * The hash keys specify file descriptors in the child process.
- * The hash values specifies file descriptors in the parent process.
- * So the above specifies exchanging stdout and stderr.
- * Internally, +spawn+ uses an extra file descriptor to resolve such cyclic
- * file descriptor mapping.
+ * spawn('echo', 'C*') # => 799392
+ * Process.wait # => 799392
+ * spawn('echo', 'hello', 'world') # => 799393
+ * Process.wait # => 799393
*
- * See Kernel.exec for the standard shell.
+ * Output:
+ *
+ * C*
+ * hello world
+ *
+ * Raises an exception if the new process could not execute.
*/
static VALUE
@@ -4936,22 +5039,18 @@ rb_f_spawn(int argc, VALUE *argv, VALUE _)
/*
* call-seq:
- * sleep([duration]) -> integer
- *
- * Suspends the current thread for _duration_ seconds (which may be any number,
- * including a +Float+ with fractional seconds). Returns the actual number of
- * seconds slept (rounded), which may be less than that asked for if another
- * thread calls Thread#run. Called without an argument, sleep()
- * will sleep forever.
- *
- * If the +duration+ is not supplied, or is +nil+, the thread sleeps forever.
- * Threads in this state may still be interrupted by other threads.
- *
- * Time.new #=> 2008-03-08 19:56:19 +0900
- * sleep 1.2 #=> 1
- * Time.new #=> 2008-03-08 19:56:20 +0900
- * sleep 1.9 #=> 2
- * Time.new #=> 2008-03-08 19:56:22 +0900
+ * sleep(secs = nil) -> slept_secs
+ *
+ * Suspends execution of the current thread for the number of seconds
+ * specified by numeric argument +secs+, or forever if +secs+ is +nil+;
+ * returns the integer number of seconds suspended (rounded).
+ *
+ * Time.new # => 2008-03-08 19:56:19 +0900
+ * sleep 1.2 # => 1
+ * Time.new # => 2008-03-08 19:56:20 +0900
+ * sleep 1.9 # => 2
+ * Time.new # => 2008-03-08 19:56:22 +0900
+ *
*/
static VALUE
@@ -4982,13 +5081,13 @@ rb_f_sleep(int argc, VALUE *argv, VALUE _)
#if (defined(HAVE_GETPGRP) && defined(GETPGRP_VOID)) || defined(HAVE_GETPGID)
/*
* call-seq:
- * Process.getpgrp -> integer
+ * Process.getpgrp -> integer
+ *
+ * Returns the process group ID for the current process:
*
- * Returns the process group ID for this process. Not available on
- * all platforms.
+ * Process.getpgid(0) # => 25527
+ * Process.getpgrp # => 25527
*
- * Process.getpgid(0) #=> 25527
- * Process.getpgrp #=> 25527
*/
static VALUE
@@ -5014,10 +5113,11 @@ proc_getpgrp(VALUE _)
#if defined(HAVE_SETPGID) || (defined(HAVE_SETPGRP) && defined(SETPGRP_VOID))
/*
* call-seq:
- * Process.setpgrp -> 0
+ * Process.setpgrp -> 0
*
- * Equivalent to <code>setpgid(0,0)</code>. Not available on all
- * platforms.
+ * Equivalent to <tt>setpgid(0, 0)</tt>.
+ *
+ * Not available on all platforms.
*/
static VALUE
@@ -5042,12 +5142,13 @@ proc_setpgrp(VALUE _)
#if defined(HAVE_GETPGID)
/*
* call-seq:
- * Process.getpgid(pid) -> integer
+ * Process.getpgid(pid) -> integer
*
- * Returns the process group ID for the given process id. Not
- * available on all platforms.
+ * Returns the process group ID for the given process ID +pid+:
*
- * Process.getpgid(Process.ppid()) #=> 25527
+ * Process.getpgid(Process.ppid) # => 25527
+ *
+ * Not available on all platforms.
*/
static VALUE
@@ -5067,10 +5168,12 @@ proc_getpgid(VALUE obj, VALUE pid)
#ifdef HAVE_SETPGID
/*
* call-seq:
- * Process.setpgid(pid, integer) -> 0
+ * Process.setpgid(pid, pgid) -> 0
+ *
+ * Sets the process group ID for the process given by process ID +pid+
+ * to +pgid+.
*
- * Sets the process group ID of _pid_ (0 indicates this
- * process) to <em>integer</em>. Not available on all platforms.
+ * Not available on all platforms.
*/
static VALUE
@@ -5092,15 +5195,16 @@ proc_setpgid(VALUE obj, VALUE pid, VALUE pgrp)
#ifdef HAVE_GETSID
/*
* call-seq:
- * Process.getsid() -> integer
- * Process.getsid(pid) -> integer
+ * Process.getsid(pid = nil) -> integer
*
- * Returns the session ID for the given process id. If not given,
- * return current process sid. Not available on all platforms.
+ * Returns the session ID of the given process ID +pid+,
+ * or of the current process if not given:
*
- * Process.getsid() #=> 27422
- * Process.getsid(0) #=> 27422
- * Process.getsid(Process.pid()) #=> 27422
+ * Process.getsid # => 27422
+ * Process.getsid(0) # => 27422
+ * Process.getsid(Process.pid()) # => 27422
+ *
+ * Not available on all platforms.
*/
static VALUE
proc_getsid(int argc, VALUE *argv, VALUE _)
@@ -5127,13 +5231,15 @@ static rb_pid_t ruby_setsid(void);
#endif
/*
* call-seq:
- * Process.setsid -> integer
+ * Process.setsid -> integer
*
- * Establishes this process as a new session and process group
- * leader, with no controlling tty. Returns the session id. Not
- * available on all platforms.
+ * Establishes the current process as a new session and process group leader,
+ * with no controlling tty;
+ * returns the session ID:
*
- * Process.setsid #=> 27422
+ * Process.setsid # => 27422
+ *
+ * Not available on all platforms.
*/
static VALUE
@@ -5152,7 +5258,7 @@ static rb_pid_t
ruby_setsid(void)
{
rb_pid_t pid;
- int ret;
+ int ret, fd;
pid = getpid();
#if defined(SETPGRP_VOID)
@@ -5181,19 +5287,26 @@ ruby_setsid(void)
#ifdef HAVE_GETPRIORITY
/*
* call-seq:
- * Process.getpriority(kind, integer) -> integer
- *
- * Gets the scheduling priority for specified process, process group,
- * or user. <em>kind</em> indicates the kind of entity to find: one
- * of Process::PRIO_PGRP,
- * Process::PRIO_USER, or
- * Process::PRIO_PROCESS. _integer_ is an id
- * indicating the particular process, process group, or user (an id
- * of 0 means _current_). Lower priorities are more favorable
- * for scheduling. Not available on all platforms.
- *
- * Process.getpriority(Process::PRIO_USER, 0) #=> 19
- * Process.getpriority(Process::PRIO_PROCESS, 0) #=> 19
+ * Process.getpriority(kind, id) -> integer
+ *
+ * Returns the scheduling priority for specified process, process group,
+ * or user.
+ *
+ * Argument +kind+ is one of:
+ *
+ * - Process::PRIO_PROCESS: return priority for process.
+ * - Process::PRIO_PGRP: return priority for process group.
+ * - Process::PRIO_USER: return priority for user.
+ *
+ * Argument +id+ is the ID for the process, process group, or user;
+ * zero specified the current ID for +kind+.
+ *
+ * Examples:
+ *
+ * Process.getpriority(Process::PRIO_USER, 0) # => 19
+ * Process.getpriority(Process::PRIO_PROCESS, 0) # => 19
+ *
+ * Not available on all platforms.
*/
static VALUE
@@ -5217,14 +5330,18 @@ proc_getpriority(VALUE obj, VALUE which, VALUE who)
#ifdef HAVE_GETPRIORITY
/*
* call-seq:
- * Process.setpriority(kind, integer, priority) -> 0
+ * Process.setpriority(kind, integer, priority) -> 0
*
* See Process.getpriority.
*
- * Process.setpriority(Process::PRIO_USER, 0, 19) #=> 0
- * Process.setpriority(Process::PRIO_PROCESS, 0, 19) #=> 0
- * Process.getpriority(Process::PRIO_USER, 0) #=> 19
- * Process.getpriority(Process::PRIO_PROCESS, 0) #=> 19
+ * Examples:
+ *
+ * Process.setpriority(Process::PRIO_USER, 0, 19) # => 0
+ * Process.setpriority(Process::PRIO_PROCESS, 0, 19) # => 0
+ * Process.getpriority(Process::PRIO_USER, 0) # => 19
+ * Process.getpriority(Process::PRIO_PROCESS, 0) # => 19
+ *
+ * Not available on all platforms.
*/
static VALUE
@@ -5472,22 +5589,24 @@ rlimit_resource_value(VALUE rval)
#if defined(HAVE_GETRLIMIT) && defined(RLIM2NUM)
/*
* call-seq:
- * Process.getrlimit(resource) -> [cur_limit, max_limit]
- *
- * Gets the resource limit of the process.
- * _cur_limit_ means current (soft) limit and
- * _max_limit_ means maximum (hard) limit.
- *
- * _resource_ indicates the kind of resource to limit.
- * It is specified as a symbol such as <code>:CORE</code>,
- * a string such as <code>"CORE"</code> or
- * a constant such as Process::RLIMIT_CORE.
- * See Process.setrlimit for details.
- *
- * _cur_limit_ and _max_limit_ may be Process::RLIM_INFINITY,
- * Process::RLIM_SAVED_MAX or
- * Process::RLIM_SAVED_CUR.
- * See Process.setrlimit and the system getrlimit(2) manual for details.
+ * Process.getrlimit(resource) -> [cur_limit, max_limit]
+ *
+ * Returns a 2-element array of the current (soft) limit
+ * and maximum (hard) limit for the given +resource+.
+ *
+ * Argument +resource+ specifies the resource whose limits are to be returned;
+ * see Process.setrlimit.
+ *
+ * Each of the returned values +cur_limit+ and +max_limit+ is an integer;
+ * see Process.setrlimit.
+ *
+ * Example:
+ *
+ * Process.getrlimit(:CORE) # => [0, 18446744073709551615]
+ *
+ * See Process.setrlimit.
+ *
+ * Not available on all platforms.
*/
static VALUE
@@ -5507,54 +5626,54 @@ proc_getrlimit(VALUE obj, VALUE resource)
#if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM)
/*
* call-seq:
- * Process.setrlimit(resource, cur_limit, max_limit) -> nil
- * Process.setrlimit(resource, cur_limit) -> nil
- *
- * Sets the resource limit of the process.
- * _cur_limit_ means current (soft) limit and
- * _max_limit_ means maximum (hard) limit.
- *
- * If _max_limit_ is not given, _cur_limit_ is used.
- *
- * _resource_ indicates the kind of resource to limit.
- * It should be a symbol such as <code>:CORE</code>,
- * a string such as <code>"CORE"</code> or
- * a constant such as Process::RLIMIT_CORE.
- * The available resources are OS dependent.
- * Ruby may support following resources.
- *
- * [AS] total available memory (bytes) (SUSv3, NetBSD, FreeBSD, OpenBSD but 4.4BSD-Lite)
- * [CORE] core size (bytes) (SUSv3)
- * [CPU] CPU time (seconds) (SUSv3)
- * [DATA] data segment (bytes) (SUSv3)
- * [FSIZE] file size (bytes) (SUSv3)
- * [MEMLOCK] total size for mlock(2) (bytes) (4.4BSD, GNU/Linux)
- * [MSGQUEUE] allocation for POSIX message queues (bytes) (GNU/Linux)
- * [NICE] ceiling on process's nice(2) value (number) (GNU/Linux)
- * [NOFILE] file descriptors (number) (SUSv3)
- * [NPROC] number of processes for the user (number) (4.4BSD, GNU/Linux)
- * [NPTS] number of pseudo terminals (number) (FreeBSD)
- * [RSS] resident memory size (bytes) (4.2BSD, GNU/Linux)
- * [RTPRIO] ceiling on the process's real-time priority (number) (GNU/Linux)
- * [RTTIME] CPU time for real-time process (us) (GNU/Linux)
- * [SBSIZE] all socket buffers (bytes) (NetBSD, FreeBSD)
- * [SIGPENDING] number of queued signals allowed (signals) (GNU/Linux)
- * [STACK] stack size (bytes) (SUSv3)
- *
- * _cur_limit_ and _max_limit_ may be
- * <code>:INFINITY</code>, <code>"INFINITY"</code> or
- * Process::RLIM_INFINITY,
- * which means that the resource is not limited.
- * They may be Process::RLIM_SAVED_MAX,
- * Process::RLIM_SAVED_CUR and
- * corresponding symbols and strings too.
- * See system setrlimit(2) manual for details.
- *
- * The following example raises the soft limit of core size to
- * the hard limit to try to make core dump possible.
+ * Process.setrlimit(resource, cur_limit, max_limit = cur_limit) -> nil
+ *
+ * Sets limits for the current process for the given +resource+
+ * to +cur_limit+ (soft limit) and +max_limit+ (hard limit);
+ * returns +nil+.
+ *
+ * Argument +resource+ specifies the resource whose limits are to be set;
+ * the argument may be given as a symbol, as a string, or as a constant
+ * beginning with <tt>Process::RLIMIT_</tt>
+ * (e.g., +:CORE+, <tt>'CORE'</tt>, or <tt>Process::RLIMIT_CORE</tt>.
+ *
+ * The resources available and supported are system-dependent,
+ * and may include (here expressed as symbols):
+ *
+ * - +:AS+: Total available memory (bytes) (SUSv3, NetBSD, FreeBSD, OpenBSD except 4.4BSD-Lite).
+ * - +:CORE+: Core size (bytes) (SUSv3).
+ * - +:CPU+: CPU time (seconds) (SUSv3).
+ * - +:DATA+: Data segment (bytes) (SUSv3).
+ * - +:FSIZE+: File size (bytes) (SUSv3).
+ * - +:MEMLOCK+: Total size for mlock(2) (bytes) (4.4BSD, GNU/Linux).
+ * - +:MSGQUEUE+: Allocation for POSIX message queues (bytes) (GNU/Linux).
+ * - +:NICE+: Ceiling on process's nice(2) value (number) (GNU/Linux).
+ * - +:NOFILE+: File descriptors (number) (SUSv3).
+ * - +:NPROC+: Number of processes for the user (number) (4.4BSD, GNU/Linux).
+ * - +:NPTS+: Number of pseudo terminals (number) (FreeBSD).
+ * - +:RSS+: Resident memory size (bytes) (4.2BSD, GNU/Linux).
+ * - +:RTPRIO+: Ceiling on the process's real-time priority (number) (GNU/Linux).
+ * - +:RTTIME+: CPU time for real-time process (us) (GNU/Linux).
+ * - +:SBSIZE+: All socket buffers (bytes) (NetBSD, FreeBSD).
+ * - +:SIGPENDING+: Number of queued signals allowed (signals) (GNU/Linux).
+ * - +:STACK+: Stack size (bytes) (SUSv3).
+ *
+ * Arguments +cur_limit+ and +max_limit+ may be:
+ *
+ * - Integers (+max_limit+ should not be smaller than +cur_limit+).
+ * - Symbol +:SAVED_MAX+, string <tt>'SAVED_MAX'</tt>,
+ * or constant <tt>Process::RLIM_SAVED_MAX</tt>: saved maximum limit.
+ * - Symbol +:SAVED_CUR+, string <tt>'SAVED_CUR'</tt>,
+ * or constant <tt>Process::RLIM_SAVED_CUR</tt>: saved current limit.
+ * - Symbol +:INFINITY+, string <tt>'INFINITY'</tt>,
+ * or constant <tt>Process::RLIM_INFINITY</tt>: no limit on resource.
+ *
+ * This example raises the soft limit of core size to
+ * the hard limit to try to make core dump possible:
*
* Process.setrlimit(:CORE, Process.getrlimit(:CORE)[1])
*
+ * Not available on all platforms.
*/
static VALUE
@@ -6133,13 +6252,14 @@ p_sys_setresuid(VALUE obj, VALUE rid, VALUE eid, VALUE sid)
/*
* call-seq:
- * Process.uid -> integer
- * Process::UID.rid -> integer
- * Process::Sys.getuid -> integer
+ * Process.uid -> integer
+ * Process::UID.rid -> integer
+ * Process::Sys.getuid -> integer
+ *
+ * Returns the (real) user ID of the current process.
*
- * Returns the (real) user ID of this process.
+ * Process.uid # => 1000
*
- * Process.uid #=> 501
*/
static VALUE
@@ -6153,10 +6273,13 @@ proc_getuid(VALUE obj)
#if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETRUID) || defined(HAVE_SETUID)
/*
* call-seq:
- * Process.uid= user -> numeric
+ * Process.uid = new_uid -> new_uid
+ *
+ * Sets the (user) user ID for the current process to +new_uid+:
*
- * Sets the (user) user ID for this process. Not available on all
- * platforms.
+ * Process.uid = 1000 # => 1000
+ *
+ * Not available on all platforms.
*/
static VALUE
@@ -6531,13 +6654,14 @@ p_sys_issetugid(VALUE obj)
/*
* call-seq:
- * Process.gid -> integer
- * Process::GID.rid -> integer
- * Process::Sys.getgid -> integer
+ * Process.gid -> integer
+ * Process::GID.rid -> integer
+ * Process::Sys.getgid -> integer
+ *
+ * Returns the (real) group ID for the current process:
*
- * Returns the (real) group ID for this process.
+ * Process.gid # => 1000
*
- * Process.gid #=> 500
*/
static VALUE
@@ -6551,9 +6675,12 @@ proc_getgid(VALUE obj)
#if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETRGID) || defined(HAVE_SETGID)
/*
* call-seq:
- * Process.gid= integer -> integer
+ * Process.gid = new_gid -> new_gid
+ *
+ * Sets the group ID for the current process to +new_gid+:
+ *
+ * Process.gid = 1000 # => 1000
*
- * Sets the group ID for this process.
*/
static VALUE
@@ -6637,26 +6764,23 @@ maxgroups(void)
#ifdef HAVE_GETGROUPS
/*
* call-seq:
- * Process.groups -> array
+ * Process.groups -> array
*
- * Get an Array of the group IDs in the
- * supplemental group access list for this process.
+ * Returns an array of the group IDs
+ * in the supplemental group access list for the current process:
*
- * Process.groups #=> [27, 6, 10, 11]
+ * Process.groups # => [4, 24, 27, 30, 46, 122, 135, 136, 1000]
*
- * Note that this method is just a wrapper of getgroups(2).
- * This means that the following characteristics of
- * the result completely depend on your system:
+ * These properties of the returned array are system-dependent:
*
- * - the result is sorted
- * - the result includes effective GIDs
- * - the result does not include duplicated GIDs
- * - the result size does not exceed the value of Process.maxgroups
+ * - Whether (and how) the array is sorted.
+ * - Whether the array includes effective group IDs.
+ * - Whether the array includes duplicate group IDs.
+ * - Whether the array size exceeds the value of Process.maxgroups.
*
- * You can make sure to get a sorted unique GID list of
- * the current process by this expression:
+ * Use this call to get a sorted and unique array:
*
- * Process.groups.uniq.sort
+ * Process.groups.uniq.sort
*
*/
@@ -6693,14 +6817,14 @@ proc_getgroups(VALUE obj)
#ifdef HAVE_SETGROUPS
/*
* call-seq:
- * Process.groups= array -> array
+ * Process.groups = new_groups -> new_groups
*
- * Set the supplemental group access list to the given
- * Array of group IDs.
+ * Sets the supplemental group access list to the given
+ * array of group IDs.
*
- * Process.groups #=> [0, 1, 2, 3, 4, 6, 10, 11, 20, 26, 27]
- * Process.groups = [27, 6, 10, 11] #=> [27, 6, 10, 11]
- * Process.groups #=> [27, 6, 10, 11]
+ * Process.groups # => [0, 1, 2, 3, 4, 6, 10, 11, 20, 26, 27]
+ * Process.groups = [27, 6, 10, 11] # => [27, 6, 10, 11]
+ * Process.groups # => [27, 6, 10, 11]
*
*/
@@ -6742,19 +6866,21 @@ proc_setgroups(VALUE obj, VALUE ary)
#ifdef HAVE_INITGROUPS
/*
* call-seq:
- * Process.initgroups(username, gid) -> array
+ * Process.initgroups(username, gid) -> array
+ *
+ * Sets the supplemental group access list;
+ * the new list includes:
*
- * Initializes the supplemental group access list by reading the
- * system group database and using all groups of which the given user
- * is a member. The group with the specified _gid_ is also added to
- * the list. Returns the resulting Array of the GIDs of all the
- * groups in the supplementary group access list. Not available on
- * all platforms.
+ * - The group IDs of those groups to which the user given by +username+ belongs.
+ * - The group ID +gid+.
*
- * Process.groups #=> [0, 1, 2, 3, 4, 6, 10, 11, 20, 26, 27]
- * Process.initgroups( "mgranger", 30 ) #=> [30, 6, 10, 11]
- * Process.groups #=> [30, 6, 10, 11]
+ * Example:
*
+ * Process.groups # => [0, 1, 2, 3, 4, 6, 10, 11, 20, 26, 27]
+ * Process.initgroups('me', 30) # => [30, 6, 10, 11]
+ * Process.groups # => [30, 6, 10, 11]
+ *
+ * Not available on all platforms.
*/
static VALUE
@@ -6772,12 +6898,13 @@ proc_initgroups(VALUE obj, VALUE uname, VALUE base_grp)
#if defined(_SC_NGROUPS_MAX) || defined(NGROUPS_MAX)
/*
* call-seq:
- * Process.maxgroups -> integer
+ * Process.maxgroups -> integer
*
- * Returns the maximum number of GIDs allowed in the supplemental
- * group access list.
+ * Returns the maximum number of group IDs allowed
+ * in the supplemental group access list:
+ *
+ * Process.maxgroups # => 32
*
- * Process.maxgroups #=> 32
*/
static VALUE
@@ -6792,10 +6919,10 @@ proc_getmaxgroups(VALUE obj)
#ifdef HAVE_SETGROUPS
/*
* call-seq:
- * Process.maxgroups= integer -> integer
+ * Process.maxgroups = new_max -> new_max
*
- * Sets the maximum number of GIDs allowed in the supplemental group
- * access list.
+ * Sets the maximum number of group IDs allowed
+ * in the supplemental group access list.
*/
static VALUE
@@ -6826,16 +6953,22 @@ static int rb_daemon(int nochdir, int noclose);
/*
* call-seq:
- * Process.daemon() -> 0
- * Process.daemon(nochdir=nil,noclose=nil) -> 0
- *
- * Detach the process from controlling terminal and run in
- * the background as system daemon. Unless the argument
- * nochdir is true (i.e. non false), it changes the current
- * working directory to the root ("/"). Unless the argument
- * noclose is true, daemon() will redirect standard input,
- * standard output and standard error to /dev/null.
- * Return zero on success, or raise one of Errno::*.
+ * Process.daemon(nochdir = nil, noclose = nil) -> 0
+ *
+ * Detaches the current process from its controlling terminal
+ * and runs it in the background as system daemon;
+ * returns zero.
+ *
+ * By default:
+ *
+ * - Changes the current working directory to the root directory.
+ * - Redirects $stdin, $stdout, and $stderr to the null device.
+ *
+ * If optional argument +nochdir+ is +true+,
+ * does not change the current working directory.
+ *
+ * If optional argument +noclose+ is +true+,
+ * does not redirect $stdin, $stdout, or $stderr.
*/
static VALUE
@@ -6854,6 +6987,8 @@ proc_daemon(int argc, VALUE *argv, VALUE _)
return INT2FIX(n);
}
+extern const char ruby_null_device[];
+
static int
rb_daemon(int nochdir, int noclose)
{
@@ -6877,7 +7012,7 @@ rb_daemon(int nochdir, int noclose)
if (!nochdir)
err = chdir("/");
- if (!noclose && (n = rb_cloexec_open("/dev/null", O_RDWR, 0)) != -1) {
+ if (!noclose && (n = rb_cloexec_open(ruby_null_device, O_RDWR, 0)) != -1) {
rb_update_max_fd(n);
(void)dup2(n, 0);
(void)dup2(n, 1);
@@ -7090,13 +7225,14 @@ p_gid_change_privilege(VALUE obj, VALUE id)
/*
* call-seq:
- * Process.euid -> integer
- * Process::UID.eid -> integer
- * Process::Sys.geteuid -> integer
+ * Process.euid -> integer
+ * Process::UID.eid -> integer
+ * Process::Sys.geteuid -> integer
+ *
+ * Returns the effective user ID for the current process.
*
- * Returns the effective user ID for this process.
+ * Process.euid # => 501
*
- * Process.euid #=> 501
*/
static VALUE
@@ -7132,10 +7268,11 @@ proc_seteuid(rb_uid_t uid)
#if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETEUID) || defined(HAVE_SETUID)
/*
* call-seq:
- * Process.euid= user
+ * Process.euid = new_euid -> new_euid
*
- * Sets the effective user ID for this process. Not available on all
- * platforms.
+ * Sets the effective user ID for the current process.
+ *
+ * Not available on all platforms.
*/
static VALUE
@@ -7213,14 +7350,15 @@ p_uid_grant_privilege(VALUE obj, VALUE id)
/*
* call-seq:
- * Process.egid -> integer
- * Process::GID.eid -> integer
- * Process::Sys.geteid -> integer
+ * Process.egid -> integer
+ * Process::GID.eid -> integer
+ * Process::Sys.geteid -> integer
+ *
+ * Returns the effective group ID for the current process:
*
- * Returns the effective group ID for this process. Not available on
- * all platforms.
+ * Process.egid # => 500
*
- * Process.egid #=> 500
+ * Not available on all platforms.
*/
static VALUE
@@ -7234,10 +7372,11 @@ proc_getegid(VALUE obj)
#if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID) || defined(_POSIX_SAVED_IDS)
/*
* call-seq:
- * Process.egid = integer -> integer
+ * Process.egid = new_egid -> new_egid
+ *
+ * Sets the effective group ID for the current process.
*
- * Sets the effective group ID for this process. Not available on all
- * platforms.
+ * Not available on all platforms.
*/
static VALUE
@@ -7710,14 +7849,15 @@ get_clk_tck(void)
/*
* call-seq:
- * Process.times -> aProcessTms
+ * Process.times -> process_tms
*
- * Returns a <code>Tms</code> structure (see Process::Tms)
- * that contains user and system CPU times for this process,
- * and also for children processes.
+ * Returns a Process::Tms structure that contains user and system CPU times
+ * for the current process, and for its children processes:
*
- * t = Process.times
- * [ t.utime, t.stime, t.cutime, t.cstime ] #=> [0.0, 0.02, 0.00, 0.00]
+ * Process.times
+ * # => #<struct Process::Tms utime=55.122118, stime=35.533068, cutime=0.0, cstime=0.002846>
+ *
+ * The precision is platform-defined.
*/
VALUE
@@ -7979,130 +8119,167 @@ ruby_real_ms_time(void)
/*
* call-seq:
- * Process.clock_gettime(clock_id [, unit]) -> number
- *
- * Returns a time returned by POSIX clock_gettime() function.
- *
- * p Process.clock_gettime(Process::CLOCK_MONOTONIC)
- * #=> 896053.968060096
- *
- * +clock_id+ specifies a kind of clock.
- * It is specified as a constant which begins with <code>Process::CLOCK_</code>
- * such as Process::CLOCK_REALTIME and Process::CLOCK_MONOTONIC.
- *
- * The supported constants depends on OS and version.
- * Ruby provides following types of +clock_id+ if available.
- *
- * [CLOCK_REALTIME] SUSv2 to 4, Linux 2.5.63, FreeBSD 3.0, NetBSD 2.0, OpenBSD 2.1, macOS 10.12, Windows-8/Server-2012
- * [CLOCK_MONOTONIC] SUSv3 to 4, Linux 2.5.63, FreeBSD 3.0, NetBSD 2.0, OpenBSD 3.4, macOS 10.12, Windows-2000
- * [CLOCK_PROCESS_CPUTIME_ID] SUSv3 to 4, Linux 2.5.63, FreeBSD 9.3, OpenBSD 5.4, macOS 10.12
- * [CLOCK_THREAD_CPUTIME_ID] SUSv3 to 4, Linux 2.5.63, FreeBSD 7.1, OpenBSD 5.4, macOS 10.12
- * [CLOCK_VIRTUAL] FreeBSD 3.0, OpenBSD 2.1
- * [CLOCK_PROF] FreeBSD 3.0, OpenBSD 2.1
- * [CLOCK_REALTIME_FAST] FreeBSD 8.1
- * [CLOCK_REALTIME_PRECISE] FreeBSD 8.1
- * [CLOCK_REALTIME_COARSE] Linux 2.6.32
- * [CLOCK_REALTIME_ALARM] Linux 3.0
- * [CLOCK_MONOTONIC_FAST] FreeBSD 8.1
- * [CLOCK_MONOTONIC_PRECISE] FreeBSD 8.1
- * [CLOCK_MONOTONIC_COARSE] Linux 2.6.32
- * [CLOCK_MONOTONIC_RAW] Linux 2.6.28, macOS 10.12
- * [CLOCK_MONOTONIC_RAW_APPROX] macOS 10.12
- * [CLOCK_BOOTTIME] Linux 2.6.39
- * [CLOCK_BOOTTIME_ALARM] Linux 3.0
- * [CLOCK_UPTIME] FreeBSD 7.0, OpenBSD 5.5
- * [CLOCK_UPTIME_FAST] FreeBSD 8.1
- * [CLOCK_UPTIME_RAW] macOS 10.12
- * [CLOCK_UPTIME_RAW_APPROX] macOS 10.12
- * [CLOCK_UPTIME_PRECISE] FreeBSD 8.1
- * [CLOCK_SECOND] FreeBSD 8.1
- * [CLOCK_TAI] Linux 3.10
+ * Process.clock_gettime(clock_id, unit = :float_second) -> number
+ *
+ * Returns a clock time as determined by POSIX function
+ * {clock_gettime()}[https://man7.org/linux/man-pages/man3/clock_gettime.3.html]:
+ *
+ * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID) # => 198.650379677
+ *
+ * Argument +clock_id+ should be a symbol or a constant that specifies
+ * the clock whose time is to be returned;
+ * see below.
+ *
+ * Optional argument +unit+ should be a symbol that specifies
+ * the unit to be used in the returned clock time;
+ * see below.
+ *
+ * <b>Argument +clock_id+</b>
+ *
+ * Argument +clock_id+ specifies the clock whose time is to be returned;
+ * it may be a constant such as <tt>Process::CLOCK_REALTIME</tt>,
+ * or a symbol shorthand such as +:CLOCK_REALTIME+.
+ *
+ * The supported clocks depend on the underlying operating system;
+ * this method supports the following clocks on the indicated platforms
+ * (raises Errno::EINVAL if called with an unsupported clock):
+ *
+ * - +:CLOCK_BOOTTIME+: Linux 2.6.39.
+ * - +:CLOCK_BOOTTIME_ALARM+: Linux 3.0.
+ * - +:CLOCK_MONOTONIC+: SUSv3 to 4, Linux 2.5.63, FreeBSD 3.0, NetBSD 2.0, OpenBSD 3.4, macOS 10.12, Windows-2000.
+ * - +:CLOCK_MONOTONIC_COARSE+: Linux 2.6.32.
+ * - +:CLOCK_MONOTONIC_FAST+: FreeBSD 8.1.
+ * - +:CLOCK_MONOTONIC_PRECISE+: FreeBSD 8.1.
+ * - +:CLOCK_MONOTONIC_RAW+: Linux 2.6.28, macOS 10.12.
+ * - +:CLOCK_MONOTONIC_RAW_APPROX+: macOS 10.12.
+ * - +:CLOCK_PROCESS_CPUTIME_ID+: SUSv3 to 4, Linux 2.5.63, FreeBSD 9.3, OpenBSD 5.4, macOS 10.12.
+ * - +:CLOCK_PROF+: FreeBSD 3.0, OpenBSD 2.1.
+ * - +:CLOCK_REALTIME+: SUSv2 to 4, Linux 2.5.63, FreeBSD 3.0, NetBSD 2.0, OpenBSD 2.1, macOS 10.12, Windows-8/Server-2012.
+ * Time.now is recommended over +:CLOCK_REALTIME:.
+ * - +:CLOCK_REALTIME_ALARM+: Linux 3.0.
+ * - +:CLOCK_REALTIME_COARSE+: Linux 2.6.32.
+ * - +:CLOCK_REALTIME_FAST+: FreeBSD 8.1.
+ * - +:CLOCK_REALTIME_PRECISE+: FreeBSD 8.1.
+ * - +:CLOCK_SECOND+: FreeBSD 8.1.
+ * - +:CLOCK_TAI+: Linux 3.10.
+ * - +:CLOCK_THREAD_CPUTIME_ID+: SUSv3 to 4, Linux 2.5.63, FreeBSD 7.1, OpenBSD 5.4, macOS 10.12.
+ * - +:CLOCK_UPTIME+: FreeBSD 7.0, OpenBSD 5.5.
+ * - +:CLOCK_UPTIME_FAST+: FreeBSD 8.1.
+ * - +:CLOCK_UPTIME_PRECISE+: FreeBSD 8.1.
+ * - +:CLOCK_UPTIME_RAW+: macOS 10.12.
+ * - +:CLOCK_UPTIME_RAW_APPROX+: macOS 10.12.
+ * - +:CLOCK_VIRTUAL+: FreeBSD 3.0, OpenBSD 2.1.
*
* Note that SUS stands for Single Unix Specification.
* SUS contains POSIX and clock_gettime is defined in the POSIX part.
- * SUS defines CLOCK_REALTIME mandatory but
- * CLOCK_MONOTONIC, CLOCK_PROCESS_CPUTIME_ID and CLOCK_THREAD_CPUTIME_ID are optional.
- *
- * Also, several symbols are accepted as +clock_id+.
- * There are emulations for clock_gettime().
- *
- * For example, Process::CLOCK_REALTIME is defined as
- * +:GETTIMEOFDAY_BASED_CLOCK_REALTIME+ when clock_gettime() is not available.
- *
- * Emulations for +CLOCK_REALTIME+:
- * [:GETTIMEOFDAY_BASED_CLOCK_REALTIME]
- * Use gettimeofday() defined by SUS.
- * (SUSv4 obsoleted it, though.)
- * The resolution is 1 microsecond.
- * [:TIME_BASED_CLOCK_REALTIME]
- * Use time() defined by ISO C.
- * The resolution is 1 second.
- *
- * Emulations for +CLOCK_MONOTONIC+:
- * [:MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC]
- * Use mach_absolute_time(), available on Darwin.
- * The resolution is CPU dependent.
- * [:TIMES_BASED_CLOCK_MONOTONIC]
- * Use the result value of times() defined by POSIX.
- * POSIX defines it as "times() shall return the elapsed real time, in clock ticks, since an arbitrary point in the past (for example, system start-up time)".
- * For example, GNU/Linux returns a value based on jiffies and it is monotonic.
- * However, 4.4BSD uses gettimeofday() and it is not monotonic.
- * (FreeBSD uses clock_gettime(CLOCK_MONOTONIC) instead, though.)
- * The resolution is the clock tick.
- * "getconf CLK_TCK" command shows the clock ticks per second.
- * (The clock ticks per second is defined by HZ macro in older systems.)
- * If it is 100 and clock_t is 32 bits integer type, the resolution is 10 millisecond and
- * cannot represent over 497 days.
- *
- * Emulations for +CLOCK_PROCESS_CPUTIME_ID+:
- * [:GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID]
- * Use getrusage() defined by SUS.
- * getrusage() is used with RUSAGE_SELF to obtain the time only for
- * the calling process (excluding the time for child processes).
- * The result is addition of user time (ru_utime) and system time (ru_stime).
- * The resolution is 1 microsecond.
- * [:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID]
- * Use times() defined by POSIX.
- * The result is addition of user time (tms_utime) and system time (tms_stime).
- * tms_cutime and tms_cstime are ignored to exclude the time for child processes.
- * The resolution is the clock tick.
- * "getconf CLK_TCK" command shows the clock ticks per second.
- * (The clock ticks per second is defined by HZ macro in older systems.)
- * If it is 100, the resolution is 10 millisecond.
- * [:CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID]
- * Use clock() defined by ISO C.
- * The resolution is 1/CLOCKS_PER_SEC.
- * CLOCKS_PER_SEC is the C-level macro defined by time.h.
- * SUS defines CLOCKS_PER_SEC is 1000000.
- * Non-Unix systems may define it a different value, though.
- * If CLOCKS_PER_SEC is 1000000 as SUS, the resolution is 1 microsecond.
- * If CLOCKS_PER_SEC is 1000000 and clock_t is 32 bits integer type, it cannot represent over 72 minutes.
- *
- * If the given +clock_id+ is not supported, Errno::EINVAL is raised.
- *
- * +unit+ specifies a type of the return value.
- *
- * [:float_second] number of seconds as a float (default)
- * [:float_millisecond] number of milliseconds as a float
- * [:float_microsecond] number of microseconds as a float
- * [:second] number of seconds as an integer
- * [:millisecond] number of milliseconds as an integer
- * [:microsecond] number of microseconds as an integer
- * [:nanosecond] number of nanoseconds as an integer
+ * SUS defines +:CLOCK_REALTIME+ as mandatory but
+ * +:CLOCK_MONOTONIC+, +:CLOCK_PROCESS_CPUTIME_ID+,
+ * and +:CLOCK_THREAD_CPUTIME_ID+ are optional.
+ *
+ * Certain emulations are used when the given +clock_id+
+ * is not supported directly:
+ *
+ * - Emulations for +:CLOCK_REALTIME+:
+ *
+ * - +:GETTIMEOFDAY_BASED_CLOCK_REALTIME+:
+ * Use gettimeofday() defined by SUS (deprecated in SUSv4).
+ * The resolution is 1 microsecond.
+ * - +:TIME_BASED_CLOCK_REALTIME+:
+ * Use time() defined by ISO C.
+ * The resolution is 1 second.
+ *
+ * - Emulations for +:CLOCK_MONOTONIC+:
+ *
+ * - +:MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC+:
+ * Use mach_absolute_time(), available on Darwin.
+ * The resolution is CPU dependent.
+ * - +:TIMES_BASED_CLOCK_MONOTONIC+:
+ * Use the result value of times() defined by POSIX, thus:
+ * >>>
+ * Upon successful completion, times() shall return the elapsed real time,
+ * in clock ticks, since an arbitrary point in the past
+ * (for example, system start-up time).
+ *
+ * For example, GNU/Linux returns a value based on jiffies and it is monotonic.
+ * However, 4.4BSD uses gettimeofday() and it is not monotonic.
+ * (FreeBSD uses +:CLOCK_MONOTONIC+ instead, though.)
+ *
+ * The resolution is the clock tick.
+ * "getconf CLK_TCK" command shows the clock ticks per second.
+ * (The clock ticks-per-second is defined by HZ macro in older systems.)
+ * If it is 100 and clock_t is 32 bits integer type,
+ * the resolution is 10 millisecond and cannot represent over 497 days.
+ *
+ * - Emulations for +:CLOCK_PROCESS_CPUTIME_ID+:
+ *
+ * - +:GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID+:
+ * Use getrusage() defined by SUS.
+ * getrusage() is used with RUSAGE_SELF to obtain the time only for
+ * the calling process (excluding the time for child processes).
+ * The result is addition of user time (ru_utime) and system time (ru_stime).
+ * The resolution is 1 microsecond.
+ * - +:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID+:
+ * Use times() defined by POSIX.
+ * The result is addition of user time (tms_utime) and system time (tms_stime).
+ * tms_cutime and tms_cstime are ignored to exclude the time for child processes.
+ * The resolution is the clock tick.
+ * "getconf CLK_TCK" command shows the clock ticks per second.
+ * (The clock ticks per second is defined by HZ macro in older systems.)
+ * If it is 100, the resolution is 10 millisecond.
+ * - +:CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID+:
+ * Use clock() defined by ISO C.
+ * The resolution is <tt>1/CLOCKS_PER_SEC</tt>.
+ * +CLOCKS_PER_SEC+ is the C-level macro defined by time.h.
+ * SUS defines +CLOCKS_PER_SEC+ as 1000000;
+ * other systems may define it differently.
+ * If +CLOCKS_PER_SEC+ is 1000000 (as in SUS),
+ * the resolution is 1 microsecond.
+ * If +CLOCKS_PER_SEC+ is 1000000 and clock_t is a 32-bit integer type,
+ * it cannot represent over 72 minutes.
+ *
+ * <b>Argument +unit+</b>
+ *
+ * Optional argument +unit+ (default +:float_second+)
+ * specifies the unit for the returned value.
+ *
+ * - +:float_microsecond+: Number of microseconds as a float.
+ * - +:float_millisecond+: Number of milliseconds as a float.
+ * - +:float_second+: Number of seconds as a float.
+ * - +:microsecond+: Number of microseconds as an integer.
+ * - +:millisecond+: Number of milliseconds as an integer.
+ * - +:nanosecond+: Number of nanoseconds as an integer.
+ * - +::second+: Number of seconds as an integer.
+ *
+ * Examples:
+ *
+ * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :float_microsecond)
+ * # => 203605054.825
+ * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :float_millisecond)
+ * # => 203643.696848
+ * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :float_second)
+ * # => 203.762181929
+ * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :microsecond)
+ * # => 204123212
+ * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :millisecond)
+ * # => 204298
+ * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :nanosecond)
+ * # => 204602286036
+ * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :second)
+ * # => 204
*
* The underlying function, clock_gettime(), returns a number of nanoseconds.
* Float object (IEEE 754 double) is not enough to represent
- * the return value for CLOCK_REALTIME.
+ * the return value for +:CLOCK_REALTIME+.
* If the exact nanoseconds value is required, use +:nanosecond+ as the +unit+.
*
- * The origin (zero) of the returned value varies.
- * For example, system start up time, process start up time, the Epoch, etc.
+ * The origin (time zero) of the returned value is system-dependent,
+ * and may be, for example, system start up time,
+ * process start up time, the Epoch, etc.
*
- * The origin in CLOCK_REALTIME is defined as the Epoch
- * (1970-01-01 00:00:00 UTC).
- * But some systems count leap seconds and others doesn't.
- * So the result can be interpreted differently across systems.
- * Time.now is recommended over CLOCK_REALTIME.
+ * The origin in +:CLOCK_REALTIME+ is defined as the Epoch:
+ * <tt>1970-01-01 00:00:00 UTC</tt>;
+ * some systems count leap seconds and others don't,
+ * so the result may vary across systems.
*/
static VALUE
rb_clock_gettime(int argc, VALUE *argv, VALUE _)
@@ -8117,7 +8294,9 @@ rb_clock_gettime(int argc, VALUE *argv, VALUE _)
VALUE unit = (rb_check_arity(argc, 1, 2) == 2) ? argv[1] : Qnil;
VALUE clk_id = argv[0];
+#ifdef HAVE_CLOCK_GETTIME
clockid_t c;
+#endif
if (SYMBOL_P(clk_id)) {
#ifdef CLOCK_REALTIME
@@ -8295,45 +8474,39 @@ rb_clock_gettime(int argc, VALUE *argv, VALUE _)
/*
* call-seq:
- * Process.clock_getres(clock_id [, unit]) -> number
- *
- * Returns an estimate of the resolution of a +clock_id+ using the POSIX
- * <code>clock_getres()</code> function.
+ * Process.clock_getres(clock_id, unit = :float_second) -> number
*
- * Note the reported resolution is often inaccurate on most platforms due to
- * underlying bugs for this function and therefore the reported resolution
- * often differs from the actual resolution of the clock in practice.
- * Inaccurate reported resolutions have been observed for various clocks including
- * CLOCK_MONOTONIC and CLOCK_MONOTONIC_RAW when using Linux, macOS, BSD or AIX
- * platforms, when using ARM processors, or when using virtualization.
+ * Returns a clock resolution as determined by POSIX function
+ * {clock_getres()}[https://man7.org/linux/man-pages/man3/clock_getres.3.html]:
*
- * +clock_id+ specifies a kind of clock.
- * See the document of +Process.clock_gettime+ for details.
- * +clock_id+ can be a symbol as for +Process.clock_gettime+.
+ * Process.clock_getres(:CLOCK_REALTIME) # => 1.0e-09
*
- * If the given +clock_id+ is not supported, Errno::EINVAL is raised.
+ * See Process.clock_gettime for the values of +clock_id+ and +unit+.
*
- * +unit+ specifies the type of the return value.
- * +Process.clock_getres+ accepts +unit+ as +Process.clock_gettime+.
- * The default value, +:float_second+, is also the same as
- * +Process.clock_gettime+.
+ * Examples:
*
- * +Process.clock_getres+ also accepts +:hertz+ as +unit+.
- * +:hertz+ means the reciprocal of +:float_second+.
+ * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :float_microsecond) # => 0.001
+ * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :float_millisecond) # => 1.0e-06
+ * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :float_second) # => 1.0e-09
+ * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :microsecond) # => 0
+ * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :millisecond) # => 0
+ * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :nanosecond) # => 1
+ * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :second) # => 0
*
- * +:hertz+ can be used to obtain the exact value of
- * the clock ticks per second for the times() function and
- * CLOCKS_PER_SEC for the clock() function.
+ * In addition to the values for +unit+ supported in Process.clock_gettime,
+ * this method supports +:hertz+, the integer number of clock ticks per second
+ * (which is the reciprocal of +:float_second+):
*
- * <code>Process.clock_getres(:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID, :hertz)</code>
- * returns the clock ticks per second.
- *
- * <code>Process.clock_getres(:CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID, :hertz)</code>
- * returns CLOCKS_PER_SEC.
- *
- * p Process.clock_getres(Process::CLOCK_MONOTONIC)
- * #=> 1.0e-09
+ * Process.clock_getres(:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID, :hertz) # => 100.0
+ * Process.clock_getres(:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID, :float_second) # => 0.01
*
+ * <b>Accuracy</b>:
+ * Note that the returned resolution may be inaccurate on some platforms
+ * due to underlying bugs.
+ * Inaccurate resolutions have been reported for various clocks including
+ * +:CLOCK_MONOTONIC+ and +:CLOCK_MONOTONIC_RAW+
+ * on Linux, macOS, BSD or AIX platforms, when using ARM processors,
+ * or when using virtualization.
*/
static VALUE
rb_clock_getres(int argc, VALUE *argv, VALUE _)
@@ -8345,7 +8518,9 @@ rb_clock_getres(int argc, VALUE *argv, VALUE _)
timetick_int_t denominators[2];
int num_numerators = 0;
int num_denominators = 0;
+#ifdef HAVE_CLOCK_GETRES
clockid_t c;
+#endif
VALUE unit = (rb_check_arity(argc, 1, 2) == 2) ? argv[1] : Qnil;
VALUE clk_id = argv[0];
@@ -8487,38 +8662,82 @@ get_PROCESS_ID(ID _x, VALUE *_y)
/*
* call-seq:
- * Process.kill(signal, pid, *pids) -> integer
- *
- * Sends the given signal to the specified process id(s) if _pid_ is positive.
- * If _pid_ is zero, _signal_ is sent to all processes whose group ID is equal
- * to the group ID of the process. If _pid_ is negative, results are dependent
- * on the operating system. _signal_ may be an integer signal number or
- * a POSIX signal name (either with or without a +SIG+ prefix). If _signal_ is
- * negative (or starts with a minus sign), kills process groups instead of
- * processes. Not all signals are available on all platforms.
- * The keys and values of Signal.list are known signal names and numbers,
- * respectively.
- *
- * pid = fork do
- * Signal.trap("HUP") { puts "Ouch!"; exit }
- * # ... do some work ...
- * end
- * # ...
- * Process.kill("HUP", pid)
- * Process.wait
- *
- * <em>produces:</em>
+ * Process.kill(signal, *ids) -> count
+ *
+ * Sends a signal to each process specified by +ids+
+ * (which must specify at least one ID);
+ * returns the count of signals sent.
+ *
+ * For each given +id+, if +id+ is:
+ *
+ * - Positive, sends the signal to the process whose process ID is +id+.
+ * - Zero, send the signal to all processes in the current process group.
+ * - Negative, sends the signal to a system-dependent collection of processes.
+ *
+ * Argument +signal+ specifies the signal to be sent;
+ * the argument may be:
+ *
+ * - An integer signal number: e.g., +-29+, +0+, +29+.
+ * - A signal name (string), with or without leading <tt>'SIG'</tt>,
+ * and with or without a further prefixed minus sign (<tt>'-'</tt>):
+ * e.g.:
+ *
+ * - <tt>'SIGPOLL'</tt>.
+ * - <tt>'POLL'</tt>,
+ * - <tt>'-SIGPOLL'</tt>.
+ * - <tt>'-POLL'</tt>.
+ *
+ * - A signal symbol, with or without leading <tt>'SIG'</tt>,
+ * and with or without a further prefixed minus sign (<tt>'-'</tt>):
+ * e.g.:
+ *
+ * - +:SIGPOLL+.
+ * - +:POLL+.
+ * - <tt>:'-SIGPOLL'</tt>.
+ * - <tt>:'-POLL'</tt>.
+ *
+ * If +signal+ is:
+ *
+ * - A non-negative integer, or a signal name or symbol
+ * without prefixed <tt>'-'</tt>,
+ * each process with process ID +id+ is signalled.
+ * - A negative integer, or a signal name or symbol
+ * with prefixed <tt>'-'</tt>,
+ * each process group with group ID +id+ is signalled.
+ *
+ * Use method Signal.list to see which signals are supported
+ * by Ruby on the underlying platform;
+ * the method returns a hash of the string names
+ * and non-negative integer values of the supported signals.
+ * The size and content of the returned hash varies widely
+ * among platforms.
+ *
+ * Additionally, signal +0+ is useful to determine if the process exists.
+ *
+ * Example:
+ *
+ * pid = fork do
+ * Signal.trap('HUP') { puts 'Ouch!'; exit }
+ * # ... do some work ...
+ * end
+ * # ...
+ * Process.kill('HUP', pid)
+ * Process.wait
+ *
+ * Output:
*
* Ouch!
*
- * If _signal_ is an integer but wrong for signal, Errno::EINVAL or
- * RangeError will be raised. Otherwise unless _signal_ is a String
- * or a Symbol, and a known signal name, ArgumentError will be
- * raised.
+ * Exceptions:
+ *
+ * - Raises Errno::EINVAL or RangeError if +signal+ is an integer
+ * but invalid.
+ * - Raises ArgumentError if +signal+ is a string or symbol
+ * but invalid.
+ * - Raises Errno::ESRCH or RangeError if one of +ids+ is invalid.
+ * - Raises Errno::EPERM if needed permissions are not in force.
*
- * Also, Errno::ESRCH or RangeError for invalid _pid_, Errno::EPERM
- * when failed because of no privilege, will be raised. In these
- * cases, signals may have been sent to preceding processes.
+ * In the last two cases, signals may have been sent to some processes.
*/
static VALUE
@@ -8532,23 +8751,456 @@ static VALUE rb_mProcUID;
static VALUE rb_mProcGID;
static VALUE rb_mProcID_Syscall;
+/*
+ * call-seq:
+ * Process.warmup -> true
+ *
+ * Notify the Ruby virtual machine that the boot sequence is finished,
+ * and that now is a good time to optimize the application. This is useful
+ * for long running applications.
+ *
+ * This method is expected to be called at the end of the application boot.
+ * If the application is deployed using a pre-forking model, +Process.warmup+
+ * should be called in the original process before the first fork.
+ *
+ * The actual optimizations performed are entirely implementation specific
+ * and may change in the future without notice.
+ *
+ * On CRuby, +Process.warmup+:
+ *
+ * * Performs a major GC.
+ * * Compacts the heap.
+ * * Promotes all surviving objects to the old generation.
+ * * Precomputes the coderange of all strings.
+ * * Frees all empty heap pages and increments the allocatable pages counter
+ * by the number of pages freed.
+ * * Invoke +malloc_trim+ if available to free empty malloc pages.
+ */
+
+static VALUE
+proc_warmup(VALUE _)
+{
+ RB_VM_LOCK_ENTER();
+ rb_gc_prepare_heap();
+ RB_VM_LOCK_LEAVE();
+ return Qtrue;
+}
/*
* Document-module: Process
*
- * The module contains several groups of functionality for handling OS processes:
- *
- * * Low-level property introspection and management of the current process, like
- * Process.argv0, Process.pid;
- * * Low-level introspection of other processes, like Process.getpgid, Process.getpriority;
- * * Management of the current process: Process.abort, Process.exit, Process.daemon, etc.
- * (for convenience, most of those are also available as global functions
- * and module functions of Kernel);
- * * Creation and management of child processes: Process.fork, Process.spawn, and
- * related methods;
- * * Management of low-level system clock: Process.times and Process.clock_gettime,
- * which could be important for proper benchmarking and other elapsed
- * time measurement tasks.
+ * \Module +Process+ represents a process in the underlying operating system.
+ * Its methods support management of the current process and its child processes.
+ *
+ * == \Process Creation
+ *
+ * Each of the following methods executes a given command in a new process or subshell,
+ * or multiple commands in new processes and/or subshells.
+ * The choice of process or subshell depends on the form of the command;
+ * see {Argument command_line or exe_path}[rdoc-ref:Process@Argument+command_line+or+exe_path].
+ *
+ * - Process.spawn, Kernel#spawn: Executes the command;
+ * returns the new pid without waiting for completion.
+ * - Process.exec: Replaces the current process by executing the command.
+ *
+ * In addition:
+ *
+ * - \Method Kernel#system executes a given command-line (string) in a subshell;
+ * returns +true+, +false+, or +nil+.
+ * - \Method Kernel#` executes a given command-line (string) in a subshell;
+ * returns its $stdout string.
+ * - \Module Open3 supports creating child processes
+ * with access to their $stdin, $stdout, and $stderr streams.
+ *
+ * === Execution Environment
+ *
+ * Optional leading argument +env+ is a hash of name/value pairs,
+ * where each name is a string and each value is a string or +nil+;
+ * each name/value pair is added to ENV in the new process.
+ *
+ * Process.spawn( 'ruby -e "p ENV[\"Foo\"]"')
+ * Process.spawn({'Foo' => '0'}, 'ruby -e "p ENV[\"Foo\"]"')
+ *
+ * Output:
+ *
+ * "0"
+ *
+ * The effect is usually similar to that of calling ENV#update with argument +env+,
+ * where each named environment variable is created or updated
+ * (if the value is non-+nil+),
+ * or deleted (if the value is +nil+).
+ *
+ * However, some modifications to the calling process may remain
+ * if the new process fails.
+ * For example, hard resource limits are not restored.
+ *
+ * === Argument +command_line+ or +exe_path+
+ *
+ * The required string argument is one of the following:
+ *
+ * - +command_line+ if it begins with a shell reserved word or special built-in,
+ * or if it contains one or more meta characters.
+ * - +exe_path+ otherwise.
+ *
+ * ==== Argument +command_line+
+ *
+ * \String argument +command_line+ is a command line to be passed to a shell;
+ * it must begin with a shell reserved word, begin with a special built-in,
+ * or contain meta characters:
+ *
+ * system('if true; then echo "Foo"; fi') # => true # Shell reserved word.
+ * system('exit') # => true # Built-in.
+ * system('date > /tmp/date.tmp') # => true # Contains meta character.
+ * system('date > /nop/date.tmp') # => false
+ * system('date > /nop/date.tmp', exception: true) # Raises RuntimeError.
+ *
+ * The command line may also contain arguments and options for the command:
+ *
+ * system('echo "Foo"') # => true
+ *
+ * Output:
+ *
+ * Foo
+ *
+ * See {Execution Shell}[rdoc-ref:Process@Execution+Shell] for details about the shell.
+ *
+ * ==== Argument +exe_path+
+ *
+ * Argument +exe_path+ is one of the following:
+ *
+ * - The string path to an executable file to be called:
+ *
+ * Example:
+ *
+ * system('/usr/bin/date') # => true # Path to date on Unix-style system.
+ * system('foo') # => nil # Command execlution failed.
+ *
+ * Output:
+ *
+ * Thu Aug 31 10:06:48 AM CDT 2023
+ *
+ * A path or command name containing spaces without arguments cannot
+ * be distinguished from +command_line+ above, so you must quote or
+ * escape the entire command name using a shell in platform
+ * dependent manner, or use the array form below.
+ *
+ * If +exe_path+ does not contain any path separator, an executable
+ * file is searched from directories specified with the +PATH+
+ * environment variable. What the word "executable" means here is
+ * depending on platforms.
+ *
+ * Even if the file considered "executable", its content may not be
+ * in proper executable format. In that case, Ruby tries to run it
+ * by using <tt>/bin/sh</tt> on a Unix-like system, like system(3)
+ * does.
+ *
+ * File.write('shell_command', 'echo $SHELL', perm: 0o755)
+ * system('./shell_command') # prints "/bin/sh" or something.
+ *
+ * - A 2-element array containing the path to an executable
+ * and the string to be used as the name of the executing process:
+ *
+ * Example:
+ *
+ * pid = spawn(['sleep', 'Hello!'], '1') # 2-element array.
+ * p `ps -p #{pid} -o command=`
+ *
+ * Output:
+ *
+ * "Hello! 1\n"
+ *
+ * === Arguments +args+
+ *
+ * If +command_line+ does not contain shell meta characters except for
+ * spaces and tabs, or +exe_path+ is given, Ruby invokes the
+ * executable directly. This form does not use the shell:
+ *
+ * spawn("doesnt_exist") # Raises Errno::ENOENT
+ * spawn("doesnt_exist", "\n") # Raises Errno::ENOENT
+ *
+ * spawn("doesnt_exist\n") # => false
+ * # sh: 1: doesnot_exist: not found
+ *
+ * The error message is from a shell and would vary depending on your
+ * system.
+ *
+ * If one or more +args+ is given after +exe_path+, each is an
+ * argument or option to be passed to the executable:
+ *
+ * Example:
+ *
+ * system('echo', '<', 'C*', '|', '$SHELL', '>') # => true
+ *
+ * Output:
+ *
+ * < C* | $SHELL >
+ *
+ * However, there are exceptions on Windows. See {Execution Shell on
+ * Windows}[rdoc-ref:Process@Execution+Shell+on+Windows].
+ *
+ * If you want to invoke a path containing spaces with no arguments
+ * without shell, you will need to use a 2-element array +exe_path+.
+ *
+ * Example:
+ *
+ * path = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
+ * spawn(path) # Raises Errno::ENOENT; No such file or directory - /Applications/Google
+ * spawn([path] * 2)
+ *
+ * === Execution Options
+ *
+ * Optional trailing argument +options+ is a hash of execution options.
+ *
+ * ==== Working Directory (+:chdir+)
+ *
+ * By default, the working directory for the new process is the same as
+ * that of the current process:
+ *
+ * Dir.chdir('/var')
+ * Process.spawn('ruby -e "puts Dir.pwd"')
+ *
+ * Output:
+ *
+ * /var
+ *
+ * Use option +:chdir+ to set the working directory for the new process:
+ *
+ * Process.spawn('ruby -e "puts Dir.pwd"', {chdir: '/tmp'})
+ *
+ * Output:
+ *
+ * /tmp
+ *
+ * The working directory of the current process is not changed:
+ *
+ * Dir.pwd # => "/var"
+ *
+ * ==== \File Redirection (\File Descriptor)
+ *
+ * Use execution options for file redirection in the new process.
+ *
+ * The key for such an option may be an integer file descriptor (fd),
+ * specifying a source,
+ * or an array of fds, specifying multiple sources.
+ *
+ * An integer source fd may be specified as:
+ *
+ * - _n_: Specifies file descriptor _n_.
+ *
+ * There are these shorthand symbols for fds:
+ *
+ * - +:in+: Specifies file descriptor 0 (STDIN).
+ * - +:out+: Specifies file descriptor 1 (STDOUT).
+ * - +:err+: Specifies file descriptor 2 (STDERR).
+ *
+ * The value given with a source is one of:
+ *
+ * - _n_:
+ * Redirects to fd _n_ in the parent process.
+ * - +filepath+:
+ * Redirects from or to the file at +filepath+ via <tt>open(filepath, mode, 0644)</tt>,
+ * where +mode+ is <tt>'r'</tt> for source +:in+,
+ * or <tt>'w'</tt> for source +:out+ or +:err+.
+ * - <tt>[filepath]</tt>:
+ * Redirects from the file at +filepath+ via <tt>open(filepath, 'r', 0644)</tt>.
+ * - <tt>[filepath, mode]</tt>:
+ * Redirects from or to the file at +filepath+ via <tt>open(filepath, mode, 0644)</tt>.
+ * - <tt>[filepath, mode, perm]</tt>:
+ * Redirects from or to the file at +filepath+ via <tt>open(filepath, mode, perm)</tt>.
+ * - <tt>[:child, fd]</tt>:
+ * Redirects to the redirected +fd+.
+ * - +:close+: Closes the file descriptor in child process.
+ *
+ * See {Access Modes}[rdoc-ref:File@Access+Modes]
+ * and {File Permissions}[rdoc-ref:File@File+Permissions].
+ *
+ * ==== Environment Variables (+:unsetenv_others+)
+ *
+ * By default, the new process inherits environment variables
+ * from the parent process;
+ * use execution option key +:unsetenv_others+ with value +true+
+ * to clear environment variables in the new process.
+ *
+ * Any changes specified by execution option +env+ are made after the new process
+ * inherits or clears its environment variables;
+ * see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
+ *
+ * ==== \File-Creation Access (+:umask+)
+ *
+ * Use execution option +:umask+ to set the file-creation access
+ * for the new process;
+ * see {Access Modes}[rdoc-ref:File@Access+Modes]:
+ *
+ * command = 'ruby -e "puts sprintf(\"0%o\", File.umask)"'
+ * options = {:umask => 0644}
+ * Process.spawn(command, options)
+ *
+ * Output:
+ *
+ * 0644
+ *
+ * ==== \Process Groups (+:pgroup+ and +:new_pgroup+)
+ *
+ * By default, the new process belongs to the same
+ * {process group}[https://en.wikipedia.org/wiki/Process_group]
+ * as the parent process.
+ *
+ * To specify a different process group.
+ * use execution option +:pgroup+ with one of the following values:
+ *
+ * - +true+: Create a new process group for the new process.
+ * - _pgid_: Create the new process in the process group
+ * whose id is _pgid_.
+ *
+ * On Windows only, use execution option +:new_pgroup+ with value +true+
+ * to create a new process group for the new process.
+ *
+ * ==== Resource Limits
+ *
+ * Use execution options to set resource limits.
+ *
+ * The keys for these options are symbols of the form
+ * <tt>:rlimit_<i>resource_name</i></tt>,
+ * where _resource_name_ is the downcased form of one of the string
+ * resource names described at method Process.setrlimit.
+ * For example, key +:rlimit_cpu+ corresponds to resource limit <tt>'CPU'</tt>.
+ *
+ * The value for such as key is one of:
+ *
+ * - An integer, specifying both the current and maximum limits.
+ * - A 2-element array of integers, specifying the current and maximum limits.
+ *
+ * ==== \File Descriptor Inheritance
+ *
+ * By default, the new process inherits file descriptors from the parent process.
+ *
+ * Use execution option <tt>:close_others => true</tt> to modify that inheritance
+ * by closing non-standard fds (3 and greater) that are not otherwise redirected.
+ *
+ * === Execution Shell
+ *
+ * On a Unix-like system, the shell invoked is <tt>/bin/sh</tt>;
+ * the entire string +command_line+ is passed as an argument
+ * to {shell option -c}[https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/utilities/sh.html].
+ *
+ * The shell performs normal shell expansion on the command line:
+ *
+ * Example:
+ *
+ * system('echo $SHELL: C*') # => true
+ *
+ * Output:
+ *
+ * /bin/bash: CONTRIBUTING.md COPYING COPYING.ja
+ *
+ * ==== Execution Shell on Windows
+ *
+ * On Windows, the shell invoked is determined by environment variable
+ * +RUBYSHELL+, if defined, or +COMSPEC+ otherwise; the entire string
+ * +command_line+ is passed as an argument to <tt>-c</tt> option for
+ * +RUBYSHELL+, as well as <tt>/bin/sh</tt>, and {/c
+ * option}[https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/cmd]
+ * for +COMSPEC+. The shell is invoked automatically in the following
+ * cases:
+ *
+ * - The command is a built-in of +cmd.exe+, such as +echo+.
+ * - The executable file is a batch file; its name ends with +.bat+ or
+ * +.cmd+.
+ *
+ * Note that the command will still be invoked as +command_line+ form
+ * even when called in +exe_path+ form, because +cmd.exe+ does not
+ * accept a script name like <tt>/bin/sh</tt> does but only works with
+ * <tt>/c</tt> option.
+ *
+ * The standard shell +cmd.exe+ performs environment variable
+ * expansion but does not have globbing functionality:
+ *
+ * Example:
+ *
+ * system("echo %COMSPEC%: C*")' # => true
+ *
+ * Output:
+ *
+ * C:\WINDOWS\system32\cmd.exe: C*
+ *
+ * == What's Here
+ *
+ * === Current-Process Getters
+ *
+ * - ::argv0: Returns the process name as a frozen string.
+ * - ::egid: Returns the effective group ID.
+ * - ::euid: Returns the effective user ID.
+ * - ::getpgrp: Return the process group ID.
+ * - ::getrlimit: Returns the resource limit.
+ * - ::gid: Returns the (real) group ID.
+ * - ::pid: Returns the process ID.
+ * - ::ppid: Returns the process ID of the parent process.
+ * - ::uid: Returns the (real) user ID.
+ *
+ * === Current-Process Setters
+ *
+ * - ::egid=: Sets the effective group ID.
+ * - ::euid=: Sets the effective user ID.
+ * - ::gid=: Sets the (real) group ID.
+ * - ::setproctitle: Sets the process title.
+ * - ::setpgrp: Sets the process group ID of the process to zero.
+ * - ::setrlimit: Sets a resource limit.
+ * - ::setsid: Establishes the process as a new session and process group leader,
+ * with no controlling tty.
+ * - ::uid=: Sets the user ID.
+ *
+ * === Current-Process Execution
+ *
+ * - ::abort: Immediately terminates the process.
+ * - ::daemon: Detaches the process from its controlling terminal
+ * and continues running it in the background as system daemon.
+ * - ::exec: Replaces the process by running a given external command.
+ * - ::exit: Initiates process termination by raising exception SystemExit
+ * (which may be caught).
+ * - ::exit!: Immediately exits the process.
+ * - ::warmup: Notifies the Ruby virtual machine that the boot sequence
+ * for the application is completed,
+ * and that the VM may begin optimizing the application.
+ *
+ * === Child Processes
+ *
+ * - ::detach: Guards against a child process becoming a zombie.
+ * - ::fork: Creates a child process.
+ * - ::kill: Sends a given signal to processes.
+ * - ::spawn: Creates a child process.
+ * - ::wait, ::waitpid: Waits for a child process to exit; returns its process ID.
+ * - ::wait2, ::waitpid2: Waits for a child process to exit; returns its process ID and status.
+ * - ::waitall: Waits for all child processes to exit;
+ * returns their process IDs and statuses.
+ *
+ * === \Process Groups
+ *
+ * - ::getpgid: Returns the process group ID for a process.
+ * - ::getpriority: Returns the scheduling priority
+ * for a process, process group, or user.
+ * - ::getsid: Returns the session ID for a process.
+ * - ::groups: Returns an array of the group IDs
+ * in the supplemental group access list for this process.
+ * - ::groups=: Sets the supplemental group access list
+ * to the given array of group IDs.
+ * - ::initgroups: Initializes the supplemental group access list.
+ * - ::last_status: Returns the status of the last executed child process
+ * in the current thread.
+ * - ::maxgroups: Returns the maximum number of group IDs allowed
+ * in the supplemental group access list.
+ * - ::maxgroups=: Sets the maximum number of group IDs allowed
+ * in the supplemental group access list.
+ * - ::setpgid: Sets the process group ID of a process.
+ * - ::setpriority: Sets the scheduling priority
+ * for a process, process group, or user.
+ *
+ * === Timing
+ *
+ * - ::clock_getres: Returns the resolution of a system clock.
+ * - ::clock_gettime: Returns the time from a system clock.
+ * - ::times: Returns a Process::Tms object containing times
+ * for the current process and its child processes.
+ *
*/
void
@@ -8649,6 +9301,8 @@ InitVM_process(void)
rb_define_module_function(rb_mProcess, "getpriority", proc_getpriority, 2);
rb_define_module_function(rb_mProcess, "setpriority", proc_setpriority, 3);
+ rb_define_module_function(rb_mProcess, "warmup", proc_warmup, 0);
+
#ifdef HAVE_GETPRIORITY
/* see Process.setpriority */
rb_define_const(rb_mProcess, "PRIO_PROCESS", INT2FIX(PRIO_PROCESS));
diff --git a/ractor.c b/ractor.c
index 77acf3a97f..f6b2b22c9a 100644
--- a/ractor.c
+++ b/ractor.c
@@ -16,7 +16,6 @@
#include "internal/struct.h"
#include "internal/thread.h"
#include "variable.h"
-#include "transient_heap.h"
#include "yjit.h"
#include "rjit.h"
@@ -113,18 +112,16 @@ ractor_unlock_self(rb_ractor_t *cr, const char *file, int line)
#define RACTOR_LOCK_SELF(r) ractor_lock_self(r, __FILE__, __LINE__)
#define RACTOR_UNLOCK_SELF(r) ractor_unlock_self(r, __FILE__, __LINE__)
-static void
-ractor_cond_wait(rb_ractor_t *r)
+void
+rb_ractor_lock_self(rb_ractor_t *r)
{
-#if RACTOR_CHECK_MODE > 0
- VALUE locked_by = r->sync.locked_by;
- r->sync.locked_by = Qnil;
-#endif
- rb_native_cond_wait(&r->sync.cond, &r->sync.lock);
+ RACTOR_LOCK_SELF(r);
+}
-#if RACTOR_CHECK_MODE > 0
- r->sync.locked_by = locked_by;
-#endif
+void
+rb_ractor_unlock_self(rb_ractor_t *r)
+{
+ RACTOR_UNLOCK_SELF(r);
}
// Ractor status
@@ -244,7 +241,9 @@ ractor_free(void *ptr)
rb_ractor_t *r = (rb_ractor_t *)ptr;
RUBY_DEBUG_LOG("free r:%d", rb_ractor_id(r));
rb_native_mutex_destroy(&r->sync.lock);
+#ifdef RUBY_THREAD_WIN32_H
rb_native_cond_destroy(&r->sync.cond);
+#endif
ractor_queue_free(&r->sync.recv_queue);
ractor_queue_free(&r->sync.takers_queue);
ractor_local_storage_free(r);
@@ -532,6 +531,19 @@ ractor_sleeping_by(const rb_ractor_t *r, enum rb_ractor_wait_status wait_status)
return (r->sync.wait.status & wait_status) && r->sync.wait.wakeup_status == wakeup_none;
}
+#ifdef RUBY_THREAD_PTHREAD_H
+// thread_*.c
+void rb_ractor_sched_wakeup(rb_ractor_t *r);
+#else
+
+static void
+rb_ractor_sched_wakeup(rb_ractor_t *r)
+{
+ rb_native_cond_broadcast(&r->sync.cond);
+}
+#endif
+
+
static bool
ractor_wakeup(rb_ractor_t *r, enum rb_ractor_wait_status wait_status, enum rb_ractor_wakeup_status wakeup_status)
{
@@ -545,7 +557,7 @@ ractor_wakeup(rb_ractor_t *r, enum rb_ractor_wait_status wait_status, enum rb_ra
if (ractor_sleeping_by(r, wait_status)) {
r->sync.wait.wakeup_status = wakeup_status;
- rb_native_cond_broadcast(&r->sync.cond);
+ rb_ractor_sched_wakeup(r);
return true;
}
else {
@@ -553,6 +565,73 @@ ractor_wakeup(rb_ractor_t *r, enum rb_ractor_wait_status wait_status, enum rb_ra
}
}
+static void
+ractor_sleep_interrupt(void *ptr)
+{
+ rb_ractor_t *r = ptr;
+
+ RACTOR_LOCK(r);
+ {
+ ractor_wakeup(r, wait_receiving | wait_taking | wait_yielding, wakeup_by_interrupt);
+ }
+ RACTOR_UNLOCK(r);
+}
+
+typedef void (*ractor_sleep_cleanup_function)(rb_ractor_t *cr, void *p);
+
+static void
+ractor_check_ints(rb_execution_context_t *ec, rb_ractor_t *cr, ractor_sleep_cleanup_function cf_func, void *cf_data)
+{
+ if (cr->sync.wait.status != wait_none) {
+ enum rb_ractor_wait_status prev_wait_status = cr->sync.wait.status;
+ cr->sync.wait.status = wait_none;
+ cr->sync.wait.wakeup_status = wakeup_by_interrupt;
+
+ RACTOR_UNLOCK(cr);
+ {
+ if (cf_func) {
+ enum ruby_tag_type state;
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ rb_thread_check_ints();
+ }
+ EC_POP_TAG();
+
+ if (state) {
+ (*cf_func)(cr, cf_data);
+ EC_JUMP_TAG(ec, state);
+ }
+ }
+ else {
+ rb_thread_check_ints();
+ }
+ }
+
+ // reachable?
+ RACTOR_LOCK(cr);
+ cr->sync.wait.status = prev_wait_status;
+ }
+}
+
+#ifdef RUBY_THREAD_PTHREAD_H
+void rb_ractor_sched_sleep(rb_execution_context_t *ec, rb_ractor_t *cr, rb_unblock_function_t *ubf);
+#else
+
+// win32
+static void
+ractor_cond_wait(rb_ractor_t *r)
+{
+#if RACTOR_CHECK_MODE > 0
+ VALUE locked_by = r->sync.locked_by;
+ r->sync.locked_by = Qnil;
+#endif
+ rb_native_cond_wait(&r->sync.cond, &r->sync.lock);
+
+#if RACTOR_CHECK_MODE > 0
+ r->sync.locked_by = locked_by;
+#endif
+}
+
static void *
ractor_sleep_wo_gvl(void *ptr)
{
@@ -570,18 +649,17 @@ ractor_sleep_wo_gvl(void *ptr)
}
static void
-ractor_sleep_interrupt(void *ptr)
+rb_ractor_sched_sleep(rb_execution_context_t *ec, rb_ractor_t *cr, rb_unblock_function_t *ubf)
{
- rb_ractor_t *r = ptr;
-
- RACTOR_LOCK(r);
+ RACTOR_UNLOCK(cr);
{
- ractor_wakeup(r, wait_receiving | wait_taking | wait_yielding, wakeup_by_interrupt);
+ rb_nogvl(ractor_sleep_wo_gvl, cr,
+ ubf, cr,
+ RB_NOGVL_UBF_ASYNC_SAFE | RB_NOGVL_INTR_FAIL);
}
- RACTOR_UNLOCK(r);
+ RACTOR_LOCK(cr);
}
-
-typedef void (*ractor_sleep_cleanup_function)(rb_ractor_t *cr, void *p);
+#endif
static enum rb_ractor_wakeup_status
ractor_sleep_with_cleanup(rb_execution_context_t *ec, rb_ractor_t *cr, enum rb_ractor_wait_status wait_status,
@@ -601,40 +679,12 @@ ractor_sleep_with_cleanup(rb_execution_context_t *ec, rb_ractor_t *cr, enum rb_r
RUBY_DEBUG_LOG("sleep by %s", wait_status_str(wait_status));
- RACTOR_UNLOCK(cr);
- {
- rb_nogvl(ractor_sleep_wo_gvl, cr,
- ractor_sleep_interrupt, cr,
- RB_NOGVL_UBF_ASYNC_SAFE | RB_NOGVL_INTR_FAIL);
+ while (cr->sync.wait.wakeup_status == wakeup_none) {
+ rb_ractor_sched_sleep(ec, cr, ractor_sleep_interrupt);
+ ractor_check_ints(ec, cr, cf_func, cf_data);
}
- RACTOR_LOCK(cr);
-
- // rb_nogvl() can be canceled by interrupts
- if (cr->sync.wait.status != wait_none) {
- cr->sync.wait.status = wait_none;
- cr->sync.wait.wakeup_status = wakeup_by_interrupt;
-
- RACTOR_UNLOCK(cr);
- {
- if (cf_func) {
- int state;
- EC_PUSH_TAG(ec);
- if ((state = EC_EXEC_TAG()) == TAG_NONE) {
- rb_thread_check_ints();
- }
- EC_POP_TAG();
- if (state) {
- (*cf_func)(cr, cf_data);
- EC_JUMP_TAG(ec, state);
- }
- }
- else {
- rb_thread_check_ints();
- }
- }
- RACTOR_LOCK(cr); // reachable?
- }
+ cr->sync.wait.status = wait_none;
// TODO: multi-thread
wakeup_status = cr->sync.wait.wakeup_status;
@@ -648,7 +698,7 @@ ractor_sleep_with_cleanup(rb_execution_context_t *ec, rb_ractor_t *cr, enum rb_r
static enum rb_ractor_wakeup_status
ractor_sleep(rb_execution_context_t *ec, rb_ractor_t *cr, enum rb_ractor_wait_status wait_status)
{
- return ractor_sleep_with_cleanup(ec, cr, wait_status, NULL, NULL);
+ return ractor_sleep_with_cleanup(ec, cr, wait_status, 0, NULL);
}
// Ractor.receive
@@ -1269,7 +1319,7 @@ ractor_try_yield(rb_execution_context_t *ec, rb_ractor_t *cr, struct rb_ractor_q
type = basket_type_will;
}
else {
- int state;
+ enum ruby_tag_type state;
// begin
EC_PUSH_TAG(ec);
@@ -1426,10 +1476,10 @@ RACTOR_SELECTOR_PTR(VALUE selv)
// Ractor::Selector.new
static VALUE
-ractor_selector_create(VALUE crv)
+ractor_selector_create(VALUE klass)
{
struct rb_ractor_selector *s;
- VALUE selv = TypedData_Make_Struct(rb_cRactorSelector, struct rb_ractor_selector, &ractor_selector_data_type, s);
+ VALUE selv = TypedData_Make_Struct(klass, struct rb_ractor_selector, &ractor_selector_data_type, s);
s->take_basket.type.e = basket_type_reserved;
s->take_ractors = st_init_numtable(); // ractor (ptr) -> take_config
return selv;
@@ -1438,7 +1488,7 @@ ractor_selector_create(VALUE crv)
// Ractor::Selector#add(r)
static VALUE
-ractor_selector_add(rb_execution_context_t *ec, VALUE selv, VALUE rv)
+ractor_selector_add(VALUE selv, VALUE rv)
{
if (!rb_ractor_p(rv)) {
rb_raise(rb_eArgError, "Not a ractor object");
@@ -1456,7 +1506,7 @@ ractor_selector_add(rb_execution_context_t *ec, VALUE selv, VALUE rv)
config->closed = false;
config->oneshot = false;
- if (ractor_register_take(rb_ec_ractor_ptr(ec), r, &s->take_basket, false, config, true)) {
+ if (ractor_register_take(GET_RACTOR(), r, &s->take_basket, false, config, true)) {
st_insert(s->take_ractors, (st_data_t)r, (st_data_t)config);
}
@@ -1466,7 +1516,7 @@ ractor_selector_add(rb_execution_context_t *ec, VALUE selv, VALUE rv)
// Ractor::Selector#remove(r)
static VALUE
-ractor_selector_remove(rb_execution_context_t *ec, VALUE selv, VALUE rv)
+ractor_selector_remove(VALUE selv, VALUE rv)
{
if (!rb_ractor_p(rv)) {
rb_raise(rb_eArgError, "Not a ractor object");
@@ -1499,28 +1549,24 @@ struct ractor_selector_clear_data {
static int
ractor_selector_clear_i(st_data_t key, st_data_t val, st_data_t data)
{
- struct ractor_selector_clear_data *ptr = (struct ractor_selector_clear_data *)data;
+ VALUE selv = (VALUE)data;
rb_ractor_t *r = (rb_ractor_t *)key;
- ractor_selector_remove(ptr->ec, ptr->selv, r->pub.self);
+ ractor_selector_remove(selv, r->pub.self);
return ST_CONTINUE;
}
static VALUE
-ractor_selector_clear(rb_execution_context_t *ec, VALUE selv)
+ractor_selector_clear(VALUE selv)
{
- struct ractor_selector_clear_data data = {
- .selv = selv,
- .ec = ec,
- };
struct rb_ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
- st_foreach(s->take_ractors, ractor_selector_clear_i, (st_data_t)&data);
+ st_foreach(s->take_ractors, ractor_selector_clear_i, (st_data_t)selv);
st_clear(s->take_ractors);
return selv;
}
static VALUE
-ractor_selector_empty_p(rb_execution_context_t *ec, VALUE selv)
+ractor_selector_empty_p(VALUE selv)
{
struct rb_ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
return s->take_ractors->num_entries == 0 ? Qtrue : Qfalse;
@@ -1592,8 +1638,9 @@ ractor_selector_wait_cleaup(rb_ractor_t *cr, void *ptr)
}
static VALUE
-ractor_selector_wait(rb_execution_context_t *ec, VALUE selv, VALUE do_receivev, VALUE do_yieldv, VALUE yield_value, VALUE move)
+ractor_selector__wait(VALUE selv, VALUE do_receivev, VALUE do_yieldv, VALUE yield_value, VALUE move)
{
+ rb_execution_context_t *ec = GET_EC();
struct rb_ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
struct rb_ractor_basket *tb = &s->take_basket;
struct rb_ractor_basket taken_basket;
@@ -1623,7 +1670,7 @@ ractor_selector_wait(rb_execution_context_t *ec, VALUE selv, VALUE do_receivev,
}
// check recv_queue
- if (do_receive && (ret_v = ractor_try_receive(ec, cr, rq)) != Qundef) {
+ if (do_receive && !UNDEF_P(ret_v = ractor_try_receive(ec, cr, rq))) {
ret_r = ID2SYM(rb_intern("receive"));
goto success;
}
@@ -1689,7 +1736,7 @@ ractor_selector_wait(rb_execution_context_t *ec, VALUE selv, VALUE do_receivev,
case basket_type_yielding:
rb_bug("unreachable");
case basket_type_deleted: {
- ractor_selector_remove(ec, selv, taken_basket.sender);
+ ractor_selector_remove(selv, taken_basket.sender);
rb_ractor_t *r = RACTOR_PTR(taken_basket.sender);
if (ractor_take_will_lock(r, &taken_basket)) {
@@ -1705,7 +1752,7 @@ ractor_selector_wait(rb_execution_context_t *ec, VALUE selv, VALUE do_receivev,
}
case basket_type_will:
// no more messages
- ractor_selector_remove(ec, selv, taken_basket.sender);
+ ractor_selector_remove(selv, taken_basket.sender);
break;
default:
break;
@@ -1719,6 +1766,60 @@ ractor_selector_wait(rb_execution_context_t *ec, VALUE selv, VALUE do_receivev,
return rb_ary_new_from_args(2, ret_r, ret_v);
}
+static VALUE
+ractor_selector_wait(int argc, VALUE *argv, VALUE selector)
+{
+ VALUE options;
+ ID keywords[3];
+ VALUE values[3];
+
+ keywords[0] = rb_intern("receive");
+ keywords[1] = rb_intern("yield_value");
+ keywords[2] = rb_intern("move");
+
+ rb_scan_args(argc, argv, "0:", &options);
+ rb_get_kwargs(options, keywords, 0, numberof(values), values);
+ return ractor_selector__wait(selector,
+ values[0] == Qundef ? Qfalse : RTEST(values[0]),
+ values[1] != Qundef, values[1], values[2]);
+}
+
+static VALUE
+ractor_selector_new(int argc, VALUE *ractors, VALUE klass)
+{
+ VALUE selector = ractor_selector_create(klass);
+
+ for (int i=0; i<argc; i++) {
+ ractor_selector_add(selector, ractors[i]);
+ }
+
+ return selector;
+}
+
+static VALUE
+ractor_select_internal(rb_execution_context_t *ec, VALUE self, VALUE ractors, VALUE do_receive, VALUE do_yield, VALUE yield_value, VALUE move)
+{
+ VALUE selector = ractor_selector_new(RARRAY_LENINT(ractors), (VALUE *)RARRAY_CONST_PTR(ractors), rb_cRactorSelector);
+ VALUE result;
+ int state;
+
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG() == TAG_NONE)) {
+ result = ractor_selector__wait(selector, do_receive, do_yield, yield_value, move);
+ }
+ else {
+ // ensure
+ ractor_selector_clear(selector);
+
+ // jump
+ EC_JUMP_TAG(ec, state);
+ }
+ EC_POP_TAG();
+
+ RB_GC_GUARD(ractors);
+ return result;
+}
+
// Ractor#close_incoming
static VALUE
@@ -1833,7 +1934,6 @@ cancel_single_ractor_mode(void)
VALUE was_disabled = rb_gc_enable();
rb_gc_start();
- rb_transient_heap_evacuate();
if (was_disabled) {
rb_gc_disable();
@@ -1912,12 +2012,11 @@ ractor_alloc(VALUE klass)
rb_ractor_t *
rb_ractor_main_alloc(void)
{
- rb_ractor_t *r = ruby_mimmalloc(sizeof(rb_ractor_t));
+ rb_ractor_t *r = ruby_mimcalloc(1, sizeof(rb_ractor_t));
if (r == NULL) {
fprintf(stderr, "[FATAL] failed to allocate memory for main ractor\n");
exit(EXIT_FAILURE);
}
- MEMZERO(r, rb_ractor_t, 1);
r->pub.id = ++ractor_last_id;
r->loc = Qnil;
r->name = Qnil;
@@ -1945,7 +2044,7 @@ rb_ractor_atfork(rb_vm_t *vm, rb_thread_t *th)
}
#endif
-void rb_thread_sched_init(struct rb_thread_sched *);
+void rb_thread_sched_init(struct rb_thread_sched *, bool atfork);
void
rb_ractor_living_threads_init(rb_ractor_t *r)
@@ -1961,11 +2060,15 @@ ractor_init(rb_ractor_t *r, VALUE name, VALUE loc)
ractor_queue_setup(&r->sync.recv_queue);
ractor_queue_setup(&r->sync.takers_queue);
rb_native_mutex_initialize(&r->sync.lock);
+ rb_native_cond_initialize(&r->barrier_wait_cond);
+
+#ifdef RUBY_THREAD_WIN32_H
rb_native_cond_initialize(&r->sync.cond);
rb_native_cond_initialize(&r->barrier_wait_cond);
+#endif
// thread management
- rb_thread_sched_init(&r->threads.sched);
+ rb_thread_sched_init(&r->threads.sched, false);
rb_ractor_living_threads_init(r);
// naming
@@ -2220,6 +2323,8 @@ ractor_check_blocking(rb_ractor_t *cr, unsigned int remained_thread_cnt, const c
}
}
+void rb_threadptr_remove(rb_thread_t *th);
+
void
rb_ractor_living_threads_remove(rb_ractor_t *cr, rb_thread_t *th)
{
@@ -2227,6 +2332,8 @@ rb_ractor_living_threads_remove(rb_ractor_t *cr, rb_thread_t *th)
RUBY_DEBUG_LOG("r->threads.cnt:%d--", cr->threads.cnt);
ractor_check_blocking(cr, cr->threads.cnt - 1, __FILE__, __LINE__);
+ rb_threadptr_remove(th);
+
if (cr->threads.cnt == 1) {
vm_remove_ractor(th->vm, cr);
}
@@ -2329,6 +2436,9 @@ ractor_terminal_interrupt_all(rb_vm_t *vm)
}
}
+void rb_add_running_thread(rb_thread_t *th);
+void rb_del_running_thread(rb_thread_t *th);
+
void
rb_ractor_terminate_all(void)
{
@@ -2356,7 +2466,9 @@ rb_ractor_terminate_all(void)
// wait for 1sec
rb_vm_ractor_blocking_cnt_inc(vm, cr, __FILE__, __LINE__);
+ rb_del_running_thread(rb_ec_thread_ptr(cr->threads.running_ec));
rb_vm_cond_timedwait(vm, &vm->ractor.sync.terminate_cond, 1000 /* ms */);
+ rb_add_running_thread(rb_ec_thread_ptr(cr->threads.running_ec));
rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
ractor_terminal_interrupt_all(vm);
@@ -2368,7 +2480,23 @@ rb_ractor_terminate_all(void)
rb_execution_context_t *
rb_vm_main_ractor_ec(rb_vm_t *vm)
{
- return vm->ractor.main_ractor->threads.running_ec;
+ /* This code needs to carefully work around two bugs:
+ * - Bug #20016: When M:N threading is enabled, running_ec is NULL if no thread is
+ * actually currently running (as opposed to without M:N threading, when
+ * running_ec will still point to the _last_ thread which ran)
+ * - Bug #20197: If the main thread is sleeping, setting its postponed job
+ * interrupt flag is pointless; it won't look at the flag until it stops sleeping
+ * for some reason. It would be better to set the flag on the running ec, which
+ * will presumably look at it soon.
+ *
+ * Solution: use running_ec if it's set, otherwise fall back to the main thread ec.
+ * This is still susceptible to some rare race conditions (what if the last thread
+ * to run just entered a long-running sleep?), but seems like the best balance of
+ * robustness and complexity.
+ */
+ rb_execution_context_t *running_ec = vm->ractor.main_ractor->threads.running_ec;
+ if (running_ec) { return running_ec; }
+ return vm->ractor.main_thread->ec;
}
static VALUE
@@ -2377,6 +2505,29 @@ ractor_moved_missing(int argc, VALUE *argv, VALUE self)
rb_raise(rb_eRactorMovedError, "can not send any methods to a moved object");
}
+#ifndef USE_RACTOR_SELECTOR
+#define USE_RACTOR_SELECTOR 0
+#endif
+
+RUBY_SYMBOL_EXPORT_BEGIN
+void rb_init_ractor_selector(void);
+RUBY_SYMBOL_EXPORT_END
+
+void
+rb_init_ractor_selector(void)
+{
+ rb_cRactorSelector = rb_define_class_under(rb_cRactor, "Selector", rb_cObject);
+ rb_undef_alloc_func(rb_cRactorSelector);
+
+ rb_define_singleton_method(rb_cRactorSelector, "new", ractor_selector_new , -1);
+ rb_define_method(rb_cRactorSelector, "add", ractor_selector_add, 1);
+ rb_define_method(rb_cRactorSelector, "remove", ractor_selector_remove, 1);
+ rb_define_method(rb_cRactorSelector, "clear", ractor_selector_clear, 0);
+ rb_define_method(rb_cRactorSelector, "empty?", ractor_selector_empty_p, 0);
+ rb_define_method(rb_cRactorSelector, "wait", ractor_selector_wait, -1);
+ rb_define_method(rb_cRactorSelector, "_wait", ractor_selector__wait, 4);
+}
+
/*
* Document-class: Ractor::ClosedError
*
@@ -2497,8 +2648,9 @@ Init_Ractor(void)
rb_define_method(rb_cRactorMovedObject, "instance_eval", ractor_moved_missing, -1);
rb_define_method(rb_cRactorMovedObject, "instance_exec", ractor_moved_missing, -1);
- rb_cRactorSelector = rb_define_class_under(rb_cRactor, "Selector", rb_cObject);
- rb_undef_alloc_func(rb_cRactorSelector);
+#if USE_RACTOR_SELECTOR
+ rb_init_ractor_selector();
+#endif
}
void
@@ -2644,19 +2796,6 @@ obj_hash_traverse_i(VALUE key, VALUE val, VALUE ptr)
return ST_CONTINUE;
}
-static int
-obj_hash_iv_traverse_i(st_data_t key, st_data_t val, st_data_t ptr)
-{
- struct obj_traverse_callback_data *d = (struct obj_traverse_callback_data *)ptr;
-
- if (obj_traverse_i((VALUE)val, d->data)) {
- d->stop = true;
- return ST_STOP;
- }
-
- return ST_CONTINUE;
-}
-
static void
obj_traverse_reachable_i(VALUE obj, void *ptr)
{
@@ -2678,6 +2817,19 @@ obj_traverse_rec(struct obj_traverse_data *data)
}
static int
+obj_traverse_ivar_foreach_i(ID key, VALUE val, st_data_t ptr)
+{
+ struct obj_traverse_callback_data *d = (struct obj_traverse_callback_data *)ptr;
+
+ if (obj_traverse_i(val, d->data)) {
+ d->stop = true;
+ return ST_STOP;
+ }
+
+ return ST_CONTINUE;
+}
+
+static int
obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
{
if (RB_SPECIAL_CONST_P(obj)) return 0;
@@ -2693,14 +2845,12 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
return 0;
}
- if (UNLIKELY(FL_TEST_RAW(obj, FL_EXIVAR))) {
- struct gen_ivtbl *ivtbl;
- rb_ivar_generic_ivtbl_lookup(obj, &ivtbl);
- for (uint32_t i = 0; i < ivtbl->numiv; i++) {
- VALUE val = ivtbl->ivptr[i];
- if (!UNDEF_P(val) && obj_traverse_i(val, data)) return 1;
- }
- }
+ struct obj_traverse_callback_data d = {
+ .stop = false,
+ .data = data,
+ };
+ rb_ivar_foreach(obj, obj_traverse_ivar_foreach_i, (st_data_t)&d);
+ if (d.stop) return 1;
switch (BUILTIN_TYPE(obj)) {
// no child node
@@ -2714,25 +2864,7 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
break;
case T_OBJECT:
- {
- if (rb_shape_obj_too_complex(obj)) {
- struct obj_traverse_callback_data d = {
- .stop = false,
- .data = data,
- };
- rb_st_foreach(ROBJECT_IV_HASH(obj), obj_hash_iv_traverse_i, (st_data_t)&d);
- if (d.stop) return 1;
- }
- else {
- uint32_t len = ROBJECT_IV_COUNT(obj);
- VALUE *ptr = ROBJECT_IVPTR(obj);
-
- for (uint32_t i=0; i<len; i++) {
- VALUE val = ptr[i];
- if (!UNDEF_P(val) && obj_traverse_i(val, data)) return 1;
- }
- }
- }
+ /* Instance variables already traversed. */
break;
case T_ARRAY:
@@ -2852,7 +2984,10 @@ rb_obj_traverse(VALUE obj,
static int
frozen_shareable_p(VALUE obj, bool *made_shareable)
{
- if (!RB_TYPE_P(obj, T_DATA)) {
+ if (CHILLED_STRING_P(obj)) {
+ return false;
+ }
+ else if (!RB_TYPE_P(obj, T_DATA)) {
return true;
}
else if (RTYPEDDATA_P(obj)) {
@@ -2881,6 +3016,17 @@ make_shareable_check_shareable(VALUE obj)
if (rb_ractor_shareable_p(obj)) {
return traverse_skip;
}
+ else if (CHILLED_STRING_P(obj)) {
+ rb_funcall(obj, idFreeze, 0);
+
+ if (UNLIKELY(!RB_OBJ_FROZEN_RAW(obj))) {
+ rb_raise(rb_eRactorError, "#freeze does not freeze object correctly");
+ }
+
+ if (RB_OBJ_SHAREABLE_P(obj)) {
+ return traverse_skip;
+ }
+ }
else if (!frozen_shareable_p(obj, &made_shareable)) {
if (made_shareable) {
return traverse_skip;
@@ -2925,10 +3071,7 @@ VALUE
rb_ractor_make_shareable_copy(VALUE obj)
{
VALUE copy = ractor_copy(obj);
- rb_obj_traverse(copy,
- make_shareable_check_shareable,
- null_leave, mark_shareable);
- return copy;
+ return rb_ractor_make_shareable(copy);
}
VALUE
@@ -3109,12 +3252,6 @@ obj_traverse_replace_rec(struct obj_traverse_replace_data *data)
return data->rec;
}
-#if USE_TRANSIENT_HEAP
-void rb_ary_transient_heap_evacuate(VALUE ary, int promote);
-void rb_obj_transient_heap_evacuate(VALUE obj, int promote);
-void rb_struct_transient_heap_evacuate(VALUE st, int promote);
-#endif
-
static void
obj_refer_only_shareables_p_i(VALUE obj, void *ptr)
{
@@ -3176,9 +3313,26 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
if (UNLIKELY(FL_TEST_RAW(obj, FL_EXIVAR))) {
struct gen_ivtbl *ivtbl;
rb_ivar_generic_ivtbl_lookup(obj, &ivtbl);
- for (uint32_t i = 0; i < ivtbl->numiv; i++) {
- if (!UNDEF_P(ivtbl->ivptr[i])) {
- CHECK_AND_REPLACE(ivtbl->ivptr[i]);
+
+ if (UNLIKELY(rb_shape_obj_too_complex(obj))) {
+ struct obj_traverse_replace_callback_data d = {
+ .stop = false,
+ .data = data,
+ .src = obj,
+ };
+ rb_st_foreach_with_replace(
+ ivtbl->as.complex.table,
+ obj_iv_hash_traverse_replace_foreach_i,
+ obj_iv_hash_traverse_replace_i,
+ (st_data_t)&d
+ );
+ if (d.stop) return 1;
+ }
+ else {
+ for (uint32_t i = 0; i < ivtbl->as.shape.numiv; i++) {
+ if (!UNDEF_P(ivtbl->as.shape.ivptr[i])) {
+ CHECK_AND_REPLACE(ivtbl->as.shape.ivptr[i]);
+ }
}
}
}
@@ -3199,30 +3353,25 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
case T_OBJECT:
{
if (rb_shape_obj_too_complex(obj)) {
- st_table * table = ROBJECT_IV_HASH(obj);
struct obj_traverse_replace_callback_data d = {
.stop = false,
.data = data,
.src = obj,
};
rb_st_foreach_with_replace(
- table,
- obj_iv_hash_traverse_replace_foreach_i,
- obj_iv_hash_traverse_replace_i,
- (st_data_t)&d);
+ ROBJECT_IV_HASH(obj),
+ obj_iv_hash_traverse_replace_foreach_i,
+ obj_iv_hash_traverse_replace_i,
+ (st_data_t)&d
+ );
+ if (d.stop) return 1;
}
else {
-#if USE_TRANSIENT_HEAP
- if (data->move) rb_obj_transient_heap_evacuate(obj, TRUE);
-#endif
-
uint32_t len = ROBJECT_IV_COUNT(obj);
VALUE *ptr = ROBJECT_IVPTR(obj);
- for (uint32_t i=0; i<len; i++) {
- if (!UNDEF_P(ptr[i])) {
- CHECK_AND_REPLACE(ptr[i]);
- }
+ for (uint32_t i = 0; i < len; i++) {
+ CHECK_AND_REPLACE(ptr[i]);
}
}
}
@@ -3231,9 +3380,6 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
case T_ARRAY:
{
rb_ary_cancel_sharing(obj);
-#if USE_TRANSIENT_HEAP
- if (data->move) rb_ary_transient_heap_evacuate(obj, TRUE);
-#endif
for (int i = 0; i < RARRAY_LENINT(obj); i++) {
VALUE e = rb_ary_entry(obj, i);
@@ -3274,9 +3420,6 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
case T_STRUCT:
{
-#if USE_TRANSIENT_HEAP
- if (data->move) rb_struct_transient_heap_evacuate(obj, TRUE);
-#endif
long len = RSTRUCT_LEN(obj);
const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
@@ -3377,6 +3520,8 @@ ractor_moved_bang(VALUE obj)
rv->v3 = 0;
rv->flags = rv->flags & ~fl_users;
+ if (BUILTIN_TYPE(obj) == T_OBJECT) ROBJECT_SET_SHAPE_ID(obj, ROOT_SHAPE_ID);
+
// TODO: record moved location
}
@@ -3388,7 +3533,9 @@ move_enter(VALUE obj, struct obj_traverse_replace_data *data)
return traverse_skip;
}
else {
- data->replacement = rb_obj_alloc(RBASIC_CLASS(obj));
+ VALUE moved = rb_obj_alloc(RBASIC_CLASS(obj));
+ rb_shape_set_shape(moved, rb_shape_get_shape(obj));
+ data->replacement = moved;
return traverse_cont;
}
}
diff --git a/ractor.rb b/ractor.rb
index 5620eeec38..d18062ca07 100644
--- a/ractor.rb
+++ b/ractor.rb
@@ -358,129 +358,13 @@ class Ractor
def self.select(*ractors, yield_value: yield_unspecified = true, move: false)
raise ArgumentError, 'specify at least one ractor or `yield_value`' if yield_unspecified && ractors.empty?
- begin
- if ractors.delete Ractor.current
- do_receive = true
- else
- do_receive = false
- end
- selector = Ractor::Selector.new(*ractors)
-
- if yield_unspecified
- selector.wait receive: do_receive
- else
- selector.wait receive: do_receive, yield_value: yield_value, move: move
- end
- ensure
- selector.clear
- end
- end
-
- #
- # Ractor::Selector provides a functionality to wait multiple Ractor events.
- # Ractor::Selector#wait is more lightweight than Ractor.select()
- # because we don't have to specify all target ractors for each wait time.
- #
- # Ractor.select() uses Ractor::Selector internally to implement it.
- #
- class Selector
- # call-seq:
- # Ractor::Selector.new(*ractors)
- #
- # Creates a selector object.
- #
- # If a ractors parameter is given, it is same as the following code.
- #
- # selector = Ractor::Selector.new
- # ractors.each{|r| selector.add r}
- #
- def self.new(*rs)
- selector = __builtin_cexpr! %q{
- ractor_selector_create(self);
- }
- rs.each{|r| selector.add(r) }
- selector
- end
-
- # call-seq:
- # selector.add(ractor)
- #
- # Registers a ractor as a taking target by the selector.
- #
- def add r
- __builtin_ractor_selector_add r
- end
-
- # call-seq:
- # selector.remove(ractor)
- #
- # Deregisters a ractor as a taking target by the selector.
- #
- def remove r
- __builtin_ractor_selector_remove r
- end
-
- # call-seq:
- # selector.clear
- #
- # Deregisters all ractors.
- def clear
- __builtin_ractor_selector_clear
- end
-
- # call-seq:
- # selector.empty?
- #
- # Returns true if the number of ractors in the waiting set at the current time is zero.
- #
- # Note that even if <tt>#empty?</tt> returns false, the subsequent <tt>#wait</tt>
- # may raise an exception because other ractors may close the target ractors.
- #
- def empty?
- __builtin_ractor_selector_empty_p
+ if ractors.delete Ractor.current
+ do_receive = true
+ else
+ do_receive = false
end
- # call-seq:
- # selector.wait(receive: false, yield_value: yield_value, move: false) -> [ractor or symbol, value]
- #
- # Waits Ractor events. It is lighter than Ractor.select() for many ractors.
- #
- # The simplest form is waiting for taking a value from one of
- # registerred ractors like that.
- #
- # selector = Ractor::Selector.new(r1, r2, r3)
- # r, v = selector.wait
- #
- # On this case, when r1, r2 or r3 is ready to take (yielding a value),
- # this method takes the value from the ready (yielded) ractor
- # and returns [the yielded ractor, the taking value].
- #
- # Note that if a take target ractor is closed, the ractor will be removed
- # automatically.
- #
- # If you also want to wait with receiving an object from other ractors,
- # you can specify receive: true keyword like:
- #
- # r, v = selector.wait receive: true
- #
- # On this case, wait for taking from r1, r2 or r3 and waiting for receving
- # a value from other ractors.
- # If it successes the receiving, it returns an array object [:receive, the received value].
- #
- # If you also want to wait with yielding a value, you can specify
- # :yield_value like:
- #
- # r, v = selector.wait yield_value: obj
- #
- # On this case wait for taking from r1, r2, or r3 and waiting for taking
- # yielding value (obj) by another ractor.
- # If antoher ractor takes the value (obj), it returns an array object [:yield, nil].
- #
- # You can specify a keyword parameter <tt>move: true</tt> like Ractor.yield(obj, move: true)
- #
- def wait receive: false, yield_value: yield_unspecified = true, move: false
- __builtin_ractor_selector_wait receive, !yield_unspecified, yield_value, move
- end
+ __builtin_ractor_select_internal ractors, do_receive, !yield_unspecified, yield_value, move
end
#
diff --git a/ractor_core.h b/ractor_core.h
index 1fd8da0d84..36c0e91c7a 100644
--- a/ractor_core.h
+++ b/ractor_core.h
@@ -103,7 +103,6 @@ struct rb_ractor_sync {
#if RACTOR_CHECK_MODE > 0
VALUE locked_by;
#endif
- rb_nativethread_cond_t cond;
bool incoming_port_closed;
bool outgoing_port_closed;
@@ -120,7 +119,12 @@ struct rb_ractor_sync {
struct ractor_wait {
enum rb_ractor_wait_status status;
enum rb_ractor_wakeup_status wakeup_status;
+ rb_thread_t *waiting_thread;
} wait;
+
+#ifndef RUBY_THREAD_PTHREAD_H
+ rb_nativethread_cond_t cond;
+#endif
};
// created
@@ -310,11 +314,13 @@ static inline void
rb_ractor_set_current_ec_(rb_ractor_t *cr, rb_execution_context_t *ec, const char *file, int line)
{
#ifdef RB_THREAD_LOCAL_SPECIFIER
- #ifdef __APPLE__
+
+# ifdef __APPLE__
rb_current_ec_set(ec);
- #else
+# else
ruby_current_ec = ec;
- #endif
+# endif
+
#else
native_tls_set(ruby_current_ec_key, ec);
#endif
diff --git a/random.c b/random.c
index 4b5b7ab6c4..ea76ea656f 100644
--- a/random.c
+++ b/random.c
@@ -73,7 +73,7 @@
#include "ruby/random.h"
#include "ruby/ractor.h"
-typedef int int_must_be_32bit_at_least[sizeof(int) * CHAR_BIT < 32 ? -1 : 1];
+STATIC_ASSERT(int_must_be_32bit_at_least, sizeof(int) * CHAR_BIT >= 32);
#include "missing/mt19937.c"
@@ -157,6 +157,12 @@ rand_start(rb_random_mt_t *r)
static rb_ractor_local_key_t default_rand_key;
+void
+rb_free_default_rand_key(void)
+{
+ xfree(default_rand_key);
+}
+
static void
default_rand_mark(void *ptr)
{
@@ -590,7 +596,7 @@ fill_random_bytes_crypt(void *seed, size_t size)
if (prov != INVALID_HCRYPTPROV) {
#undef RUBY_UNTYPED_DATA_WARNING
#define RUBY_UNTYPED_DATA_WARNING 0
- rb_gc_register_mark_object(Data_Wrap_Struct(0, 0, release_crypt, &perm_prov));
+ rb_vm_register_global_object(Data_Wrap_Struct(0, 0, release_crypt, &perm_prov));
}
}
else { /* another thread acquired */
diff --git a/range.c b/range.c
index 62e957e622..a6bf0fca51 100644
--- a/range.c
+++ b/range.c
@@ -78,7 +78,7 @@ range_modify(VALUE range)
rb_check_frozen(range);
/* Ranges are immutable, so that they should be initialized only once. */
if (RANGE_EXCL(range) != Qnil) {
- rb_name_err_raise("`initialize' called twice", range, ID2SYM(idInitialize));
+ rb_name_err_raise("'initialize' called twice", range, ID2SYM(idInitialize));
}
}
@@ -224,8 +224,8 @@ recursive_eql(VALUE range, VALUE obj, int recur)
* Returns +true+ if and only if:
*
* - +other+ is a range.
- * - <tt>other.begin eql? self.begin</tt>.
- * - <tt>other.end eql? self.end</tt>.
+ * - <tt>other.begin.eql?(self.begin)</tt>.
+ * - <tt>other.end.eql?(self.end)</tt>.
* - <tt>other.exclude_end? == self.exclude_end?</tt>.
*
* Otherwise returns +false+.
@@ -607,6 +607,10 @@ double_as_int64(double d)
static int
is_integer_p(VALUE v)
{
+ if (rb_integer_type_p(v)) {
+ return true;
+ }
+
ID id_integer_p;
VALUE is_int;
CONST_ID(id_integer_p, "integer?");
@@ -649,27 +653,30 @@ bsearch_integer_range(VALUE beg, VALUE end, int excl)
VALUE low = rb_to_int(beg);
VALUE high = rb_to_int(end);
- VALUE mid, org_high;
+ VALUE mid;
ID id_div;
CONST_ID(id_div, "div");
- if (excl) high = rb_funcall(high, '-', 1, INT2FIX(1));
- org_high = high;
+ if (!excl) high = rb_funcall(high, '+', 1, INT2FIX(1));
+ low = rb_funcall(low, '-', 1, INT2FIX(1));
- while (rb_cmpint(rb_funcall(low, id_cmp, 1, high), low, high) < 0) {
- mid = rb_funcall(rb_funcall(high, '+', 1, low), id_div, 1, INT2FIX(2));
+ /*
+ * This loop must continue while low + 1 < high.
+ * Instead of checking low + 1 < high, check low < mid, where mid = (low + high) / 2.
+ * This is to avoid the cost of calculating low + 1 on each iteration.
+ * Note that this condition replacement is valid because Integer#div always rounds
+ * towards negative infinity.
+ */
+ while (mid = rb_funcall(rb_funcall(high, '+', 1, low), id_div, 1, INT2FIX(2)),
+ rb_cmpint(rb_funcall(low, id_cmp, 1, mid), low, mid) < 0) {
BSEARCH_CHECK(mid);
if (smaller) {
high = mid;
}
else {
- low = rb_funcall(mid, '+', 1, INT2FIX(1));
+ low = mid;
}
}
- if (rb_equal(low, org_high)) {
- BSEARCH_CHECK(low);
- if (!smaller) return Qnil;
- }
return satisfied;
}
@@ -696,52 +703,58 @@ range_bsearch(VALUE range)
* by the mantissa. This is true with or without implicit bit.
*
* Finding the average of two ints needs to be careful about
- * potential overflow (since float to long can use 64 bits)
- * as well as the fact that -1/2 can be 0 or -1 in C89.
+ * potential overflow (since float to long can use 64 bits).
+ *
+ * The half-open interval (low, high] indicates where the target is located.
+ * The loop continues until low and high are adjacent.
+ *
+ * -1/2 can be either 0 or -1 in C89. However, when low and high are not adjacent,
+ * the rounding direction of mid = (low + high) / 2 does not affect the result of
+ * the binary search.
*
* Note that -0.0 is mapped to the same int as 0.0 as we don't want
* (-1...0.0).bsearch to yield -0.0.
*/
-#define BSEARCH(conv) \
+#define BSEARCH(conv, excl) \
do { \
RETURN_ENUMERATOR(range, 0, 0); \
- if (EXCL(range)) high--; \
- org_high = high; \
- while (low < high) { \
+ if (!(excl)) high++; \
+ low--; \
+ while (low + 1 < high) { \
mid = ((high < 0) == (low < 0)) ? low + ((high - low) / 2) \
- : (low < -high) ? -((-1 - low - high)/2 + 1) : (low + high) / 2; \
+ : (low + high) / 2; \
BSEARCH_CHECK(conv(mid)); \
if (smaller) { \
high = mid; \
} \
else { \
- low = mid + 1; \
+ low = mid; \
} \
} \
- if (low == org_high) { \
- BSEARCH_CHECK(conv(low)); \
- if (!smaller) return Qnil; \
- } \
return satisfied; \
} while (0)
+#define BSEARCH_FIXNUM(beg, end, excl) \
+ do { \
+ long low = FIX2LONG(beg); \
+ long high = FIX2LONG(end); \
+ long mid; \
+ BSEARCH(INT2FIX, (excl)); \
+ } while (0)
beg = RANGE_BEG(range);
end = RANGE_END(range);
if (FIXNUM_P(beg) && FIXNUM_P(end)) {
- long low = FIX2LONG(beg);
- long high = FIX2LONG(end);
- long mid, org_high;
- BSEARCH(INT2FIX);
+ BSEARCH_FIXNUM(beg, end, EXCL(range));
}
#if SIZEOF_DOUBLE == 8 && defined(HAVE_INT64_T)
else if (RB_FLOAT_TYPE_P(beg) || RB_FLOAT_TYPE_P(end)) {
int64_t low = double_as_int64(NIL_P(beg) ? -HUGE_VAL : RFLOAT_VALUE(rb_Float(beg)));
int64_t high = double_as_int64(NIL_P(end) ? HUGE_VAL : RFLOAT_VALUE(rb_Float(end)));
- int64_t mid, org_high;
- BSEARCH(int64_as_double_to_num);
+ int64_t mid;
+ BSEARCH(int64_as_double_to_num, EXCL(range));
}
#endif
else if (is_integer_p(beg) && is_integer_p(end)) {
@@ -755,9 +768,15 @@ range_bsearch(VALUE range)
VALUE mid = rb_funcall(beg, '+', 1, diff);
BSEARCH_CHECK(mid);
if (smaller) {
- return bsearch_integer_range(beg, mid, 0);
+ if (FIXNUM_P(beg) && FIXNUM_P(mid)) {
+ BSEARCH_FIXNUM(beg, mid, false);
+ }
+ else {
+ return bsearch_integer_range(beg, mid, false);
+ }
}
diff = rb_funcall(diff, '*', 1, LONG2FIX(2));
+ beg = mid;
}
}
else if (NIL_P(beg) && is_integer_p(end)) {
@@ -767,9 +786,15 @@ range_bsearch(VALUE range)
VALUE mid = rb_funcall(end, '+', 1, diff);
BSEARCH_CHECK(mid);
if (!smaller) {
- return bsearch_integer_range(mid, end, 0);
+ if (FIXNUM_P(mid) && FIXNUM_P(end)) {
+ BSEARCH_FIXNUM(mid, end, false);
+ }
+ else {
+ return bsearch_integer_range(mid, end, false);
+ }
}
diff = rb_funcall(diff, '*', 1, LONG2FIX(2));
+ end = mid;
}
}
else {
@@ -802,7 +827,12 @@ sym_each_i(VALUE v, VALUE arg)
* (1..4).size # => 4
* (1...4).size # => 3
* (1..).size # => Infinity
- * ('a'..'z').size #=> nil
+ * ('a'..'z').size # => nil
+ *
+ * If +self+ is not iterable, raises an exception:
+ *
+ * (0.5..2.5).size # TypeError
+ * (..1).size # TypeError
*
* Related: Range#count.
*/
@@ -811,7 +841,8 @@ static VALUE
range_size(VALUE range)
{
VALUE b = RANGE_BEG(range), e = RANGE_END(range);
- if (rb_obj_is_kind_of(b, rb_cNumeric)) {
+
+ if (RB_INTEGER_TYPE_P(b)) {
if (rb_obj_is_kind_of(e, rb_cNumeric)) {
return ruby_num_interval_step_size(b, e, INT2FIX(1), EXCL(range));
}
@@ -819,10 +850,10 @@ range_size(VALUE range)
return DBL2NUM(HUGE_VAL);
}
}
- else if (NIL_P(b)) {
- if (rb_obj_is_kind_of(e, rb_cNumeric)) {
- return DBL2NUM(HUGE_VAL);
- }
+
+ if (!discrete_object_p(b)) {
+ rb_raise(rb_eTypeError, "can't iterate from %s",
+ rb_obj_classname(b));
}
return Qnil;
@@ -999,6 +1030,144 @@ range_each(VALUE range)
return range;
}
+RBIMPL_ATTR_NORETURN()
+static void
+range_reverse_each_bignum_beginless(VALUE end)
+{
+ RUBY_ASSERT(RBIGNUM_NEGATIVE_P(end));
+
+ for (;; end = rb_big_minus(end, INT2FIX(1))) {
+ rb_yield(end);
+ }
+ UNREACHABLE;
+}
+
+static void
+range_reverse_each_bignum(VALUE beg, VALUE end)
+{
+ RUBY_ASSERT(RBIGNUM_POSITIVE_P(beg) == RBIGNUM_POSITIVE_P(end));
+
+ VALUE c;
+ while ((c = rb_big_cmp(beg, end)) != INT2FIX(1)) {
+ rb_yield(end);
+ if (c == INT2FIX(0)) break;
+ end = rb_big_minus(end, INT2FIX(1));
+ }
+}
+
+static void
+range_reverse_each_positive_bignum_section(VALUE beg, VALUE end)
+{
+ RUBY_ASSERT(!NIL_P(end));
+
+ if (FIXNUM_P(end) || RBIGNUM_NEGATIVE_P(end)) return;
+
+ if (NIL_P(beg) || FIXNUM_P(beg) || RBIGNUM_NEGATIVE_P(beg)) {
+ beg = LONG2NUM(FIXNUM_MAX + 1);
+ }
+
+ range_reverse_each_bignum(beg, end);
+}
+
+static void
+range_reverse_each_fixnum_section(VALUE beg, VALUE end)
+{
+ RUBY_ASSERT(!NIL_P(end));
+
+ if (!FIXNUM_P(beg)) {
+ if (!NIL_P(beg) && RBIGNUM_POSITIVE_P(beg)) return;
+
+ beg = LONG2FIX(FIXNUM_MIN);
+ }
+
+ if (!FIXNUM_P(end)) {
+ if (RBIGNUM_NEGATIVE_P(end)) return;
+
+ end = LONG2FIX(FIXNUM_MAX);
+ }
+
+ long b = FIX2LONG(beg);
+ long e = FIX2LONG(end);
+ for (long i = e; i >= b; --i) {
+ rb_yield(LONG2FIX(i));
+ }
+}
+
+static void
+range_reverse_each_negative_bignum_section(VALUE beg, VALUE end)
+{
+ RUBY_ASSERT(!NIL_P(end));
+
+ if (FIXNUM_P(end) || RBIGNUM_POSITIVE_P(end)) {
+ end = LONG2NUM(FIXNUM_MIN - 1);
+ }
+
+ if (NIL_P(beg)) {
+ range_reverse_each_bignum_beginless(end);
+ }
+
+ if (FIXNUM_P(beg) || RBIGNUM_POSITIVE_P(beg)) return;
+
+ range_reverse_each_bignum(beg, end);
+}
+
+/*
+ * call-seq:
+ * reverse_each {|element| ... } -> self
+ * reverse_each -> an_enumerator
+ *
+ * With a block given, passes each element of +self+ to the block in reverse order:
+ *
+ * a = []
+ * (1..4).reverse_each {|element| a.push(element) } # => 1..4
+ * a # => [4, 3, 2, 1]
+ *
+ * a = []
+ * (1...4).reverse_each {|element| a.push(element) } # => 1...4
+ * a # => [3, 2, 1]
+ *
+ * With no block given, returns an enumerator.
+ *
+ */
+
+static VALUE
+range_reverse_each(VALUE range)
+{
+ RETURN_SIZED_ENUMERATOR(range, 0, 0, range_enum_size);
+
+ VALUE beg = RANGE_BEG(range);
+ VALUE end = RANGE_END(range);
+ int excl = EXCL(range);
+
+ if (NIL_P(end)) {
+ rb_raise(rb_eTypeError, "can't iterate from %s",
+ rb_obj_classname(end));
+ }
+
+ if (FIXNUM_P(beg) && FIXNUM_P(end)) {
+ if (excl) {
+ if (end == LONG2FIX(FIXNUM_MIN)) return range;
+
+ end = rb_int_minus(end, INT2FIX(1));
+ }
+
+ range_reverse_each_fixnum_section(beg, end);
+ }
+ else if ((NIL_P(beg) || RB_INTEGER_TYPE_P(beg)) && RB_INTEGER_TYPE_P(end)) {
+ if (excl) {
+ end = rb_int_minus(end, INT2FIX(1));
+ }
+ range_reverse_each_positive_bignum_section(beg, end);
+ range_reverse_each_fixnum_section(beg, end);
+ range_reverse_each_negative_bignum_section(beg, end);
+ }
+ else {
+ return rb_call_super(0, NULL);
+ }
+
+ return range;
+}
+
/*
* call-seq:
* self.begin -> object
@@ -1103,11 +1272,11 @@ rb_int_range_last(int argc, VALUE *argv, VALUE range)
int x;
long n;
- assert(argc > 0);
+ RUBY_ASSERT(argc > 0);
b = RANGE_BEG(range);
e = RANGE_END(range);
- assert(RB_INTEGER_TYPE_P(b) && RB_INTEGER_TYPE_P(e));
+ RUBY_ASSERT(RB_INTEGER_TYPE_P(b) && RB_INTEGER_TYPE_P(e));
x = EXCL(range);
@@ -1681,7 +1850,6 @@ range_inspect(VALUE range)
}
static VALUE range_include_internal(VALUE range, VALUE val);
-static VALUE range_string_cover_internal(VALUE range, VALUE val);
VALUE rb_str_include_range_p(VALUE beg, VALUE end, VALUE val, VALUE exclusive);
/*
@@ -1726,8 +1894,6 @@ VALUE rb_str_include_range_p(VALUE beg, VALUE end, VALUE val, VALUE exclusive);
static VALUE
range_eqq(VALUE range, VALUE val)
{
- VALUE ret = range_string_cover_internal(range, val);
- if (!UNDEF_P(ret)) return ret;
return r_cover_p(range, RANGE_BEG(range), RANGE_END(range), val);
}
@@ -1777,12 +1943,6 @@ range_integer_edge_p(VALUE beg, VALUE end)
}
static inline bool
-range_string_edge_p(VALUE beg, VALUE end)
-{
- return RB_TYPE_P(beg, T_STRING) || RB_TYPE_P(end, T_STRING);
-}
-
-static inline bool
range_string_range_p(VALUE beg, VALUE end)
{
return RB_TYPE_P(beg, T_STRING) && RB_TYPE_P(end, T_STRING);
@@ -1803,39 +1963,6 @@ range_include_fallback(VALUE beg, VALUE end, VALUE val)
}
static VALUE
-range_string_cover_internal(VALUE range, VALUE val)
-{
- VALUE beg = RANGE_BEG(range);
- VALUE end = RANGE_END(range);
- int nv = FIXNUM_P(beg) || FIXNUM_P(end) ||
- linear_object_p(beg) || linear_object_p(end);
-
- if (nv || range_integer_edge_p(beg, end)) {
- return r_cover_p(range, beg, end, val);
- }
- else if (range_string_edge_p(beg, end)) {
- if (range_string_range_p(beg, end)) {
- return r_cover_p(range, beg, end, val);
- }
- if (NIL_P(beg)) {
- VALUE r = rb_funcall(val, id_cmp, 1, end);
- if (NIL_P(r)) return Qfalse;
- if (RANGE_EXCL(range)) {
- return RBOOL(rb_cmpint(r, val, end) < 0);
- }
- return RBOOL(rb_cmpint(r, val, end) <= 0);
- }
- else if (NIL_P(end)) {
- VALUE r = rb_funcall(beg, id_cmp, 1, val);
- if (NIL_P(r)) return Qfalse;
- return RBOOL(rb_cmpint(r, beg, val) <= 0);
- }
- }
-
- return range_include_fallback(beg, end, val);
-}
-
-static VALUE
range_include_internal(VALUE range, VALUE val)
{
VALUE beg = RANGE_BEG(range);
@@ -1877,7 +2004,7 @@ static int r_cover_range_p(VALUE range, VALUE beg, VALUE end, VALUE val);
* r.cover?(0) # => false
* r.cover?(5) # => false
* r.cover?('foo') # => false
-
+ *
* r = ('a'..'d')
* r.cover?('a') # => true
* r.cover?('d') # => true
@@ -1898,7 +2025,7 @@ static int r_cover_range_p(VALUE range, VALUE beg, VALUE end, VALUE val);
* r.cover?(0) # => false
* r.cover?(4) # => false
* r.cover?('foo') # => false
-
+ *
* r = ('a'...'d')
* r.cover?('a') # => true
* r.cover?('c') # => true
@@ -1914,7 +2041,7 @@ static int r_cover_range_p(VALUE range, VALUE beg, VALUE end, VALUE val);
* r.cover?(0..4) # => false
* r.cover?(1..5) # => false
* r.cover?('a'..'d') # => false
-
+ *
* r = (1...4)
* r.cover?(1..3) # => true
* r.cover?(1..4) # => false
@@ -2136,17 +2263,137 @@ range_count(int argc, VALUE *argv, VALUE range)
* Infinity. Just let it loop. */
return rb_call_super(argc, argv);
}
- else if (NIL_P(RANGE_END(range))) {
+
+ VALUE beg = RANGE_BEG(range), end = RANGE_END(range);
+
+ if (NIL_P(beg) || NIL_P(end)) {
/* We are confident that the answer is Infinity. */
return DBL2NUM(HUGE_VAL);
}
- else if (NIL_P(RANGE_BEG(range))) {
- /* We are confident that the answer is Infinity. */
- return DBL2NUM(HUGE_VAL);
+
+ if (is_integer_p(beg)) {
+ VALUE size = range_size(range);
+ if (!NIL_P(size)) {
+ return size;
+ }
}
- else {
- return rb_call_super(argc, argv);
+
+ return rb_call_super(argc, argv);
+}
+
+static bool
+empty_region_p(VALUE beg, VALUE end, int excl)
+{
+ if (NIL_P(beg)) return false;
+ if (NIL_P(end)) return false;
+ int less = r_less(beg, end);
+ /* empty range */
+ if (less > 0) return true;
+ if (excl && less == 0) return true;
+ return false;
+}
+
+/*
+ * call-seq:
+ * overlap?(range) -> true or false
+ *
+ * Returns +true+ if +range+ overlaps with +self+, +false+ otherwise:
+ *
+ * (0..2).overlap?(1..3) #=> true
+ * (0..2).overlap?(3..4) #=> false
+ * (0..).overlap?(..0) #=> true
+ *
+ * With non-range argument, raises TypeError.
+ *
+ * (1..3).overlap?(1) # TypeError
+ *
+ * Returns +false+ if an internal call to <tt><=></tt> returns +nil+;
+ * that is, the operands are not comparable.
+ *
+ * (1..3).overlap?('a'..'d') # => false
+ *
+ * Returns +false+ if +self+ or +range+ is empty. "Empty range" means
+ * that its begin value is larger than, or equal for an exclusive
+ * range, its end value.
+ *
+ * (4..1).overlap?(2..3) # => false
+ * (4..1).overlap?(..3) # => false
+ * (4..1).overlap?(2..) # => false
+ * (2...2).overlap?(1..2) # => false
+ *
+ * (1..4).overlap?(3..2) # => false
+ * (..4).overlap?(3..2) # => false
+ * (1..).overlap?(3..2) # => false
+ * (1..2).overlap?(2...2) # => false
+ *
+ * Returns +false+ if the begin value one of +self+ and +range+ is
+ * larger than, or equal if the other is an exclusive range, the end
+ * value of the other:
+ *
+ * (4..5).overlap?(2..3) # => false
+ * (4..5).overlap?(2...4) # => false
+ *
+ * (1..2).overlap?(3..4) # => false
+ * (1...3).overlap?(3..4) # => false
+ *
+ * Returns +false+ if the end value one of +self+ and +range+ is
+ * larger than, or equal for an exclusive range, the end value of the
+ * other:
+ *
+ * (4..5).overlap?(2..3) # => false
+ * (4..5).overlap?(2...4) # => false
+ *
+ * (1..2).overlap?(3..4) # => false
+ * (1...3).overlap?(3..4) # => false
+ *
+ * Note that the method wouldn't make any assumptions about the beginless
+ * range being actually empty, even if its upper bound is the minimum
+ * possible value of its type, so all this would return +true+:
+ *
+ * (...-Float::INFINITY).overlap?(...-Float::INFINITY) # => true
+ * (..."").overlap?(..."") # => true
+ * (...[]).overlap?(...[]) # => true
+ *
+ * Even if those ranges are effectively empty (no number can be smaller than
+ * <tt>-Float::INFINITY</tt>), they are still considered overlapping
+ * with themselves.
+ *
+ * Related: Range#cover?.
+ */
+
+static VALUE
+range_overlap(VALUE range, VALUE other)
+{
+ if (!rb_obj_is_kind_of(other, rb_cRange)) {
+ rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Range)",
+ rb_class_name(rb_obj_class(other)));
}
+
+ VALUE self_beg = RANGE_BEG(range);
+ VALUE self_end = RANGE_END(range);
+ int self_excl = EXCL(range);
+ VALUE other_beg = RANGE_BEG(other);
+ VALUE other_end = RANGE_END(other);
+ int other_excl = EXCL(other);
+
+ if (empty_region_p(self_beg, other_end, other_excl)) return Qfalse;
+ if (empty_region_p(other_beg, self_end, self_excl)) return Qfalse;
+
+ if (!NIL_P(self_beg) && !NIL_P(other_beg)) {
+ VALUE cmp = rb_funcall(self_beg, id_cmp, 1, other_beg);
+ if (NIL_P(cmp)) return Qfalse;
+ /* if both begin values are equal, no more comparisons needed */
+ if (rb_cmpint(cmp, self_beg, other_beg) == 0) return Qtrue;
+ }
+ else if (NIL_P(self_beg) && NIL_P(other_beg)) {
+ VALUE cmp = rb_funcall(self_end, id_cmp, 1, other_end);
+ return RBOOL(!NIL_P(cmp));
+ }
+
+ if (empty_region_p(self_beg, self_end, self_excl)) return Qfalse;
+ if (empty_region_p(other_beg, other_end, other_excl)) return Qfalse;
+
+ return Qtrue;
}
/* A \Range object represents a collection of values
@@ -2329,6 +2576,7 @@ range_count(int argc, VALUE *argv, VALUE range)
* - {Comparing}[rdoc-ref:Range@Methods+for+Comparing]
* - {Iterating}[rdoc-ref:Range@Methods+for+Iterating]
* - {Converting}[rdoc-ref:Range@Methods+for+Converting]
+ * - {Methods for Working with JSON}[rdoc-ref:Range@Methods+for+Working+with+JSON]
*
* === Methods for Creating a \Range
*
@@ -2363,7 +2611,7 @@ range_count(int argc, VALUE *argv, VALUE range)
* - #%: Requires argument +n+; calls the block with each +n+-th element of +self+.
* - #each: Calls the block with each element of +self+.
* - #step: Takes optional argument +n+ (defaults to 1);
- calls the block with each +n+-th element of +self+.
+ * calls the block with each +n+-th element of +self+.
*
* === Methods for Converting
*
@@ -2371,6 +2619,16 @@ range_count(int argc, VALUE *argv, VALUE range)
* - #to_a (aliased as #entries): Returns elements of +self+ in an array.
* - #to_s: Returns a string representation of +self+ (uses #to_s).
*
+ * === Methods for Working with \JSON
+ *
+ * - ::json_create: Returns a new \Range object constructed from the given object.
+ * - #as_json: Returns a 2-element hash representing +self+.
+ * - #to_json: Returns a \JSON string representing +self+.
+ *
+ * To make these methods available:
+ *
+ * require 'json/add/range'
+ *
*/
void
@@ -2395,6 +2653,7 @@ Init_Range(void)
rb_define_method(rb_cRange, "each", range_each, 0);
rb_define_method(rb_cRange, "step", range_step, -1);
rb_define_method(rb_cRange, "%", range_percent_step, 1);
+ rb_define_method(rb_cRange, "reverse_each", range_reverse_each, 0);
rb_define_method(rb_cRange, "bsearch", range_bsearch, 0);
rb_define_method(rb_cRange, "begin", range_begin, 0);
rb_define_method(rb_cRange, "end", range_end, 0);
@@ -2415,4 +2674,5 @@ Init_Range(void)
rb_define_method(rb_cRange, "include?", range_include, 1);
rb_define_method(rb_cRange, "cover?", range_cover, 1);
rb_define_method(rb_cRange, "count", range_count, -1);
+ rb_define_method(rb_cRange, "overlap?", range_overlap, 1);
}
diff --git a/rational.c b/rational.c
index c5ad7598f7..014cbb6c6a 100644
--- a/rational.c
+++ b/rational.c
@@ -389,8 +389,8 @@ f_gcd(VALUE x, VALUE y)
{
VALUE r = f_gcd_orig(x, y);
if (f_nonzero_p(r)) {
- assert(f_zero_p(f_mod(x, r)));
- assert(f_zero_p(f_mod(y, r)));
+ RUBY_ASSERT(f_zero_p(f_mod(x, r)));
+ RUBY_ASSERT(f_zero_p(f_mod(y, r)));
}
return r;
}
@@ -418,7 +418,7 @@ nurat_s_new_internal(VALUE klass, VALUE num, VALUE den)
RATIONAL_SET_NUM((VALUE)obj, num);
RATIONAL_SET_DEN((VALUE)obj, den);
- OBJ_FREEZE_RAW((VALUE)obj);
+ OBJ_FREEZE((VALUE)obj);
return (VALUE)obj;
}
@@ -456,8 +456,8 @@ nurat_int_value(VALUE num)
static void
nurat_canonicalize(VALUE *num, VALUE *den)
{
- assert(num); assert(RB_INTEGER_TYPE_P(*num));
- assert(den); assert(RB_INTEGER_TYPE_P(*den));
+ RUBY_ASSERT(num); RUBY_ASSERT(RB_INTEGER_TYPE_P(*num));
+ RUBY_ASSERT(den); RUBY_ASSERT(RB_INTEGER_TYPE_P(*den));
if (INT_NEGATIVE_P(*den)) {
*num = rb_int_uminus(*num);
*den = rb_int_uminus(*den);
@@ -497,16 +497,16 @@ nurat_s_canonicalize_internal_no_reduce(VALUE klass, VALUE num, VALUE den)
inline static VALUE
f_rational_new2(VALUE klass, VALUE x, VALUE y)
{
- assert(!k_rational_p(x));
- assert(!k_rational_p(y));
+ RUBY_ASSERT(!k_rational_p(x));
+ RUBY_ASSERT(!k_rational_p(y));
return nurat_s_canonicalize_internal(klass, x, y);
}
inline static VALUE
f_rational_new_no_reduce2(VALUE klass, VALUE x, VALUE y)
{
- assert(!k_rational_p(x));
- assert(!k_rational_p(y));
+ RUBY_ASSERT(!k_rational_p(x));
+ RUBY_ASSERT(!k_rational_p(y));
return nurat_s_canonicalize_internal_no_reduce(klass, x, y);
}
@@ -610,7 +610,7 @@ nurat_denominator(VALUE self)
VALUE
rb_rational_uminus(VALUE self)
{
- const int unused = (assert(RB_TYPE_P(self, T_RATIONAL)), 0);
+ const int unused = (RUBY_ASSERT(RB_TYPE_P(self, T_RATIONAL)), 0);
get_dat1(self);
(void)unused;
return f_rational_new2(CLASS_OF(self), rb_int_uminus(dat->num), dat->den);
@@ -646,7 +646,7 @@ inline static VALUE
f_imul(long x, long y)
{
VALUE r = f_imul_orig(x, y);
- assert(f_eqeq_p(r, f_mul(LONG2NUM(x), LONG2NUM(y))));
+ RUBY_ASSERT(f_eqeq_p(r, f_mul(LONG2NUM(x), LONG2NUM(y))));
return r;
}
#endif
@@ -795,7 +795,7 @@ f_muldiv(VALUE self, VALUE anum, VALUE aden, VALUE bnum, VALUE bden, int k)
{
VALUE num, den;
- assert(RB_TYPE_P(self, T_RATIONAL));
+ RUBY_ASSERT(RB_TYPE_P(self, T_RATIONAL));
/* Integer#** can return Rational with Float right now */
if (RB_FLOAT_TYPE_P(anum) || RB_FLOAT_TYPE_P(aden) ||
@@ -806,10 +806,10 @@ f_muldiv(VALUE self, VALUE anum, VALUE aden, VALUE bnum, VALUE bden, int k)
return DBL2NUM(x);
}
- assert(RB_INTEGER_TYPE_P(anum));
- assert(RB_INTEGER_TYPE_P(aden));
- assert(RB_INTEGER_TYPE_P(bnum));
- assert(RB_INTEGER_TYPE_P(bden));
+ RUBY_ASSERT(RB_INTEGER_TYPE_P(anum));
+ RUBY_ASSERT(RB_INTEGER_TYPE_P(aden));
+ RUBY_ASSERT(RB_INTEGER_TYPE_P(bnum));
+ RUBY_ASSERT(RB_INTEGER_TYPE_P(bden));
if (k == '/') {
VALUE t;
@@ -1847,7 +1847,7 @@ nurat_loader(VALUE self, VALUE a)
nurat_canonicalize(&num, &den);
RATIONAL_SET_NUM((VALUE)dat, num);
RATIONAL_SET_DEN((VALUE)dat, den);
- OBJ_FREEZE_RAW(self);
+ OBJ_FREEZE(self);
return self;
}
@@ -2559,7 +2559,7 @@ nurat_convert(VALUE klass, VALUE numv, VALUE denv, int raise)
VALUE a1 = numv, a2 = denv;
int state;
- assert(!UNDEF_P(a1));
+ RUBY_ASSERT(!UNDEF_P(a1));
if (NIL_P(a1) || NIL_P(a2)) {
if (!raise) return Qnil;
diff --git a/re.c b/re.c
index f6abf46131..c8940ff887 100644
--- a/re.c
+++ b/re.c
@@ -88,6 +88,9 @@ static const char casetable[] = {
# error >>> "You lose. You will need a translation table for your character set." <<<
#endif
+// The process-global timeout for regexp matching
+rb_hrtime_t rb_reg_match_time_limit = 0;
+
int
rb_memcicmp(const void *x, const void *y, long len)
{
@@ -450,7 +453,7 @@ rb_reg_expr_str(VALUE str, const char *s, long len,
}
static VALUE
-rb_reg_desc(const char *s, long len, VALUE re)
+rb_reg_desc(VALUE re)
{
rb_encoding *enc = rb_enc_get(re);
VALUE str = rb_str_buf_new2("/");
@@ -463,7 +466,11 @@ rb_reg_desc(const char *s, long len, VALUE re)
else {
rb_enc_associate(str, rb_usascii_encoding());
}
- rb_reg_expr_str(str, s, len, enc, resenc, '/');
+
+ VALUE src_str = RREGEXP_SRC(re);
+ rb_reg_expr_str(str, RSTRING_PTR(src_str), RSTRING_LEN(src_str), enc, resenc, '/');
+ RB_GC_GUARD(src_str);
+
rb_str_buf_cat2(str, "/");
if (re) {
char opts[OPTBUF_SIZE];
@@ -522,7 +529,7 @@ rb_reg_inspect(VALUE re)
if (!RREGEXP_PTR(re) || !RREGEXP_SRC(re) || !RREGEXP_SRC_PTR(re)) {
return rb_any_to_s(re);
}
- return rb_reg_desc(RREGEXP_SRC_PTR(re), RREGEXP_SRC_LEN(re), re);
+ return rb_reg_desc(re);
}
static VALUE rb_reg_str_with_term(VALUE re, int term);
@@ -538,7 +545,7 @@ static VALUE rb_reg_str_with_term(VALUE re, int term);
*
* The returned string may be used as an argument to Regexp.new,
* or as interpolated text for a
- * {Regexp literal}[rdoc-ref:regexp.rdoc@Regexp+Literal]:
+ * {Regexp interpolation}[rdoc-ref:Regexp@Interpolation+Mode]:
*
* r1 = Regexp.new(s0) # => /(?ix-m:ab+c)/
* r2 = /#{s0}/ # => /(?ix-m:ab+c)/
@@ -565,8 +572,6 @@ rb_reg_str_with_term(VALUE re, int term)
{
int options, opt;
const int embeddable = ONIG_OPTION_MULTILINE|ONIG_OPTION_IGNORECASE|ONIG_OPTION_EXTEND;
- long len;
- const UChar* ptr;
VALUE str = rb_str_buf_new2("(?");
char optbuf[OPTBUF_SIZE + 1]; /* for '-' */
rb_encoding *enc = rb_enc_get(re);
@@ -575,8 +580,9 @@ rb_reg_str_with_term(VALUE re, int term)
rb_enc_copy(str, re);
options = RREGEXP_PTR(re)->options;
- ptr = (UChar*)RREGEXP_SRC_PTR(re);
- len = RREGEXP_SRC_LEN(re);
+ VALUE src_str = RREGEXP_SRC(re);
+ const UChar *ptr = (UChar *)RSTRING_PTR(src_str);
+ long len = RSTRING_LEN(src_str);
again:
if (len >= 4 && ptr[0] == '(' && ptr[1] == '?') {
int err = 1;
@@ -666,15 +672,17 @@ rb_reg_str_with_term(VALUE re, int term)
}
rb_enc_copy(str, re);
+ RB_GC_GUARD(src_str);
+
return str;
}
-NORETURN(static void rb_reg_raise(const char *s, long len, const char *err, VALUE re));
+NORETURN(static void rb_reg_raise(const char *err, VALUE re));
static void
-rb_reg_raise(const char *s, long len, const char *err, VALUE re)
+rb_reg_raise(const char *err, VALUE re)
{
- VALUE desc = rb_reg_desc(s, len, re);
+ VALUE desc = rb_reg_desc(re);
rb_raise(rb_eRegexpError, "%s: %"PRIsVALUE, err, desc);
}
@@ -961,12 +969,13 @@ VALUE rb_cMatch;
static VALUE
match_alloc(VALUE klass)
{
- NEWOBJ_OF(match, struct RMatch, klass, T_MATCH | (RGENGC_WB_PROTECTED_MATCH ? FL_WB_PROTECTED : 0), sizeof(struct RMatch), 0);
+ size_t alloc_size = sizeof(struct RMatch) + sizeof(rb_matchext_t);
+ VALUE flags = T_MATCH | (RGENGC_WB_PROTECTED_MATCH ? FL_WB_PROTECTED : 0);
+ NEWOBJ_OF(match, struct RMatch, klass, flags, alloc_size, 0);
match->str = Qfalse;
- match->rmatch = 0;
match->regexp = Qfalse;
- match->rmatch = ZALLOC(struct rmatch);
+ memset(RMATCH_EXT(match), 0, sizeof(rb_matchext_t));
return (VALUE)match;
}
@@ -1001,7 +1010,7 @@ pair_byte_cmp(const void *pair1, const void *pair2)
static void
update_char_offset(VALUE match)
{
- struct rmatch *rm = RMATCH(match)->rmatch;
+ rb_matchext_t *rm = RMATCH_EXT(match);
struct re_registers *regs;
int i, num_regs, num_pos;
long c;
@@ -1079,23 +1088,23 @@ match_check(VALUE match)
static VALUE
match_init_copy(VALUE obj, VALUE orig)
{
- struct rmatch *rm;
+ rb_matchext_t *rm;
if (!OBJ_INIT_COPY(obj, orig)) return obj;
RB_OBJ_WRITE(obj, &RMATCH(obj)->str, RMATCH(orig)->str);
RB_OBJ_WRITE(obj, &RMATCH(obj)->regexp, RMATCH(orig)->regexp);
- rm = RMATCH(obj)->rmatch;
+ rm = RMATCH_EXT(obj);
if (rb_reg_region_copy(&rm->regs, RMATCH_REGS(orig)))
rb_memerror();
- if (RMATCH(orig)->rmatch->char_offset_num_allocated) {
+ if (RMATCH_EXT(orig)->char_offset_num_allocated) {
if (rm->char_offset_num_allocated < rm->regs.num_regs) {
REALLOC_N(rm->char_offset, struct rmatch_offset, rm->regs.num_regs);
rm->char_offset_num_allocated = rm->regs.num_regs;
}
- MEMCPY(rm->char_offset, RMATCH(orig)->rmatch->char_offset,
+ MEMCPY(rm->char_offset, RMATCH_EXT(orig)->char_offset,
struct rmatch_offset, rm->regs.num_regs);
RB_GC_GUARD(orig);
}
@@ -1250,8 +1259,8 @@ match_offset(VALUE match, VALUE n)
return rb_assoc_new(Qnil, Qnil);
update_char_offset(match);
- return rb_assoc_new(LONG2NUM(RMATCH(match)->rmatch->char_offset[i].beg),
- LONG2NUM(RMATCH(match)->rmatch->char_offset[i].end));
+ return rb_assoc_new(LONG2NUM(RMATCH_EXT(match)->char_offset[i].beg),
+ LONG2NUM(RMATCH_EXT(match)->char_offset[i].end));
}
/*
@@ -1309,7 +1318,7 @@ match_begin(VALUE match, VALUE n)
return Qnil;
update_char_offset(match);
- return LONG2NUM(RMATCH(match)->rmatch->char_offset[i].beg);
+ return LONG2NUM(RMATCH_EXT(match)->char_offset[i].beg);
}
@@ -1335,7 +1344,7 @@ match_end(VALUE match, VALUE n)
return Qnil;
update_char_offset(match);
- return LONG2NUM(RMATCH(match)->rmatch->char_offset[i].end);
+ return LONG2NUM(RMATCH_EXT(match)->char_offset[i].end);
}
/*
@@ -1422,7 +1431,7 @@ match_nth_length(VALUE match, VALUE n)
update_char_offset(match);
const struct rmatch_offset *const ofs =
- &RMATCH(match)->rmatch->char_offset[i];
+ &RMATCH_EXT(match)->char_offset[i];
return LONG2NUM(ofs->end - ofs->beg);
}
@@ -1450,28 +1459,11 @@ rb_match_count(VALUE match)
return regs->num_regs;
}
-int
-rb_match_nth_defined(int nth, VALUE match)
-{
- struct re_registers *regs;
- if (NIL_P(match)) return FALSE;
- regs = RMATCH_REGS(match);
- if (!regs) return FALSE;
- if (nth >= regs->num_regs) {
- return FALSE;
- }
- if (nth < 0) {
- nth += regs->num_regs;
- if (nth <= 0) return FALSE;
- }
- return (BEG(nth) != -1);
-}
-
static void
match_set_string(VALUE m, VALUE string, long pos, long len)
{
struct RMatch *match = (struct RMatch *)m;
- struct rmatch *rmatch = match->rmatch;
+ rb_matchext_t *rmatch = RMATCH_EXT(match);
RB_OBJ_WRITE(match, &RMATCH(match)->str, string);
RB_OBJ_WRITE(match, &RMATCH(match)->regexp, Qnil);
@@ -1538,8 +1530,8 @@ reg_enc_error(VALUE re, VALUE str)
{
rb_raise(rb_eEncCompatError,
"incompatible encoding regexp match (%s regexp with %s string)",
- rb_enc_name(rb_enc_get(re)),
- rb_enc_name(rb_enc_get(str)));
+ rb_enc_inspect_name(rb_enc_get(re)),
+ rb_enc_inspect_name(rb_enc_get(str)));
}
static inline int
@@ -1592,24 +1584,25 @@ rb_reg_prepare_enc(VALUE re, VALUE str, int warn)
}
regex_t *
-rb_reg_prepare_re0(VALUE re, VALUE str, onig_errmsg_buffer err)
+rb_reg_prepare_re(VALUE re, VALUE str)
{
- regex_t *reg = RREGEXP_PTR(re);
int r;
OnigErrorInfo einfo;
- const char *pattern;
VALUE unescaped;
rb_encoding *fixed_enc = 0;
rb_encoding *enc = rb_reg_prepare_enc(re, str, 1);
+ regex_t *reg = RREGEXP_PTR(re);
if (reg->enc == enc) return reg;
rb_reg_check(re);
- reg = RREGEXP_PTR(re);
- pattern = RREGEXP_SRC_PTR(re);
+ VALUE src_str = RREGEXP_SRC(re);
+ const char *pattern = RSTRING_PTR(src_str);
+
+ onig_errmsg_buffer err = "";
unescaped = rb_reg_preprocess(
- pattern, pattern + RREGEXP_SRC_LEN(re), enc,
+ pattern, pattern + RSTRING_LEN(src_str), enc,
&fixed_enc, err, 0);
if (NIL_P(unescaped)) {
@@ -1622,25 +1615,70 @@ rb_reg_prepare_re0(VALUE re, VALUE str, onig_errmsg_buffer err)
const char *ptr;
long len;
RSTRING_GETMEM(unescaped, ptr, len);
- r = onig_new(&reg, (UChar *)ptr, (UChar *)(ptr + len),
- reg->options, enc,
- OnigDefaultSyntax, &einfo);
+
+ /* If there are no other users of this regex, then we can directly overwrite it. */
+ if (RREGEXP(re)->usecnt == 0) {
+ regex_t tmp_reg;
+ r = onig_new_without_alloc(&tmp_reg, (UChar *)ptr, (UChar *)(ptr + len),
+ reg->options, enc,
+ OnigDefaultSyntax, &einfo);
+
+ if (r) {
+ /* There was an error so perform cleanups. */
+ onig_free_body(&tmp_reg);
+ }
+ else {
+ onig_free_body(reg);
+ /* There are no errors so set reg to tmp_reg. */
+ *reg = tmp_reg;
+ }
+ }
+ else {
+ r = onig_new(&reg, (UChar *)ptr, (UChar *)(ptr + len),
+ reg->options, enc,
+ OnigDefaultSyntax, &einfo);
+ }
+
if (r) {
onig_error_code_to_str((UChar*)err, r, &einfo);
- rb_reg_raise(pattern, RREGEXP_SRC_LEN(re), err, re);
+ rb_reg_raise(err, re);
}
reg->timelimit = timelimit;
RB_GC_GUARD(unescaped);
+ RB_GC_GUARD(src_str);
return reg;
}
-regex_t *
-rb_reg_prepare_re(VALUE re, VALUE str)
+OnigPosition
+rb_reg_onig_match(VALUE re, VALUE str,
+ OnigPosition (*match)(regex_t *reg, VALUE str, struct re_registers *regs, void *args),
+ void *args, struct re_registers *regs)
{
- onig_errmsg_buffer err = "";
- return rb_reg_prepare_re0(re, str, err);
+ regex_t *reg = rb_reg_prepare_re(re, str);
+
+ bool tmpreg = reg != RREGEXP_PTR(re);
+ if (!tmpreg) RREGEXP(re)->usecnt++;
+
+ OnigPosition result = match(reg, str, regs, args);
+
+ if (!tmpreg) RREGEXP(re)->usecnt--;
+ if (tmpreg) {
+ onig_free(reg);
+ }
+
+ if (result < 0) {
+ onig_region_free(regs, 0);
+
+ if (result != ONIG_MISMATCH) {
+ onig_errmsg_buffer err = "";
+ onig_error_code_to_str((UChar*)err, (int)result);
+ rb_reg_raise(err, re);
+ }
+ }
+
+ return result;
}
long
@@ -1674,65 +1712,88 @@ rb_reg_adjust_startpos(VALUE re, VALUE str, long pos, int reverse)
return pos;
}
+struct reg_onig_search_args {
+ long pos;
+ long range;
+};
+
+static OnigPosition
+reg_onig_search(regex_t *reg, VALUE str, struct re_registers *regs, void *args_ptr)
+{
+ struct reg_onig_search_args *args = (struct reg_onig_search_args *)args_ptr;
+ const char *ptr;
+ long len;
+ RSTRING_GETMEM(str, ptr, len);
+
+ return onig_search(
+ reg,
+ (UChar *)ptr,
+ (UChar *)(ptr + len),
+ (UChar *)(ptr + args->pos),
+ (UChar *)(ptr + args->range),
+ regs,
+ ONIG_OPTION_NONE);
+}
+
+struct rb_reg_onig_match_args {
+ VALUE re;
+ VALUE str;
+ struct reg_onig_search_args args;
+ struct re_registers regs;
+
+ OnigPosition result;
+};
+
+static VALUE
+rb_reg_onig_match_try(VALUE value_args)
+{
+ struct rb_reg_onig_match_args *args = (struct rb_reg_onig_match_args *)value_args;
+ args->result = rb_reg_onig_match(args->re, args->str, reg_onig_search, &args->args, &args->regs);
+ return Qnil;
+}
+
/* returns byte offset */
static long
rb_reg_search_set_match(VALUE re, VALUE str, long pos, int reverse, int set_backref_str, VALUE *set_match)
{
- long result;
- VALUE match;
- struct re_registers regi, *regs = &regi;
- char *start, *range;
- long len;
- regex_t *reg;
- int tmpreg;
- onig_errmsg_buffer err = "";
-
- RSTRING_GETMEM(str, start, len);
- range = start;
+ long len = RSTRING_LEN(str);
if (pos > len || pos < 0) {
rb_backref_set(Qnil);
return -1;
}
- reg = rb_reg_prepare_re0(re, str, err);
- tmpreg = reg != RREGEXP_PTR(re);
- if (!tmpreg) RREGEXP(re)->usecnt++;
-
- MEMZERO(regs, struct re_registers, 1);
- if (!reverse) {
- range += len;
- }
- result = onig_search(reg,
- (UChar*)start,
- ((UChar*)(start + len)),
- ((UChar*)(start + pos)),
- ((UChar*)range),
- regs, ONIG_OPTION_NONE);
- if (!tmpreg) RREGEXP(re)->usecnt--;
- if (tmpreg) {
- if (RREGEXP(re)->usecnt) {
- onig_free(reg);
- }
- else {
- onig_free(RREGEXP_PTR(re));
- RREGEXP_PTR(re) = reg;
+ struct rb_reg_onig_match_args args = {
+ .re = re,
+ .str = str,
+ .args = {
+ .pos = pos,
+ .range = reverse ? 0 : len,
+ },
+ .regs = {0}
+ };
+
+ /* If there is a timeout set, then rb_reg_onig_match could raise a
+ * Regexp::TimeoutError so we want to protect it from leaking memory. */
+ if (rb_reg_match_time_limit) {
+ int state;
+ rb_protect(rb_reg_onig_match_try, (VALUE)&args, &state);
+ if (state) {
+ onig_region_free(&args.regs, false);
+ rb_jump_tag(state);
}
}
- if (result < 0) {
- if (regs == &regi)
- onig_region_free(regs, 0);
- if (result == ONIG_MISMATCH) {
- rb_backref_set(Qnil);
- return result;
- }
- else {
- onig_error_code_to_str((UChar*)err, (int)result);
- rb_reg_raise(RREGEXP_SRC_PTR(re), RREGEXP_SRC_LEN(re), err, re);
- }
+ else {
+ rb_reg_onig_match_try((VALUE)&args);
}
- match = match_alloc(rb_cMatch);
- memcpy(RMATCH_REGS(match), regs, sizeof(struct re_registers));
+ if (args.result == ONIG_MISMATCH) {
+ rb_backref_set(Qnil);
+ return ONIG_MISMATCH;
+ }
+
+ VALUE match = match_alloc(rb_cMatch);
+ rb_matchext_t *rm = RMATCH_EXT(match);
+ rm->regs = args.regs;
if (set_backref_str) {
RB_OBJ_WRITE(match, &RMATCH(match)->str, rb_str_new4(str));
@@ -1749,7 +1810,7 @@ rb_reg_search_set_match(VALUE re, VALUE str, long pos, int reverse, int set_back
rb_backref_set(match);
if (set_match) *set_match = match;
- return result;
+ return args.result;
}
long
@@ -1764,69 +1825,35 @@ rb_reg_search(VALUE re, VALUE str, long pos, int reverse)
return rb_reg_search0(re, str, pos, reverse, 1);
}
-bool
-rb_reg_start_with_p(VALUE re, VALUE str)
+static OnigPosition
+reg_onig_match(regex_t *reg, VALUE str, struct re_registers *regs, void *_)
{
- long result;
- VALUE match;
- struct re_registers regi, *regs = &regi;
- regex_t *reg;
- int tmpreg;
- onig_errmsg_buffer err = "";
-
- reg = rb_reg_prepare_re0(re, str, err);
- tmpreg = reg != RREGEXP_PTR(re);
- if (!tmpreg) RREGEXP(re)->usecnt++;
-
- match = rb_backref_get();
- if (!NIL_P(match)) {
- if (FL_TEST(match, MATCH_BUSY)) {
- match = Qnil;
- }
- else {
- regs = RMATCH_REGS(match);
- }
- }
- if (NIL_P(match)) {
- MEMZERO(regs, struct re_registers, 1);
- }
const char *ptr;
long len;
RSTRING_GETMEM(str, ptr, len);
- result = onig_match(reg,
- (UChar*)(ptr),
- ((UChar*)(ptr + len)),
- (UChar*)(ptr),
- regs, ONIG_OPTION_NONE);
- if (!tmpreg) RREGEXP(re)->usecnt--;
- if (tmpreg) {
- if (RREGEXP(re)->usecnt) {
- onig_free(reg);
- }
- else {
- onig_free(RREGEXP_PTR(re));
- RREGEXP_PTR(re) = reg;
- }
- }
- if (result < 0) {
- if (regs == &regi)
- onig_region_free(regs, 0);
- if (result == ONIG_MISMATCH) {
- rb_backref_set(Qnil);
- return false;
- }
- else {
- onig_error_code_to_str((UChar*)err, (int)result);
- rb_reg_raise(RREGEXP_SRC_PTR(re), RREGEXP_SRC_LEN(re), err, re);
- }
- }
- if (NIL_P(match)) {
- int err;
+ return onig_match(
+ reg,
+ (UChar *)ptr,
+ (UChar *)(ptr + len),
+ (UChar *)ptr,
+ regs,
+ ONIG_OPTION_NONE);
+}
+
+bool
+rb_reg_start_with_p(VALUE re, VALUE str)
+{
+ VALUE match = rb_backref_get();
+ if (NIL_P(match) || FL_TEST(match, MATCH_BUSY)) {
match = match_alloc(rb_cMatch);
- err = rb_reg_region_copy(RMATCH_REGS(match), regs);
- onig_region_free(regs, 0);
- if (err) rb_memerror();
+ }
+
+ struct re_registers *regs = RMATCH_REGS(match);
+
+ if (rb_reg_onig_match(re, str, reg_onig_match, NULL, regs) == ONIG_MISMATCH) {
+ rb_backref_set(Qnil);
+ return false;
}
RB_OBJ_WRITE(match, &RMATCH(match)->str, rb_str_new4(str));
@@ -1952,21 +1979,37 @@ rb_reg_match_post(VALUE match)
return str;
}
-VALUE
-rb_reg_match_last(VALUE match)
+static int
+match_last_index(VALUE match)
{
int i;
struct re_registers *regs;
- if (NIL_P(match)) return Qnil;
+ if (NIL_P(match)) return -1;
match_check(match);
regs = RMATCH_REGS(match);
- if (BEG(0) == -1) return Qnil;
+ if (BEG(0) == -1) return -1;
for (i=regs->num_regs-1; BEG(i) == -1 && i > 0; i--)
;
- if (i == 0) return Qnil;
- return rb_reg_nth_match(i, match);
+ return i;
+}
+
+VALUE
+rb_reg_match_last(VALUE match)
+{
+ int i = match_last_index(match);
+ if (i <= 0) return Qnil;
+ struct re_registers *regs = RMATCH_REGS(match);
+ return rb_str_subseq(RMATCH(match)->str, BEG(i), END(i) - BEG(i));
+}
+
+VALUE
+rb_reg_last_defined(VALUE match)
+{
+ int i = match_last_index(match);
+ if (i < 0) return Qnil;
+ return RBOOL(i);
}
static VALUE
@@ -2154,6 +2197,17 @@ match_ary_aref(VALUE match, VALUE idx, VALUE result)
* m['foo'] # => "h"
* m[:bar] # => "ge"
*
+ * If multiple captures have the same name, returns the last matched
+ * substring.
+ *
+ * m = /(?<foo>.)(?<foo>.+)/.match("hoge")
+ * # => #<MatchData "hoge" foo:"h" foo:"oge">
+ * m[:foo] #=> "oge"
+ *
+ * m = /\W(?<foo>.+)|\w(?<foo>.+)|(?<foo>.+)/.match("hoge")
+ * #<MatchData "hoge" foo:nil foo:"oge" foo:nil>
+ * m[:foo] #=> "oge"
+ *
*/
static VALUE
@@ -2337,8 +2391,8 @@ match_named_captures_iter(const OnigUChar *name, const OnigUChar *name_end,
* # => #<MatchData "01" a:"0" a:"1">
* m.named_captures #=> {"a" => "1"}
*
- * If keyword argument +symbolize_names+ is given
- * a true value, the keys in the resulting hash are Symbols:
+ * If keyword argument +symbolize_names+ is given
+ * a true value, the keys in the resulting hash are Symbols:
*
* m = /(?<a>.)(?<a>.)/.match("01")
* # => #<MatchData "01" a:"0" a:"1">
@@ -2393,7 +2447,7 @@ match_named_captures(int argc, VALUE *argv, VALUE match)
* m.deconstruct_keys([:hours, :minutes]) # => {:hours => "18", :minutes => "37"}
* m.deconstruct_keys(nil) # => {:hours => "18", :minutes => "37", :seconds => "22"}
*
- * Returns an empty hash of no named captures were defined:
+ * Returns an empty hash if no named captures were defined:
*
* m = /(\d{2}):(\d{2}):(\d{2})/.match("18:37:22")
* m.deconstruct_keys(nil) # => {}
@@ -2492,10 +2546,10 @@ match_inspect_name_iter(const OnigUChar *name, const OnigUChar *name_end,
}
/*
- * call-seq:
- * inspect -> string
+ * call-seq:
+ * inspect -> string
*
- * Returns a string representation of +self+:
+ * Returns a string representation of +self+:
*
* m = /.$/.match("foo")
* # => #<MatchData "o">
@@ -2510,7 +2564,6 @@ match_inspect_name_iter(const OnigUChar *name, const OnigUChar *name_end,
* m.inspect # => "#<MatchData \"fo\" 1:\"f\" 2:nil 3:\"o\">"
*
* Related: MatchData#to_s.
- *
*/
static VALUE
@@ -3023,55 +3076,55 @@ escape_asis:
parens++;
}
- for(s = p+1; s < end; s++) {
+ for (s = p+1; s < end; s++) {
switch(*s) {
- case 'x':
- local_extend = invert ? -1 : 1;
- break;
- case '-':
- invert = 1;
- break;
- case ':':
- case ')':
- if (local_extend == 0 ||
- (local_extend == -1 && !extended_mode) ||
- (local_extend == 1 && extended_mode)) {
- /* no changes to extended flag */
- goto fallthrough;
- }
+ case 'x':
+ local_extend = invert ? -1 : 1;
+ break;
+ case '-':
+ invert = 1;
+ break;
+ case ':':
+ case ')':
+ if (local_extend == 0 ||
+ (local_extend == -1 && !extended_mode) ||
+ (local_extend == 1 && extended_mode)) {
+ /* no changes to extended flag */
+ goto fallthrough;
+ }
- if (*s == ':') {
- /* change extended flag until ')' */
- int local_options = options;
- if (local_extend == 1) {
- local_options |= ONIG_OPTION_EXTEND;
- }
- else {
- local_options &= ~ONIG_OPTION_EXTEND;
- }
-
- rb_str_buf_cat(buf, (char *)&c, 1);
- int ret = unescape_nonascii0(&p, end, enc, buf, encp,
- has_property, err,
- local_options, 1);
- if (ret < 0) return ret;
- goto begin_scan;
+ if (*s == ':') {
+ /* change extended flag until ')' */
+ int local_options = options;
+ if (local_extend == 1) {
+ local_options |= ONIG_OPTION_EXTEND;
}
else {
- /* change extended flag for rest of expression */
- extended_mode = local_extend == 1;
- goto fallthrough;
+ local_options &= ~ONIG_OPTION_EXTEND;
}
- case 'i':
- case 'm':
- case 'a':
- case 'd':
- case 'u':
- /* other option flags, ignored during scanning */
- break;
- default:
- /* other character, no extended flag change*/
+
+ rb_str_buf_cat(buf, (char *)&c, 1);
+ int ret = unescape_nonascii0(&p, end, enc, buf, encp,
+ has_property, err,
+ local_options, 1);
+ if (ret < 0) return ret;
+ goto begin_scan;
+ }
+ else {
+ /* change extended flag for rest of expression */
+ extended_mode = local_extend == 1;
goto fallthrough;
+ }
+ case 'i':
+ case 'm':
+ case 'a':
+ case 'd':
+ case 'u':
+ /* other option flags, ignored during scanning */
+ break;
+ default:
+ /* other character, no extended flag change*/
+ goto fallthrough;
}
}
}
@@ -3213,6 +3266,15 @@ rb_reg_preprocess_dregexp(VALUE ary, int options)
return result;
}
+static void
+rb_reg_initialize_check(VALUE obj)
+{
+ rb_check_frozen(obj);
+ if (RREGEXP_PTR(obj)) {
+ rb_raise(rb_eTypeError, "already initialized regexp");
+ }
+}
+
static int
rb_reg_initialize(VALUE obj, const char *s, long len, rb_encoding *enc,
int options, onig_errmsg_buffer err,
@@ -3223,10 +3285,7 @@ rb_reg_initialize(VALUE obj, const char *s, long len, rb_encoding *enc,
rb_encoding *fixed_enc = 0;
rb_encoding *a_enc = rb_ascii8bit_encoding();
- rb_check_frozen(obj);
- if (re->ptr)
- rb_raise(rb_eTypeError, "already initialized regexp");
- re->ptr = 0;
+ rb_reg_initialize_check(obj);
if (rb_enc_dummy_p(enc)) {
errcpy(err, "can't make regexp with dummy encoding");
@@ -3562,7 +3621,7 @@ reg_match_pos(VALUE re, VALUE *strp, long pos, VALUE* set_match)
* Returns the integer index (in characters) of the first match
* for +self+ and +string+, or +nil+ if none;
* also sets the
- * {rdoc-ref:Regexp Global Variables}[rdoc-ref:Regexp@Regexp+Global+Variables]:
+ * {rdoc-ref:Regexp global variables}[rdoc-ref:Regexp@Global+Variables]:
*
* /at/ =~ 'input data' # => 7
* $~ # => #<MatchData "at">
@@ -3575,7 +3634,7 @@ reg_match_pos(VALUE re, VALUE *strp, long pos, VALUE* set_match)
* - Is a regexp literal;
* see {Regexp Literals}[rdoc-ref:literals.rdoc@Regexp+Literals].
* - Does not contain interpolations;
- * see {Regexp Interpolation}[rdoc-ref:Regexp@Regexp+Interpolation].
+ * see {Regexp interpolation}[rdoc-ref:Regexp@Interpolation+Mode].
* - Is at the left of the expression.
*
* Example:
@@ -3778,12 +3837,6 @@ rb_reg_match_m_p(int argc, VALUE *argv, VALUE re)
VALUE
rb_reg_match_p(VALUE re, VALUE str, long pos)
{
- regex_t *reg;
- onig_errmsg_buffer err = "";
- OnigPosition result;
- const UChar *start, *end;
- int tmpreg;
-
if (NIL_P(str)) return Qfalse;
str = SYMBOL_P(str) ? rb_sym2str(str) : StringValue(str);
if (pos) {
@@ -3798,33 +3851,13 @@ rb_reg_match_p(VALUE re, VALUE str, long pos)
pos = beg - RSTRING_PTR(str);
}
}
- reg = rb_reg_prepare_re0(re, str, err);
- tmpreg = reg != RREGEXP_PTR(re);
- if (!tmpreg) RREGEXP(re)->usecnt++;
- start = ((UChar*)RSTRING_PTR(str));
- end = start + RSTRING_LEN(str);
- result = onig_search(reg, start, end, start + pos, end,
- NULL, ONIG_OPTION_NONE);
- if (!tmpreg) RREGEXP(re)->usecnt--;
- if (tmpreg) {
- if (RREGEXP(re)->usecnt) {
- onig_free(reg);
- }
- else {
- onig_free(RREGEXP_PTR(re));
- RREGEXP_PTR(re) = reg;
- }
- }
- if (result < 0) {
- if (result == ONIG_MISMATCH) {
- return Qfalse;
- }
- else {
- onig_error_code_to_str((UChar*)err, (int)result);
- rb_reg_raise(RREGEXP_SRC_PTR(re), RREGEXP_SRC_LEN(re), err, re);
- }
- }
- return Qtrue;
+
+ struct reg_onig_search_args args = {
+ .pos = pos,
+ .range = RSTRING_LEN(str),
+ };
+
+ return rb_reg_onig_match(re, str, reg_onig_search, &args, NULL) == ONIG_MISMATCH ? Qfalse : Qtrue;
}
/*
@@ -3862,6 +3895,26 @@ set_timeout(rb_hrtime_t *hrt, VALUE timeout)
double2hrtime(hrt, timeout_d);
}
+static VALUE
+reg_copy(VALUE copy, VALUE orig)
+{
+ int r;
+ regex_t *re;
+
+ rb_reg_initialize_check(copy);
+ if ((r = onig_reg_copy(&re, RREGEXP_PTR(orig))) != 0) {
+ /* ONIGERR_MEMORY only */
+ rb_raise(rb_eRegexpError, "%s", onig_error_code_to_format(r));
+ }
+ RREGEXP_PTR(copy) = re;
+ RB_OBJ_WRITE(copy, &RREGEXP(copy)->src, RREGEXP(orig)->src);
+ RREGEXP_PTR(copy)->timelimit = RREGEXP_PTR(orig)->timelimit;
+ rb_enc_copy(copy, orig);
+ FL_SET_RAW(copy, FL_TEST_RAW(orig, KCODE_FIXED|REG_ENCODING_NONE));
+
+ return copy;
+}
+
struct reg_init_args {
VALUE str;
VALUE timeout;
@@ -3931,9 +3984,14 @@ static VALUE
rb_reg_initialize_m(int argc, VALUE *argv, VALUE self)
{
struct reg_init_args args;
+ VALUE re = reg_extract_args(argc, argv, &args);
- reg_extract_args(argc, argv, &args);
- reg_init_args(self, args.str, args.enc, args.flags);
+ if (NIL_P(re)) {
+ reg_init_args(self, args.str, args.enc, args.flags);
+ }
+ else {
+ reg_copy(self, re);
+ }
set_timeout(&RREGEXP_PTR(self)->timelimit, args.timeout);
@@ -4356,7 +4414,7 @@ rb_reg_init_copy(VALUE copy, VALUE re)
{
if (!OBJ_INIT_COPY(copy, re)) return copy;
rb_reg_check(re);
- return rb_reg_init_str(copy, RREGEXP_SRC(re), rb_reg_options(re));
+ return reg_copy(copy, re);
}
VALUE
@@ -4530,7 +4588,7 @@ match_setter(VALUE val, ID _x, VALUE *_y)
*
* With no argument, returns the value of <tt>$!</tt>,
* which is the result of the most recent pattern match
- * (see {Regexp Global Variables}[rdoc-ref:Regexp@Regexp+Global+Variables]):
+ * (see {Regexp global variables}[rdoc-ref:Regexp@Global+Variables]):
*
* /c(.)t/ =~ 'cat' # => 0
* Regexp.last_match # => #<MatchData "cat" 1:"a">
@@ -4579,12 +4637,9 @@ re_warn(const char *s)
rb_warn("%s", s);
}
-// The process-global timeout for regexp matching
-rb_hrtime_t rb_reg_match_time_limit = 0;
-
// This function is periodically called during regexp matching
-void
-rb_reg_check_timeout(regex_t *reg, void *end_time_)
+bool
+rb_reg_timeout_p(regex_t *reg, void *end_time_)
{
rb_hrtime_t *end_time = (rb_hrtime_t *)end_time_;
@@ -4609,10 +4664,18 @@ rb_reg_check_timeout(regex_t *reg, void *end_time_)
}
else {
if (*end_time < rb_hrtime_now()) {
- // timeout is exceeded
- rb_raise(rb_eRegexpTimeoutError, "regexp match timeout");
+ // Timeout has exceeded
+ return true;
}
}
+
+ return false;
+}
+
+void
+rb_reg_raise_timeout(void)
+{
+ rb_raise(rb_eRegexpTimeoutError, "regexp match timeout");
}
/*
@@ -4693,7 +4756,7 @@ rb_reg_timeout_get(VALUE re)
/*
* Document-class: Regexp
*
- * :include: doc/regexp.rdoc
+ * :include: doc/_regexp.rdoc
*/
void
diff --git a/regcomp.c b/regcomp.c
index be85d85f93..38bfed5631 100644
--- a/regcomp.c
+++ b/regcomp.c
@@ -195,8 +195,7 @@ unset_addr_list_init(UnsetAddrList* uslist, int size)
static void
unset_addr_list_end(UnsetAddrList* uslist)
{
- if (IS_NOT_NULL(uslist->us))
- xfree(uslist->us);
+ xfree(uslist->us);
}
static int
@@ -1274,6 +1273,10 @@ compile_length_enclose_node(EncloseNode* node, regex_t* reg)
break;
case ENCLOSE_STOP_BACKTRACK:
+ /* Disable POP_STOP_BT optimization for simple repeat under the match cache */
+ /* optimization because the match cache optimization pushes an extra item to */
+ /* the stack and it breaks the assumption for this optimization. */
+#ifndef USE_MATCH_CACHE
if (IS_ENCLOSE_STOP_BT_SIMPLE_REPEAT(node)) {
QtfrNode* qn = NQTFR(node->target);
tlen = compile_length_tree(qn->target, reg);
@@ -1283,8 +1286,11 @@ compile_length_enclose_node(EncloseNode* node, regex_t* reg)
+ SIZE_OP_PUSH + tlen + SIZE_OP_POP + SIZE_OP_JUMP;
}
else {
+#endif
len = SIZE_OP_PUSH_STOP_BT + tlen + SIZE_OP_POP_STOP_BT;
+#ifndef USE_MATCH_CACHE
}
+#endif
break;
case ENCLOSE_CONDITION:
@@ -1396,6 +1402,10 @@ compile_enclose_node(EncloseNode* node, regex_t* reg)
break;
case ENCLOSE_STOP_BACKTRACK:
+ /* Disable POP_STOP_BT optimization for simple repeat under the match cache */
+ /* optimization because the match cache optimization pushes an extra item to */
+ /* the stack and it breaks the assumption for this optimization. */
+#ifndef USE_MATCH_CACHE
if (IS_ENCLOSE_STOP_BT_SIMPLE_REPEAT(node)) {
QtfrNode* qn = NQTFR(node->target);
r = compile_tree_n_times(qn->target, qn->lower, reg);
@@ -1414,12 +1424,15 @@ compile_enclose_node(EncloseNode* node, regex_t* reg)
-((int )SIZE_OP_PUSH + len + (int )SIZE_OP_POP + (int )SIZE_OP_JUMP));
}
else {
+#endif
r = add_opcode(reg, OP_PUSH_STOP_BT);
if (r) return r;
r = compile_tree(node->target, reg);
if (r) return r;
r = add_opcode(reg, OP_POP_STOP_BT);
+#ifndef USE_MATCH_CACHE
}
+#endif
break;
case ENCLOSE_CONDITION:
@@ -3382,7 +3395,7 @@ next_setup(Node* node, Node* next_node, regex_t* reg)
}
else if (type == NT_ENCLOSE) {
EncloseNode* en = NENCLOSE(node);
- if (en->type == ENCLOSE_MEMORY) {
+ if (en->type == ENCLOSE_MEMORY && !IS_ENCLOSE_CALLED(en)) {
node = en->target;
goto retry;
}
@@ -5483,10 +5496,8 @@ clear_optimize_info(regex_t* reg)
reg->sub_anchor = 0;
reg->exact_end = (UChar* )NULL;
reg->threshold_len = 0;
- if (IS_NOT_NULL(reg->exact)) {
- xfree(reg->exact);
- reg->exact = (UChar* )NULL;
- }
+ xfree(reg->exact);
+ reg->exact = (UChar* )NULL;
}
#ifdef ONIG_DEBUG
@@ -5649,12 +5660,12 @@ extern void
onig_free_body(regex_t* reg)
{
if (IS_NOT_NULL(reg)) {
- if (IS_NOT_NULL(reg->p)) xfree(reg->p);
- if (IS_NOT_NULL(reg->exact)) xfree(reg->exact);
- if (IS_NOT_NULL(reg->int_map)) xfree(reg->int_map);
- if (IS_NOT_NULL(reg->int_map_backward)) xfree(reg->int_map_backward);
- if (IS_NOT_NULL(reg->repeat_range)) xfree(reg->repeat_range);
- if (IS_NOT_NULL(reg->chain)) onig_free(reg->chain);
+ xfree(reg->p);
+ xfree(reg->exact);
+ xfree(reg->int_map);
+ xfree(reg->int_map_backward);
+ xfree(reg->repeat_range);
+ onig_free(reg->chain);
#ifdef USE_NAMED_GROUP
onig_names_free(reg);
@@ -5671,6 +5682,80 @@ onig_free(regex_t* reg)
}
}
+static void*
+dup_copy(const void *ptr, size_t size)
+{
+ void *newptr = xmalloc(size);
+ if (IS_NOT_NULL(newptr)) {
+ memcpy(newptr, ptr, size);
+ }
+ return newptr;
+}
+
+extern int
+onig_reg_copy(regex_t** nreg, regex_t* oreg)
+{
+ if (IS_NOT_NULL(oreg)) {
+ regex_t *reg = *nreg = (regex_t* )xmalloc(sizeof(regex_t));
+ if (IS_NULL(reg)) return ONIGERR_MEMORY;
+
+ *reg = *oreg;
+
+# define COPY_FAILED(mem, size) IS_NULL(reg->mem = dup_copy(reg->mem, size))
+
+ if (IS_NOT_NULL(reg->exact)) {
+ size_t exact_size = reg->exact_end - reg->exact;
+ if (COPY_FAILED(exact, exact_size))
+ goto err;
+ (reg)->exact_end = (reg)->exact + exact_size;
+ }
+
+ if (IS_NOT_NULL(reg->int_map)) {
+ if (COPY_FAILED(int_map, sizeof(int) * ONIG_CHAR_TABLE_SIZE))
+ goto err_int_map;
+ }
+ if (IS_NOT_NULL(reg->int_map_backward)) {
+ if (COPY_FAILED(int_map_backward, sizeof(int) * ONIG_CHAR_TABLE_SIZE))
+ goto err_int_map_backward;
+ }
+ if (IS_NOT_NULL(reg->p)) {
+ if (COPY_FAILED(p, reg->alloc))
+ goto err_p;
+ }
+ if (IS_NOT_NULL(reg->repeat_range)) {
+ if (COPY_FAILED(repeat_range, reg->repeat_range_alloc * sizeof(OnigRepeatRange)))
+ goto err_repeat_range;
+ }
+ if (IS_NOT_NULL(reg->name_table)) {
+ if (onig_names_copy(reg, oreg))
+ goto err_name_table;
+ }
+ if (IS_NOT_NULL(reg->chain)) {
+ if (onig_reg_copy(&reg->chain, reg->chain))
+ goto err_chain;
+ }
+ return 0;
+# undef COPY_FAILED
+
+ err_chain:
+ onig_names_free(reg);
+ err_name_table:
+ xfree(reg->repeat_range);
+ err_repeat_range:
+ xfree(reg->p);
+ err_p:
+ xfree(reg->int_map_backward);
+ err_int_map_backward:
+ xfree(reg->int_map);
+ err_int_map:
+ xfree(reg->exact);
+ err:
+ xfree(reg);
+ return ONIGERR_MEMORY;
+ }
+ return 0;
+}
+
#ifdef RUBY
size_t
onig_memsize(const regex_t *reg)
@@ -5927,8 +6012,8 @@ onig_compile(regex_t* reg, const UChar* pattern, const UChar* pattern_end,
}
onig_node_free(root);
- if (IS_NOT_NULL(scan_env.mem_nodes_dynamic))
- xfree(scan_env.mem_nodes_dynamic);
+ xfree(scan_env.mem_nodes_dynamic);
+
return r;
}
@@ -6000,20 +6085,15 @@ onig_new(regex_t** reg, const UChar* pattern, const UChar* pattern_end,
OnigOptionType option, OnigEncoding enc, const OnigSyntaxType* syntax,
OnigErrorInfo* einfo)
{
- int r;
-
*reg = (regex_t* )xmalloc(sizeof(regex_t));
if (IS_NULL(*reg)) return ONIGERR_MEMORY;
- r = onig_reg_init(*reg, option, ONIGENC_CASE_FOLD_DEFAULT, enc, syntax);
- if (r) goto err;
-
- r = onig_compile(*reg, pattern, pattern_end, einfo);
+ int r = onig_new_without_alloc(*reg, pattern, pattern_end, option, enc, syntax, einfo);
if (r) {
- err:
onig_free(*reg);
*reg = NULL;
}
+
return r;
}
diff --git a/regenc.c b/regenc.c
index fc131d2533..eb523e1ae5 100644
--- a/regenc.c
+++ b/regenc.c
@@ -57,7 +57,7 @@ onigenc_mbclen(const OnigUChar* p,const OnigUChar* e, OnigEncoding enc)
int ret = ONIGENC_PRECISE_MBC_ENC_LEN(enc, p, e);
if (ONIGENC_MBCLEN_CHARFOUND_P(ret)) {
ret = ONIGENC_MBCLEN_CHARFOUND_LEN(ret);
- if (ret > (int)(e - p)) ret = (int)(e - p); // just for case
+ if (p + ret > e) ret = (int)(e - p); // just for case
return ret;
}
else if (ONIGENC_MBCLEN_NEEDMORE_P(ret)) {
diff --git a/regexec.c b/regexec.c
index a45be566d2..6d82429e03 100644
--- a/regexec.c
+++ b/regexec.c
@@ -242,9 +242,6 @@ The `Regexp#match` optimization by using a cache.
"cache opcode"
A cachable opcode (e.g. `OP_PUSH`, `OP_REPEAT`, etc).
It is corresponding to some cache points.
-`OP_NULL_CHECK_START` and `OP_NULL_CHECK_END_MEMST` are exceptions.
-They are counted as cache opcodes, but they have no corresponding cache points.
-They are used for resetting a match cache buffer when a null loop is detected.
"cache point"
A cachable point on matching.
@@ -258,21 +255,22 @@ We encode a match cache point to an integer value by the following equation:
"match cache point" = "position on input string" * "total number of cache points" + "cache point"
"match cache buffer"
-A bit-array for recording (caching) match cache points once arrived.
+A bit-array for memoizing (recording) match cache points once backtracked.
*/
-/* count the total number of cache opcodes for allocating a match cache buffer. */
-static OnigPosition
-count_num_cache_opcodes(const regex_t* reg, long* num_cache_opcodes_ptr)
+static OnigPosition count_num_cache_opcodes_inner(
+ const regex_t* reg,
+ MemNumType current_repeat_mem, int lookaround_nesting,
+ UChar** pp, long* num_cache_opcodes_ptr
+)
{
- UChar* p = reg->p;
- UChar* pend = p + reg->used;
+ UChar* p = *pp;
+ UChar* pend = reg->p + reg->used;
LengthType len;
MemNumType repeat_mem;
OnigEncoding enc = reg->enc;
- MemNumType current_repeat_mem = -1;
- long num_cache_opcodes = 0;
- long num_cache_opcodes_at_null_check_start = -1;
+ long num_cache_opcodes = *num_cache_opcodes_ptr;
+ OnigPosition result;
while (p < pend) {
switch (*p++) {
@@ -326,7 +324,7 @@ count_num_cache_opcodes(const regex_t* reg, long* num_cache_opcodes_ptr)
break;
case OP_ANYCHAR_STAR:
case OP_ANYCHAR_ML_STAR:
- num_cache_opcodes++;break;
+ num_cache_opcodes++; break;
case OP_ANYCHAR_STAR_PEEK_NEXT:
case OP_ANYCHAR_ML_STAR_PEEK_NEXT:
p++; num_cache_opcodes++; break;
@@ -370,7 +368,12 @@ count_num_cache_opcodes(const regex_t* reg, long* num_cache_opcodes_ptr)
case OP_MEMORY_END_PUSH_REC:
case OP_MEMORY_END:
case OP_MEMORY_END_REC:
- p += SIZE_MEMNUM; break;
+ p += SIZE_MEMNUM;
+ // A memory (capture) in look-around is found.
+ if (lookaround_nesting != 0) {
+ goto impossible;
+ }
+ break;
case OP_KEEP:
break;
@@ -400,7 +403,16 @@ count_num_cache_opcodes(const regex_t* reg, long* num_cache_opcodes_ptr)
if (reg->repeat_range[repeat_mem].lower == 0) {
num_cache_opcodes++;
}
- current_repeat_mem = repeat_mem;
+ result = count_num_cache_opcodes_inner(reg, repeat_mem, lookaround_nesting, &p, &num_cache_opcodes);
+ if (result < 0 || num_cache_opcodes < 0) {
+ goto fail;
+ }
+ {
+ OnigRepeatRange *repeat_range = &reg->repeat_range[repeat_mem];
+ if (repeat_range->lower < repeat_range->upper) {
+ num_cache_opcodes++;
+ }
+ }
break;
case OP_REPEAT_INC:
case OP_REPEAT_INC_NG:
@@ -409,50 +421,73 @@ count_num_cache_opcodes(const regex_t* reg, long* num_cache_opcodes_ptr)
// A lone or invalid OP_REPEAT_INC is found.
goto impossible;
}
- {
- OnigRepeatRange *repeat_range = &reg->repeat_range[repeat_mem];
- if (repeat_range->lower < repeat_range->upper) {
- num_cache_opcodes++;
- }
- current_repeat_mem = -1;
- }
- break;
+ goto exit;
case OP_REPEAT_INC_SG:
case OP_REPEAT_INC_NG_SG:
goto impossible;
case OP_NULL_CHECK_START:
p += SIZE_MEMNUM;
- if (num_cache_opcodes_at_null_check_start != -1) {
- // A nested OP_NULL_CHECK_START is not yet supported.
- goto impossible;
- }
- num_cache_opcodes_at_null_check_start = num_cache_opcodes;
break;
case OP_NULL_CHECK_END:
case OP_NULL_CHECK_END_MEMST_PUSH:
p += SIZE_MEMNUM;
- num_cache_opcodes_at_null_check_start = -1;
break;
case OP_NULL_CHECK_END_MEMST:
p += SIZE_MEMNUM;
- // OP_NULL_CHECK_START and OP_NULL_CHECK_END_MEMST are counted as cache opcodes.
- if (num_cache_opcodes_at_null_check_start < num_cache_opcodes) {
- num_cache_opcodes += 2;
- }
- num_cache_opcodes_at_null_check_start = -1;
break;
case OP_PUSH_POS:
- case OP_POP_POS:
+ if (lookaround_nesting < 0) {
+ // A look-around nested in a atomic grouping is found.
+ goto impossible;
+ }
+ result = count_num_cache_opcodes_inner(reg, current_repeat_mem, lookaround_nesting + 1, &p, &num_cache_opcodes);
+ if (result < 0 || num_cache_opcodes < 0) {
+ goto fail;
+ }
+ break;
case OP_PUSH_POS_NOT:
- case OP_FAIL_POS:
- goto impossible;
- case OP_PUSH_STOP_BT:
- case OP_POP_STOP_BT:
+ if (lookaround_nesting < 0) {
+ // A look-around nested in a atomic grouping is found.
+ goto impossible;
+ }
+ p += SIZE_RELADDR;
+ result = count_num_cache_opcodes_inner(reg, current_repeat_mem, lookaround_nesting + 1, &p, &num_cache_opcodes);
+ if (result < 0 || num_cache_opcodes < 0) {
+ goto fail;
+ }
break;
- case OP_LOOK_BEHIND:
case OP_PUSH_LOOK_BEHIND_NOT:
+ if (lookaround_nesting < 0) {
+ // A look-around nested in a atomic grouping is found.
+ goto impossible;
+ }
+ p += SIZE_RELADDR;
+ p += SIZE_LENGTH;
+ result = count_num_cache_opcodes_inner(reg, current_repeat_mem, lookaround_nesting + 1, &p, &num_cache_opcodes);
+ if (result < 0 || num_cache_opcodes < 0) {
+ goto fail;
+ }
+ break;
+ case OP_PUSH_STOP_BT:
+ if (lookaround_nesting != 0) {
+ // A nested atomic grouping is found.
+ goto impossible;
+ }
+ result = count_num_cache_opcodes_inner(reg, current_repeat_mem, -1, &p, &num_cache_opcodes);
+ if (result < 0 || num_cache_opcodes < 0) {
+ goto fail;
+ }
+ break;
+ case OP_POP_POS:
+ case OP_FAIL_POS:
case OP_FAIL_LOOK_BEHIND_NOT:
+ case OP_POP_STOP_BT:
+ goto exit;
+ case OP_LOOK_BEHIND:
+ p += SIZE_LENGTH;
+ break;
+
case OP_PUSH_ABSENT_POS:
case OP_ABSENT_END:
case OP_ABSENT:
@@ -482,9 +517,15 @@ count_num_cache_opcodes(const regex_t* reg, long* num_cache_opcodes_ptr)
}
}
+exit:
+ *pp = p;
*num_cache_opcodes_ptr = num_cache_opcodes;
return 0;
+fail:
+ *num_cache_opcodes_ptr = num_cache_opcodes;
+ return result;
+
impossible:
*num_cache_opcodes_ptr = NUM_CACHE_OPCODES_IMPOSSIBLE;
return 0;
@@ -493,32 +534,48 @@ bytecode_error:
return ONIGERR_UNDEFINED_BYTECODE;
}
-/* collect cache opcodes from the given regex program, and compute the total number of cache points. */
+/* count the total number of cache opcodes for allocating a match cache buffer. */
static OnigPosition
-init_cache_opcodes(const regex_t* reg, OnigCacheOpcode* cache_opcodes, long* num_cache_points_ptr)
+count_num_cache_opcodes(const regex_t* reg, long* num_cache_opcodes_ptr)
{
- UChar* pbegin;
UChar* p = reg->p;
- UChar* pend = p + reg->used;
+ *num_cache_opcodes_ptr = 0;
+ OnigPosition result = count_num_cache_opcodes_inner(reg, -1, 0, &p, num_cache_opcodes_ptr);
+ if (result == 0 && *num_cache_opcodes_ptr >= 0 && p != reg->p + reg->used) {
+ return ONIGERR_UNDEFINED_BYTECODE;
+ }
+
+ return result;
+}
+
+static OnigPosition
+init_cache_opcodes_inner(
+ const regex_t* reg,
+ MemNumType current_repeat_mem, int lookaround_nesting,
+ OnigCacheOpcode** cache_opcodes_ptr, UChar** pp, long* num_cache_points_ptr
+)
+{
+ UChar* p = *pp;
+ UChar* pend = reg->p + reg->used;
+ UChar* pbegin;
LengthType len;
MemNumType repeat_mem;
OnigEncoding enc = reg->enc;
- MemNumType current_repeat_mem = -1;
- long cache_point = 0;
- long num_cache_points_at_repeat = 0;
- OnigCacheOpcode* cache_opcodes_at_repeat = NULL;
- UChar* null_check_start_addr = NULL;
- OnigCacheOpcode* cache_opcodes_at_null_check_start = NULL;
-
-# define INC_CACHE_OPCODES do {\
+ long cache_point = *num_cache_points_ptr;
+ OnigCacheOpcode *cache_opcodes = *cache_opcodes_ptr;
+ OnigPosition result;
+
+# define INC_CACHE_OPCODES do {\
cache_opcodes->addr = pbegin;\
- cache_opcodes->cache_point = cache_point - num_cache_points_at_repeat;\
+ cache_opcodes->cache_point = cache_point;\
cache_opcodes->outer_repeat_mem = current_repeat_mem;\
- cache_opcodes->num_cache_points_at_outer_repeat = num_cache_points_at_repeat;\
+ cache_opcodes->num_cache_points_at_outer_repeat = 0;\
cache_opcodes->num_cache_points_in_outer_repeat = 0;\
- cache_point++;\
+ cache_opcodes->lookaround_nesting = lookaround_nesting;\
+ cache_opcodes->match_addr = NULL;\
+ cache_point += lookaround_nesting != 0 ? 2 : 1;\
cache_opcodes++;\
- } while (0)
+ } while (0)
while (p < pend) {
pbegin = p;
@@ -620,7 +677,11 @@ init_cache_opcodes(const regex_t* reg, OnigCacheOpcode* cache_opcodes, long* num
case OP_MEMORY_END_PUSH_REC:
case OP_MEMORY_END:
case OP_MEMORY_END_REC:
- p += SIZE_MEMNUM; break;
+ p += SIZE_MEMNUM;
+ if (lookaround_nesting != 0) {
+ goto unexpected_bytecode_error;
+ }
+ break;
case OP_KEEP:
break;
@@ -648,77 +709,92 @@ init_cache_opcodes(const regex_t* reg, OnigCacheOpcode* cache_opcodes, long* num
if (reg->repeat_range[repeat_mem].lower == 0) {
INC_CACHE_OPCODES;
}
- current_repeat_mem = repeat_mem;
- num_cache_points_at_repeat = cache_point;
- cache_opcodes_at_repeat = cache_opcodes;
- break;
- case OP_REPEAT_INC:
- case OP_REPEAT_INC_NG:
- GET_MEMNUM_INC(repeat_mem, p);
{
- long num_cache_points_in_repeat = cache_point - num_cache_points_at_repeat;
+ long num_cache_points_in_repeat = 0;
+ long num_cache_points_at_repeat = cache_point;
+ OnigCacheOpcode* cache_opcodes_in_repeat = cache_opcodes;
+ result = init_cache_opcodes_inner(reg, repeat_mem, lookaround_nesting, &cache_opcodes, &p, &num_cache_points_in_repeat);
+ if (result != 0) {
+ goto fail;
+ }
OnigRepeatRange *repeat_range = &reg->repeat_range[repeat_mem];
if (repeat_range->lower < repeat_range->upper) {
INC_CACHE_OPCODES;
- cache_point--;
+ cache_point -= lookaround_nesting != 0 ? 2 : 1;
}
- cache_point -= num_cache_points_in_repeat;
int repeat_bounds = repeat_range->upper == 0x7fffffff ? 1 : repeat_range->upper - repeat_range->lower;
- cache_point += num_cache_points_in_repeat * repeat_range->lower + (num_cache_points_in_repeat + 1) * repeat_bounds;
- OnigCacheOpcode* cache_opcodes_in_repeat = cache_opcodes - 1;
- while (cache_opcodes_at_repeat <= cache_opcodes_in_repeat) {
+ cache_point += num_cache_points_in_repeat * repeat_range->lower + (num_cache_points_in_repeat + (lookaround_nesting != 0 ? 2 : 1)) * repeat_bounds;
+ for (; cache_opcodes_in_repeat < cache_opcodes; cache_opcodes_in_repeat++) {
+ cache_opcodes_in_repeat->num_cache_points_at_outer_repeat = num_cache_points_at_repeat;
cache_opcodes_in_repeat->num_cache_points_in_outer_repeat = num_cache_points_in_repeat;
- cache_opcodes_in_repeat--;
}
- current_repeat_mem = -1;
- num_cache_points_at_repeat = 0;
- cache_opcodes_at_repeat = NULL;
}
break;
+ case OP_REPEAT_INC:
+ case OP_REPEAT_INC_NG:
+ p += SIZE_MEMNUM;
+ goto exit;
case OP_REPEAT_INC_SG:
case OP_REPEAT_INC_NG_SG:
goto unexpected_bytecode_error;
case OP_NULL_CHECK_START:
- if (cache_opcodes_at_null_check_start != NULL) {
- goto unexpected_bytecode_error;
- }
p += SIZE_MEMNUM;
- null_check_start_addr = pbegin;
- cache_opcodes_at_null_check_start = cache_opcodes;
break;
case OP_NULL_CHECK_END:
case OP_NULL_CHECK_END_MEMST_PUSH:
p += SIZE_MEMNUM;
- cache_opcodes_at_null_check_start = NULL;
break;
case OP_NULL_CHECK_END_MEMST:
p += SIZE_MEMNUM;
+ break;
+
+ case OP_PUSH_POS:
+ lookaround:
{
- if (cache_opcodes_at_null_check_start < cache_opcodes) {
- *cache_opcodes = *(cache_opcodes - 1);
- cache_opcodes->addr = pbegin;
- cache_opcodes++;
- long num_cache_opcodes_in_null_check = (cache_opcodes - cache_opcodes_at_null_check_start);
- xmemmove(cache_opcodes_at_null_check_start + 1, cache_opcodes_at_null_check_start, sizeof(OnigCacheOpcode) * num_cache_opcodes_in_null_check);
- cache_opcodes_at_null_check_start->addr = null_check_start_addr;
- cache_opcodes++;
+ OnigCacheOpcode* cache_opcodes_in_lookaround = cache_opcodes;
+ result = init_cache_opcodes_inner(reg, current_repeat_mem, lookaround_nesting + 1, &cache_opcodes, &p, &cache_point);
+ if (result != 0) {
+ goto fail;
+ }
+ UChar* match_addr = p - 1;
+ for (; cache_opcodes_in_lookaround < cache_opcodes; cache_opcodes_in_lookaround++) {
+ if (cache_opcodes_in_lookaround->match_addr == NULL) {
+ cache_opcodes_in_lookaround->match_addr = match_addr;
+ }
}
- cache_opcodes_at_null_check_start = NULL;
}
break;
-
- case OP_PUSH_POS:
- case OP_POP_POS:
case OP_PUSH_POS_NOT:
- case OP_FAIL_POS:
- goto unexpected_bytecode_error;
+ p += SIZE_RELADDR;
+ goto lookaround;
+ case OP_PUSH_LOOK_BEHIND_NOT:
+ p += SIZE_RELADDR;
+ p += SIZE_LENGTH;
+ goto lookaround;
case OP_PUSH_STOP_BT:
- case OP_POP_STOP_BT:
+ {
+ OnigCacheOpcode* cache_opcodes_in_atomic = cache_opcodes;
+ result = init_cache_opcodes_inner(reg, current_repeat_mem, -1, &cache_opcodes, &p, &cache_point);
+ if (result != 0) {
+ goto fail;
+ }
+ UChar* match_addr = p - 1;
+ for (; cache_opcodes_in_atomic < cache_opcodes; cache_opcodes_in_atomic++) {
+ if (cache_opcodes_in_atomic->match_addr == NULL) {
+ cache_opcodes_in_atomic->match_addr = match_addr;
+ }
+ }
+ }
break;
- case OP_LOOK_BEHIND:
- case OP_PUSH_LOOK_BEHIND_NOT:
+ case OP_POP_POS:
+ case OP_FAIL_POS:
case OP_FAIL_LOOK_BEHIND_NOT:
- case OP_PUSH_ABSENT_POS:
+ case OP_POP_STOP_BT:
+ goto exit;
+ case OP_LOOK_BEHIND:
+ p += SIZE_LENGTH;
+ break;
+
case OP_ABSENT_END:
case OP_ABSENT:
goto unexpected_bytecode_error;
@@ -747,15 +823,35 @@ init_cache_opcodes(const regex_t* reg, OnigCacheOpcode* cache_opcodes, long* num
}
}
+exit:
+ *cache_opcodes_ptr = cache_opcodes;
+ *pp = p;
*num_cache_points_ptr = cache_point;
return 0;
+fail:
+ return result;
+
unexpected_bytecode_error:
return ONIGERR_UNEXPECTED_BYTECODE;
bytecode_error:
return ONIGERR_UNDEFINED_BYTECODE;
}
+
+/* collect cache opcodes from the given regex program, and compute the total number of cache points. */
+static OnigPosition
+init_cache_opcodes(const regex_t* reg, OnigCacheOpcode* cache_opcodes_ptr, long* num_cache_points_ptr)
+{
+ UChar* p = reg->p;
+ *num_cache_points_ptr = 0;
+ OnigPosition result = init_cache_opcodes_inner(reg, -1, 0, &cache_opcodes_ptr, &p, num_cache_points_ptr);
+ if (result == 0 && p != reg->p + reg->used) {
+ return ONIGERR_UNDEFINED_BYTECODE;
+ }
+
+ return result;
+}
#else
static OnigPosition
count_num_cache_opcodes(regex_t* reg, long* num_cache_opcodes)
@@ -886,14 +982,18 @@ onig_region_free(OnigRegion* r, int free_self)
{
if (r) {
if (r->allocated > 0) {
- if (r->beg) xfree(r->beg);
- if (r->end) xfree(r->end);
- r->allocated = 0;
+ xfree(r->beg);
+ xfree(r->end);
}
#ifdef USE_CAPTURE_HISTORY
history_root_free(r);
#endif
- if (free_self) xfree(r);
+ if (free_self) {
+ xfree(r);
+ }
+ else {
+ memset(r, 0, sizeof(OnigRegion));
+ }
}
}
@@ -929,31 +1029,33 @@ onig_region_copy(OnigRegion* to, const OnigRegion* from)
/* stack type */
/* used by normal-POP */
-#define STK_ALT 0x0001
-#define STK_LOOK_BEHIND_NOT 0x0002
-#define STK_POS_NOT 0x0003
+#define STK_ALT 0x0001
+#define STK_LOOK_BEHIND_NOT 0x0002
+#define STK_POS_NOT 0x0003
/* handled by normal-POP */
-#define STK_MEM_START 0x0100
-#define STK_MEM_END 0x8200
-#define STK_REPEAT_INC 0x0300
-#define STK_STATE_CHECK_MARK 0x1000
+#define STK_MEM_START 0x0100
+#define STK_MEM_END 0x8200
+#define STK_REPEAT_INC 0x0300
+#define STK_STATE_CHECK_MARK 0x1000
/* avoided by normal-POP */
-#define STK_NULL_CHECK_START 0x3000
-#define STK_NULL_CHECK_END 0x5000 /* for recursive call */
-#define STK_MEM_END_MARK 0x8400
-#define STK_POS 0x0500 /* used when POP-POS */
-#define STK_STOP_BT 0x0600 /* mark for "(?>...)" */
-#define STK_REPEAT 0x0700
-#define STK_CALL_FRAME 0x0800
-#define STK_RETURN 0x0900
-#define STK_VOID 0x0a00 /* for fill a blank */
-#define STK_ABSENT_POS 0x0b00 /* for absent */
-#define STK_ABSENT 0x0c00 /* absent inner loop marker */
+#define STK_NULL_CHECK_START 0x3000
+#define STK_NULL_CHECK_END 0x5000 /* for recursive call */
+#define STK_MEM_END_MARK 0x8400
+#define STK_POS 0x0500 /* used when POP-POS */
+#define STK_STOP_BT 0x0600 /* mark for "(?>...)" */
+#define STK_REPEAT 0x0700
+#define STK_CALL_FRAME 0x0800
+#define STK_RETURN 0x0900
+#define STK_VOID 0x0a00 /* for fill a blank */
+#define STK_ABSENT_POS 0x0b00 /* for absent */
+#define STK_ABSENT 0x0c00 /* absent inner loop marker */
+#define STK_MATCH_CACHE_POINT 0x0d00 /* for the match cache optimization */
+#define STK_ATOMIC_MATCH_CACHE_POINT 0x0e00
/* stack type check mask */
-#define STK_MASK_POP_USED 0x00ff
-#define STK_MASK_TO_VOID_TARGET 0x10ff
-#define STK_MASK_MEM_END_OR_MARK 0x8000 /* MEM_END or MEM_END_MARK */
+#define STK_MASK_POP_USED 0x00ff
+#define STK_MASK_TO_VOID_TARGET 0x10ff
+#define STK_MASK_MEM_END_OR_MARK 0x8000 /* MEM_END or MEM_END_MARK */
#ifdef USE_MATCH_CACHE
#define MATCH_ARG_INIT_MATCH_CACHE(msa) do {\
@@ -965,8 +1067,8 @@ onig_region_copy(OnigRegion* to, const OnigRegion* from)
(msa).match_cache_buf = (uint8_t*)NULL;\
} while(0)
#define MATCH_ARG_FREE_MATCH_CACHE(msa) do {\
- if ((msa).cache_opcodes != NULL) xfree((msa).cache_opcodes);\
- if ((msa).match_cache_buf != NULL) xfree((msa).match_cache_buf);\
+ xfree((msa).cache_opcodes);\
+ xfree((msa).match_cache_buf);\
(msa).cache_opcodes = (OnigCacheOpcode*)NULL;\
(msa).match_cache_buf = (uint8_t*)NULL;\
} while(0)
@@ -1031,15 +1133,15 @@ onig_region_copy(OnigRegion* to, const OnigRegion* from)
} while(0)
# define MATCH_ARG_FREE(msa) do {\
- if ((msa).stack_p) xfree((msa).stack_p);\
+ xfree((msa).stack_p);\
if ((msa).state_check_buff_size >= STATE_CHECK_BUFF_MALLOC_THRESHOLD_SIZE) { \
- if ((msa).state_check_buff) xfree((msa).state_check_buff);\
+ xfree((msa).state_check_buff);\
}\
MATCH_ARG_FREE_MATCH_CACHE(msa);\
} while(0)
#else /* USE_COMBINATION_EXPLOSION_CHECK */
# define MATCH_ARG_FREE(msa) do {\
- if ((msa).stack_p) xfree((msa).stack_p);\
+ xfree((msa).stack_p);\
MATCH_ARG_FREE_MATCH_CACHE(msa);\
} while (0)
#endif /* USE_COMBINATION_EXPLOSION_CHECK */
@@ -1151,7 +1253,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
int r = stack_double(&stk_base, &stk_end, &stk, stk_alloc, msa);\
if (r != 0) {\
STACK_SAVE;\
- if (xmalloc_base) xfree(xmalloc_base);\
+ xfree(xmalloc_base);\
return r;\
}\
}\
@@ -1383,6 +1485,15 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
STACK_INC;\
} while(0)
+#define STACK_PUSH_MATCH_CACHE_POINT(match_cache_point_index, match_cache_point_mask) do {\
+ STACK_ENSURE(1);\
+ stk->type = STK_MATCH_CACHE_POINT;\
+ stk->null_check = stk == stk_base ? 0 : (stk-1)->null_check;\
+ stk->u.match_cache_point.index = (match_cache_point_index);\
+ stk->u.match_cache_point.mask = (match_cache_point_mask);\
+ STACK_INC;\
+} while(0)
+
#ifdef ONIG_DEBUG
# define STACK_BASE_CHECK(p, at) \
@@ -1394,6 +1505,42 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
# define STACK_BASE_CHECK(p, at)
#endif
+#ifdef ONIG_DEBUG_MATCH_CACHE
+# define MATCH_CACHE_DEBUG_MEMOIZE(stkp) fprintf(stderr, "MATCH CACHE: memoize (index=%ld mask=%d)\n", stkp->u.match_cache_point.index, stkp->u.match_cache_point.mask);
+#else
+# define MATCH_CACHE_DEBUG_MEMOIZE(stkp) ((void) 0)
+#endif
+
+#ifdef USE_MATCH_CACHE
+# define INC_NUM_FAILS msa->num_fails++
+# define MEMOIZE_MATCH_CACHE_POINT do {\
+ if (stk->type == STK_MATCH_CACHE_POINT) {\
+ msa->match_cache_buf[stk->u.match_cache_point.index] |= stk->u.match_cache_point.mask;\
+ MATCH_CACHE_DEBUG_MEMOIZE(stk);\
+ } else if (stk->type == STK_ATOMIC_MATCH_CACHE_POINT) {\
+ memoize_extended_match_cache_point(msa->match_cache_buf, stk->u.match_cache_point.index, stk->u.match_cache_point.mask);\
+ MATCH_CACHE_DEBUG_MEMOIZE(stkp);\
+ }\
+ } while(0)
+# define MEMOIZE_LOOKAROUND_MATCH_CACHE_POINT(stkp) do {\
+ if (stkp->type == STK_MATCH_CACHE_POINT) {\
+ stkp->type = STK_VOID;\
+ memoize_extended_match_cache_point(msa->match_cache_buf, stkp->u.match_cache_point.index, stkp->u.match_cache_point.mask);\
+ MATCH_CACHE_DEBUG_MEMOIZE(stkp);\
+ }\
+ } while(0)
+# define MEMOIZE_ATOMIC_MATCH_CACHE_POINT do {\
+ if (stk->type == STK_MATCH_CACHE_POINT) {\
+ memoize_extended_match_cache_point(msa->match_cache_buf, stk->u.match_cache_point.index, stk->u.match_cache_point.mask);\
+ MATCH_CACHE_DEBUG_MEMOIZE(stkp);\
+ }\
+ } while(0)
+#else
+# define INC_NUM_FAILS ((void) 0)
+# define MEMOIZE_MATCH_CACHE_POINT ((void) 0)
+# define MEMOIZE_LOOKAROUND_MATCH_CACHE_POINT ((void) 0)
+#endif
+
#define STACK_POP_ONE do {\
stk--;\
STACK_BASE_CHECK(stk, "STACK_POP_ONE"); \
@@ -1407,6 +1554,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
STACK_BASE_CHECK(stk, "STACK_POP"); \
if ((stk->type & STK_MASK_POP_USED) != 0) break;\
ELSE_IF_STATE_CHECK_MARK(stk);\
+ MEMOIZE_MATCH_CACHE_POINT;\
}\
break;\
case STACK_POP_LEVEL_MEM_START:\
@@ -1419,6 +1567,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
mem_end_stk[stk->u.mem.num] = stk->u.mem.end;\
}\
ELSE_IF_STATE_CHECK_MARK(stk);\
+ MEMOIZE_MATCH_CACHE_POINT;\
}\
break;\
default:\
@@ -1438,6 +1587,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
mem_end_stk[stk->u.mem.num] = stk->u.mem.end;\
}\
ELSE_IF_STATE_CHECK_MARK(stk);\
+ MEMOIZE_MATCH_CACHE_POINT;\
}\
break;\
}\
@@ -1459,7 +1609,11 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
mem_start_stk[stk->u.mem.num] = stk->u.mem.start;\
mem_end_stk[stk->u.mem.num] = stk->u.mem.end;\
}\
+ else if (IS_TO_VOID_TARGET(stk)) {\
+ INC_NUM_FAILS;\
+ }\
ELSE_IF_STATE_CHECK_MARK(stk);\
+ MEMOIZE_LOOKAROUND_MATCH_CACHE_POINT(stk);\
}\
} while(0)
@@ -1516,12 +1670,14 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
k--;\
STACK_BASE_CHECK(k, "STACK_POS_END"); \
if (IS_TO_VOID_TARGET(k)) {\
+ INC_NUM_FAILS;\
k->type = STK_VOID;\
}\
else if (k->type == STK_POS) {\
k->type = STK_VOID;\
break;\
}\
+ MEMOIZE_LOOKAROUND_MATCH_CACHE_POINT(k);\
}\
} while(0)
@@ -1531,12 +1687,28 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
k--;\
STACK_BASE_CHECK(k, "STACK_STOP_BT_END"); \
if (IS_TO_VOID_TARGET(k)) {\
+ INC_NUM_FAILS;\
k->type = STK_VOID;\
}\
else if (k->type == STK_STOP_BT) {\
k->type = STK_VOID;\
break;\
}\
+ else if (k->type == STK_MATCH_CACHE_POINT) {\
+ k->type = STK_ATOMIC_MATCH_CACHE_POINT;\
+ }\
+ }\
+} while(0)
+
+#define STACK_STOP_BT_FAIL do {\
+ while (1) {\
+ stk--;\
+ STACK_BASE_CHECK(stk, "STACK_STOP_BT_END"); \
+ if (stk->type == STK_STOP_BT) {\
+ stk->type = STK_VOID;\
+ break;\
+ }\
+ MEMOIZE_ATOMIC_MATCH_CACHE_POINT;\
}\
} while(0)
@@ -1575,7 +1747,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
}\
} while(0)
-#define STACK_NULL_CHECK_MEMST(isnull,ischange,id,s,reg) do {\
+#define STACK_NULL_CHECK_MEMST(isnull,id,s,reg) do {\
OnigStackType* k = STACK_AT((stk-1)->null_check)+1;\
while (1) {\
k--;\
@@ -1592,14 +1764,14 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
while (k < stk) {\
if (k->type == STK_MEM_START) {\
if (k->u.mem.end == INVALID_STACK_INDEX) {\
- (isnull) = 0; (ischange) = 1; break;\
+ (isnull) = 0; break;\
}\
if (BIT_STATUS_AT(reg->bt_mem_end, k->u.mem.num))\
endp = STACK_AT(k->u.mem.end)->u.mem.pstr;\
else\
endp = (UChar* )k->u.mem.end;\
if (STACK_AT(k->u.mem.start)->u.mem.pstr != endp) {\
- (isnull) = 0; (ischange) = 1; break;\
+ (isnull) = 0; break;\
}\
else if (endp != s) {\
(isnull) = -1; /* empty, but position changed */ \
@@ -1771,6 +1943,19 @@ static int string_cmp_ic(OnigEncoding enc, int case_fold_flag,
# define ABSENT_END_POS end
#endif /* USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE */
+int onigenc_mbclen_approximate(const OnigUChar* p,const OnigUChar* e, const struct OnigEncodingTypeST* enc);
+
+static inline int
+enclen_approx(OnigEncoding enc, const OnigUChar* p, const OnigUChar* e)
+{
+ if (enc->max_enc_len == enc->min_enc_len) {
+ return (p < e ? enc->min_enc_len : 0);
+ }
+ else {
+ return onigenc_mbclen_approximate(p, e, enc);
+ }
+}
+
#ifdef USE_CAPTURE_HISTORY
static int
@@ -2004,6 +2189,7 @@ stack_type_str(int stack_type)
case STK_VOID: return "Void ";
case STK_ABSENT_POS: return "AbsPos";
case STK_ABSENT: return "Absent";
+ case STK_MATCH_CACHE_POINT: return "MCache";
default: return " ";
}
}
@@ -2025,7 +2211,7 @@ bsearch_cache_opcodes(const OnigCacheOpcode *cache_opcodes, long num_cache_opcod
}
static long
-find_cache_point(regex_t* reg, const OnigCacheOpcode* cache_opcodes, long num_cache_opcodes, const UChar* p, const OnigStackType *stk, const OnigStackIndex *repeat_stk)
+find_cache_point(regex_t* reg, const OnigCacheOpcode* cache_opcodes, long num_cache_opcodes, const UChar* p, const OnigStackType *stk, const OnigStackIndex *repeat_stk, const OnigCacheOpcode **cache_opcode_ptr)
{
long m;
const OnigCacheOpcode* cache_opcode;
@@ -2044,6 +2230,7 @@ find_cache_point(regex_t* reg, const OnigCacheOpcode* cache_opcodes, long num_ca
}
cache_opcode = &cache_opcodes[m];
+ *cache_opcode_ptr = &cache_opcodes[m];
cache_point = cache_opcode->cache_point;
if (cache_opcode->outer_repeat_mem == -1) {
return cache_point;
@@ -2075,54 +2262,23 @@ find_cache_point(regex_t* reg, const OnigCacheOpcode* cache_opcodes, long num_ca
cache_point;
}
-static void
-reset_match_cache_on_null_check(regex_t* reg, const UChar* pstart, const UChar* pend, long pos, uint8_t* match_cache_buf, const OnigCacheOpcode* cache_opcodes, long num_cache_opcodes, long num_cache_points, const OnigStackType *stk, const OnigStackIndex *repeat_stk)
-{
- long cache_point_start, cache_point_end;
- long match_cache_point_start, match_cache_point_end;
- long match_cache_point_start_index, match_cache_point_end_index;
- uint8_t match_cache_point_start_bit, match_cache_point_end_bit;
-
-#ifdef ONIG_DEBUG_MATCH_CACHE
- fprintf(stderr, "MATCH CACHE: reset start=%p end=%p pos=%ld cache_opcodes=%p num_cache_opcodes=%ld\n", pstart, pend, pos, cache_opcodes, num_cache_opcodes);
-#endif
-
- cache_point_start = find_cache_point(reg, cache_opcodes, num_cache_opcodes, pstart, stk, repeat_stk);
- cache_point_end = find_cache_point(reg, cache_opcodes, num_cache_opcodes, pend, stk, repeat_stk);
- // Skip to reset if `pstart` or `pend` is not found.
- // This situation occurs when there is no cache point
- // between OP_NULL_CHECK_START and OP_NULL_CHECK_END_MEMST.
- if (cache_point_start == -1 || cache_point_end == -1) return;
-
- match_cache_point_start = pos * num_cache_points + cache_point_start;
- match_cache_point_end = pos * num_cache_points + cache_point_end;
- match_cache_point_start_index = match_cache_point_start >> 3;
- match_cache_point_end_index = match_cache_point_end >> 3;
- match_cache_point_start_bit = match_cache_point_start & 7;
- match_cache_point_end_bit = match_cache_point_end & 7;
-
-#ifdef ONIG_DEBUG_MATCH_CACHE
- fprintf(stderr, "MATCH CACHE: reset start %ld (%ld index=%ld bit=%d)\n", match_cache_point_start, cache_point_start, match_cache_point_start_index, match_cache_point_start_bit);
- fprintf(stderr, "MATCH CACHE: reset end %ld (%ld index=%ld bit=%d)\n", match_cache_point_end, cache_point_end, match_cache_point_end_index, match_cache_point_end_bit);
-#endif
+static int check_extended_match_cache_point(uint8_t *match_cache_buf, long match_cache_point_index, uint8_t match_cache_point_mask) {
+ if (match_cache_point_mask & 0x80) {
+ return (match_cache_buf[match_cache_point_index + 1] & 0x01) > 0;
+ } else {
+ return (match_cache_buf[match_cache_point_index] & (match_cache_point_mask << 1)) > 0;
+ }
+}
- if (match_cache_point_start_index == match_cache_point_end_index) {
- match_cache_buf[match_cache_point_start_index] &=
- (((1 << (8 - match_cache_point_end_bit - 1)) - 1) << (match_cache_point_end_bit + 1)) |
- ((1 << match_cache_point_start_bit) - 1);
+static void memoize_extended_match_cache_point(uint8_t *match_cache_buf, long match_cache_point_index, uint8_t match_cache_point_mask) {
+ match_cache_buf[match_cache_point_index] |= match_cache_point_mask;
+ if (match_cache_point_mask & 0x80) {
+ match_cache_buf[match_cache_point_index + 1] |= 0x01;
} else {
- if (match_cache_point_start_bit) {
- match_cache_buf[match_cache_point_start_index] &= (1 << (match_cache_point_start_bit - 1)) - 1;
- match_cache_point_start_index++;
- }
- if (match_cache_point_start_index < match_cache_point_end_index) {
- xmemset(&match_cache_buf[match_cache_point_start_index], 0, match_cache_point_end_index - match_cache_point_start_index);
- if (match_cache_point_end_bit) {
- match_cache_buf[match_cache_point_end_index] &= (((1 << (8 - match_cache_point_end_bit - 1)) - 1) << (match_cache_point_end_bit + 1));
- }
- }
+ match_cache_buf[match_cache_point_index] |= match_cache_point_mask << 1;
}
}
+
#endif /* USE_MATCH_CACHE */
/* match data(str - end) from position (sstart). */
@@ -2150,7 +2306,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
UChar *pkeep;
char *alloca_base;
char *xmalloc_base = NULL;
- OnigStackType *stk_alloc, *stk_base, *stk, *stk_end;
+ OnigStackType *stk_alloc, *stk_base = NULL, *stk, *stk_end;
OnigStackType *stkp; /* used as any purpose. */
OnigStackIndex si;
OnigStackIndex *repeat_stk;
@@ -2441,19 +2597,34 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
#define MATCH_CACHE_DEBUG_HIT ((void) 0)
#endif
+#define MATCH_CACHE_HIT ((void) 0)
+
# define CHECK_MATCH_CACHE do {\
if (msa->match_cache_status == MATCH_CACHE_STATUS_ENABLED) {\
- long cache_point = find_cache_point(reg, msa->cache_opcodes, msa->num_cache_opcodes, pbegin, stk_base, repeat_stk);\
+ const OnigCacheOpcode *cache_opcode;\
+ long cache_point = find_cache_point(reg, msa->cache_opcodes, msa->num_cache_opcodes, pbegin, stk_base, repeat_stk, &cache_opcode);\
if (cache_point >= 0) {\
long match_cache_point = msa->num_cache_points * (long)(s - str) + cache_point;\
long match_cache_point_index = match_cache_point >> 3;\
uint8_t match_cache_point_mask = 1 << (match_cache_point & 7);\
MATCH_CACHE_DEBUG;\
if (msa->match_cache_buf[match_cache_point_index] & match_cache_point_mask) {\
- MATCH_CACHE_DEBUG_HIT;\
- goto fail;\
+ MATCH_CACHE_DEBUG_HIT; MATCH_CACHE_HIT;\
+ if (cache_opcode->lookaround_nesting == 0) goto fail;\
+ else if (cache_opcode->lookaround_nesting < 0) {\
+ if (check_extended_match_cache_point(msa->match_cache_buf, match_cache_point_index, match_cache_point_mask)) {\
+ STACK_STOP_BT_FAIL;\
+ goto fail;\
+ } else goto fail;\
+ } else {\
+ if (check_extended_match_cache_point(msa->match_cache_buf, match_cache_point_index, match_cache_point_mask)) {\
+ p = cache_opcode->match_addr;\
+ MOP_OUT;\
+ JUMP;\
+ } else goto fail;\
+ }\
}\
- msa->match_cache_buf[match_cache_point_index] |= match_cache_point_mask;\
+ STACK_PUSH_MATCH_CACHE_POINT(match_cache_point_index, match_cache_point_mask);\
}\
}\
} while (0)
@@ -2765,7 +2936,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
int mb_len;
DATA_ENSURE(1);
- mb_len = enclen(encode, s, end);
+ mb_len = enclen_approx(encode, s, end);
DATA_ENSURE(mb_len);
ss = s;
s += mb_len;
@@ -2870,7 +3041,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_ANYCHAR) MOP_IN(OP_ANYCHAR);
DATA_ENSURE(1);
- n = enclen(encode, s, end);
+ n = enclen_approx(encode, s, end);
DATA_ENSURE(n);
if (ONIGENC_IS_MBC_NEWLINE_EX(encode, s, str, end, option, 0)) goto fail;
s += n;
@@ -2879,7 +3050,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_ANYCHAR_ML) MOP_IN(OP_ANYCHAR_ML);
DATA_ENSURE(1);
- n = enclen(encode, s, end);
+ n = enclen_approx(encode, s, end);
DATA_ENSURE(n);
s += n;
MOP_OUT;
@@ -2889,7 +3060,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
while (DATA_ENSURE_CHECK1) {
CHECK_MATCH_CACHE;
STACK_PUSH_ALT(p, s, sprev, pkeep);
- n = enclen(encode, s, end);
+ n = enclen_approx(encode, s, end);
DATA_ENSURE(n);
if (ONIGENC_IS_MBC_NEWLINE_EX(encode, s, str, end, option, 0)) goto fail;
sprev = s;
@@ -2902,7 +3073,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
while (DATA_ENSURE_CHECK1) {
CHECK_MATCH_CACHE;
STACK_PUSH_ALT(p, s, sprev, pkeep);
- n = enclen(encode, s, end);
+ n = enclen_approx(encode, s, end);
if (n > 1) {
DATA_ENSURE(n);
sprev = s;
@@ -2928,7 +3099,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
msa->num_fails++;
#endif
}
- n = enclen(encode, s, end);
+ n = enclen_approx(encode, s, end);
DATA_ENSURE(n);
if (ONIGENC_IS_MBC_NEWLINE_EX(encode, s, str, end, option, 0)) goto fail;
sprev = s;
@@ -2950,7 +3121,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
msa->num_fails++;
#endif
}
- n = enclen(encode, s, end);
+ n = enclen_approx(encode, s, end);
if (n > 1) {
DATA_ENSURE(n);
sprev = s;
@@ -2973,7 +3144,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
if (scv) goto fail;
STACK_PUSH_ALT_WITH_STATE_CHECK(p, s, sprev, mem, pkeep);
- n = enclen(encode, s, end);
+ n = enclen_approx(encode, s, end);
DATA_ENSURE(n);
if (ONIGENC_IS_MBC_NEWLINE_EX(encode, s, str, end, option, 0)) goto fail;
sprev = s;
@@ -2991,7 +3162,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
if (scv) goto fail;
STACK_PUSH_ALT_WITH_STATE_CHECK(p, s, sprev, mem, pkeep);
- n = enclen(encode, s, end);
+ n = enclen_approx(encode, s, end);
if (n > 1) {
DATA_ENSURE(n);
sprev = s;
@@ -3278,8 +3449,8 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_MEMORY_END_PUSH_REC) MOP_IN(OP_MEMORY_END_PUSH_REC);
GET_MEMNUM_INC(mem, p);
STACK_GET_MEM_START(mem, stkp); /* should be before push mem-end. */
- STACK_PUSH_MEM_END(mem, s);
mem_start_stk[mem] = GET_STACK_INDEX(stkp);
+ STACK_PUSH_MEM_END(mem, s);
MOP_OUT;
JUMP;
@@ -3333,7 +3504,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
DATA_ENSURE(n);
sprev = s;
STRING_CMP(pstart, s, n);
- while (sprev + (len = enclen(encode, sprev, end)) < s)
+ while (sprev + (len = enclen_approx(encode, sprev, end)) < s)
sprev += len;
MOP_OUT;
@@ -3363,8 +3534,8 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
n = pend - pstart;
DATA_ENSURE(n);
sprev = s;
- STRING_CMP_IC(case_fold_flag, pstart, &s, (int)n, end);
- while (sprev + (len = enclen(encode, sprev, end)) < s)
+ STRING_CMP_IC(case_fold_flag, pstart, &s, n, end);
+ while (sprev + (len = enclen_approx(encode, sprev, end)) < s)
sprev += len;
MOP_OUT;
@@ -3399,7 +3570,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
STRING_CMP_VALUE(pstart, swork, n, is_fail);
if (is_fail) continue;
s = swork;
- while (sprev + (len = enclen(encode, sprev, end)) < s)
+ while (sprev + (len = enclen_approx(encode, sprev, end)) < s)
sprev += len;
p += (SIZE_MEMNUM * (tlen - i - 1));
@@ -3534,10 +3705,9 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_NULL_CHECK_END_MEMST) MOP_IN(OP_NULL_CHECK_END_MEMST);
{
int isnull;
- int ischanged = 0; // set 1 when a loop is empty but memory status is changed.
GET_MEMNUM_INC(mem, p); /* mem: null check id */
- STACK_NULL_CHECK_MEMST(isnull, ischanged, mem, s, reg);
+ STACK_NULL_CHECK_MEMST(isnull, mem, s, reg);
if (isnull) {
# ifdef ONIG_DEBUG_MATCH
fprintf(stderr, "NULL_CHECK_END_MEMST: skip id:%d, s:%"PRIuPTR" (%p)\n",
@@ -3546,31 +3716,6 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
if (isnull == -1) goto fail;
goto null_check_found;
}
-# ifdef USE_MATCH_CACHE
- if (ischanged && msa->match_cache_status == MATCH_CACHE_STATUS_ENABLED) {
- RelAddrType rel;
- OnigUChar *null_check_start;
- OnigUChar *null_check_end = pbegin;
- MemNumType mem;
- UChar* tmp = p;
- switch (*tmp++) {
- case OP_JUMP:
- case OP_PUSH:
- GET_RELADDR_INC(rel, tmp);
- null_check_start = tmp + rel;
- break;
- case OP_REPEAT_INC:
- case OP_REPEAT_INC_NG:
- GET_MEMNUM_INC(mem, tmp);
- // Skip OP_REPEAT (or OP_REPEAT_NG)
- null_check_start = STACK_AT(repeat_stk[mem])->u.repeat.pcode;
- break;
- default:
- goto unexpected_bytecode_error;
- }
- reset_match_cache_on_null_check(reg, null_check_start, null_check_end, (long)(s - str), msa->match_cache_buf, msa->cache_opcodes, msa->num_cache_opcodes, msa->num_cache_points, stk_base, repeat_stk);
- }
-# endif
}
MOP_OUT;
JUMP;
@@ -3679,14 +3824,15 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_PUSH_IF_PEEK_NEXT) MOP_IN(OP_PUSH_IF_PEEK_NEXT);
GET_RELADDR_INC(addr, p);
+ CHECK_MATCH_CACHE;
if (*p == *s) {
p++;
- CHECK_MATCH_CACHE;
STACK_PUSH_ALT(p + addr, s, sprev, pkeep);
MOP_OUT;
JUMP;
}
p++;
+ INC_NUM_FAILS;
MOP_OUT;
JUMP;
@@ -3736,9 +3882,15 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
/* end of repeat. Nothing to do. */
}
else if (stkp->u.repeat.count >= reg->repeat_range[mem].lower) {
+#ifdef USE_MATCH_CACHE
if (*pbegin == OP_REPEAT_INC) {
+#undef MATCH_CACHE_HIT
+#define MATCH_CACHE_HIT stkp->u.repeat.count--;
CHECK_MATCH_CACHE;
+#undef MATCH_CACHE_HIT
+#define MATCH_CACHE_HIT ((void) 0)
}
+#endif
STACK_PUSH_ALT(p, s, sprev, pkeep);
p = STACK_AT(si)->u.repeat.pcode; /* Don't use stkp after PUSH. */
}
@@ -3998,7 +4150,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
fprintf(stderr, "MATCH CACHE: #cache points = %ld\n", msa->num_cache_points);
fprintf(stderr, "MATCH CACHE: cache opcodes (%p):\n", msa->cache_opcodes);
for (int i = 0; i < msa->num_cache_opcodes; i++) {
- fprintf(stderr, "MATCH CACHE: [%p] cache_point=%ld outer_repeat_mem=%d num_cache_opcodes_at_outer_repeat=%ld num_cache_opcodes_in_outer_repeat=%ld\n", msa->cache_opcodes[i].addr, msa->cache_opcodes[i].cache_point, msa->cache_opcodes[i].outer_repeat_mem, msa->cache_opcodes[i].num_cache_points_at_outer_repeat, msa->cache_opcodes[i].num_cache_points_in_outer_repeat);
+ fprintf(stderr, "MATCH CACHE: [%p] cache_point=%ld outer_repeat_mem=%d num_cache_opcodes_at_outer_repeat=%ld num_cache_opcodes_in_outer_repeat=%ld lookaround_nesting=%d match_addr=%p\n", msa->cache_opcodes[i].addr, msa->cache_opcodes[i].cache_point, msa->cache_opcodes[i].outer_repeat_mem, msa->cache_opcodes[i].num_cache_points_at_outer_repeat, msa->cache_opcodes[i].num_cache_points_in_outer_repeat, msa->cache_opcodes[i].lookaround_nesting, msa->cache_opcodes[i].match_addr);
}
#endif
}
@@ -4015,7 +4167,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
if (num_match_cache_points >= LONG_MAX_LIMIT) {
return ONIGERR_MEMORY;
}
- size_t match_cache_buf_length = (num_match_cache_points >> 3) + (num_match_cache_points & 7 ? 1 : 0);
+ size_t match_cache_buf_length = (num_match_cache_points >> 3) + (num_match_cache_points & 7 ? 1 : 0) + 1;
uint8_t* match_cache_buf = (uint8_t*)xmalloc(match_cache_buf_length * sizeof(uint8_t));
if (match_cache_buf == NULL) {
return ONIGERR_MEMORY;
@@ -4044,25 +4196,31 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
finish:
STACK_SAVE;
- if (xmalloc_base) xfree(xmalloc_base);
+ xfree(xmalloc_base);
return best_len;
#ifdef ONIG_DEBUG
stack_error:
STACK_SAVE;
- if (xmalloc_base) xfree(xmalloc_base);
+ xfree(xmalloc_base);
return ONIGERR_STACK_BUG;
#endif
bytecode_error:
STACK_SAVE;
- if (xmalloc_base) xfree(xmalloc_base);
+ xfree(xmalloc_base);
return ONIGERR_UNDEFINED_BYTECODE;
unexpected_bytecode_error:
STACK_SAVE;
- if (xmalloc_base) xfree(xmalloc_base);
+ xfree(xmalloc_base);
return ONIGERR_UNEXPECTED_BYTECODE;
+
+ timeout:
+ xfree(xmalloc_base);
+ if (stk_base != stk_alloc || IS_NOT_NULL(msa->stack_p))
+ xfree(stk_base);
+ HANDLE_REG_TIMEOUT_IN_MATCH_AT;
}
@@ -4763,12 +4921,17 @@ forward_search_range(regex_t* reg, const UChar* str, const UChar* end, UChar* s,
UChar* range, UChar** low, UChar** high, UChar** low_prev)
{
UChar *p, *pprev = (UChar* )NULL;
+ size_t input_len = end - str;
#ifdef ONIG_DEBUG_SEARCH
fprintf(stderr, "forward_search_range: str: %"PRIuPTR" (%p), end: %"PRIuPTR" (%p), s: %"PRIuPTR" (%p), range: %"PRIuPTR" (%p)\n",
(uintptr_t )str, str, (uintptr_t )end, end, (uintptr_t )s, s, (uintptr_t )range, range);
#endif
+ if (reg->dmin > input_len) {
+ return 0;
+ }
+
p = s;
if (reg->dmin > 0) {
if (ONIGENC_IS_SINGLEBYTE(reg->enc)) {
@@ -4905,6 +5068,11 @@ backward_search_range(regex_t* reg, const UChar* str, const UChar* end,
UChar** low, UChar** high)
{
UChar *p;
+ size_t input_len = end - str;
+
+ if (reg->dmin > input_len) {
+ return 0;
+ }
range += reg->dmin;
p = s;
diff --git a/regint.h b/regint.h
index 650d615c64..57cbb81654 100644
--- a/regint.h
+++ b/regint.h
@@ -154,13 +154,18 @@
#ifdef RUBY
# define CHECK_INTERRUPT_IN_MATCH_AT do { \
- msa->counter++; \
- if (msa->counter >= 128) { \
- msa->counter = 0; \
- rb_reg_check_timeout(reg, &msa->end_time); \
- rb_thread_check_ints(); \
- } \
+ msa->counter++; \
+ if (msa->counter >= 128) { \
+ msa->counter = 0; \
+ if (rb_reg_timeout_p(reg, &msa->end_time)) { \
+ goto timeout; \
+ } \
+ rb_thread_check_ints(); \
+ } \
} while(0)
+# define HANDLE_REG_TIMEOUT_IN_MATCH_AT do { \
+ rb_reg_raise_timeout(); \
+} while (0)
# define onig_st_init_table st_init_table
# define onig_st_init_table_with_size st_init_table_with_size
# define onig_st_init_numtable st_init_numtable
@@ -873,6 +878,12 @@ typedef struct _OnigStackType {
UChar *abs_pstr; /* absent start position */
const UChar *end_pstr; /* end position */
} absent_pos;
+#ifdef USE_MATCH_CACHE
+ struct {
+ long index; /* index of the match cache buffer */
+ uint8_t mask; /* bit-mask for the match cache buffer */
+ } match_cache_point;
+#endif
} u;
} OnigStackType;
@@ -883,6 +894,8 @@ typedef struct {
int outer_repeat_mem;
long num_cache_points_at_outer_repeat;
long num_cache_points_in_outer_repeat;
+ int lookaround_nesting;
+ UChar *match_addr;
} OnigCacheOpcode;
#endif
@@ -988,7 +1001,8 @@ extern int onig_st_insert_strend(hash_table_type* table, const UChar* str_key, c
#ifdef RUBY
extern size_t onig_memsize(const regex_t *reg);
extern size_t onig_region_memsize(const struct re_registers *regs);
-void rb_reg_check_timeout(regex_t *reg, void *end_time);
+bool rb_reg_timeout_p(regex_t *reg, void *end_time);
+NORETURN(void rb_reg_raise_timeout(void));
#endif
RUBY_SYMBOL_EXPORT_END
diff --git a/regparse.c b/regparse.c
index 4ebd5f1c46..33815f2445 100644
--- a/regparse.c
+++ b/regparse.c
@@ -142,7 +142,7 @@ static void
bbuf_free(BBuf* bbuf)
{
if (IS_NOT_NULL(bbuf)) {
- if (IS_NOT_NULL(bbuf->p)) xfree(bbuf->p);
+ xfree(bbuf->p);
xfree(bbuf);
}
}
@@ -251,7 +251,7 @@ bitset_copy(BitSetRef dest, BitSetRef bs)
#if defined(USE_NAMED_GROUP) && !defined(USE_ST_LIBRARY)
extern int
-onig_strncmp(const UChar* s1, const UChar* s2, int n)
+onig_strncmp(const UChar* s1, const UChar* s2, size_t n)
{
int x;
@@ -275,7 +275,7 @@ onig_strcpy(UChar* dest, const UChar* src, const UChar* end)
#ifdef USE_NAMED_GROUP
static UChar*
-strdup_with_null(OnigEncoding enc, UChar* s, UChar* end)
+strdup_with_null(OnigEncoding enc, const UChar* s, const UChar* end)
{
ptrdiff_t slen;
int term_len, i;
@@ -476,8 +476,10 @@ typedef st_data_t HashDataType; /* 1.6 st.h doesn't define st_data_t type */
# ifdef ONIG_DEBUG
static int
-i_print_name_entry(UChar* key, NameEntry* e, void* arg)
+i_print_name_entry(HashDataType key_, HashDataType e_, HashDataType arg_)
{
+ NameEntry* e = (NameEntry *)e_;
+ void* arg = (void *)arg_;
int i;
FILE* fp = (FILE* )arg;
@@ -503,7 +505,7 @@ onig_print_names(FILE* fp, regex_t* reg)
if (IS_NOT_NULL(t)) {
fprintf(fp, "name table\n");
- onig_st_foreach(t, (st_foreach_callback_func *)i_print_name_entry, (HashDataType )fp);
+ onig_st_foreach(t, i_print_name_entry, (HashDataType )fp);
fputs("\n", fp);
}
return 0;
@@ -511,10 +513,12 @@ onig_print_names(FILE* fp, regex_t* reg)
# endif /* ONIG_DEBUG */
static int
-i_free_name_entry(UChar* key, NameEntry* e, void* arg ARG_UNUSED)
+i_free_name_entry(HashDataType key_, HashDataType e_, HashDataType arg_ ARG_UNUSED)
{
+ UChar* key = (UChar *)key_;
+ NameEntry* e = (NameEntry *)e_;
xfree(e->name);
- if (IS_NOT_NULL(e->back_refs)) xfree(e->back_refs);
+ xfree(e->back_refs);
xfree(key);
xfree(e);
return ST_DELETE;
@@ -526,7 +530,7 @@ names_clear(regex_t* reg)
NameTable* t = (NameTable* )reg->name_table;
if (IS_NOT_NULL(t)) {
- onig_st_foreach(t, (st_foreach_callback_func *)i_free_name_entry, 0);
+ onig_st_foreach(t, i_free_name_entry, 0);
}
return 0;
}
@@ -546,6 +550,58 @@ onig_names_free(regex_t* reg)
return 0;
}
+static int
+copy_named_captures_iter(const OnigUChar *name, const OnigUChar *name_end,
+ int back_num, int *back_refs, OnigRegex regex, void *arg)
+{
+ NameTable *copy_table = (NameTable *)arg;
+ NameEntry *entry_copy = (NameEntry* )xmalloc(sizeof(NameEntry));
+ if (IS_NULL(entry_copy)) return -1;
+
+ entry_copy->name_len = name_end - name;
+ entry_copy->back_num = back_num;
+ entry_copy->back_alloc = back_num;
+ entry_copy->back_ref1 = back_refs[0];
+ entry_copy->back_refs = xmalloc(back_num * (sizeof(int*)));
+ if (IS_NULL(entry_copy->back_refs)) {
+ xfree(entry_copy);
+ return -1;
+ }
+ memcpy(entry_copy->back_refs, back_refs, back_num * sizeof(back_refs[0]));
+
+ UChar *new_name = strdup_with_null(regex->enc, name, name_end);
+ if (IS_NULL(new_name)) {
+ xfree(entry_copy->back_refs);
+ xfree(entry_copy);
+ return -1;
+ }
+ entry_copy->name = new_name;
+
+ if (onig_st_insert_strend(copy_table, new_name, (new_name + entry_copy->name_len), (hash_data_type)entry_copy)) {
+ xfree(entry_copy->name);
+ xfree(entry_copy->back_refs);
+ xfree(entry_copy);
+ return -1;
+ }
+ return 0;
+}
+
+extern int
+onig_names_copy(regex_t* reg, regex_t* oreg)
+{
+ NameTable* table = oreg->name_table;
+ if (table) {
+ NameTable * t = onig_st_init_strend_table_with_size(onig_number_of_names(oreg));
+ CHECK_NULL_RETURN_MEMERR(t);
+ if (onig_foreach_name(oreg, copy_named_captures_iter, (void*)t)) {
+ onig_st_free_table(t);
+ return ONIGERR_MEMORY;
+ }
+ reg->name_table = t;
+ }
+ return 0;
+}
+
static NameEntry*
name_find(regex_t* reg, const UChar* name, const UChar* name_end)
{
@@ -568,8 +624,10 @@ typedef struct {
} INamesArg;
static int
-i_names(UChar* key ARG_UNUSED, NameEntry* e, INamesArg* arg)
+i_names(HashDataType key_ ARG_UNUSED, HashDataType e_, HashDataType arg_)
{
+ NameEntry* e = (NameEntry *)e_;
+ INamesArg* arg = (INamesArg *)arg_;
int r = (*(arg->func))(e->name,
e->name + e->name_len,
e->back_num,
@@ -595,14 +653,16 @@ onig_foreach_name(regex_t* reg,
narg.reg = reg;
narg.arg = arg;
narg.enc = reg->enc; /* should be pattern encoding. */
- onig_st_foreach(t, (st_foreach_callback_func *)i_names, (HashDataType )&narg);
+ onig_st_foreach(t, i_names, (HashDataType )&narg);
}
return narg.ret;
}
static int
-i_renumber_name(UChar* key ARG_UNUSED, NameEntry* e, GroupNumRemap* map)
+i_renumber_name(HashDataType key_ ARG_UNUSED, HashDataType e_, HashDataType map_)
{
+ NameEntry* e = (NameEntry *)e_;
+ GroupNumRemap* map = (GroupNumRemap *)map_;
int i;
if (e->back_num > 1) {
@@ -623,7 +683,7 @@ onig_renumber_name_table(regex_t* reg, GroupNumRemap* map)
NameTable* t = (NameTable* )reg->name_table;
if (IS_NOT_NULL(t)) {
- onig_st_foreach(t, (st_foreach_callback_func *)i_renumber_name, (HashDataType )map);
+ onig_st_foreach(t, i_renumber_name, (HashDataType )map);
}
return 0;
}
@@ -699,14 +759,13 @@ names_clear(regex_t* reg)
e->name_len = 0;
e->back_num = 0;
e->back_alloc = 0;
- if (IS_NOT_NULL(e->back_refs)) xfree(e->back_refs);
+ xfree(e->back_refs);
e->back_refs = (int* )NULL;
}
}
- if (IS_NOT_NULL(t->e)) {
- xfree(t->e);
- t->e = NULL;
- }
+
+ xfree(t->e);
+ t->e = NULL;
t->num = 0;
}
return 0;
@@ -722,15 +781,57 @@ onig_names_free(regex_t* reg)
if (r) return r;
t = (NameTable* )reg->name_table;
- if (IS_NOT_NULL(t)) xfree(t);
+ xfree(t);
reg->name_table = NULL;
return 0;
}
+extern int
+onig_names_copy(regex_t* reg, regex_t* oreg)
+{
+ NameTable* ot = oreg->name_table;
+ if (ot) {
+ OnigEncoding enc = oreg->enc;
+ int i, num = ot->num;
+ NameTable* t = xmalloc(sizeof(*t));
+ CHECK_NULL_RETURN_MEMERR(t);
+ *t = *ot;
+ t->e = xmalloc(t->alloc * sizeof(t->e[0]));
+ if (IS_NULL(t->e)) {
+ xfree(t);
+ return ONIGERR_MEMORY;
+ }
+ t->num = 0;
+ reg->name_table = t;
+ for (i = 0; i < num; t->num = ++i) {
+ NameEntry* oe = &(ot->e[i]);
+ NameEntry* e = &(t->e[i]);
+ *e = *oe;
+ e->name = NULL;
+ e->back_refs = NULL;
+ e->name = strdup_with_null(enc, oe->name, oe->name + e->name_len);
+ if (IS_NULL(e->name)) {
+ onig_names_free(reg);
+ return ONIGERR_MEMORY;
+ }
+ e->back_refs = xmalloc(e->back_alloc * sizeof(e->back_refs[0]));
+ if (IS_NULL(e->back_refs)) {
+ xfree(e->name);
+ onig_names_free(reg);
+ return ONIGERR_MEMORY;
+ }
+ memcpy(e->back_refs, oe->back_refs, e->back_num * sizeof(e->back_refs[0]));
+ e->back_ref1 = e->back_refs[0];
+ }
+ }
+ return 0;
+}
+
static NameEntry*
name_find(regex_t* reg, const UChar* name, const UChar* name_end)
{
- int i, len;
+ int i;
+ size_t len;
NameEntry* e;
NameTable* t = (NameTable* )reg->name_table;
@@ -766,6 +867,30 @@ onig_foreach_name(regex_t* reg,
}
extern int
+onig_renumber_name_table(regex_t* reg, GroupNumRemap* map)
+{
+ int i, j;
+ NameEntry* e;
+ NameTable* t = (NameTable* )reg->name_table;
+
+ if (IS_NOT_NULL(t)) {
+ for (i = 0; i < t->num; i++) {
+ e = &(t->e[i]);
+
+ if (e->back_num > 1) {
+ for (j = 0; j < e->back_num; j++) {
+ e->back_refs[j] = map[e->back_refs[j]].new_val;
+ }
+ }
+ else if (e->back_num == 1) {
+ e->back_ref1 = map[e->back_ref1].new_val;
+ }
+ }
+ }
+ return 0;
+}
+
+extern int
onig_number_of_names(const regex_t* reg)
{
NameTable* t = (NameTable* )reg->name_table;
@@ -967,6 +1092,12 @@ onig_number_of_names(const regex_t* reg)
{
return 0;
}
+
+extern int
+onig_names_copy(regex_t* reg, regex_t* oreg)
+{
+ return 0;
+}
#endif /* else USE_NAMED_GROUP */
extern int
@@ -1098,29 +1229,24 @@ onig_node_free(Node* node)
{
CClassNode* cc = NCCLASS(node);
- if (cc->mbuf)
- bbuf_free(cc->mbuf);
+ bbuf_free(cc->mbuf);
}
break;
case NT_QTFR:
- if (NQTFR(node)->target)
- onig_node_free(NQTFR(node)->target);
+ onig_node_free(NQTFR(node)->target);
break;
case NT_ENCLOSE:
- if (NENCLOSE(node)->target)
- onig_node_free(NENCLOSE(node)->target);
+ onig_node_free(NENCLOSE(node)->target);
break;
case NT_BREF:
- if (IS_NOT_NULL(NBREF(node)->back_dynamic))
- xfree(NBREF(node)->back_dynamic);
+ xfree(NBREF(node)->back_dynamic);
break;
case NT_ANCHOR:
- if (NANCHOR(node)->target)
- onig_node_free(NANCHOR(node)->target);
+ onig_node_free(NANCHOR(node)->target);
break;
}
@@ -5977,7 +6103,8 @@ node_extended_grapheme_cluster(Node** np, ScanEnv* env)
R_ERR(add_code_range(&(cc->mbuf), env, 0x000A, 0x000A)); /* CR */
R_ERR(add_code_range(&(cc->mbuf), env, 0x000D, 0x000D)); /* LF */
R_ERR(not_code_range_buf(env->enc, cc->mbuf, &inverted_buf, env));
- cc->mbuf = inverted_buf; /* TODO: check what to do with buffer before inversion */
+ bbuf_free(cc->mbuf);
+ cc->mbuf = inverted_buf;
env->warnings_flag &= dup_not_warned; /* TODO: fix false warning */
}
diff --git a/regparse.h b/regparse.h
index acdd3e2f5c..de980d0ac8 100644
--- a/regparse.h
+++ b/regparse.h
@@ -339,7 +339,7 @@ typedef struct {
extern int onig_renumber_name_table(regex_t* reg, GroupNumRemap* map);
#endif
-extern int onig_strncmp(const UChar* s1, const UChar* s2, int n);
+extern int onig_strncmp(const UChar* s1, const UChar* s2, size_t n);
extern void onig_strcpy(UChar* dest, const UChar* src, const UChar* end);
extern void onig_scan_env_set_error_string(ScanEnv* env, int ecode, UChar* arg, UChar* arg_end);
extern int onig_scan_unsigned_number(UChar** src, const UChar* end, OnigEncoding enc);
@@ -356,6 +356,7 @@ extern Node* onig_node_list_add(Node* list, Node* x);
extern Node* onig_node_new_alt(Node* left, Node* right);
extern void onig_node_str_clear(Node* node);
extern int onig_names_free(regex_t* reg);
+extern int onig_names_copy(regex_t* reg, regex_t* oreg);
extern int onig_parse_make_tree(Node** root, const UChar* pattern, const UChar* end, regex_t* reg, ScanEnv* env);
extern int onig_free_shared_cclass_table(void);
diff --git a/rjit.c b/rjit.c
index 4ce6af4d4b..72660394b3 100644
--- a/rjit.c
+++ b/rjit.c
@@ -101,6 +101,9 @@ VALUE rb_rjit_raw_samples = 0;
// Line numbers for --rjit-trace-exits
VALUE rb_rjit_line_samples = 0;
+// Postponed job handle for triggering rjit_iseq_update_references
+static rb_postponed_job_handle_t rjit_iseq_update_references_pjob;
+
// A default threshold used to add iseq to JIT.
#define DEFAULT_CALL_THRESHOLD 10
// Size of executable memory block in MiB.
@@ -118,15 +121,21 @@ rb_rjit_setup_options(const char *s, struct rb_rjit_options *rjit_opt)
if (l == 0) {
return;
}
- else if (opt_match_arg(s, l, "call-threshold")) {
- rjit_opt->call_threshold = atoi(s + 1);
- }
else if (opt_match_arg(s, l, "exec-mem-size")) {
rjit_opt->exec_mem_size = atoi(s + 1);
}
+ else if (opt_match_arg(s, l, "call-threshold")) {
+ rjit_opt->call_threshold = atoi(s + 1);
+ }
else if (opt_match_noarg(s, l, "stats")) {
rjit_opt->stats = true;
}
+ else if (opt_match_noarg(s, l, "disable")) {
+ rjit_opt->disable = true;
+ }
+ else if (opt_match_noarg(s, l, "trace")) {
+ rjit_opt->trace = true;
+ }
else if (opt_match_noarg(s, l, "trace-exits")) {
rjit_opt->trace_exits = true;
}
@@ -136,24 +145,20 @@ rb_rjit_setup_options(const char *s, struct rb_rjit_options *rjit_opt)
else if (opt_match_noarg(s, l, "verify-ctx")) {
rjit_opt->verify_ctx = true;
}
- // --rjit=pause is an undocumented feature for experiments
- else if (opt_match_noarg(s, l, "pause")) {
- rjit_opt->pause = true;
- }
else {
rb_raise(rb_eRuntimeError,
- "invalid RJIT option `%s' (--help will show valid RJIT options)", s);
+ "invalid RJIT option '%s' (--help will show valid RJIT options)", s);
}
}
#define M(shortopt, longopt, desc) RUBY_OPT_MESSAGE(shortopt, longopt, desc)
const struct ruby_opt_message rb_rjit_option_messages[] = {
- M("--rjit-stats", "", "Enable collecting RJIT statistics"),
-#if RJIT_STATS
- M("--rjit-trace-exits", "", "Trace side exit locations"),
-#endif
- M("--rjit-exec-mem-size=num", "", "Size of executable memory block in MiB (default: " STRINGIZE(DEFAULT_EXEC_MEM_SIZE) ")"),
- M("--rjit-call-threshold=num", "", "Number of calls to trigger JIT (default: " STRINGIZE(DEFAULT_CALL_THRESHOLD) ")"),
+ M("--rjit-exec-mem-size=num", "", "Size of executable memory block in MiB (default: " STRINGIZE(DEFAULT_EXEC_MEM_SIZE) ")."),
+ M("--rjit-call-threshold=num", "", "Number of calls to trigger JIT (default: " STRINGIZE(DEFAULT_CALL_THRESHOLD) ")."),
+ M("--rjit-stats", "", "Enable collecting RJIT statistics."),
+ M("--rjit-disable", "", "Disable RJIT for lazily enabling it with RubyVM::RJIT.enable."),
+ M("--rjit-trace", "", "Allow TracePoint during JIT compilation."),
+ M("--rjit-trace-exits", "", "Trace side exit locations."),
#ifdef HAVE_LIBCAPSTONE
M("--rjit-dump-disasm", "", "Dump all JIT code"),
#endif
@@ -163,32 +168,47 @@ const struct ruby_opt_message rb_rjit_option_messages[] = {
struct rb_rjit_runtime_counters rb_rjit_counters = { 0 };
-#if RJIT_STATS
-void
-rb_rjit_collect_vm_usage_insn(int insn)
-{
- if (!rjit_stats_p) return;
- rb_rjit_counters.vm_insns_count++;
-}
-#endif // YJIT_STATS
-
extern VALUE rb_gc_enable(void);
extern VALUE rb_gc_disable(void);
+extern uint64_t rb_vm_insns_count;
-#define WITH_RJIT_ISOLATED(stmt) do { \
+// Disable GC, TracePoint, JIT, stats, and $!
+#define WITH_RJIT_ISOLATED_USING_PC(using_pc, stmt) do { \
VALUE was_disabled = rb_gc_disable(); \
+ \
rb_hook_list_t *global_hooks = rb_ec_ractor_hooks(GET_EC()); \
rb_rjit_global_events = global_hooks->events; \
- global_hooks->events = 0; \
+ \
+ const VALUE *pc = NULL; \
+ if (rb_rjit_opts.trace) { \
+ pc = GET_EC()->cfp->pc; \
+ if (!using_pc) GET_EC()->cfp->pc = 0; /* avoid crashing on calc_lineno */ \
+ } \
+ else global_hooks->events = 0; \
+ \
bool original_call_p = rb_rjit_call_p; \
- rjit_stats_p = false; \
rb_rjit_call_p = false; \
+ \
+ rjit_stats_p = false; \
+ uint64_t insns_count = rb_vm_insns_count; \
+ \
+ VALUE err = rb_errinfo(); \
+ \
stmt; \
- rb_rjit_call_p = (rjit_cancel_p ? false : original_call_p); \
+ \
+ rb_set_errinfo(err); \
+ \
+ rb_vm_insns_count = insns_count; \
rjit_stats_p = rb_rjit_opts.stats; \
- global_hooks->events = rb_rjit_global_events; \
+ \
+ rb_rjit_call_p = (rjit_cancel_p ? false : original_call_p); \
+ \
+ if (rb_rjit_opts.trace) GET_EC()->cfp->pc = pc; \
+ else global_hooks->events = rb_rjit_global_events; \
+ \
if (!was_disabled) rb_gc_enable(); \
} while (0);
+#define WITH_RJIT_ISOLATED(stmt) WITH_RJIT_ISOLATED_USING_PC(false, stmt)
void
rb_rjit_cancel_all(const char *reason)
@@ -301,7 +321,7 @@ rb_rjit_iseq_update_references(struct rb_iseq_constant_body *const body)
// Asynchronously hook the Ruby code to avoid allocation during GC.compact.
// Using _one because it's too slow to invalidate all for each ISEQ. Thus
// not giving an ISEQ pointer.
- rb_postponed_job_register_one(0, rjit_iseq_update_references, NULL);
+ rb_postponed_job_trigger(rjit_iseq_update_references_pjob);
}
void
@@ -354,7 +374,7 @@ rb_rjit_compile(const rb_iseq_t *iseq)
RB_VM_LOCK_ENTER();
rb_vm_barrier();
- WITH_RJIT_ISOLATED({
+ WITH_RJIT_ISOLATED_USING_PC(true, {
VALUE iseq_ptr = rb_funcall(rb_cRJITIseqPtr, rb_intern("new"), 1, SIZET2NUM((size_t)iseq));
VALUE cfp_ptr = rb_funcall(rb_cRJITCfpPtr, rb_intern("new"), 1, SIZET2NUM((size_t)GET_EC()->cfp));
rb_funcall(rb_RJITCompiler, rb_intern("compile"), 2, iseq_ptr, cfp_ptr);
@@ -373,7 +393,7 @@ rb_rjit_entry_stub_hit(VALUE branch_stub)
rb_control_frame_t *cfp = GET_EC()->cfp;
- WITH_RJIT_ISOLATED({
+ WITH_RJIT_ISOLATED_USING_PC(true, {
VALUE cfp_ptr = rb_funcall(rb_cRJITCfpPtr, rb_intern("new"), 1, SIZET2NUM((size_t)cfp));
result = rb_funcall(rb_RJITCompiler, rb_intern("entry_stub_hit"), 2, branch_stub, cfp_ptr);
});
@@ -429,6 +449,10 @@ rb_rjit_init(const struct rb_rjit_options *opts)
rb_rjit_enabled = false;
return;
}
+ rjit_iseq_update_references_pjob = rb_postponed_job_preregister(0, rjit_iseq_update_references, NULL);
+ if (rjit_iseq_update_references_pjob == POSTPONED_JOB_HANDLE_INVALID) {
+ rb_bug("Could not preregister postponed job for RJIT");
+ }
rb_mRJITC = rb_const_get(rb_mRJIT, rb_intern("C"));
VALUE rb_cRJITCompiler = rb_const_get(rb_mRJIT, rb_intern("Compiler"));
rb_RJITCompiler = rb_funcall(rb_cRJITCompiler, rb_intern("new"), 0);
@@ -441,7 +465,7 @@ rb_rjit_init(const struct rb_rjit_options *opts)
}
// Enable RJIT and stats from here
- rb_rjit_call_p = !rb_rjit_opts.pause;
+ rb_rjit_call_p = !rb_rjit_opts.disable;
rjit_stats_p = rb_rjit_opts.stats;
}
diff --git a/rjit.h b/rjit.h
index 1efd4f3ae7..26d1d67fb1 100644
--- a/rjit.h
+++ b/rjit.h
@@ -14,10 +14,6 @@
# if USE_RJIT
-#ifndef RJIT_STATS
-# define RJIT_STATS RUBY_DEBUG
-#endif
-
#include "ruby.h"
#include "vm_core.h"
@@ -26,20 +22,22 @@ struct rb_rjit_options {
// Converted from "rjit" feature flag to tell the enablement
// information to ruby_show_version().
bool on;
- // Number of calls to trigger JIT compilation.
- unsigned int call_threshold;
// Size of executable memory block in MiB
unsigned int exec_mem_size;
+ // Number of calls to trigger JIT compilation
+ unsigned int call_threshold;
// Collect RJIT statistics
bool stats;
+ // Do not start RJIT until RJIT.enable is called
+ bool disable;
+ // Allow TracePoint during JIT compilation
+ bool trace;
// Trace side exit locations
bool trace_exits;
// Enable disasm of all JIT code
bool dump_disasm;
// Verify context objects
bool verify_ctx;
- // [experimental] Do not start RJIT until RJIT.resume is called.
- bool pause;
};
RUBY_SYMBOL_EXPORT_BEGIN
diff --git a/rjit.rb b/rjit.rb
index ebad3529ef..12fc10c91e 100644
--- a/rjit.rb
+++ b/rjit.rb
@@ -1,11 +1,11 @@
module RubyVM::RJIT
- # Return true if RJIT is enabled.
+ # Return true if \RJIT is enabled.
def self.enabled?
Primitive.cexpr! 'RBOOL(rb_rjit_enabled)'
end
- # Start generating JITed code again after --rjit-pause.
- def self.resume
+ # Start JIT compilation after \--rjit-disable.
+ def self.enable
Primitive.cstmt! %{
rb_rjit_call_p = true;
return Qnil;
diff --git a/rjit_c.c b/rjit_c.c
index bdc9ce8072..e6d8d5da5c 100644
--- a/rjit_c.c
+++ b/rjit_c.c
@@ -509,7 +509,7 @@ rjit_for_each_iseq(rb_execution_context_t *ec, VALUE self, VALUE block)
return Qnil;
}
-// bindgen funcs
+// bindgen references
extern ID rb_get_symbol_id(VALUE name);
extern VALUE rb_fix_aref(VALUE fix, VALUE idx);
extern VALUE rb_str_getbyte(VALUE str, VALUE index);
@@ -535,6 +535,7 @@ extern VALUE rb_vm_set_ivar_id(VALUE obj, ID id, VALUE val);
extern VALUE rb_ary_unshift_m(int argc, VALUE *argv, VALUE ary);
extern void* rb_rjit_entry_stub_hit(VALUE branch_stub);
extern void* rb_rjit_branch_stub_hit(VALUE branch_stub, int sp_offset, int target0_p);
+extern uint64_t rb_vm_insns_count;
#include "rjit_c.rbinc"
diff --git a/rjit_c.h b/rjit_c.h
index 518d336c00..029f811637 100644
--- a/rjit_c.h
+++ b/rjit_c.h
@@ -17,7 +17,6 @@ extern uint8_t *rb_rjit_mem_block;
#define RJIT_RUNTIME_COUNTERS(...) struct rb_rjit_runtime_counters { size_t __VA_ARGS__; };
RJIT_RUNTIME_COUNTERS(
- vm_insns_count,
rjit_insns_count,
send_args_splat_kw_splat,
@@ -70,6 +69,7 @@ RJIT_RUNTIME_COUNTERS(
send_iseq_has_rest,
send_iseq_block_arg0_splat,
send_iseq_kw_call,
+ send_iseq_kw_splat,
send_iseq_splat,
send_iseq_has_rest_and_optional,
send_iseq_arity_error,
diff --git a/rjit_c.rb b/rjit_c.rb
index 9373507275..b9a5bb0b55 100644
--- a/rjit_c.rb
+++ b/rjit_c.rb
@@ -171,12 +171,6 @@ module RubyVM::RJIT # :nodoc: all
me_addr == 0 ? nil : rb_method_entry_t.new(me_addr)
end
- def rb_shape_transition_shape_capa(shape, new_capacity)
- _shape = shape.to_i
- shape_addr = Primitive.cexpr! 'SIZET2NUM((size_t)rb_shape_transition_shape_capa((rb_shape_t *)NUM2SIZET(_shape), NUM2UINT(new_capacity)))'
- rb_shape_t.new(shape_addr)
- end
-
def rb_shape_get_next(shape, obj, id)
_shape = shape.to_i
shape_addr = Primitive.cexpr! 'SIZET2NUM((size_t)rb_shape_get_next((rb_shape_t *)NUM2SIZET(_shape), obj, (ID)NUM2SIZET(id)))'
@@ -332,6 +326,10 @@ module RubyVM::RJIT # :nodoc: all
def RCLASS_ORIGIN(klass)
Primitive.cexpr! 'RCLASS_ORIGIN(klass)'
end
+
+ def RCLASS_SINGLETON_P(klass)
+ Primitive.cexpr! 'RCLASS_SINGLETON_P(klass)'
+ end
end
#
@@ -370,7 +368,6 @@ module RubyVM::RJIT # :nodoc: all
C::BOP_OR = Primitive.cexpr! %q{ SIZET2NUM(BOP_OR) }
C::BOP_PLUS = Primitive.cexpr! %q{ SIZET2NUM(BOP_PLUS) }
C::BUILTIN_ATTR_LEAF = Primitive.cexpr! %q{ SIZET2NUM(BUILTIN_ATTR_LEAF) }
- C::BUILTIN_ATTR_NO_GC = Primitive.cexpr! %q{ SIZET2NUM(BUILTIN_ATTR_NO_GC) }
C::HASH_REDEFINED_OP_FLAG = Primitive.cexpr! %q{ SIZET2NUM(HASH_REDEFINED_OP_FLAG) }
C::INTEGER_REDEFINED_OP_FLAG = Primitive.cexpr! %q{ SIZET2NUM(INTEGER_REDEFINED_OP_FLAG) }
C::INVALID_SHAPE_ID = Primitive.cexpr! %q{ SIZET2NUM(INVALID_SHAPE_ID) }
@@ -399,7 +396,6 @@ module RubyVM::RJIT # :nodoc: all
C::RUBY_FLONUM_FLAG = Primitive.cexpr! %q{ SIZET2NUM(RUBY_FLONUM_FLAG) }
C::RUBY_FLONUM_MASK = Primitive.cexpr! %q{ SIZET2NUM(RUBY_FLONUM_MASK) }
C::RUBY_FL_FREEZE = Primitive.cexpr! %q{ SIZET2NUM(RUBY_FL_FREEZE) }
- C::RUBY_FL_SINGLETON = Primitive.cexpr! %q{ SIZET2NUM(RUBY_FL_SINGLETON) }
C::RUBY_IMMEDIATE_MASK = Primitive.cexpr! %q{ SIZET2NUM(RUBY_IMMEDIATE_MASK) }
C::RUBY_SPECIAL_SHIFT = Primitive.cexpr! %q{ SIZET2NUM(RUBY_SPECIAL_SHIFT) }
C::RUBY_SYMBOL_FLAG = Primitive.cexpr! %q{ SIZET2NUM(RUBY_SYMBOL_FLAG) }
@@ -412,11 +408,9 @@ module RubyVM::RJIT # :nodoc: all
C::RUBY_T_OBJECT = Primitive.cexpr! %q{ SIZET2NUM(RUBY_T_OBJECT) }
C::RUBY_T_STRING = Primitive.cexpr! %q{ SIZET2NUM(RUBY_T_STRING) }
C::RUBY_T_SYMBOL = Primitive.cexpr! %q{ SIZET2NUM(RUBY_T_SYMBOL) }
- C::SHAPE_CAPACITY_CHANGE = Primitive.cexpr! %q{ SIZET2NUM(SHAPE_CAPACITY_CHANGE) }
C::SHAPE_FLAG_SHIFT = Primitive.cexpr! %q{ SIZET2NUM(SHAPE_FLAG_SHIFT) }
C::SHAPE_FROZEN = Primitive.cexpr! %q{ SIZET2NUM(SHAPE_FROZEN) }
C::SHAPE_ID_NUM_BITS = Primitive.cexpr! %q{ SIZET2NUM(SHAPE_ID_NUM_BITS) }
- C::SHAPE_INITIAL_CAPACITY = Primitive.cexpr! %q{ SIZET2NUM(SHAPE_INITIAL_CAPACITY) }
C::SHAPE_IVAR = Primitive.cexpr! %q{ SIZET2NUM(SHAPE_IVAR) }
C::SHAPE_MASK = Primitive.cexpr! %q{ SIZET2NUM(SHAPE_MASK) }
C::SHAPE_ROOT = Primitive.cexpr! %q{ SIZET2NUM(SHAPE_ROOT) }
@@ -460,65 +454,22 @@ module RubyVM::RJIT # :nodoc: all
C::VM_METHOD_TYPE_ZSUPER = Primitive.cexpr! %q{ SIZET2NUM(VM_METHOD_TYPE_ZSUPER) }
C::VM_SPECIAL_OBJECT_VMCORE = Primitive.cexpr! %q{ SIZET2NUM(VM_SPECIAL_OBJECT_VMCORE) }
- def C.block_type_iseq
- Primitive.cexpr! %q{ SIZET2NUM(block_type_iseq) }
- end
-
- def C.idRespond_to_missing
- Primitive.cexpr! %q{ SIZET2NUM(idRespond_to_missing) }
- end
-
- def C.imemo_callinfo
- Primitive.cexpr! %q{ SIZET2NUM(imemo_callinfo) }
- end
-
- def C.imemo_iseq
- Primitive.cexpr! %q{ SIZET2NUM(imemo_iseq) }
- end
-
- def C.rb_block_param_proxy
- Primitive.cexpr! %q{ SIZET2NUM(rb_block_param_proxy) }
- end
-
- def C.rb_cArray
- Primitive.cexpr! %q{ SIZET2NUM(rb_cArray) }
- end
-
- def C.rb_cFalseClass
- Primitive.cexpr! %q{ SIZET2NUM(rb_cFalseClass) }
- end
-
- def C.rb_cFloat
- Primitive.cexpr! %q{ SIZET2NUM(rb_cFloat) }
- end
-
- def C.rb_cInteger
- Primitive.cexpr! %q{ SIZET2NUM(rb_cInteger) }
- end
-
- def C.rb_cNilClass
- Primitive.cexpr! %q{ SIZET2NUM(rb_cNilClass) }
- end
-
- def C.rb_cString
- Primitive.cexpr! %q{ SIZET2NUM(rb_cString) }
- end
-
- def C.rb_cSymbol
- Primitive.cexpr! %q{ SIZET2NUM(rb_cSymbol) }
- end
-
- def C.rb_cTrueClass
- Primitive.cexpr! %q{ SIZET2NUM(rb_cTrueClass) }
- end
-
- def C.rb_mRubyVMFrozenCore
- Primitive.cexpr! %q{ SIZET2NUM(rb_mRubyVMFrozenCore) }
- end
-
- def C.rb_rjit_global_events
- Primitive.cexpr! %q{ SIZET2NUM(rb_rjit_global_events) }
- end
+ def C.block_type_iseq = Primitive.cexpr!(%q{ SIZET2NUM(block_type_iseq) })
+ def C.idRespond_to_missing = Primitive.cexpr!(%q{ SIZET2NUM(idRespond_to_missing) })
+ def C.imemo_callinfo = Primitive.cexpr!(%q{ SIZET2NUM(imemo_callinfo) })
+ def C.imemo_iseq = Primitive.cexpr!(%q{ SIZET2NUM(imemo_iseq) })
+ def C.rb_block_param_proxy = Primitive.cexpr!(%q{ SIZET2NUM(rb_block_param_proxy) })
+ def C.rb_cArray = Primitive.cexpr!(%q{ SIZET2NUM(rb_cArray) })
+ def C.rb_cFalseClass = Primitive.cexpr!(%q{ SIZET2NUM(rb_cFalseClass) })
+ def C.rb_cFloat = Primitive.cexpr!(%q{ SIZET2NUM(rb_cFloat) })
+ def C.rb_cInteger = Primitive.cexpr!(%q{ SIZET2NUM(rb_cInteger) })
+ def C.rb_cNilClass = Primitive.cexpr!(%q{ SIZET2NUM(rb_cNilClass) })
+ def C.rb_cString = Primitive.cexpr!(%q{ SIZET2NUM(rb_cString) })
+ def C.rb_cSymbol = Primitive.cexpr!(%q{ SIZET2NUM(rb_cSymbol) })
+ def C.rb_cTrueClass = Primitive.cexpr!(%q{ SIZET2NUM(rb_cTrueClass) })
+ def C.rb_mRubyVMFrozenCore = Primitive.cexpr!(%q{ SIZET2NUM(rb_mRubyVMFrozenCore) })
+ def C.rb_rjit_global_events = Primitive.cexpr!(%q{ SIZET2NUM(rb_rjit_global_events) })
+ def C.rb_vm_insns_count = Primitive.cexpr!(%q{ SIZET2NUM(rb_vm_insns_count) })
def C.rb_ary_clear
Primitive.cexpr! %q{ SIZET2NUM((size_t)rb_ary_clear) }
@@ -616,6 +567,10 @@ module RubyVM::RJIT # :nodoc: all
Primitive.cexpr! %q{ SIZET2NUM((size_t)rb_hash_new_with_size) }
end
+ def C.rb_hash_resurrect
+ Primitive.cexpr! %q{ SIZET2NUM((size_t)rb_hash_resurrect) }
+ end
+
def C.rb_ivar_defined
Primitive.cexpr! %q{ SIZET2NUM((size_t)rb_ivar_defined) }
end
@@ -820,7 +775,7 @@ module RubyVM::RJIT # :nodoc: all
), Primitive.cexpr!("OFFSETOF(((struct RArray *)NULL)->as.heap, aux)")],
ptr: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF(((struct RArray *)NULL)->as.heap, ptr)")],
),
- ary: CType::Pointer.new { self.VALUE },
+ ary: CType::Array.new { self.VALUE },
), Primitive.cexpr!("OFFSETOF((*((struct RArray *)NULL)), as)")],
)
end
@@ -848,7 +803,7 @@ module RubyVM::RJIT # :nodoc: all
ivptr: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF(((struct RObject *)NULL)->as.heap, ivptr)")],
iv_index_tbl: [CType::Pointer.new { self.rb_id_table }, Primitive.cexpr!("OFFSETOF(((struct RObject *)NULL)->as.heap, iv_index_tbl)")],
),
- ary: CType::Pointer.new { self.VALUE },
+ ary: CType::Array.new { self.VALUE },
), Primitive.cexpr!("OFFSETOF((*((struct RObject *)NULL)), as)")],
)
end
@@ -857,11 +812,11 @@ module RubyVM::RJIT # :nodoc: all
@RString ||= CType::Struct.new(
"RString", Primitive.cexpr!("SIZEOF(struct RString)"),
basic: [self.RBasic, Primitive.cexpr!("OFFSETOF((*((struct RString *)NULL)), basic)")],
+ len: [CType::Immediate.parse("long"), Primitive.cexpr!("OFFSETOF((*((struct RString *)NULL)), len)")],
as: [CType::Union.new(
"", Primitive.cexpr!("SIZEOF(((struct RString *)NULL)->as)"),
heap: CType::Struct.new(
"", Primitive.cexpr!("SIZEOF(((struct RString *)NULL)->as.heap)"),
- len: [CType::Immediate.parse("long"), Primitive.cexpr!("OFFSETOF(((struct RString *)NULL)->as.heap, len)")],
ptr: [CType::Pointer.new { CType::Immediate.parse("char") }, Primitive.cexpr!("OFFSETOF(((struct RString *)NULL)->as.heap, ptr)")],
aux: [CType::Union.new(
"", Primitive.cexpr!("SIZEOF(((struct RString *)NULL)->as.heap.aux)"),
@@ -871,8 +826,7 @@ module RubyVM::RJIT # :nodoc: all
),
embed: CType::Struct.new(
"", Primitive.cexpr!("SIZEOF(((struct RString *)NULL)->as.embed)"),
- len: [CType::Immediate.parse("long"), Primitive.cexpr!("OFFSETOF(((struct RString *)NULL)->as.embed, len)")],
- ary: [CType::Pointer.new { CType::Immediate.parse("char") }, Primitive.cexpr!("OFFSETOF(((struct RString *)NULL)->as.embed, ary)")],
+ ary: [CType::Array.new { CType::Immediate.parse("char") }, Primitive.cexpr!("OFFSETOF(((struct RString *)NULL)->as.embed, ary)")],
),
), Primitive.cexpr!("OFFSETOF((*((struct RString *)NULL)), as)")],
)
@@ -889,7 +843,7 @@ module RubyVM::RJIT # :nodoc: all
len: [CType::Immediate.parse("long"), Primitive.cexpr!("OFFSETOF(((struct RStruct *)NULL)->as.heap, len)")],
ptr: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF(((struct RStruct *)NULL)->as.heap, ptr)")],
),
- ary: CType::Pointer.new { self.VALUE },
+ ary: CType::Array.new { self.VALUE },
), Primitive.cexpr!("OFFSETOF((*((struct RStruct *)NULL)), as)")],
)
end
@@ -1034,6 +988,7 @@ module RubyVM::RJIT # :nodoc: all
@rb_callinfo_kwarg ||= CType::Struct.new(
"rb_callinfo_kwarg", Primitive.cexpr!("SIZEOF(struct rb_callinfo_kwarg)"),
keyword_len: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo_kwarg *)NULL)), keyword_len)")],
+ references: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo_kwarg *)NULL)), references)")],
keywords: [CType::Immediate.parse("void *"), Primitive.cexpr!("OFFSETOF((*((struct rb_callinfo_kwarg *)NULL)), keywords)")],
)
end
@@ -1052,6 +1007,10 @@ module RubyVM::RJIT # :nodoc: all
)
end
+ def C.rb_cfunc_t
+ @rb_cfunc_t ||= self.VALUE
+ end
+
def C.rb_control_frame_t
@rb_control_frame_t ||= CType::Struct.new(
"rb_control_frame_struct", Primitive.cexpr!("SIZEOF(struct rb_control_frame_struct)"),
@@ -1061,7 +1020,6 @@ module RubyVM::RJIT # :nodoc: all
self: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), self)")],
ep: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), ep)")],
block_code: [CType::Immediate.parse("void *"), Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), block_code)")],
- __bp__: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), __bp__)")],
jit_return: [CType::Pointer.new { CType::Immediate.parse("void") }, Primitive.cexpr!("OFFSETOF((*((struct rb_control_frame_struct *)NULL)), jit_return)")],
)
end
@@ -1133,6 +1091,9 @@ module RubyVM::RJIT # :nodoc: all
ambiguous_param0: [CType::BitField.new(1, 7), 7],
accepts_no_kwarg: [CType::BitField.new(1, 0), 8],
ruby2_keywords: [CType::BitField.new(1, 1), 9],
+ anon_rest: [CType::BitField.new(1, 2), 10],
+ anon_kwrest: [CType::BitField.new(1, 3), 11],
+ use_block: [CType::BitField.new(1, 4), 12],
), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, flags)")],
size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, size)")],
lead_num: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, lead_num)")],
@@ -1168,6 +1129,7 @@ module RubyVM::RJIT # :nodoc: all
ci_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), ci_size)")],
stack_max: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), stack_max)")],
builtin_attrs: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), builtin_attrs)")],
+ prism: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), prism)")],
mark_bits: [CType::Union.new(
"", Primitive.cexpr!("SIZEOF(((struct rb_iseq_constant_body *)NULL)->mark_bits)"),
list: CType::Pointer.new { self.iseq_bits_t },
@@ -1175,8 +1137,8 @@ module RubyVM::RJIT # :nodoc: all
), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), mark_bits)")],
outer_variables: [CType::Pointer.new { self.rb_id_table }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), outer_variables)")],
mandatory_only_iseq: [CType::Pointer.new { self.rb_iseq_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), mandatory_only_iseq)")],
- jit_func: [self.rb_jit_func_t, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), jit_func)")],
- total_calls: [CType::Immediate.parse("unsigned long"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), total_calls)")],
+ jit_entry: [self.rb_jit_func_t, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), jit_entry)")],
+ jit_entry_calls: [CType::Immediate.parse("unsigned long"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), jit_entry_calls)")],
rjit_blocks: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), rjit_blocks)"), true],
)
end
@@ -1256,7 +1218,7 @@ module RubyVM::RJIT # :nodoc: all
def C.rb_method_cfunc_t
@rb_method_cfunc_t ||= CType::Struct.new(
"rb_method_cfunc_struct", Primitive.cexpr!("SIZEOF(struct rb_method_cfunc_struct)"),
- func: [CType::Immediate.parse("void *"), Primitive.cexpr!("OFFSETOF((*((struct rb_method_cfunc_struct *)NULL)), func)")],
+ func: [self.rb_cfunc_t, Primitive.cexpr!("OFFSETOF((*((struct rb_method_cfunc_struct *)NULL)), func)")],
invoker: [CType::Immediate.parse("void *"), Primitive.cexpr!("OFFSETOF((*((struct rb_method_cfunc_struct *)NULL)), invoker)")],
argc: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_method_cfunc_struct *)NULL)), argc)")],
)
@@ -1267,9 +1229,9 @@ module RubyVM::RJIT # :nodoc: all
"rb_method_definition_struct", Primitive.cexpr!("SIZEOF(struct rb_method_definition_struct)"),
type: [CType::BitField.new(4, 0), 0],
iseq_overload: [CType::BitField.new(1, 4), 4],
- alias_count: [CType::BitField.new(27, 5), 5],
- complemented_count: [CType::BitField.new(28, 0), 32],
- no_redef_warning: [CType::BitField.new(1, 4), 60],
+ no_redef_warning: [CType::BitField.new(1, 5), 5],
+ aliased: [CType::BitField.new(1, 6), 6],
+ reference_count: [CType::BitField.new(28, 0), 32],
body: [CType::Union.new(
"", Primitive.cexpr!("SIZEOF(((struct rb_method_definition_struct *)NULL)->body)"),
iseq: self.rb_method_iseq_t,
@@ -1327,20 +1289,20 @@ module RubyVM::RJIT # :nodoc: all
@rb_rjit_options ||= CType::Struct.new(
"rb_rjit_options", Primitive.cexpr!("SIZEOF(struct rb_rjit_options)"),
on: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_options *)NULL)), on)")],
- call_threshold: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_options *)NULL)), call_threshold)")],
exec_mem_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_options *)NULL)), exec_mem_size)")],
+ call_threshold: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_options *)NULL)), call_threshold)")],
stats: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_options *)NULL)), stats)")],
+ disable: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_options *)NULL)), disable)")],
+ trace: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_options *)NULL)), trace)")],
trace_exits: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_options *)NULL)), trace_exits)")],
dump_disasm: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_options *)NULL)), dump_disasm)")],
verify_ctx: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_options *)NULL)), verify_ctx)")],
- pause: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_options *)NULL)), pause)")],
)
end
def C.rb_rjit_runtime_counters
@rb_rjit_runtime_counters ||= CType::Struct.new(
"rb_rjit_runtime_counters", Primitive.cexpr!("SIZEOF(struct rb_rjit_runtime_counters)"),
- vm_insns_count: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), vm_insns_count)")],
rjit_insns_count: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), rjit_insns_count)")],
send_args_splat_kw_splat: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), send_args_splat_kw_splat)")],
send_args_splat: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), send_args_splat)")],
@@ -1390,6 +1352,7 @@ module RubyVM::RJIT # :nodoc: all
send_iseq_has_rest: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), send_iseq_has_rest)")],
send_iseq_block_arg0_splat: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), send_iseq_block_arg0_splat)")],
send_iseq_kw_call: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), send_iseq_kw_call)")],
+ send_iseq_kw_splat: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), send_iseq_kw_splat)")],
send_iseq_splat: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), send_iseq_splat)")],
send_iseq_has_rest_and_optional: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), send_iseq_has_rest_and_optional)")],
send_iseq_arity_error: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), send_iseq_arity_error)")],
@@ -1480,6 +1443,7 @@ module RubyVM::RJIT # :nodoc: all
type: [CType::Immediate.parse("uint8_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), type)")],
size_pool_index: [CType::Immediate.parse("uint8_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), size_pool_index)")],
parent_id: [self.shape_id_t, Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), parent_id)")],
+ ancestor_index: [CType::Pointer.new { self.redblack_node_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), ancestor_index)")],
)
end
@@ -1497,6 +1461,7 @@ module RubyVM::RJIT # :nodoc: all
nt: [CType::Pointer.new { self.rb_native_thread }, Primitive.cexpr!("OFFSETOF((*((struct rb_thread_struct *)NULL)), nt)")],
ec: [CType::Pointer.new { self.rb_execution_context_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_thread_struct *)NULL)), ec)")],
sched: [self.rb_thread_sched_item, Primitive.cexpr!("OFFSETOF((*((struct rb_thread_struct *)NULL)), sched)")],
+ mn_schedulable: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_thread_struct *)NULL)), mn_schedulable)")],
serial: [self.rb_atomic_t, Primitive.cexpr!("OFFSETOF((*((struct rb_thread_struct *)NULL)), serial)")],
last_status: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_thread_struct *)NULL)), last_status)")],
calling: [CType::Pointer.new { self.rb_calling_info }, Primitive.cexpr!("OFFSETOF((*((struct rb_thread_struct *)NULL)), calling)")],
@@ -1534,16 +1499,23 @@ module RubyVM::RJIT # :nodoc: all
scheduler: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_thread_struct *)NULL)), scheduler)")],
blocking: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rb_thread_struct *)NULL)), blocking)")],
name: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_thread_struct *)NULL)), name)")],
+ specific_storage: [CType::Pointer.new { CType::Pointer.new { CType::Immediate.parse("void") } }, Primitive.cexpr!("OFFSETOF((*((struct rb_thread_struct *)NULL)), specific_storage)")],
ext_config: [self.rb_ext_config, Primitive.cexpr!("OFFSETOF((*((struct rb_thread_struct *)NULL)), ext_config)")],
)
end
def C.VALUE
- @VALUE ||= CType::Immediate.find(Primitive.cexpr!("SIZEOF(VALUE)"), Primitive.cexpr!("SIGNED_TYPE_P(VALUE)"))
+ @VALUE ||= CType::Immediate.find(
+ Primitive.cexpr!("SIZEOF(VALUE)"),
+ Primitive.cexpr!("SIGNED_TYPE_P(VALUE)"),
+ )
end
def C.shape_id_t
- @shape_id_t ||= CType::Immediate.find(Primitive.cexpr!("SIZEOF(shape_id_t)"), Primitive.cexpr!("SIGNED_TYPE_P(shape_id_t)"))
+ @shape_id_t ||= CType::Immediate.find(
+ Primitive.cexpr!("SIZEOF(shape_id_t)"),
+ Primitive.cexpr!("SIGNED_TYPE_P(shape_id_t)"),
+ )
end
def C.rb_id_table
@@ -1606,6 +1578,10 @@ module RubyVM::RJIT # :nodoc: all
CType::Stub.new(:rb_snum_t)
end
+ def C._Bool
+ CType::Bool.new
+ end
+
def C.iseq_bits_t
CType::Stub.new(:iseq_bits_t)
end
@@ -1634,8 +1610,8 @@ module RubyVM::RJIT # :nodoc: all
CType::Stub.new(:rb_method_refined_t)
end
- def C._Bool
- CType::Bool.new
+ def C.redblack_node_t
+ CType::Stub.new(:redblack_node_t)
end
def C.ccan_list_node
diff --git a/ruby-runner.c b/ruby-runner.c
index bbc2b78fa0..48acf4396e 100644
--- a/ruby-runner.c
+++ b/ruby-runner.c
@@ -7,6 +7,10 @@
#include <sys/types.h>
#include <sys/stat.h>
+#ifdef _WIN32
+# error This feature is unnecessary on Windows in favor of SxS.
+#endif
+
#include "ruby-runner.h"
static void
@@ -25,12 +29,24 @@ insert_env_path(const char *envname, const char *paths, size_t size, int prepend
char *e = malloc(size+n+1);
size_t pos = 0;
if (prepend) {
+ if (size == n || (size < n && env[size] == PATH_SEP)) {
+ if (strncmp(paths, env, size) == 0) {
+ free(e);
+ return;
+ }
+ }
memcpy(e, paths, pos = size-1);
e[pos++] = PATH_SEP;
}
memcpy(e+pos, env, n);
pos += n;
if (!prepend) {
+ if (size == n || (size < n && env[n-size-1] == PATH_SEP)) {
+ if (strncmp(paths, &env[n-size], size) == 0) {
+ free(e);
+ return;
+ }
+ }
e[pos++] = PATH_SEP;
memcpy(e+pos, paths, size-1);
pos += size-1;
diff --git a/ruby.c b/ruby.c
index b2d412c49d..bfff15ba30 100644
--- a/ruby.c
+++ b/ruby.c
@@ -22,7 +22,7 @@
# include <sys/cygwin.h>
#endif
-#if (defined(LOAD_RELATIVE) || defined(__MACH__)) && defined(HAVE_DLADDR)
+#if defined(LOAD_RELATIVE) && defined(HAVE_DLADDR)
# include <dlfcn.h>
#endif
@@ -53,7 +53,8 @@
#include "internal/loadpath.h"
#include "internal/missing.h"
#include "internal/object.h"
-#include "internal/parse.h"
+#include "internal/thread.h"
+#include "internal/ruby_parser.h"
#include "internal/variable.h"
#include "ruby/encoding.h"
#include "ruby/thread.h"
@@ -150,22 +151,19 @@ enum feature_flag_bits {
SEP \
X(parsetree) \
SEP \
- X(parsetree_with_comment) \
- SEP \
X(insns) \
- SEP \
- X(insns_without_opt) \
/* END OF DUMPS */
enum dump_flag_bits {
dump_version_v,
- dump_error_tolerant,
+ dump_opt_error_tolerant,
+ dump_opt_comment,
+ dump_opt_optimize,
EACH_DUMPS(DEFINE_DUMP, COMMA),
- dump_error_tolerant_bits = (DUMP_BIT(yydebug) |
- DUMP_BIT(parsetree) |
- DUMP_BIT(parsetree_with_comment)),
dump_exit_bits = (DUMP_BIT(yydebug) | DUMP_BIT(syntax) |
- DUMP_BIT(parsetree) | DUMP_BIT(parsetree_with_comment) |
- DUMP_BIT(insns) | DUMP_BIT(insns_without_opt))
+ DUMP_BIT(parsetree) | DUMP_BIT(insns)),
+ dump_optional_bits = (DUMP_BIT(opt_error_tolerant) |
+ DUMP_BIT(opt_comment) |
+ DUMP_BIT(opt_optimize))
};
static inline void
@@ -203,6 +201,10 @@ enum {
)
};
+#define BACKTRACE_LENGTH_LIMIT_VALID_P(n) ((n) >= -1)
+#define OPT_BACKTRACE_LENGTH_LIMIT_VALID_P(opt) \
+ BACKTRACE_LENGTH_LIMIT_VALID_P((opt)->backtrace_length_limit)
+
static ruby_cmdline_options_t *
cmdline_options_init(ruby_cmdline_options_t *opt)
{
@@ -217,11 +219,13 @@ cmdline_options_init(ruby_cmdline_options_t *opt)
#elif defined(YJIT_FORCE_ENABLE)
opt->features.set |= FEATURE_BIT(yjit);
#endif
+ opt->dump |= DUMP_BIT(opt_optimize);
+ opt->backtrace_length_limit = LONG_MIN;
return opt;
}
-static rb_ast_t *load_file(VALUE parser, VALUE fname, VALUE f, int script,
+static VALUE load_file(VALUE parser, VALUE fname, VALUE f, int script,
ruby_cmdline_options_t *opt);
static VALUE open_load_file(VALUE fname_v, int *xflag);
static void forbid_setid(const char *, const ruby_cmdline_options_t *);
@@ -236,46 +240,71 @@ static const char esc_standout[] = "\n\033[1;7m";
static const char esc_bold[] = "\033[1m";
static const char esc_reset[] = "\033[0m";
static const char esc_none[] = "";
+#define USAGE_INDENT " " /* macro for concatenation */
static void
-show_usage_line(const struct ruby_opt_message *m,
+show_usage_part(const char *str, const unsigned int namelen,
+ const char *str2, const unsigned int secondlen,
+ const char *desc,
int help, int highlight, unsigned int w, int columns)
{
- const char *str = m->str;
- const unsigned int namelen = m->namelen, secondlen = m->secondlen;
+ static const int indent_width = (int)rb_strlen_lit(USAGE_INDENT);
const char *sb = highlight ? esc_bold : esc_none;
const char *se = highlight ? esc_reset : esc_none;
- const char *desc = str + namelen + secondlen;
unsigned int desclen = (unsigned int)strcspn(desc, "\n");
- if (help && (namelen > w) && (int)(namelen + secondlen) >= columns) {
- printf(" %s" "%.*s" "%s\n", sb, namelen-1, str, se);
- if (secondlen > 1) {
- const int second_end = namelen+secondlen-1;
- int n = namelen;
- if (str[n] == ',') n++;
- if (str[n] == ' ') n++;
- printf(" %s" "%.*s" "%s\n", sb, second_end-n, str+n, se);
+ if (!help && desclen > 0 && strchr(".;:", desc[desclen-1])) --desclen;
+ if (help && (namelen + 1 > w) && /* a padding space */
+ (int)(namelen + secondlen + indent_width) >= columns) {
+ printf(USAGE_INDENT "%s" "%.*s" "%s\n", sb, namelen, str, se);
+ if (secondlen > 0) {
+ const int second_end = secondlen;
+ int n = 0;
+ if (str2[n] == ',') n++;
+ if (str2[n] == ' ') n++;
+ printf(USAGE_INDENT "%s" "%.*s" "%s\n", sb, second_end-n, str2+n, se);
}
- printf("%-*s%.*s\n", w + 2, "", desclen, desc);
+ printf("%-*s%.*s\n", w + indent_width, USAGE_INDENT, desclen, desc);
}
else {
- const int wrap = help && namelen + secondlen - 1 > w;
- printf(" %s%.*s%-*.*s%s%-*s%.*s\n", sb, namelen-1, str,
- (wrap ? 0 : w - namelen + 1),
- (help ? secondlen-1 : 0), str + namelen, se,
- (wrap ? w + 3 : 0), (wrap ? "\n" : ""),
+ const int wrap = help && namelen + secondlen >= w;
+ printf(USAGE_INDENT "%s%.*s%-*.*s%s%-*s%.*s\n", sb, namelen, str,
+ (wrap ? 0 : w - namelen),
+ (help ? secondlen : 0), str2, se,
+ (wrap ? (int)(w + rb_strlen_lit("\n" USAGE_INDENT)) : 0),
+ (wrap ? "\n" USAGE_INDENT : ""),
desclen, desc);
}
if (help) {
while (desc[desclen]) {
- desc += desclen + 1;
+ desc += desclen + rb_strlen_lit("\n");
desclen = (unsigned int)strcspn(desc, "\n");
- printf("%-*s%.*s\n", w + 2, "", desclen, desc);
+ printf("%-*s%.*s\n", w + indent_width, USAGE_INDENT, desclen, desc);
}
}
}
static void
+show_usage_line(const struct ruby_opt_message *m,
+ int help, int highlight, unsigned int w, int columns)
+{
+ const char *str = m->str;
+ const unsigned int namelen = m->namelen, secondlen = m->secondlen;
+ const char *desc = str + namelen + secondlen;
+ show_usage_part(str, namelen - 1, str + namelen, secondlen - 1, desc,
+ help, highlight, w, columns);
+}
+
+void
+ruby_show_usage_line(const char *name, const char *secondary, const char *description,
+ int help, int highlight, unsigned int width, int columns)
+{
+ unsigned int namelen = (unsigned int)strlen(name);
+ unsigned int secondlen = (secondary ? (unsigned int)strlen(secondary) : 0);
+ show_usage_part(name, namelen, secondary, secondlen,
+ description, help, highlight, width, columns);
+}
+
+static void
usage(const char *name, int help, int highlight, int columns)
{
#define M(shortopt, longopt, desc) RUBY_OPT_MESSAGE(shortopt, longopt, desc)
@@ -289,89 +318,84 @@ usage(const char *name, int help, int highlight, int columns)
/* This message really ought to be max 23 lines.
* Removed -h because the user already knows that option. Others? */
static const struct ruby_opt_message usage_msg[] = {
- M("-0[octal]", "", "specify record separator (\\0, if no argument)\n"
- "(-00 for paragraph mode, -0777 for slurp mode)"),
- M("-a", "", "autosplit mode with -n or -p (splits $_ into $F)"),
- M("-c", "", "check syntax only"),
- M("-Cdirectory", "", "cd to directory before executing your script"),
- M("-d", ", --debug", "set debugging flags (set $DEBUG to true)"),
- M("-e 'command'", "", "one line of script. Several -e's allowed. Omit [programfile]"),
- M("-Eex[:in]", ", --encoding=ex[:in]", "specify the default external and internal character encodings"),
- M("-Fpattern", "", "split() pattern for autosplit (-a)"),
- M("-i[extension]", "", "edit ARGV files in place (make backup if extension supplied)"),
- M("-Idirectory", "", "specify $LOAD_PATH directory (may be used more than once)"),
- M("-l", "", "enable line ending processing"),
- M("-n", "", "assume 'while gets(); ... end' loop around your script"),
- M("-p", "", "assume loop like -n but print line also like sed"),
- M("-rlibrary", "", "require the library before executing your script"),
- M("-s", "", "enable some switch parsing for switches after script name"),
- M("-S", "", "look for the script using PATH environment variable"),
- M("-v", "", "print the version number, then turn on verbose mode"),
- M("-w", "", "turn warnings on for your script"),
- M("-W[level=2|:category]", "", "set warning level; 0=silence, 1=medium, 2=verbose"),
- M("-x[directory]", "", "strip off text before #!ruby line and perhaps cd to directory"),
- M("--jit", "", "enable JIT for the platform, same as " PLATFORM_JIT_OPTION),
+ M("-0[octal]", "", "Set input record separator ($/):\n"
+ "-0 for \\0; -00 for paragraph mode; -0777 for slurp mode."),
+ M("-a", "", "Split each input line ($_) into fields ($F)."),
+ M("-c", "", "Check syntax (no execution)."),
+ M("-Cdirpath", "", "Execute program in specified directory."),
+ M("-d", ", --debug", "Set debugging flag ($DEBUG) to true."),
+ M("-e 'code'", "", "Execute given Ruby code; multiple -e allowed."),
+ M("-Eex[:in]", ", --encoding=ex[:in]", "Set default external and internal encodings."),
+ M("-Fpattern", "", "Set input field separator ($;); used with -a."),
+ M("-i[extension]", "", "Set ARGF in-place mode;\n"
+ "create backup files with given extension."),
+ M("-Idirpath", "", "Add specified directory to load paths ($LOAD_PATH);\n"
+ "multiple -I allowed."),
+ M("-l", "", "Set output record separator ($\\) to $/;\n"
+ "used for line-oriented output."),
+ M("-n", "", "Run program in gets loop."),
+ M("-p", "", "Like -n, with printing added."),
+ M("-rlibrary", "", "Require the given library."),
+ M("-s", "", "Define global variables using switches following program path."),
+ M("-S", "", "Search directories found in the PATH environment variable."),
+ M("-v", "", "Print version; set $VERBOSE to true."),
+ M("-w", "", "Synonym for -W1."),
+ M("-W[level=2|:category]", "", "Set warning flag ($-W):\n"
+ "0 for silent; 1 for moderate; 2 for verbose."),
+ M("-x[dirpath]", "", "Execute Ruby code starting from a #!ruby line."),
+ M("--jit", "", "Enable JIT for the platform; same as " PLATFORM_JIT_OPTION "."),
#if USE_YJIT
- M("--yjit", "", "enable in-process JIT compiler"),
+ M("--yjit", "", "Enable in-process JIT compiler."),
#endif
#if USE_RJIT
- M("--rjit", "", "enable pure-Ruby JIT compiler (experimental)"),
+ M("--rjit", "", "Enable pure-Ruby JIT compiler (experimental)."),
#endif
- M("-h", "", "show this message, --help for more info"),
+ M("-h", "", "Print this help message; use --help for longer message."),
};
STATIC_ASSERT(usage_msg_size, numberof(usage_msg) < 25);
static const struct ruby_opt_message help_msg[] = {
- M("--copyright", "", "print the copyright"),
- M("--dump={insns|parsetree|...}[,...]", "",
- "dump debug information. see below for available dump list"),
- M("--enable={jit|rubyopt|...}[,...]", ", --disable={jit|rubyopt|...}[,...]",
- "enable or disable features. see below for available features"),
- M("--external-encoding=encoding", ", --internal-encoding=encoding",
- "specify the default external or internal character encoding"),
- M("--backtrace-limit=num", "", "limit the maximum length of backtrace"),
- M("--verbose", "", "turn on verbose mode and disable script from stdin"),
- M("--version", "", "print the version number, then exit"),
- M("-y", ", --yydebug", "print log of parser. Backward compatibility is not guaranteed"),
- M("--help", "", "show this message, -h for short message"),
+ M("--backtrace-limit=num", "", "Set backtrace limit."),
+ M("--copyright", "", "Print Ruby copyright."),
+ M("--crash-report=template", "", "Set template for crash report file."),
+ M("--disable=features", "", "Disable features; see list below."),
+ M("--dump=items", "", "Dump items; see list below."),
+ M("--enable=features", "", "Enable features; see list below."),
+ M("--external-encoding=encoding", "", "Set default external encoding."),
+ M("--help", "", "Print long help message; use -h for short message."),
+ M("--internal-encoding=encoding", "", "Set default internal encoding."),
+ M("--parser=parser", "", "Set Ruby parser: parse.y or prism."),
+ M("--verbose", "", "Set $VERBOSE to true; ignore input from $stdin."),
+ M("--version", "", "Print Ruby version."),
+ M("-y", ", --yydebug", "Print parser log; backward compatibility not guaranteed."),
};
static const struct ruby_opt_message dumps[] = {
- M("insns", "", "instruction sequences"),
- M("insns_without_opt", "", "instruction sequences compiled with no optimization"),
- M("yydebug(+error-tolerant)", "", "yydebug of yacc parser generator"),
- M("parsetree(+error-tolerant)","", "AST"),
- M("parsetree_with_comment(+error-tolerant)", "", "AST with comments"),
+ M("insns", "", "Instruction sequences."),
+ M("yydebug", "", "yydebug of yacc parser generator."),
+ M("parsetree", "", "Abstract syntax tree (AST)."),
+ M("-optimize", "", "Disable optimization (affects insns)."),
+ M("+error-tolerant", "", "Error-tolerant parsing (affects yydebug, parsetree)."),
+ M("+comment", "", "Add comments to AST (affects parsetree)."),
};
static const struct ruby_opt_message features[] = {
- M("gems", "", "rubygems (only for debugging, default: "DEFAULT_RUBYGEMS_ENABLED")"),
- M("error_highlight", "", "error_highlight (default: "DEFAULT_RUBYGEMS_ENABLED")"),
- M("did_you_mean", "", "did_you_mean (default: "DEFAULT_RUBYGEMS_ENABLED")"),
- M("syntax_suggest", "", "syntax_suggest (default: "DEFAULT_RUBYGEMS_ENABLED")"),
- M("rubyopt", "", "RUBYOPT environment variable (default: enabled)"),
- M("frozen-string-literal", "", "freeze all string literals (default: disabled)"),
+ M("gems", "", "Rubygems (only for debugging, default: "DEFAULT_RUBYGEMS_ENABLED")."),
+ M("error_highlight", "", "error_highlight (default: "DEFAULT_RUBYGEMS_ENABLED")."),
+ M("did_you_mean", "", "did_you_mean (default: "DEFAULT_RUBYGEMS_ENABLED")."),
+ M("syntax_suggest", "", "syntax_suggest (default: "DEFAULT_RUBYGEMS_ENABLED")."),
+ M("rubyopt", "", "RUBYOPT environment variable (default: enabled)."),
+ M("frozen-string-literal", "", "Freeze all string literals (default: disabled)."),
#if USE_YJIT
- M("yjit", "", "in-process JIT compiler (default: disabled)"),
+ M("yjit", "", "In-process JIT compiler (default: disabled)."),
#endif
#if USE_RJIT
- M("rjit", "", "pure-Ruby JIT compiler (default: disabled)"),
+ M("rjit", "", "Pure-Ruby JIT compiler (experimental, default: disabled)."),
#endif
};
static const struct ruby_opt_message warn_categories[] = {
- M("deprecated", "", "deprecated features"),
- M("experimental", "", "experimental features"),
- M("performance", "", "performance issues"),
- };
-#if USE_YJIT
- static const struct ruby_opt_message yjit_options[] = {
- M("--yjit-stats", "", "Enable collecting YJIT statistics"),
- M("--yjit-trace-exits", "", "Record Ruby source location when exiting from generated code"),
- M("--yjit-trace-exits-sample-rate", "", "Trace exit locations only every Nth occurrence"),
- M("--yjit-exec-mem-size=num", "", "Size of executable memory block in MiB (default: 64)"),
- M("--yjit-call-threshold=num", "", "Number of calls to trigger JIT (default: 30)"),
- M("--yjit-max-versions=num", "", "Maximum number of versions per basic block (default: 4)"),
- M("--yjit-greedy-versioning", "", "Greedy versioning mode (default: disabled)"),
+ M("deprecated", "", "Deprecated features."),
+ M("experimental", "", "Experimental features."),
+ M("performance", "", "Performance issues."),
};
-#endif
#if USE_RJIT
extern const struct ruby_opt_message rb_rjit_option_messages[];
#endif
@@ -382,7 +406,7 @@ usage(const char *name, int help, int highlight, int columns)
unsigned int w = (columns > 80 ? (columns - 79) / 2 : 0) + 16;
#define SHOW(m) show_usage_line(&(m), help, highlight, w, columns)
- printf("%sUsage:%s %s [switches] [--] [programfile] [arguments]\n", sb, se, name);
+ printf("%sUsage:%s %s [options] [--] [filepath] [arguments]\n", sb, se, name);
for (i = 0; i < num; ++i)
SHOW(usage_msg[i]);
@@ -403,8 +427,7 @@ usage(const char *name, int help, int highlight, int columns)
SHOW(warn_categories[i]);
#if USE_YJIT
printf("%s""YJIT options:%s\n", sb, se);
- for (i = 0; i < numberof(yjit_options); ++i)
- SHOW(yjit_options[i]);
+ rb_yjit_show_usage(help, highlight, w, columns);
#endif
#if USE_RJIT
printf("%s""RJIT options (experimental):%s\n", sb, se);
@@ -416,39 +439,31 @@ usage(const char *name, int help, int highlight, int columns)
#define rubylib_path_new rb_str_new
static void
-push_include(const char *path, VALUE (*filter)(VALUE))
+ruby_push_include(const char *path, VALUE (*filter)(VALUE))
{
const char sep = PATH_SEP_CHAR;
const char *p, *s;
VALUE load_path = GET_VM()->load_path;
-
- p = path;
- while (*p) {
- while (*p == sep)
- p++;
- if (!*p) break;
- for (s = p; *s && *s != sep; s = CharNext(s));
- rb_ary_push(load_path, (*filter)(rubylib_path_new(p, s - p)));
- p = s;
- }
-}
-
#ifdef __CYGWIN__
-static void
-push_include_cygwin(const char *path, VALUE (*filter)(VALUE))
-{
- const char *p, *s;
char rubylib[FILENAME_MAX];
VALUE buf = 0;
+# define is_path_sep(c) ((c) == sep || (c) == ';')
+#else
+# define is_path_sep(c) ((c) == sep)
+#endif
+ if (path == 0) return;
p = path;
while (*p) {
- unsigned int len;
- while (*p == ';')
+ long len;
+ while (is_path_sep(*p))
p++;
if (!*p) break;
- for (s = p; *s && *s != ';'; s = CharNext(s));
+ for (s = p; *s && !is_path_sep(*s); s = CharNext(s));
len = s - p;
+#undef is_path_sep
+
+#ifdef __CYGWIN__
if (*s) {
if (!buf) {
buf = rb_str_new(p, len);
@@ -465,23 +480,14 @@ push_include_cygwin(const char *path, VALUE (*filter)(VALUE))
#else
# error no cygwin_conv_path
#endif
- if (CONV_TO_POSIX_PATH(p, rubylib) == 0)
+ if (CONV_TO_POSIX_PATH(p, rubylib) == 0) {
p = rubylib;
- push_include(p, filter);
- if (!*s) break;
- p = s + 1;
- }
-}
-
-#define push_include push_include_cygwin
+ len = strlen(p);
+ }
#endif
-
-void
-ruby_push_include(const char *path, VALUE (*filter)(VALUE))
-{
- if (path == 0)
- return;
- push_include(path, filter);
+ rb_ary_push(load_path, (*filter)(rubylib_path_new(p, len)));
+ p = s;
+ }
}
static VALUE
@@ -489,6 +495,7 @@ identical_path(VALUE path)
{
return path;
}
+
static VALUE
locale_path(VALUE path)
{
@@ -575,7 +582,7 @@ str_conv_enc(VALUE str, rb_encoding *from, rb_encoding *to)
void ruby_init_loadpath(void);
-#if defined(LOAD_RELATIVE) || defined(__MACH__)
+#if defined(LOAD_RELATIVE)
static VALUE
runtime_libruby_path(void)
{
@@ -652,10 +659,6 @@ runtime_libruby_path(void)
#define INITIAL_LOAD_PATH_MARK rb_intern_const("@gem_prelude_index")
VALUE ruby_archlibdir_path, ruby_prefix_path;
-#if defined(__MACH__)
-// A path to libruby.dylib itself or where it's statically linked to.
-VALUE rb_libruby_selfpath;
-#endif
void
ruby_init_loadpath(void)
@@ -663,19 +666,6 @@ ruby_init_loadpath(void)
VALUE load_path, archlibdir = 0;
ID id_initial_load_path_mark;
const char *paths = ruby_initial_load_paths;
-#if defined(LOAD_RELATIVE) || defined(__MACH__)
- VALUE libruby_path = runtime_libruby_path();
-# if defined(__MACH__)
- VALUE selfpath = libruby_path;
-# if defined(LOAD_RELATIVE)
- selfpath = rb_str_dup(selfpath);
-# endif
- rb_obj_hide(selfpath);
- OBJ_FREEZE_RAW(selfpath);
- rb_gc_register_address(&rb_libruby_selfpath);
- rb_libruby_selfpath = selfpath;
-# endif
-#endif
#if defined LOAD_RELATIVE
#if !defined ENABLE_MULTIARCH
@@ -690,7 +680,7 @@ ruby_init_loadpath(void)
size_t baselen;
const char *p;
- sopath = libruby_path;
+ sopath = runtime_libruby_path();
libpath = RSTRING_PTR(sopath);
p = strrchr(libpath, '/');
@@ -716,11 +706,11 @@ ruby_init_loadpath(void)
p -= bindir_len;
archlibdir = rb_str_subseq(sopath, 0, p - libpath);
rb_str_cat_cstr(archlibdir, libdir);
- OBJ_FREEZE_RAW(archlibdir);
+ OBJ_FREEZE(archlibdir);
}
else if (p - libpath >= libdir_len && !strncmp(p - libdir_len, libdir, libdir_len)) {
archlibdir = rb_str_subseq(sopath, 0, (p2 ? p2 : p) - libpath);
- OBJ_FREEZE_RAW(archlibdir);
+ OBJ_FREEZE(archlibdir);
p -= libdir_len;
}
#ifdef ENABLE_MULTIARCH
@@ -751,7 +741,7 @@ ruby_init_loadpath(void)
#endif
rb_gc_register_address(&ruby_prefix_path);
ruby_prefix_path = PREFIX_PATH();
- OBJ_FREEZE_RAW(ruby_prefix_path);
+ OBJ_FREEZE(ruby_prefix_path);
if (!archlibdir) archlibdir = ruby_prefix_path;
rb_gc_register_address(&ruby_archlibdir_path);
ruby_archlibdir_path = archlibdir;
@@ -811,10 +801,10 @@ toplevel_context(rb_binding_t *bind)
return &bind->block;
}
-static void
-process_sflag(int *sflag)
+static int
+process_sflag(int sflag)
{
- if (*sflag > 0) {
+ if (sflag > 0) {
long n;
const VALUE *args;
VALUE argv = rb_argv;
@@ -871,8 +861,9 @@ process_sflag(int *sflag)
while (n--) {
rb_ary_shift(argv);
}
- *sflag = -1;
+ return -1;
}
+ return sflag;
}
static long proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt);
@@ -886,14 +877,26 @@ moreswitches(const char *s, ruby_cmdline_options_t *opt, int envopt)
VALUE argstr, argary;
void *ptr;
+ VALUE src_enc_name = opt->src.enc.name;
+ VALUE ext_enc_name = opt->ext.enc.name;
+ VALUE int_enc_name = opt->intern.enc.name;
+ ruby_features_t feat = opt->features;
+ ruby_features_t warn = opt->warn;
+ long backtrace_length_limit = opt->backtrace_length_limit;
+ const char *crash_report = opt->crash_report;
+
while (ISSPACE(*s)) s++;
if (!*s) return;
- argstr = rb_str_tmp_new((len = strlen(s)) + (envopt!=0));
+
+ opt->src.enc.name = opt->ext.enc.name = opt->intern.enc.name = 0;
+
+ const int hyphen = *s != '-';
+ argstr = rb_str_tmp_new((len = strlen(s)) + hyphen);
argary = rb_str_tmp_new(0);
p = RSTRING_PTR(argstr);
- if (envopt) *p++ = ' ';
- memcpy(p, s, len + 1);
+ if (hyphen) *p = '-';
+ memcpy(p + hyphen, s, len + 1);
ap = 0;
rb_str_cat(argary, (char *)&ap, sizeof(ap));
while (*p) {
@@ -921,6 +924,24 @@ moreswitches(const char *s, ruby_cmdline_options_t *opt, int envopt)
}
}
+ if (src_enc_name) {
+ opt->src.enc.name = src_enc_name;
+ }
+ if (ext_enc_name) {
+ opt->ext.enc.name = ext_enc_name;
+ }
+ if (int_enc_name) {
+ opt->intern.enc.name = int_enc_name;
+ }
+ FEATURE_SET_RESTORE(opt->features, feat);
+ FEATURE_SET_RESTORE(opt->warn, warn);
+ if (BACKTRACE_LENGTH_LIMIT_VALID_P(backtrace_length_limit)) {
+ opt->backtrace_length_limit = backtrace_length_limit;
+ }
+ if (crash_report) {
+ opt->crash_report = crash_report;
+ }
+
ruby_xfree(ptr);
/* get rid of GC */
rb_str_resize(argary, 0);
@@ -940,7 +961,7 @@ name_match_p(const char *name, const char *str, size_t len)
if (*str != '-' && *str != '_') return 0;
while (ISALNUM(*name)) name++;
if (*name != '-' && *name != '_') return 0;
- ++name;
+ if (!*++name) return 1;
++str;
if (--len == 0) return 1;
}
@@ -991,7 +1012,7 @@ feature_option(const char *str, int len, void *arg, const unsigned int enable)
#if AMBIGUOUS_FEATURE_NAMES
if (matched == 1) goto found;
if (matched > 1) {
- VALUE mesg = rb_sprintf("ambiguous feature: `%.*s' (", len, str);
+ VALUE mesg = rb_sprintf("ambiguous feature: '%.*s' (", len, str);
#define ADD_FEATURE_NAME(bit) \
if (FEATURE_BIT(bit) & set) { \
rb_str_cat_cstr(mesg, #bit); \
@@ -1005,7 +1026,7 @@ feature_option(const char *str, int len, void *arg, const unsigned int enable)
#else
(void)set;
#endif
- rb_warn("unknown argument for --%s: `%.*s'",
+ rb_warn("unknown argument for --%s: '%.*s'",
enable ? "enable" : "disable", len, str);
rb_warn("features are [%.*s].", (int)strlen(list), list);
return;
@@ -1044,7 +1065,7 @@ debug_option(const char *str, int len, void *arg)
#ifdef RUBY_DEVEL
if (ruby_patchlevel < 0 && ruby_env_debug_option(str, len, 0)) return;
#endif
- rb_warn("unknown argument for --debug: `%.*s'", len, str);
+ rb_warn("unknown argument for --debug: '%.*s'", len, str);
rb_warn("debug features are [%.*s].", (int)strlen(list), list);
}
@@ -1060,21 +1081,45 @@ memtermspn(const char *str, char term, int len)
static const char additional_opt_sep = '+';
static unsigned int
-dump_additional_option(const char *str, int len, unsigned int bits, const char *name)
+dump_additional_option_flag(const char *str, int len, unsigned int bits, bool set)
+{
+#define SET_DUMP_OPT(bit) if (NAME_MATCH_P(#bit, str, len)) { \
+ return set ? (bits | DUMP_BIT(opt_ ## bit)) : (bits & ~DUMP_BIT(opt_ ## bit)); \
+ }
+ SET_DUMP_OPT(error_tolerant);
+ SET_DUMP_OPT(comment);
+ SET_DUMP_OPT(optimize);
+#undef SET_DUMP_OPT
+ rb_warn("don't know how to dump with%s '%.*s'", set ? "" : "out", len, str);
+ return bits;
+}
+
+static unsigned int
+dump_additional_option(const char *str, int len, unsigned int bits)
{
int w;
for (; len-- > 0 && *str++ == additional_opt_sep; len -= w, str += w) {
w = memtermspn(str, additional_opt_sep, len);
-#define SET_ADDITIONAL(bit) if (NAME_MATCH_P(#bit, str, w)) { \
- if (bits & DUMP_BIT(bit)) \
- rb_warn("duplicate option to dump %s: `%.*s'", name, w, str); \
- bits |= DUMP_BIT(bit); \
- continue; \
+ bool set = true;
+ if (*str == '-' || *str == '+') {
+ set = *str++ == '+';
+ --w;
}
- if (dump_error_tolerant_bits & bits) {
- SET_ADDITIONAL(error_tolerant);
+ else {
+ int n = memtermspn(str, '-', w);
+ if (str[n] == '-') {
+ if (NAME_MATCH_P("with", str, n)) {
+ str += n;
+ w -= n;
+ }
+ else if (NAME_MATCH_P("without", str, n)) {
+ set = false;
+ str += n;
+ w -= n;
+ }
+ }
}
- rb_warn("don't know how to dump %s with `%.*s'", name, w, str);
+ bits = dump_additional_option_flag(str, w, bits, set);
}
return bits;
}
@@ -1083,16 +1128,21 @@ static void
dump_option(const char *str, int len, void *arg)
{
static const char list[] = EACH_DUMPS(LITERAL_NAME_ELEMENT, ", ");
+ unsigned int *bits_ptr = (unsigned int *)arg;
+ if (*str == '+' || *str == '-') {
+ bool set = *str++ == '+';
+ *bits_ptr = dump_additional_option_flag(str, --len, *bits_ptr, set);
+ return;
+ }
int w = memtermspn(str, additional_opt_sep, len);
#define SET_WHEN_DUMP(bit) \
- if (NAME_MATCH_P(#bit, (str), (w))) { \
- *(unsigned int *)arg |= \
- dump_additional_option(str + w, len - w, DUMP_BIT(bit), #bit); \
+ if (NAME_MATCH_P(#bit "-", (str), (w))) { \
+ *bits_ptr = dump_additional_option(str + w, len - w, *bits_ptr | DUMP_BIT(bit)); \
return; \
}
EACH_DUMPS(SET_WHEN_DUMP, ;);
- rb_warn("don't know how to dump `%.*s',", len, str);
+ rb_warn("don't know how to dump '%.*s',", len, str);
rb_warn("but only [%.*s].", (int)strlen(list), list);
}
@@ -1138,12 +1188,328 @@ setup_yjit_options(const char *s)
rb_raise(
rb_eRuntimeError,
- "invalid YJIT option `%s' (--help will show valid yjit options)",
+ "invalid YJIT option '%s' (--help will show valid yjit options)",
s
);
}
#endif
+/*
+ * Following proc_*_option functions are tree kinds:
+ *
+ * - with a required argument, takes also `argc` and `argv`, and
+ * returns the number of consumed argv including the option itself.
+ *
+ * - with a mandatory argument just after the option.
+ *
+ * - no required argument, this returns the address of
+ * the next character after the last consumed character.
+ */
+
+/* optional */
+static const char *
+proc_W_option(ruby_cmdline_options_t *opt, const char *s, int *warning)
+{
+ if (s[1] == ':') {
+ unsigned int bits = 0;
+ static const char no_prefix[] = "no-";
+ int enable = strncmp(s += 2, no_prefix, sizeof(no_prefix)-1) != 0;
+ if (!enable) s += sizeof(no_prefix)-1;
+ size_t len = strlen(s);
+ if (NAME_MATCH_P("deprecated", s, len)) {
+ bits = 1U << RB_WARN_CATEGORY_DEPRECATED;
+ }
+ else if (NAME_MATCH_P("experimental", s, len)) {
+ bits = 1U << RB_WARN_CATEGORY_EXPERIMENTAL;
+ }
+ else if (NAME_MATCH_P("performance", s, len)) {
+ bits = 1U << RB_WARN_CATEGORY_PERFORMANCE;
+ }
+ else {
+ rb_warn("unknown warning category: '%s'", s);
+ }
+ if (bits) FEATURE_SET_TO(opt->warn, bits, enable ? bits : 0);
+ return 0;
+ }
+ else {
+ size_t numlen;
+ int v = 2; /* -W as -W2 */
+
+ if (*++s) {
+ v = scan_oct(s, 1, &numlen);
+ if (numlen == 0)
+ v = 2;
+ s += numlen;
+ }
+ if (!opt->warning) {
+ switch (v) {
+ case 0:
+ ruby_verbose = Qnil;
+ break;
+ case 1:
+ ruby_verbose = Qfalse;
+ break;
+ default:
+ ruby_verbose = Qtrue;
+ break;
+ }
+ }
+ *warning = 1;
+ switch (v) {
+ case 0:
+ FEATURE_SET_TO(opt->warn, RB_WARN_CATEGORY_DEFAULT_BITS, 0);
+ break;
+ case 1:
+ FEATURE_SET_TO(opt->warn, 1U << RB_WARN_CATEGORY_DEPRECATED, 0);
+ break;
+ default:
+ FEATURE_SET(opt->warn, RB_WARN_CATEGORY_DEFAULT_BITS);
+ break;
+ }
+ return s;
+ }
+}
+
+/* required */
+static long
+proc_e_option(ruby_cmdline_options_t *opt, const char *s, long argc, char **argv)
+{
+ long n = 1;
+ forbid_setid("-e");
+ if (!*++s) {
+ if (!--argc)
+ rb_raise(rb_eRuntimeError, "no code specified for -e");
+ s = *++argv;
+ n++;
+ }
+ if (!opt->e_script) {
+ opt->e_script = rb_str_new(0, 0);
+ if (opt->script == 0)
+ opt->script = "-e";
+ }
+ rb_str_cat2(opt->e_script, s);
+ rb_str_cat2(opt->e_script, "\n");
+ return n;
+}
+
+/* optional */
+static const char *
+proc_K_option(ruby_cmdline_options_t *opt, const char *s)
+{
+ if (*++s) {
+ const char *enc_name = 0;
+ switch (*s) {
+ case 'E': case 'e':
+ enc_name = "EUC-JP";
+ break;
+ case 'S': case 's':
+ enc_name = "Windows-31J";
+ break;
+ case 'U': case 'u':
+ enc_name = "UTF-8";
+ break;
+ case 'N': case 'n': case 'A': case 'a':
+ enc_name = "ASCII-8BIT";
+ break;
+ }
+ if (enc_name) {
+ opt->src.enc.name = rb_str_new2(enc_name);
+ if (!opt->ext.enc.name)
+ opt->ext.enc.name = opt->src.enc.name;
+ }
+ s++;
+ }
+ return s;
+}
+
+/* optional */
+static const char *
+proc_0_option(ruby_cmdline_options_t *opt, const char *s)
+{
+ size_t numlen;
+ int v;
+ char c;
+
+ v = scan_oct(s, 4, &numlen);
+ s += numlen;
+ if (v > 0377)
+ rb_rs = Qnil;
+ else if (v == 0 && numlen >= 2) {
+ rb_rs = rb_str_new2("");
+ }
+ else {
+ c = v & 0xff;
+ rb_rs = rb_str_new(&c, 1);
+ }
+ return s;
+}
+
+/* mandatory */
+static void
+proc_encoding_option(ruby_cmdline_options_t *opt, const char *s, const char *opt_name)
+{
+ char *p;
+# define set_encoding_part(type) \
+ if (!(p = strchr(s, ':'))) { \
+ set_##type##_encoding_once(opt, s, 0); \
+ return; \
+ } \
+ else if (p > s) { \
+ set_##type##_encoding_once(opt, s, p-s); \
+ }
+ set_encoding_part(external);
+ if (!*(s = ++p)) return;
+ set_encoding_part(internal);
+ if (!*(s = ++p)) return;
+#if defined ALLOW_DEFAULT_SOURCE_ENCODING && ALLOW_DEFAULT_SOURCE_ENCODING
+ set_encoding_part(source);
+ if (!*(s = ++p)) return;
+#endif
+ rb_raise(rb_eRuntimeError, "extra argument for %s: %s", opt_name, s);
+# undef set_encoding_part
+ UNREACHABLE;
+}
+
+static long
+proc_long_options(ruby_cmdline_options_t *opt, const char *s, long argc, char **argv, int envopt)
+{
+ size_t n;
+ long argc0 = argc;
+# define is_option_end(c, allow_hyphen) \
+ (!(c) || ((allow_hyphen) && (c) == '-') || (c) == '=')
+# define check_envopt(name, allow_envopt) \
+ (((allow_envopt) || !envopt) ? (void)0 : \
+ rb_raise(rb_eRuntimeError, "invalid switch in RUBYOPT: --" name))
+# define need_argument(name, s, needs_arg, next_arg) \
+ ((*(s) ? !*++(s) : (next_arg) && (argc <= 1 || !((s) = argv[1]) || (--argc, ++argv, 0))) && (needs_arg) ? \
+ rb_raise(rb_eRuntimeError, "missing argument for --" name) \
+ : (void)0)
+# define is_option_with_arg(name, allow_hyphen, allow_envopt) \
+ is_option_with_optarg(name, allow_hyphen, allow_envopt, Qtrue, Qtrue)
+# define is_option_with_optarg(name, allow_hyphen, allow_envopt, needs_arg, next_arg) \
+ (strncmp((name), s, n = sizeof(name) - 1) == 0 && is_option_end(s[n], (allow_hyphen)) && \
+ (s[n] != '-' || (s[n] && s[n+1])) ? \
+ (check_envopt(name, (allow_envopt)), s += n, \
+ need_argument(name, s, needs_arg, next_arg), 1) : 0)
+
+ if (strcmp("copyright", s) == 0) {
+ if (envopt) goto noenvopt_long;
+ opt->dump |= DUMP_BIT(copyright);
+ }
+ else if (is_option_with_optarg("debug", Qtrue, Qtrue, Qfalse, Qfalse)) {
+ if (s && *s) {
+ ruby_each_words(s, debug_option, &opt->features);
+ }
+ else {
+ ruby_debug = Qtrue;
+ ruby_verbose = Qtrue;
+ }
+ }
+ else if (is_option_with_arg("enable", Qtrue, Qtrue)) {
+ ruby_each_words(s, enable_option, &opt->features);
+ }
+ else if (is_option_with_arg("disable", Qtrue, Qtrue)) {
+ ruby_each_words(s, disable_option, &opt->features);
+ }
+ else if (is_option_with_arg("encoding", Qfalse, Qtrue)) {
+ proc_encoding_option(opt, s, "--encoding");
+ }
+ else if (is_option_with_arg("internal-encoding", Qfalse, Qtrue)) {
+ set_internal_encoding_once(opt, s, 0);
+ }
+ else if (is_option_with_arg("external-encoding", Qfalse, Qtrue)) {
+ set_external_encoding_once(opt, s, 0);
+ }
+ else if (is_option_with_arg("parser", Qfalse, Qtrue)) {
+ if (strcmp("prism", s) == 0) {
+ (*rb_ruby_prism_ptr()) = true;
+ }
+ else if (strcmp("parse.y", s) == 0) {
+ // default behavior
+ }
+ else {
+ rb_raise(rb_eRuntimeError, "unknown parser %s", s);
+ }
+ }
+#if defined ALLOW_DEFAULT_SOURCE_ENCODING && ALLOW_DEFAULT_SOURCE_ENCODING
+ else if (is_option_with_arg("source-encoding", Qfalse, Qtrue)) {
+ set_source_encoding_once(opt, s, 0);
+ }
+#endif
+ else if (strcmp("version", s) == 0) {
+ if (envopt) goto noenvopt_long;
+ opt->dump |= DUMP_BIT(version);
+ }
+ else if (strcmp("verbose", s) == 0) {
+ opt->verbose = 1;
+ ruby_verbose = Qtrue;
+ }
+ else if (strcmp("jit", s) == 0) {
+#if USE_YJIT || USE_RJIT
+ FEATURE_SET(opt->features, FEATURE_BIT(jit));
+#else
+ rb_warn("Ruby was built without JIT support");
+#endif
+ }
+ else if (is_option_with_optarg("rjit", '-', true, false, false)) {
+#if USE_RJIT
+ extern void rb_rjit_setup_options(const char *s, struct rb_rjit_options *rjit_opt);
+ FEATURE_SET(opt->features, FEATURE_BIT(rjit));
+ rb_rjit_setup_options(s, &opt->rjit);
+#else
+ rb_warn("RJIT support is disabled.");
+#endif
+ }
+ else if (is_option_with_optarg("yjit", '-', true, false, false)) {
+#if USE_YJIT
+ FEATURE_SET(opt->features, FEATURE_BIT(yjit));
+ setup_yjit_options(s);
+#else
+ rb_warn("Ruby was built without YJIT support."
+ " You may need to install rustc to build Ruby with YJIT.");
+#endif
+ }
+ else if (strcmp("yydebug", s) == 0) {
+ if (envopt) goto noenvopt_long;
+ opt->dump |= DUMP_BIT(yydebug);
+ }
+ else if (is_option_with_arg("dump", Qfalse, Qfalse)) {
+ ruby_each_words(s, dump_option, &opt->dump);
+ }
+ else if (strcmp("help", s) == 0) {
+ if (envopt) goto noenvopt_long;
+ opt->dump |= DUMP_BIT(help);
+ return 0;
+ }
+ else if (is_option_with_arg("backtrace-limit", Qfalse, Qtrue)) {
+ char *e;
+ long n = strtol(s, &e, 10);
+ if (errno == ERANGE || !BACKTRACE_LENGTH_LIMIT_VALID_P(n) || *e) {
+ rb_raise(rb_eRuntimeError, "wrong limit for backtrace length");
+ }
+ else {
+ opt->backtrace_length_limit = n;
+ }
+ }
+ else if (is_option_with_arg("crash-report", true, true)) {
+ opt->crash_report = s;
+ }
+ else {
+ rb_raise(rb_eRuntimeError,
+ "invalid option --%s (-h will show valid options)", s);
+ }
+ return argc0 - argc + 1;
+
+ noenvopt_long:
+ rb_raise(rb_eRuntimeError, "invalid switch in RUBYOPT: --%s", s);
+# undef is_option_end
+# undef check_envopt
+# undef need_argument
+# undef is_option_with_arg
+# undef is_option_with_optarg
+ UNREACHABLE_RETURN(0);
+}
+
static long
proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt)
{
@@ -1207,63 +1573,7 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt)
goto reswitch;
case 'W':
- if (s[1] == ':') {
- unsigned int bits = 0;
- static const char no_prefix[] = "no-";
- int enable = strncmp(s += 2, no_prefix, sizeof(no_prefix)-1) != 0;
- if (!enable) s += sizeof(no_prefix)-1;
- size_t len = strlen(s);
- if (NAME_MATCH_P("deprecated", s, len)) {
- bits = 1U << RB_WARN_CATEGORY_DEPRECATED;
- }
- else if (NAME_MATCH_P("experimental", s, len)) {
- bits = 1U << RB_WARN_CATEGORY_EXPERIMENTAL;
- }
- else if (NAME_MATCH_P("performance", s, len)) {
- bits = 1U << RB_WARN_CATEGORY_PERFORMANCE;
- }
- else {
- rb_warn("unknown warning category: `%s'", s);
- }
- if (bits) FEATURE_SET_TO(opt->warn, bits, enable ? bits : 0);
- break;
- }
- {
- size_t numlen;
- int v = 2; /* -W as -W2 */
-
- if (*++s) {
- v = scan_oct(s, 1, &numlen);
- if (numlen == 0)
- v = 2;
- s += numlen;
- }
- if (!opt->warning) {
- switch (v) {
- case 0:
- ruby_verbose = Qnil;
- break;
- case 1:
- ruby_verbose = Qfalse;
- break;
- default:
- ruby_verbose = Qtrue;
- break;
- }
- }
- warning = 1;
- switch (v) {
- case 0:
- FEATURE_SET_TO(opt->warn, RB_WARN_CATEGORY_DEFAULT_BITS, 0);
- break;
- case 1:
- FEATURE_SET_TO(opt->warn, 1U << RB_WARN_CATEGORY_DEPRECATED, 0);
- break;
- default:
- FEATURE_SET(opt->warn, RB_WARN_CATEGORY_DEFAULT_BITS);
- break;
- }
- }
+ if (!(s = proc_W_option(opt, s, &warning))) break;
goto reswitch;
case 'c':
@@ -1300,19 +1610,10 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt)
case 'e':
if (envopt) goto noenvopt;
- forbid_setid("-e");
- if (!*++s) {
- if (!--argc)
- rb_raise(rb_eRuntimeError, "no code specified for -e");
- s = *++argv;
- }
- if (!opt->e_script) {
- opt->e_script = rb_str_new(0, 0);
- if (opt->script == 0)
- opt->script = "-e";
- }
- rb_str_cat2(opt->e_script, s);
- rb_str_cat2(opt->e_script, "\n");
+ if (!(n = proc_e_option(opt, s, argc, argv))) break;
+ --n;
+ argc -= n;
+ argv += n;
break;
case 'r':
@@ -1364,7 +1665,8 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt)
if (!*++s && (!--argc || !(s = *++argv))) {
rb_raise(rb_eRuntimeError, "missing argument for -E");
}
- goto encoding;
+ proc_encoding_option(opt, s, "-E");
+ break;
case 'U':
set_internal_encoding_once(opt, "UTF-8", 0);
@@ -1372,29 +1674,7 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt)
goto reswitch;
case 'K':
- if (*++s) {
- const char *enc_name = 0;
- switch (*s) {
- case 'E': case 'e':
- enc_name = "EUC-JP";
- break;
- case 'S': case 's':
- enc_name = "Windows-31J";
- break;
- case 'U': case 'u':
- enc_name = "UTF-8";
- break;
- case 'N': case 'n': case 'A': case 'a':
- enc_name = "ASCII-8BIT";
- break;
- }
- if (enc_name) {
- opt->src.enc.name = rb_str_new2(enc_name);
- if (!opt->ext.enc.name)
- opt->ext.enc.name = opt->src.enc.name;
- }
- s++;
- }
+ if (!(s = proc_K_option(opt, s))) break;
goto reswitch;
case 'I':
@@ -1409,23 +1689,7 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt)
case '0':
if (envopt) goto noenvopt;
- {
- size_t numlen;
- int v;
- char c;
-
- v = scan_oct(s, 4, &numlen);
- s += numlen;
- if (v > 0377)
- rb_rs = Qnil;
- else if (v == 0 && numlen >= 2) {
- rb_rs = rb_str_new2("");
- }
- else {
- c = v & 0xff;
- rb_rs = rb_str_new(&c, 1);
- }
- }
+ if (!(s = proc_0_option(opt, s))) break;
goto reswitch;
case '-':
@@ -1435,133 +1699,10 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt)
}
s++;
-# define is_option_end(c, allow_hyphen) \
- (!(c) || ((allow_hyphen) && (c) == '-') || (c) == '=')
-# define check_envopt(name, allow_envopt) \
- (((allow_envopt) || !envopt) ? (void)0 : \
- rb_raise(rb_eRuntimeError, "invalid switch in RUBYOPT: --" name))
-# define need_argument(name, s, needs_arg, next_arg) \
- ((*(s) ? !*++(s) : (next_arg) && (!argc || !((s) = argv[1]) || (--argc, ++argv, 0))) && (needs_arg) ? \
- rb_raise(rb_eRuntimeError, "missing argument for --" name) \
- : (void)0)
-# define is_option_with_arg(name, allow_hyphen, allow_envopt) \
- is_option_with_optarg(name, allow_hyphen, allow_envopt, Qtrue, Qtrue)
-# define is_option_with_optarg(name, allow_hyphen, allow_envopt, needs_arg, next_arg) \
- (strncmp((name), s, n = sizeof(name) - 1) == 0 && is_option_end(s[n], (allow_hyphen)) && \
- (s[n] != '-' || s[n+1]) ? \
- (check_envopt(name, (allow_envopt)), s += n, \
- need_argument(name, s, needs_arg, next_arg), 1) : 0)
-
- if (strcmp("copyright", s) == 0) {
- if (envopt) goto noenvopt_long;
- opt->dump |= DUMP_BIT(copyright);
- }
- else if (is_option_with_optarg("debug", Qtrue, Qtrue, Qfalse, Qfalse)) {
- if (s && *s) {
- ruby_each_words(s, debug_option, &opt->features);
- }
- else {
- ruby_debug = Qtrue;
- ruby_verbose = Qtrue;
- }
- }
- else if (is_option_with_arg("enable", Qtrue, Qtrue)) {
- ruby_each_words(s, enable_option, &opt->features);
- }
- else if (is_option_with_arg("disable", Qtrue, Qtrue)) {
- ruby_each_words(s, disable_option, &opt->features);
- }
- else if (is_option_with_arg("encoding", Qfalse, Qtrue)) {
- char *p;
- encoding:
- do {
-# define set_encoding_part(type) \
- if (!(p = strchr(s, ':'))) { \
- set_##type##_encoding_once(opt, s, 0); \
- break; \
- } \
- else if (p > s) { \
- set_##type##_encoding_once(opt, s, p-s); \
- }
- set_encoding_part(external);
- if (!*(s = ++p)) break;
- set_encoding_part(internal);
- if (!*(s = ++p)) break;
-#if defined ALLOW_DEFAULT_SOURCE_ENCODING && ALLOW_DEFAULT_SOURCE_ENCODING
- set_encoding_part(source);
- if (!*(s = ++p)) break;
-#endif
- rb_raise(rb_eRuntimeError, "extra argument for %s: %s",
- (arg[1] == '-' ? "--encoding" : "-E"), s);
-# undef set_encoding_part
- } while (0);
- }
- else if (is_option_with_arg("internal-encoding", Qfalse, Qtrue)) {
- set_internal_encoding_once(opt, s, 0);
- }
- else if (is_option_with_arg("external-encoding", Qfalse, Qtrue)) {
- set_external_encoding_once(opt, s, 0);
- }
-#if defined ALLOW_DEFAULT_SOURCE_ENCODING && ALLOW_DEFAULT_SOURCE_ENCODING
- else if (is_option_with_arg("source-encoding", Qfalse, Qtrue)) {
- set_source_encoding_once(opt, s, 0);
- }
-#endif
- else if (strcmp("version", s) == 0) {
- if (envopt) goto noenvopt_long;
- opt->dump |= DUMP_BIT(version);
- }
- else if (strcmp("verbose", s) == 0) {
- opt->verbose = 1;
- ruby_verbose = Qtrue;
- }
- else if (strcmp("jit", s) == 0) {
-#if !USE_RJIT
- rb_warn("Ruby was built without JIT support");
-#else
- FEATURE_SET(opt->features, FEATURE_BIT(jit));
-#endif
- }
- else if (is_option_with_optarg("rjit", '-', true, false, false)) {
-#if USE_RJIT
- extern void rb_rjit_setup_options(const char *s, struct rb_rjit_options *rjit_opt);
- FEATURE_SET(opt->features, FEATURE_BIT(rjit));
- rb_rjit_setup_options(s, &opt->rjit);
-#else
- rb_warn("RJIT support is disabled.");
-#endif
- }
- else if (is_option_with_optarg("yjit", '-', true, false, false)) {
-#if USE_YJIT
- FEATURE_SET(opt->features, FEATURE_BIT(yjit));
- setup_yjit_options(s);
-#else
- rb_warn("Ruby was built without YJIT support."
- " You may need to install rustc to build Ruby with YJIT.");
-#endif
- }
- else if (strcmp("yydebug", s) == 0) {
- if (envopt) goto noenvopt_long;
- opt->dump |= DUMP_BIT(yydebug);
- }
- else if (is_option_with_arg("dump", Qfalse, Qfalse)) {
- ruby_each_words(s, dump_option, &opt->dump);
- }
- else if (strcmp("help", s) == 0) {
- if (envopt) goto noenvopt_long;
- opt->dump |= DUMP_BIT(help);
- goto switch_end;
- }
- else if (is_option_with_arg("backtrace-limit", Qfalse, Qfalse)) {
- char *e;
- long n = strtol(s, &e, 10);
- if (errno == ERANGE || n < 0 || *e) rb_raise(rb_eRuntimeError, "wrong limit for backtrace length");
- rb_backtrace_length_limit = n;
- }
- else {
- rb_raise(rb_eRuntimeError,
- "invalid option --%s (-h will show valid options)", s);
- }
+ if (!(n = proc_long_options(opt, s, argc, argv, envopt))) goto switch_end;
+ --n;
+ argc -= n;
+ argv += n;
break;
case '\r':
@@ -1569,11 +1710,9 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt)
break;
default:
- {
- rb_raise(rb_eRuntimeError,
- "invalid option -%c (-h will show valid options)",
- (int)(unsigned char)*s);
- }
+ rb_raise(rb_eRuntimeError,
+ "invalid option -%c (-h will show valid options)",
+ (int)(unsigned char)*s);
goto switch_end;
noenvopt:
@@ -1581,17 +1720,8 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt)
rb_raise(rb_eRuntimeError, "invalid switch in RUBYOPT: -%c", *s);
break;
- noenvopt_long:
- rb_raise(rb_eRuntimeError, "invalid switch in RUBYOPT: --%s", s);
- break;
-
case 0:
break;
-# undef is_option_end
-# undef check_envopt
-# undef need_argument
-# undef is_option_with_arg
-# undef is_option_with_optarg
}
}
@@ -1624,6 +1754,8 @@ Init_extra_exts(void)
static void
ruby_opt_init(ruby_cmdline_options_t *opt)
{
+ rb_warning_category_update(opt->warn.mask, opt->warn.set);
+
if (opt->dump & dump_exit_bits) return;
if (FEATURE_SET_P(opt->features, gems)) {
@@ -1639,7 +1771,17 @@ ruby_opt_init(ruby_cmdline_options_t *opt)
}
}
- rb_warning_category_update(opt->warn.mask, opt->warn.set);
+ /* [Feature #19785] Warning for removed GC environment variable.
+ * Remove this in Ruby 3.4. */
+ if (getenv("RUBY_GC_HEAP_INIT_SLOTS")) {
+ rb_warn_deprecated("The environment variable RUBY_GC_HEAP_INIT_SLOTS",
+ "environment variables RUBY_GC_HEAP_%d_INIT_SLOTS");
+ }
+
+ if (getenv("RUBY_FREE_AT_EXIT")) {
+ rb_warn("Free at exit is experimental and may be unstable");
+ rb_free_at_exit = true;
+ }
#if USE_RJIT
// rb_call_builtin_inits depends on RubyVM::RJIT.enabled?
@@ -1653,7 +1795,12 @@ ruby_opt_init(ruby_cmdline_options_t *opt)
Init_ext(); /* load statically linked extensions before rubygems */
Init_extra_exts();
+
+ GET_VM()->running = 0;
rb_call_builtin_inits();
+ GET_VM()->running = 1;
+ memset(ruby_vm_redefined_flag, 0, sizeof(ruby_vm_redefined_flag));
+
ruby_init_prelude();
// Initialize JITs after prelude because JITing prelude is typically not optimal.
@@ -1663,13 +1810,8 @@ ruby_opt_init(ruby_cmdline_options_t *opt)
rb_rjit_init(&opt->rjit);
#endif
#if USE_YJIT
- if (opt->yjit)
- rb_yjit_init();
+ rb_yjit_init(opt->yjit);
#endif
- // rb_threadptr_root_fiber_setup for the initial thread is called before rb_yjit_enabled_p()
- // or rjit_enabled becomes true, meaning jit_cont_new is skipped for the initial root fiber.
- // Therefore we need to call this again here to set the initial root fiber's jit_cont.
- rb_jit_cont_init(); // must be after rjit_enabled = true and rb_yjit_init()
ruby_set_script_name(opt->script_name);
require_libraries(&opt->req_list);
@@ -1857,12 +1999,254 @@ env_var_truthy(const char *name)
rb_pid_t rb_fork_ruby(int *status);
+static void
+show_help(const char *progname, int help)
+{
+ int tty = isatty(1);
+ int columns = 0;
+ if (help && tty) {
+ const char *pager_env = getenv("RUBY_PAGER");
+ if (!pager_env) pager_env = getenv("PAGER");
+ if (pager_env && *pager_env && isatty(0)) {
+ const char *columns_env = getenv("COLUMNS");
+ if (columns_env) columns = atoi(columns_env);
+ VALUE pager = rb_str_new_cstr(pager_env);
+#ifdef HAVE_WORKING_FORK
+ int fds[2];
+ if (rb_pipe(fds) == 0) {
+ rb_pid_t pid = rb_fork_ruby(NULL);
+ if (pid > 0) {
+ /* exec PAGER with reading from child */
+ dup2(fds[0], 0);
+ }
+ else if (pid == 0) {
+ /* send the help message to the parent PAGER */
+ dup2(fds[1], 1);
+ dup2(fds[1], 2);
+ }
+ close(fds[0]);
+ close(fds[1]);
+ if (pid > 0) {
+ setup_pager_env();
+ rb_f_exec(1, &pager);
+ kill(SIGTERM, pid);
+ rb_waitpid(pid, 0, 0);
+ }
+ }
+#else
+ setup_pager_env();
+ VALUE port = rb_io_popen(pager, rb_str_new_lit("w"), Qnil, Qnil);
+ if (!NIL_P(port)) {
+ int oldout = dup(1);
+ int olderr = dup(2);
+ int fd = RFILE(port)->fptr->fd;
+ tty = tty_enabled();
+ dup2(fd, 1);
+ dup2(fd, 2);
+ usage(progname, 1, tty, columns);
+ fflush(stdout);
+ dup2(oldout, 1);
+ dup2(olderr, 2);
+ rb_io_close(port);
+ return;
+ }
+#endif
+ }
+ }
+ usage(progname, help, tty, columns);
+}
+
+static VALUE
+process_script(ruby_cmdline_options_t *opt)
+{
+ rb_ast_t *ast;
+ VALUE vast;
+ VALUE parser = rb_parser_new();
+ const unsigned int dump = opt->dump;
+
+ if (dump & DUMP_BIT(yydebug)) {
+ rb_parser_set_yydebug(parser, Qtrue);
+ }
+
+ if ((dump & dump_exit_bits) && (dump & DUMP_BIT(opt_error_tolerant))) {
+ rb_parser_error_tolerant(parser);
+ }
+
+ if (opt->e_script) {
+ VALUE progname = rb_progname;
+ rb_parser_set_context(parser, 0, TRUE);
+
+ ruby_opt_init(opt);
+ ruby_set_script_name(progname);
+ rb_parser_set_options(parser, opt->do_print, opt->do_loop,
+ opt->do_line, opt->do_split);
+ vast = rb_parser_compile_string(parser, opt->script, opt->e_script, 1);
+ }
+ else {
+ VALUE f;
+ int xflag = opt->xflag;
+ f = open_load_file(opt->script_name, &xflag);
+ opt->xflag = xflag != 0;
+ rb_parser_set_context(parser, 0, f == rb_stdin);
+ vast = load_file(parser, opt->script_name, f, 1, opt);
+ }
+ ast = rb_ruby_ast_data_get(vast);
+ if (!ast->body.root) {
+ rb_ast_dispose(ast);
+ return Qnil;
+ }
+ return vast;
+}
+
+/**
+ * Call ruby_opt_init to set up the global state based on the command line
+ * options, and then warn if prism is enabled and the experimental warning
+ * category is enabled.
+ */
+static void
+prism_opt_init(ruby_cmdline_options_t *opt)
+{
+ ruby_opt_init(opt);
+
+ if (rb_warning_category_enabled_p(RB_WARN_CATEGORY_EXPERIMENTAL)) {
+ rb_category_warn(
+ RB_WARN_CATEGORY_EXPERIMENTAL,
+ "The compiler based on the Prism parser is currently experimental "
+ "and compatibility with the compiler based on parse.y is not yet "
+ "complete. Please report any issues you find on the `ruby/prism` "
+ "issue tracker."
+ );
+ }
+}
+
+/**
+ * Process the command line options and parse the script into the given result.
+ * Raise an error if the script cannot be parsed.
+ */
+static void
+prism_script(ruby_cmdline_options_t *opt, pm_parse_result_t *result)
+{
+ memset(result, 0, sizeof(pm_parse_result_t));
+
+ pm_options_t *options = &result->options;
+ pm_options_line_set(options, 1);
+
+ if (opt->ext.enc.name != 0) {
+ pm_options_encoding_set(options, StringValueCStr(opt->ext.enc.name));
+ }
+
+ uint8_t command_line = 0;
+ if (opt->do_split) command_line |= PM_OPTIONS_COMMAND_LINE_A;
+ if (opt->do_line) command_line |= PM_OPTIONS_COMMAND_LINE_L;
+ if (opt->do_loop) command_line |= PM_OPTIONS_COMMAND_LINE_N;
+ if (opt->do_print) command_line |= PM_OPTIONS_COMMAND_LINE_P;
+ if (opt->xflag) command_line |= PM_OPTIONS_COMMAND_LINE_X;
+
+ VALUE error;
+ if (strcmp(opt->script, "-") == 0) {
+ pm_options_command_line_set(options, command_line);
+ pm_options_filepath_set(options, "-");
+
+ prism_opt_init(opt);
+ error = pm_parse_stdin(result);
+ }
+ else if (opt->e_script) {
+ command_line |= PM_OPTIONS_COMMAND_LINE_E;
+ pm_options_command_line_set(options, command_line);
+
+ prism_opt_init(opt);
+ error = pm_parse_string(result, opt->e_script, rb_str_new2("-e"));
+ }
+ else {
+ pm_options_command_line_set(options, command_line);
+ error = pm_load_file(result, opt->script_name, true);
+
+ // If reading the file did not error, at that point we load the command
+ // line options. We do it in this order so that if the main script fails
+ // to load, it doesn't require files required by -r.
+ if (NIL_P(error)) {
+ prism_opt_init(opt);
+ error = pm_parse_file(result, opt->script_name);
+ }
+
+ // If we found an __END__ marker, then we're going to define a global
+ // DATA constant that is a file object that can be read to read the
+ // contents after the marker.
+ if (NIL_P(error) && result->parser.data_loc.start != NULL) {
+ int xflag = opt->xflag;
+ VALUE file = open_load_file(opt->script_name, &xflag);
+
+ const pm_parser_t *parser = &result->parser;
+ size_t offset = parser->data_loc.start - parser->start + 7;
+
+ if ((parser->start + offset < parser->end) && parser->start[offset] == '\r') offset++;
+ if ((parser->start + offset < parser->end) && parser->start[offset] == '\n') offset++;
+
+ rb_funcall(file, rb_intern_const("seek"), 2, SIZET2NUM(offset), INT2FIX(SEEK_SET));
+ rb_define_global_const("DATA", file);
+ }
+ }
+
+ if (!NIL_P(error)) {
+ pm_parse_result_free(result);
+ rb_exc_raise(error);
+ }
+}
+
+static VALUE
+prism_dump_tree(pm_parse_result_t *result)
+{
+ pm_buffer_t output_buffer = { 0 };
+
+ pm_prettyprint(&output_buffer, &result->parser, result->node.ast_node);
+ VALUE tree = rb_str_new(output_buffer.value, output_buffer.length);
+ pm_buffer_free(&output_buffer);
+ return tree;
+}
+
+static void
+process_options_global_setup(const ruby_cmdline_options_t *opt, const rb_iseq_t *iseq)
+{
+ if (OPT_BACKTRACE_LENGTH_LIMIT_VALID_P(opt)) {
+ rb_backtrace_length_limit = opt->backtrace_length_limit;
+ }
+
+ if (opt->do_loop) {
+ rb_define_global_function("sub", rb_f_sub, -1);
+ rb_define_global_function("gsub", rb_f_gsub, -1);
+ rb_define_global_function("chop", rb_f_chop, 0);
+ rb_define_global_function("chomp", rb_f_chomp, -1);
+ }
+
+ rb_define_readonly_boolean("$-p", opt->do_print);
+ rb_define_readonly_boolean("$-l", opt->do_line);
+ rb_define_readonly_boolean("$-a", opt->do_split);
+
+ rb_gvar_ractor_local("$-p");
+ rb_gvar_ractor_local("$-l");
+ rb_gvar_ractor_local("$-a");
+
+ if ((rb_e_script = opt->e_script) != 0) {
+ rb_str_freeze(rb_e_script);
+ rb_vm_register_global_object(opt->e_script);
+ }
+
+ rb_execution_context_t *ec = GET_EC();
+ VALUE script = (opt->e_script ? opt->e_script : Qnil);
+ rb_exec_event_hook_script_compiled(ec, iseq, script);
+}
+
static VALUE
process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
{
- rb_ast_t *ast = 0;
- VALUE parser;
- VALUE script_name;
+ VALUE vast = Qnil;
+ struct {
+ rb_ast_t *ast;
+ pm_parse_result_t prism;
+ } result = {0};
+#define dispose_result() \
+ (result.ast ? rb_ast_dispose(result.ast) : pm_parse_result_free(&result.prism))
+
const rb_iseq_t *iseq;
rb_encoding *enc, *lenc;
#if UTF8_PATH
@@ -1877,62 +2261,11 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
const long loaded_before_enc = RARRAY_LEN(vm->loaded_features);
if (opt->dump & (DUMP_BIT(usage)|DUMP_BIT(help))) {
- int tty = isatty(1);
const char *const progname =
(argc > 0 && argv && argv[0] ? argv[0] :
origarg.argc > 0 && origarg.argv && origarg.argv[0] ? origarg.argv[0] :
ruby_engine);
- int columns = 0;
- if ((opt->dump & DUMP_BIT(help)) && tty) {
- const char *pager_env = getenv("RUBY_PAGER");
- if (!pager_env) pager_env = getenv("PAGER");
- if (pager_env && *pager_env && isatty(0)) {
- const char *columns_env = getenv("COLUMNS");
- if (columns_env) columns = atoi(columns_env);
- VALUE pager = rb_str_new_cstr(pager_env);
-#ifdef HAVE_WORKING_FORK
- int fds[2];
- if (rb_pipe(fds) == 0) {
- rb_pid_t pid = rb_fork_ruby(NULL);
- if (pid > 0) {
- /* exec PAGER with reading from child */
- dup2(fds[0], 0);
- }
- else if (pid == 0) {
- /* send the help message to the parent PAGER */
- dup2(fds[1], 1);
- dup2(fds[1], 2);
- }
- close(fds[0]);
- close(fds[1]);
- if (pid > 0) {
- setup_pager_env();
- rb_f_exec(1, &pager);
- kill(SIGTERM, pid);
- rb_waitpid(pid, 0, 0);
- }
- }
-#else
- setup_pager_env();
- VALUE port = rb_io_popen(pager, rb_str_new_lit("w"), Qnil, Qnil);
- if (!NIL_P(port)) {
- int oldout = dup(1);
- int olderr = dup(2);
- int fd = RFILE(port)->fptr->fd;
- tty = tty_enabled();
- dup2(fd, 1);
- dup2(fd, 2);
- usage(progname, 1, tty, columns);
- fflush(stdout);
- dup2(oldout, 1);
- dup2(olderr, 2);
- rb_io_close(port);
- return Qtrue;
- }
-#endif
- }
- }
- usage(progname, (opt->dump & DUMP_BIT(help)), tty, columns);
+ show_help(progname, (opt->dump & DUMP_BIT(help)));
return Qtrue;
}
@@ -1940,22 +2273,7 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
argv += i;
if (FEATURE_SET_P(opt->features, rubyopt) && (s = getenv("RUBYOPT"))) {
- VALUE src_enc_name = opt->src.enc.name;
- VALUE ext_enc_name = opt->ext.enc.name;
- VALUE int_enc_name = opt->intern.enc.name;
- ruby_features_t feat = opt->features;
- ruby_features_t warn = opt->warn;
-
- opt->src.enc.name = opt->ext.enc.name = opt->intern.enc.name = 0;
moreswitches(s, opt, 1);
- if (src_enc_name)
- opt->src.enc.name = src_enc_name;
- if (ext_enc_name)
- opt->ext.enc.name = ext_enc_name;
- if (int_enc_name)
- opt->intern.enc.name = int_enc_name;
- FEATURE_SET_RESTORE(opt->features, feat);
- FEATURE_SET_RESTORE(opt->warn, warn);
}
if (opt->src.enc.name)
@@ -1986,7 +2304,10 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
opt->yjit = true; // set opt->yjit for Init_ruby_description() and calling rb_yjit_init()
}
#endif
+
+ ruby_mn_threads_params();
Init_ruby_description(opt);
+
if (opt->dump & (DUMP_BIT(version) | DUMP_BIT(version_v))) {
ruby_show_version();
if (opt->dump & DUMP_BIT(version)) return Qtrue;
@@ -2033,8 +2354,6 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
#ifdef _WIN32
translit_char_bin(RSTRING_PTR(opt->script_name), '\\', '/');
-#elif defined DOSISH
- translit_char(RSTRING_PTR(opt->script_name), '\\', '/');
#endif
ruby_gc_set_params();
@@ -2044,13 +2363,6 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
lenc = rb_locale_encoding();
rb_enc_associate(rb_progname, lenc);
rb_obj_freeze(rb_progname);
- parser = rb_parser_new();
- if (opt->dump & DUMP_BIT(yydebug)) {
- rb_parser_set_yydebug(parser, Qtrue);
- }
- if (opt->dump & DUMP_BIT(error_tolerant)) {
- rb_parser_error_tolerant(parser);
- }
if (opt->ext.enc.name != 0) {
opt->ext.enc.index = opt_enc_index(opt->ext.enc.name);
}
@@ -2076,7 +2388,6 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
ienc = enc;
#endif
}
- script_name = opt->script_name;
rb_enc_associate(opt->script_name, IF_UTF8_PATH(uenc, lenc));
#if UTF8_PATH
if (uenc != lenc) {
@@ -2141,13 +2452,10 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
#undef SET_COMPILE_OPTION
}
ruby_set_argv(argc, argv);
- process_sflag(&opt->sflag);
+ opt->sflag = process_sflag(opt->sflag);
if (opt->e_script) {
- VALUE progname = rb_progname;
rb_encoding *eenc;
- rb_parser_set_context(parser, 0, TRUE);
-
if (opt->src.enc.index >= 0) {
eenc = rb_enc_from_index(opt->src.enc.index);
}
@@ -2163,22 +2471,19 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
}
#endif
rb_enc_associate(opt->e_script, eenc);
- ruby_opt_init(opt);
- ruby_set_script_name(progname);
- rb_parser_set_options(parser, opt->do_print, opt->do_loop,
- opt->do_line, opt->do_split);
- ast = rb_parser_compile_string(parser, opt->script, opt->e_script, 1);
+ }
+
+ if (!(*rb_ruby_prism_ptr())) {
+ vast = process_script(opt);
+ if (!(result.ast = rb_ruby_ast_data_get(vast))) return Qfalse;
}
else {
- VALUE f;
- f = open_load_file(script_name, &opt->xflag);
- rb_parser_set_context(parser, 0, f == rb_stdin);
- ast = load_file(parser, opt->script_name, f, 1, opt);
+ prism_script(opt, &result.prism);
}
ruby_set_script_name(opt->script_name);
- if (dump & DUMP_BIT(yydebug)) {
- dump &= ~DUMP_BIT(yydebug);
- if (!dump) return Qtrue;
+ if ((dump & DUMP_BIT(yydebug)) && !(dump &= ~DUMP_BIT(yydebug))) {
+ dispose_result();
+ return Qtrue;
}
if (opt->ext.enc.index >= 0) {
@@ -2198,12 +2503,7 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
rb_enc_set_default_internal(Qnil);
rb_stdio_set_default_encoding();
- if (!ast->body.root) {
- rb_ast_dispose(ast);
- return Qfalse;
- }
-
- process_sflag(&opt->sflag);
+ opt->sflag = process_sflag(opt->sflag);
opt->xflag = 0;
if (dump & DUMP_BIT(syntax)) {
@@ -2212,19 +2512,20 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
if (!dump) return Qtrue;
}
- if (opt->do_loop) {
- rb_define_global_function("sub", rb_f_sub, -1);
- rb_define_global_function("gsub", rb_f_gsub, -1);
- rb_define_global_function("chop", rb_f_chop, 0);
- rb_define_global_function("chomp", rb_f_chomp, -1);
- }
-
- if (dump & (DUMP_BIT(parsetree)|DUMP_BIT(parsetree_with_comment))) {
- rb_io_write(rb_stdout, rb_parser_dump_tree(ast->body.root, dump & DUMP_BIT(parsetree_with_comment)));
+ if (dump & DUMP_BIT(parsetree)) {
+ VALUE tree;
+ if (result.ast) {
+ int comment = opt->dump & DUMP_BIT(opt_comment);
+ tree = rb_parser_dump_tree(result.ast->body.root, comment);
+ }
+ else {
+ tree = prism_dump_tree(&result.prism);
+ }
+ rb_io_write(rb_stdout, tree);
rb_io_flush(rb_stdout);
- dump &= ~DUMP_BIT(parsetree)&~DUMP_BIT(parsetree_with_comment);
+ dump &= ~DUMP_BIT(parsetree);
if (!dump) {
- rb_ast_dispose(ast);
+ dispose_result();
return Qtrue;
}
}
@@ -2232,7 +2533,7 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
{
VALUE path = Qnil;
if (!opt->e_script && strcmp(opt->script, "-")) {
- path = rb_realpath_internal(Qnil, script_name, 1);
+ path = rb_realpath_internal(Qnil, opt->script_name, 1);
#if UTF8_PATH
if (uenc != lenc) {
path = str_conv_enc(path, uenc, lenc);
@@ -2244,14 +2545,24 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
}
rb_binding_t *toplevel_binding;
- GetBindingPtr(rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING")),
- toplevel_binding);
+ GetBindingPtr(rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING")), toplevel_binding);
const struct rb_block *base_block = toplevel_context(toplevel_binding);
- iseq = rb_iseq_new_main(&ast->body, opt->script_name, path, vm_block_iseq(base_block), !(dump & DUMP_BIT(insns_without_opt)));
- rb_ast_dispose(ast);
+ const rb_iseq_t *parent = vm_block_iseq(base_block);
+ bool optimize = (opt->dump & DUMP_BIT(opt_optimize)) != 0;
+
+ if (!result.ast) {
+ pm_parse_result_t *pm = &result.prism;
+ iseq = pm_iseq_new_main(&pm->node, opt->script_name, path, parent, optimize);
+ pm_parse_result_free(pm);
+ }
+ else {
+ rb_ast_t *ast = result.ast;
+ iseq = rb_iseq_new_main(vast, opt->script_name, path, parent, optimize);
+ rb_ast_dispose(ast);
+ }
}
- if (dump & (DUMP_BIT(insns) | DUMP_BIT(insns_without_opt))) {
+ if (dump & DUMP_BIT(insns)) {
rb_io_write(rb_stdout, rb_iseq_disasm((const rb_iseq_t *)iseq));
rb_io_flush(rb_stdout);
dump &= ~DUMP_BIT(insns);
@@ -2259,31 +2570,7 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
}
if (opt->dump & dump_exit_bits) return Qtrue;
- rb_define_readonly_boolean("$-p", opt->do_print);
- rb_define_readonly_boolean("$-l", opt->do_line);
- rb_define_readonly_boolean("$-a", opt->do_split);
-
- rb_gvar_ractor_local("$-p");
- rb_gvar_ractor_local("$-l");
- rb_gvar_ractor_local("$-a");
-
- if ((rb_e_script = opt->e_script) != 0) {
- rb_str_freeze(rb_e_script);
- rb_gc_register_mark_object(opt->e_script);
- }
-
- {
- rb_execution_context_t *ec = GET_EC();
-
- if (opt->e_script) {
- /* -e */
- rb_exec_event_hook_script_compiled(ec, iseq, opt->e_script);
- }
- else {
- /* file */
- rb_exec_event_hook_script_compiled(ec, iseq, Qnil);
- }
- }
+ process_options_global_setup(opt, iseq);
return (VALUE)iseq;
}
@@ -2291,7 +2578,7 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
static void
warn_cr_in_shebang(const char *str, long len)
{
- if (str[len-1] == '\n' && str[len-2] == '\r') {
+ if (len > 1 && str[len-1] == '\n' && str[len-2] == '\r') {
rb_warn("shebang line ending with \\r may cause problems");
}
}
@@ -2309,6 +2596,8 @@ struct load_file_arg {
VALUE f;
};
+void rb_set_script_lines_for(VALUE vparser, VALUE path);
+
static VALUE
load_file_internal(VALUE argp_v)
{
@@ -2319,7 +2608,7 @@ load_file_internal(VALUE argp_v)
ruby_cmdline_options_t *opt = argp->opt;
VALUE f = argp->f;
int line_start = 1;
- rb_ast_t *ast = 0;
+ VALUE vast = Qnil;
rb_encoding *enc;
ID set_encoding;
@@ -2411,13 +2700,16 @@ load_file_internal(VALUE argp_v)
}
rb_parser_set_options(parser, opt->do_print, opt->do_loop,
opt->do_line, opt->do_split);
+
+ rb_set_script_lines_for(parser, orig_fname);
+
if (NIL_P(f)) {
f = rb_str_new(0, 0);
rb_enc_associate(f, enc);
- return (VALUE)rb_parser_compile_string_path(parser, orig_fname, f, line_start);
+ return rb_parser_compile_string_path(parser, orig_fname, f, line_start);
}
rb_funcall(f, set_encoding, 2, rb_enc_from_encoding(enc), rb_str_new_cstr("-"));
- ast = rb_parser_compile_file_path(parser, orig_fname, f, line_start);
+ vast = rb_parser_compile_file_path(parser, orig_fname, f, line_start);
rb_funcall(f, set_encoding, 1, rb_parser_encoding(parser));
if (script && rb_parser_end_seen_p(parser)) {
/*
@@ -2435,7 +2727,7 @@ load_file_internal(VALUE argp_v)
rb_define_global_const("DATA", f);
argp->f = Qnil;
}
- return (VALUE)ast;
+ return vast;
}
/* disabling O_NONBLOCK, and returns 0 on success, otherwise errno */
@@ -2544,7 +2836,7 @@ restore_load_file(VALUE arg)
return Qnil;
}
-static rb_ast_t *
+static VALUE
load_file(VALUE parser, VALUE fname, VALUE f, int script, ruby_cmdline_options_t *opt)
{
struct load_file_arg arg;
@@ -2553,7 +2845,7 @@ load_file(VALUE parser, VALUE fname, VALUE f, int script, ruby_cmdline_options_t
arg.script = script;
arg.opt = opt;
arg.f = f;
- return (rb_ast_t *)rb_ensure(load_file_internal, (VALUE)&arg,
+ return rb_ensure(load_file_internal, (VALUE)&arg,
restore_load_file, (VALUE)&arg);
}
@@ -2567,14 +2859,18 @@ rb_load_file(const char *fname)
void *
rb_load_file_str(VALUE fname_v)
{
- return rb_parser_load_file(rb_parser_new(), fname_v);
+ VALUE vast;
+ vast = rb_parser_load_file(rb_parser_new(), fname_v);
+ return (void *)rb_ruby_ast_data_get(vast);
}
-void *
+VALUE
rb_parser_load_file(VALUE parser, VALUE fname_v)
{
ruby_cmdline_options_t opt;
- VALUE f = open_load_file(fname_v, &cmdline_options_init(&opt)->xflag);
+ int xflag = 0;
+ VALUE f = open_load_file(fname_v, &xflag);
+ cmdline_options_init(&opt)->xflag = xflag != 0;
return load_file(parser, fname_v, f, 0, &opt);
}
@@ -2791,19 +3087,26 @@ ruby_process_options(int argc, char **argv)
VALUE iseq;
const char *script_name = (argc > 0 && argv[0]) ? argv[0] : ruby_engine;
+ (*rb_ruby_prism_ptr()) = false;
+
if (!origarg.argv || origarg.argc <= 0) {
origarg.argc = argc;
origarg.argv = argv;
}
set_progname(external_str_new_cstr(script_name)); /* for the time being */
rb_argv0 = rb_str_new4(rb_progname);
- rb_gc_register_mark_object(rb_argv0);
- iseq = process_options(argc, argv, cmdline_options_init(&opt));
+ rb_vm_register_global_object(rb_argv0);
#ifndef HAVE_SETPROCTITLE
ruby_init_setproctitle(argc, argv);
#endif
+ iseq = process_options(argc, argv, cmdline_options_init(&opt));
+
+ if (opt.crash_report && *opt.crash_report) {
+ void ruby_set_crash_report(const char *template);
+ ruby_set_crash_report(opt.crash_report);
+ }
return (void*)(struct RData*)iseq;
}
diff --git a/ruby_parser.c b/ruby_parser.c
new file mode 100644
index 0000000000..4ba0a626c5
--- /dev/null
+++ b/ruby_parser.c
@@ -0,0 +1,1158 @@
+/* This is a wrapper for parse.y */
+
+#include "internal/parse.h"
+#include "internal/re.h"
+#include "internal/ruby_parser.h"
+
+#include "node.h"
+#include "rubyparser.h"
+#include "internal/error.h"
+
+#ifdef UNIVERSAL_PARSER
+
+#include "internal.h"
+#include "internal/array.h"
+#include "internal/bignum.h"
+#include "internal/compile.h"
+#include "internal/complex.h"
+#include "internal/encoding.h"
+#include "internal/gc.h"
+#include "internal/hash.h"
+#include "internal/io.h"
+#include "internal/rational.h"
+#include "internal/re.h"
+#include "internal/string.h"
+#include "internal/symbol.h"
+#include "internal/thread.h"
+
+#include "ruby/ractor.h"
+#include "ruby/ruby.h"
+#include "ruby/util.h"
+#include "internal.h"
+#include "vm_core.h"
+#include "symbol.h"
+
+static int
+is_ascii_string2(VALUE str)
+{
+ return is_ascii_string(str);
+}
+
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 6, 0)
+static VALUE
+syntax_error_append(VALUE exc, VALUE file, int line, int column,
+ void *enc, const char *fmt, va_list args)
+{
+ return rb_syntax_error_append(exc, file, line, column, (rb_encoding *)enc, fmt, args);
+}
+
+static int
+local_defined(ID id, const void *p)
+{
+ return rb_local_defined(id, (const rb_iseq_t *)p);
+}
+
+static int
+dvar_defined(ID id, const void *p)
+{
+ return rb_dvar_defined(id, (const rb_iseq_t *)p);
+}
+
+static int
+is_usascii_enc(void *enc)
+{
+ return rb_is_usascii_enc((rb_encoding *)enc);
+}
+
+static int
+is_local_id2(ID id)
+{
+ return is_local_id(id);
+}
+
+static int
+is_attrset_id2(ID id)
+{
+ return is_attrset_id(id);
+}
+
+static int
+is_notop_id2(ID id)
+{
+ return is_notop_id(id);
+}
+
+static VALUE
+enc_str_new(const char *ptr, long len, void *enc)
+{
+ return rb_enc_str_new(ptr, len, (rb_encoding *)enc);
+}
+
+static int
+enc_isalnum(OnigCodePoint c, void *enc)
+{
+ return rb_enc_isalnum(c, (rb_encoding *)enc);
+}
+
+static int
+enc_precise_mbclen(const char *p, const char *e, void *enc)
+{
+ return rb_enc_precise_mbclen(p, e, (rb_encoding *)enc);
+}
+
+static int
+mbclen_charfound_p(int len)
+{
+ return MBCLEN_CHARFOUND_P(len);
+}
+
+static int
+mbclen_charfound_len(int len)
+{
+ return MBCLEN_CHARFOUND_LEN(len);
+}
+
+static const char *
+enc_name(void *enc)
+{
+ return rb_enc_name((rb_encoding *)enc);
+}
+
+static char *
+enc_prev_char(const char *s, const char *p, const char *e, void *enc)
+{
+ return rb_enc_prev_char(s, p, e, (rb_encoding *)enc);
+}
+
+static void *
+enc_get(VALUE obj)
+{
+ return (void *)rb_enc_get(obj);
+}
+
+static int
+enc_asciicompat(void *enc)
+{
+ return rb_enc_asciicompat((rb_encoding *)enc);
+}
+
+static void *
+utf8_encoding(void)
+{
+ return (void *)rb_utf8_encoding();
+}
+
+static VALUE
+enc_associate(VALUE obj, void *enc)
+{
+ return rb_enc_associate(obj, (rb_encoding *)enc);
+}
+
+static void *
+ascii8bit_encoding(void)
+{
+ return (void *)rb_ascii8bit_encoding();
+}
+
+static int
+enc_codelen(int c, void *enc)
+{
+ return rb_enc_codelen(c, (rb_encoding *)enc);
+}
+
+static int
+enc_mbcput(unsigned int c, void *buf, void *enc)
+{
+ return rb_enc_mbcput(c, buf, (rb_encoding *)enc);
+}
+
+static int
+enc_mbclen(const char *p, const char *e, void *enc)
+{
+ return rb_enc_mbclen(p, e, (rb_encoding *)enc);
+}
+
+static void *
+enc_from_index(int idx)
+{
+ return (void *)rb_enc_from_index(idx);
+}
+
+static int
+enc_isspace(OnigCodePoint c, void *enc)
+{
+ return rb_enc_isspace(c, (rb_encoding *)enc);
+}
+
+static ID
+intern3(const char *name, long len, void *enc)
+{
+ return rb_intern3(name, len, (rb_encoding *)enc);
+}
+
+static void *
+usascii_encoding(void)
+{
+ return (void *)rb_usascii_encoding();
+}
+
+static int
+enc_symname_type(const char *name, long len, void *enc, unsigned int allowed_attrset)
+{
+ return rb_enc_symname_type(name, len, (rb_encoding *)enc, allowed_attrset);
+}
+
+typedef struct {
+ struct parser_params *parser;
+ rb_encoding *enc;
+ NODE *succ_block;
+ const rb_code_location_t *loc;
+} reg_named_capture_assign_t;
+
+static int
+reg_named_capture_assign_iter(const OnigUChar *name, const OnigUChar *name_end,
+ int back_num, int *back_refs, OnigRegex regex, void *arg0)
+{
+ reg_named_capture_assign_t *arg = (reg_named_capture_assign_t*)arg0;
+ struct parser_params* p = arg->parser;
+ rb_encoding *enc = arg->enc;
+ const rb_code_location_t *loc = arg->loc;
+ long len = name_end - name;
+ const char *s = (const char *)name;
+
+ return rb_reg_named_capture_assign_iter_impl(p, s, len, (void *)enc, &arg->succ_block, loc);
+}
+
+static NODE *
+reg_named_capture_assign(struct parser_params* p, VALUE regexp, const rb_code_location_t *loc)
+{
+ reg_named_capture_assign_t arg;
+
+ arg.parser = p;
+ arg.enc = rb_enc_get(regexp);
+ arg.succ_block = 0;
+ arg.loc = loc;
+ onig_foreach_name(RREGEXP_PTR(regexp), reg_named_capture_assign_iter, &arg);
+
+ if (!arg.succ_block) return 0;
+ return RNODE_BLOCK(arg.succ_block)->nd_next;
+}
+
+static int
+rtest(VALUE obj)
+{
+ return (int)RB_TEST(obj);
+}
+
+static int
+nil_p(VALUE obj)
+{
+ return (int)NIL_P(obj);
+}
+
+static VALUE
+syntax_error_new(void)
+{
+ return rb_class_new_instance(0, 0, rb_eSyntaxError);
+}
+
+static void *
+memmove2(void *dest, const void *src, size_t t, size_t n)
+{
+ return memmove(dest, src, rbimpl_size_mul_or_raise(t, n));
+}
+
+static void *
+nonempty_memcpy(void *dest, const void *src, size_t t, size_t n)
+{
+ return ruby_nonempty_memcpy(dest, src, rbimpl_size_mul_or_raise(t, n));
+}
+
+static VALUE
+ruby_verbose2(void)
+{
+ return ruby_verbose;
+}
+
+static int *
+rb_errno_ptr2(void)
+{
+ return rb_errno_ptr();
+}
+
+static void *
+zalloc(size_t elemsiz)
+{
+ return ruby_xcalloc(1, elemsiz);
+}
+
+static void
+gc_guard(VALUE obj)
+{
+ RB_GC_GUARD(obj);
+}
+
+static VALUE
+arg_error(void)
+{
+ return rb_eArgError;
+}
+
+static VALUE
+static_id2sym(ID id)
+{
+ return (((VALUE)(id)<<RUBY_SPECIAL_SHIFT)|SYMBOL_FLAG);
+}
+
+static long
+str_coderange_scan_restartable(const char *s, const char *e, void *enc, int *cr)
+{
+ return rb_str_coderange_scan_restartable(s, e, (rb_encoding *)enc, cr);
+}
+
+static int
+enc_mbminlen(void *enc)
+{
+ return rb_enc_mbminlen((rb_encoding *)enc);
+}
+
+static bool
+enc_isascii(OnigCodePoint c, void *enc)
+{
+ return rb_enc_isascii(c, (rb_encoding *)enc);
+}
+
+static OnigCodePoint
+enc_mbc_to_codepoint(const char *p, const char *e, void *enc)
+{
+ const OnigUChar *up = RBIMPL_CAST((const OnigUChar *)p);
+ const OnigUChar *ue = RBIMPL_CAST((const OnigUChar *)e);
+
+ return ONIGENC_MBC_TO_CODE((rb_encoding *)enc, up, ue);
+}
+
+extern VALUE rb_eArgError;
+
+static const rb_parser_config_t rb_global_parser_config = {
+ .malloc = ruby_xmalloc,
+ .calloc = ruby_xcalloc,
+ .realloc = ruby_xrealloc,
+ .free = ruby_xfree,
+ .alloc_n = ruby_xmalloc2,
+ .alloc = ruby_xmalloc,
+ .realloc_n = ruby_xrealloc2,
+ .zalloc = zalloc,
+ .rb_memmove = memmove2,
+ .nonempty_memcpy = nonempty_memcpy,
+ .xmalloc_mul_add = rb_xmalloc_mul_add,
+
+ .compile_callback = rb_suppress_tracing,
+ .reg_named_capture_assign = reg_named_capture_assign,
+
+ .attr_get = rb_attr_get,
+
+ .ary_new = rb_ary_new,
+ .ary_push = rb_ary_push,
+ .ary_new_from_args = rb_ary_new_from_args,
+ .ary_unshift = rb_ary_unshift,
+
+ .make_temporary_id = rb_make_temporary_id,
+ .is_local_id = is_local_id2,
+ .is_attrset_id = is_attrset_id2,
+ .is_global_name_punct = is_global_name_punct,
+ .id_type = id_type,
+ .id_attrset = rb_id_attrset,
+ .intern = rb_intern,
+ .intern2 = rb_intern2,
+ .intern3 = intern3,
+ .intern_str = rb_intern_str,
+ .is_notop_id = is_notop_id2,
+ .enc_symname_type = enc_symname_type,
+ .id2name = rb_id2name,
+ .id2str = rb_id2str,
+ .id2sym = rb_id2sym,
+ .sym2id = rb_sym2id,
+
+ .str_catf = rb_str_catf,
+ .str_cat_cstr = rb_str_cat_cstr,
+ .str_modify = rb_str_modify,
+ .str_set_len = rb_str_set_len,
+ .str_cat = rb_str_cat,
+ .str_resize = rb_str_resize,
+ .str_new = rb_str_new,
+ .str_new_cstr = rb_str_new_cstr,
+ .str_to_interned_str = rb_str_to_interned_str,
+ .is_ascii_string = is_ascii_string2,
+ .enc_str_new = enc_str_new,
+ .str_vcatf = rb_str_vcatf,
+ .string_value_cstr = rb_string_value_cstr,
+ .rb_sprintf = rb_sprintf,
+ .rstring_ptr = RSTRING_PTR,
+ .rstring_end = RSTRING_END,
+ .rstring_len = RSTRING_LEN,
+ .obj_as_string = rb_obj_as_string,
+
+ .int2num = rb_int2num_inline,
+
+ .stderr_tty_p = rb_stderr_tty_p,
+ .write_error_str = rb_write_error_str,
+ .io_write = rb_io_write,
+ .io_flush = rb_io_flush,
+ .io_puts = rb_io_puts,
+
+ .debug_output_stdout = rb_ractor_stdout,
+ .debug_output_stderr = rb_ractor_stderr,
+
+ .is_usascii_enc = is_usascii_enc,
+ .enc_isalnum = enc_isalnum,
+ .enc_precise_mbclen = enc_precise_mbclen,
+ .mbclen_charfound_p = mbclen_charfound_p,
+ .mbclen_charfound_len = mbclen_charfound_len,
+ .enc_name = enc_name,
+ .enc_prev_char = enc_prev_char,
+ .enc_get = enc_get,
+ .enc_asciicompat = enc_asciicompat,
+ .utf8_encoding = utf8_encoding,
+ .enc_associate = enc_associate,
+ .ascii8bit_encoding = ascii8bit_encoding,
+ .enc_codelen = enc_codelen,
+ .enc_mbcput = enc_mbcput,
+ .enc_mbclen = enc_mbclen,
+ .enc_find_index = rb_enc_find_index,
+ .enc_from_index = enc_from_index,
+ .enc_isspace = enc_isspace,
+ .enc_coderange_7bit = ENC_CODERANGE_7BIT,
+ .enc_coderange_unknown = ENC_CODERANGE_UNKNOWN,
+ .usascii_encoding = usascii_encoding,
+ .enc_coderange_broken = ENC_CODERANGE_BROKEN,
+ .enc_mbminlen = enc_mbminlen,
+ .enc_isascii = enc_isascii,
+ .enc_mbc_to_codepoint = enc_mbc_to_codepoint,
+
+ .local_defined = local_defined,
+ .dvar_defined = dvar_defined,
+
+ .syntax_error_append = syntax_error_append,
+ .raise = rb_raise,
+ .syntax_error_new = syntax_error_new,
+
+ .errinfo = rb_errinfo,
+ .set_errinfo = rb_set_errinfo,
+ .exc_raise = rb_exc_raise,
+ .make_exception = rb_make_exception,
+
+ .sized_xfree = ruby_sized_xfree,
+ .sized_realloc_n = ruby_sized_realloc_n,
+ .gc_guard = gc_guard,
+ .gc_mark = rb_gc_mark,
+
+ .reg_compile = rb_reg_compile,
+ .reg_check_preprocess = rb_reg_check_preprocess,
+ .memcicmp = rb_memcicmp,
+
+ .compile_warn = rb_compile_warn,
+ .compile_warning = rb_compile_warning,
+ .bug = rb_bug,
+ .fatal = rb_fatal,
+ .verbose = ruby_verbose2,
+ .errno_ptr = rb_errno_ptr2,
+
+ .make_backtrace = rb_make_backtrace,
+
+ .scan_hex = ruby_scan_hex,
+ .scan_oct = ruby_scan_oct,
+ .scan_digits = ruby_scan_digits,
+ .strtod = ruby_strtod,
+
+ .rtest = rtest,
+ .nil_p = nil_p,
+ .qnil = Qnil,
+ .qfalse = Qfalse,
+ .eArgError = arg_error,
+ .long2int = rb_long2int,
+
+ /* For Ripper */
+ .static_id2sym = static_id2sym,
+ .str_coderange_scan_restartable = str_coderange_scan_restartable,
+};
+#endif
+
+enum lex_type {
+ lex_type_str,
+ lex_type_io,
+ lex_type_array,
+ lex_type_generic,
+};
+
+struct ruby_parser {
+ rb_parser_t *parser_params;
+ enum lex_type type;
+ union {
+ struct lex_pointer_string lex_str;
+ struct {
+ VALUE file;
+ } lex_io;
+ struct {
+ VALUE ary;
+ } lex_array;
+ } data;
+};
+
+static void
+parser_mark(void *ptr)
+{
+ struct ruby_parser *parser = (struct ruby_parser*)ptr;
+ rb_ruby_parser_mark(parser->parser_params);
+
+ switch (parser->type) {
+ case lex_type_str:
+ rb_gc_mark(parser->data.lex_str.str);
+ break;
+ case lex_type_io:
+ rb_gc_mark(parser->data.lex_io.file);
+ break;
+ case lex_type_array:
+ rb_gc_mark(parser->data.lex_array.ary);
+ break;
+ case lex_type_generic:
+ /* noop. Caller of rb_parser_compile_generic should mark the objects. */
+ break;
+ }
+}
+
+static void
+parser_free(void *ptr)
+{
+ struct ruby_parser *parser = (struct ruby_parser*)ptr;
+ rb_ruby_parser_free(parser->parser_params);
+ xfree(parser);
+}
+
+static size_t
+parser_memsize(const void *ptr)
+{
+ struct ruby_parser *parser = (struct ruby_parser*)ptr;
+ return rb_ruby_parser_memsize(parser->parser_params);
+}
+
+static const rb_data_type_t ruby_parser_data_type = {
+ "parser",
+ {
+ parser_mark,
+ parser_free,
+ parser_memsize,
+ },
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+};
+
+#ifdef UNIVERSAL_PARSER
+const rb_parser_config_t *
+rb_ruby_parser_config(void)
+{
+ return &rb_global_parser_config;
+}
+
+rb_parser_t *
+rb_parser_params_new(void)
+{
+ return rb_ruby_parser_new(&rb_global_parser_config);
+}
+#else
+rb_parser_t *
+rb_parser_params_new(void)
+{
+ return rb_ruby_parser_new();
+}
+#endif /* UNIVERSAL_PARSER */
+
+VALUE
+rb_parser_new(void)
+{
+ struct ruby_parser *parser;
+ rb_parser_t *parser_params;
+
+ /*
+ * Create parser_params ahead of vparser because
+ * rb_ruby_parser_new can run GC so if create vparser
+ * first, parser_mark tries to mark not initialized parser_params.
+ */
+ parser_params = rb_parser_params_new();
+ VALUE vparser = TypedData_Make_Struct(0, struct ruby_parser,
+ &ruby_parser_data_type, parser);
+ parser->parser_params = parser_params;
+
+ return vparser;
+}
+
+void
+rb_parser_set_options(VALUE vparser, int print, int loop, int chomp, int split)
+{
+ struct ruby_parser *parser;
+
+ TypedData_Get_Struct(vparser, struct ruby_parser, &ruby_parser_data_type, parser);
+ rb_ruby_parser_set_options(parser->parser_params, print, loop, chomp, split);
+}
+
+VALUE
+rb_parser_set_context(VALUE vparser, const struct rb_iseq_struct *base, int main)
+{
+ struct ruby_parser *parser;
+
+ TypedData_Get_Struct(vparser, struct ruby_parser, &ruby_parser_data_type, parser);
+ rb_ruby_parser_set_context(parser->parser_params, base, main);
+ return vparser;
+}
+
+void
+rb_parser_set_script_lines(VALUE vparser)
+{
+ struct ruby_parser *parser;
+
+ TypedData_Get_Struct(vparser, struct ruby_parser, &ruby_parser_data_type, parser);
+ rb_ruby_parser_set_script_lines(parser->parser_params);
+}
+
+void
+rb_parser_error_tolerant(VALUE vparser)
+{
+ struct ruby_parser *parser;
+
+ TypedData_Get_Struct(vparser, struct ruby_parser, &ruby_parser_data_type, parser);
+ rb_ruby_parser_error_tolerant(parser->parser_params);
+}
+
+void
+rb_parser_keep_tokens(VALUE vparser)
+{
+ struct ruby_parser *parser;
+
+ TypedData_Get_Struct(vparser, struct ruby_parser, &ruby_parser_data_type, parser);
+ rb_ruby_parser_keep_tokens(parser->parser_params);
+}
+
+VALUE
+rb_parser_lex_get_str(struct lex_pointer_string *ptr_str)
+{
+ char *beg, *end, *start;
+ long len;
+ VALUE s = ptr_str->str;
+
+ beg = RSTRING_PTR(s);
+ len = RSTRING_LEN(s);
+ start = beg;
+ if (ptr_str->ptr) {
+ if (len == ptr_str->ptr) return Qnil;
+ beg += ptr_str->ptr;
+ len -= ptr_str->ptr;
+ }
+ end = memchr(beg, '\n', len);
+ if (end) len = ++end - beg;
+ ptr_str->ptr += len;
+ return rb_str_subseq(s, beg - start, len);
+}
+
+static VALUE
+lex_get_str(struct parser_params *p, rb_parser_input_data input, int line_count)
+{
+ return rb_parser_lex_get_str((struct lex_pointer_string *)input);
+}
+
+static void parser_aset_script_lines_for(VALUE path, rb_parser_ary_t *lines);
+
+static rb_ast_t*
+parser_compile(rb_parser_t *p, rb_parser_lex_gets_func *gets, VALUE fname, rb_parser_input_data input, int line)
+{
+ rb_ast_t *ast;
+ const char *ptr = 0;
+ long len = 0;
+ rb_encoding *enc = 0;
+
+ if (!NIL_P(fname)) {
+ StringValueCStr(fname);
+ ptr = RSTRING_PTR(fname);
+ len = RSTRING_LEN(fname);
+ enc = rb_enc_get(fname);
+ }
+
+ ast = rb_parser_compile(p, gets, ptr, len, enc, input, line);
+ parser_aset_script_lines_for(fname, ast->body.script_lines);
+ return ast;
+}
+
+static rb_ast_t*
+parser_compile_string0(struct ruby_parser *parser, VALUE fname, VALUE s, int line)
+{
+ VALUE str = rb_str_new_frozen(s);
+
+ parser->type = lex_type_str;
+ parser->data.lex_str.str = str;
+ parser->data.lex_str.ptr = 0;
+
+ return parser_compile(parser->parser_params, lex_get_str, fname, (rb_parser_input_data)&parser->data, line);
+}
+
+static rb_encoding *
+must_be_ascii_compatible(VALUE s)
+{
+ rb_encoding *enc = rb_enc_get(s);
+ if (!rb_enc_asciicompat(enc)) {
+ rb_raise(rb_eArgError, "invalid source encoding");
+ }
+ return enc;
+}
+
+static rb_ast_t*
+parser_compile_string_path(struct ruby_parser *parser, VALUE f, VALUE s, int line)
+{
+ must_be_ascii_compatible(s);
+ return parser_compile_string0(parser, f, s, line);
+}
+
+static rb_ast_t*
+parser_compile_string(struct ruby_parser *parser, const char *f, VALUE s, int line)
+{
+ return parser_compile_string_path(parser, rb_filesystem_str_new_cstr(f), s, line);
+}
+
+VALUE rb_io_gets_internal(VALUE io);
+
+static VALUE
+lex_io_gets(struct parser_params *p, rb_parser_input_data input, int line_count)
+{
+ VALUE io = (VALUE)input;
+
+ return rb_io_gets_internal(io);
+}
+
+static VALUE
+lex_gets_array(struct parser_params *p, rb_parser_input_data data, int index)
+{
+ VALUE array = (VALUE)data;
+ VALUE str = rb_ary_entry(array, index);
+ if (!NIL_P(str)) {
+ StringValue(str);
+ if (!rb_enc_asciicompat(rb_enc_get(str))) {
+ rb_raise(rb_eArgError, "invalid source encoding");
+ }
+ }
+ return str;
+}
+
+static rb_ast_t*
+parser_compile_file_path(struct ruby_parser *parser, VALUE fname, VALUE file, int start)
+{
+ parser->type = lex_type_io;
+ parser->data.lex_io.file = file;
+
+ return parser_compile(parser->parser_params, lex_io_gets, fname, (rb_parser_input_data)file, start);
+}
+
+static rb_ast_t*
+parser_compile_array(struct ruby_parser *parser, VALUE fname, VALUE array, int start)
+{
+ parser->type = lex_type_array;
+ parser->data.lex_array.ary = array;
+
+ return parser_compile(parser->parser_params, lex_gets_array, fname, (rb_parser_input_data)array, start);
+}
+
+static rb_ast_t*
+parser_compile_generic(struct ruby_parser *parser, rb_parser_lex_gets_func *lex_gets, VALUE fname, VALUE input, int start)
+{
+ parser->type = lex_type_generic;
+
+ return parser_compile(parser->parser_params, lex_gets, fname, (rb_parser_input_data)input, start);
+}
+
+static void
+ast_free(void *ptr)
+{
+ rb_ast_t *ast = (rb_ast_t *)ptr;
+ rb_ast_free(ast);
+}
+
+static const rb_data_type_t ast_data_type = {
+ "AST",
+ {
+ NULL,
+ ast_free,
+ NULL, // No dsize() because this object does not appear in ObjectSpace.
+ },
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+};
+
+static VALUE
+ast_alloc(rb_ast_t *ast)
+{
+ VALUE vast = TypedData_Wrap_Struct(0, &ast_data_type, ast);
+#ifdef UNIVERSAL_PARSER
+ ast = (rb_ast_t *)DATA_PTR(vast);
+ ast->config = &rb_global_parser_config;
+#endif
+ return vast;
+}
+
+VALUE
+rb_parser_compile_file_path(VALUE vparser, VALUE fname, VALUE file, int start)
+{
+ struct ruby_parser *parser;
+ TypedData_Get_Struct(vparser, struct ruby_parser, &ruby_parser_data_type, parser);
+
+ VALUE vast = ast_alloc(parser_compile_file_path(parser, fname, file, start));
+ RB_GC_GUARD(vparser);
+
+ return vast;
+}
+
+VALUE
+rb_parser_compile_array(VALUE vparser, VALUE fname, VALUE array, int start)
+{
+ struct ruby_parser *parser;
+ TypedData_Get_Struct(vparser, struct ruby_parser, &ruby_parser_data_type, parser);
+
+ VALUE vast = ast_alloc(parser_compile_array(parser, fname, array, start));
+ RB_GC_GUARD(vparser);
+
+ return vast;
+}
+
+VALUE
+rb_parser_compile_generic(VALUE vparser, rb_parser_lex_gets_func *lex_gets, VALUE fname, VALUE input, int start)
+{
+ struct ruby_parser *parser;
+ TypedData_Get_Struct(vparser, struct ruby_parser, &ruby_parser_data_type, parser);
+
+ VALUE vast = ast_alloc(parser_compile_generic(parser, lex_gets, fname, input, start));
+ RB_GC_GUARD(vparser);
+
+ return vast;
+}
+
+VALUE
+rb_parser_compile_string(VALUE vparser, const char *f, VALUE s, int line)
+{
+ struct ruby_parser *parser;
+ TypedData_Get_Struct(vparser, struct ruby_parser, &ruby_parser_data_type, parser);
+
+ VALUE vast = ast_alloc(parser_compile_string(parser, f, s, line));
+ RB_GC_GUARD(vparser);
+
+ return vast;
+}
+
+VALUE
+rb_parser_compile_string_path(VALUE vparser, VALUE f, VALUE s, int line)
+{
+ struct ruby_parser *parser;
+ TypedData_Get_Struct(vparser, struct ruby_parser, &ruby_parser_data_type, parser);
+
+ VALUE vast = ast_alloc(parser_compile_string_path(parser, f, s, line));
+ RB_GC_GUARD(vparser);
+
+ return vast;
+}
+
+VALUE
+rb_parser_encoding(VALUE vparser)
+{
+ struct ruby_parser *parser;
+
+ TypedData_Get_Struct(vparser, struct ruby_parser, &ruby_parser_data_type, parser);
+ return rb_enc_from_encoding(rb_ruby_parser_encoding(parser->parser_params));
+}
+
+VALUE
+rb_parser_end_seen_p(VALUE vparser)
+{
+ struct ruby_parser *parser;
+
+ TypedData_Get_Struct(vparser, struct ruby_parser, &ruby_parser_data_type, parser);
+ return RBOOL(rb_ruby_parser_end_seen_p(parser->parser_params));
+}
+
+VALUE
+rb_parser_set_yydebug(VALUE vparser, VALUE flag)
+{
+ struct ruby_parser *parser;
+
+ TypedData_Get_Struct(vparser, struct ruby_parser, &ruby_parser_data_type, parser);
+ rb_ruby_parser_set_yydebug(parser->parser_params, RTEST(flag));
+ return flag;
+}
+
+void
+rb_set_script_lines_for(VALUE vparser, VALUE path)
+{
+ struct ruby_parser *parser;
+ VALUE hash;
+ ID script_lines;
+ CONST_ID(script_lines, "SCRIPT_LINES__");
+ if (!rb_const_defined_at(rb_cObject, script_lines)) return;
+ hash = rb_const_get_at(rb_cObject, script_lines);
+ if (RB_TYPE_P(hash, T_HASH)) {
+ rb_hash_aset(hash, path, Qtrue);
+ TypedData_Get_Struct(vparser, struct ruby_parser, &ruby_parser_data_type, parser);
+ rb_ruby_parser_set_script_lines(parser->parser_params);
+ }
+}
+
+VALUE
+rb_parser_build_script_lines_from(rb_parser_ary_t *lines)
+{
+ int i;
+ if (!lines) return Qnil;
+ if (lines->data_type != PARSER_ARY_DATA_SCRIPT_LINE) {
+ rb_bug("unexpected rb_parser_ary_data_type (%d) for script lines", lines->data_type);
+ }
+ VALUE script_lines = rb_ary_new_capa(lines->len);
+ for (i = 0; i < lines->len; i++) {
+ rb_parser_string_t *str = (rb_parser_string_t *)lines->data[i];
+ rb_ary_push(script_lines, rb_enc_str_new(str->ptr, str->len, str->enc));
+ }
+ return script_lines;
+}
+
+VALUE
+rb_str_new_parser_string(rb_parser_string_t *str)
+{
+ VALUE string = rb_enc_interned_str(str->ptr, str->len, str->enc);
+ rb_enc_str_coderange(string);
+ return string;
+}
+
+VALUE
+rb_str_new_mutable_parser_string(rb_parser_string_t *str)
+{
+ return rb_enc_str_new(str->ptr, str->len, str->enc);
+}
+
+static VALUE
+negative_numeric(VALUE val)
+{
+ if (FIXNUM_P(val)) {
+ return LONG2FIX(-FIX2LONG(val));
+ }
+ if (SPECIAL_CONST_P(val)) {
+#if USE_FLONUM
+ if (FLONUM_P(val)) {
+ return DBL2NUM(-RFLOAT_VALUE(val));
+ }
+#endif
+ goto unknown;
+ }
+ switch (BUILTIN_TYPE(val)) {
+ case T_BIGNUM:
+ BIGNUM_NEGATE(val);
+ val = rb_big_norm(val);
+ break;
+ case T_RATIONAL:
+ RATIONAL_SET_NUM(val, negative_numeric(RRATIONAL(val)->num));
+ break;
+ case T_COMPLEX:
+ RCOMPLEX_SET_REAL(val, negative_numeric(RCOMPLEX(val)->real));
+ RCOMPLEX_SET_IMAG(val, negative_numeric(RCOMPLEX(val)->imag));
+ break;
+ case T_FLOAT:
+ val = DBL2NUM(-RFLOAT_VALUE(val));
+ break;
+ unknown:
+ default:
+ rb_bug("unknown literal type (%s) passed to negative_numeric",
+ rb_builtin_class_name(val));
+ break;
+ }
+ return val;
+}
+
+static VALUE
+integer_value(const char *val, int base)
+{
+ return rb_cstr_to_inum(val, base, FALSE);
+}
+
+static VALUE
+rational_value(const char *node_val, int base, int seen_point)
+{
+ VALUE lit;
+ char* val = strdup(node_val);
+ if (seen_point > 0) {
+ int len = (int)(strlen(val));
+ char *point = &val[seen_point];
+ size_t fraclen = len-seen_point-1;
+ memmove(point, point+1, fraclen+1);
+
+ lit = rb_rational_new(integer_value(val, base), rb_int_positive_pow(10, fraclen));
+ }
+ else {
+ lit = rb_rational_raw1(integer_value(val, base));
+ }
+
+ free(val);
+
+ return lit;
+}
+
+VALUE
+rb_node_integer_literal_val(const NODE *n)
+{
+ const rb_node_integer_t *node = RNODE_INTEGER(n);
+ VALUE val = integer_value(node->val, node->base);
+ if (node->minus) {
+ val = negative_numeric(val);
+ }
+ return val;
+}
+
+VALUE
+rb_node_float_literal_val(const NODE *n)
+{
+ const rb_node_float_t *node = RNODE_FLOAT(n);
+ double d = strtod(node->val, 0);
+ if (node->minus) {
+ d = -d;
+ }
+ VALUE val = DBL2NUM(d);
+ return val;
+}
+
+VALUE
+rb_node_rational_literal_val(const NODE *n)
+{
+ VALUE lit;
+ const rb_node_rational_t *node = RNODE_RATIONAL(n);
+
+ lit = rational_value(node->val, node->base, node->seen_point);
+
+ if (node->minus) {
+ lit = negative_numeric(lit);
+ }
+
+ return lit;
+}
+
+VALUE
+rb_node_imaginary_literal_val(const NODE *n)
+{
+ VALUE lit;
+ const rb_node_imaginary_t *node = RNODE_IMAGINARY(n);
+
+ enum rb_numeric_type type = node->type;
+
+ switch (type) {
+ case integer_literal:
+ lit = integer_value(node->val, node->base);
+ break;
+ case float_literal:{
+ double d = strtod(node->val, 0);
+ lit = DBL2NUM(d);
+ break;
+ }
+ case rational_literal:
+ lit = rational_value(node->val, node->base, node->seen_point);
+ break;
+ default:
+ rb_bug("unreachable");
+ }
+
+ lit = rb_complex_raw(INT2FIX(0), lit);
+
+ if (node->minus) {
+ lit = negative_numeric(lit);
+ }
+ return lit;
+}
+
+VALUE
+rb_node_str_string_val(const NODE *node)
+{
+ rb_parser_string_t *str = RNODE_STR(node)->string;
+ return rb_str_new_parser_string(str);
+}
+
+VALUE
+rb_node_sym_string_val(const NODE *node)
+{
+ rb_parser_string_t *str = RNODE_SYM(node)->string;
+ return ID2SYM(rb_intern3(str->ptr, str->len, str->enc));
+}
+
+VALUE
+rb_node_dstr_string_val(const NODE *node)
+{
+ rb_parser_string_t *str = RNODE_DSTR(node)->string;
+ return str ? rb_str_new_parser_string(str) : Qnil;
+}
+
+VALUE
+rb_node_dregx_string_val(const NODE *node)
+{
+ rb_parser_string_t *str = RNODE_DREGX(node)->string;
+ return rb_str_new_parser_string(str);
+}
+
+VALUE
+rb_node_regx_string_val(const NODE *node)
+{
+ rb_node_regx_t *node_reg = RNODE_REGX(node);
+ rb_parser_string_t *string = node_reg->string;
+ VALUE str = rb_enc_str_new(string->ptr, string->len, string->enc);
+
+ return rb_reg_compile(str, node_reg->options, NULL, 0);
+}
+
+VALUE
+rb_node_line_lineno_val(const NODE *node)
+{
+ return INT2FIX(node->nd_loc.beg_pos.lineno);
+}
+
+VALUE
+rb_node_file_path_val(const NODE *node)
+{
+ return rb_str_new_parser_string(RNODE_FILE(node)->path);
+}
+
+VALUE
+rb_node_encoding_val(const NODE *node)
+{
+ return rb_enc_from_encoding(RNODE_ENCODING(node)->enc);
+}
+
+static void
+parser_aset_script_lines_for(VALUE path, rb_parser_ary_t *lines)
+{
+ VALUE hash, script_lines;
+ ID script_lines_id;
+ if (NIL_P(path) || !lines) return;
+ CONST_ID(script_lines_id, "SCRIPT_LINES__");
+ if (!rb_const_defined_at(rb_cObject, script_lines_id)) return;
+ hash = rb_const_get_at(rb_cObject, script_lines_id);
+ if (!RB_TYPE_P(hash, T_HASH)) return;
+ if (rb_hash_lookup(hash, path) == Qnil) return;
+ script_lines = rb_parser_build_script_lines_from(lines);
+ rb_hash_aset(hash, path, script_lines);
+}
+
+VALUE
+rb_ruby_ast_new(const NODE *const root)
+{
+ rb_ast_t *ast = ruby_xcalloc(1, sizeof(rb_ast_t));
+ VALUE vast = ast_alloc(ast);
+ ast->body = (rb_ast_body_t){
+ .root = root,
+ .frozen_string_literal = -1,
+ .coverage_enabled = -1,
+ .script_lines = NULL,
+ .line_count = 0,
+ };
+ return vast;
+}
+
+rb_ast_t *
+rb_ruby_ast_data_get(VALUE vast)
+{
+ rb_ast_t *ast;
+ if (NIL_P(vast)) return NULL;
+ TypedData_Get_Struct(vast, rb_ast_t, &ast_data_type, ast);
+ return ast;
+}
diff --git a/rubyparser.h b/rubyparser.h
new file mode 100644
index 0000000000..cb90b847b6
--- /dev/null
+++ b/rubyparser.h
@@ -0,0 +1,1430 @@
+#ifndef RUBY_RUBYPARSER_H
+#define RUBY_RUBYPARSER_H 1
+/*
+ * This is a header file for librubyparser interface
+ */
+
+#include <stdarg.h> /* for va_list */
+#include <assert.h>
+
+#ifdef UNIVERSAL_PARSER
+
+#define rb_encoding void
+#define OnigCodePoint unsigned int
+#include "parser_st.h"
+#ifndef RUBY_RUBY_H
+#include "parser_value.h"
+#endif
+
+#else
+
+#include "ruby/encoding.h"
+
+#endif
+
+#ifndef FLEX_ARY_LEN
+/* From internal/compilers.h */
+/* A macro for defining a flexible array, like: VALUE ary[FLEX_ARY_LEN]; */
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+# define FLEX_ARY_LEN /* VALUE ary[]; */
+#elif defined(__GNUC__) && !defined(__STRICT_ANSI__)
+# define FLEX_ARY_LEN 0 /* VALUE ary[0]; */
+#else
+# define FLEX_ARY_LEN 1 /* VALUE ary[1]; */
+#endif
+#endif
+
+#if defined(__GNUC__)
+# if defined(__MINGW_PRINTF_FORMAT)
+# define RUBYPARSER_ATTRIBUTE_FORMAT(string_index, argument_index) __attribute__((format(__MINGW_PRINTF_FORMAT, string_index, argument_index)))
+# else
+# define RUBYPARSER_ATTRIBUTE_FORMAT(string_index, argument_index) __attribute__((format(printf, string_index, argument_index)))
+# endif
+#elif defined(__clang__)
+# define RUBYPARSER_ATTRIBUTE_FORMAT(string_index, argument_index) __attribute__((__format__(__printf__, string_index, argument_index)))
+#else
+# define RUBYPARSER_ATTRIBUTE_FORMAT(string_index, argument_index)
+#endif
+
+/*
+ * Parser String
+ */
+enum rb_parser_string_coderange_type {
+ /** The object's coderange is unclear yet. */
+ RB_PARSER_ENC_CODERANGE_UNKNOWN = 0,
+ RB_PARSER_ENC_CODERANGE_7BIT = 1,
+ RB_PARSER_ENC_CODERANGE_VALID = 2,
+ RB_PARSER_ENC_CODERANGE_BROKEN = 3
+};
+
+typedef struct rb_parser_string {
+ enum rb_parser_string_coderange_type coderange;
+ rb_encoding *enc;
+ /* Length of the string, not including terminating NUL character. */
+ long len;
+ /* Pointer to the contents of the string. */
+ char *ptr;
+} rb_parser_string_t;
+
+enum rb_parser_shareability {
+ rb_parser_shareable_none,
+ rb_parser_shareable_literal,
+ rb_parser_shareable_copy,
+ rb_parser_shareable_everything,
+};
+
+typedef void* rb_parser_input_data;
+
+/*
+ * AST Node
+ */
+enum node_type {
+ NODE_SCOPE,
+ NODE_BLOCK,
+ NODE_IF,
+ NODE_UNLESS,
+ NODE_CASE,
+ NODE_CASE2,
+ NODE_CASE3,
+ NODE_WHEN,
+ NODE_IN,
+ NODE_WHILE,
+ NODE_UNTIL,
+ NODE_ITER,
+ NODE_FOR,
+ NODE_FOR_MASGN,
+ NODE_BREAK,
+ NODE_NEXT,
+ NODE_REDO,
+ NODE_RETRY,
+ NODE_BEGIN,
+ NODE_RESCUE,
+ NODE_RESBODY,
+ NODE_ENSURE,
+ NODE_AND,
+ NODE_OR,
+ NODE_MASGN,
+ NODE_LASGN,
+ NODE_DASGN,
+ NODE_GASGN,
+ NODE_IASGN,
+ NODE_CDECL,
+ NODE_CVASGN,
+ NODE_OP_ASGN1,
+ NODE_OP_ASGN2,
+ NODE_OP_ASGN_AND,
+ NODE_OP_ASGN_OR,
+ NODE_OP_CDECL,
+ NODE_CALL,
+ NODE_OPCALL,
+ NODE_FCALL,
+ NODE_VCALL,
+ NODE_QCALL,
+ NODE_SUPER,
+ NODE_ZSUPER,
+ NODE_LIST,
+ NODE_ZLIST,
+ NODE_HASH,
+ NODE_RETURN,
+ NODE_YIELD,
+ NODE_LVAR,
+ NODE_DVAR,
+ NODE_GVAR,
+ NODE_IVAR,
+ NODE_CONST,
+ NODE_CVAR,
+ NODE_NTH_REF,
+ NODE_BACK_REF,
+ NODE_MATCH,
+ NODE_MATCH2,
+ NODE_MATCH3,
+ NODE_INTEGER,
+ NODE_FLOAT,
+ NODE_RATIONAL,
+ NODE_IMAGINARY,
+ NODE_STR,
+ NODE_DSTR,
+ NODE_XSTR,
+ NODE_DXSTR,
+ NODE_EVSTR,
+ NODE_REGX,
+ NODE_DREGX,
+ NODE_ONCE,
+ NODE_ARGS,
+ NODE_ARGS_AUX,
+ NODE_OPT_ARG,
+ NODE_KW_ARG,
+ NODE_POSTARG,
+ NODE_ARGSCAT,
+ NODE_ARGSPUSH,
+ NODE_SPLAT,
+ NODE_BLOCK_PASS,
+ NODE_DEFN,
+ NODE_DEFS,
+ NODE_ALIAS,
+ NODE_VALIAS,
+ NODE_UNDEF,
+ NODE_CLASS,
+ NODE_MODULE,
+ NODE_SCLASS,
+ NODE_COLON2,
+ NODE_COLON3,
+ NODE_DOT2,
+ NODE_DOT3,
+ NODE_FLIP2,
+ NODE_FLIP3,
+ NODE_SELF,
+ NODE_NIL,
+ NODE_TRUE,
+ NODE_FALSE,
+ NODE_ERRINFO,
+ NODE_DEFINED,
+ NODE_POSTEXE,
+ NODE_SYM,
+ NODE_DSYM,
+ NODE_ATTRASGN,
+ NODE_LAMBDA,
+ NODE_ARYPTN,
+ NODE_HSHPTN,
+ NODE_FNDPTN,
+ NODE_ERROR,
+ NODE_LINE,
+ NODE_FILE,
+ NODE_ENCODING,
+ NODE_LAST
+};
+
+typedef struct rb_ast_id_table {
+ int size;
+ ID ids[FLEX_ARY_LEN];
+} rb_ast_id_table_t;
+
+typedef struct rb_code_position_struct {
+ int lineno;
+ int column;
+} rb_code_position_t;
+
+typedef struct rb_code_location_struct {
+ rb_code_position_t beg_pos;
+ rb_code_position_t end_pos;
+} rb_code_location_t;
+#define YYLTYPE rb_code_location_t
+#define YYLTYPE_IS_DECLARED 1
+
+typedef struct rb_parser_ast_token {
+ int id;
+ const char *type_name;
+ rb_parser_string_t *str;
+ rb_code_location_t loc;
+} rb_parser_ast_token_t;
+
+/*
+ * Array-like object for parser
+ */
+typedef void* rb_parser_ary_data;
+
+enum rb_parser_ary_data_type {
+ PARSER_ARY_DATA_AST_TOKEN,
+ PARSER_ARY_DATA_SCRIPT_LINE
+};
+
+typedef struct rb_parser_ary {
+ enum rb_parser_ary_data_type data_type;
+ rb_parser_ary_data *data;
+ long len; // current size
+ long capa; // capacity
+} rb_parser_ary_t;
+
+/* Header part of AST Node */
+typedef struct RNode {
+ VALUE flags;
+ rb_code_location_t nd_loc;
+ int node_id;
+} NODE;
+
+typedef struct RNode_SCOPE {
+ NODE node;
+
+ rb_ast_id_table_t *nd_tbl;
+ struct RNode *nd_body;
+ struct RNode_ARGS *nd_args;
+} rb_node_scope_t;
+
+typedef struct RNode_BLOCK {
+ NODE node;
+
+ struct RNode *nd_head;
+ struct RNode *nd_end;
+ struct RNode *nd_next;
+} rb_node_block_t;
+
+typedef struct RNode_IF {
+ NODE node;
+
+ struct RNode *nd_cond;
+ struct RNode *nd_body;
+ struct RNode *nd_else;
+} rb_node_if_t;
+
+typedef struct RNode_UNLESS {
+ NODE node;
+
+ struct RNode *nd_cond;
+ struct RNode *nd_body;
+ struct RNode *nd_else;
+} rb_node_unless_t;
+
+typedef struct RNode_CASE {
+ NODE node;
+
+ struct RNode *nd_head;
+ struct RNode *nd_body;
+} rb_node_case_t;
+
+typedef struct RNode_CASE2 {
+ NODE node;
+
+ struct RNode *nd_head;
+ struct RNode *nd_body;
+} rb_node_case2_t;
+
+typedef struct RNode_CASE3 {
+ NODE node;
+
+ struct RNode *nd_head;
+ struct RNode *nd_body;
+} rb_node_case3_t;
+
+typedef struct RNode_WHEN {
+ NODE node;
+
+ struct RNode *nd_head;
+ struct RNode *nd_body;
+ struct RNode *nd_next;
+} rb_node_when_t;
+
+typedef struct RNode_IN {
+ NODE node;
+
+ struct RNode *nd_head;
+ struct RNode *nd_body;
+ struct RNode *nd_next;
+} rb_node_in_t;
+
+/* RNode_WHILE and RNode_UNTIL should be same structure */
+typedef struct RNode_WHILE {
+ NODE node;
+
+ struct RNode *nd_cond;
+ struct RNode *nd_body;
+ long nd_state;
+} rb_node_while_t;
+
+typedef struct RNode_UNTIL {
+ NODE node;
+
+ struct RNode *nd_cond;
+ struct RNode *nd_body;
+ long nd_state;
+} rb_node_until_t;
+
+/* RNode_ITER and RNode_FOR should be same structure */
+typedef struct RNode_ITER {
+ NODE node;
+
+ struct RNode *nd_body;
+ struct RNode *nd_iter;
+} rb_node_iter_t;
+
+typedef struct RNode_FOR {
+ NODE node;
+
+ struct RNode *nd_body;
+ struct RNode *nd_iter;
+} rb_node_for_t;
+
+typedef struct RNode_FOR_MASGN {
+ NODE node;
+
+ struct RNode *nd_var;
+} rb_node_for_masgn_t;
+
+/* RNode_BREAK, RNode_NEXT and RNode_REDO should be same structure */
+typedef struct RNode_BREAK {
+ NODE node;
+
+ struct RNode *nd_chain;
+ struct RNode *nd_stts;
+} rb_node_break_t;
+
+typedef struct RNode_NEXT {
+ NODE node;
+
+ struct RNode *nd_chain;
+ struct RNode *nd_stts;
+} rb_node_next_t;
+
+typedef struct RNode_REDO {
+ NODE node;
+
+ struct RNode *nd_chain;
+} rb_node_redo_t;
+
+typedef struct RNode_RETRY {
+ NODE node;
+} rb_node_retry_t;
+
+typedef struct RNode_BEGIN {
+ NODE node;
+
+ struct RNode *nd_body;
+} rb_node_begin_t;
+
+typedef struct RNode_RESCUE {
+ NODE node;
+
+ struct RNode *nd_head;
+ struct RNode *nd_resq;
+ struct RNode *nd_else;
+} rb_node_rescue_t;
+
+typedef struct RNode_RESBODY {
+ NODE node;
+
+ struct RNode *nd_args;
+ struct RNode *nd_body;
+ struct RNode *nd_next;
+} rb_node_resbody_t;
+
+typedef struct RNode_ENSURE {
+ NODE node;
+
+ struct RNode *nd_head;
+ struct RNode *nd_ensr;
+} rb_node_ensure_t;
+
+/* RNode_AND and RNode_OR should be same structure */
+typedef struct RNode_AND {
+ NODE node;
+
+ struct RNode *nd_1st;
+ struct RNode *nd_2nd;
+} rb_node_and_t;
+
+typedef struct RNode_OR {
+ NODE node;
+
+ struct RNode *nd_1st;
+ struct RNode *nd_2nd;
+} rb_node_or_t;
+
+typedef struct RNode_MASGN {
+ NODE node;
+
+ struct RNode *nd_head;
+ struct RNode *nd_value;
+ struct RNode *nd_args;
+} rb_node_masgn_t;
+
+typedef struct RNode_LASGN {
+ NODE node;
+
+ ID nd_vid;
+ struct RNode *nd_value;
+} rb_node_lasgn_t;
+
+typedef struct RNode_DASGN {
+ NODE node;
+
+ ID nd_vid;
+ struct RNode *nd_value;
+} rb_node_dasgn_t;
+
+typedef struct RNode_GASGN {
+ NODE node;
+
+ ID nd_vid;
+ struct RNode *nd_value;
+} rb_node_gasgn_t;
+
+typedef struct RNode_IASGN {
+ NODE node;
+
+ ID nd_vid;
+ struct RNode *nd_value;
+} rb_node_iasgn_t;
+
+typedef struct RNode_CDECL {
+ NODE node;
+
+ ID nd_vid;
+ struct RNode *nd_value;
+ struct RNode *nd_else;
+ enum rb_parser_shareability shareability;
+} rb_node_cdecl_t;
+
+typedef struct RNode_CVASGN {
+ NODE node;
+
+ ID nd_vid;
+ struct RNode *nd_value;
+} rb_node_cvasgn_t;
+
+typedef struct RNode_OP_ASGN1 {
+ NODE node;
+
+ struct RNode *nd_recv;
+ ID nd_mid;
+ struct RNode *nd_index;
+ struct RNode *nd_rvalue;
+} rb_node_op_asgn1_t;
+
+typedef struct RNode_OP_ASGN2 {
+ NODE node;
+
+ struct RNode *nd_recv;
+ struct RNode *nd_value;
+ ID nd_vid;
+ ID nd_mid;
+ bool nd_aid;
+} rb_node_op_asgn2_t;
+
+typedef struct RNode_OP_ASGN_AND {
+ NODE node;
+
+ struct RNode *nd_head;
+ struct RNode *nd_value;
+} rb_node_op_asgn_and_t;
+
+typedef struct RNode_OP_ASGN_OR {
+ NODE node;
+
+ struct RNode *nd_head;
+ struct RNode *nd_value;
+} rb_node_op_asgn_or_t;
+
+typedef struct RNode_OP_CDECL {
+ NODE node;
+
+ struct RNode *nd_head;
+ struct RNode *nd_value;
+ ID nd_aid;
+ enum rb_parser_shareability shareability;
+} rb_node_op_cdecl_t;
+
+typedef struct RNode_CALL {
+ NODE node;
+
+ struct RNode *nd_recv;
+ ID nd_mid;
+ struct RNode *nd_args;
+} rb_node_call_t;
+
+typedef struct RNode_OPCALL {
+ NODE node;
+
+ struct RNode *nd_recv;
+ ID nd_mid;
+ struct RNode *nd_args;
+} rb_node_opcall_t;
+
+typedef struct RNode_FCALL {
+ NODE node;
+
+ ID nd_mid;
+ struct RNode *nd_args;
+} rb_node_fcall_t;
+
+typedef struct RNode_VCALL {
+ NODE node;
+
+ ID nd_mid;
+} rb_node_vcall_t;
+
+typedef struct RNode_QCALL {
+ NODE node;
+
+ struct RNode *nd_recv;
+ ID nd_mid;
+ struct RNode *nd_args;
+} rb_node_qcall_t;
+
+typedef struct RNode_SUPER {
+ NODE node;
+
+ struct RNode *nd_args;
+} rb_node_super_t;
+
+typedef struct RNode_ZSUPER {
+ NODE node;
+} rb_node_zsuper_t;
+
+/*
+
+ Structure of LIST:
+
+ LIST +--> LIST
+ * head --> element | * head
+ * alen (length of list) | * nd_end (point to the last LIST)
+ * next -----------------+ * next
+
+
+ RNode_LIST and RNode_VALUES should be same structure
+*/
+typedef struct RNode_LIST {
+ NODE node;
+
+ struct RNode *nd_head; /* element */
+ union {
+ long nd_alen;
+ struct RNode *nd_end; /* Second list node has this structure */
+ } as;
+ struct RNode *nd_next; /* next list node */
+} rb_node_list_t;
+
+typedef struct RNode_ZLIST {
+ NODE node;
+} rb_node_zlist_t;
+
+typedef struct RNode_VALUES {
+ NODE node;
+
+ struct RNode *nd_head;
+ long nd_alen;
+ struct RNode *nd_next;
+} rb_node_values_t;
+
+typedef struct RNode_HASH {
+ NODE node;
+
+ struct RNode *nd_head;
+ long nd_brace;
+} rb_node_hash_t;
+
+typedef struct RNode_RETURN {
+ NODE node;
+
+ struct RNode *nd_stts;
+} rb_node_return_t;
+
+typedef struct RNode_YIELD {
+ NODE node;
+
+ struct RNode *nd_head;
+} rb_node_yield_t;
+
+typedef struct RNode_LVAR {
+ NODE node;
+
+ ID nd_vid;
+} rb_node_lvar_t;
+
+typedef struct RNode_DVAR {
+ NODE node;
+
+ ID nd_vid;
+} rb_node_dvar_t;
+
+typedef struct RNode_GVAR {
+ NODE node;
+
+ ID nd_vid;
+} rb_node_gvar_t;
+
+typedef struct RNode_IVAR {
+ NODE node;
+
+ ID nd_vid;
+} rb_node_ivar_t;
+
+typedef struct RNode_CONST {
+ NODE node;
+
+ ID nd_vid;
+} rb_node_const_t;
+
+typedef struct RNode_CVAR {
+ NODE node;
+
+ ID nd_vid;
+} rb_node_cvar_t;
+
+typedef struct RNode_NTH_REF {
+ NODE node;
+
+ long nd_nth;
+} rb_node_nth_ref_t;
+
+typedef struct RNode_BACK_REF {
+ NODE node;
+
+ long nd_nth;
+} rb_node_back_ref_t;
+
+/* RNode_MATCH and RNode_REGX should be same structure */
+typedef struct RNode_MATCH {
+ NODE node;
+
+ struct rb_parser_string *string;
+ int options;
+} rb_node_match_t;
+
+typedef struct RNode_MATCH2 {
+ NODE node;
+
+ struct RNode *nd_recv;
+ struct RNode *nd_value;
+ struct RNode *nd_args;
+} rb_node_match2_t;
+
+typedef struct RNode_MATCH3 {
+ NODE node;
+
+ struct RNode *nd_recv;
+ struct RNode *nd_value;
+} rb_node_match3_t;
+
+typedef struct RNode_INTEGER {
+ NODE node;
+
+ char *val;
+ int minus;
+ int base;
+} rb_node_integer_t;
+
+typedef struct RNode_FLOAT {
+ NODE node;
+
+ char *val;
+ int minus;
+} rb_node_float_t;
+
+typedef struct RNode_RATIONAL {
+ NODE node;
+
+ char *val;
+ int minus;
+ int base;
+ int seen_point;
+} rb_node_rational_t;
+
+enum rb_numeric_type {
+ integer_literal,
+ float_literal,
+ rational_literal
+};
+
+typedef struct RNode_IMAGINARY {
+ NODE node;
+
+ char *val;
+ int minus;
+ int base;
+ int seen_point;
+ enum rb_numeric_type type;
+} rb_node_imaginary_t;
+
+/* RNode_STR and RNode_XSTR should be same structure */
+typedef struct RNode_STR {
+ NODE node;
+
+ struct rb_parser_string *string;
+} rb_node_str_t;
+
+/* RNode_DSTR, RNode_DXSTR and RNode_DSYM should be same structure */
+typedef struct RNode_DSTR {
+ NODE node;
+
+ struct rb_parser_string *string;
+ union {
+ long nd_alen;
+ struct RNode *nd_end; /* Second dstr node has this structure. See also RNode_LIST */
+ } as;
+ struct RNode_LIST *nd_next;
+} rb_node_dstr_t;
+
+typedef struct RNode_XSTR {
+ NODE node;
+
+ struct rb_parser_string *string;
+} rb_node_xstr_t;
+
+typedef struct RNode_DXSTR {
+ NODE node;
+
+ struct rb_parser_string *string;
+ long nd_alen;
+ struct RNode_LIST *nd_next;
+} rb_node_dxstr_t;
+
+typedef struct RNode_EVSTR {
+ NODE node;
+
+ struct RNode *nd_body;
+} rb_node_evstr_t;
+
+typedef struct RNode_REGX {
+ NODE node;
+
+ struct rb_parser_string *string;
+ int options;
+} rb_node_regx_t;
+
+typedef struct RNode_DREGX {
+ NODE node;
+
+ struct rb_parser_string *string;
+ ID nd_cflag;
+ struct RNode_LIST *nd_next;
+} rb_node_dregx_t;
+
+typedef struct RNode_ONCE {
+ NODE node;
+
+ struct RNode *nd_body;
+} rb_node_once_t;
+
+struct rb_args_info {
+ NODE *pre_init;
+ NODE *post_init;
+
+ int pre_args_num; /* count of mandatory pre-arguments */
+ int post_args_num; /* count of mandatory post-arguments */
+
+ ID first_post_arg;
+
+ ID rest_arg;
+ ID block_arg;
+
+ struct RNode_KW_ARG *kw_args;
+ NODE *kw_rest_arg;
+
+ struct RNode_OPT_ARG *opt_args;
+ unsigned int no_kwarg: 1;
+ unsigned int ruby2_keywords: 1;
+ unsigned int forwarding: 1;
+};
+
+typedef struct RNode_ARGS {
+ NODE node;
+
+ struct rb_args_info nd_ainfo;
+} rb_node_args_t;
+
+typedef struct RNode_ARGS_AUX {
+ NODE node;
+
+ ID nd_pid;
+ int nd_plen;
+ struct RNode *nd_next;
+} rb_node_args_aux_t;
+
+typedef struct RNode_OPT_ARG {
+ NODE node;
+
+ struct RNode *nd_body;
+ struct RNode_OPT_ARG *nd_next;
+} rb_node_opt_arg_t;
+
+typedef struct RNode_KW_ARG {
+ NODE node;
+
+ struct RNode *nd_body;
+ struct RNode_KW_ARG *nd_next;
+} rb_node_kw_arg_t;
+
+typedef struct RNode_POSTARG {
+ NODE node;
+
+ struct RNode *nd_1st;
+ struct RNode *nd_2nd;
+} rb_node_postarg_t;
+
+typedef struct RNode_ARGSCAT {
+ NODE node;
+
+ struct RNode *nd_head;
+ struct RNode *nd_body;
+} rb_node_argscat_t;
+
+typedef struct RNode_ARGSPUSH {
+ NODE node;
+
+ struct RNode *nd_head;
+ struct RNode *nd_body;
+} rb_node_argspush_t;
+
+typedef struct RNode_SPLAT {
+ NODE node;
+
+ struct RNode *nd_head;
+} rb_node_splat_t;
+
+typedef struct RNode_BLOCK_PASS {
+ NODE node;
+
+ struct RNode *nd_head;
+ struct RNode *nd_body;
+} rb_node_block_pass_t;
+
+typedef struct RNode_DEFN {
+ NODE node;
+
+ ID nd_mid;
+ struct RNode *nd_defn;
+} rb_node_defn_t;
+
+typedef struct RNode_DEFS {
+ NODE node;
+
+ struct RNode *nd_recv;
+ ID nd_mid;
+ struct RNode *nd_defn;
+} rb_node_defs_t;
+
+typedef struct RNode_ALIAS {
+ NODE node;
+
+ struct RNode *nd_1st;
+ struct RNode *nd_2nd;
+} rb_node_alias_t;
+
+typedef struct RNode_VALIAS {
+ NODE node;
+
+ ID nd_alias;
+ ID nd_orig;
+} rb_node_valias_t;
+
+typedef struct RNode_UNDEF {
+ NODE node;
+
+ struct RNode *nd_undef;
+} rb_node_undef_t;
+
+typedef struct RNode_CLASS {
+ NODE node;
+
+ struct RNode *nd_cpath;
+ struct RNode *nd_body;
+ struct RNode *nd_super;
+} rb_node_class_t;
+
+typedef struct RNode_MODULE {
+ NODE node;
+
+ struct RNode *nd_cpath;
+ struct RNode *nd_body;
+} rb_node_module_t;
+
+typedef struct RNode_SCLASS {
+ NODE node;
+
+ struct RNode *nd_recv;
+ struct RNode *nd_body;
+} rb_node_sclass_t;
+
+typedef struct RNode_COLON2 {
+ NODE node;
+
+ struct RNode *nd_head;
+ ID nd_mid;
+} rb_node_colon2_t;
+
+typedef struct RNode_COLON3 {
+ NODE node;
+
+ ID nd_mid;
+} rb_node_colon3_t;
+
+/* RNode_DOT2, RNode_DOT3, RNode_FLIP2 and RNode_FLIP3 should be same structure */
+typedef struct RNode_DOT2 {
+ NODE node;
+
+ struct RNode *nd_beg;
+ struct RNode *nd_end;
+} rb_node_dot2_t;
+
+typedef struct RNode_DOT3 {
+ NODE node;
+
+ struct RNode *nd_beg;
+ struct RNode *nd_end;
+} rb_node_dot3_t;
+
+typedef struct RNode_FLIP2 {
+ NODE node;
+
+ struct RNode *nd_beg;
+ struct RNode *nd_end;
+} rb_node_flip2_t;
+
+typedef struct RNode_FLIP3 {
+ NODE node;
+
+ struct RNode *nd_beg;
+ struct RNode *nd_end;
+} rb_node_flip3_t;
+
+typedef struct RNode_SELF {
+ NODE node;
+
+ long nd_state; /* Default 1. See NEW_SELF. */
+} rb_node_self_t;
+
+typedef struct RNode_NIL {
+ NODE node;
+} rb_node_nil_t;
+
+typedef struct RNode_TRUE {
+ NODE node;
+} rb_node_true_t;
+
+typedef struct RNode_FALSE {
+ NODE node;
+} rb_node_false_t;
+
+typedef struct RNode_ERRINFO {
+ NODE node;
+} rb_node_errinfo_t;
+
+typedef struct RNode_DEFINED {
+ NODE node;
+
+ struct RNode *nd_head;
+} rb_node_defined_t;
+
+typedef struct RNode_POSTEXE {
+ NODE node;
+
+ struct RNode *nd_body;
+} rb_node_postexe_t;
+
+typedef struct RNode_SYM {
+ NODE node;
+
+ struct rb_parser_string *string;
+} rb_node_sym_t;
+
+typedef struct RNode_DSYM {
+ NODE node;
+
+ struct rb_parser_string *string;
+ long nd_alen;
+ struct RNode_LIST *nd_next;
+} rb_node_dsym_t;
+
+typedef struct RNode_ATTRASGN {
+ NODE node;
+
+ struct RNode *nd_recv;
+ ID nd_mid;
+ struct RNode *nd_args;
+} rb_node_attrasgn_t;
+
+typedef struct RNode_LAMBDA {
+ NODE node;
+
+ struct RNode *nd_body;
+} rb_node_lambda_t;
+
+typedef struct RNode_ARYPTN {
+ NODE node;
+
+ struct RNode *nd_pconst;
+ NODE *pre_args;
+ NODE *rest_arg;
+ NODE *post_args;
+} rb_node_aryptn_t;
+
+typedef struct RNode_HSHPTN {
+ NODE node;
+
+ struct RNode *nd_pconst;
+ struct RNode *nd_pkwargs;
+ struct RNode *nd_pkwrestarg;
+} rb_node_hshptn_t;
+
+typedef struct RNode_FNDPTN {
+ NODE node;
+
+ struct RNode *nd_pconst;
+ NODE *pre_rest_arg;
+ NODE *args;
+ NODE *post_rest_arg;
+} rb_node_fndptn_t;
+
+typedef struct RNode_LINE {
+ NODE node;
+} rb_node_line_t;
+
+typedef struct RNode_FILE {
+ NODE node;
+
+ struct rb_parser_string *path;
+} rb_node_file_t;
+
+typedef struct RNode_ENCODING {
+ NODE node;
+ rb_encoding *enc;
+} rb_node_encoding_t;
+
+typedef struct RNode_ERROR {
+ NODE node;
+} rb_node_error_t;
+
+#define RNODE(obj) ((struct RNode *)(obj))
+
+#define RNODE_SCOPE(node) ((struct RNode_SCOPE *)(node))
+#define RNODE_BLOCK(node) ((struct RNode_BLOCK *)(node))
+#define RNODE_IF(node) ((struct RNode_IF *)(node))
+#define RNODE_UNLESS(node) ((struct RNode_UNLESS *)(node))
+#define RNODE_CASE(node) ((struct RNode_CASE *)(node))
+#define RNODE_CASE2(node) ((struct RNode_CASE2 *)(node))
+#define RNODE_CASE3(node) ((struct RNode_CASE3 *)(node))
+#define RNODE_WHEN(node) ((struct RNode_WHEN *)(node))
+#define RNODE_IN(node) ((struct RNode_IN *)(node))
+#define RNODE_WHILE(node) ((struct RNode_WHILE *)(node))
+#define RNODE_UNTIL(node) ((struct RNode_UNTIL *)(node))
+#define RNODE_ITER(node) ((struct RNode_ITER *)(node))
+#define RNODE_FOR(node) ((struct RNode_FOR *)(node))
+#define RNODE_FOR_MASGN(node) ((struct RNode_FOR_MASGN *)(node))
+#define RNODE_BREAK(node) ((struct RNode_BREAK *)(node))
+#define RNODE_NEXT(node) ((struct RNode_NEXT *)(node))
+#define RNODE_REDO(node) ((struct RNode_REDO *)(node))
+#define RNODE_RETRY(node) ((struct RNode_RETRY *)(node))
+#define RNODE_BEGIN(node) ((struct RNode_BEGIN *)(node))
+#define RNODE_RESCUE(node) ((struct RNode_RESCUE *)(node))
+#define RNODE_RESBODY(node) ((struct RNode_RESBODY *)(node))
+#define RNODE_ENSURE(node) ((struct RNode_ENSURE *)(node))
+#define RNODE_AND(node) ((struct RNode_AND *)(node))
+#define RNODE_OR(node) ((struct RNode_OR *)(node))
+#define RNODE_MASGN(node) ((struct RNode_MASGN *)(node))
+#define RNODE_LASGN(node) ((struct RNode_LASGN *)(node))
+#define RNODE_DASGN(node) ((struct RNode_DASGN *)(node))
+#define RNODE_GASGN(node) ((struct RNode_GASGN *)(node))
+#define RNODE_IASGN(node) ((struct RNode_IASGN *)(node))
+#define RNODE_CDECL(node) ((struct RNode_CDECL *)(node))
+#define RNODE_CVASGN(node) ((struct RNode_CVASGN *)(node))
+#define RNODE_OP_ASGN1(node) ((struct RNode_OP_ASGN1 *)(node))
+#define RNODE_OP_ASGN2(node) ((struct RNode_OP_ASGN2 *)(node))
+#define RNODE_OP_ASGN_AND(node) ((struct RNode_OP_ASGN_AND *)(node))
+#define RNODE_OP_ASGN_OR(node) ((struct RNode_OP_ASGN_OR *)(node))
+#define RNODE_OP_CDECL(node) ((struct RNode_OP_CDECL *)(node))
+#define RNODE_CALL(node) ((struct RNode_CALL *)(node))
+#define RNODE_OPCALL(node) ((struct RNode_OPCALL *)(node))
+#define RNODE_FCALL(node) ((struct RNode_FCALL *)(node))
+#define RNODE_VCALL(node) ((struct RNode_VCALL *)(node))
+#define RNODE_QCALL(node) ((struct RNode_QCALL *)(node))
+#define RNODE_SUPER(node) ((struct RNode_SUPER *)(node))
+#define RNODE_ZSUPER(node) ((struct RNode_ZSUPER *)(node))
+#define RNODE_LIST(node) ((struct RNode_LIST *)(node))
+#define RNODE_ZLIST(node) ((struct RNode_ZLIST *)(node))
+#define RNODE_HASH(node) ((struct RNode_HASH *)(node))
+#define RNODE_RETURN(node) ((struct RNode_RETURN *)(node))
+#define RNODE_YIELD(node) ((struct RNode_YIELD *)(node))
+#define RNODE_LVAR(node) ((struct RNode_LVAR *)(node))
+#define RNODE_DVAR(node) ((struct RNode_DVAR *)(node))
+#define RNODE_GVAR(node) ((struct RNode_GVAR *)(node))
+#define RNODE_IVAR(node) ((struct RNode_IVAR *)(node))
+#define RNODE_CONST(node) ((struct RNode_CONST *)(node))
+#define RNODE_CVAR(node) ((struct RNode_CVAR *)(node))
+#define RNODE_NTH_REF(node) ((struct RNode_NTH_REF *)(node))
+#define RNODE_BACK_REF(node) ((struct RNode_BACK_REF *)(node))
+#define RNODE_MATCH(node) ((struct RNode_MATCH *)(node))
+#define RNODE_MATCH2(node) ((struct RNode_MATCH2 *)(node))
+#define RNODE_MATCH3(node) ((struct RNode_MATCH3 *)(node))
+#define RNODE_INTEGER(node) ((struct RNode_INTEGER *)(node))
+#define RNODE_FLOAT(node) ((struct RNode_FLOAT *)(node))
+#define RNODE_RATIONAL(node) ((struct RNode_RATIONAL *)(node))
+#define RNODE_IMAGINARY(node) ((struct RNode_IMAGINARY *)(node))
+#define RNODE_STR(node) ((struct RNode_STR *)(node))
+#define RNODE_DSTR(node) ((struct RNode_DSTR *)(node))
+#define RNODE_XSTR(node) ((struct RNode_XSTR *)(node))
+#define RNODE_DXSTR(node) ((struct RNode_DXSTR *)(node))
+#define RNODE_EVSTR(node) ((struct RNode_EVSTR *)(node))
+#define RNODE_REGX(node) ((struct RNode_REGX *)(node))
+#define RNODE_DREGX(node) ((struct RNode_DREGX *)(node))
+#define RNODE_ONCE(node) ((struct RNode_ONCE *)(node))
+#define RNODE_ARGS(node) ((struct RNode_ARGS *)(node))
+#define RNODE_ARGS_AUX(node) ((struct RNode_ARGS_AUX *)(node))
+#define RNODE_OPT_ARG(node) ((struct RNode_OPT_ARG *)(node))
+#define RNODE_KW_ARG(node) ((struct RNode_KW_ARG *)(node))
+#define RNODE_POSTARG(node) ((struct RNode_POSTARG *)(node))
+#define RNODE_ARGSCAT(node) ((struct RNode_ARGSCAT *)(node))
+#define RNODE_ARGSPUSH(node) ((struct RNode_ARGSPUSH *)(node))
+#define RNODE_SPLAT(node) ((struct RNode_SPLAT *)(node))
+#define RNODE_BLOCK_PASS(node) ((struct RNode_BLOCK_PASS *)(node))
+#define RNODE_DEFN(node) ((struct RNode_DEFN *)(node))
+#define RNODE_DEFS(node) ((struct RNode_DEFS *)(node))
+#define RNODE_ALIAS(node) ((struct RNode_ALIAS *)(node))
+#define RNODE_VALIAS(node) ((struct RNode_VALIAS *)(node))
+#define RNODE_UNDEF(node) ((struct RNode_UNDEF *)(node))
+#define RNODE_CLASS(node) ((struct RNode_CLASS *)(node))
+#define RNODE_MODULE(node) ((struct RNode_MODULE *)(node))
+#define RNODE_SCLASS(node) ((struct RNode_SCLASS *)(node))
+#define RNODE_COLON2(node) ((struct RNode_COLON2 *)(node))
+#define RNODE_COLON3(node) ((struct RNode_COLON3 *)(node))
+#define RNODE_DOT2(node) ((struct RNode_DOT2 *)(node))
+#define RNODE_DOT3(node) ((struct RNode_DOT3 *)(node))
+#define RNODE_FLIP2(node) ((struct RNode_FLIP2 *)(node))
+#define RNODE_FLIP3(node) ((struct RNode_FLIP3 *)(node))
+#define RNODE_SELF(node) ((struct RNode_SELF *)(node))
+#define RNODE_NIL(node) ((struct RNode_NIL *)(node))
+#define RNODE_TRUE(node) ((struct RNode_TRUE *)(node))
+#define RNODE_FALSE(node) ((struct RNode_FALSE *)(node))
+#define RNODE_ERRINFO(node) ((struct RNode_ERRINFO *)(node))
+#define RNODE_DEFINED(node) ((struct RNode_DEFINED *)(node))
+#define RNODE_POSTEXE(node) ((struct RNode_POSTEXE *)(node))
+#define RNODE_SYM(node) ((struct RNode_SYM *)(node))
+#define RNODE_DSYM(node) ((struct RNode_DSYM *)(node))
+#define RNODE_ATTRASGN(node) ((struct RNode_ATTRASGN *)(node))
+#define RNODE_LAMBDA(node) ((struct RNode_LAMBDA *)(node))
+#define RNODE_ARYPTN(node) ((struct RNode_ARYPTN *)(node))
+#define RNODE_HSHPTN(node) ((struct RNode_HSHPTN *)(node))
+#define RNODE_FNDPTN(node) ((struct RNode_FNDPTN *)(node))
+#define RNODE_LINE(node) ((struct RNode_LINE *)(node))
+#define RNODE_FILE(node) ((struct RNode_FILE *)(node))
+#define RNODE_ENCODING(node) ((struct RNode_ENCODING *)(node))
+
+/* FL : 0..4: T_TYPES, 5: KEEP_WB, 6: PROMOTED, 7: FINALIZE, 8: UNUSED, 9: UNUSED, 10: EXIVAR, 11: FREEZE */
+/* NODE_FL: 0..4: UNUSED, 5: UNUSED, 6: UNUSED, 7: NODE_FL_NEWLINE,
+ * 8..14: nd_type,
+ * 15..: nd_line
+ */
+#define NODE_FL_NEWLINE (((VALUE)1)<<7)
+
+#define NODE_TYPESHIFT 8
+#define NODE_TYPEMASK (((VALUE)0x7f)<<NODE_TYPESHIFT)
+
+#define nd_fl_newline(n) (n)->flags & NODE_FL_NEWLINE
+#define nd_set_fl_newline(n) (n)->flags |= NODE_FL_NEWLINE
+#define nd_unset_fl_newline(n) (n)->flags &= ~NODE_FL_NEWLINE
+
+#define nd_type(n) ((int) ((RNODE(n)->flags & NODE_TYPEMASK)>>NODE_TYPESHIFT))
+#define nd_set_type(n,t) \
+ rb_node_set_type(n, t)
+#define nd_init_type(n,t) \
+ (n)->flags=(((n)->flags&~NODE_TYPEMASK)|((((unsigned long)(t))<<NODE_TYPESHIFT)&NODE_TYPEMASK))
+
+typedef struct node_buffer_struct node_buffer_t;
+
+#ifdef UNIVERSAL_PARSER
+typedef struct rb_parser_config_struct rb_parser_config_t;
+#endif
+
+typedef struct rb_ast_body_struct {
+ const NODE *root;
+ rb_parser_ary_t *script_lines;
+ int line_count;
+ signed int frozen_string_literal:2; /* -1: not specified, 0: false, 1: true */
+ signed int coverage_enabled:2; /* -1: not specified, 0: false, 1: true */
+} rb_ast_body_t;
+typedef struct rb_ast_struct {
+ node_buffer_t *node_buffer;
+ rb_ast_body_t body;
+#ifdef UNIVERSAL_PARSER
+ const rb_parser_config_t *config;
+#endif
+} rb_ast_t;
+
+
+
+/*
+ * Parser Interface
+ */
+
+
+typedef struct parser_params rb_parser_t;
+#ifndef INTERNAL_IMEMO_H
+typedef struct rb_imemo_tmpbuf_struct rb_imemo_tmpbuf_t;
+#endif
+
+#ifdef UNIVERSAL_PARSER
+typedef struct rb_parser_config_struct {
+ /* Memory */
+ void *(*malloc)(size_t size);
+ void *(*calloc)(size_t number, size_t size);
+ void *(*realloc)(void *ptr, size_t newsiz);
+ void (*free)(void *ptr);
+ void *(*alloc_n)(size_t nelems, size_t elemsiz);
+ void *(*alloc)(size_t elemsiz);
+ void *(*realloc_n)(void *ptr, size_t newelems, size_t newsiz);
+ void *(*zalloc)(size_t elemsiz);
+ void *(*rb_memmove)(void *dest, const void *src, size_t t, size_t n);
+ void *(*nonempty_memcpy)(void *dest, const void *src, size_t t, size_t n);
+ void *(*xmalloc_mul_add)(size_t x, size_t y, size_t z);
+
+ // VALUE rb_suppress_tracing(VALUE (*func)(VALUE), VALUE arg);
+ VALUE (*compile_callback)(VALUE (*func)(VALUE), VALUE arg);
+ NODE *(*reg_named_capture_assign)(struct parser_params* p, VALUE regexp, const rb_code_location_t *loc);
+
+ /* Variable */
+ VALUE (*attr_get)(VALUE obj, ID id);
+
+ /* Array */
+ VALUE (*ary_new)(void);
+ VALUE (*ary_push)(VALUE ary, VALUE elem);
+ VALUE (*ary_new_from_args)(long n, ...);
+ VALUE (*ary_unshift)(VALUE ary, VALUE item);
+
+ /* Symbol */
+ ID (*make_temporary_id)(size_t n);
+ int (*is_local_id)(ID);
+ int (*is_attrset_id)(ID);
+ int (*is_global_name_punct)(const int c);
+ int (*id_type)(ID id);
+ ID (*id_attrset)(ID);
+ ID (*intern)(const char *name);
+ ID (*intern2)(const char *name, long len);
+ ID (*intern3)(const char *name, long len, rb_encoding *enc);
+ ID (*intern_str)(VALUE str);
+ int (*is_notop_id)(ID);
+ int (*enc_symname_type)(const char *name, long len, rb_encoding *enc, unsigned int allowed_attrset);
+ const char *(*id2name)(ID id);
+ VALUE (*id2str)(ID id);
+ VALUE (*id2sym)(ID x);
+ ID (*sym2id)(VALUE sym);
+
+ /* String */
+ RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3)
+ VALUE (*str_catf)(VALUE str, const char *format, ...);
+ VALUE (*str_cat_cstr)(VALUE str, const char *ptr);
+ void (*str_modify)(VALUE str);
+ void (*str_set_len)(VALUE str, long len);
+ VALUE (*str_cat)(VALUE str, const char *ptr, long len);
+ VALUE (*str_resize)(VALUE str, long len);
+ VALUE (*str_new)(const char *ptr, long len);
+ VALUE (*str_new_cstr)(const char *ptr);
+ VALUE (*str_to_interned_str)(VALUE);
+ int (*is_ascii_string)(VALUE str);
+ VALUE (*enc_str_new)(const char *ptr, long len, rb_encoding *enc);
+ RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 0)
+ VALUE (*str_vcatf)(VALUE str, const char *fmt, va_list ap);
+ char *(*string_value_cstr)(volatile VALUE *ptr);
+ RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 2)
+ VALUE (*rb_sprintf)(const char *format, ...);
+ char *(*rstring_ptr)(VALUE str);
+ char *(*rstring_end)(VALUE str);
+ long (*rstring_len)(VALUE str);
+ VALUE (*obj_as_string)(VALUE);
+
+ /* Numeric */
+ VALUE (*int2num)(int v);
+
+ /* IO */
+ int (*stderr_tty_p)(void);
+ void (*write_error_str)(VALUE mesg);
+ VALUE (*io_write)(VALUE io, VALUE str);
+ VALUE (*io_flush)(VALUE io);
+ VALUE (*io_puts)(int argc, const VALUE *argv, VALUE out);
+
+ /* IO (Ractor) */
+ VALUE (*debug_output_stdout)(void);
+ VALUE (*debug_output_stderr)(void);
+
+ /* Encoding */
+ int (*is_usascii_enc)(rb_encoding *enc);
+ int (*enc_isalnum)(OnigCodePoint c, rb_encoding *enc);
+ int (*enc_precise_mbclen)(const char *p, const char *e, rb_encoding *enc);
+ int (*mbclen_charfound_p)(int len);
+ int (*mbclen_charfound_len)(int len);
+ const char *(*enc_name)(rb_encoding *enc);
+ char *(*enc_prev_char)(const char *s, const char *p, const char *e, rb_encoding *enc);
+ rb_encoding* (*enc_get)(VALUE obj);
+ int (*enc_asciicompat)(rb_encoding *enc);
+ rb_encoding *(*utf8_encoding)(void);
+ VALUE (*enc_associate)(VALUE obj, rb_encoding *enc);
+ rb_encoding *(*ascii8bit_encoding)(void);
+ int (*enc_codelen)(int c, rb_encoding *enc);
+ int (*enc_mbcput)(unsigned int c, void *buf, rb_encoding *enc);
+ int (*enc_mbclen)(const char *p, const char *e, rb_encoding *enc);
+ int (*enc_find_index)(const char *name);
+ rb_encoding *(*enc_from_index)(int idx);
+ int (*enc_isspace)(OnigCodePoint c, rb_encoding *enc);
+ rb_encoding *(*usascii_encoding)(void);
+ int enc_coderange_broken;
+ int (*enc_mbminlen)(rb_encoding *enc);
+ bool (*enc_isascii)(OnigCodePoint c, rb_encoding *enc);
+ OnigCodePoint (*enc_mbc_to_codepoint)(const char *p, const char *e, rb_encoding *enc);
+
+ /* Compile */
+ // int rb_local_defined(ID id, const rb_iseq_t *iseq);
+ int (*local_defined)(ID, const void*);
+ // int rb_dvar_defined(ID id, const rb_iseq_t *iseq);
+ int (*dvar_defined)(ID, const void*);
+
+ /* Error (Exception) */
+ RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 6, 0)
+ VALUE (*syntax_error_append)(VALUE, VALUE, int, int, rb_encoding*, const char*, va_list);
+ RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3)
+ void (*raise)(VALUE exc, const char *fmt, ...);
+ VALUE (*syntax_error_new)(void);
+
+ /* Eval */
+ VALUE (*errinfo)(void);
+ void (*set_errinfo)(VALUE err);
+ void (*exc_raise)(VALUE mesg);
+ VALUE (*make_exception)(int argc, const VALUE *argv);
+
+ /* GC */
+ void (*sized_xfree)(void *x, size_t size);
+ void *(*sized_realloc_n)(void *ptr, size_t new_count, size_t element_size, size_t old_count);
+ void (*gc_guard)(VALUE);
+ void (*gc_mark)(VALUE);
+
+ /* Re */
+ VALUE (*reg_compile)(VALUE str, int options, const char *sourcefile, int sourceline);
+ VALUE (*reg_check_preprocess)(VALUE str);
+ int (*memcicmp)(const void *x, const void *y, long len);
+
+ /* Error */
+ void (*compile_warn)(const char *file, int line, const char *fmt, ...) RUBYPARSER_ATTRIBUTE_FORMAT(3, 4);
+ void (*compile_warning)(const char *file, int line, const char *fmt, ...) RUBYPARSER_ATTRIBUTE_FORMAT(3, 4);
+ void (*bug)(const char *fmt, ...) RUBYPARSER_ATTRIBUTE_FORMAT(1, 2);
+ void (*fatal)(const char *fmt, ...) RUBYPARSER_ATTRIBUTE_FORMAT(1, 2);
+ VALUE (*verbose)(void);
+ int *(*errno_ptr)(void);
+
+ /* VM */
+ VALUE (*make_backtrace)(void);
+
+ /* Util */
+ unsigned long (*scan_hex)(const char *start, size_t len, size_t *retlen);
+ unsigned long (*scan_oct)(const char *start, size_t len, size_t *retlen);
+ unsigned long (*scan_digits)(const char *str, ssize_t len, int base, size_t *retlen, int *overflow);
+ double (*strtod)(const char *s00, char **se);
+
+ /* Misc */
+ int (*rtest)(VALUE obj);
+ int (*nil_p)(VALUE obj);
+ VALUE qnil;
+ VALUE qfalse;
+ VALUE (*eArgError)(void);
+ int (*long2int)(long);
+
+ /* For Ripper */
+ int enc_coderange_7bit;
+ int enc_coderange_unknown;
+ VALUE (*static_id2sym)(ID id);
+ long (*str_coderange_scan_restartable)(const char *s, const char *e, rb_encoding *enc, int *cr);
+} rb_parser_config_t;
+
+#undef rb_encoding
+#undef OnigCodePoint
+#endif /* UNIVERSAL_PARSER */
+
+RUBY_SYMBOL_EXPORT_BEGIN
+void rb_ruby_parser_free(void *ptr);
+
+#ifdef UNIVERSAL_PARSER
+rb_parser_t *rb_ruby_parser_allocate(const rb_parser_config_t *config);
+rb_parser_t *rb_ruby_parser_new(const rb_parser_config_t *config);
+#endif
+
+long rb_parser_string_length(rb_parser_string_t *str);
+char *rb_parser_string_pointer(rb_parser_string_t *str);
+
+RUBY_SYMBOL_EXPORT_END
+
+#endif /* RUBY_RUBYPARSER_H */
diff --git a/sample/getoptlong/abbrev.rb b/sample/getoptlong/abbrev.rb
deleted file mode 100644
index 9b89863626..0000000000
--- a/sample/getoptlong/abbrev.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require 'getoptlong'
-
-options = GetoptLong.new(
- ['--xxx', GetoptLong::NO_ARGUMENT],
- ['--xyz', GetoptLong::NO_ARGUMENT]
-)
-options.each do |option, argument|
- p [option, argument]
-end
diff --git a/sample/getoptlong/aliases.rb b/sample/getoptlong/aliases.rb
deleted file mode 100644
index 895254c6ae..0000000000
--- a/sample/getoptlong/aliases.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require 'getoptlong'
-
-options = GetoptLong.new(
- ['--xxx', '-x', '--aaa', '-a', '-p', GetoptLong::NO_ARGUMENT]
-)
-options.each do |option, argument|
- p [option, argument]
-end
diff --git a/sample/getoptlong/argv.rb b/sample/getoptlong/argv.rb
deleted file mode 100644
index 8efcad22ea..0000000000
--- a/sample/getoptlong/argv.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require 'getoptlong'
-
-options = GetoptLong.new(
- ['--xxx', GetoptLong::REQUIRED_ARGUMENT],
- ['--yyy', GetoptLong::OPTIONAL_ARGUMENT],
- ['--zzz', GetoptLong::NO_ARGUMENT]
-)
-puts "Original ARGV: #{ARGV}"
-options.each do |option, argument|
- p [option, argument]
-end
-puts "Remaining ARGV: #{ARGV}"
diff --git a/sample/getoptlong/each.rb b/sample/getoptlong/each.rb
deleted file mode 100644
index 661e0a968f..0000000000
--- a/sample/getoptlong/each.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require 'getoptlong'
-
-options = GetoptLong.new(
- ['--xxx', '-x', GetoptLong::REQUIRED_ARGUMENT],
- ['--yyy', '-y', GetoptLong::OPTIONAL_ARGUMENT],
- ['--zzz', '-z',GetoptLong::NO_ARGUMENT]
-)
-puts "Original ARGV: #{ARGV}"
-options.each do |option, argument|
- p [option, argument]
-end
-puts "Remaining ARGV: #{ARGV}"
diff --git a/sample/getoptlong/fibonacci.rb b/sample/getoptlong/fibonacci.rb
deleted file mode 100644
index 24a2aab3c3..0000000000
--- a/sample/getoptlong/fibonacci.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-require 'getoptlong'
-
-options = GetoptLong.new(
- ['--number', '-n', GetoptLong::REQUIRED_ARGUMENT],
- ['--verbose', '-v', GetoptLong::OPTIONAL_ARGUMENT],
- ['--help', '-h', GetoptLong::NO_ARGUMENT]
-)
-
-def help(status = 0)
- puts <<~HELP
- Usage:
-
- -n n, --number n:
- Compute Fibonacci number for n.
- -v [boolean], --verbose [boolean]:
- Show intermediate results; default is 'false'.
- -h, --help:
- Show this help.
- HELP
- exit(status)
-end
-
-def print_fibonacci (number)
- return 0 if number == 0
- return 1 if number == 1 or number == 2
- i = 0
- j = 1
- (2..number).each do
- k = i + j
- i = j
- j = k
- puts j if @verbose
- end
- puts j unless @verbose
-end
-
-options.each do |option, argument|
- case option
- when '--number'
- @number = argument.to_i
- when '--verbose'
- @verbose = if argument.empty?
- true
- elsif argument.match(/true/i)
- true
- elsif argument.match(/false/i)
- false
- else
- puts '--verbose argument must be true or false'
- help(255)
- end
- when '--help'
- help
- end
-end
-
-unless @number
- puts 'Option --number is required.'
- help(255)
-end
-
-print_fibonacci(@number)
diff --git a/sample/getoptlong/permute.rb b/sample/getoptlong/permute.rb
deleted file mode 100644
index 8efcad22ea..0000000000
--- a/sample/getoptlong/permute.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require 'getoptlong'
-
-options = GetoptLong.new(
- ['--xxx', GetoptLong::REQUIRED_ARGUMENT],
- ['--yyy', GetoptLong::OPTIONAL_ARGUMENT],
- ['--zzz', GetoptLong::NO_ARGUMENT]
-)
-puts "Original ARGV: #{ARGV}"
-options.each do |option, argument|
- p [option, argument]
-end
-puts "Remaining ARGV: #{ARGV}"
diff --git a/sample/getoptlong/require_order.rb b/sample/getoptlong/require_order.rb
deleted file mode 100644
index 357f667905..0000000000
--- a/sample/getoptlong/require_order.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require 'getoptlong'
-
-options = GetoptLong.new(
- ['--xxx', GetoptLong::REQUIRED_ARGUMENT],
- ['--yyy', GetoptLong::OPTIONAL_ARGUMENT],
- ['--zzz', GetoptLong::NO_ARGUMENT]
-)
-options.ordering = GetoptLong::REQUIRE_ORDER
-puts "Original ARGV: #{ARGV}"
-options.each do |option, argument|
- p [option, argument]
-end
-puts "Remaining ARGV: #{ARGV}"
diff --git a/sample/getoptlong/return_in_order.rb b/sample/getoptlong/return_in_order.rb
deleted file mode 100644
index 91ce1ef996..0000000000
--- a/sample/getoptlong/return_in_order.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require 'getoptlong'
-
-options = GetoptLong.new(
- ['--xxx', GetoptLong::REQUIRED_ARGUMENT],
- ['--yyy', GetoptLong::OPTIONAL_ARGUMENT],
- ['--zzz', GetoptLong::NO_ARGUMENT]
-)
-options.ordering = GetoptLong::RETURN_IN_ORDER
-puts "Original ARGV: #{ARGV}"
-options.each do |option, argument|
- p [option, argument]
-end
-puts "Remaining ARGV: #{ARGV}"
diff --git a/sample/getoptlong/simple.rb b/sample/getoptlong/simple.rb
deleted file mode 100644
index 1af6447632..0000000000
--- a/sample/getoptlong/simple.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-require 'getoptlong'
-
-options = GetoptLong.new(
- ['--number', '-n', GetoptLong::REQUIRED_ARGUMENT],
- ['--verbose', '-v', GetoptLong::OPTIONAL_ARGUMENT],
- ['--help', '-h', GetoptLong::NO_ARGUMENT]
-)
diff --git a/sample/getoptlong/types.rb b/sample/getoptlong/types.rb
deleted file mode 100644
index ac74bfe12e..0000000000
--- a/sample/getoptlong/types.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require 'getoptlong'
-
-options = GetoptLong.new(
- ['--xxx', GetoptLong::REQUIRED_ARGUMENT],
- ['--yyy', GetoptLong::OPTIONAL_ARGUMENT],
- ['--zzz', GetoptLong::NO_ARGUMENT]
-)
-options.each do |option, argument|
- p [option, argument]
-end
diff --git a/sample/net-imap.rb b/sample/net-imap.rb
deleted file mode 100644
index b93ecb746e..0000000000
--- a/sample/net-imap.rb
+++ /dev/null
@@ -1,167 +0,0 @@
-require 'net/imap'
-require "getoptlong"
-
-$stdout.sync = true
-$port = nil
-$user = ENV["USER"] || ENV["LOGNAME"]
-$auth = "login"
-$ssl = false
-$starttls = false
-
-def usage
- <<EOF
-usage: #{$0} [options] <host>
-
- --help print this message
- --port=PORT specifies port
- --user=USER specifies user
- --auth=AUTH specifies auth type
- --starttls use starttls
- --ssl use ssl
-EOF
-end
-
-begin
- require 'io/console'
-rescue LoadError
- def _noecho(&block)
- system("stty", "-echo")
- begin
- yield STDIN
- ensure
- system("stty", "echo")
- end
- end
-else
- def _noecho(&block)
- STDIN.noecho(&block)
- end
-end
-
-def get_password
- print "password: "
- begin
- return _noecho(&:gets).chomp
- ensure
- puts
- end
-end
-
-def get_command
- printf("%s@%s> ", $user, $host)
- if line = gets
- return line.strip.split(/\s+/)
- else
- return nil
- end
-end
-
-parser = GetoptLong.new
-parser.set_options(['--debug', GetoptLong::NO_ARGUMENT],
- ['--help', GetoptLong::NO_ARGUMENT],
- ['--port', GetoptLong::REQUIRED_ARGUMENT],
- ['--user', GetoptLong::REQUIRED_ARGUMENT],
- ['--auth', GetoptLong::REQUIRED_ARGUMENT],
- ['--starttls', GetoptLong::NO_ARGUMENT],
- ['--ssl', GetoptLong::NO_ARGUMENT])
-begin
- parser.each_option do |name, arg|
- case name
- when "--port"
- $port = arg
- when "--user"
- $user = arg
- when "--auth"
- $auth = arg
- when "--ssl"
- $ssl = true
- when "--starttls"
- $starttls = true
- when "--debug"
- Net::IMAP.debug = true
- when "--help"
- usage
- exit
- end
- end
-rescue
- abort usage
-end
-
-$host = ARGV.shift
-unless $host
- abort usage
-end
-
-imap = Net::IMAP.new($host, :port => $port, :ssl => $ssl)
-begin
- imap.starttls if $starttls
- class << password = method(:get_password)
- alias to_str call
- end
- imap.authenticate($auth, $user, password)
- while true
- cmd, *args = get_command
- break unless cmd
- begin
- case cmd
- when "list"
- for mbox in imap.list("", args[0] || "*")
- if mbox.attr.include?(Net::IMAP::NOSELECT)
- prefix = "!"
- elsif mbox.attr.include?(Net::IMAP::MARKED)
- prefix = "*"
- else
- prefix = " "
- end
- print prefix, mbox.name, "\n"
- end
- when "select"
- imap.select(args[0] || "inbox")
- print "ok\n"
- when "close"
- imap.close
- print "ok\n"
- when "summary"
- unless messages = imap.responses["EXISTS"][-1]
- puts "not selected"
- next
- end
- if messages > 0
- for data in imap.fetch(1..-1, ["ENVELOPE"])
- print data.seqno, ": ", data.attr["ENVELOPE"].subject, "\n"
- end
- else
- puts "no message"
- end
- when "fetch"
- if args[0]
- data = imap.fetch(args[0].to_i, ["RFC822.HEADER", "RFC822.TEXT"])[0]
- puts data.attr["RFC822.HEADER"]
- puts data.attr["RFC822.TEXT"]
- else
- puts "missing argument"
- end
- when "logout", "exit", "quit"
- break
- when "help", "?"
- print <<EOF
-list [pattern] list mailboxes
-select [mailbox] select mailbox
-close close mailbox
-summary display summary
-fetch [msgno] display message
-logout logout
-help, ? display help message
-EOF
- else
- print "unknown command: ", cmd, "\n"
- end
- rescue Net::IMAP::Error
- puts $!
- end
- end
-ensure
- imap.logout
- imap.disconnect
-end
diff --git a/ext/win32ole/sample/excel1.rb b/sample/win32ole/excel1.rb
index 4fe1d0c2a9..4fe1d0c2a9 100644
--- a/ext/win32ole/sample/excel1.rb
+++ b/sample/win32ole/excel1.rb
diff --git a/ext/win32ole/sample/excel2.rb b/sample/win32ole/excel2.rb
index 47a5715f84..47a5715f84 100644
--- a/ext/win32ole/sample/excel2.rb
+++ b/sample/win32ole/excel2.rb
diff --git a/ext/win32ole/sample/excel3.rb b/sample/win32ole/excel3.rb
index 72aee2a929..72aee2a929 100644
--- a/ext/win32ole/sample/excel3.rb
+++ b/sample/win32ole/excel3.rb
diff --git a/ext/win32ole/sample/ie.rb b/sample/win32ole/ie.rb
index 4db64eed30..4db64eed30 100644
--- a/ext/win32ole/sample/ie.rb
+++ b/sample/win32ole/ie.rb
diff --git a/ext/win32ole/sample/ieconst.rb b/sample/win32ole/ieconst.rb
index 363a4f8153..363a4f8153 100644
--- a/ext/win32ole/sample/ieconst.rb
+++ b/sample/win32ole/ieconst.rb
diff --git a/ext/win32ole/sample/ienavi.rb b/sample/win32ole/ienavi.rb
index 5d0536028b..5d0536028b 100644
--- a/ext/win32ole/sample/ienavi.rb
+++ b/sample/win32ole/ienavi.rb
diff --git a/ext/win32ole/sample/ienavi2.rb b/sample/win32ole/ienavi2.rb
index 3248393077..3248393077 100644
--- a/ext/win32ole/sample/ienavi2.rb
+++ b/sample/win32ole/ienavi2.rb
diff --git a/ext/win32ole/sample/oledirs.rb b/sample/win32ole/oledirs.rb
index e52a0fd7ac..e52a0fd7ac 100644
--- a/ext/win32ole/sample/oledirs.rb
+++ b/sample/win32ole/oledirs.rb
diff --git a/sample/win32ole/olegen.rb b/sample/win32ole/olegen.rb
new file mode 100644
index 0000000000..9398194cf1
--- /dev/null
+++ b/sample/win32ole/olegen.rb
@@ -0,0 +1,348 @@
+# frozen_string_literal: false
+#-----------------------------
+# olegen.rb
+# $Revision$
+#-----------------------------
+
+require 'win32ole'
+
+class WIN32COMGen
+ def initialize(typelib)
+ @typelib = typelib
+ @receiver = ""
+ end
+ attr_reader :typelib
+
+ def ole_classes(typelib)
+ begin
+ @ole = WIN32OLE.new(typelib)
+ [@ole.ole_obj_help]
+ rescue
+ WIN32OLE_TYPE.ole_classes(typelib)
+ end
+ end
+
+ def generate_args(method)
+ args = []
+ if method.size_opt_params >= 0
+ size_required_params = method.size_params - method.size_opt_params
+ else
+ size_required_params = method.size_params - 1
+ end
+ size_required_params.times do |i|
+ if method.params[i] && method.params[i].optional?
+ args.push "arg#{i}=nil"
+ else
+ args.push "arg#{i}"
+ end
+ end
+ if method.size_opt_params >= 0
+ method.size_opt_params.times do |i|
+ args.push "arg#{i + size_required_params}=nil"
+ end
+ else
+ args.push "*arg"
+ end
+ args.join(", ")
+ end
+
+ def generate_argtype(typedetails)
+ ts = ''
+ typedetails.each do |t|
+ case t
+ when 'CARRAY', 'VOID', 'UINT', 'RESULT', 'DECIMAL', 'I8', 'UI8'
+# raise "Sorry type\"" + t + "\" not supported"
+ ts << "\"??? NOT SUPPORTED TYPE:`#{t}'\""
+ when 'USERDEFINED', 'Unknown Type 9'
+ ts << 'VT_DISPATCH'
+ break;
+ when 'SAFEARRAY'
+ ts << 'VT_ARRAY|'
+ when 'PTR'
+ ts << 'VT_BYREF|'
+ when 'INT'
+ ts << 'VT_I4'
+ else
+ if String === t
+ ts << 'VT_' + t
+ end
+ end
+ end
+ if ts.empty?
+ ts = 'VT_VARIANT'
+ elsif ts.end_with?(?|)
+ ts += 'VT_VARIANT'
+ end
+ ts
+ end
+
+ def generate_argtypes(method, proptypes)
+ types = method.params.collect{|param|
+ generate_argtype(param.ole_type_detail)
+ }.join(", ")
+ if proptypes
+ types += ", " if types.size > 0
+ types += generate_argtype(proptypes)
+ end
+ types
+ end
+
+ def generate_method_body(method, disptype, types=nil)
+ " ret = #{@receiver}#{disptype}(#{method.dispid}, [" +
+ generate_args(method).gsub("=nil", "") +
+ "], [" +
+ generate_argtypes(method, types) +
+ "])\n" +
+ " @lastargs = WIN32OLE::ARGV\n" +
+ " ret"
+ end
+
+ def generate_method_help(method, type = nil)
+ str = " # "
+ if type
+ str += type
+ else
+ str += method.return_type
+ end
+ str += " #{method.name}"
+ if method.event?
+ str += " EVENT"
+ str += " in #{method.event_interface}"
+ end
+ if method.helpstring && method.helpstring != ""
+ str += "\n # "
+ str += method.helpstring
+ end
+ args_help = generate_method_args_help(method)
+ if args_help
+ str += "\n"
+ str += args_help
+ end
+ str
+ end
+
+ def generate_method_args_help(method)
+ args = []
+ method.params.each_with_index {|param, i|
+ h = " # #{param.ole_type} arg#{i} --- #{param.name}"
+ inout = []
+ inout.push "IN" if param.input?
+ inout.push "OUT" if param.output?
+ h += " [#{inout.join('/')}]"
+ h += " ( = #{param.default})" if param.default
+ args.push h
+ }
+ if args.size > 0
+ args.join("\n")
+ else
+ nil
+ end
+ end
+
+ def generate_method(method, disptype, io = STDOUT, types = nil)
+ io.puts "\n"
+ io.puts generate_method_help(method)
+ if method.invoke_kind == 'PROPERTYPUT'
+ io.print " def #{method.name}=("
+ else
+ io.print " def #{method.name}("
+ end
+ io.print generate_args(method)
+ io.puts ")"
+ io.puts generate_method_body(method, disptype, types)
+ io.puts " end"
+ end
+
+ def generate_propputref_methods(klass, io = STDOUT)
+ klass.ole_methods.select {|method|
+ method.invoke_kind == 'PROPERTYPUTREF' && method.visible?
+ }.each do |method|
+ generate_method(method, io)
+ end
+ end
+
+ def generate_properties_with_args(klass, io = STDOUT)
+ klass.ole_methods.select {|method|
+ method.invoke_kind == 'PROPERTYGET' &&
+ method.visible? &&
+ method.size_params > 0
+ }.each do |method|
+ types = method.return_type_detail
+ io.puts "\n"
+ io.puts generate_method_help(method, types[0])
+ io.puts " def #{method.name}"
+ if klass.ole_type == "Class"
+ io.print " OLEProperty.new(@dispatch, #{method.dispid}, ["
+ else
+ io.print " OLEProperty.new(self, #{method.dispid}, ["
+ end
+ io.print generate_argtypes(method, nil)
+ io.print "], ["
+ io.print generate_argtypes(method, types)
+ io.puts "])"
+ io.puts " end"
+ end
+ end
+
+ def generate_propput_methods(klass, io = STDOUT)
+ klass.ole_methods.select {|method|
+ method.invoke_kind == 'PROPERTYPUT' && method.visible? &&
+ method.size_params == 1
+ }.each do |method|
+ ms = klass.ole_methods.select {|m|
+ m.invoke_kind == 'PROPERTYGET' &&
+ m.dispid == method.dispid
+ }
+ types = []
+ if ms.size == 1
+ types = ms[0].return_type_detail
+ end
+ generate_method(method, '_setproperty', io, types)
+ end
+ end
+
+ def generate_propget_methods(klass, io = STDOUT)
+ klass.ole_methods.select {|method|
+ method.invoke_kind == 'PROPERTYGET' && method.visible? &&
+ method.size_params == 0
+ }.each do |method|
+ generate_method(method, '_getproperty', io)
+ end
+ end
+
+ def generate_func_methods(klass, io = STDOUT)
+ klass.ole_methods.select {|method|
+ method.invoke_kind == "FUNC" && method.visible?
+ }.each do |method|
+ generate_method(method, '_invoke', io)
+ end
+ end
+
+ def generate_methods(klass, io = STDOUT)
+ generate_propget_methods(klass, io)
+ generate_propput_methods(klass, io)
+ generate_properties_with_args(klass, io)
+ generate_func_methods(klass, io)
+# generate_propputref_methods(klass, io)
+ end
+
+ def generate_constants(klass, io = STDOUT)
+ klass.variables.select {|v|
+ v.visible? && v.variable_kind == 'CONSTANT'
+ }.each do |v|
+ io.print " "
+ io.print v.name.sub(/^./){$&.upcase}
+ io.print " = "
+ io.puts v.value
+ end
+ end
+
+ def class_name(klass)
+ klass_name = klass.name
+ if klass.ole_type == "Class" &&
+ klass.guid &&
+ klass.progid
+ klass_name = klass.progid.gsub(/\./, '_')
+ end
+ if /^[A-Z]/ !~ klass_name || Module.constants.include?(klass_name)
+ klass_name = 'OLE' + klass_name
+ end
+ klass_name
+ end
+
+ def define_initialize(klass)
+ <<STR
+
+ def initialize(obj = nil)
+ @clsid = "#{klass.guid}"
+ @progid = "#{klass.progid}"
+ if obj.nil?
+ @dispatch = WIN32OLE.new @progid
+ else
+ @dispatch = obj
+ end
+ end
+STR
+ end
+
+ def define_include
+ " include WIN32OLE::VARIANT"
+ end
+
+ def define_instance_variables
+ " attr_reader :lastargs"
+ end
+
+ def define_method_missing
+ <<STR
+
+ def method_missing(cmd, *arg)
+ @dispatch.method_missing(cmd, *arg)
+ end
+STR
+ end
+
+ def define_class(klass, io = STDOUT)
+ io.puts "class #{class_name(klass)} # #{klass.name}"
+ io.puts define_include
+ io.puts define_instance_variables
+ io.puts " attr_reader :dispatch"
+ io.puts " attr_reader :clsid"
+ io.puts " attr_reader :progid"
+ io.puts define_initialize(klass)
+ io.puts define_method_missing
+ end
+
+ def define_module(klass, io = STDOUT)
+ io.puts "module #{class_name(klass)}"
+ io.puts define_include
+ io.puts define_instance_variables
+ end
+
+ def generate_class(klass, io = STDOUT)
+ io.puts "\n# #{klass.helpstring}"
+ if klass.ole_type == "Class" &&
+ klass.guid &&
+ klass.progid
+ @receiver = "@dispatch."
+ define_class(klass, io)
+ else
+ @receiver = ""
+ define_module(klass, io)
+ end
+ generate_constants(klass, io)
+ generate_methods(klass, io)
+ io.puts "end"
+ end
+
+ def generate(io = STDOUT)
+ io.puts "require 'win32ole'"
+ io.puts "require 'win32ole/property'"
+
+ ole_classes(typelib).select{|klass|
+ klass.visible? &&
+ (klass.ole_type == "Class" ||
+ klass.ole_type == "Interface" ||
+ klass.ole_type == "Dispatch" ||
+ klass.ole_type == "Enum")
+ }.each do |klass|
+ generate_class(klass, io)
+ end
+ begin
+ @ole.quit if @ole
+ rescue
+ end
+ end
+end
+
+require 'win32ole'
+if __FILE__ == $0
+ if ARGV.size == 0
+ $stderr.puts "usage: #{$0} Type Library [...]"
+ exit 1
+ end
+ ARGV.each do |typelib|
+ comgen = WIN32COMGen.new(typelib)
+ comgen.generate
+ end
+end
diff --git a/ext/win32ole/sample/xml.rb b/sample/win32ole/xml.rb
index 5a239c9336..5a239c9336 100644
--- a/ext/win32ole/sample/xml.rb
+++ b/sample/win32ole/xml.rb
diff --git a/scheduler.c b/scheduler.c
index 866e53993f..3159635dba 100644
--- a/scheduler.c
+++ b/scheduler.c
@@ -161,6 +161,21 @@ verify_interface(VALUE scheduler)
}
}
+static VALUE
+fiber_scheduler_close(VALUE scheduler)
+{
+ return rb_fiber_scheduler_close(scheduler);
+}
+
+static VALUE
+fiber_scheduler_close_ensure(VALUE _thread)
+{
+ rb_thread_t *thread = (rb_thread_t*)_thread;
+ thread->scheduler = Qnil;
+
+ return Qnil;
+}
+
VALUE
rb_fiber_scheduler_set(VALUE scheduler)
{
@@ -178,7 +193,8 @@ rb_fiber_scheduler_set(VALUE scheduler)
// That way, we do not need to consider interactions, e.g., of a Fiber from
// the previous scheduler with the new scheduler.
if (thread->scheduler != Qnil) {
- rb_fiber_scheduler_close(thread->scheduler);
+ // rb_fiber_scheduler_close(thread->scheduler);
+ rb_ensure(fiber_scheduler_close, thread->scheduler, fiber_scheduler_close_ensure, (VALUE)thread);
}
thread->scheduler = scheduler;
@@ -458,7 +474,7 @@ VALUE rb_fiber_scheduler_io_selectv(VALUE scheduler, int argc, VALUE *argv)
/*
* Document-method: Fiber::Scheduler#io_read
- * call-seq: io_read(io, buffer, length) -> read length or -errno
+ * call-seq: io_read(io, buffer, length, offset) -> read length or -errno
*
* Invoked by IO#read or IO#Buffer.read to read +length+ bytes from +io+ into a
* specified +buffer+ (see IO::Buffer) at the given +offset+.
@@ -518,7 +534,7 @@ rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, rb_off_t from, VALUE buff
/*
* Document-method: Scheduler#io_write
- * call-seq: io_write(io, buffer, length) -> written length or -errno
+ * call-seq: io_write(io, buffer, length, offset) -> written length or -errno
*
* Invoked by IO#write or IO::Buffer#write to write +length+ bytes to +io+ from
* from a specified +buffer+ (see IO::Buffer) at the given +offset+.
diff --git a/shape.c b/shape.c
index dcdb9d28aa..68c74034e7 100644
--- a/shape.c
+++ b/shape.c
@@ -4,26 +4,294 @@
#include "symbol.h"
#include "id_table.h"
#include "internal/class.h"
+#include "internal/error.h"
#include "internal/gc.h"
+#include "internal/object.h"
#include "internal/symbol.h"
#include "internal/variable.h"
-#include "internal/error.h"
#include "variable.h"
#include <stdbool.h>
+#ifndef _WIN32
+#include <sys/mman.h>
+#endif
+
#ifndef SHAPE_DEBUG
#define SHAPE_DEBUG (VM_CHECK_MODE > 0)
#endif
+#if SIZEOF_SHAPE_T == 4
+#if RUBY_DEBUG
+#define SHAPE_BUFFER_SIZE 0x8000
+#else
+#define SHAPE_BUFFER_SIZE 0x80000
+#endif
+#else
+#define SHAPE_BUFFER_SIZE 0x8000
+#endif
+
+#define REDBLACK_CACHE_SIZE (SHAPE_BUFFER_SIZE * 32)
+
#define SINGLE_CHILD_TAG 0x1
#define TAG_SINGLE_CHILD(x) (struct rb_id_table *)((uintptr_t)x | SINGLE_CHILD_TAG)
#define SINGLE_CHILD_MASK (~((uintptr_t)SINGLE_CHILD_TAG))
#define SINGLE_CHILD_P(x) (((uintptr_t)x) & SINGLE_CHILD_TAG)
#define SINGLE_CHILD(x) (rb_shape_t *)((uintptr_t)x & SINGLE_CHILD_MASK)
+#define ANCESTOR_CACHE_THRESHOLD 10
+#define MAX_SHAPE_ID (SHAPE_BUFFER_SIZE - 1)
+#define ANCESTOR_SEARCH_MAX_DEPTH 2
static ID id_frozen;
static ID id_t_object;
-static ID size_pool_edge_names[SIZE_POOL_COUNT];
+
+#define LEAF 0
+#define BLACK 0x0
+#define RED 0x1
+
+static redblack_node_t *
+redblack_left(redblack_node_t * node)
+{
+ if (node->l == LEAF) {
+ return LEAF;
+ }
+ else {
+ RUBY_ASSERT(node->l < GET_SHAPE_TREE()->cache_size);
+ redblack_node_t * left = &GET_SHAPE_TREE()->shape_cache[node->l - 1];
+ return left;
+ }
+}
+
+static redblack_node_t *
+redblack_right(redblack_node_t * node)
+{
+ if (node->r == LEAF) {
+ return LEAF;
+ }
+ else {
+ RUBY_ASSERT(node->r < GET_SHAPE_TREE()->cache_size);
+ redblack_node_t * right = &GET_SHAPE_TREE()->shape_cache[node->r - 1];
+ return right;
+ }
+}
+
+static redblack_node_t *
+redblack_find(redblack_node_t * tree, ID key)
+{
+ if (tree == LEAF) {
+ return LEAF;
+ }
+ else {
+ RUBY_ASSERT(redblack_left(tree) == LEAF || redblack_left(tree)->key < tree->key);
+ RUBY_ASSERT(redblack_right(tree) == LEAF || redblack_right(tree)->key > tree->key);
+
+ if (tree->key == key) {
+ return tree;
+ }
+ else {
+ if (key < tree->key) {
+ return redblack_find(redblack_left(tree), key);
+ }
+ else {
+ return redblack_find(redblack_right(tree), key);
+ }
+ }
+ }
+}
+
+static inline char
+redblack_color(redblack_node_t * node)
+{
+ return node && ((uintptr_t)node->value & RED);
+}
+
+static inline bool
+redblack_red_p(redblack_node_t * node)
+{
+ return redblack_color(node) == RED;
+}
+
+static inline rb_shape_t *
+redblack_value(redblack_node_t * node)
+{
+ // Color is stored in the bottom bit of the shape pointer
+ // Mask away the bit so we get the actual pointer back
+ return (rb_shape_t *)((uintptr_t)node->value & (((uintptr_t)-1) - 1));
+}
+
+static redblack_id_t
+redblack_id_for(redblack_node_t * node)
+{
+ RUBY_ASSERT(node || node == LEAF);
+ if (node == LEAF) {
+ return 0;
+ }
+ else {
+ redblack_node_t * redblack_nodes = GET_SHAPE_TREE()->shape_cache;
+ redblack_id_t id = (redblack_id_t)(node - redblack_nodes);
+ return id + 1;
+ }
+}
+
+static redblack_node_t *
+redblack_new(char color, ID key, rb_shape_t * value, redblack_node_t * left, redblack_node_t * right)
+{
+ if (GET_SHAPE_TREE()->cache_size + 1 >= REDBLACK_CACHE_SIZE) {
+ // We're out of cache, just quit
+ return LEAF;
+ }
+
+ RUBY_ASSERT(left == LEAF || left->key < key);
+ RUBY_ASSERT(right == LEAF || right->key > key);
+
+ redblack_node_t * redblack_nodes = GET_SHAPE_TREE()->shape_cache;
+ redblack_node_t * node = &redblack_nodes[(GET_SHAPE_TREE()->cache_size)++];
+ node->key = key;
+ node->value = (rb_shape_t *)((uintptr_t)value | color);
+ node->l = redblack_id_for(left);
+ node->r = redblack_id_for(right);
+ return node;
+}
+
+static redblack_node_t *
+redblack_balance(char color, ID key, rb_shape_t * value, redblack_node_t * left, redblack_node_t * right)
+{
+ if (color == BLACK) {
+ ID new_key, new_left_key, new_right_key;
+ rb_shape_t *new_value, *new_left_value, *new_right_value;
+ redblack_node_t *new_left_left, *new_left_right, *new_right_left, *new_right_right;
+
+ if (redblack_red_p(left) && redblack_red_p(redblack_left(left))) {
+ new_right_key = key;
+ new_right_value = value;
+ new_right_right = right;
+
+ new_key = left->key;
+ new_value = redblack_value(left);
+ new_right_left = redblack_right(left);
+
+ new_left_key = redblack_left(left)->key;
+ new_left_value = redblack_value(redblack_left(left));
+
+ new_left_left = redblack_left(redblack_left(left));
+ new_left_right = redblack_right(redblack_left(left));
+ }
+ else if (redblack_red_p(left) && redblack_red_p(redblack_right(left))) {
+ new_right_key = key;
+ new_right_value = value;
+ new_right_right = right;
+
+ new_left_key = left->key;
+ new_left_value = redblack_value(left);
+ new_left_left = redblack_left(left);
+
+ new_key = redblack_right(left)->key;
+ new_value = redblack_value(redblack_right(left));
+ new_left_right = redblack_left(redblack_right(left));
+ new_right_left = redblack_right(redblack_right(left));
+ }
+ else if (redblack_red_p(right) && redblack_red_p(redblack_left(right))) {
+ new_left_key = key;
+ new_left_value = value;
+ new_left_left = left;
+
+ new_right_key = right->key;
+ new_right_value = redblack_value(right);
+ new_right_right = redblack_right(right);
+
+ new_key = redblack_left(right)->key;
+ new_value = redblack_value(redblack_left(right));
+ new_left_right = redblack_left(redblack_left(right));
+ new_right_left = redblack_right(redblack_left(right));
+ }
+ else if (redblack_red_p(right) && redblack_red_p(redblack_right(right))) {
+ new_left_key = key;
+ new_left_value = value;
+ new_left_left = left;
+
+ new_key = right->key;
+ new_value = redblack_value(right);
+ new_left_right = redblack_left(right);
+
+ new_right_key = redblack_right(right)->key;
+ new_right_value = redblack_value(redblack_right(right));
+ new_right_left = redblack_left(redblack_right(right));
+ new_right_right = redblack_right(redblack_right(right));
+ }
+ else {
+ return redblack_new(color, key, value, left, right);
+ }
+
+ RUBY_ASSERT(new_left_key < new_key);
+ RUBY_ASSERT(new_right_key > new_key);
+ RUBY_ASSERT(new_left_left == LEAF || new_left_left->key < new_left_key);
+ RUBY_ASSERT(new_left_right == LEAF || new_left_right->key > new_left_key);
+ RUBY_ASSERT(new_left_right == LEAF || new_left_right->key < new_key);
+ RUBY_ASSERT(new_right_left == LEAF || new_right_left->key < new_right_key);
+ RUBY_ASSERT(new_right_left == LEAF || new_right_left->key > new_key);
+ RUBY_ASSERT(new_right_right == LEAF || new_right_right->key > new_right_key);
+
+ return redblack_new(
+ RED, new_key, new_value,
+ redblack_new(BLACK, new_left_key, new_left_value, new_left_left, new_left_right),
+ redblack_new(BLACK, new_right_key, new_right_value, new_right_left, new_right_right));
+ }
+
+ return redblack_new(color, key, value, left, right);
+}
+
+static redblack_node_t *
+redblack_insert_aux(redblack_node_t * tree, ID key, rb_shape_t * value)
+{
+ if (tree == LEAF) {
+ return redblack_new(RED, key, value, LEAF, LEAF);
+ }
+ else {
+ redblack_node_t *left, *right;
+ if (key < tree->key) {
+ left = redblack_insert_aux(redblack_left(tree), key, value);
+ RUBY_ASSERT(left != LEAF);
+ right = redblack_right(tree);
+ RUBY_ASSERT(right == LEAF || right->key > tree->key);
+ }
+ else if (key > tree->key) {
+ left = redblack_left(tree);
+ RUBY_ASSERT(left == LEAF || left->key < tree->key);
+ right = redblack_insert_aux(redblack_right(tree), key, value);
+ RUBY_ASSERT(right != LEAF);
+ }
+ else {
+ return tree;
+ }
+
+ return redblack_balance(
+ redblack_color(tree),
+ tree->key,
+ redblack_value(tree),
+ left,
+ right
+ );
+ }
+}
+
+static redblack_node_t *
+redblack_force_black(redblack_node_t * node)
+{
+ node->value = redblack_value(node);
+ return node;
+}
+
+static redblack_node_t *
+redblack_insert(redblack_node_t * tree, ID key, rb_shape_t * value)
+{
+ redblack_node_t * root = redblack_insert_aux(tree, key, value);
+
+ if (redblack_red_p(root)) {
+ return redblack_force_black(root);
+ }
+ else {
+ return root;
+ }
+}
rb_shape_tree_t *rb_shape_tree_ptr = NULL;
@@ -53,7 +321,7 @@ rb_shape_each_shape(each_shape_callback callback, void *data)
}
}
-RUBY_FUNC_EXPORTED rb_shape_t*
+RUBY_FUNC_EXPORTED rb_shape_t *
rb_shape_get_shape_by_id(shape_id_t shape_id)
{
RUBY_ASSERT(shape_id != INVALID_SHAPE_ID);
@@ -120,7 +388,7 @@ shape_alloc(void)
shape_id_t shape_id = GET_SHAPE_TREE()->next_shape_id;
GET_SHAPE_TREE()->next_shape_id++;
- if (shape_id == MAX_SHAPE_ID) {
+ if (shape_id == (MAX_SHAPE_ID + 1)) {
// TODO: Make an OutOfShapesError ??
rb_bug("Out of shapes");
}
@@ -152,6 +420,41 @@ rb_shape_alloc(ID edge_name, rb_shape_t * parent, enum shape_type type)
return shape;
}
+#ifdef HAVE_MMAP
+static redblack_node_t *
+redblack_cache_ancestors(rb_shape_t * shape)
+{
+ if (!(shape->ancestor_index || shape->parent_id == INVALID_SHAPE_ID)) {
+ redblack_node_t * parent_index;
+
+ parent_index = redblack_cache_ancestors(rb_shape_get_parent(shape));
+
+ if (shape->type == SHAPE_IVAR) {
+ shape->ancestor_index = redblack_insert(parent_index, shape->edge_name, shape);
+
+#if RUBY_DEBUG
+ if (shape->ancestor_index) {
+ redblack_node_t *inserted_node = redblack_find(shape->ancestor_index, shape->edge_name);
+ RUBY_ASSERT(inserted_node);
+ RUBY_ASSERT(redblack_value(inserted_node) == shape);
+ }
+#endif
+ }
+ else {
+ shape->ancestor_index = parent_index;
+ }
+ }
+
+ return shape->ancestor_index;
+}
+#else
+static redblack_node_t *
+redblack_cache_ancestors(rb_shape_t * shape)
+{
+ return LEAF;
+}
+#endif
+
static rb_shape_t *
rb_shape_alloc_new_child(ID id, rb_shape_t * shape, enum shape_type shape_type)
{
@@ -159,16 +462,22 @@ rb_shape_alloc_new_child(ID id, rb_shape_t * shape, enum shape_type shape_type)
switch (shape_type) {
case SHAPE_IVAR:
+ if (UNLIKELY(shape->next_iv_index >= shape->capacity)) {
+ RUBY_ASSERT(shape->next_iv_index == shape->capacity);
+ new_shape->capacity = (uint32_t)rb_malloc_grow_capa(shape->capacity, sizeof(VALUE));
+ }
+ RUBY_ASSERT(new_shape->capacity > shape->next_iv_index);
new_shape->next_iv_index = shape->next_iv_index + 1;
+ if (new_shape->next_iv_index > ANCESTOR_CACHE_THRESHOLD) {
+ redblack_cache_ancestors(new_shape);
+ }
break;
- case SHAPE_CAPACITY_CHANGE:
case SHAPE_FROZEN:
- case SHAPE_T_OBJECT:
new_shape->next_iv_index = shape->next_iv_index;
break;
case SHAPE_OBJ_TOO_COMPLEX:
- case SHAPE_INITIAL_CAPACITY:
case SHAPE_ROOT:
+ case SHAPE_T_OBJECT:
rb_bug("Unreachable");
break;
}
@@ -177,7 +486,7 @@ rb_shape_alloc_new_child(ID id, rb_shape_t * shape, enum shape_type shape_type)
}
static rb_shape_t*
-get_next_shape_internal(rb_shape_t * shape, ID id, enum shape_type shape_type, bool * variation_created, bool new_shapes_allowed, bool new_shape_necessary)
+get_next_shape_internal(rb_shape_t * shape, ID id, enum shape_type shape_type, bool * variation_created, bool new_variations_allowed)
{
rb_shape_t *res = NULL;
@@ -186,56 +495,60 @@ get_next_shape_internal(rb_shape_t * shape, ID id, enum shape_type shape_type, b
*variation_created = false;
- if (new_shape_necessary || (new_shapes_allowed && (shape->next_iv_index < SHAPE_MAX_NUM_IVS))) {
- RB_VM_LOCK_ENTER();
- {
- // If the current shape has children
- if (shape->edges) {
- // Check if it only has one child
- if (SINGLE_CHILD_P(shape->edges)) {
- rb_shape_t * child = SINGLE_CHILD(shape->edges);
- // If the one child has a matching edge name, then great,
- // we found what we want.
- if (child->edge_name == id) {
- res = child;
- }
- else {
- // Otherwise we're going to have to create a new shape
- // and insert it as a child node, so create an id
- // table and insert the existing child
- shape->edges = rb_id_table_create(2);
- rb_id_table_insert(shape->edges, child->edge_name, (VALUE)child);
- }
+ RB_VM_LOCK_ENTER();
+ {
+ // If the current shape has children
+ if (shape->edges) {
+ // Check if it only has one child
+ if (SINGLE_CHILD_P(shape->edges)) {
+ rb_shape_t * child = SINGLE_CHILD(shape->edges);
+ // If the one child has a matching edge name, then great,
+ // we found what we want.
+ if (child->edge_name == id) {
+ res = child;
}
- else {
- // If it has more than one child, do a hash lookup to find it.
- VALUE lookup_result;
- if (rb_id_table_lookup(shape->edges, id, &lookup_result)) {
- res = (rb_shape_t *)lookup_result;
- }
+ }
+ else {
+ // If it has more than one child, do a hash lookup to find it.
+ VALUE lookup_result;
+ if (rb_id_table_lookup(shape->edges, id, &lookup_result)) {
+ res = (rb_shape_t *)lookup_result;
}
+ }
+ }
- // If the shape we were looking for above was found,
- // then `res` will be set to the child. If it isn't set, then
- // we know we need a new child shape, and that we must insert
- // it in to the table.
- if (!res) {
- *variation_created = true;
- rb_shape_t * new_shape = rb_shape_alloc_new_child(id, shape, shape_type);
- rb_id_table_insert(shape->edges, id, (VALUE)new_shape);
- res = new_shape;
- }
+ // If we didn't find the shape we're looking for we create it.
+ if (!res) {
+ // If we're not allowed to create a new variation, of if we're out of shapes
+ // we return TOO_COMPLEX_SHAPE.
+ if (!new_variations_allowed || GET_SHAPE_TREE()->next_shape_id > MAX_SHAPE_ID) {
+ res = rb_shape_get_shape_by_id(OBJ_TOO_COMPLEX_SHAPE_ID);
}
else {
- // If the shape didn't have any outgoing edges, then create
- // the new outgoing edge and tag the pointer.
rb_shape_t * new_shape = rb_shape_alloc_new_child(id, shape, shape_type);
- shape->edges = TAG_SINGLE_CHILD(new_shape);
+
+ if (!shape->edges) {
+ // If the shape had no edge yet, we can directly set the new child
+ shape->edges = TAG_SINGLE_CHILD(new_shape);
+ }
+ else {
+ // If the edge was single child we need to allocate a table.
+ if (SINGLE_CHILD_P(shape->edges)) {
+ rb_shape_t * old_child = SINGLE_CHILD(shape->edges);
+ shape->edges = rb_id_table_create(2);
+ rb_id_table_insert(shape->edges, old_child->edge_name, (VALUE)old_child);
+ }
+
+ rb_id_table_insert(shape->edges, new_shape->edge_name, (VALUE)new_shape);
+ *variation_created = true;
+ }
+
res = new_shape;
}
}
- RB_VM_LOCK_LEAVE();
}
+ RB_VM_LOCK_LEAVE();
+
return res;
}
@@ -245,29 +558,8 @@ rb_shape_frozen_shape_p(rb_shape_t* shape)
return SHAPE_FROZEN == (enum shape_type)shape->type;
}
-static void
-move_iv(VALUE obj, ID id, attr_index_t from, attr_index_t to)
-{
- switch(BUILTIN_TYPE(obj)) {
- case T_CLASS:
- case T_MODULE:
- RCLASS_IVPTR(obj)[to] = RCLASS_IVPTR(obj)[from];
- break;
- case T_OBJECT:
- RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
- ROBJECT_IVPTR(obj)[to] = ROBJECT_IVPTR(obj)[from];
- break;
- default: {
- struct gen_ivtbl *ivtbl;
- rb_gen_ivtbl_get(obj, id, &ivtbl);
- ivtbl->ivptr[to] = ivtbl->ivptr[from];
- break;
- }
- }
-}
-
static rb_shape_t *
-remove_shape_recursive(VALUE obj, ID id, rb_shape_t * shape, VALUE * removed)
+remove_shape_recursive(rb_shape_t *shape, ID id, rb_shape_t **removed_shape)
{
if (shape->parent_id == INVALID_SHAPE_ID) {
// We've hit the top of the shape tree and couldn't find the
@@ -276,43 +568,29 @@ remove_shape_recursive(VALUE obj, ID id, rb_shape_t * shape, VALUE * removed)
}
else {
if (shape->type == SHAPE_IVAR && shape->edge_name == id) {
- // We've hit the edge we wanted to remove, return it's _parent_
- // as the new parent while we go back down the stack.
- attr_index_t index = shape->next_iv_index - 1;
-
- switch(BUILTIN_TYPE(obj)) {
- case T_CLASS:
- case T_MODULE:
- *removed = RCLASS_IVPTR(obj)[index];
- break;
- case T_OBJECT:
- *removed = ROBJECT_IVPTR(obj)[index];
- break;
- default: {
- struct gen_ivtbl *ivtbl;
- rb_gen_ivtbl_get(obj, id, &ivtbl);
- *removed = ivtbl->ivptr[index];
- break;
- }
- }
+ *removed_shape = shape;
+
return rb_shape_get_parent(shape);
}
else {
// This isn't the IV we want to remove, keep walking up.
- rb_shape_t * new_parent = remove_shape_recursive(obj, id, rb_shape_get_parent(shape), removed);
+ rb_shape_t *new_parent = remove_shape_recursive(rb_shape_get_parent(shape), id, removed_shape);
// We found a new parent. Create a child of the new parent that
// has the same attributes as this shape.
if (new_parent) {
+ if (UNLIKELY(new_parent->type == SHAPE_OBJ_TOO_COMPLEX)) {
+ return new_parent;
+ }
+
bool dont_care;
- enum ruby_value_type type = BUILTIN_TYPE(obj);
- bool new_shape_necessary = type != T_OBJECT;
- rb_shape_t * new_child = get_next_shape_internal(new_parent, shape->edge_name, shape->type, &dont_care, true, new_shape_necessary);
- new_child->capacity = shape->capacity;
- if (new_child->type == SHAPE_IVAR) {
- move_iv(obj, id, shape->next_iv_index - 1, new_child->next_iv_index - 1);
+ rb_shape_t *new_child = get_next_shape_internal(new_parent, shape->edge_name, shape->type, &dont_care, true);
+ if (UNLIKELY(new_child->type == SHAPE_OBJ_TOO_COMPLEX)) {
+ return new_child;
}
+ RUBY_ASSERT(new_child->capacity <= shape->capacity);
+
return new_child;
}
else {
@@ -324,16 +602,63 @@ remove_shape_recursive(VALUE obj, ID id, rb_shape_t * shape, VALUE * removed)
}
}
-void
-rb_shape_transition_shape_remove_ivar(VALUE obj, ID id, rb_shape_t *shape, VALUE * removed)
+bool
+rb_shape_transition_shape_remove_ivar(VALUE obj, ID id, rb_shape_t *shape, VALUE *removed)
{
- rb_shape_t * new_shape = remove_shape_recursive(obj, id, shape, removed);
+ if (UNLIKELY(shape->type == SHAPE_OBJ_TOO_COMPLEX)) {
+ return false;
+ }
+
+ rb_shape_t *removed_shape = NULL;
+ rb_shape_t *new_shape = remove_shape_recursive(shape, id, &removed_shape);
if (new_shape) {
+ RUBY_ASSERT(removed_shape != NULL);
+
+ if (UNLIKELY(new_shape->type == SHAPE_OBJ_TOO_COMPLEX)) {
+ return false;
+ }
+
+ RUBY_ASSERT(new_shape->next_iv_index == shape->next_iv_index - 1);
+
+ VALUE *ivptr;
+ switch(BUILTIN_TYPE(obj)) {
+ case T_CLASS:
+ case T_MODULE:
+ ivptr = RCLASS_IVPTR(obj);
+ break;
+ case T_OBJECT:
+ ivptr = ROBJECT_IVPTR(obj);
+ break;
+ default: {
+ struct gen_ivtbl *ivtbl;
+ rb_gen_ivtbl_get(obj, id, &ivtbl);
+ ivptr = ivtbl->as.shape.ivptr;
+ break;
+ }
+ }
+
+ *removed = ivptr[removed_shape->next_iv_index - 1];
+
+ memmove(&ivptr[removed_shape->next_iv_index - 1], &ivptr[removed_shape->next_iv_index],
+ ((new_shape->next_iv_index + 1) - removed_shape->next_iv_index) * sizeof(VALUE));
+
+ // Re-embed objects when instances become small enough
+ // This is necessary because YJIT assumes that objects with the same shape
+ // have the same embeddedness for efficiency (avoid extra checks)
+ if (BUILTIN_TYPE(obj) == T_OBJECT &&
+ !RB_FL_TEST_RAW(obj, ROBJECT_EMBED) &&
+ rb_obj_embedded_size(new_shape->next_iv_index) <= rb_gc_obj_slot_size(obj)) {
+ RB_FL_SET_RAW(obj, ROBJECT_EMBED);
+ memcpy(ROBJECT_IVPTR(obj), ivptr, new_shape->next_iv_index * sizeof(VALUE));
+ xfree(ivptr);
+ }
+
rb_shape_set_shape(obj, new_shape);
}
+ return true;
}
-void
+rb_shape_t *
rb_shape_transition_shape_frozen(VALUE obj)
{
rb_shape_t* shape = rb_shape_get_shape(obj);
@@ -341,21 +666,20 @@ rb_shape_transition_shape_frozen(VALUE obj)
RUBY_ASSERT(RB_OBJ_FROZEN(obj));
if (rb_shape_frozen_shape_p(shape) || rb_shape_obj_too_complex(obj)) {
- return;
+ return shape;
}
rb_shape_t* next_shape;
if (shape == rb_shape_get_root_shape()) {
- rb_shape_set_shape_id(obj, SPECIAL_CONST_SHAPE_ID);
- return;
+ return rb_shape_get_shape_by_id(SPECIAL_CONST_SHAPE_ID);
}
bool dont_care;
- next_shape = get_next_shape_internal(shape, (ID)id_frozen, SHAPE_FROZEN, &dont_care, true, false);
+ next_shape = get_next_shape_internal(shape, (ID)id_frozen, SHAPE_FROZEN, &dont_care, true);
RUBY_ASSERT(next_shape);
- rb_shape_set_shape(obj, next_shape);
+ return next_shape;
}
/*
@@ -367,13 +691,23 @@ rb_shape_get_next_iv_shape(rb_shape_t* shape, ID id)
{
RUBY_ASSERT(!is_instance_id(id) || RTEST(rb_sym2str(ID2SYM(id))));
bool dont_care;
- return get_next_shape_internal(shape, id, SHAPE_IVAR, &dont_care, true, false);
+ return get_next_shape_internal(shape, id, SHAPE_IVAR, &dont_care, true);
}
rb_shape_t *
-rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id)
+rb_shape_get_next(rb_shape_t *shape, VALUE obj, ID id)
{
RUBY_ASSERT(!is_instance_id(id) || RTEST(rb_sym2str(ID2SYM(id))));
+ if (UNLIKELY(shape->type == SHAPE_OBJ_TOO_COMPLEX)) {
+ return shape;
+ }
+
+#if RUBY_DEBUG
+ attr_index_t index;
+ if (rb_shape_get_iv_index(shape, id, &index)) {
+ rb_bug("rb_shape_get_next: trying to create ivar that already exists at index %u", index);
+ }
+#endif
bool allow_new_shape = true;
@@ -383,14 +717,7 @@ rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id)
}
bool variation_created = false;
- // For non T_OBJECTS, force a new shape
- bool new_shape_necessary = BUILTIN_TYPE(obj) != T_OBJECT;
- rb_shape_t * new_shape = get_next_shape_internal(shape, id, SHAPE_IVAR, &variation_created, allow_new_shape, new_shape_necessary);
-
- if (!new_shape) {
- RUBY_ASSERT(BUILTIN_TYPE(obj) == T_OBJECT);
- new_shape = rb_shape_get_shape_by_id(OBJ_TOO_COMPLEX_SHAPE_ID);
- }
+ rb_shape_t *new_shape = get_next_shape_internal(shape, id, SHAPE_IVAR, &variation_created, allow_new_shape);
// Check if we should update max_iv_count on the object's class
if (BUILTIN_TYPE(obj) == T_OBJECT) {
@@ -405,9 +732,10 @@ rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id)
if (RCLASS_EXT(klass)->variation_count >= SHAPE_MAX_VARIATIONS) {
rb_category_warn(
RB_WARN_CATEGORY_PERFORMANCE,
- "Maximum shapes variations (%d) reached by %"PRIsVALUE", instance variables accesses will be slower.",
- SHAPE_MAX_VARIATIONS,
- rb_class_path(klass)
+ "The class %"PRIsVALUE" reached %d shape variations, instance variables accesses will be slower and memory usage increased.\n"
+ "It is recommended to define instance variables in a consistent order, for instance by eagerly defining them all in the #initialize method.",
+ rb_class_path(klass),
+ SHAPE_MAX_VARIATIONS
);
}
}
@@ -417,23 +745,65 @@ rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id)
return new_shape;
}
-rb_shape_t *
-rb_shape_transition_shape_capa(rb_shape_t* shape, uint32_t new_capacity)
+// Same as rb_shape_get_iv_index, but uses a provided valid shape id and index
+// to return a result faster if branches of the shape tree are closely related.
+bool
+rb_shape_get_iv_index_with_hint(shape_id_t shape_id, ID id, attr_index_t *value, shape_id_t *shape_id_hint)
{
- ID edge_name = rb_make_temporary_id(new_capacity);
- bool dont_care;
- rb_shape_t * new_shape = get_next_shape_internal(shape, edge_name, SHAPE_CAPACITY_CHANGE, &dont_care, true, false);
- new_shape->capacity = new_capacity;
- return new_shape;
+ attr_index_t index_hint = *value;
+ rb_shape_t *shape = rb_shape_get_shape_by_id(shape_id);
+ rb_shape_t *initial_shape = shape;
+
+ if (*shape_id_hint == INVALID_SHAPE_ID) {
+ *shape_id_hint = shape_id;
+ return rb_shape_get_iv_index(shape, id, value);
+ }
+
+ rb_shape_t * shape_hint = rb_shape_get_shape_by_id(*shape_id_hint);
+
+ // We assume it's likely shape_id_hint and shape_id have a close common
+ // ancestor, so we check up to ANCESTOR_SEARCH_MAX_DEPTH ancestors before
+ // eventually using the index, as in case of a match it will be faster.
+ // However if the shape doesn't have an index, we walk the entire tree.
+ int depth = INT_MAX;
+ if (shape->ancestor_index && shape->next_iv_index >= ANCESTOR_CACHE_THRESHOLD) {
+ depth = ANCESTOR_SEARCH_MAX_DEPTH;
+ }
+
+ while (depth > 0 && shape->next_iv_index > index_hint) {
+ while (shape_hint->next_iv_index > shape->next_iv_index) {
+ shape_hint = rb_shape_get_parent(shape_hint);
+ }
+
+ if (shape_hint == shape) {
+ // We've found a common ancestor so use the index hint
+ *value = index_hint;
+ *shape_id_hint = rb_shape_id(shape);
+ return true;
+ }
+ if (shape->edge_name == id) {
+ // We found the matching id before a common ancestor
+ *value = shape->next_iv_index - 1;
+ *shape_id_hint = rb_shape_id(shape);
+ return true;
+ }
+
+ shape = rb_shape_get_parent(shape);
+ depth--;
+ }
+
+ // If the original shape had an index but its ancestor doesn't
+ // we switch back to the original one as it will be faster.
+ if (!shape->ancestor_index && initial_shape->ancestor_index) {
+ shape = initial_shape;
+ }
+ *shape_id_hint = shape_id;
+ return rb_shape_get_iv_index(shape, id, value);
}
-bool
-rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t *value)
+static bool
+shape_get_iv_index(rb_shape_t *shape, ID id, attr_index_t *value)
{
- // It doesn't make sense to ask for the index of an IV that's stored
- // on an object that is "too complex" as it uses a hash for storing IVs
- RUBY_ASSERT(rb_shape_id(shape) != OBJ_TOO_COMPLEX_SHAPE_ID);
-
while (shape->parent_id != INVALID_SHAPE_ID) {
if (shape->edge_name == id) {
enum shape_type shape_type;
@@ -444,9 +814,7 @@ rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t *value)
RUBY_ASSERT(shape->next_iv_index > 0);
*value = shape->next_iv_index - 1;
return true;
- case SHAPE_CAPACITY_CHANGE:
case SHAPE_ROOT:
- case SHAPE_INITIAL_CAPACITY:
case SHAPE_T_OBJECT:
return false;
case SHAPE_OBJ_TOO_COMPLEX:
@@ -454,11 +822,59 @@ rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t *value)
rb_bug("Ivar should not exist on transition");
}
}
+
shape = rb_shape_get_parent(shape);
}
+
return false;
}
+static bool
+shape_cache_get_iv_index(rb_shape_t *shape, ID id, attr_index_t *value)
+{
+ if (shape->ancestor_index && shape->next_iv_index >= ANCESTOR_CACHE_THRESHOLD) {
+ redblack_node_t *node = redblack_find(shape->ancestor_index, id);
+ if (node) {
+ rb_shape_t *shape = redblack_value(node);
+ *value = shape->next_iv_index - 1;
+
+#if RUBY_DEBUG
+ attr_index_t shape_tree_index;
+ RUBY_ASSERT(shape_get_iv_index(shape, id, &shape_tree_index));
+ RUBY_ASSERT(shape_tree_index == *value);
+#endif
+
+ return true;
+ }
+
+ /* Verify the cache is correct by checking that this instance variable
+ * does not exist in the shape tree either. */
+ RUBY_ASSERT(!shape_get_iv_index(shape, id, value));
+ }
+
+ return false;
+}
+
+bool
+rb_shape_get_iv_index(rb_shape_t *shape, ID id, attr_index_t *value)
+{
+ // It doesn't make sense to ask for the index of an IV that's stored
+ // on an object that is "too complex" as it uses a hash for storing IVs
+ RUBY_ASSERT(rb_shape_id(shape) != OBJ_TOO_COMPLEX_SHAPE_ID);
+
+ if (!shape_cache_get_iv_index(shape, id, value)) {
+ // If it wasn't in the ancestor cache, then don't do a linear search
+ if (shape->ancestor_index && shape->next_iv_index >= ANCESTOR_CACHE_THRESHOLD) {
+ return false;
+ }
+ else {
+ return shape_get_iv_index(shape, id, value);
+ }
+ }
+
+ return true;
+}
+
void
rb_shape_set_shape(VALUE obj, rb_shape_t* shape)
{
@@ -511,8 +927,6 @@ rb_shape_traverse_from_new_root(rb_shape_t *initial_shape, rb_shape_t *dest_shap
}
break;
case SHAPE_ROOT:
- case SHAPE_CAPACITY_CHANGE:
- case SHAPE_INITIAL_CAPACITY:
case SHAPE_T_OBJECT:
break;
case SHAPE_OBJ_TOO_COMPLEX:
@@ -526,12 +940,18 @@ rb_shape_traverse_from_new_root(rb_shape_t *initial_shape, rb_shape_t *dest_shap
rb_shape_t *
rb_shape_rebuild_shape(rb_shape_t * initial_shape, rb_shape_t * dest_shape)
{
+ RUBY_ASSERT(rb_shape_id(initial_shape) != OBJ_TOO_COMPLEX_SHAPE_ID);
+ RUBY_ASSERT(rb_shape_id(dest_shape) != OBJ_TOO_COMPLEX_SHAPE_ID);
+
rb_shape_t * midway_shape;
RUBY_ASSERT(initial_shape->type == SHAPE_T_OBJECT);
if (dest_shape->type != initial_shape->type) {
midway_shape = rb_shape_rebuild_shape(initial_shape, rb_shape_get_parent(dest_shape));
+ if (UNLIKELY(rb_shape_id(midway_shape) == OBJ_TOO_COMPLEX_SHAPE_ID)) {
+ return midway_shape;
+ }
}
else {
midway_shape = initial_shape;
@@ -539,17 +959,10 @@ rb_shape_rebuild_shape(rb_shape_t * initial_shape, rb_shape_t * dest_shape)
switch ((enum shape_type)dest_shape->type) {
case SHAPE_IVAR:
- if (midway_shape->capacity <= midway_shape->next_iv_index) {
- // There isn't enough room to write this IV, so we need to increase the capacity
- midway_shape = rb_shape_transition_shape_capa(midway_shape, midway_shape->capacity * 2);
- }
-
midway_shape = rb_shape_get_next_iv_shape(midway_shape, dest_shape->edge_name);
break;
case SHAPE_ROOT:
case SHAPE_FROZEN:
- case SHAPE_CAPACITY_CHANGE:
- case SHAPE_INITIAL_CAPACITY:
case SHAPE_T_OBJECT:
break;
case SHAPE_OBJ_TOO_COMPLEX:
@@ -566,14 +979,6 @@ rb_shape_obj_too_complex(VALUE obj)
return rb_shape_get_shape_id(obj) == OBJ_TOO_COMPLEX_SHAPE_ID;
}
-void
-rb_shape_set_too_complex(VALUE obj)
-{
- RUBY_ASSERT(BUILTIN_TYPE(obj) == T_OBJECT);
- RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
- rb_shape_set_shape_id(obj, OBJ_TOO_COMPLEX_SHAPE_ID);
-}
-
size_t
rb_shape_edges_count(rb_shape_t *shape)
{
@@ -724,6 +1129,23 @@ rb_shape_root_shape(VALUE self)
return rb_shape_t_to_rb_cShape(rb_shape_get_root_shape());
}
+/* :nodoc: */
+static VALUE
+rb_shape_shapes_available(VALUE self)
+{
+ return INT2NUM(MAX_SHAPE_ID - (GET_SHAPE_TREE()->next_shape_id - 1));
+}
+
+/* :nodoc: */
+static VALUE
+rb_shape_exhaust(int argc, VALUE *argv, VALUE self)
+{
+ rb_check_arity(argc, 0, 1);
+ int offset = argc == 1 ? NUM2INT(argv[0]) : 0;
+ GET_SHAPE_TREE()->next_shape_id = MAX_SHAPE_ID - offset + 1;
+ return Qnil;
+}
+
VALUE rb_obj_shape(rb_shape_t* shape);
static enum rb_id_table_iterator_result collect_keys_and_values(ID key, VALUE value, void *ref)
@@ -791,9 +1213,7 @@ rb_shape_find_by_id(VALUE mod, VALUE id)
void
Init_default_shapes(void)
{
- rb_shape_tree_t *st = ruby_mimmalloc(sizeof(rb_shape_tree_t));
- memset(st, 0, sizeof(rb_shape_tree_t));
- rb_shape_tree_ptr = st;
+ rb_shape_tree_ptr = xcalloc(1, sizeof(rb_shape_tree_t));
#ifdef HAVE_MMAP
rb_shape_tree_ptr->shape_list = (rb_shape_t *)mmap(NULL, rb_size_mul_or_raise(SHAPE_BUFFER_SIZE, sizeof(rb_shape_t), rb_eRuntimeError),
@@ -812,53 +1232,55 @@ Init_default_shapes(void)
id_frozen = rb_make_internal_id();
id_t_object = rb_make_internal_id();
- // Shapes by size pool
- for (int i = 0; i < SIZE_POOL_COUNT; i++) {
- size_pool_edge_names[i] = rb_make_internal_id();
+#ifdef HAVE_MMAP
+ rb_shape_tree_ptr->shape_cache = (redblack_node_t *)mmap(NULL, rb_size_mul_or_raise(REDBLACK_CACHE_SIZE, sizeof(redblack_node_t), rb_eRuntimeError),
+ PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ rb_shape_tree_ptr->cache_size = 0;
+
+ // If mmap fails, then give up on the redblack tree cache.
+ // We set the cache size such that the redblack node allocators think
+ // the cache is full.
+ if (GET_SHAPE_TREE()->shape_cache == MAP_FAILED) {
+ GET_SHAPE_TREE()->shape_cache = 0;
+ GET_SHAPE_TREE()->cache_size = REDBLACK_CACHE_SIZE;
}
+#endif
// Root shape
- rb_shape_t * root = rb_shape_alloc_with_parent_id(0, INVALID_SHAPE_ID);
- root->capacity = (uint32_t)((rb_size_pool_slot_size(0) - offsetof(struct RObject, as.ary)) / sizeof(VALUE));
+ rb_shape_t *root = rb_shape_alloc_with_parent_id(0, INVALID_SHAPE_ID);
+ root->capacity = 0;
root->type = SHAPE_ROOT;
root->size_pool_index = 0;
GET_SHAPE_TREE()->root_shape = root;
RUBY_ASSERT(rb_shape_id(GET_SHAPE_TREE()->root_shape) == ROOT_SHAPE_ID);
- // Shapes by size pool
- for (int i = 1; i < SIZE_POOL_COUNT; i++) {
- uint32_t capa = (uint32_t)((rb_size_pool_slot_size(i) - offsetof(struct RObject, as.ary)) / sizeof(VALUE));
- rb_shape_t * new_shape = rb_shape_transition_shape_capa(root, capa);
- new_shape->type = SHAPE_INITIAL_CAPACITY;
- new_shape->size_pool_index = i;
- RUBY_ASSERT(rb_shape_id(new_shape) == (shape_id_t)i);
- }
-
- // Make shapes for T_OBJECT
- for (int i = 0; i < SIZE_POOL_COUNT; i++) {
- rb_shape_t * shape = rb_shape_get_shape_by_id(i);
- bool dont_care;
- rb_shape_t * t_object_shape =
- get_next_shape_internal(shape, id_t_object, SHAPE_T_OBJECT, &dont_care, true, false);
- t_object_shape->edges = rb_id_table_create(0);
- RUBY_ASSERT(rb_shape_id(t_object_shape) == (shape_id_t)(i + SIZE_POOL_COUNT));
- }
-
bool dont_care;
// Special const shape
#if RUBY_DEBUG
- rb_shape_t * special_const_shape =
+ rb_shape_t *special_const_shape =
#endif
- get_next_shape_internal(root, (ID)id_frozen, SHAPE_FROZEN, &dont_care, true, false);
+ get_next_shape_internal(root, (ID)id_frozen, SHAPE_FROZEN, &dont_care, true);
RUBY_ASSERT(rb_shape_id(special_const_shape) == SPECIAL_CONST_SHAPE_ID);
RUBY_ASSERT(SPECIAL_CONST_SHAPE_ID == (GET_SHAPE_TREE()->next_shape_id - 1));
RUBY_ASSERT(rb_shape_frozen_shape_p(special_const_shape));
- rb_shape_t * hash_fallback_shape = rb_shape_alloc_with_parent_id(0, ROOT_SHAPE_ID);
- hash_fallback_shape->type = SHAPE_OBJ_TOO_COMPLEX;
- hash_fallback_shape->size_pool_index = 0;
+ rb_shape_t *too_complex_shape = rb_shape_alloc_with_parent_id(0, ROOT_SHAPE_ID);
+ too_complex_shape->type = SHAPE_OBJ_TOO_COMPLEX;
+ too_complex_shape->size_pool_index = 0;
RUBY_ASSERT(OBJ_TOO_COMPLEX_SHAPE_ID == (GET_SHAPE_TREE()->next_shape_id - 1));
- RUBY_ASSERT(rb_shape_id(hash_fallback_shape) == OBJ_TOO_COMPLEX_SHAPE_ID);
+ RUBY_ASSERT(rb_shape_id(too_complex_shape) == OBJ_TOO_COMPLEX_SHAPE_ID);
+
+ // Make shapes for T_OBJECT
+ size_t *sizes = rb_gc_size_pool_sizes();
+ for (int i = 0; sizes[i] > 0; i++) {
+ rb_shape_t *t_object_shape = rb_shape_alloc_with_parent_id(0, INVALID_SHAPE_ID);
+ t_object_shape->type = SHAPE_T_OBJECT;
+ t_object_shape->size_pool_index = i;
+ t_object_shape->capacity = (uint32_t)((sizes[i] - offsetof(struct RObject, as.ary)) / sizeof(VALUE));
+ t_object_shape->edges = rb_id_table_create(0);
+ t_object_shape->ancestor_index = LEAF;
+ RUBY_ASSERT(rb_shape_id(t_object_shape) == (shape_id_t)(i + FIRST_T_OBJECT_SHAPE_ID));
+ }
}
void
@@ -887,12 +1309,18 @@ Init_shape(void)
rb_define_const(rb_cShape, "SHAPE_FLAG_SHIFT", INT2NUM(SHAPE_FLAG_SHIFT));
rb_define_const(rb_cShape, "SPECIAL_CONST_SHAPE_ID", INT2NUM(SPECIAL_CONST_SHAPE_ID));
rb_define_const(rb_cShape, "OBJ_TOO_COMPLEX_SHAPE_ID", INT2NUM(OBJ_TOO_COMPLEX_SHAPE_ID));
+ rb_define_const(rb_cShape, "FIRST_T_OBJECT_SHAPE_ID", INT2NUM(FIRST_T_OBJECT_SHAPE_ID));
rb_define_const(rb_cShape, "SHAPE_MAX_VARIATIONS", INT2NUM(SHAPE_MAX_VARIATIONS));
- rb_define_const(rb_cShape, "SHAPE_MAX_NUM_IVS", INT2NUM(SHAPE_MAX_NUM_IVS));
+ rb_define_const(rb_cShape, "SIZEOF_RB_SHAPE_T", INT2NUM(sizeof(rb_shape_t)));
+ rb_define_const(rb_cShape, "SIZEOF_REDBLACK_NODE_T", INT2NUM(sizeof(redblack_node_t)));
+ rb_define_const(rb_cShape, "SHAPE_BUFFER_SIZE", INT2NUM(sizeof(rb_shape_t) * SHAPE_BUFFER_SIZE));
+ rb_define_const(rb_cShape, "REDBLACK_CACHE_SIZE", INT2NUM(sizeof(redblack_node_t) * REDBLACK_CACHE_SIZE));
rb_define_singleton_method(rb_cShape, "transition_tree", shape_transition_tree, 0);
rb_define_singleton_method(rb_cShape, "find_by_id", rb_shape_find_by_id, 1);
rb_define_singleton_method(rb_cShape, "of", rb_shape_debug_shape, 1);
rb_define_singleton_method(rb_cShape, "root_shape", rb_shape_root_shape, 0);
+ rb_define_singleton_method(rb_cShape, "shapes_available", rb_shape_shapes_available, 0);
+ rb_define_singleton_method(rb_cShape, "exhaust_shapes", rb_shape_exhaust, -1);
#endif
}
diff --git a/shape.h b/shape.h
index 025dfb4dd8..07eb2c979f 100644
--- a/shape.h
+++ b/shape.h
@@ -4,41 +4,42 @@
#include "internal/gc.h"
#if (SIZEOF_UINT64_T <= SIZEOF_VALUE)
+
#define SIZEOF_SHAPE_T 4
#define SHAPE_IN_BASIC_FLAGS 1
typedef uint32_t attr_index_t;
+typedef uint32_t shape_id_t;
+# define SHAPE_ID_NUM_BITS 32
+
#else
+
#define SIZEOF_SHAPE_T 2
#define SHAPE_IN_BASIC_FLAGS 0
typedef uint16_t attr_index_t;
-#endif
-
-#define MAX_IVARS (attr_index_t)(-1)
-
-#if SIZEOF_SHAPE_T == 4
-typedef uint32_t shape_id_t;
-# define SHAPE_ID_NUM_BITS 32
-# define SHAPE_BUFFER_SIZE 0x80000
-#else
typedef uint16_t shape_id_t;
# define SHAPE_ID_NUM_BITS 16
-# define SHAPE_BUFFER_SIZE 0x8000
+
#endif
+typedef uint32_t redblack_id_t;
+
+#define MAX_IVARS (attr_index_t)(-1)
+
# define SHAPE_MASK (((uintptr_t)1 << SHAPE_ID_NUM_BITS) - 1)
# define SHAPE_FLAG_MASK (((VALUE)-1) >> SHAPE_ID_NUM_BITS)
# define SHAPE_FLAG_SHIFT ((SIZEOF_VALUE * 8) - SHAPE_ID_NUM_BITS)
# define SHAPE_MAX_VARIATIONS 8
-# define SHAPE_MAX_NUM_IVS 80
-# define MAX_SHAPE_ID SHAPE_BUFFER_SIZE
# define INVALID_SHAPE_ID SHAPE_MASK
# define ROOT_SHAPE_ID 0x0
-# define SPECIAL_CONST_SHAPE_ID (SIZE_POOL_COUNT * 2)
+# define SPECIAL_CONST_SHAPE_ID (ROOT_SHAPE_ID + 1)
# define OBJ_TOO_COMPLEX_SHAPE_ID (SPECIAL_CONST_SHAPE_ID + 1)
+# define FIRST_T_OBJECT_SHAPE_ID (OBJ_TOO_COMPLEX_SHAPE_ID + 1)
+
+typedef struct redblack_node redblack_node_t;
struct rb_shape {
struct rb_id_table * edges; // id_table from ID (ivar) to next shape
@@ -48,16 +49,22 @@ struct rb_shape {
uint8_t type;
uint8_t size_pool_index;
shape_id_t parent_id;
+ redblack_node_t * ancestor_index;
};
typedef struct rb_shape rb_shape_t;
+struct redblack_node {
+ ID key;
+ rb_shape_t * value;
+ redblack_id_t l;
+ redblack_id_t r;
+};
+
enum shape_type {
SHAPE_ROOT,
SHAPE_IVAR,
SHAPE_FROZEN,
- SHAPE_CAPACITY_CHANGE,
- SHAPE_INITIAL_CAPACITY,
SHAPE_T_OBJECT,
SHAPE_OBJ_TOO_COMPLEX,
};
@@ -67,6 +74,9 @@ typedef struct {
rb_shape_t *shape_list;
rb_shape_t *root_shape;
shape_id_t next_shape_id;
+
+ redblack_node_t *shape_cache;
+ unsigned int cache_size;
} rb_shape_tree_t;
RUBY_EXTERN rb_shape_tree_t *rb_shape_tree_ptr;
@@ -141,18 +151,18 @@ int32_t rb_shape_id_offset(void);
rb_shape_t * rb_shape_get_parent(rb_shape_t * shape);
-rb_shape_t* rb_shape_get_shape_by_id(shape_id_t shape_id);
-shape_id_t rb_shape_get_shape_id(VALUE obj);
+RUBY_FUNC_EXPORTED rb_shape_t *rb_shape_get_shape_by_id(shape_id_t shape_id);
+RUBY_FUNC_EXPORTED shape_id_t rb_shape_get_shape_id(VALUE obj);
rb_shape_t * rb_shape_get_next_iv_shape(rb_shape_t * shape, ID id);
bool rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t * value);
-bool rb_shape_obj_too_complex(VALUE obj);
+bool rb_shape_get_iv_index_with_hint(shape_id_t shape_id, ID id, attr_index_t * value, shape_id_t *shape_id_hint);
+RUBY_FUNC_EXPORTED bool rb_shape_obj_too_complex(VALUE obj);
void rb_shape_set_shape(VALUE obj, rb_shape_t* shape);
rb_shape_t* rb_shape_get_shape(VALUE obj);
int rb_shape_frozen_shape_p(rb_shape_t* shape);
-void rb_shape_transition_shape_frozen(VALUE obj);
-void rb_shape_transition_shape_remove_ivar(VALUE obj, ID id, rb_shape_t *shape, VALUE * removed);
-rb_shape_t * rb_shape_transition_shape_capa(rb_shape_t * shape, uint32_t new_capacity);
+rb_shape_t* rb_shape_transition_shape_frozen(VALUE obj);
+bool rb_shape_transition_shape_remove_ivar(VALUE obj, ID id, rb_shape_t *shape, VALUE * removed);
rb_shape_t* rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id);
rb_shape_t * rb_shape_rebuild_shape(rb_shape_t * initial_shape, rb_shape_t * dest_shape);
@@ -163,7 +173,7 @@ ROBJECT_IV_CAPACITY(VALUE obj)
RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
// Asking for capacity doesn't make sense when the object is using
// a hash table for storing instance variables
- RUBY_ASSERT(ROBJECT_SHAPE_ID(obj) != OBJ_TOO_COMPLEX_SHAPE_ID);
+ RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
return rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj))->capacity;
}
@@ -171,15 +181,15 @@ static inline st_table *
ROBJECT_IV_HASH(VALUE obj)
{
RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
- RUBY_ASSERT(ROBJECT_SHAPE_ID(obj) == OBJ_TOO_COMPLEX_SHAPE_ID);
+ RUBY_ASSERT(rb_shape_obj_too_complex(obj));
return (st_table *)ROBJECT(obj)->as.heap.ivptr;
}
static inline void
-ROBJECT_SET_IV_HASH(VALUE obj, const struct rb_id_table *tbl)
+ROBJECT_SET_IV_HASH(VALUE obj, const st_table *tbl)
{
RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
- RUBY_ASSERT(ROBJECT_SHAPE_ID(obj) == OBJ_TOO_COMPLEX_SHAPE_ID);
+ RUBY_ASSERT(rb_shape_obj_too_complex(obj));
ROBJECT(obj)->as.heap.ivptr = (VALUE *)tbl;
}
@@ -188,12 +198,12 @@ size_t rb_id_table_size(const struct rb_id_table *tbl);
static inline uint32_t
ROBJECT_IV_COUNT(VALUE obj)
{
- if (ROBJECT_SHAPE_ID(obj) == OBJ_TOO_COMPLEX_SHAPE_ID) {
+ if (rb_shape_obj_too_complex(obj)) {
return (uint32_t)rb_st_table_size(ROBJECT_IV_HASH(obj));
}
else {
RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
- RUBY_ASSERT(ROBJECT_SHAPE_ID(obj) != OBJ_TOO_COMPLEX_SHAPE_ID);
+ RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
return rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj))->next_iv_index;
}
}
@@ -204,20 +214,11 @@ RBASIC_IV_COUNT(VALUE obj)
return rb_shape_get_shape_by_id(rb_shape_get_shape_id(obj))->next_iv_index;
}
-static inline uint32_t
-RCLASS_IV_COUNT(VALUE obj)
-{
- RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
- uint32_t ivc = rb_shape_get_shape_by_id(RCLASS_SHAPE_ID(obj))->next_iv_index;
- return ivc;
-}
-
rb_shape_t *rb_shape_traverse_from_new_root(rb_shape_t *initial_shape, rb_shape_t *orig_shape);
bool rb_shape_set_shape_id(VALUE obj, shape_id_t shape_id);
VALUE rb_obj_debug_shape(VALUE self, VALUE obj);
-void rb_shape_set_too_complex(VALUE obj);
// For ext/objspace
RUBY_SYMBOL_EXPORT_BEGIN
diff --git a/signal.c b/signal.c
index 8dfeff9a50..1c8f8c112b 100644
--- a/signal.c
+++ b/signal.c
@@ -274,7 +274,7 @@ signm2signo(VALUE *sig_ptr, int negative, int exit, int *prefix_ptr)
vsig = rb_str_subseq(vsig, prefix, len);
prefix = signame_prefix_len;
}
- rb_raise(rb_eArgError, "unsupported signal `%.*s%"PRIsVALUE"'",
+ rb_raise(rb_eArgError, "unsupported signal '%.*s%"PRIsVALUE"'",
prefix, signame_prefix, vsig);
UNREACHABLE_RETURN(0);
}
@@ -403,6 +403,9 @@ interrupt_init(int argc, VALUE *argv, VALUE self)
}
void rb_malloc_info_show_results(void); /* gc.c */
+#if defined(USE_SIGALTSTACK) || defined(_WIN32)
+static void reset_sigmask(int sig);
+#endif
void
ruby_default_signal(int sig)
@@ -413,6 +416,9 @@ ruby_default_signal(int sig)
rb_malloc_info_show_results();
signal(sig, SIG_DFL);
+#if defined(USE_SIGALTSTACK) || defined(_WIN32)
+ reset_sigmask(sig);
+#endif
raise(sig);
}
@@ -591,7 +597,7 @@ ruby_signal(int signum, sighandler_t handler)
#endif
sigemptyset(&sigact.sa_mask);
-#ifdef USE_SIGALTSTACK
+#if defined(USE_SIGALTSTACK) && !defined(__wasm__)
if (handler == SIG_IGN || handler == SIG_DFL) {
sigact.sa_handler = handler;
sigact.sa_flags = 0;
@@ -628,7 +634,7 @@ ruby_signal(int signum, sighandler_t handler)
}
sighandler_t
-posix_signal(int signum, sighandler_t handler)
+ruby_posix_signal(int signum, sighandler_t handler)
{
return ruby_signal(signum, handler);
}
@@ -861,16 +867,19 @@ check_stack_overflow(int sig, const void *addr)
}
}
# endif
+
# ifdef _WIN32
# define CHECK_STACK_OVERFLOW() check_stack_overflow(sig, 0)
# else
# define FAULT_ADDRESS info->si_addr
# ifdef USE_UCONTEXT_REG
-# define CHECK_STACK_OVERFLOW() check_stack_overflow(sig, (uintptr_t)FAULT_ADDRESS, ctx)
+# define CHECK_STACK_OVERFLOW_() check_stack_overflow(sig, (uintptr_t)FAULT_ADDRESS, ctx)
# else
-# define CHECK_STACK_OVERFLOW() check_stack_overflow(sig, FAULT_ADDRESS)
+# define CHECK_STACK_OVERFLOW_() check_stack_overflow(sig, FAULT_ADDRESS)
# endif
# define MESSAGE_FAULT_ADDRESS " at %p", FAULT_ADDRESS
+# define SIGNAL_FROM_USER_P() ((info)->si_code == SI_USER)
+# define CHECK_STACK_OVERFLOW() (SIGNAL_FROM_USER_P() ? (void)0 : CHECK_STACK_OVERFLOW_())
# endif
#else
# define CHECK_STACK_OVERFLOW() (void)0
@@ -880,10 +889,10 @@ check_stack_overflow(int sig, const void *addr)
#endif
#if defined SIGSEGV || defined SIGBUS || defined SIGILL || defined SIGFPE
-NOINLINE(static void check_reserved_signal_(const char *name, size_t name_len));
+NOINLINE(static void check_reserved_signal_(const char *name, size_t name_len, int signo));
/* noinine to reduce stack usage in signal handers */
-#define check_reserved_signal(name) check_reserved_signal_(name, sizeof(name)-1)
+#define check_reserved_signal(name) check_reserved_signal_(name, sizeof(name)-1, sig)
#ifdef SIGBUS
@@ -955,33 +964,38 @@ ruby_abort(void)
}
static void
-check_reserved_signal_(const char *name, size_t name_len)
+check_reserved_signal_(const char *name, size_t name_len, int signo)
{
const char *prev = ATOMIC_PTR_EXCHANGE(received_signal, name);
if (prev) {
ssize_t RB_UNUSED_VAR(err);
+ static const int stderr_fd = 2;
#define NOZ(name, str) name[sizeof(str)-1] = str
static const char NOZ(msg1, " received in ");
static const char NOZ(msg2, " handler\n");
#ifdef HAVE_WRITEV
struct iovec iov[4];
-
- iov[0].iov_base = (void *)name;
- iov[0].iov_len = name_len;
- iov[1].iov_base = (void *)msg1;
- iov[1].iov_len = sizeof(msg1);
- iov[2].iov_base = (void *)prev;
- iov[2].iov_len = strlen(prev);
- iov[3].iov_base = (void *)msg2;
- iov[3].iov_len = sizeof(msg2);
- err = writev(2, iov, 4);
+ int i = 0;
+# define W(str, len) \
+ iov[i++] = (struct iovec){.iov_base = (void *)(str), .iov_len = (len)}
#else
- err = write(2, name, name_len);
- err = write(2, msg1, sizeof(msg1));
- err = write(2, prev, strlen(prev));
- err = write(2, msg2, sizeof(msg2));
+# define W(str, len) err = write(stderr_fd, (str), (len))
+#endif
+
+#if __has_feature(address_sanitizer) || \
+ __has_feature(memory_sanitizer) || \
+ defined(HAVE_VALGRIND_MEMCHECK_H)
+ ruby_posix_signal(signo, SIG_DFL);
+#endif
+ W(name, name_len);
+ W(msg1, sizeof(msg1));
+ W(prev, strlen(prev));
+ W(msg2, sizeof(msg2));
+# undef W
+#ifdef HAVE_WRITEV
+ err = writev(stderr_fd, iov, i);
#endif
ruby_abort();
}
@@ -1483,6 +1497,9 @@ Init_signal(void)
rb_alias(rb_eSignal, rb_intern_const("signm"), rb_intern_const("message"));
rb_define_method(rb_eInterrupt, "initialize", interrupt_init, -1);
+ // It should be ready to call rb_signal_exec()
+ VM_ASSERT(GET_THREAD()->pending_interrupt_queue);
+
/* At this time, there is no subthread. Then sigmask guarantee atomics. */
rb_disable_interrupt();
@@ -1531,21 +1548,3 @@ Init_signal(void)
rb_enable_interrupt();
}
-
-#if defined(HAVE_GRANTPT)
-extern int grantpt(int);
-#else
-static int
-fake_grantfd(int masterfd)
-{
- errno = ENOSYS;
- return -1;
-}
-#define grantpt(fd) fake_grantfd(fd)
-#endif
-
-int
-rb_grantpt(int masterfd)
-{
- return grantpt(masterfd);
-}
diff --git a/spec/README.md b/spec/README.md
index 4fcf090759..6a88c06e09 100644
--- a/spec/README.md
+++ b/spec/README.md
@@ -91,24 +91,24 @@ To run all specs:
make test-spec
```
-Extra arguments can be added via `MSPECOPT`.
+Extra arguments can be added via `SPECOPTS`.
For instance, to show the help:
```bash
-make test-spec MSPECOPT=-h
+make test-spec SPECOPTS=-h
```
You can also run the specs in parallel, which is currently experimental.
It takes around 10s instead of 60s on a quad-core laptop.
```bash
-make test-spec MSPECOPT=-j
+make test-spec SPECOPTS=-j
```
To run a specific test, add its path to the command:
```bash
-make test-spec MSPECOPT=spec/ruby/language/for_spec.rb
+make test-spec SPECOPTS=spec/ruby/language/for_spec.rb
```
If ruby trunk is your current `ruby` in `$PATH`, you can also run `mspec` directly:
diff --git a/spec/bundled_gems.mspec b/spec/bundled_gems.mspec
new file mode 100644
index 0000000000..762fa1c165
--- /dev/null
+++ b/spec/bundled_gems.mspec
@@ -0,0 +1,6 @@
+load File.dirname(__FILE__) + '/default.mspec'
+
+class MSpecScript
+ set :library, get(:stdlibs).to_a & get(:bundled_gems).to_a
+ set :files, get(:library)
+end
diff --git a/spec/bundler/bundler/bundler_spec.rb b/spec/bundler/bundler/bundler_spec.rb
index 54c12dbf3d..6a2e435e54 100644
--- a/spec/bundler/bundler/bundler_spec.rb
+++ b/spec/bundler/bundler/bundler_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe Bundler do
describe "#load_marshal" do
it "is a private method and raises an error" do
data = Marshal.dump(Bundler)
- expect { Bundler.load_marshal(data) }.to raise_error(NoMethodError, /private method `load_marshal' called/)
+ expect { Bundler.load_marshal(data) }.to raise_error(NoMethodError, /private method [`']load_marshal' called/)
end
it "loads any data" do
@@ -23,7 +23,7 @@ RSpec.describe Bundler do
end
it "loads simple structure" do
- simple_structure = { "name" => [:abc] }
+ simple_structure = { "name" => [:development] }
data = Marshal.dump(simple_structure)
expect(Bundler.safe_load_marshal(data)).to eq(simple_structure)
end
@@ -76,7 +76,7 @@ RSpec.describe Bundler do
context "with incorrect YAML file" do
before do
File.open(app_gemspec_path, "wb") do |f|
- f.write strip_whitespace(<<-GEMSPEC)
+ f.write <<~GEMSPEC
---
{:!00 ao=gu\g1= 7~f
GEMSPEC
@@ -88,7 +88,7 @@ RSpec.describe Bundler do
end
end
- context "with correct YAML file", :if => defined?(Encoding) do
+ context "with correct YAML file", if: defined?(Encoding) do
it "can load a gemspec with unicode characters with default ruby encoding" do
# spec_helper forces the external encoding to UTF-8 but that's not the
# default until Ruby 2.0
@@ -139,7 +139,7 @@ RSpec.describe Bundler do
end
GEMSPEC
end
- expect(Bundler.rubygems).to receive(:validate).with have_attributes(:name => "validated")
+ expect(Bundler.rubygems).to receive(:validate).with have_attributes(name: "validated")
subject
end
end
@@ -147,7 +147,7 @@ RSpec.describe Bundler do
context "with gemspec containing local variables" do
before do
File.open(app_gemspec_path, "wb") do |f|
- f.write strip_whitespace(<<-GEMSPEC)
+ f.write <<~GEMSPEC
must_not_leak = true
Gem::Specification.new do |gem|
gem.name = "leak check"
@@ -224,24 +224,6 @@ RSpec.describe Bundler do
end
end
- describe "#rm_rf" do
- context "the directory is world writable" do
- let(:bundler_ui) { Bundler.ui }
- it "should raise a friendly error" do
- allow(File).to receive(:exist?).and_return(true)
- allow(::Bundler::FileUtils).to receive(:remove_entry_secure).and_raise(ArgumentError)
- allow(File).to receive(:world_writable?).and_return(true)
- message = <<EOF
-It is a security vulnerability to allow your home directory to be world-writable, and bundler cannot continue.
-You should probably consider fixing this issue by running `chmod o-w ~` on *nix.
-Please refer to https://ruby-doc.org/stdlib-3.1.2/libdoc/fileutils/rdoc/FileUtils.html#method-c-remove_entry_secure for details.
-EOF
- expect(bundler_ui).to receive(:warn).with(message)
- expect { Bundler.send(:rm_rf, bundled_app) }.to raise_error(Bundler::PathError)
- end
- end
- end
-
describe "#mkdir_p" do
it "creates a folder at the given path" do
install_gemfile <<-G
diff --git a/spec/bundler/bundler/ci_detector_spec.rb b/spec/bundler/bundler/ci_detector_spec.rb
new file mode 100644
index 0000000000..299d8005e8
--- /dev/null
+++ b/spec/bundler/bundler/ci_detector_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::CIDetector do
+ # This is properly tested in rubygems, under the name Gem::CIDetector
+ # But the test that confirms that our version _stays in sync_ with that version
+ # will live here.
+
+ it "stays in sync with the rubygems implementation" do
+ rubygems_implementation_path = File.join(git_root, "lib", "rubygems", "ci_detector.rb")
+ expect(File.exist?(rubygems_implementation_path)).to be_truthy
+ rubygems_code = File.read(rubygems_implementation_path)
+ denamespaced_rubygems_code = rubygems_code.sub("Gem", "NAMESPACE")
+
+ bundler_implementation_path = File.join(source_lib_dir, "bundler", "ci_detector.rb")
+ expect(File.exist?(bundler_implementation_path)).to be_truthy
+ bundler_code = File.read(bundler_implementation_path)
+ denamespaced_bundler_code = bundler_code.sub("Bundler", "NAMESPACE")
+
+ expect(denamespaced_bundler_code).to eq(denamespaced_rubygems_code)
+ end
+end
diff --git a/spec/bundler/bundler/cli_spec.rb b/spec/bundler/bundler/cli_spec.rb
index b752cd7e70..c71fc8e9e7 100644
--- a/spec/bundler/bundler/cli_spec.rb
+++ b/spec/bundler/bundler/cli_spec.rb
@@ -4,12 +4,12 @@ require "bundler/cli"
RSpec.describe "bundle executable" do
it "returns non-zero exit status when passed unrecognized options" do
- bundle "--invalid_argument", :raise_on_error => false
+ bundle "--invalid_argument", raise_on_error: false
expect(exitstatus).to_not be_zero
end
it "returns non-zero exit status when passed unrecognized task" do
- bundle "unrecognized-task", :raise_on_error => false
+ bundle "unrecognized-task", raise_on_error: false
expect(exitstatus).to_not be_zero
end
@@ -87,7 +87,7 @@ RSpec.describe "bundle executable" do
end
context "with no arguments" do
- it "prints a concise help message", :bundler => "3" do
+ it "prints a concise help message", bundler: "3" do
bundle ""
expect(err).to be_empty
expect(out).to include("Bundler version #{Bundler::VERSION}").
@@ -105,7 +105,7 @@ RSpec.describe "bundle executable" do
gem 'rack'
G
- bundle :install, :env => { "BUNDLE_GEMFILE" => "" }
+ bundle :install, env: { "BUNDLE_GEMFILE" => "" }
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -114,17 +114,17 @@ RSpec.describe "bundle executable" do
context "with --verbose" do
it "prints the running command" do
gemfile "source \"#{file_uri_for(gem_repo1)}\""
- bundle "info bundler", :verbose => true
+ bundle "info bundler", verbose: true
expect(out).to start_with("Running `bundle info bundler --verbose` with bundler #{Bundler::VERSION}")
end
it "doesn't print defaults" do
- install_gemfile "source \"#{file_uri_for(gem_repo1)}\"", :verbose => true
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\"", verbose: true
expect(out).to start_with("Running `bundle install --verbose` with bundler #{Bundler::VERSION}")
end
it "doesn't print defaults" do
- install_gemfile "source \"#{file_uri_for(gem_repo1)}\"", :verbose => true
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\"", verbose: true
expect(out).to start_with("Running `bundle install --verbose` with bundler #{Bundler::VERSION}")
end
end
@@ -133,7 +133,7 @@ RSpec.describe "bundle executable" do
let(:run_command) do
bundle "install"
- bundle "outdated #{flags}", :raise_on_error => false
+ bundle "outdated #{flags}", raise_on_error: false
end
before do
@@ -178,7 +178,7 @@ RSpec.describe "bundle executable" do
describe "printing the outdated warning" do
shared_examples_for "no warning" do
it "prints no warning" do
- bundle "fail", :env => { "BUNDLER_VERSION" => bundler_version }, :raise_on_error => false
+ bundle "fail", env: { "BUNDLER_VERSION" => bundler_version }, raise_on_error: false
expect(last_command.stdboth).to eq("Could not find command \"fail\".")
end
end
@@ -213,7 +213,7 @@ RSpec.describe "bundle executable" do
context "when the latest version is greater than the current version" do
let(:latest_version) { "222.0" }
it "prints the version warning" do
- bundle "fail", :env => { "BUNDLER_VERSION" => bundler_version }, :raise_on_error => false
+ bundle "fail", env: { "BUNDLER_VERSION" => bundler_version }, raise_on_error: false
expect(err).to start_with(<<-EOS.strip)
The latest bundler is #{latest_version}, but you are currently running #{bundler_version}.
To update to the most recent version, run `bundle update --bundler`
@@ -221,16 +221,16 @@ To update to the most recent version, run `bundle update --bundler`
end
context "and disable_version_check is set" do
- before { bundle "config set disable_version_check true", :env => { "BUNDLER_VERSION" => bundler_version } }
+ before { bundle "config set disable_version_check true", env: { "BUNDLER_VERSION" => bundler_version } }
include_examples "no warning"
end
context "running a parseable command" do
it "prints no warning" do
- bundle "config get --parseable foo", :env => { "BUNDLER_VERSION" => bundler_version }
+ bundle "config get --parseable foo", env: { "BUNDLER_VERSION" => bundler_version }
expect(last_command.stdboth).to eq ""
- bundle "platform --ruby", :env => { "BUNDLER_VERSION" => bundler_version }, :raise_on_error => false
+ bundle "platform --ruby", env: { "BUNDLER_VERSION" => bundler_version }, raise_on_error: false
expect(last_command.stdboth).to eq "Could not locate Gemfile"
end
end
@@ -238,7 +238,7 @@ To update to the most recent version, run `bundle update --bundler`
context "and is a pre-release" do
let(:latest_version) { "222.0.0.pre.4" }
it "prints the version warning" do
- bundle "fail", :env => { "BUNDLER_VERSION" => bundler_version }, :raise_on_error => false
+ bundle "fail", env: { "BUNDLER_VERSION" => bundler_version }, raise_on_error: false
expect(err).to start_with(<<-EOS.strip)
The latest bundler is #{latest_version}, but you are currently running #{bundler_version}.
To update to the most recent version, run `bundle update --bundler`
@@ -250,12 +250,12 @@ To update to the most recent version, run `bundle update --bundler`
end
RSpec.describe "bundler executable" do
- it "shows the bundler version just as the `bundle` executable does", :bundler => "< 3" do
+ it "shows the bundler version just as the `bundle` executable does", bundler: "< 3" do
bundler "--version"
expect(out).to eq("Bundler version #{Bundler::VERSION}")
end
- it "shows the bundler version just as the `bundle` executable does", :bundler => "3" do
+ it "shows the bundler version just as the `bundle` executable does", bundler: "3" do
bundler "--version"
expect(out).to eq(Bundler::VERSION)
end
diff --git a/spec/bundler/bundler/compact_index_client/updater_spec.rb b/spec/bundler/bundler/compact_index_client/updater_spec.rb
index fe417e3920..6eed88ca9e 100644
--- a/spec/bundler/bundler/compact_index_client/updater_spec.rb
+++ b/spec/bundler/bundler/compact_index_client/updater_spec.rb
@@ -1,43 +1,224 @@
# frozen_string_literal: true
-require "net/http"
+require "bundler/vendored_net_http"
require "bundler/compact_index_client"
require "bundler/compact_index_client/updater"
require "tmpdir"
RSpec.describe Bundler::CompactIndexClient::Updater do
+ subject(:updater) { described_class.new(fetcher) }
+
let(:fetcher) { double(:fetcher) }
- let(:local_path) { Pathname.new Dir.mktmpdir("localpath") }
+ let(:local_path) { Pathname.new(Dir.mktmpdir("localpath")).join("versions") }
+ let(:etag_path) { Pathname.new(Dir.mktmpdir("localpath-etags")).join("versions.etag") }
let(:remote_path) { double(:remote_path) }
- let!(:updater) { described_class.new(fetcher) }
+ let(:full_body) { "abc123" }
+ let(:response) { double(:response, body: full_body, is_a?: false) }
+ let(:digest) { Digest::SHA256.base64digest(full_body) }
+
+ context "when the local path does not exist" do
+ before do
+ allow(response).to receive(:[]).with("Repr-Digest") { nil }
+ allow(response).to receive(:[]).with("Digest") { nil }
+ allow(response).to receive(:[]).with("ETag") { '"thisisanetag"' }
+ end
+
+ it "downloads the file without attempting append" do
+ expect(fetcher).to receive(:call).once.with(remote_path, {}) { response }
+
+ updater.update(remote_path, local_path, etag_path)
+
+ expect(local_path.read).to eq(full_body)
+ expect(etag_path.read).to eq("thisisanetag")
+ end
+
+ it "fails immediately on bad checksum" do
+ expect(fetcher).to receive(:call).once.with(remote_path, {}) { response }
+ allow(response).to receive(:[]).with("Repr-Digest") { "sha-256=:baddigest:" }
+
+ expect do
+ updater.update(remote_path, local_path, etag_path)
+ end.to raise_error(Bundler::CompactIndexClient::Updater::MismatchedChecksumError)
+ end
+ end
+
+ context "when the local path exists" do
+ let(:local_body) { "abc" }
+
+ before do
+ local_path.open("w") {|f| f.write(local_body) }
+ end
+
+ context "with an etag" do
+ before do
+ etag_path.open("w") {|f| f.write("LocalEtag") }
+ end
+
+ let(:headers) do
+ {
+ "If-None-Match" => '"LocalEtag"',
+ "Range" => "bytes=2-",
+ }
+ end
+
+ it "does nothing if etags match" do
+ expect(fetcher).to receive(:call).once.with(remote_path, headers).and_return(response)
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPPartialContent) { false }
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPNotModified) { true }
+
+ updater.update(remote_path, local_path, etag_path)
+
+ expect(local_path.read).to eq("abc")
+ expect(etag_path.read).to eq("LocalEtag")
+ end
+
+ it "appends the file if etags do not match" do
+ expect(fetcher).to receive(:call).once.with(remote_path, headers).and_return(response)
+ allow(response).to receive(:[]).with("Repr-Digest") { "sha-256=:#{digest}:" }
+ allow(response).to receive(:[]).with("ETag") { '"NewEtag"' }
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPPartialContent) { true }
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPNotModified) { false }
+ allow(response).to receive(:body) { "c123" }
+
+ updater.update(remote_path, local_path, etag_path)
+
+ expect(local_path.read).to eq(full_body)
+ expect(etag_path.read).to eq("NewEtag")
+ end
+
+ it "replaces the file if response ignores range" do
+ expect(fetcher).to receive(:call).once.with(remote_path, headers).and_return(response)
+ allow(response).to receive(:[]).with("Repr-Digest") { "sha-256=:#{digest}:" }
+ allow(response).to receive(:[]).with("ETag") { '"NewEtag"' }
+ allow(response).to receive(:body) { full_body }
+
+ updater.update(remote_path, local_path, etag_path)
+
+ expect(local_path.read).to eq(full_body)
+ expect(etag_path.read).to eq("NewEtag")
+ end
+
+ it "tries the request again if the partial response fails digest check" do
+ allow(response).to receive(:[]).with("Repr-Digest") { "sha-256=:baddigest:" }
+ allow(response).to receive(:body) { "the beginning of the file changed" }
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPPartialContent) { true }
+ expect(fetcher).to receive(:call).once.with(remote_path, headers).and_return(response)
+
+ full_response = double(:full_response, body: full_body, is_a?: false)
+ allow(full_response).to receive(:[]).with("Repr-Digest") { "sha-256=:#{digest}:" }
+ allow(full_response).to receive(:[]).with("ETag") { '"NewEtag"' }
+ expect(fetcher).to receive(:call).once.with(remote_path, { "If-None-Match" => '"LocalEtag"' }).and_return(full_response)
+
+ updater.update(remote_path, local_path, etag_path)
+
+ expect(local_path.read).to eq(full_body)
+ expect(etag_path.read).to eq("NewEtag")
+ end
+ end
+
+ context "without an etag file" do
+ let(:headers) do
+ {
+ "Range" => "bytes=2-",
+ # This MD5 feature should be deleted after sufficient time has passed since release.
+ # From then on, requests that still don't have a saved etag will be made without this header.
+ "If-None-Match" => %("#{Digest::MD5.hexdigest(local_body)}"),
+ }
+ end
+
+ it "saves only the etag_path if generated etag matches" do
+ expect(fetcher).to receive(:call).once.with(remote_path, headers).and_return(response)
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPPartialContent) { false }
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPNotModified) { true }
+
+ updater.update(remote_path, local_path, etag_path)
+
+ expect(local_path.read).to eq("abc")
+ expect(%("#{etag_path.read}")).to eq(headers["If-None-Match"])
+ end
+
+ it "appends the file" do
+ expect(fetcher).to receive(:call).once.with(remote_path, headers).and_return(response)
+ allow(response).to receive(:[]).with("Repr-Digest") { "sha-256=:#{digest}:" }
+ allow(response).to receive(:[]).with("ETag") { '"OpaqueEtag"' }
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPPartialContent) { true }
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPNotModified) { false }
+ allow(response).to receive(:body) { "c123" }
+
+ updater.update(remote_path, local_path, etag_path)
+
+ expect(local_path.read).to eq(full_body)
+ expect(etag_path.read).to eq("OpaqueEtag")
+ end
+
+ it "replaces the file on full file response that ignores range request" do
+ expect(fetcher).to receive(:call).once.with(remote_path, headers).and_return(response)
+ allow(response).to receive(:[]).with("Repr-Digest") { nil }
+ allow(response).to receive(:[]).with("Digest") { nil }
+ allow(response).to receive(:[]).with("ETag") { '"OpaqueEtag"' }
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPPartialContent) { false }
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPNotModified) { false }
+ allow(response).to receive(:body) { full_body }
+
+ updater.update(remote_path, local_path, etag_path)
+
+ expect(local_path.read).to eq(full_body)
+ expect(etag_path.read).to eq("OpaqueEtag")
+ end
+
+ it "tries the request again if the partial response fails digest check" do
+ allow(response).to receive(:[]).with("Repr-Digest") { "sha-256=:baddigest:" }
+ allow(response).to receive(:body) { "the beginning of the file changed" }
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPPartialContent) { true }
+ expect(fetcher).to receive(:call).once.with(remote_path, headers) do
+ # During the failed first request, we simulate another process writing the etag.
+ # This ensures the second request doesn't generate the md5 etag again but just uses whatever is written.
+ etag_path.open("w") {|f| f.write("LocalEtag") }
+ response
+ end
+
+ full_response = double(:full_response, body: full_body, is_a?: false)
+ allow(full_response).to receive(:[]).with("Repr-Digest") { "sha-256=:#{digest}:" }
+ allow(full_response).to receive(:[]).with("ETag") { '"NewEtag"' }
+ expect(fetcher).to receive(:call).once.with(remote_path, { "If-None-Match" => '"LocalEtag"' }).and_return(full_response)
+
+ updater.update(remote_path, local_path, etag_path)
+
+ expect(local_path.read).to eq(full_body)
+ expect(etag_path.read).to eq("NewEtag")
+ end
+ end
+ end
context "when the ETag header is missing" do
# Regression test for https://github.com/rubygems/bundler/issues/5463
- let(:response) { double(:response, :body => "abc123") }
+ let(:response) { double(:response, body: full_body) }
it "treats the response as an update" do
- expect(response).to receive(:[]).with("ETag") { nil }
+ allow(response).to receive(:[]).with("Repr-Digest") { nil }
+ allow(response).to receive(:[]).with("Digest") { nil }
+ allow(response).to receive(:[]).with("ETag") { nil }
expect(fetcher).to receive(:call) { response }
- updater.update(local_path, remote_path)
+ updater.update(remote_path, local_path, etag_path)
end
end
context "when the download is corrupt" do
- let(:response) { double(:response, :body => "") }
+ let(:response) { double(:response, body: "") }
it "raises HTTPError" do
expect(fetcher).to receive(:call).and_raise(Zlib::GzipFile::Error)
expect do
- updater.update(local_path, remote_path)
+ updater.update(remote_path, local_path, etag_path)
end.to raise_error(Bundler::HTTPError)
end
end
context "when receiving non UTF-8 data and default internal encoding set to ASCII" do
- let(:response) { double(:response, :body => "\x8B".b) }
+ let(:response) { double(:response, body: "\x8B".b) }
it "works just fine" do
old_verbose = $VERBOSE
@@ -46,10 +227,12 @@ RSpec.describe Bundler::CompactIndexClient::Updater do
begin
$VERBOSE = false
Encoding.default_internal = "ASCII"
- expect(response).to receive(:[]).with("ETag") { nil }
+ allow(response).to receive(:[]).with("Repr-Digest") { nil }
+ allow(response).to receive(:[]).with("Digest") { nil }
+ allow(response).to receive(:[]).with("ETag") { nil }
expect(fetcher).to receive(:call) { response }
- updater.update(local_path, remote_path)
+ updater.update(remote_path, local_path, etag_path)
ensure
Encoding.default_internal = previous_internal_encoding
$VERBOSE = old_verbose
diff --git a/spec/bundler/bundler/definition_spec.rb b/spec/bundler/bundler/definition_spec.rb
index 59b958ae42..28c04e0860 100644
--- a/spec/bundler/bundler/definition_spec.rb
+++ b/spec/bundler/bundler/definition_spec.rb
@@ -5,58 +5,60 @@ require "bundler/definition"
RSpec.describe Bundler::Definition do
describe "#lock" do
before do
- allow(Bundler).to receive(:settings) { Bundler::Settings.new(".") }
- allow(Bundler::SharedHelpers).to receive(:find_gemfile) { Pathname.new("Gemfile") }
- allow(Bundler).to receive(:ui) { double("UI", :info => "", :debug => "") }
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile) { bundled_app_gemfile }
+ allow(Bundler).to receive(:ui) { double("UI", info: "", debug: "") }
end
- context "when it's not possible to write to the file" do
- subject { Bundler::Definition.new(nil, [], Bundler::SourceList.new, []) }
+ subject { Bundler::Definition.new(bundled_app_lock, [], Bundler::SourceList.new, {}) }
+
+ context "when it's not possible to write to the file" do
it "raises an PermissionError with explanation" do
allow(File).to receive(:open).and_call_original
- expect(File).to receive(:open).with("Gemfile.lock", "wb").
+ expect(File).to receive(:open).with(bundled_app_lock, "wb").
and_raise(Errno::EACCES)
- expect { subject.lock("Gemfile.lock") }.
+ expect { subject.lock }.
to raise_error(Bundler::PermissionError, /Gemfile\.lock/)
end
end
context "when a temporary resource access issue occurs" do
- subject { Bundler::Definition.new(nil, [], Bundler::SourceList.new, []) }
-
it "raises a TemporaryResourceError with explanation" do
allow(File).to receive(:open).and_call_original
- expect(File).to receive(:open).with("Gemfile.lock", "wb").
+ expect(File).to receive(:open).with(bundled_app_lock, "wb").
and_raise(Errno::EAGAIN)
- expect { subject.lock("Gemfile.lock") }.
+ expect { subject.lock }.
to raise_error(Bundler::TemporaryResourceError, /temporarily unavailable/)
end
end
context "when Bundler::Definition.no_lock is set to true" do
- subject { Bundler::Definition.new(nil, [], Bundler::SourceList.new, []) }
before { Bundler::Definition.no_lock = true }
after { Bundler::Definition.no_lock = false }
it "does not create a lock file" do
- subject.lock("Gemfile.lock")
- expect(File.file?("Gemfile.lock")).to eq false
+ subject.lock
+ expect(bundled_app_lock).not_to be_file
end
end
end
describe "detects changes" do
it "for a path gem with changes" do
- build_lib "foo", "1.0", :path => lib_path("foo")
+ build_lib "foo", "1.0", path: lib_path("foo")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => "#{lib_path("foo")}"
G
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
s.add_dependency "rack", "1.0"
end
- bundle :install, :env => { "DEBUG" => "1" }
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "foo", "1.0"
+ c.checksum gem_repo1, "rack", "1.0.0"
+ end
+
+ bundle :install, env: { "DEBUG" => "1" }
expect(out).to match(/re-resolving dependencies/)
expect(lockfile).to eq <<~G
@@ -76,7 +78,7 @@ RSpec.describe Bundler::Definition do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -95,26 +97,28 @@ RSpec.describe Bundler::Definition do
bundle "lock --add-platform java"
- bundle "update ffi", :env => { "DEBUG" => "1" }
+ bundle "update ffi", env: { "DEBUG" => "1" }
expect(out).to match(/because bundler is unlocking gems: \(ffi\)/)
end
it "for a path gem with deps and no changes" do
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
s.add_dependency "rack", "1.0"
s.add_development_dependency "net-ssh", "1.0"
end
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "foo", "1.0"
+ c.checksum gem_repo1, "rack", "1.0.0"
+ end
+
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => "#{lib_path("foo")}"
G
- bundle :check, :env => { "DEBUG" => "1" }
-
- expect(out).to match(/using resolution from the lockfile/)
- expect(lockfile).to eq <<~G
+ expected_lockfile = <<~G
PATH
remote: #{lib_path("foo")}
specs:
@@ -131,20 +135,31 @@ RSpec.describe Bundler::Definition do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
+
+ expect(lockfile).to eq(expected_lockfile)
+
+ bundle :check, env: { "DEBUG" => "1" }
+
+ expect(out).to match(/using resolution from the lockfile/)
+ expect(lockfile).to eq(expected_lockfile)
end
it "for a locked gem for another platform" do
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "only_java", "1.1", "java"
+ end
+
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "only_java", platform: :jruby
G
bundle "lock --add-platform java"
- bundle :check, :env => { "DEBUG" => "1" }
+ bundle :check, env: { "DEBUG" => "1" }
expect(out).to match(/using resolution from the lockfile/)
expect(lockfile).to eq <<~G
@@ -158,19 +173,23 @@ RSpec.describe Bundler::Definition do
DEPENDENCIES
only_java
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
end
it "for a rubygems gem" do
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo1, "foo", "1.0"
+ end
+
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "foo"
G
- bundle :check, :env => { "DEBUG" => "1" }
+ bundle :check, env: { "DEBUG" => "1" }
expect(out).to match(/using resolution from the lockfile/)
expect(lockfile).to eq <<~G
@@ -184,7 +203,7 @@ RSpec.describe Bundler::Definition do
DEPENDENCIES
foo
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -260,7 +279,7 @@ RSpec.describe Bundler::Definition do
bundled_app_lock,
updated_deps_in_gemfile,
source_list,
- :gems => ["shared_owner_a"], :conservative => true
+ gems: ["shared_owner_a"], conservative: true
)
locked = definition.send(:converge_locked_specs).map(&:name)
expect(locked).to eq %w[isolated_dep isolated_owner shared_dep shared_owner_b]
diff --git a/spec/bundler/bundler/dependency_spec.rb b/spec/bundler/bundler/dependency_spec.rb
index 1ef511f1aa..a953372742 100644
--- a/spec/bundler/bundler/dependency_spec.rb
+++ b/spec/bundler/bundler/dependency_spec.rb
@@ -40,116 +40,123 @@ RSpec.describe Bundler::Dependency do
# rubocop:disable Naming/VariableNumber
let(:platforms) do
- { :ruby => Gem::Platform::RUBY,
- :ruby_18 => Gem::Platform::RUBY,
- :ruby_19 => Gem::Platform::RUBY,
- :ruby_20 => Gem::Platform::RUBY,
- :ruby_21 => Gem::Platform::RUBY,
- :ruby_22 => Gem::Platform::RUBY,
- :ruby_23 => Gem::Platform::RUBY,
- :ruby_24 => Gem::Platform::RUBY,
- :ruby_25 => Gem::Platform::RUBY,
- :ruby_26 => Gem::Platform::RUBY,
- :ruby_27 => Gem::Platform::RUBY,
- :ruby_30 => Gem::Platform::RUBY,
- :ruby_31 => Gem::Platform::RUBY,
- :ruby_32 => Gem::Platform::RUBY,
- :ruby_33 => Gem::Platform::RUBY,
- :mri => Gem::Platform::RUBY,
- :mri_18 => Gem::Platform::RUBY,
- :mri_19 => Gem::Platform::RUBY,
- :mri_20 => Gem::Platform::RUBY,
- :mri_21 => Gem::Platform::RUBY,
- :mri_22 => Gem::Platform::RUBY,
- :mri_23 => Gem::Platform::RUBY,
- :mri_24 => Gem::Platform::RUBY,
- :mri_25 => Gem::Platform::RUBY,
- :mri_26 => Gem::Platform::RUBY,
- :mri_27 => Gem::Platform::RUBY,
- :mri_30 => Gem::Platform::RUBY,
- :mri_31 => Gem::Platform::RUBY,
- :mri_32 => Gem::Platform::RUBY,
- :mri_33 => Gem::Platform::RUBY,
- :rbx => Gem::Platform::RUBY,
- :truffleruby => Gem::Platform::RUBY,
- :jruby => Gem::Platform::JAVA,
- :jruby_18 => Gem::Platform::JAVA,
- :jruby_19 => Gem::Platform::JAVA,
- :windows => Gem::Platform::WINDOWS,
- :windows_18 => Gem::Platform::WINDOWS,
- :windows_19 => Gem::Platform::WINDOWS,
- :windows_20 => Gem::Platform::WINDOWS,
- :windows_21 => Gem::Platform::WINDOWS,
- :windows_22 => Gem::Platform::WINDOWS,
- :windows_23 => Gem::Platform::WINDOWS,
- :windows_24 => Gem::Platform::WINDOWS,
- :windows_25 => Gem::Platform::WINDOWS,
- :windows_26 => Gem::Platform::WINDOWS,
- :windows_27 => Gem::Platform::WINDOWS,
- :windows_30 => Gem::Platform::WINDOWS,
- :windows_31 => Gem::Platform::WINDOWS,
- :windows_32 => Gem::Platform::WINDOWS,
- :windows_33 => Gem::Platform::WINDOWS }
+ { ruby: Gem::Platform::RUBY,
+ ruby_18: Gem::Platform::RUBY,
+ ruby_19: Gem::Platform::RUBY,
+ ruby_20: Gem::Platform::RUBY,
+ ruby_21: Gem::Platform::RUBY,
+ ruby_22: Gem::Platform::RUBY,
+ ruby_23: Gem::Platform::RUBY,
+ ruby_24: Gem::Platform::RUBY,
+ ruby_25: Gem::Platform::RUBY,
+ ruby_26: Gem::Platform::RUBY,
+ ruby_27: Gem::Platform::RUBY,
+ ruby_30: Gem::Platform::RUBY,
+ ruby_31: Gem::Platform::RUBY,
+ ruby_32: Gem::Platform::RUBY,
+ ruby_33: Gem::Platform::RUBY,
+ ruby_34: Gem::Platform::RUBY,
+ mri: Gem::Platform::RUBY,
+ mri_18: Gem::Platform::RUBY,
+ mri_19: Gem::Platform::RUBY,
+ mri_20: Gem::Platform::RUBY,
+ mri_21: Gem::Platform::RUBY,
+ mri_22: Gem::Platform::RUBY,
+ mri_23: Gem::Platform::RUBY,
+ mri_24: Gem::Platform::RUBY,
+ mri_25: Gem::Platform::RUBY,
+ mri_26: Gem::Platform::RUBY,
+ mri_27: Gem::Platform::RUBY,
+ mri_30: Gem::Platform::RUBY,
+ mri_31: Gem::Platform::RUBY,
+ mri_32: Gem::Platform::RUBY,
+ mri_33: Gem::Platform::RUBY,
+ mri_34: Gem::Platform::RUBY,
+ rbx: Gem::Platform::RUBY,
+ truffleruby: Gem::Platform::RUBY,
+ jruby: Gem::Platform::JAVA,
+ jruby_18: Gem::Platform::JAVA,
+ jruby_19: Gem::Platform::JAVA,
+ windows: Gem::Platform::WINDOWS,
+ windows_18: Gem::Platform::WINDOWS,
+ windows_19: Gem::Platform::WINDOWS,
+ windows_20: Gem::Platform::WINDOWS,
+ windows_21: Gem::Platform::WINDOWS,
+ windows_22: Gem::Platform::WINDOWS,
+ windows_23: Gem::Platform::WINDOWS,
+ windows_24: Gem::Platform::WINDOWS,
+ windows_25: Gem::Platform::WINDOWS,
+ windows_26: Gem::Platform::WINDOWS,
+ windows_27: Gem::Platform::WINDOWS,
+ windows_30: Gem::Platform::WINDOWS,
+ windows_31: Gem::Platform::WINDOWS,
+ windows_32: Gem::Platform::WINDOWS,
+ windows_33: Gem::Platform::WINDOWS,
+ windows_34: Gem::Platform::WINDOWS }
end
let(:deprecated) do
- { :mswin => Gem::Platform::MSWIN,
- :mswin_18 => Gem::Platform::MSWIN,
- :mswin_19 => Gem::Platform::MSWIN,
- :mswin_20 => Gem::Platform::MSWIN,
- :mswin_21 => Gem::Platform::MSWIN,
- :mswin_22 => Gem::Platform::MSWIN,
- :mswin_23 => Gem::Platform::MSWIN,
- :mswin_24 => Gem::Platform::MSWIN,
- :mswin_25 => Gem::Platform::MSWIN,
- :mswin_26 => Gem::Platform::MSWIN,
- :mswin_27 => Gem::Platform::MSWIN,
- :mswin_30 => Gem::Platform::MSWIN,
- :mswin_31 => Gem::Platform::MSWIN,
- :mswin_32 => Gem::Platform::MSWIN,
- :mswin_33 => Gem::Platform::MSWIN,
- :mswin64 => Gem::Platform::MSWIN64,
- :mswin64_19 => Gem::Platform::MSWIN64,
- :mswin64_20 => Gem::Platform::MSWIN64,
- :mswin64_21 => Gem::Platform::MSWIN64,
- :mswin64_22 => Gem::Platform::MSWIN64,
- :mswin64_23 => Gem::Platform::MSWIN64,
- :mswin64_24 => Gem::Platform::MSWIN64,
- :mswin64_25 => Gem::Platform::MSWIN64,
- :mswin64_26 => Gem::Platform::MSWIN64,
- :mswin64_27 => Gem::Platform::MSWIN64,
- :mswin64_30 => Gem::Platform::MSWIN64,
- :mswin64_31 => Gem::Platform::MSWIN64,
- :mswin64_32 => Gem::Platform::MSWIN64,
- :mswin64_33 => Gem::Platform::MSWIN64,
- :mingw => Gem::Platform::MINGW,
- :mingw_18 => Gem::Platform::MINGW,
- :mingw_19 => Gem::Platform::MINGW,
- :mingw_20 => Gem::Platform::MINGW,
- :mingw_21 => Gem::Platform::MINGW,
- :mingw_22 => Gem::Platform::MINGW,
- :mingw_23 => Gem::Platform::MINGW,
- :mingw_24 => Gem::Platform::MINGW,
- :mingw_25 => Gem::Platform::MINGW,
- :mingw_26 => Gem::Platform::MINGW,
- :mingw_27 => Gem::Platform::MINGW,
- :mingw_30 => Gem::Platform::MINGW,
- :mingw_31 => Gem::Platform::MINGW,
- :mingw_32 => Gem::Platform::MINGW,
- :mingw_33 => Gem::Platform::MINGW,
- :x64_mingw => Gem::Platform::X64_MINGW,
- :x64_mingw_20 => Gem::Platform::X64_MINGW,
- :x64_mingw_21 => Gem::Platform::X64_MINGW,
- :x64_mingw_22 => Gem::Platform::X64_MINGW,
- :x64_mingw_23 => Gem::Platform::X64_MINGW,
- :x64_mingw_24 => Gem::Platform::X64_MINGW,
- :x64_mingw_25 => Gem::Platform::X64_MINGW,
- :x64_mingw_26 => Gem::Platform::X64_MINGW,
- :x64_mingw_27 => Gem::Platform::X64_MINGW,
- :x64_mingw_30 => Gem::Platform::X64_MINGW,
- :x64_mingw_31 => Gem::Platform::X64_MINGW,
- :x64_mingw_32 => Gem::Platform::X64_MINGW,
- :x64_mingw_33 => Gem::Platform::X64_MINGW }
+ { mswin: Gem::Platform::MSWIN,
+ mswin_18: Gem::Platform::MSWIN,
+ mswin_19: Gem::Platform::MSWIN,
+ mswin_20: Gem::Platform::MSWIN,
+ mswin_21: Gem::Platform::MSWIN,
+ mswin_22: Gem::Platform::MSWIN,
+ mswin_23: Gem::Platform::MSWIN,
+ mswin_24: Gem::Platform::MSWIN,
+ mswin_25: Gem::Platform::MSWIN,
+ mswin_26: Gem::Platform::MSWIN,
+ mswin_27: Gem::Platform::MSWIN,
+ mswin_30: Gem::Platform::MSWIN,
+ mswin_31: Gem::Platform::MSWIN,
+ mswin_32: Gem::Platform::MSWIN,
+ mswin_33: Gem::Platform::MSWIN,
+ mswin_34: Gem::Platform::MSWIN,
+ mswin64: Gem::Platform::MSWIN64,
+ mswin64_19: Gem::Platform::MSWIN64,
+ mswin64_20: Gem::Platform::MSWIN64,
+ mswin64_21: Gem::Platform::MSWIN64,
+ mswin64_22: Gem::Platform::MSWIN64,
+ mswin64_23: Gem::Platform::MSWIN64,
+ mswin64_24: Gem::Platform::MSWIN64,
+ mswin64_25: Gem::Platform::MSWIN64,
+ mswin64_26: Gem::Platform::MSWIN64,
+ mswin64_27: Gem::Platform::MSWIN64,
+ mswin64_30: Gem::Platform::MSWIN64,
+ mswin64_31: Gem::Platform::MSWIN64,
+ mswin64_32: Gem::Platform::MSWIN64,
+ mswin64_33: Gem::Platform::MSWIN64,
+ mswin64_34: Gem::Platform::MSWIN64,
+ mingw: Gem::Platform::MINGW,
+ mingw_18: Gem::Platform::MINGW,
+ mingw_19: Gem::Platform::MINGW,
+ mingw_20: Gem::Platform::MINGW,
+ mingw_21: Gem::Platform::MINGW,
+ mingw_22: Gem::Platform::MINGW,
+ mingw_23: Gem::Platform::MINGW,
+ mingw_24: Gem::Platform::MINGW,
+ mingw_25: Gem::Platform::MINGW,
+ mingw_26: Gem::Platform::MINGW,
+ mingw_27: Gem::Platform::MINGW,
+ mingw_30: Gem::Platform::MINGW,
+ mingw_31: Gem::Platform::MINGW,
+ mingw_32: Gem::Platform::MINGW,
+ mingw_33: Gem::Platform::MINGW,
+ mingw_34: Gem::Platform::MINGW,
+ x64_mingw: Gem::Platform::X64_MINGW,
+ x64_mingw_20: Gem::Platform::X64_MINGW,
+ x64_mingw_21: Gem::Platform::X64_MINGW,
+ x64_mingw_22: Gem::Platform::X64_MINGW,
+ x64_mingw_23: Gem::Platform::X64_MINGW,
+ x64_mingw_24: Gem::Platform::X64_MINGW,
+ x64_mingw_25: Gem::Platform::X64_MINGW,
+ x64_mingw_26: Gem::Platform::X64_MINGW,
+ x64_mingw_27: Gem::Platform::X64_MINGW,
+ x64_mingw_30: Gem::Platform::X64_MINGW,
+ x64_mingw_31: Gem::Platform::X64_MINGW,
+ x64_mingw_32: Gem::Platform::X64_MINGW,
+ x64_mingw_33: Gem::Platform::X64_MINGW,
+ x64_mingw_34: Gem::Platform::X64_MINGW }
end
# rubocop:enable Naming/VariableNumber
diff --git a/spec/bundler/bundler/digest_spec.rb b/spec/bundler/bundler/digest_spec.rb
index fd7b0c968e..f876827964 100644
--- a/spec/bundler/bundler/digest_spec.rb
+++ b/spec/bundler/bundler/digest_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe Bundler::Digest do
it "is compatible with stdlib" do
random_strings = ["foo", "skfjsdlkfjsdf", "3924m", "ldskfj"]
- # https://datatracker.ietf.org/doc/html/rfc3174#section-7.3
+ # https://www.rfc-editor.org/rfc/rfc3174#section-7.3
rfc3174_test_cases = ["abc", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "a", "01234567" * 8]
(random_strings + rfc3174_test_cases).each do |payload|
diff --git a/spec/bundler/bundler/dsl_spec.rb b/spec/bundler/bundler/dsl_spec.rb
index 8b5bf930f2..3c3b6c26c3 100644
--- a/spec/bundler/bundler/dsl_spec.rb
+++ b/spec/bundler/bundler/dsl_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe Bundler::Dsl do
it "registers custom hosts" do
subject.git_source(:example) {|repo_name| "git@git.example.com:#{repo_name}.git" }
subject.git_source(:foobar) {|repo_name| "git@foobar.com:#{repo_name}.git" }
- subject.gem("dobry-pies", :example => "strzalek/dobry-pies")
+ subject.gem("dobry-pies", example: "strzalek/dobry-pies")
example_uri = "git@git.example.com:strzalek/dobry-pies.git"
expect(subject.dependencies.first.source.uri).to eq(example_uri)
end
@@ -26,85 +26,136 @@ RSpec.describe Bundler::Dsl do
end
it "converts :github PR to URI using https" do
- subject.gem("sparks", :github => "https://github.com/indirect/sparks/pull/5")
+ subject.gem("sparks", github: "https://github.com/indirect/sparks/pull/5")
github_uri = "https://github.com/indirect/sparks.git"
expect(subject.dependencies.first.source.uri).to eq(github_uri)
expect(subject.dependencies.first.source.ref).to eq("refs/pull/5/head")
end
+ it "converts :gitlab PR to URI using https" do
+ subject.gem("sparks", gitlab: "https://gitlab.com/indirect/sparks/-/merge_requests/5")
+ gitlab_uri = "https://gitlab.com/indirect/sparks.git"
+ expect(subject.dependencies.first.source.uri).to eq(gitlab_uri)
+ expect(subject.dependencies.first.source.ref).to eq("refs/merge-requests/5/head")
+ end
+
it "rejects :github PR URI with a branch, ref or tag" do
expect do
- subject.gem("sparks", :github => "https://github.com/indirect/sparks/pull/5", :branch => "foo")
+ subject.gem("sparks", github: "https://github.com/indirect/sparks/pull/5", branch: "foo")
end.to raise_error(
Bundler::GemfileError,
%(The :branch option can't be used with `github: "https://github.com/indirect/sparks/pull/5"`),
)
expect do
- subject.gem("sparks", :github => "https://github.com/indirect/sparks/pull/5", :ref => "foo")
+ subject.gem("sparks", github: "https://github.com/indirect/sparks/pull/5", ref: "foo")
end.to raise_error(
Bundler::GemfileError,
%(The :ref option can't be used with `github: "https://github.com/indirect/sparks/pull/5"`),
)
expect do
- subject.gem("sparks", :github => "https://github.com/indirect/sparks/pull/5", :tag => "foo")
+ subject.gem("sparks", github: "https://github.com/indirect/sparks/pull/5", tag: "foo")
end.to raise_error(
Bundler::GemfileError,
%(The :tag option can't be used with `github: "https://github.com/indirect/sparks/pull/5"`),
)
end
+ it "rejects :gitlab PR URI with a branch, ref or tag" do
+ expect do
+ subject.gem("sparks", gitlab: "https://gitlab.com/indirect/sparks/-/merge_requests/5", branch: "foo")
+ end.to raise_error(
+ Bundler::GemfileError,
+ %(The :branch option can't be used with `gitlab: "https://gitlab.com/indirect/sparks/-/merge_requests/5"`),
+ )
+
+ expect do
+ subject.gem("sparks", gitlab: "https://gitlab.com/indirect/sparks/-/merge_requests/5", ref: "foo")
+ end.to raise_error(
+ Bundler::GemfileError,
+ %(The :ref option can't be used with `gitlab: "https://gitlab.com/indirect/sparks/-/merge_requests/5"`),
+ )
+
+ expect do
+ subject.gem("sparks", gitlab: "https://gitlab.com/indirect/sparks/-/merge_requests/5", tag: "foo")
+ end.to raise_error(
+ Bundler::GemfileError,
+ %(The :tag option can't be used with `gitlab: "https://gitlab.com/indirect/sparks/-/merge_requests/5"`),
+ )
+ end
+
it "rejects :github with :git" do
expect do
- subject.gem("sparks", :github => "indirect/sparks", :git => "https://github.com/indirect/sparks.git")
+ subject.gem("sparks", github: "indirect/sparks", git: "https://github.com/indirect/sparks.git")
end.to raise_error(
Bundler::GemfileError,
%(The :git option can't be used with `github: "indirect/sparks"`),
)
end
- context "default hosts", :bundler => "< 3" do
+ it "rejects :gitlab with :git" do
+ expect do
+ subject.gem("sparks", gitlab: "indirect/sparks", git: "https://gitlab.com/indirect/sparks.git")
+ end.to raise_error(
+ Bundler::GemfileError,
+ %(The :git option can't be used with `gitlab: "indirect/sparks"`),
+ )
+ end
+
+ context "default hosts", bundler: "< 3" do
it "converts :github to URI using https" do
- subject.gem("sparks", :github => "indirect/sparks")
+ subject.gem("sparks", github: "indirect/sparks")
github_uri = "https://github.com/indirect/sparks.git"
expect(subject.dependencies.first.source.uri).to eq(github_uri)
end
it "converts :github shortcut to URI using https" do
- subject.gem("sparks", :github => "rails")
+ subject.gem("sparks", github: "rails")
github_uri = "https://github.com/rails/rails.git"
expect(subject.dependencies.first.source.uri).to eq(github_uri)
end
+ it "converts :gitlab to URI using https" do
+ subject.gem("sparks", gitlab: "indirect/sparks")
+ gitlab_uri = "https://gitlab.com/indirect/sparks.git"
+ expect(subject.dependencies.first.source.uri).to eq(gitlab_uri)
+ end
+
+ it "converts :gitlab shortcut to URI using https" do
+ subject.gem("sparks", gitlab: "rails")
+ gitlab_uri = "https://gitlab.com/rails/rails.git"
+ expect(subject.dependencies.first.source.uri).to eq(gitlab_uri)
+ end
+
it "converts numeric :gist to :git" do
- subject.gem("not-really-a-gem", :gist => 2_859_988)
+ subject.gem("not-really-a-gem", gist: 2_859_988)
github_uri = "https://gist.github.com/2859988.git"
expect(subject.dependencies.first.source.uri).to eq(github_uri)
end
it "converts :gist to :git" do
- subject.gem("not-really-a-gem", :gist => "2859988")
+ subject.gem("not-really-a-gem", gist: "2859988")
github_uri = "https://gist.github.com/2859988.git"
expect(subject.dependencies.first.source.uri).to eq(github_uri)
end
it "converts :bitbucket to :git" do
- subject.gem("not-really-a-gem", :bitbucket => "mcorp/flatlab-rails")
+ subject.gem("not-really-a-gem", bitbucket: "mcorp/flatlab-rails")
bitbucket_uri = "https://mcorp@bitbucket.org/mcorp/flatlab-rails.git"
expect(subject.dependencies.first.source.uri).to eq(bitbucket_uri)
end
it "converts 'mcorp' to 'mcorp/mcorp'" do
- subject.gem("not-really-a-gem", :bitbucket => "mcorp")
+ subject.gem("not-really-a-gem", bitbucket: "mcorp")
bitbucket_uri = "https://mcorp@bitbucket.org/mcorp/mcorp.git"
expect(subject.dependencies.first.source.uri).to eq(bitbucket_uri)
end
end
context "default git sources" do
- it "has bitbucket, gist, and github" do
- expect(subject.instance_variable_get(:@git_sources).keys.sort).to eq(%w[bitbucket gist github])
+ it "has bitbucket, gist, github, and gitlab" do
+ expect(subject.instance_variable_get(:@git_sources).keys.sort).to eq(%w[bitbucket gist github gitlab])
end
end
end
@@ -134,6 +185,17 @@ RSpec.describe Bundler::Dsl do
expect { subject.eval_gemfile("Gemfile") }.
to raise_error(Bundler::GemfileError, /There was an error evaluating `Gemfile`: ruby_version must match the :engine_version for MRI/)
end
+
+ it "populates __dir__ and __FILE__ correctly" do
+ abs_path = source_root.join("../fragment.rb").to_s
+ expect(Bundler).to receive(:read_file).with(abs_path).and_return(<<~RUBY)
+ @fragment_dir = __dir__
+ @fragment_file = __FILE__
+ RUBY
+ subject.eval_gemfile("../fragment.rb")
+ expect(subject.instance_variable_get(:@fragment_dir)).to eq(source_root.dirname.to_s)
+ expect(subject.instance_variable_get(:@fragment_file)).to eq(abs_path)
+ end
end
describe "#gem" do
@@ -142,18 +204,18 @@ RSpec.describe Bundler::Dsl do
:ruby_30, :ruby_31, :ruby_32, :ruby_33, :mri, :mri_18, :mri_19, :mri_20, :mri_21, :mri_22, :mri_23, :mri_24,
:mri_25, :mri_26, :mri_27, :mri_30, :mri_31, :mri_32, :mri_33, :jruby, :rbx, :truffleruby].each do |platform|
it "allows #{platform} as a valid platform" do
- subject.gem("foo", :platform => platform)
+ subject.gem("foo", platform: platform)
end
end
# rubocop:enable Naming/VariableNumber
it "allows platforms matching the running Ruby version" do
platform = "ruby_#{RbConfig::CONFIG["MAJOR"]}#{RbConfig::CONFIG["MINOR"]}"
- subject.gem("foo", :platform => platform)
+ subject.gem("foo", platform: platform)
end
it "rejects invalid platforms" do
- expect { subject.gem("foo", :platform => :bogus) }.
+ expect { subject.gem("foo", platform: :bogus) }.
to raise_error(Bundler::GemfileError, /is not a valid platform/)
end
@@ -203,19 +265,19 @@ RSpec.describe Bundler::Dsl do
end
it "rejects branch option on non-git gems" do
- expect { subject.gem("foo", :branch => "test") }.
+ expect { subject.gem("foo", branch: "test") }.
to raise_error(Bundler::GemfileError, /The `branch` option for `gem 'foo'` is not allowed. Only gems with a git source can specify a branch/)
end
it "allows specifying a branch on git gems" do
- subject.gem("foo", :branch => "test", :git => "http://mytestrepo")
+ subject.gem("foo", branch: "test", git: "http://mytestrepo")
dep = subject.dependencies.last
expect(dep.name).to eq "foo"
end
it "allows specifying a branch on git gems with a git_source" do
subject.git_source(:test_source) {|n| "https://github.com/#{n}" }
- subject.gem("foo", :branch => "test", :test_source => "bundler/bundler")
+ subject.gem("foo", branch: "test", test_source: "bundler/bundler")
dep = subject.dependencies.last
expect(dep.name).to eq "foo"
end
@@ -280,7 +342,7 @@ RSpec.describe Bundler::Dsl do
allow(Bundler).to receive(:default_gemfile).and_return(Pathname.new("./Gemfile"))
subject.source("https://other-source.org") do
- subject.gem("dobry-pies", :path => "foo")
+ subject.gem("dobry-pies", path: "foo")
subject.gem("foo")
end
@@ -292,7 +354,7 @@ RSpec.describe Bundler::Dsl do
describe "#check_primary_source_safety" do
context "when a global source is not defined implicitly" do
it "will raise a major deprecation warning" do
- not_a_global_source = double("not-a-global-source", :no_remotes? => true)
+ not_a_global_source = double("not-a-global-source", no_remotes?: true)
allow(Bundler::Source::Rubygems).to receive(:new).and_return(not_a_global_source)
warning = "This Gemfile does not include an explicit global source. " \
diff --git a/spec/bundler/bundler/endpoint_specification_spec.rb b/spec/bundler/bundler/endpoint_specification_spec.rb
index 7dd6925007..e7e10730cf 100644
--- a/spec/bundler/bundler/endpoint_specification_spec.rb
+++ b/spec/bundler/bundler/endpoint_specification_spec.rb
@@ -60,21 +60,21 @@ RSpec.describe Bundler::EndpointSpecification do
it "should return the remote spec value when not set on endpoint specification and remote spec has one" do
remote_value = "remote_value"
- remote_spec = double(:remote_spec, :required_ruby_version => remote_value, :required_rubygems_version => nil)
+ remote_spec = double(:remote_spec, required_ruby_version: remote_value, required_rubygems_version: nil)
allow(spec_fetcher).to receive(:fetch_spec).and_return(remote_spec)
expect(spec.required_ruby_version). eql?(remote_value)
end
it "should use the default Gem Requirement value when not set on endpoint specification and not set on remote spec" do
- remote_spec = double(:remote_spec, :required_ruby_version => nil, :required_rubygems_version => nil)
+ remote_spec = double(:remote_spec, required_ruby_version: nil, required_rubygems_version: nil)
allow(spec_fetcher).to receive(:fetch_spec).and_return(remote_spec)
expect(spec.required_ruby_version). eql?(Gem::Requirement.default)
end
end
it "supports equality comparison" do
- remote_spec = double(:remote_spec, :required_ruby_version => nil, :required_rubygems_version => nil)
+ remote_spec = double(:remote_spec, required_ruby_version: nil, required_rubygems_version: nil)
allow(spec_fetcher).to receive(:fetch_spec).and_return(remote_spec)
other_spec = described_class.new("bar", version, platform, spec_fetcher, dependencies, metadata)
expect(spec).to eql(spec)
diff --git a/spec/bundler/bundler/env_spec.rb b/spec/bundler/bundler/env_spec.rb
index 186b207b9f..7997cb0c40 100644
--- a/spec/bundler/bundler/env_spec.rb
+++ b/spec/bundler/bundler/env_spec.rb
@@ -88,7 +88,7 @@ RSpec.describe Bundler::Env do
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
end
- let(:output) { described_class.report(:print_gemfile => true) }
+ let(:output) { described_class.report(print_gemfile: true) }
it "prints the Gemfile" do
expect(output).to include("Gemfile")
@@ -102,7 +102,7 @@ RSpec.describe Bundler::Env do
end
context "when there no Gemfile and print_gemfile is true" do
- let(:output) { described_class.report(:print_gemfile => true) }
+ let(:output) { described_class.report(print_gemfile: true) }
it "prints the environment" do
expect(output).to start_with("## Environment")
@@ -114,7 +114,7 @@ RSpec.describe Bundler::Env do
bundle "config set https://localgemserver.test/ user:pass"
end
- let(:output) { described_class.report(:print_gemfile => true) }
+ let(:output) { described_class.report(print_gemfile: true) }
it "prints the config with redacted values" do
expect(output).to include("https://localgemserver.test")
@@ -128,7 +128,7 @@ RSpec.describe Bundler::Env do
bundle "config set https://localgemserver.test/ api_token:x-oauth-basic"
end
- let(:output) { described_class.report(:print_gemfile => true) }
+ let(:output) { described_class.report(print_gemfile: true) }
it "prints the config with redacted values" do
expect(output).to include("https://localgemserver.test")
@@ -139,7 +139,7 @@ RSpec.describe Bundler::Env do
context "when Gemfile contains a gemspec and print_gemspecs is true" do
let(:gemspec) do
- strip_whitespace(<<-GEMSPEC)
+ <<~GEMSPEC
Gem::Specification.new do |gem|
gem.name = "foo"
gem.author = "Fumofu"
@@ -158,7 +158,7 @@ RSpec.describe Bundler::Env do
end
it "prints the gemspec" do
- output = described_class.report(:print_gemspecs => true)
+ output = described_class.report(print_gemspecs: true)
expect(output).to include("foo.gemspec")
expect(output).to include(gemspec)
@@ -177,8 +177,8 @@ RSpec.describe Bundler::Env do
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
allow(Bundler::SharedHelpers).to receive(:pwd).and_return(bundled_app)
- output = described_class.report(:print_gemspecs => true)
- expect(output).to include(strip_whitespace(<<-ENV))
+ output = described_class.report(print_gemspecs: true)
+ expect(output).to include(<<~ENV)
## Gemfile
### Gemfile
@@ -217,7 +217,7 @@ RSpec.describe Bundler::Env do
context "when the git version is OS specific" do
it "includes OS specific information with the version number" do
- expect(git_proxy_stub).to receive(:git).with("--version").
+ expect(git_proxy_stub).to receive(:git_local).with("--version").
and_return("git version 1.2.3 (Apple Git-BS)")
expect(Bundler::Source::Git::GitProxy).to receive(:new).and_return(git_proxy_stub)
diff --git a/spec/bundler/bundler/environment_preserver_spec.rb b/spec/bundler/bundler/environment_preserver_spec.rb
index 530ca6f835..6c7066d0c6 100644
--- a/spec/bundler/bundler/environment_preserver_spec.rb
+++ b/spec/bundler/bundler/environment_preserver_spec.rb
@@ -27,8 +27,12 @@ RSpec.describe Bundler::EnvironmentPreserver do
context "when a key is empty" do
let(:env) { { "foo" => "" } }
- it "should not create backup entries" do
- expect(subject).not_to have_key "BUNDLER_ORIG_foo"
+ it "should keep the original entry" do
+ expect(subject["foo"]).to be_empty
+ end
+
+ it "should still create backup entries" do
+ expect(subject).to have_key "BUNDLER_ORIG_foo"
end
end
@@ -71,8 +75,12 @@ RSpec.describe Bundler::EnvironmentPreserver do
context "when the original key is empty" do
let(:env) { { "foo" => "my-foo", "BUNDLER_ORIG_foo" => "" } }
- it "should keep the current value" do
- expect(subject["foo"]).to eq("my-foo")
+ it "should restore the original value" do
+ expect(subject["foo"]).to be_empty
+ end
+
+ it "should delete the backup value" do
+ expect(subject.key?("BUNDLER_ORIG_foo")).to eq(false)
end
end
end
diff --git a/spec/bundler/bundler/fetcher/base_spec.rb b/spec/bundler/bundler/fetcher/base_spec.rb
index 02506591f3..b8c6b57b10 100644
--- a/spec/bundler/bundler/fetcher/base_spec.rb
+++ b/spec/bundler/bundler/fetcher/base_spec.rb
@@ -4,15 +4,16 @@ RSpec.describe Bundler::Fetcher::Base do
let(:downloader) { double(:downloader) }
let(:remote) { double(:remote) }
let(:display_uri) { "http://sample_uri.com" }
+ let(:gem_remote_fetcher) { nil }
class TestClass < described_class; end
- subject { TestClass.new(downloader, remote, display_uri) }
+ subject { TestClass.new(downloader, remote, display_uri, gem_remote_fetcher) }
describe "#initialize" do
context "with the abstract Base class" do
it "should raise an error" do
- expect { described_class.new(downloader, remote, display_uri) }.to raise_error(RuntimeError, "Abstract class")
+ expect { described_class.new(downloader, remote, display_uri, gem_remote_fetcher) }.to raise_error(RuntimeError, "Abstract class")
end
end
@@ -36,7 +37,7 @@ RSpec.describe Bundler::Fetcher::Base do
end
describe "#fetch_uri" do
- let(:remote_uri_obj) { Bundler::URI("http://rubygems.org") }
+ let(:remote_uri_obj) { Gem::URI("http://rubygems.org") }
before { allow(subject).to receive(:remote_uri).and_return(remote_uri_obj) }
@@ -49,10 +50,10 @@ RSpec.describe Bundler::Fetcher::Base do
end
context "when the remote uri's host is not rubygems.org" do
- let(:remote_uri_obj) { Bundler::URI("http://otherhost.org") }
+ let(:remote_uri_obj) { Gem::URI("http://otherhost.org") }
it "should return the remote uri" do
- expect(subject.fetch_uri).to eq(Bundler::URI("http://otherhost.org"))
+ expect(subject.fetch_uri).to eq(Gem::URI("http://otherhost.org"))
end
end
diff --git a/spec/bundler/bundler/fetcher/compact_index_spec.rb b/spec/bundler/bundler/fetcher/compact_index_spec.rb
index 00eb27edea..a988171f34 100644
--- a/spec/bundler/bundler/fetcher/compact_index_spec.rb
+++ b/spec/bundler/bundler/fetcher/compact_index_spec.rb
@@ -5,9 +5,10 @@ require "bundler/compact_index_client"
RSpec.describe Bundler::Fetcher::CompactIndex do
let(:downloader) { double(:downloader) }
- let(:display_uri) { Bundler::URI("http://sampleuri.com") }
- let(:remote) { double(:remote, :cache_slug => "lsjdf", :uri => display_uri) }
- let(:compact_index) { described_class.new(downloader, remote, display_uri) }
+ let(:display_uri) { Gem::URI("http://sampleuri.com") }
+ let(:remote) { double(:remote, cache_slug: "lsjdf", uri: display_uri) }
+ let(:gem_remote_fetcher) { nil }
+ let(:compact_index) { described_class.new(downloader, remote, display_uri, gem_remote_fetcher) }
before do
allow(compact_index).to receive(:log_specs) {}
@@ -33,7 +34,7 @@ RSpec.describe Bundler::Fetcher::CompactIndex do
describe "#available?" do
before do
allow(compact_index).to receive(:compact_index_client).
- and_return(double(:compact_index_client, :update_and_parse_checksums! => true))
+ and_return(double(:compact_index_client, update_and_parse_checksums!: true))
end
it "returns true" do
diff --git a/spec/bundler/bundler/fetcher/dependency_spec.rb b/spec/bundler/bundler/fetcher/dependency_spec.rb
index 20307f9c99..c420b7c07f 100644
--- a/spec/bundler/bundler/fetcher/dependency_spec.rb
+++ b/spec/bundler/bundler/fetcher/dependency_spec.rb
@@ -2,10 +2,11 @@
RSpec.describe Bundler::Fetcher::Dependency do
let(:downloader) { double(:downloader) }
- let(:remote) { double(:remote, :uri => Bundler::URI("http://localhost:5000")) }
+ let(:remote) { double(:remote, uri: Gem::URI("http://localhost:5000")) }
let(:display_uri) { "http://sample_uri.com" }
+ let(:gem_remote_fetcher) { nil }
- subject { described_class.new(downloader, remote, display_uri) }
+ subject { described_class.new(downloader, remote, display_uri, gem_remote_fetcher) }
describe "#available?" do
let(:dependency_api_uri) { double(:dependency_api_uri) }
@@ -210,7 +211,7 @@ RSpec.describe Bundler::Fetcher::Dependency do
let(:gem_names) { [%w[foo bar], %w[bundler rubocop]] }
let(:dep_api_uri) { double(:dep_api_uri) }
let(:unmarshalled_gems) { double(:unmarshalled_gems) }
- let(:fetch_response) { double(:fetch_response, :body => double(:body)) }
+ let(:fetch_response) { double(:fetch_response, body: double(:body)) }
let(:rubygems_limit) { 50 }
before { allow(subject).to receive(:dependency_api_uri).with(gem_names).and_return(dep_api_uri) }
@@ -227,20 +228,20 @@ RSpec.describe Bundler::Fetcher::Dependency do
let(:gem_list) do
[
{
- :dependencies => {
+ dependencies: {
"resque" => "req3,req4",
},
- :name => "typhoeus",
- :number => "1.0.1",
- :platform => "ruby",
+ name: "typhoeus",
+ number: "1.0.1",
+ platform: "ruby",
},
{
- :dependencies => {
+ dependencies: {
"faraday" => "req1,req2",
},
- :name => "grape",
- :number => "2.0.2",
- :platform => "jruby",
+ name: "grape",
+ number: "2.0.2",
+ platform: "jruby",
},
]
end
@@ -254,7 +255,7 @@ RSpec.describe Bundler::Fetcher::Dependency do
end
describe "#dependency_api_uri" do
- let(:uri) { Bundler::URI("http://gem-api.com") }
+ let(:uri) { Gem::URI("http://gem-api.com") }
context "with gem names" do
let(:gem_names) { %w[foo bar bundler rubocop] }
diff --git a/spec/bundler/bundler/fetcher/downloader_spec.rb b/spec/bundler/bundler/fetcher/downloader_spec.rb
index 0412ddb83a..d5c32f4730 100644
--- a/spec/bundler/bundler/fetcher/downloader_spec.rb
+++ b/spec/bundler/bundler/fetcher/downloader_spec.rb
@@ -3,7 +3,7 @@
RSpec.describe Bundler::Fetcher::Downloader do
let(:connection) { double(:connection) }
let(:redirect_limit) { 5 }
- let(:uri) { Bundler::URI("http://www.uri-to-fetch.com/api/v2/endpoint") }
+ let(:uri) { Gem::URI("http://www.uri-to-fetch.com/api/v2/endpoint") }
let(:options) { double(:options) }
subject { described_class.new(connection, redirect_limit) }
@@ -27,7 +27,7 @@ RSpec.describe Bundler::Fetcher::Downloader do
end
context "logging" do
- let(:http_response) { Net::HTTPSuccess.new("1.1", 200, "Success") }
+ let(:http_response) { Gem::Net::HTTPSuccess.new("1.1", 200, "Success") }
it "should log the HTTP response code and message to debug" do
expect(Bundler).to receive_message_chain(:ui, :debug).with("HTTP 200 Success #{uri}")
@@ -35,48 +35,48 @@ RSpec.describe Bundler::Fetcher::Downloader do
end
end
- context "when the request response is a Net::HTTPRedirection" do
- let(:http_response) { Net::HTTPRedirection.new(httpv, 308, "Moved") }
+ context "when the request response is a Gem::Net::HTTPRedirection" do
+ let(:http_response) { Gem::Net::HTTPRedirection.new(httpv, 308, "Moved") }
before { http_response["location"] = "http://www.redirect-uri.com/api/v2/endpoint" }
it "should try to fetch the redirect uri and iterate the # requests counter" do
- expect(subject).to receive(:fetch).with(Bundler::URI("http://www.uri-to-fetch.com/api/v2/endpoint"), options, 0).and_call_original
- expect(subject).to receive(:fetch).with(Bundler::URI("http://www.redirect-uri.com/api/v2/endpoint"), options, 1)
+ expect(subject).to receive(:fetch).with(Gem::URI("http://www.uri-to-fetch.com/api/v2/endpoint"), options, 0).and_call_original
+ expect(subject).to receive(:fetch).with(Gem::URI("http://www.redirect-uri.com/api/v2/endpoint"), options, 1)
subject.fetch(uri, options, counter)
end
context "when the redirect uri and original uri are the same" do
- let(:uri) { Bundler::URI("ssh://username:password@www.uri-to-fetch.com/api/v2/endpoint") }
+ let(:uri) { Gem::URI("ssh://username:password@www.uri-to-fetch.com/api/v2/endpoint") }
before { http_response["location"] = "ssh://www.uri-to-fetch.com/api/v1/endpoint" }
it "should set the same user and password for the redirect uri" do
- expect(subject).to receive(:fetch).with(Bundler::URI("ssh://username:password@www.uri-to-fetch.com/api/v2/endpoint"), options, 0).and_call_original
- expect(subject).to receive(:fetch).with(Bundler::URI("ssh://username:password@www.uri-to-fetch.com/api/v1/endpoint"), options, 1)
+ expect(subject).to receive(:fetch).with(Gem::URI("ssh://username:password@www.uri-to-fetch.com/api/v2/endpoint"), options, 0).and_call_original
+ expect(subject).to receive(:fetch).with(Gem::URI("ssh://username:password@www.uri-to-fetch.com/api/v1/endpoint"), options, 1)
subject.fetch(uri, options, counter)
end
end
end
- context "when the request response is a Net::HTTPSuccess" do
- let(:http_response) { Net::HTTPSuccess.new("1.1", 200, "Success") }
+ context "when the request response is a Gem::Net::HTTPSuccess" do
+ let(:http_response) { Gem::Net::HTTPSuccess.new("1.1", 200, "Success") }
it "should return the response body" do
expect(subject.fetch(uri, options, counter)).to eq(http_response)
end
end
- context "when the request response is a Net::HTTPRequestEntityTooLarge" do
- let(:http_response) { Net::HTTPRequestEntityTooLarge.new("1.1", 413, "Too Big") }
+ context "when the request response is a Gem::Net::HTTPRequestEntityTooLarge" do
+ let(:http_response) { Gem::Net::HTTPRequestEntityTooLarge.new("1.1", 413, "Too Big") }
it "should raise a Bundler::Fetcher::FallbackError with the response body" do
expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::Fetcher::FallbackError, "Body with info")
end
end
- context "when the request response is a Net::HTTPUnauthorized" do
- let(:http_response) { Net::HTTPUnauthorized.new("1.1", 401, "Unauthorized") }
+ context "when the request response is a Gem::Net::HTTPUnauthorized" do
+ let(:http_response) { Gem::Net::HTTPUnauthorized.new("1.1", 401, "Unauthorized") }
it "should raise a Bundler::Fetcher::AuthenticationRequiredError with the uri host" do
expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::Fetcher::AuthenticationRequiredError,
@@ -89,7 +89,7 @@ RSpec.describe Bundler::Fetcher::Downloader do
end
context "when the there are credentials provided in the request" do
- let(:uri) { Bundler::URI("http://user:password@www.uri-to-fetch.com") }
+ let(:uri) { Gem::URI("http://user:password@www.uri-to-fetch.com") }
it "should raise a Bundler::Fetcher::BadAuthenticationError that doesn't contain the password" do
expect { subject.fetch(uri, options, counter) }.
@@ -98,29 +98,39 @@ RSpec.describe Bundler::Fetcher::Downloader do
end
end
- context "when the request response is a Net::HTTPNotFound" do
- let(:http_response) { Net::HTTPNotFound.new("1.1", 404, "Not Found") }
+ context "when the request response is a Gem::Net::HTTPForbidden" do
+ let(:http_response) { Gem::Net::HTTPForbidden.new("1.1", 403, "Forbidden") }
+ let(:uri) { Gem::URI("http://user:password@www.uri-to-fetch.com") }
- it "should raise a Bundler::Fetcher::FallbackError with Net::HTTPNotFound" do
+ it "should raise a Bundler::Fetcher::AuthenticationForbiddenError with the uri host" do
+ expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::Fetcher::AuthenticationForbiddenError,
+ /Access token could not be authenticated for www.uri-to-fetch.com/)
+ end
+ end
+
+ context "when the request response is a Gem::Net::HTTPNotFound" do
+ let(:http_response) { Gem::Net::HTTPNotFound.new("1.1", 404, "Not Found") }
+
+ it "should raise a Bundler::Fetcher::FallbackError with Gem::Net::HTTPNotFound" do
expect { subject.fetch(uri, options, counter) }.
- to raise_error(Bundler::Fetcher::FallbackError, "Net::HTTPNotFound: http://www.uri-to-fetch.com/api/v2/endpoint")
+ to raise_error(Bundler::Fetcher::FallbackError, "Gem::Net::HTTPNotFound: http://www.uri-to-fetch.com/api/v2/endpoint")
end
context "when the there are credentials provided in the request" do
- let(:uri) { Bundler::URI("http://username:password@www.uri-to-fetch.com/api/v2/endpoint") }
+ let(:uri) { Gem::URI("http://username:password@www.uri-to-fetch.com/api/v2/endpoint") }
it "should raise a Bundler::Fetcher::FallbackError that doesn't contain the password" do
expect { subject.fetch(uri, options, counter) }.
- to raise_error(Bundler::Fetcher::FallbackError, "Net::HTTPNotFound: http://username@www.uri-to-fetch.com/api/v2/endpoint")
+ to raise_error(Bundler::Fetcher::FallbackError, "Gem::Net::HTTPNotFound: http://username@www.uri-to-fetch.com/api/v2/endpoint")
end
end
end
context "when the request response is some other type" do
- let(:http_response) { Net::HTTPBadGateway.new("1.1", 500, "Fatal Error") }
+ let(:http_response) { Gem::Net::HTTPBadGateway.new("1.1", 500, "Fatal Error") }
it "should raise a Bundler::HTTPError with the response class and body" do
- expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::HTTPError, "Net::HTTPBadGateway: Body with info")
+ expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::HTTPError, "Gem::Net::HTTPBadGateway: Body with info")
end
end
end
@@ -130,7 +140,7 @@ RSpec.describe Bundler::Fetcher::Downloader do
let(:response) { double(:response) }
before do
- allow(Net::HTTP::Get).to receive(:new).with("/api/v2/endpoint", options).and_return(net_http_get)
+ allow(Gem::Net::HTTP::Get).to receive(:new).with("/api/v2/endpoint", options).and_return(net_http_get)
allow(connection).to receive(:request).with(uri, net_http_get).and_return(response)
end
@@ -142,7 +152,7 @@ RSpec.describe Bundler::Fetcher::Downloader do
context "when there is a user provided in the request" do
context "and there is also a password provided" do
context "that contains cgi escaped characters" do
- let(:uri) { Bundler::URI("http://username:password%24@www.uri-to-fetch.com/api/v2/endpoint") }
+ let(:uri) { Gem::URI("http://username:password%24@www.uri-to-fetch.com/api/v2/endpoint") }
it "should request basic authentication with the username and password" do
expect(net_http_get).to receive(:basic_auth).with("username", "password$")
@@ -151,7 +161,7 @@ RSpec.describe Bundler::Fetcher::Downloader do
end
context "that is all unescaped characters" do
- let(:uri) { Bundler::URI("http://username:password@www.uri-to-fetch.com/api/v2/endpoint") }
+ let(:uri) { Gem::URI("http://username:password@www.uri-to-fetch.com/api/v2/endpoint") }
it "should request basic authentication with the username and proper cgi compliant password" do
expect(net_http_get).to receive(:basic_auth).with("username", "password")
subject.request(uri, options)
@@ -160,7 +170,7 @@ RSpec.describe Bundler::Fetcher::Downloader do
end
context "and there is no password provided" do
- let(:uri) { Bundler::URI("http://username@www.uri-to-fetch.com/api/v2/endpoint") }
+ let(:uri) { Gem::URI("http://username@www.uri-to-fetch.com/api/v2/endpoint") }
it "should request basic authentication with just the user" do
expect(net_http_get).to receive(:basic_auth).with("username", nil)
@@ -169,7 +179,7 @@ RSpec.describe Bundler::Fetcher::Downloader do
end
context "that contains cgi escaped characters" do
- let(:uri) { Bundler::URI("http://username%24@www.uri-to-fetch.com/api/v2/endpoint") }
+ let(:uri) { Gem::URI("http://username%24@www.uri-to-fetch.com/api/v2/endpoint") }
it "should request basic authentication with the proper cgi compliant password user" do
expect(net_http_get).to receive(:basic_auth).with("username$", nil)
@@ -220,7 +230,7 @@ RSpec.describe Bundler::Fetcher::Downloader do
end
context "when the there are credentials provided in the request" do
- let(:uri) { Bundler::URI("http://username:password@www.uri-to-fetch.com/api/v2/endpoint") }
+ let(:uri) { Gem::URI("http://username:password@www.uri-to-fetch.com/api/v2/endpoint") }
before do
allow(net_http_get).to receive(:basic_auth).with("username", "password")
end
diff --git a/spec/bundler/bundler/fetcher/index_spec.rb b/spec/bundler/bundler/fetcher/index_spec.rb
index f0db07583c..dff9ccc3cc 100644
--- a/spec/bundler/bundler/fetcher/index_spec.rb
+++ b/spec/bundler/bundler/fetcher/index_spec.rb
@@ -8,8 +8,9 @@ RSpec.describe Bundler::Fetcher::Index do
let(:display_uri) { "http://sample_uri.com" }
let(:rubygems) { double(:rubygems) }
let(:gem_names) { %w[foo bar] }
+ let(:gem_remote_fetcher) { nil }
- subject { described_class.new(downloader, remote, display_uri) }
+ subject { described_class.new(downloader, remote, display_uri, gem_remote_fetcher) }
before { allow(Bundler).to receive(:rubygems).and_return(rubygems) }
@@ -19,7 +20,7 @@ RSpec.describe Bundler::Fetcher::Index do
end
context "error handling" do
- let(:remote_uri) { Bundler::URI("http://remote-uri.org") }
+ let(:remote_uri) { Gem::URI("http://remote-uri.org") }
before do
allow(rubygems).to receive(:fetch_all_remote_specs) { raise Gem::RemoteFetcher::FetchError.new(error_message, display_uri) }
allow(subject).to receive(:remote_uri).and_return(remote_uri)
@@ -63,33 +64,16 @@ RSpec.describe Bundler::Fetcher::Index do
context "when a 403 response occurs" do
let(:error_message) { "403" }
- before do
- allow(remote_uri).to receive(:userinfo).and_return(userinfo)
- end
-
- context "and there was userinfo" do
- let(:userinfo) { double(:userinfo) }
-
- it "should raise a Bundler::Fetcher::BadAuthenticationError" do
- expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::BadAuthenticationError,
- %r{Bad username or password for http://remote-uri.org})
- end
- end
-
- context "and there was no userinfo" do
- let(:userinfo) { nil }
-
- it "should raise a Bundler::Fetcher::AuthenticationRequiredError" do
- expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::AuthenticationRequiredError,
- %r{Authentication is required for http://remote-uri.org})
- end
+ it "should raise a Bundler::Fetcher::AuthenticationForbiddenError" do
+ expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::AuthenticationForbiddenError,
+ %r{Access token could not be authenticated for http://remote-uri.org})
end
end
context "any other message is returned" do
let(:error_message) { "You get an error, you get an error!" }
- before { allow(Bundler).to receive(:ui).and_return(double(:trace => nil)) }
+ before { allow(Bundler).to receive(:ui).and_return(double(trace: nil)) }
it "should raise a Bundler::HTTPError" do
expect { subject.specs(gem_names) }.to raise_error(Bundler::HTTPError, "Could not fetch specs from http://sample_uri.com due to underlying error <You get an error, you get an error! (http://sample_uri.com)>")
diff --git a/spec/bundler/bundler/fetcher_spec.rb b/spec/bundler/bundler/fetcher_spec.rb
index 27a63c476d..e20f7e7c48 100644
--- a/spec/bundler/bundler/fetcher_spec.rb
+++ b/spec/bundler/bundler/fetcher_spec.rb
@@ -3,8 +3,8 @@
require "bundler/fetcher"
RSpec.describe Bundler::Fetcher do
- let(:uri) { Bundler::URI("https://example.com") }
- let(:remote) { double("remote", :uri => uri, :original_uri => nil) }
+ let(:uri) { Gem::URI("https://example.com") }
+ let(:remote) { double("remote", uri: uri, original_uri: nil) }
subject(:fetcher) { Bundler::Fetcher.new(remote) }
@@ -45,9 +45,9 @@ RSpec.describe Bundler::Fetcher do
end
context "when a rubygems source mirror is set" do
- let(:orig_uri) { Bundler::URI("http://zombo.com") }
+ let(:orig_uri) { Gem::URI("http://zombo.com") }
let(:remote_with_mirror) do
- double("remote", :uri => uri, :original_uri => orig_uri, :anonymized_uri => uri)
+ double("remote", uri: uri, original_uri: orig_uri, anonymized_uri: uri)
end
let(:fetcher) { Bundler::Fetcher.new(remote_with_mirror) }
@@ -61,7 +61,7 @@ RSpec.describe Bundler::Fetcher do
context "when there is no rubygems source mirror set" do
let(:remote_no_mirror) do
- double("remote", :uri => uri, :original_uri => nil, :anonymized_uri => uri)
+ double("remote", uri: uri, original_uri: nil, anonymized_uri: uri)
end
let(:fetcher) { Bundler::Fetcher.new(remote_no_mirror) }
@@ -114,9 +114,9 @@ RSpec.describe Bundler::Fetcher do
context "when gem ssl configuration is set" do
before do
allow(Gem.configuration).to receive_messages(
- :http_proxy => nil,
- :ssl_client_cert => "cert",
- :ssl_ca_cert => "ca"
+ http_proxy: nil,
+ ssl_client_cert: "cert",
+ ssl_ca_cert: "ca"
)
expect(File).to receive(:read).and_return("")
expect(OpenSSL::X509::Certificate).to receive(:new).and_return("cert")
@@ -143,18 +143,20 @@ RSpec.describe Bundler::Fetcher do
describe "include CI information" do
it "from one CI" do
- with_env_vars("JENKINS_URL" => "foo") do
+ with_env_vars("CI" => nil, "JENKINS_URL" => "foo") do
ci_part = fetcher.user_agent.split(" ").find {|x| x.start_with?("ci/") }
- expect(ci_part).to match("jenkins")
+ cis = ci_part.split("/").last.split(",")
+ expect(cis).to include("jenkins")
+ expect(cis).not_to include("ci")
end
end
it "from many CI" do
- with_env_vars("TRAVIS" => "foo", "GITLAB_CI" => "gitlab", "CI_NAME" => "my_ci") do
+ with_env_vars("CI" => "true", "SEMAPHORE" => nil, "TRAVIS" => "foo", "GITLAB_CI" => "gitlab", "CI_NAME" => "MY_ci") do
ci_part = fetcher.user_agent.split(" ").find {|x| x.start_with?("ci/") }
- expect(ci_part).to match("travis")
- expect(ci_part).to match("gitlab")
- expect(ci_part).to match("my_ci")
+ cis = ci_part.split("/").last.split(",")
+ expect(cis).to include("ci", "gitlab", "my_ci", "travis")
+ expect(cis).not_to include("semaphore")
end
end
end
@@ -165,7 +167,7 @@ RSpec.describe Bundler::Fetcher do
let(:version) { "1.3.17" }
let(:platform) { "platform" }
let(:downloader) { double("downloader") }
- let(:body) { double(Net::HTTP::Get, :body => downloaded_data) }
+ let(:body) { double(Gem::Net::HTTP::Get, body: downloaded_data) }
context "when attempting to load a Gem::Specification" do
let(:spec) { Gem::Specification.new(name, version) }
@@ -189,4 +191,70 @@ RSpec.describe Bundler::Fetcher do
end
end
end
+
+ describe "#specs_with_retry" do
+ let(:downloader) { double(:downloader) }
+ let(:remote) { double(:remote, cache_slug: "slug", uri: uri, original_uri: nil, anonymized_uri: uri) }
+ let(:compact_index) { double(Bundler::Fetcher::CompactIndex, available?: true, api_fetcher?: true) }
+ let(:dependency) { double(Bundler::Fetcher::Dependency, available?: true, api_fetcher?: true) }
+ let(:index) { double(Bundler::Fetcher::Index, available?: true, api_fetcher?: false) }
+
+ before do
+ allow(Bundler::Fetcher::CompactIndex).to receive(:new).and_return(compact_index)
+ allow(Bundler::Fetcher::Dependency).to receive(:new).and_return(dependency)
+ allow(Bundler::Fetcher::Index).to receive(:new).and_return(index)
+ end
+
+ it "picks the first fetcher that works" do
+ expect(compact_index).to receive(:specs).with("name").and_return([["name", "1.2.3", "ruby"]])
+ expect(dependency).not_to receive(:specs)
+ expect(index).not_to receive(:specs)
+ fetcher.specs_with_retry("name", double(Bundler::Source::Rubygems))
+ end
+
+ context "when APIs are not available" do
+ before do
+ allow(compact_index).to receive(:available?).and_return(false)
+ allow(dependency).to receive(:available?).and_return(false)
+ end
+
+ it "uses the index" do
+ expect(compact_index).not_to receive(:specs)
+ expect(dependency).not_to receive(:specs)
+ expect(index).to receive(:specs).with("name").and_return([["name", "1.2.3", "ruby"]])
+
+ fetcher.specs_with_retry("name", double(Bundler::Source::Rubygems))
+ end
+ end
+ end
+
+ describe "#api_fetcher?" do
+ let(:downloader) { double(:downloader) }
+ let(:remote) { double(:remote, cache_slug: "slug", uri: uri, original_uri: nil, anonymized_uri: uri) }
+ let(:compact_index) { double(Bundler::Fetcher::CompactIndex, available?: false, api_fetcher?: true) }
+ let(:dependency) { double(Bundler::Fetcher::Dependency, available?: false, api_fetcher?: true) }
+ let(:index) { double(Bundler::Fetcher::Index, available?: true, api_fetcher?: false) }
+
+ before do
+ allow(Bundler::Fetcher::CompactIndex).to receive(:new).and_return(compact_index)
+ allow(Bundler::Fetcher::Dependency).to receive(:new).and_return(dependency)
+ allow(Bundler::Fetcher::Index).to receive(:new).and_return(index)
+ end
+
+ context "when an api fetcher is available" do
+ before do
+ allow(compact_index).to receive(:available?).and_return(true)
+ end
+
+ it "is truthy" do
+ expect(fetcher).to be_api_fetcher
+ end
+ end
+
+ context "when only the index fetcher is available" do
+ it "is falsey" do
+ expect(fetcher).not_to be_api_fetcher
+ end
+ end
+ end
end
diff --git a/spec/bundler/bundler/friendly_errors_spec.rb b/spec/bundler/bundler/friendly_errors_spec.rb
index 37afe488f3..cda2ef31de 100644
--- a/spec/bundler/bundler/friendly_errors_spec.rb
+++ b/spec/bundler/bundler/friendly_errors_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe Bundler, "friendly errors" do
gem "rack"
G
- bundle :install, :env => { "DEBUG" => "true" }
+ bundle :install, env: { "DEBUG" => "true" }
expect(err).to include("Failed to load #{home(".gemrc")}")
end
@@ -101,7 +101,7 @@ RSpec.describe Bundler, "friendly errors" do
context "BundlerError" do
it "Bundler.ui receive error" do
error = Bundler::BundlerError.new
- expect(Bundler.ui).to receive(:error).with(error.message, :wrap => true)
+ expect(Bundler.ui).to receive(:error).with(error.message, wrap: true)
Bundler::FriendlyErrors.log_error(error)
end
end
@@ -121,7 +121,7 @@ RSpec.describe Bundler, "friendly errors" do
context "Gem::InvalidSpecificationException" do
it "Bundler.ui receive error" do
error = Gem::InvalidSpecificationException.new
- expect(Bundler.ui).to receive(:error).with(error.message, :wrap => true)
+ expect(Bundler.ui).to receive(:error).with(error.message, wrap: true)
Bundler::FriendlyErrors.log_error(error)
end
end
diff --git a/spec/bundler/bundler/gem_helper_spec.rb b/spec/bundler/bundler/gem_helper_spec.rb
index 7d955007ab..940e5df9de 100644
--- a/spec/bundler/bundler/gem_helper_spec.rb
+++ b/spec/bundler/bundler/gem_helper_spec.rb
@@ -253,11 +253,11 @@ RSpec.describe Bundler::GemHelper do
end
before do
- sys_exec("git init", :dir => app_path)
- sys_exec("git config user.email \"you@example.com\"", :dir => app_path)
- sys_exec("git config user.name \"name\"", :dir => app_path)
- sys_exec("git config commit.gpgsign false", :dir => app_path)
- sys_exec("git config push.default simple", :dir => app_path)
+ sys_exec("git init", dir: app_path)
+ sys_exec("git config user.email \"you@example.com\"", dir: app_path)
+ sys_exec("git config user.name \"name\"", dir: app_path)
+ sys_exec("git config commit.gpgsign false", dir: app_path)
+ sys_exec("git config push.default simple", dir: app_path)
# silence messages
allow(Bundler.ui).to receive(:confirm)
@@ -271,23 +271,23 @@ RSpec.describe Bundler::GemHelper do
end
it "when there are uncommitted files" do
- sys_exec("git add .", :dir => app_path)
+ sys_exec("git add .", dir: app_path)
expect { Rake.application["release"].invoke }.
to raise_error("There are files that need to be committed first.")
end
it "when there is no git remote" do
- sys_exec("git commit -a -m \"initial commit\"", :dir => app_path)
+ sys_exec("git commit -a -m \"initial commit\"", dir: app_path)
expect { Rake.application["release"].invoke }.to raise_error(RuntimeError)
end
end
context "succeeds" do
- let(:repo) { build_git("foo", :bare => true) }
+ let(:repo) { build_git("foo", bare: true) }
before do
- sys_exec("git remote add origin #{file_uri_for(repo.path)}", :dir => app_path)
- sys_exec('git commit -a -m "initial commit"', :dir => app_path)
+ sys_exec("git remote add origin #{file_uri_for(repo.path)}", dir: app_path)
+ sys_exec('git commit -a -m "initial commit"', dir: app_path)
end
context "on releasing" do
@@ -296,7 +296,7 @@ RSpec.describe Bundler::GemHelper do
mock_confirm_message "Tagged v#{app_version}."
mock_confirm_message "Pushed git commits and release tag."
- sys_exec("git push -u origin main", :dir => app_path)
+ sys_exec("git push -u origin main", dir: app_path)
end
it "calls rubygem_push with proper arguments" do
@@ -314,8 +314,8 @@ RSpec.describe Bundler::GemHelper do
it "also works when releasing from an ambiguous reference" do
# Create a branch with the same name as the tag
- sys_exec("git checkout -b v#{app_version}", :dir => app_path)
- sys_exec("git push -u origin v#{app_version}", :dir => app_path)
+ sys_exec("git checkout -b v#{app_version}", dir: app_path)
+ sys_exec("git push -u origin v#{app_version}", dir: app_path)
expect(subject).to receive(:rubygem_push).with(app_gem_path.to_s)
@@ -323,7 +323,7 @@ RSpec.describe Bundler::GemHelper do
end
it "also works with releasing from a branch not yet pushed" do
- sys_exec("git checkout -b module_function", :dir => app_path)
+ sys_exec("git checkout -b module_function", dir: app_path)
expect(subject).to receive(:rubygem_push).with(app_gem_path.to_s)
@@ -337,7 +337,7 @@ RSpec.describe Bundler::GemHelper do
mock_build_message app_name, app_version
mock_confirm_message "Pushed git commits and release tag."
- sys_exec("git push -u origin main", :dir => app_path)
+ sys_exec("git push -u origin main", dir: app_path)
expect(subject).to receive(:rubygem_push).with(app_gem_path.to_s)
end
@@ -353,7 +353,7 @@ RSpec.describe Bundler::GemHelper do
mock_confirm_message "Tag v#{app_version} has already been created."
expect(subject).to receive(:rubygem_push).with(app_gem_path.to_s)
- sys_exec("git tag -a -m \"Version #{app_version}\" v#{app_version}", :dir => app_path)
+ sys_exec("git tag -a -m \"Version #{app_version}\" v#{app_version}", dir: app_path)
Rake.application["release"].invoke
end
@@ -374,10 +374,10 @@ RSpec.describe Bundler::GemHelper do
end
before do
- sys_exec("git init", :dir => app_path)
- sys_exec("git config user.email \"you@example.com\"", :dir => app_path)
- sys_exec("git config user.name \"name\"", :dir => app_path)
- sys_exec("git config push.gpgsign simple", :dir => app_path)
+ sys_exec("git init", dir: app_path)
+ sys_exec("git config user.email \"you@example.com\"", dir: app_path)
+ sys_exec("git config user.name \"name\"", dir: app_path)
+ sys_exec("git config push.gpgsign simple", dir: app_path)
# silence messages
allow(Bundler.ui).to receive(:confirm)
diff --git a/spec/bundler/bundler/gem_version_promoter_spec.rb b/spec/bundler/bundler/gem_version_promoter_spec.rb
index 8058412f32..917daba95d 100644
--- a/spec/bundler/bundler/gem_version_promoter_spec.rb
+++ b/spec/bundler/bundler/gem_version_promoter_spec.rb
@@ -21,7 +21,7 @@ RSpec.describe Bundler::GemVersionPromoter do
end
def build_package(name, version, locked = [])
- Bundler::Resolver::Package.new(name, [], :locked_specs => Bundler::SpecSet.new(build_spec(name, version)), :unlock => locked)
+ Bundler::Resolver::Package.new(name, [], locked_specs: Bundler::SpecSet.new(build_spec(name, version)), unlock: locked)
end
def sorted_versions(candidates:, current:, name: "foo", locked: [])
@@ -32,14 +32,14 @@ RSpec.describe Bundler::GemVersionPromoter do
end
it "numerically sorts versions" do
- versions = sorted_versions(:candidates => %w[1.7.7 1.7.8 1.7.9 1.7.15 1.8.0], :current => "1.7.8")
- expect(versions).to eq %w[1.7.7 1.7.8 1.7.9 1.7.15 1.8.0]
+ versions = sorted_versions(candidates: %w[1.7.7 1.7.8 1.7.9 1.7.15 1.8.0], current: "1.7.8")
+ expect(versions).to eq %w[1.8.0 1.7.15 1.7.9 1.7.8 1.7.7]
end
context "with no options" do
it "defaults to level=:major, strict=false, pre=false" do
- versions = sorted_versions(:candidates => %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], :current => "0.3.0")
- expect(versions).to eq %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0]
+ versions = sorted_versions(candidates: %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], current: "0.3.0")
+ expect(versions).to eq %w[2.1.0 2.0.1 1.0.0 0.9.0 0.3.1 0.3.0 0.2.0]
end
end
@@ -50,26 +50,26 @@ RSpec.describe Bundler::GemVersionPromoter do
before { gvp.level = :major }
it "keeps downgrades" do
- versions = sorted_versions(:candidates => %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], :current => "0.3.0")
- expect(versions).to eq %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0]
+ versions = sorted_versions(candidates: %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], current: "0.3.0")
+ expect(versions).to eq %w[2.1.0 2.0.1 1.0.0 0.9.0 0.3.1 0.3.0 0.2.0]
end
end
context "when level is minor" do
before { gvp.level = :minor }
- it "removes downgrades and major upgrades" do
- versions = sorted_versions(:candidates => %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], :current => "0.3.0")
- expect(versions).to eq %w[0.3.0 0.3.1 0.9.0]
+ it "sorts highest minor within same major in first position" do
+ versions = sorted_versions(candidates: %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], current: "0.3.0")
+ expect(versions).to eq %w[0.9.0 0.3.1 0.3.0 1.0.0 2.1.0 2.0.1 0.2.0]
end
end
context "when level is patch" do
before { gvp.level = :patch }
- it "removes downgrades and major and minor upgrades" do
- versions = sorted_versions(:candidates => %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], :current => "0.3.0")
- expect(versions).to eq %w[0.3.0 0.3.1]
+ it "sorts highest patch within same minor in first position" do
+ versions = sorted_versions(candidates: %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], current: "0.3.0")
+ expect(versions).to eq %w[0.3.1 0.3.0 0.9.0 1.0.0 2.0.1 2.1.0 0.2.0]
end
end
end
@@ -81,26 +81,26 @@ RSpec.describe Bundler::GemVersionPromoter do
before { gvp.level = :major }
it "orders by version" do
- versions = sorted_versions(:candidates => %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], :current => "0.3.0")
- expect(versions).to eq %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0]
+ versions = sorted_versions(candidates: %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], current: "0.3.0")
+ expect(versions).to eq %w[2.1.0 2.0.1 1.0.0 0.9.0 0.3.1 0.3.0 0.2.0]
end
end
context "when level is minor" do
before { gvp.level = :minor }
- it "favors downgrades, then upgrades by major descending, minor ascending, patch ascending" do
- versions = sorted_versions(:candidates => %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], :current => "0.3.0")
- expect(versions).to eq %w[0.2.0 2.0.1 2.1.0 1.0.0 0.3.0 0.3.1 0.9.0]
+ it "favors minor upgrades, then patch upgrades, then major upgrades, then downgrades" do
+ versions = sorted_versions(candidates: %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], current: "0.3.0")
+ expect(versions).to eq %w[0.9.0 0.3.1 0.3.0 1.0.0 2.1.0 2.0.1 0.2.0]
end
end
context "when level is patch" do
before { gvp.level = :patch }
- it "favors downgrades, then upgrades by major descending, minor descending, patch ascending" do
- versions = sorted_versions(:candidates => %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], :current => "0.3.0")
- expect(versions).to eq %w[0.2.0 2.1.0 2.0.1 1.0.0 0.9.0 0.3.0 0.3.1]
+ it "favors patch upgrades, then minor upgrades, then major upgrades, then downgrades" do
+ versions = sorted_versions(candidates: %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], current: "0.3.0")
+ expect(versions).to eq %w[0.3.1 0.3.0 0.9.0 1.0.0 2.0.1 2.1.0 0.2.0]
end
end
end
@@ -109,8 +109,8 @@ RSpec.describe Bundler::GemVersionPromoter do
before { gvp.pre = true }
it "sorts regardless of prerelease status" do
- versions = sorted_versions(:candidates => %w[1.7.7.pre 1.8.0 1.8.1.pre 1.8.1 2.0.0.pre 2.0.0], :current => "1.8.0")
- expect(versions).to eq %w[1.7.7.pre 1.8.0 1.8.1.pre 1.8.1 2.0.0.pre 2.0.0]
+ versions = sorted_versions(candidates: %w[1.7.7.pre 1.8.0 1.8.1.pre 1.8.1 2.0.0.pre 2.0.0], current: "1.8.0")
+ expect(versions).to eq %w[2.0.0 2.0.0.pre 1.8.1 1.8.1.pre 1.8.0 1.7.7.pre]
end
end
@@ -118,17 +118,17 @@ RSpec.describe Bundler::GemVersionPromoter do
before { gvp.pre = false }
it "deprioritizes prerelease gems" do
- versions = sorted_versions(:candidates => %w[1.7.7.pre 1.8.0 1.8.1.pre 1.8.1 2.0.0.pre 2.0.0], :current => "1.8.0")
- expect(versions).to eq %w[1.7.7.pre 1.8.1.pre 2.0.0.pre 1.8.0 1.8.1 2.0.0]
+ versions = sorted_versions(candidates: %w[1.7.7.pre 1.8.0 1.8.1.pre 1.8.1 2.0.0.pre 2.0.0], current: "1.8.0")
+ expect(versions).to eq %w[2.0.0 1.8.1 1.8.0 2.0.0.pre 1.8.1.pre 1.7.7.pre]
end
end
context "when locking and not major" do
before { gvp.level = :minor }
- it "keeps the current version last" do
- versions = sorted_versions(:candidates => %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.1.0 2.0.1], :current => "0.3.0", :locked => ["bar"])
- expect(versions.last).to eq("0.3.0")
+ it "keeps the current version first" do
+ versions = sorted_versions(candidates: %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.1.0 2.0.1], current: "0.3.0", locked: ["bar"])
+ expect(versions.first).to eq("0.3.0")
end
end
end
diff --git a/spec/bundler/bundler/installer/gem_installer_spec.rb b/spec/bundler/bundler/installer/gem_installer_spec.rb
index e86bdf009a..4b6a07f344 100644
--- a/spec/bundler/bundler/installer/gem_installer_spec.rb
+++ b/spec/bundler/bundler/installer/gem_installer_spec.rb
@@ -3,10 +3,10 @@
require "bundler/installer/gem_installer"
RSpec.describe Bundler::GemInstaller do
- let(:definition) { instance_double("Definition", :locked_gems => nil) }
- let(:installer) { instance_double("Installer", :definition => definition) }
+ let(:definition) { instance_double("Definition", locked_gems: nil) }
+ let(:installer) { instance_double("Installer", definition: definition) }
let(:spec_source) { instance_double("SpecSource") }
- let(:spec) { instance_double("Specification", :name => "dummy", :version => "0.0.1", :loaded_from => "dummy", :source => spec_source) }
+ let(:spec) { instance_double("Specification", name: "dummy", version: "0.0.1", loaded_from: "dummy", source: spec_source) }
subject { described_class.new(spec, installer) }
@@ -14,7 +14,7 @@ RSpec.describe Bundler::GemInstaller do
it "invokes install method with empty build_args" do
allow(spec_source).to receive(:install).with(
spec,
- { :force => false, :ensure_builtin_gems_cached => false, :build_args => [], :previous_spec => nil }
+ { force: false, ensure_builtin_gems_cached: false, build_args: [], previous_spec: nil }
)
subject.install_from_spec
end
@@ -28,7 +28,7 @@ RSpec.describe Bundler::GemInstaller do
allow(Bundler.settings).to receive(:[]).with("build.dummy").and_return("--with-dummy-config=dummy")
expect(spec_source).to receive(:install).with(
spec,
- { :force => false, :ensure_builtin_gems_cached => false, :build_args => ["--with-dummy-config=dummy"], :previous_spec => nil }
+ { force: false, ensure_builtin_gems_cached: false, build_args: ["--with-dummy-config=dummy"], previous_spec: nil }
)
subject.install_from_spec
end
@@ -42,7 +42,7 @@ RSpec.describe Bundler::GemInstaller do
allow(Bundler.settings).to receive(:[]).with("build.dummy").and_return("--with-dummy-config=dummy --with-another-dummy-config")
expect(spec_source).to receive(:install).with(
spec,
- { :force => false, :ensure_builtin_gems_cached => false, :build_args => ["--with-dummy-config=dummy", "--with-another-dummy-config"], :previous_spec => nil }
+ { force: false, ensure_builtin_gems_cached: false, build_args: ["--with-dummy-config=dummy", "--with-another-dummy-config"], previous_spec: nil }
)
subject.install_from_spec
end
diff --git a/spec/bundler/bundler/installer/parallel_installer_spec.rb b/spec/bundler/bundler/installer/parallel_installer_spec.rb
deleted file mode 100644
index c8403a2e38..0000000000
--- a/spec/bundler/bundler/installer/parallel_installer_spec.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-
-require "bundler/installer/parallel_installer"
-
-RSpec.describe Bundler::ParallelInstaller do
- let(:installer) { instance_double("Installer") }
- let(:all_specs) { [] }
- let(:size) { 1 }
- let(:standalone) { false }
- let(:force) { false }
-
- subject { described_class.new(installer, all_specs, size, standalone, force) }
-
- context "when the spec set is not a valid resolution" do
- let(:all_specs) do
- [
- build_spec("cucumber", "4.1.0") {|s| s.runtime "diff-lcs", "< 1.4" },
- build_spec("diff-lcs", "1.4.4"),
- ].flatten
- end
-
- it "prints a warning" do
- expect(Bundler.ui).to receive(:warn).with(<<-W.strip)
-Your lockfile doesn't include a valid resolution.
-You can fix this by regenerating your lockfile or manually editing the bad locked gems to a version that satisfies all dependencies.
-The unmet dependencies are:
-* diff-lcs (< 1.4), dependency of cucumber-4.1.0, unsatisfied by diff-lcs-1.4.4
- W
- subject.check_for_unmet_dependencies
- end
- end
-
- context "when the spec set is a valid resolution" do
- let(:all_specs) do
- [
- build_spec("cucumber", "4.1.0") {|s| s.runtime "diff-lcs", "< 1.4" },
- build_spec("diff-lcs", "1.3"),
- ].flatten
- end
-
- it "doesn't print a warning" do
- expect(Bundler.ui).not_to receive(:warn)
- subject.check_for_unmet_dependencies
- end
- end
-end
diff --git a/spec/bundler/bundler/installer/spec_installation_spec.rb b/spec/bundler/bundler/installer/spec_installation_spec.rb
index e63ef26cb3..cbe2589b99 100644
--- a/spec/bundler/bundler/installer/spec_installation_spec.rb
+++ b/spec/bundler/bundler/installer/spec_installation_spec.rb
@@ -42,24 +42,26 @@ RSpec.describe Bundler::ParallelInstaller::SpecInstallation do
context "when all dependencies are installed" do
it "returns true" do
dependencies = []
- dependencies << instance_double("SpecInstallation", :spec => "alpha", :name => "alpha", :installed? => true, :all_dependencies => [], :type => :production)
- dependencies << instance_double("SpecInstallation", :spec => "beta", :name => "beta", :installed? => true, :all_dependencies => [], :type => :production)
- all_specs = dependencies + [instance_double("SpecInstallation", :spec => "gamma", :name => "gamma", :installed? => false, :all_dependencies => [], :type => :production)]
+ dependencies << instance_double("SpecInstallation", spec: "alpha", name: "alpha", installed?: true, all_dependencies: [], type: :production)
+ dependencies << instance_double("SpecInstallation", spec: "beta", name: "beta", installed?: true, all_dependencies: [], type: :production)
+ all_specs = dependencies + [instance_double("SpecInstallation", spec: "gamma", name: "gamma", installed?: false, all_dependencies: [], type: :production)]
spec = described_class.new(dep)
allow(spec).to receive(:all_dependencies).and_return(dependencies)
- expect(spec.dependencies_installed?(all_specs)).to be_truthy
+ installed_specs = all_specs.select(&:installed?).map {|s| [s.name, true] }.to_h
+ expect(spec.dependencies_installed?(installed_specs)).to be_truthy
end
end
context "when all dependencies are not installed" do
it "returns false" do
dependencies = []
- dependencies << instance_double("SpecInstallation", :spec => "alpha", :name => "alpha", :installed? => false, :all_dependencies => [], :type => :production)
- dependencies << instance_double("SpecInstallation", :spec => "beta", :name => "beta", :installed? => true, :all_dependencies => [], :type => :production)
- all_specs = dependencies + [instance_double("SpecInstallation", :spec => "gamma", :name => "gamma", :installed? => false, :all_dependencies => [], :type => :production)]
+ dependencies << instance_double("SpecInstallation", spec: "alpha", name: "alpha", installed?: false, all_dependencies: [], type: :production)
+ dependencies << instance_double("SpecInstallation", spec: "beta", name: "beta", installed?: true, all_dependencies: [], type: :production)
+ all_specs = dependencies + [instance_double("SpecInstallation", spec: "gamma", name: "gamma", installed?: false, all_dependencies: [], type: :production)]
spec = described_class.new(dep)
allow(spec).to receive(:all_dependencies).and_return(dependencies)
- expect(spec.dependencies_installed?(all_specs)).to be_falsey
+ installed_specs = all_specs.select(&:installed?).map {|s| [s.name, true] }.to_h
+ expect(spec.dependencies_installed?(installed_specs)).to be_falsey
end
end
end
diff --git a/spec/bundler/bundler/lockfile_parser_spec.rb b/spec/bundler/bundler/lockfile_parser_spec.rb
index 3a6d61336f..88932bf009 100644
--- a/spec/bundler/bundler/lockfile_parser_spec.rb
+++ b/spec/bundler/bundler/lockfile_parser_spec.rb
@@ -3,7 +3,7 @@
require "bundler/lockfile_parser"
RSpec.describe Bundler::LockfileParser do
- let(:lockfile_contents) { strip_whitespace(<<-L) }
+ let(:lockfile_contents) { <<~L }
GIT
remote: https://github.com/alloy/peiji-san.git
revision: eca485d8dc95f12aaec1a434b49d295c7e91844b
@@ -22,6 +22,9 @@ RSpec.describe Bundler::LockfileParser do
peiji-san!
rake
+ CHECKSUMS
+ rake (10.3.2) sha256=814828c34f1315d7e7b7e8295184577cc4e969bad6156ac069d02d63f58d82e8
+
RUBY VERSION
ruby 2.1.3p242
@@ -33,13 +36,13 @@ RSpec.describe Bundler::LockfileParser do
it "returns the attributes" do
attributes = described_class.sections_in_lockfile(lockfile_contents)
expect(attributes).to contain_exactly(
- "BUNDLED WITH", "DEPENDENCIES", "GEM", "GIT", "PLATFORMS", "RUBY VERSION"
+ "BUNDLED WITH", "CHECKSUMS", "DEPENDENCIES", "GEM", "GIT", "PLATFORMS", "RUBY VERSION"
)
end
end
describe ".unknown_sections_in_lockfile" do
- let(:lockfile_contents) { strip_whitespace(<<-L) }
+ let(:lockfile_contents) { <<~L }
UNKNOWN ATTR
UNKNOWN ATTR 2
@@ -60,7 +63,7 @@ RSpec.describe Bundler::LockfileParser do
it "returns the same as > 1.0" do
expect(subject).to contain_exactly(
- described_class::BUNDLED, described_class::RUBY, described_class::PLUGIN
+ described_class::BUNDLED, described_class::CHECKSUMS, described_class::RUBY, described_class::PLUGIN
)
end
end
@@ -70,7 +73,7 @@ RSpec.describe Bundler::LockfileParser do
it "returns the same as for the release version" do
expect(subject).to contain_exactly(
- described_class::RUBY, described_class::PLUGIN
+ described_class::CHECKSUMS, described_class::RUBY, described_class::PLUGIN
)
end
end
@@ -115,6 +118,14 @@ RSpec.describe Bundler::LockfileParser do
let(:platforms) { [rb] }
let(:bundler_version) { Gem::Version.new("1.12.0.rc.2") }
let(:ruby_version) { "ruby 2.1.3p242" }
+ let(:lockfile_path) { Bundler.default_lockfile.relative_path_from(Dir.pwd) }
+ let(:rake_sha256_checksum) do
+ Bundler::Checksum.from_lock(
+ "sha256=814828c34f1315d7e7b7e8295184577cc4e969bad6156ac069d02d63f58d82e8",
+ "#{lockfile_path}:20:17"
+ )
+ end
+ let(:rake_checksums) { [rake_sha256_checksum] }
shared_examples_for "parsing" do
it "parses correctly" do
@@ -125,6 +136,9 @@ RSpec.describe Bundler::LockfileParser do
expect(subject.platforms).to eq platforms
expect(subject.bundler_version).to eq bundler_version
expect(subject.ruby_version).to eq ruby_version
+ rake_spec = specs.last
+ checksums = subject.sources.last.checksum_store.to_lock(specs.last)
+ expect(checksums).to eq("#{rake_spec.name_tuple.lock_name} #{rake_checksums.map(&:to_lock).sort.join(",")}")
end
end
@@ -149,5 +163,59 @@ RSpec.describe Bundler::LockfileParser do
let(:lockfile_contents) { super().sub("peiji-san!", "peiji-san!\n foo: bar") }
include_examples "parsing"
end
+
+ context "when the checksum is urlsafe base64 encoded" do
+ let(:lockfile_contents) do
+ super().sub(
+ "sha256=814828c34f1315d7e7b7e8295184577cc4e969bad6156ac069d02d63f58d82e8",
+ "sha256=gUgow08TFdfnt-gpUYRXfMTpabrWFWrAadAtY_WNgug="
+ )
+ end
+ include_examples "parsing"
+ end
+
+ context "when the checksum is of an unknown algorithm" do
+ let(:rake_sha512_checksum) do
+ Bundler::Checksum.from_lock(
+ "sha512=pVDn9GLmcFkz8vj1ueiVxj5uGKkAyaqYjEX8zG6L5O4BeVg3wANaKbQdpj/B82Nd/MHVszy6polHcyotUdwilQ==",
+ "#{lockfile_path}:20:17"
+ )
+ end
+ let(:lockfile_contents) do
+ super().sub(
+ "sha256=",
+ "sha512=pVDn9GLmcFkz8vj1ueiVxj5uGKkAyaqYjEX8zG6L5O4BeVg3wANaKbQdpj/B82Nd/MHVszy6polHcyotUdwilQ==,sha256="
+ )
+ end
+ let(:rake_checksums) { [rake_sha256_checksum, rake_sha512_checksum] }
+ include_examples "parsing"
+ end
+
+ context "when CHECKSUMS has duplicate checksums in the lockfile that don't match" do
+ let(:bad_checksum) { "sha256=c0ffee11c0ffee11c0ffee11c0ffee11c0ffee11c0ffee11c0ffee11c0ffee11" }
+ let(:lockfile_contents) { super().split(/(?<=CHECKSUMS\n)/m).insert(1, " rake (10.3.2) #{bad_checksum}\n").join }
+
+ it "raises a security error" do
+ expect { subject }.to raise_error(Bundler::SecurityError) do |e|
+ expect(e.message).to match <<~MESSAGE
+ Bundler found mismatched checksums. This is a potential security risk.
+ rake (10.3.2) #{bad_checksum}
+ from the lockfile CHECKSUMS at #{lockfile_path}:20:17
+ rake (10.3.2) #{rake_sha256_checksum.to_lock}
+ from the lockfile CHECKSUMS at #{lockfile_path}:21:17
+
+ To resolve this issue you can either:
+ 1. remove the matching checksum in #{lockfile_path}:21:17
+ 2. run `bundle install`
+ or if you are sure that the new checksum from the lockfile CHECKSUMS at #{lockfile_path}:21:17 is correct:
+ 1. remove the matching checksum in #{lockfile_path}:20:17
+ 2. run `bundle install`
+
+ To ignore checksum security warnings, disable checksum validation with
+ `bundle config set --local disable_checksum_validation true`
+ MESSAGE
+ end
+ end
+ end
end
end
diff --git a/spec/bundler/bundler/mirror_spec.rb b/spec/bundler/bundler/mirror_spec.rb
index 1eaf1e9a8e..ba1c6ed413 100644
--- a/spec/bundler/bundler/mirror_spec.rb
+++ b/spec/bundler/bundler/mirror_spec.rb
@@ -36,12 +36,12 @@ RSpec.describe Bundler::Settings::Mirror do
it "takes a string for the uri but returns an uri object" do
mirror.uri = "http://localhost:9292"
- expect(mirror.uri).to eq(Bundler::URI("http://localhost:9292"))
+ expect(mirror.uri).to eq(Gem::URI("http://localhost:9292"))
end
it "takes an uri object for the uri" do
- mirror.uri = Bundler::URI("http://localhost:9293")
- expect(mirror.uri).to eq(Bundler::URI("http://localhost:9293"))
+ mirror.uri = Gem::URI("http://localhost:9293")
+ expect(mirror.uri).to eq(Gem::URI("http://localhost:9293"))
end
context "without a uri" do
@@ -145,7 +145,7 @@ RSpec.describe Bundler::Settings::Mirror do
end
RSpec.describe Bundler::Settings::Mirrors do
- let(:localhost_uri) { Bundler::URI("http://localhost:9292") }
+ let(:localhost_uri) { Gem::URI("http://localhost:9292") }
context "with a just created mirror" do
let(:mirrors) do
@@ -260,7 +260,7 @@ RSpec.describe Bundler::Settings::Mirrors do
before { mirrors.parse("mirror.all.fallback_timeout", "true") }
it "returns the source uri, not localhost" do
- expect(mirrors.for("http://whatever.com").uri).to eq(Bundler::URI("http://whatever.com/"))
+ expect(mirrors.for("http://whatever.com").uri).to eq(Gem::URI("http://whatever.com/"))
end
end
end
@@ -270,7 +270,7 @@ RSpec.describe Bundler::Settings::Mirrors do
context "without a fallback timeout" do
it "returns the uri that is not mirrored" do
- expect(mirrors.for("http://whatever.com").uri).to eq(Bundler::URI("http://whatever.com/"))
+ expect(mirrors.for("http://whatever.com").uri).to eq(Gem::URI("http://whatever.com/"))
end
it "returns localhost for rubygems.org" do
@@ -282,11 +282,11 @@ RSpec.describe Bundler::Settings::Mirrors do
before { mirrors.parse("mirror.http://rubygems.org/.fallback_timeout", "true") }
it "returns the uri that is not mirrored" do
- expect(mirrors.for("http://whatever.com").uri).to eq(Bundler::URI("http://whatever.com/"))
+ expect(mirrors.for("http://whatever.com").uri).to eq(Gem::URI("http://whatever.com/"))
end
it "returns rubygems.org for rubygems.org" do
- expect(mirrors.for("http://rubygems.org/").uri).to eq(Bundler::URI("http://rubygems.org/"))
+ expect(mirrors.for("http://rubygems.org/").uri).to eq(Gem::URI("http://rubygems.org/"))
end
end
end
diff --git a/spec/bundler/bundler/plugin/api/source_spec.rb b/spec/bundler/bundler/plugin/api/source_spec.rb
index 428ceb220a..ae02e08bea 100644
--- a/spec/bundler/bundler/plugin/api/source_spec.rb
+++ b/spec/bundler/bundler/plugin/api/source_spec.rb
@@ -51,7 +51,7 @@ RSpec.describe Bundler::Plugin::API::Source do
context "to_lock" do
it "returns the string with remote and type" do
- expected = strip_whitespace <<-L
+ expected = <<~L
PLUGIN SOURCE
remote: #{uri}
type: #{type}
@@ -67,7 +67,7 @@ RSpec.describe Bundler::Plugin::API::Source do
end
it "includes them" do
- expected = strip_whitespace <<-L
+ expected = <<~L
PLUGIN SOURCE
remote: #{uri}
type: #{type}
diff --git a/spec/bundler/bundler/plugin/dsl_spec.rb b/spec/bundler/bundler/plugin/dsl_spec.rb
index 00e39dca69..235a549735 100644
--- a/spec/bundler/bundler/plugin/dsl_spec.rb
+++ b/spec/bundler/bundler/plugin/dsl_spec.rb
@@ -23,7 +23,7 @@ RSpec.describe Bundler::Plugin::DSL do
it "adds #source with :type to list and also inferred_plugins list" do
expect(dsl).to receive(:plugin).with("bundler-source-news").once
- dsl.source("some_random_url", :type => "news") {}
+ dsl.source("some_random_url", type: "news") {}
expect(dsl.inferred_plugins).to eq(["bundler-source-news"])
end
@@ -31,8 +31,8 @@ RSpec.describe Bundler::Plugin::DSL do
it "registers a source type plugin only once for multiple declarations" do
expect(dsl).to receive(:plugin).with("bundler-source-news").and_call_original.once
- dsl.source("some_random_url", :type => "news") {}
- dsl.source("another_random_url", :type => "news") {}
+ dsl.source("some_random_url", type: "news") {}
+ dsl.source("another_random_url", type: "news") {}
end
end
end
diff --git a/spec/bundler/bundler/plugin/installer_spec.rb b/spec/bundler/bundler/plugin/installer_spec.rb
index 2c50ee5afc..ed40029f5a 100644
--- a/spec/bundler/bundler/plugin/installer_spec.rb
+++ b/spec/bundler/bundler/plugin/installer_spec.rb
@@ -6,7 +6,6 @@ RSpec.describe Bundler::Plugin::Installer do
describe "cli install" do
it "uses Gem.sources when non of the source is provided" do
sources = double(:sources)
- Bundler.settings # initialize it before we have to touch rubygems.ext_lock
allow(Gem).to receive(:sources) { sources }
allow(installer).to receive(:install_rubygems).
@@ -21,15 +20,15 @@ RSpec.describe Bundler::Plugin::Installer do
allow(installer).to receive(:install_git).
and_return("new-plugin" => spec)
- expect(installer.install(["new-plugin"], :git => "https://some.ran/dom")).
+ expect(installer.install(["new-plugin"], git: "https://some.ran/dom")).
to eq("new-plugin" => spec)
end
it "returns the installed spec after installing local git plugins" do
- allow(installer).to receive(:install_local_git).
+ allow(installer).to receive(:install_git).
and_return("new-plugin" => spec)
- expect(installer.install(["new-plugin"], :local_git => "/phony/path/repo")).
+ expect(installer.install(["new-plugin"], git: "/phony/path/repo")).
to eq("new-plugin" => spec)
end
@@ -37,7 +36,7 @@ RSpec.describe Bundler::Plugin::Installer do
allow(installer).to receive(:install_rubygems).
and_return("new-plugin" => spec)
- expect(installer.install(["new-plugin"], :source => "https://some.ran/dom")).
+ expect(installer.install(["new-plugin"], source: "https://some.ran/dom")).
to eq("new-plugin" => spec)
end
end
@@ -52,13 +51,13 @@ RSpec.describe Bundler::Plugin::Installer do
context "git plugins" do
before do
- build_git "ga-plugin", :path => lib_path("ga-plugin") do |s|
+ build_git "ga-plugin", path: lib_path("ga-plugin") do |s|
s.write "plugins.rb"
end
end
let(:result) do
- installer.install(["ga-plugin"], :git => file_uri_for(lib_path("ga-plugin")))
+ installer.install(["ga-plugin"], git: file_uri_for(lib_path("ga-plugin")))
end
it "returns the installed spec after installing" do
@@ -75,13 +74,13 @@ RSpec.describe Bundler::Plugin::Installer do
context "local git plugins" do
before do
- build_git "ga-plugin", :path => lib_path("ga-plugin") do |s|
+ build_git "ga-plugin", path: lib_path("ga-plugin") do |s|
s.write "plugins.rb"
end
end
let(:result) do
- installer.install(["ga-plugin"], :local_git => lib_path("ga-plugin").to_s)
+ installer.install(["ga-plugin"], git: lib_path("ga-plugin").to_s)
end
it "returns the installed spec after installing" do
@@ -98,7 +97,7 @@ RSpec.describe Bundler::Plugin::Installer do
context "rubygems plugins" do
let(:result) do
- installer.install(["re-plugin"], :source => file_uri_for(gem_repo2))
+ installer.install(["re-plugin"], source: file_uri_for(gem_repo2))
end
it "returns the installed spec after installing " do
@@ -113,7 +112,7 @@ RSpec.describe Bundler::Plugin::Installer do
context "multiple plugins" do
let(:result) do
- installer.install(["re-plugin", "ma-plugin"], :source => file_uri_for(gem_repo2))
+ installer.install(["re-plugin", "ma-plugin"], source: file_uri_for(gem_repo2))
end
it "returns the installed spec after installing " do
diff --git a/spec/bundler/bundler/plugin_spec.rb b/spec/bundler/bundler/plugin_spec.rb
index d28479cf31..f41b4eff3a 100644
--- a/spec/bundler/bundler/plugin_spec.rb
+++ b/spec/bundler/bundler/plugin_spec.rb
@@ -9,11 +9,11 @@ RSpec.describe Bundler::Plugin do
let(:spec2) { double(:spec2) }
before do
- build_lib "new-plugin", :path => lib_path("new-plugin") do |s|
+ build_lib "new-plugin", path: lib_path("new-plugin") do |s|
s.write "plugins.rb"
end
- build_lib "another-plugin", :path => lib_path("another-plugin") do |s|
+ build_lib "another-plugin", path: lib_path("another-plugin") do |s|
s.write "plugins.rb"
end
@@ -225,7 +225,7 @@ RSpec.describe Bundler::Plugin do
end
end
- describe "#source_from_lock" do
+ describe "#from_lock" do
it "returns instance of registered class initialized with locked opts" do
opts = { "type" => "l_source", "remote" => "xyz", "other" => "random" }
allow(index).to receive(:source_plugin).with("l_source") { "plugin_name" }
@@ -236,7 +236,7 @@ RSpec.describe Bundler::Plugin do
expect(SClass).to receive(:new).
with(hash_including("type" => "l_source", "uri" => "xyz", "other" => "random")) { s_instance }
- expect(subject.source_from_lock(opts)).to be(s_instance)
+ expect(subject.from_lock(opts)).to be(s_instance)
end
end
@@ -275,7 +275,7 @@ RSpec.describe Bundler::Plugin do
describe "#hook" do
before do
path = lib_path("foo-plugin")
- build_lib "foo-plugin", :path => path do |s|
+ build_lib "foo-plugin", path: path do |s|
s.write "plugins.rb", code
end
diff --git a/spec/bundler/bundler/remote_specification_spec.rb b/spec/bundler/bundler/remote_specification_spec.rb
index 921a47a2d3..f35b231d58 100644
--- a/spec/bundler/bundler/remote_specification_spec.rb
+++ b/spec/bundler/bundler/remote_specification_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe Bundler::RemoteSpecification do
end
describe "#fetch_platform" do
- let(:remote_spec) { double(:remote_spec, :platform => "jruby") }
+ let(:remote_spec) { double(:remote_spec, platform: "jruby") }
before { allow(spec_fetcher).to receive(:fetch_spec).and_return(remote_spec) }
@@ -113,7 +113,7 @@ RSpec.describe Bundler::RemoteSpecification do
context "comparing a non sortable object" do
let(:other) { Object.new }
- let(:remote_spec) { double(:remote_spec, :platform => "jruby") }
+ let(:remote_spec) { double(:remote_spec, platform: "jruby") }
before do
allow(spec_fetcher).to receive(:fetch_spec).and_return(remote_spec)
@@ -127,8 +127,8 @@ RSpec.describe Bundler::RemoteSpecification do
end
describe "#__swap__" do
- let(:spec) { double(:spec, :dependencies => []) }
- let(:new_spec) { double(:new_spec, :dependencies => [], :runtime_dependencies => []) }
+ let(:spec) { double(:spec, dependencies: []) }
+ let(:new_spec) { double(:new_spec, dependencies: [], runtime_dependencies: []) }
before { subject.instance_variable_set(:@_remote_specification, spec) }
@@ -157,7 +157,7 @@ RSpec.describe Bundler::RemoteSpecification do
describe "method missing" do
context "and is present in Gem::Specification" do
- let(:remote_spec) { double(:remote_spec, :authors => "abcd") }
+ let(:remote_spec) { double(:remote_spec, authors: "abcd") }
before do
allow(subject).to receive(:_remote_specification).and_return(remote_spec)
@@ -172,7 +172,7 @@ RSpec.describe Bundler::RemoteSpecification do
describe "respond to missing?" do
context "and is present in Gem::Specification" do
- let(:remote_spec) { double(:remote_spec, :authors => "abcd") }
+ let(:remote_spec) { double(:remote_spec, authors: "abcd") }
before do
allow(subject).to receive(:_remote_specification).and_return(remote_spec)
diff --git a/spec/bundler/bundler/resolver/candidate_spec.rb b/spec/bundler/bundler/resolver/candidate_spec.rb
index cd52c867c4..f7b378d32b 100644
--- a/spec/bundler/bundler/resolver/candidate_spec.rb
+++ b/spec/bundler/bundler/resolver/candidate_spec.rb
@@ -2,7 +2,7 @@
RSpec.describe Bundler::Resolver::Candidate do
it "compares fine" do
- version1 = described_class.new("1.12.5", :specs => [Gem::Specification.new("foo", "1.12.5") {|s| s.platform = Gem::Platform::RUBY }])
+ version1 = described_class.new("1.12.5", specs: [Gem::Specification.new("foo", "1.12.5") {|s| s.platform = Gem::Platform::RUBY }])
version2 = described_class.new("1.12.5") # passing no specs creates a platform specific candidate, so sorts higher
expect(version2 >= version1).to be true
@@ -13,8 +13,8 @@ RSpec.describe Bundler::Resolver::Candidate do
expect(version1.platform_specific! >= version2.generic!).to be true
expect(version2.platform_specific! >= version1.generic!).to be true
- version1 = described_class.new("1.12.5", :specs => [Gem::Specification.new("foo", "1.12.5") {|s| s.platform = Gem::Platform::RUBY }])
- version2 = described_class.new("1.12.5", :specs => [Gem::Specification.new("foo", "1.12.5") {|s| s.platform = Gem::Platform::X64_LINUX }])
+ version1 = described_class.new("1.12.5", specs: [Gem::Specification.new("foo", "1.12.5") {|s| s.platform = Gem::Platform::RUBY }])
+ version2 = described_class.new("1.12.5", specs: [Gem::Specification.new("foo", "1.12.5") {|s| s.platform = Gem::Platform::X64_LINUX }])
expect(version2 >= version1).to be true
end
diff --git a/spec/bundler/bundler/ruby_dsl_spec.rb b/spec/bundler/bundler/ruby_dsl_spec.rb
index bc1ca98457..384ac4b8b2 100644
--- a/spec/bundler/bundler/ruby_dsl_spec.rb
+++ b/spec/bundler/bundler/ruby_dsl_spec.rb
@@ -7,28 +7,37 @@ RSpec.describe Bundler::RubyDsl do
include Bundler::RubyDsl
attr_reader :ruby_version
+ attr_accessor :gemfile
end
let(:dsl) { MockDSL.new }
let(:ruby_version) { "2.0.0" }
+ let(:ruby_version_arg) { ruby_version }
let(:version) { "2.0.0" }
let(:engine) { "jruby" }
let(:engine_version) { "9000" }
let(:patchlevel) { "100" }
let(:options) do
- { :patchlevel => patchlevel,
- :engine => engine,
- :engine_version => engine_version }
+ { patchlevel: patchlevel,
+ engine: engine,
+ engine_version: engine_version }
end
+ let(:project_root) { Pathname.new("/path/to/project") }
+ let(:gemfile) { project_root.join("Gemfile") }
+ before { allow(Bundler).to receive(:root).and_return(project_root) }
let(:invoke) do
proc do
- args = Array(ruby_version) + [options]
+ args = []
+ args << ruby_version_arg if ruby_version_arg
+ args << options
+
dsl.ruby(*args)
end
end
subject do
+ dsl.gemfile = gemfile
invoke.call
dsl.ruby_version
end
@@ -59,6 +68,15 @@ RSpec.describe Bundler::RubyDsl do
it_behaves_like "it stores the ruby version"
end
+ context "with a preview version" do
+ let(:ruby_version) { "3.3.0-preview2" }
+
+ it "stores the version" do
+ expect(subject.versions).to eq(Array("3.3.0.preview2"))
+ expect(subject.gem_version.version).to eq("3.3.0.preview2")
+ end
+ end
+
context "with two requirements in the same string" do
let(:ruby_version) { ">= 2.0.0, < 3.0" }
it "raises an error" do
@@ -91,5 +109,94 @@ RSpec.describe Bundler::RubyDsl do
it_behaves_like "it stores the ruby version"
end
end
+
+ context "with a file option" do
+ let(:file) { ".ruby-version" }
+ let(:ruby_version_file_path) { gemfile.dirname.join(file) }
+ let(:options) do
+ { file: file,
+ patchlevel: patchlevel,
+ engine: engine,
+ engine_version: engine_version }
+ end
+ let(:ruby_version_arg) { nil }
+ let(:file_content) { "#{version}\n" }
+
+ before do
+ allow(Bundler).to receive(:read_file) do |path|
+ raise Errno::ENOENT, <<~ERROR unless path == ruby_version_file_path
+ #{file} not found in specs:
+ expected: #{ruby_version_file_path}
+ received: #{path}
+ ERROR
+ file_content
+ end
+ end
+
+ it_behaves_like "it stores the ruby version"
+
+ context "with the Gemfile ruby file: path is relative to the Gemfile in a subdir" do
+ let(:gemfile) { project_root.join("subdir", "Gemfile") }
+ let(:file) { "../.ruby-version" }
+ let(:ruby_version_file_path) { gemfile.dirname.join(file) }
+
+ it_behaves_like "it stores the ruby version"
+ end
+
+ context "with bundler root in a subdir of the project" do
+ let(:project_root) { Pathname.new("/path/to/project/subdir") }
+ let(:gemfile) { project_root.parent.join("Gemfile") }
+
+ it_behaves_like "it stores the ruby version"
+ end
+
+ context "with the ruby- prefix in the file" do
+ let(:file_content) { "ruby-#{version}\n" }
+
+ it_behaves_like "it stores the ruby version"
+ end
+
+ context "and a version" do
+ let(:ruby_version_arg) { version }
+
+ it "raises an error" do
+ expect { subject }.to raise_error(Bundler::GemfileError, "Do not pass version argument when using :file option")
+ end
+ end
+
+ context "with a @gemset" do
+ let(:file_content) { "ruby-#{version}@gemset\n" }
+
+ it "raises an error" do
+ expect { subject }.to raise_error(Gem::Requirement::BadRequirementError, "Illformed requirement [\"#{version}@gemset\"]")
+ end
+ end
+
+ context "with a .tool-versions file format" do
+ let(:file) { ".tool-versions" }
+ let(:ruby_version_arg) { nil }
+ let(:file_content) do
+ <<~TOOLS
+ nodejs 18.16.0
+ ruby #{version} # This is a comment
+ pnpm 8.6.12
+ TOOLS
+ end
+
+ it_behaves_like "it stores the ruby version"
+
+ context "with extra spaces and a very cozy comment" do
+ let(:file_content) do
+ <<~TOOLS
+ nodejs 18.16.0
+ ruby #{version}# This is a cozy comment
+ pnpm 8.6.12
+ TOOLS
+ end
+
+ it_behaves_like "it stores the ruby version"
+ end
+ end
+ end
end
end
diff --git a/spec/bundler/bundler/rubygems_integration_spec.rb b/spec/bundler/bundler/rubygems_integration_spec.rb
index 182aa3646a..b6bda9f43e 100644
--- a/spec/bundler/bundler/rubygems_integration_spec.rb
+++ b/spec/bundler/bundler/rubygems_integration_spec.rb
@@ -1,10 +1,6 @@
# frozen_string_literal: true
RSpec.describe Bundler::RubygemsIntegration do
- it "uses the same chdir lock as rubygems" do
- expect(Bundler.rubygems.ext_lock).to eq(Gem::Ext::Builder::CHDIR_MONITOR)
- end
-
context "#validate" do
let(:spec) do
Gem::Specification.new do |s|
@@ -36,7 +32,7 @@ RSpec.describe Bundler::RubygemsIntegration do
describe "#download_gem" do
let(:bundler_retry) { double(Bundler::Retry) }
- let(:uri) { Bundler::URI.parse("https://foo.bar") }
+ let(:uri) { Gem::URI.parse("https://foo.bar") }
let(:cache_dir) { "#{Gem.path.first}/cache" }
let(:spec) do
spec = Gem::Specification.new("Foo", Gem::Version.new("2.5.2"))
@@ -46,14 +42,12 @@ RSpec.describe Bundler::RubygemsIntegration do
let(:fetcher) { double("gem_remote_fetcher") }
it "successfully downloads gem with retries" do
- expect(Bundler.rubygems).to receive(:gem_remote_fetcher).and_return(fetcher)
- expect(fetcher).to receive(:headers=).with({ "X-Gemfile-Source" => "https://foo.bar" })
expect(Bundler::Retry).to receive(:new).with("download gem from #{uri}/").
and_return(bundler_retry)
expect(bundler_retry).to receive(:attempts).and_yield
expect(fetcher).to receive(:cache_update_path)
- Bundler.rubygems.download_gem(spec, uri, cache_dir)
+ Bundler.rubygems.download_gem(spec, uri, cache_dir, fetcher)
end
end
@@ -64,40 +58,35 @@ RSpec.describe Bundler::RubygemsIntegration do
let(:prerelease_specs_response) { Marshal.dump(["prerelease_specs"]) }
context "when a rubygems source mirror is set" do
- let(:orig_uri) { Bundler::URI("http://zombo.com") }
- let(:remote_with_mirror) { double("remote", :uri => uri, :original_uri => orig_uri) }
+ let(:orig_uri) { Gem::URI("http://zombo.com") }
+ let(:remote_with_mirror) { double("remote", uri: uri, original_uri: orig_uri) }
it "sets the 'X-Gemfile-Source' header containing the original source" do
- expect(Bundler.rubygems).to receive(:gem_remote_fetcher).twice.and_return(fetcher)
- expect(fetcher).to receive(:headers=).with({ "X-Gemfile-Source" => "http://zombo.com" }).twice
expect(fetcher).to receive(:fetch_path).with(uri + "specs.4.8.gz").and_return(specs_response)
expect(fetcher).to receive(:fetch_path).with(uri + "prerelease_specs.4.8.gz").and_return(prerelease_specs_response)
- result = Bundler.rubygems.fetch_all_remote_specs(remote_with_mirror)
+ result = Bundler.rubygems.fetch_all_remote_specs(remote_with_mirror, fetcher)
expect(result).to eq(%w[specs prerelease_specs])
end
end
context "when there is no rubygems source mirror set" do
- let(:remote_no_mirror) { double("remote", :uri => uri, :original_uri => nil) }
+ let(:remote_no_mirror) { double("remote", uri: uri, original_uri: nil) }
it "does not set the 'X-Gemfile-Source' header" do
- expect(Bundler.rubygems).to receive(:gem_remote_fetcher).twice.and_return(fetcher)
- expect(fetcher).to_not receive(:headers=)
expect(fetcher).to receive(:fetch_path).with(uri + "specs.4.8.gz").and_return(specs_response)
expect(fetcher).to receive(:fetch_path).with(uri + "prerelease_specs.4.8.gz").and_return(prerelease_specs_response)
- result = Bundler.rubygems.fetch_all_remote_specs(remote_no_mirror)
+ result = Bundler.rubygems.fetch_all_remote_specs(remote_no_mirror, fetcher)
expect(result).to eq(%w[specs prerelease_specs])
end
end
context "when loading an unexpected class" do
- let(:remote_no_mirror) { double("remote", :uri => uri, :original_uri => nil) }
+ let(:remote_no_mirror) { double("remote", uri: uri, original_uri: nil) }
let(:unexpected_specs_response) { Marshal.dump(3) }
it "raises a MarshalError error" do
- expect(Bundler.rubygems).to receive(:gem_remote_fetcher).once.and_return(fetcher)
expect(fetcher).to receive(:fetch_path).with(uri + "specs.4.8.gz").and_return(unexpected_specs_response)
- expect { Bundler.rubygems.fetch_all_remote_specs(remote_no_mirror) }.to raise_error(Bundler::MarshalError, /unexpected class/i)
+ expect { Bundler.rubygems.fetch_all_remote_specs(remote_no_mirror, fetcher) }.to raise_error(Bundler::MarshalError, /unexpected class/i)
end
end
end
diff --git a/spec/bundler/bundler/settings/validator_spec.rb b/spec/bundler/bundler/settings/validator_spec.rb
index e4ffd89435..b252ba59a0 100644
--- a/spec/bundler/bundler/settings/validator_spec.rb
+++ b/spec/bundler/bundler/settings/validator_spec.rb
@@ -44,14 +44,14 @@ RSpec.describe Bundler::Settings::Validator do
validate!("without", "b:c", "BUNDLE_WITH" => "a")
end.not_to raise_error
- expect { validate!("with", "b:c", "BUNDLE_WITHOUT" => "c:d") }.to raise_error Bundler::InvalidOption, strip_whitespace(<<-EOS).strip
+ expect { validate!("with", "b:c", "BUNDLE_WITHOUT" => "c:d") }.to raise_error Bundler::InvalidOption, <<~EOS.strip
Setting `with` to "b:c" failed:
- a group cannot be in both `with` & `without` simultaneously
- `without` is current set to [:c, :d]
- the `c` groups conflict
EOS
- expect { validate!("without", "b:c", "BUNDLE_WITH" => "c:d") }.to raise_error Bundler::InvalidOption, strip_whitespace(<<-EOS).strip
+ expect { validate!("without", "b:c", "BUNDLE_WITH" => "c:d") }.to raise_error Bundler::InvalidOption, <<~EOS.strip
Setting `without` to "b:c" failed:
- a group cannot be in both `with` & `without` simultaneously
- `with` is current set to [:c, :d]
@@ -74,7 +74,7 @@ RSpec.describe Bundler::Settings::Validator do
describe "#fail!" do
it "raises with a helpful message" do
- expect { subject.fail!("key", "value", "reason1", "reason2") }.to raise_error Bundler::InvalidOption, strip_whitespace(<<-EOS).strip
+ expect { subject.fail!("key", "value", "reason1", "reason2") }.to raise_error Bundler::InvalidOption, <<~EOS.strip
Setting `key` to "value" failed:
- rule description
- reason1
diff --git a/spec/bundler/bundler/settings_spec.rb b/spec/bundler/bundler/settings_spec.rb
index 4636993d9f..634e0faf91 100644
--- a/spec/bundler/bundler/settings_spec.rb
+++ b/spec/bundler/bundler/settings_spec.rb
@@ -131,7 +131,7 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
Bundler.settings.set_command_option :no_install, true
- Bundler.settings.temporary(:no_install => false) do
+ Bundler.settings.temporary(no_install: false) do
expect(Bundler.settings[:no_install]).to eq false
end
@@ -147,12 +147,12 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
context "when called without a block" do
it "leaves the setting changed" do
- Bundler.settings.temporary(:foo => :random)
+ Bundler.settings.temporary(foo: :random)
expect(Bundler.settings[:foo]).to eq "random"
end
it "returns nil" do
- expect(Bundler.settings.temporary(:foo => :bar)).to be_nil
+ expect(Bundler.settings.temporary(foo: :bar)).to be_nil
end
end
end
@@ -179,7 +179,7 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
end
describe "#mirror_for" do
- let(:uri) { Bundler::URI("https://rubygems.org/") }
+ let(:uri) { Gem::URI("https://rubygems.org/") }
context "with no configured mirror" do
it "returns the original URI" do
@@ -192,7 +192,7 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
end
context "with a configured mirror" do
- let(:mirror_uri) { Bundler::URI("https://rubygems-mirror.org/") }
+ let(:mirror_uri) { Gem::URI("https://rubygems-mirror.org/") }
before { settings.set_local "mirror.https://rubygems.org/", mirror_uri.to_s }
@@ -213,7 +213,7 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
end
context "with a file URI" do
- let(:mirror_uri) { Bundler::URI("file:/foo/BAR/baz/qUx/") }
+ let(:mirror_uri) { Gem::URI("file:/foo/BAR/baz/qUx/") }
it "returns the mirror URI" do
expect(settings.mirror_for(uri)).to eq(mirror_uri)
@@ -231,7 +231,7 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
end
describe "#credentials_for" do
- let(:uri) { Bundler::URI("https://gemserver.example.org/") }
+ let(:uri) { Gem::URI("https://gemserver.example.org/") }
let(:credentials) { "username:password" }
context "with no configured credentials" do
@@ -291,7 +291,7 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
it "reads older keys without trailing slashes" do
settings.set_local "mirror.https://rubygems.org", "http://rubygems-mirror.org"
expect(settings.mirror_for("https://rubygems.org/")).to eq(
- Bundler::URI("http://rubygems-mirror.org/")
+ Gem::URI("http://rubygems-mirror.org/")
)
end
@@ -319,6 +319,15 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
expect(settings["mirror.https://rubygems.org/"]).to eq("http://rubygems-mirror.org")
end
+ it "ignores commented out keys" do
+ create_file bundled_app(".bundle/config"), <<~C
+ # BUNDLE_MY-PERSONAL-SERVER__ORG: my-personal-server.org
+ C
+
+ expect(Bundler.ui).not_to receive(:warn)
+ expect(settings.all).to be_empty
+ end
+
it "converts older keys with dashes" do
config("BUNDLE_MY-PERSONAL-SERVER__ORG" => "my-personal-server.org")
expect(Bundler.ui).to receive(:warn).with(
diff --git a/spec/bundler/bundler/shared_helpers_spec.rb b/spec/bundler/bundler/shared_helpers_spec.rb
index 3c6536c4eb..918f73b337 100644
--- a/spec/bundler/bundler/shared_helpers_spec.rb
+++ b/spec/bundler/bundler/shared_helpers_spec.rb
@@ -1,12 +1,8 @@
# frozen_string_literal: true
RSpec.describe Bundler::SharedHelpers do
- let(:ext_lock_double) { double(:ext_lock) }
-
before do
pwd_stub
- allow(Bundler.rubygems).to receive(:ext_lock).and_return(ext_lock_double)
- allow(ext_lock_double).to receive(:synchronize) {|&block| block.call }
end
let(:pwd_stub) { allow(subject).to receive(:pwd).and_return(bundled_app) }
@@ -242,14 +238,12 @@ RSpec.describe Bundler::SharedHelpers do
shared_examples_for "ENV['RUBYOPT'] gets set correctly" do
it "ensures -rbundler/setup is at the beginning of ENV['RUBYOPT']" do
subject.set_bundle_environment
- expect(ENV["RUBYOPT"].split(" ")).to start_with("-r#{source_lib_dir}/bundler/setup")
+ expect(ENV["RUBYOPT"].split(" ")).to start_with("-r#{install_path}/bundler/setup")
end
end
shared_examples_for "ENV['BUNDLER_SETUP'] gets set correctly" do
it "ensures bundler/setup is set in ENV['BUNDLER_SETUP']" do
- skip "Does not play well with DidYouMean being a bundled gem instead of a default gem in Ruby 2.6" if RUBY_VERSION < "2.7"
-
subject.set_bundle_environment
expect(ENV["BUNDLER_SETUP"]).to eq("#{source_lib_dir}/bundler/setup")
end
@@ -367,20 +361,41 @@ RSpec.describe Bundler::SharedHelpers do
end
end
- context "ENV['RUBYOPT'] does not exist" do
- before { ENV.delete("RUBYOPT") }
+ context "when bundler install path is standard" do
+ let(:install_path) { source_lib_dir }
- it_behaves_like "ENV['RUBYOPT'] gets set correctly"
- end
+ context "ENV['RUBYOPT'] does not exist" do
+ before { ENV.delete("RUBYOPT") }
- context "ENV['RUBYOPT'] exists without -rbundler/setup" do
- before { ENV["RUBYOPT"] = "-I/some_app_path/lib" }
+ it_behaves_like "ENV['RUBYOPT'] gets set correctly"
+ end
- it_behaves_like "ENV['RUBYOPT'] gets set correctly"
+ context "ENV['RUBYOPT'] exists without -rbundler/setup" do
+ before { ENV["RUBYOPT"] = "-I/some_app_path/lib" }
+
+ it_behaves_like "ENV['RUBYOPT'] gets set correctly"
+ end
+
+ context "ENV['RUBYOPT'] exists and contains -rbundler/setup" do
+ before { ENV["RUBYOPT"] = "-rbundler/setup" }
+
+ it_behaves_like "ENV['RUBYOPT'] gets set correctly"
+ end
end
- context "ENV['RUBYOPT'] exists and contains -rbundler/setup" do
- before { ENV["RUBYOPT"] = "-rbundler/setup" }
+ context "when bundler install path contains special characters" do
+ let(:install_path) { "/opt/ruby3.3.0-preview2/lib/ruby/3.3.0+0" }
+
+ before do
+ ENV["RUBYOPT"] = "-r#{install_path}/bundler/setup"
+ allow(File).to receive(:expand_path).and_return("#{install_path}/bundler/setup")
+ allow(Gem).to receive(:bin_path).and_return("#{install_path}/bundler/setup")
+ end
+
+ it "ensures -rbundler/setup is not duplicated" do
+ subject.set_bundle_environment
+ expect(ENV["RUBYOPT"].split(" ").grep(%r{-r.*/bundler/setup}).length).to eq(1)
+ end
it_behaves_like "ENV['RUBYOPT'] gets set correctly"
end
@@ -503,4 +518,34 @@ RSpec.describe Bundler::SharedHelpers do
end
end
end
+
+ describe "#major_deprecation" do
+ before { allow(Bundler).to receive(:bundler_major_version).and_return(37) }
+ before { allow(Bundler.ui).to receive(:warn) }
+
+ it "prints and raises nothing below the deprecated major version" do
+ subject.major_deprecation(38, "Message")
+ subject.major_deprecation(39, "Message", removed_message: "Removal", print_caller_location: true)
+ expect(Bundler.ui).not_to have_received(:warn)
+ end
+
+ it "prints but does not raise _at_ the deprecated major version" do
+ subject.major_deprecation(37, "Message")
+ subject.major_deprecation(37, "Message", removed_message: "Removal")
+ expect(Bundler.ui).to have_received(:warn).with("[DEPRECATED] Message").twice
+
+ subject.major_deprecation(37, "Message", print_caller_location: true)
+ expect(Bundler.ui).to have_received(:warn).
+ with(a_string_matching(/^\[DEPRECATED\] Message \(called at .*:\d+\)$/))
+ end
+
+ it "raises the appropriate errors when _past_ the deprecated major version" do
+ expect { subject.major_deprecation(36, "Message") }.
+ to raise_error(Bundler::DeprecatedError, "[REMOVED] Message")
+ expect { subject.major_deprecation(36, "Message", removed_message: "Removal") }.
+ to raise_error(Bundler::DeprecatedError, "[REMOVED] Removal")
+ expect { subject.major_deprecation(35, "Message", removed_message: "Removal", print_caller_location: true) }.
+ to raise_error(Bundler::DeprecatedError, /^\[REMOVED\] Removal \(called at .*:\d+\)$/)
+ end
+ end
end
diff --git a/spec/bundler/bundler/source/git/git_proxy_spec.rb b/spec/bundler/bundler/source/git/git_proxy_spec.rb
index 98d54015e7..1450316d59 100644
--- a/spec/bundler/bundler/source/git/git_proxy_spec.rb
+++ b/spec/bundler/bundler/source/git/git_proxy_spec.rb
@@ -3,34 +3,84 @@
RSpec.describe Bundler::Source::Git::GitProxy do
let(:path) { Pathname("path") }
let(:uri) { "https://github.com/rubygems/rubygems.git" }
- let(:ref) { "HEAD" }
+ let(:ref) { nil }
+ let(:branch) { nil }
+ let(:tag) { nil }
+ let(:options) { { "ref" => ref, "branch" => branch, "tag" => tag }.compact }
let(:revision) { nil }
let(:git_source) { nil }
- let(:clone_result) { double(Process::Status, :success? => true) }
+ let(:clone_result) { double(Process::Status, success?: true) }
let(:base_clone_args) { ["clone", "--bare", "--no-hardlinks", "--quiet", "--no-tags", "--depth", "1", "--single-branch"] }
- subject { described_class.new(path, uri, ref, revision, git_source) }
+ subject(:git_proxy) { described_class.new(path, uri, options, revision, git_source) }
+
+ context "with explicit ref" do
+ context "with branch only" do
+ let(:branch) { "main" }
+ it "sets explicit ref to branch" do
+ expect(git_proxy.explicit_ref).to eq(branch)
+ end
+ end
+
+ context "with ref only" do
+ let(:ref) { "HEAD" }
+ it "sets explicit ref to ref" do
+ expect(git_proxy.explicit_ref).to eq(ref)
+ end
+ end
+
+ context "with tag only" do
+ let(:tag) { "v1.0" }
+ it "sets explicit ref to ref" do
+ expect(git_proxy.explicit_ref).to eq(tag)
+ end
+ end
+
+ context "with tag and branch" do
+ let(:tag) { "v1.0" }
+ let(:branch) { "main" }
+ it "raises error" do
+ expect { git_proxy }.to raise_error(Bundler::Source::Git::AmbiguousGitReference)
+ end
+ end
+
+ context "with tag and ref" do
+ let(:tag) { "v1.0" }
+ let(:ref) { "HEAD" }
+ it "raises error" do
+ expect { git_proxy }.to raise_error(Bundler::Source::Git::AmbiguousGitReference)
+ end
+ end
+
+ context "with branch and ref" do
+ let(:branch) { "main" }
+ let(:ref) { "HEAD" }
+ it "honors ref over branch" do
+ expect(git_proxy.explicit_ref).to eq(ref)
+ end
+ end
+ end
context "with configured credentials" do
it "adds username and password to URI" do
Bundler.settings.temporary(uri => "u:p") do
- allow(subject).to receive(:git).with("--version").and_return("git version 2.14.0")
- expect(subject).to receive(:capture).with([*base_clone_args, "--", "https://u:p@github.com/rubygems/rubygems.git", path.to_s], nil).and_return(["", "", clone_result])
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:capture).with([*base_clone_args, "--", "https://u:p@github.com/rubygems/rubygems.git", path.to_s], nil).and_return(["", "", clone_result])
subject.checkout
end
end
it "adds username and password to URI for host" do
Bundler.settings.temporary("github.com" => "u:p") do
- allow(subject).to receive(:git).with("--version").and_return("git version 2.14.0")
- expect(subject).to receive(:capture).with([*base_clone_args, "--", "https://u:p@github.com/rubygems/rubygems.git", path.to_s], nil).and_return(["", "", clone_result])
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:capture).with([*base_clone_args, "--", "https://u:p@github.com/rubygems/rubygems.git", path.to_s], nil).and_return(["", "", clone_result])
subject.checkout
end
end
it "does not add username and password to mismatched URI" do
Bundler.settings.temporary("https://u:p@github.com/rubygems/rubygems-mismatch.git" => "u:p") do
- allow(subject).to receive(:git).with("--version").and_return("git version 2.14.0")
- expect(subject).to receive(:capture).with([*base_clone_args, "--", uri, path.to_s], nil).and_return(["", "", clone_result])
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:capture).with([*base_clone_args, "--", uri, path.to_s], nil).and_return(["", "", clone_result])
subject.checkout
end
end
@@ -38,10 +88,10 @@ RSpec.describe Bundler::Source::Git::GitProxy do
it "keeps original userinfo" do
Bundler.settings.temporary("github.com" => "u:p") do
original = "https://orig:info@github.com/rubygems/rubygems.git"
- subject = described_class.new(Pathname("path"), original, "HEAD")
- allow(subject).to receive(:git).with("--version").and_return("git version 2.14.0")
- expect(subject).to receive(:capture).with([*base_clone_args, "--", original, path.to_s], nil).and_return(["", "", clone_result])
- subject.checkout
+ git_proxy = described_class.new(Pathname("path"), original, options)
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:capture).with([*base_clone_args, "--", original, path.to_s], nil).and_return(["", "", clone_result])
+ git_proxy.checkout
end
end
end
@@ -49,46 +99,46 @@ RSpec.describe Bundler::Source::Git::GitProxy do
describe "#version" do
context "with a normal version number" do
before do
- expect(subject).to receive(:git).with("--version").
+ expect(git_proxy).to receive(:git_local).with("--version").
and_return("git version 1.2.3")
end
it "returns the git version number" do
- expect(subject.version).to eq("1.2.3")
+ expect(git_proxy.version).to eq("1.2.3")
end
it "does not raise an error when passed into Gem::Version.create" do
- expect { Gem::Version.create subject.version }.not_to raise_error
+ expect { Gem::Version.create git_proxy.version }.not_to raise_error
end
end
context "with a OSX version number" do
before do
- expect(subject).to receive(:git).with("--version").
+ expect(git_proxy).to receive(:git_local).with("--version").
and_return("git version 1.2.3 (Apple Git-BS)")
end
it "strips out OSX specific additions in the version string" do
- expect(subject.version).to eq("1.2.3")
+ expect(git_proxy.version).to eq("1.2.3")
end
it "does not raise an error when passed into Gem::Version.create" do
- expect { Gem::Version.create subject.version }.not_to raise_error
+ expect { Gem::Version.create git_proxy.version }.not_to raise_error
end
end
context "with a msysgit version number" do
before do
- expect(subject).to receive(:git).with("--version").
+ expect(git_proxy).to receive(:git_local).with("--version").
and_return("git version 1.2.3.msysgit.0")
end
it "strips out msysgit specific additions in the version string" do
- expect(subject.version).to eq("1.2.3")
+ expect(git_proxy.version).to eq("1.2.3")
end
it "does not raise an error when passed into Gem::Version.create" do
- expect { Gem::Version.create subject.version }.not_to raise_error
+ expect { Gem::Version.create git_proxy.version }.not_to raise_error
end
end
end
@@ -96,34 +146,34 @@ RSpec.describe Bundler::Source::Git::GitProxy do
describe "#full_version" do
context "with a normal version number" do
before do
- expect(subject).to receive(:git).with("--version").
+ expect(git_proxy).to receive(:git_local).with("--version").
and_return("git version 1.2.3")
end
it "returns the git version number" do
- expect(subject.full_version).to eq("1.2.3")
+ expect(git_proxy.full_version).to eq("1.2.3")
end
end
context "with a OSX version number" do
before do
- expect(subject).to receive(:git).with("--version").
+ expect(git_proxy).to receive(:git_local).with("--version").
and_return("git version 1.2.3 (Apple Git-BS)")
end
it "does not strip out OSX specific additions in the version string" do
- expect(subject.full_version).to eq("1.2.3 (Apple Git-BS)")
+ expect(git_proxy.full_version).to eq("1.2.3 (Apple Git-BS)")
end
end
context "with a msysgit version number" do
before do
- expect(subject).to receive(:git).with("--version").
+ expect(git_proxy).to receive(:git_local).with("--version").
and_return("git version 1.2.3.msysgit.0")
end
it "does not strip out msysgit specific additions in the version string" do
- expect(subject.full_version).to eq("1.2.3.msysgit.0")
+ expect(git_proxy.full_version).to eq("1.2.3.msysgit.0")
end
end
end
@@ -143,7 +193,7 @@ RSpec.describe Bundler::Source::Git::GitProxy do
FileUtils.chmod("+x", file)
- bundle :lock, :raise_on_error => false
+ bundle :lock, raise_on_error: false
expect(Pathname.new(bundled_app("canary"))).not_to exist
end
diff --git a/spec/bundler/bundler/source/git_spec.rb b/spec/bundler/bundler/source/git_spec.rb
index ed6dc3cd29..feef54bbf4 100644
--- a/spec/bundler/bundler/source/git_spec.rb
+++ b/spec/bundler/bundler/source/git_spec.rb
@@ -38,7 +38,7 @@ RSpec.describe Bundler::Source::Git do
context "when the source has a reference" do
let(:git_proxy_stub) do
- instance_double(Bundler::Source::Git::GitProxy, :revision => "123abc", :branch => "v1.0.0")
+ instance_double(Bundler::Source::Git::GitProxy, revision: "123abc", branch: "v1.0.0")
end
let(:options) do
{ "uri" => uri, "ref" => "v1.0.0" }
@@ -55,7 +55,7 @@ RSpec.describe Bundler::Source::Git do
context "when the source has both reference and glob specifiers" do
let(:git_proxy_stub) do
- instance_double(Bundler::Source::Git::GitProxy, :revision => "123abc", :branch => "v1.0.0")
+ instance_double(Bundler::Source::Git::GitProxy, revision: "123abc", branch: "v1.0.0")
end
let(:options) do
{ "uri" => uri, "ref" => "v1.0.0", "glob" => "gems/foo/*.gemspec" }
diff --git a/spec/bundler/bundler/source/rubygems/remote_spec.rb b/spec/bundler/bundler/source/rubygems/remote_spec.rb
index 07ce4f968e..56f3bee459 100644
--- a/spec/bundler/bundler/source/rubygems/remote_spec.rb
+++ b/spec/bundler/bundler/source/rubygems/remote_spec.rb
@@ -11,8 +11,8 @@ RSpec.describe Bundler::Source::Rubygems::Remote do
allow(Digest(:MD5)).to receive(:hexdigest).with(duck_type(:to_s)) {|string| "MD5HEX(#{string})" }
end
- let(:uri_no_auth) { Bundler::URI("https://gems.example.com") }
- let(:uri_with_auth) { Bundler::URI("https://#{credentials}@gems.example.com") }
+ let(:uri_no_auth) { Gem::URI("https://gems.example.com") }
+ let(:uri_with_auth) { Gem::URI("https://#{credentials}@gems.example.com") }
let(:credentials) { "username:password" }
context "when the original URI has no credentials" do
@@ -89,11 +89,11 @@ RSpec.describe Bundler::Source::Rubygems::Remote do
end
context "when the original URI has only a username" do
- let(:uri) { Bundler::URI("https://SeCrEt-ToKeN@gem.fury.io/me/") }
+ let(:uri) { Gem::URI("https://SeCrEt-ToKeN@gem.fury.io/me/") }
describe "#anonymized_uri" do
it "returns the URI without username and password" do
- expect(remote(uri).anonymized_uri).to eq(Bundler::URI("https://gem.fury.io/me/"))
+ expect(remote(uri).anonymized_uri).to eq(Gem::URI("https://gem.fury.io/me/"))
end
end
@@ -105,9 +105,9 @@ RSpec.describe Bundler::Source::Rubygems::Remote do
end
context "when a mirror with inline credentials is configured for the URI" do
- let(:uri) { Bundler::URI("https://rubygems.org/") }
- let(:mirror_uri_with_auth) { Bundler::URI("https://username:password@rubygems-mirror.org/") }
- let(:mirror_uri_no_auth) { Bundler::URI("https://rubygems-mirror.org/") }
+ let(:uri) { Gem::URI("https://rubygems.org/") }
+ let(:mirror_uri_with_auth) { Gem::URI("https://username:password@rubygems-mirror.org/") }
+ let(:mirror_uri_no_auth) { Gem::URI("https://rubygems-mirror.org/") }
before { Bundler.settings.temporary("mirror.https://rubygems.org/" => mirror_uri_with_auth.to_s) }
@@ -131,9 +131,9 @@ RSpec.describe Bundler::Source::Rubygems::Remote do
end
context "when a mirror with configured credentials is configured for the URI" do
- let(:uri) { Bundler::URI("https://rubygems.org/") }
- let(:mirror_uri_with_auth) { Bundler::URI("https://#{credentials}@rubygems-mirror.org/") }
- let(:mirror_uri_no_auth) { Bundler::URI("https://rubygems-mirror.org/") }
+ let(:uri) { Gem::URI("https://rubygems.org/") }
+ let(:mirror_uri_with_auth) { Gem::URI("https://#{credentials}@rubygems-mirror.org/") }
+ let(:mirror_uri_no_auth) { Gem::URI("https://rubygems-mirror.org/") }
before do
Bundler.settings.temporary("mirror.https://rubygems.org/" => mirror_uri_no_auth.to_s)
diff --git a/spec/bundler/bundler/source_list_spec.rb b/spec/bundler/bundler/source_list_spec.rb
index f860e9ff58..13453cb2a3 100644
--- a/spec/bundler/bundler/source_list_spec.rb
+++ b/spec/bundler/bundler/source_list_spec.rb
@@ -85,7 +85,7 @@ RSpec.describe Bundler::SourceList do
end
it "ignores git protocols on request" do
- Bundler.settings.temporary(:"git.allow_insecure" => true)
+ Bundler.settings.temporary("git.allow_insecure": true)
expect(Bundler.ui).to_not receive(:warn).with(msg)
source_list.add_git_source("uri" => "git://existing-git.org/path.git")
end
@@ -125,8 +125,8 @@ RSpec.describe Bundler::SourceList do
it "adds the provided remote to the beginning of the aggregate source" do
source_list.add_global_rubygems_remote("https://othersource.org")
expect(returned_source.remotes).to eq [
- Bundler::URI("https://othersource.org/"),
- Bundler::URI("https://rubygems.org/"),
+ Gem::URI("https://othersource.org/"),
+ Gem::URI("https://rubygems.org/"),
]
end
end
diff --git a/spec/bundler/bundler/source_spec.rb b/spec/bundler/bundler/source_spec.rb
index ceb369ecdb..3b49c37431 100644
--- a/spec/bundler/bundler/source_spec.rb
+++ b/spec/bundler/bundler/source_spec.rb
@@ -21,7 +21,7 @@ RSpec.describe Bundler::Source do
end
describe "#version_message" do
- let(:spec) { double(:spec, :name => "nokogiri", :version => ">= 1.6", :platform => rb) }
+ let(:spec) { double(:spec, name: "nokogiri", version: ">= 1.6", platform: rb) }
shared_examples_for "the lockfile specs are not relevant" do
it "should return a string with the spec name and version" do
@@ -32,19 +32,19 @@ RSpec.describe Bundler::Source do
context "when there are locked gems" do
context "that contain the relevant gem spec" do
context "without a version" do
- let(:locked_gem) { double(:locked_gem, :name => "nokogiri", :version => nil) }
+ let(:locked_gem) { double(:locked_gem, name: "nokogiri", version: nil) }
it_behaves_like "the lockfile specs are not relevant"
end
context "with the same version" do
- let(:locked_gem) { double(:locked_gem, :name => "nokogiri", :version => ">= 1.6") }
+ let(:locked_gem) { double(:locked_gem, name: "nokogiri", version: ">= 1.6") }
it_behaves_like "the lockfile specs are not relevant"
end
context "with a different version" do
- let(:locked_gem) { double(:locked_gem, :name => "nokogiri", :version => "< 1.5") }
+ let(:locked_gem) { double(:locked_gem, name: "nokogiri", version: "< 1.5") }
context "with color", :no_color_tty do
before do
@@ -70,8 +70,8 @@ RSpec.describe Bundler::Source do
end
context "with a more recent version" do
- let(:spec) { double(:spec, :name => "nokogiri", :version => "1.6.1", :platform => rb) }
- let(:locked_gem) { double(:locked_gem, :name => "nokogiri", :version => "1.7.0") }
+ let(:spec) { double(:spec, name: "nokogiri", version: "1.6.1", platform: rb) }
+ let(:locked_gem) { double(:locked_gem, name: "nokogiri", version: "1.7.0") }
context "with color", :no_color_tty do
before do
@@ -97,8 +97,8 @@ RSpec.describe Bundler::Source do
end
context "with an older version" do
- let(:spec) { double(:spec, :name => "nokogiri", :version => "1.7.1", :platform => rb) }
- let(:locked_gem) { double(:locked_gem, :name => "nokogiri", :version => "1.7.0") }
+ let(:spec) { double(:spec, name: "nokogiri", version: "1.7.1", platform: rb) }
+ let(:locked_gem) { double(:locked_gem, name: "nokogiri", version: "1.7.0") }
context "with color", :no_color_tty do
before do
@@ -128,7 +128,7 @@ RSpec.describe Bundler::Source do
describe "#can_lock?" do
context "when the passed spec's source is equivalent" do
- let(:spec) { double(:spec, :source => subject) }
+ let(:spec) { double(:spec, source: subject) }
it "should return true" do
expect(subject.can_lock?(spec)).to be_truthy
@@ -136,7 +136,7 @@ RSpec.describe Bundler::Source do
end
context "when the passed spec's source is not equivalent" do
- let(:spec) { double(:spec, :source => double(:other_source)) }
+ let(:spec) { double(:spec, source: double(:other_source)) }
it "should return false" do
expect(subject.can_lock?(spec)).to be_falsey
diff --git a/spec/bundler/bundler/spec_set_spec.rb b/spec/bundler/bundler/spec_set_spec.rb
index 6fedd38b50..c4b6676223 100644
--- a/spec/bundler/bundler/spec_set_spec.rb
+++ b/spec/bundler/bundler/spec_set_spec.rb
@@ -45,24 +45,6 @@ RSpec.describe Bundler::SpecSet do
end
end
- describe "#merge" do
- let(:other_specs) do
- [
- build_spec("f", "1.0"),
- build_spec("g", "2.0"),
- ].flatten
- end
-
- let(:other_spec_set) { described_class.new(other_specs) }
-
- it "merges the items in each gemspec" do
- new_spec_set = subject.merge(other_spec_set)
- specs = new_spec_set.to_a.map(&:full_name)
- expect(specs).to include("a-1.0")
- expect(specs).to include("f-1.0")
- end
- end
-
describe "#to_a" do
it "returns the specs in order" do
expect(subject.to_a.map(&:full_name)).to eq %w[
diff --git a/spec/bundler/bundler/specifications/foo.gemspec b/spec/bundler/bundler/specifications/foo.gemspec
new file mode 100644
index 0000000000..46eb068cd1
--- /dev/null
+++ b/spec/bundler/bundler/specifications/foo.gemspec
@@ -0,0 +1,13 @@
+# rubocop:disable Style/FrozenStringLiteralComment
+# stub: foo 1.0.0 ruby lib
+
+# The first line would be '# -*- encoding: utf-8 -*-' in a real stub gemspec
+
+Gem::Specification.new do |s|
+ s.name = "foo"
+ s.version = "1.0.0"
+ s.loaded_from = __FILE__
+ s.extensions = "ext/foo"
+ s.required_ruby_version = ">= 3.0.0"
+end
+# rubocop:enable Style/FrozenStringLiteralComment
diff --git a/spec/bundler/bundler/stub_specification_spec.rb b/spec/bundler/bundler/stub_specification_spec.rb
index fb612813c2..dae9f3cfba 100644
--- a/spec/bundler/bundler/stub_specification_spec.rb
+++ b/spec/bundler/bundler/stub_specification_spec.rb
@@ -19,6 +19,17 @@ RSpec.describe Bundler::StubSpecification do
end
end
+ describe "#gem_build_complete_path" do
+ it "StubSpecification should have equal gem_build_complete_path as Specification" do
+ spec_path = File.join(File.dirname(__FILE__), "specifications", "foo.gemspec")
+ spec = Gem::Specification.load(spec_path)
+ gem_stub = Gem::StubSpecification.new(spec_path, File.dirname(__FILE__),"","")
+
+ stub = described_class.from_stub(gem_stub)
+ expect(stub.gem_build_complete_path).to eq spec.gem_build_complete_path
+ end
+ end
+
describe "#manually_installed?" do
it "returns true if installed_by_version is nil or 0" do
stub = described_class.from_stub(with_bundler_stub_spec)
diff --git a/spec/bundler/bundler/uri_credentials_filter_spec.rb b/spec/bundler/bundler/uri_credentials_filter_spec.rb
index 466c1b8594..ed24744a1c 100644
--- a/spec/bundler/bundler/uri_credentials_filter_spec.rb
+++ b/spec/bundler/bundler/uri_credentials_filter_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe Bundler::URICredentialsFilter do
let(:credentials) { "oauth_token:x-oauth-basic@" }
it "returns the uri without the oauth token" do
- expect(subject.credential_filtered_uri(uri).to_s).to eq(Bundler::URI("https://x-oauth-basic@github.com/company/private-repo").to_s)
+ expect(subject.credential_filtered_uri(uri).to_s).to eq(Gem::URI("https://x-oauth-basic@github.com/company/private-repo").to_s)
end
it_behaves_like "original type of uri is maintained"
@@ -26,7 +26,7 @@ RSpec.describe Bundler::URICredentialsFilter do
let(:credentials) { "oauth_token:x@" }
it "returns the uri without the oauth token" do
- expect(subject.credential_filtered_uri(uri).to_s).to eq(Bundler::URI("https://x@github.com/company/private-repo").to_s)
+ expect(subject.credential_filtered_uri(uri).to_s).to eq(Gem::URI("https://x@github.com/company/private-repo").to_s)
end
it_behaves_like "original type of uri is maintained"
@@ -37,7 +37,7 @@ RSpec.describe Bundler::URICredentialsFilter do
let(:credentials) { "username1:hunter3@" }
it "returns the uri without the password" do
- expect(subject.credential_filtered_uri(uri).to_s).to eq(Bundler::URI("https://username1@github.com/company/private-repo").to_s)
+ expect(subject.credential_filtered_uri(uri).to_s).to eq(Gem::URI("https://username1@github.com/company/private-repo").to_s)
end
it_behaves_like "original type of uri is maintained"
@@ -55,7 +55,7 @@ RSpec.describe Bundler::URICredentialsFilter do
end
context "uri is a uri object" do
- let(:uri) { Bundler::URI("https://#{credentials}github.com/company/private-repo") }
+ let(:uri) { Gem::URI("https://#{credentials}github.com/company/private-repo") }
it_behaves_like "sensitive credentials in uri are filtered out"
end
@@ -90,7 +90,7 @@ RSpec.describe Bundler::URICredentialsFilter do
describe "#credential_filtered_string" do
let(:str_to_filter) { "This is a git message containing a uri #{uri}!" }
let(:credentials) { "" }
- let(:uri) { Bundler::URI("https://#{credentials}github.com/company/private-repo") }
+ let(:uri) { Gem::URI("https://#{credentials}github.com/company/private-repo") }
context "with a uri that contains credentials" do
let(:credentials) { "oauth_token:x-oauth-basic@" }
diff --git a/spec/bundler/bundler/yaml_serializer_spec.rb b/spec/bundler/bundler/yaml_serializer_spec.rb
index 1241c74bbf..de437f764a 100644
--- a/spec/bundler/bundler/yaml_serializer_spec.rb
+++ b/spec/bundler/bundler/yaml_serializer_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe Bundler::YAMLSerializer do
it "works for simple hash" do
hash = { "Q" => "Where does Thursday come before Wednesday? In the dictionary. :P" }
- expected = strip_whitespace <<-YAML
+ expected = <<~YAML
---
Q: "Where does Thursday come before Wednesday? In the dictionary. :P"
YAML
@@ -24,7 +24,7 @@ RSpec.describe Bundler::YAMLSerializer do
},
}
- expected = strip_whitespace <<-YAML
+ expected = <<~YAML
---
nice-one:
read_ahead: "All generalizations are false, including this one"
@@ -45,7 +45,7 @@ RSpec.describe Bundler::YAMLSerializer do
},
}
- expected = strip_whitespace <<-YAML
+ expected = <<~YAML
---
nested_hash:
contains_array:
@@ -57,11 +57,24 @@ RSpec.describe Bundler::YAMLSerializer do
expect(serializer.dump(hash)).to eq(expected)
end
+
+ it "handles empty array" do
+ hash = {
+ "empty_array" => [],
+ }
+
+ expected = <<~YAML
+ ---
+ empty_array: []
+ YAML
+
+ expect(serializer.dump(hash)).to eq(expected)
+ end
end
describe "#load" do
it "works for simple hash" do
- yaml = strip_whitespace <<-YAML
+ yaml = <<~YAML
---
Jon: "Air is free dude!"
Jack: "Yes.. until you buy a bag of chips!"
@@ -76,7 +89,7 @@ RSpec.describe Bundler::YAMLSerializer do
end
it "works for nested hash" do
- yaml = strip_whitespace <<-YAML
+ yaml = <<~YAML
---
baa:
baa: "black sheep"
@@ -98,7 +111,7 @@ RSpec.describe Bundler::YAMLSerializer do
end
it "handles colon in key/value" do
- yaml = strip_whitespace <<-YAML
+ yaml = <<~YAML
BUNDLE_MIRROR__HTTPS://RUBYGEMS__ORG/: http://rubygems-mirror.org
YAML
@@ -106,7 +119,7 @@ RSpec.describe Bundler::YAMLSerializer do
end
it "handles arrays inside hashes" do
- yaml = strip_whitespace <<-YAML
+ yaml = <<~YAML
---
nested_hash:
contains_array:
@@ -127,7 +140,7 @@ RSpec.describe Bundler::YAMLSerializer do
end
it "handles windows-style CRLF line endings" do
- yaml = strip_whitespace(<<-YAML).gsub("\n", "\r\n")
+ yaml = <<~YAML.gsub("\n", "\r\n")
---
nested_hash:
contains_array:
@@ -148,6 +161,34 @@ RSpec.describe Bundler::YAMLSerializer do
expect(serializer.load(yaml)).to eq(hash)
end
+
+ it "handles empty array" do
+ yaml = <<~YAML
+ ---
+ empty_array: []
+ YAML
+
+ hash = {
+ "empty_array" => [],
+ }
+
+ expect(serializer.load(yaml)).to eq(hash)
+ end
+
+ it "skip commented out words" do
+ yaml = <<~YAML
+ ---
+ foo: bar
+ buzz: foo # bar
+ YAML
+
+ hash = {
+ "foo" => "bar",
+ "buzz" => "foo",
+ }
+
+ expect(serializer.load(yaml)).to eq(hash)
+ end
end
describe "against yaml lib" do
diff --git a/spec/bundler/cache/gems_spec.rb b/spec/bundler/cache/gems_spec.rb
index 63c00eba01..abbc2c3cf2 100644
--- a/spec/bundler/cache/gems_spec.rb
+++ b/spec/bundler/cache/gems_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe "bundle cache" do
gem 'rack'
G
- system_gems "rack-1.0.0", :path => path
+ system_gems "rack-1.0.0", path: path
bundle :cache
end
@@ -17,7 +17,7 @@ RSpec.describe "bundle cache" do
end
it "uses the cache as a source when installing gems" do
- build_gem "omg", :path => bundled_app("vendor/cache")
+ build_gem "omg", path: bundled_app("vendor/cache")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -28,14 +28,14 @@ RSpec.describe "bundle cache" do
end
it "uses the cache as a source when installing gems with --local" do
- system_gems [], :path => default_bundle_path
+ system_gems [], path: default_bundle_path
bundle "install --local"
expect(the_bundle).to include_gems("rack 1.0.0")
end
it "does not reinstall gems from the cache if they exist on the system" do
- build_gem "rack", "1.0.0", :path => bundled_app("vendor/cache") do |s|
+ build_gem "rack", "1.0.0", path: bundled_app("vendor/cache") do |s|
s.write "lib/rack.rb", "RACK = 'FAIL'"
end
@@ -48,18 +48,18 @@ RSpec.describe "bundle cache" do
end
it "does not reinstall gems from the cache if they exist in the bundle" do
- system_gems "rack-1.0.0", :path => default_bundle_path
+ system_gems "rack-1.0.0", path: default_bundle_path
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
- build_gem "rack", "1.0.0", :path => bundled_app("vendor/cache") do |s|
+ build_gem "rack", "1.0.0", path: bundled_app("vendor/cache") do |s|
s.write "lib/rack.rb", "RACK = 'FAIL'"
end
- bundle :install, :local => true
+ bundle :install, local: true
expect(the_bundle).to include_gems("rack 1.0.0")
end
@@ -97,12 +97,12 @@ RSpec.describe "bundle cache" do
build_gem "json", default_json_version
end
- build_gem "json", default_json_version, :to_system => true, :default => true
+ build_gem "json", default_json_version, to_system: true, default: true
end
it "uses builtin gems when installing to system gems" do
bundle "config set path.system true"
- install_gemfile %(source "#{file_uri_for(gem_repo1)}"; gem 'json', '#{default_json_version}'), :verbose => true
+ install_gemfile %(source "#{file_uri_for(gem_repo1)}"; gem 'json', '#{default_json_version}'), verbose: true
expect(out).to include("Using json #{default_json_version}")
end
@@ -131,7 +131,7 @@ RSpec.describe "bundle cache" do
end
it "doesn't make remote request after caching the gem" do
- build_gem "builtin_gem_2", "1.0.2", :path => bundled_app("vendor/cache") do |s|
+ build_gem "builtin_gem_2", "1.0.2", path: bundled_app("vendor/cache") do |s|
s.summary = "This builtin_gem is bundled with Ruby"
end
@@ -152,7 +152,7 @@ RSpec.describe "bundle cache" do
gem 'json', '#{default_json_version}'
G
- bundle :cache, :raise_on_error => false
+ bundle :cache, raise_on_error: false
expect(exitstatus).to_not eq(0)
expect(err).to include("json-#{default_json_version} is built in to Ruby, and can't be cached")
end
@@ -218,7 +218,7 @@ RSpec.describe "bundle cache" do
end
end
- bundle "update", :all => true
+ bundle "update", all: true
expect(cached_gem("rack-1.2")).to exist
expect(cached_gem("rack-1.0.0")).not_to exist
end
@@ -279,14 +279,61 @@ RSpec.describe "bundle cache" do
it "doesn't remove gems with mismatched :rubygems_version or :date" do
cached_gem("rack-1.0.0").rmtree
build_gem "rack", "1.0.0",
- :path => bundled_app("vendor/cache"),
- :rubygems_version => "1.3.2"
+ path: bundled_app("vendor/cache"),
+ rubygems_version: "1.3.2"
+ # This test is only really valid if the checksum isn't saved. It otherwise can't be the same gem. Tested below.
+ bundled_app_lock.write remove_checksums_from_lockfile(bundled_app_lock.read, "rack (1.0.0)")
simulate_new_machine
bundle :install
expect(cached_gem("rack-1.0.0")).to exist
end
+ it "raises an error when the gem is altered and produces a different checksum" do
+ cached_gem("rack-1.0.0").rmtree
+ build_gem "rack", "1.0.0", path: bundled_app("vendor/cache")
+
+ checksums = checksums_section do |c|
+ c.checksum gem_repo1, "rack", "1.0.0"
+ end
+
+ simulate_new_machine
+
+ lockfile <<-L
+ GEM
+ remote: #{file_uri_for(gem_repo2)}/
+ specs:
+ rack (1.0.0)
+ #{checksums}
+ L
+
+ bundle :install, raise_on_error: false
+ expect(exitstatus).to eq(37)
+ expect(err).to include("Bundler found mismatched checksums.")
+ expect(err).to include("1. remove the gem at #{cached_gem("rack-1.0.0")}")
+
+ expect(cached_gem("rack-1.0.0")).to exist
+ cached_gem("rack-1.0.0").rmtree
+ bundle :install
+ expect(cached_gem("rack-1.0.0")).to exist
+ end
+
+ it "installs a modified gem with a non-matching checksum when checksums is not opted in" do
+ cached_gem("rack-1.0.0").rmtree
+ build_gem "rack", "1.0.0", path: bundled_app("vendor/cache")
+ simulate_new_machine
+
+ lockfile <<-L
+ GEM
+ remote: #{file_uri_for(gem_repo2)}/
+ specs:
+ rack (1.0.0)
+ L
+
+ bundle :install
+ expect(cached_gem("rack-1.0.0")).to exist
+ end
+
it "handles directories and non .gem files in the cache" do
bundled_app("vendor/cache/foo").mkdir
File.open(bundled_app("vendor/cache/bar"), "w") {|f| f.write("not a gem") }
@@ -314,7 +361,7 @@ RSpec.describe "bundle cache" do
it "should install gems with the name bundler in them (that aren't bundler)" do
build_gem "foo-bundler", "1.0",
- :path => bundled_app("vendor/cache")
+ path: bundled_app("vendor/cache")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
diff --git a/spec/bundler/cache/git_spec.rb b/spec/bundler/cache/git_spec.rb
index 890ba88605..4b3cd4d2eb 100644
--- a/spec/bundler/cache/git_spec.rb
+++ b/spec/bundler/cache/git_spec.rb
@@ -89,7 +89,7 @@ RSpec.describe "bundle cache with git" do
ref = git.ref_for("main", 11)
expect(ref).not_to eq(old_ref)
- bundle "update", :all => true
+ bundle "update", all: true
bundle :cache
expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
@@ -164,8 +164,8 @@ RSpec.describe "bundle cache with git" do
s.add_dependency "submodule"
end
- sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", :dir => lib_path("has_submodule-1.0")
- sys_exec "git commit -m \"submodulator\"", :dir => lib_path("has_submodule-1.0")
+ sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", dir: lib_path("has_submodule-1.0")
+ sys_exec "git commit -m \"submodulator\"", dir: lib_path("has_submodule-1.0")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -216,7 +216,7 @@ RSpec.describe "bundle cache with git" do
simulate_new_machine
with_path_as "" do
bundle "config set deployment true"
- bundle :install, :local => true
+ bundle :install, local: true
expect(the_bundle).to include_gem "foo 1.0"
end
end
@@ -266,8 +266,8 @@ RSpec.describe "bundle cache with git" do
# Verify that the compilation worked and the result is in $LOAD_PATH by simply attempting
# to require it; that should make sure this spec does not break if the load path behaviour
# is changed.
- bundle :install, :local => true
- ruby <<~R, :raise_on_error => false
+ bundle :install, local: true
+ ruby <<~R, raise_on_error: false
require 'bundler/setup'
require 'foo_c'
R
diff --git a/spec/bundler/cache/path_spec.rb b/spec/bundler/cache/path_spec.rb
index 2ad136a008..2c8a52617a 100644
--- a/spec/bundler/cache/path_spec.rb
+++ b/spec/bundler/cache/path_spec.rb
@@ -2,7 +2,7 @@
RSpec.describe "bundle cache with path" do
it "is no-op when the path is within the bundle" do
- build_lib "foo", :path => bundled_app("lib/foo")
+ build_lib "foo", path: bundled_app("lib/foo")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -35,7 +35,7 @@ RSpec.describe "bundle cache with path" do
libname = File.basename(bundled_app) + "_gem"
libpath = File.join(File.dirname(bundled_app), libname)
- build_lib libname, :path => libpath
+ build_lib libname, path: libpath
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -97,7 +97,7 @@ RSpec.describe "bundle cache with path" do
expect(bundled_app("vendor/cache/foo-1.0")).not_to exist
end
- it "does not cache path gems by default", :bundler => "< 3" do
+ it "does not cache path gems by default", bundler: "< 3" do
build_lib "foo"
install_gemfile <<-G
@@ -110,7 +110,7 @@ RSpec.describe "bundle cache with path" do
expect(bundled_app("vendor/cache/foo-1.0")).not_to exist
end
- it "caches path gems by default", :bundler => "3" do
+ it "caches path gems by default", bundler: "3" do
build_lib "foo"
install_gemfile <<-G
@@ -163,7 +163,7 @@ RSpec.describe "bundle cache with path" do
gem "baz", :path => '#{lib_path("baz-1.0")}'
G
- bundle "cache --no-all", :raise_on_error => false
+ bundle "cache --no-all", raise_on_error: false
expect(bundled_app("vendor/cache/baz-1.0")).not_to exist
end
end
diff --git a/spec/bundler/cache/platform_spec.rb b/spec/bundler/cache/platform_spec.rb
index 128278956c..36db954c79 100644
--- a/spec/bundler/cache/platform_spec.rb
+++ b/spec/bundler/cache/platform_spec.rb
@@ -41,7 +41,7 @@ RSpec.describe "bundle cache with multiple platforms" do
end
it "ensures that a successful bundle update does not delete gems for other platforms" do
- bundle "update", :all => true
+ bundle "update", all: true
expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
expect(bundled_app("vendor/cache/activesupport-2.3.5.gem")).to exist
diff --git a/spec/bundler/commands/add_spec.rb b/spec/bundler/commands/add_spec.rb
index 5a5b534e8d..36e286793b 100644
--- a/spec/bundler/commands/add_spec.rb
+++ b/spec/bundler/commands/add_spec.rb
@@ -9,6 +9,7 @@ RSpec.describe "bundle add" do
build_gem "bar", "0.12.3"
build_gem "cat", "0.12.3.pre"
build_gem "dog", "1.1.3.pre"
+ build_gem "lemur", "3.1.1.pre.2023.1.1"
end
build_git "foo", "2.0"
@@ -21,7 +22,7 @@ RSpec.describe "bundle add" do
context "when no gems are specified" do
it "shows error" do
- bundle "add", :raise_on_error => false
+ bundle "add", raise_on_error: false
expect(err).to include("Please specify gems to add")
end
@@ -51,6 +52,13 @@ RSpec.describe "bundle add" do
expect(bundled_app_gemfile.read).to match(/gem "dog", "~> 1.1.pre"/)
expect(the_bundle).to include_gems "dog 1.1.3.pre"
end
+
+ it "version requirement becomes ~> major.minor.pre.tail when resolved version has a very long tail pre version" do
+ bundle "add 'lemur'"
+ # the trailing pre purposely matches the release version to ensure that subbing the release doesn't change the pre.version"
+ expect(bundled_app_gemfile.read).to match(/gem "lemur", "~> 3.1.pre.2023.1.1"/)
+ expect(the_bundle).to include_gems "lemur 3.1.1.pre.2023.1.1"
+ end
end
describe "with --version" do
@@ -63,7 +71,7 @@ RSpec.describe "bundle add" do
it "adds multiple version constraints when specified" do
requirements = ["< 3.0", "> 1.0"]
bundle "add 'foo' --version='#{requirements.join(", ")}'"
- expect(bundled_app_gemfile.read).to match(/gem "foo", #{Gem::Requirement.new(requirements).as_list.map(&:dump).join(', ')}/)
+ expect(bundled_app_gemfile.read).to match(/gem "foo", #{Gem::Requirement.new(requirements).as_list.map(&:dump).join(", ")}/)
expect(the_bundle).to include_gems "foo 2.0"
end
end
@@ -123,7 +131,7 @@ RSpec.describe "bundle add" do
describe "with --git and --branch" do
before do
- update_git "foo", "2.0", :branch => "test"
+ update_git "foo", "2.0", branch: "test"
end
it "adds dependency with specified git source and branch" do
@@ -147,7 +155,7 @@ RSpec.describe "bundle add" do
it "adds dependency with specified github source", :realworld do
bundle "add rake --github=ruby/rake"
- expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.0", :github => "ruby\/rake"})
+ expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.\d+", :github => "ruby\/rake"})
end
end
@@ -155,7 +163,7 @@ RSpec.describe "bundle add" do
it "adds dependency with specified github source and branch", :realworld do
bundle "add rake --github=ruby/rake --branch=master"
- expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.0", :github => "ruby\/rake", :branch => "master"})
+ expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.\d+", :github => "ruby\/rake", :branch => "master"})
end
end
@@ -163,7 +171,62 @@ RSpec.describe "bundle add" do
it "adds dependency with specified github source and ref", :realworld do
bundle "add rake --github=ruby/rake --ref=5c60da8"
- expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.0", :github => "ruby\/rake", :ref => "5c60da8"})
+ expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.\d+", :github => "ruby\/rake", :ref => "5c60da8"})
+ end
+ end
+
+ describe "with --git and --glob" do
+ it "adds dependency with specified git source" do
+ bundle "add foo --git=#{lib_path("foo-2.0")} --glob='./*.gemspec'"
+
+ expect(bundled_app_gemfile.read).to match(%r{gem "foo", "~> 2.0", :git => "#{lib_path("foo-2.0")}", :glob => "\./\*\.gemspec"})
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
+ describe "with --git and --branch and --glob" do
+ before do
+ update_git "foo", "2.0", branch: "test"
+ end
+
+ it "adds dependency with specified git source and branch" do
+ bundle "add foo --git=#{lib_path("foo-2.0")} --branch=test --glob='./*.gemspec'"
+
+ expect(bundled_app_gemfile.read).to match(%r{gem "foo", "~> 2.0", :git => "#{lib_path("foo-2.0")}", :branch => "test", :glob => "\./\*\.gemspec"})
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
+ describe "with --git and --ref and --glob" do
+ it "adds dependency with specified git source and branch" do
+ bundle "add foo --git=#{lib_path("foo-2.0")} --ref=#{revision_for(lib_path("foo-2.0"))} --glob='./*.gemspec'"
+
+ expect(bundled_app_gemfile.read).to match(%r{gem "foo", "~> 2\.0", :git => "#{lib_path("foo-2.0")}", :ref => "#{revision_for(lib_path("foo-2.0"))}", :glob => "\./\*\.gemspec"})
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
+ describe "with --github and --glob" do
+ it "adds dependency with specified github source", :realworld do
+ bundle "add rake --github=ruby/rake --glob='./*.gemspec'"
+
+ expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.\d+", :github => "ruby\/rake", :glob => "\.\/\*\.gemspec"})
+ end
+ end
+
+ describe "with --github and --branch --and glob" do
+ it "adds dependency with specified github source and branch", :realworld do
+ bundle "add rake --github=ruby/rake --branch=master --glob='./*.gemspec'"
+
+ expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.\d+", :github => "ruby\/rake", :branch => "master", :glob => "\.\/\*\.gemspec"})
+ end
+ end
+
+ describe "with --github and --ref and --glob" do
+ it "adds dependency with specified github source and ref", :realworld do
+ bundle "add rake --github=ruby/rake --ref=5c60da8 --glob='./*.gemspec'"
+
+ expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.\d+", :github => "ruby\/rake", :ref => "5c60da8", :glob => "\.\/\*\.gemspec"})
end
end
@@ -183,24 +246,24 @@ RSpec.describe "bundle add" do
end
it "shows error message when version is not formatted correctly" do
- bundle "add 'foo' -v='~>1 . 0'", :raise_on_error => false
+ bundle "add 'foo' -v='~>1 . 0'", raise_on_error: false
expect(err).to match("Invalid gem requirement pattern '~>1 . 0'")
end
it "shows error message when gem cannot be found" do
bundle "config set force_ruby_platform true"
- bundle "add 'werk_it'", :raise_on_error => false
+ bundle "add 'werk_it'", raise_on_error: false
expect(err).to match("Could not find gem 'werk_it' in")
- bundle "add 'werk_it' -s='#{file_uri_for(gem_repo2)}'", :raise_on_error => false
+ bundle "add 'werk_it' -s='#{file_uri_for(gem_repo2)}'", raise_on_error: false
expect(err).to match("Could not find gem 'werk_it' in rubygems repository")
end
it "shows error message when source cannot be reached" do
- bundle "add 'baz' --source='http://badhostasdf'", :raise_on_error => false
+ bundle "add 'baz' --source='http://badhostasdf'", raise_on_error: false
expect(err).to include("Could not reach host badhostasdf. Check your network connection and try again.")
- bundle "add 'baz' --source='file://does/not/exist'", :raise_on_error => false
+ bundle "add 'baz' --source='file://does/not/exist'", raise_on_error: false
expect(err).to include("Could not fetch specs from file://does/not/exist/")
end
@@ -230,7 +293,7 @@ RSpec.describe "bundle add" do
describe "with --optimistic and --strict" do
it "throws error" do
- bundle "add 'foo' --strict --optimistic", :raise_on_error => false
+ bundle "add 'foo' --strict --optimistic", raise_on_error: false
expect(err).to include("You can not specify `--strict` and `--optimistic` at the same time")
end
@@ -245,7 +308,7 @@ RSpec.describe "bundle add" do
end
it "throws error if any of the specified gems are present in the gemfile with different version" do
- bundle "add weakling bar", :raise_on_error => false
+ bundle "add weakling bar", raise_on_error: false
expect(err).to include("You cannot specify the same gem twice with different version requirements")
expect(err).to include("You specified: weakling (~> 0.0.1) and weakling (>= 0).")
@@ -259,7 +322,7 @@ RSpec.describe "bundle add" do
gem "rack", "1.0"
G
- bundle "add 'rack' --version=1.1", :raise_on_error => false
+ bundle "add 'rack' --version=1.1", raise_on_error: false
expect(err).to include("You cannot specify the same gem twice with different version requirements")
expect(err).to include("If you want to update the gem version, run `bundle update rack`. You may also need to change the version requirement specified in the Gemfile if it's too restrictive")
@@ -271,7 +334,7 @@ RSpec.describe "bundle add" do
gem "rack", "1.0"
G
- bundle "add 'rack'", :raise_on_error => false
+ bundle "add 'rack'", raise_on_error: false
expect(err).to include("Gem already added.")
expect(err).to include("You cannot specify the same gem twice with different version requirements")
@@ -286,7 +349,7 @@ RSpec.describe "bundle add" do
gem "rack"
G
- bundle "add 'rack' --version=1.1", :raise_on_error => false
+ bundle "add 'rack' --version=1.1", raise_on_error: false
expect(err).to include("You cannot specify the same gem twice with different version requirements")
expect(err).to include("If you want to update the gem version, run `bundle update rack`.")
diff --git a/spec/bundler/commands/binstubs_spec.rb b/spec/bundler/commands/binstubs_spec.rb
index bfbef58181..6c3dc7bb2d 100644
--- a/spec/bundler/commands/binstubs_spec.rb
+++ b/spec/bundler/commands/binstubs_spec.rb
@@ -45,7 +45,7 @@ RSpec.describe "bundle binstubs <gem>" do
gem "rails"
G
- bundle :binstubs, :all => true
+ bundle :binstubs, all: true
expect(bundled_app("bin/rails")).to exist
expect(bundled_app("bin/rake")).to exist
@@ -69,7 +69,7 @@ RSpec.describe "bundle binstubs <gem>" do
gem "rack"
G
- bundle "binstubs", :raise_on_error => false
+ bundle "binstubs", raise_on_error: false
expect(exitstatus).to eq(1)
expect(err).to include("`bundle binstubs` needs at least one gem to run.")
end
@@ -80,7 +80,7 @@ RSpec.describe "bundle binstubs <gem>" do
gem "rack"
G
- bundle "binstubs rack", :all => true, :raise_on_error => false
+ bundle "binstubs rack", all: true, raise_on_error: false
expect(last_command).to be_failure
expect(err).to include("Cannot specify --all with specific gems")
end
@@ -98,7 +98,7 @@ RSpec.describe "bundle binstubs <gem>" do
file.print "OMG"
end
- sys_exec "bin/rackup", :raise_on_error => false
+ sys_exec "bin/rackup", raise_on_error: false
expect(err).to include("was not generated by Bundler")
end
@@ -132,13 +132,13 @@ RSpec.describe "bundle binstubs <gem>" do
let(:system_bundler_version) { Bundler::VERSION }
it "runs bundler" do
- sys_exec "bin/bundle install", :env => { "DEBUG" => "1" }
+ sys_exec "bin/bundle install", env: { "DEBUG" => "1" }
expect(out).to include %(Using bundler #{system_bundler_version}\n)
end
context "when BUNDLER_VERSION is set" do
it "runs the correct version of bundler" do
- sys_exec "bin/bundle install", :env => { "BUNDLER_VERSION" => "999.999.999" }, :raise_on_error => false
+ sys_exec "bin/bundle install", env: { "BUNDLER_VERSION" => "999.999.999" }, raise_on_error: false
expect(exitstatus).to eq(42)
expect(err).to include("Activating bundler (999.999.999) failed:").
and include("To install the version of bundler this project requires, run `gem install bundler -v '999.999.999'`")
@@ -147,7 +147,7 @@ RSpec.describe "bundle binstubs <gem>" do
it "runs the correct version of bundler even if a higher version is installed" do
system_gems "bundler-999.999.998", "bundler-999.999.999"
- sys_exec "bin/bundle install", :env => { "BUNDLER_VERSION" => "999.999.998", "DEBUG" => "1" }, :raise_on_error => false
+ sys_exec "bin/bundle install", env: { "BUNDLER_VERSION" => "999.999.998", "DEBUG" => "1" }, raise_on_error: false
expect(out).to include %(Using bundler 999.999.998\n)
end
end
@@ -159,7 +159,7 @@ RSpec.describe "bundle binstubs <gem>" do
end
it "runs the correct version of bundler" do
- sys_exec "bin/bundle install", :raise_on_error => false
+ sys_exec "bin/bundle install", raise_on_error: false
expect(exitstatus).to eq(42)
expect(err).to include("Activating bundler (~> 999.999) failed:").
and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 999.999'`")
@@ -173,7 +173,7 @@ RSpec.describe "bundle binstubs <gem>" do
end
it "runs the correct version of bundler" do
- sys_exec "bin/bundle install", :env => { "BUNDLE_GEMFILE" => "gems.rb" }, :raise_on_error => false
+ sys_exec "bin/bundle install", env: { "BUNDLE_GEMFILE" => "gems.rb" }, raise_on_error: false
expect(exitstatus).to eq(42)
expect(err).to include("Activating bundler (~> 999.999) failed:").
@@ -189,7 +189,7 @@ RSpec.describe "bundle binstubs <gem>" do
end
it "runs the correct version of bundler" do
- sys_exec "bin/bundle install", :raise_on_error => false
+ sys_exec "bin/bundle install", raise_on_error: false
expect(exitstatus).to eq(42)
expect(err).to include("Activating bundler (~> 44.0) failed:").
and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 44.0'`")
@@ -205,7 +205,7 @@ RSpec.describe "bundle binstubs <gem>" do
end
it "runs the correct version of bundler" do
- sys_exec "bin/bundle install", :env => { "BUNDLE_GEMFILE" => "gems.rb" }, :raise_on_error => false
+ sys_exec "bin/bundle install", env: { "BUNDLE_GEMFILE" => "gems.rb" }, raise_on_error: false
expect(exitstatus).to eq(42)
expect(err).to include("Activating bundler (~> 44.0) failed:").
and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 44.0'`")
@@ -219,15 +219,15 @@ RSpec.describe "bundle binstubs <gem>" do
lockfile lockfile.gsub(/BUNDLED WITH\n .*$/m, "BUNDLED WITH\n 2.3.0")
end
- it "installs and runs the exact version of bundler", :rubygems => ">= 3.3.0.dev", :realworld => true do
- sys_exec "bin/bundle install --verbose", :artifice => "vcr"
+ it "installs and runs the exact version of bundler", rubygems: ">= 3.3.0.dev", realworld: true do
+ sys_exec "bin/bundle install --verbose", artifice: "vcr"
expect(exitstatus).not_to eq(42)
expect(out).to include("Bundler 2.999.999 is running, but your lockfile was generated with 2.3.0. Installing Bundler 2.3.0 and restarting using that version.")
expect(out).to include("Using bundler 2.3.0")
expect(err).not_to include("Activating bundler (~> 2.3.0) failed:")
end
- it "runs the available version of bundler", :rubygems => "< 3.3.0.dev" do
+ it "runs the available version of bundler", rubygems: "< 3.3.0.dev" do
sys_exec "bin/bundle install --verbose"
expect(exitstatus).not_to eq(42)
expect(out).not_to include("Bundler 2.999.999 is running, but your lockfile was generated with 2.3.0. Installing Bundler 2.3.0 and restarting using that version.")
@@ -244,7 +244,7 @@ RSpec.describe "bundle binstubs <gem>" do
end
it "runs the correct version of bundler when the version is a pre-release" do
- sys_exec "bin/bundle install", :raise_on_error => false
+ sys_exec "bin/bundle install", raise_on_error: false
expect(exitstatus).to eq(42)
expect(err).to include("Activating bundler (~> 2.12.a) failed:").
and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 2.12.a'`")
@@ -256,7 +256,7 @@ RSpec.describe "bundle binstubs <gem>" do
before { lockfile.gsub(system_bundler_version, "1.1.1") }
it "calls through to the latest bundler version", :realworld do
- sys_exec "bin/bundle update --bundler", :env => { "DEBUG" => "1" }
+ sys_exec "bin/bundle update --bundler", env: { "DEBUG" => "1" }
using_bundler_line = /Using bundler ([\w\.]+)\n/.match(out)
expect(using_bundler_line).to_not be_nil
latest_version = using_bundler_line[1]
@@ -264,7 +264,7 @@ RSpec.describe "bundle binstubs <gem>" do
end
it "calls through to the explicit bundler version" do
- sys_exec "bin/bundle update --bundler=999.999.999", :raise_on_error => false
+ sys_exec "bin/bundle update --bundler=999.999.999", raise_on_error: false
expect(exitstatus).to eq(42)
expect(err).to include("Activating bundler (999.999.999) failed:").
and include("To install the version of bundler this project requires, run `gem install bundler -v '999.999.999'`")
@@ -274,7 +274,7 @@ RSpec.describe "bundle binstubs <gem>" do
context "without a lockfile" do
it "falls back to the latest installed bundler" do
FileUtils.rm bundled_app_lock
- sys_exec "bin/bundle install", :env => { "DEBUG" => "1" }
+ sys_exec "bin/bundle install", env: { "DEBUG" => "1" }
expect(out).to include "Using bundler #{system_bundler_version}\n"
end
end
@@ -289,7 +289,7 @@ RSpec.describe "bundle binstubs <gem>" do
before { lockfile lockfile.gsub(Bundler::VERSION, "999.999.999") }
it "attempts to load that version" do
- sys_exec bundled_app("bin/rackup").to_s, :raise_on_error => false
+ sys_exec bundled_app("bin/rackup").to_s, raise_on_error: false
expect(exitstatus).to eq(42)
expect(err).to include("Activating bundler (~> 999.999) failed:").
and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 999.999'`")
@@ -301,7 +301,7 @@ RSpec.describe "bundle binstubs <gem>" do
it "installs binstubs from git gems" do
FileUtils.mkdir_p(lib_path("foo/bin"))
FileUtils.touch(lib_path("foo/bin/foo"))
- build_git "foo", "1.0", :path => lib_path("foo") do |s|
+ build_git "foo", "1.0", path: lib_path("foo") do |s|
s.executables = %w[foo]
end
install_gemfile <<-G
@@ -317,7 +317,7 @@ RSpec.describe "bundle binstubs <gem>" do
it "installs binstubs from path gems" do
FileUtils.mkdir_p(lib_path("foo/bin"))
FileUtils.touch(lib_path("foo/bin/foo"))
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
s.executables = %w[foo]
end
install_gemfile <<-G
@@ -362,7 +362,7 @@ RSpec.describe "bundle binstubs <gem>" do
source "#{file_uri_for(gem_repo1)}"
G
- bundle "binstubs doesnt_exist", :raise_on_error => false
+ bundle "binstubs doesnt_exist", raise_on_error: false
expect(exitstatus).to eq(7)
expect(err).to include("Could not find gem 'doesnt_exist'.")
@@ -381,14 +381,14 @@ RSpec.describe "bundle binstubs <gem>" do
expect(bundled_app("exec/rackup")).to exist
end
- it "setting is saved for bundle install", :bundler => "< 3" do
+ it "setting is saved for bundle install", bundler: "< 3" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
gem "rails"
G
- bundle "binstubs rack", :path => "exec"
+ bundle "binstubs rack", path: "exec"
bundle :install
expect(bundled_app("exec/rails")).to exist
@@ -549,7 +549,7 @@ RSpec.describe "bundle binstubs <gem>" do
G
bundle "config set auto_install 1"
- bundle "binstubs rack", :env => { "BUNDLE_INSTALL" => "1" }
+ bundle "binstubs rack", env: { "BUNDLE_INSTALL" => "1" }
expect(out).not_to include("Installing rack 1.0.0")
end
end
diff --git a/spec/bundler/commands/cache_spec.rb b/spec/bundler/commands/cache_spec.rb
index 790373be1f..70e2c84961 100644
--- a/spec/bundler/commands/cache_spec.rb
+++ b/spec/bundler/commands/cache_spec.rb
@@ -158,7 +158,7 @@ RSpec.describe "bundle cache" do
end
end
- context "with --path", :bundler => "< 3" do
+ context "with --path", bundler: "< 3" do
it "sets root directory for gems" do
gemfile <<-D
source "#{file_uri_for(gem_repo1)}"
@@ -211,7 +211,7 @@ RSpec.describe "bundle cache" do
end
context "with --all-platforms" do
- it "puts the gems in vendor/cache even for other rubies", :bundler => ">= 2.4.0" do
+ it "puts the gems in vendor/cache even for other rubies", bundler: ">= 2.4.0" do
gemfile <<-D
source "#{file_uri_for(gem_repo1)}"
gem 'rack', :platforms => [:ruby_20, :windows_20]
@@ -221,7 +221,7 @@ RSpec.describe "bundle cache" do
expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
end
- it "puts the gems in vendor/cache even for legacy windows rubies", :bundler => ">= 2.4.0" do
+ it "puts the gems in vendor/cache even for legacy windows rubies", bundler: ">= 2.4.0" do
gemfile <<-D
source "#{file_uri_for(gem_repo1)}"
gem 'rack', :platforms => [:ruby_20, :x64_mingw_20]
@@ -272,7 +272,7 @@ RSpec.describe "bundle cache" do
end
G
- bundle :lock, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s }
+ bundle :lock, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s }
bundle :cache, "all-platforms" => true, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s }
expect(bundled_app("vendor/cache/weakling-0.0.3.gem")).to exist
end
@@ -289,7 +289,7 @@ RSpec.describe "bundle cache" do
subject do
bundle "config set --local frozen true"
- bundle :cache, :raise_on_error => false
+ bundle :cache, raise_on_error: false
end
it "tries to install with frozen" do
@@ -301,7 +301,7 @@ RSpec.describe "bundle cache" do
G
subject
expect(exitstatus).to eq(16)
- expect(err).to include("deployment mode")
+ expect(err).to include("frozen mode")
expect(err).to include("You have added to the Gemfile")
expect(err).to include("* rack-obama")
bundle "env"
@@ -393,7 +393,7 @@ RSpec.describe "bundle install with gem sources" do
G
bundle :cache
- build_gem "rack", "1.0.0", :path => bundled_app("vendor/cache") do |s|
+ build_gem "rack", "1.0.0", path: bundled_app("vendor/cache") do |s|
s.write "lib/rack.rb", "raise 'omg'"
end
diff --git a/spec/bundler/commands/check_spec.rb b/spec/bundler/commands/check_spec.rb
index 99a858e9e9..02f9bb5b7a 100644
--- a/spec/bundler/commands/check_spec.rb
+++ b/spec/bundler/commands/check_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe "bundle check" do
gem "rails"
G
- bundle "check --gemfile bundled_app/Gemfile", :dir => tmp
+ bundle "check --gemfile bundled_app/Gemfile", dir: tmp
expect(out).to include("The Gemfile's dependencies are satisfied")
end
@@ -55,7 +55,7 @@ RSpec.describe "bundle check" do
gem "rails"
G
- bundle :check, :raise_on_error => false
+ bundle :check, raise_on_error: false
expect(err).to include("Bundler can't satisfy your Gemfile's dependencies.")
end
@@ -65,7 +65,7 @@ RSpec.describe "bundle check" do
gem "rails"
G
- bundle :check, :raise_on_error => false
+ bundle :check, raise_on_error: false
expect(exitstatus).to be > 0
expect(err).to include("Bundler can't satisfy your Gemfile's dependencies.")
end
@@ -88,11 +88,11 @@ RSpec.describe "bundle check" do
gem "rails_pinned_to_old_activesupport"
G
- bundle :check, :raise_on_error => false
+ bundle :check, raise_on_error: false
expect(err).to include("Bundler can't satisfy your Gemfile's dependencies.")
end
- it "remembers --without option from install", :bundler => "< 3" do
+ it "remembers --without option from install", bundler: "< 3" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
group :foo do
@@ -132,7 +132,7 @@ RSpec.describe "bundle check" do
gem "rack"
G
- bundle "check", :raise_on_error => false
+ bundle "check", raise_on_error: false
expect(err).to include("* rack (1.0.0)")
expect(exitstatus).to eq(1)
end
@@ -146,9 +146,9 @@ RSpec.describe "bundle check" do
bundle "config set --local path vendor/bundle"
bundle :cache
- gem_command "uninstall rack", :env => { "GEM_HOME" => vendored_gems.to_s }
+ gem_command "uninstall rack", env: { "GEM_HOME" => vendored_gems.to_s }
- bundle "check", :raise_on_error => false
+ bundle "check", raise_on_error: false
expect(err).to include("* rack (1.0.0)")
expect(exitstatus).to eq(1)
end
@@ -162,7 +162,7 @@ RSpec.describe "bundle check" do
end
G
- system_gems "rack-1.0.0", :path => default_bundle_path
+ system_gems "rack-1.0.0", path: default_bundle_path
lockfile <<-G
GEM
@@ -193,7 +193,7 @@ RSpec.describe "bundle check" do
end
G
- system_gems "rack-1.0.0", :path => default_bundle_path
+ system_gems "rack-1.0.0", path: default_bundle_path
lockfile <<-G
GEM
@@ -216,13 +216,13 @@ RSpec.describe "bundle check" do
end
it "outputs an error when the default Gemfile is not found" do
- bundle :check, :raise_on_error => false
+ bundle :check, raise_on_error: false
expect(exitstatus).to eq(10)
expect(err).to include("Could not locate Gemfile")
end
it "does not output fatal error message" do
- bundle :check, :raise_on_error => false
+ bundle :check, raise_on_error: false
expect(exitstatus).to eq(10)
expect(err).not_to include("Unfortunately, a fatal error has occurred. ")
end
@@ -237,11 +237,11 @@ RSpec.describe "bundle check" do
bundle "install"
FileUtils.rm(bundled_app_lock)
- bundle :check, :raise_on_error => false
+ bundle :check, raise_on_error: false
expect(last_command).to be_failure
end
- context "--path", :bundler => "< 3" do
+ context "--path", bundler: "< 3" do
context "after installing gems in the proper directory" do
before do
gemfile <<-G
@@ -271,7 +271,7 @@ RSpec.describe "bundle check" do
gem "rails"
G
- bundle "check --path vendor/bundle", :raise_on_error => false
+ bundle "check --path vendor/bundle", raise_on_error: false
end
it "returns false" do
@@ -298,7 +298,7 @@ RSpec.describe "bundle check" do
it "shows what is missing with the current Gemfile if it is not satisfied" do
simulate_new_machine
- bundle :check, :raise_on_error => false
+ bundle :check, raise_on_error: false
expect(err).to match(/The following gems are missing/)
expect(err).to include("* rack (1.0")
end
@@ -326,7 +326,7 @@ RSpec.describe "bundle check" do
end
it "shows what is missing with the current Gemfile without duplications" do
- bundle :check, :raise_on_error => false
+ bundle :check, raise_on_error: false
expect(err).to match(/The following gems are missing/)
expect(err).to include("* rack (1.0").once
end
@@ -362,7 +362,7 @@ RSpec.describe "bundle check" do
end
it "shows what is missing with the current Gemfile without duplications" do
- bundle :check, :raise_on_error => false
+ bundle :check, raise_on_error: false
expect(err).to match(/The following gems are missing/)
expect(err).to include("* rack (1.0").once
end
@@ -379,7 +379,7 @@ RSpec.describe "bundle check" do
end
it "returns success when the Gemfile is satisfied" do
- system_gems "rack-1.0.0", :path => default_bundle_path
+ system_gems "rack-1.0.0", path: default_bundle_path
bundle :check
expect(out).to include("The Gemfile's dependencies are satisfied")
end
@@ -404,8 +404,14 @@ RSpec.describe "bundle check" do
end
it "returns success when the Gemfile is satisfied and generates a correct lockfile" do
- system_gems "depends_on_rack-1.0", "rack-1.0", :gem_repo => gem_repo4, :path => default_bundle_path
+ system_gems "depends_on_rack-1.0", "rack-1.0", gem_repo: gem_repo4, path: default_bundle_path
bundle :check
+
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "depends_on_rack", "1.0"
+ c.no_checksum "rack", "1.0"
+ end
+
expect(out).to include("The Gemfile's dependencies are satisfied")
expect(lockfile).to eq <<~L
GEM
@@ -424,7 +430,7 @@ RSpec.describe "bundle check" do
DEPENDENCIES
depends_on_rack!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -441,7 +447,7 @@ RSpec.describe "bundle check" do
build_gem "dex-dispatch-engine"
end
- build_lib("bundle-check-issue", :path => tmp.join("bundle-check-issue")) do |s|
+ build_lib("bundle-check-issue", path: tmp.join("bundle-check-issue")) do |s|
s.write "Gemfile", <<-G
source "https://localgemserver.test"
@@ -455,14 +461,20 @@ RSpec.describe "bundle check" do
s.add_dependency "awesome_print"
end
- bundle "install", :artifice => "compact_index_extra", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }, :dir => tmp.join("bundle-check-issue")
+ bundle "install", artifice: "compact_index_extra", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }, dir: tmp.join("bundle-check-issue")
end
it "does not corrupt lockfile when changing version" do
version_file = tmp.join("bundle-check-issue/bundle-check-issue.gemspec")
File.write(version_file, File.read(version_file).gsub(/s\.version = .+/, "s.version = '9999'"))
- bundle "check --verbose", :dir => tmp.join("bundle-check-issue")
+ bundle "check --verbose", dir: tmp.join("bundle-check-issue")
+
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo4, "awesome_print", "1.0"
+ c.no_checksum "bundle-check-issue", "9999"
+ c.checksum gem_repo2, "dex-dispatch-engine", "1.0"
+ end
expect(File.read(tmp.join("bundle-check-issue/Gemfile.lock"))).to eq <<~L
PATH
@@ -487,7 +499,7 @@ RSpec.describe "bundle check" do
DEPENDENCIES
bundle-check-issue!
dex-dispatch-engine!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
diff --git a/spec/bundler/commands/clean_spec.rb b/spec/bundler/commands/clean_spec.rb
index 471cd6c354..0b559a87c8 100644
--- a/spec/bundler/commands/clean_spec.rb
+++ b/spec/bundler/commands/clean_spec.rb
@@ -156,7 +156,7 @@ RSpec.describe "bundle clean" do
end
it "removes unused git gems" do
- build_git "foo", :path => lib_path("foo")
+ build_git "foo", path: lib_path("foo")
git_path = lib_path("foo")
revision = revision_for(git_path)
@@ -194,7 +194,7 @@ RSpec.describe "bundle clean" do
end
it "keeps used git gems even if installed to a symlinked location" do
- build_git "foo", :path => lib_path("foo")
+ build_git "foo", path: lib_path("foo")
git_path = lib_path("foo")
revision = revision_for(git_path)
@@ -221,7 +221,7 @@ RSpec.describe "bundle clean" do
end
it "removes old git gems" do
- build_git "foo-bar", :path => lib_path("foo-bar")
+ build_git "foo-bar", path: lib_path("foo-bar")
revision = revision_for(lib_path("foo-bar"))
gemfile <<-G
@@ -236,10 +236,10 @@ RSpec.describe "bundle clean" do
bundle "config set path vendor/bundle"
bundle "install"
- update_git "foo-bar", :path => lib_path("foo-bar")
+ update_git "foo-bar", path: lib_path("foo-bar")
revision2 = revision_for(lib_path("foo-bar"))
- bundle "update", :all => true
+ bundle "update", all: true
bundle :clean
expect(out).to include("Removing foo-bar (#{revision[0..11]})")
@@ -254,8 +254,8 @@ RSpec.describe "bundle clean" do
end
it "does not remove nested gems in a git repo" do
- build_lib "activesupport", "3.0", :path => lib_path("rails/activesupport")
- build_git "rails", "3.0", :path => lib_path("rails") do |s|
+ build_lib "activesupport", "3.0", path: lib_path("rails/activesupport")
+ build_git "rails", "3.0", path: lib_path("rails") do |s|
s.add_dependency "activesupport", "= 3.0"
end
revision = revision_for(lib_path("rails"))
@@ -274,7 +274,7 @@ RSpec.describe "bundle clean" do
end
it "does not remove git sources that are in without groups" do
- build_git "foo", :path => lib_path("foo")
+ build_git "foo", path: lib_path("foo")
git_path = lib_path("foo")
revision = revision_for(git_path)
@@ -326,7 +326,7 @@ RSpec.describe "bundle clean" do
gem "rack", "1.0.0"
G
- bundle :clean, :raise_on_error => false
+ bundle :clean, raise_on_error: false
expect(exitstatus).to eq(15)
expect(err).to include("--force")
@@ -383,7 +383,7 @@ RSpec.describe "bundle clean" do
expect(out).to include("rack (1.0.0)").and include("thin (1.0)")
end
- it "--clean should override the bundle setting on install", :bundler => "< 3" do
+ it "--clean should override the bundle setting on install", bundler: "< 3" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -405,7 +405,7 @@ RSpec.describe "bundle clean" do
should_not_have_gems "thin-1.0"
end
- it "--clean should override the bundle setting on update", :bundler => "< 3" do
+ it "--clean should override the bundle setting on update", bundler: "< 3" do
build_repo2
gemfile <<-G
@@ -421,13 +421,13 @@ RSpec.describe "bundle clean" do
build_gem "foo", "1.0.1"
end
- bundle "update", :all => true
+ bundle "update", all: true
should_have_gems "foo-1.0.1"
should_not_have_gems "foo-1.0"
end
- it "automatically cleans when path has not been set", :bundler => "3" do
+ it "automatically cleans when path has not been set", bundler: "3" do
build_repo2
install_gemfile <<-G
@@ -440,7 +440,7 @@ RSpec.describe "bundle clean" do
build_gem "foo", "1.0.1"
end
- bundle "update", :all => true
+ bundle "update", all: true
files = Pathname.glob(bundled_app(".bundle", Bundler.ruby_scope, "*", "*"))
files.map! {|f| f.to_s.sub(bundled_app(".bundle", Bundler.ruby_scope).to_s, "") }
@@ -486,7 +486,7 @@ RSpec.describe "bundle clean" do
build_gem "foo", "1.0.1"
end
- bundle :update, :all => true
+ bundle :update, all: true
should_have_gems "foo-1.0", "foo-1.0.1"
end
@@ -505,7 +505,7 @@ RSpec.describe "bundle clean" do
update_repo2 do
build_gem "foo", "1.0.1"
end
- bundle :update, :all => true
+ bundle :update, all: true
gem_command :list
expect(out).to include("foo (1.0.1, 1.0)")
@@ -560,7 +560,7 @@ RSpec.describe "bundle clean" do
FileUtils.chmod(0o500, system_cache_path)
- bundle :clean, :force => true, :raise_on_error => false
+ bundle :clean, force: true, raise_on_error: false
expect(err).to include(system_gem_path.to_s)
expect(err).to include("grant write permissions")
@@ -626,15 +626,11 @@ RSpec.describe "bundle clean" do
end
it "when using --force, it doesn't remove default gem binaries", :realworld do
- skip "does not work on old rubies because the realworld gems that need to be installed don't support them" if RUBY_VERSION < "2.7.0"
-
- skip "does not work on rubygems versions where `--install_dir` doesn't respect --default" unless Gem::Installer.for_spec(loaded_gemspec, :install_dir => "/foo").default_spec_file == "/foo/specifications/default/bundler-#{Bundler::VERSION}.gemspec" # Since rubygems 3.2.0.rc.2
-
- default_irb_version = ruby "gem 'irb', '< 999999'; require 'irb'; puts IRB::VERSION", :raise_on_error => false
+ default_irb_version = ruby "gem 'irb', '< 999999'; require 'irb'; puts IRB::VERSION", raise_on_error: false
skip "irb isn't a default gem" if default_irb_version.empty?
# simulate executable for default gem
- build_gem "irb", default_irb_version, :to_system => true, :default => true do |s|
+ build_gem "irb", default_irb_version, to_system: true, default: true do |s|
s.executables = "irb"
end
@@ -644,7 +640,7 @@ RSpec.describe "bundle clean" do
source "#{file_uri_for(gem_repo2)}"
G
- bundle "clean --force", :env => { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s }
+ bundle "clean --force", env: { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s }
expect(out).not_to include("Removing irb")
end
@@ -905,7 +901,7 @@ RSpec.describe "bundle clean" do
bundle :lock
bundle "config set without development"
bundle "config set path vendor/bundle"
- bundle "install"
+ bundle "install", verbose: true
bundle :clean
very_simple_binary_extensions_dir =
diff --git a/spec/bundler/commands/config_spec.rb b/spec/bundler/commands/config_spec.rb
index ede93f99eb..547fd2d869 100644
--- a/spec/bundler/commands/config_spec.rb
+++ b/spec/bundler/commands/config_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe ".bundle/config" do
context "with env overwrite" do
it "prints config with env" do
- bundle "config list --parseable", :env => { "BUNDLE_FOO" => "bar3" }
+ bundle "config list --parseable", env: { "BUNDLE_FOO" => "bar3" }
expect(out).to include("foo=bar3")
end
end
@@ -64,11 +64,11 @@ RSpec.describe ".bundle/config" do
ENV["BUNDLE_APP_CONFIG"] = "../foo"
bundle "config set --local path vendor/bundle"
- bundle "install", :dir => bundled_app("omg")
+ bundle "install", dir: bundled_app("omg")
expect(bundled_app(".bundle")).not_to exist
expect(bundled_app("../foo/config")).to exist
- expect(the_bundle).to include_gems "rack 1.0.0", :dir => bundled_app("omg")
+ expect(the_bundle).to include_gems "rack 1.0.0", dir: bundled_app("omg")
end
end
@@ -96,8 +96,8 @@ RSpec.describe ".bundle/config" do
end
it "can be configured through BUNDLE_USER_CONFIG" do
- bundle "config set path vendor", :env => { "BUNDLE_USER_CONFIG" => bundle_user_config }
- bundle "config get path", :env => { "BUNDLE_USER_CONFIG" => bundle_user_config }
+ bundle "config set path vendor", env: { "BUNDLE_USER_CONFIG" => bundle_user_config }
+ bundle "config get path", env: { "BUNDLE_USER_CONFIG" => bundle_user_config }
expect(out).to include("Set for the current user (#{bundle_user_config}): \"vendor\"")
end
@@ -105,8 +105,8 @@ RSpec.describe ".bundle/config" do
let(:bundle_user_home) { bundled_app(".bundle").to_s }
it "uses the right location" do
- bundle "config set path vendor", :env => { "BUNDLE_USER_HOME" => bundle_user_home }
- bundle "config get path", :env => { "BUNDLE_USER_HOME" => bundle_user_home }
+ bundle "config set path vendor", env: { "BUNDLE_USER_HOME" => bundle_user_home }
+ bundle "config get path", env: { "BUNDLE_USER_HOME" => bundle_user_home }
expect(out).to include("Set for the current user (#{bundle_user_home}/config): \"vendor\"")
end
end
@@ -443,7 +443,7 @@ E
expect(err).to be_empty
ruby(<<~RUBY)
- require "#{entrypoint}"
+ require "bundler"
print Bundler.settings.mirror_for("https://rails-assets.org")
RUBY
expect(out).to eq("https://rails-assets.org/")
@@ -451,7 +451,7 @@ E
bundle "config set mirror.all http://localhost:9293"
ruby(<<~RUBY)
- require "#{entrypoint}"
+ require "bundler"
print Bundler.settings.mirror_for("https://rails-assets.org")
RUBY
expect(out).to eq("http://localhost:9293/")
@@ -461,26 +461,26 @@ E
describe "subcommands" do
it "list" do
- bundle "config list", :env => { "BUNDLE_FOO" => "bar" }
+ bundle "config list", env: { "BUNDLE_FOO" => "bar" }
expect(out).to eq "Settings are listed in order of priority. The top value will be used.\nfoo\nSet via BUNDLE_FOO: \"bar\""
- bundle "config list", :env => { "BUNDLE_FOO" => "bar" }, :parseable => true
+ bundle "config list", env: { "BUNDLE_FOO" => "bar" }, parseable: true
expect(out).to eq "foo=bar"
end
it "list with credentials" do
- bundle "config list", :env => { "BUNDLE_GEMS__MYSERVER__COM" => "user:password" }
+ bundle "config list", env: { "BUNDLE_GEMS__MYSERVER__COM" => "user:password" }
expect(out).to eq "Settings are listed in order of priority. The top value will be used.\ngems.myserver.com\nSet via BUNDLE_GEMS__MYSERVER__COM: \"user:[REDACTED]\""
- bundle "config list", :parseable => true, :env => { "BUNDLE_GEMS__MYSERVER__COM" => "user:password" }
+ bundle "config list", parseable: true, env: { "BUNDLE_GEMS__MYSERVER__COM" => "user:password" }
expect(out).to eq "gems.myserver.com=user:password"
end
it "list with API token credentials" do
- bundle "config list", :env => { "BUNDLE_GEMS__MYSERVER__COM" => "api_token:x-oauth-basic" }
+ bundle "config list", env: { "BUNDLE_GEMS__MYSERVER__COM" => "api_token:x-oauth-basic" }
expect(out).to eq "Settings are listed in order of priority. The top value will be used.\ngems.myserver.com\nSet via BUNDLE_GEMS__MYSERVER__COM: \"[REDACTED]:x-oauth-basic\""
- bundle "config list", :parseable => true, :env => { "BUNDLE_GEMS__MYSERVER__COM" => "api_token:x-oauth-basic" }
+ bundle "config list", parseable: true, env: { "BUNDLE_GEMS__MYSERVER__COM" => "api_token:x-oauth-basic" }
expect(out).to eq "gems.myserver.com=api_token:x-oauth-basic"
end
@@ -515,7 +515,7 @@ E
bundle "config set --local foo 4.1"
expect(out).to eq "You are replacing the current local value of foo, which is currently \"4\""
- bundle "config set --global --local foo 5", :raise_on_error => false
+ bundle "config set --global --local foo 5", raise_on_error: false
expect(last_command).to be_failure
expect(err).to eq "The options global and local were specified. Please only use one of the switches at a time."
end
@@ -555,7 +555,7 @@ E
expect(out).to eq ""
expect(bundle("config get foo")).to eq "Settings for `foo` in order of priority. The top value will be used\nYou have not configured a value for `foo`"
- bundle "config unset foo --local --global", :raise_on_error => false
+ bundle "config unset foo --local --global", raise_on_error: false
expect(last_command).to be_failure
expect(err).to eq "The options global and local were specified. Please only use one of the switches at a time."
end
diff --git a/spec/bundler/commands/console_spec.rb b/spec/bundler/commands/console_spec.rb
index aa76096e3d..a41432b88a 100644
--- a/spec/bundler/commands/console_spec.rb
+++ b/spec/bundler/commands/console_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.describe "bundle console", :bundler => "< 3", :readline => true do
+RSpec.describe "bundle console", bundler: "< 3", readline: true do
before :each do
build_repo2 do
# A minimal fake pry console
diff --git a/spec/bundler/commands/doctor_spec.rb b/spec/bundler/commands/doctor_spec.rb
index 1afac00923..666b23a141 100644
--- a/spec/bundler/commands/doctor_spec.rb
+++ b/spec/bundler/commands/doctor_spec.rb
@@ -38,6 +38,11 @@ RSpec.describe "bundle doctor" do
allow(stat).to receive(:uid) { Process.uid }
allow(File).to receive(:writable?).with(unwritable_file) { true }
allow(File).to receive(:readable?).with(unwritable_file) { true }
+
+ # The following lines are for `Gem::PathSupport#initialize`.
+ allow(File).to receive(:exist?).with(Gem.default_dir)
+ allow(File).to receive(:writable?).with(Gem.default_dir)
+ allow(File).to receive(:writable?).with(File.expand_path("..", Gem.default_dir))
end
it "exits with no message if the installed gem has no C extensions" do
@@ -59,7 +64,7 @@ RSpec.describe "bundle doctor" do
expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/rack/rack.bundle"]
expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib"]
allow(Fiddle).to receive(:dlopen).with("/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib").and_raise(Fiddle::DLError)
- expect { doctor.run }.to raise_error(Bundler::ProductionError, strip_whitespace(<<-E).strip), @stdout.string
+ expect { doctor.run }.to raise_error(Bundler::ProductionError, <<~E.strip), @stdout.string
The following gems are missing OS dependencies:
* bundler: /usr/local/opt/icu4c/lib/libicui18n.57.1.dylib
* rack: /usr/local/opt/icu4c/lib/libicui18n.57.1.dylib
diff --git a/spec/bundler/commands/exec_spec.rb b/spec/bundler/commands/exec_spec.rb
index 95db25b358..d59b690d2f 100644
--- a/spec/bundler/commands/exec_spec.rb
+++ b/spec/bundler/commands/exec_spec.rb
@@ -4,7 +4,7 @@ RSpec.describe "bundle exec" do
let(:system_gems_to_install) { %w[rack-1.0.0 rack-0.9.1] }
it "works with --gemfile flag" do
- system_gems(system_gems_to_install, :path => default_bundle_path)
+ system_gems(system_gems_to_install, path: default_bundle_path)
create_file "CustomGemfile", <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -16,7 +16,7 @@ RSpec.describe "bundle exec" do
end
it "activates the correct gem" do
- system_gems(system_gems_to_install, :path => default_bundle_path)
+ system_gems(system_gems_to_install, path: default_bundle_path)
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -28,14 +28,14 @@ RSpec.describe "bundle exec" do
end
it "works and prints no warnings when HOME is not writable" do
- system_gems(system_gems_to_install, :path => default_bundle_path)
+ system_gems(system_gems_to_install, path: default_bundle_path)
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack", "0.9.1"
G
- bundle "exec rackup", :env => { "HOME" => "/" }
+ bundle "exec rackup", env: { "HOME" => "/" }
expect(out).to eq("0.9.1")
expect(err).to be_empty
end
@@ -108,7 +108,7 @@ RSpec.describe "bundle exec" do
2.1.4
L
- bundle "exec bundle cache", :env => { "BUNDLER_VERSION" => Bundler::VERSION }
+ bundle "exec bundle cache", env: { "BUNDLER_VERSION" => Bundler::VERSION }
expect(out).to include("Updating files in vendor/cache")
end
@@ -198,7 +198,7 @@ RSpec.describe "bundle exec" do
gem "rack", "0.9.1"
G
- install_gemfile bundled_app2("Gemfile"), <<-G, :dir => bundled_app2
+ install_gemfile bundled_app2("Gemfile"), <<-G, dir: bundled_app2
source "#{file_uri_for(gem_repo2)}"
gem "rack_two", "1.0.0"
G
@@ -207,12 +207,12 @@ RSpec.describe "bundle exec" do
expect(out).to eq("0.9.1")
- bundle "exec rackup", :dir => bundled_app2
+ bundle "exec rackup", dir: bundled_app2
expect(out).to eq("1.0.0")
end
context "with default gems" do
- let(:default_irb_version) { ruby "gem 'irb', '< 999999'; require 'irb'; puts IRB::VERSION", :raise_on_error => false }
+ let(:default_irb_version) { ruby "gem 'irb', '< 999999'; require 'irb'; puts IRB::VERSION", raise_on_error: false }
context "when not specified in Gemfile" do
before do
@@ -299,7 +299,7 @@ RSpec.describe "bundle exec" do
gem "rack", "0.9.1"
G
- install_gemfile bundled_app2("Gemfile"), <<-G, :dir => bundled_app2
+ install_gemfile bundled_app2("Gemfile"), <<-G, dir: bundled_app2
source "#{file_uri_for(gem_repo2)}"
gem "rack_two", "1.0.0"
G
@@ -344,7 +344,7 @@ RSpec.describe "bundle exec" do
bundle "exec 'echo $RUBYOPT'"
expect(out.split(" ").count(bundler_setup_opt)).to eq(1)
- bundle "exec 'echo $RUBYOPT'", :env => { "RUBYOPT" => rubyopt }
+ bundle "exec 'echo $RUBYOPT'", env: { "RUBYOPT" => rubyopt }
expect(out.split(" ").count(bundler_setup_opt)).to eq(1)
end
@@ -363,7 +363,7 @@ RSpec.describe "bundle exec" do
bundle "exec 'echo $RUBYLIB'"
expect(out).to include(rubylib)
- bundle "exec 'echo $RUBYLIB'", :env => { "RUBYLIB" => rubylib }
+ bundle "exec 'echo $RUBYLIB'", env: { "RUBYLIB" => rubylib }
expect(out).to include(rubylib)
end
@@ -373,7 +373,7 @@ RSpec.describe "bundle exec" do
gem "rack"
G
- bundle "exec foobarbaz", :raise_on_error => false
+ bundle "exec foobarbaz", raise_on_error: false
expect(exitstatus).to eq(127)
expect(err).to include("bundler: command not found: foobarbaz")
expect(err).to include("Install missing gem executables with `bundle install`")
@@ -386,7 +386,7 @@ RSpec.describe "bundle exec" do
G
bundle "exec touch foo"
- bundle "exec ./foo", :raise_on_error => false
+ bundle "exec ./foo", raise_on_error: false
expect(exitstatus).to eq(126)
expect(err).to include("bundler: not executable: ./foo")
end
@@ -397,13 +397,13 @@ RSpec.describe "bundle exec" do
gem "rack"
G
- bundle "exec", :raise_on_error => false
+ bundle "exec", raise_on_error: false
expect(exitstatus).to eq(128)
expect(err).to include("bundler: exec needs a command to run")
end
it "raises a helpful error when exec'ing to something outside of the bundle" do
- system_gems(system_gems_to_install, :path => default_bundle_path)
+ system_gems(system_gems_to_install, path: default_bundle_path)
bundle "config set clean false" # want to keep the rackup binstub
install_gemfile <<-G
@@ -412,7 +412,7 @@ RSpec.describe "bundle exec" do
G
[true, false].each do |l|
bundle "config set disable_exec_load #{l}"
- bundle "exec rackup", :raise_on_error => false
+ bundle "exec rackup", raise_on_error: false
expect(err).to include "can't find executable rackup for gem rack. rack is not currently included in the bundle, perhaps you meant to add it to your Gemfile?"
end
end
@@ -531,7 +531,7 @@ RSpec.describe "bundle exec" do
describe "from gems bundled via :path" do
before(:each) do
- build_lib "fizz", :path => home("fizz") do |s|
+ build_lib "fizz", path: home("fizz") do |s|
s.executables = "fizz"
end
@@ -580,7 +580,7 @@ RSpec.describe "bundle exec" do
describe "from gems bundled via :git with no gemspec" do
before(:each) do
- build_git "fizz_no_gemspec", :gemspec => false do |s|
+ build_git "fizz_no_gemspec", gemspec: false do |s|
s.executables = "fizz_no_gemspec"
end
@@ -615,8 +615,25 @@ RSpec.describe "bundle exec" do
expect(out).to include("Installing foo 1.0")
end
+ it "performs an automatic bundle install with git gems" do
+ build_git "foo" do |s|
+ s.executables = "foo"
+ end
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rack", "0.9.1"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ bundle "config set auto_install 1"
+ bundle "exec foo"
+ expect(out).to include("Fetching rack 0.9.1")
+ expect(out).to include("Fetching #{lib_path("foo-1.0")}")
+ expect(out.lines).to end_with("1.0")
+ end
+
it "loads the correct optparse when `auto_install` is set, and optparse is a dependency" do
- if Gem.ruby_version >= Gem::Version.new("3.0.0") && Gem.rubygems_version < Gem::Version.new("3.3.0.a")
+ if Gem.rubygems_version < Gem::Version.new("3.3.0.a")
skip "optparse is a default gem, and rubygems loads it during install"
end
@@ -630,7 +647,7 @@ RSpec.describe "bundle exec" do
build_gem "optparse", "999.999.999"
end
- system_gems "optparse-999.999.998", :gem_repo => gem_repo4
+ system_gems "optparse-999.999.998", gem_repo: gem_repo4
bundle "config set auto_install 1"
bundle "config set --local path vendor/bundle"
@@ -666,7 +683,7 @@ RSpec.describe "bundle exec" do
gem "foo", :path => "#{lib_path("foo-1.0")}"
G
- bundle "exec irb", :raise_on_error => false
+ bundle "exec irb", raise_on_error: false
expect(err).to match("The gemspec at #{lib_path("foo-1.0").join("foo.gemspec")} is not valid")
expect(err).to match('"TODO" is not a summary')
@@ -689,7 +706,7 @@ RSpec.describe "bundle exec" do
G
bundle "config set path.system true"
bundle "install"
- bundle "exec ruby -e '`bundle -v`; puts $?.success?'", :env => { "BUNDLER_VERSION" => Bundler::VERSION }
+ bundle "exec ruby -e '`bundle -v`; puts $?.success?'", env: { "BUNDLER_VERSION" => Bundler::VERSION }
expect(out).to match("true")
end
end
@@ -709,7 +726,7 @@ RSpec.describe "bundle exec" do
RUBY
before do
- system_gems(system_gems_to_install, :path => default_bundle_path)
+ system_gems(system_gems_to_install, path: default_bundle_path)
bundled_app(path).open("w") {|f| f << executable }
bundled_app(path).chmod(0o755)
@@ -732,7 +749,7 @@ RSpec.describe "bundle exec" do
let(:expected) { [exec, args, rack, process].join("\n") }
let(:expected_err) { "" }
- subject { bundle "exec #{path} arg1 arg2", :raise_on_error => false }
+ subject { bundle "exec #{path} arg1 arg2", raise_on_error: false }
it "runs" do
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
@@ -814,8 +831,7 @@ RSpec.describe "bundle exec" do
let(:executable) { super() << "\nraise 'ERROR'" }
let(:exit_code) { 1 }
let(:expected_err) do
- "bundler: failed to load command: #{path} (#{path})" \
- "\n#{path}:10:in `<top (required)>': ERROR (RuntimeError)"
+ /\Abundler: failed to load command: #{Regexp.quote(path.to_s)} \(#{Regexp.quote(path.to_s)}\)\n#{Regexp.quote(path.to_s)}:10:in [`']<top \(required\)>': ERROR \(RuntimeError\)/
end
it "runs like a normally executed executable" do
@@ -823,7 +839,7 @@ RSpec.describe "bundle exec" do
subject
expect(exitstatus).to eq(exit_code)
- expect(err).to start_with(expected_err)
+ expect(err).to match(expected_err)
expect(out).to eq(expected)
end
end
@@ -857,7 +873,7 @@ RSpec.describe "bundle exec" do
end
end
- context "when Bundler.setup fails", :bundler => "< 3" do
+ context "when Bundler.setup fails", bundler: "< 3" do
before do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -869,7 +885,7 @@ RSpec.describe "bundle exec" do
let(:exit_code) { Bundler::GemNotFound.new.status_code }
let(:expected) { "" }
let(:expected_err) { <<-EOS.strip }
-Could not find gem 'rack (= 2)' in locally installed gems.
+Could not find gem 'rack (= 2)' in cached gems or installed locally.
The source contains the following gems matching 'rack':
* rack-0.9.1
@@ -887,7 +903,7 @@ Run `bundle install` to install missing gems.
end
end
- context "when Bundler.setup fails", :bundler => "3" do
+ context "when Bundler.setup fails", bundler: "3" do
before do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -899,7 +915,7 @@ Run `bundle install` to install missing gems.
let(:exit_code) { Bundler::GemNotFound.new.status_code }
let(:expected) { "" }
let(:expected_err) { <<-EOS.strip }
-Could not find gem 'rack (= 2)' in locally installed gems.
+Could not find gem 'rack (= 2)' in cached gems or installed locally.
The source contains the following gems matching 'rack':
* rack-1.0.0
@@ -1024,7 +1040,7 @@ __FILE__: #{path.to_s.inspect}
end
context "signals being trapped by bundler" do
- let(:executable) { strip_whitespace <<-RUBY }
+ let(:executable) { <<~RUBY }
#{shebang}
begin
Thread.new do
@@ -1051,7 +1067,7 @@ __FILE__: #{path.to_s.inspect}
end
context "signals not being trapped by bunder" do
- let(:executable) { strip_whitespace <<-RUBY }
+ let(:executable) { <<~RUBY }
#{shebang}
signals = #{test_signals.inspect}
@@ -1096,7 +1112,7 @@ __FILE__: #{path.to_s.inspect}
puts `bundle exec echo foo`
RUBY
file.chmod(0o777)
- bundle "exec #{file}", :env => { "PATH" => path }
+ bundle "exec #{file}", env: { "PATH" => path }
expect(out).to eq("foo")
end
end
@@ -1189,7 +1205,7 @@ __FILE__: #{path.to_s.inspect}
context "with a system gem that shadows a default gem" do
let(:openssl_version) { "99.9.9" }
- let(:expected) { ruby "gem 'openssl', '< 999999'; require 'openssl'; puts OpenSSL::VERSION", :artifice => nil, :raise_on_error => false }
+ let(:expected) { ruby "gem 'openssl', '< 999999'; require 'openssl'; puts OpenSSL::VERSION", artifice: nil, raise_on_error: false }
it "only leaves the default gem in the stdlib available" do
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
@@ -1205,7 +1221,7 @@ __FILE__: #{path.to_s.inspect}
end
end
- system_gems("openssl-#{openssl_version}", :gem_repo => gem_repo4)
+ system_gems("openssl-#{openssl_version}", gem_repo: gem_repo4)
file = bundled_app("require_openssl.rb")
create_file(file, <<-RUBY)
@@ -1218,15 +1234,15 @@ __FILE__: #{path.to_s.inspect}
env = { "PATH" => path }
aggregate_failures do
- expect(bundle("exec #{file}", :artifice => nil, :env => env)).to eq(expected)
- expect(bundle("exec bundle exec #{file}", :artifice => nil, :env => env)).to eq(expected)
- expect(bundle("exec ruby #{file}", :artifice => nil, :env => env)).to eq(expected)
- expect(run(file.read, :artifice => nil, :env => env)).to eq(expected)
+ expect(bundle("exec #{file}", artifice: nil, env: env)).to eq(expected)
+ expect(bundle("exec bundle exec #{file}", artifice: nil, env: env)).to eq(expected)
+ expect(bundle("exec ruby #{file}", artifice: nil, env: env)).to eq(expected)
+ expect(run(file.read, artifice: nil, env: env)).to eq(expected)
end
skip "ruby_core has openssl and rubygems in the same folder, and this test needs rubygems require but default openssl not in a directly added entry in $LOAD_PATH" if ruby_core?
# sanity check that we get the newer, custom version without bundler
- sys_exec "#{Gem.ruby} #{file}", :env => env, :raise_on_error => false
+ sys_exec "#{Gem.ruby} #{file}", env: env, raise_on_error: false
expect(err).to include("custom openssl should not be loaded")
end
end
diff --git a/spec/bundler/commands/help_spec.rb b/spec/bundler/commands/help_spec.rb
index 409c49f9e1..0c7031e813 100644
--- a/spec/bundler/commands/help_spec.rb
+++ b/spec/bundler/commands/help_spec.rb
@@ -15,6 +15,13 @@ RSpec.describe "bundle help" do
expect(out).to eq(%(["#{man_dir}/bundle-install.1"]))
end
+ it "prexifes bundle commands with bundle- and resolves aliases when finding the man files" do
+ with_fake_man do
+ bundle "help package"
+ end
+ expect(out).to eq(%(["#{man_dir}/bundle-cache.1"]))
+ end
+
it "simply outputs the human readable file when there is no man on the path" do
with_path_as("") do
bundle "help install"
@@ -71,7 +78,7 @@ RSpec.describe "bundle help" do
it "has helpful output when using --help flag for a non-existent command" do
with_fake_man do
- bundle "instill -h", :raise_on_error => false
+ bundle "instill -h", raise_on_error: false
end
expect(err).to include('Could not find command "instill".')
end
diff --git a/spec/bundler/commands/info_spec.rb b/spec/bundler/commands/info_spec.rb
index 851f50240f..a5a09bc147 100644
--- a/spec/bundler/commands/info_spec.rb
+++ b/spec/bundler/commands/info_spec.rb
@@ -65,7 +65,7 @@ RSpec.describe "bundle info" do
end
it "complains if gem not in bundle" do
- bundle "info missing", :raise_on_error => false
+ bundle "info missing", raise_on_error: false
expect(err).to eq("Could not find gem 'missing'.")
end
@@ -74,16 +74,16 @@ RSpec.describe "bundle info" do
bundle "info rails --path"
- expect(err).to match(/The gem rails has been deleted/i)
- expect(err).to match(default_bundle_path("gems", "rails-2.3.2").to_s)
+ expect(err).to include("The gem rails has been deleted.")
+ expect(err).to include(default_bundle_path("gems", "rails-2.3.2").to_s)
bundle "info rail --path"
- expect(err).to match(/The gem rails has been deleted/i)
- expect(err).to match(default_bundle_path("gems", "rails-2.3.2").to_s)
+ expect(err).to include("The gem rails has been deleted.")
+ expect(err).to include(default_bundle_path("gems", "rails-2.3.2").to_s)
bundle "info rails"
- expect(err).to match(/The gem rails has been deleted/i)
- expect(err).to match(default_bundle_path("gems", "rails-2.3.2").to_s)
+ expect(err).to include("The gem rails has been deleted.")
+ expect(err).to include(default_bundle_path("gems", "rails-2.3.2").to_s)
end
context "given a default gem shippped in ruby", :ruby_repo do
@@ -167,7 +167,7 @@ RSpec.describe "bundle info" do
end
it "prints out branch names other than main" do
- update_git "foo", :branch => "omg" do |s|
+ update_git "foo", branch: "omg" do |s|
s.write "lib/foo.rb", "FOO = '1.0.omg'"
end
@revision = revision_for(lib_path("foo-1.0"))[0...6]
@@ -194,7 +194,7 @@ RSpec.describe "bundle info" do
end
it "handles when a version is a '-' prerelease" do
- @git = build_git("foo", "1.0.0-beta.1", :path => lib_path("foo"))
+ @git = build_git("foo", "1.0.0-beta.1", path: lib_path("foo"))
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "foo", "1.0.0-beta.1", :git => "#{lib_path("foo")}"
@@ -228,7 +228,7 @@ RSpec.describe "bundle info" do
invalid_regexp = "[]"
- bundle "info #{invalid_regexp}", :raise_on_error => false
+ bundle "info #{invalid_regexp}", raise_on_error: false
expect(err).to include("Could not find gem '#{invalid_regexp}'.")
end
end
@@ -242,7 +242,7 @@ RSpec.describe "bundle info" do
gem "rails", group: :test
G
- bundle "info rails", :raise_on_error => false
+ bundle "info rails", raise_on_error: false
expect(err).to include("Could not find gem 'rails', because it's in the group 'test', configured to be ignored.")
end
end
diff --git a/spec/bundler/commands/init_spec.rb b/spec/bundler/commands/init_spec.rb
index 6aa3e9edd1..0a1336572a 100644
--- a/spec/bundler/commands/init_spec.rb
+++ b/spec/bundler/commands/init_spec.rb
@@ -24,7 +24,7 @@ RSpec.describe "bundle init" do
it "honours the current process umask when generating from a template" do
FileUtils.mkdir(target_dir)
- bundle :init, :dir => target_dir
+ bundle :init, dir: target_dir
generated_mode = File.stat(File.join(target_dir, "Gemfile")).mode & 0o111
expect(generated_mode).to be_zero
end
@@ -38,11 +38,11 @@ RSpec.describe "bundle init" do
end
it "does not change existing Gemfiles" do
- expect { bundle :init, :raise_on_error => false }.not_to change { File.read(bundled_app_gemfile) }
+ expect { bundle :init, raise_on_error: false }.not_to change { File.read(bundled_app_gemfile) }
end
it "notifies the user that an existing Gemfile already exists" do
- bundle :init, :raise_on_error => false
+ bundle :init, raise_on_error: false
expect(err).to include("Gemfile already exists")
end
end
@@ -55,7 +55,7 @@ RSpec.describe "bundle init" do
FileUtils.mkdir bundled_app(subdir)
- bundle :init, :dir => bundled_app(subdir)
+ bundle :init, dir: bundled_app(subdir)
expect(out).to include("Writing new Gemfile")
expect(bundled_app("#{subdir}/Gemfile")).to be_file
@@ -71,7 +71,7 @@ RSpec.describe "bundle init" do
mode = File.stat(bundled_app(subdir)).mode ^ 0o222
FileUtils.chmod mode, bundled_app(subdir)
- bundle :init, :dir => bundled_app(subdir), :raise_on_error => false
+ bundle :init, dir: bundled_app(subdir), raise_on_error: false
expect(err).to include("directory is not writable")
expect(Dir[bundled_app("#{subdir}/*")]).to be_empty
@@ -92,7 +92,7 @@ RSpec.describe "bundle init" do
S
end
- bundle :init, :gemspec => spec_file
+ bundle :init, gemspec: spec_file
gemfile = bundled_app_gemfile.read
expect(gemfile).to match(%r{source 'https://rubygems.org'})
@@ -112,7 +112,7 @@ RSpec.describe "bundle init" do
S
end
- bundle :init, :gemspec => spec_file, :raise_on_error => false
+ bundle :init, gemspec: spec_file, raise_on_error: false
expect(err).to include("There was an error while loading `test.gemspec`")
end
end
@@ -135,11 +135,11 @@ RSpec.describe "bundle init" do
end
it "does not change existing Gemfiles" do
- expect { bundle :init, :raise_on_error => false }.not_to change { File.read(bundled_app("gems.rb")) }
+ expect { bundle :init, raise_on_error: false }.not_to change { File.read(bundled_app("gems.rb")) }
end
it "notifies the user that an existing gems.rb already exists" do
- bundle :init, :raise_on_error => false
+ bundle :init, raise_on_error: false
expect(err).to include("gems.rb already exists")
end
end
@@ -152,7 +152,7 @@ RSpec.describe "bundle init" do
FileUtils.mkdir bundled_app(subdir)
- bundle :init, :dir => bundled_app(subdir)
+ bundle :init, dir: bundled_app(subdir)
expect(out).to include("Writing new gems.rb")
expect(bundled_app("#{subdir}/gems.rb")).to be_file
@@ -175,7 +175,7 @@ RSpec.describe "bundle init" do
end
it "should generate from an existing gemspec" do
- bundle :init, :gemspec => spec_file
+ bundle :init, gemspec: spec_file
gemfile = bundled_app("gems.rb").read
expect(gemfile).to match(%r{source 'https://rubygems.org'})
@@ -185,7 +185,7 @@ RSpec.describe "bundle init" do
end
it "prints message to user" do
- bundle :init, :gemspec => spec_file
+ bundle :init, gemspec: spec_file
expect(out).to include("Writing new gems.rb")
end
@@ -196,7 +196,7 @@ RSpec.describe "bundle init" do
it "should use the --gemfile value to name the gemfile" do
custom_gemfile_name = "NiceGemfileName"
- bundle :init, :gemfile => custom_gemfile_name
+ bundle :init, gemfile: custom_gemfile_name
expect(out).to include("Writing new #{custom_gemfile_name}")
used_template = File.read("#{source_root}/lib/bundler/templates/Gemfile")
diff --git a/spec/bundler/commands/inject_spec.rb b/spec/bundler/commands/inject_spec.rb
index 92e86bd6cc..255a03c135 100644
--- a/spec/bundler/commands/inject_spec.rb
+++ b/spec/bundler/commands/inject_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.describe "bundle inject", :bundler => "< 3" do
+RSpec.describe "bundle inject", bundler: "< 3" do
before :each do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -36,14 +36,14 @@ RSpec.describe "bundle inject", :bundler => "< 3" do
context "with injected gems already in the Gemfile" do
it "doesn't add existing gems" do
- bundle "inject 'rack' '> 0'", :raise_on_error => false
+ bundle "inject 'rack' '> 0'", raise_on_error: false
expect(err).to match(/cannot specify the same gem twice/i)
end
end
context "incorrect arguments" do
it "fails when more than 2 arguments are passed" do
- bundle "inject gem_name 1 v", :raise_on_error => false
+ bundle "inject gem_name 1 v", raise_on_error: false
expect(err).to eq(<<-E.strip)
ERROR: "bundle inject" was called with arguments ["gem_name", "1", "v"]
Usage: "bundle inject GEM VERSION"
@@ -108,8 +108,8 @@ Usage: "bundle inject GEM VERSION"
source "#{file_uri_for(gem_repo1)}"
gem "rack-obama"
G
- bundle "inject 'rack' '> 0'", :raise_on_error => false
- expect(err).to match(/trying to install in deployment mode after changing/)
+ bundle "inject 'rack' '> 0'", raise_on_error: false
+ expect(err).to match(/the lockfile can't be updated because frozen mode is set/)
expect(bundled_app_lock.read).not_to match(/rack-obama/)
end
diff --git a/spec/bundler/commands/install_spec.rb b/spec/bundler/commands/install_spec.rb
index f572bdf176..1e57414377 100644
--- a/spec/bundler/commands/install_spec.rb
+++ b/spec/bundler/commands/install_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe "bundle install with gem sources" do
end
it "does not make a lockfile if the install fails" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
raise StandardError, "FAIL"
G
@@ -29,7 +29,7 @@ RSpec.describe "bundle install with gem sources" do
expect(bundled_app_lock).to exist
end
- it "does not create ./.bundle by default", :bundler => "< 3" do
+ it "does not create ./.bundle by default", bundler: "< 3" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
@@ -45,7 +45,7 @@ RSpec.describe "bundle install with gem sources" do
gem "rack"
G
- bundle :install, :env => { "BUNDLE_PATH__SYSTEM" => "true" } # can't use install_gemfile since it sets retry
+ bundle :install, env: { "BUNDLE_PATH__SYSTEM" => "true" } # can't use install_gemfile since it sets retry
expect(bundled_app(".bundle")).not_to exist
end
@@ -68,7 +68,7 @@ RSpec.describe "bundle install with gem sources" do
lockfile = File.read(bundled_app_lock)
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
raise StandardError, "FAIL"
G
@@ -130,7 +130,7 @@ RSpec.describe "bundle install with gem sources" do
end
it "raises an appropriate error when gems are specified using symbols" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
gem :rack
G
@@ -197,7 +197,7 @@ RSpec.describe "bundle install with gem sources" do
end
it "does not reinstall any gem that is already available locally" do
- system_gems "activesupport-2.3.2", :path => default_bundle_path
+ system_gems "activesupport-2.3.2", path: default_bundle_path
build_repo2 do
build_gem "activesupport", "2.3.2" do |s|
@@ -214,7 +214,7 @@ RSpec.describe "bundle install with gem sources" do
end
it "works when the gemfile specifies gems that only exist in the system" do
- build_gem "foo", :to_bundle => true
+ build_gem "foo", to_bundle: true
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
@@ -225,7 +225,7 @@ RSpec.describe "bundle install with gem sources" do
end
it "prioritizes local gems over remote gems" do
- build_gem "rack", "1.0.0", :to_bundle => true do |s|
+ build_gem "rack", "1.0.0", to_bundle: true do |s|
s.add_dependency "activesupport", "2.3.5"
end
@@ -241,7 +241,7 @@ RSpec.describe "bundle install with gem sources" do
plugin_msg = "hello from an env plugin!"
create_file "plugins/rubygems_plugin.rb", "puts '#{plugin_msg}'"
rubylib = ENV["RUBYLIB"].to_s.split(File::PATH_SEPARATOR).unshift(bundled_app("plugins").to_s).join(File::PATH_SEPARATOR)
- install_gemfile <<-G, :env => { "RUBYLIB" => rubylib }
+ install_gemfile <<-G, env: { "RUBYLIB" => rubylib }
source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
@@ -311,14 +311,14 @@ RSpec.describe "bundle install with gem sources" do
expect(the_bundle).to include_gems "rack 1.0"
end
- it "allows running bundle install --system without deleting foo", :bundler => "< 3" do
+ it "allows running bundle install --system without deleting foo", bundler: "< 3" do
bundle "install --path vendor"
bundle "install --system"
FileUtils.rm_rf(bundled_app("vendor"))
expect(the_bundle).to include_gems "rack 1.0"
end
- it "allows running bundle install --system after deleting foo", :bundler => "< 3" do
+ it "allows running bundle install --system after deleting foo", bundler: "< 3" do
bundle "install --path vendor"
FileUtils.rm_rf(bundled_app("vendor"))
bundle "install --system"
@@ -326,7 +326,7 @@ RSpec.describe "bundle install with gem sources" do
end
end
- it "finds gems in multiple sources", :bundler => "< 3" do
+ it "finds gems in multiple sources", bundler: "< 3" do
build_repo2 do
build_gem "rack", "1.2" do |s|
s.executables = "rackup"
@@ -345,7 +345,7 @@ RSpec.describe "bundle install with gem sources" do
end
it "gives a useful error if no sources are set" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
gem "rack"
G
@@ -408,7 +408,7 @@ RSpec.describe "bundle install with gem sources" do
end
it "does not throw a warning if a gem is added once in Gemfile and also inside a gemspec as a development dependency" do
- build_lib "my-gem", :path => bundled_app do |s|
+ build_lib "my-gem", path: bundled_app do |s|
s.add_development_dependency "my-private-gem"
end
@@ -430,8 +430,155 @@ RSpec.describe "bundle install with gem sources" do
expect(the_bundle).to include_gems("my-private-gem 1.0")
end
+ it "throws a warning if a gem is added once in Gemfile and also inside a gemspec as a development dependency, with different requirements" do
+ build_lib "my-gem", path: bundled_app do |s|
+ s.add_development_dependency "rubocop", "~> 1.36.0"
+ end
+
+ build_repo4 do
+ build_gem "rubocop", "1.36.0"
+ build_gem "rubocop", "1.37.1"
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gemspec
+
+ gem "rubocop", group: :development
+ G
+
+ bundle :install
+
+ expect(err).to include("A gemspec development dependency (rubocop, ~> 1.36.0) is being overridden by a Gemfile dependency (rubocop, >= 0).")
+ expect(err).to include("This behaviour may change in the future. Please remove either of them, or make sure they both have the same requirement")
+
+ # This is not the best behavior I believe, it would be better if both
+ # requirements are considered if they are compatible, and a version
+ # satisfying both is chosen. But not sure about changing it right now, so
+ # I went with a warning for the time being.
+ expect(the_bundle).to include_gems("rubocop 1.37.1")
+ end
+
+ it "includes the gem without warning if two gemspecs add it with the same requirement" do
+ gem1 = tmp.join("my-gem-1")
+ gem2 = tmp.join("my-gem-2")
+
+ build_lib "my-gem", path: gem1 do |s|
+ s.add_development_dependency "rubocop", "~> 1.36.0"
+ end
+
+ build_lib "my-gem-2", path: gem2 do |s|
+ s.add_development_dependency "rubocop", "~> 1.36.0"
+ end
+
+ build_repo4 do
+ build_gem "rubocop", "1.36.0"
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gemspec path: "#{gem1}"
+ gemspec path: "#{gem2}"
+ G
+
+ bundle :install
+
+ expect(err).to be_empty
+ expect(the_bundle).to include_gems("rubocop 1.36.0")
+ end
+
+ it "warns when a Gemfile dependency is overriding a gemspec development dependency, with different requirements" do
+ build_lib "my-gem", path: bundled_app do |s|
+ s.add_development_dependency "rails", ">= 5"
+ end
+
+ build_repo4 do
+ build_gem "rails", "7.0.8"
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "rails", "~> 7.0.8"
+
+ gemspec
+ G
+
+ bundle :install
+
+ expect(err).to include("A gemspec development dependency (rails, >= 5) is being overridden by a Gemfile dependency (rails, ~> 7.0.8).")
+ expect(err).to include("This behaviour may change in the future. Please remove either of them, or make sure they both have the same requirement")
+
+ # This is not the best behavior I believe, it would be better if both
+ # requirements are considered if they are compatible, and a version
+ # satisfying both is chosen. But not sure about changing it right now, so
+ # I went with a warning for the time being.
+ expect(the_bundle).to include_gems("rails 7.0.8")
+ end
+
+ it "does not warn if a gem is added once in Gemfile and also inside a gemspec as a development dependency, with same requirements, and different sources" do
+ build_lib "my-gem", path: bundled_app do |s|
+ s.add_development_dependency "activesupport"
+ end
+
+ build_repo4 do
+ build_gem "activesupport"
+ end
+
+ build_git "activesupport", "1.0", path: lib_path("activesupport")
+
+ install_gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gemspec
+
+ gem "activesupport", :git => "#{file_uri_for(lib_path("activesupport"))}"
+ G
+
+ expect(err).to be_empty
+ expect(the_bundle).to include_gems "activesupport 1.0", source: "git@#{lib_path("activesupport")}"
+
+ # if the Gemfile dependency is specified first
+ install_gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "activesupport", :git => "#{file_uri_for(lib_path("activesupport"))}"
+
+ gemspec
+ G
+
+ expect(err).to be_empty
+ expect(the_bundle).to include_gems "activesupport 1.0", source: "git@#{lib_path("activesupport")}"
+ end
+
+ it "considers both dependencies for resolution if a gem is added once in Gemfile and also inside a local gemspec as a runtime dependency, with different requirements" do
+ build_lib "my-gem", path: bundled_app do |s|
+ s.add_dependency "rubocop", "~> 1.36.0"
+ end
+
+ build_repo4 do
+ build_gem "rubocop", "1.36.0"
+ build_gem "rubocop", "1.37.1"
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gemspec
+
+ gem "rubocop"
+ G
+
+ bundle :install
+
+ expect(err).to be_empty
+ expect(the_bundle).to include_gems("rubocop 1.36.0")
+ end
+
it "throws an error if a gem is added twice in Gemfile when version of one dependency is not specified" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo2)}"
gem "rack"
gem "rack", "1.0"
@@ -442,7 +589,7 @@ RSpec.describe "bundle install with gem sources" do
end
it "throws an error if a gem is added twice in Gemfile when different versions of both dependencies are specified" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo2)}"
gem "rack", "1.0"
gem "rack", "1.1"
@@ -455,7 +602,7 @@ RSpec.describe "bundle install with gem sources" do
it "gracefully handles error when rubygems server is unavailable" do
skip "networking issue" if Gem.win_platform?
- install_gemfile <<-G, :artifice => nil, :raise_on_error => false
+ install_gemfile <<-G, artifice: nil, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
source "http://0.0.0.0:9384" do
gem 'foo'
@@ -467,8 +614,8 @@ RSpec.describe "bundle install with gem sources" do
end
it "fails gracefully when downloading an invalid specification from the full index" do
- build_repo2 do
- build_gem "ajp-rails", "0.0.0", :gemspec => false, :skip_validation => true do |s|
+ build_repo2(build_compact_index: false) do
+ build_gem "ajp-rails", "0.0.0", gemspec: false, skip_validation: true do |s|
bad_deps = [["ruby-ajp", ">= 0.2.0"], ["rails", ">= 0.14"]]
s.
instance_variable_get(:@spec).
@@ -479,7 +626,7 @@ RSpec.describe "bundle install with gem sources" do
build_gem "ruby-ajp", "1.0.0"
end
- install_gemfile <<-G, :full_index => true, :raise_on_error => false
+ install_gemfile <<-G, full_index: true, raise_on_error: false
source "#{file_uri_for(gem_repo2)}"
gem "ajp-rails", "0.0.0"
@@ -514,11 +661,9 @@ RSpec.describe "bundle install with gem sources" do
end
describe "Ruby version in Gemfile.lock" do
- include Bundler::GemHelpers
-
context "and using an unsupported Ruby version" do
it "prints an error" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
ruby '~> 1.2'
source "#{file_uri_for(gem_repo1)}"
G
@@ -535,6 +680,7 @@ RSpec.describe "bundle install with gem sources" do
end
it "writes current Ruby version to Gemfile.lock" do
+ checksums = checksums_section_when_existing
expect(lockfile).to eq <<~L
GEM
remote: #{file_uri_for(gem_repo1)}/
@@ -544,7 +690,7 @@ RSpec.describe "bundle install with gem sources" do
#{lockfile_platforms}
DEPENDENCIES
-
+ #{checksums}
RUBY VERSION
#{Bundler::RubyVersion.system}
@@ -559,6 +705,8 @@ RSpec.describe "bundle install with gem sources" do
source "#{file_uri_for(gem_repo1)}"
G
+ checksums = checksums_section_when_existing
+
expect(lockfile).to eq <<~L
GEM
remote: #{file_uri_for(gem_repo1)}/
@@ -568,7 +716,7 @@ RSpec.describe "bundle install with gem sources" do
#{lockfile_platforms}
DEPENDENCIES
-
+ #{checksums}
RUBY VERSION
#{Bundler::RubyVersion.system}
@@ -605,7 +753,7 @@ RSpec.describe "bundle install with gem sources" do
file.puts gemfile
end
- bundle :install, :dir => root_dir
+ bundle :install, dir: root_dir
end
it "doesn't blow up when using the `gemspec` DSL" do
@@ -613,7 +761,7 @@ RSpec.describe "bundle install with gem sources" do
FileUtils.mkdir_p(root_dir)
- build_lib "foo", :path => root_dir
+ build_lib "foo", path: root_dir
gemfile = <<-G
source "#{file_uri_for(gem_repo1)}"
gemspec
@@ -622,7 +770,7 @@ RSpec.describe "bundle install with gem sources" do
file.puts gemfile
end
- bundle :install, :dir => root_dir
+ bundle :install, dir: root_dir
end
end
@@ -635,7 +783,7 @@ RSpec.describe "bundle install with gem sources" do
gem 'rack'
G
- bundle :install, :quiet => true
+ bundle :install, quiet: true
expect(out).to be_empty
expect(err).to be_empty
end
@@ -665,7 +813,7 @@ RSpec.describe "bundle install with gem sources" do
gem 'non-existing-gem'
G
- bundle :install, :quiet => true, :raise_on_error => false, :env => { "RUBYOPT" => "-r#{bundled_app("install_with_warning.rb")}" }
+ bundle :install, quiet: true, raise_on_error: false, env: { "RUBYOPT" => "-r#{bundled_app("install_with_warning.rb")}" }
expect(out).to be_empty
expect(err).to include("Could not find gem 'non-existing-gem'")
expect(err).to include("BOOOOO")
@@ -687,7 +835,7 @@ RSpec.describe "bundle install with gem sources" do
FileUtils.chmod(0o500, bundle_path)
bundle "config set --local path vendor"
- bundle :install, :raise_on_error => false
+ bundle :install, raise_on_error: false
expect(err).to include(bundle_path.to_s)
expect(err).to include("grant write permissions")
end
@@ -709,7 +857,7 @@ RSpec.describe "bundle install with gem sources" do
bundle "config set --local path vendor"
begin
- bundle :install, :raise_on_error => false
+ bundle :install, raise_on_error: false
ensure
FileUtils.chmod("+x", gems_path)
end
@@ -739,7 +887,7 @@ RSpec.describe "bundle install with gem sources" do
bundle "config set --local path vendor"
begin
- bundle :install, :raise_on_error => false
+ bundle :install, raise_on_error: false
ensure
FileUtils.chmod("+x", extensions_path)
end
@@ -779,7 +927,7 @@ RSpec.describe "bundle install with gem sources" do
FileUtils.chmod("-x", foo_path)
begin
- bundle "install --redownload", :raise_on_error => false
+ bundle "install --redownload", raise_on_error: false
ensure
FileUtils.chmod("+x", foo_path)
end
@@ -790,6 +938,71 @@ RSpec.describe "bundle install with gem sources" do
end
end
+ describe "when gem home does not have the writable bit set, yet it's still writable", :permissions do
+ let(:gem_home) { bundled_app("vendor/#{Bundler.ruby_scope}") }
+
+ before do
+ build_repo4 do
+ build_gem "foo", "1.0.0" do |s|
+ s.write "CHANGELOG.md", "foo"
+ end
+ end
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem 'foo'
+ G
+ end
+
+ it "should display a proper message to explain the problem" do
+ bundle "config set --local path vendor"
+ bundle :install
+ expect(out).to include("Bundle complete!")
+ expect(err).to be_empty
+
+ FileUtils.chmod("-w", gem_home)
+
+ begin
+ bundle "install --redownload"
+ ensure
+ FileUtils.chmod("+w", gem_home)
+ end
+
+ expect(out).to include("Bundle complete!")
+ expect(err).to be_empty
+ end
+ end
+
+ describe "when gems path is world writable (no sticky bit set)", :permissions do
+ let(:gems_path) { bundled_app("vendor/#{Bundler.ruby_scope}/gems") }
+
+ before do
+ build_repo4 do
+ build_gem "foo", "1.0.0" do |s|
+ s.write "CHANGELOG.md", "foo"
+ end
+ end
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem 'foo'
+ G
+ end
+
+ it "should display a proper message to explain the problem" do
+ bundle "config set --local path vendor"
+ bundle :install
+ expect(out).to include("Bundle complete!")
+ expect(err).to be_empty
+
+ FileUtils.chmod(0o777, gems_path)
+
+ bundle "install --redownload", raise_on_error: false
+
+ expect(err).to include("The installation path is insecure. Bundler cannot continue.")
+ end
+ end
+
describe "when bundle cache path does not have write access", :permissions do
let(:cache_path) { bundled_app("vendor/#{Bundler.ruby_scope}/cache") }
@@ -805,12 +1018,35 @@ RSpec.describe "bundle install with gem sources" do
FileUtils.chmod(0o500, cache_path)
bundle "config set --local path vendor"
- bundle :install, :raise_on_error => false
+ bundle :install, raise_on_error: false
expect(err).to include(cache_path.to_s)
expect(err).to include("grant write permissions")
end
end
+ describe "when gemspecs are unreadable", :permissions do
+ let(:gemspec_path) { vendored_gems("specifications/rack-1.0.0.gemspec") }
+
+ before do
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo1)}"
+ gem 'rack'
+ G
+ bundle "config path vendor/bundle"
+ bundle :install
+ expect(out).to include("Bundle complete!")
+ expect(err).to be_empty
+
+ FileUtils.chmod("-r", gemspec_path)
+ end
+
+ it "shows a good error" do
+ bundle :install, raise_on_error: false
+ expect(err).to include(gemspec_path.to_s)
+ expect(err).to include("grant read permissions")
+ end
+ end
+
context "after installing with --standalone" do
before do
install_gemfile <<-G
@@ -818,11 +1054,11 @@ RSpec.describe "bundle install with gem sources" do
gem "rack"
G
bundle "config set --local path bundle"
- bundle "install", :standalone => true
+ bundle "install", standalone: true
end
it "includes the standalone path" do
- bundle "binstubs rack", :standalone => true
+ bundle "binstubs rack", standalone: true
standalone_line = File.read(bundled_app("bin/rackup")).each_line.find {|line| line.include? "$:.unshift" }.strip
expect(standalone_line).to eq %($:.unshift File.expand_path "../bundle", __dir__)
end
@@ -837,7 +1073,7 @@ RSpec.describe "bundle install with gem sources" do
end
it "should display a helpful message explaining how to fix it" do
- bundle :install, :env => { "BUNDLE_RUBYGEMS__ORG" => "user:pass{word" }, :raise_on_error => false
+ bundle :install, env: { "BUNDLE_RUBYGEMS__ORG" => "user:pass{word" }, raise_on_error: false
expect(exitstatus).to eq(17)
expect(err).to eq("Please CGI escape your usernames and passwords before " \
"setting them for authentication.")
@@ -878,7 +1114,7 @@ RSpec.describe "bundle install with gem sources" do
end
it "should fail loudly if the lockfile platforms don't include the current platform" do
- simulate_platform(Gem::Platform.new("x86_64-linux")) { bundle "install", :raise_on_error => false }
+ simulate_platform(Gem::Platform.new("x86_64-linux")) { bundle "install", raise_on_error: false }
expect(err).to eq(
"Your bundle only supports platforms [\"x86_64-darwin-19\"] but your local platform is x86_64-linux. " \
@@ -890,16 +1126,16 @@ RSpec.describe "bundle install with gem sources" do
context "with missing platform specific gems in lockfile" do
before do
build_repo4 do
- build_gem "racc", "1.5.2"
+ build_gem "racca", "1.5.2"
build_gem "nokogiri", "1.12.4" do |s|
s.platform = "x86_64-darwin"
- s.add_runtime_dependency "racc", "~> 1.4"
+ s.add_runtime_dependency "racca", "~> 1.4"
end
build_gem "nokogiri", "1.12.4" do |s|
s.platform = "x86_64-linux"
- s.add_runtime_dependency "racc", "~> 1.4"
+ s.add_runtime_dependency "racca", "~> 1.4"
end
build_gem "crass", "1.0.6"
@@ -918,6 +1154,13 @@ RSpec.describe "bundle install with gem sources" do
gem "loofah", "~> 2.12.0"
G
+ checksums = checksums_section do |c|
+ c.checksum gem_repo4, "crass", "1.0.6"
+ c.checksum gem_repo4, "loofah", "2.12.0"
+ c.checksum gem_repo4, "nokogiri", "1.12.4", "x86_64-darwin"
+ c.checksum gem_repo4, "racca", "1.5.2"
+ end
+
lockfile <<-L
GEM
remote: https://gem.repo4/
@@ -927,8 +1170,8 @@ RSpec.describe "bundle install with gem sources" do
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
nokogiri (1.12.4-x86_64-darwin)
- racc (~> 1.4)
- racc (1.5.2)
+ racca (~> 1.4)
+ racca (1.5.2)
PLATFORMS
x86_64-darwin-20
@@ -936,7 +1179,7 @@ RSpec.describe "bundle install with gem sources" do
DEPENDENCIES
loofah (~> 2.12.0)
-
+ #{checksums}
RUBY VERSION
#{Bundler::RubyVersion.system}
@@ -949,7 +1192,15 @@ RSpec.describe "bundle install with gem sources" do
bundle "config set --local path vendor/bundle"
simulate_platform "x86_64-linux" do
- bundle "install", :artifice => "compact_index"
+ bundle "install", artifice: "compact_index"
+ end
+
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo4, "crass", "1.0.6"
+ c.checksum gem_repo4, "loofah", "2.12.0"
+ c.checksum gem_repo4, "nokogiri", "1.12.4", "x86_64-darwin"
+ c.checksum gem_repo4, "racca", "1.5.2"
+ c.checksum gem_repo4, "nokogiri", "1.12.4", "x86_64-linux"
end
expect(lockfile).to eq <<~L
@@ -961,10 +1212,10 @@ RSpec.describe "bundle install with gem sources" do
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
nokogiri (1.12.4-x86_64-darwin)
- racc (~> 1.4)
+ racca (~> 1.4)
nokogiri (1.12.4-x86_64-linux)
- racc (~> 1.4)
- racc (1.5.2)
+ racca (~> 1.4)
+ racca (1.5.2)
PLATFORMS
x86_64-darwin-20
@@ -972,7 +1223,7 @@ RSpec.describe "bundle install with gem sources" do
DEPENDENCIES
loofah (~> 2.12.0)
-
+ #{checksums}
RUBY VERSION
#{Bundler::RubyVersion.system}
@@ -984,11 +1235,11 @@ RSpec.describe "bundle install with gem sources" do
context "with --local flag" do
before do
- system_gems "rack-1.0.0", :path => default_bundle_path
+ system_gems "rack-1.0.0", path: default_bundle_path
end
it "respects installed gems without fetching any remote sources" do
- install_gemfile <<-G, :local => true
+ install_gemfile <<-G, local: true
source "#{file_uri_for(gem_repo1)}"
source "https://not-existing-source" do
@@ -1028,11 +1279,11 @@ RSpec.describe "bundle install with gem sources" do
build_gem "bar", "1.0.0"
end
- system_gems "foo-1.0.0", :path => default_bundle_path, :gem_repo => gem_repo4
+ system_gems "foo-1.0.0", path: default_bundle_path, gem_repo: gem_repo4
end
it "fetches remote sources only when not available locally" do
- install_gemfile <<-G, :"prefer-local" => true, :verbose => true
+ install_gemfile <<-G, "prefer-local": true, verbose: true
source "#{file_uri_for(gem_repo4)}"
gem "foo"
@@ -1062,7 +1313,7 @@ RSpec.describe "bundle install with gem sources" do
File.symlink("../../README.markdown", File.join(man_path, "README.markdown"))
build_repo4 do
- build_gem "binman", :path => gem_repo4("gems"), :lib_path => binman_path, :no_default => true do |s|
+ build_gem "binman", path: gem_repo4("gems"), lib_path: binman_path, no_default: true do |s|
s.files = ["README.markdown", "man/man0/README.markdown"]
end
end
@@ -1097,10 +1348,63 @@ RSpec.describe "bundle install with gem sources" do
gem "autobuild", "1.10.rc2"
G
- bundle "install --jobs 1", :raise_on_error => false
+ bundle "install --jobs 1", raise_on_error: false
expect(err).not_to include("ERROR REPORT TEMPLATE")
expect(err).to include("Could not find compatible versions")
end
end
+
+ context "when a lockfile has unmet dependencies, and the Gemfile has no resolution" do
+ before do
+ build_repo4 do
+ build_gem "aaa", "0.2.0" do |s|
+ s.add_dependency "zzz", "< 0.2.0"
+ end
+
+ build_gem "zzz", "0.2.0"
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "aaa"
+ gem "zzz"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ aaa (0.2.0)
+ zzz (< 0.2.0)
+ zzz (0.2.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ aaa!
+ zzz!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "does not install, but raises a resolution error" do
+ bundle "install", raise_on_error: false
+ expect(err).to include("Could not find compatible versions")
+ end
+ end
+
+ context "when --jobs option given" do
+ before do
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\"", jobs: 1
+ end
+
+ it "does not save the flag to config" do
+ expect(bundled_app(".bundle/config")).not_to exist
+ end
+ end
end
diff --git a/spec/bundler/commands/list_spec.rb b/spec/bundler/commands/list_spec.rb
index 66930ded75..5ac2077d81 100644
--- a/spec/bundler/commands/list_spec.rb
+++ b/spec/bundler/commands/list_spec.rb
@@ -3,7 +3,7 @@
RSpec.describe "bundle list" do
context "with name-only and paths option" do
it "raises an error" do
- bundle "list --name-only --paths", :raise_on_error => false
+ bundle "list --name-only --paths", raise_on_error: false
expect(err).to eq "The `--name-only` and `--paths` options cannot be used together"
end
@@ -11,7 +11,7 @@ RSpec.describe "bundle list" do
context "with without-group and only-group option" do
it "raises an error" do
- bundle "list --without-group dev --only-group test", :raise_on_error => false
+ bundle "list --without-group dev --only-group test", raise_on_error: false
expect(err).to eq "The `--only-group` and `--without-group` options cannot be used together"
end
@@ -40,7 +40,7 @@ RSpec.describe "bundle list" do
context "when group is not found" do
it "raises an error" do
- bundle "list --without-group random", :raise_on_error => false
+ bundle "list --without-group random", raise_on_error: false
expect(err).to eq "`random` group could not be found."
end
@@ -79,7 +79,7 @@ RSpec.describe "bundle list" do
context "when group is not found" do
it "raises an error" do
- bundle "list --only-group random", :raise_on_error => false
+ bundle "list --only-group random", raise_on_error: false
expect(err).to eq "`random` group could not be found."
end
@@ -124,9 +124,9 @@ RSpec.describe "bundle list" do
build_gem "bar"
end
- build_git "git_test", "1.0.0", :path => lib_path("git_test")
+ build_git "git_test", "1.0.0", path: lib_path("git_test")
- build_lib("gemspec_test", :path => tmp.join("gemspec_test")) do |s|
+ build_lib("gemspec_test", path: tmp.join("gemspec_test")) do |s|
s.add_dependency "bar", "=1.0.0"
end
diff --git a/spec/bundler/commands/lock_spec.rb b/spec/bundler/commands/lock_spec.rb
index 85b3d4a075..c6bb0f58af 100644
--- a/spec/bundler/commands/lock_spec.rb
+++ b/spec/bundler/commands/lock_spec.rb
@@ -1,14 +1,6 @@
# frozen_string_literal: true
RSpec.describe "bundle lock" do
- def strip_lockfile(lockfile)
- strip_whitespace(lockfile).sub(/\n\Z/, "")
- end
-
- def read_lockfile(file = "Gemfile.lock")
- strip_lockfile bundled_app(file).read
- end
-
let(:repo) { gem_repo1 }
before :each do
@@ -19,7 +11,19 @@ RSpec.describe "bundle lock" do
gem "foo"
G
- @lockfile = strip_lockfile(<<-L)
+ checksums = checksums_section_when_existing do |c|
+ c.checksum repo, "actionmailer", "2.3.2"
+ c.checksum repo, "actionpack", "2.3.2"
+ c.checksum repo, "activerecord", "2.3.2"
+ c.checksum repo, "activeresource", "2.3.2"
+ c.checksum repo, "activesupport", "2.3.2"
+ c.checksum repo, "foo", "1.0"
+ c.checksum repo, "rails", "2.3.2"
+ c.checksum repo, "rake", rake_version
+ c.checksum repo, "weakling", "0.0.3"
+ end
+
+ @lockfile = <<~L
GEM
remote: #{file_uri_for(repo)}/
specs:
@@ -38,8 +42,8 @@ RSpec.describe "bundle lock" do
actionpack (= 2.3.2)
activerecord (= 2.3.2)
activeresource (= 2.3.2)
- rake (= 13.0.1)
- rake (13.0.1)
+ rake (= #{rake_version})
+ rake (#{rake_version})
weakling (0.0.3)
PLATFORMS
@@ -49,7 +53,7 @@ RSpec.describe "bundle lock" do
foo
rails
weakling
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -58,15 +62,23 @@ RSpec.describe "bundle lock" do
it "prints a lockfile when there is no existing lockfile with --print" do
bundle "lock --print"
- expect(out).to eq(@lockfile)
+ expect(out).to eq(@lockfile.chomp)
end
it "prints a lockfile when there is an existing lockfile with --print" do
+ lockfile remove_checksums_section_from_lockfile(@lockfile)
+
+ bundle "lock --print"
+
+ expect(out).to eq(remove_checksums_section_from_lockfile(@lockfile).chomp)
+ end
+
+ it "prints a lockfile when there is an existing checksums lockfile with --print" do
lockfile @lockfile
bundle "lock --print"
- expect(out).to eq(@lockfile)
+ expect(out).to eq(@lockfile.chomp)
end
it "writes a lockfile when there is no existing lockfile" do
@@ -75,7 +87,39 @@ RSpec.describe "bundle lock" do
expect(read_lockfile).to eq(@lockfile)
end
+ it "prints a lockfile without fetching new checksums if the existing lockfile had no checksums" do
+ lockfile remove_checksums_from_lockfile(@lockfile)
+
+ bundle "lock --print"
+
+ expect(out).to eq(remove_checksums_from_lockfile(@lockfile).chomp)
+ end
+
+ it "touches the lockfile when there is an existing lockfile that does not need changes" do
+ lockfile @lockfile
+
+ expect do
+ bundle "lock"
+ end.to change { bundled_app_lock.mtime }
+ end
+
+ it "does not touch lockfile with --print" do
+ lockfile @lockfile
+
+ expect do
+ bundle "lock --print"
+ end.not_to change { bundled_app_lock.mtime }
+ end
+
it "writes a lockfile when there is an outdated lockfile using --update" do
+ lockfile remove_checksums_from_lockfile(@lockfile.gsub("2.3.2", "2.3.1"), " (2.3.1)")
+
+ bundle "lock --update"
+
+ expect(read_lockfile).to eq(remove_checksums_from_lockfile(@lockfile))
+ end
+
+ it "writes a lockfile with checksums on --update when checksums exist" do
lockfile @lockfile.gsub("2.3.2", "2.3.1")
bundle "lock --update"
@@ -83,10 +127,27 @@ RSpec.describe "bundle lock" do
expect(read_lockfile).to eq(@lockfile)
end
+ it "writes a lockfile when there is an outdated lockfile and bundle is frozen" do
+ lockfile @lockfile.gsub("2.3.2", "2.3.1")
+
+ bundle "lock --update", env: { "BUNDLE_FROZEN" => "true" }
+
+ expect(read_lockfile).to eq(@lockfile)
+ end
+
it "does not fetch remote specs when using the --local option" do
- bundle "lock --update --local", :raise_on_error => false
+ bundle "lock --update --local", raise_on_error: false
+
+ expect(err).to match(/cached gems or installed locally/)
+ end
+
+ it "does not fetch remote checksums with --local" do
+ lockfile remove_checksums_from_lockfile(@lockfile)
+
+ bundle "lock --print --local"
- expect(err).to match(/locally installed gems/)
+ # No checksums because --local prevents fetching them
+ expect(out).to eq(remove_checksums_from_lockfile(@lockfile).chomp)
end
it "works with --gemfile flag" do
@@ -94,7 +155,11 @@ RSpec.describe "bundle lock" do
source "#{file_uri_for(repo)}"
gem "foo"
G
- lockfile = strip_lockfile(<<-L)
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "foo", "1.0"
+ end
+
+ lockfile = <<~L
GEM
remote: #{file_uri_for(repo)}/
specs:
@@ -105,7 +170,7 @@ RSpec.describe "bundle lock" do
DEPENDENCIES
foo
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -120,7 +185,7 @@ RSpec.describe "bundle lock" do
bundle "lock --lockfile=lock"
expect(out).to match(/Writing lockfile to.+lock/)
- expect(read_lockfile("lock")).to eq(@lockfile)
+ expect(read_lockfile("lock")).to eq(remove_checksums_from_lockfile(@lockfile))
expect { read_lockfile }.to raise_error(Errno::ENOENT)
end
@@ -128,16 +193,195 @@ RSpec.describe "bundle lock" do
bundle "install"
bundle "lock --lockfile=lock"
+ checksums = checksums_section_when_existing do |c|
+ c.checksum repo, "actionmailer", "2.3.2"
+ c.checksum repo, "actionpack", "2.3.2"
+ c.checksum repo, "activerecord", "2.3.2"
+ c.checksum repo, "activeresource", "2.3.2"
+ c.checksum repo, "activesupport", "2.3.2"
+ c.checksum repo, "foo", "1.0"
+ c.checksum repo, "rails", "2.3.2"
+ c.checksum repo, "rake", rake_version
+ c.checksum repo, "weakling", "0.0.3"
+ end
+
+ lockfile = <<~L
+ GEM
+ remote: #{file_uri_for(repo)}/
+ specs:
+ actionmailer (2.3.2)
+ activesupport (= 2.3.2)
+ actionpack (2.3.2)
+ activesupport (= 2.3.2)
+ activerecord (2.3.2)
+ activesupport (= 2.3.2)
+ activeresource (2.3.2)
+ activesupport (= 2.3.2)
+ activesupport (2.3.2)
+ foo (1.0)
+ rails (2.3.2)
+ actionmailer (= 2.3.2)
+ actionpack (= 2.3.2)
+ activerecord (= 2.3.2)
+ activeresource (= 2.3.2)
+ rake (= #{rake_version})
+ rake (#{rake_version})
+ weakling (0.0.3)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo
+ rails
+ weakling
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
expect(out).to match(/Writing lockfile to.+lock/)
- expect(read_lockfile("lock")).to eq(@lockfile)
+ expect(read_lockfile("lock")).to eq(lockfile)
end
it "update specific gems using --update" do
- lockfile @lockfile.gsub("2.3.2", "2.3.1").gsub("13.0.1", "10.0.1")
+ lockfile @lockfile.gsub("2.3.2", "2.3.1").gsub(rake_version, "10.0.1")
bundle "lock --update rails rake"
- expect(read_lockfile).to eq(@lockfile)
+ expect(read_lockfile).to eq(remove_checksums_from_lockfile(@lockfile, "(2.3.2)", "(#{rake_version})"))
+ end
+
+ it "updates specific gems using --update, even if that requires unlocking other top level gems" do
+ build_repo4 do
+ build_gem "prism", "0.15.1"
+ build_gem "prism", "0.24.0"
+
+ build_gem "ruby-lsp", "0.12.0" do |s|
+ s.add_dependency "prism", "< 0.24.0"
+ end
+
+ build_gem "ruby-lsp", "0.16.1" do |s|
+ s.add_dependency "prism", ">= 0.24.0"
+ end
+
+ build_gem "tapioca", "0.11.10" do |s|
+ s.add_dependency "prism", "< 0.24.0"
+ end
+
+ build_gem "tapioca", "0.13.1" do |s|
+ s.add_dependency "prism", ">= 0.24.0"
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "tapioca"
+ gem "ruby-lsp"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}
+ specs:
+ prism (0.15.1)
+ ruby-lsp (0.12.0)
+ prism (< 0.24.0)
+ tapioca (0.11.10)
+ prism (< 0.24.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ruby-lsp
+ tapioca
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock --update tapioca --verbose"
+
+ expect(lockfile).to include("tapioca (0.13.1)")
+ end
+
+ it "updates specific gems using --update, even if that requires unlocking other top level gems, but only as few as possible" do
+ build_repo4 do
+ build_gem "prism", "0.15.1"
+ build_gem "prism", "0.24.0"
+
+ build_gem "ruby-lsp", "0.12.0" do |s|
+ s.add_dependency "prism", "< 0.24.0"
+ end
+
+ build_gem "ruby-lsp", "0.16.1" do |s|
+ s.add_dependency "prism", ">= 0.24.0"
+ end
+
+ build_gem "tapioca", "0.11.10" do |s|
+ s.add_dependency "prism", "< 0.24.0"
+ end
+
+ build_gem "tapioca", "0.13.1" do |s|
+ s.add_dependency "prism", ">= 0.24.0"
+ end
+
+ build_gem "other-prism-dependent", "1.0.0" do |s|
+ s.add_dependency "prism", ">= 0.15.1"
+ end
+
+ build_gem "other-prism-dependent", "1.1.0" do |s|
+ s.add_dependency "prism", ">= 0.15.1"
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "tapioca"
+ gem "ruby-lsp"
+ gem "other-prism-dependent"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}
+ specs:
+ other-prism-dependent (1.0.0)
+ prism (>= 0.15.1)
+ prism (0.15.1)
+ ruby-lsp (0.12.0)
+ prism (< 0.24.0)
+ tapioca (0.11.10)
+ prism (< 0.24.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ruby-lsp
+ tapioca
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock --update tapioca"
+
+ expect(lockfile).to include("tapioca (0.13.1)")
+ expect(lockfile).to include("other-prism-dependent (1.0.0)")
+ end
+
+ it "preserves unknown checksum algorithms" do
+ lockfile @lockfile.gsub(/(sha256=[a-f0-9]+)$/, "constant=true,\\1,xyz=123")
+
+ previous_lockfile = read_lockfile
+
+ bundle "lock"
+
+ expect(read_lockfile).to eq(previous_lockfile)
end
it "does not unlock git sources when only uri shape changes" do
@@ -149,7 +393,7 @@ RSpec.describe "bundle lock" do
G
# Change uri format to end with "/" and reinstall
- install_gemfile <<-G, :verbose => true
+ install_gemfile <<-G, verbose: true
source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}/"
G
@@ -193,13 +437,13 @@ RSpec.describe "bundle lock" do
bundle "lock --update rake --verbose"
expect(out).to match(/Writing lockfile to.+lock/)
- expect(lockfile).to include("rake (13.0.1)")
+ expect(lockfile).to include("rake (#{rake_version})")
end
it "errors when updating a missing specific gems using --update" do
lockfile @lockfile
- bundle "lock --update blahblah", :raise_on_error => false
+ bundle "lock --update blahblah", raise_on_error: false
expect(err).to eq("Could not find gem 'blahblah'.")
expect(read_lockfile).to eq(@lockfile)
@@ -214,7 +458,7 @@ RSpec.describe "bundle lock" do
G
bundle "config set without test"
bundle "config set path vendor/bundle"
- bundle "lock"
+ bundle "lock", verbose: true
expect(bundled_app("vendor/bundle")).not_to exist
end
@@ -270,6 +514,22 @@ RSpec.describe "bundle lock" do
expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.5.0 bar-2.1.1 qux-1.1.0].sort)
end
+ it "shows proper error when Gemfile changes forbid patch upgrades, and --patch --strict is given" do
+ # force next minor via Gemfile
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem 'foo', '1.5.0'
+ gem 'qux'
+ G
+
+ bundle "lock --update foo --patch --strict", raise_on_error: false
+
+ expect(err).to include(
+ "foo is locked to 1.4.3, while Gemfile is requesting foo (= 1.5.0). " \
+ "--strict --patch was specified, but there are no patch level upgrades from 1.4.3 satisfying foo (= 1.5.0), so version solving has failed"
+ )
+ end
+
context "pre" do
it "defaults to major" do
bundle "lock --update --pre"
@@ -297,24 +557,68 @@ RSpec.describe "bundle lock" do
end
end
- it "updates the bundler version in the lockfile without re-resolving", :rubygems => ">= 3.3.0.dev" do
+ context "conservative updates when minor update adds a new dependency" do
+ before do
+ build_repo4 do
+ build_gem "sequel", "5.71.0"
+ build_gem "sequel", "5.72.0" do |s|
+ s.add_dependency "bigdecimal", ">= 0"
+ end
+ build_gem "bigdecimal", %w[1.4.4 99.1.4]
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem 'sequel'
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ sequel (5.71.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ sequel
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
+ end
+
+ it "adds the latest version of the new dependency" do
+ bundle "lock --minor --update sequel"
+
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[sequel-5.72.0 bigdecimal-99.1.4].sort)
+ end
+ end
+
+ it "updates the bundler version in the lockfile to the latest bundler version" do
build_repo4 do
- build_gem "rack", "1.0"
+ build_gem "bundler", "55"
end
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
- gem "rack"
+ system_gems "bundler-55", gem_repo: gem_repo4
+
+ install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ source "https://gems.repo4"
G
lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, '\11.0.0\2')
- FileUtils.rm_r gem_repo4
+ bundle "lock --update --bundler --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ expect(lockfile).to end_with("BUNDLED WITH\n 55\n")
- bundle "lock --update --bundler"
- expect(the_bundle).to include_gem "rack 1.0"
+ update_repo4 do
+ build_gem "bundler", "99"
+ end
- allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- expect(the_bundle.locked_gems.bundler_version).to eq v(Bundler::VERSION)
+ bundle "lock --update --bundler --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ expect(lockfile).to end_with("BUNDLED WITH\n 99\n")
end
it "supports adding new platforms" do
@@ -322,7 +626,7 @@ RSpec.describe "bundle lock" do
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
lockfile = Bundler::LockfileParser.new(read_lockfile)
- expect(lockfile.platforms).to match_array([java, x86_mingw32, local_platform].uniq)
+ expect(lockfile.platforms).to match_array(default_platform_list(java, x86_mingw32))
end
it "supports adding new platforms with force_ruby_platform = true" do
@@ -354,7 +658,7 @@ RSpec.describe "bundle lock" do
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
lockfile = Bundler::LockfileParser.new(read_lockfile)
- expect(lockfile.platforms).to match_array(["ruby", local_platform].uniq)
+ expect(lockfile.platforms).to match_array(default_platform_list("ruby"))
end
it "warns when adding an unknown platform" do
@@ -367,12 +671,12 @@ RSpec.describe "bundle lock" do
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
lockfile = Bundler::LockfileParser.new(read_lockfile)
- expect(lockfile.platforms).to match_array([java, x86_mingw32, local_platform].uniq)
+ expect(lockfile.platforms).to match_array(default_platform_list(java, x86_mingw32))
bundle "lock --remove-platform java"
lockfile = Bundler::LockfileParser.new(read_lockfile)
- expect(lockfile.platforms).to match_array([x86_mingw32, local_platform].uniq)
+ expect(lockfile.platforms).to match_array(default_platform_list(x86_mingw32))
end
it "also cleans up redundant platform gems when removing platforms" do
@@ -383,6 +687,11 @@ RSpec.describe "bundle lock" do
end
end
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo4, "nokogiri", "1.12.0"
+ c.checksum gem_repo4, "nokogiri", "1.12.0", "x86_64-darwin"
+ end
+
simulate_platform "x86_64-darwin-22" do
install_gemfile <<~G
source "#{file_uri_for(gem_repo4)}"
@@ -404,11 +713,13 @@ RSpec.describe "bundle lock" do
DEPENDENCIES
nokogiri
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
+ checksums.delete("nokogiri", Gem::Platform::RUBY)
+
simulate_platform "x86_64-darwin-22" do
bundle "lock --remove-platform ruby"
end
@@ -424,14 +735,14 @@ RSpec.describe "bundle lock" do
DEPENDENCIES
nokogiri
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
end
it "errors when removing all platforms" do
- bundle "lock --remove-platform #{local_platform}", :raise_on_error => false
+ bundle "lock --remove-platform #{local_platform}", raise_on_error: false
expect(err).to include("Removing all platforms from the bundle is not allowed")
end
@@ -472,6 +783,13 @@ RSpec.describe "bundle lock" do
gem "gssapi"
G
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "ffi", "1.9.14", "x86-mingw32"
+ c.no_checksum "gssapi", "1.2.0"
+ c.no_checksum "mixlib-shellout", "2.2.6", "universal-mingw32"
+ c.no_checksum "win32-process", "0.8.3"
+ end
+
simulate_platform(x86_mingw32) { bundle :lock }
expect(lockfile).to eq <<~G
@@ -492,7 +810,7 @@ RSpec.describe "bundle lock" do
DEPENDENCIES
gssapi
mixlib-shellout
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -500,6 +818,9 @@ RSpec.describe "bundle lock" do
bundle "config set --local force_ruby_platform true"
bundle :lock
+ checksums.no_checksum "ffi", "1.9.14"
+ checksums.no_checksum "mixlib-shellout", "2.2.6"
+
expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo4)}/
@@ -521,7 +842,7 @@ RSpec.describe "bundle lock" do
DEPENDENCIES
gssapi
mixlib-shellout
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -585,7 +906,12 @@ RSpec.describe "bundle lock" do
gem "libv8"
G
- simulate_platform(Gem::Platform.new("x86_64-darwin")) { bundle "lock" }
+ simulate_platform(Gem::Platform.new("x86_64-darwin-19")) { bundle "lock" }
+
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "libv8", "8.4.255.0", "x86_64-darwin-19"
+ c.no_checksum "libv8", "8.4.255.0", "x86_64-darwin-20"
+ end
expect(lockfile).to eq <<~G
GEM
@@ -595,11 +921,12 @@ RSpec.describe "bundle lock" do
libv8 (8.4.255.0-x86_64-darwin-20)
PLATFORMS
- x86_64-darwin
+ x86_64-darwin-19
+ x86_64-darwin-20
DEPENDENCIES
libv8
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -616,6 +943,11 @@ RSpec.describe "bundle lock" do
end
end
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo4, "libv8", "8.4.255.0", "x86_64-darwin-19"
+ c.checksum gem_repo4, "libv8", "8.4.255.0", "x86_64-darwin-20"
+ end
+
gemfile <<-G
source "#{file_uri_for(gem_repo4)}"
@@ -634,7 +966,7 @@ RSpec.describe "bundle lock" do
DEPENDENCIES
libv8
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -697,7 +1029,7 @@ RSpec.describe "bundle lock" do
#{Bundler::VERSION}
L
- bundle "lock --add-platform x86_64-linux", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ bundle "lock --add-platform x86_64-linux", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
end
it "does not crash on conflicting ruby requirements between platform versions in two different gems" do
@@ -753,7 +1085,7 @@ RSpec.describe "bundle lock" do
#{Bundler::VERSION}
L
- bundle "install --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s, "DEBUG_RESOLVER" => "1" }
+ bundle "install --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s, "DEBUG_RESOLVER" => "1" }
end
it "respects lower bound ruby requirements" do
@@ -785,30 +1117,126 @@ RSpec.describe "bundle lock" do
#{Bundler::VERSION}
L
- bundle "install", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ bundle "install", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
end
context "when an update is available" do
- let(:repo) { gem_repo2 }
-
- before do
- lockfile(@lockfile)
+ let(:repo) do
build_repo2 do
build_gem "foo", "2.0"
end
+ gem_repo2
+ end
+
+ before do
+ lockfile(@lockfile)
end
it "does not implicitly update" do
bundle "lock"
- expect(read_lockfile).to eq(@lockfile)
+ checksums = checksums_section_when_existing do |c|
+ c.checksum repo, "actionmailer", "2.3.2"
+ c.checksum repo, "actionpack", "2.3.2"
+ c.checksum repo, "activerecord", "2.3.2"
+ c.checksum repo, "activeresource", "2.3.2"
+ c.checksum repo, "activesupport", "2.3.2"
+ c.checksum repo, "foo", "1.0"
+ c.checksum repo, "rails", "2.3.2"
+ c.checksum repo, "rake", rake_version
+ c.checksum repo, "weakling", "0.0.3"
+ end
+
+ expected_lockfile = <<~L
+ GEM
+ remote: #{file_uri_for(repo)}/
+ specs:
+ actionmailer (2.3.2)
+ activesupport (= 2.3.2)
+ actionpack (2.3.2)
+ activesupport (= 2.3.2)
+ activerecord (2.3.2)
+ activesupport (= 2.3.2)
+ activeresource (2.3.2)
+ activesupport (= 2.3.2)
+ activesupport (2.3.2)
+ foo (1.0)
+ rails (2.3.2)
+ actionmailer (= 2.3.2)
+ actionpack (= 2.3.2)
+ activerecord (= 2.3.2)
+ activeresource (= 2.3.2)
+ rake (= #{rake_version})
+ rake (#{rake_version})
+ weakling (0.0.3)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo
+ rails
+ weakling
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ expect(read_lockfile).to eq(expected_lockfile)
end
it "accounts for changes in the gemfile" do
gemfile gemfile.gsub('"foo"', '"foo", "2.0"')
bundle "lock"
- expect(read_lockfile).to eq(@lockfile.sub("foo (1.0)", "foo (2.0)").sub(/foo$/, "foo (= 2.0)"))
+ checksums = checksums_section_when_existing do |c|
+ c.checksum repo, "actionmailer", "2.3.2"
+ c.checksum repo, "actionpack", "2.3.2"
+ c.checksum repo, "activerecord", "2.3.2"
+ c.checksum repo, "activeresource", "2.3.2"
+ c.checksum repo, "activesupport", "2.3.2"
+ c.no_checksum "foo", "2.0"
+ c.checksum repo, "rails", "2.3.2"
+ c.checksum repo, "rake", rake_version
+ c.checksum repo, "weakling", "0.0.3"
+ end
+
+ expected_lockfile = <<~L
+ GEM
+ remote: #{file_uri_for(repo)}/
+ specs:
+ actionmailer (2.3.2)
+ activesupport (= 2.3.2)
+ actionpack (2.3.2)
+ activesupport (= 2.3.2)
+ activerecord (2.3.2)
+ activesupport (= 2.3.2)
+ activeresource (2.3.2)
+ activesupport (= 2.3.2)
+ activesupport (2.3.2)
+ foo (2.0)
+ rails (2.3.2)
+ actionmailer (= 2.3.2)
+ actionpack (= 2.3.2)
+ activerecord (= 2.3.2)
+ activeresource (= 2.3.2)
+ rake (= #{rake_version})
+ rake (#{rake_version})
+ weakling (0.0.3)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo (= 2.0)
+ rails
+ weakling
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ expect(read_lockfile).to eq(expected_lockfile)
end
end
@@ -822,8 +1250,8 @@ RSpec.describe "bundle lock" do
build_gem "irb", "1.5.0"
end
- system_gems "irb-1.5.0", :gem_repo => gem_repo4
- system_gems "debug-1.6.3", :gem_repo => gem_repo4
+ system_gems "irb-1.5.0", gem_repo: gem_repo4
+ system_gems "debug-1.6.3", gem_repo: gem_repo4
# simulate gemspec with wrong empty dependencies
debug_gemspec_path = system_gem_path("specifications/debug-1.6.3.gemspec")
@@ -852,7 +1280,7 @@ RSpec.describe "bundle lock" do
DEPENDENCIES
debug
-
+ #{checksums_section}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -861,6 +1289,11 @@ RSpec.describe "bundle lock" do
bundle "lock"
end
+ checksums = checksums_section do |c|
+ c.no_checksum "debug", "1.6.3"
+ c.no_checksum "irb", "1.5.0"
+ end
+
expect(lockfile).to eq <<~L
GEM
remote: #{file_uri_for(gem_repo4)}/
@@ -875,7 +1308,7 @@ RSpec.describe "bundle lock" do
DEPENDENCIES
debug
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -908,7 +1341,7 @@ RSpec.describe "bundle lock" do
gem "activeadmin", "2.13.1"
G
- bundle "lock", :raise_on_error => false
+ bundle "lock", raise_on_error: false
expect(err).to eq <<~ERR.strip
Could not find compatible versions
@@ -916,7 +1349,7 @@ RSpec.describe "bundle lock" do
Because rails >= 7.0.4 depends on railties = 7.0.4
and rails < 7.0.4 depends on railties = 7.0.3.1,
railties = 7.0.3.1 OR = 7.0.4 is required.
- So, because railties = 7.0.3.1 OR = 7.0.4 could not be found in rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally,
+ So, because railties = 7.0.3.1 OR = 7.0.4 could not be found in rubygems repository #{file_uri_for(gem_repo4)}/, cached gems or installed locally,
version solving has failed.
ERR
end
@@ -973,7 +1406,7 @@ RSpec.describe "bundle lock" do
activemodel (>= 6.0.4)
PLATFORMS
- #{lockfile_platforms}
+ #{local_platform}
DEPENDENCIES
activeadmin (= 2.13.1)
@@ -983,7 +1416,7 @@ RSpec.describe "bundle lock" do
#{Bundler::VERSION}
L
- bundle "lock", :raise_on_error => false
+ bundle "lock", raise_on_error: false
expect(err).to eq <<~ERR.strip
Could not find compatible versions
@@ -1009,9 +1442,9 @@ RSpec.describe "bundle lock" do
version solving has failed.
ERR
- lockfile lockfile.gsub(/PLATFORMS\n #{lockfile_platforms}/m, "PLATFORMS\n #{lockfile_platforms("ruby")}")
+ lockfile lockfile.gsub(/PLATFORMS\n #{local_platform}/m, "PLATFORMS\n #{lockfile_platforms("ruby")}")
- bundle "lock", :raise_on_error => false
+ bundle "lock", raise_on_error: false
expect(err).to eq <<~ERR.strip
Could not find compatible versions
@@ -1027,7 +1460,7 @@ RSpec.describe "bundle lock" do
Thus, rails >= 7.0.2.3, < 7.0.4 cannot be used.
And because rails >= 7.0.4 depends on activemodel = 7.0.4,
rails >= 7.0.2.3 requires activemodel = 7.0.4.
- So, because activemodel = 7.0.4 could not be found in rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally
+ So, because activemodel = 7.0.4 could not be found in rubygems repository #{file_uri_for(gem_repo4)}/, cached gems or installed locally
and Gemfile depends on rails >= 7.0.2.3,
version solving has failed.
ERR
@@ -1104,7 +1537,7 @@ RSpec.describe "bundle lock" do
G
simulate_platform "universal-java-19" do
- bundle "lock", :raise_on_error => false
+ bundle "lock", raise_on_error: false
end
expect(err).to include("Could not find compatible versions")
@@ -1139,7 +1572,7 @@ RSpec.describe "bundle lock" do
context "when resolving platform specific gems as indirect dependencies on truffleruby", :truffleruby_only do
before do
- build_lib "foo", :path => bundled_app do |s|
+ build_lib "foo", path: bundled_app do |s|
s.add_dependency "nokogiri"
end
@@ -1157,6 +1590,11 @@ RSpec.describe "bundle lock" do
end
it "locks ruby specs" do
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "foo", "1.0"
+ c.no_checksum "nokogiri", "1.14.2"
+ end
+
simulate_platform "x86_64-linux" do
bundle "lock"
end
@@ -1174,11 +1612,11 @@ RSpec.describe "bundle lock" do
nokogiri (1.14.2)
PLATFORMS
- x86_64-linux
+ #{lockfile_platforms}
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -1239,6 +1677,13 @@ RSpec.describe "bundle lock" do
end
it "does not downgrade top level dependencies" do
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "actionpack", "7.0.4.3"
+ c.no_checksum "activesupport", "7.0.4.3"
+ c.no_checksum "govuk_app_config", "4.13.0"
+ c.no_checksum "railties", "7.0.4.3"
+ end
+
simulate_platform "arm64-darwin-22" do
bundle "lock"
end
@@ -1261,7 +1706,7 @@ RSpec.describe "bundle lock" do
DEPENDENCIES
activesupport (= 7.0.4.3)
govuk_app_config
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
diff --git a/spec/bundler/commands/newgem_spec.rb b/spec/bundler/commands/newgem_spec.rb
index 970e51b8ef..199340b131 100644
--- a/spec/bundler/commands/newgem_spec.rb
+++ b/spec/bundler/commands/newgem_spec.rb
@@ -12,14 +12,14 @@ RSpec.describe "bundle gem" do
def bundle_exec_rubocop
prepare_gemspec(bundled_app(gem_name, "#{gem_name}.gemspec"))
- bundle "config set path #{rubocop_gems}", :dir => bundled_app(gem_name)
- bundle "exec rubocop --debug --config .rubocop.yml", :dir => bundled_app(gem_name)
+ bundle "config set path #{rubocop_gems}", dir: bundled_app(gem_name)
+ bundle "exec rubocop --debug --config .rubocop.yml", dir: bundled_app(gem_name)
end
def bundle_exec_standardrb
prepare_gemspec(bundled_app(gem_name, "#{gem_name}.gemspec"))
- bundle "config set path #{standard_gems}", :dir => bundled_app(gem_name)
- bundle "exec standardrb --debug", :dir => bundled_app(gem_name)
+ bundle "config set path #{standard_gems}", dir: bundled_app(gem_name)
+ bundle "exec standardrb --debug", dir: bundled_app(gem_name)
end
let(:generated_gemspec) { Bundler.load_gemspec_uncached(bundled_app(gem_name).join("#{gem_name}.gemspec")) }
@@ -63,7 +63,7 @@ RSpec.describe "bundle gem" do
end
it "properly initializes git repo", :readline do
- bundle "gem #{gem_name}", :dir => bundled_app("path with spaces")
+ bundle "gem #{gem_name}", dir: bundled_app("path with spaces")
expect(bundled_app("path with spaces/#{gem_name}/.git")).to exist
end
end
@@ -103,7 +103,7 @@ RSpec.describe "bundle gem" do
expect(bundled_app("#{gem_name}/README.md").read).to match(%r{https://github\.com/bundleuser/#{gem_name}/blob/.*/CODE_OF_CONDUCT.md})
end
- it "generates the README with a section for the Code of Conduct, respecting the configured git default branch", :git => ">= 2.28.0" do
+ it "generates the README with a section for the Code of Conduct, respecting the configured git default branch", git: ">= 2.28.0" do
sys_exec("git config --global init.defaultBranch main")
bundle "gem #{gem_name} --coc"
@@ -148,7 +148,7 @@ RSpec.describe "bundle gem" do
end
shared_examples_for "--rubocop flag" do
- context "is deprecated", :bundler => "< 3" do
+ context "is deprecated", bundler: "< 3" do
before do
bundle "gem #{gem_name} --rubocop"
end
@@ -179,7 +179,7 @@ RSpec.describe "bundle gem" do
end
shared_examples_for "--no-rubocop flag" do
- context "is deprecated", :bundler => "< 3" do
+ context "is deprecated", bundler: "< 3" do
define_negated_matcher :exclude, :include
before do
@@ -435,7 +435,7 @@ RSpec.describe "bundle gem" do
load_paths = [lib_dir, spec_dir]
load_path_str = "-I#{load_paths.join(File::PATH_SEPARATOR)}"
- sys_exec "#{Gem.ruby} #{load_path_str} #{bindir.join("bundle")} gem #{gem_name}", :env => { "PATH" => "" }
+ sys_exec "#{Gem.ruby} #{load_path_str} #{bindir.join("bundle")} gem #{gem_name}", env: { "PATH" => "" }
end
it "creates the gem without the need for git" do
@@ -456,10 +456,10 @@ RSpec.describe "bundle gem" do
prepare_gemspec(bundled_app("newgem", "newgem.gemspec"))
- gems = ["rake-13.0.1"]
- path = Bundler.feature_flag.default_install_uses_path? ? local_gem_path(:base => bundled_app("newgem")) : system_gem_path
- system_gems gems, :path => path
- bundle "exec rake build", :dir => bundled_app("newgem")
+ gems = ["rake-#{rake_version}"]
+ path = Bundler.feature_flag.default_install_uses_path? ? local_gem_path(base: bundled_app("newgem")) : system_gem_path
+ system_gems gems, path: path
+ bundle "exec rake build", dir: bundled_app("newgem")
expect(last_command.stdboth).not_to include("ERROR")
end
@@ -468,7 +468,7 @@ RSpec.describe "bundle gem" do
it "resolves ." do
create_temporary_dir("tmp")
- bundle "gem .", :dir => bundled_app("tmp")
+ bundle "gem .", dir: bundled_app("tmp")
expect(bundled_app("tmp/lib/tmp.rb")).to exist
end
@@ -476,7 +476,7 @@ RSpec.describe "bundle gem" do
it "resolves .." do
create_temporary_dir("temp/empty_dir")
- bundle "gem ..", :dir => bundled_app("temp/empty_dir")
+ bundle "gem ..", dir: bundled_app("temp/empty_dir")
expect(bundled_app("temp/lib/temp.rb")).to exist
end
@@ -484,7 +484,7 @@ RSpec.describe "bundle gem" do
it "resolves relative directory" do
create_temporary_dir("tmp/empty/tmp")
- bundle "gem ../../empty", :dir => bundled_app("tmp/empty/tmp")
+ bundle "gem ../../empty", dir: bundled_app("tmp/empty/tmp")
expect(bundled_app("tmp/empty/lib/empty.rb")).to exist
end
@@ -638,12 +638,20 @@ RSpec.describe "bundle gem" do
expect(bundler_gemspec.files).not_to include("#{gem_name}.gemspec")
end
+ it "does not include the Gemfile file in files" do
+ bundle "gem #{gem_name}"
+
+ bundler_gemspec = Bundler::GemHelper.new(bundled_app(gem_name), gem_name).gemspec
+
+ expect(bundler_gemspec.files).not_to include("Gemfile")
+ end
+
it "runs rake without problems" do
bundle "gem #{gem_name}"
- system_gems ["rake-13.0.1"]
+ system_gems ["rake-#{rake_version}"]
- rakefile = strip_whitespace <<-RAKEFILE
+ rakefile = <<~RAKEFILE
task :default do
puts 'SUCCESS'
end
@@ -652,7 +660,7 @@ RSpec.describe "bundle gem" do
file.puts rakefile
end
- sys_exec(rake, :dir => bundled_app(gem_name))
+ sys_exec(rake, dir: bundled_app(gem_name))
expect(out).to include("SUCCESS")
end
@@ -789,17 +797,13 @@ RSpec.describe "bundle gem" do
end
it "creates a default rake task to run the test suite" do
- rakefile = strip_whitespace <<-RAKEFILE
+ rakefile = <<~RAKEFILE
# frozen_string_literal: true
require "bundler/gem_tasks"
- require "rake/testtask"
+ require "minitest/test_task"
- Rake::TestTask.new(:test) do |t|
- t.libs << "test"
- t.libs << "lib"
- t.test_files = FileList["test/**/test_*.rb"]
- end
+ Minitest::TestTask.create
task default: :test
RAKEFILE
@@ -847,7 +851,7 @@ RSpec.describe "bundle gem" do
end
it "creates a default rake task to run the test suite" do
- rakefile = strip_whitespace <<-RAKEFILE
+ rakefile = <<~RAKEFILE
# frozen_string_literal: true
require "bundler/gem_tasks"
@@ -928,29 +932,17 @@ RSpec.describe "bundle gem" do
end
end
- context "--ci set to travis" do
- it "generates a GitHub Actions config file" do
- bundle "gem #{gem_name} --ci=travis", :raise_on_error => false
- expect(err).to include("Support for Travis CI was removed from gem skeleton generator.")
-
- expect(bundled_app("#{gem_name}/.travis.yml")).to_not exist
- end
- end
-
- context "--ci set to travis" do
+ context "--ci set to github" do
it "generates a GitHub Actions config file" do
- bundle "gem #{gem_name} --ci=travis", :raise_on_error => false
- expect(err).to include("Support for Travis CI was removed from gem skeleton generator.")
+ bundle "gem #{gem_name} --ci=github"
- expect(bundled_app("#{gem_name}/.travis.yml")).to_not exist
+ expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to exist
end
- end
- context "--ci set to github" do
- it "generates a GitHub Actions config file" do
+ it "contained .gitlab-ci.yml into ignore list" do
bundle "gem #{gem_name} --ci=github"
- expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to exist
+ expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to include(".git .github appveyor")
end
end
@@ -960,6 +952,12 @@ RSpec.describe "bundle gem" do
expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to exist
end
+
+ it "contained .gitlab-ci.yml into ignore list" do
+ bundle "gem #{gem_name} --ci=gitlab"
+
+ expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to include(".git .gitlab-ci.yml appveyor")
+ end
end
context "--ci set to circle" do
@@ -968,6 +966,12 @@ RSpec.describe "bundle gem" do
expect(bundled_app("#{gem_name}/.circleci/config.yml")).to exist
end
+
+ it "contained .circleci into ignore list" do
+ bundle "gem #{gem_name} --ci=circle"
+
+ expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to include(".git .circleci appveyor")
+ end
end
context "gem.ci setting set to none" do
@@ -978,18 +982,6 @@ RSpec.describe "bundle gem" do
end
end
- context "gem.ci setting set to travis" do
- it "errors with friendly message" do
- bundle "config set gem.ci travis"
- bundle "gem #{gem_name}", :raise_on_error => false
-
- expect(err).to include("Support for Travis CI was removed from gem skeleton generator,")
- expect(err).to include("bundle config unset gem.ci")
-
- expect(bundled_app("#{gem_name}/.travis.yml")).to_not exist
- end
- end
-
context "gem.ci setting set to github" do
it "generates a GitHub Actions config file" do
bundle "config set gem.ci github"
@@ -1117,7 +1109,7 @@ RSpec.describe "bundle gem" do
end
end
- context "gem.rubocop setting set to true", :bundler => "< 3" do
+ context "gem.rubocop setting set to true", bundler: "< 3" do
before do
bundle "config set gem.rubocop true"
bundle "gem #{gem_name}"
@@ -1369,7 +1361,7 @@ RSpec.describe "bundle gem" do
include_examples "generating a gem"
context "--ext parameter with no value" do
- context "is deprecated", :bundler => "< 3" do
+ context "is deprecated", bundler: "< 3" do
it "prints deprecation when used after gem name" do
bundle ["gem", "--ext", gem_name].compact.join(" ")
expect(err).to include "[DEPRECATED]"
@@ -1411,7 +1403,7 @@ RSpec.describe "bundle gem" do
end
it "depends on compile task for build" do
- rakefile = strip_whitespace <<-RAKEFILE
+ rakefile = <<~RAKEFILE
# frozen_string_literal: true
require "bundler/gem_tasks"
@@ -1419,7 +1411,9 @@ RSpec.describe "bundle gem" do
task build: :compile
- Rake::ExtensionTask.new("#{gem_name}") do |ext|
+ GEMSPEC = Gem::Specification.load("#{gem_name}.gemspec")
+
+ Rake::ExtensionTask.new("#{gem_name}", GEMSPEC) do |ext|
ext.lib_dir = "lib/#{gem_name}"
end
@@ -1469,7 +1463,7 @@ RSpec.describe "bundle gem" do
end
it "depends on compile task for build" do
- rakefile = strip_whitespace <<-RAKEFILE
+ rakefile = <<~RAKEFILE
# frozen_string_literal: true
require "bundler/gem_tasks"
@@ -1477,7 +1471,9 @@ RSpec.describe "bundle gem" do
task build: :compile
- RbSys::ExtensionTask.new("#{gem_name}") do |ext|
+ GEMSPEC = Gem::Specification.load("#{gem_name}.gemspec")
+
+ RbSys::ExtensionTask.new("#{gem_name}", GEMSPEC) do |ext|
ext.lib_dir = "lib/#{gem_name}"
end
@@ -1517,22 +1513,22 @@ RSpec.describe "bundle gem" do
end
it "fails gracefully with a ." do
- bundle "gem foo.gemspec", :raise_on_error => false
+ bundle "gem foo.gemspec", raise_on_error: false
expect(err).to end_with("Invalid gem name foo.gemspec -- `Foo.gemspec` is an invalid constant name")
end
it "fails gracefully with a ^" do
- bundle "gem ^", :raise_on_error => false
+ bundle "gem ^", raise_on_error: false
expect(err).to end_with("Invalid gem name ^ -- `^` is an invalid constant name")
end
it "fails gracefully with a space" do
- bundle "gem 'foo bar'", :raise_on_error => false
+ bundle "gem 'foo bar'", raise_on_error: false
expect(err).to end_with("Invalid gem name foo bar -- `Foo bar` is an invalid constant name")
end
it "fails gracefully when multiple names are passed" do
- bundle "gem foo bar baz", :raise_on_error => false
+ bundle "gem foo bar baz", raise_on_error: false
expect(err).to eq(<<-E.strip)
ERROR: "bundle gem" was called with arguments ["foo", "bar", "baz"]
Usage: "bundle gem NAME [OPTIONS]"
@@ -1542,7 +1538,7 @@ Usage: "bundle gem NAME [OPTIONS]"
describe "#ensure_safe_gem_name", :readline do
before do
- bundle "gem #{subject}", :raise_on_error => false
+ bundle "gem #{subject}", raise_on_error: false
end
context "with an existing const name" do
@@ -1575,7 +1571,7 @@ Usage: "bundle gem NAME [OPTIONS]"
end
expect(bundled_app("foobar/spec/spec_helper.rb")).to exist
- rakefile = strip_whitespace <<-RAKEFILE
+ rakefile = <<~RAKEFILE
# frozen_string_literal: true
require "bundler/gem_tasks"
@@ -1637,7 +1633,7 @@ Usage: "bundle gem NAME [OPTIONS]"
context "on conflicts with a previously created file", :readline do
it "should fail gracefully" do
FileUtils.touch(bundled_app("conflict-foobar"))
- bundle "gem conflict-foobar", :raise_on_error => false
+ bundle "gem conflict-foobar", raise_on_error: false
expect(err).to eq("Couldn't create a new gem named `conflict-foobar` because there's an existing file named `conflict-foobar`.")
expect(exitstatus).to eql(32)
end
diff --git a/spec/bundler/commands/open_spec.rb b/spec/bundler/commands/open_spec.rb
index e30ebfea5b..97374f30c3 100644
--- a/spec/bundler/commands/open_spec.rb
+++ b/spec/bundler/commands/open_spec.rb
@@ -10,27 +10,27 @@ RSpec.describe "bundle open" do
end
it "opens the gem with BUNDLER_EDITOR as highest priority" do
- bundle "open rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
+ bundle "open rails", env: { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
expect(out).to include("bundler_editor #{default_bundle_path("gems", "rails-2.3.2")}")
end
it "opens the gem with VISUAL as 2nd highest priority" do
- bundle "open rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "" }
+ bundle "open rails", env: { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "" }
expect(out).to include("visual #{default_bundle_path("gems", "rails-2.3.2")}")
end
it "opens the gem with EDITOR as 3rd highest priority" do
- bundle "open rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ bundle "open rails", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
expect(out).to include("editor #{default_bundle_path("gems", "rails-2.3.2")}")
end
it "complains if no EDITOR is set" do
- bundle "open rails", :env => { "EDITOR" => "", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ bundle "open rails", env: { "EDITOR" => "", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
expect(out).to eq("To open a bundled gem, set $EDITOR or $BUNDLER_EDITOR")
end
it "complains if gem not in bundle" do
- bundle "open missing", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, :raise_on_error => false
+ bundle "open missing", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, raise_on_error: false
expect(err).to match(/could not find gem 'missing'/i)
end
@@ -43,91 +43,91 @@ RSpec.describe "bundle open" do
gem 'foo', :git => "#{lib_path("foo-1.0")}"
G
- bundle "open foo", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
- expect(out).to match("editor #{default_bundle_path.join("bundler/gems/foo-1.0-#{ref}")}")
+ bundle "open foo", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to include("editor #{default_bundle_path.join("bundler", "gems", "foo-1.0-#{ref}")}")
end
it "suggests alternatives for similar-sounding gems" do
- bundle "open Rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, :raise_on_error => false
+ bundle "open Rails", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, raise_on_error: false
expect(err).to match(/did you mean rails\?/i)
end
it "opens the gem with short words" do
- bundle "open rec", :env => { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
+ bundle "open rec", env: { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
expect(out).to include("bundler_editor #{default_bundle_path("gems", "activerecord-2.3.2")}")
end
it "opens subpath of the gem" do
- bundle "open activerecord --path lib/activerecord", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ bundle "open activerecord --path lib/activerecord", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/lib/activerecord")
end
it "opens subpath file of the gem" do
- bundle "open activerecord --path lib/version.rb", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ bundle "open activerecord --path lib/version.rb", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/lib/version.rb")
end
it "opens deep subpath of the gem" do
- bundle "open activerecord --path lib/active_record", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ bundle "open activerecord --path lib/active_record", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/lib/active_record")
end
it "requires value for --path arg" do
- bundle "open activerecord --path", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, :raise_on_error => false
+ bundle "open activerecord --path", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, raise_on_error: false
expect(err).to eq "Cannot specify `--path` option without a value"
end
it "suggests alternatives for similar-sounding gems when using subpath" do
- bundle "open Rails --path README.md", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, :raise_on_error => false
+ bundle "open Rails --path README.md", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, raise_on_error: false
expect(err).to match(/did you mean rails\?/i)
end
it "suggests alternatives for similar-sounding gems when using deep subpath" do
- bundle "open Rails --path some/path/here", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, :raise_on_error => false
+ bundle "open Rails --path some/path/here", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, raise_on_error: false
expect(err).to match(/did you mean rails\?/i)
end
it "opens subpath of the short worded gem" do
- bundle "open rec --path CHANGELOG.md", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ bundle "open rec --path CHANGELOG.md", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/CHANGELOG.md")
end
it "opens deep subpath of the short worded gem" do
- bundle "open rec --path lib/activerecord", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ bundle "open rec --path lib/activerecord", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/lib/activerecord")
end
it "opens subpath of the selected matching gem", :readline do
env = { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
- bundle "open active --path CHANGELOG.md", :env => env do |input, _, _|
+ bundle "open active --path CHANGELOG.md", env: env do |input, _, _|
input.puts "2"
end
- expect(out).to match(%r{bundler_editor #{default_bundle_path('gems', 'activerecord-2.3.2')}/CHANGELOG\.md\z})
+ expect(out).to include("bundler_editor #{default_bundle_path("gems", "activerecord-2.3.2").join("CHANGELOG.md")}")
end
it "opens deep subpath of the selected matching gem", :readline do
env = { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
- bundle "open active --path lib/activerecord/version.rb", :env => env do |input, _, _|
+ bundle "open active --path lib/activerecord/version.rb", env: env do |input, _, _|
input.puts "2"
end
- expect(out).to match(%r{bundler_editor #{default_bundle_path('gems', 'activerecord-2.3.2')}/lib/activerecord/version\.rb\z})
+ expect(out).to include("bundler_editor #{default_bundle_path("gems", "activerecord-2.3.2").join("lib", "activerecord", "version.rb")}")
end
it "select the gem from many match gems", :readline do
env = { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
- bundle "open active", :env => env do |input, _, _|
+ bundle "open active", env: env do |input, _, _|
input.puts "2"
end
- expect(out).to match(/bundler_editor #{default_bundle_path('gems', 'activerecord-2.3.2')}\z/)
+ expect(out).to include("bundler_editor #{default_bundle_path("gems", "activerecord-2.3.2")}")
end
it "allows selecting exit from many match gems", :readline do
env = { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
- bundle "open active", :env => env do |input, _, _|
+ bundle "open active", env: env do |input, _, _|
input.puts "0"
end
end
@@ -140,12 +140,12 @@ RSpec.describe "bundle open" do
G
bundle "config set auto_install 1"
- bundle "open rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ bundle "open rails", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
expect(out).to include("Installing foo 1.0")
end
it "opens the editor with a clean env" do
- bundle "open", :env => { "EDITOR" => "sh -c 'env'", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, :raise_on_error => false
+ bundle "open", env: { "EDITOR" => "sh -c 'env'", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, raise_on_error: false
expect(out).not_to include("BUNDLE_GEMFILE=")
end
end
@@ -169,7 +169,7 @@ RSpec.describe "bundle open" do
end
it "throws proper error when trying to open default gem" do
- bundle "open json", :env => { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
+ bundle "open json", env: { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
expect(out).to include("Unable to open json because it's a default gem, so the directory it would normally be installed to does not exist.")
end
end
diff --git a/spec/bundler/commands/outdated_spec.rb b/spec/bundler/commands/outdated_spec.rb
index bbc3cb54ae..e7edc67e57 100644
--- a/spec/bundler/commands/outdated_spec.rb
+++ b/spec/bundler/commands/outdated_spec.rb
@@ -4,8 +4,8 @@ RSpec.describe "bundle outdated" do
describe "with no arguments" do
before do
build_repo2 do
- build_git "foo", :path => lib_path("foo")
- build_git "zebra", :path => lib_path("zebra")
+ build_git "foo", path: lib_path("foo")
+ build_git "zebra", path: lib_path("zebra")
end
install_gemfile <<-G
@@ -23,11 +23,11 @@ RSpec.describe "bundle outdated" do
update_repo2 do
build_gem "activesupport", "3.0"
build_gem "weakling", "0.2"
- update_git "foo", :path => lib_path("foo")
- update_git "zebra", :path => lib_path("zebra")
+ update_git "foo", path: lib_path("foo")
+ update_git "zebra", path: lib_path("zebra")
end
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
expected_output = <<~TABLE.gsub("x", "\\\h").tr(".", "\.").strip
Gem Current Latest Requested Groups
@@ -50,7 +50,7 @@ RSpec.describe "bundle outdated" do
gem "AAA", "1.0.0"
G
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
expected_output = <<~TABLE
Gem Current Latest Requested Groups
@@ -63,10 +63,10 @@ RSpec.describe "bundle outdated" do
it "returns non zero exit status if outdated gems present" do
update_repo2 do
build_gem "activesupport", "3.0"
- update_git "foo", :path => lib_path("foo")
+ update_git "foo", path: lib_path("foo")
end
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
expect(exitstatus).to_not be_zero
end
@@ -89,7 +89,7 @@ RSpec.describe "bundle outdated" do
update_repo2 { build_gem "activesupport", "3.0" }
update_repo2 { build_gem "terranova", "9" }
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
expected_output = <<~TABLE.strip
Gem Current Latest Requested Groups
@@ -104,8 +104,8 @@ RSpec.describe "bundle outdated" do
describe "with --verbose option" do
before do
build_repo2 do
- build_git "foo", :path => lib_path("foo")
- build_git "zebra", :path => lib_path("zebra")
+ build_git "foo", path: lib_path("foo")
+ build_git "zebra", path: lib_path("zebra")
end
install_gemfile <<-G
@@ -139,7 +139,7 @@ RSpec.describe "bundle outdated" do
gem 'activesupport', '2.3.5'
G
- bundle "outdated --verbose", :raise_on_error => false
+ bundle "outdated --verbose", raise_on_error: false
expected_output = <<~TABLE.strip
Gem Current Latest Requested Groups Path
@@ -151,7 +151,7 @@ RSpec.describe "bundle outdated" do
end
end
- describe "with multiple, duplicated sources, with lockfile in old format", :bundler => "< 3" do
+ describe "with multiple, duplicated sources, with lockfile in old format", bundler: "< 3" do
before do
build_repo2 do
build_gem "dotenv", "2.7.6"
@@ -205,8 +205,8 @@ RSpec.describe "bundle outdated" do
end
it "works" do
- bundle :install, :artifice => "compact_index"
- bundle :outdated, :artifice => "compact_index", :raise_on_error => false
+ bundle :install, artifice: "compact_index"
+ bundle :outdated, artifice: "compact_index", raise_on_error: false
expected_output = <<~TABLE
Gem Current Latest Requested Groups
@@ -220,8 +220,8 @@ RSpec.describe "bundle outdated" do
describe "with --group option" do
before do
build_repo2 do
- build_git "foo", :path => lib_path("foo")
- build_git "zebra", :path => lib_path("zebra")
+ build_git "foo", path: lib_path("foo")
+ build_git "zebra", path: lib_path("zebra")
end
install_gemfile <<-G
@@ -243,7 +243,7 @@ RSpec.describe "bundle outdated" do
build_gem "duradura", "8.0"
end
- bundle "outdated --group #{group}", :raise_on_error => false
+ bundle "outdated --group #{group}", raise_on_error: false
end
it "works when the bundle is up to date" do
@@ -290,8 +290,8 @@ RSpec.describe "bundle outdated" do
describe "with --groups option and outdated transitive dependencies" do
before do
build_repo2 do
- build_git "foo", :path => lib_path("foo")
- build_git "zebra", :path => lib_path("zebra")
+ build_git "foo", path: lib_path("foo")
+ build_git "zebra", path: lib_path("zebra")
build_gem "bar", %w[2.0.0]
@@ -312,7 +312,7 @@ RSpec.describe "bundle outdated" do
end
it "returns a sorted list of outdated gems" do
- bundle "outdated --groups", :raise_on_error => false
+ bundle "outdated --groups", raise_on_error: false
expected_output = <<~TABLE.strip
Gem Current Latest Requested Groups
@@ -326,8 +326,8 @@ RSpec.describe "bundle outdated" do
describe "with --groups option" do
before do
build_repo2 do
- build_git "foo", :path => lib_path("foo")
- build_git "zebra", :path => lib_path("zebra")
+ build_git "foo", path: lib_path("foo")
+ build_git "zebra", path: lib_path("zebra")
end
install_gemfile <<-G
@@ -354,7 +354,7 @@ RSpec.describe "bundle outdated" do
build_gem "duradura", "8.0"
end
- bundle "outdated --groups", :raise_on_error => false
+ bundle "outdated --groups", raise_on_error: false
expected_output = <<~TABLE.strip
Gem Current Latest Requested Groups
@@ -370,8 +370,8 @@ RSpec.describe "bundle outdated" do
describe "with --local option" do
before do
build_repo2 do
- build_git "foo", :path => lib_path("foo")
- build_git "zebra", :path => lib_path("zebra")
+ build_git "foo", path: lib_path("foo")
+ build_git "zebra", path: lib_path("zebra")
end
install_gemfile <<-G
@@ -398,7 +398,7 @@ RSpec.describe "bundle outdated" do
gem "activesupport", "2.3.4"
G
- bundle "outdated --local", :raise_on_error => false
+ bundle "outdated --local", raise_on_error: false
expected_output = <<~TABLE.strip
Gem Current Latest Requested Groups
@@ -420,8 +420,8 @@ RSpec.describe "bundle outdated" do
context "and gems are outdated" do
before do
build_repo2 do
- build_git "foo", :path => lib_path("foo")
- build_git "zebra", :path => lib_path("zebra")
+ build_git "foo", path: lib_path("foo")
+ build_git "zebra", path: lib_path("zebra")
build_gem "activesupport", "3.0"
build_gem "weakling", "0.2"
@@ -455,13 +455,13 @@ RSpec.describe "bundle outdated" do
end
describe "with --parseable option" do
- subject { bundle "outdated --parseable", :raise_on_error => false }
+ subject { bundle "outdated --parseable", raise_on_error: false }
it_behaves_like "a minimal output is desired"
end
describe "with aliased --porcelain option" do
- subject { bundle "outdated --porcelain", :raise_on_error => false }
+ subject { bundle "outdated --porcelain", raise_on_error: false }
it_behaves_like "a minimal output is desired"
end
@@ -469,8 +469,8 @@ RSpec.describe "bundle outdated" do
describe "with specified gems" do
it "returns list of outdated gems" do
build_repo2 do
- build_git "foo", :path => lib_path("foo")
- build_git "zebra", :path => lib_path("zebra")
+ build_git "foo", path: lib_path("foo")
+ build_git "zebra", path: lib_path("zebra")
end
install_gemfile <<-G
@@ -485,10 +485,10 @@ RSpec.describe "bundle outdated" do
update_repo2 do
build_gem "activesupport", "3.0"
- update_git "foo", :path => lib_path("foo")
+ update_git "foo", path: lib_path("foo")
end
- bundle "outdated foo", :raise_on_error => false
+ bundle "outdated foo", raise_on_error: false
expected_output = <<~TABLE.gsub("x", "\\\h").tr(".", "\.").strip
Gem Current Latest Requested Groups
@@ -502,8 +502,8 @@ RSpec.describe "bundle outdated" do
describe "pre-release gems" do
before do
build_repo2 do
- build_git "foo", :path => lib_path("foo")
- build_git "zebra", :path => lib_path("zebra")
+ build_git "foo", path: lib_path("foo")
+ build_git "zebra", path: lib_path("zebra")
end
install_gemfile <<-G
@@ -535,7 +535,7 @@ RSpec.describe "bundle outdated" do
build_gem "activesupport", "3.0.0.beta"
end
- bundle "outdated --pre", :raise_on_error => false
+ bundle "outdated --pre", raise_on_error: false
expected_output = <<~TABLE.strip
Gem Current Latest Requested Groups
@@ -558,7 +558,7 @@ RSpec.describe "bundle outdated" do
gem "activesupport", "3.0.0.beta.1"
G
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
expected_output = <<~TABLE.strip
Gem Current Latest Requested Groups
@@ -573,8 +573,8 @@ RSpec.describe "bundle outdated" do
describe "with --filter-strict option" do
before do
build_repo2 do
- build_git "foo", :path => lib_path("foo")
- build_git "zebra", :path => lib_path("zebra")
+ build_git "foo", path: lib_path("foo")
+ build_git "zebra", path: lib_path("zebra")
end
install_gemfile <<-G
@@ -594,7 +594,7 @@ RSpec.describe "bundle outdated" do
build_gem "weakling", "0.0.5"
end
- bundle :outdated, :"filter-strict" => true, :raise_on_error => false
+ bundle :outdated, "filter-strict": true, raise_on_error: false
expected_output = <<~TABLE.strip
Gem Current Latest Requested Groups
@@ -610,7 +610,7 @@ RSpec.describe "bundle outdated" do
build_gem "weakling", "0.0.5"
end
- bundle :outdated, :strict => true, :raise_on_error => false
+ bundle :outdated, strict: true, raise_on_error: false
expected_output = <<~TABLE.strip
Gem Current Latest Requested Groups
@@ -626,7 +626,7 @@ RSpec.describe "bundle outdated" do
gem "activesupport", platforms: [:ruby_22]
G
- bundle :outdated, :"filter-strict" => true
+ bundle :outdated, "filter-strict": true
expect(out).to end_with("Bundle up to date!")
end
@@ -637,7 +637,7 @@ RSpec.describe "bundle outdated" do
gem "rack_middleware", "1.0"
G
- bundle :outdated, :"filter-strict" => true
+ bundle :outdated, "filter-strict": true
expect(out).to end_with("Bundle up to date!")
end
@@ -714,8 +714,8 @@ RSpec.describe "bundle outdated" do
describe "with invalid gem name" do
before do
build_repo2 do
- build_git "foo", :path => lib_path("foo")
- build_git "zebra", :path => lib_path("zebra")
+ build_git "foo", path: lib_path("foo")
+ build_git "zebra", path: lib_path("zebra")
end
install_gemfile <<-G
@@ -730,12 +730,12 @@ RSpec.describe "bundle outdated" do
end
it "returns could not find gem name" do
- bundle "outdated invalid_gem_name", :raise_on_error => false
+ bundle "outdated invalid_gem_name", raise_on_error: false
expect(err).to include("Could not find gem 'invalid_gem_name'.")
end
it "returns non-zero exit code" do
- bundle "outdated invalid_gem_name", :raise_on_error => false
+ bundle "outdated invalid_gem_name", raise_on_error: false
expect(exitstatus).to_not be_zero
end
end
@@ -748,11 +748,11 @@ RSpec.describe "bundle outdated" do
G
bundle "config set auto_install 1"
- bundle :outdated, :raise_on_error => false
+ bundle :outdated, raise_on_error: false
expect(out).to include("Installing foo 1.0")
end
- context "after bundle install --deployment", :bundler => "< 3" do
+ context "after bundle install --deployment", bundler: "< 3" do
before do
build_repo2
@@ -763,13 +763,13 @@ RSpec.describe "bundle outdated" do
gem "foo"
G
bundle :lock
- bundle :install, :deployment => true
+ bundle :install, deployment: true
end
it "outputs a helpful message about being in deployment mode" do
update_repo2 { build_gem "activesupport", "3.0" }
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
expect(last_command).to be_failure
expect(err).to include("You are trying to check outdated gems in deployment mode.")
expect(err).to include("Run `bundle outdated` elsewhere.")
@@ -781,8 +781,8 @@ RSpec.describe "bundle outdated" do
context "after bundle config set --local deployment true" do
before do
build_repo2 do
- build_git "foo", :path => lib_path("foo")
- build_git "zebra", :path => lib_path("zebra")
+ build_git "foo", path: lib_path("foo")
+ build_git "zebra", path: lib_path("zebra")
end
install_gemfile <<-G
@@ -797,7 +797,7 @@ RSpec.describe "bundle outdated" do
it "outputs a helpful message about being in deployment mode" do
update_repo2 { build_gem "activesupport", "3.0" }
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
expect(last_command).to be_failure
expect(err).to include("You are trying to check outdated gems in deployment mode.")
expect(err).to include("Run `bundle outdated` elsewhere.")
@@ -843,7 +843,7 @@ RSpec.describe "bundle outdated" do
gem "laduradura", '= 5.15.2', :platforms => [:ruby, :jruby]
G
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
expected_output = <<~TABLE.strip
Gem Current Latest Requested Groups
@@ -867,8 +867,8 @@ RSpec.describe "bundle outdated" do
shared_examples_for "major version updates are detected" do
before do
build_repo2 do
- build_git "foo", :path => lib_path("foo")
- build_git "zebra", :path => lib_path("zebra")
+ build_git "foo", path: lib_path("foo")
+ build_git "zebra", path: lib_path("zebra")
end
install_gemfile <<-G
@@ -893,8 +893,8 @@ RSpec.describe "bundle outdated" do
context "when on a new machine" do
before do
build_repo2 do
- build_git "foo", :path => lib_path("foo")
- build_git "zebra", :path => lib_path("zebra")
+ build_git "foo", path: lib_path("foo")
+ build_git "zebra", path: lib_path("zebra")
end
install_gemfile <<-G
@@ -909,22 +909,22 @@ RSpec.describe "bundle outdated" do
simulate_new_machine
- update_git "foo", :path => lib_path("foo")
+ update_git "foo", path: lib_path("foo")
update_repo2 do
build_gem "activesupport", "3.3.5"
build_gem "weakling", "0.8.0"
end
end
- subject { bundle "outdated", :raise_on_error => false }
+ subject { bundle "outdated", raise_on_error: false }
it_behaves_like "version update is detected"
end
shared_examples_for "minor version updates are detected" do
before do
build_repo2 do
- build_git "foo", :path => lib_path("foo")
- build_git "zebra", :path => lib_path("zebra")
+ build_git "foo", path: lib_path("foo")
+ build_git "zebra", path: lib_path("zebra")
end
install_gemfile <<-G
@@ -949,8 +949,8 @@ RSpec.describe "bundle outdated" do
shared_examples_for "patch version updates are detected" do
before do
build_repo2 do
- build_git "foo", :path => lib_path("foo")
- build_git "zebra", :path => lib_path("zebra")
+ build_git "foo", path: lib_path("foo")
+ build_git "zebra", path: lib_path("zebra")
end
install_gemfile <<-G
@@ -982,8 +982,8 @@ RSpec.describe "bundle outdated" do
shared_examples_for "major version is ignored" do
before do
build_repo2 do
- build_git "foo", :path => lib_path("foo")
- build_git "zebra", :path => lib_path("zebra")
+ build_git "foo", path: lib_path("foo")
+ build_git "zebra", path: lib_path("zebra")
end
install_gemfile <<-G
@@ -1008,8 +1008,8 @@ RSpec.describe "bundle outdated" do
shared_examples_for "minor version is ignored" do
before do
build_repo2 do
- build_git "foo", :path => lib_path("foo")
- build_git "zebra", :path => lib_path("zebra")
+ build_git "foo", path: lib_path("foo")
+ build_git "zebra", path: lib_path("zebra")
end
install_gemfile <<-G
@@ -1034,8 +1034,8 @@ RSpec.describe "bundle outdated" do
shared_examples_for "patch version is ignored" do
before do
build_repo2 do
- build_git "foo", :path => lib_path("foo")
- build_git "zebra", :path => lib_path("zebra")
+ build_git "foo", path: lib_path("foo")
+ build_git "zebra", path: lib_path("zebra")
end
install_gemfile <<-G
@@ -1058,7 +1058,7 @@ RSpec.describe "bundle outdated" do
end
describe "with --filter-major option" do
- subject { bundle "outdated --filter-major", :raise_on_error => false }
+ subject { bundle "outdated --filter-major", raise_on_error: false }
it_behaves_like "major version updates are detected"
it_behaves_like "minor version is ignored"
@@ -1066,7 +1066,7 @@ RSpec.describe "bundle outdated" do
end
describe "with --filter-minor option" do
- subject { bundle "outdated --filter-minor", :raise_on_error => false }
+ subject { bundle "outdated --filter-minor", raise_on_error: false }
it_behaves_like "minor version updates are detected"
it_behaves_like "major version is ignored"
@@ -1074,7 +1074,7 @@ RSpec.describe "bundle outdated" do
end
describe "with --filter-patch option" do
- subject { bundle "outdated --filter-patch", :raise_on_error => false }
+ subject { bundle "outdated --filter-patch", raise_on_error: false }
it_behaves_like "patch version updates are detected"
it_behaves_like "major version is ignored"
@@ -1082,7 +1082,7 @@ RSpec.describe "bundle outdated" do
end
describe "with --filter-minor --filter-patch options" do
- subject { bundle "outdated --filter-minor --filter-patch", :raise_on_error => false }
+ subject { bundle "outdated --filter-minor --filter-patch", raise_on_error: false }
it_behaves_like "minor version updates are detected"
it_behaves_like "patch version updates are detected"
@@ -1090,7 +1090,7 @@ RSpec.describe "bundle outdated" do
end
describe "with --filter-major --filter-minor options" do
- subject { bundle "outdated --filter-major --filter-minor", :raise_on_error => false }
+ subject { bundle "outdated --filter-major --filter-minor", raise_on_error: false }
it_behaves_like "major version updates are detected"
it_behaves_like "minor version updates are detected"
@@ -1098,7 +1098,7 @@ RSpec.describe "bundle outdated" do
end
describe "with --filter-major --filter-patch options" do
- subject { bundle "outdated --filter-major --filter-patch", :raise_on_error => false }
+ subject { bundle "outdated --filter-major --filter-patch", raise_on_error: false }
it_behaves_like "major version updates are detected"
it_behaves_like "patch version updates are detected"
@@ -1106,7 +1106,7 @@ RSpec.describe "bundle outdated" do
end
describe "with --filter-major --filter-minor --filter-patch options" do
- subject { bundle "outdated --filter-major --filter-minor --filter-patch", :raise_on_error => false }
+ subject { bundle "outdated --filter-major --filter-minor --filter-patch", raise_on_error: false }
it_behaves_like "major version updates are detected"
it_behaves_like "minor version updates are detected"
@@ -1145,7 +1145,7 @@ RSpec.describe "bundle outdated" do
end
it "shows all gems when patching and filtering to patch" do
- bundle "outdated --patch --filter-patch", :raise_on_error => false
+ bundle "outdated --patch --filter-patch", raise_on_error: false
expected_output = <<~TABLE.strip
Gem Current Latest Requested Groups
@@ -1158,7 +1158,7 @@ RSpec.describe "bundle outdated" do
end
it "shows minor and major when updating to minor and filtering to patch and minor" do
- bundle "outdated --minor --filter-minor", :raise_on_error => false
+ bundle "outdated --minor --filter-minor", raise_on_error: false
expected_output = <<~TABLE.strip
Gem Current Latest Requested Groups
@@ -1170,7 +1170,7 @@ RSpec.describe "bundle outdated" do
end
it "shows minor when updating to major and filtering to minor with parseable" do
- bundle "outdated --major --filter-minor --parseable", :raise_on_error => false
+ bundle "outdated --major --filter-minor --parseable", raise_on_error: false
expect(out).not_to include("patch (newest")
expect(out).to include("minor (newest")
@@ -1212,7 +1212,7 @@ RSpec.describe "bundle outdated" do
end
it "shows gems updating to patch and filtering to patch" do
- bundle "outdated --patch --filter-patch", :raise_on_error => false, :env => { "DEBUG_RESOLVER" => "1" }
+ bundle "outdated --patch --filter-patch", raise_on_error: false, env: { "DEBUG_RESOLVER" => "1" }
expected_output = <<~TABLE.strip
Gem Current Latest Requested Groups
@@ -1224,7 +1224,7 @@ RSpec.describe "bundle outdated" do
end
it "shows gems updating to patch and filtering to patch, in debug mode" do
- bundle "outdated --patch --filter-patch", :raise_on_error => false, :env => { "DEBUG" => "1" }
+ bundle "outdated --patch --filter-patch", raise_on_error: false, env: { "DEBUG" => "1" }
expected_output = <<~TABLE.strip
Gem Current Latest Requested Groups Path
@@ -1256,7 +1256,7 @@ RSpec.describe "bundle outdated" do
gem 'weakling'
G
- bundle "outdated --only-explicit", :raise_on_error => false
+ bundle "outdated --only-explicit", raise_on_error: false
expected_output = <<~TABLE.strip
Gem Current Latest Requested Groups
@@ -1306,7 +1306,7 @@ RSpec.describe "bundle outdated" do
end
it "reports a single entry per gem" do
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
expected_output = <<~TABLE.strip
Gem Current Latest Requested Groups
@@ -1355,7 +1355,7 @@ RSpec.describe "bundle outdated" do
end
it "works" do
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
expected_output = <<~TABLE.strip
Gem Current Latest Requested Groups
diff --git a/spec/bundler/commands/platform_spec.rb b/spec/bundler/commands/platform_spec.rb
index 688bf7b6df..61e615acae 100644
--- a/spec/bundler/commands/platform_spec.rb
+++ b/spec/bundler/commands/platform_spec.rb
@@ -164,7 +164,7 @@ G
gem "foo"
G
- bundle "platform", :raise_on_error => false
+ bundle "platform", raise_on_error: false
expect(exitstatus).not_to eq(0)
end
@@ -177,7 +177,7 @@ G
gem "foo"
G
- bundle "platform", :raise_on_error => false
+ bundle "platform", raise_on_error: false
expect(exitstatus).not_to eq(0)
end
@@ -190,7 +190,7 @@ G
gem "foo"
G
- bundle "platform", :raise_on_error => false
+ bundle "platform", raise_on_error: false
expect(exitstatus).not_to eq(0)
end
@@ -347,7 +347,7 @@ G
end
it "doesn't install when the ruby version doesn't match" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
gem "rack"
@@ -359,7 +359,7 @@ G
end
it "doesn't install when engine doesn't match" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
gem "rack"
@@ -371,7 +371,7 @@ G
end
it "doesn't install when engine version doesn't match", :jruby_only do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
gem "rack"
@@ -383,7 +383,7 @@ G
end
it "doesn't install when patchlevel doesn't match" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
gem "rack"
@@ -443,7 +443,7 @@ G
#{ruby_version_incorrect}
G
- bundle :check, :raise_on_error => false
+ bundle :check, raise_on_error: false
should_be_ruby_version_incorrect
end
@@ -460,7 +460,7 @@ G
#{engine_incorrect}
G
- bundle :check, :raise_on_error => false
+ bundle :check, raise_on_error: false
should_be_engine_incorrect
end
@@ -477,7 +477,7 @@ G
#{engine_version_incorrect}
G
- bundle :check, :raise_on_error => false
+ bundle :check, raise_on_error: false
should_be_engine_version_incorrect
end
@@ -494,7 +494,7 @@ G
#{patchlevel_incorrect}
G
- bundle :check, :raise_on_error => false
+ bundle :check, raise_on_error: false
should_be_patchlevel_incorrect
end
end
@@ -526,7 +526,7 @@ G
build_gem "activesupport", "3.0"
end
- bundle "update", :all => true
+ bundle "update", all: true
expect(the_bundle).to include_gems "rack 1.2", "rack-obama 1.0", "activesupport 3.0"
end
@@ -546,7 +546,7 @@ G
build_gem "activesupport", "3.0"
end
- bundle "update", :all => true
+ bundle "update", all: true
expect(the_bundle).to include_gems "rack 1.2", "rack-obama 1.0", "activesupport 3.0"
end
@@ -562,7 +562,7 @@ G
build_gem "activesupport", "3.0"
end
- bundle :update, :all => true, :raise_on_error => false
+ bundle :update, all: true, raise_on_error: false
should_be_ruby_version_incorrect
end
@@ -578,7 +578,7 @@ G
build_gem "activesupport", "3.0"
end
- bundle :update, :all => true, :raise_on_error => false
+ bundle :update, all: true, raise_on_error: false
should_be_engine_incorrect
end
@@ -594,7 +594,7 @@ G
build_gem "activesupport", "3.0"
end
- bundle :update, :all => true, :raise_on_error => false
+ bundle :update, all: true, raise_on_error: false
should_be_engine_version_incorrect
end
@@ -609,7 +609,7 @@ G
build_gem "activesupport", "3.0"
end
- bundle :update, :all => true, :raise_on_error => false
+ bundle :update, all: true, raise_on_error: false
should_be_patchlevel_incorrect
end
end
@@ -646,7 +646,7 @@ G
expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s)
end
- it "fails if ruby version doesn't match", :bundler => "< 3" do
+ it "fails if ruby version doesn't match", bundler: "< 3" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rails"
@@ -654,11 +654,11 @@ G
#{ruby_version_incorrect}
G
- bundle "show rails", :raise_on_error => false
+ bundle "show rails", raise_on_error: false
should_be_ruby_version_incorrect
end
- it "fails if engine doesn't match", :bundler => "< 3" do
+ it "fails if engine doesn't match", bundler: "< 3" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rails"
@@ -666,11 +666,11 @@ G
#{engine_incorrect}
G
- bundle "show rails", :raise_on_error => false
+ bundle "show rails", raise_on_error: false
should_be_engine_incorrect
end
- it "fails if engine version doesn't match", :bundler => "< 3", :jruby_only => true do
+ it "fails if engine version doesn't match", bundler: "< 3", jruby_only: true do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rails"
@@ -678,11 +678,11 @@ G
#{engine_version_incorrect}
G
- bundle "show rails", :raise_on_error => false
+ bundle "show rails", raise_on_error: false
should_be_engine_version_incorrect
end
- it "fails when patchlevel doesn't match", :bundler => "< 3" do
+ it "fails when patchlevel doesn't match", bundler: "< 3" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
@@ -693,7 +693,7 @@ G
build_gem "activesupport", "3.0"
end
- bundle "show rails", :raise_on_error => false
+ bundle "show rails", raise_on_error: false
should_be_patchlevel_incorrect
end
end
@@ -738,7 +738,7 @@ G
#{ruby_version_incorrect}
G
- bundle :cache, :raise_on_error => false
+ bundle :cache, raise_on_error: false
should_be_ruby_version_incorrect
end
@@ -750,7 +750,7 @@ G
#{engine_incorrect}
G
- bundle :cache, :raise_on_error => false
+ bundle :cache, raise_on_error: false
should_be_engine_incorrect
end
@@ -762,7 +762,7 @@ G
#{engine_version_incorrect}
G
- bundle :cache, :raise_on_error => false
+ bundle :cache, raise_on_error: false
should_be_engine_version_incorrect
end
@@ -774,7 +774,7 @@ G
#{patchlevel_incorrect}
G
- bundle :cache, :raise_on_error => false
+ bundle :cache, raise_on_error: false
should_be_patchlevel_incorrect
end
end
@@ -819,7 +819,7 @@ G
#{ruby_version_incorrect}
G
- bundle :cache, :raise_on_error => false
+ bundle :cache, raise_on_error: false
should_be_ruby_version_incorrect
end
@@ -831,7 +831,7 @@ G
#{engine_incorrect}
G
- bundle :cache, :raise_on_error => false
+ bundle :cache, raise_on_error: false
should_be_engine_incorrect
end
@@ -843,7 +843,7 @@ G
#{engine_version_incorrect}
G
- bundle :cache, :raise_on_error => false
+ bundle :cache, raise_on_error: false
should_be_engine_version_incorrect
end
@@ -855,7 +855,7 @@ G
#{patchlevel_incorrect}
G
- bundle :cache, :raise_on_error => false
+ bundle :cache, raise_on_error: false
should_be_patchlevel_incorrect
end
end
@@ -863,7 +863,7 @@ G
context "bundle exec" do
before do
ENV["BUNDLER_FORCE_TTY"] = "true"
- system_gems "rack-1.0.0", "rack-0.9.1", :path => default_bundle_path
+ system_gems "rack-1.0.0", "rack-0.9.1", path: default_bundle_path
end
it "activates the correct gem when ruby version matches" do
@@ -879,7 +879,7 @@ G
end
it "activates the correct gem when ruby version matches any engine", :jruby_only do
- system_gems "rack-1.0.0", "rack-0.9.1", :path => default_bundle_path
+ system_gems "rack-1.0.0", "rack-0.9.1", path: default_bundle_path
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack", "0.9.1"
@@ -899,7 +899,7 @@ G
#{ruby_version_incorrect}
G
- bundle "exec rackup", :raise_on_error => false
+ bundle "exec rackup", raise_on_error: false
should_be_ruby_version_incorrect
end
@@ -911,7 +911,7 @@ G
#{engine_incorrect}
G
- bundle "exec rackup", :raise_on_error => false
+ bundle "exec rackup", raise_on_error: false
should_be_engine_incorrect
end
@@ -934,12 +934,12 @@ G
#{patchlevel_incorrect}
G
- bundle "exec rackup", :raise_on_error => false
+ bundle "exec rackup", raise_on_error: false
should_be_patchlevel_incorrect
end
end
- context "bundle console", :bundler => "< 3" do
+ context "bundle console", bundler: "< 3" do
before do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -993,7 +993,7 @@ G
#{ruby_version_incorrect}
G
- bundle "console", :raise_on_error => false
+ bundle "console", raise_on_error: false
should_be_ruby_version_incorrect
end
@@ -1007,7 +1007,7 @@ G
#{engine_incorrect}
G
- bundle "console", :raise_on_error => false
+ bundle "console", raise_on_error: false
should_be_engine_incorrect
end
@@ -1021,7 +1021,7 @@ G
#{engine_version_incorrect}
G
- bundle "console", :raise_on_error => false
+ bundle "console", raise_on_error: false
should_be_engine_version_incorrect
end
@@ -1035,7 +1035,7 @@ G
#{patchlevel_incorrect}
G
- bundle "console", :raise_on_error => false
+ bundle "console", raise_on_error: false
should_be_patchlevel_incorrect
end
end
@@ -1082,7 +1082,7 @@ G
end
it "fails when ruby version doesn't match" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
gem "yard"
gem "rack"
@@ -1092,14 +1092,14 @@ G
FileUtils.rm(bundled_app_lock)
- ruby "require 'bundler/setup'", :env => { "BUNDLER_VERSION" => Bundler::VERSION }, :raise_on_error => false
+ ruby "require 'bundler/setup'", env: { "BUNDLER_VERSION" => Bundler::VERSION }, raise_on_error: false
expect(bundled_app_lock).not_to exist
should_be_ruby_version_incorrect
end
it "fails when engine doesn't match" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
gem "yard"
gem "rack"
@@ -1109,14 +1109,14 @@ G
FileUtils.rm(bundled_app_lock)
- ruby "require 'bundler/setup'", :env => { "BUNDLER_VERSION" => Bundler::VERSION }, :raise_on_error => false
+ ruby "require 'bundler/setup'", env: { "BUNDLER_VERSION" => Bundler::VERSION }, raise_on_error: false
expect(bundled_app_lock).not_to exist
should_be_engine_incorrect
end
it "fails when engine version doesn't match", :jruby_only do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
gem "yard"
gem "rack"
@@ -1126,14 +1126,14 @@ G
FileUtils.rm(bundled_app_lock)
- ruby "require 'bundler/setup'", :env => { "BUNDLER_VERSION" => Bundler::VERSION }, :raise_on_error => false
+ ruby "require 'bundler/setup'", env: { "BUNDLER_VERSION" => Bundler::VERSION }, raise_on_error: false
expect(bundled_app_lock).not_to exist
should_be_engine_version_incorrect
end
it "fails when patchlevel doesn't match" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
gem "yard"
gem "rack"
@@ -1143,7 +1143,7 @@ G
FileUtils.rm(bundled_app_lock)
- ruby "require 'bundler/setup'", :env => { "BUNDLER_VERSION" => Bundler::VERSION }, :raise_on_error => false
+ ruby "require 'bundler/setup'", env: { "BUNDLER_VERSION" => Bundler::VERSION }, raise_on_error: false
expect(bundled_app_lock).not_to exist
should_be_patchlevel_incorrect
@@ -1153,7 +1153,7 @@ G
context "bundle outdated" do
before do
build_repo2 do
- build_git "foo", :path => lib_path("foo")
+ build_git "foo", path: lib_path("foo")
end
install_gemfile <<-G
@@ -1166,7 +1166,7 @@ G
it "returns list of outdated gems when the ruby version matches" do
update_repo2 do
build_gem "activesupport", "3.0"
- update_git "foo", :path => lib_path("foo")
+ update_git "foo", path: lib_path("foo")
end
gemfile <<-G
@@ -1177,7 +1177,7 @@ G
#{ruby_version_correct}
G
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
expected_output = <<~TABLE.gsub("x", "\\\h").tr(".", "\.").strip
Gem Current Latest Requested Groups
@@ -1192,7 +1192,7 @@ G
bundle :install
update_repo2 do
build_gem "activesupport", "3.0"
- update_git "foo", :path => lib_path("foo")
+ update_git "foo", path: lib_path("foo")
end
gemfile <<-G
@@ -1203,7 +1203,7 @@ G
#{ruby_version_correct_engineless}
G
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
expected_output = <<~TABLE.gsub("x", "\\\h").tr(".", "\.").strip
Gem Current Latest Requested Groups
@@ -1217,7 +1217,7 @@ G
it "fails when the ruby version doesn't match" do
update_repo2 do
build_gem "activesupport", "3.0"
- update_git "foo", :path => lib_path("foo")
+ update_git "foo", path: lib_path("foo")
end
gemfile <<-G
@@ -1228,14 +1228,14 @@ G
#{ruby_version_incorrect}
G
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
should_be_ruby_version_incorrect
end
it "fails when the engine doesn't match" do
update_repo2 do
build_gem "activesupport", "3.0"
- update_git "foo", :path => lib_path("foo")
+ update_git "foo", path: lib_path("foo")
end
gemfile <<-G
@@ -1246,14 +1246,14 @@ G
#{engine_incorrect}
G
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
should_be_engine_incorrect
end
it "fails when the engine version doesn't match", :jruby_only do
update_repo2 do
build_gem "activesupport", "3.0"
- update_git "foo", :path => lib_path("foo")
+ update_git "foo", path: lib_path("foo")
end
gemfile <<-G
@@ -1264,14 +1264,14 @@ G
#{engine_version_incorrect}
G
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
should_be_engine_version_incorrect
end
it "fails when the patchlevel doesn't match", :jruby_only do
update_repo2 do
build_gem "activesupport", "3.0"
- update_git "foo", :path => lib_path("foo")
+ update_git "foo", path: lib_path("foo")
end
gemfile <<-G
@@ -1282,14 +1282,14 @@ G
#{patchlevel_incorrect}
G
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
should_be_patchlevel_incorrect
end
it "fails when the patchlevel is a fixnum", :jruby_only do
update_repo2 do
build_gem "activesupport", "3.0"
- update_git "foo", :path => lib_path("foo")
+ update_git "foo", path: lib_path("foo")
end
gemfile <<-G
@@ -1300,7 +1300,7 @@ G
#{patchlevel_fixnum}
G
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
should_be_patchlevel_fixnum
end
end
diff --git a/spec/bundler/commands/post_bundle_message_spec.rb b/spec/bundler/commands/post_bundle_message_spec.rb
index 3050b87754..07fd5a79e9 100644
--- a/spec/bundler/commands/post_bundle_message_spec.rb
+++ b/spec/bundler/commands/post_bundle_message_spec.rb
@@ -114,13 +114,13 @@ RSpec.describe "post bundle message" do
end
it "should report a helpful error message" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
gem "rack"
gem "not-a-gem", :group => :development
G
expect(err).to include <<-EOS.strip
-Could not find gem 'not-a-gem' in rubygems repository #{file_uri_for(gem_repo1)}/ or installed locally.
+Could not find gem 'not-a-gem' in rubygems repository #{file_uri_for(gem_repo1)}/, cached gems or installed locally.
EOS
end
@@ -131,7 +131,7 @@ Could not find gem 'not-a-gem' in rubygems repository #{file_uri_for(gem_repo1)}
G
bundle :cache
expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
gem "rack"
gem "not-a-gem", :group => :development
@@ -142,7 +142,7 @@ Could not find gem 'not-a-gem' in rubygems repository #{file_uri_for(gem_repo1)}
end
end
- describe "for second bundle install run", :bundler => "< 3" do
+ describe "for second bundle install run", bundler: "< 3" do
it "without any options" do
2.times { bundle :install }
expect(out).to include(bundle_show_message)
@@ -179,25 +179,25 @@ Could not find gem 'not-a-gem' in rubygems repository #{file_uri_for(gem_repo1)}
describe "for bundle update" do
it "shows proper messages according to the configured groups" do
- bundle :update, :all => true
+ bundle :update, all: true
expect(out).not_to include("Gems in the groups")
expect(out).to include(bundle_updated_message)
bundle "config set --local without emo"
bundle :install
- bundle :update, :all => true
+ bundle :update, all: true
expect(out).to include("Gems in the group 'emo' were not updated")
expect(out).to include(bundle_updated_message)
bundle "config set --local without emo test"
bundle :install
- bundle :update, :all => true
+ bundle :update, all: true
expect(out).to include("Gems in the groups 'emo' and 'test' were not updated")
expect(out).to include(bundle_updated_message)
bundle "config set --local without emo obama test"
bundle :install
- bundle :update, :all => true
+ bundle :update, all: true
expect(out).to include("Gems in the groups 'emo', 'obama' and 'test' were not updated")
expect(out).to include(bundle_updated_message)
end
diff --git a/spec/bundler/commands/pristine_spec.rb b/spec/bundler/commands/pristine_spec.rb
index 9e496dc91a..1aec37f850 100644
--- a/spec/bundler/commands/pristine_spec.rb
+++ b/spec/bundler/commands/pristine_spec.rb
@@ -4,7 +4,7 @@ require "bundler/vendored_fileutils"
RSpec.describe "bundle pristine" do
before :each do
- build_lib "baz", :path => bundled_app do |s|
+ build_lib "baz", path: bundled_app do |s|
s.version = "1.0.0"
s.add_development_dependency "baz-dev", "=1.0.0"
end
@@ -13,9 +13,9 @@ RSpec.describe "bundle pristine" do
build_gem "weakling"
build_gem "baz-dev", "1.0.0"
build_gem "very_simple_binary", &:add_c_extension
- build_git "foo", :path => lib_path("foo")
- build_git "git_with_ext", :path => lib_path("git_with_ext"), &:add_c_extension
- build_lib "bar", :path => lib_path("bar")
+ build_git "foo", path: lib_path("foo")
+ build_git "git_with_ext", path: lib_path("git_with_ext"), &:add_c_extension
+ build_lib "bar", path: lib_path("bar")
end
install_gemfile <<-G
@@ -164,7 +164,7 @@ RSpec.describe "bundle pristine" do
end
it "raises when one of them is not in the lockfile" do
- bundle "pristine abcabcabc", :raise_on_error => false
+ bundle "pristine abcabcabc", raise_on_error: false
expect(err).to include("Could not find gem 'abcabcabc'.")
end
end
@@ -181,8 +181,8 @@ RSpec.describe "bundle pristine" do
bundle "pristine"
makefile_contents = File.read(c_ext_dir.join("Makefile").to_s)
- expect(makefile_contents).to match(/libpath =.*#{c_ext_dir}/)
- expect(makefile_contents).to match(/LIBPATH =.*-L#{c_ext_dir}/)
+ expect(makefile_contents).to match(/libpath =.*#{Regexp.escape(c_ext_dir.to_s)}/)
+ expect(makefile_contents).to match(/LIBPATH =.*-L#{Regexp.escape(c_ext_dir.to_s)}/)
end
end
@@ -198,14 +198,14 @@ RSpec.describe "bundle pristine" do
bundle "pristine"
makefile_contents = File.read(c_ext_dir.join("Makefile").to_s)
- expect(makefile_contents).to match(/libpath =.*#{c_ext_dir}/)
- expect(makefile_contents).to match(/LIBPATH =.*-L#{c_ext_dir}/)
+ expect(makefile_contents).to match(/libpath =.*#{Regexp.escape(c_ext_dir.to_s)}/)
+ expect(makefile_contents).to match(/LIBPATH =.*-L#{Regexp.escape(c_ext_dir.to_s)}/)
end
end
context "when BUNDLE_GEMFILE doesn't exist" do
before do
- bundle "pristine", :env => { "BUNDLE_GEMFILE" => "does/not/exist" }, :raise_on_error => false
+ bundle "pristine", env: { "BUNDLE_GEMFILE" => "does/not/exist" }, raise_on_error: false
end
it "shows a meaningful error" do
diff --git a/spec/bundler/commands/remove_spec.rb b/spec/bundler/commands/remove_spec.rb
index d757e0be4b..197fcde091 100644
--- a/spec/bundler/commands/remove_spec.rb
+++ b/spec/bundler/commands/remove_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe "bundle remove" do
source "#{file_uri_for(gem_repo1)}"
G
- bundle "remove", :raise_on_error => false
+ bundle "remove", raise_on_error: false
expect(err).to include("Please specify gems to remove.")
end
@@ -43,7 +43,7 @@ RSpec.describe "bundle remove" do
end
end
- context "when --install flag is specified", :bundler => "< 3" do
+ context "when --install flag is specified", bundler: "< 3" do
it "removes gems from .bundle" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -109,7 +109,7 @@ RSpec.describe "bundle remove" do
source "#{file_uri_for(gem_repo1)}"
G
- bundle "remove rack", :raise_on_error => false
+ bundle "remove rack", raise_on_error: false
expect(err).to include("`rack` is not specified in #{bundled_app_gemfile} so it could not be removed.")
end
@@ -146,7 +146,7 @@ RSpec.describe "bundle remove" do
gem "rspec"
G
- bundle "remove rails rack minitest", :raise_on_error => false
+ bundle "remove rails rack minitest", raise_on_error: false
expect(err).to include("`rack` is not specified in #{bundled_app_gemfile} so it could not be removed.")
expect(gemfile).to eq <<~G
@@ -373,7 +373,7 @@ RSpec.describe "bundle remove" do
gem "rack"; gem "rails"
G
- bundle "remove rails", :raise_on_error => false
+ bundle "remove rails", raise_on_error: false
expect(err).to include("Gems could not be removed. rack (>= 0) would also have been removed.")
expect(gemfile).to eq <<~G
@@ -385,7 +385,7 @@ RSpec.describe "bundle remove" do
context "when some gems could not be removed" do
it "shows warning for gems not removed and success for those removed" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
gem"rack"
gem"rspec"
@@ -491,7 +491,7 @@ RSpec.describe "bundle remove" do
eval_gemfile "Gemfile-other"
G
- bundle "remove rack", :raise_on_error => false
+ bundle "remove rack", raise_on_error: false
expect(err).to include("`rack` is not specified in #{bundled_app_gemfile} so it could not be removed.")
end
@@ -510,7 +510,7 @@ RSpec.describe "bundle remove" do
gem "rack"
G
- bundle "remove rack", :raise_on_error => false
+ bundle "remove rack", raise_on_error: false
expect(out).to include("rack was removed.")
expect(err).to include("`rack` is not specified in #{bundled_app("Gemfile-other")} so it could not be removed.")
@@ -535,7 +535,7 @@ RSpec.describe "bundle remove" do
gem "rack"
G
- bundle "remove rack", :raise_on_error => false
+ bundle "remove rack", raise_on_error: false
expect(out).to include("rack was removed.")
expect(err).to include("Gems could not be removed. rails (>= 0) would also have been removed.")
@@ -560,7 +560,7 @@ RSpec.describe "bundle remove" do
gem "rails"; gem "rack"
G
- bundle "remove rack", :raise_on_error => false
+ bundle "remove rack", raise_on_error: false
expect(err).to include("Gems could not be removed. rails (>= 0) would also have been removed.")
expect(bundled_app("Gemfile-other").read).to include("gem \"rack\"")
@@ -634,7 +634,7 @@ RSpec.describe "bundle remove" do
context "with gemspec" do
it "should not remove the gem" do
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp.join("foo")) do |s|
s.write("foo.gemspec", "")
s.add_dependency "rack"
end
diff --git a/spec/bundler/commands/show_spec.rb b/spec/bundler/commands/show_spec.rb
index 1c6244be41..2b6d4d2d00 100644
--- a/spec/bundler/commands/show_spec.rb
+++ b/spec/bundler/commands/show_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.describe "bundle show", :bundler => "< 3" do
+RSpec.describe "bundle show", bundler: "< 3" do
context "with a standard Gemfile" do
before :each do
install_gemfile <<-G
@@ -50,14 +50,14 @@ RSpec.describe "bundle show", :bundler => "< 3" do
end
it "complains if gem not in bundle" do
- bundle "show missing", :raise_on_error => false
+ bundle "show missing", raise_on_error: false
expect(err).to match(/could not find gem 'missing'/i)
end
it "prints path of all gems in bundle sorted by name" do
bundle "show --paths"
- expect(out).to include(default_bundle_path("gems", "rake-13.0.1").to_s)
+ expect(out).to include(default_bundle_path("gems", "rake-#{rake_version}").to_s)
expect(out).to include(default_bundle_path("gems", "rails-2.3.2").to_s)
# Gem names are the last component of their path.
@@ -104,7 +104,7 @@ RSpec.describe "bundle show", :bundler => "< 3" do
end
it "prints out branch names other than main" do
- update_git "foo", :branch => "omg" do |s|
+ update_git "foo", branch: "omg" do |s|
s.write "lib/foo.rb", "FOO = '1.0.omg'"
end
@revision = revision_for(lib_path("foo-1.0"))[0...6]
@@ -129,7 +129,7 @@ RSpec.describe "bundle show", :bundler => "< 3" do
end
it "handles when a version is a '-' prerelease" do
- @git = build_git("foo", "1.0.0-beta.1", :path => lib_path("foo"))
+ @git = build_git("foo", "1.0.0-beta.1", path: lib_path("foo"))
install_gemfile <<-G
gem "foo", "1.0.0-beta.1", :git => "#{lib_path("foo")}"
G
@@ -142,13 +142,13 @@ RSpec.describe "bundle show", :bundler => "< 3" do
context "in a fresh gem in a blank git repo" do
before :each do
- build_git "foo", :path => lib_path("foo")
+ build_git "foo", path: lib_path("foo")
File.open(lib_path("foo/Gemfile"), "w") {|f| f.puts "gemspec" }
- sys_exec "rm -rf .git && git init", :dir => lib_path("foo")
+ sys_exec "rm -rf .git && git init", dir: lib_path("foo")
end
it "does not output git errors" do
- bundle :show, :dir => lib_path("foo")
+ bundle :show, dir: lib_path("foo")
expect(err_without_deprecations).to be_empty
end
end
@@ -186,7 +186,7 @@ RSpec.describe "bundle show", :bundler => "< 3" do
invalid_regexp = "[]"
- bundle "show #{invalid_regexp}", :raise_on_error => false
+ bundle "show #{invalid_regexp}", raise_on_error: false
expect(err).to include("Could not find gem '#{invalid_regexp}'.")
end
end
@@ -219,6 +219,6 @@ RSpec.describe "bundle show", :bundler => "< 3" do
end
end
-RSpec.describe "bundle show", :bundler => "3" do
+RSpec.describe "bundle show", bundler: "3" do
pending "shows a friendly error about the command removal"
end
diff --git a/spec/bundler/commands/update_spec.rb b/spec/bundler/commands/update_spec.rb
index 7a0d435860..8565e27ebf 100644
--- a/spec/bundler/commands/update_spec.rb
+++ b/spec/bundler/commands/update_spec.rb
@@ -34,7 +34,7 @@ RSpec.describe "bundle update" do
gem "rack-obama"
exit!
G
- bundle "update", :raise_on_error => false
+ bundle "update", raise_on_error: false
expect(bundled_app_lock).to exist
end
end
@@ -60,7 +60,7 @@ RSpec.describe "bundle update" do
build_gem "activesupport", "3.0"
end
- bundle "update", :all => true
+ bundle "update", all: true
expect(out).to include("Bundle updated!")
expect(the_bundle).to include_gems "rack 1.2", "rack-obama 1.0", "activesupport 3.0"
end
@@ -74,7 +74,7 @@ RSpec.describe "bundle update" do
gem "rack-obama"
exit!
G
- bundle "update", :all => true, :raise_on_error => false
+ bundle "update", all: true, raise_on_error: false
expect(bundled_app_lock).to exist
end
end
@@ -86,7 +86,7 @@ RSpec.describe "bundle update" do
gem "rack", "1.0"
G
- bundle "update --gemfile OmgFile", :all => true
+ bundle "update --gemfile OmgFile", all: true
expect(bundled_app("OmgFile.lock")).to exist
end
@@ -97,13 +97,13 @@ RSpec.describe "bundle update" do
it "errors when passed nothing" do
install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
- bundle :update, :raise_on_error => false
+ bundle :update, raise_on_error: false
expect(err).to eq("To update everything, pass the `--all` flag.")
end
it "errors when passed --all and another option" do
install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
- bundle "update --all foo", :raise_on_error => false
+ bundle "update --all foo", raise_on_error: false
expect(err).to eq("Cannot specify --all along with specific options.")
end
@@ -171,11 +171,11 @@ RSpec.describe "bundle update" do
end
it "should inform the user" do
- bundle "update halting-problem-solver", :raise_on_error => false
+ bundle "update halting-problem-solver", raise_on_error: false
expect(err).to include "Could not find gem 'halting-problem-solver'"
end
it "should suggest alternatives" do
- bundle "update platformspecific", :raise_on_error => false
+ bundle "update platformspecific", raise_on_error: false
expect(err).to include "Did you mean platform_specific?"
end
end
@@ -236,7 +236,7 @@ RSpec.describe "bundle update" do
end
end
- bundle "update", :all => true
+ bundle "update", all: true
expect(the_bundle).to include_gems("slim 3.0.9", "slim-rails 3.1.3", "slim_lint 0.16.1")
end
@@ -275,6 +275,11 @@ RSpec.describe "bundle update" do
gem "countries"
G
+ checksums = checksums_section_when_existing do |c|
+ c.checksum(gem_repo4, "countries", "3.1.0")
+ c.checksum(gem_repo4, "country_select", "5.1.0")
+ end
+
lockfile <<~L
GEM
remote: #{file_uri_for(gem_repo4)}/
@@ -289,14 +294,14 @@ RSpec.describe "bundle update" do
DEPENDENCIES
countries
country_select
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
previous_lockfile = lockfile
- bundle "lock --update"
+ bundle "lock --update", env: { "DEBUG" => "1" }, verbose: true
expect(lockfile).to eq(previous_lockfile)
end
@@ -434,7 +439,7 @@ RSpec.describe "bundle update" do
build_gem "c", "2.0"
end
- install_gemfile <<-G, :verbose => true
+ install_gemfile <<-G, verbose: true
source "#{file_uri_for(gem_repo4)}"
gem "a"
G
@@ -447,7 +452,7 @@ RSpec.describe "bundle update" do
end
end
- bundle "update", :all => true, :verbose => true
+ bundle "update", all: true, verbose: true
expect(the_bundle).to include_gems("a 1.0", "b 1.0", "c 2.0")
end
@@ -505,6 +510,11 @@ RSpec.describe "bundle update" do
original_lockfile = lockfile
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo4, "activesupport", "6.0.4.1"
+ c.checksum gem_repo4, "tzinfo", "1.2.9"
+ end
+
expected_lockfile = <<~L
GEM
remote: #{file_uri_for(gem_repo4)}/
@@ -518,7 +528,7 @@ RSpec.describe "bundle update" do
DEPENDENCIES
activesupport (~> 6.0.0)
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -527,6 +537,10 @@ RSpec.describe "bundle update" do
expect(the_bundle).to include_gems("activesupport 6.0.4.1", "tzinfo 1.2.9")
expect(lockfile).to eq(expected_lockfile)
+ # needed because regressing to versions already present on the system
+ # won't add a checksum
+ expected_lockfile = remove_checksums_from_lockfile(expected_lockfile)
+
lockfile original_lockfile
bundle "update"
expect(the_bundle).to include_gems("activesupport 6.0.4.1", "tzinfo 1.2.9")
@@ -543,12 +557,36 @@ RSpec.describe "bundle update" do
before do
build_repo2
- install_gemfile <<-G
+ gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
gem "activesupport"
gem "rack-obama"
gem "platform_specific"
G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo2)}/
+ specs:
+ activesupport (2.3.5)
+ platform_specific (1.0-#{local_platform})
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+
+ PLATFORMS
+ #{local_platform}
+
+ DEPENDENCIES
+ activesupport
+ platform_specific
+ rack-obama
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
end
it "doesn't hit repo2" do
@@ -601,7 +639,7 @@ RSpec.describe "bundle update" do
context "when there is a source with the same name as a gem in a group" do
before do
- build_git "foo", :path => lib_path("activesupport")
+ build_git "foo", path: lib_path("activesupport")
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
gem "activesupport", :group => :development
@@ -611,7 +649,7 @@ RSpec.describe "bundle update" do
it "should not update the gems from that source" do
update_repo2 { build_gem "activesupport", "3.0" }
- update_git "foo", "2.0", :path => lib_path("activesupport")
+ update_git "foo", "2.0", path: lib_path("activesupport")
bundle "update --group development"
expect(the_bundle).to include_gems "activesupport 3.0"
@@ -653,32 +691,32 @@ RSpec.describe "bundle update" do
G
end
- it "should fail loudly", :bundler => "< 3" do
+ it "should fail loudly", bundler: "< 3" do
bundle "install --deployment"
- bundle "update", :all => true, :raise_on_error => false
+ bundle "update", all: true, raise_on_error: false
expect(last_command).to be_failure
- expect(err).to match(/You are trying to install in deployment mode after changing.your Gemfile/m)
- expect(err).to match(/freeze \nby running `bundle config set frozen false`./m)
+ expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/)
+ expect(err).to match(/freeze by running `bundle config set frozen false`./)
end
it "should fail loudly when frozen is set globally" do
bundle "config set --global frozen 1"
- bundle "update", :all => true, :raise_on_error => false
- expect(err).to match(/You are trying to install in deployment mode after changing.your Gemfile/m).
- and match(/freeze \nby running `bundle config set frozen false`./m)
+ bundle "update", all: true, raise_on_error: false
+ expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/).
+ and match(/freeze by running `bundle config set frozen false`./)
end
it "should fail loudly when deployment is set globally" do
bundle "config set --global deployment true"
- bundle "update", :all => true, :raise_on_error => false
- expect(err).to match(/You are trying to install in deployment mode after changing.your Gemfile/m).
- and match(/freeze \nby running `bundle config set frozen false`./m)
+ bundle "update", all: true, raise_on_error: false
+ expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/).
+ and match(/freeze by running `bundle config set frozen false`./)
end
it "should not suggest any command to unfreeze bundler if frozen is set through ENV" do
- bundle "update", :all => true, :raise_on_error => false, :env => { "BUNDLE_FROZEN" => "true" }
- expect(err).to match(/You are trying to install in deployment mode after changing.your Gemfile/m)
+ bundle "update", all: true, raise_on_error: false, env: { "BUNDLE_FROZEN" => "true" }
+ expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/)
expect(err).not_to match(/by running/)
end
end
@@ -772,7 +810,7 @@ RSpec.describe "bundle update" do
end
end
- it "shows the previous version of the gem when updated from rubygems source", :bundler => "< 3" do
+ it "shows the previous version of the gem when updated from rubygems source" do
build_repo2
install_gemfile <<-G
@@ -780,43 +818,39 @@ RSpec.describe "bundle update" do
gem "activesupport"
G
- bundle "update", :all => true
+ bundle "update", all: true, verbose: true
expect(out).to include("Using activesupport 2.3.5")
update_repo2 do
build_gem "activesupport", "3.0"
end
- bundle "update", :all => true
+ bundle "update", all: true
expect(out).to include("Installing activesupport 3.0 (was 2.3.5)")
end
- context "with suppress_install_using_messages set" do
- before { bundle "config set suppress_install_using_messages true" }
-
- it "only prints `Using` for versions that have changed" do
- build_repo4 do
- build_gem "bar"
- build_gem "foo"
- end
-
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
- gem "bar"
- gem "foo"
- G
+ it "only prints `Using` for versions that have changed" do
+ build_repo4 do
+ build_gem "bar"
+ build_gem "foo"
+ end
- bundle "update", :all => true
- expect(out).to match(/Resolving dependencies\.\.\.\.*\nBundle updated!/)
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "bar"
+ gem "foo"
+ G
- update_repo4 do
- build_gem "foo", "2.0"
- end
+ bundle "update", all: true
+ expect(out).to match(/Resolving dependencies\.\.\.\.*\nBundle updated!/)
- bundle "update", :all => true
- out.sub!("Removing foo (1.0)\n", "")
- expect(out).to match(/Resolving dependencies\.\.\.\.*\nFetching foo 2\.0 \(was 1\.0\)\nInstalling foo 2\.0 \(was 1\.0\)\nBundle updated/)
+ update_repo4 do
+ build_gem "foo", "2.0"
end
+
+ bundle "update", all: true
+ out.sub!("Removing foo (1.0)\n", "")
+ expect(out).to match(/Resolving dependencies\.\.\.\.*\nFetching foo 2\.0 \(was 1\.0\)\nInstalling foo 2\.0 \(was 1\.0\)\nBundle updated/)
end
it "shows error message when Gemfile.lock is not preset and gem is specified" do
@@ -825,12 +859,96 @@ RSpec.describe "bundle update" do
gem "activesupport"
G
- bundle "update nonexisting", :raise_on_error => false
+ bundle "update nonexisting", raise_on_error: false
expect(err).to include("This Bundle hasn't been installed yet. Run `bundle install` to update and install the bundled gems.")
expect(exitstatus).to eq(22)
end
- context "with multiple, duplicated sources, with lockfile in old format", :bundler => "< 3" do
+ context "with multiple sources and caching enabled" do
+ before do
+ build_repo2 do
+ build_gem "rack", "1.0.0"
+
+ build_gem "request_store", "1.0.0" do |s|
+ s.add_dependency "rack", "1.0.0"
+ end
+ end
+
+ build_repo4 do
+ # set up repo with no gems
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo2)}"
+
+ gem "request_store"
+
+ source "#{file_uri_for(gem_repo4)}" do
+ end
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo2)}/
+ specs:
+ rack (1.0.0)
+ request_store (1.0.0)
+ rack (= 1.0.0)
+
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+
+ PLATFORMS
+ #{local_platform}
+
+ DEPENDENCIES
+ request_store
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "works" do
+ bundle :install
+ bundle :cache
+
+ update_repo2 do
+ build_gem "request_store", "1.1.0" do |s|
+ s.add_dependency "rack", "1.0.0"
+ end
+ end
+
+ bundle "update request_store"
+
+ expect(out).to include("Bundle updated!")
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo2)}/
+ specs:
+ rack (1.0.0)
+ request_store (1.1.0)
+ rack (= 1.0.0)
+
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+
+ PLATFORMS
+ #{local_platform}
+
+ DEPENDENCIES
+ request_store
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "with multiple, duplicated sources, with lockfile in old format", bundler: "< 3" do
before do
build_repo2 do
build_gem "dotenv", "2.7.6"
@@ -884,8 +1002,8 @@ RSpec.describe "bundle update" do
end
it "works" do
- bundle :install, :artifice => "compact_index"
- bundle "update oj", :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
+ bundle "update oj", artifice: "compact_index"
expect(out).to include("Bundle updated!")
expect(the_bundle).to include_gems "oj 3.11.5"
@@ -1021,7 +1139,7 @@ RSpec.describe "bundle update in more complicated situations" do
end
it "allows updating" do
- bundle :update, :all => true
+ bundle :update, all: true
expect(the_bundle).to include_gem "a 1.1"
end
@@ -1077,7 +1195,7 @@ RSpec.describe "bundle update without a Gemfile.lock" do
gem "rack", "1.0"
G
- bundle "update", :all => true
+ bundle "update", all: true
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -1100,7 +1218,7 @@ RSpec.describe "bundle update when a gem depends on a newer version of bundler"
end
it "should explain that bundler conflicted and how to resolve the conflict" do
- bundle "update", :all => true, :raise_on_error => false
+ bundle "update", all: true, raise_on_error: false
expect(last_command.stdboth).not_to match(/in snapshot/i)
expect(err).to match(/current Bundler version/i).
and match(/Install the necessary version with `gem install bundler:#{Bundler::VERSION.succ}`/i)
@@ -1116,9 +1234,10 @@ RSpec.describe "bundle update --ruby" do
G
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "#{file_uri_for(gem_repo1)}"
G
end
+
it "removes the Ruby from the Gemfile.lock" do
bundle "update --ruby"
@@ -1146,28 +1265,29 @@ RSpec.describe "bundle update --ruby" do
G
gemfile <<-G
- ruby '~> #{current_ruby_minor}'
- source "#{file_uri_for(gem_repo1)}"
+ ruby '~> #{current_ruby_minor}'
+ source "#{file_uri_for(gem_repo1)}"
G
end
+
it "updates the Gemfile.lock with the latest version" do
bundle "update --ruby"
expect(lockfile).to eq <<~L
- GEM
- remote: #{file_uri_for(gem_repo1)}/
- specs:
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ specs:
- PLATFORMS
- #{lockfile_platforms}
+ PLATFORMS
+ #{lockfile_platforms}
- DEPENDENCIES
+ DEPENDENCIES
- RUBY VERSION
- #{Bundler::RubyVersion.system}
+ RUBY VERSION
+ #{Bundler::RubyVersion.system}
- BUNDLED WITH
- #{Bundler::VERSION}
+ BUNDLED WITH
+ #{Bundler::VERSION}
L
end
end
@@ -1185,7 +1305,7 @@ RSpec.describe "bundle update --ruby" do
G
end
it "shows a helpful error message" do
- bundle "update --ruby", :raise_on_error => false
+ bundle "update --ruby", raise_on_error: false
expect(err).to include("Your Ruby version is #{Bundler::RubyVersion.system.gem_version}, but your Gemfile specified ~> 2.1.0")
end
@@ -1203,6 +1323,8 @@ RSpec.describe "bundle update --ruby" do
DEPENDENCIES
+ CHECKSUMS
+
RUBY VERSION
ruby 2.1.4p222
@@ -1215,6 +1337,7 @@ RSpec.describe "bundle update --ruby" do
source "#{file_uri_for(gem_repo1)}"
G
end
+
it "updates the Gemfile.lock with the latest version" do
bundle "update --ruby"
@@ -1228,6 +1351,8 @@ RSpec.describe "bundle update --ruby" do
DEPENDENCIES
+ CHECKSUMS
+
RUBY VERSION
#{Bundler::RubyVersion.system}
@@ -1239,20 +1364,37 @@ RSpec.describe "bundle update --ruby" do
end
RSpec.describe "bundle update --bundler" do
- it "updates the bundler version in the lockfile without re-resolving" do
+ it "updates the bundler version in the lockfile" do
build_repo4 do
build_gem "rack", "1.0"
end
+ checksums = checksums_section_when_existing do |c|
+ c.checksum(gem_repo4, "rack", "1.0")
+ end
+
install_gemfile <<-G
source "#{file_uri_for(gem_repo4)}"
gem "rack"
G
- lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, '\11.0.0\2')
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ rack (1.0)
- FileUtils.rm_r gem_repo4
+ PLATFORMS
+ #{lockfile_platforms}
- bundle :update, :bundler => true, :artifice => "compact_index", :verbose => true
+ DEPENDENCIES
+ rack
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, '\11.0.0\2')
+
+ bundle :update, bundler: true, artifice: "compact_index", verbose: true
expect(out).to include("Using bundler #{Bundler::VERSION}")
expect(lockfile).to eq <<~L
@@ -1266,7 +1408,7 @@ RSpec.describe "bundle update --bundler" do
DEPENDENCIES
rack
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -1287,7 +1429,11 @@ RSpec.describe "bundle update --bundler" do
G
lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, "2.3.9")
- bundle :update, :bundler => true, :artifice => "compact_index", :verbose => true
+ checksums = checksums_section_when_existing do |c|
+ c.checksum(gem_repo4, "rack", "1.0")
+ end
+
+ bundle :update, bundler: true, artifice: "compact_index", verbose: true
expect(out).to include("Using bundler #{Bundler::VERSION}")
expect(lockfile).to eq <<~L
@@ -1301,7 +1447,7 @@ RSpec.describe "bundle update --bundler" do
DEPENDENCIES
rack
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -1309,26 +1455,28 @@ RSpec.describe "bundle update --bundler" do
expect(the_bundle).to include_gem "rack 1.0"
end
- it "updates the bundler version in the lockfile even if the latest version is not installed", :ruby_repo, :realworld do
+ it "updates the bundler version in the lockfile even if the latest version is not installed", :ruby_repo do
pristine_system_gems "bundler-2.3.9"
build_repo4 do
build_gem "rack", "1.0"
+
+ build_bundler "999.0.0"
end
- install_gemfile <<-G, :env => { "BUNDLER_IGNORE_DEFAULT_GEM" => "true" }
+ install_gemfile <<-G, artifice: nil, env: { "BUNDLER_IGNORE_DEFAULT_GEM" => "true" }
source "#{file_uri_for(gem_repo4)}"
gem "rack"
G
lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, "2.3.9")
- bundle :update, :bundler => true, :artifice => "vcr", :verbose => true, :env => { "BUNDLER_IGNORE_DEFAULT_GEM" => "true" }
+ bundle :update, bundler: true, artifice: "compact_index", verbose: true, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
# Only updates properly on modern RubyGems.
if Gem.rubygems_version >= Gem::Version.new("3.3.0.dev")
- expect(out).to include("Updating bundler to 2.3.10")
- expect(out).to include("Using bundler 2.3.10")
+ expect(out).to include("Updating bundler to 999.0.0")
+ expect(out).to include("Using bundler 999.0.0")
expect(out).not_to include("Installing Bundler 2.3.9 and restarting using that version.")
expect(lockfile).to eq <<~L
@@ -1344,29 +1492,113 @@ RSpec.describe "bundle update --bundler" do
rack
BUNDLED WITH
- 2.3.10
+ 999.0.0
L
- expect(the_bundle).to include_gems "bundler 2.3.10"
+ expect(the_bundle).to include_gems "bundler 999.0.0"
+ expect(the_bundle).to include_gems "rack 1.0"
+ else
+ # Old RubyGems versions do not trampoline but they still change BUNDLED
+ # WITH to the latest bundler version. This means the below check fails
+ # because it tries to use bundler 999.0.0 which did not get installed.
+ # Workaround the bug by forcing the version we know is installed.
+ expect(the_bundle).to include_gems "rack 1.0", env: { "BUNDLER_VERSION" => "2.3.9" }
+ end
+ end
+
+ it "does not claim to update to Bundler version to a wrong version when cached gems are present" do
+ pristine_system_gems "bundler-2.99.0"
+
+ build_repo4 do
+ build_gem "rack", "3.0.9.1"
+
+ build_bundler "2.99.0"
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "rack"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ rack (3.0.9.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 2.99.0
+ L
+
+ bundle :cache, verbose: true
+
+ bundle :update, bundler: true, artifice: "compact_index", verbose: true, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+
+ expect(out).not_to include("Updating bundler to")
+ end
+
+ it "does not update the bundler version in the lockfile if the latest version is not compatible with current ruby", :ruby_repo do
+ pristine_system_gems "bundler-2.3.9"
+
+ build_repo4 do
+ build_gem "rack", "1.0"
+
+ build_bundler "2.3.9"
+ build_bundler "999.0.0" do |s|
+ s.required_ruby_version = "> #{Gem.ruby_version}"
+ end
end
+ install_gemfile <<-G, env: { "BUNDLER_IGNORE_DEFAULT_GEM" => "true" }
+ source "#{file_uri_for(gem_repo4)}"
+ gem "rack"
+ G
+ lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, "2.3.9")
+
+ bundle :update, bundler: true, artifice: "compact_index", verbose: true, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s, "BUNDLER_IGNORE_DEFAULT_GEM" => "true" }
+
+ expect(out).to include("Using bundler 2.3.9")
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ rack (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 2.3.9
+ L
+
+ expect(the_bundle).to include_gems "bundler 2.3.9"
expect(the_bundle).to include_gems "rack 1.0"
end
- it "errors if the explicit target version does not exist", :realworld do
+ it "errors if the explicit target version does not exist" do
pristine_system_gems "bundler-2.3.9"
build_repo4 do
build_gem "rack", "1.0"
end
- install_gemfile <<-G, :env => { "BUNDLER_IGNORE_DEFAULT_GEM" => "true" }
+ install_gemfile <<-G, env: { "BUNDLER_IGNORE_DEFAULT_GEM" => "true" }
source "#{file_uri_for(gem_repo4)}"
gem "rack"
G
lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, "2.3.9")
- bundle :update, :bundler => "999.999.999", :artifice => "vcr", :raise_on_error => false
+ bundle :update, bundler: "999.999.999", artifice: "compact_index", raise_on_error: false
# Only gives a meaningful error message on modern RubyGems.
@@ -1388,11 +1620,14 @@ RSpec.describe "bundle update --bundler" do
gem "rack"
G
- bundle :update, :bundler => "2.3.0.dev"
+ bundle :update, bundler: "2.3.0.dev", verbose: "true"
# Only updates properly on modern RubyGems.
-
if Gem.rubygems_version >= Gem::Version.new("3.3.0.dev")
+ checksums = checksums_section_when_existing do |c|
+ c.checksum(gem_repo4, "rack", "1.0")
+ end
+
expect(lockfile).to eq <<~L
GEM
remote: #{file_uri_for(gem_repo4)}/
@@ -1404,7 +1639,7 @@ RSpec.describe "bundle update --bundler" do
DEPENDENCIES
rack
-
+ #{checksums}
BUNDLED WITH
2.3.0.dev
L
@@ -1425,11 +1660,14 @@ RSpec.describe "bundle update --bundler" do
gem "rack"
G
- bundle :update, :bundler => "2.3.9", :raise_on_error => false
+ bundle :update, bundler: "2.3.9", verbose: true
expect(out).not_to include("Fetching gem metadata from https://rubygems.org/")
# Only updates properly on modern RubyGems.
+ checksums = checksums_section_when_existing do |c|
+ c.checksum(gem_repo4, "rack", "1.0")
+ end
if Gem.rubygems_version >= Gem::Version.new("3.3.0.dev")
expect(lockfile).to eq <<~L
@@ -1443,7 +1681,7 @@ RSpec.describe "bundle update --bundler" do
DEPENDENCIES
rack
-
+ #{checksums}
BUNDLED WITH
2.3.9
L
@@ -1473,8 +1711,8 @@ RSpec.describe "bundle update --bundler" do
2.1.4
L
- bundle "update --bundler=2.3.9", :env => { "BUNDLE_FROZEN" => "true" }
- expect(err).to include("Cannot write a changed lockfile while frozen")
+ bundle "update --bundler=2.3.9", env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false
+ expect(err).to include("An update to the version of bundler itself was requested, but the lockfile can't be updated because frozen mode is set")
end
end
@@ -1533,7 +1771,7 @@ RSpec.describe "bundle update conservative" do
end
it "update all" do
- bundle "update --patch", :all => true
+ bundle "update --patch", all: true
expect(the_bundle).to include_gems "foo 1.4.5", "bar 2.1.1", "qux 1.0.1"
end
@@ -1555,7 +1793,7 @@ RSpec.describe "bundle update conservative" do
end
it "minor preferred" do
- bundle "update --minor --strict", :all => true
+ bundle "update --minor --strict", all: true
expect(the_bundle).to include_gems "foo 1.5.0", "bar 2.1.1", "qux 1.1.0"
end
@@ -1634,6 +1872,8 @@ RSpec.describe "bundle update conservative" do
shared_owner_a
shared_owner_b
+ CHECKSUMS
+
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -1687,6 +1927,13 @@ RSpec.describe "bundle update conservative" do
shared_owner_a
shared_owner_b
+ CHECKSUMS
+ isolated_dep (2.0.1)
+ isolated_owner (1.0.2)
+ shared_dep (5.0.1)
+ shared_owner_a (3.0.2)
+ shared_owner_b (4.0.2)
+
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -1707,13 +1954,59 @@ RSpec.describe "bundle update conservative" do
end
end
+ context "when Gemfile dependencies have changed" do
+ before do
+ build_repo4 do
+ build_gem "nokogiri", "1.16.4" do |s|
+ s.platform = "arm64-darwin"
+ end
+
+ build_gem "nokogiri", "1.16.4" do |s|
+ s.platform = "x86_64-linux"
+ end
+
+ build_gem "prism", "0.25.0"
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "nokogiri", ">=1.16.4"
+ gem "prism", ">=0.25.0"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.16.4-arm64-darwin)
+ nokogiri (1.16.4-x86_64-linux)
+
+ PLATFORMS
+ arm64-darwin
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri (>= 1.16.4)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "still works" do
+ simulate_platform "arm64-darwin-23" do
+ bundle "update"
+ end
+ end
+ end
+
context "error handling" do
before do
gemfile "source \"#{file_uri_for(gem_repo1)}\""
end
it "raises if too many flags are provided" do
- bundle "update --patch --minor", :all => true, :raise_on_error => false
+ bundle "update --patch --minor", all: true, raise_on_error: false
expect(err).to eq "Provide only one of the following options: minor, patch"
end
diff --git a/spec/bundler/commands/version_spec.rb b/spec/bundler/commands/version_spec.rb
index 53d545f5fa..307058a5dd 100644
--- a/spec/bundler/commands/version_spec.rb
+++ b/spec/bundler/commands/version_spec.rb
@@ -4,42 +4,42 @@ require_relative "../support/path"
RSpec.describe "bundle version" do
if Spec::Path.ruby_core?
- COMMIT_HASH = /unknown|[a-fA-F0-9]{7,}/.freeze
+ COMMIT_HASH = /unknown|[a-fA-F0-9]{7,}/
else
- COMMIT_HASH = /[a-fA-F0-9]{7,}/.freeze
+ COMMIT_HASH = /[a-fA-F0-9]{7,}/
end
context "with -v" do
- it "outputs the version", :bundler => "< 3" do
+ it "outputs the version", bundler: "< 3" do
bundle "-v"
expect(out).to eq("Bundler version #{Bundler::VERSION}")
end
- it "outputs the version", :bundler => "3" do
+ it "outputs the version", bundler: "3" do
bundle "-v"
expect(out).to eq(Bundler::VERSION)
end
end
context "with --version" do
- it "outputs the version", :bundler => "< 3" do
+ it "outputs the version", bundler: "< 3" do
bundle "--version"
expect(out).to eq("Bundler version #{Bundler::VERSION}")
end
- it "outputs the version", :bundler => "3" do
+ it "outputs the version", bundler: "3" do
bundle "--version"
expect(out).to eq(Bundler::VERSION)
end
end
context "with version" do
- it "outputs the version with build metadata", :bundler => "< 3" do
+ it "outputs the version with build metadata", bundler: "< 3" do
bundle "version"
expect(out).to match(/\ABundler version #{Regexp.escape(Bundler::VERSION)} \(\d{4}-\d{2}-\d{2} commit #{COMMIT_HASH}\)\z/)
end
- it "outputs the version with build metadata", :bundler => "3" do
+ it "outputs the version with build metadata", bundler: "3" do
bundle "version"
expect(out).to match(/\A#{Regexp.escape(Bundler::VERSION)} \(\d{4}-\d{2}-\d{2} commit #{COMMIT_HASH}\)\z/)
end
diff --git a/spec/bundler/commands/viz_spec.rb b/spec/bundler/commands/viz_spec.rb
index cf612397ab..f8b5f7836e 100644
--- a/spec/bundler/commands/viz_spec.rb
+++ b/spec/bundler/commands/viz_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.describe "bundle viz", :bundler => "< 3", :if => Bundler.which("dot"), :realworld => true do
+RSpec.describe "bundle viz", bundler: "< 3", if: Bundler.which("dot"), realworld: true do
before do
realworld_system_gems "ruby-graphviz --version 1.2.5"
end
@@ -15,8 +15,8 @@ RSpec.describe "bundle viz", :bundler => "< 3", :if => Bundler.which("dot"), :re
bundle "viz"
expect(out).to include("gem_graph.png")
- bundle "viz", :format => "debug"
- expect(out).to eq(strip_whitespace(<<-DOT).strip)
+ bundle "viz", format: "debug"
+ expect(out).to eq(<<~DOT.strip)
digraph Gemfile {
concentrate = "true";
normalize = "true";
@@ -49,8 +49,8 @@ RSpec.describe "bundle viz", :bundler => "< 3", :if => Bundler.which("dot"), :re
bundle "viz"
expect(out).to include("gem_graph.png")
- bundle "viz", :format => :debug, :version => true
- expect(out).to eq(strip_whitespace(<<-EOS).strip)
+ bundle "viz", format: :debug, version: true
+ expect(out).to eq(<<~EOS.strip)
digraph Gemfile {
concentrate = "true";
normalize = "true";
@@ -77,7 +77,7 @@ RSpec.describe "bundle viz", :bundler => "< 3", :if => Bundler.which("dot"), :re
end
end
- system_gems "graphviz-999", :gem_repo => gem_repo4
+ system_gems "graphviz-999", gem_repo: gem_repo4
end
it "loads the correct ruby-graphviz gem" do
@@ -87,8 +87,8 @@ RSpec.describe "bundle viz", :bundler => "< 3", :if => Bundler.which("dot"), :re
gem "rack-obama"
G
- bundle "viz", :format => "debug"
- expect(out).to eq(strip_whitespace(<<-DOT).strip)
+ bundle "viz", format: "debug"
+ expect(out).to eq(<<~DOT.strip)
digraph Gemfile {
concentrate = "true";
normalize = "true";
diff --git a/spec/bundler/install/allow_offline_install_spec.rb b/spec/bundler/install/allow_offline_install_spec.rb
index 4c6c77a61e..8da94718e0 100644
--- a/spec/bundler/install/allow_offline_install_spec.rb
+++ b/spec/bundler/install/allow_offline_install_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe "bundle install with :allow_offline_install" do
context "with no cached data locally" do
it "still installs" do
- install_gemfile <<-G, :artifice => "compact_index"
+ install_gemfile <<-G, artifice: "compact_index"
source "http://testgemserver.local"
gem "rack-obama"
G
@@ -15,7 +15,7 @@ RSpec.describe "bundle install with :allow_offline_install" do
end
it "still fails when the network is down" do
- install_gemfile <<-G, :artifice => "fail", :raise_on_error => false
+ install_gemfile <<-G, artifice: "fail", raise_on_error: false
source "http://testgemserver.local"
gem "rack-obama"
G
@@ -26,10 +26,10 @@ RSpec.describe "bundle install with :allow_offline_install" do
context "with cached data locally" do
it "will install from the compact index" do
- system_gems ["rack-1.0.0"], :path => default_bundle_path
+ system_gems ["rack-1.0.0"], path: default_bundle_path
bundle "config set clean false"
- install_gemfile <<-G, :artifice => "compact_index"
+ install_gemfile <<-G, artifice: "compact_index"
source "http://testgemserver.local"
gem "rack-obama"
gem "rack", "< 1.0"
@@ -42,7 +42,7 @@ RSpec.describe "bundle install with :allow_offline_install" do
gem "rack-obama"
G
- bundle :update, :artifice => "fail", :all => true
+ bundle :update, artifice: "fail", all: true
expect(last_command.stdboth).to include "Using the cached data for the new index because of a network error"
expect(the_bundle).to include_gems("rack-obama 1.0", "rack 1.0.0")
@@ -51,7 +51,7 @@ RSpec.describe "bundle install with :allow_offline_install" do
def break_git_remote_ops!
FileUtils.mkdir_p(tmp("broken_path"))
File.open(tmp("broken_path/git"), "w", 0o755) do |f|
- f.puts strip_whitespace(<<-RUBY)
+ f.puts <<~RUBY
#!/usr/bin/env ruby
fetch_args = %w(fetch --force --quiet)
clone_args = %w(clone --bare --no-hardlinks --quiet)
@@ -75,14 +75,14 @@ RSpec.describe "bundle install with :allow_offline_install" do
it "will install from a cached git repo" do
skip "doesn't print errors" if Gem.win_platform?
- git = build_git "a", "1.0.0", :path => lib_path("a")
- update_git("a", :path => git.path, :branch => "new_branch")
+ git = build_git "a", "1.0.0", path: lib_path("a")
+ update_git("a", path: git.path, branch: "new_branch")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "a", :git => #{git.path.to_s.dump}
G
- break_git_remote_ops! { bundle :update, :all => true }
+ break_git_remote_ops! { bundle :update, all: true }
expect(err).to include("Using cached git data because of network errors")
expect(the_bundle).to be_locked
diff --git a/spec/bundler/install/bundler_spec.rb b/spec/bundler/install/bundler_spec.rb
index a0d5332e96..19911f1154 100644
--- a/spec/bundler/install/bundler_spec.rb
+++ b/spec/bundler/install/bundler_spec.rb
@@ -30,7 +30,7 @@ RSpec.describe "bundle install" do
end
it "causes a conflict if explicitly requesting a different version of bundler" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo2)}"
gem "rails", "3.0"
gem "bundler", "0.9.1"
@@ -50,7 +50,7 @@ RSpec.describe "bundle install" do
end
it "causes a conflict if explicitly requesting a non matching requirement on bundler" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo2)}"
gem "rails", "3.0"
gem "bundler", "~> 0.8"
@@ -73,7 +73,7 @@ RSpec.describe "bundle install" do
end
it "causes a conflict if explicitly requesting a version of bundler that doesn't exist" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo2)}"
gem "rails", "3.0"
gem "bundler", "0.9.2"
@@ -143,7 +143,7 @@ RSpec.describe "bundle install" do
end
end
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo2)}"
gem "activemerchant"
gem "rails_pinned_to_old_activesupport"
@@ -171,7 +171,7 @@ RSpec.describe "bundle install" do
end
end
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo2)}"
gem "rails_pinned_to_old_activesupport"
gem "activesupport", "2.3.5"
diff --git a/spec/bundler/install/deploy_spec.rb b/spec/bundler/install/deploy_spec.rb
index 3f98d56f41..d89fdea6f1 100644
--- a/spec/bundler/install/deploy_spec.rb
+++ b/spec/bundler/install/deploy_spec.rb
@@ -8,26 +8,26 @@ RSpec.describe "install in deployment or frozen mode" do
G
end
- context "with CLI flags", :bundler => "< 3" do
+ context "with CLI flags", bundler: "< 3" do
it "fails without a lockfile and says that --deployment requires a lock" do
- bundle "install --deployment", :raise_on_error => false
- expect(err).to include("The --deployment flag requires a Gemfile.lock")
+ bundle "install --deployment", raise_on_error: false
+ expect(err).to include("The --deployment flag requires a lockfile")
end
it "fails without a lockfile and says that --frozen requires a lock" do
- bundle "install --frozen", :raise_on_error => false
- expect(err).to include("The --frozen flag requires a Gemfile.lock")
+ bundle "install --frozen", raise_on_error: false
+ expect(err).to include("The --frozen flag requires a lockfile")
end
it "disallows --deployment --system" do
- bundle "install --deployment --system", :raise_on_error => false
+ bundle "install --deployment --system", raise_on_error: false
expect(err).to include("You have specified both --deployment")
expect(err).to include("Please choose only one option")
expect(exitstatus).to eq(15)
end
it "disallows --deployment --path --system" do
- bundle "install --deployment --path . --system", :raise_on_error => false
+ bundle "install --deployment --path . --system", raise_on_error: false
expect(err).to include("You have specified both --path")
expect(err).to include("as well as --system")
expect(err).to include("Please choose only one option")
@@ -35,7 +35,7 @@ RSpec.describe "install in deployment or frozen mode" do
end
it "doesn't mess up a subsequent `bundle install` after you try to deploy without a lock" do
- bundle "install --deployment", :raise_on_error => false
+ bundle "install --deployment", raise_on_error: false
bundle :install
expect(the_bundle).to include_gems "rack 1.0"
end
@@ -65,8 +65,8 @@ RSpec.describe "install in deployment or frozen mode" do
gem "rack-obama"
G
- bundle "install --deployment", :raise_on_error => false
- expect(err).to include("deployment mode")
+ bundle "install --deployment", raise_on_error: false
+ expect(err).to include("frozen mode")
expect(err).to include("You have added to the Gemfile")
expect(err).to include("* rack-obama")
expect(err).not_to include("You have deleted from the Gemfile")
@@ -79,7 +79,7 @@ RSpec.describe "install in deployment or frozen mode" do
simulate_new_machine
bundle "config set --local deployment true"
bundle "config set --local path vendor/bundle"
- bundle "install --gemfile #{tmp}/bundled_app/Gemfile", :dir => tmp
+ bundle "install --gemfile #{tmp}/bundled_app/Gemfile", dir: tmp
expect(the_bundle).to include_gems "rack 1.0"
end
@@ -103,12 +103,12 @@ RSpec.describe "install in deployment or frozen mode" do
bundle :install
bundle "config set --local deployment true"
bundle :install
- bundle "exec bundle check", :env => { "PATH" => path }
+ bundle "exec bundle check", env: { "PATH" => path }
end
it "works when using path gems from the same path and the version is specified" do
- build_lib "foo", :path => lib_path("nested/foo")
- build_lib "bar", :path => lib_path("nested/bar")
+ build_lib "foo", path: lib_path("nested/foo")
+ build_lib "bar", path: lib_path("nested/bar")
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "foo", "1.0", :path => "#{lib_path("nested")}"
@@ -121,7 +121,7 @@ RSpec.describe "install in deployment or frozen mode" do
end
it "works when path gems are specified twice" do
- build_lib "foo", :path => lib_path("nested/foo")
+ build_lib "foo", path: lib_path("nested/foo")
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => "#{lib_path("nested/foo")}"
@@ -134,14 +134,14 @@ RSpec.describe "install in deployment or frozen mode" do
end
it "works when there are credentials in the source URL" do
- install_gemfile(<<-G, :artifice => "endpoint_strict_basic_authentication", :quiet => true)
+ install_gemfile(<<-G, artifice: "endpoint_strict_basic_authentication", quiet: true)
source "http://user:pass@localgemserver.test/"
gem "rack-obama", ">= 1.0"
G
bundle "config set --local deployment true"
- bundle :install, :artifice => "endpoint_strict_basic_authentication"
+ bundle :install, artifice: "endpoint_strict_basic_authentication"
end
it "works with sources given by a block" do
@@ -183,50 +183,10 @@ RSpec.describe "install in deployment or frozen mode" do
bundle "config set --local deployment true"
end
- it "prevents the replace by default" do
- bundle :install, :raise_on_error => false
-
- expect(err).to match(/The list of sources changed/)
- end
-
- context "when allow_deployment_source_credential_changes is true" do
- before { bundle "config set allow_deployment_source_credential_changes true" }
-
- it "allows the replace" do
- bundle :install
-
- expect(out).to match(/Bundle complete!/)
- end
- end
-
- context "when allow_deployment_source_credential_changes is false" do
- before { bundle "config set allow_deployment_source_credential_changes false" }
-
- it "prevents the replace" do
- bundle :install, :raise_on_error => false
-
- expect(err).to match(/The list of sources changed/)
- end
- end
-
- context "when BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES env var is true" do
- before { ENV["BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES"] = "true" }
-
- it "allows the replace" do
- bundle :install
-
- expect(out).to match(/Bundle complete!/)
- end
- end
-
- context "when BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES env var is false" do
- before { ENV["BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES"] = "false" }
-
- it "prevents the replace" do
- bundle :install, :raise_on_error => false
+ it "allows the replace" do
+ bundle :install
- expect(err).to match(/The list of sources changed/)
- end
+ expect(out).to match(/Bundle complete!/)
end
end
@@ -237,7 +197,9 @@ RSpec.describe "install in deployment or frozen mode" do
it "installs gems by default to vendor/bundle" do
bundle "config set deployment true"
- bundle "install"
+ expect do
+ bundle "install"
+ end.not_to change { bundled_app_lock.mtime }
expect(out).to include("vendor/bundle")
end
@@ -250,17 +212,21 @@ RSpec.describe "install in deployment or frozen mode" do
it "installs gems to custom path if specified, even when configured through ENV" do
bundle "config set deployment true"
- bundle "install", :env => { "BUNDLE_PATH" => "vendor/bundle2" }
+ bundle "install", env: { "BUNDLE_PATH" => "vendor/bundle2" }
expect(out).to include("vendor/bundle2")
end
it "works with the `frozen` setting" do
bundle "config set frozen true"
- bundle "install"
+ expect do
+ bundle "install"
+ end.not_to change { bundled_app_lock.mtime }
end
it "works with BUNDLE_FROZEN if you didn't change anything" do
- bundle :install, :env => { "BUNDLE_FROZEN" => "true" }
+ expect do
+ bundle :install, env: { "BUNDLE_FROZEN" => "true" }
+ end.not_to change { bundled_app_lock.mtime }
end
it "explodes with the `deployment` setting if you make a change and don't check in the lockfile" do
@@ -271,8 +237,8 @@ RSpec.describe "install in deployment or frozen mode" do
G
bundle "config set --local deployment true"
- bundle :install, :raise_on_error => false
- expect(err).to include("deployment mode")
+ bundle :install, raise_on_error: false
+ expect(err).to include("frozen mode")
expect(err).to include("You have added to the Gemfile")
expect(err).to include("* rack-obama")
expect(err).not_to include("You have deleted from the Gemfile")
@@ -292,22 +258,60 @@ RSpec.describe "install in deployment or frozen mode" do
bundle "config set --local path .bundle"
bundle "config set --local without development"
bundle "config set --local deployment true"
- bundle :install, :env => { "DEBUG" => "1" }
+ bundle :install, env: { "DEBUG" => "1" }
run "puts :WIN"
expect(out).to eq("WIN")
end
- it "works if a gem is missing, but it's on a different platform, and the Gemfile has no global source", :bundler => "< 3" do
+ it "works if a gem is missing, but it's on a different platform" do
+ build_repo2
+
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+
source "#{file_uri_for(gem_repo1)}" do
gem "rake", platform: :#{not_local_tag}
end
G
- bundle :install, :env => { "BUNDLE_FROZEN" => "true" }
+ bundle :install, env: { "BUNDLE_FROZEN" => "true" }
expect(last_command).to be_success
end
+ it "shows a good error if a gem is missing from the lockfile" do
+ build_repo4 do
+ build_gem "foo"
+ build_gem "bar"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "foo"
+ gem "bar"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo
+ bar
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle :install, env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false, artifice: "compact_index"
+ expect(err).to include("Your lock file is missing \"bar\", but the lockfile can't be updated because frozen mode is set")
+ end
+
it "explodes if a path gem is missing" do
build_lib "path_gem"
install_gemfile <<-G
@@ -320,7 +324,7 @@ RSpec.describe "install in deployment or frozen mode" do
bundle "config set --local path .bundle"
bundle "config set --local deployment true"
- bundle :install, :raise_on_error => false
+ bundle :install, raise_on_error: false
expect(err).to include("The path `#{lib_path("path_gem-1.0")}` does not exist.")
end
@@ -332,8 +336,8 @@ RSpec.describe "install in deployment or frozen mode" do
G
ENV["BUNDLE_FROZEN"] = "1"
- bundle "install", :raise_on_error => false
- expect(err).to include("deployment mode")
+ bundle "install", raise_on_error: false
+ expect(err).to include("frozen mode")
expect(err).to include("You have added to the Gemfile")
expect(err).to include("* rack-obama")
expect(err).not_to include("You have deleted from the Gemfile")
@@ -348,8 +352,8 @@ RSpec.describe "install in deployment or frozen mode" do
G
ENV["BUNDLE_DEPLOYMENT"] = "true"
- bundle "install", :raise_on_error => false
- expect(err).to include("deployment mode")
+ bundle "install", raise_on_error: false
+ expect(err).to include("frozen mode")
expect(err).to include("You have added to the Gemfile")
expect(err).to include("* rack-obama")
expect(err).not_to include("You have deleted from the Gemfile")
@@ -379,7 +383,7 @@ RSpec.describe "install in deployment or frozen mode" do
ENV["BUNDLE_FROZEN"] = "false"
ENV["BUNDLE_DEPLOYMENT"] = "false"
bundle "install"
- expect(out).not_to include("deployment mode")
+ expect(out).not_to include("frozen mode")
expect(out).not_to include("You have added to the Gemfile")
expect(out).not_to include("* rack-obama")
end
@@ -391,8 +395,8 @@ RSpec.describe "install in deployment or frozen mode" do
G
bundle "config set --local deployment true"
- bundle :install, :raise_on_error => false
- expect(err).to include("deployment mode")
+ bundle :install, raise_on_error: false
+ expect(err).to include("frozen mode")
expect(err).to include("You have added to the Gemfile:\n* activesupport\n\n")
expect(err).to include("You have deleted from the Gemfile:\n* rack")
expect(err).not_to include("You have changed in the Gemfile")
@@ -405,8 +409,8 @@ RSpec.describe "install in deployment or frozen mode" do
G
bundle "config set --local deployment true"
- bundle :install, :raise_on_error => false
- expect(err).to include("deployment mode")
+ bundle :install, raise_on_error: false
+ expect(err).to include("frozen mode")
expect(err).not_to include("You have added to the Gemfile")
expect(err).to include("You have changed in the Gemfile:\n* rack from `no specified source` to `git://hubz.com`")
end
@@ -425,16 +429,16 @@ RSpec.describe "install in deployment or frozen mode" do
G
bundle "config set --local deployment true"
- bundle :install, :raise_on_error => false
- expect(err).to include("deployment mode")
+ bundle :install, raise_on_error: false
+ expect(err).to include("frozen mode")
expect(err).not_to include("You have deleted from the Gemfile")
expect(err).not_to include("You have added to the Gemfile")
expect(err).to include("You have changed in the Gemfile:\n* rack from `#{lib_path("rack-1.0")}` to `no specified source`")
end
it "explodes if you change a source" do
- build_lib "foo", :path => lib_path("rack/foo")
- build_git "rack", :path => lib_path("rack")
+ build_lib "foo", path: lib_path("rack/foo")
+ build_git "rack", path: lib_path("rack")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -449,8 +453,8 @@ RSpec.describe "install in deployment or frozen mode" do
G
bundle "config set --local deployment true"
- bundle :install, :raise_on_error => false
- expect(err).to include("deployment mode")
+ bundle :install, raise_on_error: false
+ expect(err).to include("frozen mode")
expect(err).to include("You have changed in the Gemfile:\n* rack from `#{lib_path("rack")}` to `no specified source`")
expect(err).not_to include("You have added to the Gemfile")
expect(err).not_to include("You have deleted from the Gemfile")
@@ -467,23 +471,23 @@ RSpec.describe "install in deployment or frozen mode" do
gem "rack-obama"
G
- run "require 'rack'", :raise_on_error => false
- expect(err).to include strip_whitespace(<<-E).strip
-The dependencies in your gemfile changed
+ run "require 'rack'", raise_on_error: false
+ expect(err).to include <<~E.strip
+ The dependencies in your gemfile changed, but the lockfile can't be updated because frozen mode is set (Bundler::ProductionError)
-You have added to the Gemfile:
-* rack (= 1.0.0)
-* rack-obama
+ You have added to the Gemfile:
+ * rack (= 1.0.0)
+ * rack-obama
-You have deleted from the Gemfile:
-* rack
+ You have deleted from the Gemfile:
+ * rack
E
end
end
context "with path in Gemfile and packed" do
it "works fine after bundle package and bundle install --local" do
- build_lib "foo", :path => lib_path("foo")
+ build_lib "foo", path: lib_path("foo")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => "#{lib_path("foo")}"
@@ -502,7 +506,7 @@ You have deleted from the Gemfile:
simulate_new_machine
bundle "config set --local deployment true"
bundle "install --verbose"
- expect(out).not_to include("You are trying to install in deployment mode after changing your Gemfile")
+ expect(out).not_to include("but the lockfile can't be updated because frozen mode is set")
expect(out).not_to include("You have added to the Gemfile")
expect(out).not_to include("You have deleted from the Gemfile")
expect(out).to include("vendor/cache/foo")
diff --git a/spec/bundler/install/failure_spec.rb b/spec/bundler/install/failure_spec.rb
index 4a9c33754f..f972a37bf6 100644
--- a/spec/bundler/install/failure_spec.rb
+++ b/spec/bundler/install/failure_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe "bundle install" do
end
end
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo2)}"
gem "rails"
G
@@ -39,7 +39,7 @@ In Gemfile:
end
it "removes the downloaded .gem" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo4)}"
gem "a"
G
diff --git a/spec/bundler/install/gemfile/eval_gemfile_spec.rb b/spec/bundler/install/gemfile/eval_gemfile_spec.rb
index 02283291b4..cfa66e5986 100644
--- a/spec/bundler/install/gemfile/eval_gemfile_spec.rb
+++ b/spec/bundler/install/gemfile/eval_gemfile_spec.rb
@@ -2,7 +2,7 @@
RSpec.describe "bundle install with gemfile that uses eval_gemfile" do
before do
- build_lib("gunks", :path => bundled_app.join("gems/gunks")) do |s|
+ build_lib("gunks", path: bundled_app.join("gems/gunks")) do |s|
s.name = "gunks"
s.version = "0.0.1"
end
@@ -24,7 +24,7 @@ RSpec.describe "bundle install with gemfile that uses eval_gemfile" do
expect(out).to include("Resolving dependencies")
expect(out).to include("Bundle complete")
- expect(the_bundle).to include_gem "gunks 0.0.1", :source => "path@#{bundled_app("gems", "gunks")}"
+ expect(the_bundle).to include_gem "gunks 0.0.1", source: "path@#{bundled_app("gems", "gunks")}"
end
end
@@ -64,7 +64,7 @@ RSpec.describe "bundle install with gemfile that uses eval_gemfile" do
context "eval-ed Gemfile has relative-path gems" do
before do
- build_lib("a", :path => bundled_app("gems/a"))
+ build_lib("a", path: bundled_app("gems/a"))
create_file bundled_app("nested/Gemfile-nested"), <<-G
source "#{file_uri_for(gem_repo1)}"
gem "a", :path => "../gems/a"
@@ -102,7 +102,7 @@ RSpec.describe "bundle install with gemfile that uses eval_gemfile" do
expect(out).to include("Resolving dependencies")
expect(out).to include("Bundle complete")
- expect(the_bundle).to include_gem "gunks 0.0.1", :source => "path@#{bundled_app("gems", "gunks")}"
+ expect(the_bundle).to include_gem "gunks 0.0.1", source: "path@#{bundled_app("gems", "gunks")}"
end
end
diff --git a/spec/bundler/install/gemfile/force_ruby_platform_spec.rb b/spec/bundler/install/gemfile/force_ruby_platform_spec.rb
index 0e9f1f0292..a29b79ad62 100644
--- a/spec/bundler/install/gemfile/force_ruby_platform_spec.rb
+++ b/spec/bundler/install/gemfile/force_ruby_platform_spec.rb
@@ -39,7 +39,7 @@ RSpec.describe "bundle install with force_ruby_platform DSL option", :jruby do
end
it "still respects a global `force_ruby_platform` config" do
- install_gemfile <<-G, :env => { "BUNDLE_FORCE_RUBY_PLATFORM" => "true" }
+ install_gemfile <<-G, env: { "BUNDLE_FORCE_RUBY_PLATFORM" => "true" }
source "#{file_uri_for(gem_repo4)}"
gem "platform_specific_forced", :force_ruby_platform => true
@@ -105,7 +105,7 @@ RSpec.describe "bundle install with force_ruby_platform DSL option", :jruby do
end
it "ignores ruby variants for the transitive dependencies" do
- install_gemfile <<-G, :env => { "DEBUG_RESOLVER" => "true" }
+ install_gemfile <<-G, env: { "DEBUG_RESOLVER" => "true" }
source "#{file_uri_for(gem_repo4)}"
gem "depends_on_platform_specific", :force_ruby_platform => true
@@ -114,5 +114,33 @@ RSpec.describe "bundle install with force_ruby_platform DSL option", :jruby do
expect(the_bundle).to include_gems "depends_on_platform_specific 1.0.0 RUBY"
expect(the_bundle).to include_gems "platform_specific 1.0.0 #{Bundler.local_platform}"
end
+
+ it "reinstalls the ruby variant when a platform specific variant is already installed, the lockile has only RUBY platform, and :force_ruby_platform is used in the Gemfile" do
+ lockfile <<-L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}
+ specs:
+ platform_specific (1.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ platform_specific
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ system_gems "platform_specific-1.0-#{Gem::Platform.local}", path: default_bundle_path
+
+ install_gemfile <<-G, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }, artifice: "compact_index"
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "platform_specific", :force_ruby_platform => true
+ G
+
+ expect(the_bundle).to include_gems "platform_specific 1.0.0 RUBY"
+ end
end
end
diff --git a/spec/bundler/install/gemfile/gemspec_spec.rb b/spec/bundler/install/gemfile/gemspec_spec.rb
index 2aa4214818..63778567cf 100644
--- a/spec/bundler/install/gemfile/gemspec_spec.rb
+++ b/spec/bundler/install/gemfile/gemspec_spec.rb
@@ -28,8 +28,18 @@ RSpec.describe "bundle install from an existing gemspec" do
x64_mingw_archs.join("\n ")
end
+ def x64_mingw_checksums(checksums)
+ x64_mingw_archs.each do |arch|
+ if arch == "x64-mingw-ucrt"
+ checksums.no_checksum "platform_specific", "1.0", arch
+ else
+ checksums.checksum gem_repo2, "platform_specific", "1.0", arch
+ end
+ end
+ end
+
it "should install runtime and development dependencies" do
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp.join("foo")) do |s|
s.write("Gemfile", "source :rubygems\ngemspec")
s.add_dependency "bar", "=1.0.0"
s.add_development_dependency "bar-dev", "=1.0.0"
@@ -40,11 +50,11 @@ RSpec.describe "bundle install from an existing gemspec" do
G
expect(the_bundle).to include_gems "bar 1.0.0"
- expect(the_bundle).to include_gems "bar-dev 1.0.0", :groups => :development
+ expect(the_bundle).to include_gems "bar-dev 1.0.0", groups: :development
end
it "that is hidden should install runtime and development dependencies" do
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp.join("foo")) do |s|
s.write("Gemfile", "source :rubygems\ngemspec")
s.add_dependency "bar", "=1.0.0"
s.add_development_dependency "bar-dev", "=1.0.0"
@@ -57,7 +67,7 @@ RSpec.describe "bundle install from an existing gemspec" do
G
expect(the_bundle).to include_gems "bar 1.0.0"
- expect(the_bundle).to include_gems "bar-dev 1.0.0", :groups => :development
+ expect(the_bundle).to include_gems "bar-dev 1.0.0", groups: :development
end
it "should handle a list of requirements" do
@@ -66,7 +76,7 @@ RSpec.describe "bundle install from an existing gemspec" do
build_gem "baz", "1.1"
end
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp.join("foo")) do |s|
s.write("Gemfile", "source :rubygems\ngemspec")
s.add_dependency "baz", ">= 1.0", "< 1.1"
end
@@ -79,29 +89,29 @@ RSpec.describe "bundle install from an existing gemspec" do
end
it "should raise if there are no gemspecs available" do
- build_lib("foo", :path => tmp.join("foo"), :gemspec => false)
+ build_lib("foo", path: tmp.join("foo"), gemspec: false)
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo2)}"
gemspec :path => '#{tmp.join("foo")}'
G
- expect(err).to match(/There are no gemspecs at #{tmp.join('foo')}/)
+ expect(err).to match(/There are no gemspecs at #{tmp.join("foo")}/)
end
it "should raise if there are too many gemspecs available" do
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp.join("foo")) do |s|
s.write("foo2.gemspec", build_spec("foo", "4.0").first.to_ruby)
end
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo2)}"
gemspec :path => '#{tmp.join("foo")}'
G
- expect(err).to match(/There are multiple gemspecs at #{tmp.join('foo')}/)
+ expect(err).to match(/There are multiple gemspecs at #{tmp.join("foo")}/)
end
it "should pick a specific gemspec" do
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp.join("foo")) do |s|
s.write("foo2.gemspec", "")
s.add_dependency "bar", "=1.0.0"
s.add_development_dependency "bar-dev", "=1.0.0"
@@ -113,11 +123,11 @@ RSpec.describe "bundle install from an existing gemspec" do
G
expect(the_bundle).to include_gems "bar 1.0.0"
- expect(the_bundle).to include_gems "bar-dev 1.0.0", :groups => :development
+ expect(the_bundle).to include_gems "bar-dev 1.0.0", groups: :development
end
it "should use a specific group for development dependencies" do
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp.join("foo")) do |s|
s.write("foo2.gemspec", "")
s.add_dependency "bar", "=1.0.0"
s.add_development_dependency "bar-dev", "=1.0.0"
@@ -129,32 +139,32 @@ RSpec.describe "bundle install from an existing gemspec" do
G
expect(the_bundle).to include_gems "bar 1.0.0"
- expect(the_bundle).not_to include_gems "bar-dev 1.0.0", :groups => :development
- expect(the_bundle).to include_gems "bar-dev 1.0.0", :groups => :dev
+ expect(the_bundle).not_to include_gems "bar-dev 1.0.0", groups: :development
+ expect(the_bundle).to include_gems "bar-dev 1.0.0", groups: :dev
end
it "should match a lockfile even if the gemspec defines development dependencies" do
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp.join("foo")) do |s|
s.write("Gemfile", "source '#{file_uri_for(gem_repo1)}'\ngemspec")
s.add_dependency "actionpack", "=2.3.2"
- s.add_development_dependency "rake", "=13.0.1"
+ s.add_development_dependency "rake", rake_version
end
- bundle "install", :dir => tmp.join("foo")
+ bundle "install", dir: tmp.join("foo")
# This should really be able to rely on $stderr, but, it's not written
# right, so we can't. In fact, this is a bug negation test, and so it'll
# ghost pass in future, and will only catch a regression if the message
# doesn't change. Exit codes should be used correctly (they can be more
# than just 0 and 1).
bundle "config set --local deployment true"
- output = bundle("install", :dir => tmp.join("foo"))
+ output = bundle("install", dir: tmp.join("foo"))
expect(output).not_to match(/You have added to the Gemfile/)
expect(output).not_to match(/You have deleted from the Gemfile/)
- expect(output).not_to match(/install in deployment mode after changing/)
+ expect(output).not_to match(/the lockfile can't be updated because frozen mode is set/)
end
it "should match a lockfile without needing to re-resolve" do
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp.join("foo")) do |s|
s.add_dependency "rack"
end
@@ -163,7 +173,7 @@ RSpec.describe "bundle install from an existing gemspec" do
gemspec :path => '#{tmp.join("foo")}'
G
- bundle "install", :verbose => true
+ bundle "install", verbose: true
message = "Found no changes, using resolution from the lockfile"
expect(out.scan(message).size).to eq(1)
@@ -172,7 +182,7 @@ RSpec.describe "bundle install from an existing gemspec" do
it "should match a lockfile without needing to re-resolve with development dependencies" do
simulate_platform java
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp.join("foo")) do |s|
s.add_dependency "rack"
s.add_development_dependency "thin"
end
@@ -182,34 +192,34 @@ RSpec.describe "bundle install from an existing gemspec" do
gemspec :path => '#{tmp.join("foo")}'
G
- bundle "install", :verbose => true
+ bundle "install", verbose: true
message = "Found no changes, using resolution from the lockfile"
expect(out.scan(message).size).to eq(1)
end
it "should match a lockfile on non-ruby platforms with a transitive platform dependency", :jruby_only do
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp.join("foo")) do |s|
s.add_dependency "platform_specific"
end
- system_gems "platform_specific-1.0-java", :path => default_bundle_path
+ system_gems "platform_specific-1.0-java", path: default_bundle_path
install_gemfile <<-G
gemspec :path => '#{tmp.join("foo")}'
G
- bundle "update --bundler", :artifice => "compact_index", :verbose => true
+ bundle "update --bundler", artifice: "compact_index", verbose: true
expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 JAVA"
end
it "should evaluate the gemspec in its directory" do
- build_lib("foo", :path => tmp.join("foo"))
+ build_lib("foo", path: tmp.join("foo"))
File.open(tmp.join("foo/foo.gemspec"), "w") do |s|
s.write "raise 'ahh' unless Dir.pwd == '#{tmp.join("foo")}'"
end
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
gemspec :path => '#{tmp.join("foo")}'
G
expect(last_command.stdboth).not_to include("ahh")
@@ -223,7 +233,7 @@ RSpec.describe "bundle install from an existing gemspec" do
# so emulate that
system_gems %w[rack-1.0.0 rack-0.9.1 rack-obama-1.0]
- build_lib("foo", :path => bundled_app)
+ build_lib("foo", path: bundled_app)
gemspec = bundled_app("foo.gemspec").read
bundled_app("foo.gemspec").open("w") do |f|
f.write "#{gemspec.strip}.tap { gem 'rack-obama'; require 'rack/obama' }"
@@ -238,14 +248,14 @@ RSpec.describe "bundle install from an existing gemspec" do
end
it "allows conflicts" do
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp.join("foo")) do |s|
s.version = "1.0.0"
s.add_dependency "bar", "= 1.0.0"
end
- build_gem "deps", :to_bundle => true do |s|
+ build_gem "deps", to_bundle: true do |s|
s.add_dependency "foo", "= 0.0.1"
end
- build_gem "foo", "0.0.1", :to_bundle => true
+ build_gem "foo", "0.0.1", to_bundle: true
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
@@ -257,7 +267,7 @@ RSpec.describe "bundle install from an existing gemspec" do
end
it "does not break Gem.finish_resolve with conflicts" do
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp.join("foo")) do |s|
s.version = "1.0.0"
s.add_dependency "bar", "= 1.0.0"
end
@@ -281,14 +291,14 @@ RSpec.describe "bundle install from an existing gemspec" do
end
it "handles downgrades" do
- build_lib "omg", "2.0", :path => lib_path("omg")
+ build_lib "omg", "2.0", path: lib_path("omg")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gemspec :path => "#{lib_path("omg")}"
G
- build_lib "omg", "1.0", :path => lib_path("omg")
+ build_lib "omg", "1.0", path: lib_path("omg")
bundle :install
@@ -298,7 +308,7 @@ RSpec.describe "bundle install from an existing gemspec" do
context "in deployment mode" do
context "when the lockfile was not updated after a change to the gemspec's dependencies" do
it "reports that installation failed" do
- build_lib "cocoapods", :path => bundled_app do |s|
+ build_lib "cocoapods", path: bundled_app do |s|
s.add_dependency "activesupport", ">= 1"
end
@@ -309,12 +319,12 @@ RSpec.describe "bundle install from an existing gemspec" do
expect(the_bundle).to include_gems("cocoapods 1.0", "activesupport 2.3.5")
- build_lib "cocoapods", :path => bundled_app do |s|
+ build_lib "cocoapods", path: bundled_app do |s|
s.add_dependency "activesupport", ">= 1.0.1"
end
bundle "config set --local deployment true"
- bundle :install, :raise_on_error => false
+ bundle :install, raise_on_error: false
expect(err).to include("changed")
end
@@ -324,13 +334,13 @@ RSpec.describe "bundle install from an existing gemspec" do
context "when child gemspecs conflict with a released gemspec" do
before do
# build the "parent" gem that depends on another gem in the same repo
- build_lib "source_conflict", :path => bundled_app do |s|
+ build_lib "source_conflict", path: bundled_app do |s|
s.add_dependency "rack_middleware"
end
# build the "child" gem that is the same version as a released gem, but
# has completely different and conflicting dependency requirements
- build_lib "rack_middleware", "1.0", :path => bundled_app("rack_middleware") do |s|
+ build_lib "rack_middleware", "1.0", path: bundled_app("rack_middleware") do |s|
s.add_dependency "rack", "1.0" # anything other than 0.9.1
end
end
@@ -349,7 +359,7 @@ RSpec.describe "bundle install from an existing gemspec" do
let(:source_uri) { "http://localgemserver.test" }
before do
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp.join("foo")) do |s|
s.add_dependency "rack", "=1.0.0"
end
@@ -358,6 +368,10 @@ RSpec.describe "bundle install from an existing gemspec" do
gemspec :path => "../foo"
G
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "foo", "1.0"
+ end
+
lockfile <<-L
PATH
remote: ../foo
@@ -375,7 +389,7 @@ RSpec.describe "bundle install from an existing gemspec" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -394,14 +408,14 @@ RSpec.describe "bundle install from an existing gemspec" do
end
it "should install" do
- results = bundle "install", :artifice => "endpoint"
+ results = bundle "install", artifice: "endpoint"
expect(results).to include("Installing rack 1.0.0")
expect(the_bundle).to include_gems "rack 1.0.0"
end
end
it "should install", :jruby do
- results = bundle "install", :artifice => "endpoint"
+ results = bundle "install", artifice: "endpoint"
expect(results).to include("Installing rack 1.0.0")
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -416,7 +430,7 @@ RSpec.describe "bundle install from an existing gemspec" do
end
end
- build_lib "foo", :path => bundled_app do |s|
+ build_lib "foo", path: bundled_app do |s|
if platform_specific_type == :runtime
s.add_runtime_dependency dependency
elsif platform_specific_type == :development
@@ -448,7 +462,15 @@ RSpec.describe "bundle install from an existing gemspec" do
context "as a runtime dependency" do
it "keeps all platform dependencies in the lockfile" do
expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 RUBY"
- expect(lockfile).to eq strip_whitespace(<<-L)
+
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "foo", "1.0"
+ c.checksum gem_repo2, "platform_specific", "1.0"
+ c.checksum gem_repo2, "platform_specific", "1.0", "java"
+ x64_mingw_checksums(c)
+ end
+
+ expect(lockfile).to eq <<~L
PATH
remote: .
specs:
@@ -469,7 +491,7 @@ RSpec.describe "bundle install from an existing gemspec" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -481,7 +503,15 @@ RSpec.describe "bundle install from an existing gemspec" do
it "keeps all platform dependencies in the lockfile" do
expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 RUBY"
- expect(lockfile).to eq strip_whitespace(<<-L)
+
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "foo", "1.0"
+ c.checksum gem_repo2, "platform_specific", "1.0"
+ c.checksum gem_repo2, "platform_specific", "1.0", "java"
+ x64_mingw_checksums(c)
+ end
+
+ expect(lockfile).to eq <<~L
PATH
remote: .
specs:
@@ -502,7 +532,7 @@ RSpec.describe "bundle install from an existing gemspec" do
DEPENDENCIES
foo!
platform_specific
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -515,7 +545,16 @@ RSpec.describe "bundle install from an existing gemspec" do
it "keeps all platform dependencies in the lockfile" do
expect(the_bundle).to include_gems "foo 1.0", "indirect_platform_specific 1.0", "platform_specific 1.0 RUBY"
- expect(lockfile).to eq strip_whitespace(<<-L)
+
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "foo", "1.0"
+ c.checksum gem_repo2, "indirect_platform_specific", "1.0"
+ c.checksum gem_repo2, "platform_specific", "1.0"
+ c.checksum gem_repo2, "platform_specific", "1.0", "java"
+ x64_mingw_checksums(c)
+ end
+
+ expect(lockfile).to eq <<~L
PATH
remote: .
specs:
@@ -538,7 +577,7 @@ RSpec.describe "bundle install from an existing gemspec" do
DEPENDENCIES
foo!
indirect_platform_specific
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -550,7 +589,7 @@ RSpec.describe "bundle install from an existing gemspec" do
context "with multiple platforms" do
before do
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp.join("foo")) do |s|
s.version = "1.0.0"
s.add_development_dependency "rack"
s.write "foo-universal-java.gemspec", build_spec("foo", "1.0.0", "universal-java") {|sj| sj.runtime "rack", "1.0.0" }.first.to_ruby
@@ -584,7 +623,7 @@ RSpec.describe "bundle install from an existing gemspec" do
context "with multiple platforms and resolving for more specific platforms" do
before do
- build_lib("chef", :path => tmp.join("chef")) do |s|
+ build_lib("chef", path: tmp.join("chef")) do |s|
s.version = "17.1.17"
s.write "chef-universal-mingw32.gemspec", build_spec("chef", "17.1.17", "universal-mingw32") {|sw| sw.runtime "win32-api", "~> 1.5.3" }.first.to_ruby
end
@@ -602,6 +641,12 @@ RSpec.describe "bundle install from an existing gemspec" do
gemspec :path => "../chef"
G
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "chef", "17.1.17"
+ c.no_checksum "chef", "17.1.17", "universal-mingw32"
+ c.checksum gem_repo4, "win32-api", "1.5.3", "universal-mingw32"
+ end
+
initial_lockfile = <<~L
PATH
remote: ../chef
@@ -622,7 +667,7 @@ RSpec.describe "bundle install from an existing gemspec" do
DEPENDENCIES
chef!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -637,7 +682,7 @@ RSpec.describe "bundle install from an existing gemspec" do
context "with multiple locked platforms" do
before do
- build_lib("activeadmin", :path => tmp.join("activeadmin")) do |s|
+ build_lib("activeadmin", path: tmp.join("activeadmin")) do |s|
s.version = "2.9.0"
s.add_dependency "railties", ">= 5.2", "< 6.2"
end
@@ -660,6 +705,12 @@ RSpec.describe "bundle install from an existing gemspec" do
end
it "does not remove the platform specific specs from the lockfile when re-resolving due to gemspec changes" do
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "activeadmin", "2.9.0"
+ c.no_checksum "jruby-openssl", "0.10.7", "java"
+ c.checksum gem_repo4, "railties", "6.1.4"
+ end
+
expect(lockfile).to eq <<~L
PATH
remote: ../activeadmin
@@ -679,7 +730,7 @@ RSpec.describe "bundle install from an existing gemspec" do
DEPENDENCIES
activeadmin!
jruby-openssl
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
diff --git a/spec/bundler/install/gemfile/git_spec.rb b/spec/bundler/install/gemfile/git_spec.rb
index c96a78bc1c..45ee7b44d1 100644
--- a/spec/bundler/install/gemfile/git_spec.rb
+++ b/spec/bundler/install/gemfile/git_spec.rb
@@ -26,15 +26,22 @@ RSpec.describe "bundle install with git sources" do
expect(out).to eq("WIN")
end
- it "caches the git repo", :bundler => "< 3" do
- expect(Dir["#{default_bundle_path}/cache/bundler/git/foo-1.0-*"]).to have_attributes :size => 1
+ it "caches the git repo", bundler: "< 3" do
+ expect(Dir["#{default_bundle_path}/cache/bundler/git/foo-1.0-*"]).to have_attributes size: 1
+ end
+
+ it "does not write to cache on bundler/setup" do
+ cache_path = default_bundle_path.join("cache")
+ FileUtils.rm_rf(cache_path)
+ ruby "require 'bundler/setup'"
+ expect(cache_path).not_to exist
end
it "caches the git repo globally and properly uses the cached repo on the next invocation" do
simulate_new_machine
bundle "config set global_gem_cache true"
bundle :install
- expect(Dir["#{home}/.bundle/cache/git/foo-1.0-*"]).to have_attributes :size => 1
+ expect(Dir["#{home}/.bundle/cache/git/foo-1.0-*"]).to have_attributes size: 1
bundle "install --verbose"
expect(err).to be_empty
@@ -62,7 +69,7 @@ RSpec.describe "bundle install with git sources" do
it "does not update the git source implicitly" do
update_git "foo"
- install_gemfile bundled_app2("Gemfile"), <<-G, :dir => bundled_app2
+ install_gemfile bundled_app2("Gemfile"), <<-G, dir: bundled_app2
source "#{file_uri_for(gem_repo1)}"
git "#{lib_path("foo-1.0")}" do
gem 'foo'
@@ -85,7 +92,7 @@ RSpec.describe "bundle install with git sources" do
it "complains if pinned specs don't exist in the git repo" do
build_git "foo"
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
gem "foo", "1.1", :git => "#{lib_path("foo-1.0")}"
G
@@ -98,7 +105,7 @@ RSpec.describe "bundle install with git sources" do
s.platform = "java"
end
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
platforms :jruby do
gem "only_java", "1.2", :git => "#{lib_path("only_java-1.0-java")}"
@@ -118,7 +125,7 @@ RSpec.describe "bundle install with git sources" do
s.write "only_java1-0.gemspec", File.read("#{lib_path("only_java-1.0-java")}/only_java.gemspec")
end
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
platforms :jruby do
gem "only_java", "1.2", :git => "#{lib_path("only_java-1.1-java")}"
@@ -134,7 +141,7 @@ RSpec.describe "bundle install with git sources" do
FileUtils.mv bundled_app, tmp("bundled_app.bck")
- expect(the_bundle).to include_gems "foo 1.0", :dir => tmp("bundled_app.bck")
+ expect(the_bundle).to include_gems "foo 1.0", dir: tmp("bundled_app.bck")
end
it "can still install after moving the application directory" do
@@ -143,7 +150,7 @@ RSpec.describe "bundle install with git sources" do
FileUtils.mv bundled_app, tmp("bundled_app.bck")
- update_git "foo", "1.1", :path => lib_path("foo-1.0")
+ update_git "foo", "1.1", path: lib_path("foo-1.0")
gemfile tmp("bundled_app.bck/Gemfile"), <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -154,9 +161,9 @@ RSpec.describe "bundle install with git sources" do
gem "rack", "1.0"
G
- bundle "update foo", :dir => tmp("bundled_app.bck")
+ bundle "update foo", dir: tmp("bundled_app.bck")
- expect(the_bundle).to include_gems "foo 1.1", "rack 1.0", :dir => tmp("bundled_app.bck")
+ expect(the_bundle).to include_gems "foo 1.1", "rack 1.0", dir: tmp("bundled_app.bck")
end
end
@@ -238,9 +245,9 @@ RSpec.describe "bundle install with git sources" do
it "works when a tag that does not look like a commit hash is used as the value of :ref" do
build_git "foo"
- @remote = build_git("bar", :bare => true)
- update_git "foo", :remote => file_uri_for(@remote.path)
- update_git "foo", :push => "main"
+ @remote = build_git("bar", bare: true)
+ update_git "foo", remote: file_uri_for(@remote.path)
+ update_git "foo", push: "main"
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -248,8 +255,8 @@ RSpec.describe "bundle install with git sources" do
G
# Create a new tag on the remote that needs fetching
- update_git "foo", :tag => "v1.0.0"
- update_git "foo", :push => "v1.0.0"
+ update_git "foo", tag: "v1.0.0"
+ update_git "foo", push: "v1.0.0"
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -261,14 +268,14 @@ RSpec.describe "bundle install with git sources" do
it "works when the revision is a non-head ref" do
# want to ensure we don't fallback to main
- update_git "foo", :path => lib_path("foo-1.0") do |s|
+ update_git "foo", path: lib_path("foo-1.0") do |s|
s.write("lib/foo.rb", "raise 'FAIL'")
end
- sys_exec("git update-ref -m \"Bundler Spec!\" refs/bundler/1 main~1", :dir => lib_path("foo-1.0"))
+ sys_exec("git update-ref -m \"Bundler Spec!\" refs/bundler/1 main~1", dir: lib_path("foo-1.0"))
# want to ensure we don't fallback to HEAD
- update_git "foo", :path => lib_path("foo-1.0"), :branch => "rando" do |s|
+ update_git "foo", path: lib_path("foo-1.0"), branch: "rando" do |s|
s.write("lib/foo.rb", "raise 'FAIL_FROM_RANDO'")
end
@@ -297,14 +304,14 @@ RSpec.describe "bundle install with git sources" do
G
# want to ensure we don't fallback to main
- update_git "foo", :path => lib_path("foo-1.0") do |s|
+ update_git "foo", path: lib_path("foo-1.0") do |s|
s.write("lib/foo.rb", "raise 'FAIL'")
end
- sys_exec("git update-ref -m \"Bundler Spec!\" refs/bundler/1 main~1", :dir => lib_path("foo-1.0"))
+ sys_exec("git update-ref -m \"Bundler Spec!\" refs/bundler/1 main~1", dir: lib_path("foo-1.0"))
# want to ensure we don't fallback to HEAD
- update_git "foo", :path => lib_path("foo-1.0"), :branch => "rando" do |s|
+ update_git "foo", path: lib_path("foo-1.0"), branch: "rando" do |s|
s.write("lib/foo.rb", "raise 'FAIL_FROM_RANDO'")
end
@@ -325,7 +332,7 @@ RSpec.describe "bundle install with git sources" do
end
it "does not download random non-head refs" do
- sys_exec("git update-ref -m \"Bundler Spec!\" refs/bundler/1 main~1", :dir => lib_path("foo-1.0"))
+ sys_exec("git update-ref -m \"Bundler Spec!\" refs/bundler/1 main~1", dir: lib_path("foo-1.0"))
bundle "config set global_gem_cache true"
@@ -337,9 +344,9 @@ RSpec.describe "bundle install with git sources" do
G
# ensure we also git fetch after cloning
- bundle :update, :all => true
+ bundle :update, all: true
- sys_exec("git ls-remote .", :dir => Dir[home(".bundle/cache/git/foo-*")].first)
+ sys_exec("git ls-remote .", dir: Dir[home(".bundle/cache/git/foo-*")].first)
expect(out).not_to include("refs/bundler/1")
end
@@ -350,7 +357,7 @@ RSpec.describe "bundle install with git sources" do
let(:repo) { build_git("foo").path }
it "works" do
- update_git("foo", :path => repo, :branch => branch)
+ update_git("foo", path: repo, branch: branch)
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -367,7 +374,7 @@ RSpec.describe "bundle install with git sources" do
it "works" do
skip "git does not accept this" if Gem.win_platform?
- update_git("foo", :path => repo, :branch => branch)
+ update_git("foo", path: repo, branch: branch)
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -385,7 +392,7 @@ RSpec.describe "bundle install with git sources" do
it "works" do
skip "git does not accept this" if Gem.win_platform?
- update_git("foo", :path => repo, :branch => branch)
+ update_git("foo", path: repo, branch: branch)
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -404,7 +411,7 @@ RSpec.describe "bundle install with git sources" do
let(:repo) { build_git("foo").path }
it "works" do
- update_git("foo", :path => repo, :tag => tag)
+ update_git("foo", path: repo, tag: tag)
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -421,7 +428,7 @@ RSpec.describe "bundle install with git sources" do
it "works" do
skip "git does not accept this" if Gem.win_platform?
- update_git("foo", :path => repo, :tag => tag)
+ update_git("foo", path: repo, tag: tag)
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -439,7 +446,7 @@ RSpec.describe "bundle install with git sources" do
it "works" do
skip "git does not accept this" if Gem.win_platform?
- update_git("foo", :path => repo, :tag => tag)
+ update_git("foo", path: repo, tag: tag)
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -455,7 +462,7 @@ RSpec.describe "bundle install with git sources" do
describe "when specifying local override" do
it "uses the local repository instead of checking a new one out" do
- build_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ build_git "rack", "0.8", path: lib_path("local-rack") do |s|
s.write "lib/rack.rb", "puts :LOCAL"
end
@@ -476,7 +483,7 @@ RSpec.describe "bundle install with git sources" do
FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
- update_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ update_git "rack", "0.8", path: lib_path("local-rack") do |s|
s.write "lib/rack.rb", "puts :LOCAL"
end
@@ -495,7 +502,7 @@ RSpec.describe "bundle install with git sources" do
FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
- update_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ update_git "rack", "0.8", path: lib_path("local-rack") do |s|
s.write "rack.gemspec", build_spec("rack", "0.8") { runtime "rspec", "> 0" }.first.to_ruby
s.write "lib/rack.rb", "puts :LOCAL"
end
@@ -524,7 +531,7 @@ RSpec.describe "bundle install with git sources" do
lockfile0 = File.read(bundled_app_lock)
FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
- update_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ update_git "rack", "0.8", path: lib_path("local-rack") do |s|
s.add_dependency "nokogiri", "1.4.2"
end
@@ -546,7 +553,7 @@ RSpec.describe "bundle install with git sources" do
lockfile0 = File.read(bundled_app_lock)
FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
- update_git "rack", "0.8", :path => lib_path("local-rack")
+ update_git "rack", "0.8", path: lib_path("local-rack")
bundle %(config set local.rack #{lib_path("local-rack")})
bundle :install
@@ -564,8 +571,8 @@ RSpec.describe "bundle install with git sources" do
G
bundle %(config set local.rack #{lib_path("local-rack")})
- bundle :install, :raise_on_error => false
- expect(err).to match(/Cannot use local override for rack-0.8 because #{Regexp.escape(lib_path('local-rack').to_s)} does not exist/)
+ bundle :install, raise_on_error: false
+ expect(err).to match(/Cannot use local override for rack-0.8 because #{Regexp.escape(lib_path("local-rack").to_s)} does not exist/)
solution = "config unset local.rack"
expect(err).to match(/Run `bundle #{solution}` to remove the local override/)
@@ -586,8 +593,8 @@ RSpec.describe "bundle install with git sources" do
G
bundle %(config set local.rack #{lib_path("local-rack")})
- bundle :install, :raise_on_error => false
- expect(err).to match(/Cannot use local override for rack-0.8 at #{Regexp.escape(lib_path('local-rack').to_s)} because :branch is not specified in Gemfile/)
+ bundle :install, raise_on_error: false
+ expect(err).to match(/Cannot use local override for rack-0.8 at #{Regexp.escape(lib_path("local-rack").to_s)} because :branch is not specified in Gemfile/)
solution = "config unset local.rack"
expect(err).to match(/Specify a branch or run `bundle #{solution}` to remove the local override/)
@@ -618,7 +625,7 @@ RSpec.describe "bundle install with git sources" do
FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
- update_git "rack", "0.8", :path => lib_path("local-rack"), :branch => "another" do |s|
+ update_git "rack", "0.8", path: lib_path("local-rack"), branch: "another" do |s|
s.write "lib/rack.rb", "puts :LOCAL"
end
@@ -628,14 +635,14 @@ RSpec.describe "bundle install with git sources" do
G
bundle %(config set local.rack #{lib_path("local-rack")})
- bundle :install, :raise_on_error => false
+ bundle :install, raise_on_error: false
expect(err).to match(/is using branch another but Gemfile specifies main/)
end
it "explodes on invalid revision on install" do
build_git "rack", "0.8"
- build_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ build_git "rack", "0.8", path: lib_path("local-rack") do |s|
s.write "lib/rack.rb", "puts :LOCAL"
end
@@ -645,14 +652,14 @@ RSpec.describe "bundle install with git sources" do
G
bundle %(config set local.rack #{lib_path("local-rack")})
- bundle :install, :raise_on_error => false
+ bundle :install, raise_on_error: false
expect(err).to match(/The Gemfile lock is pointing to revision \w+/)
end
it "does not explode on invalid revision on install" do
build_git "rack", "0.8"
- build_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ build_git "rack", "0.8", path: lib_path("local-rack") do |s|
s.write "lib/rack.rb", "puts :LOCAL"
end
@@ -699,11 +706,11 @@ RSpec.describe "bundle install with git sources" do
it "installs dependencies from git even if a newer gem is available elsewhere" do
system_gems "rack-1.0.0"
- build_lib "rack", "1.0", :path => lib_path("nested/bar") do |s|
+ build_lib "rack", "1.0", path: lib_path("nested/bar") do |s|
s.write "lib/rack.rb", "puts 'WIN OVERRIDE'"
end
- build_git "foo", :path => lib_path("nested") do |s|
+ build_git "foo", path: lib_path("nested") do |s|
s.add_dependency "rack", "= 1.0"
end
@@ -722,7 +729,7 @@ RSpec.describe "bundle install with git sources" do
gem "rack", "0.9.1"
G
- build_git "rack", :path => lib_path("rack")
+ build_git "rack", path: lib_path("rack")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -738,7 +745,7 @@ RSpec.describe "bundle install with git sources" do
gem "rack"
G
- build_git "rack", "1.2", :path => lib_path("rack")
+ build_git "rack", "1.2", path: lib_path("rack")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -751,8 +758,8 @@ RSpec.describe "bundle install with git sources" do
describe "block syntax" do
it "pulls all gems from a git block" do
- build_lib "omg", :path => lib_path("hi2u/omg")
- build_lib "hi2u", :path => lib_path("hi2u")
+ build_lib "omg", path: lib_path("hi2u/omg")
+ build_lib "hi2u", path: lib_path("hi2u")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -800,7 +807,7 @@ RSpec.describe "bundle install with git sources" do
end
it "runs the gemspec in the context of its parent directory" do
- build_lib "bar", :path => lib_path("foo/bar"), :gemspec => false do |s|
+ build_lib "bar", path: lib_path("foo/bar"), gemspec: false do |s|
s.write lib_path("foo/bar/lib/version.rb"), %(BAR_VERSION = '1.0')
s.write "bar.gemspec", <<-G
$:.unshift Dir.pwd
@@ -815,7 +822,7 @@ RSpec.describe "bundle install with git sources" do
G
end
- build_git "foo", :path => lib_path("foo") do |s|
+ build_git "foo", path: lib_path("foo") do |s|
s.write "bin/foo", ""
end
@@ -830,7 +837,7 @@ RSpec.describe "bundle install with git sources" do
end
it "installs from git even if a rubygems gem is present" do
- build_gem "foo", "1.0", :path => lib_path("fake_foo"), :to_system => true do |s|
+ build_gem "foo", "1.0", path: lib_path("fake_foo"), to_system: true do |s|
s.write "lib/foo.rb", "raise 'FAIL'"
end
@@ -845,7 +852,7 @@ RSpec.describe "bundle install with git sources" do
end
it "fakes the gem out if there is no gemspec" do
- build_git "foo", :gemspec => false
+ build_git "foo", gemspec: false
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -863,7 +870,7 @@ RSpec.describe "bundle install with git sources" do
gem "foo", "1.0", :git => "omgomg"
G
- bundle :install, :raise_on_error => false
+ bundle :install, raise_on_error: false
expect(err).to include("Git error:")
expect(err).to include("fatal")
@@ -871,7 +878,7 @@ RSpec.describe "bundle install with git sources" do
end
it "works when the gem path has spaces in it" do
- build_git "foo", :path => lib_path("foo space-1.0")
+ build_git "foo", path: lib_path("foo space-1.0")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -896,12 +903,12 @@ RSpec.describe "bundle install with git sources" do
s.write "lib/forced.rb", "FORCED = '1.1'"
end
- bundle "update", :all => true
+ bundle "update", all: true
expect(the_bundle).to include_gems "forced 1.1"
- sys_exec("git reset --hard HEAD^", :dir => lib_path("forced-1.0"))
+ sys_exec("git reset --hard HEAD^", dir: lib_path("forced-1.0"))
- bundle "update", :all => true
+ bundle "update", all: true
expect(the_bundle).to include_gems "forced 1.0"
end
@@ -913,16 +920,16 @@ RSpec.describe "bundle install with git sources" do
build_git "has_submodule", "1.0" do |s|
s.add_dependency "submodule"
end
- sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", :dir => lib_path("has_submodule-1.0")
- sys_exec "git commit -m \"submodulator\"", :dir => lib_path("has_submodule-1.0")
+ sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", dir: lib_path("has_submodule-1.0")
+ sys_exec "git commit -m \"submodulator\"", dir: lib_path("has_submodule-1.0")
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
git "#{lib_path("has_submodule-1.0")}" do
gem "has_submodule"
end
G
- expect(err).to match(%r{submodule >= 0 could not be found in rubygems repository #{file_uri_for(gem_repo1)}/ or installed locally})
+ expect(err).to match(%r{submodule >= 0 could not be found in rubygems repository #{file_uri_for(gem_repo1)}/, cached gems or installed locally})
expect(the_bundle).not_to include_gems "has_submodule 1.0"
end
@@ -935,8 +942,8 @@ RSpec.describe "bundle install with git sources" do
build_git "has_submodule", "1.0" do |s|
s.add_dependency "submodule"
end
- sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", :dir => lib_path("has_submodule-1.0")
- sys_exec "git commit -m \"submodulator\"", :dir => lib_path("has_submodule-1.0")
+ sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", dir: lib_path("has_submodule-1.0")
+ sys_exec "git commit -m \"submodulator\"", dir: lib_path("has_submodule-1.0")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -955,8 +962,8 @@ RSpec.describe "bundle install with git sources" do
build_git "submodule", "1.0"
build_git "has_submodule", "1.0"
- sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", :dir => lib_path("has_submodule-1.0")
- sys_exec "git commit -m \"submodulator\"", :dir => lib_path("has_submodule-1.0")
+ sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", dir: lib_path("has_submodule-1.0")
+ sys_exec "git commit -m \"submodulator\"", dir: lib_path("has_submodule-1.0")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -1030,7 +1037,7 @@ RSpec.describe "bundle install with git sources" do
FileUtils.mkdir_p(default_bundle_path)
FileUtils.touch(default_bundle_path("bundler"))
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -1042,11 +1049,11 @@ RSpec.describe "bundle install with git sources" do
end
it "does not duplicate git gem sources" do
- build_lib "foo", :path => lib_path("nested/foo")
- build_lib "bar", :path => lib_path("nested/bar")
+ build_lib "foo", path: lib_path("nested/foo")
+ build_lib "bar", path: lib_path("nested/bar")
- build_git "foo", :path => lib_path("nested")
- build_git "bar", :path => lib_path("nested")
+ build_git "foo", path: lib_path("nested")
+ build_git "bar", path: lib_path("nested")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -1059,11 +1066,11 @@ RSpec.describe "bundle install with git sources" do
describe "switching sources" do
it "doesn't explode when switching Path to Git sources" do
- build_gem "foo", "1.0", :to_system => true do |s|
+ build_gem "foo", "1.0", to_system: true do |s|
s.write "lib/foo.rb", "raise 'fail'"
end
- build_lib "foo", "1.0", :path => lib_path("bar/foo")
- build_git "bar", "1.0", :path => lib_path("bar") do |s|
+ build_lib "foo", "1.0", path: lib_path("bar/foo")
+ build_git "bar", "1.0", path: lib_path("bar") do |s|
s.add_dependency "foo"
end
@@ -1138,7 +1145,7 @@ RSpec.describe "bundle install with git sources" do
G
expect(out).to_not match(/Revision.*does not exist/)
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}", :ref => "deadbeef"
G
@@ -1148,7 +1155,7 @@ RSpec.describe "bundle install with git sources" do
it "gives a helpful error message when the remote branch no longer exists" do
build_git "foo"
- install_gemfile <<-G, :env => { "LANG" => "en" }, :raise_on_error => false
+ install_gemfile <<-G, env: { "LANG" => "en" }, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}", :branch => "deadbeef"
G
@@ -1159,7 +1166,7 @@ RSpec.describe "bundle install with git sources" do
describe "bundle install with deployment mode configured and git sources" do
it "works" do
- build_git "valim", :path => lib_path("valim")
+ build_git "valim", path: lib_path("valim")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -1190,7 +1197,7 @@ RSpec.describe "bundle install with git sources" do
end
bundle :install,
- :requires => [lib_path("install_hooks.rb")]
+ requires: [lib_path("install_hooks.rb")]
expect(err_without_deprecations).to eq("Ran pre-install hook: foo-1.0")
end
@@ -1210,7 +1217,7 @@ RSpec.describe "bundle install with git sources" do
end
bundle :install,
- :requires => [lib_path("install_hooks.rb")]
+ requires: [lib_path("install_hooks.rb")]
expect(err_without_deprecations).to eq("Ran post-install hook: foo-1.0")
end
@@ -1229,7 +1236,7 @@ RSpec.describe "bundle install with git sources" do
H
end
- bundle :install, :requires => [lib_path("install_hooks.rb")], :raise_on_error => false
+ bundle :install, requires: [lib_path("install_hooks.rb")], raise_on_error: false
expect(err).to include("failed for foo-1.0")
end
end
@@ -1268,11 +1275,10 @@ RSpec.describe "bundle install with git sources" do
end
it "does not use old extension after ref changes" do
- git_reader = build_git "foo", :no_default => true do |s|
+ git_reader = build_git "foo", no_default: true do |s|
s.extensions = ["ext/extconf.rb"]
s.write "ext/extconf.rb", <<-RUBY
require "mkmf"
- $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"]
create_makefile("foo")
RUBY
s.write "ext/foo.c", "void Init_foo() {}"
@@ -1286,7 +1292,7 @@ RSpec.describe "bundle install with git sources" do
void Init_foo() { rb_define_global_function("foo", &foo, 0); }
C
end
- sys_exec("git commit -m \"commit for iteration #{i}\" ext/foo.c", :dir => git_reader.path)
+ sys_exec("git commit -m \"commit for iteration #{i}\" ext/foo.c", dir: git_reader.path)
git_commit_sha = git_reader.ref_for("HEAD")
@@ -1315,7 +1321,7 @@ RSpec.describe "bundle install with git sources" do
RUBY
end
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -1441,7 +1447,7 @@ In Gemfile:
installed_time = out
- update_git("foo", :branch => "branch2")
+ update_git("foo", branch: "branch2")
expect(installed_time).to match(/\A\d+\.\d+\z/)
@@ -1520,7 +1526,7 @@ In Gemfile:
L
with_path_as("") do
- bundle "install", :raise_on_error => false
+ bundle "install", raise_on_error: false
end
expect(err).
to include("You need to install git to be able to use gems from git repositories. For help installing git, please refer to GitHub's tutorial at https://help.github.com/articles/set-up-git")
@@ -1537,7 +1543,7 @@ In Gemfile:
G
with_path_as("") do
- bundle "update", :all => true, :raise_on_error => false
+ bundle "update", all: true, raise_on_error: false
end
expect(err).
to include("You need to install git to be able to use gems from git repositories. For help installing git, please refer to GitHub's tutorial at https://help.github.com/articles/set-up-git")
@@ -1556,7 +1562,7 @@ In Gemfile:
bundle :cache
simulate_new_machine
- bundle "install", :env => { "PATH" => "" }
+ bundle "install", env: { "PATH" => "" }
expect(out).to_not include("You need to install git to be able to use gems from git repositories.")
end
end
@@ -1574,7 +1580,7 @@ In Gemfile:
end
it "installs successfully" do
- build_git "foo", "1.0", :path => lib_path("foo")
+ build_git "foo", "1.0", path: lib_path("foo")
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -1592,7 +1598,7 @@ In Gemfile:
let(:credentials) { "user1:password1" }
it "does not display the password" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
git "https://#{credentials}@github.com/company/private-repo" do
gem "foo"
@@ -1608,7 +1614,7 @@ In Gemfile:
let(:credentials) { "oauth_token" }
it "displays the oauth scheme but not the oauth token" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
git "https://#{credentials}:x-oauth-basic@github.com/company/private-repo" do
gem "foo"
diff --git a/spec/bundler/install/gemfile/groups_spec.rb b/spec/bundler/install/gemfile/groups_spec.rb
index 734e012e84..f7907a9cad 100644
--- a/spec/bundler/install/gemfile/groups_spec.rb
+++ b/spec/bundler/install/gemfile/groups_spec.rb
@@ -88,32 +88,32 @@ RSpec.describe "bundle install with groups" do
it "installs gems in the default group" do
bundle "config set --local without emo"
bundle :install
- expect(the_bundle).to include_gems "rack 1.0.0", :groups => [:default]
+ expect(the_bundle).to include_gems "rack 1.0.0", groups: [:default]
end
it "respects global `without` configuration, but does not save it locally" do
bundle "config set --global without emo"
bundle :install
- expect(the_bundle).to include_gems "rack 1.0.0", :groups => [:default]
+ expect(the_bundle).to include_gems "rack 1.0.0", groups: [:default]
bundle "config list"
expect(out).not_to include("Set for your local app (#{bundled_app(".bundle/config")}): [:emo]")
expect(out).to include("Set for the current user (#{home(".bundle/config")}): [:emo]")
end
- it "allows running application where groups where configured by a different user", :bundler => "< 3" do
+ it "allows running application where groups where configured by a different user", bundler: "< 3" do
bundle "config set without emo"
bundle :install
- bundle "exec ruby -e 'puts 42'", :env => { "BUNDLE_USER_HOME" => tmp("new_home").to_s }
+ bundle "exec ruby -e 'puts 42'", env: { "BUNDLE_USER_HOME" => tmp("new_home").to_s }
expect(out).to include("42")
end
it "does not install gems from the excluded group" do
bundle "config set --local without emo"
bundle :install
- expect(the_bundle).not_to include_gems "activesupport 2.3.5", :groups => [:default]
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5", groups: [:default]
end
- it "remembers previous exclusion with `--without`", :bundler => "< 3" do
+ it "remembers previous exclusion with `--without`", bundler: "< 3" do
bundle "install --without emo"
expect(the_bundle).not_to include_gems "activesupport 2.3.5"
bundle :install
@@ -144,7 +144,7 @@ RSpec.describe "bundle install with groups" do
bundle "config set --local without emo"
bundle :install
- expect(the_bundle).to include_gems "activesupport 2.3.2", :groups => [:default]
+ expect(the_bundle).to include_gems "activesupport 2.3.2", groups: [:default]
end
it "still works when BUNDLE_WITHOUT is set" do
@@ -153,20 +153,20 @@ RSpec.describe "bundle install with groups" do
bundle :install
expect(out).not_to include("activesupport")
- expect(the_bundle).to include_gems "rack 1.0.0", :groups => [:default]
- expect(the_bundle).not_to include_gems "activesupport 2.3.5", :groups => [:default]
+ expect(the_bundle).to include_gems "rack 1.0.0", groups: [:default]
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5", groups: [:default]
ENV["BUNDLE_WITHOUT"] = nil
end
- it "clears --without when passed an empty list", :bundler => "< 3" do
+ it "clears --without when passed an empty list", bundler: "< 3" do
bundle "install --without emo"
bundle "install --without ''"
expect(the_bundle).to include_gems "activesupport 2.3.5"
end
- it "doesn't clear without when nothing is passed", :bundler => "< 3" do
+ it "doesn't clear without when nothing is passed", bundler: "< 3" do
bundle "install --without emo"
bundle :install
@@ -184,7 +184,7 @@ RSpec.describe "bundle install with groups" do
expect(the_bundle).to include_gems "thin 1.0"
end
- it "installs gems from the previously requested group", :bundler => "< 3" do
+ it "installs gems from the previously requested group", bundler: "< 3" do
bundle "install --with debugging"
expect(the_bundle).to include_gems "thin 1.0"
bundle :install
@@ -198,26 +198,26 @@ RSpec.describe "bundle install with groups" do
ENV["BUNDLE_WITH"] = nil
end
- it "clears --with when passed an empty list", :bundler => "< 3" do
+ it "clears --with when passed an empty list", bundler: "< 3" do
bundle "install --with debugging"
bundle "install --with ''"
expect(the_bundle).not_to include_gems "thin 1.0"
end
- it "removes groups from without when passed at --with", :bundler => "< 3" do
+ it "removes groups from without when passed at --with", bundler: "< 3" do
bundle "config set --local without emo"
bundle "install --with emo"
expect(the_bundle).to include_gems "activesupport 2.3.5"
end
- it "removes groups from with when passed at --without", :bundler => "< 3" do
+ it "removes groups from with when passed at --without", bundler: "< 3" do
bundle "config set --local with debugging"
- bundle "install --without debugging", :raise_on_error => false
+ bundle "install --without debugging", raise_on_error: false
expect(the_bundle).not_to include_gem "thin 1.0"
end
- it "errors out when passing a group to with and without via CLI flags", :bundler => "< 3" do
- bundle "install --with emo debugging --without emo", :raise_on_error => false
+ it "errors out when passing a group to with and without via CLI flags", bundler: "< 3" do
+ bundle "install --with emo debugging --without emo", raise_on_error: false
expect(last_command).to be_failure
expect(err).to include("The offending groups are: emo")
end
@@ -235,7 +235,7 @@ RSpec.describe "bundle install with groups" do
expect(the_bundle).to include_gem "thin 1.0"
end
- it "can add and remove a group at the same time", :bundler => "< 3" do
+ it "can add and remove a group at the same time", bundler: "< 3" do
bundle "install --with debugging --without emo"
expect(the_bundle).to include_gems "thin 1.0"
expect(the_bundle).not_to include_gems "activesupport 2.3.5"
@@ -349,7 +349,7 @@ RSpec.describe "bundle install with groups" do
G
ruby <<-R
- require "#{entrypoint}"
+ require "bundler"
Bundler.setup :default
Bundler.require :default
puts RACK
@@ -396,7 +396,7 @@ RSpec.describe "bundle install with groups" do
it "does not hit the remote a second time" do
FileUtils.rm_rf gem_repo2
bundle "config set --local without rack"
- bundle :install, :verbose => true
+ bundle :install, verbose: true
expect(last_command.stdboth).not_to match(/fetching/i)
end
end
diff --git a/spec/bundler/install/gemfile/install_if_spec.rb b/spec/bundler/install/gemfile/install_if_spec.rb
index 3d2d15a698..c7640d07e1 100644
--- a/spec/bundler/install/gemfile/install_if_spec.rb
+++ b/spec/bundler/install/gemfile/install_if_spec.rb
@@ -18,6 +18,13 @@ RSpec.describe "bundle install with install_if conditionals" do
expect(the_bundle).not_to include_gems("thin")
expect(the_bundle).not_to include_gems("foo")
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo1, "activesupport", "2.3.5"
+ c.no_checksum "foo", "1.0"
+ c.checksum gem_repo1, "rack", "1.0.0"
+ c.no_checksum "thin", "1.0"
+ end
+
expect(lockfile).to eq <<~L
GEM
remote: #{file_uri_for(gem_repo1)}/
@@ -36,7 +43,7 @@ RSpec.describe "bundle install with install_if conditionals" do
foo
rack
thin
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
diff --git a/spec/bundler/install/gemfile/lockfile_spec.rb b/spec/bundler/install/gemfile/lockfile_spec.rb
index 313e99d0b8..4601d3e2a8 100644
--- a/spec/bundler/install/gemfile/lockfile_spec.rb
+++ b/spec/bundler/install/gemfile/lockfile_spec.rb
@@ -11,6 +11,11 @@ RSpec.describe "bundle install with a lockfile present" do
install_gemfile(gf)
end
+ it "touches the lockfile on install even when nothing has changed" do
+ subject
+ expect { bundle :install }.to change { bundled_app_lock.mtime }
+ end
+
context "gemfile evaluation" do
let(:gf) { super() + "\n\n File.open('evals', 'a') {|f| f << %(1\n) } unless ENV['BUNDLER_SPEC_NO_APPEND']" }
diff --git a/spec/bundler/install/gemfile/path_spec.rb b/spec/bundler/install/gemfile/path_spec.rb
index 9ef1c879f8..a57b7ee560 100644
--- a/spec/bundler/install/gemfile/path_spec.rb
+++ b/spec/bundler/install/gemfile/path_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
RSpec.describe "bundle install with explicit source paths" do
- it "fetches gems with a global path source", :bundler => "< 3" do
+ it "fetches gems with a global path source", bundler: "< 3" do
build_lib "foo"
install_gemfile <<-G
@@ -69,7 +69,7 @@ RSpec.describe "bundle install with explicit source paths" do
username = "some_unexisting_user"
relative_path = lib_path("foo-1.0").relative_path_from(Pathname.new("/home/#{username}").expand_path)
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
gem 'foo', :path => "~#{username}/#{relative_path}"
G
@@ -78,19 +78,19 @@ RSpec.describe "bundle install with explicit source paths" do
end
it "expands paths relative to Bundler.root" do
- build_lib "foo", :path => bundled_app("foo-1.0")
+ build_lib "foo", path: bundled_app("foo-1.0")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem 'foo', :path => "./foo-1.0"
G
- expect(the_bundle).to include_gems("foo 1.0", :dir => bundled_app("subdir").mkpath)
+ expect(the_bundle).to include_gems("foo 1.0", dir: bundled_app("subdir").mkpath)
end
it "sorts paths consistently on install and update when they start with ./" do
- build_lib "demo", :path => lib_path("demo")
- build_lib "aaa", :path => lib_path("demo/aaa")
+ build_lib "demo", path: lib_path("demo")
+ build_lib "aaa", path: lib_path("demo/aaa")
gemfile lib_path("demo/Gemfile"), <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -98,6 +98,11 @@ RSpec.describe "bundle install with explicit source paths" do
gem "aaa", :path => "./aaa"
G
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "aaa", "1.0"
+ c.no_checksum "demo", "1.0"
+ end
+
lockfile = <<~L
PATH
remote: .
@@ -119,19 +124,19 @@ RSpec.describe "bundle install with explicit source paths" do
DEPENDENCIES
aaa!
demo!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
- bundle :install, :dir => lib_path("demo")
+ bundle :install, dir: lib_path("demo")
expect(lib_path("demo/Gemfile.lock")).to read_as(lockfile)
- bundle :update, :all => true, :dir => lib_path("demo")
+ bundle :update, all: true, dir: lib_path("demo")
expect(lib_path("demo/Gemfile.lock")).to read_as(lockfile)
end
it "expands paths when comparing locked paths to Gemfile paths" do
- build_lib "foo", :path => bundled_app("foo-1.0")
+ build_lib "foo", path: bundled_app("foo-1.0")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -145,11 +150,11 @@ RSpec.describe "bundle install with explicit source paths" do
it "installs dependencies from the path even if a newer gem is available elsewhere" do
system_gems "rack-1.0.0"
- build_lib "rack", "1.0", :path => lib_path("nested/bar") do |s|
+ build_lib "rack", "1.0", path: lib_path("nested/bar") do |s|
s.write "lib/rack.rb", "puts 'WIN OVERRIDE'"
end
- build_lib "foo", :path => lib_path("nested") do |s|
+ build_lib "foo", path: lib_path("nested") do |s|
s.add_dependency "rack", "= 1.0"
end
@@ -163,15 +168,15 @@ RSpec.describe "bundle install with explicit source paths" do
end
it "works" do
- build_gem "foo", "1.0.0", :to_system => true do |s|
+ build_gem "foo", "1.0.0", to_system: true do |s|
s.write "lib/foo.rb", "puts 'FAIL'"
end
- build_lib "omg", "1.0", :path => lib_path("omg") do |s|
+ build_lib "omg", "1.0", path: lib_path("omg") do |s|
s.add_dependency "foo"
end
- build_lib "foo", "1.0.0", :path => lib_path("omg/foo")
+ build_lib "foo", "1.0.0", path: lib_path("omg/foo")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -182,7 +187,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
it "works when using prereleases of 0.0.0" do
- build_lib "foo", "0.0.0.dev", :path => lib_path("foo")
+ build_lib "foo", "0.0.0.dev", path: lib_path("foo")
gemfile <<~G
source "#{file_uri_for(gem_repo1)}"
@@ -215,7 +220,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
it "works when using uppercase prereleases of 0.0.0" do
- build_lib "foo", "0.0.0.SNAPSHOT", :path => lib_path("foo")
+ build_lib "foo", "0.0.0.SNAPSHOT", path: lib_path("foo")
gemfile <<~G
source "#{file_uri_for(gem_repo1)}"
@@ -248,14 +253,14 @@ RSpec.describe "bundle install with explicit source paths" do
end
it "handles downgrades" do
- build_lib "omg", "2.0", :path => lib_path("omg")
+ build_lib "omg", "2.0", path: lib_path("omg")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "omg", :path => "#{lib_path("omg")}"
G
- build_lib "omg", "1.0", :path => lib_path("omg")
+ build_lib "omg", "1.0", path: lib_path("omg")
bundle :install
@@ -263,7 +268,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
it "prefers gemspecs closer to the path root" do
- build_lib "premailer", "1.0.0", :path => lib_path("premailer") do |s|
+ build_lib "premailer", "1.0.0", path: lib_path("premailer") do |s|
s.write "gemfiles/ruby187.gemspec", <<-G
Gem::Specification.new do |s|
s.name = 'premailer'
@@ -296,7 +301,7 @@ RSpec.describe "bundle install with explicit source paths" do
G
end
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => "#{lib_path("foo-1.0")}"
G
@@ -308,7 +313,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
it "supports gemspec syntax" do
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
s.add_dependency "rack", "1.0"
end
@@ -317,9 +322,9 @@ RSpec.describe "bundle install with explicit source paths" do
gemspec
G
- bundle "install", :dir => lib_path("foo")
- expect(the_bundle).to include_gems "foo 1.0", :dir => lib_path("foo")
- expect(the_bundle).to include_gems "rack 1.0", :dir => lib_path("foo")
+ bundle "install", dir: lib_path("foo")
+ expect(the_bundle).to include_gems "foo 1.0", dir: lib_path("foo")
+ expect(the_bundle).to include_gems "rack 1.0", dir: lib_path("foo")
end
it "does not unlock dependencies of path sources" do
@@ -328,7 +333,7 @@ RSpec.describe "bundle install with explicit source paths" do
build_gem "graphql", "2.0.16"
end
- build_lib "foo", "0.1.0", :path => lib_path("foo") do |s|
+ build_lib "foo", "0.1.0", path: lib_path("foo") do |s|
s.add_dependency "graphql", "~> 2.0"
end
@@ -341,6 +346,11 @@ RSpec.describe "bundle install with explicit source paths" do
lockfile_path = lib_path("foo/Gemfile.lock")
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "foo", "0.1.0"
+ c.checksum gem_repo4, "graphql", "2.0.15"
+ end
+
original_lockfile = <<~L
PATH
remote: .
@@ -358,23 +368,23 @@ RSpec.describe "bundle install with explicit source paths" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
lockfile lockfile_path, original_lockfile
- build_lib "foo", "0.1.1", :path => lib_path("foo") do |s|
+ build_lib "foo", "0.1.1", path: lib_path("foo") do |s|
s.add_dependency "graphql", "~> 2.0"
end
- bundle "install", :dir => lib_path("foo")
+ bundle "install", dir: lib_path("foo")
expect(lockfile_path).to read_as(original_lockfile.gsub("foo (0.1.0)", "foo (0.1.1)"))
end
it "supports gemspec syntax with an alternative path" do
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
s.add_dependency "rack", "1.0"
end
@@ -388,48 +398,48 @@ RSpec.describe "bundle install with explicit source paths" do
end
it "doesn't automatically unlock dependencies when using the gemspec syntax" do
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
s.add_dependency "rack", ">= 1.0"
end
- install_gemfile lib_path("foo/Gemfile"), <<-G, :dir => lib_path("foo")
+ install_gemfile lib_path("foo/Gemfile"), <<-G, dir: lib_path("foo")
source "#{file_uri_for(gem_repo1)}"
gemspec
G
- build_gem "rack", "1.0.1", :to_system => true
+ build_gem "rack", "1.0.1", to_system: true
- bundle "install", :dir => lib_path("foo")
+ bundle "install", dir: lib_path("foo")
- expect(the_bundle).to include_gems "foo 1.0", :dir => lib_path("foo")
- expect(the_bundle).to include_gems "rack 1.0", :dir => lib_path("foo")
+ expect(the_bundle).to include_gems "foo 1.0", dir: lib_path("foo")
+ expect(the_bundle).to include_gems "rack 1.0", dir: lib_path("foo")
end
it "doesn't automatically unlock dependencies when using the gemspec syntax and the gem has development dependencies" do
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
s.add_dependency "rack", ">= 1.0"
s.add_development_dependency "activesupport"
end
- install_gemfile lib_path("foo/Gemfile"), <<-G, :dir => lib_path("foo")
+ install_gemfile lib_path("foo/Gemfile"), <<-G, dir: lib_path("foo")
source "#{file_uri_for(gem_repo1)}"
gemspec
G
- build_gem "rack", "1.0.1", :to_system => true
+ build_gem "rack", "1.0.1", to_system: true
- bundle "install", :dir => lib_path("foo")
+ bundle "install", dir: lib_path("foo")
- expect(the_bundle).to include_gems "foo 1.0", :dir => lib_path("foo")
- expect(the_bundle).to include_gems "rack 1.0", :dir => lib_path("foo")
+ expect(the_bundle).to include_gems "foo 1.0", dir: lib_path("foo")
+ expect(the_bundle).to include_gems "rack 1.0", dir: lib_path("foo")
end
it "raises if there are multiple gemspecs" do
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
s.write "bar.gemspec", build_spec("bar", "1.0").first.to_ruby
end
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
gemspec :path => "#{lib_path("foo")}"
G
@@ -439,7 +449,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
it "allows :name to be specified to resolve ambiguity" do
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
s.write "bar.gemspec"
end
@@ -456,7 +466,7 @@ RSpec.describe "bundle install with explicit source paths" do
s.executables = "foobar"
end
- install_gemfile <<-G, :verbose => true
+ install_gemfile <<-G, verbose: true
source "#{file_uri_for(gem_repo1)}"
path "#{lib_path("foo-1.0")}" do
gem 'foo'
@@ -510,9 +520,9 @@ RSpec.describe "bundle install with explicit source paths" do
end
it "keeps source pinning" do
- build_lib "foo", "1.0", :path => lib_path("foo")
- build_lib "omg", "1.0", :path => lib_path("omg")
- build_lib "foo", "1.0", :path => lib_path("omg/foo") do |s|
+ build_lib "foo", "1.0", path: lib_path("foo")
+ build_lib "omg", "1.0", path: lib_path("omg")
+ build_lib "foo", "1.0", path: lib_path("omg/foo") do |s|
s.write "lib/foo.rb", "puts 'FAIL'"
end
@@ -526,7 +536,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
it "works when the path does not have a gemspec" do
- build_lib "foo", :gemspec => false
+ build_lib "foo", gemspec: false
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -564,17 +574,17 @@ RSpec.describe "bundle install with explicit source paths" do
gem 'net-ssh', '1.0'
G
- bundle :check, :env => { "DEBUG" => "1" }
+ bundle :check, env: { "DEBUG" => "1" }
expect(out).to match(/using resolution from the lockfile/)
expect(the_bundle).to include_gems "rack-obama 1.0", "net-ssh 1.0"
end
it "source path gems w/deps don't re-resolve without changes" do
- build_lib "rack-obama", "1.0", :path => lib_path("omg") do |s|
+ build_lib "rack-obama", "1.0", path: lib_path("omg") do |s|
s.add_dependency "yard"
end
- build_lib "net-ssh", "1.0", :path => lib_path("omg") do |s|
+ build_lib "net-ssh", "1.0", path: lib_path("omg") do |s|
s.add_dependency "yard"
end
@@ -584,7 +594,7 @@ RSpec.describe "bundle install with explicit source paths" do
gem 'net-ssh', :path => "#{lib_path("omg")}"
G
- bundle :check, :env => { "DEBUG" => "1" }
+ bundle :check, env: { "DEBUG" => "1" }
expect(out).to match(/using resolution from the lockfile/)
expect(the_bundle).to include_gems "rack-obama 1.0", "net-ssh 1.0"
end
@@ -606,10 +616,10 @@ RSpec.describe "bundle install with explicit source paths" do
describe "when the gem version in the path is updated" do
before :each do
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
s.add_dependency "bar"
end
- build_lib "bar", "1.0", :path => lib_path("foo/bar")
+ build_lib "bar", "1.0", path: lib_path("foo/bar")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -618,7 +628,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
it "unlocks all gems when the top level gem is updated" do
- build_lib "foo", "2.0", :path => lib_path("foo") do |s|
+ build_lib "foo", "2.0", path: lib_path("foo") do |s|
s.add_dependency "bar"
end
@@ -628,7 +638,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
it "unlocks all gems when a child dependency gem is updated" do
- build_lib "bar", "2.0", :path => lib_path("foo/bar")
+ build_lib "bar", "2.0", path: lib_path("foo/bar")
bundle "install"
@@ -638,7 +648,7 @@ RSpec.describe "bundle install with explicit source paths" do
describe "when dependencies in the path are updated" do
before :each do
- build_lib "foo", "1.0", :path => lib_path("foo")
+ build_lib "foo", "1.0", path: lib_path("foo")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -647,7 +657,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
it "gets dependencies that are updated in the path" do
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
s.add_dependency "rack"
end
@@ -657,7 +667,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
it "keeps using the same version if it's compatible" do
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
s.add_dependency "rack", "0.9.1"
end
@@ -665,6 +675,11 @@ RSpec.describe "bundle install with explicit source paths" do
expect(the_bundle).to include_gems "rack 0.9.1"
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "foo", "1.0"
+ c.checksum gem_repo1, "rack", "0.9.1"
+ end
+
expect(lockfile).to eq <<~G
PATH
remote: #{lib_path("foo")}
@@ -682,12 +697,12 @@ RSpec.describe "bundle install with explicit source paths" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
s.add_dependency "rack"
end
@@ -710,7 +725,7 @@ RSpec.describe "bundle install with explicit source paths" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -719,7 +734,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
it "keeps using the same version even when another dependency is added" do
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
s.add_dependency "rack", "0.9.1"
end
@@ -727,6 +742,11 @@ RSpec.describe "bundle install with explicit source paths" do
expect(the_bundle).to include_gems "rack 0.9.1"
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "foo", "1.0"
+ c.checksum gem_repo1, "rack", "0.9.1"
+ end
+
expect(lockfile).to eq <<~G
PATH
remote: #{lib_path("foo")}
@@ -744,53 +764,107 @@ RSpec.describe "bundle install with explicit source paths" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
s.add_dependency "rack"
- s.add_dependency "rake", "13.0.1"
+ s.add_dependency "rake", rake_version
end
bundle "install"
+ checksums.checksum gem_repo1, "rake", rake_version
+
expect(lockfile).to eq <<~G
PATH
remote: #{lib_path("foo")}
specs:
foo (1.0)
rack
- rake (= 13.0.1)
+ rake (= #{rake_version})
GEM
remote: #{file_uri_for(gem_repo1)}/
specs:
rack (0.9.1)
- rake (13.0.1)
+ rake (#{rake_version})
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
expect(the_bundle).to include_gems "rack 0.9.1"
end
+
+ it "does not remove existing ruby platform" do
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
+ s.add_dependency "rack", "0.9.1"
+ end
+
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "foo", "1.0"
+ end
+
+ lockfile <<~L
+ PATH
+ remote: #{lib_path("foo")}
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms("ruby")}
+
+ DEPENDENCIES
+ foo!
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock"
+
+ checksums.no_checksum "rack", "0.9.1"
+
+ expect(lockfile).to eq <<~G
+ PATH
+ remote: #{lib_path("foo")}
+ specs:
+ foo (1.0)
+ rack (= 0.9.1)
+
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ specs:
+ rack (0.9.1)
+
+ PLATFORMS
+ #{lockfile_platforms("ruby")}
+
+ DEPENDENCIES
+ foo!
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
end
describe "switching sources" do
it "doesn't switch pinned git sources to rubygems when pinning the parent gem to a path source" do
- build_gem "foo", "1.0", :to_system => true do |s|
+ build_gem "foo", "1.0", to_system: true do |s|
s.write "lib/foo.rb", "raise 'fail'"
end
- build_lib "foo", "1.0", :path => lib_path("bar/foo")
- build_git "bar", "1.0", :path => lib_path("bar") do |s|
+ build_lib "foo", "1.0", path: lib_path("bar/foo")
+ build_git "bar", "1.0", path: lib_path("bar") do |s|
s.add_dependency "foo"
end
@@ -808,8 +882,8 @@ RSpec.describe "bundle install with explicit source paths" do
end
it "switches the source when the gem existed in rubygems and the path was already being used for another gem" do
- build_lib "foo", "1.0", :path => lib_path("foo")
- build_gem "bar", "1.0", :to_bundle => true do |s|
+ build_lib "foo", "1.0", path: lib_path("foo")
+ build_gem "bar", "1.0", to_bundle: true do |s|
s.write "lib/bar.rb", "raise 'fail'"
end
@@ -821,7 +895,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
G
- build_lib "bar", "1.0", :path => lib_path("foo/bar")
+ build_lib "bar", "1.0", path: lib_path("foo/bar")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -837,17 +911,17 @@ RSpec.describe "bundle install with explicit source paths" do
describe "when there are both a gemspec and remote gems" do
it "doesn't query rubygems for local gemspec name" do
- build_lib "private_lib", "2.2", :path => lib_path("private_lib")
+ build_lib "private_lib", "2.2", path: lib_path("private_lib")
gemfile lib_path("private_lib/Gemfile"), <<-G
source "http://localgemserver.test"
gemspec
gem 'rack'
G
- bundle :install, :env => { "DEBUG" => "1" }, :artifice => "endpoint", :dir => lib_path("private_lib")
+ bundle :install, env: { "DEBUG" => "1" }, artifice: "endpoint", dir: lib_path("private_lib")
expect(out).to match(%r{^HTTP GET http://localgemserver\.test/api/v1/dependencies\?gems=rack$})
expect(out).not_to match(/^HTTP GET.*private_lib/)
- expect(the_bundle).to include_gems "private_lib 2.2", :dir => lib_path("private_lib")
- expect(the_bundle).to include_gems "rack 1.0", :dir => lib_path("private_lib")
+ expect(the_bundle).to include_gems "private_lib 2.2", dir: lib_path("private_lib")
+ expect(the_bundle).to include_gems "rack 1.0", dir: lib_path("private_lib")
end
end
@@ -868,7 +942,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
bundle :install,
- :requires => [lib_path("install_hooks.rb")]
+ requires: [lib_path("install_hooks.rb")]
expect(err_without_deprecations).to eq("Ran pre-install hook: foo-1.0")
end
@@ -888,7 +962,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
bundle :install,
- :requires => [lib_path("install_hooks.rb")]
+ requires: [lib_path("install_hooks.rb")]
expect(err_without_deprecations).to eq("Ran post-install hook: foo-1.0")
end
@@ -907,7 +981,7 @@ RSpec.describe "bundle install with explicit source paths" do
H
end
- bundle :install, :requires => [lib_path("install_hooks.rb")], :raise_on_error => false
+ bundle :install, requires: [lib_path("install_hooks.rb")], raise_on_error: false
expect(err).to include("failed for foo-1.0")
end
diff --git a/spec/bundler/install/gemfile/platform_spec.rb b/spec/bundler/install/gemfile/platform_spec.rb
index d28c7b781f..d90dacdc02 100644
--- a/spec/bundler/install/gemfile/platform_spec.rb
+++ b/spec/bundler/install/gemfile/platform_spec.rb
@@ -203,6 +203,15 @@ RSpec.describe "bundle install across platforms" do
gem "pry"
G
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo4, "coderay", "1.1.2"
+ c.checksum gem_repo4, "empyrean", "0.1.0"
+ c.checksum gem_repo4, "ffi", "1.9.23", "java"
+ c.checksum gem_repo4, "method_source", "0.9.0"
+ c.checksum gem_repo4, "pry", "0.11.3", "java"
+ c.checksum gem_repo4, "spoon", "0.0.6"
+ end
+
expect(lockfile).to eq <<~L
GEM
remote: #{file_uri_for(gem_repo4)}/
@@ -224,7 +233,7 @@ RSpec.describe "bundle install across platforms" do
DEPENDENCIES
empyrean (= 0.1.0)
pry
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -256,7 +265,7 @@ RSpec.describe "bundle install across platforms" do
DEPENDENCIES
empyrean (= 0.1.0)
pry
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -289,30 +298,30 @@ RSpec.describe "bundle install across platforms" do
DEPENDENCIES
empyrean (= 0.1.0)
pry
-
+ #{checksums}
BUNDLED WITH
1.16.1
L
aggregate_failures do
lockfile bad_lockfile
- bundle :install, :env => { "BUNDLER_VERSION" => Bundler::VERSION }
+ bundle :install, env: { "BUNDLER_VERSION" => Bundler::VERSION }
expect(lockfile).to eq good_lockfile
lockfile bad_lockfile
- bundle :update, :all => true, :env => { "BUNDLER_VERSION" => Bundler::VERSION }
+ bundle :update, all: true, env: { "BUNDLER_VERSION" => Bundler::VERSION }
expect(lockfile).to eq good_lockfile
lockfile bad_lockfile
- bundle "update ffi", :env => { "BUNDLER_VERSION" => Bundler::VERSION }
+ bundle "update ffi", env: { "BUNDLER_VERSION" => Bundler::VERSION }
expect(lockfile).to eq good_lockfile
lockfile bad_lockfile
- bundle "update empyrean", :env => { "BUNDLER_VERSION" => Bundler::VERSION }
+ bundle "update empyrean", env: { "BUNDLER_VERSION" => Bundler::VERSION }
expect(lockfile).to eq good_lockfile
lockfile bad_lockfile
- bundle :lock, :env => { "BUNDLER_VERSION" => Bundler::VERSION }
+ bundle :lock, env: { "BUNDLER_VERSION" => Bundler::VERSION }
expect(lockfile).to eq good_lockfile
end
end
@@ -363,6 +372,11 @@ RSpec.describe "bundle install across platforms" do
end
it "keeps existing platforms when installing with force_ruby_platform" do
+ checksums = checksums_section do |c|
+ c.no_checksum "platform_specific", "1.0"
+ c.no_checksum "platform_specific", "1.0", "java"
+ end
+
lockfile <<-G
GEM
remote: #{file_uri_for(gem_repo1)}/
@@ -374,6 +388,7 @@ RSpec.describe "bundle install across platforms" do
DEPENDENCIES
platform_specific
+ #{checksums}
G
bundle "config set --local force_ruby_platform true"
@@ -383,6 +398,8 @@ RSpec.describe "bundle install across platforms" do
gem "platform_specific"
G
+ checksums.checksum gem_repo1, "platform_specific", "1.0"
+
expect(the_bundle).to include_gem "platform_specific 1.0 RUBY"
expect(lockfile).to eq <<~G
@@ -398,7 +415,7 @@ RSpec.describe "bundle install across platforms" do
DEPENDENCIES
platform_specific
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -567,7 +584,7 @@ RSpec.describe "bundle install with platform conditionals" do
DEPENDENCIES
rack
-
+ #{checksums_section_when_existing}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -612,7 +629,7 @@ RSpec.describe "when a gem has no architecture" do
gem "rcov"
G
- bundle :install, :artifice => "windows", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+ bundle :install, artifice: "windows", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
expect(the_bundle).to include_gems "rcov 1.0.0"
end
end
diff --git a/spec/bundler/install/gemfile/ruby_spec.rb b/spec/bundler/install/gemfile/ruby_spec.rb
index 39f09031b7..b64d633fd3 100644
--- a/spec/bundler/install/gemfile/ruby_spec.rb
+++ b/spec/bundler/install/gemfile/ruby_spec.rb
@@ -112,7 +112,7 @@ RSpec.describe "ruby requirement" do
end
it "fails gracefully with malformed requirements" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
ruby ">= 0", "-.\\0"
gem "rack"
@@ -120,4 +120,38 @@ RSpec.describe "ruby requirement" do
expect(err).to include("There was an error parsing") # i.e. DSL error, not error template
end
+
+ it "allows picking up ruby version from a file" do
+ create_file ".ruby-version", Gem.ruby_version.to_s
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ ruby file: ".ruby-version"
+ gem "rack"
+ G
+
+ expect(lockfile).to include("RUBY VERSION")
+ end
+
+ it "reads the ruby version file from the right folder when nested Gemfiles are involved" do
+ create_file ".ruby-version", Gem.ruby_version.to_s
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ ruby file: ".ruby-version"
+ gem "rack"
+ G
+
+ nested_dir = bundled_app(".ruby-lsp")
+
+ FileUtils.mkdir nested_dir
+
+ create_file ".ruby-lsp/Gemfile", <<-G
+ eval_gemfile(File.expand_path("../Gemfile", __dir__))
+ G
+
+ bundle "install", dir: nested_dir
+
+ expect(bundled_app(".ruby-lsp/Gemfile.lock").read).to include("RUBY VERSION")
+ end
end
diff --git a/spec/bundler/install/gemfile/sources_spec.rb b/spec/bundler/install/gemfile/sources_spec.rb
index 562fa6c5b4..a5ba76f4d9 100644
--- a/spec/bundler/install/gemfile/sources_spec.rb
+++ b/spec/bundler/install/gemfile/sources_spec.rb
@@ -27,18 +27,72 @@ RSpec.describe "bundle install with gems on multiple sources" do
G
end
- it "warns about ambiguous gems, but installs anyway, prioritizing sources last to first", :bundler => "< 3" do
- bundle :install, :artifice => "compact_index"
+ it "refuses to install mismatched checksum because one gem has been tampered with", bundler: "< 3" do
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo3/
+ remote: https://gem.repo1/
+ specs:
+ rack (1.0.0)
- expect(err).to include("Warning: the gem 'rack' was found in multiple sources.")
- expect(err).to include("Installed from: https://gem.repo1")
- expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0", :source => "remote1")
+ PLATFORMS
+ #{local_platform}
+
+ DEPENDENCIES
+ depends_on_rack!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle :install, artifice: "compact_index", raise_on_error: false
+
+ expect(exitstatus).to eq(37)
+ expect(err).to eq <<~E.strip
+ [DEPRECATED] Your Gemfile contains multiple global sources. Using `source` more than once without a block is a security risk, and may result in installing unexpected gems. To resolve this warning, use a block to indicate which gems should come from the secondary source.
+ Bundler found mismatched checksums. This is a potential security risk.
+ #{checksum_to_lock(gem_repo1, "rack", "1.0.0")}
+ from the API at https://gem.repo1/
+ #{checksum_to_lock(gem_repo3, "rack", "1.0.0")}
+ from the API at https://gem.repo3/
+
+ Mismatched checksums each have an authoritative source:
+ 1. the API at https://gem.repo1/
+ 2. the API at https://gem.repo3/
+ You may need to alter your Gemfile sources to resolve this issue.
+
+ To ignore checksum security warnings, disable checksum validation with
+ `bundle config set --local disable_checksum_validation true`
+ E
end
- it "fails", :bundler => "3" do
- bundle :install, :artifice => "compact_index", :raise_on_error => false
- expect(err).to include("Each source after the first must include a block")
- expect(exitstatus).to eq(4)
+ context "when checksum validation is disabled" do
+ before do
+ bundle "config set --local disable_checksum_validation true"
+ end
+
+ it "warns about ambiguous gems, but installs anyway, prioritizing sources last to first", bundler: "< 3" do
+ bundle :install, artifice: "compact_index"
+
+ expect(err).to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(err).to include("Installed from: https://gem.repo1")
+ expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0", source: "remote1")
+ end
+
+ it "does not use the full index unnecessarily", bundler: "< 3" do
+ bundle :install, artifice: "compact_index", verbose: true
+
+ expect(out).to include("https://gem.repo1/versions")
+ expect(out).to include("https://gem.repo3/versions")
+ expect(out).not_to include("https://gem.repo1/quick/Marshal.4.8/")
+ expect(out).not_to include("https://gem.repo3/quick/Marshal.4.8/")
+ end
+
+ it "fails", bundler: "3" do
+ bundle :install, artifice: "compact_index", raise_on_error: false
+ expect(err).to include("Each source after the first must include a block")
+ expect(exitstatus).to eq(4)
+ end
end
end
@@ -54,21 +108,49 @@ RSpec.describe "bundle install with gems on multiple sources" do
G
end
- it "warns about ambiguous gems, but installs anyway", :bundler => "< 3" do
- bundle :install, :artifice => "compact_index"
+ it "warns about ambiguous gems, but installs anyway", bundler: "< 3" do
+ bundle :install, artifice: "compact_index"
expect(err).to include("Warning: the gem 'rack' was found in multiple sources.")
expect(err).to include("Installed from: https://gem.repo1")
- expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0", :source => "remote1")
+ expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0", source: "remote1")
end
- it "fails", :bundler => "3" do
- bundle :install, :artifice => "compact_index", :raise_on_error => false
+ it "fails", bundler: "3" do
+ bundle :install, artifice: "compact_index", raise_on_error: false
expect(err).to include("Each source after the first must include a block")
expect(exitstatus).to eq(4)
end
end
end
+ context "without source affinity, and a stdlib gem present in one of the sources", :ruby_repo do
+ let(:default_json_version) { ruby "gem 'json'; require 'json'; puts JSON::VERSION" }
+
+ before do
+ build_repo2 do
+ build_gem "json", default_json_version
+ end
+
+ build_repo4 do
+ build_gem "foo" do |s|
+ s.add_dependency "json", default_json_version
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ source "https://gem.repo4"
+
+ gem "foo"
+ G
+ end
+
+ it "works in standalone mode", bundler: "< 3" do
+ gem_checksum = checksum_digest(gem_repo4, "foo", "1.0")
+ bundle "install --standalone", artifice: "compact_index", env: { "BUNDLER_SPEC_FOO_CHECKSUM" => gem_checksum }
+ end
+ end
+
context "with source affinity" do
context "with sources given by a block" do
before do
@@ -95,20 +177,20 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
it "installs the gems without any warning" do
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(err).not_to include("Warning")
expect(the_bundle).to include_gems("rack-obama 1.0.0")
- expect(the_bundle).to include_gems("rack 1.0.0", :source => "remote1")
+ expect(the_bundle).to include_gems("rack 1.0.0", source: "remote1")
end
it "can cache and deploy" do
- bundle :cache, :artifice => "compact_index"
+ bundle :cache, artifice: "compact_index"
expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
expect(bundled_app("vendor/cache/rack-obama-1.0.gem")).to exist
bundle "config set --local deployment true"
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0")
end
@@ -128,7 +210,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
end
- install_gemfile <<-G, :artifice => "compact_index"
+ install_gemfile <<-G, artifice: "compact_index"
source "https://gem.repo3"
gem "rack-obama" # should come from repo3!
gem "rack", :source => "https://gem.repo1"
@@ -168,9 +250,9 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
it "installs from the same source without any warning" do
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(err).not_to include("Warning")
- expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote3")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", source: "remote3")
end
end
@@ -185,18 +267,18 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
it "installs from the same source without any warning" do
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.")
- expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote3")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", source: "remote3")
# In https://github.com/bundler/bundler/issues/3585 this failed
# when there is already a lock file, and the gems are missing, so try again
system_gems []
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.")
- expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote3")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", source: "remote3")
end
end
end
@@ -218,7 +300,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
context "and not in any other sources" do
before do
- install_gemfile <<-G, :artifice => "compact_index"
+ install_gemfile <<-G, artifice: "compact_index"
source "https://gem.repo2"
source "https://gem.repo3" do
gem "depends_on_rack"
@@ -243,11 +325,63 @@ RSpec.describe "bundle install with gems on multiple sources" do
G
end
- it "installs from the other source and warns about ambiguous gems", :bundler => "< 3" do
- bundle :install, :artifice => "compact_index"
+ it "fails when the two sources don't have the same checksum", bundler: "< 3" do
+ bundle :install, artifice: "compact_index", raise_on_error: false
+
+ expect(err).to eq(<<~E.strip)
+ [DEPRECATED] Your Gemfile contains multiple global sources. Using `source` more than once without a block is a security risk, and may result in installing unexpected gems. To resolve this warning, use a block to indicate which gems should come from the secondary source.
+ Bundler found mismatched checksums. This is a potential security risk.
+ #{checksum_to_lock(gem_repo2, "rack", "1.0.0")}
+ from the API at https://gem.repo2/
+ #{checksum_to_lock(gem_repo1, "rack", "1.0.0")}
+ from the API at https://gem.repo1/
+
+ Mismatched checksums each have an authoritative source:
+ 1. the API at https://gem.repo2/
+ 2. the API at https://gem.repo1/
+ You may need to alter your Gemfile sources to resolve this issue.
+
+ To ignore checksum security warnings, disable checksum validation with
+ `bundle config set --local disable_checksum_validation true`
+ E
+ expect(exitstatus).to eq(37)
+ end
+
+ it "fails when the two sources agree, but the local gem calculates a different checksum", bundler: "< 3" do
+ rack_checksum = "c0ffee11" * 8
+ bundle :install, artifice: "compact_index", env: { "BUNDLER_SPEC_RACK_CHECKSUM" => rack_checksum }, raise_on_error: false
+
+ expect(err).to eq(<<~E.strip)
+ [DEPRECATED] Your Gemfile contains multiple global sources. Using `source` more than once without a block is a security risk, and may result in installing unexpected gems. To resolve this warning, use a block to indicate which gems should come from the secondary source.
+ Bundler found mismatched checksums. This is a potential security risk.
+ rack (1.0.0) sha256=#{rack_checksum}
+ from the API at https://gem.repo2/
+ and the API at https://gem.repo1/
+ #{checksum_to_lock(gem_repo2, "rack", "1.0.0")}
+ from the gem at #{default_bundle_path("cache", "rack-1.0.0.gem")}
+
+ If you trust the API at https://gem.repo2/, to resolve this issue you can:
+ 1. remove the gem at #{default_bundle_path("cache", "rack-1.0.0.gem")}
+ 2. run `bundle install`
+
+ To ignore checksum security warnings, disable checksum validation with
+ `bundle config set --local disable_checksum_validation true`
+ E
+ expect(exitstatus).to eq(37)
+ end
+
+ it "installs from the other source and warns about ambiguous gems when the sources have the same checksum", bundler: "< 3" do
+ gem_checksum = checksum_digest(gem_repo2, "rack", "1.0.0")
+ bundle :install, artifice: "compact_index", env: { "BUNDLER_SPEC_RACK_CHECKSUM" => gem_checksum, "DEBUG" => "1" }
+
expect(err).to include("Warning: the gem 'rack' was found in multiple sources.")
expect(err).to include("Installed from: https://gem.repo2")
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo3, "depends_on_rack", "1.0.1"
+ c.checksum gem_repo2, "rack", "1.0.0"
+ end
+
expect(lockfile).to eq <<~L
GEM
remote: https://gem.repo1/
@@ -262,11 +396,51 @@ RSpec.describe "bundle install with gems on multiple sources" do
rack
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
depends_on_rack!
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ previous_lockfile = lockfile
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ expect(lockfile).to eq(previous_lockfile)
+ end
+
+ it "installs from the other source and warns about ambiguous gems when checksum validation is disabled", bundler: "< 3" do
+ bundle "config set --local disable_checksum_validation true"
+ bundle :install, artifice: "compact_index"
+
+ expect(err).to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(err).to include("Installed from: https://gem.repo2")
+
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "depends_on_rack", "1.0.1"
+ c.no_checksum "rack", "1.0.0"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo1/
+ remote: https://gem.repo2/
+ specs:
+ rack (1.0.0)
+
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ depends_on_rack (1.0.1)
+ rack
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ depends_on_rack!
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -276,8 +450,8 @@ RSpec.describe "bundle install with gems on multiple sources" do
expect(lockfile).to eq(previous_lockfile)
end
- it "fails", :bundler => "3" do
- bundle :install, :artifice => "compact_index", :raise_on_error => false
+ it "fails", bundler: "3" do
+ bundle :install, artifice: "compact_index", raise_on_error: false
expect(err).to include("Each source after the first must include a block")
expect(exitstatus).to eq(4)
end
@@ -301,8 +475,8 @@ RSpec.describe "bundle install with gems on multiple sources" do
G
end
- it "installs the dependency from the pinned source without warning", :bundler => "< 3" do
- bundle :install, :artifice => "compact_index"
+ it "installs the dependency from the pinned source without warning", bundler: "< 3" do
+ bundle :install, artifice: "compact_index"
expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.")
expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
@@ -310,14 +484,14 @@ RSpec.describe "bundle install with gems on multiple sources" do
# In https://github.com/rubygems/bundler/issues/3585 this failed
# when there is already a lock file, and the gems are missing, so try again
system_gems []
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.")
expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
end
- it "fails", :bundler => "3" do
- bundle :install, :artifice => "compact_index", :raise_on_error => false
+ it "fails", bundler: "3" do
+ bundle :install, artifice: "compact_index", raise_on_error: false
expect(err).to include("Each source after the first must include a block")
expect(exitstatus).to eq(4)
end
@@ -345,12 +519,12 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
it "fails" do
- bundle :install, :artifice => "compact_index", :raise_on_error => false
- expect(err).to include("Could not find gem 'private_gem_1' in rubygems repository https://gem.repo2/ or installed locally.")
+ bundle :install, artifice: "compact_index", raise_on_error: false
+ expect(err).to include("Could not find gem 'private_gem_1' in rubygems repository https://gem.repo2/, cached gems or installed locally.")
end
end
- context "when an indirect dependency can't be found in the aggregate rubygems source", :bundler => "< 3" do
+ context "when an indirect dependency can't be found in the aggregate rubygems source", bundler: "< 3" do
before do
build_repo2
@@ -370,7 +544,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
it "fails" do
- bundle :install, :artifice => "compact_index", :raise_on_error => false
+ bundle :install, artifice: "compact_index", raise_on_error: false
expect(err).to end_with <<~E.strip
Could not find compatible versions
@@ -414,11 +588,11 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
it "installs the dependency from the top-level source without warning" do
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(err).not_to include("Warning")
expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", "unrelated_gem 1.0.0")
- expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote2")
- expect(the_bundle).to include_gems("unrelated_gem 1.0.0", :source => "remote3")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", source: "remote2")
+ expect(the_bundle).to include_gems("unrelated_gem 1.0.0", source: "remote3")
end
end
@@ -432,12 +606,12 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
it "does not find the dependency" do
- bundle :install, :artifice => "compact_index", :raise_on_error => false
+ bundle :install, artifice: "compact_index", raise_on_error: false
expect(err).to end_with <<~E.strip
Could not find compatible versions
Because every version of depends_on_rack depends on rack >= 0
- and rack >= 0 could not be found in rubygems repository https://gem.repo2/ or installed locally,
+ and rack >= 0 could not be found in rubygems repository https://gem.repo2/, cached gems or installed locally,
depends_on_rack cannot be used.
So, because Gemfile depends on depends_on_rack >= 0,
version solving has failed.
@@ -459,12 +633,12 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
it "installs the dependency from the top-level source without warning" do
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(err).not_to include("Warning")
expect(run("require 'rack'; puts RACK")).to eq("1.0.0")
expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", "unrelated_gem 1.0.0")
- expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote2")
- expect(the_bundle).to include_gems("unrelated_gem 1.0.0", :source => "remote3")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", source: "remote2")
+ expect(the_bundle).to include_gems("unrelated_gem 1.0.0", source: "remote3")
end
end
end
@@ -498,10 +672,10 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
it "installs the dependency from the top-level source" do
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(the_bundle).to include_gems("depends_on_depends_on_rack 1.0.1", "depends_on_rack 1.0.1", "rack 1.0.0")
- expect(the_bundle).to include_gems("rack 1.0.0", :source => "remote2")
- expect(the_bundle).to include_gems("depends_on_depends_on_rack 1.0.1", "depends_on_rack 1.0.1", :source => "remote3")
+ expect(the_bundle).to include_gems("rack 1.0.0", source: "remote2")
+ expect(the_bundle).to include_gems("depends_on_depends_on_rack 1.0.1", "depends_on_rack 1.0.1", source: "remote3")
end
end
@@ -515,8 +689,8 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
it "installs the dependency from the pinned source" do
- bundle :install, :artifice => "compact_index"
- expect(the_bundle).to include_gems("depends_on_depends_on_rack 1.0.1", "depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote3")
+ bundle :install, artifice: "compact_index"
+ expect(the_bundle).to include_gems("depends_on_depends_on_rack 1.0.1", "depends_on_rack 1.0.1", "rack 1.0.0", source: "remote3")
end
end
@@ -534,8 +708,8 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
it "installs the dependency from the pinned source without warning" do
- bundle :install, :artifice => "compact_index"
- expect(the_bundle).to include_gems("depends_on_depends_on_rack 1.0.1", "depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote3")
+ bundle :install, artifice: "compact_index"
+ expect(the_bundle).to include_gems("depends_on_depends_on_rack 1.0.1", "depends_on_rack 1.0.1", "rack 1.0.0", source: "remote3")
end
end
end
@@ -609,6 +783,21 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
G
+ @locked_checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo2, "activesupport", "6.0.3.4"
+ c.checksum gem_repo2, "concurrent-ruby", "1.1.8"
+ c.checksum gem_repo2, "connection_pool", "2.2.3"
+ c.checksum gem_repo2, "i18n", "1.8.9"
+ c.checksum gem_repo2, "minitest", "5.14.3"
+ c.checksum gem_repo2, "rack", "2.2.3"
+ c.checksum gem_repo2, "redis", "4.2.5"
+ c.checksum gem_repo2, "sidekiq", "6.1.3"
+ c.checksum gem_repo3, "sidekiq-pro", "5.2.1"
+ c.checksum gem_repo2, "thread_safe", "0.3.6"
+ c.checksum gem_repo2, "tzinfo", "1.2.9"
+ c.checksum gem_repo2, "zeitwerk", "2.4.2"
+ end
+
lockfile <<~L
GEM
remote: https://gem.repo2/
@@ -640,19 +829,19 @@ RSpec.describe "bundle install with gems on multiple sources" do
zeitwerk (2.4.2)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
activesupport
sidekiq-pro!
-
+ #{@locked_checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
end
it "does not install newer versions but updates the lockfile format when running bundle install in non frozen mode, and doesn't warn" do
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(err).to be_empty
expect(the_bundle).to include_gems("activesupport 6.0.3.4")
@@ -696,22 +885,22 @@ RSpec.describe "bundle install with gems on multiple sources" do
sidekiq (>= 6.1.0)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
activesupport
sidekiq-pro!
-
+ #{@locked_checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
end
- it "does not install newer versions or generate lockfile changes when running bundle install in frozen mode, and warns", :bundler => "< 3" do
+ it "does not install newer versions or generate lockfile changes when running bundle install in frozen mode, and warns", bundler: "< 3" do
initial_lockfile = lockfile
bundle "config set --local frozen true"
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(err).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure.")
@@ -725,11 +914,11 @@ RSpec.describe "bundle install with gems on multiple sources" do
expect(lockfile).to eq(initial_lockfile)
end
- it "fails when running bundle install in frozen mode", :bundler => "3" do
+ it "fails when running bundle install in frozen mode", bundler: "3" do
initial_lockfile = lockfile
bundle "config set --local frozen true"
- bundle :install, :artifice => "compact_index", :raise_on_error => false
+ bundle :install, artifice: "compact_index", raise_on_error: false
expect(err).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure.")
@@ -737,15 +926,21 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
it "splits sections and upgrades gems when running bundle update, and doesn't warn" do
- bundle "update --all", :artifice => "compact_index"
+ bundle "update --all", artifice: "compact_index"
expect(err).to be_empty
expect(the_bundle).not_to include_gems("activesupport 6.0.3.4")
expect(the_bundle).to include_gems("activesupport 6.1.2.1")
+ @locked_checksums.checksum gem_repo2, "activesupport", "6.1.2.1"
+
expect(the_bundle).not_to include_gems("tzinfo 1.2.9")
expect(the_bundle).to include_gems("tzinfo 2.0.4")
+ @locked_checksums.checksum gem_repo2, "tzinfo", "2.0.4"
+ @locked_checksums.delete "thread_safe"
+
expect(the_bundle).not_to include_gems("concurrent-ruby 1.1.8")
expect(the_bundle).to include_gems("concurrent-ruby 1.1.9")
+ @locked_checksums.checksum gem_repo2, "concurrent-ruby", "1.1.9"
expect(lockfile).to eq <<~L
GEM
@@ -780,19 +975,19 @@ RSpec.describe "bundle install with gems on multiple sources" do
sidekiq (>= 6.1.0)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
activesupport
sidekiq-pro!
-
+ #{@locked_checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
end
it "upgrades the lockfile format and upgrades the requested gem when running bundle update with an argument" do
- bundle "update concurrent-ruby", :artifice => "compact_index"
+ bundle "update concurrent-ruby", artifice: "compact_index"
expect(err).to be_empty
expect(the_bundle).to include_gems("activesupport 6.0.3.4")
@@ -802,6 +997,8 @@ RSpec.describe "bundle install with gems on multiple sources" do
expect(the_bundle).to include_gems("concurrent-ruby 1.1.9")
expect(the_bundle).not_to include_gems("concurrent-ruby 1.1.8")
+ @locked_checksums.checksum gem_repo2, "concurrent-ruby", "1.1.9"
+
expect(lockfile).to eq <<~L
GEM
remote: https://gem.repo2/
@@ -836,12 +1033,12 @@ RSpec.describe "bundle install with gems on multiple sources" do
sidekiq (>= 6.1.0)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
activesupport
sidekiq-pro!
-
+ #{@locked_checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -850,8 +1047,8 @@ RSpec.describe "bundle install with gems on multiple sources" do
context "when a top-level gem has an indirect dependency present in the default source, but with a different version from the one resolved" do
before do
- build_lib "activesupport", "7.0.0.alpha", :path => lib_path("rails/activesupport")
- build_lib "rails", "7.0.0.alpha", :path => lib_path("rails") do |s|
+ build_lib "activesupport", "7.0.0.alpha", path: lib_path("rails/activesupport")
+ build_lib "rails", "7.0.0.alpha", path: lib_path("rails") do |s|
s.add_dependency "activesupport", "= 7.0.0.alpha"
end
@@ -873,11 +1070,11 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
it "installs all gems without warning" do
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(err).not_to include("Warning")
expect(the_bundle).to include_gems("activesupport 7.0.0.alpha", "rails 7.0.0.alpha")
- expect(the_bundle).to include_gems("activesupport 7.0.0.alpha", :source => "path@#{lib_path("rails/activesupport")}")
- expect(the_bundle).to include_gems("rails 7.0.0.alpha", :source => "path@#{lib_path("rails")}")
+ expect(the_bundle).to include_gems("activesupport 7.0.0.alpha", source: "path@#{lib_path("rails/activesupport")}")
+ expect(the_bundle).to include_gems("rails 7.0.0.alpha", source: "path@#{lib_path("rails")}")
end
end
@@ -909,6 +1106,12 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
it "installs from the default source without any warnings or errors and generates a proper lockfile" do
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo3, "handsoap", "0.2.5.5"
+ c.checksum gem_repo2, "nokogiri", "1.11.1"
+ c.checksum gem_repo2, "racca", "1.5.2"
+ end
+
expected_lockfile = <<~L
GEM
remote: https://gem.repo2/
@@ -924,30 +1127,30 @@ RSpec.describe "bundle install with gems on multiple sources" do
nokogiri (>= 1.2.3)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
handsoap!
nokogiri
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
- bundle "install --verbose", :artifice => "compact_index"
+ bundle "install --verbose", artifice: "compact_index"
expect(err).not_to include("Warning")
expect(the_bundle).to include_gems("handsoap 0.2.5.5", "nokogiri 1.11.1", "racca 1.5.2")
- expect(the_bundle).to include_gems("handsoap 0.2.5.5", :source => "remote3")
- expect(the_bundle).to include_gems("nokogiri 1.11.1", "racca 1.5.2", :source => "remote2")
+ expect(the_bundle).to include_gems("handsoap 0.2.5.5", source: "remote3")
+ expect(the_bundle).to include_gems("nokogiri 1.11.1", "racca 1.5.2", source: "remote2")
expect(lockfile).to eq(expected_lockfile)
# Even if the gems are already installed
FileUtils.rm bundled_app_lock
- bundle "install --verbose", :artifice => "compact_index"
+ bundle "install --verbose", artifice: "compact_index"
expect(err).not_to include("Warning")
expect(the_bundle).to include_gems("handsoap 0.2.5.5", "nokogiri 1.11.1", "racca 1.5.2")
- expect(the_bundle).to include_gems("handsoap 0.2.5.5", :source => "remote3")
- expect(the_bundle).to include_gems("nokogiri 1.11.1", "racca 1.5.2", :source => "remote2")
+ expect(the_bundle).to include_gems("handsoap 0.2.5.5", source: "remote3")
+ expect(the_bundle).to include_gems("nokogiri 1.11.1", "racca 1.5.2", source: "remote2")
expect(lockfile).to eq(expected_lockfile)
end
end
@@ -958,7 +1161,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
build_gem "not_in_repo1", "1.0.0"
end
- install_gemfile <<-G, :artifice => "compact_index", :raise_on_error => false
+ install_gemfile <<-G, artifice: "compact_index", raise_on_error: false
source "https://gem.repo3"
gem "not_in_repo1", :source => "https://gem.repo1"
G
@@ -971,7 +1174,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
context "with an existing lockfile" do
before do
- system_gems "rack-0.9.1", "rack-1.0.0", :path => default_bundle_path
+ system_gems "rack-0.9.1", "rack-1.0.0", path: default_bundle_path
lockfile <<-L
GEM
@@ -984,7 +1187,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
rack (0.9.1)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
rack!
@@ -1014,11 +1217,11 @@ RSpec.describe "bundle install with gems on multiple sources" do
rack (0.9.1)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
rack!
-
+ #{checksums_section}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -1036,7 +1239,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
rack (0.9.1)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
rack!
@@ -1061,20 +1264,48 @@ RSpec.describe "bundle install with gems on multiple sources" do
lockfile aggregate_gem_section_lockfile
end
- it "installs the existing lockfile but prints a warning", :bundler => "< 3" do
+ it "installs the existing lockfile but prints a warning when checksum validation is disabled", bundler: "< 3" do
bundle "config set --local deployment true"
+ bundle "config set --local disable_checksum_validation true"
- bundle "install", :artifice => "compact_index"
+ bundle "install", artifice: "compact_index"
expect(lockfile).to eq(aggregate_gem_section_lockfile)
expect(err).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure.")
- expect(the_bundle).to include_gems("rack 0.9.1", :source => "remote3")
+ expect(the_bundle).to include_gems("rack 0.9.1", source: "remote3")
+ end
+
+ it "prints a checksum warning when the checksums from both sources do not match", bundler: "< 3" do
+ bundle "config set --local deployment true"
+
+ bundle "install", artifice: "compact_index", raise_on_error: false
+
+ api_checksum1 = checksum_digest(gem_repo1, "rack", "0.9.1")
+ api_checksum3 = checksum_digest(gem_repo3, "rack", "0.9.1")
+
+ expect(exitstatus).to eq(37)
+ expect(err).to eq(<<~E.strip)
+ [DEPRECATED] Your lockfile contains a single rubygems source section with multiple remotes, which is insecure. Make sure you run `bundle install` in non frozen mode and commit the result to make your lockfile secure.
+ Bundler found mismatched checksums. This is a potential security risk.
+ rack (0.9.1) sha256=#{api_checksum3}
+ from the API at https://gem.repo3/
+ rack (0.9.1) sha256=#{api_checksum1}
+ from the API at https://gem.repo1/
+
+ Mismatched checksums each have an authoritative source:
+ 1. the API at https://gem.repo3/
+ 2. the API at https://gem.repo1/
+ You may need to alter your Gemfile sources to resolve this issue.
+
+ To ignore checksum security warnings, disable checksum validation with
+ `bundle config set --local disable_checksum_validation true`
+ E
end
- it "refuses to install the existing lockfile and prints an error", :bundler => "3" do
+ it "refuses to install the existing lockfile and prints an error", bundler: "3" do
bundle "config set --local deployment true"
- bundle "install", :artifice => "compact_index", :raise_on_error => false
+ bundle "install", artifice: "compact_index", raise_on_error: false
expect(lockfile).to eq(aggregate_gem_section_lockfile)
expect(err).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure.")
@@ -1094,7 +1325,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
it "does not unlock the non-path gem after install" do
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
bundle %(exec ruby -e 'puts "OK"')
@@ -1107,7 +1338,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
before do
system_gems "rack-0.9.1"
- install_gemfile <<-G, :artifice => "compact_index"
+ install_gemfile <<-G, artifice: "compact_index"
source "https://gem.repo1"
gem "rack" # should come from repo1!
G
@@ -1136,7 +1367,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
G
bundle "config set --local path ../gems/system"
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
# And then we add some new versions...
update_repo4 do
@@ -1147,7 +1378,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
it "allows them to be unlocked separately" do
# And install this gemfile, updating only foo.
- install_gemfile <<-G, :artifice => "compact_index"
+ install_gemfile <<-G, artifice: "compact_index"
source 'https://gem.repo1'
gem 'rack'
gem 'foo', '~> 0.2', :source => 'https://gem.repo4'
@@ -1171,7 +1402,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
build_git "git1"
build_git "git2"
- install_gemfile <<-G, :artifice => "compact_index"
+ install_gemfile <<-G, artifice: "compact_index"
source "https://gem.repo1"
gem "rails"
@@ -1187,7 +1418,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
it "does not re-resolve" do
- bundle :install, :artifice => "compact_index", :verbose => true
+ bundle :install, artifice: "compact_index", verbose: true
expect(out).to include("using resolution from the lockfile")
expect(out).not_to include("re-resolving dependencies")
end
@@ -1196,7 +1427,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
context "when a gem is installed to system gems" do
before do
- install_gemfile <<-G, :artifice => "compact_index"
+ install_gemfile <<-G, artifice: "compact_index"
source "https://gem.repo1"
gem "rack"
G
@@ -1210,7 +1441,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
# When this gemfile is installed...
- install_gemfile <<-G, :artifice => "compact_index"
+ install_gemfile <<-G, artifice: "compact_index"
source "https://gem.repo1"
source "https://gem.repo4" do
@@ -1232,14 +1463,14 @@ RSpec.describe "bundle install with gems on multiple sources" do
G
# But we should still be able to find rack 2.0.1.1.forked and install it
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
end
end
end
describe "source changed to one containing a higher version of a dependency" do
before do
- install_gemfile <<-G, :artifice => "compact_index"
+ install_gemfile <<-G, artifice: "compact_index"
source "https://gem.repo1"
gem "rack"
@@ -1253,11 +1484,11 @@ RSpec.describe "bundle install with gems on multiple sources" do
build_gem "bar"
end
- build_lib("gemspec_test", :path => tmp.join("gemspec_test")) do |s|
+ build_lib("gemspec_test", path: tmp.join("gemspec_test")) do |s|
s.add_dependency "bar", "=1.0.0"
end
- install_gemfile <<-G, :artifice => "compact_index"
+ install_gemfile <<-G, artifice: "compact_index"
source "https://gem.repo2"
gem "rack"
gemspec :path => "#{tmp.join("gemspec_test")}"
@@ -1275,11 +1506,11 @@ RSpec.describe "bundle install with gems on multiple sources" do
build_gem "bar"
end
- build_lib("gemspec_test", :path => tmp.join("gemspec_test")) do |s|
+ build_lib("gemspec_test", path: tmp.join("gemspec_test")) do |s|
s.add_development_dependency "bar"
end
- install_gemfile <<-G, :artifice => "compact_index"
+ install_gemfile <<-G, artifice: "compact_index"
source "https://gem.repo1"
source "https://gem.repo4" do
@@ -1304,7 +1535,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
build_gem "example", "1.0.2"
end
- install_gemfile <<-G, :artifice => "compact_index"
+ install_gemfile <<-G, artifice: "compact_index"
source "https://gem.repo4"
gem "example", :source => "https://gem.repo2"
@@ -1313,9 +1544,9 @@ RSpec.describe "bundle install with gems on multiple sources" do
bundle "info example"
expect(out).to include("example (0.1.0)")
- system_gems "example-1.0.2", :path => default_bundle_path, :gem_repo => gem_repo4
+ system_gems "example-1.0.2", path: default_bundle_path, gem_repo: gem_repo4
- bundle "update example --verbose", :artifice => "compact_index"
+ bundle "update example --verbose", artifice: "compact_index"
expect(out).not_to include("Using example 1.0.2")
expect(out).to include("Using example 0.1.0")
end
@@ -1329,11 +1560,9 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
G
- simulate_bundler_version_when_missing_prerelease_default_gem_activation do
- ruby <<~R, :raise_on_error => false
- require 'bundler/setup'
- R
- end
+ ruby <<~R, raise_on_error: false
+ require 'bundler/setup'
+ R
expect(last_command).to be_failure
expect(err).to include("Could not find gem 'example' in locally installed gems.")
@@ -1348,13 +1577,13 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
G
- bundle "install", :artifice => nil, :raise_on_error => false
+ bundle "install", artifice: nil, raise_on_error: false
expect(last_command).to be_failure
expect(err).to include("Could not reach host gem.repo4. Check your network connection and try again.")
end
- context "when an indirect dependency is available from multiple ambiguous sources", :bundler => "< 3" do
+ context "when an indirect dependency is available from multiple ambiguous sources", bundler: "< 3" do
it "succeeds but warns, suggesting a source block" do
build_repo4 do
build_gem "depends_on_rack" do |s|
@@ -1363,7 +1592,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
build_gem "rack"
end
- install_gemfile <<-G, :artifice => "compact_index", :raise_on_error => false
+ install_gemfile <<-G, artifice: "compact_index", raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
source "https://gem.repo4" do
@@ -1374,7 +1603,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
gem "thin"
end
G
- expect(err).to eq strip_whitespace(<<-EOS).strip
+ expect(err).to eq <<~EOS.strip
Warning: The gem 'rack' was found in multiple relevant sources.
* rubygems repository https://gem.repo1/
* rubygems repository https://gem.repo4/
@@ -1385,7 +1614,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
end
- context "when an indirect dependency is available from multiple ambiguous sources", :bundler => "3" do
+ context "when an indirect dependency is available from multiple ambiguous sources", bundler: "3" do
it "raises, suggesting a source block" do
build_repo4 do
build_gem "depends_on_rack" do |s|
@@ -1394,7 +1623,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
build_gem "rack"
end
- install_gemfile <<-G, :artifice => "compact_index", :raise_on_error => false
+ install_gemfile <<-G, artifice: "compact_index", raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
source "https://gem.repo4" do
gem "depends_on_rack"
@@ -1404,7 +1633,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
G
expect(last_command).to be_failure
- expect(err).to eq strip_whitespace(<<-EOS).strip
+ expect(err).to eq <<~EOS.strip
The gem 'rack' was found in multiple relevant sources.
* rubygems repository https://gem.repo1/
* rubygems repository https://gem.repo4/
@@ -1448,16 +1677,23 @@ RSpec.describe "bundle install with gems on multiple sources" do
mime-types (3.3.1)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
capybara (~> 2.5.0)
mime-types (~> 3.0)!
+
+ CHECKSUMS
L
end
it "upgrades the lockfile correctly" do
- bundle "lock --update", :artifice => "compact_index"
+ bundle "lock --update", artifice: "compact_index"
+
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo2, "capybara", "2.5.0"
+ c.checksum gem_repo4, "mime-types", "3.0.0"
+ end
expect(lockfile).to eq <<~L
GEM
@@ -1472,12 +1708,12 @@ RSpec.describe "bundle install with gems on multiple sources" do
mime-types (3.0.0)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
capybara (~> 2.5.0)
mime-types (~> 3.0)!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -1511,7 +1747,12 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
it "handles that fine" do
- bundle "install", :artifice => "compact_index_extra", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ bundle "install", artifice: "compact_index_extra", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo4, "pdf-writer", "1.1.8"
+ c.checksum gem_repo2, "ruport", "1.7.0.3"
+ end
expect(lockfile).to eq <<~L
GEM
@@ -1526,11 +1767,11 @@ RSpec.describe "bundle install with gems on multiple sources" do
pdf-writer (= 1.1.8)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
ruport (= 1.7.0.3)!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -1564,7 +1805,12 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
it "handles that fine" do
- bundle "install", :artifice => "compact_index_extra", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ bundle "install", artifice: "compact_index_extra", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo4, "pdf-writer", "1.1.8"
+ c.checksum gem_repo2, "ruport", "1.7.0.3"
+ end
expect(lockfile).to eq <<~L
GEM
@@ -1579,11 +1825,11 @@ RSpec.describe "bundle install with gems on multiple sources" do
pdf-writer (= 1.1.8)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
ruport (= 1.7.0.3)!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -1611,7 +1857,11 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
it "handles that fine" do
- bundle "install --verbose", :artifice => "endpoint", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ bundle "install --verbose", artifice: "endpoint", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo4, "pdf-writer", "1.1.8"
+ end
expect(lockfile).to eq <<~L
GEM
@@ -1620,11 +1870,11 @@ RSpec.describe "bundle install with gems on multiple sources" do
pdf-writer (1.1.8)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
pdf-writer (= 1.1.8)
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -1643,7 +1893,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
build_gem "example", "1.0.0"
end
- install_gemfile <<~G, :artifice => "compact_index"
+ install_gemfile <<~G, artifice: "compact_index"
source "https://gem.repo2"
source "https://gem.repo4" do
@@ -1663,7 +1913,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
it "shows a proper error message and does not generate a corrupted lockfile" do
expect do
- bundle :install, :artifice => "compact_index", :raise_on_error => false, :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ bundle :install, artifice: "compact_index", raise_on_error: false, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
end.not_to change { lockfile }
expect(err).to include("Could not find gem 'example' in rubygems repository https://gem.repo4/")
diff --git a/spec/bundler/install/gemfile/specific_platform_spec.rb b/spec/bundler/install/gemfile/specific_platform_spec.rb
index 6974af3270..5f1b034bfc 100644
--- a/spec/bundler/install/gemfile/specific_platform_spec.rb
+++ b/spec/bundler/install/gemfile/specific_platform_spec.rb
@@ -11,11 +11,11 @@ RSpec.describe "bundle install with specific platforms" do
setup_multiplatform_gem
install_gemfile(google_protobuf)
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- expect(the_bundle.locked_gems.platforms).to eq([pl("x86_64-darwin-15")])
+ expect(the_bundle.locked_gems.platforms).to include(pl("x86_64-darwin-15"))
expect(the_bundle).to include_gem("google-protobuf 3.0.0.alpha.5.0.5.1 universal-darwin")
- expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[
- google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin
- ])
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to include(
+ "google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin"
+ )
end
end
@@ -26,9 +26,9 @@ RSpec.describe "bundle install with specific platforms" do
system_gems "bundler-2.1.4"
# Consistent location to install and look for gems
- bundle "config set --local path vendor/bundle", :env => { "BUNDLER_VERSION" => "2.1.4" }
+ bundle "config set --local path vendor/bundle", env: { "BUNDLER_VERSION" => "2.1.4" }
- install_gemfile(google_protobuf, :env => { "BUNDLER_VERSION" => "2.1.4" })
+ install_gemfile(google_protobuf, env: { "BUNDLER_VERSION" => "2.1.4" })
# simulate lockfile created with old bundler, which only locks for ruby platform
lockfile <<-L
@@ -48,7 +48,7 @@ RSpec.describe "bundle install with specific platforms" do
L
# force strict usage of the lock file by setting frozen mode
- bundle "config set --local frozen true", :env => { "BUNDLER_VERSION" => "2.1.4" }
+ bundle "config set --local frozen true", env: { "BUNDLER_VERSION" => "2.1.4" }
# make sure the platform that got actually installed with the old bundler is used
expect(the_bundle).to include_gem("google-protobuf 3.0.0.alpha.5.0.5.1 universal-darwin")
@@ -62,10 +62,14 @@ RSpec.describe "bundle install with specific platforms" do
system_gems "bundler-2.1.4"
# Consistent location to install and look for gems
- bundle "config set --local path vendor/bundle", :env => { "BUNDLER_VERSION" => "2.1.4" }
+ bundle "config set --local path vendor/bundle", env: { "BUNDLER_VERSION" => "2.1.4" }
gemfile google_protobuf
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo2, "google-protobuf", "3.0.0.alpha.4.0"
+ end
+
# simulate lockfile created with old bundler, which only locks for ruby platform
lockfile <<-L
GEM
@@ -78,12 +82,14 @@ RSpec.describe "bundle install with specific platforms" do
DEPENDENCIES
google-protobuf
-
+ #{checksums}
BUNDLED WITH
2.1.4
L
- bundle "update", :env => { "BUNDLER_VERSION" => Bundler::VERSION }
+ bundle "update", env: { "BUNDLER_VERSION" => Bundler::VERSION }
+
+ checksums.checksum gem_repo2, "google-protobuf", "3.0.0.alpha.5.0.5.1"
# make sure the platform that the platform specific dependency is used, since we're only locked to ruby
expect(the_bundle).to include_gem("google-protobuf 3.0.0.alpha.5.0.5.1 universal-darwin")
@@ -100,7 +106,7 @@ RSpec.describe "bundle install with specific platforms" do
DEPENDENCIES
google-protobuf
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -115,8 +121,6 @@ RSpec.describe "bundle install with specific platforms" do
s.platform = "arm64-darwin"
s.required_ruby_version = "< #{Gem.ruby_version}"
end
-
- build_gem "bundler", "2.1.4"
end
gemfile <<~G
@@ -137,22 +141,21 @@ RSpec.describe "bundle install with specific platforms" do
DEPENDENCIES
nokogiri
- RUBY VERSION
- 2.5.3p105
-
BUNDLED WITH
- 2.1.4
+ #{Bundler::VERSION}
L
simulate_platform "arm64-darwin-22", &example
end
it "still installs the generic RUBY variant if necessary" do
- bundle "update --bundler", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ bundle "install --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ expect(out).to include("Installing nokogiri 1.3.10")
end
it "still installs the generic RUBY variant if necessary, even in frozen mode" do
- bundle "update --bundler", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s, "BUNDLE_FROZEN" => "true" }
+ bundle "install --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s, "BUNDLE_FROZEN" => "true" }
+ expect(out).to include("Installing nokogiri 1.3.10")
end
end
@@ -170,7 +173,7 @@ RSpec.describe "bundle install with specific platforms" do
system_gems "bundler-2.1.4"
# Consistent location to install and look for gems
- bundle "config set --local path vendor/bundle", :env => { "BUNDLER_VERSION" => "2.1.4" }
+ bundle "config set --local path vendor/bundle", env: { "BUNDLER_VERSION" => "2.1.4" }
gemfile <<-G
source "https://localgemserver.test"
@@ -194,10 +197,10 @@ RSpec.describe "bundle install with specific platforms" do
2.1.4
L
- bundle "install --verbose", :artifice => "compact_index", :env => { "BUNDLER_VERSION" => "2.1.4", "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+ bundle "install --verbose", artifice: "compact_index", env: { "BUNDLER_VERSION" => "2.1.4", "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
expect(out).to include("Installing libv8 8.4.255.0 (universal-darwin)")
- bundle "add mini_racer --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+ bundle "add mini_racer --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
expect(out).to include("Using libv8 8.4.255.0 (universal-darwin)")
end
end
@@ -231,7 +234,7 @@ RSpec.describe "bundle install with specific platforms" do
#{Bundler::VERSION}
L
- bundle "install --verbose", :artifice => "compact_index_precompiled_before", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ bundle "install --verbose", artifice: "compact_index_precompiled_before", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
expect(out).to include("Installing grpc 1.50.0 (universal-darwin)")
end
end
@@ -306,10 +309,10 @@ RSpec.describe "bundle install with specific platforms" do
G
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- expect(the_bundle.locked_gems.platforms).to eq([pl("x86_64-darwin-15")])
+ expect(the_bundle.locked_gems.platforms).to include(pl("x86_64-darwin-15"))
expect(the_bundle).to include_gems("facter 2.4.6 universal-darwin", "CFPropertyList 1.0")
- expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(["CFPropertyList-1.0",
- "facter-2.4.6-universal-darwin"])
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to include("CFPropertyList-1.0",
+ "facter-2.4.6-universal-darwin")
end
end
@@ -324,8 +327,8 @@ RSpec.describe "bundle install with specific platforms" do
install_gemfile(google_protobuf)
bundle "lock --add-platform=#{x64_mingw32}"
- expect(the_bundle.locked_gems.platforms).to eq([x64_mingw32, pl("x86_64-darwin-15")])
- expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[
+ expect(the_bundle.locked_gems.platforms).to include(x64_mingw32, pl("x86_64-darwin-15"))
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to include(*%w[
google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin
google-protobuf-3.0.0.alpha.5.0.5.1-x64-mingw32
])
@@ -338,11 +341,11 @@ RSpec.describe "bundle install with specific platforms" do
install_gemfile(google_protobuf)
bundle "lock --add-platform=#{java}"
- expect(the_bundle.locked_gems.platforms).to eq([java, pl("x86_64-darwin-15")])
- expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[
- google-protobuf-3.0.0.alpha.5.0.5.1
- google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin
- ])
+ expect(the_bundle.locked_gems.platforms).to include(java, pl("x86_64-darwin-15"))
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to include(
+ "google-protobuf-3.0.0.alpha.5.0.5.1",
+ "google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin"
+ )
end
end
end
@@ -392,7 +395,7 @@ RSpec.describe "bundle install with specific platforms" do
G
error_message = <<~ERROR.strip
- Could not find gem 'sorbet-static (= 0.5.6433)' with platform 'arm64-darwin-21' in rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally.
+ Could not find gem 'sorbet-static (= 0.5.6433)' with platform 'arm64-darwin-21' in rubygems repository #{file_uri_for(gem_repo4)}/, cached gems or installed locally.
The source contains the following gems matching 'sorbet-static (= 0.5.6433)':
* sorbet-static-0.5.6433-universal-darwin-20
@@ -400,7 +403,7 @@ RSpec.describe "bundle install with specific platforms" do
ERROR
simulate_platform "arm64-darwin-21" do
- bundle "lock", :raise_on_error => false
+ bundle "lock", raise_on_error: false
end
expect(err).to include(error_message).once
@@ -408,7 +411,7 @@ RSpec.describe "bundle install with specific platforms" do
# Make sure it doesn't print error twice in verbose mode
simulate_platform "arm64-darwin-21" do
- bundle "lock --verbose", :raise_on_error => false
+ bundle "lock --verbose", raise_on_error: false
end
expect(err).to include(error_message).once
@@ -431,7 +434,7 @@ RSpec.describe "bundle install with specific platforms" do
Could not find compatible versions
Because every version of sorbet depends on sorbet-static = 0.5.6433
- and sorbet-static = 0.5.6433 could not be found in rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally for any resolution platforms (arm64-darwin-21),
+ and sorbet-static = 0.5.6433 could not be found in rubygems repository #{file_uri_for(gem_repo4)}/, cached gems or installed locally for any resolution platforms (arm64-darwin-21),
sorbet cannot be used.
So, because Gemfile depends on sorbet = 0.5.6433,
version solving has failed.
@@ -442,7 +445,7 @@ RSpec.describe "bundle install with specific platforms" do
ERROR
simulate_platform "arm64-darwin-21" do
- bundle "lock", :raise_on_error => false
+ bundle "lock", raise_on_error: false
end
expect(err).to include(error_message).once
@@ -450,7 +453,7 @@ RSpec.describe "bundle install with specific platforms" do
# Make sure it doesn't print error twice in verbose mode
simulate_platform "arm64-darwin-21" do
- bundle "lock --verbose", :raise_on_error => false
+ bundle "lock --verbose", raise_on_error: false
end
expect(err).to include(error_message).once
@@ -467,10 +470,10 @@ RSpec.describe "bundle install with specific platforms" do
gem "sorbet-static", "0.5.9889"
G
- bundle "lock", :raise_on_error => false, :env => { "BUNDLE_FORCE_RUBY_PLATFORM" => "true" }
+ bundle "lock", raise_on_error: false, env: { "BUNDLE_FORCE_RUBY_PLATFORM" => "true" }
expect(err).to include <<~ERROR.rstrip
- Could not find gem 'sorbet-static (= 0.5.9889)' with platform 'ruby' in rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally.
+ Could not find gem 'sorbet-static (= 0.5.9889)' with platform 'ruby' in rubygems repository #{file_uri_for(gem_repo4)}/, cached gems or installed locally.
The source contains the following gems matching 'sorbet-static (= 0.5.9889)':
* sorbet-static-0.5.9889-#{Gem::Platform.local}
@@ -514,7 +517,7 @@ RSpec.describe "bundle install with specific platforms" do
sorbet-runtime (= 0.5.10160)
PLATFORMS
- #{lockfile_platforms("ruby")}
+ #{lockfile_platforms}
DEPENDENCIES
sorbet-static-and-runtime
@@ -525,6 +528,13 @@ RSpec.describe "bundle install with specific platforms" do
bundle "update"
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo4, "sorbet", "0.5.10160"
+ c.checksum gem_repo4, "sorbet-runtime", "0.5.10160"
+ c.checksum gem_repo4, "sorbet-static", "0.5.10160", Gem::Platform.local
+ c.checksum gem_repo4, "sorbet-static-and-runtime", "0.5.10160"
+ end
+
expect(lockfile).to eq <<~L
GEM
remote: #{file_uri_for(gem_repo4)}/
@@ -538,11 +548,11 @@ RSpec.describe "bundle install with specific platforms" do
sorbet-runtime (= 0.5.10160)
PLATFORMS
- #{lockfile_platforms}
+ #{local_platform}
DEPENDENCIES
sorbet-static-and-runtime
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -574,6 +584,11 @@ RSpec.describe "bundle install with specific platforms" do
G
end
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "nokogiri", "1.13.0", "x86_64-darwin"
+ c.no_checksum "sorbet-static", "0.5.10601", "x86_64-darwin"
+ end
+
lockfile <<~L
GEM
remote: #{file_uri_for(gem_repo4)}/
@@ -588,8 +603,8 @@ RSpec.describe "bundle install with specific platforms" do
DEPENDENCIES
nokogiri
- sorbet
-
+ sorbet-static
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -611,7 +626,7 @@ RSpec.describe "bundle install with specific platforms" do
DEPENDENCIES
nokogiri
sorbet-static
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -665,6 +680,13 @@ RSpec.describe "bundle install with specific platforms" do
bundle "update"
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo4, "sorbet", "0.5.10160"
+ c.checksum gem_repo4, "sorbet-runtime", "0.5.10160"
+ c.checksum gem_repo4, "sorbet-static", "0.5.10160", Gem::Platform.local
+ c.checksum gem_repo4, "sorbet-static-and-runtime", "0.5.10160"
+ end
+
expect(lockfile).to eq <<~L
GEM
remote: #{file_uri_for(gem_repo4)}/
@@ -678,16 +700,86 @@ RSpec.describe "bundle install with specific platforms" do
sorbet-runtime (= 0.5.10160)
PLATFORMS
- #{lockfile_platforms}
+ #{local_platform}
DEPENDENCIES
sorbet-static-and-runtime
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
end
+ it "automatically fixes the lockfile if multiple platforms locked, but no valid versions of direct dependencies for all of them" do
+ simulate_platform "x86_64-linux" do
+ build_repo4 do
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "x86_64-linux"
+ end
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "arm-linux"
+ end
+
+ build_gem "sorbet-static", "0.5.10696" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "nokogiri"
+ gem "sorbet-static"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.14.0-arm-linux)
+ nokogiri (1.14.0-x86_64-linux)
+ sorbet-static (0.5.10696-x86_64-linux)
+
+ PLATFORMS
+ aarch64-linux
+ arm-linux
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+ sorbet-static
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update"
+
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo4, "nokogiri", "1.14.0", "x86_64-linux"
+ c.checksum gem_repo4, "sorbet-static", "0.5.10696", "x86_64-linux"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.14.0-x86_64-linux)
+ sorbet-static (0.5.10696-x86_64-linux)
+
+ PLATFORMS
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+ sorbet-static
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
it "automatically fixes the lockfile without removing other variants if it's missing platform gems, but they are installed locally" do
simulate_platform "x86_64-darwin-21" do
build_repo4 do
@@ -707,6 +799,11 @@ RSpec.describe "bundle install with specific platforms" do
gem "sorbet-static", "= 0.5.10549"
G
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo4, "sorbet-static", "0.5.10549", "universal-darwin-20"
+ c.checksum gem_repo4, "sorbet-static", "0.5.10549", "universal-darwin-21"
+ end
+
# Make sure the lockfile is missing sorbet-static-0.5.10549-universal-darwin-21
lockfile <<~L
GEM
@@ -719,13 +816,15 @@ RSpec.describe "bundle install with specific platforms" do
DEPENDENCIES
sorbet-static (= 0.5.10549)
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
bundle "install"
+ checksums.no_checksum "sorbet-static", "0.5.10549", "universal-darwin-21"
+
expect(lockfile).to eq <<~L
GEM
remote: #{file_uri_for(gem_repo4)}/
@@ -738,7 +837,7 @@ RSpec.describe "bundle install with specific platforms" do
DEPENDENCIES
sorbet-static (= 0.5.10549)
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -775,6 +874,8 @@ RSpec.describe "bundle install with specific platforms" do
nokogiri
tzinfo (~> 1.2)
+ CHECKSUMS
+
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -783,7 +884,30 @@ RSpec.describe "bundle install with specific platforms" do
bundle "lock --update"
- expect(lockfile).to eq(original_lockfile)
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "nokogiri", "1.13.8"
+ c.no_checksum "nokogiri", "1.13.8", Gem::Platform.local
+ end
+
+ updated_lockfile = <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.13.8)
+ nokogiri (1.13.8-#{Gem::Platform.local})
+
+ PLATFORMS
+ #{lockfile_platforms("ruby")}
+
+ DEPENDENCIES
+ nokogiri
+ tzinfo (~> 1.2)
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ expect(lockfile).to eq(updated_lockfile)
end
it "does not remove ruby when adding a new gem to the Gemfile" do
@@ -799,6 +923,11 @@ RSpec.describe "bundle install with specific platforms" do
gem "rack"
G
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "concurrent-ruby", "1.2.2"
+ c.no_checksum "rack", "3.0.7"
+ end
+
lockfile <<~L
GEM
remote: #{file_uri_for(gem_repo4)}/
@@ -810,7 +939,7 @@ RSpec.describe "bundle install with specific platforms" do
DEPENDENCIES
concurrent-ruby
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -825,12 +954,12 @@ RSpec.describe "bundle install with specific platforms" do
rack (3.0.7)
PLATFORMS
- #{formatted_lockfile_platforms(*["ruby", generic_local_platform].uniq)}
+ #{lockfile_platforms("ruby", generic_local_platform, defaults: [])}
DEPENDENCIES
concurrent-ruby
rack
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -893,6 +1022,10 @@ RSpec.describe "bundle install with specific platforms" do
gem "nokogiri", "1.14.0"
G
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo4, "nokogiri", "1.14.0", "x86_64-linux"
+ end
+
lockfile <<~L
GEM
remote: #{file_uri_for(gem_repo4)}/
@@ -904,13 +1037,17 @@ RSpec.describe "bundle install with specific platforms" do
DEPENDENCIES
nokogiri (= 1.14.0)
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
bundle :install
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo4, "nokogiri", "1.14.0"
+ end
+
expect(lockfile).to eq(<<~L)
GEM
remote: #{file_uri_for(gem_repo4)}/
@@ -922,6 +1059,320 @@ RSpec.describe "bundle install with specific platforms" do
DEPENDENCIES
nokogiri (= 1.14.0)
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ it "locks specific platforms automatically" do
+ simulate_platform "x86_64-linux" do
+ build_repo4 do
+ build_gem "nokogiri", "1.14.0"
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "x86_64-linux"
+ end
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "arm-linux"
+ end
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "x64-mingw32"
+ end
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "java"
+ end
+
+ build_gem "sorbet-static", "0.5.10696" do |s|
+ s.platform = "x86_64-linux"
+ end
+ build_gem "sorbet-static", "0.5.10696" do |s|
+ s.platform = "universal-darwin-22"
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "nokogiri"
+ G
+
+ bundle "lock"
+
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "nokogiri", "1.14.0"
+ c.no_checksum "nokogiri", "1.14.0", "arm-linux"
+ c.no_checksum "nokogiri", "1.14.0", "x86_64-linux"
+ end
+
+ # locks all compatible platforms, excluding Java and Windows
+ expect(lockfile).to eq(<<~L)
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.14.0)
+ nokogiri (1.14.0-arm-linux)
+ nokogiri (1.14.0-x86_64-linux)
+
+ PLATFORMS
+ arm-linux
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "nokogiri"
+ gem "sorbet-static"
+ G
+
+ FileUtils.rm bundled_app_lock
+
+ bundle "lock"
+
+ checksums.delete "nokogiri", "arm-linux"
+ checksums.no_checksum "sorbet-static", "0.5.10696", "universal-darwin-22"
+ checksums.no_checksum "sorbet-static", "0.5.10696", "x86_64-linux"
+
+ # locks only platforms compatible with all gems in the bundle
+ expect(lockfile).to eq(<<~L)
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.14.0)
+ nokogiri (1.14.0-x86_64-linux)
+ sorbet-static (0.5.10696-universal-darwin-22)
+ sorbet-static (0.5.10696-x86_64-linux)
+
+ PLATFORMS
+ universal-darwin-22
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+ sorbet-static
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ it "does not fail when a platform variant is incompatible with the current ruby and another equivalent platform specific variant is part of the resolution", rubygems: ">= 3.3.21" do
+ build_repo4 do
+ build_gem "nokogiri", "1.15.5"
+
+ build_gem "nokogiri", "1.15.5" do |s|
+ s.platform = "x86_64-linux"
+ s.required_ruby_version = "< #{current_ruby_minor}.dev"
+ end
+
+ build_gem "sass-embedded", "1.69.5"
+
+ build_gem "sass-embedded", "1.69.5" do |s|
+ s.platform = "x86_64-linux-gnu"
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "nokogiri"
+ gem "sass-embedded"
+ G
+
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo4, "nokogiri", "1.15.5"
+ c.no_checksum "sass-embedded", "1.69.5"
+ c.checksum gem_repo4, "sass-embedded", "1.69.5", "x86_64-linux-gnu"
+ end
+
+ simulate_platform "x86_64-linux" do
+ bundle "install --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+
+ # locks all compatible platforms, excluding Java and Windows
+ expect(lockfile).to eq(<<~L)
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.15.5)
+ sass-embedded (1.69.5)
+ sass-embedded (1.69.5-x86_64-linux-gnu)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+ sass-embedded
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ it "does not add ruby platform gem if it brings extra dependencies not resolved originally" do
+ build_repo4 do
+ build_gem "nokogiri", "1.15.5" do |s|
+ s.add_dependency "mini_portile2", "~> 2.8.2"
+ end
+
+ build_gem "nokogiri", "1.15.5" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "nokogiri"
+ G
+
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo4, "nokogiri", "1.15.5", "x86_64-linux"
+ end
+
+ simulate_platform "x86_64-linux" do
+ bundle "install --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+
+ expect(lockfile).to eq(<<~L)
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.15.5-x86_64-linux)
+
+ PLATFORMS
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ ["x86_64-linux", "x86_64-linux-musl"].each do |host_platform|
+ describe "on host platform #{host_platform}" do
+ it "adds current musl platform" do
+ build_repo4 do
+ build_gem "rcee_precompiled", "0.5.0" do |s|
+ s.platform = "x86_64-linux"
+ end
+
+ build_gem "rcee_precompiled", "0.5.0" do |s|
+ s.platform = "x86_64-linux-musl"
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "rcee_precompiled", "0.5.0"
+ G
+
+ simulate_platform host_platform do
+ bundle "lock", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+
+ expect(lockfile).to eq(<<~L)
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ rcee_precompiled (0.5.0-x86_64-linux)
+ rcee_precompiled (0.5.0-x86_64-linux-musl)
+
+ PLATFORMS
+ x86_64-linux
+ x86_64-linux-musl
+
+ DEPENDENCIES
+ rcee_precompiled (= 0.5.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+ end
+ end
+
+ it "adds current musl platform, when there are also gnu variants", rubygems: ">= 3.3.21" do
+ build_repo4 do
+ build_gem "rcee_precompiled", "0.5.0" do |s|
+ s.platform = "x86_64-linux-gnu"
+ end
+
+ build_gem "rcee_precompiled", "0.5.0" do |s|
+ s.platform = "x86_64-linux-musl"
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "rcee_precompiled", "0.5.0"
+ G
+
+ simulate_platform "x86_64-linux-musl" do
+ bundle "lock", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+
+ expect(lockfile).to eq(<<~L)
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ rcee_precompiled (0.5.0-x86_64-linux-gnu)
+ rcee_precompiled (0.5.0-x86_64-linux-musl)
+
+ PLATFORMS
+ x86_64-linux-gnu
+ x86_64-linux-musl
+
+ DEPENDENCIES
+ rcee_precompiled (= 0.5.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ it "does not add current platform if there's an equivalent less specific platform among the ones resolved" do
+ build_repo4 do
+ build_gem "rcee_precompiled", "0.5.0" do |s|
+ s.platform = "universal-darwin"
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "rcee_precompiled", "0.5.0"
+ G
+
+ simulate_platform "x86_64-darwin-15" do
+ bundle "lock", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+
+ expect(lockfile).to eq(<<~L)
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ rcee_precompiled (0.5.0-universal-darwin)
+
+ PLATFORMS
+ universal-darwin
+
+ DEPENDENCIES
+ rcee_precompiled (= 0.5.0)
BUNDLED WITH
#{Bundler::VERSION}
diff --git a/spec/bundler/install/gemfile_spec.rb b/spec/bundler/install/gemfile_spec.rb
index e643573454..158645d3eb 100644
--- a/spec/bundler/install/gemfile_spec.rb
+++ b/spec/bundler/install/gemfile_spec.rb
@@ -3,7 +3,7 @@
RSpec.describe "bundle install" do
context "with duplicated gems" do
it "will display a warning" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
gem 'rails', '~> 4.0.0'
@@ -20,7 +20,7 @@ RSpec.describe "bundle install" do
gem 'rack'
G
- bundle :install, :gemfile => bundled_app("NotGemfile")
+ bundle :install, gemfile: bundled_app("NotGemfile")
# Specify BUNDLE_GEMFILE for `the_bundle`
# to retrieve the proper Gemfile
@@ -46,8 +46,8 @@ RSpec.describe "bundle install" do
end
it "uses the gemfile while in a subdirectory" do
bundled_app("subdir").mkpath
- bundle "install", :dir => bundled_app("subdir")
- bundle "list", :dir => bundled_app("subdir")
+ bundle "install", dir: bundled_app("subdir")
+ bundle "list", dir: bundled_app("subdir")
expect(out).to include("rack (1.0.0)")
end
@@ -61,7 +61,7 @@ RSpec.describe "bundle install" do
gem "rack", :lib => "rack"
G
- bundle :install, :raise_on_error => false
+ bundle :install, raise_on_error: false
expect(err).to match(/You passed :lib as an option for gem 'rack', but it is invalid/)
end
end
diff --git a/spec/bundler/install/gems/compact_index_spec.rb b/spec/bundler/install/gems/compact_index_spec.rb
index 8ab8e61673..50add8743b 100644
--- a/spec/bundler/install/gems/compact_index_spec.rb
+++ b/spec/bundler/install/gems/compact_index_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe "compact index api" do
gem "rack"
G
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(out).to include("Fetching gem metadata from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -21,7 +21,7 @@ RSpec.describe "compact index api" do
gem " sinatra"
G
- bundle :install, :artifice => "compact_index", :raise_on_error => false
+ bundle :install, artifice: "compact_index", raise_on_error: false
expect(err).to include("' sinatra' is not a valid gem name because it contains whitespace.")
end
@@ -31,7 +31,7 @@ RSpec.describe "compact index api" do
gem "rails"
G
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(out).to include("Fetching gem metadata from #{source_uri}")
expect(the_bundle).to include_gems(
"rails 2.3.2",
@@ -44,14 +44,14 @@ RSpec.describe "compact index api" do
end
it "should handle case sensitivity conflicts" do
- build_repo4 do
+ build_repo4(build_compact_index: false) do
build_gem "rack", "1.0" do |s|
s.add_runtime_dependency("Rack", "0.1")
end
build_gem "Rack", "0.1"
end
- install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
source "#{source_uri}"
gem "rack", "1.0"
gem "Rack", "0.1"
@@ -69,7 +69,7 @@ RSpec.describe "compact index api" do
gem "net-sftp"
G
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(the_bundle).to include_gems "net-sftp 1.1.1"
end
@@ -78,11 +78,11 @@ RSpec.describe "compact index api" do
source "#{source_uri}"
gem "rack"
G
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
bundle "config set --local deployment true"
bundle "config set --local path vendor/bundle"
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(out).to include("Fetching gem metadata from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -100,7 +100,7 @@ RSpec.describe "compact index api" do
end
G
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(the_bundle).to include_gems("rails 2.3.2")
end
@@ -116,10 +116,10 @@ RSpec.describe "compact index api" do
gem 'foo', :git => "#{file_uri_for(lib_path("foo-1.0"))}"
G
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
bundle "config set --local deployment true"
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(the_bundle).to include_gems("rails 2.3.2")
end
@@ -131,9 +131,9 @@ RSpec.describe "compact index api" do
gem 'foo', :git => "#{file_uri_for(lib_path("foo-1.0"))}"
G
- bundle "install", :artifice => "compact_index"
+ bundle "install", artifice: "compact_index"
bundle "config set --local deployment true"
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(the_bundle).to include_gems("foo 1.0")
end
@@ -144,7 +144,7 @@ RSpec.describe "compact index api" do
gem "rack"
G
- bundle :install, :verbose => true, :artifice => "compact_index_forbidden"
+ bundle :install, verbose: true, artifice: "compact_index_forbidden"
expect(out).to include("Fetching gem metadata from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -155,11 +155,10 @@ RSpec.describe "compact index api" do
gem "rack"
G
- bundle :install, :verbose => true, :artifice => "compact_index_checksum_mismatch"
+ bundle :install, verbose: true, artifice: "compact_index_checksum_mismatch"
expect(out).to include("Fetching gem metadata from #{source_uri}")
- expect(out).to include <<-'WARN'
-The checksum of /versions does not match the checksum provided by the server! Something is wrong (local checksum is "\"d41d8cd98f00b204e9800998ecf8427e\"", was expecting "\"123\"").
- WARN
+ expect(out).to include("The checksum of /versions does not match the checksum provided by the server!")
+ expect(out).to include('Calculated checksums {"sha-256"=>"8KfZiM/fszVkqhP/m5s9lvE6M9xKu4I1bU4Izddp5Ms="} did not match expected {"sha-256"=>"ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0="}')
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -169,13 +168,15 @@ The checksum of /versions does not match the checksum provided by the server! So
gem "rack"
G
- versions = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index",
- "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions")
- FileUtils.mkdir_p(File.dirname(versions))
- FileUtils.touch(versions)
+ versions = Pathname.new(Bundler.rubygems.user_home).join(
+ ".bundle", "cache", "compact_index",
+ "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions"
+ )
+ versions.dirname.mkpath
+ versions.write("created_at")
FileUtils.chmod("-r", versions)
- bundle :install, :artifice => "compact_index", :raise_on_error => false
+ bundle :install, artifice: "compact_index", raise_on_error: false
expect(err).to include(
"There was an error while trying to read from `#{versions}`. It is likely that you need to grant read permissions for that path."
@@ -190,7 +191,7 @@ The checksum of /versions does not match the checksum provided by the server! So
gem "rack"
G
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(out).to include("Fetching gem metadata from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -201,11 +202,11 @@ The checksum of /versions does not match the checksum provided by the server! So
gem "rack"
G
- bundle :install, :artifice => "compact_index_host_redirect"
+ bundle :install, artifice: "compact_index_host_redirect"
expect(the_bundle).to include_gems "rack 1.0.0"
end
- it "handles host redirects without Net::HTTP::Persistent" do
+ it "handles host redirects without Gem::Net::HTTP::Persistent" do
gemfile <<-G
source "#{source_uri}"
gem "rack"
@@ -224,7 +225,7 @@ The checksum of /versions does not match the checksum provided by the server! So
H
end
- bundle :install, :artifice => "compact_index_host_redirect", :requires => [lib_path("disable_net_http_persistent.rb")]
+ bundle :install, artifice: "compact_index_host_redirect", requires: [lib_path("disable_net_http_persistent.rb")]
expect(out).to_not match(/Too many redirects/)
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -235,7 +236,7 @@ The checksum of /versions does not match the checksum provided by the server! So
gem "rack"
G
- bundle :install, :artifice => "compact_index_redirects", :raise_on_error => false
+ bundle :install, artifice: "compact_index_redirects", raise_on_error: false
expect(err).to match(/Too many redirects/)
end
@@ -246,7 +247,7 @@ The checksum of /versions does not match the checksum provided by the server! So
gem "rack"
G
- bundle "install --full-index", :artifice => "compact_index"
+ bundle "install --full-index", artifice: "compact_index"
expect(out).to include("Fetching source index from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -257,7 +258,7 @@ The checksum of /versions does not match the checksum provided by the server! So
gem "rack"
G
- bundle "update --full-index", :artifice => "compact_index", :all => true
+ bundle "update --full-index", artifice: "compact_index", all: true
expect(out).to include("Fetching source index from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -287,14 +288,14 @@ The checksum of /versions does not match the checksum provided by the server! So
end
end
- system_gems %w[rack-1.0.0 thin-1.0 net_a-1.0], :gem_repo => gem_repo2
+ system_gems %w[rack-1.0.0 thin-1.0 net_a-1.0], gem_repo: gem_repo2
bundle "config set --local path.system true"
- ENV["BUNDLER_SPEC_ALL_REQUESTS"] = strip_whitespace(<<-EOS).strip
+ ENV["BUNDLER_SPEC_ALL_REQUESTS"] = <<~EOS.strip
#{source_uri}/versions
#{source_uri}/info/rack
EOS
- install_gemfile <<-G, :artifice => "compact_index", :verbose => true, :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+ install_gemfile <<-G, artifice: "compact_index", verbose: true, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
source "#{source_uri}"
gem "rack"
G
@@ -302,7 +303,7 @@ The checksum of /versions does not match the checksum provided by the server! So
expect(last_command.stdboth).not_to include "Double checking"
end
- it "fetches again when more dependencies are found in subsequent sources", :bundler => "< 3" do
+ it "fetches again when more dependencies are found in subsequent sources", bundler: "< 3" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
@@ -316,7 +317,7 @@ The checksum of /versions does not match the checksum provided by the server! So
gem "back_deps"
G
- bundle :install, :artifice => "compact_index_extra"
+ bundle :install, artifice: "compact_index_extra"
expect(the_bundle).to include_gems "back_deps 1.0", "foo 1.0"
end
@@ -328,7 +329,7 @@ The checksum of /versions does not match the checksum provided by the server! So
FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
end
- install_gemfile <<-G, :artifice => "compact_index_extra", :verbose => true
+ install_gemfile <<-G, artifice: "compact_index_extra", verbose: true
source "#{source_uri}"
source "#{source_uri}/extra" do
gem "back_deps"
@@ -343,7 +344,7 @@ The checksum of /versions does not match the checksum provided by the server! So
source "#{source_uri}"
gem "rack", "1.0.0"
G
- bundle :install, :artifice => "compact_index_extra_api"
+ bundle :install, artifice: "compact_index_extra_api"
expect(the_bundle).to include_gems "rack 1.0.0"
build_repo4 do
@@ -357,11 +358,11 @@ The checksum of /versions does not match the checksum provided by the server! So
source "#{source_uri}/extra"
gem "rack", "1.2"
G
- bundle :install, :artifice => "compact_index_extra_api"
+ bundle :install, artifice: "compact_index_extra_api"
expect(the_bundle).to include_gems "rack 1.2"
end
- it "considers all possible versions of dependencies from all api gem sources", :bundler => "< 3" do
+ it "considers all possible versions of dependencies from all api gem sources", bundler: "< 3" do
# In this scenario, the gem "somegem" only exists in repo4. It depends on specific version of activesupport that
# exists only in repo1. There happens also be a version of activesupport in repo4, but not the one that version 1.0.0
# of somegem wants. This test makes sure that bundler actually finds version 1.2.3 of active support in the other
@@ -379,7 +380,7 @@ The checksum of /versions does not match the checksum provided by the server! So
gem 'somegem', '1.0.0'
G
- bundle :install, :artifice => "compact_index_extra_api"
+ bundle :install, artifice: "compact_index_extra_api"
expect(the_bundle).to include_gems "somegem 1.0.0"
expect(the_bundle).to include_gems "activesupport 1.2.3"
@@ -400,7 +401,7 @@ The checksum of /versions does not match the checksum provided by the server! So
end
G
- bundle :install, :artifice => "compact_index_extra"
+ bundle :install, artifice: "compact_index_extra"
expect(out).to include("Fetching gem metadata from http://localgemserver.test/")
expect(out).to include("Fetching source index from http://localgemserver.test/extra")
@@ -416,7 +417,7 @@ The checksum of /versions does not match the checksum provided by the server! So
FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
end
- install_gemfile <<-G, :artifice => "compact_index_extra_missing", :env => env_for_missing_prerelease_default_gem_activation
+ install_gemfile <<-G, artifice: "compact_index_extra_missing"
source "#{source_uri}"
source "#{source_uri}/extra" do
gem "back_deps"
@@ -436,7 +437,7 @@ The checksum of /versions does not match the checksum provided by the server! So
FileUtils.rm_rf Dir[gem_repo4("gems/foo-*.gem")]
end
- install_gemfile <<-G, :artifice => "compact_index_extra_api_missing", :env => env_for_missing_prerelease_default_gem_activation
+ install_gemfile <<-G, artifice: "compact_index_extra_api_missing"
source "#{source_uri}"
source "#{source_uri}/extra" do
gem "back_deps"
@@ -453,11 +454,11 @@ The checksum of /versions does not match the checksum provided by the server! So
gem 'foo'
G
- bundle :install, :artifice => "compact_index_api_missing"
+ bundle :install, artifice: "compact_index_api_missing"
expect(the_bundle).to include_gems "foo 1.0"
end
- it "fetches again when more dependencies are found in subsequent sources using deployment mode", :bundler => "< 3" do
+ it "fetches again when more dependencies are found in subsequent sources using deployment mode", bundler: "< 3" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
@@ -471,9 +472,9 @@ The checksum of /versions does not match the checksum provided by the server! So
gem "back_deps"
G
- bundle :install, :artifice => "compact_index_extra"
+ bundle :install, artifice: "compact_index_extra"
bundle "config --set local deployment true"
- bundle :install, :artifice => "compact_index_extra"
+ bundle :install, artifice: "compact_index_extra"
expect(the_bundle).to include_gems "back_deps 1.0"
end
@@ -492,9 +493,9 @@ The checksum of /versions does not match the checksum provided by the server! So
end
G
- bundle :install, :artifice => "compact_index_extra"
+ bundle :install, artifice: "compact_index_extra"
bundle "config set --local deployment true"
- bundle :install, :artifice => "compact_index_extra"
+ bundle :install, artifice: "compact_index_extra"
expect(the_bundle).to include_gems "back_deps 1.0"
end
@@ -511,40 +512,40 @@ The checksum of /versions does not match the checksum provided by the server! So
gem "bundler_dep"
G
- bundle :install, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+ bundle :install, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
expect(out).to include("Fetching gem metadata from #{source_uri}")
end
- it "installs the binstubs", :bundler => "< 3" do
+ it "installs the binstubs", bundler: "< 3" do
gemfile <<-G
source "#{source_uri}"
gem "rack"
G
- bundle "install --binstubs", :artifice => "compact_index"
+ bundle "install --binstubs", artifice: "compact_index"
gembin "rackup"
expect(out).to eq("1.0.0")
end
- it "installs the bins when using --path and uses autoclean", :bundler => "< 3" do
+ it "installs the bins when using --path and uses autoclean", bundler: "< 3" do
gemfile <<-G
source "#{source_uri}"
gem "rack"
G
- bundle "install --path vendor/bundle", :artifice => "compact_index"
+ bundle "install --path vendor/bundle", artifice: "compact_index"
expect(vendored_gems("bin/rackup")).to exist
end
- it "installs the bins when using --path and uses bundle clean", :bundler => "< 3" do
+ it "installs the bins when using --path and uses bundle clean", bundler: "< 3" do
gemfile <<-G
source "#{source_uri}"
gem "rack"
G
- bundle "install --path vendor/bundle --no-clean", :artifice => "compact_index"
+ bundle "install --path vendor/bundle --no-clean", artifice: "compact_index"
expect(vendored_gems("bin/rackup")).to exist
end
@@ -555,7 +556,7 @@ The checksum of /versions does not match the checksum provided by the server! So
gem 'rack-obama'
G
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(out).to include("Post-install message from rack:")
end
@@ -565,7 +566,7 @@ The checksum of /versions does not match the checksum provided by the server! So
gem 'rack_middleware'
G
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(out).to include("Post-install message from rack:")
expect(out).to include("Rack's post install message")
end
@@ -574,7 +575,7 @@ The checksum of /versions does not match the checksum provided by the server! So
let(:user) { "user" }
let(:password) { "pass" }
let(:basic_auth_source_uri) do
- uri = Bundler::URI.parse(source_uri)
+ uri = Gem::URI.parse(source_uri)
uri.user = user
uri.password = password
@@ -587,7 +588,7 @@ The checksum of /versions does not match the checksum provided by the server! So
gem "rack"
G
- bundle :install, :artifice => "compact_index_basic_authentication"
+ bundle :install, artifice: "compact_index_basic_authentication"
expect(out).not_to include("#{user}:#{password}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -598,19 +599,19 @@ The checksum of /versions does not match the checksum provided by the server! So
gem "rack"
G
- bundle :install, :verbose => true, :artifice => "compact_index_basic_authentication"
+ bundle :install, verbose: true, artifice: "compact_index_basic_authentication"
expect(out).not_to include("#{user}:#{password}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
- it "strips http basic auth creds when warning about ambiguous sources", :bundler => "< 3" do
+ it "strips http basic auth creds when warning about ambiguous sources", bundler: "< 3" do
gemfile <<-G
source "#{basic_auth_source_uri}"
source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
- bundle :install, :artifice => "compact_index_basic_authentication"
+ bundle :install, artifice: "compact_index_basic_authentication"
expect(err).to include("Warning: the gem 'rack' was found in multiple sources.")
expect(err).not_to include("#{user}:#{password}")
expect(the_bundle).to include_gems "rack 1.0.0"
@@ -622,7 +623,7 @@ The checksum of /versions does not match the checksum provided by the server! So
gem "rack"
G
- bundle :install, :artifice => "compact_index_creds_diff_host"
+ bundle :install, artifice: "compact_index_creds_diff_host"
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -637,7 +638,7 @@ The checksum of /versions does not match the checksum provided by the server! So
it "reads authentication details by host name from bundle config" do
bundle "config set #{source_hostname} #{user}:#{password}"
- bundle :install, :artifice => "compact_index_strict_basic_authentication"
+ bundle :install, artifice: "compact_index_strict_basic_authentication"
expect(out).to include("Fetching gem metadata from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
@@ -647,7 +648,7 @@ The checksum of /versions does not match the checksum provided by the server! So
# The trailing slash is necessary here; Fetcher canonicalizes the URI.
bundle "config set #{source_uri}/ #{user}:#{password}"
- bundle :install, :artifice => "compact_index_strict_basic_authentication"
+ bundle :install, artifice: "compact_index_strict_basic_authentication"
expect(out).to include("Fetching gem metadata from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
@@ -655,7 +656,7 @@ The checksum of /versions does not match the checksum provided by the server! So
it "should use the API" do
bundle "config set #{source_hostname} #{user}:#{password}"
- bundle :install, :artifice => "compact_index_strict_basic_authentication"
+ bundle :install, artifice: "compact_index_strict_basic_authentication"
expect(out).to include("Fetching gem metadata from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -668,21 +669,30 @@ The checksum of /versions does not match the checksum provided by the server! So
bundle "config set #{source_hostname} otheruser:wrong"
- bundle :install, :artifice => "compact_index_strict_basic_authentication"
+ bundle :install, artifice: "compact_index_strict_basic_authentication"
expect(the_bundle).to include_gems "rack 1.0.0"
end
it "shows instructions if auth is not provided for the source" do
- bundle :install, :artifice => "compact_index_strict_basic_authentication", :raise_on_error => false
+ bundle :install, artifice: "compact_index_strict_basic_authentication", raise_on_error: false
expect(err).to include("bundle config set --global #{source_hostname} username:password")
end
it "fails if authentication has already been provided, but failed" do
bundle "config set #{source_hostname} #{user}:wrong"
- bundle :install, :artifice => "compact_index_strict_basic_authentication", :raise_on_error => false
+ bundle :install, artifice: "compact_index_strict_basic_authentication", raise_on_error: false
expect(err).to include("Bad username or password")
end
+
+ it "does not fallback to old dependency API if bad authentication is provided" do
+ bundle "config set #{source_hostname} #{user}:wrong"
+
+ bundle :install, artifice: "compact_index_strict_basic_authentication", raise_on_error: false, verbose: true
+ expect(err).to include("Bad username or password")
+ expect(out).to include("HTTP 401 Unauthorized http://user@localgemserver.test/versions")
+ expect(out).not_to include("HTTP 401 Unauthorized http://user@localgemserver.test/api/v1/dependencies")
+ end
end
describe "with no password" do
@@ -694,7 +704,7 @@ The checksum of /versions does not match the checksum provided by the server! So
gem "rack"
G
- bundle :install, :artifice => "compact_index_basic_authentication"
+ bundle :install, artifice: "compact_index_basic_authentication"
expect(the_bundle).to include_gems "rack 1.0.0"
end
end
@@ -719,7 +729,7 @@ The checksum of /versions does not match the checksum provided by the server! So
gem "rack"
G
- bundle :install, :env => { "RUBYOPT" => opt_add("-I#{bundled_app("broken_ssl")}", ENV["RUBYOPT"]) }, :raise_on_error => false
+ bundle :install, env: { "RUBYOPT" => opt_add("-I#{bundled_app("broken_ssl")}", ENV["RUBYOPT"]) }, raise_on_error: false
expect(err).to include("OpenSSL")
end
end
@@ -729,7 +739,7 @@ The checksum of /versions does not match the checksum provided by the server! So
# Install a monkeypatch that reproduces the effects of openssl raising
# a certificate validation error when RubyGems tries to connect.
gemfile <<-G
- class Net::HTTP
+ class Gem::Net::HTTP
def start
raise OpenSSL::SSL::SSLError, "certificate verify failed"
end
@@ -739,7 +749,7 @@ The checksum of /versions does not match the checksum provided by the server! So
gem "rack"
G
- bundle :install, :raise_on_error => false
+ bundle :install, raise_on_error: false
expect(err).to match(/could not verify the SSL certificate/i)
end
end
@@ -747,7 +757,7 @@ The checksum of /versions does not match the checksum provided by the server! So
context ".gemrc with sources is present" do
it "uses other sources declared in the Gemfile" do
File.open(home(".gemrc"), "w") do |file|
- file.puts({ :sources => ["https://rubygems.org"] }.to_yaml)
+ file.puts({ sources: ["https://rubygems.org"] }.to_yaml)
end
begin
@@ -756,21 +766,30 @@ The checksum of /versions does not match the checksum provided by the server! So
gem 'rack'
G
- bundle :install, :artifice => "compact_index_forbidden"
+ bundle :install, artifice: "compact_index_forbidden"
ensure
home(".gemrc").rmtree
end
end
end
- it "performs partial update with a non-empty range" do
+ it "performs update with etag not-modified" do
+ versions_etag = Pathname.new(Bundler.rubygems.user_home).join(
+ ".bundle", "cache", "compact_index",
+ "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions.etag"
+ )
+ expect(versions_etag.file?).to eq(false)
+
gemfile <<-G
source "#{source_uri}"
gem 'rack', '0.9.1'
G
- # Initial install creates the cached versions file
- bundle :install, :artifice => "compact_index"
+ # Initial install creates the cached versions file and etag file
+ bundle :install, artifice: "compact_index"
+
+ expect(versions_etag.file?).to eq(true)
+ previous_content = versions_etag.binread
# Update the Gemfile so we can check subsequent install was successful
gemfile <<-G
@@ -778,8 +797,59 @@ The checksum of /versions does not match the checksum provided by the server! So
gem 'rack', '1.0.0'
G
- # Second install should make only a partial request to /versions
- bundle :install, :artifice => "compact_index_partial_update"
+ # Second install should match etag
+ bundle :install, artifice: "compact_index_etag_match"
+
+ expect(versions_etag.binread).to eq(previous_content)
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "performs full update when range is ignored" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack', '0.9.1'
+ G
+
+ # Initial install creates the cached versions file and etag file
+ bundle :install, artifice: "compact_index"
+
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack', '1.0.0'
+ G
+
+ versions = Pathname.new(Bundler.rubygems.user_home).join(
+ ".bundle", "cache", "compact_index",
+ "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions"
+ )
+ # Modify the cached file. The ranged request will be based on this but,
+ # in this test, the range is ignored so this gets overwritten, allowing install.
+ versions.write "ruining this file"
+
+ bundle :install, artifice: "compact_index_range_ignored"
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "performs partial update with a non-empty range" do
+ build_repo4 do
+ build_gem "rack", "0.9.1"
+ end
+
+ # Initial install creates the cached versions file
+ install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ source "#{source_uri}"
+ gem 'rack', '0.9.1'
+ G
+
+ update_repo4 do
+ build_gem "rack", "1.0.0"
+ end
+
+ install_gemfile <<-G, artifice: "compact_index_partial_update", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ source "#{source_uri}"
+ gem 'rack', '1.0.0'
+ G
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -790,24 +860,48 @@ The checksum of /versions does not match the checksum provided by the server! So
gem 'rack'
G
- # Create an empty file to trigger a partial download
- versions = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index",
- "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions")
- FileUtils.mkdir_p(File.dirname(versions))
- FileUtils.touch(versions)
+ # Create a partial cache versions file
+ versions = Pathname.new(Bundler.rubygems.user_home).join(
+ ".bundle", "cache", "compact_index",
+ "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions"
+ )
+ versions.dirname.mkpath
+ versions.write("created_at")
+
+ bundle :install, artifice: "compact_index_concurrent_download"
+
+ expect(versions.read).to start_with("created_at")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "performs a partial update that fails digest check, then a full update" do
+ build_repo4 do
+ build_gem "rack", "0.9.1"
+ end
+
+ install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ source "#{source_uri}"
+ gem 'rack', '0.9.1'
+ G
+
+ update_repo4 do
+ build_gem "rack", "1.0.0"
+ end
- bundle :install, :artifice => "compact_index_concurrent_download"
+ install_gemfile <<-G, artifice: "compact_index_partial_update_bad_digest", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ source "#{source_uri}"
+ gem 'rack', '1.0.0'
+ G
- expect(File.read(versions)).to start_with("created_at")
expect(the_bundle).to include_gems "rack 1.0.0"
end
- it "performs full update if server endpoints serve partial content responses but don't have incremental content and provide no Etag" do
+ it "performs full update if server endpoints serve partial content responses but don't have incremental content and provide no digest" do
build_repo4 do
build_gem "rack", "0.9.1"
end
- install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
source "#{source_uri}"
gem 'rack', '0.9.1'
G
@@ -816,7 +910,7 @@ The checksum of /versions does not match the checksum provided by the server! So
build_gem "rack", "1.0.0"
end
- install_gemfile <<-G, :artifice => "compact_index_partial_update_no_etag_not_incremental", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ install_gemfile <<-G, artifice: "compact_index_partial_update_no_digest_not_incremental", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
source "#{source_uri}"
gem 'rack', '1.0.0'
G
@@ -830,15 +924,19 @@ The checksum of /versions does not match the checksum provided by the server! So
gem 'rack', '0.9.1'
G
- rake_info_path = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index",
- "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "info", "rack")
+ bundle :install, artifice: "compact_index"
- bundle :install, :artifice => "compact_index"
+ # We must remove the etag so that we don't ignore the range and get a 304 Not Modified.
+ rake_info_etag_path = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index",
+ "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "info-etags", "rack-11690b09f16021ff06a6857d784a1870")
+ File.unlink(rake_info_etag_path) if File.exist?(rake_info_etag_path)
+ rake_info_path = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index",
+ "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "info", "rack")
expected_rack_info_content = File.read(rake_info_path)
- # Modify the cache files. We expect them to be reset to the normal ones when we re-run :install
- File.open(rake_info_path, "w") {|f| f << (expected_rack_info_content + "this is different") }
+ # Modify the cache files to make the range not satisfiable
+ File.open(rake_info_path, "a") {|f| f << "0.9.2 |checksum:c55b525b421fd833a93171ad3d7f04528ca8e87d99ac273f8933038942a5888c" }
# Update the Gemfile so the next install does its normal things
gemfile <<-G
@@ -848,7 +946,7 @@ The checksum of /versions does not match the checksum provided by the server! So
# The cache files now being longer means the requested range is going to be not satisfiable
# Bundler must end up requesting the whole file to fix things up.
- bundle :install, :artifice => "compact_index_range_not_satisfiable"
+ bundle :install, artifice: "compact_index_range_not_satisfiable"
resulting_rack_info_content = File.read(rake_info_path)
@@ -856,7 +954,7 @@ The checksum of /versions does not match the checksum provided by the server! So
end
it "fails gracefully when the source URI has an invalid scheme" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "htps://rubygems.org"
gem "rack"
G
@@ -867,38 +965,76 @@ The checksum of /versions does not match the checksum provided by the server! So
end
describe "checksum validation" do
+ before do
+ lockfile <<-L
+ GEM
+ remote: #{source_uri}
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ #{checksums_section}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "handles checksums from the server in base64" do
+ api_checksum = checksum_digest(gem_repo1, "rack", "1.0.0")
+ rack_checksum = [[api_checksum].pack("H*")].pack("m0")
+ install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_RACK_CHECKSUM" => rack_checksum }
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems("rack 1.0.0")
+ end
+
it "raises when the checksum does not match" do
- install_gemfile <<-G, :artifice => "compact_index_wrong_gem_checksum", :raise_on_error => false
+ install_gemfile <<-G, artifice: "compact_index_wrong_gem_checksum", raise_on_error: false
source "#{source_uri}"
gem "rack"
G
- expect(exitstatus).to eq(19)
- expect(err).
- to include("Bundler cannot continue installing rack (1.0.0).").
- and include("The checksum for the downloaded `rack-1.0.0.gem` does not match the checksum given by the server.").
- and include("This means the contents of the downloaded gem is different from what was uploaded to the server, and could be a potential security issue.").
- and include("To resolve this issue:").
- and include("1. delete the downloaded gem located at: `#{default_bundle_path}/gems/rack-1.0.0/rack-1.0.0.gem`").
- and include("2. run `bundle install`").
- and include("If you wish to continue installing the downloaded gem, and are certain it does not pose a security issue despite the mismatching checksum, do the following:").
- and include("1. run `bundle config set --local disable_checksum_validation true` to turn off checksum verification").
- and include("2. run `bundle install`").
- and match(/\(More info: The expected SHA256 checksum was "#{"ab" * 22}", but the checksum for the downloaded gem was ".+?"\.\)/)
+ gem_path = if Bundler.feature_flag.global_gem_cache?
+ default_cache_path.dirname.join("cache", "gems", "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "rack-1.0.0.gem")
+ else
+ default_cache_path.dirname.join("rack-1.0.0.gem")
+ end
+
+ expect(exitstatus).to eq(37)
+ expect(err).to eq <<~E.strip
+ Bundler found mismatched checksums. This is a potential security risk.
+ rack (1.0.0) sha256=2222222222222222222222222222222222222222222222222222222222222222
+ from the API at http://localgemserver.test/
+ #{checksum_to_lock(gem_repo1, "rack", "1.0.0")}
+ from the gem at #{gem_path}
+
+ If you trust the API at http://localgemserver.test/, to resolve this issue you can:
+ 1. remove the gem at #{gem_path}
+ 2. run `bundle install`
+
+ To ignore checksum security warnings, disable checksum validation with
+ `bundle config set --local disable_checksum_validation true`
+ E
end
it "raises when the checksum is the wrong length" do
- install_gemfile <<-G, :artifice => "compact_index_wrong_gem_checksum", :env => { "BUNDLER_SPEC_RACK_CHECKSUM" => "checksum!", "DEBUG" => "1" }, :verbose => true, :raise_on_error => false
+ install_gemfile <<-G, artifice: "compact_index_wrong_gem_checksum", env: { "BUNDLER_SPEC_RACK_CHECKSUM" => "checksum!", "DEBUG" => "1" }, verbose: true, raise_on_error: false
source "#{source_uri}"
gem "rack"
G
- expect(exitstatus).to eq(5)
- expect(err).to include("The given checksum for rack-1.0.0 (\"checksum!\") is not a valid SHA256 hexdigest nor base64digest")
+ expect(exitstatus).to eq(14)
+ expect(err).to include('Invalid checksum for rack-0.9.1: "checksum!" is not a valid SHA256 hex or base64 digest')
end
it "does not raise when disable_checksum_validation is set" do
bundle "config set disable_checksum_validation true"
- install_gemfile <<-G, :artifice => "compact_index_wrong_gem_checksum"
+ install_gemfile <<-G, artifice: "compact_index_wrong_gem_checksum"
source "#{source_uri}"
gem "rack"
G
@@ -906,7 +1042,7 @@ The checksum of /versions does not match the checksum provided by the server! So
end
it "works when cache dir is world-writable" do
- install_gemfile <<-G, :artifice => "compact_index"
+ install_gemfile <<-G, artifice: "compact_index"
File.umask(0000)
source "#{source_uri}"
gem "rack"
@@ -914,11 +1050,11 @@ The checksum of /versions does not match the checksum provided by the server! So
end
it "doesn't explode when the API dependencies are wrong" do
- install_gemfile <<-G, :artifice => "compact_index_wrong_dependencies", :env => { "DEBUG" => "true" }, :raise_on_error => false
+ install_gemfile <<-G, artifice: "compact_index_wrong_dependencies", env: { "DEBUG" => "true" }, raise_on_error: false
source "#{source_uri}"
gem "rails"
G
- deps = [Gem::Dependency.new("rake", "= 13.0.1"),
+ deps = [Gem::Dependency.new("rake", "= #{rake_version}"),
Gem::Dependency.new("actionpack", "= 2.3.2"),
Gem::Dependency.new("activerecord", "= 2.3.2"),
Gem::Dependency.new("actionmailer", "= 2.3.2"),
@@ -931,7 +1067,7 @@ Running `bundle update rails` should fix the problem.
end
it "does not duplicate specs in the lockfile when updating and a dependency is not installed" do
- install_gemfile <<-G, :artifice => "compact_index"
+ install_gemfile <<-G, artifice: "compact_index"
source "#{file_uri_for(gem_repo1)}"
source "#{source_uri}" do
gem "rails"
@@ -939,7 +1075,8 @@ Running `bundle update rails` should fix the problem.
end
G
gem_command "uninstall activemerchant"
- bundle "update rails", :artifice => "compact_index"
- expect(lockfile.scan(/activemerchant \(/).size).to eq(1)
+ bundle "update rails", artifice: "compact_index"
+ count = lockfile.match?("CHECKSUMS") ? 2 : 1 # Once in the specs, and once in CHECKSUMS
+ expect(lockfile.scan(/activemerchant \(/).size).to eq(count)
end
end
diff --git a/spec/bundler/install/gems/dependency_api_spec.rb b/spec/bundler/install/gems/dependency_api_spec.rb
index a54f1db772..35468b3a1c 100644
--- a/spec/bundler/install/gems/dependency_api_spec.rb
+++ b/spec/bundler/install/gems/dependency_api_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe "gemcutter's dependency API" do
gem "rack"
G
- bundle :install, :artifice => "endpoint"
+ bundle :install, artifice: "endpoint"
expect(out).to include("Fetching gem metadata from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -21,7 +21,7 @@ RSpec.describe "gemcutter's dependency API" do
gem " sinatra"
G
- bundle :install, :artifice => "endpoint", :raise_on_error => false
+ bundle :install, artifice: "endpoint", raise_on_error: false
expect(err).to include("' sinatra' is not a valid gem name because it contains whitespace.")
end
@@ -31,7 +31,7 @@ RSpec.describe "gemcutter's dependency API" do
gem "rails"
G
- bundle :install, :artifice => "endpoint"
+ bundle :install, artifice: "endpoint"
expect(out).to include("Fetching gem metadata from #{source_uri}/...")
expect(the_bundle).to include_gems(
"rails 2.3.2",
@@ -49,7 +49,7 @@ RSpec.describe "gemcutter's dependency API" do
gem "net-sftp"
G
- bundle :install, :artifice => "endpoint"
+ bundle :install, artifice: "endpoint"
expect(the_bundle).to include_gems "net-sftp 1.1.1"
end
@@ -58,11 +58,11 @@ RSpec.describe "gemcutter's dependency API" do
source "#{source_uri}"
gem "rack"
G
- bundle :install, :artifice => "endpoint"
+ bundle :install, artifice: "endpoint"
bundle "config set --local deployment true"
bundle "config set --local path vendor/bundle"
- bundle :install, :artifice => "endpoint"
+ bundle :install, artifice: "endpoint"
expect(out).to include("Fetching gem metadata from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -80,7 +80,7 @@ RSpec.describe "gemcutter's dependency API" do
end
G
- bundle :install, :artifice => "endpoint"
+ bundle :install, artifice: "endpoint"
expect(the_bundle).to include_gems("rails 2.3.2")
end
@@ -96,10 +96,10 @@ RSpec.describe "gemcutter's dependency API" do
gem 'foo', :git => "#{file_uri_for(lib_path("foo-1.0"))}"
G
- bundle :install, :artifice => "endpoint"
+ bundle :install, artifice: "endpoint"
bundle "config set --local deployment true"
- bundle :install, :artifice => "endpoint"
+ bundle :install, artifice: "endpoint"
expect(the_bundle).to include_gems("rails 2.3.2")
end
@@ -111,9 +111,9 @@ RSpec.describe "gemcutter's dependency API" do
gem 'foo', :git => "#{file_uri_for(lib_path("foo-1.0"))}"
G
- bundle "install", :artifice => "endpoint"
+ bundle "install", artifice: "endpoint"
bundle "config set --local deployment true"
- bundle :install, :artifice => "endpoint"
+ bundle :install, artifice: "endpoint"
expect(the_bundle).to include_gems("foo 1.0")
end
@@ -134,7 +134,7 @@ RSpec.describe "gemcutter's dependency API" do
gem "rcov"
G
- bundle :install, :artifice => "windows", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+ bundle :install, artifice: "windows", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
expect(out).to include("Fetching source index from #{source_uri}")
expect(the_bundle).to include_gems "rcov 1.0.0"
end
@@ -150,7 +150,7 @@ RSpec.describe "gemcutter's dependency API" do
gem "rack"
gem "rails"
G
- bundle :install, :artifice => "endpoint_fallback"
+ bundle :install, artifice: "endpoint_fallback"
expect(out).to include("Fetching source index from #{source_uri}")
expect(the_bundle).to include_gems(
@@ -171,7 +171,7 @@ RSpec.describe "gemcutter's dependency API" do
gem "rack"
G
- bundle :install, :verbose => true, :artifice => "endpoint_marshal_fail"
+ bundle :install, verbose: true, artifice: "endpoint_marshal_fail"
expect(out).to include("could not fetch from the dependency API, trying the full index")
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -182,7 +182,7 @@ RSpec.describe "gemcutter's dependency API" do
gem "rack"
G
- bundle :install, :verbose => true, :artifice => "endpoint_api_forbidden"
+ bundle :install, verbose: true, artifice: "endpoint_api_forbidden"
expect(out).to include("Fetching source index from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -193,11 +193,11 @@ RSpec.describe "gemcutter's dependency API" do
gem "rack"
G
- bundle :install, :artifice => "endpoint_host_redirect"
+ bundle :install, artifice: "endpoint_host_redirect"
expect(the_bundle).to include_gems "rack 1.0.0"
end
- it "handles host redirects without Net::HTTP::Persistent" do
+ it "handles host redirects without Gem::Net::HTTP::Persistent" do
gemfile <<-G
source "#{source_uri}"
gem "rack"
@@ -216,7 +216,7 @@ RSpec.describe "gemcutter's dependency API" do
H
end
- bundle :install, :artifice => "endpoint_host_redirect", :requires => [lib_path("disable_net_http_persistent.rb")]
+ bundle :install, artifice: "endpoint_host_redirect", requires: [lib_path("disable_net_http_persistent.rb")]
expect(out).to_not match(/Too many redirects/)
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -227,7 +227,7 @@ RSpec.describe "gemcutter's dependency API" do
gem "rack"
G
- bundle :install, :artifice => "endpoint_redirect", :raise_on_error => false
+ bundle :install, artifice: "endpoint_redirect", raise_on_error: false
expect(err).to match(/Too many redirects/)
end
@@ -238,7 +238,7 @@ RSpec.describe "gemcutter's dependency API" do
gem "rack"
G
- bundle "install --full-index", :artifice => "endpoint"
+ bundle "install --full-index", artifice: "endpoint"
expect(out).to include("Fetching source index from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -249,13 +249,13 @@ RSpec.describe "gemcutter's dependency API" do
gem "rack"
G
- bundle "update --full-index", :artifice => "endpoint", :all => true
+ bundle "update --full-index", artifice: "endpoint", all: true
expect(out).to include("Fetching source index from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
end
- it "fetches again when more dependencies are found in subsequent sources", :bundler => "< 3" do
+ it "fetches again when more dependencies are found in subsequent sources", bundler: "< 3" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
@@ -269,7 +269,7 @@ RSpec.describe "gemcutter's dependency API" do
gem "back_deps"
G
- bundle :install, :artifice => "endpoint_extra"
+ bundle :install, artifice: "endpoint_extra"
expect(the_bundle).to include_gems "back_deps 1.0", "foo 1.0"
end
@@ -288,7 +288,7 @@ RSpec.describe "gemcutter's dependency API" do
end
G
- bundle :install, :artifice => "endpoint_extra"
+ bundle :install, artifice: "endpoint_extra"
expect(the_bundle).to include_gems "back_deps 1.0", "foo 1.0"
end
@@ -297,7 +297,7 @@ RSpec.describe "gemcutter's dependency API" do
source "#{source_uri}"
gem "rack", "1.0.0"
G
- bundle :install, :artifice => "endpoint_extra_api"
+ bundle :install, artifice: "endpoint_extra_api"
build_repo4 do
build_gem "rack", "1.2" do |s|
@@ -310,11 +310,11 @@ RSpec.describe "gemcutter's dependency API" do
source "#{source_uri}/extra"
gem "rack", "1.2"
G
- bundle :install, :artifice => "endpoint_extra_api"
+ bundle :install, artifice: "endpoint_extra_api"
expect(the_bundle).to include_gems "rack 1.2"
end
- it "considers all possible versions of dependencies from all api gem sources", :bundler => "< 3" do
+ it "considers all possible versions of dependencies from all api gem sources", bundler: "< 3" do
# In this scenario, the gem "somegem" only exists in repo4. It depends on specific version of activesupport that
# exists only in repo1. There happens also be a version of activesupport in repo4, but not the one that version 1.0.0
# of somegem wants. This test makes sure that bundler actually finds version 1.2.3 of active support in the other
@@ -332,7 +332,7 @@ RSpec.describe "gemcutter's dependency API" do
gem 'somegem', '1.0.0'
G
- bundle :install, :artifice => "endpoint_extra_api"
+ bundle :install, artifice: "endpoint_extra_api"
expect(the_bundle).to include_gems "somegem 1.0.0"
expect(the_bundle).to include_gems "activesupport 1.2.3"
@@ -353,13 +353,13 @@ RSpec.describe "gemcutter's dependency API" do
end
G
- bundle :install, :artifice => "endpoint_extra"
+ bundle :install, artifice: "endpoint_extra"
expect(out).to include("Fetching gem metadata from http://localgemserver.test/.")
expect(out).to include("Fetching source index from http://localgemserver.test/extra")
end
- it "does not fetch every spec when doing back deps", :bundler => "< 3" do
+ it "does not fetch every spec when doing back deps", bundler: "< 3" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
@@ -369,7 +369,7 @@ RSpec.describe "gemcutter's dependency API" do
FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
end
- install_gemfile <<-G, :artifice => "endpoint_extra_missing", :env => env_for_missing_prerelease_default_gem_activation
+ install_gemfile <<-G, artifice: "endpoint_extra_missing"
source "#{source_uri}"
source "#{source_uri}/extra"
gem "back_deps"
@@ -388,7 +388,7 @@ RSpec.describe "gemcutter's dependency API" do
FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
end
- install_gemfile <<-G, :artifice => "endpoint_extra_missing", :env => env_for_missing_prerelease_default_gem_activation
+ install_gemfile <<-G, artifice: "endpoint_extra_missing"
source "#{source_uri}"
source "#{source_uri}/extra" do
gem "back_deps"
@@ -398,7 +398,7 @@ RSpec.describe "gemcutter's dependency API" do
expect(the_bundle).to include_gems "back_deps 1.0"
end
- it "fetches again when more dependencies are found in subsequent sources using deployment mode", :bundler => "< 3" do
+ it "fetches again when more dependencies are found in subsequent sources using deployment mode", bundler: "< 3" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
@@ -412,9 +412,9 @@ RSpec.describe "gemcutter's dependency API" do
gem "back_deps"
G
- bundle :install, :artifice => "endpoint_extra"
+ bundle :install, artifice: "endpoint_extra"
bundle "config set --local deployment true"
- bundle :install, :artifice => "endpoint_extra"
+ bundle :install, artifice: "endpoint_extra"
expect(the_bundle).to include_gems "back_deps 1.0"
end
@@ -433,9 +433,9 @@ RSpec.describe "gemcutter's dependency API" do
end
G
- bundle :install, :artifice => "endpoint_extra"
+ bundle :install, artifice: "endpoint_extra"
bundle "config set --local deployment true"
- bundle "install", :artifice => "endpoint_extra"
+ bundle "install", artifice: "endpoint_extra"
expect(the_bundle).to include_gems "back_deps 1.0"
end
@@ -445,7 +445,7 @@ RSpec.describe "gemcutter's dependency API" do
build_gem "foo", "2.0"
end
- install_gemfile <<-G, :artifice => "endpoint", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }, :verbose => true
+ install_gemfile <<-G, artifice: "endpoint", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }, verbose: true
source "#{source_uri}"
gem "foo"
@@ -468,40 +468,40 @@ RSpec.describe "gemcutter's dependency API" do
gem "bundler_dep"
G
- bundle :install, :artifice => "endpoint", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+ bundle :install, artifice: "endpoint", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
expect(out).to include("Fetching gem metadata from #{source_uri}")
end
- it "installs the binstubs", :bundler => "< 3" do
+ it "installs the binstubs", bundler: "< 3" do
gemfile <<-G
source "#{source_uri}"
gem "rack"
G
- bundle "install --binstubs", :artifice => "endpoint"
+ bundle "install --binstubs", artifice: "endpoint"
gembin "rackup"
expect(out).to eq("1.0.0")
end
- it "installs the bins when using --path and uses autoclean", :bundler => "< 3" do
+ it "installs the bins when using --path and uses autoclean", bundler: "< 3" do
gemfile <<-G
source "#{source_uri}"
gem "rack"
G
- bundle "install --path vendor/bundle", :artifice => "endpoint"
+ bundle "install --path vendor/bundle", artifice: "endpoint"
expect(vendored_gems("bin/rackup")).to exist
end
- it "installs the bins when using --path and uses bundle clean", :bundler => "< 3" do
+ it "installs the bins when using --path and uses bundle clean", bundler: "< 3" do
gemfile <<-G
source "#{source_uri}"
gem "rack"
G
- bundle "install --path vendor/bundle --no-clean", :artifice => "endpoint"
+ bundle "install --path vendor/bundle --no-clean", artifice: "endpoint"
expect(vendored_gems("bin/rackup")).to exist
end
@@ -512,7 +512,7 @@ RSpec.describe "gemcutter's dependency API" do
gem 'rack-obama'
G
- bundle :install, :artifice => "endpoint"
+ bundle :install, artifice: "endpoint"
expect(out).to include("Post-install message from rack:")
end
@@ -522,7 +522,7 @@ RSpec.describe "gemcutter's dependency API" do
gem 'rack_middleware'
G
- bundle :install, :artifice => "endpoint"
+ bundle :install, artifice: "endpoint"
expect(out).to include("Post-install message from rack:")
expect(out).to include("Rack's post install message")
end
@@ -531,7 +531,7 @@ RSpec.describe "gemcutter's dependency API" do
let(:user) { "user" }
let(:password) { "pass" }
let(:basic_auth_source_uri) do
- uri = Bundler::URI.parse(source_uri)
+ uri = Gem::URI.parse(source_uri)
uri.user = user
uri.password = password
@@ -544,7 +544,7 @@ RSpec.describe "gemcutter's dependency API" do
gem "rack"
G
- bundle :install, :artifice => "endpoint_basic_authentication"
+ bundle :install, artifice: "endpoint_basic_authentication"
expect(out).not_to include("#{user}:#{password}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -555,7 +555,7 @@ RSpec.describe "gemcutter's dependency API" do
gem "rack"
G
- bundle :install, :verbose => true, :artifice => "endpoint_basic_authentication"
+ bundle :install, verbose: true, artifice: "endpoint_basic_authentication"
expect(out).not_to include("#{user}:#{password}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -566,7 +566,7 @@ RSpec.describe "gemcutter's dependency API" do
gem "rack"
G
- bundle :install, :artifice => "endpoint_marshal_fail_basic_authentication"
+ bundle :install, artifice: "endpoint_marshal_fail_basic_authentication"
expect(out).not_to include("#{user}:#{password}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -577,18 +577,18 @@ RSpec.describe "gemcutter's dependency API" do
gem "rack"
G
- bundle :install, :artifice => "endpoint_500", :raise_on_error => false
+ bundle :install, artifice: "endpoint_500", raise_on_error: false
expect(out).not_to include("#{user}:#{password}")
end
- it "strips http basic auth creds when warning about ambiguous sources", :bundler => "< 3" do
+ it "strips http basic auth creds when warning about ambiguous sources", bundler: "< 3" do
gemfile <<-G
source "#{basic_auth_source_uri}"
source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
- bundle :install, :artifice => "endpoint_basic_authentication"
+ bundle :install, artifice: "endpoint_basic_authentication"
expect(err).to include("Warning: the gem 'rack' was found in multiple sources.")
expect(err).not_to include("#{user}:#{password}")
expect(the_bundle).to include_gems "rack 1.0.0"
@@ -600,7 +600,7 @@ RSpec.describe "gemcutter's dependency API" do
gem "rack"
G
- bundle :install, :artifice => "endpoint_creds_diff_host"
+ bundle :install, artifice: "endpoint_creds_diff_host"
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -613,7 +613,7 @@ RSpec.describe "gemcutter's dependency API" do
end
it "reads authentication details from a valid ENV variable" do
- bundle :install, :artifice => "endpoint_strict_basic_authentication", :env => { "BUNDLE_LOCAL___GEMSERVER__TEST" => "#{user}:#{password}" }
+ bundle :install, artifice: "endpoint_strict_basic_authentication", env: { "BUNDLE_LOCAL___GEMSERVER__TEST" => "#{user}:#{password}" }
expect(out).to include("Fetching gem metadata from http://local-gemserver.test")
expect(the_bundle).to include_gems "rack 1.0.0"
@@ -631,7 +631,7 @@ RSpec.describe "gemcutter's dependency API" do
it "reads authentication details by host name from bundle config" do
bundle "config set #{source_hostname} #{user}:#{password}"
- bundle :install, :artifice => "endpoint_strict_basic_authentication"
+ bundle :install, artifice: "endpoint_strict_basic_authentication"
expect(out).to include("Fetching gem metadata from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
@@ -641,7 +641,7 @@ RSpec.describe "gemcutter's dependency API" do
# The trailing slash is necessary here; Fetcher canonicalizes the URI.
bundle "config set #{source_uri}/ #{user}:#{password}"
- bundle :install, :artifice => "endpoint_strict_basic_authentication"
+ bundle :install, artifice: "endpoint_strict_basic_authentication"
expect(out).to include("Fetching gem metadata from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
@@ -649,7 +649,7 @@ RSpec.describe "gemcutter's dependency API" do
it "should use the API" do
bundle "config set #{source_hostname} #{user}:#{password}"
- bundle :install, :artifice => "endpoint_strict_basic_authentication"
+ bundle :install, artifice: "endpoint_strict_basic_authentication"
expect(out).to include("Fetching gem metadata from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -662,19 +662,19 @@ RSpec.describe "gemcutter's dependency API" do
bundle "config set #{source_hostname} otheruser:wrong"
- bundle :install, :artifice => "endpoint_strict_basic_authentication"
+ bundle :install, artifice: "endpoint_strict_basic_authentication"
expect(the_bundle).to include_gems "rack 1.0.0"
end
it "shows instructions if auth is not provided for the source" do
- bundle :install, :artifice => "endpoint_strict_basic_authentication", :raise_on_error => false
+ bundle :install, artifice: "endpoint_strict_basic_authentication", raise_on_error: false
expect(err).to include("bundle config set --global #{source_hostname} username:password")
end
it "fails if authentication has already been provided, but failed" do
bundle "config set #{source_hostname} #{user}:wrong"
- bundle :install, :artifice => "endpoint_strict_basic_authentication", :raise_on_error => false
+ bundle :install, artifice: "endpoint_strict_basic_authentication", raise_on_error: false
expect(err).to include("Bad username or password")
end
end
@@ -688,7 +688,7 @@ RSpec.describe "gemcutter's dependency API" do
gem "rack"
G
- bundle :install, :artifice => "endpoint_basic_authentication"
+ bundle :install, artifice: "endpoint_basic_authentication"
expect(the_bundle).to include_gems "rack 1.0.0"
end
end
@@ -713,7 +713,7 @@ RSpec.describe "gemcutter's dependency API" do
gem "rack"
G
- bundle :install, :env => { "RUBYOPT" => opt_add("-I#{bundled_app("broken_ssl")}", ENV["RUBYOPT"]) }, :raise_on_error => false
+ bundle :install, env: { "RUBYOPT" => opt_add("-I#{bundled_app("broken_ssl")}", ENV["RUBYOPT"]) }, raise_on_error: false
expect(err).to include("OpenSSL")
end
end
@@ -723,7 +723,7 @@ RSpec.describe "gemcutter's dependency API" do
# Install a monkeypatch that reproduces the effects of openssl raising
# a certificate validation error when RubyGems tries to connect.
gemfile <<-G
- class Net::HTTP
+ class Gem::Net::HTTP
def start
raise OpenSSL::SSL::SSLError, "certificate verify failed"
end
@@ -733,7 +733,7 @@ RSpec.describe "gemcutter's dependency API" do
gem "rack"
G
- bundle :install, :raise_on_error => false
+ bundle :install, raise_on_error: false
expect(err).to match(/could not verify the SSL certificate/i)
end
end
@@ -741,7 +741,7 @@ RSpec.describe "gemcutter's dependency API" do
context ".gemrc with sources is present" do
it "uses other sources declared in the Gemfile" do
File.open(home(".gemrc"), "w") do |file|
- file.puts({ :sources => ["https://rubygems.org"] }.to_yaml)
+ file.puts({ sources: ["https://rubygems.org"] }.to_yaml)
end
begin
@@ -750,7 +750,7 @@ RSpec.describe "gemcutter's dependency API" do
gem 'rack'
G
- bundle "install", :artifice => "endpoint_marshal_fail"
+ bundle "install", artifice: "endpoint_marshal_fail"
ensure
home(".gemrc").rmtree
end
diff --git a/spec/bundler/install/gems/flex_spec.rb b/spec/bundler/install/gems/flex_spec.rb
index ddb9480343..8ef3984975 100644
--- a/spec/bundler/install/gems/flex_spec.rb
+++ b/spec/bundler/install/gems/flex_spec.rb
@@ -183,8 +183,8 @@ RSpec.describe "bundle flex_install" do
end
it "does not install gems whose dependencies are not met" do
- bundle :install, :raise_on_error => false
- ruby <<-RUBY, :raise_on_error => false
+ bundle :install, raise_on_error: false
+ ruby <<-RUBY, raise_on_error: false
require 'bundler/setup'
RUBY
expect(err).to match(/could not find gem 'rack-obama/i)
@@ -197,13 +197,13 @@ RSpec.describe "bundle flex_install" do
Could not find compatible versions
Because rack-obama >= 2.0 depends on rack = 1.2
- and rack = 1.2 could not be found in rubygems repository #{file_uri_for(gem_repo2)}/ or installed locally,
+ and rack = 1.2 could not be found in rubygems repository #{file_uri_for(gem_repo2)}/, cached gems or installed locally,
rack-obama >= 2.0 cannot be used.
So, because Gemfile depends on rack-obama = 2.0,
version solving has failed.
E
- bundle :install, :retry => 0, :raise_on_error => false
+ bundle :install, retry: 0, raise_on_error: false
expect(err).to end_with(nice_error)
end
@@ -216,7 +216,7 @@ RSpec.describe "bundle flex_install" do
rack-obama (= 2.0)
E
- bundle "update rack_middleware", :retry => 0, :raise_on_error => false
+ bundle "update rack_middleware", retry: 0, raise_on_error: false
expect(err).not_to end_with(bad_error)
end
end
@@ -245,7 +245,7 @@ RSpec.describe "bundle flex_install" do
end
it "discards the conflicting lockfile information and resolves properly" do
- bundle :update, :raise_on_error => false, :all => true
+ bundle :update, raise_on_error: false, all: true
expect(err).to be_empty
end
end
@@ -268,6 +268,11 @@ RSpec.describe "bundle flex_install" do
it "should work when you install" do
bundle "install"
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo1, "rack", "0.9.1"
+ c.checksum gem_repo1, "rack-obama", "1.0"
+ end
+
expect(lockfile).to eq <<~L
GEM
remote: #{file_uri_for(gem_repo1)}/
@@ -282,7 +287,7 @@ RSpec.describe "bundle flex_install" do
DEPENDENCIES
rack (= 0.9.1)
rack-obama
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -308,6 +313,10 @@ RSpec.describe "bundle flex_install" do
gem "rack"
G
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo1, "rack", "1.0.0"
+ end
+
expect(lockfile).to eq <<~L
GEM
remote: #{file_uri_for(gem_repo1)}/
@@ -323,7 +332,7 @@ RSpec.describe "bundle flex_install" do
DEPENDENCIES
rack
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
diff --git a/spec/bundler/install/gems/native_extensions_spec.rb b/spec/bundler/install/gems/native_extensions_spec.rb
index 5c18d2cc51..907778a384 100644
--- a/spec/bundler/install/gems/native_extensions_spec.rb
+++ b/spec/bundler/install/gems/native_extensions_spec.rb
@@ -7,7 +7,6 @@ RSpec.describe "installing a gem with native extensions" do
s.extensions = ["ext/extconf.rb"]
s.write "ext/extconf.rb", <<-E
require "mkmf"
- $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"]
name = "c_extension_bundle"
dir_config(name)
raise "OMG" unless with_config("c_extension") == "hello"
@@ -52,7 +51,6 @@ RSpec.describe "installing a gem with native extensions" do
s.extensions = ["ext/extconf.rb"]
s.write "ext/extconf.rb", <<-E
require "mkmf"
- $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"]
name = "c_extension_bundle"
dir_config(name)
raise "OMG" unless with_config("c_extension") == "hello"
@@ -93,11 +91,10 @@ RSpec.describe "installing a gem with native extensions" do
it "installs correctly from git when multiple gems with extensions share one repository" do
build_repo2 do
["one", "two"].each do |n|
- build_lib "c_extension_#{n}", "1.0", :path => lib_path("gems/c_extension_#{n}") do |s|
+ build_lib "c_extension_#{n}", "1.0", path: lib_path("gems/c_extension_#{n}") do |s|
s.extensions = ["ext/extconf.rb"]
s.write "ext/extconf.rb", <<-E
require "mkmf"
- $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"]
name = "c_extension_bundle_#{n}"
dir_config(name)
raise "OMG" unless with_config("c_extension_#{n}") == "#{n}"
@@ -122,7 +119,7 @@ RSpec.describe "installing a gem with native extensions" do
C
end
end
- build_git "gems", :path => lib_path("gems"), :gemspec => false
+ build_git "gems", path: lib_path("gems"), gemspec: false
end
bundle "config set build.c_extension_one --with-c_extension_one=one"
@@ -150,7 +147,6 @@ RSpec.describe "installing a gem with native extensions" do
s.extensions = ["ext/extconf.rb"]
s.write "ext/extconf.rb", <<-E
require "mkmf"
- $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"]
name = "c_extension_bundle"
dir_config(name)
raise "OMG" unless with_config("c_extension") == "hello" && with_config("c_extension_bundle-dir") == "hola"
diff --git a/spec/bundler/install/gems/resolving_spec.rb b/spec/bundler/install/gems/resolving_spec.rb
index fb6dda2f88..c5f9c4a3d3 100644
--- a/spec/bundler/install/gems/resolving_spec.rb
+++ b/spec/bundler/install/gems/resolving_spec.rb
@@ -106,7 +106,7 @@ RSpec.describe "bundle install with install-time dependencies" do
path = "#{gem_repo2}/#{Gem::MARSHAL_SPEC_DIR}/actionpack-2.3.2.gemspec.rz"
spec = Marshal.load(Bundler.rubygems.inflate(File.binread(path)))
spec.dependencies.each do |d|
- d.instance_variable_set(:@type, :fail)
+ d.instance_variable_set(:@type, "fail")
end
File.open(path, "wb") do |f|
f.write Gem.deflate(Marshal.dump(spec))
@@ -131,7 +131,7 @@ RSpec.describe "bundle install with install-time dependencies" do
end
it "installs plugins depended on by other plugins" do
- install_gemfile <<-G, :env => { "DEBUG" => "1" }
+ install_gemfile <<-G, env: { "DEBUG" => "1" }
source "#{file_uri_for(gem_repo2)}"
gem "net_a"
G
@@ -140,7 +140,7 @@ RSpec.describe "bundle install with install-time dependencies" do
end
it "installs multiple levels of dependencies" do
- install_gemfile <<-G, :env => { "DEBUG" => "1" }
+ install_gemfile <<-G, env: { "DEBUG" => "1" }
source "#{file_uri_for(gem_repo2)}"
gem "net_c"
gem "net_e"
@@ -157,7 +157,7 @@ RSpec.describe "bundle install with install-time dependencies" do
gem "net_e"
G
- bundle :install, :env => { "BUNDLER_DEBUG_RESOLVER" => "1", "DEBUG" => "1" }
+ bundle :install, env: { "BUNDLER_DEBUG_RESOLVER" => "1", "DEBUG" => "1" }
expect(out).to include("Resolving dependencies...")
end
@@ -171,7 +171,7 @@ RSpec.describe "bundle install with install-time dependencies" do
gem "net_e"
G
- bundle :install, :env => { "DEBUG_RESOLVER" => "1", "DEBUG" => "1" }
+ bundle :install, env: { "DEBUG_RESOLVER" => "1", "DEBUG" => "1" }
expect(out).to include("Resolving dependencies...")
end
@@ -185,7 +185,7 @@ RSpec.describe "bundle install with install-time dependencies" do
gem "net_e"
G
- bundle :install, :env => { "DEBUG_RESOLVER_TREE" => "1", "DEBUG" => "1" }
+ bundle :install, env: { "DEBUG_RESOLVER_TREE" => "1", "DEBUG" => "1" }
expect(out).to include(" net_b").
and include("Resolving dependencies...").
@@ -208,7 +208,7 @@ RSpec.describe "bundle install with install-time dependencies" do
end
end
- install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+ install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
ruby "#{Gem.ruby_version}"
source "http://localgemserver.test/"
gem 'rack'
@@ -229,7 +229,7 @@ RSpec.describe "bundle install with install-time dependencies" do
end
end
- install_gemfile <<-G, :artifice => "endpoint", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+ install_gemfile <<-G, artifice: "endpoint", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
ruby "#{Gem.ruby_version}"
source "http://localgemserver.test/"
gem 'rack'
@@ -256,6 +256,10 @@ RSpec.describe "bundle install with install-time dependencies" do
gem 'parallel_tests'
G
+ checksums = checksums_section do |c|
+ c.checksum gem_repo2, "parallel_tests", "3.8.0"
+ end
+
lockfile <<~L
GEM
remote: http://localgemserver.test/
@@ -267,14 +271,18 @@ RSpec.describe "bundle install with install-time dependencies" do
DEPENDENCIES
parallel_tests
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
end
it "automatically updates lockfile to use the older version" do
- bundle "install --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+ bundle "install --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo2, "parallel_tests", "3.7.0"
+ end
expect(lockfile).to eq <<~L
GEM
@@ -287,7 +295,7 @@ RSpec.describe "bundle install with install-time dependencies" do
DEPENDENCIES
parallel_tests
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -295,7 +303,7 @@ RSpec.describe "bundle install with install-time dependencies" do
it "gives a meaningful error if we're in frozen mode" do
expect do
- bundle "install --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s, "BUNDLE_FROZEN" => "true" }, :raise_on_error => false
+ bundle "install --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s, "BUNDLE_FROZEN" => "true" }, raise_on_error: false
end.not_to change { lockfile }
expect(err).to include("parallel_tests-3.8.0 requires ruby version >= #{next_ruby_minor}")
@@ -332,6 +340,11 @@ RSpec.describe "bundle install with install-time dependencies" do
gem 'rubocop'
G
+ checksums = checksums_section do |c|
+ c.checksum gem_repo2, "rubocop", "1.35.0"
+ c.checksum gem_repo2, "rubocop-ast", "1.21.0"
+ end
+
lockfile <<~L
GEM
remote: http://localgemserver.test/
@@ -345,14 +358,19 @@ RSpec.describe "bundle install with install-time dependencies" do
DEPENDENCIES
parallel_tests
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
end
it "automatically updates lockfile to use the older compatible versions" do
- bundle "install --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+ bundle "install --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo2, "rubocop", "1.28.2"
+ c.checksum gem_repo2, "rubocop-ast", "1.17.0"
+ end
expect(lockfile).to eq <<~L
GEM
@@ -367,7 +385,7 @@ RSpec.describe "bundle install with install-time dependencies" do
DEPENDENCIES
rubocop
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -412,11 +430,11 @@ RSpec.describe "bundle install with install-time dependencies" do
it "raises a proper error" do
simulate_platform "aarch64-linux" do
- bundle "install", :raise_on_error => false
+ bundle "install", raise_on_error: false
end
- nice_error = strip_whitespace(<<-E).strip
- Could not find gem 'sorbet-static (= 0.5.10554)' with platforms 'arm64-darwin-21', 'aarch64-linux' in rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally.
+ nice_error = <<~E.strip
+ Could not find gems matching 'sorbet-static (= 0.5.10554)' valid for all resolution platforms (arm64-darwin-21, aarch64-linux) in rubygems repository #{file_uri_for(gem_repo4)}/, cached gems or installed locally.
The source contains the following gems matching 'sorbet-static (= 0.5.10554)':
* sorbet-static-0.5.10554-universal-darwin-21
@@ -425,6 +443,62 @@ RSpec.describe "bundle install with install-time dependencies" do
end
end
+ context "when adding a new gem that does not resolve under all locked platforms" do
+ before do
+ simulate_platform "x86_64-linux" do
+ build_repo4 do
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "x86_64-linux"
+ end
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "arm-linux"
+ end
+
+ build_gem "sorbet-static", "0.5.10696" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.14.0-arm-linux)
+ nokogiri (1.14.0-x86_64-linux)
+
+ PLATFORMS
+ arm-linux
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "nokogiri"
+ gem "sorbet-static"
+ G
+
+ bundle "lock", raise_on_error: false
+ end
+ end
+
+ it "raises a proper error" do
+ nice_error = <<~E.strip
+ Could not find gems matching 'sorbet-static' valid for all resolution platforms (arm-linux, x86_64-linux) in rubygems repository #{file_uri_for(gem_repo4)}/, cached gems or installed locally.
+
+ The source contains the following gems matching 'sorbet-static':
+ * sorbet-static-0.5.10696-x86_64-linux
+ E
+ expect(err).to end_with(nice_error)
+ end
+ end
+
it "gives a meaningful error on ruby version mismatches between dependencies" do
build_repo4 do
build_gem "requires-old-ruby" do |s|
@@ -432,13 +506,13 @@ RSpec.describe "bundle install with install-time dependencies" do
end
end
- build_lib("foo", :path => bundled_app) do |s|
+ build_lib("foo", path: bundled_app) do |s|
s.required_ruby_version = ">= #{Gem.ruby_version}"
s.add_dependency "requires-old-ruby"
end
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo4)}"
gemspec
G
@@ -464,7 +538,7 @@ RSpec.describe "bundle install with install-time dependencies" do
build_gem "foo1", "1.0"
end
- install_gemfile <<-G, :artifice => "compact_index_rate_limited", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ install_gemfile <<-G, artifice: "compact_index_rate_limited", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
ruby "#{Gem.ruby_version}"
source "http://localgemserver.test/"
gem 'rack'
@@ -488,7 +562,7 @@ RSpec.describe "bundle install with install-time dependencies" do
end
simulate_platform x86_mingw32 do
- install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
ruby "#{Gem.ruby_version}"
source "http://localgemserver.test/"
gem 'rack'
@@ -514,14 +588,14 @@ RSpec.describe "bundle install with install-time dependencies" do
let(:error_message_requirement) { "= #{Gem.ruby_version}" }
it "raises a proper error that mentions the current Ruby version during resolution" do
- install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }, :raise_on_error => false
+ install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }, raise_on_error: false
source "http://localgemserver.test/"
gem 'require_ruby'
G
expect(out).to_not include("Gem::InstallError: require_ruby requires Ruby version > 9000")
- nice_error = strip_whitespace(<<-E).strip
+ nice_error = <<~E.strip
Could not find compatible versions
Because every version of require_ruby depends on Ruby > 9000
@@ -535,7 +609,7 @@ RSpec.describe "bundle install with install-time dependencies" do
shared_examples_for "ruby version conflicts" do
it "raises an error during resolution" do
- install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }, :raise_on_error => false
+ install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }, raise_on_error: false
source "http://localgemserver.test/"
ruby #{ruby_requirement}
gem 'require_ruby'
@@ -543,7 +617,7 @@ RSpec.describe "bundle install with install-time dependencies" do
expect(out).to_not include("Gem::InstallError: require_ruby requires Ruby version > 9000")
- nice_error = strip_whitespace(<<-E).strip
+ nice_error = <<~E.strip
Could not find compatible versions
Because every version of require_ruby depends on Ruby > 9000
@@ -581,13 +655,13 @@ RSpec.describe "bundle install with install-time dependencies" do
end
end
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo2)}"
gem 'require_rubygems'
G
expect(err).to_not include("Gem::InstallError: require_rubygems requires RubyGems version > 9000")
- nice_error = strip_whitespace(<<-E).strip
+ nice_error = <<~E.strip
Because every version of require_rubygems depends on RubyGems > 9000
and Gemfile depends on require_rubygems >= 0,
RubyGems > 9000 is required.
diff --git a/spec/bundler/install/gems/standalone_spec.rb b/spec/bundler/install/gems/standalone_spec.rb
index 4d08752256..46cab2dfeb 100644
--- a/spec/bundler/install/gems/standalone_spec.rb
+++ b/spec/bundler/install/gems/standalone_spec.rb
@@ -97,7 +97,23 @@ RSpec.shared_examples "bundle install --standalone" do
testrb << "\nrequire \"#{k}\""
testrb << "\nputs #{k.upcase}"
end
- ruby testrb, :dir => "#{bundled_app}2"
+ ruby testrb, dir: "#{bundled_app}2"
+
+ expect(out).to eq(expected_gems.values.join("\n"))
+ end
+
+ it "skips activating gems" do
+ testrb = String.new <<-RUBY
+ $:.unshift File.expand_path("bundle")
+ require "bundler/setup"
+
+ gem "do_not_activate_me"
+ RUBY
+ expected_gems.each do |k, _|
+ testrb << "\nrequire \"#{k}\""
+ testrb << "\nputs #{k.upcase}"
+ end
+ sys_exec %(#{Gem.ruby} -w -e #{testrb.shellescape})
expect(out).to eq(expected_gems.values.join("\n"))
end
@@ -110,7 +126,7 @@ RSpec.shared_examples "bundle install --standalone" do
gem "rails"
G
bundle "config set --local path #{bundled_app("bundle")}"
- bundle :install, :standalone => true, :dir => cwd
+ bundle :install, standalone: true, dir: cwd
end
let(:expected_gems) do
@@ -125,21 +141,18 @@ RSpec.shared_examples "bundle install --standalone" do
describe "with default gems and a lockfile", :ruby_repo do
before do
- skip "does not work on rubygems versions where `--install_dir` doesn't respect --default" unless Gem::Installer.for_spec(loaded_gemspec, :install_dir => "/foo").default_spec_file == "/foo/specifications/default/bundler-#{Bundler::VERSION}.gemspec" # Since rubygems 3.2.0.rc.2
- skip "does not work on old rubies because the realworld gems that need to be installed don't support them" if RUBY_VERSION < "2.7.0"
-
realworld_system_gems "tsort --version 0.1.0"
- necessary_system_gems = ["optparse --version 0.1.1", "psych --version 3.3.2", "logger --version 1.4.3", "etc --version 1.2.0", "stringio --version 3.0.1"]
+ necessary_system_gems = ["optparse --version 0.1.1", "psych --version 3.3.2", "logger --version 1.4.3", "etc --version 1.2.0", "stringio --version 3.1.0"]
necessary_system_gems += ["shellwords --version 0.1.0", "base64 --version 0.1.0", "resolv --version 0.2.1"] if Gem.rubygems_version < Gem::Version.new("3.3.a")
necessary_system_gems += ["yaml --version 0.1.1"] if Gem.rubygems_version < Gem::Version.new("3.4.a")
- realworld_system_gems(*necessary_system_gems, :path => scoped_gem_path(bundled_app("bundle")))
+ realworld_system_gems(*necessary_system_gems, path: scoped_gem_path(bundled_app("bundle")))
- build_gem "foo", "1.0.0", :to_system => true, :default => true do |s|
+ build_gem "foo", "1.0.0", to_system: true, default: true do |s|
s.add_dependency "bar"
end
- build_gem "bar", "1.0.0", :to_system => true, :default => true
+ build_gem "bar", "1.0.0", to_system: true, default: true
build_repo4 do
build_gem "foo", "1.0.0" do |s|
@@ -154,12 +167,12 @@ RSpec.shared_examples "bundle install --standalone" do
gem "foo"
G
- bundle "lock", :dir => cwd, :artifice => "compact_index"
+ bundle "lock", dir: cwd, artifice: "compact_index"
end
it "works and points to the vendored copies, not to the default copies", :realworld do
bundle "config set --local path #{bundled_app("bundle")}"
- bundle :install, :standalone => true, :dir => cwd, :artifice => "compact_index", :env => { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s }
+ bundle :install, standalone: true, dir: cwd, artifice: "compact_index", env: { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s }
load_path_lines = bundled_app("bundle/bundler/setup.rb").read.split("\n").select {|line| line.start_with?("$:.unshift") }
@@ -172,7 +185,7 @@ RSpec.shared_examples "bundle install --standalone" do
describe "with Gemfiles using absolute path sources and resulting bundle moved to a folder hierarchy with different nesting" do
before do
- build_lib "minitest", "1.0.0", :path => lib_path("minitest")
+ build_lib "minitest", "1.0.0", path: lib_path("minitest")
Dir.mkdir bundled_app("app")
@@ -181,14 +194,14 @@ RSpec.shared_examples "bundle install --standalone" do
gem "minitest", :path => "#{lib_path("minitest")}"
G
- bundle "install", :standalone => true, :dir => bundled_app("app")
+ bundle "install", standalone: true, dir: bundled_app("app")
Dir.mkdir tmp("one_more_level")
FileUtils.mv bundled_app, tmp("one_more_level")
end
it "also works" do
- ruby <<-RUBY, :dir => tmp("one_more_level/bundled_app/app")
+ ruby <<-RUBY, dir: tmp("one_more_level/bundled_app/app")
require "./bundle/bundler/setup"
require "minitest"
@@ -204,20 +217,20 @@ RSpec.shared_examples "bundle install --standalone" do
before do
FileUtils.mkdir_p bundled_app("app/vendor")
- build_lib "minitest", "1.0.0", :path => bundled_app("app/vendor/minitest")
+ build_lib "minitest", "1.0.0", path: bundled_app("app/vendor/minitest")
gemfile bundled_app("app/Gemfile"), <<-G
source "#{file_uri_for(gem_repo1)}"
gem "minitest", :path => "vendor/minitest"
G
- bundle "install", :standalone => true, :dir => bundled_app("app")
+ bundle "install", standalone: true, dir: bundled_app("app")
FileUtils.mv(bundled_app("app"), bundled_app2("app"))
end
it "also works" do
- ruby <<-RUBY, :dir => bundled_app2("app")
+ ruby <<-RUBY, dir: bundled_app2("app")
require "./bundle/bundler/setup"
require "minitest"
@@ -232,7 +245,7 @@ RSpec.shared_examples "bundle install --standalone" do
describe "with gems with native extension" do
before do
bundle "config set --local path #{bundled_app("bundle")}"
- install_gemfile <<-G, :standalone => true, :dir => cwd
+ install_gemfile <<-G, standalone: true, dir: cwd
source "#{file_uri_for(gem_repo1)}"
gem "very_simple_binary"
G
@@ -252,7 +265,7 @@ RSpec.shared_examples "bundle install --standalone" do
describe "with gem that has an invalid gemspec" do
before do
- build_git "bar", :gemspec => false do |s|
+ build_git "bar", gemspec: false do |s|
s.write "lib/bar/version.rb", %(BAR_VERSION = '1.0')
s.write "bar.gemspec", <<-G
lib = File.expand_path('lib/', __dir__)
@@ -270,7 +283,7 @@ RSpec.shared_examples "bundle install --standalone" do
G
end
bundle "config set --local path #{bundled_app("bundle")}"
- install_gemfile <<-G, :standalone => true, :dir => cwd, :raise_on_error => false
+ install_gemfile <<-G, standalone: true, dir: cwd, raise_on_error: false
source "#{file_uri_for(gem_repo1)}"
gem "bar", :git => "#{lib_path("bar-1.0")}"
G
@@ -292,7 +305,7 @@ RSpec.shared_examples "bundle install --standalone" do
gem "devise", :git => "#{lib_path("devise-1.0")}"
G
bundle "config set --local path #{bundled_app("bundle")}"
- bundle :install, :standalone => true, :dir => cwd
+ bundle :install, standalone: true, dir: cwd
end
let(:expected_gems) do
@@ -320,7 +333,7 @@ RSpec.shared_examples "bundle install --standalone" do
end
G
bundle "config set --local path #{bundled_app("bundle")}"
- bundle :install, :standalone => true, :dir => cwd
+ bundle :install, standalone: true, dir: cwd
end
let(:expected_gems) do
@@ -334,7 +347,7 @@ RSpec.shared_examples "bundle install --standalone" do
it "allows creating a standalone file with limited groups" do
bundle "config set --local path #{bundled_app("bundle")}"
- bundle :install, :standalone => "default", :dir => cwd
+ bundle :install, standalone: "default", dir: cwd
load_error_ruby <<-RUBY, "spec"
$:.unshift File.expand_path("bundle")
@@ -352,7 +365,7 @@ RSpec.shared_examples "bundle install --standalone" do
it "allows `without` configuration to limit the groups used in a standalone" do
bundle "config set --local path #{bundled_app("bundle")}"
bundle "config set --local without test"
- bundle :install, :standalone => true, :dir => cwd
+ bundle :install, standalone: true, dir: cwd
load_error_ruby <<-RUBY, "spec"
$:.unshift File.expand_path("bundle")
@@ -369,7 +382,7 @@ RSpec.shared_examples "bundle install --standalone" do
it "allows `path` configuration to change the location of the standalone bundle" do
bundle "config set --local path path/to/bundle"
- bundle "install", :standalone => true, :dir => cwd
+ bundle "install", standalone: true, dir: cwd
ruby <<-RUBY
$:.unshift File.expand_path("path/to/bundle")
@@ -384,9 +397,9 @@ RSpec.shared_examples "bundle install --standalone" do
it "allows `without` to limit the groups used in a standalone" do
bundle "config set --local without test"
- bundle :install, :dir => cwd
+ bundle :install, dir: cwd
bundle "config set --local path #{bundled_app("bundle")}"
- bundle :install, :standalone => true, :dir => cwd
+ bundle :install, standalone: true, dir: cwd
load_error_ruby <<-RUBY, "spec"
$:.unshift File.expand_path("bundle")
@@ -412,7 +425,7 @@ RSpec.shared_examples "bundle install --standalone" do
gem "rails"
G
bundle "config set --local path #{bundled_app("bundle")}"
- bundle :install, :standalone => true, :artifice => "endpoint", :dir => cwd
+ bundle :install, standalone: true, artifice: "endpoint", dir: cwd
end
let(:expected_gems) do
@@ -426,14 +439,14 @@ RSpec.shared_examples "bundle install --standalone" do
end
end
- describe "with --binstubs", :bundler => "< 3" do
+ describe "with --binstubs", bundler: "< 3" do
before do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rails"
G
bundle "config set --local path #{bundled_app("bundle")}"
- bundle :install, :standalone => true, :binstubs => true, :dir => cwd
+ bundle :install, standalone: true, binstubs: true, dir: cwd
end
let(:expected_gems) do
@@ -451,7 +464,7 @@ RSpec.shared_examples "bundle install --standalone" do
it "creates stubs that can be executed from anywhere" do
require "tmpdir"
- sys_exec(%(#{bundled_app("bin/rails")} -v), :dir => Dir.tmpdir)
+ sys_exec(%(#{bundled_app("bin/rails")} -v), dir: Dir.tmpdir)
expect(out).to eq("2.3.2")
end
@@ -493,7 +506,7 @@ RSpec.describe "bundle install --standalone --local" do
gem "rack"
G
- system_gems "rack-1.0.0", :path => default_bundle_path
+ system_gems "rack-1.0.0", path: default_bundle_path
end
it "generates script pointing to system gems" do
diff --git a/spec/bundler/install/gemspecs_spec.rb b/spec/bundler/install/gemspecs_spec.rb
index 7b58ea9839..51aa0ed14f 100644
--- a/spec/bundler/install/gemspecs_spec.rb
+++ b/spec/bundler/install/gemspecs_spec.rb
@@ -4,7 +4,7 @@ RSpec.describe "bundle install" do
describe "when a gem has a YAML gemspec" do
before :each do
build_repo2 do
- build_gem "yaml_spec", :gemspec => :yaml
+ build_gem "yaml_spec", gemspec: :yaml
end
end
@@ -18,7 +18,7 @@ RSpec.describe "bundle install" do
end
it "still installs correctly when using path" do
- build_lib "yaml_spec", :gemspec => :yaml
+ build_lib "yaml_spec", gemspec: :yaml
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -34,7 +34,7 @@ RSpec.describe "bundle install" do
gem 'rack'
G
- system_gems "rack-1.0.0", :path => default_bundle_path
+ system_gems "rack-1.0.0", path: default_bundle_path
FileUtils.mkdir_p "#{default_bundle_path}/specifications"
File.open("#{default_bundle_path}/specifications/rack-1.0.0.gemspec", "w+") do |f|
@@ -45,7 +45,7 @@ RSpec.describe "bundle install" do
end
f.write spec.to_ruby
end
- bundle :install, :artifice => "endpoint_marshal_fail" # force gemspec load
+ bundle :install, artifice: "endpoint_marshal_fail" # force gemspec load
expect(the_bundle).to include_gems "rack 1.0.0", "activesupport 2.3.2"
end
@@ -59,7 +59,7 @@ RSpec.describe "bundle install" do
end
G
- install_gemfile <<-G, :env => { "LANG" => "C" }
+ install_gemfile <<-G, env: { "LANG" => "C" }
source "#{file_uri_for(gem_repo1)}"
gemspec
G
@@ -95,8 +95,8 @@ RSpec.describe "bundle install" do
context "when ruby version is specified in gemspec and gemfile" do
it "installs when patch level is not specified and the version matches",
- :if => RUBY_PATCHLEVEL >= 0 do
- build_lib("foo", :path => bundled_app) do |s|
+ if: RUBY_PATCHLEVEL >= 0 do
+ build_lib("foo", path: bundled_app) do |s|
s.required_ruby_version = "~> #{RUBY_VERSION}.0"
end
@@ -109,12 +109,12 @@ RSpec.describe "bundle install" do
end
it "installs when patch level is specified and the version still matches the current version",
- :if => RUBY_PATCHLEVEL >= 0 do
- build_lib("foo", :path => bundled_app) do |s|
+ if: RUBY_PATCHLEVEL >= 0 do
+ build_lib("foo", path: bundled_app) do |s|
s.required_ruby_version = "#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}"
end
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
ruby '#{RUBY_VERSION}', :engine_version => '#{RUBY_VERSION}', :engine => 'ruby', :patchlevel => '#{RUBY_PATCHLEVEL}'
source "#{file_uri_for(gem_repo1)}"
gemspec
@@ -123,13 +123,13 @@ RSpec.describe "bundle install" do
end
it "fails and complains about patchlevel on patchlevel mismatch",
- :if => RUBY_PATCHLEVEL >= 0 do
+ if: RUBY_PATCHLEVEL >= 0 do
patchlevel = RUBY_PATCHLEVEL.to_i + 1
- build_lib("foo", :path => bundled_app) do |s|
+ build_lib("foo", path: bundled_app) do |s|
s.required_ruby_version = "#{RUBY_VERSION}.#{patchlevel}"
end
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
ruby '#{RUBY_VERSION}', :engine_version => '#{RUBY_VERSION}', :engine => 'ruby', :patchlevel => '#{patchlevel}'
source "#{file_uri_for(gem_repo1)}"
gemspec
@@ -143,11 +143,11 @@ RSpec.describe "bundle install" do
it "fails and complains about version on version mismatch" do
version = Gem::Requirement.create(RUBY_VERSION).requirements.first.last.bump.version
- build_lib("foo", :path => bundled_app) do |s|
+ build_lib("foo", path: bundled_app) do |s|
s.required_ruby_version = version
end
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
ruby '#{version}', :engine_version => '#{version}', :engine => 'ruby'
source "#{file_uri_for(gem_repo1)}"
gemspec
diff --git a/spec/bundler/install/git_spec.rb b/spec/bundler/install/git_spec.rb
index 0fa7ed8531..c8d574baf3 100644
--- a/spec/bundler/install/git_spec.rb
+++ b/spec/bundler/install/git_spec.rb
@@ -3,51 +3,51 @@
RSpec.describe "bundle install" do
context "git sources" do
it "displays the revision hash of the gem repository" do
- build_git "foo", "1.0", :path => lib_path("foo")
+ build_git "foo", "1.0", path: lib_path("foo")
- install_gemfile <<-G, :verbose => true
+ install_gemfile <<-G, verbose: true
source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{file_uri_for(lib_path("foo"))}"
G
expect(out).to include("Using foo 1.0 from #{file_uri_for(lib_path("foo"))} (at main@#{revision_for(lib_path("foo"))[0..6]})")
- expect(the_bundle).to include_gems "foo 1.0", :source => "git@#{lib_path("foo")}"
+ expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}"
end
- it "displays the correct default branch", :git => ">= 2.28.0" do
- build_git "foo", "1.0", :path => lib_path("foo"), :default_branch => "main"
+ it "displays the correct default branch", git: ">= 2.28.0" do
+ build_git "foo", "1.0", path: lib_path("foo"), default_branch: "main"
- install_gemfile <<-G, :verbose => true
+ install_gemfile <<-G, verbose: true
source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{file_uri_for(lib_path("foo"))}"
G
expect(out).to include("Using foo 1.0 from #{file_uri_for(lib_path("foo"))} (at main@#{revision_for(lib_path("foo"))[0..6]})")
- expect(the_bundle).to include_gems "foo 1.0", :source => "git@#{lib_path("foo")}"
+ expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}"
end
it "displays the ref of the gem repository when using branch~num as a ref" do
skip "maybe branch~num notation doesn't work on Windows' git" if Gem.win_platform?
- build_git "foo", "1.0", :path => lib_path("foo")
+ build_git "foo", "1.0", path: lib_path("foo")
rev = revision_for(lib_path("foo"))[0..6]
- update_git "foo", "2.0", :path => lib_path("foo"), :gemspec => true
+ update_git "foo", "2.0", path: lib_path("foo"), gemspec: true
rev2 = revision_for(lib_path("foo"))[0..6]
- update_git "foo", "3.0", :path => lib_path("foo"), :gemspec => true
+ update_git "foo", "3.0", path: lib_path("foo"), gemspec: true
- install_gemfile <<-G, :verbose => true
+ install_gemfile <<-G, verbose: true
source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{file_uri_for(lib_path("foo"))}", :ref => "main~2"
G
expect(out).to include("Using foo 1.0 from #{file_uri_for(lib_path("foo"))} (at main~2@#{rev})")
- expect(the_bundle).to include_gems "foo 1.0", :source => "git@#{lib_path("foo")}"
+ expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}"
- update_git "foo", "4.0", :path => lib_path("foo"), :gemspec => true
+ update_git "foo", "4.0", path: lib_path("foo"), gemspec: true
- bundle :update, :all => true, :verbose => true
+ bundle :update, all: true, verbose: true
expect(out).to include("Using foo 2.0 (was 1.0) from #{file_uri_for(lib_path("foo"))} (at main~2@#{rev2})")
- expect(the_bundle).to include_gems "foo 2.0", :source => "git@#{lib_path("foo")}"
+ expect(the_bundle).to include_gems "foo 2.0", source: "git@#{lib_path("foo")}"
end
it "should allows git repos that are missing but not being installed" do
@@ -81,9 +81,9 @@ RSpec.describe "bundle install" do
it "allows multiple gems from the same git source" do
build_repo2 do
- build_lib "foo", "1.0", :path => lib_path("gems/foo")
- build_lib "zebra", "2.0", :path => lib_path("gems/zebra")
- build_git "gems", :path => lib_path("gems"), :gemspec => false
+ build_lib "foo", "1.0", path: lib_path("gems/foo")
+ build_lib "zebra", "2.0", path: lib_path("gems/zebra")
+ build_git "gems", path: lib_path("gems"), gemspec: false
end
install_gemfile <<-G
@@ -98,5 +98,108 @@ RSpec.describe "bundle install" do
bundle "info zebra"
expect(out).to include("* zebra (2.0 #{revision_for(lib_path("gems"))[0..6]})")
end
+
+ it "should always sort dependencies in the same order" do
+ # This Gemfile + lockfile had a problem where the first
+ # `bundle install` would change the order, but the second would
+ # change it back.
+
+ # NOTE: both gems MUST have the same path! It has to be two gems in one repo.
+
+ test = build_git "test", "1.0.0", path: lib_path("test-and-other")
+ other = build_git "other", "1.0.0", path: lib_path("test-and-other")
+ test_ref = test.ref_for("HEAD")
+ other_ref = other.ref_for("HEAD")
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+
+ gem "test", git: #{test.path.to_s.inspect}
+ gem "other", ref: #{other_ref.inspect}, git: #{other.path.to_s.inspect}
+ G
+
+ lockfile <<-L
+ GIT
+ remote: #{test.path}
+ revision: #{test_ref}
+ specs:
+ test (1.0.0)
+
+ GIT
+ remote: #{other.path}
+ revision: #{other_ref}
+ ref: #{other_ref}
+ specs:
+ other (1.0.0)
+
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ specs:
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ other!
+ test!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ # If GH#6743 is present, the first `bundle install` will change the
+ # lockfile, by flipping the order (`other` would be moved to the top).
+ #
+ # The second `bundle install` would then change the lockfile back
+ # to the original.
+ #
+ # The fix makes it so it may change it once, but it will not change
+ # it a second time.
+ #
+ # So, we run `bundle install` once, and store the value of the
+ # modified lockfile.
+ bundle :install
+ modified_lockfile = lockfile
+
+ # If GH#6743 is present, the second `bundle install` would change the
+ # lockfile back to what it was originally.
+ #
+ # This `expect` makes sure it doesn't change a second time.
+ bundle :install
+ expect(lockfile).to eq(modified_lockfile)
+
+ expect(out).to include("Bundle complete!")
+ end
+
+ it "allows older revisions of git source when clean true" do
+ build_git "foo", "1.0", path: lib_path("foo")
+ rev = revision_for(lib_path("foo"))
+
+ bundle "config set path vendor/bundle"
+ bundle "config set clean true"
+ install_gemfile <<-G, verbose: true
+ source "#{file_uri_for(gem_repo1)}"
+ gem "foo", :git => "#{file_uri_for(lib_path("foo"))}"
+ G
+
+ expect(out).to include("Using foo 1.0 from #{file_uri_for(lib_path("foo"))} (at main@#{rev[0..6]})")
+ expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}"
+
+ old_lockfile = lockfile
+
+ update_git "foo", "2.0", path: lib_path("foo"), gemspec: true
+ rev2 = revision_for(lib_path("foo"))
+
+ bundle :update, all: true, verbose: true
+ expect(out).to include("Using foo 2.0 (was 1.0) from #{file_uri_for(lib_path("foo"))} (at main@#{rev2[0..6]})")
+ expect(out).to include("Removing foo (#{rev[0..11]})")
+ expect(the_bundle).to include_gems "foo 2.0", source: "git@#{lib_path("foo")}"
+
+ lockfile(old_lockfile)
+
+ bundle :install, verbose: true
+ expect(out).to include("Using foo 1.0 from #{file_uri_for(lib_path("foo"))} (at main@#{rev[0..6]})")
+ expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}"
+ end
end
end
diff --git a/spec/bundler/install/global_cache_spec.rb b/spec/bundler/install/global_cache_spec.rb
index be34d8b5bc..0da4de05b2 100644
--- a/spec/bundler/install/global_cache_spec.rb
+++ b/spec/bundler/install/global_cache_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe "global gem caching" do
end
it "caches gems into the global cache on download" do
- install_gemfile <<-G, :artifice => "compact_index"
+ install_gemfile <<-G, artifice: "compact_index"
source "#{source}"
gem "rack"
G
@@ -29,7 +29,7 @@ RSpec.describe "global gem caching" do
source_global_cache.mkpath
FileUtils.cp(gem_repo1("gems/rack-1.0.0.gem"), source_global_cache("rack-1.0.0.gem"))
- install_gemfile <<-G, :artifice => "compact_index_no_gem"
+ install_gemfile <<-G, artifice: "compact_index_no_gem"
source "#{source}"
gem "rack"
G
@@ -41,7 +41,7 @@ RSpec.describe "global gem caching" do
source_global_cache.mkpath
FileUtils.touch(source_global_cache("rack-1.0.0.gem"))
- install_gemfile <<-G, :artifice => "compact_index_no_gem", :raise_on_error => false
+ install_gemfile <<-G, artifice: "compact_index_no_gem", raise_on_error: false
source "#{source}"
gem "rack"
G
@@ -51,7 +51,7 @@ RSpec.describe "global gem caching" do
describe "when the same gem from different sources is installed" do
it "should use the appropriate one from the global cache" do
- install_gemfile <<-G, :artifice => "compact_index"
+ install_gemfile <<-G, artifice: "compact_index"
source "#{source}"
gem "rack"
G
@@ -61,7 +61,7 @@ RSpec.describe "global gem caching" do
expect(source_global_cache("rack-1.0.0.gem")).to exist
# rack 1.0.0 is not installed and it is in the global cache
- install_gemfile <<-G, :artifice => "compact_index"
+ install_gemfile <<-G, artifice: "compact_index"
source "#{source2}"
gem "rack", "0.9.1"
G
@@ -76,7 +76,7 @@ RSpec.describe "global gem caching" do
gem "rack", "1.0.0"
G
- bundle :install, :artifice => "compact_index_no_gem"
+ bundle :install, artifice: "compact_index_no_gem"
# rack 1.0.0 is installed and rack 0.9.1 is not
expect(the_bundle).to include_gems "rack 1.0.0"
expect(the_bundle).not_to include_gems "rack 0.9.1"
@@ -87,7 +87,7 @@ RSpec.describe "global gem caching" do
gem "rack", "0.9.1"
G
- bundle :install, :artifice => "compact_index_no_gem"
+ bundle :install, artifice: "compact_index_no_gem"
# rack 0.9.1 is installed and rack 1.0.0 is not
expect(the_bundle).to include_gems "rack 0.9.1"
expect(the_bundle).not_to include_gems "rack 1.0.0"
@@ -99,7 +99,7 @@ RSpec.describe "global gem caching" do
gem "rack"
G
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
simulate_new_machine
expect(the_bundle).not_to include_gems "rack 1.0.0"
expect(source_global_cache("rack-1.0.0.gem")).to exist
@@ -110,7 +110,7 @@ RSpec.describe "global gem caching" do
gem "rack", "0.9.1"
G
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
simulate_new_machine
expect(the_bundle).not_to include_gems "rack 0.9.1"
expect(source2_global_cache("rack-0.9.1.gem")).to exist
@@ -123,7 +123,7 @@ RSpec.describe "global gem caching" do
expect(source_global_cache("rack-1.0.0.gem")).to exist
expect(source2_global_cache("rack-0.9.1.gem")).to exist
- bundle :install, :artifice => "compact_index_no_gem", :raise_on_error => false
+ bundle :install, artifice: "compact_index_no_gem", raise_on_error: false
expect(err).to include("Internal Server Error 500")
expect(err).not_to include("ERROR REPORT TEMPLATE")
@@ -138,7 +138,7 @@ RSpec.describe "global gem caching" do
expect(source_global_cache("rack-1.0.0.gem")).to exist
expect(source2_global_cache("rack-0.9.1.gem")).to exist
- bundle :install, :artifice => "compact_index_no_gem", :raise_on_error => false
+ bundle :install, artifice: "compact_index_no_gem", raise_on_error: false
expect(err).to include("Internal Server Error 500")
expect(err).not_to include("ERROR REPORT TEMPLATE")
@@ -150,7 +150,7 @@ RSpec.describe "global gem caching" do
describe "when installing gems from a different directory" do
it "uses the global cache as a source" do
- install_gemfile <<-G, :artifice => "compact_index"
+ install_gemfile <<-G, artifice: "compact_index"
source "#{source}"
gem "rack"
gem "activesupport"
@@ -166,7 +166,7 @@ RSpec.describe "global gem caching" do
expect(the_bundle).not_to include_gems "rack 1.0.0"
expect(the_bundle).not_to include_gems "activesupport 2.3.5"
- install_gemfile <<-G, :artifice => "compact_index_no_gem"
+ install_gemfile <<-G, artifice: "compact_index_no_gem"
source "#{source}"
gem "rack"
G
@@ -183,20 +183,18 @@ RSpec.describe "global gem caching" do
G
# Neither gem is installed and both are in the global cache
- expect(the_bundle).not_to include_gems "rack 1.0.0", :dir => bundled_app2
- expect(the_bundle).not_to include_gems "activesupport 2.3.5", :dir => bundled_app2
+ expect(the_bundle).not_to include_gems "rack 1.0.0", dir: bundled_app2
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5", dir: bundled_app2
expect(source_global_cache("rack-1.0.0.gem")).to exist
expect(source_global_cache("activesupport-2.3.5.gem")).to exist
# Install using the global cache instead of by downloading the .gem
# from the server
- bundle :install, :artifice => "compact_index_no_gem", :dir => bundled_app2
+ bundle :install, artifice: "compact_index_no_gem", dir: bundled_app2
# activesupport is installed and both are in the global cache
- simulate_bundler_version_when_missing_prerelease_default_gem_activation do
- expect(the_bundle).not_to include_gems "rack 1.0.0", :dir => bundled_app2
- expect(the_bundle).to include_gems "activesupport 2.3.5", :dir => bundled_app2
- end
+ expect(the_bundle).not_to include_gems "rack 1.0.0", dir: bundled_app2
+ expect(the_bundle).to include_gems "activesupport 2.3.5", dir: bundled_app2
expect(source_global_cache("rack-1.0.0.gem")).to exist
expect(source_global_cache("activesupport-2.3.5.gem")).to exist
@@ -234,7 +232,7 @@ RSpec.describe "global gem caching" do
R
expect(out).to eq "VERY_SIMPLE_BINARY_IN_C\nVERY_SIMPLE_GIT_BINARY_IN_C"
- FileUtils.rm Dir[home(".bundle", "cache", "extensions", "**", "*binary_c*")]
+ FileUtils.rm_rf Dir[home(".bundle", "cache", "extensions", "**", "*binary_c*")]
gem_binary_cache.join("very_simple_binary_c.rb").open("w") {|f| f << "puts File.basename(__FILE__)" }
git_binary_cache.join("very_simple_git_binary_c.rb").open("w") {|f| f << "puts File.basename(__FILE__)" }
diff --git a/spec/bundler/install/path_spec.rb b/spec/bundler/install/path_spec.rb
index bd5385b265..0a30e402b7 100644
--- a/spec/bundler/install/path_spec.rb
+++ b/spec/bundler/install/path_spec.rb
@@ -3,7 +3,7 @@
RSpec.describe "bundle install" do
describe "with path configured" do
before :each do
- build_gem "rack", "1.0.0", :to_system => true do |s|
+ build_gem "rack", "1.0.0", to_system: true do |s|
s.write "lib/rack.rb", "puts 'FAIL'"
end
@@ -23,7 +23,7 @@ RSpec.describe "bundle install" do
bundle "config set --local path.system true"
bundle "config set --global path vendor/bundle"
bundle :install
- run "require 'rack'", :raise_on_error => false
+ run "require 'rack'", raise_on_error: false
expect(out).to include("FAIL")
end
@@ -32,7 +32,7 @@ RSpec.describe "bundle install" do
dir.mkpath
bundle "config set --local path #{dir.join("vendor/bundle")}"
- bundle :install, :dir => dir
+ bundle :install, dir: dir
expect(out).to include("installed into `./vendor/bundle`")
dir.rmtree
@@ -44,13 +44,13 @@ RSpec.describe "bundle install" do
expect(out).to include("gems are installed into `./vendor/bundle`")
end
- it "disallows --path vendor/bundle --system", :bundler => "< 3" do
- bundle "install --path vendor/bundle --system", :raise_on_error => false
+ it "disallows --path vendor/bundle --system", bundler: "< 3" do
+ bundle "install --path vendor/bundle --system", raise_on_error: false
expect(err).to include("Please choose only one option.")
expect(exitstatus).to eq(15)
end
- it "remembers to disable system gems after the first time with bundle --path vendor/bundle", :bundler => "< 3" do
+ it "remembers to disable system gems after the first time with bundle --path vendor/bundle", bundler: "< 3" do
bundle "install --path vendor/bundle"
FileUtils.rm_rf bundled_app("vendor")
bundle "install"
@@ -62,22 +62,22 @@ RSpec.describe "bundle install" do
context "with path_relative_to_cwd set to true" do
before { bundle "config set path_relative_to_cwd true" }
- it "installs the bundle relatively to current working directory", :bundler => "< 3" do
- bundle "install --gemfile='#{bundled_app}/Gemfile' --path vendor/bundle", :dir => bundled_app.parent
+ it "installs the bundle relatively to current working directory", bundler: "< 3" do
+ bundle "install --gemfile='#{bundled_app}/Gemfile' --path vendor/bundle", dir: bundled_app.parent
expect(out).to include("installed into `./vendor/bundle`")
expect(bundled_app("../vendor/bundle")).to be_directory
expect(the_bundle).to include_gems "rack 1.0.0"
end
it "installs the standalone bundle relative to the cwd" do
- bundle :install, :gemfile => bundled_app_gemfile, :standalone => true, :dir => bundled_app.parent
+ bundle :install, gemfile: bundled_app_gemfile, standalone: true, dir: bundled_app.parent
expect(out).to include("installed into `./bundled_app/bundle`")
expect(bundled_app("bundle")).to be_directory
expect(bundled_app("bundle/ruby")).to be_directory
bundle "config unset path"
- bundle :install, :gemfile => bundled_app_gemfile, :standalone => true, :dir => bundled_app("subdir").tap(&:mkpath)
+ bundle :install, gemfile: bundled_app_gemfile, standalone: true, dir: bundled_app("subdir").tap(&:mkpath)
expect(out).to include("installed into `../bundle`")
expect(bundled_app("bundle")).to be_directory
expect(bundled_app("bundle/ruby")).to be_directory
@@ -87,7 +87,7 @@ RSpec.describe "bundle install" do
describe "when BUNDLE_PATH or the global path config is set" do
before :each do
- build_lib "rack", "1.0.0", :to_system => true do |s|
+ build_lib "rack", "1.0.0", to_system: true do |s|
s.write "lib/rack.rb", "raise 'FAIL'"
end
@@ -141,7 +141,7 @@ RSpec.describe "bundle install" do
set_bundle_path(type, "vendor")
FileUtils.mkdir_p bundled_app("lol")
- bundle :install, :dir => bundled_app("lol")
+ bundle :install, dir: bundled_app("lol")
expect(bundled_app("vendor", Bundler.ruby_scope, "gems/rack-1.0.0")).to be_directory
expect(the_bundle).to include_gems "rack 1.0.0"
@@ -168,7 +168,7 @@ RSpec.describe "bundle install" do
it "disables system gems when passing a path to install" do
# This is so that vendored gems can be distributed to others
- build_gem "rack", "1.1.0", :to_system => true
+ build_gem "rack", "1.1.0", to_system: true
bundle "config set --local path ./vendor/bundle"
bundle :install
@@ -177,7 +177,7 @@ RSpec.describe "bundle install" do
end
it "re-installs gems whose extensions have been deleted" do
- build_lib "very_simple_binary", "1.0.0", :to_system => true do |s|
+ build_lib "very_simple_binary", "1.0.0", to_system: true do |s|
s.write "lib/very_simple_binary.rb", "raise 'FAIL'"
end
@@ -191,11 +191,11 @@ RSpec.describe "bundle install" do
expect(vendored_gems("gems/very_simple_binary-1.0")).to be_directory
expect(vendored_gems("extensions")).to be_directory
- expect(the_bundle).to include_gems "very_simple_binary 1.0", :source => "remote1"
+ expect(the_bundle).to include_gems "very_simple_binary 1.0", source: "remote1"
vendored_gems("extensions").rmtree
- run "require 'very_simple_binary_c'", :raise_on_error => false
+ run "require 'very_simple_binary_c'", raise_on_error: false
expect(err).to include("Bundler::GemNotFound")
bundle "config set --local path ./vendor/bundle"
@@ -203,7 +203,7 @@ RSpec.describe "bundle install" do
expect(vendored_gems("gems/very_simple_binary-1.0")).to be_directory
expect(vendored_gems("extensions")).to be_directory
- expect(the_bundle).to include_gems "very_simple_binary 1.0", :source => "remote1"
+ expect(the_bundle).to include_gems "very_simple_binary 1.0", source: "remote1"
end
end
@@ -219,7 +219,7 @@ RSpec.describe "bundle install" do
G
bundle "config set --local path bundle"
- bundle :install, :raise_on_error => false
+ bundle :install, raise_on_error: false
expect(err).to include("file already exists")
end
end
diff --git a/spec/bundler/install/redownload_spec.rb b/spec/bundler/install/redownload_spec.rb
index a936b2b536..3a72c356d9 100644
--- a/spec/bundler/install/redownload_spec.rb
+++ b/spec/bundler/install/redownload_spec.rb
@@ -57,7 +57,7 @@ RSpec.describe "bundle install" do
end
end
- describe "with --force", :bundler => 2 do
+ describe "with --force", bundler: 2 do
it_behaves_like "an option to force redownloading gems" do
let(:flag) { "force" }
end
diff --git a/spec/bundler/install/security_policy_spec.rb b/spec/bundler/install/security_policy_spec.rb
index 43c3069c4e..befeb81da5 100644
--- a/spec/bundler/install/security_policy_spec.rb
+++ b/spec/bundler/install/security_policy_spec.rb
@@ -16,23 +16,23 @@ RSpec.describe "policies with unsigned gems" do
end
it "will work after you try to deploy without a lock" do
- bundle "install --deployment", :raise_on_error => false
+ bundle "install --deployment", raise_on_error: false
bundle :install
expect(the_bundle).to include_gems "rack 1.0", "signed_gem 1.0"
end
it "will fail when given invalid security policy" do
- bundle "install --trust-policy=InvalidPolicyName", :raise_on_error => false
+ bundle "install --trust-policy=InvalidPolicyName", raise_on_error: false
expect(err).to include("RubyGems doesn't know about trust policy")
end
it "will fail with High Security setting due to presence of unsigned gem" do
- bundle "install --trust-policy=HighSecurity", :raise_on_error => false
+ bundle "install --trust-policy=HighSecurity", raise_on_error: false
expect(err).to include("security policy didn't allow")
end
it "will fail with Medium Security setting due to presence of unsigned gem" do
- bundle "install --trust-policy=MediumSecurity", :raise_on_error => false
+ bundle "install --trust-policy=MediumSecurity", raise_on_error: false
expect(err).to include("security policy didn't allow")
end
@@ -51,12 +51,12 @@ RSpec.describe "policies with signed gems and no CA" do
end
it "will fail with High Security setting, gem is self-signed" do
- bundle "install --trust-policy=HighSecurity", :raise_on_error => false
+ bundle "install --trust-policy=HighSecurity", raise_on_error: false
expect(err).to include("security policy didn't allow")
end
it "will fail with Medium Security setting, gem is self-signed" do
- bundle "install --trust-policy=MediumSecurity", :raise_on_error => false
+ bundle "install --trust-policy=MediumSecurity", raise_on_error: false
expect(err).to include("security policy didn't allow")
end
diff --git a/spec/bundler/install/yanked_spec.rb b/spec/bundler/install/yanked_spec.rb
index dc054b50bb..7408c24327 100644
--- a/spec/bundler/install/yanked_spec.rb
+++ b/spec/bundler/install/yanked_spec.rb
@@ -22,7 +22,7 @@ RSpec.context "when installing a bundle that includes yanked gems" do
L
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo4)}"
gem "foo", "10.0.0"
G
@@ -79,7 +79,7 @@ RSpec.context "when installing a bundle that includes yanked gems" do
let(:source_uri) { file_uri_for(gem_repo4) }
it "reports the yanked gem properly" do
- bundle "install", :raise_on_error => false
+ bundle "install", raise_on_error: false
expect(err).to include("Your bundle is locked to nokogiri (1.13.8-#{Bundler.local_platform})")
end
@@ -89,7 +89,7 @@ RSpec.context "when installing a bundle that includes yanked gems" do
let(:source_uri) { "https://gem.repo4" }
it "reports the yanked gem properly" do
- bundle "install", :artifice => "compact_index", :raise_on_error => false
+ bundle "install", artifice: "compact_index", raise_on_error: false
expect(err).to include("Your bundle is locked to nokogiri (1.13.8-#{Bundler.local_platform})")
end
@@ -99,7 +99,7 @@ RSpec.context "when installing a bundle that includes yanked gems" do
it "throws the original error when only the Gemfile specifies a gem version that doesn't exist" do
bundle "config set force_ruby_platform true"
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo4)}"
gem "foo", "10.0.0"
G
@@ -186,9 +186,9 @@ RSpec.context "when using gem before installing" do
rack (= 0.9.1)
L
- bundle :list, :raise_on_error => false
+ bundle :list, raise_on_error: false
- expect(err).to include("Could not find rack-0.9.1 in locally installed gems")
+ expect(err).to include("Could not find rack-0.9.1 in cached gems or installed locally")
expect(err).to_not include("Your bundle is locked to rack (0.9.1) from")
expect(err).to_not include("If you haven't changed sources, that means the author of rack (0.9.1) has removed it.")
expect(err).to_not include("You'll need to update your bundle to a different version of rack (0.9.1) that hasn't been removed in order to install.")
@@ -196,8 +196,8 @@ RSpec.context "when using gem before installing" do
# Check error message is still correct when multiple platforms are locked
lockfile lockfile.gsub(/PLATFORMS\n #{lockfile_platforms}/m, "PLATFORMS\n #{lockfile_platforms("ruby")}")
- bundle :list, :raise_on_error => false
- expect(err).to include("Could not find rack-0.9.1 in locally installed gems")
+ bundle :list, raise_on_error: false
+ expect(err).to include("Could not find rack-0.9.1 in cached gems or installed locally")
end
it "does not suggest the author has yanked the gem when using more than one gem, but shows all gems that couldn't be found in the source" do
@@ -222,9 +222,9 @@ RSpec.context "when using gem before installing" do
rack_middleware (1.0)
L
- bundle :list, :raise_on_error => false
+ bundle :list, raise_on_error: false
- expect(err).to include("Could not find rack-0.9.1, rack_middleware-1.0 in locally installed gems")
+ expect(err).to include("Could not find rack-0.9.1, rack_middleware-1.0 in cached gems or installed locally")
expect(err).to include("Install missing gems with `bundle install`.")
expect(err).to_not include("Your bundle is locked to rack (0.9.1) from")
expect(err).to_not include("If you haven't changed sources, that means the author of rack (0.9.1) has removed it.")
diff --git a/spec/bundler/lock/git_spec.rb b/spec/bundler/lock/git_spec.rb
index ac3d10223c..ad13e8ffc6 100644
--- a/spec/bundler/lock/git_spec.rb
+++ b/spec/bundler/lock/git_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe "bundle lock with git gems" do
gem 'foo', :git => "#{lib_path("foo-1.0")}", :branch => "bad"
G
- bundle "lock --update foo", :env => { "LANG" => "en" }, :raise_on_error => false
+ bundle "lock --update foo", env: { "LANG" => "en" }, raise_on_error: false
expect(err).to include("Revision bad does not exist in the repository")
end
@@ -55,7 +55,7 @@ RSpec.describe "bundle lock with git gems" do
#{Bundler::VERSION}
L
- bundle "install", :raise_on_error => false
+ bundle "install", raise_on_error: false
expect(err).to include("Revision #{"a" * 40} does not exist in the repository")
end
@@ -75,7 +75,7 @@ RSpec.describe "bundle lock with git gems" do
it "properly clones a git source locked to an out of date ref" do
update_git "foo"
- bundle :install, :env => { "BUNDLE_PATH" => "foo" }
+ bundle :install, env: { "BUNDLE_PATH" => "foo" }
expect(err).to be_empty
end
diff --git a/spec/bundler/lock/lockfile_spec.rb b/spec/bundler/lock/lockfile_spec.rb
index 8d9554c68d..4fd081e7d0 100644
--- a/spec/bundler/lock/lockfile_spec.rb
+++ b/spec/bundler/lock/lockfile_spec.rb
@@ -6,6 +6,10 @@ RSpec.describe "the lockfile format" do
end
it "generates a simple lockfile for a single source, gem" do
+ checksums = checksums_section_when_existing do |c|
+ c.checksum(gem_repo2, "rack", "1.0.0")
+ end
+
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
@@ -23,7 +27,7 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
rack
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -54,7 +58,7 @@ RSpec.describe "the lockfile format" do
1.8.2
L
- install_gemfile <<-G, :verbose => true, :env => { "BUNDLER_VERSION" => Bundler::VERSION }
+ install_gemfile <<-G, verbose: true, env: { "BUNDLER_VERSION" => Bundler::VERSION }
source "#{file_uri_for(gem_repo2)}"
gem "rack"
@@ -80,7 +84,7 @@ RSpec.describe "the lockfile format" do
G
end
- it "does not update the lockfile's bundler version if nothing changed during bundle install, but uses the locked version", :rubygems => ">= 3.3.0.a", :realworld => true do
+ it "does not update the lockfile's bundler version if nothing changed during bundle install, but uses the locked version", rubygems: ">= 3.3.0.a", realworld: true do
version = "2.3.0"
lockfile <<-L
@@ -99,7 +103,7 @@ RSpec.describe "the lockfile format" do
#{version}
L
- install_gemfile <<-G, :verbose => true, :artifice => "vcr"
+ install_gemfile <<-G, verbose: true, artifice: "vcr"
source "#{file_uri_for(gem_repo2)}"
gem "rack"
@@ -125,9 +129,13 @@ RSpec.describe "the lockfile format" do
G
end
- it "does not update the lockfile's bundler version if nothing changed during bundle install, and uses the latest version", :rubygems => "< 3.3.0.a" do
+ it "does not update the lockfile's bundler version if nothing changed during bundle install, and uses the latest version", rubygems: "< 3.3.0.a" do
version = "#{Bundler::VERSION.split(".").first}.0.0.a"
+ checksums = checksums_section do |c|
+ c.checksum(gem_repo2, "rack", "1.0.0")
+ end
+
lockfile <<-L
GEM
remote: #{file_uri_for(gem_repo2)}/
@@ -139,12 +147,12 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
rack
-
+ #{checksums}
BUNDLED WITH
#{version}
L
- install_gemfile <<-G, :verbose => true
+ install_gemfile <<-G, verbose: true
source "#{file_uri_for(gem_repo2)}"
gem "rack"
@@ -164,7 +172,7 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
rack
-
+ #{checksums}
BUNDLED WITH
#{version}
G
@@ -229,7 +237,7 @@ RSpec.describe "the lockfile format" do
#{older_major}
L
- install_gemfile <<-G, :env => { "BUNDLER_VERSION" => Bundler::VERSION }
+ install_gemfile <<-G, env: { "BUNDLER_VERSION" => Bundler::VERSION }
source "#{file_uri_for(gem_repo2)}/"
gem "rack"
@@ -261,6 +269,11 @@ RSpec.describe "the lockfile format" do
gem "rack-obama"
G
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo2, "rack", "1.0.0"
+ c.checksum gem_repo2, "rack-obama", "1.0"
+ end
+
expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
@@ -274,7 +287,7 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
rack-obama
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -287,6 +300,11 @@ RSpec.describe "the lockfile format" do
gem "rack-obama", ">= 1.0"
G
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo2, "rack", "1.0.0"
+ c.checksum gem_repo2, "rack-obama", "1.0"
+ end
+
expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
@@ -300,16 +318,16 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
rack-obama (>= 1.0)
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
end
- it "generates a lockfile without credentials for a configured source" do
+ it "generates a lockfile without credentials" do
bundle "config set http://localgemserver.test/ user:pass"
- install_gemfile(<<-G, :artifice => "endpoint_strict_basic_authentication", :quiet => true)
+ install_gemfile(<<-G, artifice: "endpoint_strict_basic_authentication", quiet: true)
source "#{file_uri_for(gem_repo1)}"
source "http://localgemserver.test/" do
@@ -321,6 +339,11 @@ RSpec.describe "the lockfile format" do
end
G
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo2, "rack", "1.0.0"
+ c.checksum gem_repo2, "rack-obama", "1.0"
+ end
+
expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo1)}/
@@ -331,7 +354,7 @@ RSpec.describe "the lockfile format" do
specs:
GEM
- remote: http://user:pass@othergemserver.test/
+ remote: http://othergemserver.test/
specs:
rack (1.0.0)
rack-obama (1.0)
@@ -342,7 +365,7 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
rack-obama (>= 1.0)!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -354,6 +377,11 @@ RSpec.describe "the lockfile format" do
gem "net-sftp"
G
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo2, "net-sftp", "1.1.1"
+ c.checksum gem_repo2, "net-ssh", "1.0"
+ end
+
expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
@@ -367,7 +395,7 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
net-sftp
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -383,6 +411,10 @@ RSpec.describe "the lockfile format" do
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "foo", "1.0"
+ end
+
expect(lockfile).to eq <<~G
GIT
remote: #{lib_path("foo-1.0")}
@@ -399,14 +431,14 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
end
it "does not asplode when a platform specific dependency is present and the Gemfile has not been resolved on that platform" do
- build_lib "omg", :path => lib_path("omg")
+ build_lib "omg", path: lib_path("omg")
gemfile <<-G
source "#{file_uri_for(gem_repo2)}/"
@@ -447,6 +479,10 @@ RSpec.describe "the lockfile format" do
it "serializes global git sources" do
git = build_git "foo"
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "foo", "1.0"
+ end
+
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
git "#{lib_path("foo-1.0")}" do
@@ -470,7 +506,7 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -478,7 +514,11 @@ RSpec.describe "the lockfile format" do
it "generates a lockfile with a ref for a single pinned source, git gem with a branch requirement" do
git = build_git "foo"
- update_git "foo", :branch => "omg"
+ update_git "foo", branch: "omg"
+
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "foo", "1.0"
+ end
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -502,7 +542,7 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -510,7 +550,11 @@ RSpec.describe "the lockfile format" do
it "generates a lockfile with a ref for a single pinned source, git gem with a tag requirement" do
git = build_git "foo"
- update_git "foo", :tag => "omg"
+ update_git "foo", tag: "omg"
+
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "foo", "1.0"
+ end
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -534,15 +578,105 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
end
+ it "is conservative with dependencies of git gems" do
+ build_repo4 do
+ build_gem "orm_adapter", "0.4.1"
+ build_gem "orm_adapter", "0.5.0"
+ end
+
+ FileUtils.mkdir_p lib_path("ckeditor/lib")
+
+ @remote = build_git("ckeditor_remote", bare: true)
+
+ build_git "ckeditor", path: lib_path("ckeditor") do |s|
+ s.write "lib/ckeditor.rb", "CKEDITOR = '4.0.7'"
+ s.version = "4.0.7"
+ s.add_dependency "orm_adapter"
+ end
+
+ update_git "ckeditor", path: lib_path("ckeditor"), remote: file_uri_for(@remote.path)
+ update_git "ckeditor", path: lib_path("ckeditor"), tag: "v4.0.7"
+ old_git = update_git "ckeditor", path: lib_path("ckeditor"), push: "v4.0.7"
+
+ update_git "ckeditor", path: lib_path("ckeditor"), gemspec: true do |s|
+ s.write "lib/ckeditor.rb", "CKEDITOR = '4.0.8'"
+ s.version = "4.0.8"
+ s.add_dependency "orm_adapter"
+ end
+ update_git "ckeditor", path: lib_path("ckeditor"), tag: "v4.0.8"
+
+ new_git = update_git "ckeditor", path: lib_path("ckeditor"), push: "v4.0.8"
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "ckeditor", :git => "#{@remote.path}", :tag => "v4.0.8"
+ G
+
+ lockfile <<~L
+ GIT
+ remote: #{@remote.path}
+ revision: #{old_git.ref_for("v4.0.7")}
+ tag: v4.0.7
+ specs:
+ ckeditor (4.0.7)
+ orm_adapter
+
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ orm_adapter (0.4.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ckeditor!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock"
+
+ # Bumps the git gem, but keeps its dependency locked
+ expect(lockfile).to eq <<~L
+ GIT
+ remote: #{@remote.path}
+ revision: #{new_git.ref_for("v4.0.8")}
+ tag: v4.0.8
+ specs:
+ ckeditor (4.0.8)
+ orm_adapter
+
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ orm_adapter (0.4.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ckeditor!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
it "serializes pinned path sources to the lockfile" do
build_lib "foo"
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "foo", "1.0"
+ end
+
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => "#{lib_path("foo-1.0")}"
@@ -563,7 +697,7 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -572,6 +706,10 @@ RSpec.describe "the lockfile format" do
it "serializes pinned path sources to the lockfile even when packaging" do
build_lib "foo"
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "foo", "1.0"
+ end
+
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => "#{lib_path("foo-1.0")}"
@@ -579,7 +717,7 @@ RSpec.describe "the lockfile format" do
bundle "config set cache_all true"
bundle :cache
- bundle :install, :local => true
+ bundle :install, local: true
expect(lockfile).to eq <<~G
PATH
@@ -596,7 +734,7 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -606,6 +744,12 @@ RSpec.describe "the lockfile format" do
build_lib "foo"
bar = build_git "bar"
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "foo", "1.0"
+ c.no_checksum "bar", "1.0"
+ c.checksum gem_repo2, "rack", "1.0.0"
+ end
+
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}/"
@@ -638,7 +782,7 @@ RSpec.describe "the lockfile format" do
bar!
foo!
rack
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -651,6 +795,10 @@ RSpec.describe "the lockfile format" do
gem "rack", :source => "#{file_uri_for(gem_repo2)}/"
G
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo2, "rack", "1.0.0"
+ end
+
expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
@@ -662,7 +810,7 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
rack!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -677,6 +825,14 @@ RSpec.describe "the lockfile format" do
gem "rack-obama"
G
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo2, "actionpack", "2.3.2"
+ c.checksum gem_repo2, "activesupport", "2.3.2"
+ c.checksum gem_repo2, "rack", "1.0.0"
+ c.checksum gem_repo2, "rack-obama", "1.0"
+ c.checksum gem_repo2, "thin", "1.0"
+ end
+
expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
@@ -697,7 +853,7 @@ RSpec.describe "the lockfile format" do
actionpack
rack-obama
thin
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -710,6 +866,16 @@ RSpec.describe "the lockfile format" do
gem "rails"
G
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo2, "actionmailer", "2.3.2"
+ c.checksum gem_repo2, "actionpack", "2.3.2"
+ c.checksum gem_repo2, "activerecord", "2.3.2"
+ c.checksum gem_repo2, "activeresource", "2.3.2"
+ c.checksum gem_repo2, "activesupport", "2.3.2"
+ c.checksum gem_repo2, "rails", "2.3.2"
+ c.checksum gem_repo2, "rake", rake_version
+ end
+
expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
@@ -728,15 +894,15 @@ RSpec.describe "the lockfile format" do
actionpack (= 2.3.2)
activerecord (= 2.3.2)
activeresource (= 2.3.2)
- rake (= 13.0.1)
- rake (13.0.1)
+ rake (= #{rake_version})
+ rake (#{rake_version})
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
rails
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -747,7 +913,7 @@ RSpec.describe "the lockfile format" do
# Capistrano did this (at least until version 2.5.10)
# RubyGems 2.2 doesn't allow the specifying of a dependency twice
# See https://github.com/rubygems/rubygems/commit/03dbac93a3396a80db258d9bc63500333c25bd2f
- build_gem "double_deps", "1.0", :skip_validation => true do |s|
+ build_gem "double_deps", "1.0", skip_validation: true do |s|
s.add_dependency "net-ssh", ">= 1.0.0"
s.add_dependency "net-ssh"
end
@@ -758,6 +924,11 @@ RSpec.describe "the lockfile format" do
gem 'double_deps'
G
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo2, "double_deps", "1.0"
+ c.checksum gem_repo2, "net-ssh", "1.0"
+ end
+
expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
@@ -772,7 +943,7 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
double_deps
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -785,6 +956,11 @@ RSpec.describe "the lockfile format" do
gem "rack-obama", ">= 1.0", :require => "rack/obama"
G
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo2, "rack", "1.0.0"
+ c.checksum gem_repo2, "rack-obama", "1.0"
+ end
+
expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
@@ -798,7 +974,7 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
rack-obama (>= 1.0)
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -811,6 +987,11 @@ RSpec.describe "the lockfile format" do
gem "rack-obama", ">= 1.0", :group => :test
G
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo2, "rack", "1.0.0"
+ c.checksum gem_repo2, "rack-obama", "1.0"
+ end
+
expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
@@ -824,14 +1005,18 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
rack-obama (>= 1.0)
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
end
it "stores relative paths when the path is provided in a relative fashion and in Gemfile dir" do
- build_lib "foo", :path => bundled_app("foo")
+ build_lib "foo", path: bundled_app("foo")
+
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "foo", "1.0"
+ end
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -855,14 +1040,18 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
end
it "stores relative paths when the path is provided in a relative fashion and is above Gemfile dir" do
- build_lib "foo", :path => bundled_app(File.join("..", "foo"))
+ build_lib "foo", path: bundled_app(File.join("..", "foo"))
+
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "foo", "1.0"
+ end
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -886,14 +1075,18 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
end
it "stores relative paths when the path is provided in an absolute fashion but is relative" do
- build_lib "foo", :path => bundled_app("foo")
+ build_lib "foo", path: bundled_app("foo")
+
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "foo", "1.0"
+ end
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -917,14 +1110,18 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
end
it "stores relative paths when the path is provided for gemspec" do
- build_lib("foo", :path => tmp.join("foo"))
+ build_lib("foo", path: tmp.join("foo"))
+
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "foo", "1.0"
+ end
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -946,13 +1143,17 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
end
it "keeps existing platforms in the lockfile" do
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "rack", "1.0.0"
+ end
+
lockfile <<-G
GEM
remote: #{file_uri_for(gem_repo2)}/
@@ -964,7 +1165,7 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
rack
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -975,6 +1176,8 @@ RSpec.describe "the lockfile format" do
gem "rack"
G
+ checksums.checksum(gem_repo2, "rack", "1.0.0")
+
expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
@@ -982,16 +1185,56 @@ RSpec.describe "the lockfile format" do
rack (1.0.0)
PLATFORMS
- #{lockfile_platforms("java")}
+ #{lockfile_platforms("java", local_platform, defaults: [])}
DEPENDENCIES
rack
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
end
+ it "adds compatible platform specific variants to the lockfile, even if resolution fallback to RUBY due to some other incompatible platform specific variant" do
+ simulate_platform "arm64-darwin-23" do
+ build_repo4 do
+ build_gem "google-protobuf", "3.25.1"
+ build_gem "google-protobuf", "3.25.1" do |s|
+ s.platform = "arm64-darwin-23"
+ end
+ build_gem "google-protobuf", "3.25.1" do |s|
+ s.platform = "x64-mingw-ucrt"
+ s.required_ruby_version = "> #{Gem.ruby_version}"
+ end
+ end
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "google-protobuf"
+ G
+ bundle "lock --add-platform x64-mingw-ucrt"
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ google-protobuf (3.25.1)
+ google-protobuf (3.25.1-arm64-darwin-23)
+
+ PLATFORMS
+ arm64-darwin-23
+ ruby
+ x64-mingw-ucrt
+
+ DEPENDENCIES
+ google-protobuf
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
it "persists the spec's specific platform to the lockfile" do
build_repo2 do
build_gem "platform_specific", "1.0" do |s|
@@ -1006,6 +1249,10 @@ RSpec.describe "the lockfile format" do
gem "platform_specific"
G
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo2, "platform_specific", "1.0", "universal-java-16"
+ end
+
expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
@@ -1017,13 +1264,18 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
platform_specific
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
end
it "does not add duplicate gems" do
+ checksums = checksums_section_when_existing do |c|
+ c.checksum(gem_repo2, "activesupport", "2.3.5")
+ c.checksum(gem_repo2, "rack", "1.0.0")
+ end
+
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}/"
gem "rack"
@@ -1048,13 +1300,17 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
activesupport
rack
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
end
it "does not add duplicate dependencies" do
+ checksums = checksums_section_when_existing do |c|
+ c.checksum(gem_repo2, "rack", "1.0.0")
+ end
+
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}/"
gem "rack"
@@ -1072,13 +1328,17 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
rack
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
end
it "does not add duplicate dependencies with versions" do
+ checksums = checksums_section_when_existing do |c|
+ c.checksum(gem_repo2, "rack", "1.0.0")
+ end
+
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}/"
gem "rack", "1.0"
@@ -1096,13 +1356,17 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
rack (= 1.0)
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
end
it "does not add duplicate dependencies in different groups" do
+ checksums = checksums_section_when_existing do |c|
+ c.checksum(gem_repo2, "rack", "1.0.0")
+ end
+
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}/"
gem "rack", "1.0", :group => :one
@@ -1120,14 +1384,14 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
rack (= 1.0)
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
end
it "raises if two different versions are used" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo2)}/"
gem "rack", "1.0"
gem "rack", "1.1"
@@ -1138,7 +1402,7 @@ RSpec.describe "the lockfile format" do
end
it "raises if two different sources are used" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo2)}/"
gem "rack"
gem "rack", :git => "git://hubz.com"
@@ -1149,6 +1413,10 @@ RSpec.describe "the lockfile format" do
end
it "works correctly with multiple version dependencies" do
+ checksums = checksums_section_when_existing do |c|
+ c.checksum(gem_repo2, "rack", "0.9.1")
+ end
+
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}/"
gem "rack", "> 0.9", "< 1.0"
@@ -1165,13 +1433,17 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
rack (> 0.9, < 1.0)
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
end
it "captures the Ruby version in the lockfile" do
+ checksums = checksums_section_when_existing do |c|
+ c.checksum(gem_repo2, "rack", "0.9.1")
+ end
+
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}/"
ruby '#{Gem.ruby_version}'
@@ -1189,7 +1461,7 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
rack (> 0.9, < 1.0)
-
+ #{checksums}
RUBY VERSION
#{Bundler::RubyVersion.system}
@@ -1212,7 +1484,7 @@ RSpec.describe "the lockfile format" do
rack_middleware
L
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo2)}"
gem "rack_middleware"
G
@@ -1262,7 +1534,7 @@ RSpec.describe "the lockfile format" do
indirect_dependency (1.2.3)
PLATFORMS
- #{formatted_lockfile_platforms(*["ruby", generic_local_platform].uniq)}
+ #{lockfile_platforms("ruby", generic_local_platform, defaults: [])}
DEPENDENCIES
direct_dependency
@@ -1304,7 +1576,7 @@ RSpec.describe "the lockfile format" do
#{Bundler::VERSION}
L
- cache_gems "minitest-bisect-1.6.0", "path_expander-1.1.1", :gem_repo => gem_repo4
+ cache_gems "minitest-bisect-1.6.0", "path_expander-1.1.1", gem_repo: gem_repo4
bundle :install
expect(lockfile).to eq <<~L
@@ -1371,7 +1643,7 @@ RSpec.describe "the lockfile format" do
L
bundle "install --verbose"
- expect(out).to include("re-resolving dependencies because your lock file is missing some gems")
+ expect(out).to include("re-resolving dependencies because your lock file includes \"minitest-bisect\" but not some of its dependencies")
expect(lockfile).to eq <<~L
GEM
@@ -1420,7 +1692,7 @@ RSpec.describe "the lockfile format" do
end
end
- expect { bundle "update", :all => true }.to change { File.mtime(bundled_app_lock) }
+ expect { bundle "update", all: true }.to change { File.mtime(bundled_app_lock) }
expect(File.read(bundled_app_lock)).not_to match("\r\n")
expect(the_bundle).to include_gems "rack 1.2"
end
@@ -1438,12 +1710,10 @@ RSpec.describe "the lockfile format" do
File.open(bundled_app_lock, "wb") {|f| f.puts(win_lock) }
set_lockfile_mtime_to_known_value
- expect { bundle "update", :all => true }.to change { File.mtime(bundled_app_lock) }
+ expect { bundle "update", all: true }.to change { File.mtime(bundled_app_lock) }
expect(File.read(bundled_app_lock)).to match("\r\n")
- simulate_bundler_version_when_missing_prerelease_default_gem_activation do
- expect(the_bundle).to include_gems "rack 1.2"
- end
+ expect(the_bundle).to include_gems "rack 1.2"
end
end
@@ -1464,7 +1734,7 @@ RSpec.describe "the lockfile format" do
expect do
ruby <<-RUBY
- require '#{entrypoint}'
+ require 'bundler'
Bundler.setup
RUBY
end.not_to change { File.mtime(bundled_app_lock) }
@@ -1493,7 +1763,7 @@ RSpec.describe "the lockfile format" do
#{Bundler::VERSION}
L
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo2)}/"
gem "rack"
G
diff --git a/spec/bundler/other/cli_dispatch_spec.rb b/spec/bundler/other/cli_dispatch_spec.rb
index 2d6080296f..48b285045a 100644
--- a/spec/bundler/other/cli_dispatch_spec.rb
+++ b/spec/bundler/other/cli_dispatch_spec.rb
@@ -2,19 +2,19 @@
RSpec.describe "bundle command names" do
it "work when given fully" do
- bundle "install", :raise_on_error => false
+ bundle "install", raise_on_error: false
expect(err).to eq("Could not locate Gemfile")
expect(last_command.stdboth).not_to include("Ambiguous command")
end
it "work when not ambiguous" do
- bundle "ins", :raise_on_error => false
+ bundle "ins", raise_on_error: false
expect(err).to eq("Could not locate Gemfile")
expect(last_command.stdboth).not_to include("Ambiguous command")
end
it "print a friendly error when ambiguous" do
- bundle "in", :raise_on_error => false
+ bundle "in", raise_on_error: false
expect(err).to eq("Ambiguous command in matches [info, init, inject, install]")
end
end
diff --git a/spec/bundler/other/ext_spec.rb b/spec/bundler/other/ext_spec.rb
index 9b829a2b36..4d954b474f 100644
--- a/spec/bundler/other/ext_spec.rb
+++ b/spec/bundler/other/ext_spec.rb
@@ -59,7 +59,27 @@ RSpec.describe "Gem::SourceIndex#refresh!" do
end
it "does not explode when called" do
- run "Gem.source_index.refresh!", :raise_on_error => false
- run "Gem::SourceIndex.new([]).refresh!", :raise_on_error => false
+ run "Gem.source_index.refresh!", raise_on_error: false
+ run "Gem::SourceIndex.new([]).refresh!", raise_on_error: false
+ end
+end
+
+RSpec.describe "Gem::NameTuple" do
+ describe "#initialize" do
+ it "creates a Gem::NameTuple with equality regardless of platform type" do
+ gem_platform = Gem::NameTuple.new "a", v("1"), pl("x86_64-linux")
+ str_platform = Gem::NameTuple.new "a", v("1"), "x86_64-linux"
+ expect(gem_platform).to eq(str_platform)
+ expect(gem_platform.hash).to eq(str_platform.hash)
+ expect(gem_platform.to_a).to eq(str_platform.to_a)
+ end
+ end
+
+ describe "#lock_name" do
+ it "returns the lock name" do
+ expect(Gem::NameTuple.new("a", v("1.0.0"), pl("x86_64-linux")).lock_name).to eq("a (1.0.0-x86_64-linux)")
+ expect(Gem::NameTuple.new("a", v("1.0.0"), "ruby").lock_name).to eq("a (1.0.0)")
+ expect(Gem::NameTuple.new("a", v("1.0.0")).lock_name).to eq("a (1.0.0)")
+ end
end
end
diff --git a/spec/bundler/other/major_deprecation_spec.rb b/spec/bundler/other/major_deprecation_spec.rb
index 7b7ceb4586..939b68a0bb 100644
--- a/spec/bundler/other/major_deprecation_spec.rb
+++ b/spec/bundler/other/major_deprecation_spec.rb
@@ -17,14 +17,14 @@ RSpec.describe "major deprecations" do
bundle "exec ruby -e #{source.dump}"
end
- it "is deprecated in favor of .unbundled_env", :bundler => "< 3" do
+ it "is deprecated in favor of .unbundled_env", bundler: "< 3" do
expect(deprecations).to include \
"`Bundler.clean_env` has been deprecated in favor of `Bundler.unbundled_env`. " \
"If you instead want the environment before bundler was originally loaded, use `Bundler.original_env` " \
"(called at -e:1)"
end
- pending "is removed and shows a helpful error message about it", :bundler => "3"
+ pending "is removed and shows a helpful error message about it", bundler: "3"
end
describe ".with_clean_env" do
@@ -33,7 +33,7 @@ RSpec.describe "major deprecations" do
bundle "exec ruby -e #{source.dump}"
end
- it "is deprecated in favor of .unbundled_env", :bundler => "< 3" do
+ it "is deprecated in favor of .unbundled_env", bundler: "< 3" do
expect(deprecations).to include(
"`Bundler.with_clean_env` has been deprecated in favor of `Bundler.with_unbundled_env`. " \
"If you instead want the environment before bundler was originally loaded, use `Bundler.with_original_env` " \
@@ -41,7 +41,7 @@ RSpec.describe "major deprecations" do
)
end
- pending "is removed and shows a helpful error message about it", :bundler => "3"
+ pending "is removed and shows a helpful error message about it", bundler: "3"
end
describe ".clean_system" do
@@ -50,7 +50,7 @@ RSpec.describe "major deprecations" do
bundle "exec ruby -e #{source.dump}"
end
- it "is deprecated in favor of .unbundled_system", :bundler => "< 3" do
+ it "is deprecated in favor of .unbundled_system", bundler: "< 3" do
expect(deprecations).to include(
"`Bundler.clean_system` has been deprecated in favor of `Bundler.unbundled_system`. " \
"If you instead want to run the command in the environment before bundler was originally loaded, use `Bundler.original_system` " \
@@ -58,7 +58,7 @@ RSpec.describe "major deprecations" do
)
end
- pending "is removed and shows a helpful error message about it", :bundler => "3"
+ pending "is removed and shows a helpful error message about it", bundler: "3"
end
describe ".clean_exec" do
@@ -67,7 +67,7 @@ RSpec.describe "major deprecations" do
bundle "exec ruby -e #{source.dump}"
end
- it "is deprecated in favor of .unbundled_exec", :bundler => "< 3" do
+ it "is deprecated in favor of .unbundled_exec", bundler: "< 3" do
expect(deprecations).to include(
"`Bundler.clean_exec` has been deprecated in favor of `Bundler.unbundled_exec`. " \
"If you instead want to exec to a command in the environment before bundler was originally loaded, use `Bundler.original_exec` " \
@@ -75,7 +75,7 @@ RSpec.describe "major deprecations" do
)
end
- pending "is removed and shows a helpful error message about it", :bundler => "3"
+ pending "is removed and shows a helpful error message about it", bundler: "3"
end
describe ".environment" do
@@ -84,29 +84,29 @@ RSpec.describe "major deprecations" do
bundle "exec ruby -e #{source.dump}"
end
- it "is deprecated in favor of .load", :bundler => "< 3" do
+ it "is deprecated in favor of .load", bundler: "< 3" do
expect(deprecations).to include "Bundler.environment has been removed in favor of Bundler.load (called at -e:1)"
end
- pending "is removed and shows a helpful error message about it", :bundler => "3"
+ pending "is removed and shows a helpful error message about it", bundler: "3"
end
end
describe "bundle exec --no-keep-file-descriptors" do
before do
- bundle "exec --no-keep-file-descriptors -e 1", :raise_on_error => false
+ bundle "exec --no-keep-file-descriptors -e 1", raise_on_error: false
end
- it "is deprecated", :bundler => "< 3" do
+ it "is deprecated", bundler: "< 3" do
expect(deprecations).to include "The `--no-keep-file-descriptors` has been deprecated. `bundle exec` no longer mess with your file descriptors. Close them in the exec'd script if you need to"
end
- pending "is removed and shows a helpful error message about it", :bundler => "3"
+ pending "is removed and shows a helpful error message about it", bundler: "3"
end
describe "bundle update --quiet" do
it "does not print any deprecations" do
- bundle :update, :quiet => true, :raise_on_error => false
+ bundle :update, quiet: true, raise_on_error: false
expect(deprecations).to be_empty
end
end
@@ -118,19 +118,19 @@ RSpec.describe "major deprecations" do
gem "rack"
G
- bundle "check --path vendor/bundle", :raise_on_error => false
+ bundle "check --path vendor/bundle", raise_on_error: false
end
- it "should print a deprecation warning", :bundler => "< 3" do
+ it "should print a deprecation warning", bundler: "< 3" do
expect(deprecations).to include(
"The `--path` flag is deprecated because it relies on being " \
"remembered across bundler invocations, which bundler will no " \
- "longer do in future versions. Instead please use `bundle config set --local " \
+ "longer do in future versions. Instead please use `bundle config set " \
"path 'vendor/bundle'`, and stop using this flag"
)
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "3"
end
context "bundle check --path=" do
@@ -140,19 +140,19 @@ RSpec.describe "major deprecations" do
gem "rack"
G
- bundle "check --path=vendor/bundle", :raise_on_error => false
+ bundle "check --path=vendor/bundle", raise_on_error: false
end
- it "should print a deprecation warning", :bundler => "< 3" do
+ it "should print a deprecation warning", bundler: "< 3" do
expect(deprecations).to include(
"The `--path` flag is deprecated because it relies on being " \
"remembered across bundler invocations, which bundler will no " \
- "longer do in future versions. Instead please use `bundle config set --local " \
+ "longer do in future versions. Instead please use `bundle config set " \
"path 'vendor/bundle'`, and stop using this flag"
)
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "3"
end
context "bundle cache --all" do
@@ -162,10 +162,10 @@ RSpec.describe "major deprecations" do
gem "rack"
G
- bundle "cache --all", :raise_on_error => false
+ bundle "cache --all", raise_on_error: false
end
- it "should print a deprecation warning", :bundler => "< 3" do
+ it "should print a deprecation warning", bundler: "< 3" do
expect(deprecations).to include(
"The `--all` flag is deprecated because it relies on being " \
"remembered across bundler invocations, which bundler will no " \
@@ -174,7 +174,7 @@ RSpec.describe "major deprecations" do
)
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "3"
end
context "bundle cache --path" do
@@ -184,10 +184,10 @@ RSpec.describe "major deprecations" do
gem "rack"
G
- bundle "cache --path foo", :raise_on_error => false
+ bundle "cache --path foo", raise_on_error: false
end
- it "should print a deprecation warning", :bundler => "< 3" do
+ it "should print a deprecation warning", bundler: "< 3" do
expect(deprecations).to include(
"The `--path` flag is deprecated because its semantics are unclear. " \
"Use `bundle config cache_path` to configure the path of your cache of gems, " \
@@ -196,7 +196,7 @@ RSpec.describe "major deprecations" do
)
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "3"
end
describe "bundle config" do
@@ -205,11 +205,11 @@ RSpec.describe "major deprecations" do
bundle "config"
end
- it "warns", :bundler => "3" do
+ it "warns", bundler: "3" do
expect(deprecations).to include("Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle config list` instead.")
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "3"
end
describe "old get interface" do
@@ -217,11 +217,11 @@ RSpec.describe "major deprecations" do
bundle "config waka"
end
- it "warns", :bundler => "3" do
+ it "warns", bundler: "3" do
expect(deprecations).to include("Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle config get waka` instead.")
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "3"
end
describe "old set interface" do
@@ -229,11 +229,11 @@ RSpec.describe "major deprecations" do
bundle "config waka wakapun"
end
- it "warns", :bundler => "3" do
+ it "warns", bundler: "3" do
expect(deprecations).to include("Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle config set waka wakapun` instead.")
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "3"
end
describe "old set interface with --local" do
@@ -241,11 +241,11 @@ RSpec.describe "major deprecations" do
bundle "config --local waka wakapun"
end
- it "warns", :bundler => "3" do
+ it "warns", bundler: "3" do
expect(deprecations).to include("Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle config set --local waka wakapun` instead.")
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "3"
end
describe "old set interface with --global" do
@@ -253,11 +253,11 @@ RSpec.describe "major deprecations" do
bundle "config --global waka wakapun"
end
- it "warns", :bundler => "3" do
+ it "warns", bundler: "3" do
expect(deprecations).to include("Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle config set --global waka wakapun` instead.")
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "3"
end
describe "old unset interface" do
@@ -265,11 +265,11 @@ RSpec.describe "major deprecations" do
bundle "config --delete waka"
end
- it "warns", :bundler => "3" do
+ it "warns", bundler: "3" do
expect(deprecations).to include("Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle config unset waka` instead.")
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "3"
end
describe "old unset interface with --local" do
@@ -277,11 +277,11 @@ RSpec.describe "major deprecations" do
bundle "config --delete --local waka"
end
- it "warns", :bundler => "3" do
+ it "warns", bundler: "3" do
expect(deprecations).to include("Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle config unset --local waka` instead.")
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "3"
end
describe "old unset interface with --global" do
@@ -289,11 +289,11 @@ RSpec.describe "major deprecations" do
bundle "config --delete --global waka"
end
- it "warns", :bundler => "3" do
+ it "warns", bundler: "3" do
expect(deprecations).to include("Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle config unset --global waka` instead.")
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "3"
end
end
@@ -305,12 +305,12 @@ RSpec.describe "major deprecations" do
G
end
- it "warns when no options are given", :bundler => "3" do
+ it "warns when no options are given", bundler: "3" do
bundle "update"
expect(deprecations).to include("Pass --all to `bundle update` to update everything")
end
- pending "fails with a helpful error when no options are given", :bundler => "3"
+ pending "fails with a helpful error when no options are given", bundler: "3"
it "does not warn when --all is passed" do
bundle "update --all"
@@ -320,17 +320,17 @@ RSpec.describe "major deprecations" do
describe "bundle install --binstubs" do
before do
- install_gemfile <<-G, :binstubs => true
+ install_gemfile <<-G, binstubs: true
source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
end
- it "should output a deprecation warning", :bundler => "< 3" do
+ it "should output a deprecation warning", bundler: "< 3" do
expect(deprecations).to include("The --binstubs option will be removed in favor of `bundle binstubs --all`")
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "3"
end
context "bundle install with both gems.rb and Gemfile present" do
@@ -370,16 +370,16 @@ RSpec.describe "major deprecations" do
end
{
- "clean" => ["clean", true],
- "deployment" => ["deployment", true],
- "frozen" => ["frozen", true],
- "no-deployment" => ["deployment", false],
- "no-prune" => ["no_prune", true],
- "path" => ["path", "vendor/bundle"],
- "shebang" => ["shebang", "ruby27"],
- "system" => ["system", true],
- "without" => ["without", "development"],
- "with" => ["with", "development"],
+ "clean" => ["clean", "true"],
+ "deployment" => ["deployment", "true"],
+ "frozen" => ["frozen", "true"],
+ "no-deployment" => ["deployment", "false"],
+ "no-prune" => ["no_prune", "true"],
+ "path" => ["path", "'vendor/bundle'"],
+ "shebang" => ["shebang", "'ruby27'"],
+ "system" => ["path.system", "true"],
+ "without" => ["without", "'development'"],
+ "with" => ["with", "'development'"],
}.each do |name, expectations|
option_name, value = *expectations
flag_name = "--#{name}"
@@ -390,16 +390,16 @@ RSpec.describe "major deprecations" do
bundle "install #{flag_name} #{value}"
end
- it "should print a deprecation warning", :bundler => "< 3" do
+ it "should print a deprecation warning", bundler: "< 3" do
expect(deprecations).to include(
"The `#{flag_name}` flag is deprecated because it relies on " \
"being remembered across bundler invocations, which bundler " \
"will no longer do in future versions. Instead please use " \
- "`bundle config set --local #{option_name} '#{value}'`, and stop using this flag"
+ "`bundle config set #{option_name} #{value}`, and stop using this flag"
)
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "3"
end
end
end
@@ -412,7 +412,7 @@ RSpec.describe "major deprecations" do
G
end
- it "shows a deprecation", :bundler => "< 3" do
+ it "shows a deprecation", bundler: "< 3" do
expect(deprecations).to include(
"Your Gemfile contains multiple global sources. " \
"Using `source` more than once without a block is a security risk, and " \
@@ -421,7 +421,7 @@ RSpec.describe "major deprecations" do
)
end
- it "doesn't show lockfile deprecations if there's a lockfile", :bundler => "< 3" do
+ it "doesn't show lockfile deprecations if there's a lockfile", bundler: "< 3" do
bundle "install"
expect(deprecations).to include(
@@ -449,7 +449,7 @@ RSpec.describe "major deprecations" do
)
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "3"
end
context "bundle install in frozen mode with a lockfile with a single rubygems section with multiple remotes" do
@@ -485,13 +485,13 @@ RSpec.describe "major deprecations" do
bundle "config set --local frozen true"
end
- it "shows a deprecation", :bundler => "< 3" do
+ it "shows a deprecation", bundler: "< 3" do
bundle "install"
expect(deprecations).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure. Make sure you run `bundle install` in non frozen mode and commit the result to make your lockfile secure.")
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "3"
end
context "when Bundler.setup is run in a ruby script" do
@@ -503,7 +503,7 @@ RSpec.describe "major deprecations" do
G
ruby <<-RUBY
- require '#{entrypoint}'
+ require 'bundler'
Bundler.setup
Bundler.setup
@@ -519,19 +519,19 @@ RSpec.describe "major deprecations" do
context "when `bundler/deployment` is required in a ruby script" do
before do
- ruby(<<-RUBY, :env => env_for_missing_prerelease_default_gem_activation)
+ ruby <<-RUBY
require 'bundler/deployment'
RUBY
end
- it "should print a capistrano deprecation warning", :bundler => "< 3" do
+ it "should print a capistrano deprecation warning", bundler: "< 3" do
expect(deprecations).to include("Bundler no longer integrates " \
"with Capistrano, but Capistrano provides " \
"its own integration with Bundler via the " \
"capistrano-bundler gem. Use it instead.")
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "3"
end
context "bundle show" do
@@ -547,11 +547,11 @@ RSpec.describe "major deprecations" do
bundle "show --outdated"
end
- it "prints a deprecation warning informing about its removal", :bundler => "< 3" do
+ it "prints a deprecation warning informing about its removal", bundler: "< 3" do
expect(deprecations).to include("the `--outdated` flag to `bundle show` was undocumented and will be removed without replacement")
end
- pending "fails with a helpful message", :bundler => "3"
+ pending "fails with a helpful message", bundler: "3"
end
end
@@ -564,27 +564,27 @@ RSpec.describe "major deprecations" do
end
context "with --install" do
- it "shows a deprecation warning", :bundler => "< 3" do
+ it "shows a deprecation warning", bundler: "< 3" do
bundle "remove rack --install"
expect(err).to include "[DEPRECATED] The `--install` flag has been deprecated. `bundle install` is triggered by default."
end
- pending "fails with a helpful message", :bundler => "3"
+ pending "fails with a helpful message", bundler: "3"
end
end
context "bundle console" do
before do
- bundle "console", :raise_on_error => false
+ bundle "console", raise_on_error: false
end
- it "prints a deprecation warning", :bundler => "< 3" do
+ it "prints a deprecation warning", bundler: "< 3" do
expect(deprecations).to include \
"bundle console will be replaced by `bin/console` generated by `bundle gem <name>`"
end
- pending "fails with a helpful message", :bundler => "3"
+ pending "fails with a helpful message", bundler: "3"
end
context "bundle viz", :realworld do
@@ -594,20 +594,37 @@ RSpec.describe "major deprecations" do
bundle "viz"
end
- it "prints a deprecation warning", :bundler => "< 3" do
+ it "prints a deprecation warning", bundler: "< 3" do
expect(deprecations).to include "The `viz` command has been renamed to `graph` and moved to a plugin. See https://github.com/rubygems/bundler-graph"
end
- pending "fails with a helpful message", :bundler => "3"
+ pending "fails with a helpful message", bundler: "3"
+ end
+
+ context "bundle plugin install --local_git" do
+ before do
+ build_git "foo" do |s|
+ s.write "plugins.rb"
+ end
+ end
+
+ it "prints a deprecation warning", bundler: "< 3" do
+ bundle "plugin install foo --local_git #{lib_path("foo-1.0")}"
+
+ expect(out).to include("Installed plugin foo")
+ expect(deprecations).to include "--local_git is deprecated, use --git"
+ end
+
+ pending "fails with a helpful message", bundler: "3"
end
describe "deprecating rubocop", :readline do
context "bundle gem --rubocop" do
before do
- bundle "gem my_new_gem --rubocop", :raise_on_error => false
+ bundle "gem my_new_gem --rubocop", raise_on_error: false
end
- it "prints a deprecation warning", :bundler => "< 3" do
+ it "prints a deprecation warning", bundler: "< 3" do
expect(deprecations).to include \
"--rubocop is deprecated, use --linter=rubocop"
end
@@ -615,10 +632,10 @@ RSpec.describe "major deprecations" do
context "bundle gem --no-rubocop" do
before do
- bundle "gem my_new_gem --no-rubocop", :raise_on_error => false
+ bundle "gem my_new_gem --no-rubocop", raise_on_error: false
end
- it "prints a deprecation warning", :bundler => "< 3" do
+ it "prints a deprecation warning", bundler: "< 3" do
expect(deprecations).to include \
"--no-rubocop is deprecated, use --linter"
end
@@ -626,10 +643,10 @@ RSpec.describe "major deprecations" do
context "bundle gem with gem.rubocop set to true" do
before do
- bundle "gem my_new_gem", :env => { "BUNDLE_GEM__RUBOCOP" => "true" }, :raise_on_error => false
+ bundle "gem my_new_gem", env: { "BUNDLE_GEM__RUBOCOP" => "true" }, raise_on_error: false
end
- it "prints a deprecation warning", :bundler => "< 3" do
+ it "prints a deprecation warning", bundler: "< 3" do
expect(deprecations).to include \
"config gem.rubocop is deprecated; we've updated your config to use gem.linter instead"
end
@@ -637,10 +654,10 @@ RSpec.describe "major deprecations" do
context "bundle gem with gem.rubocop set to false" do
before do
- bundle "gem my_new_gem", :env => { "BUNDLE_GEM__RUBOCOP" => "false" }, :raise_on_error => false
+ bundle "gem my_new_gem", env: { "BUNDLE_GEM__RUBOCOP" => "false" }, raise_on_error: false
end
- it "prints a deprecation warning", :bundler => "< 3" do
+ it "prints a deprecation warning", bundler: "< 3" do
expect(deprecations).to include \
"config gem.rubocop is deprecated; we've updated your config to use gem.linter instead"
end
diff --git a/spec/bundler/plugins/command_spec.rb b/spec/bundler/plugins/command_spec.rb
index 3a7adf4b48..af132d6550 100644
--- a/spec/bundler/plugins/command_spec.rb
+++ b/spec/bundler/plugins/command_spec.rb
@@ -69,7 +69,7 @@ RSpec.describe "command plugins" do
end
end
- bundle "plugin install copycat --source #{file_uri_for(gem_repo2)}", :raise_on_error => false
+ bundle "plugin install copycat --source #{file_uri_for(gem_repo2)}", raise_on_error: false
expect(out).not_to include("Installed plugin copycat")
diff --git a/spec/bundler/plugins/hook_spec.rb b/spec/bundler/plugins/hook_spec.rb
index 72feb14d84..f6ee0ba210 100644
--- a/spec/bundler/plugins/hook_spec.rb
+++ b/spec/bundler/plugins/hook_spec.rb
@@ -106,4 +106,103 @@ RSpec.describe "hook plugins" do
expect(out).to include "installed gem rack : installed"
end
end
+
+ context "before-require-all hook" do
+ before do
+ build_repo2 do
+ build_plugin "before-require-all-plugin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_BEFORE_REQUIRE_ALL do |deps|
+ puts "gems to be required \#{deps.map(&:name).join(", ")}"
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install before-require-all-plugin --source #{file_uri_for(gem_repo2)}"
+ end
+
+ it "runs before all rubygems are required" do
+ install_gemfile_and_bundler_require
+ expect(out).to include "gems to be required rake, rack"
+ end
+ end
+
+ context "before-require hook" do
+ before do
+ build_repo2 do
+ build_plugin "before-require-plugin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_BEFORE_REQUIRE do |dep|
+ puts "requiring gem \#{dep.name}"
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install before-require-plugin --source #{file_uri_for(gem_repo2)}"
+ end
+
+ it "runs before each rubygem is required" do
+ install_gemfile_and_bundler_require
+ expect(out).to include "requiring gem rake"
+ expect(out).to include "requiring gem rack"
+ end
+ end
+
+ context "after-require-all hook" do
+ before do
+ build_repo2 do
+ build_plugin "after-require-all-plugin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_AFTER_REQUIRE_ALL do |deps|
+ puts "required gems \#{deps.map(&:name).join(", ")}"
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install after-require-all-plugin --source #{file_uri_for(gem_repo2)}"
+ end
+
+ it "runs after all rubygems are required" do
+ install_gemfile_and_bundler_require
+ expect(out).to include "required gems rake, rack"
+ end
+ end
+
+ context "after-require hook" do
+ before do
+ build_repo2 do
+ build_plugin "after-require-plugin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_AFTER_REQUIRE do |dep|
+ puts "required gem \#{dep.name}"
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install after-require-plugin --source #{file_uri_for(gem_repo2)}"
+ end
+
+ it "runs after each rubygem is required" do
+ install_gemfile_and_bundler_require
+ expect(out).to include "required gem rake"
+ expect(out).to include "required gem rack"
+ end
+ end
+
+ def install_gemfile_and_bundler_require
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rake"
+ gem "rack"
+ G
+
+ ruby <<-RUBY
+ require "bundler"
+ Bundler.require
+ RUBY
+ end
end
diff --git a/spec/bundler/plugins/install_spec.rb b/spec/bundler/plugins/install_spec.rb
index aec131f2ee..20c2f1fd26 100644
--- a/spec/bundler/plugins/install_spec.rb
+++ b/spec/bundler/plugins/install_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe "bundler plugin install" do
end
it "shows proper message when gem in not found in the source" do
- bundle "plugin install no-foo --source #{file_uri_for(gem_repo1)}", :raise_on_error => false
+ bundle "plugin install no-foo --source #{file_uri_for(gem_repo1)}", raise_on_error: false
expect(err).to include("Could not find")
plugin_should_not_be_installed("no-foo")
@@ -23,14 +23,14 @@ RSpec.describe "bundler plugin install" do
end
it "installs from rubygems source in frozen mode" do
- bundle "plugin install foo --source #{file_uri_for(gem_repo2)}", :env => { "BUNDLE_DEPLOYMENT" => "true" }
+ bundle "plugin install foo --source #{file_uri_for(gem_repo2)}", env: { "BUNDLE_DEPLOYMENT" => "true" }
expect(out).to include("Installed plugin foo")
plugin_should_be_installed("foo")
end
it "installs from sources configured as Gem.sources without any flags" do
- bundle "plugin install foo", :env => { "BUNDLER_SPEC_GEM_SOURCES" => file_uri_for(gem_repo2).to_s }
+ bundle "plugin install foo", env: { "BUNDLER_SPEC_GEM_SOURCES" => file_uri_for(gem_repo2).to_s }
expect(out).to include("Installed plugin foo")
plugin_should_be_installed("foo")
@@ -92,20 +92,22 @@ RSpec.describe "bundler plugin install" do
expect(out).to include("Using foo 1.1")
end
- it "installs when --branch specified" do
- bundle "plugin install foo --branch main --source #{file_uri_for(gem_repo2)}"
+ it "raises an error when when --branch specified" do
+ bundle "plugin install foo --branch main --source #{file_uri_for(gem_repo2)}", raise_on_error: false
- expect(out).to include("Installed plugin foo")
+ expect(out).not_to include("Installed plugin foo")
+
+ expect(err).to include("--branch can only be used with git sources")
end
- it "installs when --ref specified" do
- bundle "plugin install foo --ref v1.2.3 --source #{file_uri_for(gem_repo2)}"
+ it "raises an error when --ref specified" do
+ bundle "plugin install foo --ref v1.2.3 --source #{file_uri_for(gem_repo2)}", raise_on_error: false
- expect(out).to include("Installed plugin foo")
+ expect(err).to include("--ref can only be used with git sources")
end
it "raises error when both --branch and --ref options are specified" do
- bundle "plugin install foo --source #{file_uri_for(gem_repo2)} --branch main --ref v1.2.3", :raise_on_error => false
+ bundle "plugin install foo --source #{file_uri_for(gem_repo2)} --branch main --ref v1.2.3", raise_on_error: false
expect(out).not_to include("Installed plugin foo")
@@ -152,7 +154,7 @@ RSpec.describe "bundler plugin install" do
build_gem "charlie"
end
- bundle "plugin install charlie --source #{file_uri_for(gem_repo2)}", :raise_on_error => false
+ bundle "plugin install charlie --source #{file_uri_for(gem_repo2)}", raise_on_error: false
expect(err).to include("Failed to install plugin `charlie`, due to Bundler::Plugin::MalformattedPlugin (plugins.rb was not found in the plugin.)")
@@ -171,7 +173,7 @@ RSpec.describe "bundler plugin install" do
end
end
- bundle "plugin install chaplin --source #{file_uri_for(gem_repo2)}", :raise_on_error => false
+ bundle "plugin install chaplin --source #{file_uri_for(gem_repo2)}", raise_on_error: false
expect(global_plugin_gem("chaplin-1.0")).not_to be_directory
@@ -196,20 +198,58 @@ RSpec.describe "bundler plugin install" do
s.write "plugins.rb"
end
- bundle "plugin install foo --local_git #{lib_path("foo-1.0")}"
+ bundle "plugin install foo --git #{lib_path("foo-1.0")}"
expect(out).to include("Installed plugin foo")
plugin_should_be_installed("foo")
end
- it "raises an error when both git and local git sources are specified" do
- bundle "plugin install foo --local_git /phony/path/project --git git@gitphony.com:/repo/project", :raise_on_error => false
+ it "raises an error when both git and local git sources are specified", bundler: "< 3" do
+ bundle "plugin install foo --git /phony/path/project --local_git git@gitphony.com:/repo/project", raise_on_error: false
expect(exitstatus).not_to eq(0)
expect(err).to eq("Remote and local plugin git sources can't be both specified")
end
end
+ context "path plugins" do
+ it "installs from a path source" do
+ build_lib "path_plugin" do |s|
+ s.write "plugins.rb"
+ end
+ bundle "plugin install path_plugin --path #{lib_path("path_plugin-1.0")}"
+
+ expect(out).to include("Installed plugin path_plugin")
+ plugin_should_be_installed("path_plugin")
+ end
+
+ it "installs from a relative path source" do
+ build_lib "path_plugin" do |s|
+ s.write "plugins.rb"
+ end
+ path = lib_path("path_plugin-1.0").relative_path_from(bundled_app)
+ bundle "plugin install path_plugin --path #{path}"
+
+ expect(out).to include("Installed plugin path_plugin")
+ plugin_should_be_installed("path_plugin")
+ end
+
+ it "installs from a relative path source when inside an app" do
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
+ gemfile ""
+
+ build_lib "ga-plugin" do |s|
+ s.write "plugins.rb"
+ end
+
+ path = lib_path("ga-plugin-1.0").relative_path_from(bundled_app)
+ bundle "plugin install ga-plugin --path #{path}"
+
+ plugin_should_be_installed("ga-plugin")
+ expect(local_plugin_gem("foo-1.0")).not_to be_directory
+ end
+ end
+
context "Gemfile eval" do
before do
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
@@ -279,6 +319,21 @@ RSpec.describe "bundler plugin install" do
plugin_should_be_installed("ga-plugin")
end
+ it "accepts relative path sources" do
+ build_lib "ga-plugin" do |s|
+ s.write "plugins.rb"
+ end
+
+ path = lib_path("ga-plugin-1.0").relative_path_from(bundled_app)
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ plugin 'ga-plugin', :path => "#{path}"
+ G
+
+ expect(out).to include("Installed plugin ga-plugin")
+ plugin_should_be_installed("ga-plugin")
+ end
+
context "in deployment mode" do
it "installs plugins" do
install_gemfile <<-G
@@ -314,7 +369,7 @@ RSpec.describe "bundler plugin install" do
end
RUBY
- ruby code, :env => { "BUNDLER_VERSION" => Bundler::VERSION }
+ ruby code, env: { "BUNDLER_VERSION" => Bundler::VERSION }
expect(local_plugin_gem("foo-1.0", "plugins.rb")).to exist
end
end
@@ -364,7 +419,7 @@ RSpec.describe "bundler plugin install" do
end
# outside the app
- bundle "plugin install fubar --source #{file_uri_for(gem_repo2)}", :dir => tmp
+ bundle "plugin install fubar --source #{file_uri_for(gem_repo2)}", dir: tmp
end
it "inside the app takes precedence over global plugin" do
@@ -373,7 +428,7 @@ RSpec.describe "bundler plugin install" do
end
it "outside the app global plugin is used" do
- bundle "shout", :dir => tmp
+ bundle "shout", dir: tmp
expect(out).to eq("global_one")
end
end
diff --git a/spec/bundler/plugins/source/example_spec.rb b/spec/bundler/plugins/source/example_spec.rb
index 9d153b6063..e569f3e415 100644
--- a/spec/bundler/plugins/source/example_spec.rb
+++ b/spec/bundler/plugins/source/example_spec.rb
@@ -70,6 +70,10 @@ RSpec.describe "real source plugins" do
it "writes to lock file" do
bundle "install"
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "a-path-gem", "1.0"
+ end
+
expect(lockfile).to eq <<~G
PLUGIN SOURCE
remote: #{lib_path("a-path-gem-1.0")}
@@ -86,7 +90,7 @@ RSpec.describe "real source plugins" do
DEPENDENCIES
a-path-gem!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -336,6 +340,10 @@ RSpec.describe "real source plugins" do
revision = revision_for(lib_path("ma-gitp-gem-1.0"))
bundle "install"
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "ma-gitp-gem", "1.0"
+ end
+
expect(lockfile).to eq <<~G
PLUGIN SOURCE
remote: #{file_uri_for(lib_path("ma-gitp-gem-1.0"))}
@@ -353,7 +361,7 @@ RSpec.describe "real source plugins" do
DEPENDENCIES
ma-gitp-gem!
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
@@ -413,7 +421,7 @@ RSpec.describe "real source plugins" do
end
it "updates the deps on change in gemfile" do
- update_git "ma-gitp-gem", "1.1", :path => lib_path("ma-gitp-gem-1.0"), :gemspec => true
+ update_git "ma-gitp-gem", "1.1", path: lib_path("ma-gitp-gem-1.0"), gemspec: true
gemfile <<-G
source "#{file_uri_for(gem_repo2)}" # plugin source
source "#{file_uri_for(lib_path("ma-gitp-gem-1.0"))}", :type => :gitp do
diff --git a/spec/bundler/plugins/uninstall_spec.rb b/spec/bundler/plugins/uninstall_spec.rb
index 8180241911..555c6a7002 100644
--- a/spec/bundler/plugins/uninstall_spec.rb
+++ b/spec/bundler/plugins/uninstall_spec.rb
@@ -30,6 +30,31 @@ RSpec.describe "bundler plugin uninstall" do
plugin_should_not_be_installed("foo")
end
+ it "doesn't wipe out path plugins" do
+ build_lib "path_plugin" do |s|
+ s.write "plugins.rb"
+ end
+ path = lib_path("path_plugin-1.0")
+ expect(path).to be_a_directory
+
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
+
+ install_gemfile <<-G
+ source '#{file_uri_for(gem_repo2)}'
+ plugin 'path_plugin', :path => "#{path}"
+ gem 'rack', '1.0.0'
+ G
+
+ plugin_should_be_installed("path_plugin")
+ expect(Bundler::Plugin.index.plugin_path("path_plugin")).to eq path
+
+ bundle "plugin uninstall path_plugin"
+ expect(out).to include("Uninstalled plugin path_plugin")
+ plugin_should_not_be_installed("path_plugin")
+ # the actual gem still exists though
+ expect(path).to be_a_directory
+ end
+
describe "with --all" do
it "uninstalls all installed plugins" do
bundle "plugin install foo kung-foo --source #{file_uri_for(gem_repo2)}"
diff --git a/spec/bundler/quality_spec.rb b/spec/bundler/quality_spec.rb
index a98815158e..7cdb993017 100644
--- a/spec/bundler/quality_spec.rb
+++ b/spec/bundler/quality_spec.rb
@@ -40,16 +40,14 @@ RSpec.describe "The library itself" do
"#{filename} has spaces on the EOL on lines #{failing_lines.join(", ")}"
end
- def check_for_straneous_quotes(filename)
- return if File.expand_path(filename) == __FILE__
-
+ def check_for_extraneous_quotes(filename)
failing_lines = []
each_line(filename) do |line, number|
- failing_lines << number + 1 if /’/.match?(line)
+ failing_lines << number + 1 if /\u{2019}/.match?(line)
end
return if failing_lines.empty?
- "#{filename} has an straneous quote on lines #{failing_lines.join(", ")}"
+ "#{filename} has an extraneous quote on lines #{failing_lines.join(", ")}"
end
def check_for_expendable_words(filename)
@@ -96,12 +94,12 @@ RSpec.describe "The library itself" do
expect(error_messages.compact).to be_well_formed
end
- it "has no estraneous quotes" do
+ it "has no extraneous quotes" do
exempt = /vendor|vcr_cassettes|LICENSE|rbreadline\.diff/
error_messages = []
tracked_files.each do |filename|
next if filename&.match?(exempt)
- error_messages << check_for_straneous_quotes(filename)
+ error_messages << check_for_extraneous_quotes(filename)
end
expect(error_messages.compact).to be_well_formed
end
@@ -193,9 +191,10 @@ RSpec.describe "The library itself" do
end
it "ships the correct set of files" do
- git_list = git_ls_files(ruby_core? ? "lib/bundler lib/bundler.rb libexec/bundle*" : "lib exe CHANGELOG.md LICENSE.md README.md bundler.gemspec")
+ git_list = tracked_files.reject {|f| f.start_with?("spec/") }
gem_list = loaded_gemspec.files
+ gem_list.map! {|f| f.sub(%r{\Aexe/}, "libexec/") } if ruby_core?
expect(git_list).to match_array(gem_list)
end
@@ -240,6 +239,6 @@ RSpec.describe "The library itself" do
private
def each_line(filename, &block)
- File.readlines(filename, :encoding => "UTF-8").each_with_index(&block)
+ File.readlines(filename, encoding: "UTF-8").each_with_index(&block)
end
end
diff --git a/spec/bundler/realworld/dependency_api_spec.rb b/spec/bundler/realworld/dependency_api_spec.rb
index 14f99bd262..ee5c0e3d0a 100644
--- a/spec/bundler/realworld/dependency_api_spec.rb
+++ b/spec/bundler/realworld/dependency_api_spec.rb
@@ -2,7 +2,7 @@
require_relative "../support/silent_logger"
-RSpec.describe "gemcutter's dependency API", :realworld => true do
+RSpec.describe "gemcutter's dependency API", realworld: true do
context "when Gemcutter API takes too long to respond" do
before do
require_rack
@@ -13,12 +13,12 @@ RSpec.describe "gemcutter's dependency API", :realworld => true do
require_relative "../support/artifice/endpoint_timeout"
@t = Thread.new do
- server = Rack::Server.start(:app => EndpointTimeout,
- :Host => "0.0.0.0",
- :Port => port,
- :server => "webrick",
- :AccessLog => [],
- :Logger => Spec::SilentLogger.new)
+ server = Rack::Server.start(app: EndpointTimeout,
+ Host: "0.0.0.0",
+ Port: port,
+ server: "webrick",
+ AccessLog: [],
+ Logger: Spec::SilentLogger.new)
server.start
end
@t.run
@@ -34,7 +34,7 @@ RSpec.describe "gemcutter's dependency API", :realworld => true do
end
it "times out and falls back on the modern index" do
- install_gemfile <<-G, :artifice => nil
+ install_gemfile <<-G, artifice: nil
source "#{@server_uri}"
gem "rack"
G
diff --git a/spec/bundler/realworld/double_check_spec.rb b/spec/bundler/realworld/double_check_spec.rb
index d7f28d10bb..0741560395 100644
--- a/spec/bundler/realworld/double_check_spec.rb
+++ b/spec/bundler/realworld/double_check_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.describe "double checking sources", :realworld => true do
+RSpec.describe "double checking sources", realworld: true do
it "finds already-installed gems" do
create_file("rails.gemspec", <<-RUBY)
Gem::Specification.new do |s|
@@ -25,9 +25,9 @@ RSpec.describe "double checking sources", :realworld => true do
RUBY
cmd = <<-RUBY
- require "#{entrypoint}"
+ require "bundler"
require "#{spec_dir}/support/artifice/vcr"
- require "#{entrypoint}/inline"
+ require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
gem "rails", path: "."
diff --git a/spec/bundler/realworld/edgecases_spec.rb b/spec/bundler/realworld/edgecases_spec.rb
index 2f465b7b25..94ca3554b1 100644
--- a/spec/bundler/realworld/edgecases_spec.rb
+++ b/spec/bundler/realworld/edgecases_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.describe "real world edgecases", :realworld => true do
+RSpec.describe "real world edgecases", realworld: true do
def rubygems_version(name, requirement)
ruby <<-RUBY
require "#{spec_dir}/support/artifice/vcr"
@@ -8,9 +8,10 @@ RSpec.describe "real world edgecases", :realworld => true do
require "bundler/source/rubygems/remote"
require "bundler/fetcher"
rubygem = Bundler.ui.silence do
- source = Bundler::Source::Rubygems::Remote.new(Bundler::URI("https://rubygems.org"))
- fetcher = Bundler::Fetcher.new(source)
- index = fetcher.specs([#{name.dump}], nil)
+ remote = Bundler::Source::Rubygems::Remote.new(Gem::URI("https://rubygems.org"))
+ source = Bundler::Source::Rubygems.new
+ fetcher = Bundler::Fetcher.new(remote)
+ index = fetcher.specs([#{name.dump}], source)
requirement = Gem::Requirement.create(#{requirement.dump})
index.search(#{name.dump}).select {|spec| requirement.satisfied_by?(spec.version) }.last
end
@@ -192,13 +193,13 @@ RSpec.describe "real world edgecases", :realworld => true do
rails (~> 4.2.7.1)
L
- bundle "lock --update paperclip", :env => { "BUNDLER_VERSION" => "1.99.0" }
+ bundle "lock --update paperclip", env: { "BUNDLER_VERSION" => "1.99.0" }
expect(lockfile).to include(rubygems_version("paperclip", "~> 5.1.0"))
end
- it "outputs a helpful error message when gems have invalid gemspecs", :rubygems => "< 3.3.16" do
- install_gemfile <<-G, :standalone => true, :raise_on_error => false, :env => { "BUNDLE_FORCE_RUBY_PLATFORM" => "1" }
+ it "outputs a helpful error message when gems have invalid gemspecs", rubygems: "< 3.3.16" do
+ install_gemfile <<-G, standalone: true, raise_on_error: false, env: { "BUNDLE_FORCE_RUBY_PLATFORM" => "1" }
source 'https://rubygems.org'
gem "resque-scheduler", "2.2.0"
gem "redis-namespace", "1.6.0" # for a consistent resolution including ruby 2.3.0
@@ -208,8 +209,8 @@ RSpec.describe "real world edgecases", :realworld => true do
expect(err).to include("resque-scheduler 2.2.0 has an invalid gemspec")
end
- it "outputs a helpful warning when gems have a gemspec with invalid `require_paths`", :rubygems => ">= 3.3.16" do
- install_gemfile <<-G, :standalone => true, :env => { "BUNDLE_FORCE_RUBY_PLATFORM" => "1" }
+ it "outputs a helpful warning when gems have a gemspec with invalid `require_paths`", rubygems: ">= 3.3.16" do
+ install_gemfile <<-G, standalone: true, env: { "BUNDLE_FORCE_RUBY_PLATFORM" => "1" }
source 'https://rubygems.org'
gem "resque-scheduler", "2.2.0"
gem "redis-namespace", "1.6.0" # for a consistent resolution including ruby 2.3.0
@@ -218,140 +219,6 @@ RSpec.describe "real world edgecases", :realworld => true do
expect(err).to include("resque-scheduler 2.2.0 includes a gemspec with `require_paths` set to an array of arrays. Newer versions of this gem might've already fixed this").once
end
- it "doesn't hang on big gemfile" do
- skip "Only for ruby 2.7" unless RUBY_VERSION.start_with?("2.7")
-
- gemfile <<~G
- # frozen_string_literal: true
-
- source "https://rubygems.org"
-
- ruby "~> 2.7.7"
-
- gem "rails"
- gem "pg", ">= 0.18", "< 2.0"
- gem "goldiloader"
- gem "awesome_nested_set"
- gem "circuitbox"
- gem "passenger"
- gem "globalid"
- gem "rack-cors"
- gem "rails-pg-extras"
- gem "linear_regression_trend"
- gem "rack-protection"
- gem "pundit"
- gem "remote_ip_proxy_scrubber"
- gem "bcrypt"
- gem "searchkick"
- gem "excon"
- gem "faraday_middleware-aws-sigv4"
- gem "typhoeus"
- gem "sidekiq"
- gem "sidekiq-undertaker"
- gem "sidekiq-cron"
- gem "storext"
- gem "appsignal"
- gem "fcm"
- gem "business_time"
- gem "tzinfo"
- gem "holidays"
- gem "bigdecimal"
- gem "progress_bar"
- gem "redis"
- gem "hiredis"
- gem "state_machines"
- gem "state_machines-audit_trail"
- gem "state_machines-activerecord"
- gem "interactor"
- gem "ar_transaction_changes"
- gem "redis-rails"
- gem "seed_migration"
- gem "lograge"
- gem "graphiql-rails", group: :development
- gem "graphql"
- gem "pusher"
- gem "rbnacl"
- gem "jwt"
- gem "json-schema"
- gem "discard"
- gem "money"
- gem "strip_attributes"
- gem "validates_email_format_of"
- gem "audited"
- gem "concurrent-ruby"
- gem "with_advisory_lock"
-
- group :test do
- gem "rspec-sidekiq"
- gem "simplecov", require: false
- end
-
- group :development, :test do
- gem "byebug", platform: :mri
- gem "guard"
- gem "guard-bundler"
- gem "guard-rspec"
- gem "rb-fsevent"
- gem "rspec_junit_formatter"
- gem "rspec-collection_matchers"
- gem "rspec-rails"
- gem "rspec-retry"
- gem "state_machines-rspec"
- gem "dotenv-rails"
- gem "database_cleaner-active_record"
- gem "database_cleaner-redis"
- gem "timecop"
- end
-
- gem "factory_bot_rails"
- gem "faker"
-
- group :development do
- gem "listen"
- gem "sql_queries_count"
- gem "rubocop"
- gem "rubocop-performance"
- gem "rubocop-rspec"
- gem "rubocop-rails"
- gem "brakeman"
- gem "bundler-audit"
- gem "solargraph"
- gem "annotate"
- end
- G
-
- if Bundler.feature_flag.bundler_3_mode?
- # Conflicts on bundler version, so we count attempts differently
- bundle :lock, :env => { "DEBUG_RESOLVER" => "1" }, :raise_on_error => false
- expect(out.split("\n").grep(/backtracking to/).count).to eq(16)
- else
- bundle :lock, :env => { "DEBUG_RESOLVER" => "1" }
- expect(out).to include("Solution found after 7 attempts")
- end
- end
-
- it "doesn't hang on tricky gemfile" do
- skip "Only for ruby 2.7" unless RUBY_VERSION.start_with?("2.7")
-
- gemfile <<~G
- source 'https://rubygems.org'
-
- group :development do
- gem "puppet-module-posix-default-r2.7", '~> 0.3'
- gem "puppet-module-posix-dev-r2.7", '~> 0.3'
- gem "beaker-rspec"
- gem "beaker-puppet"
- gem "beaker-docker"
- gem "beaker-puppet_install_helper"
- gem "beaker-module_install_helper"
- end
- G
-
- bundle :lock, :env => { "DEBUG_RESOLVER" => "1" }
-
- expect(out).to include("Solution found after 6 attempts")
- end
-
it "doesn't hang on nix gemfile" do
skip "Only for ruby 3.0" unless RUBY_VERSION.start_with?("3.0")
@@ -509,7 +376,7 @@ RSpec.describe "real world edgecases", :realworld => true do
gem "zookeeper"
G
- bundle :lock, :env => { "DEBUG_RESOLVER" => "1" }
+ bundle :lock, env: { "DEBUG_RESOLVER" => "1" }
expect(out).to include("Solution found after 4 attempts")
end
diff --git a/spec/bundler/realworld/ffi_spec.rb b/spec/bundler/realworld/ffi_spec.rb
index fdefc14091..5f40b43a0f 100644
--- a/spec/bundler/realworld/ffi_spec.rb
+++ b/spec/bundler/realworld/ffi_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.describe "loading dynamically linked library on a bundle exec context", :realworld => true do
+RSpec.describe "loading dynamically linked library on a bundle exec context", realworld: true do
it "passes ENV right after argv in memory" do
create_file "foo.rb", <<~RUBY
require 'ffi'
diff --git a/spec/bundler/realworld/fixtures/warbler/Gemfile b/spec/bundler/realworld/fixtures/warbler/Gemfile
index 4fbf2d05a7..a8dbb4911c 100644
--- a/spec/bundler/realworld/fixtures/warbler/Gemfile
+++ b/spec/bundler/realworld/fixtures/warbler/Gemfile
@@ -2,6 +2,6 @@
source "https://rubygems.org"
-gem "demo", :path => "./demo"
+gem "demo", path: "./demo"
gem "jruby-jars", "~> 9.2"
gem "warbler", "~> 2.0"
diff --git a/spec/bundler/realworld/gemfile_source_header_spec.rb b/spec/bundler/realworld/gemfile_source_header_spec.rb
index 60c0055a62..45f5d0fd22 100644
--- a/spec/bundler/realworld/gemfile_source_header_spec.rb
+++ b/spec/bundler/realworld/gemfile_source_header_spec.rb
@@ -2,7 +2,7 @@
require_relative "../support/silent_logger"
-RSpec.describe "fetching dependencies with a mirrored source", :realworld => true do
+RSpec.describe "fetching dependencies with a mirrored source", realworld: true do
let(:mirror) { "https://server.example.org" }
let(:original) { "http://127.0.0.1:#{@port}" }
@@ -17,13 +17,13 @@ RSpec.describe "fetching dependencies with a mirrored source", :realworld => tru
@t.join
end
- it "sets the 'X-Gemfile-Source' header and bundles successfully" do
+ it "sets the 'X-Gemfile-Source' and 'User-Agent' headers and bundles successfully" do
gemfile <<-G
source "#{mirror}"
gem 'weakling'
G
- bundle :install, :artifice => nil
+ bundle :install, artifice: nil
expect(out).to include("Installing weakling")
expect(out).to include("Bundle complete")
@@ -40,12 +40,12 @@ RSpec.describe "fetching dependencies with a mirrored source", :realworld => tru
require_relative "../support/artifice/endpoint_mirror_source"
@t = Thread.new do
- Rack::Server.start(:app => EndpointMirrorSource,
- :Host => "0.0.0.0",
- :Port => @port,
- :server => "webrick",
- :AccessLog => [],
- :Logger => Spec::SilentLogger.new)
+ Rack::Server.start(app: EndpointMirrorSource,
+ Host: "0.0.0.0",
+ Port: @port,
+ server: "webrick",
+ AccessLog: [],
+ Logger: Spec::SilentLogger.new)
end.run
wait_for_server("127.0.0.1", @port)
diff --git a/spec/bundler/realworld/git_spec.rb b/spec/bundler/realworld/git_spec.rb
index 3d352626ea..9eff74f1c9 100644
--- a/spec/bundler/realworld/git_spec.rb
+++ b/spec/bundler/realworld/git_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.describe "github source", :realworld => true do
+RSpec.describe "github source", realworld: true do
it "properly fetches PRs" do
install_gemfile <<-G
source "https://rubygems.org"
diff --git a/spec/bundler/realworld/mirror_probe_spec.rb b/spec/bundler/realworld/mirror_probe_spec.rb
index f2ce477c10..fc97f92375 100644
--- a/spec/bundler/realworld/mirror_probe_spec.rb
+++ b/spec/bundler/realworld/mirror_probe_spec.rb
@@ -2,7 +2,7 @@
require_relative "../support/silent_logger"
-RSpec.describe "fetching dependencies with a not available mirror", :realworld => true do
+RSpec.describe "fetching dependencies with a not available mirror", realworld: true do
let(:mirror) { @mirror_uri }
let(:original) { @server_uri }
let(:server_port) { @server_port }
@@ -32,7 +32,7 @@ RSpec.describe "fetching dependencies with a not available mirror", :realworld =
gem 'weakling'
G
- bundle :install, :artifice => nil
+ bundle :install, artifice: nil
expect(out).to include("Installing weakling")
expect(out).to include("Bundle complete")
@@ -52,7 +52,7 @@ RSpec.describe "fetching dependencies with a not available mirror", :realworld =
gem 'weakling'
G
- bundle :install, :artifice => nil
+ bundle :install, artifice: nil
expect(out).to include("Installing weakling")
expect(out).to include("Bundle complete")
@@ -71,7 +71,7 @@ RSpec.describe "fetching dependencies with a not available mirror", :realworld =
gem 'weakling'
G
- bundle :install, :artifice => nil, :raise_on_error => false
+ bundle :install, artifice: nil, raise_on_error: false
expect(out).to include("Fetching source index from #{mirror}")
@@ -94,7 +94,7 @@ RSpec.describe "fetching dependencies with a not available mirror", :realworld =
gem 'weakling'
G
- bundle :install, :artifice => nil, :raise_on_error => false
+ bundle :install, artifice: nil, raise_on_error: false
expect(out).to include("Fetching source index from #{mirror}")
@@ -113,12 +113,12 @@ RSpec.describe "fetching dependencies with a not available mirror", :realworld =
require_relative "../support/artifice/endpoint"
@server_thread = Thread.new do
- Rack::Server.start(:app => Endpoint,
- :Host => host,
- :Port => @server_port,
- :server => "webrick",
- :AccessLog => [],
- :Logger => Spec::SilentLogger.new)
+ Rack::Server.start(app: Endpoint,
+ Host: host,
+ Port: @server_port,
+ server: "webrick",
+ AccessLog: [],
+ Logger: Spec::SilentLogger.new)
end.run
wait_for_server(host, @server_port)
diff --git a/spec/bundler/realworld/parallel_spec.rb b/spec/bundler/realworld/parallel_spec.rb
index a1e4f83909..b57fdfd0ee 100644
--- a/spec/bundler/realworld/parallel_spec.rb
+++ b/spec/bundler/realworld/parallel_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.describe "parallel", :realworld => true do
+RSpec.describe "parallel", realworld: true do
it "installs" do
gemfile <<-G
source "https://rubygems.org"
@@ -9,7 +9,7 @@ RSpec.describe "parallel", :realworld => true do
gem 'i18n', '~> 0.6.0' # Because 0.7+ requires Ruby 1.9.3+
G
- bundle :install, :jobs => 4, :env => { "DEBUG" => "1" }
+ bundle :install, jobs: 4, env: { "DEBUG" => "1" }
expect(out).to match(/[1-3]: /)
@@ -34,7 +34,7 @@ RSpec.describe "parallel", :realworld => true do
gem 'i18n', '~> 0.6.0' # Because 0.7+ requires Ruby 1.9.3+
G
- bundle :update, :jobs => 4, :env => { "DEBUG" => "1" }, :all => true
+ bundle :update, jobs: 4, env: { "DEBUG" => "1" }, all: true
expect(out).to match(/[1-3]: /)
@@ -51,7 +51,7 @@ RSpec.describe "parallel", :realworld => true do
gem "diff-lcs"
G
- bundle :install, :standalone => true, :jobs => 4
+ bundle :install, standalone: true, jobs: 4
ruby <<-RUBY
$:.unshift File.expand_path("bundle")
diff --git a/spec/bundler/realworld/slow_perf_spec.rb b/spec/bundler/realworld/slow_perf_spec.rb
index aa8a48fcc7..32e266ff1b 100644
--- a/spec/bundler/realworld/slow_perf_spec.rb
+++ b/spec/bundler/realworld/slow_perf_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe "bundle install with complex dependencies", :realworld => true do
+RSpec.describe "bundle install with complex dependencies", realworld: true do
it "resolves quickly" do
gemfile <<-G
source 'https://rubygems.org'
@@ -11,7 +11,8 @@ RSpec.describe "bundle install with complex dependencies", :realworld => true do
gem "mongoid", ">= 0.10.2"
G
- expect { bundle "lock" }.to take_less_than(18) # seconds
+ bundle "lock", env: { "DEBUG_RESOLVER" => "1" }
+ expect(out).to include("Solution found after 1 attempts")
end
it "resolves quickly (case 2)" do
@@ -28,6 +29,116 @@ RSpec.describe "bundle install with complex dependencies", :realworld => true do
gem 'rspec-rails'
G
- expect { bundle "lock" }.to take_less_than(30) # seconds
+ bundle "lock", env: { "DEBUG_RESOLVER" => "1" }
+ expect(out).to include("Solution found after 1 attempts")
+ end
+
+ it "resolves big gemfile quickly" do
+ gemfile <<~G
+ # frozen_string_literal: true
+
+ source "https://rubygems.org"
+
+ gem "rails"
+ gem "pg", ">= 0.18", "< 2.0"
+ gem "goldiloader"
+ gem "awesome_nested_set"
+ gem "circuitbox"
+ gem "passenger"
+ gem "globalid"
+ gem "rack-cors"
+ gem "rails-pg-extras"
+ gem "linear_regression_trend"
+ gem "rack-protection"
+ gem "pundit"
+ gem "remote_ip_proxy_scrubber"
+ gem "bcrypt"
+ gem "searchkick"
+ gem "excon"
+ gem "faraday_middleware-aws-sigv4"
+ gem "typhoeus"
+ gem "sidekiq"
+ gem "sidekiq-undertaker"
+ gem "sidekiq-cron"
+ gem "storext"
+ gem "appsignal"
+ gem "fcm"
+ gem "business_time"
+ gem "tzinfo"
+ gem "holidays"
+ gem "bigdecimal"
+ gem "progress_bar"
+ gem "redis"
+ gem "hiredis"
+ gem "state_machines"
+ gem "state_machines-audit_trail"
+ gem "state_machines-activerecord"
+ gem "interactor"
+ gem "ar_transaction_changes"
+ gem "redis-rails"
+ gem "seed_migration"
+ gem "lograge"
+ gem "graphiql-rails", group: :development
+ gem "graphql"
+ gem "pusher"
+ gem "rbnacl"
+ gem "jwt"
+ gem "json-schema"
+ gem "discard"
+ gem "money"
+ gem "strip_attributes"
+ gem "validates_email_format_of"
+ gem "audited"
+ gem "concurrent-ruby"
+ gem "with_advisory_lock"
+
+ group :test do
+ gem "rspec-sidekiq"
+ gem "simplecov", require: false
+ end
+
+ group :development, :test do
+ gem "byebug", platform: :mri
+ gem "guard"
+ gem "guard-bundler"
+ gem "guard-rspec"
+ gem "rb-fsevent"
+ gem "rspec_junit_formatter"
+ gem "rspec-collection_matchers"
+ gem "rspec-rails"
+ gem "rspec-retry"
+ gem "state_machines-rspec"
+ gem "dotenv-rails"
+ gem "database_cleaner-active_record"
+ gem "database_cleaner-redis"
+ gem "timecop"
+ end
+
+ gem "factory_bot_rails"
+ gem "faker"
+
+ group :development do
+ gem "listen"
+ gem "sql_queries_count"
+ gem "rubocop"
+ gem "rubocop-performance"
+ gem "rubocop-rspec"
+ gem "rubocop-rails"
+ gem "brakeman"
+ gem "bundler-audit"
+ gem "solargraph"
+ gem "annotate"
+ end
+ G
+
+ if Bundler.feature_flag.bundler_3_mode?
+ bundle "lock", env: { "DEBUG_RESOLVER" => "1" }, raise_on_error: false
+
+ expect(out).to include("backtracking").exactly(26).times
+ else
+ bundle "lock", env: { "DEBUG_RESOLVER" => "1" }
+
+ expect(out).to include("Solution found after 10 attempts")
+ end
end
end
diff --git a/spec/bundler/resolver/basic_spec.rb b/spec/bundler/resolver/basic_spec.rb
index f739f8c02b..4a0dd37bf9 100644
--- a/spec/bundler/resolver/basic_spec.rb
+++ b/spec/bundler/resolver/basic_spec.rb
@@ -100,13 +100,22 @@ RSpec.describe "Resolving" do
end
it "raises an exception if a child dependency is not resolved" do
- @index = a_unresovable_child_index
+ @index = a_unresolvable_child_index
dep "chef_app_error"
expect do
resolve
end.to raise_error(Bundler::SolveFailure)
end
+ it "does not try to re-resolve including prereleases if gems involved don't have prereleases" do
+ @index = a_unresolvable_child_index
+ dep "chef_app_error"
+ expect(Bundler.ui).not_to receive(:debug).with("Retrying resolution...", any_args)
+ expect do
+ resolve
+ end.to raise_error(Bundler::SolveFailure)
+ end
+
it "raises an exception with the minimal set of conflicting dependencies" do
@index = build_index do
%w[0.9 1.0 2.0].each {|v| gem("a", v) }
@@ -347,4 +356,27 @@ RSpec.describe "Resolving" do
should_resolve_as %w[rack-3.0.0 standalone_migrations-1.0.13]
end
+
+ it "does not ignore versions that incorrectly depend on themselves when dependency_api is not available" do
+ @index = build_index do
+ gem "rack", "3.0.0"
+
+ gem "standalone_migrations", "7.1.0" do
+ dep "rack", "~> 2.0"
+ end
+
+ gem "standalone_migrations", "2.0.4" do
+ dep "standalone_migrations", ">= 2.0.5"
+ end
+
+ gem "standalone_migrations", "1.0.13" do
+ dep "rack", ">= 0"
+ end
+ end
+
+ dep "rack", "~> 3.0"
+ dep "standalone_migrations"
+
+ should_resolve_without_dependency_api %w[rack-3.0.0 standalone_migrations-2.0.4]
+ end
end
diff --git a/spec/bundler/resolver/platform_spec.rb b/spec/bundler/resolver/platform_spec.rb
index a710dfcb28..3e959aeb89 100644
--- a/spec/bundler/resolver/platform_spec.rb
+++ b/spec/bundler/resolver/platform_spec.rb
@@ -82,7 +82,7 @@ RSpec.describe "Resolving platform craziness" do
should_resolve_as %w[foo-1.0.0-x64-mingw32]
end
- describe "on a linux platform", :rubygems => ">= 3.1.0.pre.1" do
+ describe "on a linux platform" do
# Ruby's platform is *-linux => platform's libc is glibc, so not musl
# Ruby's platform is *-linux-musl => platform's libc is musl, so not glibc
# Gem's platform is *-linux => gem is glibc + maybe musl compatible
diff --git a/spec/bundler/runtime/executable_spec.rb b/spec/bundler/runtime/executable_spec.rb
index a11f547648..36ce6dcf67 100644
--- a/spec/bundler/runtime/executable_spec.rb
+++ b/spec/bundler/runtime/executable_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe "Running bin/* commands" do
it "runs the bundled command when in the bundle" do
bundle "binstubs rack"
- build_gem "rack", "2.0", :to_system => true do |s|
+ build_gem "rack", "2.0", to_system: true do |s|
s.executables = "rackup"
end
@@ -20,7 +20,7 @@ RSpec.describe "Running bin/* commands" do
end
it "allows the location of the gem stubs to be specified" do
- bundle "binstubs rack", :path => "gbin"
+ bundle "binstubs rack", path: "gbin"
expect(bundled_app("bin")).not_to exist
expect(bundled_app("gbin/rackup")).to exist
@@ -30,7 +30,7 @@ RSpec.describe "Running bin/* commands" do
end
it "allows absolute paths as a specification of where to install bin stubs" do
- bundle "binstubs rack", :path => tmp("bin")
+ bundle "binstubs rack", path: tmp("bin")
gembin tmp("bin/rackup")
expect(out).to eq("1.0.0")
@@ -42,23 +42,23 @@ RSpec.describe "Running bin/* commands" do
end
it "allows the name of the shebang executable to be specified" do
- bundle "binstubs rack", :shebang => "ruby-foo"
+ bundle "binstubs rack", shebang: "ruby-foo"
expect(File.readlines(bundled_app("bin/rackup")).first).to eq("#!/usr/bin/env ruby-foo\n")
end
it "runs the bundled command when out of the bundle" do
bundle "binstubs rack"
- build_gem "rack", "2.0", :to_system => true do |s|
+ build_gem "rack", "2.0", to_system: true do |s|
s.executables = "rackup"
end
- gembin "rackup", :dir => tmp
+ gembin "rackup", dir: tmp
expect(out).to eq("1.0.0")
end
it "works with gems in path" do
- build_lib "rack", :path => lib_path("rack") do |s|
+ build_lib "rack", path: lib_path("rack") do |s|
s.executables = "rackup"
end
@@ -69,7 +69,7 @@ RSpec.describe "Running bin/* commands" do
bundle "binstubs rack"
- build_gem "rack", "2.0", :to_system => true do |s|
+ build_gem "rack", "2.0", to_system: true do |s|
s.executables = "rackup"
end
@@ -94,7 +94,7 @@ RSpec.describe "Running bin/* commands" do
expect(bundled_app("bin/rackup")).not_to exist
end
- it "allows you to stop installing binstubs", :bundler => "< 3" do
+ it "allows you to stop installing binstubs", bundler: "< 3" do
skip "delete permission error" if Gem.win_platform?
bundle "install --binstubs bin/"
@@ -107,13 +107,13 @@ RSpec.describe "Running bin/* commands" do
expect(out).to include("You have not configured a value for `bin`")
end
- it "remembers that the option was specified", :bundler => "< 3" do
+ it "remembers that the option was specified", bundler: "< 3" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "activesupport"
G
- bundle :install, :binstubs => "bin"
+ bundle :install, binstubs: "bin"
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
diff --git a/spec/bundler/runtime/gem_tasks_spec.rb b/spec/bundler/runtime/gem_tasks_spec.rb
index b89fdf2cb1..f7afc0eb92 100644
--- a/spec/bundler/runtime/gem_tasks_spec.rb
+++ b/spec/bundler/runtime/gem_tasks_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe "require 'bundler/gem_tasks'" do
it "includes the relevant tasks" do
with_gem_path_as(base_system_gem_path.to_s) do
- sys_exec "#{rake} -T", :env => { "GEM_HOME" => system_gem_path.to_s }
+ sys_exec "#{rake} -T", env: { "GEM_HOME" => system_gem_path.to_s }
end
expect(err).to be_empty
@@ -45,7 +45,7 @@ RSpec.describe "require 'bundler/gem_tasks'" do
it "defines a working `rake install` task", :ruby_repo do
with_gem_path_as(base_system_gem_path.to_s) do
- sys_exec "#{rake} install", :env => { "GEM_HOME" => system_gem_path.to_s }
+ sys_exec "#{rake} install", env: { "GEM_HOME" => system_gem_path.to_s }
end
expect(err).to be_empty
@@ -59,7 +59,7 @@ RSpec.describe "require 'bundler/gem_tasks'" do
before do
spaced_bundled_app = tmp.join("bundled app")
FileUtils.cp_r bundled_app, spaced_bundled_app
- bundle "exec rake build", :dir => spaced_bundled_app
+ bundle "exec rake build", dir: spaced_bundled_app
end
it "still runs successfully" do
@@ -71,7 +71,7 @@ RSpec.describe "require 'bundler/gem_tasks'" do
before do
bracketed_bundled_app = tmp.join("bundled[app")
FileUtils.cp_r bundled_app, bracketed_bundled_app
- bundle "exec rake build", :dir => bracketed_bundled_app
+ bundle "exec rake build", dir: bracketed_bundled_app
end
it "still runs successfully" do
@@ -99,7 +99,7 @@ RSpec.describe "require 'bundler/gem_tasks'" do
it "adds 'pkg' to rake/clean's CLOBBER" do
with_gem_path_as(base_system_gem_path.to_s) do
- sys_exec %(#{rake} -e 'load "Rakefile"; puts CLOBBER.inspect'), :env => { "GEM_HOME" => system_gem_path.to_s }
+ sys_exec %(#{rake} -e 'load "Rakefile"; puts CLOBBER.inspect'), env: { "GEM_HOME" => system_gem_path.to_s }
end
expect(out).to eq '["pkg"]'
end
diff --git a/spec/bundler/runtime/inline_spec.rb b/spec/bundler/runtime/inline_spec.rb
index 29ef036828..50a5258dc7 100644
--- a/spec/bundler/runtime/inline_spec.rb
+++ b/spec/bundler/runtime/inline_spec.rb
@@ -2,7 +2,7 @@
RSpec.describe "bundler/inline#gemfile" do
def script(code, options = {})
- requires = ["#{entrypoint}/inline"]
+ requires = ["bundler/inline"]
requires.unshift "#{spec_dir}/support/artifice/" + options.delete(:artifice) if options.key?(:artifice)
requires = requires.map {|r| "require '#{r}'" }.join("\n")
ruby("#{requires}\n\n" + code, options)
@@ -28,7 +28,7 @@ RSpec.describe "bundler/inline#gemfile" do
s.write "lib/four.rb", "puts 'four'"
end
- build_lib "five", "1.0.0", :no_default => true do |s|
+ build_lib "five", "1.0.0", no_default: true do |s|
s.write "lib/mofive.rb", "puts 'five'"
end
@@ -57,7 +57,7 @@ RSpec.describe "bundler/inline#gemfile" do
expect(out).to eq("two")
- script <<-RUBY, :raise_on_error => false
+ script <<-RUBY, raise_on_error: false
gemfile do
source "#{file_uri_for(gem_repo1)}"
path "#{lib_path}" do
@@ -80,7 +80,7 @@ RSpec.describe "bundler/inline#gemfile" do
expect(out).to include("Rack's post install message")
- script <<-RUBY, :artifice => "endpoint"
+ script <<-RUBY, artifice: "endpoint"
gemfile(true) do
source "https://notaserver.com"
gem "activesupport", :require => true
@@ -89,13 +89,13 @@ RSpec.describe "bundler/inline#gemfile" do
expect(out).to include("Installing activesupport")
err_lines = err.split("\n")
- err_lines.reject! {|line| line =~ /\.rb:\d+: warning: / } unless RUBY_VERSION < "2.7"
+ err_lines.reject! {|line| line =~ /\.rb:\d+: warning: / }
expect(err_lines).to be_empty
end
it "lets me use my own ui object" do
- script <<-RUBY, :artifice => "endpoint"
- require '#{entrypoint}'
+ script <<-RUBY, artifice: "endpoint"
+ require 'bundler'
class MyBundlerUI < Bundler::UI::Shell
def confirm(msg, newline = nil)
puts "CONFIRMED!"
@@ -113,8 +113,8 @@ RSpec.describe "bundler/inline#gemfile" do
end
it "has an option for quiet installation" do
- script <<-RUBY, :artifice => "endpoint"
- require '#{entrypoint}/inline'
+ script <<-RUBY, artifice: "endpoint"
+ require 'bundler/inline'
gemfile(true, :quiet => true) do
source "https://notaserver.com"
@@ -126,7 +126,7 @@ RSpec.describe "bundler/inline#gemfile" do
end
it "raises an exception if passed unknown arguments" do
- script <<-RUBY, :raise_on_error => false
+ script <<-RUBY, raise_on_error: false
gemfile(true, :arglebargle => true) do
path "#{lib_path}"
gem "two"
@@ -140,7 +140,7 @@ RSpec.describe "bundler/inline#gemfile" do
it "does not mutate the option argument" do
script <<-RUBY
- require '#{entrypoint}'
+ require 'bundler'
options = { :ui => Bundler::UI::Shell.new }
gemfile(false, options) do
source "#{file_uri_for(gem_repo1)}"
@@ -259,7 +259,7 @@ RSpec.describe "bundler/inline#gemfile" do
system_gems "rack-1.0.0"
script <<-RUBY
- require '#{entrypoint}'
+ require 'bundler'
ui = Bundler::UI::Shell.new
ui.level = "confirm"
@@ -279,7 +279,7 @@ RSpec.describe "bundler/inline#gemfile" do
system_gems "rack-1.0.0"
script <<-RUBY
- require '#{entrypoint}'
+ require 'bundler'
ui = Bundler::UI::Shell.new
ui.level = "confirm"
gemfile(true, ui: ui) do
@@ -302,7 +302,7 @@ RSpec.describe "bundler/inline#gemfile" do
system_gems "rack-1.0.0"
script <<-RUBY
- require '#{entrypoint}'
+ require 'bundler'
ui = Bundler::UI::Shell.new
ui.level = "confirm"
gemfile(true, ui: ui) do
@@ -339,7 +339,7 @@ RSpec.describe "bundler/inline#gemfile" do
end
script <<-RUBY
- require '#{entrypoint}'
+ require 'bundler'
ui = Bundler::UI::Shell.new
ui.level = "confirm"
gemfile(true, ui: ui) do
@@ -421,17 +421,17 @@ RSpec.describe "bundler/inline#gemfile" do
script <<-RUBY
gemfile(true) do
source "#{file_uri_for(gem_repo1)}"
- gem "rake", "~> 13.0"
+ gem "rake", "#{rake_version}"
end
RUBY
- expect(out).to include("Installing rake 13.0")
+ expect(out).to include("Installing rake #{rake_version}")
expect(out).not_to include("was 11.3.0")
expect(err).to be_empty
end
it "installs inline gems when frozen is set" do
- script <<-RUBY, :env => { "BUNDLE_FROZEN" => "true" }
+ script <<-RUBY, env: { "BUNDLE_FROZEN" => "true" }
gemfile do
source "#{file_uri_for(gem_repo1)}"
gem "rack"
@@ -444,7 +444,7 @@ RSpec.describe "bundler/inline#gemfile" do
end
it "installs inline gems when deployment is set" do
- script <<-RUBY, :env => { "BUNDLE_DEPLOYMENT" => "true" }
+ script <<-RUBY, env: { "BUNDLE_DEPLOYMENT" => "true" }
gemfile do
source "#{file_uri_for(gem_repo1)}"
gem "rack"
@@ -488,7 +488,7 @@ RSpec.describe "bundler/inline#gemfile" do
context "when BUNDLE_PATH is set" do
it "installs inline gems to the system path regardless" do
- script <<-RUBY, :env => { "BUNDLE_PATH" => "./vendor/inline" }
+ script <<-RUBY, env: { "BUNDLE_PATH" => "./vendor/inline" }
gemfile(true) do
source "#{file_uri_for(gem_repo1)}"
gem "rack"
@@ -576,7 +576,7 @@ RSpec.describe "bundler/inline#gemfile" do
s.write "lib/foo.rb", foo_code
end
- script <<-RUBY, :dir => tmp("path_without_gemfile")
+ script <<-RUBY, dir: tmp("path_without_gemfile")
gemfile do
source "#{file_uri_for(gem_repo2)}"
path "#{lib_path}" do
@@ -592,26 +592,16 @@ RSpec.describe "bundler/inline#gemfile" do
end
it "when requiring fileutils after does not show redefinition warnings", :realworld do
- dependency_installer_loads_fileutils = ruby "require 'rubygems/dependency_installer'; puts $LOADED_FEATURES.grep(/fileutils/)", :raise_on_error => false
- skip "does not work if rubygems/dependency_installer loads fileutils, which happens until rubygems 3.2.0" unless dependency_installer_loads_fileutils.empty?
-
- skip "pathname does not install cleanly on this ruby" if RUBY_VERSION < "2.7.0"
-
Dir.mkdir tmp("path_without_gemfile")
- default_fileutils_version = ruby "gem 'fileutils', '< 999999'; require 'fileutils'; puts FileUtils::VERSION", :raise_on_error => false
+ default_fileutils_version = ruby "gem 'fileutils', '< 999999'; require 'fileutils'; puts FileUtils::VERSION", raise_on_error: false
skip "fileutils isn't a default gem" if default_fileutils_version.empty?
realworld_system_gems "fileutils --version 1.4.1"
realworld_system_gems "pathname --version 0.2.0"
- realworld_system_gems "timeout uri" # this spec uses net/http which requires these default gems
-
- # on prerelease rubies, a required_rubygems_version constraint is added by RubyGems to the resolution, causing Molinillo to load the `set` gem
- realworld_system_gems "set --version 1.0.3" if Gem.ruby_version.prerelease?
-
- script <<-RUBY, :dir => tmp("path_without_gemfile"), :env => { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s }
+ script <<-RUBY, dir: tmp("path_without_gemfile"), env: { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s }
require "bundler/inline"
gemfile(true) do
@@ -623,4 +613,47 @@ RSpec.describe "bundler/inline#gemfile" do
expect(err).to eq("The Gemfile specifies no dependencies")
end
+
+ it "does not load default timeout" do
+ default_timeout_version = ruby "gem 'timeout', '< 999999'; require 'timeout'; puts Timeout::VERSION", raise_on_error: false
+ skip "timeout isn't a default gem" if default_timeout_version.empty?
+
+ # This only works on RubyGems 3.5.0 or higher
+ ruby "require 'rubygems/timeout'", raise_on_error: false
+ skip "rubygems under test does not yet vendor timeout" unless last_command.success?
+
+ build_repo4 do
+ build_gem "timeout", "999"
+ end
+
+ script <<-RUBY
+ require "bundler/inline"
+
+ gemfile(true) do
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "timeout"
+ end
+ RUBY
+
+ expect(out).to include("Installing timeout 999")
+ end
+
+ it "does not upcase ENV" do
+ script <<-RUBY
+ require 'bundler/inline'
+
+ ENV['Test_Variable'] = 'value string'
+ puts("before: \#{ENV.each_key.select { |key| key.match?(/test_variable/i) }}")
+
+ gemfile do
+ source "#{file_uri_for(gem_repo1)}"
+ end
+
+ puts("after: \#{ENV.each_key.select { |key| key.match?(/test_variable/i) }}")
+ RUBY
+
+ expect(out).to include("before: [\"Test_Variable\"]")
+ expect(out).to include("after: [\"Test_Variable\"]")
+ end
end
diff --git a/spec/bundler/runtime/load_spec.rb b/spec/bundler/runtime/load_spec.rb
index 96a22a46cc..f28ffd9460 100644
--- a/spec/bundler/runtime/load_spec.rb
+++ b/spec/bundler/runtime/load_spec.rb
@@ -82,7 +82,7 @@ RSpec.describe "Bundler.load" do
G
ruby <<-RUBY
- require "#{entrypoint}"
+ require "bundler"
Bundler.setup :default
Bundler.require :default
puts RACK
diff --git a/spec/bundler/runtime/platform_spec.rb b/spec/bundler/runtime/platform_spec.rb
index b31bc4abe8..1925e9bf2e 100644
--- a/spec/bundler/runtime/platform_spec.rb
+++ b/spec/bundler/runtime/platform_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
ruby <<-R
begin
- require '#{entrypoint}'
+ require 'bundler'
Bundler.ui.silence { Bundler.setup }
rescue Bundler::GemNotFound => e
puts "WIN"
@@ -61,16 +61,23 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
build_repo4 do
build_gem "nokogiri", "1.11.1" do |s|
s.add_dependency "mini_portile2", "~> 2.5.0"
- s.add_dependency "racc", "~> 1.5.2"
+ s.add_dependency "racca", "~> 1.5.2"
end
build_gem "nokogiri", "1.11.1" do |s|
s.platform = Bundler.local_platform
- s.add_dependency "racc", "~> 1.4"
+ s.add_dependency "racca", "~> 1.4"
end
build_gem "mini_portile2", "2.5.0"
- build_gem "racc", "1.5.2"
+ build_gem "racca", "1.5.2"
+ end
+
+ checksums = checksums_section do |c|
+ c.checksum gem_repo4, "mini_portile2", "2.5.0"
+ c.checksum gem_repo4, "nokogiri", "1.11.1"
+ c.checksum gem_repo4, "nokogiri", "1.11.1", Bundler.local_platform
+ c.checksum gem_repo4, "racca", "1.5.2"
end
good_lockfile = <<~L
@@ -80,17 +87,17 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
mini_portile2 (2.5.0)
nokogiri (1.11.1)
mini_portile2 (~> 2.5.0)
- racc (~> 1.5.2)
+ racca (~> 1.5.2)
nokogiri (1.11.1-#{Bundler.local_platform})
- racc (~> 1.4)
- racc (1.5.2)
+ racca (~> 1.4)
+ racca (1.5.2)
PLATFORMS
#{lockfile_platforms("ruby")}
DEPENDENCIES
nokogiri (~> 1.11)
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
@@ -138,7 +145,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
#{Bundler::VERSION}
L
- bundle "install", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ bundle "install", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
expect(out).to include("Fetching nokogiri 1.11.1")
expect(the_bundle).to include_gems "nokogiri 1.11.1"
@@ -352,7 +359,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
end
simulate_platform "aarch64-linux-musl" do
- install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }, :verbose => true
+ install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }, verbose: true
source "https://gems.repo4"
gem "nokogiri"
G
@@ -379,7 +386,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
end
it "allows specifying only-ruby-platform on windows with gemspec dependency" do
- build_lib("foo", "1.0", :path => bundled_app) do |s|
+ build_lib("foo", "1.0", path: bundled_app) do |s|
s.add_dependency "rack"
end
@@ -420,7 +427,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
requires_platform_specific
L
- install_gemfile <<-G, :verbose => true
+ install_gemfile <<-G, verbose: true
source "#{file_uri_for(gem_repo2)}"
gem "requires_platform_specific"
G
diff --git a/spec/bundler/runtime/require_spec.rb b/spec/bundler/runtime/require_spec.rb
index e59fa564f6..76271a5593 100644
--- a/spec/bundler/runtime/require_spec.rb
+++ b/spec/bundler/runtime/require_spec.rb
@@ -21,7 +21,7 @@ RSpec.describe "Bundler.require" do
s.write "lib/four.rb", "puts 'four'"
end
- build_lib "five", "1.0.0", :no_default => true do |s|
+ build_lib "five", "1.0.0", no_default: true do |s|
s.write "lib/mofive.rb", "puts 'five'"
end
@@ -138,7 +138,7 @@ RSpec.describe "Bundler.require" do
end
G
- run "Bundler.require", :raise_on_error => false
+ run "Bundler.require", raise_on_error: false
expect(err).to match("error while trying to load the gem 'faulty'")
expect(err).to match("Gem Internal Error Message")
end
@@ -187,7 +187,7 @@ RSpec.describe "Bundler.require" do
end
it "silently passes if the require fails" do
- build_lib "bcrypt-ruby", "1.0.0", :no_default => true do |s|
+ build_lib "bcrypt-ruby", "1.0.0", no_default: true do |s|
s.write "lib/brcrypt.rb", "BCrypt = '1.0.0'"
end
gemfile <<-G
@@ -199,7 +199,7 @@ RSpec.describe "Bundler.require" do
G
cmd = <<-RUBY
- require '#{entrypoint}'
+ require 'bundler'
Bundler.require
RUBY
ruby(cmd)
@@ -323,7 +323,7 @@ RSpec.describe "Bundler.require" do
describe "a gem with different requires for different envs" do
before(:each) do
- build_gem "multi_gem", :to_bundle => true do |s|
+ build_gem "multi_gem", to_bundle: true do |s|
s.write "lib/one.rb", "puts 'ONE'"
s.write "lib/two.rb", "puts 'TWO'"
end
@@ -366,7 +366,7 @@ RSpec.describe "Bundler.require" do
describe "with busted gems" do
it "should be busted" do
- build_gem "busted_require", :to_bundle => true do |s|
+ build_gem "busted_require", to_bundle: true do |s|
s.write "lib/busted_require.rb", "require 'no_such_file_omg'"
end
diff --git a/spec/bundler/runtime/requiring_spec.rb b/spec/bundler/runtime/requiring_spec.rb
new file mode 100644
index 0000000000..1f32269622
--- /dev/null
+++ b/spec/bundler/runtime/requiring_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+RSpec.describe "Requiring bundler" do
+ it "takes care of requiring rubygems when entrypoint is bundler/setup" do
+ sys_exec("#{Gem.ruby} -I#{lib_dir} -rbundler/setup -e'puts true'", env: { "RUBYOPT" => opt_add("--disable=gems", ENV["RUBYOPT"]) })
+
+ expect(last_command.stdboth).to eq("true")
+ end
+
+ it "takes care of requiring rubygems when requiring just bundler" do
+ sys_exec("#{Gem.ruby} -I#{lib_dir} -rbundler -e'puts true'", env: { "RUBYOPT" => opt_add("--disable=gems", ENV["RUBYOPT"]) })
+
+ expect(last_command.stdboth).to eq("true")
+ end
+end
diff --git a/spec/bundler/runtime/self_management_spec.rb b/spec/bundler/runtime/self_management_spec.rb
index 700084babf..d15ca3189e 100644
--- a/spec/bundler/runtime/self_management_spec.rb
+++ b/spec/bundler/runtime/self_management_spec.rb
@@ -1,11 +1,15 @@
# frozen_string_literal: true
-RSpec.describe "Self management", :rubygems => ">= 3.3.0.dev", :realworld => true do
+RSpec.describe "Self management", rubygems: ">= 3.3.0.dev", realworld: true do
describe "auto switching" do
let(:previous_minor) do
"2.3.0"
end
+ let(:current_version) do
+ "2.4.0"
+ end
+
before do
build_repo2
@@ -20,19 +24,19 @@ RSpec.describe "Self management", :rubygems => ">= 3.3.0.dev", :realworld => tru
lockfile_bundled_with(previous_minor)
bundle "config set --local path.system true"
- bundle "install", :artifice => "vcr"
+ bundle "install", artifice: "vcr"
expect(out).to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
# It uninstalls the older system bundler
- bundle "clean --force"
+ bundle "clean --force", artifice: nil
expect(out).to eq("Removing bundler (#{Bundler::VERSION})")
# App now uses locked version
- bundle "-v"
+ bundle "-v", artifice: nil
expect(out).to end_with(previous_minor[0] == "2" ? "Bundler version #{previous_minor}" : previous_minor)
# Subsequent installs use the locked version without reinstalling
- bundle "install --verbose"
+ bundle "install --verbose", artifice: nil
expect(out).to include("Using bundler #{previous_minor}")
expect(out).not_to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
end
@@ -41,7 +45,7 @@ RSpec.describe "Self management", :rubygems => ">= 3.3.0.dev", :realworld => tru
lockfile_bundled_with(previous_minor)
bundle "config set --local path vendor/bundle"
- bundle "install", :artifice => "vcr"
+ bundle "install", artifice: "vcr"
expect(out).to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
expect(vendored_gems("gems/bundler-#{previous_minor}")).to exist
@@ -63,7 +67,7 @@ RSpec.describe "Self management", :rubygems => ">= 3.3.0.dev", :realworld => tru
lockfile_bundled_with(previous_minor)
bundle "config set --local deployment true"
- bundle "install", :artifice => "vcr"
+ bundle "install", artifice: "vcr"
expect(out).to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
expect(vendored_gems("gems/bundler-#{previous_minor}")).to exist
@@ -96,13 +100,42 @@ RSpec.describe "Self management", :rubygems => ">= 3.3.0.dev", :realworld => tru
lockfile_bundled_with(missing_minor)
- bundle "install", :artifice => "vcr"
+ bundle "install", artifice: "vcr"
expect(err).to eq("Your lockfile is locked to a version of bundler (#{missing_minor}) that doesn't exist at https://rubygems.org/. Going on using #{Bundler::VERSION}")
bundle "-v"
expect(out).to eq(Bundler::VERSION[0] == "2" ? "Bundler version #{Bundler::VERSION}" : Bundler::VERSION)
end
+ it "installs BUNDLE_VERSION version when using bundle config version x.y.z" do
+ lockfile_bundled_with(current_version)
+
+ bundle "config set --local version #{previous_minor}"
+ bundle "install", artifice: "vcr"
+ expect(out).to include("Bundler #{Bundler::VERSION} is running, but your configuration was #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
+
+ bundle "-v"
+ expect(out).to eq(previous_minor[0] == "2" ? "Bundler version #{previous_minor}" : previous_minor)
+ end
+
+ it "does not try to install when using bundle config version global" do
+ lockfile_bundled_with(previous_minor)
+
+ bundle "config set version system"
+ bundle "install", artifice: "vcr"
+ expect(out).not_to match(/restarting using that version/)
+
+ bundle "-v"
+ expect(out).to eq(Bundler::VERSION[0] == "2" ? "Bundler version #{Bundler::VERSION}" : Bundler::VERSION)
+ end
+
+ it "ignores malformed lockfile version" do
+ lockfile_bundled_with("2.3.")
+
+ bundle "install --verbose"
+ expect(out).to include("Using bundler #{Bundler::VERSION}")
+ end
+
private
def lockfile_bundled_with(version)
diff --git a/spec/bundler/runtime/setup_spec.rb b/spec/bundler/runtime/setup_spec.rb
index 9bfcbdaed8..2d78825de4 100644
--- a/spec/bundler/runtime/setup_spec.rb
+++ b/spec/bundler/runtime/setup_spec.rb
@@ -89,7 +89,7 @@ RSpec.describe "Bundler.setup" do
end
it "handles multiple non-additive invocations" do
- ruby <<-RUBY, :raise_on_error => false
+ ruby <<-RUBY, raise_on_error: false
require 'bundler'
Bundler.setup(:default, :test)
Bundler.setup(:default)
@@ -144,6 +144,7 @@ RSpec.describe "Bundler.setup" do
ruby <<-RUBY
require 'bundler'
+ gem "bundler", "#{Bundler::VERSION}" if #{ruby_core?}
Bundler.setup
puts $LOAD_PATH
RUBY
@@ -157,7 +158,7 @@ RSpec.describe "Bundler.setup" do
"/gems/actionpack-2.3.2/lib",
"/gems/actionmailer-2.3.2/lib",
"/gems/activesupport-2.3.2/lib",
- "/gems/rake-13.0.1/lib"
+ "/gems/rake-#{rake_version}/lib"
)
end
@@ -193,7 +194,7 @@ RSpec.describe "Bundler.setup" do
G
ruby <<-R
- require '#{entrypoint}'
+ require 'bundler'
begin
Bundler.setup
@@ -212,7 +213,7 @@ RSpec.describe "Bundler.setup" do
gem "rack"
G
- ruby <<-R, :raise_on_error => false
+ ruby <<-R, raise_on_error: false
require 'bundler'
Bundler.setup
@@ -235,7 +236,7 @@ RSpec.describe "Bundler.setup" do
gem "nosuchgem", "10.0"
G
- ruby <<-R, :raise_on_error => false
+ ruby <<-R, raise_on_error: false
require 'bundler'
Bundler.setup
@@ -311,7 +312,7 @@ RSpec.describe "Bundler.setup" do
gem "rack", "1.0.0"
G
- build_gem "rack", "1.0", :to_system => true do |s|
+ build_gem "rack", "1.0", to_system: true do |s|
s.write "lib/rack.rb", "RACK = 'FAIL'"
end
@@ -371,7 +372,7 @@ RSpec.describe "Bundler.setup" do
context "when the ruby stdlib is a substring of Gem.path" do
it "does not reject the stdlib from $LOAD_PATH" do
substring = "/" + $LOAD_PATH.find {|p| p.include?("vendor_ruby") }.split("/")[2]
- run "puts 'worked!'", :env => { "GEM_PATH" => substring }
+ run "puts 'worked!'", env: { "GEM_PATH" => substring }
expect(out).to eq("worked!")
end
end
@@ -409,8 +410,8 @@ RSpec.describe "Bundler.setup" do
end
it "provides a useful exception when the git repo is not checked out yet" do
- run "1", :raise_on_error => false
- expect(err).to match(/the git source #{lib_path('rack-1.0.0')} is not yet checked out. Please run `bundle install`/i)
+ run "1", raise_on_error: false
+ expect(err).to match(/the git source #{lib_path("rack-1.0.0")} is not yet checked out. Please run `bundle install`/i)
end
it "does not hit the git binary if the lockfile is available and up to date" do
@@ -440,7 +441,7 @@ RSpec.describe "Bundler.setup" do
break_git!
ruby <<-R
- require "#{entrypoint}"
+ require "bundler"
begin
Bundler.setup
@@ -450,7 +451,7 @@ RSpec.describe "Bundler.setup" do
end
R
- run "puts 'FAIL'", :raise_on_error => false
+ run "puts 'FAIL'", raise_on_error: false
expect(err).not_to include "This is not the git you are looking for"
end
@@ -496,8 +497,8 @@ RSpec.describe "Bundler.setup" do
bundle :install
FileUtils.rm_rf(lib_path("local-rack"))
- run "require 'rack'", :raise_on_error => false
- expect(err).to match(/Cannot use local override for rack-0.8 because #{Regexp.escape(lib_path('local-rack').to_s)} does not exist/)
+ run "require 'rack'", raise_on_error: false
+ expect(err).to match(/Cannot use local override for rack-0.8 because #{Regexp.escape(lib_path("local-rack").to_s)} does not exist/)
end
it "explodes if branch is not given on runtime" do
@@ -518,7 +519,7 @@ RSpec.describe "Bundler.setup" do
gem "rack", :git => "#{lib_path("rack-0.8")}"
G
- run "require 'rack'", :raise_on_error => false
+ run "require 'rack'", raise_on_error: false
expect(err).to match(/because :branch is not specified in Gemfile/)
end
@@ -540,7 +541,7 @@ RSpec.describe "Bundler.setup" do
gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "changed"
G
- run "require 'rack'", :raise_on_error => false
+ run "require 'rack'", raise_on_error: false
expect(err).to match(/is using branch main but Gemfile specifies changed/)
end
@@ -560,7 +561,7 @@ RSpec.describe "Bundler.setup" do
G
bundle %(config set local.rack #{lib_path("local-rack")})
- run "require 'rack'", :raise_on_error => false
+ run "require 'rack'", raise_on_error: false
expect(err).to match(/is using branch main but Gemfile specifies nonexistent/)
end
end
@@ -579,7 +580,7 @@ RSpec.describe "Bundler.setup" do
system_gems "activesupport-2.3.5"
- expect(the_bundle).to include_gems "activesupport 2.3.2", :groups => :default
+ expect(the_bundle).to include_gems "activesupport 2.3.2", groups: :default
end
it "remembers --without and does not bail on bare Bundler.setup" do
@@ -602,7 +603,7 @@ RSpec.describe "Bundler.setup" do
bundle "config set --local without development"
path = bundled_app(File.join("vendor", "foo"))
- build_lib "foo", :path => path
+ build_lib "foo", path: path
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -612,7 +613,7 @@ RSpec.describe "Bundler.setup" do
FileUtils.rm_rf(path)
- ruby "require 'bundler'; Bundler.setup", :env => { "DEBUG" => "1" }
+ ruby "require 'bundler'; Bundler.setup", env: { "DEBUG" => "1" }
expect(out).to include("Assuming that source at `vendor/foo` has not changed since fetching its specs errored")
expect(out).to include("Found no changes, using resolution from the lockfile")
expect(err).to be_empty
@@ -632,12 +633,22 @@ RSpec.describe "Bundler.setup" do
gem "depends_on_bundler"
G
- ruby "require '#{system_gem_path("gems/bundler-9.99.9.beta1/lib/bundler.rb")}'; Bundler.setup", :env => { "DEBUG" => "1" }
+ ruby "require '#{system_gem_path("gems/bundler-9.99.9.beta1/lib/bundler.rb")}'; Bundler.setup", env: { "DEBUG" => "1" }
expect(out).to include("Found no changes, using resolution from the lockfile")
expect(out).not_to include("lockfile does not have all gems needed for the current platform")
expect(err).to be_empty
end
+ it "doesn't fail in frozen mode when bundler is a Gemfile dependency" do
+ install_gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "bundler"
+ G
+
+ bundle "install --verbose", env: { "BUNDLE_FROZEN" => "true" }
+ expect(err).to be_empty
+ end
+
it "doesn't re-resolve when deleting dependencies" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -645,7 +656,7 @@ RSpec.describe "Bundler.setup" do
gem "actionpack"
G
- install_gemfile <<-G, :verbose => true
+ install_gemfile <<-G, verbose: true
source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
@@ -669,15 +680,15 @@ RSpec.describe "Bundler.setup" do
end
G
- expect(the_bundle).not_to include_gems "activesupport 2.3.2", :groups => :rack
- expect(the_bundle).to include_gems "rack 1.0.0", :groups => :rack
+ expect(the_bundle).not_to include_gems "activesupport 2.3.2", groups: :rack
+ expect(the_bundle).to include_gems "rack 1.0.0", groups: :rack
end
end
# RubyGems returns loaded_from as a string
it "has loaded_from as a string on all specs" do
build_git "foo"
- build_git "no-gemspec", :gemspec => false
+ build_git "no-gemspec", gemspec: false
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -717,6 +728,27 @@ end
R
run <<-R
+ File.open(File.join(Gem.dir, "specifications", "broken-ext.gemspec"), "w") do |f|
+ f.write <<-RUBY
+# -*- encoding: utf-8 -*-
+# stub: broken-ext 1.0.0 ruby lib
+# stub: a.ext\\0b.ext
+
+Gem::Specification.new do |s|
+ s.name = "broken-ext"
+ s.version = "1.0.0"
+ raise "BROKEN GEMSPEC EXT"
+end
+ RUBY
+ end
+ # Need to write the gem.build_complete file,
+ # otherwise the full spec is loaded to check the installed_by_version
+ extensions_dir = Gem.default_ext_dir_for(Gem.dir) || File.join(Gem.dir, "extensions", Gem::Platform.local.to_s, Gem.extension_api_version)
+ Bundler::FileUtils.mkdir_p(File.join(extensions_dir, "broken-ext-1.0.0"))
+ File.open(File.join(extensions_dir, "broken-ext-1.0.0", "gem.build_complete"), "w") {}
+ R
+
+ run <<-R
puts "WIN"
R
@@ -735,6 +767,18 @@ end
expect(err).to be_empty
end
+ it "can require rubygems without warnings, when using a local cache", rubygems: ">= 3.5.10" do
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rack"
+ G
+
+ bundle "package"
+ bundle %(exec ruby -w -e "require 'rubygems'")
+
+ expect(err).to be_empty
+ end
+
context "when the user has `MANPATH` set", :man do
before { ENV["MANPATH"] = "/foo#{File::PATH_SEPARATOR}" }
@@ -875,7 +919,7 @@ end
it "should not remove itself from the LOAD_PATH and require a different copy of 'bundler/setup'" do
install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
- ruby <<-R, :env => { "GEM_PATH" => symlinked_gem_home }
+ ruby <<-R, env: { "GEM_PATH" => symlinked_gem_home }
TracePoint.trace(:class) do |tp|
if tp.path.include?("bundler") && !tp.path.start_with?("#{source_root}")
puts "OMG. Defining a class from another bundler at \#{tp.path}:\#{tp.lineno}"
@@ -915,7 +959,7 @@ end
it "should resolve paths relative to the Gemfile" do
path = bundled_app(File.join("vendor", "foo"))
- build_lib "foo", :path => path
+ build_lib "foo", path: path
# If the .gemspec exists, then Bundler handles the path differently.
# See Source::Path.load_spec_files for details.
@@ -926,7 +970,7 @@ end
gem 'foo', '1.2.3', :path => 'vendor/foo'
G
- run <<-R, :env => { "BUNDLE_GEMFILE" => bundled_app_gemfile.to_s }, :dir => bundled_app.parent
+ run <<-R, env: { "BUNDLE_GEMFILE" => bundled_app_gemfile.to_s }, dir: bundled_app.parent
require 'foo'
R
expect(err).to be_empty
@@ -936,7 +980,7 @@ end
relative_path = File.join("vendor", Dir.pwd.gsub(/^#{filesystem_root}/, ""))
absolute_path = bundled_app(relative_path)
FileUtils.mkdir_p(absolute_path)
- build_lib "foo", :path => absolute_path
+ build_lib "foo", path: absolute_path
# If the .gemspec exists, then Bundler handles the path differently.
# See Source::Path.load_spec_files for details.
@@ -949,7 +993,7 @@ end
bundle :install
- run <<-R, :env => { "BUNDLE_GEMFILE" => bundled_app_gemfile.to_s }, :dir => bundled_app.parent
+ run <<-R, env: { "BUNDLE_GEMFILE" => bundled_app_gemfile.to_s }, dir: bundled_app.parent
require 'foo'
R
@@ -959,7 +1003,7 @@ end
describe "with git gems that don't have gemspecs" do
before :each do
- build_git "no_gemspec", :gemspec => false
+ build_git "no_gemspec", gemspec: false
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -1049,7 +1093,7 @@ end
describe "with a gemspec that requires other files" do
before :each do
- build_git "bar", :gemspec => false do |s|
+ build_git "bar", gemspec: false do |s|
s.write "lib/bar/version.rb", %(BAR_VERSION = '1.0')
s.write "bar.gemspec", <<-G
require_relative 'lib/bar/version'
@@ -1079,10 +1123,10 @@ end
it "error intelligently if the gemspec has a LoadError" do
skip "whitespace issue?" if Gem.win_platform?
- ref = update_git "bar", :gemspec => false do |s|
+ ref = update_git "bar", gemspec: false do |s|
s.write "bar.gemspec", "require 'foobarbaz'"
end.ref_for("HEAD")
- bundle :install, :raise_on_error => false
+ bundle :install, raise_on_error: false
expect(err.lines.map(&:chomp)).to include(
a_string_starting_with("[!] There was an error while loading `bar.gemspec`:"),
@@ -1155,7 +1199,7 @@ end
context "is not present" do
it "does not change the lock" do
lockfile lock_with(nil)
- ruby "require '#{entrypoint}/setup'"
+ ruby "require 'bundler/setup'"
expect(lockfile).to eq lock_with(nil)
end
end
@@ -1174,7 +1218,7 @@ end
it "does not change the lock" do
system_gems "bundler-1.10.1"
lockfile lock_with("1.10.1")
- ruby "require '#{entrypoint}/setup'"
+ ruby "require 'bundler/setup'"
expect(lockfile).to eq lock_with("1.10.1")
end
end
@@ -1184,6 +1228,10 @@ end
let(:ruby_version) { nil }
def lock_with(ruby_version = nil)
+ checksums = checksums_section do |c|
+ c.checksum gem_repo1, "rack", "1.0.0"
+ end
+
lock = <<~L
GEM
remote: #{file_uri_for(gem_repo1)}/
@@ -1195,6 +1243,7 @@ end
DEPENDENCIES
rack
+ #{checksums}
L
if ruby_version
@@ -1244,9 +1293,7 @@ end
describe "with gemified standard libraries" do
it "does not load Digest", :ruby_repo do
- skip "Only for Ruby 3.0+" unless RUBY_VERSION >= "3.0"
-
- build_git "bar", :gemspec => false do |s|
+ build_git "bar", gemspec: false do |s|
s.write "lib/bar/version.rb", %(BAR_VERSION = '1.0')
s.write "bar.gemspec", <<-G
require_relative 'lib/bar/version'
@@ -1271,7 +1318,7 @@ end
bundle :install
ruby <<-RUBY
- require '#{entrypoint}/setup'
+ require 'bundler/setup'
puts defined?(::Digest) ? "Digest defined" : "Digest undefined"
require 'digest'
RUBY
@@ -1281,7 +1328,7 @@ end
it "does not load Psych" do
gemfile "source \"#{file_uri_for(gem_repo1)}\""
ruby <<-RUBY
- require '#{entrypoint}/setup'
+ require 'bundler/setup'
puts defined?(Psych::VERSION) ? Psych::VERSION : "undefined"
require 'psych'
puts Psych::VERSION
@@ -1302,11 +1349,37 @@ end
expect(out).to eq("undefined\nconstant")
end
+ it "does not load uri while reading gemspecs", rubygems: ">= 3.6.0.dev" do
+ Dir.mkdir bundled_app("test")
+
+ create_file(bundled_app("test/test.gemspec"), <<-G)
+ Gem::Specification.new do |s|
+ s.name = "test"
+ s.version = "1.0.0"
+ s.summary = "test"
+ s.authors = ['John Doe']
+ s.homepage = 'https://example.com'
+ end
+ G
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "test", path: "#{bundled_app("test")}"
+ G
+
+ ruby <<-RUBY
+ require "bundler/setup"
+ puts defined?(URI) || "undefined"
+ require "uri"
+ puts defined?(URI) || "undefined"
+ RUBY
+ expect(out).to eq("undefined\nconstant")
+ end
+
describe "default gem activation" do
let(:exemptions) do
- exempts = %w[did_you_mean bundler]
- exempts << "uri" if Gem.ruby_version >= Gem::Version.new("2.7")
- exempts << "pathname" if Gem.ruby_version >= Gem::Version.new("3.0")
+ exempts = %w[did_you_mean bundler uri pathname]
+ exempts << "etc" if Gem.ruby_version < Gem::Version.new("3.2") && Gem.win_platform?
exempts << "set" unless Gem.rubygems_version >= Gem::Version.new("3.2.6")
exempts << "tsort" unless Gem.rubygems_version >= Gem::Version.new("3.2.31")
exempts << "error_highlight" # added in Ruby 3.1 as a default gem
@@ -1315,7 +1388,7 @@ end
exempts
end
- let(:activation_warning_hack) { strip_whitespace(<<-RUBY) }
+ let(:activation_warning_hack) { <<~RUBY }
require #{spec_dir.join("support/hax").to_s.dump}
Gem::Specification.send(:alias_method, :bundler_spec_activate, :activate)
@@ -1335,7 +1408,7 @@ end
"-r#{bundled_app("activation_warning_hack.rb")} #{ENV["RUBYOPT"]}"
end
- let(:code) { strip_whitespace(<<-RUBY) }
+ let(:code) { <<~RUBY }
require "pp"
loaded_specs = Gem.loaded_specs.dup
#{exemptions.inspect}.each {|s| loaded_specs.delete(s) }
@@ -1350,14 +1423,14 @@ end
it "activates no gems with -rbundler/setup" do
install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
- ruby code, :env => { "RUBYOPT" => activation_warning_hack_rubyopt + " -rbundler/setup" }
+ ruby code, env: { "RUBYOPT" => activation_warning_hack_rubyopt + " -rbundler/setup" }
expect(out).to eq("{}")
end
it "activates no gems with bundle exec" do
install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
create_file("script.rb", code)
- bundle "exec ruby ./script.rb", :env => { "RUBYOPT" => activation_warning_hack_rubyopt }
+ bundle "exec ruby ./script.rb", env: { "RUBYOPT" => activation_warning_hack_rubyopt }
expect(out).to eq("{}")
end
@@ -1367,7 +1440,7 @@ end
install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
create_file("script.rb", "#!/usr/bin/env ruby\n\n#{code}")
FileUtils.chmod(0o777, bundled_app("script.rb"))
- bundle "exec ./script.rb", :artifice => nil, :env => { "RUBYOPT" => activation_warning_hack_rubyopt }
+ bundle "exec ./script.rb", artifice: nil, env: { "RUBYOPT" => activation_warning_hack_rubyopt }
expect(out).to eq("{}")
end
@@ -1376,7 +1449,7 @@ end
build_gem "net-http-pipeline", "1.0.1"
end
- system_gems "net-http-pipeline-1.0.1", :gem_repo => gem_repo4
+ system_gems "net-http-pipeline-1.0.1", gem_repo: gem_repo4
gemfile <<-G
source "#{file_uri_for(gem_repo4)}"
@@ -1405,7 +1478,7 @@ end
gem "#{g}", "999999"
G
- expect(the_bundle).to include_gem("#{g} 999999", :env => { "RUBYOPT" => activation_warning_hack_rubyopt })
+ expect(the_bundle).to include_gem("#{g} 999999", env: { "RUBYOPT" => activation_warning_hack_rubyopt })
end
it "activates older versions of #{g}", :ruby_repo do
@@ -1420,14 +1493,14 @@ end
gem "#{g}", "0.0.0.a"
G
- expect(the_bundle).to include_gem("#{g} 0.0.0.a", :env => { "RUBYOPT" => activation_warning_hack_rubyopt })
+ expect(the_bundle).to include_gem("#{g} 0.0.0.a", env: { "RUBYOPT" => activation_warning_hack_rubyopt })
end
end
end
end
describe "after setup" do
- it "allows calling #gem on random objects", :bundler => "< 3" do
+ it "allows calling #gem on random objects", bundler: "< 3" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
@@ -1442,13 +1515,13 @@ end
expect(out).to eq("rack-1.0.0")
end
- it "keeps Kernel#gem private", :bundler => "3" do
+ it "keeps Kernel#gem private", bundler: "3" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
- ruby <<-RUBY, :raise_on_error => false
+ ruby <<-RUBY, raise_on_error: false
require "bundler/setup"
Object.new.gem "rack"
puts "FAIL"
@@ -1464,20 +1537,14 @@ end
gem "rack"
G
- ruby <<-RUBY, :raise_on_error => false
+ ruby <<-RUBY, raise_on_error: false
require "bundler/setup"
Object.new.require "rack"
puts "FAIL"
RUBY
expect(last_command.stdboth).not_to include "FAIL"
- expect(err).to include "private method `require'"
- end
-
- it "takes care of requiring rubygems" do
- sys_exec("#{Gem.ruby} -I#{lib_dir} -rbundler/setup -e'puts true'", :env => { "RUBYOPT" => opt_add("--disable=gems", ENV["RUBYOPT"]) })
-
- expect(last_command.stdboth).to eq("true")
+ expect(err).to match(/private method [`']require'/)
end
it "memoizes initial set of specs when requiring bundler/setup, so that even if further code mutates dependencies, Bundler.definition.specs is not affected" do
@@ -1487,7 +1554,7 @@ end
gem "rack", :group => :test
G
- ruby <<-RUBY, :raise_on_error => false
+ ruby <<-RUBY, raise_on_error: false
require "bundler/setup"
Bundler.require(:test).select! {|d| (d.groups & [:test]).any? }
puts Bundler.definition.specs.map(&:name).join(", ")
@@ -1507,7 +1574,7 @@ end
end
end
- system_gems "json-999.999.999", :gem_repo => gem_repo2
+ system_gems "json-999.999.999", gem_repo: gem_repo2
install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
ruby <<-RUBY
@@ -1520,7 +1587,7 @@ end
end
end
- it "does not undo the Kernel.require decorations", :rubygems => ">= 3.4.6" do
+ it "does not undo the Kernel.require decorations", rubygems: ">= 3.4.6" do
install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
script = bundled_app("bin/script")
create_file(script, <<~RUBY)
@@ -1541,7 +1608,22 @@ end
require "foo"
RUBY
- sys_exec "#{Gem.ruby} #{script}", :raise_on_error => false
+ sys_exec "#{Gem.ruby} #{script}", raise_on_error: false
expect(out).to include("requiring foo used the monkeypatch")
end
+
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rack", :group => :test
+ G
+
+ bundle "config set auto_install 1"
+
+ ruby <<-RUBY
+ require 'bundler/setup'
+ RUBY
+ expect(err).to be_empty
+ expect(out).to include("Installing rack 1.0.0")
+ end
end
diff --git a/spec/bundler/runtime/with_unbundled_env_spec.rb b/spec/bundler/runtime/with_unbundled_env_spec.rb
index 731a9921a2..135c71b0af 100644
--- a/spec/bundler/runtime/with_unbundled_env_spec.rb
+++ b/spec/bundler/runtime/with_unbundled_env_spec.rb
@@ -90,9 +90,7 @@ RSpec.describe "Bundler.with_env helpers" do
RUBY
setup_require = "-r#{lib_dir}/bundler/setup"
ENV["BUNDLER_ORIG_RUBYOPT"] = "-W2 #{setup_require} #{ENV["RUBYOPT"]}"
- simulate_bundler_version_when_missing_prerelease_default_gem_activation do
- bundle_exec_ruby bundled_app("source.rb")
- end
+ bundle_exec_ruby bundled_app("source.rb")
expect(last_command.stdboth).not_to include(setup_require)
end
@@ -101,9 +99,7 @@ RSpec.describe "Bundler.with_env helpers" do
print #{modified_env}['RUBYOPT']
RUBY
ENV["BUNDLER_ORIG_RUBYOPT"] = "-W2 -rbundler/setup #{ENV["RUBYOPT"]}"
- simulate_bundler_version_when_missing_prerelease_default_gem_activation do
- bundle_exec_ruby bundled_app("source.rb")
- end
+ bundle_exec_ruby bundled_app("source.rb")
expect(last_command.stdboth).not_to include("-rbundler/setup")
end
@@ -134,7 +130,7 @@ RSpec.describe "Bundler.with_env helpers" do
it_behaves_like "an unbundling helper"
end
- describe "Bundler.clean_env", :bundler => 2 do
+ describe "Bundler.clean_env", bundler: 2 do
let(:modified_env) { "Bundler.clean_env" }
it_behaves_like "an unbundling helper"
@@ -143,7 +139,7 @@ RSpec.describe "Bundler.with_env helpers" do
describe "Bundler.with_original_env" do
it "should set ENV to original_env in the block" do
expected = Bundler.original_env
- actual = Bundler.with_original_env { Bundler::EnvironmentPreserver.env_to_hash(ENV) }
+ actual = Bundler.with_original_env { ENV.to_hash }
expect(actual).to eq(expected)
end
@@ -156,12 +152,12 @@ RSpec.describe "Bundler.with_env helpers" do
end
end
- describe "Bundler.with_clean_env", :bundler => 2 do
+ describe "Bundler.with_clean_env", bundler: 2 do
it "should set ENV to unbundled_env in the block" do
expected = Bundler.unbundled_env
actual = Bundler.ui.silence do
- Bundler.with_clean_env { Bundler::EnvironmentPreserver.env_to_hash(ENV) }
+ Bundler.with_clean_env { ENV.to_hash }
end
expect(actual).to eq(expected)
@@ -179,7 +175,7 @@ RSpec.describe "Bundler.with_env helpers" do
describe "Bundler.with_unbundled_env" do
it "should set ENV to unbundled_env in the block" do
expected = Bundler.unbundled_env
- actual = Bundler.with_unbundled_env { Bundler::EnvironmentPreserver.env_to_hash(ENV) }
+ actual = Bundler.with_unbundled_env { ENV.to_hash }
expect(actual).to eq(expected)
end
@@ -207,7 +203,7 @@ RSpec.describe "Bundler.with_env helpers" do
end
end
- describe "Bundler.clean_system", :bundler => 2 do
+ describe "Bundler.clean_system", bundler: 2 do
before do
create_file("source.rb", <<-'RUBY')
Bundler.ui.silence { Bundler.clean_system("ruby", "-e", "exit(42) unless ENV['BUNDLE_FOO'] == 'bar'") }
@@ -258,7 +254,7 @@ RSpec.describe "Bundler.with_env helpers" do
end
end
- describe "Bundler.clean_exec", :bundler => 2 do
+ describe "Bundler.clean_exec", bundler: 2 do
before do
create_file("source.rb", <<-'RUBY')
Process.fork do
diff --git a/spec/bundler/spec_helper.rb b/spec/bundler/spec_helper.rb
index 6a7e2891a6..66bdcfa028 100644
--- a/spec/bundler/spec_helper.rb
+++ b/spec/bundler/spec_helper.rb
@@ -17,6 +17,7 @@ require "rspec/support/differ"
require_relative "support/builders"
require_relative "support/build_metadata"
+require_relative "support/checksums"
require_relative "support/filters"
require_relative "support/helpers"
require_relative "support/indexes"
@@ -34,6 +35,7 @@ end
RSpec.configure do |config|
config.include Spec::Builders
+ config.include Spec::Checksums
config.include Spec::Helpers
config.include Spec::Indexes
config.include Spec::Matchers
@@ -46,6 +48,9 @@ RSpec.configure do |config|
config.silence_filter_announcements = !ENV["TEST_ENV_NUMBER"].nil?
+ config.backtrace_exclusion_patterns <<
+ %r{./spec/(spec_helper\.rb|support/.+)}
+
config.disable_monkey_patching!
# Since failures cause us to keep a bunch of long strings in memory, stop
@@ -72,7 +77,6 @@ RSpec.configure do |config|
require_relative "support/rubygems_ext"
Spec::Rubygems.test_setup
ENV["BUNDLER_SPEC_RUN"] = "true"
- ENV["BUNDLER_NO_OLD_RUBYGEMS_WARNING"] = "true"
ENV["BUNDLE_USER_CONFIG"] = ENV["BUNDLE_USER_CACHE"] = ENV["BUNDLE_USER_PLUGIN"] = nil
ENV["BUNDLE_APP_CONFIG"] = nil
ENV["BUNDLE_SILENCE_ROOT_WARNING"] = nil
@@ -84,7 +88,7 @@ RSpec.configure do |config|
ENV["THOR_COLUMNS"] = "10000"
extend(Spec::Helpers)
- system_gems :bundler, :path => pristine_system_gem_path
+ system_gems :bundler, path: pristine_system_gem_path
end
config.before :all do
@@ -114,6 +118,6 @@ RSpec.configure do |config|
end
config.after :suite do
- FileUtils.rm_r Spec::Path.pristine_system_gem_path
+ FileUtils.rm_rf Spec::Path.pristine_system_gem_path
end
end
diff --git a/spec/bundler/support/activate.rb b/spec/bundler/support/activate.rb
new file mode 100644
index 0000000000..143b77833d
--- /dev/null
+++ b/spec/bundler/support/activate.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require "rubygems"
+Gem.instance_variable_set(:@ruby, ENV["RUBY"]) if ENV["RUBY"]
+
+require_relative "path"
+bundler_gemspec = Spec::Path.loaded_gemspec
+bundler_gemspec.instance_variable_set(:@full_gem_path, Spec::Path.source_root.to_s)
+bundler_gemspec.activate if bundler_gemspec.respond_to?(:activate)
diff --git a/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb b/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb
index a6545b9ee4..83b147d2ae 100644
--- a/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb
+++ b/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb
@@ -4,10 +4,10 @@ require_relative "helpers/compact_index"
class CompactIndexChecksumMismatch < CompactIndexAPI
get "/versions" do
- headers "ETag" => quote("123")
+ headers "Repr-Digest" => "sha-256=:ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0=:"
headers "Surrogate-Control" => "max-age=2592000, stale-while-revalidate=60"
content_type "text/plain"
- body ""
+ body "content does not match the checksum"
end
end
diff --git a/spec/bundler/support/artifice/compact_index_concurrent_download.rb b/spec/bundler/support/artifice/compact_index_concurrent_download.rb
index 35548f278c..5d55b8a72b 100644
--- a/spec/bundler/support/artifice/compact_index_concurrent_download.rb
+++ b/spec/bundler/support/artifice/compact_index_concurrent_download.rb
@@ -7,11 +7,12 @@ class CompactIndexConcurrentDownload < CompactIndexAPI
versions = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index",
"localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions")
- # Verify the original (empty) content hasn't been deleted, e.g. on a retry
- File.binread(versions) == "" || raise("Original file should be present and empty")
+ # Verify the original content hasn't been deleted, e.g. on a retry
+ data = File.binread(versions)
+ data == "created_at" || raise("Original file should be present with expected content")
# Verify this is only requested once for a partial download
- env["HTTP_RANGE"] || raise("Missing Range header for expected partial download")
+ env["HTTP_RANGE"] == "bytes=#{data.bytesize - 1}-" || raise("Missing Range header for expected partial download")
# Overwrite the file in parallel, which should be then overwritten
# after a successful download to prevent corruption
diff --git a/spec/bundler/support/artifice/compact_index_etag_match.rb b/spec/bundler/support/artifice/compact_index_etag_match.rb
new file mode 100644
index 0000000000..08d7b5ec53
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_etag_match.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require_relative "helpers/compact_index"
+
+class CompactIndexEtagMatch < CompactIndexAPI
+ get "/versions" do
+ raise "ETag header should be present" unless env["HTTP_IF_NONE_MATCH"]
+ headers "ETag" => env["HTTP_IF_NONE_MATCH"]
+ status 304
+ body ""
+ end
+end
+
+require_relative "helpers/artifice"
+
+Artifice.activate_with(CompactIndexEtagMatch)
diff --git a/spec/bundler/support/artifice/compact_index_host_redirect.rb b/spec/bundler/support/artifice/compact_index_host_redirect.rb
index 9a711186db..4f82bf3812 100644
--- a/spec/bundler/support/artifice/compact_index_host_redirect.rb
+++ b/spec/bundler/support/artifice/compact_index_host_redirect.rb
@@ -3,7 +3,7 @@
require_relative "helpers/compact_index"
class CompactIndexHostRedirect < CompactIndexAPI
- get "/fetch/actual/gem/:id", :host_name => "localgemserver.test" do
+ get "/fetch/actual/gem/:id", host_name: "localgemserver.test" do
redirect "http://bundler.localgemserver.test#{request.path_info}"
end
diff --git a/spec/bundler/support/artifice/compact_index_partial_update.rb b/spec/bundler/support/artifice/compact_index_partial_update.rb
index 8c73011346..f111d91ef9 100644
--- a/spec/bundler/support/artifice/compact_index_partial_update.rb
+++ b/spec/bundler/support/artifice/compact_index_partial_update.rb
@@ -23,7 +23,7 @@ class CompactIndexPartialUpdate < CompactIndexAPI
# Verify that a partial request is made, starting from the index of the
# final byte of the cached file.
unless env["HTTP_RANGE"] == "bytes=#{File.binread(cached_versions_path).bytesize - 1}-"
- raise("Range header should be present, and start from the index of the final byte of the cache.")
+ raise("Range header should be present, and start from the index of the final byte of the cache. #{env["HTTP_RANGE"].inspect}")
end
etag_response do
diff --git a/spec/bundler/support/artifice/compact_index_partial_update_bad_digest.rb b/spec/bundler/support/artifice/compact_index_partial_update_bad_digest.rb
new file mode 100644
index 0000000000..ac04336636
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_partial_update_bad_digest.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require_relative "helpers/compact_index"
+
+# The purpose of this Artifice is to test that an incremental response is invalidated
+# and a second request is issued for the full content.
+class CompactIndexPartialUpdateBadDigest < CompactIndexAPI
+ def partial_update_bad_digest
+ response_body = yield
+ if request.env["HTTP_RANGE"]
+ headers "Repr-Digest" => "sha-256=:#{Digest::SHA256.base64digest("wrong digest on ranged request")}:"
+ else
+ headers "Repr-Digest" => "sha-256=:#{Digest::SHA256.base64digest(response_body)}:"
+ end
+ headers "Surrogate-Control" => "max-age=2592000, stale-while-revalidate=60"
+ content_type "text/plain"
+ requested_range_for(response_body)
+ end
+
+ get "/versions" do
+ partial_update_bad_digest do
+ file = tmp("versions.list")
+ FileUtils.rm_f(file)
+ file = CompactIndex::VersionsFile.new(file.to_s)
+ file.create(gems)
+ file.contents([], calculate_info_checksums: true)
+ end
+ end
+
+ get "/info/:name" do
+ partial_update_bad_digest do
+ gem = gems.find {|g| g.name == params[:name] }
+ CompactIndex.info(gem ? gem.versions : [])
+ end
+ end
+end
+
+require_relative "helpers/artifice"
+
+Artifice.activate_with(CompactIndexPartialUpdateBadDigest)
diff --git a/spec/bundler/support/artifice/compact_index_partial_update_no_digest_not_incremental.rb b/spec/bundler/support/artifice/compact_index_partial_update_no_digest_not_incremental.rb
new file mode 100644
index 0000000000..99bae039f0
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_partial_update_no_digest_not_incremental.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require_relative "helpers/compact_index"
+
+# The purpose of this Artifice is to test that an incremental response is ignored
+# when the digest is not present to verify that the partial response is valid.
+class CompactIndexPartialUpdateNoDigestNotIncremental < CompactIndexAPI
+ def partial_update_no_digest
+ response_body = yield
+ headers "Surrogate-Control" => "max-age=2592000, stale-while-revalidate=60"
+ content_type "text/plain"
+ requested_range_for(response_body)
+ end
+
+ get "/versions" do
+ partial_update_no_digest do
+ file = tmp("versions.list")
+ FileUtils.rm_f(file)
+ file = CompactIndex::VersionsFile.new(file.to_s)
+ file.create(gems)
+ lines = file.contents([], calculate_info_checksums: true).split("\n")
+ name, versions, checksum = lines.last.split(" ")
+
+ # shuffle versions so new versions are not appended to the end
+ [*lines[0..-2], [name, versions.split(",").reverse.join(","), checksum].join(" ")].join("\n")
+ end
+ end
+
+ get "/info/:name" do
+ partial_update_no_digest do
+ gem = gems.find {|g| g.name == params[:name] }
+ lines = CompactIndex.info(gem ? gem.versions : []).split("\n")
+
+ # shuffle versions so new versions are not appended to the end
+ [lines.first, lines.last, *lines[1..-2]].join("\n")
+ end
+ end
+end
+
+require_relative "helpers/artifice"
+
+Artifice.activate_with(CompactIndexPartialUpdateNoDigestNotIncremental)
diff --git a/spec/bundler/support/artifice/compact_index_partial_update_no_etag_not_incremental.rb b/spec/bundler/support/artifice/compact_index_partial_update_no_etag_not_incremental.rb
deleted file mode 100644
index 20546ba4c3..0000000000
--- a/spec/bundler/support/artifice/compact_index_partial_update_no_etag_not_incremental.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "helpers/compact_index"
-
-class CompactIndexPartialUpdateNoEtagNotIncremental < CompactIndexAPI
- def partial_update_no_etag
- response_body = yield
- headers "Surrogate-Control" => "max-age=2592000, stale-while-revalidate=60"
- content_type "text/plain"
- requested_range_for(response_body)
- end
-
- get "/versions" do
- partial_update_no_etag do
- file = tmp("versions.list")
- FileUtils.rm_f(file)
- file = CompactIndex::VersionsFile.new(file.to_s)
- file.create(gems)
- lines = file.contents([], :calculate_info_checksums => true).split("\n")
- name, versions, checksum = lines.last.split(" ")
-
- # shuffle versions so new versions are not appended to the end
- [*lines[0..-2], [name, versions.split(",").reverse.join(","), checksum].join(" ")].join("\n")
- end
- end
-
- get "/info/:name" do
- partial_update_no_etag do
- gem = gems.find {|g| g.name == params[:name] }
- lines = CompactIndex.info(gem ? gem.versions : []).split("\n")
-
- # shuffle versions so new versions are not appended to the end
- [lines.first, lines.last, *lines[1..-2]].join("\n")
- end
- end
-end
-
-require_relative "helpers/artifice"
-
-Artifice.activate_with(CompactIndexPartialUpdateNoEtagNotIncremental)
diff --git a/spec/bundler/support/artifice/compact_index_range_ignored.rb b/spec/bundler/support/artifice/compact_index_range_ignored.rb
new file mode 100644
index 0000000000..2303682c1f
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_range_ignored.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require_relative "helpers/compact_index"
+
+class CompactIndexRangeIgnored < CompactIndexAPI
+ # Stub the server to not return 304 so that we don't bypass all the logic
+ def not_modified?(_checksum)
+ false
+ end
+
+ get "/versions" do
+ cached_versions_path = File.join(
+ Bundler.rubygems.user_home, ".bundle", "cache", "compact_index",
+ "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions"
+ )
+
+ # Verify a cached copy of the versions file exists
+ unless File.binread(cached_versions_path).size > 0
+ raise("Cached versions file should be present and have content")
+ end
+
+ # Verify that a partial request is made, starting from the index of the
+ # final byte of the cached file.
+ unless env.delete("HTTP_RANGE")
+ raise("Expected client to write the full response on the first try")
+ end
+
+ etag_response do
+ file = tmp("versions.list")
+ FileUtils.rm_f(file)
+ file = CompactIndex::VersionsFile.new(file.to_s)
+ file.create(gems)
+ file.contents
+ end
+ end
+end
+
+require_relative "helpers/artifice"
+
+Artifice.activate_with(CompactIndexRangeIgnored)
diff --git a/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb b/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb
index fa25c4eca1..96259385e7 100644
--- a/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb
+++ b/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb
@@ -10,7 +10,7 @@ class CompactIndexStrictBasicAuthentication < CompactIndexAPI
# Only accepts password == "password"
unless env["HTTP_AUTHORIZATION"] == "Basic dXNlcjpwYXNz"
- halt 403, "Authentication failed"
+ halt 401, "Authentication failed"
end
end
end
diff --git a/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb b/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb
index acc13a56ff..9bd2ca0a9d 100644
--- a/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb
+++ b/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb
@@ -7,7 +7,8 @@ class CompactIndexWrongGemChecksum < CompactIndexAPI
etag_response do
name = params[:name]
gem = gems.find {|g| g.name == name }
- checksum = ENV.fetch("BUNDLER_SPEC_#{name.upcase}_CHECKSUM") { "ab" * 22 }
+ # This generates the hexdigest "2222222222222222222222222222222222222222222222222222222222222222"
+ checksum = ENV.fetch("BUNDLER_SPEC_#{name.upcase}_CHECKSUM") { "IiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI=" }
versions = gem ? gem.versions : []
versions.each {|v| v.checksum = checksum }
CompactIndex.info(versions)
diff --git a/spec/bundler/support/artifice/endpoint_500.rb b/spec/bundler/support/artifice/endpoint_500.rb
index d8ab6b65bc..b1ed1964c8 100644
--- a/spec/bundler/support/artifice/endpoint_500.rb
+++ b/spec/bundler/support/artifice/endpoint_500.rb
@@ -2,7 +2,7 @@
require_relative "../path"
-$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s))
+$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{mustermann,rack,tilt,sinatra,ruby2_keywords,base64}-*/lib")].map(&:to_s))
require "sinatra/base"
diff --git a/spec/bundler/support/artifice/endpoint_host_redirect.rb b/spec/bundler/support/artifice/endpoint_host_redirect.rb
index 0efb6cda02..6ce51bed93 100644
--- a/spec/bundler/support/artifice/endpoint_host_redirect.rb
+++ b/spec/bundler/support/artifice/endpoint_host_redirect.rb
@@ -3,7 +3,7 @@
require_relative "helpers/endpoint"
class EndpointHostRedirect < Endpoint
- get "/fetch/actual/gem/:id", :host_name => "localgemserver.test" do
+ get "/fetch/actual/gem/:id", host_name: "localgemserver.test" do
redirect "http://bundler.localgemserver.test#{request.path_info}"
end
diff --git a/spec/bundler/support/artifice/endpoint_mirror_source.rb b/spec/bundler/support/artifice/endpoint_mirror_source.rb
index 6ea1a77eca..fed7a746b9 100644
--- a/spec/bundler/support/artifice/endpoint_mirror_source.rb
+++ b/spec/bundler/support/artifice/endpoint_mirror_source.rb
@@ -4,7 +4,7 @@ require_relative "helpers/endpoint"
class EndpointMirrorSource < Endpoint
get "/gems/:id" do
- if request.env["HTTP_X_GEMFILE_SOURCE"] == "https://server.example.org/"
+ if request.env["HTTP_X_GEMFILE_SOURCE"] == "https://server.example.org/" && request.env["HTTP_USER_AGENT"].start_with?("bundler")
File.binread("#{gem_repo1}/gems/#{params[:id]}")
else
halt 500
diff --git a/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb b/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb
index 8ce1bdd4ad..dff360c5c5 100644
--- a/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb
+++ b/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb
@@ -10,7 +10,7 @@ class EndpointStrictBasicAuthentication < Endpoint
# Only accepts password == "password"
unless env["HTTP_AUTHORIZATION"] == "Basic dXNlcjpwYXNz"
- halt 403, "Authentication failed"
+ halt 401, "Authentication failed"
end
end
end
diff --git a/spec/bundler/support/artifice/fail.rb b/spec/bundler/support/artifice/fail.rb
index 6286e43fbd..8822e5b8e2 100644
--- a/spec/bundler/support/artifice/fail.rb
+++ b/spec/bundler/support/artifice/fail.rb
@@ -1,11 +1,11 @@
# frozen_string_literal: true
-require "net/http"
+require "bundler/vendored_net_http"
-class Fail < Net::HTTP
- # Net::HTTP uses a @newimpl instance variable to decide whether
+class Fail < Gem::Net::HTTP
+ # Gem::Net::HTTP uses a @newimpl instance variable to decide whether
# to use a legacy implementation. Since we are subclassing
- # Net::HTTP, we must set it
+ # Gem::Net::HTTP, we must set it
@newimpl = true
def request(req, body = nil, &block)
@@ -17,13 +17,11 @@ class Fail < Net::HTTP
end
def exception(req)
- name = ENV.fetch("BUNDLER_SPEC_EXCEPTION") { "Errno::ENETUNREACH" }
- const = name.split("::").reduce(Object) {|mod, sym| mod.const_get(sym) }
- const.new("host down: Bundler spec artifice fail! #{req["PATH_INFO"]}")
+ Errno::ENETUNREACH.new("host down: Bundler spec artifice fail! #{req["PATH_INFO"]}")
end
end
require_relative "helpers/artifice"
-# Replace Net::HTTP with our failing subclass
+# Replace Gem::Net::HTTP with our failing subclass
Artifice.replace_net_http(::Fail)
diff --git a/spec/bundler/support/artifice/helpers/artifice.rb b/spec/bundler/support/artifice/helpers/artifice.rb
index b8c78614fb..788268295c 100644
--- a/spec/bundler/support/artifice/helpers/artifice.rb
+++ b/spec/bundler/support/artifice/helpers/artifice.rb
@@ -4,7 +4,7 @@
module Artifice
# Activate Artifice with a particular Rack endpoint.
#
- # Calling this method will replace the Net::HTTP system
+ # Calling this method will replace the Gem::Net::HTTP system
# with a replacement that routes all requests to the
# Rack endpoint.
#
@@ -18,11 +18,11 @@ module Artifice
# Deactivate the Artifice replacement.
def self.deactivate
- replace_net_http(::Net::HTTP)
+ replace_net_http(::Gem::Net::HTTP)
end
def self.replace_net_http(value)
- ::Net.class_eval do
+ ::Gem::Net.class_eval do
remove_const(:HTTP)
const_set(:HTTP, value)
end
diff --git a/spec/bundler/support/artifice/helpers/compact_index.rb b/spec/bundler/support/artifice/helpers/compact_index.rb
index 4df47a9659..a803a2d30a 100644
--- a/spec/bundler/support/artifice/helpers/compact_index.rb
+++ b/spec/bundler/support/artifice/helpers/compact_index.rb
@@ -4,6 +4,7 @@ require_relative "endpoint"
$LOAD_PATH.unshift Dir[Spec::Path.base_system_gem_path.join("gems/compact_index*/lib")].first.to_s
require "compact_index"
+require "digest"
class CompactIndexAPI < Endpoint
helpers do
@@ -17,9 +18,10 @@ class CompactIndexAPI < Endpoint
def etag_response
response_body = yield
- checksum = Digest(:MD5).hexdigest(response_body)
- return if not_modified?(checksum)
- headers "ETag" => quote(checksum)
+ etag = Digest::MD5.hexdigest(response_body)
+ headers "ETag" => quote(etag)
+ return if not_modified?(etag)
+ headers "Repr-Digest" => "sha-256=:#{Digest::SHA256.base64digest(response_body)}:"
headers "Surrogate-Control" => "max-age=2592000, stale-while-revalidate=60"
content_type "text/plain"
requested_range_for(response_body)
@@ -29,11 +31,10 @@ class CompactIndexAPI < Endpoint
raise
end
- def not_modified?(checksum)
+ def not_modified?(etag)
etags = parse_etags(request.env["HTTP_IF_NONE_MATCH"])
- return unless etags.include?(checksum)
- headers "ETag" => quote(checksum)
+ return unless etags.include?(etag)
status 304
body ""
end
@@ -75,15 +76,17 @@ class CompactIndexAPI < Endpoint
specs.group_by(&:name).map do |name, versions|
gem_versions = versions.map do |spec|
- deps = spec.dependencies.select {|d| d.type == :runtime }.map do |d|
+ deps = spec.runtime_dependencies.map do |d|
reqs = d.requirement.requirements.map {|r| r.join(" ") }.join(", ")
CompactIndex::Dependency.new(d.name, reqs)
end
- checksum = begin
- Digest(:SHA256).file("#{gem_repo}/gems/#{spec.original_name}.gem").base64digest
- rescue StandardError
- nil
- end
+ begin
+ checksum = ENV.fetch("BUNDLER_SPEC_#{name.upcase}_CHECKSUM") do
+ Digest(:SHA256).file("#{gem_repo}/gems/#{spec.original_name}.gem").hexdigest
+ end
+ rescue StandardError
+ checksum = nil
+ end
CompactIndex::GemVersion.new(spec.version.version, spec.platform.to_s, checksum, nil,
deps, spec.required_ruby_version.to_s, spec.required_rubygems_version.to_s)
end
diff --git a/spec/bundler/support/artifice/helpers/endpoint.rb b/spec/bundler/support/artifice/helpers/endpoint.rb
index fc0381dc38..83ba1be0fc 100644
--- a/spec/bundler/support/artifice/helpers/endpoint.rb
+++ b/spec/bundler/support/artifice/helpers/endpoint.rb
@@ -2,7 +2,7 @@
require_relative "../../path"
-$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s))
+$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{mustermann,rack,tilt,sinatra,ruby2_keywords,base64}-*/lib")].map(&:to_s))
require "sinatra/base"
@@ -69,10 +69,10 @@ class Endpoint < Sinatra::Base
spec = load_spec(name, version, platform, gem_repo)
next unless gem_names.include?(spec.name)
{
- :name => spec.name,
- :number => spec.version.version,
- :platform => spec.platform.to_s,
- :dependencies => spec.dependencies.select {|dep| dep.type == :runtime }.map do |dep|
+ name: spec.name,
+ number: spec.version.version,
+ platform: spec.platform.to_s,
+ dependencies: spec.runtime_dependencies.map do |dep|
[dep.name, dep.requirement.requirements.map {|a| a.join(" ") }.join(", ")]
end,
}
diff --git a/spec/bundler/support/artifice/helpers/rack_request.rb b/spec/bundler/support/artifice/helpers/rack_request.rb
index c4a07812a6..f419bacb8c 100644
--- a/spec/bundler/support/artifice/helpers/rack_request.rb
+++ b/spec/bundler/support/artifice/helpers/rack_request.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "rack/test"
-require "net/http"
+require "bundler/vendored_net_http"
module Artifice
module Net
@@ -16,25 +16,25 @@ module Artifice
end
end
- class HTTP < ::Net::HTTP
+ class HTTP < ::Gem::Net::HTTP
class << self
attr_accessor :endpoint
end
- # Net::HTTP uses a @newimpl instance variable to decide whether
+ # Gem::Net::HTTP uses a @newimpl instance variable to decide whether
# to use a legacy implementation. Since we are subclassing
- # Net::HTTP, we must set it
+ # Gem::Net::HTTP, we must set it
@newimpl = true
# We don't need to connect, so blank out this method
def connect
end
- # Replace the Net::HTTP request method with a method
+ # Replace the Gem::Net::HTTP request method with a method
# that converts the request into a Rack request and
# dispatches it to the Rack endpoint.
#
- # @param [Net::HTTPRequest] req A Net::HTTPRequest
+ # @param [Net::HTTPRequest] req A Gem::Net::HTTPRequest
# object, or one if its subclasses
# @param [optional, String, #read] body This should
# be sent as "rack.input". If it's a String, it will
@@ -42,7 +42,7 @@ module Artifice
# @return [Net::HTTPResponse]
#
# @yield [Net::HTTPResponse] If a block is provided,
- # this method will yield the Net::HTTPResponse to
+ # this method will yield the Gem::Net::HTTPResponse to
# it after the body is read.
def request(req, body = nil, &block)
rack_request = RackRequest.new(self.class.endpoint)
@@ -56,17 +56,17 @@ module Artifice
body_stream_contents = req.body_stream.read if req.body_stream
response = rack_request.request("#{prefix}#{req.path}",
- { :method => req.method, :input => body || req.body || body_stream_contents })
+ { method: req.method, input: body || req.body || body_stream_contents })
make_net_http_response(response, &block)
end
private
- # This method takes a Rack response and creates a Net::HTTPResponse
+ # This method takes a Rack response and creates a Gem::Net::HTTPResponse
# Instead of trying to mock HTTPResponse directly, we just convert
# the Rack response into a String that looks like a normal HTTP
- # response and call Net::HTTPResponse.read_new
+ # response and call Gem::Net::HTTPResponse.read_new
#
# @param [Array(#to_i, Hash, #each)] response a Rack response
# @return [Net::HTTPResponse]
@@ -86,8 +86,8 @@ module Artifice
response_string << "" << body
- response_io = ::Net::BufferedIO.new(StringIO.new(response_string.join("\n")))
- res = ::Net::HTTPResponse.read_new(response_io)
+ response_io = ::Gem::Net::BufferedIO.new(StringIO.new(response_string.join("\n")))
+ res = ::Gem::Net::HTTPResponse.read_new(response_io)
res.reading_body(response_io, true) do
yield res if block_given?
diff --git a/spec/bundler/support/artifice/vcr.rb b/spec/bundler/support/artifice/vcr.rb
index 6a346f1ff9..7b9a8bdeaf 100644
--- a/spec/bundler/support/artifice/vcr.rb
+++ b/spec/bundler/support/artifice/vcr.rb
@@ -1,13 +1,13 @@
# frozen_string_literal: true
-require "net/http"
+require "bundler/vendored_net_http"
require_relative "../path"
-CASSETTE_PATH = "#{Spec::Path.spec_dir}/support/artifice/vcr_cassettes"
-USED_CASSETTES_PATH = "#{Spec::Path.spec_dir}/support/artifice/used_cassettes.txt"
+CASSETTE_PATH = "#{Spec::Path.spec_dir}/support/artifice/vcr_cassettes".freeze
+USED_CASSETTES_PATH = "#{Spec::Path.spec_dir}/support/artifice/used_cassettes.txt".freeze
CASSETTE_NAME = ENV.fetch("BUNDLER_SPEC_VCR_CASSETTE_NAME") { "realworld" }
-class BundlerVCRHTTP < Net::HTTP
+class BundlerVCRHTTP < Gem::Net::HTTP
class RequestHandler
attr_reader :http, :request, :body, :response_block
def initialize(http, request, body = nil, &response_block)
@@ -41,8 +41,8 @@ class BundlerVCRHTTP < Net::HTTP
def recorded_response
File.open(request_pair_paths.last, "rb:ASCII-8BIT") do |response_file|
- response_io = ::Net::BufferedIO.new(response_file)
- ::Net::HTTPResponse.read_new(response_io).tap do |response|
+ response_io = ::Gem::Net::BufferedIO.new(response_file)
+ ::Gem::Net::HTTPResponse.read_new(response_io).tap do |response|
response.decode_content = request.decode_content if request.respond_to?(:decode_content)
response.uri = request.uri
@@ -148,5 +148,5 @@ end
require_relative "helpers/artifice"
-# Replace Net::HTTP with our VCR subclass
+# Replace Gem::Net::HTTP with our VCR subclass
Artifice.replace_net_http(BundlerVCRHTTP)
diff --git a/spec/bundler/support/artifice/windows.rb b/spec/bundler/support/artifice/windows.rb
index 4d90e0a426..fea991c071 100644
--- a/spec/bundler/support/artifice/windows.rb
+++ b/spec/bundler/support/artifice/windows.rb
@@ -2,7 +2,7 @@
require_relative "../path"
-$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s))
+$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{mustermann,rack,tilt,sinatra,ruby2_keywords,base64}-*/lib")].map(&:to_s))
require "sinatra/base"
diff --git a/spec/bundler/support/build_metadata.rb b/spec/bundler/support/build_metadata.rb
index 98d8ac23c8..5898e7f3bd 100644
--- a/spec/bundler/support/build_metadata.rb
+++ b/spec/bundler/support/build_metadata.rb
@@ -10,20 +10,20 @@ module Spec
def write_build_metadata(dir: source_root)
build_metadata = {
- :git_commit_sha => git_commit_sha,
- :built_at => loaded_gemspec.date.utc.strftime("%Y-%m-%d"),
- :release => true,
+ git_commit_sha: git_commit_sha,
+ built_at: loaded_gemspec.date.utc.strftime("%Y-%m-%d"),
+ release: true,
}
- replace_build_metadata(build_metadata, dir: dir) # rubocop:disable Style/HashSyntax
+ replace_build_metadata(build_metadata, dir: dir)
end
def reset_build_metadata(dir: source_root)
build_metadata = {
- :release => false,
+ release: false,
}
- replace_build_metadata(build_metadata, dir: dir) # rubocop:disable Style/HashSyntax
+ replace_build_metadata(build_metadata, dir: dir)
end
private
@@ -41,7 +41,7 @@ module Spec
end
def git_commit_sha
- ruby_core_tarball? ? "unknown" : sys_exec("git rev-parse --short HEAD", :dir => source_root).strip
+ ruby_core_tarball? ? "unknown" : sys_exec("git rev-parse --short HEAD", dir: source_root).strip
end
extend self
diff --git a/spec/bundler/support/builders.rb b/spec/bundler/support/builders.rb
index 7c16470153..ab2dafb0b9 100644
--- a/spec/bundler/support/builders.rb
+++ b/spec/bundler/support/builders.rb
@@ -17,6 +17,10 @@ module Spec
Gem::Platform.new(platform)
end
+ def rake_version
+ "13.2.1"
+ end
+
def build_repo1
rake_path = Dir["#{Path.base_system_gems}/**/rake*.gem"].first
@@ -49,7 +53,7 @@ module Spec
build_gem "rails", "2.3.2" do |s|
s.executables = "rails"
- s.add_dependency "rake", "13.0.1"
+ s.add_dependency "rake", rake_version
s.add_dependency "actionpack", "2.3.2"
s.add_dependency "activerecord", "2.3.2"
s.add_dependency "actionmailer", "2.3.2"
@@ -73,11 +77,11 @@ module Spec
s.add_dependency "activesupport", ">= 2.0.0"
end
- build_gem "rspec", "1.2.7", :no_default => true do |s|
+ build_gem "rspec", "1.2.7", no_default: true do |s|
s.write "lib/spec.rb", "SPEC = '1.2.7'"
end
- build_gem "rack-test", :no_default => true do |s|
+ build_gem "rack-test", no_default: true do |s|
s.write "lib/rack/test.rb", "RACK_TEST = '1.0'"
end
@@ -191,27 +195,25 @@ module Spec
end
end
- def build_repo2(&blk)
+ def build_repo2(**kwargs, &blk)
FileUtils.rm_rf gem_repo2
FileUtils.cp_r gem_repo1, gem_repo2
- update_repo2(&blk) if block_given?
+ update_repo2(**kwargs, &blk) if block_given?
end
# A repo that has no pre-installed gems included. (The caller completely
# determines the contents with the block.)
- def build_repo4(&blk)
+ def build_repo4(**kwargs, &blk)
FileUtils.rm_rf gem_repo4
- build_repo(gem_repo4, &blk)
+ build_repo(gem_repo4, **kwargs, &blk)
end
def update_repo4(&blk)
update_repo(gem_repo4, &blk)
end
- def update_repo2
- update_repo gem_repo2 do
- yield if block_given?
- end
+ def update_repo2(**kwargs, &blk)
+ update_repo(gem_repo2, **kwargs, &blk)
end
def build_security_repo
@@ -229,12 +231,12 @@ module Spec
end
end
- def build_repo(path, &blk)
+ def build_repo(path, **kwargs, &blk)
return if File.directory?(path)
FileUtils.mkdir_p("#{path}/gems")
- update_repo(path, &blk)
+ update_repo(path,**kwargs, &blk)
end
def check_test_gems!
@@ -251,7 +253,7 @@ module Spec
end
end
- def update_repo(path)
+ def update_repo(path, build_compact_index: true)
if path == gem_repo1 && caller.first.split(" ").last == "`build_repo`"
raise "Updating gem_repo1 is unsupported -- use gem_repo2 instead"
end
@@ -260,7 +262,12 @@ module Spec
@_build_repo = File.basename(path)
yield
with_gem_path_as Path.base_system_gem_path do
- gem_command :generate_index, :dir => path
+ Dir[Spec::Path.base_system_gem_path.join("gems/rubygems-generate_index*/lib")].first ||
+ raise("Could not find rubygems-generate_index lib directory in #{Spec::Path.base_system_gem_path}")
+
+ command = "generate_index"
+ command += " --no-compact" if !build_compact_index && gem_command(command + " --help").include?("--[no-]compact")
+ gem_command command, dir: path
end
ensure
@_build_path = nil
@@ -290,6 +297,10 @@ module Spec
build_with(LibBuilder, name, args, &blk)
end
+ def build_bundler(*args, &blk)
+ build_with(BundlerBuilder, "bundler", args, &blk)
+ end
+
def build_gem(name, *args, &blk)
build_with(GemBuilder, name, args, &blk)
end
@@ -395,6 +406,49 @@ module Spec
alias_method :dep, :runtime
end
+ class BundlerBuilder
+ attr_writer :required_ruby_version
+
+ def initialize(context, name, version)
+ raise "can only build bundler" unless name == "bundler"
+
+ @context = context
+ @version = version || Bundler::VERSION
+ end
+
+ def _build(options = {})
+ full_name = "bundler-#{@version}"
+ build_path = @context.tmp + full_name
+ bundler_path = build_path + "#{full_name}.gem"
+
+ FileUtils.mkdir_p build_path
+
+ @context.shipped_files.each do |shipped_file|
+ target_shipped_file = shipped_file
+ target_shipped_file = shipped_file.sub(/\Alibexec/, "exe") if @context.ruby_core?
+ target_shipped_file = build_path + target_shipped_file
+ target_shipped_dir = File.dirname(target_shipped_file)
+ FileUtils.mkdir_p target_shipped_dir unless File.directory?(target_shipped_dir)
+ FileUtils.cp shipped_file, target_shipped_file, preserve: true
+ end
+
+ @context.replace_version_file(@version, dir: build_path)
+ @context.replace_required_ruby_version(@required_ruby_version, dir: build_path) if @required_ruby_version
+
+ Spec::BuildMetadata.write_build_metadata(dir: build_path)
+
+ @context.gem_command "build #{@context.relative_gemspec}", dir: build_path
+
+ if block_given?
+ yield(bundler_path)
+ else
+ FileUtils.mv bundler_path, options[:path]
+ end
+ ensure
+ build_path.rmtree
+ end
+ end
+
class LibBuilder
def initialize(context, name, version)
@context = context
@@ -440,8 +494,6 @@ module Spec
write "ext/extconf.rb", <<-RUBY
require "mkmf"
- $extout = "$(topdir)/" + RbConfig::CONFIG["EXTOUT"]
-
extension_name = "#{name}_c"
if extra_lib_dir = with_config("ext-lib")
# add extra libpath if --with-ext-lib is
@@ -523,7 +575,7 @@ module Spec
default_branch = options[:default_branch] || "main"
path = options[:path] || _default_path
source = options[:source] || "git@#{path}"
- super(options.merge(:path => path, :source => source))
+ super(options.merge(path: path, source: source))
@context.git("config --global init.defaultBranch #{default_branch}", path)
@context.git("init", path)
@context.git("add *", path)
@@ -537,7 +589,7 @@ module Spec
class GitBareBuilder < LibBuilder
def _build(options)
path = options[:path] || _default_path
- super(options.merge(:path => path))
+ super(options.merge(path: path))
@context.git("init --bare", path)
end
end
@@ -562,7 +614,7 @@ module Spec
_default_files.keys.each do |path|
_default_files[path] += "\n#{Builders.constantize(name)}_PREV_REF = '#{current_ref}'"
end
- super(options.merge(:path => libpath, :gemspec => update_gemspec, :source => source))
+ super(options.merge(path: libpath, gemspec: update_gemspec, source: source))
@context.git("commit -am BUMP", libpath)
end
end
@@ -585,7 +637,7 @@ module Spec
class GemBuilder < LibBuilder
def _build(opts)
lib_path = opts[:lib_path] || @context.tmp(".tmp/#{@spec.full_name}")
- lib_path = super(opts.merge(:path => lib_path, :no_default => opts[:no_default]))
+ lib_path = super(opts.merge(path: lib_path, no_default: opts[:no_default]))
destination = opts[:path] || _default_path
FileUtils.mkdir_p(lib_path.join(destination))
@@ -594,16 +646,16 @@ module Spec
Bundler.rubygems.build(@spec, opts[:skip_validation])
end
elsif opts[:skip_validation]
- @context.gem_command "build --force #{@spec.name}", :dir => lib_path
+ @context.gem_command "build --force #{@spec.name}", dir: lib_path
else
- @context.gem_command "build #{@spec.name}", :dir => lib_path
+ @context.gem_command "build #{@spec.name}", dir: lib_path
end
gem_path = File.expand_path("#{@spec.full_name}.gem", lib_path)
if opts[:to_system]
- @context.system_gems gem_path, :default => opts[:default]
+ @context.system_gems gem_path, default: opts[:default]
elsif opts[:to_bundle]
- @context.system_gems gem_path, :path => @context.default_bundle_path
+ @context.system_gems gem_path, path: @context.default_bundle_path
else
FileUtils.mv(gem_path, destination)
end
diff --git a/spec/bundler/support/bundle.rb b/spec/bundler/support/bundle.rb
index 5f808531ff..5d6d658040 100644
--- a/spec/bundler/support/bundle.rb
+++ b/spec/bundler/support/bundle.rb
@@ -1,10 +1,5 @@
# frozen_string_literal: true
-require "rubygems"
-Gem.instance_variable_set(:@ruby, ENV["RUBY"]) if ENV["RUBY"]
+require_relative "activate"
-require_relative "path"
-bundler_gemspec = Spec::Path.loaded_gemspec
-bundler_gemspec.instance_variable_set(:@full_gem_path, Spec::Path.source_root)
-bundler_gemspec.activate if bundler_gemspec.respond_to?(:activate)
load File.expand_path("bundle", Spec::Path.bindir)
diff --git a/spec/bundler/support/checksums.rb b/spec/bundler/support/checksums.rb
new file mode 100644
index 0000000000..f758559b3b
--- /dev/null
+++ b/spec/bundler/support/checksums.rb
@@ -0,0 +1,114 @@
+# frozen_string_literal: true
+
+module Spec
+ module Checksums
+ class ChecksumsBuilder
+ def initialize(enabled = true, &block)
+ @enabled = enabled
+ @checksums = {}
+ yield self if block_given?
+ end
+
+ def initialize_copy(original)
+ super
+ @checksums = @checksums.dup
+ end
+
+ def checksum(repo, name, version, platform = Gem::Platform::RUBY)
+ name_tuple = Gem::NameTuple.new(name, version, platform)
+ gem_file = File.join(repo, "gems", "#{name_tuple.full_name}.gem")
+ File.open(gem_file, "rb") do |f|
+ register(name_tuple, Bundler::Checksum.from_gem(f, "#{gem_file} (via ChecksumsBuilder#checksum)"))
+ end
+ end
+
+ def no_checksum(name, version, platform = Gem::Platform::RUBY)
+ name_tuple = Gem::NameTuple.new(name, version, platform)
+ register(name_tuple, nil)
+ end
+
+ def delete(name, platform = nil)
+ @checksums.reject! {|k, _| k.name == name && (platform.nil? || k.platform == platform) }
+ end
+
+ def to_s
+ return "" unless @enabled
+
+ locked_checksums = @checksums.map do |name_tuple, checksum|
+ checksum &&= " #{checksum.to_lock}"
+ " #{name_tuple.lock_name}#{checksum}\n"
+ end
+
+ "\nCHECKSUMS\n#{locked_checksums.sort.join}"
+ end
+
+ private
+
+ def register(name_tuple, checksum)
+ delete(name_tuple.name, name_tuple.platform)
+ @checksums[name_tuple] = checksum
+ end
+ end
+
+ def checksums_section(enabled = true, &block)
+ ChecksumsBuilder.new(enabled, &block)
+ end
+
+ def checksums_section_when_existing(&block)
+ begin
+ enabled = lockfile.match?(/^CHECKSUMS$/)
+ rescue Errno::ENOENT
+ enabled = false
+ end
+ checksums_section(enabled, &block)
+ end
+
+ def checksum_to_lock(*args)
+ checksums_section do |c|
+ c.checksum(*args)
+ end.to_s.sub(/^CHECKSUMS\n/, "").strip
+ end
+
+ def checksum_digest(*args)
+ checksum_to_lock(*args).split(Bundler::Checksum::ALGO_SEPARATOR, 2).last
+ end
+
+ # if prefixes is given, removes all checksums where the line
+ # has any of the prefixes on the line before the checksum
+ # otherwise, removes all checksums from the lockfile
+ def remove_checksums_from_lockfile(lockfile, *prefixes)
+ head, remaining = lockfile.split(/^CHECKSUMS$/, 2)
+ return lockfile unless remaining
+ checksums, tail = remaining.split("\n\n", 2)
+
+ prefixes =
+ if prefixes.empty?
+ nil
+ else
+ /(#{prefixes.map {|p| Regexp.escape(p) }.join("|")})/
+ end
+
+ checksums = checksums.each_line.map do |line|
+ if prefixes.nil? || line.match?(prefixes)
+ line.gsub(/ sha256=[a-f0-9]{64}/i, "")
+ else
+ line
+ end
+ end
+
+ head.concat(
+ "CHECKSUMS",
+ checksums.join,
+ "\n\n",
+ tail
+ )
+ end
+
+ def remove_checksums_section_from_lockfile(lockfile)
+ head, remaining = lockfile.split(/^CHECKSUMS$/, 2)
+ return lockfile unless remaining
+ _checksums, tail = remaining.split("\n\n", 2)
+ head.concat(tail)
+ end
+ end
+end
diff --git a/spec/bundler/support/command_execution.rb b/spec/bundler/support/command_execution.rb
index 68e5c56c75..5639fda3b6 100644
--- a/spec/bundler/support/command_execution.rb
+++ b/spec/bundler/support/command_execution.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Spec
- CommandExecution = Struct.new(:command, :working_directory, :exitstatus, :stdout, :stderr) do
+ CommandExecution = Struct.new(:command, :working_directory, :exitstatus, :original_stdout, :original_stderr) do
def to_s
"$ #{command}"
end
@@ -11,6 +11,19 @@ module Spec
@stdboth ||= [stderr, stdout].join("\n").strip
end
+ def stdout
+ original_stdout
+ end
+
+ # Can be removed once/if https://github.com/oneclick/rubyinstaller2/pull/369 is resolved
+ def stderr
+ return original_stderr unless Gem.win_platform?
+
+ original_stderr.split("\n").reject do |l|
+ l.include?("operating_system_defaults")
+ end.join("\n")
+ end
+
def to_s_verbose
[
to_s,
diff --git a/spec/bundler/support/filters.rb b/spec/bundler/support/filters.rb
index 78545d2e64..8e164af756 100644
--- a/spec/bundler/support/filters.rb
+++ b/spec/bundler/support/filters.rb
@@ -19,20 +19,20 @@ class RequirementChecker < Proc
end
RSpec.configure do |config|
- config.filter_run_excluding :realworld => true
+ config.filter_run_excluding realworld: true
git_version = Bundler::Source::Git::GitProxy.new(nil, nil).version
- config.filter_run_excluding :git => RequirementChecker.against(git_version)
- config.filter_run_excluding :bundler => RequirementChecker.against(Bundler::VERSION.split(".")[0])
- config.filter_run_excluding :rubygems => RequirementChecker.against(Gem::VERSION)
- config.filter_run_excluding :ruby_repo => !ENV["GEM_COMMAND"].nil?
- config.filter_run_excluding :no_color_tty => Gem.win_platform? || !ENV["GITHUB_ACTION"].nil?
- config.filter_run_excluding :permissions => Gem.win_platform?
- config.filter_run_excluding :readline => Gem.win_platform?
- config.filter_run_excluding :jruby_only => RUBY_ENGINE != "jruby"
- config.filter_run_excluding :truffleruby_only => RUBY_ENGINE != "truffleruby"
- config.filter_run_excluding :man => Gem.win_platform?
+ config.filter_run_excluding git: RequirementChecker.against(git_version)
+ config.filter_run_excluding bundler: RequirementChecker.against(Bundler::VERSION.split(".")[0])
+ config.filter_run_excluding rubygems: RequirementChecker.against(Gem::VERSION)
+ config.filter_run_excluding ruby_repo: !ENV["GEM_COMMAND"].nil?
+ config.filter_run_excluding no_color_tty: Gem.win_platform? || !ENV["GITHUB_ACTION"].nil?
+ config.filter_run_excluding permissions: Gem.win_platform?
+ config.filter_run_excluding readline: Gem.win_platform?
+ config.filter_run_excluding jruby_only: RUBY_ENGINE != "jruby"
+ config.filter_run_excluding truffleruby_only: RUBY_ENGINE != "truffleruby"
+ config.filter_run_excluding man: Gem.win_platform?
config.filter_run_when_matching :focus unless ENV["CI"]
end
diff --git a/spec/bundler/support/helpers.rb b/spec/bundler/support/helpers.rb
index 7b8c56b6ad..1ad9cc78ca 100644
--- a/spec/bundler/support/helpers.rb
+++ b/spec/bundler/support/helpers.rb
@@ -43,7 +43,7 @@ module Spec
last_command.stderr
end
- MAJOR_DEPRECATION = /^\[DEPRECATED\]\s*/.freeze
+ MAJOR_DEPRECATION = /^\[DEPRECATED\]\s*/
def err_without_deprecations
err.gsub(/#{MAJOR_DEPRECATION}.+[\n]?/, "")
@@ -60,7 +60,7 @@ module Spec
def run(cmd, *args)
opts = args.last.is_a?(Hash) ? args.pop : {}
groups = args.map(&:inspect).join(", ")
- setup = "require '#{entrypoint}' ; Bundler.ui.silence { Bundler.setup(#{groups}) }"
+ setup = "require 'bundler' ; Bundler.ui.silence { Bundler.setup(#{groups}) }"
ruby([setup, cmd].join(" ; "), opts)
end
@@ -116,9 +116,9 @@ module Spec
end
end.join
- ruby_cmd = build_ruby_cmd({ :load_path => load_path, :requires => requires })
+ ruby_cmd = build_ruby_cmd({ load_path: load_path, requires: requires, env: env })
cmd = "#{ruby_cmd} #{bundle_bin} #{cmd}#{args}"
- sys_exec(cmd, { :env => env, :dir => dir, :raise_on_error => raise_on_error }, &block)
+ sys_exec(cmd, { env: env, dir: dir, raise_on_error: raise_on_error }, &block)
end
def bundler(cmd, options = {})
@@ -147,7 +147,13 @@ module Spec
lib_option = libs ? "-I#{libs.join(File::PATH_SEPARATOR)}" : []
requires = options.delete(:requires) || []
- requires << "#{Path.spec_dir}/support/hax.rb"
+
+ hax_path = "#{Path.spec_dir}/support/hax.rb"
+
+ # For specs that need to ignore the default Bundler gem, load hax before
+ # anything else since other stuff may actually load bundler and not skip
+ # the default version
+ options[:env]&.include?("BUNDLER_IGNORE_DEFAULT_GEM") ? requires.prepend(hax_path) : requires.append(hax_path)
require_option = requires.map {|r| "-r#{r}" }
[Gem.ruby, *lib_option, *require_option].compact.join(" ")
@@ -170,7 +176,7 @@ module Spec
end
def git(cmd, path, options = {})
- sys_exec("git #{cmd}", options.merge(:dir => path))
+ sys_exec("git #{cmd}", options.merge(dir: path))
end
def sys_exec(cmd, options = {})
@@ -181,14 +187,14 @@ module Spec
require "open3"
require "shellwords"
- Open3.popen3(env, *cmd.shellsplit, :chdir => dir) do |stdin, stdout, stderr, wait_thr|
+ Open3.popen3(env, *cmd.shellsplit, chdir: dir) do |stdin, stdout, stderr, wait_thr|
yield stdin, stdout, wait_thr if block_given?
stdin.close
stdout_read_thread = Thread.new { stdout.read }
stderr_read_thread = Thread.new { stderr.read }
- command_execution.stdout = stdout_read_thread.value.strip
- command_execution.stderr = stderr_read_thread.value.strip
+ command_execution.original_stdout = stdout_read_thread.value.strip
+ command_execution.original_stderr = stderr_read_thread.value.strip
status = wait_thr.value
command_execution.exitstatus = if status.exited?
@@ -244,7 +250,7 @@ module Spec
contents = args.pop
if contents.nil?
- File.open(bundled_app_gemfile, "r", &:read)
+ read_gemfile
else
create_file(args.pop || "Gemfile", contents)
end
@@ -254,12 +260,24 @@ module Spec
contents = args.pop
if contents.nil?
- File.open(bundled_app_lock, "r", &:read)
+ read_lockfile
else
create_file(args.pop || "Gemfile.lock", contents)
end
end
+ def read_gemfile(file = "Gemfile")
+ read_bundled_app_file(file)
+ end
+
+ def read_lockfile(file = "Gemfile.lock")
+ read_bundled_app_file(file)
+ end
+
+ def read_bundled_app_file(file)
+ bundled_app(file).read
+ end
+
def strip_whitespace(str)
# Trim the leading spaces
spaces = str[/\A\s+/, 0] || ""
@@ -281,59 +299,35 @@ module Spec
def system_gems(*gems)
gems = gems.flatten
options = gems.last.is_a?(Hash) ? gems.pop : {}
- path = options.fetch(:path, system_gem_path)
+ install_dir = options.fetch(:path, system_gem_path)
default = options.fetch(:default, false)
- with_gem_path_as(path) do
+ with_gem_path_as(install_dir) do
gem_repo = options.fetch(:gem_repo, gem_repo1)
gems.each do |g|
gem_name = g.to_s
if gem_name.start_with?("bundler")
version = gem_name.match(/\Abundler-(?<version>.*)\z/)[:version] if gem_name != "bundler"
- with_built_bundler(version) {|gem_path| install_gem(gem_path, default) }
+ with_built_bundler(version) {|gem_path| install_gem(gem_path, install_dir, default) }
elsif %r{\A(?:[a-zA-Z]:)?/.*\.gem\z}.match?(gem_name)
- install_gem(gem_name, default)
+ install_gem(gem_name, install_dir, default)
else
- install_gem("#{gem_repo}/gems/#{gem_name}.gem", default)
+ install_gem("#{gem_repo}/gems/#{gem_name}.gem", install_dir, default)
end
end
end
end
- def install_gem(path, default = false)
+ def install_gem(path, install_dir, default = false)
raise "OMG `#{path}` does not exist!" unless File.exist?(path)
- args = "--no-document --ignore-dependencies --verbose --local"
- args += " --default --install-dir #{system_gem_path}" if default
+ args = "--no-document --ignore-dependencies --verbose --local --install-dir #{install_dir}"
+ args += " --default" if default
gem_command "install #{args} '#{path}'"
end
- def with_built_bundler(version = nil)
- version ||= Bundler::VERSION
- full_name = "bundler-#{version}"
- build_path = tmp + full_name
- bundler_path = build_path + "#{full_name}.gem"
-
- Dir.mkdir build_path
-
- begin
- shipped_files.each do |shipped_file|
- target_shipped_file = build_path + shipped_file
- target_shipped_dir = File.dirname(target_shipped_file)
- FileUtils.mkdir_p target_shipped_dir unless File.directory?(target_shipped_dir)
- FileUtils.cp shipped_file, target_shipped_file, :preserve => true
- end
-
- replace_version_file(version, dir: build_path) # rubocop:disable Style/HashSyntax
-
- Spec::BuildMetadata.write_build_metadata(dir: build_path) # rubocop:disable Style/HashSyntax
-
- gem_command "build #{relative_gemspec}", :dir => build_path
-
- yield(bundler_path)
- ensure
- build_path.rmtree
- end
+ def with_built_bundler(version = nil, &block)
+ Builders::BundlerBuilder.new(self, "bundler", version)._build(&block)
end
def with_gem_path_as(path)
@@ -452,32 +446,12 @@ module Spec
old = ENV["BUNDLER_SPEC_WINDOWS"]
ENV["BUNDLER_SPEC_WINDOWS"] = "true"
simulate_platform platform do
- simulate_bundler_version_when_missing_prerelease_default_gem_activation do
- yield
- end
+ yield
end
ensure
ENV["BUNDLER_SPEC_WINDOWS"] = old
end
- def simulate_bundler_version_when_missing_prerelease_default_gem_activation
- return yield unless rubygems_version_failing_to_activate_bundler_prereleases
-
- old = ENV["BUNDLER_VERSION"]
- ENV["BUNDLER_VERSION"] = Bundler::VERSION
- yield
- ensure
- ENV["BUNDLER_VERSION"] = old
- end
-
- def env_for_missing_prerelease_default_gem_activation
- if rubygems_version_failing_to_activate_bundler_prereleases
- { "BUNDLER_VERSION" => Bundler::VERSION }
- else
- {}
- end
- end
-
def current_ruby_minor
Gem.ruby_version.segments.tap {|s| s.delete_at(2) }.join(".")
end
@@ -496,14 +470,8 @@ module Spec
Gem.ruby_version.segments[0..1]
end
- # versions not including
- # https://github.com/rubygems/rubygems/commit/929e92d752baad3a08f3ac92eaec162cb96aedd1
- def rubygems_version_failing_to_activate_bundler_prereleases
- Gem.rubygems_version < Gem::Version.new("3.1.0.pre.1")
- end
-
def revision_for(path)
- sys_exec("git rev-parse HEAD", :dir => path).strip
+ sys_exec("git rev-parse HEAD", dir: path).strip
end
def with_read_only(pattern)
diff --git a/spec/bundler/support/indexes.rb b/spec/bundler/support/indexes.rb
index 78372302f1..086a311551 100644
--- a/spec/bundler/support/indexes.rb
+++ b/spec/bundler/support/indexes.rb
@@ -14,10 +14,10 @@ module Spec
alias_method :platforms, :platform
- def resolve(args = [])
+ def resolve(args = [], dependency_api_available: true)
@platforms ||= ["ruby"]
- default_source = instance_double("Bundler::Source::Rubygems", :specs => @index, :to_s => "locally install gems")
- source_requirements = { :default => default_source }
+ default_source = instance_double("Bundler::Source::Rubygems", specs: @index, to_s: "locally install gems", dependency_api_available?: dependency_api_available)
+ source_requirements = { default: default_source }
base = args[0] || Bundler::SpecSet.new([])
base.each {|ls| ls.source = default_source }
gem_version_promoter = args[1] || Bundler::GemVersionPromoter.new
@@ -27,7 +27,7 @@ module Spec
name = d.name
source_requirements[name] = d.source = default_source
end
- packages = Bundler::Resolver::Base.new(source_requirements, @deps, base, @platforms, :locked_specs => originally_locked, :unlock => unlock)
+ packages = Bundler::Resolver::Base.new(source_requirements, @deps, base, @platforms, locked_specs: originally_locked, unlock: unlock)
Bundler::Resolver.new(packages, gem_version_promoter).start
end
@@ -41,6 +41,12 @@ module Spec
expect(got).to eq(specs.sort)
end
+ def should_resolve_without_dependency_api(specs)
+ got = resolve(dependency_api_available: false)
+ got = got.map(&:full_name).sort
+ expect(got).to eq(specs.sort)
+ end
+
def should_resolve_and_include(specs, args = [])
got = resolve(args)
got = got.map(&:full_name).sort
@@ -298,7 +304,7 @@ module Spec
end
end
- def a_unresovable_child_index
+ def a_unresolvable_child_index
build_index do
gem "json", %w[1.8.0]
diff --git a/spec/bundler/support/matchers.rb b/spec/bundler/support/matchers.rb
index ea7c784683..0f027dcf04 100644
--- a/spec/bundler/support/matchers.rb
+++ b/spec/bundler/support/matchers.rb
@@ -97,18 +97,6 @@ module Spec
end
end
- RSpec::Matchers.define :take_less_than do |seconds|
- match do |actual|
- start_time = Time.now
-
- actual.call
-
- (Time.now - start_time).to_f < seconds
- end
-
- supports_block_expectations
- end
-
define_compound_matcher :read_as, [exist] do |file_contents|
diffable
diff --git a/spec/bundler/support/path.rb b/spec/bundler/support/path.rb
index 8d1807b56c..7352d5a353 100644
--- a/spec/bundler/support/path.rb
+++ b/spec/bundler/support/path.rb
@@ -42,8 +42,7 @@ module Spec
end
def dev_gemfile
- name = RUBY_VERSION.start_with?("2.6") ? "dev26_gems.rb" : "dev_gems.rb"
- @dev_gemfile ||= tool_dir.join(name)
+ @dev_gemfile ||= tool_dir.join("dev_gems.rb")
end
def bindir
@@ -81,7 +80,13 @@ module Spec
end
def shipped_files
- @shipped_files ||= loaded_gemspec.files
+ @shipped_files ||= if ruby_core_tarball?
+ loaded_gemspec.files.map {|f| f.gsub(%r{^exe/}, "libexec/") }
+ elsif ruby_core?
+ tracked_files
+ else
+ loaded_gemspec.files
+ end
end
def lib_tracked_files
@@ -226,13 +231,6 @@ module Spec
root.join("lib")
end
- # Sometimes rubygems version under test does not include
- # https://github.com/rubygems/rubygems/pull/2728 and will not always end up
- # activating the current bundler. In that case, require bundler absolutely.
- def entrypoint
- Gem.rubygems_version < Gem::Version.new("3.1.a") ? "#{lib_dir}/bundler" : "bundler"
- end
-
def global_plugin_gem(*args)
home ".bundle", "plugin", "gems", *args
end
@@ -252,6 +250,13 @@ module Spec
File.open(version_file, "w") {|f| f << contents }
end
+ def replace_required_ruby_version(version, dir:)
+ gemspec_file = File.expand_path("bundler.gemspec", dir)
+ contents = File.read(gemspec_file)
+ contents.sub!(/(^\s+s\.required_ruby_version\s*=\s*)"[^"]+"/, %(\\1"#{version}"))
+ File.open(gemspec_file, "w") {|f| f << contents }
+ end
+
def ruby_core?
# avoid to warnings
@ruby_core ||= nil
@@ -272,11 +277,11 @@ module Spec
def git_ls_files(glob)
skip "Not running on a git context, since running tests from a tarball" if ruby_core_tarball?
- sys_exec("git ls-files -z -- #{glob}", :dir => source_root).split("\x0")
+ sys_exec("git ls-files -z -- #{glob}", dir: source_root).split("\x0")
end
def tracked_files_glob
- ruby_core? ? "lib/bundler lib/bundler.rb spec/bundler man/bundle*" : ""
+ ruby_core? ? "libexec/bundle* lib/bundler lib/bundler.rb spec/bundler man/bundle*" : "lib exe spec CHANGELOG.md LICENSE.md README.md bundler.gemspec"
end
def lib_tracked_files_glob
diff --git a/spec/bundler/support/platforms.rb b/spec/bundler/support/platforms.rb
index eca1b2e60d..526e1c09a9 100644
--- a/spec/bundler/support/platforms.rb
+++ b/spec/bundler/support/platforms.rb
@@ -95,12 +95,17 @@ module Spec
9999
end
- def lockfile_platforms(*extra)
- formatted_lockfile_platforms(local_platform, *extra)
+ def default_platform_list(*extra, defaults: default_locked_platforms)
+ defaults.concat(extra).uniq
end
- def formatted_lockfile_platforms(*platforms)
+ def lockfile_platforms(*extra, defaults: default_locked_platforms)
+ platforms = default_platform_list(*extra, defaults: defaults)
platforms.map(&:to_s).sort.join("\n ")
end
+
+ def default_locked_platforms
+ [local_platform, generic_local_platform]
+ end
end
end
diff --git a/spec/bundler/support/rubygems_ext.rb b/spec/bundler/support/rubygems_ext.rb
index 4553c0606e..889ebc90c3 100644
--- a/spec/bundler/support/rubygems_ext.rb
+++ b/spec/bundler/support/rubygems_ext.rb
@@ -24,9 +24,9 @@ module Spec
gem_load_activate_and_possibly_install(gem_name, bin_container)
end
- def gem_require(gem_name)
+ def gem_require(gem_name, entrypoint)
gem_activate(gem_name)
- require gem_name
+ require entrypoint
end
def test_setup
@@ -38,6 +38,10 @@ module Spec
FileUtils.mkdir_p(Path.tmpdir)
ENV["HOME"] = Path.home.to_s
+ # Remove "RUBY_CODESIGN", which is used by mkmf-generated Makefile to
+ # sign extension bundles on macOS, to avoid trying to find the specified key
+ # from the fake $HOME/Library/Keychains directory.
+ ENV.delete "RUBY_CODESIGN"
ENV["TMPDIR"] = Path.tmpdir.to_s
require "rubygems/user_interaction"
@@ -86,7 +90,7 @@ module Spec
puts success_message
puts
else
- system("git status --porcelain")
+ system("git diff")
puts
puts error_message
@@ -118,6 +122,7 @@ module Spec
end
def gem_activate(gem_name)
+ require_relative "activate"
require "bundler"
gem_requirement = Bundler::LockfileParser.new(File.read(dev_lockfile)).specs.find {|spec| spec.name == gem_name }.version
gem gem_name, gem_requirement
diff --git a/spec/bundler/support/rubygems_version_manager.rb b/spec/bundler/support/rubygems_version_manager.rb
index 5653601ae8..88da14b67e 100644
--- a/spec/bundler/support/rubygems_version_manager.rb
+++ b/spec/bundler/support/rubygems_version_manager.rb
@@ -30,11 +30,10 @@ class RubygemsVersionManager
rubygems_default_path = rubygems_path + "/defaults"
bundler_path = rubylibdir + "/bundler"
- bundler_exemptions = Gem.rubygems_version < Gem::Version.new("3.2.0") ? [bundler_path + "/errors.rb"] : []
bad_loaded_features = $LOADED_FEATURES.select do |loaded_feature|
(loaded_feature.start_with?(rubygems_path) && !loaded_feature.start_with?(rubygems_default_path)) ||
- (loaded_feature.start_with?(bundler_path) && !bundler_exemptions.any? {|bundler_exemption| loaded_feature.start_with?(bundler_exemption) })
+ loaded_feature.start_with?(bundler_path)
end
errors = if bad_loaded_features.any?
@@ -66,7 +65,7 @@ class RubygemsVersionManager
def switch_local_copy_if_needed
return unless local_copy_switch_needed?
- sys_exec("git checkout #{target_tag}", :dir => local_copy_path)
+ sys_exec("git checkout #{target_tag}", dir: local_copy_path)
ENV["RGV"] = local_copy_path.to_s
end
@@ -85,7 +84,7 @@ class RubygemsVersionManager
end
def local_copy_tag
- sys_exec("git rev-parse --abbrev-ref HEAD", :dir => local_copy_path)
+ sys_exec("git rev-parse --abbrev-ref HEAD", dir: local_copy_path)
end
def local_copy_path
@@ -98,7 +97,7 @@ class RubygemsVersionManager
rubygems_path = source_root.join("tmp/rubygems")
unless rubygems_path.directory?
- sys_exec("git clone .. #{rubygems_path}", :dir => source_root)
+ sys_exec("git clone .. #{rubygems_path}", dir: source_root)
end
rubygems_path
diff --git a/spec/bundler/update/gemfile_spec.rb b/spec/bundler/update/gemfile_spec.rb
index 1c5294101e..d32a7945b0 100644
--- a/spec/bundler/update/gemfile_spec.rb
+++ b/spec/bundler/update/gemfile_spec.rb
@@ -8,8 +8,8 @@ RSpec.describe "bundle update" do
gem 'rack'
G
- bundle :install, :gemfile => bundled_app("NotGemfile")
- bundle :update, :gemfile => bundled_app("NotGemfile"), :all => true
+ bundle :install, gemfile: bundled_app("NotGemfile")
+ bundle :update, gemfile: bundled_app("NotGemfile"), all: true
# Specify BUNDLE_GEMFILE for `the_bundle`
# to retrieve the proper Gemfile
@@ -30,7 +30,7 @@ RSpec.describe "bundle update" do
end
it "uses the gemfile to update" do
- bundle "update", :all => true
+ bundle "update", all: true
bundle "list"
expect(out).to include("rack (1.0.0)")
@@ -38,8 +38,8 @@ RSpec.describe "bundle update" do
it "uses the gemfile while in a subdirectory" do
bundled_app("subdir").mkpath
- bundle "update", :all => true, :dir => bundled_app("subdir")
- bundle "list", :dir => bundled_app("subdir")
+ bundle "update", all: true, dir: bundled_app("subdir")
+ bundle "list", dir: bundled_app("subdir")
expect(out).to include("rack (1.0.0)")
end
diff --git a/spec/bundler/update/gems/fund_spec.rb b/spec/bundler/update/gems/fund_spec.rb
index d80f4018f3..4a87c16bf7 100644
--- a/spec/bundler/update/gems/fund_spec.rb
+++ b/spec/bundler/update/gems/fund_spec.rb
@@ -40,7 +40,7 @@ RSpec.describe "bundle update" do
gem 'has_funding'
G
- bundle :update, :all => true
+ bundle :update, all: true
end
it "displays fund message" do
diff --git a/spec/bundler/update/gems/post_install_spec.rb b/spec/bundler/update/gems/post_install_spec.rb
index 3aaa659d57..e3593387d4 100644
--- a/spec/bundler/update/gems/post_install_spec.rb
+++ b/spec/bundler/update/gems/post_install_spec.rb
@@ -52,7 +52,7 @@ RSpec.describe "bundle update" do
gem 'thin'
G
- bundle :update, :all => true
+ bundle :update, all: true
end
it_behaves_like "a post-install message outputter"
@@ -67,7 +67,7 @@ RSpec.describe "bundle update" do
gem 'thin'
G
- bundle :update, :all => true
+ bundle :update, all: true
end
it_behaves_like "a post-install message outputter"
diff --git a/spec/bundler/update/git_spec.rb b/spec/bundler/update/git_spec.rb
index 59e3d2f5fb..3b7bbfd979 100644
--- a/spec/bundler/update/git_spec.rb
+++ b/spec/bundler/update/git_spec.rb
@@ -4,7 +4,7 @@ RSpec.describe "bundle update" do
describe "git sources" do
it "floats on a branch when :branch is used" do
build_git "foo", "1.0"
- update_git "foo", :branch => "omg"
+ update_git "foo", branch: "omg"
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -17,14 +17,14 @@ RSpec.describe "bundle update" do
s.write "lib/foo.rb", "FOO = '1.1'"
end
- bundle "update", :all => true
+ bundle "update", all: true
expect(the_bundle).to include_gems "foo 1.1"
end
it "updates correctly when you have like craziness" do
- build_lib "activesupport", "3.0", :path => lib_path("rails/activesupport")
- build_git "rails", "3.0", :path => lib_path("rails") do |s|
+ build_lib "activesupport", "3.0", path: lib_path("rails/activesupport")
+ build_git "rails", "3.0", path: lib_path("rails") do |s|
s.add_dependency "activesupport", "= 3.0"
end
@@ -38,8 +38,8 @@ RSpec.describe "bundle update" do
end
it "floats on a branch when :branch is used and the source is specified in the update" do
- build_git "foo", "1.0", :path => lib_path("foo")
- update_git "foo", :branch => "omg", :path => lib_path("foo")
+ build_git "foo", "1.0", path: lib_path("foo")
+ update_git "foo", branch: "omg", path: lib_path("foo")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -48,7 +48,7 @@ RSpec.describe "bundle update" do
end
G
- update_git "foo", :path => lib_path("foo") do |s|
+ update_git "foo", path: lib_path("foo") do |s|
s.write "lib/foo.rb", "FOO = '1.1'"
end
@@ -58,8 +58,8 @@ RSpec.describe "bundle update" do
end
it "floats on main when updating all gems that are pinned to the source even if you have child dependencies" do
- build_git "foo", :path => lib_path("foo")
- build_gem "bar", :to_bundle => true do |s|
+ build_git "foo", path: lib_path("foo")
+ build_gem "bar", to_bundle: true do |s|
s.add_dependency "foo"
end
@@ -69,7 +69,7 @@ RSpec.describe "bundle update" do
gem "bar"
G
- update_git "foo", :path => lib_path("foo") do |s|
+ update_git "foo", path: lib_path("foo") do |s|
s.write "lib/foo.rb", "FOO = '1.1'"
end
@@ -79,8 +79,8 @@ RSpec.describe "bundle update" do
end
it "notices when you change the repo url in the Gemfile" do
- build_git "foo", :path => lib_path("foo_one")
- build_git "foo", :path => lib_path("foo_two")
+ build_git "foo", path: lib_path("foo_one")
+ build_git "foo", path: lib_path("foo_two")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -101,9 +101,9 @@ RSpec.describe "bundle update" do
it "fetches tags from the remote" do
build_git "foo"
- @remote = build_git("bar", :bare => true)
- update_git "foo", :remote => file_uri_for(@remote.path)
- update_git "foo", :push => "main"
+ @remote = build_git("bar", bare: true)
+ update_git "foo", remote: file_uri_for(@remote.path)
+ update_git "foo", push: "main"
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -111,15 +111,15 @@ RSpec.describe "bundle update" do
G
# Create a new tag on the remote that needs fetching
- update_git "foo", :tag => "fubar"
- update_git "foo", :push => "fubar"
+ update_git "foo", tag: "fubar"
+ update_git "foo", push: "fubar"
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem 'foo', :git => "#{@remote.path}", :tag => "fubar"
G
- bundle "update", :all => true
+ bundle "update", all: true
expect(err).to be_empty
end
@@ -142,8 +142,8 @@ RSpec.describe "bundle update" do
s.add_dependency "submodule"
end
- sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", :dir => lib_path("has_submodule-1.0")
- sys_exec "git commit -m \"submodulator\"", :dir => lib_path("has_submodule-1.0")
+ sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", dir: lib_path("has_submodule-1.0")
+ sys_exec "git commit -m \"submodulator\"", dir: lib_path("has_submodule-1.0")
end
it "it unlocks the source when submodules are added to a git source" do
@@ -168,7 +168,7 @@ RSpec.describe "bundle update" do
expect(out).to eq("GIT")
end
- it "unlocks the source when submodules are removed from git source", :git => ">= 2.9.0" do
+ it "unlocks the source when submodules are removed from git source", git: ">= 2.9.0" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo4)}"
git "#{lib_path("has_submodule-1.0")}", :submodules => true do
@@ -201,7 +201,7 @@ RSpec.describe "bundle update" do
lib_path("foo-1.0").join(".git").rmtree
- bundle :update, :all => true, :raise_on_error => false
+ bundle :update, all: true, raise_on_error: false
expect(err).to include(lib_path("foo-1.0").to_s).
and match(/Git error: command `git fetch.+has failed/)
end
@@ -209,7 +209,7 @@ RSpec.describe "bundle update" do
it "should not explode on invalid revision on update of gem by name" do
build_git "rack", "0.8"
- build_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ build_git "rack", "0.8", path: lib_path("local-rack") do |s|
s.write "lib/rack.rb", "puts :LOCAL"
end
@@ -224,16 +224,16 @@ RSpec.describe "bundle update" do
end
it "shows the previous version of the gem" do
- build_git "rails", "2.3.2", :path => lib_path("rails")
+ build_git "rails", "2.3.2", path: lib_path("rails")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rails", :git => "#{file_uri_for(lib_path("rails"))}"
G
- update_git "rails", "3.0", :path => lib_path("rails"), :gemspec => true
+ update_git "rails", "3.0", path: lib_path("rails"), gemspec: true
- bundle "update", :all => true
+ bundle "update", all: true
expect(out).to include("Using rails 3.0 (was 2.3.2) from #{file_uri_for(lib_path("rails"))} (at main@#{revision_for(lib_path("rails"))[0..6]})")
end
end
@@ -241,7 +241,7 @@ RSpec.describe "bundle update" do
describe "with --source flag" do
before :each do
build_repo2
- @git = build_git "foo", :path => lib_path("foo") do |s|
+ @git = build_git "foo", path: lib_path("foo") do |s|
s.executables = "foobar"
end
@@ -255,7 +255,7 @@ RSpec.describe "bundle update" do
end
it "updates the source" do
- update_git "foo", :path => @git.path
+ update_git "foo", path: @git.path
bundle "update --source foo"
@@ -268,7 +268,7 @@ RSpec.describe "bundle update" do
end
it "unlocks gems that were originally pulled in by the source" do
- update_git "foo", "2.0", :path => @git.path
+ update_git "foo", "2.0", path: @git.path
bundle "update --source foo"
expect(the_bundle).to include_gems "foo 2.0"
@@ -276,7 +276,7 @@ RSpec.describe "bundle update" do
it "leaves all other gems frozen" do
update_repo2
- update_git "foo", :path => @git.path
+ update_git "foo", path: @git.path
bundle "update --source foo"
expect(the_bundle).to include_gems "rack 1.0"
@@ -286,7 +286,7 @@ RSpec.describe "bundle update" do
context "when the gem and the repository have different names" do
before :each do
build_repo2
- @git = build_git "foo", :path => lib_path("bar")
+ @git = build_git "foo", path: lib_path("bar")
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
@@ -301,7 +301,7 @@ RSpec.describe "bundle update" do
spec_lines = lib_path("bar/foo.gemspec").read.split("\n")
spec_lines[5] = "s.version = '2.0'"
- update_git "foo", "2.0", :path => @git.path do |s|
+ update_git "foo", "2.0", path: @git.path do |s|
s.write "foo.gemspec", spec_lines.join("\n")
end
@@ -309,6 +309,11 @@ RSpec.describe "bundle update" do
bundle "update --source bar"
+ checksums = checksums_section_when_existing do |c|
+ c.no_checksum "foo", "2.0"
+ c.checksum gem_repo2, "rack", "1.0.0"
+ end
+
expect(lockfile).to eq <<~G
GIT
remote: #{@git.path}
@@ -327,7 +332,7 @@ RSpec.describe "bundle update" do
DEPENDENCIES
foo!
rack
-
+ #{checksums}
BUNDLED WITH
#{Bundler::VERSION}
G
diff --git a/spec/bundler/update/path_spec.rb b/spec/bundler/update/path_spec.rb
index 756770313b..1f8992b33f 100644
--- a/spec/bundler/update/path_spec.rb
+++ b/spec/bundler/update/path_spec.rb
@@ -3,14 +3,14 @@
RSpec.describe "path sources" do
describe "bundle update --source" do
it "shows the previous version of the gem when updated from path source" do
- build_lib "activesupport", "2.3.5", :path => lib_path("rails/activesupport")
+ build_lib "activesupport", "2.3.5", path: lib_path("rails/activesupport")
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "activesupport", :path => "#{lib_path("rails/activesupport")}"
G
- build_lib "activesupport", "3.0", :path => lib_path("rails/activesupport")
+ build_lib "activesupport", "3.0", path: lib_path("rails/activesupport")
bundle "update --source activesupport"
expect(out).to include("Using activesupport 3.0 (was 2.3.5) from source at `#{lib_path("rails/activesupport")}`")
diff --git a/spec/bundler/update/redownload_spec.rb b/spec/bundler/update/redownload_spec.rb
index 147be823f5..4a8c711bfa 100644
--- a/spec/bundler/update/redownload_spec.rb
+++ b/spec/bundler/update/redownload_spec.rb
@@ -9,12 +9,12 @@ RSpec.describe "bundle update" do
end
describe "with --force" do
- it "shows a deprecation when single flag passed", :bundler => 2 do
+ it "shows a deprecation when single flag passed", bundler: 2 do
bundle "update rack --force"
expect(err).to include "[DEPRECATED] The `--force` option has been renamed to `--redownload`"
end
- it "shows a deprecation when multiple flags passed", :bundler => 2 do
+ it "shows a deprecation when multiple flags passed", bundler: 2 do
bundle "update rack --no-color --force"
expect(err).to include "[DEPRECATED] The `--force` option has been renamed to `--redownload`"
end
@@ -30,5 +30,15 @@ RSpec.describe "bundle update" do
bundle "update rack --no-color --redownload"
expect(err).not_to include "[DEPRECATED] The `--force` option has been renamed to `--redownload`"
end
+
+ it "re-installs installed gems" do
+ rack_lib = default_bundle_path("gems/rack-1.0.0/lib/rack.rb")
+ rack_lib.open("w") {|f| f.write("blah blah blah") }
+ bundle :update, redownload: true
+
+ expect(out).to include "Installing rack 1.0.0"
+ expect(rack_lib.open(&:read)).to eq("RACK = '1.0.0'\n")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
end
end
diff --git a/spec/default.mspec b/spec/default.mspec
index b928a2c9aa..cae5fa374f 100644
--- a/spec/default.mspec
+++ b/spec/default.mspec
@@ -3,10 +3,14 @@ $VERBOSE = false
if (opt = ENV["RUBYOPT"]) and (opt = opt.dup).sub!(/(?:\A|\s)-w(?=\z|\s)/, '')
ENV["RUBYOPT"] = opt
end
+
+# Enable constant leak checks by ruby/mspec
+ENV["CHECK_CONSTANT_LEAKS"] ||= "true"
+
require "./rbconfig" unless defined?(RbConfig)
require_relative "../tool/test-coverage" if ENV.key?("COVERAGE")
load File.dirname(__FILE__) + '/ruby/default.mspec'
-OBJDIR = File.expand_path("spec/ruby/optional/capi/ext")
+OBJDIR = File.expand_path("spec/ruby/optional/capi/ext") unless defined?(OBJDIR)
class MSpecScript
@testing_ruby = true
@@ -27,6 +31,16 @@ class MSpecScript
]
end
+ # Disable to run for bundled gems in test-spec
+ set :bundled_gems, (File.readlines("#{srcdir}/gems/bundled_gems").map do |line|
+ next if /^\s*(?:#|$)/ =~ line
+ "#{srcdir}/spec/ruby/library/" + line.split.first
+ end.compact)
+ set :stdlibs, Dir.glob("#{srcdir}/spec/ruby/library/*")
+ set :library, get(:stdlibs).to_a - get(:bundled_gems).to_a
+
+ set :files, get(:command_line) + get(:language) + get(:core) + get(:library) + get(:security) + get(:optional)
+
if ENV.key?("COVERAGE")
set :excludes, ["Coverage"]
end
@@ -70,3 +84,72 @@ end
class MSpecScript
prepend JobServer
end
+
+require 'mspec/runner/formatters/dotted'
+
+class DottedFormatter
+ prepend Module.new {
+ BASE = __dir__ + "/ruby/" unless defined?(BASE)
+
+ def initialize(out = nil)
+ super
+ if out
+ @columns = nil
+ else
+ columns = ENV["COLUMNS"]&.to_i
+ @columns = columns&.nonzero? || 80
+ end
+ @dotted = 0
+ @loaded = false
+ @count = 0
+ end
+
+ def register
+ super
+ MSpec.register :load, self
+ MSpec.register :unload, self
+ end
+
+ def after(*)
+ if @columns
+ if @dotted == 0
+ s = sprintf("%6d ", @count)
+ print(s)
+ @dotted += s.size
+ end
+ @count +=1
+ end
+ super
+ if @columns and (@dotted += 1) >= @columns
+ print "\n"
+ @dotted = 0
+ end
+ end
+
+ def load(*)
+ file = MSpec.file || MSpec.files_array.first
+ @loaded = true
+ s = "#{file.delete_prefix(BASE)}:"
+ print s
+ if @columns
+ if (@dotted += s.size) >= @columns
+ print "\n"
+ @dotted = 0
+ else
+ print " "
+ @dotted += 1
+ end
+ end
+ @count = 0
+ end
+
+ def unload
+ super
+ if @loaded
+ print "\n" if @dotted > 0
+ @dotted = 0
+ @loaded = nil
+ end
+ end
+ }
+end
diff --git a/spec/mspec/lib/mspec/helpers/tmp.rb b/spec/mspec/lib/mspec/helpers/tmp.rb
index b2a38ee983..4c0eddab75 100644
--- a/spec/mspec/lib/mspec/helpers/tmp.rb
+++ b/spec/mspec/lib/mspec/helpers/tmp.rb
@@ -12,7 +12,7 @@ else
end
SPEC_TEMP_DIR = spec_temp_dir
-SPEC_TEMP_UNIQUIFIER = "0"
+SPEC_TEMP_UNIQUIFIER = +"0"
at_exit do
begin
@@ -41,6 +41,7 @@ def tmp(name, uniquify = true)
if uniquify and !name.empty?
slash = name.rindex "/"
index = slash ? slash + 1 : 0
+ name = +name
name.insert index, "#{SPEC_TEMP_UNIQUIFIER.succ!}-"
end
diff --git a/spec/mspec/lib/mspec/mocks/mock.rb b/spec/mspec/lib/mspec/mocks/mock.rb
index 28a083cc15..c61ba35ea7 100644
--- a/spec/mspec/lib/mspec/mocks/mock.rb
+++ b/spec/mspec/lib/mspec/mocks/mock.rb
@@ -18,20 +18,16 @@ module Mock
@stubs ||= Hash.new { |h,k| h[k] = [] }
end
- def self.replaced_name(obj, sym)
- :"__mspec_#{obj.__id__}_#{sym}__"
+ def self.replaced_name(key)
+ :"__mspec_#{key.last}__"
end
def self.replaced_key(obj, sym)
- [replaced_name(obj, sym), sym]
+ [obj.__id__, sym]
end
- def self.has_key?(keys, sym)
- !!keys.find { |k| k.first == sym }
- end
-
- def self.replaced?(sym)
- has_key?(mocks.keys, sym) or has_key?(stubs.keys, sym)
+ def self.replaced?(key)
+ mocks.include?(key) or stubs.include?(key)
end
def self.clear_replaced(key)
@@ -40,8 +36,9 @@ module Mock
end
def self.mock_respond_to?(obj, sym, include_private = false)
- name = replaced_name(obj, :respond_to?)
- if replaced? name
+ key = replaced_key(obj, :respond_to?)
+ if replaced? key
+ name = replaced_name(key)
obj.__send__ name, sym, include_private
else
obj.respond_to? sym, include_private
@@ -59,8 +56,8 @@ module Mock
return
end
- if (sym == :respond_to? or mock_respond_to?(obj, sym, true)) and !replaced?(key.first)
- meta.__send__ :alias_method, key.first, sym
+ if (sym == :respond_to? or mock_respond_to?(obj, sym, true)) and !replaced?(key)
+ meta.__send__ :alias_method, replaced_name(key), sym
end
suppress_warning {
@@ -191,7 +188,7 @@ module Mock
next
end
- replaced = key.first
+ replaced = replaced_name(key)
sym = key.last
meta = obj.singleton_class
diff --git a/spec/mspec/lib/mspec/runner/actions/leakchecker.rb b/spec/mspec/lib/mspec/runner/actions/leakchecker.rb
index 69181b71d3..71797b9815 100644
--- a/spec/mspec/lib/mspec/runner/actions/leakchecker.rb
+++ b/spec/mspec/lib/mspec/runner/actions/leakchecker.rb
@@ -301,6 +301,7 @@ class LeakCheckerAction
end
def start
+ disable_nss_modules
@checker = LeakChecker.new
end
@@ -316,4 +317,61 @@ class LeakCheckerAction
end
end
end
+
+ private
+
+ # This function is intended to disable all NSS modules when ruby is compiled
+ # against glibc. NSS modules allow the system administrator to load custom
+ # shared objects into all processes using glibc, and use them to customise
+ # the behaviour of username, groupname, hostname, etc lookups. This is
+ # normally configured in the file /etc/nsswitch.conf.
+ # These modules often do things like open cache files or connect to system
+ # daemons like sssd or dbus, which of course means they have open file
+ # descriptors of their own. This can cause the leak-checking functionality
+ # in this file to report that such descriptors have been leaked, and fail
+ # the test suite.
+ # This function uses glibc's __nss_configure_lookup function to override any
+ # configuration in /etc/nsswitch.conf, and just use the built in files/dns
+ # name lookup functionality (which is of course perfectly sufficient for
+ # running ruby/spec).
+ def disable_nss_modules
+ begin
+ require 'fiddle'
+ rescue LoadError
+ # Make sure it's possible to run the test suite on a ruby implementation
+ # which does not (yet?) have Fiddle.
+ return
+ end
+
+ begin
+ libc = Fiddle.dlopen(nil)
+ # Older versions of fiddle don't have Fiddle::Type (and instead rely on Fiddle::TYPE_)
+ # Even older versions of fiddle don't have CONST_STRING,
+ string_type = defined?(Fiddle::TYPE_CONST_STRING) ? Fiddle::TYPE_CONST_STRING : Fiddle::TYPE_VOIDP
+ nss_configure_lookup = Fiddle::Function.new(
+ libc['__nss_configure_lookup'],
+ [string_type, string_type],
+ Fiddle::TYPE_INT
+ )
+ rescue Fiddle::DLError
+ # We're not running with glibc - no need to do this.
+ return
+ end
+
+ nss_configure_lookup.call 'passwd', 'files'
+ nss_configure_lookup.call 'shadow', 'files'
+ nss_configure_lookup.call 'group', 'files'
+ nss_configure_lookup.call 'hosts', 'files dns'
+ nss_configure_lookup.call 'services', 'files'
+ nss_configure_lookup.call 'netgroup', 'files'
+ nss_configure_lookup.call 'automount', 'files'
+ nss_configure_lookup.call 'aliases', 'files'
+ nss_configure_lookup.call 'ethers', 'files'
+ nss_configure_lookup.call 'gshadow', 'files'
+ nss_configure_lookup.call 'initgroups', 'files'
+ nss_configure_lookup.call 'networks', 'files dns'
+ nss_configure_lookup.call 'protocols', 'files'
+ nss_configure_lookup.call 'publickey', 'files'
+ nss_configure_lookup.call 'rpc', 'files'
+ end
end
diff --git a/spec/mspec/lib/mspec/runner/actions/timeout.rb b/spec/mspec/lib/mspec/runner/actions/timeout.rb
index 499001c952..1200926872 100644
--- a/spec/mspec/lib/mspec/runner/actions/timeout.rb
+++ b/spec/mspec/lib/mspec/runner/actions/timeout.rb
@@ -48,11 +48,12 @@ class TimeoutAction
show_backtraces
if MSpec.subprocesses.empty?
- exit 2
+ exit! 2
else
# Do not exit but signal the subprocess so we can get their output
MSpec.subprocesses.each do |pid|
- Process.kill :SIGTERM, pid
+ kill_wait_one_second :SIGTERM, pid
+ hard_kill :SIGKILL, pid
end
@fail = true
@current_state = nil
@@ -80,7 +81,7 @@ class TimeoutAction
if @fail
STDERR.puts "\n\nThe last example #{@error_message}. See above for the subprocess stacktrace."
- exit 2
+ exit! 2
end
end
@@ -89,12 +90,28 @@ class TimeoutAction
@thread.join
end
+ private def hard_kill(signal, pid)
+ begin
+ Process.kill signal, pid
+ rescue Errno::ESRCH
+ # Process already terminated
+ end
+ end
+
+ private def kill_wait_one_second(signal, pid)
+ begin
+ Process.kill signal, pid
+ sleep 1
+ rescue Errno::ESRCH
+ # Process already terminated
+ end
+ end
+
private def show_backtraces
java_stacktraces = -> pid {
if RUBY_ENGINE == 'truffleruby' || RUBY_ENGINE == 'jruby'
STDERR.puts 'Java stacktraces:'
- Process.kill :SIGQUIT, pid
- sleep 1
+ kill_wait_one_second :SIGQUIT, pid
end
}
@@ -118,8 +135,7 @@ class TimeoutAction
if RUBY_ENGINE == 'truffleruby'
STDERR.puts "\nRuby backtraces:"
- Process.kill :SIGALRM, pid
- sleep 1
+ kill_wait_one_second :SIGALRM, pid
else
STDERR.puts "Don't know how to print backtraces of a subprocess on #{RUBY_ENGINE}"
end
diff --git a/spec/mspec/lib/mspec/runner/formatters/base.rb b/spec/mspec/lib/mspec/runner/formatters/base.rb
index 54a83c9c32..e3b5bb23e0 100644
--- a/spec/mspec/lib/mspec/runner/formatters/base.rb
+++ b/spec/mspec/lib/mspec/runner/formatters/base.rb
@@ -5,6 +5,9 @@ require 'mspec/utils/options'
if ENV['CHECK_LEAKS']
require 'mspec/runner/actions/leakchecker'
+end
+
+if ENV['CHECK_LEAKS'] || ENV['CHECK_CONSTANT_LEAKS']
require 'mspec/runner/actions/constants_leak_checker'
end
@@ -40,8 +43,11 @@ class BaseFormatter
@counter = @tally.counter
if ENV['CHECK_LEAKS']
- save = ENV['CHECK_LEAKS'] == 'save'
LeakCheckerAction.new.register
+ end
+
+ if ENV['CHECK_LEAKS'] || ENV['CHECK_CONSTANT_LEAKS']
+ save = ENV['CHECK_LEAKS'] == 'save' || ENV['CHECK_CONSTANT_LEAKS'] == 'save'
ConstantsLeakCheckerAction.new(save).register
end
diff --git a/spec/mspec/lib/mspec/utils/options.rb b/spec/mspec/lib/mspec/utils/options.rb
index 612caf6771..3b5962dbe6 100644
--- a/spec/mspec/lib/mspec/utils/options.rb
+++ b/spec/mspec/lib/mspec/utils/options.rb
@@ -477,7 +477,7 @@ class MSpecOptions
def debug
on("-d", "--debug",
- "Set MSpec debugging flag for more verbose output") do
+ "Disable MSpec backtrace filtering") do
$MSPEC_DEBUG = true
end
end
diff --git a/spec/mspec/spec/integration/run_spec.rb b/spec/mspec/spec/integration/run_spec.rb
index 90dc051543..ea0735e9b2 100644
--- a/spec/mspec/spec/integration/run_spec.rb
+++ b/spec/mspec/spec/integration/run_spec.rb
@@ -1,20 +1,21 @@
require 'spec_helper'
RSpec.describe "Running mspec" do
+ q = BACKTRACE_QUOTE
a_spec_output = <<EOS
1)
Foo#bar errors FAILED
Expected 1 == 2
to be truthy but was false
-CWD/spec/fixtures/a_spec.rb:8:in `block (2 levels) in <top (required)>'
-CWD/spec/fixtures/a_spec.rb:2:in `<top (required)>'
+CWD/spec/fixtures/a_spec.rb:8:in #{q}block (2 levels) in <top (required)>'
+CWD/spec/fixtures/a_spec.rb:2:in #{q}<top (required)>'
2)
Foo#bar fails ERROR
RuntimeError: failure
-CWD/spec/fixtures/a_spec.rb:12:in `block (2 levels) in <top (required)>'
-CWD/spec/fixtures/a_spec.rb:2:in `<top (required)>'
+CWD/spec/fixtures/a_spec.rb:12:in #{q}block (2 levels) in <top (required)>'
+CWD/spec/fixtures/a_spec.rb:2:in #{q}<top (required)>'
Finished in D.DDDDDD seconds
EOS
diff --git a/spec/mspec/spec/integration/tag_spec.rb b/spec/mspec/spec/integration/tag_spec.rb
index 33df1cfd40..ae08e9d45f 100644
--- a/spec/mspec/spec/integration/tag_spec.rb
+++ b/spec/mspec/spec/integration/tag_spec.rb
@@ -13,6 +13,7 @@ RSpec.describe "Running mspec tag" do
it "tags the failing specs" do
fixtures = "spec/fixtures"
out, ret = run_mspec("tag", "--add fails --fail #{fixtures}/tagging_spec.rb")
+ q = BACKTRACE_QUOTE
expect(out).to eq <<EOS
RUBY_DESCRIPTION
.FF
@@ -26,15 +27,15 @@ Tag#me érròrs in unicode
Tag#me errors FAILED
Expected 1 == 2
to be truthy but was false
-CWD/spec/fixtures/tagging_spec.rb:9:in `block (2 levels) in <top (required)>'
-CWD/spec/fixtures/tagging_spec.rb:3:in `<top (required)>'
+CWD/spec/fixtures/tagging_spec.rb:9:in #{q}block (2 levels) in <top (required)>'
+CWD/spec/fixtures/tagging_spec.rb:3:in #{q}<top (required)>'
2)
Tag#me érròrs in unicode FAILED
Expected 1 == 2
to be truthy but was false
-CWD/spec/fixtures/tagging_spec.rb:13:in `block (2 levels) in <top (required)>'
-CWD/spec/fixtures/tagging_spec.rb:3:in `<top (required)>'
+CWD/spec/fixtures/tagging_spec.rb:13:in #{q}block (2 levels) in <top (required)>'
+CWD/spec/fixtures/tagging_spec.rb:3:in #{q}<top (required)>'
Finished in D.DDDDDD seconds
diff --git a/spec/mspec/spec/mocks/mock_spec.rb b/spec/mspec/spec/mocks/mock_spec.rb
index 73f9bdfa14..7426e0ff88 100644
--- a/spec/mspec/spec/mocks/mock_spec.rb
+++ b/spec/mspec/spec/mocks/mock_spec.rb
@@ -22,14 +22,14 @@ end
RSpec.describe Mock, ".replaced_name" do
it "returns the name for a method that is being replaced by a mock method" do
m = double('a fake id')
- expect(Mock.replaced_name(m, :method_call)).to eq(:"__mspec_#{m.object_id}_method_call__")
+ expect(Mock.replaced_name(Mock.replaced_key(m, :method_call))).to eq(:"__mspec_method_call__")
end
end
RSpec.describe Mock, ".replaced_key" do
it "returns a key used internally by Mock" do
m = double('a fake id')
- expect(Mock.replaced_key(m, :method_call)).to eq([:"__mspec_#{m.object_id}_method_call__", :method_call])
+ expect(Mock.replaced_key(m, :method_call)).to eq([m.object_id, :method_call])
end
end
@@ -42,16 +42,16 @@ RSpec.describe Mock, ".replaced?" do
it "returns true if a method has been stubbed on an object" do
Mock.install_method @mock, :method_call
- expect(Mock.replaced?(Mock.replaced_name(@mock, :method_call))).to be_truthy
+ expect(Mock.replaced?(Mock.replaced_key(@mock, :method_call))).to be_truthy
end
it "returns true if a method has been mocked on an object" do
Mock.install_method @mock, :method_call, :stub
- expect(Mock.replaced?(Mock.replaced_name(@mock, :method_call))).to be_truthy
+ expect(Mock.replaced?(Mock.replaced_key(@mock, :method_call))).to be_truthy
end
it "returns false if a method has not been stubbed or mocked" do
- expect(Mock.replaced?(Mock.replaced_name(@mock, :method_call))).to be_falsey
+ expect(Mock.replaced?(Mock.replaced_key(@mock, :method_call))).to be_falsey
end
end
@@ -197,11 +197,11 @@ RSpec.describe Mock, ".install_method" do
Mock.install_method @mock, :method_call
expect(@mock).to respond_to(:method_call)
- expect(@mock).not_to respond_to(Mock.replaced_name(@mock, :method_call))
+ expect(@mock).not_to respond_to(Mock.replaced_name(Mock.replaced_key(@mock, :method_call)))
Mock.install_method @mock, :method_call, :stub
expect(@mock).to respond_to(:method_call)
- expect(@mock).not_to respond_to(Mock.replaced_name(@mock, :method_call))
+ expect(@mock).not_to respond_to(Mock.replaced_name(Mock.replaced_key(@mock, :method_call)))
end
end
@@ -493,7 +493,7 @@ RSpec.describe Mock, ".cleanup" do
it "removes the replaced method if the mock method overrides an existing method" do
def @mock.already_here() :hey end
expect(@mock).to respond_to(:already_here)
- replaced_name = Mock.replaced_name(@mock, :already_here)
+ replaced_name = Mock.replaced_name(Mock.replaced_key(@mock, :already_here))
Mock.install_method @mock, :already_here
expect(@mock).to respond_to(replaced_name)
@@ -521,10 +521,9 @@ RSpec.describe Mock, ".cleanup" do
replaced_key = Mock.replaced_key(@mock, :method_call)
expect(Mock).to receive(:clear_replaced).with(replaced_key)
- replaced_name = Mock.replaced_name(@mock, :method_call)
- expect(Mock.replaced?(replaced_name)).to be_truthy
+ expect(Mock.replaced?(replaced_key)).to be_truthy
Mock.cleanup
- expect(Mock.replaced?(replaced_name)).to be_falsey
+ expect(Mock.replaced?(replaced_key)).to be_falsey
end
end
diff --git a/spec/mspec/spec/runner/context_spec.rb b/spec/mspec/spec/runner/context_spec.rb
index a864428aec..9ebc708c0c 100644
--- a/spec/mspec/spec/runner/context_spec.rb
+++ b/spec/mspec/spec/runner/context_spec.rb
@@ -914,7 +914,7 @@ RSpec.describe ContextState, "#it_should_behave_like" do
it "raises an Exception if unable to find the shared ContextState" do
expect(MSpec).to receive(:retrieve_shared).and_return(nil)
- expect { @state.it_should_behave_like "this" }.to raise_error(Exception)
+ expect { @state.it_should_behave_like :this }.to raise_error(Exception)
end
describe "for nested ContextState instances" do
diff --git a/spec/mspec/spec/spec_helper.rb b/spec/mspec/spec/spec_helper.rb
index 3a749581ee..5cabfe5626 100644
--- a/spec/mspec/spec/spec_helper.rb
+++ b/spec/mspec/spec/spec_helper.rb
@@ -66,3 +66,5 @@ PublicMSpecMatchers = Class.new {
include MSpecMatchers
public :raise_error
}.new
+
+BACKTRACE_QUOTE = RUBY_VERSION >= "3.4" ? "'" : "`"
diff --git a/spec/mspec/tool/remove_old_guards.rb b/spec/mspec/tool/remove_old_guards.rb
index 67485446bb..3fd95e6b31 100644
--- a/spec/mspec/tool/remove_old_guards.rb
+++ b/spec/mspec/tool/remove_old_guards.rb
@@ -46,6 +46,51 @@ def remove_guards(guard, keep)
end
end
+def remove_empty_files
+ each_spec_file do |file|
+ unless file.include?("fixtures/")
+ lines = File.readlines(file)
+ if lines.all? { |line| line.chomp.empty? or line.start_with?('require', '#') }
+ puts "Removing empty file #{file}"
+ File.delete(file)
+ end
+ end
+ end
+end
+
+def remove_unused_shared_specs
+ shared_groups = {}
+ # Dir["**/shared/**/*.rb"].each do |shared|
+ each_spec_file do |shared|
+ next if File.basename(shared) == 'constants.rb'
+ contents = File.binread(shared)
+ found = false
+ contents.scan(/^\s*describe (:[\w_?]+), shared: true do$/) {
+ shared_groups[$1] = 0
+ found = true
+ }
+ if !found and shared.include?('shared/') and !shared.include?('fixtures/') and !shared.end_with?('/constants.rb')
+ puts "no shared describe in #{shared} ?"
+ end
+ end
+
+ each_spec_file do |file|
+ contents = File.binread(file)
+ contents.scan(/(?:it_behaves_like|it_should_behave_like) (:[\w_?]+)[,\s]/) do
+ puts $1 unless shared_groups.key?($1)
+ shared_groups[$1] += 1
+ end
+ end
+
+ shared_groups.each_pair do |group, value|
+ if value == 0
+ puts "Shared describe #{group} seems unused"
+ elsif value == 1
+ puts "Shared describe #{group} seems used only once" if $VERBOSE
+ end
+ end
+end
+
def search(regexp)
each_spec_file do |file|
contents = File.binread(file)
@@ -64,7 +109,11 @@ version = Regexp.escape(ARGV.fetch(0))
version += "(?:\\.0)?" if version.count(".") < 2
remove_guards(/ruby_version_is (["'])#{version}\1 do/, true)
remove_guards(/ruby_version_is (["'])[0-9.]*\1 *... *(["'])#{version}\2 do/, false)
-remove_guards(/ruby_bug "#\d+", (["'])[0-9.]*\1 *... *(["'])#{version}\2 do/, true)
+remove_guards(/ruby_bug ["']#\d+["'], (["'])[0-9.]*\1 *... *(["'])#{version}\2 do/, true)
+
+remove_empty_files
+remove_unused_shared_specs
+puts "Search:"
search(/(["'])#{version}\1/)
search(/^\s*#.+#{version}/)
diff --git a/spec/mspec/tool/tag_from_output.rb b/spec/mspec/tool/tag_from_output.rb
index a6e60945cd..b6b4603855 100755
--- a/spec/mspec/tool/tag_from_output.rb
+++ b/spec/mspec/tool/tag_from_output.rb
@@ -3,6 +3,8 @@
# Adds tags based on error and failures output (e.g., from a CI log),
# without running any spec code.
+tag = ENV["TAG"] || "fails"
+
tags_dir = %w[
spec/tags
spec/tags/ruby
@@ -30,9 +32,9 @@ output.slice_before(NUMBER).select { |number, *rest|
if spec_file
spec_file = spec_file[SPEC_FILE, 1] or raise
else
- if error_line =~ /^(\w+)[#\.](\w+) /
- module_method = error_line.split(' ', 2).first
- file = "#{$1.downcase}/#{$2}_spec.rb"
+ if error_line =~ /^([\w:]+)[#\.](\w+) /
+ mod, method = $1, $2
+ file = "#{mod.downcase.gsub('::', '/')}/#{method}_spec.rb"
spec_file = ['spec/ruby/core', 'spec/ruby/library', *Dir.glob('spec/ruby/library/*')].find { |dir|
path = "#{dir}/#{file}"
break path if File.exist?(path)
@@ -54,7 +56,7 @@ output.slice_before(NUMBER).select { |number, *rest|
dir = File.dirname(tags_file)
Dir.mkdir(dir) unless Dir.exist?(dir)
- tag_line = "fails:#{description}"
+ tag_line = "#{tag}:#{description}"
lines = File.exist?(tags_file) ? File.readlines(tags_file, chomp: true) : []
unless lines.include?(tag_line)
puts tags_file
diff --git a/spec/prism.mspec b/spec/prism.mspec
new file mode 100644
index 0000000000..c13b58b1cd
--- /dev/null
+++ b/spec/prism.mspec
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+# This is turned off because when we run with --parser=prism we explicitly turn
+# off experimental warnings to make sure the output is consistent.
+MSpec.register(:exclude, "Warning.[] returns default values for categories :deprecated and :experimental")
+
+## Language
+MSpec.register(:exclude, "Hash literal raises a SyntaxError at parse time when Symbol key with invalid bytes")
+MSpec.register(:exclude, "Hash literal raises a SyntaxError at parse time when Symbol key with invalid bytes and 'key: value' syntax used")
+MSpec.register(:exclude, "Regexps with encoding modifiers supports /e (EUC encoding) with interpolation")
+MSpec.register(:exclude, "Regexps with encoding modifiers supports /e (EUC encoding) with interpolation /o")
+MSpec.register(:exclude, "Regexps with encoding modifiers preserves EUC-JP as /e encoding through interpolation")
+MSpec.register(:exclude, "Regexps with encoding modifiers supports /s (Windows_31J encoding) with interpolation")
+MSpec.register(:exclude, "Regexps with encoding modifiers supports /s (Windows_31J encoding) with interpolation and /o")
+MSpec.register(:exclude, "Regexps with encoding modifiers preserves Windows-31J as /s encoding through interpolation")
+MSpec.register(:exclude, "Regexps with encoding modifiers supports /u (UTF8 encoding) with interpolation")
+MSpec.register(:exclude, "Regexps with encoding modifiers supports /u (UTF8 encoding) with interpolation and /o")
+MSpec.register(:exclude, "Regexps with encoding modifiers preserves UTF-8 as /u encoding through interpolation")
+MSpec.register(:exclude, "A Symbol literal raises an SyntaxError at parse time when Symbol with invalid bytes")
+
+## Core
+MSpec.register(:exclude, "TracePoint#path equals \"(eval at __FILE__:__LINE__)\" inside an eval for :end event")
+
+## Library
+MSpec.register(:exclude, "Coverage.result returns the correct results when eval coverage is disabled")
+MSpec.register(:exclude, "Socket.gethostbyaddr using an IPv6 address with an explicit address family raises SocketError when the address is not supported by the family")
diff --git a/spec/ruby/.rubocop.yml b/spec/ruby/.rubocop.yml
index 82733c4b4d..be32ce8900 100644
--- a/spec/ruby/.rubocop.yml
+++ b/spec/ruby/.rubocop.yml
@@ -1,7 +1,7 @@
inherit_from: .rubocop_todo.yml
AllCops:
- TargetRubyVersion: 2.7
+ TargetRubyVersion: 3.0
DisplayCopNames: true
Exclude:
- command_line/fixtures/bad_syntax.rb
@@ -33,15 +33,16 @@ Lint/AssignmentInCondition:
Lint/BooleanSymbol:
Enabled: false
+Lint/DeprecatedOpenSSLConstant:
+ Exclude:
+ - library/openssl/digest/**/*.rb
+
Lint/InterpolationCheck:
Enabled: false
Lint/LiteralAsCondition:
Enabled: false
-Lint/RedundantRequireStatement:
- Enabled: false
-
Lint/RedundantSplatExpansion:
Enabled: false
@@ -102,6 +103,9 @@ Lint/OutOfRangeRegexpRef:
Lint/InheritException:
Enabled: false
+Lint/SafeNavigationChain:
+ Enabled: false
+
Lint/ElseLayout:
Exclude:
- 'language/if_spec.rb'
diff --git a/spec/ruby/.rubocop_todo.yml b/spec/ruby/.rubocop_todo.yml
index a59e64bd58..3ebb23a8bb 100644
--- a/spec/ruby/.rubocop_todo.yml
+++ b/spec/ruby/.rubocop_todo.yml
@@ -63,7 +63,6 @@ Lint/LiteralInInterpolation:
- 'language/string_spec.rb'
- 'language/symbol_spec.rb'
- 'language/undef_spec.rb'
- - 'library/net/ftp/connect_spec.rb'
# Offense count: 8
# Cop supports --auto-correct.
diff --git a/spec/ruby/CONTRIBUTING.md b/spec/ruby/CONTRIBUTING.md
index adfc2fb0ca..c82eb5ea4f 100644
--- a/spec/ruby/CONTRIBUTING.md
+++ b/spec/ruby/CONTRIBUTING.md
@@ -138,12 +138,12 @@ Here is a list of the most commonly-used guards:
#### Version guards
```ruby
-ruby_version_is ""..."2.6" do
- # Specs for RUBY_VERSION < 2.6
+ruby_version_is ""..."3.2" do
+ # Specs for RUBY_VERSION < 3.2
end
-ruby_version_is "2.6" do
- # Specs for RUBY_VERSION >= 2.6
+ruby_version_is "3.2" do
+ # Specs for RUBY_VERSION >= 3.2
end
```
@@ -179,6 +179,7 @@ In case there is a bug in MRI and the fix will be backported to previous version
If it is not backported or not likely, use `ruby_version_is` instead.
First, file a bug at https://bugs.ruby-lang.org/.
The problem is `ruby_bug` would make non-MRI implementations fail this spec while MRI itself does not pass it, so it should only be used if the bug is/will be fixed and backported.
+Otherwise, non-MRI implementations would have to choose between being incompatible with the latest release of MRI to pass the spec or fail the spec, both which make no sense.
```ruby
ruby_bug '#13669', ''...'3.2' do
@@ -191,11 +192,11 @@ end
#### Combining guards
```ruby
-guard -> { platform_is :windows and ruby_version_is ""..."2.6" } do
- # Windows and RUBY_VERSION < 2.6
+guard -> { platform_is :windows and ruby_version_is ""..."3.2" } do
+ # Windows and RUBY_VERSION < 3.2
end
-guard_not -> { platform_is :windows and ruby_version_is ""..."2.6" } do
+guard_not -> { platform_is :windows and ruby_version_is ""..."3.2" } do
# The opposite
end
```
diff --git a/spec/ruby/README.md b/spec/ruby/README.md
index 018bf0ca3e..115392835f 100644
--- a/spec/ruby/README.md
+++ b/spec/ruby/README.md
@@ -30,8 +30,8 @@ ruby/spec is known to be tested in these implementations for every commit:
* [Opal](https://github.com/opal/opal/tree/master/spec)
* [Artichoke](https://github.com/artichoke/spec/tree/artichoke-vendor)
-ruby/spec describes the behavior of Ruby 2.7 and more recent Ruby versions.
-More precisely, every latest stable MRI release should [pass](https://github.com/ruby/spec/actions/workflows/ci.yml) all specs of ruby/spec (2.7.x, 3.0.x, 3.1.x, etc), and those are tested in CI.
+ruby/spec describes the behavior of Ruby 3.0 and more recent Ruby versions.
+More precisely, every latest stable MRI release should [pass](https://github.com/ruby/spec/actions/workflows/ci.yml) all specs of ruby/spec (3.0.x, 3.1.x, 3.2.x, etc), and those are tested in CI.
### Synchronization with Ruby Implementations
@@ -61,6 +61,7 @@ For older specs try these commits:
* Ruby 2.4.10 - [Suite](https://github.com/ruby/spec/commit/bce4f2b81d6c31db67cf4d023a0625ceadde59bd) using [MSpec](https://github.com/ruby/mspec/commit/e7eb8aa4c26495b7b461e687d950b96eb08b3ff2)
* Ruby 2.5.9 - [Suite](https://github.com/ruby/spec/commit/c503335d3d9f6ec6ef24de60a0716c34af69b64f) using [MSpec](https://github.com/ruby/mspec/commit/0091e8a62e954717cd54641f935eaf1403692041)
* Ruby 2.6.10 - [Suite](https://github.com/ruby/spec/commit/aaf998fb8c92c4e63ad423a2e7ca6e6921818c6e) using [MSpec](https://github.com/ruby/mspec/commit/5e36c684e9e2b92b1187589bba1df22c640a8661)
+* Ruby 2.7.8 - [Suite](https://github.com/ruby/spec/commit/93787e6035c925b593a9c0c6fb0e7e07a6f1df1f) using [MSpec](https://github.com/ruby/mspec/commit/1d8cf64722d8a7529f7cd205be5f16a89b7a67fd)
### Running the specs
@@ -127,6 +128,12 @@ MSpec can automatically add new top-level constants in this file with:
$ CHECK_LEAKS=save mspec ../mspec/bin/mspec file
+### Running Specs on S390x CPU Architecture
+
+Run the specs with `DFLTCC=0` if you see failing specs related to the zlib library on s390x CPU architecture. The failures can happen with the zlib library applying the patch madler/zlib#410 to enable the deflate algorithm producing a different compressed byte stream.
+
+ $ DFLTCC=0 ../mspec/bin/mspec
+
### Contributing and Writing Specs
See [CONTRIBUTING.md](https://github.com/ruby/spec/blob/master/CONTRIBUTING.md) for documentation about contributing and writing specs (guards, matchers, etc).
diff --git a/spec/ruby/command_line/backtrace_limit_spec.rb b/spec/ruby/command_line/backtrace_limit_spec.rb
index 56afa8efef..4d57bc268b 100644
--- a/spec/ruby/command_line/backtrace_limit_spec.rb
+++ b/spec/ruby/command_line/backtrace_limit_spec.rb
@@ -1,7 +1,7 @@
require_relative '../spec_helper'
-ruby_version_is "3.0" do
- describe "The --backtrace-limit command line option" do
+describe "The --backtrace-limit command line option" do
+ ruby_version_is ""..."3.4" do
it "limits top-level backtraces to a given number of entries" do
file = fixture(__FILE__ , "backtrace.rb")
out = ruby_exe(file, options: "--backtrace-limit=2", args: "top 2>&1", exit_status: 1)
@@ -45,4 +45,49 @@ backtrace
MSG
end
end
+
+ ruby_version_is "3.4" do
+ it "limits top-level backtraces to a given number of entries" do
+ file = fixture(__FILE__ , "backtrace.rb")
+ out = ruby_exe(file, options: "--backtrace-limit=2", args: "top 2>&1", exit_status: 1)
+ out = out.gsub(__dir__, '')
+
+ out.should == <<-MSG
+top
+/fixtures/backtrace.rb:2:in 'Object#a': oops (RuntimeError)
+\tfrom /fixtures/backtrace.rb:6:in 'Object#b'
+\tfrom /fixtures/backtrace.rb:10:in 'Object#c'
+\t ... 2 levels...
+ MSG
+ end
+
+ it "affects Exception#full_message" do
+ file = fixture(__FILE__ , "backtrace.rb")
+ out = ruby_exe(file, options: "--backtrace-limit=2", args: "full_message 2>&1")
+ out = out.gsub(__dir__, '')
+
+ out.should == <<-MSG
+full_message
+/fixtures/backtrace.rb:2:in 'Object#a': oops (RuntimeError)
+\tfrom /fixtures/backtrace.rb:6:in 'Object#b'
+\tfrom /fixtures/backtrace.rb:10:in 'Object#c'
+\t ... 2 levels...
+ MSG
+ end
+
+ it "does not affect Exception#backtrace" do
+ file = fixture(__FILE__ , "backtrace.rb")
+ out = ruby_exe(file, options: "--backtrace-limit=2", args: "backtrace 2>&1")
+ out = out.gsub(__dir__, '')
+
+ out.should == <<-MSG
+backtrace
+/fixtures/backtrace.rb:2:in 'Object#a'
+/fixtures/backtrace.rb:6:in 'Object#b'
+/fixtures/backtrace.rb:10:in 'Object#c'
+/fixtures/backtrace.rb:14:in 'Object#d'
+/fixtures/backtrace.rb:29:in '<main>'
+ MSG
+ end
+ end
end
diff --git a/spec/ruby/command_line/dash_a_spec.rb b/spec/ruby/command_line/dash_a_spec.rb
index 9ea135dc76..43d923ce16 100644
--- a/spec/ruby/command_line/dash_a_spec.rb
+++ b/spec/ruby/command_line/dash_a_spec.rb
@@ -6,13 +6,13 @@ describe "The -a command line option" do
end
it "runs the code in loop conditional on Kernel.gets()" do
- ruby_exe("puts $F.last", options: "-n -a", escape: true,
+ ruby_exe("puts $F.last", options: "-n -a",
args: " < #{@names}").should ==
"jones\nfield\ngrey\n"
end
it "sets $-a" do
- ruby_exe("puts $-a", options: "-n -a", escape: true,
+ ruby_exe("puts $-a", options: "-n -a",
args: " < #{@names}").should ==
"true\ntrue\ntrue\n"
end
diff --git a/spec/ruby/command_line/dash_l_spec.rb b/spec/ruby/command_line/dash_l_spec.rb
index 5c1d3cf4cd..44a98445f3 100644
--- a/spec/ruby/command_line/dash_l_spec.rb
+++ b/spec/ruby/command_line/dash_l_spec.rb
@@ -6,25 +6,25 @@ describe "The -l command line option" do
end
it "chomps lines with default separator" do
- ruby_exe('puts $_.end_with?("\n")', options: "-n -l", escape: true,
+ ruby_exe('puts $_.end_with?("\n")', options: "-n -l",
args: " < #{@names}").should ==
"false\nfalse\nfalse\n"
end
it "chomps last line based on $/" do
- ruby_exe('BEGIN { $/ = "ones\n" }; puts $_', options: "-W0 -n -l", escape: true,
+ ruby_exe('BEGIN { $/ = "ones\n" }; puts $_', options: "-W0 -n -l",
args: " < #{@names}").should ==
"alice j\nbob field\njames grey\n"
end
it "sets $\\ to the value of $/" do
- ruby_exe("puts $\\ == $/", options: "-W0 -n -l", escape: true,
+ ruby_exe("puts $\\ == $/", options: "-W0 -n -l",
args: " < #{@names}").should ==
"true\ntrue\ntrue\n"
end
it "sets $-l" do
- ruby_exe("puts $-l", options: "-n -l", escape: true,
+ ruby_exe("puts $-l", options: "-n -l",
args: " < #{@names}").should ==
"true\ntrue\ntrue\n"
end
diff --git a/spec/ruby/command_line/dash_n_spec.rb b/spec/ruby/command_line/dash_n_spec.rb
index 9d331d6065..1dd9379259 100644
--- a/spec/ruby/command_line/dash_n_spec.rb
+++ b/spec/ruby/command_line/dash_n_spec.rb
@@ -6,19 +6,19 @@ describe "The -n command line option" do
end
it "runs the code in loop conditional on Kernel.gets()" do
- ruby_exe("puts $_", options: "-n", escape: true,
+ ruby_exe("puts $_", options: "-n",
args: " < #{@names}").should ==
"alice\nbob\njames\n"
end
it "only evaluates BEGIN blocks once" do
- ruby_exe("BEGIN { puts \"hi\" }; puts $_", options: "-n", escape: true,
+ ruby_exe("BEGIN { puts \"hi\" }; puts $_", options: "-n",
args: " < #{@names}").should ==
"hi\nalice\nbob\njames\n"
end
it "only evaluates END blocks once" do
- ruby_exe("puts $_; END {puts \"bye\"}", options: "-n", escape: true,
+ ruby_exe("puts $_; END {puts \"bye\"}", options: "-n",
args: " < #{@names}").should ==
"alice\nbob\njames\nbye\n"
end
@@ -29,7 +29,7 @@ describe "The -n command line option" do
$total += 1
END { puts $total }
script
- ruby_exe(script, options: "-n", escape: true,
+ ruby_exe(script, options: "-n",
args: " < #{@names}").should ==
"3\n"
end
diff --git a/spec/ruby/command_line/dash_p_spec.rb b/spec/ruby/command_line/dash_p_spec.rb
index 39827c3868..967e3796de 100644
--- a/spec/ruby/command_line/dash_p_spec.rb
+++ b/spec/ruby/command_line/dash_p_spec.rb
@@ -6,13 +6,13 @@ describe "The -p command line option" do
end
it "runs the code in loop conditional on Kernel.gets() and prints $_" do
- ruby_exe("$_ = $_.upcase", options: "-p", escape: true,
+ ruby_exe("$_ = $_.upcase", options: "-p",
args: " < #{@names}").should ==
"ALICE\nBOB\nJAMES\n"
end
it "sets $-p" do
- ruby_exe("$_ = $-p", options: "-p", escape: true,
+ ruby_exe("$_ = $-p", options: "-p",
args: " < #{@names}").should ==
"truetruetrue"
end
diff --git a/spec/ruby/command_line/dash_r_spec.rb b/spec/ruby/command_line/dash_r_spec.rb
index ea5bde5adf..9f673c53dc 100644
--- a/spec/ruby/command_line/dash_r_spec.rb
+++ b/spec/ruby/command_line/dash_r_spec.rb
@@ -16,7 +16,10 @@ describe "The -r command line option" do
out = ruby_exe(fixture(__FILE__, "bad_syntax.rb"), options: "-r #{@test_file}", args: "2>&1", exit_status: 1)
$?.should_not.success?
out.should include("REQUIRED")
- out.should include("syntax error")
+
+ # it's tempting not to rely on error message and rely only on exception class name,
+ # but CRuby before 3.2 doesn't print class name for syntax error
+ out.should include_any_of("syntax error", "SyntaxError")
end
it "does not require the file if the main script file does not exist" do
diff --git a/spec/ruby/command_line/dash_upper_f_spec.rb b/spec/ruby/command_line/dash_upper_f_spec.rb
index 967acc2ece..5c10a7140d 100644
--- a/spec/ruby/command_line/dash_upper_f_spec.rb
+++ b/spec/ruby/command_line/dash_upper_f_spec.rb
@@ -6,7 +6,7 @@ describe "the -F command line option" do
end
it "specifies the field separator pattern for -a" do
- ruby_exe("puts $F[0]", options: "-naF:", escape: true,
+ ruby_exe("puts $F[0]", options: "-naF:",
args: " < #{@passwd}").should ==
"nobody\nroot\ndaemon\n"
end
diff --git a/spec/ruby/command_line/dash_upper_u_spec.rb b/spec/ruby/command_line/dash_upper_u_spec.rb
index d62718b095..15854e7b73 100644
--- a/spec/ruby/command_line/dash_upper_u_spec.rb
+++ b/spec/ruby/command_line/dash_upper_u_spec.rb
@@ -6,6 +6,13 @@ describe "ruby -U" do
options: '-U').should == 'UTF-8'
end
+ it "sets Encoding.default_internal to UTF-8 when RUBYOPT is empty or only spaces" do
+ ruby_exe('p Encoding.default_internal',
+ options: '-U', env: { 'RUBYOPT' => '' }).should == "#<Encoding:UTF-8>\n"
+ ruby_exe('p Encoding.default_internal',
+ options: '-U', env: { 'RUBYOPT' => ' ' }).should == "#<Encoding:UTF-8>\n"
+ end
+
it "does nothing different if specified multiple times" do
ruby_exe('print Encoding.default_internal.name',
options: '-U -U').should == 'UTF-8'
diff --git a/spec/ruby/command_line/dash_v_spec.rb b/spec/ruby/command_line/dash_v_spec.rb
index 9b570fa1eb..747db7f755 100644
--- a/spec/ruby/command_line/dash_v_spec.rb
+++ b/spec/ruby/command_line/dash_v_spec.rb
@@ -6,8 +6,9 @@ describe "The -v command line option" do
describe "when used alone" do
it "prints version and ends" do
- ruby_exe(nil, args: '-v').should include(RUBY_DESCRIPTION)
+ ruby_exe(nil, args: '-v').sub("+PRISM ", "").should include(RUBY_DESCRIPTION.sub("+PRISM ", ""))
end unless (defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled?) ||
- (defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?)
+ (defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?) ||
+ (ENV['RUBY_MN_THREADS'] == '1')
end
end
diff --git a/spec/ruby/command_line/fixtures/string_literal_frozen_comment.rb b/spec/ruby/command_line/fixtures/string_literal_frozen_comment.rb
new file mode 100644
index 0000000000..fb84b546c0
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/string_literal_frozen_comment.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+frozen = "test".frozen?
+interned = "test".equal?("test")
+puts "frozen:#{frozen} interned:#{interned}"
diff --git a/spec/ruby/command_line/fixtures/string_literal_mutable_comment.rb b/spec/ruby/command_line/fixtures/string_literal_mutable_comment.rb
new file mode 100644
index 0000000000..381a742001
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/string_literal_mutable_comment.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: false
+frozen = "test".frozen?
+interned = "test".equal?("test")
+puts "frozen:#{frozen} interned:#{interned}"
diff --git a/spec/ruby/command_line/fixtures/string_literal_raw.rb b/spec/ruby/command_line/fixtures/string_literal_raw.rb
new file mode 100644
index 0000000000..56b1841296
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/string_literal_raw.rb
@@ -0,0 +1,3 @@
+frozen = "test".frozen?
+interned = "test".equal?("test")
+puts "frozen:#{frozen} interned:#{interned}"
diff --git a/spec/ruby/command_line/frozen_strings_spec.rb b/spec/ruby/command_line/frozen_strings_spec.rb
index 647b69daed..334b98273b 100644
--- a/spec/ruby/command_line/frozen_strings_spec.rb
+++ b/spec/ruby/command_line/frozen_strings_spec.rb
@@ -19,6 +19,50 @@ describe "The --enable-frozen-string-literal flag causes string literals to" do
end
end
+describe "The --disable-frozen-string-literal flag causes string literals to" do
+
+ it "produce a different object each time" do
+ ruby_exe(fixture(__FILE__, "freeze_flag_one_literal.rb"), options: "--disable-frozen-string-literal").chomp.should == "false"
+ end
+
+end
+
+describe "With neither --enable-frozen-string-literal nor --disable-frozen-string-literal flag set" do
+ before do
+ # disable --enable-frozen-string-literal and --disable-frozen-string-literal passed in $RUBYOPT
+ @rubyopt = ENV["RUBYOPT"]
+ ENV["RUBYOPT"] = ""
+ end
+
+ after do
+ ENV["RUBYOPT"] = @rubyopt
+ end
+
+ it "produce a different object each time" do
+ ruby_exe(fixture(__FILE__, "freeze_flag_one_literal.rb")).chomp.should == "false"
+ end
+
+ ruby_version_is "3.4" do
+ it "if file has no frozen_string_literal comment produce different frozen strings each time" do
+ ruby_exe(fixture(__FILE__, "string_literal_raw.rb")).chomp.should == "frozen:true interned:false"
+ end
+ end
+
+ ruby_version_is ""..."3.4" do
+ it "if file has no frozen_string_literal comment produce different mutable strings each time" do
+ ruby_exe(fixture(__FILE__, "string_literal_raw.rb")).chomp.should == "frozen:false interned:false"
+ end
+ end
+
+ it "if file has frozen_string_literal:true comment produce same frozen strings each time" do
+ ruby_exe(fixture(__FILE__, "string_literal_frozen_comment.rb")).chomp.should == "frozen:true interned:true"
+ end
+
+ it "if file has frozen_string_literal:false comment produce different mutable strings each time" do
+ ruby_exe(fixture(__FILE__, "string_literal_mutable_comment.rb")).chomp.should == "frozen:false interned:false"
+ end
+end
+
describe "The --debug flag produces" do
it "debugging info on attempted frozen string modification" do
error_str = ruby_exe(fixture(__FILE__, 'debug_info.rb'), options: '--debug', args: "2>&1")
diff --git a/spec/ruby/command_line/rubyopt_spec.rb b/spec/ruby/command_line/rubyopt_spec.rb
index bbea4d557d..18a5959b18 100644
--- a/spec/ruby/command_line/rubyopt_spec.rb
+++ b/spec/ruby/command_line/rubyopt_spec.rb
@@ -11,52 +11,52 @@ describe "Processing RUBYOPT" do
it "adds the -I path to $LOAD_PATH" do
ENV["RUBYOPT"] = "-Ioptrubyspecincl"
- result = ruby_exe("puts $LOAD_PATH.grep(/byspecin/)", escape: true)
+ result = ruby_exe("puts $LOAD_PATH.grep(/byspecin/)")
result.chomp[-15..-1].should == "optrubyspecincl"
end
it "sets $DEBUG to true for '-d'" do
ENV["RUBYOPT"] = '-d'
command = %[puts "value of $DEBUG is \#{$DEBUG}"]
- result = ruby_exe(command, escape: true, args: "2>&1")
+ result = ruby_exe(command, args: "2>&1")
result.should =~ /value of \$DEBUG is true/
end
guard -> { not CROSS_COMPILING } do
it "prints the version number for '-v'" do
ENV["RUBYOPT"] = '-v'
- ruby_exe("")[/\A.*/].should == RUBY_DESCRIPTION
+ ruby_exe("")[/\A.*/].should == RUBY_DESCRIPTION.sub("+PRISM ", "")
end
it "ignores whitespace around the option" do
ENV["RUBYOPT"] = ' -v '
- ruby_exe("")[/\A.*/].should == RUBY_DESCRIPTION
+ ruby_exe("")[/\A.*/].should == RUBY_DESCRIPTION.sub("+PRISM ", "")
end
end
it "sets $VERBOSE to true for '-w'" do
ENV["RUBYOPT"] = '-w'
- ruby_exe("p $VERBOSE", escape: true).chomp.should == "true"
+ ruby_exe("p $VERBOSE").chomp.should == "true"
end
it "sets $VERBOSE to true for '-W'" do
ENV["RUBYOPT"] = '-W'
- ruby_exe("p $VERBOSE", escape: true).chomp.should == "true"
+ ruby_exe("p $VERBOSE").chomp.should == "true"
end
it "sets $VERBOSE to nil for '-W0'" do
ENV["RUBYOPT"] = '-W0'
- ruby_exe("p $VERBOSE", escape: true).chomp.should == "nil"
+ ruby_exe("p $VERBOSE").chomp.should == "nil"
end
it "sets $VERBOSE to false for '-W1'" do
ENV["RUBYOPT"] = '-W1'
- ruby_exe("p $VERBOSE", escape: true).chomp.should == "false"
+ ruby_exe("p $VERBOSE").chomp.should == "false"
end
it "sets $VERBOSE to true for '-W2'" do
ENV["RUBYOPT"] = '-W2'
- ruby_exe("p $VERBOSE", escape: true).chomp.should == "true"
+ ruby_exe("p $VERBOSE").chomp.should == "true"
end
it "suppresses deprecation warnings for '-W:no-deprecated'" do
diff --git a/spec/ruby/command_line/syntax_error_spec.rb b/spec/ruby/command_line/syntax_error_spec.rb
index 444ea9635c..9ba87b9e22 100644
--- a/spec/ruby/command_line/syntax_error_spec.rb
+++ b/spec/ruby/command_line/syntax_error_spec.rb
@@ -3,11 +3,17 @@ require_relative '../spec_helper'
describe "The interpreter" do
it "prints an error when given a file with invalid syntax" do
out = ruby_exe(fixture(__FILE__, "bad_syntax.rb"), args: "2>&1", exit_status: 1)
- out.should include "syntax error"
+
+ # it's tempting not to rely on error message and rely only on exception class name,
+ # but CRuby before 3.2 doesn't print class name for syntax error
+ out.should include_any_of("syntax error", "SyntaxError")
end
it "prints an error when given code via -e with invalid syntax" do
out = ruby_exe(nil, args: "-e 'a{' 2>&1", exit_status: 1)
- out.should include "syntax error"
+
+ # it's tempting not to rely on error message and rely only on exception class name,
+ # but CRuby before 3.2 doesn't print class name for syntax error
+ out.should include_any_of("syntax error", "SyntaxError")
end
end
diff --git a/spec/ruby/core/argf/bytes_spec.rb b/spec/ruby/core/argf/bytes_spec.rb
deleted file mode 100644
index bf35ded1db..0000000000
--- a/spec/ruby/core/argf/bytes_spec.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-require_relative '../../spec_helper'
-require_relative 'shared/each_byte'
-
-ruby_version_is ''...'3.0' do
- describe "ARGF.bytes" do
- before :each do
- @verbose, $VERBOSE = $VERBOSE, nil
- end
-
- after :each do
- $VERBOSE = @verbose
- end
-
- it_behaves_like :argf_each_byte, :bytes
- end
-end
diff --git a/spec/ruby/core/argf/chars_spec.rb b/spec/ruby/core/argf/chars_spec.rb
deleted file mode 100644
index 6af73cdabb..0000000000
--- a/spec/ruby/core/argf/chars_spec.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-require_relative '../../spec_helper'
-require_relative 'shared/each_char'
-
-ruby_version_is ''...'3.0' do
- describe "ARGF.chars" do
- before :each do
- @verbose, $VERBOSE = $VERBOSE, nil
- end
-
- after :each do
- $VERBOSE = @verbose
- end
-
- it_behaves_like :argf_each_char, :chars
- end
-end
diff --git a/spec/ruby/core/argf/codepoints_spec.rb b/spec/ruby/core/argf/codepoints_spec.rb
deleted file mode 100644
index bb28c17fbb..0000000000
--- a/spec/ruby/core/argf/codepoints_spec.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-require_relative '../../spec_helper'
-require_relative 'shared/each_codepoint'
-
-ruby_version_is ''...'3.0' do
- describe "ARGF.codepoints" do
- before :each do
- @verbose, $VERBOSE = $VERBOSE, nil
- end
-
- after :each do
- $VERBOSE = @verbose
- end
-
- it_behaves_like :argf_each_codepoint, :codepoints
- end
-end
diff --git a/spec/ruby/core/argf/lines_spec.rb b/spec/ruby/core/argf/lines_spec.rb
deleted file mode 100644
index e964dbd0d3..0000000000
--- a/spec/ruby/core/argf/lines_spec.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-require_relative '../../spec_helper'
-require_relative 'shared/each_line'
-
-ruby_version_is ''...'3.0' do
- describe "ARGF.lines" do
- before :each do
- @verbose, $VERBOSE = $VERBOSE, nil
- end
-
- after :each do
- $VERBOSE = @verbose
- end
-
- it_behaves_like :argf_each_line, :lines
- end
-end
diff --git a/spec/ruby/core/argf/readpartial_spec.rb b/spec/ruby/core/argf/readpartial_spec.rb
index 5e284b3423..ea4301f25c 100644
--- a/spec/ruby/core/argf/readpartial_spec.rb
+++ b/spec/ruby/core/argf/readpartial_spec.rb
@@ -29,7 +29,7 @@ describe "ARGF.readpartial" do
it "clears output buffer even if EOFError is raised because @argf is at end" do
begin
- output = "to be cleared"
+ output = +"to be cleared"
argf [@file1_name] do
@argf.read
@@ -69,7 +69,7 @@ describe "ARGF.readpartial" do
print ARGF.readpartial(#{@stdin.size})
ARGF.readpartial(1) rescue print $!.class
STR
- stdin = ruby_exe(ruby_str, args: "< #{@stdin_name}", escape: true)
+ stdin = ruby_exe(ruby_str, args: "< #{@stdin_name}")
stdin.should == @stdin + "EOFError"
end
end
diff --git a/spec/ruby/core/argf/shared/getc.rb b/spec/ruby/core/argf/shared/getc.rb
index 8be39c60b6..d63372d9d7 100644
--- a/spec/ruby/core/argf/shared/getc.rb
+++ b/spec/ruby/core/argf/shared/getc.rb
@@ -9,7 +9,7 @@ describe :argf_getc, shared: true do
it "reads each char of files" do
argf [@file1, @file2] do
- chars = ""
+ chars = +""
@chars.size.times { chars << @argf.send(@method) }
chars.should == @chars
end
diff --git a/spec/ruby/core/argf/shared/read.rb b/spec/ruby/core/argf/shared/read.rb
index fe903983c0..e76d022139 100644
--- a/spec/ruby/core/argf/shared/read.rb
+++ b/spec/ruby/core/argf/shared/read.rb
@@ -15,7 +15,7 @@ describe :argf_read, shared: true do
it "treats second argument as an output buffer" do
argf [@file1_name] do
- buffer = ""
+ buffer = +""
@argf.send(@method, @file1.size, buffer)
buffer.should == @file1
end
@@ -23,7 +23,7 @@ describe :argf_read, shared: true do
it "clears output buffer before appending to it" do
argf [@file1_name] do
- buffer = "to be cleared"
+ buffer = +"to be cleared"
@argf.send(@method, @file1.size, buffer)
buffer.should == @file1
end
diff --git a/spec/ruby/core/array/assoc_spec.rb b/spec/ruby/core/array/assoc_spec.rb
index af95528281..f0be3de795 100644
--- a/spec/ruby/core/array/assoc_spec.rb
+++ b/spec/ruby/core/array/assoc_spec.rb
@@ -37,4 +37,16 @@ describe "Array#assoc" do
a.assoc(s1.first).should equal(s1)
a.assoc(s2.first).should equal(s2)
end
+
+ it "calls to_ary on non-array elements" do
+ s1 = [1, 2]
+ s2 = ArraySpecs::ArrayConvertible.new(2, 3)
+ a = [s1, s2]
+
+ s1.should_not_receive(:to_ary)
+ a.assoc(s1.first).should equal(s1)
+
+ a.assoc(2).should == [2, 3]
+ s2.called.should equal(:to_ary)
+ end
end
diff --git a/spec/ruby/core/array/bsearch_index_spec.rb b/spec/ruby/core/array/bsearch_index_spec.rb
index df2c7c098e..94d85b37f3 100644
--- a/spec/ruby/core/array/bsearch_index_spec.rb
+++ b/spec/ruby/core/array/bsearch_index_spec.rb
@@ -63,10 +63,6 @@ describe "Array#bsearch_index" do
@array.bsearch_index { |x| -1 }.should be_nil
end
- it "returns the middle element when block always returns zero" do
- @array.bsearch_index { |x| 0 }.should == 2
- end
-
context "magnitude does not effect the result" do
it "returns the index of any matched elements where element is between 4n <= xn < 8n" do
[1, 2].should include(@array.bsearch_index { |x| (1 - x / 4) * (2**100) })
diff --git a/spec/ruby/core/array/drop_spec.rb b/spec/ruby/core/array/drop_spec.rb
index f911fd9018..0ea748e47d 100644
--- a/spec/ruby/core/array/drop_spec.rb
+++ b/spec/ruby/core/array/drop_spec.rb
@@ -50,15 +50,7 @@ describe "Array#drop" do
-> { [1, 2].drop(obj) }.should raise_error(TypeError)
end
- ruby_version_is ''...'3.0' do
- it 'returns a subclass instance for Array subclasses' do
- ArraySpecs::MyArray[1, 2, 3, 4, 5].drop(1).should be_an_instance_of(ArraySpecs::MyArray)
- end
- end
-
- ruby_version_is '3.0' do
- it 'returns a Array instance for Array subclasses' do
- ArraySpecs::MyArray[1, 2, 3, 4, 5].drop(1).should be_an_instance_of(Array)
- end
+ it 'returns a Array instance for Array subclasses' do
+ ArraySpecs::MyArray[1, 2, 3, 4, 5].drop(1).should be_an_instance_of(Array)
end
end
diff --git a/spec/ruby/core/array/drop_while_spec.rb b/spec/ruby/core/array/drop_while_spec.rb
index 94064528aa..bd46e8b882 100644
--- a/spec/ruby/core/array/drop_while_spec.rb
+++ b/spec/ruby/core/array/drop_while_spec.rb
@@ -18,15 +18,7 @@ describe "Array#drop_while" do
[1, 2, 3, false, 5].drop_while { |n| n }.should == [false, 5]
end
- ruby_version_is ''...'3.0' do
- it 'returns a subclass instance for Array subclasses' do
- ArraySpecs::MyArray[1, 2, 3, 4, 5].drop_while { |n| n < 4 }.should be_an_instance_of(ArraySpecs::MyArray)
- end
- end
-
- ruby_version_is '3.0' do
- it 'returns a Array instance for Array subclasses' do
- ArraySpecs::MyArray[1, 2, 3, 4, 5].drop_while { |n| n < 4 }.should be_an_instance_of(Array)
- end
+ it 'returns a Array instance for Array subclasses' do
+ ArraySpecs::MyArray[1, 2, 3, 4, 5].drop_while { |n| n < 4 }.should be_an_instance_of(Array)
end
end
diff --git a/spec/ruby/core/array/fill_spec.rb b/spec/ruby/core/array/fill_spec.rb
index 02360e550d..2c3b5d9e84 100644
--- a/spec/ruby/core/array/fill_spec.rb
+++ b/spec/ruby/core/array/fill_spec.rb
@@ -21,7 +21,7 @@ describe "Array#fill" do
it "does not replicate the filler" do
ary = [1, 2, 3, 4]
- str = "x"
+ str = +"x"
ary.fill(str).should == [str, str, str, str]
str << "y"
ary.should == [str, str, str, str]
diff --git a/spec/ruby/core/array/fixtures/encoded_strings.rb b/spec/ruby/core/array/fixtures/encoded_strings.rb
index 5b85bd0e06..b5888d86ae 100644
--- a/spec/ruby/core/array/fixtures/encoded_strings.rb
+++ b/spec/ruby/core/array/fixtures/encoded_strings.rb
@@ -2,14 +2,14 @@
module ArraySpecs
def self.array_with_usascii_and_7bit_utf8_strings
[
- 'foo'.force_encoding('US-ASCII'),
+ 'foo'.dup.force_encoding('US-ASCII'),
'bar'
]
end
def self.array_with_usascii_and_utf8_strings
[
- 'foo'.force_encoding('US-ASCII'),
+ 'foo'.dup.force_encoding('US-ASCII'),
'báz'
]
end
@@ -17,7 +17,7 @@ module ArraySpecs
def self.array_with_7bit_utf8_and_usascii_strings
[
'bar',
- 'foo'.force_encoding('US-ASCII')
+ 'foo'.dup.force_encoding('US-ASCII')
]
end
@@ -25,13 +25,13 @@ module ArraySpecs
[
'báz',
'bar',
- 'foo'.force_encoding('US-ASCII')
+ 'foo'.dup.force_encoding('US-ASCII')
]
end
def self.array_with_usascii_and_utf8_strings
[
- 'foo'.force_encoding('US-ASCII'),
+ 'foo'.dup.force_encoding('US-ASCII'),
'bar',
'báz'
]
@@ -41,7 +41,7 @@ module ArraySpecs
[
'bar',
'báz',
- 'foo'.force_encoding('BINARY')
+ 'foo'.dup.force_encoding('BINARY')
]
end
@@ -55,14 +55,14 @@ module ArraySpecs
def self.array_with_usascii_and_7bit_binary_strings
[
- 'bar'.force_encoding('US-ASCII'),
- 'foo'.force_encoding('BINARY')
+ 'bar'.dup.force_encoding('US-ASCII'),
+ 'foo'.dup.force_encoding('BINARY')
]
end
def self.array_with_usascii_and_binary_strings
[
- 'bar'.force_encoding('US-ASCII'),
+ 'bar'.dup.force_encoding('US-ASCII'),
[255].pack('C').force_encoding('BINARY')
]
end
diff --git a/spec/ruby/core/array/flatten_spec.rb b/spec/ruby/core/array/flatten_spec.rb
index 1770b5389a..8c97000c79 100644
--- a/spec/ruby/core/array/flatten_spec.rb
+++ b/spec/ruby/core/array/flatten_spec.rb
@@ -75,24 +75,12 @@ describe "Array#flatten" do
[[obj]].flatten(1)
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instance for Array subclasses" do
- ArraySpecs::MyArray[].flatten.should be_an_instance_of(ArraySpecs::MyArray)
- ArraySpecs::MyArray[1, 2, 3].flatten.should be_an_instance_of(ArraySpecs::MyArray)
- ArraySpecs::MyArray[1, [2], 3].flatten.should be_an_instance_of(ArraySpecs::MyArray)
- ArraySpecs::MyArray[1, [2, 3], 4].flatten.should == ArraySpecs::MyArray[1, 2, 3, 4]
- [ArraySpecs::MyArray[1, 2, 3]].flatten.should be_an_instance_of(Array)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns Array instance for Array subclasses" do
- ArraySpecs::MyArray[].flatten.should be_an_instance_of(Array)
- ArraySpecs::MyArray[1, 2, 3].flatten.should be_an_instance_of(Array)
- ArraySpecs::MyArray[1, [2], 3].flatten.should be_an_instance_of(Array)
- ArraySpecs::MyArray[1, [2, 3], 4].flatten.should == [1, 2, 3, 4]
- [ArraySpecs::MyArray[1, 2, 3]].flatten.should be_an_instance_of(Array)
- end
+ it "returns Array instance for Array subclasses" do
+ ArraySpecs::MyArray[].flatten.should be_an_instance_of(Array)
+ ArraySpecs::MyArray[1, 2, 3].flatten.should be_an_instance_of(Array)
+ ArraySpecs::MyArray[1, [2], 3].flatten.should be_an_instance_of(Array)
+ ArraySpecs::MyArray[1, [2, 3], 4].flatten.should == [1, 2, 3, 4]
+ [ArraySpecs::MyArray[1, 2, 3]].flatten.should be_an_instance_of(Array)
end
it "is not destructive" do
diff --git a/spec/ruby/core/array/multiply_spec.rb b/spec/ruby/core/array/multiply_spec.rb
index 23d5c99f3a..eca51142fb 100644
--- a/spec/ruby/core/array/multiply_spec.rb
+++ b/spec/ruby/core/array/multiply_spec.rb
@@ -76,20 +76,10 @@ describe "Array#* with an integer" do
@array = ArraySpecs::MyArray[1, 2, 3, 4, 5]
end
- ruby_version_is ''...'3.0' do
- it "returns a subclass instance" do
- (@array * 0).should be_an_instance_of(ArraySpecs::MyArray)
- (@array * 1).should be_an_instance_of(ArraySpecs::MyArray)
- (@array * 2).should be_an_instance_of(ArraySpecs::MyArray)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns an Array instance" do
- (@array * 0).should be_an_instance_of(Array)
- (@array * 1).should be_an_instance_of(Array)
- (@array * 2).should be_an_instance_of(Array)
- end
+ it "returns an Array instance" do
+ (@array * 0).should be_an_instance_of(Array)
+ (@array * 1).should be_an_instance_of(Array)
+ (@array * 2).should be_an_instance_of(Array)
end
it "does not call #initialize on the subclass instance" do
diff --git a/spec/ruby/core/array/pack/buffer_spec.rb b/spec/ruby/core/array/pack/buffer_spec.rb
index ecb40bfd06..f1206efb3e 100644
--- a/spec/ruby/core/array/pack/buffer_spec.rb
+++ b/spec/ruby/core/array/pack/buffer_spec.rb
@@ -13,13 +13,13 @@ describe "Array#pack with :buffer option" do
it "adds result at the end of buffer content" do
n = [ 65, 66, 67 ] # result without buffer is "ABC"
- buffer = ""
+ buffer = +""
n.pack("ccc", buffer: buffer).should == "ABC"
- buffer = "123"
+ buffer = +"123"
n.pack("ccc", buffer: buffer).should == "123ABC"
- buffer = "12345"
+ buffer = +"12345"
n.pack("ccc", buffer: buffer).should == "12345ABC"
end
@@ -31,19 +31,19 @@ describe "Array#pack with :buffer option" do
context "offset (@) is specified" do
it 'keeps buffer content if it is longer than offset' do
n = [ 65, 66, 67 ]
- buffer = "123456"
+ buffer = +"123456"
n.pack("@3ccc", buffer: buffer).should == "123ABC"
end
it "fills the gap with \\0 if buffer content is shorter than offset" do
n = [ 65, 66, 67 ]
- buffer = "123"
+ buffer = +"123"
n.pack("@6ccc", buffer: buffer).should == "123\0\0\0ABC"
end
it 'does not keep buffer content if it is longer than offset + result' do
n = [ 65, 66, 67 ]
- buffer = "1234567890"
+ buffer = +"1234567890"
n.pack("@3ccc", buffer: buffer).should == "123ABC"
end
end
diff --git a/spec/ruby/core/array/pack/shared/string.rb b/spec/ruby/core/array/pack/shared/string.rb
index 8c82e8c617..2f70dc3951 100644
--- a/spec/ruby/core/array/pack/shared/string.rb
+++ b/spec/ruby/core/array/pack/shared/string.rb
@@ -40,7 +40,7 @@ describe :array_pack_string, shared: true do
f = pack_format("*")
[ [["\u{3042 3044 3046 3048}", 0x2000B].pack(f+"U"), Encoding::BINARY],
[["abcde\xd1", "\xFF\xFe\x81\x82"].pack(f+"u"), Encoding::BINARY],
- [["a".force_encoding("ascii"), "\xFF\xFe\x81\x82"].pack(f+"u"), Encoding::BINARY],
+ [["a".dup.force_encoding("ascii"), "\xFF\xFe\x81\x82"].pack(f+"u"), Encoding::BINARY],
# under discussion [ruby-dev:37294]
[["\u{3042 3044 3046 3048}", 1].pack(f+"N"), Encoding::BINARY]
].should be_computed_by(:encoding)
diff --git a/spec/ruby/core/array/rassoc_spec.rb b/spec/ruby/core/array/rassoc_spec.rb
index 62fbd40611..632a05e8b3 100644
--- a/spec/ruby/core/array/rassoc_spec.rb
+++ b/spec/ruby/core/array/rassoc_spec.rb
@@ -35,4 +35,18 @@ describe "Array#rassoc" do
[[1, :foobar, o], [2, o, 1], [3, mock('foo')]].rassoc(key).should == [2, o, 1]
end
+
+ ruby_version_is "3.3" do
+ it "calls to_ary on non-array elements" do
+ s1 = [1, 2]
+ s2 = ArraySpecs::ArrayConvertible.new(2, 3)
+ a = [s1, s2]
+
+ s1.should_not_receive(:to_ary)
+ a.rassoc(2).should equal(s1)
+
+ a.rassoc(3).should == [2, 3]
+ s2.called.should equal(:to_ary)
+ end
+ end
end
diff --git a/spec/ruby/core/array/shared/inspect.rb b/spec/ruby/core/array/shared/inspect.rb
index a2b43d4959..af5128c645 100644
--- a/spec/ruby/core/array/shared/inspect.rb
+++ b/spec/ruby/core/array/shared/inspect.rb
@@ -19,7 +19,7 @@ describe :array_inspect, shared: true do
end
it "does not call #to_s on a String returned from #inspect" do
- str = "abc"
+ str = +"abc"
str.should_not_receive(:to_s)
[str].send(@method).should == '["abc"]'
@@ -98,8 +98,8 @@ describe :array_inspect, shared: true do
end
it "does not raise if inspected result is not default external encoding" do
- utf_16be = mock("utf_16be")
- utf_16be.should_receive(:inspect).and_return(%<"utf_16be \u3042">.encode!(Encoding::UTF_16BE))
+ utf_16be = mock(+"utf_16be")
+ utf_16be.should_receive(:inspect).and_return(%<"utf_16be \u3042">.encode(Encoding::UTF_16BE))
[utf_16be].send(@method).should == '["utf_16be \u3042"]'
end
diff --git a/spec/ruby/core/array/shared/slice.rb b/spec/ruby/core/array/shared/slice.rb
index 8fb33738b9..d2866970a5 100644
--- a/spec/ruby/core/array/shared/slice.rb
+++ b/spec/ruby/core/array/shared/slice.rb
@@ -397,56 +397,28 @@ describe :array_slice, shared: true do
@array = ArraySpecs::MyArray[1, 2, 3, 4, 5]
end
- ruby_version_is ''...'3.0' do
- it "returns a subclass instance with [n, m]" do
- @array.send(@method, 0, 2).should be_an_instance_of(ArraySpecs::MyArray)
- end
-
- it "returns a subclass instance with [-n, m]" do
- @array.send(@method, -3, 2).should be_an_instance_of(ArraySpecs::MyArray)
- end
-
- it "returns a subclass instance with [n..m]" do
- @array.send(@method, 1..3).should be_an_instance_of(ArraySpecs::MyArray)
- end
-
- it "returns a subclass instance with [n...m]" do
- @array.send(@method, 1...3).should be_an_instance_of(ArraySpecs::MyArray)
- end
-
- it "returns a subclass instance with [-n..-m]" do
- @array.send(@method, -3..-1).should be_an_instance_of(ArraySpecs::MyArray)
- end
-
- it "returns a subclass instance with [-n...-m]" do
- @array.send(@method, -3...-1).should be_an_instance_of(ArraySpecs::MyArray)
- end
+ it "returns a Array instance with [n, m]" do
+ @array.send(@method, 0, 2).should be_an_instance_of(Array)
end
- ruby_version_is '3.0' do
- it "returns a Array instance with [n, m]" do
- @array.send(@method, 0, 2).should be_an_instance_of(Array)
- end
-
- it "returns a Array instance with [-n, m]" do
- @array.send(@method, -3, 2).should be_an_instance_of(Array)
- end
+ it "returns a Array instance with [-n, m]" do
+ @array.send(@method, -3, 2).should be_an_instance_of(Array)
+ end
- it "returns a Array instance with [n..m]" do
- @array.send(@method, 1..3).should be_an_instance_of(Array)
- end
+ it "returns a Array instance with [n..m]" do
+ @array.send(@method, 1..3).should be_an_instance_of(Array)
+ end
- it "returns a Array instance with [n...m]" do
- @array.send(@method, 1...3).should be_an_instance_of(Array)
- end
+ it "returns a Array instance with [n...m]" do
+ @array.send(@method, 1...3).should be_an_instance_of(Array)
+ end
- it "returns a Array instance with [-n..-m]" do
- @array.send(@method, -3..-1).should be_an_instance_of(Array)
- end
+ it "returns a Array instance with [-n..-m]" do
+ @array.send(@method, -3..-1).should be_an_instance_of(Array)
+ end
- it "returns a Array instance with [-n...-m]" do
- @array.send(@method, -3...-1).should be_an_instance_of(Array)
- end
+ it "returns a Array instance with [-n...-m]" do
+ @array.send(@method, -3...-1).should be_an_instance_of(Array)
end
it "returns an empty array when m == n with [m...n]" do
@@ -534,239 +506,237 @@ describe :array_slice, shared: true do
a.send(@method, eval("(-9...)")).should == nil
end
- ruby_version_is "3.0" do
- describe "can be sliced with Enumerator::ArithmeticSequence" do
- before :each do
- @array = [0, 1, 2, 3, 4, 5]
- end
+ describe "can be sliced with Enumerator::ArithmeticSequence" do
+ before :each do
+ @array = [0, 1, 2, 3, 4, 5]
+ end
- it "has endless range and positive steps" do
- @array.send(@method, eval("(0..).step(1)")).should == [0, 1, 2, 3, 4, 5]
- @array.send(@method, eval("(0..).step(2)")).should == [0, 2, 4]
- @array.send(@method, eval("(0..).step(10)")).should == [0]
+ it "has endless range and positive steps" do
+ @array.send(@method, eval("(0..).step(1)")).should == [0, 1, 2, 3, 4, 5]
+ @array.send(@method, eval("(0..).step(2)")).should == [0, 2, 4]
+ @array.send(@method, eval("(0..).step(10)")).should == [0]
- @array.send(@method, eval("(2..).step(1)")).should == [2, 3, 4, 5]
- @array.send(@method, eval("(2..).step(2)")).should == [2, 4]
- @array.send(@method, eval("(2..).step(10)")).should == [2]
+ @array.send(@method, eval("(2..).step(1)")).should == [2, 3, 4, 5]
+ @array.send(@method, eval("(2..).step(2)")).should == [2, 4]
+ @array.send(@method, eval("(2..).step(10)")).should == [2]
- @array.send(@method, eval("(-3..).step(1)")).should == [3, 4, 5]
- @array.send(@method, eval("(-3..).step(2)")).should == [3, 5]
- @array.send(@method, eval("(-3..).step(10)")).should == [3]
- end
+ @array.send(@method, eval("(-3..).step(1)")).should == [3, 4, 5]
+ @array.send(@method, eval("(-3..).step(2)")).should == [3, 5]
+ @array.send(@method, eval("(-3..).step(10)")).should == [3]
+ end
- it "has beginless range and positive steps" do
- # end with zero index
- @array.send(@method, (..0).step(1)).should == [0]
- @array.send(@method, (...0).step(1)).should == []
+ it "has beginless range and positive steps" do
+ # end with zero index
+ @array.send(@method, (..0).step(1)).should == [0]
+ @array.send(@method, (...0).step(1)).should == []
- @array.send(@method, (..0).step(2)).should == [0]
- @array.send(@method, (...0).step(2)).should == []
+ @array.send(@method, (..0).step(2)).should == [0]
+ @array.send(@method, (...0).step(2)).should == []
- @array.send(@method, (..0).step(10)).should == [0]
- @array.send(@method, (...0).step(10)).should == []
+ @array.send(@method, (..0).step(10)).should == [0]
+ @array.send(@method, (...0).step(10)).should == []
- # end with positive index
- @array.send(@method, (..3).step(1)).should == [0, 1, 2, 3]
- @array.send(@method, (...3).step(1)).should == [0, 1, 2]
+ # end with positive index
+ @array.send(@method, (..3).step(1)).should == [0, 1, 2, 3]
+ @array.send(@method, (...3).step(1)).should == [0, 1, 2]
- @array.send(@method, (..3).step(2)).should == [0, 2]
- @array.send(@method, (...3).step(2)).should == [0, 2]
+ @array.send(@method, (..3).step(2)).should == [0, 2]
+ @array.send(@method, (...3).step(2)).should == [0, 2]
- @array.send(@method, (..3).step(10)).should == [0]
- @array.send(@method, (...3).step(10)).should == [0]
+ @array.send(@method, (..3).step(10)).should == [0]
+ @array.send(@method, (...3).step(10)).should == [0]
- # end with negative index
- @array.send(@method, (..-2).step(1)).should == [0, 1, 2, 3, 4,]
- @array.send(@method, (...-2).step(1)).should == [0, 1, 2, 3]
+ # end with negative index
+ @array.send(@method, (..-2).step(1)).should == [0, 1, 2, 3, 4,]
+ @array.send(@method, (...-2).step(1)).should == [0, 1, 2, 3]
- @array.send(@method, (..-2).step(2)).should == [0, 2, 4]
- @array.send(@method, (...-2).step(2)).should == [0, 2]
+ @array.send(@method, (..-2).step(2)).should == [0, 2, 4]
+ @array.send(@method, (...-2).step(2)).should == [0, 2]
- @array.send(@method, (..-2).step(10)).should == [0]
- @array.send(@method, (...-2).step(10)).should == [0]
- end
+ @array.send(@method, (..-2).step(10)).should == [0]
+ @array.send(@method, (...-2).step(10)).should == [0]
+ end
- it "has endless range and negative steps" do
- @array.send(@method, eval("(0..).step(-1)")).should == [0]
- @array.send(@method, eval("(0..).step(-2)")).should == [0]
- @array.send(@method, eval("(0..).step(-10)")).should == [0]
+ it "has endless range and negative steps" do
+ @array.send(@method, eval("(0..).step(-1)")).should == [0]
+ @array.send(@method, eval("(0..).step(-2)")).should == [0]
+ @array.send(@method, eval("(0..).step(-10)")).should == [0]
- @array.send(@method, eval("(2..).step(-1)")).should == [2, 1, 0]
- @array.send(@method, eval("(2..).step(-2)")).should == [2, 0]
+ @array.send(@method, eval("(2..).step(-1)")).should == [2, 1, 0]
+ @array.send(@method, eval("(2..).step(-2)")).should == [2, 0]
- @array.send(@method, eval("(-3..).step(-1)")).should == [3, 2, 1, 0]
- @array.send(@method, eval("(-3..).step(-2)")).should == [3, 1]
- end
+ @array.send(@method, eval("(-3..).step(-1)")).should == [3, 2, 1, 0]
+ @array.send(@method, eval("(-3..).step(-2)")).should == [3, 1]
+ end
- it "has closed range and positive steps" do
- # start and end with 0
- @array.send(@method, eval("(0..0).step(1)")).should == [0]
- @array.send(@method, eval("(0...0).step(1)")).should == []
+ it "has closed range and positive steps" do
+ # start and end with 0
+ @array.send(@method, eval("(0..0).step(1)")).should == [0]
+ @array.send(@method, eval("(0...0).step(1)")).should == []
- @array.send(@method, eval("(0..0).step(2)")).should == [0]
- @array.send(@method, eval("(0...0).step(2)")).should == []
+ @array.send(@method, eval("(0..0).step(2)")).should == [0]
+ @array.send(@method, eval("(0...0).step(2)")).should == []
- @array.send(@method, eval("(0..0).step(10)")).should == [0]
- @array.send(@method, eval("(0...0).step(10)")).should == []
+ @array.send(@method, eval("(0..0).step(10)")).should == [0]
+ @array.send(@method, eval("(0...0).step(10)")).should == []
- # start and end with positive index
- @array.send(@method, eval("(1..3).step(1)")).should == [1, 2, 3]
- @array.send(@method, eval("(1...3).step(1)")).should == [1, 2]
+ # start and end with positive index
+ @array.send(@method, eval("(1..3).step(1)")).should == [1, 2, 3]
+ @array.send(@method, eval("(1...3).step(1)")).should == [1, 2]
- @array.send(@method, eval("(1..3).step(2)")).should == [1, 3]
- @array.send(@method, eval("(1...3).step(2)")).should == [1]
+ @array.send(@method, eval("(1..3).step(2)")).should == [1, 3]
+ @array.send(@method, eval("(1...3).step(2)")).should == [1]
- @array.send(@method, eval("(1..3).step(10)")).should == [1]
- @array.send(@method, eval("(1...3).step(10)")).should == [1]
+ @array.send(@method, eval("(1..3).step(10)")).should == [1]
+ @array.send(@method, eval("(1...3).step(10)")).should == [1]
- # start with positive index, end with negative index
- @array.send(@method, eval("(1..-2).step(1)")).should == [1, 2, 3, 4]
- @array.send(@method, eval("(1...-2).step(1)")).should == [1, 2, 3]
+ # start with positive index, end with negative index
+ @array.send(@method, eval("(1..-2).step(1)")).should == [1, 2, 3, 4]
+ @array.send(@method, eval("(1...-2).step(1)")).should == [1, 2, 3]
- @array.send(@method, eval("(1..-2).step(2)")).should == [1, 3]
- @array.send(@method, eval("(1...-2).step(2)")).should == [1, 3]
+ @array.send(@method, eval("(1..-2).step(2)")).should == [1, 3]
+ @array.send(@method, eval("(1...-2).step(2)")).should == [1, 3]
- @array.send(@method, eval("(1..-2).step(10)")).should == [1]
- @array.send(@method, eval("(1...-2).step(10)")).should == [1]
+ @array.send(@method, eval("(1..-2).step(10)")).should == [1]
+ @array.send(@method, eval("(1...-2).step(10)")).should == [1]
- # start with negative index, end with positive index
- @array.send(@method, eval("(-4..4).step(1)")).should == [2, 3, 4]
- @array.send(@method, eval("(-4...4).step(1)")).should == [2, 3]
+ # start with negative index, end with positive index
+ @array.send(@method, eval("(-4..4).step(1)")).should == [2, 3, 4]
+ @array.send(@method, eval("(-4...4).step(1)")).should == [2, 3]
- @array.send(@method, eval("(-4..4).step(2)")).should == [2, 4]
- @array.send(@method, eval("(-4...4).step(2)")).should == [2]
+ @array.send(@method, eval("(-4..4).step(2)")).should == [2, 4]
+ @array.send(@method, eval("(-4...4).step(2)")).should == [2]
- @array.send(@method, eval("(-4..4).step(10)")).should == [2]
- @array.send(@method, eval("(-4...4).step(10)")).should == [2]
+ @array.send(@method, eval("(-4..4).step(10)")).should == [2]
+ @array.send(@method, eval("(-4...4).step(10)")).should == [2]
- # start with negative index, end with negative index
- @array.send(@method, eval("(-4..-2).step(1)")).should == [2, 3, 4]
- @array.send(@method, eval("(-4...-2).step(1)")).should == [2, 3]
+ # start with negative index, end with negative index
+ @array.send(@method, eval("(-4..-2).step(1)")).should == [2, 3, 4]
+ @array.send(@method, eval("(-4...-2).step(1)")).should == [2, 3]
- @array.send(@method, eval("(-4..-2).step(2)")).should == [2, 4]
- @array.send(@method, eval("(-4...-2).step(2)")).should == [2]
+ @array.send(@method, eval("(-4..-2).step(2)")).should == [2, 4]
+ @array.send(@method, eval("(-4...-2).step(2)")).should == [2]
- @array.send(@method, eval("(-4..-2).step(10)")).should == [2]
- @array.send(@method, eval("(-4...-2).step(10)")).should == [2]
- end
+ @array.send(@method, eval("(-4..-2).step(10)")).should == [2]
+ @array.send(@method, eval("(-4...-2).step(10)")).should == [2]
+ end
- it "has closed range and negative steps" do
- # start and end with 0
- @array.send(@method, eval("(0..0).step(-1)")).should == [0]
- @array.send(@method, eval("(0...0).step(-1)")).should == []
+ it "has closed range and negative steps" do
+ # start and end with 0
+ @array.send(@method, eval("(0..0).step(-1)")).should == [0]
+ @array.send(@method, eval("(0...0).step(-1)")).should == []
- @array.send(@method, eval("(0..0).step(-2)")).should == [0]
- @array.send(@method, eval("(0...0).step(-2)")).should == []
+ @array.send(@method, eval("(0..0).step(-2)")).should == [0]
+ @array.send(@method, eval("(0...0).step(-2)")).should == []
- @array.send(@method, eval("(0..0).step(-10)")).should == [0]
- @array.send(@method, eval("(0...0).step(-10)")).should == []
+ @array.send(@method, eval("(0..0).step(-10)")).should == [0]
+ @array.send(@method, eval("(0...0).step(-10)")).should == []
- # start and end with positive index
- @array.send(@method, eval("(1..3).step(-1)")).should == []
- @array.send(@method, eval("(1...3).step(-1)")).should == []
+ # start and end with positive index
+ @array.send(@method, eval("(1..3).step(-1)")).should == []
+ @array.send(@method, eval("(1...3).step(-1)")).should == []
- @array.send(@method, eval("(1..3).step(-2)")).should == []
- @array.send(@method, eval("(1...3).step(-2)")).should == []
+ @array.send(@method, eval("(1..3).step(-2)")).should == []
+ @array.send(@method, eval("(1...3).step(-2)")).should == []
- @array.send(@method, eval("(1..3).step(-10)")).should == []
- @array.send(@method, eval("(1...3).step(-10)")).should == []
+ @array.send(@method, eval("(1..3).step(-10)")).should == []
+ @array.send(@method, eval("(1...3).step(-10)")).should == []
- # start with positive index, end with negative index
- @array.send(@method, eval("(1..-2).step(-1)")).should == []
- @array.send(@method, eval("(1...-2).step(-1)")).should == []
+ # start with positive index, end with negative index
+ @array.send(@method, eval("(1..-2).step(-1)")).should == []
+ @array.send(@method, eval("(1...-2).step(-1)")).should == []
- @array.send(@method, eval("(1..-2).step(-2)")).should == []
- @array.send(@method, eval("(1...-2).step(-2)")).should == []
+ @array.send(@method, eval("(1..-2).step(-2)")).should == []
+ @array.send(@method, eval("(1...-2).step(-2)")).should == []
- @array.send(@method, eval("(1..-2).step(-10)")).should == []
- @array.send(@method, eval("(1...-2).step(-10)")).should == []
+ @array.send(@method, eval("(1..-2).step(-10)")).should == []
+ @array.send(@method, eval("(1...-2).step(-10)")).should == []
- # start with negative index, end with positive index
- @array.send(@method, eval("(-4..4).step(-1)")).should == []
- @array.send(@method, eval("(-4...4).step(-1)")).should == []
+ # start with negative index, end with positive index
+ @array.send(@method, eval("(-4..4).step(-1)")).should == []
+ @array.send(@method, eval("(-4...4).step(-1)")).should == []
- @array.send(@method, eval("(-4..4).step(-2)")).should == []
- @array.send(@method, eval("(-4...4).step(-2)")).should == []
+ @array.send(@method, eval("(-4..4).step(-2)")).should == []
+ @array.send(@method, eval("(-4...4).step(-2)")).should == []
- @array.send(@method, eval("(-4..4).step(-10)")).should == []
- @array.send(@method, eval("(-4...4).step(-10)")).should == []
+ @array.send(@method, eval("(-4..4).step(-10)")).should == []
+ @array.send(@method, eval("(-4...4).step(-10)")).should == []
- # start with negative index, end with negative index
- @array.send(@method, eval("(-4..-2).step(-1)")).should == []
- @array.send(@method, eval("(-4...-2).step(-1)")).should == []
+ # start with negative index, end with negative index
+ @array.send(@method, eval("(-4..-2).step(-1)")).should == []
+ @array.send(@method, eval("(-4...-2).step(-1)")).should == []
- @array.send(@method, eval("(-4..-2).step(-2)")).should == []
- @array.send(@method, eval("(-4...-2).step(-2)")).should == []
+ @array.send(@method, eval("(-4..-2).step(-2)")).should == []
+ @array.send(@method, eval("(-4...-2).step(-2)")).should == []
- @array.send(@method, eval("(-4..-2).step(-10)")).should == []
- @array.send(@method, eval("(-4...-2).step(-10)")).should == []
- end
+ @array.send(@method, eval("(-4..-2).step(-10)")).should == []
+ @array.send(@method, eval("(-4...-2).step(-10)")).should == []
+ end
- it "has inverted closed range and positive steps" do
- # start and end with positive index
- @array.send(@method, eval("(3..1).step(1)")).should == []
- @array.send(@method, eval("(3...1).step(1)")).should == []
+ it "has inverted closed range and positive steps" do
+ # start and end with positive index
+ @array.send(@method, eval("(3..1).step(1)")).should == []
+ @array.send(@method, eval("(3...1).step(1)")).should == []
- @array.send(@method, eval("(3..1).step(2)")).should == []
- @array.send(@method, eval("(3...1).step(2)")).should == []
+ @array.send(@method, eval("(3..1).step(2)")).should == []
+ @array.send(@method, eval("(3...1).step(2)")).should == []
- @array.send(@method, eval("(3..1).step(10)")).should == []
- @array.send(@method, eval("(3...1).step(10)")).should == []
+ @array.send(@method, eval("(3..1).step(10)")).should == []
+ @array.send(@method, eval("(3...1).step(10)")).should == []
- # start with negative index, end with positive index
- @array.send(@method, eval("(-2..1).step(1)")).should == []
- @array.send(@method, eval("(-2...1).step(1)")).should == []
+ # start with negative index, end with positive index
+ @array.send(@method, eval("(-2..1).step(1)")).should == []
+ @array.send(@method, eval("(-2...1).step(1)")).should == []
- @array.send(@method, eval("(-2..1).step(2)")).should == []
- @array.send(@method, eval("(-2...1).step(2)")).should == []
+ @array.send(@method, eval("(-2..1).step(2)")).should == []
+ @array.send(@method, eval("(-2...1).step(2)")).should == []
- @array.send(@method, eval("(-2..1).step(10)")).should == []
- @array.send(@method, eval("(-2...1).step(10)")).should == []
+ @array.send(@method, eval("(-2..1).step(10)")).should == []
+ @array.send(@method, eval("(-2...1).step(10)")).should == []
- # start with positive index, end with negative index
- @array.send(@method, eval("(4..-4).step(1)")).should == []
- @array.send(@method, eval("(4...-4).step(1)")).should == []
+ # start with positive index, end with negative index
+ @array.send(@method, eval("(4..-4).step(1)")).should == []
+ @array.send(@method, eval("(4...-4).step(1)")).should == []
- @array.send(@method, eval("(4..-4).step(2)")).should == []
- @array.send(@method, eval("(4...-4).step(2)")).should == []
+ @array.send(@method, eval("(4..-4).step(2)")).should == []
+ @array.send(@method, eval("(4...-4).step(2)")).should == []
- @array.send(@method, eval("(4..-4).step(10)")).should == []
- @array.send(@method, eval("(4...-4).step(10)")).should == []
+ @array.send(@method, eval("(4..-4).step(10)")).should == []
+ @array.send(@method, eval("(4...-4).step(10)")).should == []
- # start with negative index, end with negative index
- @array.send(@method, eval("(-2..-4).step(1)")).should == []
- @array.send(@method, eval("(-2...-4).step(1)")).should == []
+ # start with negative index, end with negative index
+ @array.send(@method, eval("(-2..-4).step(1)")).should == []
+ @array.send(@method, eval("(-2...-4).step(1)")).should == []
- @array.send(@method, eval("(-2..-4).step(2)")).should == []
- @array.send(@method, eval("(-2...-4).step(2)")).should == []
+ @array.send(@method, eval("(-2..-4).step(2)")).should == []
+ @array.send(@method, eval("(-2...-4).step(2)")).should == []
- @array.send(@method, eval("(-2..-4).step(10)")).should == []
- @array.send(@method, eval("(-2...-4).step(10)")).should == []
- end
+ @array.send(@method, eval("(-2..-4).step(10)")).should == []
+ @array.send(@method, eval("(-2...-4).step(10)")).should == []
+ end
- it "has range with bounds outside of array" do
- # end is equal to array's length
- @array.send(@method, (0..6).step(1)).should == [0, 1, 2, 3, 4, 5]
- -> { @array.send(@method, (0..6).step(2)) }.should raise_error(RangeError)
+ it "has range with bounds outside of array" do
+ # end is equal to array's length
+ @array.send(@method, (0..6).step(1)).should == [0, 1, 2, 3, 4, 5]
+ -> { @array.send(@method, (0..6).step(2)) }.should raise_error(RangeError)
- # end is greater than length with positive steps
- @array.send(@method, (1..6).step(2)).should == [1, 3, 5]
- @array.send(@method, (2..7).step(2)).should == [2, 4]
- -> { @array.send(@method, (2..8).step(2)) }.should raise_error(RangeError)
+ # end is greater than length with positive steps
+ @array.send(@method, (1..6).step(2)).should == [1, 3, 5]
+ @array.send(@method, (2..7).step(2)).should == [2, 4]
+ -> { @array.send(@method, (2..8).step(2)) }.should raise_error(RangeError)
- # begin is greater than length with negative steps
- @array.send(@method, (6..1).step(-2)).should == [5, 3, 1]
- @array.send(@method, (7..2).step(-2)).should == [5, 3]
- -> { @array.send(@method, (8..2).step(-2)) }.should raise_error(RangeError)
- end
+ # begin is greater than length with negative steps
+ @array.send(@method, (6..1).step(-2)).should == [5, 3, 1]
+ @array.send(@method, (7..2).step(-2)).should == [5, 3]
+ -> { @array.send(@method, (8..2).step(-2)) }.should raise_error(RangeError)
+ end
- it "has endless range with start outside of array's bounds" do
- @array.send(@method, eval("(6..).step(1)")).should == []
- @array.send(@method, eval("(7..).step(1)")).should == nil
+ it "has endless range with start outside of array's bounds" do
+ @array.send(@method, eval("(6..).step(1)")).should == []
+ @array.send(@method, eval("(7..).step(1)")).should == nil
- @array.send(@method, eval("(6..).step(2)")).should == []
- -> { @array.send(@method, eval("(7..).step(2)")) }.should raise_error(RangeError)
- end
+ @array.send(@method, eval("(6..).step(2)")).should == []
+ -> { @array.send(@method, eval("(7..).step(2)")) }.should raise_error(RangeError)
end
end
diff --git a/spec/ruby/core/array/slice_spec.rb b/spec/ruby/core/array/slice_spec.rb
index 8e1499855a..731c129251 100644
--- a/spec/ruby/core/array/slice_spec.rb
+++ b/spec/ruby/core/array/slice_spec.rb
@@ -187,56 +187,28 @@ describe "Array#slice!" do
@array = ArraySpecs::MyArray[1, 2, 3, 4, 5]
end
- ruby_version_is ''...'3.0' do
- it "returns a subclass instance with [n, m]" do
- @array.slice!(0, 2).should be_an_instance_of(ArraySpecs::MyArray)
- end
-
- it "returns a subclass instance with [-n, m]" do
- @array.slice!(-3, 2).should be_an_instance_of(ArraySpecs::MyArray)
- end
-
- it "returns a subclass instance with [n..m]" do
- @array.slice!(1..3).should be_an_instance_of(ArraySpecs::MyArray)
- end
-
- it "returns a subclass instance with [n...m]" do
- @array.slice!(1...3).should be_an_instance_of(ArraySpecs::MyArray)
- end
-
- it "returns a subclass instance with [-n..-m]" do
- @array.slice!(-3..-1).should be_an_instance_of(ArraySpecs::MyArray)
- end
-
- it "returns a subclass instance with [-n...-m]" do
- @array.slice!(-3...-1).should be_an_instance_of(ArraySpecs::MyArray)
- end
+ it "returns a Array instance with [n, m]" do
+ @array.slice!(0, 2).should be_an_instance_of(Array)
end
- ruby_version_is '3.0' do
- it "returns a Array instance with [n, m]" do
- @array.slice!(0, 2).should be_an_instance_of(Array)
- end
-
- it "returns a Array instance with [-n, m]" do
- @array.slice!(-3, 2).should be_an_instance_of(Array)
- end
+ it "returns a Array instance with [-n, m]" do
+ @array.slice!(-3, 2).should be_an_instance_of(Array)
+ end
- it "returns a Array instance with [n..m]" do
- @array.slice!(1..3).should be_an_instance_of(Array)
- end
+ it "returns a Array instance with [n..m]" do
+ @array.slice!(1..3).should be_an_instance_of(Array)
+ end
- it "returns a Array instance with [n...m]" do
- @array.slice!(1...3).should be_an_instance_of(Array)
- end
+ it "returns a Array instance with [n...m]" do
+ @array.slice!(1...3).should be_an_instance_of(Array)
+ end
- it "returns a Array instance with [-n..-m]" do
- @array.slice!(-3..-1).should be_an_instance_of(Array)
- end
+ it "returns a Array instance with [-n..-m]" do
+ @array.slice!(-3..-1).should be_an_instance_of(Array)
+ end
- it "returns a Array instance with [-n...-m]" do
- @array.slice!(-3...-1).should be_an_instance_of(Array)
- end
+ it "returns a Array instance with [-n...-m]" do
+ @array.slice!(-3...-1).should be_an_instance_of(Array)
end
end
end
diff --git a/spec/ruby/core/array/take_spec.rb b/spec/ruby/core/array/take_spec.rb
index 4fb6f0ce75..c4f0ac9aa4 100644
--- a/spec/ruby/core/array/take_spec.rb
+++ b/spec/ruby/core/array/take_spec.rb
@@ -26,15 +26,7 @@ describe "Array#take" do
->{ [1].take(-3) }.should raise_error(ArgumentError)
end
- ruby_version_is ''...'3.0' do
- it 'returns a subclass instance for Array subclasses' do
- ArraySpecs::MyArray[1, 2, 3, 4, 5].take(1).should be_an_instance_of(ArraySpecs::MyArray)
- end
- end
-
- ruby_version_is '3.0' do
- it 'returns a Array instance for Array subclasses' do
- ArraySpecs::MyArray[1, 2, 3, 4, 5].take(1).should be_an_instance_of(Array)
- end
+ it 'returns a Array instance for Array subclasses' do
+ ArraySpecs::MyArray[1, 2, 3, 4, 5].take(1).should be_an_instance_of(Array)
end
end
diff --git a/spec/ruby/core/array/take_while_spec.rb b/spec/ruby/core/array/take_while_spec.rb
index 73f25493c8..8f50260b42 100644
--- a/spec/ruby/core/array/take_while_spec.rb
+++ b/spec/ruby/core/array/take_while_spec.rb
@@ -15,16 +15,8 @@ describe "Array#take_while" do
[1, 2, false, 4].take_while{ |element| element }.should == [1, 2]
end
- ruby_version_is ''...'3.0' do
- it 'returns a subclass instance for Array subclasses' do
- ArraySpecs::MyArray[1, 2, 3, 4, 5].take_while { |n| n < 4 }.should be_an_instance_of(ArraySpecs::MyArray)
- end
- end
-
- ruby_version_is '3.0' do
- it 'returns a Array instance for Array subclasses' do
- ArraySpecs::MyArray[1, 2, 3, 4, 5].take_while { |n| n < 4 }.should be_an_instance_of(Array)
- end
+ it 'returns a Array instance for Array subclasses' do
+ ArraySpecs::MyArray[1, 2, 3, 4, 5].take_while { |n| n < 4 }.should be_an_instance_of(Array)
end
end
diff --git a/spec/ruby/core/array/uniq_spec.rb b/spec/ruby/core/array/uniq_spec.rb
index 905ab59634..d5d826db15 100644
--- a/spec/ruby/core/array/uniq_spec.rb
+++ b/spec/ruby/core/array/uniq_spec.rb
@@ -85,16 +85,8 @@ describe "Array#uniq" do
[false, nil, 42].uniq { :bar }.should == [false]
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instance on Array subclasses" do
- ArraySpecs::MyArray[1, 2, 3].uniq.should be_an_instance_of(ArraySpecs::MyArray)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns Array instance on Array subclasses" do
- ArraySpecs::MyArray[1, 2, 3].uniq.should be_an_instance_of(Array)
- end
+ it "returns Array instance on Array subclasses" do
+ ArraySpecs::MyArray[1, 2, 3].uniq.should be_an_instance_of(Array)
end
it "properly handles an identical item even when its #eql? isn't reflexive" do
diff --git a/spec/ruby/core/basicobject/instance_eval_spec.rb b/spec/ruby/core/basicobject/instance_eval_spec.rb
index 699c965171..1f3a43f341 100644
--- a/spec/ruby/core/basicobject/instance_eval_spec.rb
+++ b/spec/ruby/core/basicobject/instance_eval_spec.rb
@@ -84,6 +84,13 @@ describe "BasicObject#instance_eval" do
end
+ ruby_version_is "3.3" do
+ it "uses the caller location as default location" do
+ f = Object.new
+ f.instance_eval("[__FILE__, __LINE__]").should == ["(eval at #{__FILE__}:#{__LINE__})", 1]
+ end
+ end
+
it "has access to receiver's instance variables" do
BasicObjectSpecs::IVars.new.instance_eval { @secret }.should == 99
BasicObjectSpecs::IVars.new.instance_eval("@secret").should == 99
diff --git a/spec/ruby/core/basicobject/singleton_method_added_spec.rb b/spec/ruby/core/basicobject/singleton_method_added_spec.rb
index ab6b2a2d10..fc65a091aa 100644
--- a/spec/ruby/core/basicobject/singleton_method_added_spec.rb
+++ b/spec/ruby/core/basicobject/singleton_method_added_spec.rb
@@ -94,7 +94,7 @@ describe "BasicObject#singleton_method_added" do
-> {
def self.foo
end
- }.should raise_error(NoMethodError, /undefined method `singleton_method_added' for/)
+ }.should raise_error(NoMethodError, /undefined method [`']singleton_method_added' for/)
end
end
@@ -106,16 +106,16 @@ describe "BasicObject#singleton_method_added" do
-> {
def foo
end
- }.should raise_error(NoMethodError, /undefined method `singleton_method_added' for #<Object:/)
+ }.should raise_error(NoMethodError, /undefined method [`']singleton_method_added' for #<Object:/)
-> {
define_method(:bar) {}
- }.should raise_error(NoMethodError, /undefined method `singleton_method_added' for #<Object:/)
+ }.should raise_error(NoMethodError, /undefined method [`']singleton_method_added' for #<Object:/)
end
-> {
object.define_singleton_method(:baz) {}
- }.should raise_error(NoMethodError, /undefined method `singleton_method_added' for #<Object:/)
+ }.should raise_error(NoMethodError, /undefined method [`']singleton_method_added' for #<Object:/)
end
it "calls #method_missing" do
diff --git a/spec/ruby/core/binding/clone_spec.rb b/spec/ruby/core/binding/clone_spec.rb
index ebd40f5377..f1769ac6de 100644
--- a/spec/ruby/core/binding/clone_spec.rb
+++ b/spec/ruby/core/binding/clone_spec.rb
@@ -4,4 +4,10 @@ require_relative 'shared/clone'
describe "Binding#clone" do
it_behaves_like :binding_clone, :clone
+
+ it "preserves frozen status" do
+ bind = binding.freeze
+ bind.frozen?.should == true
+ bind.clone.frozen?.should == true
+ end
end
diff --git a/spec/ruby/core/binding/dup_spec.rb b/spec/ruby/core/binding/dup_spec.rb
index 43968213c8..55fac6e333 100644
--- a/spec/ruby/core/binding/dup_spec.rb
+++ b/spec/ruby/core/binding/dup_spec.rb
@@ -4,4 +4,10 @@ require_relative 'shared/clone'
describe "Binding#dup" do
it_behaves_like :binding_clone, :dup
+
+ it "resets frozen status" do
+ bind = binding.freeze
+ bind.frozen?.should == true
+ bind.dup.frozen?.should == false
+ end
end
diff --git a/spec/ruby/core/binding/eval_spec.rb b/spec/ruby/core/binding/eval_spec.rb
index 4bb3da7a6c..bb2036f739 100644
--- a/spec/ruby/core/binding/eval_spec.rb
+++ b/spec/ruby/core/binding/eval_spec.rb
@@ -23,58 +23,29 @@ describe "Binding#eval" do
bind2.local_variables.should == []
end
- ruby_version_is ""..."3.0" do
- it "inherits __LINE__ from the enclosing scope" do
- obj = BindingSpecs::Demo.new(1)
- bind = obj.get_binding
- suppress_warning {bind.eval("__LINE__")}.should == obj.get_line_of_binding
- end
-
- it "preserves __LINE__ across multiple calls to eval" do
- obj = BindingSpecs::Demo.new(1)
- bind = obj.get_binding
- suppress_warning {bind.eval("__LINE__")}.should == obj.get_line_of_binding
- suppress_warning {bind.eval("__LINE__")}.should == obj.get_line_of_binding
- end
-
- it "increments __LINE__ on each line of a multiline eval" do
- obj = BindingSpecs::Demo.new(1)
- bind = obj.get_binding
- suppress_warning {bind.eval("#foo\n__LINE__")}.should == obj.get_line_of_binding + 1
- end
-
- it "inherits __LINE__ from the enclosing scope even if the Binding is created with #send" do
- obj = BindingSpecs::Demo.new(1)
- bind, line = obj.get_binding_with_send_and_line
- suppress_warning {bind.eval("__LINE__")}.should == line
- end
+ it "starts with line 1 if single argument is given" do
+ obj = BindingSpecs::Demo.new(1)
+ bind = obj.get_binding
+ bind.eval("__LINE__").should == 1
end
- ruby_version_is "3.0" do
- it "starts with line 1 if single argument is given" do
- obj = BindingSpecs::Demo.new(1)
- bind = obj.get_binding
- bind.eval("__LINE__").should == 1
- end
-
- it "preserves __LINE__ across multiple calls to eval" do
- obj = BindingSpecs::Demo.new(1)
- bind = obj.get_binding
- bind.eval("__LINE__").should == 1
- bind.eval("__LINE__").should == 1
- end
+ it "preserves __LINE__ across multiple calls to eval" do
+ obj = BindingSpecs::Demo.new(1)
+ bind = obj.get_binding
+ bind.eval("__LINE__").should == 1
+ bind.eval("__LINE__").should == 1
+ end
- it "increments __LINE__ on each line of a multiline eval" do
- obj = BindingSpecs::Demo.new(1)
- bind = obj.get_binding
- bind.eval("#foo\n__LINE__").should == 2
- end
+ it "increments __LINE__ on each line of a multiline eval" do
+ obj = BindingSpecs::Demo.new(1)
+ bind = obj.get_binding
+ bind.eval("#foo\n__LINE__").should == 2
+ end
- it "starts with line 1 if the Binding is created with #send" do
- obj = BindingSpecs::Demo.new(1)
- bind, line = obj.get_binding_with_send_and_line
- bind.eval("__LINE__").should == 1
- end
+ it "starts with line 1 if the Binding is created with #send" do
+ obj = BindingSpecs::Demo.new(1)
+ bind, line = obj.get_binding_with_send_and_line
+ bind.eval("__LINE__").should == 1
end
it "starts with a __LINE__ of 1 if a filename is passed" do
@@ -89,32 +60,18 @@ describe "Binding#eval" do
bind.eval("#foo\n__LINE__", "(test)", 88).should == 89
end
- ruby_version_is ""..."3.0" do
- it "inherits __FILE__ from the enclosing scope" do
- obj = BindingSpecs::Demo.new(1)
- bind = obj.get_binding
- suppress_warning { bind.eval("__FILE__") }.should == obj.get_file_of_binding
- end
-
- it "inherits __LINE__ from the enclosing scope" do
- obj = BindingSpecs::Demo.new(1)
- bind, line = obj.get_binding_and_line
- suppress_warning { bind.eval("__LINE__") }.should == line
- end
- end
-
- ruby_version_is "3.0" do
+ ruby_version_is ""..."3.3" do
it "uses (eval) as __FILE__ if single argument given" do
obj = BindingSpecs::Demo.new(1)
bind = obj.get_binding
bind.eval("__FILE__").should == '(eval)'
end
+ end
- it "uses 1 as __LINE__" do
- obj = BindingSpecs::Demo.new(1)
- bind = obj.get_binding
- suppress_warning { bind.eval("__LINE__") }.should == 1
- end
+ it "uses 1 as __LINE__" do
+ obj = BindingSpecs::Demo.new(1)
+ bind = obj.get_binding
+ suppress_warning { bind.eval("__LINE__") }.should == 1
end
it "uses the __FILE__ that is passed in" do
@@ -149,4 +106,10 @@ describe "Binding#eval" do
bind.eval("'bar'.foo").should == "foo"
end
+
+ ruby_version_is "3.3" do
+ it "uses the caller location as default filename" do
+ binding.eval("[__FILE__, __LINE__]").should == ["(eval at #{__FILE__}:#{__LINE__})", 1]
+ end
+ end
end
diff --git a/spec/ruby/core/binding/fixtures/irbrc b/spec/ruby/core/binding/fixtures/irbrc
deleted file mode 100644
index 2bc12af2f7..0000000000
--- a/spec/ruby/core/binding/fixtures/irbrc
+++ /dev/null
@@ -1 +0,0 @@
-# empty configuration
diff --git a/spec/ruby/core/binding/irb_spec.rb b/spec/ruby/core/binding/irb_spec.rb
index b3bc274f78..25521f0dd7 100644
--- a/spec/ruby/core/binding/irb_spec.rb
+++ b/spec/ruby/core/binding/irb_spec.rb
@@ -1,16 +1,19 @@
require_relative '../../spec_helper'
+require 'tmpdir'
describe "Binding#irb" do
it "creates an IRB session with the binding in scope" do
irb_fixture = fixture __FILE__, "irb.rb"
- irbrc_fixture = fixture __FILE__, "irbrc"
+ envs = %w[IRBRC HOME XDG_CONFIG_HOME].to_h {|e| [e, nil]}
- out = IO.popen([{"IRBRC"=>irbrc_fixture}, *ruby_exe, irb_fixture], "r+") do |pipe|
- pipe.puts "a ** 2"
- pipe.puts "exit"
- pipe.readlines.map(&:chomp)
+ out = Dir.mktmpdir do |dir|
+ IO.popen([envs, *ruby_exe, irb_fixture, chdir: dir], "r+") do |pipe|
+ pipe.puts "a ** 2"
+ pipe.puts "exit"
+ pipe.readlines.map(&:chomp)
+ end
end
- out[-3..-1].should == ["a ** 2", "100", "exit"]
+ out.last(3).should == ["a ** 2", "100", "exit"]
end
end
diff --git a/spec/ruby/core/binding/shared/clone.rb b/spec/ruby/core/binding/shared/clone.rb
index 0e934ac1b5..1224b8ec7d 100644
--- a/spec/ruby/core/binding/shared/clone.rb
+++ b/spec/ruby/core/binding/shared/clone.rb
@@ -31,4 +31,26 @@ describe :binding_clone, shared: true do
b2.local_variable_defined?(:x).should == false
end
end
+
+ ruby_version_is "3.4" do
+ it "copies instance variables" do
+ @b1.instance_variable_set(:@ivar, 1)
+ cl = @b1.send(@method)
+ cl.instance_variables.should == [:@ivar]
+ end
+
+ it "copies the finalizer" do
+ code = <<-RUBY
+ obj = binding
+
+ ObjectSpace.define_finalizer(obj, Proc.new { STDOUT.write "finalized\n" })
+
+ obj.clone
+
+ exit 0
+ RUBY
+
+ ruby_exe(code).lines.sort.should == ["finalized\n", "finalized\n"]
+ end
+ end
end
diff --git a/spec/ruby/core/class/attached_object_spec.rb b/spec/ruby/core/class/attached_object_spec.rb
index 115d5fa563..f1c0f63a44 100644
--- a/spec/ruby/core/class/attached_object_spec.rb
+++ b/spec/ruby/core/class/attached_object_spec.rb
@@ -19,13 +19,13 @@ ruby_version_is '3.2' do
it "raises TypeError if the class is not a singleton class" do
a = Class.new
- -> { a.attached_object }.should raise_error(TypeError)
+ -> { a.attached_object }.should raise_error(TypeError, /is not a singleton class/)
end
it "raises TypeError for special singleton classes" do
- -> { nil.singleton_class.attached_object }.should raise_error(TypeError)
- -> { true.singleton_class.attached_object }.should raise_error(TypeError)
- -> { false.singleton_class.attached_object }.should raise_error(TypeError)
+ -> { nil.singleton_class.attached_object }.should raise_error(TypeError, /[`']NilClass' is not a singleton class/)
+ -> { true.singleton_class.attached_object }.should raise_error(TypeError, /[`']TrueClass' is not a singleton class/)
+ -> { false.singleton_class.attached_object }.should raise_error(TypeError, /[`']FalseClass' is not a singleton class/)
end
end
end
diff --git a/spec/ruby/core/class/dup_spec.rb b/spec/ruby/core/class/dup_spec.rb
index 701fd72e19..c09ed71b31 100644
--- a/spec/ruby/core/class/dup_spec.rb
+++ b/spec/ruby/core/class/dup_spec.rb
@@ -61,4 +61,7 @@ describe "Class#dup" do
CoreClassSpecs::RecordCopy.name.should == "CoreClassSpecs::RecordCopy"
end
+ it "raises TypeError if called on BasicObject" do
+ -> { BasicObject.dup }.should raise_error(TypeError, "can't copy the root class")
+ end
end
diff --git a/spec/ruby/core/class/subclasses_spec.rb b/spec/ruby/core/class/subclasses_spec.rb
index a16b934d4f..50eb5358d9 100644
--- a/spec/ruby/core/class/subclasses_spec.rb
+++ b/spec/ruby/core/class/subclasses_spec.rb
@@ -7,7 +7,7 @@ ruby_version_is '3.1' do
assert_subclasses(ModuleSpecs::Parent, [ModuleSpecs::Child, ModuleSpecs::Child2])
end
- it "does not return included modules" do
+ it "does not return included modules from the parent" do
parent = Class.new
child = Class.new(parent)
mod = Module.new
@@ -16,6 +16,33 @@ ruby_version_is '3.1' do
assert_subclasses(parent, [child])
end
+ it "does not return included modules from the child" do
+ parent = Class.new
+ child = Class.new(parent)
+ mod = Module.new
+ parent.include(mod)
+
+ assert_subclasses(parent, [child])
+ end
+
+ it "does not return prepended modules from the parent" do
+ parent = Class.new
+ child = Class.new(parent)
+ mod = Module.new
+ parent.prepend(mod)
+
+ assert_subclasses(parent, [child])
+ end
+
+ it "does not return prepended modules from the child" do
+ parent = Class.new
+ child = Class.new(parent)
+ mod = Module.new
+ child.prepend(mod)
+
+ assert_subclasses(parent, [child])
+ end
+
it "does not return singleton classes" do
a = Class.new
diff --git a/spec/ruby/core/complex/inspect_spec.rb b/spec/ruby/core/complex/inspect_spec.rb
index 7a89ec6854..045be94b22 100644
--- a/spec/ruby/core/complex/inspect_spec.rb
+++ b/spec/ruby/core/complex/inspect_spec.rb
@@ -17,7 +17,8 @@ describe "Complex#inspect" do
it "calls #inspect on real and imaginary" do
real = NumericSpecs::Subclass.new
- real.should_receive(:inspect).and_return("1")
+ # + because of https://bugs.ruby-lang.org/issues/20337
+ real.should_receive(:inspect).and_return(+"1")
imaginary = NumericSpecs::Subclass.new
imaginary.should_receive(:inspect).and_return("2")
imaginary.should_receive(:<).any_number_of_times.and_return(false)
@@ -26,7 +27,8 @@ describe "Complex#inspect" do
it "adds an `*' before the `i' if the last character of the imaginary part is not numeric" do
real = NumericSpecs::Subclass.new
- real.should_receive(:inspect).and_return("(1)")
+ # + because of https://bugs.ruby-lang.org/issues/20337
+ real.should_receive(:inspect).and_return(+"(1)")
imaginary = NumericSpecs::Subclass.new
imaginary.should_receive(:inspect).and_return("(2)")
imaginary.should_receive(:<).any_number_of_times.and_return(false)
diff --git a/spec/ruby/core/complex/to_r_spec.rb b/spec/ruby/core/complex/to_r_spec.rb
index 4559921492..788027a500 100644
--- a/spec/ruby/core/complex/to_r_spec.rb
+++ b/spec/ruby/core/complex/to_r_spec.rb
@@ -34,8 +34,16 @@ describe "Complex#to_r" do
end
describe "when the imaginary part is Float 0.0" do
- it "raises RangeError" do
- -> { Complex(0, 0.0).to_r }.should raise_error(RangeError)
+ ruby_version_is ''...'3.4' do
+ it "raises RangeError" do
+ -> { Complex(0, 0.0).to_r }.should raise_error(RangeError)
+ end
+ end
+
+ ruby_version_is '3.4' do
+ it "returns a Rational" do
+ Complex(0, 0.0).to_r.should == 0r
+ end
end
end
end
diff --git a/spec/ruby/core/complex/to_s_spec.rb b/spec/ruby/core/complex/to_s_spec.rb
index 7677dcd0b5..ceccffe470 100644
--- a/spec/ruby/core/complex/to_s_spec.rb
+++ b/spec/ruby/core/complex/to_s_spec.rb
@@ -45,7 +45,8 @@ describe "Complex#to_s" do
it "treats real and imaginary parts as strings" do
real = NumericSpecs::Subclass.new
- real.should_receive(:to_s).and_return("1")
+ # + because of https://bugs.ruby-lang.org/issues/20337
+ real.should_receive(:to_s).and_return(+"1")
imaginary = NumericSpecs::Subclass.new
imaginary.should_receive(:to_s).and_return("2")
imaginary.should_receive(:<).any_number_of_times.and_return(false)
diff --git a/spec/ruby/core/conditionvariable/broadcast_spec.rb b/spec/ruby/core/conditionvariable/broadcast_spec.rb
index d88159df23..55a7b89c72 100644
--- a/spec/ruby/core/conditionvariable/broadcast_spec.rb
+++ b/spec/ruby/core/conditionvariable/broadcast_spec.rb
@@ -1,5 +1,4 @@
require_relative '../../spec_helper'
-require 'thread'
describe "ConditionVariable#broadcast" do
it "releases all threads waiting in line for this resource" do
diff --git a/spec/ruby/core/conditionvariable/marshal_dump_spec.rb b/spec/ruby/core/conditionvariable/marshal_dump_spec.rb
index f951a13e28..88b1cc38c1 100644
--- a/spec/ruby/core/conditionvariable/marshal_dump_spec.rb
+++ b/spec/ruby/core/conditionvariable/marshal_dump_spec.rb
@@ -1,5 +1,4 @@
require_relative '../../spec_helper'
-require 'thread'
describe "ConditionVariable#marshal_dump" do
it "raises a TypeError" do
diff --git a/spec/ruby/core/conditionvariable/signal_spec.rb b/spec/ruby/core/conditionvariable/signal_spec.rb
index 86383073f1..43a9cc611b 100644
--- a/spec/ruby/core/conditionvariable/signal_spec.rb
+++ b/spec/ruby/core/conditionvariable/signal_spec.rb
@@ -1,5 +1,4 @@
require_relative '../../spec_helper'
-require 'thread'
describe "ConditionVariable#signal" do
it "releases the first thread waiting in line for this resource" do
diff --git a/spec/ruby/core/conditionvariable/wait_spec.rb b/spec/ruby/core/conditionvariable/wait_spec.rb
index 9a68c2b5a1..fe73e513c0 100644
--- a/spec/ruby/core/conditionvariable/wait_spec.rb
+++ b/spec/ruby/core/conditionvariable/wait_spec.rb
@@ -1,5 +1,4 @@
require_relative '../../spec_helper'
-require 'thread'
describe "ConditionVariable#wait" do
it "calls #sleep on the given object" do
diff --git a/spec/ruby/core/data/constants_spec.rb b/spec/ruby/core/data/constants_spec.rb
index d9d55b50f9..2eb43d501e 100644
--- a/spec/ruby/core/data/constants_spec.rb
+++ b/spec/ruby/core/data/constants_spec.rb
@@ -1,20 +1,6 @@
require_relative '../../spec_helper'
-ruby_version_is ''...'3.0' do
- describe "Data" do
- it "is a subclass of Object" do
- suppress_warning do
- Data.superclass.should == Object
- end
- end
-
- it "is deprecated" do
- -> { Data }.should complain(/constant ::Data is deprecated/)
- end
- end
-end
-
-ruby_version_is '3.0'...'3.2' do
+ruby_version_is ''...'3.2' do
describe "Data" do
it "does not exist anymore" do
Object.should_not have_constant(:Data)
diff --git a/spec/ruby/core/data/fixtures/classes.rb b/spec/ruby/core/data/fixtures/classes.rb
index d1e10e02ed..46a6b48bb2 100644
--- a/spec/ruby/core/data/fixtures/classes.rb
+++ b/spec/ruby/core/data/fixtures/classes.rb
@@ -1,5 +1,5 @@
module DataSpecs
- ruby_version_is "3.2" do
+ guard -> { ruby_version_is "3.2" and Data.respond_to?(:define) } do
Measure = Data.define(:amount, :unit)
end
end
diff --git a/spec/ruby/core/data/initialize_spec.rb b/spec/ruby/core/data/initialize_spec.rb
index 94470cd108..2c36bd3ac4 100644
--- a/spec/ruby/core/data/initialize_spec.rb
+++ b/spec/ruby/core/data/initialize_spec.rb
@@ -31,6 +31,13 @@ ruby_version_is "3.2" do
data.unit.should == "km"
end
+ it "accepts String keyword arguments" do
+ data = DataSpecs::Measure.new("amount" => 42, "unit" => "km")
+
+ data.amount.should == 42
+ data.unit.should == "km"
+ end
+
it "raises ArgumentError if no arguments are given" do
-> {
DataSpecs::Measure.new
diff --git a/spec/ruby/core/data/with_spec.rb b/spec/ruby/core/data/with_spec.rb
new file mode 100644
index 0000000000..97e34c951f
--- /dev/null
+++ b/spec/ruby/core/data/with_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is "3.2" do
+ describe "Data#with" do
+ it "returns self if given no arguments" do
+ data = DataSpecs::Measure.new(amount: 42, unit: "km")
+ data = data.with.should.equal?(data)
+ end
+
+ it "accepts keyword arguments" do
+ data = DataSpecs::Measure.new(amount: 42, unit: "km")
+ data = data.with(amount: 4, unit: "m")
+
+ data.amount.should == 4
+ data.unit.should == "m"
+ end
+
+ it "accepts String keyword arguments" do
+ data = DataSpecs::Measure.new(amount: 42, unit: "km")
+ data = data.with("amount" => 4, "unit" => "m")
+
+ data.amount.should == 4
+ data.unit.should == "m"
+ end
+
+ it "raises ArgumentError if no keyword arguments are given" do
+ data = DataSpecs::Measure.new(amount: 42, unit: "km")
+
+ -> {
+ data.with(4, "m")
+ }.should raise_error(ArgumentError, "wrong number of arguments (given 2, expected 0)")
+ end
+ end
+end
diff --git a/spec/ruby/core/dir/children_spec.rb b/spec/ruby/core/dir/children_spec.rb
index 03698cc246..0ad3df4669 100644
--- a/spec/ruby/core/dir/children_spec.rb
+++ b/spec/ruby/core/dir/children_spec.rb
@@ -47,7 +47,7 @@ describe "Dir.children" do
encoding = Encoding.find("filesystem")
encoding = Encoding::BINARY if encoding == Encoding::US_ASCII
platform_is_not :windows do
- children.should include("ã“ã‚“ã«ã¡ã¯.txt".force_encoding(encoding))
+ children.should include("ã“ã‚“ã«ã¡ã¯.txt".dup.force_encoding(encoding))
end
children.first.encoding.should equal(Encoding.find("filesystem"))
end
@@ -113,7 +113,7 @@ describe "Dir#children" do
encoding = Encoding.find("filesystem")
encoding = Encoding::BINARY if encoding == Encoding::US_ASCII
platform_is_not :windows do
- children.should include("ã“ã‚“ã«ã¡ã¯.txt".force_encoding(encoding))
+ children.should include("ã“ã‚“ã«ã¡ã¯.txt".dup.force_encoding(encoding))
end
children.first.encoding.should equal(Encoding.find("filesystem"))
end
@@ -131,4 +131,17 @@ describe "Dir#children" do
children = @dir.children.sort
children.first.encoding.should equal(Encoding::EUC_KR)
end
+
+ it "returns the same result when called repeatedly" do
+ @dir = Dir.open DirSpecs.mock_dir
+
+ a = []
+ @dir.each {|dir| a << dir}
+
+ b = []
+ @dir.each {|dir| b << dir}
+
+ a.sort.should == b.sort
+ a.sort.should == DirSpecs.expected_paths
+ end
end
diff --git a/spec/ruby/core/dir/each_child_spec.rb b/spec/ruby/core/dir/each_child_spec.rb
index 520186e79e..7194273b95 100644
--- a/spec/ruby/core/dir/each_child_spec.rb
+++ b/spec/ruby/core/dir/each_child_spec.rb
@@ -86,6 +86,19 @@ describe "Dir#each_child" do
@dir.each_child { |f| f }.should == @dir
end
+ it "returns the same result when called repeatedly" do
+ @dir = Dir.open DirSpecs.mock_dir
+
+ a = []
+ @dir.each {|dir| a << dir}
+
+ b = []
+ @dir.each {|dir| b << dir}
+
+ a.sort.should == b.sort
+ a.sort.should == DirSpecs.expected_paths
+ end
+
describe "when no block is given" do
it "returns an Enumerator" do
@dir = Dir.new(DirSpecs.mock_dir)
diff --git a/spec/ruby/core/dir/each_spec.rb b/spec/ruby/core/dir/each_spec.rb
index 8c69a7212b..7674663d82 100644
--- a/spec/ruby/core/dir/each_spec.rb
+++ b/spec/ruby/core/dir/each_spec.rb
@@ -35,6 +35,17 @@ describe "Dir#each" do
ls.should include(@dir.read)
end
+ it "returns the same result when called repeatedly" do
+ a = []
+ @dir.each {|dir| a << dir}
+
+ b = []
+ @dir.each {|dir| b << dir}
+
+ a.sort.should == b.sort
+ a.sort.should == DirSpecs.expected_paths
+ end
+
describe "when no block is given" do
it "returns an Enumerator" do
@dir.each.should be_an_instance_of(Enumerator)
diff --git a/spec/ruby/core/dir/entries_spec.rb b/spec/ruby/core/dir/entries_spec.rb
index 91c30fccae..7462542acf 100644
--- a/spec/ruby/core/dir/entries_spec.rb
+++ b/spec/ruby/core/dir/entries_spec.rb
@@ -47,7 +47,7 @@ describe "Dir.entries" do
encoding = Encoding.find("filesystem")
encoding = Encoding::BINARY if encoding == Encoding::US_ASCII
platform_is_not :windows do
- entries.should include("ã“ã‚“ã«ã¡ã¯.txt".force_encoding(encoding))
+ entries.should include("ã“ã‚“ã«ã¡ã¯.txt".dup.force_encoding(encoding))
end
entries.first.encoding.should equal(Encoding.find("filesystem"))
end
diff --git a/spec/ruby/core/dir/exist_spec.rb b/spec/ruby/core/dir/exist_spec.rb
index 43987b0f32..9023de533f 100644
--- a/spec/ruby/core/dir/exist_spec.rb
+++ b/spec/ruby/core/dir/exist_spec.rb
@@ -13,3 +13,11 @@ describe "Dir.exist?" do
it_behaves_like :dir_exist, :exist?
end
+
+ruby_version_is "3.2" do
+ describe "Dir.exists?" do
+ it "has been removed" do
+ Dir.should_not.respond_to?(:exists?)
+ end
+ end
+end
diff --git a/spec/ruby/core/dir/glob_spec.rb b/spec/ruby/core/dir/glob_spec.rb
index 72d6337e15..32f515c81d 100644
--- a/spec/ruby/core/dir/glob_spec.rb
+++ b/spec/ruby/core/dir/glob_spec.rb
@@ -106,11 +106,11 @@ describe "Dir.glob" do
ruby_version_is '3.1' do
it "recursively matches files and directories in nested dot subdirectory except . with 'nested/**/*' from the current directory and option File::FNM_DOTMATCH" do
expected = %w[
- nested/.
- nested/.dotsubir
- nested/.dotsubir/.dotfile
- nested/.dotsubir/nondotfile
- ]
+ nested/.
+ nested/.dotsubir
+ nested/.dotsubir/.dotfile
+ nested/.dotsubir/nondotfile
+ ]
Dir.glob('nested/**/*', File::FNM_DOTMATCH).sort.should == expected.sort
end
@@ -260,7 +260,7 @@ describe "Dir.glob" do
Dir.glob('**/.*', base: "deeply/nested").sort.should == expected
end
- # 2.7 and 3.0 include a "." entry for every dir: ["directory/.", "directory/structure/.", ...]
+ # < 3.1 include a "." entry for every dir: ["directory/.", "directory/structure/.", ...]
ruby_version_is '3.1' do
it "handles **/.* with base keyword argument and FNM_DOTMATCH" do
expected = %w[
diff --git a/spec/ruby/core/dir/home_spec.rb b/spec/ruby/core/dir/home_spec.rb
index 90a008faf1..3cf745ab46 100644
--- a/spec/ruby/core/dir/home_spec.rb
+++ b/spec/ruby/core/dir/home_spec.rb
@@ -40,22 +40,21 @@ describe "Dir.home" do
home.should == "C:/rubyspäc/home"
home.encoding.should == Encoding::UTF_8
end
- end
- it "retrieves the directory from HOME, USERPROFILE, HOMEDRIVE/HOMEPATH and the WinAPI in that order" do
- old_dirs = [ENV.delete('HOME'), ENV.delete('USERPROFILE'), ENV.delete('HOMEDRIVE'), ENV.delete('HOMEPATH')]
+ it "retrieves the directory from HOME, USERPROFILE, HOMEDRIVE/HOMEPATH and the WinAPI in that order" do
+ old_dirs = [ENV.delete('HOME'), ENV.delete('USERPROFILE'), ENV.delete('HOMEDRIVE'), ENV.delete('HOMEPATH')]
- Dir.home.should == old_dirs[1].gsub("\\", "/")
- ENV['HOMEDRIVE'] = "C:"
- ENV['HOMEPATH'] = "\\rubyspec\\home1"
- Dir.home.should == "C:/rubyspec/home1"
- ENV['USERPROFILE'] = "C:\\rubyspec\\home2"
- # https://bugs.ruby-lang.org/issues/19244
- # Dir.home.should == "C:/rubyspec/home2"
- ENV['HOME'] = "C:\\rubyspec\\home3"
- Dir.home.should == "C:/rubyspec/home3"
- ensure
- ENV['HOME'], ENV['USERPROFILE'], ENV['HOMEDRIVE'], ENV['HOMEPATH'] = *old_dirs
+ Dir.home.should == old_dirs[1].gsub("\\", "/")
+ ENV['HOMEDRIVE'] = "C:"
+ ENV['HOMEPATH'] = "\\rubyspec\\home1"
+ Dir.home.should == "C:/rubyspec/home1"
+ ENV['USERPROFILE'] = "C:\\rubyspec\\home2"
+ Dir.home.should == "C:/rubyspec/home2"
+ ENV['HOME'] = "C:\\rubyspec\\home3"
+ Dir.home.should == "C:/rubyspec/home3"
+ ensure
+ ENV['HOME'], ENV['USERPROFILE'], ENV['HOMEDRIVE'], ENV['HOMEPATH'] = *old_dirs
+ end
end
end
end
diff --git a/spec/ruby/core/dir/shared/chroot.rb b/spec/ruby/core/dir/shared/chroot.rb
index 8c0599fe3f..a8f7c10a19 100644
--- a/spec/ruby/core/dir/shared/chroot.rb
+++ b/spec/ruby/core/dir/shared/chroot.rb
@@ -2,7 +2,7 @@ describe :dir_chroot_as_root, shared: true do
before :all do
DirSpecs.create_mock_dirs
- @real_root = "../" * (File.dirname(__FILE__).count('/') - 1)
+ @real_root = "../" * (__dir__.count('/') - 1)
@ref_dir = File.join("/", File.basename(Dir["/*"].first))
end
@@ -18,7 +18,7 @@ describe :dir_chroot_as_root, shared: true do
compilations_ci = ENV["GITHUB_WORKFLOW"] == "Compilations"
it "can be used to change the process' root directory" do
- -> { Dir.send(@method, File.dirname(__FILE__)) }.should_not raise_error
+ -> { Dir.send(@method, __dir__) }.should_not raise_error
File.should.exist?("/#{File.basename(__FILE__)}")
end unless compilations_ci
diff --git a/spec/ruby/core/dir/shared/exist.rb b/spec/ruby/core/dir/shared/exist.rb
index 765d1b656c..2ea4f88a80 100644
--- a/spec/ruby/core/dir/shared/exist.rb
+++ b/spec/ruby/core/dir/shared/exist.rb
@@ -1,6 +1,6 @@
describe :dir_exist, shared: true do
it "returns true if the given directory exists" do
- Dir.send(@method, File.dirname(__FILE__)).should be_true
+ Dir.send(@method, __dir__).should be_true
end
it "returns true for '.'" do
@@ -20,7 +20,7 @@ describe :dir_exist, shared: true do
end
it "understands relative paths" do
- Dir.send(@method, File.dirname(__FILE__) + '/../').should be_true
+ Dir.send(@method, __dir__ + '/../').should be_true
end
it "returns false if the given directory doesn't exist" do
@@ -28,7 +28,7 @@ describe :dir_exist, shared: true do
end
it "doesn't require the name to have a trailing slash" do
- dir = File.dirname(__FILE__)
+ dir = __dir__
dir.sub!(/\/$/,'')
Dir.send(@method, dir).should be_true
end
@@ -50,7 +50,7 @@ describe :dir_exist, shared: true do
it "calls #to_path on non String arguments" do
p = mock('path')
- p.should_receive(:to_path).and_return(File.dirname(__FILE__))
+ p.should_receive(:to_path).and_return(__dir__)
Dir.send(@method, p)
end
end
diff --git a/spec/ruby/core/dir/shared/glob.rb b/spec/ruby/core/dir/shared/glob.rb
index 33b2828c27..745f02d46b 100644
--- a/spec/ruby/core/dir/shared/glob.rb
+++ b/spec/ruby/core/dir/shared/glob.rb
@@ -12,7 +12,7 @@ describe :dir_glob, shared: true do
end
it "raises an Encoding::CompatibilityError if the argument encoding is not compatible with US-ASCII" do
- pattern = "file*".force_encoding Encoding::UTF_16BE
+ pattern = "file*".dup.force_encoding Encoding::UTF_16BE
-> { Dir.send(@method, pattern) }.should raise_error(Encoding::CompatibilityError)
end
@@ -27,24 +27,22 @@ describe :dir_glob, shared: true do
-> {Dir.send(@method, "file_o*\0file_t*")}.should raise_error ArgumentError, /nul-separated/
end
- ruby_version_is "3.0" do
- it "result is sorted by default" do
- result = Dir.send(@method, '*')
- result.should == result.sort
- end
+ it "result is sorted by default" do
+ result = Dir.send(@method, '*')
+ result.should == result.sort
+ end
- it "result is sorted with sort: true" do
- result = Dir.send(@method, '*', sort: true)
- result.should == result.sort
- end
+ it "result is sorted with sort: true" do
+ result = Dir.send(@method, '*', sort: true)
+ result.should == result.sort
+ end
- it "sort: false returns same files" do
- result = Dir.send(@method,'*', sort: false)
- result.sort.should == Dir.send(@method, '*').sort
- end
+ it "sort: false returns same files" do
+ result = Dir.send(@method,'*', sort: false)
+ result.sort.should == Dir.send(@method, '*').sort
end
- ruby_version_is "3.0"..."3.1" do
+ ruby_version_is ""..."3.1" do
it "result is sorted with any non false value of sort:" do
result = Dir.send(@method, '*', sort: 0)
result.should == result.sort
diff --git a/spec/ruby/core/encoding/compatible_spec.rb b/spec/ruby/core/encoding/compatible_spec.rb
index 80ecab6155..f18d8680a9 100644
--- a/spec/ruby/core/encoding/compatible_spec.rb
+++ b/spec/ruby/core/encoding/compatible_spec.rb
@@ -7,7 +7,7 @@ require_relative '../../spec_helper'
describe "Encoding.compatible? String, String" do
describe "when the first's Encoding is valid US-ASCII" do
before :each do
- @str = "abc".force_encoding Encoding::US_ASCII
+ @str = "abc".dup.force_encoding Encoding::US_ASCII
end
it "returns US-ASCII when the second's is US-ASCII" do
@@ -33,28 +33,28 @@ describe "Encoding.compatible? String, String" do
describe "when the first's Encoding is ASCII compatible and ASCII only" do
it "returns the first's Encoding if the second is ASCII compatible and ASCII only" do
- [ [Encoding, "abc".force_encoding("UTF-8"), "123".force_encoding("Shift_JIS"), Encoding::UTF_8],
- [Encoding, "123".force_encoding("Shift_JIS"), "abc".force_encoding("UTF-8"), Encoding::Shift_JIS]
+ [ [Encoding, "abc".dup.force_encoding("UTF-8"), "123".dup.force_encoding("Shift_JIS"), Encoding::UTF_8],
+ [Encoding, "123".dup.force_encoding("Shift_JIS"), "abc".dup.force_encoding("UTF-8"), Encoding::Shift_JIS]
].should be_computed_by(:compatible?)
end
it "returns the first's Encoding if the second is ASCII compatible and ASCII only" do
- [ [Encoding, "abc".force_encoding("BINARY"), "123".force_encoding("US-ASCII"), Encoding::BINARY],
- [Encoding, "123".force_encoding("US-ASCII"), "abc".force_encoding("BINARY"), Encoding::US_ASCII]
+ [ [Encoding, "abc".dup.force_encoding("BINARY"), "123".dup.force_encoding("US-ASCII"), Encoding::BINARY],
+ [Encoding, "123".dup.force_encoding("US-ASCII"), "abc".dup.force_encoding("BINARY"), Encoding::US_ASCII]
].should be_computed_by(:compatible?)
end
it "returns the second's Encoding if the second is ASCII compatible but not ASCII only" do
- [ [Encoding, "abc".force_encoding("UTF-8"), "\xff".force_encoding("Shift_JIS"), Encoding::Shift_JIS],
- [Encoding, "123".force_encoding("Shift_JIS"), "\xff".force_encoding("UTF-8"), Encoding::UTF_8],
- [Encoding, "abc".force_encoding("BINARY"), "\xff".force_encoding("US-ASCII"), Encoding::US_ASCII],
- [Encoding, "123".force_encoding("US-ASCII"), "\xff".force_encoding("BINARY"), Encoding::BINARY],
+ [ [Encoding, "abc".dup.force_encoding("UTF-8"), "\xff".dup.force_encoding("Shift_JIS"), Encoding::Shift_JIS],
+ [Encoding, "123".dup.force_encoding("Shift_JIS"), "\xff".dup.force_encoding("UTF-8"), Encoding::UTF_8],
+ [Encoding, "abc".dup.force_encoding("BINARY"), "\xff".dup.force_encoding("US-ASCII"), Encoding::US_ASCII],
+ [Encoding, "123".dup.force_encoding("US-ASCII"), "\xff".dup.force_encoding("BINARY"), Encoding::BINARY],
].should be_computed_by(:compatible?)
end
it "returns nil if the second's Encoding is not ASCII compatible" do
- a = "abc".force_encoding("UTF-8")
- b = "1234".force_encoding("UTF-16LE")
+ a = "abc".dup.force_encoding("UTF-8")
+ b = "1234".dup.force_encoding("UTF-16LE")
Encoding.compatible?(a, b).should be_nil
end
end
@@ -75,7 +75,7 @@ describe "Encoding.compatible? String, String" do
describe "when the first's Encoding is not ASCII compatible" do
before :each do
- @str = "abc".force_encoding Encoding::UTF_7
+ @str = "abc".dup.force_encoding Encoding::UTF_7
end
it "returns nil when the second String is US-ASCII" do
@@ -91,14 +91,14 @@ describe "Encoding.compatible? String, String" do
end
it "returns the Encoding when the second's Encoding is not ASCII compatible but the same as the first's Encoding" do
- encoding = Encoding.compatible?(@str, "def".force_encoding("utf-7"))
+ encoding = Encoding.compatible?(@str, "def".dup.force_encoding("utf-7"))
encoding.should == Encoding::UTF_7
end
end
describe "when the first's Encoding is invalid" do
before :each do
- @str = "\xff".force_encoding Encoding::UTF_8
+ @str = "\xff".dup.force_encoding Encoding::UTF_8
end
it "returns the first's Encoding when the second's Encoding is US-ASCII" do
@@ -114,11 +114,11 @@ describe "Encoding.compatible? String, String" do
end
it "returns nil when the second's Encoding is invalid and ASCII only" do
- Encoding.compatible?(@str, "\x7f".force_encoding("utf-16be")).should be_nil
+ Encoding.compatible?(@str, "\x7f".dup.force_encoding("utf-16be")).should be_nil
end
it "returns nil when the second's Encoding is invalid and not ASCII only" do
- Encoding.compatible?(@str, "\xff".force_encoding("utf-16be")).should be_nil
+ Encoding.compatible?(@str, "\xff".dup.force_encoding("utf-16be")).should be_nil
end
it "returns the Encoding when the second's Encoding is invalid but the same as the first" do
@@ -129,7 +129,7 @@ describe "Encoding.compatible? String, String" do
describe "when the first String is empty and the second is not" do
describe "and the first's Encoding is ASCII compatible" do
before :each do
- @str = "".force_encoding("utf-8")
+ @str = "".dup.force_encoding("utf-8")
end
it "returns the first's encoding when the second String is ASCII only" do
@@ -143,7 +143,7 @@ describe "Encoding.compatible? String, String" do
describe "when the first's Encoding is not ASCII compatible" do
before :each do
- @str = "".force_encoding Encoding::UTF_7
+ @str = "".dup.force_encoding Encoding::UTF_7
end
it "returns the second string's encoding" do
@@ -154,7 +154,7 @@ describe "Encoding.compatible? String, String" do
describe "when the second String is empty" do
before :each do
- @str = "abc".force_encoding("utf-7")
+ @str = "abc".dup.force_encoding("utf-7")
end
it "returns the first Encoding" do
@@ -165,7 +165,7 @@ end
describe "Encoding.compatible? String, Regexp" do
it "returns US-ASCII if both are US-ASCII" do
- str = "abc".force_encoding("us-ascii")
+ str = "abc".dup.force_encoding("us-ascii")
Encoding.compatible?(str, /abc/).should == Encoding::US_ASCII
end
@@ -180,15 +180,15 @@ describe "Encoding.compatible? String, Regexp" do
it "returns the String's Encoding if the String is not ASCII only" do
[ [Encoding, "\xff", Encoding::BINARY],
[Encoding, "\u3042".encode("utf-8"), Encoding::UTF_8],
- [Encoding, "\xa4\xa2".force_encoding("euc-jp"), Encoding::EUC_JP],
- [Encoding, "\x82\xa0".force_encoding("shift_jis"), Encoding::Shift_JIS],
+ [Encoding, "\xa4\xa2".dup.force_encoding("euc-jp"), Encoding::EUC_JP],
+ [Encoding, "\x82\xa0".dup.force_encoding("shift_jis"), Encoding::Shift_JIS],
].should be_computed_by(:compatible?, /abc/)
end
end
describe "Encoding.compatible? String, Symbol" do
it "returns US-ASCII if both are ASCII only" do
- str = "abc".force_encoding("us-ascii")
+ str = "abc".dup.force_encoding("us-ascii")
Encoding.compatible?(str, :abc).should == Encoding::US_ASCII
end
@@ -203,8 +203,8 @@ describe "Encoding.compatible? String, Symbol" do
it "returns the String's Encoding if the String is not ASCII only" do
[ [Encoding, "\xff", Encoding::BINARY],
[Encoding, "\u3042".encode("utf-8"), Encoding::UTF_8],
- [Encoding, "\xa4\xa2".force_encoding("euc-jp"), Encoding::EUC_JP],
- [Encoding, "\x82\xa0".force_encoding("shift_jis"), Encoding::Shift_JIS],
+ [Encoding, "\xa4\xa2".dup.force_encoding("euc-jp"), Encoding::EUC_JP],
+ [Encoding, "\x82\xa0".dup.force_encoding("shift_jis"), Encoding::Shift_JIS],
].should be_computed_by(:compatible?, :abc)
end
end
@@ -221,8 +221,8 @@ describe "Encoding.compatible? String, Encoding" do
it "returns the String's encoding if the Encoding is US-ASCII" do
[ [Encoding, "\xff", Encoding::BINARY],
[Encoding, "\u3042".encode("utf-8"), Encoding::UTF_8],
- [Encoding, "\xa4\xa2".force_encoding("euc-jp"), Encoding::EUC_JP],
- [Encoding, "\x82\xa0".force_encoding("shift_jis"), Encoding::Shift_JIS],
+ [Encoding, "\xa4\xa2".dup.force_encoding("euc-jp"), Encoding::EUC_JP],
+ [Encoding, "\x82\xa0".dup.force_encoding("shift_jis"), Encoding::Shift_JIS],
].should be_computed_by(:compatible?, Encoding::US_ASCII)
end
@@ -242,7 +242,7 @@ end
describe "Encoding.compatible? Regexp, String" do
it "returns US-ASCII if both are US-ASCII" do
- str = "abc".force_encoding("us-ascii")
+ str = "abc".dup.force_encoding("us-ascii")
Encoding.compatible?(/abc/, str).should == Encoding::US_ASCII
end
@@ -256,8 +256,8 @@ describe "Encoding.compatible? Regexp, Regexp" do
it "returns the first's Encoding if it is not US-ASCII and not ASCII only" do
[ [Encoding, Regexp.new("\xff"), Encoding::BINARY],
[Encoding, Regexp.new("\u3042".encode("utf-8")), Encoding::UTF_8],
- [Encoding, Regexp.new("\xa4\xa2".force_encoding("euc-jp")), Encoding::EUC_JP],
- [Encoding, Regexp.new("\x82\xa0".force_encoding("shift_jis")), Encoding::Shift_JIS],
+ [Encoding, Regexp.new("\xa4\xa2".dup.force_encoding("euc-jp")), Encoding::EUC_JP],
+ [Encoding, Regexp.new("\x82\xa0".dup.force_encoding("shift_jis")), Encoding::Shift_JIS],
].should be_computed_by(:compatible?, /abc/)
end
end
@@ -270,15 +270,15 @@ describe "Encoding.compatible? Regexp, Symbol" do
it "returns the first's Encoding if it is not US-ASCII and not ASCII only" do
[ [Encoding, Regexp.new("\xff"), Encoding::BINARY],
[Encoding, Regexp.new("\u3042".encode("utf-8")), Encoding::UTF_8],
- [Encoding, Regexp.new("\xa4\xa2".force_encoding("euc-jp")), Encoding::EUC_JP],
- [Encoding, Regexp.new("\x82\xa0".force_encoding("shift_jis")), Encoding::Shift_JIS],
+ [Encoding, Regexp.new("\xa4\xa2".dup.force_encoding("euc-jp")), Encoding::EUC_JP],
+ [Encoding, Regexp.new("\x82\xa0".dup.force_encoding("shift_jis")), Encoding::Shift_JIS],
].should be_computed_by(:compatible?, /abc/)
end
end
describe "Encoding.compatible? Symbol, String" do
it "returns US-ASCII if both are ASCII only" do
- str = "abc".force_encoding("us-ascii")
+ str = "abc".dup.force_encoding("us-ascii")
Encoding.compatible?(str, :abc).should == Encoding::US_ASCII
end
end
@@ -291,8 +291,8 @@ describe "Encoding.compatible? Symbol, Regexp" do
it "returns the Regexp's Encoding if it is not US-ASCII and not ASCII only" do
a = Regexp.new("\xff")
b = Regexp.new("\u3042".encode("utf-8"))
- c = Regexp.new("\xa4\xa2".force_encoding("euc-jp"))
- d = Regexp.new("\x82\xa0".force_encoding("shift_jis"))
+ c = Regexp.new("\xa4\xa2".dup.force_encoding("euc-jp"))
+ d = Regexp.new("\x82\xa0".dup.force_encoding("shift_jis"))
[ [Encoding, :abc, a, Encoding::BINARY],
[Encoding, :abc, b, Encoding::UTF_8],
@@ -310,8 +310,8 @@ describe "Encoding.compatible? Symbol, Symbol" do
it "returns the first's Encoding if it is not ASCII only" do
[ [Encoding, "\xff".to_sym, Encoding::BINARY],
[Encoding, "\u3042".encode("utf-8").to_sym, Encoding::UTF_8],
- [Encoding, "\xa4\xa2".force_encoding("euc-jp").to_sym, Encoding::EUC_JP],
- [Encoding, "\x82\xa0".force_encoding("shift_jis").to_sym, Encoding::Shift_JIS],
+ [Encoding, "\xa4\xa2".dup.force_encoding("euc-jp").to_sym, Encoding::EUC_JP],
+ [Encoding, "\x82\xa0".dup.force_encoding("shift_jis").to_sym, Encoding::Shift_JIS],
].should be_computed_by(:compatible?, :abc)
end
end
diff --git a/spec/ruby/core/encoding/converter/convert_spec.rb b/spec/ruby/core/encoding/converter/convert_spec.rb
index 95a9e0b758..7f249d90a3 100644
--- a/spec/ruby/core/encoding/converter/convert_spec.rb
+++ b/spec/ruby/core/encoding/converter/convert_spec.rb
@@ -1,4 +1,5 @@
# -*- encoding: binary -*-
+# frozen_string_literal: true
require_relative '../../../spec_helper'
describe "Encoding::Converter#convert" do
@@ -9,31 +10,31 @@ describe "Encoding::Converter#convert" do
it "sets the encoding of the result to the target encoding" do
ec = Encoding::Converter.new('ascii', 'utf-8')
- str = 'glark'.force_encoding('ascii')
+ str = 'glark'.dup.force_encoding('ascii')
ec.convert(str).encoding.should == Encoding::UTF_8
end
it "transcodes the given String to the target encoding" do
ec = Encoding::Converter.new("utf-8", "euc-jp")
- ec.convert("\u3042".force_encoding('UTF-8')).should == \
- "\xA4\xA2".force_encoding('EUC-JP')
+ ec.convert("\u3042".dup.force_encoding('UTF-8')).should == \
+ "\xA4\xA2".dup.force_encoding('EUC-JP')
end
it "allows Strings of different encodings to the source encoding" do
ec = Encoding::Converter.new('ascii', 'utf-8')
- str = 'glark'.force_encoding('SJIS')
+ str = 'glark'.dup.force_encoding('SJIS')
ec.convert(str).encoding.should == Encoding::UTF_8
end
it "reuses the given encoding pair if called multiple times" do
ec = Encoding::Converter.new('ascii', 'SJIS')
- ec.convert('a'.force_encoding('ASCII')).should == 'a'.force_encoding('SJIS')
- ec.convert('b'.force_encoding('ASCII')).should == 'b'.force_encoding('SJIS')
+ ec.convert('a'.dup.force_encoding('ASCII')).should == 'a'.dup.force_encoding('SJIS')
+ ec.convert('b'.dup.force_encoding('ASCII')).should == 'b'.dup.force_encoding('SJIS')
end
it "raises UndefinedConversionError if the String contains characters invalid for the target encoding" do
ec = Encoding::Converter.new('UTF-8', Encoding.find('macCyrillic'))
- -> { ec.convert("\u{6543}".force_encoding('UTF-8')) }.should \
+ -> { ec.convert("\u{6543}".dup.force_encoding('UTF-8')) }.should \
raise_error(Encoding::UndefinedConversionError)
end
diff --git a/spec/ruby/core/encoding/converter/finish_spec.rb b/spec/ruby/core/encoding/converter/finish_spec.rb
index 11ca7e8510..239243430b 100644
--- a/spec/ruby/core/encoding/converter/finish_spec.rb
+++ b/spec/ruby/core/encoding/converter/finish_spec.rb
@@ -16,8 +16,8 @@ describe "Encoding::Converter#finish" do
end
it "returns the last part of the converted String if it hasn't already" do
- @ec.convert("\u{9999}").should == "\e$B9a".force_encoding('iso-2022-jp')
- @ec.finish.should == "\e(B".force_encoding('iso-2022-jp')
+ @ec.convert("\u{9999}").should == "\e$B9a".dup.force_encoding('iso-2022-jp')
+ @ec.finish.should == "\e(B".dup.force_encoding('iso-2022-jp')
end
it "returns a String in the destination encoding" do
diff --git a/spec/ruby/core/encoding/converter/last_error_spec.rb b/spec/ruby/core/encoding/converter/last_error_spec.rb
index 68567737b7..78779be70b 100644
--- a/spec/ruby/core/encoding/converter/last_error_spec.rb
+++ b/spec/ruby/core/encoding/converter/last_error_spec.rb
@@ -9,45 +9,45 @@ describe "Encoding::Converter#last_error" do
it "returns nil when the last conversion did not produce an error" do
ec = Encoding::Converter.new('ascii','utf-8')
- ec.convert('a'.force_encoding('ascii'))
+ ec.convert('a'.dup.force_encoding('ascii'))
ec.last_error.should be_nil
end
it "returns nil when #primitive_convert last returned :destination_buffer_full" do
ec = Encoding::Converter.new("utf-8", "iso-2022-jp")
- ec.primitive_convert("\u{9999}", "", 0, 0, partial_input: false) \
+ ec.primitive_convert(+"\u{9999}", +"", 0, 0, partial_input: false) \
.should == :destination_buffer_full
ec.last_error.should be_nil
end
it "returns nil when #primitive_convert last returned :finished" do
ec = Encoding::Converter.new("utf-8", "iso-8859-1")
- ec.primitive_convert("glark".force_encoding('utf-8'),"").should == :finished
+ ec.primitive_convert("glark".dup.force_encoding('utf-8'), +"").should == :finished
ec.last_error.should be_nil
end
it "returns nil if the last conversion succeeded but the penultimate failed" do
ec = Encoding::Converter.new("utf-8", "iso-8859-1")
- ec.primitive_convert("\xf1abcd","").should == :invalid_byte_sequence
- ec.primitive_convert("glark".force_encoding('utf-8'),"").should == :finished
+ ec.primitive_convert(+"\xf1abcd", +"").should == :invalid_byte_sequence
+ ec.primitive_convert("glark".dup.force_encoding('utf-8'), +"").should == :finished
ec.last_error.should be_nil
end
it "returns an Encoding::InvalidByteSequenceError when #primitive_convert last returned :invalid_byte_sequence" do
ec = Encoding::Converter.new("utf-8", "iso-8859-1")
- ec.primitive_convert("\xf1abcd","").should == :invalid_byte_sequence
+ ec.primitive_convert(+"\xf1abcd", +"").should == :invalid_byte_sequence
ec.last_error.should be_an_instance_of(Encoding::InvalidByteSequenceError)
end
it "returns an Encoding::UndefinedConversionError when #primitive_convert last returned :undefined_conversion" do
ec = Encoding::Converter.new("utf-8", "iso-8859-1")
- ec.primitive_convert("\u{9876}","").should == :undefined_conversion
+ ec.primitive_convert(+"\u{9876}", +"").should == :undefined_conversion
ec.last_error.should be_an_instance_of(Encoding::UndefinedConversionError)
end
it "returns an Encoding::InvalidByteSequenceError when #primitive_convert last returned :incomplete_input" do
ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
- ec.primitive_convert("\xa4", "", nil, 10).should == :incomplete_input
+ ec.primitive_convert(+"\xa4", +"", nil, 10).should == :incomplete_input
ec.last_error.should be_an_instance_of(Encoding::InvalidByteSequenceError)
end
diff --git a/spec/ruby/core/encoding/converter/new_spec.rb b/spec/ruby/core/encoding/converter/new_spec.rb
index 1f7affc72b..db9c3364d7 100644
--- a/spec/ruby/core/encoding/converter/new_spec.rb
+++ b/spec/ruby/core/encoding/converter/new_spec.rb
@@ -107,7 +107,7 @@ describe "Encoding::Converter.new" do
it "sets the replacement String to '\\uFFFD'" do
conv = Encoding::Converter.new("us-ascii", "utf-8", replace: nil)
- conv.replacement.should == "\u{fffd}".force_encoding("utf-8")
+ conv.replacement.should == "\u{fffd}".dup.force_encoding("utf-8")
end
it "sets the replacement String encoding to UTF-8" do
diff --git a/spec/ruby/core/encoding/converter/primitive_convert_spec.rb b/spec/ruby/core/encoding/converter/primitive_convert_spec.rb
index 802d8e7cb1..63f25eddef 100644
--- a/spec/ruby/core/encoding/converter/primitive_convert_spec.rb
+++ b/spec/ruby/core/encoding/converter/primitive_convert_spec.rb
@@ -1,4 +1,5 @@
# -*- encoding: binary -*-
+# frozen_string_literal: false
require_relative '../../../spec_helper'
describe "Encoding::Converter#primitive_convert" do
@@ -14,6 +15,10 @@ describe "Encoding::Converter#primitive_convert" do
-> { @ec.primitive_convert("","") }.should_not raise_error
end
+ it "raises FrozenError when the destination buffer is a frozen String" do
+ -> { @ec.primitive_convert("", "".freeze) }.should raise_error(FrozenError)
+ end
+
it "accepts nil for the destination byte offset" do
-> { @ec.primitive_convert("","", nil) }.should_not raise_error
end
diff --git a/spec/ruby/core/encoding/converter/primitive_errinfo_spec.rb b/spec/ruby/core/encoding/converter/primitive_errinfo_spec.rb
index 1f836b259f..668eb9a924 100644
--- a/spec/ruby/core/encoding/converter/primitive_errinfo_spec.rb
+++ b/spec/ruby/core/encoding/converter/primitive_errinfo_spec.rb
@@ -1,4 +1,5 @@
# -*- encoding: binary -*-
+# frozen_string_literal: false
require_relative '../../../spec_helper'
describe "Encoding::Converter#primitive_errinfo" do
diff --git a/spec/ruby/core/encoding/converter/putback_spec.rb b/spec/ruby/core/encoding/converter/putback_spec.rb
index c4e0a5da21..e19fe6c314 100644
--- a/spec/ruby/core/encoding/converter/putback_spec.rb
+++ b/spec/ruby/core/encoding/converter/putback_spec.rb
@@ -4,7 +4,7 @@ require_relative '../../../spec_helper'
describe "Encoding::Converter#putback" do
before :each do
@ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
- @ret = @ec.primitive_convert(@src="abc\xa1def", @dst="", nil, 10)
+ @ret = @ec.primitive_convert(@src=+"abc\xa1def", @dst=+"", nil, 10)
end
it "returns a String" do
@@ -36,21 +36,21 @@ describe "Encoding::Converter#putback" do
it "returns the problematic bytes for UTF-16LE" do
ec = Encoding::Converter.new("utf-16le", "iso-8859-1")
- src = "\x00\xd8\x61\x00"
- dst = ""
+ src = +"\x00\xd8\x61\x00"
+ dst = +""
ec.primitive_convert(src, dst).should == :invalid_byte_sequence
ec.primitive_errinfo.should == [:invalid_byte_sequence, "UTF-16LE", "UTF-8", "\x00\xD8", "a\x00"]
- ec.putback.should == "a\x00".force_encoding("utf-16le")
+ ec.putback.should == "a\x00".dup.force_encoding("utf-16le")
ec.putback.should == ""
end
it "accepts an integer argument corresponding to the number of bytes to be put back" do
ec = Encoding::Converter.new("utf-16le", "iso-8859-1")
- src = "\x00\xd8\x61\x00"
- dst = ""
+ src = +"\x00\xd8\x61\x00"
+ dst = +""
ec.primitive_convert(src, dst).should == :invalid_byte_sequence
ec.primitive_errinfo.should == [:invalid_byte_sequence, "UTF-16LE", "UTF-8", "\x00\xD8", "a\x00"]
- ec.putback(2).should == "a\x00".force_encoding("utf-16le")
+ ec.putback(2).should == "a\x00".dup.force_encoding("utf-16le")
ec.putback.should == ""
end
end
diff --git a/spec/ruby/core/encoding/converter/replacement_spec.rb b/spec/ruby/core/encoding/converter/replacement_spec.rb
index 5ca42e7e5a..ea514ca8dd 100644
--- a/spec/ruby/core/encoding/converter/replacement_spec.rb
+++ b/spec/ruby/core/encoding/converter/replacement_spec.rb
@@ -13,7 +13,7 @@ describe "Encoding::Converter#replacement" do
it "returns \\uFFFD when the destination encoding is UTF-8" do
ec = Encoding::Converter.new("us-ascii", "utf-8")
- ec.replacement.should == "\u{fffd}".force_encoding('utf-8')
+ ec.replacement.should == "\u{fffd}".dup.force_encoding('utf-8')
ec.replacement.encoding.should == Encoding::UTF_8
end
end
@@ -38,33 +38,33 @@ describe "Encoding::Converter#replacement=" do
it "sets #replacement" do
ec = Encoding::Converter.new("us-ascii", "utf-8")
- ec.replacement.should == "\u{fffd}".force_encoding('utf-8')
+ ec.replacement.should == "\u{fffd}".dup.force_encoding('utf-8')
ec.replacement = '?'.encode('utf-8')
- ec.replacement.should == '?'.force_encoding('utf-8')
+ ec.replacement.should == '?'.dup.force_encoding('utf-8')
end
it "raises an UndefinedConversionError is the argument cannot be converted into the destination encoding" do
ec = Encoding::Converter.new("sjis", "ascii")
- utf8_q = "\u{986}".force_encoding('utf-8')
- ec.primitive_convert(utf8_q.dup, "").should == :undefined_conversion
+ utf8_q = "\u{986}".dup.force_encoding('utf-8')
+ ec.primitive_convert(utf8_q.dup, +"").should == :undefined_conversion
-> { ec.replacement = utf8_q }.should \
raise_error(Encoding::UndefinedConversionError)
end
it "does not change the replacement character if the argument cannot be converted into the destination encoding" do
ec = Encoding::Converter.new("sjis", "ascii")
- utf8_q = "\u{986}".force_encoding('utf-8')
- ec.primitive_convert(utf8_q.dup, "").should == :undefined_conversion
+ utf8_q = "\u{986}".dup.force_encoding('utf-8')
+ ec.primitive_convert(utf8_q.dup, +"").should == :undefined_conversion
-> { ec.replacement = utf8_q }.should \
raise_error(Encoding::UndefinedConversionError)
- ec.replacement.should == "?".force_encoding('us-ascii')
+ ec.replacement.should == "?".dup.force_encoding('us-ascii')
end
it "uses the replacement character" do
ec = Encoding::Converter.new("utf-8", "us-ascii", :invalid => :replace, :undef => :replace)
ec.replacement = "!"
- dest = ""
- status = ec.primitive_convert "中文123", dest
+ dest = +""
+ status = ec.primitive_convert(+"中文123", dest)
status.should == :finished
dest.should == "!!123"
diff --git a/spec/ruby/core/encoding/default_external_spec.rb b/spec/ruby/core/encoding/default_external_spec.rb
index 682d49d37c..9aae4976e0 100644
--- a/spec/ruby/core/encoding/default_external_spec.rb
+++ b/spec/ruby/core/encoding/default_external_spec.rb
@@ -18,11 +18,9 @@ describe "Encoding.default_external" do
Encoding.default_external.should == Encoding::SHIFT_JIS
end
- ruby_version_is "3.0" do
- platform_is :windows do
- it 'is UTF-8 by default on Windows' do
- Encoding.default_external.should == Encoding::UTF_8
- end
+ platform_is :windows do
+ it 'is UTF-8 by default on Windows' do
+ Encoding.default_external.should == Encoding::UTF_8
end
end
end
diff --git a/spec/ruby/core/encoding/inspect_spec.rb b/spec/ruby/core/encoding/inspect_spec.rb
index 9a930b2a77..df96141db9 100644
--- a/spec/ruby/core/encoding/inspect_spec.rb
+++ b/spec/ruby/core/encoding/inspect_spec.rb
@@ -5,9 +5,23 @@ describe "Encoding#inspect" do
Encoding::UTF_8.inspect.should be_an_instance_of(String)
end
- it "returns #<Encoding:name> for a non-dummy encoding named 'name'" do
- Encoding.list.to_a.reject {|e| e.dummy? }.each do |enc|
- enc.inspect.should =~ /#<Encoding:#{enc.name}>/
+ ruby_version_is ""..."3.4" do
+ it "returns #<Encoding:name> for a non-dummy encoding named 'name'" do
+ Encoding.list.to_a.reject {|e| e.dummy? }.each do |enc|
+ enc.inspect.should =~ /#<Encoding:#{enc.name}>/
+ end
+ end
+ end
+
+ ruby_version_is "3.4" do
+ it "returns #<Encoding:name> for a non-dummy encoding named 'name'" do
+ Encoding.list.to_a.reject {|e| e.dummy? }.each do |enc|
+ if enc.name == "ASCII-8BIT"
+ enc.inspect.should == "#<Encoding:BINARY (ASCII-8BIT)>"
+ else
+ enc.inspect.should =~ /#<Encoding:#{enc.name}>/
+ end
+ end
end
end
diff --git a/spec/ruby/core/encoding/invalid_byte_sequence_error/incomplete_input_spec.rb b/spec/ruby/core/encoding/invalid_byte_sequence_error/incomplete_input_spec.rb
index 94201a9b15..8a3f3de69a 100644
--- a/spec/ruby/core/encoding/invalid_byte_sequence_error/incomplete_input_spec.rb
+++ b/spec/ruby/core/encoding/invalid_byte_sequence_error/incomplete_input_spec.rb
@@ -8,7 +8,7 @@ describe "Encoding::InvalidByteSequenceError#incomplete_input?" do
it "returns true if #primitive_convert returned :incomplete_input for the same data" do
ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
- ec.primitive_convert("\xA1",'').should == :incomplete_input
+ ec.primitive_convert(+"\xA1", +'').should == :incomplete_input
begin
ec.convert("\xA1")
rescue Encoding::InvalidByteSequenceError => e
@@ -18,7 +18,7 @@ describe "Encoding::InvalidByteSequenceError#incomplete_input?" do
it "returns false if #primitive_convert returned :invalid_byte_sequence for the same data" do
ec = Encoding::Converter.new("ascii", "utf-8")
- ec.primitive_convert("\xfffffffff",'').should == :invalid_byte_sequence
+ ec.primitive_convert(+"\xfffffffff", +'').should == :invalid_byte_sequence
begin
ec.convert("\xfffffffff")
rescue Encoding::InvalidByteSequenceError => e
diff --git a/spec/ruby/core/encoding/invalid_byte_sequence_error/readagain_bytes_spec.rb b/spec/ruby/core/encoding/invalid_byte_sequence_error/readagain_bytes_spec.rb
index 9866310c25..a5e2824984 100644
--- a/spec/ruby/core/encoding/invalid_byte_sequence_error/readagain_bytes_spec.rb
+++ b/spec/ruby/core/encoding/invalid_byte_sequence_error/readagain_bytes_spec.rb
@@ -15,11 +15,11 @@ describe "Encoding::InvalidByteSequenceError#readagain_bytes" do
it "returns the bytes to be read again" do
@exception.readagain_bytes.size.should == 1
- @exception.readagain_bytes.should == "a".force_encoding('binary')
+ @exception.readagain_bytes.should == "a".dup.force_encoding('binary')
@exception.readagain_bytes.should == @errinfo[-1]
@exception2.readagain_bytes.size.should == 1
- @exception2.readagain_bytes.should == "\xFF".force_encoding('binary')
+ @exception2.readagain_bytes.should == "\xFF".dup.force_encoding('binary')
@exception2.readagain_bytes.should == @errinfo2[-1]
end
diff --git a/spec/ruby/core/encoding/replicate_spec.rb b/spec/ruby/core/encoding/replicate_spec.rb
index 498d03581a..e22673db7d 100644
--- a/spec/ruby/core/encoding/replicate_spec.rb
+++ b/spec/ruby/core/encoding/replicate_spec.rb
@@ -18,8 +18,8 @@ describe "Encoding#replicate" do
e.name.should == name
Encoding.find(name).should == e
- "a".force_encoding(e).valid_encoding?.should be_true
- "\x80".force_encoding(e).valid_encoding?.should be_false
+ "a".dup.force_encoding(e).valid_encoding?.should be_true
+ "\x80".dup.force_encoding(e).valid_encoding?.should be_false
end
it "returns a replica of UTF-8" do
@@ -28,9 +28,9 @@ describe "Encoding#replicate" do
e.name.should == name
Encoding.find(name).should == e
- "a".force_encoding(e).valid_encoding?.should be_true
- "\u3042".force_encoding(e).valid_encoding?.should be_true
- "\x80".force_encoding(e).valid_encoding?.should be_false
+ "a".dup.force_encoding(e).valid_encoding?.should be_true
+ "\u3042".dup.force_encoding(e).valid_encoding?.should be_true
+ "\x80".dup.force_encoding(e).valid_encoding?.should be_false
end
it "returns a replica of UTF-16BE" do
@@ -39,9 +39,9 @@ describe "Encoding#replicate" do
e.name.should == name
Encoding.find(name).should == e
- "a".force_encoding(e).valid_encoding?.should be_false
- "\x30\x42".force_encoding(e).valid_encoding?.should be_true
- "\x80".force_encoding(e).valid_encoding?.should be_false
+ "a".dup.force_encoding(e).valid_encoding?.should be_false
+ "\x30\x42".dup.force_encoding(e).valid_encoding?.should be_true
+ "\x80".dup.force_encoding(e).valid_encoding?.should be_false
end
it "returns a replica of ISO-2022-JP" do
@@ -61,7 +61,7 @@ describe "Encoding#replicate" do
e.name.should == name
Encoding.find(name).should == e
- s = "abc".force_encoding(e)
+ s = "abc".dup.force_encoding(e)
s.encoding.should == e
s.encoding.name.should == name
end
@@ -73,6 +73,11 @@ describe "Encoding#replicate" do
Encoding::US_ASCII.replicate('MY-US-ASCII')
}.should complain(/warning: Encoding#replicate is deprecated and will be removed in Ruby 3.3; use the original encoding instead/)
end
+
+ it "raises EncodingError if too many encodings" do
+ code = '1_000.times {|i| Encoding::US_ASCII.replicate("R_#{i}") }'
+ ruby_exe(code, args: "2>&1", exit_status: 1).should.include?('too many encoding (> 256) (EncodingError)')
+ end
end
ruby_version_is "3.3" do
diff --git a/spec/ruby/core/enumerable/fixtures/classes.rb b/spec/ruby/core/enumerable/fixtures/classes.rb
index fb4951c6e6..2701c6999c 100644
--- a/spec/ruby/core/enumerable/fixtures/classes.rb
+++ b/spec/ruby/core/enumerable/fixtures/classes.rb
@@ -342,4 +342,10 @@ module EnumerableSpecs
@block.call(*args)
end
end
+
+ # Set is a core class since Ruby 3.2
+ ruby_version_is '3.2' do
+ class SetSubclass < Set
+ end
+ end
end # EnumerableSpecs utility classes
diff --git a/spec/ruby/core/enumerable/grep_spec.rb b/spec/ruby/core/enumerable/grep_spec.rb
index b81075291f..989358f01b 100644
--- a/spec/ruby/core/enumerable/grep_spec.rb
+++ b/spec/ruby/core/enumerable/grep_spec.rb
@@ -40,43 +40,28 @@ describe "Enumerable#grep" do
$~.should == nil
end
- ruby_version_is ""..."3.0.0" do
- it "sets $~ to the last match when given no block" do
- "z" =~ /z/ # Reset $~
- ["abc", "def"].grep(/b/).should == ["abc"]
-
- # Set by the failed match of "def"
- $~.should == nil
-
- ["abc", "def"].grep(/e/)
- $&.should == "e"
- end
+ it "does not set $~ when given no block" do
+ "z" =~ /z/ # Reset $~
+ ["abc", "def"].grep(/b/).should == ["abc"]
+ $&.should == "z"
end
- ruby_version_is "3.0.0" do
- it "does not set $~ when given no block" do
- "z" =~ /z/ # Reset $~
- ["abc", "def"].grep(/b/).should == ["abc"]
- $&.should == "z"
- end
-
- it "does not modify Regexp.last_match without block" do
- "z" =~ /z/ # Reset last match
- ["abc", "def"].grep(/b/).should == ["abc"]
- Regexp.last_match[0].should == "z"
- end
+ it "does not modify Regexp.last_match without block" do
+ "z" =~ /z/ # Reset last match
+ ["abc", "def"].grep(/b/).should == ["abc"]
+ Regexp.last_match[0].should == "z"
+ end
- it "correctly handles non-string elements" do
- 'set last match' =~ /set last (.*)/
- [:a, 'b', 'z', :c, 42, nil].grep(/[a-d]/).should == [:a, 'b', :c]
- $1.should == 'match'
+ it "correctly handles non-string elements" do
+ 'set last match' =~ /set last (.*)/
+ [:a, 'b', 'z', :c, 42, nil].grep(/[a-d]/).should == [:a, 'b', :c]
+ $1.should == 'match'
- o = Object.new
- def o.to_str
- 'hello'
- end
- [o].grep(/ll/).first.should.equal?(o)
+ o = Object.new
+ def o.to_str
+ 'hello'
end
+ [o].grep(/ll/).first.should.equal?(o)
end
describe "with a block" do
diff --git a/spec/ruby/core/enumerable/grep_v_spec.rb b/spec/ruby/core/enumerable/grep_v_spec.rb
index 35fde27eb6..ba19216968 100644
--- a/spec/ruby/core/enumerable/grep_v_spec.rb
+++ b/spec/ruby/core/enumerable/grep_v_spec.rb
@@ -20,43 +20,28 @@ describe "Enumerable#grep_v" do
$&.should == "e"
end
- ruby_version_is ""..."3.0.0" do
- it "sets $~ to the last match when given no block" do
- "z" =~ /z/ # Reset $~
- ["abc", "def"].grep_v(/e/).should == ["abc"]
-
- # Set by the match of "def"
- $&.should == "e"
-
- ["abc", "def"].grep_v(/b/)
- $&.should == nil
- end
+ it "does not set $~ when given no block" do
+ "z" =~ /z/ # Reset $~
+ ["abc", "def"].grep_v(/e/).should == ["abc"]
+ $&.should == "z"
end
- ruby_version_is "3.0.0" do
- it "does not set $~ when given no block" do
- "z" =~ /z/ # Reset $~
- ["abc", "def"].grep_v(/e/).should == ["abc"]
- $&.should == "z"
- end
-
- it "does not modify Regexp.last_match without block" do
- "z" =~ /z/ # Reset last match
- ["abc", "def"].grep_v(/e/).should == ["abc"]
- Regexp.last_match[0].should == "z"
- end
+ it "does not modify Regexp.last_match without block" do
+ "z" =~ /z/ # Reset last match
+ ["abc", "def"].grep_v(/e/).should == ["abc"]
+ Regexp.last_match[0].should == "z"
+ end
- it "correctly handles non-string elements" do
- 'set last match' =~ /set last (.*)/
- [:a, 'b', 'z', :c, 42, nil].grep_v(/[a-d]/).should == ['z', 42, nil]
- $1.should == 'match'
+ it "correctly handles non-string elements" do
+ 'set last match' =~ /set last (.*)/
+ [:a, 'b', 'z', :c, 42, nil].grep_v(/[a-d]/).should == ['z', 42, nil]
+ $1.should == 'match'
- o = Object.new
- def o.to_str
- 'hello'
- end
- [o].grep_v(/mm/).first.should.equal?(o)
+ o = Object.new
+ def o.to_str
+ 'hello'
end
+ [o].grep_v(/mm/).first.should.equal?(o)
end
describe "without block" do
diff --git a/spec/ruby/core/enumerable/to_set_spec.rb b/spec/ruby/core/enumerable/to_set_spec.rb
new file mode 100644
index 0000000000..c21a2772c4
--- /dev/null
+++ b/spec/ruby/core/enumerable/to_set_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is "3.2" do
+ describe "Enumerable#to_set" do
+ it "returns a new Set created from self" do
+ [1, 2, 3].to_set.should == Set[1, 2, 3]
+ {a: 1, b: 2}.to_set.should == Set[[:b, 2], [:a, 1]]
+ end
+
+ it "passes down passed blocks" do
+ [1, 2, 3].to_set { |x| x * x }.should == Set[1, 4, 9]
+ end
+
+ it "instantiates an object of provided as the first argument set class" do
+ set = [1, 2, 3].to_set(EnumerableSpecs::SetSubclass)
+ set.should be_kind_of(EnumerableSpecs::SetSubclass)
+ set.to_a.sort.should == [1, 2, 3]
+ end
+
+ it "does not need explicit `require 'set'`" do
+ output = ruby_exe(<<~RUBY, options: '--disable-gems', args: '2>&1')
+ puts [1, 2, 3].to_set
+ RUBY
+
+ output.chomp.should == "#<Set: {1, 2, 3}>"
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/chain/initialize_spec.rb b/spec/ruby/core/enumerator/chain/initialize_spec.rb
index 69484dfcb4..daa30351d7 100644
--- a/spec/ruby/core/enumerator/chain/initialize_spec.rb
+++ b/spec/ruby/core/enumerator/chain/initialize_spec.rb
@@ -22,10 +22,10 @@ describe "Enumerator::Chain#initialize" do
end
describe "on frozen instance" do
- it "raises a RuntimeError" do
+ it "raises a FrozenError" do
-> {
@uninitialized.freeze.send(:initialize)
- }.should raise_error(RuntimeError)
+ }.should raise_error(FrozenError)
end
end
end
diff --git a/spec/ruby/core/enumerator/each_spec.rb b/spec/ruby/core/enumerator/each_spec.rb
index 99ac3120af..3af16e5587 100644
--- a/spec/ruby/core/enumerator/each_spec.rb
+++ b/spec/ruby/core/enumerator/each_spec.rb
@@ -10,41 +10,41 @@ describe "Enumerator#each" do
@enum_with_arguments = object_each_with_arguments.to_enum(:each_with_arguments, :arg0, :arg1, :arg2)
- @enum_with_yielder = Enumerator.new {|y| y.yield :ok}
+ @enum_with_yielder = Enumerator.new { |y| y.yield :ok }
end
it "yields each element of self to the given block" do
acc = []
- [1,2,3].to_enum.each {|e| acc << e }
- acc.should == [1,2,3]
+ [1, 2, 3].to_enum.each { |e| acc << e }
+ acc.should == [1, 2, 3]
end
it "calls #each on the object given in the constructor by default" do
each = mock('each')
each.should_receive(:each)
- each.to_enum.each {|e| e }
+ each.to_enum.each { |e| e }
end
it "calls #each on the underlying object until it's exhausted" do
each = mock('each')
each.should_receive(:each).and_yield(1).and_yield(2).and_yield(3)
acc = []
- each.to_enum.each {|e| acc << e }
- acc.should == [1,2,3]
+ each.to_enum.each { |e| acc << e }
+ acc.should == [1, 2, 3]
end
it "calls the method given in the constructor instead of #each" do
each = mock('peach')
each.should_receive(:peach)
- each.to_enum(:peach).each {|e| e }
+ each.to_enum(:peach).each { |e| e }
end
it "calls the method given in the constructor until it's exhausted" do
each = mock('peach')
each.should_receive(:peach).and_yield(1).and_yield(2).and_yield(3)
acc = []
- each.to_enum(:peach).each {|e| acc << e }
- acc.should == [1,2,3]
+ each.to_enum(:peach).each { |e| acc << e }
+ acc.should == [1, 2, 3]
end
it "raises a NoMethodError if the object doesn't respond to #each" do
diff --git a/spec/ruby/core/enumerator/generator/initialize_spec.rb b/spec/ruby/core/enumerator/generator/initialize_spec.rb
index f75c7d6f26..acc1174253 100644
--- a/spec/ruby/core/enumerator/generator/initialize_spec.rb
+++ b/spec/ruby/core/enumerator/generator/initialize_spec.rb
@@ -17,10 +17,10 @@ describe "Enumerator::Generator#initialize" do
end
describe "on frozen instance" do
- it "raises a RuntimeError" do
+ it "raises a FrozenError" do
-> {
@uninitialized.freeze.send(:initialize) {}
- }.should raise_error(RuntimeError)
+ }.should raise_error(FrozenError)
end
end
end
diff --git a/spec/ruby/core/enumerator/initialize_spec.rb b/spec/ruby/core/enumerator/initialize_spec.rb
index 217af1d3bc..5e0256ca46 100644
--- a/spec/ruby/core/enumerator/initialize_spec.rb
+++ b/spec/ruby/core/enumerator/initialize_spec.rb
@@ -11,14 +11,6 @@ describe "Enumerator#initialize" do
Enumerator.should have_private_instance_method(:initialize, false)
end
- ruby_version_is ''...'3.0' do
- it "returns self when given an object" do
- suppress_warning do
- @uninitialized.send(:initialize, Object.new).should equal(@uninitialized)
- end
- end
- end
-
it "returns self when given a block" do
@uninitialized.send(:initialize) {}.should equal(@uninitialized)
end
@@ -56,10 +48,10 @@ describe "Enumerator#initialize" do
end
describe "on frozen instance" do
- it "raises a RuntimeError" do
+ it "raises a FrozenError" do
-> {
@uninitialized.freeze.send(:initialize) {}
- }.should raise_error(RuntimeError)
+ }.should raise_error(FrozenError)
end
end
end
diff --git a/spec/ruby/core/enumerator/lazy/initialize_spec.rb b/spec/ruby/core/enumerator/lazy/initialize_spec.rb
index f23018d010..e1e0b1d608 100644
--- a/spec/ruby/core/enumerator/lazy/initialize_spec.rb
+++ b/spec/ruby/core/enumerator/lazy/initialize_spec.rb
@@ -56,8 +56,8 @@ describe "Enumerator::Lazy#initialize" do
end
describe "on frozen instance" do
- it "raises a RuntimeError" do
- -> { @uninitialized.freeze.send(:initialize, @receiver) {} }.should raise_error(RuntimeError)
+ it "raises a FrozenError" do
+ -> { @uninitialized.freeze.send(:initialize, @receiver) {} }.should raise_error(FrozenError)
end
end
end
diff --git a/spec/ruby/core/enumerator/new_spec.rb b/spec/ruby/core/enumerator/new_spec.rb
index c439469525..671912224f 100644
--- a/spec/ruby/core/enumerator/new_spec.rb
+++ b/spec/ruby/core/enumerator/new_spec.rb
@@ -2,51 +2,8 @@ require_relative '../../spec_helper'
describe "Enumerator.new" do
context "no block given" do
- ruby_version_is '3.0' do
- it "raises" do
- -> { Enumerator.new(1, :upto, 3) }.should raise_error(ArgumentError)
- end
- end
-
- ruby_version_is ''...'3.0' do
- it "creates a new custom enumerator with the given object, iterator and arguments" do
- enum = suppress_warning { Enumerator.new(1, :upto, 3) }
- enum.should be_an_instance_of(Enumerator)
- end
-
- it "creates a new custom enumerator that responds to #each" do
- enum = suppress_warning { Enumerator.new(1, :upto, 3) }
- enum.respond_to?(:each).should == true
- end
-
- it "creates a new custom enumerator that runs correctly" do
- suppress_warning { Enumerator.new(1, :upto, 3) }.map{ |x| x }.should == [1,2,3]
- end
-
- it "aliases the second argument to :each" do
- suppress_warning { Enumerator.new(1..2) }.to_a.should ==
- suppress_warning { Enumerator.new(1..2, :each) }.to_a
- end
-
- it "doesn't check for the presence of the iterator method" do
- suppress_warning { Enumerator.new(nil) }.should be_an_instance_of(Enumerator)
- end
-
- it "uses the latest define iterator method" do
- class StrangeEach
- def each
- yield :foo
- end
- end
- enum = suppress_warning { Enumerator.new(StrangeEach.new) }
- enum.to_a.should == [:foo]
- class StrangeEach
- def each
- yield :bar
- end
- end
- enum.to_a.should == [:bar]
- end
+ it "raises" do
+ -> { Enumerator.new(1, :upto, 3) }.should raise_error(ArgumentError)
end
end
diff --git a/spec/ruby/core/enumerator/product/each_spec.rb b/spec/ruby/core/enumerator/product/each_spec.rb
new file mode 100644
index 0000000000..cabeb9d93a
--- /dev/null
+++ b/spec/ruby/core/enumerator/product/each_spec.rb
@@ -0,0 +1,73 @@
+require_relative '../../../spec_helper'
+require_relative '../../enumerable/shared/enumeratorized'
+
+ruby_version_is "3.2" do
+ describe "Enumerator::Product#each" do
+ it_behaves_like :enumeratorized_with_origin_size, :each, Enumerator::Product.new([1, 2], [:a, :b])
+
+ it "yields each element of Cartesian product of enumerators" do
+ enum = Enumerator::Product.new([1, 2], [:a, :b])
+ acc = []
+ enum.each { |e| acc << e }
+ acc.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
+ end
+
+ it "calls #each_entry method on enumerators" do
+ object1 = Object.new
+ def object1.each_entry
+ yield 1
+ yield 2
+ end
+
+ object2 = Object.new
+ def object2.each_entry
+ yield :a
+ yield :b
+ end
+
+ enum = Enumerator::Product.new(object1, object2)
+ acc = []
+ enum.each { |e| acc << e }
+ acc.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
+ end
+
+ it "raises a NoMethodError if the object doesn't respond to #each_entry" do
+ -> {
+ Enumerator::Product.new(Object.new).each {}
+ }.should raise_error(NoMethodError, /undefined method [`']each_entry' for/)
+ end
+
+ it "returns enumerator if not given a block" do
+ enum = Enumerator::Product.new([1, 2], [:a, :b])
+ enum.each.should.kind_of?(Enumerator)
+
+ enum = Enumerator::Product.new([1, 2], [:a, :b])
+ enum.each.to_a.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
+ end
+
+ it "returns self if given a block" do
+ enum = Enumerator::Product.new([1, 2], [:a, :b])
+ enum.each {}.should.equal?(enum)
+ end
+
+ it "doesn't accept arguments" do
+ Enumerator::Product.instance_method(:each).arity.should == 0
+ end
+
+ it "yields each element to a block that takes multiple arguments" do
+ enum = Enumerator::Product.new([1, 2], [:a, :b])
+
+ acc = []
+ enum.each { |x, y| acc << x }
+ acc.should == [1, 1, 2, 2]
+
+ acc = []
+ enum.each { |x, y| acc << y }
+ acc.should == [:a, :b, :a, :b]
+
+ acc = []
+ enum.each { |x, y, z| acc << z }
+ acc.should == [nil, nil, nil, nil]
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/product/initialize_copy_spec.rb b/spec/ruby/core/enumerator/product/initialize_copy_spec.rb
new file mode 100644
index 0000000000..46e8421322
--- /dev/null
+++ b/spec/ruby/core/enumerator/product/initialize_copy_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../../../spec_helper'
+
+ruby_version_is "3.2" do
+ describe "Enumerator::Product#initialize_copy" do
+ it "replaces content of the receiver with content of the other object" do
+ enum = Enumerator::Product.new([true, false])
+ enum2 = Enumerator::Product.new([1, 2], [:a, :b])
+
+ enum.send(:initialize_copy, enum2)
+ enum.each.to_a.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
+ end
+
+ it "returns self" do
+ enum = Enumerator::Product.new([true, false])
+ enum2 = Enumerator::Product.new([1, 2], [:a, :b])
+
+ enum.send(:initialize_copy, enum2).should.equal?(enum)
+ end
+
+ it "is a private method" do
+ Enumerator::Product.should have_private_instance_method(:initialize_copy, false)
+ end
+
+ it "does nothing if the argument is the same as the receiver" do
+ enum = Enumerator::Product.new(1..2)
+ enum.send(:initialize_copy, enum).should.equal?(enum)
+
+ enum.freeze
+ enum.send(:initialize_copy, enum).should.equal?(enum)
+ end
+
+ it "raises FrozenError if the receiver is frozen" do
+ enum = Enumerator::Product.new(1..2)
+ enum2 = Enumerator::Product.new(3..4)
+
+ -> { enum.freeze.send(:initialize_copy, enum2) }.should raise_error(FrozenError)
+ end
+
+ it "raises TypeError if the objects are of different class" do
+ enum = Enumerator::Product.new(1..2)
+ enum2 = Class.new(Enumerator::Product).new(3..4)
+
+ -> { enum.send(:initialize_copy, enum2) }.should raise_error(TypeError, 'initialize_copy should take same class object')
+ -> { enum2.send(:initialize_copy, enum) }.should raise_error(TypeError, 'initialize_copy should take same class object')
+ end
+
+ it "raises ArgumentError if the argument is not initialized yet" do
+ enum = Enumerator::Product.new(1..2)
+ enum2 = Enumerator::Product.allocate
+
+ -> { enum.send(:initialize_copy, enum2) }.should raise_error(ArgumentError, 'uninitialized product')
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/product/initialize_spec.rb b/spec/ruby/core/enumerator/product/initialize_spec.rb
new file mode 100644
index 0000000000..4b60564240
--- /dev/null
+++ b/spec/ruby/core/enumerator/product/initialize_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../../spec_helper'
+
+ruby_version_is "3.2" do
+ describe "Enumerator::Product#initialize" do
+ before :each do
+ @uninitialized = Enumerator::Product.allocate
+ end
+
+ it "is a private method" do
+ Enumerator::Product.should have_private_instance_method(:initialize, false)
+ end
+
+ it "returns self" do
+ @uninitialized.send(:initialize).should equal(@uninitialized)
+ end
+
+ it "accepts many arguments" do
+ @uninitialized.send(:initialize, 0..1, 2..3, 4..5).should equal(@uninitialized)
+ end
+
+ it "accepts arguments that are not Enumerable nor responding to :each_entry" do
+ @uninitialized.send(:initialize, Object.new).should equal(@uninitialized)
+ end
+
+ describe "on frozen instance" do
+ it "raises a FrozenError" do
+ -> {
+ @uninitialized.freeze.send(:initialize, 0..1)
+ }.should raise_error(FrozenError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/product/inspect_spec.rb b/spec/ruby/core/enumerator/product/inspect_spec.rb
new file mode 100644
index 0000000000..1ea8e9c49b
--- /dev/null
+++ b/spec/ruby/core/enumerator/product/inspect_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+
+ruby_version_is "3.2" do
+ describe "Enumerator::Product#inspect" do
+ it "returns a String including enumerators" do
+ enum = Enumerator::Product.new([1, 2], [:a, :b])
+ enum.inspect.should == "#<Enumerator::Product: [[1, 2], [:a, :b]]>"
+ end
+
+ it "represents a recursive element with '[...]'" do
+ enum = [1, 2]
+ enum_recursive = Enumerator::Product.new(enum)
+
+ enum << enum_recursive
+ enum_recursive.inspect.should == "#<Enumerator::Product: [[1, 2, #<Enumerator::Product: ...>]]>"
+ end
+
+ it "returns a not initialized representation if #initialized is not called yet" do
+ Enumerator::Product.allocate.inspect.should == "#<Enumerator::Product: uninitialized>"
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/product/rewind_spec.rb b/spec/ruby/core/enumerator/product/rewind_spec.rb
new file mode 100644
index 0000000000..e8ee730239
--- /dev/null
+++ b/spec/ruby/core/enumerator/product/rewind_spec.rb
@@ -0,0 +1,64 @@
+require_relative '../../../spec_helper'
+
+ruby_version_is "3.2" do
+ describe "Enumerator::Product#rewind" do
+ before :each do
+ @enum = Enumerator::Product.new([1, 2].each.to_enum, [:a, :b].each.to_enum)
+ end
+
+ it "resets the enumerator to its initial state" do
+ @enum.each.to_a.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
+ @enum.rewind
+ @enum.each.to_a.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
+ end
+
+ it "returns self" do
+ @enum.rewind.should.equal? @enum
+ end
+
+ it "has no effect on a new enumerator" do
+ @enum.rewind
+ @enum.each.to_a.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
+ end
+
+ it "has no effect if called multiple, consecutive times" do
+ @enum.each.to_a.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
+ @enum.rewind
+ @enum.rewind
+ @enum.each.to_a.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
+ end
+
+ it "calls the enclosed object's rewind method if one exists" do
+ obj = mock('rewinder')
+ enum = Enumerator::Product.new(obj.to_enum)
+
+ obj.should_receive(:rewind)
+ enum.rewind
+ end
+
+ it "does nothing if the object doesn't have a #rewind method" do
+ obj = mock('rewinder')
+ enum = Enumerator::Product.new(obj.to_enum)
+
+ enum.rewind.should == enum
+ end
+
+ it "calls a rewind method on each enumerable in direct order" do
+ ScratchPad.record []
+
+ object1 = Object.new
+ def object1.rewind; ScratchPad << :object1; end
+
+ object2 = Object.new
+ def object2.rewind; ScratchPad << :object2; end
+
+ object3 = Object.new
+ def object3.rewind; ScratchPad << :object3; end
+
+ enum = Enumerator::Product.new(object1, object2, object3)
+ enum.rewind
+
+ ScratchPad.recorded.should == [:object1, :object2, :object3]
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/product/size_spec.rb b/spec/ruby/core/enumerator/product/size_spec.rb
new file mode 100644
index 0000000000..46958b1a22
--- /dev/null
+++ b/spec/ruby/core/enumerator/product/size_spec.rb
@@ -0,0 +1,56 @@
+require_relative '../../../spec_helper'
+
+ruby_version_is "3.2" do
+ describe "Enumerator::Product#size" do
+ it "returns the total size of the enumerator product calculated by multiplying the sizes of enumerables in the product" do
+ product = Enumerator::Product.new(1..2, 1..3, 1..4)
+ product.size.should == 24 # 2 * 3 * 4
+ end
+
+ it "returns nil if any enumerable reports its size as nil" do
+ enum = Object.new
+ def enum.size; nil; end
+
+ product = Enumerator::Product.new(1..2, enum)
+ product.size.should == nil
+ end
+
+ it "returns Float::INFINITY if any enumerable reports its size as Float::INFINITY" do
+ enum = Object.new
+ def enum.size; Float::INFINITY; end
+
+ product = Enumerator::Product.new(1..2, enum)
+ product.size.should == Float::INFINITY
+ end
+
+ it "returns nil if any enumerable reports its size as Float::NAN" do
+ enum = Object.new
+ def enum.size; Float::NAN; end
+
+ product = Enumerator::Product.new(1..2, enum)
+ product.size.should == nil
+ end
+
+ it "returns nil if any enumerable doesn't respond to #size" do
+ enum = Object.new
+ product = Enumerator::Product.new(1..2, enum)
+ product.size.should == nil
+ end
+
+ it "returns nil if any enumerable reports a not-convertible to Integer" do
+ enum = Object.new
+ def enum.size; :symbol; end
+
+ product = Enumerator::Product.new(1..2, enum)
+ product.size.should == nil
+ end
+
+ it "returns nil if any enumerable reports a non-Integer but convertible to Integer size" do
+ enum = Object.new
+ def enum.size; 1.0; end
+
+ product = Enumerator::Product.new(1..2, enum)
+ product.size.should == nil
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/product_spec.rb b/spec/ruby/core/enumerator/product_spec.rb
index 44fc6441e1..0acca6690e 100644
--- a/spec/ruby/core/enumerator/product_spec.rb
+++ b/spec/ruby/core/enumerator/product_spec.rb
@@ -44,6 +44,11 @@ ruby_version_is "3.2" do
elems.should == [[1, "X"], [1, "Y"], [2, "X"], [2, "Y"]]
end
+ it "returns nil when a block passed" do
+ Enumerator.product(1..2) {}.should == nil
+ end
+
+ # https://bugs.ruby-lang.org/issues/19829
it "reject keyword arguments" do
-> {
Enumerator.product(1..3, foo: 1, bar: 2)
@@ -64,7 +69,7 @@ ruby_version_is "3.2" do
it "raises NoMethodError when argument doesn't respond to #each_entry" do
-> {
Enumerator.product(Object.new).to_a
- }.should raise_error(NoMethodError, /undefined method `each_entry' for/)
+ }.should raise_error(NoMethodError, /undefined method [`']each_entry' for/)
end
it "calls #each_entry lazily" do
diff --git a/spec/ruby/core/enumerator/rewind_spec.rb b/spec/ruby/core/enumerator/rewind_spec.rb
index a105f2c619..6ba0edf174 100644
--- a/spec/ruby/core/enumerator/rewind_spec.rb
+++ b/spec/ruby/core/enumerator/rewind_spec.rb
@@ -14,7 +14,7 @@ describe "Enumerator#rewind" do
end
it "returns self" do
- @enum.rewind.should == @enum
+ @enum.rewind.should.equal? @enum
end
it "has no effect on a new enumerator" do
@@ -49,7 +49,7 @@ describe "Enumerator#rewind" do
obj = mock('rewinder')
enum = obj.to_enum
obj.should_receive(:each).at_most(1)
- -> { enum.rewind.should == enum }.should_not raise_error
+ enum.rewind.should == enum
end
end
diff --git a/spec/ruby/core/env/delete_spec.rb b/spec/ruby/core/env/delete_spec.rb
index 5e7891f74d..f28ac97911 100644
--- a/spec/ruby/core/env/delete_spec.rb
+++ b/spec/ruby/core/env/delete_spec.rb
@@ -30,11 +30,9 @@ describe "ENV.delete" do
ScratchPad.recorded.should == "foo"
end
- ruby_version_is "3.0" do
- it "returns the result of given block if the named environment variable does not exist" do
- ENV.delete("foo")
- ENV.delete("foo") { |name| "bar" }.should == "bar"
- end
+ it "returns the result of given block if the named environment variable does not exist" do
+ ENV.delete("foo")
+ ENV.delete("foo") { |name| "bar" }.should == "bar"
end
it "does not evaluate the block if the environment variable exists" do
@@ -43,6 +41,14 @@ describe "ENV.delete" do
ENV["foo"].should == nil
end
+ it "removes the variable coerced with #to_str" do
+ ENV["foo"] = "bar"
+ k = mock('key')
+ k.should_receive(:to_str).and_return("foo")
+ ENV.delete(k)
+ ENV["foo"].should == nil
+ end
+
it "raises TypeError if the argument is not a String and does not respond to #to_str" do
-> { ENV.delete(Object.new) }.should raise_error(TypeError, "no implicit conversion of Object into String")
end
diff --git a/spec/ruby/core/env/except_spec.rb b/spec/ruby/core/env/except_spec.rb
index cfe5865abe..fb8f3b7536 100644
--- a/spec/ruby/core/env/except_spec.rb
+++ b/spec/ruby/core/env/except_spec.rb
@@ -1,36 +1,34 @@
require_relative 'spec_helper'
require_relative 'shared/to_hash'
-ruby_version_is "3.0" do
- describe "ENV.except" do
- before do
- @orig_hash = ENV.to_hash
- end
+describe "ENV.except" do
+ before do
+ @orig_hash = ENV.to_hash
+ end
- after do
- ENV.replace @orig_hash
- end
+ after do
+ ENV.replace @orig_hash
+ end
- # Testing the method without arguments is covered via
- it_behaves_like :env_to_hash, :except
+ # Testing the method without arguments is covered via
+ it_behaves_like :env_to_hash, :except
- it "returns a hash without the requested subset" do
- ENV.clear
+ it "returns a hash without the requested subset" do
+ ENV.clear
- ENV['one'] = '1'
- ENV['two'] = '2'
- ENV['three'] = '3'
+ ENV['one'] = '1'
+ ENV['two'] = '2'
+ ENV['three'] = '3'
- ENV.except('one', 'three').should == { 'two' => '2' }
- end
+ ENV.except('one', 'three').should == { 'two' => '2' }
+ end
- it "ignores keys not present in the original hash" do
- ENV.clear
+ it "ignores keys not present in the original hash" do
+ ENV.clear
- ENV['one'] = '1'
- ENV['two'] = '2'
+ ENV['one'] = '1'
+ ENV['two'] = '2'
- ENV.except('one', 'three').should == { 'two' => '2' }
- end
+ ENV.except('one', 'three').should == { 'two' => '2' }
end
end
diff --git a/spec/ruby/core/env/index_spec.rb b/spec/ruby/core/env/index_spec.rb
deleted file mode 100644
index 301a66ab4e..0000000000
--- a/spec/ruby/core/env/index_spec.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-require_relative '../../spec_helper'
-require_relative 'shared/key'
-
-ruby_version_is ''...'3.0' do
- describe "ENV.index" do
- it_behaves_like :env_key, :index
-
- it "warns about deprecation" do
- -> do
- ENV.index("foo")
- end.should complain(/warning: ENV.index is deprecated; use ENV.key/)
- end
- end
-end
diff --git a/spec/ruby/core/env/indexes_spec.rb b/spec/ruby/core/env/indexes_spec.rb
deleted file mode 100644
index e724feaa39..0000000000
--- a/spec/ruby/core/env/indexes_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../spec_helper'
diff --git a/spec/ruby/core/env/indices_spec.rb b/spec/ruby/core/env/indices_spec.rb
deleted file mode 100644
index e724feaa39..0000000000
--- a/spec/ruby/core/env/indices_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../spec_helper'
diff --git a/spec/ruby/core/env/key_spec.rb b/spec/ruby/core/env/key_spec.rb
index 82cfbefa39..cf70286409 100644
--- a/spec/ruby/core/env/key_spec.rb
+++ b/spec/ruby/core/env/key_spec.rb
@@ -1,11 +1,39 @@
require_relative '../../spec_helper'
require_relative 'shared/include'
-require_relative 'shared/key'
describe "ENV.key?" do
it_behaves_like :env_include, :key?
end
describe "ENV.key" do
- it_behaves_like :env_key, :key
+ before :each do
+ @saved_foo = ENV["foo"]
+ end
+
+ after :each do
+ ENV["foo"] = @saved_foo
+ end
+
+ it "returns the index associated with the passed value" do
+ ENV["foo"] = "bar"
+ ENV.key("bar").should == "foo"
+ end
+
+ it "returns nil if the passed value is not found" do
+ ENV.delete("foo")
+ ENV.key("foo").should be_nil
+ end
+
+ it "coerces the key element with #to_str" do
+ ENV["foo"] = "bar"
+ k = mock('key')
+ k.should_receive(:to_str).and_return("bar")
+ ENV.key(k).should == "foo"
+ end
+
+ it "raises TypeError if the argument is not a String and does not respond to #to_str" do
+ -> {
+ ENV.key(Object.new)
+ }.should raise_error(TypeError, "no implicit conversion of Object into String")
+ end
end
diff --git a/spec/ruby/core/env/shared/include.rb b/spec/ruby/core/env/shared/include.rb
index 3efcd523d6..70aa555301 100644
--- a/spec/ruby/core/env/shared/include.rb
+++ b/spec/ruby/core/env/shared/include.rb
@@ -17,6 +17,13 @@ describe :env_include, shared: true do
ENV.send(@method, "foo").should == false
end
+ it "coerces the key with #to_str" do
+ ENV["foo"] = "bar"
+ k = mock('key')
+ k.should_receive(:to_str).and_return("foo")
+ ENV.send(@method, k).should == true
+ end
+
it "raises TypeError if the argument is not a String and does not respond to #to_str" do
-> { ENV.send(@method, Object.new) }.should raise_error(TypeError, "no implicit conversion of Object into String")
end
diff --git a/spec/ruby/core/env/shared/key.rb b/spec/ruby/core/env/shared/key.rb
deleted file mode 100644
index 93396d2aca..0000000000
--- a/spec/ruby/core/env/shared/key.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-describe :env_key, shared: true do
- before :each do
- @saved_foo = ENV["foo"]
- end
-
- after :each do
- ENV["foo"] = @saved_foo
- end
-
- it "returns the index associated with the passed value" do
- ENV["foo"] = "bar"
- suppress_warning {
- ENV.send(@method, "bar").should == "foo"
- }
- end
-
- it "returns nil if the passed value is not found" do
- ENV.delete("foo")
- suppress_warning {
- ENV.send(@method, "foo").should be_nil
- }
- end
-
- it "raises TypeError if the argument is not a String and does not respond to #to_str" do
- -> {
- suppress_warning {
- ENV.send(@method, Object.new)
- }
- }.should raise_error(TypeError, "no implicit conversion of Object into String")
- end
-end
diff --git a/spec/ruby/core/env/shared/value.rb b/spec/ruby/core/env/shared/value.rb
index bef96b5fef..c2b5025465 100644
--- a/spec/ruby/core/env/shared/value.rb
+++ b/spec/ruby/core/env/shared/value.rb
@@ -16,6 +16,13 @@ describe :env_value, shared: true do
ENV.send(@method, "foo").should == false
end
+ it "coerces the value element with #to_str" do
+ ENV["foo"] = "bar"
+ v = mock('value')
+ v.should_receive(:to_str).and_return("bar")
+ ENV.send(@method, v).should == true
+ end
+
it "returns nil if the argument is not a String and does not respond to #to_str" do
ENV.send(@method, Object.new).should == nil
end
diff --git a/spec/ruby/core/env/slice_spec.rb b/spec/ruby/core/env/slice_spec.rb
index e3b6020391..959239d2b2 100644
--- a/spec/ruby/core/env/slice_spec.rb
+++ b/spec/ruby/core/env/slice_spec.rb
@@ -21,6 +21,16 @@ describe "ENV.slice" do
ENV.slice("foo", "boo", "bar").should == {"foo" => "0", "bar" => "1"}
end
+ it "returns the values for the keys coerced with #to_str, but keeps the original objects as result keys" do
+ foo = mock('key 1')
+ foo.should_receive(:to_str).and_return("foo")
+ boo = mock('key 2')
+ boo.should_receive(:to_str).and_return("boo")
+ bar = mock('key 3')
+ bar.should_receive(:to_str).and_return("bar")
+ ENV.slice(foo, boo, bar).should == {foo => "0", bar => "1"}
+ end
+
it "raises TypeError if any argument is not a String and does not respond to #to_str" do
-> { ENV.slice(Object.new) }.should raise_error(TypeError, "no implicit conversion of Object into String")
end
diff --git a/spec/ruby/core/env/to_a_spec.rb b/spec/ruby/core/env/to_a_spec.rb
index 39e3877b48..2b1649281f 100644
--- a/spec/ruby/core/env/to_a_spec.rb
+++ b/spec/ruby/core/env/to_a_spec.rb
@@ -6,7 +6,10 @@ describe "ENV.to_a" do
a = ENV.to_a
a.is_a?(Array).should == true
a.size.should == ENV.size
- ENV.each_pair { |k, v| a.should include([k, v])}
+ a.each { |k,v| ENV[k].should == v }
+
+ a.first.should.is_a?(Array)
+ a.first.size.should == 2
end
it "returns the entries in the locale encoding" do
diff --git a/spec/ruby/core/exception/backtrace_spec.rb b/spec/ruby/core/exception/backtrace_spec.rb
index 3f74c4cefe..9a65ea3820 100644
--- a/spec/ruby/core/exception/backtrace_spec.rb
+++ b/spec/ruby/core/exception/backtrace_spec.rb
@@ -27,7 +27,7 @@ describe "Exception#backtrace" do
end
it "includes the name of the method from where self raised in the first element" do
- @backtrace.first.should =~ /in `backtrace'/
+ @backtrace.first.should =~ /in [`'](?:ExceptionSpecs::Backtrace\.)?backtrace'/
end
it "includes the filename of the location immediately prior to where self raised in the second element" do
@@ -38,12 +38,25 @@ describe "Exception#backtrace" do
@backtrace[1].should =~ /:6(:in )?/
end
- it "contains lines of the same format for each prior position in the stack" do
- @backtrace[2..-1].each do |line|
- # This regexp is deliberately imprecise to account for the need to abstract out
- # the paths of the included mspec files and the desire to avoid specifying in any
- # detail what the in `...' portion looks like.
- line.should =~ /^.+:\d+:in `[^`]+'$/
+ ruby_version_is ""..."3.4" do
+ it "contains lines of the same format for each prior position in the stack" do
+ @backtrace[2..-1].each do |line|
+ # This regexp is deliberately imprecise to account for the need to abstract out
+ # the paths of the included mspec files and the desire to avoid specifying in any
+ # detail what the in `...' portion looks like.
+ line.should =~ /^.+:\d+:in `[^`]+'$/
+ end
+ end
+ end
+
+ ruby_version_is "3.4" do
+ it "contains lines of the same format for each prior position in the stack" do
+ @backtrace[2..-1].each do |line|
+ # This regexp is deliberately imprecise to account for the need to abstract out
+ # the paths of the included mspec files and the desire to avoid specifying in any
+ # detail what the in '...' portion looks like.
+ line.should =~ /^.+:\d+:in '[^`]+'$/
+ end
end
end
diff --git a/spec/ruby/core/exception/case_compare_spec.rb b/spec/ruby/core/exception/case_compare_spec.rb
index 87b9dee3ca..5fd11ae741 100644
--- a/spec/ruby/core/exception/case_compare_spec.rb
+++ b/spec/ruby/core/exception/case_compare_spec.rb
@@ -26,13 +26,11 @@ describe "SystemCallError.===" do
end
it "returns true if receiver is generic and arg is kind of SystemCallError" do
- unknown_error_number = Errno.constants.size
e = SystemCallError.new('foo', @example_errno)
SystemCallError.===(e).should == true
end
it "returns false if receiver is generic and arg is not kind of SystemCallError" do
- unknown_error_number = Errno.constants.size
e = Object.new
SystemCallError.===(e).should == false
end
diff --git a/spec/ruby/core/exception/detailed_message_spec.rb b/spec/ruby/core/exception/detailed_message_spec.rb
index bd85927dbe..fbe4443daa 100644
--- a/spec/ruby/core/exception/detailed_message_spec.rb
+++ b/spec/ruby/core/exception/detailed_message_spec.rb
@@ -7,6 +7,14 @@ describe "Exception#detailed_message" do
RuntimeError.new("new error").detailed_message.should == "new error (RuntimeError)"
end
+ it "is called by #full_message to allow message customization" do
+ exception = Exception.new("new error")
+ def exception.detailed_message(**)
+ "<prefix>#{message}<suffix>"
+ end
+ exception.full_message(highlight: false).should.include? "<prefix>new error<suffix>"
+ end
+
it "accepts highlight keyword argument and adds escape control sequences" do
RuntimeError.new("new error").detailed_message(highlight: true).should == "\e[1mnew error (\e[1;4mRuntimeError\e[m\e[1m)\e[m"
end
@@ -23,13 +31,13 @@ describe "Exception#detailed_message" do
RuntimeError.new("").detailed_message.should == "unhandled exception"
end
- it "returns just class name for an instance of RuntimeError sublass with empty message" do
+ it "returns just class name for an instance of RuntimeError subclass with empty message" do
DetailedMessageSpec::C.new("").detailed_message.should == "DetailedMessageSpec::C"
end
it "returns a generated class name for an instance of RuntimeError anonymous subclass with empty message" do
klass = Class.new(RuntimeError)
- klass.new("").detailed_message.should =~ /\A#<Class:0x\h+>\z/
+ klass.new("").detailed_message.should =~ /\A#<Class:0x\h+>\z/
end
end
end
diff --git a/spec/ruby/core/exception/equal_value_spec.rb b/spec/ruby/core/exception/equal_value_spec.rb
index 7f2065511a..e8f3ce0f8d 100644
--- a/spec/ruby/core/exception/equal_value_spec.rb
+++ b/spec/ruby/core/exception/equal_value_spec.rb
@@ -22,18 +22,18 @@ describe "Exception#==" do
it "returns true if both exceptions have the same class, the same message, and the same backtrace" do
one = TypeError.new("message")
- one.set_backtrace [File.dirname(__FILE__)]
+ one.set_backtrace [__dir__]
two = TypeError.new("message")
- two.set_backtrace [File.dirname(__FILE__)]
+ two.set_backtrace [__dir__]
one.should == two
end
it "returns false if the two exceptions inherit from Exception but have different classes" do
one = RuntimeError.new("message")
- one.set_backtrace [File.dirname(__FILE__)]
+ one.set_backtrace [__dir__]
one.should be_kind_of(Exception)
two = TypeError.new("message")
- two.set_backtrace [File.dirname(__FILE__)]
+ two.set_backtrace [__dir__]
two.should be_kind_of(Exception)
one.should_not == two
end
@@ -52,7 +52,7 @@ describe "Exception#==" do
it "returns false if the two exceptions differ only in their backtrace" do
one = RuntimeError.new("message")
- one.set_backtrace [File.dirname(__FILE__)]
+ one.set_backtrace [__dir__]
two = RuntimeError.new("message")
two.set_backtrace nil
one.should_not == two
@@ -60,9 +60,9 @@ describe "Exception#==" do
it "returns false if the two exceptions differ only in their message" do
one = RuntimeError.new("message")
- one.set_backtrace [File.dirname(__FILE__)]
+ one.set_backtrace [__dir__]
two = RuntimeError.new("message2")
- two.set_backtrace [File.dirname(__FILE__)]
+ two.set_backtrace [__dir__]
one.should_not == two
end
end
diff --git a/spec/ruby/core/exception/fixtures/syntax_error.rb b/spec/ruby/core/exception/fixtures/syntax_error.rb
new file mode 100644
index 0000000000..ccec62f7a1
--- /dev/null
+++ b/spec/ruby/core/exception/fixtures/syntax_error.rb
@@ -0,0 +1,3 @@
+# rubocop:disable Lint/Syntax
+1+1=2
+# rubocop:enable Lint/Syntax
diff --git a/spec/ruby/core/exception/frozen_error_spec.rb b/spec/ruby/core/exception/frozen_error_spec.rb
index 2efdc239d8..979ec2ff98 100644
--- a/spec/ruby/core/exception/frozen_error_spec.rb
+++ b/spec/ruby/core/exception/frozen_error_spec.rb
@@ -20,3 +20,19 @@ describe "FrozenError#receiver" do
end
end
end
+
+describe "Modifying a frozen object" do
+ context "#inspect is redefined and modifies the object" do
+ it "returns ... instead of String representation of object" do
+ object = Object.new
+ def object.inspect; @a = 1 end
+ def object.modify; @a = 2 end
+
+ object.freeze
+
+ # CRuby's message contains multiple whitespaces before '...'.
+ # So handle both multiple and single whitespace.
+ -> { object.modify }.should raise_error(FrozenError, /can't modify frozen .*?: \s*.../)
+ end
+ end
+end
diff --git a/spec/ruby/core/exception/full_message_spec.rb b/spec/ruby/core/exception/full_message_spec.rb
index ee66582022..5154354555 100644
--- a/spec/ruby/core/exception/full_message_spec.rb
+++ b/spec/ruby/core/exception/full_message_spec.rb
@@ -42,16 +42,59 @@ describe "Exception#full_message" do
e = RuntimeError.new("Some runtime error")
e.backtrace.should == nil
full_message = e.full_message(highlight: false, order: :top).lines
- full_message[0].should.start_with?("#{__FILE__}:#{__LINE__-1}:in `")
+ full_message[0].should.start_with?("#{__FILE__}:#{__LINE__-1}:in ")
full_message[0].should.end_with?("': Some runtime error (RuntimeError)\n")
end
+ describe "includes details about whether an exception was handled" do
+ describe "RuntimeError" do
+ it "should report as unhandled if message is empty" do
+ err = RuntimeError.new("")
+
+ err.full_message.should =~ /unhandled exception/
+ err.full_message(highlight: true).should =~ /unhandled exception/
+ err.full_message(highlight: false).should =~ /unhandled exception/
+ end
+
+ it "should not report as unhandled if the message is not empty" do
+ err = RuntimeError.new("non-empty")
+
+ err.full_message.should !~ /unhandled exception/
+ err.full_message(highlight: true).should !~ /unhandled exception/
+ err.full_message(highlight: false).should !~ /unhandled exception/
+ end
+
+ it "should not report as unhandled if the message is nil" do
+ err = RuntimeError.new(nil)
+
+ err.full_message.should !~ /unhandled exception/
+ err.full_message(highlight: true).should !~ /unhandled exception/
+ err.full_message(highlight: false).should !~ /unhandled exception/
+ end
+
+ it "should not report as unhandled if the message is not specified" do
+ err = RuntimeError.new()
+
+ err.full_message.should !~ /unhandled exception/
+ err.full_message(highlight: true).should !~ /unhandled exception/
+ err.full_message(highlight: false).should !~ /unhandled exception/
+ end
+ end
+
+ describe "generic Error" do
+ it "should not report as unhandled in any event" do
+ StandardError.new("").full_message.should !~ /unhandled exception/
+ StandardError.new("non-empty").full_message.should !~ /unhandled exception/
+ end
+ end
+ end
+
it "shows the exception class at the end of the first line of the message when the message contains multiple lines" do
begin
line = __LINE__; raise "first line\nsecond line"
rescue => e
full_message = e.full_message(highlight: false, order: :top).lines
- full_message[0].should.start_with?("#{__FILE__}:#{line}:in `")
+ full_message[0].should.start_with?("#{__FILE__}:#{line}:in ")
full_message[0].should.end_with?(": first line (RuntimeError)\n")
full_message[1].should == "second line\n"
end
@@ -62,7 +105,7 @@ describe "Exception#full_message" do
line = __LINE__; raise "first line\nsecond line\nthird line"
rescue => e
full_message = e.full_message(highlight: true, order: :top).lines
- full_message[0].should.start_with?("#{__FILE__}:#{line}:in `")
+ full_message[0].should.start_with?("#{__FILE__}:#{line}:in ")
full_message[0].should.end_with?(": \e[1mfirst line (\e[1;4mRuntimeError\e[m\e[1m)\e[m\n")
full_message[1].should == "\e[1msecond line\e[m\n"
full_message[2].should == "\e[1mthird line\e[m\n"
@@ -107,21 +150,49 @@ describe "Exception#full_message" do
ruby_version_is "3.2" do
it "relies on #detailed_message" do
e = RuntimeError.new("new error")
- e.define_singleton_method(:detailed_message) { |**opt| "DETAILED MESSAGE" }
+ e.define_singleton_method(:detailed_message) { |**| "DETAILED MESSAGE" }
e.full_message.lines.first.should =~ /DETAILED MESSAGE/
end
- it "passes all its own keyword arguments to #detailed_message" do
+ it "passes all its own keyword arguments (with :highlight default value and without :order default value) to #detailed_message" do
e = RuntimeError.new("new error")
- opt_ = nil
- e.define_singleton_method(:detailed_message) do |**opt|
- opt_ = opt
+ options_passed = nil
+ e.define_singleton_method(:detailed_message) do |**options|
+ options_passed = options
"DETAILED MESSAGE"
end
e.full_message(foo: "bar")
- opt_.should == { foo: "bar", highlight: Exception.to_tty? }
+ options_passed.should == { foo: "bar", highlight: Exception.to_tty? }
+ end
+
+ it "converts #detailed_message returned value to String if it isn't a String" do
+ message = Object.new
+ def message.to_str; "DETAILED MESSAGE"; end
+
+ e = RuntimeError.new("new error")
+ e.define_singleton_method(:detailed_message) { |**| message }
+
+ e.full_message.lines.first.should =~ /DETAILED MESSAGE/
+ end
+
+ it "uses class name if #detailed_message returns nil" do
+ e = RuntimeError.new("new error")
+ e.define_singleton_method(:detailed_message) { |**| nil }
+
+ e.full_message(highlight: false).lines.first.should =~ /RuntimeError/
+ e.full_message(highlight: true).lines.first.should =~ /#{Regexp.escape("\e[1;4mRuntimeError\e[m")}/
+ end
+
+ it "uses class name if exception object doesn't respond to #detailed_message" do
+ e = RuntimeError.new("new error")
+ class << e
+ undef :detailed_message
+ end
+
+ e.full_message(highlight: false).lines.first.should =~ /RuntimeError/
+ e.full_message(highlight: true).lines.first.should =~ /#{Regexp.escape("\e[1;4mRuntimeError\e[m")}/
end
end
end
diff --git a/spec/ruby/core/exception/interrupt_spec.rb b/spec/ruby/core/exception/interrupt_spec.rb
index 299b5b81f3..90d261e470 100644
--- a/spec/ruby/core/exception/interrupt_spec.rb
+++ b/spec/ruby/core/exception/interrupt_spec.rb
@@ -54,7 +54,7 @@ describe "Interrupt" do
err = IO.popen([*ruby_exe, '-e', 'Process.kill :INT, Process.pid; sleep'], err: [:child, :out], &:read)
$?.termsig.should == Signal.list.fetch('INT')
err.should.include? ': Interrupt'
- err.should.include? "from -e:1:in `<main>'"
+ err.should =~ /from -e:1:in [`']<main>'/
end
end
end
diff --git a/spec/ruby/core/exception/no_method_error_spec.rb b/spec/ruby/core/exception/no_method_error_spec.rb
index f84f3418a4..26df3338e9 100644
--- a/spec/ruby/core/exception/no_method_error_spec.rb
+++ b/spec/ruby/core/exception/no_method_error_spec.rb
@@ -62,7 +62,7 @@ describe "NoMethodError#message" do
NoMethodErrorSpecs::NoMethodErrorC.new.a_private_method
rescue Exception => e
e.should be_kind_of(NoMethodError)
- e.message.lines[0].should =~ /private method `a_private_method' called for /
+ e.message.lines[0].should =~ /private method [`']a_private_method' called for /
end
end
@@ -125,21 +125,19 @@ describe "NoMethodError#message" do
end
end
- ruby_version_is "3.0" do
- it "uses #name to display the receiver if it is a class or a module" do
- klass = Class.new { def self.name; "MyClass"; end }
- begin
- klass.foo
- rescue NoMethodError => error
- error.message.lines.first.chomp.should =~ /^undefined method `foo' for /
- end
+ it "uses #name to display the receiver if it is a class or a module" do
+ klass = Class.new { def self.name; "MyClass"; end }
+ begin
+ klass.foo
+ rescue NoMethodError => error
+ error.message.lines.first.chomp.should =~ /^undefined method [`']foo' for /
+ end
- mod = Module.new { def self.name; "MyModule"; end }
- begin
- mod.foo
- rescue NoMethodError => error
- error.message.lines.first.chomp.should =~ /^undefined method `foo' for /
- end
+ mod = Module.new { def self.name; "MyModule"; end }
+ begin
+ mod.foo
+ rescue NoMethodError => error
+ error.message.lines.first.chomp.should =~ /^undefined method [`']foo' for /
end
end
end
diff --git a/spec/ruby/core/exception/set_backtrace_spec.rb b/spec/ruby/core/exception/set_backtrace_spec.rb
index ba2e1bf7aa..12c1da919c 100644
--- a/spec/ruby/core/exception/set_backtrace_spec.rb
+++ b/spec/ruby/core/exception/set_backtrace_spec.rb
@@ -11,9 +11,37 @@ describe "Exception#set_backtrace" do
it "allows the user to set the backtrace from a rescued exception" do
bt = ExceptionSpecs::Backtrace.backtrace
err = RuntimeError.new
+ err.backtrace.should == nil
+ err.backtrace_locations.should == nil
err.set_backtrace bt
+
err.backtrace.should == bt
+ err.backtrace_locations.should == nil
+ end
+
+ ruby_version_is "3.4" do
+ it "allows the user to set backtrace locations from a rescued exception" do
+ bt_locations = ExceptionSpecs::Backtrace.backtrace_locations
+ err = RuntimeError.new
+ err.backtrace.should == nil
+ err.backtrace_locations.should == nil
+
+ err.set_backtrace bt_locations
+
+ err.backtrace_locations.size.should == bt_locations.size
+ err.backtrace_locations.each_with_index do |loc, index|
+ other_loc = bt_locations[index]
+
+ loc.path.should == other_loc.path
+ loc.label.should == other_loc.label
+ loc.base_label.should == other_loc.base_label
+ loc.lineno.should == other_loc.lineno
+ loc.absolute_path.should == other_loc.absolute_path
+ loc.to_s.should == other_loc.to_s
+ end
+ err.backtrace.size.should == err.backtrace_locations.size
+ end
end
it "accepts an empty Array" do
diff --git a/spec/ruby/core/exception/syntax_error_spec.rb b/spec/ruby/core/exception/syntax_error_spec.rb
new file mode 100644
index 0000000000..6cc8522de3
--- /dev/null
+++ b/spec/ruby/core/exception/syntax_error_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "3.2" do
+ describe "SyntaxError#path" do
+ it "returns the file path provided to eval" do
+ filename = "speccing.rb"
+
+ -> {
+ eval("if true", TOPLEVEL_BINDING, filename)
+ }.should raise_error(SyntaxError) { |e|
+ e.path.should == filename
+ }
+ end
+
+ it "returns the file path that raised an exception" do
+ expected_path = fixture(__FILE__, "syntax_error.rb")
+
+ -> {
+ require_relative "fixtures/syntax_error"
+ }.should raise_error(SyntaxError) { |e| e.path.should == expected_path }
+ end
+
+ it "returns nil when constructed directly" do
+ SyntaxError.new.path.should == nil
+ end
+ end
+end
diff --git a/spec/ruby/core/exception/to_s_spec.rb b/spec/ruby/core/exception/to_s_spec.rb
index 4c4c7ab432..65c0d73a98 100644
--- a/spec/ruby/core/exception/to_s_spec.rb
+++ b/spec/ruby/core/exception/to_s_spec.rb
@@ -23,7 +23,7 @@ describe "NameError#to_s" do
begin
puts not_defined
rescue => exception
- exception.message.should =~ /undefined local variable or method `not_defined'/
+ exception.message.should =~ /undefined local variable or method [`']not_defined'/
end
end
diff --git a/spec/ruby/core/exception/top_level_spec.rb b/spec/ruby/core/exception/top_level_spec.rb
index bcd09205b6..cc961d06d5 100644
--- a/spec/ruby/core/exception/top_level_spec.rb
+++ b/spec/ruby/core/exception/top_level_spec.rb
@@ -2,30 +2,38 @@ require_relative '../../spec_helper'
describe "An Exception reaching the top level" do
it "is printed on STDERR" do
- ruby_exe('raise "foo"', args: "2>&1", exit_status: 1).should.include?("in `<main>': foo (RuntimeError)")
+ ruby_exe('raise "foo"', args: "2>&1", exit_status: 1).should =~ /in [`']<main>': foo \(RuntimeError\)/
end
it "the Exception#cause is printed to STDERR with backtraces" do
code = <<-RUBY
def raise_cause
- raise "the cause"
+ raise "the cause" # 2
end
def raise_wrapped
- raise "wrapped"
+ raise "wrapped" # 5
end
begin
- raise_cause
+ raise_cause # 8
rescue
- raise_wrapped
+ raise_wrapped # 10
end
RUBY
lines = ruby_exe(code, args: "2>&1", exit_status: 1).lines
- lines.reject! { |l| l.include?('rescue in') }
- lines.map! { |l| l.chomp[/:(in.+)/, 1] }
- lines.should == ["in `raise_wrapped': wrapped (RuntimeError)",
- "in `<main>'",
- "in `raise_cause': the cause (RuntimeError)",
- "in `<main>'"]
+
+ lines.map! { |l| l.chomp[/:(\d+:in.+)/, 1] }
+ lines[0].should =~ /\A5:in [`'](?:Object#)?raise_wrapped': wrapped \(RuntimeError\)\z/
+ if lines[1].include? 'rescue in'
+ # CRuby < 3.4 has an extra 'rescue in' backtrace entry
+ lines[1].should =~ /\A10:in [`']rescue in <main>'\z/
+ lines.delete_at 1
+ lines[1].should =~ /\A7:in [`']<main>'\z/
+ else
+ lines[1].should =~ /\A10:in [`']<main>'\z/
+ end
+ lines[2].should =~ /\A2:in [`'](?:Object#)?raise_cause': the cause \(RuntimeError\)\z/
+ lines[3].should =~ /\A8:in [`']<main>'\z/
+ lines.size.should == 4
end
describe "with a custom backtrace" do
diff --git a/spec/ruby/core/false/singleton_method_spec.rb b/spec/ruby/core/false/singleton_method_spec.rb
new file mode 100644
index 0000000000..738794b46c
--- /dev/null
+++ b/spec/ruby/core/false/singleton_method_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+
+describe "FalseClass#singleton_method" do
+ ruby_version_is '3.3' do
+ it "raises regardless of whether FalseClass defines the method" do
+ -> { false.singleton_method(:foo) }.should raise_error(NameError)
+ begin
+ def (false).foo; end
+ -> { false.singleton_method(:foo) }.should raise_error(NameError)
+ ensure
+ FalseClass.send(:remove_method, :foo)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/fiber/blocking_spec.rb b/spec/ruby/core/fiber/blocking_spec.rb
index eeee5a71c1..ebefa116af 100644
--- a/spec/ruby/core/fiber/blocking_spec.rb
+++ b/spec/ruby/core/fiber/blocking_spec.rb
@@ -1,61 +1,59 @@
require_relative '../../spec_helper'
require_relative 'shared/blocking'
-ruby_version_is "3.0" do
- require "fiber"
+require "fiber"
- describe "Fiber.blocking?" do
- it_behaves_like :non_blocking_fiber, -> { Fiber.blocking? }
+describe "Fiber.blocking?" do
+ it_behaves_like :non_blocking_fiber, -> { Fiber.blocking? }
- context "when fiber is blocking" do
- context "root Fiber of the main thread" do
- it "returns 1 for blocking: true" do
+ context "when fiber is blocking" do
+ context "root Fiber of the main thread" do
+ it "returns 1 for blocking: true" do
+ fiber = Fiber.new(blocking: true) { Fiber.blocking? }
+ blocking = fiber.resume
+
+ blocking.should == 1
+ end
+ end
+
+ context "root Fiber of a new thread" do
+ it "returns 1 for blocking: true" do
+ thread = Thread.new do
fiber = Fiber.new(blocking: true) { Fiber.blocking? }
blocking = fiber.resume
blocking.should == 1
end
+
+ thread.join
end
+ end
+ end
+end
- context "root Fiber of a new thread" do
- it "returns 1 for blocking: true" do
- thread = Thread.new do
- fiber = Fiber.new(blocking: true) { Fiber.blocking? }
- blocking = fiber.resume
+describe "Fiber#blocking?" do
+ it_behaves_like :non_blocking_fiber, -> { Fiber.current.blocking? }
- blocking.should == 1
- end
+ context "when fiber is blocking" do
+ context "root Fiber of the main thread" do
+ it "returns true for blocking: true" do
+ fiber = Fiber.new(blocking: true) { Fiber.current.blocking? }
+ blocking = fiber.resume
- thread.join
- end
+ blocking.should == true
end
end
- end
- describe "Fiber#blocking?" do
- it_behaves_like :non_blocking_fiber, -> { Fiber.current.blocking? }
-
- context "when fiber is blocking" do
- context "root Fiber of the main thread" do
- it "returns true for blocking: true" do
+ context "root Fiber of a new thread" do
+ it "returns true for blocking: true" do
+ thread = Thread.new do
fiber = Fiber.new(blocking: true) { Fiber.current.blocking? }
blocking = fiber.resume
blocking.should == true
end
- end
- context "root Fiber of a new thread" do
- it "returns true for blocking: true" do
- thread = Thread.new do
- fiber = Fiber.new(blocking: true) { Fiber.current.blocking? }
- blocking = fiber.resume
-
- blocking.should == true
- end
-
- thread.join
- end
+ thread.join
end
end
end
diff --git a/spec/ruby/core/fiber/inspect_spec.rb b/spec/ruby/core/fiber/inspect_spec.rb
index ee53af3a39..f20a153fc2 100644
--- a/spec/ruby/core/fiber/inspect_spec.rb
+++ b/spec/ruby/core/fiber/inspect_spec.rb
@@ -18,11 +18,9 @@ describe "Fiber#inspect" do
inspected.should =~ /\A#<Fiber:0x\h+ .+ \(resumed\)>\z/
end
- ruby_version_is "3.0" do
- it "is resumed for a Fiber which was transferred" do
- inspected = Fiber.new { Fiber.current.inspect }.transfer
- inspected.should =~ /\A#<Fiber:0x\h+ .+ \(resumed\)>\z/
- end
+ it "is resumed for a Fiber which was transferred" do
+ inspected = Fiber.new { Fiber.current.inspect }.transfer
+ inspected.should =~ /\A#<Fiber:0x\h+ .+ \(resumed\)>\z/
end
it "is suspended for a Fiber which was resumed and yielded" do
diff --git a/spec/ruby/core/fiber/raise_spec.rb b/spec/ruby/core/fiber/raise_spec.rb
index 09c4c1b524..b3e021e636 100644
--- a/spec/ruby/core/fiber/raise_spec.rb
+++ b/spec/ruby/core/fiber/raise_spec.rb
@@ -91,29 +91,49 @@ describe "Fiber#raise" do
fiber_two.resume.should == [:yield_one, :rescued]
end
-end
+ ruby_version_is "3.4" do
+ it "raises on the resumed fiber" do
+ root_fiber = Fiber.current
+ f1 = Fiber.new { root_fiber.transfer }
+ f2 = Fiber.new { f1.resume }
+ f2.transfer
+
+ -> do
+ f2.raise(RuntimeError, "Expected error")
+ end.should raise_error(RuntimeError, "Expected error")
+ end
-ruby_version_is ""..."3.0" do
- describe "Fiber#raise" do
- it "raises a FiberError if invoked on a transferring Fiber" do
- require "fiber"
- root = Fiber.current
- fiber = Fiber.new { root.transfer }
- fiber.transfer
- -> { fiber.raise }.should raise_error(FiberError, "cannot resume transferred Fiber")
+ it "raises on itself" do
+ -> do
+ Fiber.current.raise(RuntimeError, "Expected error")
+ end.should raise_error(RuntimeError, "Expected error")
+ end
+
+ it "should raise on parent fiber" do
+ f2 = nil
+ f1 = Fiber.new do
+ # This is equivalent to Kernel#raise:
+ f2.raise(RuntimeError, "Expected error")
+ end
+ f2 = Fiber.new do
+ f1.resume
+ end
+
+ -> do
+ f2.resume
+ end.should raise_error(RuntimeError, "Expected error")
end
end
end
-ruby_version_is "3.0" do
- describe "Fiber#raise" do
- it "transfers and raises on a transferring fiber" do
- require "fiber"
- root = Fiber.current
- fiber = Fiber.new { root.transfer }
- fiber.transfer
- -> { fiber.raise "msg" }.should raise_error(RuntimeError, "msg")
- end
+
+describe "Fiber#raise" do
+ it "transfers and raises on a transferring fiber" do
+ require "fiber"
+ root = Fiber.current
+ fiber = Fiber.new { root.transfer }
+ fiber.transfer
+ -> { fiber.raise "msg" }.should raise_error(RuntimeError, "msg")
end
end
diff --git a/spec/ruby/core/fiber/resume_spec.rb b/spec/ruby/core/fiber/resume_spec.rb
index 273bc866af..ab9a6799ab 100644
--- a/spec/ruby/core/fiber/resume_spec.rb
+++ b/spec/ruby/core/fiber/resume_spec.rb
@@ -28,18 +28,9 @@ describe "Fiber#resume" do
fiber.resume :second
end
- ruby_version_is '3.0' do
- it "raises a FiberError if the Fiber tries to resume itself" do
- fiber = Fiber.new { fiber.resume }
- -> { fiber.resume }.should raise_error(FiberError, /current fiber/)
- end
- end
-
- ruby_version_is '' ... '3.0' do
- it "raises a FiberError if the Fiber tries to resume itself" do
- fiber = Fiber.new { fiber.resume }
- -> { fiber.resume }.should raise_error(FiberError, /double resume/)
- end
+ it "raises a FiberError if the Fiber tries to resume itself" do
+ fiber = Fiber.new { fiber.resume }
+ -> { fiber.resume }.should raise_error(FiberError, /current fiber/)
end
it "returns control to the calling Fiber if called from one" do
diff --git a/spec/ruby/core/fiber/storage_spec.rb b/spec/ruby/core/fiber/storage_spec.rb
index e99fe6e4df..5c87ed5d41 100644
--- a/spec/ruby/core/fiber/storage_spec.rb
+++ b/spec/ruby/core/fiber/storage_spec.rb
@@ -1,9 +1,7 @@
require_relative '../../spec_helper'
-require 'fiber'
-
-describe "Fiber.new(storage:)" do
- ruby_version_is "3.2" do
+ruby_version_is "3.2" do
+ describe "Fiber.new(storage:)" do
it "creates a Fiber with the given storage" do
storage = {life: 42}
fiber = Fiber.new(storage: storage) { Fiber.current.storage }
@@ -24,11 +22,26 @@ describe "Fiber.new(storage:)" do
it "cannot create a fiber with non-hash storage" do
-> { Fiber.new(storage: 42) {} }.should raise_error(TypeError)
end
+
+ it "cannot create a fiber with a frozen hash as storage" do
+ -> { Fiber.new(storage: {life: 43}.freeze) {} }.should raise_error(FrozenError)
+ end
+
+ it "cannot create a fiber with a storage hash with non-symbol keys" do
+ -> { Fiber.new(storage: {life: 43, Object.new => 44}) {} }.should raise_error(TypeError)
+ end
end
-end
-describe "Fiber#storage=" do
- ruby_version_is "3.2" do
+ describe "Fiber#storage" do
+ it "cannot be accessed from a different fiber" do
+ f = Fiber.new(storage: {life: 42}) { nil }
+ -> {
+ f.storage
+ }.should raise_error(ArgumentError, /Fiber storage can only be accessed from the Fiber it belongs to/)
+ end
+ end
+
+ describe "Fiber#storage=" do
it "can clear the storage of the fiber" do
fiber = Fiber.new(storage: {life: 42}) do
Fiber.current.storage = nil
@@ -58,10 +71,8 @@ describe "Fiber#storage=" do
-> { Fiber.current.storage = {life: 43, Object.new => 44} }.should raise_error(TypeError)
end
end
-end
-describe "Fiber.[]" do
- ruby_version_is "3.2" do
+ describe "Fiber.[]" do
it "returns the value of the given key in the storage of the current fiber" do
Fiber.new(storage: {life: 42}) { Fiber[:life] }.resume.should == 42
end
@@ -73,11 +84,34 @@ describe "Fiber.[]" do
it "returns nil if the current fiber has no storage" do
Fiber.new { Fiber[:life] }.resume.should be_nil
end
+
+ ruby_version_is "3.2.3" do
+ it "can use dynamically defined keys" do
+ key = :"#{self.class.name}#.#{self.object_id}"
+ Fiber.new { Fiber[key] = 42; Fiber[key] }.resume.should == 42
+ end
+
+ it "can't use invalid keys" do
+ invalid_keys = [Object.new, "Foo", 12]
+ invalid_keys.each do |key|
+ -> { Fiber[key] }.should raise_error(TypeError)
+ end
+ end
+ end
+
+ it "can access the storage of the parent fiber" do
+ f = Fiber.new(storage: {life: 42}) do
+ Fiber.new { Fiber[:life] }.resume
+ end
+ f.resume.should == 42
+ end
+
+ it "can't access the storage of the fiber with non-symbol keys" do
+ -> { Fiber[Object.new] }.should raise_error(TypeError)
+ end
end
-end
-describe "Fiber.[]=" do
- ruby_version_is "3.2" do
+ describe "Fiber.[]=" do
it "sets the value of the given key in the storage of the current fiber" do
Fiber.new(storage: {life: 42}) { Fiber[:life] = 43; Fiber[:life] }.resume.should == 43
end
@@ -89,17 +123,31 @@ describe "Fiber.[]=" do
it "sets the value of the given key in the storage of the current fiber" do
Fiber.new { Fiber[:life] = 43; Fiber[:life] }.resume.should == 43
end
- end
- ruby_version_is "3.3" do
- it "deletes the fiber storage key when assigning nil" do
- Fiber.new(storage: {life: 42}) { Fiber[:life] = nil; Fiber.current.storage }.resume.should == {}
+ it "does not overwrite the storage of the parent fiber" do
+ f = Fiber.new(storage: {life: 42}) do
+ Fiber.yield Fiber.new { Fiber[:life] = 43; Fiber[:life] }.resume
+ Fiber[:life]
+ end
+ f.resume.should == 43 # Value of the inner fiber
+ f.resume.should == 42 # Value of the outer fiber
+ end
+
+ it "can't access the storage of the fiber with non-symbol keys" do
+ -> { Fiber[Object.new] = 44 }.should raise_error(TypeError)
+ end
+
+ ruby_version_is "3.3" do
+ it "deletes the fiber storage key when assigning nil" do
+ Fiber.new(storage: {life: 42}) {
+ Fiber[:life] = nil
+ Fiber.current.storage
+ }.resume.should == {}
+ end
end
end
-end
-describe "Thread.new" do
- ruby_version_is "3.2" do
+ describe "Thread.new" do
it "creates a thread with the storage of the current fiber" do
fiber = Fiber.new(storage: {life: 42}) do
Thread.new { Fiber.current.storage }.value
diff --git a/spec/ruby/core/file/absolute_path_spec.rb b/spec/ruby/core/file/absolute_path_spec.rb
index e35c80ec3c..315eead34f 100644
--- a/spec/ruby/core/file/absolute_path_spec.rb
+++ b/spec/ruby/core/file/absolute_path_spec.rb
@@ -85,7 +85,7 @@ describe "File.absolute_path" do
end
it "accepts a second argument of a directory from which to resolve the path" do
- File.absolute_path(__FILE__, File.dirname(__FILE__)).should == @abs
+ File.absolute_path(__FILE__, __dir__).should == @abs
end
it "calls #to_path on its argument" do
diff --git a/spec/ruby/core/file/atime_spec.rb b/spec/ruby/core/file/atime_spec.rb
index 1b47576e6b..e47e70e5ac 100644
--- a/spec/ruby/core/file/atime_spec.rb
+++ b/spec/ruby/core/file/atime_spec.rb
@@ -27,6 +27,9 @@ describe "File.atime" do
else
File.atime(__FILE__).usec.should == 0
end
+ rescue Errno::ENOENT => e
+ # Native Windows don't have stat command.
+ skip e.message
end
end
end
diff --git a/spec/ruby/core/file/ctime_spec.rb b/spec/ruby/core/file/ctime_spec.rb
index d17ba1a77f..718f26d5cc 100644
--- a/spec/ruby/core/file/ctime_spec.rb
+++ b/spec/ruby/core/file/ctime_spec.rb
@@ -22,6 +22,9 @@ describe "File.ctime" do
else
File.ctime(__FILE__).usec.should == 0
end
+ rescue Errno::ENOENT => e
+ # Windows don't have stat command.
+ skip e.message
end
end
diff --git a/spec/ruby/core/file/exist_spec.rb b/spec/ruby/core/file/exist_spec.rb
index ddb5febcba..2633376880 100644
--- a/spec/ruby/core/file/exist_spec.rb
+++ b/spec/ruby/core/file/exist_spec.rb
@@ -4,3 +4,11 @@ require_relative '../../shared/file/exist'
describe "File.exist?" do
it_behaves_like :file_exist, :exist?, File
end
+
+ruby_version_is "3.2" do
+ describe "File.exists?" do
+ it "has been removed" do
+ File.should_not.respond_to?(:exists?)
+ end
+ end
+end
diff --git a/spec/ruby/core/file/expand_path_spec.rb b/spec/ruby/core/file/expand_path_spec.rb
index c31f885b92..1abcf93900 100644
--- a/spec/ruby/core/file/expand_path_spec.rb
+++ b/spec/ruby/core/file/expand_path_spec.rb
@@ -137,7 +137,7 @@ describe "File.expand_path" do
it "returns a String in the same encoding as the argument" do
Encoding.default_external = Encoding::SHIFT_JIS
- path = "./a".force_encoding Encoding::CP1251
+ path = "./a".dup.force_encoding Encoding::CP1251
File.expand_path(path).encoding.should equal(Encoding::CP1251)
weird_path = [222, 173, 190, 175].pack('C*')
diff --git a/spec/ruby/core/file/flock_spec.rb b/spec/ruby/core/file/flock_spec.rb
index 751e99d994..070d830bc4 100644
--- a/spec/ruby/core/file/flock_spec.rb
+++ b/spec/ruby/core/file/flock_spec.rb
@@ -30,7 +30,7 @@ describe "File#flock" do
it "returns false if trying to lock an exclusively locked file" do
@file.flock File::LOCK_EX
- ruby_exe(<<-END_OF_CODE, escape: true).should == "false"
+ ruby_exe(<<-END_OF_CODE).should == "false"
File.open('#{@name}', "w") do |f2|
print f2.flock(File::LOCK_EX | File::LOCK_NB).to_s
end
@@ -40,7 +40,7 @@ describe "File#flock" do
it "blocks if trying to lock an exclusively locked file" do
@file.flock File::LOCK_EX
- out = ruby_exe(<<-END_OF_CODE, escape: true)
+ out = ruby_exe(<<-END_OF_CODE)
running = false
t = Thread.new do
diff --git a/spec/ruby/core/file/lutime_spec.rb b/spec/ruby/core/file/lutime_spec.rb
index 1f0625f61e..0f6df42ea3 100644
--- a/spec/ruby/core/file/lutime_spec.rb
+++ b/spec/ruby/core/file/lutime_spec.rb
@@ -1,7 +1,12 @@
require_relative '../../spec_helper'
+require_relative 'shared/update_time'
-describe "File.lutime" do
- platform_is_not :windows do
+platform_is_not :windows do
+ describe "File.lutime" do
+ it_behaves_like :update_time, :lutime
+ end
+
+ describe "File.lutime" do
before :each do
@atime = Time.utc(2000)
@mtime = Time.utc(2001)
diff --git a/spec/ruby/core/file/mtime_spec.rb b/spec/ruby/core/file/mtime_spec.rb
index 5304bbf057..0e9c95caee 100644
--- a/spec/ruby/core/file/mtime_spec.rb
+++ b/spec/ruby/core/file/mtime_spec.rb
@@ -26,6 +26,9 @@ describe "File.mtime" do
else
File.mtime(__FILE__).usec.should == 0
end
+ rescue Errno::ENOENT => e
+ # Windows don't have stat command.
+ skip e.message
end
end
end
diff --git a/spec/ruby/core/file/new_spec.rb b/spec/ruby/core/file/new_spec.rb
index a1ca46979e..1e82a070b1 100644
--- a/spec/ruby/core/file/new_spec.rb
+++ b/spec/ruby/core/file/new_spec.rb
@@ -100,7 +100,7 @@ describe "File.new" do
File.should.exist?(@file)
end
- it "raises an Errorno::EEXIST if the file exists when create a new file with File::CREAT|File::EXCL" do
+ it "raises an Errno::EEXIST if the file exists when create a new file with File::CREAT|File::EXCL" do
-> { @fh = File.new(@file, File::CREAT|File::EXCL) }.should raise_error(Errno::EEXIST)
end
@@ -168,16 +168,14 @@ describe "File.new" do
File.should.exist?(@file)
end
- ruby_version_is "3.0" do
- it "accepts options as a keyword argument" do
- @fh = File.new(@file, 'w', 0755, flags: @flags)
- @fh.should be_kind_of(File)
- @fh.close
+ it "accepts options as a keyword argument" do
+ @fh = File.new(@file, 'w', 0755, flags: @flags)
+ @fh.should be_kind_of(File)
+ @fh.close
- -> {
- @fh = File.new(@file, 'w', 0755, {flags: @flags})
- }.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 1..3)")
- end
+ -> {
+ @fh = File.new(@file, 'w', 0755, {flags: @flags})
+ }.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 1..3)")
end
it "bitwise-ORs mode and flags option" do
@@ -190,6 +188,12 @@ describe "File.new" do
}.should raise_error(Errno::EEXIST, /File exists/)
end
+ it "does not use the given block and warns to use File::open" do
+ -> {
+ @fh = File.new(@file) { raise }
+ }.should complain(/warning: File::new\(\) does not take block; use File::open\(\) instead/)
+ end
+
it "raises a TypeError if the first parameter can't be coerced to a string" do
-> { File.new(true) }.should raise_error(TypeError)
-> { File.new(false) }.should raise_error(TypeError)
diff --git a/spec/ruby/core/file/open_spec.rb b/spec/ruby/core/file/open_spec.rb
index 0c6d6cd19c..6bfc16bbf9 100644
--- a/spec/ruby/core/file/open_spec.rb
+++ b/spec/ruby/core/file/open_spec.rb
@@ -314,7 +314,7 @@ describe "File.open" do
end
end
- it "raises an IOError when read in a block opened with File::RDONLY|File::APPEND mode" do
+ it "raises an IOError when write in a block opened with File::RDONLY|File::APPEND mode" do
-> {
File.open(@file, File::RDONLY|File::APPEND ) do |f|
f.puts("writing")
@@ -354,7 +354,7 @@ describe "File.open" do
end
end
- it "raises an Errorno::EEXIST if the file exists when open with File::CREAT|File::EXCL" do
+ it "raises an Errno::EEXIST if the file exists when open with File::CREAT|File::EXCL" do
-> {
File.open(@file, File::CREAT|File::EXCL) do |f|
f.puts("writing")
@@ -423,7 +423,7 @@ describe "File.open" do
}.should raise_error(IOError)
end
- it "raises an Errorno::EEXIST if the file exists when open with File::RDONLY|File::TRUNC" do
+ it "raises an Errno::EEXIST if the file exists when open with File::RDONLY|File::TRUNC" do
-> {
File.open(@file, File::RDONLY|File::TRUNC) do |f|
f.puts("writing").should == nil
@@ -441,7 +441,7 @@ describe "File.open" do
}.should raise_error(Errno::EINVAL)
end
- it "raises an Errorno::EEXIST if the file exists when open with File::RDONLY|File::TRUNC" do
+ it "raises an Errno::EEXIST if the file exists when open with File::RDONLY|File::TRUNC" do
-> {
File.open(@file, File::RDONLY|File::TRUNC) do |f|
f.puts("writing").should == nil
@@ -565,15 +565,13 @@ describe "File.open" do
File.open(@file, 'wb+') {|f| f.external_encoding.should == Encoding::BINARY}
end
- ruby_version_is "3.0" do
- it "accepts options as a keyword argument" do
- @fh = File.open(@file, 'w', 0755, flags: File::CREAT)
- @fh.should be_an_instance_of(File)
+ it "accepts options as a keyword argument" do
+ @fh = File.open(@file, 'w', 0755, flags: File::CREAT)
+ @fh.should be_an_instance_of(File)
- -> {
- File.open(@file, 'w', 0755, {flags: File::CREAT})
- }.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 1..3)")
- end
+ -> {
+ File.open(@file, 'w', 0755, {flags: File::CREAT})
+ }.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 1..3)")
end
it "uses the second argument as an options Hash" do
diff --git a/spec/ruby/core/file/realpath_spec.rb b/spec/ruby/core/file/realpath_spec.rb
index bd27e09da6..bd25bfdecf 100644
--- a/spec/ruby/core/file/realpath_spec.rb
+++ b/spec/ruby/core/file/realpath_spec.rb
@@ -54,6 +54,10 @@ platform_is_not :windows do
File.realpath(@relative_symlink).should == @file
end
+ it "removes the file element when going one level up" do
+ File.realpath('../', @file).should == @real_dir
+ end
+
it "raises an Errno::ELOOP if the symlink points to itself" do
File.unlink @link
File.symlink(@link, @link)
diff --git a/spec/ruby/core/file/shared/fnmatch.rb b/spec/ruby/core/file/shared/fnmatch.rb
index 94f22144b0..db4b5c5d8c 100644
--- a/spec/ruby/core/file/shared/fnmatch.rb
+++ b/spec/ruby/core/file/shared/fnmatch.rb
@@ -102,6 +102,7 @@ describe :file_fnmatch, shared: true do
it "matches ranges of characters using exclusive bracket expression (e.g. [^t] or [!t])" do
File.send(@method, 'ca[^t]', 'cat').should == false
+ File.send(@method, 'ca[^t]', 'cas').should == true
File.send(@method, 'ca[!t]', 'cat').should == false
end
@@ -125,6 +126,13 @@ describe :file_fnmatch, shared: true do
end
end
+ it "matches wildcard with characters when flags includes FNM_PATHNAME" do
+ File.send(@method, '*a', 'aa', File::FNM_PATHNAME).should == true
+ File.send(@method, 'a*', 'aa', File::FNM_PATHNAME).should == true
+ File.send(@method, 'a*', 'aaa', File::FNM_PATHNAME).should == true
+ File.send(@method, '*a', 'aaa', File::FNM_PATHNAME).should == true
+ end
+
it "does not match '/' characters with ? or * when flags includes FNM_PATHNAME" do
File.send(@method, '?', '/', File::FNM_PATHNAME).should == false
File.send(@method, '*', '/', File::FNM_PATHNAME).should == false
@@ -165,9 +173,19 @@ describe :file_fnmatch, shared: true do
File.should_not.send(@method, '*/*', 'dave/.profile', File::FNM_PATHNAME)
end
- it "matches patterns with leading periods to dotfiles by default" do
+ it "matches patterns with leading periods to dotfiles" do
File.send(@method, '.*', '.profile').should == true
+ File.send(@method, '.*', '.profile', File::FNM_PATHNAME).should == true
File.send(@method, ".*file", "nondotfile").should == false
+ File.send(@method, ".*file", "nondotfile", File::FNM_PATHNAME).should == false
+ end
+
+ it "does not match directories with leading periods by default with FNM_PATHNAME" do
+ File.send(@method, '.*', '.directory/nondotfile', File::FNM_PATHNAME).should == false
+ File.send(@method, '.*', '.directory/.profile', File::FNM_PATHNAME).should == false
+ File.send(@method, '.*', 'foo/.directory/nondotfile', File::FNM_PATHNAME).should == false
+ File.send(@method, '.*', 'foo/.directory/.profile', File::FNM_PATHNAME).should == false
+ File.send(@method, '**/.dotfile', '.dotsubdir/.dotfile', File::FNM_PATHNAME).should == false
end
it "matches leading periods in filenames when flags includes FNM_DOTMATCH" do
@@ -221,6 +239,33 @@ describe :file_fnmatch, shared: true do
File.send(@method, pattern, 'a/.b/c/foo', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_true
end
+ it "has special handling for ./ when using * and FNM_PATHNAME" do
+ File.send(@method, './*', '.', File::FNM_PATHNAME).should be_false
+ File.send(@method, './*', './', File::FNM_PATHNAME).should be_true
+ File.send(@method, './*/', './', File::FNM_PATHNAME).should be_false
+ File.send(@method, './**', './', File::FNM_PATHNAME).should be_true
+ File.send(@method, './**/', './', File::FNM_PATHNAME).should be_true
+ File.send(@method, './*', '.', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_false
+ File.send(@method, './*', './', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_true
+ File.send(@method, './*/', './', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_false
+ File.send(@method, './**', './', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_true
+ File.send(@method, './**/', './', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_true
+ end
+
+ it "matches **/* with FNM_PATHNAME to recurse directories" do
+ File.send(@method, 'nested/**/*', 'nested/subdir', File::FNM_PATHNAME).should be_true
+ File.send(@method, 'nested/**/*', 'nested/subdir/file', File::FNM_PATHNAME).should be_true
+ File.send(@method, 'nested/**/*', 'nested/.dotsubdir', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_true
+ File.send(@method, 'nested/**/*', 'nested/.dotsubir/.dotfile', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_true
+ end
+
+ it "matches ** with FNM_PATHNAME only in current directory" do
+ File.send(@method, 'nested/**', 'nested/subdir', File::FNM_PATHNAME).should be_true
+ File.send(@method, 'nested/**', 'nested/subdir/file', File::FNM_PATHNAME).should be_false
+ File.send(@method, 'nested/**', 'nested/.dotsubdir', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_true
+ File.send(@method, 'nested/**', 'nested/.dotsubir/.dotfile', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_false
+ end
+
it "accepts an object that has a #to_path method" do
File.send(@method, '\*', mock_to_path('a')).should == false
end
diff --git a/spec/ruby/core/file/shared/path.rb b/spec/ruby/core/file/shared/path.rb
index ee8109ba05..aa2a64cf25 100644
--- a/spec/ruby/core/file/shared/path.rb
+++ b/spec/ruby/core/file/shared/path.rb
@@ -1,7 +1,7 @@
describe :file_path, shared: true do
before :each do
- @name = "file_to_path"
- @path = tmp(@name)
+ @path = tmp("file_to_path")
+ @name = File.basename(@path)
touch @path
end
diff --git a/spec/ruby/core/file/shared/update_time.rb b/spec/ruby/core/file/shared/update_time.rb
new file mode 100644
index 0000000000..9c063a8e93
--- /dev/null
+++ b/spec/ruby/core/file/shared/update_time.rb
@@ -0,0 +1,105 @@
+describe :update_time, shared: true do
+ before :all do
+ @time_is_float = platform_is :windows
+ end
+
+ before :each do
+ @atime = Time.now
+ @mtime = Time.now
+ @file1 = tmp("specs_file_utime1")
+ @file2 = tmp("specs_file_utime2")
+ touch @file1
+ touch @file2
+ end
+
+ after :each do
+ rm_r @file1, @file2
+ end
+
+ it "sets the access and modification time of each file" do
+ File.send(@method, @atime, @mtime, @file1, @file2)
+
+ if @time_is_float
+ File.atime(@file1).should be_close(@atime, 0.0001)
+ File.mtime(@file1).should be_close(@mtime, 0.0001)
+ File.atime(@file2).should be_close(@atime, 0.0001)
+ File.mtime(@file2).should be_close(@mtime, 0.0001)
+ else
+ File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
+ File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
+ File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
+ File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
+ end
+ end
+
+ it "uses the current times if two nil values are passed" do
+ tn = Time.now
+ File.send(@method, nil, nil, @file1, @file2)
+
+ if @time_is_float
+ File.atime(@file1).should be_close(tn, 0.050)
+ File.mtime(@file1).should be_close(tn, 0.050)
+ File.atime(@file2).should be_close(tn, 0.050)
+ File.mtime(@file2).should be_close(tn, 0.050)
+ else
+ File.atime(@file1).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
+ File.mtime(@file1).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
+ File.atime(@file2).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
+ File.mtime(@file2).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
+ end
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.send(@method, @atime, @mtime, mock_to_path(@file1), mock_to_path(@file2))
+ end
+
+ it "accepts numeric atime and mtime arguments" do
+ if @time_is_float
+ File.send(@method, @atime.to_f, @mtime.to_f, @file1, @file2)
+
+ File.atime(@file1).should be_close(@atime, 0.0001)
+ File.mtime(@file1).should be_close(@mtime, 0.0001)
+ File.atime(@file2).should be_close(@atime, 0.0001)
+ File.mtime(@file2).should be_close(@mtime, 0.0001)
+ else
+ File.send(@method, @atime.to_i, @mtime.to_i, @file1, @file2)
+
+ File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
+ File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
+ File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
+ File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
+ end
+ end
+
+ it "may set nanosecond precision" do
+ t = Time.utc(2007, 11, 1, 15, 25, 0, 123456.789r)
+ File.send(@method, t, t, @file1)
+
+ File.atime(@file1).nsec.should.between?(0, 123500000)
+ File.mtime(@file1).nsec.should.between?(0, 123500000)
+ end
+
+ it "returns the number of filenames in the arguments" do
+ File.send(@method, @atime.to_f, @mtime.to_f, @file1, @file2).should == 2
+ end
+
+ platform_is :linux do
+ platform_is wordsize: 64 do
+ it "allows Time instances in the far future to set mtime and atime (but some filesystems limit it up to 2446-05-10 or 2038-01-19 or 2486-07-02)" do
+ # https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Inode_Timestamps
+ # "Therefore, timestamps should not overflow until May 2446."
+ # https://lwn.net/Articles/804382/
+ # "On-disk timestamps hitting the y2038 limit..."
+ # The problem seems to be being improved, but currently it actually fails on XFS on RHEL8
+ # https://rubyci.org/logs/rubyci.s3.amazonaws.com/rhel8/ruby-master/log/20201112T123004Z.fail.html.gz
+ # Amazon Linux 2023 returns 2486-07-02 in this example
+ # http://rubyci.s3.amazonaws.com/amazon2023/ruby-master/log/20230322T063004Z.fail.html.gz
+ time = Time.at(1<<44)
+ File.send(@method, time, time, @file1)
+
+ [559444, 2486, 2446, 2038].should.include? File.atime(@file1).year
+ [559444, 2486, 2446, 2038].should.include? File.mtime(@file1).year
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/file/utime_spec.rb b/spec/ruby/core/file/utime_spec.rb
index 0b0e4f979c..d87626be50 100644
--- a/spec/ruby/core/file/utime_spec.rb
+++ b/spec/ruby/core/file/utime_spec.rb
@@ -1,102 +1,6 @@
require_relative '../../spec_helper'
+require_relative 'shared/update_time'
describe "File.utime" do
-
- before :all do
- @time_is_float = platform_is :windows
- end
-
- before :each do
- @atime = Time.now
- @mtime = Time.now
- @file1 = tmp("specs_file_utime1")
- @file2 = tmp("specs_file_utime2")
- touch @file1
- touch @file2
- end
-
- after :each do
- rm_r @file1, @file2
- end
-
- it "sets the access and modification time of each file" do
- File.utime(@atime, @mtime, @file1, @file2)
- if @time_is_float
- File.atime(@file1).should be_close(@atime, 0.0001)
- File.mtime(@file1).should be_close(@mtime, 0.0001)
- File.atime(@file2).should be_close(@atime, 0.0001)
- File.mtime(@file2).should be_close(@mtime, 0.0001)
- else
- File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
- File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
- File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
- File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
- end
- end
-
- it "uses the current times if two nil values are passed" do
- tn = Time.now
- File.utime(nil, nil, @file1, @file2)
- if @time_is_float
- File.atime(@file1).should be_close(tn, 0.050)
- File.mtime(@file1).should be_close(tn, 0.050)
- File.atime(@file2).should be_close(tn, 0.050)
- File.mtime(@file2).should be_close(tn, 0.050)
- else
- File.atime(@file1).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
- File.mtime(@file1).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
- File.atime(@file2).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
- File.mtime(@file2).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
- end
- end
-
- it "accepts an object that has a #to_path method" do
- File.utime(@atime, @mtime, mock_to_path(@file1), mock_to_path(@file2))
- end
-
- it "accepts numeric atime and mtime arguments" do
- if @time_is_float
- File.utime(@atime.to_f, @mtime.to_f, @file1, @file2)
- File.atime(@file1).should be_close(@atime, 0.0001)
- File.mtime(@file1).should be_close(@mtime, 0.0001)
- File.atime(@file2).should be_close(@atime, 0.0001)
- File.mtime(@file2).should be_close(@mtime, 0.0001)
- else
- File.utime(@atime.to_i, @mtime.to_i, @file1, @file2)
- File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
- File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
- File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
- File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
- end
- end
-
- it "may set nanosecond precision" do
- t = Time.utc(2007, 11, 1, 15, 25, 0, 123456.789r)
- File.utime(t, t, @file1)
- File.atime(@file1).nsec.should.between?(0, 123500000)
- File.mtime(@file1).nsec.should.between?(0, 123500000)
- end
-
- it "returns the number of filenames in the arguments" do
- File.utime(@atime.to_f, @mtime.to_f, @file1, @file2).should == 2
- end
-
- platform_is :linux do
- platform_is wordsize: 64 do
- it "allows Time instances in the far future to set mtime and atime (but some filesystems limit it up to 2446-05-10 or 2038-01-19 or 2486-07-02)" do
- # https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Inode_Timestamps
- # "Therefore, timestamps should not overflow until May 2446."
- # https://lwn.net/Articles/804382/
- # "On-disk timestamps hitting the y2038 limit..."
- # The problem seems to be being improved, but currently it actually fails on XFS on RHEL8
- # https://rubyci.org/logs/rubyci.s3.amazonaws.com/rhel8/ruby-master/log/20201112T123004Z.fail.html.gz
- # Amazon Linux 2023 returns 2486-07-02 in this example
- # http://rubyci.s3.amazonaws.com/amazon2023/ruby-master/log/20230322T063004Z.fail.html.gz
- time = Time.at(1<<44)
- File.utime(time, time, @file1)
- [559444, 2486, 2446, 2038].should.include? File.atime(@file1).year
- [559444, 2486, 2446, 2038].should.include? File.mtime(@file1).year
- end
- end
- end
+ it_behaves_like :update_time, :utime
end
diff --git a/spec/ruby/core/filetest/exist_spec.rb b/spec/ruby/core/filetest/exist_spec.rb
index 4d14bea231..a95d3f91a1 100644
--- a/spec/ruby/core/filetest/exist_spec.rb
+++ b/spec/ruby/core/filetest/exist_spec.rb
@@ -4,3 +4,11 @@ require_relative '../../shared/file/exist'
describe "FileTest.exist?" do
it_behaves_like :file_exist, :exist?, FileTest
end
+
+ruby_version_is "3.2" do
+ describe "FileTest.exists?" do
+ it "has been removed" do
+ FileTest.should_not.respond_to?(:exists?)
+ end
+ end
+end
diff --git a/spec/ruby/core/gc/auto_compact_spec.rb b/spec/ruby/core/gc/auto_compact_spec.rb
index 4f9d043171..33ad1cb56c 100644
--- a/spec/ruby/core/gc/auto_compact_spec.rb
+++ b/spec/ruby/core/gc/auto_compact_spec.rb
@@ -1,26 +1,24 @@
require_relative '../../spec_helper'
-ruby_version_is "3.0" do
- describe "GC.auto_compact" do
- it "can set and get a boolean value" do
- begin
- GC.auto_compact = GC.auto_compact
- rescue NotImplementedError # platform does not support autocompact
- skip
- end
+describe "GC.auto_compact" do
+ it "can set and get a boolean value" do
+ begin
+ GC.auto_compact = GC.auto_compact
+ rescue NotImplementedError # platform does not support autocompact
+ skip
+ end
- original = GC.auto_compact
- begin
- GC.auto_compact = !original
- rescue NotImplementedError # platform does not support autocompact
- skip
- end
+ original = GC.auto_compact
+ begin
+ GC.auto_compact = !original
+ rescue NotImplementedError # platform does not support autocompact
+ skip
+ end
- begin
- GC.auto_compact.should == !original
- ensure
- GC.auto_compact = original
- end
+ begin
+ GC.auto_compact.should == !original
+ ensure
+ GC.auto_compact = original
end
end
end
diff --git a/spec/ruby/core/hash/assoc_spec.rb b/spec/ruby/core/hash/assoc_spec.rb
index 64442918d1..62b2a11b30 100644
--- a/spec/ruby/core/hash/assoc_spec.rb
+++ b/spec/ruby/core/hash/assoc_spec.rb
@@ -22,11 +22,11 @@ describe "Hash#assoc" do
end
it "only returns the first matching key-value pair for identity hashes" do
- # Avoid literal String keys in Hash#[]= due to https://bugs.ruby-lang.org/issues/12855
+ # Avoid literal String keys since string literals can be frozen and interned e.g. with --enable-frozen-string-literal
h = {}.compare_by_identity
- k1 = 'pear'
+ k1 = 'pear'.dup
h[k1] = :red
- k2 = 'pear'
+ k2 = 'pear'.dup
h[k2] = :green
h.size.should == 2
h.keys.grep(/pear/).size.should == 2
diff --git a/spec/ruby/core/hash/compare_by_identity_spec.rb b/spec/ruby/core/hash/compare_by_identity_spec.rb
index 874cd46eb7..2975526a97 100644
--- a/spec/ruby/core/hash/compare_by_identity_spec.rb
+++ b/spec/ruby/core/hash/compare_by_identity_spec.rb
@@ -85,19 +85,21 @@ describe "Hash#compare_by_identity" do
-> { @h.compare_by_identity }.should raise_error(FrozenError)
end
- # Behaviour confirmed in bug #1871
+ # Behaviour confirmed in https://bugs.ruby-lang.org/issues/1871
it "persists over #dups" do
- @idh['foo'] = :bar
- @idh['foo'] = :glark
+ @idh['foo'.dup] = :bar
+ @idh['foo'.dup] = :glark
@idh.dup.should == @idh
@idh.dup.size.should == @idh.size
+ @idh.dup.should.compare_by_identity?
end
it "persists over #clones" do
- @idh['foo'] = :bar
- @idh['foo'] = :glark
+ @idh['foo'.dup] = :bar
+ @idh['foo'.dup] = :glark
@idh.clone.should == @idh
@idh.clone.size.should == @idh.size
+ @idh.dup.should.compare_by_identity?
end
it "does not copy string keys" do
@@ -108,9 +110,16 @@ describe "Hash#compare_by_identity" do
@idh.keys.first.should equal foo
end
+ # Check `#[]=` call with a String literal.
+ # Don't use `#+` because with `#+` it's no longer a String literal.
+ #
+ # See https://bugs.ruby-lang.org/issues/12855
it "gives different identity for string literals" do
+ eval <<~RUBY
+ # frozen_string_literal: false
@idh['foo'] = 1
@idh['foo'] = 2
+ RUBY
@idh.values.should == [1, 2]
@idh.size.should == 2
end
diff --git a/spec/ruby/core/hash/delete_spec.rb b/spec/ruby/core/hash/delete_spec.rb
index b262e8846b..3e3479c69c 100644
--- a/spec/ruby/core/hash/delete_spec.rb
+++ b/spec/ruby/core/hash/delete_spec.rb
@@ -24,7 +24,7 @@ describe "Hash#delete" do
it "allows removing a key while iterating" do
h = { a: 1, b: 2 }
visited = []
- h.each_pair { |k,v|
+ h.each_pair { |k, v|
visited << k
h.delete(k)
}
@@ -32,13 +32,27 @@ describe "Hash#delete" do
h.should == {}
end
+ it "allows removing a key while iterating for big hashes" do
+ h = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10,
+ k: 11, l: 12, m: 13, n: 14, o: 15, p: 16, q: 17, r: 18, s: 19, t: 20,
+ u: 21, v: 22, w: 23, x: 24, y: 25, z: 26 }
+ visited = []
+ h.each_pair { |k, v|
+ visited << k
+ h.delete(k)
+ }
+ visited.should == [:a, :b, :c, :d, :e, :f, :g, :h, :i, :j, :k, :l, :m,
+ :n, :o, :p, :q, :r, :s, :t, :u, :v, :w, :x, :y, :z]
+ h.should == {}
+ end
+
it "accepts keys with private #hash method" do
key = HashSpecs::KeyWithPrivateHash.new
{ key => 5 }.delete(key).should == 5
end
it "raises a FrozenError if called on a frozen instance" do
- -> { HashSpecs.frozen_hash.delete("foo") }.should raise_error(FrozenError)
+ -> { HashSpecs.frozen_hash.delete("foo") }.should raise_error(FrozenError)
-> { HashSpecs.empty_frozen_hash.delete("foo") }.should raise_error(FrozenError)
end
end
diff --git a/spec/ruby/core/hash/element_reference_spec.rb b/spec/ruby/core/hash/element_reference_spec.rb
index e271f37ea6..94e8237839 100644
--- a/spec/ruby/core/hash/element_reference_spec.rb
+++ b/spec/ruby/core/hash/element_reference_spec.rb
@@ -30,7 +30,7 @@ describe "Hash#[]" do
end
it "does not create copies of the immediate default value" do
- str = "foo"
+ str = +"foo"
h = Hash.new(str)
a = h[:a]
b = h[:b]
diff --git a/spec/ruby/core/hash/except_spec.rb b/spec/ruby/core/hash/except_spec.rb
index 82cfced72f..ac84f9975c 100644
--- a/spec/ruby/core/hash/except_spec.rb
+++ b/spec/ruby/core/hash/except_spec.rb
@@ -1,34 +1,32 @@
require_relative '../../spec_helper'
-ruby_version_is "3.0" do
- describe "Hash#except" do
- before :each do
- @hash = { a: 1, b: 2, c: 3 }
- end
+describe "Hash#except" do
+ before :each do
+ @hash = { a: 1, b: 2, c: 3 }
+ end
- it "returns a new duplicate hash without arguments" do
- ret = @hash.except
- ret.should_not equal(@hash)
- ret.should == @hash
- end
+ it "returns a new duplicate hash without arguments" do
+ ret = @hash.except
+ ret.should_not equal(@hash)
+ ret.should == @hash
+ end
- it "returns a hash without the requested subset" do
- @hash.except(:c, :a).should == { b: 2 }
- end
+ it "returns a hash without the requested subset" do
+ @hash.except(:c, :a).should == { b: 2 }
+ end
- it "ignores keys not present in the original hash" do
- @hash.except(:a, :chunky_bacon).should == { b: 2, c: 3 }
- end
+ it "ignores keys not present in the original hash" do
+ @hash.except(:a, :chunky_bacon).should == { b: 2, c: 3 }
+ end
- it "always returns a Hash without a default" do
- klass = Class.new(Hash)
- h = klass.new(:default)
- h[:bar] = 12
- h[:foo] = 42
- r = h.except(:foo)
- r.should == {bar: 12}
- r.class.should == Hash
- r.default.should == nil
- end
+ it "always returns a Hash without a default" do
+ klass = Class.new(Hash)
+ h = klass.new(:default)
+ h[:bar] = 12
+ h[:foo] = 42
+ r = h.except(:foo)
+ r.should == {bar: 12}
+ r.class.should == Hash
+ r.default.should == nil
end
end
diff --git a/spec/ruby/core/hash/hash_spec.rb b/spec/ruby/core/hash/hash_spec.rb
index 2ccb483120..19eb806dc4 100644
--- a/spec/ruby/core/hash/hash_spec.rb
+++ b/spec/ruby/core/hash/hash_spec.rb
@@ -43,7 +43,7 @@ describe "Hash#hash" do
end
ruby_version_is "3.1" do
- it "allows ommiting values" do
+ it "allows omitting values" do
a = 1
b = 2
diff --git a/spec/ruby/core/hash/index_spec.rb b/spec/ruby/core/hash/index_spec.rb
deleted file mode 100644
index be4e2cc6ab..0000000000
--- a/spec/ruby/core/hash/index_spec.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/index'
-
-ruby_version_is ''...'3.0' do
- describe "Hash#index" do
- it_behaves_like :hash_index, :index
- end
-end
diff --git a/spec/ruby/core/hash/rehash_spec.rb b/spec/ruby/core/hash/rehash_spec.rb
index 0049080456..db3e91b166 100644
--- a/spec/ruby/core/hash/rehash_spec.rb
+++ b/spec/ruby/core/hash/rehash_spec.rb
@@ -77,6 +77,36 @@ describe "Hash#rehash" do
h.keys.should_not.include? [1]
end
+ it "iterates keys in insertion order" do
+ key = Class.new do
+ attr_reader :name
+
+ def initialize(name)
+ @name = name
+ end
+
+ def hash
+ 123
+ end
+ end
+
+ a, b, c, d = key.new('a'), key.new('b'), key.new('c'), key.new('d')
+ h = { a => 1, b => 2, c => 3, d => 4 }
+ h.size.should == 4
+
+ key.class_exec do
+ def eql?(other)
+ true
+ end
+ end
+
+ h.rehash
+ h.size.should == 1
+ k, v = h.first
+ k.name.should == 'a'
+ v.should == 4
+ end
+
it "raises a FrozenError if called on a frozen instance" do
-> { HashSpecs.frozen_hash.rehash }.should raise_error(FrozenError)
-> { HashSpecs.empty_frozen_hash.rehash }.should raise_error(FrozenError)
diff --git a/spec/ruby/core/hash/shared/each.rb b/spec/ruby/core/hash/shared/each.rb
index b2483c8116..f9839ff58f 100644
--- a/spec/ruby/core/hash/shared/each.rb
+++ b/spec/ruby/core/hash/shared/each.rb
@@ -21,37 +21,18 @@ describe :hash_each, shared: true do
ary.sort.should == ["a", "b", "c"]
end
- ruby_version_is ""..."3.0" do
- it "yields 2 values and not an Array of 2 elements when given a callable of arity 2" do
- obj = Object.new
- def obj.foo(key, value)
- ScratchPad << key << value
- end
-
- ScratchPad.record([])
- { "a" => 1 }.send(@method, &obj.method(:foo))
- ScratchPad.recorded.should == ["a", 1]
-
- ScratchPad.record([])
- { "a" => 1 }.send(@method, &-> key, value { ScratchPad << key << value })
- ScratchPad.recorded.should == ["a", 1]
+ it "always yields an Array of 2 elements, even when given a callable of arity 2" do
+ obj = Object.new
+ def obj.foo(key, value)
end
- end
-
- ruby_version_is "3.0" do
- it "always yields an Array of 2 elements, even when given a callable of arity 2" do
- obj = Object.new
- def obj.foo(key, value)
- end
- -> {
- { "a" => 1 }.send(@method, &obj.method(:foo))
- }.should raise_error(ArgumentError)
+ -> {
+ { "a" => 1 }.send(@method, &obj.method(:foo))
+ }.should raise_error(ArgumentError)
- -> {
- { "a" => 1 }.send(@method, &-> key, value { })
- }.should raise_error(ArgumentError)
- end
+ -> {
+ { "a" => 1 }.send(@method, &-> key, value { })
+ }.should raise_error(ArgumentError)
end
it "yields an Array of 2 elements when given a callable of arity 1" do
diff --git a/spec/ruby/core/hash/shared/equal.rb b/spec/ruby/core/hash/shared/equal.rb
deleted file mode 100644
index 43606437fe..0000000000
--- a/spec/ruby/core/hash/shared/equal.rb
+++ /dev/null
@@ -1,90 +0,0 @@
-describe :hash_equal, shared: true do
- it "does not compare values when keys don't match" do
- value = mock('x')
- value.should_not_receive(:==)
- value.should_not_receive(:eql?)
- { 1 => value }.send(@method, { 2 => value }).should be_false
- end
-
- it "returns false when the numbers of keys differ without comparing any elements" do
- obj = mock('x')
- h = { obj => obj }
-
- obj.should_not_receive(:==)
- obj.should_not_receive(:eql?)
-
- {}.send(@method, h).should be_false
- h.send(@method, {}).should be_false
- end
-
- it "first compares keys via hash" do
- x = mock('x')
- x.should_receive(:hash).and_return(0)
- y = mock('y')
- y.should_receive(:hash).and_return(0)
-
- { x => 1 }.send(@method, { y => 1 }).should be_false
- end
-
- it "does not compare keys with different hash codes via eql?" do
- x = mock('x')
- y = mock('y')
- x.should_not_receive(:eql?)
- y.should_not_receive(:eql?)
-
- x.should_receive(:hash).and_return(0)
- y.should_receive(:hash).and_return(1)
-
- def x.hash() 0 end
- def y.hash() 1 end
-
- { x => 1 }.send(@method, { y => 1 }).should be_false
- end
-
- it "computes equality for recursive hashes" do
- h = {}
- h[:a] = h
- h.send(@method, h[:a]).should be_true
- (h == h[:a]).should be_true
- end
-
- it "computes equality for complex recursive hashes" do
- a, b = {}, {}
- a.merge! self: a, other: b
- b.merge! self: b, other: a
- a.send(@method, b).should be_true # they both have the same structure!
-
- c = {}
- c.merge! other: c, self: c
- c.send(@method, a).should be_true # subtle, but they both have the same structure!
- a[:delta] = c[:delta] = a
- c.send(@method, a).should be_false # not quite the same structure, as a[:other][:delta] = nil
- c[:delta] = 42
- c.send(@method, a).should be_false
- a[:delta] = 42
- c.send(@method, a).should be_false
- b[:delta] = 42
- c.send(@method, a).should be_true
- end
-
- it "computes equality for recursive hashes & arrays" do
- x, y, z = [], [], []
- a, b, c = {foo: x, bar: 42}, {foo: y, bar: 42}, {foo: z, bar: 42}
- x << a
- y << c
- z << b
- b.send(@method, c).should be_true # they clearly have the same structure!
- y.send(@method, z).should be_true
- a.send(@method, b).should be_true # subtle, but they both have the same structure!
- x.send(@method, y).should be_true
- y << x
- y.send(@method, z).should be_false
- z << x
- y.send(@method, z).should be_true
-
- a[:foo], a[:bar] = a[:bar], a[:foo]
- a.send(@method, b).should be_false
- b[:bar] = b[:foo]
- b.send(@method, c).should be_false
- end
-end
diff --git a/spec/ruby/core/hash/shared/store.rb b/spec/ruby/core/hash/shared/store.rb
index b823ea45ca..dd1bb52bac 100644
--- a/spec/ruby/core/hash/shared/store.rb
+++ b/spec/ruby/core/hash/shared/store.rb
@@ -9,7 +9,7 @@ describe :hash_store, shared: true do
it "duplicates string keys using dup semantics" do
# dup doesn't copy singleton methods
- key = "foo"
+ key = +"foo"
def key.reverse() "bar" end
h = {}
h.send(@method, key, 0)
@@ -44,7 +44,7 @@ describe :hash_store, shared: true do
end
it "duplicates and freezes string keys" do
- key = "foo"
+ key = +"foo"
h = {}
h.send(@method, key, 0)
key << "bar"
@@ -75,8 +75,8 @@ describe :hash_store, shared: true do
it "keeps the existing String key in the hash if there is a matching one" do
h = { "a" => 1, "b" => 2, "c" => 3, "d" => 4 }
- key1 = "foo"
- key2 = "foo"
+ key1 = "foo".dup
+ key2 = "foo".dup
key1.should_not equal(key2)
h[key1] = 41
frozen_key = h.keys.last
diff --git a/spec/ruby/core/hash/shared/to_s.rb b/spec/ruby/core/hash/shared/to_s.rb
index 2db3a96583..7864d7cd4c 100644
--- a/spec/ruby/core/hash/shared/to_s.rb
+++ b/spec/ruby/core/hash/shared/to_s.rb
@@ -24,7 +24,7 @@ describe :hash_to_s, shared: true do
end
it "does not call #to_s on a String returned from #inspect" do
- str = "abc"
+ str = +"abc"
str.should_not_receive(:to_s)
{ a: str }.send(@method).should == '{:a=>"abc"}'
@@ -78,7 +78,7 @@ describe :hash_to_s, shared: true do
it "does not raise if inspected result is not default external encoding" do
utf_16be = mock("utf_16be")
- utf_16be.should_receive(:inspect).and_return(%<"utf_16be \u3042">.encode!(Encoding::UTF_16BE))
+ utf_16be.should_receive(:inspect).and_return(%<"utf_16be \u3042">.encode(Encoding::UTF_16BE))
{a: utf_16be}.send(@method).should == '{:a=>"utf_16be \u3042"}'
end
diff --git a/spec/ruby/core/hash/to_a_spec.rb b/spec/ruby/core/hash/to_a_spec.rb
index 8b7894a2ba..5baf677929 100644
--- a/spec/ruby/core/hash/to_a_spec.rb
+++ b/spec/ruby/core/hash/to_a_spec.rb
@@ -26,14 +26,4 @@ describe "Hash#to_a" do
ent.should be_kind_of(Array)
ent.should == pairs
end
-
- ruby_version_is ''...'3.0' do
- it "returns a not tainted array if self is tainted" do
- {}.taint.to_a.tainted?.should be_false
- end
-
- it "returns a trusted array if self is untrusted" do
- {}.untrust.to_a.untrusted?.should be_false
- end
- end
end
diff --git a/spec/ruby/core/hash/to_proc_spec.rb b/spec/ruby/core/hash/to_proc_spec.rb
index 8f5d21beb5..9dbc79e5eb 100644
--- a/spec/ruby/core/hash/to_proc_spec.rb
+++ b/spec/ruby/core/hash/to_proc_spec.rb
@@ -19,20 +19,12 @@ describe "Hash#to_proc" do
@proc = @hash.to_proc
end
- ruby_version_is ""..."3.0" do
- it "is not a lambda" do
- @proc.should_not.lambda?
- end
+ it "is a lambda" do
+ @proc.should.lambda?
end
- ruby_version_is "3.0" do
- it "is a lambda" do
- @proc.should.lambda?
- end
-
- it "has an arity of 1" do
- @proc.arity.should == 1
- end
+ it "has an arity of 1" do
+ @proc.arity.should == 1
end
it "raises ArgumentError if not passed exactly one argument" do
diff --git a/spec/ruby/core/hash/transform_keys_spec.rb b/spec/ruby/core/hash/transform_keys_spec.rb
index 361089ca97..2fbb17a8e2 100644
--- a/spec/ruby/core/hash/transform_keys_spec.rb
+++ b/spec/ruby/core/hash/transform_keys_spec.rb
@@ -43,18 +43,16 @@ describe "Hash#transform_keys" do
r.class.should == Hash
end
- ruby_version_is "3.0" do
- it "allows a hash argument" do
- @hash.transform_keys({ a: :A, b: :B, c: :C }).should == { A: 1, B: 2, C: 3 }
- end
+ it "allows a hash argument" do
+ @hash.transform_keys({ a: :A, b: :B, c: :C }).should == { A: 1, B: 2, C: 3 }
+ end
- it "allows a partial transformation of keys when using a hash argument" do
- @hash.transform_keys({ a: :A, c: :C }).should == { A: 1, b: 2, C: 3 }
- end
+ it "allows a partial transformation of keys when using a hash argument" do
+ @hash.transform_keys({ a: :A, c: :C }).should == { A: 1, b: 2, C: 3 }
+ end
- it "allows a combination of hash and block argument" do
- @hash.transform_keys({ a: :A }, &:to_s).should == { A: 1, 'b' => 2, 'c' => 3 }
- end
+ it "allows a combination of hash and block argument" do
+ @hash.transform_keys({ a: :A }, &:to_s).should == { A: 1, 'b' => 2, 'c' => 3 }
end
end
@@ -111,11 +109,9 @@ describe "Hash#transform_keys!" do
end
end
- ruby_version_is "3.0" do
- it "allows a hash argument" do
- @hash.transform_keys!({ a: :A, b: :B, c: :C, d: :D })
- @hash.should == { A: 1, B: 2, C: 3, D: 4 }
- end
+ it "allows a hash argument" do
+ @hash.transform_keys!({ a: :A, b: :B, c: :C, d: :D })
+ @hash.should == { A: 1, B: 2, C: 3, D: 4 }
end
describe "on frozen instance" do
@@ -132,10 +128,8 @@ describe "Hash#transform_keys!" do
@hash.should == @initial_pairs
end
- ruby_version_is "3.0" do
- it "raises a FrozenError on hash argument" do
- ->{ @hash.transform_keys!({ a: :A, b: :B, c: :C }) }.should raise_error(FrozenError)
- end
+ it "raises a FrozenError on hash argument" do
+ ->{ @hash.transform_keys!({ a: :A, b: :B, c: :C }) }.should raise_error(FrozenError)
end
context "when no block is given" do
diff --git a/spec/ruby/core/integer/coerce_spec.rb b/spec/ruby/core/integer/coerce_spec.rb
index f1f3256032..1d6dc9713f 100644
--- a/spec/ruby/core/integer/coerce_spec.rb
+++ b/spec/ruby/core/integer/coerce_spec.rb
@@ -1,7 +1,5 @@
require_relative '../../spec_helper'
-require 'bigdecimal'
-
describe "Integer#coerce" do
context "fixnum" do
describe "when given a Fixnum" do
@@ -90,15 +88,4 @@ describe "Integer#coerce" do
ary.should == [1.2, a.to_f]
end
end
-
- context "bigdecimal" do
- it "produces Floats" do
- x, y = 3.coerce(BigDecimal("3.4"))
- x.class.should == Float
- x.should == 3.4
- y.class.should == Float
- y.should == 3.0
- end
- end
-
end
diff --git a/spec/ruby/core/integer/div_spec.rb b/spec/ruby/core/integer/div_spec.rb
index 344e095179..2eb9c0623b 100644
--- a/spec/ruby/core/integer/div_spec.rb
+++ b/spec/ruby/core/integer/div_spec.rb
@@ -143,4 +143,12 @@ describe "Integer#div" do
-> { @bignum.div(-0) }.should raise_error(ZeroDivisionError)
end
end
+
+ context "rational" do
+ it "returns self divided by the given argument as an Integer" do
+ 2.div(6/5r).should == 1
+ 1.div(6/5r).should == 0
+ 5.div(6/5r).should == 4
+ end
+ end
end
diff --git a/spec/ruby/core/integer/shared/arithmetic_coerce.rb b/spec/ruby/core/integer/shared/arithmetic_coerce.rb
index 4c0cbcb999..1260192df1 100644
--- a/spec/ruby/core/integer/shared/arithmetic_coerce.rb
+++ b/spec/ruby/core/integer/shared/arithmetic_coerce.rb
@@ -1,25 +1,5 @@
require_relative '../fixtures/classes'
-describe :integer_arithmetic_coerce_rescue, shared: true do
- it "rescues exception (StandardError and subclasses) raised in other#coerce and raises TypeError" do
- b = mock("numeric with failed #coerce")
- b.should_receive(:coerce).and_raise(IntegerSpecs::CoerceError)
-
- # e.g. 1 + b
- -> { 1.send(@method, b) }.should raise_error(TypeError, /MockObject can't be coerced into Integer/)
- end
-
- it "does not rescue Exception and StandardError siblings raised in other#coerce" do
- [Exception, NoMemoryError].each do |exception|
- b = mock("numeric with failed #coerce")
- b.should_receive(:coerce).and_raise(exception)
-
- # e.g. 1 + b
- -> { 1.send(@method, b) }.should raise_error(exception)
- end
- end
-end
-
describe :integer_arithmetic_coerce_not_rescue, shared: true do
it "does not rescue exception raised in other#coerce" do
b = mock("numeric with failed #coerce")
diff --git a/spec/ruby/core/integer/zero_spec.rb b/spec/ruby/core/integer/zero_spec.rb
index 2dac50c406..bd362c4181 100644
--- a/spec/ruby/core/integer/zero_spec.rb
+++ b/spec/ruby/core/integer/zero_spec.rb
@@ -7,15 +7,7 @@ describe "Integer#zero?" do
-1.should_not.zero?
end
- ruby_version_is "3.0" do
- it "Integer#zero? overrides Numeric#zero?" do
- 42.method(:zero?).owner.should == Integer
- end
- end
-
- ruby_version_is ""..."3.0" do
- it "Integer#zero? uses Numeric#zero?" do
- 42.method(:zero?).owner.should == Numeric
- end
+ it "Integer#zero? overrides Numeric#zero?" do
+ 42.method(:zero?).owner.should == Integer
end
end
diff --git a/spec/ruby/core/io/binread_spec.rb b/spec/ruby/core/io/binread_spec.rb
index a3f752d8f9..418e89213b 100644
--- a/spec/ruby/core/io/binread_spec.rb
+++ b/spec/ruby/core/io/binread_spec.rb
@@ -44,4 +44,14 @@ describe "IO.binread" do
it "raises an Errno::EINVAL when not passed a valid offset" do
-> { IO.binread @fname, 0, -1 }.should raise_error(Errno::EINVAL)
end
+
+ ruby_version_is "3.3" do
+ # https://bugs.ruby-lang.org/issues/19630
+ it "warns about deprecation given a path with a pipe" do
+ cmd = "|echo ok"
+ -> {
+ IO.binread(cmd)
+ }.should complain(/IO process creation with a leading '\|'/)
+ end
+ end
end
diff --git a/spec/ruby/core/io/bytes_spec.rb b/spec/ruby/core/io/bytes_spec.rb
deleted file mode 100644
index 6e328983f2..0000000000
--- a/spec/ruby/core/io/bytes_spec.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-# -*- encoding: utf-8 -*-
-require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-
-ruby_version_is ''...'3.0' do
- describe "IO#bytes" do
- before :each do
- @io = IOSpecs.io_fixture "lines.txt"
- @verbose, $VERBOSE = $VERBOSE, nil
- end
-
- after :each do
- $VERBOSE = @verbose
- @io.close unless @io.closed?
- end
-
- it "returns an enumerator of the next bytes from the stream" do
- enum = @io.bytes
- enum.should be_an_instance_of(Enumerator)
- @io.readline.should == "Voici la ligne une.\n"
- enum.first(5).should == [81, 117, 105, 32, 195]
- end
-
- it "yields each byte" do
- count = 0
- ScratchPad.record []
- @io.each_byte do |byte|
- ScratchPad << byte
- break if 4 < count += 1
- end
-
- ScratchPad.recorded.should == [86, 111, 105, 99, 105]
- end
-
- it "raises an IOError on closed stream" do
- enum = IOSpecs.closed_io.bytes
- -> { enum.first }.should raise_error(IOError)
- end
-
- it "raises an IOError on an enumerator for a stream that has been closed" do
- enum = @io.bytes
- enum.first.should == 86
- @io.close
- -> { enum.first }.should raise_error(IOError)
- end
- end
-end
diff --git a/spec/ruby/core/io/chars_spec.rb b/spec/ruby/core/io/chars_spec.rb
deleted file mode 100644
index 15db595aed..0000000000
--- a/spec/ruby/core/io/chars_spec.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-# -*- encoding: utf-8 -*-
-require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/chars'
-
-ruby_version_is ''...'3.0' do
- describe "IO#chars" do
- before :each do
- @verbose, $VERBOSE = $VERBOSE, nil
- end
-
- after :each do
- $VERBOSE = @verbose
- end
-
- it_behaves_like :io_chars, :chars
- end
-
- describe "IO#chars" do
- before :each do
- @verbose, $VERBOSE = $VERBOSE, nil
- end
-
- after :each do
- $VERBOSE = @verbose
- end
-
- it_behaves_like :io_chars_empty, :chars
- end
-end
diff --git a/spec/ruby/core/io/close_read_spec.rb b/spec/ruby/core/io/close_read_spec.rb
index 26454bfddd..e700e85bd9 100644
--- a/spec/ruby/core/io/close_read_spec.rb
+++ b/spec/ruby/core/io/close_read_spec.rb
@@ -4,7 +4,8 @@ require_relative 'fixtures/classes'
describe "IO#close_read" do
before :each do
- @io = IO.popen 'cat', "r+"
+ cmd = platform_is(:windows) ? 'rem' : 'cat'
+ @io = IO.popen cmd, "r+"
@path = tmp('io.close.txt')
end
diff --git a/spec/ruby/core/io/close_write_spec.rb b/spec/ruby/core/io/close_write_spec.rb
index 14835e4e2c..70610a3e9d 100644
--- a/spec/ruby/core/io/close_write_spec.rb
+++ b/spec/ruby/core/io/close_write_spec.rb
@@ -3,7 +3,8 @@ require_relative 'fixtures/classes'
describe "IO#close_write" do
before :each do
- @io = IO.popen 'cat', 'r+'
+ cmd = platform_is(:windows) ? 'rem' : 'cat'
+ @io = IO.popen cmd, 'r+'
@path = tmp('io.close.txt')
end
@@ -48,12 +49,15 @@ describe "IO#close_write" do
io.should.closed?
end
- it "flushes and closes the write stream" do
- @io.puts '12345'
+ # Windows didn't have command like cat
+ platform_is_not :windows do
+ it "flushes and closes the write stream" do
+ @io.puts '12345'
- @io.close_write
+ @io.close_write
- @io.read.should == "12345\n"
+ @io.read.should == "12345\n"
+ end
end
it "does nothing on closed stream" do
diff --git a/spec/ruby/core/io/codepoints_spec.rb b/spec/ruby/core/io/codepoints_spec.rb
deleted file mode 100644
index 04c115dd3f..0000000000
--- a/spec/ruby/core/io/codepoints_spec.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/codepoints'
-
-ruby_version_is ''...'3.0' do
-
- # See redmine #1667
- describe "IO#codepoints" do
- before :each do
- @verbose, $VERBOSE = $VERBOSE, nil
- end
-
- after :each do
- $VERBOSE = @verbose
- end
-
- it_behaves_like :io_codepoints, :codepoints
- end
-
- describe "IO#codepoints" do
- before :each do
- @io = IOSpecs.io_fixture "lines.txt"
- @verbose, $VERBOSE = $VERBOSE, nil
- end
-
- after :each do
- $VERBOSE = @verbose
- @io.close unless @io.closed?
- end
-
- it "calls the given block" do
- r = []
- @io.codepoints { |c| r << c }
- r[24].should == 232
- r.last.should == 10
- end
- end
-end
diff --git a/spec/ruby/core/io/copy_stream_spec.rb b/spec/ruby/core/io/copy_stream_spec.rb
index df9c5c7390..ffa2ea992c 100644
--- a/spec/ruby/core/io/copy_stream_spec.rb
+++ b/spec/ruby/core/io/copy_stream_spec.rb
@@ -69,9 +69,12 @@ describe :io_copy_stream_to_io, shared: true do
end
it "raises an IOError if the destination IO is not open for writing" do
- @to_io.close
- @to_io = new_io @to_name, "r"
- -> { IO.copy_stream @object.from, @to_io }.should raise_error(IOError)
+ to_io = new_io __FILE__, "r"
+ begin
+ -> { IO.copy_stream @object.from, to_io }.should raise_error(IOError)
+ ensure
+ to_io.close
+ end
end
it "does not close the destination IO" do
@@ -109,7 +112,8 @@ describe "IO.copy_stream" do
end
after :each do
- rm_r @to_name, @from_bigfile
+ rm_r @to_name if @to_name
+ rm_r @from_bigfile
end
describe "from an IO" do
@@ -164,6 +168,25 @@ describe "IO.copy_stream" do
it_behaves_like :io_copy_stream_to_io, nil, IOSpecs::CopyStream
it_behaves_like :io_copy_stream_to_io_with_offset, nil, IOSpecs::CopyStream
end
+
+ describe "to a Tempfile" do
+ before :all do
+ require 'tempfile'
+ end
+
+ before :each do
+ @to_io = Tempfile.new("rubyspec_copy_stream", encoding: Encoding::BINARY, mode: File::RDONLY)
+ @to_name = @to_io.path
+ end
+
+ after :each do
+ @to_io.close!
+ @to_name = nil # do not rm_r it, already done by Tempfile#close!
+ end
+
+ it_behaves_like :io_copy_stream_to_io, nil, IOSpecs::CopyStream
+ it_behaves_like :io_copy_stream_to_io_with_offset, nil, IOSpecs::CopyStream
+ end
end
describe "from a file name" do
@@ -277,10 +300,8 @@ describe "IO.copy_stream" do
@io.should_not_receive(:pos)
IO.copy_stream(@io, @to_name)
end
-
end
-
describe "with a destination that does partial reads" do
before do
@from_out, @from_in = IO.pipe
diff --git a/spec/ruby/core/io/eof_spec.rb b/spec/ruby/core/io/eof_spec.rb
index 315345d942..b4850df437 100644
--- a/spec/ruby/core/io/eof_spec.rb
+++ b/spec/ruby/core/io/eof_spec.rb
@@ -76,7 +76,7 @@ describe "IO#eof?" do
end
it "returns true on one-byte stream after single-byte read" do
- File.open(File.dirname(__FILE__) + '/fixtures/one_byte.txt') { |one_byte|
+ File.open(__dir__ + '/fixtures/one_byte.txt') { |one_byte|
one_byte.read(1)
one_byte.should.eof?
}
diff --git a/spec/ruby/core/io/foreach_spec.rb b/spec/ruby/core/io/foreach_spec.rb
index c2276cf544..c361d27879 100644
--- a/spec/ruby/core/io/foreach_spec.rb
+++ b/spec/ruby/core/io/foreach_spec.rb
@@ -20,7 +20,10 @@ describe "IO.foreach" do
platform_is :windows do
cmd = "|cmd.exe /C echo hello&echo line2"
end
- IO.foreach(cmd) { |l| ScratchPad << l }
+
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ IO.foreach(cmd) { |l| ScratchPad << l }
+ end
ScratchPad.recorded.should == ["hello\n", "line2\n"]
end
@@ -28,7 +31,9 @@ describe "IO.foreach" do
it "gets data from a fork when passed -" do
parent_pid = $$
- IO.foreach("|-") { |l| ScratchPad << l }
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ IO.foreach("|-") { |l| ScratchPad << l }
+ end
if $$ == parent_pid
ScratchPad.recorded.should == ["hello\n", "from a fork\n"]
@@ -39,6 +44,16 @@ describe "IO.foreach" do
end
end
end
+
+ ruby_version_is "3.3" do
+ # https://bugs.ruby-lang.org/issues/19630
+ it "warns about deprecation given a path with a pipe" do
+ cmd = "|echo ok"
+ -> {
+ IO.foreach(cmd).to_a
+ }.should complain(/IO process creation with a leading '\|'/)
+ end
+ end
end
end
diff --git a/spec/ruby/core/io/getbyte_spec.rb b/spec/ruby/core/io/getbyte_spec.rb
index 6ba8f0a3e0..b4351160e6 100644
--- a/spec/ruby/core/io/getbyte_spec.rb
+++ b/spec/ruby/core/io/getbyte_spec.rb
@@ -40,3 +40,19 @@ describe "IO#getbyte" do
@io.getbyte.should == nil
end
end
+
+describe "IO#getbyte" do
+ before :each do
+ @name = tmp("io_getbyte.txt")
+ @io = new_io(@name, 'w')
+ end
+
+ after :each do
+ @io.close if @io
+ rm_r @name if @name
+ end
+
+ it "raises an IOError if the stream is not readable" do
+ -> { @io.getbyte }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/core/io/gets_spec.rb b/spec/ruby/core/io/gets_spec.rb
index f38e3d3974..ca64bf860e 100644
--- a/spec/ruby/core/io/gets_spec.rb
+++ b/spec/ruby/core/io/gets_spec.rb
@@ -24,6 +24,12 @@ describe "IO#gets" do
end
end
+ it "sets $_ to nil after the last line has been read" do
+ while @io.gets
+ end
+ $_.should be_nil
+ end
+
it "returns nil if called at the end of the stream" do
IOSpecs.lines.length.times { @io.gets }
@io.gets.should == nil
@@ -149,14 +155,12 @@ describe "IO#gets" do
@io.gets(chomp: true).should == IOSpecs.lines_without_newline_characters[0]
end
- ruby_version_is "3.0" do
- it "raises exception when options passed as Hash" do
- -> { @io.gets({ chomp: true }) }.should raise_error(TypeError)
+ it "raises exception when options passed as Hash" do
+ -> { @io.gets({ chomp: true }) }.should raise_error(TypeError)
- -> {
- @io.gets("\n", 1, { chomp: true })
- }.should raise_error(ArgumentError, "wrong number of arguments (given 3, expected 0..2)")
- end
+ -> {
+ @io.gets("\n", 1, { chomp: true })
+ }.should raise_error(ArgumentError, "wrong number of arguments (given 3, expected 0..2)")
end
end
end
diff --git a/spec/ruby/core/io/initialize_spec.rb b/spec/ruby/core/io/initialize_spec.rb
index 28fd7af7ab..026252a13d 100644
--- a/spec/ruby/core/io/initialize_spec.rb
+++ b/spec/ruby/core/io/initialize_spec.rb
@@ -27,17 +27,15 @@ describe "IO#initialize" do
@io.fileno.should == fd
end
- ruby_version_is "3.0" do
- it "accepts options as keyword arguments" do
- fd = new_fd @name, "w:utf-8"
+ it "accepts options as keyword arguments" do
+ fd = new_fd @name, "w:utf-8"
- @io.send(:initialize, fd, "w", flags: File::CREAT)
- @io.fileno.should == fd
+ @io.send(:initialize, fd, "w", flags: File::CREAT)
+ @io.fileno.should == fd
- -> {
- @io.send(:initialize, fd, "w", {flags: File::CREAT})
- }.should raise_error(ArgumentError, "wrong number of arguments (given 3, expected 1..2)")
- end
+ -> {
+ @io.send(:initialize, fd, "w", {flags: File::CREAT})
+ }.should raise_error(ArgumentError, "wrong number of arguments (given 3, expected 1..2)")
end
it "raises a TypeError when passed an IO" do
diff --git a/spec/ruby/core/io/ioctl_spec.rb b/spec/ruby/core/io/ioctl_spec.rb
index 8dcd9eb2c6..3f7b5ad5d7 100644
--- a/spec/ruby/core/io/ioctl_spec.rb
+++ b/spec/ruby/core/io/ioctl_spec.rb
@@ -12,7 +12,7 @@ describe "IO#ioctl" do
guard -> { RUBY_PLATFORM.include?("86") } do # x86 / x86_64
it "resizes an empty String to match the output size" do
File.open(__FILE__, 'r') do |f|
- buffer = ''
+ buffer = +''
# FIONREAD in /usr/include/asm-generic/ioctls.h
f.ioctl 0x541B, buffer
buffer.unpack('I').first.should be_kind_of(Integer)
diff --git a/spec/ruby/core/io/lineno_spec.rb b/spec/ruby/core/io/lineno_spec.rb
index 9a4ad90880..e82cdd9f17 100644
--- a/spec/ruby/core/io/lineno_spec.rb
+++ b/spec/ruby/core/io/lineno_spec.rb
@@ -26,7 +26,8 @@ describe "IO#lineno" do
end
it "raises an IOError on a duplexed stream with the read side closed" do
- IO.popen('cat', 'r+') do |p|
+ cmd = platform_is(:windows) ? 'rem' : 'cat'
+ IO.popen(cmd, 'r+') do |p|
p.close_read
-> { p.lineno }.should raise_error(IOError)
end
@@ -70,7 +71,8 @@ describe "IO#lineno=" do
end
it "raises an IOError on a duplexed stream with the read side closed" do
- IO.popen('cat', 'r+') do |p|
+ cmd = platform_is(:windows) ? 'rem' : 'cat'
+ IO.popen(cmd, 'r+') do |p|
p.close_read
-> { p.lineno = 0 }.should raise_error(IOError)
end
diff --git a/spec/ruby/core/io/lines_spec.rb b/spec/ruby/core/io/lines_spec.rb
deleted file mode 100644
index 5b29a1d07e..0000000000
--- a/spec/ruby/core/io/lines_spec.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# -*- encoding: utf-8 -*-
-require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-
-ruby_version_is ''...'3.0' do
- describe "IO#lines" do
- before :each do
- @io = IOSpecs.io_fixture "lines.txt"
- @verbose, $VERBOSE = $VERBOSE, nil
- end
-
- after :each do
- $VERBOSE = @verbose
- @io.close if @io
- end
-
- it "returns an Enumerator" do
- @io.lines.should be_an_instance_of(Enumerator)
- end
-
- describe "when no block is given" do
- it "returns an Enumerator" do
- @io.lines.should be_an_instance_of(Enumerator)
- end
-
- describe "returned Enumerator" do
- describe "size" do
- it "should return nil" do
- @io.lines.size.should == nil
- end
- end
- end
- end
-
- it "returns a line when accessed" do
- enum = @io.lines
- enum.first.should == IOSpecs.lines[0]
- end
-
- it "yields each line to the passed block" do
- ScratchPad.record []
- @io.lines { |s| ScratchPad << s }
- ScratchPad.recorded.should == IOSpecs.lines
- end
- end
-end
diff --git a/spec/ruby/core/io/new_spec.rb b/spec/ruby/core/io/new_spec.rb
index 0ef30991fd..979ac0efcb 100644
--- a/spec/ruby/core/io/new_spec.rb
+++ b/spec/ruby/core/io/new_spec.rb
@@ -1,10 +1,16 @@
require_relative '../../spec_helper'
require_relative 'shared/new'
-# NOTE: should be syncronized with library/stringio/initialize_spec.rb
+# NOTE: should be synchronized with library/stringio/initialize_spec.rb
describe "IO.new" do
it_behaves_like :io_new, :new
+
+ it "does not use the given block and warns to use IO::open" do
+ -> {
+ @io = IO.send(@method, @fd) { raise }
+ }.should complain(/warning: IO::new\(\) does not take block; use IO::open\(\) instead/)
+ end
end
describe "IO.new" do
diff --git a/spec/ruby/core/io/nonblock_spec.rb b/spec/ruby/core/io/nonblock_spec.rb
index e81ac10c58..99dc0cafd0 100644
--- a/spec/ruby/core/io/nonblock_spec.rb
+++ b/spec/ruby/core/io/nonblock_spec.rb
@@ -12,43 +12,21 @@ platform_is_not :windows do
end
end
- ruby_version_is ""..."3.0" do
- it "returns false for pipe by default" do
- r, w = IO.pipe
- begin
- r.nonblock?.should == false
- w.nonblock?.should == false
- ensure
- r.close
- w.close
- end
- end
-
- it "returns false for socket by default" do
- require 'socket'
- TCPServer.open(0) do |socket|
- socket.nonblock?.should == false
- end
+ it "returns true for pipe by default" do
+ r, w = IO.pipe
+ begin
+ r.nonblock?.should == true
+ w.nonblock?.should == true
+ ensure
+ r.close
+ w.close
end
end
- ruby_version_is "3.0" do
- it "returns true for pipe by default" do
- r, w = IO.pipe
- begin
- r.nonblock?.should == true
- w.nonblock?.should == true
- ensure
- r.close
- w.close
- end
- end
-
- it "returns true for socket by default" do
- require 'socket'
- TCPServer.open(0) do |socket|
- socket.nonblock?.should == true
- end
+ it "returns true for socket by default" do
+ require 'socket'
+ TCPServer.open(0) do |socket|
+ socket.nonblock?.should == true
end
end
end
diff --git a/spec/ruby/core/io/open_spec.rb b/spec/ruby/core/io/open_spec.rb
index d3a3961df7..d151da9ce5 100644
--- a/spec/ruby/core/io/open_spec.rb
+++ b/spec/ruby/core/io/open_spec.rb
@@ -37,6 +37,19 @@ describe "IO.open" do
ScratchPad.recorded.should == :called
end
+ it "propagate an exception in the block after calling #close" do
+ -> do
+ IO.open(@fd, "w") do |io|
+ IOSpecs.io_mock(io, :close) do
+ super()
+ ScratchPad.record :called
+ end
+ raise Exception
+ end
+ end.should raise_error(Exception)
+ ScratchPad.recorded.should == :called
+ end
+
it "propagates an exception raised by #close that is not a StandardError" do
-> do
IO.open(@fd, "w") do |io|
diff --git a/spec/ruby/core/io/pread_spec.rb b/spec/ruby/core/io/pread_spec.rb
index fb0645dec6..28afc80e5c 100644
--- a/spec/ruby/core/io/pread_spec.rb
+++ b/spec/ruby/core/io/pread_spec.rb
@@ -21,16 +21,93 @@ guard -> { platform_is_not :windows or ruby_version_is "3.3" } do
end
it "accepts a length, an offset, and an output buffer" do
- buffer = "foo"
+ buffer = +"foo"
@file.pread(3, 4, buffer)
buffer.should == "567"
end
+ it "shrinks the buffer in case of less bytes read" do
+ buffer = +"foo"
+ @file.pread(1, 0, buffer)
+ buffer.should == "1"
+ end
+
+ it "grows the buffer in case of more bytes read" do
+ buffer = +"foo"
+ @file.pread(5, 0, buffer)
+ buffer.should == "12345"
+ end
+
it "does not advance the file pointer" do
@file.pread(4, 0).should == "1234"
@file.read.should == "1234567890"
end
+ it "ignores the current offset" do
+ @file.pos = 3
+ @file.pread(4, 0).should == "1234"
+ end
+
+ it "returns an empty string for maxlen = 0" do
+ @file.pread(0, 4).should == ""
+ end
+
+ it "ignores the offset for maxlen = 0, even if it is out of file bounds" do
+ @file.pread(0, 400).should == ""
+ end
+
+ it "does not reset the buffer when reading with maxlen = 0" do
+ buffer = +"foo"
+ @file.pread(0, 4, buffer)
+ buffer.should == "foo"
+
+ @file.pread(0, 400, buffer)
+ buffer.should == "foo"
+ end
+
+ it "converts maxlen to Integer using #to_int" do
+ maxlen = mock('maxlen')
+ maxlen.should_receive(:to_int).and_return(4)
+ @file.pread(maxlen, 0).should == "1234"
+ end
+
+ it "converts offset to Integer using #to_int" do
+ offset = mock('offset')
+ offset.should_receive(:to_int).and_return(0)
+ @file.pread(4, offset).should == "1234"
+ end
+
+ it "converts a buffer to String using to_str" do
+ buffer = mock('buffer')
+ buffer.should_receive(:to_str).at_least(1).and_return(+"foo")
+ @file.pread(4, 0, buffer)
+ buffer.should_not.is_a?(String)
+ buffer.to_str.should == "1234"
+ end
+
+ it "raises TypeError if maxlen is not an Integer and cannot be coerced into Integer" do
+ maxlen = Object.new
+ -> { @file.pread(maxlen, 0) }.should raise_error(TypeError, 'no implicit conversion of Object into Integer')
+ end
+
+ it "raises TypeError if offset is not an Integer and cannot be coerced into Integer" do
+ offset = Object.new
+ -> { @file.pread(4, offset) }.should raise_error(TypeError, 'no implicit conversion of Object into Integer')
+ end
+
+ it "raises ArgumentError for negative values of maxlen" do
+ -> { @file.pread(-4, 0) }.should raise_error(ArgumentError, 'negative string size (or size too big)')
+ end
+
+ it "raised Errno::EINVAL for negative values of offset" do
+ -> { @file.pread(4, -1) }.should raise_error(Errno::EINVAL, /Invalid argument/)
+ end
+
+ it "raises TypeError if the buffer is not a String and cannot be coerced into String" do
+ buffer = Object.new
+ -> { @file.pread(4, 0, buffer) }.should raise_error(TypeError, 'no implicit conversion of Object into String')
+ end
+
it "raises EOFError if end-of-file is reached" do
-> { @file.pread(1, 10) }.should raise_error(EOFError)
end
diff --git a/spec/ruby/core/io/puts_spec.rb b/spec/ruby/core/io/puts_spec.rb
index 9a708fffef..9ed343c94c 100644
--- a/spec/ruby/core/io/puts_spec.rb
+++ b/spec/ruby/core/io/puts_spec.rb
@@ -6,7 +6,7 @@ describe "IO#puts" do
@before_separator = $/
@name = tmp("io_puts.txt")
@io = new_io @name
- ScratchPad.record ""
+ ScratchPad.record(+"")
def @io.write(str)
ScratchPad << str
end
diff --git a/spec/ruby/core/io/pwrite_spec.rb b/spec/ruby/core/io/pwrite_spec.rb
index c10578a8eb..2bc508b37d 100644
--- a/spec/ruby/core/io/pwrite_spec.rb
+++ b/spec/ruby/core/io/pwrite_spec.rb
@@ -28,16 +28,42 @@ guard -> { platform_is_not :windows or ruby_version_is "3.3" } do
@file.pread(6, 0).should == "foobar"
end
+ it "calls #to_s on the object to be written" do
+ object = mock("to_s")
+ object.should_receive(:to_s).and_return("foo")
+ @file.pwrite(object, 0)
+ @file.pread(3, 0).should == "foo"
+ end
+
+ it "calls #to_int on the offset" do
+ offset = mock("to_int")
+ offset.should_receive(:to_int).and_return(2)
+ @file.pwrite("foo", offset)
+ @file.pread(3, 2).should == "foo"
+ end
+
it "raises IOError when file is not open in write mode" do
File.open(@fname, "r") do |file|
- -> { file.pwrite("foo", 1) }.should raise_error(IOError)
+ -> { file.pwrite("foo", 1) }.should raise_error(IOError, "not opened for writing")
end
end
it "raises IOError when file is closed" do
file = File.open(@fname, "w+")
file.close
- -> { file.pwrite("foo", 1) }.should raise_error(IOError)
+ -> { file.pwrite("foo", 1) }.should raise_error(IOError, "closed stream")
+ end
+
+ it "raises a NoMethodError if object does not respond to #to_s" do
+ -> {
+ @file.pwrite(BasicObject.new, 0)
+ }.should raise_error(NoMethodError, /undefined method [`']to_s'/)
+ end
+
+ it "raises a TypeError if the offset cannot be converted to an Integer" do
+ -> {
+ @file.pwrite("foo", Object.new)
+ }.should raise_error(TypeError, "no implicit conversion of Object into Integer")
end
end
end
diff --git a/spec/ruby/core/io/read_nonblock_spec.rb b/spec/ruby/core/io/read_nonblock_spec.rb
index a62b75274c..51e7cd6bd2 100644
--- a/spec/ruby/core/io/read_nonblock_spec.rb
+++ b/spec/ruby/core/io/read_nonblock_spec.rb
@@ -96,21 +96,21 @@ describe "IO#read_nonblock" do
end
it "reads into the passed buffer" do
- buffer = ""
+ buffer = +""
@write.write("1")
@read.read_nonblock(1, buffer)
buffer.should == "1"
end
it "returns the passed buffer" do
- buffer = ""
+ buffer = +""
@write.write("1")
output = @read.read_nonblock(1, buffer)
output.should equal(buffer)
end
it "discards the existing buffer content upon successful read" do
- buffer = "existing content"
+ buffer = +"existing content"
@write.write("hello world")
@write.close
@read.read_nonblock(11, buffer)
@@ -118,7 +118,7 @@ describe "IO#read_nonblock" do
end
it "discards the existing buffer content upon error" do
- buffer = "existing content"
+ buffer = +"existing content"
@write.close
-> { @read.read_nonblock(1, buffer) }.should raise_error(EOFError)
buffer.should be_empty
diff --git a/spec/ruby/core/io/read_spec.rb b/spec/ruby/core/io/read_spec.rb
index 8bffd50876..eb3652e692 100644
--- a/spec/ruby/core/io/read_spec.rb
+++ b/spec/ruby/core/io/read_spec.rb
@@ -23,15 +23,13 @@ describe "IO.read" do
IO.read(p)
end
- ruby_version_is "3.0" do
- # https://bugs.ruby-lang.org/issues/19354
- it "accepts options as keyword arguments" do
- IO.read(@fname, 3, 0, mode: "r+").should == @contents[0, 3]
+ # https://bugs.ruby-lang.org/issues/19354
+ it "accepts options as keyword arguments" do
+ IO.read(@fname, 3, 0, mode: "r+").should == @contents[0, 3]
- -> {
- IO.read(@fname, 3, 0, {mode: "r+"})
- }.should raise_error(ArgumentError, /wrong number of arguments/)
- end
+ -> {
+ IO.read(@fname, 3, 0, {mode: "r+"})
+ }.should raise_error(ArgumentError, /wrong number of arguments/)
end
it "accepts an empty options Hash" do
@@ -115,6 +113,15 @@ describe "IO.read" do
IO.read(@fname, 1, 10).should == nil
end
+ it "returns an empty string when reading zero bytes" do
+ IO.read(@fname, 0).should == ''
+ end
+
+ it "returns a String in BINARY when passed a size" do
+ IO.read(@fname, 1).encoding.should == Encoding::BINARY
+ IO.read(@fname, 0).encoding.should == Encoding::BINARY
+ end
+
it "raises an Errno::ENOENT when the requested file does not exist" do
rm_r @fname
-> { IO.read @fname }.should raise_error(Errno::ENOENT)
@@ -167,12 +174,19 @@ describe "IO.read from a pipe" do
platform_is :windows do
cmd = "|cmd.exe /C echo hello"
end
- IO.read(cmd).should == "hello\n"
+
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ IO.read(cmd).should == "hello\n"
+ end
end
platform_is_not :windows do
it "opens a pipe to a fork if the rest is -" do
- str = IO.read("|-")
+ str = nil
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ str = IO.read("|-")
+ end
+
if str # parent
str.should == "hello from child\n"
else #child
@@ -187,13 +201,18 @@ describe "IO.read from a pipe" do
platform_is :windows do
cmd = "|cmd.exe /C echo hello"
end
- IO.read(cmd, 1).should == "h"
+
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ IO.read(cmd, 1).should == "h"
+ end
end
platform_is_not :windows do
it "raises Errno::ESPIPE if passed an offset" do
-> {
- IO.read("|sh -c 'echo hello'", 1, 1)
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ IO.read("|sh -c 'echo hello'", 1, 1)
+ end
}.should raise_error(Errno::ESPIPE)
end
end
@@ -204,11 +223,23 @@ quarantine! do # The process tried to write to a nonexistent pipe.
# once https://bugs.ruby-lang.org/issues/12230 is fixed.
it "raises Errno::EINVAL if passed an offset" do
-> {
- IO.read("|cmd.exe /C echo hello", 1, 1)
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ IO.read("|cmd.exe /C echo hello", 1, 1)
+ end
}.should raise_error(Errno::EINVAL)
end
end
end
+
+ ruby_version_is "3.3" do
+ # https://bugs.ruby-lang.org/issues/19630
+ it "warns about deprecation given a path with a pipe" do
+ cmd = "|echo ok"
+ -> {
+ IO.read(cmd)
+ }.should complain(/IO process creation with a leading '\|'/)
+ end
+ end
end
describe "IO.read on an empty file" do
@@ -252,21 +283,55 @@ describe "IO#read" do
@io.read(4).should == '7890'
end
+ it "treats first nil argument as no length limit" do
+ @io.read(nil).should == @contents
+ end
+
+ it "raises an ArgumentError when not passed a valid length" do
+ -> { @io.read(-1) }.should raise_error(ArgumentError)
+ end
+
it "clears the output buffer if there is nothing to read" do
@io.pos = 10
- buf = 'non-empty string'
+ buf = +'non-empty string'
@io.read(10, buf).should == nil
buf.should == ''
+
+ buf = +'non-empty string'
+
+ @io.read(nil, buf).should == ""
+
+ buf.should == ''
+
+ buf = +'non-empty string'
+
+ @io.read(0, buf).should == ""
+
+ buf.should == ''
+ end
+
+ it "raise FrozenError if the output buffer is frozen" do
+ @io.read
+ -> { @io.read(0, 'frozen-string'.freeze) }.should raise_error(FrozenError)
+ -> { @io.read(1, 'frozen-string'.freeze) }.should raise_error(FrozenError)
+ -> { @io.read(nil, 'frozen-string'.freeze) }.should raise_error(FrozenError)
+ end
+
+ ruby_bug "", ""..."3.3" do
+ it "raise FrozenError if the output buffer is frozen (2)" do
+ @io.read
+ -> { @io.read(1, ''.freeze) }.should raise_error(FrozenError)
+ end
end
it "consumes zero bytes when reading zero bytes" do
@io.read(0).should == ''
@io.pos.should == 0
- @io.getc.chr.should == '1'
+ @io.getc.should == '1'
end
it "is at end-of-file when everything has been read" do
@@ -279,53 +344,53 @@ describe "IO#read" do
end
it "places the specified number of bytes in the buffer" do
- buf = ""
+ buf = +""
@io.read 5, buf
buf.should == "12345"
end
it "expands the buffer when too small" do
- buf = "ABCDE"
+ buf = +"ABCDE"
@io.read nil, buf
buf.should == @contents
end
it "overwrites the buffer" do
- buf = "ABCDEFGHIJ"
+ buf = +"ABCDEFGHIJ"
@io.read nil, buf
buf.should == @contents
end
it "truncates the buffer when too big" do
- buf = "ABCDEFGHIJKLMNO"
+ buf = +"ABCDEFGHIJKLMNO"
@io.read nil, buf
buf.should == @contents
@io.rewind
- buf = "ABCDEFGHIJKLMNO"
+ buf = +"ABCDEFGHIJKLMNO"
@io.read 5, buf
buf.should == @contents[0..4]
end
it "returns the given buffer" do
- buf = ""
+ buf = +""
@io.read(nil, buf).should equal buf
end
it "returns the given buffer when there is nothing to read" do
- buf = ""
+ buf = +""
@io.read
@io.read(nil, buf).should equal buf
end
it "coerces the second argument to string and uses it as a buffer" do
- buf = "ABCDE"
+ buf = +"ABCDE"
obj = mock("buff")
obj.should_receive(:to_str).any_number_of_times.and_return(buf)
@@ -523,13 +588,13 @@ describe :io_read_internal_encoding, shared: true do
describe "when passed nil for limit" do
it "sets the buffer to a transcoded String" do
- result = @io.read(nil, buf = "")
+ result = @io.read(nil, buf = +"")
buf.should equal(result)
buf.should == "ã‚ã‚ŠãŒã¨ã†\n"
end
it "sets the buffer's encoding to the internal encoding" do
- buf = "".force_encoding Encoding::ISO_8859_1
+ buf = "".dup.force_encoding Encoding::ISO_8859_1
@io.read(nil, buf)
buf.encoding.should equal(Encoding::UTF_8)
end
@@ -543,17 +608,18 @@ describe :io_read_size_internal_encoding, shared: true do
it "returns a String in BINARY when passed a size" do
@io.read(4).encoding.should equal(Encoding::BINARY)
+ @io.read(0).encoding.should equal(Encoding::BINARY)
end
it "does not change the buffer's encoding when passed a limit" do
- buf = "".force_encoding Encoding::ISO_8859_1
+ buf = "".dup.force_encoding Encoding::ISO_8859_1
@io.read(4, buf)
buf.should == [164, 162, 164, 234].pack('C*').force_encoding(Encoding::ISO_8859_1)
buf.encoding.should equal(Encoding::ISO_8859_1)
end
it "truncates the buffer but does not change the buffer's encoding when no data remains" do
- buf = "abc".force_encoding Encoding::ISO_8859_1
+ buf = "abc".dup.force_encoding Encoding::ISO_8859_1
@io.read
@io.read(1, buf).should be_nil
diff --git a/spec/ruby/core/io/readline_spec.rb b/spec/ruby/core/io/readline_spec.rb
index cf9f0dfc11..a814c1be90 100644
--- a/spec/ruby/core/io/readline_spec.rb
+++ b/spec/ruby/core/io/readline_spec.rb
@@ -73,14 +73,12 @@ describe "IO#readline" do
@io.readline(chomp: true).should == IOSpecs.lines_without_newline_characters[0]
end
- ruby_version_is "3.0" do
- it "raises exception when options passed as Hash" do
- -> { @io.readline({ chomp: true }) }.should raise_error(TypeError)
+ it "raises exception when options passed as Hash" do
+ -> { @io.readline({ chomp: true }) }.should raise_error(TypeError)
- -> {
- @io.readline("\n", 1, { chomp: true })
- }.should raise_error(ArgumentError, "wrong number of arguments (given 3, expected 0..2)")
- end
+ -> {
+ @io.readline("\n", 1, { chomp: true })
+ }.should raise_error(ArgumentError, "wrong number of arguments (given 3, expected 0..2)")
end
end
end
diff --git a/spec/ruby/core/io/readlines_spec.rb b/spec/ruby/core/io/readlines_spec.rb
index 43d0750a72..3a6ff3d0f3 100644
--- a/spec/ruby/core/io/readlines_spec.rb
+++ b/spec/ruby/core/io/readlines_spec.rb
@@ -117,14 +117,12 @@ describe "IO#readlines" do
@io.readlines(chomp: true).should == IOSpecs.lines_without_newline_characters
end
- ruby_version_is "3.0" do
- it "raises exception when options passed as Hash" do
- -> { @io.readlines({ chomp: true }) }.should raise_error(TypeError)
+ it "raises exception when options passed as Hash" do
+ -> { @io.readlines({ chomp: true }) }.should raise_error(TypeError)
- -> {
- @io.readlines("\n", 1, { chomp: true })
- }.should raise_error(ArgumentError, "wrong number of arguments (given 3, expected 0..2)")
- end
+ -> {
+ @io.readlines("\n", 1, { chomp: true })
+ }.should raise_error(ArgumentError, "wrong number of arguments (given 3, expected 0..2)")
end
end
@@ -182,13 +180,20 @@ describe "IO.readlines" do
platform_is :windows do
cmd = "|cmd.exe /C echo hello&echo line2"
end
- lines = IO.readlines(cmd)
+
+ lines = nil
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ lines = IO.readlines(cmd)
+ end
lines.should == ["hello\n", "line2\n"]
end
platform_is_not :windows do
it "gets data from a fork when passed -" do
- lines = IO.readlines("|-")
+ lines = nil
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ lines = IO.readlines("|-")
+ end
if lines # parent
lines.should == ["hello\n", "from a fork\n"]
@@ -201,6 +206,16 @@ describe "IO.readlines" do
end
end
+ ruby_version_is "3.3" do
+ # https://bugs.ruby-lang.org/issues/19630
+ it "warns about deprecation given a path with a pipe" do
+ cmd = "|echo ok"
+ -> {
+ IO.readlines(cmd)
+ }.should complain(/IO process creation with a leading '\|'/)
+ end
+ end
+
it_behaves_like :io_readlines, :readlines
it_behaves_like :io_readlines_options_19, :readlines
end
diff --git a/spec/ruby/core/io/readpartial_spec.rb b/spec/ruby/core/io/readpartial_spec.rb
index 2901b429c2..0060beb545 100644
--- a/spec/ruby/core/io/readpartial_spec.rb
+++ b/spec/ruby/core/io/readpartial_spec.rb
@@ -59,7 +59,7 @@ describe "IO#readpartial" do
end
it "discards the existing buffer content upon successful read" do
- buffer = "existing content"
+ buffer = +"existing content"
@wr.write("hello world")
@wr.close
@rd.readpartial(11, buffer)
@@ -74,7 +74,7 @@ describe "IO#readpartial" do
end
it "discards the existing buffer content upon error" do
- buffer = 'hello'
+ buffer = +'hello'
@wr.close
-> { @rd.readpartial(1, buffer) }.should raise_error(EOFError)
buffer.should be_empty
@@ -95,7 +95,7 @@ describe "IO#readpartial" do
ruby_bug "#18421", ""..."3.0.4" do
it "clears and returns the given buffer if the length argument is 0" do
- buffer = "existing content"
+ buffer = +"existing content"
@rd.readpartial(0, buffer).should == buffer
buffer.should == ""
end
diff --git a/spec/ruby/core/io/select_spec.rb b/spec/ruby/core/io/select_spec.rb
index 4603c1fbbc..3893e7620f 100644
--- a/spec/ruby/core/io/select_spec.rb
+++ b/spec/ruby/core/io/select_spec.rb
@@ -55,8 +55,8 @@ describe "IO.select" do
end
end
- it "returns supplied objects correctly even when monitoring the same object in different arrays" do
- filename = tmp("IO_select_pipe_file") + $$.to_s
+ it "returns supplied objects correctly when monitoring the same object in different arrays" do
+ filename = tmp("IO_select_pipe_file")
io = File.open(filename, 'w+')
result = IO.select [io], [io], nil, 0
result.should == [[io], [io], []]
@@ -64,6 +64,17 @@ describe "IO.select" do
rm_r filename
end
+ it "returns the pipe read end in read set if the pipe write end is closed concurrently" do
+ main = Thread.current
+ t = Thread.new {
+ Thread.pass until main.stop?
+ @wr.close
+ }
+ IO.select([@rd]).should == [[@rd], [], []]
+ ensure
+ t.join
+ end
+
it "invokes to_io on supplied objects that are not IO and returns the supplied objects" do
# make some data available
@wr.write("foobar")
@@ -103,6 +114,39 @@ describe "IO.select" do
it "raises an ArgumentError when passed a negative timeout" do
-> { IO.select(nil, nil, nil, -5)}.should raise_error(ArgumentError)
end
+
+ describe "returns the available descriptors when the file descriptor" do
+ it "is in both read and error arrays" do
+ @wr.write("foobar")
+ result = IO.select([@rd], nil, [@rd])
+ result.should == [[@rd], [], []]
+ end
+
+ it "is in both write and error arrays" do
+ result = IO.select(nil, [@wr], [@wr])
+ result.should == [[], [@wr], []]
+ end
+
+ it "is in both read and write arrays" do
+ filename = tmp("IO_select_read_write_file")
+ w = File.open(filename, 'w+')
+ begin
+ IO.select([w], [w], []).should == [[w], [w], []]
+ ensure
+ w.close
+ rm_r filename
+ end
+
+ IO.select([@wr], [@wr], []).should == [[], [@wr], []]
+
+ @wr.write("foobar")
+ # CRuby on macOS returns [[@rd], [@rd], []], weird but we accept it here, probably only for pipe read-end
+ [
+ [[@rd], [], []],
+ [[@rd], [@rd], []]
+ ].should.include? IO.select([@rd], [@rd], [])
+ end
+ end
end
describe "IO.select when passed nil for timeout" do
diff --git a/spec/ruby/core/io/shared/binwrite.rb b/spec/ruby/core/io/shared/binwrite.rb
index 950a6f51ab..e51093329b 100644
--- a/spec/ruby/core/io/shared/binwrite.rb
+++ b/spec/ruby/core/io/shared/binwrite.rb
@@ -21,14 +21,12 @@ describe :io_binwrite, shared: true do
IO.send(@method, @filename, "abcde").should == 5
end
- ruby_version_is "3.0" do
- it "accepts options as a keyword argument" do
- IO.send(@method, @filename, "hi", 0, flags: File::CREAT).should == 2
+ it "accepts options as a keyword argument" do
+ IO.send(@method, @filename, "hi", 0, flags: File::CREAT).should == 2
- -> {
- IO.send(@method, @filename, "hi", 0, {flags: File::CREAT})
- }.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 2..3)")
- end
+ -> {
+ IO.send(@method, @filename, "hi", 0, {flags: File::CREAT})
+ }.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 2..3)")
end
it "creates a file if missing" do
diff --git a/spec/ruby/core/io/shared/each.rb b/spec/ruby/core/io/shared/each.rb
index 02bbe19c1a..aca622834f 100644
--- a/spec/ruby/core/io/shared/each.rb
+++ b/spec/ruby/core/io/shared/each.rb
@@ -33,10 +33,6 @@ describe :io_each, shared: true do
$_.should == "test"
end
- it "returns self" do
- @io.send(@method) { |l| l }.should equal(@io)
- end
-
it "raises an IOError when self is not readable" do
-> { IOSpecs.closed_io.send(@method) {} }.should raise_error(IOError)
end
@@ -180,16 +176,14 @@ describe :io_each, shared: true do
ScratchPad.recorded.should == IOSpecs.lines_without_newline_characters
end
- ruby_version_is "3.0" do
- it "raises exception when options passed as Hash" do
- -> {
- @io.send(@method, { chomp: true }) { |s| }
- }.should raise_error(TypeError)
+ it "raises exception when options passed as Hash" do
+ -> {
+ @io.send(@method, { chomp: true }) { |s| }
+ }.should raise_error(TypeError)
- -> {
- @io.send(@method, "\n", 1, { chomp: true }) { |s| }
- }.should raise_error(ArgumentError, "wrong number of arguments (given 3, expected 0..2)")
- end
+ -> {
+ @io.send(@method, "\n", 1, { chomp: true }) { |s| }
+ }.should raise_error(ArgumentError, "wrong number of arguments (given 3, expected 0..2)")
end
end
diff --git a/spec/ruby/core/io/shared/new.rb b/spec/ruby/core/io/shared/new.rb
index da4c0af7a9..cba5f33ebf 100644
--- a/spec/ruby/core/io/shared/new.rb
+++ b/spec/ruby/core/io/shared/new.rb
@@ -1,6 +1,6 @@
require_relative '../fixtures/classes'
-# NOTE: should be syncronized with library/stringio/initialize_spec.rb
+# NOTE: should be synchronized with library/stringio/initialize_spec.rb
# This group of specs may ONLY contain specs that do successfully create
# an IO instance from the file descriptor returned by #new_fd helper.
@@ -64,15 +64,13 @@ describe :io_new, shared: true do
@io.should be_an_instance_of(IO)
end
- ruby_version_is "3.0" do
- it "accepts options as keyword arguments" do
- @io = IO.send(@method, @fd, "w", flags: File::CREAT)
- @io.write("foo").should == 3
+ it "accepts options as keyword arguments" do
+ @io = IO.send(@method, @fd, "w", flags: File::CREAT)
+ @io.write("foo").should == 3
- -> {
- IO.send(@method, @fd, "w", {flags: File::CREAT})
- }.should raise_error(ArgumentError, "wrong number of arguments (given 3, expected 1..2)")
- end
+ -> {
+ IO.send(@method, @fd, "w", {flags: File::CREAT})
+ }.should raise_error(ArgumentError, "wrong number of arguments (given 3, expected 1..2)")
end
it "accepts a :mode option" do
@@ -210,21 +208,10 @@ describe :io_new, shared: true do
@io.internal_encoding.to_s.should == 'IBM866'
end
- ruby_version_is ''...'3.0' do
- it "accepts nil options" do
- @io = suppress_keyword_warning do
- IO.send(@method, @fd, 'w', nil)
- end
- @io.write("foo").should == 3
- end
- end
-
- ruby_version_is '3.0' do
- it "raises ArgumentError for nil options" do
- -> {
- IO.send(@method, @fd, 'w', nil)
- }.should raise_error(ArgumentError)
- end
+ it "raises ArgumentError for nil options" do
+ -> {
+ IO.send(@method, @fd, 'w', nil)
+ }.should raise_error(ArgumentError)
end
it "coerces mode with #to_str" do
@@ -395,21 +382,9 @@ describe :io_new_errors, shared: true do
}.should raise_error(ArgumentError)
end
- ruby_version_is ''...'3.0' do
- it "raises TypeError if passed a hash for mode and nil for options" do
- -> {
- suppress_keyword_warning do
- @io = IO.send(@method, @fd, {mode: 'w'}, nil)
- end
- }.should raise_error(TypeError)
- end
- end
-
- ruby_version_is '3.0' do
- it "raises ArgumentError if passed a hash for mode and nil for options" do
- -> {
- @io = IO.send(@method, @fd, {mode: 'w'}, nil)
- }.should raise_error(ArgumentError)
- end
+ it "raises ArgumentError if passed a hash for mode and nil for options" do
+ -> {
+ @io = IO.send(@method, @fd, {mode: 'w'}, nil)
+ }.should raise_error(ArgumentError)
end
end
diff --git a/spec/ruby/core/io/shared/readlines.rb b/spec/ruby/core/io/shared/readlines.rb
index 7681e1b5c1..6c1fa11a59 100644
--- a/spec/ruby/core/io/shared/readlines.rb
+++ b/spec/ruby/core/io/shared/readlines.rb
@@ -99,18 +99,16 @@ describe :io_readlines_options_19, shared: true do
end
it "accepts non-ASCII data as separator" do
- result = IO.send(@method, @name, "\303\250".force_encoding("utf-8"), &@object)
+ result = IO.send(@method, @name, "\303\250".dup.force_encoding("utf-8"), &@object)
(result ? result : ScratchPad.recorded).should == IOSpecs.lines_arbitrary_separator
end
end
describe "when the object is an options Hash" do
- ruby_version_is "3.0" do
- it "raises TypeError exception" do
- -> {
- IO.send(@method, @name, { chomp: true }, &@object)
- }.should raise_error(TypeError)
- end
+ it "raises TypeError exception" do
+ -> {
+ IO.send(@method, @name, { chomp: true }, &@object)
+ }.should raise_error(TypeError)
end
end
@@ -179,12 +177,10 @@ describe :io_readlines_options_19, shared: true do
end
describe "when the second object is an options Hash" do
- ruby_version_is "3.0" do
- it "raises TypeError exception" do
- -> {
- IO.send(@method, @name, "", { chomp: true }, &@object)
- }.should raise_error(TypeError)
- end
+ it "raises TypeError exception" do
+ -> {
+ IO.send(@method, @name, "", { chomp: true }, &@object)
+ }.should raise_error(TypeError)
end
end
end
diff --git a/spec/ruby/core/io/stat_spec.rb b/spec/ruby/core/io/stat_spec.rb
index 58eba02b8f..717c45d0a3 100644
--- a/spec/ruby/core/io/stat_spec.rb
+++ b/spec/ruby/core/io/stat_spec.rb
@@ -3,7 +3,8 @@ require_relative 'fixtures/classes'
describe "IO#stat" do
before :each do
- @io = IO.popen 'cat', "r+"
+ cmd = platform_is(:windows) ? 'rem' : 'cat'
+ @io = IO.popen cmd, "r+"
end
after :each do
diff --git a/spec/ruby/core/io/sysread_spec.rb b/spec/ruby/core/io/sysread_spec.rb
index e7f63cefec..003bb9eb94 100644
--- a/spec/ruby/core/io/sysread_spec.rb
+++ b/spec/ruby/core/io/sysread_spec.rb
@@ -21,25 +21,25 @@ describe "IO#sysread on a file" do
end
it "reads the specified number of bytes from the file to the buffer" do
- buf = "" # empty buffer
+ buf = +"" # empty buffer
@file.sysread(15, buf).should == buf
buf.should == "012345678901234"
@file.rewind
- buf = "ABCDE" # small buffer
+ buf = +"ABCDE" # small buffer
@file.sysread(15, buf).should == buf
buf.should == "012345678901234"
@file.rewind
- buf = "ABCDE" * 5 # large buffer
+ buf = +"ABCDE" * 5 # large buffer
@file.sysread(15, buf).should == buf
buf.should == "012345678901234"
end
it "coerces the second argument to string and uses it as a buffer" do
- buf = "ABCDE"
+ buf = +"ABCDE"
(obj = mock("buff")).should_receive(:to_str).any_number_of_times.and_return(buf)
@file.sysread(15, obj).should == buf
buf.should == "012345678901234"
@@ -90,19 +90,19 @@ describe "IO#sysread on a file" do
end
it "immediately returns the given buffer if the length argument is 0" do
- buffer = "existing content"
+ buffer = +"existing content"
@file.sysread(0, buffer).should == buffer
buffer.should == "existing content"
end
it "discards the existing buffer content upon successful read" do
- buffer = "existing content"
+ buffer = +"existing content"
@file.sysread(11, buffer)
buffer.should == "01234567890"
end
it "discards the existing buffer content upon error" do
- buffer = "existing content"
+ buffer = +"existing content"
@file.seek(0, :END)
-> { @file.sysread(1, buffer) }.should raise_error(EOFError)
buffer.should be_empty
diff --git a/spec/ruby/core/io/ungetc_spec.rb b/spec/ruby/core/io/ungetc_spec.rb
index 41a455c836..47a4e99ebf 100644
--- a/spec/ruby/core/io/ungetc_spec.rb
+++ b/spec/ruby/core/io/ungetc_spec.rb
@@ -103,19 +103,9 @@ describe "IO#ungetc" do
-> { @io.sysread(1) }.should raise_error(IOError)
end
- ruby_version_is ""..."3.0" do
- it "does not affect the stream and returns nil when passed nil" do
- @io.getc.should == ?V
- @io.ungetc(nil)
- @io.getc.should == ?o
- end
- end
-
- ruby_version_is "3.0" do
- it "raises TypeError if passed nil" do
- @io.getc.should == ?V
- proc{@io.ungetc(nil)}.should raise_error(TypeError)
- end
+ it "raises TypeError if passed nil" do
+ @io.getc.should == ?V
+ proc{@io.ungetc(nil)}.should raise_error(TypeError)
end
it "puts one or more characters back in the stream" do
diff --git a/spec/ruby/core/io/write_spec.rb b/spec/ruby/core/io/write_spec.rb
index bf23634372..4a26f8dbaf 100644
--- a/spec/ruby/core/io/write_spec.rb
+++ b/spec/ruby/core/io/write_spec.rb
@@ -203,18 +203,33 @@ describe "IO.write" do
rm_r @fifo
end
- it "writes correctly" do
- thr = Thread.new do
- IO.read(@fifo)
- end
- begin
- string = "hi"
- IO.write(@fifo, string).should == string.length
- ensure
- thr.join
+ # rb_cloexec_open() is currently missing a retry on EINTR.
+ # @ioquatix is looking into fixing it. Quarantined until it's done.
+ quarantine! do
+ it "writes correctly" do
+ thr = Thread.new do
+ IO.read(@fifo)
+ end
+ begin
+ string = "hi"
+ IO.write(@fifo, string).should == string.length
+ ensure
+ thr.join
+ end
end
end
end
+
+ ruby_version_is "3.3" do
+ # https://bugs.ruby-lang.org/issues/19630
+ it "warns about deprecation given a path with a pipe" do
+ -> {
+ -> {
+ IO.write("|cat", "xxx")
+ }.should output_to_fd("xxx")
+ }.should complain(/IO process creation with a leading '\|'/)
+ end
+ end
end
end
@@ -259,25 +274,23 @@ platform_is :windows do
end
end
-ruby_version_is "3.0" do
- describe "IO#write on STDOUT" do
- # https://bugs.ruby-lang.org/issues/14413
- platform_is_not :windows do
- it "raises SignalException SIGPIPE if the stream is closed instead of Errno::EPIPE like other IOs" do
- stderr_file = tmp("stderr")
- begin
- IO.popen([*ruby_exe, "-e", "loop { puts :ok }"], "r", err: stderr_file) do |io|
- io.gets.should == "ok\n"
- io.close
- end
- status = $?
- status.should_not.success?
- status.should.signaled?
- Signal.signame(status.termsig).should == 'PIPE'
- File.read(stderr_file).should.empty?
- ensure
- rm_r stderr_file
+describe "IO#write on STDOUT" do
+ # https://bugs.ruby-lang.org/issues/14413
+ platform_is_not :windows do
+ it "raises SignalException SIGPIPE if the stream is closed instead of Errno::EPIPE like other IOs" do
+ stderr_file = tmp("stderr")
+ begin
+ IO.popen([*ruby_exe, "-e", "loop { puts :ok }"], "r", err: stderr_file) do |io|
+ io.gets.should == "ok\n"
+ io.close
end
+ status = $?
+ status.should_not.success?
+ status.should.signaled?
+ Signal.signame(status.termsig).should == 'PIPE'
+ File.read(stderr_file).should.empty?
+ ensure
+ rm_r stderr_file
end
end
end
diff --git a/spec/ruby/core/kernel/Float_spec.rb b/spec/ruby/core/kernel/Float_spec.rb
index 015bcb33d6..0f83cb5824 100644
--- a/spec/ruby/core/kernel/Float_spec.rb
+++ b/spec/ruby/core/kernel/Float_spec.rb
@@ -41,7 +41,7 @@ describe :kernel_float, shared: true do
end
it "converts Strings to floats without calling #to_f" do
- string = "10"
+ string = +"10"
string.should_not_receive(:to_f)
@object.send(:Float, string).should == 10.0
end
diff --git a/spec/ruby/core/kernel/Integer_spec.rb b/spec/ruby/core/kernel/Integer_spec.rb
index c691cb4c41..74dd3e0dd2 100644
--- a/spec/ruby/core/kernel/Integer_spec.rb
+++ b/spec/ruby/core/kernel/Integer_spec.rb
@@ -145,7 +145,7 @@ describe :kernel_integer, shared: true do
end
end
-describe "Integer() given a String", shared: true do
+describe :kernel_integer_string, shared: true do
it "raises an ArgumentError if the String is a null byte" do
-> { Integer("\0") }.should raise_error(ArgumentError)
end
@@ -348,7 +348,7 @@ describe "Integer() given a String", shared: true do
end
end
-describe "Integer() given a String and base", shared: true do
+describe :kernel_integer_string_base, shared: true do
it "raises an ArgumentError if the String is a null byte" do
-> { Integer("\0", 2) }.should raise_error(ArgumentError)
end
@@ -586,6 +586,21 @@ describe "Integer() given a String and base", shared: true do
Integer("777", obj).should == 0777
end
+ # https://bugs.ruby-lang.org/issues/19349
+ ruby_version_is ''...'3.3' do
+ it "ignores the base if it is not an integer and does not respond to #to_i" do
+ Integer("777", "8").should == 777
+ end
+ end
+
+ ruby_version_is '3.3' do
+ it "raises a TypeError if it is not an integer and does not respond to #to_i" do
+ -> {
+ Integer("777", "8")
+ }.should raise_error(TypeError, "no implicit conversion of String into Integer")
+ end
+ end
+
describe "when passed exception: false" do
describe "and valid argument" do
it "returns an Integer number" do
@@ -784,9 +799,9 @@ describe "Kernel.Integer" do
# TODO: fix these specs
it_behaves_like :kernel_integer, :Integer, Kernel
- it_behaves_like "Integer() given a String", :Integer
+ it_behaves_like :kernel_integer_string, :Integer
- it_behaves_like "Integer() given a String and base", :Integer
+ it_behaves_like :kernel_integer_string_base, :Integer
it "is a public method" do
Kernel.Integer(10).should == 10
@@ -798,9 +813,9 @@ describe "Kernel#Integer" do
# TODO: fix these specs
it_behaves_like :kernel_integer, :Integer, Object.new
- it_behaves_like "Integer() given a String", :Integer
+ it_behaves_like :kernel_integer_string, :Integer
- it_behaves_like "Integer() given a String and base", :Integer
+ it_behaves_like :kernel_integer_string_base, :Integer
it "is a private method" do
Kernel.should have_private_instance_method(:Integer)
diff --git a/spec/ruby/core/kernel/String_spec.rb b/spec/ruby/core/kernel/String_spec.rb
index 47ee797be5..7caec6eda5 100644
--- a/spec/ruby/core/kernel/String_spec.rb
+++ b/spec/ruby/core/kernel/String_spec.rb
@@ -78,7 +78,7 @@ describe :kernel_String, shared: true do
end
it "returns the same object if it is already a String" do
- string = "Hello"
+ string = +"Hello"
string.should_not_receive(:to_s)
string2 = @object.send(@method, string)
string.should equal(string2)
diff --git a/spec/ruby/core/kernel/__dir___spec.rb b/spec/ruby/core/kernel/__dir___spec.rb
index 324792a408..242adbf48b 100644
--- a/spec/ruby/core/kernel/__dir___spec.rb
+++ b/spec/ruby/core/kernel/__dir___spec.rb
@@ -19,19 +19,9 @@ describe "Kernel#__dir__" do
end
end
- ruby_version_is ""..."3.0" do
- context "when used in eval with top level binding" do
- it "returns the real name of the directory containing the currently-executing file" do
- eval("__dir__", binding).should == File.realpath(File.dirname(__FILE__))
- end
- end
- end
-
- ruby_version_is "3.0" do
- context "when used in eval with top level binding" do
- it "returns nil" do
- eval("__dir__", binding).should == nil
- end
+ context "when used in eval with top level binding" do
+ it "returns nil" do
+ eval("__dir__", binding).should == nil
end
end
end
diff --git a/spec/ruby/core/kernel/caller_locations_spec.rb b/spec/ruby/core/kernel/caller_locations_spec.rb
index 5994b28fa3..aaacd9a910 100644
--- a/spec/ruby/core/kernel/caller_locations_spec.rb
+++ b/spec/ruby/core/kernel/caller_locations_spec.rb
@@ -71,14 +71,28 @@ describe 'Kernel#caller_locations' do
end
guard -> { Kernel.instance_method(:tap).source_location } do
- it "includes core library methods defined in Ruby" do
- file, line = Kernel.instance_method(:tap).source_location
- file.should.start_with?('<internal:')
-
- loc = nil
- tap { loc = caller_locations(1, 1)[0] }
- loc.label.should == "tap"
- loc.path.should.start_with? "<internal:"
+ ruby_version_is ""..."3.4" do
+ it "includes core library methods defined in Ruby" do
+ file, line = Kernel.instance_method(:tap).source_location
+ file.should.start_with?('<internal:')
+
+ loc = nil
+ tap { loc = caller_locations(1, 1)[0] }
+ loc.label.should == "tap"
+ loc.path.should.start_with? "<internal:"
+ end
+ end
+
+ ruby_version_is "3.4" do
+ it "includes core library methods defined in Ruby" do
+ file, line = Kernel.instance_method(:tap).source_location
+ file.should.start_with?('<internal:')
+
+ loc = nil
+ tap { loc = caller_locations(1, 1)[0] }
+ loc.label.should == "Kernel#tap"
+ loc.path.should.start_with? "<internal:"
+ end
end
end
end
diff --git a/spec/ruby/core/kernel/caller_spec.rb b/spec/ruby/core/kernel/caller_spec.rb
index f1ff7044b8..c3d63ccb00 100644
--- a/spec/ruby/core/kernel/caller_spec.rb
+++ b/spec/ruby/core/kernel/caller_spec.rb
@@ -38,10 +38,9 @@ describe 'Kernel#caller' do
it "returns an Array with the block given to #at_exit at the base of the stack" do
path = fixture(__FILE__, "caller_at_exit.rb")
lines = ruby_exe(path).lines
- lines.should == [
- "#{path}:6:in `foo'\n",
- "#{path}:2:in `block in <main>'\n"
- ]
+ lines.size.should == 2
+ lines[0].should =~ /\A#{path}:6:in [`'](?:Object#)?foo'\n\z/
+ lines[1].should =~ /\A#{path}:2:in [`']block in <main>'\n\z/
end
it "works with endless ranges" do
@@ -63,8 +62,7 @@ describe 'Kernel#caller' do
loc = nil
tap { loc = caller(1, 1)[0] }
- loc.should.end_with? "in `tap'"
- loc.should.start_with? "<internal:"
+ loc.should =~ /\A<internal:.*in [`'](?:Kernel#)?tap'\z/
end
end
end
diff --git a/spec/ruby/core/kernel/catch_spec.rb b/spec/ruby/core/kernel/catch_spec.rb
index 4060172429..9f59d3b384 100644
--- a/spec/ruby/core/kernel/catch_spec.rb
+++ b/spec/ruby/core/kernel/catch_spec.rb
@@ -35,7 +35,7 @@ describe "Kernel.catch" do
end
it "raises an ArgumentError if a String with different identity is thrown" do
- -> { catch("exit") { throw "exit" } }.should raise_error(ArgumentError)
+ -> { catch("exit".dup) { throw "exit".dup } }.should raise_error(ArgumentError)
end
it "catches a Symbol when thrown a matching Symbol" do
diff --git a/spec/ruby/core/kernel/class_spec.rb b/spec/ruby/core/kernel/class_spec.rb
index 2725bde19b..b1d9df1671 100644
--- a/spec/ruby/core/kernel/class_spec.rb
+++ b/spec/ruby/core/kernel/class_spec.rb
@@ -19,7 +19,7 @@ describe "Kernel#class" do
end
it "returns the first non-singleton class" do
- a = "hello"
+ a = +"hello"
def a.my_singleton_method; end
a.class.should equal(String)
end
diff --git a/spec/ruby/core/kernel/clone_spec.rb b/spec/ruby/core/kernel/clone_spec.rb
index a87c7544fe..5adcbbe603 100644
--- a/spec/ruby/core/kernel/clone_spec.rb
+++ b/spec/ruby/core/kernel/clone_spec.rb
@@ -45,26 +45,18 @@ describe "Kernel#clone" do
end
describe "with freeze: nil" do
- ruby_version_is ""..."3.0" do
- it "raises ArgumentError" do
- -> { @obj.clone(freeze: nil) }.should raise_error(ArgumentError, /unexpected value for freeze: NilClass/)
- end
- end
-
- ruby_version_is "3.0" do
- it "copies frozen state from the original, like #clone without arguments" do
- o2 = @obj.clone(freeze: nil)
- o2.should_not.frozen?
+ it "copies frozen state from the original, like #clone without arguments" do
+ o2 = @obj.clone(freeze: nil)
+ o2.should_not.frozen?
- @obj.freeze
- o3 = @obj.clone(freeze: nil)
- o3.should.frozen?
- end
+ @obj.freeze
+ o3 = @obj.clone(freeze: nil)
+ o3.should.frozen?
+ end
- it "copies frozen?" do
- o = "".freeze.clone(freeze: nil)
- o.frozen?.should be_true
- end
+ it "copies frozen?" do
+ o = "".freeze.clone(freeze: nil)
+ o.frozen?.should be_true
end
end
@@ -74,33 +66,19 @@ describe "Kernel#clone" do
@obj.clone(freeze: true).should.frozen?
end
- ruby_version_is ''...'3.0' do
- it 'does not freeze the copy even if the original is not frozen' do
- @obj.clone(freeze: true).should_not.frozen?
- end
-
- it "calls #initialize_clone with no kwargs" do
- obj = KernelSpecs::CloneFreeze.new
- obj.clone(freeze: true)
- ScratchPad.recorded.should == [obj, {}]
- end
+ it 'freezes the copy even if the original was not frozen' do
+ @obj.clone(freeze: true).should.frozen?
end
- ruby_version_is '3.0' do
- it 'freezes the copy even if the original was not frozen' do
- @obj.clone(freeze: true).should.frozen?
- end
-
- it "calls #initialize_clone with kwargs freeze: true" do
- obj = KernelSpecs::CloneFreeze.new
- obj.clone(freeze: true)
- ScratchPad.recorded.should == [obj, { freeze: true }]
- end
+ it "calls #initialize_clone with kwargs freeze: true" do
+ obj = KernelSpecs::CloneFreeze.new
+ obj.clone(freeze: true)
+ ScratchPad.recorded.should == [obj, { freeze: true }]
+ end
- it "calls #initialize_clone with kwargs freeze: true even if #initialize_clone only takes a single argument" do
- obj = KernelSpecs::Clone.new
- -> { obj.clone(freeze: true) }.should raise_error(ArgumentError, 'wrong number of arguments (given 2, expected 1)')
- end
+ it "calls #initialize_clone with kwargs freeze: true even if #initialize_clone only takes a single argument" do
+ obj = KernelSpecs::Clone.new
+ -> { obj.clone(freeze: true) }.should raise_error(ArgumentError, 'wrong number of arguments (given 2, expected 1)')
end
end
@@ -114,25 +92,15 @@ describe "Kernel#clone" do
@obj.clone(freeze: false).should_not.frozen?
end
- ruby_version_is ''...'3.0' do
- it "calls #initialize_clone with no kwargs" do
- obj = KernelSpecs::CloneFreeze.new
- obj.clone(freeze: false)
- ScratchPad.recorded.should == [obj, {}]
- end
+ it "calls #initialize_clone with kwargs freeze: false" do
+ obj = KernelSpecs::CloneFreeze.new
+ obj.clone(freeze: false)
+ ScratchPad.recorded.should == [obj, { freeze: false }]
end
- ruby_version_is '3.0' do
- it "calls #initialize_clone with kwargs freeze: false" do
- obj = KernelSpecs::CloneFreeze.new
- obj.clone(freeze: false)
- ScratchPad.recorded.should == [obj, { freeze: false }]
- end
-
- it "calls #initialize_clone with kwargs freeze: false even if #initialize_clone only takes a single argument" do
- obj = KernelSpecs::Clone.new
- -> { obj.clone(freeze: false) }.should raise_error(ArgumentError, 'wrong number of arguments (given 2, expected 1)')
- end
+ it "calls #initialize_clone with kwargs freeze: false even if #initialize_clone only takes a single argument" do
+ obj = KernelSpecs::Clone.new
+ -> { obj.clone(freeze: false) }.should raise_error(ArgumentError, 'wrong number of arguments (given 2, expected 1)')
end
end
diff --git a/spec/ruby/core/kernel/eval_spec.rb b/spec/ruby/core/kernel/eval_spec.rb
index 9be0f2dfd3..454bc4a58e 100644
--- a/spec/ruby/core/kernel/eval_spec.rb
+++ b/spec/ruby/core/kernel/eval_spec.rb
@@ -135,7 +135,7 @@ describe "Kernel#eval" do
it "includes file and line information in syntax error" do
expected = 'speccing.rb'
-> {
- eval('if true',TOPLEVEL_BINDING, expected)
+ eval('if true', TOPLEVEL_BINDING, expected)
}.should raise_error(SyntaxError) { |e|
e.message.should =~ /#{expected}:1:.+/
}
@@ -144,7 +144,7 @@ describe "Kernel#eval" do
it "evaluates string with given filename and negative linenumber" do
expected_file = 'speccing.rb'
-> {
- eval('if true',TOPLEVEL_BINDING, expected_file, -100)
+ eval('if true', TOPLEVEL_BINDING, expected_file, -100)
}.should raise_error(SyntaxError) { |e|
e.message.should =~ /#{expected_file}:-100:.+/
}
@@ -159,24 +159,7 @@ describe "Kernel#eval" do
end
end
- ruby_version_is ""..."3.0" do
- it "uses the filename of the binding if none is provided" do
- eval("__FILE__").should == "(eval)"
- suppress_warning {eval("__FILE__", binding)}.should == __FILE__
- eval("__FILE__", binding, "success").should == "success"
- suppress_warning {eval("eval '__FILE__', binding")}.should == "(eval)"
- suppress_warning {eval("eval '__FILE__', binding", binding)}.should == __FILE__
- suppress_warning {eval("eval '__FILE__', binding", binding, 'success')}.should == 'success'
- end
-
- it 'uses the given binding file and line for __FILE__ and __LINE__' do
- suppress_warning {
- eval("[__FILE__, __LINE__]", binding).should == [__FILE__, __LINE__]
- }
- end
- end
-
- ruby_version_is "3.0" do
+ ruby_version_is ""..."3.3" do
it "uses (eval) filename if none is provided" do
eval("__FILE__").should == "(eval)"
eval("__FILE__", binding).should == "(eval)"
@@ -192,6 +175,21 @@ describe "Kernel#eval" do
end
end
+ ruby_version_is "3.3" do
+ it "uses (eval at __FILE__:__LINE__) if none is provided" do
+ eval("__FILE__").should == "(eval at #{__FILE__}:#{__LINE__})"
+ eval("__FILE__", binding).should == "(eval at #{__FILE__}:#{__LINE__})"
+ eval("__FILE__", binding, "success").should == "success"
+ eval("eval '__FILE__', binding").should == "(eval at (eval at #{__FILE__}:#{__LINE__}):1)"
+ eval("eval '__FILE__', binding", binding).should == "(eval at (eval at #{__FILE__}:#{__LINE__}):1)"
+ eval("eval '__FILE__', binding", binding, 'success').should == "(eval at success:1)"
+ eval("eval '__FILE__', binding, 'success'", binding).should == 'success'
+ end
+
+ it 'uses (eval at __FILE__:__LINE__) for __FILE__ and 1 for __LINE__ with a binding argument' do
+ eval("[__FILE__, __LINE__]", binding).should == ["(eval at #{__FILE__}:#{__LINE__})", 1]
+ end
+ end
# Found via Rubinius bug github:#149
it "does not alter the value of __FILE__ in the binding" do
first_time = EvalSpecs.call_eval
@@ -228,6 +226,20 @@ describe "Kernel#eval" do
-> { eval("return :eval") }.call.should == :eval
end
+ it "returns from the method calling #eval when evaluating 'return'" do
+ def eval_return(n)
+ eval("return n*2")
+ end
+ -> { eval_return(3) }.call.should == 6
+ end
+
+ it "returns from the method calling #eval when evaluating 'return' in BEGIN" do
+ def eval_return(n)
+ eval("BEGIN {return n*3}")
+ end
+ -> { eval_return(4) }.call.should == 12
+ end
+
it "unwinds through a Proc-style closure and returns from a lambda-style closure in the closure chain" do
code = fixture __FILE__, "eval_return_with_lambda.rb"
ruby_exe(code).chomp.should == "a,b,c,eval,f"
@@ -249,6 +261,19 @@ describe "Kernel#eval" do
end
end
+ it "makes flip-flop operator work correctly" do
+ ScratchPad.record []
+
+ eval "10.times { |i| ScratchPad << i if (i == 4)...(i == 4) }"
+ ScratchPad.recorded.should == [4, 5, 6, 7, 8, 9]
+
+ ScratchPad.clear
+ end
+
+ it "returns nil if given an empty string" do
+ eval("").should == nil
+ end
+
# See language/magic_comment_spec.rb for more magic comments specs
describe "with a magic encoding comment" do
it "uses the magic comment encoding for the encoding of literal strings" do
@@ -325,12 +350,11 @@ CODE
end
it "allows a magic encoding comment and a subsequent frozen_string_literal magic comment" do
- # Make sure frozen_string_literal is not default true
- eval("'foo'".b).frozen?.should be_false
+ frozen_string_default = "test".frozen?
code = <<CODE.b
# encoding: UTF-8
-# frozen_string_literal: true
+# frozen_string_literal: #{!frozen_string_default}
class EvalSpecs
VÏ€string = "frozen"
end
@@ -340,7 +364,7 @@ CODE
EvalSpecs.constants(false).should include(:"VÏ€string")
EvalSpecs::VÏ€string.should == "frozen"
EvalSpecs::VÏ€string.encoding.should == Encoding::UTF_8
- EvalSpecs::VÏ€string.frozen?.should be_true
+ EvalSpecs::VÏ€string.frozen?.should == !frozen_string_default
end
it "allows a magic encoding comment and a frozen_string_literal magic comment on the same line in emacs style" do
@@ -359,8 +383,9 @@ CODE
end
it "ignores the magic encoding comment if it is after a frozen_string_literal magic comment" do
+ frozen_string_default = "test".frozen?
code = <<CODE.b
-# frozen_string_literal: true
+# frozen_string_literal: #{!frozen_string_default}
# encoding: UTF-8
class EvalSpecs
VÏ€frozen_first = "frozen"
@@ -374,23 +399,24 @@ CODE
value = EvalSpecs.const_get(binary_constant)
value.should == "frozen"
value.encoding.should == Encoding::BINARY
- value.frozen?.should be_true
+ value.frozen?.should == !frozen_string_default
end
it "ignores the frozen_string_literal magic comment if it appears after a token and warns if $VERBOSE is true" do
+ frozen_string_default = "test".frozen?
code = <<CODE
some_token_before_magic_comment = :anything
-# frozen_string_literal: true
+# frozen_string_literal: #{!frozen_string_default}
class EvalSpecs
VÏ€string_not_frozen = "not frozen"
end
CODE
- -> { eval(code) }.should complain(/warning: `frozen_string_literal' is ignored after any tokens/, verbose: true)
- EvalSpecs::VÏ€string_not_frozen.frozen?.should be_false
+ -> { eval(code) }.should complain(/warning: [`']frozen_string_literal' is ignored after any tokens/, verbose: true)
+ EvalSpecs::VÏ€string_not_frozen.frozen?.should == frozen_string_default
EvalSpecs.send :remove_const, :VÏ€string_not_frozen
-> { eval(code) }.should_not complain(verbose: false)
- EvalSpecs::VÏ€string_not_frozen.frozen?.should be_false
+ EvalSpecs::VÏ€string_not_frozen.frozen?.should == frozen_string_default
EvalSpecs.send :remove_const, :VÏ€string_not_frozen
end
end
diff --git a/spec/ruby/core/kernel/exec_spec.rb b/spec/ruby/core/kernel/exec_spec.rb
index 1b4a7ae6f4..3d9520ad67 100644
--- a/spec/ruby/core/kernel/exec_spec.rb
+++ b/spec/ruby/core/kernel/exec_spec.rb
@@ -7,12 +7,12 @@ describe "Kernel#exec" do
end
it "runs the specified command, replacing current process" do
- ruby_exe('exec "echo hello"; puts "fail"', escape: true).should == "hello\n"
+ ruby_exe('exec "echo hello"; puts "fail"').should == "hello\n"
end
end
describe "Kernel.exec" do
it "runs the specified command, replacing current process" do
- ruby_exe('Kernel.exec "echo hello"; puts "fail"', escape: true).should == "hello\n"
+ ruby_exe('Kernel.exec "echo hello"; puts "fail"').should == "hello\n"
end
end
diff --git a/spec/ruby/core/kernel/initialize_clone_spec.rb b/spec/ruby/core/kernel/initialize_clone_spec.rb
index 2d889f5aad..21a90c19f0 100644
--- a/spec/ruby/core/kernel/initialize_clone_spec.rb
+++ b/spec/ruby/core/kernel/initialize_clone_spec.rb
@@ -18,11 +18,9 @@ describe "Kernel#initialize_clone" do
a.send(:initialize_clone, b)
end
- ruby_version_is "3.0" do
- it "accepts a :freeze keyword argument for obj.clone(freeze: value)" do
- a = Object.new
- b = Object.new
- a.send(:initialize_clone, b, freeze: true).should == a
- end
+ it "accepts a :freeze keyword argument for obj.clone(freeze: value)" do
+ a = Object.new
+ b = Object.new
+ a.send(:initialize_clone, b, freeze: true).should == a
end
end
diff --git a/spec/ruby/core/kernel/initialize_copy_spec.rb b/spec/ruby/core/kernel/initialize_copy_spec.rb
index fe08d184ad..d71ca9f60f 100644
--- a/spec/ruby/core/kernel/initialize_copy_spec.rb
+++ b/spec/ruby/core/kernel/initialize_copy_spec.rb
@@ -1,11 +1,18 @@
require_relative '../../spec_helper'
describe "Kernel#initialize_copy" do
+ it "returns self" do
+ obj = Object.new
+ obj.send(:initialize_copy, obj).should.equal?(obj)
+ end
+
it "does nothing if the argument is the same as the receiver" do
obj = Object.new
obj.send(:initialize_copy, obj).should.equal?(obj)
- obj.freeze
+
+ obj = Object.new.freeze
obj.send(:initialize_copy, obj).should.equal?(obj)
+
1.send(:initialize_copy, 1).should.equal?(1)
end
diff --git a/spec/ruby/core/kernel/iterator_spec.rb b/spec/ruby/core/kernel/iterator_spec.rb
deleted file mode 100644
index 3fe8317f26..0000000000
--- a/spec/ruby/core/kernel/iterator_spec.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-
-ruby_version_is ""..."3.0" do
- describe "Kernel#iterator?" do
- it "is a private method" do
- Kernel.should have_private_instance_method(:iterator?)
- end
- end
-
- describe "Kernel.iterator?" do
- it "needs to be reviewed for spec completeness"
- end
-end
diff --git a/spec/ruby/core/kernel/lambda_spec.rb b/spec/ruby/core/kernel/lambda_spec.rb
index 2aa4d4f2fb..565536ac0d 100644
--- a/spec/ruby/core/kernel/lambda_spec.rb
+++ b/spec/ruby/core/kernel/lambda_spec.rb
@@ -26,42 +26,44 @@ describe "Kernel.lambda" do
l.lambda?.should be_true
end
- it "creates a lambda-style Proc if given a literal block via Kernel.public_send" do
- suppress_warning do
- l = Kernel.public_send(:lambda) { 42 }
- l.lambda?.should be_true
+ ruby_version_is ""..."3.3" do
+ it "creates a lambda-style Proc if given a literal block via Kernel.public_send" do
+ suppress_warning do
+ l = Kernel.public_send(:lambda) { 42 }
+ l.lambda?.should be_true
+ end
end
- end
- it "returns the passed Proc if given an existing Proc" do
- some_proc = proc {}
- l = suppress_warning {lambda(&some_proc)}
- l.should equal(some_proc)
- l.lambda?.should be_false
- end
+ it "returns the passed Proc if given an existing Proc" do
+ some_proc = proc {}
+ l = suppress_warning {lambda(&some_proc)}
+ l.should equal(some_proc)
+ l.lambda?.should be_false
+ end
- it "creates a lambda-style Proc when called with zsuper" do
- suppress_warning do
- l = KernelSpecs::LambdaSpecs::ForwardBlockWithZSuper.new.lambda { 42 }
- l.lambda?.should be_true
- l.call.should == 42
+ it "creates a lambda-style Proc when called with zsuper" do
+ suppress_warning do
+ l = KernelSpecs::LambdaSpecs::ForwardBlockWithZSuper.new.lambda { 42 }
+ l.lambda?.should be_true
+ l.call.should == 42
- lambda { l.call(:extra) }.should raise_error(ArgumentError)
+ lambda { l.call(:extra) }.should raise_error(ArgumentError)
+ end
end
- end
- it "returns the passed Proc if given an existing Proc through super" do
- some_proc = proc { }
- l = KernelSpecs::LambdaSpecs::SuperAmpersand.new.lambda(&some_proc)
- l.should equal(some_proc)
- l.lambda?.should be_false
- end
+ it "returns the passed Proc if given an existing Proc through super" do
+ some_proc = proc { }
+ l = KernelSpecs::LambdaSpecs::SuperAmpersand.new.lambda(&some_proc)
+ l.should equal(some_proc)
+ l.lambda?.should be_false
+ end
- it "does not create lambda-style Procs when captured with #method" do
- kernel_lambda = method(:lambda)
- l = suppress_warning {kernel_lambda.call { 42 }}
- l.lambda?.should be_false
- l.call(:extra).should == 42
+ it "does not create lambda-style Procs when captured with #method" do
+ kernel_lambda = method(:lambda)
+ l = suppress_warning {kernel_lambda.call { 42 }}
+ l.lambda?.should be_false
+ l.call(:extra).should == 42
+ end
end
it "checks the arity of the call when no args are specified" do
@@ -136,15 +138,21 @@ describe "Kernel.lambda" do
klass.new.ret.should == 1
end
- ruby_version_is "3.0" do
- context "when called without a literal block" do
+ context "when called without a literal block" do
+ ruby_version_is ""..."3.3" do
it "warns when proc isn't a lambda" do
-> { lambda(&proc{}) }.should complain("#{__FILE__}:#{__LINE__}: warning: lambda without a literal block is deprecated; use the proc without lambda instead\n")
end
+ end
- it "doesn't warn when proc is lambda" do
- -> { lambda(&lambda{}) }.should_not complain(verbose: true)
+ ruby_version_is "3.3" do
+ it "raises when proc isn't a lambda" do
+ -> { lambda(&proc{}) }.should raise_error(ArgumentError, /the lambda method requires a literal block/)
end
end
+
+ it "doesn't warn when proc is lambda" do
+ -> { lambda(&lambda{}) }.should_not complain(verbose: true)
+ end
end
end
diff --git a/spec/ruby/core/kernel/not_match_spec.rb b/spec/ruby/core/kernel/not_match_spec.rb
index 906f18df2c..f8dd82fad8 100644
--- a/spec/ruby/core/kernel/not_match_spec.rb
+++ b/spec/ruby/core/kernel/not_match_spec.rb
@@ -14,6 +14,20 @@ describe "Kernel#!~" do
(obj !~ :foo).should == false
end
+ ruby_version_is ""..."3.2" do
+ it "returns true if self does not respond to #=~" do
+ suppress_warning do
+ (Object.new !~ :foo).should == true
+ end
+ end
+ end
+
+ ruby_version_is "3.2" do
+ it "raises NoMethodError if self does not respond to #=~" do
+ -> { Object.new !~ :foo }.should raise_error(NoMethodError)
+ end
+ end
+
it 'can be overridden in subclasses' do
obj = KernelSpecs::NotMatch.new
(obj !~ :bar).should == :foo
diff --git a/spec/ruby/core/kernel/open_spec.rb b/spec/ruby/core/kernel/open_spec.rb
index bad2ae9d2c..bb42c31f31 100644
--- a/spec/ruby/core/kernel/open_spec.rb
+++ b/spec/ruby/core/kernel/open_spec.rb
@@ -29,7 +29,9 @@ describe "Kernel#open" do
platform_is_not :windows, :wasi do
it "opens an io when path starts with a pipe" do
- @io = open("|date")
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ @io = open("|date")
+ end
begin
@io.should be_kind_of(IO)
@io.read
@@ -39,21 +41,27 @@ describe "Kernel#open" do
end
it "opens an io when called with a block" do
- @output = open("|date") { |f| f.read }
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ @output = open("|date") { |f| f.read }
+ end
@output.should_not == ''
end
it "opens an io for writing" do
- -> do
- bytes = open("|cat", "w") { |io| io.write(".") }
- bytes.should == 1
- end.should output_to_fd(".")
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ -> {
+ bytes = open("|cat", "w") { |io| io.write(".") }
+ bytes.should == 1
+ }.should output_to_fd(".")
+ end
end
end
platform_is :windows do
it "opens an io when path starts with a pipe" do
- @io = open("|date /t")
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ @io = open("|date /t")
+ end
begin
@io.should be_kind_of(IO)
@io.read
@@ -63,24 +71,34 @@ describe "Kernel#open" do
end
it "opens an io when called with a block" do
- @output = open("|date /t") { |f| f.read }
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ @output = open("|date /t") { |f| f.read }
+ end
@output.should_not == ''
end
end
+ ruby_version_is "3.3" do
+ # https://bugs.ruby-lang.org/issues/19630
+ it "warns about deprecation given a path with a pipe" do
+ cmd = "|echo ok"
+ -> {
+ open(cmd) { |f| f.read }
+ }.should complain(/Kernel#open with a leading '\|'/)
+ end
+ end
+
it "raises an ArgumentError if not passed one argument" do
-> { open }.should raise_error(ArgumentError)
end
- ruby_version_is "3.0" do
- it "accepts options as keyword arguments" do
- @file = open(@name, "r", 0666, flags: File::CREAT)
- @file.should be_kind_of(File)
+ it "accepts options as keyword arguments" do
+ @file = open(@name, "r", 0666, flags: File::CREAT)
+ @file.should be_kind_of(File)
- -> {
- open(@name, "r", 0666, {flags: File::CREAT})
- }.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 1..3)")
- end
+ -> {
+ open(@name, "r", 0666, {flags: File::CREAT})
+ }.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 1..3)")
end
describe "when given an object that responds to to_open" do
@@ -158,28 +176,14 @@ describe "Kernel#open" do
open(@name, nil, nil) { |f| f.gets }.should == @content
end
- ruby_version_is ""..."3.0" do
- it "works correctly when redefined by open-uri" do
- code = <<~RUBY
+ it "is not redefined by open-uri" do
+ code = <<~RUBY
+ before = Kernel.instance_method(:open)
require 'open-uri'
- obj = Object.new
- def obj.to_open; self; end
- p open(obj) == obj
- RUBY
- ruby_exe(code, args: "2>&1").should == "true\n"
- end
- end
-
- ruby_version_is "3.0" do
- it "is not redefined by open-uri" do
- code = <<~RUBY
- before = Kernel.instance_method(:open)
- require 'open-uri'
- after = Kernel.instance_method(:open)
- p before == after
- RUBY
- ruby_exe(code, args: "2>&1").should == "true\n"
- end
+ after = Kernel.instance_method(:open)
+ p before == after
+ RUBY
+ ruby_exe(code, args: "2>&1").should == "true\n"
end
end
diff --git a/spec/ruby/core/kernel/printf_spec.rb b/spec/ruby/core/kernel/printf_spec.rb
index d8f93ce429..61bf955c25 100644
--- a/spec/ruby/core/kernel/printf_spec.rb
+++ b/spec/ruby/core/kernel/printf_spec.rb
@@ -31,6 +31,13 @@ describe "Kernel.printf" do
object.should_receive(:write).with("string")
Kernel.printf(object, "%s", "string")
end
+
+ it "calls #to_str to convert the format object to a String" do
+ object = mock('format string')
+ object.should_receive(:to_str).and_return("to_str: %i")
+ $stdout.should_receive(:write).with("to_str: 42")
+ Kernel.printf($stdout, object, 42)
+ end
end
describe "Kernel.printf" do
diff --git a/spec/ruby/core/kernel/proc_spec.rb b/spec/ruby/core/kernel/proc_spec.rb
index 231c1f0dfb..6553b8fd04 100644
--- a/spec/ruby/core/kernel/proc_spec.rb
+++ b/spec/ruby/core/kernel/proc_spec.rb
@@ -40,19 +40,9 @@ describe "Kernel#proc" do
proc
end
- ruby_version_is ""..."3.0" do
- it "can be created when called with no block" do
- -> {
- some_method { "hello" }
- }.should complain(/Capturing the given block using Kernel#proc is deprecated/)
- end
- end
-
- ruby_version_is "3.0" do
- it "raises an ArgumentError when passed no block" do
- -> {
- some_method { "hello" }
- }.should raise_error(ArgumentError, 'tried to create Proc object without a block')
- end
+ it "raises an ArgumentError when passed no block" do
+ -> {
+ some_method { "hello" }
+ }.should raise_error(ArgumentError, 'tried to create Proc object without a block')
end
end
diff --git a/spec/ruby/core/kernel/public_send_spec.rb b/spec/ruby/core/kernel/public_send_spec.rb
index 4dae419ff9..b684b1729c 100644
--- a/spec/ruby/core/kernel/public_send_spec.rb
+++ b/spec/ruby/core/kernel/public_send_spec.rb
@@ -105,11 +105,11 @@ describe "Kernel#public_send" do
end
it "includes `public_send` in the backtrace when passed not enough arguments" do
- -> { public_send() }.should raise_error(ArgumentError) { |e| e.backtrace[0].should.include?("`public_send'") }
+ -> { public_send() }.should raise_error(ArgumentError) { |e| e.backtrace[0].should =~ /[`'](?:Kernel#)?public_send'/ }
end
it "includes `public_send` in the backtrace when passed a single incorrect argument" do
- -> { public_send(Object.new) }.should raise_error(TypeError) { |e| e.backtrace[0].should.include?("`public_send'") }
+ -> { public_send(Object.new) }.should raise_error(TypeError) { |e| e.backtrace[0].should =~ /[`'](?:Kernel#)?public_send'/ }
end
it_behaves_like :basicobject_send, :public_send
diff --git a/spec/ruby/core/kernel/require_relative_spec.rb b/spec/ruby/core/kernel/require_relative_spec.rb
index 5999855de6..6188d13a4e 100644
--- a/spec/ruby/core/kernel/require_relative_spec.rb
+++ b/spec/ruby/core/kernel/require_relative_spec.rb
@@ -5,9 +5,9 @@ describe "Kernel#require_relative with a relative path" do
before :each do
CodeLoadingSpecs.spec_setup
@dir = "../../fixtures/code"
- @abs_dir = File.realpath(@dir, File.dirname(__FILE__))
+ @abs_dir = File.realpath(@dir, __dir__)
@path = "#{@dir}/load_fixture.rb"
- @abs_path = File.realpath(@path, File.dirname(__FILE__))
+ @abs_path = File.realpath(@path, __dir__)
end
after :each do
@@ -92,7 +92,7 @@ describe "Kernel#require_relative with a relative path" do
it "raises a LoadError that includes the missing path" do
missing_path = "#{@dir}/nonexistent.rb"
- expanded_missing_path = File.expand_path(missing_path, File.dirname(__FILE__))
+ expanded_missing_path = File.expand_path(missing_path, __dir__)
-> { require_relative(missing_path) }.should raise_error(LoadError) { |e|
e.message.should include(expanded_missing_path)
e.path.should == expanded_missing_path
@@ -277,7 +277,7 @@ end
describe "Kernel#require_relative with an absolute path" do
before :each do
CodeLoadingSpecs.spec_setup
- @dir = File.expand_path "../../fixtures/code", File.dirname(__FILE__)
+ @dir = File.expand_path "../../fixtures/code", __dir__
@abs_dir = @dir
@path = File.join @dir, "load_fixture.rb"
@abs_path = @path
diff --git a/spec/ruby/core/kernel/require_spec.rb b/spec/ruby/core/kernel/require_spec.rb
index 896afb840a..4029e68725 100644
--- a/spec/ruby/core/kernel/require_spec.rb
+++ b/spec/ruby/core/kernel/require_spec.rb
@@ -26,7 +26,7 @@ describe "Kernel#require" do
features = out.lines.map { |line| File.basename(line.chomp, '.*') }
# Ignore CRuby internals
- features -= %w[encdb transdb windows_1252]
+ features -= %w[encdb transdb windows_1252 windows_31j]
features.reject! { |feature| feature.end_with?('-fake') }
features.sort.should == provided.sort
diff --git a/spec/ruby/core/kernel/shared/load.rb b/spec/ruby/core/kernel/shared/load.rb
index 5c41c19bf6..0fe2d5ce16 100644
--- a/spec/ruby/core/kernel/shared/load.rb
+++ b/spec/ruby/core/kernel/shared/load.rb
@@ -1,5 +1,6 @@
main = self
+# The big difference is Kernel#load does not attempt to add an extension to the passed path, unlike Kernel#require
describe :kernel_load, shared: true do
before :each do
CodeLoadingSpecs.spec_setup
@@ -10,22 +11,31 @@ describe :kernel_load, shared: true do
CodeLoadingSpecs.spec_cleanup
end
- it "loads a non-extensioned file as a Ruby source file" do
- path = File.expand_path "load_fixture", CODE_LOADING_DIR
- @object.load(path).should be_true
- ScratchPad.recorded.should == [:no_ext]
- end
+ describe "(path resolution)" do
+ # This behavior is specific to Kernel#load, it differs for Kernel#require
+ it "loads a non-extensioned file as a Ruby source file" do
+ path = File.expand_path "load_fixture", CODE_LOADING_DIR
+ @object.load(path).should be_true
+ ScratchPad.recorded.should == [:no_ext]
+ end
- it "loads a non .rb extensioned file as a Ruby source file" do
- path = File.expand_path "load_fixture.ext", CODE_LOADING_DIR
- @object.load(path).should be_true
- ScratchPad.recorded.should == [:no_rb_ext]
- end
+ it "loads a non .rb extensioned file as a Ruby source file" do
+ path = File.expand_path "load_fixture.ext", CODE_LOADING_DIR
+ @object.load(path).should be_true
+ ScratchPad.recorded.should == [:no_rb_ext]
+ end
- it "loads from the current working directory" do
- Dir.chdir CODE_LOADING_DIR do
- @object.load("load_fixture.rb").should be_true
- ScratchPad.recorded.should == [:loaded]
+ it "loads from the current working directory" do
+ Dir.chdir CODE_LOADING_DIR do
+ @object.load("load_fixture.rb").should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+ end
+
+ # This behavior is specific to Kernel#load, it differs for Kernel#require
+ it "does not look for a c-extension file when passed a path without extension (when no .rb is present)" do
+ path = File.join CODE_LOADING_DIR, "a", "load_fixture"
+ -> { @object.send(@method, path) }.should raise_error(LoadError)
end
end
diff --git a/spec/ruby/core/kernel/shared/require.rb b/spec/ruby/core/kernel/shared/require.rb
index 5cbc11c9ec..250813191b 100644
--- a/spec/ruby/core/kernel/shared/require.rb
+++ b/spec/ruby/core/kernel/shared/require.rb
@@ -212,6 +212,34 @@ end
describe :kernel_require, shared: true do
describe "(path resolution)" do
+ it "loads .rb file when passed absolute path without extension" do
+ path = File.expand_path "load_fixture", CODE_LOADING_DIR
+ @object.send(@method, path).should be_true
+ # This should _not_ be [:no_ext]
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ platform_is :linux, :darwin do
+ it "loads c-extension file when passed absolute path without extension when no .rb is present" do
+ # the error message is specific to what dlerror() returns
+ path = File.join CODE_LOADING_DIR, "a", "load_fixture"
+ -> { @object.send(@method, path) }.should raise_error(Exception, /file too short|not a mach-o file/)
+ end
+ end
+
+ platform_is :darwin do
+ it "loads .bundle file when passed absolute path with .so" do
+ # the error message is specific to what dlerror() returns
+ path = File.join CODE_LOADING_DIR, "a", "load_fixture.so"
+ -> { @object.send(@method, path) }.should raise_error(Exception, /load_fixture\.bundle.+(file too short|not a mach-o file)/)
+ end
+ end
+
+ it "does not try an extra .rb if the path already ends in .rb" do
+ path = File.join CODE_LOADING_DIR, "d", "load_fixture.rb"
+ -> { @object.send(@method, path) }.should raise_error(LoadError)
+ end
+
# For reference see [ruby-core:24155] in which matz confirms this feature is
# intentional for security reasons.
it "does not load a bare filename unless the current working directory is in $LOAD_PATH" do
@@ -262,13 +290,11 @@ describe :kernel_require, shared: true do
ScratchPad.recorded.should == [:loaded]
end
- ruby_bug "#16926", ""..."3.0" do
- it "does not load a feature twice when $LOAD_PATH has been modified" do
- $LOAD_PATH.replace [CODE_LOADING_DIR]
- @object.require("load_fixture").should be_true
- $LOAD_PATH.replace [File.expand_path("b", CODE_LOADING_DIR), CODE_LOADING_DIR]
- @object.require("load_fixture").should be_false
- end
+ it "does not load a feature twice when $LOAD_PATH has been modified" do
+ $LOAD_PATH.replace [CODE_LOADING_DIR]
+ @object.require("load_fixture").should be_true
+ $LOAD_PATH.replace [File.expand_path("b", CODE_LOADING_DIR), CODE_LOADING_DIR]
+ @object.require("load_fixture").should be_false
end
end
@@ -566,23 +592,21 @@ describe :kernel_require, shared: true do
-> { @object.require("unicode_normalize") }.should raise_error(LoadError)
end
- ruby_version_is "3.0" do
- it "does not load a file earlier on the $LOAD_PATH when other similar features were already loaded" do
- Dir.chdir CODE_LOADING_DIR do
- @object.send(@method, "../code/load_fixture").should be_true
- end
- ScratchPad.recorded.should == [:loaded]
+ it "does not load a file earlier on the $LOAD_PATH when other similar features were already loaded" do
+ Dir.chdir CODE_LOADING_DIR do
+ @object.send(@method, "../code/load_fixture").should be_true
+ end
+ ScratchPad.recorded.should == [:loaded]
- $LOAD_PATH.unshift "#{CODE_LOADING_DIR}/b"
- # This loads because the above load was not on the $LOAD_PATH
- @object.send(@method, "load_fixture").should be_true
- ScratchPad.recorded.should == [:loaded, :loaded]
+ $LOAD_PATH.unshift "#{CODE_LOADING_DIR}/b"
+ # This loads because the above load was not on the $LOAD_PATH
+ @object.send(@method, "load_fixture").should be_true
+ ScratchPad.recorded.should == [:loaded, :loaded]
- $LOAD_PATH.unshift "#{CODE_LOADING_DIR}/c"
- # This does not load because the above load was on the $LOAD_PATH
- @object.send(@method, "load_fixture").should be_false
- ScratchPad.recorded.should == [:loaded, :loaded]
- end
+ $LOAD_PATH.unshift "#{CODE_LOADING_DIR}/c"
+ # This does not load because the above load was on the $LOAD_PATH
+ @object.send(@method, "load_fixture").should be_false
+ ScratchPad.recorded.should == [:loaded, :loaded]
end
end
diff --git a/spec/ruby/core/kernel/shared/sprintf.rb b/spec/ruby/core/kernel/shared/sprintf.rb
index 2db50bd686..13dc6e97f0 100644
--- a/spec/ruby/core/kernel/shared/sprintf.rb
+++ b/spec/ruby/core/kernel/shared/sprintf.rb
@@ -356,13 +356,13 @@ describe :kernel_sprintf, shared: true do
it "raises TypeError if converting to Integer with to_int returns non-Integer" do
obj = BasicObject.new
- def obj.to_str
+ def obj.to_int
:foo
end
-> {
@method.call("%c", obj)
- }.should raise_error(TypeError, /can't convert BasicObject to String/)
+ }.should raise_error(TypeError, /can't convert BasicObject to Integer/)
end
end
diff --git a/spec/ruby/core/kernel/shared/sprintf_encoding.rb b/spec/ruby/core/kernel/shared/sprintf_encoding.rb
index 9cedb8b662..7ec0fe4c48 100644
--- a/spec/ruby/core/kernel/shared/sprintf_encoding.rb
+++ b/spec/ruby/core/kernel/shared/sprintf_encoding.rb
@@ -14,14 +14,14 @@ describe :kernel_sprintf_encoding, shared: true do
end
it "returns a String in the same encoding as the format String if compatible" do
- string = "%s".force_encoding(Encoding::KOI8_U)
+ string = "%s".dup.force_encoding(Encoding::KOI8_U)
result = @method.call(string, "dogs")
result.encoding.should equal(Encoding::KOI8_U)
end
it "returns a String in the argument's encoding if format encoding is more restrictive" do
- string = "foo %s".force_encoding(Encoding::US_ASCII)
- argument = "b\303\274r".force_encoding(Encoding::UTF_8)
+ string = "foo %s".dup.force_encoding(Encoding::US_ASCII)
+ argument = "b\303\274r".dup.force_encoding(Encoding::UTF_8)
result = @method.call(string, argument)
result.encoding.should equal(Encoding::UTF_8)
@@ -56,7 +56,7 @@ describe :kernel_sprintf_encoding, shared: true do
end
it "uses the encoding of the format string to interpret codepoints" do
- format = "%c".force_encoding("euc-jp")
+ format = "%c".dup.force_encoding("euc-jp")
result = @method.call(format, 9415601)
result.encoding.should == Encoding::EUC_JP
diff --git a/spec/ruby/core/kernel/singleton_class_spec.rb b/spec/ruby/core/kernel/singleton_class_spec.rb
index 4865e29c10..23c400f9bd 100644
--- a/spec/ruby/core/kernel/singleton_class_spec.rb
+++ b/spec/ruby/core/kernel/singleton_class_spec.rb
@@ -1,3 +1,4 @@
+# truffleruby_primitives: true
require_relative '../../spec_helper'
describe "Kernel#singleton_class" do
@@ -42,4 +43,32 @@ describe "Kernel#singleton_class" do
obj.freeze
obj.singleton_class.frozen?.should be_true
end
+
+ context "for an IO object with a replaced singleton class" do
+ it "looks up singleton methods from the fresh singleton class after an object instance got a new one" do
+ proxy = -> io { io.foo }
+ if RUBY_ENGINE == 'truffleruby'
+ # We need an inline cache with only this object seen, the best way to do that is to use a Primitive
+ sclass = -> io { Primitive.singleton_class(io) }
+ else
+ sclass = -> io { io.singleton_class }
+ end
+
+ io = File.new(__FILE__)
+ io.define_singleton_method(:foo) { "old" }
+ sclass1 = sclass.call(io)
+ proxy.call(io).should == "old"
+
+ # IO#reopen is the only method which can replace an object's singleton class
+ io2 = File.new(__FILE__)
+ io.reopen(io2)
+ io.define_singleton_method(:foo) { "new" }
+ sclass2 = sclass.call(io)
+ sclass2.should_not.equal?(sclass1)
+ proxy.call(io).should == "new"
+ ensure
+ io2.close
+ io.close
+ end
+ end
end
diff --git a/spec/ruby/core/kernel/sleep_spec.rb b/spec/ruby/core/kernel/sleep_spec.rb
index 44b417a92e..0570629723 100644
--- a/spec/ruby/core/kernel/sleep_spec.rb
+++ b/spec/ruby/core/kernel/sleep_spec.rb
@@ -1,5 +1,4 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
describe "Kernel#sleep" do
it "is a private method" do
diff --git a/spec/ruby/core/kernel/sprintf_spec.rb b/spec/ruby/core/kernel/sprintf_spec.rb
index 7adf71be76..9ef7f86f16 100644
--- a/spec/ruby/core/kernel/sprintf_spec.rb
+++ b/spec/ruby/core/kernel/sprintf_spec.rb
@@ -3,6 +3,14 @@ require_relative 'fixtures/classes'
require_relative 'shared/sprintf'
require_relative 'shared/sprintf_encoding'
+describe :kernel_sprintf_to_str, shared: true do
+ it "calls #to_str to convert the format object to a String" do
+ obj = mock('format string')
+ obj.should_receive(:to_str).and_return("to_str: %i")
+ @method.call(obj, 42).should == "to_str: 42"
+ end
+end
+
describe "Kernel#sprintf" do
it_behaves_like :kernel_sprintf, -> format, *args {
sprintf(format, *args)
@@ -11,6 +19,10 @@ describe "Kernel#sprintf" do
it_behaves_like :kernel_sprintf_encoding, -> format, *args {
sprintf(format, *args)
}
+
+ it_behaves_like :kernel_sprintf_to_str, -> format, *args {
+ sprintf(format, *args)
+ }
end
describe "Kernel.sprintf" do
@@ -21,4 +33,8 @@ describe "Kernel.sprintf" do
it_behaves_like :kernel_sprintf_encoding, -> format, *args {
Kernel.sprintf(format, *args)
}
+
+ it_behaves_like :kernel_sprintf_to_str, -> format, *args {
+ Kernel.sprintf(format, *args)
+ }
end
diff --git a/spec/ruby/core/kernel/system_spec.rb b/spec/ruby/core/kernel/system_spec.rb
index 9671a650cc..9bc03924dd 100644
--- a/spec/ruby/core/kernel/system_spec.rb
+++ b/spec/ruby/core/kernel/system_spec.rb
@@ -64,6 +64,23 @@ describe :kernel_system, shared: true do
end
end
+ platform_is_not :windows do
+ before :each do
+ require 'tmpdir'
+ @shell_command = File.join(Dir.mktmpdir, "noshebang.cmd")
+ File.write(@shell_command, %[echo "$PATH"\n], perm: 0o700)
+ end
+
+ after :each do
+ File.unlink(@shell_command)
+ Dir.rmdir(File.dirname(@shell_command))
+ end
+
+ it "executes with `sh` if the command is executable but not binary and there is no shebang" do
+ -> { @object.system(@shell_command) }.should output_to_fd(ENV['PATH'] + "\n")
+ end
+ end
+
before :each do
ENV['TEST_SH_EXPANSION'] = 'foo'
@shell_var = '$TEST_SH_EXPANSION'
diff --git a/spec/ruby/core/kernel/taint_spec.rb b/spec/ruby/core/kernel/taint_spec.rb
index eff9b4a450..0c16b1dbbf 100644
--- a/spec/ruby/core/kernel/taint_spec.rb
+++ b/spec/ruby/core/kernel/taint_spec.rb
@@ -4,9 +4,11 @@ require_relative 'fixtures/classes'
describe "Kernel#taint" do
ruby_version_is ""..."3.2" do
it "is a no-op" do
- o = Object.new
- o.taint
- o.should_not.tainted?
+ suppress_warning do
+ o = Object.new
+ o.taint
+ o.should_not.tainted?
+ end
end
it "warns in verbose mode" do
diff --git a/spec/ruby/core/kernel/tainted_spec.rb b/spec/ruby/core/kernel/tainted_spec.rb
index 4e81971e58..fcae433069 100644
--- a/spec/ruby/core/kernel/tainted_spec.rb
+++ b/spec/ruby/core/kernel/tainted_spec.rb
@@ -4,11 +4,13 @@ require_relative 'fixtures/classes'
describe "Kernel#tainted?" do
ruby_version_is ""..."3.2" do
it "is a no-op" do
- o = mock('o')
- p = mock('p')
- p.taint
- o.should_not.tainted?
- p.should_not.tainted?
+ suppress_warning do
+ o = mock('o')
+ p = mock('p')
+ p.taint
+ o.should_not.tainted?
+ p.should_not.tainted?
+ end
end
it "warns in verbose mode" do
diff --git a/spec/ruby/core/kernel/test_spec.rb b/spec/ruby/core/kernel/test_spec.rb
index abb365aed2..d26dc06361 100644
--- a/spec/ruby/core/kernel/test_spec.rb
+++ b/spec/ruby/core/kernel/test_spec.rb
@@ -3,8 +3,8 @@ require_relative 'fixtures/classes'
describe "Kernel#test" do
before :all do
- @file = File.dirname(__FILE__) + '/fixtures/classes.rb'
- @dir = File.dirname(__FILE__) + '/fixtures'
+ @file = __dir__ + '/fixtures/classes.rb'
+ @dir = __dir__ + '/fixtures'
end
it "is a private method" do
diff --git a/spec/ruby/core/kernel/trust_spec.rb b/spec/ruby/core/kernel/trust_spec.rb
index 3a49ab3085..db6f17e0fb 100644
--- a/spec/ruby/core/kernel/trust_spec.rb
+++ b/spec/ruby/core/kernel/trust_spec.rb
@@ -4,10 +4,12 @@ require_relative 'fixtures/classes'
describe "Kernel#trust" do
ruby_version_is ""..."3.2" do
it "is a no-op" do
- o = Object.new.untrust
- o.should_not.untrusted?
- o.trust
- o.should_not.untrusted?
+ suppress_warning do
+ o = Object.new.untrust
+ o.should_not.untrusted?
+ o.trust
+ o.should_not.untrusted?
+ end
end
it "warns in verbose mode" do
diff --git a/spec/ruby/core/kernel/untaint_spec.rb b/spec/ruby/core/kernel/untaint_spec.rb
index a543025c5d..26b2aabbe9 100644
--- a/spec/ruby/core/kernel/untaint_spec.rb
+++ b/spec/ruby/core/kernel/untaint_spec.rb
@@ -4,10 +4,12 @@ require_relative 'fixtures/classes'
describe "Kernel#untaint" do
ruby_version_is ""..."3.2" do
it "is a no-op" do
- o = Object.new.taint
- o.should_not.tainted?
- o.untaint
- o.should_not.tainted?
+ suppress_warning do
+ o = Object.new.taint
+ o.should_not.tainted?
+ o.untaint
+ o.should_not.tainted?
+ end
end
it "warns in verbose mode" do
diff --git a/spec/ruby/core/kernel/untrust_spec.rb b/spec/ruby/core/kernel/untrust_spec.rb
index 6eefb624c0..5310cd8eb4 100644
--- a/spec/ruby/core/kernel/untrust_spec.rb
+++ b/spec/ruby/core/kernel/untrust_spec.rb
@@ -4,9 +4,11 @@ require_relative 'fixtures/classes'
describe "Kernel#untrust" do
ruby_version_is ""..."3.2" do
it "is a no-op" do
- o = Object.new
- o.untrust
- o.should_not.untrusted?
+ suppress_warning do
+ o = Object.new
+ o.untrust
+ o.should_not.untrusted?
+ end
end
it "warns in verbose mode" do
diff --git a/spec/ruby/core/kernel/untrusted_spec.rb b/spec/ruby/core/kernel/untrusted_spec.rb
index bd0bf154ed..ea36d6c98c 100644
--- a/spec/ruby/core/kernel/untrusted_spec.rb
+++ b/spec/ruby/core/kernel/untrusted_spec.rb
@@ -4,10 +4,12 @@ require_relative 'fixtures/classes'
describe "Kernel#untrusted?" do
ruby_version_is ""..."3.2" do
it "is a no-op" do
- o = mock('o')
- o.should_not.untrusted?
- o.untrust
- o.should_not.untrusted?
+ suppress_warning do
+ o = mock('o')
+ o.should_not.untrusted?
+ o.untrust
+ o.should_not.untrusted?
+ end
end
it "warns in verbose mode" do
diff --git a/spec/ruby/core/kernel/warn_spec.rb b/spec/ruby/core/kernel/warn_spec.rb
index 7df6fa72d1..00164ad90b 100644
--- a/spec/ruby/core/kernel/warn_spec.rb
+++ b/spec/ruby/core/kernel/warn_spec.rb
@@ -128,36 +128,34 @@ describe "Kernel#warn" do
end
end
- ruby_version_is "3.0" do
- it "accepts :category keyword with a symbol" do
- -> {
- $VERBOSE = true
- warn("message", category: :deprecated)
- }.should output(nil, "message\n")
- end
+ it "accepts :category keyword with a symbol" do
+ -> {
+ $VERBOSE = true
+ warn("message", category: :deprecated)
+ }.should output(nil, "message\n")
+ end
- it "accepts :category keyword with nil" do
- -> {
- $VERBOSE = true
- warn("message", category: nil)
- }.should output(nil, "message\n")
- end
+ it "accepts :category keyword with nil" do
+ -> {
+ $VERBOSE = true
+ warn("message", category: nil)
+ }.should output(nil, "message\n")
+ end
- it "accepts :category keyword with object convertible to symbol" do
- o = Object.new
- def o.to_sym; :deprecated; end
- -> {
- $VERBOSE = true
- warn("message", category: o)
- }.should output(nil, "message\n")
- end
+ it "accepts :category keyword with object convertible to symbol" do
+ o = Object.new
+ def o.to_sym; :deprecated; end
+ -> {
+ $VERBOSE = true
+ warn("message", category: o)
+ }.should output(nil, "message\n")
+ end
- it "raises if :category keyword is not nil and not convertible to symbol" do
- -> {
- $VERBOSE = true
- warn("message", category: Object.new)
- }.should raise_error(TypeError)
- end
+ it "raises if :category keyword is not nil and not convertible to symbol" do
+ -> {
+ $VERBOSE = true
+ warn("message", category: Object.new)
+ }.should raise_error(TypeError)
end
it "converts first arg using to_s" do
@@ -212,67 +210,52 @@ describe "Kernel#warn" do
-> { warn('foo', **h) }.should complain("foo\n")
end
- ruby_version_is '3.0' do
- it "calls Warning.warn without keyword arguments if Warning.warn does not accept keyword arguments" do
- verbose = $VERBOSE
- $VERBOSE = false
- class << Warning
- alias_method :_warn, :warn
- def warn(message)
- ScratchPad.record(message)
- end
- end
-
- begin
- ScratchPad.clear
- Kernel.warn("Chunky bacon!")
- ScratchPad.recorded.should == "Chunky bacon!\n"
-
- Kernel.warn("Deprecated bacon!", category: :deprecated)
- ScratchPad.recorded.should == "Deprecated bacon!\n"
- ensure
- class << Warning
- remove_method :warn
- alias_method :warn, :_warn
- remove_method :_warn
- end
- $VERBOSE = verbose
+ it "calls Warning.warn without keyword arguments if Warning.warn does not accept keyword arguments" do
+ verbose = $VERBOSE
+ $VERBOSE = false
+ class << Warning
+ alias_method :_warn, :warn
+ def warn(message)
+ ScratchPad.record(message)
end
end
- it "calls Warning.warn with category: nil if Warning.warn accepts keyword arguments" do
- Warning.should_receive(:warn).with("Chunky bacon!\n", category: nil)
- verbose = $VERBOSE
- $VERBOSE = false
- begin
- Kernel.warn("Chunky bacon!")
- ensure
- $VERBOSE = verbose
+ begin
+ ScratchPad.clear
+ Kernel.warn("Chunky bacon!")
+ ScratchPad.recorded.should == "Chunky bacon!\n"
+
+ Kernel.warn("Deprecated bacon!", category: :deprecated)
+ ScratchPad.recorded.should == "Deprecated bacon!\n"
+ ensure
+ class << Warning
+ remove_method :warn
+ alias_method :warn, :_warn
+ remove_method :_warn
end
+ $VERBOSE = verbose
end
+ end
- it "calls Warning.warn with given category keyword converted to a symbol" do
- Warning.should_receive(:warn).with("Chunky bacon!\n", category: :deprecated)
- verbose = $VERBOSE
- $VERBOSE = false
- begin
- Kernel.warn("Chunky bacon!", category: 'deprecated')
- ensure
- $VERBOSE = verbose
- end
+ it "calls Warning.warn with category: nil if Warning.warn accepts keyword arguments" do
+ Warning.should_receive(:warn).with("Chunky bacon!\n", category: nil)
+ verbose = $VERBOSE
+ $VERBOSE = false
+ begin
+ Kernel.warn("Chunky bacon!")
+ ensure
+ $VERBOSE = verbose
end
end
- ruby_version_is ''...'3.0' do
- it "calls Warning.warn with no keyword arguments" do
- Warning.should_receive(:warn).with("Chunky bacon!\n")
- verbose = $VERBOSE
- $VERBOSE = false
- begin
- Kernel.warn("Chunky bacon!")
- ensure
- $VERBOSE = verbose
- end
+ it "calls Warning.warn with given category keyword converted to a symbol" do
+ Warning.should_receive(:warn).with("Chunky bacon!\n", category: :deprecated)
+ verbose = $VERBOSE
+ $VERBOSE = false
+ begin
+ Kernel.warn("Chunky bacon!", category: 'deprecated')
+ ensure
+ $VERBOSE = verbose
end
end
diff --git a/spec/ruby/core/main/private_spec.rb b/spec/ruby/core/main/private_spec.rb
index cac0645b40..e8c1f3f44c 100644
--- a/spec/ruby/core/main/private_spec.rb
+++ b/spec/ruby/core/main/private_spec.rb
@@ -22,13 +22,11 @@ describe "main#private" do
end
end
- ruby_version_is "3.0" do
- context "when single argument is passed and is an array" do
- it "sets the visibility of the given methods to private" do
- eval "private [:main_public_method, :main_public_method2]", TOPLEVEL_BINDING
- Object.should have_private_method(:main_public_method)
- Object.should have_private_method(:main_public_method2)
- end
+ context "when single argument is passed and is an array" do
+ it "sets the visibility of the given methods to private" do
+ eval "private [:main_public_method, :main_public_method2]", TOPLEVEL_BINDING
+ Object.should have_private_method(:main_public_method)
+ Object.should have_private_method(:main_public_method2)
end
end
diff --git a/spec/ruby/core/main/public_spec.rb b/spec/ruby/core/main/public_spec.rb
index 91f045dbab..31baad4583 100644
--- a/spec/ruby/core/main/public_spec.rb
+++ b/spec/ruby/core/main/public_spec.rb
@@ -22,13 +22,11 @@ describe "main#public" do
end
end
- ruby_version_is "3.0" do
- context "when single argument is passed and is an array" do
- it "sets the visibility of the given methods to public" do
- eval "public [:main_private_method, :main_private_method2]", TOPLEVEL_BINDING
- Object.should_not have_private_method(:main_private_method)
- Object.should_not have_private_method(:main_private_method2)
- end
+ context "when single argument is passed and is an array" do
+ it "sets the visibility of the given methods to public" do
+ eval "public [:main_private_method, :main_private_method2]", TOPLEVEL_BINDING
+ Object.should_not have_private_method(:main_private_method)
+ Object.should_not have_private_method(:main_private_method2)
end
end
diff --git a/spec/ruby/core/marshal/dump_spec.rb b/spec/ruby/core/marshal/dump_spec.rb
index f38250b513..0f77279a4f 100644
--- a/spec/ruby/core/marshal/dump_spec.rb
+++ b/spec/ruby/core/marshal/dump_spec.rb
@@ -76,7 +76,7 @@ describe "Marshal.dump" do
end
it "dumps a binary encoded Symbol" do
- s = "\u2192".force_encoding("binary").to_sym
+ s = "\u2192".dup.force_encoding("binary").to_sym
Marshal.dump(s).should == "\x04\b:\b\xE2\x86\x92"
end
@@ -85,8 +85,8 @@ describe "Marshal.dump" do
symbol1 = "I:\t\xE2\x82\xACa\x06:\x06ET"
symbol2 = "I:\t\xE2\x82\xACb\x06;\x06T"
value = [
- "€a".force_encoding(Encoding::UTF_8).to_sym,
- "€b".force_encoding(Encoding::UTF_8).to_sym
+ "€a".dup.force_encoding(Encoding::UTF_8).to_sym,
+ "€b".dup.force_encoding(Encoding::UTF_8).to_sym
]
Marshal.dump(value).should == "\x04\b[\a#{symbol1}#{symbol2}"
@@ -150,7 +150,7 @@ describe "Marshal.dump" do
it "indexes instance variables of a String returned by #_dump at first and then indexes the object itself" do
class MarshalSpec::M1::A
def _dump(level)
- s = "<dump>"
+ s = +"<dump>"
s.instance_variable_set(:@foo, "bar")
s
end
@@ -194,7 +194,7 @@ describe "Marshal.dump" do
end
it "dumps a class with multibyte characters in name" do
- source_object = eval("MarshalSpec::Multibyteãã‚ãƒã„Class".force_encoding(Encoding::UTF_8))
+ source_object = eval("MarshalSpec::Multibyteãã‚ãƒã„Class".dup.force_encoding(Encoding::UTF_8))
Marshal.dump(source_object).should == "\x04\bc,MarshalSpec::Multibyte\xE3\x81\x81\xE3\x81\x82\xE3\x81\x83\xE3\x81\x84Class"
end
@@ -217,7 +217,7 @@ describe "Marshal.dump" do
end
it "dumps a module with multibyte characters in name" do
- source_object = eval("MarshalSpec::Multibyteã‘ã’ã“ã”Module".force_encoding(Encoding::UTF_8))
+ source_object = eval("MarshalSpec::Multibyteã‘ã’ã“ã”Module".dup.force_encoding(Encoding::UTF_8))
Marshal.dump(source_object).should == "\x04\bm-MarshalSpec::Multibyte\xE3\x81\x91\xE3\x81\x92\xE3\x81\x93\xE3\x81\x94Module"
end
@@ -271,13 +271,25 @@ describe "Marshal.dump" do
end
end
+ describe "with a Rational" do
+ it "dumps a Rational" do
+ Marshal.dump(Rational(2, 3)).should == "\x04\bU:\rRational[\ai\ai\b"
+ end
+ end
+
+ describe "with a Complex" do
+ it "dumps a Complex" do
+ Marshal.dump(Complex(2, 3)).should == "\x04\bU:\fComplex[\ai\ai\b"
+ end
+ end
+
describe "with a String" do
it "dumps a blank String" do
- Marshal.dump("".force_encoding("binary")).should == "\004\b\"\000"
+ Marshal.dump("".dup.force_encoding("binary")).should == "\004\b\"\000"
end
it "dumps a short String" do
- Marshal.dump("short".force_encoding("binary")).should == "\004\b\"\012short"
+ Marshal.dump("short".dup.force_encoding("binary")).should == "\004\b\"\012short"
end
it "dumps a long String" do
@@ -285,7 +297,7 @@ describe "Marshal.dump" do
end
it "dumps a String extended with a Module" do
- Marshal.dump("".extend(Meths).force_encoding("binary")).should == "\004\be:\nMeths\"\000"
+ Marshal.dump("".dup.extend(Meths).force_encoding("binary")).should == "\004\be:\nMeths\"\000"
end
it "dumps a String subclass" do
@@ -302,23 +314,23 @@ describe "Marshal.dump" do
end
it "dumps a String with instance variables" do
- str = ""
+ str = +""
str.instance_variable_set("@foo", "bar")
Marshal.dump(str.force_encoding("binary")).should == "\x04\bI\"\x00\x06:\t@foo\"\bbar"
end
it "dumps a US-ASCII String" do
- str = "abc".force_encoding("us-ascii")
+ str = "abc".dup.force_encoding("us-ascii")
Marshal.dump(str).should == "\x04\bI\"\babc\x06:\x06EF"
end
it "dumps a UTF-8 String" do
- str = "\x6d\xc3\xb6\x68\x72\x65".force_encoding("utf-8")
+ str = "\x6d\xc3\xb6\x68\x72\x65".dup.force_encoding("utf-8")
Marshal.dump(str).should == "\x04\bI\"\vm\xC3\xB6hre\x06:\x06ET"
end
it "dumps a String in another encoding" do
- str = "\x6d\x00\xf6\x00\x68\x00\x72\x00\x65\x00".force_encoding("utf-16le")
+ str = "\x6d\x00\xf6\x00\x68\x00\x72\x00\x65\x00".dup.force_encoding("utf-16le")
result = "\x04\bI\"\x0Fm\x00\xF6\x00h\x00r\x00e\x00\x06:\rencoding\"\rUTF-16LE"
Marshal.dump(str).should == result
end
@@ -352,7 +364,7 @@ describe "Marshal.dump" do
end
it "dumps a binary Regexp" do
- o = Regexp.new("".force_encoding("binary"), Regexp::FIXEDENCODING)
+ o = Regexp.new("".dup.force_encoding("binary"), Regexp::FIXEDENCODING)
Marshal.dump(o).should == "\x04\b/\x00\x10"
end
@@ -371,18 +383,18 @@ describe "Marshal.dump" do
end
it "dumps a UTF-8 Regexp" do
- o = Regexp.new("".force_encoding("utf-8"), Regexp::FIXEDENCODING)
+ o = Regexp.new("".dup.force_encoding("utf-8"), Regexp::FIXEDENCODING)
Marshal.dump(o).should == "\x04\bI/\x00\x10\x06:\x06ET"
- o = Regexp.new("a".force_encoding("utf-8"), Regexp::FIXEDENCODING)
+ o = Regexp.new("a".dup.force_encoding("utf-8"), Regexp::FIXEDENCODING)
Marshal.dump(o).should == "\x04\bI/\x06a\x10\x06:\x06ET"
- o = Regexp.new("\u3042".force_encoding("utf-8"), Regexp::FIXEDENCODING)
+ o = Regexp.new("\u3042".dup.force_encoding("utf-8"), Regexp::FIXEDENCODING)
Marshal.dump(o).should == "\x04\bI/\b\xE3\x81\x82\x10\x06:\x06ET"
end
it "dumps a Regexp in another encoding" do
- o = Regexp.new("".force_encoding("utf-16le"), Regexp::FIXEDENCODING)
+ o = Regexp.new("".dup.force_encoding("utf-16le"), Regexp::FIXEDENCODING)
Marshal.dump(o).should == "\x04\bI/\x00\x10\x06:\rencoding\"\rUTF-16LE"
o = Regexp.new("a".encode("utf-16le"), Regexp::FIXEDENCODING)
@@ -541,7 +553,7 @@ describe "Marshal.dump" do
it "dumps an Object with a non-US-ASCII instance variable" do
obj = Object.new
- ivar = "@é".force_encoding(Encoding::UTF_8).to_sym
+ ivar = "@é".dup.force_encoding(Encoding::UTF_8).to_sym
obj.instance_variable_set(ivar, 1)
Marshal.dump(obj).should == "\x04\bo:\vObject\x06I:\b@\xC3\xA9\x06:\x06ETi\x06"
end
@@ -626,17 +638,6 @@ describe "Marshal.dump" do
load.should == (1...2)
end
- ruby_version_is ""..."3.0" do
- it "dumps a Range with extra instance variables" do
- range = (1...3)
- range.instance_variable_set :@foo, 42
- dump = Marshal.dump(range)
- load = Marshal.load(dump)
- load.should == range
- load.instance_variable_get(:@foo).should == 42
- end
- end
-
it "raises TypeError with an anonymous Range subclass" do
-> { Marshal.dump(Class.new(Range).new(1, 2)) }.should raise_error(TypeError, /can't dump anonymous class/)
end
@@ -684,7 +685,7 @@ describe "Marshal.dump" do
end
it "dumps a Time subclass with multibyte characters in name" do
- source_object = eval("MarshalSpec::Multibyteãã‚ãƒã„Time".force_encoding(Encoding::UTF_8))
+ source_object = eval("MarshalSpec::Multibyteãã‚ãƒã„Time".dup.force_encoding(Encoding::UTF_8))
Marshal.dump(source_object).should == "\x04\bc+MarshalSpec::Multibyte\xE3\x81\x81\xE3\x81\x82\xE3\x81\x83\xE3\x81\x84Time"
end
@@ -739,7 +740,7 @@ describe "Marshal.dump" do
rescue => e
end
- Marshal.dump(e).should =~ /undefined method `foo' for ("":String|an instance of String)/
+ Marshal.dump(e).should =~ /undefined method [`']foo' for ("":String|an instance of String)/
end
it "raises TypeError if an Object is an instance of an anonymous class" do
@@ -780,7 +781,6 @@ describe "Marshal.dump" do
end
describe "when passed an IO" do
-
it "writes the serialized data to the IO-Object" do
(obj = mock('test')).should_receive(:write).at_least(1)
Marshal.dump("test", obj)
@@ -803,8 +803,6 @@ describe "Marshal.dump" do
obj.should_receive(:binmode).at_least(1)
Marshal.dump("test", obj)
end
-
-
end
describe "when passed a StringIO" do
diff --git a/spec/ruby/core/marshal/fixtures/marshal_data.rb b/spec/ruby/core/marshal/fixtures/marshal_data.rb
index 680cb08ac7..a508b6bea1 100644
--- a/spec/ruby/core/marshal/fixtures/marshal_data.rb
+++ b/spec/ruby/core/marshal/fixtures/marshal_data.rb
@@ -38,7 +38,7 @@ class UserDefinedWithIvar
attr_reader :a, :b, :c
def initialize
- @a = 'stuff'
+ @a = +'stuff'
@a.instance_variable_set :@foo, :UserDefinedWithIvar
@b = 'more'
@c = @b
@@ -267,7 +267,7 @@ module MarshalSpec
end
end
- module_eval(<<~ruby.force_encoding(Encoding::UTF_8))
+ module_eval(<<~ruby.dup.force_encoding(Encoding::UTF_8))
class Multibyteãã‚ãƒã„Class
end
@@ -313,7 +313,7 @@ module MarshalSpec
"\004\b\"\012small"],
"String big" => ['big' * 100,
"\004\b\"\002,\001#{'big' * 100}"],
- "String extended" => [''.extend(Meths), # TODO: check for module on load
+ "String extended" => [''.dup.extend(Meths), # TODO: check for module on load
"\004\be:\nMeths\"\000"],
"String subclass" => [UserString.new,
"\004\bC:\017UserString\"\000"],
@@ -420,7 +420,7 @@ module MarshalSpec
"\x04\bI\"\nsmall\x06:\x06EF"],
"String big" => ['big' * 100,
"\x04\bI\"\x02,\x01bigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbig\x06:\x06EF"],
- "String extended" => [''.extend(Meths), # TODO: check for module on load
+ "String extended" => [''.dup.extend(Meths), # TODO: check for module on load
"\x04\bIe:\nMeths\"\x00\x06:\x06EF"],
"String subclass" => [UserString.new,
"\004\bC:\017UserString\"\000"],
diff --git a/spec/ruby/core/marshal/shared/load.rb b/spec/ruby/core/marshal/shared/load.rb
index bc094f28f2..f599042529 100644
--- a/spec/ruby/core/marshal/shared/load.rb
+++ b/spec/ruby/core/marshal/shared/load.rb
@@ -183,7 +183,7 @@ describe :marshal_load, shared: true do
describe "when called with a proc" do
it "call the proc with frozen objects" do
arr = []
- s = 'hi'
+ s = +'hi'
s.instance_variable_set(:@foo, 5)
st = Struct.new("Brittle", :a).new
st.instance_variable_set(:@clue, 'none')
@@ -268,7 +268,7 @@ describe :marshal_load, shared: true do
it "loads an Array with proc" do
arr = []
- s = 'hi'
+ s = +'hi'
s.instance_variable_set(:@foo, 5)
st = Struct.new("Brittle", :a).new
st.instance_variable_set(:@clue, 'none')
@@ -413,13 +413,13 @@ describe :marshal_load, shared: true do
end
it "raises a TypeError with bad Marshal version" do
- marshal_data = '\xff\xff'
+ marshal_data = +'\xff\xff'
marshal_data[0] = (Marshal::MAJOR_VERSION).chr
marshal_data[1] = (Marshal::MINOR_VERSION + 1).chr
-> { Marshal.send(@method, marshal_data) }.should raise_error(TypeError)
- marshal_data = '\xff\xff'
+ marshal_data = +'\xff\xff'
marshal_data[0] = (Marshal::MAJOR_VERSION - 1).chr
marshal_data[1] = (Marshal::MINOR_VERSION).chr
@@ -470,7 +470,7 @@ describe :marshal_load, shared: true do
end
it "loads an array having ivar" do
- s = 'well'
+ s = +'well'
s.instance_variable_set(:@foo, 10)
obj = ['5', s, 'hi'].extend(Meths, MethsMore)
obj.instance_variable_set(:@mix, s)
@@ -516,7 +516,7 @@ describe :marshal_load, shared: true do
end
it "preserves hash ivars when hash contains a string having ivar" do
- s = 'string'
+ s = +'string'
s.instance_variable_set :@string_ivar, 'string ivar'
h = { key: s }
h.instance_variable_set :@hash_ivar, 'hash ivar'
@@ -600,7 +600,7 @@ describe :marshal_load, shared: true do
end
it "loads a binary encoded Symbol" do
- s = "\u2192".force_encoding("binary").to_sym
+ s = "\u2192".dup.force_encoding("binary").to_sym
sym = Marshal.send(@method, "\x04\b:\b\xE2\x86\x92")
sym.should == s
sym.encoding.should == Encoding::BINARY
@@ -614,8 +614,8 @@ describe :marshal_load, shared: true do
value = Marshal.send(@method, dump)
value.map(&:encoding).should == [Encoding::UTF_8, Encoding::UTF_8]
expected = [
- "€a".force_encoding(Encoding::UTF_8).to_sym,
- "€b".force_encoding(Encoding::UTF_8).to_sym
+ "€a".dup.force_encoding(Encoding::UTF_8).to_sym,
+ "€b".dup.force_encoding(Encoding::UTF_8).to_sym
]
value.should == expected
@@ -623,11 +623,19 @@ describe :marshal_load, shared: true do
value.map(&:encoding).should == [Encoding::UTF_8, Encoding::UTF_8, Encoding::UTF_8]
value.should == [*expected, expected[0]]
end
+
+ it "raises ArgumentError when end of byte sequence reached before symbol characters end" do
+ Marshal.dump(:hello).should == "\x04\b:\nhello"
+
+ -> {
+ Marshal.send(@method, "\x04\b:\nhel")
+ }.should raise_error(ArgumentError, "marshal data too short")
+ end
end
describe "for a String" do
it "loads a string having ivar with ref to self" do
- obj = 'hi'
+ obj = +'hi'
obj.instance_variable_set(:@self, obj)
Marshal.send(@method, "\004\bI\"\ahi\006:\n@self@\000").should == obj
end
@@ -655,7 +663,7 @@ describe :marshal_load, shared: true do
end
it "loads a US-ASCII String" do
- str = "abc".force_encoding("us-ascii")
+ str = "abc".dup.force_encoding("us-ascii")
data = "\x04\bI\"\babc\x06:\x06EF"
result = Marshal.send(@method, data)
result.should == str
@@ -663,7 +671,7 @@ describe :marshal_load, shared: true do
end
it "loads a UTF-8 String" do
- str = "\x6d\xc3\xb6\x68\x72\x65".force_encoding("utf-8")
+ str = "\x6d\xc3\xb6\x68\x72\x65".dup.force_encoding("utf-8")
data = "\x04\bI\"\vm\xC3\xB6hre\x06:\x06ET"
result = Marshal.send(@method, data)
result.should == str
@@ -671,7 +679,7 @@ describe :marshal_load, shared: true do
end
it "loads a String in another encoding" do
- str = "\x6d\x00\xf6\x00\x68\x00\x72\x00\x65\x00".force_encoding("utf-16le")
+ str = "\x6d\x00\xf6\x00\x68\x00\x72\x00\x65\x00".dup.force_encoding("utf-16le")
data = "\x04\bI\"\x0Fm\x00\xF6\x00h\x00r\x00e\x00\x06:\rencoding\"\rUTF-16LE"
result = Marshal.send(@method, data)
result.should == str
@@ -679,12 +687,20 @@ describe :marshal_load, shared: true do
end
it "loads a String as BINARY if no encoding is specified at the end" do
- str = "\xC3\xB8".force_encoding("BINARY")
- data = "\x04\b\"\a\xC3\xB8".force_encoding("UTF-8")
+ str = "\xC3\xB8".dup.force_encoding("BINARY")
+ data = "\x04\b\"\a\xC3\xB8".dup.force_encoding("UTF-8")
result = Marshal.send(@method, data)
result.encoding.should == Encoding::BINARY
result.should == str
end
+
+ it "raises ArgumentError when end of byte sequence reached before string characters end" do
+ Marshal.dump("hello").should == "\x04\b\"\nhello"
+
+ -> {
+ Marshal.send(@method, "\x04\b\"\nhel")
+ }.should raise_error(ArgumentError, "marshal data too short")
+ end
end
describe "for a Struct" do
@@ -807,7 +823,7 @@ describe :marshal_load, shared: true do
end
it "loads an Object with a non-US-ASCII instance variable" do
- ivar = "@é".force_encoding(Encoding::UTF_8).to_sym
+ ivar = "@é".dup.force_encoding(Encoding::UTF_8).to_sym
obj = Marshal.send(@method, "\x04\bo:\vObject\x06I:\b@\xC3\xA9\x06:\x06ETi\x06")
obj.instance_variables.should == [ivar]
obj.instance_variables[0].encoding.should == Encoding::UTF_8
@@ -819,6 +835,14 @@ describe :marshal_load, shared: true do
Marshal.send(@method, "\x04\bo:\tFile\001\001:\001\005@path\"\x10/etc/passwd")
end.should raise_error(ArgumentError)
end
+
+ it "raises ArgumentError when end of byte sequence reached before class name end" do
+ Marshal.dump(Object.new).should == "\x04\bo:\vObject\x00"
+
+ -> {
+ Marshal.send(@method, "\x04\bo:\vObj")
+ }.should raise_error(ArgumentError, "marshal data too short")
+ end
end
describe "for an object responding to #marshal_dump and #marshal_load" do
@@ -908,6 +932,14 @@ describe :marshal_load, shared: true do
regexp.encoding.should == Encoding::UTF_32LE
regexp.source.should == "a".encode("utf-32le")
end
+
+ it "raises ArgumentError when end of byte sequence reached before source string end" do
+ Marshal.dump(/hello world/).should == "\x04\bI/\x10hello world\x00\x06:\x06EF"
+
+ -> {
+ Marshal.send(@method, "\x04\bI/\x10hel")
+ }.should raise_error(ArgumentError, "marshal data too short")
+ end
end
describe "for a Float" do
@@ -929,6 +961,14 @@ describe :marshal_load, shared: true do
obj = 1.1867345e+22
Marshal.send(@method, "\004\bf\0361.1867344999999999e+22\000\344@").should == obj
end
+
+ it "raises ArgumentError when end of byte sequence reached before float string representation end" do
+ Marshal.dump(1.3).should == "\x04\bf\b1.3"
+
+ -> {
+ Marshal.send(@method, "\004\bf\v1")
+ }.should raise_error(ArgumentError, "marshal data too short")
+ end
end
describe "for an Integer" do
@@ -994,13 +1034,17 @@ describe :marshal_load, shared: true do
describe "for a Rational" do
it "loads" do
- Marshal.send(@method, Marshal.dump(Rational(1, 3))).should == Rational(1, 3)
+ r = Marshal.send(@method, Marshal.dump(Rational(1, 3)))
+ r.should == Rational(1, 3)
+ r.should.frozen?
end
end
describe "for a Complex" do
it "loads" do
- Marshal.send(@method, Marshal.dump(Complex(4, 3))).should == Complex(4, 3)
+ c = Marshal.send(@method, Marshal.dump(Complex(4, 3)))
+ c.should == Complex(4, 3)
+ c.should.frozen?
end
end
@@ -1114,6 +1158,14 @@ describe :marshal_load, shared: true do
it "raises ArgumentError if given a nonexistent class" do
-> { Marshal.send(@method, "\x04\bc\vStrung") }.should raise_error(ArgumentError)
end
+
+ it "raises ArgumentError when end of byte sequence reached before class name end" do
+ Marshal.dump(String).should == "\x04\bc\vString"
+
+ -> {
+ Marshal.send(@method, "\x04\bc\vStr")
+ }.should raise_error(ArgumentError, "marshal data too short")
+ end
end
describe "for a Module" do
@@ -1128,6 +1180,14 @@ describe :marshal_load, shared: true do
it "loads an old module" do
Marshal.send(@method, "\x04\bM\vKernel").should == Kernel
end
+
+ it "raises ArgumentError when end of byte sequence reached before module name end" do
+ Marshal.dump(Kernel).should == "\x04\bm\vKernel"
+
+ -> {
+ Marshal.send(@method, "\x04\bm\vKer")
+ }.should raise_error(ArgumentError, "marshal data too short")
+ end
end
describe "for a wrapped C pointer" do
diff --git a/spec/ruby/core/matchdata/begin_spec.rb b/spec/ruby/core/matchdata/begin_spec.rb
index 85c454da56..54b4e0a33f 100644
--- a/spec/ruby/core/matchdata/begin_spec.rb
+++ b/spec/ruby/core/matchdata/begin_spec.rb
@@ -36,6 +36,18 @@ describe "MatchData#begin" do
match_data = /(.)(.)(\d+)(\d)/.match("THX1138.")
match_data.begin(obj).should == 2
end
+
+ it "raises IndexError if index is out of bounds" do
+ match_data = /(?<f>foo)(?<b>bar)/.match("foobar")
+
+ -> {
+ match_data.begin(-1)
+ }.should raise_error(IndexError, "index -1 out of matches")
+
+ -> {
+ match_data.begin(3)
+ }.should raise_error(IndexError, "index 3 out of matches")
+ end
end
context "when passed a String argument" do
@@ -68,6 +80,14 @@ describe "MatchData#begin" do
match_data = /(?<æ>.)(.)(?<b>\d+)(\d)/.match("THX1138.")
match_data.begin("æ").should == 1
end
+
+ it "raises IndexError if there is no group with the provided name" do
+ match_data = /(?<f>foo)(?<b>bar)/.match("foobar")
+
+ -> {
+ match_data.begin("y")
+ }.should raise_error(IndexError, "undefined group name reference: y")
+ end
end
context "when passed a Symbol argument" do
@@ -100,5 +120,13 @@ describe "MatchData#begin" do
match_data = /(?<æ>.)(.)(?<b>\d+)(\d)/.match("THX1138.")
match_data.begin(:æ).should == 1
end
+
+ it "raises IndexError if there is no group with the provided name" do
+ match_data = /(?<f>foo)(?<b>bar)/.match("foobar")
+
+ -> {
+ match_data.begin(:y)
+ }.should raise_error(IndexError, "undefined group name reference: y")
+ end
end
end
diff --git a/spec/ruby/core/matchdata/byteoffset_spec.rb b/spec/ruby/core/matchdata/byteoffset_spec.rb
index 6036097834..b27267fd0e 100644
--- a/spec/ruby/core/matchdata/byteoffset_spec.rb
+++ b/spec/ruby/core/matchdata/byteoffset_spec.rb
@@ -60,7 +60,7 @@ describe "MatchData#byteoffset" do
m.byteoffset(obj).should == [3, 6]
end
- it "raises IndexError if there is no group with provided name" do
+ it "raises IndexError if there is no group with the provided name" do
m = /(?<f>foo)(?<b>bar)/.match("foobar")
-> {
@@ -72,7 +72,7 @@ describe "MatchData#byteoffset" do
}.should raise_error(IndexError, "undefined group name reference: y")
end
- it "raises IndexError if index is out of matches" do
+ it "raises IndexError if index is out of bounds" do
m = /(?<f>foo)(?<b>bar)/.match("foobar")
-> {
diff --git a/spec/ruby/core/matchdata/element_reference_spec.rb b/spec/ruby/core/matchdata/element_reference_spec.rb
index 0e0d3991cb..806db2d7b5 100644
--- a/spec/ruby/core/matchdata/element_reference_spec.rb
+++ b/spec/ruby/core/matchdata/element_reference_spec.rb
@@ -48,11 +48,9 @@ describe "MatchData#[]" do
/(.)(.)(\d+)(\d)/.match("THX1138.")[nil..nil].should == %w|HX1138 H X 113 8|
end
- ruby_version_is "3.0" do
- it "returns instances of String when given a String subclass" do
- str = MatchDataSpecs::MyString.new("THX1138.")
- /(.)(.)(\d+)(\d)/.match(str)[0..-1].each { |m| m.should be_an_instance_of(String) }
- end
+ it "returns instances of String when given a String subclass" do
+ str = MatchDataSpecs::MyString.new("THX1138.")
+ /(.)(.)(\d+)(\d)/.match(str)[0..-1].each { |m| m.should be_an_instance_of(String) }
end
end
@@ -115,7 +113,7 @@ describe "MatchData#[Symbol]" do
it "returns matches in the String's encoding" do
rex = /(?<t>t(?<a>ack))/u
- md = 'haystack'.force_encoding('euc-jp').match(rex)
+ md = 'haystack'.dup.force_encoding('euc-jp').match(rex)
md[:t].encoding.should == Encoding::EUC_JP
end
end
diff --git a/spec/ruby/core/matchdata/post_match_spec.rb b/spec/ruby/core/matchdata/post_match_spec.rb
index d3aa4c8900..7bfe6df119 100644
--- a/spec/ruby/core/matchdata/post_match_spec.rb
+++ b/spec/ruby/core/matchdata/post_match_spec.rb
@@ -8,19 +8,17 @@ describe "MatchData#post_match" do
end
it "sets the encoding to the encoding of the source String" do
- str = "abc".force_encoding Encoding::EUC_JP
+ str = "abc".dup.force_encoding Encoding::EUC_JP
str.match(/b/).post_match.encoding.should equal(Encoding::EUC_JP)
end
it "sets an empty result to the encoding of the source String" do
- str = "abc".force_encoding Encoding::ISO_8859_1
+ str = "abc".dup.force_encoding Encoding::ISO_8859_1
str.match(/c/).post_match.encoding.should equal(Encoding::ISO_8859_1)
end
- ruby_version_is "3.0" do
- it "returns an instance of String when given a String subclass" do
- str = MatchDataSpecs::MyString.new("THX1138: The Movie")
- /(.)(.)(\d+)(\d)/.match(str).post_match.should be_an_instance_of(String)
- end
+ it "returns an instance of String when given a String subclass" do
+ str = MatchDataSpecs::MyString.new("THX1138: The Movie")
+ /(.)(.)(\d+)(\d)/.match(str).post_match.should be_an_instance_of(String)
end
end
diff --git a/spec/ruby/core/matchdata/pre_match_spec.rb b/spec/ruby/core/matchdata/pre_match_spec.rb
index b43be5fb41..2f1ba9b8f6 100644
--- a/spec/ruby/core/matchdata/pre_match_spec.rb
+++ b/spec/ruby/core/matchdata/pre_match_spec.rb
@@ -8,19 +8,17 @@ describe "MatchData#pre_match" do
end
it "sets the encoding to the encoding of the source String" do
- str = "abc".force_encoding Encoding::EUC_JP
+ str = "abc".dup.force_encoding Encoding::EUC_JP
str.match(/b/).pre_match.encoding.should equal(Encoding::EUC_JP)
end
it "sets an empty result to the encoding of the source String" do
- str = "abc".force_encoding Encoding::ISO_8859_1
+ str = "abc".dup.force_encoding Encoding::ISO_8859_1
str.match(/a/).pre_match.encoding.should equal(Encoding::ISO_8859_1)
end
- ruby_version_is "3.0" do
- it "returns an instance of String when given a String subclass" do
- str = MatchDataSpecs::MyString.new("THX1138: The Movie")
- /(.)(.)(\d+)(\d)/.match(str).pre_match.should be_an_instance_of(String)
- end
+ it "returns an instance of String when given a String subclass" do
+ str = MatchDataSpecs::MyString.new("THX1138: The Movie")
+ /(.)(.)(\d+)(\d)/.match(str).pre_match.should be_an_instance_of(String)
end
end
diff --git a/spec/ruby/core/matchdata/shared/captures.rb b/spec/ruby/core/matchdata/shared/captures.rb
index d2c85ece5a..33f834561a 100644
--- a/spec/ruby/core/matchdata/shared/captures.rb
+++ b/spec/ruby/core/matchdata/shared/captures.rb
@@ -6,10 +6,8 @@ describe :matchdata_captures, shared: true do
/(.)(.)(\d+)(\d)/.match("THX1138.").send(@method).should == ["H","X","113","8"]
end
- ruby_version_is "3.0" do
- it "returns instances of String when given a String subclass" do
- str = MatchDataSpecs::MyString.new("THX1138: The Movie")
- /(.)(.)(\d+)(\d)/.match(str).send(@method).each { |c| c.should be_an_instance_of(String) }
- end
+ it "returns instances of String when given a String subclass" do
+ str = MatchDataSpecs::MyString.new("THX1138: The Movie")
+ /(.)(.)(\d+)(\d)/.match(str).send(@method).each { |c| c.should be_an_instance_of(String) }
end
end
diff --git a/spec/ruby/core/matchdata/string_spec.rb b/spec/ruby/core/matchdata/string_spec.rb
index 420233e1f3..952e953318 100644
--- a/spec/ruby/core/matchdata/string_spec.rb
+++ b/spec/ruby/core/matchdata/string_spec.rb
@@ -17,8 +17,9 @@ describe "MatchData#string" do
md.string.should equal(md.string)
end
- it "returns a frozen copy of the matched string for gsub(String)" do
- 'he[[o'.gsub!('[', ']')
+ it "returns a frozen copy of the matched string for gsub!(String)" do
+ s = +'he[[o'
+ s.gsub!('[', ']')
$~.string.should == 'he[[o'
$~.string.should.frozen?
end
diff --git a/spec/ruby/core/matchdata/to_a_spec.rb b/spec/ruby/core/matchdata/to_a_spec.rb
index 50f5a161a5..4fa11ff604 100644
--- a/spec/ruby/core/matchdata/to_a_spec.rb
+++ b/spec/ruby/core/matchdata/to_a_spec.rb
@@ -6,10 +6,8 @@ describe "MatchData#to_a" do
/(.)(.)(\d+)(\d)/.match("THX1138.").to_a.should == ["HX1138", "H", "X", "113", "8"]
end
- ruby_version_is "3.0" do
- it "returns instances of String when given a String subclass" do
- str = MatchDataSpecs::MyString.new("THX1138.")
- /(.)(.)(\d+)(\d)/.match(str)[0..-1].to_a.each { |m| m.should be_an_instance_of(String) }
- end
+ it "returns instances of String when given a String subclass" do
+ str = MatchDataSpecs::MyString.new("THX1138.")
+ /(.)(.)(\d+)(\d)/.match(str)[0..-1].to_a.each { |m| m.should be_an_instance_of(String) }
end
end
diff --git a/spec/ruby/core/matchdata/to_s_spec.rb b/spec/ruby/core/matchdata/to_s_spec.rb
index aab0955ae1..cd1c4dbca2 100644
--- a/spec/ruby/core/matchdata/to_s_spec.rb
+++ b/spec/ruby/core/matchdata/to_s_spec.rb
@@ -6,10 +6,8 @@ describe "MatchData#to_s" do
/(.)(.)(\d+)(\d)/.match("THX1138.").to_s.should == "HX1138"
end
- ruby_version_is "3.0" do
- it "returns an instance of String when given a String subclass" do
- str = MatchDataSpecs::MyString.new("THX1138.")
- /(.)(.)(\d+)(\d)/.match(str).to_s.should be_an_instance_of(String)
- end
+ it "returns an instance of String when given a String subclass" do
+ str = MatchDataSpecs::MyString.new("THX1138.")
+ /(.)(.)(\d+)(\d)/.match(str).to_s.should be_an_instance_of(String)
end
end
diff --git a/spec/ruby/core/matchdata/values_at_spec.rb b/spec/ruby/core/matchdata/values_at_spec.rb
index 4fd0bfc42a..535719a2ee 100644
--- a/spec/ruby/core/matchdata/values_at_spec.rb
+++ b/spec/ruby/core/matchdata/values_at_spec.rb
@@ -1,6 +1,6 @@
require_relative '../../spec_helper'
-describe "Struct#values_at" do
+describe "MatchData#values_at" do
# Should be synchronized with core/array/values_at_spec.rb and core/struct/values_at_spec.rb
#
# /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").to_a # => ["HX1138", "H", "X", "113", "8"]
@@ -34,7 +34,7 @@ describe "Struct#values_at" do
end
it "supports beginningless Range" do
- /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(0..2).should == ["HX1138", "H", "X"]
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(..2).should == ["HX1138", "H", "X"]
end
it "returns an empty Array when Range is empty" do
diff --git a/spec/ruby/core/method/clone_spec.rb b/spec/ruby/core/method/clone_spec.rb
index 3fe4000fb7..b0eb5751a9 100644
--- a/spec/ruby/core/method/clone_spec.rb
+++ b/spec/ruby/core/method/clone_spec.rb
@@ -1,14 +1,13 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
+require_relative 'shared/dup'
describe "Method#clone" do
- it "returns a copy of the method" do
- m1 = MethodSpecs::Methods.new.method(:foo)
- m2 = m1.clone
+ it_behaves_like :method_dup, :clone
- m1.should == m2
- m1.should_not equal(m2)
-
- m1.call.should == m2.call
+ it "preserves frozen status" do
+ method = Object.new.method(:method)
+ method.freeze
+ method.frozen?.should == true
+ method.clone.frozen?.should == true
end
end
diff --git a/spec/ruby/core/method/compose_spec.rb b/spec/ruby/core/method/compose_spec.rb
index 87cf61f7ad..7506e33ea8 100644
--- a/spec/ruby/core/method/compose_spec.rb
+++ b/spec/ruby/core/method/compose_spec.rb
@@ -38,8 +38,7 @@ describe "Method#<<" do
double = proc { |x| x + x }
(pow_2 << double).is_a?(Proc).should == true
- ruby_version_is(''...'3.0') { (pow_2 << double).should.lambda? }
- ruby_version_is('3.0') { (pow_2 << double).should_not.lambda? }
+ (pow_2 << double).should_not.lambda?
end
it "may accept multiple arguments" do
diff --git a/spec/ruby/core/method/dup_spec.rb b/spec/ruby/core/method/dup_spec.rb
new file mode 100644
index 0000000000..e3e29d8a68
--- /dev/null
+++ b/spec/ruby/core/method/dup_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'shared/dup'
+
+describe "Method#dup" do
+ ruby_version_is "3.4" do
+ it_behaves_like :method_dup, :dup
+
+ it "resets frozen status" do
+ method = Object.new.method(:method)
+ method.freeze
+ method.frozen?.should == true
+ method.dup.frozen?.should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/method/parameters_spec.rb b/spec/ruby/core/method/parameters_spec.rb
index 0f730fe013..8495aef4d2 100644
--- a/spec/ruby/core/method/parameters_spec.rb
+++ b/spec/ruby/core/method/parameters_spec.rb
@@ -7,6 +7,7 @@ describe "Method#parameters" do
def one_keyrest(**a); end
def one_keyreq(a:); end
+ def one_nokey(**nil); end
def one_splat_one_req(*a,b); end
def one_splat_two_req(*a,b,c); end
@@ -15,11 +16,14 @@ describe "Method#parameters" do
def one_opt_with_stabby(a=-> b { true }); end
def one_unnamed_splat(*); end
+ def one_unnamed_keyrest(**); end
def one_splat_one_block(*args, &block)
local_is_not_parameter = {}
end
+ def forward_parameters(...) end
+
def underscore_parameters(_, _, _ = 1, *_, _:, _: 2, **_, &_); end
define_method(:one_optional_defined_method) {|x = 1|}
@@ -178,6 +182,11 @@ describe "Method#parameters" do
m.parameters.should == [[:keyreq,:a]]
end
+ it "returns [[:nokey]] for a method with a single **nil parameter" do
+ m = MethodSpecs::Methods.instance_method(:one_nokey)
+ m.parameters.should == [[:nokey]]
+ end
+
it "works with ->(){} as the value of an optional argument" do
m = MethodSpecs::Methods.instance_method(:one_opt_with_stabby)
m.parameters.should == [[:opt,:a]]
@@ -225,10 +234,15 @@ describe "Method#parameters" do
end
ruby_version_is '3.2' do
- it "adds * rest arg for \"star\" argument" do
+ it "adds rest arg with name * for \"star\" argument" do
m = MethodSpecs::Methods.new
m.method(:one_unnamed_splat).parameters.should == [[:rest, :*]]
end
+
+ it "adds keyrest arg with ** as a name for \"double star\" argument" do
+ m = MethodSpecs::Methods.new
+ m.method(:one_unnamed_keyrest).parameters.should == [[:keyrest, :**]]
+ end
end
ruby_version_is ''...'3.2' do
@@ -236,6 +250,37 @@ describe "Method#parameters" do
m = MethodSpecs::Methods.new
m.method(:one_unnamed_splat).parameters.should == [[:rest]]
end
+
+ it "adds nameless keyrest arg for \"double star\" argument" do
+ m = MethodSpecs::Methods.new
+ m.method(:one_unnamed_keyrest).parameters.should == [[:keyrest]]
+ end
+ end
+
+ ruby_version_is '3.1' do
+ it "adds block arg with name & for anonymous block argument" do
+ object = Object.new
+
+ eval(<<~RUBY).should == [[:block, :&]]
+ def object.foo(&)
+ end
+ object.method(:foo).parameters
+ RUBY
+ end
+ end
+
+ ruby_version_is ""..."3.1" do
+ it "returns [:rest, :*], [:block, :&] for forward parameters operator" do
+ m = MethodSpecs::Methods.new
+ m.method(:forward_parameters).parameters.should == [[:rest, :*], [:block, :&]]
+ end
+ end
+
+ ruby_version_is "3.1" do
+ it "returns [:rest, :*], [:keyrest, :**], [:block, :&] for forward parameters operator" do
+ m = MethodSpecs::Methods.new
+ m.method(:forward_parameters).parameters.should == [[:rest, :*], [:keyrest, :**], [:block, :&]]
+ end
end
it "returns the args and block for a splat and block argument" do
diff --git a/spec/ruby/core/method/shared/dup.rb b/spec/ruby/core/method/shared/dup.rb
new file mode 100644
index 0000000000..1a10b90689
--- /dev/null
+++ b/spec/ruby/core/method/shared/dup.rb
@@ -0,0 +1,32 @@
+describe :method_dup, shared: true do
+ it "returns a copy of self" do
+ a = Object.new.method(:method)
+ b = a.send(@method)
+
+ a.should == b
+ a.should_not equal(b)
+ end
+
+ ruby_version_is "3.4" do
+ it "copies instance variables" do
+ method = Object.new.method(:method)
+ method.instance_variable_set(:@ivar, 1)
+ cl = method.send(@method)
+ cl.instance_variables.should == [:@ivar]
+ end
+
+ it "copies the finalizer" do
+ code = <<-RUBY
+ obj = Object.new.method(:method)
+
+ ObjectSpace.define_finalizer(obj, Proc.new { STDOUT.write "finalized\n" })
+
+ obj.clone
+
+ exit 0
+ RUBY
+
+ ruby_exe(code).lines.sort.should == ["finalized\n", "finalized\n"]
+ end
+ end
+end
diff --git a/spec/ruby/core/method/shared/to_s.rb b/spec/ruby/core/method/shared/to_s.rb
index 6fdeaaf99c..b2d27d370f 100644
--- a/spec/ruby/core/method/shared/to_s.rb
+++ b/spec/ruby/core/method/shared/to_s.rb
@@ -53,20 +53,18 @@ describe :method_to_s, shared: true do
MethodSpecs::A.new.method(:baz).send(@method).should.start_with? "#<Method: MethodSpecs::A#baz"
end
- ruby_version_is '3.0' do
- it "returns a String containing the Module containing the method if object has a singleton class but method is not defined in the singleton class" do
- obj = MethodSpecs::MySub.new
- obj.singleton_class
- @m = obj.method(:bar)
- @string = @m.send(@method)
- @string.should.start_with? "#<Method: MethodSpecs::MySub(MethodSpecs::MyMod)#bar"
+ it "returns a String containing the Module containing the method if object has a singleton class but method is not defined in the singleton class" do
+ obj = MethodSpecs::MySub.new
+ obj.singleton_class
+ @m = obj.method(:bar)
+ @string = @m.send(@method)
+ @string.should.start_with? "#<Method: MethodSpecs::MySub(MethodSpecs::MyMod)#bar"
- c = MethodSpecs::MySub.dup
- m = Module.new{def bar; end}
- c.extend(m)
- @string = c.method(:bar).send(@method)
- @string.should.start_with? "#<Method: #<Class:#{c.inspect}>(#{m.inspect})#bar"
- end
+ c = MethodSpecs::MySub.dup
+ m = Module.new{def bar; end}
+ c.extend(m)
+ @string = c.method(:bar).send(@method)
+ @string.should.start_with? "#<Method: #<Class:#{c.inspect}>(#{m.inspect})#bar"
end
it "returns a String containing the singleton class if method is defined in the singleton class" do
@@ -77,9 +75,7 @@ describe :method_to_s, shared: true do
@string.should.start_with? "#<Method: #<MethodSpecs::MySub:0xXXXXXX>.bar"
end
- ruby_bug '#17428', ''...'3.0' do
- it "shows the metaclass and the owner for a Module instance method retrieved from a class" do
- String.method(:include).inspect.should.start_with?("#<Method: #<Class:String>(Module)#include")
- end
+ it "shows the metaclass and the owner for a Module instance method retrieved from a class" do
+ String.method(:include).inspect.should.start_with?("#<Method: #<Class:String>(Module)#include")
end
end
diff --git a/spec/ruby/core/method/source_location_spec.rb b/spec/ruby/core/method/source_location_spec.rb
index 4cfb21c5d0..c5b296f6e2 100644
--- a/spec/ruby/core/method/source_location_spec.rb
+++ b/spec/ruby/core/method/source_location_spec.rb
@@ -13,7 +13,7 @@ describe "Method#source_location" do
it "sets the first value to the path of the file in which the method was defined" do
file = @method.source_location.first
file.should be_an_instance_of(String)
- file.should == File.realpath('../fixtures/classes.rb', __FILE__)
+ file.should == File.realpath('fixtures/classes.rb', __dir__)
end
it "sets the last value to an Integer representing the line on which the method was defined" do
diff --git a/spec/ruby/core/method/super_method_spec.rb b/spec/ruby/core/method/super_method_spec.rb
index f9a18f3878..c63a7aaa0f 100644
--- a/spec/ruby/core/method/super_method_spec.rb
+++ b/spec/ruby/core/method/super_method_spec.rb
@@ -55,12 +55,10 @@ describe "Method#super_method" do
end
end
- ruby_version_is "2.7.3" do
- context "after aliasing an inherited method" do
- it "returns the expected super_method" do
- method = MethodSpecs::InheritedMethods::C.new.method(:meow)
- method.super_method.owner.should == MethodSpecs::InheritedMethods::A
- end
+ context "after aliasing an inherited method" do
+ it "returns the expected super_method" do
+ method = MethodSpecs::InheritedMethods::C.new.method(:meow)
+ method.super_method.owner.should == MethodSpecs::InheritedMethods::A
end
end
end
diff --git a/spec/ruby/core/method/to_proc_spec.rb b/spec/ruby/core/method/to_proc_spec.rb
index 29b7bec2b3..4993cce239 100644
--- a/spec/ruby/core/method/to_proc_spec.rb
+++ b/spec/ruby/core/method/to_proc_spec.rb
@@ -35,7 +35,7 @@ describe "Method#to_proc" do
end
it "returns a proc that can be used by define_method" do
- x = 'test'
+ x = +'test'
to_s = class << x
define_method :foo, method(:to_s).to_proc
to_s
diff --git a/spec/ruby/core/module/alias_method_spec.rb b/spec/ruby/core/module/alias_method_spec.rb
index 391efa227a..c36dedd2d8 100644
--- a/spec/ruby/core/module/alias_method_spec.rb
+++ b/spec/ruby/core/module/alias_method_spec.rb
@@ -92,17 +92,9 @@ describe "Module#alias_method" do
end
describe "returned value" do
- ruby_version_is ""..."3.0" do
- it "returns self" do
- @class.send(:alias_method, :checking_return_value, :public_one).should equal(@class)
- end
- end
-
- ruby_version_is "3.0" do
- it "returns symbol of the defined method name" do
- @class.send(:alias_method, :checking_return_value, :public_one).should equal(:checking_return_value)
- @class.send(:alias_method, 'checking_return_value', :public_one).should equal(:checking_return_value)
- end
+ it "returns symbol of the defined method name" do
+ @class.send(:alias_method, :checking_return_value, :public_one).should equal(:checking_return_value)
+ @class.send(:alias_method, 'checking_return_value', :public_one).should equal(:checking_return_value)
end
end
diff --git a/spec/ruby/core/module/attr_accessor_spec.rb b/spec/ruby/core/module/attr_accessor_spec.rb
index ba5289cbea..503dccc61e 100644
--- a/spec/ruby/core/module/attr_accessor_spec.rb
+++ b/spec/ruby/core/module/attr_accessor_spec.rb
@@ -1,5 +1,6 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
+require_relative 'shared/attr_added'
describe "Module#attr_accessor" do
it "creates a getter and setter for each given attribute name" do
@@ -80,19 +81,9 @@ describe "Module#attr_accessor" do
Module.should have_public_instance_method(:attr_accessor, false)
end
- ruby_version_is ""..."3.0" do
- it "returns nil" do
- Class.new do
- (attr_accessor :foo, 'bar').should == nil
- end
- end
- end
-
- ruby_version_is "3.0" do
- it "returns an array of defined method names as symbols" do
- Class.new do
- (attr_accessor :foo, 'bar').should == [:foo, :foo=, :bar, :bar=]
- end
+ it "returns an array of defined method names as symbols" do
+ Class.new do
+ (attr_accessor :foo, 'bar').should == [:foo, :foo=, :bar, :bar=]
end
end
@@ -116,4 +107,6 @@ describe "Module#attr_accessor" do
1.foobar.should be_nil
end
end
+
+ it_behaves_like :module_attr_added, :attr_accessor
end
diff --git a/spec/ruby/core/module/attr_reader_spec.rb b/spec/ruby/core/module/attr_reader_spec.rb
index b0ae906ab5..37fd537ff5 100644
--- a/spec/ruby/core/module/attr_reader_spec.rb
+++ b/spec/ruby/core/module/attr_reader_spec.rb
@@ -1,5 +1,6 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
+require_relative 'shared/attr_added'
describe "Module#attr_reader" do
it "creates a getter for each given attribute name" do
@@ -62,19 +63,11 @@ describe "Module#attr_reader" do
Module.should have_public_instance_method(:attr_reader, false)
end
- ruby_version_is ""..."3.0" do
- it "returns nil" do
- Class.new do
- (attr_reader :foo, 'bar').should == nil
- end
+ it "returns an array of defined method names as symbols" do
+ Class.new do
+ (attr_reader :foo, 'bar').should == [:foo, :bar]
end
end
- ruby_version_is "3.0" do
- it "returns an array of defined method names as symbols" do
- Class.new do
- (attr_reader :foo, 'bar').should == [:foo, :bar]
- end
- end
- end
+ it_behaves_like :module_attr_added, :attr_reader
end
diff --git a/spec/ruby/core/module/attr_spec.rb b/spec/ruby/core/module/attr_spec.rb
index 33e0eb8628..2f9f4e26dc 100644
--- a/spec/ruby/core/module/attr_spec.rb
+++ b/spec/ruby/core/module/attr_spec.rb
@@ -1,5 +1,6 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
+require_relative 'shared/attr_added'
describe "Module#attr" do
before :each do
@@ -146,23 +147,13 @@ describe "Module#attr" do
Module.should have_public_instance_method(:attr, false)
end
- ruby_version_is ""..."3.0" do
- it "returns nil" do
- Class.new do
- (attr :foo, 'bar').should == nil
- (attr :baz, false).should == nil
- (attr :qux, true).should == nil
- end
+ it "returns an array of defined method names as symbols" do
+ Class.new do
+ (attr :foo, 'bar').should == [:foo, :bar]
+ (attr :baz, false).should == [:baz]
+ (attr :qux, true).should == [:qux, :qux=]
end
end
- ruby_version_is "3.0" do
- it "returns an array of defined method names as symbols" do
- Class.new do
- (attr :foo, 'bar').should == [:foo, :bar]
- (attr :baz, false).should == [:baz]
- (attr :qux, true).should == [:qux, :qux=]
- end
- end
- end
+ it_behaves_like :module_attr_added, :attr
end
diff --git a/spec/ruby/core/module/attr_writer_spec.rb b/spec/ruby/core/module/attr_writer_spec.rb
index 0e9d201317..5b863ef88c 100644
--- a/spec/ruby/core/module/attr_writer_spec.rb
+++ b/spec/ruby/core/module/attr_writer_spec.rb
@@ -1,5 +1,6 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
+require_relative 'shared/attr_added'
describe "Module#attr_writer" do
it "creates a setter for each given attribute name" do
@@ -72,19 +73,11 @@ describe "Module#attr_writer" do
Module.should have_public_instance_method(:attr_writer, false)
end
- ruby_version_is ""..."3.0" do
- it "returns nil" do
- Class.new do
- (attr_writer :foo, 'bar').should == nil
- end
+ it "returns an array of defined method names as symbols" do
+ Class.new do
+ (attr_writer :foo, 'bar').should == [:foo=, :bar=]
end
end
- ruby_version_is "3.0" do
- it "returns an array of defined method names as symbols" do
- Class.new do
- (attr_writer :foo, 'bar').should == [:foo=, :bar=]
- end
- end
- end
+ it_behaves_like :module_attr_added, :attr_writer
end
diff --git a/spec/ruby/core/module/autoload_spec.rb b/spec/ruby/core/module/autoload_spec.rb
index af04ab26c8..45d18b8608 100644
--- a/spec/ruby/core/module/autoload_spec.rb
+++ b/spec/ruby/core/module/autoload_spec.rb
@@ -1,7 +1,6 @@
require_relative '../../spec_helper'
require_relative '../../fixtures/code_loading'
require_relative 'fixtures/classes'
-require 'thread'
describe "Module#autoload?" do
it "returns the name of the file that will be autoloaded" do
@@ -343,6 +342,29 @@ describe "Module#autoload" do
end
end
+ def check_before_during_thread_after(const, &check)
+ before = check.call
+ to_autoload_thread, from_autoload_thread = Queue.new, Queue.new
+ ScratchPad.record -> {
+ from_autoload_thread.push check.call
+ to_autoload_thread.pop
+ }
+ t = Thread.new {
+ in_loading_thread = from_autoload_thread.pop
+ in_other_thread = check.call
+ to_autoload_thread.push :done
+ [in_loading_thread, in_other_thread]
+ }
+ in_loading_thread, in_other_thread = nil
+ begin
+ ModuleSpecs::Autoload.const_get(const)
+ ensure
+ in_loading_thread, in_other_thread = t.value
+ end
+ after = check.call
+ [before, in_loading_thread, in_other_thread, after]
+ end
+
describe "during the autoload before the constant is assigned" do
before :each do
@path = fixture(__FILE__, "autoload_during_autoload.rb")
@@ -351,58 +373,83 @@ describe "Module#autoload" do
raise unless ModuleSpecs::Autoload.autoload?(:DuringAutoload) == @path
end
- def check_before_during_thread_after(&check)
- before = check.call
- to_autoload_thread, from_autoload_thread = Queue.new, Queue.new
- ScratchPad.record -> {
- from_autoload_thread.push check.call
- to_autoload_thread.pop
- }
- t = Thread.new {
- in_loading_thread = from_autoload_thread.pop
- in_other_thread = check.call
- to_autoload_thread.push :done
- [in_loading_thread, in_other_thread]
- }
- in_loading_thread, in_other_thread = nil
- begin
- ModuleSpecs::Autoload::DuringAutoload
- ensure
- in_loading_thread, in_other_thread = t.value
- end
- after = check.call
- [before, in_loading_thread, in_other_thread, after]
- end
-
it "returns nil in autoload thread and 'constant' otherwise for defined?" do
- results = check_before_during_thread_after {
+ results = check_before_during_thread_after(:DuringAutoload) {
defined?(ModuleSpecs::Autoload::DuringAutoload)
}
results.should == ['constant', nil, 'constant', 'constant']
end
it "keeps the constant in Module#constants" do
- results = check_before_during_thread_after {
+ results = check_before_during_thread_after(:DuringAutoload) {
ModuleSpecs::Autoload.constants(false).include?(:DuringAutoload)
}
results.should == [true, true, true, true]
end
it "returns false in autoload thread and true otherwise for Module#const_defined?" do
- results = check_before_during_thread_after {
+ results = check_before_during_thread_after(:DuringAutoload) {
ModuleSpecs::Autoload.const_defined?(:DuringAutoload, false)
}
results.should == [true, false, true, true]
end
it "returns nil in autoload thread and returns the path in other threads for Module#autoload?" do
- results = check_before_during_thread_after {
+ results = check_before_during_thread_after(:DuringAutoload) {
ModuleSpecs::Autoload.autoload?(:DuringAutoload)
}
results.should == [@path, nil, @path, nil]
end
end
+ describe "during the autoload after the constant is assigned" do
+ before :each do
+ @path = fixture(__FILE__, "autoload_during_autoload_after_define.rb")
+ ModuleSpecs::Autoload.autoload :DuringAutoloadAfterDefine, @path
+ @autoload_location = [__FILE__, __LINE__ - 1]
+ @const_location = [@path, 2]
+ @remove << :DuringAutoloadAfterDefine
+ raise unless ModuleSpecs::Autoload.autoload?(:DuringAutoloadAfterDefine) == @path
+ end
+
+ it "returns 'constant' in both threads" do
+ results = check_before_during_thread_after(:DuringAutoloadAfterDefine) {
+ defined?(ModuleSpecs::Autoload::DuringAutoloadAfterDefine)
+ }
+ results.should == ['constant', 'constant', 'constant', 'constant']
+ end
+
+ it "Module#constants include the autoloaded in both threads" do
+ results = check_before_during_thread_after(:DuringAutoloadAfterDefine) {
+ ModuleSpecs::Autoload.constants(false).include?(:DuringAutoloadAfterDefine)
+ }
+ results.should == [true, true, true, true]
+ end
+
+ it "Module#const_defined? returns true in both threads" do
+ results = check_before_during_thread_after(:DuringAutoloadAfterDefine) {
+ ModuleSpecs::Autoload.const_defined?(:DuringAutoloadAfterDefine, false)
+ }
+ results.should == [true, true, true, true]
+ end
+
+ it "returns nil in autoload thread and returns the path in other threads for Module#autoload?" do
+ results = check_before_during_thread_after(:DuringAutoloadAfterDefine) {
+ ModuleSpecs::Autoload.autoload?(:DuringAutoloadAfterDefine)
+ }
+ results.should == [@path, nil, @path, nil]
+ end
+
+ ruby_bug("#20188", ""..."3.4") do
+ it "returns the real constant location in autoload thread and returns the autoload location in other threads for Module#const_source_location" do
+ results = check_before_during_thread_after(:DuringAutoloadAfterDefine) {
+ ModuleSpecs::Autoload.const_source_location(:DuringAutoloadAfterDefine)
+ }
+ results.should == [@autoload_location, @const_location, @autoload_location, @const_location]
+ end
+ end
+ end
+
it "does not remove the constant from Module#constants if load fails and keeps it as an autoload" do
ModuleSpecs::Autoload.autoload :Fail, @non_existent
diff --git a/spec/ruby/core/module/const_added_spec.rb b/spec/ruby/core/module/const_added_spec.rb
index 31ac6eb105..f9edda3a07 100644
--- a/spec/ruby/core/module/const_added_spec.rb
+++ b/spec/ruby/core/module/const_added_spec.rb
@@ -121,5 +121,40 @@ describe "Module#const_added" do
ScratchPad.recorded.should == [line + 2, line + 4, line + 7, line + 11]
end
+
+ it "is called when the constant is already assigned a value" do
+ ScratchPad.record []
+
+ mod = Module.new do
+ def self.const_added(name)
+ ScratchPad.record const_get(name)
+ end
+ end
+
+ mod.module_eval(<<-RUBY, __FILE__, __LINE__ + 1)
+ TEST = 123
+ RUBY
+
+ ScratchPad.recorded.should == 123
+ end
+
+ it "records re-definition of existing constants" do
+ ScratchPad.record []
+
+ mod = Module.new do
+ def self.const_added(name)
+ ScratchPad << const_get(name)
+ end
+ end
+
+ -> {
+ mod.module_eval(<<-RUBY, __FILE__, __LINE__ + 1)
+ TEST = 123
+ TEST = 456
+ RUBY
+ }.should complain(/warning: already initialized constant .+::TEST/)
+
+ ScratchPad.recorded.should == [123, 456]
+ end
end
end
diff --git a/spec/ruby/core/module/const_get_spec.rb b/spec/ruby/core/module/const_get_spec.rb
index 69f181cf51..0233118f4b 100644
--- a/spec/ruby/core/module/const_get_spec.rb
+++ b/spec/ruby/core/module/const_get_spec.rb
@@ -105,7 +105,7 @@ describe "Module#const_get" do
-> { ConstantSpecs.const_get("CS_CONST1", false) }.should raise_error(NameError)
end
- it "returns a constant whose module is defined the the toplevel" do
+ it "returns a constant whose module is defined the toplevel" do
ConstantSpecs.const_get("ConstantSpecsTwo::Foo").should == :cs_two_foo
ConstantSpecsThree.const_get("ConstantSpecsTwo::Foo").should == :cs_three_foo
end
diff --git a/spec/ruby/core/module/const_set_spec.rb b/spec/ruby/core/module/const_set_spec.rb
index ba7810d17b..5bdfd7b68f 100644
--- a/spec/ruby/core/module/const_set_spec.rb
+++ b/spec/ruby/core/module/const_set_spec.rb
@@ -20,20 +20,10 @@ describe "Module#const_set" do
m.name.should == "ConstantSpecs::CS_CONST1000"
end
- ruby_version_is ""..."3.0" do
- it "does not set the name of a module scoped by an anonymous module" do
- a, b = Module.new, Module.new
- a.const_set :B, b
- b.name.should be_nil
- end
- end
-
- ruby_version_is "3.0" do
- it "sets the name of a module scoped by an anonymous module" do
- a, b = Module.new, Module.new
- a.const_set :B, b
- b.name.should.end_with? '::B'
- end
+ it "sets the name of a module scoped by an anonymous module" do
+ a, b = Module.new, Module.new
+ a.const_set :B, b
+ b.name.should.end_with? '::B'
end
it "sets the name of contained modules when assigning a toplevel anonymous module" do
diff --git a/spec/ruby/core/module/const_source_location_spec.rb b/spec/ruby/core/module/const_source_location_spec.rb
index ded2aa51d7..c194c9113f 100644
--- a/spec/ruby/core/module/const_source_location_spec.rb
+++ b/spec/ruby/core/module/const_source_location_spec.rb
@@ -233,5 +233,17 @@ describe "Module#const_source_location" do
line = ConstantSpecs::CONST_LOCATION
ConstantSpecs.const_source_location('CONST_LOCATION').should == [file, line]
end
+
+ ruby_bug("#20188", ""..."3.4") do
+ it 'returns the real constant location as soon as it is defined' do
+ file = fixture(__FILE__, 'autoload_const_source_location.rb')
+ ConstantSpecs.autoload :ConstSource, file
+ autoload_location = [__FILE__, __LINE__ - 1]
+
+ ConstantSpecs.const_source_location(:ConstSource).should == autoload_location
+ ConstantSpecs::ConstSource::LOCATION.should == ConstantSpecs.const_source_location(:ConstSource)
+ ConstantSpecs::BEFORE_DEFINE_LOCATION.should == autoload_location
+ end
+ end
end
end
diff --git a/spec/ruby/core/module/define_method_spec.rb b/spec/ruby/core/module/define_method_spec.rb
index ce94436bfd..e04bb87ceb 100644
--- a/spec/ruby/core/module/define_method_spec.rb
+++ b/spec/ruby/core/module/define_method_spec.rb
@@ -133,6 +133,17 @@ describe "Module#define_method when name is not a special private name" do
klass.should have_public_instance_method(:baz)
end
end
+
+ it "sets the method owner for a dynamically added method with a different original owner" do
+ mixin_module = Module.new do
+ def bar; end
+ end
+
+ foo = Object.new
+ foo.singleton_class.define_method(:bar, mixin_module.instance_method(:bar))
+
+ foo.method(:bar).owner.should == foo.singleton_class
+ end
end
describe "passed a block" do
@@ -488,6 +499,33 @@ describe "Module#define_method" do
Class.new { define_method :bar, m }
}.should raise_error(TypeError, /can't bind singleton method to a different class/)
end
+
+ it "defines a new method with public visibility when a Method passed and the class/module of the context isn't equal to the receiver of #define_method" do
+ c = Class.new do
+ private def foo
+ "public"
+ end
+ end
+
+ object = c.new
+ object.singleton_class.define_method(:bar, object.method(:foo))
+
+ object.bar.should == "public"
+ end
+
+ it "defines the new method according to the scope visibility when a Method passed and the class/module of the context is equal to the receiver of #define_method" do
+ c = Class.new do
+ def foo; end
+ end
+
+ object = c.new
+ object.singleton_class.class_eval do
+ private
+ define_method(:bar, c.new.method(:foo))
+ end
+
+ -> { object.bar }.should raise_error(NoMethodError)
+ end
end
describe "Module#define_method" do
@@ -686,7 +724,7 @@ describe "Module#define_method" do
end
end
-describe "Method#define_method when passed a Method object" do
+describe "Module#define_method when passed a Method object" do
before :each do
@klass = Class.new do
def m(a, b, *c)
@@ -711,7 +749,7 @@ describe "Method#define_method when passed a Method object" do
end
end
-describe "Method#define_method when passed an UnboundMethod object" do
+describe "Module#define_method when passed an UnboundMethod object" do
before :each do
@klass = Class.new do
def m(a, b, *c)
@@ -736,7 +774,7 @@ describe "Method#define_method when passed an UnboundMethod object" do
end
end
-describe "Method#define_method when passed a Proc object" do
+describe "Module#define_method when passed a Proc object" do
describe "and a method is defined inside" do
it "defines the nested method in the default definee where the Proc was created" do
prc = nil
@@ -761,7 +799,7 @@ describe "Method#define_method when passed a Proc object" do
end
end
-describe "Method#define_method when passed a block" do
+describe "Module#define_method when passed a block" do
describe "behaves exactly like a lambda" do
it "for return" do
Class.new do
diff --git a/spec/ruby/core/module/fixtures/autoload_const_source_location.rb b/spec/ruby/core/module/fixtures/autoload_const_source_location.rb
new file mode 100644
index 0000000000..ee0e5a689f
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_const_source_location.rb
@@ -0,0 +1,6 @@
+module ConstantSpecs
+ BEFORE_DEFINE_LOCATION = const_source_location(:ConstSource)
+ module ConstSource
+ LOCATION = Object.const_source_location(name)
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_during_autoload_after_define.rb b/spec/ruby/core/module/fixtures/autoload_during_autoload_after_define.rb
new file mode 100644
index 0000000000..a9d886dfd6
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_during_autoload_after_define.rb
@@ -0,0 +1,6 @@
+module ModuleSpecs::Autoload
+ class DuringAutoloadAfterDefine
+ block = ScratchPad.recorded
+ ScratchPad.record(block.call)
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/module.rb b/spec/ruby/core/module/fixtures/module.rb
index 9050a272ec..34543ca2b4 100644
--- a/spec/ruby/core/module/fixtures/module.rb
+++ b/spec/ruby/core/module/fixtures/module.rb
@@ -1,4 +1,8 @@
module ModuleSpecs
module Anonymous
+ module Child
+ end
+
+ SameChild = Child
end
end
diff --git a/spec/ruby/core/module/module_function_spec.rb b/spec/ruby/core/module/module_function_spec.rb
index 0602e95ca9..1c3ec5471b 100644
--- a/spec/ruby/core/module/module_function_spec.rb
+++ b/spec/ruby/core/module/module_function_spec.rb
@@ -155,15 +155,62 @@ describe "Module#module_function with specific method names" do
m.foo.should == ["m", "super_m"]
end
+
+ context "methods created with define_method" do
+ context "passed a block" do
+ it "creates duplicates of the given instance methods" do
+ m = Module.new do
+ define_method :test1 do; end
+ module_function :test1
+ end
+
+ m.respond_to?(:test1).should == true
+ end
+ end
+
+ context "passed a method" do
+ it "creates duplicates of the given instance methods" do
+ module_with_method = Module.new do
+ def test1; end
+ end
+
+ c = Class.new do
+ extend module_with_method
+ end
+
+ m = Module.new do
+ define_method :test2, c.method(:test1)
+ module_function :test2
+ end
+
+ m.respond_to?(:test2).should == true
+ end
+ end
+
+ context "passed an unbound method" do
+ it "creates duplicates of the given instance methods" do
+ module_with_method = Module.new do
+ def test1; end
+ end
+
+ m = Module.new do
+ define_method :test2, module_with_method.instance_method(:test1)
+ module_function :test2
+ end
+
+ m.respond_to?(:test2).should == true
+ end
+ end
+ end
end
describe "Module#module_function as a toggle (no arguments) in a Module body" do
it "makes any subsequently defined methods module functions with the normal semantics" do
- m = Module.new {
+ m = Module.new do
module_function
def test1() end
def test2() end
- }
+ end
m.respond_to?(:test1).should == true
m.respond_to?(:test2).should == true
@@ -187,13 +234,13 @@ describe "Module#module_function as a toggle (no arguments) in a Module body" do
it "stops creating module functions if the body encounters another toggle " \
"like public/protected/private without arguments" do
- m = Module.new {
+ m = Module.new do
module_function
def test1() end
def test2() end
public
def test3() end
- }
+ end
m.respond_to?(:test1).should == true
m.respond_to?(:test2).should == true
@@ -202,14 +249,14 @@ describe "Module#module_function as a toggle (no arguments) in a Module body" do
it "does not stop creating module functions if the body encounters " \
"public/protected/private WITH arguments" do
- m = Module.new {
+ m = Module.new do
def foo() end
module_function
def test1() end
def test2() end
public :foo
def test3() end
- }
+ end
m.respond_to?(:test1).should == true
m.respond_to?(:test2).should == true
@@ -217,69 +264,116 @@ describe "Module#module_function as a toggle (no arguments) in a Module body" do
end
it "does not affect module_evaled method definitions also if outside the eval itself" do
- m = Module.new {
+ m = Module.new do
module_function
module_eval { def test1() end }
module_eval " def test2() end "
- }
+ end
m.respond_to?(:test1).should == false
m.respond_to?(:test2).should == false
end
it "has no effect if inside a module_eval if the definitions are outside of it" do
- m = Module.new {
+ m = Module.new do
module_eval { module_function }
def test1() end
def test2() end
- }
+ end
m.respond_to?(:test1).should == false
m.respond_to?(:test2).should == false
end
it "functions normally if both toggle and definitions inside a module_eval" do
- m = Module.new {
- module_eval {
+ m = Module.new do
+ module_eval do
module_function
def test1() end
def test2() end
- }
- }
+ end
+ end
m.respond_to?(:test1).should == true
m.respond_to?(:test2).should == true
end
- it "affects evaled method definitions also even when outside the eval itself" do
- m = Module.new {
+ it "affects eval'ed method definitions also even when outside the eval itself" do
+ m = Module.new do
module_function
eval "def test1() end"
- }
+ end
m.respond_to?(:test1).should == true
end
it "doesn't affect definitions when inside an eval even if the definitions are outside of it" do
- m = Module.new {
+ m = Module.new do
eval "module_function"
def test1() end
- }
+ end
m.respond_to?(:test1).should == false
end
it "functions normally if both toggle and definitions inside a eval" do
- m = Module.new {
+ m = Module.new do
eval <<-CODE
module_function
def test1() end
def test2() end
CODE
- }
+ end
m.respond_to?(:test1).should == true
m.respond_to?(:test2).should == true
end
+
+ context "methods are defined with define_method" do
+ context "passed a block" do
+ it "makes any subsequently defined methods module functions with the normal semantics" do
+ m = Module.new do
+ module_function
+ define_method :test1 do; end
+ end
+
+ m.respond_to?(:test1).should == true
+ end
+ end
+
+ context "passed a method" do
+ it "makes any subsequently defined methods module functions with the normal semantics" do
+ module_with_method = Module.new do
+ def test1; end
+ end
+
+ c = Class.new do
+ extend module_with_method
+ end
+
+ m = Module.new do
+ module_function
+ define_method :test2, c.method(:test1)
+ end
+
+ m.respond_to?(:test2).should == true
+ end
+ end
+
+ context "passed an unbound method" do
+ it "makes any subsequently defined methods module functions with the normal semantics" do
+ module_with_method = Module.new do
+ def test1; end
+ end
+
+ m = Module.new do
+ module_function
+ define_method :test2, module_with_method.instance_method(:test1)
+ end
+
+ m.respond_to?(:test2).should == true
+ end
+ end
+ end
end
diff --git a/spec/ruby/core/module/name_spec.rb b/spec/ruby/core/module/name_spec.rb
index b78bbfcc80..0d1f4e24d5 100644
--- a/spec/ruby/core/module/name_spec.rb
+++ b/spec/ruby/core/module/name_spec.rb
@@ -6,20 +6,10 @@ describe "Module#name" do
Module.new.name.should be_nil
end
- ruby_version_is ""..."3.0" do
- it "is nil when assigned to a constant in an anonymous module" do
- m = Module.new
- m::N = Module.new
- m::N.name.should be_nil
- end
- end
-
- ruby_version_is "3.0" do
- it "is not nil when assigned to a constant in an anonymous module" do
- m = Module.new
- m::N = Module.new
- m::N.name.should.end_with? '::N'
- end
+ it "is not nil when assigned to a constant in an anonymous module" do
+ m = Module.new
+ m::N = Module.new
+ m::N.name.should.end_with? '::N'
end
it "is not nil for a nested module created with the module keyword" do
@@ -42,6 +32,19 @@ describe "Module#name" do
m::N.name.should == "ModuleSpecs::Anonymous::WasAnnon"
end
+ it "may be the repeated in different module objects" do
+ m = Module.new
+ n = Module.new
+
+ suppress_warning do
+ ModuleSpecs::Anonymous::SameName = m
+ ModuleSpecs::Anonymous::SameName = n
+ end
+
+ m.name.should == "ModuleSpecs::Anonymous::SameName"
+ n.name.should == "ModuleSpecs::Anonymous::SameName"
+ end
+
it "is set after it is removed from a constant" do
module ModuleSpecs
module ModuleToRemove
@@ -69,12 +72,18 @@ describe "Module#name" do
ModuleSpecs::Anonymous.name.should == "ModuleSpecs::Anonymous"
end
- it "is set when assigning to a constant" do
+ it "is set when assigning to a constant (constant path matches outer module name)" do
m = Module.new
ModuleSpecs::Anonymous::A = m
m.name.should == "ModuleSpecs::Anonymous::A"
end
+ it "is set when assigning to a constant (constant path does not match outer module name)" do
+ m = Module.new
+ ModuleSpecs::Anonymous::SameChild::A = m
+ m.name.should == "ModuleSpecs::Anonymous::Child::A"
+ end
+
it "is not modified when assigning to a new constant after it has been accessed" do
m = Module.new
ModuleSpecs::Anonymous::B = m
@@ -111,13 +120,26 @@ describe "Module#name" do
ModuleSpecs::NameEncoding.new.name.encoding.should == Encoding::UTF_8
end
- it "is set when the anonymous outer module name is set" do
+ it "is set when the anonymous outer module name is set (module in one single constant)" do
m = Module.new
m::N = Module.new
ModuleSpecs::Anonymous::E = m
m::N.name.should == "ModuleSpecs::Anonymous::E::N"
end
+ # https://bugs.ruby-lang.org/issues/19681
+ it "is set when the anonymous outer module name is set (module in several constants)" do
+ m = Module.new
+ m::N = Module.new
+ m::O = m::N
+ ModuleSpecs::Anonymous::StoredInMultiplePlaces = m
+ valid_names = [
+ "ModuleSpecs::Anonymous::StoredInMultiplePlaces::N",
+ "ModuleSpecs::Anonymous::StoredInMultiplePlaces::O"
+ ]
+ valid_names.should include(m::N.name) # You get one of the two, but you don't know which one.
+ end
+
it "returns a frozen String" do
ModuleSpecs.name.should.frozen?
end
diff --git a/spec/ruby/core/module/prepend_spec.rb b/spec/ruby/core/module/prepend_spec.rb
index 976b09b105..c90fa9700e 100644
--- a/spec/ruby/core/module/prepend_spec.rb
+++ b/spec/ruby/core/module/prepend_spec.rb
@@ -499,34 +499,17 @@ describe "Module#prepend" do
c.dup.new.should be_kind_of(m)
end
- ruby_version_is ''...'3.0' do
- it "keeps the module in the chain when dupping an intermediate module" do
- m1 = Module.new { def calc(x) x end }
- m2 = Module.new { prepend(m1) }
- c1 = Class.new { prepend(m2) }
- m2dup = m2.dup
- m2dup.ancestors.should == [m2dup,m1,m2]
- c2 = Class.new { prepend(m2dup) }
- c1.ancestors[0,3].should == [m1,m2,c1]
- c1.new.should be_kind_of(m1)
- c2.ancestors[0,4].should == [m2dup,m1,m2,c2]
- c2.new.should be_kind_of(m1)
- end
- end
-
- ruby_version_is '3.0' do
- it "uses only new module when dupping the module" do
- m1 = Module.new { def calc(x) x end }
- m2 = Module.new { prepend(m1) }
- c1 = Class.new { prepend(m2) }
- m2dup = m2.dup
- m2dup.ancestors.should == [m1,m2dup]
- c2 = Class.new { prepend(m2dup) }
- c1.ancestors[0,3].should == [m1,m2,c1]
- c1.new.should be_kind_of(m1)
- c2.ancestors[0,3].should == [m1,m2dup,c2]
- c2.new.should be_kind_of(m1)
- end
+ it "uses only new module when dupping the module" do
+ m1 = Module.new { def calc(x) x end }
+ m2 = Module.new { prepend(m1) }
+ c1 = Class.new { prepend(m2) }
+ m2dup = m2.dup
+ m2dup.ancestors.should == [m1,m2dup]
+ c2 = Class.new { prepend(m2dup) }
+ c1.ancestors[0,3].should == [m1,m2,c1]
+ c1.new.should be_kind_of(m1)
+ c2.ancestors[0,3].should == [m1,m2dup,c2]
+ c2.new.should be_kind_of(m1)
end
it "depends on prepend_features to add the module" do
@@ -743,6 +726,50 @@ describe "Module#prepend" do
ary.should == [3, 2, 1]
end
+ it "does not prepend a second copy if the module already indirectly exists in the hierarchy" do
+ mod = Module.new do; end
+ submod = Module.new do; end
+ klass = Class.new do; end
+ klass.include(mod)
+ mod.prepend(submod)
+ klass.include(mod)
+
+ klass.ancestors.take(4).should == [klass, submod, mod, Object]
+ end
+
+ # https://bugs.ruby-lang.org/issues/17423
+ describe "when module already exists in ancestor chain" do
+ ruby_version_is ""..."3.1" do
+ it "does not modify the ancestor chain" do
+ m = Module.new do; end
+ a = Module.new do; end
+ b = Class.new do; end
+
+ b.include(a)
+ a.prepend(m)
+ b.ancestors.take(4).should == [b, m, a, Object]
+
+ b.prepend(m)
+ b.ancestors.take(4).should == [b, m, a, Object]
+ end
+ end
+
+ ruby_version_is "3.1" do
+ it "modifies the ancestor chain" do
+ m = Module.new do; end
+ a = Module.new do; end
+ b = Class.new do; end
+
+ b.include(a)
+ a.prepend(m)
+ b.ancestors.take(4).should == [b, m, a, Object]
+
+ b.prepend(m)
+ b.ancestors.take(5).should == [m, b, m, a, Object]
+ end
+ end
+ end
+
describe "called on a module" do
describe "included into a class"
it "does not obscure the module's methods from reflective access" do
diff --git a/spec/ruby/core/module/private_class_method_spec.rb b/spec/ruby/core/module/private_class_method_spec.rb
index 407779cccc..f899c71a57 100644
--- a/spec/ruby/core/module/private_class_method_spec.rb
+++ b/spec/ruby/core/module/private_class_method_spec.rb
@@ -79,15 +79,13 @@ describe "Module#private_class_method" do
end.should raise_error(NameError)
end
- ruby_version_is "3.0" do
- context "when single argument is passed and is an array" do
- it "sets the visibility of the given methods to private" do
- c = Class.new do
- def self.foo() "foo" end
- private_class_method [:foo]
- end
- -> { c.foo }.should raise_error(NoMethodError)
+ context "when single argument is passed and is an array" do
+ it "sets the visibility of the given methods to private" do
+ c = Class.new do
+ def self.foo() "foo" end
+ private_class_method [:foo]
end
+ -> { c.foo }.should raise_error(NoMethodError)
end
end
end
diff --git a/spec/ruby/core/module/public_class_method_spec.rb b/spec/ruby/core/module/public_class_method_spec.rb
index b5d76e7b7a..71b20acda5 100644
--- a/spec/ruby/core/module/public_class_method_spec.rb
+++ b/spec/ruby/core/module/public_class_method_spec.rb
@@ -78,19 +78,17 @@ describe "Module#public_class_method" do
end.should raise_error(NameError)
end
- ruby_version_is "3.0" do
- context "when single argument is passed and is an array" do
- it "makes a class method public" do
- c = Class.new do
- class << self
- private
- def foo() "foo" end
- end
- public_class_method [:foo]
+ context "when single argument is passed and is an array" do
+ it "makes a class method public" do
+ c = Class.new do
+ class << self
+ private
+ def foo() "foo" end
end
-
- c.foo.should == "foo"
+ public_class_method [:foo]
end
+
+ c.foo.should == "foo"
end
end
end
diff --git a/spec/ruby/core/module/ruby2_keywords_spec.rb b/spec/ruby/core/module/ruby2_keywords_spec.rb
index 80a99e2624..aca419f522 100644
--- a/spec/ruby/core/module/ruby2_keywords_spec.rb
+++ b/spec/ruby/core/module/ruby2_keywords_spec.rb
@@ -22,9 +22,6 @@ describe "Module#ruby2_keywords" do
end
h = {a: 1}
- ruby_version_is "3.0" do
- obj.regular(**h).should.equal?(h)
- end
last = mark(**h).last
Hash.ruby2_keywords_hash?(last).should == true
@@ -223,25 +220,6 @@ describe "Module#ruby2_keywords" do
Hash.ruby2_keywords_hash?(last).should == true
end
- ruby_version_is ""..."3.0" do
- it "fixes delegation warnings when calling a method accepting keywords" do
- obj = Object.new
-
- obj.singleton_class.class_exec do
- def foo(*a) bar(*a) end
- def bar(*a, **b) end
- end
-
- -> { obj.foo(1, 2, {a: "a"}) }.should complain(/Using the last argument as keyword parameters is deprecated/)
-
- obj.singleton_class.class_exec do
- ruby2_keywords :foo
- end
-
- -> { obj.foo(1, 2, {a: "a"}) }.should_not complain
- end
- end
-
it "returns nil" do
obj = Object.new
@@ -259,7 +237,7 @@ describe "Module#ruby2_keywords" do
obj.singleton_class.class_exec do
ruby2_keywords :not_existing
end
- }.should raise_error(NameError, /undefined method `not_existing'/)
+ }.should raise_error(NameError, /undefined method [`']not_existing'/)
end
it "accepts String as well" do
diff --git a/spec/ruby/core/module/set_temporary_name_spec.rb b/spec/ruby/core/module/set_temporary_name_spec.rb
new file mode 100644
index 0000000000..f5886a3398
--- /dev/null
+++ b/spec/ruby/core/module/set_temporary_name_spec.rb
@@ -0,0 +1,68 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "3.3" do
+ describe "Module#set_temporary_name" do
+ it "can assign a temporary name" do
+ m = Module.new
+ m.name.should be_nil
+
+ m.set_temporary_name("fake_name")
+ m.name.should == "fake_name"
+
+ m.set_temporary_name(nil)
+ m.name.should be_nil
+ end
+
+ it "can assign a temporary name which is not a valid constant path" do
+ m = Module.new
+ m.set_temporary_name("a::B")
+ m.name.should == "a::B"
+
+ m.set_temporary_name("Template['foo.rb']")
+ m.name.should == "Template['foo.rb']"
+ end
+
+ it "can't assign empty string as name" do
+ m = Module.new
+ -> { m.set_temporary_name("") }.should raise_error(ArgumentError, "empty class/module name")
+ end
+
+ it "can't assign a constant name as a temporary name" do
+ m = Module.new
+ -> { m.set_temporary_name("Object") }.should raise_error(ArgumentError, "the temporary name must not be a constant path to avoid confusion")
+ end
+
+ it "can't assign a constant path as a temporary name" do
+ m = Module.new
+ -> { m.set_temporary_name("A::B") }.should raise_error(ArgumentError, "the temporary name must not be a constant path to avoid confusion")
+ -> { m.set_temporary_name("::A") }.should raise_error(ArgumentError, "the temporary name must not be a constant path to avoid confusion")
+ -> { m.set_temporary_name("::A::B") }.should raise_error(ArgumentError, "the temporary name must not be a constant path to avoid confusion")
+ end
+
+ it "can't assign name to permanent module" do
+ -> { Object.set_temporary_name("fake_name") }.should raise_error(RuntimeError, "can't change permanent name")
+ end
+
+ it "can assign a temporary name to a nested module" do
+ m = Module.new
+ module m::N; end
+ m::N.name.should =~ /\A#<Module:0x\h+>::N\z/
+
+ m::N.set_temporary_name("fake_name")
+ m::N.name.should == "fake_name"
+
+ m::N.set_temporary_name(nil)
+ m::N.name.should be_nil
+ end
+
+ it "can update the name when assigned to a constant" do
+ m = Module.new
+ m::N = Module.new
+ m::N.name.should =~ /\A#<Module:0x\h+>::N\z/
+ m::N.set_temporary_name(nil)
+
+ m::M = m::N
+ m::M.name.should =~ /\A#<Module:0x\h+>::M\z/m
+ end
+ end
+end
diff --git a/spec/ruby/core/module/shared/attr_added.rb b/spec/ruby/core/module/shared/attr_added.rb
new file mode 100644
index 0000000000..ce832cdcff
--- /dev/null
+++ b/spec/ruby/core/module/shared/attr_added.rb
@@ -0,0 +1,34 @@
+describe :module_attr_added, shared: true do
+ it "calls method_added for normal classes" do
+ ScratchPad.record []
+
+ cls = Class.new do
+ class << self
+ def method_added(name)
+ ScratchPad.recorded << name
+ end
+ end
+ end
+
+ cls.send(@method, :foo)
+
+ ScratchPad.recorded.each {|name| name.to_s.should =~ /foo[=]?/}
+ end
+
+ it "calls singleton_method_added for singleton classes" do
+ ScratchPad.record []
+ cls = Class.new do
+ class << self
+ def singleton_method_added(name)
+ # called for this def so ignore it
+ return if name == :singleton_method_added
+ ScratchPad.recorded << name
+ end
+ end
+ end
+
+ cls.singleton_class.send(@method, :foo)
+
+ ScratchPad.recorded.each {|name| name.to_s.should =~ /foo[=]?/}
+ end
+end
diff --git a/spec/ruby/core/module/shared/class_eval.rb b/spec/ruby/core/module/shared/class_eval.rb
index 9ef7b5be44..b1d5cb3814 100644
--- a/spec/ruby/core/module/shared/class_eval.rb
+++ b/spec/ruby/core/module/shared/class_eval.rb
@@ -52,6 +52,12 @@ describe :module_class_eval, shared: true do
ModuleSpecs.send(@method, "[__FILE__, __LINE__]", "test", 102).should == ["test", 102]
end
+ ruby_version_is "3.3" do
+ it "uses the caller location as default filename" do
+ ModuleSpecs.send(@method, "[__FILE__, __LINE__]").should == ["(eval at #{__FILE__}:#{__LINE__})", 1]
+ end
+ end
+
it "converts a non-string filename to a string using to_str" do
(file = mock(__FILE__)).should_receive(:to_str).and_return(__FILE__)
ModuleSpecs.send(@method, "1+1", file)
diff --git a/spec/ruby/core/module/shared/set_visibility.rb b/spec/ruby/core/module/shared/set_visibility.rb
index 9f31e230ca..a1586dd2bd 100644
--- a/spec/ruby/core/module/shared/set_visibility.rb
+++ b/spec/ruby/core/module/shared/set_visibility.rb
@@ -22,21 +22,19 @@ describe :set_visibility, shared: true do
end
end
- ruby_version_is "3.0" do
- describe "array as a single argument" do
- it "sets visibility of given method names" do
- visibility = @method
- old_visibility = [:protected, :private].find {|vis| vis != visibility }
-
- mod = Module.new {
- send old_visibility
- def test1() end
- def test2() end
- send visibility, [:test1, :test2]
- }
- mod.should send(:"have_#{visibility}_instance_method", :test1, false)
- mod.should send(:"have_#{visibility}_instance_method", :test2, false)
- end
+ describe "array as a single argument" do
+ it "sets visibility of given method names" do
+ visibility = @method
+ old_visibility = [:protected, :private].find {|vis| vis != visibility }
+
+ mod = Module.new {
+ send old_visibility
+ def test1() end
+ def test2() end
+ send visibility, [:test1, :test2]
+ }
+ mod.should send(:"have_#{visibility}_instance_method", :test1, false)
+ mod.should send(:"have_#{visibility}_instance_method", :test2, false)
end
end
diff --git a/spec/ruby/core/module/undef_method_spec.rb b/spec/ruby/core/module/undef_method_spec.rb
index c2ad200536..d4efcd51cb 100644
--- a/spec/ruby/core/module/undef_method_spec.rb
+++ b/spec/ruby/core/module/undef_method_spec.rb
@@ -50,7 +50,7 @@ describe "Module#undef_method" do
end
it "raises a NameError when passed a missing name for a module" do
- -> { @module.send :undef_method, :not_exist }.should raise_error(NameError, /undefined method `not_exist' for module `#{@module}'/) { |e|
+ -> { @module.send :undef_method, :not_exist }.should raise_error(NameError, /undefined method [`']not_exist' for module [`']#{@module}'/) { |e|
# a NameError and not a NoMethodError
e.class.should == NameError
}
@@ -58,7 +58,7 @@ describe "Module#undef_method" do
it "raises a NameError when passed a missing name for a class" do
klass = Class.new
- -> { klass.send :undef_method, :not_exist }.should raise_error(NameError, /undefined method `not_exist' for class `#{klass}'/) { |e|
+ -> { klass.send :undef_method, :not_exist }.should raise_error(NameError, /undefined method [`']not_exist' for class [`']#{klass}'/) { |e|
# a NameError and not a NoMethodError
e.class.should == NameError
}
@@ -69,8 +69,8 @@ describe "Module#undef_method" do
obj = klass.new
sclass = obj.singleton_class
- -> { sclass.send :undef_method, :not_exist }.should raise_error(NameError, /undefined method `not_exist' for class `#{sclass}'/) { |e|
- e.message.should include('`#<Class:#<#<Class:')
+ -> { sclass.send :undef_method, :not_exist }.should raise_error(NameError, /undefined method [`']not_exist' for class [`']#{sclass}'/) { |e|
+ e.message.should =~ /[`']#<Class:#<#<Class:/
# a NameError and not a NoMethodError
e.class.should == NameError
@@ -79,7 +79,7 @@ describe "Module#undef_method" do
it "raises a NameError when passed a missing name for a metaclass" do
klass = String.singleton_class
- -> { klass.send :undef_method, :not_exist }.should raise_error(NameError, /undefined method `not_exist' for class `String'/) { |e|
+ -> { klass.send :undef_method, :not_exist }.should raise_error(NameError, /undefined method [`']not_exist' for class [`']String'/) { |e|
# a NameError and not a NoMethodError
e.class.should == NameError
}
diff --git a/spec/ruby/core/module/using_spec.rb b/spec/ruby/core/module/using_spec.rb
index 4781b99bb7..a908363c96 100644
--- a/spec/ruby/core/module/using_spec.rb
+++ b/spec/ruby/core/module/using_spec.rb
@@ -316,7 +316,7 @@ describe "Module#using" do
using refinement
def initialize
- @a = "1703"
+ @a = +"1703"
@a.instance_eval do
def abc
diff --git a/spec/ruby/core/mutex/lock_spec.rb b/spec/ruby/core/mutex/lock_spec.rb
index 7a39817b11..e9d33f5fd9 100644
--- a/spec/ruby/core/mutex/lock_spec.rb
+++ b/spec/ruby/core/mutex/lock_spec.rb
@@ -1,10 +1,6 @@
require_relative '../../spec_helper'
describe "Mutex#lock" do
- before :each do
- ScratchPad.clear
- end
-
it "returns self" do
m = Mutex.new
m.lock.should == m
diff --git a/spec/ruby/core/mutex/owned_spec.rb b/spec/ruby/core/mutex/owned_spec.rb
index 1f843cd576..7bfc7d8f83 100644
--- a/spec/ruby/core/mutex/owned_spec.rb
+++ b/spec/ruby/core/mutex/owned_spec.rb
@@ -41,15 +41,13 @@ describe "Mutex#owned?" do
end
end
- ruby_version_is "3.0" do
- it "is held per Fiber" do
- m = Mutex.new
- m.lock
-
- Fiber.new do
- m.locked?.should == true
- m.owned?.should == false
- end.resume
- end
+ it "is held per Fiber" do
+ m = Mutex.new
+ m.lock
+
+ Fiber.new do
+ m.locked?.should == true
+ m.owned?.should == false
+ end.resume
end
end
diff --git a/spec/ruby/core/nil/singleton_method_spec.rb b/spec/ruby/core/nil/singleton_method_spec.rb
new file mode 100644
index 0000000000..8d898b1cc9
--- /dev/null
+++ b/spec/ruby/core/nil/singleton_method_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+
+describe "NilClass#singleton_method" do
+ ruby_version_is '3.3' do
+ it "raises regardless of whether NilClass defines the method" do
+ -> { nil.singleton_method(:foo) }.should raise_error(NameError)
+ begin
+ def (nil).foo; end
+ -> { nil.singleton_method(:foo) }.should raise_error(NameError)
+ ensure
+ NilClass.send(:remove_method, :foo)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/numeric/clone_spec.rb b/spec/ruby/core/numeric/clone_spec.rb
index c3b06ca0c9..423cec85dd 100644
--- a/spec/ruby/core/numeric/clone_spec.rb
+++ b/spec/ruby/core/numeric/clone_spec.rb
@@ -14,7 +14,7 @@ describe "Numeric#clone" do
1.clone.frozen?.should == true
end
- it "accepts optonal keyword argument :freeze" do
+ it "accepts optional keyword argument :freeze" do
value = 1
value.clone(freeze: true).should equal(value)
end
@@ -23,10 +23,8 @@ describe "Numeric#clone" do
-> { 1.clone(freeze: false) }.should raise_error(ArgumentError, /can't unfreeze/)
end
- ruby_version_is "3.0" do
- it "does not change frozen status if passed freeze: nil" do
- value = 1
- value.clone(freeze: nil).should equal(value)
- end
+ it "does not change frozen status if passed freeze: nil" do
+ value = 1
+ value.clone(freeze: nil).should equal(value)
end
end
diff --git a/spec/ruby/core/numeric/fdiv_spec.rb b/spec/ruby/core/numeric/fdiv_spec.rb
index 907e5d343c..e97fa77f79 100644
--- a/spec/ruby/core/numeric/fdiv_spec.rb
+++ b/spec/ruby/core/numeric/fdiv_spec.rb
@@ -1,5 +1,4 @@
require_relative '../../spec_helper'
-require_relative 'shared/quo'
describe "Numeric#fdiv" do
it "coerces self with #to_f" do
diff --git a/spec/ruby/core/numeric/quo_spec.rb b/spec/ruby/core/numeric/quo_spec.rb
index 67bacee9b5..6e3ce7a374 100644
--- a/spec/ruby/core/numeric/quo_spec.rb
+++ b/spec/ruby/core/numeric/quo_spec.rb
@@ -1,6 +1,5 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
-require_relative 'shared/quo'
describe "Numeric#quo" do
it "returns the result of self divided by the given Integer as a Rational" do
diff --git a/spec/ruby/core/numeric/shared/quo.rb b/spec/ruby/core/numeric/shared/quo.rb
deleted file mode 100644
index 2392636fe7..0000000000
--- a/spec/ruby/core/numeric/shared/quo.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-describe :numeric_quo_18, shared: true do
- it "returns the result of calling self#/ with other" do
- obj = mock_numeric('numeric')
- obj.should_receive(:/).with(19).and_return(:result)
- obj.send(@method, 19).should == :result
- end
-end
diff --git a/spec/ruby/core/numeric/shared/step.rb b/spec/ruby/core/numeric/shared/step.rb
index 8b1a7bf307..977ec6de02 100644
--- a/spec/ruby/core/numeric/shared/step.rb
+++ b/spec/ruby/core/numeric/shared/step.rb
@@ -5,7 +5,7 @@ require_relative '../fixtures/classes'
# To be able to do it, the @step ivar must contain a Proc that transforms
# the step call arguments passed as positional arguments to the style of
# arguments pretended to test.
-describe :numeric_step, :shared => true do
+describe :numeric_step, shared: true do
before :each do
ScratchPad.record []
@prc = -> x { ScratchPad << x }
@@ -258,12 +258,6 @@ describe :numeric_step, :shared => true do
describe "when no block is given" do
step_enum_class = Enumerator::ArithmeticSequence
- ruby_version_is ""..."3.0" do
- it "returns an #{step_enum_class} when step is 0" do
- @step.call(1, 2, 0).should be_an_instance_of(step_enum_class)
- end
- end
-
it "returns an #{step_enum_class} when not passed a block and self > stop" do
@step.call(1, 0, 2).should be_an_instance_of(step_enum_class)
end
diff --git a/spec/ruby/core/numeric/step_spec.rb b/spec/ruby/core/numeric/step_spec.rb
index 095c474fec..1705fb1b4e 100644
--- a/spec/ruby/core/numeric/step_spec.rb
+++ b/spec/ruby/core/numeric/step_spec.rb
@@ -23,30 +23,8 @@ describe "Numeric#step" do
describe "when no block is given" do
step_enum_class = Enumerator::ArithmeticSequence
- ruby_version_is ""..."3.0" do
- it "returns an #{step_enum_class} when step is 0" do
- 1.step(5, 0).should be_an_instance_of(step_enum_class)
- end
-
- it "returns an #{step_enum_class} when step is 0.0" do
- 1.step(2, 0.0).should be_an_instance_of(step_enum_class)
- end
- end
-
describe "returned #{step_enum_class}" do
describe "size" do
- ruby_version_is ""..."3.0" do
- it "is infinity when step is 0" do
- enum = 1.step(5, 0)
- enum.size.should == Float::INFINITY
- end
-
- it "is infinity when step is 0.0" do
- enum = 1.step(2, 0.0)
- enum.size.should == Float::INFINITY
- end
- end
-
it "defaults to an infinite size" do
enum = 1.step
enum.size.should == Float::INFINITY
@@ -63,22 +41,6 @@ describe "Numeric#step" do
end
describe 'with keyword arguments' do
- ruby_version_is ""..."3.0" do
- it "doesn't raise an error when step is 0" do
- -> { 1.step(to: 5, by: 0) { break } }.should_not raise_error
- end
-
- it "doesn't raise an error when step is 0.0" do
- -> { 1.step(to: 2, by: 0.0) { break } }.should_not raise_error
- end
-
- it "should loop over self when step is 0 or 0.0" do
- 1.step(to: 2, by: 0.0).take(5).should eql [1.0, 1.0, 1.0, 1.0, 1.0]
- 1.step(to: 2, by: 0).take(5).should eql [1, 1, 1, 1, 1]
- 1.1.step(to: 2, by: 0).take(5).should eql [1.1, 1.1, 1.1, 1.1, 1.1]
- end
- end
-
describe "when no block is given" do
describe "returned Enumerator" do
describe "size" do
@@ -86,16 +48,6 @@ describe "Numeric#step" do
1.step(by: 42).size.should == infinity_value
end
- ruby_version_is ""..."3.0" do
- it "should return infinity_value when step is 0" do
- 1.step(to: 5, by: 0).size.should == infinity_value
- end
-
- it "should return infinity_value when step is 0.0" do
- 1.step(to: 2, by: 0.0).size.should == infinity_value
- end
- end
-
it "should return infinity_value when ascending towards a limit of Float::INFINITY" do
1.step(to: Float::INFINITY, by: 42).size.should == infinity_value
end
@@ -128,24 +80,12 @@ describe "Numeric#step" do
end
describe 'with mixed arguments' do
- ruby_version_is ""..."3.0" do
- it "doesn't raise an error when step is 0" do
- -> { 1.step(5, by: 0) { break } }.should_not raise_error
- end
-
- it "doesn't raise an error when step is 0.0" do
- -> { 1.step(2, by: 0.0) { break } }.should_not raise_error
- end
+ it " raises an ArgumentError when step is 0" do
+ -> { 1.step(5, by: 0) { break } }.should raise_error(ArgumentError)
end
- ruby_version_is "3.0" do
- it " raises an ArgumentError when step is 0" do
- -> { 1.step(5, by: 0) { break } }.should raise_error(ArgumentError)
- end
-
- it "raises an ArgumentError when step is 0.0" do
- -> { 1.step(2, by: 0.0) { break } }.should raise_error(ArgumentError)
- end
+ it "raises an ArgumentError when step is 0.0" do
+ -> { 1.step(2, by: 0.0) { break } }.should raise_error(ArgumentError)
end
it "raises a ArgumentError when limit and to are defined" do
@@ -156,26 +96,9 @@ describe "Numeric#step" do
-> { 1.step(5, 1, by: 5) { break } }.should raise_error(ArgumentError)
end
- ruby_version_is ""..."3.0" do
- it "should loop over self when step is 0 or 0.0" do
- 1.step(2, by: 0.0).take(5).should eql [1.0, 1.0, 1.0, 1.0, 1.0]
- 1.step(2, by: 0).take(5).should eql [1, 1, 1, 1, 1]
- 1.1.step(2, by: 0).take(5).should eql [1.1, 1.1, 1.1, 1.1, 1.1]
- end
- end
-
describe "when no block is given" do
describe "returned Enumerator" do
describe "size" do
- ruby_version_is ""..."3.0" do
- it "should return infinity_value when step is 0" do
- 1.step(5, by: 0).size.should == infinity_value
- end
-
- it "should return infinity_value when step is 0.0" do
- 1.step(2, by: 0.0).size.should == infinity_value
- end
- end
end
end
end
diff --git a/spec/ruby/core/objectspace/define_finalizer_spec.rb b/spec/ruby/core/objectspace/define_finalizer_spec.rb
index d9db027e0b..effecc41d0 100644
--- a/spec/ruby/core/objectspace/define_finalizer_spec.rb
+++ b/spec/ruby/core/objectspace/define_finalizer_spec.rb
@@ -52,7 +52,7 @@ describe "ObjectSpace.define_finalizer" do
Proc.new { puts "finalizer run" }
end
handler = scoped
- obj = "Test"
+ obj = +"Test"
ObjectSpace.define_finalizer(obj, handler)
exit 0
RUBY
@@ -60,60 +60,58 @@ describe "ObjectSpace.define_finalizer" do
ruby_exe(code, :args => "2>&1").should include("finalizer run\n")
end
- ruby_version_is "3.0" do
- it "warns if the finalizer has the object as the receiver" do
- code = <<-RUBY
- class CapturesSelf
- def initialize
- ObjectSpace.define_finalizer(self, proc {
- puts "finalizer run"
- })
- end
+ it "warns if the finalizer has the object as the receiver" do
+ code = <<-RUBY
+ class CapturesSelf
+ def initialize
+ ObjectSpace.define_finalizer(self, proc {
+ puts "finalizer run"
+ })
end
- CapturesSelf.new
- exit 0
- RUBY
+ end
+ CapturesSelf.new
+ exit 0
+ RUBY
- ruby_exe(code, :args => "2>&1").should include("warning: finalizer references object to be finalized\n")
- end
+ ruby_exe(code, :args => "2>&1").should include("warning: finalizer references object to be finalized\n")
+ end
- it "warns if the finalizer is a method bound to the receiver" do
- code = <<-RUBY
- class CapturesSelf
- def initialize
- ObjectSpace.define_finalizer(self, method(:finalize))
- end
- def finalize(id)
- puts "finalizer run"
- end
+ it "warns if the finalizer is a method bound to the receiver" do
+ code = <<-RUBY
+ class CapturesSelf
+ def initialize
+ ObjectSpace.define_finalizer(self, method(:finalize))
end
- CapturesSelf.new
- exit 0
- RUBY
+ def finalize(id)
+ puts "finalizer run"
+ end
+ end
+ CapturesSelf.new
+ exit 0
+ RUBY
- ruby_exe(code, :args => "2>&1").should include("warning: finalizer references object to be finalized\n")
- end
+ ruby_exe(code, :args => "2>&1").should include("warning: finalizer references object to be finalized\n")
+ end
- it "warns if the finalizer was a block in the receiver" do
- code = <<-RUBY
- class CapturesSelf
- def initialize
- ObjectSpace.define_finalizer(self) do
- puts "finalizer run"
- end
+ it "warns if the finalizer was a block in the receiver" do
+ code = <<-RUBY
+ class CapturesSelf
+ def initialize
+ ObjectSpace.define_finalizer(self) do
+ puts "finalizer run"
end
end
- CapturesSelf.new
- exit 0
- RUBY
+ end
+ CapturesSelf.new
+ exit 0
+ RUBY
- ruby_exe(code, :args => "2>&1").should include("warning: finalizer references object to be finalized\n")
- end
+ ruby_exe(code, :args => "2>&1").should include("warning: finalizer references object to be finalized\n")
end
it "calls a finalizer at exit even if it is self-referencing" do
code = <<-RUBY
- obj = "Test"
+ obj = +"Test"
handler = Proc.new { puts "finalizer run" }
ObjectSpace.define_finalizer(obj, handler)
exit 0
@@ -143,9 +141,9 @@ describe "ObjectSpace.define_finalizer" do
it "calls a finalizer defined in a finalizer running at exit" do
code = <<-RUBY
- obj = "Test"
+ obj = +"Test"
handler = Proc.new do
- obj2 = "Test"
+ obj2 = +"Test"
handler2 = Proc.new { puts "finalizer 2 run" }
ObjectSpace.define_finalizer(obj2, handler2)
exit 0
diff --git a/spec/ruby/core/objectspace/weakkeymap/element_set_spec.rb b/spec/ruby/core/objectspace/weakkeymap/element_set_spec.rb
index 689509d820..c427e01ca5 100644
--- a/spec/ruby/core/objectspace/weakkeymap/element_set_spec.rb
+++ b/spec/ruby/core/objectspace/weakkeymap/element_set_spec.rb
@@ -24,7 +24,7 @@ ruby_version_is "3.3" do
it "requires the keys to implement #hash" do
map = ObjectSpace::WeakKeyMap.new
- -> { map[BasicObject.new] = 1 }.should raise_error(NoMethodError, "undefined method `hash' for an instance of BasicObject")
+ -> { map[BasicObject.new] = 1 }.should raise_error(NoMethodError, /undefined method [`']hash' for an instance of BasicObject/)
end
it "accepts frozen keys or values" do
diff --git a/spec/ruby/core/proc/arity_spec.rb b/spec/ruby/core/proc/arity_spec.rb
index f7cb5ad0f8..5c7728cb30 100644
--- a/spec/ruby/core/proc/arity_spec.rb
+++ b/spec/ruby/core/proc/arity_spec.rb
@@ -268,6 +268,14 @@ describe "Proc#arity" do
@a.arity.should == 3
@b.arity.should == 3
end
+
+ # implicit rest
+ evaluate <<-ruby do
+ @a = lambda { |a, | }
+ ruby
+
+ @a.arity.should == 1
+ end
end
context "returns negative values" do
@@ -530,6 +538,14 @@ describe "Proc#arity" do
@a.arity.should == 1
@b.arity.should == 5
end
+
+ # implicit rest
+ evaluate <<-ruby do
+ @a = proc { |a, | }
+ ruby
+
+ @a.arity.should == 1
+ end
end
context "returns negative values" do
diff --git a/spec/ruby/core/proc/clone_spec.rb b/spec/ruby/core/proc/clone_spec.rb
index a1a1292654..7eca9c561e 100644
--- a/spec/ruby/core/proc/clone_spec.rb
+++ b/spec/ruby/core/proc/clone_spec.rb
@@ -3,4 +3,13 @@ require_relative 'shared/dup'
describe "Proc#clone" do
it_behaves_like :proc_dup, :clone
+
+ ruby_bug "cloning a frozen proc is broken on Ruby 3.3", "3.3"..."3.4" do
+ it "preserves frozen status" do
+ proc = Proc.new { }
+ proc.freeze
+ proc.frozen?.should == true
+ proc.clone.frozen?.should == true
+ end
+ end
end
diff --git a/spec/ruby/core/proc/compose_spec.rb b/spec/ruby/core/proc/compose_spec.rb
index 94814d11bc..9e9b57e06f 100644
--- a/spec/ruby/core/proc/compose_spec.rb
+++ b/spec/ruby/core/proc/compose_spec.rb
@@ -37,42 +37,22 @@ describe "Proc#<<" do
(f << g).should_not.lambda?
end
- ruby_version_is(''...'3.0') do
- it "is a Proc when other is lambda" do
- f = proc { |x| x * x }
- g = -> x { x + x }
-
- (f << g).is_a?(Proc).should == true
- (f << g).should_not.lambda?
- end
-
- it "is a lambda when self is lambda" do
- f = -> x { x * x }
- g = proc { |x| x + x }
-
- (f << g).is_a?(Proc).should == true
- (f << g).should.lambda?
- end
- end
-
- ruby_version_is('3.0') do
- it "is a lambda when parameter is lambda" do
- f = -> x { x * x }
- g = proc { |x| x + x }
- lambda_proc = -> x { x }
+ it "is a lambda when parameter is lambda" do
+ f = -> x { x * x }
+ g = proc { |x| x + x }
+ lambda_proc = -> x { x }
- # lambda << proc
- (f << g).is_a?(Proc).should == true
- (f << g).should_not.lambda?
+ # lambda << proc
+ (f << g).is_a?(Proc).should == true
+ (f << g).should_not.lambda?
- # lambda << lambda
- (f << lambda_proc).is_a?(Proc).should == true
- (f << lambda_proc).should.lambda?
+ # lambda << lambda
+ (f << lambda_proc).is_a?(Proc).should == true
+ (f << lambda_proc).should.lambda?
- # proc << lambda
- (g << f).is_a?(Proc).should == true
- (g << f).should.lambda?
- end
+ # proc << lambda
+ (g << f).is_a?(Proc).should == true
+ (g << f).should.lambda?
end
it "may accept multiple arguments" do
diff --git a/spec/ruby/core/proc/dup_spec.rb b/spec/ruby/core/proc/dup_spec.rb
index 6da2f3080c..dd19b3c1e9 100644
--- a/spec/ruby/core/proc/dup_spec.rb
+++ b/spec/ruby/core/proc/dup_spec.rb
@@ -3,4 +3,11 @@ require_relative 'shared/dup'
describe "Proc#dup" do
it_behaves_like :proc_dup, :dup
+
+ it "resets frozen status" do
+ proc = Proc.new { }
+ proc.freeze
+ proc.frozen?.should == true
+ proc.dup.frozen?.should == false
+ end
end
diff --git a/spec/ruby/core/proc/eql_spec.rb b/spec/ruby/core/proc/eql_spec.rb
index 06aee272e5..ad8f6749fc 100644
--- a/spec/ruby/core/proc/eql_spec.rb
+++ b/spec/ruby/core/proc/eql_spec.rb
@@ -2,11 +2,5 @@ require_relative '../../spec_helper'
require_relative 'shared/equal'
describe "Proc#eql?" do
- ruby_version_is ""..."3.0" do
- it_behaves_like :proc_equal_undefined, :eql?
- end
-
- ruby_version_is "3.0" do
- it_behaves_like :proc_equal, :eql?
- end
+ it_behaves_like :proc_equal, :eql?
end
diff --git a/spec/ruby/core/proc/equal_value_spec.rb b/spec/ruby/core/proc/equal_value_spec.rb
index ee88c0537d..ec7f274732 100644
--- a/spec/ruby/core/proc/equal_value_spec.rb
+++ b/spec/ruby/core/proc/equal_value_spec.rb
@@ -2,11 +2,5 @@ require_relative '../../spec_helper'
require_relative 'shared/equal'
describe "Proc#==" do
- ruby_version_is ""..."3.0" do
- it_behaves_like :proc_equal_undefined, :==
- end
-
- ruby_version_is "3.0" do
- it_behaves_like :proc_equal, :==
- end
+ it_behaves_like :proc_equal, :==
end
diff --git a/spec/ruby/core/proc/fixtures/proc_aref.rb b/spec/ruby/core/proc/fixtures/proc_aref.rb
index a305667797..8ee355b14c 100644
--- a/spec/ruby/core/proc/fixtures/proc_aref.rb
+++ b/spec/ruby/core/proc/fixtures/proc_aref.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
module ProcArefSpecs
def self.aref
proc {|a| a }["sometext"]
diff --git a/spec/ruby/core/proc/lambda_spec.rb b/spec/ruby/core/proc/lambda_spec.rb
index b2d3f50350..5c3c38fc2a 100644
--- a/spec/ruby/core/proc/lambda_spec.rb
+++ b/spec/ruby/core/proc/lambda_spec.rb
@@ -14,9 +14,11 @@ describe "Proc#lambda?" do
Proc.new {}.lambda?.should be_false
end
- it "is preserved when passing a Proc with & to the lambda keyword" do
- suppress_warning {lambda(&->{})}.lambda?.should be_true
- suppress_warning {lambda(&proc{})}.lambda?.should be_false
+ ruby_version_is ""..."3.3" do
+ it "is preserved when passing a Proc with & to the lambda keyword" do
+ suppress_warning {lambda(&->{})}.lambda?.should be_true
+ suppress_warning {lambda(&proc{})}.lambda?.should be_false
+ end
end
it "is preserved when passing a Proc with & to the proc keyword" do
diff --git a/spec/ruby/core/proc/new_spec.rb b/spec/ruby/core/proc/new_spec.rb
index cb52e94f44..b2b7387756 100644
--- a/spec/ruby/core/proc/new_spec.rb
+++ b/spec/ruby/core/proc/new_spec.rb
@@ -166,36 +166,13 @@ describe "Proc.new without a block" do
-> { ProcSpecs.new_proc_subclass_in_method }.should raise_error(ArgumentError)
end
- ruby_version_is ""..."3.0" do
- it "can be created if invoked from within a method with a block" do
- -> { ProcSpecs.new_proc_in_method { "hello" } }.should complain(/Capturing the given block using Proc.new is deprecated/)
- end
-
- it "can be created if invoked on a subclass from within a method with a block" do
- -> { ProcSpecs.new_proc_subclass_in_method { "hello" } }.should complain(/Capturing the given block using Proc.new is deprecated/)
- end
-
-
- it "can be create when called with no block" do
- def some_method
- Proc.new
- end
-
- -> {
- some_method { "hello" }
- }.should complain(/Capturing the given block using Proc.new is deprecated/)
+ it "raises an ArgumentError when passed no block" do
+ def some_method
+ Proc.new
end
- end
- ruby_version_is "3.0" do
- it "raises an ArgumentError when passed no block" do
- def some_method
- Proc.new
- end
-
- -> { ProcSpecs.new_proc_in_method { "hello" } }.should raise_error(ArgumentError, 'tried to create Proc object without a block')
- -> { ProcSpecs.new_proc_subclass_in_method { "hello" } }.should raise_error(ArgumentError, 'tried to create Proc object without a block')
- -> { some_method { "hello" } }.should raise_error(ArgumentError, 'tried to create Proc object without a block')
- end
+ -> { ProcSpecs.new_proc_in_method { "hello" } }.should raise_error(ArgumentError, 'tried to create Proc object without a block')
+ -> { ProcSpecs.new_proc_subclass_in_method { "hello" } }.should raise_error(ArgumentError, 'tried to create Proc object without a block')
+ -> { some_method { "hello" } }.should raise_error(ArgumentError, 'tried to create Proc object without a block')
end
end
diff --git a/spec/ruby/core/proc/parameters_spec.rb b/spec/ruby/core/proc/parameters_spec.rb
index 1ffaf17315..972596d2ea 100644
--- a/spec/ruby/core/proc/parameters_spec.rb
+++ b/spec/ruby/core/proc/parameters_spec.rb
@@ -21,7 +21,7 @@ describe "Proc#parameters" do
end
ruby_version_is "3.2" do
- it "sets the first element of each sub-Array to :req if argument would be required if a lambda if lambda keyword used" do
+ it "sets the first element of each sub-Array to :req for required argument if lambda keyword used" do
proc {|x| }.parameters(lambda: true).first.first.should == :req
proc {|y,*x| }.parameters(lambda: true).first.first.should == :req
end
@@ -33,6 +33,16 @@ describe "Proc#parameters" do
it "regards named parameters in lambda as optional if lambda: false keyword used" do
-> x { }.parameters(lambda: false).first.first.should == :opt
end
+
+ it "regards named parameters in procs and lambdas as required if lambda keyword is truthy" do
+ proc {|x| }.parameters(lambda: 123).first.first.should == :req
+ -> x { }.parameters(lambda: 123).first.first.should == :req
+ end
+
+ it "ignores the lambda keyword if it is nil" do
+ proc {|x|}.parameters(lambda: nil).first.first.should == :opt
+ -> x { }.parameters(lambda: nil).first.first.should == :req
+ end
end
it "regards optional keyword parameters in procs as optional" do
@@ -91,19 +101,38 @@ describe "Proc#parameters" do
proc {|&block| }.parameters.first.last.should == :block
end
- it "ignores unnamed rest args" do
+ it "ignores unnamed rest arguments" do
-> x {}.parameters.should == [[:req, :x]]
end
+ it "ignores implicit rest arguments" do
+ proc { |x, | }.parameters.should == [[:opt, :x]]
+ -> x { }.parameters.should == [[:req, :x]]
+ end
+
ruby_version_is '3.2' do
- it "adds * rest arg for \"star\" argument" do
- -> x, * {}.parameters.should == [[:req, :x], [:rest, :*]]
+ it "adds rest arg with name * for \"star\" argument" do
+ -> * {}.parameters.should == [[:rest, :*]]
+ end
+
+ it "adds keyrest arg with ** as a name for \"double star\" argument" do
+ -> ** {}.parameters.should == [[:keyrest, :**]]
end
end
ruby_version_is ''...'3.2' do
it "adds nameless rest arg for \"star\" argument" do
- -> x, * {}.parameters.should == [[:req, :x], [:rest]]
+ -> * {}.parameters.should == [[:rest]]
+ end
+
+ it "adds nameless keyrest arg for \"double star\" argument" do
+ -> ** {}.parameters.should == [[:keyrest]]
+ end
+ end
+
+ ruby_version_is '3.1' do
+ it "adds block arg with name & for anonymous block argument" do
+ eval('-> & {}.parameters').should == [[:block, :&]]
end
end
@@ -141,4 +170,8 @@ describe "Proc#parameters" do
[:block, :_]
]
end
+
+ it "returns :nokey for **nil parameter" do
+ proc { |**nil| }.parameters.should == [[:nokey]]
+ end
end
diff --git a/spec/ruby/core/proc/ruby2_keywords_spec.rb b/spec/ruby/core/proc/ruby2_keywords_spec.rb
index c6eb03e693..ab67302231 100644
--- a/spec/ruby/core/proc/ruby2_keywords_spec.rb
+++ b/spec/ruby/core/proc/ruby2_keywords_spec.rb
@@ -25,28 +25,6 @@ describe "Proc#ruby2_keywords" do
Hash.ruby2_keywords_hash?(f4.call(1, 2, a: "a")).should == true
end
- ruby_version_is ""..."3.0" do
- it "fixes delegation warnings when calling a method accepting keywords" do
- obj = Object.new
- def obj.foo(*a, **b) end
-
- f = -> *a { obj.foo(*a) }
-
- -> { f.call(1, 2, {a: "a"}) }.should complain(/Using the last argument as keyword parameters is deprecated/)
- f.ruby2_keywords
- -> { f.call(1, 2, {a: "a"}) }.should_not complain
- end
-
- it "fixes delegation warnings when calling a proc accepting keywords" do
- g = -> *a, **b { }
- f = -> *a { g.call(*a) }
-
- -> { f.call(1, 2, {a: "a"}) }.should complain(/Using the last argument as keyword parameters is deprecated/)
- f.ruby2_keywords
- -> { f.call(1, 2, {a: "a"}) }.should_not complain
- end
- end
-
it "returns self" do
f = -> *a { }
f.ruby2_keywords.should equal f
diff --git a/spec/ruby/core/proc/shared/dup.rb b/spec/ruby/core/proc/shared/dup.rb
index 4480f3d0c9..c419a4078a 100644
--- a/spec/ruby/core/proc/shared/dup.rb
+++ b/spec/ruby/core/proc/shared/dup.rb
@@ -15,4 +15,27 @@ describe :proc_dup, shared: true do
cl.new{}.send(@method).class.should == cl
end
end
+
+ ruby_version_is "3.4" do
+ it "copies instance variables" do
+ proc = -> { "hello" }
+ proc.instance_variable_set(:@ivar, 1)
+ cl = proc.send(@method)
+ cl.instance_variables.should == [:@ivar]
+ end
+
+ it "copies the finalizer" do
+ code = <<-RUBY
+ obj = Proc.new { }
+
+ ObjectSpace.define_finalizer(obj, Proc.new { STDOUT.write "finalized\n" })
+
+ obj.clone
+
+ exit 0
+ RUBY
+
+ ruby_exe(code).lines.sort.should == ["finalized\n", "finalized\n"]
+ end
+ end
end
diff --git a/spec/ruby/core/proc/shared/equal.rb b/spec/ruby/core/proc/shared/equal.rb
index 0c0020ca7f..d0503fb064 100644
--- a/spec/ruby/core/proc/shared/equal.rb
+++ b/spec/ruby/core/proc/shared/equal.rb
@@ -81,20 +81,3 @@ describe :proc_equal, shared: true do
p.send(@method, p2).should be_false
end
end
-
-describe :proc_equal_undefined, shared: true do
- it "is not defined" do
- Proc.should_not have_instance_method(@method, false)
- end
-
- it "returns false if other is a dup of the original" do
- p = proc { :foo }
- p.send(@method, p.dup).should be_false
-
- p = Proc.new { :foo }
- p.send(@method, p.dup).should be_false
-
- p = -> { :foo }
- p.send(@method, p.dup).should be_false
- end
-end
diff --git a/spec/ruby/core/proc/shared/to_s.rb b/spec/ruby/core/proc/shared/to_s.rb
index f1e2f416fc..a52688a89f 100644
--- a/spec/ruby/core/proc/shared/to_s.rb
+++ b/spec/ruby/core/proc/shared/to_s.rb
@@ -31,13 +31,13 @@ describe :proc_to_s, shared: true do
describe "for a proc created with UnboundMethod#to_proc" do
it "returns a description including '(lambda)' and optionally including file and line number" do
- def hello; end
- s = method("hello").to_proc.send(@method)
- if s.include? __FILE__
- s.should =~ /^#<Proc:([^ ]*?) #{Regexp.escape __FILE__}:#{__LINE__ - 3} \(lambda\)>$/
- else
- s.should =~ /^#<Proc:([^ ]*?) \(lambda\)>$/
- end
+ def hello; end
+ s = method("hello").to_proc.send(@method)
+ if s.include? __FILE__
+ s.should =~ /^#<Proc:([^ ]*?) #{Regexp.escape __FILE__}:#{__LINE__ - 3} \(lambda\)>$/
+ else
+ s.should =~ /^#<Proc:([^ ]*?) \(lambda\)>$/
+ end
end
it "has a binary encoding" do
diff --git a/spec/ruby/core/proc/source_location_spec.rb b/spec/ruby/core/proc/source_location_spec.rb
index a5895a7fcb..a8b99287d5 100644
--- a/spec/ruby/core/proc/source_location_spec.rb
+++ b/spec/ruby/core/proc/source_location_spec.rb
@@ -19,19 +19,19 @@ describe "Proc#source_location" do
it "sets the first value to the path of the file in which the proc was defined" do
file = @proc.source_location.first
file.should be_an_instance_of(String)
- file.should == File.realpath('../fixtures/source_location.rb', __FILE__)
+ file.should == File.realpath('fixtures/source_location.rb', __dir__)
file = @proc_new.source_location.first
file.should be_an_instance_of(String)
- file.should == File.realpath('../fixtures/source_location.rb', __FILE__)
+ file.should == File.realpath('fixtures/source_location.rb', __dir__)
file = @lambda.source_location.first
file.should be_an_instance_of(String)
- file.should == File.realpath('../fixtures/source_location.rb', __FILE__)
+ file.should == File.realpath('fixtures/source_location.rb', __dir__)
file = @method.source_location.first
file.should be_an_instance_of(String)
- file.should == File.realpath('../fixtures/source_location.rb', __FILE__)
+ file.should == File.realpath('fixtures/source_location.rb', __dir__)
end
it "sets the last value to an Integer representing the line on which the proc was defined" do
diff --git a/spec/ruby/core/process/argv0_spec.rb b/spec/ruby/core/process/argv0_spec.rb
index 7c2342f959..f5aba719e9 100644
--- a/spec/ruby/core/process/argv0_spec.rb
+++ b/spec/ruby/core/process/argv0_spec.rb
@@ -8,7 +8,7 @@ describe "Process.argv0" do
it "is the path given as the main script and the same as __FILE__" do
script = "fixtures/argv0.rb"
- Dir.chdir(File.dirname(__FILE__)) do
+ Dir.chdir(__dir__) do
ruby_exe(script).should == "#{script}\n#{script}\nOK"
end
end
diff --git a/spec/ruby/core/process/constants_spec.rb b/spec/ruby/core/process/constants_spec.rb
index 4130bb58a5..616c54b8e1 100644
--- a/spec/ruby/core/process/constants_spec.rb
+++ b/spec/ruby/core/process/constants_spec.rb
@@ -56,12 +56,18 @@ describe "Process::Constants" do
end
platform_is :netbsd, :freebsd do
- it "Process::RLIMIT_SBSIZE" do
+ it "has the correct constant values on NetBSD and FreeBSD" do
Process::RLIMIT_SBSIZE.should == 9 # FIXME: what's it equal?
Process::RLIMIT_AS.should == 10
end
end
+ platform_is :freebsd do
+ it "has the correct constant values on FreeBSD" do
+ Process::RLIMIT_NPTS.should == 11
+ end
+ end
+
platform_is :windows do
it "does not define RLIMIT constants" do
%i[
diff --git a/spec/ruby/core/process/detach_spec.rb b/spec/ruby/core/process/detach_spec.rb
index 91661afcea..f13bda1f5d 100644
--- a/spec/ruby/core/process/detach_spec.rb
+++ b/spec/ruby/core/process/detach_spec.rb
@@ -44,11 +44,17 @@ describe "Process.detach" do
end
it "tolerates not existing child process pid" do
- # ensure there is no child process with this hardcoded pid
- # `kill 0 pid` for existing process returns "1" and raises Errno::ESRCH if process doesn't exist
- -> { Process.kill(0, 100500) }.should raise_error(Errno::ESRCH)
+ # Use a value that is close to the INT_MAX (pid usually is signed int).
+ # It should (at least) be greater than allowed pid limit value that depends on OS.
+ pid_not_existing = 2.pow(30)
- thr = Process.detach(100500)
+ # Check that there is no a child process with this hardcoded pid.
+ # Command `kill 0 pid`:
+ # - returns "1" if a process exists and
+ # - raises Errno::ESRCH otherwise
+ -> { Process.kill(0, pid_not_existing) }.should raise_error(Errno::ESRCH)
+
+ thr = Process.detach(pid_not_existing)
thr.join
thr.should be_kind_of(Thread)
diff --git a/spec/ruby/core/process/exec_spec.rb b/spec/ruby/core/process/exec_spec.rb
index deb8913b6b..0f371b39c8 100644
--- a/spec/ruby/core/process/exec_spec.rb
+++ b/spec/ruby/core/process/exec_spec.rb
@@ -30,20 +30,20 @@ describe "Process.exec" do
end
it "raises Errno::EACCES when passed a directory" do
- -> { Process.exec File.dirname(__FILE__) }.should raise_error(Errno::EACCES)
+ -> { Process.exec __dir__ }.should raise_error(Errno::EACCES)
end
it "runs the specified command, replacing current process" do
- ruby_exe('Process.exec "echo hello"; puts "fail"', escape: true).should == "hello\n"
+ ruby_exe('Process.exec "echo hello"; puts "fail"').should == "hello\n"
end
it "sets the current directory when given the :chdir option" do
tmpdir = tmp("")[0..-2]
platform_is_not :windows do
- ruby_exe("Process.exec(\"pwd\", chdir: #{tmpdir.inspect})", escape: true).should == "#{tmpdir}\n"
+ ruby_exe("Process.exec(\"pwd\", chdir: #{tmpdir.inspect})").should == "#{tmpdir}\n"
end
platform_is :windows do
- ruby_exe("Process.exec(\"cd\", chdir: #{tmpdir.inspect})", escape: true).tr('\\', '/').should == "#{tmpdir}\n"
+ ruby_exe("Process.exec(\"cd\", chdir: #{tmpdir.inspect})").tr('\\', '/').should == "#{tmpdir}\n"
end
end
@@ -73,13 +73,13 @@ describe "Process.exec" do
platform_is_not :windows do
it "subjects the specified command to shell expansion" do
result = Dir.chdir(@dir) do
- ruby_exe('Process.exec "echo *"', escape: true)
+ ruby_exe('Process.exec "echo *"')
end
result.chomp.should == @name
end
it "creates an argument array with shell parsing semantics for whitespace" do
- ruby_exe('Process.exec "echo a b c d"', escape: true).should == "a b c d\n"
+ ruby_exe('Process.exec "echo a b c d"').should == "a b c d\n"
end
end
@@ -87,13 +87,13 @@ describe "Process.exec" do
# There is no shell expansion on Windows
it "does not subject the specified command to shell expansion on Windows" do
result = Dir.chdir(@dir) do
- ruby_exe('Process.exec "echo *"', escape: true)
+ ruby_exe('Process.exec "echo *"')
end
result.should == "*\n"
end
it "does not create an argument array with shell parsing semantics for whitespace on Windows" do
- ruby_exe('Process.exec "echo a b c d"', escape: true).should == "a b c d\n"
+ ruby_exe('Process.exec "echo a b c d"').should == "a b c d\n"
end
end
@@ -105,7 +105,7 @@ describe "Process.exec" do
platform_is :windows do
cmd = '"cmd.exe", "/C", "echo", "*"'
end
- ruby_exe("Process.exec #{cmd}", escape: true).should == "*\n"
+ ruby_exe("Process.exec #{cmd}").should == "*\n"
end
end
@@ -124,29 +124,29 @@ describe "Process.exec" do
end
it "sets environment variables in the child environment" do
- ruby_exe('Process.exec({"FOO" => "BAR"}, "echo ' + var + '")', escape: true).should == "BAR\n"
+ ruby_exe('Process.exec({"FOO" => "BAR"}, "echo ' + var + '")').should == "BAR\n"
end
it "unsets environment variables whose value is nil" do
platform_is_not :windows do
- ruby_exe('Process.exec({"FOO" => nil}, "echo ' + var + '")', escape: true).should == "\n"
+ ruby_exe('Process.exec({"FOO" => nil}, "echo ' + var + '")').should == "\n"
end
platform_is :windows do
# On Windows, echo-ing a non-existent env var is treated as echo-ing any other string of text
- ruby_exe('Process.exec({"FOO" => nil}, "echo ' + var + '")', escape: true).should == var + "\n"
+ ruby_exe('Process.exec({"FOO" => nil}, "echo ' + var + '")').should == var + "\n"
end
end
it "coerces environment argument using to_hash" do
- ruby_exe('o = Object.new; def o.to_hash; {"FOO" => "BAR"}; end; Process.exec(o, "echo ' + var + '")', escape: true).should == "BAR\n"
+ ruby_exe('o = Object.new; def o.to_hash; {"FOO" => "BAR"}; end; Process.exec(o, "echo ' + var + '")').should == "BAR\n"
end
it "unsets other environment variables when given a true :unsetenv_others option" do
platform_is_not :windows do
- ruby_exe('Process.exec("echo ' + var + '", unsetenv_others: true)', escape: true).should == "\n"
+ ruby_exe('Process.exec("echo ' + var + '", unsetenv_others: true)').should == "\n"
end
platform_is :windows do
- ruby_exe('Process.exec("' + ENV['COMSPEC'].gsub('\\', '\\\\\\') + ' /C echo ' + var + '", unsetenv_others: true)', escape: true).should == var + "\n"
+ ruby_exe('Process.exec("' + ENV['COMSPEC'].gsub('\\', '\\\\\\') + ' /C echo ' + var + '", unsetenv_others: true)').should == var + "\n"
end
end
end
@@ -154,19 +154,19 @@ describe "Process.exec" do
describe "with a command array" do
it "uses the first element as the command name and the second as the argv[0] value" do
platform_is_not :windows do
- ruby_exe('Process.exec(["/bin/sh", "argv_zero"], "-c", "echo $0")', escape: true).should == "argv_zero\n"
+ ruby_exe('Process.exec(["/bin/sh", "argv_zero"], "-c", "echo $0")').should == "argv_zero\n"
end
platform_is :windows do
- ruby_exe('Process.exec(["cmd.exe", "/C"], "/C", "echo", "argv_zero")', escape: true).should == "argv_zero\n"
+ ruby_exe('Process.exec(["cmd.exe", "/C"], "/C", "echo", "argv_zero")').should == "argv_zero\n"
end
end
it "coerces the argument using to_ary" do
platform_is_not :windows do
- ruby_exe('o = Object.new; def o.to_ary; ["/bin/sh", "argv_zero"]; end; Process.exec(o, "-c", "echo $0")', escape: true).should == "argv_zero\n"
+ ruby_exe('o = Object.new; def o.to_ary; ["/bin/sh", "argv_zero"]; end; Process.exec(o, "-c", "echo $0")').should == "argv_zero\n"
end
platform_is :windows do
- ruby_exe('o = Object.new; def o.to_ary; ["cmd.exe", "/C"]; end; Process.exec(o, "/C", "echo", "argv_zero")', escape: true).should == "argv_zero\n"
+ ruby_exe('o = Object.new; def o.to_ary; ["cmd.exe", "/C"]; end; Process.exec(o, "/C", "echo", "argv_zero")').should == "argv_zero\n"
end
end
@@ -200,7 +200,7 @@ describe "Process.exec" do
end
EOC
- ruby_exe(cmd, escape: true)
+ ruby_exe(cmd)
child_fd = IO.read(@child_fd_file).to_i
child_fd.to_i.should > STDERR.fileno
@@ -216,7 +216,7 @@ describe "Process.exec" do
Process.exec("#{ruby_cmd(map_fd_fixture)} \#{f.fileno}", f.fileno => f.fileno)
EOC
- output = ruby_exe(cmd, escape: true)
+ output = ruby_exe(cmd)
child_fd, close_on_exec = output.split
child_fd.to_i.should > STDERR.fileno
@@ -232,7 +232,7 @@ describe "Process.exec" do
puts(f.close_on_exec?)
EOC
- output = ruby_exe(cmd, escape: true)
+ output = ruby_exe(cmd)
output.split.should == ['true', 'false']
end
end
diff --git a/spec/ruby/core/process/fixtures/kill.rb b/spec/ruby/core/process/fixtures/kill.rb
index 0b88f8ee1f..b922a043f1 100644
--- a/spec/ruby/core/process/fixtures/kill.rb
+++ b/spec/ruby/core/process/fixtures/kill.rb
@@ -1,5 +1,3 @@
-require 'thread'
-
pid_file = ARGV.shift
scenario = ARGV.shift
diff --git a/spec/ruby/core/process/spawn_spec.rb b/spec/ruby/core/process/spawn_spec.rb
index c8a58c4d04..283a7f033d 100644
--- a/spec/ruby/core/process/spawn_spec.rb
+++ b/spec/ruby/core/process/spawn_spec.rb
@@ -714,7 +714,7 @@ describe "Process.spawn" do
end
it "raises an Errno::EACCES or Errno::EISDIR when passed a directory" do
- -> { Process.spawn File.dirname(__FILE__) }.should raise_error(SystemCallError) { |e|
+ -> { Process.spawn __dir__ }.should raise_error(SystemCallError) { |e|
[Errno::EACCES, Errno::EISDIR].should include(e.class)
}
end
diff --git a/spec/ruby/core/process/status/termsig_spec.rb b/spec/ruby/core/process/status/termsig_spec.rb
index 5d286950f8..9a22dbea71 100644
--- a/spec/ruby/core/process/status/termsig_spec.rb
+++ b/spec/ruby/core/process/status/termsig_spec.rb
@@ -6,7 +6,7 @@ describe "Process::Status#termsig" do
ruby_exe("exit(0)")
end
- it "returns true" do
+ it "returns nil" do
$?.termsig.should be_nil
end
end
diff --git a/spec/ruby/core/process/status/wait_spec.rb b/spec/ruby/core/process/status/wait_spec.rb
index ee9ea80ebb..57d56209a9 100644
--- a/spec/ruby/core/process/status/wait_spec.rb
+++ b/spec/ruby/core/process/status/wait_spec.rb
@@ -1,102 +1,100 @@
require_relative '../../../spec_helper'
require_relative '../fixtures/common'
-ruby_version_is "3.0" do
- describe "Process::Status.wait" do
- ProcessSpecs.use_system_ruby(self)
-
- before :all do
- begin
- leaked = Process.waitall
- # Ruby-space should not see PIDs used by rjit
- raise "subprocesses leaked before wait specs: #{leaked}" unless leaked.empty?
- rescue NotImplementedError
- end
+describe "Process::Status.wait" do
+ ProcessSpecs.use_system_ruby(self)
+
+ before :all do
+ begin
+ leaked = Process.waitall
+ # Ruby-space should not see PIDs used by rjit
+ raise "subprocesses leaked before wait specs: #{leaked}" unless leaked.empty?
+ rescue NotImplementedError
end
+ end
+
+ it "returns a status with pid -1 if there are no child processes" do
+ Process::Status.wait.pid.should == -1
+ end
- it "returns a status with pid -1 if there are no child processes" do
- Process::Status.wait.pid.should == -1
+ platform_is_not :windows do
+ it "returns a status with its child pid" do
+ pid = Process.spawn(ruby_cmd('exit'))
+ status = Process::Status.wait
+ status.should be_an_instance_of(Process::Status)
+ status.pid.should == pid
end
- platform_is_not :windows do
- it "returns a status with its child pid" do
- pid = Process.spawn(ruby_cmd('exit'))
- status = Process::Status.wait
- status.should be_an_instance_of(Process::Status)
- status.pid.should == pid
- end
+ it "should not set $? to the Process::Status" do
+ pid = Process.spawn(ruby_cmd('exit'))
+ status = Process::Status.wait
+ $?.should_not equal(status)
+ end
- it "should not set $? to the Process::Status" do
- pid = Process.spawn(ruby_cmd('exit'))
- status = Process::Status.wait
- $?.should_not equal(status)
- end
+ it "should not change the value of $?" do
+ pid = Process.spawn(ruby_cmd('exit'))
+ Process.wait
+ status = $?
+ Process::Status.wait
+ status.should equal($?)
+ end
- it "should not change the value of $?" do
- pid = Process.spawn(ruby_cmd('exit'))
- Process.wait
- status = $?
- Process::Status.wait
- status.should equal($?)
- end
+ it "waits for any child process if no pid is given" do
+ pid = Process.spawn(ruby_cmd('exit'))
+ Process::Status.wait.pid.should == pid
+ -> { Process.kill(0, pid) }.should raise_error(Errno::ESRCH)
+ end
- it "waits for any child process if no pid is given" do
- pid = Process.spawn(ruby_cmd('exit'))
- Process::Status.wait.pid.should == pid
- -> { Process.kill(0, pid) }.should raise_error(Errno::ESRCH)
- end
+ it "waits for a specific child if a pid is given" do
+ pid1 = Process.spawn(ruby_cmd('exit'))
+ pid2 = Process.spawn(ruby_cmd('exit'))
+ Process::Status.wait(pid2).pid.should == pid2
+ Process::Status.wait(pid1).pid.should == pid1
+ -> { Process.kill(0, pid1) }.should raise_error(Errno::ESRCH)
+ -> { Process.kill(0, pid2) }.should raise_error(Errno::ESRCH)
+ end
- it "waits for a specific child if a pid is given" do
- pid1 = Process.spawn(ruby_cmd('exit'))
- pid2 = Process.spawn(ruby_cmd('exit'))
- Process::Status.wait(pid2).pid.should == pid2
- Process::Status.wait(pid1).pid.should == pid1
- -> { Process.kill(0, pid1) }.should raise_error(Errno::ESRCH)
- -> { Process.kill(0, pid2) }.should raise_error(Errno::ESRCH)
- end
+ it "coerces the pid to an Integer" do
+ pid1 = Process.spawn(ruby_cmd('exit'))
+ Process::Status.wait(mock_int(pid1)).pid.should == pid1
+ -> { Process.kill(0, pid1) }.should raise_error(Errno::ESRCH)
+ end
- it "coerces the pid to an Integer" do
- pid1 = Process.spawn(ruby_cmd('exit'))
- Process::Status.wait(mock_int(pid1)).pid.should == pid1
- -> { Process.kill(0, pid1) }.should raise_error(Errno::ESRCH)
- end
+ # This spec is probably system-dependent.
+ it "waits for a child whose process group ID is that of the calling process" do
+ pid1 = Process.spawn(ruby_cmd('exit'), pgroup: true)
+ pid2 = Process.spawn(ruby_cmd('exit'))
- # This spec is probably system-dependent.
- it "waits for a child whose process group ID is that of the calling process" do
- pid1 = Process.spawn(ruby_cmd('exit'), pgroup: true)
- pid2 = Process.spawn(ruby_cmd('exit'))
+ Process::Status.wait(0).pid.should == pid2
+ Process::Status.wait.pid.should == pid1
+ end
- Process::Status.wait(0).pid.should == pid2
- Process::Status.wait.pid.should == pid1
+ # This spec is probably system-dependent.
+ it "doesn't block if no child is available when WNOHANG is used" do
+ read, write = IO.pipe
+ pid = Process.fork do
+ read.close
+ Signal.trap("TERM") { Process.exit! }
+ write << 1
+ write.close
+ sleep
end
- # This spec is probably system-dependent.
- it "doesn't block if no child is available when WNOHANG is used" do
- read, write = IO.pipe
- pid = Process.fork do
- read.close
- Signal.trap("TERM") { Process.exit! }
- write << 1
- write.close
- sleep
- end
+ Process::Status.wait(pid, Process::WNOHANG).should be_nil
- Process::Status.wait(pid, Process::WNOHANG).should be_nil
+ # wait for the child to setup its TERM handler
+ write.close
+ read.read(1)
+ read.close
- # wait for the child to setup its TERM handler
- write.close
- read.read(1)
- read.close
-
- Process.kill("TERM", pid)
- Process::Status.wait.pid.should == pid
- end
+ Process.kill("TERM", pid)
+ Process::Status.wait.pid.should == pid
+ end
- it "always accepts flags=0" do
- pid = Process.spawn(ruby_cmd('exit'))
- Process::Status.wait(-1, 0).pid.should == pid
- -> { Process.kill(0, pid) }.should raise_error(Errno::ESRCH)
- end
+ it "always accepts flags=0" do
+ pid = Process.spawn(ruby_cmd('exit'))
+ Process::Status.wait(-1, 0).pid.should == pid
+ -> { Process.kill(0, pid) }.should raise_error(Errno::ESRCH)
end
end
end
diff --git a/spec/ruby/core/process/times_spec.rb b/spec/ruby/core/process/times_spec.rb
index d2610f6415..d3bff2cda9 100644
--- a/spec/ruby/core/process/times_spec.rb
+++ b/spec/ruby/core/process/times_spec.rb
@@ -16,31 +16,4 @@ describe "Process.times" do
Process.times.utime.should > user
end
end
-
- # TODO: The precision of `getrusage` depends on platforms (OpenBSD
- # seems not supporting under-milliseconds in fact); this example is
- # very questionable as an example of Ruby, and it just repeats the
- # guard condition.
- guard -> do
- 1000.times.any? do
- # If getrusage has precision beyond milliseconds, there will be
- # very likely at least one non-zero microsecond results when
- # repeating enough.
- time = Process.clock_gettime(:GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID)
- not ('%.6f' % time).end_with?('000')
- end
- rescue Errno::EINVAL
- false
- end do
- it "uses getrusage when available to improve precision beyond milliseconds" do
- max = 10_000
-
- found = (max * 100).times.find do
- time = Process.times.utime
- !('%.6f' % time).end_with?('000')
- end
-
- found.should_not == nil
- end
- end
end
diff --git a/spec/ruby/core/process/waitpid_spec.rb b/spec/ruby/core/process/waitpid_spec.rb
index f7cf1a45a8..a02147b663 100644
--- a/spec/ruby/core/process/waitpid_spec.rb
+++ b/spec/ruby/core/process/waitpid_spec.rb
@@ -2,7 +2,8 @@ require_relative '../../spec_helper'
describe "Process.waitpid" do
it "returns nil when the process has not yet completed and WNOHANG is specified" do
- pid = spawn("sleep 5")
+ cmd = platform_is(:windows) ? "timeout" : "sleep"
+ pid = spawn("#{cmd} 5")
begin
Process.waitpid(pid, Process::WNOHANG).should == nil
Process.kill("KILL", pid)
diff --git a/spec/ruby/core/process/warmup_spec.rb b/spec/ruby/core/process/warmup_spec.rb
new file mode 100644
index 0000000000..b562d52d22
--- /dev/null
+++ b/spec/ruby/core/process/warmup_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Process.warmup" do
+ ruby_version_is "3.3" do
+ # The behavior is entirely implementation specific.
+ # Other implementations are free to just make it a noop
+ it "is implemented" do
+ Process.warmup.should == true
+ end
+ end
+end
diff --git a/spec/ruby/core/queue/deq_spec.rb b/spec/ruby/core/queue/deq_spec.rb
index 9510978eac..f84d4220ea 100644
--- a/spec/ruby/core/queue/deq_spec.rb
+++ b/spec/ruby/core/queue/deq_spec.rb
@@ -1,6 +1,13 @@
require_relative '../../spec_helper'
require_relative '../../shared/queue/deque'
+require_relative '../../shared/types/rb_num2dbl_fails'
describe "Queue#deq" do
it_behaves_like :queue_deq, :deq, -> { Queue.new }
end
+
+describe "Queue operations with timeout" do
+ ruby_version_is "3.2" do
+ it_behaves_like :rb_num2dbl_fails, nil, -> v { q = Queue.new; q.push(1); q.deq(timeout: v) }
+ end
+end
diff --git a/spec/ruby/core/queue/pop_spec.rb b/spec/ruby/core/queue/pop_spec.rb
index 1ce9231685..d344740834 100644
--- a/spec/ruby/core/queue/pop_spec.rb
+++ b/spec/ruby/core/queue/pop_spec.rb
@@ -1,6 +1,13 @@
require_relative '../../spec_helper'
require_relative '../../shared/queue/deque'
+require_relative '../../shared/types/rb_num2dbl_fails'
describe "Queue#pop" do
it_behaves_like :queue_deq, :pop, -> { Queue.new }
end
+
+describe "Queue operations with timeout" do
+ ruby_version_is "3.2" do
+ it_behaves_like :rb_num2dbl_fails, nil, -> v { q = Queue.new; q.push(1); q.pop(timeout: v) }
+ end
+end
diff --git a/spec/ruby/core/queue/shift_spec.rb b/spec/ruby/core/queue/shift_spec.rb
index f84058e1df..64165e0b61 100644
--- a/spec/ruby/core/queue/shift_spec.rb
+++ b/spec/ruby/core/queue/shift_spec.rb
@@ -1,6 +1,13 @@
require_relative '../../spec_helper'
require_relative '../../shared/queue/deque'
+require_relative '../../shared/types/rb_num2dbl_fails'
describe "Queue#shift" do
it_behaves_like :queue_deq, :shift, -> { Queue.new }
end
+
+describe "Queue operations with timeout" do
+ ruby_version_is "3.2" do
+ it_behaves_like :rb_num2dbl_fails, nil, -> v { q = Queue.new; q.push(1); q.shift(timeout: v) }
+ end
+end
diff --git a/spec/ruby/core/random/bytes_spec.rb b/spec/ruby/core/random/bytes_spec.rb
index ed1b3a7b41..b42dc61234 100644
--- a/spec/ruby/core/random/bytes_spec.rb
+++ b/spec/ruby/core/random/bytes_spec.rb
@@ -9,7 +9,6 @@ describe "Random#bytes" do
Random.new(33).bytes(2).should == Random.new(33).bytes(2)
end
- # Should double check this is official spec
it "returns the same numeric output for a given seed across all implementations and platforms" do
rnd = Random.new(33)
rnd.bytes(2).should == "\x14\\"
diff --git a/spec/ruby/core/random/default_spec.rb b/spec/ruby/core/random/default_spec.rb
index b4ffcb81f4..01d7430df8 100644
--- a/spec/ruby/core/random/default_spec.rb
+++ b/spec/ruby/core/random/default_spec.rb
@@ -14,26 +14,16 @@ describe "Random::DEFAULT" do
seed1.should != seed2
end
- ruby_version_is ''...'3.0' do
- it "returns a Random instance" do
- suppress_warning do
- Random::DEFAULT.should be_an_instance_of(Random)
- end
+ it "refers to the Random class" do
+ suppress_warning do
+ Random::DEFAULT.should.equal?(Random)
end
end
- ruby_version_is '3.0' do
- it "refers to the Random class" do
- suppress_warning do
- Random::DEFAULT.should.equal?(Random)
- end
- end
-
- it "is deprecated" do
- -> {
- Random::DEFAULT.should.equal?(Random)
- }.should complain(/constant Random::DEFAULT is deprecated/)
- end
+ it "is deprecated" do
+ -> {
+ Random::DEFAULT.should.equal?(Random)
+ }.should complain(/constant Random::DEFAULT is deprecated/)
end
end
diff --git a/spec/ruby/core/range/bsearch_spec.rb b/spec/ruby/core/range/bsearch_spec.rb
index 9c93671d85..5254ab756c 100644
--- a/spec/ruby/core/range/bsearch_spec.rb
+++ b/spec/ruby/core/range/bsearch_spec.rb
@@ -9,22 +9,30 @@ describe "Range#bsearch" do
it_behaves_like :enumeratorized_with_unknown_size, :bsearch, (1..3)
it "raises a TypeError if the block returns an Object" do
- -> { (0..1).bsearch { Object.new } }.should raise_error(TypeError)
+ -> { (0..1).bsearch { Object.new } }.should raise_error(TypeError, "wrong argument type Object (must be numeric, true, false or nil)")
end
- it "raises a TypeError if the block returns a String" do
- -> { (0..1).bsearch { "1" } }.should raise_error(TypeError)
+ it "raises a TypeError if the block returns a String and boundaries are Integer values" do
+ -> { (0..1).bsearch { "1" } }.should raise_error(TypeError, "wrong argument type String (must be numeric, true, false or nil)")
+ end
+
+ it "raises a TypeError if the block returns a String and boundaries are Float values" do
+ -> { (0.0..1.0).bsearch { "1" } }.should raise_error(TypeError, "wrong argument type String (must be numeric, true, false or nil)")
end
it "raises a TypeError if the Range has Object values" do
value = mock("range bsearch")
r = Range.new value, value
- -> { r.bsearch { true } }.should raise_error(TypeError)
+ -> { r.bsearch { true } }.should raise_error(TypeError, "can't do binary search for MockObject")
end
it "raises a TypeError if the Range has String values" do
- -> { ("a".."e").bsearch { true } }.should raise_error(TypeError)
+ -> { ("a".."e").bsearch { true } }.should raise_error(TypeError, "can't do binary search for String")
+ end
+
+ it "raises TypeError when non-Numeric begin/end and block not passed" do
+ -> { ("a".."e").bsearch }.should raise_error(TypeError, "can't do binary search for String")
end
context "with Integer values" do
@@ -94,6 +102,10 @@ describe "Range#bsearch" do
(4..2).bsearch { 0 }.should == nil
(4..2).bsearch { -1 }.should == nil
end
+
+ it "returns enumerator when block not passed" do
+ (0...3).bsearch.kind_of?(Enumerator).should == true
+ end
end
context "with Float values" do
@@ -156,7 +168,6 @@ describe "Range#bsearch" do
it "returns nil if the block returns greater than zero for every element" do
(0.3..3.0).bsearch { |x| x <=> -1 }.should be_nil
-
end
it "returns nil if the block never returns zero" do
@@ -213,6 +224,10 @@ describe "Range#bsearch" do
(0...inf).bsearch { |x| x >= Float::MAX ? 0 : 1 }.should == Float::MAX
end
end
+
+ it "returns enumerator when block not passed" do
+ (0.1...2.3).bsearch.kind_of?(Enumerator).should == true
+ end
end
context "with endless ranges and Integer values" do
@@ -250,6 +265,10 @@ describe "Range#bsearch" do
[1, 2, 3].should include(result)
end
end
+
+ it "returns enumerator when block not passed" do
+ eval("(-2..)").bsearch.kind_of?(Enumerator).should == true
+ end
end
context "with endless ranges and Float values" do
@@ -327,8 +346,11 @@ describe "Range#bsearch" do
eval("(0.0...)").bsearch { 0 }.should != inf
end
end
- end
+ it "returns enumerator when block not passed" do
+ eval("(0.1..)").bsearch.kind_of?(Enumerator).should == true
+ end
+ end
context "with beginless ranges and Integer values" do
context "with a block returning true or false" do
@@ -361,6 +383,10 @@ describe "Range#bsearch" do
[1, 2, 3].should include(result)
end
end
+
+ it "returns enumerator when block not passed" do
+ (..10).bsearch.kind_of?(Enumerator).should == true
+ end
end
context "with beginless ranges and Float values" do
@@ -432,5 +458,9 @@ describe "Range#bsearch" do
(...inf).bsearch { |x| 3 - x }.should == 3
end
end
+
+ it "returns enumerator when block not passed" do
+ (..-0.1).bsearch.kind_of?(Enumerator).should == true
+ end
end
end
diff --git a/spec/ruby/core/range/cover_spec.rb b/spec/ruby/core/range/cover_spec.rb
index fa881607e9..eb7cddc967 100644
--- a/spec/ruby/core/range/cover_spec.rb
+++ b/spec/ruby/core/range/cover_spec.rb
@@ -7,4 +7,8 @@ describe "Range#cover?" do
it_behaves_like :range_cover_and_include, :cover?
it_behaves_like :range_cover, :cover?
it_behaves_like :range_cover_subrange, :cover?
+
+ it "covers U+9995 in the range U+0999..U+9999" do
+ ("\u{999}".."\u{9999}").cover?("\u{9995}").should be_true
+ end
end
diff --git a/spec/ruby/core/range/frozen_spec.rb b/spec/ruby/core/range/frozen_spec.rb
index 298ffc87cb..8dab5e5339 100644
--- a/spec/ruby/core/range/frozen_spec.rb
+++ b/spec/ruby/core/range/frozen_spec.rb
@@ -2,26 +2,24 @@ require_relative '../../spec_helper'
# There is no Range#frozen? method but this feels like the best place for these specs
describe "Range#frozen?" do
- ruby_version_is "3.0" do
- it "is true for literal ranges" do
- (1..2).should.frozen?
- (1..).should.frozen?
- (..1).should.frozen?
- end
+ it "is true for literal ranges" do
+ (1..2).should.frozen?
+ (1..).should.frozen?
+ (..1).should.frozen?
+ end
- it "is true for Range.new" do
- Range.new(1, 2).should.frozen?
- Range.new(1, nil).should.frozen?
- Range.new(nil, 1).should.frozen?
- end
+ it "is true for Range.new" do
+ Range.new(1, 2).should.frozen?
+ Range.new(1, nil).should.frozen?
+ Range.new(nil, 1).should.frozen?
+ end
- it "is false for instances of a subclass of Range" do
- sub_range = Class.new(Range).new(1, 2)
- sub_range.should_not.frozen?
- end
+ it "is false for instances of a subclass of Range" do
+ sub_range = Class.new(Range).new(1, 2)
+ sub_range.should_not.frozen?
+ end
- it "is false for Range.allocate" do
- Range.allocate.should_not.frozen?
- end
+ it "is false for Range.allocate" do
+ Range.allocate.should_not.frozen?
end
end
diff --git a/spec/ruby/core/range/include_spec.rb b/spec/ruby/core/range/include_spec.rb
index b2c7a54545..277de205d1 100644
--- a/spec/ruby/core/range/include_spec.rb
+++ b/spec/ruby/core/range/include_spec.rb
@@ -7,4 +7,8 @@ require_relative 'shared/cover'
describe "Range#include?" do
it_behaves_like :range_cover_and_include, :include?
it_behaves_like :range_include, :include?
+
+ it "does not include U+9995 in the range U+0999..U+9999" do
+ ("\u{999}".."\u{9999}").include?("\u{9995}").should be_false
+ end
end
diff --git a/spec/ruby/core/range/initialize_spec.rb b/spec/ruby/core/range/initialize_spec.rb
index 8a6ca65daa..c653caf0c6 100644
--- a/spec/ruby/core/range/initialize_spec.rb
+++ b/spec/ruby/core/range/initialize_spec.rb
@@ -27,18 +27,9 @@ describe "Range#initialize" do
-> { @range.send(:initialize, 1, 3, 5, 7, 9) }.should raise_error(ArgumentError)
end
- ruby_version_is ""..."3.0" do
- it "raises a NameError if called on an already initialized Range" do
- -> { (0..1).send(:initialize, 1, 3) }.should raise_error(NameError)
- -> { (0..1).send(:initialize, 1, 3, true) }.should raise_error(NameError)
- end
- end
-
- ruby_version_is "3.0" do
- it "raises a FrozenError if called on an already initialized Range" do
- -> { (0..1).send(:initialize, 1, 3) }.should raise_error(FrozenError)
- -> { (0..1).send(:initialize, 1, 3, true) }.should raise_error(FrozenError)
- end
+ it "raises a FrozenError if called on an already initialized Range" do
+ -> { (0..1).send(:initialize, 1, 3) }.should raise_error(FrozenError)
+ -> { (0..1).send(:initialize, 1, 3, true) }.should raise_error(FrozenError)
end
it "raises an ArgumentError if arguments don't respond to <=>" do
diff --git a/spec/ruby/core/range/max_spec.rb b/spec/ruby/core/range/max_spec.rb
index 6c9ada2a3c..a3bbc31e7d 100644
--- a/spec/ruby/core/range/max_spec.rb
+++ b/spec/ruby/core/range/max_spec.rb
@@ -50,17 +50,15 @@ describe "Range#max" do
-> { eval("(1..)").max }.should raise_error(RangeError)
end
- ruby_version_is "3.0" do
- it "returns the end point for beginless ranges" do
- (..1).max.should == 1
- (..1.0).max.should == 1.0
- end
+ it "returns the end point for beginless ranges" do
+ (..1).max.should == 1
+ (..1.0).max.should == 1.0
+ end
- it "raises for an exclusive beginless range" do
- -> {
- (...1).max
- }.should raise_error(TypeError, 'cannot exclude end value with non Integer begin value')
- end
+ it "raises for an exclusive beginless range" do
+ -> {
+ (...1).max
+ }.should raise_error(TypeError, 'cannot exclude end value with non Integer begin value')
end
end
diff --git a/spec/ruby/core/range/minmax_spec.rb b/spec/ruby/core/range/minmax_spec.rb
index b2b4fd61a1..6651ae3726 100644
--- a/spec/ruby/core/range/minmax_spec.rb
+++ b/spec/ruby/core/range/minmax_spec.rb
@@ -86,24 +86,22 @@ describe 'Range#minmax' do
/cannot get the maximum of beginless range with custom comparison method|cannot get the minimum of beginless range/)
end
- ruby_bug "#17014", ""..."3.0" do
- it 'should return nil pair if beginning and end are equal without iterating the range' do
- @x.should_not_receive(:succ)
+ it 'should return nil pair if beginning and end are equal without iterating the range' do
+ @x.should_not_receive(:succ)
- (@x...@x).minmax.should == [nil, nil]
- end
+ (@x...@x).minmax.should == [nil, nil]
+ end
- it 'should return nil pair if beginning is greater than end without iterating the range' do
- @y.should_not_receive(:succ)
+ it 'should return nil pair if beginning is greater than end without iterating the range' do
+ @y.should_not_receive(:succ)
- (@y...@x).minmax.should == [nil, nil]
- end
+ (@y...@x).minmax.should == [nil, nil]
+ end
- it 'should return the minimum and maximum values for a non-numeric range by iterating the range' do
- @x.should_receive(:succ).once.and_return(@y)
+ it 'should return the minimum and maximum values for a non-numeric range by iterating the range' do
+ @x.should_receive(:succ).once.and_return(@y)
- (@x...@y).minmax.should == [@x, @x]
- end
+ (@x...@y).minmax.should == [@x, @x]
end
it 'should return the minimum and maximum values for a numeric range' do
diff --git a/spec/ruby/core/range/new_spec.rb b/spec/ruby/core/range/new_spec.rb
index 40df914b83..3cab887799 100644
--- a/spec/ruby/core/range/new_spec.rb
+++ b/spec/ruby/core/range/new_spec.rb
@@ -66,14 +66,12 @@ describe "Range.new" do
range_exclude.should_not == range_include
end
- ruby_version_is "3.0" do
- it "creates a frozen range if the class is Range.class" do
- Range.new(1, 2).should.frozen?
- end
-
- it "does not create a frozen range if the class is not Range.class" do
- Class.new(Range).new(1, 2).should_not.frozen?
- end
+ it "creates a frozen range if the class is Range.class" do
+ Range.new(1, 2).should.frozen?
+ end
+
+ it "does not create a frozen range if the class is not Range.class" do
+ Class.new(Range).new(1, 2).should_not.frozen?
end
end
end
diff --git a/spec/ruby/core/range/shared/cover_and_include.rb b/spec/ruby/core/range/shared/cover_and_include.rb
index 7028afaa89..f36a2cef8b 100644
--- a/spec/ruby/core/range/shared/cover_and_include.rb
+++ b/spec/ruby/core/range/shared/cover_and_include.rb
@@ -57,7 +57,6 @@ describe :range_cover_and_include, shared: true do
it "returns true if argument is less than the last value of the range and greater than the first value" do
(20..30).send(@method, 28).should be_true
('e'..'h').send(@method, 'g').should be_true
- ("\u{999}".."\u{9999}").send @method, "\u{9995}"
end
it "returns true if argument is sole element in the range" do
diff --git a/spec/ruby/core/range/size_spec.rb b/spec/ruby/core/range/size_spec.rb
index 81ea5a3846..a1fe3ce17d 100644
--- a/spec/ruby/core/range/size_spec.rb
+++ b/spec/ruby/core/range/size_spec.rb
@@ -4,34 +4,22 @@ describe "Range#size" do
it "returns the number of elements in the range" do
(1..16).size.should == 16
(1...16).size.should == 15
-
- (1.0..16.0).size.should == 16
- (1.0...16.0).size.should == 15
- (1.0..15.9).size.should == 15
- (1.1..16.0).size.should == 15
- (1.1..15.9).size.should == 15
end
it "returns 0 if last is less than first" do
(16..0).size.should == 0
- (16.0..0.0).size.should == 0
- (Float::INFINITY..0).size.should == 0
end
it 'returns Float::INFINITY for increasing, infinite ranges' do
(0..Float::INFINITY).size.should == Float::INFINITY
- (-Float::INFINITY..0).size.should == Float::INFINITY
- (-Float::INFINITY..Float::INFINITY).size.should == Float::INFINITY
end
it 'returns Float::INFINITY for endless ranges if the start is numeric' do
eval("(1..)").size.should == Float::INFINITY
- eval("(0.5...)").size.should == Float::INFINITY
end
it 'returns nil for endless ranges if the start is not numeric' do
eval("('z'..)").size.should == nil
- eval("([]...)").size.should == nil
end
ruby_version_is ""..."3.2" do
@@ -43,7 +31,7 @@ describe "Range#size" do
end
end
- ruby_version_is "3.2" do
+ ruby_version_is "3.2"..."3.4" do
it 'returns Float::INFINITY for all beginless ranges if the end is numeric' do
(..1).size.should == Float::INFINITY
(...0.5).size.should == Float::INFINITY
@@ -58,6 +46,54 @@ describe "Range#size" do
end
end
+ ruby_version_is ""..."3.4" do
+ it "returns the number of elements in the range" do
+ (1.0..16.0).size.should == 16
+ (1.0...16.0).size.should == 15
+ (1.0..15.9).size.should == 15
+ (1.1..16.0).size.should == 15
+ (1.1..15.9).size.should == 15
+ end
+
+ it "returns 0 if last is less than first" do
+ (16.0..0.0).size.should == 0
+ (Float::INFINITY..0).size.should == 0
+ end
+
+ it 'returns Float::INFINITY for increasing, infinite ranges' do
+ (-Float::INFINITY..0).size.should == Float::INFINITY
+ (-Float::INFINITY..Float::INFINITY).size.should == Float::INFINITY
+ end
+
+ it 'returns Float::INFINITY for endless ranges if the start is numeric' do
+ eval("(0.5...)").size.should == Float::INFINITY
+ end
+
+ it 'returns nil for endless ranges if the start is not numeric' do
+ eval("([]...)").size.should == nil
+ end
+ end
+
+ ruby_version_is "3.4" do
+ it 'raises TypeError if a range is not iterable' do
+ -> { (1.0..16.0).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (1.0...16.0).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (1.0..15.9).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (1.1..16.0).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (1.1..15.9).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (16.0..0.0).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (Float::INFINITY..0).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (-Float::INFINITY..0).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (-Float::INFINITY..Float::INFINITY).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (..1).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (...0.5).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (..nil).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (...'o').size }.should raise_error(TypeError, /can't iterate from/)
+ -> { eval("(0.5...)").size }.should raise_error(TypeError, /can't iterate from/)
+ -> { eval("([]...)").size }.should raise_error(TypeError, /can't iterate from/)
+ end
+ end
+
it "returns nil if first and last are not Numeric" do
(:a..:z).size.should be_nil
('a'..'z').size.should be_nil
diff --git a/spec/ruby/core/range/step_spec.rb b/spec/ruby/core/range/step_spec.rb
index 9024636d55..64ea3de4ed 100644
--- a/spec/ruby/core/range/step_spec.rb
+++ b/spec/ruby/core/range/step_spec.rb
@@ -377,48 +377,21 @@ describe "Range#step" do
end
describe "when no block is given" do
- ruby_version_is "3.0" do
- it "raises an ArgumentError if step is 0" do
- -> { (-1..1).step(0) }.should raise_error(ArgumentError)
- end
+ it "raises an ArgumentError if step is 0" do
+ -> { (-1..1).step(0) }.should raise_error(ArgumentError)
end
describe "returned Enumerator" do
describe "size" do
- ruby_version_is ""..."3.0" do
- it "raises a TypeError if step does not respond to #to_int" do
- obj = mock("Range#step non-integer")
- enum = (1..2).step(obj)
- -> { enum.size }.should raise_error(TypeError)
- end
-
- it "raises a TypeError if #to_int does not return an Integer" do
- obj = mock("Range#step non-integer")
- obj.should_receive(:to_int).and_return("1")
- enum = (1..2).step(obj)
-
- -> { enum.size }.should raise_error(TypeError)
- end
+ it "raises a TypeError if step does not respond to #to_int" do
+ obj = mock("Range#step non-integer")
+ -> { (1..2).step(obj) }.should raise_error(TypeError)
end
- ruby_version_is "3.0" do
- it "raises a TypeError if step does not respond to #to_int" do
- obj = mock("Range#step non-integer")
- -> { (1..2).step(obj) }.should raise_error(TypeError)
- end
-
- it "raises a TypeError if #to_int does not return an Integer" do
- obj = mock("Range#step non-integer")
- obj.should_receive(:to_int).and_return("1")
- -> { (1..2).step(obj) }.should raise_error(TypeError)
- end
- end
-
- ruby_version_is ""..."3.0" do
- it "returns Float::INFINITY for zero step" do
- (-1..1).step(0).size.should == Float::INFINITY
- (-1..1).step(0.0).size.should == Float::INFINITY
- end
+ it "raises a TypeError if #to_int does not return an Integer" do
+ obj = mock("Range#step non-integer")
+ obj.should_receive(:to_int).and_return("1")
+ -> { (1..2).step(obj) }.should raise_error(TypeError)
end
it "returns the ceil of range size divided by the number of steps" do
diff --git a/spec/ruby/core/rational/coerce_spec.rb b/spec/ruby/core/rational/coerce_spec.rb
deleted file mode 100644
index 9c0f05829b..0000000000
--- a/spec/ruby/core/rational/coerce_spec.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-require_relative "../../spec_helper"
-require_relative '../../shared/rational/coerce'
-
-describe "Rational#coerce" do
- it_behaves_like :rational_coerce, :coerce
-end
diff --git a/spec/ruby/core/refinement/extend_object_spec.rb b/spec/ruby/core/refinement/extend_object_spec.rb
index e44e9f46d8..6c2a0af4f3 100644
--- a/spec/ruby/core/refinement/extend_object_spec.rb
+++ b/spec/ruby/core/refinement/extend_object_spec.rb
@@ -11,8 +11,10 @@ describe "Refinement#extend_object" do
Module.new do
refine c do
called = false
- define_method(:extend_object){called = true}
- proc{c.extend(self)}.should raise_error(TypeError)
+ define_method(:extend_object) { called = true }
+ -> {
+ c.extend(self)
+ }.should raise_error(TypeError)
called.should == false
end
end
diff --git a/spec/ruby/core/refinement/import_methods_spec.rb b/spec/ruby/core/refinement/import_methods_spec.rb
index 05973b2380..614c54dff8 100644
--- a/spec/ruby/core/refinement/import_methods_spec.rb
+++ b/spec/ruby/core/refinement/import_methods_spec.rb
@@ -128,7 +128,7 @@ describe "Refinement#import_methods" do
using self
-> {
"foo".indent(3)
- }.should raise_error(NoMethodError, /undefined method `indent' for ("foo":String|an instance of String)/)
+ }.should raise_error(NoMethodError, /undefined method [`']indent' for ("foo":String|an instance of String)/)
end
end
@@ -214,7 +214,7 @@ describe "Refinement#import_methods" do
using self
-> {
String.indent(3)
- }.should raise_error(NoMethodError, /undefined method `indent' for (String:Class|class String)/)
+ }.should raise_error(NoMethodError, /undefined method [`']indent' for (String:Class|class String)/)
end
end
diff --git a/spec/ruby/core/refinement/refined_class_spec.rb b/spec/ruby/core/refinement/refined_class_spec.rb
index bcf48c4e25..00b65d0895 100644
--- a/spec/ruby/core/refinement/refined_class_spec.rb
+++ b/spec/ruby/core/refinement/refined_class_spec.rb
@@ -1,7 +1,7 @@
require_relative '../../spec_helper'
describe "Refinement#refined_class" do
- ruby_version_is "3.2" do
+ ruby_version_is "3.2"..."3.3" do
it "returns the class refined by the receiver" do
refinement_int = nil
diff --git a/spec/ruby/core/regexp/initialize_spec.rb b/spec/ruby/core/regexp/initialize_spec.rb
index a1583384af..dd57292242 100644
--- a/spec/ruby/core/regexp/initialize_spec.rb
+++ b/spec/ruby/core/regexp/initialize_spec.rb
@@ -5,16 +5,8 @@ describe "Regexp#initialize" do
Regexp.should have_private_instance_method(:initialize)
end
- ruby_version_is ""..."3.0" do
- it "raises a SecurityError on a Regexp literal" do
- -> { //.send(:initialize, "") }.should raise_error(SecurityError)
- end
- end
-
- ruby_version_is "3.0" do
- it "raises a FrozenError on a Regexp literal" do
- -> { //.send(:initialize, "") }.should raise_error(FrozenError)
- end
+ it "raises a FrozenError on a Regexp literal" do
+ -> { //.send(:initialize, "") }.should raise_error(FrozenError)
end
it "raises a TypeError on an initialized non-literal Regexp" do
diff --git a/spec/ruby/core/regexp/shared/new.rb b/spec/ruby/core/regexp/shared/new.rb
index 773882e495..7c3fabf612 100644
--- a/spec/ruby/core/regexp/shared/new.rb
+++ b/spec/ruby/core/regexp/shared/new.rb
@@ -123,14 +123,30 @@ describe :regexp_new_string, shared: true do
(r.options & Regexp::EXTENDED).should_not == 0
end
- it "does not try to convert the second argument to Integer with #to_int method call" do
- ScratchPad.clear
- obj = Object.new
- def obj.to_int() ScratchPad.record(:called) end
+ ruby_version_is ""..."3.2" do
+ it "does not try to convert the second argument to Integer with #to_int method call" do
+ ScratchPad.clear
+ obj = Object.new
+ def obj.to_int() ScratchPad.record(:called) end
+
+ Regexp.send(@method, "Hi", obj)
- Regexp.send(@method, "Hi", obj)
+ ScratchPad.recorded.should == nil
+ end
+ end
- ScratchPad.recorded.should == nil
+ ruby_version_is "3.2" do
+ it "does not try to convert the second argument to Integer with #to_int method call" do
+ ScratchPad.clear
+ obj = Object.new
+ def obj.to_int() ScratchPad.record(:called) end
+
+ -> {
+ Regexp.send(@method, "Hi", obj)
+ }.should complain(/expected true or false as ignorecase/, {verbose: true})
+
+ ScratchPad.recorded.should == nil
+ end
end
ruby_version_is ""..."3.2" do
@@ -188,12 +204,12 @@ describe :regexp_new_string, shared: true do
end
it "raises an Argument error if the second argument contains unsupported chars" do
- -> { Regexp.send(@method, 'Hi', 'e') }.should raise_error(ArgumentError)
- -> { Regexp.send(@method, 'Hi', 'n') }.should raise_error(ArgumentError)
- -> { Regexp.send(@method, 'Hi', 's') }.should raise_error(ArgumentError)
- -> { Regexp.send(@method, 'Hi', 'u') }.should raise_error(ArgumentError)
- -> { Regexp.send(@method, 'Hi', 'j') }.should raise_error(ArgumentError)
- -> { Regexp.send(@method, 'Hi', 'mjx') }.should raise_error(ArgumentError)
+ -> { Regexp.send(@method, 'Hi', 'e') }.should raise_error(ArgumentError, "unknown regexp option: e")
+ -> { Regexp.send(@method, 'Hi', 'n') }.should raise_error(ArgumentError, "unknown regexp option: n")
+ -> { Regexp.send(@method, 'Hi', 's') }.should raise_error(ArgumentError, "unknown regexp option: s")
+ -> { Regexp.send(@method, 'Hi', 'u') }.should raise_error(ArgumentError, "unknown regexp option: u")
+ -> { Regexp.send(@method, 'Hi', 'j') }.should raise_error(ArgumentError, "unknown regexp option: j")
+ -> { Regexp.send(@method, 'Hi', 'mjx') }.should raise_error(ArgumentError, /unknown regexp option: mjx\b/)
end
end
@@ -473,12 +489,12 @@ describe :regexp_new_string, shared: true do
end
it "returns a Regexp with the input String's encoding" do
- str = "\x82\xa0".force_encoding(Encoding::Shift_JIS)
+ str = "\x82\xa0".dup.force_encoding(Encoding::Shift_JIS)
Regexp.send(@method, str).encoding.should == Encoding::Shift_JIS
end
it "returns a Regexp with source String having the input String's encoding" do
- str = "\x82\xa0".force_encoding(Encoding::Shift_JIS)
+ str = "\x82\xa0".dup.force_encoding(Encoding::Shift_JIS)
Regexp.send(@method, str).source.encoding.should == Encoding::Shift_JIS
end
end
diff --git a/spec/ruby/core/regexp/shared/quote.rb b/spec/ruby/core/regexp/shared/quote.rb
index 9533102766..b5ecc35f04 100644
--- a/spec/ruby/core/regexp/shared/quote.rb
+++ b/spec/ruby/core/regexp/shared/quote.rb
@@ -18,23 +18,23 @@ describe :regexp_quote, shared: true do
end
it "works for broken strings" do
- Regexp.send(@method, "a.\x85b.".force_encoding("US-ASCII")).should =="a\\.\x85b\\.".force_encoding("US-ASCII")
- Regexp.send(@method, "a.\x80".force_encoding("UTF-8")).should == "a\\.\x80".force_encoding("UTF-8")
+ Regexp.send(@method, "a.\x85b.".dup.force_encoding("US-ASCII")).should =="a\\.\x85b\\.".dup.force_encoding("US-ASCII")
+ Regexp.send(@method, "a.\x80".dup.force_encoding("UTF-8")).should == "a\\.\x80".dup.force_encoding("UTF-8")
end
it "sets the encoding of the result to US-ASCII if there are only US-ASCII characters present in the input String" do
- str = "abc".force_encoding("euc-jp")
+ str = "abc".dup.force_encoding("euc-jp")
Regexp.send(@method, str).encoding.should == Encoding::US_ASCII
end
it "sets the encoding of the result to the encoding of the String if any non-US-ASCII characters are present in an input String with valid encoding" do
- str = "ã‚ã‚ŠãŒã¨ã†".force_encoding("utf-8")
+ str = "ã‚ã‚ŠãŒã¨ã†".dup.force_encoding("utf-8")
str.valid_encoding?.should be_true
Regexp.send(@method, str).encoding.should == Encoding::UTF_8
end
it "sets the encoding of the result to BINARY if any non-US-ASCII characters are present in an input String with invalid encoding" do
- str = "\xff".force_encoding "us-ascii"
+ str = "\xff".dup.force_encoding "us-ascii"
str.valid_encoding?.should be_false
Regexp.send(@method, "\xff").encoding.should == Encoding::BINARY
end
diff --git a/spec/ruby/core/regexp/union_spec.rb b/spec/ruby/core/regexp/union_spec.rb
index 8076836471..ea5a5053f7 100644
--- a/spec/ruby/core/regexp/union_spec.rb
+++ b/spec/ruby/core/regexp/union_spec.rb
@@ -43,6 +43,27 @@ describe "Regexp.union" do
Regexp.union("\u00A9".encode("ISO-8859-1"), "a".encode("UTF-8")).encoding.should == Encoding::ISO_8859_1
end
+ it "returns ASCII-8BIT if the regexp encodings are ASCII-8BIT and at least one has non-ASCII characters" do
+ us_ascii_implicit, us_ascii_explicit, binary = /abc/, /[\x00-\x7f]/n, /[\x80-\xBF]/n
+ us_ascii_implicit.encoding.should == Encoding::US_ASCII
+ us_ascii_explicit.encoding.should == Encoding::US_ASCII
+ binary.encoding.should == Encoding::BINARY
+
+ Regexp.union(us_ascii_implicit, us_ascii_explicit, binary).encoding.should == Encoding::BINARY
+ Regexp.union(us_ascii_implicit, binary, us_ascii_explicit).encoding.should == Encoding::BINARY
+ Regexp.union(us_ascii_explicit, us_ascii_implicit, binary).encoding.should == Encoding::BINARY
+ Regexp.union(us_ascii_explicit, binary, us_ascii_implicit).encoding.should == Encoding::BINARY
+ Regexp.union(binary, us_ascii_implicit, us_ascii_explicit).encoding.should == Encoding::BINARY
+ Regexp.union(binary, us_ascii_explicit, us_ascii_implicit).encoding.should == Encoding::BINARY
+ end
+
+ it "return US-ASCII if all patterns are ASCII-only" do
+ Regexp.union(/abc/e, /def/e).encoding.should == Encoding::US_ASCII
+ Regexp.union(/abc/n, /def/n).encoding.should == Encoding::US_ASCII
+ Regexp.union(/abc/s, /def/s).encoding.should == Encoding::US_ASCII
+ Regexp.union(/abc/u, /def/u).encoding.should == Encoding::US_ASCII
+ end
+
it "returns a Regexp with UTF-8 if one part is UTF-8" do
Regexp.union(/probl[éeè]me/i, /help/i).encoding.should == Encoding::UTF_8
end
@@ -54,83 +75,83 @@ describe "Regexp.union" do
it "raises ArgumentError if the arguments include conflicting ASCII-incompatible Strings" do
-> {
Regexp.union("a".encode("UTF-16LE"), "b".encode("UTF-16BE"))
- }.should raise_error(ArgumentError)
+ }.should raise_error(ArgumentError, 'incompatible encodings: UTF-16LE and UTF-16BE')
end
it "raises ArgumentError if the arguments include conflicting ASCII-incompatible Regexps" do
-> {
Regexp.union(Regexp.new("a".encode("UTF-16LE")),
Regexp.new("b".encode("UTF-16BE")))
- }.should raise_error(ArgumentError)
+ }.should raise_error(ArgumentError, 'incompatible encodings: UTF-16LE and UTF-16BE')
end
it "raises ArgumentError if the arguments include conflicting fixed encoding Regexps" do
-> {
Regexp.union(Regexp.new("a".encode("UTF-8"), Regexp::FIXEDENCODING),
Regexp.new("b".encode("US-ASCII"), Regexp::FIXEDENCODING))
- }.should raise_error(ArgumentError)
+ }.should raise_error(ArgumentError, 'incompatible encodings: UTF-8 and US-ASCII')
end
it "raises ArgumentError if the arguments include a fixed encoding Regexp and a String containing non-ASCII-compatible characters in a different encoding" do
-> {
Regexp.union(Regexp.new("a".encode("UTF-8"), Regexp::FIXEDENCODING),
"\u00A9".encode("ISO-8859-1"))
- }.should raise_error(ArgumentError)
+ }.should raise_error(ArgumentError, 'incompatible encodings: UTF-8 and ISO-8859-1')
end
it "raises ArgumentError if the arguments include a String containing non-ASCII-compatible characters and a fixed encoding Regexp in a different encoding" do
-> {
Regexp.union("\u00A9".encode("ISO-8859-1"),
Regexp.new("a".encode("UTF-8"), Regexp::FIXEDENCODING))
- }.should raise_error(ArgumentError)
+ }.should raise_error(ArgumentError, 'incompatible encodings: ISO-8859-1 and UTF-8')
end
it "raises ArgumentError if the arguments include an ASCII-incompatible String and an ASCII-only String" do
-> {
Regexp.union("a".encode("UTF-16LE"), "b".encode("UTF-8"))
- }.should raise_error(ArgumentError)
+ }.should raise_error(ArgumentError, /ASCII incompatible encoding: UTF-16LE|incompatible encodings: UTF-16LE and US-ASCII/)
end
it "raises ArgumentError if the arguments include an ASCII-incompatible Regexp and an ASCII-only String" do
-> {
Regexp.union(Regexp.new("a".encode("UTF-16LE")), "b".encode("UTF-8"))
- }.should raise_error(ArgumentError)
+ }.should raise_error(ArgumentError, /ASCII incompatible encoding: UTF-16LE|incompatible encodings: UTF-16LE and US-ASCII/)
end
it "raises ArgumentError if the arguments include an ASCII-incompatible String and an ASCII-only Regexp" do
-> {
Regexp.union("a".encode("UTF-16LE"), Regexp.new("b".encode("UTF-8")))
- }.should raise_error(ArgumentError)
+ }.should raise_error(ArgumentError, /ASCII incompatible encoding: UTF-16LE|incompatible encodings: UTF-16LE and US-ASCII/)
end
it "raises ArgumentError if the arguments include an ASCII-incompatible Regexp and an ASCII-only Regexp" do
-> {
Regexp.union(Regexp.new("a".encode("UTF-16LE")), Regexp.new("b".encode("UTF-8")))
- }.should raise_error(ArgumentError)
+ }.should raise_error(ArgumentError, /ASCII incompatible encoding: UTF-16LE|incompatible encodings: UTF-16LE and US-ASCII/)
end
it "raises ArgumentError if the arguments include an ASCII-incompatible String and a String containing non-ASCII-compatible characters in a different encoding" do
-> {
Regexp.union("a".encode("UTF-16LE"), "\u00A9".encode("ISO-8859-1"))
- }.should raise_error(ArgumentError)
+ }.should raise_error(ArgumentError, 'incompatible encodings: UTF-16LE and ISO-8859-1')
end
it "raises ArgumentError if the arguments include an ASCII-incompatible Regexp and a String containing non-ASCII-compatible characters in a different encoding" do
-> {
Regexp.union(Regexp.new("a".encode("UTF-16LE")), "\u00A9".encode("ISO-8859-1"))
- }.should raise_error(ArgumentError)
+ }.should raise_error(ArgumentError, 'incompatible encodings: UTF-16LE and ISO-8859-1')
end
it "raises ArgumentError if the arguments include an ASCII-incompatible String and a Regexp containing non-ASCII-compatible characters in a different encoding" do
-> {
Regexp.union("a".encode("UTF-16LE"), Regexp.new("\u00A9".encode("ISO-8859-1")))
- }.should raise_error(ArgumentError)
+ }.should raise_error(ArgumentError, 'incompatible encodings: UTF-16LE and ISO-8859-1')
end
it "raises ArgumentError if the arguments include an ASCII-incompatible Regexp and a Regexp containing non-ASCII-compatible characters in a different encoding" do
-> {
Regexp.union(Regexp.new("a".encode("UTF-16LE")), Regexp.new("\u00A9".encode("ISO-8859-1")))
- }.should raise_error(ArgumentError)
+ }.should raise_error(ArgumentError, 'incompatible encodings: UTF-16LE and ISO-8859-1')
end
it "uses to_str to convert arguments (if not Regexp)" do
@@ -154,6 +175,8 @@ describe "Regexp.union" do
not_supported_on :opal do
Regexp.union([/dogs/, /cats/i]).should == /(?-mix:dogs)|(?i-mx:cats)/
end
- ->{Regexp.union(["skiing", "sledding"], [/dogs/, /cats/i])}.should raise_error(TypeError)
+ -> {
+ Regexp.union(["skiing", "sledding"], [/dogs/, /cats/i])
+ }.should raise_error(TypeError, 'no implicit conversion of Array into String')
end
end
diff --git a/spec/ruby/core/signal/signame_spec.rb b/spec/ruby/core/signal/signame_spec.rb
index b66de9fc85..adfe895d97 100644
--- a/spec/ruby/core/signal/signame_spec.rb
+++ b/spec/ruby/core/signal/signame_spec.rb
@@ -9,10 +9,22 @@ describe "Signal.signame" do
Signal.signame(-1).should == nil
end
+ it "calls #to_int on an object to convert to an Integer" do
+ obj = mock('signal')
+ obj.should_receive(:to_int).and_return(0)
+ Signal.signame(obj).should == "EXIT"
+ end
+
it "raises a TypeError when the passed argument can't be coerced to Integer" do
-> { Signal.signame("hello") }.should raise_error(TypeError)
end
+ it "raises a TypeError when the passed argument responds to #to_int but does not return an Integer" do
+ obj = mock('signal')
+ obj.should_receive(:to_int).and_return('not an int')
+ -> { Signal.signame(obj) }.should raise_error(TypeError)
+ end
+
platform_is_not :windows do
it "the original should take precedence over alias when looked up by number" do
Signal.signame(Signal.list["ABRT"]).should == "ABRT"
diff --git a/spec/ruby/core/signal/trap_spec.rb b/spec/ruby/core/signal/trap_spec.rb
index 10e122e072..6d654a99be 100644
--- a/spec/ruby/core/signal/trap_spec.rb
+++ b/spec/ruby/core/signal/trap_spec.rb
@@ -221,10 +221,29 @@ describe "Signal.trap" do
Signal.trap(:HUP, @saved_trap).should equal(@proc)
end
+ it "calls #to_str on an object to convert to a String" do
+ obj = mock("signal")
+ obj.should_receive(:to_str).exactly(2).times.and_return("HUP")
+ Signal.trap obj, @proc
+ Signal.trap(obj, @saved_trap).should equal(@proc)
+ end
+
+ it "accepts Integer values" do
+ hup = Signal.list["HUP"]
+ Signal.trap hup, @proc
+ Signal.trap(hup, @saved_trap).should equal(@proc)
+ end
+
+ it "does not call #to_int on an object to convert to an Integer" do
+ obj = mock("signal")
+ obj.should_not_receive(:to_int)
+ -> { Signal.trap obj, @proc }.should raise_error(ArgumentError, /bad signal type/)
+ end
+
it "raises ArgumentError when passed unknown signal" do
-> { Signal.trap(300) { } }.should raise_error(ArgumentError, "invalid signal number (300)")
- -> { Signal.trap("USR10") { } }.should raise_error(ArgumentError, "unsupported signal `SIGUSR10'")
- -> { Signal.trap("SIGUSR10") { } }.should raise_error(ArgumentError, "unsupported signal `SIGUSR10'")
+ -> { Signal.trap("USR10") { } }.should raise_error(ArgumentError, /\Aunsupported signal [`']SIGUSR10'\z/)
+ -> { Signal.trap("SIGUSR10") { } }.should raise_error(ArgumentError, /\Aunsupported signal [`']SIGUSR10'\z/)
end
it "raises ArgumentError when passed signal is not Integer, String or Symbol" do
@@ -245,6 +264,14 @@ describe "Signal.trap" do
end
end
+ %w[SEGV BUS ILL FPE VTALRM].each do |signal|
+ it "raises ArgumentError for SIG#{signal} which is reserved by Ruby" do
+ -> {
+ Signal.trap(signal, -> {})
+ }.should raise_error(ArgumentError, "can't trap reserved signal: SIG#{signal}")
+ end
+ end
+
it "allows to register a handler for all known signals, except reserved signals for which it raises ArgumentError" do
out = ruby_exe(fixture(__FILE__, "trap_all.rb"), args: "2>&1")
out.should == "OK\n"
diff --git a/spec/ruby/core/sizedqueue/append_spec.rb b/spec/ruby/core/sizedqueue/append_spec.rb
index ca79068930..6fffe2f272 100644
--- a/spec/ruby/core/sizedqueue/append_spec.rb
+++ b/spec/ruby/core/sizedqueue/append_spec.rb
@@ -1,6 +1,7 @@
require_relative '../../spec_helper'
require_relative '../../shared/queue/enque'
require_relative '../../shared/sizedqueue/enque'
+require_relative '../../shared/types/rb_num2dbl_fails'
describe "SizedQueue#<<" do
it_behaves_like :queue_enq, :<<, -> { SizedQueue.new(10) }
@@ -9,3 +10,9 @@ end
describe "SizedQueue#<<" do
it_behaves_like :sizedqueue_enq, :<<, -> n { SizedQueue.new(n) }
end
+
+describe "SizedQueue operations with timeout" do
+ ruby_version_is "3.2" do
+ it_behaves_like :rb_num2dbl_fails, nil, -> v { q = SizedQueue.new(1); q.send(:<<, 1, timeout: v) }
+ end
+end
diff --git a/spec/ruby/core/sizedqueue/deq_spec.rb b/spec/ruby/core/sizedqueue/deq_spec.rb
index 5e1bd9f746..985d654bb3 100644
--- a/spec/ruby/core/sizedqueue/deq_spec.rb
+++ b/spec/ruby/core/sizedqueue/deq_spec.rb
@@ -1,6 +1,13 @@
require_relative '../../spec_helper'
require_relative '../../shared/queue/deque'
+require_relative '../../shared/types/rb_num2dbl_fails'
describe "SizedQueue#deq" do
it_behaves_like :queue_deq, :deq, -> { SizedQueue.new(10) }
end
+
+describe "SizedQueue operations with timeout" do
+ ruby_version_is "3.2" do
+ it_behaves_like :rb_num2dbl_fails, nil, -> v { q = SizedQueue.new(10); q.push(1); q.deq(timeout: v) }
+ end
+end
diff --git a/spec/ruby/core/sizedqueue/enq_spec.rb b/spec/ruby/core/sizedqueue/enq_spec.rb
index 3821afac95..619373e46b 100644
--- a/spec/ruby/core/sizedqueue/enq_spec.rb
+++ b/spec/ruby/core/sizedqueue/enq_spec.rb
@@ -1,6 +1,7 @@
require_relative '../../spec_helper'
require_relative '../../shared/queue/enque'
require_relative '../../shared/sizedqueue/enque'
+require_relative '../../shared/types/rb_num2dbl_fails'
describe "SizedQueue#enq" do
it_behaves_like :queue_enq, :enq, -> { SizedQueue.new(10) }
@@ -9,3 +10,9 @@ end
describe "SizedQueue#enq" do
it_behaves_like :sizedqueue_enq, :enq, -> n { SizedQueue.new(n) }
end
+
+describe "SizedQueue operations with timeout" do
+ ruby_version_is "3.2" do
+ it_behaves_like :rb_num2dbl_fails, nil, -> v { q = SizedQueue.new(1); q.enq(1, timeout: v) }
+ end
+end
diff --git a/spec/ruby/core/sizedqueue/pop_spec.rb b/spec/ruby/core/sizedqueue/pop_spec.rb
index a0cf6f509c..5e7cfea8fb 100644
--- a/spec/ruby/core/sizedqueue/pop_spec.rb
+++ b/spec/ruby/core/sizedqueue/pop_spec.rb
@@ -1,6 +1,13 @@
require_relative '../../spec_helper'
require_relative '../../shared/queue/deque'
+require_relative '../../shared/types/rb_num2dbl_fails'
describe "SizedQueue#pop" do
it_behaves_like :queue_deq, :pop, -> { SizedQueue.new(10) }
end
+
+describe "SizedQueue operations with timeout" do
+ ruby_version_is "3.2" do
+ it_behaves_like :rb_num2dbl_fails, nil, -> v { q = SizedQueue.new(10); q.push(1); q.pop(timeout: v) }
+ end
+end
diff --git a/spec/ruby/core/sizedqueue/push_spec.rb b/spec/ruby/core/sizedqueue/push_spec.rb
index bba9be9e3f..ce61e89b53 100644
--- a/spec/ruby/core/sizedqueue/push_spec.rb
+++ b/spec/ruby/core/sizedqueue/push_spec.rb
@@ -1,6 +1,7 @@
require_relative '../../spec_helper'
require_relative '../../shared/queue/enque'
require_relative '../../shared/sizedqueue/enque'
+require_relative '../../shared/types/rb_num2dbl_fails'
describe "SizedQueue#push" do
it_behaves_like :queue_enq, :push, -> { SizedQueue.new(10) }
@@ -9,3 +10,9 @@ end
describe "SizedQueue#push" do
it_behaves_like :sizedqueue_enq, :push, -> n { SizedQueue.new(n) }
end
+
+describe "SizedQueue operations with timeout" do
+ ruby_version_is "3.2" do
+ it_behaves_like :rb_num2dbl_fails, nil, -> v { q = SizedQueue.new(1); q.push(1, timeout: v) }
+ end
+end
diff --git a/spec/ruby/core/sizedqueue/shift_spec.rb b/spec/ruby/core/sizedqueue/shift_spec.rb
index 5138e68258..3220801f3a 100644
--- a/spec/ruby/core/sizedqueue/shift_spec.rb
+++ b/spec/ruby/core/sizedqueue/shift_spec.rb
@@ -1,6 +1,13 @@
require_relative '../../spec_helper'
require_relative '../../shared/queue/deque'
+require_relative '../../shared/types/rb_num2dbl_fails'
describe "SizedQueue#shift" do
it_behaves_like :queue_deq, :shift, -> { SizedQueue.new(10) }
end
+
+describe "SizedQueue operations with timeout" do
+ ruby_version_is "3.2" do
+ it_behaves_like :rb_num2dbl_fails, nil, -> v { q = SizedQueue.new(10); q.push(1); q.shift(timeout: v) }
+ end
+end
diff --git a/spec/ruby/core/string/ascii_only_spec.rb b/spec/ruby/core/string/ascii_only_spec.rb
index c7e02fd874..88a0559cfd 100644
--- a/spec/ruby/core/string/ascii_only_spec.rb
+++ b/spec/ruby/core/string/ascii_only_spec.rb
@@ -7,12 +7,12 @@ describe "String#ascii_only?" do
it "returns true if the encoding is UTF-8" do
[ ["hello", true],
["hello".encode('UTF-8'), true],
- ["hello".force_encoding('UTF-8'), true],
+ ["hello".dup.force_encoding('UTF-8'), true],
].should be_computed_by(:ascii_only?)
end
it "returns true if the encoding is US-ASCII" do
- "hello".force_encoding(Encoding::US_ASCII).ascii_only?.should be_true
+ "hello".dup.force_encoding(Encoding::US_ASCII).ascii_only?.should be_true
"hello".encode(Encoding::US_ASCII).ascii_only?.should be_true
end
@@ -34,13 +34,13 @@ describe "String#ascii_only?" do
[ ["\u{6666}", false],
["hello, \u{6666}", false],
["\u{6666}".encode('UTF-8'), false],
- ["\u{6666}".force_encoding('UTF-8'), false],
+ ["\u{6666}".dup.force_encoding('UTF-8'), false],
].should be_computed_by(:ascii_only?)
end
it "returns false if the encoding is US-ASCII" do
- [ ["\u{6666}".force_encoding(Encoding::US_ASCII), false],
- ["hello, \u{6666}".force_encoding(Encoding::US_ASCII), false],
+ [ ["\u{6666}".dup.force_encoding(Encoding::US_ASCII), false],
+ ["hello, \u{6666}".dup.force_encoding(Encoding::US_ASCII), false],
].should be_computed_by(:ascii_only?)
end
end
@@ -51,17 +51,16 @@ describe "String#ascii_only?" do
end
it "returns false for the empty String with a non-ASCII-compatible encoding" do
- "".force_encoding('UTF-16LE').ascii_only?.should be_false
+ "".dup.force_encoding('UTF-16LE').ascii_only?.should be_false
"".encode('UTF-16BE').ascii_only?.should be_false
end
it "returns false for a non-empty String with non-ASCII-compatible encoding" do
- "\x78\x00".force_encoding("UTF-16LE").ascii_only?.should be_false
+ "\x78\x00".dup.force_encoding("UTF-16LE").ascii_only?.should be_false
end
it "returns false when interpolating non ascii strings" do
- base = "EU currency is"
- base.force_encoding(Encoding::US_ASCII)
+ base = "EU currency is".dup.force_encoding(Encoding::US_ASCII)
euro = "\u20AC"
interp = "#{base} #{euro}"
euro.ascii_only?.should be_false
@@ -70,14 +69,14 @@ describe "String#ascii_only?" do
end
it "returns false after appending non ASCII characters to an empty String" do
- ("" << "λ").ascii_only?.should be_false
+ ("".dup << "λ").ascii_only?.should be_false
end
it "returns false when concatenating an ASCII and non-ASCII String" do
- "".concat("λ").ascii_only?.should be_false
+ "".dup.concat("λ").ascii_only?.should be_false
end
it "returns false when replacing an ASCII String with a non-ASCII String" do
- "".replace("λ").ascii_only?.should be_false
+ "".dup.replace("λ").ascii_only?.should be_false
end
end
diff --git a/spec/ruby/core/string/b_spec.rb b/spec/ruby/core/string/b_spec.rb
index 37c7994700..4b1fafff11 100644
--- a/spec/ruby/core/string/b_spec.rb
+++ b/spec/ruby/core/string/b_spec.rb
@@ -1,4 +1,5 @@
# -*- encoding: utf-8 -*-
+# frozen_string_literal: false
require_relative '../../spec_helper'
describe "String#b" do
diff --git a/spec/ruby/core/string/byteindex_spec.rb b/spec/ruby/core/string/byteindex_spec.rb
new file mode 100644
index 0000000000..47c7be1029
--- /dev/null
+++ b/spec/ruby/core/string/byteindex_spec.rb
@@ -0,0 +1,304 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/byte_index_common.rb'
+
+describe "String#byteindex" do
+ ruby_version_is "3.2" do
+ it "calls #to_str to convert the first argument" do
+ char = mock("string index char")
+ char.should_receive(:to_str).and_return("b")
+ "abc".byteindex(char).should == 1
+ end
+
+ it "calls #to_int to convert the second argument" do
+ offset = mock("string index offset")
+ offset.should_receive(:to_int).and_return(1)
+ "abc".byteindex("c", offset).should == 2
+ end
+
+ it "does not raise IndexError when byte offset is correct or on string boundary" do
+ "ã‚".byteindex("").should == 0
+ "ã‚".byteindex("", 0).should == 0
+ "ã‚".byteindex("", 3).should == 3
+ end
+
+ it_behaves_like :byte_index_common, :byteindex
+ end
+end
+
+describe "String#byteindex with String" do
+ ruby_version_is "3.2" do
+ it "behaves the same as String#byteindex(char) for one-character strings" do
+ "blablabla hello cruel world...!".split("").uniq.each do |str|
+ chr = str[0]
+ str.byteindex(str).should == str.byteindex(chr)
+
+ 0.upto(str.size + 1) do |start|
+ str.byteindex(str, start).should == str.byteindex(chr, start)
+ end
+
+ (-str.size - 1).upto(-1) do |start|
+ str.byteindex(str, start).should == str.byteindex(chr, start)
+ end
+ end
+ end
+
+ it "returns the byteindex of the first occurrence of the given substring" do
+ "blablabla".byteindex("").should == 0
+ "blablabla".byteindex("b").should == 0
+ "blablabla".byteindex("bla").should == 0
+ "blablabla".byteindex("blabla").should == 0
+ "blablabla".byteindex("blablabla").should == 0
+
+ "blablabla".byteindex("l").should == 1
+ "blablabla".byteindex("la").should == 1
+ "blablabla".byteindex("labla").should == 1
+ "blablabla".byteindex("lablabla").should == 1
+
+ "blablabla".byteindex("a").should == 2
+ "blablabla".byteindex("abla").should == 2
+ "blablabla".byteindex("ablabla").should == 2
+ end
+
+ it "treats the offset as a byteindex" do
+ "aaaaa".byteindex("a", 0).should == 0
+ "aaaaa".byteindex("a", 2).should == 2
+ "aaaaa".byteindex("a", 4).should == 4
+ end
+
+ it "ignores string subclasses" do
+ "blablabla".byteindex(StringSpecs::MyString.new("bla")).should == 0
+ StringSpecs::MyString.new("blablabla").byteindex("bla").should == 0
+ StringSpecs::MyString.new("blablabla").byteindex(StringSpecs::MyString.new("bla")).should == 0
+ end
+
+ it "starts the search at the given offset" do
+ "blablabla".byteindex("bl", 0).should == 0
+ "blablabla".byteindex("bl", 1).should == 3
+ "blablabla".byteindex("bl", 2).should == 3
+ "blablabla".byteindex("bl", 3).should == 3
+
+ "blablabla".byteindex("bla", 0).should == 0
+ "blablabla".byteindex("bla", 1).should == 3
+ "blablabla".byteindex("bla", 2).should == 3
+ "blablabla".byteindex("bla", 3).should == 3
+
+ "blablabla".byteindex("blab", 0).should == 0
+ "blablabla".byteindex("blab", 1).should == 3
+ "blablabla".byteindex("blab", 2).should == 3
+ "blablabla".byteindex("blab", 3).should == 3
+
+ "blablabla".byteindex("la", 1).should == 1
+ "blablabla".byteindex("la", 2).should == 4
+ "blablabla".byteindex("la", 3).should == 4
+ "blablabla".byteindex("la", 4).should == 4
+
+ "blablabla".byteindex("lab", 1).should == 1
+ "blablabla".byteindex("lab", 2).should == 4
+ "blablabla".byteindex("lab", 3).should == 4
+ "blablabla".byteindex("lab", 4).should == 4
+
+ "blablabla".byteindex("ab", 2).should == 2
+ "blablabla".byteindex("ab", 3).should == 5
+ "blablabla".byteindex("ab", 4).should == 5
+ "blablabla".byteindex("ab", 5).should == 5
+
+ "blablabla".byteindex("", 0).should == 0
+ "blablabla".byteindex("", 1).should == 1
+ "blablabla".byteindex("", 2).should == 2
+ "blablabla".byteindex("", 7).should == 7
+ "blablabla".byteindex("", 8).should == 8
+ "blablabla".byteindex("", 9).should == 9
+ end
+
+ it "starts the search at offset + self.length if offset is negative" do
+ str = "blablabla"
+
+ ["bl", "bla", "blab", "la", "lab", "ab", ""].each do |needle|
+ (-str.length .. -1).each do |offset|
+ str.byteindex(needle, offset).should ==
+ str.byteindex(needle, offset + str.length)
+ end
+ end
+ end
+
+ it "returns nil if the substring isn't found" do
+ "blablabla".byteindex("B").should == nil
+ "blablabla".byteindex("z").should == nil
+ "blablabla".byteindex("BLA").should == nil
+ "blablabla".byteindex("blablablabla").should == nil
+ "blablabla".byteindex("", 10).should == nil
+
+ "hello".byteindex("he", 1).should == nil
+ "hello".byteindex("he", 2).should == nil
+ "I’ve got a multibyte character.\n".byteindex("\n\n").should == nil
+ end
+
+ it "returns the character byteindex of a multibyte character" do
+ "ã‚ã‚ŠãŒã¨ã†".byteindex("ãŒ").should == 6
+ end
+
+ it "returns the character byteindex after offset" do
+ "ã‚ã‚Œã‚ã‚Œ".byteindex("ã‚", 3).should == 6
+ "ã‚ã‚ŠãŒã¨ã†ã‚ã‚ŠãŒã¨ã†".byteindex("ãŒ", 9).should == 21
+ end
+
+ it "returns the character byteindex after a partial first match" do
+ "</</h".byteindex("</h").should == 2
+ end
+
+ it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
+ char = "れ".encode Encoding::EUC_JP
+ -> do
+ "ã‚ã‚Œ".byteindex(char)
+ end.should raise_error(Encoding::CompatibilityError)
+ end
+
+ it "handles a substring in a superset encoding" do
+ 'abc'.dup.force_encoding(Encoding::US_ASCII).byteindex('é').should == nil
+ end
+
+ it "handles a substring in a subset encoding" do
+ 'été'.byteindex('t'.dup.force_encoding(Encoding::US_ASCII)).should == 2
+ end
+ end
+end
+
+describe "String#byteindex with Regexp" do
+ ruby_version_is "3.2" do
+ it "behaves the same as String#byteindex(string) for escaped string regexps" do
+ ["blablabla", "hello cruel world...!"].each do |str|
+ ["", "b", "bla", "lab", "o c", "d."].each do |needle|
+ regexp = Regexp.new(Regexp.escape(needle))
+ str.byteindex(regexp).should == str.byteindex(needle)
+
+ 0.upto(str.size + 1) do |start|
+ str.byteindex(regexp, start).should == str.byteindex(needle, start)
+ end
+
+ (-str.size - 1).upto(-1) do |start|
+ str.byteindex(regexp, start).should == str.byteindex(needle, start)
+ end
+ end
+ end
+ end
+
+ it "returns the byteindex of the first match of regexp" do
+ "blablabla".byteindex(/bla/).should == 0
+ "blablabla".byteindex(/BLA/i).should == 0
+
+ "blablabla".byteindex(/.{0}/).should == 0
+ "blablabla".byteindex(/.{6}/).should == 0
+ "blablabla".byteindex(/.{9}/).should == 0
+
+ "blablabla".byteindex(/.*/).should == 0
+ "blablabla".byteindex(/.+/).should == 0
+
+ "blablabla".byteindex(/lab|b/).should == 0
+
+ not_supported_on :opal do
+ "blablabla".byteindex(/\A/).should == 0
+ "blablabla".byteindex(/\Z/).should == 9
+ "blablabla".byteindex(/\z/).should == 9
+ "blablabla\n".byteindex(/\Z/).should == 9
+ "blablabla\n".byteindex(/\z/).should == 10
+ end
+
+ "blablabla".byteindex(/^/).should == 0
+ "\nblablabla".byteindex(/^/).should == 0
+ "b\nablabla".byteindex(/$/).should == 1
+ "bl\nablabla".byteindex(/$/).should == 2
+
+ "blablabla".byteindex(/.l./).should == 0
+ end
+
+ it "starts the search at the given offset" do
+ "blablabla".byteindex(/.{0}/, 5).should == 5
+ "blablabla".byteindex(/.{1}/, 5).should == 5
+ "blablabla".byteindex(/.{2}/, 5).should == 5
+ "blablabla".byteindex(/.{3}/, 5).should == 5
+ "blablabla".byteindex(/.{4}/, 5).should == 5
+
+ "blablabla".byteindex(/.{0}/, 3).should == 3
+ "blablabla".byteindex(/.{1}/, 3).should == 3
+ "blablabla".byteindex(/.{2}/, 3).should == 3
+ "blablabla".byteindex(/.{5}/, 3).should == 3
+ "blablabla".byteindex(/.{6}/, 3).should == 3
+
+ "blablabla".byteindex(/.l./, 0).should == 0
+ "blablabla".byteindex(/.l./, 1).should == 3
+ "blablabla".byteindex(/.l./, 2).should == 3
+ "blablabla".byteindex(/.l./, 3).should == 3
+
+ "xblaxbla".byteindex(/x./, 0).should == 0
+ "xblaxbla".byteindex(/x./, 1).should == 4
+ "xblaxbla".byteindex(/x./, 2).should == 4
+
+ not_supported_on :opal do
+ "blablabla\n".byteindex(/\Z/, 9).should == 9
+ end
+ end
+
+ it "starts the search at offset + self.length if offset is negative" do
+ str = "blablabla"
+
+ ["bl", "bla", "blab", "la", "lab", "ab", ""].each do |needle|
+ (-str.length .. -1).each do |offset|
+ str.byteindex(needle, offset).should ==
+ str.byteindex(needle, offset + str.length)
+ end
+ end
+ end
+
+ it "returns nil if the substring isn't found" do
+ "blablabla".byteindex(/BLA/).should == nil
+
+ "blablabla".byteindex(/.{10}/).should == nil
+ "blaxbla".byteindex(/.x/, 3).should == nil
+ "blaxbla".byteindex(/..x/, 2).should == nil
+ end
+
+ it "returns nil if the Regexp matches the empty string and the offset is out of range" do
+ "ruby".byteindex(//, 12).should be_nil
+ end
+
+ it "supports \\G which matches at the given start offset" do
+ "helloYOU.".byteindex(/\GYOU/, 5).should == 5
+ "helloYOU.".byteindex(/\GYOU/).should == nil
+
+ re = /\G.+YOU/
+ # The # marks where \G will match.
+ [
+ ["#hi!YOUall.", 0],
+ ["h#i!YOUall.", 1],
+ ["hi#!YOUall.", 2],
+ ["hi!#YOUall.", nil]
+ ].each do |spec|
+
+ start = spec[0].byteindex("#")
+ str = spec[0].delete("#")
+
+ str.byteindex(re, start).should == spec[1]
+ end
+ end
+
+ it "converts start_offset to an integer via to_int" do
+ obj = mock('1')
+ obj.should_receive(:to_int).and_return(1)
+ "RWOARW".byteindex(/R./, obj).should == 4
+ end
+
+ it "returns the character byteindex of a multibyte character" do
+ "ã‚ã‚ŠãŒã¨ã†".byteindex(/ãŒ/).should == 6
+ end
+
+ it "returns the character byteindex after offset" do
+ "ã‚ã‚Œã‚ã‚Œ".byteindex(/ã‚/, 3).should == 6
+ end
+
+ it "treats the offset as a byteindex" do
+ "ã‚ã‚Œã‚ã‚ã‚Œ".byteindex(/ã‚/, 6).should == 6
+ end
+ end
+end
diff --git a/spec/ruby/core/string/byterindex_spec.rb b/spec/ruby/core/string/byterindex_spec.rb
new file mode 100644
index 0000000000..150f709b90
--- /dev/null
+++ b/spec/ruby/core/string/byterindex_spec.rb
@@ -0,0 +1,359 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/byte_index_common.rb'
+
+describe "String#byterindex with object" do
+ ruby_version_is "3.2" do
+ it "tries to convert obj to a string via to_str" do
+ obj = mock('lo')
+ def obj.to_str() "lo" end
+ "hello".byterindex(obj).should == "hello".byterindex("lo")
+
+ obj = mock('o')
+ def obj.respond_to?(arg, *) true end
+ def obj.method_missing(*args) "o" end
+ "hello".byterindex(obj).should == "hello".byterindex("o")
+ end
+
+ it "calls #to_int to convert the second argument" do
+ offset = mock("string index offset")
+ offset.should_receive(:to_int).and_return(3)
+ "abc".byterindex("c", offset).should == 2
+ end
+
+ it "does not raise IndexError when byte offset is correct or on string boundary" do
+ "ã‚".byterindex("", 0).should == 0
+ "ã‚".byterindex("", 3).should == 3
+ "ã‚".byterindex("").should == 3
+ end
+
+ it_behaves_like :byte_index_common, :byterindex
+ end
+end
+
+describe "String#byterindex with String" do
+ ruby_version_is "3.2" do
+ it "behaves the same as String#byterindex(char) for one-character strings" do
+ "blablabla hello cruel world...!".split("").uniq.each do |str|
+ chr = str[0]
+ str.byterindex(str).should == str.byterindex(chr)
+
+ 0.upto(str.size + 1) do |start|
+ str.byterindex(str, start).should == str.byterindex(chr, start)
+ end
+
+ (-str.size - 1).upto(-1) do |start|
+ str.byterindex(str, start).should == str.byterindex(chr, start)
+ end
+ end
+ end
+
+ it "behaves the same as String#byterindex(?char) for one-character strings" do
+ "blablabla hello cruel world...!".split("").uniq.each do |str|
+ chr = str[0] =~ / / ? str[0] : eval("?#{str[0]}")
+ str.byterindex(str).should == str.byterindex(chr)
+
+ 0.upto(str.size + 1) do |start|
+ str.byterindex(str, start).should == str.byterindex(chr, start)
+ end
+
+ (-str.size - 1).upto(-1) do |start|
+ str.byterindex(str, start).should == str.byterindex(chr, start)
+ end
+ end
+ end
+
+ it "returns the index of the last occurrence of the given substring" do
+ "blablabla".byterindex("").should == 9
+ "blablabla".byterindex("a").should == 8
+ "blablabla".byterindex("la").should == 7
+ "blablabla".byterindex("bla").should == 6
+ "blablabla".byterindex("abla").should == 5
+ "blablabla".byterindex("labla").should == 4
+ "blablabla".byterindex("blabla").should == 3
+ "blablabla".byterindex("ablabla").should == 2
+ "blablabla".byterindex("lablabla").should == 1
+ "blablabla".byterindex("blablabla").should == 0
+
+ "blablabla".byterindex("l").should == 7
+ "blablabla".byterindex("bl").should == 6
+ "blablabla".byterindex("abl").should == 5
+ "blablabla".byterindex("labl").should == 4
+ "blablabla".byterindex("blabl").should == 3
+ "blablabla".byterindex("ablabl").should == 2
+ "blablabla".byterindex("lablabl").should == 1
+ "blablabla".byterindex("blablabl").should == 0
+
+ "blablabla".byterindex("b").should == 6
+ "blablabla".byterindex("ab").should == 5
+ "blablabla".byterindex("lab").should == 4
+ "blablabla".byterindex("blab").should == 3
+ "blablabla".byterindex("ablab").should == 2
+ "blablabla".byterindex("lablab").should == 1
+ "blablabla".byterindex("blablab").should == 0
+ end
+
+ it "ignores string subclasses" do
+ "blablabla".byterindex(StringSpecs::MyString.new("bla")).should == 6
+ StringSpecs::MyString.new("blablabla").byterindex("bla").should == 6
+ StringSpecs::MyString.new("blablabla").byterindex(StringSpecs::MyString.new("bla")).should == 6
+ end
+
+ it "starts the search at the given offset" do
+ "blablabla".byterindex("bl", 0).should == 0
+ "blablabla".byterindex("bl", 1).should == 0
+ "blablabla".byterindex("bl", 2).should == 0
+ "blablabla".byterindex("bl", 3).should == 3
+
+ "blablabla".byterindex("bla", 0).should == 0
+ "blablabla".byterindex("bla", 1).should == 0
+ "blablabla".byterindex("bla", 2).should == 0
+ "blablabla".byterindex("bla", 3).should == 3
+
+ "blablabla".byterindex("blab", 0).should == 0
+ "blablabla".byterindex("blab", 1).should == 0
+ "blablabla".byterindex("blab", 2).should == 0
+ "blablabla".byterindex("blab", 3).should == 3
+ "blablabla".byterindex("blab", 6).should == 3
+ "blablablax".byterindex("blab", 6).should == 3
+
+ "blablabla".byterindex("la", 1).should == 1
+ "blablabla".byterindex("la", 2).should == 1
+ "blablabla".byterindex("la", 3).should == 1
+ "blablabla".byterindex("la", 4).should == 4
+
+ "blablabla".byterindex("lab", 1).should == 1
+ "blablabla".byterindex("lab", 2).should == 1
+ "blablabla".byterindex("lab", 3).should == 1
+ "blablabla".byterindex("lab", 4).should == 4
+
+ "blablabla".byterindex("ab", 2).should == 2
+ "blablabla".byterindex("ab", 3).should == 2
+ "blablabla".byterindex("ab", 4).should == 2
+ "blablabla".byterindex("ab", 5).should == 5
+
+ "blablabla".byterindex("", 0).should == 0
+ "blablabla".byterindex("", 1).should == 1
+ "blablabla".byterindex("", 2).should == 2
+ "blablabla".byterindex("", 7).should == 7
+ "blablabla".byterindex("", 8).should == 8
+ "blablabla".byterindex("", 9).should == 9
+ "blablabla".byterindex("", 10).should == 9
+ end
+
+ it "starts the search at offset + self.length if offset is negative" do
+ str = "blablabla"
+
+ ["bl", "bla", "blab", "la", "lab", "ab", ""].each do |needle|
+ (-str.length .. -1).each do |offset|
+ str.byterindex(needle, offset).should ==
+ str.byterindex(needle, offset + str.length)
+ end
+ end
+ end
+
+ it "returns nil if the substring isn't found" do
+ "blablabla".byterindex("B").should == nil
+ "blablabla".byterindex("z").should == nil
+ "blablabla".byterindex("BLA").should == nil
+ "blablabla".byterindex("blablablabla").should == nil
+
+ "hello".byterindex("lo", 0).should == nil
+ "hello".byterindex("lo", 1).should == nil
+ "hello".byterindex("lo", 2).should == nil
+
+ "hello".byterindex("llo", 0).should == nil
+ "hello".byterindex("llo", 1).should == nil
+
+ "hello".byterindex("el", 0).should == nil
+ "hello".byterindex("ello", 0).should == nil
+
+ "hello".byterindex("", -6).should == nil
+ "hello".byterindex("", -7).should == nil
+
+ "hello".byterindex("h", -6).should == nil
+ end
+
+ it "tries to convert start_offset to an integer via to_int" do
+ obj = mock('5')
+ def obj.to_int() 5 end
+ "str".byterindex("st", obj).should == 0
+
+ obj = mock('5')
+ def obj.respond_to?(arg, *) true end
+ def obj.method_missing(*args) 5 end
+ "str".byterindex("st", obj).should == 0
+ end
+
+ it "raises a TypeError when given offset is nil" do
+ -> { "str".byterindex("st", nil) }.should raise_error(TypeError)
+ end
+
+ it "handles a substring in a superset encoding" do
+ 'abc'.dup.force_encoding(Encoding::US_ASCII).byterindex('é').should == nil
+ end
+
+ it "handles a substring in a subset encoding" do
+ 'été'.byterindex('t'.dup.force_encoding(Encoding::US_ASCII)).should == 2
+ end
+ end
+end
+
+describe "String#byterindex with Regexp" do
+ ruby_version_is "3.2" do
+ it "behaves the same as String#byterindex(string) for escaped string regexps" do
+ ["blablabla", "hello cruel world...!"].each do |str|
+ ["", "b", "bla", "lab", "o c", "d."].each do |needle|
+ regexp = Regexp.new(Regexp.escape(needle))
+ str.byterindex(regexp).should == str.byterindex(needle)
+
+ 0.upto(str.size + 1) do |start|
+ str.byterindex(regexp, start).should == str.byterindex(needle, start)
+ end
+
+ (-str.size - 1).upto(-1) do |start|
+ str.byterindex(regexp, start).should == str.byterindex(needle, start)
+ end
+ end
+ end
+ end
+
+ it "returns the index of the first match from the end of string of regexp" do
+ "blablabla".byterindex(/bla/).should == 6
+ "blablabla".byterindex(/BLA/i).should == 6
+
+ "blablabla".byterindex(/.{0}/).should == 9
+ "blablabla".byterindex(/.{1}/).should == 8
+ "blablabla".byterindex(/.{2}/).should == 7
+ "blablabla".byterindex(/.{6}/).should == 3
+ "blablabla".byterindex(/.{9}/).should == 0
+
+ "blablabla".byterindex(/.*/).should == 9
+ "blablabla".byterindex(/.+/).should == 8
+
+ "blablabla".byterindex(/bla|a/).should == 8
+
+ not_supported_on :opal do
+ "blablabla".byterindex(/\A/).should == 0
+ "blablabla".byterindex(/\Z/).should == 9
+ "blablabla".byterindex(/\z/).should == 9
+ "blablabla\n".byterindex(/\Z/).should == 10
+ "blablabla\n".byterindex(/\z/).should == 10
+ end
+
+ "blablabla".byterindex(/^/).should == 0
+ not_supported_on :opal do
+ "\nblablabla".byterindex(/^/).should == 1
+ "b\nlablabla".byterindex(/^/).should == 2
+ end
+ "blablabla".byterindex(/$/).should == 9
+
+ "blablabla".byterindex(/.l./).should == 6
+ end
+
+ it "starts the search at the given offset" do
+ "blablabla".byterindex(/.{0}/, 5).should == 5
+ "blablabla".byterindex(/.{1}/, 5).should == 5
+ "blablabla".byterindex(/.{2}/, 5).should == 5
+ "blablabla".byterindex(/.{3}/, 5).should == 5
+ "blablabla".byterindex(/.{4}/, 5).should == 5
+
+ "blablabla".byterindex(/.{0}/, 3).should == 3
+ "blablabla".byterindex(/.{1}/, 3).should == 3
+ "blablabla".byterindex(/.{2}/, 3).should == 3
+ "blablabla".byterindex(/.{5}/, 3).should == 3
+ "blablabla".byterindex(/.{6}/, 3).should == 3
+
+ "blablabla".byterindex(/.l./, 0).should == 0
+ "blablabla".byterindex(/.l./, 1).should == 0
+ "blablabla".byterindex(/.l./, 2).should == 0
+ "blablabla".byterindex(/.l./, 3).should == 3
+
+ "blablablax".byterindex(/.x/, 10).should == 8
+ "blablablax".byterindex(/.x/, 9).should == 8
+ "blablablax".byterindex(/.x/, 8).should == 8
+
+ "blablablax".byterindex(/..x/, 10).should == 7
+ "blablablax".byterindex(/..x/, 9).should == 7
+ "blablablax".byterindex(/..x/, 8).should == 7
+ "blablablax".byterindex(/..x/, 7).should == 7
+
+ not_supported_on :opal do
+ "blablabla\n".byterindex(/\Z/, 9).should == 9
+ end
+ end
+
+ it "starts the search at offset + self.length if offset is negative" do
+ str = "blablabla"
+
+ ["bl", "bla", "blab", "la", "lab", "ab", ""].each do |needle|
+ (-str.length .. -1).each do |offset|
+ str.byterindex(needle, offset).should ==
+ str.byterindex(needle, offset + str.length)
+ end
+ end
+ end
+
+ it "returns nil if the substring isn't found" do
+ "blablabla".byterindex(/BLA/).should == nil
+ "blablabla".byterindex(/.{10}/).should == nil
+ "blablablax".byterindex(/.x/, 7).should == nil
+ "blablablax".byterindex(/..x/, 6).should == nil
+
+ not_supported_on :opal do
+ "blablabla".byterindex(/\Z/, 5).should == nil
+ "blablabla".byterindex(/\z/, 5).should == nil
+ "blablabla\n".byterindex(/\z/, 9).should == nil
+ end
+ end
+
+ not_supported_on :opal do
+ it "supports \\G which matches at the given start offset" do
+ "helloYOU.".byterindex(/YOU\G/, 8).should == 5
+ "helloYOU.".byterindex(/YOU\G/).should == nil
+
+ idx = "helloYOUall!".index("YOU")
+ re = /YOU.+\G.+/
+ # The # marks where \G will match.
+ [
+ ["helloYOU#all.", nil],
+ ["helloYOUa#ll.", idx],
+ ["helloYOUal#l.", idx],
+ ["helloYOUall#.", idx],
+ ["helloYOUall.#", nil]
+ ].each do |i|
+ start = i[0].index("#")
+ str = i[0].delete("#")
+
+ str.byterindex(re, start).should == i[1]
+ end
+ end
+ end
+
+ it "tries to convert start_offset to an integer" do
+ obj = mock('5')
+ def obj.to_int() 5 end
+ "str".byterindex(/../, obj).should == 1
+
+ obj = mock('5')
+ def obj.respond_to?(arg, *) true end
+ def obj.method_missing(*args); 5; end
+ "str".byterindex(/../, obj).should == 1
+ end
+
+ it "raises a TypeError when given offset is nil" do
+ -> { "str".byterindex(/../, nil) }.should raise_error(TypeError)
+ end
+
+ it "returns the reverse byte index of a multibyte character" do
+ "ã‚ã‚ŠãŒã‚ŠãŒã¨ã†".byterindex("ãŒ").should == 12
+ "ã‚ã‚ŠãŒã‚ŠãŒã¨ã†".byterindex(/ãŒ/).should == 12
+ end
+
+ it "returns the character index before the finish" do
+ "ã‚ã‚ŠãŒã‚ŠãŒã¨ã†".byterindex("ãŒ", 9).should == 6
+ "ã‚ã‚ŠãŒã‚ŠãŒã¨ã†".byterindex(/ãŒ/, 9).should == 6
+ end
+ end
+end
diff --git a/spec/ruby/core/string/bytes_spec.rb b/spec/ruby/core/string/bytes_spec.rb
index 859b346550..02151eebbc 100644
--- a/spec/ruby/core/string/bytes_spec.rb
+++ b/spec/ruby/core/string/bytes_spec.rb
@@ -50,6 +50,6 @@ describe "String#bytes" do
end
it "is unaffected by #force_encoding" do
- @utf8.force_encoding('ASCII').bytes.to_a.should == @utf8.bytes.to_a
+ @utf8.dup.force_encoding('ASCII').bytes.to_a.should == @utf8.bytes.to_a
end
end
diff --git a/spec/ruby/core/string/bytesize_spec.rb b/spec/ruby/core/string/bytesize_spec.rb
index a31f3ae671..2bbefc0820 100644
--- a/spec/ruby/core/string/bytesize_spec.rb
+++ b/spec/ruby/core/string/bytesize_spec.rb
@@ -13,21 +13,21 @@ describe "String#bytesize" do
end
it "works with pseudo-ASCII strings containing single UTF-8 characters" do
- "\u{6666}".force_encoding('ASCII').bytesize.should == 3
+ "\u{6666}".dup.force_encoding('ASCII').bytesize.should == 3
end
it "works with strings containing UTF-8 characters" do
- "c \u{6666}".force_encoding('UTF-8').bytesize.should == 5
+ "c \u{6666}".dup.force_encoding('UTF-8').bytesize.should == 5
"c \u{6666}".bytesize.should == 5
end
it "works with pseudo-ASCII strings containing UTF-8 characters" do
- "c \u{6666}".force_encoding('ASCII').bytesize.should == 5
+ "c \u{6666}".dup.force_encoding('ASCII').bytesize.should == 5
end
it "returns 0 for the empty string" do
"".bytesize.should == 0
- "".force_encoding('ASCII').bytesize.should == 0
- "".force_encoding('UTF-8').bytesize.should == 0
+ "".dup.force_encoding('ASCII').bytesize.should == 0
+ "".dup.force_encoding('UTF-8').bytesize.should == 0
end
end
diff --git a/spec/ruby/core/string/byteslice_spec.rb b/spec/ruby/core/string/byteslice_spec.rb
index 312229523d..5b1027f4a5 100644
--- a/spec/ruby/core/string/byteslice_spec.rb
+++ b/spec/ruby/core/string/byteslice_spec.rb
@@ -19,10 +19,10 @@ end
describe "String#byteslice on on non ASCII strings" do
it "returns byteslice of unicode strings" do
- "\u3042".byteslice(1).should == "\x81".force_encoding("UTF-8")
- "\u3042".byteslice(1, 2).should == "\x81\x82".force_encoding("UTF-8")
- "\u3042".byteslice(1..2).should == "\x81\x82".force_encoding("UTF-8")
- "\u3042".byteslice(-1).should == "\x82".force_encoding("UTF-8")
+ "\u3042".byteslice(1).should == "\x81".dup.force_encoding("UTF-8")
+ "\u3042".byteslice(1, 2).should == "\x81\x82".dup.force_encoding("UTF-8")
+ "\u3042".byteslice(1..2).should == "\x81\x82".dup.force_encoding("UTF-8")
+ "\u3042".byteslice(-1).should == "\x82".dup.force_encoding("UTF-8")
end
it "returns a String in the same encoding as self" do
diff --git a/spec/ruby/core/string/bytesplice_spec.rb b/spec/ruby/core/string/bytesplice_spec.rb
index f13024a79b..967edcba29 100644
--- a/spec/ruby/core/string/bytesplice_spec.rb
+++ b/spec/ruby/core/string/bytesplice_spec.rb
@@ -1,4 +1,5 @@
# -*- encoding: utf-8 -*-
+# frozen_string_literal: false
require_relative '../../spec_helper'
describe "String#bytesplice" do
diff --git a/spec/ruby/core/string/capitalize_spec.rb b/spec/ruby/core/string/capitalize_spec.rb
index 3f85cf5ae4..5e59b656c5 100644
--- a/spec/ruby/core/string/capitalize_spec.rb
+++ b/spec/ruby/core/string/capitalize_spec.rb
@@ -78,18 +78,9 @@ describe "String#capitalize" do
-> { "abc".capitalize(:invalid_option) }.should raise_error(ArgumentError)
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances when called on a subclass" do
- StringSpecs::MyString.new("hello").capitalize.should be_an_instance_of(StringSpecs::MyString)
- StringSpecs::MyString.new("Hello").capitalize.should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns String instances when called on a subclass" do
- StringSpecs::MyString.new("hello").capitalize.should be_an_instance_of(String)
- StringSpecs::MyString.new("Hello").capitalize.should be_an_instance_of(String)
- end
+ it "returns String instances when called on a subclass" do
+ StringSpecs::MyString.new("hello").capitalize.should be_an_instance_of(String)
+ StringSpecs::MyString.new("Hello").capitalize.should be_an_instance_of(String)
end
it "returns a String in the same encoding as self" do
@@ -99,7 +90,7 @@ end
describe "String#capitalize!" do
it "capitalizes self in place" do
- a = "hello"
+ a = +"hello"
a.capitalize!.should equal(a)
a.should == "Hello"
end
@@ -112,13 +103,13 @@ describe "String#capitalize!" do
describe "full Unicode case mapping" do
it "modifies self in place for all of Unicode with no option" do
- a = "äöÜ"
+ a = +"äöÜ"
a.capitalize!
a.should == "Äöü"
end
it "only capitalizes the first resulting character when upcasing a character produces a multi-character sequence" do
- a = "ß"
+ a = +"ß"
a.capitalize!
a.should == "Ss"
end
@@ -130,7 +121,7 @@ describe "String#capitalize!" do
end
it "updates string metadata" do
- capitalized = "ßeT"
+ capitalized = +"ßeT"
capitalized.capitalize!
capitalized.should == "Sset"
@@ -142,7 +133,7 @@ describe "String#capitalize!" do
describe "modifies self in place for ASCII-only case mapping" do
it "does not capitalize non-ASCII characters" do
- a = "ßet"
+ a = +"ßet"
a.capitalize!(:ascii)
a.should == "ßet"
end
@@ -156,13 +147,13 @@ describe "String#capitalize!" do
describe "modifies self in place for full Unicode case mapping adapted for Turkic languages" do
it "capitalizes ASCII characters according to Turkic semantics" do
- a = "iSa"
+ a = +"iSa"
a.capitalize!(:turkic)
a.should == "Ä°sa"
end
it "allows Lithuanian as an extra option" do
- a = "iSa"
+ a = +"iSa"
a.capitalize!(:turkic, :lithuanian)
a.should == "Ä°sa"
end
@@ -174,13 +165,13 @@ describe "String#capitalize!" do
describe "modifies self in place for full Unicode case mapping adapted for Lithuanian" do
it "currently works the same as full Unicode case mapping" do
- a = "iß"
+ a = +"iß"
a.capitalize!(:lithuanian)
a.should == "Iß"
end
it "allows Turkic as an extra option (and applies Turkic semantics)" do
- a = "iß"
+ a = +"iß"
a.capitalize!(:lithuanian, :turkic)
a.should == "İß"
end
@@ -199,12 +190,12 @@ describe "String#capitalize!" do
end
it "returns nil when no changes are made" do
- a = "Hello"
+ a = +"Hello"
a.capitalize!.should == nil
a.should == "Hello"
- "".capitalize!.should == nil
- "H".capitalize!.should == nil
+ (+"").capitalize!.should == nil
+ (+"H").capitalize!.should == nil
end
it "raises a FrozenError when self is frozen" do
diff --git a/spec/ruby/core/string/center_spec.rb b/spec/ruby/core/string/center_spec.rb
index 76da6e1e09..1667b59327 100644
--- a/spec/ruby/core/string/center_spec.rb
+++ b/spec/ruby/core/string/center_spec.rb
@@ -81,31 +81,18 @@ describe "String#center with length, padding" do
-> { "hello".center(0, "") }.should raise_error(ArgumentError)
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances when called on subclasses" do
- StringSpecs::MyString.new("").center(10).should be_an_instance_of(StringSpecs::MyString)
- StringSpecs::MyString.new("foo").center(10).should be_an_instance_of(StringSpecs::MyString)
- StringSpecs::MyString.new("foo").center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(StringSpecs::MyString)
-
- "".center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
- "foo".center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
- end
- end
+ it "returns String instances when called on subclasses" do
+ StringSpecs::MyString.new("").center(10).should be_an_instance_of(String)
+ StringSpecs::MyString.new("foo").center(10).should be_an_instance_of(String)
+ StringSpecs::MyString.new("foo").center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
- ruby_version_is '3.0' do
- it "returns String instances when called on subclasses" do
- StringSpecs::MyString.new("").center(10).should be_an_instance_of(String)
- StringSpecs::MyString.new("foo").center(10).should be_an_instance_of(String)
- StringSpecs::MyString.new("foo").center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
-
- "".center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
- "foo".center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
- end
+ "".center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
+ "foo".center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
end
describe "with width" do
it "returns a String in the same encoding as the original" do
- str = "abc".force_encoding Encoding::IBM437
+ str = "abc".dup.force_encoding Encoding::IBM437
result = str.center 6
result.should == " abc "
result.encoding.should equal(Encoding::IBM437)
@@ -114,7 +101,7 @@ describe "String#center with length, padding" do
describe "with width, pattern" do
it "returns a String in the compatible encoding" do
- str = "abc".force_encoding Encoding::IBM437
+ str = "abc".dup.force_encoding Encoding::IBM437
result = str.center 6, "ã‚"
result.should == "ã‚abcã‚ã‚"
result.encoding.should equal(Encoding::UTF_8)
diff --git a/spec/ruby/core/string/chilled_string_spec.rb b/spec/ruby/core/string/chilled_string_spec.rb
new file mode 100644
index 0000000000..8de4fc421b
--- /dev/null
+++ b/spec/ruby/core/string/chilled_string_spec.rb
@@ -0,0 +1,69 @@
+require_relative '../../spec_helper'
+
+describe "chilled String" do
+ guard -> { ruby_version_is "3.4" and !"test".equal?("test") } do
+ describe "#frozen?" do
+ it "returns true" do
+ "chilled".frozen?.should == true
+ end
+ end
+
+ describe "#-@" do
+ it "returns a different instance" do
+ input = "chilled"
+ interned = (-input)
+ interned.frozen?.should == true
+ interned.object_id.should_not == input.object_id
+ end
+ end
+
+ describe "#+@" do
+ it "returns a different instance" do
+ input = "chilled"
+ duped = (+input)
+ duped.frozen?.should == false
+ duped.object_id.should_not == input.object_id
+ end
+ end
+
+ describe "#clone" do
+ it "preserves chilled status" do
+ input = "chilled".clone
+ -> {
+ input << "-mutated"
+ }.should complain(/literal string will be frozen in the future/)
+ input.should == "chilled-mutated"
+ end
+ end
+
+ describe "mutation" do
+ it "emits a warning" do
+ input = "chilled"
+ -> {
+ input << "-mutated"
+ }.should complain(/literal string will be frozen in the future/)
+ input.should == "chilled-mutated"
+ end
+
+ it "emits a warning on singleton_class creaation" do
+ -> {
+ "chilled".singleton_class
+ }.should complain(/literal string will be frozen in the future/)
+ end
+
+ it "emits a warning on instance variable assignment" do
+ -> {
+ "chilled".instance_variable_set(:@ivar, 42)
+ }.should complain(/literal string will be frozen in the future/)
+ end
+
+ it "raises FrozenError after the string was explictly frozen" do
+ input = "chilled"
+ input.freeze
+ -> {
+ input << "mutated"
+ }.should raise_error(FrozenError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/string/chomp_spec.rb b/spec/ruby/core/string/chomp_spec.rb
index d0508d938f..d27c84c6f6 100644
--- a/spec/ruby/core/string/chomp_spec.rb
+++ b/spec/ruby/core/string/chomp_spec.rb
@@ -1,4 +1,5 @@
# -*- encoding: utf-8 -*-
+# frozen_string_literal: false
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
@@ -44,18 +45,9 @@ describe "String#chomp" do
"abc\n\n".encode("US-ASCII").chomp.encoding.should == Encoding::US_ASCII
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances when called on a subclass" do
- str = StringSpecs::MyString.new("hello\n").chomp
- str.should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns String instances when called on a subclass" do
- str = StringSpecs::MyString.new("hello\n").chomp
- str.should be_an_instance_of(String)
- end
+ it "returns String instances when called on a subclass" do
+ str = StringSpecs::MyString.new("hello\n").chomp
+ str.should be_an_instance_of(String)
end
it "removes trailing characters that match $/ when it has been assigned a value" do
diff --git a/spec/ruby/core/string/chop_spec.rb b/spec/ruby/core/string/chop_spec.rb
index f598d34bc8..99c2c82190 100644
--- a/spec/ruby/core/string/chop_spec.rb
+++ b/spec/ruby/core/string/chop_spec.rb
@@ -1,4 +1,5 @@
# -*- encoding: utf-8 -*-
+# frozen_string_literal: false
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
@@ -49,16 +50,8 @@ describe "String#chop" do
s.chop.should_not equal(s)
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances when called on a subclass" do
- StringSpecs::MyString.new("hello\n").chop.should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns String instances when called on a subclass" do
- StringSpecs::MyString.new("hello\n").chop.should be_an_instance_of(String)
- end
+ it "returns String instances when called on a subclass" do
+ StringSpecs::MyString.new("hello\n").chop.should be_an_instance_of(String)
end
it "returns a String in the same encoding as self" do
diff --git a/spec/ruby/core/string/clear_spec.rb b/spec/ruby/core/string/clear_spec.rb
index e1d68e03bd..152986fd0f 100644
--- a/spec/ruby/core/string/clear_spec.rb
+++ b/spec/ruby/core/string/clear_spec.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require_relative '../../spec_helper'
describe "String#clear" do
diff --git a/spec/ruby/core/string/codepoints_spec.rb b/spec/ruby/core/string/codepoints_spec.rb
index 0b6cde82f7..b276d0baa8 100644
--- a/spec/ruby/core/string/codepoints_spec.rb
+++ b/spec/ruby/core/string/codepoints_spec.rb
@@ -11,7 +11,7 @@ describe "String#codepoints" do
end
it "raises an ArgumentError when no block is given if self has an invalid encoding" do
- s = "\xDF".force_encoding(Encoding::UTF_8)
+ s = "\xDF".dup.force_encoding(Encoding::UTF_8)
s.valid_encoding?.should be_false
-> { s.codepoints }.should raise_error(ArgumentError)
end
diff --git a/spec/ruby/core/string/comparison_spec.rb b/spec/ruby/core/string/comparison_spec.rb
index 91cfdca25a..9db0cff5ee 100644
--- a/spec/ruby/core/string/comparison_spec.rb
+++ b/spec/ruby/core/string/comparison_spec.rb
@@ -61,12 +61,12 @@ describe "String#<=> with String" do
end
it "ignores encoding difference" do
- ("ÄÖÛ".force_encoding("utf-8") <=> "ÄÖÜ".force_encoding("iso-8859-1")).should == -1
- ("ÄÖÜ".force_encoding("utf-8") <=> "ÄÖÛ".force_encoding("iso-8859-1")).should == 1
+ ("ÄÖÛ".dup.force_encoding("utf-8") <=> "ÄÖÜ".dup.force_encoding("iso-8859-1")).should == -1
+ ("ÄÖÜ".dup.force_encoding("utf-8") <=> "ÄÖÛ".dup.force_encoding("iso-8859-1")).should == 1
end
it "returns 0 with identical ASCII-compatible bytes of different encodings" do
- ("abc".force_encoding("utf-8") <=> "abc".force_encoding("iso-8859-1")).should == 0
+ ("abc".dup.force_encoding("utf-8") <=> "abc".dup.force_encoding("iso-8859-1")).should == 0
end
it "compares the indices of the encodings when the strings have identical non-ASCII-compatible bytes" do
@@ -77,7 +77,7 @@ describe "String#<=> with String" do
end
it "returns 0 when comparing 2 empty strings but one is not ASCII-compatible" do
- ("" <=> "".force_encoding('iso-2022-jp')).should == 0
+ ("" <=> "".dup.force_encoding('iso-2022-jp')).should == 0
end
end
diff --git a/spec/ruby/core/string/concat_spec.rb b/spec/ruby/core/string/concat_spec.rb
index 6f487eaa3a..cbd7df54e2 100644
--- a/spec/ruby/core/string/concat_spec.rb
+++ b/spec/ruby/core/string/concat_spec.rb
@@ -8,19 +8,19 @@ describe "String#concat" do
it_behaves_like :string_concat_type_coercion, :concat
it "takes multiple arguments" do
- str = "hello "
+ str = +"hello "
str.concat "wo", "", "rld"
str.should == "hello world"
end
it "concatenates the initial value when given arguments contain 2 self" do
- str = "hello"
+ str = +"hello"
str.concat str, str
str.should == "hellohellohello"
end
it "returns self when given no arguments" do
- str = "hello"
+ str = +"hello"
str.concat.should equal(str)
str.should == "hello"
end
diff --git a/spec/ruby/core/string/delete_prefix_spec.rb b/spec/ruby/core/string/delete_prefix_spec.rb
index 238de85f05..ee7f044905 100644
--- a/spec/ruby/core/string/delete_prefix_spec.rb
+++ b/spec/ruby/core/string/delete_prefix_spec.rb
@@ -1,4 +1,5 @@
# -*- encoding: utf-8 -*-
+# frozen_string_literal: false
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
@@ -38,18 +39,9 @@ describe "String#delete_prefix" do
'hello'.delete_prefix(o).should == 'o'
end
- ruby_version_is ''...'3.0' do
- it "returns a subclass instance when called on a subclass instance" do
- s = StringSpecs::MyString.new('hello')
- s.delete_prefix('hell').should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns a String instance when called on a subclass instance" do
- s = StringSpecs::MyString.new('hello')
- s.delete_prefix('hell').should be_an_instance_of(String)
- end
+ it "returns a String instance when called on a subclass instance" do
+ s = StringSpecs::MyString.new('hello')
+ s.delete_prefix('hell').should be_an_instance_of(String)
end
it "returns a String in the same encoding as self" do
diff --git a/spec/ruby/core/string/delete_spec.rb b/spec/ruby/core/string/delete_spec.rb
index 87831a9d19..6d359776e4 100644
--- a/spec/ruby/core/string/delete_spec.rb
+++ b/spec/ruby/core/string/delete_spec.rb
@@ -1,4 +1,5 @@
# -*- encoding: utf-8 -*-
+# frozen_string_literal: false
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
@@ -84,16 +85,8 @@ describe "String#delete" do
-> { "hello world".delete(mock('x')) }.should raise_error(TypeError)
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances when called on a subclass" do
- StringSpecs::MyString.new("oh no!!!").delete("!").should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns String instances when called on a subclass" do
- StringSpecs::MyString.new("oh no!!!").delete("!").should be_an_instance_of(String)
- end
+ it "returns String instances when called on a subclass" do
+ StringSpecs::MyString.new("oh no!!!").delete("!").should be_an_instance_of(String)
end
it "returns a String in the same encoding as self" do
diff --git a/spec/ruby/core/string/delete_suffix_spec.rb b/spec/ruby/core/string/delete_suffix_spec.rb
index 6883d6938c..1842d75aa5 100644
--- a/spec/ruby/core/string/delete_suffix_spec.rb
+++ b/spec/ruby/core/string/delete_suffix_spec.rb
@@ -1,4 +1,5 @@
# -*- encoding: utf-8 -*-
+# frozen_string_literal: false
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
@@ -38,18 +39,9 @@ describe "String#delete_suffix" do
'hello'.delete_suffix(o).should == 'h'
end
- ruby_version_is ''...'3.0' do
- it "returns a subclass instance when called on a subclass instance" do
- s = StringSpecs::MyString.new('hello')
- s.delete_suffix('ello').should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns a String instance when called on a subclass instance" do
- s = StringSpecs::MyString.new('hello')
- s.delete_suffix('ello').should be_an_instance_of(String)
- end
+ it "returns a String instance when called on a subclass instance" do
+ s = StringSpecs::MyString.new('hello')
+ s.delete_suffix('ello').should be_an_instance_of(String)
end
it "returns a String in the same encoding as self" do
diff --git a/spec/ruby/core/string/downcase_spec.rb b/spec/ruby/core/string/downcase_spec.rb
index 153b4ce191..2d260f23f1 100644
--- a/spec/ruby/core/string/downcase_spec.rb
+++ b/spec/ruby/core/string/downcase_spec.rb
@@ -1,4 +1,5 @@
# -*- encoding: utf-8 -*-
+# frozen_string_literal: false
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
@@ -76,16 +77,8 @@ describe "String#downcase" do
-> { "ABC".downcase(:invalid_option) }.should raise_error(ArgumentError)
end
- ruby_version_is ''...'3.0' do
- it "returns a subclass instance for subclasses" do
- StringSpecs::MyString.new("FOObar").downcase.should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns a String instance for subclasses" do
- StringSpecs::MyString.new("FOObar").downcase.should be_an_instance_of(String)
- end
+ it "returns a String instance for subclasses" do
+ StringSpecs::MyString.new("FOObar").downcase.should be_an_instance_of(String)
end
end
diff --git a/spec/ruby/core/string/dump_spec.rb b/spec/ruby/core/string/dump_spec.rb
index 81de0cfae4..cab8beff5a 100644
--- a/spec/ruby/core/string/dump_spec.rb
+++ b/spec/ruby/core/string/dump_spec.rb
@@ -7,16 +7,8 @@ describe "String#dump" do
"foo".freeze.dump.should_not.frozen?
end
- ruby_version_is ''...'3.0' do
- it "returns a subclass instance" do
- StringSpecs::MyString.new.dump.should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns a String instance" do
- StringSpecs::MyString.new.dump.should be_an_instance_of(String)
- end
+ it "returns a String instance" do
+ StringSpecs::MyString.new.dump.should be_an_instance_of(String)
end
it "wraps string with \"" do
diff --git a/spec/ruby/core/string/dup_spec.rb b/spec/ruby/core/string/dup_spec.rb
index 73f71b8ffc..073802d84b 100644
--- a/spec/ruby/core/string/dup_spec.rb
+++ b/spec/ruby/core/string/dup_spec.rb
@@ -51,7 +51,7 @@ describe "String#dup" do
end
it "does not modify the original setbyte-mutated string when changing dupped string" do
- orig = "a"
+ orig = +"a"
orig.setbyte 0, "b".ord
copy = orig.dup
orig.setbyte 0, "c".ord
diff --git a/spec/ruby/core/string/each_byte_spec.rb b/spec/ruby/core/string/each_byte_spec.rb
index e04dca807f..7b3db265ac 100644
--- a/spec/ruby/core/string/each_byte_spec.rb
+++ b/spec/ruby/core/string/each_byte_spec.rb
@@ -9,26 +9,26 @@ describe "String#each_byte" do
end
it "keeps iterating from the old position (to new string end) when self changes" do
- r = ""
- s = "hello world"
+ r = +""
+ s = +"hello world"
s.each_byte do |c|
r << c
s.insert(0, "<>") if r.size < 3
end
r.should == "h><>hello world"
- r = ""
- s = "hello world"
+ r = +""
+ s = +"hello world"
s.each_byte { |c| s.slice!(-1); r << c }
r.should == "hello "
- r = ""
- s = "hello world"
+ r = +""
+ s = +"hello world"
s.each_byte { |c| s.slice!(0); r << c }
r.should == "hlowrd"
- r = ""
- s = "hello world"
+ r = +""
+ s = +"hello world"
s.each_byte { |c| s.slice!(0..-1); r << c }
r.should == "h"
end
diff --git a/spec/ruby/core/string/each_grapheme_cluster_spec.rb b/spec/ruby/core/string/each_grapheme_cluster_spec.rb
index f28e24000e..e1fa4ae67b 100644
--- a/spec/ruby/core/string/each_grapheme_cluster_spec.rb
+++ b/spec/ruby/core/string/each_grapheme_cluster_spec.rb
@@ -8,11 +8,9 @@ describe "String#each_grapheme_cluster" do
it_behaves_like :string_grapheme_clusters, :each_grapheme_cluster
it_behaves_like :string_each_char_without_block, :each_grapheme_cluster
- ruby_version_is '3.0' do
- it "yields String instances for subclasses" do
- a = []
- StringSpecs::MyString.new("abc").each_grapheme_cluster { |s| a << s.class }
- a.should == [String, String, String]
- end
+ it "yields String instances for subclasses" do
+ a = []
+ StringSpecs::MyString.new("abc").each_grapheme_cluster { |s| a << s.class }
+ a.should == [String, String, String]
end
end
diff --git a/spec/ruby/core/string/element_set_spec.rb b/spec/ruby/core/string/element_set_spec.rb
index fa041fa31d..e7599f832c 100644
--- a/spec/ruby/core/string/element_set_spec.rb
+++ b/spec/ruby/core/string/element_set_spec.rb
@@ -1,4 +1,5 @@
# -*- encoding: utf-8 -*-
+# frozen_string_literal: false
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
diff --git a/spec/ruby/core/string/encode_spec.rb b/spec/ruby/core/string/encode_spec.rb
index 5604ab7210..97dd753b62 100644
--- a/spec/ruby/core/string/encode_spec.rb
+++ b/spec/ruby/core/string/encode_spec.rb
@@ -34,8 +34,8 @@ describe "String#encode" do
it "encodes an ascii substring of a binary string to UTF-8" do
x82 = [0x82].pack('C')
- str = "#{x82}foo".force_encoding("binary")[1..-1].encode("utf-8")
- str.should == "foo".force_encoding("utf-8")
+ str = "#{x82}foo".dup.force_encoding("binary")[1..-1].encode("utf-8")
+ str.should == "foo".dup.force_encoding("utf-8")
str.encoding.should equal(Encoding::UTF_8)
end
end
@@ -49,7 +49,7 @@ describe "String#encode" do
end
it "round trips a String" do
- str = "abc def".force_encoding Encoding::US_ASCII
+ str = "abc def".dup.force_encoding Encoding::US_ASCII
str.encode("utf-32be").encode("ascii").should == "abc def"
end
end
@@ -79,6 +79,10 @@ describe "String#encode" do
encoded.encode("UTF-8").should == "ã¡foofoo"
end
+ it "replace multiple invalid bytes at the end with a single replacement character" do
+ "\xE3\x81\x93\xE3\x81".encode("UTF-8", invalid: :replace).should == "\u3053\ufffd"
+ end
+
it "replaces invalid encoding in source using a specified replacement even when a fallback is given" do
encoded = "ã¡\xE3\x81\xFF".encode("UTF-16LE", invalid: :replace, replace: "foo", fallback: -> c { "bar" })
encoded.should == "\u3061foofoo".encode("UTF-16LE")
@@ -118,8 +122,7 @@ describe "String#encode" do
describe "when passed to, from" do
it "returns a copy in the destination encoding when both encodings are the same" do
- str = "ã‚"
- str.force_encoding("binary")
+ str = "ã‚".dup.force_encoding("binary")
encoded = str.encode("utf-8", "utf-8")
encoded.should_not equal(str)
@@ -151,8 +154,7 @@ describe "String#encode" do
end
it "returns a copy in the destination encoding when both encodings are the same" do
- str = "ã‚"
- str.force_encoding("binary")
+ str = "ã‚".dup.force_encoding("binary")
encoded = str.encode("utf-8", "utf-8", invalid: :replace)
encoded.should_not equal(str)
@@ -187,13 +189,13 @@ describe "String#encode!" do
describe "when passed no options" do
it "returns self when Encoding.default_internal is nil" do
Encoding.default_internal = nil
- str = "ã‚"
+ str = +"ã‚"
str.encode!.should equal(str)
end
it "returns self for a ASCII-only String when Encoding.default_internal is nil" do
Encoding.default_internal = nil
- str = "abc"
+ str = +"abc"
str.encode!.should equal(str)
end
end
@@ -201,14 +203,14 @@ describe "String#encode!" do
describe "when passed options" do
it "returns self for ASCII-only String when Encoding.default_internal is nil" do
Encoding.default_internal = nil
- str = "abc"
+ str = +"abc"
str.encode!(invalid: :replace).should equal(str)
end
end
describe "when passed to encoding" do
it "returns self" do
- str = "abc"
+ str = +"abc"
result = str.encode!(Encoding::BINARY)
result.encoding.should equal(Encoding::BINARY)
result.should equal(str)
@@ -217,7 +219,7 @@ describe "String#encode!" do
describe "when passed to, from" do
it "returns self" do
- str = "ã‚ã‚"
+ str = +"ã‚ã‚"
result = str.encode!("euc-jp", "utf-8")
result.encoding.should equal(Encoding::EUC_JP)
result.should equal(str)
diff --git a/spec/ruby/core/string/encoding_spec.rb b/spec/ruby/core/string/encoding_spec.rb
index 574a1e2f92..f6e8fd3470 100644
--- a/spec/ruby/core/string/encoding_spec.rb
+++ b/spec/ruby/core/string/encoding_spec.rb
@@ -14,11 +14,11 @@ describe "String#encoding" do
end
it "returns the given encoding if #force_encoding has been called" do
- "a".force_encoding(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
+ "a".dup.force_encoding(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
end
it "returns the given encoding if #encode!has been called" do
- "a".encode!(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
+ "a".dup.encode!(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
end
end
@@ -108,13 +108,13 @@ describe "String#encoding for Strings with \\u escapes" do
end
it "returns the given encoding if #force_encoding has been called" do
- "\u{20}".force_encoding(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
- "\u{2020}".force_encoding(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
+ "\u{20}".dup.force_encoding(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
+ "\u{2020}".dup.force_encoding(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
end
it "returns the given encoding if #encode!has been called" do
- "\u{20}".encode!(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
- "\u{2020}".encode!(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
+ "\u{20}".dup.encode!(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
+ "\u{2020}".dup.encode!(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
end
end
@@ -173,16 +173,12 @@ describe "String#encoding for Strings with \\x escapes" do
end
it "returns the given encoding if #force_encoding has been called" do
- x50 = "\x50"
- x50.force_encoding(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
- xD4 = [212].pack('C')
- xD4.force_encoding(Encoding::ISO_8859_9).encoding.should == Encoding::ISO_8859_9
+ "\x50".dup.force_encoding(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
+ [212].pack('C').force_encoding(Encoding::ISO_8859_9).encoding.should == Encoding::ISO_8859_9
end
it "returns the given encoding if #encode!has been called" do
- x50 = "\x50"
- x50.encode!(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
- x00 = "x\00"
- x00.encode!(Encoding::UTF_8).encoding.should == Encoding::UTF_8
+ "\x50".dup.encode!(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
+ "x\00".dup.encode!(Encoding::UTF_8).encoding.should == Encoding::UTF_8
end
end
diff --git a/spec/ruby/core/string/fixtures/utf-8-encoding.rb b/spec/ruby/core/string/fixtures/utf-8-encoding.rb
deleted file mode 100644
index fd243ec522..0000000000
--- a/spec/ruby/core/string/fixtures/utf-8-encoding.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# -*- encoding: utf-8 -*-
-module StringSpecs
- class UTF8Encoding
- def self.source_encoding; __ENCODING__; end
- def self.egrave; "é"; end
- end
-end
diff --git a/spec/ruby/core/string/force_encoding_spec.rb b/spec/ruby/core/string/force_encoding_spec.rb
index f37aaf9eb4..2259dcf3cf 100644
--- a/spec/ruby/core/string/force_encoding_spec.rb
+++ b/spec/ruby/core/string/force_encoding_spec.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require_relative '../../spec_helper'
describe "String#force_encoding" do
diff --git a/spec/ruby/core/string/freeze_spec.rb b/spec/ruby/core/string/freeze_spec.rb
index 04d1e9513c..2e8e70386d 100644
--- a/spec/ruby/core/string/freeze_spec.rb
+++ b/spec/ruby/core/string/freeze_spec.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require_relative '../../spec_helper'
describe "String#freeze" do
diff --git a/spec/ruby/core/string/gsub_spec.rb b/spec/ruby/core/string/gsub_spec.rb
index c87a566591..0d9f32eca2 100644
--- a/spec/ruby/core/string/gsub_spec.rb
+++ b/spec/ruby/core/string/gsub_spec.rb
@@ -1,4 +1,5 @@
# -*- encoding: utf-8 -*-
+# frozen_string_literal: false
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
@@ -192,22 +193,11 @@ describe "String#gsub with pattern and replacement" do
-> { "hello".gsub(/[aeiou]/, nil) }.should raise_error(TypeError)
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances when called on a subclass" do
- StringSpecs::MyString.new("").gsub(//, "").should be_an_instance_of(StringSpecs::MyString)
- StringSpecs::MyString.new("").gsub(/foo/, "").should be_an_instance_of(StringSpecs::MyString)
- StringSpecs::MyString.new("foo").gsub(/foo/, "").should be_an_instance_of(StringSpecs::MyString)
- StringSpecs::MyString.new("foo").gsub("foo", "").should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns String instances when called on a subclass" do
- StringSpecs::MyString.new("").gsub(//, "").should be_an_instance_of(String)
- StringSpecs::MyString.new("").gsub(/foo/, "").should be_an_instance_of(String)
- StringSpecs::MyString.new("foo").gsub(/foo/, "").should be_an_instance_of(String)
- StringSpecs::MyString.new("foo").gsub("foo", "").should be_an_instance_of(String)
- end
+ it "returns String instances when called on a subclass" do
+ StringSpecs::MyString.new("").gsub(//, "").should be_an_instance_of(String)
+ StringSpecs::MyString.new("").gsub(/foo/, "").should be_an_instance_of(String)
+ StringSpecs::MyString.new("foo").gsub(/foo/, "").should be_an_instance_of(String)
+ StringSpecs::MyString.new("foo").gsub("foo", "").should be_an_instance_of(String)
end
it "sets $~ to MatchData of last match and nil when there's none" do
diff --git a/spec/ruby/core/string/include_spec.rb b/spec/ruby/core/string/include_spec.rb
index 23e1e134ec..9781140a55 100644
--- a/spec/ruby/core/string/include_spec.rb
+++ b/spec/ruby/core/string/include_spec.rb
@@ -15,16 +15,16 @@ describe "String#include? with String" do
it "returns true if both strings are empty" do
"".should.include?("")
- "".force_encoding("EUC-JP").should.include?("")
- "".should.include?("".force_encoding("EUC-JP"))
- "".force_encoding("EUC-JP").should.include?("".force_encoding("EUC-JP"))
+ "".dup.force_encoding("EUC-JP").should.include?("")
+ "".should.include?("".dup.force_encoding("EUC-JP"))
+ "".dup.force_encoding("EUC-JP").should.include?("".dup.force_encoding("EUC-JP"))
end
it "returns true if the RHS is empty" do
"a".should.include?("")
- "a".force_encoding("EUC-JP").should.include?("")
- "a".should.include?("".force_encoding("EUC-JP"))
- "a".force_encoding("EUC-JP").should.include?("".force_encoding("EUC-JP"))
+ "a".dup.force_encoding("EUC-JP").should.include?("")
+ "a".should.include?("".dup.force_encoding("EUC-JP"))
+ "a".dup.force_encoding("EUC-JP").should.include?("".dup.force_encoding("EUC-JP"))
end
it "tries to convert other to string using to_str" do
diff --git a/spec/ruby/core/string/index_spec.rb b/spec/ruby/core/string/index_spec.rb
index 2eeee9be87..be79708045 100644
--- a/spec/ruby/core/string/index_spec.rb
+++ b/spec/ruby/core/string/index_spec.rb
@@ -161,11 +161,18 @@ describe "String#index with String" do
end
it "handles a substring in a superset encoding" do
- 'abc'.force_encoding(Encoding::US_ASCII).index('é').should == nil
+ 'abc'.dup.force_encoding(Encoding::US_ASCII).index('é').should == nil
end
it "handles a substring in a subset encoding" do
- 'été'.index('t'.force_encoding(Encoding::US_ASCII)).should == 1
+ 'été'.index('t'.dup.force_encoding(Encoding::US_ASCII)).should == 1
+ end
+
+ it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
+ str = 'abc'.dup.force_encoding("ISO-2022-JP")
+ pattern = 'b'.dup.force_encoding("EUC-JP")
+
+ -> { str.index(pattern) }.should raise_error(Encoding::CompatibilityError, "incompatible character encodings: ISO-2022-JP and EUC-JP")
end
end
@@ -312,6 +319,17 @@ describe "String#index with Regexp" do
"ã‚ã‚Œã‚ã‚ã‚Œ".index(/ã‚/, 3).should == 3
end
+ ruby_bug "#19763", ""..."3.3.0" do
+ it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
+ re = Regexp.new "れ".encode(Encoding::EUC_JP)
+ -> do
+ "ã‚ã‚Œ".index re
+ end.should raise_error(Encoding::CompatibilityError, "incompatible encoding regexp match (EUC-JP regexp with UTF-8 string)")
+ end
+ end
+
+ # The exception message was incorrectly "incompatible character encodings: UTF-8 and EUC-JP" before 3.3.0
+ # Still test that the right exception class is used before that.
it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
re = Regexp.new "れ".encode(Encoding::EUC_JP)
-> do
diff --git a/spec/ruby/core/string/insert_spec.rb b/spec/ruby/core/string/insert_spec.rb
index 0c87df3a95..483f3c9367 100644
--- a/spec/ruby/core/string/insert_spec.rb
+++ b/spec/ruby/core/string/insert_spec.rb
@@ -1,5 +1,5 @@
# -*- encoding: utf-8 -*-
-
+# frozen_string_literal: false
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
diff --git a/spec/ruby/core/string/inspect_spec.rb b/spec/ruby/core/string/inspect_spec.rb
index 8bf3d3161f..15db06c7f5 100644
--- a/spec/ruby/core/string/inspect_spec.rb
+++ b/spec/ruby/core/string/inspect_spec.rb
@@ -327,7 +327,7 @@ describe "String#inspect" do
end
it "works for broken US-ASCII strings" do
- s = "©".force_encoding("US-ASCII")
+ s = "©".dup.force_encoding("US-ASCII")
s.inspect.should == '"\xC2\xA9"'
end
diff --git a/spec/ruby/core/string/ljust_spec.rb b/spec/ruby/core/string/ljust_spec.rb
index 9a25d3abd4..47324c59d2 100644
--- a/spec/ruby/core/string/ljust_spec.rb
+++ b/spec/ruby/core/string/ljust_spec.rb
@@ -64,31 +64,18 @@ describe "String#ljust with length, padding" do
-> { "hello".ljust(10, '') }.should raise_error(ArgumentError)
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances when called on subclasses" do
- StringSpecs::MyString.new("").ljust(10).should be_an_instance_of(StringSpecs::MyString)
- StringSpecs::MyString.new("foo").ljust(10).should be_an_instance_of(StringSpecs::MyString)
- StringSpecs::MyString.new("foo").ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(StringSpecs::MyString)
-
- "".ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
- "foo".ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
- end
- end
+ it "returns String instances when called on subclasses" do
+ StringSpecs::MyString.new("").ljust(10).should be_an_instance_of(String)
+ StringSpecs::MyString.new("foo").ljust(10).should be_an_instance_of(String)
+ StringSpecs::MyString.new("foo").ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
- ruby_version_is '3.0' do
- it "returns String instances when called on subclasses" do
- StringSpecs::MyString.new("").ljust(10).should be_an_instance_of(String)
- StringSpecs::MyString.new("foo").ljust(10).should be_an_instance_of(String)
- StringSpecs::MyString.new("foo").ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
-
- "".ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
- "foo".ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
- end
+ "".ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
+ "foo".ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
end
describe "with width" do
it "returns a String in the same encoding as the original" do
- str = "abc".force_encoding Encoding::IBM437
+ str = "abc".dup.force_encoding Encoding::IBM437
result = str.ljust 5
result.should == "abc "
result.encoding.should equal(Encoding::IBM437)
@@ -97,7 +84,7 @@ describe "String#ljust with length, padding" do
describe "with width, pattern" do
it "returns a String in the compatible encoding" do
- str = "abc".force_encoding Encoding::IBM437
+ str = "abc".dup.force_encoding Encoding::IBM437
result = str.ljust 5, "ã‚"
result.should == "abcã‚ã‚"
result.encoding.should equal(Encoding::UTF_8)
diff --git a/spec/ruby/core/string/lstrip_spec.rb b/spec/ruby/core/string/lstrip_spec.rb
index 75434613f1..99bab6f349 100644
--- a/spec/ruby/core/string/lstrip_spec.rb
+++ b/spec/ruby/core/string/lstrip_spec.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
require_relative 'shared/strip'
@@ -20,11 +21,9 @@ describe "String#lstrip" do
" ã“ã«ã¡ã‚ "[1...-1].lstrip.should == "ã“ã«ã¡ã‚"
end
- ruby_version_is '3.0' do
- it "strips leading \\0" do
- "\x00hello".lstrip.should == "hello"
- "\000 \000hello\000 \000".lstrip.should == "hello\000 \000"
- end
+ it "strips leading \\0" do
+ "\x00hello".lstrip.should == "hello"
+ "\000 \000hello\000 \000".lstrip.should == "hello\000 \000"
end
end
@@ -47,12 +46,10 @@ describe "String#lstrip!" do
" ".lstrip.should == ""
end
- ruby_version_is '3.0' do
- it "removes leading NULL bytes and whitespace" do
- a = "\000 \000hello\000 \000"
- a.lstrip!
- a.should == "hello\000 \000"
- end
+ it "removes leading NULL bytes and whitespace" do
+ a = "\000 \000hello\000 \000"
+ a.lstrip!
+ a.should == "hello\000 \000"
end
it "raises a FrozenError on a frozen instance that is modified" do
diff --git a/spec/ruby/core/string/ord_spec.rb b/spec/ruby/core/string/ord_spec.rb
index 4cf26990fe..35af3b5458 100644
--- a/spec/ruby/core/string/ord_spec.rb
+++ b/spec/ruby/core/string/ord_spec.rb
@@ -27,7 +27,7 @@ describe "String#ord" do
end
it "raises ArgumentError if the character is broken" do
- s = "©".force_encoding("US-ASCII")
+ s = "©".dup.force_encoding("US-ASCII")
-> { s.ord }.should raise_error(ArgumentError, "invalid byte sequence in US-ASCII")
end
end
diff --git a/spec/ruby/core/string/partition_spec.rb b/spec/ruby/core/string/partition_spec.rb
index 9cb3672881..d5370dcc73 100644
--- a/spec/ruby/core/string/partition_spec.rb
+++ b/spec/ruby/core/string/partition_spec.rb
@@ -40,7 +40,7 @@ describe "String#partition with String" do
end
it "handles a pattern in a superset encoding" do
- string = "hello".force_encoding(Encoding::US_ASCII)
+ string = "hello".dup.force_encoding(Encoding::US_ASCII)
result = string.partition("é")
@@ -51,7 +51,7 @@ describe "String#partition with String" do
end
it "handles a pattern in a subset encoding" do
- pattern = "o".force_encoding(Encoding::US_ASCII)
+ pattern = "o".dup.force_encoding(Encoding::US_ASCII)
result = "héllo world".partition(pattern)
diff --git a/spec/ruby/core/string/prepend_spec.rb b/spec/ruby/core/string/prepend_spec.rb
index a0393d4760..5248ea8056 100644
--- a/spec/ruby/core/string/prepend_spec.rb
+++ b/spec/ruby/core/string/prepend_spec.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
diff --git a/spec/ruby/core/string/reverse_spec.rb b/spec/ruby/core/string/reverse_spec.rb
index 73526256ef..aa6abe6036 100644
--- a/spec/ruby/core/string/reverse_spec.rb
+++ b/spec/ruby/core/string/reverse_spec.rb
@@ -1,4 +1,5 @@
# encoding: utf-8
+# frozen_string_literal: false
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
@@ -10,20 +11,10 @@ describe "String#reverse" do
"".reverse.should == ""
end
- ruby_version_is '3.0' do
- it "returns String instances when called on a subclass" do
- StringSpecs::MyString.new("stressed").reverse.should be_an_instance_of(String)
- StringSpecs::MyString.new("m").reverse.should be_an_instance_of(String)
- StringSpecs::MyString.new("").reverse.should be_an_instance_of(String)
- end
- end
-
- ruby_version_is ''...'3.0' do
- it "returns subclass instances when called on a subclass" do
- StringSpecs::MyString.new("stressed").reverse.should be_an_instance_of(StringSpecs::MyString)
- StringSpecs::MyString.new("m").reverse.should be_an_instance_of(StringSpecs::MyString)
- StringSpecs::MyString.new("").reverse.should be_an_instance_of(StringSpecs::MyString)
- end
+ it "returns String instances when called on a subclass" do
+ StringSpecs::MyString.new("stressed").reverse.should be_an_instance_of(String)
+ StringSpecs::MyString.new("m").reverse.should be_an_instance_of(String)
+ StringSpecs::MyString.new("").reverse.should be_an_instance_of(String)
end
it "reverses a string with multi byte characters" do
diff --git a/spec/ruby/core/string/rindex_spec.rb b/spec/ruby/core/string/rindex_spec.rb
index e795105e1d..88ce733583 100644
--- a/spec/ruby/core/string/rindex_spec.rb
+++ b/spec/ruby/core/string/rindex_spec.rb
@@ -1,7 +1,6 @@
# -*- encoding: utf-8 -*-
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
-require_relative 'fixtures/utf-8-encoding'
describe "String#rindex with object" do
it "raises a TypeError if obj isn't a String or Regexp" do
@@ -198,11 +197,18 @@ describe "String#rindex with String" do
end
it "handles a substring in a superset encoding" do
- 'abc'.force_encoding(Encoding::US_ASCII).rindex('é').should == nil
+ 'abc'.dup.force_encoding(Encoding::US_ASCII).rindex('é').should == nil
end
it "handles a substring in a subset encoding" do
- 'été'.rindex('t'.force_encoding(Encoding::US_ASCII)).should == 1
+ 'été'.rindex('t'.dup.force_encoding(Encoding::US_ASCII)).should == 1
+ end
+
+ it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
+ str = 'abc'.dup.force_encoding("ISO-2022-JP")
+ pattern = 'b'.dup.force_encoding("EUC-JP")
+
+ -> { str.rindex(pattern) }.should raise_error(Encoding::CompatibilityError, "incompatible character encodings: ISO-2022-JP and EUC-JP")
end
end
@@ -373,6 +379,6 @@ describe "String#rindex with Regexp" do
re = Regexp.new "れ".encode(Encoding::EUC_JP)
-> do
"ã‚ã‚Œ".rindex re
- end.should raise_error(Encoding::CompatibilityError)
+ end.should raise_error(Encoding::CompatibilityError, "incompatible encoding regexp match (EUC-JP regexp with UTF-8 string)")
end
end
diff --git a/spec/ruby/core/string/rjust_spec.rb b/spec/ruby/core/string/rjust_spec.rb
index d067b7bdb3..4ad3e54aea 100644
--- a/spec/ruby/core/string/rjust_spec.rb
+++ b/spec/ruby/core/string/rjust_spec.rb
@@ -64,31 +64,18 @@ describe "String#rjust with length, padding" do
-> { "hello".rjust(10, '') }.should raise_error(ArgumentError)
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances when called on subclasses" do
- StringSpecs::MyString.new("").rjust(10).should be_an_instance_of(StringSpecs::MyString)
- StringSpecs::MyString.new("foo").rjust(10).should be_an_instance_of(StringSpecs::MyString)
- StringSpecs::MyString.new("foo").rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(StringSpecs::MyString)
-
- "".rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
- "foo".rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
- end
- end
+ it "returns String instances when called on subclasses" do
+ StringSpecs::MyString.new("").rjust(10).should be_an_instance_of(String)
+ StringSpecs::MyString.new("foo").rjust(10).should be_an_instance_of(String)
+ StringSpecs::MyString.new("foo").rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
- ruby_version_is '3.0' do
- it "returns String instances when called on subclasses" do
- StringSpecs::MyString.new("").rjust(10).should be_an_instance_of(String)
- StringSpecs::MyString.new("foo").rjust(10).should be_an_instance_of(String)
- StringSpecs::MyString.new("foo").rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
-
- "".rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
- "foo".rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
- end
+ "".rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
+ "foo".rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
end
describe "with width" do
it "returns a String in the same encoding as the original" do
- str = "abc".force_encoding Encoding::IBM437
+ str = "abc".dup.force_encoding Encoding::IBM437
result = str.rjust 5
result.should == " abc"
result.encoding.should equal(Encoding::IBM437)
@@ -97,7 +84,7 @@ describe "String#rjust with length, padding" do
describe "with width, pattern" do
it "returns a String in the compatible encoding" do
- str = "abc".force_encoding Encoding::IBM437
+ str = "abc".dup.force_encoding Encoding::IBM437
result = str.rjust 5, "ã‚"
result.should == "ã‚ã‚abc"
result.encoding.should equal(Encoding::UTF_8)
diff --git a/spec/ruby/core/string/rpartition_spec.rb b/spec/ruby/core/string/rpartition_spec.rb
index 21e87f530a..cef0384c73 100644
--- a/spec/ruby/core/string/rpartition_spec.rb
+++ b/spec/ruby/core/string/rpartition_spec.rb
@@ -48,7 +48,7 @@ describe "String#rpartition with String" do
end
it "handles a pattern in a superset encoding" do
- string = "hello".force_encoding(Encoding::US_ASCII)
+ string = "hello".dup.force_encoding(Encoding::US_ASCII)
result = string.rpartition("é")
@@ -59,7 +59,7 @@ describe "String#rpartition with String" do
end
it "handles a pattern in a subset encoding" do
- pattern = "o".force_encoding(Encoding::US_ASCII)
+ pattern = "o".dup.force_encoding(Encoding::US_ASCII)
result = "héllo world".rpartition(pattern)
diff --git a/spec/ruby/core/string/rstrip_spec.rb b/spec/ruby/core/string/rstrip_spec.rb
index e96ce4120f..6d46eb590e 100644
--- a/spec/ruby/core/string/rstrip_spec.rb
+++ b/spec/ruby/core/string/rstrip_spec.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
require_relative 'shared/strip'
@@ -51,12 +52,10 @@ describe "String#rstrip!" do
" ".rstrip.should == ""
end
- ruby_version_is '3.0' do
- it "removes trailing NULL bytes and whitespace" do
- a = "\000 goodbye \000"
- a.rstrip!
- a.should == "\000 goodbye"
- end
+ it "removes trailing NULL bytes and whitespace" do
+ a = "\000 goodbye \000"
+ a.rstrip!
+ a.should == "\000 goodbye"
end
it "raises a FrozenError on a frozen instance that is modified" do
diff --git a/spec/ruby/core/string/scan_spec.rb b/spec/ruby/core/string/scan_spec.rb
index a2d1815132..70c3b7fb7b 100644
--- a/spec/ruby/core/string/scan_spec.rb
+++ b/spec/ruby/core/string/scan_spec.rb
@@ -165,11 +165,9 @@ describe "String#scan with pattern and block" do
end
end
- ruby_version_is '3.0' do
- it "yields String instances for subclasses" do
- a = []
- StringSpecs::MyString.new("abc").scan(/./) { |s| a << s.class }
- a.should == [String, String, String]
- end
+ it "yields String instances for subclasses" do
+ a = []
+ StringSpecs::MyString.new("abc").scan(/./) { |s| a << s.class }
+ a.should == [String, String, String]
end
end
diff --git a/spec/ruby/core/string/scrub_spec.rb b/spec/ruby/core/string/scrub_spec.rb
index a51fbd020a..b9ef0f1a16 100644
--- a/spec/ruby/core/string/scrub_spec.rb
+++ b/spec/ruby/core/string/scrub_spec.rb
@@ -1,4 +1,5 @@
# -*- encoding: utf-8 -*-
+# frozen_string_literal: false
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
@@ -36,18 +37,10 @@ describe "String#scrub with a default replacement" do
"abc\u3042#{x81}".scrub.encoding.should == Encoding::UTF_8
end
- ruby_version_is '3.0' do
- it "returns String instances when called on a subclass" do
- StringSpecs::MyString.new("foo").scrub.should be_an_instance_of(String)
- input = [0x81].pack('C').force_encoding('utf-8')
- StringSpecs::MyString.new(input).scrub.should be_an_instance_of(String)
- end
- end
-
- ruby_version_is ''...'3.0' do
- it "returns subclass instances when called on a subclass" do
- StringSpecs::MyString.new("foo").scrub.should be_an_instance_of(StringSpecs::MyString)
- end
+ it "returns String instances when called on a subclass" do
+ StringSpecs::MyString.new("foo").scrub.should be_an_instance_of(String)
+ input = [0x81].pack('C').force_encoding('utf-8')
+ StringSpecs::MyString.new(input).scrub.should be_an_instance_of(String)
end
end
@@ -97,12 +90,10 @@ describe "String#scrub with a custom replacement" do
block.should raise_error(TypeError)
end
- ruby_version_is '3.0' do
- it "returns String instances when called on a subclass" do
- StringSpecs::MyString.new("foo").scrub("*").should be_an_instance_of(String)
- input = [0x81].pack('C').force_encoding('utf-8')
- StringSpecs::MyString.new(input).scrub("*").should be_an_instance_of(String)
- end
+ it "returns String instances when called on a subclass" do
+ StringSpecs::MyString.new("foo").scrub("*").should be_an_instance_of(String)
+ input = [0x81].pack('C').force_encoding('utf-8')
+ StringSpecs::MyString.new(input).scrub("*").should be_an_instance_of(String)
end
end
@@ -129,12 +120,10 @@ describe "String#scrub with a block" do
replaced.should == "€€"
end
- ruby_version_is '3.0' do
- it "returns String instances when called on a subclass" do
- StringSpecs::MyString.new("foo").scrub { |b| "*" }.should be_an_instance_of(String)
- input = [0x81].pack('C').force_encoding('utf-8')
- StringSpecs::MyString.new(input).scrub { |b| "<#{b.unpack("H*")[0]}>" }.should be_an_instance_of(String)
- end
+ it "returns String instances when called on a subclass" do
+ StringSpecs::MyString.new("foo").scrub { |b| "*" }.should be_an_instance_of(String)
+ input = [0x81].pack('C').force_encoding('utf-8')
+ StringSpecs::MyString.new(input).scrub { |b| "<#{b.unpack("H*")[0]}>" }.should be_an_instance_of(String)
end
end
diff --git a/spec/ruby/core/string/setbyte_spec.rb b/spec/ruby/core/string/setbyte_spec.rb
index 77bff64038..85403ca62c 100644
--- a/spec/ruby/core/string/setbyte_spec.rb
+++ b/spec/ruby/core/string/setbyte_spec.rb
@@ -1,4 +1,5 @@
# -*- encoding: utf-8 -*-
+# frozen_string_literal: false
require_relative '../../spec_helper'
describe "String#setbyte" do
diff --git a/spec/ruby/core/string/shared/byte_index_common.rb b/spec/ruby/core/string/shared/byte_index_common.rb
new file mode 100644
index 0000000000..3de1453f4f
--- /dev/null
+++ b/spec/ruby/core/string/shared/byte_index_common.rb
@@ -0,0 +1,63 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../../spec_helper'
+
+describe :byte_index_common, shared: true do
+ describe "raises on type errors" do
+ it "raises a TypeError if passed nil" do
+ -> { "abc".send(@method, nil) }.should raise_error(TypeError, "no implicit conversion of nil into String")
+ end
+
+ it "raises a TypeError if passed a boolean" do
+ -> { "abc".send(@method, true) }.should raise_error(TypeError, "no implicit conversion of true into String")
+ end
+
+ it "raises a TypeError if passed a Symbol" do
+ not_supported_on :opal do
+ -> { "abc".send(@method, :a) }.should raise_error(TypeError, "no implicit conversion of Symbol into String")
+ end
+ end
+
+ it "raises a TypeError if passed a Symbol" do
+ obj = mock('x')
+ obj.should_not_receive(:to_int)
+ -> { "hello".send(@method, obj) }.should raise_error(TypeError, "no implicit conversion of MockObject into String")
+ end
+
+ it "raises a TypeError if passed an Integer" do
+ -> { "abc".send(@method, 97) }.should raise_error(TypeError, "no implicit conversion of Integer into String")
+ end
+ end
+
+ describe "with multibyte codepoints" do
+ it "raises an IndexError when byte offset lands in the middle of a multibyte character" do
+ -> { "ã‚".send(@method, "", 1) }.should raise_error(IndexError, "offset 1 does not land on character boundary")
+ -> { "ã‚".send(@method, "", 2) }.should raise_error(IndexError, "offset 2 does not land on character boundary")
+ -> { "ã‚".send(@method, "", -1) }.should raise_error(IndexError, "offset 2 does not land on character boundary")
+ -> { "ã‚".send(@method, "", -2) }.should raise_error(IndexError, "offset 1 does not land on character boundary")
+ end
+
+ it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
+ re = Regexp.new "れ".encode(Encoding::EUC_JP)
+ -> do
+ "ã‚ã‚Œ".send(@method, re)
+ end.should raise_error(Encoding::CompatibilityError, "incompatible encoding regexp match (EUC-JP regexp with UTF-8 string)")
+ end
+ end
+
+ describe "with global variables" do
+ it "doesn't set $~ for non regex search" do
+ $~ = nil
+
+ 'hello.'.send(@method, 'll')
+ $~.should == nil
+ end
+
+ it "sets $~ to MatchData of match and nil when there's none" do
+ 'hello.'.send(@method, /.e./)
+ $~[0].should == 'hel'
+
+ 'hello.'.send(@method, /not/)
+ $~.should == nil
+ end
+ end
+end
diff --git a/spec/ruby/core/string/shared/chars.rb b/spec/ruby/core/string/shared/chars.rb
index e9fdf89fd6..c730643cf4 100644
--- a/spec/ruby/core/string/shared/chars.rb
+++ b/spec/ruby/core/string/shared/chars.rb
@@ -21,12 +21,12 @@ describe :string_chars, shared: true do
end
it "returns characters in the same encoding as self" do
- "&%".force_encoding('Shift_JIS').send(@method).to_a.all? {|c| c.encoding.name.should == 'Shift_JIS'}
+ "&%".dup.force_encoding('Shift_JIS').send(@method).to_a.all? {|c| c.encoding.name.should == 'Shift_JIS'}
"&%".encode('BINARY').send(@method).to_a.all? {|c| c.encoding.should == Encoding::BINARY }
end
it "works with multibyte characters" do
- s = "\u{8987}".force_encoding("UTF-8")
+ s = "\u{8987}".dup.force_encoding("UTF-8")
s.bytesize.should == 3
s.send(@method).to_a.should == [s]
end
@@ -39,14 +39,14 @@ describe :string_chars, shared: true do
end
it "returns a different character if the String is transcoded" do
- s = "\u{20AC}".force_encoding('UTF-8')
- s.encode('UTF-8').send(@method).to_a.should == ["\u{20AC}".force_encoding('UTF-8')]
+ s = "\u{20AC}".dup.force_encoding('UTF-8')
+ s.encode('UTF-8').send(@method).to_a.should == ["\u{20AC}".dup.force_encoding('UTF-8')]
s.encode('iso-8859-15').send(@method).to_a.should == [[0xA4].pack('C').force_encoding('iso-8859-15')]
- s.encode('iso-8859-15').encode('UTF-8').send(@method).to_a.should == ["\u{20AC}".force_encoding('UTF-8')]
+ s.encode('iso-8859-15').encode('UTF-8').send(@method).to_a.should == ["\u{20AC}".dup.force_encoding('UTF-8')]
end
it "uses the String's encoding to determine what characters it contains" do
- s = "\u{24B62}"
+ s = +"\u{24B62}"
s.force_encoding('UTF-8').send(@method).to_a.should == [
s.force_encoding('UTF-8')
diff --git a/spec/ruby/core/string/shared/codepoints.rb b/spec/ruby/core/string/shared/codepoints.rb
index 0b2e078e0a..f71263054a 100644
--- a/spec/ruby/core/string/shared/codepoints.rb
+++ b/spec/ruby/core/string/shared/codepoints.rb
@@ -7,7 +7,7 @@ describe :string_codepoints, shared: true do
end
it "raises an ArgumentError when self has an invalid encoding and a method is called on the returned Enumerator" do
- s = "\xDF".force_encoding(Encoding::UTF_8)
+ s = "\xDF".dup.force_encoding(Encoding::UTF_8)
s.valid_encoding?.should be_false
-> { s.send(@method).to_a }.should raise_error(ArgumentError)
end
@@ -21,7 +21,7 @@ describe :string_codepoints, shared: true do
end
it "raises an ArgumentError if self's encoding is invalid and a block is given" do
- s = "\xDF".force_encoding(Encoding::UTF_8)
+ s = "\xDF".dup.force_encoding(Encoding::UTF_8)
s.valid_encoding?.should be_false
-> { s.send(@method) { } }.should raise_error(ArgumentError)
end
@@ -49,7 +49,7 @@ describe :string_codepoints, shared: true do
it "round-trips to the original String using Integer#chr" do
s = "\u{13}\u{7711}\u{1010}"
- s2 = ""
+ s2 = +""
s.send(@method) {|n| s2 << n.chr(Encoding::UTF_8)}
s.should == s2
end
diff --git a/spec/ruby/core/string/shared/concat.rb b/spec/ruby/core/string/shared/concat.rb
index ee5ef2a98f..dded9a69e7 100644
--- a/spec/ruby/core/string/shared/concat.rb
+++ b/spec/ruby/core/string/shared/concat.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
describe :string_concat, shared: true do
it "concatenates the given argument to self and returns self" do
str = 'hello '
diff --git a/spec/ruby/core/string/shared/dedup.rb b/spec/ruby/core/string/shared/dedup.rb
index 6ffcb9b045..97b5df6ed1 100644
--- a/spec/ruby/core/string/shared/dedup.rb
+++ b/spec/ruby/core/string/shared/dedup.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
describe :string_dedup, shared: true do
it 'returns self if the String is frozen' do
input = 'foo'.freeze
@@ -48,10 +49,8 @@ describe :string_dedup, shared: true do
dynamic.send(@method).should equal(dynamic)
end
- ruby_version_is "3.0" do
- it "interns the provided string if it is frozen" do
- dynamic = "this string is unique and frozen #{rand}".freeze
- dynamic.send(@method).should equal(dynamic)
- end
+ it "interns the provided string if it is frozen" do
+ dynamic = "this string is unique and frozen #{rand}".freeze
+ dynamic.send(@method).should equal(dynamic)
end
end
diff --git a/spec/ruby/core/string/shared/each_codepoint_without_block.rb b/spec/ruby/core/string/shared/each_codepoint_without_block.rb
index 92b7f76032..31b4c02c9c 100644
--- a/spec/ruby/core/string/shared/each_codepoint_without_block.rb
+++ b/spec/ruby/core/string/shared/each_codepoint_without_block.rb
@@ -6,7 +6,7 @@ describe :string_each_codepoint_without_block, shared: true do
end
it "returns an Enumerator even when self has an invalid encoding" do
- s = "\xDF".force_encoding(Encoding::UTF_8)
+ s = "\xDF".dup.force_encoding(Encoding::UTF_8)
s.valid_encoding?.should be_false
s.send(@method).should be_an_instance_of(Enumerator)
end
@@ -23,7 +23,7 @@ describe :string_each_codepoint_without_block, shared: true do
end
it "should return the size of the string even when the string has an invalid encoding" do
- s = "\xDF".force_encoding(Encoding::UTF_8)
+ s = "\xDF".dup.force_encoding(Encoding::UTF_8)
s.valid_encoding?.should be_false
s.send(@method).size.should == 1
end
diff --git a/spec/ruby/core/string/shared/each_line.rb b/spec/ruby/core/string/shared/each_line.rb
index df78bd2186..231a6d9d4f 100644
--- a/spec/ruby/core/string/shared/each_line.rb
+++ b/spec/ruby/core/string/shared/each_line.rb
@@ -85,20 +85,10 @@ describe :string_each_line, shared: true do
end
end
- ruby_version_is ''...'3.0' do
- it "yields subclass instances for subclasses" do
- a = []
- StringSpecs::MyString.new("hello\nworld").send(@method) { |s| a << s.class }
- a.should == [StringSpecs::MyString, StringSpecs::MyString]
- end
- end
-
- ruby_version_is '3.0' do
- it "yields String instances for subclasses" do
- a = []
- StringSpecs::MyString.new("hello\nworld").send(@method) { |s| a << s.class }
- a.should == [String, String]
- end
+ it "yields String instances for subclasses" do
+ a = []
+ StringSpecs::MyString.new("hello\nworld").send(@method) { |s| a << s.class }
+ a.should == [String, String]
end
it "returns self" do
@@ -116,7 +106,7 @@ describe :string_each_line, shared: true do
end
it "does not care if the string is modified while substituting" do
- str = "hello\nworld."
+ str = +"hello\nworld."
out = []
str.send(@method){|x| out << x; str[-1] = '!' }.should == "hello\nworld!"
out.should == ["hello\n", "world."]
diff --git a/spec/ruby/core/string/shared/encode.rb b/spec/ruby/core/string/shared/encode.rb
index a73de5b943..3776e0d709 100644
--- a/spec/ruby/core/string/shared/encode.rb
+++ b/spec/ruby/core/string/shared/encode.rb
@@ -1,4 +1,5 @@
# -*- encoding: utf-8 -*-
+# frozen_string_literal: false
describe :string_encode, shared: true do
describe "when passed no options" do
it "transcodes to Encoding.default_internal when set" do
diff --git a/spec/ruby/core/string/shared/eql.rb b/spec/ruby/core/string/shared/eql.rb
index 6f268c929c..845b0a3e15 100644
--- a/spec/ruby/core/string/shared/eql.rb
+++ b/spec/ruby/core/string/shared/eql.rb
@@ -13,15 +13,15 @@ describe :string_eql_value, shared: true do
end
it "ignores encoding difference of compatible string" do
- "hello".force_encoding("utf-8").send(@method, "hello".force_encoding("iso-8859-1")).should be_true
+ "hello".dup.force_encoding("utf-8").send(@method, "hello".dup.force_encoding("iso-8859-1")).should be_true
end
it "considers encoding difference of incompatible string" do
- "\xff".force_encoding("utf-8").send(@method, "\xff".force_encoding("iso-8859-1")).should be_false
+ "\xff".dup.force_encoding("utf-8").send(@method, "\xff".dup.force_encoding("iso-8859-1")).should be_false
end
it "considers encoding compatibility" do
- "abcd".force_encoding("utf-8").send(@method, "abcd".force_encoding("utf-32le")).should be_false
+ "abcd".dup.force_encoding("utf-8").send(@method, "abcd".dup.force_encoding("utf-32le")).should be_false
end
it "ignores subclass differences" do
@@ -33,6 +33,6 @@ describe :string_eql_value, shared: true do
end
it "returns true when comparing 2 empty strings but one is not ASCII-compatible" do
- "".send(@method, "".force_encoding('iso-2022-jp')).should == true
+ "".send(@method, "".dup.force_encoding('iso-2022-jp')).should == true
end
end
diff --git a/spec/ruby/core/string/shared/length.rb b/spec/ruby/core/string/shared/length.rb
index 94e5ec135b..ae572ba755 100644
--- a/spec/ruby/core/string/shared/length.rb
+++ b/spec/ruby/core/string/shared/length.rb
@@ -18,7 +18,7 @@ describe :string_length, shared: true do
end
it "returns the length of the new self after encoding is changed" do
- str = 'ã“ã«ã¡ã‚'
+ str = +'ã“ã«ã¡ã‚'
str.send(@method)
str.force_encoding('BINARY').send(@method).should == 12
@@ -44,12 +44,12 @@ describe :string_length, shared: true do
end
it "adds 1 (and not 2) for a incomplete surrogate in UTF-16" do
- "\x00\xd8".force_encoding("UTF-16LE").send(@method).should == 1
- "\xd8\x00".force_encoding("UTF-16BE").send(@method).should == 1
+ "\x00\xd8".dup.force_encoding("UTF-16LE").send(@method).should == 1
+ "\xd8\x00".dup.force_encoding("UTF-16BE").send(@method).should == 1
end
it "adds 1 for a broken sequence in UTF-32" do
- "\x04\x03\x02\x01".force_encoding("UTF-32LE").send(@method).should == 1
- "\x01\x02\x03\x04".force_encoding("UTF-32BE").send(@method).should == 1
+ "\x04\x03\x02\x01".dup.force_encoding("UTF-32LE").send(@method).should == 1
+ "\x01\x02\x03\x04".dup.force_encoding("UTF-32BE").send(@method).should == 1
end
end
diff --git a/spec/ruby/core/string/shared/partition.rb b/spec/ruby/core/string/shared/partition.rb
index 41b3c7e0c9..4cac149ce5 100644
--- a/spec/ruby/core/string/shared/partition.rb
+++ b/spec/ruby/core/string/shared/partition.rb
@@ -2,35 +2,17 @@ require_relative '../../../spec_helper'
require_relative '../fixtures/classes'
describe :string_partition, shared: true do
- ruby_version_is '3.0' do
- it "returns String instances when called on a subclass" do
- StringSpecs::MyString.new("hello").send(@method, "l").each do |item|
- item.should be_an_instance_of(String)
- end
-
- StringSpecs::MyString.new("hello").send(@method, "x").each do |item|
- item.should be_an_instance_of(String)
- end
-
- StringSpecs::MyString.new("hello").send(@method, /l./).each do |item|
- item.should be_an_instance_of(String)
- end
+ it "returns String instances when called on a subclass" do
+ StringSpecs::MyString.new("hello").send(@method, "l").each do |item|
+ item.should be_an_instance_of(String)
end
- end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances when called on a subclass" do
- StringSpecs::MyString.new("hello").send(@method, StringSpecs::MyString.new("l")).each do |item|
- item.should be_an_instance_of(StringSpecs::MyString)
- end
-
- StringSpecs::MyString.new("hello").send(@method, "x").each do |item|
- item.should be_an_instance_of(StringSpecs::MyString)
- end
+ StringSpecs::MyString.new("hello").send(@method, "x").each do |item|
+ item.should be_an_instance_of(String)
+ end
- StringSpecs::MyString.new("hello").send(@method, /l./).each do |item|
- item.should be_an_instance_of(StringSpecs::MyString)
- end
+ StringSpecs::MyString.new("hello").send(@method, /l./).each do |item|
+ item.should be_an_instance_of(String)
end
end
diff --git a/spec/ruby/core/string/shared/replace.rb b/spec/ruby/core/string/shared/replace.rb
index a5108d9e7c..24dac0eb27 100644
--- a/spec/ruby/core/string/shared/replace.rb
+++ b/spec/ruby/core/string/shared/replace.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
describe :string_replace, shared: true do
it "returns self" do
a = "a"
diff --git a/spec/ruby/core/string/shared/slice.rb b/spec/ruby/core/string/shared/slice.rb
index a7c1d05b56..2f69b9ddce 100644
--- a/spec/ruby/core/string/shared/slice.rb
+++ b/spec/ruby/core/string/shared/slice.rb
@@ -84,8 +84,8 @@ describe :string_slice_index_length, shared: true do
s = "hello there"
s.send(@method, 1, 9).encoding.should == s.encoding
- a = "hello".force_encoding("binary")
- b = " there".force_encoding("ISO-8859-1")
+ a = "hello".dup.force_encoding("binary")
+ b = " there".dup.force_encoding("ISO-8859-1")
c = (a + b).force_encoding(Encoding::US_ASCII)
c.send(@method, 0, 5).encoding.should == Encoding::US_ASCII
@@ -152,22 +152,11 @@ describe :string_slice_index_length, shared: true do
-> { "hello".send(@method, 0, bignum_value) }.should raise_error(RangeError)
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances" do
- s = StringSpecs::MyString.new("hello")
- s.send(@method, 0,0).should be_an_instance_of(StringSpecs::MyString)
- s.send(@method, 0,4).should be_an_instance_of(StringSpecs::MyString)
- s.send(@method, 1,4).should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns String instances" do
- s = StringSpecs::MyString.new("hello")
- s.send(@method, 0,0).should be_an_instance_of(String)
- s.send(@method, 0,4).should be_an_instance_of(String)
- s.send(@method, 1,4).should be_an_instance_of(String)
- end
+ it "returns String instances" do
+ s = StringSpecs::MyString.new("hello")
+ s.send(@method, 0,0).should be_an_instance_of(String)
+ s.send(@method, 0,4).should be_an_instance_of(String)
+ s.send(@method, 1,4).should be_an_instance_of(String)
end
it "handles repeated application" do
@@ -242,22 +231,11 @@ describe :string_slice_range, shared: true do
"x".send(@method, 1...-1).should == ""
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances" do
- s = StringSpecs::MyString.new("hello")
- s.send(@method, 0...0).should be_an_instance_of(StringSpecs::MyString)
- s.send(@method, 0..4).should be_an_instance_of(StringSpecs::MyString)
- s.send(@method, 1..4).should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns String instances" do
- s = StringSpecs::MyString.new("hello")
- s.send(@method, 0...0).should be_an_instance_of(String)
- s.send(@method, 0..4).should be_an_instance_of(String)
- s.send(@method, 1..4).should be_an_instance_of(String)
- end
+ it "returns String instances" do
+ s = StringSpecs::MyString.new("hello")
+ s.send(@method, 0...0).should be_an_instance_of(String)
+ s.send(@method, 0..4).should be_an_instance_of(String)
+ s.send(@method, 1..4).should be_an_instance_of(String)
end
it "calls to_int on range arguments" do
@@ -336,20 +314,10 @@ describe :string_slice_regexp, shared: true do
"hello there".encode("US-ASCII").send(@method, /[aeiou](.)\1/).encoding.should == Encoding::US_ASCII
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances" do
- s = StringSpecs::MyString.new("hello")
- s.send(@method, //).should be_an_instance_of(StringSpecs::MyString)
- s.send(@method, /../).should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns String instances" do
- s = StringSpecs::MyString.new("hello")
- s.send(@method, //).should be_an_instance_of(String)
- s.send(@method, /../).should be_an_instance_of(String)
- end
+ it "returns String instances" do
+ s = StringSpecs::MyString.new("hello")
+ s.send(@method, //).should be_an_instance_of(String)
+ s.send(@method, /../).should be_an_instance_of(String)
end
it "sets $~ to MatchData when there is a match and nil when there's none" do
@@ -418,20 +386,10 @@ describe :string_slice_regexp_index, shared: true do
-> { "hello".send(@method, /(.)(.)(.)/, nil) }.should raise_error(TypeError)
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances" do
- s = StringSpecs::MyString.new("hello")
- s.send(@method, /(.)(.)/, 0).should be_an_instance_of(StringSpecs::MyString)
- s.send(@method, /(.)(.)/, 1).should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns String instances" do
- s = StringSpecs::MyString.new("hello")
- s.send(@method, /(.)(.)/, 0).should be_an_instance_of(String)
- s.send(@method, /(.)(.)/, 1).should be_an_instance_of(String)
- end
+ it "returns String instances" do
+ s = StringSpecs::MyString.new("hello")
+ s.send(@method, /(.)(.)/, 0).should be_an_instance_of(String)
+ s.send(@method, /(.)(.)/, 1).should be_an_instance_of(String)
end
it "sets $~ to MatchData when there is a match and nil when there's none" do
@@ -470,22 +428,11 @@ describe :string_slice_string, shared: true do
-> { "hello".send(@method, o) }.should raise_error(TypeError)
end
- ruby_version_is ''...'3.0' do
- it "returns a subclass instance when given a subclass instance" do
- s = StringSpecs::MyString.new("el")
- r = "hello".send(@method, s)
- r.should == "el"
- r.should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns a String instance when given a subclass instance" do
- s = StringSpecs::MyString.new("el")
- r = "hello".send(@method, s)
- r.should == "el"
- r.should be_an_instance_of(String)
- end
+ it "returns a String instance when given a subclass instance" do
+ s = StringSpecs::MyString.new("el")
+ r = "hello".send(@method, s)
+ r.should == "el"
+ r.should be_an_instance_of(String)
end
end
@@ -531,18 +478,9 @@ describe :string_slice_regexp_group, shared: true do
-> { "hello".send(@method, /(?<q>)/, '') }.should raise_error(IndexError)
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances" do
- s = StringSpecs::MyString.new("hello")
- s.send(@method, /(?<q>.)/, 'q').should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns String instances" do
- s = StringSpecs::MyString.new("hello")
- s.send(@method, /(?<q>.)/, 'q').should be_an_instance_of(String)
- end
+ it "returns String instances" do
+ s = StringSpecs::MyString.new("hello")
+ s.send(@method, /(?<q>.)/, 'q').should be_an_instance_of(String)
end
it "sets $~ to MatchData when there is a match and nil when there's none" do
diff --git a/spec/ruby/core/string/shared/strip.rb b/spec/ruby/core/string/shared/strip.rb
index 0c0aae20f3..3af77b50fe 100644
--- a/spec/ruby/core/string/shared/strip.rb
+++ b/spec/ruby/core/string/shared/strip.rb
@@ -6,19 +6,9 @@ describe :string_strip, shared: true do
" hello ".encode("US-ASCII").send(@method).encoding.should == Encoding::US_ASCII
end
- ruby_version_is '3.0' do
- it "returns String instances when called on a subclass" do
- StringSpecs::MyString.new(" hello ").send(@method).should be_an_instance_of(String)
- StringSpecs::MyString.new(" ").send(@method).should be_an_instance_of(String)
- StringSpecs::MyString.new("").send(@method).should be_an_instance_of(String)
- end
- end
-
- ruby_version_is ''...'3.0' do
- it "returns subclass instances when called on a subclass" do
- StringSpecs::MyString.new(" hello ").send(@method).should be_an_instance_of(StringSpecs::MyString)
- StringSpecs::MyString.new(" ").send(@method).should be_an_instance_of(StringSpecs::MyString)
- StringSpecs::MyString.new("").send(@method).should be_an_instance_of(StringSpecs::MyString)
- end
+ it "returns String instances when called on a subclass" do
+ StringSpecs::MyString.new(" hello ").send(@method).should be_an_instance_of(String)
+ StringSpecs::MyString.new(" ").send(@method).should be_an_instance_of(String)
+ StringSpecs::MyString.new("").send(@method).should be_an_instance_of(String)
end
end
diff --git a/spec/ruby/core/string/shared/succ.rb b/spec/ruby/core/string/shared/succ.rb
index 3605fa99a2..b69a394875 100644
--- a/spec/ruby/core/string/shared/succ.rb
+++ b/spec/ruby/core/string/shared/succ.rb
@@ -59,20 +59,10 @@ describe :string_succ, shared: true do
"\xFF\xFF".send(@method).should == "\x01\x00\x00"
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances when called on a subclass" do
- StringSpecs::MyString.new("").send(@method).should be_an_instance_of(StringSpecs::MyString)
- StringSpecs::MyString.new("a").send(@method).should be_an_instance_of(StringSpecs::MyString)
- StringSpecs::MyString.new("z").send(@method).should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns String instances when called on a subclass" do
- StringSpecs::MyString.new("").send(@method).should be_an_instance_of(String)
- StringSpecs::MyString.new("a").send(@method).should be_an_instance_of(String)
- StringSpecs::MyString.new("z").send(@method).should be_an_instance_of(String)
- end
+ it "returns String instances when called on a subclass" do
+ StringSpecs::MyString.new("").send(@method).should be_an_instance_of(String)
+ StringSpecs::MyString.new("a").send(@method).should be_an_instance_of(String)
+ StringSpecs::MyString.new("z").send(@method).should be_an_instance_of(String)
end
it "returns a String in the same encoding as self" do
@@ -83,6 +73,7 @@ end
describe :string_succ_bang, shared: true do
it "is equivalent to succ, but modifies self in place (still returns self)" do
["", "abcd", "THX1138"].each do |s|
+ s = +s
r = s.dup.send(@method)
s.send(@method).should equal(s)
s.should == r
diff --git a/spec/ruby/core/string/shared/to_a.rb b/spec/ruby/core/string/shared/to_a.rb
deleted file mode 100644
index bad3ea6584..0000000000
--- a/spec/ruby/core/string/shared/to_a.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-describe :string_to_a, shared: true do
- it "returns an empty array for empty strings" do
- "".send(@method).should == []
- end
-
- it "returns an array containing the string for non-empty strings" do
- "hello".send(@method).should == ["hello"]
- end
-end
diff --git a/spec/ruby/core/string/shared/to_sym.rb b/spec/ruby/core/string/shared/to_sym.rb
index 52d8314211..833eae100e 100644
--- a/spec/ruby/core/string/shared/to_sym.rb
+++ b/spec/ruby/core/string/shared/to_sym.rb
@@ -56,9 +56,9 @@ describe :string_to_sym, shared: true do
it "ignores existing symbols with different encoding" do
source = "fée"
- iso_symbol = source.force_encoding(Encoding::ISO_8859_1).send(@method)
+ iso_symbol = source.dup.force_encoding(Encoding::ISO_8859_1).send(@method)
iso_symbol.encoding.should == Encoding::ISO_8859_1
- binary_symbol = source.force_encoding(Encoding::BINARY).send(@method)
+ binary_symbol = source.dup.force_encoding(Encoding::BINARY).send(@method)
binary_symbol.encoding.should == Encoding::BINARY
end
diff --git a/spec/ruby/core/string/slice_spec.rb b/spec/ruby/core/string/slice_spec.rb
index c9e13ed1bc..5aba2d3be0 100644
--- a/spec/ruby/core/string/slice_spec.rb
+++ b/spec/ruby/core/string/slice_spec.rb
@@ -1,5 +1,5 @@
# -*- encoding: utf-8 -*-
-
+# frozen_string_literal: false
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
require_relative 'shared/slice'
@@ -132,20 +132,10 @@ describe "String#slice! with index, length" do
"hello".slice!(obj, obj).should == "ll"
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances" do
- s = StringSpecs::MyString.new("hello")
- s.slice!(0, 0).should be_an_instance_of(StringSpecs::MyString)
- s.slice!(0, 4).should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns String instances" do
- s = StringSpecs::MyString.new("hello")
- s.slice!(0, 0).should be_an_instance_of(String)
- s.slice!(0, 4).should be_an_instance_of(String)
- end
+ it "returns String instances" do
+ s = StringSpecs::MyString.new("hello")
+ s.slice!(0, 0).should be_an_instance_of(String)
+ s.slice!(0, 4).should be_an_instance_of(String)
end
it "returns the substring given by the character offsets" do
@@ -185,20 +175,10 @@ describe "String#slice! Range" do
b.should == "hello"
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances" do
- s = StringSpecs::MyString.new("hello")
- s.slice!(0...0).should be_an_instance_of(StringSpecs::MyString)
- s.slice!(0..4).should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns String instances" do
- s = StringSpecs::MyString.new("hello")
- s.slice!(0...0).should be_an_instance_of(String)
- s.slice!(0..4).should be_an_instance_of(String)
- end
+ it "returns String instances" do
+ s = StringSpecs::MyString.new("hello")
+ s.slice!(0...0).should be_an_instance_of(String)
+ s.slice!(0..4).should be_an_instance_of(String)
end
it "calls to_int on range arguments" do
@@ -274,20 +254,10 @@ describe "String#slice! with Regexp" do
s.should == "this is a string"
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances" do
- s = StringSpecs::MyString.new("hello")
- s.slice!(//).should be_an_instance_of(StringSpecs::MyString)
- s.slice!(/../).should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns String instances" do
- s = StringSpecs::MyString.new("hello")
- s.slice!(//).should be_an_instance_of(String)
- s.slice!(/../).should be_an_instance_of(String)
- end
+ it "returns String instances" do
+ s = StringSpecs::MyString.new("hello")
+ s.slice!(//).should be_an_instance_of(String)
+ s.slice!(/../).should be_an_instance_of(String)
end
it "returns the matching portion of self with a multi byte character" do
@@ -344,20 +314,10 @@ describe "String#slice! with Regexp, index" do
"har".slice!(/(.)(.)(.)/, obj).should == "a"
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances" do
- s = StringSpecs::MyString.new("hello")
- s.slice!(/(.)(.)/, 0).should be_an_instance_of(StringSpecs::MyString)
- s.slice!(/(.)(.)/, 1).should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns String instances" do
- s = StringSpecs::MyString.new("hello")
- s.slice!(/(.)(.)/, 0).should be_an_instance_of(String)
- s.slice!(/(.)(.)/, 1).should be_an_instance_of(String)
- end
+ it "returns String instances" do
+ s = StringSpecs::MyString.new("hello")
+ s.slice!(/(.)(.)/, 0).should be_an_instance_of(String)
+ s.slice!(/(.)(.)/, 1).should be_an_instance_of(String)
end
it "returns the encoding aware capture for the given index" do
@@ -415,22 +375,11 @@ describe "String#slice! with String" do
-> { "hello".slice!(o) }.should raise_error(TypeError)
end
- ruby_version_is ''...'3.0' do
- it "returns a subclass instance when given a subclass instance" do
- s = StringSpecs::MyString.new("el")
- r = "hello".slice!(s)
- r.should == "el"
- r.should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns a subclass instance when given a subclass instance" do
- s = StringSpecs::MyString.new("el")
- r = "hello".slice!(s)
- r.should == "el"
- r.should be_an_instance_of(String)
- end
+ it "returns a subclass instance when given a subclass instance" do
+ s = StringSpecs::MyString.new("el")
+ r = "hello".slice!(s)
+ r.should == "el"
+ r.should be_an_instance_of(String)
end
it "raises a FrozenError if self is frozen" do
diff --git a/spec/ruby/core/string/split_spec.rb b/spec/ruby/core/string/split_spec.rb
index 519c5d845d..3c6d1864d1 100644
--- a/spec/ruby/core/string/split_spec.rb
+++ b/spec/ruby/core/string/split_spec.rb
@@ -4,7 +4,7 @@ require_relative 'fixtures/classes'
describe "String#split with String" do
it "throws an ArgumentError if the string is not a valid" do
- s = "\xDF".force_encoding(Encoding::UTF_8)
+ s = "\xDF".dup.force_encoding(Encoding::UTF_8)
-> { s.split }.should raise_error(ArgumentError)
-> { s.split(':') }.should raise_error(ArgumentError)
@@ -12,7 +12,7 @@ describe "String#split with String" do
it "throws an ArgumentError if the pattern is not a valid string" do
str = 'проверка'
- broken_str = "\xDF".force_encoding(Encoding::UTF_8)
+ broken_str = "\xDF".dup.force_encoding(Encoding::UTF_8)
-> { str.split(broken_str) }.should raise_error(ArgumentError)
end
@@ -192,44 +192,16 @@ describe "String#split with String" do
"foo".split("bar", 3).should == ["foo"]
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances based on self" do
- ["", "x.y.z.", " x y "].each do |str|
- ["", ".", " "].each do |pat|
- [-1, 0, 1, 2].each do |limit|
- StringSpecs::MyString.new(str).split(pat, limit).each do |x|
- x.should be_an_instance_of(StringSpecs::MyString)
- end
-
- str.split(StringSpecs::MyString.new(pat), limit).each do |x|
- x.should be_an_instance_of(String)
- end
+ it "returns String instances based on self" do
+ ["", "x.y.z.", " x y "].each do |str|
+ ["", ".", " "].each do |pat|
+ [-1, 0, 1, 2].each do |limit|
+ StringSpecs::MyString.new(str).split(pat, limit).each do |x|
+ x.should be_an_instance_of(String)
end
- end
- end
- end
- it "does not call constructor on created subclass instances" do
- # can't call should_not_receive on an object that doesn't yet exist
- # so failure here is signalled by exception, not expectation failure
-
- s = StringSpecs::StringWithRaisingConstructor.new('silly:string')
- s.split(':').first.should == 'silly'
- end
- end
-
- ruby_version_is '3.0' do
- it "returns String instances based on self" do
- ["", "x.y.z.", " x y "].each do |str|
- ["", ".", " "].each do |pat|
- [-1, 0, 1, 2].each do |limit|
- StringSpecs::MyString.new(str).split(pat, limit).each do |x|
- x.should be_an_instance_of(String)
- end
-
- str.split(StringSpecs::MyString.new(pat), limit).each do |x|
- x.should be_an_instance_of(String)
- end
+ str.split(StringSpecs::MyString.new(pat), limit).each do |x|
+ x.should be_an_instance_of(String)
end
end
end
@@ -257,7 +229,7 @@ end
describe "String#split with Regexp" do
it "throws an ArgumentError if the string is not a valid" do
- s = "\xDF".force_encoding(Encoding::UTF_8)
+ s = "\xDF".dup.force_encoding(Encoding::UTF_8)
-> { s.split(/./) }.should raise_error(ArgumentError)
end
@@ -414,36 +386,12 @@ describe "String#split with Regexp" do
"foo".split(/bar/, 3).should == ["foo"]
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances based on self" do
- ["", "x:y:z:", " x y "].each do |str|
- [//, /:/, /\s+/].each do |pat|
- [-1, 0, 1, 2].each do |limit|
- StringSpecs::MyString.new(str).split(pat, limit).each do |x|
- x.should be_an_instance_of(StringSpecs::MyString)
- end
- end
- end
- end
- end
-
- it "does not call constructor on created subclass instances" do
- # can't call should_not_receive on an object that doesn't yet exist
- # so failure here is signalled by exception, not expectation failure
-
- s = StringSpecs::StringWithRaisingConstructor.new('silly:string')
- s.split(/:/).first.should == 'silly'
- end
- end
-
- ruby_version_is '3.0' do
- it "returns String instances based on self" do
- ["", "x:y:z:", " x y "].each do |str|
- [//, /:/, /\s+/].each do |pat|
- [-1, 0, 1, 2].each do |limit|
- StringSpecs::MyString.new(str).split(pat, limit).each do |x|
- x.should be_an_instance_of(String)
- end
+ it "returns String instances based on self" do
+ ["", "x:y:z:", " x y "].each do |str|
+ [//, /:/, /\s+/].each do |pat|
+ [-1, 0, 1, 2].each do |limit|
+ StringSpecs::MyString.new(str).split(pat, limit).each do |x|
+ x.should be_an_instance_of(String)
end
end
end
@@ -461,7 +409,7 @@ describe "String#split with Regexp" do
end
it "returns an ArgumentError if an invalid UTF-8 string is supplied" do
- broken_str = 'проверка' # in russian, means "test"
+ broken_str = +'проверка' # in russian, means "test"
broken_str.force_encoding('binary')
broken_str.chop!
broken_str.force_encoding('utf-8')
@@ -569,32 +517,16 @@ describe "String#split with Regexp" do
end
describe "for a String subclass" do
- ruby_version_is ''...'3.0' do
- it "yields instances of the same subclass" do
- a = []
- StringSpecs::MyString.new("a|b").split("|") { |str| a << str }
- first, last = a
-
- first.should be_an_instance_of(StringSpecs::MyString)
- first.should == "a"
-
- last.should be_an_instance_of(StringSpecs::MyString)
- last.should == "b"
- end
- end
-
- ruby_version_is '3.0' do
- it "yields instances of String" do
- a = []
- StringSpecs::MyString.new("a|b").split("|") { |str| a << str }
- first, last = a
+ it "yields instances of String" do
+ a = []
+ StringSpecs::MyString.new("a|b").split("|") { |str| a << str }
+ first, last = a
- first.should be_an_instance_of(String)
- first.should == "a"
+ first.should be_an_instance_of(String)
+ first.should == "a"
- last.should be_an_instance_of(String)
- last.should == "b"
- end
+ last.should be_an_instance_of(String)
+ last.should == "b"
end
end
diff --git a/spec/ruby/core/string/squeeze_spec.rb b/spec/ruby/core/string/squeeze_spec.rb
index 2f3fa65745..4ea238e6b5 100644
--- a/spec/ruby/core/string/squeeze_spec.rb
+++ b/spec/ruby/core/string/squeeze_spec.rb
@@ -1,4 +1,5 @@
# -*- encoding: binary -*-
+# frozen_string_literal: false
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
@@ -75,16 +76,8 @@ describe "String#squeeze" do
-> { "hello world".squeeze(mock('x')) }.should raise_error(TypeError)
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances when called on a subclass" do
- StringSpecs::MyString.new("oh no!!!").squeeze("!").should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns String instances when called on a subclass" do
- StringSpecs::MyString.new("oh no!!!").squeeze("!").should be_an_instance_of(String)
- end
+ it "returns String instances when called on a subclass" do
+ StringSpecs::MyString.new("oh no!!!").squeeze("!").should be_an_instance_of(String)
end
end
diff --git a/spec/ruby/core/string/start_with_spec.rb b/spec/ruby/core/string/start_with_spec.rb
index 3833289f96..35e33b46a6 100644
--- a/spec/ruby/core/string/start_with_spec.rb
+++ b/spec/ruby/core/string/start_with_spec.rb
@@ -7,12 +7,21 @@ describe "String#start_with?" do
it_behaves_like :start_with, :to_s
# Here and not in the shared examples because this is invalid as a Symbol
- it "does not check that we are not starting to match at the head of a character" do
+ it "matches part of a character with the same part" do
"\xA9".should.start_with?("\xA9") # A9 is not a character head for UTF-8
end
- it "does not check we are matching only part of a character" do
- "\xe3\x81\x82".size.should == 1
- "\xe3\x81\x82".should.start_with?("\xe3")
+ ruby_version_is ""..."3.3" do
+ it "does not check we are matching only part of a character" do
+ "\xe3\x81\x82".size.should == 1
+ "\xe3\x81\x82".should.start_with?("\xe3")
+ end
+ end
+
+ ruby_version_is "3.3" do # #19784
+ it "checks we are matching only part of a character" do
+ "\xe3\x81\x82".size.should == 1
+ "\xe3\x81\x82".should_not.start_with?("\xe3")
+ end
end
end
diff --git a/spec/ruby/core/string/strip_spec.rb b/spec/ruby/core/string/strip_spec.rb
index 662f13b032..edb6ea3b44 100644
--- a/spec/ruby/core/string/strip_spec.rb
+++ b/spec/ruby/core/string/strip_spec.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
require_relative 'shared/strip'
@@ -11,10 +12,8 @@ describe "String#strip" do
"\tgoodbye\r\v\n".strip.should == "goodbye"
end
- ruby_version_is '3.0' do
- it "returns a copy of self without leading and trailing NULL bytes and whitespace" do
- " \x00 goodbye \x00 ".strip.should == "goodbye"
- end
+ it "returns a copy of self without leading and trailing NULL bytes and whitespace" do
+ " \x00 goodbye \x00 ".strip.should == "goodbye"
end
end
@@ -41,12 +40,10 @@ describe "String#strip!" do
" ".strip.should == ""
end
- ruby_version_is '3.0' do
- it "removes leading and trailing NULL bytes and whitespace" do
- a = "\000 goodbye \000"
- a.strip!
- a.should == "goodbye"
- end
+ it "removes leading and trailing NULL bytes and whitespace" do
+ a = "\000 goodbye \000"
+ a.strip!
+ a.should == "goodbye"
end
it "raises a FrozenError on a frozen instance that is modified" do
diff --git a/spec/ruby/core/string/sub_spec.rb b/spec/ruby/core/string/sub_spec.rb
index 99dd7b45a8..4f9f87a433 100644
--- a/spec/ruby/core/string/sub_spec.rb
+++ b/spec/ruby/core/string/sub_spec.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
@@ -170,22 +171,11 @@ describe "String#sub with pattern, replacement" do
-> { "hello".sub(/[aeiou]/, 99) }.should raise_error(TypeError)
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances when called on a subclass" do
- StringSpecs::MyString.new("").sub(//, "").should be_an_instance_of(StringSpecs::MyString)
- StringSpecs::MyString.new("").sub(/foo/, "").should be_an_instance_of(StringSpecs::MyString)
- StringSpecs::MyString.new("foo").sub(/foo/, "").should be_an_instance_of(StringSpecs::MyString)
- StringSpecs::MyString.new("foo").sub("foo", "").should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns String instances when called on a subclass" do
- StringSpecs::MyString.new("").sub(//, "").should be_an_instance_of(String)
- StringSpecs::MyString.new("").sub(/foo/, "").should be_an_instance_of(String)
- StringSpecs::MyString.new("foo").sub(/foo/, "").should be_an_instance_of(String)
- StringSpecs::MyString.new("foo").sub("foo", "").should be_an_instance_of(String)
- end
+ it "returns String instances when called on a subclass" do
+ StringSpecs::MyString.new("").sub(//, "").should be_an_instance_of(String)
+ StringSpecs::MyString.new("").sub(/foo/, "").should be_an_instance_of(String)
+ StringSpecs::MyString.new("foo").sub(/foo/, "").should be_an_instance_of(String)
+ StringSpecs::MyString.new("foo").sub("foo", "").should be_an_instance_of(String)
end
it "sets $~ to MatchData of match and nil when there's none" do
diff --git a/spec/ruby/core/string/swapcase_spec.rb b/spec/ruby/core/string/swapcase_spec.rb
index d369ab3e4e..7f4c68366d 100644
--- a/spec/ruby/core/string/swapcase_spec.rb
+++ b/spec/ruby/core/string/swapcase_spec.rb
@@ -1,4 +1,5 @@
# -*- encoding: utf-8 -*-
+# frozen_string_literal: false
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
@@ -74,18 +75,9 @@ describe "String#swapcase" do
-> { "abc".swapcase(:invalid_option) }.should raise_error(ArgumentError)
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances when called on a subclass" do
- StringSpecs::MyString.new("").swapcase.should be_an_instance_of(StringSpecs::MyString)
- StringSpecs::MyString.new("hello").swapcase.should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns String instances when called on a subclass" do
- StringSpecs::MyString.new("").swapcase.should be_an_instance_of(String)
- StringSpecs::MyString.new("hello").swapcase.should be_an_instance_of(String)
- end
+ it "returns String instances when called on a subclass" do
+ StringSpecs::MyString.new("").swapcase.should be_an_instance_of(String)
+ StringSpecs::MyString.new("hello").swapcase.should be_an_instance_of(String)
end
end
diff --git a/spec/ruby/core/string/to_i_spec.rb b/spec/ruby/core/string/to_i_spec.rb
index e4fa89aab3..9931502baa 100644
--- a/spec/ruby/core/string/to_i_spec.rb
+++ b/spec/ruby/core/string/to_i_spec.rb
@@ -10,6 +10,18 @@ describe "String#to_i" do
"1_2_3asdf".to_i.should == 123
end
+ it "ignores multiple non-consecutive underscoes when the first digit is 0" do
+ (2..16).each do |base|
+ "0_0_010".to_i(base).should == base;
+ end
+ end
+
+ it "bails out at the first double underscore if the first digit is 0" do
+ (2..16).each do |base|
+ "010__1".to_i(base).should == base;
+ end
+ end
+
it "ignores leading whitespaces" do
[ " 123", " 123", "\r\n\r\n123", "\t\t123",
"\r\n\t\n123", " \t\n\r\t 123"].each do |str|
diff --git a/spec/ruby/core/string/tr_s_spec.rb b/spec/ruby/core/string/tr_s_spec.rb
index e1bb20ce35..dd72da440c 100644
--- a/spec/ruby/core/string/tr_s_spec.rb
+++ b/spec/ruby/core/string/tr_s_spec.rb
@@ -1,4 +1,5 @@
# -*- encoding: utf-8 -*-
+# frozen_string_literal: false
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
@@ -17,6 +18,15 @@ describe "String#tr_s" do
"hello ^--^".tr_s("---", "_").should == "hello ^_^"
end
+ ruby_bug "#19769", ""..."3.3" do
+ it "accepts c1-c1 notation to denote range of one character" do
+ "hello".tr_s('e-e', 'x').should == "hxllo"
+ "123456789".tr_s("2-23","xy").should == "1xy456789"
+ "hello ^-^".tr_s("e-", "a-a_").should == "hallo ^_^"
+ "hello ^-^".tr_s("---o", "_a").should == "hella ^_^"
+ end
+ end
+
it "pads to_str with its last char if it is shorter than from_string" do
"this".tr_s("this", "x").should == "x"
end
@@ -45,16 +55,8 @@ describe "String#tr_s" do
"bla".tr_s(from_str, to_str).should == "BlA"
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances when called on a subclass" do
- StringSpecs::MyString.new("hello").tr_s("e", "a").should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns String instances when called on a subclass" do
- StringSpecs::MyString.new("hello").tr_s("e", "a").should be_an_instance_of(String)
- end
+ it "returns String instances when called on a subclass" do
+ StringSpecs::MyString.new("hello").tr_s("e", "a").should be_an_instance_of(String)
end
# http://redmine.ruby-lang.org/issues/show/1839
diff --git a/spec/ruby/core/string/tr_spec.rb b/spec/ruby/core/string/tr_spec.rb
index 72adb9f2eb..75841a974f 100644
--- a/spec/ruby/core/string/tr_spec.rb
+++ b/spec/ruby/core/string/tr_spec.rb
@@ -1,4 +1,5 @@
# -*- encoding: utf-8 -*-
+# frozen_string_literal: false
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
@@ -16,6 +17,15 @@ describe "String#tr" do
"hello ^-^".tr("---", "_").should == "hello ^_^"
end
+ ruby_bug "#19769", ""..."3.3" do
+ it "accepts c1-c1 notation to denote range of one character" do
+ "hello".tr('e-e', 'x').should == "hxllo"
+ "123456789".tr("2-23","xy").should == "1xy456789"
+ "hello ^-^".tr("e-", "a-a_").should == "hallo ^_^"
+ "hello ^-^".tr("---o", "_a").should == "hella ^_^"
+ end
+ end
+
it "pads to_str with its last char if it is shorter than from_string" do
"this".tr("this", "x").should == "xxxx"
"hello".tr("a-z", "A-H.").should == "HE..."
@@ -57,16 +67,8 @@ describe "String#tr" do
"bla".tr(from_str, to_str).should == "BlA"
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances when called on a subclass" do
- StringSpecs::MyString.new("hello").tr("e", "a").should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns Stringinstances when called on a subclass" do
- StringSpecs::MyString.new("hello").tr("e", "a").should be_an_instance_of(String)
- end
+ it "returns Stringinstances when called on a subclass" do
+ StringSpecs::MyString.new("hello").tr("e", "a").should be_an_instance_of(String)
end
# http://redmine.ruby-lang.org/issues/show/1839
diff --git a/spec/ruby/core/string/unicode_normalize_spec.rb b/spec/ruby/core/string/unicode_normalize_spec.rb
index 6de7533fc7..2e7d22394a 100644
--- a/spec/ruby/core/string/unicode_normalize_spec.rb
+++ b/spec/ruby/core/string/unicode_normalize_spec.rb
@@ -1,4 +1,5 @@
# -*- encoding: utf-8 -*-
+# frozen_string_literal: false
require_relative '../../spec_helper'
# Examples taken from http://www.unicode.org/reports/tr15/#Norm_Forms
diff --git a/spec/ruby/core/string/unicode_normalized_spec.rb b/spec/ruby/core/string/unicode_normalized_spec.rb
index 87f3740459..91cf2086b2 100644
--- a/spec/ruby/core/string/unicode_normalized_spec.rb
+++ b/spec/ruby/core/string/unicode_normalized_spec.rb
@@ -1,4 +1,5 @@
# -*- encoding: utf-8 -*-
+# frozen_string_literal: false
require_relative '../../spec_helper'
describe "String#unicode_normalized?" do
diff --git a/spec/ruby/core/string/unpack/a_spec.rb b/spec/ruby/core/string/unpack/a_spec.rb
index 2d83b4c824..4002ece697 100644
--- a/spec/ruby/core/string/unpack/a_spec.rb
+++ b/spec/ruby/core/string/unpack/a_spec.rb
@@ -31,7 +31,7 @@ describe "String#unpack with format 'A'" do
end
it "decodes into raw (ascii) string values" do
- str = "str".force_encoding('UTF-8').unpack("A*")[0]
+ str = "str".dup.force_encoding('UTF-8').unpack("A*")[0]
str.encoding.should == Encoding::BINARY
end
diff --git a/spec/ruby/core/string/unpack/b_spec.rb b/spec/ruby/core/string/unpack/b_spec.rb
index 5c53eff721..23d93a8aea 100644
--- a/spec/ruby/core/string/unpack/b_spec.rb
+++ b/spec/ruby/core/string/unpack/b_spec.rb
@@ -107,7 +107,7 @@ describe "String#unpack with format 'B'" do
end
it "decodes into US-ASCII string values" do
- str = "s".force_encoding('UTF-8').unpack("B*")[0]
+ str = "s".dup.force_encoding('UTF-8').unpack("B*")[0]
str.encoding.name.should == 'US-ASCII'
end
end
@@ -215,7 +215,7 @@ describe "String#unpack with format 'b'" do
end
it "decodes into US-ASCII string values" do
- str = "s".force_encoding('UTF-8').unpack("b*")[0]
+ str = "s".dup.force_encoding('UTF-8').unpack("b*")[0]
str.encoding.name.should == 'US-ASCII'
end
end
diff --git a/spec/ruby/core/string/unpack/u_spec.rb b/spec/ruby/core/string/unpack/u_spec.rb
index 7845e6d5f2..456abee784 100644
--- a/spec/ruby/core/string/unpack/u_spec.rb
+++ b/spec/ruby/core/string/unpack/u_spec.rb
@@ -33,7 +33,7 @@ describe "String#unpack with format 'u'" do
str = "".unpack("u")[0]
str.encoding.should == Encoding::BINARY
- str = "1".force_encoding('UTF-8').unpack("u")[0]
+ str = "1".dup.force_encoding('UTF-8').unpack("u")[0]
str.encoding.should == Encoding::BINARY
end
diff --git a/spec/ruby/core/string/upcase_spec.rb b/spec/ruby/core/string/upcase_spec.rb
index 5ce7b0b95f..652de5c2ef 100644
--- a/spec/ruby/core/string/upcase_spec.rb
+++ b/spec/ruby/core/string/upcase_spec.rb
@@ -1,4 +1,5 @@
# -*- encoding: utf-8 -*-
+# frozen_string_literal: false
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
@@ -73,16 +74,8 @@ describe "String#upcase" do
-> { "abc".upcase(:invalid_option) }.should raise_error(ArgumentError)
end
- ruby_version_is ''...'3.0' do
- it "returns a subclass instance for subclasses" do
- StringSpecs::MyString.new("fooBAR").upcase.should be_an_instance_of(StringSpecs::MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns a String instance for subclasses" do
- StringSpecs::MyString.new("fooBAR").upcase.should be_an_instance_of(String)
- end
+ it "returns a String instance for subclasses" do
+ StringSpecs::MyString.new("fooBAR").upcase.should be_an_instance_of(String)
end
end
diff --git a/spec/ruby/core/string/uplus_spec.rb b/spec/ruby/core/string/uplus_spec.rb
index 038b283c90..c0b0c49ede 100644
--- a/spec/ruby/core/string/uplus_spec.rb
+++ b/spec/ruby/core/string/uplus_spec.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require_relative '../../spec_helper'
describe 'String#+@' do
@@ -7,6 +8,9 @@ describe 'String#+@' do
output.should_not.frozen?
output.should == 'foo'
+
+ output << 'bar'
+ output.should == 'foobar'
end
it 'returns self if the String is not frozen' do
diff --git a/spec/ruby/core/string/upto_spec.rb b/spec/ruby/core/string/upto_spec.rb
index f8529b1d2b..8bc847d5ac 100644
--- a/spec/ruby/core/string/upto_spec.rb
+++ b/spec/ruby/core/string/upto_spec.rb
@@ -80,6 +80,12 @@ describe "String#upto" do
a.should == ["Σ", "Τ", "Υ", "Φ", "Χ", "Ψ", "Ω"]
end
+ it "raises Encoding::CompatibilityError when incompatible characters are given" do
+ char1 = 'a'.dup.force_encoding("EUC-JP")
+ char2 = 'b'.dup.force_encoding("ISO-2022-JP")
+ -> { char1.upto(char2) {} }.should raise_error(Encoding::CompatibilityError, "incompatible character encodings: EUC-JP and ISO-2022-JP")
+ end
+
describe "on sequence of numbers" do
it "calls the block as Integer#upto" do
"8".upto("11").to_a.should == 8.upto(11).map(&:to_s)
diff --git a/spec/ruby/core/string/valid_encoding_spec.rb b/spec/ruby/core/string/valid_encoding_spec.rb
index be7cef7a8e..375035cd94 100644
--- a/spec/ruby/core/string/valid_encoding_spec.rb
+++ b/spec/ruby/core/string/valid_encoding_spec.rb
@@ -7,13 +7,13 @@ describe "String#valid_encoding?" do
end
it "returns true if self is valid in the current encoding and other encodings" do
- str = "\x77"
+ str = +"\x77"
str.force_encoding('utf-8').valid_encoding?.should be_true
str.force_encoding('binary').valid_encoding?.should be_true
end
it "returns true for all encodings self is valid in" do
- str = "\xE6\x9D\x94"
+ str = +"\xE6\x9D\x94"
str.force_encoding('BINARY').valid_encoding?.should be_true
str.force_encoding('UTF-8').valid_encoding?.should be_true
str.force_encoding('US-ASCII').valid_encoding?.should be_false
@@ -43,10 +43,10 @@ describe "String#valid_encoding?" do
str.force_encoding('KOI8-R').valid_encoding?.should be_true
str.force_encoding('KOI8-U').valid_encoding?.should be_true
str.force_encoding('Shift_JIS').valid_encoding?.should be_false
- "\xD8\x00".force_encoding('UTF-16BE').valid_encoding?.should be_false
- "\x00\xD8".force_encoding('UTF-16LE').valid_encoding?.should be_false
- "\x04\x03\x02\x01".force_encoding('UTF-32BE').valid_encoding?.should be_false
- "\x01\x02\x03\x04".force_encoding('UTF-32LE').valid_encoding?.should be_false
+ "\xD8\x00".dup.force_encoding('UTF-16BE').valid_encoding?.should be_false
+ "\x00\xD8".dup.force_encoding('UTF-16LE').valid_encoding?.should be_false
+ "\x04\x03\x02\x01".dup.force_encoding('UTF-32BE').valid_encoding?.should be_false
+ "\x01\x02\x03\x04".dup.force_encoding('UTF-32LE').valid_encoding?.should be_false
str.force_encoding('Windows-1251').valid_encoding?.should be_true
str.force_encoding('IBM437').valid_encoding?.should be_true
str.force_encoding('IBM737').valid_encoding?.should be_true
@@ -100,27 +100,25 @@ describe "String#valid_encoding?" do
str.force_encoding('UTF8-MAC').valid_encoding?.should be_true
end
- ruby_version_is '3.0' do
- it "returns true for IBM720 encoding self is valid in" do
- str = "\xE6\x9D\x94"
- str.force_encoding('IBM720').valid_encoding?.should be_true
- str.force_encoding('CP720').valid_encoding?.should be_true
- end
+ it "returns true for IBM720 encoding self is valid in" do
+ str = +"\xE6\x9D\x94"
+ str.force_encoding('IBM720').valid_encoding?.should be_true
+ str.force_encoding('CP720').valid_encoding?.should be_true
end
it "returns false if self is valid in one encoding, but invalid in the one it's tagged with" do
- str = "\u{8765}"
+ str = +"\u{8765}"
str.valid_encoding?.should be_true
- str = str.force_encoding('ascii')
+ str.force_encoding('ascii')
str.valid_encoding?.should be_false
end
it "returns false if self contains a character invalid in the associated encoding" do
- "abc#{[0x80].pack('C')}".force_encoding('ascii').valid_encoding?.should be_false
+ "abc#{[0x80].pack('C')}".dup.force_encoding('ascii').valid_encoding?.should be_false
end
it "returns false if a valid String had an invalid character appended to it" do
- str = "a"
+ str = +"a"
str.valid_encoding?.should be_true
str << [0xDD].pack('C').force_encoding('utf-8')
str.valid_encoding?.should be_false
diff --git a/spec/ruby/core/struct/constants_spec.rb b/spec/ruby/core/struct/constants_spec.rb
new file mode 100644
index 0000000000..fa61a4b912
--- /dev/null
+++ b/spec/ruby/core/struct/constants_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "3.2" do
+ describe "Struct::Group" do
+ it "is no longer defined" do
+ Struct.should_not.const_defined?(:Group)
+ end
+ end
+
+ describe "Struct::Passwd" do
+ it "is no longer defined" do
+ Struct.should_not.const_defined?(:Passwd)
+ end
+ end
+end
diff --git a/spec/ruby/core/struct/new_spec.rb b/spec/ruby/core/struct/new_spec.rb
index 8758051a81..a94eb852e1 100644
--- a/spec/ruby/core/struct/new_spec.rb
+++ b/spec/ruby/core/struct/new_spec.rb
@@ -48,7 +48,7 @@ describe "Struct.new" do
end
it "allows non-ASCII member name" do
- name = "r\xe9sum\xe9".force_encoding(Encoding::ISO_8859_1).to_sym
+ name = "r\xe9sum\xe9".dup.force_encoding(Encoding::ISO_8859_1).to_sym
struct = Struct.new(name)
struct.new("foo").send(name).should == "foo"
end
diff --git a/spec/ruby/core/symbol/name_spec.rb b/spec/ruby/core/symbol/name_spec.rb
index 15b9aa75e9..f9b631266c 100644
--- a/spec/ruby/core/symbol/name_spec.rb
+++ b/spec/ruby/core/symbol/name_spec.rb
@@ -1,19 +1,17 @@
require_relative '../../spec_helper'
-ruby_version_is "3.0" do
- describe "Symbol#name" do
- it "returns string" do
- :ruby.name.should == "ruby"
- :ルビー.name.should == "ルビー"
- end
+describe "Symbol#name" do
+ it "returns string" do
+ :ruby.name.should == "ruby"
+ :ルビー.name.should == "ルビー"
+ end
- it "returns same string instance" do
- :"ruby_3".name.should.equal?(:ruby_3.name)
- :"ruby_#{1+2}".name.should.equal?(:ruby_3.name)
- end
+ it "returns same string instance" do
+ :"ruby_3".name.should.equal?(:ruby_3.name)
+ :"ruby_#{1+2}".name.should.equal?(:ruby_3.name)
+ end
- it "returns frozen string" do
- :symbol.name.should.frozen?
- end
+ it "returns frozen string" do
+ :symbol.name.should.frozen?
end
end
diff --git a/spec/ruby/core/symbol/to_proc_spec.rb b/spec/ruby/core/symbol/to_proc_spec.rb
index 6d9c4bc622..54eccdba11 100644
--- a/spec/ruby/core/symbol/to_proc_spec.rb
+++ b/spec/ruby/core/symbol/to_proc_spec.rb
@@ -12,38 +12,19 @@ describe "Symbol#to_proc" do
:to_s.to_proc.call(obj).should == "Received #to_s"
end
- ruby_version_is ""..."3.0" do
- it "returns a Proc with #lambda? false" do
- pr = :to_s.to_proc
- pr.should_not.lambda?
- end
-
- it "produces a Proc with arity -1" do
- pr = :to_s.to_proc
- pr.arity.should == -1
- end
-
- it "produces a Proc that always returns [[:rest]] for #parameters" do
- pr = :to_s.to_proc
- pr.parameters.should == [[:rest]]
- end
+ it "returns a Proc with #lambda? true" do
+ pr = :to_s.to_proc
+ pr.should.lambda?
end
- ruby_version_is "3.0" do
- it "returns a Proc with #lambda? true" do
- pr = :to_s.to_proc
- pr.should.lambda?
- end
-
- it "produces a Proc with arity -2" do
- pr = :to_s.to_proc
- pr.arity.should == -2
- end
+ it "produces a Proc with arity -2" do
+ pr = :to_s.to_proc
+ pr.arity.should == -2
+ end
- it "produces a Proc that always returns [[:req], [:rest]] for #parameters" do
- pr = :to_s.to_proc
- pr.parameters.should == [[:req], [:rest]]
- end
+ it "produces a Proc that always returns [[:req], [:rest]] for #parameters" do
+ pr = :to_s.to_proc
+ pr.parameters.should == [[:req], [:rest]]
end
ruby_version_is "3.2" do
@@ -58,8 +39,8 @@ describe "Symbol#to_proc" do
@a = []
singleton_class.class_eval(&body)
tap(&:pub)
- proc{tap(&:pro)}.should raise_error(NoMethodError, /protected method `pro' called/)
- proc{tap(&:pri)}.should raise_error(NoMethodError, /private method `pri' called/)
+ proc{tap(&:pro)}.should raise_error(NoMethodError, /protected method [`']pro' called/)
+ proc{tap(&:pri)}.should raise_error(NoMethodError, /private method [`']pri' called/)
@a.should == [:pub]
@a = []
@@ -67,8 +48,8 @@ describe "Symbol#to_proc" do
o = c.new
o.instance_variable_set(:@a, [])
o.tap(&:pub)
- proc{tap(&:pro)}.should raise_error(NoMethodError, /protected method `pro' called/)
- proc{o.tap(&:pri)}.should raise_error(NoMethodError, /private method `pri' called/)
+ proc{tap(&:pro)}.should raise_error(NoMethodError, /protected method [`']pro' called/)
+ proc{o.tap(&:pri)}.should raise_error(NoMethodError, /private method [`']pri' called/)
o.a.should == [:pub]
end
end
diff --git a/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb b/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb
index e35e1fc0b4..6e381e4868 100644
--- a/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb
+++ b/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb
@@ -59,7 +59,7 @@ describe 'Thread::Backtrace::Location#absolute_path' do
it "returns nil" do
location = nil
tap { location = caller_locations(1, 1)[0] }
- location.label.should == "tap"
+ location.label.should =~ /\A(?:Kernel#)?tap\z/
if location.path.start_with?("<internal:")
location.absolute_path.should == nil
else
diff --git a/spec/ruby/core/thread/backtrace/location/label_spec.rb b/spec/ruby/core/thread/backtrace/location/label_spec.rb
index 7312d017e5..85ddccc8e3 100644
--- a/spec/ruby/core/thread/backtrace/location/label_spec.rb
+++ b/spec/ruby/core/thread/backtrace/location/label_spec.rb
@@ -7,11 +7,11 @@ describe 'Thread::Backtrace::Location#label' do
end
it 'returns the method name for a method location' do
- ThreadBacktraceLocationSpecs.method_location[0].label.should == "method_location"
+ ThreadBacktraceLocationSpecs.method_location[0].label.should =~ /\A(?:ThreadBacktraceLocationSpecs\.)?method_location\z/
end
it 'returns the block name for a block location' do
- ThreadBacktraceLocationSpecs.block_location[0].label.should == "block in block_location"
+ ThreadBacktraceLocationSpecs.block_location[0].label.should =~ /\Ablock in (?:ThreadBacktraceLocationSpecs\.)?block_location\z/
end
it 'returns the module name for a module location' do
@@ -22,9 +22,9 @@ describe 'Thread::Backtrace::Location#label' do
first_level_location, second_level_location, third_level_location =
ThreadBacktraceLocationSpecs.locations_inside_nested_blocks
- first_level_location.label.should == 'block in locations_inside_nested_blocks'
- second_level_location.label.should == 'block (2 levels) in locations_inside_nested_blocks'
- third_level_location.label.should == 'block (3 levels) in locations_inside_nested_blocks'
+ first_level_location.label.should =~ /\Ablock in (?:ThreadBacktraceLocationSpecs\.)?locations_inside_nested_blocks\z/
+ second_level_location.label.should =~ /\Ablock \(2 levels\) in (?:ThreadBacktraceLocationSpecs\.)?locations_inside_nested_blocks\z/
+ third_level_location.label.should =~ /\Ablock \(3 levels\) in (?:ThreadBacktraceLocationSpecs\.)?locations_inside_nested_blocks\z/
end
it 'sets the location label for a top-level block differently depending on it being in the main file or a required file' do
diff --git a/spec/ruby/core/thread/backtrace/location/lineno_spec.rb b/spec/ruby/core/thread/backtrace/location/lineno_spec.rb
index d14cf17514..10457f80f0 100644
--- a/spec/ruby/core/thread/backtrace/location/lineno_spec.rb
+++ b/spec/ruby/core/thread/backtrace/location/lineno_spec.rb
@@ -7,7 +7,7 @@ describe 'Thread::Backtrace::Location#lineno' do
@line = __LINE__ - 1
end
- it 'returns the absolute path of the call frame' do
+ it 'returns the line number of the call frame' do
@frame.lineno.should == @line
end
diff --git a/spec/ruby/core/thread/backtrace/location/path_spec.rb b/spec/ruby/core/thread/backtrace/location/path_spec.rb
index 7863c055d3..75f76833a9 100644
--- a/spec/ruby/core/thread/backtrace/location/path_spec.rb
+++ b/spec/ruby/core/thread/backtrace/location/path_spec.rb
@@ -41,7 +41,7 @@ describe 'Thread::Backtrace::Location#path' do
context 'when using a relative script path' do
it 'returns a path relative to the working directory' do
path = 'fixtures/main.rb'
- directory = File.dirname(__FILE__)
+ directory = __dir__
Dir.chdir(directory) {
ruby_exe(path)
}.should == path
diff --git a/spec/ruby/core/thread/backtrace_locations_spec.rb b/spec/ruby/core/thread/backtrace_locations_spec.rb
index c970ae023b..09fe622e0d 100644
--- a/spec/ruby/core/thread/backtrace_locations_spec.rb
+++ b/spec/ruby/core/thread/backtrace_locations_spec.rb
@@ -70,7 +70,7 @@ describe "Thread#backtrace_locations" do
end
it "the first location reports the call to #backtrace_locations" do
- Thread.current.backtrace_locations(0..0)[0].to_s.should == "#{__FILE__ }:#{__LINE__ }:in `backtrace_locations'"
+ Thread.current.backtrace_locations(0..0)[0].to_s.should =~ /\A#{__FILE__ }:#{__LINE__ }:in [`'](?:Thread#)?backtrace_locations'\z/
end
it "[1..-1] is the same as #caller_locations(0..-1) for Thread.current" do
diff --git a/spec/ruby/core/thread/backtrace_spec.rb b/spec/ruby/core/thread/backtrace_spec.rb
index 9001b1b7eb..15bb29a349 100644
--- a/spec/ruby/core/thread/backtrace_spec.rb
+++ b/spec/ruby/core/thread/backtrace_spec.rb
@@ -13,7 +13,7 @@ describe "Thread#backtrace" do
backtrace = t.backtrace
backtrace.should be_kind_of(Array)
- backtrace.first.should =~ /`sleep'/
+ backtrace.first.should =~ /[`'](?:Kernel#)?sleep'/
t.raise 'finish the thread'
t.join
diff --git a/spec/ruby/core/thread/each_caller_location_spec.rb b/spec/ruby/core/thread/each_caller_location_spec.rb
new file mode 100644
index 0000000000..29c271789b
--- /dev/null
+++ b/spec/ruby/core/thread/each_caller_location_spec.rb
@@ -0,0 +1,49 @@
+require_relative '../../spec_helper'
+
+describe "Thread.each_caller_location" do
+ ruby_version_is "3.2" do
+ it "iterates through the current execution stack and matches caller_locations content and type" do
+ ScratchPad.record []
+ Thread.each_caller_location { |l| ScratchPad << l; }
+
+ ScratchPad.recorded.map(&:to_s).should == caller_locations.map(&:to_s)
+ ScratchPad.recorded[0].should be_kind_of(Thread::Backtrace::Location)
+ end
+
+ it "returns subset of 'Thread.to_enum(:each_caller_location)' locations" do
+ ar = []
+ ecl = Thread.each_caller_location { |x| ar << x }
+
+ (ar.map(&:to_s) - Thread.to_enum(:each_caller_location).to_a.map(&:to_s)).should.empty?
+ end
+
+ it "stops the backtrace iteration if 'break' occurs" do
+ i = 0
+ ar = []
+ ecl = Thread.each_caller_location do |x|
+ ar << x
+ i += 1
+ break x if i == 2
+ end
+
+ ar.map(&:to_s).should == caller_locations(1, 2).map(&:to_s)
+ ecl.should be_kind_of(Thread::Backtrace::Location)
+ end
+
+ it "returns nil" do
+ Thread.each_caller_location {}.should == nil
+ end
+
+ it "raises LocalJumpError when called without a block" do
+ -> {
+ Thread.each_caller_location
+ }.should raise_error(LocalJumpError, "no block given")
+ end
+
+ it "doesn't accept keyword arguments" do
+ -> {
+ Thread.each_caller_location(12, foo: 10) {}
+ }.should raise_error(ArgumentError);
+ end
+ end
+end
diff --git a/spec/ruby/core/thread/exclusive_spec.rb b/spec/ruby/core/thread/exclusive_spec.rb
deleted file mode 100644
index 37c4b19d1a..0000000000
--- a/spec/ruby/core/thread/exclusive_spec.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-require_relative '../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- describe "Thread.exclusive" do
- before :each do
- ScratchPad.clear
- $VERBOSE, @verbose = nil, $VERBOSE
- end
-
- after :each do
- $VERBOSE = @verbose
- end
-
- it "yields to the block" do
- Thread.exclusive { ScratchPad.record true }
- ScratchPad.recorded.should == true
- end
-
- it "returns the result of yielding" do
- Thread.exclusive { :result }.should == :result
- end
-
- it "blocks the caller if another thread is also in an exclusive block" do
- m = Mutex.new
- q1 = Queue.new
- q2 = Queue.new
-
- t = Thread.new {
- Thread.exclusive {
- q1.push :ready
- q2.pop
- }
- }
-
- q1.pop.should == :ready
-
- -> { Thread.exclusive { } }.should block_caller
-
- q2.push :done
- t.join
- end
-
- it "is not recursive" do
- Thread.exclusive do
- -> { Thread.exclusive { } }.should raise_error(ThreadError)
- end
- end
- end
-end
diff --git a/spec/ruby/core/thread/fetch_spec.rb b/spec/ruby/core/thread/fetch_spec.rb
index 6b37d4cfc5..85ffb71874 100644
--- a/spec/ruby/core/thread/fetch_spec.rb
+++ b/spec/ruby/core/thread/fetch_spec.rb
@@ -29,6 +29,36 @@ describe 'Thread#fetch' do
end
end
+ describe 'with a block' do
+ it 'returns the value of the fiber-local variable if value has been assigned' do
+ th = Thread.new { Thread.current[:cat] = 'meow' }
+ th.join
+ th.fetch(:cat) { true }.should == 'meow'
+ end
+
+ it "returns the block value if fiber-local variable hasn't been assigned" do
+ th = Thread.new {}
+ th.join
+ th.fetch(:cat) { true }.should == true
+ end
+
+ it "does not call the block if value has been assigned" do
+ th = Thread.new { Thread.current[:cat] = 'meow' }
+ th.join
+ var = :not_updated
+ th.fetch(:cat) { var = :updated }.should == 'meow'
+ var.should == :not_updated
+ end
+
+ it "uses the block if a default is given and warns about it" do
+ th = Thread.new {}
+ th.join
+ -> {
+ th.fetch(:cat, false) { true }.should == true
+ }.should complain(/warning: block supersedes default value argument/)
+ end
+ end
+
it 'raises an ArgumentError when not passed one or two arguments' do
-> { Thread.current.fetch() }.should raise_error(ArgumentError)
-> { Thread.current.fetch(1, 2, 3) }.should raise_error(ArgumentError)
diff --git a/spec/ruby/core/thread/ignore_deadlock_spec.rb b/spec/ruby/core/thread/ignore_deadlock_spec.rb
index 53cc2a7f5b..b48bc9f9b0 100644
--- a/spec/ruby/core/thread/ignore_deadlock_spec.rb
+++ b/spec/ruby/core/thread/ignore_deadlock_spec.rb
@@ -1,21 +1,19 @@
require_relative '../../spec_helper'
-ruby_version_is "3.0" do
- describe "Thread.ignore_deadlock" do
- it "returns false by default" do
- Thread.ignore_deadlock.should == false
- end
+describe "Thread.ignore_deadlock" do
+ it "returns false by default" do
+ Thread.ignore_deadlock.should == false
end
+end
- describe "Thread.ignore_deadlock=" do
- it "changes the value of Thread.ignore_deadlock" do
- ignore_deadlock = Thread.ignore_deadlock
- Thread.ignore_deadlock = true
- begin
- Thread.ignore_deadlock.should == true
- ensure
- Thread.ignore_deadlock = ignore_deadlock
- end
+describe "Thread.ignore_deadlock=" do
+ it "changes the value of Thread.ignore_deadlock" do
+ ignore_deadlock = Thread.ignore_deadlock
+ Thread.ignore_deadlock = true
+ begin
+ Thread.ignore_deadlock.should == true
+ ensure
+ Thread.ignore_deadlock = ignore_deadlock
end
end
end
diff --git a/spec/ruby/core/thread/native_thread_id_spec.rb b/spec/ruby/core/thread/native_thread_id_spec.rb
index 8460a1db8c..17a08c8a15 100644
--- a/spec/ruby/core/thread/native_thread_id_spec.rb
+++ b/spec/ruby/core/thread/native_thread_id_spec.rb
@@ -19,8 +19,15 @@ ruby_version_is "3.1" do
main_thread_id = Thread.current.native_thread_id
t_thread_id = t.native_thread_id
- t_thread_id.should be_kind_of(Integer)
+ if ruby_version_is "3.3"
+ # native_thread_id can be nil on a M:N scheduler
+ t_thread_id.should be_kind_of(Integer) if t_thread_id != nil
+ else
+ t_thread_id.should be_kind_of(Integer)
+ end
+
main_thread_id.should_not == t_thread_id
+
t.run
t.join
t.native_thread_id.should == nil
diff --git a/spec/ruby/core/thread/report_on_exception_spec.rb b/spec/ruby/core/thread/report_on_exception_spec.rb
index 9279fa1da5..d9daa041cd 100644
--- a/spec/ruby/core/thread/report_on_exception_spec.rb
+++ b/spec/ruby/core/thread/report_on_exception_spec.rb
@@ -61,34 +61,32 @@ describe "Thread#report_on_exception=" do
}.should raise_error(RuntimeError, "Thread#report_on_exception specs")
end
- ruby_version_is "3.0" do
- it "prints a backtrace on $stderr in the regular backtrace order" do
- line_raise = __LINE__ + 2
- def foo
- raise RuntimeError, "Thread#report_on_exception specs backtrace order"
- end
-
- line_call_foo = __LINE__ + 5
- go = false
- t = Thread.new {
- Thread.current.report_on_exception = true
- Thread.pass until go
- foo
- }
+ it "prints a backtrace on $stderr in the regular backtrace order" do
+ line_raise = __LINE__ + 2
+ def foo
+ raise RuntimeError, "Thread#report_on_exception specs backtrace order"
+ end
- -> {
- go = true
- Thread.pass while t.alive?
- }.should output("", <<ERR)
-#{t.inspect} terminated with exception (report_on_exception is true):
-#{__FILE__}:#{line_raise}:in `foo': Thread#report_on_exception specs backtrace order (RuntimeError)
-\tfrom #{__FILE__}:#{line_call_foo}:in `block (5 levels) in <top (required)>'
-ERR
+ line_call_foo = __LINE__ + 5
+ go = false
+ t = Thread.new {
+ Thread.current.report_on_exception = true
+ Thread.pass until go
+ foo
+ }
- -> {
- t.join
- }.should raise_error(RuntimeError, "Thread#report_on_exception specs backtrace order")
- end
+ -> {
+ go = true
+ Thread.pass while t.alive?
+ }.should output("", /\A
+#{Regexp.quote(t.inspect)}\sterminated\swith\sexception\s\(report_on_exception\sis\strue\):\n
+#{Regexp.quote(__FILE__)}:#{line_raise}:in\s[`']foo':\sThread\#report_on_exception\sspecs\sbacktrace\sorder\s\(RuntimeError\)\n
+\tfrom\s#{Regexp.quote(__FILE__)}:#{line_call_foo}:in\s[`']block\s\(4\slevels\)\sin\s<top\s\(required\)>'\n
+\z/x)
+
+ -> {
+ t.join
+ }.should raise_error(RuntimeError, "Thread#report_on_exception specs backtrace order")
end
it "prints the backtrace even if the thread was killed just after Thread#raise" do
diff --git a/spec/ruby/core/thread/thread_variable_get_spec.rb b/spec/ruby/core/thread/thread_variable_get_spec.rb
index 38f90d5830..0ad19bfd88 100644
--- a/spec/ruby/core/thread/thread_variable_get_spec.rb
+++ b/spec/ruby/core/thread/thread_variable_get_spec.rb
@@ -13,7 +13,7 @@ describe "Thread#thread_variable_get" do
@t.thread_variable_get(:a).should be_nil
end
- it "returns the value previously set by #[]=" do
+ it "returns the value previously set by #thread_variable_set" do
@t.thread_variable_set :a, 49
@t.thread_variable_get(:a).should == 49
end
diff --git a/spec/ruby/core/time/_load_spec.rb b/spec/ruby/core/time/_load_spec.rb
index 152934370f..bb0d705bbc 100644
--- a/spec/ruby/core/time/_load_spec.rb
+++ b/spec/ruby/core/time/_load_spec.rb
@@ -44,8 +44,7 @@ describe "Time._load" do
end
it "treats the data as binary data" do
- data = "\x04\bu:\tTime\r\fM\x1C\xC0\x00\x00\xD0\xBE"
- data.force_encoding Encoding::UTF_8
+ data = "\x04\bu:\tTime\r\fM\x1C\xC0\x00\x00\xD0\xBE".dup.force_encoding Encoding::UTF_8
t = Marshal.load(data)
t.to_s.should == "2013-04-08 12:47:45 UTC"
end
diff --git a/spec/ruby/core/time/at_spec.rb b/spec/ruby/core/time/at_spec.rb
index 0459589f01..48fb3c6f52 100644
--- a/spec/ruby/core/time/at_spec.rb
+++ b/spec/ruby/core/time/at_spec.rb
@@ -32,13 +32,6 @@ describe "Time.at" do
t2.nsec.should == t.nsec
end
- describe "passed BigDecimal" do
- it "doesn't round input value" do
- require 'bigdecimal'
- Time.at(BigDecimal('1.1')).to_f.should == 1.1
- end
- end
-
describe "passed Rational" do
it "returns Time with correct microseconds" do
t = Time.at(Rational(1_486_570_508_539_759, 1_000_000))
@@ -203,7 +196,7 @@ describe "Time.at" do
end
it "does not try to convert format to Symbol with #to_sym" do
- format = "usec"
+ format = +"usec"
format.should_not_receive(:to_sym)
-> { Time.at(0, 123456, format) }.should raise_error(ArgumentError)
end
diff --git a/spec/ruby/core/time/deconstruct_keys_spec.rb b/spec/ruby/core/time/deconstruct_keys_spec.rb
index fbb0ec2164..ee17e7dbd4 100644
--- a/spec/ruby/core/time/deconstruct_keys_spec.rb
+++ b/spec/ruby/core/time/deconstruct_keys_spec.rb
@@ -37,8 +37,9 @@ ruby_version_is "3.2" do
Time.new(2022, 10, 5, 13, 30).deconstruct_keys(['year', []]).should == {}
end
- it "ignores not existing Symbol keys" do
- Time.new(2022, 10, 5, 13, 30).deconstruct_keys([:year, :a]).should == { year: 2022 }
+ it "ignores not existing Symbol keys and processes keys after the first non-existing one" do
+ d = Time.utc(2022, 10, 5, 13, 30)
+ d.deconstruct_keys([:year, :a, :month, :b, :day]).should == { year: 2022, month: 10, day: 5 }
end
end
end
diff --git a/spec/ruby/core/time/fixtures/classes.rb b/spec/ruby/core/time/fixtures/classes.rb
index 1a9511b261..21c4e1effb 100644
--- a/spec/ruby/core/time/fixtures/classes.rb
+++ b/spec/ruby/core/time/fixtures/classes.rb
@@ -59,7 +59,6 @@ module TimeSpecs
Zone = Struct.new(:std, :dst, :dst_range)
Zones = {
"Asia/Colombo" => Zone[Z[5*3600+30*60, "MMT"], nil, nil],
- "Europe/Kiev" => Zone[Z[2*3600, "EET"], Z[3*3600, "EEST"], 4..10],
"PST" => Zone[Z[(-9*60*60), "PST"], nil, nil],
}
diff --git a/spec/ruby/core/time/new_spec.rb b/spec/ruby/core/time/new_spec.rb
index 3752cd146d..d686355270 100644
--- a/spec/ruby/core/time/new_spec.rb
+++ b/spec/ruby/core/time/new_spec.rb
@@ -58,7 +58,7 @@ describe "Time.new with a utc_offset argument" do
Time.new(2000, 1, 1, 0, 0, 0, "-04:10:43").utc_offset.should == -15043
end
- ruby_bug '#13669', '3.0'...'3.1' do
+ ruby_bug '#13669', ''...'3.1' do
it "returns a Time with a UTC offset specified as +HH" do
Time.new(2000, 1, 1, 0, 0, 0, "+05").utc_offset.should == 3600 * 5
end
@@ -200,10 +200,8 @@ describe "Time.new with a timezone argument" do
time.zone.should == zone
time.utc_offset.should == 5*3600+30*60
- ruby_version_is "3.0" do
- time.wday.should == 6
- time.yday.should == 1
- end
+ time.wday.should == 6
+ time.yday.should == 1
end
it "accepts timezone argument that must have #local_to_utc and #utc_to_local methods" do
@@ -360,7 +358,7 @@ describe "Time.new with a timezone argument" do
-> {
Marshal.dump(time)
- }.should raise_error(NoMethodError, /undefined method `name' for/)
+ }.should raise_error(NoMethodError, /undefined method [`']name' for/)
end
end
@@ -561,15 +559,15 @@ describe "Time.new with a timezone argument" do
-> {
Time.new("2020-012-25 00:56:17 +0900")
- }.should raise_error(ArgumentError, "two digits mon is expected after `-': -012-25 00:")
+ }.should raise_error(ArgumentError, /\Atwo digits mon is expected after [`']-': -012-25 00:\z/)
-> {
Time.new("2020-2-25 00:56:17 +0900")
- }.should raise_error(ArgumentError, "two digits mon is expected after `-': -2-25 00:56")
+ }.should raise_error(ArgumentError, /\Atwo digits mon is expected after [`']-': -2-25 00:56\z/)
-> {
Time.new("2020-12-215 00:56:17 +0900")
- }.should raise_error(ArgumentError, "two digits mday is expected after `-': -215 00:56:")
+ }.should raise_error(ArgumentError, /\Atwo digits mday is expected after [`']-': -215 00:56:\z/)
-> {
Time.new("2020-12-25 000:56:17 +0900")
@@ -581,19 +579,19 @@ describe "Time.new with a timezone argument" do
-> {
Time.new("2020-12-25 00:516:17 +0900")
- }.should raise_error(ArgumentError, "two digits min is expected after `:': :516:17 +09")
+ }.should raise_error(ArgumentError, /\Atwo digits min is expected after [`']:': :516:17 \+09\z/)
-> {
Time.new("2020-12-25 00:6:17 +0900")
- }.should raise_error(ArgumentError, "two digits min is expected after `:': :6:17 +0900")
+ }.should raise_error(ArgumentError, /\Atwo digits min is expected after [`']:': :6:17 \+0900\z/)
-> {
Time.new("2020-12-25 00:56:137 +0900")
- }.should raise_error(ArgumentError, "two digits sec is expected after `:': :137 +0900")
+ }.should raise_error(ArgumentError, /\Atwo digits sec is expected after [`']:': :137 \+0900\z/)
-> {
Time.new("2020-12-25 00:56:7 +0900")
- }.should raise_error(ArgumentError, "two digits sec is expected after `:': :7 +0900")
+ }.should raise_error(ArgumentError, /\Atwo digits sec is expected after [`']:': :7 \+0900\z/)
-> {
Time.new("2020-12-25 00:56. +0900")
diff --git a/spec/ruby/core/time/succ_spec.rb b/spec/ruby/core/time/succ_spec.rb
deleted file mode 100644
index cbf7cf0951..0000000000
--- a/spec/ruby/core/time/succ_spec.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-require_relative '../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require_relative 'fixtures/classes'
-
- describe "Time#succ" do
- it "returns a new time one second later than time" do
- suppress_warning {
- @result = Time.at(100).succ
- }
-
- @result.should == Time.at(101)
- end
-
- it "returns a new instance" do
- time = Time.at(100)
-
- suppress_warning {
- @result = time.succ
- }
-
- @result.should_not equal time
- end
-
- it "is obsolete" do
- -> {
- Time.at(100).succ
- }.should complain(/Time#succ is obsolete/)
- end
-
- context "zone is a timezone object" do
- it "preserves time zone" do
- zone = TimeSpecs::Timezone.new(offset: (5*3600+30*60))
- time = Time.new(2012, 1, 1, 12, 0, 0, zone) - 1
-
- time.zone.should == zone
- end
- end
- end
-end
diff --git a/spec/ruby/core/tracepoint/enable_spec.rb b/spec/ruby/core/tracepoint/enable_spec.rb
index 24f6070b97..6cc8bb3897 100644
--- a/spec/ruby/core/tracepoint/enable_spec.rb
+++ b/spec/ruby/core/tracepoint/enable_spec.rb
@@ -149,13 +149,7 @@ describe 'TracePoint#enable' do
describe "when nested" do
before do
- ruby_version_is ""..."3.0" do
- @path_prefix = '@'
- end
-
- ruby_version_is "3.0" do
- @path_prefix = ' '
- end
+ @path_prefix = ' '
end
it "enables both TracePoints but only calls the respective callbacks" do
diff --git a/spec/ruby/core/tracepoint/inspect_spec.rb b/spec/ruby/core/tracepoint/inspect_spec.rb
index 151a08e7b4..cc6bf0f842 100644
--- a/spec/ruby/core/tracepoint/inspect_spec.rb
+++ b/spec/ruby/core/tracepoint/inspect_spec.rb
@@ -3,13 +3,7 @@ require_relative 'fixtures/classes'
describe 'TracePoint#inspect' do
before do
- ruby_version_is ""..."3.0" do
- @path_prefix = '@'
- end
-
- ruby_version_is "3.0" do
- @path_prefix = ' '
- end
+ @path_prefix = ' '
end
it 'returns a string containing a human-readable TracePoint status' do
@@ -50,7 +44,7 @@ describe 'TracePoint#inspect' do
trace_point_spec_test_call
end
- inspect.should == "#<TracePoint:call `trace_point_spec_test_call'#{@path_prefix}#{__FILE__}:#{line}>"
+ inspect.should =~ /\A#<TracePoint:call [`']trace_point_spec_test_call'#{@path_prefix}#{__FILE__}:#{line}>\z/
end
it 'returns a String showing the event, method, path and line for a :return event' do
@@ -68,7 +62,7 @@ describe 'TracePoint#inspect' do
trace_point_spec_test_return
end
- inspect.should == "#<TracePoint:return `trace_point_spec_test_return'#{@path_prefix}#{__FILE__}:#{line}>"
+ inspect.should =~ /\A#<TracePoint:return [`']trace_point_spec_test_return'#{@path_prefix}#{__FILE__}:#{line}>\z/
end
it 'returns a String showing the event, method, path and line for a :c_call event' do
@@ -82,7 +76,7 @@ describe 'TracePoint#inspect' do
[0, 1].max
end
- inspect.should == "#<TracePoint:c_call `max'#{@path_prefix}#{__FILE__}:#{line}>"
+ inspect.should =~ /\A#<TracePoint:c_call [`']max'#{@path_prefix}#{__FILE__}:#{line}>\z/
end
it 'returns a String showing the event, path and line for a :class event' do
diff --git a/spec/ruby/core/tracepoint/path_spec.rb b/spec/ruby/core/tracepoint/path_spec.rb
index 5b6c6d4cfc..dc2ca840b8 100644
--- a/spec/ruby/core/tracepoint/path_spec.rb
+++ b/spec/ruby/core/tracepoint/path_spec.rb
@@ -13,14 +13,29 @@ describe 'TracePoint#path' do
path.should == "#{__FILE__}"
end
- it 'equals (eval) inside an eval for :end event' do
- path = nil
- TracePoint.new(:end) { |tp|
- next unless TracePointSpec.target_thread?
- path = tp.path
- }.enable do
- eval("module TracePointSpec; end")
+ ruby_version_is ""..."3.3" do
+ it 'equals (eval) inside an eval for :end event' do
+ path = nil
+ TracePoint.new(:end) { |tp|
+ next unless TracePointSpec.target_thread?
+ path = tp.path
+ }.enable do
+ eval("module TracePointSpec; end")
+ end
+ path.should == '(eval)'
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it 'equals "(eval at __FILE__:__LINE__)" inside an eval for :end event' do
+ path = nil
+ TracePoint.new(:end) { |tp|
+ next unless TracePointSpec.target_thread?
+ path = tp.path
+ }.enable do
+ eval("module TracePointSpec; end")
+ end
+ path.should == "(eval at #{__FILE__}:#{__LINE__ - 2})"
end
- path.should == '(eval)'
end
end
diff --git a/spec/ruby/core/true/singleton_method_spec.rb b/spec/ruby/core/true/singleton_method_spec.rb
new file mode 100644
index 0000000000..c06793850f
--- /dev/null
+++ b/spec/ruby/core/true/singleton_method_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+
+describe "TrueClass#singleton_method" do
+ ruby_version_is '3.3' do
+ it "raises regardless of whether TrueClass defines the method" do
+ -> { true.singleton_method(:foo) }.should raise_error(NameError)
+ begin
+ def (true).foo; end
+ -> { true.singleton_method(:foo) }.should raise_error(NameError)
+ ensure
+ TrueClass.send(:remove_method, :foo)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/unboundmethod/clone_spec.rb b/spec/ruby/core/unboundmethod/clone_spec.rb
index 098ee61476..1e7fb18744 100644
--- a/spec/ruby/core/unboundmethod/clone_spec.rb
+++ b/spec/ruby/core/unboundmethod/clone_spec.rb
@@ -1,12 +1,13 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
+require_relative 'shared/dup'
describe "UnboundMethod#clone" do
- it "returns a copy of the UnboundMethod" do
- um1 = UnboundMethodSpecs::Methods.instance_method(:foo)
- um2 = um1.clone
+ it_behaves_like :unboundmethod_dup, :clone
- (um1 == um2).should == true
- um1.bind(UnboundMethodSpecs::Methods.new).call.should == um2.bind(UnboundMethodSpecs::Methods.new).call
+ it "preserves frozen status" do
+ method = Class.instance_method(:instance_method)
+ method.freeze
+ method.frozen?.should == true
+ method.clone.frozen?.should == true
end
end
diff --git a/spec/ruby/core/unboundmethod/dup_spec.rb b/spec/ruby/core/unboundmethod/dup_spec.rb
new file mode 100644
index 0000000000..5a78dd8e36
--- /dev/null
+++ b/spec/ruby/core/unboundmethod/dup_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'shared/dup'
+
+describe "UnboundMethod#dup" do
+ ruby_version_is "3.4" do
+ it_behaves_like :unboundmethod_dup, :dup
+
+ it "resets frozen status" do
+ method = Class.instance_method(:instance_method)
+ method.freeze
+ method.frozen?.should == true
+ method.dup.frozen?.should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/unboundmethod/shared/dup.rb b/spec/ruby/core/unboundmethod/shared/dup.rb
new file mode 100644
index 0000000000..943a7faaa3
--- /dev/null
+++ b/spec/ruby/core/unboundmethod/shared/dup.rb
@@ -0,0 +1,32 @@
+describe :unboundmethod_dup, shared: true do
+ it "returns a copy of self" do
+ a = Class.instance_method(:instance_method)
+ b = a.send(@method)
+
+ a.should == b
+ a.should_not equal(b)
+ end
+
+ ruby_version_is "3.4" do
+ it "copies instance variables" do
+ method = Class.instance_method(:instance_method)
+ method.instance_variable_set(:@ivar, 1)
+ cl = method.send(@method)
+ cl.instance_variables.should == [:@ivar]
+ end
+
+ it "copies the finalizer" do
+ code = <<-RUBY
+ obj = Class.instance_method(:instance_method)
+
+ ObjectSpace.define_finalizer(obj, Proc.new { STDOUT.write "finalized\n" })
+
+ obj.clone
+
+ exit 0
+ RUBY
+
+ ruby_exe(code).lines.sort.should == ["finalized\n", "finalized\n"]
+ end
+ end
+end
diff --git a/spec/ruby/core/unboundmethod/source_location_spec.rb b/spec/ruby/core/unboundmethod/source_location_spec.rb
index c6823aa84b..5c2f14362c 100644
--- a/spec/ruby/core/unboundmethod/source_location_spec.rb
+++ b/spec/ruby/core/unboundmethod/source_location_spec.rb
@@ -9,7 +9,7 @@ describe "UnboundMethod#source_location" do
it "sets the first value to the path of the file in which the method was defined" do
file = @method.source_location.first
file.should be_an_instance_of(String)
- file.should == File.realpath('../fixtures/classes.rb', __FILE__)
+ file.should == File.realpath('fixtures/classes.rb', __dir__)
end
it "sets the last value to an Integer representing the line on which the method was defined" do
diff --git a/spec/ruby/core/unboundmethod/super_method_spec.rb b/spec/ruby/core/unboundmethod/super_method_spec.rb
index 101c83b8b3..aa7c129377 100644
--- a/spec/ruby/core/unboundmethod/super_method_spec.rb
+++ b/spec/ruby/core/unboundmethod/super_method_spec.rb
@@ -40,12 +40,10 @@ describe "UnboundMethod#super_method" do
end
end
- ruby_version_is "2.7.3" do
- context "after aliasing an inherited method" do
- it "returns the expected super_method" do
- method = MethodSpecs::InheritedMethods::C.instance_method(:meow)
- method.super_method.owner.should == MethodSpecs::InheritedMethods::A
- end
+ context "after aliasing an inherited method" do
+ it "returns the expected super_method" do
+ method = MethodSpecs::InheritedMethods::C.instance_method(:meow)
+ method.super_method.owner.should == MethodSpecs::InheritedMethods::A
end
end
end
diff --git a/spec/ruby/core/warning/element_reference_spec.rb b/spec/ruby/core/warning/element_reference_spec.rb
index 41129e533a..8cb4018c20 100644
--- a/spec/ruby/core/warning/element_reference_spec.rb
+++ b/spec/ruby/core/warning/element_reference_spec.rb
@@ -1,11 +1,9 @@
require_relative '../../spec_helper'
describe "Warning.[]" do
- ruby_version_is '2.7.2' do
- it "returns default values for categories :deprecated and :experimental" do
- ruby_exe('p Warning[:deprecated]').chomp.should == "false"
- ruby_exe('p Warning[:experimental]').chomp.should == "true"
- end
+ it "returns default values for categories :deprecated and :experimental" do
+ ruby_exe('p [Warning[:deprecated], Warning[:experimental]]').chomp.should == "[false, true]"
+ ruby_exe('p [Warning[:deprecated], Warning[:experimental]]', options: "-w").chomp.should == "[true, true]"
end
ruby_version_is '3.3' do
diff --git a/spec/ruby/core/warning/element_set_spec.rb b/spec/ruby/core/warning/element_set_spec.rb
index d20ee215ad..d59a7d4c9e 100644
--- a/spec/ruby/core/warning/element_set_spec.rb
+++ b/spec/ruby/core/warning/element_set_spec.rb
@@ -8,13 +8,7 @@ describe "Warning.[]=" do
describe ":experimental" do
before do
- ruby_version_is ""..."3.0" do
- @src = 'case [0, 1]; in [a, b]; end'
- end
-
- ruby_version_is "3.0" do
- @src = 'warn "This is experimental warning.", category: :experimental'
- end
+ @src = 'warn "This is experimental warning.", category: :experimental'
end
it "emits and suppresses warnings for :experimental" do
@@ -23,6 +17,18 @@ describe "Warning.[]=" do
end
end
+ ruby_version_is '3.3' do
+ it "enables or disables performance warnings" do
+ original = Warning[:performance]
+ begin
+ Warning[:performance] = !original
+ Warning[:performance].should == !original
+ ensure
+ Warning[:performance] = original
+ end
+ end
+ end
+
it "raises for unknown category" do
-> { Warning[:noop] = false }.should raise_error(ArgumentError, /unknown category: noop/)
end
diff --git a/spec/ruby/core/warning/warn_spec.rb b/spec/ruby/core/warning/warn_spec.rb
index e2fcfbf93f..8f96fe9287 100644
--- a/spec/ruby/core/warning/warn_spec.rb
+++ b/spec/ruby/core/warning/warn_spec.rb
@@ -51,100 +51,85 @@ describe "Warning.warn" do
end
end
- ruby_version_is '3.0' do
- it "is called by Kernel.warn with nil category keyword" do
- Warning.should_receive(:warn).with("Chunky bacon!\n", category: nil)
- verbose = $VERBOSE
- $VERBOSE = false
- begin
- Kernel.warn("Chunky bacon!")
- ensure
- $VERBOSE = verbose
- end
- end
-
- it "is called by Kernel.warn with given category keyword converted to a symbol" do
- Warning.should_receive(:warn).with("Chunky bacon!\n", category: :deprecated)
- verbose = $VERBOSE
- $VERBOSE = false
- begin
- Kernel.warn("Chunky bacon!", category: "deprecated")
- ensure
- $VERBOSE = verbose
- end
+ it "is called by Kernel.warn with nil category keyword" do
+ Warning.should_receive(:warn).with("Chunky bacon!\n", category: nil)
+ verbose = $VERBOSE
+ $VERBOSE = false
+ begin
+ Kernel.warn("Chunky bacon!")
+ ensure
+ $VERBOSE = verbose
end
+ end
- it "warns when category is :deprecated and Warning[:deprecated] is true" do
- warn_deprecated = Warning[:deprecated]
- Warning[:deprecated] = true
- begin
- -> {
- Warning.warn("foo", category: :deprecated)
- }.should complain("foo")
- ensure
- Warning[:deprecated] = warn_deprecated
- end
+ it "is called by Kernel.warn with given category keyword converted to a symbol" do
+ Warning.should_receive(:warn).with("Chunky bacon!\n", category: :deprecated)
+ verbose = $VERBOSE
+ $VERBOSE = false
+ begin
+ Kernel.warn("Chunky bacon!", category: "deprecated")
+ ensure
+ $VERBOSE = verbose
end
+ end
- it "warns when category is :experimental and Warning[:experimental] is true" do
- warn_experimental = Warning[:experimental]
- Warning[:experimental] = true
- begin
- -> {
- Warning.warn("foo", category: :experimental)
- }.should complain("foo")
- ensure
- Warning[:experimental] = warn_experimental
- end
+ it "warns when category is :deprecated and Warning[:deprecated] is true" do
+ warn_deprecated = Warning[:deprecated]
+ Warning[:deprecated] = true
+ begin
+ -> {
+ Warning.warn("foo", category: :deprecated)
+ }.should complain("foo")
+ ensure
+ Warning[:deprecated] = warn_deprecated
end
+ end
- it "doesn't print message when category is :deprecated but Warning[:deprecated] is false" do
- warn_deprecated = Warning[:deprecated]
- Warning[:deprecated] = false
- begin
- -> {
- Warning.warn("foo", category: :deprecated)
- }.should_not complain
- ensure
- Warning[:deprecated] = warn_deprecated
- end
+ it "warns when category is :experimental and Warning[:experimental] is true" do
+ warn_experimental = Warning[:experimental]
+ Warning[:experimental] = true
+ begin
+ -> {
+ Warning.warn("foo", category: :experimental)
+ }.should complain("foo")
+ ensure
+ Warning[:experimental] = warn_experimental
end
+ end
- it "doesn't print message when category is :experimental but Warning[:experimental] is false" do
- warn_experimental = Warning[:experimental]
- Warning[:experimental] = false
- begin
- -> {
- Warning.warn("foo", category: :experimental)
- }.should_not complain
- ensure
- Warning[:experimental] = warn_experimental
- end
+ it "doesn't print message when category is :deprecated but Warning[:deprecated] is false" do
+ warn_deprecated = Warning[:deprecated]
+ Warning[:deprecated] = false
+ begin
+ -> {
+ Warning.warn("foo", category: :deprecated)
+ }.should_not complain
+ ensure
+ Warning[:deprecated] = warn_deprecated
end
+ end
- it "prints the message when VERBOSE is false" do
- -> { Warning.warn("foo") }.should complain("foo")
+ it "doesn't print message when category is :experimental but Warning[:experimental] is false" do
+ warn_experimental = Warning[:experimental]
+ Warning[:experimental] = false
+ begin
+ -> {
+ Warning.warn("foo", category: :experimental)
+ }.should_not complain
+ ensure
+ Warning[:experimental] = warn_experimental
end
+ end
- it "prints the message when VERBOSE is nil" do
- -> { Warning.warn("foo") }.should complain("foo", verbose: nil)
- end
+ it "prints the message when VERBOSE is false" do
+ -> { Warning.warn("foo") }.should complain("foo")
+ end
- it "prints the message when VERBOSE is true" do
- -> { Warning.warn("foo") }.should complain("foo", verbose: true)
- end
+ it "prints the message when VERBOSE is nil" do
+ -> { Warning.warn("foo") }.should complain("foo", verbose: nil)
end
- ruby_version_is ''...'3.0' do
- it "is called by Kernel.warn" do
- Warning.should_receive(:warn).with("Chunky bacon!\n")
- verbose = $VERBOSE
- $VERBOSE = false
- begin
- Kernel.warn("Chunky bacon!")
- ensure
- $VERBOSE = verbose
- end
- end
+ it "prints the message when VERBOSE is true" do
+ -> { Warning.warn("foo") }.should complain("foo", verbose: true)
end
end
diff --git a/spec/ruby/default.mspec b/spec/ruby/default.mspec
index a0dc69c03d..1e8f8893aa 100644
--- a/spec/ruby/default.mspec
+++ b/spec/ruby/default.mspec
@@ -1,3 +1,4 @@
+# -*- ruby -*-
# Configuration file for Ruby >= 2.0 implementations.
class MSpecScript
diff --git a/spec/ruby/fixtures/code/d/load_fixture.rb.rb b/spec/ruby/fixtures/code/d/load_fixture.rb.rb
new file mode 100644
index 0000000000..7e9217729a
--- /dev/null
+++ b/spec/ruby/fixtures/code/d/load_fixture.rb.rb
@@ -0,0 +1 @@
+ScratchPad << :rbrb
diff --git a/spec/ruby/language/alias_spec.rb b/spec/ruby/language/alias_spec.rb
index c353390679..61fddb0184 100644
--- a/spec/ruby/language/alias_spec.rb
+++ b/spec/ruby/language/alias_spec.rb
@@ -52,6 +52,15 @@ describe "The alias keyword" do
@obj.a.should == 5
end
+ it "works with an interpolated symbol with non-literal embedded expression on the left-hand side" do
+ @meta.class_eval do
+ eval %Q{
+ alias :"#{'a' + ''.to_s}" value
+ }
+ end
+ @obj.a.should == 5
+ end
+
it "works with a simple symbol on the right-hand side" do
@meta.class_eval do
alias a :value
@@ -80,6 +89,15 @@ describe "The alias keyword" do
@obj.a.should == 5
end
+ it "works with an interpolated symbol with non-literal embedded expression on the right-hand side" do
+ @meta.class_eval do
+ eval %Q{
+ alias a :"#{'value' + ''.to_s}"
+ }
+ end
+ @obj.a.should == 5
+ end
+
it "adds the new method to the list of methods" do
original_methods = @obj.methods
@meta.class_eval do
@@ -234,7 +252,7 @@ describe "The alias keyword" do
it "on top level defines the alias on Object" do
# because it defines on the default definee / current module
- ruby_exe("def foo; end; alias bla foo; print method(:bla).owner", escape: true).should == "Object"
+ ruby_exe("def foo; end; alias bla foo; print method(:bla).owner").should == "Object"
end
it "raises a NameError when passed a missing name" do
diff --git a/spec/ruby/language/assignments_spec.rb b/spec/ruby/language/assignments_spec.rb
new file mode 100644
index 0000000000..2773508d8d
--- /dev/null
+++ b/spec/ruby/language/assignments_spec.rb
@@ -0,0 +1,529 @@
+require_relative '../spec_helper'
+
+# Should be synchronized with spec/ruby/language/optional_assignments_spec.rb
+# Some specs for assignments are located in language/variables_spec.rb
+describe 'Assignments' do
+ describe 'using =' do
+ describe 'evaluation order' do
+ it 'evaluates expressions left to right when assignment with an accessor' do
+ object = Object.new
+ def object.a=(value) end
+ ScratchPad.record []
+
+ (ScratchPad << :receiver; object).a = (ScratchPad << :rhs; :value)
+ ScratchPad.recorded.should == [:receiver, :rhs]
+ end
+
+ it 'evaluates expressions left to right when assignment with a #[]=' do
+ object = Object.new
+ def object.[]=(_, _) end
+ ScratchPad.record []
+
+ (ScratchPad << :receiver; object)[(ScratchPad << :argument; :a)] = (ScratchPad << :rhs; :value)
+ ScratchPad.recorded.should == [:receiver, :argument, :rhs]
+ end
+
+ # similar tests for evaluation order are located in language/constants_spec.rb
+ ruby_version_is ''...'3.2' do
+ it 'evaluates expressions right to left when assignment with compounded constant' do
+ m = Module.new
+ ScratchPad.record []
+
+ (ScratchPad << :module; m)::A = (ScratchPad << :rhs; :value)
+ ScratchPad.recorded.should == [:rhs, :module]
+ end
+ end
+
+ ruby_version_is '3.2' do
+ it 'evaluates expressions left to right when assignment with compounded constant' do
+ m = Module.new
+ ScratchPad.record []
+
+ (ScratchPad << :module; m)::A = (ScratchPad << :rhs; :value)
+ ScratchPad.recorded.should == [:module, :rhs]
+ end
+ end
+
+ it 'raises TypeError after evaluation of right-hand-side when compounded constant module is not a module' do
+ ScratchPad.record []
+
+ -> {
+ (:not_a_module)::A = (ScratchPad << :rhs; :value)
+ }.should raise_error(TypeError)
+
+ ScratchPad.recorded.should == [:rhs]
+ end
+ end
+ end
+
+ describe 'using +=' do
+ describe 'using an accessor' do
+ before do
+ klass = Class.new { attr_accessor :b }
+ @a = klass.new
+ end
+
+ it 'does evaluate receiver only once when assigns' do
+ ScratchPad.record []
+ @a.b = 1
+
+ (ScratchPad << :evaluated; @a).b += 2
+
+ ScratchPad.recorded.should == [:evaluated]
+ @a.b.should == 3
+ end
+
+ it 'ignores method visibility when receiver is self' do
+ klass_with_private_methods = Class.new do
+ def initialize(n) @a = n end
+ def public_method(n); self.a += n end
+ private
+ def a; @a end
+ def a=(n) @a = n; 42 end
+ end
+
+ a = klass_with_private_methods.new(0)
+ a.public_method(2).should == 2
+ end
+ end
+
+ describe 'using a #[]' do
+ before do
+ klass = Class.new do
+ def [](k)
+ @hash ||= {}
+ @hash[k]
+ end
+
+ def []=(k, v)
+ @hash ||= {}
+ @hash[k] = v
+ 7
+ end
+ end
+ @b = klass.new
+ end
+
+ it 'evaluates receiver only once when assigns' do
+ ScratchPad.record []
+ a = {k: 1}
+
+ (ScratchPad << :evaluated; a)[:k] += 2
+
+ ScratchPad.recorded.should == [:evaluated]
+ a[:k].should == 3
+ end
+
+ it 'ignores method visibility when receiver is self' do
+ klass_with_private_methods = Class.new do
+ def initialize(h) @a = h end
+ def public_method(k, n); self[k] += n end
+ private
+ def [](k) @a[k] end
+ def []=(k, v) @a[k] = v; 42 end
+ end
+
+ a = klass_with_private_methods.new(k: 0)
+ a.public_method(:k, 2).should == 2
+ end
+
+ context 'splatted argument' do
+ it 'correctly handles it' do
+ @b[:m] = 10
+ (@b[*[:m]] += 10).should == 20
+ @b[:m].should == 20
+
+ @b[:n] = 10
+ (@b[*(1; [:n])] += 10).should == 20
+ @b[:n].should == 20
+
+ @b[:k] = 10
+ (@b[*begin 1; [:k] end] += 10).should == 20
+ @b[:k].should == 20
+ end
+
+ it 'calls #to_a only once' do
+ k = Object.new
+ def k.to_a
+ ScratchPad << :to_a
+ [:k]
+ end
+
+ ScratchPad.record []
+ @b[:k] = 10
+ (@b[*k] += 10).should == 20
+ @b[:k].should == 20
+ ScratchPad.recorded.should == [:to_a]
+ end
+
+ it 'correctly handles a nested splatted argument' do
+ @b[:k] = 10
+ (@b[*[*[:k]]] += 10).should == 20
+ @b[:k].should == 20
+ end
+
+ it 'correctly handles multiple nested splatted arguments' do
+ klass_with_multiple_parameters = Class.new do
+ def [](k1, k2, k3)
+ @hash ||= {}
+ @hash[:"#{k1}#{k2}#{k3}"]
+ end
+
+ def []=(k1, k2, k3, v)
+ @hash ||= {}
+ @hash[:"#{k1}#{k2}#{k3}"] = v
+ 7
+ end
+ end
+ a = klass_with_multiple_parameters.new
+
+ a[:a, :b, :c] = 10
+ (a[*[:a], *[:b], *[:c]] += 10).should == 20
+ a[:a, :b, :c].should == 20
+ end
+ end
+ end
+
+ describe 'using compounded constants' do
+ it 'causes side-effects of the module part to be applied only once (when assigns)' do
+ module ConstantSpecs
+ OpAssignTrue = 1
+ end
+
+ suppress_warning do # already initialized constant
+ x = 0
+ (x += 1; ConstantSpecs)::OpAssignTrue += 2
+ x.should == 1
+ ConstantSpecs::OpAssignTrue.should == 3
+ end
+
+ ConstantSpecs.send :remove_const, :OpAssignTrue
+ end
+ end
+ end
+end
+
+# generic cases
+describe 'Multiple assignments' do
+ it 'assigns multiple targets when assignment with an accessor' do
+ object = Object.new
+ class << object
+ attr_accessor :a, :b
+ end
+
+ object.a, object.b = :a, :b
+
+ object.a.should == :a
+ object.b.should == :b
+ end
+
+ it 'assigns multiple targets when assignment with a nested accessor' do
+ object = Object.new
+ class << object
+ attr_accessor :a, :b
+ end
+
+ (object.a, object.b), c = [:a, :b], nil
+
+ object.a.should == :a
+ object.b.should == :b
+ end
+
+ it 'assigns multiple targets when assignment with a #[]=' do
+ object = Object.new
+ class << object
+ def []=(k, v) (@h ||= {})[k] = v; end
+ def [](k) (@h ||= {})[k]; end
+ end
+
+ object[:a], object[:b] = :a, :b
+
+ object[:a].should == :a
+ object[:b].should == :b
+ end
+
+ it 'assigns multiple targets when assignment with a nested #[]=' do
+ object = Object.new
+ class << object
+ def []=(k, v) (@h ||= {})[k] = v; end
+ def [](k) (@h ||= {})[k]; end
+ end
+
+ (object[:a], object[:b]), c = [:v1, :v2], nil
+
+ object[:a].should == :v1
+ object[:b].should == :v2
+ end
+
+ it 'assigns multiple targets when assignment with compounded constant' do
+ m = Module.new
+
+ m::A, m::B = :a, :b
+
+ m::A.should == :a
+ m::B.should == :b
+ end
+
+ it 'assigns multiple targets when assignment with a nested compounded constant' do
+ m = Module.new
+
+ (m::A, m::B), c = [:a, :b], nil
+
+ m::A.should == :a
+ m::B.should == :b
+ end
+end
+
+describe 'Multiple assignments' do
+ describe 'evaluation order' do
+ ruby_version_is ''...'3.1' do
+ it 'evaluates expressions right to left when assignment with an accessor' do
+ object = Object.new
+ def object.a=(value) end
+ ScratchPad.record []
+
+ (ScratchPad << :a; object).a, (ScratchPad << :b; object).a = (ScratchPad << :c; :c), (ScratchPad << :d; :d)
+ ScratchPad.recorded.should == [:c, :d, :a, :b]
+ end
+
+ it 'evaluates expressions right to left when assignment with a nested accessor' do
+ object = Object.new
+ def object.a=(value) end
+ ScratchPad.record []
+
+ ((ScratchPad << :a; object).a, foo), bar = [(ScratchPad << :b; :b)]
+ ScratchPad.recorded.should == [:b, :a]
+ end
+ end
+
+ ruby_version_is '3.1' do
+ it 'evaluates expressions left to right when assignment with an accessor' do
+ object = Object.new
+ def object.a=(value) end
+ ScratchPad.record []
+
+ (ScratchPad << :a; object).a, (ScratchPad << :b; object).a = (ScratchPad << :c; :c), (ScratchPad << :d; :d)
+ ScratchPad.recorded.should == [:a, :b, :c, :d]
+ end
+
+ it 'evaluates expressions left to right when assignment with a nested accessor' do
+ object = Object.new
+ def object.a=(value) end
+ ScratchPad.record []
+
+ ((ScratchPad << :a; object).a, foo), bar = [(ScratchPad << :b; :b)]
+ ScratchPad.recorded.should == [:a, :b]
+ end
+
+ it 'evaluates expressions left to right when assignment with a deeply nested accessor' do
+ o = Object.new
+ def o.a=(value) end
+ def o.b=(value) end
+ def o.c=(value) end
+ def o.d=(value) end
+ def o.e=(value) end
+ def o.f=(value) end
+ ScratchPad.record []
+
+ (ScratchPad << :a; o).a,
+ ((ScratchPad << :b; o).b,
+ ((ScratchPad << :c; o).c, (ScratchPad << :d; o).d),
+ (ScratchPad << :e; o).e),
+ (ScratchPad << :f; o).f = (ScratchPad << :value; :value)
+
+ ScratchPad.recorded.should == [:a, :b, :c, :d, :e, :f, :value]
+ end
+ end
+
+ ruby_version_is ''...'3.1' do
+ it 'evaluates expressions right to left when assignment with a #[]=' do
+ object = Object.new
+ def object.[]=(_, _) end
+ ScratchPad.record []
+
+ (ScratchPad << :a; object)[(ScratchPad << :b; :b)], (ScratchPad << :c; object)[(ScratchPad << :d; :d)] = (ScratchPad << :e; :e), (ScratchPad << :f; :f)
+ ScratchPad.recorded.should == [:e, :f, :a, :b, :c, :d]
+ end
+
+ it 'evaluates expressions right to left when assignment with a nested #[]=' do
+ object = Object.new
+ def object.[]=(_, _) end
+ ScratchPad.record []
+
+ ((ScratchPad << :a; object)[(ScratchPad << :b; :b)], foo), bar = [(ScratchPad << :c; :c)]
+ ScratchPad.recorded.should == [:c, :a, :b]
+ end
+ end
+
+ ruby_version_is '3.1' do
+ it 'evaluates expressions left to right when assignment with a #[]=' do
+ object = Object.new
+ def object.[]=(_, _) end
+ ScratchPad.record []
+
+ (ScratchPad << :a; object)[(ScratchPad << :b; :b)], (ScratchPad << :c; object)[(ScratchPad << :d; :d)] = (ScratchPad << :e; :e), (ScratchPad << :f; :f)
+ ScratchPad.recorded.should == [:a, :b, :c, :d, :e, :f]
+ end
+
+ it 'evaluates expressions left to right when assignment with a nested #[]=' do
+ object = Object.new
+ def object.[]=(_, _) end
+ ScratchPad.record []
+
+ ((ScratchPad << :a; object)[(ScratchPad << :b; :b)], foo), bar = [(ScratchPad << :c; :c)]
+ ScratchPad.recorded.should == [:a, :b, :c]
+ end
+
+ it 'evaluates expressions left to right when assignment with a deeply nested #[]=' do
+ o = Object.new
+ def o.[]=(_, _) end
+ ScratchPad.record []
+
+ (ScratchPad << :ra; o)[(ScratchPad << :aa; :aa)],
+ ((ScratchPad << :rb; o)[(ScratchPad << :ab; :ab)],
+ ((ScratchPad << :rc; o)[(ScratchPad << :ac; :ac)], (ScratchPad << :rd; o)[(ScratchPad << :ad; :ad)]),
+ (ScratchPad << :re; o)[(ScratchPad << :ae; :ae)]),
+ (ScratchPad << :rf; o)[(ScratchPad << :af; :af)] = (ScratchPad << :value; :value)
+
+ ScratchPad.recorded.should == [:ra, :aa, :rb, :ab, :rc, :ac, :rd, :ad, :re, :ae, :rf, :af, :value]
+ end
+ end
+
+ ruby_version_is ''...'3.2' do
+ it 'evaluates expressions right to left when assignment with compounded constant' do
+ m = Module.new
+ ScratchPad.record []
+
+ (ScratchPad << :a; m)::A, (ScratchPad << :b; m)::B = (ScratchPad << :c; :c), (ScratchPad << :d; :d)
+ ScratchPad.recorded.should == [:c, :d, :a, :b]
+ end
+ end
+
+ ruby_version_is '3.2' do
+ it 'evaluates expressions left to right when assignment with compounded constant' do
+ m = Module.new
+ ScratchPad.record []
+
+ (ScratchPad << :a; m)::A, (ScratchPad << :b; m)::B = (ScratchPad << :c; :c), (ScratchPad << :d; :d)
+ ScratchPad.recorded.should == [:a, :b, :c, :d]
+ end
+
+ it 'evaluates expressions left to right when assignment with a nested compounded constant' do
+ m = Module.new
+ ScratchPad.record []
+
+ ((ScratchPad << :a; m)::A, foo), bar = [(ScratchPad << :b; :b)]
+ ScratchPad.recorded.should == [:a, :b]
+ end
+
+ it 'evaluates expressions left to right when assignment with deeply nested compounded constants' do
+ m = Module.new
+ ScratchPad.record []
+
+ (ScratchPad << :a; m)::A,
+ ((ScratchPad << :b; m)::B,
+ ((ScratchPad << :c; m)::C, (ScratchPad << :d; m)::D),
+ (ScratchPad << :e; m)::E),
+ (ScratchPad << :f; m)::F = (ScratchPad << :value; :value)
+
+ ScratchPad.recorded.should == [:a, :b, :c, :d, :e, :f, :value]
+ end
+ end
+ end
+
+ context 'when assignment with method call and receiver is self' do
+ it 'assigns values correctly when assignment with accessor' do
+ object = Object.new
+ class << object
+ attr_accessor :a, :b
+
+ def assign(v1, v2)
+ self.a, self.b = v1, v2
+ end
+ end
+
+ object.assign :v1, :v2
+ object.a.should == :v1
+ object.b.should == :v2
+ end
+
+ it 'evaluates expressions right to left when assignment with a nested accessor' do
+ object = Object.new
+ class << object
+ attr_accessor :a, :b
+
+ def assign(v1, v2)
+ (self.a, self.b), c = [v1, v2], nil
+ end
+ end
+
+ object.assign :v1, :v2
+ object.a.should == :v1
+ object.b.should == :v2
+ end
+
+ it 'assigns values correctly when assignment with a #[]=' do
+ object = Object.new
+ class << object
+ def []=(key, v)
+ @h ||= {}
+ @h[key] = v
+ end
+
+ def [](key)
+ (@h || {})[key]
+ end
+
+ def assign(k1, v1, k2, v2)
+ self[k1], self[k2] = v1, v2
+ end
+ end
+
+ object.assign :k1, :v1, :k2, :v2
+ object[:k1].should == :v1
+ object[:k2].should == :v2
+ end
+
+ it 'assigns values correctly when assignment with a nested #[]=' do
+ object = Object.new
+ class << object
+ def []=(key, v)
+ @h ||= {}
+ @h[key] = v
+ end
+
+ def [](key)
+ (@h || {})[key]
+ end
+
+ def assign(k1, v1, k2, v2)
+ (self[k1], self[k2]), c = [v1, v2], nil
+ end
+ end
+
+ object.assign :k1, :v1, :k2, :v2
+ object[:k1].should == :v1
+ object[:k2].should == :v2
+ end
+
+ it 'assigns values correctly when assignment with compounded constant' do
+ m = Module.new
+ m.module_exec do
+ self::A, self::B = :v1, :v2
+ end
+
+ m::A.should == :v1
+ m::B.should == :v2
+ end
+
+ it 'assigns values correctly when assignment with a nested compounded constant' do
+ m = Module.new
+ m.module_exec do
+ (self::A, self::B), c = [:v1, :v2], nil
+ end
+
+ m::A.should == :v1
+ m::B.should == :v2
+ end
+ end
+end
diff --git a/spec/ruby/language/block_spec.rb b/spec/ruby/language/block_spec.rb
index 8488b945d5..578d9cb3b0 100644
--- a/spec/ruby/language/block_spec.rb
+++ b/spec/ruby/language/block_spec.rb
@@ -40,79 +40,73 @@ describe "A block yielded a single" do
m([1, 2]) { |a=5, b, c, d| [a, b, c, d] }.should == [5, 1, 2, nil]
end
- ruby_version_is "3.2" do
- it "does not autosplat single argument to required arguments when a keyword rest argument is present" do
- m([1, 2]) { |a, **k| [a, k] }.should == [[1, 2], {}]
- end
+ it "assigns elements to pre arguments" do
+ m([1, 2]) { |a, b, c, d=5| [a, b, c, d] }.should == [1, 2, nil, 5]
end
- ruby_version_is ''..."3.2" do
- # https://bugs.ruby-lang.org/issues/18633
- it "autosplats single argument to required arguments when a keyword rest argument is present" do
- m([1, 2]) { |a, **k| [a, k] }.should == [1, {}]
- end
+ it "assigns elements to pre and post arguments" do
+ m([1 ]) { |a, b=5, c=6, d, e| [a, b, c, d, e] }.should == [1, 5, 6, nil, nil]
+ m([1, 2 ]) { |a, b=5, c=6, d, e| [a, b, c, d, e] }.should == [1, 5, 6, 2, nil]
+ m([1, 2, 3 ]) { |a, b=5, c=6, d, e| [a, b, c, d, e] }.should == [1, 5, 6, 2, 3]
+ m([1, 2, 3, 4 ]) { |a, b=5, c=6, d, e| [a, b, c, d, e] }.should == [1, 2, 6, 3, 4]
+ m([1, 2, 3, 4, 5 ]) { |a, b=5, c=6, d, e| [a, b, c, d, e] }.should == [1, 2, 3, 4, 5]
+ m([1, 2, 3, 4, 5, 6]) { |a, b=5, c=6, d, e| [a, b, c, d, e] }.should == [1, 2, 3, 4, 5]
end
- ruby_version_is ''..."3.0" do
- it "assigns elements to mixed argument types" do
- suppress_keyword_warning do
- result = m([1, 2, 3, {x: 9}]) { |a, b=5, *c, d, e: 2, **k| [a, b, c, d, e, k] }
- result.should == [1, 2, [], 3, 2, {x: 9}]
- end
- end
+ it "assigns elements to pre and post arguments when *rest is present" do
+ m([1 ]) { |a, b=5, c=6, *d, e, f| [a, b, c, d, e, f] }.should == [1, 5, 6, [], nil, nil]
+ m([1, 2 ]) { |a, b=5, c=6, *d, e, f| [a, b, c, d, e, f] }.should == [1, 5, 6, [], 2, nil]
+ m([1, 2, 3 ]) { |a, b=5, c=6, *d, e, f| [a, b, c, d, e, f] }.should == [1, 5, 6, [], 2, 3]
+ m([1, 2, 3, 4 ]) { |a, b=5, c=6, *d, e, f| [a, b, c, d, e, f] }.should == [1, 2, 6, [], 3, 4]
+ m([1, 2, 3, 4, 5 ]) { |a, b=5, c=6, *d, e, f| [a, b, c, d, e, f] }.should == [1, 2, 3, [], 4, 5]
+ m([1, 2, 3, 4, 5, 6]) { |a, b=5, c=6, *d, e, f| [a, b, c, d, e, f] }.should == [1, 2, 3, [4], 5, 6]
+ end
- it "assigns symbol keys from a Hash to keyword arguments" do
- suppress_keyword_warning do
- result = m(["a" => 1, a: 10]) { |a=nil, **b| [a, b] }
- result.should == [{"a" => 1}, a: 10]
- end
+ ruby_version_is "3.2" do
+ it "does not autosplat single argument to required arguments when a keyword rest argument is present" do
+ m([1, 2]) { |a, **k| [a, k] }.should == [[1, 2], {}]
end
- it "assigns symbol keys from a Hash returned by #to_hash to keyword arguments" do
- suppress_keyword_warning do
- obj = mock("coerce block keyword arguments")
- obj.should_receive(:to_hash).and_return({"a" => 1, b: 2})
+ it "does not autosplat single argument to required arguments when keyword arguments are present" do
+ m([1, 2]) { |a, b: :b, c: :c| [a, b, c] }.should == [[1, 2], :b, :c]
+ end
- result = m([obj]) { |a=nil, **b| [a, b] }
- result.should == [{"a" => 1}, b: 2]
- end
+ it "raises error when required keyword arguments are present" do
+ -> {
+ m([1, 2]) { |a, b:, c:| [a, b, c] }
+ }.should raise_error(ArgumentError, "missing keywords: :b, :c")
end
end
- ruby_version_is "3.0" do
- it "assigns elements to mixed argument types" do
- result = m([1, 2, 3, {x: 9}]) { |a, b=5, *c, d, e: 2, **k| [a, b, c, d, e, k] }
- result.should == [1, 2, [3], {x: 9}, 2, {}]
+ ruby_version_is ''..."3.2" do
+ # https://bugs.ruby-lang.org/issues/18633
+ it "autosplats single argument to required arguments when a keyword rest argument is present" do
+ m([1, 2]) { |a, **k| [a, k] }.should == [1, {}]
end
- it "does not treat final Hash as keyword arguments and does not autosplat" do
- result = m(["a" => 1, a: 10]) { |a=nil, **b| [a, b] }
- result.should == [[{"a" => 1, a: 10}], {}]
+ it "autosplats single argument to required arguments when optional keyword arguments are present" do
+ m([1, 2]) { |a, b: :b, c: :c| [a, b, c] }.should == [1, :b, :c]
end
- it "does not call #to_hash on final argument to get keyword arguments and does not autosplat" do
- suppress_keyword_warning do
- obj = mock("coerce block keyword arguments")
- obj.should_not_receive(:to_hash)
-
- result = m([obj]) { |a=nil, **b| [a, b] }
- result.should == [[obj], {}]
- end
+ it "raises error when required keyword arguments are present" do
+ -> {
+ m([1, 2]) { |a, b:, c:| [a, b, c] }
+ }.should raise_error(ArgumentError, "missing keywords: :b, :c")
end
end
- ruby_version_is ""...'3.0' do
- it "calls #to_hash on the argument but ignores result when optional argument and keyword argument accepted" do
- obj = mock("coerce block keyword arguments")
- obj.should_receive(:to_hash).and_return({"a" => 1, "b" => 2})
+ it "assigns elements to mixed argument types" do
+ result = m([1, 2, 3, {x: 9}]) { |a, b=5, *c, d, e: 2, **k| [a, b, c, d, e, k] }
+ result.should == [1, 2, [3], {x: 9}, 2, {}]
+ end
- result = m([obj]) { |a=nil, **b| [a, b] }
- result.should == [obj, {}]
- end
+ it "does not treat final Hash as keyword arguments and does not autosplat" do
+ result = m(["a" => 1, a: 10]) { |a=nil, **b| [a, b] }
+ result.should == [[{"a" => 1, a: 10}], {}]
end
- ruby_version_is "3.0" do
- it "does not call #to_hash on the argument when optional argument and keyword argument accepted and does not autosplat" do
+ it "does not call #to_hash on final argument to get keyword arguments and does not autosplat" do
+ suppress_keyword_warning do
obj = mock("coerce block keyword arguments")
obj.should_not_receive(:to_hash)
@@ -121,102 +115,42 @@ describe "A block yielded a single" do
end
end
- describe "when non-symbol keys are in a keyword arguments Hash" do
- ruby_version_is ""..."3.0" do
- it "separates non-symbol keys and symbol keys" do
- suppress_keyword_warning do
- result = m(["a" => 10, b: 2]) { |a=nil, **b| [a, b] }
- result.should == [{"a" => 10}, {b: 2}]
- end
- end
- end
- ruby_version_is "3.0" do
- it "does not separate non-symbol keys and symbol keys and does not autosplat" do
- suppress_keyword_warning do
- result = m(["a" => 10, b: 2]) { |a=nil, **b| [a, b] }
- result.should == [[{"a" => 10, b: 2}], {}]
- end
- end
- end
- end
+ it "does not call #to_hash on the argument when optional argument and keyword argument accepted and does not autosplat" do
+ obj = mock("coerce block keyword arguments")
+ obj.should_not_receive(:to_hash)
- ruby_version_is ""..."3.0" do
- it "does not treat hashes with string keys as keyword arguments" do
- result = m(["a" => 10]) { |a = nil, **b| [a, b] }
- result.should == [{"a" => 10}, {}]
- end
+ result = m([obj]) { |a=nil, **b| [a, b] }
+ result.should == [[obj], {}]
end
- ruby_version_is "3.0" do
- it "does not treat hashes with string keys as keyword arguments and does not autosplat" do
- result = m(["a" => 10]) { |a = nil, **b| [a, b] }
- result.should == [[{"a" => 10}], {}]
- end
- end
-
- ruby_version_is ''...'3.0' do
- it "calls #to_hash on the last element if keyword arguments are present" do
- suppress_keyword_warning do
- obj = mock("destructure block keyword arguments")
- obj.should_receive(:to_hash).and_return({x: 9})
-
- result = m([1, 2, 3, obj]) { |a, *b, c, **k| [a, b, c, k] }
- result.should == [1, [2], 3, {x: 9}]
- end
- end
-
- it "assigns the last element to a non-keyword argument if #to_hash returns nil" do
- suppress_keyword_warning do
- obj = mock("destructure block keyword arguments")
- obj.should_receive(:to_hash).and_return(nil)
-
- result = m([1, 2, 3, obj]) { |a, *b, c, **k| [a, b, c, k] }
- result.should == [1, [2, 3], obj, {}]
- end
- end
-
- it "calls #to_hash on the last element when there are more arguments than parameters" do
+ describe "when non-symbol keys are in a keyword arguments Hash" do
+ it "does not separate non-symbol keys and symbol keys and does not autosplat" do
suppress_keyword_warning do
- x = mock("destructure matching block keyword argument")
- x.should_receive(:to_hash).and_return({x: 9})
-
- result = m([1, 2, 3, {y: 9}, 4, 5, x]) { |a, b=5, c, **k| [a, b, c, k] }
- result.should == [1, 2, 3, {x: 9}]
+ result = m(["a" => 10, b: 2]) { |a=nil, **b| [a, b] }
+ result.should == [[{"a" => 10, b: 2}], {}]
end
end
+ end
- it "raises a TypeError if #to_hash does not return a Hash" do
- obj = mock("destructure block keyword arguments")
- obj.should_receive(:to_hash).and_return(1)
-
- -> { m([1, 2, 3, obj]) { |a, *b, c, **k| } }.should raise_error(TypeError)
- end
-
- it "raises the error raised inside #to_hash" do
- obj = mock("destructure block keyword arguments")
- error = RuntimeError.new("error while converting to a hash")
- obj.should_receive(:to_hash).and_raise(error)
-
- -> { m([1, 2, 3, obj]) { |a, *b, c, **k| } }.should raise_error(error)
- end
+ it "does not treat hashes with string keys as keyword arguments and does not autosplat" do
+ result = m(["a" => 10]) { |a = nil, **b| [a, b] }
+ result.should == [[{"a" => 10}], {}]
end
- ruby_version_is '3.0' do
- it "does not call #to_hash on the last element if keyword arguments are present" do
- obj = mock("destructure block keyword arguments")
- obj.should_not_receive(:to_hash)
+ it "does not call #to_hash on the last element if keyword arguments are present" do
+ obj = mock("destructure block keyword arguments")
+ obj.should_not_receive(:to_hash)
- result = m([1, 2, 3, obj]) { |a, *b, c, **k| [a, b, c, k] }
- result.should == [1, [2, 3], obj, {}]
- end
+ result = m([1, 2, 3, obj]) { |a, *b, c, **k| [a, b, c, k] }
+ result.should == [1, [2, 3], obj, {}]
+ end
- it "does not call #to_hash on the last element when there are more arguments than parameters" do
- x = mock("destructure matching block keyword argument")
- x.should_not_receive(:to_hash)
+ it "does not call #to_hash on the last element when there are more arguments than parameters" do
+ x = mock("destructure matching block keyword argument")
+ x.should_not_receive(:to_hash)
- result = m([1, 2, 3, {y: 9}, 4, 5, x]) { |a, b=5, c, **k| [a, b, c, k] }
- result.should == [1, 2, 3, {}]
- end
+ result = m([1, 2, 3, {y: 9}, 4, 5, x]) { |a, b=5, c, **k| [a, b, c, k] }
+ result.should == [1, 2, 3, {}]
end
it "does not call #to_ary on the Array" do
@@ -476,7 +410,6 @@ describe "A block" do
-> { @y.s(obj) { |a, b| } }.should raise_error(ZeroDivisionError)
end
-
end
describe "taking |a, *b| arguments" do
@@ -809,6 +742,42 @@ describe "A block" do
eval("Proc.new { |_,_| }").should be_an_instance_of(Proc)
end
end
+
+ describe 'pre and post parameters' do
+ it "assigns nil to unassigned required arguments" do
+ proc { |a, *b, c, d| [a, b, c, d] }.call(1, 2).should == [1, [], 2, nil]
+ end
+
+ it "assigns elements to optional arguments" do
+ proc { |a=5, b=4, c=3| [a, b, c] }.call(1, 2).should == [1, 2, 3]
+ end
+
+ it "assigns elements to post arguments" do
+ proc { |a=5, b, c, d| [a, b, c, d] }.call(1, 2).should == [5, 1, 2, nil]
+ end
+
+ it "assigns elements to pre arguments" do
+ proc { |a, b, c, d=5| [a, b, c, d] }.call(1, 2).should == [1, 2, nil, 5]
+ end
+
+ it "assigns elements to pre and post arguments" do
+ proc { |a, b=5, c=6, d, e| [a, b, c, d, e] }.call(1 ).should == [1, 5, 6, nil, nil]
+ proc { |a, b=5, c=6, d, e| [a, b, c, d, e] }.call(1, 2 ).should == [1, 5, 6, 2, nil]
+ proc { |a, b=5, c=6, d, e| [a, b, c, d, e] }.call(1, 2, 3 ).should == [1, 5, 6, 2, 3]
+ proc { |a, b=5, c=6, d, e| [a, b, c, d, e] }.call(1, 2, 3, 4 ).should == [1, 2, 6, 3, 4]
+ proc { |a, b=5, c=6, d, e| [a, b, c, d, e] }.call(1, 2, 3, 4, 5 ).should == [1, 2, 3, 4, 5]
+ proc { |a, b=5, c=6, d, e| [a, b, c, d, e] }.call(1, 2, 3, 4, 5, 6).should == [1, 2, 3, 4, 5]
+ end
+
+ it "assigns elements to pre and post arguments when *rest is present" do
+ proc { |a, b=5, c=6, *d, e, f| [a, b, c, d, e, f] }.call(1 ).should == [1, 5, 6, [], nil, nil]
+ proc { |a, b=5, c=6, *d, e, f| [a, b, c, d, e, f] }.call(1, 2 ).should == [1, 5, 6, [], 2, nil]
+ proc { |a, b=5, c=6, *d, e, f| [a, b, c, d, e, f] }.call(1, 2, 3 ).should == [1, 5, 6, [], 2, 3]
+ proc { |a, b=5, c=6, *d, e, f| [a, b, c, d, e, f] }.call(1, 2, 3, 4 ).should == [1, 2, 6, [], 3, 4]
+ proc { |a, b=5, c=6, *d, e, f| [a, b, c, d, e, f] }.call(1, 2, 3, 4, 5 ).should == [1, 2, 3, [], 4, 5]
+ proc { |a, b=5, c=6, *d, e, f| [a, b, c, d, e, f] }.call(1, 2, 3, 4, 5, 6).should == [1, 2, 3, [4], 5, 6]
+ end
+ end
end
describe "Block-local variables" do
@@ -1029,7 +998,7 @@ end
describe "Anonymous block forwarding" do
ruby_version_is "3.1" do
- it "forwards blocks to other functions that formally declare anonymous blocks" do
+ it "forwards blocks to other method that formally declares anonymous block" do
eval <<-EOF
def b(&); c(&) end
def c(&); yield :non_null end
diff --git a/spec/ruby/language/break_spec.rb b/spec/ruby/language/break_spec.rb
index 627cb4a071..e725e77e80 100644
--- a/spec/ruby/language/break_spec.rb
+++ b/spec/ruby/language/break_spec.rb
@@ -372,7 +372,7 @@ describe "Executing break from within a block" do
end.should_not raise_error
end
- it "raises LocalJumpError when converted into a proc during a a super call" do
+ it "raises LocalJumpError when converted into a proc during a super call" do
cls1 = Class.new { def foo(&b); b; end }
cls2 = Class.new(cls1) { def foo; super { break 1 }.call; end }
diff --git a/spec/ruby/language/case_spec.rb b/spec/ruby/language/case_spec.rb
index 915c032a71..3262f09dd5 100644
--- a/spec/ruby/language/case_spec.rb
+++ b/spec/ruby/language/case_spec.rb
@@ -329,49 +329,6 @@ describe "The 'case'-construct" do
100
end.should == 100
end
-end
-
-describe "The 'case'-construct with no target expression" do
- it "evaluates the body of the first clause when at least one of its condition expressions is true" do
- case
- when true, false; 'foo'
- end.should == 'foo'
- end
-
- it "evaluates the body of the first when clause that is not false/nil" do
- case
- when false; 'foo'
- when 2; 'bar'
- when 1 == 1; 'baz'
- end.should == 'bar'
-
- case
- when false; 'foo'
- when nil; 'foo'
- when 1 == 1; 'bar'
- end.should == 'bar'
- end
-
- it "evaluates the body of the else clause if all when clauses are false/nil" do
- case
- when false; 'foo'
- when nil; 'foo'
- when 1 == 2; 'bar'
- else 'baz'
- end.should == 'baz'
- end
-
- it "evaluates multiple conditional expressions as a boolean disjunction" do
- case
- when true, false; 'foo'
- else 'bar'
- end.should == 'foo'
-
- case
- when false, true; 'foo'
- else 'bar'
- end.should == 'foo'
- end
it "evaluates true as only 'true' when true is the first clause" do
case 1
@@ -434,6 +391,87 @@ describe "The 'case'-construct with no target expression" do
end.should == :called
end
+ it "only matches last value in complex expressions within ()" do
+ case 'a'
+ when ('a'; 'b')
+ :wrong_called
+ when ('b'; 'a')
+ :called
+ end.should == :called
+ end
+
+ it "supports declaring variables in the case target expression" do
+ def test(v)
+ case new_variable_in_expression = v
+ when true
+ # This extra block is a test that `new_variable_in_expression` is declared outside of it and not inside
+ self.then { new_variable_in_expression }
+ else
+ # Same
+ self.then { new_variable_in_expression.casecmp?("foo") }
+ end
+ end
+
+ self.test("bar").should == false
+ self.test(true).should == true
+ end
+
+ it "warns if there are identical when clauses" do
+ -> {
+ eval <<~RUBY
+ case 1
+ when 2
+ :foo
+ when 2
+ :bar
+ end
+ RUBY
+ }.should complain(/warning: duplicated .when' clause with line \d+ is ignored/, verbose: true)
+ end
+end
+
+describe "The 'case'-construct with no target expression" do
+ it "evaluates the body of the first clause when at least one of its condition expressions is true" do
+ case
+ when true, false; 'foo'
+ end.should == 'foo'
+ end
+
+ it "evaluates the body of the first when clause that is not false/nil" do
+ case
+ when false; 'foo'
+ when 2; 'bar'
+ when 1 == 1; 'baz'
+ end.should == 'bar'
+
+ case
+ when false; 'foo'
+ when nil; 'foo'
+ when 1 == 1; 'bar'
+ end.should == 'bar'
+ end
+
+ it "evaluates the body of the else clause if all when clauses are false/nil" do
+ case
+ when false; 'foo'
+ when nil; 'foo'
+ when 1 == 2; 'bar'
+ else 'baz'
+ end.should == 'baz'
+ end
+
+ it "evaluates multiple conditional expressions as a boolean disjunction" do
+ case
+ when true, false; 'foo'
+ else 'bar'
+ end.should == 'foo'
+
+ case
+ when false, true; 'foo'
+ else 'bar'
+ end.should == 'foo'
+ end
+
# Homogeneous cases are often optimized to avoid === using a jump table, and should be tested separately.
# See https://github.com/jruby/jruby/issues/6440
it "handles homogeneous cases" do
@@ -442,4 +480,13 @@ describe "The 'case'-construct with no target expression" do
when 2; 'bar'
end.should == 'foo'
end
+
+ it "expands arrays to lists of values" do
+ case
+ when *[false]
+ "foo"
+ when *[true]
+ "bar"
+ end.should == "bar"
+ end
end
diff --git a/spec/ruby/language/class_spec.rb b/spec/ruby/language/class_spec.rb
index 877895bf15..eab3cd0651 100644
--- a/spec/ruby/language/class_spec.rb
+++ b/spec/ruby/language/class_spec.rb
@@ -308,20 +308,10 @@ describe "A class definition extending an object (sclass)" do
-> { class TestClass < BasicObject.new; end }.should raise_error(TypeError, error_msg)
end
- ruby_version_is ""..."3.0" do
- it "allows accessing the block of the original scope" do
- suppress_warning do
- ClassSpecs.sclass_with_block { 123 }.should == 123
- end
- end
- end
-
- ruby_version_is "3.0" do
- it "does not allow accessing the block of the original scope" do
- -> {
- ClassSpecs.sclass_with_block { 123 }
- }.should raise_error(SyntaxError)
- end
+ it "does not allow accessing the block of the original scope" do
+ -> {
+ ClassSpecs.sclass_with_block { 123 }
+ }.should raise_error(SyntaxError)
end
it "can use return to cause the enclosing method to return" do
diff --git a/spec/ruby/language/class_variable_spec.rb b/spec/ruby/language/class_variable_spec.rb
index f98deaa081..a26a3fb8de 100644
--- a/spec/ruby/language/class_variable_spec.rb
+++ b/spec/ruby/language/class_variable_spec.rb
@@ -83,34 +83,32 @@ describe 'A class variable definition' do
end
end
-ruby_version_is "3.0" do
- describe 'Accessing a class variable' do
- it "raises a RuntimeError when accessed from the toplevel scope (not in some module or class)" do
- -> {
- eval "@@cvar_toplevel1"
- }.should raise_error(RuntimeError, 'class variable access from toplevel')
- -> {
- eval "@@cvar_toplevel2 = 2"
- }.should raise_error(RuntimeError, 'class variable access from toplevel')
- end
-
- it "does not raise an error when checking if defined from the toplevel scope" do
- -> {
- eval "defined?(@@cvar_toplevel1)"
- }.should_not raise_error
- end
-
- it "raises a RuntimeError when a class variable is overtaken in an ancestor class" do
- parent = Class.new()
- subclass = Class.new(parent)
- subclass.class_variable_set(:@@cvar_overtaken, :subclass)
- parent.class_variable_set(:@@cvar_overtaken, :parent)
-
- -> {
- subclass.class_variable_get(:@@cvar_overtaken)
- }.should raise_error(RuntimeError, /class variable @@cvar_overtaken of .+ is overtaken by .+/)
-
- parent.class_variable_get(:@@cvar_overtaken).should == :parent
- end
+describe 'Accessing a class variable' do
+ it "raises a RuntimeError when accessed from the toplevel scope (not in some module or class)" do
+ -> {
+ eval "@@cvar_toplevel1"
+ }.should raise_error(RuntimeError, 'class variable access from toplevel')
+ -> {
+ eval "@@cvar_toplevel2 = 2"
+ }.should raise_error(RuntimeError, 'class variable access from toplevel')
+ end
+
+ it "does not raise an error when checking if defined from the toplevel scope" do
+ -> {
+ eval "defined?(@@cvar_toplevel1)"
+ }.should_not raise_error
+ end
+
+ it "raises a RuntimeError when a class variable is overtaken in an ancestor class" do
+ parent = Class.new()
+ subclass = Class.new(parent)
+ subclass.class_variable_set(:@@cvar_overtaken, :subclass)
+ parent.class_variable_set(:@@cvar_overtaken, :parent)
+
+ -> {
+ subclass.class_variable_get(:@@cvar_overtaken)
+ }.should raise_error(RuntimeError, /class variable @@cvar_overtaken of .+ is overtaken by .+/)
+
+ parent.class_variable_get(:@@cvar_overtaken).should == :parent
end
end
diff --git a/spec/ruby/language/constants_spec.rb b/spec/ruby/language/constants_spec.rb
index 8586e46158..08c534487e 100644
--- a/spec/ruby/language/constants_spec.rb
+++ b/spec/ruby/language/constants_spec.rb
@@ -170,34 +170,32 @@ describe "Literal (A::X) constant resolution" do
-> { ConstantSpecs::ParentA::CS_CONSTX }.should raise_error(NameError)
end
- ruby_version_is "3.0" do
- it "uses the module or class #name to craft the error message" do
- mod = Module.new do
- def self.name
- "ModuleName"
- end
-
- def self.inspect
- "<unusable info>"
- end
+ it "uses the module or class #name to craft the error message" do
+ mod = Module.new do
+ def self.name
+ "ModuleName"
end
- -> { mod::DOES_NOT_EXIST }.should raise_error(NameError, /uninitialized constant ModuleName::DOES_NOT_EXIST/)
+ def self.inspect
+ "<unusable info>"
+ end
end
- it "uses the module or class #inspect to craft the error message if they are anonymous" do
- mod = Module.new do
- def self.name
- nil
- end
+ -> { mod::DOES_NOT_EXIST }.should raise_error(NameError, /uninitialized constant ModuleName::DOES_NOT_EXIST/)
+ end
- def self.inspect
- "<unusable info>"
- end
+ it "uses the module or class #inspect to craft the error message if they are anonymous" do
+ mod = Module.new do
+ def self.name
+ nil
end
- -> { mod::DOES_NOT_EXIST }.should raise_error(NameError, /uninitialized constant <unusable info>::DOES_NOT_EXIST/)
+ def self.inspect
+ "<unusable info>"
+ end
end
+
+ -> { mod::DOES_NOT_EXIST }.should raise_error(NameError, /uninitialized constant <unusable info>::DOES_NOT_EXIST/)
end
it "sends #const_missing to the original class or module scope" do
diff --git a/spec/ruby/language/def_spec.rb b/spec/ruby/language/def_spec.rb
index c8531343c0..42e721c68c 100644
--- a/spec/ruby/language/def_spec.rb
+++ b/spec/ruby/language/def_spec.rb
@@ -238,7 +238,7 @@ describe "A singleton method definition" do
end
it "can be declared for a global variable" do
- $__a__ = "hi"
+ $__a__ = +"hi"
def $__a__.foo
7
end
diff --git a/spec/ruby/language/defined_spec.rb b/spec/ruby/language/defined_spec.rb
index ae2bf45bda..34408c0190 100644
--- a/spec/ruby/language/defined_spec.rb
+++ b/spec/ruby/language/defined_spec.rb
@@ -116,6 +116,11 @@ describe "The defined? keyword when called with a method name" do
defined?(obj.a_defined_method).should == "method"
end
+ it "returns 'method' for []=" do
+ a = []
+ defined?(a[0] = 1).should == "method"
+ end
+
it "returns nil if the method is not defined" do
obj = DefinedSpecs::Basic.new
defined?(obj.an_undefined_method).should be_nil
@@ -180,6 +185,32 @@ describe "The defined? keyword when called with a method name" do
ScratchPad.recorded.should == :defined_specs_fixnum_method
end
end
+
+ describe "having a throw in the receiver" do
+ it "escapes defined? and performs the throw semantics as normal" do
+ defined_returned = false
+ catch(:out) {
+ # NOTE: defined? behaves differently if it is called in a void context, see below
+ defined?(throw(:out, 42).foo).should == :unreachable
+ defined_returned = true
+ }.should == 42
+ defined_returned.should == false
+ end
+ end
+
+ describe "in a void context" do
+ it "does not execute the receiver" do
+ ScratchPad.record :not_executed
+ defined?(DefinedSpecs.side_effects / 2)
+ ScratchPad.recorded.should == :not_executed
+ end
+
+ it "warns about the void context when parsing it" do
+ -> {
+ eval "defined?(DefinedSpecs.side_effects / 2); 42"
+ }.should complain(/warning: possibly useless use of defined\? in void context/, verbose: true)
+ end
+ end
end
describe "The defined? keyword for an expression" do
@@ -205,6 +236,14 @@ describe "The defined? keyword for an expression" do
defined?(@@defined_specs_x = 2).should == "assignment"
end
+ it "returns 'assignment' for assigning a constant" do
+ defined?(A = 2).should == "assignment"
+ end
+
+ it "returns 'assignment' for assigning a fully qualified constant" do
+ defined?(Object::A = 2).should == "assignment"
+ end
+
it "returns 'assignment' for assigning multiple variables" do
defined?((a, b = 1, 2)).should == "assignment"
end
@@ -222,7 +261,27 @@ describe "The defined? keyword for an expression" do
end
it "returns 'assignment' for an expression with '+='" do
- defined?(x += 2).should == "assignment"
+ defined?(a += 1).should == "assignment"
+ defined?(@a += 1).should == "assignment"
+ defined?(@@a += 1).should == "assignment"
+ defined?($a += 1).should == "assignment"
+ defined?(A += 1).should == "assignment"
+ # fully qualified constant check is moved out into a separate test case
+ defined?(a.b += 1).should == "assignment"
+ defined?(a[:b] += 1).should == "assignment"
+ end
+
+ # https://bugs.ruby-lang.org/issues/20111
+ ruby_version_is ""..."3.4" do
+ it "returns 'expression' for an assigning a fully qualified constant with '+='" do
+ defined?(Object::A += 1).should == "expression"
+ end
+ end
+
+ ruby_version_is "3.4" do
+ it "returns 'assignment' for an assigning a fully qualified constant with '+='" do
+ defined?(Object::A += 1).should == "assignment"
+ end
end
it "returns 'assignment' for an expression with '*='" do
@@ -253,12 +312,90 @@ describe "The defined? keyword for an expression" do
defined?(x >>= 2).should == "assignment"
end
- it "returns 'assignment' for an expression with '||='" do
- defined?(x ||= 2).should == "assignment"
+ context "||=" do
+ it "returns 'assignment' for assigning a local variable with '||='" do
+ defined?(a ||= true).should == "assignment"
+ end
+
+ it "returns 'assignment' for assigning an instance variable with '||='" do
+ defined?(@a ||= true).should == "assignment"
+ end
+
+ it "returns 'assignment' for assigning a class variable with '||='" do
+ defined?(@@a ||= true).should == "assignment"
+ end
+
+ it "returns 'assignment' for assigning a global variable with '||='" do
+ defined?($a ||= true).should == "assignment"
+ end
+
+ it "returns 'assignment' for assigning a constant with '||='" do
+ defined?(A ||= true).should == "assignment"
+ end
+
+ # https://bugs.ruby-lang.org/issues/20111
+ ruby_version_is ""..."3.4" do
+ it "returns 'expression' for assigning a fully qualified constant with '||='" do
+ defined?(Object::A ||= true).should == "expression"
+ end
+ end
+
+ ruby_version_is "3.4" do
+ it "returns 'assignment' for assigning a fully qualified constant with '||='" do
+ defined?(Object::A ||= true).should == "assignment"
+ end
+ end
+
+ it "returns 'assignment' for assigning an attribute with '||='" do
+ defined?(a.b ||= true).should == "assignment"
+ end
+
+ it "returns 'assignment' for assigning a referenced element with '||='" do
+ defined?(a[:b] ||= true).should == "assignment"
+ end
end
- it "returns 'assignment' for an expression with '&&='" do
- defined?(x &&= 2).should == "assignment"
+ context "&&=" do
+ it "returns 'assignment' for assigning a local variable with '&&='" do
+ defined?(a &&= true).should == "assignment"
+ end
+
+ it "returns 'assignment' for assigning an instance variable with '&&='" do
+ defined?(@a &&= true).should == "assignment"
+ end
+
+ it "returns 'assignment' for assigning a class variable with '&&='" do
+ defined?(@@a &&= true).should == "assignment"
+ end
+
+ it "returns 'assignment' for assigning a global variable with '&&='" do
+ defined?($a &&= true).should == "assignment"
+ end
+
+ it "returns 'assignment' for assigning a constant with '&&='" do
+ defined?(A &&= true).should == "assignment"
+ end
+
+ # https://bugs.ruby-lang.org/issues/20111
+ ruby_version_is ""..."3.4" do
+ it "returns 'expression' for assigning a fully qualified constant with '&&='" do
+ defined?(Object::A &&= true).should == "expression"
+ end
+ end
+
+ ruby_version_is "3.4" do
+ it "returns 'assignment' for assigning a fully qualified constant with '&&='" do
+ defined?(Object::A &&= true).should == "assignment"
+ end
+ end
+
+ it "returns 'assignment' for assigning an attribute with '&&='" do
+ defined?(a.b &&= true).should == "assignment"
+ end
+
+ it "returns 'assignment' for assigning a referenced element with '&&='" do
+ defined?(a[:b] &&= true).should == "assignment"
+ end
end
it "returns 'assignment' for an expression with '**='" do
diff --git a/spec/ruby/language/delegation_spec.rb b/spec/ruby/language/delegation_spec.rb
index 3f24a79d5c..d780506421 100644
--- a/spec/ruby/language/delegation_spec.rb
+++ b/spec/ruby/language/delegation_spec.rb
@@ -31,35 +31,63 @@ describe "delegation with def(...)" do
def delegate(...)
target ...
end
- RUBY
- end
+ RUBY
+ end
- a.new.delegate(1, b: 2).should == Range.new([[], {}], nil, true)
+ a.new.delegate(1, b: 2).should == Range.new([[], {}], nil, true)
end
end
-ruby_version_is "2.7.3" do
- describe "delegation with def(x, ...)" do
- it "delegates rest and kwargs" do
+describe "delegation with def(x, ...)" do
+ it "delegates rest and kwargs" do
+ a = Class.new(DelegationSpecs::Target)
+ a.class_eval(<<-RUBY)
+ def delegate(x, ...)
+ target(...)
+ end
+ RUBY
+
+ a.new.delegate(0, 1, b: 2).should == [[1], {b: 2}]
+ end
+
+ it "delegates block" do
+ a = Class.new(DelegationSpecs::Target)
+ a.class_eval(<<-RUBY)
+ def delegate_block(x, ...)
+ target_block(...)
+ end
+ RUBY
+
+ a.new.delegate_block(0, 1, b: 2) { |x| x }.should == [{b: 2}, [1]]
+ end
+end
+
+ruby_version_is "3.2" do
+ describe "delegation with def(*)" do
+ it "delegates rest" do
a = Class.new(DelegationSpecs::Target)
a.class_eval(<<-RUBY)
- def delegate(x, ...)
- target(...)
- end
- RUBY
+ def delegate(*)
+ target(*)
+ end
+ RUBY
- a.new.delegate(0, 1, b: 2).should == [[1], {b: 2}]
+ a.new.delegate(0, 1).should == [[0, 1], {}]
end
+ end
+end
- it "delegates block" do
+ruby_version_is "3.2" do
+ describe "delegation with def(**)" do
+ it "delegates kwargs" do
a = Class.new(DelegationSpecs::Target)
a.class_eval(<<-RUBY)
- def delegate_block(x, ...)
- target_block(...)
- end
- RUBY
+ def delegate(**)
+ target(**)
+ end
+ RUBY
- a.new.delegate_block(0, 1, b: 2) { |x| x }.should == [{b: 2}, [1]]
+ a.new.delegate(a: 1) { |x| x }.should == [[], {a: 1}]
end
end
end
diff --git a/spec/ruby/language/encoding_spec.rb b/spec/ruby/language/encoding_spec.rb
index 5430c9cb98..e761a53cb6 100644
--- a/spec/ruby/language/encoding_spec.rb
+++ b/spec/ruby/language/encoding_spec.rb
@@ -13,15 +13,15 @@ describe "The __ENCODING__ pseudo-variable" do
end
it "is the evaluated strings's one inside an eval" do
- eval("__ENCODING__".force_encoding("US-ASCII")).should == Encoding::US_ASCII
- eval("__ENCODING__".force_encoding("BINARY")).should == Encoding::BINARY
+ eval("__ENCODING__".dup.force_encoding("US-ASCII")).should == Encoding::US_ASCII
+ eval("__ENCODING__".dup.force_encoding("BINARY")).should == Encoding::BINARY
end
it "is the encoding specified by a magic comment inside an eval" do
- code = "# encoding: BINARY\n__ENCODING__".force_encoding("US-ASCII")
+ code = "# encoding: BINARY\n__ENCODING__".dup.force_encoding("US-ASCII")
eval(code).should == Encoding::BINARY
- code = "# encoding: us-ascii\n__ENCODING__".force_encoding("BINARY")
+ code = "# encoding: us-ascii\n__ENCODING__".dup.force_encoding("BINARY")
eval(code).should == Encoding::US_ASCII
end
diff --git a/spec/ruby/language/ensure_spec.rb b/spec/ruby/language/ensure_spec.rb
index e893904bcb..16e626b4d0 100644
--- a/spec/ruby/language/ensure_spec.rb
+++ b/spec/ruby/language/ensure_spec.rb
@@ -328,4 +328,21 @@ describe "An ensure block inside 'do end' block" do
result.should == :begin
end
+
+ ruby_version_is "3.4" do
+ it "does not introduce extra backtrace entries" do
+ def foo
+ begin
+ raise "oops"
+ ensure
+ return caller(0, 2) # rubocop:disable Lint/EnsureReturn
+ end
+ end
+ line = __LINE__
+ foo.should == [
+ "#{__FILE__}:#{line-3}:in 'foo'",
+ "#{__FILE__}:#{line+1}:in 'block (3 levels) in <top (required)>'"
+ ]
+ end
+ end
end
diff --git a/spec/ruby/language/execution_spec.rb b/spec/ruby/language/execution_spec.rb
index 4e0310946d..ef1de38899 100644
--- a/spec/ruby/language/execution_spec.rb
+++ b/spec/ruby/language/execution_spec.rb
@@ -5,6 +5,45 @@ describe "``" do
ip = 'world'
`echo disc #{ip}`.should == "disc world\n"
end
+
+ it "can be redefined and receive a frozen string as argument" do
+ called = false
+ runner = Object.new
+
+ runner.singleton_class.define_method(:`) do |str|
+ called = true
+
+ str.should == "test command"
+ str.frozen?.should == true
+ end
+
+ runner.instance_exec do
+ `test command`
+ end
+
+ called.should == true
+ end
+
+ it "the argument isn't frozen if it contains interpolation" do
+ called = false
+ runner = Object.new
+
+ runner.singleton_class.define_method(:`) do |str|
+ called = true
+
+ str.should == "test command"
+ str.frozen?.should == false
+ str << "mutated"
+ end
+
+ 2.times do
+ runner.instance_exec do
+ `test #{:command}`
+ end
+ end
+
+ called.should == true
+ end
end
describe "%x" do
@@ -12,4 +51,43 @@ describe "%x" do
ip = 'world'
%x(echo disc #{ip}).should == "disc world\n"
end
+
+ it "can be redefined and receive a frozen string as argument" do
+ called = false
+ runner = Object.new
+
+ runner.singleton_class.define_method(:`) do |str|
+ called = true
+
+ str.should == "test command"
+ str.frozen?.should == true
+ end
+
+ runner.instance_exec do
+ %x{test command}
+ end
+
+ called.should == true
+ end
+
+ it "the argument isn't frozen if it contains interpolation" do
+ called = false
+ runner = Object.new
+
+ runner.singleton_class.define_method(:`) do |str|
+ called = true
+
+ str.should == "test command"
+ str.frozen?.should == false
+ str << "mutated"
+ end
+
+ 2.times do
+ runner.instance_exec do
+ %x{test #{:command}}
+ end
+ end
+
+ called.should == true
+ end
end
diff --git a/spec/ruby/language/file_spec.rb b/spec/ruby/language/file_spec.rb
index 136b262d07..59563d9642 100644
--- a/spec/ruby/language/file_spec.rb
+++ b/spec/ruby/language/file_spec.rb
@@ -7,8 +7,16 @@ describe "The __FILE__ pseudo-variable" do
-> { eval("__FILE__ = 1") }.should raise_error(SyntaxError)
end
- it "equals (eval) inside an eval" do
- eval("__FILE__").should == "(eval)"
+ ruby_version_is ""..."3.3" do
+ it "equals (eval) inside an eval" do
+ eval("__FILE__").should == "(eval)"
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "equals (eval at __FILE__:__LINE__) inside an eval" do
+ eval("__FILE__").should == "(eval at #{__FILE__}:#{__LINE__})"
+ end
end
end
diff --git a/spec/ruby/language/fixtures/rescue/top_level.rb b/spec/ruby/language/fixtures/rescue/top_level.rb
new file mode 100644
index 0000000000..59e78ef1d6
--- /dev/null
+++ b/spec/ruby/language/fixtures/rescue/top_level.rb
@@ -0,0 +1,7 @@
+# capturing in local variable at top-level
+
+begin
+ raise "message"
+rescue => e
+ ScratchPad << e.message
+end
diff --git a/spec/ruby/language/fixtures/super.rb b/spec/ruby/language/fixtures/super.rb
index 94a2a91be0..c5bdcf0e40 100644
--- a/spec/ruby/language/fixtures/super.rb
+++ b/spec/ruby/language/fixtures/super.rb
@@ -539,6 +539,30 @@ module SuperSpecs
args
end
+ def m3(*args)
+ args
+ end
+
+ def m4(*args)
+ args
+ end
+
+ def m_default(*args)
+ args
+ end
+
+ def m_rest(*args)
+ args
+ end
+
+ def m_pre_default_rest_post(*args)
+ args
+ end
+
+ def m_kwrest(**kw)
+ kw
+ end
+
def m_modified(*args)
args
end
@@ -549,6 +573,30 @@ module SuperSpecs
super
end
+ def m3(_, _, _)
+ super
+ end
+
+ def m4(_, _, _, _)
+ super
+ end
+
+ def m_default(_ = 0)
+ super
+ end
+
+ def m_rest(*_)
+ super
+ end
+
+ def m_pre_default_rest_post(_, _, _=:a, _=:b, *_, _, _)
+ super
+ end
+
+ def m_kwrest(**_)
+ super
+ end
+
def m_modified(_, _)
_ = 14
super
diff --git a/spec/ruby/language/hash_spec.rb b/spec/ruby/language/hash_spec.rb
index fa5c8723e9..a7631fb0d6 100644
--- a/spec/ruby/language/hash_spec.rb
+++ b/spec/ruby/language/hash_spec.rb
@@ -33,7 +33,7 @@ describe "Hash literal" do
end
it "freezes string keys on initialization" do
- key = "foo"
+ key = +"foo"
h = {key => "bar"}
key.reverse!
h["foo"].should == "bar"
@@ -191,20 +191,22 @@ describe "Hash literal" do
usascii_hash.keys.first.encoding.should == Encoding::US_ASCII
end
- it "raises an EncodingError at parse time when Symbol key with invalid bytes" do
- ScratchPad.record []
- -> {
- eval 'ScratchPad << 1; {:"\xC3" => 1}'
- }.should raise_error(EncodingError, 'invalid symbol in encoding UTF-8 :"\xC3"')
- ScratchPad.recorded.should == []
- end
+ ruby_bug "#20280", ""..."3.4" do
+ it "raises a SyntaxError at parse time when Symbol key with invalid bytes" do
+ ScratchPad.record []
+ -> {
+ eval 'ScratchPad << 1; {:"\xC3" => 1}'
+ }.should raise_error(SyntaxError, /invalid symbol/)
+ ScratchPad.recorded.should == []
+ end
- it "raises an EncodingError at parse time when Symbol key with invalid bytes and 'key: value' syntax used" do
- ScratchPad.record []
- -> {
- eval 'ScratchPad << 1; {"\xC3": 1}'
- }.should raise_error(EncodingError, 'invalid symbol in encoding UTF-8 :"\xC3"')
- ScratchPad.recorded.should == []
+ it "raises a SyntaxError at parse time when Symbol key with invalid bytes and 'key: value' syntax used" do
+ ScratchPad.record []
+ -> {
+ eval 'ScratchPad << 1; {"\xC3": 1}'
+ }.should raise_error(SyntaxError, /invalid symbol/)
+ ScratchPad.recorded.should == []
+ end
end
end
@@ -220,8 +222,8 @@ describe "The ** operator" do
h.should == { one: 1, two: 2 }
end
- ruby_version_is ""..."3.0" do
- it "makes a caller-side copy when calling a method taking a positional Hash" do
+ ruby_bug "#20012", ""..."3.3" do
+ it "makes a copy when calling a method taking a positional Hash" do
def m(h)
h.delete(:one); h
end
@@ -233,19 +235,6 @@ describe "The ** operator" do
end
end
- ruby_version_is "3.0" do
- it "does not copy when calling a method taking a positional Hash" do
- def m(h)
- h.delete(:one); h
- end
-
- h = { one: 1, two: 2 }
- m(**h).should == { two: 2 }
- m(**h).should.equal?(h)
- h.should == { two: 2 }
- end
- end
-
ruby_version_is "3.1" do
describe "hash with omitted value" do
it "accepts short notation 'key' for 'key: value' syntax" do
diff --git a/spec/ruby/language/if_spec.rb b/spec/ruby/language/if_spec.rb
index a5da696000..2d1a89f081 100644
--- a/spec/ruby/language/if_spec.rb
+++ b/spec/ruby/language/if_spec.rb
@@ -305,6 +305,16 @@ describe "The if expression" do
6.times(&b)
ScratchPad.recorded.should == [4, 5, 4, 5]
end
+
+ it "warns when Integer literals are used instead of predicates" do
+ -> {
+ eval <<~RUBY
+ $. = 0
+ 10.times { |i| ScratchPad << i if 4..5 }
+ RUBY
+ }.should complain(/warning: integer literal in flip-flop/, verbose: true)
+ ScratchPad.recorded.should == []
+ end
end
describe "when a branch syntactically does not return a value" do
diff --git a/spec/ruby/language/keyword_arguments_spec.rb b/spec/ruby/language/keyword_arguments_spec.rb
index c47b7b0ae9..ffb5b1fab0 100644
--- a/spec/ruby/language/keyword_arguments_spec.rb
+++ b/spec/ruby/language/keyword_arguments_spec.rb
@@ -1,316 +1,353 @@
require_relative '../spec_helper'
-ruby_version_is "3.0" do
- describe "Keyword arguments" do
- def target(*args, **kwargs)
+describe "Keyword arguments" do
+ def target(*args, **kwargs)
+ [args, kwargs]
+ end
+
+ it "are separated from positional arguments" do
+ def m(*args, **kwargs)
[args, kwargs]
end
- it "are separated from positional arguments" do
- def m(*args, **kwargs)
- [args, kwargs]
- end
+ empty = {}
+ m(**empty).should == [[], {}]
+ m(empty).should == [[{}], {}]
- empty = {}
- m(**empty).should == [[], {}]
- m(empty).should == [[{}], {}]
+ m(a: 1).should == [[], {a: 1}]
+ m({a: 1}).should == [[{a: 1}], {}]
+ end
- m(a: 1).should == [[], {a: 1}]
- m({a: 1}).should == [[{a: 1}], {}]
+ it "when the receiving method has not keyword parameters it treats kwargs as positional" do
+ def m(*a)
+ a
end
- it "when the receiving method has not keyword parameters it treats kwargs as positional" do
+ m(a: 1).should == [{a: 1}]
+ m({a: 1}).should == [{a: 1}]
+ end
+
+ context "empty kwargs are treated as if they were not passed" do
+ it "when calling a method" do
def m(*a)
a
end
- m(a: 1).should == [{a: 1}]
- m({a: 1}).should == [{a: 1}]
+ empty = {}
+ m(**empty).should == []
+ m(empty).should == [{}]
end
- context "empty kwargs are treated as if they were not passed" do
- it "when calling a method" do
- def m(*a)
- a
- end
-
- empty = {}
- m(**empty).should == []
- m(empty).should == [{}]
+ it "when yielding to a block" do
+ def y(*args, **kwargs)
+ yield(*args, **kwargs)
end
- it "when yielding to a block" do
- def y(*args, **kwargs)
- yield(*args, **kwargs)
- end
-
- empty = {}
- y(**empty) { |*a| a }.should == []
- y(empty) { |*a| a }.should == [{}]
- end
+ empty = {}
+ y(**empty) { |*a| a }.should == []
+ y(empty) { |*a| a }.should == [{}]
end
+ end
- it "extra keywords are not allowed without **kwrest" do
- def m(*a, kw:)
- a
- end
-
- m(kw: 1).should == []
- -> { m(kw: 1, kw2: 2) }.should raise_error(ArgumentError, 'unknown keyword: :kw2')
- -> { m(kw: 1, true => false) }.should raise_error(ArgumentError, 'unknown keyword: true')
- -> { m(kw: 1, a: 1, b: 2, c: 3) }.should raise_error(ArgumentError, 'unknown keywords: :a, :b, :c')
+ it "extra keywords are not allowed without **kwrest" do
+ def m(*a, kw:)
+ a
end
- it "raises ArgumentError exception when required keyword argument is not passed" do
- def m(a:, b:, c:)
- [a, b, c]
- end
+ m(kw: 1).should == []
+ -> { m(kw: 1, kw2: 2) }.should raise_error(ArgumentError, 'unknown keyword: :kw2')
+ -> { m(kw: 1, true => false) }.should raise_error(ArgumentError, 'unknown keyword: true')
+ -> { m(kw: 1, a: 1, b: 2, c: 3) }.should raise_error(ArgumentError, 'unknown keywords: :a, :b, :c')
+ end
- -> { m(a: 1, b: 2) }.should raise_error(ArgumentError, /missing keyword: :c/)
- -> { m() }.should raise_error(ArgumentError, /missing keywords: :a, :b, :c/)
+ it "raises ArgumentError exception when required keyword argument is not passed" do
+ def m(a:, b:, c:)
+ [a, b, c]
end
- it "raises ArgumentError for missing keyword arguments even if there are extra ones" do
- def m(a:)
- a
- end
+ -> { m(a: 1, b: 2) }.should raise_error(ArgumentError, /missing keyword: :c/)
+ -> { m() }.should raise_error(ArgumentError, /missing keywords: :a, :b, :c/)
+ end
- -> { m(b: 1) }.should raise_error(ArgumentError, /missing keyword: :a/)
+ it "raises ArgumentError for missing keyword arguments even if there are extra ones" do
+ def m(a:)
+ a
end
- it "handle * and ** at the same call site" do
- def m(*a)
- a
- end
+ -> { m(b: 1) }.should raise_error(ArgumentError, /missing keyword: :a/)
+ end
- m(*[], **{}).should == []
- m(*[], 42, **{}).should == [42]
+ it "handle * and ** at the same call site" do
+ def m(*a)
+ a
end
- context "**" do
- it "does not copy a non-empty Hash for a method taking (*args)" do
+ m(*[], **{}).should == []
+ m(*[], 42, **{}).should == [42]
+ end
+
+ context "**" do
+ ruby_version_is "3.3" do
+ it "copies a non-empty Hash for a method taking (*args)" do
def m(*args)
args[0]
end
h = {a: 1}
- m(**h).should.equal?(h)
+ m(**h).should_not.equal?(h)
+ h.should == {a: 1}
end
+ end
- it "copies the given Hash for a method taking (**kwargs)" do
- def m(**kw)
- kw
- end
+ it "copies the given Hash for a method taking (**kwargs)" do
+ def m(**kw)
+ kw
+ end
- empty = {}
- m(**empty).should == empty
- m(**empty).should_not.equal?(empty)
+ empty = {}
+ m(**empty).should == empty
+ m(**empty).should_not.equal?(empty)
- h = {a: 1}
- m(**h).should == h
- m(**h).should_not.equal?(h)
- end
+ h = {a: 1}
+ m(**h).should == h
+ m(**h).should_not.equal?(h)
end
+ end
- context "delegation" do
- it "works with (*args, **kwargs)" do
- def m(*args, **kwargs)
- target(*args, **kwargs)
- end
+ context "delegation" do
+ it "works with (*args, **kwargs)" do
+ def m(*args, **kwargs)
+ target(*args, **kwargs)
+ end
- empty = {}
- m(**empty).should == [[], {}]
- m(empty).should == [[{}], {}]
+ empty = {}
+ m(**empty).should == [[], {}]
+ m(empty).should == [[{}], {}]
- m(a: 1).should == [[], {a: 1}]
- m({a: 1}).should == [[{a: 1}], {}]
+ m(a: 1).should == [[], {a: 1}]
+ m({a: 1}).should == [[{a: 1}], {}]
+ end
+
+ it "works with proc { |*args, **kwargs| }" do
+ m = proc do |*args, **kwargs|
+ target(*args, **kwargs)
end
- it "works with proc { |*args, **kwargs| }" do
- m = proc do |*args, **kwargs|
- target(*args, **kwargs)
- end
+ empty = {}
+ m.(**empty).should == [[], {}]
+ m.(empty).should == [[{}], {}]
- empty = {}
- m.(**empty).should == [[], {}]
- m.(empty).should == [[{}], {}]
+ m.(a: 1).should == [[], {a: 1}]
+ m.({a: 1}).should == [[{a: 1}], {}]
- m.(a: 1).should == [[], {a: 1}]
- m.({a: 1}).should == [[{a: 1}], {}]
+ # no autosplatting for |*args, **kwargs|
+ m.([1, 2]).should == [[[1, 2]], {}]
+ end
- # no autosplatting for |*args, **kwargs|
- m.([1, 2]).should == [[[1, 2]], {}]
+ it "works with -> (*args, **kwargs) {}" do
+ m = -> *args, **kwargs do
+ target(*args, **kwargs)
end
- it "works with -> (*args, **kwargs) {}" do
- m = -> *args, **kwargs do
- target(*args, **kwargs)
- end
+ empty = {}
+ m.(**empty).should == [[], {}]
+ m.(empty).should == [[{}], {}]
- empty = {}
- m.(**empty).should == [[], {}]
- m.(empty).should == [[{}], {}]
+ m.(a: 1).should == [[], {a: 1}]
+ m.({a: 1}).should == [[{a: 1}], {}]
+ end
- m.(a: 1).should == [[], {a: 1}]
- m.({a: 1}).should == [[{a: 1}], {}]
+ it "works with (...)" do
+ instance_eval <<~DEF
+ def m(...)
+ target(...)
end
+ DEF
- it "works with (...)" do
- instance_eval <<~DEF
- def m(...)
- target(...)
- end
- DEF
+ empty = {}
+ m(**empty).should == [[], {}]
+ m(empty).should == [[{}], {}]
- empty = {}
- m(**empty).should == [[], {}]
- m(empty).should == [[{}], {}]
+ m(a: 1).should == [[], {a: 1}]
+ m({a: 1}).should == [[{a: 1}], {}]
+ end
- m(a: 1).should == [[], {a: 1}]
- m({a: 1}).should == [[{a: 1}], {}]
+ it "works with call(*ruby2_keyword_args)" do
+ class << self
+ ruby2_keywords def m(*args)
+ target(*args)
+ end
end
- it "works with call(*ruby2_keyword_args)" do
- class << self
- ruby2_keywords def m(*args)
- target(*args)
- end
- end
+ empty = {}
+ m(**empty).should == [[], {}]
+ Hash.ruby2_keywords_hash?(empty).should == false
+ m(empty).should == [[{}], {}]
+ Hash.ruby2_keywords_hash?(empty).should == false
- empty = {}
- m(**empty).should == [[], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
- m(empty).should == [[{}], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
+ m(a: 1).should == [[], {a: 1}]
+ m({a: 1}).should == [[{a: 1}], {}]
- m(a: 1).should == [[], {a: 1}]
- m({a: 1}).should == [[{a: 1}], {}]
+ kw = {a: 1}
- kw = {a: 1}
+ m(**kw).should == [[], {a: 1}]
+ m(**kw)[1].should == kw
+ m(**kw)[1].should_not.equal?(kw)
+ Hash.ruby2_keywords_hash?(kw).should == false
+ Hash.ruby2_keywords_hash?(m(**kw)[1]).should == false
- m(**kw).should == [[], {a: 1}]
- m(**kw)[1].should == kw
- m(**kw)[1].should_not.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
- Hash.ruby2_keywords_hash?(m(**kw)[1]).should == false
+ m(kw).should == [[{a: 1}], {}]
+ m(kw)[0][0].should.equal?(kw)
+ Hash.ruby2_keywords_hash?(kw).should == false
+ end
- m(kw).should == [[{a: 1}], {}]
- m(kw)[0][0].should.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
+ it "works with super(*ruby2_keyword_args)" do
+ parent = Class.new do
+ def m(*args, **kwargs)
+ [args, kwargs]
+ end
end
- it "works with super(*ruby2_keyword_args)" do
- parent = Class.new do
- def m(*args, **kwargs)
- [args, kwargs]
- end
+ child = Class.new(parent) do
+ ruby2_keywords def m(*args)
+ super(*args)
end
+ end
- child = Class.new(parent) do
- ruby2_keywords def m(*args)
- super(*args)
- end
- end
+ obj = child.new
- obj = child.new
+ empty = {}
+ obj.m(**empty).should == [[], {}]
+ Hash.ruby2_keywords_hash?(empty).should == false
+ obj.m(empty).should == [[{}], {}]
+ Hash.ruby2_keywords_hash?(empty).should == false
- empty = {}
- obj.m(**empty).should == [[], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
- obj.m(empty).should == [[{}], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
+ obj.m(a: 1).should == [[], {a: 1}]
+ obj.m({a: 1}).should == [[{a: 1}], {}]
- obj.m(a: 1).should == [[], {a: 1}]
- obj.m({a: 1}).should == [[{a: 1}], {}]
+ kw = {a: 1}
- kw = {a: 1}
+ obj.m(**kw).should == [[], {a: 1}]
+ obj.m(**kw)[1].should == kw
+ obj.m(**kw)[1].should_not.equal?(kw)
+ Hash.ruby2_keywords_hash?(kw).should == false
+ Hash.ruby2_keywords_hash?(obj.m(**kw)[1]).should == false
- obj.m(**kw).should == [[], {a: 1}]
- obj.m(**kw)[1].should == kw
- obj.m(**kw)[1].should_not.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
- Hash.ruby2_keywords_hash?(obj.m(**kw)[1]).should == false
+ obj.m(kw).should == [[{a: 1}], {}]
+ obj.m(kw)[0][0].should.equal?(kw)
+ Hash.ruby2_keywords_hash?(kw).should == false
+ end
- obj.m(kw).should == [[{a: 1}], {}]
- obj.m(kw)[0][0].should.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
+ it "works with zsuper" do
+ parent = Class.new do
+ def m(*args, **kwargs)
+ [args, kwargs]
+ end
end
- it "works with zsuper" do
- parent = Class.new do
- def m(*args, **kwargs)
- [args, kwargs]
- end
+ child = Class.new(parent) do
+ ruby2_keywords def m(*args)
+ super
end
+ end
- child = Class.new(parent) do
- ruby2_keywords def m(*args)
- super
- end
- end
+ obj = child.new
- obj = child.new
+ empty = {}
+ obj.m(**empty).should == [[], {}]
+ Hash.ruby2_keywords_hash?(empty).should == false
+ obj.m(empty).should == [[{}], {}]
+ Hash.ruby2_keywords_hash?(empty).should == false
- empty = {}
- obj.m(**empty).should == [[], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
- obj.m(empty).should == [[{}], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
+ obj.m(a: 1).should == [[], {a: 1}]
+ obj.m({a: 1}).should == [[{a: 1}], {}]
- obj.m(a: 1).should == [[], {a: 1}]
- obj.m({a: 1}).should == [[{a: 1}], {}]
+ kw = {a: 1}
- kw = {a: 1}
+ obj.m(**kw).should == [[], {a: 1}]
+ obj.m(**kw)[1].should == kw
+ obj.m(**kw)[1].should_not.equal?(kw)
+ Hash.ruby2_keywords_hash?(kw).should == false
+ Hash.ruby2_keywords_hash?(obj.m(**kw)[1]).should == false
- obj.m(**kw).should == [[], {a: 1}]
- obj.m(**kw)[1].should == kw
- obj.m(**kw)[1].should_not.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
- Hash.ruby2_keywords_hash?(obj.m(**kw)[1]).should == false
+ obj.m(kw).should == [[{a: 1}], {}]
+ obj.m(kw)[0][0].should.equal?(kw)
+ Hash.ruby2_keywords_hash?(kw).should == false
+ end
- obj.m(kw).should == [[{a: 1}], {}]
- obj.m(kw)[0][0].should.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
+ it "works with yield(*ruby2_keyword_args)" do
+ class << self
+ def y(args)
+ yield(*args)
+ end
+
+ ruby2_keywords def m(*outer_args)
+ y(outer_args, &-> *args, **kwargs { target(*args, **kwargs) })
+ end
end
- it "works with yield(*ruby2_keyword_args)" do
- class << self
- def y(args)
- yield(*args)
- end
+ empty = {}
+ m(**empty).should == [[], {}]
+ Hash.ruby2_keywords_hash?(empty).should == false
+ m(empty).should == [[{}], {}]
+ Hash.ruby2_keywords_hash?(empty).should == false
- ruby2_keywords def m(*outer_args)
- y(outer_args, &-> *args, **kwargs { target(*args, **kwargs) })
- end
+ m(a: 1).should == [[], {a: 1}]
+ m({a: 1}).should == [[{a: 1}], {}]
+
+ kw = {a: 1}
+
+ m(**kw).should == [[], {a: 1}]
+ m(**kw)[1].should == kw
+ m(**kw)[1].should_not.equal?(kw)
+ Hash.ruby2_keywords_hash?(kw).should == false
+ Hash.ruby2_keywords_hash?(m(**kw)[1]).should == false
+
+ m(kw).should == [[{a: 1}], {}]
+ m(kw)[0][0].should.equal?(kw)
+ Hash.ruby2_keywords_hash?(kw).should == false
+ end
+
+ it "does not work with (*args)" do
+ class << self
+ def m(*args)
+ target(*args)
end
+ end
- empty = {}
- m(**empty).should == [[], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
- m(empty).should == [[{}], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
+ empty = {}
+ m(**empty).should == [[], {}]
+ m(empty).should == [[{}], {}]
- m(a: 1).should == [[], {a: 1}]
- m({a: 1}).should == [[{a: 1}], {}]
+ m(a: 1).should == [[{a: 1}], {}]
+ m({a: 1}).should == [[{a: 1}], {}]
+ end
- kw = {a: 1}
+ ruby_version_is "3.1" do
+ describe "omitted values" do
+ it "accepts short notation 'key' for 'key: value' syntax" do
+ def m(a:, b:)
+ [a, b]
+ end
- m(**kw).should == [[], {a: 1}]
- m(**kw)[1].should == kw
- m(**kw)[1].should_not.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
- Hash.ruby2_keywords_hash?(m(**kw)[1]).should == false
+ a = 1
+ b = 2
- m(kw).should == [[{a: 1}], {}]
- m(kw)[0][0].should.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
+ eval('m(a:, b:).should == [1, 2]')
+ end
end
+ end
- it "does not work with (*args)" do
+ ruby_version_is "3.2" do
+ it "does not work with call(*ruby2_keyword_args) with missing ruby2_keywords in between" do
class << self
- def m(*args)
+ def n(*args) # Note the missing ruby2_keywords here
target(*args)
end
+
+ ruby2_keywords def m(*args)
+ n(*args)
+ end
end
empty = {}
@@ -320,77 +357,41 @@ ruby_version_is "3.0" do
m(a: 1).should == [[{a: 1}], {}]
m({a: 1}).should == [[{a: 1}], {}]
end
+ end
- ruby_version_is "3.1" do
- describe "omitted values" do
- it "accepts short notation 'key' for 'key: value' syntax" do
- def m(a:, b:)
- [a, b]
- end
-
- a = 1
- b = 2
-
- eval('m(a:, b:).should == [1, 2]')
+ ruby_version_is ""..."3.2" do
+ # https://bugs.ruby-lang.org/issues/18625
+ it "works with call(*ruby2_keyword_args) with missing ruby2_keywords in between due to CRuby bug #18625" do
+ class << self
+ def n(*args) # Note the missing ruby2_keywords here
+ target(*args)
end
- end
- end
-
- ruby_version_is "3.2" do
- it "does not work with call(*ruby2_keyword_args) with missing ruby2_keywords in between" do
- class << self
- def n(*args) # Note the missing ruby2_keywords here
- target(*args)
- end
- ruby2_keywords def m(*args)
- n(*args)
- end
+ ruby2_keywords def m(*args)
+ n(*args)
end
-
- empty = {}
- m(**empty).should == [[], {}]
- m(empty).should == [[{}], {}]
-
- m(a: 1).should == [[{a: 1}], {}]
- m({a: 1}).should == [[{a: 1}], {}]
end
- end
-
- ruby_version_is ""..."3.2" do
- # https://bugs.ruby-lang.org/issues/18625
- it "works with call(*ruby2_keyword_args) with missing ruby2_keywords in between due to CRuby bug #18625" do
- class << self
- def n(*args) # Note the missing ruby2_keywords here
- target(*args)
- end
-
- ruby2_keywords def m(*args)
- n(*args)
- end
- end
- empty = {}
- m(**empty).should == [[], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
- m(empty).should == [[{}], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
+ empty = {}
+ m(**empty).should == [[], {}]
+ Hash.ruby2_keywords_hash?(empty).should == false
+ m(empty).should == [[{}], {}]
+ Hash.ruby2_keywords_hash?(empty).should == false
- m(a: 1).should == [[], {a: 1}]
- m({a: 1}).should == [[{a: 1}], {}]
+ m(a: 1).should == [[], {a: 1}]
+ m({a: 1}).should == [[{a: 1}], {}]
- kw = {a: 1}
+ kw = {a: 1}
- m(**kw).should == [[], {a: 1}]
- m(**kw)[1].should == kw
- m(**kw)[1].should_not.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
- Hash.ruby2_keywords_hash?(m(**kw)[1]).should == false
+ m(**kw).should == [[], {a: 1}]
+ m(**kw)[1].should == kw
+ m(**kw)[1].should_not.equal?(kw)
+ Hash.ruby2_keywords_hash?(kw).should == false
+ Hash.ruby2_keywords_hash?(m(**kw)[1]).should == false
- m(kw).should == [[{a: 1}], {}]
- m(kw)[0][0].should.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
- end
+ m(kw).should == [[{a: 1}], {}]
+ m(kw)[0][0].should.equal?(kw)
+ Hash.ruby2_keywords_hash?(kw).should == false
end
end
end
diff --git a/spec/ruby/language/lambda_spec.rb b/spec/ruby/language/lambda_spec.rb
index a3f01ec04b..3ab3569ebe 100644
--- a/spec/ruby/language/lambda_spec.rb
+++ b/spec/ruby/language/lambda_spec.rb
@@ -177,34 +177,16 @@ describe "A lambda literal -> () { }" do
result.should == [1, 2, 3, [4, 5], 6, [7, 8], 9, 10, 11, 12]
end
- ruby_version_is ''...'3.0' do
- evaluate <<-ruby do
- @a = -> (*, **k) { k }
- ruby
-
- @a.().should == {}
- @a.(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
-
- suppress_keyword_warning do
- h = mock("keyword splat")
- h.should_receive(:to_hash).and_return({a: 1})
- @a.(h).should == {a: 1}
- end
- end
- end
-
- ruby_version_is '3.0' do
- evaluate <<-ruby do
- @a = -> (*, **k) { k }
- ruby
+ evaluate <<-ruby do
+ @a = -> (*, **k) { k }
+ ruby
- @a.().should == {}
- @a.(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
+ @a.().should == {}
+ @a.(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
- h = mock("keyword splat")
- h.should_not_receive(:to_hash)
- @a.(h).should == {}
- end
+ h = mock("keyword splat")
+ h.should_not_receive(:to_hash)
+ @a.(h).should == {}
end
evaluate <<-ruby do
@@ -514,34 +496,16 @@ describe "A lambda expression 'lambda { ... }'" do
result.should == [1, 2, 3, [4, 5], 6, [7, 8], 9, 10, 11, 12]
end
- ruby_version_is ''...'3.0' do
- evaluate <<-ruby do
- @a = lambda { |*, **k| k }
- ruby
-
- @a.().should == {}
- @a.(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
-
- suppress_keyword_warning do
- h = mock("keyword splat")
- h.should_receive(:to_hash).and_return({a: 1})
- @a.(h).should == {a: 1}
- end
- end
- end
-
- ruby_version_is '3.0' do
- evaluate <<-ruby do
- @a = lambda { |*, **k| k }
- ruby
+ evaluate <<-ruby do
+ @a = lambda { |*, **k| k }
+ ruby
- @a.().should == {}
- @a.(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
+ @a.().should == {}
+ @a.(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
- h = mock("keyword splat")
- h.should_not_receive(:to_hash)
- @a.(h).should == {}
- end
+ h = mock("keyword splat")
+ h.should_not_receive(:to_hash)
+ @a.(h).should == {}
end
evaluate <<-ruby do
diff --git a/spec/ruby/language/method_spec.rb b/spec/ruby/language/method_spec.rb
index b80b314f6f..9abe4cde20 100644
--- a/spec/ruby/language/method_spec.rb
+++ b/spec/ruby/language/method_spec.rb
@@ -749,68 +749,31 @@ describe "A method" do
end
end
- ruby_version_is ""..."3.0" do
- evaluate <<-ruby do
- def m(a, b: 1) [a, b] end
- ruby
-
- m(2).should == [2, 1]
- m(1, b: 2).should == [1, 2]
- suppress_keyword_warning do
- m("a" => 1, b: 2).should == [{"a" => 1, b: 2}, 1]
- end
- end
-
- evaluate <<-ruby do
- def m(a, **) a end
- ruby
-
- m(1).should == 1
- m(1, a: 2, b: 3).should == 1
- suppress_keyword_warning do
- m("a" => 1, b: 2).should == {"a" => 1, b: 2}
- end
- end
-
- evaluate <<-ruby do
- def m(a, **k) [a, k] end
- ruby
+ evaluate <<-ruby do
+ def m(a, b: 1) [a, b] end
+ ruby
- m(1).should == [1, {}]
- m(1, a: 2, b: 3).should == [1, {a: 2, b: 3}]
- suppress_keyword_warning do
- m("a" => 1, b: 2).should == [{"a" => 1, b: 2}, {}]
- end
- end
+ m(2).should == [2, 1]
+ m(1, b: 2).should == [1, 2]
+ -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
end
- ruby_version_is "3.0" do
- evaluate <<-ruby do
- def m(a, b: 1) [a, b] end
- ruby
-
- m(2).should == [2, 1]
- m(1, b: 2).should == [1, 2]
- -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
- end
-
- evaluate <<-ruby do
- def m(a, **) a end
- ruby
+ evaluate <<-ruby do
+ def m(a, **) a end
+ ruby
- m(1).should == 1
- m(1, a: 2, b: 3).should == 1
- -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
- end
+ m(1).should == 1
+ m(1, a: 2, b: 3).should == 1
+ -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
+ end
- evaluate <<-ruby do
- def m(a, **k) [a, k] end
- ruby
+ evaluate <<-ruby do
+ def m(a, **k) [a, k] end
+ ruby
- m(1).should == [1, {}]
- m(1, a: 2, b: 3).should == [1, {a: 2, b: 3}]
- -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
- end
+ m(1).should == [1, {}]
+ m(1, a: 2, b: 3).should == [1, {a: 2, b: 3}]
+ -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
end
evaluate <<-ruby do
@@ -921,50 +884,23 @@ describe "A method" do
result.should == [[1, 2, 3], 4, [5, 6], 7, [], 8]
end
- ruby_version_is ""..."3.0" do
- evaluate <<-ruby do
- def m(a=1, b:) [a, b] end
- ruby
-
- m(b: 2).should == [1, 2]
- m(2, b: 1).should == [2, 1]
- suppress_keyword_warning do
- m("a" => 1, b: 2).should == [{"a" => 1}, 2]
- end
- end
-
- evaluate <<-ruby do
- def m(a=1, b: 2) [a, b] end
- ruby
+ evaluate <<-ruby do
+ def m(a=1, b:) [a, b] end
+ ruby
- m().should == [1, 2]
- m(2).should == [2, 2]
- m(b: 3).should == [1, 3]
- suppress_keyword_warning do
- m("a" => 1, b: 2).should == [{"a" => 1}, 2]
- end
- end
+ m(b: 2).should == [1, 2]
+ m(2, b: 1).should == [2, 1]
+ -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
end
- ruby_version_is "3.0" do
- evaluate <<-ruby do
- def m(a=1, b:) [a, b] end
- ruby
-
- m(b: 2).should == [1, 2]
- m(2, b: 1).should == [2, 1]
- -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
- end
-
- evaluate <<-ruby do
- def m(a=1, b: 2) [a, b] end
- ruby
+ evaluate <<-ruby do
+ def m(a=1, b: 2) [a, b] end
+ ruby
- m().should == [1, 2]
- m(2).should == [2, 2]
- m(b: 3).should == [1, 3]
- -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
- end
+ m().should == [1, 2]
+ m(2).should == [2, 2]
+ m(b: 3).should == [1, 3]
+ -> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
end
evaluate <<-ruby do
@@ -1013,292 +949,6 @@ describe "A method" do
m(1, 2, 3).should == [[1, 2], 3]
end
- ruby_version_is ""...'3.0' do
- evaluate <<-ruby do
- def m(*, a:) a end
- ruby
-
- m(a: 1).should == 1
- m(1, 2, a: 3).should == 3
- suppress_keyword_warning do
- m("a" => 1, a: 2).should == 2
- end
- end
-
- evaluate <<-ruby do
- def m(*a, b:) [a, b] end
- ruby
-
- m(b: 1).should == [[], 1]
- m(1, 2, b: 3).should == [[1, 2], 3]
- suppress_keyword_warning do
- m("a" => 1, b: 2).should == [[{"a" => 1}], 2]
- end
- end
-
- evaluate <<-ruby do
- def m(*, a: 1) a end
- ruby
-
- m().should == 1
- m(1, 2).should == 1
- m(a: 2).should == 2
- m(1, a: 2).should == 2
- suppress_keyword_warning do
- m("a" => 1, a: 2).should == 2
- end
- end
-
- evaluate <<-ruby do
- def m(*a, b: 1) [a, b] end
- ruby
-
- m().should == [[], 1]
- m(1, 2, 3, b: 4).should == [[1, 2, 3], 4]
- suppress_keyword_warning do
- m("a" => 1, b: 2).should == [[{"a" => 1}], 2]
- end
-
- a = mock("splat")
- a.should_not_receive(:to_ary)
- m(*a).should == [[a], 1]
- end
-
- evaluate <<-ruby do
- def m(*a, **) a end
- ruby
-
- m().should == []
- m(1, 2, 3, a: 4, b: 5).should == [1, 2, 3]
- m("a" => 1, a: 1).should == []
- m(1, **{a: 2}).should == [1]
-
- h = mock("keyword splat")
- h.should_receive(:to_hash)
- -> { m(**h) }.should raise_error(TypeError)
- end
-
- evaluate <<-ruby do
- def m(*, **k) k end
- ruby
-
- m().should == {}
- m(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
- m("a" => 1, a: 1).should == {"a" => 1, a: 1}
-
- h = mock("keyword splat")
- h.should_receive(:to_hash).and_return({a: 1})
- suppress_warning do
- m(h).should == {a: 1}
- end
- end
-
- evaluate <<-ruby do
- def m(a = nil, **k) [a, k] end
- ruby
-
- m().should == [nil, {}]
- m("a" => 1).should == [nil, {"a" => 1}]
- m(a: 1).should == [nil, {a: 1}]
- m("a" => 1, a: 1).should == [nil, {"a" => 1, a: 1}]
- m({ "a" => 1 }, a: 1).should == [{"a" => 1}, {a: 1}]
- suppress_warning do
- m({a: 1}, {}).should == [{a: 1}, {}]
-
- h = {"a" => 1, b: 2}
- m(h).should == [{"a" => 1}, {b: 2}]
- h.should == {"a" => 1, b: 2}
-
- h = {"a" => 1}
- m(h).first.should == h
-
- h = {}
- r = m(h)
- r.first.should be_nil
- r.last.should == {}
-
- hh = {}
- h = mock("keyword splat empty hash")
- h.should_receive(:to_hash).and_return(hh)
- r = m(h)
- r.first.should be_nil
- r.last.should == {}
-
- h = mock("keyword splat")
- h.should_receive(:to_hash).and_return({"a" => 1, a: 2})
- m(h).should == [{"a" => 1}, {a: 2}]
- end
- end
-
- evaluate <<-ruby do
- def m(*a, **k) [a, k] end
- ruby
-
- m().should == [[], {}]
- m(1).should == [[1], {}]
- m(a: 1, b: 2).should == [[], {a: 1, b: 2}]
- m(1, 2, 3, a: 2).should == [[1, 2, 3], {a: 2}]
-
- m("a" => 1).should == [[], {"a" => 1}]
- m(a: 1).should == [[], {a: 1}]
- m("a" => 1, a: 1).should == [[], {"a" => 1, a: 1}]
- m({ "a" => 1 }, a: 1).should == [[{"a" => 1}], {a: 1}]
- suppress_warning do
- m({a: 1}, {}).should == [[{a: 1}], {}]
- end
- m({a: 1}, {"a" => 1}).should == [[{a: 1}, {"a" => 1}], {}]
-
- bo = BasicObject.new
- def bo.to_a; [1, 2, 3]; end
- def bo.to_hash; {:b => 2, :c => 3}; end
-
- m(*bo, **bo).should == [[1, 2, 3], {:b => 2, :c => 3}]
- end
-
- evaluate <<-ruby do
- def m(*, a:) a end
- ruby
-
- m(a: 1).should == 1
- m(1, 2, a: 3).should == 3
- suppress_keyword_warning do
- m("a" => 1, a: 2).should == 2
- end
- end
-
- evaluate <<-ruby do
- def m(*a, b:) [a, b] end
- ruby
-
- m(b: 1).should == [[], 1]
- m(1, 2, b: 3).should == [[1, 2], 3]
- suppress_keyword_warning do
- m("a" => 1, b: 2).should == [[{"a" => 1}], 2]
- end
- end
-
- evaluate <<-ruby do
- def m(*, a: 1) a end
- ruby
-
- m().should == 1
- m(1, 2).should == 1
- m(a: 2).should == 2
- m(1, a: 2).should == 2
- suppress_keyword_warning do
- m("a" => 1, a: 2).should == 2
- end
- end
-
- evaluate <<-ruby do
- def m(*a, b: 1) [a, b] end
- ruby
-
- m().should == [[], 1]
- m(1, 2, 3, b: 4).should == [[1, 2, 3], 4]
- suppress_keyword_warning do
- m("a" => 1, b: 2).should == [[{"a" => 1}], 2]
- end
-
- a = mock("splat")
- a.should_not_receive(:to_ary)
- m(*a).should == [[a], 1]
- end
-
- evaluate <<-ruby do
- def m(*a, **) a end
- ruby
-
- m().should == []
- m(1, 2, 3, a: 4, b: 5).should == [1, 2, 3]
- m("a" => 1, a: 1).should == []
- m(1, **{a: 2}).should == [1]
-
- h = mock("keyword splat")
- h.should_receive(:to_hash)
- -> { m(**h) }.should raise_error(TypeError)
- end
-
- evaluate <<-ruby do
- def m(*, **k) k end
- ruby
-
- m().should == {}
- m(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
- m("a" => 1, a: 1).should == {"a" => 1, a: 1}
-
- h = mock("keyword splat")
- h.should_receive(:to_hash).and_return({a: 1})
- suppress_keyword_warning do
- m(h).should == {a: 1}
- end
- end
-
- evaluate <<-ruby do
- def m(a = nil, **k) [a, k] end
- ruby
-
- m().should == [nil, {}]
- m("a" => 1).should == [nil, {"a" => 1}]
- m(a: 1).should == [nil, {a: 1}]
- m("a" => 1, a: 1).should == [nil, {"a" => 1, a: 1}]
- m({ "a" => 1 }, a: 1).should == [{"a" => 1}, {a: 1}]
- suppress_keyword_warning do
- m({a: 1}, {}).should == [{a: 1}, {}]
- end
-
- h = {"a" => 1, b: 2}
- suppress_keyword_warning do
- m(h).should == [{"a" => 1}, {b: 2}]
- end
- h.should == {"a" => 1, b: 2}
-
- h = {"a" => 1}
- m(h).first.should == h
-
- h = {}
- suppress_keyword_warning do
- m(h).should == [nil, {}]
- end
-
- hh = {}
- h = mock("keyword splat empty hash")
- h.should_receive(:to_hash).and_return({a: 1})
- suppress_keyword_warning do
- m(h).should == [nil, {a: 1}]
- end
-
- h = mock("keyword splat")
- h.should_receive(:to_hash).and_return({"a" => 1})
- m(h).should == [h, {}]
- end
-
- evaluate <<-ruby do
- def m(*a, **k) [a, k] end
- ruby
-
- m().should == [[], {}]
- m(1).should == [[1], {}]
- m(a: 1, b: 2).should == [[], {a: 1, b: 2}]
- m(1, 2, 3, a: 2).should == [[1, 2, 3], {a: 2}]
-
- m("a" => 1).should == [[], {"a" => 1}]
- m(a: 1).should == [[], {a: 1}]
- m("a" => 1, a: 1).should == [[], {"a" => 1, a: 1}]
- m({ "a" => 1 }, a: 1).should == [[{"a" => 1}], {a: 1}]
- suppress_keyword_warning do
- m({a: 1}, {}).should == [[{a: 1}], {}]
- end
- m({a: 1}, {"a" => 1}).should == [[{a: 1}, {"a" => 1}], {}]
-
- bo = BasicObject.new
- def bo.to_a; [1, 2, 3]; end
- def bo.to_hash; {:b => 2, :c => 3}; end
-
- m(*bo, **bo).should == [[1, 2, 3], {:b => 2, :c => 3}]
- end
- end
-
evaluate <<-ruby do
def m(*, &b) b end
ruby
@@ -1457,47 +1107,25 @@ describe "A method" do
m({a: 1}).should == {a: 1}
m({"a" => 1}).should == {"a" => 1}
- -> { m(a: 1) }.should raise_error(ArgumentError)
- -> { m(**{a: 1}) }.should raise_error(ArgumentError)
- -> { m("a" => 1) }.should raise_error(ArgumentError)
+ -> { m(a: 1) }.should raise_error(ArgumentError, 'no keywords accepted')
+ -> { m(**{a: 1}) }.should raise_error(ArgumentError, 'no keywords accepted')
+ -> { m("a" => 1) }.should raise_error(ArgumentError, 'no keywords accepted')
end
- ruby_version_is ''...'3.0' do
- evaluate <<-ruby do
- def m(a, b = nil, c = nil, d, e: nil, **f)
- [a, b, c, d, e, f]
- end
- ruby
-
- result = m(1, 2)
- result.should == [1, nil, nil, 2, nil, {}]
-
- suppress_warning do
- result = m(1, 2, {foo: :bar})
- result.should == [1, nil, nil, 2, nil, {foo: :bar}]
+ evaluate <<-ruby do
+ def m(a, b = nil, c = nil, d, e: nil, **f)
+ [a, b, c, d, e, f]
end
+ ruby
- result = m(1, {foo: :bar})
- result.should == [1, nil, nil, {foo: :bar}, nil, {}]
- end
- end
-
- ruby_version_is '3.0' do
- evaluate <<-ruby do
- def m(a, b = nil, c = nil, d, e: nil, **f)
- [a, b, c, d, e, f]
- end
- ruby
-
- result = m(1, 2)
- result.should == [1, nil, nil, 2, nil, {}]
+ result = m(1, 2)
+ result.should == [1, nil, nil, 2, nil, {}]
- result = m(1, 2, {foo: :bar})
- result.should == [1, 2, nil, {foo: :bar}, nil, {}]
+ result = m(1, 2, {foo: :bar})
+ result.should == [1, 2, nil, {foo: :bar}, nil, {}]
- result = m(1, {foo: :bar})
- result.should == [1, nil, nil, {foo: :bar}, nil, {}]
- end
+ result = m(1, {foo: :bar})
+ result.should == [1, nil, nil, {foo: :bar}, nil, {}]
end
end
@@ -1511,60 +1139,27 @@ describe "A method" do
end
end
- ruby_version_is ''...'3.0' do
- context 'when passing an empty keyword splat to a method that does not accept keywords' do
- evaluate <<-ruby do
- def m(a); a; end
- ruby
- h = {}
-
- -> do
- m(**h).should == {}
- end.should complain(/warning: Passing the keyword argument as the last hash parameter is deprecated/)
- end
- end
- end
-
- ruby_version_is ''...'3.0' do
- context "assigns keyword arguments from a passed Hash without modifying it" do
- evaluate <<-ruby do
- def m(a: nil); a; end
- ruby
+ context 'when passing an empty keyword splat to a method that does not accept keywords' do
+ evaluate <<-ruby do
+ def m(a); a; end
+ ruby
+ h = {}
- options = {a: 1}.freeze
- -> do
- suppress_warning do
- m(options).should == 1
- end
- end.should_not raise_error
- options.should == {a: 1}
- end
+ -> do
+ m(**h).should == {}
+ end.should raise_error(ArgumentError)
end
end
- ruby_version_is '3.0' do
- context 'when passing an empty keyword splat to a method that does not accept keywords' do
- evaluate <<-ruby do
- def m(a); a; end
- ruby
- h = {}
-
- -> do
- m(**h).should == {}
- end.should raise_error(ArgumentError)
- end
- end
-
- context "raises ArgumentError if passing hash as keyword arguments" do
- evaluate <<-ruby do
- def m(a: nil); a; end
- ruby
+ context "raises ArgumentError if passing hash as keyword arguments" do
+ evaluate <<-ruby do
+ def m(a: nil); a; end
+ ruby
- options = {a: 1}.freeze
- -> do
- m(options)
- end.should raise_error(ArgumentError)
- end
+ options = {a: 1}.freeze
+ -> do
+ m(options)
+ end.should raise_error(ArgumentError)
end
end
@@ -1598,14 +1193,42 @@ describe "A method call with a space between method name and parentheses" do
end
end
- context "when a single argument provided" do
- it "assigns it" do
+ context "when a single argument is provided" do
+ it "assigns a simple expression" do
+ args = m (1)
+ args.should == [1]
+ end
+
+ it "assigns an expression consisting of multiple statements" do
+ args = m ((0; 1))
+ args.should == [1]
+ end
+
+ it "assigns one single statement, without the need of parentheses" do
args = m (1 == 1 ? true : false)
args.should == [true]
end
+
+ ruby_version_is "3.3" do
+ it "supports multiple statements" do
+ eval("m (1; 2)").should == [2]
+ end
+ end
end
- context "when 2+ arguments provided" do
+ context "when multiple arguments are provided" do
+ it "assigns simple expressions" do
+ args = m (1), (2)
+ args.should == [1, 2]
+ end
+
+ it "assigns expressions consisting of multiple statements" do
+ args = m ((0; 1)), ((2; 3))
+ args.should == [1, 3]
+ end
+ end
+
+ context "when the argument looks like an argument list" do
it "raises a syntax error" do
-> {
eval("m (1, 2)")
@@ -1670,105 +1293,108 @@ describe "An array-dereference method ([])" do
end
end
-ruby_version_is "3.0" do
- describe "An endless method definition" do
- context "without arguments" do
- evaluate <<-ruby do
- def m() = 42
- ruby
-
- m.should == 42
- end
-
- context "without parenthesis" do
- evaluate <<-ruby do
- def m = 42
- ruby
+describe "An endless method definition" do
+ context "without arguments" do
+ evaluate <<-ruby do
+ def m() = 42
+ ruby
- m.should == 42
- end
- end
+ m.should == 42
end
- context "with arguments" do
+ context "without parenthesis" do
evaluate <<-ruby do
- def m(a, b) = a + b
- ruby
+ def m = 42
+ ruby
- m(1, 4).should == 5
+ m.should == 42
end
end
+ end
- context "with multiline body" do
- evaluate <<-ruby do
- def m(n) =
- if n > 2
- m(n - 2) + m(n - 1)
- else
- 1
- end
- ruby
+ context "with arguments" do
+ evaluate <<-ruby do
+ def m(a, b) = a + b
+ ruby
- m(6).should == 8
- end
+ m(1, 4).should == 5
end
+ end
- context "with args forwarding" do
- evaluate <<-ruby do
- def mm(word, num:)
- word * num
+ context "with multiline body" do
+ evaluate <<-ruby do
+ def m(n) =
+ if n > 2
+ m(n - 2) + m(n - 1)
+ else
+ 1
end
+ ruby
- def m(...) = mm(...) + mm(...)
- ruby
-
- m("meow", num: 2).should == "meow" * 4
- end
+ m(6).should == 8
end
+ end
- ruby_version_is ""..."3.0" do
- context "inside 'endless' method definitions" do
- it "does not allow method calls without parenthesis" do
- -> {
- eval("def greet(person) = 'Hi, '.concat person")
- }.should raise_error(SyntaxError)
+ # tested more thoroughly in language/delegation_spec.rb
+ context "with args forwarding" do
+ evaluate <<-ruby do
+ def mm(word, num:)
+ word * num
end
- end
+
+ def m(...) = mm(...) + mm(...)
+ ruby
+
+ m("meow", num: 2).should == "meow" * 4
end
end
+end
- describe "Keyword arguments are now separated from positional arguments" do
- context "when the method has only positional parameters" do
- it "treats incoming keyword arguments as positional for compatibility" do
- def foo(a, b, c, hsh)
- hsh[:key]
- end
-
- foo(1, 2, 3, key: 42).should == 42
+describe "Keyword arguments are now separated from positional arguments" do
+ context "when the method has only positional parameters" do
+ it "treats incoming keyword arguments as positional for compatibility" do
+ def foo(a, b, c, hsh)
+ hsh[:key]
end
+
+ foo(1, 2, 3, key: 42).should == 42
end
+ end
- context "when the method takes a ** parameter" do
- it "captures the passed literal keyword arguments" do
- def foo(a, b, c, **hsh)
- hsh[:key]
- end
+ context "when the method takes a ** parameter" do
+ it "captures the passed literal keyword arguments" do
+ def foo(a, b, c, **hsh)
+ hsh[:key]
+ end
- foo(1, 2, 3, key: 42).should == 42
+ foo(1, 2, 3, key: 42).should == 42
+ end
+
+ it "captures the passed ** keyword arguments" do
+ def foo(a, b, c, **hsh)
+ hsh[:key]
end
- it "captures the passed ** keyword arguments" do
- def foo(a, b, c, **hsh)
- hsh[:key]
- end
+ h = { key: 42 }
+ foo(1, 2, 3, **h).should == 42
+ end
- h = { key: 42 }
- foo(1, 2, 3, **h).should == 42
+ it "does not convert a positional Hash to keyword arguments" do
+ def foo(a, b, c, **hsh)
+ hsh[:key]
end
- it "does not convert a positional Hash to keyword arguments" do
- def foo(a, b, c, **hsh)
- hsh[:key]
+ -> {
+ foo(1, 2, 3, { key: 42 })
+ }.should raise_error(ArgumentError, 'wrong number of arguments (given 4, expected 3)')
+ end
+ end
+
+ context "when the method takes a key: parameter" do
+ context "when it's called with a positional Hash and no **" do
+ it "raises ArgumentError" do
+ def foo(a, b, c, key: 1)
+ key
end
-> {
@@ -1777,28 +1403,14 @@ ruby_version_is "3.0" do
end
end
- context "when the method takes a key: parameter" do
- context "when it's called with a positional Hash and no **" do
- it "raises ArgumentError" do
- def foo(a, b, c, key: 1)
- key
- end
-
- -> {
- foo(1, 2, 3, { key: 42 })
- }.should raise_error(ArgumentError, 'wrong number of arguments (given 4, expected 3)')
+ context "when it's called with **" do
+ it "captures the passed keyword arguments" do
+ def foo(a, b, c, key: 1)
+ key
end
- end
- context "when it's called with **" do
- it "captures the passed keyword arguments" do
- def foo(a, b, c, key: 1)
- key
- end
-
- h = { key: 42 }
- foo(1, 2, 3, **h).should == 42
- end
+ h = { key: 42 }
+ foo(1, 2, 3, **h).should == 42
end
end
end
@@ -1847,7 +1459,7 @@ ruby_version_is "3.1" do
describe "Inside 'endless' method definitions" do
it "allows method calls without parenthesis" do
eval <<-ruby
- def greet(person) = "Hi, ".concat person
+ def greet(person) = "Hi, ".dup.concat person
ruby
greet("Homer").should == "Hi, Homer"
diff --git a/spec/ruby/language/module_spec.rb b/spec/ruby/language/module_spec.rb
index e361bf58f5..fffcf9c90d 100644
--- a/spec/ruby/language/module_spec.rb
+++ b/spec/ruby/language/module_spec.rb
@@ -78,20 +78,10 @@ describe "Assigning an anonymous module to a constant" do
mod.name.should == "ModuleSpecs_CS1"
end
- ruby_version_is ""..."3.0" do
- it "does not set the name of a module scoped by an anonymous module" do
- a, b = Module.new, Module.new
- a::B = b
- b.name.should be_nil
- end
- end
-
- ruby_version_is "3.0" do
- it "sets the name of a module scoped by an anonymous module" do
- a, b = Module.new, Module.new
- a::B = b
- b.name.should.end_with? '::B'
- end
+ it "sets the name of a module scoped by an anonymous module" do
+ a, b = Module.new, Module.new
+ a::B = b
+ b.name.should.end_with? '::B'
end
it "sets the name of contained modules when assigning a toplevel anonymous module" do
diff --git a/spec/ruby/language/numbered_parameters_spec.rb b/spec/ruby/language/numbered_parameters_spec.rb
index 424d7a06e3..06f9948c58 100644
--- a/spec/ruby/language/numbered_parameters_spec.rb
+++ b/spec/ruby/language/numbered_parameters_spec.rb
@@ -22,7 +22,7 @@ describe "Numbered parameters" do
it "does not support more than 9 parameters" do
-> {
proc { [_10] }.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
- }.should raise_error(NameError, /undefined local variable or method `_10'/)
+ }.should raise_error(NameError, /undefined local variable or method [`']_10'/)
end
it "can not be used in both outer and nested blocks at the same time" do
@@ -31,38 +31,19 @@ describe "Numbered parameters" do
}.should raise_error(SyntaxError, /numbered parameter is already used in/m)
end
- ruby_version_is ''...'3.0' do
- it "can be overwritten with local variable" do
- suppress_warning do
- eval <<~CODE
- _1 = 0
- proc { _1 }.call("a").should == 0
- CODE
- end
- end
-
- it "warns when numbered parameter is overwritten with local variable" do
- -> {
- eval("_1 = 0")
- }.should complain(/warning: `_1' is reserved for numbered parameter; consider another name/)
- end
+ it "cannot be overwritten with local variable" do
+ -> {
+ eval <<~CODE
+ _1 = 0
+ proc { _1 }.call("a").should == 0
+ CODE
+ }.should raise_error(SyntaxError, /_1 is reserved for numbered parameter/)
end
- ruby_version_is '3.0' do
- it "cannot be overwritten with local variable" do
- -> {
- eval <<~CODE
- _1 = 0
- proc { _1 }.call("a").should == 0
- CODE
- }.should raise_error(SyntaxError, /_1 is reserved for numbered parameter/)
- end
-
- it "errors when numbered parameter is overwritten with local variable" do
- -> {
- eval("_1 = 0")
- }.should raise_error(SyntaxError, /_1 is reserved for numbered parameter/)
- end
+ it "errors when numbered parameter is overwritten with local variable" do
+ -> {
+ eval("_1 = 0")
+ }.should raise_error(SyntaxError, /_1 is reserved for numbered parameter/)
end
it "raises SyntaxError when block parameters are specified explicitly" do
@@ -80,16 +61,8 @@ describe "Numbered parameters" do
end
describe "assigning to a numbered parameter" do
- ruby_version_is ''...'3.0' do
- it "warns" do
- -> { eval("proc { _1 = 0 }") }.should complain(/warning: `_1' is reserved for numbered parameter; consider another name/)
- end
- end
-
- ruby_version_is '3.0' do
- it "raises SyntaxError" do
- -> { eval("proc { _1 = 0 }") }.should raise_error(SyntaxError, /_1 is reserved for numbered parameter/)
- end
+ it "raises SyntaxError" do
+ -> { eval("proc { _1 = 0 }") }.should raise_error(SyntaxError, /_1 is reserved for numbered parameter/)
end
end
@@ -109,6 +82,19 @@ describe "Numbered parameters" do
lambda { _9 }.arity.should == 9
end
+ it "affects block parameters" do
+ -> { _1 }.parameters.should == [[:req, :_1]]
+ -> { _2 }.parameters.should == [[:req, :_1], [:req, :_2]]
+
+ proc { _1 }.parameters.should == [[:opt, :_1]]
+ proc { _2 }.parameters.should == [[:opt, :_1], [:opt, :_2]]
+ end
+
+ it "affects binding local variables" do
+ -> { _1; binding.local_variables }.call("a").should == [:_1]
+ -> { _2; binding.local_variables }.call("a", "b").should == [:_1, :_2]
+ end
+
it "does not work in methods" do
obj = Object.new
def obj.foo; _1 end
diff --git a/spec/ruby/language/optional_assignments_spec.rb b/spec/ruby/language/optional_assignments_spec.rb
index 02461655d6..2443cc6b79 100644
--- a/spec/ruby/language/optional_assignments_spec.rb
+++ b/spec/ruby/language/optional_assignments_spec.rb
@@ -57,7 +57,7 @@ describe 'Optional variable assignments' do
end
end
- describe 'using a accessor' do
+ describe 'using an accessor' do
before do
klass = Class.new { attr_accessor :b }
@a = klass.new
@@ -103,6 +103,16 @@ describe 'Optional variable assignments' do
@a.b.should == 10
end
+ it 'does evaluate receiver only once when assigns' do
+ ScratchPad.record []
+ @a.b = nil
+
+ (ScratchPad << :evaluated; @a).b ||= 10
+
+ ScratchPad.recorded.should == [:evaluated]
+ @a.b.should == 10
+ end
+
it 'returns the new value if set to false' do
def @a.b=(x)
:v
@@ -122,29 +132,148 @@ describe 'Optional variable assignments' do
(@a.b ||= 20).should == 10
end
- it 'works when writer is private' do
+ it 'ignores method visibility when receiver is self' do
+ klass_with_private_methods = Class.new do
+ def initialize(v) @a = v end
+ def public_method(v); self.a ||= v end
+ private
+ def a; @a end
+ def a=(v) @a = v; 42 end
+ end
+
+ a = klass_with_private_methods.new(false)
+ a.public_method(10).should == 10
+ end
+ end
+
+ describe 'using a #[]' do
+ before do
+ @a = {}
klass = Class.new do
- def t
- self.b = false
- (self.b ||= 10).should == 10
- (self.b ||= 20).should == 10
+ def [](k)
+ @hash ||= {}
+ @hash[k]
+ end
+
+ def []=(k, v)
+ @hash ||= {}
+ @hash[k] = v
+ 7
end
+ end
+ @b = klass.new
+ end
+
+ it 'returns the assigned value, not the result of the []= method with ||=' do
+ (@b[:k] ||= 12).should == 12
+ end
+
+ it "evaluates the index precisely once" do
+ ary = [:x, :y]
+ @a[:x] = 15
+ @a[ary.pop] ||= 25
+ ary.should == [:x]
+ @a.should == { x: 15, y: 25 }
+ end
- def b
- @b
+ it "evaluates the index arguments in the correct order" do
+ ary = Class.new(Array) do
+ def [](x, y)
+ super(x + 3 * y)
end
- def b=(x)
- @b = x
- :v
+ def []=(x, y, value)
+ super(x + 3 * y, value)
end
+ end.new
+ ary[0, 0] = 1
+ ary[1, 0] = 1
+ ary[2, 0] = nil
+ ary[3, 0] = 1
+ ary[4, 0] = 1
+ ary[5, 0] = 1
+ ary[6, 0] = nil
+
+ foo = [0, 2]
+
+ ary[foo.pop, foo.pop] ||= 2 # expected `ary[2, 0] ||= 2`
+
+ ary[2, 0].should == 2
+ ary[6, 0].should == nil # returns the same element as `ary[0, 2]`
+ end
+
+ it 'evaluates receiver only once when assigns' do
+ ScratchPad.record []
+ @a[:k] = nil
- private :b=
+ (ScratchPad << :evaluated; @a)[:k] ||= 2
+
+ ScratchPad.recorded.should == [:evaluated]
+ @a[:k].should == 2
+ end
+
+ it 'ignores method visibility when receiver is self' do
+ klass_with_private_methods = Class.new do
+ def initialize(h) @a = h end
+ def public_method(k, v); self[k] ||= v end
+ private
+ def [](k) @a[k] end
+ def []=(k, v) @a[k] = v; 42 end
end
- klass.new.t
+ a = klass_with_private_methods.new(k: false)
+ a.public_method(:k, 10).should == 10
end
+ context 'splatted argument' do
+ it 'correctly handles it' do
+ (@b[*[:m]] ||= 10).should == 10
+ @b[:m].should == 10
+
+ (@b[*(1; [:n])] ||= 10).should == 10
+ @b[:n].should == 10
+
+ (@b[*begin 1; [:k] end] ||= 10).should == 10
+ @b[:k].should == 10
+ end
+
+ it 'calls #to_a only once' do
+ k = Object.new
+ def k.to_a
+ ScratchPad << :to_a
+ [:k]
+ end
+
+ ScratchPad.record []
+ (@b[*k] ||= 20).should == 20
+ @b[:k].should == 20
+ ScratchPad.recorded.should == [:to_a]
+ end
+
+ it 'correctly handles a nested splatted argument' do
+ (@b[*[*[:k]]] ||= 20).should == 20
+ @b[:k].should == 20
+ end
+
+ it 'correctly handles multiple nested splatted arguments' do
+ klass_with_multiple_parameters = Class.new do
+ def [](k1, k2, k3)
+ @hash ||= {}
+ @hash[:"#{k1}#{k2}#{k3}"]
+ end
+
+ def []=(k1, k2, k3, v)
+ @hash ||= {}
+ @hash[:"#{k1}#{k2}#{k3}"] = v
+ 7
+ end
+ end
+ a = klass_with_multiple_parameters.new
+
+ (a[*[:a], *[:b], *[:c]] ||= 20).should == 20
+ a[:a, :b, :c].should == 20
+ end
+ end
end
end
@@ -191,7 +320,7 @@ describe 'Optional variable assignments' do
end
end
- describe 'using a single variable' do
+ describe 'using an accessor' do
before do
klass = Class.new { attr_accessor :b }
@a = klass.new
@@ -236,6 +365,29 @@ describe 'Optional variable assignments' do
@a.b.should == 20
end
+
+ it 'does evaluate receiver only once when assigns' do
+ ScratchPad.record []
+ @a.b = 10
+
+ (ScratchPad << :evaluated; @a).b &&= 20
+
+ ScratchPad.recorded.should == [:evaluated]
+ @a.b.should == 20
+ end
+
+ it 'ignores method visibility when receiver is self' do
+ klass_with_private_methods = Class.new do
+ def initialize(v) @a = v end
+ def public_method(v); self.a &&= v end
+ private
+ def a; @a end
+ def a=(v) @a = v; 42 end
+ end
+
+ a = klass_with_private_methods.new(true)
+ a.public_method(10).should == 10
+ end
end
describe 'using a #[]' do
@@ -297,17 +449,15 @@ describe 'Optional variable assignments' do
end
it 'returns the assigned value, not the result of the []= method with ||=' do
- (@b[:k] ||= 12).should == 12
- end
-
- it 'correctly handles a splatted argument for the index' do
- (@b[*[:k]] ||= 12).should == 12
+ @b[:k] = 10
+ (@b[:k] &&= 12).should == 12
end
it "evaluates the index precisely once" do
ary = [:x, :y]
@a[:x] = 15
- @a[ary.pop] ||= 25
+ @a[:y] = 20
+ @a[ary.pop] &&= 25
ary.should == [:x]
@a.should == { x: 15, y: 25 }
end
@@ -324,24 +474,103 @@ describe 'Optional variable assignments' do
end.new
ary[0, 0] = 1
ary[1, 0] = 1
- ary[2, 0] = nil
+ ary[2, 0] = 1
ary[3, 0] = 1
ary[4, 0] = 1
ary[5, 0] = 1
- ary[6, 0] = nil
+ ary[6, 0] = 1
foo = [0, 2]
- ary[foo.pop, foo.pop] ||= 2
+ ary[foo.pop, foo.pop] &&= 2 # expected `ary[2, 0] &&= 2`
ary[2, 0].should == 2
- ary[6, 0].should == nil
+ ary[6, 0].should == 1 # returns the same element as `ary[0, 2]`
+ end
+
+ it 'evaluates receiver only once when assigns' do
+ ScratchPad.record []
+ @a[:k] = 1
+
+ (ScratchPad << :evaluated; @a)[:k] &&= 2
+
+ ScratchPad.recorded.should == [:evaluated]
+ @a[:k].should == 2
end
it 'returns the assigned value, not the result of the []= method with +=' do
@b[:k] = 17
(@b[:k] += 12).should == 29
end
+
+ it 'ignores method visibility when receiver is self' do
+ klass_with_private_methods = Class.new do
+ def initialize(h) @a = h end
+ def public_method(k, v); self[k] &&= v end
+ private
+ def [](k) @a[k] end
+ def []=(k, v) @a[k] = v; 42 end
+ end
+
+ a = klass_with_private_methods.new(k: true)
+ a.public_method(:k, 10).should == 10
+ end
+
+ context 'splatted argument' do
+ it 'correctly handles it' do
+ @b[:m] = 0
+ (@b[*[:m]] &&= 10).should == 10
+ @b[:m].should == 10
+
+ @b[:n] = 0
+ (@b[*(1; [:n])] &&= 10).should == 10
+ @b[:n].should == 10
+
+ @b[:k] = 0
+ (@b[*begin 1; [:k] end] &&= 10).should == 10
+ @b[:k].should == 10
+ end
+
+ it 'calls #to_a only once' do
+ k = Object.new
+ def k.to_a
+ ScratchPad << :to_a
+ [:k]
+ end
+
+ ScratchPad.record []
+ @b[:k] = 10
+ (@b[*k] &&= 20).should == 20
+ @b[:k].should == 20
+ ScratchPad.recorded.should == [:to_a]
+ end
+
+ it 'correctly handles a nested splatted argument' do
+ @b[:k] = 10
+ (@b[*[*[:k]]] &&= 20).should == 20
+ @b[:k].should == 20
+ end
+
+ it 'correctly handles multiple nested splatted arguments' do
+ klass_with_multiple_parameters = Class.new do
+ def [](k1, k2, k3)
+ @hash ||= {}
+ @hash[:"#{k1}#{k2}#{k3}"]
+ end
+
+ def []=(k1, k2, k3, v)
+ @hash ||= {}
+ @hash[:"#{k1}#{k2}#{k3}"] = v
+ 7
+ end
+ end
+ a = klass_with_multiple_parameters.new
+
+ a[:a, :b, :c] = 10
+ (a[*[:a], *[:b], *[:c]] &&= 20).should == 20
+ a[:a, :b, :c].should == 20
+ end
+ end
end
end
@@ -434,7 +663,7 @@ describe 'Optional constant assignment' do
ConstantSpecs::ClassA::OR_ASSIGNED_CONSTANT2.should == :assigned
end
- it 'causes side-effects of the module part to be applied (for nil constant)' do
+ it 'causes side-effects of the module part to be applied only once (for nil constant)' do
suppress_warning do # already initialized constant
ConstantSpecs::ClassA::NIL_OR_ASSIGNED_CONSTANT2 = nil
x = 0
@@ -492,5 +721,20 @@ describe 'Optional constant assignment' do
ConstantSpecs::OpAssignFalse.should == false
ConstantSpecs.send :remove_const, :OpAssignFalse
end
+
+ it 'causes side-effects of the module part to be applied only once (when assigns)' do
+ module ConstantSpecs
+ OpAssignTrue = true
+ end
+
+ suppress_warning do # already initialized constant
+ x = 0
+ (x += 1; ConstantSpecs)::OpAssignTrue &&= :assigned
+ x.should == 1
+ ConstantSpecs::OpAssignTrue.should == :assigned
+ end
+
+ ConstantSpecs.send :remove_const, :OpAssignTrue
+ end
end
end
diff --git a/spec/ruby/language/pattern_matching_spec.rb b/spec/ruby/language/pattern_matching_spec.rb
index e4b7a5105e..a8ec078cd0 100644
--- a/spec/ruby/language/pattern_matching_spec.rb
+++ b/spec/ruby/language/pattern_matching_spec.rb
@@ -8,8 +8,8 @@ describe "Pattern matching" do
ScratchPad.record []
end
- ruby_version_is "3.0" do
- it "can be standalone assoc operator that deconstructs value" do
+ describe "can be standalone assoc operator that" do
+ it "deconstructs value" do
suppress_warning do
eval(<<-RUBY).should == [0, 1]
[0, 1] => [a, b]
@@ -18,105 +18,117 @@ describe "Pattern matching" do
end
end
- describe "find pattern" do
- it "captures preceding elements to the pattern" do
- eval(<<~RUBY).should == [0, 1]
- case [0, 1, 2, 3]
- in [*pre, 2, 3]
- pre
- else
- false
- end
+ it "deconstructs value and properly scopes variables" do
+ suppress_warning do
+ eval(<<-RUBY).should == [0, nil]
+ a = nil
+ eval(<<-PATTERN)
+ [0, 1] => [a, b]
+ PATTERN
+ [a, defined?(b)]
RUBY
end
+ end
+ end
- it "captures following elements to the pattern" do
- eval(<<~RUBY).should == [2, 3]
- case [0, 1, 2, 3]
- in [0, 1, *post]
- post
- else
- false
- end
- RUBY
- end
+ describe "find pattern" do
+ it "captures preceding elements to the pattern" do
+ eval(<<~RUBY).should == [0, 1]
+ case [0, 1, 2, 3]
+ in [*pre, 2, 3]
+ pre
+ else
+ false
+ end
+ RUBY
+ end
- it "captures both preceding and following elements to the pattern" do
- eval(<<~RUBY).should == [[0, 1], [3, 4]]
- case [0, 1, 2, 3, 4]
- in [*pre, 2, *post]
- [pre, post]
- else
- false
- end
- RUBY
- end
+ it "captures following elements to the pattern" do
+ eval(<<~RUBY).should == [2, 3]
+ case [0, 1, 2, 3]
+ in [0, 1, *post]
+ post
+ else
+ false
+ end
+ RUBY
+ end
- it "can capture the entirety of the pattern" do
- eval(<<~RUBY).should == [0, 1, 2, 3, 4]
- case [0, 1, 2, 3, 4]
- in [*everything]
- everything
- else
- false
- end
- RUBY
- end
+ it "captures both preceding and following elements to the pattern" do
+ eval(<<~RUBY).should == [[0, 1], [3, 4]]
+ case [0, 1, 2, 3, 4]
+ in [*pre, 2, *post]
+ [pre, post]
+ else
+ false
+ end
+ RUBY
+ end
- it "will match an empty Array-like structure" do
- eval(<<~RUBY).should == []
- case []
- in [*everything]
- everything
- else
- false
- end
- RUBY
- end
+ it "can capture the entirety of the pattern" do
+ eval(<<~RUBY).should == [0, 1, 2, 3, 4]
+ case [0, 1, 2, 3, 4]
+ in [*everything]
+ everything
+ else
+ false
+ end
+ RUBY
+ end
- it "can be nested" do
- eval(<<~RUBY).should == [[0, [2, 4, 6]], [[4, 16, 64]], 27]
- case [0, [2, 4, 6], [3, 9, 27], [4, 16, 64]]
- in [*pre, [*, 9, a], *post]
- [pre, post, a]
- else
- false
- end
- RUBY
- end
+ it "will match an empty Array-like structure" do
+ eval(<<~RUBY).should == []
+ case []
+ in [*everything]
+ everything
+ else
+ false
+ end
+ RUBY
+ end
- it "can be nested with an array pattern" do
- eval(<<~RUBY).should == [[4, 16, 64]]
- case [0, [2, 4, 6], [3, 9, 27], [4, 16, 64]]
- in [_, _, [*, 9, *], *post]
- post
- else
- false
- end
- RUBY
- end
+ it "can be nested" do
+ eval(<<~RUBY).should == [[0, [2, 4, 6]], [[4, 16, 64]], 27]
+ case [0, [2, 4, 6], [3, 9, 27], [4, 16, 64]]
+ in [*pre, [*, 9, a], *post]
+ [pre, post, a]
+ else
+ false
+ end
+ RUBY
+ end
- it "can be nested within a hash pattern" do
- eval(<<~RUBY).should == [27]
- case {a: [3, 9, 27]}
- in {a: [*, 9, *post]}
- post
- else
- false
- end
- RUBY
- end
+ it "can be nested with an array pattern" do
+ eval(<<~RUBY).should == [[4, 16, 64]]
+ case [0, [2, 4, 6], [3, 9, 27], [4, 16, 64]]
+ in [_, _, [*, 9, *], *post]
+ post
+ else
+ false
+ end
+ RUBY
+ end
- it "can nest hash and array patterns" do
- eval(<<~RUBY).should == [42, 2]
- case [0, {a: 42, b: [0, 1]}, {a: 42, b: [1, 2]}]
- in [*, {a:, b: [1, c]}, *]
- [a, c]
- else
- false
- end
- RUBY
- end
+ it "can be nested within a hash pattern" do
+ eval(<<~RUBY).should == [27]
+ case {a: [3, 9, 27]}
+ in {a: [*, 9, *post]}
+ post
+ else
+ false
+ end
+ RUBY
+ end
+
+ it "can nest hash and array patterns" do
+ eval(<<~RUBY).should == [42, 2]
+ case [0, {a: 42, b: [0, 1]}, {a: 42, b: [1, 2]}]
+ in [*, {a:, b: [1, c]}, *]
+ [a, c]
+ else
+ false
+ end
+ RUBY
end
end
@@ -154,35 +166,25 @@ describe "Pattern matching" do
@src = 'case [0, 1]; in [a, b]; end'
end
- ruby_version_is ""..."3.0" do
- it "warns about pattern matching is experimental feature" do
- -> { eval @src }.should complain(/pattern matching is experimental, and the behavior may change in future versions of Ruby!/i)
- end
- end
-
- ruby_version_is "3.0" do
- it "does not warn about pattern matching is experimental feature" do
- -> { eval @src }.should_not complain
- end
+ it "does not warn about pattern matching is experimental feature" do
+ -> { eval @src }.should_not complain
end
end
context 'when one-line form' do
- ruby_version_is '3.0' do
- before :each do
- @src = '[0, 1] => [a, b]'
- end
+ before :each do
+ @src = '[0, 1] => [a, b]'
+ end
- ruby_version_is ""..."3.1" do
- it "warns about pattern matching is experimental feature" do
- -> { eval @src }.should complain(/pattern matching is experimental, and the behavior may change in future versions of Ruby!/i)
- end
+ ruby_version_is ""..."3.1" do
+ it "warns about pattern matching is experimental feature" do
+ -> { eval @src }.should complain(/pattern matching is experimental, and the behavior may change in future versions of Ruby!/i)
end
+ end
- ruby_version_is "3.1" do
- it "does not warn about pattern matching is experimental feature" do
- -> { eval @src }.should_not complain
- end
+ ruby_version_is "3.1" do
+ it "does not warn about pattern matching is experimental feature" do
+ -> { eval @src }.should_not complain
end
end
end
@@ -205,7 +207,7 @@ describe "Pattern matching" do
in []
end
RUBY
- }.should raise_error(SyntaxError, /syntax error, unexpected `in'|\(eval\):3: syntax error, unexpected keyword_in/)
+ }.should raise_error(SyntaxError, /syntax error, unexpected `in'|\(eval\):3: syntax error, unexpected keyword_in|unexpected 'in'/)
-> {
eval <<~RUBY
@@ -214,7 +216,7 @@ describe "Pattern matching" do
when 1 == 1
end
RUBY
- }.should raise_error(SyntaxError, /syntax error, unexpected `when'|\(eval\):3: syntax error, unexpected keyword_when/)
+ }.should raise_error(SyntaxError, /syntax error, unexpected `when'|\(eval\):3: syntax error, unexpected keyword_when|unexpected 'when'/)
end
it "checks patterns until the first matching" do
@@ -271,7 +273,7 @@ describe "Pattern matching" do
true
end
RUBY
- }.should raise_error(SyntaxError, /unexpected/)
+ }.should raise_error(SyntaxError, /unexpected|expected a delimiter after the predicates of a `when` clause/)
end
it "evaluates the case expression once for multiple patterns, caching the result" do
@@ -687,26 +689,24 @@ describe "Pattern matching" do
RUBY
end
- ruby_version_is "3.0" do
- it "calls #deconstruct once for multiple patterns, caching the result" do
- obj = Object.new
+ it "calls #deconstruct once for multiple patterns, caching the result" do
+ obj = Object.new
- def obj.deconstruct
- ScratchPad << :deconstruct
- [0, 1]
- end
+ def obj.deconstruct
+ ScratchPad << :deconstruct
+ [0, 1]
+ end
- eval(<<~RUBY).should == true
- case obj
- in [1, 2]
- false
- in [0, 1]
- true
- end
- RUBY
+ eval(<<~RUBY).should == true
+ case obj
+ in [1, 2]
+ false
+ in [0, 1]
+ true
+ end
+ RUBY
- ScratchPad.recorded.should == [:deconstruct]
- end
+ ScratchPad.recorded.should == [:deconstruct]
end
it "calls #deconstruct even on objects that are already an array" do
@@ -739,6 +739,20 @@ describe "Pattern matching" do
RUBY
end
+ it "checks Constant === object before calling #deconstruct" do
+ c1 = Class.new
+ obj = c1.new
+ obj.should_not_receive(:deconstruct)
+ eval(<<~RUBY).should == false
+ case obj
+ in String[1]
+ true
+ else
+ false
+ end
+ RUBY
+ end
+
it "does not match object without #deconstruct method" do
obj = Object.new
obj.should_receive(:respond_to?).with(:deconstruct)
@@ -770,11 +784,7 @@ describe "Pattern matching" do
it "accepts a subclass of Array from #deconstruct" do
obj = Object.new
def obj.deconstruct
- subarray = Class.new(Array).new(2)
- def subarray.[](n)
- n
- end
- subarray
+ Class.new(Array).new([0, 1])
end
eval(<<~RUBY).should == true
@@ -1004,7 +1014,7 @@ describe "Pattern matching" do
in {"a" => 1}
end
RUBY
- }.should raise_error(SyntaxError, /unexpected/)
+ }.should raise_error(SyntaxError, /unexpected|expected a label as the key in the hash pattern/)
end
it "does not support string interpolation in keys" do
@@ -1016,7 +1026,7 @@ describe "Pattern matching" do
in {"#{x}": 1}
end
RUBY
- }.should raise_error(SyntaxError, /symbol literal with interpolation is not allowed/)
+ }.should raise_error(SyntaxError, /symbol literal with interpolation is not allowed|expected a label as the key in the hash pattern/)
end
it "raise SyntaxError when keys duplicate in pattern" do
@@ -1072,6 +1082,20 @@ describe "Pattern matching" do
RUBY
end
+ it "checks Constant === object before calling #deconstruct_keys" do
+ c1 = Class.new
+ obj = c1.new
+ obj.should_not_receive(:deconstruct_keys)
+ eval(<<~RUBY).should == false
+ case obj
+ in String(a: 1)
+ true
+ else
+ false
+ end
+ RUBY
+ end
+
it "does not match object without #deconstruct_keys method" do
obj = Object.new
obj.should_receive(:respond_to?).with(:deconstruct_keys)
@@ -1232,6 +1256,37 @@ describe "Pattern matching" do
RUBY
end
+ it "in {} only matches empty hashes" do
+ eval(<<~RUBY).should == false
+ case {a: 1}
+ in {}
+ true
+ else
+ false
+ end
+ RUBY
+ end
+
+ it "in {**nil} only matches empty hashes" do
+ eval(<<~RUBY).should == true
+ case {}
+ in {**nil}
+ true
+ else
+ false
+ end
+ RUBY
+
+ eval(<<~RUBY).should == false
+ case {a: 1}
+ in {**nil}
+ true
+ else
+ false
+ end
+ RUBY
+ end
+
it "matches anything with **" do
eval(<<~RUBY).should == true
case {a: 1}
@@ -1340,76 +1395,115 @@ describe "Pattern matching" do
end
end
- ruby_version_is "3.1" do
- it "can omit parentheses in one line pattern matching" do
- eval(<<~RUBY).should == [1, 2]
- [1, 2] => a, b
- [a, b]
- RUBY
+ describe "Ruby 3.1 improvements" do
+ ruby_version_is "3.1" do
+ it "can omit parentheses in one line pattern matching" do
+ eval(<<~RUBY).should == [1, 2]
+ [1, 2] => a, b
+ [a, b]
+ RUBY
- eval(<<~RUBY).should == 1
- {a: 1} => a:
- a
- RUBY
- end
+ eval(<<~RUBY).should == 1
+ {a: 1} => a:
+ a
+ RUBY
+ end
- it "supports pinning instance variables" do
- eval(<<~RUBY).should == true
- @a = /a/
- case 'abc'
- in ^@a
- true
+ it "supports pinning instance variables" do
+ eval(<<~RUBY).should == true
+ @a = /a/
+ case 'abc'
+ in ^@a
+ true
+ end
+ RUBY
+ end
+
+ it "supports pinning class variables" do
+ result = nil
+ Module.new do
+ result = module_eval(<<~RUBY)
+ @@a = 0..10
+
+ case 2
+ in ^@@a
+ true
+ end
+ RUBY
end
- RUBY
- end
- it "supports pinning class variables" do
- result = nil
- Module.new do
- result = module_eval(<<~RUBY)
- @@a = 0..10
+ result.should == true
+ end
- case 2
- in ^@@a
+ it "supports pinning global variables" do
+ eval(<<~RUBY).should == true
+ $a = /a/
+ case 'abc'
+ in ^$a
true
end
RUBY
end
- result.should == true
+ it "supports pinning expressions" do
+ eval(<<~RUBY).should == true
+ case 'abc'
+ in ^(/a/)
+ true
+ end
+ RUBY
+
+ eval(<<~RUBY).should == true
+ case 0
+ in ^(0+0)
+ true
+ end
+ RUBY
+ end
+
+ it "supports pinning expressions in array pattern" do
+ eval(<<~RUBY).should == true
+ case [3]
+ in [^(1+2)]
+ true
+ end
+ RUBY
+ end
+
+ it "supports pinning expressions in hash pattern" do
+ eval(<<~RUBY).should == true
+ case {name: '2.6', released_at: Time.new(2018, 12, 25)}
+ in {released_at: ^(Time.new(2010)..Time.new(2020))}
+ true
+ end
+ RUBY
+ end
end
+ end
- it "supports pinning global variables" do
- eval(<<~RUBY).should == true
- $a = /a/
- case 'abc'
- in ^$a
- true
- end
- RUBY
+ describe "value in pattern" do
+ it "returns true if the pattern matches" do
+ eval("1 in 1").should == true
+
+ eval("1 in Integer").should == true
+
+ e = nil
+ eval("[1, 2] in [1, e]").should == true
+ e.should == 2
+
+ k = nil
+ eval("{k: 1} in {k:}").should == true
+ k.should == 1
end
- it "supports pinning expressions" do
- eval(<<~RUBY).should == true
- case 'abc'
- in ^(/a/)
- true
- end
- RUBY
+ it "returns false if the pattern does not match" do
+ eval("1 in 2").should == false
- eval(<<~RUBY).should == true
- case {name: '2.6', released_at: Time.new(2018, 12, 25)}
- in {released_at: ^(Time.new(2010)..Time.new(2020))}
- true
- end
- RUBY
+ eval("1 in Float").should == false
- eval(<<~RUBY).should == true
- case 0
- in ^(0+0)
- true
- end
- RUBY
+ eval("[1, 2] in [2, e]").should == false
+
+ eval("{k: 1} in {k: 2}").should == false
end
end
end
diff --git a/spec/ruby/language/predefined_spec.rb b/spec/ruby/language/predefined_spec.rb
index f6cec5fa75..ac28f1e8a0 100644
--- a/spec/ruby/language/predefined_spec.rb
+++ b/spec/ruby/language/predefined_spec.rb
@@ -133,7 +133,7 @@ describe "Predefined global $&" do
end
it "sets the encoding to the encoding of the source String" do
- "abc".force_encoding(Encoding::EUC_JP) =~ /b/
+ "abc".dup.force_encoding(Encoding::EUC_JP) =~ /b/
$&.encoding.should equal(Encoding::EUC_JP)
end
end
@@ -146,12 +146,12 @@ describe "Predefined global $`" do
end
it "sets the encoding to the encoding of the source String" do
- "abc".force_encoding(Encoding::EUC_JP) =~ /b/
+ "abc".dup.force_encoding(Encoding::EUC_JP) =~ /b/
$`.encoding.should equal(Encoding::EUC_JP)
end
it "sets an empty result to the encoding of the source String" do
- "abc".force_encoding(Encoding::ISO_8859_1) =~ /a/
+ "abc".dup.force_encoding(Encoding::ISO_8859_1) =~ /a/
$`.encoding.should equal(Encoding::ISO_8859_1)
end
end
@@ -164,12 +164,12 @@ describe "Predefined global $'" do
end
it "sets the encoding to the encoding of the source String" do
- "abc".force_encoding(Encoding::EUC_JP) =~ /b/
+ "abc".dup.force_encoding(Encoding::EUC_JP) =~ /b/
$'.encoding.should equal(Encoding::EUC_JP)
end
it "sets an empty result to the encoding of the source String" do
- "abc".force_encoding(Encoding::ISO_8859_1) =~ /c/
+ "abc".dup.force_encoding(Encoding::ISO_8859_1) =~ /c/
$'.encoding.should equal(Encoding::ISO_8859_1)
end
end
@@ -187,7 +187,7 @@ describe "Predefined global $+" do
end
it "sets the encoding to the encoding of the source String" do
- "abc".force_encoding(Encoding::EUC_JP) =~ /(b)/
+ "abc".dup.force_encoding(Encoding::EUC_JP) =~ /(b)/
$+.encoding.should equal(Encoding::EUC_JP)
end
end
@@ -214,7 +214,7 @@ describe "Predefined globals $1..N" do
end
it "sets the encoding to the encoding of the source String" do
- "abc".force_encoding(Encoding::EUC_JP) =~ /(b)/
+ "abc".dup.force_encoding(Encoding::EUC_JP) =~ /(b)/
$1.encoding.should equal(Encoding::EUC_JP)
end
end
@@ -687,7 +687,7 @@ describe "Predefined global $," do
end
it "warns if assigned non-nil" do
- -> { $, = "_" }.should complain(/warning: `\$,' is deprecated/)
+ -> { $, = "_" }.should complain(/warning: [`']\$,' is deprecated/)
end
end
@@ -724,7 +724,7 @@ describe "Predefined global $;" do
end
it "warns if assigned non-nil" do
- -> { $; = "_" }.should complain(/warning: `\$;' is deprecated/)
+ -> { $; = "_" }.should complain(/warning: [`']\$;' is deprecated/)
end
end
@@ -1037,7 +1037,7 @@ describe "Global variable $0" do
it "is the path given as the main script and the same as __FILE__" do
script = "fixtures/dollar_zero.rb"
- Dir.chdir(File.dirname(__FILE__)) do
+ Dir.chdir(__dir__) do
ruby_exe(script).should == "#{script}\n#{script}\nOK"
end
end
@@ -1161,47 +1161,20 @@ end
describe "The predefined global constants" do
describe "TRUE" do
- ruby_version_is "3.0" do
- it "is no longer defined" do
- Object.const_defined?(:TRUE).should == false
- end
- end
-
- ruby_version_is ""..."3.0" do
- it "includes TRUE" do
- Object.const_defined?(:TRUE).should == true
- -> { TRUE }.should complain(/constant ::TRUE is deprecated/)
- end
+ it "is no longer defined" do
+ Object.const_defined?(:TRUE).should == false
end
end
describe "FALSE" do
- ruby_version_is "3.0" do
- it "is no longer defined" do
- Object.const_defined?(:FALSE).should == false
- end
- end
-
- ruby_version_is ""..."3.0" do
- it "includes FALSE" do
- Object.const_defined?(:FALSE).should == true
- -> { FALSE }.should complain(/constant ::FALSE is deprecated/)
- end
+ it "is no longer defined" do
+ Object.const_defined?(:FALSE).should == false
end
end
describe "NIL" do
- ruby_version_is "3.0" do
- it "is no longer defined" do
- Object.const_defined?(:NIL).should == false
- end
- end
-
- ruby_version_is ""..."3.0" do
- it "includes NIL" do
- Object.const_defined?(:NIL).should == true
- -> { NIL }.should complain(/constant ::NIL is deprecated/)
- end
+ it "is no longer defined" do
+ Object.const_defined?(:NIL).should == false
end
end
diff --git a/spec/ruby/language/proc_spec.rb b/spec/ruby/language/proc_spec.rb
index c5876fb2ed..cc69b7799c 100644
--- a/spec/ruby/language/proc_spec.rb
+++ b/spec/ruby/language/proc_spec.rb
@@ -235,18 +235,8 @@ describe "A Proc" do
@p = proc { |*a, **kw| [a, kw] }
end
- ruby_version_is ""..."3.0" do
- it 'autosplats keyword arguments and warns' do
- -> {
- @p.call([1, {a: 1}]).should == [[1], {a: 1}]
- }.should complain(/warning: Using the last argument as keyword parameters is deprecated; maybe \*\* should be added to the call/)
- end
- end
-
- ruby_version_is "3.0" do
- it 'does not autosplat keyword arguments' do
- @p.call([1, {a: 1}]).should == [[[1, {a: 1}]], {}]
- end
+ it 'does not autosplat keyword arguments' do
+ @p.call([1, {a: 1}]).should == [[[1, {a: 1}]], {}]
end
end
diff --git a/spec/ruby/language/regexp/character_classes_spec.rb b/spec/ruby/language/regexp/character_classes_spec.rb
index a86200ff34..98d431a817 100644
--- a/spec/ruby/language/regexp/character_classes_spec.rb
+++ b/spec/ruby/language/regexp/character_classes_spec.rb
@@ -614,10 +614,8 @@ describe "Regexp with character classes" do
"1".match(eval("/\P{N}/")).should be_nil
end
- ruby_bug "#17340", ''...'3.0' do
- it "raises a RegexpError for an unterminated unicode property" do
- -> { Regexp.new('\p{') }.should raise_error(RegexpError)
- end
+ it "raises a RegexpError for an unterminated unicode property" do
+ -> { Regexp.new('\p{') }.should raise_error(RegexpError)
end
it "supports \\X (unicode 9.0 with UTR #51 workarounds)" do
diff --git a/spec/ruby/language/regexp/encoding_spec.rb b/spec/ruby/language/regexp/encoding_spec.rb
index febc3fdb37..0571b2d3cf 100644
--- a/spec/ruby/language/regexp/encoding_spec.rb
+++ b/spec/ruby/language/regexp/encoding_spec.rb
@@ -4,18 +4,18 @@ require_relative '../fixtures/classes'
describe "Regexps with encoding modifiers" do
it "supports /e (EUC encoding)" do
- match = /./e.match("\303\251".force_encoding(Encoding::EUC_JP))
- match.to_a.should == ["\303\251".force_encoding(Encoding::EUC_JP)]
+ match = /./e.match("\303\251".dup.force_encoding(Encoding::EUC_JP))
+ match.to_a.should == ["\303\251".dup.force_encoding(Encoding::EUC_JP)]
end
it "supports /e (EUC encoding) with interpolation" do
- match = /#{/./}/e.match("\303\251".force_encoding(Encoding::EUC_JP))
- match.to_a.should == ["\303\251".force_encoding(Encoding::EUC_JP)]
+ match = /#{/./}/e.match("\303\251".dup.force_encoding(Encoding::EUC_JP))
+ match.to_a.should == ["\303\251".dup.force_encoding(Encoding::EUC_JP)]
end
it "supports /e (EUC encoding) with interpolation /o" do
- match = /#{/./}/e.match("\303\251".force_encoding(Encoding::EUC_JP))
- match.to_a.should == ["\303\251".force_encoding(Encoding::EUC_JP)]
+ match = /#{/./}/e.match("\303\251".dup.force_encoding(Encoding::EUC_JP))
+ match.to_a.should == ["\303\251".dup.force_encoding(Encoding::EUC_JP)]
end
it 'uses EUC-JP as /e encoding' do
@@ -39,7 +39,7 @@ describe "Regexps with encoding modifiers" do
end
it "warns when using /n with a match string with non-ASCII characters and an encoding other than ASCII-8BIT" do
- -> { /./n.match("\303\251".force_encoding('utf-8')) }.should complain(%r{historical binary regexp match /.../n against UTF-8 string})
+ -> { /./n.match("\303\251".dup.force_encoding('utf-8')) }.should complain(%r{historical binary regexp match /.../n against UTF-8 string})
end
it 'uses US-ASCII as /n encoding if all chars are 7-bit' do
@@ -63,18 +63,18 @@ describe "Regexps with encoding modifiers" do
end
it "supports /s (Windows_31J encoding)" do
- match = /./s.match("\303\251".force_encoding(Encoding::Windows_31J))
- match.to_a.should == ["\303".force_encoding(Encoding::Windows_31J)]
+ match = /./s.match("\303\251".dup.force_encoding(Encoding::Windows_31J))
+ match.to_a.should == ["\303".dup.force_encoding(Encoding::Windows_31J)]
end
it "supports /s (Windows_31J encoding) with interpolation" do
- match = /#{/./}/s.match("\303\251".force_encoding(Encoding::Windows_31J))
- match.to_a.should == ["\303".force_encoding(Encoding::Windows_31J)]
+ match = /#{/./}/s.match("\303\251".dup.force_encoding(Encoding::Windows_31J))
+ match.to_a.should == ["\303".dup.force_encoding(Encoding::Windows_31J)]
end
it "supports /s (Windows_31J encoding) with interpolation and /o" do
- match = /#{/./}/s.match("\303\251".force_encoding(Encoding::Windows_31J))
- match.to_a.should == ["\303".force_encoding(Encoding::Windows_31J)]
+ match = /#{/./}/s.match("\303\251".dup.force_encoding(Encoding::Windows_31J))
+ match.to_a.should == ["\303".dup.force_encoding(Encoding::Windows_31J)]
end
it 'uses Windows-31J as /s encoding' do
@@ -86,15 +86,15 @@ describe "Regexps with encoding modifiers" do
end
it "supports /u (UTF8 encoding)" do
- /./u.match("\303\251".force_encoding('utf-8')).to_a.should == ["\u{e9}"]
+ /./u.match("\303\251".dup.force_encoding('utf-8')).to_a.should == ["\u{e9}"]
end
it "supports /u (UTF8 encoding) with interpolation" do
- /#{/./}/u.match("\303\251".force_encoding('utf-8')).to_a.should == ["\u{e9}"]
+ /#{/./}/u.match("\303\251".dup.force_encoding('utf-8')).to_a.should == ["\u{e9}"]
end
it "supports /u (UTF8 encoding) with interpolation and /o" do
- /#{/./}/u.match("\303\251".force_encoding('utf-8')).to_a.should == ["\u{e9}"]
+ /#{/./}/u.match("\303\251".dup.force_encoding('utf-8')).to_a.should == ["\u{e9}"]
end
it 'uses UTF-8 as /u encoding' do
@@ -122,26 +122,26 @@ describe "Regexps with encoding modifiers" do
end
it "raises Encoding::CompatibilityError when the regexp has a fixed, non-ASCII-compatible encoding" do
- -> { Regexp.new("".force_encoding("UTF-16LE"), Regexp::FIXEDENCODING) =~ " ".encode("UTF-8") }.should raise_error(Encoding::CompatibilityError)
+ -> { Regexp.new("".dup.force_encoding("UTF-16LE"), Regexp::FIXEDENCODING) =~ " ".encode("UTF-8") }.should raise_error(Encoding::CompatibilityError)
end
it "raises Encoding::CompatibilityError when the regexp has a fixed encoding and the match string has non-ASCII characters" do
- -> { Regexp.new("".force_encoding("US-ASCII"), Regexp::FIXEDENCODING) =~ "\303\251".force_encoding('UTF-8') }.should raise_error(Encoding::CompatibilityError)
+ -> { Regexp.new("".dup.force_encoding("US-ASCII"), Regexp::FIXEDENCODING) =~ "\303\251".dup.force_encoding('UTF-8') }.should raise_error(Encoding::CompatibilityError)
end
it "raises ArgumentError when trying to match a broken String" do
- s = "\x80".force_encoding('UTF-8')
+ s = "\x80".dup.force_encoding('UTF-8')
-> { s =~ /./ }.should raise_error(ArgumentError, "invalid byte sequence in UTF-8")
end
it "computes the Regexp Encoding for each interpolated Regexp instance" do
make_regexp = -> str { /#{str}/ }
- r = make_regexp.call("été".force_encoding(Encoding::UTF_8))
+ r = make_regexp.call("été".dup.force_encoding(Encoding::UTF_8))
r.should.fixed_encoding?
r.encoding.should == Encoding::UTF_8
- r = make_regexp.call("abc".force_encoding(Encoding::UTF_8))
+ r = make_regexp.call("abc".dup.force_encoding(Encoding::UTF_8))
r.should_not.fixed_encoding?
r.encoding.should == Encoding::US_ASCII
end
diff --git a/spec/ruby/language/regexp/repetition_spec.rb b/spec/ruby/language/regexp/repetition_spec.rb
index 9a191d74e2..d76619688f 100644
--- a/spec/ruby/language/regexp/repetition_spec.rb
+++ b/spec/ruby/language/regexp/repetition_spec.rb
@@ -87,9 +87,7 @@ describe "Regexps with repetition" do
/a+?*/.match("a")[0].should == "a"
/(a+?)*/.match("a")[0].should == "a"
- ruby_bug '#17341', ''...'3.0' do
- /a+?*/.match("aa")[0].should == "aa"
- end
+ /a+?*/.match("aa")[0].should == "aa"
/(a+?)*/.match("aa")[0].should == "aa"
# a+?+ should not be reduced, it should be equivalent to (a+?)+
@@ -100,9 +98,7 @@ describe "Regexps with repetition" do
/a+?+/.match("a")[0].should == "a"
/(a+?)+/.match("a")[0].should == "a"
- ruby_bug '#17341', ''...'3.0' do
- /a+?+/.match("aa")[0].should == "aa"
- end
+ /a+?+/.match("aa")[0].should == "aa"
/(a+?)+/.match("aa")[0].should == "aa"
# both a**? and a+*? should be equivalent to (a+)??
diff --git a/spec/ruby/language/regexp_spec.rb b/spec/ruby/language/regexp_spec.rb
index 646fb89d41..89d0914807 100644
--- a/spec/ruby/language/regexp_spec.rb
+++ b/spec/ruby/language/regexp_spec.rb
@@ -18,10 +18,8 @@ describe "Literal Regexps" do
/Hello/.should be_kind_of(Regexp)
end
- ruby_version_is "3.0" do
- it "is frozen" do
- /Hello/.should.frozen?
- end
+ it "is frozen" do
+ /Hello/.should.frozen?
end
it "caches the Regexp object" do
@@ -114,7 +112,7 @@ describe "Literal Regexps" do
/foo.(?<=\d)/.match("fooA foo1").to_a.should == ["foo1"]
end
- ruby_bug "#13671", ""..."3.4" do # https://bugs.ruby-lang.org/issues/13671
+ ruby_bug "#13671", ""..."3.5" do # https://bugs.ruby-lang.org/issues/13671
it "handles a lookbehind with ss characters" do
r = Regexp.new("(?<!dss)", Regexp::IGNORECASE)
r.should =~ "✨"
diff --git a/spec/ruby/language/rescue_spec.rb b/spec/ruby/language/rescue_spec.rb
index b91b52fa0f..a3ee4807ac 100644
--- a/spec/ruby/language/rescue_spec.rb
+++ b/spec/ruby/language/rescue_spec.rb
@@ -61,6 +61,78 @@ describe "The rescue keyword" do
end
end
+ describe 'capturing in a local variable (that defines it)' do
+ it 'captures successfully in a method' do
+ ScratchPad.record []
+
+ def a
+ raise "message"
+ rescue => e
+ ScratchPad << e.message
+ end
+
+ a
+ ScratchPad.recorded.should == ["message"]
+ end
+
+ it 'captures successfully in a block' do
+ ScratchPad.record []
+
+ p = proc do
+ raise "message"
+ rescue => e
+ ScratchPad << e.message
+ end
+
+ p.call
+ ScratchPad.recorded.should == ["message"]
+ end
+
+ it 'captures successfully in a class' do
+ ScratchPad.record []
+
+ class RescueSpecs::C
+ raise "message"
+ rescue => e
+ ScratchPad << e.message
+ end
+
+ ScratchPad.recorded.should == ["message"]
+ end
+
+ it 'captures successfully in a module' do
+ ScratchPad.record []
+
+ module RescueSpecs::M
+ raise "message"
+ rescue => e
+ ScratchPad << e.message
+ end
+
+ ScratchPad.recorded.should == ["message"]
+ end
+
+ it 'captures sucpcessfully in a singleton class' do
+ ScratchPad.record []
+
+ class << Object.new
+ raise "message"
+ rescue => e
+ ScratchPad << e.message
+ end
+
+ ScratchPad.recorded.should == ["message"]
+ end
+
+ it 'captures successfully at the top-level' do
+ ScratchPad.record []
+
+ require_relative 'fixtures/rescue/top_level'
+
+ ScratchPad.recorded.should == ["message"]
+ end
+ end
+
it "returns value from `rescue` if an exception was raised" do
begin
raise
@@ -191,7 +263,7 @@ describe "The rescue keyword" do
rescue ArgumentError
end
rescue StandardError => e
- e.backtrace.first.should include ":in `raise_standard_error'"
+ e.backtrace.first.should =~ /:in [`'](?:RescueSpecs\.)?raise_standard_error'/
else
fail("exception wasn't handled by the correct rescue block")
end
@@ -481,6 +553,23 @@ describe "The rescue keyword" do
eval('1.+((1 rescue 1))').should == 2
end
+ ruby_version_is "3.4" do
+ it "does not introduce extra backtrace entries" do
+ def foo
+ begin
+ raise "oops"
+ rescue
+ return caller(0, 2)
+ end
+ end
+ line = __LINE__
+ foo.should == [
+ "#{__FILE__}:#{line-3}:in 'foo'",
+ "#{__FILE__}:#{line+1}:in 'block (3 levels) in <top (required)>'"
+ ]
+ end
+ end
+
describe "inline form" do
it "can be inlined" do
a = 1/0 rescue 1
diff --git a/spec/ruby/language/return_spec.rb b/spec/ruby/language/return_spec.rb
index 94c15b697e..a62ed1242d 100644
--- a/spec/ruby/language/return_spec.rb
+++ b/spec/ruby/language/return_spec.rb
@@ -435,6 +435,21 @@ describe "The return keyword" do
end
end
+ describe "within BEGIN" do
+ it "is allowed" do
+ File.write(@filename, <<-END_OF_CODE)
+ BEGIN {
+ ScratchPad << "before call"
+ return
+ ScratchPad << "after call"
+ }
+ END_OF_CODE
+
+ load @filename
+ ScratchPad.recorded.should == ["before call"]
+ end
+ end
+
describe "file loading" do
it "stops file loading and execution" do
File.write(@filename, <<-END_OF_CODE)
diff --git a/spec/ruby/language/safe_navigator_spec.rb b/spec/ruby/language/safe_navigator_spec.rb
index c3aecff2dd..b1e28c3963 100644
--- a/spec/ruby/language/safe_navigator_spec.rb
+++ b/spec/ruby/language/safe_navigator_spec.rb
@@ -7,43 +7,43 @@ describe "Safe navigator" do
context "when context is nil" do
it "always returns nil" do
- eval("nil&.unknown").should == nil
- eval("[][10]&.unknown").should == nil
+ nil&.unknown.should == nil
+ [][10]&.unknown.should == nil
end
it "can be chained" do
- eval("nil&.one&.two&.three").should == nil
+ nil&.one&.two&.three.should == nil
end
it "doesn't evaluate arguments" do
obj = Object.new
obj.should_not_receive(:m)
- eval("nil&.unknown(obj.m) { obj.m }")
+ nil&.unknown(obj.m) { obj.m }
end
end
context "when context is false" do
it "calls the method" do
- eval("false&.to_s").should == "false"
+ false&.to_s.should == "false"
- -> { eval("false&.unknown") }.should raise_error(NoMethodError)
+ -> { false&.unknown }.should raise_error(NoMethodError)
end
end
context "when context is truthy" do
it "calls the method" do
- eval("1&.to_s").should == "1"
+ 1&.to_s.should == "1"
- -> { eval("1&.unknown") }.should raise_error(NoMethodError)
+ -> { 1&.unknown }.should raise_error(NoMethodError)
end
end
it "takes a list of arguments" do
- eval("[1,2,3]&.first(2)").should == [1,2]
+ [1,2,3]&.first(2).should == [1,2]
end
it "takes a block" do
- eval("[1,2]&.map { |i| i * 2 }").should == [2, 4]
+ [1,2]&.map { |i| i * 2 }.should == [2, 4]
end
it "allows assignment methods" do
@@ -56,29 +56,77 @@ describe "Safe navigator" do
end
obj = klass.new
- eval("obj&.foo = 3").should == 3
+ (obj&.foo = 3).should == 3
obj.foo.should == 3
obj = nil
- eval("obj&.foo = 3").should == nil
+ (obj&.foo = 3).should == nil
end
it "allows assignment operators" do
klass = Class.new do
- attr_accessor :m
+ attr_reader :m
def initialize
@m = 0
end
+
+ def m=(v)
+ @m = v
+ 42
+ end
end
obj = klass.new
- eval("obj&.m += 3")
+ obj&.m += 3
obj.m.should == 3
obj = nil
- eval("obj&.m += 3").should == nil
+ (obj&.m += 3).should == nil
+ end
+
+ it "allows ||= operator" do
+ klass = Class.new do
+ attr_reader :m
+
+ def initialize
+ @m = false
+ end
+
+ def m=(v)
+ @m = v
+ 42
+ end
+ end
+
+ obj = klass.new
+
+ (obj&.m ||= true).should == true
+ obj.m.should == true
+
+ obj = nil
+ (obj&.m ||= true).should == nil
+ obj.should == nil
+ end
+
+ it "allows &&= operator" do
+ klass = Class.new do
+ attr_accessor :m
+
+ def initialize
+ @m = true
+ end
+ end
+
+ obj = klass.new
+
+ (obj&.m &&= false).should == false
+ obj.m.should == false
+
+ obj = nil
+ (obj&.m &&= false).should == nil
+ obj.should == nil
end
it "does not call the operator method lazily with an assignment operator" do
@@ -91,7 +139,7 @@ describe "Safe navigator" do
obj = klass.new
-> {
- eval("obj&.foo += 3")
+ obj&.foo += 3
}.should raise_error(NoMethodError) { |e|
e.name.should == :+
}
diff --git a/spec/ruby/language/safe_spec.rb b/spec/ruby/language/safe_spec.rb
index ee5c1b3ccc..03ae96148e 100644
--- a/spec/ruby/language/safe_spec.rb
+++ b/spec/ruby/language/safe_spec.rb
@@ -1,27 +1,11 @@
require_relative '../spec_helper'
describe "The $SAFE variable" do
- ruby_version_is ""..."3.0" do
- it "warn when access" do
- -> {
- $SAFE
- }.should complain(/\$SAFE will become a normal global variable in Ruby 3.0/)
- end
-
- it "warn when set" do
- -> {
- $SAFE = 1
- }.should complain(/\$SAFE will become a normal global variable in Ruby 3.0/)
- end
- end
-
- ruby_version_is "3.0" do
- it "$SAFE is a regular global variable" do
- $SAFE.should == nil
- $SAFE = 42
- $SAFE.should == 42
- ensure
- $SAFE = nil
- end
+ it "$SAFE is a regular global variable" do
+ $SAFE.should == nil
+ $SAFE = 42
+ $SAFE.should == 42
+ ensure
+ $SAFE = nil
end
end
diff --git a/spec/ruby/language/send_spec.rb b/spec/ruby/language/send_spec.rb
index 5999079d58..aaccdf0998 100644
--- a/spec/ruby/language/send_spec.rb
+++ b/spec/ruby/language/send_spec.rb
@@ -43,7 +43,7 @@ describe "Invoking a method" do
end
describe "with optional arguments" do
- it "uses the optional argument if none is is passed" do
+ it "uses the optional argument if none is passed" do
specs.fooM0O1.should == [1]
end
@@ -411,36 +411,18 @@ describe "Invoking a method" do
specs.rest_len(0,*a,4,*5,6,7,*c,-1).should == 11
end
- ruby_version_is ""..."3.0" do
- it "expands the Array elements from the splat after executing the arguments and block if no other arguments follow the splat" do
- def self.m(*args, &block)
- [args, block]
- end
-
- args = [1, nil]
- m(*args, &args.pop).should == [[1], nil]
-
- args = [1, nil]
- order = []
- m(*(order << :args; args), &(order << :block; args.pop)).should == [[1], nil]
- order.should == [:args, :block]
+ it "expands the Array elements from the splat before applying block argument operations" do
+ def self.m(*args, &block)
+ [args, block]
end
- end
- ruby_version_is "3.0" do
- it "expands the Array elements from the splat before applying block argument operations" do
- def self.m(*args, &block)
- [args, block]
- end
-
- args = [1, nil]
- m(*args, &args.pop).should == [[1, nil], nil]
+ args = [1, nil]
+ m(*args, &args.pop).should == [[1, nil], nil]
- args = [1, nil]
- order = []
- m(*(order << :args; args), &(order << :block; args.pop)).should == [[1, nil], nil]
- order.should == [:args, :block]
- end
+ args = [1, nil]
+ order = []
+ m(*(order << :args; args), &(order << :block; args.pop)).should == [[1, nil], nil]
+ order.should == [:args, :block]
end
it "evaluates the splatted arguments before the block if there are other arguments after the splat" do
diff --git a/spec/ruby/language/singleton_class_spec.rb b/spec/ruby/language/singleton_class_spec.rb
index 7512f0eb39..45e1f7f3ad 100644
--- a/spec/ruby/language/singleton_class_spec.rb
+++ b/spec/ruby/language/singleton_class_spec.rb
@@ -70,7 +70,7 @@ describe "A singleton class" do
end
it "has class String as the superclass of a String instance" do
- "blah".singleton_class.superclass.should == String
+ "blah".dup.singleton_class.superclass.should == String
end
it "doesn't have singleton class" do
@@ -307,4 +307,11 @@ describe "Frozen properties" do
o.freeze
klass.frozen?.should == true
end
+
+ it "will be unfrozen if the frozen object is cloned with freeze set to false" do
+ o = Object.new
+ o.freeze
+ o2 = o.clone(freeze: false)
+ o2.singleton_class.frozen?.should == false
+ end
end
diff --git a/spec/ruby/language/string_spec.rb b/spec/ruby/language/string_spec.rb
index 02e3488a1f..083a7f5db5 100644
--- a/spec/ruby/language/string_spec.rb
+++ b/spec/ruby/language/string_spec.rb
@@ -231,8 +231,16 @@ describe "Ruby String literals" do
ruby_exe(fixture(__FILE__, "freeze_magic_comment_across_files.rb")).chomp.should == "true"
end
- it "produce different objects for literals with the same content in different files if the other file doesn't have the comment" do
- ruby_exe(fixture(__FILE__, "freeze_magic_comment_across_files_no_comment.rb")).chomp.should == "true"
+ guard -> { !(eval("'test'").frozen? && "test".equal?("test")) } do
+ it "produces different objects for literals with the same content in different files if the other file doesn't have the comment and String literals aren't frozen by default" do
+ ruby_exe(fixture(__FILE__, "freeze_magic_comment_across_files_no_comment.rb")).chomp.should == "true"
+ end
+ end
+
+ guard -> { eval("'test'").frozen? && "test".equal?("test") } do
+ it "produces the same objects for literals with the same content in different files if the other file doesn't have the comment and String literals are frozen by default" do
+ ruby_exe(fixture(__FILE__, "freeze_magic_comment_across_files_no_comment.rb")).chomp.should == "false"
+ end
end
it "produce different objects for literals with the same content in different files if they have different encodings" do
@@ -251,12 +259,12 @@ describe "Ruby String interpolation" do
it "returns a string with the source encoding by default" do
"a#{"b"}c".encoding.should == Encoding::BINARY
- eval('"a#{"b"}c"'.force_encoding("us-ascii")).encoding.should == Encoding::US_ASCII
+ eval('"a#{"b"}c"'.dup.force_encoding("us-ascii")).encoding.should == Encoding::US_ASCII
eval("# coding: US-ASCII \n 'a#{"b"}c'").encoding.should == Encoding::US_ASCII
end
it "returns a string with the source encoding, even if the components have another encoding" do
- a = "abc".force_encoding("euc-jp")
+ a = "abc".dup.force_encoding("euc-jp")
"#{a}".encoding.should == Encoding::BINARY
b = "abc".encode("utf-8")
@@ -265,7 +273,7 @@ describe "Ruby String interpolation" do
it "raises an Encoding::CompatibilityError if the Encodings are not compatible" do
a = "\u3042"
- b = "\xff".force_encoding "binary"
+ b = "\xff".dup.force_encoding "binary"
-> { "#{a} #{b}" }.should raise_error(Encoding::CompatibilityError)
end
@@ -277,23 +285,11 @@ describe "Ruby String interpolation" do
eval(code).should_not.frozen?
end
- ruby_version_is "3.0" do
- it "creates a non-frozen String when # frozen-string-literal: true is used" do
- code = <<~'RUBY'
- # frozen-string-literal: true
- "a#{6*7}c"
- RUBY
- eval(code).should_not.frozen?
- end
- end
-
- ruby_version_is ""..."3.0" do
- it "creates a frozen String when # frozen-string-literal: true is used" do
- code = <<~'RUBY'
- # frozen-string-literal: true
- "a#{6*7}c"
- RUBY
- eval(code).should.frozen?
- end
+ it "creates a non-frozen String when # frozen-string-literal: true is used" do
+ code = <<~'RUBY'
+ # frozen-string-literal: true
+ "a#{6*7}c"
+ RUBY
+ eval(code).should_not.frozen?
end
end
diff --git a/spec/ruby/language/super_spec.rb b/spec/ruby/language/super_spec.rb
index 1fd7acc727..a98b3b3091 100644
--- a/spec/ruby/language/super_spec.rb
+++ b/spec/ruby/language/super_spec.rb
@@ -203,6 +203,25 @@ describe "The super keyword" do
-> { klass.new.a(:a_called) }.should raise_error(RuntimeError)
end
+ it "is able to navigate to super, when a method is defined dynamically on the singleton class" do
+ foo_class = Class.new do
+ def bar
+ "bar"
+ end
+ end
+
+ mixin_module = Module.new do
+ def bar
+ "super_" + super
+ end
+ end
+
+ foo = foo_class.new
+ foo.singleton_class.define_method(:bar, mixin_module.instance_method(:bar))
+
+ foo.bar.should == "super_bar"
+ end
+
# Rubinius ticket github#157
it "calls method_missing when a superclass method is not found" do
SuperSpecs::MM_B.new.is_a?(Hash).should == false
@@ -316,6 +335,13 @@ describe "The super keyword" do
it "without explicit arguments that are '_'" do
SuperSpecs::ZSuperWithUnderscores::B.new.m(1, 2).should == [1, 2]
+ SuperSpecs::ZSuperWithUnderscores::B.new.m3(1, 2, 3).should == [1, 2, 3]
+ SuperSpecs::ZSuperWithUnderscores::B.new.m4(1, 2, 3, 4).should == [1, 2, 3, 4]
+ SuperSpecs::ZSuperWithUnderscores::B.new.m_default(1).should == [1]
+ SuperSpecs::ZSuperWithUnderscores::B.new.m_default.should == [0]
+ SuperSpecs::ZSuperWithUnderscores::B.new.m_pre_default_rest_post(1, 2, 3, 4, 5, 6, 7).should == [1, 2, 3, 4, 5, 6, 7]
+ SuperSpecs::ZSuperWithUnderscores::B.new.m_rest(1, 2).should == [1, 2]
+ SuperSpecs::ZSuperWithUnderscores::B.new.m_kwrest(a: 1).should == {a: 1}
end
it "without explicit arguments that are '_' including any modifications" do
diff --git a/spec/ruby/language/symbol_spec.rb b/spec/ruby/language/symbol_spec.rb
index 7c1898efc2..0801d3223e 100644
--- a/spec/ruby/language/symbol_spec.rb
+++ b/spec/ruby/language/symbol_spec.rb
@@ -96,11 +96,13 @@ describe "A Symbol literal" do
%I{a b #{"c"}}.should == [:a, :b, :c]
end
- it "raises an EncodingError at parse time when Symbol with invalid bytes" do
- ScratchPad.record []
- -> {
- eval 'ScratchPad << 1; :"\xC3"'
- }.should raise_error(EncodingError, 'invalid symbol in encoding UTF-8 :"\xC3"')
- ScratchPad.recorded.should == []
+ ruby_bug "#20280", ""..."3.4" do
+ it "raises an SyntaxError at parse time when Symbol with invalid bytes" do
+ ScratchPad.record []
+ -> {
+ eval 'ScratchPad << 1; :"\xC3"'
+ }.should raise_error(SyntaxError, /invalid symbol/)
+ ScratchPad.recorded.should == []
+ end
end
end
diff --git a/spec/ruby/language/undef_spec.rb b/spec/ruby/language/undef_spec.rb
index 4e473b803f..29dba4afb4 100644
--- a/spec/ruby/language/undef_spec.rb
+++ b/spec/ruby/language/undef_spec.rb
@@ -38,12 +38,19 @@ describe "The undef keyword" do
-> { @obj.meth(5) }.should raise_error(NoMethodError)
end
- it "with a interpolated symbol" do
+ it "with an interpolated symbol" do
@undef_class.class_eval do
undef :"#{'meth'}"
end
-> { @obj.meth(5) }.should raise_error(NoMethodError)
end
+
+ it "with an interpolated symbol when interpolated expression is not a String literal" do
+ @undef_class.class_eval do
+ undef :"#{'meth'.to_sym}"
+ end
+ -> { @obj.meth(5) }.should raise_error(NoMethodError)
+ end
end
it "allows undefining multiple methods at a time" do
diff --git a/spec/ruby/language/variables_spec.rb b/spec/ruby/language/variables_spec.rb
index cd862727ac..01be61a9dc 100644
--- a/spec/ruby/language/variables_spec.rb
+++ b/spec/ruby/language/variables_spec.rb
@@ -367,8 +367,13 @@ describe "Multiple assignment" do
it "assigns indexed elements" do
a = []
- a[1], a[2] = 1
- a.should == [nil, 1, nil]
+ a[1], a[2] = 1, 2
+ a.should == [nil, 1, 2]
+
+ # with splatted argument
+ a = []
+ a[*[1]], a[*[2]] = 1, 2
+ a.should == [nil, 1, 2]
end
it "assigns constants" do
@@ -904,22 +909,11 @@ end
describe "Instance variables" do
context "when instance variable is uninitialized" do
- ruby_version_is ""..."3.0" do
- it "warns about accessing uninitialized instance variable" do
- obj = Object.new
- def obj.foobar; a = @a; end
-
- -> { obj.foobar }.should complain(/warning: instance variable @a not initialized/, verbose: true)
- end
- end
-
- ruby_version_is "3.0" do
- it "doesn't warn about accessing uninitialized instance variable" do
- obj = Object.new
- def obj.foobar; a = @a; end
+ it "doesn't warn about accessing uninitialized instance variable" do
+ obj = Object.new
+ def obj.foobar; a = @a; end
- -> { obj.foobar }.should_not complain(verbose: true)
- end
+ -> { obj.foobar }.should_not complain(verbose: true)
end
it "doesn't warn at lazy initialization" do
@@ -936,7 +930,7 @@ describe "Instance variables" do
obj = Object.new
def obj.foobar; a = $specs_uninitialized_global_variable; end
- -> { obj.foobar }.should complain(/warning: global variable `\$specs_uninitialized_global_variable' not initialized/, verbose: true)
+ -> { obj.foobar }.should complain(/warning: global variable [`']\$specs_uninitialized_global_variable' not initialized/, verbose: true)
end
it "doesn't warn at lazy initialization" do
diff --git a/spec/ruby/language/yield_spec.rb b/spec/ruby/language/yield_spec.rb
index 05d713af70..5283517636 100644
--- a/spec/ruby/language/yield_spec.rb
+++ b/spec/ruby/language/yield_spec.rb
@@ -186,31 +186,14 @@ describe "The yield call" do
end
describe "Using yield in a singleton class literal" do
- ruby_version_is ""..."3.0" do
- it 'emits a deprecation warning' do
- code = <<~RUBY
- def m
- class << Object.new
- yield
- end
- end
- m { :ok }
- RUBY
-
- -> { eval(code) }.should complain(/warning: `yield' in class syntax will not be supported from Ruby 3.0/)
- end
- end
-
- ruby_version_is "3.0" do
- it 'raises a SyntaxError' do
- code = <<~RUBY
- class << Object.new
- yield
- end
- RUBY
+ it 'raises a SyntaxError' do
+ code = <<~RUBY
+ class << Object.new
+ yield
+ end
+ RUBY
- -> { eval(code) }.should raise_error(SyntaxError, /Invalid yield/)
- end
+ -> { eval(code) }.should raise_error(SyntaxError, /Invalid yield/)
end
end
diff --git a/spec/ruby/library/English/English_spec.rb b/spec/ruby/library/English/English_spec.rb
index 480602d5e5..4d615d1e25 100644
--- a/spec/ruby/library/English/English_spec.rb
+++ b/spec/ruby/library/English/English_spec.rb
@@ -130,13 +130,15 @@ describe "English" do
$LAST_MATCH_INFO.should == $~
end
- it "aliases $IGNORECASE to $=" do
- $VERBOSE, verbose = nil, $VERBOSE
- begin
- $IGNORECASE.should_not be_nil
- $IGNORECASE.should == $=
- ensure
- $VERBOSE = verbose
+ ruby_version_is ""..."3.3" do
+ it "aliases $IGNORECASE to $=" do
+ $VERBOSE, verbose = nil, $VERBOSE
+ begin
+ $IGNORECASE.should_not be_nil
+ $IGNORECASE.should == $=
+ ensure
+ $VERBOSE = verbose
+ end
end
end
diff --git a/spec/ruby/library/bigdecimal/add_spec.rb b/spec/ruby/library/bigdecimal/add_spec.rb
index 169a071aa6..542713011d 100644
--- a/spec/ruby/library/bigdecimal/add_spec.rb
+++ b/spec/ruby/library/bigdecimal/add_spec.rb
@@ -24,7 +24,7 @@ describe "BigDecimal#add" do
end
it "returns a + b with given precision" do
- # documentation states, that precision ist optional, but it ain't,
+ # documentation states that precision is optional, but it ain't,
@two.add(@one, 1).should == @three
@one .add(@two, 1).should == @three
@one.add(@one_minus, 1).should == @zero
diff --git a/spec/ruby/library/bigdecimal/core_spec.rb b/spec/ruby/library/bigdecimal/core_spec.rb
new file mode 100644
index 0000000000..acee4dcf56
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/core_spec.rb
@@ -0,0 +1,59 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "Core extension by bigdecimal" do
+ context "Integer#coerce" do
+ it "produces Floats" do
+ x, y = 3.coerce(BigDecimal("3.4"))
+ x.class.should == Float
+ x.should == 3.4
+ y.class.should == Float
+ y.should == 3.0
+ end
+ end
+
+ describe "Time.at passed BigDecimal" do
+ it "doesn't round input value" do
+ Time.at(BigDecimal('1.1')).to_f.should == 1.1
+ end
+ end
+
+ describe "BigDecimal#log" do
+ it "handles high-precision Rational arguments" do
+ result = BigDecimal('0.22314354220170971436137296411949880462556361100856391620766259404746040597133837784E0')
+ r = Rational(1_234_567_890, 987_654_321)
+ BigMath.log(r, 50).should == result
+ end
+ end
+
+ describe "Rational#coerce" do
+ it "returns the passed argument, self as Float, when given a Float" do
+ result = Rational(3, 4).coerce(1.0)
+ result.should == [1.0, 0.75]
+ result.first.is_a?(Float).should be_true
+ result.last.is_a?(Float).should be_true
+ end
+
+ it "returns the passed argument, self as Rational, when given an Integer" do
+ result = Rational(3, 4).coerce(10)
+ result.should == [Rational(10, 1), Rational(3, 4)]
+ result.first.is_a?(Rational).should be_true
+ result.last.is_a?(Rational).should be_true
+ end
+
+ it "coerces to Rational, when given a Complex" do
+ Rational(3, 4).coerce(Complex(5)).should == [Rational(5, 1), Rational(3, 4)]
+ Rational(12, 4).coerce(Complex(5, 1)).should == [Complex(5, 1), Complex(3)]
+ end
+
+ it "returns [argument, self] when given a Rational" do
+ Rational(3, 7).coerce(Rational(9, 2)).should == [Rational(9, 2), Rational(3, 7)]
+ end
+
+ it "raises an error when passed a BigDecimal" do
+ -> {
+ Rational(500, 3).coerce(BigDecimal('166.666666666'))
+ }.should raise_error(TypeError, /BigDecimal can't be coerced into Rational/)
+ end
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/remainder_spec.rb b/spec/ruby/library/bigdecimal/remainder_spec.rb
index 35e131a0dc..bac5f37ba9 100644
--- a/spec/ruby/library/bigdecimal/remainder_spec.rb
+++ b/spec/ruby/library/bigdecimal/remainder_spec.rb
@@ -54,7 +54,7 @@ describe "BigDecimal#remainder" do
@nan.remainder(@infinity).should.nan?
end
- version_is BigDecimal::VERSION, ""..."3.1.4" do
+ version_is BigDecimal::VERSION, ""..."3.1.4" do #ruby_version_is ""..."3.3" do
it "returns NaN if Infinity is involved" do
@infinity.remainder(@infinity).should.nan?
@infinity.remainder(@one).should.nan?
diff --git a/spec/ruby/library/bigdecimal/round_spec.rb b/spec/ruby/library/bigdecimal/round_spec.rb
index bfc6dbc763..fba52df65d 100644
--- a/spec/ruby/library/bigdecimal/round_spec.rb
+++ b/spec/ruby/library/bigdecimal/round_spec.rb
@@ -228,13 +228,13 @@ describe "BigDecimal#round" do
-> { BigDecimal('-Infinity').round(2) }.should_not raise_error(FloatDomainError)
end
- ruby_version_is ''...'3.2' do
+ version_is BigDecimal::VERSION, ''...'3.1.3' do #ruby_version_is ''...'3.2' do
it 'raise for a non-existent round mode' do
-> { @p1_50.round(0, :nonsense) }.should raise_error(ArgumentError, "invalid rounding mode")
end
end
- ruby_version_is '3.2' do
+ version_is BigDecimal::VERSION, '3.1.3' do #ruby_version_is '3.2' do
it 'raise for a non-existent round mode' do
-> { @p1_50.round(0, :nonsense) }.should raise_error(ArgumentError, "invalid rounding mode (nonsense)")
end
diff --git a/spec/ruby/library/bigdecimal/shared/to_int.rb b/spec/ruby/library/bigdecimal/shared/to_int.rb
index 0f16251612..44b6a3c7b2 100644
--- a/spec/ruby/library/bigdecimal/shared/to_int.rb
+++ b/spec/ruby/library/bigdecimal/shared/to_int.rb
@@ -1,6 +1,6 @@
require 'bigdecimal'
-describe :bigdecimal_to_int , shared: true do
+describe :bigdecimal_to_int, shared: true do
it "raises FloatDomainError if BigDecimal is infinity or NaN" do
-> { BigDecimal("Infinity").send(@method) }.should raise_error(FloatDomainError)
-> { BigDecimal("NaN").send(@method) }.should raise_error(FloatDomainError)
diff --git a/spec/ruby/library/bigdecimal/sqrt_spec.rb b/spec/ruby/library/bigdecimal/sqrt_spec.rb
index d149003b9f..8fd1ec0f39 100644
--- a/spec/ruby/library/bigdecimal/sqrt_spec.rb
+++ b/spec/ruby/library/bigdecimal/sqrt_spec.rb
@@ -36,8 +36,10 @@ describe "BigDecimal#sqrt" do
BigDecimal('121').sqrt(5).should be_close(11, 0.00001)
end
- it "returns square root of 0.9E-99999 with desired precision" do
- @frac_2.sqrt(1).to_s.should =~ /\A0\.3E-49999\z/i
+ platform_is_not wordsize: 32 do # fails on i686
+ it "returns square root of 0.9E-99999 with desired precision" do
+ @frac_2.sqrt(1).to_s.should =~ /\A0\.3E-49999\z/i
+ end
end
it "raises ArgumentError when no argument is given" do
diff --git a/spec/ruby/library/bigdecimal/to_s_spec.rb b/spec/ruby/library/bigdecimal/to_s_spec.rb
index 4f1054d38e..ba9f960eb3 100644
--- a/spec/ruby/library/bigdecimal/to_s_spec.rb
+++ b/spec/ruby/library/bigdecimal/to_s_spec.rb
@@ -39,20 +39,25 @@ describe "BigDecimal#to_s" do
@bigneg.to_s("+").should_not =~ /^\+.*/
end
- it "inserts a space every n chars, if integer n is supplied" do
+ it "inserts a space every n chars to fraction part, if integer n is supplied" do
re =\
- /\A0\.314 159 265 358 979 323 846 264 338 327 950 288 419 716 939 937E1\z/i
+ /\A0\.314 159 265 358 979 323 846 264 338 327 950 288 419 716 939 937E1\z/i
@bigdec.to_s(3).should =~ re
str1 = '-123.45678 90123 45678 9'
BigDecimal("-123.45678901234567890").to_s('5F').should == str1
- BigDecimal('1000010').to_s('5F').should == "10000 10.0"
# trailing zeroes removed
BigDecimal("1.00000000000").to_s('1F').should == "1.0"
# 0 is treated as no spaces
BigDecimal("1.2345").to_s('0F').should == "1.2345"
end
+ version_is BigDecimal::VERSION, "3.1.5" do #ruby_version_is '3.3' do
+ it "inserts a space every n chars to integer part, if integer n is supplied" do
+ BigDecimal('1000010').to_s('5F').should == "10 00010.0"
+ end
+ end
+
it "can return a leading space for values > 0" do
@bigdec.to_s(" F").should =~ /\ .*/
@bigneg.to_s(" F").should_not =~ /\ .*/
@@ -83,15 +88,13 @@ describe "BigDecimal#to_s" do
end
end
- ruby_version_is "3.0" do
- it "returns a String in US-ASCII encoding when Encoding.default_internal is nil" do
- Encoding.default_internal = nil
- BigDecimal('1.23').to_s.encoding.should equal(Encoding::US_ASCII)
- end
+ it "returns a String in US-ASCII encoding when Encoding.default_internal is nil" do
+ Encoding.default_internal = nil
+ BigDecimal('1.23').to_s.encoding.should equal(Encoding::US_ASCII)
+ end
- it "returns a String in US-ASCII encoding when Encoding.default_internal is not nil" do
- Encoding.default_internal = Encoding::IBM437
- BigDecimal('1.23').to_s.encoding.should equal(Encoding::US_ASCII)
- end
+ it "returns a String in US-ASCII encoding when Encoding.default_internal is not nil" do
+ Encoding.default_internal = Encoding::IBM437
+ BigDecimal('1.23').to_s.encoding.should equal(Encoding::US_ASCII)
end
end
diff --git a/spec/ruby/library/bigmath/log_spec.rb b/spec/ruby/library/bigmath/log_spec.rb
deleted file mode 100644
index 2ffbf8b6b9..0000000000
--- a/spec/ruby/library/bigmath/log_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require_relative '../../spec_helper'
-require 'bigdecimal'
-
-describe "BigDecimal#log" do
- it "handles high-precision Rational arguments" do
- result = BigDecimal('0.22314354220170971436137296411949880462556361100856391620766259404746040597133837784E0')
- r = Rational(1_234_567_890, 987_654_321)
- BigMath.log(r, 50).should == result
- end
-end
diff --git a/spec/ruby/library/cgi/escapeURIComponent_spec.rb b/spec/ruby/library/cgi/escapeURIComponent_spec.rb
new file mode 100644
index 0000000000..f05795a2f5
--- /dev/null
+++ b/spec/ruby/library/cgi/escapeURIComponent_spec.rb
@@ -0,0 +1,57 @@
+require_relative '../../spec_helper'
+require 'cgi'
+
+ruby_version_is "3.2" do
+ describe "CGI.escapeURIComponent" do
+ it "escapes whitespace" do
+ string = "&<>\" \xE3\x82\x86\xE3\x82\x93\xE3\x82\x86\xE3\x82\x93"
+ CGI.escapeURIComponent(string).should == '%26%3C%3E%22%20%E3%82%86%E3%82%93%E3%82%86%E3%82%93'
+ end
+
+ it "does not escape with unreserved characters" do
+ string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"
+ CGI.escapeURIComponent(string).should == "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"
+ end
+
+ it "supports String with invalid encoding" do
+ string = "\xC0\<\<".dup.force_encoding("UTF-8")
+ CGI.escapeURIComponent(string).should == "%C0%3C%3C"
+ end
+
+ it "processes String bytes one by one, not characters" do
+ CGI.escapeURIComponent("β").should == "%CE%B2" # "β" bytes representation is CE B2
+ end
+
+ it "raises a TypeError with nil" do
+ -> {
+ CGI.escapeURIComponent(nil)
+ }.should raise_error(TypeError, 'no implicit conversion of nil into String')
+ end
+
+ it "encodes empty string" do
+ CGI.escapeURIComponent("").should == ""
+ end
+
+ it "encodes single whitespace" do
+ CGI.escapeURIComponent(" ").should == "%20"
+ end
+
+ it "encodes double whitespace" do
+ CGI.escapeURIComponent(" ").should == "%20%20"
+ end
+
+ it "preserves encoding" do
+ string = "whatever".encode("ASCII-8BIT")
+ CGI.escapeURIComponent(string).encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it "uses implicit type conversion to String" do
+ object = Object.new
+ def object.to_str
+ "a b"
+ end
+
+ CGI.escapeURIComponent(object).should == "a%20b"
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/initialize_spec.rb b/spec/ruby/library/cgi/initialize_spec.rb
index f794f157f0..61bc971d49 100644
--- a/spec/ruby/library/cgi/initialize_spec.rb
+++ b/spec/ruby/library/cgi/initialize_spec.rb
@@ -29,8 +29,8 @@ describe "CGI#initialize when passed no arguments" do
it "does not extend self with any of the other HTML modules" do
@cgi.send(:initialize)
- @cgi.should_not be_kind_of(CGI::Html3)
@cgi.should_not be_kind_of(CGI::HtmlExtension)
+ @cgi.should_not be_kind_of(CGI::Html3)
@cgi.should_not be_kind_of(CGI::Html4)
@cgi.should_not be_kind_of(CGI::Html4Tr)
@cgi.should_not be_kind_of(CGI::Html4Fr)
diff --git a/spec/ruby/library/coverage/result_spec.rb b/spec/ruby/library/coverage/result_spec.rb
index 4bcce00cd7..33276778e8 100644
--- a/spec/ruby/library/coverage/result_spec.rb
+++ b/spec/ruby/library/coverage/result_spec.rb
@@ -8,10 +8,16 @@ describe 'Coverage.result' do
@eval_code_file = fixture __FILE__, 'eval_code.rb'
end
+ before :each do
+ Coverage.running?.should == false
+ end
+
after :each do
$LOADED_FEATURES.delete(@class_file)
$LOADED_FEATURES.delete(@config_file)
$LOADED_FEATURES.delete(@eval_code_file)
+
+ Coverage.result if Coverage.running?
end
it 'gives the covered files as a hash with arrays of count or nil' do
@@ -26,6 +32,41 @@ describe 'Coverage.result' do
}
end
+ ruby_version_is "3.2" do
+ it 'returns results for each mode separately when enabled :all modes' do
+ Coverage.start(:all)
+ require @class_file.chomp('.rb')
+ result = Coverage.result
+
+ result.should == {
+ @class_file => {
+ lines: [
+ nil, nil, 1, nil, nil, 1, nil, nil, 0, nil, nil, nil, nil, nil, nil, nil
+ ],
+ branches: {},
+ methods: {
+ [SomeClass, :some_method, 6, 2, 11, 5] => 0
+ }
+ }
+ }
+ end
+
+ it 'returns results for each mode separately when enabled any mode explicitly' do
+ Coverage.start(lines: true)
+ require @class_file.chomp('.rb')
+ result = Coverage.result
+
+ result.should == {
+ @class_file =>
+ {
+ lines: [
+ nil, nil, 1, nil, nil, 1, nil, nil, 0, nil, nil, nil, nil, nil, nil, nil
+ ]
+ }
+ }
+ end
+ end
+
it 'no requires/loads should give empty hash' do
Coverage.start
result = Coverage.result
@@ -75,17 +116,6 @@ describe 'Coverage.result' do
end
end
- ruby_version_is '3.1' do
- it 'second Coverage.start give exception' do
- Coverage.start
- -> {
- require @config_file.chomp('.rb')
- }.should raise_error(RuntimeError, 'coverage measurement is already setup')
- ensure
- Coverage.result
- end
- end
-
it 'does not include the file starting coverage since it is not tracked' do
require @config_file.chomp('.rb')
Coverage.result.should_not include(@config_file)
@@ -98,18 +128,14 @@ describe 'Coverage.result' do
result = Coverage.result
result.should == {
- @eval_code_file => [
- 1, nil, 1, nil, 1, nil, nil, nil, nil, nil, 1
- ]
+ @eval_code_file => [
+ 1, nil, 1, nil, 1, nil, nil, nil, nil, nil, 1
+ ]
}
end
end
ruby_version_is '3.2' do
- it 'indicates support for different features' do
- Coverage.supported?(:lines).should == true
- end
-
it 'returns the correct results when eval coverage is enabled' do
Coverage.supported?(:eval).should == true
@@ -118,13 +144,13 @@ describe 'Coverage.result' do
result = Coverage.result
result.should == {
- @eval_code_file => {
- lines: [1, nil, 1, nil, 1, 1, nil, nil, nil, nil, 1]
- }
+ @eval_code_file => {
+ lines: [1, nil, 1, nil, 1, 1, nil, nil, nil, nil, 1]
+ }
}
end
- it 'returns the correct results when eval coverage is enabled' do
+ it 'returns the correct results when eval coverage is disabled' do
Coverage.supported?(:eval).should == true
Coverage.start(lines: true, eval: false)
@@ -132,10 +158,200 @@ describe 'Coverage.result' do
result = Coverage.result
result.should == {
- @eval_code_file => {
- lines: [1, nil, 1, nil, 1, nil, nil, nil, nil, nil, 1]
- }
+ @eval_code_file => {
+ lines: [1, nil, 1, nil, 1, nil, nil, nil, nil, nil, 1]
+ }
}
end
end
+
+ it "disables coverage measurement when stop option is not specified" do
+ Coverage.start
+ require @class_file.chomp('.rb')
+
+ Coverage.result
+ Coverage.running?.should == false
+ end
+
+ it "disables coverage measurement when stop: true option is specified" do
+ Coverage.start
+ require @class_file.chomp('.rb')
+
+ -> {
+ Coverage.result(stop: true)
+ }.should complain(/warning: stop implies clear/)
+
+ Coverage.running?.should == false
+ end
+
+ it "does not disable coverage measurement when stop: false option is specified" do
+ Coverage.start
+ require @class_file.chomp('.rb')
+
+ Coverage.result(stop: false)
+ Coverage.running?.should == true
+ end
+
+ it "does not disable coverage measurement when stop option is not specified but clear: true specified" do
+ Coverage.start
+ require @class_file.chomp('.rb')
+
+ Coverage.result(clear: true)
+ Coverage.running?.should == true
+ end
+
+ it "does not disable coverage measurement when stop option is not specified but clear: false specified" do
+ Coverage.start
+ require @class_file.chomp('.rb')
+
+ Coverage.result(clear: false)
+ Coverage.running?.should == true
+ end
+
+ it "disables coverage measurement when stop: true and clear: true specified" do
+ Coverage.start
+ require @class_file.chomp('.rb')
+
+ Coverage.result(stop: true, clear: true)
+ Coverage.running?.should == false
+ end
+
+ it "disables coverage measurement when stop: true and clear: false specified" do
+ Coverage.start
+ require @class_file.chomp('.rb')
+
+ -> {
+ Coverage.result(stop: true, clear: false)
+ }.should complain(/warning: stop implies clear/)
+
+ Coverage.running?.should == false
+ end
+
+ it "does not disable coverage measurement when stop: false and clear: true specified" do
+ Coverage.start
+ require @class_file.chomp('.rb')
+
+ Coverage.result(stop: false, clear: true)
+ Coverage.running?.should == true
+ end
+
+ it "does not disable coverage measurement when stop: false and clear: false specified" do
+ Coverage.start
+ require @class_file.chomp('.rb')
+
+ Coverage.result(stop: false, clear: false)
+ Coverage.running?.should == true
+ end
+
+ it "resets counters (remove them) when stop: true specified but clear option is not specified" do
+ Coverage.start
+ require @class_file.chomp('.rb')
+
+ -> {
+ Coverage.result(stop: true) # clears counters
+ }.should complain(/warning: stop implies clear/)
+
+ Coverage.start
+ Coverage.peek_result.should == {}
+ end
+
+ it "resets counters (remove them) when stop: true and clear: true specified" do
+ Coverage.start
+ require @class_file.chomp('.rb')
+
+ Coverage.result(stop: true, clear: true) # clears counters
+
+ Coverage.start
+ Coverage.peek_result.should == {}
+ end
+
+ it "resets counters (remove them) when stop: true and clear: false specified" do
+ Coverage.start
+ require @class_file.chomp('.rb')
+
+ -> {
+ Coverage.result(stop: true, clear: false) # clears counters
+ }.should complain(/warning: stop implies clear/)
+
+ Coverage.start
+ Coverage.peek_result.should == {}
+ end
+
+ it "resets counters (remove them) when both stop and clear options are not specified" do
+ Coverage.start
+ require @class_file.chomp('.rb')
+
+ Coverage.result # clears counters
+
+ Coverage.start
+ Coverage.peek_result.should == {}
+ end
+
+ it "clears counters (sets 0 values) when stop is not specified but clear: true specified" do
+ Coverage.start
+ require @class_file.chomp('.rb')
+
+ Coverage.result(clear: true) # clears counters
+
+ Coverage.peek_result.should == {
+ @class_file => [
+ nil, nil, 0, nil, nil, 0, nil, nil, 0, nil, nil, nil, nil, nil, nil, nil
+ ]
+ }
+ end
+
+ it "does not clear counters when stop is not specified but clear: false specified" do
+ Coverage.start
+ require @class_file.chomp('.rb')
+
+ result = Coverage.result(clear: false) # doesn't clear counters
+ result.should == {
+ @class_file => [
+ nil, nil, 1, nil, nil, 1, nil, nil, 0, nil, nil, nil, nil, nil, nil, nil
+ ]
+ }
+
+ Coverage.peek_result.should == result
+ end
+
+ it "does not clear counters when stop: false and clear is not specified" do
+ Coverage.start
+ require @class_file.chomp('.rb')
+
+ result = Coverage.result(stop: false) # doesn't clear counters
+ result.should == {
+ @class_file => [
+ nil, nil, 1, nil, nil, 1, nil, nil, 0, nil, nil, nil, nil, nil, nil, nil
+ ]
+ }
+
+ Coverage.peek_result.should == result
+ end
+
+ it "clears counters (sets 0 values) when stop: false and clear: true specified" do
+ Coverage.start
+ require @class_file.chomp('.rb')
+
+ Coverage.result(stop: false, clear: true) # clears counters
+
+ Coverage.peek_result.should == {
+ @class_file => [
+ nil, nil, 0, nil, nil, 0, nil, nil, 0, nil, nil, nil, nil, nil, nil, nil
+ ]
+ }
+ end
+
+ it "does not clear counters when stop: false and clear: false specified" do
+ Coverage.start
+ require @class_file.chomp('.rb')
+
+ result = Coverage.result(stop: false, clear: false) # doesn't clear counters
+ result.should == {
+ @class_file => [
+ nil, nil, 1, nil, nil, 1, nil, nil, 0, nil, nil, nil, nil, nil, nil, nil
+ ]
+ }
+
+ Coverage.peek_result.should == result
+ end
end
diff --git a/spec/ruby/library/coverage/start_spec.rb b/spec/ruby/library/coverage/start_spec.rb
index a993abbf4e..7fccf2d5cf 100644
--- a/spec/ruby/library/coverage/start_spec.rb
+++ b/spec/ruby/library/coverage/start_spec.rb
@@ -2,8 +2,87 @@ require_relative '../../spec_helper'
require 'coverage'
describe 'Coverage.start' do
+ before :each do
+ Coverage.should_not.running?
+ end
+
+ after :each do
+ Coverage.result(stop: true, clear: true) if Coverage.running?
+ end
+
+ it "enables the coverage measurement" do
+ Coverage.start
+ Coverage.should.running?
+ end
+
+ it "returns nil" do
+ Coverage.start.should == nil
+ end
+
+ ruby_version_is '3.1' do
+ it 'raises error when repeated Coverage.start call happens' do
+ Coverage.start
+
+ -> {
+ Coverage.start
+ }.should raise_error(RuntimeError, 'coverage measurement is already setup')
+ end
+ end
+
ruby_version_is '3.2' do
- it "can measure coverage within eval" do
+ it "accepts :all optional argument" do
+ Coverage.start(:all)
+ Coverage.should.running?
+ end
+
+ it "accepts lines: optional keyword argument" do
+ Coverage.start(lines: true)
+ Coverage.should.running?
+ end
+
+ it "accepts branches: optional keyword argument" do
+ Coverage.start(branches: true)
+ Coverage.should.running?
+ end
+
+ it "accepts methods: optional keyword argument" do
+ Coverage.start(methods: true)
+ Coverage.should.running?
+ end
+
+ it "accepts eval: optional keyword argument" do
+ Coverage.start(eval: true)
+ Coverage.should.running?
+ end
+
+ it "accepts oneshot_lines: optional keyword argument" do
+ Coverage.start(oneshot_lines: true)
+ Coverage.should.running?
+ end
+
+ it "ignores unknown keyword arguments" do
+ Coverage.start(foo: true)
+ Coverage.should.running?
+ end
+
+ it "expects a Hash if not passed :all" do
+ -> {
+ Coverage.start(42)
+ }.should raise_error(TypeError, "no implicit conversion of Integer into Hash")
+ end
+
+ it "does not accept both lines: and oneshot_lines: keyword arguments" do
+ -> {
+ Coverage.start(lines: true, oneshot_lines: true)
+ }.should raise_error(RuntimeError, "cannot enable lines and oneshot_lines simultaneously")
+ end
+
+ it "enables the coverage measurement if passed options with `false` value" do
+ Coverage.start(lines: false, branches: false, methods: false, eval: false, oneshot_lines: false)
+ Coverage.should.running?
+ end
+
+ it "measures coverage within eval" do
Coverage.start(lines: true, eval: true)
eval("Object.new\n"*3, binding, "test.rb", 1)
Coverage.result["test.rb"].should == {lines: [1, 1, 1]}
diff --git a/spec/ruby/library/csv/generate_spec.rb b/spec/ruby/library/csv/generate_spec.rb
index 0a1e3d9604..b45e2eb95b 100644
--- a/spec/ruby/library/csv/generate_spec.rb
+++ b/spec/ruby/library/csv/generate_spec.rb
@@ -21,7 +21,7 @@ describe "CSV.generate" do
end
it "appends and returns the argument itself" do
- str = ""
+ str = +""
csv_str = CSV.generate(str) do |csv|
csv.add_row [1, 2, 3]
csv << [4, 5, 6]
diff --git a/spec/ruby/library/date/deconstruct_keys_spec.rb b/spec/ruby/library/date/deconstruct_keys_spec.rb
index fc9caaf332..92579e35c7 100644
--- a/spec/ruby/library/date/deconstruct_keys_spec.rb
+++ b/spec/ruby/library/date/deconstruct_keys_spec.rb
@@ -1,7 +1,8 @@
require_relative '../../spec_helper'
require 'date'
+date_version = defined?(Date::VERSION) ? Date::VERSION : '3.1.0'
-ruby_version_is "3.2" do
+version_is date_version, "3.3" do #ruby_version_is "3.2" do
describe "Date#deconstruct_keys" do
it "returns whole hash for nil as an argument" do
d = Date.new(2022, 10, 5)
diff --git a/spec/ruby/library/date/iso8601_spec.rb b/spec/ruby/library/date/iso8601_spec.rb
index a29652014e..af66845a6b 100644
--- a/spec/ruby/library/date/iso8601_spec.rb
+++ b/spec/ruby/library/date/iso8601_spec.rb
@@ -22,6 +22,18 @@ describe "Date.iso8601" do
d.should == Date.civil(-4712, 1, 1)
end
+ it "raises a Date::Error if the argument is a invalid Date" do
+ -> {
+ Date.iso8601('invalid')
+ }.should raise_error(Date::Error, "invalid date")
+ end
+
+ it "raises a Date::Error when passed a nil" do
+ -> {
+ Date.iso8601(nil)
+ }.should raise_error(Date::Error, "invalid date")
+ end
+
it "raises a TypeError when passed an Object" do
-> { Date.iso8601(Object.new) }.should raise_error(TypeError)
end
@@ -32,4 +44,13 @@ describe "Date._iso8601" do
h = Date._iso8601('invalid')
h.should == {}
end
+
+ it "returns an empty hash if the argument is nil" do
+ h = Date._iso8601(nil)
+ h.should == {}
+ end
+
+ it "raises a TypeError when passed an Object" do
+ -> { Date._iso8601(Object.new) }.should raise_error(TypeError)
+ end
end
diff --git a/spec/ruby/library/date/new_spec.rb b/spec/ruby/library/date/new_spec.rb
index 18120118c0..cb64cabce6 100644
--- a/spec/ruby/library/date/new_spec.rb
+++ b/spec/ruby/library/date/new_spec.rb
@@ -1,7 +1,6 @@
require 'date'
require_relative '../../spec_helper'
require_relative 'shared/civil'
-require_relative 'shared/new_bang'
describe "Date.new" do
it_behaves_like :date_civil, :new
diff --git a/spec/ruby/library/date/parse_spec.rb b/spec/ruby/library/date/parse_spec.rb
index bfbe86fbac..5ef4f6e9b5 100644
--- a/spec/ruby/library/date/parse_spec.rb
+++ b/spec/ruby/library/date/parse_spec.rb
@@ -93,7 +93,7 @@ describe "Date#parse with '.' separator" do
@sep = '.'
end
- it_should_behave_like "date_parse"
+ it_should_behave_like :date_parse
end
describe "Date#parse with '/' separator" do
@@ -101,7 +101,7 @@ describe "Date#parse with '/' separator" do
@sep = '/'
end
- it_should_behave_like "date_parse"
+ it_should_behave_like :date_parse
end
describe "Date#parse with ' ' separator" do
@@ -109,7 +109,7 @@ describe "Date#parse with ' ' separator" do
@sep = ' '
end
- it_should_behave_like "date_parse"
+ it_should_behave_like :date_parse
end
describe "Date#parse with '/' separator US-style" do
@@ -117,7 +117,7 @@ describe "Date#parse with '/' separator US-style" do
@sep = '/'
end
- it_should_behave_like "date_parse_us"
+ it_should_behave_like :date_parse_us
end
describe "Date#parse with '-' separator EU-style" do
@@ -125,7 +125,7 @@ describe "Date#parse with '-' separator EU-style" do
@sep = '-'
end
- it_should_behave_like "date_parse_eu"
+ it_should_behave_like :date_parse_eu
end
describe "Date#parse(.)" do
diff --git a/spec/ruby/library/date/shared/new_bang.rb b/spec/ruby/library/date/shared/new_bang.rb
deleted file mode 100644
index 90f1b432f0..0000000000
--- a/spec/ruby/library/date/shared/new_bang.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-describe :date_new_bang, shared: true do
-
- it "returns a new Date object set to Astronomical Julian Day 0 if no arguments passed" do
- d = Date.send(@method)
- d.ajd.should == 0
- end
-
- it "accepts astronomical julian day number, offset as a fraction of a day and returns a new Date object" do
- d = Date.send(@method, 10, 0.5)
- d.ajd.should == 10
- d.jd.should == 11
- end
-
-end
diff --git a/spec/ruby/library/date/shared/parse.rb b/spec/ruby/library/date/shared/parse.rb
index 1015285e04..40af908386 100644
--- a/spec/ruby/library/date/shared/parse.rb
+++ b/spec/ruby/library/date/shared/parse.rb
@@ -13,7 +13,7 @@ describe :date_parse, shared: true do
d.day.should == 23
end
- it "can parse a 'mmm DD YYYY' string into a Date object" do
+ it "can parse a 'DD mmm YYYY' string into a Date object" do
d = Date.parse("23#{@sep}feb#{@sep}2008")
d.year.should == 2008
d.month.should == 2
@@ -42,7 +42,7 @@ describe :date_parse, shared: true do
d.should == Date.civil(2005, 11, 5)
end
- it "can parse a year, day and month name into a Date object" do
+ it "can parse a day, month name and year into a Date object" do
d = Date.parse("5th#{@sep}november#{@sep}2005")
d.should == Date.civil(2005, 11, 5)
end
diff --git a/spec/ruby/library/date/shared/parse_eu.rb b/spec/ruby/library/date/shared/parse_eu.rb
index ecb15e3c0e..3819524a57 100644
--- a/spec/ruby/library/date/shared/parse_eu.rb
+++ b/spec/ruby/library/date/shared/parse_eu.rb
@@ -7,28 +7,28 @@ describe :date_parse_eu, shared: true do
d.day.should == 1
end
- it "can parse a MM-DD-YYYY string into a Date object" do
+ it "can parse a DD-MM-YYYY string into a Date object" do
d = Date.parse("10#{@sep}01#{@sep}2007")
d.year.should == 2007
d.month.should == 1
d.day.should == 10
end
- it "can parse a MM-DD-YY string into a Date object" do
+ it "can parse a YY-MM-DD string into a Date object" do
d = Date.parse("10#{@sep}01#{@sep}07")
d.year.should == 2010
d.month.should == 1
d.day.should == 7
end
- it "can parse a MM-DD-YY string into a Date object NOT using the year digits as 20XX" do
+ it "can parse a YY-MM-DD string into a Date object NOT using the year digits as 20XX" do
d = Date.parse("10#{@sep}01#{@sep}07", false)
d.year.should == 10
d.month.should == 1
d.day.should == 7
end
- it "can parse a MM-DD-YY string into a Date object using the year digits as 20XX" do
+ it "can parse a YY-MM-DD string into a Date object using the year digits as 20XX" do
d = Date.parse("10#{@sep}01#{@sep}07", true)
d.year.should == 2010
d.month.should == 1
diff --git a/spec/ruby/library/date/shared/parse_us.rb b/spec/ruby/library/date/shared/parse_us.rb
index 7be62b1af1..17e2fc96c1 100644
--- a/spec/ruby/library/date/shared/parse_us.rb
+++ b/spec/ruby/library/date/shared/parse_us.rb
@@ -6,28 +6,28 @@ describe :date_parse_us, shared: true do
d.day.should == 1
end
- it "parses a MM#{@sep}DD#{@sep}YYYY string into a Date object" do
+ it "parses a DD#{@sep}MM#{@sep}YYYY string into a Date object" do
d = Date.parse("10#{@sep}01#{@sep}2007")
d.year.should == 2007
d.month.should == 1
d.day.should == 10
end
- it "parses a MM#{@sep}DD#{@sep}YY string into a Date object" do
+ it "parses a YY#{@sep}MM#{@sep}DD string into a Date object" do
d = Date.parse("10#{@sep}01#{@sep}07")
d.year.should == 2010
d.month.should == 1
d.day.should == 7
end
- it "parses a MM#{@sep}DD#{@sep}YY string into a Date object NOT using the year digits as 20XX" do
+ it "parses a YY#{@sep}MM#{@sep}DD string into a Date object NOT using the year digits as 20XX" do
d = Date.parse("10#{@sep}01#{@sep}07", false)
d.year.should == 10
d.month.should == 1
d.day.should == 7
end
- it "parses a MM#{@sep}DD#{@sep}YY string into a Date object using the year digits as 20XX" do
+ it "parses a YY#{@sep}MM#{@sep}DD string into a Date object using the year digits as 20XX" do
d = Date.parse("10#{@sep}01#{@sep}07", true)
d.year.should == 2010
d.month.should == 1
diff --git a/spec/ruby/library/date/strftime_spec.rb b/spec/ruby/library/date/strftime_spec.rb
index 33ecc4ce9d..b5232a2073 100644
--- a/spec/ruby/library/date/strftime_spec.rb
+++ b/spec/ruby/library/date/strftime_spec.rb
@@ -1,6 +1,7 @@
require_relative "../../spec_helper"
require 'date'
require_relative '../../shared/time/strftime_for_date'
+date_version = defined?(Date::VERSION) ? Date::VERSION : '3.1.0'
describe "Date#strftime" do
before :all do
@@ -23,14 +24,14 @@ describe "Date#strftime" do
end
# %v is %e-%b-%Y for Date/DateTime
- ruby_version_is ""..."3.1" do
+ version_is date_version, ""..."3.2" do #ruby_version_is ""..."3.1" do
it "should be able to show the commercial week" do
@date.strftime("%v").should == " 9-Apr-2000"
@date.strftime("%v").should == @date.strftime('%e-%b-%Y')
end
end
- ruby_version_is "3.1" do
+ version_is date_version, "3.2" do #ruby_version_is "3.1" do
it "should be able to show the commercial week" do
@date.strftime("%v").should == " 9-APR-2000"
@date.strftime("%v").should != @date.strftime('%e-%b-%Y')
diff --git a/spec/ruby/library/datetime/deconstruct_keys_spec.rb b/spec/ruby/library/datetime/deconstruct_keys_spec.rb
index c6c0f71f55..77ceaa51c4 100644
--- a/spec/ruby/library/datetime/deconstruct_keys_spec.rb
+++ b/spec/ruby/library/datetime/deconstruct_keys_spec.rb
@@ -1,7 +1,8 @@
require_relative '../../spec_helper'
require 'date'
+date_version = defined?(Date::VERSION) ? Date::VERSION : '3.1.0'
-ruby_version_is "3.2" do
+version_is date_version, "3.3" do #ruby_version_is "3.2" do
describe "DateTime#deconstruct_keys" do
it "returns whole hash for nil as an argument" do
d = DateTime.new(2022, 10, 5, 13, 30)
diff --git a/spec/ruby/library/datetime/rfc2822_spec.rb b/spec/ruby/library/datetime/rfc2822_spec.rb
index 70bfca60b4..83f7fa8d5b 100644
--- a/spec/ruby/library/datetime/rfc2822_spec.rb
+++ b/spec/ruby/library/datetime/rfc2822_spec.rb
@@ -3,4 +3,8 @@ require 'date'
describe "DateTime.rfc2822" do
it "needs to be reviewed for spec completeness"
+
+ it "raises DateError if passed nil" do
+ -> { DateTime.rfc2822(nil) }.should raise_error(Date::Error, "invalid date")
+ end
end
diff --git a/spec/ruby/library/datetime/strftime_spec.rb b/spec/ruby/library/datetime/strftime_spec.rb
index 725bcafb0d..abb0838e8e 100644
--- a/spec/ruby/library/datetime/strftime_spec.rb
+++ b/spec/ruby/library/datetime/strftime_spec.rb
@@ -2,6 +2,7 @@ require_relative '../../spec_helper'
require 'date'
require_relative '../../shared/time/strftime_for_date'
require_relative '../../shared/time/strftime_for_time'
+date_version = defined?(Date::VERSION) ? Date::VERSION : '3.1.0'
describe "DateTime#strftime" do
before :all do
@@ -33,14 +34,14 @@ describe "DateTime#strftime" do
end
# %v is %e-%b-%Y for Date/DateTime
- ruby_version_is ""..."3.1" do
+ version_is date_version, ""..."3.2" do #ruby_version_is ""..."3.1" do
it "should be able to show the commercial week" do
@time.strftime("%v").should == " 3-Feb-2001"
@time.strftime("%v").should == @time.strftime('%e-%b-%Y')
end
end
- ruby_version_is "3.1" do
+ version_is date_version, "3.2" do #ruby_version_is "3.1" do
it "should be able to show the commercial week" do
@time.strftime("%v").should == " 3-FEB-2001"
@time.strftime("%v").should != @time.strftime('%e-%b-%Y')
diff --git a/spec/ruby/library/datetime/to_time_spec.rb b/spec/ruby/library/datetime/to_time_spec.rb
index 95eca864da..09e6192e7f 100644
--- a/spec/ruby/library/datetime/to_time_spec.rb
+++ b/spec/ruby/library/datetime/to_time_spec.rb
@@ -1,5 +1,6 @@
require_relative '../../spec_helper'
require 'date'
+date_version = defined?(Date::VERSION) ? Date::VERSION : '3.1.0'
describe "DateTime#to_time" do
it "yields a new Time object" do
@@ -18,8 +19,7 @@ describe "DateTime#to_time" do
time.sec.should == 59
end
- date_version = defined?(Date::VERSION) ? Date::VERSION : '0.0.0'
- version_is(date_version, '3.2.3') do
+ version_is date_version, '3.2.3' do #ruby_version_is "3.2" do
it "returns a Time representing the same instant before Gregorian" do
datetime = DateTime.civil(1582, 10, 4, 23, 58, 59)
time = datetime.to_time.utc
diff --git a/spec/ruby/library/digest/instance/shared/update.rb b/spec/ruby/library/digest/instance/shared/update.rb
index dccc8f80df..17779e54a4 100644
--- a/spec/ruby/library/digest/instance/shared/update.rb
+++ b/spec/ruby/library/digest/instance/shared/update.rb
@@ -3,6 +3,6 @@ describe :digest_instance_update, shared: true do
c = Class.new do
include Digest::Instance
end
- -> { c.new.update("test") }.should raise_error(RuntimeError)
+ -> { c.new.send(@method, "test") }.should raise_error(RuntimeError)
end
end
diff --git a/spec/ruby/library/digest/md5/shared/sample.rb b/spec/ruby/library/digest/md5/shared/sample.rb
deleted file mode 100644
index 2bb4f658b1..0000000000
--- a/spec/ruby/library/digest/md5/shared/sample.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# -*- encoding: binary -*-
-
-require 'digest/md5'
-
-module MD5Constants
-
- Contents = "Ipsum is simply dummy text of the printing and typesetting industry. \nLorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. \nIt has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. \nIt was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
-
- Klass = ::Digest::MD5
- BlockLength = 64
- DigestLength = 16
- BlankDigest = "\324\035\214\331\217\000\262\004\351\200\t\230\354\370B~"
- Digest = "\2473\267qw\276\364\343\345\320\304\350\313\314\217n"
- BlankHexdigest = "d41d8cd98f00b204e9800998ecf8427e"
- Hexdigest = "a733b77177bef4e3e5d0c4e8cbcc8f6e"
-
-end
diff --git a/spec/ruby/library/drb/start_service_spec.rb b/spec/ruby/library/drb/start_service_spec.rb
index 016c8b2cff..57a8cf6e15 100644
--- a/spec/ruby/library/drb/start_service_spec.rb
+++ b/spec/ruby/library/drb/start_service_spec.rb
@@ -1,28 +1,33 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/test_server'
-require 'drb'
-describe "DRb.start_service" do
- before :each do
- @server = DRb.start_service("druby://localhost:0", TestServer.new)
- end
+# This does not work yet when run in CRuby via make test-spec:
+# Gem::MissingSpecError: Could not find 'ruby2_keywords' (>= 0) among 28 total gem(s)
+guard_not -> { MSpecScript.instance_variable_defined?(:@testing_ruby) } do
+ require_relative 'fixtures/test_server'
+ require 'drb'
- after :each do
- DRb.stop_service if @server
- end
+ describe "DRb.start_service" do
+ before :each do
+ @server = DRb.start_service("druby://localhost:0", TestServer.new)
+ end
- it "runs a basic remote call" do
- DRb.current_server.should == @server
- obj = DRbObject.new(nil, @server.uri)
- obj.add(1,2,3).should == 6
- end
+ after :each do
+ DRb.stop_service if @server
+ end
+
+ it "runs a basic remote call" do
+ DRb.current_server.should == @server
+ obj = DRbObject.new(nil, @server.uri)
+ obj.add(1,2,3).should == 6
+ end
- it "runs a basic remote call passing a block" do
- DRb.current_server.should == @server
- obj = DRbObject.new(nil, @server.uri)
- obj.add_yield(2) do |i|
- i.should == 2
- i+1
- end.should == 4
+ it "runs a basic remote call passing a block" do
+ DRb.current_server.should == @server
+ obj = DRbObject.new(nil, @server.uri)
+ obj.add_yield(2) do |i|
+ i.should == 2
+ i+1
+ end.should == 4
+ end
end
end
diff --git a/spec/ruby/library/erb/new_spec.rb b/spec/ruby/library/erb/new_spec.rb
index 4d7f7bf36a..a5aeeaeed1 100644
--- a/spec/ruby/library/erb/new_spec.rb
+++ b/spec/ruby/library/erb/new_spec.rb
@@ -140,7 +140,7 @@ END
end
describe "warning about arguments" do
- ruby_version_is "3.1" do
+ version_is ERB.version, "2.2.1" do #ruby_version_is "3.1" do
it "warns when passed safe_level and later arguments" do
-> {
ERB.new(@eruby_str, nil, '%')
diff --git a/spec/ruby/library/erb/run_spec.rb b/spec/ruby/library/erb/run_spec.rb
index 8c07442d8f..602e53ab38 100644
--- a/spec/ruby/library/erb/run_spec.rb
+++ b/spec/ruby/library/erb/run_spec.rb
@@ -6,7 +6,7 @@ describe "ERB#run" do
# lambda { ... }.should output
def _steal_stdout
orig = $stdout
- s = ''
+ s = +''
def s.write(arg); self << arg.to_s; end
$stdout = s
begin
diff --git a/spec/ruby/library/etc/uname_spec.rb b/spec/ruby/library/etc/uname_spec.rb
new file mode 100644
index 0000000000..a42558f593
--- /dev/null
+++ b/spec/ruby/library/etc/uname_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+require 'etc'
+
+describe "Etc.uname" do
+ it "returns a Hash with the documented keys" do
+ uname = Etc.uname
+ uname.should be_kind_of(Hash)
+ uname.should.key?(:sysname)
+ uname.should.key?(:nodename)
+ uname.should.key?(:release)
+ uname.should.key?(:version)
+ uname.should.key?(:machine)
+ end
+end
diff --git a/spec/ruby/library/fiber/current_spec.rb b/spec/ruby/library/fiber/current_spec.rb
index e18603f069..1467a88d0d 100644
--- a/spec/ruby/library/fiber/current_spec.rb
+++ b/spec/ruby/library/fiber/current_spec.rb
@@ -48,22 +48,11 @@ describe "Fiber.current" do
fiber3 = Fiber.new do
states << :fiber3
fiber2.transfer
- ruby_version_is '3.0' do
- states << :fiber3_terminated
- end
- ruby_version_is '' ... '3.0' do
- flunk
- end
+ states << :fiber3_terminated
end
fiber3.resume
- ruby_version_is "" ... "3.0" do
- states.should == [:fiber3, :fiber2, :fiber]
- end
-
- ruby_version_is "3.0" do
- states.should == [:fiber3, :fiber2, :fiber, :fiber3_terminated]
- end
+ states.should == [:fiber3, :fiber2, :fiber, :fiber3_terminated]
end
end
diff --git a/spec/ruby/library/fiber/resume_spec.rb b/spec/ruby/library/fiber/resume_spec.rb
index 8b7c104a6f..fd69d3ba99 100644
--- a/spec/ruby/library/fiber/resume_spec.rb
+++ b/spec/ruby/library/fiber/resume_spec.rb
@@ -3,33 +3,16 @@ require_relative '../../spec_helper'
require 'fiber'
describe "Fiber#resume" do
- ruby_version_is '' ... '3.0' do
- it "raises a FiberError if the Fiber has transferred control to another Fiber" do
- fiber1 = Fiber.new { true }
- fiber2 = Fiber.new { fiber1.transfer; Fiber.yield }
- fiber2.resume
- -> { fiber2.resume }.should raise_error(FiberError)
- end
-
- it "raises a FiberError if the Fiber attempts to resume a resuming fiber" do
- root_fiber = Fiber.current
- fiber1 = Fiber.new { root_fiber.resume }
- -> { fiber1.resume }.should raise_error(FiberError, /double resume/)
- end
+ it "can work with Fiber#transfer" do
+ fiber1 = Fiber.new { true }
+ fiber2 = Fiber.new { fiber1.transfer; Fiber.yield 10 ; Fiber.yield 20; raise }
+ fiber2.resume.should == 10
+ fiber2.resume.should == 20
end
- ruby_version_is '3.0' do
- it "can work with Fiber#transfer" do
- fiber1 = Fiber.new { true }
- fiber2 = Fiber.new { fiber1.transfer; Fiber.yield 10 ; Fiber.yield 20; raise }
- fiber2.resume.should == 10
- fiber2.resume.should == 20
- end
-
- it "raises a FiberError if the Fiber attempts to resume a resuming fiber" do
- root_fiber = Fiber.current
- fiber1 = Fiber.new { root_fiber.resume }
- -> { fiber1.resume }.should raise_error(FiberError, /attempt to resume a resuming fiber/)
- end
+ it "raises a FiberError if the Fiber attempts to resume a resuming fiber" do
+ root_fiber = Fiber.current
+ fiber1 = Fiber.new { root_fiber.resume }
+ -> { fiber1.resume }.should raise_error(FiberError, /attempt to resume a resuming fiber/)
end
end
diff --git a/spec/ruby/library/fiber/transfer_spec.rb b/spec/ruby/library/fiber/transfer_spec.rb
index 7af548da1a..e20d51352e 100644
--- a/spec/ruby/library/fiber/transfer_spec.rb
+++ b/spec/ruby/library/fiber/transfer_spec.rb
@@ -11,13 +11,7 @@ describe "Fiber#transfer" do
it "transfers control from one Fiber to another when called from a Fiber" do
fiber1 = Fiber.new { :fiber1 }
fiber2 = Fiber.new { fiber1.transfer; :fiber2 }
-
- ruby_version_is '' ... '3.0' do
- fiber2.resume.should == :fiber1
- end
- ruby_version_is '3.0' do
- fiber2.resume.should == :fiber2
- end
+ fiber2.resume.should == :fiber2
end
it "returns to the root Fiber when finished" do
@@ -40,24 +34,12 @@ describe "Fiber#transfer" do
states.should == [:start, :end]
end
- ruby_version_is '' ... '3.0' do
- it "can transfer control to a Fiber that has transferred to another Fiber" do
- states = []
- fiber1 = Fiber.new { states << :fiber1 }
- fiber2 = Fiber.new { states << :fiber2_start; fiber1.transfer; states << :fiber2_end}
- fiber2.resume.should == [:fiber2_start, :fiber1]
- fiber2.transfer.should == [:fiber2_start, :fiber1, :fiber2_end]
- end
- end
-
- ruby_version_is '3.0' do
- it "can not transfer control to a Fiber that has suspended by Fiber.yield" do
- states = []
- fiber1 = Fiber.new { states << :fiber1 }
- fiber2 = Fiber.new { states << :fiber2_start; Fiber.yield fiber1.transfer; states << :fiber2_end}
- fiber2.resume.should == [:fiber2_start, :fiber1]
- -> { fiber2.transfer }.should raise_error(FiberError)
- end
+ it "can not transfer control to a Fiber that has suspended by Fiber.yield" do
+ states = []
+ fiber1 = Fiber.new { states << :fiber1 }
+ fiber2 = Fiber.new { states << :fiber2_start; Fiber.yield fiber1.transfer; states << :fiber2_end}
+ fiber2.resume.should == [:fiber2_start, :fiber1]
+ -> { fiber2.transfer }.should raise_error(FiberError)
end
it "raises a FiberError when transferring to a Fiber which resumes itself" do
@@ -101,28 +83,4 @@ describe "Fiber#transfer" do
thread.join
states.should == [0, 1, 2, 3]
end
-
- ruby_version_is "" ... "3.0" do
- it "runs until Fiber.yield" do
- obj = mock('obj')
- obj.should_not_receive(:do)
- fiber = Fiber.new { 1 + 2; Fiber.yield; obj.do }
- fiber.transfer
- end
-
- it "resumes from the last call to Fiber.yield on subsequent invocations" do
- fiber = Fiber.new { Fiber.yield :first; :second }
- fiber.transfer.should == :first
- fiber.transfer.should == :second
- end
-
- it "sets the block parameters to its arguments on the first invocation" do
- first = mock('first')
- first.should_receive(:arg).with(:first).twice
-
- fiber = Fiber.new { |arg| first.arg arg; Fiber.yield; first.arg arg; }
- fiber.transfer :first
- fiber.transfer :second
- end
- end
end
diff --git a/spec/ruby/library/io-wait/wait_spec.rb b/spec/ruby/library/io-wait/wait_spec.rb
index d968c38774..fc07c6a8d9 100644
--- a/spec/ruby/library/io-wait/wait_spec.rb
+++ b/spec/ruby/library/io-wait/wait_spec.rb
@@ -24,74 +24,81 @@ describe "IO#wait" do
@w.close unless @w.closed?
end
- ruby_version_is "3.0" do
- context "[events, timeout] passed" do
- ruby_version_is "3.0"..."3.2" do
- it "returns self when the READABLE event is ready during the timeout" do
- @w.write('data to read')
- @r.wait(IO::READABLE, 2).should.equal?(@r)
- end
-
- it "returns self when the WRITABLE event is ready during the timeout" do
- @w.wait(IO::WRITABLE, 0).should.equal?(@w)
- end
+ context "[events, timeout] passed" do
+ ruby_version_is ""..."3.2" do
+ it "returns self when the READABLE event is ready during the timeout" do
+ @w.write('data to read')
+ @r.wait(IO::READABLE, 2).should.equal?(@r)
end
- ruby_version_is "3.2" do
- it "returns events mask when the READABLE event is ready during the timeout" do
- @w.write('data to read')
- @r.wait(IO::READABLE, 2).should == IO::READABLE
- end
+ it "returns self when the WRITABLE event is ready during the timeout" do
+ @w.wait(IO::WRITABLE, 0).should.equal?(@w)
+ end
+ end
+
+ ruby_version_is "3.2" do
+ it "returns events mask when the READABLE event is ready during the timeout" do
+ @w.write('data to read')
+ @r.wait(IO::READABLE, 2).should == IO::READABLE
+ end
- it "returns events mask when the WRITABLE event is ready during the timeout" do
- @w.wait(IO::WRITABLE, 0).should == IO::WRITABLE
- end
+ it "returns events mask when the WRITABLE event is ready during the timeout" do
+ @w.wait(IO::WRITABLE, 0).should == IO::WRITABLE
end
+ end
- ruby_version_is "3.0" do
- it "waits for the READABLE event to be ready" do
- queue = Queue.new
- thread = Thread.new { queue.pop; sleep 1; @w.write('data to read') };
+ it "waits for the READABLE event to be ready" do
+ @r.wait(IO::READABLE, 0).should == nil
- queue.push('signal');
- @r.wait(IO::READABLE, 2).should_not == nil
+ @w.write('data to read')
+ @r.wait(IO::READABLE, 0).should_not == nil
+ end
- thread.join
- end
+ it "waits for the WRITABLE event to be ready" do
+ written_bytes = IOWaitSpec.exhaust_write_buffer(@w)
+ @w.wait(IO::WRITABLE, 0).should == nil
- it "waits for the WRITABLE event to be ready" do
- written_bytes = IOWaitSpec.exhaust_write_buffer(@w)
+ @r.read(written_bytes)
+ @w.wait(IO::WRITABLE, 0).should_not == nil
+ end
- queue = Queue.new
- thread = Thread.new { queue.pop; sleep 1; @r.read(written_bytes) };
+ it "returns nil when the READABLE event is not ready during the timeout" do
+ @w.wait(IO::READABLE, 0).should == nil
+ end
- queue.push('signal');
- @w.wait(IO::WRITABLE, 2).should_not == nil
+ it "returns nil when the WRITABLE event is not ready during the timeout" do
+ IOWaitSpec.exhaust_write_buffer(@w)
+ @w.wait(IO::WRITABLE, 0).should == nil
+ end
- thread.join
- end
+ it "raises IOError when io is closed (closed stream (IOError))" do
+ @io.close
+ -> { @io.wait(IO::READABLE, 0) }.should raise_error(IOError, "closed stream")
+ end
- it "returns nil when the READABLE event is not ready during the timeout" do
- @w.wait(IO::READABLE, 0).should == nil
- end
+ ruby_version_is "3.2" do
+ it "raises ArgumentError when events is not positive" do
+ -> { @w.wait(0, 0) }.should raise_error(ArgumentError, "Events must be positive integer!")
+ -> { @w.wait(-1, 0) }.should raise_error(ArgumentError, "Events must be positive integer!")
+ end
+ end
- it "returns nil when the WRITABLE event is not ready during the timeout" do
- IOWaitSpec.exhaust_write_buffer(@w)
- @w.wait(IO::WRITABLE, 0).should == nil
- end
+ it "changes thread status to 'sleep' when waits for READABLE event" do
+ t = Thread.new { @r.wait(IO::READABLE, 10) }
+ sleep 1
+ t.status.should == 'sleep'
+ t.kill
+ t.join # Thread#kill doesn't wait for the thread to end
+ end
- it "raises IOError when io is closed (closed stream (IOError))" do
- @io.close
- -> { @io.wait(IO::READABLE, 0) }.should raise_error(IOError, "closed stream")
- end
+ it "changes thread status to 'sleep' when waits for WRITABLE event" do
+ written_bytes = IOWaitSpec.exhaust_write_buffer(@w)
- ruby_version_is "3.2" do
- it "raises ArgumentError when events is not positive" do
- -> { @w.wait(0, 0) }.should raise_error(ArgumentError, "Events must be positive integer!")
- -> { @w.wait(-1, 0) }.should raise_error(ArgumentError, "Events must be positive integer!")
- end
- end
- end
+ t = Thread.new { @w.wait(IO::WRITABLE, 10) }
+ sleep 1
+ t.status.should == 'sleep'
+ t.kill
+ t.join # Thread#kill doesn't wait for the thread to end
end
end
diff --git a/spec/ruby/library/ipaddr/new_spec.rb b/spec/ruby/library/ipaddr/new_spec.rb
index 053928c3cf..714c1e2f1a 100644
--- a/spec/ruby/library/ipaddr/new_spec.rb
+++ b/spec/ruby/library/ipaddr/new_spec.rb
@@ -77,7 +77,13 @@ describe "IPAddr#new" do
a.family.should == Socket::AF_INET6
end
- ruby_version_is ""..."3.1" do
+ ipaddr_version = if defined?(IPAddr::VERSION) #ruby_version_is ""..."3.1" do
+ IPAddr::VERSION
+ else
+ "1.2.2"
+ end
+
+ version_is ipaddr_version, ""..."1.2.3" do #ruby_version_is ""..."3.1" do
it "raises on incorrect IPAddr strings" do
[
["fe80::1%fxp0"],
@@ -93,7 +99,7 @@ describe "IPAddr#new" do
end
end
- ruby_version_is "3.1" do
+ version_is ipaddr_version, "1.2.3" do #ruby_version_is "3.1" do
it "raises on incorrect IPAddr strings" do
[
["::1/255.255.255.0"],
diff --git a/spec/ruby/library/logger/device/close_spec.rb b/spec/ruby/library/logger/device/close_spec.rb
index 7c5e118d56..1db5d582a7 100644
--- a/spec/ruby/library/logger/device/close_spec.rb
+++ b/spec/ruby/library/logger/device/close_spec.rb
@@ -15,17 +15,8 @@ describe "Logger::LogDevice#close" do
rm_r @file_path
end
- version_is Logger::VERSION, ""..."1.4.0" do
- it "closes the LogDevice's stream" do
- @device.close
- -> { @device.write("Test") }.should complain(/\Alog writing failed\./)
- end
- end
-
- version_is Logger::VERSION, "1.4.0" do
- it "closes the LogDevice's stream" do
- @device.close
- -> { @device.write("Test") }.should complain(/\Alog shifting failed\./)
- end
+ it "closes the LogDevice's stream" do
+ @device.close
+ -> { @device.write("Test") }.should complain(/\Alog shifting failed\./)
end
end
diff --git a/spec/ruby/library/logger/device/write_spec.rb b/spec/ruby/library/logger/device/write_spec.rb
index cd2d7e27a9..87ecf2ad6a 100644
--- a/spec/ruby/library/logger/device/write_spec.rb
+++ b/spec/ruby/library/logger/device/write_spec.rb
@@ -35,17 +35,8 @@ describe "Logger::LogDevice#write" do
rm_r path
end
- version_is Logger::VERSION, ""..."1.4.0" do
- it "fails if the device is already closed" do
- @device.close
- -> { @device.write "foo" }.should complain(/\Alog writing failed\./)
- end
- end
-
- version_is Logger::VERSION, "1.4.0" do
- it "fails if the device is already closed" do
- @device.close
- -> { @device.write "foo" }.should complain(/\Alog shifting failed\./)
- end
+ it "fails if the device is already closed" do
+ @device.close
+ -> { @device.write "foo" }.should complain(/\Alog shifting failed\./)
end
end
diff --git a/spec/ruby/library/matrix/I_spec.rb b/spec/ruby/library/matrix/I_spec.rb
index aa064ed54b..6eeffe8e98 100644
--- a/spec/ruby/library/matrix/I_spec.rb
+++ b/spec/ruby/library/matrix/I_spec.rb
@@ -1,9 +1,6 @@
require_relative '../../spec_helper'
+require_relative 'shared/identity'
-ruby_version_is ""..."3.1" do
- require_relative 'shared/identity'
-
- describe "Matrix.I" do
- it_behaves_like :matrix_identity, :I
- end
+describe "Matrix.I" do
+ it_behaves_like :matrix_identity, :I
end
diff --git a/spec/ruby/library/matrix/antisymmetric_spec.rb b/spec/ruby/library/matrix/antisymmetric_spec.rb
index dcc3c30f3e..200df703cb 100644
--- a/spec/ruby/library/matrix/antisymmetric_spec.rb
+++ b/spec/ruby/library/matrix/antisymmetric_spec.rb
@@ -1,38 +1,36 @@
require_relative '../../spec_helper'
-ruby_version_is ""..."3.1" do
- require 'matrix'
+require 'matrix'
- describe "Matrix#antisymmetric?" do
- it "returns true for an antisymmetric Matrix" do
- Matrix[[0, -2, Complex(1, 3)], [2, 0, 5], [-Complex(1, 3), -5, 0]].antisymmetric?.should be_true
- end
+describe "Matrix#antisymmetric?" do
+ it "returns true for an antisymmetric Matrix" do
+ Matrix[[0, -2, Complex(1, 3)], [2, 0, 5], [-Complex(1, 3), -5, 0]].antisymmetric?.should be_true
+ end
- it "returns true for a 0x0 empty matrix" do
- Matrix.empty.antisymmetric?.should be_true
- end
+ it "returns true for a 0x0 empty matrix" do
+ Matrix.empty.antisymmetric?.should be_true
+ end
- it "returns false for non-antisymmetric matrices" do
- [
- Matrix[[1, 2, 3], [4, 5, 6], [7, 8, 9]],
- Matrix[[1, -2, 3], [2, 0, 6], [-3, -6, 0]], # wrong diagonal element
- Matrix[[0, 2, -3], [2, 0, 6], [-3, 6, 0]] # only signs wrong
- ].each do |matrix|
- matrix.antisymmetric?.should be_false
- end
+ it "returns false for non-antisymmetric matrices" do
+ [
+ Matrix[[1, 2, 3], [4, 5, 6], [7, 8, 9]],
+ Matrix[[1, -2, 3], [2, 0, 6], [-3, -6, 0]], # wrong diagonal element
+ Matrix[[0, 2, -3], [2, 0, 6], [-3, 6, 0]] # only signs wrong
+ ].each do |matrix|
+ matrix.antisymmetric?.should be_false
end
+ end
- it "raises an error for rectangular matrices" do
- [
- Matrix[[0], [0]],
- Matrix[[0, 0]],
- Matrix.empty(0, 2),
- Matrix.empty(2, 0),
- ].each do |rectangular_matrix|
- -> {
- rectangular_matrix.antisymmetric?
- }.should raise_error(Matrix::ErrDimensionMismatch)
- end
+ it "raises an error for rectangular matrices" do
+ [
+ Matrix[[0], [0]],
+ Matrix[[0, 0]],
+ Matrix.empty(0, 2),
+ Matrix.empty(2, 0),
+ ].each do |rectangular_matrix|
+ -> {
+ rectangular_matrix.antisymmetric?
+ }.should raise_error(Matrix::ErrDimensionMismatch)
end
end
end
diff --git a/spec/ruby/library/matrix/build_spec.rb b/spec/ruby/library/matrix/build_spec.rb
index d3055a1aa7..6d8017a3df 100644
--- a/spec/ruby/library/matrix/build_spec.rb
+++ b/spec/ruby/library/matrix/build_spec.rb
@@ -1,76 +1,73 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require_relative 'fixtures/classes'
- require 'matrix'
+describe "Matrix.build" do
- describe "Matrix.build" do
-
- it "returns a Matrix object of the given size" do
- m = Matrix.build(3, 4){1}
- m.should be_an_instance_of(Matrix)
- m.row_size.should == 3
- m.column_size.should == 4
- end
+ it "returns a Matrix object of the given size" do
+ m = Matrix.build(3, 4){1}
+ m.should be_an_instance_of(Matrix)
+ m.row_size.should == 3
+ m.column_size.should == 4
+ end
- it "builds the Matrix using the given block" do
- Matrix.build(2, 3){|col, row| 10*col - row}.should ==
- Matrix[[0, -1, -2], [10, 9, 8]]
- end
+ it "builds the Matrix using the given block" do
+ Matrix.build(2, 3){|col, row| 10*col - row}.should ==
+ Matrix[[0, -1, -2], [10, 9, 8]]
+ end
- it "iterates through the first row, then the second, ..." do
- acc = []
- Matrix.build(2, 3){|*args| acc << args}
- acc.should == [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2]]
- end
+ it "iterates through the first row, then the second, ..." do
+ acc = []
+ Matrix.build(2, 3){|*args| acc << args}
+ acc.should == [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2]]
+ end
- it "returns an Enumerator is no block is given" do
- enum = Matrix.build(2, 1)
- enum.should be_an_instance_of(Enumerator)
- enum.each{1}.should == Matrix[[1], [1]]
- end
+ it "returns an Enumerator is no block is given" do
+ enum = Matrix.build(2, 1)
+ enum.should be_an_instance_of(Enumerator)
+ enum.each{1}.should == Matrix[[1], [1]]
+ end
- it "requires integers as parameters" do
- -> { Matrix.build("1", "2"){1} }.should raise_error(TypeError)
- -> { Matrix.build(nil, nil){1} }.should raise_error(TypeError)
- -> { Matrix.build(1..2){1} }.should raise_error(TypeError)
- end
+ it "requires integers as parameters" do
+ -> { Matrix.build("1", "2"){1} }.should raise_error(TypeError)
+ -> { Matrix.build(nil, nil){1} }.should raise_error(TypeError)
+ -> { Matrix.build(1..2){1} }.should raise_error(TypeError)
+ end
- it "requires non-negative integers" do
- -> { Matrix.build(-1, 1){1} }.should raise_error(ArgumentError)
- -> { Matrix.build(+1,-1){1} }.should raise_error(ArgumentError)
- end
+ it "requires non-negative integers" do
+ -> { Matrix.build(-1, 1){1} }.should raise_error(ArgumentError)
+ -> { Matrix.build(+1,-1){1} }.should raise_error(ArgumentError)
+ end
- it "returns empty Matrix if one argument is zero" do
- m = Matrix.build(0, 3){
- raise "Should not yield"
- }
- m.should be_empty
- m.column_size.should == 3
+ it "returns empty Matrix if one argument is zero" do
+ m = Matrix.build(0, 3){
+ raise "Should not yield"
+ }
+ m.should be_empty
+ m.column_size.should == 3
- m = Matrix.build(3, 0){
- raise "Should not yield"
- }
- m.should be_empty
- m.row_size.should == 3
- end
+ m = Matrix.build(3, 0){
+ raise "Should not yield"
+ }
+ m.should be_empty
+ m.row_size.should == 3
+ end
- it "tries to calls :to_int on arguments" do
- int = mock('int')
- int.should_receive(:to_int).twice.and_return(2)
- Matrix.build(int, int){ 1 }.should == Matrix[ [1,1], [1,1] ]
- end
+ it "tries to calls :to_int on arguments" do
+ int = mock('int')
+ int.should_receive(:to_int).twice.and_return(2)
+ Matrix.build(int, int){ 1 }.should == Matrix[ [1,1], [1,1] ]
+ end
- it "builds an nxn Matrix when given only one argument" do
- m = Matrix.build(3){1}
- m.row_size.should == 3
- m.column_size.should == 3
- end
+ it "builds an nxn Matrix when given only one argument" do
+ m = Matrix.build(3){1}
+ m.row_size.should == 3
+ m.column_size.should == 3
end
+end
- describe "for a subclass of Matrix" do
- it "returns an instance of that subclass" do
- MatrixSub.build(3){1}.should be_an_instance_of(MatrixSub)
- end
+describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.build(3){1}.should be_an_instance_of(MatrixSub)
end
end
diff --git a/spec/ruby/library/matrix/clone_spec.rb b/spec/ruby/library/matrix/clone_spec.rb
index bde119988f..74e5bf157e 100644
--- a/spec/ruby/library/matrix/clone_spec.rb
+++ b/spec/ruby/library/matrix/clone_spec.rb
@@ -1,28 +1,25 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require_relative 'fixtures/classes'
- require 'matrix'
-
- describe "Matrix#clone" do
- before :each do
- @a = Matrix[[1, 2], [3, 4], [5, 6]]
- end
+describe "Matrix#clone" do
+ before :each do
+ @a = Matrix[[1, 2], [3, 4], [5, 6]]
+ end
- it "returns a shallow copy of the matrix" do
- b = @a.clone
- @a.should_not equal(b)
- b.should be_kind_of(Matrix)
- b.should == @a
- 0.upto(@a.row_size - 1) do |i|
- @a.row(i).should_not equal(b.row(i))
- end
+ it "returns a shallow copy of the matrix" do
+ b = @a.clone
+ @a.should_not equal(b)
+ b.should be_kind_of(Matrix)
+ b.should == @a
+ 0.upto(@a.row_size - 1) do |i|
+ @a.row(i).should_not equal(b.row(i))
end
+ end
- describe "for a subclass of Matrix" do
- it "returns an instance of that subclass" do
- MatrixSub.ins.clone.should be_an_instance_of(MatrixSub)
- end
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.ins.clone.should be_an_instance_of(MatrixSub)
end
end
end
diff --git a/spec/ruby/library/matrix/coerce_spec.rb b/spec/ruby/library/matrix/coerce_spec.rb
index aa3a32765a..4022f00236 100644
--- a/spec/ruby/library/matrix/coerce_spec.rb
+++ b/spec/ruby/library/matrix/coerce_spec.rb
@@ -1,11 +1,8 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix#coerce" do
- it "allows the division of integer by a Matrix " do
- (1/Matrix[[0,1],[-1,0]]).should == Matrix[[0,-1],[1,0]]
- end
+describe "Matrix#coerce" do
+ it "allows the division of integer by a Matrix " do
+ (1/Matrix[[0,1],[-1,0]]).should == Matrix[[0,-1],[1,0]]
end
end
diff --git a/spec/ruby/library/matrix/collect_spec.rb b/spec/ruby/library/matrix/collect_spec.rb
index 66ec3486c8..bba640213b 100644
--- a/spec/ruby/library/matrix/collect_spec.rb
+++ b/spec/ruby/library/matrix/collect_spec.rb
@@ -1,9 +1,6 @@
require_relative '../../spec_helper'
+require_relative 'shared/collect'
-ruby_version_is ""..."3.1" do
- require_relative 'shared/collect'
-
- describe "Matrix#collect" do
- it_behaves_like :collect, :collect
- end
+describe "Matrix#collect" do
+ it_behaves_like :collect, :collect
end
diff --git a/spec/ruby/library/matrix/column_size_spec.rb b/spec/ruby/library/matrix/column_size_spec.rb
index e7b42b101e..041914e5b9 100644
--- a/spec/ruby/library/matrix/column_size_spec.rb
+++ b/spec/ruby/library/matrix/column_size_spec.rb
@@ -1,16 +1,13 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix#column_size" do
- it "returns the number of columns" do
- Matrix[ [1,2], [3,4] ].column_size.should == 2
- end
+describe "Matrix#column_size" do
+ it "returns the number of columns" do
+ Matrix[ [1,2], [3,4] ].column_size.should == 2
+ end
- it "returns 0 for empty matrices" do
- Matrix[ [], [] ].column_size.should == 0
- Matrix[ ].column_size.should == 0
- end
+ it "returns 0 for empty matrices" do
+ Matrix[ [], [] ].column_size.should == 0
+ Matrix[ ].column_size.should == 0
end
end
diff --git a/spec/ruby/library/matrix/column_spec.rb b/spec/ruby/library/matrix/column_spec.rb
index 98a767ad08..1f3c80964a 100644
--- a/spec/ruby/library/matrix/column_spec.rb
+++ b/spec/ruby/library/matrix/column_spec.rb
@@ -1,38 +1,35 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix#column" do
- before :all do
- @m = Matrix[[1,2,3], [2,3,4]]
- end
+describe "Matrix#column" do
+ before :all do
+ @m = Matrix[[1,2,3], [2,3,4]]
+ end
- it "returns a Vector when called without a block" do
- @m.column(1).should == Vector[2,3]
- end
+ it "returns a Vector when called without a block" do
+ @m.column(1).should == Vector[2,3]
+ end
- it "yields each element in the column to the block" do
- a = []
- @m.column(1) {|n| a << n }
- a.should == [2,3]
- end
+ it "yields each element in the column to the block" do
+ a = []
+ @m.column(1) {|n| a << n }
+ a.should == [2,3]
+ end
- it "counts backwards for negative argument" do
- @m.column(-1).should == Vector[3, 4]
- end
+ it "counts backwards for negative argument" do
+ @m.column(-1).should == Vector[3, 4]
+ end
- it "returns self when called with a block" do
- @m.column(0) { |x| x }.should equal(@m)
- end
+ it "returns self when called with a block" do
+ @m.column(0) { |x| x }.should equal(@m)
+ end
- it "returns nil when out of bounds" do
- @m.column(3).should == nil
- end
+ it "returns nil when out of bounds" do
+ @m.column(3).should == nil
+ end
- it "never yields when out of bounds" do
- -> { @m.column(3){ raise } }.should_not raise_error
- -> { @m.column(-4){ raise } }.should_not raise_error
- end
+ it "never yields when out of bounds" do
+ -> { @m.column(3){ raise } }.should_not raise_error
+ -> { @m.column(-4){ raise } }.should_not raise_error
end
end
diff --git a/spec/ruby/library/matrix/column_vector_spec.rb b/spec/ruby/library/matrix/column_vector_spec.rb
index afdeaced47..47e866a8d5 100644
--- a/spec/ruby/library/matrix/column_vector_spec.rb
+++ b/spec/ruby/library/matrix/column_vector_spec.rb
@@ -1,28 +1,25 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require_relative 'fixtures/classes'
- require 'matrix'
+describe "Matrix.column_vector" do
- describe "Matrix.column_vector" do
-
- it "returns a single column Matrix when called with an Array" do
- m = Matrix.column_vector([4,5,6])
- m.should be_an_instance_of(Matrix)
- m.should == Matrix[ [4],[5],[6] ]
- end
+ it "returns a single column Matrix when called with an Array" do
+ m = Matrix.column_vector([4,5,6])
+ m.should be_an_instance_of(Matrix)
+ m.should == Matrix[ [4],[5],[6] ]
+ end
- it "returns an empty Matrix when called with an empty Array" do
- m = Matrix.column_vector([])
- m.should be_an_instance_of(Matrix)
- m.row_size.should == 0
- m.column_size.should == 1
- end
+ it "returns an empty Matrix when called with an empty Array" do
+ m = Matrix.column_vector([])
+ m.should be_an_instance_of(Matrix)
+ m.row_size.should == 0
+ m.column_size.should == 1
+ end
- describe "for a subclass of Matrix" do
- it "returns an instance of that subclass" do
- MatrixSub.column_vector([4,5,6]).should be_an_instance_of(MatrixSub)
- end
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.column_vector([4,5,6]).should be_an_instance_of(MatrixSub)
end
end
end
diff --git a/spec/ruby/library/matrix/column_vectors_spec.rb b/spec/ruby/library/matrix/column_vectors_spec.rb
index 7bec095b9a..b0cb6f914c 100644
--- a/spec/ruby/library/matrix/column_vectors_spec.rb
+++ b/spec/ruby/library/matrix/column_vectors_spec.rb
@@ -1,29 +1,26 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
+describe "Matrix#column_vectors" do
- describe "Matrix#column_vectors" do
-
- before :each do
- @vectors = Matrix[ [1,2], [3,4] ].column_vectors
- end
-
- it "returns an Array" do
- Matrix[ [1,2], [3,4] ].column_vectors.should be_an_instance_of(Array)
- end
+ before :each do
+ @vectors = Matrix[ [1,2], [3,4] ].column_vectors
+ end
- it "returns an Array of Vectors" do
- @vectors.all? {|v| v.should be_an_instance_of(Vector)}
- end
+ it "returns an Array" do
+ Matrix[ [1,2], [3,4] ].column_vectors.should be_an_instance_of(Array)
+ end
- it "returns each column as a Vector" do
- @vectors.should == [Vector[1,3], Vector[2,4]]
- end
+ it "returns an Array of Vectors" do
+ @vectors.all? {|v| v.should be_an_instance_of(Vector)}
+ end
- it "returns an empty Array for empty matrices" do
- Matrix[ [] ].column_vectors.should == []
- end
+ it "returns each column as a Vector" do
+ @vectors.should == [Vector[1,3], Vector[2,4]]
+ end
+ it "returns an empty Array for empty matrices" do
+ Matrix[ [] ].column_vectors.should == []
end
+
end
diff --git a/spec/ruby/library/matrix/columns_spec.rb b/spec/ruby/library/matrix/columns_spec.rb
index 757086c14b..3095fdd7af 100644
--- a/spec/ruby/library/matrix/columns_spec.rb
+++ b/spec/ruby/library/matrix/columns_spec.rb
@@ -1,45 +1,42 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require_relative 'fixtures/classes'
- require 'matrix'
-
- describe "Matrix.columns" do
- before :each do
- @a = [1, 2]
- @b = [3, 4]
- @m = Matrix.columns([@a, @b])
- end
+describe "Matrix.columns" do
+ before :each do
+ @a = [1, 2]
+ @b = [3, 4]
+ @m = Matrix.columns([@a, @b])
+ end
- it "creates a Matrix from argument columns" do
- @m.should be_an_instance_of(Matrix)
- @m.column(0).to_a.should == @a
- @m.column(1).to_a.should == @b
- end
+ it "creates a Matrix from argument columns" do
+ @m.should be_an_instance_of(Matrix)
+ @m.column(0).to_a.should == @a
+ @m.column(1).to_a.should == @b
+ end
- it "accepts Vectors as argument columns" do
- m = Matrix.columns([Vector[*@a], Vector[*@b]])
- m.should == @m
- m.column(0).to_a.should == @a
- m.column(1).to_a.should == @b
- end
+ it "accepts Vectors as argument columns" do
+ m = Matrix.columns([Vector[*@a], Vector[*@b]])
+ m.should == @m
+ m.column(0).to_a.should == @a
+ m.column(1).to_a.should == @b
+ end
- it "handles empty matrices" do
- e = Matrix.columns([])
- e.row_size.should == 0
- e.column_size.should == 0
- e.should == Matrix[]
+ it "handles empty matrices" do
+ e = Matrix.columns([])
+ e.row_size.should == 0
+ e.column_size.should == 0
+ e.should == Matrix[]
- v = Matrix.columns([[],[],[]])
- v.row_size.should == 0
- v.column_size.should == 3
- v.should == Matrix[[], [], []].transpose
- end
+ v = Matrix.columns([[],[],[]])
+ v.row_size.should == 0
+ v.column_size.should == 3
+ v.should == Matrix[[], [], []].transpose
+ end
- describe "for a subclass of Matrix" do
- it "returns an instance of that subclass" do
- MatrixSub.columns([[1]]).should be_an_instance_of(MatrixSub)
- end
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.columns([[1]]).should be_an_instance_of(MatrixSub)
end
end
end
diff --git a/spec/ruby/library/matrix/conj_spec.rb b/spec/ruby/library/matrix/conj_spec.rb
index a922580399..ecee95c255 100644
--- a/spec/ruby/library/matrix/conj_spec.rb
+++ b/spec/ruby/library/matrix/conj_spec.rb
@@ -1,9 +1,6 @@
require_relative '../../spec_helper'
+require_relative 'shared/conjugate'
-ruby_version_is ""..."3.1" do
- require_relative 'shared/conjugate'
-
- describe "Matrix#conj" do
- it_behaves_like :matrix_conjugate, :conj
- end
+describe "Matrix#conj" do
+ it_behaves_like :matrix_conjugate, :conj
end
diff --git a/spec/ruby/library/matrix/conjugate_spec.rb b/spec/ruby/library/matrix/conjugate_spec.rb
index b99792a24b..682bd41d94 100644
--- a/spec/ruby/library/matrix/conjugate_spec.rb
+++ b/spec/ruby/library/matrix/conjugate_spec.rb
@@ -1,9 +1,6 @@
require_relative '../../spec_helper'
+require_relative 'shared/conjugate'
-ruby_version_is ""..."3.1" do
- require_relative 'shared/conjugate'
-
- describe "Matrix#conjugate" do
- it_behaves_like :matrix_conjugate, :conjugate
- end
+describe "Matrix#conjugate" do
+ it_behaves_like :matrix_conjugate, :conjugate
end
diff --git a/spec/ruby/library/matrix/constructor_spec.rb b/spec/ruby/library/matrix/constructor_spec.rb
index d8224b4430..70d77babbb 100644
--- a/spec/ruby/library/matrix/constructor_spec.rb
+++ b/spec/ruby/library/matrix/constructor_spec.rb
@@ -1,68 +1,65 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require_relative 'fixtures/classes'
- require 'matrix'
+describe "Matrix.[]" do
- describe "Matrix.[]" do
-
- it "requires arrays as parameters" do
- -> { Matrix[5] }.should raise_error(TypeError)
- -> { Matrix[nil] }.should raise_error(TypeError)
- -> { Matrix[1..2] }.should raise_error(TypeError)
- -> { Matrix[[1, 2], 3] }.should raise_error(TypeError)
- end
+ it "requires arrays as parameters" do
+ -> { Matrix[5] }.should raise_error(TypeError)
+ -> { Matrix[nil] }.should raise_error(TypeError)
+ -> { Matrix[1..2] }.should raise_error(TypeError)
+ -> { Matrix[[1, 2], 3] }.should raise_error(TypeError)
+ end
- it "creates an empty Matrix with no arguments" do
- m = Matrix[]
- m.column_size.should == 0
- m.row_size.should == 0
- end
+ it "creates an empty Matrix with no arguments" do
+ m = Matrix[]
+ m.column_size.should == 0
+ m.row_size.should == 0
+ end
- it "raises for non-rectangular matrices" do
- ->{ Matrix[ [0], [0,1] ] }.should \
- raise_error(Matrix::ErrDimensionMismatch)
- ->{ Matrix[ [0,1], [0,1,2], [0,1] ]}.should \
- raise_error(Matrix::ErrDimensionMismatch)
- end
+ it "raises for non-rectangular matrices" do
+ ->{ Matrix[ [0], [0,1] ] }.should \
+ raise_error(Matrix::ErrDimensionMismatch)
+ ->{ Matrix[ [0,1], [0,1,2], [0,1] ]}.should \
+ raise_error(Matrix::ErrDimensionMismatch)
+ end
- it "accepts vector arguments" do
- a = Matrix[Vector[1, 2], Vector[3, 4]]
- a.should be_an_instance_of(Matrix)
- a.should == Matrix[ [1, 2], [3, 4] ]
- end
+ it "accepts vector arguments" do
+ a = Matrix[Vector[1, 2], Vector[3, 4]]
+ a.should be_an_instance_of(Matrix)
+ a.should == Matrix[ [1, 2], [3, 4] ]
+ end
- it "tries to calls :to_ary on arguments" do
- array = mock('ary')
- array.should_receive(:to_ary).and_return([1,2])
- Matrix[array, [3,4] ].should == Matrix[ [1,2], [3,4] ]
- end
+ it "tries to calls :to_ary on arguments" do
+ array = mock('ary')
+ array.should_receive(:to_ary).and_return([1,2])
+ Matrix[array, [3,4] ].should == Matrix[ [1,2], [3,4] ]
+ end
- it "returns a Matrix object" do
- Matrix[ [1] ].should be_an_instance_of(Matrix)
- end
+ it "returns a Matrix object" do
+ Matrix[ [1] ].should be_an_instance_of(Matrix)
+ end
- it "can create an nxn Matrix" do
- m = Matrix[ [20,30], [40.5, 9] ]
- m.row_size.should == 2
- m.column_size.should == 2
- m.column(0).should == Vector[20, 40.5]
- m.column(1).should == Vector[30, 9]
- m.row(0).should == Vector[20, 30]
- m.row(1).should == Vector[40.5, 9]
- end
+ it "can create an nxn Matrix" do
+ m = Matrix[ [20,30], [40.5, 9] ]
+ m.row_size.should == 2
+ m.column_size.should == 2
+ m.column(0).should == Vector[20, 40.5]
+ m.column(1).should == Vector[30, 9]
+ m.row(0).should == Vector[20, 30]
+ m.row(1).should == Vector[40.5, 9]
+ end
- it "can create a 0xn Matrix" do
- m = Matrix[ [], [], [] ]
- m.row_size.should == 3
- m.column_size.should == 0
- end
+ it "can create a 0xn Matrix" do
+ m = Matrix[ [], [], [] ]
+ m.row_size.should == 3
+ m.column_size.should == 0
+ end
- describe "for a subclass of Matrix" do
- it "returns an instance of that subclass" do
- MatrixSub[ [20,30], [40.5, 9] ].should be_an_instance_of(MatrixSub)
- end
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub[ [20,30], [40.5, 9] ].should be_an_instance_of(MatrixSub)
end
end
end
diff --git a/spec/ruby/library/matrix/det_spec.rb b/spec/ruby/library/matrix/det_spec.rb
index 7d3d547735..aa7086cacf 100644
--- a/spec/ruby/library/matrix/det_spec.rb
+++ b/spec/ruby/library/matrix/det_spec.rb
@@ -1,10 +1,7 @@
require_relative '../../spec_helper'
+require_relative 'shared/determinant'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require_relative 'shared/determinant'
- require 'matrix'
-
- describe "Matrix#det" do
- it_behaves_like :determinant, :det
- end
+describe "Matrix#det" do
+ it_behaves_like :determinant, :det
end
diff --git a/spec/ruby/library/matrix/determinant_spec.rb b/spec/ruby/library/matrix/determinant_spec.rb
index bfd91fcf68..825c9907b1 100644
--- a/spec/ruby/library/matrix/determinant_spec.rb
+++ b/spec/ruby/library/matrix/determinant_spec.rb
@@ -1,10 +1,7 @@
require_relative '../../spec_helper'
+require_relative 'shared/determinant'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require_relative 'shared/determinant'
- require 'matrix'
-
- describe "Matrix#determinant" do
- it_behaves_like :determinant, :determinant
- end
+describe "Matrix#determinant" do
+ it_behaves_like :determinant, :determinant
end
diff --git a/spec/ruby/library/matrix/diagonal_spec.rb b/spec/ruby/library/matrix/diagonal_spec.rb
index 8c82433fde..ef9738e73e 100644
--- a/spec/ruby/library/matrix/diagonal_spec.rb
+++ b/spec/ruby/library/matrix/diagonal_spec.rb
@@ -1,75 +1,72 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require_relative 'fixtures/classes'
- require 'matrix'
-
- describe "Matrix.diagonal" do
- before :each do
- @m = Matrix.diagonal(10, 11, 12, 13, 14)
- end
+describe "Matrix.diagonal" do
+ before :each do
+ @m = Matrix.diagonal(10, 11, 12, 13, 14)
+ end
- it "returns an object of type Matrix" do
- @m.should be_kind_of(Matrix)
- end
+ it "returns an object of type Matrix" do
+ @m.should be_kind_of(Matrix)
+ end
- it "returns a square Matrix of the right size" do
- @m.column_size.should == 5
- @m.row_size.should == 5
- end
+ it "returns a square Matrix of the right size" do
+ @m.column_size.should == 5
+ @m.row_size.should == 5
+ end
- it "sets the diagonal to the arguments" do
- (0..4).each do |i|
- @m[i, i].should == i + 10
- end
+ it "sets the diagonal to the arguments" do
+ (0..4).each do |i|
+ @m[i, i].should == i + 10
end
+ end
- it "fills all non-diagonal cells with 0" do
- (0..4).each do |i|
- (0..4).each do |j|
- if i != j
- @m[i, j].should == 0
- end
+ it "fills all non-diagonal cells with 0" do
+ (0..4).each do |i|
+ (0..4).each do |j|
+ if i != j
+ @m[i, j].should == 0
end
end
end
+ end
- describe "for a subclass of Matrix" do
- it "returns an instance of that subclass" do
- MatrixSub.diagonal(1).should be_an_instance_of(MatrixSub)
- end
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.diagonal(1).should be_an_instance_of(MatrixSub)
end
end
+end
- describe "Matrix.diagonal?" do
- it "returns true for a diagonal Matrix" do
- Matrix.diagonal([1, 2, 3]).diagonal?.should be_true
- end
+describe "Matrix.diagonal?" do
+ it "returns true for a diagonal Matrix" do
+ Matrix.diagonal([1, 2, 3]).diagonal?.should be_true
+ end
- it "returns true for a zero square Matrix" do
- Matrix.zero(3).diagonal?.should be_true
- end
+ it "returns true for a zero square Matrix" do
+ Matrix.zero(3).diagonal?.should be_true
+ end
- it "returns false for a non diagonal square Matrix" do
- Matrix[[0, 1], [0, 0]].diagonal?.should be_false
- Matrix[[1, 2, 3], [1, 2, 3], [1, 2, 3]].diagonal?.should be_false
- end
+ it "returns false for a non diagonal square Matrix" do
+ Matrix[[0, 1], [0, 0]].diagonal?.should be_false
+ Matrix[[1, 2, 3], [1, 2, 3], [1, 2, 3]].diagonal?.should be_false
+ end
- it "returns true for an empty 0x0 matrix" do
- Matrix.empty(0,0).diagonal?.should be_true
- end
+ it "returns true for an empty 0x0 matrix" do
+ Matrix.empty(0,0).diagonal?.should be_true
+ end
- it "raises an error for rectangular matrices" do
- [
- Matrix[[0], [0]],
- Matrix[[0, 0]],
- Matrix.empty(0, 2),
- Matrix.empty(2, 0),
- ].each do |rectangular_matrix|
- -> {
- rectangular_matrix.diagonal?
- }.should raise_error(Matrix::ErrDimensionMismatch)
- end
+ it "raises an error for rectangular matrices" do
+ [
+ Matrix[[0], [0]],
+ Matrix[[0, 0]],
+ Matrix.empty(0, 2),
+ Matrix.empty(2, 0),
+ ].each do |rectangular_matrix|
+ -> {
+ rectangular_matrix.diagonal?
+ }.should raise_error(Matrix::ErrDimensionMismatch)
end
end
end
diff --git a/spec/ruby/library/matrix/divide_spec.rb b/spec/ruby/library/matrix/divide_spec.rb
index 68e6f1fde5..2e3bb85bf6 100644
--- a/spec/ruby/library/matrix/divide_spec.rb
+++ b/spec/ruby/library/matrix/divide_spec.rb
@@ -1,57 +1,54 @@
require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
+
+describe "Matrix#/" do
+ before :each do
+ @a = Matrix[ [1, 2], [3, 4] ]
+ @b = Matrix[ [4, 5], [6, 7] ]
+ @c = Matrix[ [1.2, 2.4], [3.6, 4.8] ]
+ end
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/classes'
- require 'matrix'
+ it "returns the result of dividing self by another Matrix" do
+ (@a / @b).should be_close_to_matrix([[2.5, -1.5], [1.5, -0.5]])
+ end
- describe "Matrix#/" do
- before :each do
- @a = Matrix[ [1, 2], [3, 4] ]
- @b = Matrix[ [4, 5], [6, 7] ]
- @c = Matrix[ [1.2, 2.4], [3.6, 4.8] ]
+ # Guard against the Mathn library
+ guard -> { !defined?(Math.rsqrt) } do
+ it "returns the result of dividing self by a Fixnum" do
+ (@a / 2).should == Matrix[ [0, 1], [1, 2] ]
end
- it "returns the result of dividing self by another Matrix" do
- (@a / @b).should be_close_to_matrix([[2.5, -1.5], [1.5, -0.5]])
- end
-
- # Guard against the Mathn library
- guard -> { !defined?(Math.rsqrt) } do
- it "returns the result of dividing self by a Fixnum" do
- (@a / 2).should == Matrix[ [0, 1], [1, 2] ]
- end
-
- it "returns the result of dividing self by a Bignum" do
- (@a / bignum_value).should == Matrix[ [0, 0], [0, 0] ]
- end
+ it "returns the result of dividing self by a Bignum" do
+ (@a / bignum_value).should == Matrix[ [0, 0], [0, 0] ]
end
+ end
- it "returns the result of dividing self by a Float" do
- (@c / 1.2).should == Matrix[ [1, 2], [3, 4] ]
- end
+ it "returns the result of dividing self by a Float" do
+ (@c / 1.2).should == Matrix[ [1, 2], [3, 4] ]
+ end
- it "raises a Matrix::ErrDimensionMismatch if the matrices are different sizes" do
- -> { @a / Matrix[ [1] ] }.should raise_error(Matrix::ErrDimensionMismatch)
- end
+ it "raises a Matrix::ErrDimensionMismatch if the matrices are different sizes" do
+ -> { @a / Matrix[ [1] ] }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
- it "returns an instance of Matrix" do
- (@a / @b).should be_kind_of(Matrix)
- end
+ it "returns an instance of Matrix" do
+ (@a / @b).should be_kind_of(Matrix)
+ end
- describe "for a subclass of Matrix" do
- it "returns an instance of that subclass" do
- m = MatrixSub.ins
- (m/m).should be_an_instance_of(MatrixSub)
- (m/1).should be_an_instance_of(MatrixSub)
- end
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ m = MatrixSub.ins
+ (m/m).should be_an_instance_of(MatrixSub)
+ (m/1).should be_an_instance_of(MatrixSub)
end
+ end
- it "raises a TypeError if other is of wrong type" do
- -> { @a / nil }.should raise_error(TypeError)
- -> { @a / "a" }.should raise_error(TypeError)
- -> { @a / [ [1, 2] ] }.should raise_error(TypeError)
- -> { @a / Object.new }.should raise_error(TypeError)
- end
+ it "raises a TypeError if other is of wrong type" do
+ -> { @a / nil }.should raise_error(TypeError)
+ -> { @a / "a" }.should raise_error(TypeError)
+ -> { @a / [ [1, 2] ] }.should raise_error(TypeError)
+ -> { @a / Object.new }.should raise_error(TypeError)
end
end
diff --git a/spec/ruby/library/matrix/each_spec.rb b/spec/ruby/library/matrix/each_spec.rb
index d2b13c80b6..f3b0f01867 100644
--- a/spec/ruby/library/matrix/each_spec.rb
+++ b/spec/ruby/library/matrix/each_spec.rb
@@ -1,77 +1,74 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix#each" do
- before :all do
- @m = Matrix[ [1, 2, 3], [4, 5, 6] ]
- @result = (1..6).to_a
- end
+describe "Matrix#each" do
+ before :all do
+ @m = Matrix[ [1, 2, 3], [4, 5, 6] ]
+ @result = (1..6).to_a
+ end
- it "returns an Enumerator when called without a block" do
- enum = @m.each
- enum.should be_an_instance_of(Enumerator)
- enum.to_a.should == @result
- end
+ it "returns an Enumerator when called without a block" do
+ enum = @m.each
+ enum.should be_an_instance_of(Enumerator)
+ enum.to_a.should == @result
+ end
- it "returns self" do
- @m.each{}.should equal(@m)
- end
+ it "returns self" do
+ @m.each{}.should equal(@m)
+ end
- it "yields the elements starting with the those of the first row" do
- a = []
- @m.each {|x| a << x}
- a.should == @result
- end
+ it "yields the elements starting with the those of the first row" do
+ a = []
+ @m.each {|x| a << x}
+ a.should == @result
end
+end
- describe "Matrix#each with an argument" do
- before :all do
- @m = Matrix[ [1, 2, 3, 4], [5, 6, 7, 8] ]
- @t = Matrix[ [1, 2], [3, 4], [5, 6], [7, 8] ]
- end
+describe "Matrix#each with an argument" do
+ before :all do
+ @m = Matrix[ [1, 2, 3, 4], [5, 6, 7, 8] ]
+ @t = Matrix[ [1, 2], [3, 4], [5, 6], [7, 8] ]
+ end
- it "raises an ArgumentError for unrecognized argument" do
- -> {
- @m.each("all"){}
- }.should raise_error(ArgumentError)
- -> {
- @m.each(nil){}
- }.should raise_error(ArgumentError)
- -> {
- @m.each(:left){}
- }.should raise_error(ArgumentError)
- end
+ it "raises an ArgumentError for unrecognized argument" do
+ -> {
+ @m.each("all"){}
+ }.should raise_error(ArgumentError)
+ -> {
+ @m.each(nil){}
+ }.should raise_error(ArgumentError)
+ -> {
+ @m.each(:left){}
+ }.should raise_error(ArgumentError)
+ end
- it "yields the rights elements when passed :diagonal" do
- @m.each(:diagonal).to_a.should == [1, 6]
- @t.each(:diagonal).to_a.should == [1, 4]
- end
+ it "yields the rights elements when passed :diagonal" do
+ @m.each(:diagonal).to_a.should == [1, 6]
+ @t.each(:diagonal).to_a.should == [1, 4]
+ end
- it "yields the rights elements when passed :off_diagonal" do
- @m.each(:off_diagonal).to_a.should == [2, 3, 4, 5, 7, 8]
- @t.each(:off_diagonal).to_a.should == [2, 3, 5, 6, 7, 8]
- end
+ it "yields the rights elements when passed :off_diagonal" do
+ @m.each(:off_diagonal).to_a.should == [2, 3, 4, 5, 7, 8]
+ @t.each(:off_diagonal).to_a.should == [2, 3, 5, 6, 7, 8]
+ end
- it "yields the rights elements when passed :lower" do
- @m.each(:lower).to_a.should == [1, 5, 6]
- @t.each(:lower).to_a.should == [1, 3, 4, 5, 6, 7, 8]
- end
+ it "yields the rights elements when passed :lower" do
+ @m.each(:lower).to_a.should == [1, 5, 6]
+ @t.each(:lower).to_a.should == [1, 3, 4, 5, 6, 7, 8]
+ end
- it "yields the rights elements when passed :strict_lower" do
- @m.each(:strict_lower).to_a.should == [5]
- @t.each(:strict_lower).to_a.should == [3, 5, 6, 7, 8]
- end
+ it "yields the rights elements when passed :strict_lower" do
+ @m.each(:strict_lower).to_a.should == [5]
+ @t.each(:strict_lower).to_a.should == [3, 5, 6, 7, 8]
+ end
- it "yields the rights elements when passed :strict_upper" do
- @m.each(:strict_upper).to_a.should == [2, 3, 4, 7, 8]
- @t.each(:strict_upper).to_a.should == [2]
- end
+ it "yields the rights elements when passed :strict_upper" do
+ @m.each(:strict_upper).to_a.should == [2, 3, 4, 7, 8]
+ @t.each(:strict_upper).to_a.should == [2]
+ end
- it "yields the rights elements when passed :upper" do
- @m.each(:upper).to_a.should == [1, 2, 3, 4, 6, 7, 8]
- @t.each(:upper).to_a.should == [1, 2, 4]
- end
+ it "yields the rights elements when passed :upper" do
+ @m.each(:upper).to_a.should == [1, 2, 3, 4, 6, 7, 8]
+ @t.each(:upper).to_a.should == [1, 2, 4]
end
end
diff --git a/spec/ruby/library/matrix/each_with_index_spec.rb b/spec/ruby/library/matrix/each_with_index_spec.rb
index 59549f77b7..a005b88621 100644
--- a/spec/ruby/library/matrix/each_with_index_spec.rb
+++ b/spec/ruby/library/matrix/each_with_index_spec.rb
@@ -1,84 +1,81 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix#each_with_index" do
- before :all do
- @m = Matrix[ [1, 2, 3], [4, 5, 6] ]
- @result = [
- [1, 0, 0],
- [2, 0, 1],
- [3, 0, 2],
- [4, 1, 0],
- [5, 1, 1],
- [6, 1, 2]
- ]
- end
+describe "Matrix#each_with_index" do
+ before :all do
+ @m = Matrix[ [1, 2, 3], [4, 5, 6] ]
+ @result = [
+ [1, 0, 0],
+ [2, 0, 1],
+ [3, 0, 2],
+ [4, 1, 0],
+ [5, 1, 1],
+ [6, 1, 2]
+ ]
+ end
- it "returns an Enumerator when called without a block" do
- enum = @m.each_with_index
- enum.should be_an_instance_of(Enumerator)
- enum.to_a.should == @result
- end
+ it "returns an Enumerator when called without a block" do
+ enum = @m.each_with_index
+ enum.should be_an_instance_of(Enumerator)
+ enum.to_a.should == @result
+ end
- it "returns self" do
- @m.each_with_index{}.should equal(@m)
- end
+ it "returns self" do
+ @m.each_with_index{}.should equal(@m)
+ end
- it "yields the elements starting with the those of the first row" do
- a = []
- @m.each_with_index {|x, r, c| a << [x, r, c]}
- a.should == @result
- end
+ it "yields the elements starting with the those of the first row" do
+ a = []
+ @m.each_with_index {|x, r, c| a << [x, r, c]}
+ a.should == @result
end
+end
- describe "Matrix#each_with_index with an argument" do
- before :all do
- @m = Matrix[ [1, 2, 3, 4], [5, 6, 7, 8] ]
- @t = Matrix[ [1, 2], [3, 4], [5, 6], [7, 8] ]
- end
+describe "Matrix#each_with_index with an argument" do
+ before :all do
+ @m = Matrix[ [1, 2, 3, 4], [5, 6, 7, 8] ]
+ @t = Matrix[ [1, 2], [3, 4], [5, 6], [7, 8] ]
+ end
- it "raises an ArgumentError for unrecognized argument" do
- -> {
- @m.each_with_index("all"){}
- }.should raise_error(ArgumentError)
- -> {
- @m.each_with_index(nil){}
- }.should raise_error(ArgumentError)
- -> {
- @m.each_with_index(:left){}
- }.should raise_error(ArgumentError)
- end
+ it "raises an ArgumentError for unrecognized argument" do
+ -> {
+ @m.each_with_index("all"){}
+ }.should raise_error(ArgumentError)
+ -> {
+ @m.each_with_index(nil){}
+ }.should raise_error(ArgumentError)
+ -> {
+ @m.each_with_index(:left){}
+ }.should raise_error(ArgumentError)
+ end
- it "yields the rights elements when passed :diagonal" do
- @m.each_with_index(:diagonal).to_a.should == [[1, 0, 0], [6, 1, 1]]
- @t.each_with_index(:diagonal).to_a.should == [[1, 0, 0], [4, 1, 1]]
- end
+ it "yields the rights elements when passed :diagonal" do
+ @m.each_with_index(:diagonal).to_a.should == [[1, 0, 0], [6, 1, 1]]
+ @t.each_with_index(:diagonal).to_a.should == [[1, 0, 0], [4, 1, 1]]
+ end
- it "yields the rights elements when passed :off_diagonal" do
- @m.each_with_index(:off_diagonal).to_a.should == [[2, 0, 1], [3, 0, 2], [4, 0, 3], [5, 1, 0], [7, 1, 2], [8, 1, 3]]
- @t.each_with_index(:off_diagonal).to_a.should == [[2, 0, 1], [3, 1, 0], [5, 2, 0], [6, 2, 1], [7, 3, 0], [8, 3, 1]]
- end
+ it "yields the rights elements when passed :off_diagonal" do
+ @m.each_with_index(:off_diagonal).to_a.should == [[2, 0, 1], [3, 0, 2], [4, 0, 3], [5, 1, 0], [7, 1, 2], [8, 1, 3]]
+ @t.each_with_index(:off_diagonal).to_a.should == [[2, 0, 1], [3, 1, 0], [5, 2, 0], [6, 2, 1], [7, 3, 0], [8, 3, 1]]
+ end
- it "yields the rights elements when passed :lower" do
- @m.each_with_index(:lower).to_a.should == [[1, 0, 0], [5, 1, 0], [6, 1, 1]]
- @t.each_with_index(:lower).to_a.should == [[1, 0, 0], [3, 1, 0], [4, 1, 1], [5, 2, 0], [6, 2, 1], [7, 3, 0], [8, 3, 1]]
- end
+ it "yields the rights elements when passed :lower" do
+ @m.each_with_index(:lower).to_a.should == [[1, 0, 0], [5, 1, 0], [6, 1, 1]]
+ @t.each_with_index(:lower).to_a.should == [[1, 0, 0], [3, 1, 0], [4, 1, 1], [5, 2, 0], [6, 2, 1], [7, 3, 0], [8, 3, 1]]
+ end
- it "yields the rights elements when passed :strict_lower" do
- @m.each_with_index(:strict_lower).to_a.should == [[5, 1, 0]]
- @t.each_with_index(:strict_lower).to_a.should == [[3, 1, 0], [5, 2, 0], [6, 2, 1], [7, 3, 0], [8, 3, 1]]
- end
+ it "yields the rights elements when passed :strict_lower" do
+ @m.each_with_index(:strict_lower).to_a.should == [[5, 1, 0]]
+ @t.each_with_index(:strict_lower).to_a.should == [[3, 1, 0], [5, 2, 0], [6, 2, 1], [7, 3, 0], [8, 3, 1]]
+ end
- it "yields the rights elements when passed :strict_upper" do
- @m.each_with_index(:strict_upper).to_a.should == [[2, 0, 1], [3, 0, 2], [4, 0, 3], [7, 1, 2], [8, 1, 3]]
- @t.each_with_index(:strict_upper).to_a.should == [[2, 0, 1]]
- end
+ it "yields the rights elements when passed :strict_upper" do
+ @m.each_with_index(:strict_upper).to_a.should == [[2, 0, 1], [3, 0, 2], [4, 0, 3], [7, 1, 2], [8, 1, 3]]
+ @t.each_with_index(:strict_upper).to_a.should == [[2, 0, 1]]
+ end
- it "yields the rights elements when passed :upper" do
- @m.each_with_index(:upper).to_a.should == [[1, 0, 0], [2, 0, 1], [3, 0, 2], [4, 0, 3], [6, 1, 1], [7, 1, 2], [8, 1, 3]]
- @t.each_with_index(:upper).to_a.should == [[1, 0, 0], [2, 0, 1], [4, 1, 1]]
- end
+ it "yields the rights elements when passed :upper" do
+ @m.each_with_index(:upper).to_a.should == [[1, 0, 0], [2, 0, 1], [3, 0, 2], [4, 0, 3], [6, 1, 1], [7, 1, 2], [8, 1, 3]]
+ @t.each_with_index(:upper).to_a.should == [[1, 0, 0], [2, 0, 1], [4, 1, 1]]
end
end
diff --git a/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvalue_matrix_spec.rb b/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvalue_matrix_spec.rb
index f9ffca0123..67f9dd1c19 100644
--- a/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvalue_matrix_spec.rb
+++ b/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvalue_matrix_spec.rb
@@ -1,12 +1,9 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix::EigenvalueDecomposition#eigenvalue_matrix" do
- it "returns a diagonal matrix with the eigenvalues on the diagonal" do
- Matrix[[14, 16], [-6, -6]].eigensystem.eigenvalue_matrix.should == Matrix[[6, 0],[0, 2]]
- Matrix[[1, 1], [-1, 1]].eigensystem.eigenvalue_matrix.should == Matrix[[Complex(1,1), 0],[0, Complex(1,-1)]]
- end
+describe "Matrix::EigenvalueDecomposition#eigenvalue_matrix" do
+ it "returns a diagonal matrix with the eigenvalues on the diagonal" do
+ Matrix[[14, 16], [-6, -6]].eigensystem.eigenvalue_matrix.should == Matrix[[6, 0],[0, 2]]
+ Matrix[[1, 1], [-1, 1]].eigensystem.eigenvalue_matrix.should == Matrix[[Complex(1,1), 0],[0, Complex(1,-1)]]
end
end
diff --git a/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvalues_spec.rb b/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvalues_spec.rb
index 650d43d90f..7552b7616c 100644
--- a/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvalues_spec.rb
+++ b/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvalues_spec.rb
@@ -1,25 +1,22 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix::EigenvalueDecomposition#eigenvalues" do
- it "returns an array of complex eigenvalues for a rotation matrix" do
- Matrix[[ 1, 1],
- [-1, 1]].eigensystem.eigenvalues.sort_by{|v| v.imag}.should ==
- [ Complex(1, -1), Complex(1, 1)]
- end
+describe "Matrix::EigenvalueDecomposition#eigenvalues" do
+ it "returns an array of complex eigenvalues for a rotation matrix" do
+ Matrix[[ 1, 1],
+ [-1, 1]].eigensystem.eigenvalues.sort_by{|v| v.imag}.should ==
+ [ Complex(1, -1), Complex(1, 1)]
+ end
- it "returns an array of real eigenvalues for a symmetric matrix" do
- Matrix[[1, 2],
- [2, 1]].eigensystem.eigenvalues.sort.map!{|x| x.round(10)}.should ==
- [ -1, 3 ]
- end
+ it "returns an array of real eigenvalues for a symmetric matrix" do
+ Matrix[[1, 2],
+ [2, 1]].eigensystem.eigenvalues.sort.map!{|x| x.round(10)}.should ==
+ [ -1, 3 ]
+ end
- it "returns an array of real eigenvalues for a matrix" do
- Matrix[[14, 16],
- [-6, -6]].eigensystem.eigenvalues.sort.map!{|x| x.round(10)}.should ==
- [ 2, 6 ]
- end
+ it "returns an array of real eigenvalues for a matrix" do
+ Matrix[[14, 16],
+ [-6, -6]].eigensystem.eigenvalues.sort.map!{|x| x.round(10)}.should ==
+ [ 2, 6 ]
end
end
diff --git a/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvector_matrix_spec.rb b/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvector_matrix_spec.rb
index 57299ce69e..09f229ee15 100644
--- a/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvector_matrix_spec.rb
+++ b/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvector_matrix_spec.rb
@@ -1,23 +1,20 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix::EigenvalueDecomposition#eigenvector_matrix" do
- it "returns a complex eigenvector matrix given a rotation matrix" do
- # Fix me: should test for linearity, not for equality
- Matrix[[ 1, 1],
- [-1, 1]].eigensystem.eigenvector_matrix.should ==
- Matrix[[1, 1],
- [Complex(0, 1), Complex(0, -1)]]
- end
+describe "Matrix::EigenvalueDecomposition#eigenvector_matrix" do
+ it "returns a complex eigenvector matrix given a rotation matrix" do
+ # Fix me: should test for linearity, not for equality
+ Matrix[[ 1, 1],
+ [-1, 1]].eigensystem.eigenvector_matrix.should ==
+ Matrix[[1, 1],
+ [Complex(0, 1), Complex(0, -1)]]
+ end
- it "returns an real eigenvector matrix for a symmetric matrix" do
- # Fix me: should test for linearity, not for equality
- Matrix[[1, 2],
- [2, 1]].eigensystem.eigenvector_matrix.should ==
- Matrix[[0.7071067811865475, 0.7071067811865475],
- [-0.7071067811865475, 0.7071067811865475]]
- end
+ it "returns an real eigenvector matrix for a symmetric matrix" do
+ # Fix me: should test for linearity, not for equality
+ Matrix[[1, 2],
+ [2, 1]].eigensystem.eigenvector_matrix.should ==
+ Matrix[[0.7071067811865475, 0.7071067811865475],
+ [-0.7071067811865475, 0.7071067811865475]]
end
end
diff --git a/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvectors_spec.rb b/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvectors_spec.rb
index 54e6857bf3..2b6ce74ea8 100644
--- a/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvectors_spec.rb
+++ b/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvectors_spec.rb
@@ -1,25 +1,22 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix::EigenvalueDecomposition#eigenvectors" do
- it "returns an array of complex eigenvectors for a rotation matrix" do
- # Fix me: should test for linearity, not for equality
- Matrix[[ 1, 1],
- [-1, 1]].eigensystem.eigenvectors.should ==
- [ Vector[1, Complex(0, 1)],
- Vector[1, Complex(0, -1)]
- ]
- end
+describe "Matrix::EigenvalueDecomposition#eigenvectors" do
+ it "returns an array of complex eigenvectors for a rotation matrix" do
+ # Fix me: should test for linearity, not for equality
+ Matrix[[ 1, 1],
+ [-1, 1]].eigensystem.eigenvectors.should ==
+ [ Vector[1, Complex(0, 1)],
+ Vector[1, Complex(0, -1)]
+ ]
+ end
- it "returns an array of real eigenvectors for a symmetric matrix" do
- # Fix me: should test for linearity, not for equality
- Matrix[[1, 2],
- [2, 1]].eigensystem.eigenvectors.should ==
- [ Vector[0.7071067811865475, -0.7071067811865475],
- Vector[0.7071067811865475, 0.7071067811865475]
- ]
- end
+ it "returns an array of real eigenvectors for a symmetric matrix" do
+ # Fix me: should test for linearity, not for equality
+ Matrix[[1, 2],
+ [2, 1]].eigensystem.eigenvectors.should ==
+ [ Vector[0.7071067811865475, -0.7071067811865475],
+ Vector[0.7071067811865475, 0.7071067811865475]
+ ]
end
end
diff --git a/spec/ruby/library/matrix/eigenvalue_decomposition/initialize_spec.rb b/spec/ruby/library/matrix/eigenvalue_decomposition/initialize_spec.rb
index 02aad65c4b..8438f63133 100644
--- a/spec/ruby/library/matrix/eigenvalue_decomposition/initialize_spec.rb
+++ b/spec/ruby/library/matrix/eigenvalue_decomposition/initialize_spec.rb
@@ -1,27 +1,24 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix::EigenvalueDecomposition#initialize" do
- it "raises an error if argument is not a matrix" do
- -> {
- Matrix::EigenvalueDecomposition.new([[]])
- }.should raise_error(TypeError)
- -> {
- Matrix::EigenvalueDecomposition.new(42)
- }.should raise_error(TypeError)
- end
+describe "Matrix::EigenvalueDecomposition#initialize" do
+ it "raises an error if argument is not a matrix" do
+ -> {
+ Matrix::EigenvalueDecomposition.new([[]])
+ }.should raise_error(TypeError)
+ -> {
+ Matrix::EigenvalueDecomposition.new(42)
+ }.should raise_error(TypeError)
+ end
- it "raises an error if matrix is not square" do
- -> {
- Matrix::EigenvalueDecomposition.new(Matrix[[1, 2]])
- }.should raise_error(Matrix::ErrDimensionMismatch)
- end
+ it "raises an error if matrix is not square" do
+ -> {
+ Matrix::EigenvalueDecomposition.new(Matrix[[1, 2]])
+ }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
- it "never hangs" do
- m = Matrix[ [0,0,0,0,0], [0,0,0,0,1], [0,0,0,1,0], [1,1,0,0,1], [1,0,1,0,1] ]
- Matrix::EigenvalueDecomposition.new(m).should_not == "infinite loop"
- end
+ it "never hangs" do
+ m = Matrix[ [0,0,0,0,0], [0,0,0,0,1], [0,0,0,1,0], [1,1,0,0,1], [1,0,1,0,1] ]
+ Matrix::EigenvalueDecomposition.new(m).should_not == "infinite loop"
end
end
diff --git a/spec/ruby/library/matrix/eigenvalue_decomposition/to_a_spec.rb b/spec/ruby/library/matrix/eigenvalue_decomposition/to_a_spec.rb
index 352ae274b4..8be41a5720 100644
--- a/spec/ruby/library/matrix/eigenvalue_decomposition/to_a_spec.rb
+++ b/spec/ruby/library/matrix/eigenvalue_decomposition/to_a_spec.rb
@@ -1,21 +1,18 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix::EigenvalueDecomposition#to_a" do
- before :each do
- @a = Matrix[[14, 16], [-6, -6]]
- @e = Matrix::EigenvalueDecomposition.new(@a)
- end
+describe "Matrix::EigenvalueDecomposition#to_a" do
+ before :each do
+ @a = Matrix[[14, 16], [-6, -6]]
+ @e = Matrix::EigenvalueDecomposition.new(@a)
+ end
- it "returns an array of with [V, D, V.inv]" do
- @e.to_a.should == [@e.v, @e.d, @e.v_inv]
- end
+ it "returns an array of with [V, D, V.inv]" do
+ @e.to_a.should == [@e.v, @e.d, @e.v_inv]
+ end
- it "returns a factorization" do
- v, d, v_inv = @e.to_a
- (v * d * v_inv).map{|e| e.round(10)}.should == @a
- end
+ it "returns a factorization" do
+ v, d, v_inv = @e.to_a
+ (v * d * v_inv).map{|e| e.round(10)}.should == @a
end
end
diff --git a/spec/ruby/library/matrix/element_reference_spec.rb b/spec/ruby/library/matrix/element_reference_spec.rb
index 286ab851bd..b950d1c391 100644
--- a/spec/ruby/library/matrix/element_reference_spec.rb
+++ b/spec/ruby/library/matrix/element_reference_spec.rb
@@ -1,26 +1,23 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
+describe "Matrix#[]" do
- describe "Matrix#[]" do
-
- before :all do
- @m = Matrix[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]
- end
+ before :all do
+ @m = Matrix[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]
+ end
- it "returns element at (i, j)" do
- (0..3).each do |i|
- (0..2).each do |j|
- @m[i, j].should == (i * 3) + j
- end
+ it "returns element at (i, j)" do
+ (0..3).each do |i|
+ (0..2).each do |j|
+ @m[i, j].should == (i * 3) + j
end
end
+ end
- it "returns nil for an invalid index pair" do
- @m[8,1].should be_nil
- @m[1,8].should be_nil
- end
-
+ it "returns nil for an invalid index pair" do
+ @m[8,1].should be_nil
+ @m[1,8].should be_nil
end
+
end
diff --git a/spec/ruby/library/matrix/empty_spec.rb b/spec/ruby/library/matrix/empty_spec.rb
index 0b5d5ffe0f..5f294711db 100644
--- a/spec/ruby/library/matrix/empty_spec.rb
+++ b/spec/ruby/library/matrix/empty_spec.rb
@@ -1,71 +1,68 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require_relative 'fixtures/classes'
- require 'matrix'
-
- describe "Matrix#empty?" do
- it "returns true when the Matrix is empty" do
- Matrix[ ].empty?.should be_true
- Matrix[ [], [], [] ].empty?.should be_true
- Matrix[ [], [], [] ].transpose.empty?.should be_true
- end
-
- it "returns false when the Matrix has elements" do
- Matrix[ [1, 2] ].empty?.should be_false
- Matrix[ [1], [2] ].empty?.should be_false
- end
+describe "Matrix#empty?" do
+ it "returns true when the Matrix is empty" do
+ Matrix[ ].empty?.should be_true
+ Matrix[ [], [], [] ].empty?.should be_true
+ Matrix[ [], [], [] ].transpose.empty?.should be_true
+ end
- it "doesn't accept any parameter" do
- ->{
- Matrix[ [1, 2] ].empty?(42)
- }.should raise_error(ArgumentError)
- end
+ it "returns false when the Matrix has elements" do
+ Matrix[ [1, 2] ].empty?.should be_false
+ Matrix[ [1], [2] ].empty?.should be_false
end
- describe "Matrix.empty" do
- it "returns an empty matrix of the requested size" do
- m = Matrix.empty(3, 0)
- m.row_size.should == 3
- m.column_size.should == 0
+ it "doesn't accept any parameter" do
+ ->{
+ Matrix[ [1, 2] ].empty?(42)
+ }.should raise_error(ArgumentError)
+ end
+end
- m = Matrix.empty(0, 3)
- m.row_size.should == 0
- m.column_size.should == 3
- end
+describe "Matrix.empty" do
+ it "returns an empty matrix of the requested size" do
+ m = Matrix.empty(3, 0)
+ m.row_size.should == 3
+ m.column_size.should == 0
- it "has arguments defaulting to 0" do
- Matrix.empty.should == Matrix.empty(0, 0)
- Matrix.empty(42).should == Matrix.empty(42, 0)
- end
+ m = Matrix.empty(0, 3)
+ m.row_size.should == 0
+ m.column_size.should == 3
+ end
- it "does not accept more than two parameters" do
- ->{
- Matrix.empty(1, 2, 3)
- }.should raise_error(ArgumentError)
- end
+ it "has arguments defaulting to 0" do
+ Matrix.empty.should == Matrix.empty(0, 0)
+ Matrix.empty(42).should == Matrix.empty(42, 0)
+ end
- it "raises an error if both dimensions are > 0" do
- ->{
- Matrix.empty(1, 2)
- }.should raise_error(ArgumentError)
- end
+ it "does not accept more than two parameters" do
+ ->{
+ Matrix.empty(1, 2, 3)
+ }.should raise_error(ArgumentError)
+ end
- it "raises an error if any dimension is < 0" do
- ->{
- Matrix.empty(-2, 0)
- }.should raise_error(ArgumentError)
+ it "raises an error if both dimensions are > 0" do
+ ->{
+ Matrix.empty(1, 2)
+ }.should raise_error(ArgumentError)
+ end
- ->{
- Matrix.empty(0, -2)
- }.should raise_error(ArgumentError)
- end
+ it "raises an error if any dimension is < 0" do
+ ->{
+ Matrix.empty(-2, 0)
+ }.should raise_error(ArgumentError)
+ ->{
+ Matrix.empty(0, -2)
+ }.should raise_error(ArgumentError)
end
- describe "for a subclass of Matrix" do
- it "returns an instance of that subclass" do
- MatrixSub.empty(0, 1).should be_an_instance_of(MatrixSub)
- end
+end
+
+describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.empty(0, 1).should be_an_instance_of(MatrixSub)
end
end
diff --git a/spec/ruby/library/matrix/eql_spec.rb b/spec/ruby/library/matrix/eql_spec.rb
index d3f03dd6f3..ea26c3320d 100644
--- a/spec/ruby/library/matrix/eql_spec.rb
+++ b/spec/ruby/library/matrix/eql_spec.rb
@@ -1,14 +1,11 @@
require_relative '../../spec_helper'
+require_relative 'shared/equal_value'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require_relative 'shared/equal_value'
- require 'matrix'
+describe "Matrix#eql?" do
+ it_behaves_like :equal, :eql?
- describe "Matrix#eql?" do
- it_behaves_like :equal, :eql?
-
- it "returns false if some elements are == but not eql?" do
- Matrix[[1, 2],[3, 4]].eql?(Matrix[[1, 2],[3, 4.0]]).should be_false
- end
+ it "returns false if some elements are == but not eql?" do
+ Matrix[[1, 2],[3, 4]].eql?(Matrix[[1, 2],[3, 4.0]]).should be_false
end
end
diff --git a/spec/ruby/library/matrix/equal_value_spec.rb b/spec/ruby/library/matrix/equal_value_spec.rb
index 0408a8ef3e..10cf1e6c29 100644
--- a/spec/ruby/library/matrix/equal_value_spec.rb
+++ b/spec/ruby/library/matrix/equal_value_spec.rb
@@ -1,14 +1,11 @@
require_relative '../../spec_helper'
+require_relative 'shared/equal_value'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require_relative 'shared/equal_value'
- require 'matrix'
+describe "Matrix#==" do
+ it_behaves_like :equal, :==
- describe "Matrix#==" do
- it_behaves_like :equal, :==
-
- it "returns true if some elements are == but not eql?" do
- Matrix[[1, 2],[3, 4]].should == Matrix[[1, 2],[3, 4.0]]
- end
+ it "returns true if some elements are == but not eql?" do
+ Matrix[[1, 2],[3, 4]].should == Matrix[[1, 2],[3, 4.0]]
end
end
diff --git a/spec/ruby/library/matrix/exponent_spec.rb b/spec/ruby/library/matrix/exponent_spec.rb
index 5262627fd5..b76e18b4cd 100644
--- a/spec/ruby/library/matrix/exponent_spec.rb
+++ b/spec/ruby/library/matrix/exponent_spec.rb
@@ -1,67 +1,64 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require_relative 'fixtures/classes'
- require 'matrix'
+describe "Matrix#**" do
- describe "Matrix#**" do
+ describe "given an integer _n_" do
+ it "multiples the Matrix by itself _n_ times" do
+ m = Matrix[ [7,6], [3,9] ]
+ (m ** 1).should == m
+ (m ** 2).should == Matrix[ [67, 96], [48,99] ]
+ (m ** 2).should == m * m
+ (m ** 3).should == m * m * m
+ (m ** 4).should == m * m * m * m
+ (m ** 5).should == m * m * m * m * m
+ end
- describe "given an integer _n_" do
- it "multiples the Matrix by itself _n_ times" do
- m = Matrix[ [7,6], [3,9] ]
- (m ** 1).should == m
- (m ** 2).should == Matrix[ [67, 96], [48,99] ]
- (m ** 2).should == m * m
- (m ** 3).should == m * m * m
- (m ** 4).should == m * m * m * m
- (m ** 5).should == m * m * m * m * m
- end
+ it "raises a ErrDimensionMismatch for non square matrices" do
+ m = Matrix[ [1, 1], [1, 2], [2, 3]]
+ -> { m ** 3 }.should raise_error(Matrix::ErrDimensionMismatch)
+ -> { m ** 0 }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
- it "raises a ErrDimensionMismatch for non square matrices" do
- m = Matrix[ [1, 1], [1, 2], [2, 3]]
- -> { m ** 3 }.should raise_error(Matrix::ErrDimensionMismatch)
- -> { m ** 0 }.should raise_error(Matrix::ErrDimensionMismatch)
+ describe "that is < 0" do
+ it "returns the inverse of **(-n)" do
+ m = Matrix[ [1, 1], [1, 2] ]
+ (m ** -2).should == Matrix[ [5, -3], [-3, 2]]
+ (m ** -4).should == (m.inverse ** 4)
end
- describe "that is < 0" do
- it "returns the inverse of **(-n)" do
- m = Matrix[ [1, 1], [1, 2] ]
- (m ** -2).should == Matrix[ [5, -3], [-3, 2]]
- (m ** -4).should == (m.inverse ** 4)
- end
+ it "raises a ErrNotRegular for irregular matrices" do
+ m = Matrix[ [1, 1], [1, 1] ]
+ -> { m ** -2 }.should raise_error(Matrix::ErrNotRegular)
+ end
+ end
- it "raises a ErrNotRegular for irregular matrices" do
+ ruby_version_is '3.1.0' do # https://bugs.ruby-lang.org/issues/17521
+ describe "that is 0" do
+ it "returns the identity for square matrices" do
m = Matrix[ [1, 1], [1, 1] ]
- -> { m ** -2 }.should raise_error(Matrix::ErrNotRegular)
+ (m ** 0).should == Matrix.identity(2)
end
- end
-
- ruby_version_is '3.1.0' do # https://bugs.ruby-lang.org/issues/17521
- describe "that is 0" do
- it "returns the identity for square matrices" do
- m = Matrix[ [1, 1], [1, 1] ]
- (m ** 0).should == Matrix.identity(2)
- end
- it "raises an ErrDimensionMismatch for non-square matrices" do
- m = Matrix[ [1, 1] ]
- -> { m ** 0 }.should raise_error(Matrix::ErrDimensionMismatch)
- end
+ it "raises an ErrDimensionMismatch for non-square matrices" do
+ m = Matrix[ [1, 1] ]
+ -> { m ** 0 }.should raise_error(Matrix::ErrDimensionMismatch)
end
end
end
+ end
- it "returns the power for non integer powers" do
- a = Matrix[[5, 4], [4, 5]]
- ((a ** 0.5) ** 2).round(8).should == a
- a = Matrix[[7, 10], [15, 22]]
- ((a ** 0.25) ** 4).round(8).should == a
- end
+ it "returns the power for non integer powers" do
+ a = Matrix[[5, 4], [4, 5]]
+ ((a ** 0.5) ** 2).round(8).should == a
+ a = Matrix[[7, 10], [15, 22]]
+ ((a ** 0.25) ** 4).round(8).should == a
+ end
- describe "for a subclass of Matrix" do
- it "returns an instance of that subclass" do
- (MatrixSub.ins ** 1).should be_an_instance_of(MatrixSub)
- end
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ (MatrixSub.ins ** 1).should be_an_instance_of(MatrixSub)
end
end
end
diff --git a/spec/ruby/library/matrix/find_index_spec.rb b/spec/ruby/library/matrix/find_index_spec.rb
index a0e3679aef..c2bfa6d61a 100644
--- a/spec/ruby/library/matrix/find_index_spec.rb
+++ b/spec/ruby/library/matrix/find_index_spec.rb
@@ -1,149 +1,146 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix#find_index without any argument" do
- before :all do
- @m = Matrix[ [1, 2, 3, 4], [5, 6, 7, 8] ]
- end
+describe "Matrix#find_index without any argument" do
+ before :all do
+ @m = Matrix[ [1, 2, 3, 4], [5, 6, 7, 8] ]
+ end
- it "returns an Enumerator when called without a block" do
- enum = @m.find_index
- enum.should be_an_instance_of(Enumerator)
- enum.to_a.should == [1, 2, 3, 4, 5, 6, 7, 8]
- end
+ it "returns an Enumerator when called without a block" do
+ enum = @m.find_index
+ enum.should be_an_instance_of(Enumerator)
+ enum.to_a.should == [1, 2, 3, 4, 5, 6, 7, 8]
+ end
- it "returns nil if the block is always false" do
- @m.find_index{false}.should be_nil
- end
+ it "returns nil if the block is always false" do
+ @m.find_index{false}.should be_nil
+ end
- it "returns the first index for which the block is true" do
- @m.find_index{|x| x >= 3}.should == [0, 2]
- end
+ it "returns the first index for which the block is true" do
+ @m.find_index{|x| x >= 3}.should == [0, 2]
end
+end
- describe "Matrix#find_index with a subselection argument" do
- before :all do
- @tests = [
- [ Matrix[ [1, 2, 3, 4], [5, 6, 7, 8] ], {
- diagonal: [1, 6] ,
- off_diagonal: [2, 3, 4, 5, 7, 8],
- lower: [1, 5, 6] ,
- strict_lower: [5] ,
- strict_upper: [2, 3, 4, 7, 8] ,
- upper: [1, 2, 3, 4, 6, 7, 8] ,
- }
- ],
- [ Matrix[ [1, 2], [3, 4], [5, 6], [7, 8] ], {
- diagonal: [1, 4] ,
- off_diagonal: [2, 3, 5, 6, 7, 8],
- lower: [1, 3, 4, 5, 6, 7, 8] ,
- strict_lower: [3, 5, 6, 7, 8] ,
- strict_upper: [2] ,
- upper: [1, 2, 4] ,
- }
- ]]
- end
+describe "Matrix#find_index with a subselection argument" do
+ before :all do
+ @tests = [
+ [ Matrix[ [1, 2, 3, 4], [5, 6, 7, 8] ], {
+ diagonal: [1, 6] ,
+ off_diagonal: [2, 3, 4, 5, 7, 8],
+ lower: [1, 5, 6] ,
+ strict_lower: [5] ,
+ strict_upper: [2, 3, 4, 7, 8] ,
+ upper: [1, 2, 3, 4, 6, 7, 8] ,
+ }
+ ],
+ [ Matrix[ [1, 2], [3, 4], [5, 6], [7, 8] ], {
+ diagonal: [1, 4] ,
+ off_diagonal: [2, 3, 5, 6, 7, 8],
+ lower: [1, 3, 4, 5, 6, 7, 8] ,
+ strict_lower: [3, 5, 6, 7, 8] ,
+ strict_upper: [2] ,
+ upper: [1, 2, 4] ,
+ }
+ ]]
+ end
- describe "and no generic argument" do
- it "returns an Enumerator when called without a block" do
- @tests.each do |matrix, h|
- h.each do |selector, result|
- matrix.find_index(selector).should be_an_instance_of(Enumerator)
- end
+ describe "and no generic argument" do
+ it "returns an Enumerator when called without a block" do
+ @tests.each do |matrix, h|
+ h.each do |selector, result|
+ matrix.find_index(selector).should be_an_instance_of(Enumerator)
end
end
+ end
- it "yields the rights elements" do
- @tests.each do |matrix, h|
- h.each do |selector, result|
- matrix.find_index(selector).to_a.should == result
- end
+ it "yields the rights elements" do
+ @tests.each do |matrix, h|
+ h.each do |selector, result|
+ matrix.find_index(selector).to_a.should == result
end
end
+ end
- it "returns the first index for which the block returns true" do
- @tests.each do |matrix, h|
- h.each do |selector, result|
- cnt = result.size.div 2
- which = result[cnt]
- idx = matrix.find_index(selector){|x| cnt -= 1; x == which}
- matrix[*idx].should == which
- cnt.should == -1
- end
+ it "returns the first index for which the block returns true" do
+ @tests.each do |matrix, h|
+ h.each do |selector, result|
+ cnt = result.size.div 2
+ which = result[cnt]
+ idx = matrix.find_index(selector){|x| cnt -= 1; x == which}
+ matrix[*idx].should == which
+ cnt.should == -1
end
end
+ end
- it "returns nil if the block is always false" do
- @tests.each do |matrix, h|
- h.each do |selector, result|
- matrix.find_index(selector){ nil }.should == nil
- end
+ it "returns nil if the block is always false" do
+ @tests.each do |matrix, h|
+ h.each do |selector, result|
+ matrix.find_index(selector){ nil }.should == nil
end
end
-
end
- describe "and a generic argument" do
- it "ignores a block" do
- @m.find_index(42, :diagonal){raise "oups"}.should == nil
- end
+ end
+
+ describe "and a generic argument" do
+ it "ignores a block" do
+ @m.find_index(42, :diagonal){raise "oups"}.should == nil
+ end
- it "returns the index of the requested value" do
- @tests.each do |matrix, h|
- h.each do |selector, result|
- cnt = result.size / 2
- which = result[cnt]
- idx = matrix.find_index(which, selector)
- matrix[*idx].should == which
- end
+ it "returns the index of the requested value" do
+ @tests.each do |matrix, h|
+ h.each do |selector, result|
+ cnt = result.size / 2
+ which = result[cnt]
+ idx = matrix.find_index(which, selector)
+ matrix[*idx].should == which
end
end
+ end
- it "returns nil if the requested value is not found" do
- @tests.each do |matrix, h|
- h.each do |selector, result|
- matrix.find_index(42, selector).should == nil
- end
+ it "returns nil if the requested value is not found" do
+ @tests.each do |matrix, h|
+ h.each do |selector, result|
+ matrix.find_index(42, selector).should == nil
end
end
end
-
end
- describe "Matrix#find_index with only a generic argument" do
- before :all do
- @m = Matrix[ [1, 2, 3, 4], [1, 2, 3, 4] ]
- end
+end
- it "returns nil if the value is not found" do
- @m.find_index(42).should be_nil
- end
+describe "Matrix#find_index with only a generic argument" do
+ before :all do
+ @m = Matrix[ [1, 2, 3, 4], [1, 2, 3, 4] ]
+ end
- it "returns the first index for of the requested value" do
- @m.find_index(3).should == [0, 2]
- end
+ it "returns nil if the value is not found" do
+ @m.find_index(42).should be_nil
+ end
- it "ignores a block" do
- @m.find_index(4){raise "oups"}.should == [0, 3]
- end
+ it "returns the first index for of the requested value" do
+ @m.find_index(3).should == [0, 2]
end
- describe "Matrix#find_index with two arguments" do
- it "raises an ArgumentError for an unrecognized last argument" do
- -> {
- @m.find_index(1, "all"){}
- }.should raise_error(ArgumentError)
- -> {
- @m.find_index(1, nil){}
- }.should raise_error(ArgumentError)
- -> {
- @m.find_index(1, :left){}
- }.should raise_error(ArgumentError)
- -> {
- @m.find_index(:diagonal, 1){}
- }.should raise_error(ArgumentError)
- end
+ it "ignores a block" do
+ @m.find_index(4){raise "oups"}.should == [0, 3]
+ end
+end
+
+describe "Matrix#find_index with two arguments" do
+ it "raises an ArgumentError for an unrecognized last argument" do
+ -> {
+ @m.find_index(1, "all"){}
+ }.should raise_error(ArgumentError)
+ -> {
+ @m.find_index(1, nil){}
+ }.should raise_error(ArgumentError)
+ -> {
+ @m.find_index(1, :left){}
+ }.should raise_error(ArgumentError)
+ -> {
+ @m.find_index(:diagonal, 1){}
+ }.should raise_error(ArgumentError)
end
end
diff --git a/spec/ruby/library/matrix/hash_spec.rb b/spec/ruby/library/matrix/hash_spec.rb
index 7c1970511b..7dabcd3737 100644
--- a/spec/ruby/library/matrix/hash_spec.rb
+++ b/spec/ruby/library/matrix/hash_spec.rb
@@ -1,18 +1,15 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
+describe "Matrix#hash" do
- describe "Matrix#hash" do
-
- it "returns an Integer" do
- Matrix[ [1,2] ].hash.should be_an_instance_of(Integer)
- end
-
- it "returns the same value for the same matrix" do
- data = [ [40,5], [2,7] ]
- Matrix[ *data ].hash.should == Matrix[ *data ].hash
- end
+ it "returns an Integer" do
+ Matrix[ [1,2] ].hash.should be_an_instance_of(Integer)
+ end
+ it "returns the same value for the same matrix" do
+ data = [ [40,5], [2,7] ]
+ Matrix[ *data ].hash.should == Matrix[ *data ].hash
end
+
end
diff --git a/spec/ruby/library/matrix/hermitian_spec.rb b/spec/ruby/library/matrix/hermitian_spec.rb
index 4038ee3fa9..177ca64d83 100644
--- a/spec/ruby/library/matrix/hermitian_spec.rb
+++ b/spec/ruby/library/matrix/hermitian_spec.rb
@@ -1,37 +1,34 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix.hermitian?" do
- it "returns true for a hermitian Matrix" do
- Matrix[[1, 2, Complex(0, 3)], [2, 4, 5], [Complex(0, -3), 5, 6]].hermitian?.should be_true
- end
+describe "Matrix.hermitian?" do
+ it "returns true for a hermitian Matrix" do
+ Matrix[[1, 2, Complex(0, 3)], [2, 4, 5], [Complex(0, -3), 5, 6]].hermitian?.should be_true
+ end
- it "returns true for a 0x0 empty matrix" do
- Matrix.empty.hermitian?.should be_true
- end
+ it "returns true for a 0x0 empty matrix" do
+ Matrix.empty.hermitian?.should be_true
+ end
- it "returns false for an asymmetric Matrix" do
- Matrix[[1, 2],[-2, 1]].hermitian?.should be_false
- end
+ it "returns false for an asymmetric Matrix" do
+ Matrix[[1, 2],[-2, 1]].hermitian?.should be_false
+ end
- it "raises an error for rectangular matrices" do
- [
- Matrix[[0], [0]],
- Matrix[[0, 0]],
- Matrix.empty(0, 2),
- Matrix.empty(2, 0),
- ].each do |rectangular_matrix|
- -> {
- rectangular_matrix.hermitian?
- }.should raise_error(Matrix::ErrDimensionMismatch)
- end
+ it "raises an error for rectangular matrices" do
+ [
+ Matrix[[0], [0]],
+ Matrix[[0, 0]],
+ Matrix.empty(0, 2),
+ Matrix.empty(2, 0),
+ ].each do |rectangular_matrix|
+ -> {
+ rectangular_matrix.hermitian?
+ }.should raise_error(Matrix::ErrDimensionMismatch)
end
+ end
- it "returns false for a matrix with complex values on the diagonal" do
- Matrix[[Complex(1,1)]].hermitian?.should be_false
- Matrix[[Complex(1,0)]].hermitian?.should be_true
- end
+ it "returns false for a matrix with complex values on the diagonal" do
+ Matrix[[Complex(1,1)]].hermitian?.should be_false
+ Matrix[[Complex(1,0)]].hermitian?.should be_true
end
end
diff --git a/spec/ruby/library/matrix/identity_spec.rb b/spec/ruby/library/matrix/identity_spec.rb
index 14f51c402e..646462bc47 100644
--- a/spec/ruby/library/matrix/identity_spec.rb
+++ b/spec/ruby/library/matrix/identity_spec.rb
@@ -1,9 +1,6 @@
require_relative '../../spec_helper'
+require_relative 'shared/identity'
-ruby_version_is ""..."3.1" do
- require_relative 'shared/identity'
-
- describe "Matrix.identity" do
- it_behaves_like :matrix_identity, :identity
- end
+describe "Matrix.identity" do
+ it_behaves_like :matrix_identity, :identity
end
diff --git a/spec/ruby/library/matrix/imag_spec.rb b/spec/ruby/library/matrix/imag_spec.rb
index a89186ec02..1c988753d8 100644
--- a/spec/ruby/library/matrix/imag_spec.rb
+++ b/spec/ruby/library/matrix/imag_spec.rb
@@ -1,9 +1,6 @@
require_relative '../../spec_helper'
+require_relative 'shared/imaginary'
-ruby_version_is ""..."3.1" do
- require_relative 'shared/imaginary'
-
- describe "Matrix#imag" do
- it_behaves_like :matrix_imaginary, :imag
- end
+describe "Matrix#imag" do
+ it_behaves_like :matrix_imaginary, :imag
end
diff --git a/spec/ruby/library/matrix/imaginary_spec.rb b/spec/ruby/library/matrix/imaginary_spec.rb
index e7f0e49d31..ceae4bbe8d 100644
--- a/spec/ruby/library/matrix/imaginary_spec.rb
+++ b/spec/ruby/library/matrix/imaginary_spec.rb
@@ -1,9 +1,6 @@
require_relative '../../spec_helper'
+require_relative 'shared/imaginary'
-ruby_version_is ""..."3.1" do
- require_relative 'shared/imaginary'
-
- describe "Matrix#imaginary" do
- it_behaves_like :matrix_imaginary, :imaginary
- end
+describe "Matrix#imaginary" do
+ it_behaves_like :matrix_imaginary, :imaginary
end
diff --git a/spec/ruby/library/matrix/inspect_spec.rb b/spec/ruby/library/matrix/inspect_spec.rb
index d754c0fe7f..508f478252 100644
--- a/spec/ruby/library/matrix/inspect_spec.rb
+++ b/spec/ruby/library/matrix/inspect_spec.rb
@@ -1,30 +1,27 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require_relative 'fixtures/classes'
- require 'matrix'
+describe "Matrix#inspect" do
- describe "Matrix#inspect" do
-
- it "returns a stringified representation of the Matrix" do
- Matrix[ [1,2], [2,1] ].inspect.should == "Matrix[[1, 2], [2, 1]]"
- end
+ it "returns a stringified representation of the Matrix" do
+ Matrix[ [1,2], [2,1] ].inspect.should == "Matrix[[1, 2], [2, 1]]"
+ end
- it "returns 'Matrix.empty(...)' for empty matrices" do
- Matrix[ [], [], [] ].inspect.should == "Matrix.empty(3, 0)"
- Matrix.columns([ [], [], [] ]).inspect.should == "Matrix.empty(0, 3)"
- end
+ it "returns 'Matrix.empty(...)' for empty matrices" do
+ Matrix[ [], [], [] ].inspect.should == "Matrix.empty(3, 0)"
+ Matrix.columns([ [], [], [] ]).inspect.should == "Matrix.empty(0, 3)"
+ end
- it "calls inspect on its contents" do
- obj = mock("some_value")
- obj.should_receive(:inspect).and_return("some_value")
- Matrix[ [1, 2], [3, obj] ].inspect.should == "Matrix[[1, 2], [3, some_value]]"
- end
+ it "calls inspect on its contents" do
+ obj = mock("some_value")
+ obj.should_receive(:inspect).and_return("some_value")
+ Matrix[ [1, 2], [3, obj] ].inspect.should == "Matrix[[1, 2], [3, some_value]]"
+ end
- describe "for a subclass of Matrix" do
- it "returns a string using the subclass' name" do
- MatrixSub.ins.inspect.should == "MatrixSub[[1, 0], [0, 1]]"
- end
+ describe "for a subclass of Matrix" do
+ it "returns a string using the subclass' name" do
+ MatrixSub.ins.inspect.should == "MatrixSub[[1, 0], [0, 1]]"
end
end
end
diff --git a/spec/ruby/library/matrix/inv_spec.rb b/spec/ruby/library/matrix/inv_spec.rb
index 0ad817a253..82879a6d82 100644
--- a/spec/ruby/library/matrix/inv_spec.rb
+++ b/spec/ruby/library/matrix/inv_spec.rb
@@ -1,10 +1,7 @@
require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'shared/inverse'
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'shared/inverse'
-
- describe "Matrix#inv" do
- it_behaves_like :inverse, :inv
- end
+describe "Matrix#inv" do
+ it_behaves_like :inverse, :inv
end
diff --git a/spec/ruby/library/matrix/inverse_from_spec.rb b/spec/ruby/library/matrix/inverse_from_spec.rb
index bca40542f6..651d56a244 100644
--- a/spec/ruby/library/matrix/inverse_from_spec.rb
+++ b/spec/ruby/library/matrix/inverse_from_spec.rb
@@ -1,9 +1,6 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix#inverse_from" do
- it "needs to be reviewed for spec completeness"
- end
+describe "Matrix#inverse_from" do
+ it "needs to be reviewed for spec completeness"
end
diff --git a/spec/ruby/library/matrix/inverse_spec.rb b/spec/ruby/library/matrix/inverse_spec.rb
index dd9099bec5..fa3fa7de8a 100644
--- a/spec/ruby/library/matrix/inverse_spec.rb
+++ b/spec/ruby/library/matrix/inverse_spec.rb
@@ -1,10 +1,7 @@
require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'shared/inverse'
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'shared/inverse'
-
- describe "Matrix#inverse" do
- it_behaves_like :inverse, :inverse
- end
+describe "Matrix#inverse" do
+ it_behaves_like :inverse, :inverse
end
diff --git a/spec/ruby/library/matrix/lower_triangular_spec.rb b/spec/ruby/library/matrix/lower_triangular_spec.rb
index 0223b8b619..f3aa4501f4 100644
--- a/spec/ruby/library/matrix/lower_triangular_spec.rb
+++ b/spec/ruby/library/matrix/lower_triangular_spec.rb
@@ -1,27 +1,24 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix.lower_triangular?" do
- it "returns true for a square lower triangular Matrix" do
- Matrix[[1, 0, 0], [1, 2, 0], [1, 2, 3]].lower_triangular?.should be_true
- Matrix.diagonal([1, 2, 3]).lower_triangular?.should be_true
- Matrix[[1, 0], [1, 2], [1, 2], [1, 2]].lower_triangular?.should be_true
- Matrix[[1, 0, 0, 0], [1, 2, 0, 0]].lower_triangular?.should be_true
- end
+describe "Matrix.lower_triangular?" do
+ it "returns true for a square lower triangular Matrix" do
+ Matrix[[1, 0, 0], [1, 2, 0], [1, 2, 3]].lower_triangular?.should be_true
+ Matrix.diagonal([1, 2, 3]).lower_triangular?.should be_true
+ Matrix[[1, 0], [1, 2], [1, 2], [1, 2]].lower_triangular?.should be_true
+ Matrix[[1, 0, 0, 0], [1, 2, 0, 0]].lower_triangular?.should be_true
+ end
- it "returns true for an empty Matrix" do
- Matrix.empty(3, 0).lower_triangular?.should be_true
- Matrix.empty(0, 3).lower_triangular?.should be_true
- Matrix.empty(0, 0).lower_triangular?.should be_true
- end
+ it "returns true for an empty Matrix" do
+ Matrix.empty(3, 0).lower_triangular?.should be_true
+ Matrix.empty(0, 3).lower_triangular?.should be_true
+ Matrix.empty(0, 0).lower_triangular?.should be_true
+ end
- it "returns false for a non lower triangular square Matrix" do
- Matrix[[0, 1], [0, 0]].lower_triangular?.should be_false
- Matrix[[1, 2, 3], [1, 2, 3], [1, 2, 3]].lower_triangular?.should be_false
- Matrix[[0, 1], [0, 0], [0, 0], [0, 0]].lower_triangular?.should be_false
- Matrix[[0, 0, 0, 1], [0, 0, 0, 0]].lower_triangular?.should be_false
- end
+ it "returns false for a non lower triangular square Matrix" do
+ Matrix[[0, 1], [0, 0]].lower_triangular?.should be_false
+ Matrix[[1, 2, 3], [1, 2, 3], [1, 2, 3]].lower_triangular?.should be_false
+ Matrix[[0, 1], [0, 0], [0, 0], [0, 0]].lower_triangular?.should be_false
+ Matrix[[0, 0, 0, 1], [0, 0, 0, 0]].lower_triangular?.should be_false
end
end
diff --git a/spec/ruby/library/matrix/lup_decomposition/determinant_spec.rb b/spec/ruby/library/matrix/lup_decomposition/determinant_spec.rb
index 1ac4bc971e..9d733066c1 100644
--- a/spec/ruby/library/matrix/lup_decomposition/determinant_spec.rb
+++ b/spec/ruby/library/matrix/lup_decomposition/determinant_spec.rb
@@ -1,24 +1,21 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix::LUPDecomposition#determinant" do
- it "returns the determinant when the matrix is square" do
- a = Matrix[[7, 8, 9], [14, 46, 51], [28, 82, 163]]
- a.lup.determinant.should == 15120 # == a.determinant
- end
+describe "Matrix::LUPDecomposition#determinant" do
+ it "returns the determinant when the matrix is square" do
+ a = Matrix[[7, 8, 9], [14, 46, 51], [28, 82, 163]]
+ a.lup.determinant.should == 15120 # == a.determinant
+ end
- it "raises an error for rectangular matrices" do
- [
- Matrix[[7, 8, 9], [14, 46, 51]],
- Matrix[[7, 8], [14, 46], [28, 82]],
- ].each do |m|
- lup = m.lup
- -> {
- lup.determinant
- }.should raise_error(Matrix::ErrDimensionMismatch)
- end
+ it "raises an error for rectangular matrices" do
+ [
+ Matrix[[7, 8, 9], [14, 46, 51]],
+ Matrix[[7, 8], [14, 46], [28, 82]],
+ ].each do |m|
+ lup = m.lup
+ -> {
+ lup.determinant
+ }.should raise_error(Matrix::ErrDimensionMismatch)
end
end
end
diff --git a/spec/ruby/library/matrix/lup_decomposition/initialize_spec.rb b/spec/ruby/library/matrix/lup_decomposition/initialize_spec.rb
index 42f78c7540..36afb349e6 100644
--- a/spec/ruby/library/matrix/lup_decomposition/initialize_spec.rb
+++ b/spec/ruby/library/matrix/lup_decomposition/initialize_spec.rb
@@ -1,16 +1,13 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix::LUPDecomposition#initialize" do
- it "raises an error if argument is not a matrix" do
- -> {
- Matrix::LUPDecomposition.new([[]])
- }.should raise_error(TypeError)
- -> {
- Matrix::LUPDecomposition.new(42)
- }.should raise_error(TypeError)
- end
+describe "Matrix::LUPDecomposition#initialize" do
+ it "raises an error if argument is not a matrix" do
+ -> {
+ Matrix::LUPDecomposition.new([[]])
+ }.should raise_error(TypeError)
+ -> {
+ Matrix::LUPDecomposition.new(42)
+ }.should raise_error(TypeError)
end
end
diff --git a/spec/ruby/library/matrix/lup_decomposition/l_spec.rb b/spec/ruby/library/matrix/lup_decomposition/l_spec.rb
index 6c751f12b7..9514ab5d06 100644
--- a/spec/ruby/library/matrix/lup_decomposition/l_spec.rb
+++ b/spec/ruby/library/matrix/lup_decomposition/l_spec.rb
@@ -1,21 +1,18 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix::LUPDecomposition#l" do
- before :each do
- @a = Matrix[[7, 8, 9], [14, 46, 51], [28, 82, 163]]
- @lu = Matrix::LUPDecomposition.new(@a)
- @l = @lu.l
- end
+describe "Matrix::LUPDecomposition#l" do
+ before :each do
+ @a = Matrix[[7, 8, 9], [14, 46, 51], [28, 82, 163]]
+ @lu = Matrix::LUPDecomposition.new(@a)
+ @l = @lu.l
+ end
- it "returns the first element of to_a" do
- @l.should == @lu.to_a[0]
- end
+ it "returns the first element of to_a" do
+ @l.should == @lu.to_a[0]
+ end
- it "returns a lower triangular matrix" do
- @l.lower_triangular?.should be_true
- end
+ it "returns a lower triangular matrix" do
+ @l.lower_triangular?.should be_true
end
end
diff --git a/spec/ruby/library/matrix/lup_decomposition/p_spec.rb b/spec/ruby/library/matrix/lup_decomposition/p_spec.rb
index 481f8a17d5..c7b5e9196e 100644
--- a/spec/ruby/library/matrix/lup_decomposition/p_spec.rb
+++ b/spec/ruby/library/matrix/lup_decomposition/p_spec.rb
@@ -1,21 +1,18 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix::LUPDecomposition#p" do
- before :each do
- @a = Matrix[[7, 8, 9], [14, 46, 51], [28, 82, 163]]
- @lu = Matrix::LUPDecomposition.new(@a)
- @p = @lu.p
- end
+describe "Matrix::LUPDecomposition#p" do
+ before :each do
+ @a = Matrix[[7, 8, 9], [14, 46, 51], [28, 82, 163]]
+ @lu = Matrix::LUPDecomposition.new(@a)
+ @p = @lu.p
+ end
- it "returns the third element of to_a" do
- @p.should == @lu.to_a[2]
- end
+ it "returns the third element of to_a" do
+ @p.should == @lu.to_a[2]
+ end
- it "returns a permutation matrix" do
- @p.permutation?.should be_true
- end
+ it "returns a permutation matrix" do
+ @p.permutation?.should be_true
end
end
diff --git a/spec/ruby/library/matrix/lup_decomposition/solve_spec.rb b/spec/ruby/library/matrix/lup_decomposition/solve_spec.rb
index 773fcb3e65..66242627e9 100644
--- a/spec/ruby/library/matrix/lup_decomposition/solve_spec.rb
+++ b/spec/ruby/library/matrix/lup_decomposition/solve_spec.rb
@@ -1,55 +1,52 @@
require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Matrix::LUPDecomposition#solve" do
+ describe "for rectangular matrices" do
+ it "raises an error for singular matrices" do
+ a = Matrix[[1, 2, 3], [1, 3, 5], [2, 5, 8]]
+ lu = Matrix::LUPDecomposition.new(a)
+ -> {
+ lu.solve(a)
+ }.should raise_error(Matrix::ErrNotRegular)
+ end
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix::LUPDecomposition#solve" do
- describe "for rectangular matrices" do
- it "raises an error for singular matrices" do
- a = Matrix[[1, 2, 3], [1, 3, 5], [2, 5, 8]]
- lu = Matrix::LUPDecomposition.new(a)
- -> {
- lu.solve(a)
- }.should raise_error(Matrix::ErrNotRegular)
+ describe "for non singular matrices" do
+ before :each do
+ @a = Matrix[[7, 8, 9], [14, 46, 51], [28, 82, 163]]
+ @lu = Matrix::LUPDecomposition.new(@a)
end
- describe "for non singular matrices" do
- before :each do
- @a = Matrix[[7, 8, 9], [14, 46, 51], [28, 82, 163]]
- @lu = Matrix::LUPDecomposition.new(@a)
- end
-
- it "returns the appropriate empty matrix when given an empty matrix" do
- @lu.solve(Matrix.empty(3,0)).should == Matrix.empty(3,0)
- empty = Matrix::LUPDecomposition.new(Matrix.empty(0, 0))
- empty.solve(Matrix.empty(0,3)).should == Matrix.empty(0,3)
- end
+ it "returns the appropriate empty matrix when given an empty matrix" do
+ @lu.solve(Matrix.empty(3,0)).should == Matrix.empty(3,0)
+ empty = Matrix::LUPDecomposition.new(Matrix.empty(0, 0))
+ empty.solve(Matrix.empty(0,3)).should == Matrix.empty(0,3)
+ end
- it "returns the right matrix when given a matrix of the appropriate size" do
- solution = Matrix[[1, 2, 3, 4], [0, 1, 2, 3], [-1, -2, -3, -4]]
- values = Matrix[[-2, 4, 10, 16], [-37, -28, -19, -10], [-135, -188, -241, -294]] # == @a * solution
- @lu.solve(values).should == solution
- end
+ it "returns the right matrix when given a matrix of the appropriate size" do
+ solution = Matrix[[1, 2, 3, 4], [0, 1, 2, 3], [-1, -2, -3, -4]]
+ values = Matrix[[-2, 4, 10, 16], [-37, -28, -19, -10], [-135, -188, -241, -294]] # == @a * solution
+ @lu.solve(values).should == solution
+ end
- it "raises an error when given a matrix of the wrong size" do
- values = Matrix[[1, 2, 3, 4], [0, 1, 2, 3]]
- -> {
- @lu.solve(values)
- }.should raise_error(Matrix::ErrDimensionMismatch)
- end
+ it "raises an error when given a matrix of the wrong size" do
+ values = Matrix[[1, 2, 3, 4], [0, 1, 2, 3]]
+ -> {
+ @lu.solve(values)
+ }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
- it "returns the right vector when given a vector of the appropriate size" do
- solution = Vector[1, 2, -1]
- values = Vector[14, 55, 29] # == @a * solution
- @lu.solve(values).should == solution
- end
+ it "returns the right vector when given a vector of the appropriate size" do
+ solution = Vector[1, 2, -1]
+ values = Vector[14, 55, 29] # == @a * solution
+ @lu.solve(values).should == solution
+ end
- it "raises an error when given a vector of the wrong size" do
- values = Vector[14, 55]
- -> {
- @lu.solve(values)
- }.should raise_error(Matrix::ErrDimensionMismatch)
- end
+ it "raises an error when given a vector of the wrong size" do
+ values = Vector[14, 55]
+ -> {
+ @lu.solve(values)
+ }.should raise_error(Matrix::ErrDimensionMismatch)
end
end
end
diff --git a/spec/ruby/library/matrix/lup_decomposition/to_a_spec.rb b/spec/ruby/library/matrix/lup_decomposition/to_a_spec.rb
index 8702292865..9b1dccbbac 100644
--- a/spec/ruby/library/matrix/lup_decomposition/to_a_spec.rb
+++ b/spec/ruby/library/matrix/lup_decomposition/to_a_spec.rb
@@ -1,36 +1,33 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix::LUPDecomposition#to_a" do
- before :each do
- @a = Matrix[[7, 8, 9], [14, 46, 51], [28, 82, 163]]
- @lu = Matrix::LUPDecomposition.new(@a)
- @to_a = @lu.to_a
- @l, @u, @p = @to_a
- end
+describe "Matrix::LUPDecomposition#to_a" do
+ before :each do
+ @a = Matrix[[7, 8, 9], [14, 46, 51], [28, 82, 163]]
+ @lu = Matrix::LUPDecomposition.new(@a)
+ @to_a = @lu.to_a
+ @l, @u, @p = @to_a
+ end
- it "returns an array of three matrices" do
- @to_a.should be_kind_of(Array)
- @to_a.length.should == 3
- @to_a.each{|m| m.should be_kind_of(Matrix)}
- end
+ it "returns an array of three matrices" do
+ @to_a.should be_kind_of(Array)
+ @to_a.length.should == 3
+ @to_a.each{|m| m.should be_kind_of(Matrix)}
+ end
- it "returns [l, u, p] such that l*u == a*p" do
- (@l * @u).should == (@p * @a)
- end
+ it "returns [l, u, p] such that l*u == a*p" do
+ (@l * @u).should == (@p * @a)
+ end
- it "returns the right values for rectangular matrices" do
- [
- Matrix[[7, 8, 9], [14, 46, 51]],
- Matrix[[4, 11], [5, 8], [3, 4]],
- ].each do |a|
- l, u, p = Matrix::LUPDecomposition.new(a).to_a
- (l * u).should == (p * a)
- end
+ it "returns the right values for rectangular matrices" do
+ [
+ Matrix[[7, 8, 9], [14, 46, 51]],
+ Matrix[[4, 11], [5, 8], [3, 4]],
+ ].each do |a|
+ l, u, p = Matrix::LUPDecomposition.new(a).to_a
+ (l * u).should == (p * a)
end
-
- it "has other properties implied by the specs of #l, #u and #p"
end
+
+ it "has other properties implied by the specs of #l, #u and #p"
end
diff --git a/spec/ruby/library/matrix/lup_decomposition/u_spec.rb b/spec/ruby/library/matrix/lup_decomposition/u_spec.rb
index cd884b88ec..ca3dfc1f00 100644
--- a/spec/ruby/library/matrix/lup_decomposition/u_spec.rb
+++ b/spec/ruby/library/matrix/lup_decomposition/u_spec.rb
@@ -1,21 +1,18 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix::LUPDecomposition#u" do
- before :each do
- @a = Matrix[[7, 8, 9], [14, 46, 51], [28, 82, 163]]
- @lu = Matrix::LUPDecomposition.new(@a)
- @u = @lu.u
- end
+describe "Matrix::LUPDecomposition#u" do
+ before :each do
+ @a = Matrix[[7, 8, 9], [14, 46, 51], [28, 82, 163]]
+ @lu = Matrix::LUPDecomposition.new(@a)
+ @u = @lu.u
+ end
- it "returns the second element of to_a" do
- @u.should == @lu.to_a[1]
- end
+ it "returns the second element of to_a" do
+ @u.should == @lu.to_a[1]
+ end
- it "returns an upper triangular matrix" do
- @u.upper_triangular?.should be_true
- end
+ it "returns an upper triangular matrix" do
+ @u.upper_triangular?.should be_true
end
end
diff --git a/spec/ruby/library/matrix/map_spec.rb b/spec/ruby/library/matrix/map_spec.rb
index cde0df5441..bc07c48cda 100644
--- a/spec/ruby/library/matrix/map_spec.rb
+++ b/spec/ruby/library/matrix/map_spec.rb
@@ -1,9 +1,6 @@
require_relative '../../spec_helper'
+require_relative 'shared/collect'
-ruby_version_is ""..."3.1" do
- require_relative 'shared/collect'
-
- describe "Matrix#map" do
- it_behaves_like :collect, :map
- end
+describe "Matrix#map" do
+ it_behaves_like :collect, :map
end
diff --git a/spec/ruby/library/matrix/minor_spec.rb b/spec/ruby/library/matrix/minor_spec.rb
index 0a6b0823c8..009826c3d6 100644
--- a/spec/ruby/library/matrix/minor_spec.rb
+++ b/spec/ruby/library/matrix/minor_spec.rb
@@ -1,88 +1,85 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require_relative 'fixtures/classes'
- require 'matrix'
+describe "Matrix#minor" do
+ before :each do
+ @matrix = Matrix[ [1,2], [3,4], [5,6] ]
+ end
- describe "Matrix#minor" do
- before :each do
- @matrix = Matrix[ [1,2], [3,4], [5,6] ]
+ describe "with start_row, nrows, start_col, ncols" do
+ it "returns the given portion of the Matrix" do
+ @matrix.minor(0,1,0,2).should == Matrix[ [1, 2] ]
+ @matrix.minor(1,2,1,1).should == Matrix[ [4], [6] ]
end
- describe "with start_row, nrows, start_col, ncols" do
- it "returns the given portion of the Matrix" do
- @matrix.minor(0,1,0,2).should == Matrix[ [1, 2] ]
- @matrix.minor(1,2,1,1).should == Matrix[ [4], [6] ]
- end
-
- it "returns an empty Matrix if nrows or ncols is 0" do
- @matrix.minor(0,0,0,0).should == Matrix[]
- @matrix.minor(1,0,1,0).should == Matrix[]
- @matrix.minor(1,0,1,1).should == Matrix.columns([[]])
- @matrix.minor(1,1,1,0).should == Matrix[[]]
- end
+ it "returns an empty Matrix if nrows or ncols is 0" do
+ @matrix.minor(0,0,0,0).should == Matrix[]
+ @matrix.minor(1,0,1,0).should == Matrix[]
+ @matrix.minor(1,0,1,1).should == Matrix.columns([[]])
+ @matrix.minor(1,1,1,0).should == Matrix[[]]
+ end
- it "returns nil for out-of-bounds start_row/col" do
- r = @matrix.row_size + 1
- c = @matrix.column_size + 1
- @matrix.minor(r,0,0,10).should == nil
- @matrix.minor(0,10,c,9).should == nil
- @matrix.minor(-r,0,0,10).should == nil
- @matrix.minor(0,10,-c,9).should == nil
- end
+ it "returns nil for out-of-bounds start_row/col" do
+ r = @matrix.row_size + 1
+ c = @matrix.column_size + 1
+ @matrix.minor(r,0,0,10).should == nil
+ @matrix.minor(0,10,c,9).should == nil
+ @matrix.minor(-r,0,0,10).should == nil
+ @matrix.minor(0,10,-c,9).should == nil
+ end
- it "returns nil for negative nrows or ncols" do
- @matrix.minor(0,1,0,-1).should == nil
- @matrix.minor(0,-1,0,1).should == nil
- end
+ it "returns nil for negative nrows or ncols" do
+ @matrix.minor(0,1,0,-1).should == nil
+ @matrix.minor(0,-1,0,1).should == nil
+ end
- it "start counting backwards for start_row or start_col below zero" do
- @matrix.minor(0, 1, -1, 1).should == @matrix.minor(0, 1, 1, 1)
- @matrix.minor(-1, 1, 0, 1).should == @matrix.minor(2, 1, 0, 1)
- end
+ it "start counting backwards for start_row or start_col below zero" do
+ @matrix.minor(0, 1, -1, 1).should == @matrix.minor(0, 1, 1, 1)
+ @matrix.minor(-1, 1, 0, 1).should == @matrix.minor(2, 1, 0, 1)
+ end
- it "returns empty matrices for extreme start_row/col" do
- @matrix.minor(3,10,1,10).should == Matrix.columns([[]])
- @matrix.minor(1,10,2,10).should == Matrix[[], []]
- @matrix.minor(3,0,0,10).should == Matrix.columns([[], []])
- end
+ it "returns empty matrices for extreme start_row/col" do
+ @matrix.minor(3,10,1,10).should == Matrix.columns([[]])
+ @matrix.minor(1,10,2,10).should == Matrix[[], []]
+ @matrix.minor(3,0,0,10).should == Matrix.columns([[], []])
+ end
- it "ignores big nrows or ncols" do
- @matrix.minor(0,1,0,20).should == Matrix[ [1, 2] ]
- @matrix.minor(1,20,1,1).should == Matrix[ [4], [6] ]
- end
+ it "ignores big nrows or ncols" do
+ @matrix.minor(0,1,0,20).should == Matrix[ [1, 2] ]
+ @matrix.minor(1,20,1,1).should == Matrix[ [4], [6] ]
end
+ end
- describe "with col_range, row_range" do
- it "returns the given portion of the Matrix" do
- @matrix.minor(0..0, 0..1).should == Matrix[ [1, 2] ]
- @matrix.minor(1..2, 1..2).should == Matrix[ [4], [6] ]
- @matrix.minor(1...3, 1...3).should == Matrix[ [4], [6] ]
- end
+ describe "with col_range, row_range" do
+ it "returns the given portion of the Matrix" do
+ @matrix.minor(0..0, 0..1).should == Matrix[ [1, 2] ]
+ @matrix.minor(1..2, 1..2).should == Matrix[ [4], [6] ]
+ @matrix.minor(1...3, 1...3).should == Matrix[ [4], [6] ]
+ end
- it "returns nil if col_range or row_range is out of range" do
- r = @matrix.row_size + 1
- c = @matrix.column_size + 1
- @matrix.minor(r..6, c..6).should == nil
- @matrix.minor(0..1, c..6).should == nil
- @matrix.minor(r..6, 0..1).should == nil
- @matrix.minor(-r..6, -c..6).should == nil
- @matrix.minor(0..1, -c..6).should == nil
- @matrix.minor(-r..6, 0..1).should == nil
- end
+ it "returns nil if col_range or row_range is out of range" do
+ r = @matrix.row_size + 1
+ c = @matrix.column_size + 1
+ @matrix.minor(r..6, c..6).should == nil
+ @matrix.minor(0..1, c..6).should == nil
+ @matrix.minor(r..6, 0..1).should == nil
+ @matrix.minor(-r..6, -c..6).should == nil
+ @matrix.minor(0..1, -c..6).should == nil
+ @matrix.minor(-r..6, 0..1).should == nil
+ end
- it "start counting backwards for col_range or row_range below zero" do
- @matrix.minor(0..1, -2..-1).should == @matrix.minor(0..1, 0..1)
- @matrix.minor(0..1, -2..1).should == @matrix.minor(0..1, 0..1)
- @matrix.minor(-2..-1, 0..1).should == @matrix.minor(1..2, 0..1)
- @matrix.minor(-2..2, 0..1).should == @matrix.minor(1..2, 0..1)
- end
+ it "start counting backwards for col_range or row_range below zero" do
+ @matrix.minor(0..1, -2..-1).should == @matrix.minor(0..1, 0..1)
+ @matrix.minor(0..1, -2..1).should == @matrix.minor(0..1, 0..1)
+ @matrix.minor(-2..-1, 0..1).should == @matrix.minor(1..2, 0..1)
+ @matrix.minor(-2..2, 0..1).should == @matrix.minor(1..2, 0..1)
end
+ end
- describe "for a subclass of Matrix" do
- it "returns an instance of that subclass" do
- MatrixSub.ins.minor(0, 1, 0, 1).should be_an_instance_of(MatrixSub)
- end
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.ins.minor(0, 1, 0, 1).should be_an_instance_of(MatrixSub)
end
end
end
diff --git a/spec/ruby/library/matrix/minus_spec.rb b/spec/ruby/library/matrix/minus_spec.rb
index 27dfbeaea5..95cf4a6072 100644
--- a/spec/ruby/library/matrix/minus_spec.rb
+++ b/spec/ruby/library/matrix/minus_spec.rb
@@ -1,45 +1,42 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require_relative 'fixtures/classes'
- require 'matrix'
-
- describe "Matrix#-" do
- before :each do
- @a = Matrix[ [1, 2], [3, 4] ]
- @b = Matrix[ [4, 5], [6, 7] ]
- end
+describe "Matrix#-" do
+ before :each do
+ @a = Matrix[ [1, 2], [3, 4] ]
+ @b = Matrix[ [4, 5], [6, 7] ]
+ end
- it "returns the result of subtracting the corresponding elements of other from self" do
- (@a - @b).should == Matrix[ [-3,-3], [-3,-3] ]
- end
+ it "returns the result of subtracting the corresponding elements of other from self" do
+ (@a - @b).should == Matrix[ [-3,-3], [-3,-3] ]
+ end
- it "returns an instance of Matrix" do
- (@a - @b).should be_kind_of(Matrix)
- end
+ it "returns an instance of Matrix" do
+ (@a - @b).should be_kind_of(Matrix)
+ end
- it "raises a Matrix::ErrDimensionMismatch if the matrices are different sizes" do
- -> { @a - Matrix[ [1] ] }.should raise_error(Matrix::ErrDimensionMismatch)
- end
+ it "raises a Matrix::ErrDimensionMismatch if the matrices are different sizes" do
+ -> { @a - Matrix[ [1] ] }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
- it "raises a ExceptionForMatrix::ErrOperationNotDefined if other is a Numeric Type" do
- -> { @a - 2 }.should raise_error(Matrix::ErrOperationNotDefined)
- -> { @a - 1.2 }.should raise_error(Matrix::ErrOperationNotDefined)
- -> { @a - bignum_value }.should raise_error(Matrix::ErrOperationNotDefined)
- end
+ it "raises a ExceptionForMatrix::ErrOperationNotDefined if other is a Numeric Type" do
+ -> { @a - 2 }.should raise_error(Matrix::ErrOperationNotDefined)
+ -> { @a - 1.2 }.should raise_error(Matrix::ErrOperationNotDefined)
+ -> { @a - bignum_value }.should raise_error(Matrix::ErrOperationNotDefined)
+ end
- it "raises a TypeError if other is of wrong type" do
- -> { @a - nil }.should raise_error(TypeError)
- -> { @a - "a" }.should raise_error(TypeError)
- -> { @a - [ [1, 2] ] }.should raise_error(TypeError)
- -> { @a - Object.new }.should raise_error(TypeError)
- end
+ it "raises a TypeError if other is of wrong type" do
+ -> { @a - nil }.should raise_error(TypeError)
+ -> { @a - "a" }.should raise_error(TypeError)
+ -> { @a - [ [1, 2] ] }.should raise_error(TypeError)
+ -> { @a - Object.new }.should raise_error(TypeError)
+ end
- describe "for a subclass of Matrix" do
- it "returns an instance of that subclass" do
- m = MatrixSub.ins
- (m-m).should be_an_instance_of(MatrixSub)
- end
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ m = MatrixSub.ins
+ (m-m).should be_an_instance_of(MatrixSub)
end
end
end
diff --git a/spec/ruby/library/matrix/multiply_spec.rb b/spec/ruby/library/matrix/multiply_spec.rb
index a63fcf4020..206868af92 100644
--- a/spec/ruby/library/matrix/multiply_spec.rb
+++ b/spec/ruby/library/matrix/multiply_spec.rb
@@ -1,71 +1,69 @@
require_relative '../../spec_helper'
-ruby_version_is ""..."3.1" do
- require_relative 'fixtures/classes'
- require 'matrix'
+require_relative 'fixtures/classes'
+require 'matrix'
- describe "Matrix#*" do
- before :each do
- @a = Matrix[ [1, 2], [3, 4] ]
- @b = Matrix[ [4, 5], [6, 7] ]
- end
+describe "Matrix#*" do
+ before :each do
+ @a = Matrix[ [1, 2], [3, 4] ]
+ @b = Matrix[ [4, 5], [6, 7] ]
+ end
- it "returns the result of multiplying the corresponding elements of self and a Matrix" do
- (@a * @b).should == Matrix[ [16,19], [36,43] ]
- end
+ it "returns the result of multiplying the corresponding elements of self and a Matrix" do
+ (@a * @b).should == Matrix[ [16,19], [36,43] ]
+ end
- it "returns the result of multiplying the corresponding elements of self and a Vector" do
- (@a * Vector[1,2]).should == Vector[5, 11]
- end
+ it "returns the result of multiplying the corresponding elements of self and a Vector" do
+ (@a * Vector[1,2]).should == Vector[5, 11]
+ end
- it "returns the result of multiplying the elements of self and a Fixnum" do
- (@a * 2).should == Matrix[ [2, 4], [6, 8] ]
- end
+ it "returns the result of multiplying the elements of self and a Fixnum" do
+ (@a * 2).should == Matrix[ [2, 4], [6, 8] ]
+ end
- it "returns the result of multiplying the elements of self and a Bignum" do
- (@a * bignum_value).should == Matrix[
- [18446744073709551616, 36893488147419103232],
- [55340232221128654848, 73786976294838206464]
- ]
- end
+ it "returns the result of multiplying the elements of self and a Bignum" do
+ (@a * bignum_value).should == Matrix[
+ [18446744073709551616, 36893488147419103232],
+ [55340232221128654848, 73786976294838206464]
+ ]
+ end
- it "returns the result of multiplying the elements of self and a Float" do
- (@a * 2.0).should == Matrix[ [2.0, 4.0], [6.0, 8.0] ]
- end
+ it "returns the result of multiplying the elements of self and a Float" do
+ (@a * 2.0).should == Matrix[ [2.0, 4.0], [6.0, 8.0] ]
+ end
- it "raises a Matrix::ErrDimensionMismatch if the matrices are different sizes" do
- -> { @a * Matrix[ [1] ] }.should raise_error(Matrix::ErrDimensionMismatch)
- end
+ it "raises a Matrix::ErrDimensionMismatch if the matrices are different sizes" do
+ -> { @a * Matrix[ [1] ] }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
- it "returns a zero matrix if (nx0) * (0xn)" do
- (Matrix[[],[],[]] * Matrix.columns([[],[],[]])).should == Matrix.zero(3)
- end
+ it "returns a zero matrix if (nx0) * (0xn)" do
+ (Matrix[[],[],[]] * Matrix.columns([[],[],[]])).should == Matrix.zero(3)
+ end
- it "returns an empty matrix if (0xn) * (nx0)" do
- (Matrix.columns([[],[],[]]) * Matrix[[],[],[]]).should == Matrix[]
- end
+ it "returns an empty matrix if (0xn) * (nx0)" do
+ (Matrix.columns([[],[],[]]) * Matrix[[],[],[]]).should == Matrix[]
+ end
- it "returns a mx0 matrix if (mxn) * (nx0)" do
- (Matrix[[1,2],[3,4],[5,6]] * Matrix[[],[]]).should == Matrix[[],[],[]]
- end
+ it "returns a mx0 matrix if (mxn) * (nx0)" do
+ (Matrix[[1,2],[3,4],[5,6]] * Matrix[[],[]]).should == Matrix[[],[],[]]
+ end
- it "returns a 0xm matrix if (0xm) * (mxn)" do
- (Matrix.columns([[], [], []]) * Matrix[[1,2],[3,4],[5,6]]).should == Matrix.columns([[],[]])
- end
+ it "returns a 0xm matrix if (0xm) * (mxn)" do
+ (Matrix.columns([[], [], []]) * Matrix[[1,2],[3,4],[5,6]]).should == Matrix.columns([[],[]])
+ end
- it "raises a TypeError if other is of wrong type" do
- -> { @a * nil }.should raise_error(TypeError)
- -> { @a * "a" }.should raise_error(TypeError)
- -> { @a * [ [1, 2] ] }.should raise_error(TypeError)
- -> { @a * Object.new }.should raise_error(TypeError)
- end
+ it "raises a TypeError if other is of wrong type" do
+ -> { @a * nil }.should raise_error(TypeError)
+ -> { @a * "a" }.should raise_error(TypeError)
+ -> { @a * [ [1, 2] ] }.should raise_error(TypeError)
+ -> { @a * Object.new }.should raise_error(TypeError)
+ end
- describe "for a subclass of Matrix" do
- it "returns an instance of that subclass" do
- m = MatrixSub.ins
- (m*m).should be_an_instance_of(MatrixSub)
- (m*1).should be_an_instance_of(MatrixSub)
- end
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ m = MatrixSub.ins
+ (m*m).should be_an_instance_of(MatrixSub)
+ (m*1).should be_an_instance_of(MatrixSub)
end
end
end
diff --git a/spec/ruby/library/matrix/new_spec.rb b/spec/ruby/library/matrix/new_spec.rb
index 4be2e17116..3005066846 100644
--- a/spec/ruby/library/matrix/new_spec.rb
+++ b/spec/ruby/library/matrix/new_spec.rb
@@ -1,11 +1,8 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix.new" do
- it "is private" do
- Matrix.should have_private_method(:new)
- end
+describe "Matrix.new" do
+ it "is private" do
+ Matrix.should have_private_method(:new)
end
end
diff --git a/spec/ruby/library/matrix/normal_spec.rb b/spec/ruby/library/matrix/normal_spec.rb
index 2f2e138c1b..a9e6c645fa 100644
--- a/spec/ruby/library/matrix/normal_spec.rb
+++ b/spec/ruby/library/matrix/normal_spec.rb
@@ -1,29 +1,26 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
+describe "Matrix.normal?" do
+ # it "returns false for non normal matrices" do
+ # Matrix[[0, 1], [1, 2]].should_not.normal?
+ # end
- describe "Matrix.normal?" do
- # it "returns false for non normal matrices" do
- # Matrix[[0, 1], [1, 2]].should_not.normal?
- # end
-
- it "returns true for normal matrices" do
- Matrix[[1, 1, 0], [0, 1, 1], [1, 0, 1]].should.normal?
- Matrix[[0, Complex(0, 2)], [Complex(0, -2), 0]].should.normal?
- end
+ it "returns true for normal matrices" do
+ Matrix[[1, 1, 0], [0, 1, 1], [1, 0, 1]].should.normal?
+ Matrix[[0, Complex(0, 2)], [Complex(0, -2), 0]].should.normal?
+ end
- it "raises an error for rectangular matrices" do
- [
- Matrix[[0], [0]],
- Matrix[[0, 0]],
- Matrix.empty(0, 2),
- Matrix.empty(2, 0),
- ].each do |rectangular_matrix|
- -> {
- rectangular_matrix.normal?
- }.should raise_error(Matrix::ErrDimensionMismatch)
- end
+ it "raises an error for rectangular matrices" do
+ [
+ Matrix[[0], [0]],
+ Matrix[[0, 0]],
+ Matrix.empty(0, 2),
+ Matrix.empty(2, 0),
+ ].each do |rectangular_matrix|
+ -> {
+ rectangular_matrix.normal?
+ }.should raise_error(Matrix::ErrDimensionMismatch)
end
end
end
diff --git a/spec/ruby/library/matrix/orthogonal_spec.rb b/spec/ruby/library/matrix/orthogonal_spec.rb
index eb06305040..26afe89ff0 100644
--- a/spec/ruby/library/matrix/orthogonal_spec.rb
+++ b/spec/ruby/library/matrix/orthogonal_spec.rb
@@ -1,29 +1,26 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix.orthogonal?" do
- it "returns false for non orthogonal matrices" do
- Matrix[[0, 1], [1, 2]].should_not.orthogonal?
- Matrix[[1, 1, 0], [0, 1, 1], [1, 0, 1]].should_not.orthogonal?
- end
+describe "Matrix.orthogonal?" do
+ it "returns false for non orthogonal matrices" do
+ Matrix[[0, 1], [1, 2]].should_not.orthogonal?
+ Matrix[[1, 1, 0], [0, 1, 1], [1, 0, 1]].should_not.orthogonal?
+ end
- it "returns true for orthogonal matrices" do
- Matrix[[0, 1], [1, 0]].should.orthogonal?
- end
+ it "returns true for orthogonal matrices" do
+ Matrix[[0, 1], [1, 0]].should.orthogonal?
+ end
- it "raises an error for rectangular matrices" do
- [
- Matrix[[0], [0]],
- Matrix[[0, 0]],
- Matrix.empty(0, 2),
- Matrix.empty(2, 0),
- ].each do |rectangular_matrix|
- -> {
- rectangular_matrix.orthogonal?
- }.should raise_error(Matrix::ErrDimensionMismatch)
- end
+ it "raises an error for rectangular matrices" do
+ [
+ Matrix[[0], [0]],
+ Matrix[[0, 0]],
+ Matrix.empty(0, 2),
+ Matrix.empty(2, 0),
+ ].each do |rectangular_matrix|
+ -> {
+ rectangular_matrix.orthogonal?
+ }.should raise_error(Matrix::ErrDimensionMismatch)
end
end
end
diff --git a/spec/ruby/library/matrix/permutation_spec.rb b/spec/ruby/library/matrix/permutation_spec.rb
index ed8edad755..825a9d982c 100644
--- a/spec/ruby/library/matrix/permutation_spec.rb
+++ b/spec/ruby/library/matrix/permutation_spec.rb
@@ -1,35 +1,32 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix#permutation?" do
- it "returns true for a permutation Matrix" do
- Matrix[[0, 1, 0], [0, 0, 1], [1, 0, 0]].permutation?.should be_true
- end
+describe "Matrix#permutation?" do
+ it "returns true for a permutation Matrix" do
+ Matrix[[0, 1, 0], [0, 0, 1], [1, 0, 0]].permutation?.should be_true
+ end
- it "returns false for a non permutation square Matrix" do
- Matrix[[0, 1], [0, 0]].permutation?.should be_false
- Matrix[[-1, 0], [0, -1]].permutation?.should be_false
- Matrix[[1, 0], [1, 0]].permutation?.should be_false
- Matrix[[1, 0], [1, 1]].permutation?.should be_false
- end
+ it "returns false for a non permutation square Matrix" do
+ Matrix[[0, 1], [0, 0]].permutation?.should be_false
+ Matrix[[-1, 0], [0, -1]].permutation?.should be_false
+ Matrix[[1, 0], [1, 0]].permutation?.should be_false
+ Matrix[[1, 0], [1, 1]].permutation?.should be_false
+ end
- it "returns true for an empty 0x0 matrix" do
- Matrix.empty(0,0).permutation?.should be_true
- end
+ it "returns true for an empty 0x0 matrix" do
+ Matrix.empty(0,0).permutation?.should be_true
+ end
- it "raises an error for rectangular matrices" do
- [
- Matrix[[0], [0]],
- Matrix[[0, 0]],
- Matrix.empty(0, 2),
- Matrix.empty(2, 0),
- ].each do |rectangular_matrix|
- -> {
- rectangular_matrix.permutation?
- }.should raise_error(Matrix::ErrDimensionMismatch)
- end
+ it "raises an error for rectangular matrices" do
+ [
+ Matrix[[0], [0]],
+ Matrix[[0, 0]],
+ Matrix.empty(0, 2),
+ Matrix.empty(2, 0),
+ ].each do |rectangular_matrix|
+ -> {
+ rectangular_matrix.permutation?
+ }.should raise_error(Matrix::ErrDimensionMismatch)
end
end
end
diff --git a/spec/ruby/library/matrix/plus_spec.rb b/spec/ruby/library/matrix/plus_spec.rb
index 99e6615778..2706bad060 100644
--- a/spec/ruby/library/matrix/plus_spec.rb
+++ b/spec/ruby/library/matrix/plus_spec.rb
@@ -1,45 +1,42 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require_relative 'fixtures/classes'
- require 'matrix'
-
- describe "Matrix#+" do
- before :each do
- @a = Matrix[ [1,2], [3,4] ]
- @b = Matrix[ [4,5], [6,7] ]
- end
+describe "Matrix#+" do
+ before :each do
+ @a = Matrix[ [1,2], [3,4] ]
+ @b = Matrix[ [4,5], [6,7] ]
+ end
- it "returns the result of adding the corresponding elements of self and other" do
- (@a + @b).should == Matrix[ [5,7], [9,11] ]
- end
+ it "returns the result of adding the corresponding elements of self and other" do
+ (@a + @b).should == Matrix[ [5,7], [9,11] ]
+ end
- it "returns an instance of Matrix" do
- (@a + @b).should be_kind_of(Matrix)
- end
+ it "returns an instance of Matrix" do
+ (@a + @b).should be_kind_of(Matrix)
+ end
- it "raises a Matrix::ErrDimensionMismatch if the matrices are different sizes" do
- -> { @a + Matrix[ [1] ] }.should raise_error(Matrix::ErrDimensionMismatch)
- end
+ it "raises a Matrix::ErrDimensionMismatch if the matrices are different sizes" do
+ -> { @a + Matrix[ [1] ] }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
- it "raises a ExceptionForMatrix::ErrOperationNotDefined if other is a Numeric Type" do
- -> { @a + 2 }.should raise_error(ExceptionForMatrix::ErrOperationNotDefined)
- -> { @a + 1.2 }.should raise_error(ExceptionForMatrix::ErrOperationNotDefined)
- -> { @a + bignum_value }.should raise_error(ExceptionForMatrix::ErrOperationNotDefined)
- end
+ it "raises a ExceptionForMatrix::ErrOperationNotDefined if other is a Numeric Type" do
+ -> { @a + 2 }.should raise_error(ExceptionForMatrix::ErrOperationNotDefined)
+ -> { @a + 1.2 }.should raise_error(ExceptionForMatrix::ErrOperationNotDefined)
+ -> { @a + bignum_value }.should raise_error(ExceptionForMatrix::ErrOperationNotDefined)
+ end
- it "raises a TypeError if other is of wrong type" do
- -> { @a + nil }.should raise_error(TypeError)
- -> { @a + "a" }.should raise_error(TypeError)
- -> { @a + [ [1, 2] ] }.should raise_error(TypeError)
- -> { @a + Object.new }.should raise_error(TypeError)
- end
+ it "raises a TypeError if other is of wrong type" do
+ -> { @a + nil }.should raise_error(TypeError)
+ -> { @a + "a" }.should raise_error(TypeError)
+ -> { @a + [ [1, 2] ] }.should raise_error(TypeError)
+ -> { @a + Object.new }.should raise_error(TypeError)
+ end
- describe "for a subclass of Matrix" do
- it "returns an instance of that subclass" do
- m = MatrixSub.ins
- (m+m).should be_an_instance_of(MatrixSub)
- end
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ m = MatrixSub.ins
+ (m+m).should be_an_instance_of(MatrixSub)
end
end
end
diff --git a/spec/ruby/library/matrix/rank_spec.rb b/spec/ruby/library/matrix/rank_spec.rb
index fc795b58c1..d4403d23ed 100644
--- a/spec/ruby/library/matrix/rank_spec.rb
+++ b/spec/ruby/library/matrix/rank_spec.rb
@@ -1,22 +1,19 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix#rank" do
- it "returns the rank of the Matrix" do
- Matrix[ [7,6], [3,9] ].rank.should == 2
- end
+describe "Matrix#rank" do
+ it "returns the rank of the Matrix" do
+ Matrix[ [7,6], [3,9] ].rank.should == 2
+ end
- it "doesn't loop forever" do
- Matrix[ [1,2,3], [4,5,6], [7,8,9] ].rank.should == 2
- Matrix[ [1, 2, 0, 3], [1, -2, 3, 0], [0, 0, 4, 8], [2, 4, 0, 6] ].rank.
- should == 3
- end
+ it "doesn't loop forever" do
+ Matrix[ [1,2,3], [4,5,6], [7,8,9] ].rank.should == 2
+ Matrix[ [1, 2, 0, 3], [1, -2, 3, 0], [0, 0, 4, 8], [2, 4, 0, 6] ].rank.
+ should == 3
+ end
- it "works for some easy rectangular matrices" do
- Matrix[[0,0],[0,0],[1,0]].rank.should == 1
- Matrix[[0,1],[0,0],[1,0]].rank.should == 2
- end
+ it "works for some easy rectangular matrices" do
+ Matrix[[0,0],[0,0],[1,0]].rank.should == 1
+ Matrix[[0,1],[0,0],[1,0]].rank.should == 2
end
end
diff --git a/spec/ruby/library/matrix/real_spec.rb b/spec/ruby/library/matrix/real_spec.rb
index 63a6bde923..38033c63c8 100644
--- a/spec/ruby/library/matrix/real_spec.rb
+++ b/spec/ruby/library/matrix/real_spec.rb
@@ -1,46 +1,43 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require_relative 'fixtures/classes'
- require 'matrix'
-
- describe "Matrix#real?" do
- it "returns true for matrices with all real entries" do
- Matrix[ [1, 2], [3, 4] ].real?.should be_true
- Matrix[ [1.9, 2], [3, 4] ].real?.should be_true
- end
+describe "Matrix#real?" do
+ it "returns true for matrices with all real entries" do
+ Matrix[ [1, 2], [3, 4] ].real?.should be_true
+ Matrix[ [1.9, 2], [3, 4] ].real?.should be_true
+ end
- it "returns true for empty matrices" do
- Matrix.empty.real?.should be_true
- end
+ it "returns true for empty matrices" do
+ Matrix.empty.real?.should be_true
+ end
- it "returns false if one element is a Complex" do
- Matrix[ [Complex(1,1), 2], [3, 4] ].real?.should be_false
- end
+ it "returns false if one element is a Complex" do
+ Matrix[ [Complex(1,1), 2], [3, 4] ].real?.should be_false
+ end
- # Guard against the Mathn library
- guard -> { !defined?(Math.rsqrt) } do
- it "returns false if one element is a Complex whose imaginary part is 0" do
- Matrix[ [Complex(1,0), 2], [3, 4] ].real?.should be_false
- end
+ # Guard against the Mathn library
+ guard -> { !defined?(Math.rsqrt) } do
+ it "returns false if one element is a Complex whose imaginary part is 0" do
+ Matrix[ [Complex(1,0), 2], [3, 4] ].real?.should be_false
end
end
+end
- describe "Matrix#real" do
- it "returns a matrix with the real part of the elements of the receiver" do
- Matrix[ [1, 2], [3, 4] ].real.should == Matrix[ [1, 2], [3, 4] ]
- Matrix[ [1.9, Complex(1,1)], [Complex(-0.42, 0), 4] ].real.should == Matrix[ [1.9, 1], [-0.42, 4] ]
- end
+describe "Matrix#real" do
+ it "returns a matrix with the real part of the elements of the receiver" do
+ Matrix[ [1, 2], [3, 4] ].real.should == Matrix[ [1, 2], [3, 4] ]
+ Matrix[ [1.9, Complex(1,1)], [Complex(-0.42, 0), 4] ].real.should == Matrix[ [1.9, 1], [-0.42, 4] ]
+ end
- it "returns empty matrices on the same size if empty" do
- Matrix.empty(0, 3).real.should == Matrix.empty(0, 3)
- Matrix.empty(3, 0).real.should == Matrix.empty(3, 0)
- end
+ it "returns empty matrices on the same size if empty" do
+ Matrix.empty(0, 3).real.should == Matrix.empty(0, 3)
+ Matrix.empty(3, 0).real.should == Matrix.empty(3, 0)
+ end
- describe "for a subclass of Matrix" do
- it "returns an instance of that subclass" do
- MatrixSub.ins.real.should be_an_instance_of(MatrixSub)
- end
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.ins.real.should be_an_instance_of(MatrixSub)
end
end
end
diff --git a/spec/ruby/library/matrix/rect_spec.rb b/spec/ruby/library/matrix/rect_spec.rb
index b3b7ac05c9..83a0404e47 100644
--- a/spec/ruby/library/matrix/rect_spec.rb
+++ b/spec/ruby/library/matrix/rect_spec.rb
@@ -1,9 +1,6 @@
require_relative '../../spec_helper'
+require_relative 'shared/rectangular'
-ruby_version_is ""..."3.1" do
- require_relative 'shared/rectangular'
-
- describe "Matrix#rect" do
- it_behaves_like :matrix_rectangular, :rect
- end
+describe "Matrix#rect" do
+ it_behaves_like :matrix_rectangular, :rect
end
diff --git a/spec/ruby/library/matrix/rectangular_spec.rb b/spec/ruby/library/matrix/rectangular_spec.rb
index 1fc2e0e60d..a235fac640 100644
--- a/spec/ruby/library/matrix/rectangular_spec.rb
+++ b/spec/ruby/library/matrix/rectangular_spec.rb
@@ -1,9 +1,6 @@
require_relative '../../spec_helper'
+require_relative 'shared/rectangular'
-ruby_version_is ""..."3.1" do
- require_relative 'shared/rectangular'
-
- describe "Matrix#rectangular" do
- it_behaves_like :matrix_rectangular, :rectangular
- end
+describe "Matrix#rectangular" do
+ it_behaves_like :matrix_rectangular, :rectangular
end
diff --git a/spec/ruby/library/matrix/regular_spec.rb b/spec/ruby/library/matrix/regular_spec.rb
index f6688bba30..3699d0ef8b 100644
--- a/spec/ruby/library/matrix/regular_spec.rb
+++ b/spec/ruby/library/matrix/regular_spec.rb
@@ -1,34 +1,31 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
+describe "Matrix#regular?" do
- describe "Matrix#regular?" do
+ it "returns false for singular matrices" do
+ m = Matrix[ [1,2,3], [3,4,3], [0,0,0] ]
+ m.regular?.should be_false
- it "returns false for singular matrices" do
- m = Matrix[ [1,2,3], [3,4,3], [0,0,0] ]
- m.regular?.should be_false
-
- m = Matrix[ [1,2,9], [3,4,9], [1,2,9] ]
- m.regular?.should be_false
- end
+ m = Matrix[ [1,2,9], [3,4,9], [1,2,9] ]
+ m.regular?.should be_false
+ end
- it "returns true if the Matrix is regular" do
- Matrix[ [0,1], [1,0] ].regular?.should be_true
- end
+ it "returns true if the Matrix is regular" do
+ Matrix[ [0,1], [1,0] ].regular?.should be_true
+ end
- it "returns true for an empty 0x0 matrix" do
- Matrix.empty(0,0).regular?.should be_true
- end
+ it "returns true for an empty 0x0 matrix" do
+ Matrix.empty(0,0).regular?.should be_true
+ end
- it "raises an error for rectangular matrices" do
- -> {
- Matrix[[1], [2], [3]].regular?
- }.should raise_error(Matrix::ErrDimensionMismatch)
+ it "raises an error for rectangular matrices" do
+ -> {
+ Matrix[[1], [2], [3]].regular?
+ }.should raise_error(Matrix::ErrDimensionMismatch)
- -> {
- Matrix.empty(3,0).regular?
- }.should raise_error(Matrix::ErrDimensionMismatch)
- end
+ -> {
+ Matrix.empty(3,0).regular?
+ }.should raise_error(Matrix::ErrDimensionMismatch)
end
end
diff --git a/spec/ruby/library/matrix/round_spec.rb b/spec/ruby/library/matrix/round_spec.rb
index db33268414..1dc29df890 100644
--- a/spec/ruby/library/matrix/round_spec.rb
+++ b/spec/ruby/library/matrix/round_spec.rb
@@ -1,24 +1,21 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require_relative 'fixtures/classes'
- require 'matrix'
-
- describe "Matrix#round" do
- it "returns a matrix with all entries rounded" do
- Matrix[ [1, 2.34], [5.67, 8] ].round.should == Matrix[ [1, 2], [6, 8] ]
- Matrix[ [1, 2.34], [5.67, 8] ].round(1).should == Matrix[ [1, 2.3], [5.7, 8] ]
- end
+describe "Matrix#round" do
+ it "returns a matrix with all entries rounded" do
+ Matrix[ [1, 2.34], [5.67, 8] ].round.should == Matrix[ [1, 2], [6, 8] ]
+ Matrix[ [1, 2.34], [5.67, 8] ].round(1).should == Matrix[ [1, 2.3], [5.7, 8] ]
+ end
- it "returns empty matrices on the same size if empty" do
- Matrix.empty(0, 3).round.should == Matrix.empty(0, 3)
- Matrix.empty(3, 0).round(42).should == Matrix.empty(3, 0)
- end
+ it "returns empty matrices on the same size if empty" do
+ Matrix.empty(0, 3).round.should == Matrix.empty(0, 3)
+ Matrix.empty(3, 0).round(42).should == Matrix.empty(3, 0)
+ end
- describe "for a subclass of Matrix" do
- it "returns an instance of that subclass" do
- MatrixSub.ins.round.should be_an_instance_of(MatrixSub)
- end
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.ins.round.should be_an_instance_of(MatrixSub)
end
end
end
diff --git a/spec/ruby/library/matrix/row_size_spec.rb b/spec/ruby/library/matrix/row_size_spec.rb
index fca5a846ab..eb3aef2e2f 100644
--- a/spec/ruby/library/matrix/row_size_spec.rb
+++ b/spec/ruby/library/matrix/row_size_spec.rb
@@ -1,16 +1,13 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix#row_size" do
- it "returns the number rows" do
- Matrix[ [1,2], [3, 4], [5, 6] ].row_size.should == 3
- end
-
- it "returns the number rows even for some empty matrices" do
- Matrix[ [], [], [] ].row_size.should == 3
- end
+describe "Matrix#row_size" do
+ it "returns the number rows" do
+ Matrix[ [1,2], [3, 4], [5, 6] ].row_size.should == 3
+ end
+ it "returns the number rows even for some empty matrices" do
+ Matrix[ [], [], [] ].row_size.should == 3
end
+
end
diff --git a/spec/ruby/library/matrix/row_spec.rb b/spec/ruby/library/matrix/row_spec.rb
index eb05cd9273..00b1f02a8e 100644
--- a/spec/ruby/library/matrix/row_spec.rb
+++ b/spec/ruby/library/matrix/row_spec.rb
@@ -1,39 +1,36 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix#row" do
- before :all do
- @m = Matrix[ [1, 2], [2, 3], [3, 4] ]
- end
+describe "Matrix#row" do
+ before :all do
+ @m = Matrix[ [1, 2], [2, 3], [3, 4] ]
+ end
- it "returns a Vector when called without a block" do
- @m.row(0).should == Vector[1,2]
- end
+ it "returns a Vector when called without a block" do
+ @m.row(0).should == Vector[1,2]
+ end
- it "yields the elements of the row when called with a block" do
- a = []
- @m.row(0) {|x| a << x}
- a.should == [1,2]
- end
+ it "yields the elements of the row when called with a block" do
+ a = []
+ @m.row(0) {|x| a << x}
+ a.should == [1,2]
+ end
- it "counts backwards for negative argument" do
- @m.row(-1).should == Vector[3, 4]
- end
+ it "counts backwards for negative argument" do
+ @m.row(-1).should == Vector[3, 4]
+ end
- it "returns self when called with a block" do
- @m.row(0) { |x| x }.should equal(@m)
- end
+ it "returns self when called with a block" do
+ @m.row(0) { |x| x }.should equal(@m)
+ end
- it "returns nil when out of bounds" do
- @m.row(3).should == nil
- @m.row(-4).should == nil
- end
+ it "returns nil when out of bounds" do
+ @m.row(3).should == nil
+ @m.row(-4).should == nil
+ end
- it "never yields when out of bounds" do
- -> { @m.row(3){ raise } }.should_not raise_error
- -> { @m.row(-4){ raise } }.should_not raise_error
- end
+ it "never yields when out of bounds" do
+ -> { @m.row(3){ raise } }.should_not raise_error
+ -> { @m.row(-4){ raise } }.should_not raise_error
end
end
diff --git a/spec/ruby/library/matrix/row_vector_spec.rb b/spec/ruby/library/matrix/row_vector_spec.rb
index 4c97d6013a..341437ee05 100644
--- a/spec/ruby/library/matrix/row_vector_spec.rb
+++ b/spec/ruby/library/matrix/row_vector_spec.rb
@@ -1,27 +1,24 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require_relative 'fixtures/classes'
- require 'matrix'
+describe "Matrix.row_vector" do
- describe "Matrix.row_vector" do
-
- it "returns a Matrix" do
- Matrix.row_vector([]).should be_an_instance_of(Matrix)
- end
+ it "returns a Matrix" do
+ Matrix.row_vector([]).should be_an_instance_of(Matrix)
+ end
- it "returns a single-row Matrix with the specified values" do
- Matrix.row_vector([1,2]).should == Matrix[ [1,2] ]
- end
+ it "returns a single-row Matrix with the specified values" do
+ Matrix.row_vector([1,2]).should == Matrix[ [1,2] ]
+ end
- it "returns a 1x0 matrix when called with an empty Array" do
- Matrix.row_vector([]).should == Matrix[ [] ]
- end
+ it "returns a 1x0 matrix when called with an empty Array" do
+ Matrix.row_vector([]).should == Matrix[ [] ]
+ end
- describe "for a subclass of Matrix" do
- it "returns an instance of that subclass" do
- MatrixSub.row_vector([1]).should be_an_instance_of(MatrixSub)
- end
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.row_vector([1]).should be_an_instance_of(MatrixSub)
end
end
end
diff --git a/spec/ruby/library/matrix/row_vectors_spec.rb b/spec/ruby/library/matrix/row_vectors_spec.rb
index d01a4ca10d..6f99c439a6 100644
--- a/spec/ruby/library/matrix/row_vectors_spec.rb
+++ b/spec/ruby/library/matrix/row_vectors_spec.rb
@@ -1,29 +1,26 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
+describe "Matrix#row_vectors" do
- describe "Matrix#row_vectors" do
-
- before :each do
- @vectors = Matrix[ [1,2], [3,4] ].row_vectors
- end
+ before :each do
+ @vectors = Matrix[ [1,2], [3,4] ].row_vectors
+ end
- it "returns an Array" do
- Matrix[ [1,2], [3,4] ].row_vectors.should be_an_instance_of(Array)
- end
+ it "returns an Array" do
+ Matrix[ [1,2], [3,4] ].row_vectors.should be_an_instance_of(Array)
+ end
- it "returns an Array of Vectors" do
- @vectors.all? {|v| v.should be_an_instance_of(Vector)}
- end
+ it "returns an Array of Vectors" do
+ @vectors.all? {|v| v.should be_an_instance_of(Vector)}
+ end
- it "returns each row as a Vector" do
- @vectors.should == [Vector[1,2], Vector[3,4]]
- end
+ it "returns each row as a Vector" do
+ @vectors.should == [Vector[1,2], Vector[3,4]]
+ end
- it "returns an empty Array for empty matrices" do
- Matrix[].row_vectors.should == []
- Matrix[ [] ].row_vectors.should == [ Vector[] ]
- end
+ it "returns an empty Array for empty matrices" do
+ Matrix[].row_vectors.should == []
+ Matrix[ [] ].row_vectors.should == [ Vector[] ]
end
end
diff --git a/spec/ruby/library/matrix/rows_spec.rb b/spec/ruby/library/matrix/rows_spec.rb
index 5f0515a37b..41ba635775 100644
--- a/spec/ruby/library/matrix/rows_spec.rb
+++ b/spec/ruby/library/matrix/rows_spec.rb
@@ -1,44 +1,41 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require_relative 'fixtures/classes'
- require 'matrix'
-
- describe "Matrix.rows" do
- before :each do
- @a = [1, 2]
- @b = [3, 4]
- @m = Matrix.rows([@a, @b])
- end
+describe "Matrix.rows" do
+ before :each do
+ @a = [1, 2]
+ @b = [3, 4]
+ @m = Matrix.rows([@a, @b])
+ end
- it "returns a Matrix" do
- @m.should be_kind_of(Matrix)
- end
+ it "returns a Matrix" do
+ @m.should be_kind_of(Matrix)
+ end
- it "creates a matrix from argument rows" do
- @m.row(0).to_a.should == @a
- @m.row(1).to_a.should == @b
- end
+ it "creates a matrix from argument rows" do
+ @m.row(0).to_a.should == @a
+ @m.row(1).to_a.should == @b
+ end
- it "copies the original rows by default" do
- @a << 3
- @b << 6
- @m.row(0).should_not equal(@a)
- @m.row(1).should_not equal(@b)
- end
+ it "copies the original rows by default" do
+ @a << 3
+ @b << 6
+ @m.row(0).should_not equal(@a)
+ @m.row(1).should_not equal(@b)
+ end
- it "references the original rows if copy is false" do
- @m_ref = Matrix.rows([@a, @b], false)
- @a << 3
- @b << 6
- @m_ref.row(0).to_a.should == @a
- @m_ref.row(1).to_a.should == @b
- end
+ it "references the original rows if copy is false" do
+ @m_ref = Matrix.rows([@a, @b], false)
+ @a << 3
+ @b << 6
+ @m_ref.row(0).to_a.should == @a
+ @m_ref.row(1).to_a.should == @b
+ end
- describe "for a subclass of Matrix" do
- it "returns an instance of that subclass" do
- MatrixSub.rows([[0, 1], [0, 1]]).should be_an_instance_of(MatrixSub)
- end
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.rows([[0, 1], [0, 1]]).should be_an_instance_of(MatrixSub)
end
end
end
diff --git a/spec/ruby/library/matrix/scalar/Fail_spec.rb b/spec/ruby/library/matrix/scalar/Fail_spec.rb
index 4f774eda28..9d8f9bd5e8 100644
--- a/spec/ruby/library/matrix/scalar/Fail_spec.rb
+++ b/spec/ruby/library/matrix/scalar/Fail_spec.rb
@@ -1,9 +1,6 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix::Scalar#Fail" do
- it "needs to be reviewed for spec completeness"
- end
+describe "Matrix::Scalar#Fail" do
+ it "needs to be reviewed for spec completeness"
end
diff --git a/spec/ruby/library/matrix/scalar/Raise_spec.rb b/spec/ruby/library/matrix/scalar/Raise_spec.rb
index b405b6d6c5..27e11c1f77 100644
--- a/spec/ruby/library/matrix/scalar/Raise_spec.rb
+++ b/spec/ruby/library/matrix/scalar/Raise_spec.rb
@@ -1,9 +1,6 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix::Scalar#Raise" do
- it "needs to be reviewed for spec completeness"
- end
+describe "Matrix::Scalar#Raise" do
+ it "needs to be reviewed for spec completeness"
end
diff --git a/spec/ruby/library/matrix/scalar/divide_spec.rb b/spec/ruby/library/matrix/scalar/divide_spec.rb
index 0ef2206bf6..5d726943fe 100644
--- a/spec/ruby/library/matrix/scalar/divide_spec.rb
+++ b/spec/ruby/library/matrix/scalar/divide_spec.rb
@@ -1,9 +1,6 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix::Scalar#/" do
- it "needs to be reviewed for spec completeness"
- end
+describe "Matrix::Scalar#/" do
+ it "needs to be reviewed for spec completeness"
end
diff --git a/spec/ruby/library/matrix/scalar/exponent_spec.rb b/spec/ruby/library/matrix/scalar/exponent_spec.rb
index 87eea283d1..8e9ef52ff2 100644
--- a/spec/ruby/library/matrix/scalar/exponent_spec.rb
+++ b/spec/ruby/library/matrix/scalar/exponent_spec.rb
@@ -1,9 +1,6 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix::Scalar#**" do
- it "needs to be reviewed for spec completeness"
- end
+describe "Matrix::Scalar#**" do
+ it "needs to be reviewed for spec completeness"
end
diff --git a/spec/ruby/library/matrix/scalar/included_spec.rb b/spec/ruby/library/matrix/scalar/included_spec.rb
index bab80e5120..cb3beb2ecd 100644
--- a/spec/ruby/library/matrix/scalar/included_spec.rb
+++ b/spec/ruby/library/matrix/scalar/included_spec.rb
@@ -1,9 +1,6 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix::Scalar.included" do
- it "needs to be reviewed for spec completeness"
- end
+describe "Matrix::Scalar.included" do
+ it "needs to be reviewed for spec completeness"
end
diff --git a/spec/ruby/library/matrix/scalar/initialize_spec.rb b/spec/ruby/library/matrix/scalar/initialize_spec.rb
index af51562b43..23145ad0de 100644
--- a/spec/ruby/library/matrix/scalar/initialize_spec.rb
+++ b/spec/ruby/library/matrix/scalar/initialize_spec.rb
@@ -1,9 +1,6 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix::Scalar#initialize" do
- it "needs to be reviewed for spec completeness"
- end
+describe "Matrix::Scalar#initialize" do
+ it "needs to be reviewed for spec completeness"
end
diff --git a/spec/ruby/library/matrix/scalar/minus_spec.rb b/spec/ruby/library/matrix/scalar/minus_spec.rb
index 966db1d75b..c727ea1659 100644
--- a/spec/ruby/library/matrix/scalar/minus_spec.rb
+++ b/spec/ruby/library/matrix/scalar/minus_spec.rb
@@ -1,9 +1,6 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix::Scalar#-" do
- it "needs to be reviewed for spec completeness"
- end
+describe "Matrix::Scalar#-" do
+ it "needs to be reviewed for spec completeness"
end
diff --git a/spec/ruby/library/matrix/scalar/multiply_spec.rb b/spec/ruby/library/matrix/scalar/multiply_spec.rb
index 21caac478b..1a2a83d83e 100644
--- a/spec/ruby/library/matrix/scalar/multiply_spec.rb
+++ b/spec/ruby/library/matrix/scalar/multiply_spec.rb
@@ -1,9 +1,6 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix::Scalar#*" do
- it "needs to be reviewed for spec completeness"
- end
+describe "Matrix::Scalar#*" do
+ it "needs to be reviewed for spec completeness"
end
diff --git a/spec/ruby/library/matrix/scalar/plus_spec.rb b/spec/ruby/library/matrix/scalar/plus_spec.rb
index 78cdf9367f..c94689a702 100644
--- a/spec/ruby/library/matrix/scalar/plus_spec.rb
+++ b/spec/ruby/library/matrix/scalar/plus_spec.rb
@@ -1,9 +1,6 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix::Scalar#+" do
- it "needs to be reviewed for spec completeness"
- end
+describe "Matrix::Scalar#+" do
+ it "needs to be reviewed for spec completeness"
end
diff --git a/spec/ruby/library/matrix/scalar_spec.rb b/spec/ruby/library/matrix/scalar_spec.rb
index 1ec64d2d78..7fdd64c9d9 100644
--- a/spec/ruby/library/matrix/scalar_spec.rb
+++ b/spec/ruby/library/matrix/scalar_spec.rb
@@ -1,68 +1,65 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
+describe "Matrix.scalar" do
- describe "Matrix.scalar" do
-
- before :each do
- @side = 3
- @value = 8
- @a = Matrix.scalar(@side, @value)
- end
+ before :each do
+ @side = 3
+ @value = 8
+ @a = Matrix.scalar(@side, @value)
+ end
- it "returns a Matrix" do
- @a.should be_kind_of(Matrix)
- end
+ it "returns a Matrix" do
+ @a.should be_kind_of(Matrix)
+ end
- it "returns a n x n matrix" do
- @a.row_size.should == @side
- @a.column_size.should == @side
- end
+ it "returns a n x n matrix" do
+ @a.row_size.should == @side
+ @a.column_size.should == @side
+ end
- it "initializes diagonal to value" do
- (0...@a.row_size).each do |i|
- @a[i, i].should == @value
- end
+ it "initializes diagonal to value" do
+ (0...@a.row_size).each do |i|
+ @a[i, i].should == @value
end
+ end
- it "initializes all non-diagonal values to 0" do
- (0...@a.row_size).each do |i|
- (0...@a.column_size).each do |j|
- if i != j
- @a[i, j].should == 0
- end
+ it "initializes all non-diagonal values to 0" do
+ (0...@a.row_size).each do |i|
+ (0...@a.column_size).each do |j|
+ if i != j
+ @a[i, j].should == 0
end
end
end
+ end
- before :each do
- @side = 3
- @value = 8
- @a = Matrix.scalar(@side, @value)
- end
+ before :each do
+ @side = 3
+ @value = 8
+ @a = Matrix.scalar(@side, @value)
+ end
- it "returns a Matrix" do
- @a.should be_kind_of(Matrix)
- end
+ it "returns a Matrix" do
+ @a.should be_kind_of(Matrix)
+ end
- it "returns a square matrix, where the first argument specifies the side of the square" do
- @a.row_size.should == @side
- @a.column_size.should == @side
- end
+ it "returns a square matrix, where the first argument specifies the side of the square" do
+ @a.row_size.should == @side
+ @a.column_size.should == @side
+ end
- it "puts the second argument in all diagonal values" do
- (0...@a.row_size).each do |i|
- @a[i, i].should == @value
- end
+ it "puts the second argument in all diagonal values" do
+ (0...@a.row_size).each do |i|
+ @a[i, i].should == @value
end
+ end
- it "fills all values not on the main diagonal with 0" do
- (0...@a.row_size).each do |i|
- (0...@a.column_size).each do |j|
- if i != j
- @a[i, j].should == 0
- end
+ it "fills all values not on the main diagonal with 0" do
+ (0...@a.row_size).each do |i|
+ (0...@a.column_size).each do |j|
+ if i != j
+ @a[i, j].should == 0
end
end
end
diff --git a/spec/ruby/library/matrix/singular_spec.rb b/spec/ruby/library/matrix/singular_spec.rb
index 341c2675a8..7bba36a54a 100644
--- a/spec/ruby/library/matrix/singular_spec.rb
+++ b/spec/ruby/library/matrix/singular_spec.rb
@@ -1,34 +1,31 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
+describe "Matrix#singular?" do
+ it "returns true for singular matrices" do
+ m = Matrix[ [1,2,3], [3,4,3], [0,0,0] ]
+ m.singular?.should be_true
- describe "Matrix#singular?" do
- it "returns true for singular matrices" do
- m = Matrix[ [1,2,3], [3,4,3], [0,0,0] ]
- m.singular?.should be_true
-
- m = Matrix[ [1,2,9], [3,4,9], [1,2,9] ]
- m.singular?.should be_true
- end
-
- it "returns false if the Matrix is regular" do
- Matrix[ [0,1], [1,0] ].singular?.should be_false
- end
+ m = Matrix[ [1,2,9], [3,4,9], [1,2,9] ]
+ m.singular?.should be_true
+ end
- it "returns false for an empty 0x0 matrix" do
- Matrix.empty(0,0).singular?.should be_false
- end
+ it "returns false if the Matrix is regular" do
+ Matrix[ [0,1], [1,0] ].singular?.should be_false
+ end
- it "raises an error for rectangular matrices" do
- -> {
- Matrix[[1], [2], [3]].singular?
- }.should raise_error(Matrix::ErrDimensionMismatch)
+ it "returns false for an empty 0x0 matrix" do
+ Matrix.empty(0,0).singular?.should be_false
+ end
- -> {
- Matrix.empty(3,0).singular?
- }.should raise_error(Matrix::ErrDimensionMismatch)
- end
+ it "raises an error for rectangular matrices" do
+ -> {
+ Matrix[[1], [2], [3]].singular?
+ }.should raise_error(Matrix::ErrDimensionMismatch)
+ -> {
+ Matrix.empty(3,0).singular?
+ }.should raise_error(Matrix::ErrDimensionMismatch)
end
+
end
diff --git a/spec/ruby/library/matrix/square_spec.rb b/spec/ruby/library/matrix/square_spec.rb
index e678f1c702..25d2d1ad9c 100644
--- a/spec/ruby/library/matrix/square_spec.rb
+++ b/spec/ruby/library/matrix/square_spec.rb
@@ -1,31 +1,28 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
+describe "Matrix#square?" do
- describe "Matrix#square?" do
-
- it "returns true when the Matrix is square" do
- Matrix[ [1,2], [2,4] ].square?.should be_true
- Matrix[ [100,3,5], [9.5, 4.9, 8], [2,0,77] ].square?.should be_true
- end
+ it "returns true when the Matrix is square" do
+ Matrix[ [1,2], [2,4] ].square?.should be_true
+ Matrix[ [100,3,5], [9.5, 4.9, 8], [2,0,77] ].square?.should be_true
+ end
- it "returns true when the Matrix has only one element" do
- Matrix[ [9] ].square?.should be_true
- end
+ it "returns true when the Matrix has only one element" do
+ Matrix[ [9] ].square?.should be_true
+ end
- it "returns false when the Matrix is rectangular" do
- Matrix[ [1, 2] ].square?.should be_false
- end
+ it "returns false when the Matrix is rectangular" do
+ Matrix[ [1, 2] ].square?.should be_false
+ end
- it "returns false when the Matrix is rectangular" do
- Matrix[ [1], [2] ].square?.should be_false
- end
+ it "returns false when the Matrix is rectangular" do
+ Matrix[ [1], [2] ].square?.should be_false
+ end
- it "returns handles empty matrices" do
- Matrix[].square?.should be_true
- Matrix[[]].square?.should be_false
- Matrix.columns([[]]).square?.should be_false
- end
+ it "returns handles empty matrices" do
+ Matrix[].square?.should be_true
+ Matrix[[]].square?.should be_false
+ Matrix.columns([[]]).square?.should be_false
end
end
diff --git a/spec/ruby/library/matrix/symmetric_spec.rb b/spec/ruby/library/matrix/symmetric_spec.rb
index 9eed28ac0d..6f2a99276a 100644
--- a/spec/ruby/library/matrix/symmetric_spec.rb
+++ b/spec/ruby/library/matrix/symmetric_spec.rb
@@ -1,32 +1,29 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix.symmetric?" do
- it "returns true for a symmetric Matrix" do
- Matrix[[1, 2, Complex(0, 3)], [2, 4, 5], [Complex(0, 3), 5, 6]].symmetric?.should be_true
- end
+describe "Matrix.symmetric?" do
+ it "returns true for a symmetric Matrix" do
+ Matrix[[1, 2, Complex(0, 3)], [2, 4, 5], [Complex(0, 3), 5, 6]].symmetric?.should be_true
+ end
- it "returns true for a 0x0 empty matrix" do
- Matrix.empty.symmetric?.should be_true
- end
+ it "returns true for a 0x0 empty matrix" do
+ Matrix.empty.symmetric?.should be_true
+ end
- it "returns false for an asymmetric Matrix" do
- Matrix[[1, 2],[-2, 1]].symmetric?.should be_false
- end
+ it "returns false for an asymmetric Matrix" do
+ Matrix[[1, 2],[-2, 1]].symmetric?.should be_false
+ end
- it "raises an error for rectangular matrices" do
- [
- Matrix[[0], [0]],
- Matrix[[0, 0]],
- Matrix.empty(0, 2),
- Matrix.empty(2, 0),
- ].each do |rectangular_matrix|
- -> {
- rectangular_matrix.symmetric?
- }.should raise_error(Matrix::ErrDimensionMismatch)
- end
+ it "raises an error for rectangular matrices" do
+ [
+ Matrix[[0], [0]],
+ Matrix[[0, 0]],
+ Matrix.empty(0, 2),
+ Matrix.empty(2, 0),
+ ].each do |rectangular_matrix|
+ -> {
+ rectangular_matrix.symmetric?
+ }.should raise_error(Matrix::ErrDimensionMismatch)
end
end
end
diff --git a/spec/ruby/library/matrix/t_spec.rb b/spec/ruby/library/matrix/t_spec.rb
index 6eb54371ee..6f1a5178e0 100644
--- a/spec/ruby/library/matrix/t_spec.rb
+++ b/spec/ruby/library/matrix/t_spec.rb
@@ -1,9 +1,6 @@
require_relative '../../spec_helper'
+require_relative 'shared/transpose'
-ruby_version_is ""..."3.1" do
- require_relative 'shared/transpose'
-
- describe "Matrix#transpose" do
- it_behaves_like :matrix_transpose, :t
- end
+describe "Matrix#transpose" do
+ it_behaves_like :matrix_transpose, :t
end
diff --git a/spec/ruby/library/matrix/to_a_spec.rb b/spec/ruby/library/matrix/to_a_spec.rb
index 0222a663ac..b5d55c5d67 100644
--- a/spec/ruby/library/matrix/to_a_spec.rb
+++ b/spec/ruby/library/matrix/to_a_spec.rb
@@ -1,14 +1,11 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix#to_a" do
- it "returns the array of arrays that describe the rows of the matrix" do
- Matrix[].to_a.should == []
- Matrix[[]].to_a.should == [[]]
- Matrix[[1]].to_a.should == [[1]]
- Matrix[[1, 2], [3, 4]].to_a.should == [[1, 2],[3, 4]]
- end
+describe "Matrix#to_a" do
+ it "returns the array of arrays that describe the rows of the matrix" do
+ Matrix[].to_a.should == []
+ Matrix[[]].to_a.should == [[]]
+ Matrix[[1]].to_a.should == [[1]]
+ Matrix[[1, 2], [3, 4]].to_a.should == [[1, 2],[3, 4]]
end
end
diff --git a/spec/ruby/library/matrix/to_s_spec.rb b/spec/ruby/library/matrix/to_s_spec.rb
index 7d38655e0d..f529fe3bcd 100644
--- a/spec/ruby/library/matrix/to_s_spec.rb
+++ b/spec/ruby/library/matrix/to_s_spec.rb
@@ -1,9 +1,6 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix#to_s" do
- it "needs to be reviewed for spec completeness"
- end
+describe "Matrix#to_s" do
+ it "needs to be reviewed for spec completeness"
end
diff --git a/spec/ruby/library/matrix/tr_spec.rb b/spec/ruby/library/matrix/tr_spec.rb
index bbf274bb5e..e17bd790d7 100644
--- a/spec/ruby/library/matrix/tr_spec.rb
+++ b/spec/ruby/library/matrix/tr_spec.rb
@@ -1,10 +1,7 @@
require_relative '../../spec_helper'
+require_relative 'shared/trace'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require_relative 'shared/trace'
- require 'matrix'
-
- describe "Matrix#tr" do
- it_behaves_like :trace, :tr
- end
+describe "Matrix#tr" do
+ it_behaves_like :trace, :tr
end
diff --git a/spec/ruby/library/matrix/trace_spec.rb b/spec/ruby/library/matrix/trace_spec.rb
index ac6ce7927d..290e7cb1f7 100644
--- a/spec/ruby/library/matrix/trace_spec.rb
+++ b/spec/ruby/library/matrix/trace_spec.rb
@@ -1,10 +1,7 @@
require_relative '../../spec_helper'
+require_relative 'shared/trace'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require_relative 'shared/trace'
- require 'matrix'
-
- describe "Matrix#trace" do
- it_behaves_like :trace, :trace
- end
+describe "Matrix#trace" do
+ it_behaves_like :trace, :trace
end
diff --git a/spec/ruby/library/matrix/transpose_spec.rb b/spec/ruby/library/matrix/transpose_spec.rb
index d7f495b946..79600dd439 100644
--- a/spec/ruby/library/matrix/transpose_spec.rb
+++ b/spec/ruby/library/matrix/transpose_spec.rb
@@ -1,9 +1,6 @@
require_relative '../../spec_helper'
+require_relative 'shared/transpose'
-ruby_version_is ""..."3.1" do
- require_relative 'shared/transpose'
-
- describe "Matrix#transpose" do
- it_behaves_like :matrix_transpose, :transpose
- end
+describe "Matrix#transpose" do
+ it_behaves_like :matrix_transpose, :transpose
end
diff --git a/spec/ruby/library/matrix/unit_spec.rb b/spec/ruby/library/matrix/unit_spec.rb
index 439e0d616b..6a41d729c7 100644
--- a/spec/ruby/library/matrix/unit_spec.rb
+++ b/spec/ruby/library/matrix/unit_spec.rb
@@ -1,9 +1,6 @@
require_relative '../../spec_helper'
+require_relative 'shared/identity'
-ruby_version_is ""..."3.1" do
- require_relative 'shared/identity'
-
- describe "Matrix.unit" do
- it_behaves_like :matrix_identity, :unit
- end
+describe "Matrix.unit" do
+ it_behaves_like :matrix_identity, :unit
end
diff --git a/spec/ruby/library/matrix/unitary_spec.rb b/spec/ruby/library/matrix/unitary_spec.rb
index b579cb244d..c214ee9b2f 100644
--- a/spec/ruby/library/matrix/unitary_spec.rb
+++ b/spec/ruby/library/matrix/unitary_spec.rb
@@ -1,36 +1,32 @@
require_relative '../../spec_helper'
-ruby_version_is ""..."3.1" do
- require 'matrix'
+require 'matrix'
- describe "Matrix.unitary?" do
- it "returns false for non unitary matrices" do
- Matrix[[0, 1], [1, 2]].should_not.unitary?
- Matrix[[0, Complex(0, 2)], [Complex(0, 2), 0]].should_not.unitary?
- Matrix[[1, 1, 0], [0, 1, 1], [1, 0, 1]].should_not.unitary?
- end
+describe "Matrix.unitary?" do
+ it "returns false for non unitary matrices" do
+ Matrix[[0, 1], [1, 2]].should_not.unitary?
+ Matrix[[0, Complex(0, 2)], [Complex(0, 2), 0]].should_not.unitary?
+ Matrix[[1, 1, 0], [0, 1, 1], [1, 0, 1]].should_not.unitary?
+ end
- it "returns true for unitary matrices" do
- Matrix[[0, Complex(0, 1)], [Complex(0, 1), 0]].should.unitary?
- end
+ it "returns true for unitary matrices" do
+ Matrix[[0, Complex(0, 1)], [Complex(0, 1), 0]].should.unitary?
+ end
- version_is((Matrix::const_defined?(:VERSION) ? Matrix::VERSION : "0.1.0"), "0.3.0") do
- it "returns true for unitary matrices with a Complex and a negative #imag" do
- Matrix[[0, Complex(0, 1)], [Complex(0, -1), 0]].should.unitary?
- end
- end
+ it "returns true for unitary matrices with a Complex and a negative #imag" do
+ Matrix[[0, Complex(0, 1)], [Complex(0, -1), 0]].should.unitary?
+ end
- it "raises an error for rectangular matrices" do
- [
- Matrix[[0], [0]],
- Matrix[[0, 0]],
- Matrix.empty(0, 2),
- Matrix.empty(2, 0),
- ].each do |rectangular_matrix|
- -> {
- rectangular_matrix.unitary?
- }.should raise_error(Matrix::ErrDimensionMismatch)
- end
+ it "raises an error for rectangular matrices" do
+ [
+ Matrix[[0], [0]],
+ Matrix[[0, 0]],
+ Matrix.empty(0, 2),
+ Matrix.empty(2, 0),
+ ].each do |rectangular_matrix|
+ -> {
+ rectangular_matrix.unitary?
+ }.should raise_error(Matrix::ErrDimensionMismatch)
end
end
end
diff --git a/spec/ruby/library/matrix/upper_triangular_spec.rb b/spec/ruby/library/matrix/upper_triangular_spec.rb
index cc2aeb7233..2514294a80 100644
--- a/spec/ruby/library/matrix/upper_triangular_spec.rb
+++ b/spec/ruby/library/matrix/upper_triangular_spec.rb
@@ -1,27 +1,24 @@
require_relative '../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Matrix.upper_triangular?" do
- it "returns true for an upper triangular Matrix" do
- Matrix[[1, 2, 3], [0, 2, 3], [0, 0, 3]].upper_triangular?.should be_true
- Matrix.diagonal([1, 2, 3]).upper_triangular?.should be_true
- Matrix[[1, 2], [0, 2], [0, 0], [0, 0]].upper_triangular?.should be_true
- Matrix[[1, 2, 3, 4], [0, 2, 3, 4]].upper_triangular?.should be_true
- end
+describe "Matrix.upper_triangular?" do
+ it "returns true for an upper triangular Matrix" do
+ Matrix[[1, 2, 3], [0, 2, 3], [0, 0, 3]].upper_triangular?.should be_true
+ Matrix.diagonal([1, 2, 3]).upper_triangular?.should be_true
+ Matrix[[1, 2], [0, 2], [0, 0], [0, 0]].upper_triangular?.should be_true
+ Matrix[[1, 2, 3, 4], [0, 2, 3, 4]].upper_triangular?.should be_true
+ end
- it "returns false for a non upper triangular square Matrix" do
- Matrix[[0, 0], [1, 0]].upper_triangular?.should be_false
- Matrix[[1, 2, 3], [1, 2, 3], [1, 2, 3]].upper_triangular?.should be_false
- Matrix[[0, 0], [0, 0], [0, 0], [0, 1]].upper_triangular?.should be_false
- Matrix[[0, 0, 0, 0], [1, 0, 0, 0]].upper_triangular?.should be_false
- end
+ it "returns false for a non upper triangular square Matrix" do
+ Matrix[[0, 0], [1, 0]].upper_triangular?.should be_false
+ Matrix[[1, 2, 3], [1, 2, 3], [1, 2, 3]].upper_triangular?.should be_false
+ Matrix[[0, 0], [0, 0], [0, 0], [0, 1]].upper_triangular?.should be_false
+ Matrix[[0, 0, 0, 0], [1, 0, 0, 0]].upper_triangular?.should be_false
+ end
- it "returns true for an empty matrix" do
- Matrix.empty(3,0).upper_triangular?.should be_true
- Matrix.empty(0,3).upper_triangular?.should be_true
- Matrix.empty(0,0).upper_triangular?.should be_true
- end
+ it "returns true for an empty matrix" do
+ Matrix.empty(3,0).upper_triangular?.should be_true
+ Matrix.empty(0,3).upper_triangular?.should be_true
+ Matrix.empty(0,0).upper_triangular?.should be_true
end
end
diff --git a/spec/ruby/library/matrix/vector/cross_product_spec.rb b/spec/ruby/library/matrix/vector/cross_product_spec.rb
index 88523824cd..c2698ade4c 100644
--- a/spec/ruby/library/matrix/vector/cross_product_spec.rb
+++ b/spec/ruby/library/matrix/vector/cross_product_spec.rb
@@ -1,17 +1,14 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Vector#cross_product" do
- it "returns the cross product of a vector" do
- Vector[1, 2, 3].cross_product(Vector[0, -4, 5]).should == Vector[22, -5, -4]
- end
+describe "Vector#cross_product" do
+ it "returns the cross product of a vector" do
+ Vector[1, 2, 3].cross_product(Vector[0, -4, 5]).should == Vector[22, -5, -4]
+ end
- it "raises an error unless both vectors have dimension 3" do
- -> {
- Vector[1, 2, 3].cross_product(Vector[0, -4])
- }.should raise_error(Vector::ErrDimensionMismatch)
- end
+ it "raises an error unless both vectors have dimension 3" do
+ -> {
+ Vector[1, 2, 3].cross_product(Vector[0, -4])
+ }.should raise_error(Vector::ErrDimensionMismatch)
end
end
diff --git a/spec/ruby/library/matrix/vector/each2_spec.rb b/spec/ruby/library/matrix/vector/each2_spec.rb
index bdd6966472..10d2fc404d 100644
--- a/spec/ruby/library/matrix/vector/each2_spec.rb
+++ b/spec/ruby/library/matrix/vector/each2_spec.rb
@@ -1,52 +1,49 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
+describe "Vector.each2" do
+ before :all do
+ @v = Vector[1, 2, 3]
+ @v2 = Vector[4, 5, 6]
+ end
+
+ it "requires one argument" do
+ -> { @v.each2(@v2, @v2){} }.should raise_error(ArgumentError)
+ -> { @v.each2(){} }.should raise_error(ArgumentError)
+ end
+
+ describe "given one argument" do
+ it "accepts an Array argument" do
+ a = []
+ @v.each2([7, 8, 9]){|x, y| a << x << y}
+ a.should == [1, 7, 2, 8, 3, 9]
+ end
+
+ it "raises a DimensionMismatch error if the Vector size is different" do
+ -> { @v.each2(Vector[1,2]){} }.should raise_error(Vector::ErrDimensionMismatch)
+ -> { @v.each2(Vector[1,2,3,4]){} }.should raise_error(Vector::ErrDimensionMismatch)
+ end
+
+ it "yields arguments in sequence" do
+ a = []
+ @v.each2(@v2){|first, second| a << [first, second]}
+ a.should == [[1, 4], [2, 5], [3, 6]]
+ end
- describe "Vector.each2" do
- before :all do
- @v = Vector[1, 2, 3]
- @v2 = Vector[4, 5, 6]
+ it "yield arguments in pairs" do
+ a = []
+ @v.each2(@v2){|*pair| a << pair}
+ a.should == [[1, 4], [2, 5], [3, 6]]
end
- it "requires one argument" do
- -> { @v.each2(@v2, @v2){} }.should raise_error(ArgumentError)
- -> { @v.each2(){} }.should raise_error(ArgumentError)
+ it "returns self when given a block" do
+ @v.each2(@v2){}.should equal(@v)
end
- describe "given one argument" do
- it "accepts an Array argument" do
- a = []
- @v.each2([7, 8, 9]){|x, y| a << x << y}
- a.should == [1, 7, 2, 8, 3, 9]
- end
-
- it "raises a DimensionMismatch error if the Vector size is different" do
- -> { @v.each2(Vector[1,2]){} }.should raise_error(Vector::ErrDimensionMismatch)
- -> { @v.each2(Vector[1,2,3,4]){} }.should raise_error(Vector::ErrDimensionMismatch)
- end
-
- it "yields arguments in sequence" do
- a = []
- @v.each2(@v2){|first, second| a << [first, second]}
- a.should == [[1, 4], [2, 5], [3, 6]]
- end
-
- it "yield arguments in pairs" do
- a = []
- @v.each2(@v2){|*pair| a << pair}
- a.should == [[1, 4], [2, 5], [3, 6]]
- end
-
- it "returns self when given a block" do
- @v.each2(@v2){}.should equal(@v)
- end
-
- it "returns an enumerator if no block given" do
- enum = @v.each2(@v2)
- enum.should be_an_instance_of(Enumerator)
- enum.to_a.should == [[1, 4], [2, 5], [3, 6]]
- end
+ it "returns an enumerator if no block given" do
+ enum = @v.each2(@v2)
+ enum.should be_an_instance_of(Enumerator)
+ enum.to_a.should == [[1, 4], [2, 5], [3, 6]]
end
end
end
diff --git a/spec/ruby/library/matrix/vector/eql_spec.rb b/spec/ruby/library/matrix/vector/eql_spec.rb
index fa086bd33a..eb2451b550 100644
--- a/spec/ruby/library/matrix/vector/eql_spec.rb
+++ b/spec/ruby/library/matrix/vector/eql_spec.rb
@@ -1,19 +1,16 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Vector#eql?" do
- before do
- @vector = Vector[1, 2, 3, 4, 5]
- end
+describe "Vector#eql?" do
+ before do
+ @vector = Vector[1, 2, 3, 4, 5]
+ end
- it "returns true for self" do
- @vector.eql?(@vector).should be_true
- end
+ it "returns true for self" do
+ @vector.eql?(@vector).should be_true
+ end
- it "returns false when there are a pair corresponding elements which are not equal in the sense of Kernel#eql?" do
- @vector.eql?(Vector[1, 2, 3, 4, 5.0]).should be_false
- end
+ it "returns false when there are a pair corresponding elements which are not equal in the sense of Kernel#eql?" do
+ @vector.eql?(Vector[1, 2, 3, 4, 5.0]).should be_false
end
end
diff --git a/spec/ruby/library/matrix/vector/inner_product_spec.rb b/spec/ruby/library/matrix/vector/inner_product_spec.rb
index 95dc4806b5..1cf8771e04 100644
--- a/spec/ruby/library/matrix/vector/inner_product_spec.rb
+++ b/spec/ruby/library/matrix/vector/inner_product_spec.rb
@@ -1,25 +1,22 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Vector#inner_product" do
- it "returns the inner product of a vector" do
- Vector[1, 2, 3].inner_product(Vector[0, -4, 5]).should == 7
- end
+describe "Vector#inner_product" do
+ it "returns the inner product of a vector" do
+ Vector[1, 2, 3].inner_product(Vector[0, -4, 5]).should == 7
+ end
- it "returns 0 for empty vectors" do
- Vector[].inner_product(Vector[]).should == 0
- end
+ it "returns 0 for empty vectors" do
+ Vector[].inner_product(Vector[]).should == 0
+ end
- it "raises an error for mismatched vectors" do
- -> {
- Vector[1, 2, 3].inner_product(Vector[0, -4])
- }.should raise_error(Vector::ErrDimensionMismatch)
- end
+ it "raises an error for mismatched vectors" do
+ -> {
+ Vector[1, 2, 3].inner_product(Vector[0, -4])
+ }.should raise_error(Vector::ErrDimensionMismatch)
+ end
- it "uses the conjugate of its argument" do
- Vector[Complex(1,2)].inner_product(Vector[Complex(3,4)]).should == Complex(11, 2)
- end
+ it "uses the conjugate of its argument" do
+ Vector[Complex(1,2)].inner_product(Vector[Complex(3,4)]).should == Complex(11, 2)
end
end
diff --git a/spec/ruby/library/matrix/vector/normalize_spec.rb b/spec/ruby/library/matrix/vector/normalize_spec.rb
index 7345e11fa4..527c9260de 100644
--- a/spec/ruby/library/matrix/vector/normalize_spec.rb
+++ b/spec/ruby/library/matrix/vector/normalize_spec.rb
@@ -1,21 +1,18 @@
require_relative '../../../spec_helper'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require 'matrix'
-
- describe "Vector#normalize" do
- it "returns a normalized copy of the vector" do
- x = 0.2672612419124244
- Vector[1, 2, 3].normalize.should == Vector[x, x * 2, x * 3]
- end
+describe "Vector#normalize" do
+ it "returns a normalized copy of the vector" do
+ x = 0.2672612419124244
+ Vector[1, 2, 3].normalize.should == Vector[x, x * 2, x * 3]
+ end
- it "raises an error for zero vectors" do
- -> {
- Vector[].normalize
- }.should raise_error(Vector::ZeroVectorError)
- -> {
- Vector[0, 0, 0].normalize
- }.should raise_error(Vector::ZeroVectorError)
- end
+ it "raises an error for zero vectors" do
+ -> {
+ Vector[].normalize
+ }.should raise_error(Vector::ZeroVectorError)
+ -> {
+ Vector[0, 0, 0].normalize
+ }.should raise_error(Vector::ZeroVectorError)
end
end
diff --git a/spec/ruby/library/matrix/zero_spec.rb b/spec/ruby/library/matrix/zero_spec.rb
index 80162a03d0..68e8567c26 100644
--- a/spec/ruby/library/matrix/zero_spec.rb
+++ b/spec/ruby/library/matrix/zero_spec.rb
@@ -1,55 +1,52 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
-ruby_version_is ""..."3.1" do
- require_relative 'fixtures/classes'
- require 'matrix'
-
- describe "Matrix.zero" do
- it "returns an object of type Matrix" do
- Matrix.zero(3).should be_kind_of(Matrix)
- end
+describe "Matrix.zero" do
+ it "returns an object of type Matrix" do
+ Matrix.zero(3).should be_kind_of(Matrix)
+ end
- it "creates a n x n matrix" do
- m3 = Matrix.zero(3)
- m3.row_size.should == 3
- m3.column_size.should == 3
+ it "creates a n x n matrix" do
+ m3 = Matrix.zero(3)
+ m3.row_size.should == 3
+ m3.column_size.should == 3
- m8 = Matrix.zero(8)
- m8.row_size.should == 8
- m8.column_size.should == 8
- end
+ m8 = Matrix.zero(8)
+ m8.row_size.should == 8
+ m8.column_size.should == 8
+ end
- it "initializes all cells to 0" do
- size = 10
- m = Matrix.zero(size)
+ it "initializes all cells to 0" do
+ size = 10
+ m = Matrix.zero(size)
- (0...size).each do |i|
- (0...size).each do |j|
- m[i, j].should == 0
- end
+ (0...size).each do |i|
+ (0...size).each do |j|
+ m[i, j].should == 0
end
end
+ end
- describe "for a subclass of Matrix" do
- it "returns an instance of that subclass" do
- MatrixSub.zero(3).should be_an_instance_of(MatrixSub)
- end
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.zero(3).should be_an_instance_of(MatrixSub)
end
end
+end
- describe "Matrix.zero?" do
- it "returns true for empty matrices" do
- Matrix.empty.should.zero?
- Matrix.empty(3,0).should.zero?
- Matrix.empty(0,3).should.zero?
- end
+describe "Matrix.zero?" do
+ it "returns true for empty matrices" do
+ Matrix.empty.should.zero?
+ Matrix.empty(3,0).should.zero?
+ Matrix.empty(0,3).should.zero?
+ end
- it "returns true for matrices with zero entries" do
- Matrix.zero(2,3).should.zero?
- end
+ it "returns true for matrices with zero entries" do
+ Matrix.zero(2,3).should.zero?
+ end
- it "returns false for matrices with non zero entries" do
- Matrix[[1]].should_not.zero?
- end
+ it "returns false for matrices with non zero entries" do
+ Matrix[[1]].should_not.zero?
end
end
diff --git a/spec/ruby/library/net-ftp/FTPError_spec.rb b/spec/ruby/library/net-ftp/FTPError_spec.rb
new file mode 100644
index 0000000000..0c31b65dcc
--- /dev/null
+++ b/spec/ruby/library/net-ftp/FTPError_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require 'net/ftp'
+
+describe "Net::FTPError" do
+ it "is an Exception" do
+ Net::FTPError.should < Exception
+ end
+end
diff --git a/spec/ruby/library/net-ftp/FTPPermError_spec.rb b/spec/ruby/library/net-ftp/FTPPermError_spec.rb
new file mode 100644
index 0000000000..b43e12c503
--- /dev/null
+++ b/spec/ruby/library/net-ftp/FTPPermError_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require 'net/ftp'
+
+describe "Net::FTPPermError" do
+ it "is an Exception" do
+ Net::FTPPermError.should < Exception
+ end
+
+ it "is a subclass of Net::FTPError" do
+ Net::FTPPermError.should < Net::FTPError
+ end
+end
diff --git a/spec/ruby/library/net-ftp/FTPProtoError_spec.rb b/spec/ruby/library/net-ftp/FTPProtoError_spec.rb
new file mode 100644
index 0000000000..e7abbc0dd8
--- /dev/null
+++ b/spec/ruby/library/net-ftp/FTPProtoError_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require 'net/ftp'
+
+describe "Net::FTPProtoError" do
+ it "is an Exception" do
+ Net::FTPProtoError.should < Exception
+ end
+
+ it "is a subclass of Net::FTPError" do
+ Net::FTPPermError.should < Net::FTPError
+ end
+end
diff --git a/spec/ruby/library/net-ftp/FTPReplyError_spec.rb b/spec/ruby/library/net-ftp/FTPReplyError_spec.rb
new file mode 100644
index 0000000000..fcc7501fc1
--- /dev/null
+++ b/spec/ruby/library/net-ftp/FTPReplyError_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require 'net/ftp'
+
+describe "Net::FTPReplyError" do
+ it "is an Exception" do
+ Net::FTPReplyError.should < Exception
+ end
+
+ it "is a subclass of Net::FTPError" do
+ Net::FTPPermError.should < Net::FTPError
+ end
+end
diff --git a/spec/ruby/library/net-ftp/FTPTempError_spec.rb b/spec/ruby/library/net-ftp/FTPTempError_spec.rb
new file mode 100644
index 0000000000..f4b045dfb5
--- /dev/null
+++ b/spec/ruby/library/net-ftp/FTPTempError_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require 'net/ftp'
+
+describe "Net::FTPTempError" do
+ it "is an Exception" do
+ Net::FTPTempError.should < Exception
+ end
+
+ it "is a subclass of Net::FTPError" do
+ Net::FTPPermError.should < Net::FTPError
+ end
+end
diff --git a/spec/ruby/library/net-ftp/abort_spec.rb b/spec/ruby/library/net-ftp/abort_spec.rb
new file mode 100644
index 0000000000..335d056512
--- /dev/null
+++ b/spec/ruby/library/net-ftp/abort_spec.rb
@@ -0,0 +1,62 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#abort" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the ABOR command to the server" do
+ -> { @ftp.abort }.should_not raise_error
+ end
+
+ it "ignores the response" do
+ @ftp.abort
+ @ftp.last_response.should == "220 Dummy FTP Server ready!\n"
+ end
+
+ it "returns the full response" do
+ @ftp.abort.should == "226 Closing data connection. (ABOR)\n"
+ end
+
+ it "does not raise any error when the response code is 225" do
+ @server.should_receive(:abor).and_respond("225 Data connection open; no transfer in progress.")
+ -> { @ftp.abort }.should_not raise_error
+ end
+
+ it "does not raise any error when the response code is 226" do
+ @server.should_receive(:abor).and_respond("226 Closing data connection.")
+ -> { @ftp.abort }.should_not raise_error
+ end
+
+ it "raises a Net::FTPProtoError when the response code is 500" do
+ @server.should_receive(:abor).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.abort }.should raise_error(Net::FTPProtoError)
+ end
+
+ it "raises a Net::FTPProtoError when the response code is 501" do
+ @server.should_receive(:abor).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.abort }.should raise_error(Net::FTPProtoError)
+ end
+
+ it "raises a Net::FTPProtoError when the response code is 502" do
+ @server.should_receive(:abor).and_respond("502 Command not implemented.")
+ -> { @ftp.abort }.should raise_error(Net::FTPProtoError)
+ end
+
+ it "raises a Net::FTPProtoError when the response code is 421" do
+ @server.should_receive(:abor).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.abort }.should raise_error(Net::FTPProtoError)
+ end
+end
diff --git a/spec/ruby/library/net-ftp/acct_spec.rb b/spec/ruby/library/net-ftp/acct_spec.rb
new file mode 100644
index 0000000000..ab093448a2
--- /dev/null
+++ b/spec/ruby/library/net-ftp/acct_spec.rb
@@ -0,0 +1,58 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#acct" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "writes the ACCT command to the server" do
+ @ftp.acct("my_account")
+ @ftp.last_response.should == "230 User 'my_account' logged in, proceed. (ACCT)\n"
+ end
+
+ it "returns nil" do
+ @ftp.acct("my_account").should == nil
+ end
+
+ it "does not raise any error when the response code is 230" do
+ @server.should_receive(:acct).and_respond("230 User logged in, proceed.")
+ -> { @ftp.acct("my_account") }.should_not raise_error
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:acct).and_respond("530 Not logged in.")
+ -> { @ftp.acct("my_account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:acct).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.acct("my_account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:acct).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.acct("my_account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 503" do
+ @server.should_receive(:acct).and_respond("503 Bad sequence of commands.")
+ -> { @ftp.acct("my_account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:acct).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.acct("my_account") }.should raise_error(Net::FTPTempError)
+ end
+end
diff --git a/spec/ruby/library/net-ftp/binary_spec.rb b/spec/ruby/library/net-ftp/binary_spec.rb
new file mode 100644
index 0000000000..1e0585b795
--- /dev/null
+++ b/spec/ruby/library/net-ftp/binary_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+
+describe "Net::FTP#binary" do
+ it "returns true when self is in binary mode" do
+ ftp = Net::FTP.new
+ ftp.binary.should be_true
+
+ ftp.binary = false
+ ftp.binary.should be_false
+ end
+end
+
+describe "Net::FTP#binary=" do
+ it "sets self to binary mode when passed true" do
+ ftp = Net::FTP.new
+
+ ftp.binary = true
+ ftp.binary.should be_true
+
+ ftp.binary = false
+ ftp.binary.should be_false
+ end
+end
diff --git a/spec/ruby/library/net-ftp/chdir_spec.rb b/spec/ruby/library/net-ftp/chdir_spec.rb
new file mode 100644
index 0000000000..cc129b5e42
--- /dev/null
+++ b/spec/ruby/library/net-ftp/chdir_spec.rb
@@ -0,0 +1,99 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#chdir" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ describe "when switching to the parent directory" do
+ it "sends the 'CDUP' command to the server" do
+ @ftp.chdir("..")
+ @ftp.last_response.should == "200 Command okay. (CDUP)\n"
+ end
+
+ it "returns nil" do
+ @ftp.chdir("..").should be_nil
+ end
+
+ it "does not raise a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:cdup).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.chdir("..") }.should_not raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:cdup).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.chdir("..") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:cdup).and_respond("502 Command not implemented.")
+ -> { @ftp.chdir("..") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:cdup).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.chdir("..") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:cdup).and_respond("530 Not logged in.")
+ -> { @ftp.chdir("..") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 550" do
+ @server.should_receive(:cdup).and_respond("550 Requested action not taken.")
+ -> { @ftp.chdir("..") }.should raise_error(Net::FTPPermError)
+ end
+ end
+
+ it "writes the 'CWD' command with the passed directory to the socket" do
+ @ftp.chdir("test")
+ @ftp.last_response.should == "200 Command okay. (CWD test)\n"
+ end
+
+ it "returns nil" do
+ @ftp.chdir("test").should be_nil
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:cwd).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.chdir("test") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:cwd).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.chdir("test") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:cwd).and_respond("502 Command not implemented.")
+ -> { @ftp.chdir("test") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:cwd).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.chdir("test") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:cwd).and_respond("530 Not logged in.")
+ -> { @ftp.chdir("test") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 550" do
+ @server.should_receive(:cwd).and_respond("550 Requested action not taken.")
+ -> { @ftp.chdir("test") }.should raise_error(Net::FTPPermError)
+ end
+end
diff --git a/spec/ruby/library/net-ftp/close_spec.rb b/spec/ruby/library/net-ftp/close_spec.rb
new file mode 100644
index 0000000000..183f14a84b
--- /dev/null
+++ b/spec/ruby/library/net-ftp/close_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+
+describe "Net::FTP#close" do
+ before :each do
+ @socket = mock("Socket")
+ @socket.stub!(:closed?).and_return(false)
+ @socket.stub!(:read_timeout).and_return(60)
+ @socket.stub!(:read_timeout=).and_return(3)
+
+ @ftp = Net::FTP.new
+ @ftp.instance_variable_set(:@sock, @socket)
+ end
+
+ it "closes the socket" do
+ @socket.should_receive(:close)
+ @ftp.close.should be_nil
+ end
+
+ it "does not try to close the socket if it has already been closed" do
+ @socket.should_receive(:closed?).and_return(true)
+ @socket.should_not_receive(:close)
+ @ftp.close.should be_nil
+ end
+
+ it "does not try to close the socket if it is nil" do
+ @ftp.instance_variable_set(:@sock, nil)
+ @ftp.close.should be_nil
+ end
+end
diff --git a/spec/ruby/library/net-ftp/closed_spec.rb b/spec/ruby/library/net-ftp/closed_spec.rb
new file mode 100644
index 0000000000..84001cdc0f
--- /dev/null
+++ b/spec/ruby/library/net-ftp/closed_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+
+describe "Net::FTP#closed?" do
+ before :each do
+ @socket = mock("Socket")
+
+ @ftp = Net::FTP.new
+ @ftp.instance_variable_set(:@sock, @socket)
+ end
+
+ it "returns true when the socket is closed" do
+ @socket.should_receive(:closed?).and_return(true)
+ @ftp.closed?.should be_true
+ end
+
+ it "returns true when the socket is nil" do
+ @ftp.instance_variable_set(:@sock, nil)
+ @ftp.closed?.should be_true
+ end
+end
diff --git a/spec/ruby/library/net-ftp/connect_spec.rb b/spec/ruby/library/net-ftp/connect_spec.rb
new file mode 100644
index 0000000000..4330d430b4
--- /dev/null
+++ b/spec/ruby/library/net-ftp/connect_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+# TODO: Add specs for using the SOCKSSocket
+describe "Net::FTP#connect" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ end
+
+ after :each do
+ @server.connect_message = nil
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "tries to connect to the FTP Server on the given host and port" do
+ -> { @ftp.connect(@server.hostname, @server.server_port) }.should_not raise_error
+ end
+
+ it "returns nil" do
+ @ftp.connect(@server.hostname, @server.server_port).should be_nil
+ end
+
+ ruby_version_is ""..."3.1" do
+ it "prints a small debug line when in debug mode" do
+ @ftp.debug_mode = true
+ -> { @ftp.connect(@server.hostname, @server.server_port) }.should output(/connect: #{@server.hostname}, #{@server.server_port}\nget: 220 Dummy FTP Server ready!/)
+ @ftp.debug_mode = false
+ end
+ end
+
+ it "does not raise any error when the response code is 220" do
+ @server.connect_message = "220 Dummy FTP Server ready!"
+ -> { @ftp.connect(@server.hostname, @server.server_port) }.should_not raise_error
+ end
+
+ it "raises a Net::FTPReplyError when the response code is 120" do
+ @server.connect_message = "120 Service ready in nnn minutes."
+ -> { @ftp.connect(@server.hostname, @server.server_port) }.should raise_error(Net::FTPReplyError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.connect_message = "421 Service not available, closing control connection."
+ -> { @ftp.connect(@server.hostname, @server.server_port) }.should raise_error(Net::FTPTempError)
+ end
+end
diff --git a/spec/ruby/library/net-ftp/debug_mode_spec.rb b/spec/ruby/library/net-ftp/debug_mode_spec.rb
new file mode 100644
index 0000000000..f2ef53c089
--- /dev/null
+++ b/spec/ruby/library/net-ftp/debug_mode_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+
+describe "Net::FTP#debug_mode" do
+ it "returns true when self is in debug mode" do
+ ftp = Net::FTP.new
+ ftp.debug_mode.should be_false
+
+ ftp.debug_mode = true
+ ftp.debug_mode.should be_true
+ end
+end
+
+describe "Net::FTP#debug_mode=" do
+ it "sets self into debug mode when passed true" do
+ ftp = Net::FTP.new
+ ftp.debug_mode = true
+ ftp.debug_mode.should be_true
+
+ ftp.debug_mode = false
+ ftp.debug_mode.should be_false
+ end
+end
diff --git a/spec/ruby/library/net-ftp/default_passive_spec.rb b/spec/ruby/library/net-ftp/default_passive_spec.rb
new file mode 100644
index 0000000000..3f14f6187e
--- /dev/null
+++ b/spec/ruby/library/net-ftp/default_passive_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+
+describe "Net::FTP#default_passive" do
+ it "is true by default" do
+ ruby_exe(fixture(__FILE__, "default_passive.rb")).should == "true\ntrue\n"
+ end
+end
diff --git a/spec/ruby/library/net-ftp/delete_spec.rb b/spec/ruby/library/net-ftp/delete_spec.rb
new file mode 100644
index 0000000000..bfb7da1ffe
--- /dev/null
+++ b/spec/ruby/library/net-ftp/delete_spec.rb
@@ -0,0 +1,59 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#delete" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the DELE command with the passed filename to the server" do
+ @ftp.delete("test.file")
+ @ftp.last_response.should == "250 Requested file action okay, completed. (DELE test.file)\n"
+ end
+
+ it "raises a Net::FTPTempError when the response code is 450" do
+ @server.should_receive(:dele).and_respond("450 Requested file action not taken.")
+ -> { @ftp.delete("test.file") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 550" do
+ @server.should_receive(:dele).and_respond("550 Requested action not taken.")
+ -> { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:dele).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:dele).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:dele).and_respond("502 Command not implemented.")
+ -> { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:dele).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.delete("test.file") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:dele).and_respond("530 Not logged in.")
+ -> { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError)
+ end
+end
diff --git a/spec/ruby/library/net-ftp/dir_spec.rb b/spec/ruby/library/net-ftp/dir_spec.rb
new file mode 100644
index 0000000000..894f03dd7b
--- /dev/null
+++ b/spec/ruby/library/net-ftp/dir_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+require_relative 'shared/list'
+
+describe "Net::FTP#dir" do
+ it_behaves_like :net_ftp_list, :dir
+end
diff --git a/spec/ruby/library/net/ftp/fixtures/default_passive.rb b/spec/ruby/library/net-ftp/fixtures/default_passive.rb
index b6995d6f34..b6995d6f34 100644
--- a/spec/ruby/library/net/ftp/fixtures/default_passive.rb
+++ b/spec/ruby/library/net-ftp/fixtures/default_passive.rb
diff --git a/spec/ruby/library/net/ftp/fixtures/passive.rb b/spec/ruby/library/net-ftp/fixtures/passive.rb
index 6b5cde82df..6b5cde82df 100644
--- a/spec/ruby/library/net/ftp/fixtures/passive.rb
+++ b/spec/ruby/library/net-ftp/fixtures/passive.rb
diff --git a/spec/ruby/library/net/ftp/fixtures/putbinaryfile b/spec/ruby/library/net-ftp/fixtures/putbinaryfile
index f3130c6e43..f3130c6e43 100644
--- a/spec/ruby/library/net/ftp/fixtures/putbinaryfile
+++ b/spec/ruby/library/net-ftp/fixtures/putbinaryfile
diff --git a/spec/ruby/library/net/ftp/fixtures/puttextfile b/spec/ruby/library/net-ftp/fixtures/puttextfile
index b4f3b2b62d..b4f3b2b62d 100644
--- a/spec/ruby/library/net/ftp/fixtures/puttextfile
+++ b/spec/ruby/library/net-ftp/fixtures/puttextfile
diff --git a/spec/ruby/library/net/ftp/fixtures/server.rb b/spec/ruby/library/net-ftp/fixtures/server.rb
index ecbed591d5..ecbed591d5 100644
--- a/spec/ruby/library/net/ftp/fixtures/server.rb
+++ b/spec/ruby/library/net-ftp/fixtures/server.rb
diff --git a/spec/ruby/library/net-ftp/get_spec.rb b/spec/ruby/library/net-ftp/get_spec.rb
new file mode 100644
index 0000000000..1bc1bd744b
--- /dev/null
+++ b/spec/ruby/library/net-ftp/get_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+require_relative 'shared/gettextfile'
+require_relative 'shared/getbinaryfile'
+
+describe "Net::FTP#get (binary mode)" do
+ before :each do
+ @binary_mode = true
+ end
+
+ it_behaves_like :net_ftp_getbinaryfile, :get
+end
+
+describe "Net::FTP#get (text mode)" do
+ before :each do
+ @binary_mode = false
+ end
+
+ it_behaves_like :net_ftp_gettextfile, :get
+end
diff --git a/spec/ruby/library/net-ftp/getbinaryfile_spec.rb b/spec/ruby/library/net-ftp/getbinaryfile_spec.rb
new file mode 100644
index 0000000000..e9898fccc7
--- /dev/null
+++ b/spec/ruby/library/net-ftp/getbinaryfile_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+require_relative 'shared/getbinaryfile'
+
+describe "Net::FTP#getbinaryfile" do
+ it_behaves_like :net_ftp_getbinaryfile, :getbinaryfile
+end
diff --git a/spec/ruby/library/net-ftp/getdir_spec.rb b/spec/ruby/library/net-ftp/getdir_spec.rb
new file mode 100644
index 0000000000..756d6a23af
--- /dev/null
+++ b/spec/ruby/library/net-ftp/getdir_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'shared/pwd'
+
+describe "Net::FTP#getdir" do
+ it_behaves_like :net_ftp_pwd, :getdir
+end
diff --git a/spec/ruby/library/net-ftp/gettextfile_spec.rb b/spec/ruby/library/net-ftp/gettextfile_spec.rb
new file mode 100644
index 0000000000..cdd1b4c797
--- /dev/null
+++ b/spec/ruby/library/net-ftp/gettextfile_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+require_relative 'shared/gettextfile'
+
+describe "Net::FTP#gettextfile" do
+ it_behaves_like :net_ftp_gettextfile, :gettextfile
+end
diff --git a/spec/ruby/library/net-ftp/help_spec.rb b/spec/ruby/library/net-ftp/help_spec.rb
new file mode 100644
index 0000000000..c562be50b2
--- /dev/null
+++ b/spec/ruby/library/net-ftp/help_spec.rb
@@ -0,0 +1,66 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#help" do
+ def with_connection
+ yield
+ end
+
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "writes the HELP command to the server" do
+ @ftp.help
+ @ftp.last_response.should == "211 System status, or system help reply. (HELP)\n"
+ end
+
+ it "returns the server's response" do
+ @ftp.help.should == "211 System status, or system help reply. (HELP)\n"
+ end
+
+ it "writes the HELP command with an optional parameter to the socket" do
+ @ftp.help("some parameter").should == "211 System status, or system help reply. (HELP some parameter)\n"
+ end
+
+ it "does not raise any error when the response code is 211" do
+ @server.should_receive(:help).and_respond("211 System status, or system help reply.")
+ -> { @ftp.help }.should_not raise_error
+ end
+
+ it "does not raise any error when the response code is 214" do
+ @server.should_receive(:help).and_respond("214 Help message.")
+ -> { @ftp.help }.should_not raise_error
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:help).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.help }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:help).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.help }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:help).and_respond("502 Command not implemented.")
+ -> { @ftp.help }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:help).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.help }.should raise_error(Net::FTPTempError)
+ end
+end
diff --git a/spec/ruby/library/net-ftp/initialize_spec.rb b/spec/ruby/library/net-ftp/initialize_spec.rb
new file mode 100644
index 0000000000..4d775e8dc1
--- /dev/null
+++ b/spec/ruby/library/net-ftp/initialize_spec.rb
@@ -0,0 +1,405 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+
+describe "Net::FTP#initialize" do
+ before :each do
+ @ftp = Net::FTP.allocate
+ @ftp.stub!(:connect)
+ @port_args = []
+ @port_args << 21
+ end
+
+ it "is private" do
+ Net::FTP.should have_private_instance_method(:initialize)
+ end
+
+ it "sets self into binary mode" do
+ @ftp.binary.should be_nil
+ @ftp.send(:initialize)
+ @ftp.binary.should be_true
+ end
+
+ it "sets self into active mode" do
+ @ftp.passive.should be_nil
+ @ftp.send(:initialize)
+ @ftp.passive.should be_false
+ end
+
+ it "sets self into non-debug mode" do
+ @ftp.debug_mode.should be_nil
+ @ftp.send(:initialize)
+ @ftp.debug_mode.should be_false
+ end
+
+ it "sets self to not resume file uploads/downloads" do
+ @ftp.resume.should be_nil
+ @ftp.send(:initialize)
+ @ftp.resume.should be_false
+ end
+
+ describe "when passed no arguments" do
+ it "does not try to connect" do
+ @ftp.should_not_receive(:connect)
+ @ftp.send(:initialize)
+ end
+ end
+
+ describe "when passed host" do
+ it "tries to connect to the passed host" do
+ @ftp.should_receive(:connect).with("localhost", *@port_args)
+ @ftp.send(:initialize, "localhost")
+ end
+ end
+
+ describe "when passed host, user" do
+ it "tries to connect to the passed host" do
+ @ftp.should_receive(:connect).with("localhost", *@port_args)
+ @ftp.send(:initialize, "localhost")
+ end
+
+ it "tries to login with the passed username" do
+ @ftp.should_receive(:login).with("rubyspec", nil, nil)
+ @ftp.send(:initialize, "localhost", "rubyspec")
+ end
+ end
+
+ describe "when passed host, user, password" do
+ it "tries to connect to the passed host" do
+ @ftp.should_receive(:connect).with("localhost", *@port_args)
+ @ftp.send(:initialize, "localhost")
+ end
+
+ it "tries to login with the passed username and password" do
+ @ftp.should_receive(:login).with("rubyspec", "rocks", nil)
+ @ftp.send(:initialize, "localhost", "rubyspec", "rocks")
+ end
+ end
+
+ describe "when passed host, user" do
+ it "tries to connect to the passed host" do
+ @ftp.should_receive(:connect).with("localhost", *@port_args)
+ @ftp.send(:initialize, "localhost")
+ end
+
+ it "tries to login with the passed username, password and account" do
+ @ftp.should_receive(:login).with("rubyspec", "rocks", "account")
+ @ftp.send(:initialize, "localhost", "rubyspec", "rocks", "account")
+ end
+ end
+
+ before :each do
+ @ftp.stub!(:login)
+ end
+
+ describe 'when the host' do
+ describe 'is set' do
+ describe 'and port option' do
+ describe 'is set' do
+ it 'tries to connect to the host on the specified port' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ port: 8080 })
+ @ftp.should_receive(:connect).with('localhost', 8080)
+
+ @ftp.send(:initialize, 'localhost', options)
+ end
+ end
+
+ describe 'is not set' do
+ it 'tries to connect to the host without a port' do
+ @ftp.should_receive(:connect).with("localhost", *@port_args)
+
+ @ftp.send(:initialize, 'localhost')
+ end
+ end
+ end
+
+ describe 'when the username option' do
+ describe 'is set' do
+ describe 'and the password option' do
+ describe 'is set' do
+ describe 'and the account option' do
+ describe 'is set' do
+ it 'tries to log in with the supplied parameters' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ username: 'a', password: 'topsecret', account: 'b' })
+ @ftp.should_receive(:login).with('a', 'topsecret', 'b')
+
+ @ftp.send(:initialize, 'localhost', options)
+ end
+ end
+
+ describe 'is unset' do
+ it 'tries to log in with the supplied parameters' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ username: 'a', password: 'topsecret' })
+ @ftp.should_receive(:login).with('a', 'topsecret', nil)
+
+ @ftp.send(:initialize, 'localhost', options)
+ end
+ end
+ end
+ end
+
+ describe 'is unset' do
+ describe 'and the account option' do
+ describe 'is set' do
+ it 'tries to log in with the supplied parameters' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ username: 'a', account: 'b' })
+ @ftp.should_receive(:login).with('a', nil, 'b')
+
+ @ftp.send(:initialize, 'localhost', options)
+ end
+ end
+
+ describe 'is unset' do
+ it 'tries to log in with the supplied parameters' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ username: 'a'})
+ @ftp.should_receive(:login).with('a', nil, nil)
+
+ @ftp.send(:initialize, 'localhost', options)
+ end
+ end
+ end
+ end
+ end
+ end
+
+ describe 'is not set' do
+ it 'does not try to log in' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({})
+ @ftp.should_not_receive(:login)
+
+ @ftp.send(:initialize, 'localhost', options)
+ end
+ end
+ end
+ end
+
+ describe 'is unset' do
+ it 'does not try to connect' do
+ @ftp.should_not_receive(:connect)
+
+ @ftp.send(:initialize)
+ end
+
+ it 'does not try to log in' do
+ @ftp.should_not_receive(:login)
+
+ @ftp.send(:initialize)
+ end
+ end
+ end
+
+ describe 'when the passive option' do
+ describe 'is set' do
+ describe 'to true' do
+ it 'sets passive to true' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ passive: true })
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.passive.should == true
+ end
+ end
+
+ describe 'to false' do
+ it 'sets passive to false' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ passive: false })
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.passive.should == false
+ end
+ end
+ end
+
+ describe 'is unset' do
+ it 'sets passive to false' do
+ @ftp.send(:initialize)
+ @ftp.passive.should == false
+ end
+ end
+ end
+
+ describe 'when the debug_mode option' do
+ describe 'is set' do
+ describe 'to true' do
+ it 'sets debug_mode to true' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ debug_mode: true })
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.debug_mode.should == true
+ end
+ end
+
+ describe 'to false' do
+ it 'sets debug_mode to false' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ debug_mode: false })
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.debug_mode.should == false
+ end
+ end
+ end
+
+ describe 'is unset' do
+ it 'sets debug_mode to false' do
+ @ftp.send(:initialize)
+ @ftp.debug_mode.should == false
+ end
+ end
+ end
+
+ describe 'when the open_timeout option' do
+ describe 'is set' do
+ it 'sets open_timeout to the specified value' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ open_timeout: 42 })
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.open_timeout.should == 42
+ end
+ end
+
+ describe 'is not set' do
+ it 'sets open_timeout to nil' do
+ @ftp.send(:initialize)
+ @ftp.open_timeout.should == nil
+ end
+ end
+ end
+
+ describe 'when the read_timeout option' do
+ describe 'is set' do
+ it 'sets read_timeout to the specified value' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ read_timeout: 100 })
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.read_timeout.should == 100
+ end
+ end
+
+ describe 'is not set' do
+ it 'sets read_timeout to the default value' do
+ @ftp.send(:initialize)
+ @ftp.read_timeout.should == 60
+ end
+ end
+ end
+
+ describe 'when the ssl_handshake_timeout option' do
+ describe 'is set' do
+ it 'sets ssl_handshake_timeout to the specified value' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ ssl_handshake_timeout: 23 })
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.ssl_handshake_timeout.should == 23
+ end
+ end
+
+ describe 'is not set' do
+ it 'sets ssl_handshake_timeout to nil' do
+ @ftp.send(:initialize)
+ @ftp.ssl_handshake_timeout.should == nil
+ end
+ end
+ end
+
+ describe 'when the ssl option' do
+ describe 'is set' do
+ describe "and the ssl option's value is true" do
+ it 'initializes ssl_context to a blank SSLContext object' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ ssl: true })
+
+ ssl_context = OpenSSL::SSL::SSLContext.allocate
+ ssl_context.stub!(:set_params)
+
+ OpenSSL::SSL::SSLContext.should_receive(:new).and_return(ssl_context)
+ ssl_context.should_receive(:set_params).with({})
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.instance_variable_get(:@ssl_context).should == ssl_context
+ end
+ end
+
+ describe "and the ssl option's value is a hash" do
+ it 'initializes ssl_context to a configured SSLContext object' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ ssl: {key: 'value'} })
+
+ ssl_context = OpenSSL::SSL::SSLContext.allocate
+ ssl_context.stub!(:set_params)
+
+ OpenSSL::SSL::SSLContext.should_receive(:new).and_return(ssl_context)
+ ssl_context.should_receive(:set_params).with({key: 'value'})
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.instance_variable_get(:@ssl_context).should == ssl_context
+ end
+ end
+
+ describe 'and private_data_connection' do
+ describe 'is set' do
+ it 'sets private_data_connection to that value' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ ssl: true, private_data_connection: 'true' })
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.instance_variable_get(:@private_data_connection).should == 'true'
+ end
+ end
+
+ describe 'is not set' do
+ it 'sets private_data_connection to nil' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ ssl: true })
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.instance_variable_get(:@private_data_connection).should == true
+ end
+ end
+ end
+ end
+
+ describe 'is not set' do
+ it 'sets ssl_context to nil' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({})
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.instance_variable_get(:@ssl_context).should == nil
+ end
+
+ describe 'private_data_connection' do
+ describe 'is set' do
+ it 'raises an ArgumentError' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ private_data_connection: true })
+
+ -> {
+ @ftp.send(:initialize, nil, options)
+ }.should raise_error(ArgumentError, /private_data_connection can be set to true only when ssl is enabled/)
+ end
+ end
+
+ describe 'is not set' do
+ it 'sets private_data_connection to false' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({})
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.instance_variable_get(:@private_data_connection).should == false
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/net-ftp/last_response_code_spec.rb b/spec/ruby/library/net-ftp/last_response_code_spec.rb
new file mode 100644
index 0000000000..c17c28f0f8
--- /dev/null
+++ b/spec/ruby/library/net-ftp/last_response_code_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'shared/last_response_code'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#last_response_code" do
+ it_behaves_like :net_ftp_last_response_code, :last_response_code
+end
diff --git a/spec/ruby/library/net-ftp/last_response_spec.rb b/spec/ruby/library/net-ftp/last_response_spec.rb
new file mode 100644
index 0000000000..c9d9d70f35
--- /dev/null
+++ b/spec/ruby/library/net-ftp/last_response_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#last_response" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "returns the last response" do
+ @ftp.last_response.should == "220 Dummy FTP Server ready!\n"
+ @ftp.help
+ @ftp.last_response.should == "211 System status, or system help reply. (HELP)\n"
+ end
+end
diff --git a/spec/ruby/library/net-ftp/lastresp_spec.rb b/spec/ruby/library/net-ftp/lastresp_spec.rb
new file mode 100644
index 0000000000..e0c1b862a0
--- /dev/null
+++ b/spec/ruby/library/net-ftp/lastresp_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'shared/last_response_code'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#lastresp" do
+ it_behaves_like :net_ftp_last_response_code, :lastresp
+end
diff --git a/spec/ruby/library/net-ftp/list_spec.rb b/spec/ruby/library/net-ftp/list_spec.rb
new file mode 100644
index 0000000000..6cb1bbc4b8
--- /dev/null
+++ b/spec/ruby/library/net-ftp/list_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+require_relative 'shared/list'
+
+describe "Net::FTP#list" do
+ it_behaves_like :net_ftp_list, :list
+end
diff --git a/spec/ruby/library/net-ftp/login_spec.rb b/spec/ruby/library/net-ftp/login_spec.rb
new file mode 100644
index 0000000000..0de2f5cc63
--- /dev/null
+++ b/spec/ruby/library/net-ftp/login_spec.rb
@@ -0,0 +1,195 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#login" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ describe "when passed no arguments" do
+ it "sends the USER command with 'anonymous' as name to the server" do
+ @ftp.login
+ @server.login_user.should == "anonymous"
+ end
+
+ it "sends 'anonymous@' as a password when required" do
+ @server.should_receive(:user).and_respond("331 User name okay, need password.")
+ @ftp.login
+ @server.login_pass.should == "anonymous@"
+ end
+
+ it "raises a Net::FTPReplyError when the server requests an account" do
+ @server.should_receive(:user).and_respond("331 User name okay, need password.")
+ @server.should_receive(:pass).and_respond("332 Need account for login.")
+ -> { @ftp.login }.should raise_error(Net::FTPReplyError)
+ end
+ end
+
+ describe "when passed name" do
+ it "sends the USER command with the passed name to the server" do
+ @ftp.login("rubyspec")
+ @server.login_user.should == "rubyspec"
+ end
+
+ it "raises a Net::FTPReplyError when the server requests a password, but none was given" do
+ @server.should_receive(:user).and_respond("331 User name okay, need password.")
+ -> { @ftp.login("rubyspec") }.should raise_error(Net::FTPReplyError)
+ end
+
+ it "raises a Net::FTPReplyError when the server requests an account, but none was given" do
+ @server.should_receive(:user).and_respond("331 User name okay, need password.")
+ @server.should_receive(:pass).and_respond("332 Need account for login.")
+ -> { @ftp.login("rubyspec") }.should raise_error(Net::FTPReplyError)
+ end
+ end
+
+ describe "when passed name, password" do
+ it "sends the USER command with the passed name to the server" do
+ @ftp.login("rubyspec", "rocks")
+ @server.login_user.should == "rubyspec"
+ end
+
+ it "sends the passed password when required" do
+ @server.should_receive(:user).and_respond("331 User name okay, need password.")
+ @ftp.login("rubyspec", "rocks")
+ @server.login_pass.should == "rocks"
+ end
+
+ it "raises a Net::FTPReplyError when the server requests an account" do
+ @server.should_receive(:user).and_respond("331 User name okay, need password.")
+ @server.should_receive(:pass).and_respond("332 Need account for login.")
+ -> { @ftp.login("rubyspec", "rocks") }.should raise_error(Net::FTPReplyError)
+ end
+ end
+
+ describe "when passed name, password, account" do
+ it "sends the USER command with the passed name to the server" do
+ @ftp.login("rubyspec", "rocks", "account")
+ @server.login_user.should == "rubyspec"
+ end
+
+ it "sends the passed password when required" do
+ @server.should_receive(:user).and_respond("331 User name okay, need password.")
+ @ftp.login("rubyspec", "rocks", "account")
+ @server.login_pass.should == "rocks"
+ end
+
+ it "sends the passed account when required" do
+ @server.should_receive(:user).and_respond("331 User name okay, need password.")
+ @server.should_receive(:pass).and_respond("332 Need account for login.")
+ @ftp.login("rubyspec", "rocks", "account")
+ @server.login_acct.should == "account"
+ end
+ end
+
+ describe "when the USER command fails" do
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:user).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:user).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:user).and_respond("502 Command not implemented.")
+ -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:user).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:user).and_respond("530 Not logged in.")
+ -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
+ end
+ end
+
+ describe "when the PASS command fails" do
+ before :each do
+ @server.should_receive(:user).and_respond("331 User name okay, need password.")
+ end
+
+ it "does not raise an Error when the response code is 202" do
+ @server.should_receive(:pass).and_respond("202 Command not implemented, superfluous at this site.")
+ -> { @ftp.login("rubyspec", "rocks", "account") }.should_not raise_error
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:pass).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:pass).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:pass).and_respond("502 Command not implemented.")
+ -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:pass).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:pass).and_respond("530 Not logged in.")
+ -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
+ end
+ end
+
+ describe "when the ACCT command fails" do
+ before :each do
+ @server.should_receive(:user).and_respond("331 User name okay, need password.")
+ @server.should_receive(:pass).and_respond("332 Need account for login.")
+ end
+
+ it "does not raise an Error when the response code is 202" do
+ @server.should_receive(:acct).and_respond("202 Command not implemented, superfluous at this site.")
+ -> { @ftp.login("rubyspec", "rocks", "account") }.should_not raise_error
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:acct).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:acct).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:acct).and_respond("502 Command not implemented.")
+ -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:acct).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:acct).and_respond("530 Not logged in.")
+ -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
+ end
+ end
+end
diff --git a/spec/ruby/library/net-ftp/ls_spec.rb b/spec/ruby/library/net-ftp/ls_spec.rb
new file mode 100644
index 0000000000..acd7e9e523
--- /dev/null
+++ b/spec/ruby/library/net-ftp/ls_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+require_relative 'shared/list'
+
+describe "Net::FTP#ls" do
+ it_behaves_like :net_ftp_list, :ls
+end
diff --git a/spec/ruby/library/net-ftp/mdtm_spec.rb b/spec/ruby/library/net-ftp/mdtm_spec.rb
new file mode 100644
index 0000000000..a504507c84
--- /dev/null
+++ b/spec/ruby/library/net-ftp/mdtm_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#mdtm" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the MDTM with the passed filename command to the server" do
+ @ftp.mdtm("test.file")
+ @ftp.last_response.should == "213 19980705132316\n"
+ end
+
+ it "returns the last modification time of the passed file" do
+ @ftp.mdtm("test.file").should == "19980705132316"
+ end
+
+ it "raises a Net::FTPPermError when the response code is 550" do
+ @server.should_receive(:mdtm).and_respond("550 Requested action not taken.")
+ -> { @ftp.mdtm("test.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:mdtm).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.mdtm("test.file") }.should raise_error(Net::FTPTempError)
+ end
+end
diff --git a/spec/ruby/library/net-ftp/mkdir_spec.rb b/spec/ruby/library/net-ftp/mkdir_spec.rb
new file mode 100644
index 0000000000..8cc6ae785e
--- /dev/null
+++ b/spec/ruby/library/net-ftp/mkdir_spec.rb
@@ -0,0 +1,61 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#mkdir" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the MKD command with the passed pathname to the server" do
+ @ftp.mkdir("test.folder")
+ @ftp.last_response.should == %{257 "test.folder" created.\n}
+ end
+
+ it "returns the path to the newly created directory" do
+ @ftp.mkdir("test.folder").should == "test.folder"
+ @ftp.mkdir("/absolute/path/to/test.folder").should == "/absolute/path/to/test.folder"
+ @ftp.mkdir("relative/path/to/test.folder").should == "relative/path/to/test.folder"
+ @ftp.mkdir('/usr/dm/foo"bar').should == '/usr/dm/foo"bar'
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:mkd).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.mkdir("test.folder") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:mkd).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.mkdir("test.folder") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:mkd).and_respond("502 Command not implemented.")
+ -> { @ftp.mkdir("test.folder") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:mkd).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.mkdir("test.folder") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:mkd).and_respond("530 Not logged in.")
+ -> { @ftp.mkdir("test.folder") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 550" do
+ @server.should_receive(:mkd).and_respond("550 Requested action not taken.")
+ -> { @ftp.mkdir("test.folder") }.should raise_error(Net::FTPPermError)
+ end
+end
diff --git a/spec/ruby/library/net-ftp/mtime_spec.rb b/spec/ruby/library/net-ftp/mtime_spec.rb
new file mode 100644
index 0000000000..9dde1278a8
--- /dev/null
+++ b/spec/ruby/library/net-ftp/mtime_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#mtime" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the MDTM with the passed filename command to the server" do
+ @ftp.mtime("test.file")
+ @ftp.last_response.should == "213 19980705132316\n"
+ end
+
+ describe "when passed filename" do
+ it "returns the last modification time of the passed file as a Time object in the local time" do
+ @ftp.mtime("test.file").should == Time.gm("1998", "07", "05", "13", "23", "16")
+ end
+ end
+
+ describe "when passed filename, local_time" do
+ it "returns the last modification time as a Time object in UTC when local_time is true" do
+ @ftp.mtime("test.file", true).should == Time.local("1998", "07", "05", "13", "23", "16")
+ end
+
+ it "returns the last modification time as a Time object in the local time when local_time is false" do
+ @ftp.mtime("test.file", false).should == Time.gm("1998", "07", "05", "13", "23", "16")
+ end
+ end
+
+ it "raises a Net::FTPPermError when the response code is 550" do
+ @server.should_receive(:mdtm).and_respond("550 Requested action not taken.")
+ -> { @ftp.mtime("test.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:mdtm).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.mtime("test.file") }.should raise_error(Net::FTPTempError)
+ end
+end
diff --git a/spec/ruby/library/net-ftp/nlst_spec.rb b/spec/ruby/library/net-ftp/nlst_spec.rb
new file mode 100644
index 0000000000..2f22543af6
--- /dev/null
+++ b/spec/ruby/library/net-ftp/nlst_spec.rb
@@ -0,0 +1,92 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#nlst" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.passive = false
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ describe "when passed no arguments" do
+ it "returns an Array containing a list of files in the current dir" do
+ @ftp.nlst.should == ["last_response_code.rb", "list.rb", "pwd.rb"]
+ @ftp.last_response.should == "226 transfer complete (NLST)\n"
+ end
+ end
+
+ describe "when passed dir" do
+ it "returns an Array containing a list of files in the passed dir" do
+ @ftp.nlst("test.folder").should == ["last_response_code.rb", "list.rb", "pwd.rb"]
+ @ftp.last_response.should == "226 transfer complete (NLST test.folder)\n"
+ end
+ end
+
+ describe "when the NLST command fails" do
+ it "raises a Net::FTPTempError when the response code is 450" do
+ @server.should_receive(:nlst).and_respond("450 Requested file action not taken..")
+ -> { @ftp.nlst }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:nlst).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.nlst }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:nlst).and_respond("501 Syntax error, command unrecognized.")
+ -> { @ftp.nlst }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:nlst).and_respond("502 Command not implemented.")
+ -> { @ftp.nlst }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:nlst).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.nlst }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:nlst).and_respond("530 Not logged in.")
+ -> { @ftp.nlst }.should raise_error(Net::FTPPermError)
+ end
+ end
+
+ describe "when opening the data port fails" do
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:eprt).and_respond("500 Syntax error, command unrecognized.")
+ @server.should_receive(:port).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.nlst }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:eprt).and_respond("501 Syntax error in parameters or arguments.")
+ @server.should_receive(:port).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.nlst }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:eprt).and_respond("421 Service not available, closing control connection.")
+ @server.should_receive(:port).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.nlst }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:eprt).and_respond("530 Not logged in.")
+ @server.should_receive(:port).and_respond("530 Not logged in.")
+ -> { @ftp.nlst }.should raise_error(Net::FTPPermError)
+ end
+ end
+end
diff --git a/spec/ruby/library/net-ftp/noop_spec.rb b/spec/ruby/library/net-ftp/noop_spec.rb
new file mode 100644
index 0000000000..4743a39ef6
--- /dev/null
+++ b/spec/ruby/library/net-ftp/noop_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#noop" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the NOOP command to the server" do
+ @ftp.noop
+ @ftp.last_response.should == "200 Command okay. (NOOP)\n"
+ end
+
+ it "returns nil" do
+ @ftp.noop.should be_nil
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:noop).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.noop }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:noop).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.noop }.should raise_error(Net::FTPTempError)
+ end
+end
diff --git a/spec/ruby/library/net-ftp/open_spec.rb b/spec/ruby/library/net-ftp/open_spec.rb
new file mode 100644
index 0000000000..e59496dc3c
--- /dev/null
+++ b/spec/ruby/library/net-ftp/open_spec.rb
@@ -0,0 +1,55 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+
+describe "Net::FTP.open" do
+ before :each do
+ @ftp = mock("Net::FTP instance")
+ Net::FTP.stub!(:new).and_return(@ftp)
+ end
+
+ describe "when passed no block" do
+ it "returns a new Net::FTP instance" do
+ Net::FTP.open("localhost").should equal(@ftp)
+ end
+
+ it "passes the passed arguments down to Net::FTP.new" do
+ Net::FTP.should_receive(:new).with("localhost", "user", "password", "account")
+ Net::FTP.open("localhost", "user", "password", "account")
+ end
+ end
+
+ describe "when passed a block" do
+ before :each do
+ @ftp.stub!(:close)
+ end
+
+ it "yields a new Net::FTP instance to the passed block" do
+ yielded = false
+ Net::FTP.open("localhost") do |ftp|
+ yielded = true
+ ftp.should equal(@ftp)
+ end
+ yielded.should be_true
+ end
+
+ it "closes the Net::FTP instance after yielding" do
+ Net::FTP.open("localhost") do |ftp|
+ ftp.should_receive(:close)
+ end
+ end
+
+ it "closes the Net::FTP instance even if an exception is raised while yielding" do
+ begin
+ Net::FTP.open("localhost") do |ftp|
+ ftp.should_receive(:close)
+ raise ArgumentError, "some exception"
+ end
+ rescue ArgumentError
+ end
+ end
+
+ it "returns the block's return value" do
+ Net::FTP.open("localhost") { :test }.should == :test
+ end
+ end
+end
diff --git a/spec/ruby/library/net-ftp/passive_spec.rb b/spec/ruby/library/net-ftp/passive_spec.rb
new file mode 100644
index 0000000000..97659f1b68
--- /dev/null
+++ b/spec/ruby/library/net-ftp/passive_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+
+describe "Net::FTP#passive" do
+ it "returns true when self is in passive mode" do
+ ftp = Net::FTP.new
+ ftp.passive.should be_false
+
+ ftp.passive = true
+ ftp.passive.should be_true
+ end
+
+ it "is the value of Net::FTP.default_value by default" do
+ ruby_exe(fixture(__FILE__, "passive.rb")).should == "true"
+ end
+end
+
+describe "Net::FTP#passive=" do
+ it "sets self to passive mode when passed true" do
+ ftp = Net::FTP.new
+
+ ftp.passive = true
+ ftp.passive.should be_true
+
+ ftp.passive = false
+ ftp.passive.should be_false
+ end
+end
diff --git a/spec/ruby/library/net-ftp/put_spec.rb b/spec/ruby/library/net-ftp/put_spec.rb
new file mode 100644
index 0000000000..6d40d3d5b9
--- /dev/null
+++ b/spec/ruby/library/net-ftp/put_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+require_relative 'shared/puttextfile'
+require_relative 'shared/putbinaryfile'
+
+describe "Net::FTP#put (binary mode)" do
+ before :each do
+ @binary_mode = true
+ end
+
+ it_behaves_like :net_ftp_putbinaryfile, :put
+end
+
+describe "Net::FTP#put (text mode)" do
+ before :each do
+ @binary_mode = false
+ end
+
+ it_behaves_like :net_ftp_puttextfile, :put
+end
diff --git a/spec/ruby/library/net-ftp/putbinaryfile_spec.rb b/spec/ruby/library/net-ftp/putbinaryfile_spec.rb
new file mode 100644
index 0000000000..d0398229e5
--- /dev/null
+++ b/spec/ruby/library/net-ftp/putbinaryfile_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+require_relative 'shared/putbinaryfile'
+
+describe "Net::FTP#putbinaryfile" do
+ it_behaves_like :net_ftp_putbinaryfile, :putbinaryfile
+end
diff --git a/spec/ruby/library/net-ftp/puttextfile_spec.rb b/spec/ruby/library/net-ftp/puttextfile_spec.rb
new file mode 100644
index 0000000000..b8bcac33df
--- /dev/null
+++ b/spec/ruby/library/net-ftp/puttextfile_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+require_relative 'shared/puttextfile'
+
+describe "Net::FTP#puttextfile" do
+ it_behaves_like :net_ftp_puttextfile, :puttextfile
+end
diff --git a/spec/ruby/library/net-ftp/pwd_spec.rb b/spec/ruby/library/net-ftp/pwd_spec.rb
new file mode 100644
index 0000000000..992e2c4ed2
--- /dev/null
+++ b/spec/ruby/library/net-ftp/pwd_spec.rb
@@ -0,0 +1,53 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#pwd" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the PWD command to the server" do
+ @ftp.pwd
+ @ftp.last_response.should == "257 \"/some/dir/\" - current directory\n"
+ end
+
+ it "returns the current directory" do
+ @ftp.pwd.should == "/some/dir/"
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:pwd).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.pwd }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:pwd).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.pwd }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:pwd).and_respond("502 Command not implemented.")
+ -> { @ftp.pwd }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:pwd).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.pwd }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 550" do
+ @server.should_receive(:pwd).and_respond("550 Requested action not taken.")
+ -> { @ftp.pwd }.should raise_error(Net::FTPPermError)
+ end
+end
diff --git a/spec/ruby/library/net-ftp/quit_spec.rb b/spec/ruby/library/net-ftp/quit_spec.rb
new file mode 100644
index 0000000000..c5352ceada
--- /dev/null
+++ b/spec/ruby/library/net-ftp/quit_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#quit" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the QUIT command to the server" do
+ @ftp.quit
+ @ftp.last_response.should == "221 OK, bye\n"
+ end
+
+ it "does not close the socket automatically" do
+ @ftp.quit
+ @ftp.closed?.should be_false
+ end
+
+ it "returns nil" do
+ @ftp.quit.should be_nil
+ end
+end
diff --git a/spec/ruby/library/net-ftp/rename_spec.rb b/spec/ruby/library/net-ftp/rename_spec.rb
new file mode 100644
index 0000000000..48f81b7deb
--- /dev/null
+++ b/spec/ruby/library/net-ftp/rename_spec.rb
@@ -0,0 +1,94 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#rename" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ describe "when passed from_name, to_name" do
+ it "sends the RNFR command with the passed from_name and the RNTO command with the passed to_name to the server" do
+ @ftp.rename("from.file", "to.file")
+ @ftp.last_response.should == "250 Requested file action okay, completed. (Renamed from.file to to.file)\n"
+ end
+
+ it "returns something" do
+ @ftp.rename("from.file", "to.file").should be_nil
+ end
+ end
+
+ describe "when the RNFR command fails" do
+ it "raises a Net::FTPTempError when the response code is 450" do
+ @server.should_receive(:rnfr).and_respond("450 Requested file action not taken.")
+ -> { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 550" do
+ @server.should_receive(:rnfr).and_respond("550 Requested action not taken.")
+ -> { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:rnfr).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:rnfr).and_respond("502 Command not implemented.")
+ -> { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:rnfr).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:rnfr).and_respond("530 Not logged in.")
+ -> { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
+ end
+ end
+
+ describe "when the RNTO command fails" do
+ it "raises a Net::FTPPermError when the response code is 532" do
+ @server.should_receive(:rnfr).and_respond("532 Need account for storing files.")
+ -> { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 553" do
+ @server.should_receive(:rnto).and_respond("553 Requested action not taken.")
+ -> { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:rnto).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:rnto).and_respond("502 Command not implemented.")
+ -> { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:rnto).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:rnto).and_respond("530 Not logged in.")
+ -> { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
+ end
+ end
+end
diff --git a/spec/ruby/library/net-ftp/resume_spec.rb b/spec/ruby/library/net-ftp/resume_spec.rb
new file mode 100644
index 0000000000..6592fc5bb0
--- /dev/null
+++ b/spec/ruby/library/net-ftp/resume_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+
+describe "Net::FTP#resume" do
+ it "returns true when self is set to resume uploads/downloads" do
+ ftp = Net::FTP.new
+ ftp.resume.should be_false
+
+ ftp.resume = true
+ ftp.resume.should be_true
+ end
+end
+
+describe "Net::FTP#resume=" do
+ it "sets self to resume uploads/downloads when set to true" do
+ ftp = Net::FTP.new
+ ftp.resume = true
+ ftp.resume.should be_true
+
+ ftp.resume = false
+ ftp.resume.should be_false
+ end
+end
diff --git a/spec/ruby/library/net-ftp/retrbinary_spec.rb b/spec/ruby/library/net-ftp/retrbinary_spec.rb
new file mode 100644
index 0000000000..de024208aa
--- /dev/null
+++ b/spec/ruby/library/net-ftp/retrbinary_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#retrbinary" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the passed command to the server" do
+ @ftp.retrbinary("RETR test", 4096) {}
+ @ftp.last_response.should == "226 Closing data connection. (RETR test)\n"
+ end
+
+ it "yields the received content as binary blocks of the passed size" do
+ res = []
+ @ftp.retrbinary("RETR test", 10) { |bin| res << bin }
+ res.should == [ "This is th", "e content\n", "of the fil", "e named 't", "est'.\n" ]
+ end
+end
diff --git a/spec/ruby/library/net-ftp/retrlines_spec.rb b/spec/ruby/library/net-ftp/retrlines_spec.rb
new file mode 100644
index 0000000000..866ecb5f40
--- /dev/null
+++ b/spec/ruby/library/net-ftp/retrlines_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#retrlines" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the passed command over the socket" do
+ @ftp.retrlines("LIST test.dir") {}
+ @ftp.last_response.should == "226 transfer complete (LIST test.dir)\n"
+ end
+
+ it "yields each received line to the passed block" do
+ res = []
+ @ftp.retrlines("LIST test.dir") { |x| res << x }
+ res.should == [
+ "-rw-r--r-- 1 spec staff 507 17 Jul 18:41 last_response_code.rb",
+ "-rw-r--r-- 1 spec staff 50 17 Jul 18:41 list.rb",
+ "-rw-r--r-- 1 spec staff 48 17 Jul 18:41 pwd.rb"
+ ]
+ end
+end
diff --git a/spec/ruby/library/net-ftp/return_code_spec.rb b/spec/ruby/library/net-ftp/return_code_spec.rb
new file mode 100644
index 0000000000..35a6232f7e
--- /dev/null
+++ b/spec/ruby/library/net-ftp/return_code_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+
+describe "Net::FTP#return_code" do
+ before :each do
+ @ftp = Net::FTP.new
+ end
+
+ it "outputs a warning and returns a newline" do
+ -> do
+ @ftp.return_code.should == "\n"
+ end.should complain(/warning: Net::FTP#return_code is obsolete and do nothing/)
+ end
+end
+
+describe "Net::FTP#return_code=" do
+ before :each do
+ @ftp = Net::FTP.new
+ end
+
+ it "outputs a warning" do
+ -> { @ftp.return_code = 123 }.should complain(/warning: Net::FTP#return_code= is obsolete and do nothing/)
+ end
+end
diff --git a/spec/ruby/library/net-ftp/rmdir_spec.rb b/spec/ruby/library/net-ftp/rmdir_spec.rb
new file mode 100644
index 0000000000..400874d60d
--- /dev/null
+++ b/spec/ruby/library/net-ftp/rmdir_spec.rb
@@ -0,0 +1,58 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#rmdir" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the RMD command with the passed pathname to the server" do
+ @ftp.rmdir("test.folder")
+ @ftp.last_response.should == "250 Requested file action okay, completed. (RMD test.folder)\n"
+ end
+
+ it "returns nil" do
+ @ftp.rmdir("test.folder").should be_nil
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:rmd).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.rmdir("test.folder") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:rmd).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.rmdir("test.folder") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:rmd).and_respond("502 Command not implemented.")
+ -> { @ftp.rmdir("test.folder") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:rmd).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.rmdir("test.folder") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:rmd).and_respond("530 Not logged in.")
+ -> { @ftp.rmdir("test.folder") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 550" do
+ @server.should_receive(:rmd).and_respond("550 Requested action not taken.")
+ -> { @ftp.rmdir("test.folder") }.should raise_error(Net::FTPPermError)
+ end
+end
diff --git a/spec/ruby/library/net-ftp/sendcmd_spec.rb b/spec/ruby/library/net-ftp/sendcmd_spec.rb
new file mode 100644
index 0000000000..c50b373869
--- /dev/null
+++ b/spec/ruby/library/net-ftp/sendcmd_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#sendcmd" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the passed command to the server" do
+ @ftp.sendcmd("HELP")
+ @ftp.last_response.should == "211 System status, or system help reply. (HELP)\n"
+ end
+
+ it "returns the server's response" do
+ @ftp.sendcmd("HELP").should == "211 System status, or system help reply. (HELP)\n"
+ end
+
+ it "raises no error when the response code is 1xx, 2xx or 3xx" do
+ @server.should_receive(:help).and_respond("120 Service ready in nnn minutes.")
+ -> { @ftp.sendcmd("HELP") }.should_not raise_error
+
+ @server.should_receive(:help).and_respond("200 Command okay.")
+ -> { @ftp.sendcmd("HELP") }.should_not raise_error
+
+ @server.should_receive(:help).and_respond("350 Requested file action pending further information.")
+ -> { @ftp.sendcmd("HELP") }.should_not raise_error
+ end
+
+ it "raises a Net::FTPTempError when the response code is 4xx" do
+ @server.should_receive(:help).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.sendcmd("HELP") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 5xx" do
+ @server.should_receive(:help).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.sendcmd("HELP") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPProtoError when the response code is not between 1xx-5xx" do
+ @server.should_receive(:help).and_respond("999 Invalid response.")
+ -> { @ftp.sendcmd("HELP") }.should raise_error(Net::FTPProtoError)
+ end
+end
diff --git a/spec/ruby/library/net-ftp/set_socket_spec.rb b/spec/ruby/library/net-ftp/set_socket_spec.rb
new file mode 100644
index 0000000000..8182dd8b33
--- /dev/null
+++ b/spec/ruby/library/net-ftp/set_socket_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+
+describe "Net::FTP#set_socket" do
+ # TODO: I won't spec this method, as it is not used
+ # anywhere and it should be private anyway.
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/net-ftp/shared/getbinaryfile.rb b/spec/ruby/library/net-ftp/shared/getbinaryfile.rb
new file mode 100644
index 0000000000..ceec8e7cd5
--- /dev/null
+++ b/spec/ruby/library/net-ftp/shared/getbinaryfile.rb
@@ -0,0 +1,150 @@
+describe :net_ftp_getbinaryfile, shared: true do
+ before :each do
+ @fixture_file = __dir__ + "/../fixtures/getbinaryfile"
+ @tmp_file = tmp("getbinaryfile")
+
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ @ftp.binary = @binary_mode
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+
+ rm_r @tmp_file
+ end
+
+ it "sends the RETR command to the server" do
+ @ftp.send(@method, "test", @tmp_file)
+ @ftp.last_response.should == "226 Closing data connection. (RETR test)\n"
+ end
+
+ it "returns nil" do
+ @ftp.send(@method, "test", @tmp_file).should be_nil
+ end
+
+ it "saves the contents of the passed remote file to the passed local file" do
+ @ftp.send(@method, "test", @tmp_file)
+ File.read(@tmp_file).should == "This is the content\nof the file named 'test'.\n"
+ end
+
+ describe "when passed a block" do
+ it "yields the received content as binary blocks of the passed size" do
+ res = []
+ @ftp.send(@method, "test", @tmp_file, 10) { |bin| res << bin }
+ res.should == [ "This is th", "e content\n", "of the fil", "e named 't", "est'.\n" ]
+ end
+ end
+
+ describe "when resuming an existing file" do
+ before :each do
+ @tmp_file = tmp("getbinaryfile_resume")
+
+ File.open(@tmp_file, "wb") do |f|
+ f << "This is the content\n"
+ end
+
+ @ftp.resume = true
+ end
+
+ it "saves the remaining content of the passed remote file to the passed local file" do
+ @ftp.send(@method, "test", @tmp_file)
+ File.read(@tmp_file).should == "This is the content\nof the file named 'test'.\n"
+ end
+
+ describe "and the REST command fails" do
+ it "raises a Net::FTPProtoError when the response code is 550" do
+ @server.should_receive(:rest).and_respond("Requested action not taken.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPProtoError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:rest).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:rest).and_respond("501 Syntax error, command unrecognized.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:rest).and_respond("502 Command not implemented.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:rest).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:rest).and_respond("530 Not logged in.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+ end
+ end
+
+ describe "when the RETR command fails" do
+ it "raises a Net::FTPTempError when the response code is 450" do
+ @server.should_receive(:retr).and_respond("450 Requested file action not taken.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPProtoError when the response code is 550" do
+ @server.should_receive(:retr).and_respond("Requested action not taken.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPProtoError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:retr).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:retr).and_respond("501 Syntax error, command unrecognized.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:retr).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:retr).and_respond("530 Not logged in.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+ end
+
+ describe "when opening the data port fails" do
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:eprt).and_respond("500 Syntax error, command unrecognized.")
+ @server.should_receive(:port).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:eprt).and_respond("501 Syntax error in parameters or arguments.")
+ @server.should_receive(:port).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:eprt).and_respond("421 Service not available, closing control connection.")
+ @server.should_receive(:port).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:eprt).and_respond("530 Not logged in.")
+ @server.should_receive(:port).and_respond("530 Not logged in.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+ end
+end
diff --git a/spec/ruby/library/net-ftp/shared/gettextfile.rb b/spec/ruby/library/net-ftp/shared/gettextfile.rb
new file mode 100644
index 0000000000..7fe14f7dfb
--- /dev/null
+++ b/spec/ruby/library/net-ftp/shared/gettextfile.rb
@@ -0,0 +1,100 @@
+describe :net_ftp_gettextfile, shared: true do
+ before :each do
+ @tmp_file = tmp("gettextfile")
+
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ @ftp.binary = @binary_mode
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+
+ rm_r @tmp_file
+ end
+
+ it "sends the RETR command to the server" do
+ @ftp.send(@method, "test", @tmp_file)
+ @ftp.last_response.should == "226 Closing data connection. (RETR test)\n"
+ end
+
+ it "returns nil" do
+ @ftp.send(@method, "test", @tmp_file).should be_nil
+ end
+
+ it "saves the contents of the passed remote file to the passed local file" do
+ @ftp.send(@method, "test", @tmp_file)
+ File.read(@tmp_file).should == "This is the content\nof the file named 'test'.\n"
+ end
+
+ describe "when passed a block" do
+ it "yields each line of the retrieved file to the passed block" do
+ res = []
+ @ftp.send(@method, "test", @tmp_file) { |line| res << line }
+ res.should == [ "This is the content", "of the file named 'test'."]
+ end
+ end
+
+ describe "when the RETR command fails" do
+ it "raises a Net::FTPTempError when the response code is 450" do
+ @server.should_receive(:retr).and_respond("450 Requested file action not taken.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPProtoError when the response code is 550" do
+ @server.should_receive(:retr).and_respond("Requested action not taken.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPProtoError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:retr).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:retr).and_respond("501 Syntax error, command unrecognized.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:retr).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:retr).and_respond("530 Not logged in.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+ end
+
+ describe "when opening the data port fails" do
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:eprt).and_respond("500 Syntax error, command unrecognized.")
+ @server.should_receive(:port).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:eprt).and_respond("501 Syntax error in parameters or arguments.")
+ @server.should_receive(:port).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:eprt).and_respond("421 Service not available, closing control connection.")
+ @server.should_receive(:port).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:eprt).and_respond("530 Not logged in.")
+ @server.should_receive(:port).and_respond("530 Not logged in.")
+ -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+ end
+end
diff --git a/spec/ruby/library/net/ftp/shared/last_response_code.rb b/spec/ruby/library/net-ftp/shared/last_response_code.rb
index 4fe53677db..4fe53677db 100644
--- a/spec/ruby/library/net/ftp/shared/last_response_code.rb
+++ b/spec/ruby/library/net-ftp/shared/last_response_code.rb
diff --git a/spec/ruby/library/net/ftp/shared/list.rb b/spec/ruby/library/net-ftp/shared/list.rb
index adc3fa59c1..adc3fa59c1 100644
--- a/spec/ruby/library/net/ftp/shared/list.rb
+++ b/spec/ruby/library/net-ftp/shared/list.rb
diff --git a/spec/ruby/library/net-ftp/shared/putbinaryfile.rb b/spec/ruby/library/net-ftp/shared/putbinaryfile.rb
new file mode 100644
index 0000000000..45f53adc2a
--- /dev/null
+++ b/spec/ruby/library/net-ftp/shared/putbinaryfile.rb
@@ -0,0 +1,167 @@
+describe :net_ftp_putbinaryfile, shared: true do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @local_fixture_file = __dir__ + "/../fixtures/putbinaryfile"
+ @remote_tmp_file = tmp("binaryfile", false)
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ @ftp.binary = @binary_mode
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+
+ rm_r @remote_tmp_file
+ end
+
+ it "sends the STOR command to the server" do
+ @ftp.send(@method, @local_fixture_file, "binary")
+ @ftp.last_response.should == "200 OK, Data received. (STOR binary)\n"
+ end
+
+ it "sends the contents of the passed local_file, without modifications" do
+ @ftp.send(@method, @local_fixture_file, "binary")
+
+ remote_lines = File.readlines(@remote_tmp_file)
+ local_lines = File.readlines(@local_fixture_file)
+
+ remote_lines.should == local_lines
+ end
+
+ it "returns nil" do
+ @ftp.send(@method, @local_fixture_file, "binary").should be_nil
+ end
+
+ describe "when passed a block" do
+ it "yields the transmitted content as binary blocks of the passed size" do
+ res = []
+ @ftp.send(@method, @local_fixture_file, "binary", 10) { |x| res << x }
+ res.should == [
+ "This is an", " example f",
+ "ile\nwhich ", "is going t",
+ "o be trans", "mitted\nusi",
+ "ng #putbin", "aryfile.\n"
+ ]
+ end
+ end
+
+ describe "when resuming an existing file" do
+ before :each do
+ File.open(@remote_tmp_file, "w") do |f|
+ f << "This is an example file\n"
+ end
+
+ @ftp.resume = true
+ end
+
+ it "sends the remaining content of the passed local_file to the passed remote_file" do
+ @ftp.send(@method, @local_fixture_file, "binary")
+ File.read(@remote_tmp_file).should == File.read(@local_fixture_file)
+ end
+
+ describe "and the APPE command fails" do
+ it "raises a Net::FTPProtoError when the response code is 550" do
+ @server.should_receive(:appe).and_respond("Requested action not taken.")
+ -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPProtoError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:appe).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:appe).and_respond("501 Syntax error, command unrecognized.")
+ -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:appe).and_respond("502 Command not implemented.")
+ -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:appe).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:appe).and_respond("530 Not logged in.")
+ -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
+ end
+ end
+ end
+
+ describe "when the STOR command fails" do
+ it "raises a Net::FTPPermError when the response code is 532" do
+ @server.should_receive(:stor).and_respond("532 Need account for storing files.")
+ -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 450" do
+ @server.should_receive(:stor).and_respond("450 Requested file action not taken.")
+ -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 452" do
+ @server.should_receive(:stor).and_respond("452 Requested action not taken.")
+ -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 553" do
+ @server.should_receive(:stor).and_respond("553 Requested action not taken.")
+ -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:stor).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:stor).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:stor).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:stor).and_respond("530 Not logged in.")
+ -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
+ end
+ end
+
+ describe "when opening the data port fails" do
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:eprt).and_respond("500 Syntax error, command unrecognized.")
+ @server.should_receive(:port).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:eprt).and_respond("501 Syntax error in parameters or arguments.")
+ @server.should_receive(:port).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:eprt).and_respond("421 Service not available, closing control connection.")
+ @server.should_receive(:port).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:eprt).and_respond("530 Not logged in.")
+ @server.should_receive(:port).and_respond("530 Not logged in.")
+ -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
+ end
+ end
+end
diff --git a/spec/ruby/library/net-ftp/shared/puttextfile.rb b/spec/ruby/library/net-ftp/shared/puttextfile.rb
new file mode 100644
index 0000000000..4722439674
--- /dev/null
+++ b/spec/ruby/library/net-ftp/shared/puttextfile.rb
@@ -0,0 +1,120 @@
+describe :net_ftp_puttextfile, shared: true do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @local_fixture_file = __dir__ + "/../fixtures/puttextfile"
+ @remote_tmp_file = tmp("textfile", false)
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ @ftp.binary = @binary_mode
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+
+ rm_r @remote_tmp_file
+ end
+
+ it "sends the STOR command to the server" do
+ @ftp.send(@method, @local_fixture_file, "text")
+ @ftp.last_response.should == "200 OK, Data received. (STOR text)\n"
+ end
+
+ it "sends the contents of the passed local_file, using \\r\\n as the newline separator" do
+ @ftp.send(@method, @local_fixture_file, "text")
+
+ remote_lines = File.binread(@remote_tmp_file)
+ local_lines = File.binread(@local_fixture_file)
+
+ remote_lines.should_not == local_lines
+ remote_lines.should == local_lines.gsub("\n", "\r\n")
+ end
+
+ it "returns nil" do
+ @ftp.send(@method, @local_fixture_file, "text").should be_nil
+ end
+
+ describe "when passed a block" do
+ it "yields each transmitted line" do
+ res = []
+ @ftp.send(@method, @local_fixture_file, "text") { |x| res << x }
+ res.should == [
+ "This is an example file\r\n",
+ "which is going to be transmitted\r\n",
+ "using #puttextfile.\r\n"
+ ]
+ end
+ end
+
+ describe "when the STOR command fails" do
+ it "raises a Net::FTPPermError when the response code is 532" do
+ @server.should_receive(:stor).and_respond("532 Need account for storing files.")
+ -> { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 450" do
+ @server.should_receive(:stor).and_respond("450 Requested file action not taken.")
+ -> { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 452" do
+ @server.should_receive(:stor).and_respond("452 Requested action not taken.")
+ -> { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 553" do
+ @server.should_receive(:stor).and_respond("553 Requested action not taken.")
+ -> { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:stor).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:stor).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:stor).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:stor).and_respond("530 Not logged in.")
+ -> { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError)
+ end
+ end
+
+ describe "when opening the data port fails" do
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:eprt).and_respond("500 Syntax error, command unrecognized.")
+ @server.should_receive(:port).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:eprt).and_respond("501 Syntax error in parameters or arguments.")
+ @server.should_receive(:port).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:eprt).and_respond("421 Service not available, closing control connection.")
+ @server.should_receive(:port).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:eprt).and_respond("530 Not logged in.")
+ @server.should_receive(:port).and_respond("530 Not logged in.")
+ -> { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError)
+ end
+ end
+end
diff --git a/spec/ruby/library/net/ftp/shared/pwd.rb b/spec/ruby/library/net-ftp/shared/pwd.rb
index 951d020f2d..951d020f2d 100644
--- a/spec/ruby/library/net/ftp/shared/pwd.rb
+++ b/spec/ruby/library/net-ftp/shared/pwd.rb
diff --git a/spec/ruby/library/net-ftp/site_spec.rb b/spec/ruby/library/net-ftp/site_spec.rb
new file mode 100644
index 0000000000..c3e589a920
--- /dev/null
+++ b/spec/ruby/library/net-ftp/site_spec.rb
@@ -0,0 +1,53 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#site" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the SITE command with the passed argument to the server" do
+ @ftp.site("param")
+ @ftp.last_response.should == "200 Command okay. (SITE param)\n"
+ end
+
+ it "returns nil" do
+ @ftp.site("param").should be_nil
+ end
+
+ it "does not raise an error when the response code is 202" do
+ @server.should_receive(:site).and_respond("202 Command not implemented, superfluous at this site.")
+ -> { @ftp.site("param") }.should_not raise_error
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:site).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.site("param") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:site).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.site("param") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:site).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.site("param") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:site).and_respond("530 Requested action not taken.")
+ -> { @ftp.site("param") }.should raise_error(Net::FTPPermError)
+ end
+end
diff --git a/spec/ruby/library/net-ftp/size_spec.rb b/spec/ruby/library/net-ftp/size_spec.rb
new file mode 100644
index 0000000000..0cf2e24477
--- /dev/null
+++ b/spec/ruby/library/net-ftp/size_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#size" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the SIZE command to the server" do
+ @ftp.size("test.file")
+ @ftp.last_response.should == "213 1024\n"
+ end
+
+ it "returns the size of the passed file as Integer" do
+ @ftp.size("test.file").should eql(1024)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:size).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.size("test.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:size).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.size("test.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:size).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.size("test.file") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 550" do
+ @server.should_receive(:size).and_respond("550 Requested action not taken.")
+ -> { @ftp.size("test.file") }.should raise_error(Net::FTPPermError)
+ end
+end
diff --git a/spec/ruby/library/net/ftp/spec_helper.rb b/spec/ruby/library/net-ftp/spec_helper.rb
index c87d16218b..c87d16218b 100644
--- a/spec/ruby/library/net/ftp/spec_helper.rb
+++ b/spec/ruby/library/net-ftp/spec_helper.rb
diff --git a/spec/ruby/library/net-ftp/status_spec.rb b/spec/ruby/library/net-ftp/status_spec.rb
new file mode 100644
index 0000000000..9d9f86c381
--- /dev/null
+++ b/spec/ruby/library/net-ftp/status_spec.rb
@@ -0,0 +1,67 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#status" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the STAT command to the server" do
+ @ftp.status
+ @ftp.last_response.should == "211 System status, or system help reply. (STAT)\n"
+ end
+
+ it "sends the STAT command with an optional parameter to the server" do
+ @ftp.status("/pub").should == "211 System status, or system help reply. (STAT /pub)\n"
+ end
+
+ it "returns the received information" do
+ @ftp.status.should == "211 System status, or system help reply. (STAT)\n"
+ end
+
+ it "does not raise an error when the response code is 212" do
+ @server.should_receive(:stat).and_respond("212 Directory status.")
+ -> { @ftp.status }.should_not raise_error
+ end
+
+ it "does not raise an error when the response code is 213" do
+ @server.should_receive(:stat).and_respond("213 File status.")
+ -> { @ftp.status }.should_not raise_error
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:stat).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.status }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:stat).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.status }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:stat).and_respond("502 Command not implemented.")
+ -> { @ftp.status }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:stat).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.status }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:stat).and_respond("530 Requested action not taken.")
+ -> { @ftp.status }.should raise_error(Net::FTPPermError)
+ end
+end
diff --git a/spec/ruby/library/net-ftp/storbinary_spec.rb b/spec/ruby/library/net-ftp/storbinary_spec.rb
new file mode 100644
index 0000000000..aa4c51f2e8
--- /dev/null
+++ b/spec/ruby/library/net-ftp/storbinary_spec.rb
@@ -0,0 +1,49 @@
+require_relative '../../spec_helper'
+
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#storbinary" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @local_fixture_file = __dir__ + "/fixtures/putbinaryfile"
+ @tmp_file = tmp("binaryfile", false)
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+
+ rm_r @tmp_file
+ end
+
+ it "sends the passed command and the passed File object's content to the server" do
+ File.open(@local_fixture_file) do |f|
+ f.binmode
+
+ @ftp.storbinary("STOR binary", f, 4096) {}
+ @ftp.last_response.should == "200 OK, Data received. (STOR binary)\n"
+ end
+ end
+
+ it "yields the transmitted content as binary blocks of the passed size" do
+ File.open(@local_fixture_file) do |f|
+ f.binmode
+
+ res = []
+ @ftp.storbinary("STOR binary", f, 10) { |x| res << x }
+ res.should == [
+ "This is an", " example f",
+ "ile\nwhich ", "is going t",
+ "o be trans", "mitted\nusi",
+ "ng #putbin", "aryfile.\n"
+ ]
+ end
+ end
+end
diff --git a/spec/ruby/library/net-ftp/storlines_spec.rb b/spec/ruby/library/net-ftp/storlines_spec.rb
new file mode 100644
index 0000000000..dc6830da7b
--- /dev/null
+++ b/spec/ruby/library/net-ftp/storlines_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../spec_helper'
+
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#storlines" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @local_fixture_file = __dir__ + "/fixtures/puttextfile"
+ @tmp_file = tmp("textfile", false)
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+
+ rm_r @tmp_file
+ end
+
+ it "sends the passed command and the passed File object's content to the server" do
+ File.open(@local_fixture_file) do |f|
+ @ftp.storlines("STOR text", f) {}
+ @ftp.last_response.should == "200 OK, Data received. (STOR text)\n"
+ end
+ end
+
+ it "yields each line of the transmitted content" do
+ File.open(@local_fixture_file) do |f|
+ res = []
+ @ftp.storlines("STOR text", f) { |x| res << x }
+ res.should == [
+ "This is an example file\r\n",
+ "which is going to be transmitted\r\n",
+ "using #puttextfile.\r\n"
+ ]
+ end
+ end
+end
diff --git a/spec/ruby/library/net-ftp/system_spec.rb b/spec/ruby/library/net-ftp/system_spec.rb
new file mode 100644
index 0000000000..2b7f0d2560
--- /dev/null
+++ b/spec/ruby/library/net-ftp/system_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#system" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the SYST command to the server" do
+ @ftp.system
+ @ftp.last_response.should =~ /\A215 FTP Dummy Server \(SYST\)\Z/
+ end
+
+ it "returns the received information" do
+ @ftp.system.should =~ /\AFTP Dummy Server \(SYST\)\Z/
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:syst).and_respond("500 Syntax error, command unrecognized.")
+ -> { @ftp.system }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:syst).and_respond("501 Syntax error in parameters or arguments.")
+ -> { @ftp.system }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:syst).and_respond("502 Command not implemented.")
+ -> { @ftp.system }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:syst).and_respond("421 Service not available, closing control connection.")
+ -> { @ftp.system }.should raise_error(Net::FTPTempError)
+ end
+end
diff --git a/spec/ruby/library/net-ftp/voidcmd_spec.rb b/spec/ruby/library/net-ftp/voidcmd_spec.rb
new file mode 100644
index 0000000000..f2536fe697
--- /dev/null
+++ b/spec/ruby/library/net-ftp/voidcmd_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#voidcmd" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the passed command to the server" do
+ @server.should_receive(:help).and_respond("2xx Does not raise.")
+ -> { @ftp.voidcmd("HELP") }.should_not raise_error
+ end
+
+ it "returns nil" do
+ @server.should_receive(:help).and_respond("2xx Does not raise.")
+ @ftp.voidcmd("HELP").should be_nil
+ end
+
+ it "raises a Net::FTPReplyError when the response code is 1xx" do
+ @server.should_receive(:help).and_respond("1xx Does raise a Net::FTPReplyError.")
+ -> { @ftp.voidcmd("HELP") }.should raise_error(Net::FTPReplyError)
+ end
+
+ it "raises a Net::FTPReplyError when the response code is 3xx" do
+ @server.should_receive(:help).and_respond("3xx Does raise a Net::FTPReplyError.")
+ -> { @ftp.voidcmd("HELP") }.should raise_error(Net::FTPReplyError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 4xx" do
+ @server.should_receive(:help).and_respond("4xx Does raise a Net::FTPTempError.")
+ -> { @ftp.voidcmd("HELP") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 5xx" do
+ @server.should_receive(:help).and_respond("5xx Does raise a Net::FTPPermError.")
+ -> { @ftp.voidcmd("HELP") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPProtoError when the response code is not valid" do
+ @server.should_receive(:help).and_respond("999 Does raise a Net::FTPProtoError.")
+ -> { @ftp.voidcmd("HELP") }.should raise_error(Net::FTPProtoError)
+ end
+end
diff --git a/spec/ruby/library/net-ftp/welcome_spec.rb b/spec/ruby/library/net-ftp/welcome_spec.rb
new file mode 100644
index 0000000000..4279127ce3
--- /dev/null
+++ b/spec/ruby/library/net-ftp/welcome_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#welcome" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "returns the server's welcome message" do
+ @ftp.welcome.should be_nil
+ @ftp.login
+ @ftp.welcome.should == "230 User logged in, proceed. (USER anonymous)\n"
+ end
+end
diff --git a/spec/ruby/library/net-http/HTTPBadResponse_spec.rb b/spec/ruby/library/net-http/HTTPBadResponse_spec.rb
new file mode 100644
index 0000000000..8f2e8ccfaf
--- /dev/null
+++ b/spec/ruby/library/net-http/HTTPBadResponse_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPBadResponse" do
+ it "is a subclass of StandardError" do
+ Net::HTTPBadResponse.should < StandardError
+ end
+end
diff --git a/spec/ruby/library/net-http/HTTPClientExcepton_spec.rb b/spec/ruby/library/net-http/HTTPClientExcepton_spec.rb
new file mode 100644
index 0000000000..2992e6596f
--- /dev/null
+++ b/spec/ruby/library/net-http/HTTPClientExcepton_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPClientException" do
+ it "is a subclass of Net::ProtoServerError" do
+ Net::HTTPClientException.should < Net::ProtoServerError
+ end
+
+ it "includes the Net::HTTPExceptions module" do
+ Net::HTTPClientException.should < Net::HTTPExceptions
+ end
+end
diff --git a/spec/ruby/library/net-http/HTTPError_spec.rb b/spec/ruby/library/net-http/HTTPError_spec.rb
new file mode 100644
index 0000000000..7f79eef8cf
--- /dev/null
+++ b/spec/ruby/library/net-http/HTTPError_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPError" do
+ it "is a subclass of Net::ProtocolError" do
+ Net::HTTPError.should < Net::ProtocolError
+ end
+
+ it "includes the Net::HTTPExceptions module" do
+ Net::HTTPError.should < Net::HTTPExceptions
+ end
+end
diff --git a/spec/ruby/library/net-http/HTTPFatalError_spec.rb b/spec/ruby/library/net-http/HTTPFatalError_spec.rb
new file mode 100644
index 0000000000..0113b9da2d
--- /dev/null
+++ b/spec/ruby/library/net-http/HTTPFatalError_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPFatalError" do
+ it "is a subclass of Net::ProtoFatalError" do
+ Net::HTTPFatalError.should < Net::ProtoFatalError
+ end
+
+ it "includes the Net::HTTPExceptions module" do
+ Net::HTTPFatalError.should < Net::HTTPExceptions
+ end
+end
diff --git a/spec/ruby/library/net-http/HTTPHeaderSyntaxError_spec.rb b/spec/ruby/library/net-http/HTTPHeaderSyntaxError_spec.rb
new file mode 100644
index 0000000000..b3b73ff46f
--- /dev/null
+++ b/spec/ruby/library/net-http/HTTPHeaderSyntaxError_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPHeaderSyntaxError" do
+ it "is a subclass of StandardError" do
+ Net::HTTPHeaderSyntaxError.should < StandardError
+ end
+end
diff --git a/spec/ruby/library/net-http/HTTPRetriableError_spec.rb b/spec/ruby/library/net-http/HTTPRetriableError_spec.rb
new file mode 100644
index 0000000000..677731fb68
--- /dev/null
+++ b/spec/ruby/library/net-http/HTTPRetriableError_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPRetriableError" do
+ it "is a subclass of Net::ProtoRetriableError" do
+ Net::HTTPRetriableError.should < Net::ProtoRetriableError
+ end
+
+ it "includes the Net::HTTPExceptions module" do
+ Net::HTTPRetriableError.should < Net::HTTPExceptions
+ end
+end
diff --git a/spec/ruby/library/net-http/HTTPServerException_spec.rb b/spec/ruby/library/net-http/HTTPServerException_spec.rb
new file mode 100644
index 0000000000..5e0a833fee
--- /dev/null
+++ b/spec/ruby/library/net-http/HTTPServerException_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPServerException" do
+ it "is a subclass of Net::ProtoServerError and is warned as deprecated" do
+ -> { Net::HTTPServerException.should < Net::ProtoServerError }.should complain(/warning: constant Net::HTTPServerException is deprecated/)
+ end
+
+ it "includes the Net::HTTPExceptions module and is warned as deprecated" do
+ -> { Net::HTTPServerException.should < Net::HTTPExceptions }.should complain(/warning: constant Net::HTTPServerException is deprecated/)
+ end
+end
diff --git a/spec/ruby/library/net-http/http/Proxy_spec.rb b/spec/ruby/library/net-http/http/Proxy_spec.rb
new file mode 100644
index 0000000000..a1a04fa00b
--- /dev/null
+++ b/spec/ruby/library/net-http/http/Proxy_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP.Proxy" do
+ it "returns a new subclass of Net::HTTP" do
+ Net::HTTP.Proxy("localhost").should < Net::HTTP
+ end
+
+ it "returns Net::HTTP when the passed address is nil" do
+ Net::HTTP.Proxy(nil).should == Net::HTTP
+ end
+
+ it "sets the returned subclasses' proxy options based on the passed arguments" do
+ http_with_proxy = Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks")
+ http_with_proxy.proxy_address.should == "localhost"
+ http_with_proxy.proxy_port.should eql(1234)
+ http_with_proxy.proxy_user.should == "rspec"
+ http_with_proxy.proxy_pass.should == "rocks"
+ end
+end
+
+describe "Net::HTTP#proxy?" do
+ describe "when self is no proxy class instance" do
+ it "returns false" do
+ Net::HTTP.new("localhost", 3333).proxy?.should be_false
+ end
+ end
+
+ describe "when self is a proxy class instance" do
+ it "returns false" do
+ http_with_proxy = Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks")
+ http_with_proxy.new("localhost", 3333).proxy?.should be_true
+ end
+ end
+end
diff --git a/spec/ruby/library/net-http/http/active_spec.rb b/spec/ruby/library/net-http/http/active_spec.rb
new file mode 100644
index 0000000000..c260274594
--- /dev/null
+++ b/spec/ruby/library/net-http/http/active_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+require_relative 'shared/started'
+
+describe "Net::HTTP#active?" do
+ it_behaves_like :net_http_started_p, :active?
+end
diff --git a/spec/ruby/library/net-http/http/address_spec.rb b/spec/ruby/library/net-http/http/address_spec.rb
new file mode 100644
index 0000000000..7c5b82a8f9
--- /dev/null
+++ b/spec/ruby/library/net-http/http/address_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP#address" do
+ it "returns the current host name" do
+ net = Net::HTTP.new("localhost")
+ net.address.should == "localhost"
+ end
+end
diff --git a/spec/ruby/library/net-http/http/close_on_empty_response_spec.rb b/spec/ruby/library/net-http/http/close_on_empty_response_spec.rb
new file mode 100644
index 0000000000..9cc756befb
--- /dev/null
+++ b/spec/ruby/library/net-http/http/close_on_empty_response_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP#close_on_empty_response" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "Net::HTTP#close_on_empty_response=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/net-http/http/copy_spec.rb b/spec/ruby/library/net-http/http/copy_spec.rb
new file mode 100644
index 0000000000..fba96c0f11
--- /dev/null
+++ b/spec/ruby/library/net-http/http/copy_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#copy" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends a COPY request to the passed path and returns the response" do
+ response = @http.copy("/request")
+ response.should be_kind_of(Net::HTTPResponse)
+ response.body.should == "Request type: COPY"
+ end
+end
diff --git a/spec/ruby/library/net-http/http/default_port_spec.rb b/spec/ruby/library/net-http/http/default_port_spec.rb
new file mode 100644
index 0000000000..95b7316a0c
--- /dev/null
+++ b/spec/ruby/library/net-http/http/default_port_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP.default_port" do
+ it "returns 80" do
+ Net::HTTP.http_default_port.should eql(80)
+ end
+end
diff --git a/spec/ruby/library/net-http/http/delete_spec.rb b/spec/ruby/library/net-http/http/delete_spec.rb
new file mode 100644
index 0000000000..d73aa5b375
--- /dev/null
+++ b/spec/ruby/library/net-http/http/delete_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#delete" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends a DELETE request to the passed path and returns the response" do
+ response = @http.delete("/request")
+ response.should be_kind_of(Net::HTTPResponse)
+ response.body.should == "Request type: DELETE"
+ end
+end
diff --git a/spec/ruby/library/net-http/http/finish_spec.rb b/spec/ruby/library/net-http/http/finish_spec.rb
new file mode 100644
index 0000000000..d4aa00dffe
--- /dev/null
+++ b/spec/ruby/library/net-http/http/finish_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#finish" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.new("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ describe "when self has been started" do
+ it "closes the tcp connection" do
+ @http.start
+ @http.finish
+ @http.started?.should be_false
+ end
+ end
+
+ describe "when self has not been started yet" do
+ it "raises an IOError" do
+ -> { @http.finish }.should raise_error(IOError)
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/http/fixtures/http_server.rb b/spec/ruby/library/net-http/http/fixtures/http_server.rb
index c1cedbfa76..c1cedbfa76 100644
--- a/spec/ruby/library/net/http/http/fixtures/http_server.rb
+++ b/spec/ruby/library/net-http/http/fixtures/http_server.rb
diff --git a/spec/ruby/library/net-http/http/get2_spec.rb b/spec/ruby/library/net-http/http/get2_spec.rb
new file mode 100644
index 0000000000..57c05ec64b
--- /dev/null
+++ b/spec/ruby/library/net-http/http/get2_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+require_relative 'shared/request_get'
+
+describe "Net::HTTP#get2" do
+ it_behaves_like :net_http_request_get, :get2
+end
diff --git a/spec/ruby/library/net-http/http/get_print_spec.rb b/spec/ruby/library/net-http/http/get_print_spec.rb
new file mode 100644
index 0000000000..3c24ce44ea
--- /dev/null
+++ b/spec/ruby/library/net-http/http/get_print_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP.get_print" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @port = NetHTTPSpecs.port
+ end
+
+ after :each do
+ NetHTTPSpecs.stop_server
+ end
+
+ describe "when passed URI" do
+ it "it prints the body of the specified uri to $stdout" do
+ -> do
+ Net::HTTP.get_print URI.parse("http://localhost:#{@port}/")
+ end.should output(/This is the index page\./)
+ end
+ end
+
+ describe "when passed host, path, port" do
+ it "it prints the body of the specified uri to $stdout" do
+ -> do
+ Net::HTTP.get_print 'localhost', "/", @port
+ end.should output(/This is the index page\./)
+ end
+ end
+end
diff --git a/spec/ruby/library/net-http/http/get_response_spec.rb b/spec/ruby/library/net-http/http/get_response_spec.rb
new file mode 100644
index 0000000000..7133ef8101
--- /dev/null
+++ b/spec/ruby/library/net-http/http/get_response_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP.get_response" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @port = NetHTTPSpecs.port
+ end
+
+ after :each do
+ NetHTTPSpecs.stop_server
+ end
+
+ describe "when passed URI" do
+ it "returns the response for the specified uri" do
+ res = Net::HTTP.get_response(URI.parse("http://localhost:#{@port}/"))
+ res.content_type.should == "text/plain"
+ res.body.should == "This is the index page."
+ end
+ end
+
+ describe "when passed host, path, port" do
+ it "returns the response for the specified host-path-combination" do
+ res = Net::HTTP.get_response('localhost', "/", @port)
+ res.content_type.should == "text/plain"
+ res.body.should == "This is the index page."
+ end
+ end
+end
diff --git a/spec/ruby/library/net-http/http/get_spec.rb b/spec/ruby/library/net-http/http/get_spec.rb
new file mode 100644
index 0000000000..e64a61c52c
--- /dev/null
+++ b/spec/ruby/library/net-http/http/get_spec.rb
@@ -0,0 +1,94 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP.get" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @port = NetHTTPSpecs.port
+ end
+
+ after :each do
+ NetHTTPSpecs.stop_server
+ end
+
+ describe "when passed URI" do
+ it "returns the body of the specified uri" do
+ Net::HTTP.get(URI.parse("http://localhost:#{@port}/")).should == "This is the index page."
+ end
+ end
+
+ describe "when passed host, path, port" do
+ it "returns the body of the specified host-path-combination" do
+ Net::HTTP.get('localhost', "/", @port).should == "This is the index page."
+ end
+ end
+end
+
+quarantine! do # These specs fail frequently with CHECK_LEAKS=true
+describe "Net::HTTP.get" do
+ describe "when reading gzipped contents" do
+ def start_threads
+ require 'zlib'
+ require 'stringio'
+
+ server = nil
+ server_thread = Thread.new do
+ server = TCPServer.new("127.0.0.1", 0)
+ begin
+ c = server.accept
+ ensure
+ server.close
+ end
+ c.print "HTTP/1.1 200\r\n"
+ c.print "Content-Type: text/plain\r\n"
+ c.print "Content-Encoding: gzip\r\n"
+ s = StringIO.new
+ z = Zlib::GzipWriter.new(s)
+ begin
+ z.write 'Hello World!'
+ ensure
+ z.close
+ end
+ c.print "Content-Length: #{s.length}\r\n\r\n"
+ # Write partial gzip content
+ c.write s.string.byteslice(0..-2)
+ c.flush
+ c
+ end
+ Thread.pass until server && server_thread.stop?
+
+ client_thread = Thread.new do
+ Thread.current.report_on_exception = false
+ Net::HTTP.get("127.0.0.1", '/', server.connect_address.ip_port)
+ end
+
+ socket = server_thread.value
+ Thread.pass until client_thread.stop?
+
+ [socket, client_thread]
+ end
+
+ it "propagates exceptions interrupting the thread and does not replace it with Zlib::BufError" do
+ my_exception = Class.new(RuntimeError)
+ socket, client_thread = start_threads
+ begin
+ client_thread.raise my_exception, "my exception"
+ -> { client_thread.value }.should raise_error(my_exception)
+ ensure
+ socket.close
+ end
+ end
+
+ it "lets the kill Thread exception goes through and does not replace it with Zlib::BufError" do
+ socket, client_thread = start_threads
+ begin
+ client_thread.kill
+ client_thread.value.should == nil
+ ensure
+ socket.close
+ end
+ end
+ end
+end
+end
diff --git a/spec/ruby/library/net-http/http/head2_spec.rb b/spec/ruby/library/net-http/http/head2_spec.rb
new file mode 100644
index 0000000000..84cfff33d7
--- /dev/null
+++ b/spec/ruby/library/net-http/http/head2_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+require_relative 'shared/request_head'
+
+describe "Net::HTTP#head2" do
+ it_behaves_like :net_http_request_head, :head2
+end
diff --git a/spec/ruby/library/net-http/http/head_spec.rb b/spec/ruby/library/net-http/http/head_spec.rb
new file mode 100644
index 0000000000..64621fa87b
--- /dev/null
+++ b/spec/ruby/library/net-http/http/head_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#head" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends a HEAD request to the passed path and returns the response" do
+ response = @http.head("/request")
+ # HEAD requests have no responses
+ response.body.should be_nil
+ end
+
+ it "returns a Net::HTTPResponse" do
+ @http.head("/request").should be_kind_of(Net::HTTPResponse)
+ end
+end
diff --git a/spec/ruby/library/net-http/http/http_default_port_spec.rb b/spec/ruby/library/net-http/http/http_default_port_spec.rb
new file mode 100644
index 0000000000..3b17bcd0a5
--- /dev/null
+++ b/spec/ruby/library/net-http/http/http_default_port_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP.http_default_port" do
+ it "returns 80" do
+ Net::HTTP.http_default_port.should eql(80)
+ end
+end
diff --git a/spec/ruby/library/net-http/http/https_default_port_spec.rb b/spec/ruby/library/net-http/http/https_default_port_spec.rb
new file mode 100644
index 0000000000..8c24e1d97c
--- /dev/null
+++ b/spec/ruby/library/net-http/http/https_default_port_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP.https_default_port" do
+ it "returns 443" do
+ Net::HTTP.https_default_port.should eql(443)
+ end
+end
diff --git a/spec/ruby/library/net-http/http/initialize_spec.rb b/spec/ruby/library/net-http/http/initialize_spec.rb
new file mode 100644
index 0000000000..78aa01e1aa
--- /dev/null
+++ b/spec/ruby/library/net-http/http/initialize_spec.rb
@@ -0,0 +1,46 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP#initialize" do
+ it "is private" do
+ Net::HTTP.should have_private_instance_method(:initialize)
+ end
+
+ describe "when passed address" do
+ before :each do
+ @net = Net::HTTP.allocate
+ @net.send(:initialize, "localhost")
+ end
+
+ it "sets the new Net::HTTP instance's address to the passed address" do
+ @net.address.should == "localhost"
+ end
+
+ it "sets the new Net::HTTP instance's port to the default HTTP port" do
+ @net.port.should eql(Net::HTTP.default_port)
+ end
+
+ it "does not start the new Net::HTTP instance" do
+ @net.started?.should be_false
+ end
+ end
+
+ describe "when passed address, port" do
+ before :each do
+ @net = Net::HTTP.allocate
+ @net.send(:initialize, "localhost", 3333)
+ end
+
+ it "sets the new Net::HTTP instance's address to the passed address" do
+ @net.address.should == "localhost"
+ end
+
+ it "sets the new Net::HTTP instance's port to the passed port" do
+ @net.port.should eql(3333)
+ end
+
+ it "does not start the new Net::HTTP instance" do
+ @net.started?.should be_false
+ end
+ end
+end
diff --git a/spec/ruby/library/net-http/http/inspect_spec.rb b/spec/ruby/library/net-http/http/inspect_spec.rb
new file mode 100644
index 0000000000..b8f650809e
--- /dev/null
+++ b/spec/ruby/library/net-http/http/inspect_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#inspect" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @port = NetHTTPSpecs.port
+ @http = Net::HTTP.new("localhost", @port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "returns a String representation of self" do
+ @http.inspect.should be_kind_of(String)
+ @http.inspect.should == "#<Net::HTTP localhost:#{@port} open=false>"
+
+ @http.start
+ @http.inspect.should == "#<Net::HTTP localhost:#{@port} open=true>"
+ end
+end
diff --git a/spec/ruby/library/net-http/http/is_version_1_1_spec.rb b/spec/ruby/library/net-http/http/is_version_1_1_spec.rb
new file mode 100644
index 0000000000..bdb343f9e0
--- /dev/null
+++ b/spec/ruby/library/net-http/http/is_version_1_1_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'shared/version_1_1'
+
+describe "Net::HTTP.is_version_1_1?" do
+ it_behaves_like :net_http_version_1_1_p, :is_version_1_1?
+end
diff --git a/spec/ruby/library/net-http/http/is_version_1_2_spec.rb b/spec/ruby/library/net-http/http/is_version_1_2_spec.rb
new file mode 100644
index 0000000000..555bb205dd
--- /dev/null
+++ b/spec/ruby/library/net-http/http/is_version_1_2_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'shared/version_1_2'
+
+describe "Net::HTTP.is_version_1_2?" do
+ it_behaves_like :net_http_version_1_2_p, :is_version_1_2?
+end
diff --git a/spec/ruby/library/net-http/http/lock_spec.rb b/spec/ruby/library/net-http/http/lock_spec.rb
new file mode 100644
index 0000000000..aa1f944196
--- /dev/null
+++ b/spec/ruby/library/net-http/http/lock_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#lock" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends a LOCK request to the passed path and returns the response" do
+ response = @http.lock("/request", "test=test")
+ response.should be_kind_of(Net::HTTPResponse)
+ response.body.should == "Request type: LOCK"
+ end
+end
diff --git a/spec/ruby/library/net-http/http/mkcol_spec.rb b/spec/ruby/library/net-http/http/mkcol_spec.rb
new file mode 100644
index 0000000000..f8009f9059
--- /dev/null
+++ b/spec/ruby/library/net-http/http/mkcol_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#mkcol" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends a MKCOL request to the passed path and returns the response" do
+ response = @http.mkcol("/request")
+ response.should be_kind_of(Net::HTTPResponse)
+ response.body.should == "Request type: MKCOL"
+ end
+end
diff --git a/spec/ruby/library/net-http/http/move_spec.rb b/spec/ruby/library/net-http/http/move_spec.rb
new file mode 100644
index 0000000000..ae43016a2c
--- /dev/null
+++ b/spec/ruby/library/net-http/http/move_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#head" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends a MOVE request to the passed path and returns the response" do
+ response = @http.move("/request")
+ # HEAD requests have no responses
+ response.body.should == "Request type: MOVE"
+ end
+
+ it "returns a Net::HTTPResponse" do
+ @http.move("/request").should be_kind_of(Net::HTTPResponse)
+ end
+end
diff --git a/spec/ruby/library/net-http/http/new_spec.rb b/spec/ruby/library/net-http/http/new_spec.rb
new file mode 100644
index 0000000000..1ec6bbd0c0
--- /dev/null
+++ b/spec/ruby/library/net-http/http/new_spec.rb
@@ -0,0 +1,86 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP.new" do
+ describe "when passed address" do
+ before :each do
+ @http = Net::HTTP.new("localhost")
+ end
+
+ it "returns a Net::HTTP instance" do
+ @http.proxy?.should be_false
+ @http.instance_of?(Net::HTTP).should be_true
+ end
+
+ it "sets the new Net::HTTP instance's address to the passed address" do
+ @http.address.should == "localhost"
+ end
+
+ it "sets the new Net::HTTP instance's port to the default HTTP port" do
+ @http.port.should eql(Net::HTTP.default_port)
+ end
+
+ it "does not start the new Net::HTTP instance" do
+ @http.started?.should be_false
+ end
+ end
+
+ describe "when passed address, port" do
+ before :each do
+ @http = Net::HTTP.new("localhost", 3333)
+ end
+
+ it "returns a Net::HTTP instance" do
+ @http.proxy?.should be_false
+ @http.instance_of?(Net::HTTP).should be_true
+ end
+
+ it "sets the new Net::HTTP instance's address to the passed address" do
+ @http.address.should == "localhost"
+ end
+
+ it "sets the new Net::HTTP instance's port to the passed port" do
+ @http.port.should eql(3333)
+ end
+
+ it "does not start the new Net::HTTP instance" do
+ @http.started?.should be_false
+ end
+ end
+
+ describe "when passed address, port, *proxy_options" do
+ it "returns a Net::HTTP instance" do
+ http = Net::HTTP.new("localhost", 3333, "localhost")
+ http.proxy?.should be_true
+ http.instance_of?(Net::HTTP).should be_true
+ http.should be_kind_of(Net::HTTP)
+ end
+
+ it "correctly sets the passed Proxy options" do
+ http = Net::HTTP.new("localhost", 3333, "localhost")
+ http.proxy_address.should == "localhost"
+ http.proxy_port.should eql(80)
+ http.proxy_user.should be_nil
+ http.proxy_pass.should be_nil
+
+ http = Net::HTTP.new("localhost", 3333, "localhost", 1234)
+ http.proxy_address.should == "localhost"
+ http.proxy_port.should eql(1234)
+ http.proxy_user.should be_nil
+ http.proxy_pass.should be_nil
+
+ http = Net::HTTP.new("localhost", 3333, "localhost", 1234, "rubyspec")
+ http.proxy_address.should == "localhost"
+ http.proxy_port.should eql(1234)
+ http.proxy_user.should == "rubyspec"
+ http.proxy_pass.should be_nil
+
+ http = Net::HTTP.new("localhost", 3333, "localhost", 1234, "rubyspec", "rocks")
+ http.proxy_address.should == "localhost"
+ http.proxy_port.should eql(1234)
+ http.proxy_user.should == "rubyspec"
+ http.proxy_pass.should == "rocks"
+ end
+ end
+
+end
diff --git a/spec/ruby/library/net-http/http/newobj_spec.rb b/spec/ruby/library/net-http/http/newobj_spec.rb
new file mode 100644
index 0000000000..e19b30fca9
--- /dev/null
+++ b/spec/ruby/library/net-http/http/newobj_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP.newobj" do
+ before :each do
+ @net = Net::HTTP.newobj("localhost")
+ end
+
+ describe "when passed address" do
+ it "returns a new Net::HTTP instance" do
+ @net.should be_kind_of(Net::HTTP)
+ end
+
+ it "sets the new Net::HTTP instance's address to the passed address" do
+ @net.address.should == "localhost"
+ end
+
+ it "sets the new Net::HTTP instance's port to the default HTTP port" do
+ @net.port.should eql(Net::HTTP.default_port)
+ end
+
+ it "does not start the new Net::HTTP instance" do
+ @net.started?.should be_false
+ end
+ end
+
+ describe "when passed address, port" do
+ before :each do
+ @net = Net::HTTP.newobj("localhost", 3333)
+ end
+
+ it "returns a new Net::HTTP instance" do
+ @net.should be_kind_of(Net::HTTP)
+ end
+
+ it "sets the new Net::HTTP instance's address to the passed address" do
+ @net.address.should == "localhost"
+ end
+
+ it "sets the new Net::HTTP instance's port to the passed port" do
+ @net.port.should eql(3333)
+ end
+
+ it "does not start the new Net::HTTP instance" do
+ @net.started?.should be_false
+ end
+ end
+end
diff --git a/spec/ruby/library/net-http/http/open_timeout_spec.rb b/spec/ruby/library/net-http/http/open_timeout_spec.rb
new file mode 100644
index 0000000000..0d93752271
--- /dev/null
+++ b/spec/ruby/library/net-http/http/open_timeout_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP#open_timeout" do
+ it "returns the seconds to wait till the connection is open" do
+ net = Net::HTTP.new("localhost")
+ net.open_timeout.should eql(60)
+ net.open_timeout = 10
+ net.open_timeout.should eql(10)
+ end
+end
+
+describe "Net::HTTP#open_timeout=" do
+ it "sets the seconds to wait till the connection is open" do
+ net = Net::HTTP.new("localhost")
+ net.open_timeout = 10
+ net.open_timeout.should eql(10)
+ end
+
+ it "returns the newly set value" do
+ net = Net::HTTP.new("localhost")
+ (net.open_timeout = 10).should eql(10)
+ end
+end
diff --git a/spec/ruby/library/net-http/http/options_spec.rb b/spec/ruby/library/net-http/http/options_spec.rb
new file mode 100644
index 0000000000..3d9887a557
--- /dev/null
+++ b/spec/ruby/library/net-http/http/options_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#options" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends an options request to the passed path and returns the response" do
+ response = @http.options("/request")
+
+ response.body.should == "Request type: OPTIONS"
+ end
+
+ it "returns a Net::HTTPResponse" do
+ @http.options("/request").should be_kind_of(Net::HTTPResponse)
+ end
+end
diff --git a/spec/ruby/library/net-http/http/port_spec.rb b/spec/ruby/library/net-http/http/port_spec.rb
new file mode 100644
index 0000000000..0984d5e6ce
--- /dev/null
+++ b/spec/ruby/library/net-http/http/port_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP#port" do
+ it "returns the current port number" do
+ net = Net::HTTP.new("localhost", 3333)
+ net.port.should eql(3333)
+ end
+end
diff --git a/spec/ruby/library/net-http/http/post2_spec.rb b/spec/ruby/library/net-http/http/post2_spec.rb
new file mode 100644
index 0000000000..abc998709f
--- /dev/null
+++ b/spec/ruby/library/net-http/http/post2_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+require_relative 'shared/request_post'
+
+describe "Net::HTTP#post2" do
+ it_behaves_like :net_http_request_post, :post2
+end
diff --git a/spec/ruby/library/net-http/http/post_form_spec.rb b/spec/ruby/library/net-http/http/post_form_spec.rb
new file mode 100644
index 0000000000..de64a25bae
--- /dev/null
+++ b/spec/ruby/library/net-http/http/post_form_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP.post_form when passed URI" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @port = NetHTTPSpecs.port
+ end
+
+ after :each do
+ NetHTTPSpecs.stop_server
+ end
+
+ it "POSTs the passed form data to the given uri" do
+ uri = URI.parse("http://localhost:#{@port}/request/body")
+ data = { test: :data }
+
+ res = Net::HTTP.post_form(uri, data)
+ res.body.should == "test=data"
+ end
+end
diff --git a/spec/ruby/library/net-http/http/post_spec.rb b/spec/ruby/library/net-http/http/post_spec.rb
new file mode 100644
index 0000000000..d7d94fec4a
--- /dev/null
+++ b/spec/ruby/library/net-http/http/post_spec.rb
@@ -0,0 +1,74 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require 'uri'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP.post" do
+ before :each do
+ NetHTTPSpecs.start_server
+ end
+
+ after :each do
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends post request to the specified URI and returns response" do
+ response = Net::HTTP.post(
+ URI("http://localhost:#{NetHTTPSpecs.port}/request"),
+ '{ "q": "ruby", "max": "50" }',
+ "Content-Type" => "application/json")
+ response.body.should == "Request type: POST"
+ end
+
+ it "returns a Net::HTTPResponse" do
+ response = Net::HTTP.post(URI("http://localhost:#{NetHTTPSpecs.port}/request"), "test=test")
+ response.should be_kind_of(Net::HTTPResponse)
+ end
+
+ it "sends Content-Type: application/x-www-form-urlencoded by default" do
+ response = Net::HTTP.post(URI("http://localhost:#{NetHTTPSpecs.port}/request/header"), "test=test")
+ response.body.should include('"Content-Type"=>"application/x-www-form-urlencoded"')
+ end
+
+ it "does not support HTTP Basic Auth" do
+ response = Net::HTTP.post(
+ URI("http://john:qwerty@localhost:#{NetHTTPSpecs.port}/request/basic_auth"),
+ "test=test")
+ response.body.should == "username: \npassword: "
+ end
+end
+
+describe "Net::HTTP#post" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends an post request to the passed path and returns the response" do
+ response = @http.post("/request", "test=test")
+ response.body.should == "Request type: POST"
+ end
+
+ it "returns a Net::HTTPResponse" do
+ @http.post("/request", "test=test").should be_kind_of(Net::HTTPResponse)
+ end
+
+ describe "when passed a block" do
+ it "yields fragments of the response body to the passed block" do
+ str = +""
+ @http.post("/request", "test=test") do |res|
+ str << res
+ end
+ str.should == "Request type: POST"
+ end
+
+ it "returns a Net::HTTPResponse" do
+ @http.post("/request", "test=test") {}.should be_kind_of(Net::HTTPResponse)
+ end
+ end
+end
diff --git a/spec/ruby/library/net-http/http/propfind_spec.rb b/spec/ruby/library/net-http/http/propfind_spec.rb
new file mode 100644
index 0000000000..f3742d1b1a
--- /dev/null
+++ b/spec/ruby/library/net-http/http/propfind_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#propfind" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends an propfind request to the passed path and returns the response" do
+ response = @http.propfind("/request", "test=test")
+ response.body.should == "Request type: PROPFIND"
+ end
+
+ it "returns a Net::HTTPResponse" do
+ @http.propfind("/request", "test=test").should be_kind_of(Net::HTTPResponse)
+ end
+end
diff --git a/spec/ruby/library/net-http/http/proppatch_spec.rb b/spec/ruby/library/net-http/http/proppatch_spec.rb
new file mode 100644
index 0000000000..0163d24d46
--- /dev/null
+++ b/spec/ruby/library/net-http/http/proppatch_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#proppatch" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends an proppatch request to the passed path and returns the response" do
+ response = @http.proppatch("/request", "test=test")
+ response.body.should == "Request type: PROPPATCH"
+ end
+
+ it "returns a Net::HTTPResponse" do
+ @http.proppatch("/request", "test=test").should be_kind_of(Net::HTTPResponse)
+ end
+end
diff --git a/spec/ruby/library/net-http/http/proxy_address_spec.rb b/spec/ruby/library/net-http/http/proxy_address_spec.rb
new file mode 100644
index 0000000000..5b5efb7ac0
--- /dev/null
+++ b/spec/ruby/library/net-http/http/proxy_address_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP.proxy_address" do
+ describe "when self is no proxy class" do
+ it "returns nil" do
+ Net::HTTP.proxy_address.should be_nil
+ end
+ end
+
+ describe "when self is a proxy class" do
+ it "returns the address for self's proxy connection" do
+ Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks").proxy_address.should == "localhost"
+ end
+ end
+end
+
+describe "Net::HTTP#proxy_address" do
+ describe "when self is no proxy class instance" do
+ it "returns nil" do
+ Net::HTTP.new("localhost", 3333).proxy_address.should be_nil
+ end
+ end
+
+ describe "when self is a proxy class instance" do
+ it "returns the password for self's proxy connection" do
+ http_with_proxy = Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks")
+ http_with_proxy.new("localhost", 3333).proxy_address.should == "localhost"
+ end
+ end
+end
diff --git a/spec/ruby/library/net-http/http/proxy_class_spec.rb b/spec/ruby/library/net-http/http/proxy_class_spec.rb
new file mode 100644
index 0000000000..00975aef4e
--- /dev/null
+++ b/spec/ruby/library/net-http/http/proxy_class_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP.proxy_class?" do
+ it "returns true if self is a class created with Net::HTTP.Proxy" do
+ Net::HTTP.proxy_class?.should be_false
+ Net::HTTP.Proxy("localhost").proxy_class?.should be_true
+ end
+end
diff --git a/spec/ruby/library/net-http/http/proxy_pass_spec.rb b/spec/ruby/library/net-http/http/proxy_pass_spec.rb
new file mode 100644
index 0000000000..4e393a53ff
--- /dev/null
+++ b/spec/ruby/library/net-http/http/proxy_pass_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP.proxy_pass" do
+ describe "when self is no proxy class" do
+ it "returns nil" do
+ Net::HTTP.proxy_pass.should be_nil
+ end
+ end
+
+ describe "when self is a proxy class" do
+ it "returns nil if no password was set for self's proxy connection" do
+ Net::HTTP.Proxy("localhost").proxy_pass.should be_nil
+ end
+
+ it "returns the password for self's proxy connection" do
+ Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks").proxy_pass.should == "rocks"
+ end
+ end
+end
+
+describe "Net::HTTP#proxy_pass" do
+ describe "when self is no proxy class instance" do
+ it "returns nil" do
+ Net::HTTP.new("localhost", 3333).proxy_pass.should be_nil
+ end
+ end
+
+ describe "when self is a proxy class instance" do
+ it "returns nil if no password was set for self's proxy connection" do
+ Net::HTTP.Proxy("localhost").new("localhost", 3333).proxy_pass.should be_nil
+ end
+
+ it "returns the password for self's proxy connection" do
+ http_with_proxy = Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks")
+ http_with_proxy.new("localhost", 3333).proxy_pass.should == "rocks"
+ end
+ end
+end
diff --git a/spec/ruby/library/net-http/http/proxy_port_spec.rb b/spec/ruby/library/net-http/http/proxy_port_spec.rb
new file mode 100644
index 0000000000..d7d37f3927
--- /dev/null
+++ b/spec/ruby/library/net-http/http/proxy_port_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP.proxy_port" do
+ describe "when self is no proxy class" do
+ it "returns nil" do
+ Net::HTTP.proxy_port.should be_nil
+ end
+ end
+
+ describe "when self is a proxy class" do
+ it "returns 80 if no port was set for self's proxy connection" do
+ Net::HTTP.Proxy("localhost").proxy_port.should eql(80)
+ end
+
+ it "returns the port for self's proxy connection" do
+ Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks").proxy_port.should eql(1234)
+ end
+ end
+end
+
+describe "Net::HTTP#proxy_port" do
+ describe "when self is no proxy class instance" do
+ it "returns nil" do
+ Net::HTTP.new("localhost", 3333).proxy_port.should be_nil
+ end
+ end
+
+ describe "when self is a proxy class instance" do
+ it "returns 80 if no port was set for self's proxy connection" do
+ Net::HTTP.Proxy("localhost").new("localhost", 3333).proxy_port.should eql(80)
+ end
+
+ it "returns the port for self's proxy connection" do
+ http_with_proxy = Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks")
+ http_with_proxy.new("localhost", 3333).proxy_port.should eql(1234)
+ end
+ end
+end
diff --git a/spec/ruby/library/net-http/http/proxy_user_spec.rb b/spec/ruby/library/net-http/http/proxy_user_spec.rb
new file mode 100644
index 0000000000..ef7654425d
--- /dev/null
+++ b/spec/ruby/library/net-http/http/proxy_user_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP.proxy_user" do
+ describe "when self is no proxy class" do
+ it "returns nil" do
+ Net::HTTP.proxy_user.should be_nil
+ end
+ end
+
+ describe "when self is a proxy class" do
+ it "returns nil if no username was set for self's proxy connection" do
+ Net::HTTP.Proxy("localhost").proxy_user.should be_nil
+ end
+
+ it "returns the username for self's proxy connection" do
+ Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks").proxy_user.should == "rspec"
+ end
+ end
+end
+
+describe "Net::HTTP#proxy_user" do
+ describe "when self is no proxy class instance" do
+ it "returns nil" do
+ Net::HTTP.new("localhost", 3333).proxy_user.should be_nil
+ end
+ end
+
+ describe "when self is a proxy class instance" do
+ it "returns nil if no username was set for self's proxy connection" do
+ Net::HTTP.Proxy("localhost").new("localhost", 3333).proxy_user.should be_nil
+ end
+
+ it "returns the username for self's proxy connection" do
+ http_with_proxy = Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks")
+ http_with_proxy.new("localhost", 3333).proxy_user.should == "rspec"
+ end
+ end
+end
diff --git a/spec/ruby/library/net-http/http/put2_spec.rb b/spec/ruby/library/net-http/http/put2_spec.rb
new file mode 100644
index 0000000000..7b03a39d0b
--- /dev/null
+++ b/spec/ruby/library/net-http/http/put2_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+require_relative 'shared/request_put'
+
+describe "Net::HTTP#put2" do
+ it_behaves_like :net_http_request_put, :put2
+end
diff --git a/spec/ruby/library/net-http/http/put_spec.rb b/spec/ruby/library/net-http/http/put_spec.rb
new file mode 100644
index 0000000000..75f3c243d4
--- /dev/null
+++ b/spec/ruby/library/net-http/http/put_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#put" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends an put request to the passed path and returns the response" do
+ response = @http.put("/request", "test=test")
+ response.body.should == "Request type: PUT"
+ end
+
+ it "returns a Net::HTTPResponse" do
+ @http.put("/request", "test=test").should be_kind_of(Net::HTTPResponse)
+ end
+end
diff --git a/spec/ruby/library/net-http/http/read_timeout_spec.rb b/spec/ruby/library/net-http/http/read_timeout_spec.rb
new file mode 100644
index 0000000000..7a0d2f1d72
--- /dev/null
+++ b/spec/ruby/library/net-http/http/read_timeout_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP#read_timeout" do
+ it "returns the seconds to wait until reading one block" do
+ net = Net::HTTP.new("localhost")
+ net.read_timeout.should eql(60)
+ net.read_timeout = 10
+ net.read_timeout.should eql(10)
+ end
+end
+
+describe "Net::HTTP#read_timeout=" do
+ it "sets the seconds to wait till the connection is open" do
+ net = Net::HTTP.new("localhost")
+ net.read_timeout = 10
+ net.read_timeout.should eql(10)
+ end
+
+ it "returns the newly set value" do
+ net = Net::HTTP.new("localhost")
+ (net.read_timeout = 10).should eql(10)
+ end
+end
diff --git a/spec/ruby/library/net-http/http/request_get_spec.rb b/spec/ruby/library/net-http/http/request_get_spec.rb
new file mode 100644
index 0000000000..98025a14a1
--- /dev/null
+++ b/spec/ruby/library/net-http/http/request_get_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+require_relative 'shared/request_get'
+
+describe "Net::HTTP#request_get" do
+ it_behaves_like :net_http_request_get, :get2
+end
diff --git a/spec/ruby/library/net-http/http/request_head_spec.rb b/spec/ruby/library/net-http/http/request_head_spec.rb
new file mode 100644
index 0000000000..8f514d4eee
--- /dev/null
+++ b/spec/ruby/library/net-http/http/request_head_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+require_relative 'shared/request_head'
+
+describe "Net::HTTP#request_head" do
+ it_behaves_like :net_http_request_head, :request_head
+end
diff --git a/spec/ruby/library/net-http/http/request_post_spec.rb b/spec/ruby/library/net-http/http/request_post_spec.rb
new file mode 100644
index 0000000000..719bd5a7ee
--- /dev/null
+++ b/spec/ruby/library/net-http/http/request_post_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+require_relative 'shared/request_post'
+
+describe "Net::HTTP#request_post" do
+ it_behaves_like :net_http_request_post, :request_post
+end
diff --git a/spec/ruby/library/net-http/http/request_put_spec.rb b/spec/ruby/library/net-http/http/request_put_spec.rb
new file mode 100644
index 0000000000..9fcf3a98d6
--- /dev/null
+++ b/spec/ruby/library/net-http/http/request_put_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+require_relative 'shared/request_put'
+
+describe "Net::HTTP#request_put" do
+ it_behaves_like :net_http_request_put, :request_put
+end
diff --git a/spec/ruby/library/net-http/http/request_spec.rb b/spec/ruby/library/net-http/http/request_spec.rb
new file mode 100644
index 0000000000..356e605b3b
--- /dev/null
+++ b/spec/ruby/library/net-http/http/request_spec.rb
@@ -0,0 +1,109 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#request" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ describe "when passed request_object" do
+ it "makes a HTTP Request based on the passed request_object" do
+ response = @http.request(Net::HTTP::Get.new("/request"), "test=test")
+ response.body.should == "Request type: GET"
+
+ response = @http.request(Net::HTTP::Head.new("/request"), "test=test")
+ response.body.should be_nil
+
+ response = @http.request(Net::HTTP::Post.new("/request"), "test=test")
+ response.body.should == "Request type: POST"
+
+ response = @http.request(Net::HTTP::Put.new("/request"), "test=test")
+ response.body.should == "Request type: PUT"
+
+ response = @http.request(Net::HTTP::Proppatch.new("/request"), "test=test")
+ response.body.should == "Request type: PROPPATCH"
+
+ response = @http.request(Net::HTTP::Lock.new("/request"), "test=test")
+ response.body.should == "Request type: LOCK"
+
+ response = @http.request(Net::HTTP::Unlock.new("/request"), "test=test")
+ response.body.should == "Request type: UNLOCK"
+
+ # TODO: Does not work?
+ #response = @http.request(Net::HTTP::Options.new("/request"), "test=test")
+ #response.body.should be_nil
+
+ response = @http.request(Net::HTTP::Propfind.new("/request"), "test=test")
+ response.body.should == "Request type: PROPFIND"
+
+ response = @http.request(Net::HTTP::Delete.new("/request"), "test=test")
+ response.body.should == "Request type: DELETE"
+
+ response = @http.request(Net::HTTP::Move.new("/request"), "test=test")
+ response.body.should == "Request type: MOVE"
+
+ response = @http.request(Net::HTTP::Copy.new("/request"), "test=test")
+ response.body.should == "Request type: COPY"
+
+ response = @http.request(Net::HTTP::Mkcol.new("/request"), "test=test")
+ response.body.should == "Request type: MKCOL"
+
+ response = @http.request(Net::HTTP::Trace.new("/request"), "test=test")
+ response.body.should == "Request type: TRACE"
+ end
+ end
+
+ describe "when passed request_object and request_body" do
+ it "sends the passed request_body when making the HTTP Request" do
+ response = @http.request(Net::HTTP::Get.new("/request/body"), "test=test")
+ response.body.should == "test=test"
+
+ response = @http.request(Net::HTTP::Head.new("/request/body"), "test=test")
+ response.body.should be_nil
+
+ response = @http.request(Net::HTTP::Post.new("/request/body"), "test=test")
+ response.body.should == "test=test"
+
+ response = @http.request(Net::HTTP::Put.new("/request/body"), "test=test")
+ response.body.should == "test=test"
+
+ response = @http.request(Net::HTTP::Proppatch.new("/request/body"), "test=test")
+ response.body.should == "test=test"
+
+ response = @http.request(Net::HTTP::Lock.new("/request/body"), "test=test")
+ response.body.should == "test=test"
+
+ response = @http.request(Net::HTTP::Unlock.new("/request/body"), "test=test")
+ response.body.should == "test=test"
+
+ # TODO: Does not work?
+ #response = @http.request(Net::HTTP::Options.new("/request/body"), "test=test")
+ #response.body.should be_nil
+
+ response = @http.request(Net::HTTP::Propfind.new("/request/body"), "test=test")
+ response.body.should == "test=test"
+
+ response = @http.request(Net::HTTP::Delete.new("/request/body"), "test=test")
+ response.body.should == "test=test"
+
+ response = @http.request(Net::HTTP::Move.new("/request/body"), "test=test")
+ response.body.should == "test=test"
+
+ response = @http.request(Net::HTTP::Copy.new("/request/body"), "test=test")
+ response.body.should == "test=test"
+
+ response = @http.request(Net::HTTP::Mkcol.new("/request/body"), "test=test")
+ response.body.should == "test=test"
+
+ response = @http.request(Net::HTTP::Trace.new("/request/body"), "test=test")
+ response.body.should == "test=test"
+ end
+ end
+end
diff --git a/spec/ruby/library/net-http/http/request_types_spec.rb b/spec/ruby/library/net-http/http/request_types_spec.rb
new file mode 100644
index 0000000000..53aef1ee58
--- /dev/null
+++ b/spec/ruby/library/net-http/http/request_types_spec.rb
@@ -0,0 +1,254 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP::Get" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Get.should < Net::HTTPRequest
+ end
+
+ it "represents the 'GET'-Request-Method" do
+ Net::HTTP::Get::METHOD.should == "GET"
+ end
+
+ it "has no Request Body" do
+ Net::HTTP::Get::REQUEST_HAS_BODY.should be_false
+ end
+
+ it "has a Response Body" do
+ Net::HTTP::Get::RESPONSE_HAS_BODY.should be_true
+ end
+end
+
+describe "Net::HTTP::Head" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Head.should < Net::HTTPRequest
+ end
+
+ it "represents the 'HEAD'-Request-Method" do
+ Net::HTTP::Head::METHOD.should == "HEAD"
+ end
+
+ it "has no Request Body" do
+ Net::HTTP::Head::REQUEST_HAS_BODY.should be_false
+ end
+
+ it "has no Response Body" do
+ Net::HTTP::Head::RESPONSE_HAS_BODY.should be_false
+ end
+end
+
+describe "Net::HTTP::Post" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Post.should < Net::HTTPRequest
+ end
+
+ it "represents the 'POST'-Request-Method" do
+ Net::HTTP::Post::METHOD.should == "POST"
+ end
+
+ it "has a Request Body" do
+ Net::HTTP::Post::REQUEST_HAS_BODY.should be_true
+ end
+
+ it "has a Response Body" do
+ Net::HTTP::Post::RESPONSE_HAS_BODY.should be_true
+ end
+end
+
+describe "Net::HTTP::Put" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Put.should < Net::HTTPRequest
+ end
+
+ it "represents the 'PUT'-Request-Method" do
+ Net::HTTP::Put::METHOD.should == "PUT"
+ end
+
+ it "has a Request Body" do
+ Net::HTTP::Put::REQUEST_HAS_BODY.should be_true
+ end
+
+ it "has a Response Body" do
+ Net::HTTP::Put::RESPONSE_HAS_BODY.should be_true
+ end
+end
+
+describe "Net::HTTP::Delete" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Delete.should < Net::HTTPRequest
+ end
+
+ it "represents the 'DELETE'-Request-Method" do
+ Net::HTTP::Delete::METHOD.should == "DELETE"
+ end
+
+ it "has no Request Body" do
+ Net::HTTP::Delete::REQUEST_HAS_BODY.should be_false
+ end
+
+ it "has a Response Body" do
+ Net::HTTP::Delete::RESPONSE_HAS_BODY.should be_true
+ end
+end
+
+describe "Net::HTTP::Options" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Options.should < Net::HTTPRequest
+ end
+
+ it "represents the 'OPTIONS'-Request-Method" do
+ Net::HTTP::Options::METHOD.should == "OPTIONS"
+ end
+
+ it "has no Request Body" do
+ Net::HTTP::Options::REQUEST_HAS_BODY.should be_false
+ end
+
+ it "has no Response Body" do
+ Net::HTTP::Options::RESPONSE_HAS_BODY.should be_true
+ end
+end
+
+describe "Net::HTTP::Trace" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Trace.should < Net::HTTPRequest
+ end
+
+ it "represents the 'TRACE'-Request-Method" do
+ Net::HTTP::Trace::METHOD.should == "TRACE"
+ end
+
+ it "has no Request Body" do
+ Net::HTTP::Trace::REQUEST_HAS_BODY.should be_false
+ end
+
+ it "has a Response Body" do
+ Net::HTTP::Trace::RESPONSE_HAS_BODY.should be_true
+ end
+end
+
+describe "Net::HTTP::Propfind" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Propfind.should < Net::HTTPRequest
+ end
+
+ it "represents the 'PROPFIND'-Request-Method" do
+ Net::HTTP::Propfind::METHOD.should == "PROPFIND"
+ end
+
+ it "has a Request Body" do
+ Net::HTTP::Propfind::REQUEST_HAS_BODY.should be_true
+ end
+
+ it "has a Response Body" do
+ Net::HTTP::Propfind::RESPONSE_HAS_BODY.should be_true
+ end
+end
+
+describe "Net::HTTP::Proppatch" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Proppatch.should < Net::HTTPRequest
+ end
+
+ it "represents the 'PROPPATCH'-Request-Method" do
+ Net::HTTP::Proppatch::METHOD.should == "PROPPATCH"
+ end
+
+ it "has a Request Body" do
+ Net::HTTP::Proppatch::REQUEST_HAS_BODY.should be_true
+ end
+
+ it "has a Response Body" do
+ Net::HTTP::Proppatch::RESPONSE_HAS_BODY.should be_true
+ end
+end
+
+describe "Net::HTTP::Mkcol" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Mkcol.should < Net::HTTPRequest
+ end
+
+ it "represents the 'MKCOL'-Request-Method" do
+ Net::HTTP::Mkcol::METHOD.should == "MKCOL"
+ end
+
+ it "has a Request Body" do
+ Net::HTTP::Mkcol::REQUEST_HAS_BODY.should be_true
+ end
+
+ it "has a Response Body" do
+ Net::HTTP::Mkcol::RESPONSE_HAS_BODY.should be_true
+ end
+end
+
+describe "Net::HTTP::Copy" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Copy.should < Net::HTTPRequest
+ end
+
+ it "represents the 'COPY'-Request-Method" do
+ Net::HTTP::Copy::METHOD.should == "COPY"
+ end
+
+ it "has no Request Body" do
+ Net::HTTP::Copy::REQUEST_HAS_BODY.should be_false
+ end
+
+ it "has a Response Body" do
+ Net::HTTP::Copy::RESPONSE_HAS_BODY.should be_true
+ end
+end
+
+describe "Net::HTTP::Move" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Move.should < Net::HTTPRequest
+ end
+
+ it "represents the 'MOVE'-Request-Method" do
+ Net::HTTP::Move::METHOD.should == "MOVE"
+ end
+
+ it "has no Request Body" do
+ Net::HTTP::Move::REQUEST_HAS_BODY.should be_false
+ end
+
+ it "has a Response Body" do
+ Net::HTTP::Move::RESPONSE_HAS_BODY.should be_true
+ end
+end
+
+describe "Net::HTTP::Lock" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Lock.should < Net::HTTPRequest
+ end
+
+ it "represents the 'LOCK'-Request-Method" do
+ Net::HTTP::Lock::METHOD.should == "LOCK"
+ end
+
+ it "has a Request Body" do
+ Net::HTTP::Lock::REQUEST_HAS_BODY.should be_true
+ end
+
+ it "has a Response Body" do
+ Net::HTTP::Lock::RESPONSE_HAS_BODY.should be_true
+ end
+end
+
+describe "Net::HTTP::Unlock" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Unlock.should < Net::HTTPRequest
+ end
+
+ it "represents the 'UNLOCK'-Request-Method" do
+ Net::HTTP::Unlock::METHOD.should == "UNLOCK"
+ end
+
+ it "has a Request Body" do
+ Net::HTTP::Unlock::REQUEST_HAS_BODY.should be_true
+ end
+
+ it "has a Response Body" do
+ Net::HTTP::Unlock::RESPONSE_HAS_BODY.should be_true
+ end
+end
diff --git a/spec/ruby/library/net-http/http/send_request_spec.rb b/spec/ruby/library/net-http/http/send_request_spec.rb
new file mode 100644
index 0000000000..e82b2a96a1
--- /dev/null
+++ b/spec/ruby/library/net-http/http/send_request_spec.rb
@@ -0,0 +1,61 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#send_request" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+
+ # HEAD is special so handled separately
+ @methods = %w[
+ GET POST PUT DELETE
+ OPTIONS
+ PROPFIND PROPPATCH LOCK UNLOCK
+ ]
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ # TODO: Does only work with GET and POST requests
+ describe "when passed type, path" do
+ it "sends a HTTP Request of the passed type to the passed path" do
+ response = @http.send_request("HEAD", "/request")
+ response.body.should be_nil
+
+ (@methods - %w[POST PUT]).each do |method|
+ response = @http.send_request(method, "/request")
+ response.body.should == "Request type: #{method}"
+ end
+ end
+ end
+
+ describe "when passed type, path, body" do
+ it "sends a HTTP Request with the passed body" do
+ response = @http.send_request("HEAD", "/request/body", "test=test")
+ response.body.should be_nil
+
+ @methods.each do |method|
+ response = @http.send_request(method, "/request/body", "test=test")
+ response.body.should == "test=test"
+ end
+ end
+ end
+
+ describe "when passed type, path, body, headers" do
+ it "sends a HTTP Request with the passed headers" do
+ referer = 'https://www.ruby-lang.org/'.freeze
+
+ response = @http.send_request("HEAD", "/request/header", "test=test", "referer" => referer)
+ response.body.should be_nil
+
+ @methods.each do |method|
+ response = @http.send_request(method, "/request/header", "test=test", "referer" => referer)
+ response.body.should include('"Referer"=>"' + referer + '"')
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/net-http/http/set_debug_output_spec.rb b/spec/ruby/library/net-http/http/set_debug_output_spec.rb
new file mode 100644
index 0000000000..5ceecb39fb
--- /dev/null
+++ b/spec/ruby/library/net-http/http/set_debug_output_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require "stringio"
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#set_debug_output when passed io" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.new("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sets the passed io as output stream for debugging" do
+ io = StringIO.new
+
+ @http.set_debug_output(io)
+ @http.start
+ io.string.should_not be_empty
+ size = io.string.size
+
+ @http.get("/")
+ io.string.size.should > size
+ end
+
+ it "outputs a warning when the connection has already been started" do
+ @http.start
+ -> { @http.set_debug_output(StringIO.new) }.should complain(/Net::HTTP#set_debug_output called after HTTP started/)
+ end
+end
diff --git a/spec/ruby/library/net/http/http/shared/request_get.rb b/spec/ruby/library/net-http/http/shared/request_get.rb
index d25f32049b..d25f32049b 100644
--- a/spec/ruby/library/net/http/http/shared/request_get.rb
+++ b/spec/ruby/library/net-http/http/shared/request_get.rb
diff --git a/spec/ruby/library/net/http/http/shared/request_head.rb b/spec/ruby/library/net-http/http/shared/request_head.rb
index 78b555884b..78b555884b 100644
--- a/spec/ruby/library/net/http/http/shared/request_head.rb
+++ b/spec/ruby/library/net-http/http/shared/request_head.rb
diff --git a/spec/ruby/library/net/http/http/shared/request_post.rb b/spec/ruby/library/net-http/http/shared/request_post.rb
index e832411c48..e832411c48 100644
--- a/spec/ruby/library/net/http/http/shared/request_post.rb
+++ b/spec/ruby/library/net-http/http/shared/request_post.rb
diff --git a/spec/ruby/library/net/http/http/shared/request_put.rb b/spec/ruby/library/net-http/http/shared/request_put.rb
index 3b902f4957..3b902f4957 100644
--- a/spec/ruby/library/net/http/http/shared/request_put.rb
+++ b/spec/ruby/library/net-http/http/shared/request_put.rb
diff --git a/spec/ruby/library/net/http/http/shared/started.rb b/spec/ruby/library/net-http/http/shared/started.rb
index 9ff6272c31..9ff6272c31 100644
--- a/spec/ruby/library/net/http/http/shared/started.rb
+++ b/spec/ruby/library/net-http/http/shared/started.rb
diff --git a/spec/ruby/library/net/http/http/shared/version_1_1.rb b/spec/ruby/library/net-http/http/shared/version_1_1.rb
index db3d6a986d..db3d6a986d 100644
--- a/spec/ruby/library/net/http/http/shared/version_1_1.rb
+++ b/spec/ruby/library/net-http/http/shared/version_1_1.rb
diff --git a/spec/ruby/library/net/http/http/shared/version_1_2.rb b/spec/ruby/library/net-http/http/shared/version_1_2.rb
index b044182c60..b044182c60 100644
--- a/spec/ruby/library/net/http/http/shared/version_1_2.rb
+++ b/spec/ruby/library/net-http/http/shared/version_1_2.rb
diff --git a/spec/ruby/library/net-http/http/socket_type_spec.rb b/spec/ruby/library/net-http/http/socket_type_spec.rb
new file mode 100644
index 0000000000..f6826777b0
--- /dev/null
+++ b/spec/ruby/library/net-http/http/socket_type_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP.socket_type" do
+ it "returns BufferedIO" do
+ Net::HTTP.socket_type.should == Net::BufferedIO
+ end
+end
diff --git a/spec/ruby/library/net-http/http/start_spec.rb b/spec/ruby/library/net-http/http/start_spec.rb
new file mode 100644
index 0000000000..0ce3e79269
--- /dev/null
+++ b/spec/ruby/library/net-http/http/start_spec.rb
@@ -0,0 +1,111 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP.start" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @port = NetHTTPSpecs.port
+ end
+
+ after :each do
+ NetHTTPSpecs.stop_server
+ end
+
+ describe "when not passed a block" do
+ before :each do
+ @http = Net::HTTP.start("localhost", @port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ end
+
+ it "returns a new Net::HTTP object for the passed address and port" do
+ @http.should be_kind_of(Net::HTTP)
+ @http.address.should == "localhost"
+ @http.port.should == @port
+ end
+
+ it "opens the tcp connection" do
+ @http.started?.should be_true
+ end
+ end
+
+ describe "when passed a block" do
+ it "returns the blocks return value" do
+ Net::HTTP.start("localhost", @port) { :test }.should == :test
+ end
+
+ it "yields the new Net::HTTP object to the block" do
+ yielded = false
+ Net::HTTP.start("localhost", @port) do |net|
+ yielded = true
+ net.should be_kind_of(Net::HTTP)
+ end
+ yielded.should be_true
+ end
+
+ it "opens the tcp connection before yielding" do
+ Net::HTTP.start("localhost", @port) { |http| http.started?.should be_true }
+ end
+
+ it "closes the tcp connection after yielding" do
+ net = nil
+ Net::HTTP.start("localhost", @port) { |x| net = x }
+ net.started?.should be_false
+ end
+ end
+end
+
+describe "Net::HTTP#start" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.new("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "returns self" do
+ @http.start.should equal(@http)
+ end
+
+ it "opens the tcp connection" do
+ @http.start
+ @http.started?.should be_true
+ end
+
+ describe "when self has already been started" do
+ it "raises an IOError" do
+ @http.start
+ -> { @http.start }.should raise_error(IOError)
+ end
+ end
+
+ describe "when passed a block" do
+ it "returns the blocks return value" do
+ @http.start { :test }.should == :test
+ end
+
+ it "yields the new Net::HTTP object to the block" do
+ yielded = false
+ @http.start do |http|
+ yielded = true
+ http.should equal(@http)
+ end
+ yielded.should be_true
+ end
+
+ it "opens the tcp connection before yielding" do
+ @http.start { |http| http.started?.should be_true }
+ end
+
+ it "closes the tcp connection after yielding" do
+ @http.start { }
+ @http.started?.should be_false
+ end
+ end
+end
diff --git a/spec/ruby/library/net-http/http/started_spec.rb b/spec/ruby/library/net-http/http/started_spec.rb
new file mode 100644
index 0000000000..cbb82ceefa
--- /dev/null
+++ b/spec/ruby/library/net-http/http/started_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+require_relative 'shared/started'
+
+describe "Net::HTTP#started?" do
+ it_behaves_like :net_http_started_p, :started?
+end
diff --git a/spec/ruby/library/net-http/http/trace_spec.rb b/spec/ruby/library/net-http/http/trace_spec.rb
new file mode 100644
index 0000000000..9809d537c5
--- /dev/null
+++ b/spec/ruby/library/net-http/http/trace_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#trace" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends a TRACE request to the passed path and returns the response" do
+ response = @http.trace("/request")
+ response.body.should == "Request type: TRACE"
+ end
+
+ it "returns a Net::HTTPResponse" do
+ @http.trace("/request").should be_kind_of(Net::HTTPResponse)
+ end
+end
diff --git a/spec/ruby/library/net-http/http/unlock_spec.rb b/spec/ruby/library/net-http/http/unlock_spec.rb
new file mode 100644
index 0000000000..adf0b49f65
--- /dev/null
+++ b/spec/ruby/library/net-http/http/unlock_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#unlock" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends an UNLOCK request to the passed path and returns the response" do
+ response = @http.unlock("/request", "test=test")
+ response.body.should == "Request type: UNLOCK"
+ end
+
+ it "returns a Net::HTTPResponse" do
+ @http.unlock("/request", "test=test").should be_kind_of(Net::HTTPResponse)
+ end
+end
diff --git a/spec/ruby/library/net-http/http/use_ssl_spec.rb b/spec/ruby/library/net-http/http/use_ssl_spec.rb
new file mode 100644
index 0000000000..912a62a8ba
--- /dev/null
+++ b/spec/ruby/library/net-http/http/use_ssl_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP#use_ssl?" do
+ it "returns false" do
+ http = Net::HTTP.new("localhost")
+ http.use_ssl?.should be_false
+ end
+end
diff --git a/spec/ruby/library/net-http/http/version_1_1_spec.rb b/spec/ruby/library/net-http/http/version_1_1_spec.rb
new file mode 100644
index 0000000000..34a4ac8a6b
--- /dev/null
+++ b/spec/ruby/library/net-http/http/version_1_1_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'shared/version_1_1'
+
+describe "Net::HTTP.version_1_1?" do
+ it_behaves_like :net_http_version_1_1_p, :version_1_1?
+end
diff --git a/spec/ruby/library/net-http/http/version_1_2_spec.rb b/spec/ruby/library/net-http/http/version_1_2_spec.rb
new file mode 100644
index 0000000000..e994511aea
--- /dev/null
+++ b/spec/ruby/library/net-http/http/version_1_2_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'shared/version_1_2'
+
+describe "Net::HTTP.version_1_2" do
+ it "turns on net/http 1.2 features" do
+ Net::HTTP.version_1_2
+
+ Net::HTTP.version_1_2?.should be_true
+ Net::HTTP.version_1_1?.should be_false
+ end
+
+ it "returns true" do
+ Net::HTTP.version_1_2.should be_true
+ end
+end
+
+describe "Net::HTTP.version_1_2?" do
+ it_behaves_like :net_http_version_1_2_p, :version_1_2?
+end
diff --git a/spec/ruby/library/net/http/httpexceptions/fixtures/classes.rb b/spec/ruby/library/net-http/httpexceptions/fixtures/classes.rb
index abe8855eff..abe8855eff 100644
--- a/spec/ruby/library/net/http/httpexceptions/fixtures/classes.rb
+++ b/spec/ruby/library/net-http/httpexceptions/fixtures/classes.rb
diff --git a/spec/ruby/library/net-http/httpexceptions/initialize_spec.rb b/spec/ruby/library/net-http/httpexceptions/initialize_spec.rb
new file mode 100644
index 0000000000..5316cca69d
--- /dev/null
+++ b/spec/ruby/library/net-http/httpexceptions/initialize_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPExceptions#initialize when passed message, response" do
+ before :each do
+ @exception = NetHTTPExceptionsSpecs::Simple.new("error message", "a http response")
+ end
+
+ it "calls super with the passed message" do
+ @exception.message.should == "error message"
+ end
+
+ it "sets self's response to the passed response" do
+ @exception.response.should == "a http response"
+ end
+end
diff --git a/spec/ruby/library/net-http/httpexceptions/response_spec.rb b/spec/ruby/library/net-http/httpexceptions/response_spec.rb
new file mode 100644
index 0000000000..d718b1ae21
--- /dev/null
+++ b/spec/ruby/library/net-http/httpexceptions/response_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPExceptions#response" do
+ it "returns self's response" do
+ exception = NetHTTPExceptionsSpecs::Simple.new("error message", "a http response")
+ exception.response.should == "a http response"
+ end
+end
diff --git a/spec/ruby/library/net-http/httpgenericrequest/body_exist_spec.rb b/spec/ruby/library/net-http/httpgenericrequest/body_exist_spec.rb
new file mode 100644
index 0000000000..6c886499ca
--- /dev/null
+++ b/spec/ruby/library/net-http/httpgenericrequest/body_exist_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPGenericRequest#body_exist?" do
+ it "returns true when the response is expected to have a body" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+ request.body_exist?.should be_true
+
+ request = Net::HTTPGenericRequest.new("POST", true, false, "/some/path")
+ request.body_exist?.should be_false
+ end
+
+ describe "when $VERBOSE is true" do
+ it "emits a warning" do
+ request = Net::HTTPGenericRequest.new("POST", true, false, "/some/path")
+ -> {
+ request.body_exist?
+ }.should complain(/body_exist\? is obsolete/, verbose: true)
+ end
+ end
+end
diff --git a/spec/ruby/library/net-http/httpgenericrequest/body_spec.rb b/spec/ruby/library/net-http/httpgenericrequest/body_spec.rb
new file mode 100644
index 0000000000..5f7315f303
--- /dev/null
+++ b/spec/ruby/library/net-http/httpgenericrequest/body_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require "stringio"
+
+describe "Net::HTTPGenericRequest#body" do
+ it "returns self's request body" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+ request.body.should be_nil
+
+ request.body = "Some Content"
+ request.body.should == "Some Content"
+ end
+end
+
+describe "Net::HTTPGenericRequest#body=" do
+ before :each do
+ @request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+ end
+
+ it "sets self's body content to the passed String" do
+ @request.body = "Some Content"
+ @request.body.should == "Some Content"
+ end
+
+ it "sets self's body stream to nil" do
+ @request.body_stream = StringIO.new("")
+ @request.body = "Some Content"
+ @request.body_stream.should be_nil
+ end
+end
diff --git a/spec/ruby/library/net-http/httpgenericrequest/body_stream_spec.rb b/spec/ruby/library/net-http/httpgenericrequest/body_stream_spec.rb
new file mode 100644
index 0000000000..dea1c8c883
--- /dev/null
+++ b/spec/ruby/library/net-http/httpgenericrequest/body_stream_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require "stringio"
+
+describe "Net::HTTPGenericRequest#body_stream" do
+ it "returns self's body stream Object" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+ request.body_stream.should be_nil
+
+ stream = StringIO.new("test")
+ request.body_stream = stream
+ request.body_stream.should equal(stream)
+ end
+end
+
+describe "Net::HTTPGenericRequest#body_stream=" do
+ before :each do
+ @request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+ @stream = StringIO.new("test")
+ end
+
+ it "sets self's body stream to the passed Object" do
+ @request.body_stream = @stream
+ @request.body_stream.should equal(@stream)
+ end
+
+ it "sets self's body to nil" do
+ @request.body = "Some Content"
+ @request.body_stream = @stream
+ @request.body.should be_nil
+ end
+end
diff --git a/spec/ruby/library/net-http/httpgenericrequest/exec_spec.rb b/spec/ruby/library/net-http/httpgenericrequest/exec_spec.rb
new file mode 100644
index 0000000000..7de03d7da0
--- /dev/null
+++ b/spec/ruby/library/net-http/httpgenericrequest/exec_spec.rb
@@ -0,0 +1,131 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require "stringio"
+
+describe "Net::HTTPGenericRequest#exec when passed socket, version, path" do
+ before :each do
+ @socket = StringIO.new(+"")
+ @buffered_socket = Net::BufferedIO.new(@socket)
+ end
+
+ it "executes the request over the socket to the path using the HTTP version" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+
+ request.exec(@buffered_socket, "1.1", "/some/path")
+ str = @socket.string
+
+ str.should =~ %r[POST /some/path HTTP/1.1\r\n]
+ str.should =~ %r[Accept: \*/\*\r\n]
+ str[-4..-1].should == "\r\n\r\n"
+
+ request = Net::HTTPGenericRequest.new("GET", true, true, "/some/path",
+ "Content-Type" => "text/html")
+
+ request.exec(@buffered_socket, "1.0", "/some/other/path")
+ str = @socket.string
+
+ str.should =~ %r[GET /some/other/path HTTP/1.0\r\n]
+ str.should =~ %r[Accept: \*/\*\r\n]
+ str.should =~ %r[Content-Type: text/html\r\n]
+ str[-4..-1].should == "\r\n\r\n"
+ end
+
+ describe "when a request body is set" do
+ it "sets the 'Content-Type' header to 'application/x-www-form-urlencoded' unless the 'Content-Type' header is supplied" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+ request.body = "Some Content"
+
+ request.exec(@buffered_socket, "1.1", "/some/other/path")
+ str = @socket.string
+
+ str.should =~ %r[POST /some/other/path HTTP/1.1\r\n]
+ str.should =~ %r[Accept: \*/\*\r\n]
+ str.should =~ %r[Content-Type: application/x-www-form-urlencoded\r\n]
+ str.should =~ %r[Content-Length: 12\r\n]
+ str[-16..-1].should == "\r\n\r\nSome Content"
+ end
+
+ it "correctly sets the 'Content-Length' header and includes the body" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path",
+ "Content-Type" => "text/html")
+ request.body = "Some Content"
+
+ request.exec(@buffered_socket, "1.1", "/some/other/path")
+ str = @socket.string
+
+ str.should =~ %r[POST /some/other/path HTTP/1.1\r\n]
+ str.should =~ %r[Accept: \*/\*\r\n]
+ str.should =~ %r[Content-Type: text/html\r\n]
+ str.should =~ %r[Content-Length: 12\r\n]
+ str[-16..-1].should == "\r\n\r\nSome Content"
+ end
+ end
+
+ describe "when a body stream is set" do
+ it "sets the 'Content-Type' header to 'application/x-www-form-urlencoded' unless the 'Content-Type' header is supplied" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path",
+ "Content-Length" => "10")
+ request.body_stream = StringIO.new("a" * 20)
+
+ request.exec(@buffered_socket, "1.1", "/some/other/path")
+ str = @socket.string
+
+ str.should =~ %r[POST /some/other/path HTTP/1.1\r\n]
+ str.should =~ %r[Accept: \*/\*\r\n]
+ str.should =~ %r[Content-Type: application/x-www-form-urlencoded\r\n]
+ str.should =~ %r[Content-Length: 10\r\n]
+ str[-24..-1].should == "\r\n\r\naaaaaaaaaaaaaaaaaaaa"
+ end
+
+ it "sends the whole stream, regardless of the 'Content-Length' header" do
+ request = Net::HTTPGenericRequest.new("POST", true, true,"/some/path",
+ "Content-Type" => "text/html",
+ "Content-Length" => "10")
+ request.body_stream = StringIO.new("a" * 20)
+
+ request.exec(@buffered_socket, "1.1", "/some/other/path")
+ str = @socket.string
+
+ str.should =~ %r[POST /some/other/path HTTP/1.1\r\n]
+ str.should =~ %r[Accept: \*/\*\r\n]
+ str.should =~ %r[Content-Type: text/html\r\n]
+ str.should =~ %r[Content-Length: 10\r\n]
+ str[-24..-1].should == "\r\n\r\naaaaaaaaaaaaaaaaaaaa"
+ end
+
+ it "sends the request in chunks when 'Transfer-Encoding' is set to 'chunked'" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path",
+ "Content-Type" => "text/html",
+ "Transfer-Encoding" => "chunked")
+ datasize = 1024 * 10
+ request.body_stream = StringIO.new("a" * datasize)
+
+ request.exec(@buffered_socket, "1.1", "/some/other/path")
+ str = @socket.string
+
+ str.should =~ %r[POST /some/other/path HTTP/1.1\r\n]
+ str.should =~ %r[Accept: \*/\*\r\n]
+ str.should =~ %r[Content-Type: text/html\r\n]
+ str.should =~ %r[Transfer-Encoding: chunked\r\n]
+ str =~ %r[\r\n\r\n]
+ str = $'
+ while datasize > 0
+ chunk_size_line, str = str.split(/\r\n/, 2)
+ chunk_size = chunk_size_line[/\A[0-9A-Fa-f]+/].to_i(16)
+ str.slice!(0, chunk_size).should == 'a' * chunk_size
+ datasize -= chunk_size
+ str.slice!(0, 2).should == "\r\n"
+ end
+ datasize.should == 0
+ str.should == %"0\r\n\r\n"
+ end
+
+ it "raises an ArgumentError when the 'Content-Length' is not set or 'Transfer-Encoding' is not set to 'chunked'" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path",
+ "Content-Type" => "text/html")
+ request.body_stream = StringIO.new("Some Content")
+
+ -> { request.exec(@buffered_socket, "1.1", "/some/other/path") }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/library/net-http/httpgenericrequest/inspect_spec.rb b/spec/ruby/library/net-http/httpgenericrequest/inspect_spec.rb
new file mode 100644
index 0000000000..d03b6e6953
--- /dev/null
+++ b/spec/ruby/library/net-http/httpgenericrequest/inspect_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPGenericRequest#inspect" do
+ it "returns a String representation of self" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+ request.inspect.should == "#<Net::HTTPGenericRequest POST>"
+
+ request = Net::HTTPGenericRequest.new("GET", false, true, "/some/path")
+ request.inspect.should == "#<Net::HTTPGenericRequest GET>"
+
+ request = Net::HTTPGenericRequest.new("BLA", true, true, "/some/path")
+ request.inspect.should == "#<Net::HTTPGenericRequest BLA>"
+
+ # Subclasses
+ request = Net::HTTP::Get.new("/some/path")
+ request.inspect.should == "#<Net::HTTP::Get GET>"
+
+ request = Net::HTTP::Post.new("/some/path")
+ request.inspect.should == "#<Net::HTTP::Post POST>"
+
+ request = Net::HTTP::Trace.new("/some/path")
+ request.inspect.should == "#<Net::HTTP::Trace TRACE>"
+ end
+end
diff --git a/spec/ruby/library/net-http/httpgenericrequest/method_spec.rb b/spec/ruby/library/net-http/httpgenericrequest/method_spec.rb
new file mode 100644
index 0000000000..794bd328cd
--- /dev/null
+++ b/spec/ruby/library/net-http/httpgenericrequest/method_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPGenericRequest#method" do
+ it "returns self's request method" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+ request.method.should == "POST"
+
+ request = Net::HTTPGenericRequest.new("GET", false, true, "/some/path")
+ request.method.should == "GET"
+
+ request = Net::HTTPGenericRequest.new("BLA", true, true, "/some/path")
+ request.method.should == "BLA"
+ end
+end
diff --git a/spec/ruby/library/net-http/httpgenericrequest/path_spec.rb b/spec/ruby/library/net-http/httpgenericrequest/path_spec.rb
new file mode 100644
index 0000000000..a9fac3f67e
--- /dev/null
+++ b/spec/ruby/library/net-http/httpgenericrequest/path_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPGenericRequest#path" do
+ it "returns self's request path" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+ request.path.should == "/some/path"
+
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/other/path")
+ request.path.should == "/some/other/path"
+ end
+end
diff --git a/spec/ruby/library/net-http/httpgenericrequest/request_body_permitted_spec.rb b/spec/ruby/library/net-http/httpgenericrequest/request_body_permitted_spec.rb
new file mode 100644
index 0000000000..1713b59baf
--- /dev/null
+++ b/spec/ruby/library/net-http/httpgenericrequest/request_body_permitted_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPGenericRequest#request_body_permitted?" do
+ it "returns true when the request is expected to have a body" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+ request.request_body_permitted?.should be_true
+
+ request = Net::HTTPGenericRequest.new("POST", false, true, "/some/path")
+ request.request_body_permitted?.should be_false
+ end
+end
diff --git a/spec/ruby/library/net-http/httpgenericrequest/response_body_permitted_spec.rb b/spec/ruby/library/net-http/httpgenericrequest/response_body_permitted_spec.rb
new file mode 100644
index 0000000000..2f0751c344
--- /dev/null
+++ b/spec/ruby/library/net-http/httpgenericrequest/response_body_permitted_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPGenericRequest#response_body_permitted?" do
+ it "returns true when the response is expected to have a body" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+ request.response_body_permitted?.should be_true
+
+ request = Net::HTTPGenericRequest.new("POST", true, false, "/some/path")
+ request.response_body_permitted?.should be_false
+ end
+end
diff --git a/spec/ruby/library/net-http/httpgenericrequest/set_body_internal_spec.rb b/spec/ruby/library/net-http/httpgenericrequest/set_body_internal_spec.rb
new file mode 100644
index 0000000000..358aa6cde3
--- /dev/null
+++ b/spec/ruby/library/net-http/httpgenericrequest/set_body_internal_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPGenericRequest#set_body_internal when passed string" do
+ before :each do
+ @request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+ end
+
+ it "sets self's body to the passed string" do
+ @request.set_body_internal("Some Content")
+ @request.body.should == "Some Content"
+ end
+
+ it "raises an ArgumentError when the body or body_stream of self have already been set" do
+ @request.body = "Some Content"
+ -> { @request.set_body_internal("Some other Content") }.should raise_error(ArgumentError)
+
+ @request.body_stream = "Some Content"
+ -> { @request.set_body_internal("Some other Content") }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/net-http/httpheader/add_field_spec.rb b/spec/ruby/library/net-http/httpheader/add_field_spec.rb
new file mode 100644
index 0000000000..8cd3d33517
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/add_field_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#add_field when passed key, value" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "adds the passed value to the header entry with the passed key" do
+ @headers.add_field("My-Header", "a")
+ @headers.get_fields("My-Header").should == ["a"]
+
+ @headers.add_field("My-Header", "b")
+ @headers.get_fields("My-Header").should == ["a", "b"]
+
+ @headers.add_field("My-Header", "c")
+ @headers.get_fields("My-Header").should == ["a", "b", "c"]
+ end
+
+ it "is case-insensitive" do
+ @headers.add_field("My-Header", "a")
+ @headers.get_fields("My-Header").should == ["a"]
+
+ @headers.add_field("my-header", "b")
+ @headers.get_fields("My-Header").should == ["a", "b"]
+
+ @headers.add_field("MY-HEADER", "c")
+ @headers.get_fields("My-Header").should == ["a", "b", "c"]
+ end
+end
diff --git a/spec/ruby/library/net-http/httpheader/basic_auth_spec.rb b/spec/ruby/library/net-http/httpheader/basic_auth_spec.rb
new file mode 100644
index 0000000000..db7ca84d13
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/basic_auth_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#basic_auth when passed account, password" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "sets the 'Authorization' Header entry for basic authorization" do
+ @headers.basic_auth("rubyspec", "rocks")
+ @headers["Authorization"].should == "Basic cnVieXNwZWM6cm9ja3M="
+ end
+end
diff --git a/spec/ruby/library/net-http/httpheader/canonical_each_spec.rb b/spec/ruby/library/net-http/httpheader/canonical_each_spec.rb
new file mode 100644
index 0000000000..64a5cae89e
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/canonical_each_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/each_capitalized'
+
+describe "Net::HTTPHeader#canonical_each" do
+ it_behaves_like :net_httpheader_each_capitalized, :canonical_each
+end
diff --git a/spec/ruby/library/net-http/httpheader/chunked_spec.rb b/spec/ruby/library/net-http/httpheader/chunked_spec.rb
new file mode 100644
index 0000000000..b32a0aab38
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/chunked_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#chunked?" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns true if the 'Transfer-Encoding' header entry is set to chunked" do
+ @headers.chunked?.should be_false
+
+ @headers["Transfer-Encoding"] = "bla"
+ @headers.chunked?.should be_false
+
+ @headers["Transfer-Encoding"] = "blachunkedbla"
+ @headers.chunked?.should be_false
+
+ @headers["Transfer-Encoding"] = "chunked"
+ @headers.chunked?.should be_true
+ end
+end
diff --git a/spec/ruby/library/net-http/httpheader/content_length_spec.rb b/spec/ruby/library/net-http/httpheader/content_length_spec.rb
new file mode 100644
index 0000000000..f05c5f8d8b
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/content_length_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#content_length" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns nil if no 'Content-Length' header entry is set" do
+ @headers.content_length.should be_nil
+ end
+
+ it "raises a Net::HTTPHeaderSyntaxError when the 'Content-Length' header entry has an invalid format" do
+ @headers["Content-Length"] = "invalid"
+ -> { @headers.content_length }.should raise_error(Net::HTTPHeaderSyntaxError)
+ end
+
+ it "returns the value of the 'Content-Length' header entry as an Integer" do
+ @headers["Content-Length"] = "123"
+ @headers.content_length.should eql(123)
+
+ @headers["Content-Length"] = "123valid"
+ @headers.content_length.should eql(123)
+
+ @headers["Content-Length"] = "valid123"
+ @headers.content_length.should eql(123)
+ end
+end
+
+describe "Net::HTTPHeader#content_length=" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "removes the 'Content-Length' entry if passed false or nil" do
+ @headers["Content-Length"] = "123"
+ @headers.content_length = nil
+ @headers["Content-Length"].should be_nil
+ end
+
+ it "sets the 'Content-Length' entry to the passed value" do
+ @headers.content_length = "123"
+ @headers["Content-Length"].should == "123"
+
+ @headers.content_length = "123valid"
+ @headers["Content-Length"].should == "123"
+ end
+
+ it "sets the 'Content-Length' entry to 0 if the passed value is not valid" do
+ @headers.content_length = "invalid123"
+ @headers["Content-Length"].should == "0"
+ end
+end
diff --git a/spec/ruby/library/net-http/httpheader/content_range_spec.rb b/spec/ruby/library/net-http/httpheader/content_range_spec.rb
new file mode 100644
index 0000000000..09737141a5
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/content_range_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#content_range" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns a Range object that represents the 'Content-Range' header entry" do
+ @headers["Content-Range"] = "bytes 0-499/1234"
+ @headers.content_range.should == (0..499)
+
+ @headers["Content-Range"] = "bytes 500-1233/1234"
+ @headers.content_range.should == (500..1233)
+ end
+
+ it "returns nil when there is no 'Content-Range' header entry" do
+ @headers.content_range.should be_nil
+ end
+
+ it "raises a Net::HTTPHeaderSyntaxError when the 'Content-Range' has an invalid format" do
+ @headers["Content-Range"] = "invalid"
+ -> { @headers.content_range }.should raise_error(Net::HTTPHeaderSyntaxError)
+
+ @headers["Content-Range"] = "bytes 123-abc"
+ -> { @headers.content_range }.should raise_error(Net::HTTPHeaderSyntaxError)
+
+ @headers["Content-Range"] = "bytes abc-123"
+ -> { @headers.content_range }.should raise_error(Net::HTTPHeaderSyntaxError)
+ end
+end
diff --git a/spec/ruby/library/net-http/httpheader/content_type_spec.rb b/spec/ruby/library/net-http/httpheader/content_type_spec.rb
new file mode 100644
index 0000000000..a6e1ae1093
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/content_type_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/set_content_type'
+
+describe "Net::HTTPHeader#content_type" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns the content type string, as per 'Content-Type' header entry" do
+ @headers["Content-Type"] = "text/html"
+ @headers.content_type.should == "text/html"
+
+ @headers["Content-Type"] = "text/html;charset=utf-8"
+ @headers.content_type.should == "text/html"
+ end
+
+ it "returns nil if the 'Content-Type' header entry does not exist" do
+ @headers.content_type.should be_nil
+ end
+end
+
+describe "Net::HTTPHeader#content_type=" do
+ it_behaves_like :net_httpheader_set_content_type, :content_type=
+end
diff --git a/spec/ruby/library/net-http/httpheader/delete_spec.rb b/spec/ruby/library/net-http/httpheader/delete_spec.rb
new file mode 100644
index 0000000000..8d929dbd86
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/delete_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#delete when passed key" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "removes the header entry with the passed key" do
+ @headers["My-Header"] = "test"
+ @headers.delete("My-Header")
+
+ @headers["My-Header"].should be_nil
+ @headers.size.should eql(0)
+ end
+
+ it "returns the removed values" do
+ @headers["My-Header"] = "test"
+ @headers.delete("My-Header").should == ["test"]
+ end
+
+ it "is case-insensitive" do
+ @headers["My-Header"] = "test"
+ @headers.delete("my-header")
+
+ @headers["My-Header"].should be_nil
+ @headers.size.should eql(0)
+ end
+end
diff --git a/spec/ruby/library/net-http/httpheader/each_capitalized_name_spec.rb b/spec/ruby/library/net-http/httpheader/each_capitalized_name_spec.rb
new file mode 100644
index 0000000000..27713577f9
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/each_capitalized_name_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#each_capitalized_name" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ @headers["My-Header"] = "test"
+ @headers.add_field("My-Other-Header", "a")
+ @headers.add_field("My-Other-Header", "b")
+ end
+
+ describe "when passed a block" do
+ it "yields each header key to the passed block (keys capitalized)" do
+ res = []
+ @headers.each_capitalized_name do |key|
+ res << key
+ end
+ res.sort.should == ["My-Header", "My-Other-Header"]
+ end
+ end
+
+ describe "when passed no block" do
+ it "returns an Enumerator" do
+ enumerator = @headers.each_capitalized_name
+ enumerator.should be_an_instance_of(Enumerator)
+
+ res = []
+ enumerator.each do |key|
+ res << key
+ end
+ res.sort.should == ["My-Header", "My-Other-Header"]
+ end
+ end
+end
diff --git a/spec/ruby/library/net-http/httpheader/each_capitalized_spec.rb b/spec/ruby/library/net-http/httpheader/each_capitalized_spec.rb
new file mode 100644
index 0000000000..1e853995ea
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/each_capitalized_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/each_capitalized'
+
+describe "Net::HTTPHeader#each_capitalized" do
+ it_behaves_like :net_httpheader_each_capitalized, :each_capitalized
+end
diff --git a/spec/ruby/library/net-http/httpheader/each_header_spec.rb b/spec/ruby/library/net-http/httpheader/each_header_spec.rb
new file mode 100644
index 0000000000..869feebacf
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/each_header_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/each_header'
+
+describe "Net::HTTPHeader#each_header" do
+ it_behaves_like :net_httpheader_each_header, :each_header
+end
diff --git a/spec/ruby/library/net-http/httpheader/each_key_spec.rb b/spec/ruby/library/net-http/httpheader/each_key_spec.rb
new file mode 100644
index 0000000000..1ad145629f
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/each_key_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/each_name'
+
+describe "Net::HTTPHeader#each_key" do
+ it_behaves_like :net_httpheader_each_name, :each_key
+end
diff --git a/spec/ruby/library/net-http/httpheader/each_name_spec.rb b/spec/ruby/library/net-http/httpheader/each_name_spec.rb
new file mode 100644
index 0000000000..f819bd989d
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/each_name_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/each_name'
+
+describe "Net::HTTPHeader#each_name" do
+ it_behaves_like :net_httpheader_each_name, :each_name
+end
diff --git a/spec/ruby/library/net-http/httpheader/each_spec.rb b/spec/ruby/library/net-http/httpheader/each_spec.rb
new file mode 100644
index 0000000000..ff37249d0a
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/each_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/each_header'
+
+describe "Net::HTTPHeader#each" do
+ it_behaves_like :net_httpheader_each_header, :each
+end
diff --git a/spec/ruby/library/net-http/httpheader/each_value_spec.rb b/spec/ruby/library/net-http/httpheader/each_value_spec.rb
new file mode 100644
index 0000000000..b71df58c65
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/each_value_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#each_value" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ @headers["My-Header"] = "test"
+ @headers.add_field("My-Other-Header", "a")
+ @headers.add_field("My-Other-Header", "b")
+ end
+
+ describe "when passed a block" do
+ it "yields each header entry's joined values" do
+ res = []
+ @headers.each_value do |value|
+ res << value
+ end
+ res.sort.should == ["a, b", "test"]
+ end
+ end
+
+ describe "when passed no block" do
+ it "returns an Enumerator" do
+ enumerator = @headers.each_value
+ enumerator.should be_an_instance_of(Enumerator)
+
+ res = []
+ enumerator.each do |key|
+ res << key
+ end
+ res.sort.should == ["a, b", "test"]
+ end
+ end
+end
diff --git a/spec/ruby/library/net-http/httpheader/element_reference_spec.rb b/spec/ruby/library/net-http/httpheader/element_reference_spec.rb
new file mode 100644
index 0000000000..1003c41af9
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/element_reference_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#[] when passed key" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns the value of the header entry with the passed key" do
+ @headers["My-Header"] = "test"
+ @headers["My-Header"].should == "test"
+ @headers["My-Other-Header"] = "another test"
+ @headers["My-Other-Header"].should == "another test"
+ end
+
+ it "is case-insensitive" do
+ @headers["My-Header"] = "test"
+
+ @headers['My-Header'].should == "test"
+ @headers['my-Header'].should == "test"
+ @headers['My-header'].should == "test"
+ @headers['my-header'].should == "test"
+ @headers['MY-HEADER'].should == "test"
+ end
+
+ it "returns multi-element values joined together" do
+ @headers["My-Header"] = "test"
+ @headers.add_field("My-Header", "another test")
+ @headers.add_field("My-Header", "and one more")
+
+ @headers["My-Header"].should == "test, another test, and one more"
+ end
+
+ it "returns nil for non-existing entries" do
+ @headers["My-Header"].should be_nil
+ @headers["My-Other-Header"].should be_nil
+ end
+end
diff --git a/spec/ruby/library/net-http/httpheader/element_set_spec.rb b/spec/ruby/library/net-http/httpheader/element_set_spec.rb
new file mode 100644
index 0000000000..376df2f977
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/element_set_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#[]= when passed key, value" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "sets the header entry with the passed key to the passed value" do
+ @headers["My-Header"] = "test"
+ @headers["My-Header"].should == "test"
+
+ @headers["My-Header"] = "overwritten"
+ @headers["My-Header"].should == "overwritten"
+
+ @headers["My-Other-Header"] = "another test"
+ @headers["My-Other-Header"].should == "another test"
+ end
+
+ it "is case-insensitive" do
+ @headers['My-Header'] = "test"
+ @headers['my-Header'] = "another test"
+ @headers['My-header'] = "and one more test"
+ @headers['my-header'] = "and another one"
+ @headers['MY-HEADER'] = "last one"
+
+ @headers["My-Header"].should == "last one"
+ @headers.size.should eql(1)
+ end
+
+ it "removes the header entry with the passed key when the value is false or nil" do
+ @headers['My-Header'] = "test"
+ @headers['My-Header'] = nil
+ @headers['My-Header'].should be_nil
+
+ @headers['My-Header'] = "test"
+ @headers['My-Header'] = false
+ @headers['My-Header'].should be_nil
+ end
+end
diff --git a/spec/ruby/library/net-http/httpheader/fetch_spec.rb b/spec/ruby/library/net-http/httpheader/fetch_spec.rb
new file mode 100644
index 0000000000..58c69c0377
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/fetch_spec.rb
@@ -0,0 +1,68 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#fetch" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ describe "when passed key" do
+ it "returns the header entry for the passed key" do
+ @headers["My-Header"] = "test"
+ @headers.fetch("My-Header").should == "test"
+
+ @headers.add_field("My-Other-Header", "a")
+ @headers.add_field("My-Other-Header", "b")
+ @headers.add_field("My-Other-Header", "c")
+ @headers.fetch("My-Other-Header").should == "a, b, c"
+ end
+
+ it "is case-insensitive" do
+ @headers["My-Header"] = "test"
+ @headers.fetch("my-header").should == "test"
+ @headers.fetch("MY-HEADER").should == "test"
+ end
+
+ it "returns nil when there is no entry for the passed key" do
+ -> { @headers.fetch("my-header") }.should raise_error(IndexError)
+ end
+ end
+
+ describe "when passed key, default" do
+ it "returns the header entry for the passed key" do
+ @headers["My-Header"] = "test"
+ @headers.fetch("My-Header", "bla").should == "test"
+
+ @headers.add_field("My-Other-Header", "a")
+ @headers.add_field("My-Other-Header", "b")
+ @headers.add_field("My-Other-Header", "c")
+ @headers.fetch("My-Other-Header", "bla").should == "a, b, c"
+ end
+
+ # TODO: This raises a NoMethodError: undefined method `join' for "bla":String
+ it "returns the default value when there is no entry for the passed key" do
+ @headers.fetch("My-Header", "bla").should == "bla"
+ end
+ end
+
+ describe "when passed key and block" do
+ it "returns the header entry for the passed key" do
+ @headers["My-Header"] = "test"
+ @headers.fetch("My-Header") {}.should == "test"
+
+ @headers.add_field("My-Other-Header", "a")
+ @headers.add_field("My-Other-Header", "b")
+ @headers.add_field("My-Other-Header", "c")
+ -> {
+ @result = @headers.fetch("My-Other-Header", "bla") {}
+ }.should complain(/block supersedes default value argument/)
+ @result.should == "a, b, c"
+ end
+
+ # TODO: This raises a NoMethodError: undefined method `join' for "redaeh-ym":String
+ it "yieldsand returns the block's return value when there is no entry for the passed key" do
+ @headers.fetch("My-Header") { |key| key.reverse }.should == "redaeh-ym"
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/fixtures/classes.rb b/spec/ruby/library/net-http/httpheader/fixtures/classes.rb
index b5ec6abd75..b5ec6abd75 100644
--- a/spec/ruby/library/net/http/httpheader/fixtures/classes.rb
+++ b/spec/ruby/library/net-http/httpheader/fixtures/classes.rb
diff --git a/spec/ruby/library/net-http/httpheader/form_data_spec.rb b/spec/ruby/library/net-http/httpheader/form_data_spec.rb
new file mode 100644
index 0000000000..acd913f53a
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/form_data_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/set_form_data'
+
+describe "Net::HTTPHeader#form_data=" do
+ it_behaves_like :net_httpheader_set_form_data, :form_data=
+end
diff --git a/spec/ruby/library/net-http/httpheader/get_fields_spec.rb b/spec/ruby/library/net-http/httpheader/get_fields_spec.rb
new file mode 100644
index 0000000000..0278bcede2
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/get_fields_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#get_fields when passed key" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns an Array containing the values of the header entry with the passed key" do
+ @headers["My-Header"] = "a"
+ @headers.get_fields("My-Header").should == ["a"]
+
+ @headers.add_field("My-Header", "b")
+ @headers.get_fields("My-Header").should == ["a", "b"]
+ end
+
+ it "returns a copy of the header entry values" do
+ @headers["My-Header"] = "a"
+
+ @headers.get_fields("My-Header").clear
+ @headers.get_fields("My-Header").should == ["a"]
+
+ @headers.get_fields("My-Header") << "b"
+ @headers.get_fields("My-Header").should == ["a"]
+ end
+
+ it "returns nil for non-existing header entries" do
+ @headers.get_fields("My-Header").should be_nil
+ @headers.get_fields("My-Other-header").should be_nil
+ end
+
+ it "is case-insensitive" do
+ @headers["My-Header"] = "test"
+ @headers.get_fields("My-Header").should == ["test"]
+ @headers.get_fields("my-header").should == ["test"]
+ @headers.get_fields("MY-HEADER").should == ["test"]
+ end
+end
diff --git a/spec/ruby/library/net-http/httpheader/initialize_http_header_spec.rb b/spec/ruby/library/net-http/httpheader/initialize_http_header_spec.rb
new file mode 100644
index 0000000000..f9e6d208e5
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/initialize_http_header_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#initialize_http_header when passed Hash" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.allocate
+ end
+
+ it "initializes the HTTP Header using the passed Hash" do
+ @headers.initialize_http_header("My-Header" => "test", "My-Other-Header" => "another test")
+ @headers["My-Header"].should == "test"
+ @headers["My-Other-Header"].should == "another test"
+ end
+
+ it "complains about duplicate keys when in verbose mode" do
+ -> do
+ @headers.initialize_http_header("My-Header" => "test", "my-header" => "another test")
+ end.should complain(/duplicated HTTP header/, verbose: true)
+ end
+end
diff --git a/spec/ruby/library/net-http/httpheader/key_spec.rb b/spec/ruby/library/net-http/httpheader/key_spec.rb
new file mode 100644
index 0000000000..2b7aeb9c2a
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/key_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#key? when passed key" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns true if the header entry with the passed key exists" do
+ @headers.key?("My-Header").should be_false
+ @headers["My-Header"] = "test"
+ @headers.key?("My-Header").should be_true
+ end
+
+ it "is case-insensitive" do
+ @headers["My-Header"] = "test"
+ @headers.key?("my-header").should be_true
+ @headers.key?("MY-HEADER").should be_true
+ end
+end
diff --git a/spec/ruby/library/net-http/httpheader/length_spec.rb b/spec/ruby/library/net-http/httpheader/length_spec.rb
new file mode 100644
index 0000000000..57e32742e4
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/length_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/size'
+
+describe "Net::HTTPHeader#length" do
+ it_behaves_like :net_httpheader_size, :length
+end
diff --git a/spec/ruby/library/net-http/httpheader/main_type_spec.rb b/spec/ruby/library/net-http/httpheader/main_type_spec.rb
new file mode 100644
index 0000000000..4dd551d8f4
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/main_type_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#main_type" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns the 'main-content-type', as per 'Content-Type' header entry" do
+ @headers["Content-Type"] = "text/html"
+ @headers.main_type.should == "text"
+
+ @headers["Content-Type"] = "application/pdf"
+ @headers.main_type.should == "application"
+
+ @headers["Content-Type"] = "text/html;charset=utf-8"
+ @headers.main_type.should == "text"
+ end
+
+ it "returns nil if the 'Content-Type' header entry does not exist" do
+ @headers.main_type.should be_nil
+ end
+end
diff --git a/spec/ruby/library/net-http/httpheader/proxy_basic_auth_spec.rb b/spec/ruby/library/net-http/httpheader/proxy_basic_auth_spec.rb
new file mode 100644
index 0000000000..d9f6afc5a7
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/proxy_basic_auth_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#proxy_basic_auth when passed account, password" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "sets the 'Proxy-Authorization' Header entry for basic authorization" do
+ @headers.proxy_basic_auth("rubyspec", "rocks")
+ @headers["Proxy-Authorization"].should == "Basic cnVieXNwZWM6cm9ja3M="
+ end
+end
diff --git a/spec/ruby/library/net-http/httpheader/range_length_spec.rb b/spec/ruby/library/net-http/httpheader/range_length_spec.rb
new file mode 100644
index 0000000000..77323ac872
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/range_length_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#range_length" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns the length of the Range represented by the 'Content-Range' header entry" do
+ @headers["Content-Range"] = "bytes 0-499/1234"
+ @headers.range_length.should eql(500)
+
+ @headers["Content-Range"] = "bytes 500-1233/1234"
+ @headers.range_length.should eql(734)
+ end
+
+ it "returns nil when there is no 'Content-Range' header entry" do
+ @headers.range_length.should be_nil
+ end
+
+ it "raises a Net::HTTPHeaderSyntaxError when the 'Content-Range' has an invalid format" do
+ @headers["Content-Range"] = "invalid"
+ -> { @headers.range_length }.should raise_error(Net::HTTPHeaderSyntaxError)
+
+ @headers["Content-Range"] = "bytes 123-abc"
+ -> { @headers.range_length }.should raise_error(Net::HTTPHeaderSyntaxError)
+
+ @headers["Content-Range"] = "bytes abc-123"
+ -> { @headers.range_length }.should raise_error(Net::HTTPHeaderSyntaxError)
+ end
+end
diff --git a/spec/ruby/library/net-http/httpheader/range_spec.rb b/spec/ruby/library/net-http/httpheader/range_spec.rb
new file mode 100644
index 0000000000..2de80a825e
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/range_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/set_range'
+
+describe "Net::HTTPHeader#range" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns a Range object that represents the 'Range' header entry" do
+ @headers["Range"] = "bytes=0-499"
+ @headers.range.should == [0..499]
+
+ @headers["Range"] = "bytes=500-1233"
+ @headers.range.should == [500..1233]
+
+ @headers["Range"] = "bytes=10-"
+ @headers.range.should == [10..-1]
+
+ @headers["Range"] = "bytes=-10"
+ @headers.range.should == [-10..-1]
+ end
+
+ it "returns nil when there is no 'Range' header entry" do
+ @headers.range.should be_nil
+ end
+
+ it "raises a Net::HTTPHeaderSyntaxError when the 'Range' has an invalid format" do
+ @headers["Range"] = "invalid"
+ -> { @headers.range }.should raise_error(Net::HTTPHeaderSyntaxError)
+
+ @headers["Range"] = "bytes 123-abc"
+ -> { @headers.range }.should raise_error(Net::HTTPHeaderSyntaxError)
+
+ @headers["Range"] = "bytes abc-123"
+ -> { @headers.range }.should raise_error(Net::HTTPHeaderSyntaxError)
+ end
+
+ it "raises a Net::HTTPHeaderSyntaxError when the 'Range' was not specified" do
+ @headers["Range"] = "bytes=-"
+ -> { @headers.range }.should raise_error(Net::HTTPHeaderSyntaxError)
+ end
+end
+
+describe "Net::HTTPHeader#range=" do
+ it_behaves_like :net_httpheader_set_range, :range=
+end
diff --git a/spec/ruby/library/net-http/httpheader/set_content_type_spec.rb b/spec/ruby/library/net-http/httpheader/set_content_type_spec.rb
new file mode 100644
index 0000000000..7ec4f90b8e
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/set_content_type_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/set_content_type'
+
+describe "Net::HTTPHeader#set_content_type" do
+ it_behaves_like :net_httpheader_set_content_type, :set_content_type
+end
diff --git a/spec/ruby/library/net-http/httpheader/set_form_data_spec.rb b/spec/ruby/library/net-http/httpheader/set_form_data_spec.rb
new file mode 100644
index 0000000000..7aac19f045
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/set_form_data_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/set_form_data'
+
+describe "Net::HTTPHeader#set_form_data" do
+ it_behaves_like :net_httpheader_set_form_data, :set_form_data
+end
diff --git a/spec/ruby/library/net-http/httpheader/set_range_spec.rb b/spec/ruby/library/net-http/httpheader/set_range_spec.rb
new file mode 100644
index 0000000000..0f98de55e6
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/set_range_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/set_range'
+
+describe "Net::HTTPHeader#set_range" do
+ it_behaves_like :net_httpheader_set_range, :set_range
+end
diff --git a/spec/ruby/library/net/http/httpheader/shared/each_capitalized.rb b/spec/ruby/library/net-http/httpheader/shared/each_capitalized.rb
index 3bac409876..3bac409876 100644
--- a/spec/ruby/library/net/http/httpheader/shared/each_capitalized.rb
+++ b/spec/ruby/library/net-http/httpheader/shared/each_capitalized.rb
diff --git a/spec/ruby/library/net/http/httpheader/shared/each_header.rb b/spec/ruby/library/net-http/httpheader/shared/each_header.rb
index 6bf3a6ddfe..6bf3a6ddfe 100644
--- a/spec/ruby/library/net/http/httpheader/shared/each_header.rb
+++ b/spec/ruby/library/net-http/httpheader/shared/each_header.rb
diff --git a/spec/ruby/library/net/http/httpheader/shared/each_name.rb b/spec/ruby/library/net-http/httpheader/shared/each_name.rb
index efc6a09dfd..efc6a09dfd 100644
--- a/spec/ruby/library/net/http/httpheader/shared/each_name.rb
+++ b/spec/ruby/library/net-http/httpheader/shared/each_name.rb
diff --git a/spec/ruby/library/net/http/httpheader/shared/set_content_type.rb b/spec/ruby/library/net-http/httpheader/shared/set_content_type.rb
index b7359bdca6..b7359bdca6 100644
--- a/spec/ruby/library/net/http/httpheader/shared/set_content_type.rb
+++ b/spec/ruby/library/net-http/httpheader/shared/set_content_type.rb
diff --git a/spec/ruby/library/net/http/httpheader/shared/set_form_data.rb b/spec/ruby/library/net-http/httpheader/shared/set_form_data.rb
index db20b18803..db20b18803 100644
--- a/spec/ruby/library/net/http/httpheader/shared/set_form_data.rb
+++ b/spec/ruby/library/net-http/httpheader/shared/set_form_data.rb
diff --git a/spec/ruby/library/net/http/httpheader/shared/set_range.rb b/spec/ruby/library/net-http/httpheader/shared/set_range.rb
index 87f51d46f3..87f51d46f3 100644
--- a/spec/ruby/library/net/http/httpheader/shared/set_range.rb
+++ b/spec/ruby/library/net-http/httpheader/shared/set_range.rb
diff --git a/spec/ruby/library/net/http/httpheader/shared/size.rb b/spec/ruby/library/net-http/httpheader/shared/size.rb
index e2b1e4c22b..e2b1e4c22b 100644
--- a/spec/ruby/library/net/http/httpheader/shared/size.rb
+++ b/spec/ruby/library/net-http/httpheader/shared/size.rb
diff --git a/spec/ruby/library/net-http/httpheader/size_spec.rb b/spec/ruby/library/net-http/httpheader/size_spec.rb
new file mode 100644
index 0000000000..210060ce21
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/size_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/size'
+
+describe "Net::HTTPHeader#size" do
+ it_behaves_like :net_httpheader_size, :size
+end
diff --git a/spec/ruby/library/net-http/httpheader/sub_type_spec.rb b/spec/ruby/library/net-http/httpheader/sub_type_spec.rb
new file mode 100644
index 0000000000..b39b57fe8d
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/sub_type_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#sub_type" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns the 'sub-content-type', as per 'Content-Type' header entry" do
+ @headers["Content-Type"] = "text/html"
+ @headers.sub_type.should == "html"
+
+ @headers["Content-Type"] = "application/pdf"
+ @headers.sub_type.should == "pdf"
+
+ @headers["Content-Type"] = "text/html;charset=utf-8"
+ @headers.sub_type.should == "html"
+ end
+
+ it "returns nil if no 'sub-content-type' is set" do
+ @headers["Content-Type"] = "text"
+ @headers.sub_type.should be_nil
+
+ @headers["Content-Type"] = "text;charset=utf-8"
+ @headers.sub_type.should be_nil
+ end
+
+ it "returns nil if the 'Content-Type' header entry does not exist" do
+ @headers.sub_type.should be_nil
+ end
+end
diff --git a/spec/ruby/library/net-http/httpheader/to_hash_spec.rb b/spec/ruby/library/net-http/httpheader/to_hash_spec.rb
new file mode 100644
index 0000000000..3cebc519a6
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/to_hash_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#to_hash" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns a Hash representing all Header entries (keys in lower case, values as arrays)" do
+ @headers.to_hash.should == {}
+
+ @headers["My-Header"] = "test"
+ @headers.to_hash.should == { "my-header" => ["test"] }
+
+ @headers.add_field("My-Header", "another test")
+ @headers.to_hash.should == { "my-header" => ["test", "another test"] }
+ end
+
+ it "does not allow modifying the headers from the returned hash" do
+ @headers.to_hash["my-header"] = ["test"]
+ @headers.to_hash.should == {}
+ @headers.key?("my-header").should be_false
+ end
+end
diff --git a/spec/ruby/library/net-http/httpheader/type_params_spec.rb b/spec/ruby/library/net-http/httpheader/type_params_spec.rb
new file mode 100644
index 0000000000..ac97e2b48c
--- /dev/null
+++ b/spec/ruby/library/net-http/httpheader/type_params_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#type_params" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns additional 'Content-Type' information as a Hash" do
+ @headers["Content-Type"] = "text/html;charset=utf-8"
+ @headers.type_params.should == {"charset" => "utf-8"}
+
+ @headers["Content-Type"] = "text/html; charset=utf-8; rubyspec=rocks"
+ @headers.type_params.should == {"charset" => "utf-8", "rubyspec" => "rocks"}
+ end
+
+ it "returns an empty Hash when no additional 'Content-Type' information is set" do
+ @headers.type_params.should == {}
+
+ @headers["Content-Type"] = "text/html"
+ @headers.type_params.should == {}
+ end
+end
diff --git a/spec/ruby/library/net-http/httprequest/initialize_spec.rb b/spec/ruby/library/net-http/httprequest/initialize_spec.rb
new file mode 100644
index 0000000000..d009a00ed2
--- /dev/null
+++ b/spec/ruby/library/net-http/httprequest/initialize_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+module NetHTTPRequestSpecs
+ class TestRequest < Net::HTTPRequest
+ METHOD = "TEST"
+ REQUEST_HAS_BODY = false
+ RESPONSE_HAS_BODY = true
+ end
+end
+
+describe "Net::HTTPRequest#initialize" do
+ before :each do
+ @req = NetHTTPRequestSpecs::TestRequest.allocate
+ end
+
+ it "uses the METHOD constants to set the request method" do
+ @req.send(:initialize, "/some/path")
+ @req.method.should == "TEST"
+ end
+
+ it "uses the REQUEST_HAS_BODY to set whether the Request has a body or not" do
+ @req.send(:initialize, "/some/path")
+ @req.request_body_permitted?.should be_false
+ end
+
+ it "uses the RESPONSE_HAS_BODY to set whether the Response can have a body or not" do
+ @req.send(:initialize, "/some/path")
+ @req.response_body_permitted?.should be_true
+ end
+
+ describe "when passed path" do
+ it "sets self's path to the passed path" do
+ @req.send(:initialize, "/some/path")
+ @req.path.should == "/some/path"
+ end
+ end
+
+ describe "when passed path, headers" do
+ it "uses the passed headers Hash to initialize self's header entries" do
+ @req.send(:initialize, "/some/path", "Content-Type" => "text/html")
+ @req["Content-Type"].should == "text/html"
+ end
+ end
+end
diff --git a/spec/ruby/library/net-http/httpresponse/body_permitted_spec.rb b/spec/ruby/library/net-http/httpresponse/body_permitted_spec.rb
new file mode 100644
index 0000000000..68965de4a1
--- /dev/null
+++ b/spec/ruby/library/net-http/httpresponse/body_permitted_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse.body_permitted?" do
+ it "returns true if this response type can have a response body" do
+ Net::HTTPUnknownResponse.should.body_permitted?
+ Net::HTTPInformation.should_not.body_permitted?
+ Net::HTTPSuccess.should.body_permitted?
+ Net::HTTPRedirection.should.body_permitted?
+ Net::HTTPClientError.should.body_permitted?
+ Net::HTTPServerError.should.body_permitted?
+ end
+end
diff --git a/spec/ruby/library/net-http/httpresponse/body_spec.rb b/spec/ruby/library/net-http/httpresponse/body_spec.rb
new file mode 100644
index 0000000000..ddfcd834c4
--- /dev/null
+++ b/spec/ruby/library/net-http/httpresponse/body_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'shared/body'
+
+describe "Net::HTTPResponse#body" do
+ it_behaves_like :net_httpresponse_body, :body
+end
diff --git a/spec/ruby/library/net-http/httpresponse/code_spec.rb b/spec/ruby/library/net-http/httpresponse/code_spec.rb
new file mode 100644
index 0000000000..699062ad97
--- /dev/null
+++ b/spec/ruby/library/net-http/httpresponse/code_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#code" do
+ it "returns the result code string" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ res.code.should == "???"
+
+ res = Net::HTTPInformation.new("1.0", "1xx", "test response")
+ res.code.should == "1xx"
+
+ res = Net::HTTPSuccess.new("1.0", "2xx", "test response")
+ res.code.should == "2xx"
+
+ res = Net::HTTPRedirection.new("1.0", "3xx", "test response")
+ res.code.should == "3xx"
+
+ res = Net::HTTPClientError.new("1.0", "4xx", "test response")
+ res.code.should == "4xx"
+
+ res = Net::HTTPServerError.new("1.0", "5xx", "test response")
+ res.code.should == "5xx"
+ end
+end
diff --git a/spec/ruby/library/net-http/httpresponse/code_type_spec.rb b/spec/ruby/library/net-http/httpresponse/code_type_spec.rb
new file mode 100644
index 0000000000..beb661cbbe
--- /dev/null
+++ b/spec/ruby/library/net-http/httpresponse/code_type_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#code_type" do
+ it "returns self's class" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ res.code_type.should == Net::HTTPUnknownResponse
+
+ res = Net::HTTPInformation.new("1.0", "1xx", "test response")
+ res.code_type.should == Net::HTTPInformation
+
+ res = Net::HTTPSuccess.new("1.0", "2xx", "test response")
+ res.code_type.should == Net::HTTPSuccess
+
+ res = Net::HTTPRedirection.new("1.0", "3xx", "test response")
+ res.code_type.should == Net::HTTPRedirection
+
+ res = Net::HTTPClientError.new("1.0", "4xx", "test response")
+ res.code_type.should == Net::HTTPClientError
+
+ res = Net::HTTPServerError.new("1.0", "5xx", "test response")
+ res.code_type.should == Net::HTTPServerError
+ end
+end
diff --git a/spec/ruby/library/net-http/httpresponse/entity_spec.rb b/spec/ruby/library/net-http/httpresponse/entity_spec.rb
new file mode 100644
index 0000000000..ca8c4b29c0
--- /dev/null
+++ b/spec/ruby/library/net-http/httpresponse/entity_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require_relative 'shared/body'
+
+describe "Net::HTTPResponse#entity" do
+ it_behaves_like :net_httpresponse_body, :entity
+end
diff --git a/spec/ruby/library/net-http/httpresponse/error_spec.rb b/spec/ruby/library/net-http/httpresponse/error_spec.rb
new file mode 100644
index 0000000000..6ced90fa23
--- /dev/null
+++ b/spec/ruby/library/net-http/httpresponse/error_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#error!" do
+ it "raises self's class 'EXCEPTION_TYPE' Exception" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ -> { res.error! }.should raise_error(Net::HTTPError)
+
+ res = Net::HTTPInformation.new("1.0", "1xx", "test response")
+ -> { res.error! }.should raise_error(Net::HTTPError)
+
+ res = Net::HTTPSuccess.new("1.0", "2xx", "test response")
+ -> { res.error! }.should raise_error(Net::HTTPError)
+
+ res = Net::HTTPRedirection.new("1.0", "3xx", "test response")
+ -> { res.error! }.should raise_error(Net::HTTPRetriableError)
+
+ res = Net::HTTPClientError.new("1.0", "4xx", "test response")
+ -> { res.error! }.should raise_error(Net::HTTPClientException)
+
+ res = Net::HTTPServerError.new("1.0", "5xx", "test response")
+ -> { res.error! }.should raise_error(Net::HTTPFatalError)
+ end
+end
diff --git a/spec/ruby/library/net-http/httpresponse/error_type_spec.rb b/spec/ruby/library/net-http/httpresponse/error_type_spec.rb
new file mode 100644
index 0000000000..3969621a5e
--- /dev/null
+++ b/spec/ruby/library/net-http/httpresponse/error_type_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#error_type" do
+ it "returns self's class 'EXCEPTION_TYPE' constant" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ res.error_type.should == Net::HTTPError
+
+ res = Net::HTTPInformation.new("1.0", "1xx", "test response")
+ res.error_type.should == Net::HTTPError
+
+ res = Net::HTTPSuccess.new("1.0", "2xx", "test response")
+ res.error_type.should == Net::HTTPError
+
+ res = Net::HTTPRedirection.new("1.0", "3xx", "test response")
+ res.error_type.should == Net::HTTPRetriableError
+
+ res = Net::HTTPClientError.new("1.0", "4xx", "test response")
+ res.error_type.should == Net::HTTPClientException
+
+ res = Net::HTTPServerError.new("1.0", "5xx", "test response")
+ res.error_type.should == Net::HTTPFatalError
+ end
+end
diff --git a/spec/ruby/library/net-http/httpresponse/exception_type_spec.rb b/spec/ruby/library/net-http/httpresponse/exception_type_spec.rb
new file mode 100644
index 0000000000..dd2761a744
--- /dev/null
+++ b/spec/ruby/library/net-http/httpresponse/exception_type_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse.exception_type" do
+ it "returns self's 'EXCEPTION_TYPE' constant" do
+ Net::HTTPUnknownResponse.exception_type.should == Net::HTTPError
+ Net::HTTPInformation.exception_type.should == Net::HTTPError
+ Net::HTTPSuccess.exception_type.should == Net::HTTPError
+ Net::HTTPRedirection.exception_type.should == Net::HTTPRetriableError
+ Net::HTTPClientError.exception_type.should == Net::HTTPClientException
+ Net::HTTPServerError.exception_type.should == Net::HTTPFatalError
+ end
+end
diff --git a/spec/ruby/library/net-http/httpresponse/header_spec.rb b/spec/ruby/library/net-http/httpresponse/header_spec.rb
new file mode 100644
index 0000000000..a403dbd2c3
--- /dev/null
+++ b/spec/ruby/library/net-http/httpresponse/header_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#header" do
+ it "returns self" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ res.response.should equal(res)
+ end
+end
diff --git a/spec/ruby/library/net-http/httpresponse/http_version_spec.rb b/spec/ruby/library/net-http/httpresponse/http_version_spec.rb
new file mode 100644
index 0000000000..a3e413a360
--- /dev/null
+++ b/spec/ruby/library/net-http/httpresponse/http_version_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#http_version" do
+ it "returns self's http version" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ res.http_version.should == "1.0"
+
+ res = Net::HTTPUnknownResponse.new("1.1", "???", "test response")
+ res.http_version.should == "1.1"
+ end
+end
diff --git a/spec/ruby/library/net-http/httpresponse/initialize_spec.rb b/spec/ruby/library/net-http/httpresponse/initialize_spec.rb
new file mode 100644
index 0000000000..673c11a245
--- /dev/null
+++ b/spec/ruby/library/net-http/httpresponse/initialize_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#initialize when passed http_version, response_code, response_message" do
+ it "sets self http_version, response_code and response_message to the passed values" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ res.http_version.should == "1.0"
+ res.code.should == "???"
+ res.message.should == "test response"
+ end
+end
diff --git a/spec/ruby/library/net-http/httpresponse/inspect_spec.rb b/spec/ruby/library/net-http/httpresponse/inspect_spec.rb
new file mode 100644
index 0000000000..43071ec8cd
--- /dev/null
+++ b/spec/ruby/library/net-http/httpresponse/inspect_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require "stringio"
+
+describe "Net::HTTPResponse#inspect" do
+ it "returns a String representation of self" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ res.inspect.should == "#<Net::HTTPUnknownResponse ??? test response readbody=false>"
+
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ socket = Net::BufferedIO.new(StringIO.new("test body"))
+ res.reading_body(socket, true) {}
+ res.inspect.should == "#<Net::HTTPUnknownResponse ??? test response readbody=true>"
+ end
+end
diff --git a/spec/ruby/library/net-http/httpresponse/message_spec.rb b/spec/ruby/library/net-http/httpresponse/message_spec.rb
new file mode 100644
index 0000000000..5ba73bb449
--- /dev/null
+++ b/spec/ruby/library/net-http/httpresponse/message_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#message" do
+ it "returns self's response message" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ res.message.should == "test response"
+ end
+end
diff --git a/spec/ruby/library/net-http/httpresponse/msg_spec.rb b/spec/ruby/library/net-http/httpresponse/msg_spec.rb
new file mode 100644
index 0000000000..04f5836d7a
--- /dev/null
+++ b/spec/ruby/library/net-http/httpresponse/msg_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#msg" do
+ it "returns self's response message" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ res.message.should == "test response"
+ end
+end
diff --git a/spec/ruby/library/net-http/httpresponse/read_body_spec.rb b/spec/ruby/library/net-http/httpresponse/read_body_spec.rb
new file mode 100644
index 0000000000..4530a26bfc
--- /dev/null
+++ b/spec/ruby/library/net-http/httpresponse/read_body_spec.rb
@@ -0,0 +1,86 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require 'stringio'
+
+describe "Net::HTTPResponse#read_body" do
+ before :each do
+ @res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ @socket = Net::BufferedIO.new(StringIO.new("test body"))
+ end
+
+ describe "when passed no arguments" do
+ it "returns the read body" do
+ @res.reading_body(@socket, true) do
+ @res.read_body.should == "test body"
+ end
+ end
+
+ it "returns the previously read body if called a second time" do
+ @res.reading_body(@socket, true) do
+ @res.read_body.should equal(@res.read_body)
+ end
+ end
+ end
+
+ describe "when passed a buffer" do
+ it "reads the body to the passed buffer" do
+ @res.reading_body(@socket, true) do
+ buffer = +""
+ @res.read_body(buffer)
+ buffer.should == "test body"
+ end
+ end
+
+ it "returns the passed buffer" do
+ @res.reading_body(@socket, true) do
+ buffer = +""
+ @res.read_body(buffer).should equal(buffer)
+ end
+ end
+
+ it "raises an IOError if called a second time" do
+ @res.reading_body(@socket, true) do
+ @res.read_body(+"")
+ -> { @res.read_body(+"") }.should raise_error(IOError)
+ end
+ end
+ end
+
+ describe "when passed a block" do
+ it "reads the body and yields it to the passed block (in chunks)" do
+ @res.reading_body(@socket, true) do
+ yielded = false
+
+ buffer = +""
+ @res.read_body do |body|
+ yielded = true
+ buffer << body
+ end
+
+ yielded.should be_true
+ buffer.should == "test body"
+ end
+ end
+
+ it "returns the ReadAdapter" do
+ @res.reading_body(@socket, true) do
+ @res.read_body { nil }.should be_kind_of(Net::ReadAdapter)
+ end
+ end
+
+ it "raises an IOError if called a second time" do
+ @res.reading_body(@socket, true) do
+ @res.read_body {}
+ -> { @res.read_body {} }.should raise_error(IOError)
+ end
+ end
+ end
+
+ describe "when passed buffer and block" do
+ it "raises an ArgumentError" do
+ @res.reading_body(@socket, true) do
+ -> { @res.read_body(+"") {} }.should raise_error(ArgumentError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/net-http/httpresponse/read_header_spec.rb b/spec/ruby/library/net-http/httpresponse/read_header_spec.rb
new file mode 100644
index 0000000000..3ea4ee834b
--- /dev/null
+++ b/spec/ruby/library/net-http/httpresponse/read_header_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#read_header" do
+ it "returns self" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ res.response.should equal(res)
+ end
+end
diff --git a/spec/ruby/library/net-http/httpresponse/read_new_spec.rb b/spec/ruby/library/net-http/httpresponse/read_new_spec.rb
new file mode 100644
index 0000000000..82f7a47ce8
--- /dev/null
+++ b/spec/ruby/library/net-http/httpresponse/read_new_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require 'stringio'
+
+describe "Net::HTTPResponse.read_new" do
+ it "creates a HTTPResponse object based on the response read from the passed socket" do
+ socket = Net::BufferedIO.new(StringIO.new(<<EOS))
+HTTP/1.1 200 OK
+Content-Type: text/html; charset=utf-8
+
+test-body
+EOS
+ response = Net::HTTPResponse.read_new(socket)
+
+ response.should be_kind_of(Net::HTTPOK)
+ response.code.should == "200"
+ response["Content-Type"].should == "text/html; charset=utf-8"
+
+ response.reading_body(socket, true) do
+ response.body.should == "test-body\n"
+ end
+ end
+end
diff --git a/spec/ruby/library/net-http/httpresponse/reading_body_spec.rb b/spec/ruby/library/net-http/httpresponse/reading_body_spec.rb
new file mode 100644
index 0000000000..637a2806f8
--- /dev/null
+++ b/spec/ruby/library/net-http/httpresponse/reading_body_spec.rb
@@ -0,0 +1,58 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+require "stringio"
+
+describe "Net::HTTPResponse#reading_body" do
+ before :each do
+ @res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ @socket = Net::BufferedIO.new(StringIO.new("test body"))
+ end
+
+ describe "when body_allowed is true" do
+ it "reads and returns the response body for self from the passed socket" do
+ @res.reading_body(@socket, true) {}.should == "test body"
+ @res.body.should == "test body"
+ end
+
+ it "yields the passed block before reading the body" do
+ yielded = false
+
+ @res.reading_body(@socket, true) do
+ @res.inspect.should == "#<Net::HTTPUnknownResponse ??? test response readbody=false>"
+ yielded = true
+ end
+
+ yielded.should be_true
+ end
+
+ describe "but the response type is not allowed to have a body" do
+ before :each do
+ @res = Net::HTTPInformation.new("1.0", "???", "test response")
+ end
+
+ it "returns nil" do
+ @res.reading_body(@socket, false) {}.should be_nil
+ @res.body.should be_nil
+ end
+
+ it "yields the passed block" do
+ yielded = false
+ @res.reading_body(@socket, true) { yielded = true }
+ yielded.should be_true
+ end
+ end
+ end
+
+ describe "when body_allowed is false" do
+ it "returns nil" do
+ @res.reading_body(@socket, false) {}.should be_nil
+ @res.body.should be_nil
+ end
+
+ it "yields the passed block" do
+ yielded = false
+ @res.reading_body(@socket, true) { yielded = true }
+ yielded.should be_true
+ end
+ end
+end
diff --git a/spec/ruby/library/net-http/httpresponse/response_spec.rb b/spec/ruby/library/net-http/httpresponse/response_spec.rb
new file mode 100644
index 0000000000..caa0ca2d19
--- /dev/null
+++ b/spec/ruby/library/net-http/httpresponse/response_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#response" do
+ it "returns self" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ res.response.should equal(res)
+ end
+end
diff --git a/spec/ruby/library/net/http/httpresponse/shared/body.rb b/spec/ruby/library/net-http/httpresponse/shared/body.rb
index 618e3936fb..618e3936fb 100644
--- a/spec/ruby/library/net/http/httpresponse/shared/body.rb
+++ b/spec/ruby/library/net-http/httpresponse/shared/body.rb
diff --git a/spec/ruby/library/net-http/httpresponse/value_spec.rb b/spec/ruby/library/net-http/httpresponse/value_spec.rb
new file mode 100644
index 0000000000..2df8beaa10
--- /dev/null
+++ b/spec/ruby/library/net-http/httpresponse/value_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#value" do
+ it "raises an HTTP error for non 2xx HTTP Responses" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ -> { res.value }.should raise_error(Net::HTTPError)
+
+ res = Net::HTTPInformation.new("1.0", "1xx", "test response")
+ -> { res.value }.should raise_error(Net::HTTPError)
+
+ res = Net::HTTPSuccess.new("1.0", "2xx", "test response")
+ -> { res.value }.should_not raise_error(Net::HTTPError)
+
+ res = Net::HTTPRedirection.new("1.0", "3xx", "test response")
+ -> { res.value }.should raise_error(Net::HTTPRetriableError)
+
+ res = Net::HTTPClientError.new("1.0", "4xx", "test response")
+ -> { res.value }.should raise_error(Net::HTTPClientException)
+
+ res = Net::HTTPServerError.new("1.0", "5xx", "test response")
+ -> { res.value }.should raise_error(Net::HTTPFatalError)
+ end
+end
diff --git a/spec/ruby/library/net/FTPError_spec.rb b/spec/ruby/library/net/FTPError_spec.rb
deleted file mode 100644
index 84128511db..0000000000
--- a/spec/ruby/library/net/FTPError_spec.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require_relative '../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require 'net/ftp'
-
- describe "Net::FTPError" do
- it "is an Exception" do
- Net::FTPError.should < Exception
- end
- end
-end
diff --git a/spec/ruby/library/net/FTPPermError_spec.rb b/spec/ruby/library/net/FTPPermError_spec.rb
deleted file mode 100644
index 0da35e7d82..0000000000
--- a/spec/ruby/library/net/FTPPermError_spec.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require_relative '../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require 'net/ftp'
-
- describe "Net::FTPPermError" do
- it "is an Exception" do
- Net::FTPPermError.should < Exception
- end
-
- it "is a subclass of Net::FTPError" do
- Net::FTPPermError.should < Net::FTPError
- end
- end
-end
diff --git a/spec/ruby/library/net/FTPProtoError_spec.rb b/spec/ruby/library/net/FTPProtoError_spec.rb
deleted file mode 100644
index 20e30dd2dd..0000000000
--- a/spec/ruby/library/net/FTPProtoError_spec.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require_relative '../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require 'net/ftp'
-
- describe "Net::FTPProtoError" do
- it "is an Exception" do
- Net::FTPProtoError.should < Exception
- end
-
- it "is a subclass of Net::FTPError" do
- Net::FTPPermError.should < Net::FTPError
- end
- end
-end
diff --git a/spec/ruby/library/net/FTPReplyError_spec.rb b/spec/ruby/library/net/FTPReplyError_spec.rb
deleted file mode 100644
index cc774aabe5..0000000000
--- a/spec/ruby/library/net/FTPReplyError_spec.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require_relative '../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require 'net/ftp'
-
- describe "Net::FTPReplyError" do
- it "is an Exception" do
- Net::FTPReplyError.should < Exception
- end
-
- it "is a subclass of Net::FTPError" do
- Net::FTPPermError.should < Net::FTPError
- end
- end
-end
diff --git a/spec/ruby/library/net/FTPTempError_spec.rb b/spec/ruby/library/net/FTPTempError_spec.rb
deleted file mode 100644
index e2fbdfd842..0000000000
--- a/spec/ruby/library/net/FTPTempError_spec.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require_relative '../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require 'net/ftp'
-
- describe "Net::FTPTempError" do
- it "is an Exception" do
- Net::FTPTempError.should < Exception
- end
-
- it "is a subclass of Net::FTPError" do
- Net::FTPPermError.should < Net::FTPError
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/abort_spec.rb b/spec/ruby/library/net/ftp/abort_spec.rb
deleted file mode 100644
index ebdfed4b16..0000000000
--- a/spec/ruby/library/net/ftp/abort_spec.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#abort" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- it "sends the ABOR command to the server" do
- -> { @ftp.abort }.should_not raise_error
- end
-
- it "ignores the response" do
- @ftp.abort
- @ftp.last_response.should == "220 Dummy FTP Server ready!\n"
- end
-
- it "returns the full response" do
- @ftp.abort.should == "226 Closing data connection. (ABOR)\n"
- end
-
- it "does not raise any error when the response code is 225" do
- @server.should_receive(:abor).and_respond("225 Data connection open; no transfer in progress.")
- -> { @ftp.abort }.should_not raise_error
- end
-
- it "does not raise any error when the response code is 226" do
- @server.should_receive(:abor).and_respond("226 Closing data connection.")
- -> { @ftp.abort }.should_not raise_error
- end
-
- it "raises a Net::FTPProtoError when the response code is 500" do
- @server.should_receive(:abor).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.abort }.should raise_error(Net::FTPProtoError)
- end
-
- it "raises a Net::FTPProtoError when the response code is 501" do
- @server.should_receive(:abor).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.abort }.should raise_error(Net::FTPProtoError)
- end
-
- it "raises a Net::FTPProtoError when the response code is 502" do
- @server.should_receive(:abor).and_respond("502 Command not implemented.")
- -> { @ftp.abort }.should raise_error(Net::FTPProtoError)
- end
-
- it "raises a Net::FTPProtoError when the response code is 421" do
- @server.should_receive(:abor).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.abort }.should raise_error(Net::FTPProtoError)
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/acct_spec.rb b/spec/ruby/library/net/ftp/acct_spec.rb
deleted file mode 100644
index a960ae20a4..0000000000
--- a/spec/ruby/library/net/ftp/acct_spec.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#acct" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- it "writes the ACCT command to the server" do
- @ftp.acct("my_account")
- @ftp.last_response.should == "230 User 'my_account' logged in, proceed. (ACCT)\n"
- end
-
- it "returns nil" do
- @ftp.acct("my_account").should == nil
- end
-
- it "does not raise any error when the response code is 230" do
- @server.should_receive(:acct).and_respond("230 User logged in, proceed.")
- -> { @ftp.acct("my_account") }.should_not raise_error
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:acct).and_respond("530 Not logged in.")
- -> { @ftp.acct("my_account") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:acct).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.acct("my_account") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:acct).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.acct("my_account") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 503" do
- @server.should_receive(:acct).and_respond("503 Bad sequence of commands.")
- -> { @ftp.acct("my_account") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:acct).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.acct("my_account") }.should raise_error(Net::FTPTempError)
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/binary_spec.rb b/spec/ruby/library/net/ftp/binary_spec.rb
deleted file mode 100644
index da7e2d6c14..0000000000
--- a/spec/ruby/library/net/ftp/binary_spec.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
-
- describe "Net::FTP#binary" do
- it "returns true when self is in binary mode" do
- ftp = Net::FTP.new
- ftp.binary.should be_true
-
- ftp.binary = false
- ftp.binary.should be_false
- end
- end
-
- describe "Net::FTP#binary=" do
- it "sets self to binary mode when passed true" do
- ftp = Net::FTP.new
-
- ftp.binary = true
- ftp.binary.should be_true
-
- ftp.binary = false
- ftp.binary.should be_false
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/chdir_spec.rb b/spec/ruby/library/net/ftp/chdir_spec.rb
deleted file mode 100644
index 741c3c845e..0000000000
--- a/spec/ruby/library/net/ftp/chdir_spec.rb
+++ /dev/null
@@ -1,102 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#chdir" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- describe "when switching to the parent directory" do
- it "sends the 'CDUP' command to the server" do
- @ftp.chdir("..")
- @ftp.last_response.should == "200 Command okay. (CDUP)\n"
- end
-
- it "returns nil" do
- @ftp.chdir("..").should be_nil
- end
-
- it "does not raise a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:cdup).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.chdir("..") }.should_not raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:cdup).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.chdir("..") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 502" do
- @server.should_receive(:cdup).and_respond("502 Command not implemented.")
- -> { @ftp.chdir("..") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:cdup).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.chdir("..") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:cdup).and_respond("530 Not logged in.")
- -> { @ftp.chdir("..") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 550" do
- @server.should_receive(:cdup).and_respond("550 Requested action not taken.")
- -> { @ftp.chdir("..") }.should raise_error(Net::FTPPermError)
- end
- end
-
- it "writes the 'CWD' command with the passed directory to the socket" do
- @ftp.chdir("test")
- @ftp.last_response.should == "200 Command okay. (CWD test)\n"
- end
-
- it "returns nil" do
- @ftp.chdir("test").should be_nil
- end
-
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:cwd).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.chdir("test") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:cwd).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.chdir("test") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 502" do
- @server.should_receive(:cwd).and_respond("502 Command not implemented.")
- -> { @ftp.chdir("test") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:cwd).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.chdir("test") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:cwd).and_respond("530 Not logged in.")
- -> { @ftp.chdir("test") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 550" do
- @server.should_receive(:cwd).and_respond("550 Requested action not taken.")
- -> { @ftp.chdir("test") }.should raise_error(Net::FTPPermError)
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/close_spec.rb b/spec/ruby/library/net/ftp/close_spec.rb
deleted file mode 100644
index 49cdf4dea7..0000000000
--- a/spec/ruby/library/net/ftp/close_spec.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
-
- describe "Net::FTP#close" do
- before :each do
- @socket = mock("Socket")
- @socket.stub!(:closed?).and_return(false)
- @socket.stub!(:read_timeout).and_return(60)
- @socket.stub!(:read_timeout=).and_return(3)
-
- @ftp = Net::FTP.new
- @ftp.instance_variable_set(:@sock, @socket)
- end
-
- it "closes the socket" do
- @socket.should_receive(:close)
- @ftp.close.should be_nil
- end
-
- it "does not try to close the socket if it has already been closed" do
- @socket.should_receive(:closed?).and_return(true)
- @socket.should_not_receive(:close)
- @ftp.close.should be_nil
- end
-
- it "does not try to close the socket if it is nil" do
- @ftp.instance_variable_set(:@sock, nil)
- @ftp.close.should be_nil
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/closed_spec.rb b/spec/ruby/library/net/ftp/closed_spec.rb
deleted file mode 100644
index a81917090a..0000000000
--- a/spec/ruby/library/net/ftp/closed_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
-
- describe "Net::FTP#closed?" do
- before :each do
- @socket = mock("Socket")
-
- @ftp = Net::FTP.new
- @ftp.instance_variable_set(:@sock, @socket)
- end
-
- it "returns true when the socket is closed" do
- @socket.should_receive(:closed?).and_return(true)
- @ftp.closed?.should be_true
- end
-
- it "returns true when the socket is nil" do
- @ftp.instance_variable_set(:@sock, nil)
- @ftp.closed?.should be_true
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/connect_spec.rb b/spec/ruby/library/net/ftp/connect_spec.rb
deleted file mode 100644
index b45e46c530..0000000000
--- a/spec/ruby/library/net/ftp/connect_spec.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- # TODO: Add specs for using the SOCKSSocket
- describe "Net::FTP#connect" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- end
-
- after :each do
- @server.connect_message = nil
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- it "tries to connect to the FTP Server on the given host and port" do
- -> { @ftp.connect(@server.hostname, @server.server_port) }.should_not raise_error
- end
-
- it "returns nil" do
- @ftp.connect(@server.hostname, @server.server_port).should be_nil
- end
-
- it "prints a small debug line when in debug mode" do
- @ftp.debug_mode = true
- -> { @ftp.connect(@server.hostname, @server.server_port) }.should output(/#{"connect: "}#{@server.hostname}#{", "}#{@server.server_port}#{"\\nget: 220 Dummy FTP Server ready!"}/)
- @ftp.debug_mode = false
- end
-
- it "does not raise any error when the response code is 220" do
- @server.connect_message = "220 Dummy FTP Server ready!"
- -> { @ftp.connect(@server.hostname, @server.server_port) }.should_not raise_error
- end
-
- it "raises a Net::FTPReplyError when the response code is 120" do
- @server.connect_message = "120 Service ready in nnn minutes."
- -> { @ftp.connect(@server.hostname, @server.server_port) }.should raise_error(Net::FTPReplyError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.connect_message = "421 Service not available, closing control connection."
- -> { @ftp.connect(@server.hostname, @server.server_port) }.should raise_error(Net::FTPTempError)
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/debug_mode_spec.rb b/spec/ruby/library/net/ftp/debug_mode_spec.rb
deleted file mode 100644
index 46d207bbea..0000000000
--- a/spec/ruby/library/net/ftp/debug_mode_spec.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
-
- describe "Net::FTP#debug_mode" do
- it "returns true when self is in debug mode" do
- ftp = Net::FTP.new
- ftp.debug_mode.should be_false
-
- ftp.debug_mode = true
- ftp.debug_mode.should be_true
- end
- end
-
- describe "Net::FTP#debug_mode=" do
- it "sets self into debug mode when passed true" do
- ftp = Net::FTP.new
- ftp.debug_mode = true
- ftp.debug_mode.should be_true
-
- ftp.debug_mode = false
- ftp.debug_mode.should be_false
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/default_passive_spec.rb b/spec/ruby/library/net/ftp/default_passive_spec.rb
deleted file mode 100644
index 9348d3294d..0000000000
--- a/spec/ruby/library/net/ftp/default_passive_spec.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
-
- describe "Net::FTP#default_passive" do
- it "is true by default" do
- ruby_exe(fixture(__FILE__, "default_passive.rb")).should == "true\ntrue\n"
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/delete_spec.rb b/spec/ruby/library/net/ftp/delete_spec.rb
deleted file mode 100644
index 43bfcc1541..0000000000
--- a/spec/ruby/library/net/ftp/delete_spec.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#delete" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- it "sends the DELE command with the passed filename to the server" do
- @ftp.delete("test.file")
- @ftp.last_response.should == "250 Requested file action okay, completed. (DELE test.file)\n"
- end
-
- it "raises a Net::FTPTempError when the response code is 450" do
- @server.should_receive(:dele).and_respond("450 Requested file action not taken.")
- -> { @ftp.delete("test.file") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 550" do
- @server.should_receive(:dele).and_respond("550 Requested action not taken.")
- -> { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:dele).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:dele).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 502" do
- @server.should_receive(:dele).and_respond("502 Command not implemented.")
- -> { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:dele).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.delete("test.file") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:dele).and_respond("530 Not logged in.")
- -> { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError)
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/dir_spec.rb b/spec/ruby/library/net/ftp/dir_spec.rb
deleted file mode 100644
index dce50a5ac5..0000000000
--- a/spec/ruby/library/net/ftp/dir_spec.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
- require_relative 'shared/list'
-
- describe "Net::FTP#dir" do
- it_behaves_like :net_ftp_list, :dir
- end
-end
diff --git a/spec/ruby/library/net/ftp/get_spec.rb b/spec/ruby/library/net/ftp/get_spec.rb
deleted file mode 100644
index 892b30061c..0000000000
--- a/spec/ruby/library/net/ftp/get_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
- require_relative 'shared/gettextfile'
- require_relative 'shared/getbinaryfile'
-
- describe "Net::FTP#get (binary mode)" do
- before :each do
- @binary_mode = true
- end
-
- it_behaves_like :net_ftp_getbinaryfile, :get
- end
-
- describe "Net::FTP#get (text mode)" do
- before :each do
- @binary_mode = false
- end
-
- it_behaves_like :net_ftp_gettextfile, :get
- end
-end
diff --git a/spec/ruby/library/net/ftp/getbinaryfile_spec.rb b/spec/ruby/library/net/ftp/getbinaryfile_spec.rb
deleted file mode 100644
index c5abdd67e7..0000000000
--- a/spec/ruby/library/net/ftp/getbinaryfile_spec.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
- require_relative 'shared/getbinaryfile'
-
- describe "Net::FTP#getbinaryfile" do
- it_behaves_like :net_ftp_getbinaryfile, :getbinaryfile
- end
-end
diff --git a/spec/ruby/library/net/ftp/getdir_spec.rb b/spec/ruby/library/net/ftp/getdir_spec.rb
deleted file mode 100644
index 8f6fca5bfb..0000000000
--- a/spec/ruby/library/net/ftp/getdir_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'shared/pwd'
-
- describe "Net::FTP#getdir" do
- it_behaves_like :net_ftp_pwd, :getdir
- end
-end
diff --git a/spec/ruby/library/net/ftp/gettextfile_spec.rb b/spec/ruby/library/net/ftp/gettextfile_spec.rb
deleted file mode 100644
index e272ae73b1..0000000000
--- a/spec/ruby/library/net/ftp/gettextfile_spec.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
- require_relative 'shared/gettextfile'
-
- describe "Net::FTP#gettextfile" do
- it_behaves_like :net_ftp_gettextfile, :gettextfile
- end
-end
diff --git a/spec/ruby/library/net/ftp/help_spec.rb b/spec/ruby/library/net/ftp/help_spec.rb
deleted file mode 100644
index 9b15f42ede..0000000000
--- a/spec/ruby/library/net/ftp/help_spec.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#help" do
- def with_connection
- yield
- end
-
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- it "writes the HELP command to the server" do
- @ftp.help
- @ftp.last_response.should == "211 System status, or system help reply. (HELP)\n"
- end
-
- it "returns the server's response" do
- @ftp.help.should == "211 System status, or system help reply. (HELP)\n"
- end
-
- it "writes the HELP command with an optional parameter to the socket" do
- @ftp.help("some parameter").should == "211 System status, or system help reply. (HELP some parameter)\n"
- end
-
- it "does not raise any error when the response code is 211" do
- @server.should_receive(:help).and_respond("211 System status, or system help reply.")
- -> { @ftp.help }.should_not raise_error
- end
-
- it "does not raise any error when the response code is 214" do
- @server.should_receive(:help).and_respond("214 Help message.")
- -> { @ftp.help }.should_not raise_error
- end
-
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:help).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.help }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:help).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.help }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 502" do
- @server.should_receive(:help).and_respond("502 Command not implemented.")
- -> { @ftp.help }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:help).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.help }.should raise_error(Net::FTPTempError)
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/initialize_spec.rb b/spec/ruby/library/net/ftp/initialize_spec.rb
deleted file mode 100644
index 80f71a9161..0000000000
--- a/spec/ruby/library/net/ftp/initialize_spec.rb
+++ /dev/null
@@ -1,408 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
-
- describe "Net::FTP#initialize" do
- before :each do
- @ftp = Net::FTP.allocate
- @ftp.stub!(:connect)
- @port_args = []
- @port_args << 21
- end
-
- it "is private" do
- Net::FTP.should have_private_instance_method(:initialize)
- end
-
- it "sets self into binary mode" do
- @ftp.binary.should be_nil
- @ftp.send(:initialize)
- @ftp.binary.should be_true
- end
-
- it "sets self into active mode" do
- @ftp.passive.should be_nil
- @ftp.send(:initialize)
- @ftp.passive.should be_false
- end
-
- it "sets self into non-debug mode" do
- @ftp.debug_mode.should be_nil
- @ftp.send(:initialize)
- @ftp.debug_mode.should be_false
- end
-
- it "sets self to not resume file uploads/downloads" do
- @ftp.resume.should be_nil
- @ftp.send(:initialize)
- @ftp.resume.should be_false
- end
-
- describe "when passed no arguments" do
- it "does not try to connect" do
- @ftp.should_not_receive(:connect)
- @ftp.send(:initialize)
- end
- end
-
- describe "when passed host" do
- it "tries to connect to the passed host" do
- @ftp.should_receive(:connect).with("localhost", *@port_args)
- @ftp.send(:initialize, "localhost")
- end
- end
-
- describe "when passed host, user" do
- it "tries to connect to the passed host" do
- @ftp.should_receive(:connect).with("localhost", *@port_args)
- @ftp.send(:initialize, "localhost")
- end
-
- it "tries to login with the passed username" do
- @ftp.should_receive(:login).with("rubyspec", nil, nil)
- @ftp.send(:initialize, "localhost", "rubyspec")
- end
- end
-
- describe "when passed host, user, password" do
- it "tries to connect to the passed host" do
- @ftp.should_receive(:connect).with("localhost", *@port_args)
- @ftp.send(:initialize, "localhost")
- end
-
- it "tries to login with the passed username and password" do
- @ftp.should_receive(:login).with("rubyspec", "rocks", nil)
- @ftp.send(:initialize, "localhost", "rubyspec", "rocks")
- end
- end
-
- describe "when passed host, user" do
- it "tries to connect to the passed host" do
- @ftp.should_receive(:connect).with("localhost", *@port_args)
- @ftp.send(:initialize, "localhost")
- end
-
- it "tries to login with the passed username, password and account" do
- @ftp.should_receive(:login).with("rubyspec", "rocks", "account")
- @ftp.send(:initialize, "localhost", "rubyspec", "rocks", "account")
- end
- end
-
- before :each do
- @ftp.stub!(:login)
- end
-
- describe 'when the host' do
- describe 'is set' do
- describe 'and port option' do
- describe 'is set' do
- it 'tries to connect to the host on the specified port' do
- options = mock('ftp initialize options')
- options.should_receive(:to_hash).and_return({ port: 8080 })
- @ftp.should_receive(:connect).with('localhost', 8080)
-
- @ftp.send(:initialize, 'localhost', options)
- end
- end
-
- describe 'is not set' do
- it 'tries to connect to the host without a port' do
- @ftp.should_receive(:connect).with("localhost", *@port_args)
-
- @ftp.send(:initialize, 'localhost')
- end
- end
- end
-
- describe 'when the username option' do
- describe 'is set' do
- describe 'and the password option' do
- describe 'is set' do
- describe 'and the account option' do
- describe 'is set' do
- it 'tries to log in with the supplied parameters' do
- options = mock('ftp initialize options')
- options.should_receive(:to_hash).and_return({ username: 'a', password: 'topsecret', account: 'b' })
- @ftp.should_receive(:login).with('a', 'topsecret', 'b')
-
- @ftp.send(:initialize, 'localhost', options)
- end
- end
-
- describe 'is unset' do
- it 'tries to log in with the supplied parameters' do
- options = mock('ftp initialize options')
- options.should_receive(:to_hash).and_return({ username: 'a', password: 'topsecret' })
- @ftp.should_receive(:login).with('a', 'topsecret', nil)
-
- @ftp.send(:initialize, 'localhost', options)
- end
- end
- end
- end
-
- describe 'is unset' do
- describe 'and the account option' do
- describe 'is set' do
- it 'tries to log in with the supplied parameters' do
- options = mock('ftp initialize options')
- options.should_receive(:to_hash).and_return({ username: 'a', account: 'b' })
- @ftp.should_receive(:login).with('a', nil, 'b')
-
- @ftp.send(:initialize, 'localhost', options)
- end
- end
-
- describe 'is unset' do
- it 'tries to log in with the supplied parameters' do
- options = mock('ftp initialize options')
- options.should_receive(:to_hash).and_return({ username: 'a'})
- @ftp.should_receive(:login).with('a', nil, nil)
-
- @ftp.send(:initialize, 'localhost', options)
- end
- end
- end
- end
- end
- end
-
- describe 'is not set' do
- it 'does not try to log in' do
- options = mock('ftp initialize options')
- options.should_receive(:to_hash).and_return({})
- @ftp.should_not_receive(:login)
-
- @ftp.send(:initialize, 'localhost', options)
- end
- end
- end
- end
-
- describe 'is unset' do
- it 'does not try to connect' do
- @ftp.should_not_receive(:connect)
-
- @ftp.send(:initialize)
- end
-
- it 'does not try to log in' do
- @ftp.should_not_receive(:login)
-
- @ftp.send(:initialize)
- end
- end
- end
-
- describe 'when the passive option' do
- describe 'is set' do
- describe 'to true' do
- it 'sets passive to true' do
- options = mock('ftp initialize options')
- options.should_receive(:to_hash).and_return({ passive: true })
-
- @ftp.send(:initialize, nil, options)
- @ftp.passive.should == true
- end
- end
-
- describe 'to false' do
- it 'sets passive to false' do
- options = mock('ftp initialize options')
- options.should_receive(:to_hash).and_return({ passive: false })
-
- @ftp.send(:initialize, nil, options)
- @ftp.passive.should == false
- end
- end
- end
-
- describe 'is unset' do
- it 'sets passive to false' do
- @ftp.send(:initialize)
- @ftp.passive.should == false
- end
- end
- end
-
- describe 'when the debug_mode option' do
- describe 'is set' do
- describe 'to true' do
- it 'sets debug_mode to true' do
- options = mock('ftp initialize options')
- options.should_receive(:to_hash).and_return({ debug_mode: true })
-
- @ftp.send(:initialize, nil, options)
- @ftp.debug_mode.should == true
- end
- end
-
- describe 'to false' do
- it 'sets debug_mode to false' do
- options = mock('ftp initialize options')
- options.should_receive(:to_hash).and_return({ debug_mode: false })
-
- @ftp.send(:initialize, nil, options)
- @ftp.debug_mode.should == false
- end
- end
- end
-
- describe 'is unset' do
- it 'sets debug_mode to false' do
- @ftp.send(:initialize)
- @ftp.debug_mode.should == false
- end
- end
- end
-
- describe 'when the open_timeout option' do
- describe 'is set' do
- it 'sets open_timeout to the specified value' do
- options = mock('ftp initialize options')
- options.should_receive(:to_hash).and_return({ open_timeout: 42 })
-
- @ftp.send(:initialize, nil, options)
- @ftp.open_timeout.should == 42
- end
- end
-
- describe 'is not set' do
- it 'sets open_timeout to nil' do
- @ftp.send(:initialize)
- @ftp.open_timeout.should == nil
- end
- end
- end
-
- describe 'when the read_timeout option' do
- describe 'is set' do
- it 'sets read_timeout to the specified value' do
- options = mock('ftp initialize options')
- options.should_receive(:to_hash).and_return({ read_timeout: 100 })
-
- @ftp.send(:initialize, nil, options)
- @ftp.read_timeout.should == 100
- end
- end
-
- describe 'is not set' do
- it 'sets read_timeout to the default value' do
- @ftp.send(:initialize)
- @ftp.read_timeout.should == 60
- end
- end
- end
-
- describe 'when the ssl_handshake_timeout option' do
- describe 'is set' do
- it 'sets ssl_handshake_timeout to the specified value' do
- options = mock('ftp initialize options')
- options.should_receive(:to_hash).and_return({ ssl_handshake_timeout: 23 })
-
- @ftp.send(:initialize, nil, options)
- @ftp.ssl_handshake_timeout.should == 23
- end
- end
-
- describe 'is not set' do
- it 'sets ssl_handshake_timeout to nil' do
- @ftp.send(:initialize)
- @ftp.ssl_handshake_timeout.should == nil
- end
- end
- end
-
- describe 'when the ssl option' do
- describe 'is set' do
- describe "and the ssl option's value is true" do
- it 'initializes ssl_context to a blank SSLContext object' do
- options = mock('ftp initialize options')
- options.should_receive(:to_hash).and_return({ ssl: true })
-
- ssl_context = OpenSSL::SSL::SSLContext.allocate
- ssl_context.stub!(:set_params)
-
- OpenSSL::SSL::SSLContext.should_receive(:new).and_return(ssl_context)
- ssl_context.should_receive(:set_params).with({})
-
- @ftp.send(:initialize, nil, options)
- @ftp.instance_variable_get(:@ssl_context).should == ssl_context
- end
- end
-
- describe "and the ssl option's value is a hash" do
- it 'initializes ssl_context to a configured SSLContext object' do
- options = mock('ftp initialize options')
- options.should_receive(:to_hash).and_return({ ssl: {key: 'value'} })
-
- ssl_context = OpenSSL::SSL::SSLContext.allocate
- ssl_context.stub!(:set_params)
-
- OpenSSL::SSL::SSLContext.should_receive(:new).and_return(ssl_context)
- ssl_context.should_receive(:set_params).with({key: 'value'})
-
- @ftp.send(:initialize, nil, options)
- @ftp.instance_variable_get(:@ssl_context).should == ssl_context
- end
- end
-
- describe 'and private_data_connection' do
- describe 'is set' do
- it 'sets private_data_connection to that value' do
- options = mock('ftp initialize options')
- options.should_receive(:to_hash).and_return({ ssl: true, private_data_connection: 'true' })
-
- @ftp.send(:initialize, nil, options)
- @ftp.instance_variable_get(:@private_data_connection).should == 'true'
- end
- end
-
- describe 'is not set' do
- it 'sets private_data_connection to nil' do
- options = mock('ftp initialize options')
- options.should_receive(:to_hash).and_return({ ssl: true })
-
- @ftp.send(:initialize, nil, options)
- @ftp.instance_variable_get(:@private_data_connection).should == true
- end
- end
- end
- end
-
- describe 'is not set' do
- it 'sets ssl_context to nil' do
- options = mock('ftp initialize options')
- options.should_receive(:to_hash).and_return({})
-
- @ftp.send(:initialize, nil, options)
- @ftp.instance_variable_get(:@ssl_context).should == nil
- end
-
- describe 'private_data_connection' do
- describe 'is set' do
- it 'raises an ArgumentError' do
- options = mock('ftp initialize options')
- options.should_receive(:to_hash).and_return({ private_data_connection: true })
-
- -> {
- @ftp.send(:initialize, nil, options)
- }.should raise_error(ArgumentError, /private_data_connection can be set to true only when ssl is enabled/)
- end
- end
-
- describe 'is not set' do
- it 'sets private_data_connection to false' do
- options = mock('ftp initialize options')
- options.should_receive(:to_hash).and_return({})
-
- @ftp.send(:initialize, nil, options)
- @ftp.instance_variable_get(:@private_data_connection).should == false
- end
- end
- end
- end
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/last_response_code_spec.rb b/spec/ruby/library/net/ftp/last_response_code_spec.rb
deleted file mode 100644
index 86f2b9a695..0000000000
--- a/spec/ruby/library/net/ftp/last_response_code_spec.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'shared/last_response_code'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#last_response_code" do
- it_behaves_like :net_ftp_last_response_code, :last_response_code
- end
-end
diff --git a/spec/ruby/library/net/ftp/last_response_spec.rb b/spec/ruby/library/net/ftp/last_response_spec.rb
deleted file mode 100644
index 1d29b9b73f..0000000000
--- a/spec/ruby/library/net/ftp/last_response_spec.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#last_response" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- it "returns the last response" do
- @ftp.last_response.should == "220 Dummy FTP Server ready!\n"
- @ftp.help
- @ftp.last_response.should == "211 System status, or system help reply. (HELP)\n"
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/lastresp_spec.rb b/spec/ruby/library/net/ftp/lastresp_spec.rb
deleted file mode 100644
index 9d26efb8f8..0000000000
--- a/spec/ruby/library/net/ftp/lastresp_spec.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'shared/last_response_code'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#lastresp" do
- it_behaves_like :net_ftp_last_response_code, :lastresp
- end
-end
diff --git a/spec/ruby/library/net/ftp/list_spec.rb b/spec/ruby/library/net/ftp/list_spec.rb
deleted file mode 100644
index 6cffafeb4f..0000000000
--- a/spec/ruby/library/net/ftp/list_spec.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
- require_relative 'shared/list'
-
- describe "Net::FTP#list" do
- it_behaves_like :net_ftp_list, :list
- end
-end
diff --git a/spec/ruby/library/net/ftp/login_spec.rb b/spec/ruby/library/net/ftp/login_spec.rb
deleted file mode 100644
index 981b439082..0000000000
--- a/spec/ruby/library/net/ftp/login_spec.rb
+++ /dev/null
@@ -1,198 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#login" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- describe "when passed no arguments" do
- it "sends the USER command with 'anonymous' as name to the server" do
- @ftp.login
- @server.login_user.should == "anonymous"
- end
-
- it "sends 'anonymous@' as a password when required" do
- @server.should_receive(:user).and_respond("331 User name okay, need password.")
- @ftp.login
- @server.login_pass.should == "anonymous@"
- end
-
- it "raises a Net::FTPReplyError when the server requests an account" do
- @server.should_receive(:user).and_respond("331 User name okay, need password.")
- @server.should_receive(:pass).and_respond("332 Need account for login.")
- -> { @ftp.login }.should raise_error(Net::FTPReplyError)
- end
- end
-
- describe "when passed name" do
- it "sends the USER command with the passed name to the server" do
- @ftp.login("rubyspec")
- @server.login_user.should == "rubyspec"
- end
-
- it "raises a Net::FTPReplyError when the server requests a password, but none was given" do
- @server.should_receive(:user).and_respond("331 User name okay, need password.")
- -> { @ftp.login("rubyspec") }.should raise_error(Net::FTPReplyError)
- end
-
- it "raises a Net::FTPReplyError when the server requests an account, but none was given" do
- @server.should_receive(:user).and_respond("331 User name okay, need password.")
- @server.should_receive(:pass).and_respond("332 Need account for login.")
- -> { @ftp.login("rubyspec") }.should raise_error(Net::FTPReplyError)
- end
- end
-
- describe "when passed name, password" do
- it "sends the USER command with the passed name to the server" do
- @ftp.login("rubyspec", "rocks")
- @server.login_user.should == "rubyspec"
- end
-
- it "sends the passed password when required" do
- @server.should_receive(:user).and_respond("331 User name okay, need password.")
- @ftp.login("rubyspec", "rocks")
- @server.login_pass.should == "rocks"
- end
-
- it "raises a Net::FTPReplyError when the server requests an account" do
- @server.should_receive(:user).and_respond("331 User name okay, need password.")
- @server.should_receive(:pass).and_respond("332 Need account for login.")
- -> { @ftp.login("rubyspec", "rocks") }.should raise_error(Net::FTPReplyError)
- end
- end
-
- describe "when passed name, password, account" do
- it "sends the USER command with the passed name to the server" do
- @ftp.login("rubyspec", "rocks", "account")
- @server.login_user.should == "rubyspec"
- end
-
- it "sends the passed password when required" do
- @server.should_receive(:user).and_respond("331 User name okay, need password.")
- @ftp.login("rubyspec", "rocks", "account")
- @server.login_pass.should == "rocks"
- end
-
- it "sends the passed account when required" do
- @server.should_receive(:user).and_respond("331 User name okay, need password.")
- @server.should_receive(:pass).and_respond("332 Need account for login.")
- @ftp.login("rubyspec", "rocks", "account")
- @server.login_acct.should == "account"
- end
- end
-
- describe "when the USER command fails" do
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:user).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:user).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 502" do
- @server.should_receive(:user).and_respond("502 Command not implemented.")
- -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:user).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:user).and_respond("530 Not logged in.")
- -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
- end
- end
-
- describe "when the PASS command fails" do
- before :each do
- @server.should_receive(:user).and_respond("331 User name okay, need password.")
- end
-
- it "does not raise an Error when the response code is 202" do
- @server.should_receive(:pass).and_respond("202 Command not implemented, superfluous at this site.")
- -> { @ftp.login("rubyspec", "rocks", "account") }.should_not raise_error
- end
-
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:pass).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:pass).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 502" do
- @server.should_receive(:pass).and_respond("502 Command not implemented.")
- -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:pass).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:pass).and_respond("530 Not logged in.")
- -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
- end
- end
-
- describe "when the ACCT command fails" do
- before :each do
- @server.should_receive(:user).and_respond("331 User name okay, need password.")
- @server.should_receive(:pass).and_respond("332 Need account for login.")
- end
-
- it "does not raise an Error when the response code is 202" do
- @server.should_receive(:acct).and_respond("202 Command not implemented, superfluous at this site.")
- -> { @ftp.login("rubyspec", "rocks", "account") }.should_not raise_error
- end
-
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:acct).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:acct).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 502" do
- @server.should_receive(:acct).and_respond("502 Command not implemented.")
- -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:acct).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:acct).and_respond("530 Not logged in.")
- -> { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
- end
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/ls_spec.rb b/spec/ruby/library/net/ftp/ls_spec.rb
deleted file mode 100644
index f262515865..0000000000
--- a/spec/ruby/library/net/ftp/ls_spec.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
- require_relative 'shared/list'
-
- describe "Net::FTP#ls" do
- it_behaves_like :net_ftp_list, :ls
- end
-end
diff --git a/spec/ruby/library/net/ftp/mdtm_spec.rb b/spec/ruby/library/net/ftp/mdtm_spec.rb
deleted file mode 100644
index ea55533c43..0000000000
--- a/spec/ruby/library/net/ftp/mdtm_spec.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#mdtm" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- it "sends the MDTM with the passed filename command to the server" do
- @ftp.mdtm("test.file")
- @ftp.last_response.should == "213 19980705132316\n"
- end
-
- it "returns the last modification time of the passed file" do
- @ftp.mdtm("test.file").should == "19980705132316"
- end
-
- it "raises a Net::FTPPermError when the response code is 550" do
- @server.should_receive(:mdtm).and_respond("550 Requested action not taken.")
- -> { @ftp.mdtm("test.file") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:mdtm).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.mdtm("test.file") }.should raise_error(Net::FTPTempError)
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/mkdir_spec.rb b/spec/ruby/library/net/ftp/mkdir_spec.rb
deleted file mode 100644
index 2cb437a076..0000000000
--- a/spec/ruby/library/net/ftp/mkdir_spec.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#mkdir" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- it "sends the MKD command with the passed pathname to the server" do
- @ftp.mkdir("test.folder")
- @ftp.last_response.should == %{257 "test.folder" created.\n}
- end
-
- it "returns the path to the newly created directory" do
- @ftp.mkdir("test.folder").should == "test.folder"
- @ftp.mkdir("/absolute/path/to/test.folder").should == "/absolute/path/to/test.folder"
- @ftp.mkdir("relative/path/to/test.folder").should == "relative/path/to/test.folder"
- @ftp.mkdir('/usr/dm/foo"bar').should == '/usr/dm/foo"bar'
- end
-
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:mkd).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.mkdir("test.folder") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:mkd).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.mkdir("test.folder") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 502" do
- @server.should_receive(:mkd).and_respond("502 Command not implemented.")
- -> { @ftp.mkdir("test.folder") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:mkd).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.mkdir("test.folder") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:mkd).and_respond("530 Not logged in.")
- -> { @ftp.mkdir("test.folder") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 550" do
- @server.should_receive(:mkd).and_respond("550 Requested action not taken.")
- -> { @ftp.mkdir("test.folder") }.should raise_error(Net::FTPPermError)
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/mtime_spec.rb b/spec/ruby/library/net/ftp/mtime_spec.rb
deleted file mode 100644
index 7265667a52..0000000000
--- a/spec/ruby/library/net/ftp/mtime_spec.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#mtime" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- it "sends the MDTM with the passed filename command to the server" do
- @ftp.mtime("test.file")
- @ftp.last_response.should == "213 19980705132316\n"
- end
-
- describe "when passed filename" do
- it "returns the last modification time of the passed file as a Time object in the local time" do
- @ftp.mtime("test.file").should == Time.gm("1998", "07", "05", "13", "23", "16")
- end
- end
-
- describe "when passed filename, local_time" do
- it "returns the last modification time as a Time object in UTC when local_time is true" do
- @ftp.mtime("test.file", true).should == Time.local("1998", "07", "05", "13", "23", "16")
- end
-
- it "returns the last modification time as a Time object in the local time when local_time is false" do
- @ftp.mtime("test.file", false).should == Time.gm("1998", "07", "05", "13", "23", "16")
- end
- end
-
- it "raises a Net::FTPPermError when the response code is 550" do
- @server.should_receive(:mdtm).and_respond("550 Requested action not taken.")
- -> { @ftp.mtime("test.file") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:mdtm).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.mtime("test.file") }.should raise_error(Net::FTPTempError)
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/nlst_spec.rb b/spec/ruby/library/net/ftp/nlst_spec.rb
deleted file mode 100644
index 0de84b3a76..0000000000
--- a/spec/ruby/library/net/ftp/nlst_spec.rb
+++ /dev/null
@@ -1,95 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#nlst" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.passive = false
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- describe "when passed no arguments" do
- it "returns an Array containing a list of files in the current dir" do
- @ftp.nlst.should == ["last_response_code.rb", "list.rb", "pwd.rb"]
- @ftp.last_response.should == "226 transfer complete (NLST)\n"
- end
- end
-
- describe "when passed dir" do
- it "returns an Array containing a list of files in the passed dir" do
- @ftp.nlst("test.folder").should == ["last_response_code.rb", "list.rb", "pwd.rb"]
- @ftp.last_response.should == "226 transfer complete (NLST test.folder)\n"
- end
- end
-
- describe "when the NLST command fails" do
- it "raises a Net::FTPTempError when the response code is 450" do
- @server.should_receive(:nlst).and_respond("450 Requested file action not taken..")
- -> { @ftp.nlst }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:nlst).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.nlst }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:nlst).and_respond("501 Syntax error, command unrecognized.")
- -> { @ftp.nlst }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 502" do
- @server.should_receive(:nlst).and_respond("502 Command not implemented.")
- -> { @ftp.nlst }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:nlst).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.nlst }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:nlst).and_respond("530 Not logged in.")
- -> { @ftp.nlst }.should raise_error(Net::FTPPermError)
- end
- end
-
- describe "when opening the data port fails" do
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:eprt).and_respond("500 Syntax error, command unrecognized.")
- @server.should_receive(:port).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.nlst }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:eprt).and_respond("501 Syntax error in parameters or arguments.")
- @server.should_receive(:port).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.nlst }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:eprt).and_respond("421 Service not available, closing control connection.")
- @server.should_receive(:port).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.nlst }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:eprt).and_respond("530 Not logged in.")
- @server.should_receive(:port).and_respond("530 Not logged in.")
- -> { @ftp.nlst }.should raise_error(Net::FTPPermError)
- end
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/noop_spec.rb b/spec/ruby/library/net/ftp/noop_spec.rb
deleted file mode 100644
index 71011d4af7..0000000000
--- a/spec/ruby/library/net/ftp/noop_spec.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#noop" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- it "sends the NOOP command to the server" do
- @ftp.noop
- @ftp.last_response.should == "200 Command okay. (NOOP)\n"
- end
-
- it "returns nil" do
- @ftp.noop.should be_nil
- end
-
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:noop).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.noop }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:noop).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.noop }.should raise_error(Net::FTPTempError)
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/open_spec.rb b/spec/ruby/library/net/ftp/open_spec.rb
deleted file mode 100644
index 89187b9802..0000000000
--- a/spec/ruby/library/net/ftp/open_spec.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
-
- describe "Net::FTP.open" do
- before :each do
- @ftp = mock("Net::FTP instance")
- Net::FTP.stub!(:new).and_return(@ftp)
- end
-
- describe "when passed no block" do
- it "returns a new Net::FTP instance" do
- Net::FTP.open("localhost").should equal(@ftp)
- end
-
- it "passes the passed arguments down to Net::FTP.new" do
- Net::FTP.should_receive(:new).with("localhost", "user", "password", "account")
- Net::FTP.open("localhost", "user", "password", "account")
- end
- end
-
- describe "when passed a block" do
- before :each do
- @ftp.stub!(:close)
- end
-
- it "yields a new Net::FTP instance to the passed block" do
- yielded = false
- Net::FTP.open("localhost") do |ftp|
- yielded = true
- ftp.should equal(@ftp)
- end
- yielded.should be_true
- end
-
- it "closes the Net::FTP instance after yielding" do
- Net::FTP.open("localhost") do |ftp|
- ftp.should_receive(:close)
- end
- end
-
- it "closes the Net::FTP instance even if an exception is raised while yielding" do
- begin
- Net::FTP.open("localhost") do |ftp|
- ftp.should_receive(:close)
- raise ArgumentError, "some exception"
- end
- rescue ArgumentError
- end
- end
-
- it "returns the block's return value" do
- Net::FTP.open("localhost") { :test }.should == :test
- end
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/passive_spec.rb b/spec/ruby/library/net/ftp/passive_spec.rb
deleted file mode 100644
index f9c34efb7d..0000000000
--- a/spec/ruby/library/net/ftp/passive_spec.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
-
- describe "Net::FTP#passive" do
- it "returns true when self is in passive mode" do
- ftp = Net::FTP.new
- ftp.passive.should be_false
-
- ftp.passive = true
- ftp.passive.should be_true
- end
-
- it "is the value of Net::FTP.default_value by default" do
- ruby_exe(fixture(__FILE__, "passive.rb")).should == "true"
- end
- end
-
- describe "Net::FTP#passive=" do
- it "sets self to passive mode when passed true" do
- ftp = Net::FTP.new
-
- ftp.passive = true
- ftp.passive.should be_true
-
- ftp.passive = false
- ftp.passive.should be_false
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/put_spec.rb b/spec/ruby/library/net/ftp/put_spec.rb
deleted file mode 100644
index 36ba6c1963..0000000000
--- a/spec/ruby/library/net/ftp/put_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
- require_relative 'shared/puttextfile'
- require_relative 'shared/putbinaryfile'
-
- describe "Net::FTP#put (binary mode)" do
- before :each do
- @binary_mode = true
- end
-
- it_behaves_like :net_ftp_putbinaryfile, :put
- end
-
- describe "Net::FTP#put (text mode)" do
- before :each do
- @binary_mode = false
- end
-
- it_behaves_like :net_ftp_puttextfile, :put
- end
-end
diff --git a/spec/ruby/library/net/ftp/putbinaryfile_spec.rb b/spec/ruby/library/net/ftp/putbinaryfile_spec.rb
deleted file mode 100644
index 6ced5246fe..0000000000
--- a/spec/ruby/library/net/ftp/putbinaryfile_spec.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
- require_relative 'shared/putbinaryfile'
-
- describe "Net::FTP#putbinaryfile" do
- it_behaves_like :net_ftp_putbinaryfile, :putbinaryfile
- end
-end
diff --git a/spec/ruby/library/net/ftp/puttextfile_spec.rb b/spec/ruby/library/net/ftp/puttextfile_spec.rb
deleted file mode 100644
index 0cab6bd3c3..0000000000
--- a/spec/ruby/library/net/ftp/puttextfile_spec.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
- require_relative 'shared/puttextfile'
-
- describe "Net::FTP#puttextfile" do
- it_behaves_like :net_ftp_puttextfile, :puttextfile
- end
-end
diff --git a/spec/ruby/library/net/ftp/pwd_spec.rb b/spec/ruby/library/net/ftp/pwd_spec.rb
deleted file mode 100644
index 856ff5ff9b..0000000000
--- a/spec/ruby/library/net/ftp/pwd_spec.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#pwd" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- it "sends the PWD command to the server" do
- @ftp.pwd
- @ftp.last_response.should == "257 \"/some/dir/\" - current directory\n"
- end
-
- it "returns the current directory" do
- @ftp.pwd.should == "/some/dir/"
- end
-
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:pwd).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.pwd }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:pwd).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.pwd }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 502" do
- @server.should_receive(:pwd).and_respond("502 Command not implemented.")
- -> { @ftp.pwd }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:pwd).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.pwd }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 550" do
- @server.should_receive(:pwd).and_respond("550 Requested action not taken.")
- -> { @ftp.pwd }.should raise_error(Net::FTPPermError)
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/quit_spec.rb b/spec/ruby/library/net/ftp/quit_spec.rb
deleted file mode 100644
index 12b9fd3cee..0000000000
--- a/spec/ruby/library/net/ftp/quit_spec.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#quit" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- it "sends the QUIT command to the server" do
- @ftp.quit
- @ftp.last_response.should == "221 OK, bye\n"
- end
-
- it "does not close the socket automatically" do
- @ftp.quit
- @ftp.closed?.should be_false
- end
-
- it "returns nil" do
- @ftp.quit.should be_nil
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/rename_spec.rb b/spec/ruby/library/net/ftp/rename_spec.rb
deleted file mode 100644
index aa7c1360b5..0000000000
--- a/spec/ruby/library/net/ftp/rename_spec.rb
+++ /dev/null
@@ -1,97 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#rename" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- describe "when passed from_name, to_name" do
- it "sends the RNFR command with the passed from_name and the RNTO command with the passed to_name to the server" do
- @ftp.rename("from.file", "to.file")
- @ftp.last_response.should == "250 Requested file action okay, completed. (Renamed from.file to to.file)\n"
- end
-
- it "returns something" do
- @ftp.rename("from.file", "to.file").should be_nil
- end
- end
-
- describe "when the RNFR command fails" do
- it "raises a Net::FTPTempError when the response code is 450" do
- @server.should_receive(:rnfr).and_respond("450 Requested file action not taken.")
- -> { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 550" do
- @server.should_receive(:rnfr).and_respond("550 Requested action not taken.")
- -> { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:rnfr).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 502" do
- @server.should_receive(:rnfr).and_respond("502 Command not implemented.")
- -> { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:rnfr).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:rnfr).and_respond("530 Not logged in.")
- -> { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
- end
- end
-
- describe "when the RNTO command fails" do
- it "raises a Net::FTPPermError when the response code is 532" do
- @server.should_receive(:rnfr).and_respond("532 Need account for storing files.")
- -> { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 553" do
- @server.should_receive(:rnto).and_respond("553 Requested action not taken.")
- -> { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:rnto).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 502" do
- @server.should_receive(:rnto).and_respond("502 Command not implemented.")
- -> { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:rnto).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:rnto).and_respond("530 Not logged in.")
- -> { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
- end
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/resume_spec.rb b/spec/ruby/library/net/ftp/resume_spec.rb
deleted file mode 100644
index 1b575c29f1..0000000000
--- a/spec/ruby/library/net/ftp/resume_spec.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
-
- describe "Net::FTP#resume" do
- it "returns true when self is set to resume uploads/downloads" do
- ftp = Net::FTP.new
- ftp.resume.should be_false
-
- ftp.resume = true
- ftp.resume.should be_true
- end
- end
-
- describe "Net::FTP#resume=" do
- it "sets self to resume uploads/downloads when set to true" do
- ftp = Net::FTP.new
- ftp.resume = true
- ftp.resume.should be_true
-
- ftp.resume = false
- ftp.resume.should be_false
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/retrbinary_spec.rb b/spec/ruby/library/net/ftp/retrbinary_spec.rb
deleted file mode 100644
index 1f89f0d454..0000000000
--- a/spec/ruby/library/net/ftp/retrbinary_spec.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#retrbinary" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- it "sends the passed command to the server" do
- @ftp.retrbinary("RETR test", 4096) {}
- @ftp.last_response.should == "226 Closing data connection. (RETR test)\n"
- end
-
- it "yields the received content as binary blocks of the passed size" do
- res = []
- @ftp.retrbinary("RETR test", 10) { |bin| res << bin }
- res.should == [ "This is th", "e content\n", "of the fil", "e named 't", "est'.\n" ]
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/retrlines_spec.rb b/spec/ruby/library/net/ftp/retrlines_spec.rb
deleted file mode 100644
index f26b008680..0000000000
--- a/spec/ruby/library/net/ftp/retrlines_spec.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#retrlines" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- it "sends the passed command over the socket" do
- @ftp.retrlines("LIST test.dir") {}
- @ftp.last_response.should == "226 transfer complete (LIST test.dir)\n"
- end
-
- it "yields each received line to the passed block" do
- res = []
- @ftp.retrlines("LIST test.dir") { |x| res << x }
- res.should == [
- "-rw-r--r-- 1 spec staff 507 17 Jul 18:41 last_response_code.rb",
- "-rw-r--r-- 1 spec staff 50 17 Jul 18:41 list.rb",
- "-rw-r--r-- 1 spec staff 48 17 Jul 18:41 pwd.rb"
- ]
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/return_code_spec.rb b/spec/ruby/library/net/ftp/return_code_spec.rb
deleted file mode 100644
index 67fc9d3b19..0000000000
--- a/spec/ruby/library/net/ftp/return_code_spec.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
-
- describe "Net::FTP#return_code" do
- before :each do
- @ftp = Net::FTP.new
- end
-
- it "outputs a warning and returns a newline" do
- -> do
- @ftp.return_code.should == "\n"
- end.should complain(/warning: Net::FTP#return_code is obsolete and do nothing/)
- end
- end
-
- describe "Net::FTP#return_code=" do
- before :each do
- @ftp = Net::FTP.new
- end
-
- it "outputs a warning" do
- -> { @ftp.return_code = 123 }.should complain(/warning: Net::FTP#return_code= is obsolete and do nothing/)
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/rmdir_spec.rb b/spec/ruby/library/net/ftp/rmdir_spec.rb
deleted file mode 100644
index 5b9586c6f0..0000000000
--- a/spec/ruby/library/net/ftp/rmdir_spec.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#rmdir" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- it "sends the RMD command with the passed pathname to the server" do
- @ftp.rmdir("test.folder")
- @ftp.last_response.should == "250 Requested file action okay, completed. (RMD test.folder)\n"
- end
-
- it "returns nil" do
- @ftp.rmdir("test.folder").should be_nil
- end
-
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:rmd).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.rmdir("test.folder") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:rmd).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.rmdir("test.folder") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 502" do
- @server.should_receive(:rmd).and_respond("502 Command not implemented.")
- -> { @ftp.rmdir("test.folder") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:rmd).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.rmdir("test.folder") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:rmd).and_respond("530 Not logged in.")
- -> { @ftp.rmdir("test.folder") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 550" do
- @server.should_receive(:rmd).and_respond("550 Requested action not taken.")
- -> { @ftp.rmdir("test.folder") }.should raise_error(Net::FTPPermError)
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/sendcmd_spec.rb b/spec/ruby/library/net/ftp/sendcmd_spec.rb
deleted file mode 100644
index fefb89ae0b..0000000000
--- a/spec/ruby/library/net/ftp/sendcmd_spec.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#sendcmd" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- it "sends the passed command to the server" do
- @ftp.sendcmd("HELP")
- @ftp.last_response.should == "211 System status, or system help reply. (HELP)\n"
- end
-
- it "returns the server's response" do
- @ftp.sendcmd("HELP").should == "211 System status, or system help reply. (HELP)\n"
- end
-
- it "raises no error when the response code is 1xx, 2xx or 3xx" do
- @server.should_receive(:help).and_respond("120 Service ready in nnn minutes.")
- -> { @ftp.sendcmd("HELP") }.should_not raise_error
-
- @server.should_receive(:help).and_respond("200 Command okay.")
- -> { @ftp.sendcmd("HELP") }.should_not raise_error
-
- @server.should_receive(:help).and_respond("350 Requested file action pending further information.")
- -> { @ftp.sendcmd("HELP") }.should_not raise_error
- end
-
- it "raises a Net::FTPTempError when the response code is 4xx" do
- @server.should_receive(:help).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.sendcmd("HELP") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 5xx" do
- @server.should_receive(:help).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.sendcmd("HELP") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPProtoError when the response code is not between 1xx-5xx" do
- @server.should_receive(:help).and_respond("999 Invalid response.")
- -> { @ftp.sendcmd("HELP") }.should raise_error(Net::FTPProtoError)
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/set_socket_spec.rb b/spec/ruby/library/net/ftp/set_socket_spec.rb
deleted file mode 100644
index 6c8b58fb79..0000000000
--- a/spec/ruby/library/net/ftp/set_socket_spec.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
-
- describe "Net::FTP#set_socket" do
- # TODO: I won't spec this method, as it is not used
- # anywhere and it should be private anyway.
- it "needs to be reviewed for spec completeness"
- end
-end
diff --git a/spec/ruby/library/net/ftp/shared/getbinaryfile.rb b/spec/ruby/library/net/ftp/shared/getbinaryfile.rb
deleted file mode 100644
index f324f5b85d..0000000000
--- a/spec/ruby/library/net/ftp/shared/getbinaryfile.rb
+++ /dev/null
@@ -1,150 +0,0 @@
-describe :net_ftp_getbinaryfile, shared: :true do
- before :each do
- @fixture_file = File.dirname(__FILE__) + "/../fixtures/getbinaryfile"
- @tmp_file = tmp("getbinaryfile")
-
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- @ftp.binary = @binary_mode
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
-
- rm_r @tmp_file
- end
-
- it "sends the RETR command to the server" do
- @ftp.send(@method, "test", @tmp_file)
- @ftp.last_response.should == "226 Closing data connection. (RETR test)\n"
- end
-
- it "returns nil" do
- @ftp.send(@method, "test", @tmp_file).should be_nil
- end
-
- it "saves the contents of the passed remote file to the passed local file" do
- @ftp.send(@method, "test", @tmp_file)
- File.read(@tmp_file).should == "This is the content\nof the file named 'test'.\n"
- end
-
- describe "when passed a block" do
- it "yields the received content as binary blocks of the passed size" do
- res = []
- @ftp.send(@method, "test", @tmp_file, 10) { |bin| res << bin }
- res.should == [ "This is th", "e content\n", "of the fil", "e named 't", "est'.\n" ]
- end
- end
-
- describe "when resuming an existing file" do
- before :each do
- @tmp_file = tmp("getbinaryfile_resume")
-
- File.open(@tmp_file, "wb") do |f|
- f << "This is the content\n"
- end
-
- @ftp.resume = true
- end
-
- it "saves the remaining content of the passed remote file to the passed local file" do
- @ftp.send(@method, "test", @tmp_file)
- File.read(@tmp_file).should == "This is the content\nof the file named 'test'.\n"
- end
-
- describe "and the REST command fails" do
- it "raises a Net::FTPProtoError when the response code is 550" do
- @server.should_receive(:rest).and_respond("Requested action not taken.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPProtoError)
- end
-
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:rest).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:rest).and_respond("501 Syntax error, command unrecognized.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 502" do
- @server.should_receive(:rest).and_respond("502 Command not implemented.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:rest).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:rest).and_respond("530 Not logged in.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
- end
- end
- end
-
- describe "when the RETR command fails" do
- it "raises a Net::FTPTempError when the response code is 450" do
- @server.should_receive(:retr).and_respond("450 Requested file action not taken.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPProtoError when the response code is 550" do
- @server.should_receive(:retr).and_respond("Requested action not taken.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPProtoError)
- end
-
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:retr).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:retr).and_respond("501 Syntax error, command unrecognized.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:retr).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:retr).and_respond("530 Not logged in.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
- end
- end
-
- describe "when opening the data port fails" do
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:eprt).and_respond("500 Syntax error, command unrecognized.")
- @server.should_receive(:port).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:eprt).and_respond("501 Syntax error in parameters or arguments.")
- @server.should_receive(:port).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:eprt).and_respond("421 Service not available, closing control connection.")
- @server.should_receive(:port).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:eprt).and_respond("530 Not logged in.")
- @server.should_receive(:port).and_respond("530 Not logged in.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/shared/gettextfile.rb b/spec/ruby/library/net/ftp/shared/gettextfile.rb
deleted file mode 100644
index 82bec2a4a7..0000000000
--- a/spec/ruby/library/net/ftp/shared/gettextfile.rb
+++ /dev/null
@@ -1,100 +0,0 @@
-describe :net_ftp_gettextfile, shared: :true do
- before :each do
- @tmp_file = tmp("gettextfile")
-
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- @ftp.binary = @binary_mode
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
-
- rm_r @tmp_file
- end
-
- it "sends the RETR command to the server" do
- @ftp.send(@method, "test", @tmp_file)
- @ftp.last_response.should == "226 Closing data connection. (RETR test)\n"
- end
-
- it "returns nil" do
- @ftp.send(@method, "test", @tmp_file).should be_nil
- end
-
- it "saves the contents of the passed remote file to the passed local file" do
- @ftp.send(@method, "test", @tmp_file)
- File.read(@tmp_file).should == "This is the content\nof the file named 'test'.\n"
- end
-
- describe "when passed a block" do
- it "yields each line of the retrieved file to the passed block" do
- res = []
- @ftp.send(@method, "test", @tmp_file) { |line| res << line }
- res.should == [ "This is the content", "of the file named 'test'."]
- end
- end
-
- describe "when the RETR command fails" do
- it "raises a Net::FTPTempError when the response code is 450" do
- @server.should_receive(:retr).and_respond("450 Requested file action not taken.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPProtoError when the response code is 550" do
- @server.should_receive(:retr).and_respond("Requested action not taken.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPProtoError)
- end
-
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:retr).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:retr).and_respond("501 Syntax error, command unrecognized.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:retr).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:retr).and_respond("530 Not logged in.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
- end
- end
-
- describe "when opening the data port fails" do
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:eprt).and_respond("500 Syntax error, command unrecognized.")
- @server.should_receive(:port).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:eprt).and_respond("501 Syntax error in parameters or arguments.")
- @server.should_receive(:port).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:eprt).and_respond("421 Service not available, closing control connection.")
- @server.should_receive(:port).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:eprt).and_respond("530 Not logged in.")
- @server.should_receive(:port).and_respond("530 Not logged in.")
- -> { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/shared/putbinaryfile.rb b/spec/ruby/library/net/ftp/shared/putbinaryfile.rb
deleted file mode 100644
index 1163a1cfea..0000000000
--- a/spec/ruby/library/net/ftp/shared/putbinaryfile.rb
+++ /dev/null
@@ -1,167 +0,0 @@
-describe :net_ftp_putbinaryfile, shared: :true do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @local_fixture_file = File.dirname(__FILE__) + "/../fixtures/putbinaryfile"
- @remote_tmp_file = tmp("binaryfile", false)
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- @ftp.binary = @binary_mode
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
-
- rm_r @remote_tmp_file
- end
-
- it "sends the STOR command to the server" do
- @ftp.send(@method, @local_fixture_file, "binary")
- @ftp.last_response.should == "200 OK, Data received. (STOR binary)\n"
- end
-
- it "sends the contents of the passed local_file, without modifications" do
- @ftp.send(@method, @local_fixture_file, "binary")
-
- remote_lines = File.readlines(@remote_tmp_file)
- local_lines = File.readlines(@local_fixture_file)
-
- remote_lines.should == local_lines
- end
-
- it "returns nil" do
- @ftp.send(@method, @local_fixture_file, "binary").should be_nil
- end
-
- describe "when passed a block" do
- it "yields the transmitted content as binary blocks of the passed size" do
- res = []
- @ftp.send(@method, @local_fixture_file, "binary", 10) { |x| res << x }
- res.should == [
- "This is an", " example f",
- "ile\nwhich ", "is going t",
- "o be trans", "mitted\nusi",
- "ng #putbin", "aryfile.\n"
- ]
- end
- end
-
- describe "when resuming an existing file" do
- before :each do
- File.open(@remote_tmp_file, "w") do |f|
- f << "This is an example file\n"
- end
-
- @ftp.resume = true
- end
-
- it "sends the remaining content of the passed local_file to the passed remote_file" do
- @ftp.send(@method, @local_fixture_file, "binary")
- File.read(@remote_tmp_file).should == File.read(@local_fixture_file)
- end
-
- describe "and the APPE command fails" do
- it "raises a Net::FTPProtoError when the response code is 550" do
- @server.should_receive(:appe).and_respond("Requested action not taken.")
- -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPProtoError)
- end
-
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:appe).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:appe).and_respond("501 Syntax error, command unrecognized.")
- -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 502" do
- @server.should_receive(:appe).and_respond("502 Command not implemented.")
- -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:appe).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:appe).and_respond("530 Not logged in.")
- -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
- end
- end
- end
-
- describe "when the STOR command fails" do
- it "raises a Net::FTPPermError when the response code is 532" do
- @server.should_receive(:stor).and_respond("532 Need account for storing files.")
- -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 450" do
- @server.should_receive(:stor).and_respond("450 Requested file action not taken.")
- -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPTempError when the response code is 452" do
- @server.should_receive(:stor).and_respond("452 Requested action not taken.")
- -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 553" do
- @server.should_receive(:stor).and_respond("553 Requested action not taken.")
- -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:stor).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:stor).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:stor).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:stor).and_respond("530 Not logged in.")
- -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
- end
- end
-
- describe "when opening the data port fails" do
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:eprt).and_respond("500 Syntax error, command unrecognized.")
- @server.should_receive(:port).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:eprt).and_respond("501 Syntax error in parameters or arguments.")
- @server.should_receive(:port).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:eprt).and_respond("421 Service not available, closing control connection.")
- @server.should_receive(:port).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:eprt).and_respond("530 Not logged in.")
- @server.should_receive(:port).and_respond("530 Not logged in.")
- -> { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/shared/puttextfile.rb b/spec/ruby/library/net/ftp/shared/puttextfile.rb
deleted file mode 100644
index 50e8de28e6..0000000000
--- a/spec/ruby/library/net/ftp/shared/puttextfile.rb
+++ /dev/null
@@ -1,120 +0,0 @@
-describe :net_ftp_puttextfile, shared: true do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @local_fixture_file = File.dirname(__FILE__) + "/../fixtures/puttextfile"
- @remote_tmp_file = tmp("textfile", false)
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- @ftp.binary = @binary_mode
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
-
- rm_r @remote_tmp_file
- end
-
- it "sends the STOR command to the server" do
- @ftp.send(@method, @local_fixture_file, "text")
- @ftp.last_response.should == "200 OK, Data received. (STOR text)\n"
- end
-
- it "sends the contents of the passed local_file, using \\r\\n as the newline separator" do
- @ftp.send(@method, @local_fixture_file, "text")
-
- remote_lines = open(@remote_tmp_file, "rb") {|f| f.read }
- local_lines = open(@local_fixture_file, "rb") {|f| f.read }
-
- remote_lines.should_not == local_lines
- remote_lines.should == local_lines.gsub("\n", "\r\n")
- end
-
- it "returns nil" do
- @ftp.send(@method, @local_fixture_file, "text").should be_nil
- end
-
- describe "when passed a block" do
- it "yields each transmitted line" do
- res = []
- @ftp.send(@method, @local_fixture_file, "text") { |x| res << x }
- res.should == [
- "This is an example file\r\n",
- "which is going to be transmitted\r\n",
- "using #puttextfile.\r\n"
- ]
- end
- end
-
- describe "when the STOR command fails" do
- it "raises a Net::FTPPermError when the response code is 532" do
- @server.should_receive(:stor).and_respond("532 Need account for storing files.")
- -> { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 450" do
- @server.should_receive(:stor).and_respond("450 Requested file action not taken.")
- -> { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPTempError when the response code is 452" do
- @server.should_receive(:stor).and_respond("452 Requested action not taken.")
- -> { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 553" do
- @server.should_receive(:stor).and_respond("553 Requested action not taken.")
- -> { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:stor).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:stor).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:stor).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:stor).and_respond("530 Not logged in.")
- -> { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError)
- end
- end
-
- describe "when opening the data port fails" do
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:eprt).and_respond("500 Syntax error, command unrecognized.")
- @server.should_receive(:port).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:eprt).and_respond("501 Syntax error in parameters or arguments.")
- @server.should_receive(:port).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:eprt).and_respond("421 Service not available, closing control connection.")
- @server.should_receive(:port).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:eprt).and_respond("530 Not logged in.")
- @server.should_receive(:port).and_respond("530 Not logged in.")
- -> { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError)
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/site_spec.rb b/spec/ruby/library/net/ftp/site_spec.rb
deleted file mode 100644
index ca4f499112..0000000000
--- a/spec/ruby/library/net/ftp/site_spec.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#site" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- it "sends the SITE command with the passed argument to the server" do
- @ftp.site("param")
- @ftp.last_response.should == "200 Command okay. (SITE param)\n"
- end
-
- it "returns nil" do
- @ftp.site("param").should be_nil
- end
-
- it "does not raise an error when the response code is 202" do
- @server.should_receive(:site).and_respond("202 Command not implemented, superfluous at this site.")
- -> { @ftp.site("param") }.should_not raise_error
- end
-
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:site).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.site("param") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:site).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.site("param") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:site).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.site("param") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:site).and_respond("530 Requested action not taken.")
- -> { @ftp.site("param") }.should raise_error(Net::FTPPermError)
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/size_spec.rb b/spec/ruby/library/net/ftp/size_spec.rb
deleted file mode 100644
index 0c20b10549..0000000000
--- a/spec/ruby/library/net/ftp/size_spec.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#size" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- it "sends the SIZE command to the server" do
- @ftp.size("test.file")
- @ftp.last_response.should == "213 1024\n"
- end
-
- it "returns the size of the passed file as Integer" do
- @ftp.size("test.file").should eql(1024)
- end
-
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:size).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.size("test.file") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:size).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.size("test.file") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:size).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.size("test.file") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 550" do
- @server.should_receive(:size).and_respond("550 Requested action not taken.")
- -> { @ftp.size("test.file") }.should raise_error(Net::FTPPermError)
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/status_spec.rb b/spec/ruby/library/net/ftp/status_spec.rb
deleted file mode 100644
index 03bc5d6e05..0000000000
--- a/spec/ruby/library/net/ftp/status_spec.rb
+++ /dev/null
@@ -1,70 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#status" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- it "sends the STAT command to the server" do
- @ftp.status
- @ftp.last_response.should == "211 System status, or system help reply. (STAT)\n"
- end
-
- it "sends the STAT command with an optional parameter to the server" do
- @ftp.status("/pub").should == "211 System status, or system help reply. (STAT /pub)\n"
- end
-
- it "returns the received information" do
- @ftp.status.should == "211 System status, or system help reply. (STAT)\n"
- end
-
- it "does not raise an error when the response code is 212" do
- @server.should_receive(:stat).and_respond("212 Directory status.")
- -> { @ftp.status }.should_not raise_error
- end
-
- it "does not raise an error when the response code is 213" do
- @server.should_receive(:stat).and_respond("213 File status.")
- -> { @ftp.status }.should_not raise_error
- end
-
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:stat).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.status }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:stat).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.status }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 502" do
- @server.should_receive(:stat).and_respond("502 Command not implemented.")
- -> { @ftp.status }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:stat).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.status }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 530" do
- @server.should_receive(:stat).and_respond("530 Requested action not taken.")
- -> { @ftp.status }.should raise_error(Net::FTPPermError)
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/storbinary_spec.rb b/spec/ruby/library/net/ftp/storbinary_spec.rb
deleted file mode 100644
index 64c9090760..0000000000
--- a/spec/ruby/library/net/ftp/storbinary_spec.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#storbinary" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @local_fixture_file = File.dirname(__FILE__) + "/fixtures/putbinaryfile"
- @tmp_file = tmp("binaryfile", false)
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
-
- rm_r @tmp_file
- end
-
- it "sends the passed command and the passed File object's content to the server" do
- File.open(@local_fixture_file) do |f|
- f.binmode
-
- @ftp.storbinary("STOR binary", f, 4096) {}
- @ftp.last_response.should == "200 OK, Data received. (STOR binary)\n"
- end
- end
-
- it "yields the transmitted content as binary blocks of the passed size" do
- File.open(@local_fixture_file) do |f|
- f.binmode
-
- res = []
- @ftp.storbinary("STOR binary", f, 10) { |x| res << x }
- res.should == [
- "This is an", " example f",
- "ile\nwhich ", "is going t",
- "o be trans", "mitted\nusi",
- "ng #putbin", "aryfile.\n"
- ]
- end
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/storlines_spec.rb b/spec/ruby/library/net/ftp/storlines_spec.rb
deleted file mode 100644
index a4bb7af799..0000000000
--- a/spec/ruby/library/net/ftp/storlines_spec.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#storlines" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @local_fixture_file = File.dirname(__FILE__) + "/fixtures/puttextfile"
- @tmp_file = tmp("textfile", false)
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
-
- rm_r @tmp_file
- end
-
- it "sends the passed command and the passed File object's content to the server" do
- File.open(@local_fixture_file) do |f|
- @ftp.storlines("STOR text", f) {}
- @ftp.last_response.should == "200 OK, Data received. (STOR text)\n"
- end
- end
-
- it "yields each line of the transmitted content" do
- File.open(@local_fixture_file) do |f|
- res = []
- @ftp.storlines("STOR text", f) { |x| res << x }
- res.should == [
- "This is an example file\r\n",
- "which is going to be transmitted\r\n",
- "using #puttextfile.\r\n"
- ]
- end
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/system_spec.rb b/spec/ruby/library/net/ftp/system_spec.rb
deleted file mode 100644
index 0630bbe1f6..0000000000
--- a/spec/ruby/library/net/ftp/system_spec.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#system" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- it "sends the SYST command to the server" do
- @ftp.system
- @ftp.last_response.should =~ /\A215 FTP Dummy Server \(SYST\)\Z/
- end
-
- it "returns the received information" do
- @ftp.system.should =~ /\AFTP Dummy Server \(SYST\)\Z/
- end
-
- it "raises a Net::FTPPermError when the response code is 500" do
- @server.should_receive(:syst).and_respond("500 Syntax error, command unrecognized.")
- -> { @ftp.system }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 501" do
- @server.should_receive(:syst).and_respond("501 Syntax error in parameters or arguments.")
- -> { @ftp.system }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPPermError when the response code is 502" do
- @server.should_receive(:syst).and_respond("502 Command not implemented.")
- -> { @ftp.system }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPTempError when the response code is 421" do
- @server.should_receive(:syst).and_respond("421 Service not available, closing control connection.")
- -> { @ftp.system }.should raise_error(Net::FTPTempError)
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/voidcmd_spec.rb b/spec/ruby/library/net/ftp/voidcmd_spec.rb
deleted file mode 100644
index ee74455d63..0000000000
--- a/spec/ruby/library/net/ftp/voidcmd_spec.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#voidcmd" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- it "sends the passed command to the server" do
- @server.should_receive(:help).and_respond("2xx Does not raise.")
- -> { @ftp.voidcmd("HELP") }.should_not raise_error
- end
-
- it "returns nil" do
- @server.should_receive(:help).and_respond("2xx Does not raise.")
- @ftp.voidcmd("HELP").should be_nil
- end
-
- it "raises a Net::FTPReplyError when the response code is 1xx" do
- @server.should_receive(:help).and_respond("1xx Does raise a Net::FTPReplyError.")
- -> { @ftp.voidcmd("HELP") }.should raise_error(Net::FTPReplyError)
- end
-
- it "raises a Net::FTPReplyError when the response code is 3xx" do
- @server.should_receive(:help).and_respond("3xx Does raise a Net::FTPReplyError.")
- -> { @ftp.voidcmd("HELP") }.should raise_error(Net::FTPReplyError)
- end
-
- it "raises a Net::FTPTempError when the response code is 4xx" do
- @server.should_receive(:help).and_respond("4xx Does raise a Net::FTPTempError.")
- -> { @ftp.voidcmd("HELP") }.should raise_error(Net::FTPTempError)
- end
-
- it "raises a Net::FTPPermError when the response code is 5xx" do
- @server.should_receive(:help).and_respond("5xx Does raise a Net::FTPPermError.")
- -> { @ftp.voidcmd("HELP") }.should raise_error(Net::FTPPermError)
- end
-
- it "raises a Net::FTPProtoError when the response code is not valid" do
- @server.should_receive(:help).and_respond("999 Does raise a Net::FTPProtoError.")
- -> { @ftp.voidcmd("HELP") }.should raise_error(Net::FTPProtoError)
- end
- end
-end
diff --git a/spec/ruby/library/net/ftp/welcome_spec.rb b/spec/ruby/library/net/ftp/welcome_spec.rb
deleted file mode 100644
index e5414ef607..0000000000
--- a/spec/ruby/library/net/ftp/welcome_spec.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.1" do
- require_relative 'spec_helper'
- require_relative 'fixtures/server'
-
- describe "Net::FTP#welcome" do
- before :each do
- @server = NetFTPSpecs::DummyFTP.new
- @server.serve_once
-
- @ftp = Net::FTP.new
- @ftp.connect(@server.hostname, @server.server_port)
- end
-
- after :each do
- @ftp.quit rescue nil
- @ftp.close
- @server.stop
- end
-
- it "returns the server's welcome message" do
- @ftp.welcome.should be_nil
- @ftp.login
- @ftp.welcome.should == "230 User logged in, proceed. (USER anonymous)\n"
- end
- end
-end
diff --git a/spec/ruby/library/net/http/HTTPBadResponse_spec.rb b/spec/ruby/library/net/http/HTTPBadResponse_spec.rb
deleted file mode 100644
index be644968f5..0000000000
--- a/spec/ruby/library/net/http/HTTPBadResponse_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPBadResponse" do
- it "is a subclass of StandardError" do
- Net::HTTPBadResponse.should < StandardError
- end
-end
diff --git a/spec/ruby/library/net/http/HTTPClientExcepton_spec.rb b/spec/ruby/library/net/http/HTTPClientExcepton_spec.rb
deleted file mode 100644
index d576349a57..0000000000
--- a/spec/ruby/library/net/http/HTTPClientExcepton_spec.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require_relative '../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPClientException" do
- it "is a subclass of Net::ProtoServerError" do
- Net::HTTPClientException.should < Net::ProtoServerError
- end
-
- it "includes the Net::HTTPExceptions module" do
- Net::HTTPClientException.should < Net::HTTPExceptions
- end
-end
diff --git a/spec/ruby/library/net/http/HTTPError_spec.rb b/spec/ruby/library/net/http/HTTPError_spec.rb
deleted file mode 100644
index ab5f491bb7..0000000000
--- a/spec/ruby/library/net/http/HTTPError_spec.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require_relative '../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPError" do
- it "is a subclass of Net::ProtocolError" do
- Net::HTTPError.should < Net::ProtocolError
- end
-
- it "includes the Net::HTTPExceptions module" do
- Net::HTTPError.should < Net::HTTPExceptions
- end
-end
diff --git a/spec/ruby/library/net/http/HTTPFatalError_spec.rb b/spec/ruby/library/net/http/HTTPFatalError_spec.rb
deleted file mode 100644
index 6ab36bff6c..0000000000
--- a/spec/ruby/library/net/http/HTTPFatalError_spec.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require_relative '../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPFatalError" do
- it "is a subclass of Net::ProtoFatalError" do
- Net::HTTPFatalError.should < Net::ProtoFatalError
- end
-
- it "includes the Net::HTTPExceptions module" do
- Net::HTTPFatalError.should < Net::HTTPExceptions
- end
-end
diff --git a/spec/ruby/library/net/http/HTTPHeaderSyntaxError_spec.rb b/spec/ruby/library/net/http/HTTPHeaderSyntaxError_spec.rb
deleted file mode 100644
index 38e9454f99..0000000000
--- a/spec/ruby/library/net/http/HTTPHeaderSyntaxError_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPHeaderSyntaxError" do
- it "is a subclass of StandardError" do
- Net::HTTPHeaderSyntaxError.should < StandardError
- end
-end
diff --git a/spec/ruby/library/net/http/HTTPRetriableError_spec.rb b/spec/ruby/library/net/http/HTTPRetriableError_spec.rb
deleted file mode 100644
index 3a4bb9146c..0000000000
--- a/spec/ruby/library/net/http/HTTPRetriableError_spec.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require_relative '../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPRetriableError" do
- it "is a subclass of Net::ProtoRetriableError" do
- Net::HTTPRetriableError.should < Net::ProtoRetriableError
- end
-
- it "includes the Net::HTTPExceptions module" do
- Net::HTTPRetriableError.should < Net::HTTPExceptions
- end
-end
diff --git a/spec/ruby/library/net/http/HTTPServerException_spec.rb b/spec/ruby/library/net/http/HTTPServerException_spec.rb
deleted file mode 100644
index 23b0657364..0000000000
--- a/spec/ruby/library/net/http/HTTPServerException_spec.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require_relative '../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPServerException" do
- it "is a subclass of Net::ProtoServerError and is warned as deprecated" do
- -> { Net::HTTPServerException.should < Net::ProtoServerError }.should complain(/warning: constant Net::HTTPServerException is deprecated/)
- end
-
- it "includes the Net::HTTPExceptions module and is warned as deprecated" do
- -> { Net::HTTPServerException.should < Net::HTTPExceptions }.should complain(/warning: constant Net::HTTPServerException is deprecated/)
- end
-end
diff --git a/spec/ruby/library/net/http/http/Proxy_spec.rb b/spec/ruby/library/net/http/http/Proxy_spec.rb
deleted file mode 100644
index f85ccc0ee9..0000000000
--- a/spec/ruby/library/net/http/http/Proxy_spec.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTP.Proxy" do
- it "returns a new subclass of Net::HTTP" do
- Net::HTTP.Proxy("localhost").should < Net::HTTP
- end
-
- it "returns Net::HTTP when the passed address is nil" do
- Net::HTTP.Proxy(nil).should == Net::HTTP
- end
-
- it "sets the returned subclasses' proxy options based on the passed arguments" do
- http_with_proxy = Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks")
- http_with_proxy.proxy_address.should == "localhost"
- http_with_proxy.proxy_port.should eql(1234)
- http_with_proxy.proxy_user.should == "rspec"
- http_with_proxy.proxy_pass.should == "rocks"
- end
-end
-
-describe "Net::HTTP#proxy?" do
- describe "when self is no proxy class instance" do
- it "returns false" do
- Net::HTTP.new("localhost", 3333).proxy?.should be_false
- end
- end
-
- describe "when self is a proxy class instance" do
- it "returns false" do
- http_with_proxy = Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks")
- http_with_proxy.new("localhost", 3333).proxy?.should be_true
- end
- end
-end
diff --git a/spec/ruby/library/net/http/http/active_spec.rb b/spec/ruby/library/net/http/http/active_spec.rb
deleted file mode 100644
index ef657243ba..0000000000
--- a/spec/ruby/library/net/http/http/active_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-require_relative 'shared/started'
-
-describe "Net::HTTP#active?" do
- it_behaves_like :net_http_started_p, :active?
-end
diff --git a/spec/ruby/library/net/http/http/address_spec.rb b/spec/ruby/library/net/http/http/address_spec.rb
deleted file mode 100644
index 5fce76d767..0000000000
--- a/spec/ruby/library/net/http/http/address_spec.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTP#address" do
- it "returns the current host name" do
- net = Net::HTTP.new("localhost")
- net.address.should == "localhost"
- end
-end
diff --git a/spec/ruby/library/net/http/http/close_on_empty_response_spec.rb b/spec/ruby/library/net/http/http/close_on_empty_response_spec.rb
deleted file mode 100644
index a97b7b6c43..0000000000
--- a/spec/ruby/library/net/http/http/close_on_empty_response_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTP#close_on_empty_response" do
- it "needs to be reviewed for spec completeness"
-end
-
-describe "Net::HTTP#close_on_empty_response=" do
- it "needs to be reviewed for spec completeness"
-end
diff --git a/spec/ruby/library/net/http/http/copy_spec.rb b/spec/ruby/library/net/http/http/copy_spec.rb
deleted file mode 100644
index 5ebfdc334e..0000000000
--- a/spec/ruby/library/net/http/http/copy_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-
-describe "Net::HTTP#copy" do
- before :each do
- NetHTTPSpecs.start_server
- @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
- end
-
- after :each do
- @http.finish if @http.started?
- NetHTTPSpecs.stop_server
- end
-
- it "sends a COPY request to the passed path and returns the response" do
- response = @http.copy("/request")
- response.should be_kind_of(Net::HTTPResponse)
- response.body.should == "Request type: COPY"
- end
-end
diff --git a/spec/ruby/library/net/http/http/default_port_spec.rb b/spec/ruby/library/net/http/http/default_port_spec.rb
deleted file mode 100644
index 30db18efae..0000000000
--- a/spec/ruby/library/net/http/http/default_port_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTP.default_port" do
- it "returns 80" do
- Net::HTTP.http_default_port.should eql(80)
- end
-end
diff --git a/spec/ruby/library/net/http/http/delete_spec.rb b/spec/ruby/library/net/http/http/delete_spec.rb
deleted file mode 100644
index 160c653115..0000000000
--- a/spec/ruby/library/net/http/http/delete_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-
-describe "Net::HTTP#delete" do
- before :each do
- NetHTTPSpecs.start_server
- @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
- end
-
- after :each do
- @http.finish if @http.started?
- NetHTTPSpecs.stop_server
- end
-
- it "sends a DELETE request to the passed path and returns the response" do
- response = @http.delete("/request")
- response.should be_kind_of(Net::HTTPResponse)
- response.body.should == "Request type: DELETE"
- end
-end
diff --git a/spec/ruby/library/net/http/http/finish_spec.rb b/spec/ruby/library/net/http/http/finish_spec.rb
deleted file mode 100644
index f98bc7be13..0000000000
--- a/spec/ruby/library/net/http/http/finish_spec.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-
-describe "Net::HTTP#finish" do
- before :each do
- NetHTTPSpecs.start_server
- @http = Net::HTTP.new("localhost", NetHTTPSpecs.port)
- end
-
- after :each do
- @http.finish if @http.started?
- NetHTTPSpecs.stop_server
- end
-
- describe "when self has been started" do
- it "closes the tcp connection" do
- @http.start
- @http.finish
- @http.started?.should be_false
- end
- end
-
- describe "when self has not been started yet" do
- it "raises an IOError" do
- -> { @http.finish }.should raise_error(IOError)
- end
- end
-end
diff --git a/spec/ruby/library/net/http/http/get2_spec.rb b/spec/ruby/library/net/http/http/get2_spec.rb
deleted file mode 100644
index 519e4c2599..0000000000
--- a/spec/ruby/library/net/http/http/get2_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-require_relative 'shared/request_get'
-
-describe "Net::HTTP#get2" do
- it_behaves_like :net_http_request_get, :get2
-end
diff --git a/spec/ruby/library/net/http/http/get_print_spec.rb b/spec/ruby/library/net/http/http/get_print_spec.rb
deleted file mode 100644
index f59dd68c81..0000000000
--- a/spec/ruby/library/net/http/http/get_print_spec.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-
-describe "Net::HTTP.get_print" do
- before :each do
- NetHTTPSpecs.start_server
- @port = NetHTTPSpecs.port
- end
-
- after :each do
- NetHTTPSpecs.stop_server
- end
-
- describe "when passed URI" do
- it "it prints the body of the specified uri to $stdout" do
- -> do
- Net::HTTP.get_print URI.parse("http://localhost:#{@port}/")
- end.should output(/This is the index page\./)
- end
- end
-
- describe "when passed host, path, port" do
- it "it prints the body of the specified uri to $stdout" do
- -> do
- Net::HTTP.get_print 'localhost', "/", @port
- end.should output(/This is the index page\./)
- end
- end
-end
diff --git a/spec/ruby/library/net/http/http/get_response_spec.rb b/spec/ruby/library/net/http/http/get_response_spec.rb
deleted file mode 100644
index 941b35e773..0000000000
--- a/spec/ruby/library/net/http/http/get_response_spec.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-
-describe "Net::HTTP.get_response" do
- before :each do
- NetHTTPSpecs.start_server
- @port = NetHTTPSpecs.port
- end
-
- after :each do
- NetHTTPSpecs.stop_server
- end
-
- describe "when passed URI" do
- it "returns the response for the specified uri" do
- res = Net::HTTP.get_response(URI.parse("http://localhost:#{@port}/"))
- res.content_type.should == "text/plain"
- res.body.should == "This is the index page."
- end
- end
-
- describe "when passed host, path, port" do
- it "returns the response for the specified host-path-combination" do
- res = Net::HTTP.get_response('localhost', "/", @port)
- res.content_type.should == "text/plain"
- res.body.should == "This is the index page."
- end
- end
-end
diff --git a/spec/ruby/library/net/http/http/get_spec.rb b/spec/ruby/library/net/http/http/get_spec.rb
deleted file mode 100644
index 9f6c45f26b..0000000000
--- a/spec/ruby/library/net/http/http/get_spec.rb
+++ /dev/null
@@ -1,96 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-
-describe "Net::HTTP.get" do
- before :each do
- NetHTTPSpecs.start_server
- @port = NetHTTPSpecs.port
- end
-
- after :each do
- NetHTTPSpecs.stop_server
- end
-
- describe "when passed URI" do
- it "returns the body of the specified uri" do
- Net::HTTP.get(URI.parse("http://localhost:#{@port}/")).should == "This is the index page."
- end
- end
-
- describe "when passed host, path, port" do
- it "returns the body of the specified host-path-combination" do
- Net::HTTP.get('localhost', "/", @port).should == "This is the index page."
- end
- end
-end
-
-quarantine! do # These specs fail frequently with CHECK_LEAKS=true
-describe "Net::HTTP.get" do
- describe "when reading gzipped contents" do
- def start_threads
- require 'zlib'
- require 'stringio'
-
- server = nil
- server_thread = Thread.new do
- server = TCPServer.new("127.0.0.1", 0)
- begin
- c = server.accept
- ensure
- server.close
- end
- c.print "HTTP/1.1 200\r\n"
- c.print "Content-Type: text/plain\r\n"
- c.print "Content-Encoding: gzip\r\n"
- s = StringIO.new
- z = Zlib::GzipWriter.new(s)
- begin
- z.write 'Hello World!'
- ensure
- z.close
- end
- c.print "Content-Length: #{s.length}\r\n\r\n"
- # Write partial gzip content
- c.write s.string.byteslice(0..-2)
- c.flush
- c
- end
- Thread.pass until server && server_thread.stop?
-
- client_thread = Thread.new do
- Thread.current.report_on_exception = false
- Net::HTTP.get("127.0.0.1", '/', server.connect_address.ip_port)
- end
-
- socket = server_thread.value
- Thread.pass until client_thread.stop?
-
- [socket, client_thread]
- end
-
- it "propagates exceptions interrupting the thread and does not replace it with Zlib::BufError" do
- my_exception = Class.new(RuntimeError)
- socket, client_thread = start_threads
- begin
- client_thread.raise my_exception, "my exception"
- -> { client_thread.value }.should raise_error(my_exception)
- ensure
- socket.close
- end
- end
-
- ruby_version_is "3.0" do # https://bugs.ruby-lang.org/issues/13882#note-6
- it "lets the kill Thread exception goes through and does not replace it with Zlib::BufError" do
- socket, client_thread = start_threads
- begin
- client_thread.kill
- client_thread.value.should == nil
- ensure
- socket.close
- end
- end
- end
- end
-end
-end
diff --git a/spec/ruby/library/net/http/http/head2_spec.rb b/spec/ruby/library/net/http/http/head2_spec.rb
deleted file mode 100644
index 6958204ee1..0000000000
--- a/spec/ruby/library/net/http/http/head2_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-require_relative 'shared/request_head'
-
-describe "Net::HTTP#head2" do
- it_behaves_like :net_http_request_head, :head2
-end
diff --git a/spec/ruby/library/net/http/http/head_spec.rb b/spec/ruby/library/net/http/http/head_spec.rb
deleted file mode 100644
index 925a8e6043..0000000000
--- a/spec/ruby/library/net/http/http/head_spec.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-
-describe "Net::HTTP#head" do
- before :each do
- NetHTTPSpecs.start_server
- @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
- end
-
- after :each do
- @http.finish if @http.started?
- NetHTTPSpecs.stop_server
- end
-
- it "sends a HEAD request to the passed path and returns the response" do
- response = @http.head("/request")
- # HEAD requests have no responses
- response.body.should be_nil
- end
-
- it "returns a Net::HTTPResponse" do
- @http.head("/request").should be_kind_of(Net::HTTPResponse)
- end
-end
diff --git a/spec/ruby/library/net/http/http/http_default_port_spec.rb b/spec/ruby/library/net/http/http/http_default_port_spec.rb
deleted file mode 100644
index cf7f73e630..0000000000
--- a/spec/ruby/library/net/http/http/http_default_port_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTP.http_default_port" do
- it "returns 80" do
- Net::HTTP.http_default_port.should eql(80)
- end
-end
diff --git a/spec/ruby/library/net/http/http/https_default_port_spec.rb b/spec/ruby/library/net/http/http/https_default_port_spec.rb
deleted file mode 100644
index fbf0bd1abc..0000000000
--- a/spec/ruby/library/net/http/http/https_default_port_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTP.https_default_port" do
- it "returns 443" do
- Net::HTTP.https_default_port.should eql(443)
- end
-end
diff --git a/spec/ruby/library/net/http/http/initialize_spec.rb b/spec/ruby/library/net/http/http/initialize_spec.rb
deleted file mode 100644
index 7683713a0e..0000000000
--- a/spec/ruby/library/net/http/http/initialize_spec.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTP#initialize" do
- it "is private" do
- Net::HTTP.should have_private_instance_method(:initialize)
- end
-
- describe "when passed address" do
- before :each do
- @net = Net::HTTP.allocate
- @net.send(:initialize, "localhost")
- end
-
- it "sets the new Net::HTTP instance's address to the passed address" do
- @net.address.should == "localhost"
- end
-
- it "sets the new Net::HTTP instance's port to the default HTTP port" do
- @net.port.should eql(Net::HTTP.default_port)
- end
-
- it "does not start the new Net::HTTP instance" do
- @net.started?.should be_false
- end
- end
-
- describe "when passed address, port" do
- before :each do
- @net = Net::HTTP.allocate
- @net.send(:initialize, "localhost", 3333)
- end
-
- it "sets the new Net::HTTP instance's address to the passed address" do
- @net.address.should == "localhost"
- end
-
- it "sets the new Net::HTTP instance's port to the passed port" do
- @net.port.should eql(3333)
- end
-
- it "does not start the new Net::HTTP instance" do
- @net.started?.should be_false
- end
- end
-end
diff --git a/spec/ruby/library/net/http/http/inspect_spec.rb b/spec/ruby/library/net/http/http/inspect_spec.rb
deleted file mode 100644
index b1e799ca34..0000000000
--- a/spec/ruby/library/net/http/http/inspect_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-
-describe "Net::HTTP#inspect" do
- before :each do
- NetHTTPSpecs.start_server
- @port = NetHTTPSpecs.port
- @http = Net::HTTP.new("localhost", @port)
- end
-
- after :each do
- @http.finish if @http.started?
- NetHTTPSpecs.stop_server
- end
-
- it "returns a String representation of self" do
- @http.inspect.should be_kind_of(String)
- @http.inspect.should == "#<Net::HTTP localhost:#{@port} open=false>"
-
- @http.start
- @http.inspect.should == "#<Net::HTTP localhost:#{@port} open=true>"
- end
-end
diff --git a/spec/ruby/library/net/http/http/is_version_1_1_spec.rb b/spec/ruby/library/net/http/http/is_version_1_1_spec.rb
deleted file mode 100644
index f37695b777..0000000000
--- a/spec/ruby/library/net/http/http/is_version_1_1_spec.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'shared/version_1_1'
-
-describe "Net::HTTP.is_version_1_1?" do
- it_behaves_like :net_http_version_1_1_p, :is_version_1_1?
-end
diff --git a/spec/ruby/library/net/http/http/is_version_1_2_spec.rb b/spec/ruby/library/net/http/http/is_version_1_2_spec.rb
deleted file mode 100644
index 82dbdc87aa..0000000000
--- a/spec/ruby/library/net/http/http/is_version_1_2_spec.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'shared/version_1_2'
-
-describe "Net::HTTP.is_version_1_2?" do
- it_behaves_like :net_http_version_1_2_p, :is_version_1_2?
-end
diff --git a/spec/ruby/library/net/http/http/lock_spec.rb b/spec/ruby/library/net/http/http/lock_spec.rb
deleted file mode 100644
index bb76607a8b..0000000000
--- a/spec/ruby/library/net/http/http/lock_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-
-describe "Net::HTTP#lock" do
- before :each do
- NetHTTPSpecs.start_server
- @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
- end
-
- after :each do
- @http.finish if @http.started?
- NetHTTPSpecs.stop_server
- end
-
- it "sends a LOCK request to the passed path and returns the response" do
- response = @http.lock("/request", "test=test")
- response.should be_kind_of(Net::HTTPResponse)
- response.body.should == "Request type: LOCK"
- end
-end
diff --git a/spec/ruby/library/net/http/http/mkcol_spec.rb b/spec/ruby/library/net/http/http/mkcol_spec.rb
deleted file mode 100644
index 33017625e2..0000000000
--- a/spec/ruby/library/net/http/http/mkcol_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-
-describe "Net::HTTP#mkcol" do
- before :each do
- NetHTTPSpecs.start_server
- @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
- end
-
- after :each do
- @http.finish if @http.started?
- NetHTTPSpecs.stop_server
- end
-
- it "sends a MKCOL request to the passed path and returns the response" do
- response = @http.mkcol("/request")
- response.should be_kind_of(Net::HTTPResponse)
- response.body.should == "Request type: MKCOL"
- end
-end
diff --git a/spec/ruby/library/net/http/http/move_spec.rb b/spec/ruby/library/net/http/http/move_spec.rb
deleted file mode 100644
index 4d6b828150..0000000000
--- a/spec/ruby/library/net/http/http/move_spec.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-
-describe "Net::HTTP#head" do
- before :each do
- NetHTTPSpecs.start_server
- @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
- end
-
- after :each do
- @http.finish if @http.started?
- NetHTTPSpecs.stop_server
- end
-
- it "sends a MOVE request to the passed path and returns the response" do
- response = @http.move("/request")
- # HEAD requests have no responses
- response.body.should == "Request type: MOVE"
- end
-
- it "returns a Net::HTTPResponse" do
- @http.move("/request").should be_kind_of(Net::HTTPResponse)
- end
-end
diff --git a/spec/ruby/library/net/http/http/new_spec.rb b/spec/ruby/library/net/http/http/new_spec.rb
deleted file mode 100644
index 491d1d01fd..0000000000
--- a/spec/ruby/library/net/http/http/new_spec.rb
+++ /dev/null
@@ -1,86 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTP.new" do
- describe "when passed address" do
- before :each do
- @http = Net::HTTP.new("localhost")
- end
-
- it "returns a Net::HTTP instance" do
- @http.proxy?.should be_false
- @http.instance_of?(Net::HTTP).should be_true
- end
-
- it "sets the new Net::HTTP instance's address to the passed address" do
- @http.address.should == "localhost"
- end
-
- it "sets the new Net::HTTP instance's port to the default HTTP port" do
- @http.port.should eql(Net::HTTP.default_port)
- end
-
- it "does not start the new Net::HTTP instance" do
- @http.started?.should be_false
- end
- end
-
- describe "when passed address, port" do
- before :each do
- @http = Net::HTTP.new("localhost", 3333)
- end
-
- it "returns a Net::HTTP instance" do
- @http.proxy?.should be_false
- @http.instance_of?(Net::HTTP).should be_true
- end
-
- it "sets the new Net::HTTP instance's address to the passed address" do
- @http.address.should == "localhost"
- end
-
- it "sets the new Net::HTTP instance's port to the passed port" do
- @http.port.should eql(3333)
- end
-
- it "does not start the new Net::HTTP instance" do
- @http.started?.should be_false
- end
- end
-
- describe "when passed address, port, *proxy_options" do
- it "returns a Net::HTTP instance" do
- http = Net::HTTP.new("localhost", 3333, "localhost")
- http.proxy?.should be_true
- http.instance_of?(Net::HTTP).should be_true
- http.should be_kind_of(Net::HTTP)
- end
-
- it "correctly sets the passed Proxy options" do
- http = Net::HTTP.new("localhost", 3333, "localhost")
- http.proxy_address.should == "localhost"
- http.proxy_port.should eql(80)
- http.proxy_user.should be_nil
- http.proxy_pass.should be_nil
-
- http = Net::HTTP.new("localhost", 3333, "localhost", 1234)
- http.proxy_address.should == "localhost"
- http.proxy_port.should eql(1234)
- http.proxy_user.should be_nil
- http.proxy_pass.should be_nil
-
- http = Net::HTTP.new("localhost", 3333, "localhost", 1234, "rubyspec")
- http.proxy_address.should == "localhost"
- http.proxy_port.should eql(1234)
- http.proxy_user.should == "rubyspec"
- http.proxy_pass.should be_nil
-
- http = Net::HTTP.new("localhost", 3333, "localhost", 1234, "rubyspec", "rocks")
- http.proxy_address.should == "localhost"
- http.proxy_port.should eql(1234)
- http.proxy_user.should == "rubyspec"
- http.proxy_pass.should == "rocks"
- end
- end
-
-end
diff --git a/spec/ruby/library/net/http/http/newobj_spec.rb b/spec/ruby/library/net/http/http/newobj_spec.rb
deleted file mode 100644
index b261dcc5db..0000000000
--- a/spec/ruby/library/net/http/http/newobj_spec.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTP.newobj" do
- before :each do
- @net = Net::HTTP.newobj("localhost")
- end
-
- describe "when passed address" do
- it "returns a new Net::HTTP instance" do
- @net.should be_kind_of(Net::HTTP)
- end
-
- it "sets the new Net::HTTP instance's address to the passed address" do
- @net.address.should == "localhost"
- end
-
- it "sets the new Net::HTTP instance's port to the default HTTP port" do
- @net.port.should eql(Net::HTTP.default_port)
- end
-
- it "does not start the new Net::HTTP instance" do
- @net.started?.should be_false
- end
- end
-
- describe "when passed address, port" do
- before :each do
- @net = Net::HTTP.newobj("localhost", 3333)
- end
-
- it "returns a new Net::HTTP instance" do
- @net.should be_kind_of(Net::HTTP)
- end
-
- it "sets the new Net::HTTP instance's address to the passed address" do
- @net.address.should == "localhost"
- end
-
- it "sets the new Net::HTTP instance's port to the passed port" do
- @net.port.should eql(3333)
- end
-
- it "does not start the new Net::HTTP instance" do
- @net.started?.should be_false
- end
- end
-end
diff --git a/spec/ruby/library/net/http/http/open_timeout_spec.rb b/spec/ruby/library/net/http/http/open_timeout_spec.rb
deleted file mode 100644
index 44b33615e6..0000000000
--- a/spec/ruby/library/net/http/http/open_timeout_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTP#open_timeout" do
- it "returns the seconds to wait till the connection is open" do
- net = Net::HTTP.new("localhost")
- net.open_timeout.should eql(60)
- net.open_timeout = 10
- net.open_timeout.should eql(10)
- end
-end
-
-describe "Net::HTTP#open_timeout=" do
- it "sets the seconds to wait till the connection is open" do
- net = Net::HTTP.new("localhost")
- net.open_timeout = 10
- net.open_timeout.should eql(10)
- end
-
- it "returns the newly set value" do
- net = Net::HTTP.new("localhost")
- (net.open_timeout = 10).should eql(10)
- end
-end
diff --git a/spec/ruby/library/net/http/http/options_spec.rb b/spec/ruby/library/net/http/http/options_spec.rb
deleted file mode 100644
index d798e69197..0000000000
--- a/spec/ruby/library/net/http/http/options_spec.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-
-describe "Net::HTTP#options" do
- before :each do
- NetHTTPSpecs.start_server
- @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
- end
-
- after :each do
- @http.finish if @http.started?
- NetHTTPSpecs.stop_server
- end
-
- it "sends an options request to the passed path and returns the response" do
- response = @http.options("/request")
-
- response.body.should == "Request type: OPTIONS"
- end
-
- it "returns a Net::HTTPResponse" do
- @http.options("/request").should be_kind_of(Net::HTTPResponse)
- end
-end
diff --git a/spec/ruby/library/net/http/http/port_spec.rb b/spec/ruby/library/net/http/http/port_spec.rb
deleted file mode 100644
index 7de295ca75..0000000000
--- a/spec/ruby/library/net/http/http/port_spec.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTP#port" do
- it "returns the current port number" do
- net = Net::HTTP.new("localhost", 3333)
- net.port.should eql(3333)
- end
-end
diff --git a/spec/ruby/library/net/http/http/post2_spec.rb b/spec/ruby/library/net/http/http/post2_spec.rb
deleted file mode 100644
index ccc95068fb..0000000000
--- a/spec/ruby/library/net/http/http/post2_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-require_relative 'shared/request_post'
-
-describe "Net::HTTP#post2" do
- it_behaves_like :net_http_request_post, :post2
-end
diff --git a/spec/ruby/library/net/http/http/post_form_spec.rb b/spec/ruby/library/net/http/http/post_form_spec.rb
deleted file mode 100644
index 891e05e7af..0000000000
--- a/spec/ruby/library/net/http/http/post_form_spec.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-
-describe "Net::HTTP.post_form when passed URI" do
- before :each do
- NetHTTPSpecs.start_server
- @port = NetHTTPSpecs.port
- end
-
- after :each do
- NetHTTPSpecs.stop_server
- end
-
- it "POSTs the passed form data to the given uri" do
- uri = URI.parse("http://localhost:#{@port}/request/body")
- data = { test: :data }
-
- res = Net::HTTP.post_form(uri, data)
- res.body.should == "test=data"
- end
-end
diff --git a/spec/ruby/library/net/http/http/post_spec.rb b/spec/ruby/library/net/http/http/post_spec.rb
deleted file mode 100644
index 891e20aaca..0000000000
--- a/spec/ruby/library/net/http/http/post_spec.rb
+++ /dev/null
@@ -1,74 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require 'uri'
-require_relative 'fixtures/http_server'
-
-describe "Net::HTTP.post" do
- before :each do
- NetHTTPSpecs.start_server
- end
-
- after :each do
- NetHTTPSpecs.stop_server
- end
-
- it "sends post request to the specified URI and returns response" do
- response = Net::HTTP.post(
- URI("http://localhost:#{NetHTTPSpecs.port}/request"),
- '{ "q": "ruby", "max": "50" }',
- "Content-Type" => "application/json")
- response.body.should == "Request type: POST"
- end
-
- it "returns a Net::HTTPResponse" do
- response = Net::HTTP.post(URI("http://localhost:#{NetHTTPSpecs.port}/request"), "test=test")
- response.should be_kind_of(Net::HTTPResponse)
- end
-
- it "sends Content-Type: application/x-www-form-urlencoded by default" do
- response = Net::HTTP.post(URI("http://localhost:#{NetHTTPSpecs.port}/request/header"), "test=test")
- response.body.should include('"Content-Type"=>"application/x-www-form-urlencoded"')
- end
-
- it "does not support HTTP Basic Auth" do
- response = Net::HTTP.post(
- URI("http://john:qwerty@localhost:#{NetHTTPSpecs.port}/request/basic_auth"),
- "test=test")
- response.body.should == "username: \npassword: "
- end
-end
-
-describe "Net::HTTP#post" do
- before :each do
- NetHTTPSpecs.start_server
- @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
- end
-
- after :each do
- @http.finish if @http.started?
- NetHTTPSpecs.stop_server
- end
-
- it "sends an post request to the passed path and returns the response" do
- response = @http.post("/request", "test=test")
- response.body.should == "Request type: POST"
- end
-
- it "returns a Net::HTTPResponse" do
- @http.post("/request", "test=test").should be_kind_of(Net::HTTPResponse)
- end
-
- describe "when passed a block" do
- it "yields fragments of the response body to the passed block" do
- str = ""
- @http.post("/request", "test=test") do |res|
- str << res
- end
- str.should == "Request type: POST"
- end
-
- it "returns a Net::HTTPResponse" do
- @http.post("/request", "test=test") {}.should be_kind_of(Net::HTTPResponse)
- end
- end
-end
diff --git a/spec/ruby/library/net/http/http/propfind_spec.rb b/spec/ruby/library/net/http/http/propfind_spec.rb
deleted file mode 100644
index 5240171618..0000000000
--- a/spec/ruby/library/net/http/http/propfind_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-
-describe "Net::HTTP#propfind" do
- before :each do
- NetHTTPSpecs.start_server
- @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
- end
-
- after :each do
- @http.finish if @http.started?
- NetHTTPSpecs.stop_server
- end
-
- it "sends an propfind request to the passed path and returns the response" do
- response = @http.propfind("/request", "test=test")
- response.body.should == "Request type: PROPFIND"
- end
-
- it "returns a Net::HTTPResponse" do
- @http.propfind("/request", "test=test").should be_kind_of(Net::HTTPResponse)
- end
-end
diff --git a/spec/ruby/library/net/http/http/proppatch_spec.rb b/spec/ruby/library/net/http/http/proppatch_spec.rb
deleted file mode 100644
index 7a761a9f23..0000000000
--- a/spec/ruby/library/net/http/http/proppatch_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-
-describe "Net::HTTP#proppatch" do
- before :each do
- NetHTTPSpecs.start_server
- @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
- end
-
- after :each do
- @http.finish if @http.started?
- NetHTTPSpecs.stop_server
- end
-
- it "sends an proppatch request to the passed path and returns the response" do
- response = @http.proppatch("/request", "test=test")
- response.body.should == "Request type: PROPPATCH"
- end
-
- it "returns a Net::HTTPResponse" do
- @http.proppatch("/request", "test=test").should be_kind_of(Net::HTTPResponse)
- end
-end
diff --git a/spec/ruby/library/net/http/http/proxy_address_spec.rb b/spec/ruby/library/net/http/http/proxy_address_spec.rb
deleted file mode 100644
index 8d152b8d44..0000000000
--- a/spec/ruby/library/net/http/http/proxy_address_spec.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTP.proxy_address" do
- describe "when self is no proxy class" do
- it "returns nil" do
- Net::HTTP.proxy_address.should be_nil
- end
- end
-
- describe "when self is a proxy class" do
- it "returns the address for self's proxy connection" do
- Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks").proxy_address.should == "localhost"
- end
- end
-end
-
-describe "Net::HTTP#proxy_address" do
- describe "when self is no proxy class instance" do
- it "returns nil" do
- Net::HTTP.new("localhost", 3333).proxy_address.should be_nil
- end
- end
-
- describe "when self is a proxy class instance" do
- it "returns the password for self's proxy connection" do
- http_with_proxy = Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks")
- http_with_proxy.new("localhost", 3333).proxy_address.should == "localhost"
- end
- end
-end
diff --git a/spec/ruby/library/net/http/http/proxy_class_spec.rb b/spec/ruby/library/net/http/http/proxy_class_spec.rb
deleted file mode 100644
index 2d32a39f01..0000000000
--- a/spec/ruby/library/net/http/http/proxy_class_spec.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTP.proxy_class?" do
- it "returns true if self is a class created with Net::HTTP.Proxy" do
- Net::HTTP.proxy_class?.should be_false
- Net::HTTP.Proxy("localhost").proxy_class?.should be_true
- end
-end
diff --git a/spec/ruby/library/net/http/http/proxy_pass_spec.rb b/spec/ruby/library/net/http/http/proxy_pass_spec.rb
deleted file mode 100644
index 94a0034544..0000000000
--- a/spec/ruby/library/net/http/http/proxy_pass_spec.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTP.proxy_pass" do
- describe "when self is no proxy class" do
- it "returns nil" do
- Net::HTTP.proxy_pass.should be_nil
- end
- end
-
- describe "when self is a proxy class" do
- it "returns nil if no password was set for self's proxy connection" do
- Net::HTTP.Proxy("localhost").proxy_pass.should be_nil
- end
-
- it "returns the password for self's proxy connection" do
- Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks").proxy_pass.should == "rocks"
- end
- end
-end
-
-describe "Net::HTTP#proxy_pass" do
- describe "when self is no proxy class instance" do
- it "returns nil" do
- Net::HTTP.new("localhost", 3333).proxy_pass.should be_nil
- end
- end
-
- describe "when self is a proxy class instance" do
- it "returns nil if no password was set for self's proxy connection" do
- Net::HTTP.Proxy("localhost").new("localhost", 3333).proxy_pass.should be_nil
- end
-
- it "returns the password for self's proxy connection" do
- http_with_proxy = Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks")
- http_with_proxy.new("localhost", 3333).proxy_pass.should == "rocks"
- end
- end
-end
diff --git a/spec/ruby/library/net/http/http/proxy_port_spec.rb b/spec/ruby/library/net/http/http/proxy_port_spec.rb
deleted file mode 100644
index 339f7ee850..0000000000
--- a/spec/ruby/library/net/http/http/proxy_port_spec.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTP.proxy_port" do
- describe "when self is no proxy class" do
- it "returns nil" do
- Net::HTTP.proxy_port.should be_nil
- end
- end
-
- describe "when self is a proxy class" do
- it "returns 80 if no port was set for self's proxy connection" do
- Net::HTTP.Proxy("localhost").proxy_port.should eql(80)
- end
-
- it "returns the port for self's proxy connection" do
- Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks").proxy_port.should eql(1234)
- end
- end
-end
-
-describe "Net::HTTP#proxy_port" do
- describe "when self is no proxy class instance" do
- it "returns nil" do
- Net::HTTP.new("localhost", 3333).proxy_port.should be_nil
- end
- end
-
- describe "when self is a proxy class instance" do
- it "returns 80 if no port was set for self's proxy connection" do
- Net::HTTP.Proxy("localhost").new("localhost", 3333).proxy_port.should eql(80)
- end
-
- it "returns the port for self's proxy connection" do
- http_with_proxy = Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks")
- http_with_proxy.new("localhost", 3333).proxy_port.should eql(1234)
- end
- end
-end
diff --git a/spec/ruby/library/net/http/http/proxy_user_spec.rb b/spec/ruby/library/net/http/http/proxy_user_spec.rb
deleted file mode 100644
index 01fda400e9..0000000000
--- a/spec/ruby/library/net/http/http/proxy_user_spec.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTP.proxy_user" do
- describe "when self is no proxy class" do
- it "returns nil" do
- Net::HTTP.proxy_user.should be_nil
- end
- end
-
- describe "when self is a proxy class" do
- it "returns nil if no username was set for self's proxy connection" do
- Net::HTTP.Proxy("localhost").proxy_user.should be_nil
- end
-
- it "returns the username for self's proxy connection" do
- Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks").proxy_user.should == "rspec"
- end
- end
-end
-
-describe "Net::HTTP#proxy_user" do
- describe "when self is no proxy class instance" do
- it "returns nil" do
- Net::HTTP.new("localhost", 3333).proxy_user.should be_nil
- end
- end
-
- describe "when self is a proxy class instance" do
- it "returns nil if no username was set for self's proxy connection" do
- Net::HTTP.Proxy("localhost").new("localhost", 3333).proxy_user.should be_nil
- end
-
- it "returns the username for self's proxy connection" do
- http_with_proxy = Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks")
- http_with_proxy.new("localhost", 3333).proxy_user.should == "rspec"
- end
- end
-end
diff --git a/spec/ruby/library/net/http/http/put2_spec.rb b/spec/ruby/library/net/http/http/put2_spec.rb
deleted file mode 100644
index 99329a5fd9..0000000000
--- a/spec/ruby/library/net/http/http/put2_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-require_relative 'shared/request_put'
-
-describe "Net::HTTP#put2" do
- it_behaves_like :net_http_request_put, :put2
-end
diff --git a/spec/ruby/library/net/http/http/put_spec.rb b/spec/ruby/library/net/http/http/put_spec.rb
deleted file mode 100644
index 3ca0d0963e..0000000000
--- a/spec/ruby/library/net/http/http/put_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-
-describe "Net::HTTP#put" do
- before :each do
- NetHTTPSpecs.start_server
- @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
- end
-
- after :each do
- @http.finish if @http.started?
- NetHTTPSpecs.stop_server
- end
-
- it "sends an put request to the passed path and returns the response" do
- response = @http.put("/request", "test=test")
- response.body.should == "Request type: PUT"
- end
-
- it "returns a Net::HTTPResponse" do
- @http.put("/request", "test=test").should be_kind_of(Net::HTTPResponse)
- end
-end
diff --git a/spec/ruby/library/net/http/http/read_timeout_spec.rb b/spec/ruby/library/net/http/http/read_timeout_spec.rb
deleted file mode 100644
index e23ee76025..0000000000
--- a/spec/ruby/library/net/http/http/read_timeout_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTP#read_timeout" do
- it "returns the seconds to wait until reading one block" do
- net = Net::HTTP.new("localhost")
- net.read_timeout.should eql(60)
- net.read_timeout = 10
- net.read_timeout.should eql(10)
- end
-end
-
-describe "Net::HTTP#read_timeout=" do
- it "sets the seconds to wait till the connection is open" do
- net = Net::HTTP.new("localhost")
- net.read_timeout = 10
- net.read_timeout.should eql(10)
- end
-
- it "returns the newly set value" do
- net = Net::HTTP.new("localhost")
- (net.read_timeout = 10).should eql(10)
- end
-end
diff --git a/spec/ruby/library/net/http/http/request_get_spec.rb b/spec/ruby/library/net/http/http/request_get_spec.rb
deleted file mode 100644
index 9932ef0beb..0000000000
--- a/spec/ruby/library/net/http/http/request_get_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-require_relative 'shared/request_get'
-
-describe "Net::HTTP#request_get" do
- it_behaves_like :net_http_request_get, :get2
-end
diff --git a/spec/ruby/library/net/http/http/request_head_spec.rb b/spec/ruby/library/net/http/http/request_head_spec.rb
deleted file mode 100644
index 788101c951..0000000000
--- a/spec/ruby/library/net/http/http/request_head_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-require_relative 'shared/request_head'
-
-describe "Net::HTTP#request_head" do
- it_behaves_like :net_http_request_head, :request_head
-end
diff --git a/spec/ruby/library/net/http/http/request_post_spec.rb b/spec/ruby/library/net/http/http/request_post_spec.rb
deleted file mode 100644
index 7ac67cf95d..0000000000
--- a/spec/ruby/library/net/http/http/request_post_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-require_relative 'shared/request_post'
-
-describe "Net::HTTP#request_post" do
- it_behaves_like :net_http_request_post, :request_post
-end
diff --git a/spec/ruby/library/net/http/http/request_put_spec.rb b/spec/ruby/library/net/http/http/request_put_spec.rb
deleted file mode 100644
index 110ac43ca6..0000000000
--- a/spec/ruby/library/net/http/http/request_put_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-require_relative 'shared/request_put'
-
-describe "Net::HTTP#request_put" do
- it_behaves_like :net_http_request_put, :request_put
-end
diff --git a/spec/ruby/library/net/http/http/request_spec.rb b/spec/ruby/library/net/http/http/request_spec.rb
deleted file mode 100644
index e63dde9c8d..0000000000
--- a/spec/ruby/library/net/http/http/request_spec.rb
+++ /dev/null
@@ -1,109 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-
-describe "Net::HTTP#request" do
- before :each do
- NetHTTPSpecs.start_server
- @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
- end
-
- after :each do
- @http.finish if @http.started?
- NetHTTPSpecs.stop_server
- end
-
- describe "when passed request_object" do
- it "makes a HTTP Request based on the passed request_object" do
- response = @http.request(Net::HTTP::Get.new("/request"), "test=test")
- response.body.should == "Request type: GET"
-
- response = @http.request(Net::HTTP::Head.new("/request"), "test=test")
- response.body.should be_nil
-
- response = @http.request(Net::HTTP::Post.new("/request"), "test=test")
- response.body.should == "Request type: POST"
-
- response = @http.request(Net::HTTP::Put.new("/request"), "test=test")
- response.body.should == "Request type: PUT"
-
- response = @http.request(Net::HTTP::Proppatch.new("/request"), "test=test")
- response.body.should == "Request type: PROPPATCH"
-
- response = @http.request(Net::HTTP::Lock.new("/request"), "test=test")
- response.body.should == "Request type: LOCK"
-
- response = @http.request(Net::HTTP::Unlock.new("/request"), "test=test")
- response.body.should == "Request type: UNLOCK"
-
- # TODO: Does not work?
- #response = @http.request(Net::HTTP::Options.new("/request"), "test=test")
- #response.body.should be_nil
-
- response = @http.request(Net::HTTP::Propfind.new("/request"), "test=test")
- response.body.should == "Request type: PROPFIND"
-
- response = @http.request(Net::HTTP::Delete.new("/request"), "test=test")
- response.body.should == "Request type: DELETE"
-
- response = @http.request(Net::HTTP::Move.new("/request"), "test=test")
- response.body.should == "Request type: MOVE"
-
- response = @http.request(Net::HTTP::Copy.new("/request"), "test=test")
- response.body.should == "Request type: COPY"
-
- response = @http.request(Net::HTTP::Mkcol.new("/request"), "test=test")
- response.body.should == "Request type: MKCOL"
-
- response = @http.request(Net::HTTP::Trace.new("/request"), "test=test")
- response.body.should == "Request type: TRACE"
- end
- end
-
- describe "when passed request_object and request_body" do
- it "sends the passed request_body when making the HTTP Request" do
- response = @http.request(Net::HTTP::Get.new("/request/body"), "test=test")
- response.body.should == "test=test"
-
- response = @http.request(Net::HTTP::Head.new("/request/body"), "test=test")
- response.body.should be_nil
-
- response = @http.request(Net::HTTP::Post.new("/request/body"), "test=test")
- response.body.should == "test=test"
-
- response = @http.request(Net::HTTP::Put.new("/request/body"), "test=test")
- response.body.should == "test=test"
-
- response = @http.request(Net::HTTP::Proppatch.new("/request/body"), "test=test")
- response.body.should == "test=test"
-
- response = @http.request(Net::HTTP::Lock.new("/request/body"), "test=test")
- response.body.should == "test=test"
-
- response = @http.request(Net::HTTP::Unlock.new("/request/body"), "test=test")
- response.body.should == "test=test"
-
- # TODO: Does not work?
- #response = @http.request(Net::HTTP::Options.new("/request/body"), "test=test")
- #response.body.should be_nil
-
- response = @http.request(Net::HTTP::Propfind.new("/request/body"), "test=test")
- response.body.should == "test=test"
-
- response = @http.request(Net::HTTP::Delete.new("/request/body"), "test=test")
- response.body.should == "test=test"
-
- response = @http.request(Net::HTTP::Move.new("/request/body"), "test=test")
- response.body.should == "test=test"
-
- response = @http.request(Net::HTTP::Copy.new("/request/body"), "test=test")
- response.body.should == "test=test"
-
- response = @http.request(Net::HTTP::Mkcol.new("/request/body"), "test=test")
- response.body.should == "test=test"
-
- response = @http.request(Net::HTTP::Trace.new("/request/body"), "test=test")
- response.body.should == "test=test"
- end
- end
-end
diff --git a/spec/ruby/library/net/http/http/request_types_spec.rb b/spec/ruby/library/net/http/http/request_types_spec.rb
deleted file mode 100644
index 99d754d3d1..0000000000
--- a/spec/ruby/library/net/http/http/request_types_spec.rb
+++ /dev/null
@@ -1,254 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTP::Get" do
- it "is a subclass of Net::HTTPRequest" do
- Net::HTTP::Get.should < Net::HTTPRequest
- end
-
- it "represents the 'GET'-Request-Method" do
- Net::HTTP::Get::METHOD.should == "GET"
- end
-
- it "has no Request Body" do
- Net::HTTP::Get::REQUEST_HAS_BODY.should be_false
- end
-
- it "has a Response Body" do
- Net::HTTP::Get::RESPONSE_HAS_BODY.should be_true
- end
-end
-
-describe "Net::HTTP::Head" do
- it "is a subclass of Net::HTTPRequest" do
- Net::HTTP::Head.should < Net::HTTPRequest
- end
-
- it "represents the 'HEAD'-Request-Method" do
- Net::HTTP::Head::METHOD.should == "HEAD"
- end
-
- it "has no Request Body" do
- Net::HTTP::Head::REQUEST_HAS_BODY.should be_false
- end
-
- it "has no Response Body" do
- Net::HTTP::Head::RESPONSE_HAS_BODY.should be_false
- end
-end
-
-describe "Net::HTTP::Post" do
- it "is a subclass of Net::HTTPRequest" do
- Net::HTTP::Post.should < Net::HTTPRequest
- end
-
- it "represents the 'POST'-Request-Method" do
- Net::HTTP::Post::METHOD.should == "POST"
- end
-
- it "has a Request Body" do
- Net::HTTP::Post::REQUEST_HAS_BODY.should be_true
- end
-
- it "has a Response Body" do
- Net::HTTP::Post::RESPONSE_HAS_BODY.should be_true
- end
-end
-
-describe "Net::HTTP::Put" do
- it "is a subclass of Net::HTTPRequest" do
- Net::HTTP::Put.should < Net::HTTPRequest
- end
-
- it "represents the 'PUT'-Request-Method" do
- Net::HTTP::Put::METHOD.should == "PUT"
- end
-
- it "has a Request Body" do
- Net::HTTP::Put::REQUEST_HAS_BODY.should be_true
- end
-
- it "has a Response Body" do
- Net::HTTP::Put::RESPONSE_HAS_BODY.should be_true
- end
-end
-
-describe "Net::HTTP::Delete" do
- it "is a subclass of Net::HTTPRequest" do
- Net::HTTP::Delete.should < Net::HTTPRequest
- end
-
- it "represents the 'DELETE'-Request-Method" do
- Net::HTTP::Delete::METHOD.should == "DELETE"
- end
-
- it "has no Request Body" do
- Net::HTTP::Delete::REQUEST_HAS_BODY.should be_false
- end
-
- it "has a Response Body" do
- Net::HTTP::Delete::RESPONSE_HAS_BODY.should be_true
- end
-end
-
-describe "Net::HTTP::Options" do
- it "is a subclass of Net::HTTPRequest" do
- Net::HTTP::Options.should < Net::HTTPRequest
- end
-
- it "represents the 'OPTIONS'-Request-Method" do
- Net::HTTP::Options::METHOD.should == "OPTIONS"
- end
-
- it "has no Request Body" do
- Net::HTTP::Options::REQUEST_HAS_BODY.should be_false
- end
-
- it "has no Response Body" do
- Net::HTTP::Options::RESPONSE_HAS_BODY.should be_true
- end
-end
-
-describe "Net::HTTP::Trace" do
- it "is a subclass of Net::HTTPRequest" do
- Net::HTTP::Trace.should < Net::HTTPRequest
- end
-
- it "represents the 'TRACE'-Request-Method" do
- Net::HTTP::Trace::METHOD.should == "TRACE"
- end
-
- it "has no Request Body" do
- Net::HTTP::Trace::REQUEST_HAS_BODY.should be_false
- end
-
- it "has a Response Body" do
- Net::HTTP::Trace::RESPONSE_HAS_BODY.should be_true
- end
-end
-
-describe "Net::HTTP::Propfind" do
- it "is a subclass of Net::HTTPRequest" do
- Net::HTTP::Propfind.should < Net::HTTPRequest
- end
-
- it "represents the 'PROPFIND'-Request-Method" do
- Net::HTTP::Propfind::METHOD.should == "PROPFIND"
- end
-
- it "has a Request Body" do
- Net::HTTP::Propfind::REQUEST_HAS_BODY.should be_true
- end
-
- it "has a Response Body" do
- Net::HTTP::Propfind::RESPONSE_HAS_BODY.should be_true
- end
-end
-
-describe "Net::HTTP::Proppatch" do
- it "is a subclass of Net::HTTPRequest" do
- Net::HTTP::Proppatch.should < Net::HTTPRequest
- end
-
- it "represents the 'PROPPATCH'-Request-Method" do
- Net::HTTP::Proppatch::METHOD.should == "PROPPATCH"
- end
-
- it "has a Request Body" do
- Net::HTTP::Proppatch::REQUEST_HAS_BODY.should be_true
- end
-
- it "has a Response Body" do
- Net::HTTP::Proppatch::RESPONSE_HAS_BODY.should be_true
- end
-end
-
-describe "Net::HTTP::Mkcol" do
- it "is a subclass of Net::HTTPRequest" do
- Net::HTTP::Mkcol.should < Net::HTTPRequest
- end
-
- it "represents the 'MKCOL'-Request-Method" do
- Net::HTTP::Mkcol::METHOD.should == "MKCOL"
- end
-
- it "has a Request Body" do
- Net::HTTP::Mkcol::REQUEST_HAS_BODY.should be_true
- end
-
- it "has a Response Body" do
- Net::HTTP::Mkcol::RESPONSE_HAS_BODY.should be_true
- end
-end
-
-describe "Net::HTTP::Copy" do
- it "is a subclass of Net::HTTPRequest" do
- Net::HTTP::Copy.should < Net::HTTPRequest
- end
-
- it "represents the 'COPY'-Request-Method" do
- Net::HTTP::Copy::METHOD.should == "COPY"
- end
-
- it "has no Request Body" do
- Net::HTTP::Copy::REQUEST_HAS_BODY.should be_false
- end
-
- it "has a Response Body" do
- Net::HTTP::Copy::RESPONSE_HAS_BODY.should be_true
- end
-end
-
-describe "Net::HTTP::Move" do
- it "is a subclass of Net::HTTPRequest" do
- Net::HTTP::Move.should < Net::HTTPRequest
- end
-
- it "represents the 'MOVE'-Request-Method" do
- Net::HTTP::Move::METHOD.should == "MOVE"
- end
-
- it "has no Request Body" do
- Net::HTTP::Move::REQUEST_HAS_BODY.should be_false
- end
-
- it "has a Response Body" do
- Net::HTTP::Move::RESPONSE_HAS_BODY.should be_true
- end
-end
-
-describe "Net::HTTP::Lock" do
- it "is a subclass of Net::HTTPRequest" do
- Net::HTTP::Lock.should < Net::HTTPRequest
- end
-
- it "represents the 'LOCK'-Request-Method" do
- Net::HTTP::Lock::METHOD.should == "LOCK"
- end
-
- it "has a Request Body" do
- Net::HTTP::Lock::REQUEST_HAS_BODY.should be_true
- end
-
- it "has a Response Body" do
- Net::HTTP::Lock::RESPONSE_HAS_BODY.should be_true
- end
-end
-
-describe "Net::HTTP::Unlock" do
- it "is a subclass of Net::HTTPRequest" do
- Net::HTTP::Unlock.should < Net::HTTPRequest
- end
-
- it "represents the 'UNLOCK'-Request-Method" do
- Net::HTTP::Unlock::METHOD.should == "UNLOCK"
- end
-
- it "has a Request Body" do
- Net::HTTP::Unlock::REQUEST_HAS_BODY.should be_true
- end
-
- it "has a Response Body" do
- Net::HTTP::Unlock::RESPONSE_HAS_BODY.should be_true
- end
-end
diff --git a/spec/ruby/library/net/http/http/send_request_spec.rb b/spec/ruby/library/net/http/http/send_request_spec.rb
deleted file mode 100644
index 83e9448b8b..0000000000
--- a/spec/ruby/library/net/http/http/send_request_spec.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-
-describe "Net::HTTP#send_request" do
- before :each do
- NetHTTPSpecs.start_server
- @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
-
- # HEAD is special so handled separately
- @methods = %w[
- GET POST PUT DELETE
- OPTIONS
- PROPFIND PROPPATCH LOCK UNLOCK
- ]
- end
-
- after :each do
- @http.finish if @http.started?
- NetHTTPSpecs.stop_server
- end
-
- # TODO: Does only work with GET and POST requests
- describe "when passed type, path" do
- it "sends a HTTP Request of the passed type to the passed path" do
- response = @http.send_request("HEAD", "/request")
- response.body.should be_nil
-
- (@methods - %w[POST PUT]).each do |method|
- response = @http.send_request(method, "/request")
- response.body.should == "Request type: #{method}"
- end
- end
- end
-
- describe "when passed type, path, body" do
- it "sends a HTTP Request with the passed body" do
- response = @http.send_request("HEAD", "/request/body", "test=test")
- response.body.should be_nil
-
- @methods.each do |method|
- response = @http.send_request(method, "/request/body", "test=test")
- response.body.should == "test=test"
- end
- end
- end
-
- describe "when passed type, path, body, headers" do
- it "sends a HTTP Request with the passed headers" do
- referer = 'https://www.ruby-lang.org/'.freeze
-
- response = @http.send_request("HEAD", "/request/header", "test=test", "referer" => referer)
- response.body.should be_nil
-
- @methods.each do |method|
- response = @http.send_request(method, "/request/header", "test=test", "referer" => referer)
- response.body.should include('"Referer"=>"' + referer + '"')
- end
- end
- end
-end
diff --git a/spec/ruby/library/net/http/http/set_debug_output_spec.rb b/spec/ruby/library/net/http/http/set_debug_output_spec.rb
deleted file mode 100644
index 94b6f4499d..0000000000
--- a/spec/ruby/library/net/http/http/set_debug_output_spec.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require "stringio"
-require_relative 'fixtures/http_server'
-
-describe "Net::HTTP#set_debug_output when passed io" do
- before :each do
- NetHTTPSpecs.start_server
- @http = Net::HTTP.new("localhost", NetHTTPSpecs.port)
- end
-
- after :each do
- @http.finish if @http.started?
- NetHTTPSpecs.stop_server
- end
-
- it "sets the passed io as output stream for debugging" do
- io = StringIO.new
-
- @http.set_debug_output(io)
- @http.start
- io.string.should_not be_empty
- size = io.string.size
-
- @http.get("/")
- io.string.size.should > size
- end
-
- it "outputs a warning when the connection has already been started" do
- @http.start
- -> { @http.set_debug_output(StringIO.new) }.should complain(/Net::HTTP#set_debug_output called after HTTP started/)
- end
-end
diff --git a/spec/ruby/library/net/http/http/socket_type_spec.rb b/spec/ruby/library/net/http/http/socket_type_spec.rb
deleted file mode 100644
index 5c844ddf94..0000000000
--- a/spec/ruby/library/net/http/http/socket_type_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTP.socket_type" do
- it "returns BufferedIO" do
- Net::HTTP.socket_type.should == Net::BufferedIO
- end
-end
diff --git a/spec/ruby/library/net/http/http/start_spec.rb b/spec/ruby/library/net/http/http/start_spec.rb
deleted file mode 100644
index a2768eed18..0000000000
--- a/spec/ruby/library/net/http/http/start_spec.rb
+++ /dev/null
@@ -1,111 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-
-describe "Net::HTTP.start" do
- before :each do
- NetHTTPSpecs.start_server
- @port = NetHTTPSpecs.port
- end
-
- after :each do
- NetHTTPSpecs.stop_server
- end
-
- describe "when not passed a block" do
- before :each do
- @http = Net::HTTP.start("localhost", @port)
- end
-
- after :each do
- @http.finish if @http.started?
- end
-
- it "returns a new Net::HTTP object for the passed address and port" do
- @http.should be_kind_of(Net::HTTP)
- @http.address.should == "localhost"
- @http.port.should == @port
- end
-
- it "opens the tcp connection" do
- @http.started?.should be_true
- end
- end
-
- describe "when passed a block" do
- it "returns the blocks return value" do
- Net::HTTP.start("localhost", @port) { :test }.should == :test
- end
-
- it "yields the new Net::HTTP object to the block" do
- yielded = false
- Net::HTTP.start("localhost", @port) do |net|
- yielded = true
- net.should be_kind_of(Net::HTTP)
- end
- yielded.should be_true
- end
-
- it "opens the tcp connection before yielding" do
- Net::HTTP.start("localhost", @port) { |http| http.started?.should be_true }
- end
-
- it "closes the tcp connection after yielding" do
- net = nil
- Net::HTTP.start("localhost", @port) { |x| net = x }
- net.started?.should be_false
- end
- end
-end
-
-describe "Net::HTTP#start" do
- before :each do
- NetHTTPSpecs.start_server
- @http = Net::HTTP.new("localhost", NetHTTPSpecs.port)
- end
-
- after :each do
- @http.finish if @http.started?
- NetHTTPSpecs.stop_server
- end
-
- it "returns self" do
- @http.start.should equal(@http)
- end
-
- it "opens the tcp connection" do
- @http.start
- @http.started?.should be_true
- end
-
- describe "when self has already been started" do
- it "raises an IOError" do
- @http.start
- -> { @http.start }.should raise_error(IOError)
- end
- end
-
- describe "when passed a block" do
- it "returns the blocks return value" do
- @http.start { :test }.should == :test
- end
-
- it "yields the new Net::HTTP object to the block" do
- yielded = false
- @http.start do |http|
- yielded = true
- http.should equal(@http)
- end
- yielded.should be_true
- end
-
- it "opens the tcp connection before yielding" do
- @http.start { |http| http.started?.should be_true }
- end
-
- it "closes the tcp connection after yielding" do
- @http.start { }
- @http.started?.should be_false
- end
- end
-end
diff --git a/spec/ruby/library/net/http/http/started_spec.rb b/spec/ruby/library/net/http/http/started_spec.rb
deleted file mode 100644
index ea441ed16a..0000000000
--- a/spec/ruby/library/net/http/http/started_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-require_relative 'shared/started'
-
-describe "Net::HTTP#started?" do
- it_behaves_like :net_http_started_p, :started?
-end
diff --git a/spec/ruby/library/net/http/http/trace_spec.rb b/spec/ruby/library/net/http/http/trace_spec.rb
deleted file mode 100644
index 94a1bf6655..0000000000
--- a/spec/ruby/library/net/http/http/trace_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-
-describe "Net::HTTP#trace" do
- before :each do
- NetHTTPSpecs.start_server
- @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
- end
-
- after :each do
- @http.finish if @http.started?
- NetHTTPSpecs.stop_server
- end
-
- it "sends a TRACE request to the passed path and returns the response" do
- response = @http.trace("/request")
- response.body.should == "Request type: TRACE"
- end
-
- it "returns a Net::HTTPResponse" do
- @http.trace("/request").should be_kind_of(Net::HTTPResponse)
- end
-end
diff --git a/spec/ruby/library/net/http/http/unlock_spec.rb b/spec/ruby/library/net/http/http/unlock_spec.rb
deleted file mode 100644
index a4f1b7a1d1..0000000000
--- a/spec/ruby/library/net/http/http/unlock_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/http_server'
-
-describe "Net::HTTP#unlock" do
- before :each do
- NetHTTPSpecs.start_server
- @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
- end
-
- after :each do
- @http.finish if @http.started?
- NetHTTPSpecs.stop_server
- end
-
- it "sends an UNLOCK request to the passed path and returns the response" do
- response = @http.unlock("/request", "test=test")
- response.body.should == "Request type: UNLOCK"
- end
-
- it "returns a Net::HTTPResponse" do
- @http.unlock("/request", "test=test").should be_kind_of(Net::HTTPResponse)
- end
-end
diff --git a/spec/ruby/library/net/http/http/use_ssl_spec.rb b/spec/ruby/library/net/http/http/use_ssl_spec.rb
deleted file mode 100644
index be1ec7fa25..0000000000
--- a/spec/ruby/library/net/http/http/use_ssl_spec.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTP#use_ssl?" do
- it "returns false" do
- http = Net::HTTP.new("localhost")
- http.use_ssl?.should be_false
- end
-end
diff --git a/spec/ruby/library/net/http/http/version_1_1_spec.rb b/spec/ruby/library/net/http/http/version_1_1_spec.rb
deleted file mode 100644
index 1c069e9ea6..0000000000
--- a/spec/ruby/library/net/http/http/version_1_1_spec.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'shared/version_1_1'
-
-describe "Net::HTTP.version_1_1?" do
- it_behaves_like :net_http_version_1_1_p, :version_1_1?
-end
diff --git a/spec/ruby/library/net/http/http/version_1_2_spec.rb b/spec/ruby/library/net/http/http/version_1_2_spec.rb
deleted file mode 100644
index 4e601462c9..0000000000
--- a/spec/ruby/library/net/http/http/version_1_2_spec.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'shared/version_1_2'
-
-describe "Net::HTTP.version_1_2" do
- it "turns on net/http 1.2 features" do
- Net::HTTP.version_1_2
-
- Net::HTTP.version_1_2?.should be_true
- Net::HTTP.version_1_1?.should be_false
- end
-
- it "returns true" do
- Net::HTTP.version_1_2.should be_true
- end
-end
-
-describe "Net::HTTP.version_1_2?" do
- it_behaves_like :net_http_version_1_2_p, :version_1_2?
-end
diff --git a/spec/ruby/library/net/http/httpexceptions/initialize_spec.rb b/spec/ruby/library/net/http/httpexceptions/initialize_spec.rb
deleted file mode 100644
index 8e3fd8cc02..0000000000
--- a/spec/ruby/library/net/http/httpexceptions/initialize_spec.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-
-describe "Net::HTTPExceptions#initialize when passed message, response" do
- before :each do
- @exception = NetHTTPExceptionsSpecs::Simple.new("error message", "a http response")
- end
-
- it "calls super with the passed message" do
- @exception.message.should == "error message"
- end
-
- it "sets self's response to the passed response" do
- @exception.response.should == "a http response"
- end
-end
diff --git a/spec/ruby/library/net/http/httpexceptions/response_spec.rb b/spec/ruby/library/net/http/httpexceptions/response_spec.rb
deleted file mode 100644
index 205b2bc212..0000000000
--- a/spec/ruby/library/net/http/httpexceptions/response_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-
-describe "Net::HTTPExceptions#response" do
- it "returns self's response" do
- exception = NetHTTPExceptionsSpecs::Simple.new("error message", "a http response")
- exception.response.should == "a http response"
- end
-end
diff --git a/spec/ruby/library/net/http/httpgenericrequest/body_exist_spec.rb b/spec/ruby/library/net/http/httpgenericrequest/body_exist_spec.rb
deleted file mode 100644
index 7d081939b8..0000000000
--- a/spec/ruby/library/net/http/httpgenericrequest/body_exist_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPGenericRequest#body_exist?" do
- it "returns true when the response is expected to have a body" do
- request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
- request.body_exist?.should be_true
-
- request = Net::HTTPGenericRequest.new("POST", true, false, "/some/path")
- request.body_exist?.should be_false
- end
-
- describe "when $VERBOSE is true" do
- it "emits a warning" do
- request = Net::HTTPGenericRequest.new("POST", true, false, "/some/path")
- -> {
- request.body_exist?
- }.should complain(/body_exist\? is obsolete/, verbose: true)
- end
- end
-end
diff --git a/spec/ruby/library/net/http/httpgenericrequest/body_spec.rb b/spec/ruby/library/net/http/httpgenericrequest/body_spec.rb
deleted file mode 100644
index a215895b57..0000000000
--- a/spec/ruby/library/net/http/httpgenericrequest/body_spec.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require "stringio"
-
-describe "Net::HTTPGenericRequest#body" do
- it "returns self's request body" do
- request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
- request.body.should be_nil
-
- request.body = "Some Content"
- request.body.should == "Some Content"
- end
-end
-
-describe "Net::HTTPGenericRequest#body=" do
- before :each do
- @request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
- end
-
- it "sets self's body content to the passed String" do
- @request.body = "Some Content"
- @request.body.should == "Some Content"
- end
-
- it "sets self's body stream to nil" do
- @request.body_stream = StringIO.new("")
- @request.body = "Some Content"
- @request.body_stream.should be_nil
- end
-end
diff --git a/spec/ruby/library/net/http/httpgenericrequest/body_stream_spec.rb b/spec/ruby/library/net/http/httpgenericrequest/body_stream_spec.rb
deleted file mode 100644
index c2a60e6836..0000000000
--- a/spec/ruby/library/net/http/httpgenericrequest/body_stream_spec.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require "stringio"
-
-describe "Net::HTTPGenericRequest#body_stream" do
- it "returns self's body stream Object" do
- request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
- request.body_stream.should be_nil
-
- stream = StringIO.new("test")
- request.body_stream = stream
- request.body_stream.should equal(stream)
- end
-end
-
-describe "Net::HTTPGenericRequest#body_stream=" do
- before :each do
- @request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
- @stream = StringIO.new("test")
- end
-
- it "sets self's body stream to the passed Object" do
- @request.body_stream = @stream
- @request.body_stream.should equal(@stream)
- end
-
- it "sets self's body to nil" do
- @request.body = "Some Content"
- @request.body_stream = @stream
- @request.body.should be_nil
- end
-end
diff --git a/spec/ruby/library/net/http/httpgenericrequest/exec_spec.rb b/spec/ruby/library/net/http/httpgenericrequest/exec_spec.rb
deleted file mode 100644
index da5b1a63be..0000000000
--- a/spec/ruby/library/net/http/httpgenericrequest/exec_spec.rb
+++ /dev/null
@@ -1,131 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require "stringio"
-
-describe "Net::HTTPGenericRequest#exec when passed socket, version, path" do
- before :each do
- @socket = StringIO.new("")
- @buffered_socket = Net::BufferedIO.new(@socket)
- end
-
- it "executes the request over the socket to the path using the HTTP version" do
- request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
-
- request.exec(@buffered_socket, "1.1", "/some/path")
- str = @socket.string
-
- str.should =~ %r[POST /some/path HTTP/1.1\r\n]
- str.should =~ %r[Accept: \*/\*\r\n]
- str[-4..-1].should == "\r\n\r\n"
-
- request = Net::HTTPGenericRequest.new("GET", true, true, "/some/path",
- "Content-Type" => "text/html")
-
- request.exec(@buffered_socket, "1.0", "/some/other/path")
- str = @socket.string
-
- str.should =~ %r[GET /some/other/path HTTP/1.0\r\n]
- str.should =~ %r[Accept: \*/\*\r\n]
- str.should =~ %r[Content-Type: text/html\r\n]
- str[-4..-1].should == "\r\n\r\n"
- end
-
- describe "when a request body is set" do
- it "sets the 'Content-Type' header to 'application/x-www-form-urlencoded' unless the 'Content-Type' header is supplied" do
- request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
- request.body = "Some Content"
-
- request.exec(@buffered_socket, "1.1", "/some/other/path")
- str = @socket.string
-
- str.should =~ %r[POST /some/other/path HTTP/1.1\r\n]
- str.should =~ %r[Accept: \*/\*\r\n]
- str.should =~ %r[Content-Type: application/x-www-form-urlencoded\r\n]
- str.should =~ %r[Content-Length: 12\r\n]
- str[-16..-1].should == "\r\n\r\nSome Content"
- end
-
- it "correctly sets the 'Content-Length' header and includes the body" do
- request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path",
- "Content-Type" => "text/html")
- request.body = "Some Content"
-
- request.exec(@buffered_socket, "1.1", "/some/other/path")
- str = @socket.string
-
- str.should =~ %r[POST /some/other/path HTTP/1.1\r\n]
- str.should =~ %r[Accept: \*/\*\r\n]
- str.should =~ %r[Content-Type: text/html\r\n]
- str.should =~ %r[Content-Length: 12\r\n]
- str[-16..-1].should == "\r\n\r\nSome Content"
- end
- end
-
- describe "when a body stream is set" do
- it "sets the 'Content-Type' header to 'application/x-www-form-urlencoded' unless the 'Content-Type' header is supplied" do
- request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path",
- "Content-Length" => "10")
- request.body_stream = StringIO.new("a" * 20)
-
- request.exec(@buffered_socket, "1.1", "/some/other/path")
- str = @socket.string
-
- str.should =~ %r[POST /some/other/path HTTP/1.1\r\n]
- str.should =~ %r[Accept: \*/\*\r\n]
- str.should =~ %r[Content-Type: application/x-www-form-urlencoded\r\n]
- str.should =~ %r[Content-Length: 10\r\n]
- str[-24..-1].should == "\r\n\r\naaaaaaaaaaaaaaaaaaaa"
- end
-
- it "sends the whole stream, regardless of the 'Content-Length' header" do
- request = Net::HTTPGenericRequest.new("POST", true, true,"/some/path",
- "Content-Type" => "text/html",
- "Content-Length" => "10")
- request.body_stream = StringIO.new("a" * 20)
-
- request.exec(@buffered_socket, "1.1", "/some/other/path")
- str = @socket.string
-
- str.should =~ %r[POST /some/other/path HTTP/1.1\r\n]
- str.should =~ %r[Accept: \*/\*\r\n]
- str.should =~ %r[Content-Type: text/html\r\n]
- str.should =~ %r[Content-Length: 10\r\n]
- str[-24..-1].should == "\r\n\r\naaaaaaaaaaaaaaaaaaaa"
- end
-
- it "sends the request in chunks when 'Transfer-Encoding' is set to 'chunked'" do
- request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path",
- "Content-Type" => "text/html",
- "Transfer-Encoding" => "chunked")
- datasize = 1024 * 10
- request.body_stream = StringIO.new("a" * datasize)
-
- request.exec(@buffered_socket, "1.1", "/some/other/path")
- str = @socket.string
-
- str.should =~ %r[POST /some/other/path HTTP/1.1\r\n]
- str.should =~ %r[Accept: \*/\*\r\n]
- str.should =~ %r[Content-Type: text/html\r\n]
- str.should =~ %r[Transfer-Encoding: chunked\r\n]
- str =~ %r[\r\n\r\n]
- str = $'
- while datasize > 0
- chunk_size_line, str = str.split(/\r\n/, 2)
- chunk_size = chunk_size_line[/\A[0-9A-Fa-f]+/].to_i(16)
- str.slice!(0, chunk_size).should == 'a' * chunk_size
- datasize -= chunk_size
- str.slice!(0, 2).should == "\r\n"
- end
- datasize.should == 0
- str.should == %"0\r\n\r\n"
- end
-
- it "raises an ArgumentError when the 'Content-Length' is not set or 'Transfer-Encoding' is not set to 'chunked'" do
- request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path",
- "Content-Type" => "text/html")
- request.body_stream = StringIO.new("Some Content")
-
- -> { request.exec(@buffered_socket, "1.1", "/some/other/path") }.should raise_error(ArgumentError)
- end
- end
-end
diff --git a/spec/ruby/library/net/http/httpgenericrequest/inspect_spec.rb b/spec/ruby/library/net/http/httpgenericrequest/inspect_spec.rb
deleted file mode 100644
index 36240949c3..0000000000
--- a/spec/ruby/library/net/http/httpgenericrequest/inspect_spec.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPGenericRequest#inspect" do
- it "returns a String representation of self" do
- request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
- request.inspect.should == "#<Net::HTTPGenericRequest POST>"
-
- request = Net::HTTPGenericRequest.new("GET", false, true, "/some/path")
- request.inspect.should == "#<Net::HTTPGenericRequest GET>"
-
- request = Net::HTTPGenericRequest.new("BLA", true, true, "/some/path")
- request.inspect.should == "#<Net::HTTPGenericRequest BLA>"
-
- # Subclasses
- request = Net::HTTP::Get.new("/some/path")
- request.inspect.should == "#<Net::HTTP::Get GET>"
-
- request = Net::HTTP::Post.new("/some/path")
- request.inspect.should == "#<Net::HTTP::Post POST>"
-
- request = Net::HTTP::Trace.new("/some/path")
- request.inspect.should == "#<Net::HTTP::Trace TRACE>"
- end
-end
diff --git a/spec/ruby/library/net/http/httpgenericrequest/method_spec.rb b/spec/ruby/library/net/http/httpgenericrequest/method_spec.rb
deleted file mode 100644
index 3f7c2cbf2b..0000000000
--- a/spec/ruby/library/net/http/httpgenericrequest/method_spec.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPGenericRequest#method" do
- it "returns self's request method" do
- request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
- request.method.should == "POST"
-
- request = Net::HTTPGenericRequest.new("GET", false, true, "/some/path")
- request.method.should == "GET"
-
- request = Net::HTTPGenericRequest.new("BLA", true, true, "/some/path")
- request.method.should == "BLA"
- end
-end
diff --git a/spec/ruby/library/net/http/httpgenericrequest/path_spec.rb b/spec/ruby/library/net/http/httpgenericrequest/path_spec.rb
deleted file mode 100644
index fc4cf9af53..0000000000
--- a/spec/ruby/library/net/http/httpgenericrequest/path_spec.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPGenericRequest#path" do
- it "returns self's request path" do
- request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
- request.path.should == "/some/path"
-
- request = Net::HTTPGenericRequest.new("POST", true, true, "/some/other/path")
- request.path.should == "/some/other/path"
- end
-end
diff --git a/spec/ruby/library/net/http/httpgenericrequest/request_body_permitted_spec.rb b/spec/ruby/library/net/http/httpgenericrequest/request_body_permitted_spec.rb
deleted file mode 100644
index 50cd1ff637..0000000000
--- a/spec/ruby/library/net/http/httpgenericrequest/request_body_permitted_spec.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPGenericRequest#request_body_permitted?" do
- it "returns true when the request is expected to have a body" do
- request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
- request.request_body_permitted?.should be_true
-
- request = Net::HTTPGenericRequest.new("POST", false, true, "/some/path")
- request.request_body_permitted?.should be_false
- end
-end
diff --git a/spec/ruby/library/net/http/httpgenericrequest/response_body_permitted_spec.rb b/spec/ruby/library/net/http/httpgenericrequest/response_body_permitted_spec.rb
deleted file mode 100644
index 0c4165d0ab..0000000000
--- a/spec/ruby/library/net/http/httpgenericrequest/response_body_permitted_spec.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPGenericRequest#response_body_permitted?" do
- it "returns true when the response is expected to have a body" do
- request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
- request.response_body_permitted?.should be_true
-
- request = Net::HTTPGenericRequest.new("POST", true, false, "/some/path")
- request.response_body_permitted?.should be_false
- end
-end
diff --git a/spec/ruby/library/net/http/httpgenericrequest/set_body_internal_spec.rb b/spec/ruby/library/net/http/httpgenericrequest/set_body_internal_spec.rb
deleted file mode 100644
index 7494c69173..0000000000
--- a/spec/ruby/library/net/http/httpgenericrequest/set_body_internal_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPGenericRequest#set_body_internal when passed string" do
- before :each do
- @request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
- end
-
- it "sets self's body to the passed string" do
- @request.set_body_internal("Some Content")
- @request.body.should == "Some Content"
- end
-
- it "raises an ArgumentError when the body or body_stream of self have already been set" do
- @request.body = "Some Content"
- -> { @request.set_body_internal("Some other Content") }.should raise_error(ArgumentError)
-
- @request.body_stream = "Some Content"
- -> { @request.set_body_internal("Some other Content") }.should raise_error(ArgumentError)
- end
-end
diff --git a/spec/ruby/library/net/http/httpheader/add_field_spec.rb b/spec/ruby/library/net/http/httpheader/add_field_spec.rb
deleted file mode 100644
index 882d5ac5bb..0000000000
--- a/spec/ruby/library/net/http/httpheader/add_field_spec.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-
-describe "Net::HTTPHeader#add_field when passed key, value" do
- before :each do
- @headers = NetHTTPHeaderSpecs::Example.new
- end
-
- it "adds the passed value to the header entry with the passed key" do
- @headers.add_field("My-Header", "a")
- @headers.get_fields("My-Header").should == ["a"]
-
- @headers.add_field("My-Header", "b")
- @headers.get_fields("My-Header").should == ["a", "b"]
-
- @headers.add_field("My-Header", "c")
- @headers.get_fields("My-Header").should == ["a", "b", "c"]
- end
-
- it "is case-insensitive" do
- @headers.add_field("My-Header", "a")
- @headers.get_fields("My-Header").should == ["a"]
-
- @headers.add_field("my-header", "b")
- @headers.get_fields("My-Header").should == ["a", "b"]
-
- @headers.add_field("MY-HEADER", "c")
- @headers.get_fields("My-Header").should == ["a", "b", "c"]
- end
-end
diff --git a/spec/ruby/library/net/http/httpheader/basic_auth_spec.rb b/spec/ruby/library/net/http/httpheader/basic_auth_spec.rb
deleted file mode 100644
index fa2a710fe1..0000000000
--- a/spec/ruby/library/net/http/httpheader/basic_auth_spec.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-
-describe "Net::HTTPHeader#basic_auth when passed account, password" do
- before :each do
- @headers = NetHTTPHeaderSpecs::Example.new
- end
-
- it "sets the 'Authorization' Header entry for basic authorization" do
- @headers.basic_auth("rubyspec", "rocks")
- @headers["Authorization"].should == "Basic cnVieXNwZWM6cm9ja3M="
- end
-end
diff --git a/spec/ruby/library/net/http/httpheader/canonical_each_spec.rb b/spec/ruby/library/net/http/httpheader/canonical_each_spec.rb
deleted file mode 100644
index 0dddd3049b..0000000000
--- a/spec/ruby/library/net/http/httpheader/canonical_each_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-require_relative 'shared/each_capitalized'
-
-describe "Net::HTTPHeader#canonical_each" do
- it_behaves_like :net_httpheader_each_capitalized, :canonical_each
-end
diff --git a/spec/ruby/library/net/http/httpheader/chunked_spec.rb b/spec/ruby/library/net/http/httpheader/chunked_spec.rb
deleted file mode 100644
index 96b758981b..0000000000
--- a/spec/ruby/library/net/http/httpheader/chunked_spec.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-
-describe "Net::HTTPHeader#chunked?" do
- before :each do
- @headers = NetHTTPHeaderSpecs::Example.new
- end
-
- it "returns true if the 'Transfer-Encoding' header entry is set to chunked" do
- @headers.chunked?.should be_false
-
- @headers["Transfer-Encoding"] = "bla"
- @headers.chunked?.should be_false
-
- @headers["Transfer-Encoding"] = "blachunkedbla"
- @headers.chunked?.should be_false
-
- @headers["Transfer-Encoding"] = "chunked"
- @headers.chunked?.should be_true
- end
-end
diff --git a/spec/ruby/library/net/http/httpheader/content_length_spec.rb b/spec/ruby/library/net/http/httpheader/content_length_spec.rb
deleted file mode 100644
index e344817e82..0000000000
--- a/spec/ruby/library/net/http/httpheader/content_length_spec.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-
-describe "Net::HTTPHeader#content_length" do
- before :each do
- @headers = NetHTTPHeaderSpecs::Example.new
- end
-
- it "returns nil if no 'Content-Length' header entry is set" do
- @headers.content_length.should be_nil
- end
-
- it "raises a Net::HTTPHeaderSyntaxError when the 'Content-Length' header entry has an invalid format" do
- @headers["Content-Length"] = "invalid"
- -> { @headers.content_length }.should raise_error(Net::HTTPHeaderSyntaxError)
- end
-
- it "returns the value of the 'Content-Length' header entry as an Integer" do
- @headers["Content-Length"] = "123"
- @headers.content_length.should eql(123)
-
- @headers["Content-Length"] = "123valid"
- @headers.content_length.should eql(123)
-
- @headers["Content-Length"] = "valid123"
- @headers.content_length.should eql(123)
- end
-end
-
-describe "Net::HTTPHeader#content_length=" do
- before :each do
- @headers = NetHTTPHeaderSpecs::Example.new
- end
-
- it "removes the 'Content-Length' entry if passed false or nil" do
- @headers["Content-Length"] = "123"
- @headers.content_length = nil
- @headers["Content-Length"].should be_nil
- end
-
- it "sets the 'Content-Length' entry to the passed value" do
- @headers.content_length = "123"
- @headers["Content-Length"].should == "123"
-
- @headers.content_length = "123valid"
- @headers["Content-Length"].should == "123"
- end
-
- it "sets the 'Content-Length' entry to 0 if the passed value is not valid" do
- @headers.content_length = "invalid123"
- @headers["Content-Length"].should == "0"
- end
-end
diff --git a/spec/ruby/library/net/http/httpheader/content_range_spec.rb b/spec/ruby/library/net/http/httpheader/content_range_spec.rb
deleted file mode 100644
index ba75f9a9a1..0000000000
--- a/spec/ruby/library/net/http/httpheader/content_range_spec.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-
-describe "Net::HTTPHeader#content_range" do
- before :each do
- @headers = NetHTTPHeaderSpecs::Example.new
- end
-
- it "returns a Range object that represents the 'Content-Range' header entry" do
- @headers["Content-Range"] = "bytes 0-499/1234"
- @headers.content_range.should == (0..499)
-
- @headers["Content-Range"] = "bytes 500-1233/1234"
- @headers.content_range.should == (500..1233)
- end
-
- it "returns nil when there is no 'Content-Range' header entry" do
- @headers.content_range.should be_nil
- end
-
- it "raises a Net::HTTPHeaderSyntaxError when the 'Content-Range' has an invalid format" do
- @headers["Content-Range"] = "invalid"
- -> { @headers.content_range }.should raise_error(Net::HTTPHeaderSyntaxError)
-
- @headers["Content-Range"] = "bytes 123-abc"
- -> { @headers.content_range }.should raise_error(Net::HTTPHeaderSyntaxError)
-
- @headers["Content-Range"] = "bytes abc-123"
- -> { @headers.content_range }.should raise_error(Net::HTTPHeaderSyntaxError)
- end
-end
diff --git a/spec/ruby/library/net/http/httpheader/content_type_spec.rb b/spec/ruby/library/net/http/httpheader/content_type_spec.rb
deleted file mode 100644
index 1f8b4ba326..0000000000
--- a/spec/ruby/library/net/http/httpheader/content_type_spec.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-require_relative 'shared/set_content_type'
-
-describe "Net::HTTPHeader#content_type" do
- before :each do
- @headers = NetHTTPHeaderSpecs::Example.new
- end
-
- it "returns the content type string, as per 'Content-Type' header entry" do
- @headers["Content-Type"] = "text/html"
- @headers.content_type.should == "text/html"
-
- @headers["Content-Type"] = "text/html;charset=utf-8"
- @headers.content_type.should == "text/html"
- end
-
- it "returns nil if the 'Content-Type' header entry does not exist" do
- @headers.content_type.should be_nil
- end
-end
-
-describe "Net::HTTPHeader#content_type=" do
- it_behaves_like :net_httpheader_set_content_type, :content_type=
-end
diff --git a/spec/ruby/library/net/http/httpheader/delete_spec.rb b/spec/ruby/library/net/http/httpheader/delete_spec.rb
deleted file mode 100644
index 603ae198de..0000000000
--- a/spec/ruby/library/net/http/httpheader/delete_spec.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-
-describe "Net::HTTPHeader#delete when passed key" do
- before :each do
- @headers = NetHTTPHeaderSpecs::Example.new
- end
-
- it "removes the header entry with the passed key" do
- @headers["My-Header"] = "test"
- @headers.delete("My-Header")
-
- @headers["My-Header"].should be_nil
- @headers.size.should eql(0)
- end
-
- it "returns the removed values" do
- @headers["My-Header"] = "test"
- @headers.delete("My-Header").should == ["test"]
- end
-
- it "is case-insensitive" do
- @headers["My-Header"] = "test"
- @headers.delete("my-header")
-
- @headers["My-Header"].should be_nil
- @headers.size.should eql(0)
- end
-end
diff --git a/spec/ruby/library/net/http/httpheader/each_capitalized_name_spec.rb b/spec/ruby/library/net/http/httpheader/each_capitalized_name_spec.rb
deleted file mode 100644
index 1af2c6939c..0000000000
--- a/spec/ruby/library/net/http/httpheader/each_capitalized_name_spec.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-
-describe "Net::HTTPHeader#each_capitalized_name" do
- before :each do
- @headers = NetHTTPHeaderSpecs::Example.new
- @headers["My-Header"] = "test"
- @headers.add_field("My-Other-Header", "a")
- @headers.add_field("My-Other-Header", "b")
- end
-
- describe "when passed a block" do
- it "yields each header key to the passed block (keys capitalized)" do
- res = []
- @headers.each_capitalized_name do |key|
- res << key
- end
- res.sort.should == ["My-Header", "My-Other-Header"]
- end
- end
-
- describe "when passed no block" do
- it "returns an Enumerator" do
- enumerator = @headers.each_capitalized_name
- enumerator.should be_an_instance_of(Enumerator)
-
- res = []
- enumerator.each do |key|
- res << key
- end
- res.sort.should == ["My-Header", "My-Other-Header"]
- end
- end
-end
diff --git a/spec/ruby/library/net/http/httpheader/each_capitalized_spec.rb b/spec/ruby/library/net/http/httpheader/each_capitalized_spec.rb
deleted file mode 100644
index 961a2d051f..0000000000
--- a/spec/ruby/library/net/http/httpheader/each_capitalized_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-require_relative 'shared/each_capitalized'
-
-describe "Net::HTTPHeader#each_capitalized" do
- it_behaves_like :net_httpheader_each_capitalized, :each_capitalized
-end
diff --git a/spec/ruby/library/net/http/httpheader/each_header_spec.rb b/spec/ruby/library/net/http/httpheader/each_header_spec.rb
deleted file mode 100644
index 19634a001b..0000000000
--- a/spec/ruby/library/net/http/httpheader/each_header_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-require_relative 'shared/each_header'
-
-describe "Net::HTTPHeader#each_header" do
- it_behaves_like :net_httpheader_each_header, :each_header
-end
diff --git a/spec/ruby/library/net/http/httpheader/each_key_spec.rb b/spec/ruby/library/net/http/httpheader/each_key_spec.rb
deleted file mode 100644
index ebb17d2eac..0000000000
--- a/spec/ruby/library/net/http/httpheader/each_key_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-require_relative 'shared/each_name'
-
-describe "Net::HTTPHeader#each_key" do
- it_behaves_like :net_httpheader_each_name, :each_key
-end
diff --git a/spec/ruby/library/net/http/httpheader/each_name_spec.rb b/spec/ruby/library/net/http/httpheader/each_name_spec.rb
deleted file mode 100644
index f4533ebcfb..0000000000
--- a/spec/ruby/library/net/http/httpheader/each_name_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-require_relative 'shared/each_name'
-
-describe "Net::HTTPHeader#each_name" do
- it_behaves_like :net_httpheader_each_name, :each_name
-end
diff --git a/spec/ruby/library/net/http/httpheader/each_spec.rb b/spec/ruby/library/net/http/httpheader/each_spec.rb
deleted file mode 100644
index 7ba8434f75..0000000000
--- a/spec/ruby/library/net/http/httpheader/each_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-require_relative 'shared/each_header'
-
-describe "Net::HTTPHeader#each" do
- it_behaves_like :net_httpheader_each_header, :each
-end
diff --git a/spec/ruby/library/net/http/httpheader/each_value_spec.rb b/spec/ruby/library/net/http/httpheader/each_value_spec.rb
deleted file mode 100644
index 3224de7703..0000000000
--- a/spec/ruby/library/net/http/httpheader/each_value_spec.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-
-describe "Net::HTTPHeader#each_value" do
- before :each do
- @headers = NetHTTPHeaderSpecs::Example.new
- @headers["My-Header"] = "test"
- @headers.add_field("My-Other-Header", "a")
- @headers.add_field("My-Other-Header", "b")
- end
-
- describe "when passed a block" do
- it "yields each header entry's joined values" do
- res = []
- @headers.each_value do |value|
- res << value
- end
- res.sort.should == ["a, b", "test"]
- end
- end
-
- describe "when passed no block" do
- it "returns an Enumerator" do
- enumerator = @headers.each_value
- enumerator.should be_an_instance_of(Enumerator)
-
- res = []
- enumerator.each do |key|
- res << key
- end
- res.sort.should == ["a, b", "test"]
- end
- end
-end
diff --git a/spec/ruby/library/net/http/httpheader/element_reference_spec.rb b/spec/ruby/library/net/http/httpheader/element_reference_spec.rb
deleted file mode 100644
index 4a35e20d20..0000000000
--- a/spec/ruby/library/net/http/httpheader/element_reference_spec.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-
-describe "Net::HTTPHeader#[] when passed key" do
- before :each do
- @headers = NetHTTPHeaderSpecs::Example.new
- end
-
- it "returns the value of the header entry with the passed key" do
- @headers["My-Header"] = "test"
- @headers["My-Header"].should == "test"
- @headers["My-Other-Header"] = "another test"
- @headers["My-Other-Header"].should == "another test"
- end
-
- it "is case-insensitive" do
- @headers["My-Header"] = "test"
-
- @headers['My-Header'].should == "test"
- @headers['my-Header'].should == "test"
- @headers['My-header'].should == "test"
- @headers['my-header'].should == "test"
- @headers['MY-HEADER'].should == "test"
- end
-
- it "returns multi-element values joined together" do
- @headers["My-Header"] = "test"
- @headers.add_field("My-Header", "another test")
- @headers.add_field("My-Header", "and one more")
-
- @headers["My-Header"].should == "test, another test, and one more"
- end
-
- it "returns nil for non-existing entries" do
- @headers["My-Header"].should be_nil
- @headers["My-Other-Header"].should be_nil
- end
-end
diff --git a/spec/ruby/library/net/http/httpheader/element_set_spec.rb b/spec/ruby/library/net/http/httpheader/element_set_spec.rb
deleted file mode 100644
index e9ad64fafc..0000000000
--- a/spec/ruby/library/net/http/httpheader/element_set_spec.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-
-describe "Net::HTTPHeader#[]= when passed key, value" do
- before :each do
- @headers = NetHTTPHeaderSpecs::Example.new
- end
-
- it "sets the header entry with the passed key to the passed value" do
- @headers["My-Header"] = "test"
- @headers["My-Header"].should == "test"
-
- @headers["My-Header"] = "overwritten"
- @headers["My-Header"].should == "overwritten"
-
- @headers["My-Other-Header"] = "another test"
- @headers["My-Other-Header"].should == "another test"
- end
-
- it "is case-insensitive" do
- @headers['My-Header'] = "test"
- @headers['my-Header'] = "another test"
- @headers['My-header'] = "and one more test"
- @headers['my-header'] = "and another one"
- @headers['MY-HEADER'] = "last one"
-
- @headers["My-Header"].should == "last one"
- @headers.size.should eql(1)
- end
-
- it "removes the header entry with the passed key when the value is false or nil" do
- @headers['My-Header'] = "test"
- @headers['My-Header'] = nil
- @headers['My-Header'].should be_nil
-
- @headers['My-Header'] = "test"
- @headers['My-Header'] = false
- @headers['My-Header'].should be_nil
- end
-end
diff --git a/spec/ruby/library/net/http/httpheader/fetch_spec.rb b/spec/ruby/library/net/http/httpheader/fetch_spec.rb
deleted file mode 100644
index ea15679acb..0000000000
--- a/spec/ruby/library/net/http/httpheader/fetch_spec.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-
-describe "Net::HTTPHeader#fetch" do
- before :each do
- @headers = NetHTTPHeaderSpecs::Example.new
- end
-
- describe "when passed key" do
- it "returns the header entry for the passed key" do
- @headers["My-Header"] = "test"
- @headers.fetch("My-Header").should == "test"
-
- @headers.add_field("My-Other-Header", "a")
- @headers.add_field("My-Other-Header", "b")
- @headers.add_field("My-Other-Header", "c")
- @headers.fetch("My-Other-Header").should == "a, b, c"
- end
-
- it "is case-insensitive" do
- @headers["My-Header"] = "test"
- @headers.fetch("my-header").should == "test"
- @headers.fetch("MY-HEADER").should == "test"
- end
-
- it "returns nil when there is no entry for the passed key" do
- -> { @headers.fetch("my-header") }.should raise_error(IndexError)
- end
- end
-
- describe "when passed key, default" do
- it "returns the header entry for the passed key" do
- @headers["My-Header"] = "test"
- @headers.fetch("My-Header", "bla").should == "test"
-
- @headers.add_field("My-Other-Header", "a")
- @headers.add_field("My-Other-Header", "b")
- @headers.add_field("My-Other-Header", "c")
- @headers.fetch("My-Other-Header", "bla").should == "a, b, c"
- end
-
- # TODO: This raises a NoMethodError: undefined method `join' for "bla":String
- it "returns the default value when there is no entry for the passed key" do
- @headers.fetch("My-Header", "bla").should == "bla"
- end
- end
-
- describe "when passed key and block" do
- it "returns the header entry for the passed key" do
- @headers["My-Header"] = "test"
- @headers.fetch("My-Header") {}.should == "test"
-
- @headers.add_field("My-Other-Header", "a")
- @headers.add_field("My-Other-Header", "b")
- @headers.add_field("My-Other-Header", "c")
- -> {
- @result = @headers.fetch("My-Other-Header", "bla") {}
- }.should complain(/block supersedes default value argument/)
- @result.should == "a, b, c"
- end
-
- # TODO: This raises a NoMethodError: undefined method `join' for "redaeh-ym":String
- it "yieldsand returns the block's return value when there is no entry for the passed key" do
- @headers.fetch("My-Header") { |key| key.reverse }.should == "redaeh-ym"
- end
- end
-end
diff --git a/spec/ruby/library/net/http/httpheader/form_data_spec.rb b/spec/ruby/library/net/http/httpheader/form_data_spec.rb
deleted file mode 100644
index 9b29a03159..0000000000
--- a/spec/ruby/library/net/http/httpheader/form_data_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-require_relative 'shared/set_form_data'
-
-describe "Net::HTTPHeader#form_data=" do
- it_behaves_like :net_httpheader_set_form_data, :form_data=
-end
diff --git a/spec/ruby/library/net/http/httpheader/get_fields_spec.rb b/spec/ruby/library/net/http/httpheader/get_fields_spec.rb
deleted file mode 100644
index 1b623bf2a3..0000000000
--- a/spec/ruby/library/net/http/httpheader/get_fields_spec.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-
-describe "Net::HTTPHeader#get_fields when passed key" do
- before :each do
- @headers = NetHTTPHeaderSpecs::Example.new
- end
-
- it "returns an Array containing the values of the header entry with the passed key" do
- @headers["My-Header"] = "a"
- @headers.get_fields("My-Header").should == ["a"]
-
- @headers.add_field("My-Header", "b")
- @headers.get_fields("My-Header").should == ["a", "b"]
- end
-
- it "returns a copy of the header entry values" do
- @headers["My-Header"] = "a"
-
- @headers.get_fields("My-Header").clear
- @headers.get_fields("My-Header").should == ["a"]
-
- @headers.get_fields("My-Header") << "b"
- @headers.get_fields("My-Header").should == ["a"]
- end
-
- it "returns nil for non-existing header entries" do
- @headers.get_fields("My-Header").should be_nil
- @headers.get_fields("My-Other-header").should be_nil
- end
-
- it "is case-insensitive" do
- @headers["My-Header"] = "test"
- @headers.get_fields("My-Header").should == ["test"]
- @headers.get_fields("my-header").should == ["test"]
- @headers.get_fields("MY-HEADER").should == ["test"]
- end
-end
diff --git a/spec/ruby/library/net/http/httpheader/initialize_http_header_spec.rb b/spec/ruby/library/net/http/httpheader/initialize_http_header_spec.rb
deleted file mode 100644
index efc5e7d0b2..0000000000
--- a/spec/ruby/library/net/http/httpheader/initialize_http_header_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-
-describe "Net::HTTPHeader#initialize_http_header when passed Hash" do
- before :each do
- @headers = NetHTTPHeaderSpecs::Example.allocate
- end
-
- it "initializes the HTTP Header using the passed Hash" do
- @headers.initialize_http_header("My-Header" => "test", "My-Other-Header" => "another test")
- @headers["My-Header"].should == "test"
- @headers["My-Other-Header"].should == "another test"
- end
-
- it "complains about duplicate keys when in verbose mode" do
- -> do
- @headers.initialize_http_header("My-Header" => "test", "my-header" => "another test")
- end.should complain(/duplicated HTTP header/, verbose: true)
- end
-end
diff --git a/spec/ruby/library/net/http/httpheader/key_spec.rb b/spec/ruby/library/net/http/httpheader/key_spec.rb
deleted file mode 100644
index 9099024229..0000000000
--- a/spec/ruby/library/net/http/httpheader/key_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-
-describe "Net::HTTPHeader#key? when passed key" do
- before :each do
- @headers = NetHTTPHeaderSpecs::Example.new
- end
-
- it "returns true if the header entry with the passed key exists" do
- @headers.key?("My-Header").should be_false
- @headers["My-Header"] = "test"
- @headers.key?("My-Header").should be_true
- end
-
- it "is case-insensitive" do
- @headers["My-Header"] = "test"
- @headers.key?("my-header").should be_true
- @headers.key?("MY-HEADER").should be_true
- end
-end
diff --git a/spec/ruby/library/net/http/httpheader/length_spec.rb b/spec/ruby/library/net/http/httpheader/length_spec.rb
deleted file mode 100644
index 0d1da149f4..0000000000
--- a/spec/ruby/library/net/http/httpheader/length_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-require_relative 'shared/size'
-
-describe "Net::HTTPHeader#length" do
- it_behaves_like :net_httpheader_size, :length
-end
diff --git a/spec/ruby/library/net/http/httpheader/main_type_spec.rb b/spec/ruby/library/net/http/httpheader/main_type_spec.rb
deleted file mode 100644
index 3e18de6b5b..0000000000
--- a/spec/ruby/library/net/http/httpheader/main_type_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-
-describe "Net::HTTPHeader#main_type" do
- before :each do
- @headers = NetHTTPHeaderSpecs::Example.new
- end
-
- it "returns the 'main-content-type', as per 'Content-Type' header entry" do
- @headers["Content-Type"] = "text/html"
- @headers.main_type.should == "text"
-
- @headers["Content-Type"] = "application/pdf"
- @headers.main_type.should == "application"
-
- @headers["Content-Type"] = "text/html;charset=utf-8"
- @headers.main_type.should == "text"
- end
-
- it "returns nil if the 'Content-Type' header entry does not exist" do
- @headers.main_type.should be_nil
- end
-end
diff --git a/spec/ruby/library/net/http/httpheader/proxy_basic_auth_spec.rb b/spec/ruby/library/net/http/httpheader/proxy_basic_auth_spec.rb
deleted file mode 100644
index 8b576ee164..0000000000
--- a/spec/ruby/library/net/http/httpheader/proxy_basic_auth_spec.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-
-describe "Net::HTTPHeader#proxy_basic_auth when passed account, password" do
- before :each do
- @headers = NetHTTPHeaderSpecs::Example.new
- end
-
- it "sets the 'Proxy-Authorization' Header entry for basic authorization" do
- @headers.proxy_basic_auth("rubyspec", "rocks")
- @headers["Proxy-Authorization"].should == "Basic cnVieXNwZWM6cm9ja3M="
- end
-end
diff --git a/spec/ruby/library/net/http/httpheader/range_length_spec.rb b/spec/ruby/library/net/http/httpheader/range_length_spec.rb
deleted file mode 100644
index b8fce4f690..0000000000
--- a/spec/ruby/library/net/http/httpheader/range_length_spec.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-
-describe "Net::HTTPHeader#range_length" do
- before :each do
- @headers = NetHTTPHeaderSpecs::Example.new
- end
-
- it "returns the length of the Range represented by the 'Content-Range' header entry" do
- @headers["Content-Range"] = "bytes 0-499/1234"
- @headers.range_length.should eql(500)
-
- @headers["Content-Range"] = "bytes 500-1233/1234"
- @headers.range_length.should eql(734)
- end
-
- it "returns nil when there is no 'Content-Range' header entry" do
- @headers.range_length.should be_nil
- end
-
- it "raises a Net::HTTPHeaderSyntaxError when the 'Content-Range' has an invalid format" do
- @headers["Content-Range"] = "invalid"
- -> { @headers.range_length }.should raise_error(Net::HTTPHeaderSyntaxError)
-
- @headers["Content-Range"] = "bytes 123-abc"
- -> { @headers.range_length }.should raise_error(Net::HTTPHeaderSyntaxError)
-
- @headers["Content-Range"] = "bytes abc-123"
- -> { @headers.range_length }.should raise_error(Net::HTTPHeaderSyntaxError)
- end
-end
diff --git a/spec/ruby/library/net/http/httpheader/range_spec.rb b/spec/ruby/library/net/http/httpheader/range_spec.rb
deleted file mode 100644
index 005177f6be..0000000000
--- a/spec/ruby/library/net/http/httpheader/range_spec.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-require_relative 'shared/set_range'
-
-describe "Net::HTTPHeader#range" do
- before :each do
- @headers = NetHTTPHeaderSpecs::Example.new
- end
-
- it "returns a Range object that represents the 'Range' header entry" do
- @headers["Range"] = "bytes=0-499"
- @headers.range.should == [0..499]
-
- @headers["Range"] = "bytes=500-1233"
- @headers.range.should == [500..1233]
-
- @headers["Range"] = "bytes=10-"
- @headers.range.should == [10..-1]
-
- @headers["Range"] = "bytes=-10"
- @headers.range.should == [-10..-1]
- end
-
- it "returns nil when there is no 'Range' header entry" do
- @headers.range.should be_nil
- end
-
- it "raises a Net::HTTPHeaderSyntaxError when the 'Range' has an invalid format" do
- @headers["Range"] = "invalid"
- -> { @headers.range }.should raise_error(Net::HTTPHeaderSyntaxError)
-
- @headers["Range"] = "bytes 123-abc"
- -> { @headers.range }.should raise_error(Net::HTTPHeaderSyntaxError)
-
- @headers["Range"] = "bytes abc-123"
- -> { @headers.range }.should raise_error(Net::HTTPHeaderSyntaxError)
- end
-
- it "raises a Net::HTTPHeaderSyntaxError when the 'Range' was not specified" do
- @headers["Range"] = "bytes=-"
- -> { @headers.range }.should raise_error(Net::HTTPHeaderSyntaxError)
- end
-end
-
-describe "Net::HTTPHeader#range=" do
- it_behaves_like :net_httpheader_set_range, :range=
-end
diff --git a/spec/ruby/library/net/http/httpheader/set_content_type_spec.rb b/spec/ruby/library/net/http/httpheader/set_content_type_spec.rb
deleted file mode 100644
index 125f7a7e0d..0000000000
--- a/spec/ruby/library/net/http/httpheader/set_content_type_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-require_relative 'shared/set_content_type'
-
-describe "Net::HTTPHeader#set_content_type" do
- it_behaves_like :net_httpheader_set_content_type, :set_content_type
-end
diff --git a/spec/ruby/library/net/http/httpheader/set_form_data_spec.rb b/spec/ruby/library/net/http/httpheader/set_form_data_spec.rb
deleted file mode 100644
index 14c66ae01c..0000000000
--- a/spec/ruby/library/net/http/httpheader/set_form_data_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-require_relative 'shared/set_form_data'
-
-describe "Net::HTTPHeader#set_form_data" do
- it_behaves_like :net_httpheader_set_form_data, :set_form_data
-end
diff --git a/spec/ruby/library/net/http/httpheader/set_range_spec.rb b/spec/ruby/library/net/http/httpheader/set_range_spec.rb
deleted file mode 100644
index 85b9c50422..0000000000
--- a/spec/ruby/library/net/http/httpheader/set_range_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-require_relative 'shared/set_range'
-
-describe "Net::HTTPHeader#set_range" do
- it_behaves_like :net_httpheader_set_range, :set_range
-end
diff --git a/spec/ruby/library/net/http/httpheader/size_spec.rb b/spec/ruby/library/net/http/httpheader/size_spec.rb
deleted file mode 100644
index a7d78f96e0..0000000000
--- a/spec/ruby/library/net/http/httpheader/size_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-require_relative 'shared/size'
-
-describe "Net::HTTPHeader#size" do
- it_behaves_like :net_httpheader_size, :size
-end
diff --git a/spec/ruby/library/net/http/httpheader/sub_type_spec.rb b/spec/ruby/library/net/http/httpheader/sub_type_spec.rb
deleted file mode 100644
index 3c73ca0027..0000000000
--- a/spec/ruby/library/net/http/httpheader/sub_type_spec.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-
-describe "Net::HTTPHeader#sub_type" do
- before :each do
- @headers = NetHTTPHeaderSpecs::Example.new
- end
-
- it "returns the 'sub-content-type', as per 'Content-Type' header entry" do
- @headers["Content-Type"] = "text/html"
- @headers.sub_type.should == "html"
-
- @headers["Content-Type"] = "application/pdf"
- @headers.sub_type.should == "pdf"
-
- @headers["Content-Type"] = "text/html;charset=utf-8"
- @headers.sub_type.should == "html"
- end
-
- it "returns nil if no 'sub-content-type' is set" do
- @headers["Content-Type"] = "text"
- @headers.sub_type.should be_nil
-
- @headers["Content-Type"] = "text;charset=utf-8"
- @headers.sub_type.should be_nil
- end
-
- it "returns nil if the 'Content-Type' header entry does not exist" do
- @headers.sub_type.should be_nil
- end
-end
diff --git a/spec/ruby/library/net/http/httpheader/to_hash_spec.rb b/spec/ruby/library/net/http/httpheader/to_hash_spec.rb
deleted file mode 100644
index 8c1d36c30a..0000000000
--- a/spec/ruby/library/net/http/httpheader/to_hash_spec.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-
-describe "Net::HTTPHeader#to_hash" do
- before :each do
- @headers = NetHTTPHeaderSpecs::Example.new
- end
-
- it "returns a Hash representing all Header entries (keys in lower case, values as arrays)" do
- @headers.to_hash.should == {}
-
- @headers["My-Header"] = "test"
- @headers.to_hash.should == { "my-header" => ["test"] }
-
- @headers.add_field("My-Header", "another test")
- @headers.to_hash.should == { "my-header" => ["test", "another test"] }
- end
-
- it "does not allow modifying the headers from the returned hash" do
- @headers.to_hash["my-header"] = ["test"]
- @headers.to_hash.should == {}
- @headers.key?("my-header").should be_false
- end
-end
diff --git a/spec/ruby/library/net/http/httpheader/type_params_spec.rb b/spec/ruby/library/net/http/httpheader/type_params_spec.rb
deleted file mode 100644
index e77be7ea85..0000000000
--- a/spec/ruby/library/net/http/httpheader/type_params_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'fixtures/classes'
-
-describe "Net::HTTPHeader#type_params" do
- before :each do
- @headers = NetHTTPHeaderSpecs::Example.new
- end
-
- it "returns additional 'Content-Type' information as a Hash" do
- @headers["Content-Type"] = "text/html;charset=utf-8"
- @headers.type_params.should == {"charset" => "utf-8"}
-
- @headers["Content-Type"] = "text/html; charset=utf-8; rubyspec=rocks"
- @headers.type_params.should == {"charset" => "utf-8", "rubyspec" => "rocks"}
- end
-
- it "returns an empty Hash when no additional 'Content-Type' information is set" do
- @headers.type_params.should == {}
-
- @headers["Content-Type"] = "text/html"
- @headers.type_params.should == {}
- end
-end
diff --git a/spec/ruby/library/net/http/httprequest/initialize_spec.rb b/spec/ruby/library/net/http/httprequest/initialize_spec.rb
deleted file mode 100644
index 88e9fb1c77..0000000000
--- a/spec/ruby/library/net/http/httprequest/initialize_spec.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-module NetHTTPRequestSpecs
- class TestRequest < Net::HTTPRequest
- METHOD = "TEST"
- REQUEST_HAS_BODY = false
- RESPONSE_HAS_BODY = true
- end
-end
-
-describe "Net::HTTPRequest#initialize" do
- before :each do
- @req = NetHTTPRequestSpecs::TestRequest.allocate
- end
-
- it "uses the METHOD constants to set the request method" do
- @req.send(:initialize, "/some/path")
- @req.method.should == "TEST"
- end
-
- it "uses the REQUEST_HAS_BODY to set whether the Request has a body or not" do
- @req.send(:initialize, "/some/path")
- @req.request_body_permitted?.should be_false
- end
-
- it "uses the RESPONSE_HAS_BODY to set whether the Response can have a body or not" do
- @req.send(:initialize, "/some/path")
- @req.response_body_permitted?.should be_true
- end
-
- describe "when passed path" do
- it "sets self's path to the passed path" do
- @req.send(:initialize, "/some/path")
- @req.path.should == "/some/path"
- end
- end
-
- describe "when passed path, headers" do
- it "uses the passed headers Hash to initialize self's header entries" do
- @req.send(:initialize, "/some/path", "Content-Type" => "text/html")
- @req["Content-Type"].should == "text/html"
- end
- end
-end
diff --git a/spec/ruby/library/net/http/httpresponse/body_permitted_spec.rb b/spec/ruby/library/net/http/httpresponse/body_permitted_spec.rb
deleted file mode 100644
index 8ade46689f..0000000000
--- a/spec/ruby/library/net/http/httpresponse/body_permitted_spec.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPResponse.body_permitted?" do
- it "returns true if this response type can have a response body" do
- Net::HTTPUnknownResponse.should.body_permitted?
- Net::HTTPInformation.should_not.body_permitted?
- Net::HTTPSuccess.should.body_permitted?
- Net::HTTPRedirection.should.body_permitted?
- Net::HTTPClientError.should.body_permitted?
- Net::HTTPServerError.should.body_permitted?
- end
-end
diff --git a/spec/ruby/library/net/http/httpresponse/body_spec.rb b/spec/ruby/library/net/http/httpresponse/body_spec.rb
deleted file mode 100644
index 79569441f1..0000000000
--- a/spec/ruby/library/net/http/httpresponse/body_spec.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'shared/body'
-
-describe "Net::HTTPResponse#body" do
- it_behaves_like :net_httpresponse_body, :body
-end
diff --git a/spec/ruby/library/net/http/httpresponse/code_spec.rb b/spec/ruby/library/net/http/httpresponse/code_spec.rb
deleted file mode 100644
index 114277cb43..0000000000
--- a/spec/ruby/library/net/http/httpresponse/code_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPResponse#code" do
- it "returns the result code string" do
- res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
- res.code.should == "???"
-
- res = Net::HTTPInformation.new("1.0", "1xx", "test response")
- res.code.should == "1xx"
-
- res = Net::HTTPSuccess.new("1.0", "2xx", "test response")
- res.code.should == "2xx"
-
- res = Net::HTTPRedirection.new("1.0", "3xx", "test response")
- res.code.should == "3xx"
-
- res = Net::HTTPClientError.new("1.0", "4xx", "test response")
- res.code.should == "4xx"
-
- res = Net::HTTPServerError.new("1.0", "5xx", "test response")
- res.code.should == "5xx"
- end
-end
diff --git a/spec/ruby/library/net/http/httpresponse/code_type_spec.rb b/spec/ruby/library/net/http/httpresponse/code_type_spec.rb
deleted file mode 100644
index fa2d572e9a..0000000000
--- a/spec/ruby/library/net/http/httpresponse/code_type_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPResponse#code_type" do
- it "returns self's class" do
- res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
- res.code_type.should == Net::HTTPUnknownResponse
-
- res = Net::HTTPInformation.new("1.0", "1xx", "test response")
- res.code_type.should == Net::HTTPInformation
-
- res = Net::HTTPSuccess.new("1.0", "2xx", "test response")
- res.code_type.should == Net::HTTPSuccess
-
- res = Net::HTTPRedirection.new("1.0", "3xx", "test response")
- res.code_type.should == Net::HTTPRedirection
-
- res = Net::HTTPClientError.new("1.0", "4xx", "test response")
- res.code_type.should == Net::HTTPClientError
-
- res = Net::HTTPServerError.new("1.0", "5xx", "test response")
- res.code_type.should == Net::HTTPServerError
- end
-end
diff --git a/spec/ruby/library/net/http/httpresponse/entity_spec.rb b/spec/ruby/library/net/http/httpresponse/entity_spec.rb
deleted file mode 100644
index f1639042c1..0000000000
--- a/spec/ruby/library/net/http/httpresponse/entity_spec.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require_relative 'shared/body'
-
-describe "Net::HTTPResponse#entity" do
- it_behaves_like :net_httpresponse_body, :entity
-end
diff --git a/spec/ruby/library/net/http/httpresponse/error_spec.rb b/spec/ruby/library/net/http/httpresponse/error_spec.rb
deleted file mode 100644
index 89f4a47f60..0000000000
--- a/spec/ruby/library/net/http/httpresponse/error_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPResponse#error!" do
- it "raises self's class 'EXCEPTION_TYPE' Exception" do
- res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
- -> { res.error! }.should raise_error(Net::HTTPError)
-
- res = Net::HTTPInformation.new("1.0", "1xx", "test response")
- -> { res.error! }.should raise_error(Net::HTTPError)
-
- res = Net::HTTPSuccess.new("1.0", "2xx", "test response")
- -> { res.error! }.should raise_error(Net::HTTPError)
-
- res = Net::HTTPRedirection.new("1.0", "3xx", "test response")
- -> { res.error! }.should raise_error(Net::HTTPRetriableError)
-
- res = Net::HTTPClientError.new("1.0", "4xx", "test response")
- -> { res.error! }.should raise_error(Net::HTTPClientException)
-
- res = Net::HTTPServerError.new("1.0", "5xx", "test response")
- -> { res.error! }.should raise_error(Net::HTTPFatalError)
- end
-end
diff --git a/spec/ruby/library/net/http/httpresponse/error_type_spec.rb b/spec/ruby/library/net/http/httpresponse/error_type_spec.rb
deleted file mode 100644
index 8885b7706b..0000000000
--- a/spec/ruby/library/net/http/httpresponse/error_type_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPResponse#error_type" do
- it "returns self's class 'EXCEPTION_TYPE' constant" do
- res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
- res.error_type.should == Net::HTTPError
-
- res = Net::HTTPInformation.new("1.0", "1xx", "test response")
- res.error_type.should == Net::HTTPError
-
- res = Net::HTTPSuccess.new("1.0", "2xx", "test response")
- res.error_type.should == Net::HTTPError
-
- res = Net::HTTPRedirection.new("1.0", "3xx", "test response")
- res.error_type.should == Net::HTTPRetriableError
-
- res = Net::HTTPClientError.new("1.0", "4xx", "test response")
- res.error_type.should == Net::HTTPClientException
-
- res = Net::HTTPServerError.new("1.0", "5xx", "test response")
- res.error_type.should == Net::HTTPFatalError
- end
-end
diff --git a/spec/ruby/library/net/http/httpresponse/exception_type_spec.rb b/spec/ruby/library/net/http/httpresponse/exception_type_spec.rb
deleted file mode 100644
index 0c9c11291f..0000000000
--- a/spec/ruby/library/net/http/httpresponse/exception_type_spec.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPResponse.exception_type" do
- it "returns self's 'EXCEPTION_TYPE' constant" do
- Net::HTTPUnknownResponse.exception_type.should == Net::HTTPError
- Net::HTTPInformation.exception_type.should == Net::HTTPError
- Net::HTTPSuccess.exception_type.should == Net::HTTPError
- Net::HTTPRedirection.exception_type.should == Net::HTTPRetriableError
- Net::HTTPClientError.exception_type.should == Net::HTTPClientException
- Net::HTTPServerError.exception_type.should == Net::HTTPFatalError
- end
-end
diff --git a/spec/ruby/library/net/http/httpresponse/header_spec.rb b/spec/ruby/library/net/http/httpresponse/header_spec.rb
deleted file mode 100644
index a9615feda8..0000000000
--- a/spec/ruby/library/net/http/httpresponse/header_spec.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPResponse#header" do
- it "returns self" do
- res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
- res.response.should equal(res)
- end
-end
diff --git a/spec/ruby/library/net/http/httpresponse/http_version_spec.rb b/spec/ruby/library/net/http/httpresponse/http_version_spec.rb
deleted file mode 100644
index db85696d77..0000000000
--- a/spec/ruby/library/net/http/httpresponse/http_version_spec.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPResponse#http_version" do
- it "returns self's http version" do
- res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
- res.http_version.should == "1.0"
-
- res = Net::HTTPUnknownResponse.new("1.1", "???", "test response")
- res.http_version.should == "1.1"
- end
-end
diff --git a/spec/ruby/library/net/http/httpresponse/initialize_spec.rb b/spec/ruby/library/net/http/httpresponse/initialize_spec.rb
deleted file mode 100644
index eb77e2e277..0000000000
--- a/spec/ruby/library/net/http/httpresponse/initialize_spec.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPResponse#initialize when passed http_version, response_code, response_message" do
- it "sets self http_version, response_code and response_message to the passed values" do
- res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
- res.http_version.should == "1.0"
- res.code.should == "???"
- res.message.should == "test response"
- end
-end
diff --git a/spec/ruby/library/net/http/httpresponse/inspect_spec.rb b/spec/ruby/library/net/http/httpresponse/inspect_spec.rb
deleted file mode 100644
index 1e1a2c7cc7..0000000000
--- a/spec/ruby/library/net/http/httpresponse/inspect_spec.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require "stringio"
-
-describe "Net::HTTPResponse#inspect" do
- it "returns a String representation of self" do
- res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
- res.inspect.should == "#<Net::HTTPUnknownResponse ??? test response readbody=false>"
-
- res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
- socket = Net::BufferedIO.new(StringIO.new("test body"))
- res.reading_body(socket, true) {}
- res.inspect.should == "#<Net::HTTPUnknownResponse ??? test response readbody=true>"
- end
-end
diff --git a/spec/ruby/library/net/http/httpresponse/message_spec.rb b/spec/ruby/library/net/http/httpresponse/message_spec.rb
deleted file mode 100644
index ae8e3678a1..0000000000
--- a/spec/ruby/library/net/http/httpresponse/message_spec.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPResponse#message" do
- it "returns self's response message" do
- res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
- res.message.should == "test response"
- end
-end
diff --git a/spec/ruby/library/net/http/httpresponse/msg_spec.rb b/spec/ruby/library/net/http/httpresponse/msg_spec.rb
deleted file mode 100644
index 0e5e7eb4aa..0000000000
--- a/spec/ruby/library/net/http/httpresponse/msg_spec.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPResponse#msg" do
- it "returns self's response message" do
- res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
- res.message.should == "test response"
- end
-end
diff --git a/spec/ruby/library/net/http/httpresponse/read_body_spec.rb b/spec/ruby/library/net/http/httpresponse/read_body_spec.rb
deleted file mode 100644
index ec9b42f919..0000000000
--- a/spec/ruby/library/net/http/httpresponse/read_body_spec.rb
+++ /dev/null
@@ -1,86 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require 'stringio'
-
-describe "Net::HTTPResponse#read_body" do
- before :each do
- @res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
- @socket = Net::BufferedIO.new(StringIO.new("test body"))
- end
-
- describe "when passed no arguments" do
- it "returns the read body" do
- @res.reading_body(@socket, true) do
- @res.read_body.should == "test body"
- end
- end
-
- it "returns the previously read body if called a second time" do
- @res.reading_body(@socket, true) do
- @res.read_body.should equal(@res.read_body)
- end
- end
- end
-
- describe "when passed a buffer" do
- it "reads the body to the passed buffer" do
- @res.reading_body(@socket, true) do
- buffer = ""
- @res.read_body(buffer)
- buffer.should == "test body"
- end
- end
-
- it "returns the passed buffer" do
- @res.reading_body(@socket, true) do
- buffer = ""
- @res.read_body(buffer).should equal(buffer)
- end
- end
-
- it "raises an IOError if called a second time" do
- @res.reading_body(@socket, true) do
- @res.read_body("")
- -> { @res.read_body("") }.should raise_error(IOError)
- end
- end
- end
-
- describe "when passed a block" do
- it "reads the body and yields it to the passed block (in chunks)" do
- @res.reading_body(@socket, true) do
- yielded = false
-
- buffer = ""
- @res.read_body do |body|
- yielded = true
- buffer << body
- end
-
- yielded.should be_true
- buffer.should == "test body"
- end
- end
-
- it "returns the ReadAdapter" do
- @res.reading_body(@socket, true) do
- @res.read_body { nil }.should be_kind_of(Net::ReadAdapter)
- end
- end
-
- it "raises an IOError if called a second time" do
- @res.reading_body(@socket, true) do
- @res.read_body {}
- -> { @res.read_body {} }.should raise_error(IOError)
- end
- end
- end
-
- describe "when passed buffer and block" do
- it "raises an ArgumentError" do
- @res.reading_body(@socket, true) do
- -> { @res.read_body("") {} }.should raise_error(ArgumentError)
- end
- end
- end
-end
diff --git a/spec/ruby/library/net/http/httpresponse/read_header_spec.rb b/spec/ruby/library/net/http/httpresponse/read_header_spec.rb
deleted file mode 100644
index 6af8c6bd6a..0000000000
--- a/spec/ruby/library/net/http/httpresponse/read_header_spec.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPResponse#read_header" do
- it "returns self" do
- res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
- res.response.should equal(res)
- end
-end
diff --git a/spec/ruby/library/net/http/httpresponse/read_new_spec.rb b/spec/ruby/library/net/http/httpresponse/read_new_spec.rb
deleted file mode 100644
index dc2cdc9621..0000000000
--- a/spec/ruby/library/net/http/httpresponse/read_new_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require 'stringio'
-
-describe "Net::HTTPResponse.read_new" do
- it "creates a HTTPResponse object based on the response read from the passed socket" do
- socket = Net::BufferedIO.new(StringIO.new(<<EOS))
-HTTP/1.1 200 OK
-Content-Type: text/html; charset=utf-8
-
-test-body
-EOS
- response = Net::HTTPResponse.read_new(socket)
-
- response.should be_kind_of(Net::HTTPOK)
- response.code.should == "200"
- response["Content-Type"].should == "text/html; charset=utf-8"
-
- response.reading_body(socket, true) do
- response.body.should == "test-body\n"
- end
- end
-end
diff --git a/spec/ruby/library/net/http/httpresponse/reading_body_spec.rb b/spec/ruby/library/net/http/httpresponse/reading_body_spec.rb
deleted file mode 100644
index ebdab891e1..0000000000
--- a/spec/ruby/library/net/http/httpresponse/reading_body_spec.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-require "stringio"
-
-describe "Net::HTTPResponse#reading_body" do
- before :each do
- @res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
- @socket = Net::BufferedIO.new(StringIO.new("test body"))
- end
-
- describe "when body_allowed is true" do
- it "reads and returns the response body for self from the passed socket" do
- @res.reading_body(@socket, true) {}.should == "test body"
- @res.body.should == "test body"
- end
-
- it "yields the passed block before reading the body" do
- yielded = false
-
- @res.reading_body(@socket, true) do
- @res.inspect.should == "#<Net::HTTPUnknownResponse ??? test response readbody=false>"
- yielded = true
- end
-
- yielded.should be_true
- end
-
- describe "but the response type is not allowed to have a body" do
- before :each do
- @res = Net::HTTPInformation.new("1.0", "???", "test response")
- end
-
- it "returns nil" do
- @res.reading_body(@socket, false) {}.should be_nil
- @res.body.should be_nil
- end
-
- it "yields the passed block" do
- yielded = false
- @res.reading_body(@socket, true) { yielded = true }
- yielded.should be_true
- end
- end
- end
-
- describe "when body_allowed is false" do
- it "returns nil" do
- @res.reading_body(@socket, false) {}.should be_nil
- @res.body.should be_nil
- end
-
- it "yields the passed block" do
- yielded = false
- @res.reading_body(@socket, true) { yielded = true }
- yielded.should be_true
- end
- end
-end
diff --git a/spec/ruby/library/net/http/httpresponse/response_spec.rb b/spec/ruby/library/net/http/httpresponse/response_spec.rb
deleted file mode 100644
index f6035f3695..0000000000
--- a/spec/ruby/library/net/http/httpresponse/response_spec.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPResponse#response" do
- it "returns self" do
- res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
- res.response.should equal(res)
- end
-end
diff --git a/spec/ruby/library/net/http/httpresponse/value_spec.rb b/spec/ruby/library/net/http/httpresponse/value_spec.rb
deleted file mode 100644
index 5cd58316ef..0000000000
--- a/spec/ruby/library/net/http/httpresponse/value_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'net/http'
-
-describe "Net::HTTPResponse#value" do
- it "raises an HTTP error for non 2xx HTTP Responses" do
- res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
- -> { res.value }.should raise_error(Net::HTTPError)
-
- res = Net::HTTPInformation.new("1.0", "1xx", "test response")
- -> { res.value }.should raise_error(Net::HTTPError)
-
- res = Net::HTTPSuccess.new("1.0", "2xx", "test response")
- -> { res.value }.should_not raise_error(Net::HTTPError)
-
- res = Net::HTTPRedirection.new("1.0", "3xx", "test response")
- -> { res.value }.should raise_error(Net::HTTPRetriableError)
-
- res = Net::HTTPClientError.new("1.0", "4xx", "test response")
- -> { res.value }.should raise_error(Net::HTTPClientException)
-
- res = Net::HTTPServerError.new("1.0", "5xx", "test response")
- -> { res.value }.should raise_error(Net::HTTPFatalError)
- end
-end
diff --git a/spec/ruby/library/objectspace/dump_all_spec.rb b/spec/ruby/library/objectspace/dump_all_spec.rb
index dbf5bf1f76..e9b449a905 100644
--- a/spec/ruby/library/objectspace/dump_all_spec.rb
+++ b/spec/ruby/library/objectspace/dump_all_spec.rb
@@ -55,27 +55,25 @@ describe "ObjectSpace.dump_all" do
stdout.should == "File\ntrue\n"
end
- ruby_version_is "3.0" do
- it "dumps Ruby heap to a temporary file when passed output: :nil" do
- stdout = ruby_exe(<<~RUBY, options: "-robjspace")
- string = "abc"
- file = ObjectSpace.dump_all(output: nil)
-
- begin
- file.flush
- file.rewind
- content = file.read
-
- puts file.class
- puts content.include?('"value":"abc"')
- ensure
- file.close
- File.unlink file.path
- end
- RUBY
-
- stdout.should == "File\ntrue\n"
+ it "dumps Ruby heap to a temporary file when passed output: :nil" do
+ stdout = ruby_exe(<<~RUBY, options: "-robjspace")
+ string = "abc"
+ file = ObjectSpace.dump_all(output: nil)
+
+ begin
+ file.flush
+ file.rewind
+ content = file.read
+
+ puts file.class
+ puts content.include?('"value":"abc"')
+ ensure
+ file.close
+ File.unlink file.path
end
+ RUBY
+
+ stdout.should == "File\ntrue\n"
end
it "dumps Ruby heap to stdout when passed output: :stdout" do
diff --git a/spec/ruby/library/objectspace/dump_spec.rb b/spec/ruby/library/objectspace/dump_spec.rb
index eacce51ba5..e22ee3df1e 100644
--- a/spec/ruby/library/objectspace/dump_spec.rb
+++ b/spec/ruby/library/objectspace/dump_spec.rb
@@ -23,29 +23,27 @@ describe "ObjectSpace.dump" do
string.should include('"value":"abc"')
end
- ruby_version_is "3.0" do
- it "dumps to a temporary file when passed output: :file" do
- file = ObjectSpace.dump("abc", output: :file)
- file.should be_kind_of(File)
+ it "dumps to a temporary file when passed output: :file" do
+ file = ObjectSpace.dump("abc", output: :file)
+ file.should be_kind_of(File)
- file.rewind
- content = file.read
- content.should include('"value":"abc"')
- ensure
- file.close
- File.unlink file.path
- end
+ file.rewind
+ content = file.read
+ content.should include('"value":"abc"')
+ ensure
+ file.close
+ File.unlink file.path
+ end
- it "dumps to a temporary file when passed output: :nil" do
- file = ObjectSpace.dump("abc", output: nil)
- file.should be_kind_of(File)
+ it "dumps to a temporary file when passed output: :nil" do
+ file = ObjectSpace.dump("abc", output: nil)
+ file.should be_kind_of(File)
- file.rewind
- file.read.should include('"value":"abc"')
- ensure
- file.close
- File.unlink file.path
- end
+ file.rewind
+ file.read.should include('"value":"abc"')
+ ensure
+ file.close
+ File.unlink file.path
end
it "dumps to stdout when passed output: :stdout" do
@@ -53,19 +51,17 @@ describe "ObjectSpace.dump" do
stdout.should include('"value":"abc"')
end
- ruby_version_is "3.0" do
- it "dumps to provided IO when passed output: IO" do
- filename = tmp("io_read.txt")
- io = File.open(filename, "w+")
- result = ObjectSpace.dump("abc", output: io)
- result.should.equal? io
+ it "dumps to provided IO when passed output: IO" do
+ filename = tmp("io_read.txt")
+ io = File.open(filename, "w+")
+ result = ObjectSpace.dump("abc", output: io)
+ result.should.equal? io
- io.rewind
- io.read.should include('"value":"abc"')
- ensure
- io.close
- rm_r filename
- end
+ io.rewind
+ io.read.should include('"value":"abc"')
+ ensure
+ io.close
+ rm_r filename
end
it "raises ArgumentError when passed not supported :output value" do
diff --git a/spec/ruby/library/objectspace/fixtures/trace.rb b/spec/ruby/library/objectspace/fixtures/trace.rb
index fd4524b0ba..e53a7a0cac 100644
--- a/spec/ruby/library/objectspace/fixtures/trace.rb
+++ b/spec/ruby/library/objectspace/fixtures/trace.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require "objspace/trace"
a = "foo"
b = "b" + "a" + "r"
diff --git a/spec/ruby/library/objectspace/reachable_objects_from_spec.rb b/spec/ruby/library/objectspace/reachable_objects_from_spec.rb
index 7e70bc8569..dee5961663 100644
--- a/spec/ruby/library/objectspace/reachable_objects_from_spec.rb
+++ b/spec/ruby/library/objectspace/reachable_objects_from_spec.rb
@@ -38,7 +38,6 @@ describe "ObjectSpace.reachable_objects_from" do
end
it "finds an object stored in a Queue" do
- require 'thread'
o = Object.new
q = Queue.new
q << o
@@ -49,7 +48,6 @@ describe "ObjectSpace.reachable_objects_from" do
end
it "finds an object stored in a SizedQueue" do
- require 'thread'
o = Object.new
q = SizedQueue.new(3)
q << o
diff --git a/spec/ruby/library/objectspace/trace_spec.rb b/spec/ruby/library/objectspace/trace_spec.rb
index 59952a006c..532c282ce4 100644
--- a/spec/ruby/library/objectspace/trace_spec.rb
+++ b/spec/ruby/library/objectspace/trace_spec.rb
@@ -6,8 +6,8 @@ ruby_version_is "3.1" do
file = fixture(__FILE__ , "trace.rb")
ruby_exe(file, args: "2>&1").lines(chomp: true).should == [
"objspace/trace is enabled",
- "\"foo\" @ #{file}:2",
- "\"bar\" @ #{file}:3",
+ "\"foo\" @ #{file}:3",
+ "\"bar\" @ #{file}:4",
"42"
]
end
diff --git a/spec/ruby/library/openssl/config/freeze_spec.rb b/spec/ruby/library/openssl/config/freeze_spec.rb
deleted file mode 100644
index c814341b86..0000000000
--- a/spec/ruby/library/openssl/config/freeze_spec.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-require_relative '../../../spec_helper'
-require_relative '../shared/constants'
-
-require 'openssl'
-
-version_is(OpenSSL::VERSION, ""..."2.2") do
- describe "OpenSSL::Config#freeze" do
- it "needs to be reviewed for completeness"
-
- it "freezes" do
- c = OpenSSL::Config.new
- -> {
- c['foo'] = [ ['key', 'value'] ]
- }.should_not raise_error
- c.freeze
- c.frozen?.should be_true
- -> {
- c['foo'] = [ ['key', 'value'] ]
- }.should raise_error(TypeError)
- end
- end
-end
diff --git a/spec/ruby/library/openssl/digest/append_spec.rb b/spec/ruby/library/openssl/digest/append_spec.rb
new file mode 100644
index 0000000000..08802b7253
--- /dev/null
+++ b/spec/ruby/library/openssl/digest/append_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/update'
+
+describe "OpenSSL::Digest#<<" do
+ it_behaves_like :openssl_digest_update, :<<
+end
diff --git a/spec/ruby/library/openssl/digest/block_length_spec.rb b/spec/ruby/library/openssl/digest/block_length_spec.rb
new file mode 100644
index 0000000000..444ed9d20d
--- /dev/null
+++ b/spec/ruby/library/openssl/digest/block_length_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../../spec_helper'
+require_relative '../../../library/digest/sha1/shared/constants'
+require_relative '../../../library/digest/sha256/shared/constants'
+require_relative '../../../library/digest/sha384/shared/constants'
+require_relative '../../../library/digest/sha512/shared/constants'
+require 'openssl'
+
+describe "OpenSSL::Digest#block_length" do
+ context "when the digest object is created via a name argument" do
+ it "returns a SHA1 block length" do
+ OpenSSL::Digest.new('sha1').block_length.should == SHA1Constants::BlockLength
+ end
+
+ it "returns a SHA256 block length" do
+ OpenSSL::Digest.new('sha256').block_length.should == SHA256Constants::BlockLength
+ end
+
+ it "returns a SHA384 block length" do
+ OpenSSL::Digest.new('sha384').block_length.should == SHA384Constants::BlockLength
+ end
+
+ it "returns a SHA512 block length" do
+ OpenSSL::Digest.new('sha512').block_length.should == SHA512Constants::BlockLength
+ end
+ end
+
+ context "when the digest object is created via a subclass" do
+ it "returns a SHA1 block length" do
+ OpenSSL::Digest::SHA1.new.block_length.should == SHA1Constants::BlockLength
+ end
+
+ it "returns a SHA256 block length" do
+ OpenSSL::Digest::SHA256.new.block_length.should == SHA256Constants::BlockLength
+ end
+
+ it "returns a SHA384 block length" do
+ OpenSSL::Digest::SHA384.new.block_length.should == SHA384Constants::BlockLength
+ end
+
+ it "returns a SHA512 block length" do
+ OpenSSL::Digest::SHA512.new.block_length.should == SHA512Constants::BlockLength
+ end
+ end
+end
diff --git a/spec/ruby/library/openssl/digest/digest_length_spec.rb b/spec/ruby/library/openssl/digest/digest_length_spec.rb
new file mode 100644
index 0000000000..37d1cba9a7
--- /dev/null
+++ b/spec/ruby/library/openssl/digest/digest_length_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../../spec_helper'
+require_relative '../../../library/digest/sha1/shared/constants'
+require_relative '../../../library/digest/sha256/shared/constants'
+require_relative '../../../library/digest/sha384/shared/constants'
+require_relative '../../../library/digest/sha512/shared/constants'
+require 'openssl'
+
+describe "OpenSSL::Digest#digest_length" do
+ context "when the digest object is created via a name argument" do
+ it "returns a SHA1 digest length" do
+ OpenSSL::Digest.new('sha1').digest_length.should == SHA1Constants::DigestLength
+ end
+
+ it "returns a SHA256 digest length" do
+ OpenSSL::Digest.new('sha256').digest_length.should == SHA256Constants::DigestLength
+ end
+
+ it "returns a SHA384 digest length" do
+ OpenSSL::Digest.new('sha384').digest_length.should == SHA384Constants::DigestLength
+ end
+
+ it "returns a SHA512 digest length" do
+ OpenSSL::Digest.new('sha512').digest_length.should == SHA512Constants::DigestLength
+ end
+ end
+
+ context "when the digest object is created via a subclass" do
+ it "returns a SHA1 digest length" do
+ OpenSSL::Digest::SHA1.new.digest_length.should == SHA1Constants::DigestLength
+ end
+
+ it "returns a SHA256 digest length" do
+ OpenSSL::Digest::SHA256.new.digest_length.should == SHA256Constants::DigestLength
+ end
+
+ it "returns a SHA384 digest length" do
+ OpenSSL::Digest::SHA384.new.digest_length.should == SHA384Constants::DigestLength
+ end
+
+ it "returns a SHA512 digest length" do
+ OpenSSL::Digest::SHA512.new.digest_length.should == SHA512Constants::DigestLength
+ end
+ end
+end
diff --git a/spec/ruby/library/openssl/digest/digest_spec.rb b/spec/ruby/library/openssl/digest/digest_spec.rb
new file mode 100644
index 0000000000..cf27d01b6d
--- /dev/null
+++ b/spec/ruby/library/openssl/digest/digest_spec.rb
@@ -0,0 +1,62 @@
+require_relative '../../../spec_helper'
+require_relative '../../../library/digest/sha1/shared/constants'
+require_relative '../../../library/digest/sha256/shared/constants'
+require_relative '../../../library/digest/sha384/shared/constants'
+require_relative '../../../library/digest/sha512/shared/constants'
+require 'openssl'
+
+describe "OpenSSL::Digest class methods" do
+ describe ".digest" do
+ it "returns a SHA1 digest" do
+ OpenSSL::Digest.digest('sha1', SHA1Constants::Contents).should == SHA1Constants::Digest
+ end
+
+ it "returns a SHA256 digest" do
+ OpenSSL::Digest.digest('sha256', SHA256Constants::Contents).should == SHA256Constants::Digest
+ end
+
+ it "returns a SHA384 digest" do
+ OpenSSL::Digest.digest('sha384', SHA384Constants::Contents).should == SHA384Constants::Digest
+ end
+
+ it "returns a SHA512 digest" do
+ OpenSSL::Digest.digest('sha512', SHA512Constants::Contents).should == SHA512Constants::Digest
+ end
+ end
+
+ describe ".hexdigest" do
+ it "returns a SHA1 hexdigest" do
+ OpenSSL::Digest.hexdigest('sha1', SHA1Constants::Contents).should == SHA1Constants::Hexdigest
+ end
+
+ it "returns a SHA256 hexdigest" do
+ OpenSSL::Digest.hexdigest('sha256', SHA256Constants::Contents).should == SHA256Constants::Hexdigest
+ end
+
+ it "returns a SHA384 hexdigest" do
+ OpenSSL::Digest.hexdigest('sha384', SHA384Constants::Contents).should == SHA384Constants::Hexdigest
+ end
+
+ it "returns a SHA512 hexdigest" do
+ OpenSSL::Digest.hexdigest('sha512', SHA512Constants::Contents).should == SHA512Constants::Hexdigest
+ end
+ end
+
+ describe ".base64digest" do
+ it "returns a SHA1 base64digest" do
+ OpenSSL::Digest.base64digest('sha1', SHA1Constants::Contents).should == SHA1Constants::Base64digest
+ end
+
+ it "returns a SHA256 base64digest" do
+ OpenSSL::Digest.base64digest('sha256', SHA256Constants::Contents).should == SHA256Constants::Base64digest
+ end
+
+ it "returns a SHA384 base64digest" do
+ OpenSSL::Digest.base64digest('sha384', SHA384Constants::Contents).should == SHA384Constants::Base64digest
+ end
+
+ it "returns a SHA512 base64digest" do
+ OpenSSL::Digest.base64digest('sha512', SHA512Constants::Contents).should == SHA512Constants::Base64digest
+ end
+ end
+end
diff --git a/spec/ruby/library/openssl/digest/initialize_spec.rb b/spec/ruby/library/openssl/digest/initialize_spec.rb
new file mode 100644
index 0000000000..1cd0409c4d
--- /dev/null
+++ b/spec/ruby/library/openssl/digest/initialize_spec.rb
@@ -0,0 +1,141 @@
+require_relative '../../../spec_helper'
+require_relative '../../../library/digest/sha1/shared/constants'
+require_relative '../../../library/digest/sha256/shared/constants'
+require_relative '../../../library/digest/sha384/shared/constants'
+require_relative '../../../library/digest/sha512/shared/constants'
+require 'openssl'
+
+describe "OpenSSL::Digest#initialize" do
+ describe "can be called with a digest name" do
+ it "returns a SHA1 object" do
+ OpenSSL::Digest.new("sha1").name.should == "SHA1"
+ end
+
+ it "returns a SHA256 object" do
+ OpenSSL::Digest.new("sha256").name.should == "SHA256"
+ end
+
+ it "returns a SHA384 object" do
+ OpenSSL::Digest.new("sha384").name.should == "SHA384"
+ end
+
+ it "returns a SHA512 object" do
+ OpenSSL::Digest.new("sha512").name.should == "SHA512"
+ end
+
+ it "throws an error when called with an unknown digest" do
+ -> { OpenSSL::Digest.new("wd40") }.should raise_error(RuntimeError, /Unsupported digest algorithm \(wd40\)/)
+ end
+
+ it "cannot be called with a symbol" do
+ -> { OpenSSL::Digest.new(:SHA1) }.should raise_error(TypeError, /wrong argument type Symbol/)
+ end
+
+ it "does not call #to_str on the argument" do
+ name = mock("digest name")
+ name.should_not_receive(:to_str)
+ -> { OpenSSL::Digest.new(name) }.should raise_error(TypeError, /wrong argument type/)
+ end
+ end
+
+ describe "can be called with a digest object" do
+ it "returns a SHA1 object" do
+ OpenSSL::Digest.new(OpenSSL::Digest::SHA1.new).name.should == "SHA1"
+ end
+
+ it "returns a SHA256 object" do
+ OpenSSL::Digest.new(OpenSSL::Digest::SHA256.new).name.should == "SHA256"
+ end
+
+ it "returns a SHA384 object" do
+ OpenSSL::Digest.new(OpenSSL::Digest::SHA384.new).name.should == "SHA384"
+ end
+
+ it "returns a SHA512 object" do
+ OpenSSL::Digest.new(OpenSSL::Digest::SHA512.new).name.should == "SHA512"
+ end
+
+ it "ignores the state of the digest object" do
+ sha1 = OpenSSL::Digest.new('sha1', SHA1Constants::Contents)
+ OpenSSL::Digest.new(sha1).digest.should == SHA1Constants::BlankDigest
+ end
+ end
+
+ it "cannot be called with a digest class" do
+ -> { OpenSSL::Digest.new(OpenSSL::Digest::SHA1) }.should raise_error(TypeError, /wrong argument type Class/)
+ end
+
+ context "when called without an initial String argument" do
+ it "returns a SHA1 digest" do
+ OpenSSL::Digest.new("sha1").digest.should == SHA1Constants::BlankDigest
+ end
+
+ it "returns a SHA256 digest" do
+ OpenSSL::Digest.new("sha256").digest.should == SHA256Constants::BlankDigest
+ end
+
+ it "returns a SHA384 digest" do
+ OpenSSL::Digest.new("sha384").digest.should == SHA384Constants::BlankDigest
+ end
+
+ it "returns a SHA512 digest" do
+ OpenSSL::Digest.new("sha512").digest.should == SHA512Constants::BlankDigest
+ end
+ end
+
+ context "when called with an initial String argument" do
+ it "returns a SHA1 digest of that argument" do
+ OpenSSL::Digest.new("sha1", SHA1Constants::Contents).digest.should == SHA1Constants::Digest
+ end
+
+ it "returns a SHA256 digest of that argument" do
+ OpenSSL::Digest.new("sha256", SHA256Constants::Contents).digest.should == SHA256Constants::Digest
+ end
+
+ it "returns a SHA384 digest of that argument" do
+ OpenSSL::Digest.new("sha384", SHA384Constants::Contents).digest.should == SHA384Constants::Digest
+ end
+
+ it "returns a SHA512 digest of that argument" do
+ OpenSSL::Digest.new("sha512", SHA512Constants::Contents).digest.should == SHA512Constants::Digest
+ end
+ end
+
+ context "can be called on subclasses" do
+ describe "can be called without an initial String argument on subclasses" do
+ it "returns a SHA1 digest" do
+ OpenSSL::Digest::SHA1.new.digest.should == SHA1Constants::BlankDigest
+ end
+
+ it "returns a SHA256 digest" do
+ OpenSSL::Digest::SHA256.new.digest.should == SHA256Constants::BlankDigest
+ end
+
+ it "returns a SHA384 digest" do
+ OpenSSL::Digest::SHA384.new.digest.should == SHA384Constants::BlankDigest
+ end
+
+ it "returns a SHA512 digest" do
+ OpenSSL::Digest::SHA512.new.digest.should == SHA512Constants::BlankDigest
+ end
+ end
+
+ describe "can be called with an initial String argument on subclasses" do
+ it "returns a SHA1 digest" do
+ OpenSSL::Digest::SHA1.new(SHA1Constants::Contents).digest.should == SHA1Constants::Digest
+ end
+
+ it "returns a SHA256 digest" do
+ OpenSSL::Digest::SHA256.new(SHA256Constants::Contents).digest.should == SHA256Constants::Digest
+ end
+
+ it "returns a SHA384 digest" do
+ OpenSSL::Digest::SHA384.new(SHA384Constants::Contents).digest.should == SHA384Constants::Digest
+ end
+
+ it "returns a SHA512 digest" do
+ OpenSSL::Digest::SHA512.new(SHA512Constants::Contents).digest.should == SHA512Constants::Digest
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/openssl/digest/name_spec.rb b/spec/ruby/library/openssl/digest/name_spec.rb
new file mode 100644
index 0000000000..b379f35c1c
--- /dev/null
+++ b/spec/ruby/library/openssl/digest/name_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../../spec_helper'
+require 'openssl'
+
+describe "OpenSSL::Digest#name" do
+ it "returns the name of digest" do
+ OpenSSL::Digest.new('SHA1').name.should == 'SHA1'
+ end
+
+ it "converts the name to the internal representation of OpenSSL" do
+ OpenSSL::Digest.new('sha1').name.should == 'SHA1'
+ end
+
+ it "works on subclasses too" do
+ OpenSSL::Digest::SHA1.new.name.should == 'SHA1'
+ end
+end
diff --git a/spec/ruby/library/openssl/digest/reset_spec.rb b/spec/ruby/library/openssl/digest/reset_spec.rb
new file mode 100644
index 0000000000..c19bf46633
--- /dev/null
+++ b/spec/ruby/library/openssl/digest/reset_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../../spec_helper'
+require_relative '../../../library/digest/sha1/shared/constants'
+require_relative '../../../library/digest/sha256/shared/constants'
+require_relative '../../../library/digest/sha384/shared/constants'
+require_relative '../../../library/digest/sha512/shared/constants'
+require 'openssl'
+
+describe "OpenSSL::Digest#reset" do
+ it "works for a SHA1 digest" do
+ digest = OpenSSL::Digest.new('sha1', SHA1Constants::Contents)
+ digest.reset
+ digest.update(SHA1Constants::Contents)
+ digest.digest.should == SHA1Constants::Digest
+ end
+
+ it "works for a SHA256 digest" do
+ digest = OpenSSL::Digest.new('sha256', SHA256Constants::Contents)
+ digest.reset
+ digest.update(SHA256Constants::Contents)
+ digest.digest.should == SHA256Constants::Digest
+ end
+
+ it "works for a SHA384 digest" do
+ digest = OpenSSL::Digest.new('sha384', SHA384Constants::Contents)
+ digest.reset
+ digest.update(SHA384Constants::Contents)
+ digest.digest.should == SHA384Constants::Digest
+ end
+
+ it "works for a SHA512 digest" do
+ digest = OpenSSL::Digest.new('sha512', SHA512Constants::Contents)
+ digest.reset
+ digest.update(SHA512Constants::Contents)
+ digest.digest.should == SHA512Constants::Digest
+ end
+end
diff --git a/spec/ruby/library/openssl/digest/shared/update.rb b/spec/ruby/library/openssl/digest/shared/update.rb
new file mode 100644
index 0000000000..e5ff9dcb16
--- /dev/null
+++ b/spec/ruby/library/openssl/digest/shared/update.rb
@@ -0,0 +1,123 @@
+require_relative '../../../../library/digest/sha1/shared/constants'
+require_relative '../../../../library/digest/sha256/shared/constants'
+require_relative '../../../../library/digest/sha384/shared/constants'
+require_relative '../../../../library/digest/sha512/shared/constants'
+require 'openssl'
+
+describe :openssl_digest_update, shared: true do
+ context "when given input as a single string" do
+ it "returns a SHA1 digest" do
+ digest = OpenSSL::Digest.new('sha1')
+ digest.send(@method, SHA1Constants::Contents)
+ digest.digest.should == SHA1Constants::Digest
+ end
+
+ it "returns a SHA256 digest" do
+ digest = OpenSSL::Digest.new('sha256')
+ digest.send(@method, SHA256Constants::Contents)
+ digest.digest.should == SHA256Constants::Digest
+ end
+
+ it "returns a SHA384 digest" do
+ digest = OpenSSL::Digest.new('sha384')
+ digest.send(@method, SHA384Constants::Contents)
+ digest.digest.should == SHA384Constants::Digest
+ end
+
+ it "returns a SHA512 digest" do
+ digest = OpenSSL::Digest.new('sha512')
+ digest.send(@method, SHA512Constants::Contents)
+ digest.digest.should == SHA512Constants::Digest
+ end
+ end
+
+ context "when given input as multiple smaller substrings" do
+ it "returns a SHA1 digest" do
+ digest = OpenSSL::Digest.new('sha1')
+ SHA1Constants::Contents.each_char { |b| digest.send(@method, b) }
+ digest.digest.should == SHA1Constants::Digest
+ end
+
+ it "returns a SHA256 digest" do
+ digest = OpenSSL::Digest.new('sha256')
+ SHA256Constants::Contents.each_char { |b| digest.send(@method, b) }
+ digest.digest.should == SHA256Constants::Digest
+ end
+
+ it "returns a SHA384 digest" do
+ digest = OpenSSL::Digest.new('sha384')
+ SHA384Constants::Contents.each_char { |b| digest.send(@method, b) }
+ digest.digest.should == SHA384Constants::Digest
+ end
+
+ it "returns a SHA512 digest" do
+ digest = OpenSSL::Digest.new('sha512')
+ SHA512Constants::Contents.each_char { |b| digest.send(@method, b) }
+ digest.digest.should == SHA512Constants::Digest
+ end
+ end
+
+ context "when input is not a String and responds to #to_str" do
+ it "returns a SHA1 digest" do
+ str = mock('str')
+ str.should_receive(:to_str).and_return(SHA1Constants::Contents)
+ digest = OpenSSL::Digest.new('sha1')
+ digest.send(@method, str)
+ digest.digest.should == SHA1Constants::Digest
+ end
+
+ it "returns a SHA256 digest" do
+ str = mock('str')
+ str.should_receive(:to_str).and_return(SHA256Constants::Contents)
+ digest = OpenSSL::Digest.new('sha256')
+ digest.send(@method, str)
+ digest.digest.should == SHA256Constants::Digest
+ end
+
+ it "returns a SHA384 digest" do
+ str = mock('str')
+ str.should_receive(:to_str).and_return(SHA384Constants::Contents)
+ digest = OpenSSL::Digest.new('sha384')
+ digest.send(@method, str)
+ digest.digest.should == SHA384Constants::Digest
+ end
+
+ it "returns a SHA512 digest" do
+ str = mock('str')
+ str.should_receive(:to_str).and_return(SHA512Constants::Contents)
+ digest = OpenSSL::Digest.new('sha512')
+ digest.send(@method, str)
+ digest.digest.should == SHA512Constants::Digest
+ end
+ end
+
+ context "when input is not a String and does not respond to #to_str" do
+ it "raises a TypeError with SHA1" do
+ digest = OpenSSL::Digest.new('sha1')
+ -> {
+ digest.send(@method, Object.new)
+ }.should raise_error(TypeError, 'no implicit conversion of Object into String')
+ end
+
+ it "raises a TypeError with SHA256" do
+ digest = OpenSSL::Digest.new('sha256')
+ -> {
+ digest.send(@method, Object.new)
+ }.should raise_error(TypeError, 'no implicit conversion of Object into String')
+ end
+
+ it "raises a TypeError with SHA384" do
+ digest = OpenSSL::Digest.new('sha384')
+ -> {
+ digest.send(@method, Object.new)
+ }.should raise_error(TypeError, 'no implicit conversion of Object into String')
+ end
+
+ it "raises a TypeError with SHA512" do
+ digest = OpenSSL::Digest.new('sha512')
+ -> {
+ digest.send(@method, Object.new)
+ }.should raise_error(TypeError, 'no implicit conversion of Object into String')
+ end
+ end
+end
diff --git a/spec/ruby/library/openssl/digest/update_spec.rb b/spec/ruby/library/openssl/digest/update_spec.rb
new file mode 100644
index 0000000000..3a90b06c6b
--- /dev/null
+++ b/spec/ruby/library/openssl/digest/update_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/update'
+
+describe "OpenSSL::Digest#update" do
+ it_behaves_like :openssl_digest_update, :update
+end
diff --git a/spec/ruby/library/openssl/digest_spec.rb b/spec/ruby/library/openssl/digest_spec.rb
deleted file mode 100644
index b8e82d073f..0000000000
--- a/spec/ruby/library/openssl/digest_spec.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-require_relative '../../spec_helper'
-require_relative '../../library/digest/sha1/shared/constants'
-require_relative '../../library/digest/sha256/shared/constants'
-require_relative '../../library/digest/sha384/shared/constants'
-require_relative '../../library/digest/sha512/shared/constants'
-require 'openssl'
-
-describe "OpenSSL::Digest" do
-
- describe ".digest" do
- it "returns a SHA1 digest" do
- OpenSSL::Digest.digest('sha1', SHA1Constants::Contents).should == SHA1Constants::Digest
- end
-
- it "returns a SHA256 digest" do
- OpenSSL::Digest.digest('sha256', SHA256Constants::Contents).should == SHA256Constants::Digest
- end
-
- it "returns a SHA384 digest" do
- OpenSSL::Digest.digest('sha384', SHA384Constants::Contents).should == SHA384Constants::Digest
- end
-
- it "returns a SHA512 digest" do
- OpenSSL::Digest.digest('sha512', SHA512Constants::Contents).should == SHA512Constants::Digest
- end
- end
-
- describe ".hexdigest" do
- it "returns a SHA1 hexdigest" do
- OpenSSL::Digest.hexdigest('sha1', SHA1Constants::Contents).should == SHA1Constants::Hexdigest
- end
-
- it "returns a SHA256 hexdigest" do
- OpenSSL::Digest.hexdigest('sha256', SHA256Constants::Contents).should == SHA256Constants::Hexdigest
- end
-
- it "returns a SHA384 hexdigest" do
- OpenSSL::Digest.hexdigest('sha384', SHA384Constants::Contents).should == SHA384Constants::Hexdigest
- end
-
- it "returns a SHA512 hexdigest" do
- OpenSSL::Digest.hexdigest('sha512', SHA512Constants::Contents).should == SHA512Constants::Hexdigest
- end
- end
-
- describe ".base64digest" do
- it "returns a SHA1 base64digest" do
- OpenSSL::Digest.base64digest('sha1', SHA1Constants::Contents).should == SHA1Constants::Base64digest
- end
-
- it "returns a SHA256 base64digest" do
- OpenSSL::Digest.base64digest('sha256', SHA256Constants::Contents).should == SHA256Constants::Base64digest
- end
-
- it "returns a SHA384 base64digest" do
- OpenSSL::Digest.base64digest('sha384', SHA384Constants::Contents).should == SHA384Constants::Base64digest
- end
-
- it "returns a SHA512 base64digest" do
- OpenSSL::Digest.base64digest('sha512', SHA512Constants::Contents).should == SHA512Constants::Base64digest
- end
- end
-end
diff --git a/spec/ruby/library/openssl/fixed_length_secure_compare_spec.rb b/spec/ruby/library/openssl/fixed_length_secure_compare_spec.rb
new file mode 100644
index 0000000000..5a2ca168b5
--- /dev/null
+++ b/spec/ruby/library/openssl/fixed_length_secure_compare_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+require 'openssl'
+
+describe "OpenSSL.fixed_length_secure_compare" do
+ it "returns true for two strings with the same content" do
+ input1 = "the quick brown fox jumps over the lazy dog"
+ input2 = "the quick brown fox jumps over the lazy dog"
+ OpenSSL.fixed_length_secure_compare(input1, input2).should be_true
+ end
+
+ it "returns false for two strings of equal size with different content" do
+ input1 = "the quick brown fox jumps over the lazy dog"
+ input2 = "the lazy dog jumps over the quick brown fox"
+ OpenSSL.fixed_length_secure_compare(input1, input2).should be_false
+ end
+
+ it "converts both arguments to strings using #to_str" do
+ input1 = mock("input1")
+ input1.should_receive(:to_str).and_return("the quick brown fox jumps over the lazy dog")
+ input2 = mock("input2")
+ input2.should_receive(:to_str).and_return("the quick brown fox jumps over the lazy dog")
+ OpenSSL.fixed_length_secure_compare(input1, input2).should be_true
+ end
+
+ it "does not accept arguments that are not string and cannot be coerced into strings" do
+ -> {
+ OpenSSL.fixed_length_secure_compare("input1", :input2)
+ }.should raise_error(TypeError, 'no implicit conversion of Symbol into String')
+
+ -> {
+ OpenSSL.fixed_length_secure_compare(Object.new, "input2")
+ }.should raise_error(TypeError, 'no implicit conversion of Object into String')
+ end
+
+ it "raises an ArgumentError for two strings of different size" do
+ input1 = "the quick brown fox jumps over the lazy dog"
+ input2 = "the quick brown fox"
+ -> {
+ OpenSSL.fixed_length_secure_compare(input1, input2)
+ }.should raise_error(ArgumentError, 'inputs must be of equal length')
+ end
+end
diff --git a/spec/ruby/library/openssl/kdf/pbkdf2_hmac_spec.rb b/spec/ruby/library/openssl/kdf/pbkdf2_hmac_spec.rb
new file mode 100644
index 0000000000..40f8597275
--- /dev/null
+++ b/spec/ruby/library/openssl/kdf/pbkdf2_hmac_spec.rb
@@ -0,0 +1,168 @@
+require_relative '../../../spec_helper'
+require 'openssl'
+
+describe "OpenSSL::KDF.pbkdf2_hmac" do
+ before :each do
+ @defaults = {
+ salt: "\x00".b * 16,
+ iterations: 20_000,
+ length: 16,
+ hash: "sha1"
+ }
+ end
+
+ it "creates the same value with the same input" do
+ key = OpenSSL::KDF.pbkdf2_hmac("secret", **@defaults)
+ key.should == "!\x99+\xF0^\xD0\x8BM\x158\xC4\xAC\x9C\xF1\xF0\xE0".b
+ end
+
+ it "supports nullbytes embedded in the password" do
+ key = OpenSSL::KDF.pbkdf2_hmac("sec\x00ret".b, **@defaults)
+ key.should == "\xB9\x7F\xB0\xC2\th\xC8<\x86\xF3\x94Ij7\xEF\xF1".b
+ end
+
+ it "coerces the password into a String using #to_str" do
+ pass = mock("pass")
+ pass.should_receive(:to_str).and_return("secret")
+ key = OpenSSL::KDF.pbkdf2_hmac(pass, **@defaults)
+ key.should == "!\x99+\xF0^\xD0\x8BM\x158\xC4\xAC\x9C\xF1\xF0\xE0".b
+ end
+
+ it "coerces the salt into a String using #to_str" do
+ salt = mock("salt")
+ salt.should_receive(:to_str).and_return("\x00".b * 16)
+ key = OpenSSL::KDF.pbkdf2_hmac("secret", **@defaults, salt: salt)
+ key.should == "!\x99+\xF0^\xD0\x8BM\x158\xC4\xAC\x9C\xF1\xF0\xE0".b
+ end
+
+ it "coerces the iterations into an Integer using #to_int" do
+ iterations = mock("iterations")
+ iterations.should_receive(:to_int).and_return(20_000)
+ key = OpenSSL::KDF.pbkdf2_hmac("secret", **@defaults, iterations: iterations)
+ key.should == "!\x99+\xF0^\xD0\x8BM\x158\xC4\xAC\x9C\xF1\xF0\xE0".b
+ end
+
+ it "coerces the length into an Integer using #to_int" do
+ length = mock("length")
+ length.should_receive(:to_int).and_return(16)
+ key = OpenSSL::KDF.pbkdf2_hmac("secret", **@defaults, length: length)
+ key.should == "!\x99+\xF0^\xD0\x8BM\x158\xC4\xAC\x9C\xF1\xF0\xE0".b
+ end
+
+ it "accepts a OpenSSL::Digest object as hash" do
+ hash = OpenSSL::Digest.new("sha1")
+ key = OpenSSL::KDF.pbkdf2_hmac("secret", **@defaults, hash: hash)
+ key.should == "!\x99+\xF0^\xD0\x8BM\x158\xC4\xAC\x9C\xF1\xF0\xE0".b
+ end
+
+ it "accepts an empty password" do
+ key = OpenSSL::KDF.pbkdf2_hmac("", **@defaults)
+ key.should == "k\x9F-\xB1\xF7\x9A\v\xA1(C\xF9\x85!P\xEF\x8C".b
+ end
+
+ it "accepts an empty salt" do
+ key = OpenSSL::KDF.pbkdf2_hmac("secret", **@defaults, salt: "")
+ key.should == "\xD5f\xE5\xEA\xF91\x1D\xD3evD\xED\xDB\xE80\x80".b
+ end
+
+ it "accepts an empty length" do
+ key = OpenSSL::KDF.pbkdf2_hmac("secret", **@defaults, length: 0)
+ key.should.empty?
+ end
+
+ it "accepts an arbitrary length" do
+ key = OpenSSL::KDF.pbkdf2_hmac("secret", **@defaults, length: 19)
+ key.should == "!\x99+\xF0^\xD0\x8BM\x158\xC4\xAC\x9C\xF1\xF0\xE0\xCF\xBB\x7F".b
+ end
+
+ it "accepts any hash function known to OpenSSL" do
+ key = OpenSSL::KDF.pbkdf2_hmac("secret", **@defaults, hash: "sha512")
+ key.should == "N\x12}D\xCE\x99\xDBC\x8E\xEC\xAAr\xEA1\xDF\xFF".b
+ end
+
+ it "raises a TypeError when password is not a String and does not respond to #to_str" do
+ -> {
+ OpenSSL::KDF.pbkdf2_hmac(Object.new, **@defaults)
+ }.should raise_error(TypeError, "no implicit conversion of Object into String")
+ end
+
+ it "raises a TypeError when salt is not a String and does not respond to #to_str" do
+ -> {
+ OpenSSL::KDF.pbkdf2_hmac("secret", **@defaults, salt: Object.new)
+ }.should raise_error(TypeError, "no implicit conversion of Object into String")
+ end
+
+ it "raises a TypeError when iterations is not an Integer and does not respond to #to_int" do
+ -> {
+ OpenSSL::KDF.pbkdf2_hmac("secret", **@defaults, iterations: Object.new)
+ }.should raise_error(TypeError, "no implicit conversion of Object into Integer")
+ end
+
+ it "raises a TypeError when length is not an Integer and does not respond to #to_int" do
+ -> {
+ OpenSSL::KDF.pbkdf2_hmac("secret", **@defaults, length: Object.new)
+ }.should raise_error(TypeError, "no implicit conversion of Object into Integer")
+ end
+
+ it "raises a TypeError when hash is neither a String nor an OpenSSL::Digest" do
+ -> {
+ OpenSSL::KDF.pbkdf2_hmac("secret", **@defaults, hash: Object.new)
+ }.should raise_error(TypeError, "wrong argument type Object (expected OpenSSL/Digest)")
+ end
+
+ it "raises a TypeError when hash is neither a String nor an OpenSSL::Digest, it does not try to call #to_str" do
+ hash = mock("hash")
+ hash.should_not_receive(:to_str)
+ -> {
+ OpenSSL::KDF.pbkdf2_hmac("secret", **@defaults, hash: hash)
+ }.should raise_error(TypeError, "wrong argument type MockObject (expected OpenSSL/Digest)")
+ end
+
+ it "raises a RuntimeError for unknown digest algorithms" do
+ -> {
+ OpenSSL::KDF.pbkdf2_hmac("secret", **@defaults, hash: "wd40")
+ }.should raise_error(RuntimeError, /Unsupported digest algorithm \(wd40\)/)
+ end
+
+ it "treats salt as a required keyword" do
+ -> {
+ OpenSSL::KDF.pbkdf2_hmac("secret", **@defaults.except(:salt))
+ }.should raise_error(ArgumentError, 'missing keyword: :salt')
+ end
+
+ it "treats iterations as a required keyword" do
+ -> {
+ OpenSSL::KDF.pbkdf2_hmac("secret", **@defaults.except(:iterations))
+ }.should raise_error(ArgumentError, 'missing keyword: :iterations')
+ end
+
+ it "treats length as a required keyword" do
+ -> {
+ OpenSSL::KDF.pbkdf2_hmac("secret", **@defaults.except(:length))
+ }.should raise_error(ArgumentError, 'missing keyword: :length')
+ end
+
+ it "treats hash as a required keyword" do
+ -> {
+ OpenSSL::KDF.pbkdf2_hmac("secret", **@defaults.except(:hash))
+ }.should raise_error(ArgumentError, 'missing keyword: :hash')
+ end
+
+ it "treats all keywords as required" do
+ -> {
+ OpenSSL::KDF.pbkdf2_hmac("secret")
+ }.should raise_error(ArgumentError, 'missing keywords: :salt, :iterations, :length, :hash')
+ end
+
+ guard -> { OpenSSL::OPENSSL_VERSION_NUMBER >= 0x30000000 } do
+ it "raises an OpenSSL::KDF::KDFError for 0 or less iterations" do
+ -> {
+ OpenSSL::KDF.pbkdf2_hmac("secret", **@defaults, iterations: 0)
+ }.should raise_error(OpenSSL::KDF::KDFError, "PKCS5_PBKDF2_HMAC: invalid iteration count")
+
+ -> {
+ OpenSSL::KDF.pbkdf2_hmac("secret", **@defaults, iterations: -1)
+ }.should raise_error(OpenSSL::KDF::KDFError, /PKCS5_PBKDF2_HMAC/)
+ end
+ end
+end
diff --git a/spec/ruby/library/openssl/kdf/scrypt_spec.rb b/spec/ruby/library/openssl/kdf/scrypt_spec.rb
new file mode 100644
index 0000000000..5dc9f2f281
--- /dev/null
+++ b/spec/ruby/library/openssl/kdf/scrypt_spec.rb
@@ -0,0 +1,209 @@
+require_relative '../../../spec_helper'
+require 'openssl'
+
+guard -> { OpenSSL::KDF.respond_to?(:scrypt) } do
+ describe "OpenSSL::KDF.scrypt" do
+ before :each do
+ @defaults = {
+ salt: "\x00".b * 16,
+ N: 2**14,
+ r: 8,
+ p: 1,
+ length: 32
+ }
+ end
+
+ it "creates the same value with the same input" do
+ key = OpenSSL::KDF.scrypt("secret", **@defaults)
+ key.should == "h\xB2k\xDF]\xDA\xE1.-(\xCF\xAC\x91D\x8F\xC2a\x9C\x9D\x17}\xF2\x84T\xD4)\xC2>\xFE\x93\xE3\xF4".b
+ end
+
+ it "supports nullbytes embedded into the password" do
+ key = OpenSSL::KDF.scrypt("sec\x00ret".b, **@defaults)
+ key.should == "\xF9\xA4\xA0\xF1p\xF4\xF0\xCAT\xB4v\xEB\r7\x88N\xF7\x15]Ns\xFCwt4a\xC9\xC6\xA7\x13\x81&".b
+ end
+
+ it "coerces the password into a String using #to_str" do
+ pass = mock("pass")
+ pass.should_receive(:to_str).and_return("secret")
+ key = OpenSSL::KDF.scrypt(pass, **@defaults)
+ key.should == "h\xB2k\xDF]\xDA\xE1.-(\xCF\xAC\x91D\x8F\xC2a\x9C\x9D\x17}\xF2\x84T\xD4)\xC2>\xFE\x93\xE3\xF4".b
+ end
+
+ it "coerces the salt into a String using #to_str" do
+ salt = mock("salt")
+ salt.should_receive(:to_str).and_return("\x00".b * 16)
+ key = OpenSSL::KDF.scrypt("secret", **@defaults, salt: salt)
+ key.should == "h\xB2k\xDF]\xDA\xE1.-(\xCF\xAC\x91D\x8F\xC2a\x9C\x9D\x17}\xF2\x84T\xD4)\xC2>\xFE\x93\xE3\xF4".b
+ end
+
+ it "coerces the N into an Integer using #to_int" do
+ n = mock("N")
+ n.should_receive(:to_int).and_return(2**14)
+ key = OpenSSL::KDF.scrypt("secret", **@defaults, N: n)
+ key.should == "h\xB2k\xDF]\xDA\xE1.-(\xCF\xAC\x91D\x8F\xC2a\x9C\x9D\x17}\xF2\x84T\xD4)\xC2>\xFE\x93\xE3\xF4".b
+ end
+
+ it "coerces the r into an Integer using #to_int" do
+ r = mock("r")
+ r.should_receive(:to_int).and_return(8)
+ key = OpenSSL::KDF.scrypt("secret", **@defaults, r: r)
+ key.should == "h\xB2k\xDF]\xDA\xE1.-(\xCF\xAC\x91D\x8F\xC2a\x9C\x9D\x17}\xF2\x84T\xD4)\xC2>\xFE\x93\xE3\xF4".b
+ end
+
+ it "coerces the p into an Integer using #to_int" do
+ p = mock("p")
+ p.should_receive(:to_int).and_return(1)
+ key = OpenSSL::KDF.scrypt("secret", **@defaults, p: p)
+ key.should == "h\xB2k\xDF]\xDA\xE1.-(\xCF\xAC\x91D\x8F\xC2a\x9C\x9D\x17}\xF2\x84T\xD4)\xC2>\xFE\x93\xE3\xF4".b
+ end
+
+ it "coerces the length into an Integer using #to_int" do
+ length = mock("length")
+ length.should_receive(:to_int).and_return(32)
+ key = OpenSSL::KDF.scrypt("secret", **@defaults, length: length)
+ key.should == "h\xB2k\xDF]\xDA\xE1.-(\xCF\xAC\x91D\x8F\xC2a\x9C\x9D\x17}\xF2\x84T\xD4)\xC2>\xFE\x93\xE3\xF4".b
+ end
+
+ it "accepts an empty password" do
+ key = OpenSSL::KDF.scrypt("", **@defaults)
+ key.should == "\xAA\xFC\xF5^E\x94v\xFFk\xE6\xF0vR\xE7\x13\xA7\xF5\x15'\x9A\xE4C\x9Dn\x18F_E\xD2\v\e\xB3".b
+ end
+
+ it "accepts an empty salt" do
+ key = OpenSSL::KDF.scrypt("secret", **@defaults, salt: "")
+ key.should == "\x96\xACDl\xCB3/aN\xB0F\x8A#\xD7\x92\xD2O\x1E\v\xBB\xCE\xC0\xAA\xB9\x0F]\xB09\xEA8\xDD\e".b
+ end
+
+ it "accepts a zero length" do
+ key = OpenSSL::KDF.scrypt("secret", **@defaults, length: 0)
+ key.should.empty?
+ end
+
+ it "accepts an arbitrary length" do
+ key = OpenSSL::KDF.scrypt("secret", **@defaults, length: 19)
+ key.should == "h\xB2k\xDF]\xDA\xE1.-(\xCF\xAC\x91D\x8F\xC2a\x9C\x9D".b
+ end
+
+ it "raises a TypeError when password is not a String and does not respond to #to_str" do
+ -> {
+ OpenSSL::KDF.scrypt(Object.new, **@defaults)
+ }.should raise_error(TypeError, "no implicit conversion of Object into String")
+ end
+
+ it "raises a TypeError when salt is not a String and does not respond to #to_str" do
+ -> {
+ OpenSSL::KDF.scrypt("secret", **@defaults, salt: Object.new)
+ }.should raise_error(TypeError, "no implicit conversion of Object into String")
+ end
+
+ it "raises a TypeError when N is not an Integer and does not respond to #to_int" do
+ -> {
+ OpenSSL::KDF.scrypt("secret", **@defaults, N: Object.new)
+ }.should raise_error(TypeError, "no implicit conversion of Object into Integer")
+ end
+
+ it "raises a TypeError when r is not an Integer and does not respond to #to_int" do
+ -> {
+ OpenSSL::KDF.scrypt("secret", **@defaults, r: Object.new)
+ }.should raise_error(TypeError, "no implicit conversion of Object into Integer")
+ end
+
+ it "raises a TypeError when p is not an Integer and does not respond to #to_int" do
+ -> {
+ OpenSSL::KDF.scrypt("secret", **@defaults, p: Object.new)
+ }.should raise_error(TypeError, "no implicit conversion of Object into Integer")
+ end
+
+ it "raises a TypeError when length is not an Integer and does not respond to #to_int" do
+ -> {
+ OpenSSL::KDF.scrypt("secret", **@defaults, length: Object.new)
+ }.should raise_error(TypeError, "no implicit conversion of Object into Integer")
+ end
+
+ it "treats salt as a required keyword" do
+ -> {
+ OpenSSL::KDF.scrypt("secret", **@defaults.except(:salt))
+ }.should raise_error(ArgumentError, 'missing keyword: :salt')
+ end
+
+ it "treats N as a required keyword" do
+ -> {
+ OpenSSL::KDF.scrypt("secret", **@defaults.except(:N))
+ }.should raise_error(ArgumentError, 'missing keyword: :N')
+ end
+
+ it "treats r as a required keyword" do
+ -> {
+ OpenSSL::KDF.scrypt("secret", **@defaults.except(:r))
+ }.should raise_error(ArgumentError, 'missing keyword: :r')
+ end
+
+ it "treats p as a required keyword" do
+ -> {
+ OpenSSL::KDF.scrypt("secret", **@defaults.except(:p))
+ }.should raise_error(ArgumentError, 'missing keyword: :p')
+ end
+
+ it "treats length as a required keyword" do
+ -> {
+ OpenSSL::KDF.scrypt("secret", **@defaults.except(:length))
+ }.should raise_error(ArgumentError, 'missing keyword: :length')
+ end
+
+ it "treats all keywords as required" do
+ -> {
+ OpenSSL::KDF.scrypt("secret")
+ }.should raise_error(ArgumentError, 'missing keywords: :salt, :N, :r, :p, :length')
+ end
+
+ it "requires N to be a power of 2" do
+ -> {
+ OpenSSL::KDF.scrypt("secret", **@defaults, N: 2**14 - 1)
+ }.should raise_error(OpenSSL::KDF::KDFError, /EVP_PBE_scrypt/)
+ end
+
+ it "requires N to be at least 2" do
+ key = OpenSSL::KDF.scrypt("secret", **@defaults, N: 2)
+ key.should == "\x06A$a\xA9!\xBE\x01\x85\xA7\x18\xBCEa\x82\xC5\xFEl\x93\xAB\xBD\xF7\x8B\x84\v\xFC\eN\xEBQ\xE6\xD2".b
+
+ -> {
+ OpenSSL::KDF.scrypt("secret", **@defaults, N: 1)
+ }.should raise_error(OpenSSL::KDF::KDFError, /EVP_PBE_scrypt/)
+
+ -> {
+ OpenSSL::KDF.scrypt("secret", **@defaults, N: 0)
+ }.should raise_error(OpenSSL::KDF::KDFError, /EVP_PBE_scrypt/)
+
+ -> {
+ OpenSSL::KDF.scrypt("secret", **@defaults, N: -1)
+ }.should raise_error(OpenSSL::KDF::KDFError, /EVP_PBE_scrypt/)
+ end
+
+ it "requires r to be positive" do
+ -> {
+ OpenSSL::KDF.scrypt("secret", **@defaults, r: 0)
+ }.should raise_error(OpenSSL::KDF::KDFError, /EVP_PBE_scrypt/)
+
+ -> {
+ OpenSSL::KDF.scrypt("secret", **@defaults, r: -1)
+ }.should raise_error(OpenSSL::KDF::KDFError, /EVP_PBE_scrypt/)
+ end
+
+ it "requires p to be positive" do
+ -> {
+ OpenSSL::KDF.scrypt("secret", **@defaults, p: 0)
+ }.should raise_error(OpenSSL::KDF::KDFError, /EVP_PBE_scrypt/)
+
+ -> {
+ OpenSSL::KDF.scrypt("secret", **@defaults, p: -1)
+ }.should raise_error(OpenSSL::KDF::KDFError, /EVP_PBE_scrypt/)
+ end
+
+ it "requires length to be not negative" do
+ -> {
+ OpenSSL::KDF.scrypt("secret", **@defaults, length: -1)
+ }.should raise_error(ArgumentError, "negative string size (or size too big)")
+ end
+ end
+end
diff --git a/spec/ruby/library/openssl/random/shared/random_bytes.rb b/spec/ruby/library/openssl/random/shared/random_bytes.rb
index 037f10d409..f97ccd9974 100644
--- a/spec/ruby/library/openssl/random/shared/random_bytes.rb
+++ b/spec/ruby/library/openssl/random/shared/random_bytes.rb
@@ -1,7 +1,7 @@
require_relative '../../../../spec_helper'
require 'openssl'
-describe :openssl_random_bytes, shared: true do |cmd|
+describe :openssl_random_bytes, shared: true do
it "generates a random binary string of specified length" do
(1..64).each do |idx|
bytes = OpenSSL::Random.send(@method, idx)
diff --git a/spec/ruby/library/openssl/secure_compare_spec.rb b/spec/ruby/library/openssl/secure_compare_spec.rb
new file mode 100644
index 0000000000..cec48e01e7
--- /dev/null
+++ b/spec/ruby/library/openssl/secure_compare_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+require 'openssl'
+
+describe "OpenSSL.secure_compare" do
+ it "returns true for two strings with the same content" do
+ input1 = "the quick brown fox jumps over the lazy dog"
+ input2 = "the quick brown fox jumps over the lazy dog"
+ OpenSSL.secure_compare(input1, input2).should be_true
+ end
+
+ it "returns false for two strings with different content" do
+ input1 = "the quick brown fox jumps over the lazy dog"
+ input2 = "the lazy dog jumps over the quick brown fox"
+ OpenSSL.secure_compare(input1, input2).should be_false
+ end
+
+ it "converts both arguments to strings using #to_str, but adds equality check for the original objects" do
+ input1 = mock("input1")
+ input1.should_receive(:to_str).and_return("the quick brown fox jumps over the lazy dog")
+ input2 = mock("input2")
+ input2.should_receive(:to_str).and_return("the quick brown fox jumps over the lazy dog")
+ OpenSSL.secure_compare(input1, input2).should be_false
+
+ input = mock("input")
+ input.should_receive(:to_str).twice.and_return("the quick brown fox jumps over the lazy dog")
+ OpenSSL.secure_compare(input, input).should be_true
+ end
+
+ it "does not accept arguments that are not string and cannot be coerced into strings" do
+ -> {
+ OpenSSL.secure_compare("input1", :input2)
+ }.should raise_error(TypeError, 'no implicit conversion of Symbol into String')
+
+ -> {
+ OpenSSL.secure_compare(Object.new, "input2")
+ }.should raise_error(TypeError, 'no implicit conversion of Object into String')
+ end
+end
diff --git a/spec/ruby/library/openssl/x509/name/verify_spec.rb b/spec/ruby/library/openssl/x509/name/verify_spec.rb
deleted file mode 100644
index 6dcfc99466..0000000000
--- a/spec/ruby/library/openssl/x509/name/verify_spec.rb
+++ /dev/null
@@ -1,78 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'openssl'
-
-describe "OpenSSL::X509::Name.verify" do
- it "returns true for valid certificate" do
- key = OpenSSL::PKey::RSA.new 2048
- cert = OpenSSL::X509::Certificate.new
- cert.version = 2
- cert.serial = 1
- cert.subject = OpenSSL::X509::Name.parse "/DC=org/DC=truffleruby/CN=TruffleRuby CA"
- cert.issuer = cert.subject
- cert.public_key = key.public_key
- cert.not_before = Time.now - 10
- cert.not_after = cert.not_before + 365 * 24 * 60 * 60
- cert.sign key, OpenSSL::Digest.new('SHA256')
- store = OpenSSL::X509::Store.new
- store.add_cert(cert)
- [store.verify(cert), store.error, store.error_string].should == [true, 0, "ok"]
- end
-
- it "returns false for an expired certificate" do
- key = OpenSSL::PKey::RSA.new 2048
- cert = OpenSSL::X509::Certificate.new
- cert.version = 2
- cert.serial = 1
- cert.subject = OpenSSL::X509::Name.parse "/DC=org/DC=truffleruby/CN=TruffleRuby CA"
- cert.issuer = cert.subject
- cert.public_key = key.public_key
- cert.not_before = Time.now - 10
- cert.not_after = Time.now - 5
- cert.sign key, OpenSSL::Digest.new('SHA256')
- store = OpenSSL::X509::Store.new
- store.add_cert(cert)
- store.verify(cert).should == false
- end
-
- it "returns false for an expired root certificate" do
- root_key = OpenSSL::PKey::RSA.new 2048
- root_cert = OpenSSL::X509::Certificate.new
- root_cert.version = 2
- root_cert.serial = 1
- root_cert.subject = OpenSSL::X509::Name.parse "/DC=org/DC=truffleruby/CN=TruffleRuby CA"
- root_cert.issuer = root_cert.subject
- root_cert.public_key = root_key.public_key
- root_cert.not_before = Time.now - 10
- root_cert.not_after = Time.now - 5
- ef = OpenSSL::X509::ExtensionFactory.new
- ef.subject_certificate = root_cert
- ef.issuer_certificate = root_cert
- root_cert.add_extension(ef.create_extension("basicConstraints","CA:TRUE",true))
- root_cert.add_extension(ef.create_extension("keyUsage","keyCertSign, cRLSign", true))
- root_cert.add_extension(ef.create_extension("subjectKeyIdentifier","hash",false))
- root_cert.add_extension(ef.create_extension("authorityKeyIdentifier","keyid:always",false))
- root_cert.sign(root_key, OpenSSL::Digest.new('SHA256'))
-
-
- key = OpenSSL::PKey::RSA.new 2048
- cert = OpenSSL::X509::Certificate.new
- cert.version = 2
- cert.serial = 2
- cert.subject = OpenSSL::X509::Name.parse "/DC=org/DC=truffleruby/CN=TruffleRuby certificate"
- cert.issuer = root_cert.subject
- cert.public_key = key.public_key
- cert.not_before = Time.now
- cert.not_after = cert.not_before + 1 * 365 * 24 * 60 * 60
- ef = OpenSSL::X509::ExtensionFactory.new
- ef.subject_certificate = cert
- ef.issuer_certificate = root_cert
- cert.add_extension(ef.create_extension("keyUsage","digitalSignature", true))
- cert.add_extension(ef.create_extension("subjectKeyIdentifier","hash",false))
- cert.sign(root_key, OpenSSL::Digest.new('SHA256'))
-
- store = OpenSSL::X509::Store.new
- store.add_cert(root_cert)
- store.add_cert(cert)
- store.verify(cert).should == false
- end
-end
diff --git a/spec/ruby/library/openssl/x509/store/verify_spec.rb b/spec/ruby/library/openssl/x509/store/verify_spec.rb
new file mode 100644
index 0000000000..6a6a53d992
--- /dev/null
+++ b/spec/ruby/library/openssl/x509/store/verify_spec.rb
@@ -0,0 +1,78 @@
+require_relative '../../../../spec_helper'
+require 'openssl'
+
+describe "OpenSSL::X509::Store#verify" do
+ it "returns true for valid certificate" do
+ key = OpenSSL::PKey::RSA.new 2048
+ cert = OpenSSL::X509::Certificate.new
+ cert.version = 2
+ cert.serial = 1
+ cert.subject = OpenSSL::X509::Name.parse "/DC=org/DC=truffleruby/CN=TruffleRuby CA"
+ cert.issuer = cert.subject
+ cert.public_key = key.public_key
+ cert.not_before = Time.now - 10
+ cert.not_after = cert.not_before + 365 * 24 * 60 * 60
+ cert.sign key, OpenSSL::Digest.new('SHA256')
+ store = OpenSSL::X509::Store.new
+ store.add_cert(cert)
+ [store.verify(cert), store.error, store.error_string].should == [true, 0, "ok"]
+ end
+
+ it "returns false for an expired certificate" do
+ key = OpenSSL::PKey::RSA.new 2048
+ cert = OpenSSL::X509::Certificate.new
+ cert.version = 2
+ cert.serial = 1
+ cert.subject = OpenSSL::X509::Name.parse "/DC=org/DC=truffleruby/CN=TruffleRuby CA"
+ cert.issuer = cert.subject
+ cert.public_key = key.public_key
+ cert.not_before = Time.now - 10
+ cert.not_after = Time.now - 5
+ cert.sign key, OpenSSL::Digest.new('SHA256')
+ store = OpenSSL::X509::Store.new
+ store.add_cert(cert)
+ store.verify(cert).should == false
+ end
+
+ it "returns false for an expired root certificate" do
+ root_key = OpenSSL::PKey::RSA.new 2048
+ root_cert = OpenSSL::X509::Certificate.new
+ root_cert.version = 2
+ root_cert.serial = 1
+ root_cert.subject = OpenSSL::X509::Name.parse "/DC=org/DC=truffleruby/CN=TruffleRuby CA"
+ root_cert.issuer = root_cert.subject
+ root_cert.public_key = root_key.public_key
+ root_cert.not_before = Time.now - 10
+ root_cert.not_after = Time.now - 5
+ ef = OpenSSL::X509::ExtensionFactory.new
+ ef.subject_certificate = root_cert
+ ef.issuer_certificate = root_cert
+ root_cert.add_extension(ef.create_extension("basicConstraints","CA:TRUE",true))
+ root_cert.add_extension(ef.create_extension("keyUsage","keyCertSign, cRLSign", true))
+ root_cert.add_extension(ef.create_extension("subjectKeyIdentifier","hash",false))
+ root_cert.add_extension(ef.create_extension("authorityKeyIdentifier","keyid:always",false))
+ root_cert.sign(root_key, OpenSSL::Digest.new('SHA256'))
+
+
+ key = OpenSSL::PKey::RSA.new 2048
+ cert = OpenSSL::X509::Certificate.new
+ cert.version = 2
+ cert.serial = 2
+ cert.subject = OpenSSL::X509::Name.parse "/DC=org/DC=truffleruby/CN=TruffleRuby certificate"
+ cert.issuer = root_cert.subject
+ cert.public_key = key.public_key
+ cert.not_before = Time.now
+ cert.not_after = cert.not_before + 1 * 365 * 24 * 60 * 60
+ ef = OpenSSL::X509::ExtensionFactory.new
+ ef.subject_certificate = cert
+ ef.issuer_certificate = root_cert
+ cert.add_extension(ef.create_extension("keyUsage","digitalSignature", true))
+ cert.add_extension(ef.create_extension("subjectKeyIdentifier","hash",false))
+ cert.sign(root_key, OpenSSL::Digest.new('SHA256'))
+
+ store = OpenSSL::X509::Store.new
+ store.add_cert(root_cert)
+ store.add_cert(cert)
+ store.verify(cert).should == false
+ end
+end
diff --git a/spec/ruby/library/prime/each_spec.rb b/spec/ruby/library/prime/each_spec.rb
index c89e871582..b99cf7cf0e 100644
--- a/spec/ruby/library/prime/each_spec.rb
+++ b/spec/ruby/library/prime/each_spec.rb
@@ -1,170 +1,167 @@
require_relative '../../spec_helper'
+require 'prime'
-ruby_version_is ""..."3.1" do
- require 'prime'
-
- describe :prime_each, shared: true do
- before :each do
- ScratchPad.record []
- end
+describe :prime_each, shared: true do
+ before :each do
+ ScratchPad.record []
+ end
- it "enumerates primes" do
- primes = Prime.instance
- result = []
+ it "enumerates primes" do
+ primes = Prime.instance
+ result = []
- primes.each { |p|
- result << p
- break if p > 10
- }
+ primes.each { |p|
+ result << p
+ break if p > 10
+ }
- result.should == [2, 3, 5, 7, 11]
- end
+ result.should == [2, 3, 5, 7, 11]
+ end
- it "yields ascending primes to the block" do
- previous = 1
- @object.each do |prime|
- break if prime > 1000
- ScratchPad << prime
- prime.should > previous
- previous = prime
- end
-
- all_prime = true
- ScratchPad.recorded.all? do |prime|
- all_prime &&= (2..Math.sqrt(prime)).all? { |d| prime % d != 0 }
- end
-
- all_prime.should be_true
+ it "yields ascending primes to the block" do
+ previous = 1
+ @object.each do |prime|
+ break if prime > 1000
+ ScratchPad << prime
+ prime.should > previous
+ previous = prime
end
- it "returns the last evaluated expression in the passed block" do
- @object.each { break :value }.should equal(:value)
+ all_prime = true
+ ScratchPad.recorded.all? do |prime|
+ all_prime &&= (2..Math.sqrt(prime)).all? { |d| prime % d != 0 }
end
- describe "when not passed a block" do
- before :each do
- @prime_enum = @object.each
- end
-
- it "returns an object that is Enumerable" do
- @prime_enum.each.should be_kind_of(Enumerable)
- end
-
- it "returns an object that responds to #with_index" do
- @prime_enum.should respond_to(:with_index)
- end
+ all_prime.should be_true
+ end
- it "returns an object that responds to #with_object" do
- @prime_enum.should respond_to(:with_object)
- end
+ it "returns the last evaluated expression in the passed block" do
+ @object.each { break :value }.should equal(:value)
+ end
- it "returns an object that responds to #next" do
- @prime_enum.should respond_to(:next)
- end
+ describe "when not passed a block" do
+ before :each do
+ @prime_enum = @object.each
+ end
- it "returns an object that responds to #rewind" do
- @prime_enum.should respond_to(:rewind)
- end
+ it "returns an object that is Enumerable" do
+ @prime_enum.each.should be_kind_of(Enumerable)
+ end
- it "yields primes starting at 2 independent of prior enumerators" do
- @prime_enum.next.should == 2
- @prime_enum.next.should == 3
+ it "returns an object that responds to #with_index" do
+ @prime_enum.should respond_to(:with_index)
+ end
- @object.each { |prime| break prime }.should == 2
- end
+ it "returns an object that responds to #with_object" do
+ @prime_enum.should respond_to(:with_object)
+ end
- it "returns an enumerator that yields previous primes when #rewind is called" do
- @prime_enum.next.should == 2
- @prime_enum.next.should == 3
- @prime_enum.rewind
- @prime_enum.next.should == 2
- end
+ it "returns an object that responds to #next" do
+ @prime_enum.should respond_to(:next)
+ end
- it "returns independent enumerators" do
- enum = @object.each
- enum.next.should == 2
- enum.next.should == 3
+ it "returns an object that responds to #rewind" do
+ @prime_enum.should respond_to(:rewind)
+ end
- @prime_enum.next.should == 2
+ it "yields primes starting at 2 independent of prior enumerators" do
+ @prime_enum.next.should == 2
+ @prime_enum.next.should == 3
- enum.next.should == 5
- end
+ @object.each { |prime| break prime }.should == 2
end
- end
- describe :prime_each_with_arguments, shared: true do
- before :each do
- ScratchPad.record []
+ it "returns an enumerator that yields previous primes when #rewind is called" do
+ @prime_enum.next.should == 2
+ @prime_enum.next.should == 3
+ @prime_enum.rewind
+ @prime_enum.next.should == 2
end
- it "yields ascending primes less than or equal to the argument" do
- bound = 1000
- previous = 1
- @object.each(bound) do |prime|
- ScratchPad << prime
- prime.should > previous
- previous = prime
- end
+ it "returns independent enumerators" do
+ enum = @object.each
+ enum.next.should == 2
+ enum.next.should == 3
- ScratchPad.recorded.all? do |prime|
- (2..Math.sqrt(prime)).all? { |d| prime % d != 0 }
- end.should be_true
+ @prime_enum.next.should == 2
- ScratchPad.recorded.all? { |prime| prime <= bound }.should be_true
+ enum.next.should == 5
end
+ end
+end
- it "returns nil when no prime is generated" do
- @object.each(1) { :value }.should be_nil
- end
+describe :prime_each_with_arguments, shared: true do
+ before :each do
+ ScratchPad.record []
+ end
- it "yields primes starting at 2 independent of prior enumeration" do
- @object.each(10) { |prime| prime }.should == 7
- @object.each(10) { |prime| break prime }.should == 2
+ it "yields ascending primes less than or equal to the argument" do
+ bound = 1000
+ previous = 1
+ @object.each(bound) do |prime|
+ ScratchPad << prime
+ prime.should > previous
+ previous = prime
end
- it "accepts a pseudo-prime generator as the second argument" do
- generator = mock('very bad pseudo-prime generator')
- generator.should_receive(:upper_bound=).with(100)
- generator.should_receive(:each).and_yield(2).and_yield(3).and_yield(4)
-
- @object.each(100, generator) { |prime| ScratchPad << prime }
- ScratchPad.recorded.should == [2, 3, 4]
- end
+ ScratchPad.recorded.all? do |prime|
+ (2..Math.sqrt(prime)).all? { |d| prime % d != 0 }
+ end.should be_true
- describe "when not passed a block" do
- it "returns an object that returns primes less than or equal to the bound" do
- bound = 100
- @object.each(bound).all? { |prime| prime <= bound }.should be_true
- end
- end
+ ScratchPad.recorded.all? { |prime| prime <= bound }.should be_true
end
- describe "Prime.each" do
- it_behaves_like :prime_each, :each, Prime
+ it "returns nil when no prime is generated" do
+ @object.each(1) { :value }.should be_nil
end
- describe "Prime.each" do
- it_behaves_like :prime_each_with_arguments, :each, Prime
+ it "yields primes starting at 2 independent of prior enumeration" do
+ @object.each(10) { |prime| prime }.should == 7
+ @object.each(10) { |prime| break prime }.should == 2
end
- describe "Prime#each with Prime.instance" do
- it_behaves_like :prime_each, :each, Prime.instance
- end
+ it "accepts a pseudo-prime generator as the second argument" do
+ generator = mock('very bad pseudo-prime generator')
+ generator.should_receive(:upper_bound=).with(100)
+ generator.should_receive(:each).and_yield(2).and_yield(3).and_yield(4)
- describe "Prime#each with Prime.instance" do
- it_behaves_like :prime_each_with_arguments, :each, Prime.instance
+ @object.each(100, generator) { |prime| ScratchPad << prime }
+ ScratchPad.recorded.should == [2, 3, 4]
end
- describe "Prime#each with Prime.instance" do
- before :each do
- @object = Prime.instance
+ describe "when not passed a block" do
+ it "returns an object that returns primes less than or equal to the bound" do
+ bound = 100
+ @object.each(bound).all? { |prime| prime <= bound }.should be_true
end
+ end
+end
- it_behaves_like :prime_each, :each
+describe "Prime.each" do
+ it_behaves_like :prime_each, :each, Prime
+end
- it "resets the enumerator with each call" do
- @object.each { |prime| break if prime > 10 }
- @object.each { |prime| break prime }.should == 2
- end
+describe "Prime.each" do
+ it_behaves_like :prime_each_with_arguments, :each, Prime
+end
+
+describe "Prime#each with Prime.instance" do
+ it_behaves_like :prime_each, :each, Prime.instance
+end
+
+describe "Prime#each with Prime.instance" do
+ it_behaves_like :prime_each_with_arguments, :each, Prime.instance
+end
+
+describe "Prime#each with Prime.instance" do
+ before :each do
+ @object = Prime.instance
+ end
+
+ it_behaves_like :prime_each, :each
+
+ it "resets the enumerator with each call" do
+ @object.each { |prime| break if prime > 10 }
+ @object.each { |prime| break prime }.should == 2
end
end
diff --git a/spec/ruby/library/prime/instance_spec.rb b/spec/ruby/library/prime/instance_spec.rb
index 82f21913b7..5183f36901 100644
--- a/spec/ruby/library/prime/instance_spec.rb
+++ b/spec/ruby/library/prime/instance_spec.rb
@@ -1,24 +1,21 @@
require_relative '../../spec_helper'
+require 'prime'
-ruby_version_is ""..."3.1" do
- require 'prime'
-
- describe "Prime.instance" do
- it "returns a object representing the set of prime numbers" do
- Prime.instance.should be_kind_of(Prime)
- end
+describe "Prime.instance" do
+ it "returns a object representing the set of prime numbers" do
+ Prime.instance.should be_kind_of(Prime)
+ end
- it "returns a object with no obsolete features" do
- Prime.instance.should_not respond_to(:succ)
- Prime.instance.should_not respond_to(:next)
- end
+ it "returns a object with no obsolete features" do
+ Prime.instance.should_not respond_to(:succ)
+ Prime.instance.should_not respond_to(:next)
+ end
- it "does not complain anything" do
- -> { Prime.instance }.should_not complain
- end
+ it "does not complain anything" do
+ -> { Prime.instance }.should_not complain
+ end
- it "raises a ArgumentError when is called with some arguments" do
- -> { Prime.instance(1) }.should raise_error(ArgumentError)
- end
+ it "raises a ArgumentError when is called with some arguments" do
+ -> { Prime.instance(1) }.should raise_error(ArgumentError)
end
end
diff --git a/spec/ruby/library/prime/int_from_prime_division_spec.rb b/spec/ruby/library/prime/int_from_prime_division_spec.rb
index 5c881aefa1..5abb7221df 100644
--- a/spec/ruby/library/prime/int_from_prime_division_spec.rb
+++ b/spec/ruby/library/prime/int_from_prime_division_spec.rb
@@ -1,16 +1,13 @@
require_relative '../../spec_helper'
+require 'prime'
-ruby_version_is ""..."3.1" do
- require 'prime'
-
- describe "Prime.int_from_prime_division" do
- it "returns the product of the given factorization" do
- Prime.int_from_prime_division([[2,3], [3,3], [5,3], [7,3], [11,3], [13,3], [17,3]]).
- should == 2**3 * 3**3 * 5**3 * 7**3 * 11**3 * 13**3 * 17**3
- end
+describe "Prime.int_from_prime_division" do
+ it "returns the product of the given factorization" do
+ Prime.int_from_prime_division([[2,3], [3,3], [5,3], [7,3], [11,3], [13,3], [17,3]]).
+ should == 2**3 * 3**3 * 5**3 * 7**3 * 11**3 * 13**3 * 17**3
+ end
- it "returns 1 for an empty factorization" do
- Prime.int_from_prime_division([]).should == 1
- end
+ it "returns 1 for an empty factorization" do
+ Prime.int_from_prime_division([]).should == 1
end
end
diff --git a/spec/ruby/library/prime/integer/each_prime_spec.rb b/spec/ruby/library/prime/integer/each_prime_spec.rb
index 6034802e73..a71296b0df 100644
--- a/spec/ruby/library/prime/integer/each_prime_spec.rb
+++ b/spec/ruby/library/prime/integer/each_prime_spec.rb
@@ -1,16 +1,13 @@
require_relative '../../../spec_helper'
+require 'prime'
-ruby_version_is ""..."3.1" do
- require 'prime'
-
- describe "Integer.each_prime" do
- it "is transferred to Prime.each" do
- Prime.should_receive(:each).with(100).and_yield(2).and_yield(3).and_yield(5)
- yielded = []
- Integer.each_prime(100) do |prime|
- yielded << prime
- end
- yielded.should == [2,3,5]
+describe "Integer.each_prime" do
+ it "is transferred to Prime.each" do
+ Prime.should_receive(:each).with(100).and_yield(2).and_yield(3).and_yield(5)
+ yielded = []
+ Integer.each_prime(100) do |prime|
+ yielded << prime
end
+ yielded.should == [2,3,5]
end
end
diff --git a/spec/ruby/library/prime/integer/from_prime_division_spec.rb b/spec/ruby/library/prime/integer/from_prime_division_spec.rb
index 5422bc651c..e0e74fb336 100644
--- a/spec/ruby/library/prime/integer/from_prime_division_spec.rb
+++ b/spec/ruby/library/prime/integer/from_prime_division_spec.rb
@@ -1,16 +1,13 @@
require_relative '../../../spec_helper'
+require 'prime'
-ruby_version_is ""..."3.1" do
- require 'prime'
-
- describe "Integer.from_prime_division" do
- it "returns the product of the given factorization" do
- Integer.from_prime_division([[2,3], [3,3], [5,3], [7,3], [11,3], [13,3], [17,3]]).
- should == 2**3 * 3**3 * 5**3 * 7**3 * 11**3 * 13**3 * 17**3
- end
+describe "Integer.from_prime_division" do
+ it "returns the product of the given factorization" do
+ Integer.from_prime_division([[2,3], [3,3], [5,3], [7,3], [11,3], [13,3], [17,3]]).
+ should == 2**3 * 3**3 * 5**3 * 7**3 * 11**3 * 13**3 * 17**3
+ end
- it "returns 1 for an empty factorization" do
- Integer.from_prime_division([]).should == 1
- end
+ it "returns 1 for an empty factorization" do
+ Integer.from_prime_division([]).should == 1
end
end
diff --git a/spec/ruby/library/prime/integer/prime_division_spec.rb b/spec/ruby/library/prime/integer/prime_division_spec.rb
index 03be0be27b..be03438a6f 100644
--- a/spec/ruby/library/prime/integer/prime_division_spec.rb
+++ b/spec/ruby/library/prime/integer/prime_division_spec.rb
@@ -1,22 +1,19 @@
require_relative '../../../spec_helper'
+require 'prime'
-ruby_version_is ""..."3.1" do
- require 'prime'
-
- describe "Integer#prime_division" do
- it "returns an array of a prime factor and a corresponding exponent" do
- (2*3*5*7*11*13*17).prime_division.should ==
- [[2,1], [3,1], [5,1], [7,1], [11,1], [13,1], [17,1]]
- end
+describe "Integer#prime_division" do
+ it "returns an array of a prime factor and a corresponding exponent" do
+ (2*3*5*7*11*13*17).prime_division.should ==
+ [[2,1], [3,1], [5,1], [7,1], [11,1], [13,1], [17,1]]
+ end
- it "returns an empty array for 1" do
- 1.prime_division.should == []
- end
- it "returns an empty array for -1" do
- -1.prime_division.should == [[-1, 1]]
- end
- it "raises ZeroDivisionError for 0" do
- -> { 0.prime_division }.should raise_error(ZeroDivisionError)
- end
+ it "returns an empty array for 1" do
+ 1.prime_division.should == []
+ end
+ it "returns an empty array for -1" do
+ -1.prime_division.should == [[-1, 1]]
+ end
+ it "raises ZeroDivisionError for 0" do
+ -> { 0.prime_division }.should raise_error(ZeroDivisionError)
end
end
diff --git a/spec/ruby/library/prime/integer/prime_spec.rb b/spec/ruby/library/prime/integer/prime_spec.rb
index 65b779e319..53de76d5ab 100644
--- a/spec/ruby/library/prime/integer/prime_spec.rb
+++ b/spec/ruby/library/prime/integer/prime_spec.rb
@@ -1,20 +1,17 @@
require_relative '../../../spec_helper'
+require 'prime'
-ruby_version_is ""..."3.1" do
- require 'prime'
-
- describe "Integer#prime?" do
- it "returns a true value for prime numbers" do
- 2.prime?.should be_true
- 3.prime?.should be_true
- (2**31-1).prime?.should be_true # 8th Mersenne prime (M8)
- end
+describe "Integer#prime?" do
+ it "returns a true value for prime numbers" do
+ 2.prime?.should be_true
+ 3.prime?.should be_true
+ (2**31-1).prime?.should be_true # 8th Mersenne prime (M8)
+ end
- it "returns a false value for composite numbers" do
- 4.prime?.should be_false
- 15.prime?.should be_false
- (2**32-1).prime?.should be_false
- ( (2**17-1)*(2**19-1) ).prime?.should be_false # M6*M7
- end
+ it "returns a false value for composite numbers" do
+ 4.prime?.should be_false
+ 15.prime?.should be_false
+ (2**32-1).prime?.should be_false
+ ( (2**17-1)*(2**19-1) ).prime?.should be_false # M6*M7
end
end
diff --git a/spec/ruby/library/prime/next_spec.rb b/spec/ruby/library/prime/next_spec.rb
index 8e805ed044..39c4ae16ae 100644
--- a/spec/ruby/library/prime/next_spec.rb
+++ b/spec/ruby/library/prime/next_spec.rb
@@ -1,10 +1,7 @@
require_relative '../../spec_helper'
+require_relative 'shared/next'
+require 'prime'
-ruby_version_is ""..."3.1" do
- require_relative 'shared/next'
- require 'prime'
-
- describe "Prime#next" do
- it_behaves_like :prime_next, :next
- end
+describe "Prime#next" do
+ it_behaves_like :prime_next, :next
end
diff --git a/spec/ruby/library/prime/prime_division_spec.rb b/spec/ruby/library/prime/prime_division_spec.rb
index 4c93d5936a..6293478f59 100644
--- a/spec/ruby/library/prime/prime_division_spec.rb
+++ b/spec/ruby/library/prime/prime_division_spec.rb
@@ -1,28 +1,25 @@
require_relative '../../spec_helper'
+require 'prime'
-ruby_version_is ""..."3.1" do
- require 'prime'
-
- describe "Prime.prime_division" do
- it "returns an array of a prime factor and a corresponding exponent" do
- Prime.prime_division(2*3*5*7*11*13*17).should ==
- [[2,1], [3,1], [5,1], [7,1], [11,1], [13,1], [17,1]]
- end
+describe "Prime.prime_division" do
+ it "returns an array of a prime factor and a corresponding exponent" do
+ Prime.prime_division(2*3*5*7*11*13*17).should ==
+ [[2,1], [3,1], [5,1], [7,1], [11,1], [13,1], [17,1]]
+ end
- it "returns an empty array for 1" do
- Prime.prime_division(1).should == []
- end
+ it "returns an empty array for 1" do
+ Prime.prime_division(1).should == []
+ end
- it "returns [[-1, 1]] for -1" do
- Prime.prime_division(-1).should == [[-1, 1]]
- end
+ it "returns [[-1, 1]] for -1" do
+ Prime.prime_division(-1).should == [[-1, 1]]
+ end
- it "includes [[-1, 1]] in the divisors of a negative number" do
- Prime.prime_division(-10).should include([-1, 1])
- end
+ it "includes [[-1, 1]] in the divisors of a negative number" do
+ Prime.prime_division(-10).should include([-1, 1])
+ end
- it "raises ZeroDivisionError for 0" do
- -> { Prime.prime_division(0) }.should raise_error(ZeroDivisionError)
- end
+ it "raises ZeroDivisionError for 0" do
+ -> { Prime.prime_division(0) }.should raise_error(ZeroDivisionError)
end
end
diff --git a/spec/ruby/library/prime/prime_spec.rb b/spec/ruby/library/prime/prime_spec.rb
index e2afddd5b2..0896c7f0f3 100644
--- a/spec/ruby/library/prime/prime_spec.rb
+++ b/spec/ruby/library/prime/prime_spec.rb
@@ -1,20 +1,17 @@
require_relative '../../spec_helper'
+require 'prime'
-ruby_version_is ""..."3.1" do
- require 'prime'
-
- describe "Prime#prime?" do
- it "returns a true value for prime numbers" do
- Prime.prime?(2).should be_true
- Prime.prime?(3).should be_true
- Prime.prime?(2**31-1).should be_true # 8th Mersenne prime (M8)
- end
+describe "Prime#prime?" do
+ it "returns a true value for prime numbers" do
+ Prime.prime?(2).should be_true
+ Prime.prime?(3).should be_true
+ Prime.prime?(2**31-1).should be_true # 8th Mersenne prime (M8)
+ end
- it "returns a false value for composite numbers" do
- Prime.prime?(4).should be_false
- Prime.prime?(15).should be_false
- Prime.prime?(2**32-1).should be_false
- Prime.prime?( (2**17-1)*(2**19-1) ).should be_false # M6*M7
- end
+ it "returns a false value for composite numbers" do
+ Prime.prime?(4).should be_false
+ Prime.prime?(15).should be_false
+ Prime.prime?(2**32-1).should be_false
+ Prime.prime?( (2**17-1)*(2**19-1) ).should be_false # M6*M7
end
end
diff --git a/spec/ruby/library/prime/succ_spec.rb b/spec/ruby/library/prime/succ_spec.rb
index 9843dae25d..34c18d2ba0 100644
--- a/spec/ruby/library/prime/succ_spec.rb
+++ b/spec/ruby/library/prime/succ_spec.rb
@@ -1,10 +1,7 @@
require_relative '../../spec_helper'
+require_relative 'shared/next'
+require 'prime'
-ruby_version_is ""..."3.1" do
- require_relative 'shared/next'
- require 'prime'
-
- describe "Prime#succ" do
- it_behaves_like :prime_next, :succ
- end
+describe "Prime#succ" do
+ it_behaves_like :prime_next, :succ
end
diff --git a/spec/ruby/library/rexml/attribute/clone_spec.rb b/spec/ruby/library/rexml/attribute/clone_spec.rb
deleted file mode 100644
index 5c86468d45..0000000000
--- a/spec/ruby/library/rexml/attribute/clone_spec.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attribute#clone" do
- it "returns a copy of this Attribute" do
- orig = REXML::Attribute.new("name", "value&&")
- orig.should == orig.clone
- orig.clone.to_s.should == orig.to_s
- orig.clone.to_string.should == orig.to_string
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attribute/element_spec.rb b/spec/ruby/library/rexml/attribute/element_spec.rb
deleted file mode 100644
index 0e4ce46a4f..0000000000
--- a/spec/ruby/library/rexml/attribute/element_spec.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attribute#element" do
- it "returns the parent element" do
- e = REXML::Element.new "root"
-
- REXML::Attribute.new("name", "value", e).element.should == e
- REXML::Attribute.new("name", "default_constructor").element.should == nil
- end
- end
-
- describe "REXML::Attribute#element=" do
- it "sets the parent element" do
- e = REXML::Element.new "root"
- f = REXML::Element.new "temp"
- a = REXML::Attribute.new("name", "value", e)
- a.element.should == e
-
- a.element = f
- a.element.should == f
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attribute/equal_value_spec.rb b/spec/ruby/library/rexml/attribute/equal_value_spec.rb
deleted file mode 100644
index 1498bae624..0000000000
--- a/spec/ruby/library/rexml/attribute/equal_value_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attribute#==" do
- it "returns true if other has equal name and value" do
- a1 = REXML::Attribute.new("foo", "bar")
- a1.should == a1.clone
-
- a2 = REXML::Attribute.new("foo", "bar")
- a1.should == a2
-
- a3 = REXML::Attribute.new("foo", "bla")
- a1.should_not == a3
-
- a4 = REXML::Attribute.new("baz", "bar")
- a1.should_not == a4
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attribute/hash_spec.rb b/spec/ruby/library/rexml/attribute/hash_spec.rb
deleted file mode 100644
index 7e0cbcc1ea..0000000000
--- a/spec/ruby/library/rexml/attribute/hash_spec.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attribute#hash" do
- # These are not really complete, any idea on how to make them more
- # "testable" will be appreciated.
- it "returns a hashcode made of the name and value of self" do
- a = REXML::Attribute.new("name", "value")
- a.hash.should be_kind_of(Numeric)
- b = REXML::Attribute.new(a)
- a.hash.should == b.hash
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attribute/initialize_spec.rb b/spec/ruby/library/rexml/attribute/initialize_spec.rb
deleted file mode 100644
index 35b87b0733..0000000000
--- a/spec/ruby/library/rexml/attribute/initialize_spec.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attribute#initialize" do
- before :each do
- @e = REXML::Element.new "root"
- @name = REXML::Attribute.new("name", "Nicko")
- @e.add_attribute @name
- end
-
- it "receives two strings for name and value" do
- @e.attributes["name"].should == "Nicko"
- @e.add_attribute REXML::Attribute.new("last_name", nil)
- @e.attributes["last_name"].should == ""
- end
-
- it "receives an Attribute and clones it" do
- copy = REXML::Attribute.new(@name)
- copy.should == @name
- end
-
- it "receives a parent node" do
- last_name = REXML::Attribute.new("last_name", "McBrain", @e)
- last_name.element.should == @e
-
- last_name = REXML::Attribute.new(@name, @e)
- last_name.element.should == @e
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attribute/inspect_spec.rb b/spec/ruby/library/rexml/attribute/inspect_spec.rb
deleted file mode 100644
index ee5236b98e..0000000000
--- a/spec/ruby/library/rexml/attribute/inspect_spec.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attribute#inspect" do
- it "returns the name and value as a string" do
- a = REXML::Attribute.new("my_name", "my_value")
- a.inspect.should == "my_name='my_value'"
- end
-
- it "accepts attributes with no value" do
- a = REXML::Attribute.new("my_name")
- a.inspect.should == "my_name=''"
- end
-
- it "does not escape text" do
- a = REXML::Attribute.new("name", "<>")
- a.inspect.should == "name='<>'"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attribute/namespace_spec.rb b/spec/ruby/library/rexml/attribute/namespace_spec.rb
deleted file mode 100644
index 645b3cd1b1..0000000000
--- a/spec/ruby/library/rexml/attribute/namespace_spec.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attribute#namespace" do
- it "returns the namespace url" do
- e = REXML::Element.new("root")
- e.add_attribute REXML::Attribute.new("xmlns:ns", "http://some_uri")
- e.namespace("ns").should == "http://some_uri"
- end
-
- it "returns nil if namespace is not defined" do
- e = REXML::Element.new("root")
- e.add_attribute REXML::Attribute.new("test", "value")
- e.namespace("test").should == nil
- e.namespace("ns").should == nil
- end
-
- it "defaults arg to nil" do
- e = REXML::Element.new("root")
- e.add_attribute REXML::Attribute.new("xmlns:ns", "http://some_uri")
- e.namespace.should == ""
- e.namespace("ns").should == "http://some_uri"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attribute/node_type_spec.rb b/spec/ruby/library/rexml/attribute/node_type_spec.rb
deleted file mode 100644
index da055ae8f0..0000000000
--- a/spec/ruby/library/rexml/attribute/node_type_spec.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attribute#node_type" do
- it "always returns :attribute" do
- attr = REXML::Attribute.new("foo", "bar")
- attr.node_type.should == :attribute
- REXML::Attribute.new(attr).node_type.should == :attribute
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attribute/prefix_spec.rb b/spec/ruby/library/rexml/attribute/prefix_spec.rb
deleted file mode 100644
index 87bff4822b..0000000000
--- a/spec/ruby/library/rexml/attribute/prefix_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attribute#prefix" do
- it "returns the namespace of the Attribute" do
- ans = REXML::Attribute.new("ns:someattr", "some_value")
- out = REXML::Attribute.new("out:something", "some_other_value")
-
- ans.prefix.should == "ns"
- out.prefix.should == "out"
- end
-
- it "returns an empty string for Attributes with no prefixes" do
- attr = REXML::Attribute.new("foo", "bar")
-
- attr.prefix.should == ""
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attribute/remove_spec.rb b/spec/ruby/library/rexml/attribute/remove_spec.rb
deleted file mode 100644
index 5f928b1286..0000000000
--- a/spec/ruby/library/rexml/attribute/remove_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attribute#remove" do
- before :each do
- @e = REXML::Element.new "Root"
- @attr = REXML::Attribute.new("foo", "bar")
- end
-
- it "deletes this Attribute from parent" do
- @e.add_attribute(@attr)
- @e.attributes["foo"].should_not == nil
- @attr.remove
- @e.attributes["foo"].should == nil
- end
-
- it "does not anything if element has no parent" do
- -> {@attr.remove}.should_not raise_error(Exception)
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attribute/to_s_spec.rb b/spec/ruby/library/rexml/attribute/to_s_spec.rb
deleted file mode 100644
index e362cee8f1..0000000000
--- a/spec/ruby/library/rexml/attribute/to_s_spec.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attribute#to_s" do
- it "returns the value of the Attribute" do
- REXML::Attribute.new("name", "some_value").to_s.should == "some_value"
- end
-
- it "returns the escaped value if it was created from Attribute" do
- orig = REXML::Attribute.new("name", "<&>")
- copy = REXML::Attribute.new(orig)
- copy.to_s.should == "&lt;&amp;&gt;"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attribute/to_string_spec.rb b/spec/ruby/library/rexml/attribute/to_string_spec.rb
deleted file mode 100644
index a9d249f5bb..0000000000
--- a/spec/ruby/library/rexml/attribute/to_string_spec.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attribute#to_string" do
- it "returns the attribute as XML" do
- attr = REXML::Attribute.new("name", "value")
- attr_empty = REXML::Attribute.new("name")
- attr_ns = REXML::Attribute.new("xmlns:ns", "http://uri")
-
- attr.to_string.should == "name='value'"
- attr_empty.to_string.should == "name=''"
- attr_ns.to_string.should == "xmlns:ns='http://uri'"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attribute/value_spec.rb b/spec/ruby/library/rexml/attribute/value_spec.rb
deleted file mode 100644
index 77071f6f70..0000000000
--- a/spec/ruby/library/rexml/attribute/value_spec.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attribute#value" do
- it "returns the value of the Attribute unnormalized" do
- attr = REXML::Attribute.new("name", "value")
- attr_ents = REXML::Attribute.new("name", "<&>")
- attr_empty = REXML::Attribute.new("name")
-
- attr.value.should == "value"
- attr_ents.value.should == "<&>"
- attr_empty.value.should == ""
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attribute/write_spec.rb b/spec/ruby/library/rexml/attribute/write_spec.rb
deleted file mode 100644
index 0012b3cc77..0000000000
--- a/spec/ruby/library/rexml/attribute/write_spec.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attribute#write" do
- before :each do
- @attr = REXML::Attribute.new("name", "Charlotte")
- @s = ""
- end
-
- it "writes the name and value to output" do
- @attr.write(@s)
- @s.should == "name='Charlotte'"
- end
-
- it "currently ignores the second argument" do
- @attr.write(@s, 3)
- @s.should == "name='Charlotte'"
-
- @s = ""
- @attr.write(@s, "foo")
- @s.should == "name='Charlotte'"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attribute/xpath_spec.rb b/spec/ruby/library/rexml/attribute/xpath_spec.rb
deleted file mode 100644
index 0a09046b01..0000000000
--- a/spec/ruby/library/rexml/attribute/xpath_spec.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attribute#xpath" do
-
- before :each do
- @e = REXML::Element.new "root"
- @attr = REXML::Attribute.new("year", "1989")
- end
-
- it "returns the path for Attribute" do
- @e.add_attribute @attr
- @attr.xpath.should == "root/@year"
- end
-
- it "raises an error if attribute has no parent" do
- -> { @attr.xpath }.should raise_error(Exception)
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attributes/add_spec.rb b/spec/ruby/library/rexml/attributes/add_spec.rb
deleted file mode 100644
index e24e9fabbc..0000000000
--- a/spec/ruby/library/rexml/attributes/add_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require_relative 'shared/add'
- require 'rexml/document'
-
- describe "REXML::Attributes#add" do
- it_behaves_like :rexml_attribute_add, :add
- end
-end
diff --git a/spec/ruby/library/rexml/attributes/append_spec.rb b/spec/ruby/library/rexml/attributes/append_spec.rb
deleted file mode 100644
index f96a727f47..0000000000
--- a/spec/ruby/library/rexml/attributes/append_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require_relative 'shared/add'
- require 'rexml/document'
-
- describe "REXML::Attributes#<<" do
- it_behaves_like :rexml_attribute_add, :<<
- end
-end
diff --git a/spec/ruby/library/rexml/attributes/delete_all_spec.rb b/spec/ruby/library/rexml/attributes/delete_all_spec.rb
deleted file mode 100644
index 707baa235b..0000000000
--- a/spec/ruby/library/rexml/attributes/delete_all_spec.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attributes#delete_all" do
- before :each do
- @e = REXML::Element.new("root")
- end
-
- it "deletes all attributes that match name" do
- uri = REXML::Attribute.new("uri", "http://something")
- @e.attributes << uri
- @e.attributes.delete_all("uri")
- @e.attributes.should be_empty
- @e.attributes["uri"].should == nil
- end
-
- it "deletes all attributes that match name with a namespace" do
- ns_uri = REXML::Attribute.new("xmlns:uri", "http://something_here_too")
- @e.attributes << ns_uri
- @e.attributes.delete_all("xmlns:uri")
- @e.attributes.should be_empty
- @e.attributes["xmlns:uri"].should == nil
- end
-
- it "returns the removed attribute" do
- uri = REXML::Attribute.new("uri", "http://something_here_too")
- @e.attributes << uri
- attrs = @e.attributes.delete_all("uri")
- attrs.first.should == uri
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attributes/delete_spec.rb b/spec/ruby/library/rexml/attributes/delete_spec.rb
deleted file mode 100644
index 723fa70751..0000000000
--- a/spec/ruby/library/rexml/attributes/delete_spec.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attributes#delete" do
- before :each do
- @e = REXML::Element.new("root")
- @name = REXML::Attribute.new("name", "Pepe")
- end
-
- it "takes an attribute name and deletes the attribute" do
- @e.attributes.delete("name")
- @e.attributes["name"].should be_nil
- @e.attributes.should be_empty
- end
-
- it "takes an Attribute and deletes it" do
- @e.attributes.delete(@name)
- @e.attributes["name"].should be_nil
- @e.attributes.should be_empty
- end
-
- it "returns the element with the attribute removed" do
- ret_val = @e.attributes.delete(@name)
- ret_val.should == @e
- ret_val.attributes.should be_empty
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attributes/each_attribute_spec.rb b/spec/ruby/library/rexml/attributes/each_attribute_spec.rb
deleted file mode 100644
index 692cf4f943..0000000000
--- a/spec/ruby/library/rexml/attributes/each_attribute_spec.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attributes#each_attribute" do
- it "iterates over the attributes yielding actual Attribute objects" do
- e = REXML::Element.new("root")
- name = REXML::Attribute.new("name", "Joe")
- ns_uri = REXML::Attribute.new("xmlns:ns", "http://some_uri")
- e.add_attribute name
- e.add_attribute ns_uri
-
- attributes = []
-
- e.attributes.each_attribute do |attr|
- attributes << attr
- end
-
- attributes = attributes.sort_by {|a| a.name }
- attributes.first.should == name
- attributes.last.should == ns_uri
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attributes/each_spec.rb b/spec/ruby/library/rexml/attributes/each_spec.rb
deleted file mode 100644
index 49add3b77b..0000000000
--- a/spec/ruby/library/rexml/attributes/each_spec.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attributes#each" do
- before :each do
- @e = REXML::Element.new("root")
- @name = REXML::Attribute.new("name", "Joe")
- @ns_uri = REXML::Attribute.new("xmlns:ns", "http://some_uri")
- @e.add_attribute @name
- @e.add_attribute @ns_uri
- end
-
- it "iterates over the attributes yielding expanded-name/value" do
- attributes = []
- @e.attributes.each do |attr|
- attr.should be_kind_of(Array)
- attributes << attr
- end
- attributes = attributes.sort_by {|a| a.first }
- attributes.first.should == ["name", "Joe"]
- attributes.last.should == ["xmlns:ns", "http://some_uri"]
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attributes/element_reference_spec.rb b/spec/ruby/library/rexml/attributes/element_reference_spec.rb
deleted file mode 100644
index 0d089eaab2..0000000000
--- a/spec/ruby/library/rexml/attributes/element_reference_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attributes#[]" do
- before :each do
- @e = REXML::Element.new("root")
- @lang = REXML::Attribute.new("language", "english")
- @e.attributes << @lang
- end
-
- it "returns the value of an attribute" do
- @e.attributes["language"].should == "english"
- end
-
- it "returns nil if the attribute does not exist" do
- @e.attributes["chunky bacon"].should == nil
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attributes/element_set_spec.rb b/spec/ruby/library/rexml/attributes/element_set_spec.rb
deleted file mode 100644
index 834ad682a6..0000000000
--- a/spec/ruby/library/rexml/attributes/element_set_spec.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attributes#[]=" do
- before :each do
- @e = REXML::Element.new("song")
- @name = REXML::Attribute.new("name", "Holy Smoke!")
- @e.attributes << @name
- end
-
- it "sets an attribute" do
- @e.attributes["author"] = "_why's foxes"
- @e.attributes["author"].should == "_why's foxes"
- end
-
- it "overwrites an existing attribute" do
- @e.attributes["name"] = "Chunky Bacon"
- @e.attributes["name"].should == "Chunky Bacon"
- end
-
- it "deletes an attribute is value is nil" do
- @e.attributes["name"] = nil
- @e.attributes.length.should == 0
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attributes/get_attribute_ns_spec.rb b/spec/ruby/library/rexml/attributes/get_attribute_ns_spec.rb
deleted file mode 100644
index 1109ff519c..0000000000
--- a/spec/ruby/library/rexml/attributes/get_attribute_ns_spec.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attributes#get_attribute_ns" do
- it "returns an attribute by name and namespace" do
- e = REXML::Element.new("root")
- attr = REXML::Attribute.new("xmlns:ns", "http://some_url")
- e.attributes << attr
- attr.prefix.should == "xmlns"
- # This might be a bug in Attribute, commenting until those specs
- # are ready
- # e.attributes.get_attribute_ns(attr.prefix, "name").should == "http://some_url"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attributes/get_attribute_spec.rb b/spec/ruby/library/rexml/attributes/get_attribute_spec.rb
deleted file mode 100644
index cc94191729..0000000000
--- a/spec/ruby/library/rexml/attributes/get_attribute_spec.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attributes#get_attribute" do
- before :each do
- @e = REXML::Element.new("root")
- @name = REXML::Attribute.new("name", "Dave")
- @e.attributes << @name
- end
-
- it "fetches an attributes" do
- @e.attributes.get_attribute("name").should == @name
- end
-
- it "fetches an namespaced attribute" do
- ns_name = REXML::Attribute.new("im:name", "Murray")
- @e.attributes << ns_name
- @e.attributes.get_attribute("name").should == @name
- @e.attributes.get_attribute("im:name").should == ns_name
- end
-
- it "returns an Attribute" do
- @e.attributes.get_attribute("name").should be_kind_of(REXML::Attribute)
- end
-
- it "returns nil if it attribute does not exist" do
- @e.attributes.get_attribute("fake").should be_nil
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attributes/initialize_spec.rb b/spec/ruby/library/rexml/attributes/initialize_spec.rb
deleted file mode 100644
index 42ec742e60..0000000000
--- a/spec/ruby/library/rexml/attributes/initialize_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attributes#initialize" do
- it "is auto initialized by Element" do
- e = REXML::Element.new "root"
- e.attributes.should be_kind_of(REXML::Attributes)
-
- e.attributes << REXML::Attribute.new("name", "Paul")
- e.attributes["name"].should == "Paul"
- end
-
- it "receives a parent node" do
- e = REXML::Element.new "root"
- e.attributes << REXML::Attribute.new("name", "Vic")
- e.attributes["name"].should == "Vic"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attributes/length_spec.rb b/spec/ruby/library/rexml/attributes/length_spec.rb
deleted file mode 100644
index 81733b4a96..0000000000
--- a/spec/ruby/library/rexml/attributes/length_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require_relative 'shared/length'
- require 'rexml/document'
-
- describe "REXML::Attributes#length" do
- it_behaves_like :rexml_attribute_length, :length
- end
-end
diff --git a/spec/ruby/library/rexml/attributes/namespaces_spec.rb b/spec/ruby/library/rexml/attributes/namespaces_spec.rb
deleted file mode 100644
index b88346854f..0000000000
--- a/spec/ruby/library/rexml/attributes/namespaces_spec.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attributes#namespaces" do
- it "needs to be reviewed for spec completeness"
- end
-end
diff --git a/spec/ruby/library/rexml/attributes/prefixes_spec.rb b/spec/ruby/library/rexml/attributes/prefixes_spec.rb
deleted file mode 100644
index 574b7ffbaf..0000000000
--- a/spec/ruby/library/rexml/attributes/prefixes_spec.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attributes#prefixes" do
- before :each do
- @e = REXML::Element.new("root")
- a1 = REXML::Attribute.new("xmlns:a", "bar")
- a2 = REXML::Attribute.new("xmlns:b", "bla")
- a3 = REXML::Attribute.new("xmlns:c", "baz")
- @e.attributes << a1
- @e.attributes << a2
- @e.attributes << a3
-
- @e.attributes << REXML::Attribute.new("xmlns", "foo")
- end
-
- it "returns an array with the prefixes of each attribute" do
- @e.attributes.prefixes.sort.should == ["a", "b", "c"]
- end
-
- it "does not include the default namespace" do
- @e.attributes.prefixes.include?("xmlns").should == false
- end
- end
-end
diff --git a/spec/ruby/library/rexml/attributes/shared/add.rb b/spec/ruby/library/rexml/attributes/shared/add.rb
deleted file mode 100644
index 872f149f45..0000000000
--- a/spec/ruby/library/rexml/attributes/shared/add.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-describe :rexml_attribute_add, shared: true do
- before :each do
- @e = REXML::Element.new("root")
- @attr = REXML::Attributes.new(@e)
- @name = REXML::Attribute.new("name", "Joe")
- end
-
- it "adds an attribute" do
- @attr.send(@method, @name)
- @attr["name"].should == "Joe"
- end
-
- it "replaces an existing attribute" do
- @attr.send(@method, REXML::Attribute.new("name", "Bruce"))
- @attr["name"].should == "Bruce"
- end
-end
diff --git a/spec/ruby/library/rexml/attributes/shared/length.rb b/spec/ruby/library/rexml/attributes/shared/length.rb
deleted file mode 100644
index 7848f9bf33..0000000000
--- a/spec/ruby/library/rexml/attributes/shared/length.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'rexml/document'
-
-describe :rexml_attribute_length, shared: true do
- it "returns the number of attributes" do
- e = REXML::Element.new("root")
- e.attributes.send(@method).should == 0
-
- e.attributes << REXML::Attribute.new("name", "John")
- e.attributes << REXML::Attribute.new("another_name", "Leo")
- e.attributes.send(@method).should == 2
- end
-end
diff --git a/spec/ruby/library/rexml/attributes/size_spec.rb b/spec/ruby/library/rexml/attributes/size_spec.rb
deleted file mode 100644
index 13ef08f644..0000000000
--- a/spec/ruby/library/rexml/attributes/size_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require_relative 'shared/length'
- require 'rexml/document'
-
- describe "REXML::Attributes#size" do
- it_behaves_like :rexml_attribute_length, :size
- end
-end
diff --git a/spec/ruby/library/rexml/attributes/to_a_spec.rb b/spec/ruby/library/rexml/attributes/to_a_spec.rb
deleted file mode 100644
index 902cd86a29..0000000000
--- a/spec/ruby/library/rexml/attributes/to_a_spec.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Attributes#to_a" do
- it "returns an array with the attributes" do
- e = REXML::Element.new("root")
- name = REXML::Attribute.new("name", "Dave")
- last = REXML::Attribute.new("last_name", "Murray")
-
- e.attributes << name
- e.attributes << last
-
- e.attributes.to_a.sort{|a,b|a.to_s<=>b.to_s}.should == [name, last]
- end
-
- it "returns an empty array if it has no attributes" do
- REXML::Element.new("root").attributes.to_a.should == []
- end
- end
-end
diff --git a/spec/ruby/library/rexml/cdata/clone_spec.rb b/spec/ruby/library/rexml/cdata/clone_spec.rb
deleted file mode 100644
index abe1a0b062..0000000000
--- a/spec/ruby/library/rexml/cdata/clone_spec.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::CData#clone" do
- it "makes a copy of itself" do
- c = REXML::CData.new("some text")
- c.clone.to_s.should == c.to_s
- c.clone.should == c
- end
- end
-end
diff --git a/spec/ruby/library/rexml/cdata/initialize_spec.rb b/spec/ruby/library/rexml/cdata/initialize_spec.rb
deleted file mode 100644
index 1393d97f4a..0000000000
--- a/spec/ruby/library/rexml/cdata/initialize_spec.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::CData#initialize" do
- it "creates a new CData object" do
- c = REXML::CData.new("some text")
- c.should be_kind_of(REXML::CData)
- c.should be_kind_of(REXML::Text)
- end
-
- it "respects whitespace if whitespace is true" do
- c = REXML::CData.new("whitespace test", true)
- c1 = REXML::CData.new("whitespace test", false)
-
- c.to_s.should == "whitespace test"
- c1.to_s.should == "whitespace test"
- end
-
- it "receives parent as third argument" do
- e = REXML::Element.new("root")
- REXML::CData.new("test", true, e)
- e.to_s.should == "<root><![CDATA[test]]></root>"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/cdata/shared/to_s.rb b/spec/ruby/library/rexml/cdata/shared/to_s.rb
deleted file mode 100644
index f8c4951c95..0000000000
--- a/spec/ruby/library/rexml/cdata/shared/to_s.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-describe :rexml_cdata_to_s, shared: true do
- it "returns the contents of the CData" do
- c = REXML::CData.new("some text")
- c.send(@method).should == "some text"
- end
-
- it "does not escape text" do
- c1 = REXML::CData.new("some& text\n")
- c1.send(@method).should == "some& text\n"
- end
-end
diff --git a/spec/ruby/library/rexml/cdata/to_s_spec.rb b/spec/ruby/library/rexml/cdata/to_s_spec.rb
deleted file mode 100644
index a5c061f116..0000000000
--- a/spec/ruby/library/rexml/cdata/to_s_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require_relative 'shared/to_s'
- require 'rexml/document'
-
- describe "REXML::CData#to_s" do
- it_behaves_like :rexml_cdata_to_s, :to_s
- end
-end
diff --git a/spec/ruby/library/rexml/cdata/value_spec.rb b/spec/ruby/library/rexml/cdata/value_spec.rb
deleted file mode 100644
index 9f36226976..0000000000
--- a/spec/ruby/library/rexml/cdata/value_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require_relative 'shared/to_s'
- require 'rexml/document'
-
- describe "REXML::CData#value" do
- it_behaves_like :rexml_cdata_to_s, :value
- end
-end
diff --git a/spec/ruby/library/rexml/document/add_element_spec.rb b/spec/ruby/library/rexml/document/add_element_spec.rb
deleted file mode 100644
index 29dec0b24e..0000000000
--- a/spec/ruby/library/rexml/document/add_element_spec.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Document#add_element" do
- it "adds arg1 with attributes arg2 as root node" do
- d = REXML::Document.new
- e = REXML::Element.new("root")
- d.add_element e
- d.root.should == e
- end
-
- it "sets arg2 as arg1's attributes" do
- d = REXML::Document.new
- e = REXML::Element.new("root")
- attr = {"foo" => "bar"}
- d.add_element(e,attr)
- d.root.attributes["foo"].should == attr["foo"]
- end
-
- it "accepts a node name as arg1 and adds it as root" do
- d = REXML::Document.new
- d.add_element "foo"
- d.root.name.should == "foo"
- end
-
- it "sets arg1's context to the root's context" do
- d = REXML::Document.new("", {"foo" => "bar"})
- d.add_element "foo"
- d.root.context.should == d.context
- end
- end
-end
diff --git a/spec/ruby/library/rexml/document/add_spec.rb b/spec/ruby/library/rexml/document/add_spec.rb
deleted file mode 100644
index 8666d3dbf9..0000000000
--- a/spec/ruby/library/rexml/document/add_spec.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- # This spec defines Document#add and Document#<<
-
- describe :rexml_document_add, shared: true do
- before :each do
- @doc = REXML::Document.new("<root/>")
- @decl = REXML::XMLDecl.new("1.0")
- end
-
- it "sets document's XML declaration" do
- @doc.send(@method, @decl)
- @doc.xml_decl.should == @decl
- end
-
- it "inserts XML declaration as first node" do
- @doc.send(@method, @decl)
- @doc.children[0].version.should == "1.0"
- end
-
- it "overwrites existing XML declaration" do
- @doc.send(@method, @decl)
- @doc.send(@method, REXML::XMLDecl.new("2.0"))
- @doc.xml_decl.version.should == "2.0"
- end
-
- it "sets document DocType" do
- @doc.send(@method, REXML::DocType.new("transitional"))
- @doc.doctype.name.should == "transitional"
- end
-
- it "overwrites existing DocType" do
- @doc.send(@method, REXML::DocType.new("transitional"))
- @doc.send(@method, REXML::DocType.new("strict"))
- @doc.doctype.name.should == "strict"
- end
-
- it "adds root node unless it exists" do
- d = REXML::Document.new("")
- elem = REXML::Element.new "root"
- d.send(@method, elem)
- d.root.should == elem
- end
-
- it "refuses to add second root" do
- -> { @doc.send(@method, REXML::Element.new("foo")) }.should raise_error(RuntimeError)
- end
- end
-
- describe "REXML::Document#add" do
- it_behaves_like :rexml_document_add, :add
- end
-
- describe "REXML::Document#<<" do
- it_behaves_like :rexml_document_add, :<<
- end
-end
diff --git a/spec/ruby/library/rexml/document/clone_spec.rb b/spec/ruby/library/rexml/document/clone_spec.rb
deleted file mode 100644
index 137fe8a073..0000000000
--- a/spec/ruby/library/rexml/document/clone_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- # According to the MRI documentation (http://www.ruby-doc.org/stdlib/libdoc/rexml/rdoc/index.html),
- # clone's behavior "should be obvious". Apparently "obvious" means cloning
- # only the attributes and the context of the document, not its children.
- describe "REXML::Document#clone" do
- it "clones document attributes" do
- d = REXML::Document.new("foo")
- d.attributes["foo"] = "bar"
- e = d.clone
- e.attributes.should == d.attributes
- end
-
- it "clones document context" do
- d = REXML::Document.new("foo", {"foo" => "bar"})
- e = d.clone
- e.context.should == d.context
- end
- end
-end
diff --git a/spec/ruby/library/rexml/document/doctype_spec.rb b/spec/ruby/library/rexml/document/doctype_spec.rb
deleted file mode 100644
index e1b7ba4916..0000000000
--- a/spec/ruby/library/rexml/document/doctype_spec.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Document#doctype" do
- it "returns the doctype" do
- d = REXML::Document.new
- dt = REXML::DocType.new("foo")
- d.add dt
- d.doctype.should == dt
- end
-
- it "returns nil if there's no doctype" do
- REXML::Document.new.doctype.should == nil
- end
- end
-end
diff --git a/spec/ruby/library/rexml/document/encoding_spec.rb b/spec/ruby/library/rexml/document/encoding_spec.rb
deleted file mode 100644
index 2cc947f06a..0000000000
--- a/spec/ruby/library/rexml/document/encoding_spec.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Document#encoding" do
- before :each do
- @doc = REXML::Document.new
- end
-
- it "returns encoding from XML declaration" do
- @doc.add REXML::XMLDecl.new(nil, "UTF-16", nil)
- @doc.encoding.should == "UTF-16"
- end
-
- it "returns encoding from XML declaration (for UTF-16 as well)" do
- @doc.add REXML::XMLDecl.new("1.0", "UTF-8", nil)
- @doc.encoding.should == "UTF-8"
- end
-
- it "uses UTF-8 as default encoding" do
- @doc.encoding.should == "UTF-8"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/document/expanded_name_spec.rb b/spec/ruby/library/rexml/document/expanded_name_spec.rb
deleted file mode 100644
index 9d1025b5e0..0000000000
--- a/spec/ruby/library/rexml/document/expanded_name_spec.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe :document_expanded_name, shared: true do
- it "returns an empty string for root" do # root nodes have no expanded name
- REXML::Document.new.send(@method).should == ""
- end
- end
-
- describe "REXML::Document#expanded_name" do
- it_behaves_like :document_expanded_name, :expanded_name
- end
-
- describe "REXML::Document#name" do
- it_behaves_like :document_expanded_name, :name
- end
-end
diff --git a/spec/ruby/library/rexml/document/new_spec.rb b/spec/ruby/library/rexml/document/new_spec.rb
deleted file mode 100644
index 4e24b6f5a1..0000000000
--- a/spec/ruby/library/rexml/document/new_spec.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Document#new" do
-
- it "initializes context of {} unless specified" do
- d = REXML::Document.new("<foo />")
- d.context.should == {}
- end
-
- it "has empty attributes if source is nil" do
- d = REXML::Document.new(nil)
- d.elements.should be_empty
- end
-
- it "can use other document context" do
- s = REXML::Document.new("")
- d = REXML::Document.new(s)
- d.context.should == s.context
- end
-
- it "clones source attributes" do
- s = REXML::Document.new("<root />")
- s.attributes["some_attr"] = "some_val"
- d = REXML::Document.new(s)
- d.attributes.should == s.attributes
- end
-
- it "raises an error if source is not a Document, String or IO" do
- -> {REXML::Document.new(3)}.should raise_error(RuntimeError)
- end
-
- it "does not perform XML validation" do
- REXML::Document.new("Invalid document").should be_kind_of(REXML::Document)
- end
- end
-end
diff --git a/spec/ruby/library/rexml/document/node_type_spec.rb b/spec/ruby/library/rexml/document/node_type_spec.rb
deleted file mode 100644
index b6d7e7a7da..0000000000
--- a/spec/ruby/library/rexml/document/node_type_spec.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Document#node_type" do
- it "returns :document" do
- REXML::Document.new.node_type.should == :document
- end
- end
-end
diff --git a/spec/ruby/library/rexml/document/root_spec.rb b/spec/ruby/library/rexml/document/root_spec.rb
deleted file mode 100644
index 1a584a720b..0000000000
--- a/spec/ruby/library/rexml/document/root_spec.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Document#root" do
- it "returns document root tag name" do
- REXML::Document.new("<foo/>").root.name.should == "foo"
- end
-
- it "returns nil if there is not root" do
- REXML::Document.new.root.should == nil
- end
- end
-end
diff --git a/spec/ruby/library/rexml/document/stand_alone_spec.rb b/spec/ruby/library/rexml/document/stand_alone_spec.rb
deleted file mode 100644
index e1c721e782..0000000000
--- a/spec/ruby/library/rexml/document/stand_alone_spec.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Document#stand_alone?" do
- it "returns the XMLDecl standalone value" do
- d = REXML::Document.new
- decl = REXML::XMLDecl.new("1.0", "UTF-8", "yes")
- d.add decl
- d.stand_alone?.should == "yes"
- end
-
- # According to the docs this should return the default XMLDecl but that
- # will carry some more problems when printing the document. Currently, it
- # returns nil. See http://www.ruby-forum.com/topic/146812#650061
- it "returns the default value when no XML declaration present" do
- REXML::Document.new.stand_alone?.should == nil
- end
-
- end
-end
diff --git a/spec/ruby/library/rexml/document/version_spec.rb b/spec/ruby/library/rexml/document/version_spec.rb
deleted file mode 100644
index 4f6b40551b..0000000000
--- a/spec/ruby/library/rexml/document/version_spec.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Document#version" do
- it "returns XML version from declaration" do
- d = REXML::Document.new
- d.add REXML::XMLDecl.new("1.1")
- d.version.should == "1.1"
- end
-
- it "returns the default version when declaration is not present" do
- REXML::Document.new.version.should == REXML::XMLDecl::DEFAULT_VERSION
- end
- end
-end
diff --git a/spec/ruby/library/rexml/document/write_spec.rb b/spec/ruby/library/rexml/document/write_spec.rb
deleted file mode 100644
index 00c22141b3..0000000000
--- a/spec/ruby/library/rexml/document/write_spec.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
- require 'rexml/formatters/transitive'
-
- # Maybe this can be cleaned
- describe "REXML::Document#write" do
- before :each do
- @d = REXML::Document.new
- city = REXML::Element.new "Springfield"
- street = REXML::Element.new "EvergreenTerrace"
- address = REXML::Element.new "House742"
- @d << city << street << address
- @str = ""
- end
-
- it "returns document source as string" do
- @d.write(@str)
- @str.should == "<Springfield><EvergreenTerrace><House742/></EvergreenTerrace></Springfield>"
- end
-
- it "returns document indented" do
- @d.write(@str, 2)
- @str.should =~ /\s*<Springfield>\s*<EvergreenTerrace>\s*<House742\/>\s*<\/EvergreenTerrace>\s*<\/Springfield>/
- end
-
- it "returns document with transitive support" do
- @d.write(@str, 2, true)
- @str.should =~ /\s*<Springfield\s*><EvergreenTerrace\s*><House742\s*\/><\/EvergreenTerrace\s*><\/Springfield\s*>/
- end
-
- it "returns document with support for IE" do
- @d.write(@str, -1, false, true)
- @str.should == "<Springfield><EvergreenTerrace><House742 /></EvergreenTerrace></Springfield>"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/document/xml_decl_spec.rb b/spec/ruby/library/rexml/document/xml_decl_spec.rb
deleted file mode 100644
index 8ac47510b0..0000000000
--- a/spec/ruby/library/rexml/document/xml_decl_spec.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Document#xml_decl" do
- it "returns XML declaration of the document" do
- d = REXML::Document.new
- decl = REXML::XMLDecl.new("1.0", "UTF-16", "yes")
- d.add decl
- d.xml_decl.should == decl
- end
-
- it "returns default XML declaration unless present" do
- REXML::Document.new.xml_decl.should == REXML::XMLDecl.new
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/add_attribute_spec.rb b/spec/ruby/library/rexml/element/add_attribute_spec.rb
deleted file mode 100644
index 64f2ec84a3..0000000000
--- a/spec/ruby/library/rexml/element/add_attribute_spec.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#add_attribute" do
- before :each do
- @person = REXML::Element.new "person"
- @person.attributes["name"] = "Bill"
- end
-
- it "adds a new attribute" do
- @person.add_attribute("age", "17")
- @person.attributes["age"].should == "17"
- end
-
- it "overwrites an existing attribute" do
- @person.add_attribute("name", "Bill")
- @person.attributes["name"].should == "Bill"
- end
-
- it "accepts a pair of strings" do
- @person.add_attribute("male", "true")
- @person.attributes["male"].should == "true"
- end
-
- it "accepts an Attribute for key" do
- attr = REXML::Attribute.new("male", "true")
- @person.add_attribute attr
- @person.attributes["male"].should == "true"
- end
-
- it "ignores value if key is an Attribute" do
- attr = REXML::Attribute.new("male", "true")
- @person.add_attribute(attr, "false")
- @person.attributes["male"].should == "true"
- end
-
- it "returns the attribute added" do
- attr = REXML::Attribute.new("name", "Tony")
- @person.add_attribute(attr).should == attr
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/add_attributes_spec.rb b/spec/ruby/library/rexml/element/add_attributes_spec.rb
deleted file mode 100644
index f331803dd8..0000000000
--- a/spec/ruby/library/rexml/element/add_attributes_spec.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#add_attributes" do
- before :each do
- @person = REXML::Element.new "person"
- @person.attributes["name"] = "Bill"
- end
-
- it "adds multiple attributes from a hash" do
- @person.add_attributes({"name" => "Joe", "age" => "27"})
- @person.attributes["name"].should == "Joe"
- @person.attributes["age"].should == "27"
- end
-
- it "adds multiple attributes from an array" do
- attrs = { "name" => "Joe", "age" => "27"}
- @person.add_attributes attrs.to_a
- @person.attributes["name"].should == "Joe"
- @person.attributes["age"].should == "27"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/add_element_spec.rb b/spec/ruby/library/rexml/element/add_element_spec.rb
deleted file mode 100644
index 8ba023f2c7..0000000000
--- a/spec/ruby/library/rexml/element/add_element_spec.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#add_element" do
- before :each do
- @root = REXML::Element.new("root")
- end
-
- it "adds a child without attributes" do
- name = REXML::Element.new("name")
- @root.add_element name
- @root.elements["name"].name.should == name.name
- @root.elements["name"].attributes.should == name.attributes
- @root.elements["name"].context.should == name.context
- end
-
- it "adds a child with attributes" do
- person = REXML::Element.new("person")
- @root.add_element(person, {"name" => "Madonna"})
- @root.elements["person"].name.should == person.name
- @root.elements["person"].attributes.should == person.attributes
- @root.elements["person"].context.should == person.context
- end
-
- it "adds a child with name" do
- @root.add_element "name"
- @root.elements["name"].name.should == "name"
- @root.elements["name"].attributes.should == {}
- @root.elements["name"].context.should == nil
- end
-
- it "returns the added child" do
- name = @root.add_element "name"
- @root.elements["name"].name.should == name.name
- @root.elements["name"].attributes.should == name.attributes
- @root.elements["name"].context.should == name.context
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/add_namespace_spec.rb b/spec/ruby/library/rexml/element/add_namespace_spec.rb
deleted file mode 100644
index 44b074bac7..0000000000
--- a/spec/ruby/library/rexml/element/add_namespace_spec.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#add_namespace" do
- before :each do
- @elem = REXML::Element.new("person")
- end
-
- it "adds a namespace to element" do
- @elem.add_namespace("foo", "bar")
- @elem.namespace("foo").should == "bar"
- end
-
- it "accepts a prefix string as prefix" do
- @elem.add_namespace("xmlns:foo", "bar")
- @elem.namespace("foo").should == "bar"
- end
-
- it "uses prefix as URI if uri is nil" do
- @elem.add_namespace("some_uri", nil)
- @elem.namespace.should == "some_uri"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/add_text_spec.rb b/spec/ruby/library/rexml/element/add_text_spec.rb
deleted file mode 100644
index 3a0531ad42..0000000000
--- a/spec/ruby/library/rexml/element/add_text_spec.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#add_text" do
- before :each do
- @name = REXML::Element.new "Name"
- end
-
- it "adds text to an element" do
- @name.add_text "Ringo"
- @name.to_s.should == "<Name>Ringo</Name>"
- end
-
- it "accepts a Text" do
- @name.add_text(REXML::Text.new("Ringo"))
- @name.to_s.should == "<Name>Ringo</Name>"
- end
-
- it "joins the new text with the old one" do
- @name.add_text "Ringo"
- @name.add_text " Starr"
- @name.to_s.should == "<Name>Ringo Starr</Name>"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/attribute_spec.rb b/spec/ruby/library/rexml/element/attribute_spec.rb
deleted file mode 100644
index b223d3440c..0000000000
--- a/spec/ruby/library/rexml/element/attribute_spec.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#attribute" do
- it "returns an attribute by name" do
- person = REXML::Element.new "Person"
- attribute = REXML::Attribute.new("drink", "coffee")
- person.add_attribute(attribute)
- person.attribute("drink").should == attribute
- end
-
- it "supports attributes inside namespaces" do
- e = REXML::Element.new("element")
- e.add_attributes({"xmlns:ns" => "http://some_uri"})
- e.attribute("ns", "ns").to_s.should == "http://some_uri"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/attributes_spec.rb b/spec/ruby/library/rexml/element/attributes_spec.rb
deleted file mode 100644
index 92bcecc40a..0000000000
--- a/spec/ruby/library/rexml/element/attributes_spec.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#attributes" do
- it "returns element's Attributes" do
- p = REXML::Element.new "Person"
-
- name = REXML::Attribute.new("name", "John")
- attrs = REXML::Attributes.new(p)
- attrs.add name
-
- p.add_attribute name
- p.attributes.should == attrs
- end
-
- it "returns an empty hash if element has no attributes" do
- REXML::Element.new("Person").attributes.should == {}
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/cdatas_spec.rb b/spec/ruby/library/rexml/element/cdatas_spec.rb
deleted file mode 100644
index 988b2cb422..0000000000
--- a/spec/ruby/library/rexml/element/cdatas_spec.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#cdatas" do
- before :each do
- @e = REXML::Element.new("Root")
- end
-
- it "returns the array of children cdatas" do
- c = REXML::CData.new("Primary")
- d = REXML::CData.new("Secondary")
- @e << c
- @e << d
- @e.cdatas.should == [c, d]
- end
-
- it "freezes the returned array" do
- @e.cdatas.should.frozen?
- end
-
- it "returns an empty array if element has no cdata" do
- @e.cdatas.should == []
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/clone_spec.rb b/spec/ruby/library/rexml/element/clone_spec.rb
deleted file mode 100644
index 490e43181f..0000000000
--- a/spec/ruby/library/rexml/element/clone_spec.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#clone" do
- before :each do
- @e = REXML::Element.new "a"
- end
- it "creates a copy of element" do
- @e.clone.to_s.should == @e.to_s
- end
-
- it "copies the attributes" do
- @e.add_attribute("foo", "bar")
- @e.clone.to_s.should == @e.to_s
- end
-
- it "does not copy the text" do
- @e.add_text "some text..."
- @e.clone.to_s.should_not == @e
- @e.clone.to_s.should == "<a/>"
- end
-
- it "does not copy the child elements" do
- b = REXML::Element.new "b"
- @e << b
- @e.clone.should_not == @e
- @e.clone.to_s.should == "<a/>"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/comments_spec.rb b/spec/ruby/library/rexml/element/comments_spec.rb
deleted file mode 100644
index 84ab9a7469..0000000000
--- a/spec/ruby/library/rexml/element/comments_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#comments" do
- before :each do
- @e = REXML::Element.new "root"
- @c1 = REXML::Comment.new "this is a comment"
- @c2 = REXML::Comment.new "this is another comment"
- @e << @c1
- @e << @c2
- end
-
- it "returns the array of comments" do
- @e.comments.should == [@c1, @c2]
- end
-
- it "returns a frozen object" do
- @e.comments.should.frozen?
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/delete_attribute_spec.rb b/spec/ruby/library/rexml/element/delete_attribute_spec.rb
deleted file mode 100644
index e2ba81eb0d..0000000000
--- a/spec/ruby/library/rexml/element/delete_attribute_spec.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#delete_attribute" do
- before :each do
- @e = REXML::Element.new("Person")
- @attr = REXML::Attribute.new("name", "Sean")
- @e.add_attribute(@attr)
- end
-
- it "deletes an attribute from the element" do
- @e.delete_attribute("name")
- @e.attributes["name"].should be_nil
- end
-
- # Bug was filled with a patch in Ruby's tracker #20298
- quarantine! do
- it "receives an Attribute" do
- @e.add_attribute(@attr)
- @e.delete_attribute(@attr)
- @e.attributes["name"].should be_nil
- end
- end
-
- # Docs say that it returns the removed attribute but then examples
- # show it returns the element with the attribute removed.
- # Also fixed in #20298
- it "returns the element with the attribute removed" do
- elem = @e.delete_attribute("name")
- elem.attributes.should be_empty
- elem.to_s.should eql("<Person/>")
- end
-
- it "returns nil if the attribute does not exist" do
- @e.delete_attribute("name")
- at = @e.delete_attribute("name")
- at.should be_nil
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/delete_element_spec.rb b/spec/ruby/library/rexml/element/delete_element_spec.rb
deleted file mode 100644
index c0b486a6f7..0000000000
--- a/spec/ruby/library/rexml/element/delete_element_spec.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#delete_element" do
- before :each do
- @root = REXML::Element.new("root")
- end
-
- it "deletes the child element" do
- node = REXML::Element.new("some_node")
- @root.add_element node
- @root.delete_element node
- @root.elements.size.should == 0
- end
-
- it "deletes a child via XPath" do
- @root.add_element "some_node"
- @root.delete_element "some_node"
- @root.elements.size.should == 0
- end
-
- it "deletes the child at index" do
- @root.add_element "some_node"
- @root.delete_element 1
- @root.elements.size.should == 0
- end
-
- # According to the docs this should return the deleted element
- # but it won't if it's an Element.
- it "deletes Element and returns it" do
- node = REXML::Element.new("some_node")
- @root.add_element node
- del_node = @root.delete_element node
- del_node.should == node
- end
-
- # Note how passing the string will return the removed element
- # but passing the Element as above won't.
- it "deletes an element and returns it" do
- node = REXML::Element.new("some_node")
- @root.add_element node
- del_node = @root.delete_element "some_node"
- del_node.should == node
- end
-
- it "returns nil unless element exists" do
- @root.delete_element("something").should == nil
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/delete_namespace_spec.rb b/spec/ruby/library/rexml/element/delete_namespace_spec.rb
deleted file mode 100644
index a7763d51e8..0000000000
--- a/spec/ruby/library/rexml/element/delete_namespace_spec.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#delete_namespace" do
-
- before :each do
- @doc = REXML::Document.new "<a xmlns:foo='bar' xmlns='twiddle'/>"
- end
-
- it "deletes a namespace from the element" do
- @doc.root.delete_namespace 'foo'
- @doc.root.namespace("foo").should be_nil
- @doc.root.to_s.should == "<a xmlns='twiddle'/>"
- end
-
- it "deletes default namespace when called with no args" do
- @doc.root.delete_namespace
- @doc.root.namespace.should be_empty
- @doc.root.to_s.should == "<a xmlns:foo='bar'/>"
- end
-
- it "returns the element" do
- @doc.root.delete_namespace.should == @doc.root
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/document_spec.rb b/spec/ruby/library/rexml/element/document_spec.rb
deleted file mode 100644
index 754f27d8a0..0000000000
--- a/spec/ruby/library/rexml/element/document_spec.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#document" do
-
- it "returns the element's document" do
- d = REXML::Document.new("<root><elem/></root>")
- d << REXML::XMLDecl.new
- d.root.document.should == d
- d.root.document.to_s.should == d.to_s
- end
-
- it "returns nil if it belongs to no document" do
- REXML::Element.new("standalone").document.should be_nil
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/each_element_with_attribute_spec.rb b/spec/ruby/library/rexml/element/each_element_with_attribute_spec.rb
deleted file mode 100644
index dcc6dbbf17..0000000000
--- a/spec/ruby/library/rexml/element/each_element_with_attribute_spec.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#each_element_with_attributes" do
- before :each do
- @document = REXML::Element.new("people")
- @father = REXML::Element.new("Person")
- @father.attributes["name"] = "Joe"
- @son = REXML::Element.new("Child")
- @son.attributes["name"] = "Fred"
- @document.root << @father
- @document.root << @son
- @children = []
- end
-
- it "returns children with attribute" do
- @document.each_element_with_attribute("name") { |elem| @children << elem }
- @children[0].should == @father
- @children[1].should == @son
- end
-
- it "takes attribute value as second argument" do
- @document.each_element_with_attribute("name", "Fred"){ |elem| elem.should == @son }
- end
-
- it "takes max number of children as third argument" do
- @document.each_element_with_attribute("name", nil, 1) { |elem| @children << elem }
- @children.size.should == 1
- @children[0].should == @father
- end
-
- it "takes XPath filter as fourth argument" do
- @document.each_element_with_attribute("name", nil, 0, "Child"){ |elem| elem.should == @son}
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/each_element_with_text_spec.rb b/spec/ruby/library/rexml/element/each_element_with_text_spec.rb
deleted file mode 100644
index a4a200d237..0000000000
--- a/spec/ruby/library/rexml/element/each_element_with_text_spec.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#each_element_with_text" do
- before :each do
- @document = REXML::Element.new("people")
-
- @joe = REXML::Element.new("Person")
- @joe.text = "Joe"
- @fred = REXML::Element.new("Person")
- @fred.text = "Fred"
- @another = REXML::Element.new("AnotherPerson")
- @another.text = "Fred"
- @document.root << @joe
- @document.root << @fred
- @document.root << @another
- @children = []
- end
-
- it "returns children with text" do
- @document.each_element_with_text("Joe"){|c| c.should == @joe}
- end
-
- it "takes max as second argument" do
- @document.each_element_with_text("Fred", 1){ |c| c.should == @fred}
- end
-
- it "takes XPath filter as third argument" do
- @document.each_element_with_text("Fred", 0, "Person"){ |c| c.should == @fred}
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/element_reference_spec.rb b/spec/ruby/library/rexml/element/element_reference_spec.rb
deleted file mode 100644
index 9e5d371ce4..0000000000
--- a/spec/ruby/library/rexml/element/element_reference_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#[]" do
-
- before :each do
- @doc = REXML::Document.new("<root foo='bar'></root>")
- @child = REXML::Element.new("child")
- @doc.root.add_element @child
- end
-
- it "return attribute value if argument is string or symbol" do
- @doc.root[:foo].should == 'bar'
- @doc.root['foo'].should == 'bar'
- end
-
- it "return nth element if argument is int" do
- @doc.root[0].should == @child
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/get_text_spec.rb b/spec/ruby/library/rexml/element/get_text_spec.rb
deleted file mode 100644
index 0fa8d7cb3f..0000000000
--- a/spec/ruby/library/rexml/element/get_text_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#get_text" do
- before :each do
- @doc = REXML::Document.new "<p>some text<b>this is bold!</b> more text</p>"
- end
-
- it "returns the first text child node" do
- @doc.root.get_text.value.should == "some text"
- @doc.root.get_text.should be_kind_of(REXML::Text)
- end
-
- it "returns text from an element matching path" do
- @doc.root.get_text("b").value.should == "this is bold!"
- @doc.root.get_text("b").should be_kind_of(REXML::Text)
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/has_attributes_spec.rb b/spec/ruby/library/rexml/element/has_attributes_spec.rb
deleted file mode 100644
index af3ce8ce1b..0000000000
--- a/spec/ruby/library/rexml/element/has_attributes_spec.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#has_attributes?" do
- before :each do
- @e = REXML::Element.new("test_elem")
- end
-
- it "returns true when element has any attributes" do
- @e.add_attribute("name", "Joe")
- @e.has_attributes?.should be_true
- end
-
- it "returns false if element has no attributes" do
- @e.has_attributes?.should be_false
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/has_elements_spec.rb b/spec/ruby/library/rexml/element/has_elements_spec.rb
deleted file mode 100644
index 04c7fe01a5..0000000000
--- a/spec/ruby/library/rexml/element/has_elements_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#has_elements?" do
- before :each do
- @e = REXML::Element.new("root")
- end
-
- it "returns true if element has child elements" do
- child = REXML::Element.new("child")
- @e << child
- @e.has_elements?.should be_true
- end
-
- it "returns false if element doesn't have child elements" do
- @e.has_elements?.should be_false
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/has_text_spec.rb b/spec/ruby/library/rexml/element/has_text_spec.rb
deleted file mode 100644
index 206c167ae6..0000000000
--- a/spec/ruby/library/rexml/element/has_text_spec.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#has_text?" do
-
- it "returns true if element has a Text child" do
- e = REXML::Element.new("Person")
- e.text = "My text"
- e.has_text?.should be_true
- end
-
- it "returns false if it has no Text children" do
- e = REXML::Element.new("Person")
- e.has_text?.should be_false
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/inspect_spec.rb b/spec/ruby/library/rexml/element/inspect_spec.rb
deleted file mode 100644
index ec16c136ee..0000000000
--- a/spec/ruby/library/rexml/element/inspect_spec.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#inspect" do
-
- before :each do
- @name = REXML::Element.new "name"
- end
-
- it "returns the node as a string" do
- @name.inspect.should == "<name/>"
- end
-
- it "inserts '...' if the node has children" do
- e = REXML::Element.new "last_name"
- @name << e
- @name.inspect.should == "<name> ... </>"
- # This might make more sense but differs from MRI's default behavior
- # @name.inspect.should == "<name> ... </name>"
- end
-
- it "inserts the attributes in the string" do
- @name.add_attribute "language"
- @name.attributes["language"] = "english"
- @name.inspect.should == "<name language='english'/>"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/instructions_spec.rb b/spec/ruby/library/rexml/element/instructions_spec.rb
deleted file mode 100644
index 11f1396df0..0000000000
--- a/spec/ruby/library/rexml/element/instructions_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#instructions" do
- before :each do
- @elem = REXML::Element.new("root")
- end
- it "returns the Instruction children nodes" do
- inst = REXML::Instruction.new("xml-stylesheet", "href='headlines.css'")
- @elem << inst
- @elem.instructions.first.should == inst
- end
-
- it "returns an empty array if it has no Instruction children" do
- @elem.instructions.should be_empty
- end
-
- it "freezes the returned array" do
- @elem.instructions.frozen?.should be_true
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/namespace_spec.rb b/spec/ruby/library/rexml/element/namespace_spec.rb
deleted file mode 100644
index 28966289c5..0000000000
--- a/spec/ruby/library/rexml/element/namespace_spec.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#namespace" do
- before :each do
- @doc = REXML::Document.new("<a xmlns='1' xmlns:y='2'><b/><c xmlns:z='3'/></a>")
- @elem = @doc.elements["//b"]
- end
-
- it "returns the default namespace" do
- @elem.namespace.should == "1"
- end
-
- it "accepts a namespace prefix" do
- @elem.namespace("y").should == "2"
- @doc.elements["//c"].namespace("z").should == "3"
- end
-
- it "returns an empty String if default namespace is not defined" do
- e = REXML::Document.new("<a/>")
- e.root.namespace.should be_empty
- end
-
- it "returns nil if namespace is not defined" do
- @elem.namespace("z").should be_nil
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/namespaces_spec.rb b/spec/ruby/library/rexml/element/namespaces_spec.rb
deleted file mode 100644
index 4544540173..0000000000
--- a/spec/ruby/library/rexml/element/namespaces_spec.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#namespaces" do
- before :each do
- doc = REXML::Document.new("<a xmlns='1' xmlns:y='2'><b/><c xmlns:z='3'/></a>")
- @elem = doc.elements["//c"]
- end
-
- it "returns a hash of the namespaces" do
- ns = {"y"=>"2", "z"=>"3", "xmlns"=>"1"}
- @elem.namespaces.keys.sort.should == ns.keys.sort
- @elem.namespaces.values.sort.should == ns.values.sort
- end
-
- it "returns an empty hash if no namespaces exist" do
- e = REXML::Element.new "element"
- e.namespaces.kind_of?(Hash).should == true
- e.namespaces.should be_empty
- end
-
- it "uses namespace prefixes as keys" do
- prefixes = ["y", "z", "xmlns"]
- @elem.namespaces.keys.sort.should == prefixes.sort
- end
-
- it "uses namespace values as the hash values" do
- values = ["2", "3", "1"]
- @elem.namespaces.values.sort.should == values.sort
- end
-
- end
-end
diff --git a/spec/ruby/library/rexml/element/new_spec.rb b/spec/ruby/library/rexml/element/new_spec.rb
deleted file mode 100644
index c6ab289476..0000000000
--- a/spec/ruby/library/rexml/element/new_spec.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#new" do
-
- it "creates element from tag name" do
- REXML::Element.new("foo").name.should == "foo"
- end
-
- it "creates element with default attributes" do
- e = REXML::Element.new
- e.name.should == REXML::Element::UNDEFINED
- e.context.should == nil
- e.parent.should == nil
- end
-
- it "creates element from another element" do
- e = REXML::Element.new "foo"
- f = REXML::Element.new e
- e.name.should == f.name
- e.context.should == f.context
- e.parent.should == f.parent
- end
-
- it "takes parent as second argument" do
- parent = REXML::Element.new "foo"
- child = REXML::Element.new "bar", parent
- child.parent.should == parent
- end
-
- it "takes context as third argument" do
- context = {"some_key" => "some_value"}
- REXML::Element.new("foo", nil, context).context.should == context
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/next_element_spec.rb b/spec/ruby/library/rexml/element/next_element_spec.rb
deleted file mode 100644
index 46d8f74760..0000000000
--- a/spec/ruby/library/rexml/element/next_element_spec.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#next_element" do
- before :each do
- @a = REXML::Element.new "a"
- @b = REXML::Element.new "b"
- @c = REXML::Element.new "c"
- @a.root << @b
- @a.root << @c
- end
- it "returns next existing element" do
- @a.elements["b"].next_element.should == @c
- end
-
- it "returns nil on last element" do
- @a.elements["c"].next_element.should == nil
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/node_type_spec.rb b/spec/ruby/library/rexml/element/node_type_spec.rb
deleted file mode 100644
index a39c2deca5..0000000000
--- a/spec/ruby/library/rexml/element/node_type_spec.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#node_type" do
- it "returns :element" do
- REXML::Element.new("MyElem").node_type.should == :element
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/prefixes_spec.rb b/spec/ruby/library/rexml/element/prefixes_spec.rb
deleted file mode 100644
index ea4caab4bc..0000000000
--- a/spec/ruby/library/rexml/element/prefixes_spec.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#prefixes" do
- before :each do
- doc = REXML::Document.new("<a xmlns='1' xmlns:y='2'><b/><c xmlns:z='3'/></a>")
- @elem = doc.elements["//c"]
- end
-
- it "returns an array of the prefixes of the namespaces" do
- @elem.prefixes.should == ["y", "z"]
- end
-
- it "does not include the default namespace" do
- @elem.prefixes.include?("xmlns").should == false
- end
-
- it "returns an empty array if no namespace was defined" do
- doc = REXML::Document.new "<root><something/></root>"
- root = doc.elements["//root"]
- root.prefixes.should == []
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/previous_element_spec.rb b/spec/ruby/library/rexml/element/previous_element_spec.rb
deleted file mode 100644
index a43b1ddd10..0000000000
--- a/spec/ruby/library/rexml/element/previous_element_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#previous_element" do
- before :each do
- @a = REXML::Element.new "a"
- @b = REXML::Element.new "b"
- @c = REXML::Element.new "c"
- @a.root << @b
- @a.root << @c
- end
-
- it "returns previous element" do
- @a.elements["c"].previous_element.should == @b
- end
-
- it "returns nil on first element" do
- @a.elements["b"].previous_element.should == nil
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/raw_spec.rb b/spec/ruby/library/rexml/element/raw_spec.rb
deleted file mode 100644
index 200a99d194..0000000000
--- a/spec/ruby/library/rexml/element/raw_spec.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#raw" do
- it "returns true if raw mode is set to all" do
- REXML::Element.new("MyElem", nil, {raw: :all}).raw.should == true
- end
-
- it "returns true if raw mode is set to expanded_name" do
- REXML::Element.new("MyElem", nil, {raw: "MyElem"}).raw.should == true
- end
-
- it "returns false if raw mode is not set" do
- REXML::Element.new("MyElem", nil, {raw: ""}).raw.should == false
- end
-
- it "returns false if raw is not :all or expanded_name" do
- REXML::Element.new("MyElem", nil, {raw: "Something"}).raw.should == false
- end
-
- it "returns nil if context is not set" do
- REXML::Element.new("MyElem").raw.should == nil
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/root_spec.rb b/spec/ruby/library/rexml/element/root_spec.rb
deleted file mode 100644
index 52aa4571b9..0000000000
--- a/spec/ruby/library/rexml/element/root_spec.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#root" do
- before :each do
- @doc = REXML::Document.new
- @root = REXML::Element.new "root"
- @node = REXML::Element.new "node"
- @doc << @root << @node
- end
-
- it "returns first child on documents" do
- @doc.root.should == @root
- end
-
- it "returns self on root nodes" do
- @root.root.should == @root
- end
-
- it "returns parent's root on child nodes" do
- @node.root.should == @root
- end
-
- it "returns self on standalone nodes" do
- e = REXML::Element.new "Elem" # Note that it doesn't have a parent node
- e.root.should == e
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/text_spec.rb b/spec/ruby/library/rexml/element/text_spec.rb
deleted file mode 100644
index 3234bba153..0000000000
--- a/spec/ruby/library/rexml/element/text_spec.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#text" do
- before :each do
- @e = REXML::Element.new "name"
- @e.text = "John"
- end
-
- it "returns the text node of element" do
- @e.text.should == "John"
- end
-
- it "returns the text node value" do
- t = REXML::Text.new "Joe"
- @e.text = t
- @e.text.should == "Joe"
- @e.text.should_not == t
- end
-
- it "returns nil if no text is attached" do
- elem = REXML::Element.new "name"
- elem.text.should == nil
- end
- end
-
- describe "REXML::Element#text=" do
- before :each do
- @e = REXML::Element.new "name"
- @e.text = "John"
- end
-
- it "sets the text node" do
- @e.to_s.should == "<name>John</name>"
- end
-
- it "replaces existing text" do
- @e.text = "Joe"
- @e.to_s.should == "<name>Joe</name>"
- end
-
- it "receives nil as an argument" do
- @e.text = nil
- @e.to_s.should == "<name/>"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/texts_spec.rb b/spec/ruby/library/rexml/element/texts_spec.rb
deleted file mode 100644
index 2d374d5e66..0000000000
--- a/spec/ruby/library/rexml/element/texts_spec.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#texts" do
-
- it "returns an array of the Text children" do
- e = REXML::Element.new("root")
- e.add_text "First"
- e.add_text "Second"
- e.texts.should == ["FirstSecond"]
- end
-
- it "returns an empty array if it has no Text children" do
- REXML::Element.new("root").texts.should == []
- end
- end
-end
diff --git a/spec/ruby/library/rexml/element/whitespace_spec.rb b/spec/ruby/library/rexml/element/whitespace_spec.rb
deleted file mode 100644
index f455067922..0000000000
--- a/spec/ruby/library/rexml/element/whitespace_spec.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Element#whitespace" do
- it "returns true if whitespace is respected in the element" do
- e = REXML::Element.new("root")
- e.whitespace.should be_true
-
- e = REXML::Element.new("root", nil, respect_whitespace: :all)
- e.whitespace.should be_true
-
- e = REXML::Element.new("root", nil, respect_whitespace: ["root"])
- e.whitespace.should be_true
- end
-
- it "returns false if whitespace is ignored inside element" do
- e = REXML::Element.new("root", nil, compress_whitespace: :all)
- e.whitespace.should be_false
-
- e = REXML::Element.new("root", nil, compress_whitespace: ["root"])
- e.whitespace.should be_false
- end
- end
-end
diff --git a/spec/ruby/library/rexml/node/each_recursive_spec.rb b/spec/ruby/library/rexml/node/each_recursive_spec.rb
deleted file mode 100644
index da347b1389..0000000000
--- a/spec/ruby/library/rexml/node/each_recursive_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Node#each_recursive" do
- before :each do
- @doc = REXML::Document.new
- @doc << REXML::XMLDecl.new
- @root = REXML::Element.new "root"
- @child1 = REXML::Element.new "child1"
- @child2 = REXML::Element.new "child2"
- @root << @child1
- @root << @child2
- @doc << @root
- end
-
- it "visits all subnodes of self" do
- nodes = []
- @doc.each_recursive { |node| nodes << node}
- nodes.should == [@root, @child1, @child2]
- end
- end
-end
diff --git a/spec/ruby/library/rexml/node/find_first_recursive_spec.rb b/spec/ruby/library/rexml/node/find_first_recursive_spec.rb
deleted file mode 100644
index 2a4f1097ae..0000000000
--- a/spec/ruby/library/rexml/node/find_first_recursive_spec.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Node#find_first_recursive" do
- before :each do
- @e = REXML::Element.new("root")
- @node1 = REXML::Element.new("node")
- @node2 = REXML::Element.new("another node")
- @subnode = REXML::Element.new("another node")
- @node1 << @subnode
- @e << @node1
- @e << @node2
- end
-
- it "finds the first element that matches block" do
- found = @e.find_first_recursive { |n| n.to_s == "<node><another node/></node>"}
- found.should == @node1
- end
-
- it "visits the nodes in preorder" do
- found = @e.find_first_recursive { |n| n.to_s == "<another node/>"}
- found.should == @subnode
- found.should_not == @node2
- end
- end
-end
diff --git a/spec/ruby/library/rexml/node/index_in_parent_spec.rb b/spec/ruby/library/rexml/node/index_in_parent_spec.rb
deleted file mode 100644
index 55909f86d6..0000000000
--- a/spec/ruby/library/rexml/node/index_in_parent_spec.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Node#index_in_parent" do
- it "returns the index (starting from 1) of self in parent" do
- e = REXML::Element.new("root")
- node1 = REXML::Element.new("node")
- node2 = REXML::Element.new("another node")
- e << node1
- e << node2
-
- node1.index_in_parent.should == 1
- node2.index_in_parent.should == 2
- end
- end
-end
diff --git a/spec/ruby/library/rexml/node/next_sibling_node_spec.rb b/spec/ruby/library/rexml/node/next_sibling_node_spec.rb
deleted file mode 100644
index 7aae861d75..0000000000
--- a/spec/ruby/library/rexml/node/next_sibling_node_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Node#next_sibling_node" do
- before :each do
- @e = REXML::Element.new("root")
- @node1 = REXML::Element.new("node")
- @node2 = REXML::Element.new("another node")
- @e << @node1
- @e << @node2
- end
-
- it "returns the next child node in parent" do
- @node1.next_sibling_node.should == @node2
- end
-
- it "returns nil if there are no more child nodes next" do
- @node2.next_sibling_node.should == nil
- @e.next_sibling_node.should == nil
- end
- end
-end
diff --git a/spec/ruby/library/rexml/node/parent_spec.rb b/spec/ruby/library/rexml/node/parent_spec.rb
deleted file mode 100644
index 43c3a747e0..0000000000
--- a/spec/ruby/library/rexml/node/parent_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Node#parent?" do
- it "returns true for Elements" do
- e = REXML::Element.new("foo")
- e.should.parent?
- end
-
- it "returns true for Documents" do
- e = REXML::Document.new
- e.should.parent?
- end
-
- # This includes attributes, CData and declarations.
- it "returns false for Texts" do
- e = REXML::Text.new("foo")
- e.should_not.parent?
- end
- end
-end
diff --git a/spec/ruby/library/rexml/node/previous_sibling_node_spec.rb b/spec/ruby/library/rexml/node/previous_sibling_node_spec.rb
deleted file mode 100644
index 11263968a7..0000000000
--- a/spec/ruby/library/rexml/node/previous_sibling_node_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Node#previous_sibling_node" do
- before :each do
- @e = REXML::Element.new("root")
- @node1 = REXML::Element.new("node")
- @node2 = REXML::Element.new("another node")
- @e << @node1
- @e << @node2
- end
-
- it "returns the previous child node in parent" do
- @node2.previous_sibling_node.should == @node1
- end
-
- it "returns nil if there are no more child nodes before" do
- @node1.previous_sibling_node.should == nil
- @e.previous_sibling_node.should == nil
- end
- end
-end
diff --git a/spec/ruby/library/rexml/shared/each_element.rb b/spec/ruby/library/rexml/shared/each_element.rb
deleted file mode 100644
index 2e0871161d..0000000000
--- a/spec/ruby/library/rexml/shared/each_element.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-require 'rexml/document'
-require_relative '../../../spec_helper'
-
-describe :rexml_each_element, shared: true do
- before :each do
- @e = REXML::Element.new "root"
- s1 = REXML::Element.new "node1"
- s2 = REXML::Element.new "node2"
- s3 = REXML::Element.new "node3"
- s4 = REXML::Element.new "sub_node"
- @e << s1
- @e << s2
- @e << s3
- @e << s4
- end
-
- it "iterates through element" do
- str = ""
- @e.send(@method) { |elem| str << elem.name << " " }
- str.should == "node1 node2 node3 sub_node "
- end
-
- it "iterates through element filtering with XPath" do
- str = ""
- @e.send(@method, "/*"){ |e| str << e.name << " "}
- str.should == "node1 node2 node3 sub_node "
- end
-end
-
-describe "REXML::Element#each_element" do
- it_behaves_like :rexml_each_element, :each_element
-end
-
-describe "REXML::Elements#each" do
- it_behaves_like :rexml_each_element, :each
-end
diff --git a/spec/ruby/library/rexml/shared/elements_to_a.rb b/spec/ruby/library/rexml/shared/elements_to_a.rb
deleted file mode 100644
index b7169f0b2e..0000000000
--- a/spec/ruby/library/rexml/shared/elements_to_a.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-require 'rexml/document'
-require_relative '../../../spec_helper'
-
-describe :rexml_elements_to_a, shared: true do
- before :each do
- @e = REXML::Element.new "root"
- @first = REXML::Element.new("FirstChild")
- @second = REXML::Element.new("SecondChild")
- @e << @first
- @e << @second
- end
-
- it "returns elements that match xpath" do
- @e.elements.send(@method, "FirstChild").first.should == @first
- end
-
- # According to the docs REXML::Element#get_elements is an alias for
- # REXML::Elements.to_a. Implementation wise there's a difference, get_elements
- # always needs the first param (even if it's nil).
- # A patch was submitted:
- # http://rubyforge.org/tracker/index.php?func=detail&aid=19354&group_id=426&atid=1698
- it "returns all children if xpath is nil" do
- @e.elements.send(@method).should == [@first, @second]
- end
-
-end
-
-describe "REXML::REXML::Elements#to_a" do
- it_behaves_like :rexml_elements_to_a, :to_a
-end
-
-describe "REXML::REXML::Element#get_elements" do
- it_behaves_like :rexml_elements_to_a, :get_elements
-end
diff --git a/spec/ruby/library/rexml/text/append_spec.rb b/spec/ruby/library/rexml/text/append_spec.rb
deleted file mode 100644
index 5e7a5bae7c..0000000000
--- a/spec/ruby/library/rexml/text/append_spec.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Text#<<" do
- it "appends a string to this text node" do
- text = REXML::Text.new("foo")
- text << "bar"
- text.should == "foobar"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/text/clone_spec.rb b/spec/ruby/library/rexml/text/clone_spec.rb
deleted file mode 100644
index 7801782ff5..0000000000
--- a/spec/ruby/library/rexml/text/clone_spec.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Text#clone" do
- it "creates a copy of this node" do
- text = REXML::Text.new("foo")
- text.clone.should == "foo"
- text.clone.should == text
- end
- end
-end
diff --git a/spec/ruby/library/rexml/text/comparison_spec.rb b/spec/ruby/library/rexml/text/comparison_spec.rb
deleted file mode 100644
index 119dd050a6..0000000000
--- a/spec/ruby/library/rexml/text/comparison_spec.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Text#<=>" do
- before :each do
- @first = REXML::Text.new("abc")
- @last = REXML::Text.new("def")
- end
-
- it "returns -1 if lvalue is less than rvalue" do
- val = @first <=> @last
- val.should == -1
- end
-
- it "returns -1 if lvalue is greater than rvalue" do
- val = @last <=> @first
- val.should == 1
- end
-
- it "returns 0 if both values are equal" do
- tmp = REXML::Text.new("tmp")
- val = tmp <=> tmp
- val.should == 0
- end
- end
-end
diff --git a/spec/ruby/library/rexml/text/empty_spec.rb b/spec/ruby/library/rexml/text/empty_spec.rb
deleted file mode 100644
index 4c9c899bcb..0000000000
--- a/spec/ruby/library/rexml/text/empty_spec.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Text#empty?" do
- it "returns true if the text is empty" do
- REXML::Text.new("").should.empty?
- end
-
- it "returns false if the text is not empty" do
- REXML::Text.new("some_text").should_not.empty?
- end
- end
-end
diff --git a/spec/ruby/library/rexml/text/indent_text_spec.rb b/spec/ruby/library/rexml/text/indent_text_spec.rb
deleted file mode 100644
index 73065c37da..0000000000
--- a/spec/ruby/library/rexml/text/indent_text_spec.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Text#indent_text" do
- before :each do
- @t = REXML::Text.new("")
- end
- it "indents a string with default parameters" do
- @t.indent_text("foo").should == "\tfoo"
- end
-
- it "accepts a custom indentation level as second argument" do
- @t.indent_text("foo", 2, "\t", true).should == "\t\tfoo"
- end
-
- it "accepts a custom separator as third argument" do
- @t.indent_text("foo", 1, "\n", true).should == "\nfoo"
- end
-
- it "accepts a fourth parameter to skip the first line" do
- @t.indent_text("foo", 1, "\t", false).should == "foo"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/text/inspect_spec.rb b/spec/ruby/library/rexml/text/inspect_spec.rb
deleted file mode 100644
index af389890ee..0000000000
--- a/spec/ruby/library/rexml/text/inspect_spec.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Text#inspect" do
- it "inspects the string attribute as a string" do
- REXML::Text.new("a text").inspect.should == "a text".inspect
- end
- end
-end
diff --git a/spec/ruby/library/rexml/text/new_spec.rb b/spec/ruby/library/rexml/text/new_spec.rb
deleted file mode 100644
index 8b33da9294..0000000000
--- a/spec/ruby/library/rexml/text/new_spec.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Text.new" do
-
- it "creates a Text child node with no parent" do
- t = REXML::Text.new("test")
- t.should be_kind_of(REXML::Child)
- t.should == "test"
- t.parent.should == nil
- end
-
- it "respects whitespace if second argument is true" do
- t = REXML::Text.new("testing whitespace", true)
- t.should == "testing whitespace"
- t = REXML::Text.new(" ", true)
- t.should == " "
- end
-
- it "receives a parent as third argument" do
- e = REXML::Element.new("root")
- t = REXML::Text.new("test", false, e)
- t.parent.should == e
- e.to_s.should == "<root>test</root>"
- end
-
- it "expects escaped text if raw is true" do
- t = REXML::Text.new("&lt;&amp;&gt;", false, nil, true)
- t.should == "&lt;&amp;&gt;"
-
- ->{ REXML::Text.new("<&>", false, nil, true)}.should raise_error(Exception)
- end
-
- it "uses raw value of the parent if raw is nil" do
- e1 = REXML::Element.new("root", nil, { raw: :all})
- -> {REXML::Text.new("<&>", false, e1)}.should raise_error(Exception)
-
- e2 = REXML::Element.new("root", nil, { raw: []})
- e2.raw.should be_false
- t1 = REXML::Text.new("<&>", false, e2)
- t1.should == "&lt;&amp;&gt;"
- end
-
- it "escapes the values if raw is false" do
- t = REXML::Text.new("<&>", false, nil, false)
- t.should == "&lt;&amp;&gt;"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/text/node_type_spec.rb b/spec/ruby/library/rexml/text/node_type_spec.rb
deleted file mode 100644
index f44a1ede3e..0000000000
--- a/spec/ruby/library/rexml/text/node_type_spec.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Text#node_type" do
- it "returns :text" do
- REXML::Text.new("test").node_type.should == :text
- end
- end
-end
diff --git a/spec/ruby/library/rexml/text/normalize_spec.rb b/spec/ruby/library/rexml/text/normalize_spec.rb
deleted file mode 100644
index cde11ec3c9..0000000000
--- a/spec/ruby/library/rexml/text/normalize_spec.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Text.normalize" do
- it "escapes a string with <, >, &, ' and \" " do
- REXML::Text.normalize("< > & \" '").should == "&lt; &gt; &amp; &quot; &apos;"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/text/read_with_substitution_spec.rb b/spec/ruby/library/rexml/text/read_with_substitution_spec.rb
deleted file mode 100644
index 7ff26f4d53..0000000000
--- a/spec/ruby/library/rexml/text/read_with_substitution_spec.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Text.read_with_substitution" do
- it "reads a text and escapes entities" do
- REXML::Text.read_with_substitution("&lt; &gt; &amp; &quot; &apos;").should == "< > & \" '"
- end
-
- it "accepts an regex for invalid expressions and raises an error if text matches" do
- -> {REXML::Text.read_with_substitution("this is illegal", /illegal/)}.should raise_error(Exception)
- end
- end
-end
diff --git a/spec/ruby/library/rexml/text/to_s_spec.rb b/spec/ruby/library/rexml/text/to_s_spec.rb
deleted file mode 100644
index e67632c9a1..0000000000
--- a/spec/ruby/library/rexml/text/to_s_spec.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Text#to_s" do
- it "returns the string of this Text node" do
- u = REXML::Text.new("sean russell", false, nil, true)
- u.to_s.should == "sean russell"
-
- t = REXML::Text.new("some test text")
- t.to_s.should == "some test text"
- end
-
- it "escapes the text" do
- t = REXML::Text.new("& < >")
- t.to_s.should == "&amp; &lt; &gt;"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/text/unnormalize_spec.rb b/spec/ruby/library/rexml/text/unnormalize_spec.rb
deleted file mode 100644
index 7b507194d0..0000000000
--- a/spec/ruby/library/rexml/text/unnormalize_spec.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Text.unnormalize" do
- it "unescapes a string with the values defined in SETUTITSBUS" do
- REXML::Text.unnormalize("&lt; &gt; &amp; &quot; &apos;").should == "< > & \" '"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/text/value_spec.rb b/spec/ruby/library/rexml/text/value_spec.rb
deleted file mode 100644
index 53d40c765f..0000000000
--- a/spec/ruby/library/rexml/text/value_spec.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Text#value" do
- it "returns the text value of this node" do
- REXML::Text.new("test").value.should == "test"
- end
-
- it "does not escape entities" do
- REXML::Text.new("& \"").value.should == "& \""
- end
-
- it "follows the respect_whitespace attribute" do
- REXML::Text.new("test bar", false).value.should == "test bar"
- REXML::Text.new("test bar", true).value.should == "test bar"
- end
-
- it "ignores the raw attribute" do
- REXML::Text.new("sean russell", false, nil, true).value.should == "sean russell"
- end
- end
-
- describe "REXML::Text#value=" do
- before :each do
- @t = REXML::Text.new("new")
- end
-
- it "sets the text of the node" do
- @t.value = "another text"
- @t.to_s.should == "another text"
- end
-
- it "escapes entities" do
- @t.value = "<a>"
- @t.to_s.should == "&lt;a&gt;"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/text/wrap_spec.rb b/spec/ruby/library/rexml/text/wrap_spec.rb
deleted file mode 100644
index 331a8439e2..0000000000
--- a/spec/ruby/library/rexml/text/wrap_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Text#wrap" do
- before :each do
- @t = REXML::Text.new("abc def")
- end
-
- it "wraps the text at width" do
- @t.wrap("abc def", 3, false).should == "abc\ndef"
- end
-
- it "returns the string if width is greater than the size of the string" do
- @t.wrap("abc def", 10, false).should == "abc def"
- end
-
- it "takes a newline at the beginning option as the third parameter" do
- @t.wrap("abc def", 3, true).should == "\nabc\ndef"
- end
- end
-end
diff --git a/spec/ruby/library/rexml/text/write_with_substitution_spec.rb b/spec/ruby/library/rexml/text/write_with_substitution_spec.rb
deleted file mode 100644
index 840f141e3d..0000000000
--- a/spec/ruby/library/rexml/text/write_with_substitution_spec.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Text#write_with_substitution" do
- before :each do
- @t = REXML::Text.new("test")
- @f = tmp("rexml_spec")
- @file = File.open(@f, "w+")
- end
-
- after :each do
- @file.close
- rm_r @f
- end
-
- it "writes out the input to a String" do
- s = ""
- @t.write_with_substitution(s, "some text")
- s.should == "some text"
- end
-
- it "writes out the input to an IO" do
- @t.write_with_substitution(@file, "some text")
- @file.rewind
- @file.gets.should == "some text"
- end
-
- it "escapes characters" do
- @t.write_with_substitution(@file, "& < >")
- @file.rewind
- @file.gets.should == "&amp; &lt; &gt;"
- end
- end
-end
diff --git a/spec/ruby/library/set/compare_by_identity_spec.rb b/spec/ruby/library/set/compare_by_identity_spec.rb
index 9ed1602189..602d1e758e 100644
--- a/spec/ruby/library/set/compare_by_identity_spec.rb
+++ b/spec/ruby/library/set/compare_by_identity_spec.rb
@@ -5,7 +5,7 @@ describe "Set#compare_by_identity" do
it "compares its members by identity" do
a = "a"
b1 = "b"
- b2 = "b"
+ b2 = b1.dup
set = Set.new
set.compare_by_identity
diff --git a/spec/ruby/library/set/comparison_spec.rb b/spec/ruby/library/set/comparison_spec.rb
index 6b7a71e659..ddcfbae0af 100644
--- a/spec/ruby/library/set/comparison_spec.rb
+++ b/spec/ruby/library/set/comparison_spec.rb
@@ -1,29 +1,27 @@
require_relative '../../spec_helper'
require 'set'
-ruby_version_is "3.0" do
- describe "Set#<=>" do
- it "returns 0 if the sets are equal" do
- (Set[] <=> Set[]).should == 0
- (Set[:a, :b, :c] <=> Set[:a, :b, :c]).should == 0
- end
+describe "Set#<=>" do
+ it "returns 0 if the sets are equal" do
+ (Set[] <=> Set[]).should == 0
+ (Set[:a, :b, :c] <=> Set[:a, :b, :c]).should == 0
+ end
- it "returns -1 if the set is a proper subset of the other set" do
- (Set[] <=> Set[1]).should == -1
- (Set[1, 2] <=> Set[1, 2, 3]).should == -1
- end
+ it "returns -1 if the set is a proper subset of the other set" do
+ (Set[] <=> Set[1]).should == -1
+ (Set[1, 2] <=> Set[1, 2, 3]).should == -1
+ end
- it "returns +1 if the set is a proper superset of other set" do
- (Set[1] <=> Set[]).should == +1
- (Set[1, 2, 3] <=> Set[1, 2]).should == +1
- end
+ it "returns +1 if the set is a proper superset of other set" do
+ (Set[1] <=> Set[]).should == +1
+ (Set[1, 2, 3] <=> Set[1, 2]).should == +1
+ end
- it "returns nil if the set has unique elements" do
- (Set[1, 2, 3] <=> Set[:a, :b, :c]).should be_nil
- end
+ it "returns nil if the set has unique elements" do
+ (Set[1, 2, 3] <=> Set[:a, :b, :c]).should be_nil
+ end
- it "returns nil when the argument is not set-like" do
- (Set[] <=> false).should be_nil
- end
+ it "returns nil when the argument is not set-like" do
+ (Set[] <=> false).should be_nil
end
end
diff --git a/spec/ruby/library/set/divide_spec.rb b/spec/ruby/library/set/divide_spec.rb
index fdd8cd9622..998a1b292c 100644
--- a/spec/ruby/library/set/divide_spec.rb
+++ b/spec/ruby/library/set/divide_spec.rb
@@ -13,11 +13,11 @@ describe "Set#divide" do
ret.sort.should == ["five", "four", "one", "three", "two"]
end
- # BUG: Does not raise a LocalJumpError, but a NoMethodError
- #
- # it "raises a LocalJumpError when not passed a block" do
- # lambda { Set[1].divide }.should raise_error(LocalJumpError)
- # end
+ it "returns an enumerator when not passed a block" do
+ ret = Set[1, 2, 3, 4].divide
+ ret.should be_kind_of(Enumerator)
+ ret.each(&:even?).should == Set[Set[1, 3], Set[2, 4]]
+ end
end
describe "Set#divide when passed a block with an arity of 2" do
@@ -31,4 +31,29 @@ describe "Set#divide when passed a block with an arity of 2" do
Set[1, 2].divide { |x, y| ret << [x, y] }
ret.sort.should == [[1, 1], [1, 2], [2, 1], [2, 2]]
end
+
+ it "returns an enumerator when not passed a block" do
+ ret = Set[1, 2, 3, 4].divide
+ ret.should be_kind_of(Enumerator)
+ ret.each { |a, b| (a + b).even? }.should == Set[Set[1, 3], Set[2, 4]]
+ end
+end
+
+describe "Set#divide when passed a block with an arity of > 2" do
+ it "only uses the first element if the arity > 2" do
+ set = Set["one", "two", "three", "four", "five"].divide do |x, y, z|
+ y.should be_nil
+ z.should be_nil
+ x.length
+ end
+ set.map { |x| x.to_a.sort }.sort.should == [["five", "four"], ["one", "two"], ["three"]]
+ end
+
+ it "only uses the first element if the arity = -1" do
+ set = Set["one", "two", "three", "four", "five"].divide do |*xs|
+ xs.size.should == 1
+ xs.first.length
+ end
+ set.map { |x| x.to_a.sort }.sort.should == [["five", "four"], ["one", "two"], ["three"]]
+ end
end
diff --git a/spec/ruby/library/set/enumerable/to_set_spec.rb b/spec/ruby/library/set/enumerable/to_set_spec.rb
index 3790d8deee..b2d850515b 100644
--- a/spec/ruby/library/set/enumerable/to_set_spec.rb
+++ b/spec/ruby/library/set/enumerable/to_set_spec.rb
@@ -7,14 +7,6 @@ describe "Enumerable#to_set" do
{a: 1, b: 2}.to_set.should == Set[[:b, 2], [:a, 1]]
end
- ruby_version_is ''...'3.0' do
- it "allows passing an alternate class for Set" do
- sorted_set = [1, 2, 3].to_set(SortedSet)
- sorted_set.should == SortedSet[1, 2, 3]
- sorted_set.instance_of?(SortedSet).should == true
- end
- end
-
it "passes down passed blocks" do
[1, 2, 3].to_set { |x| x * x }.should == Set[1, 4, 9]
end
diff --git a/spec/ruby/library/set/flatten_spec.rb b/spec/ruby/library/set/flatten_spec.rb
index 4ac83ea825..51b58d6439 100644
--- a/spec/ruby/library/set/flatten_spec.rb
+++ b/spec/ruby/library/set/flatten_spec.rb
@@ -1,6 +1,7 @@
require_relative '../../spec_helper'
require_relative 'fixtures/set_like'
require 'set'
+set_version = defined?(Set::VERSION) ? Set::VERSION : '1.0.0'
describe "Set#flatten" do
it "returns a copy of self with each included Set flattened" do
@@ -45,9 +46,11 @@ describe "Set#flatten!" do
-> { set.flatten! }.should raise_error(ArgumentError)
end
- context "when Set contains a Set-like object" do
- it "flattens self, including Set-like objects" do
- Set[SetSpecs::SetLike.new([1])].flatten!.should == Set[1]
+ version_is(set_version, ""..."1.1.0") do #ruby_version_is ""..."3.3" do
+ context "when Set contains a Set-like object" do
+ it "flattens self, including Set-like objects" do
+ Set[SetSpecs::SetLike.new([1])].flatten!.should == Set[1]
+ end
end
end
end
diff --git a/spec/ruby/library/set/initialize_clone_spec.rb b/spec/ruby/library/set/initialize_clone_spec.rb
index 62985987fa..bda42cd6e8 100644
--- a/spec/ruby/library/set/initialize_clone_spec.rb
+++ b/spec/ruby/library/set/initialize_clone_spec.rb
@@ -2,17 +2,15 @@ require_relative '../../spec_helper'
require 'set'
describe "Set#initialize_clone" do
- ruby_version_is "3.0" do
- # See https://bugs.ruby-lang.org/issues/14266
- it "does not freeze the new Set when called from clone(freeze: false)" do
- set1 = Set[1, 2]
- set1.freeze
- set2 = set1.clone(freeze: false)
- set1.frozen?.should == true
- set2.frozen?.should == false
- set2.add 3
- set1.should == Set[1, 2]
- set2.should == Set[1, 2, 3]
- end
+ # See https://bugs.ruby-lang.org/issues/14266
+ it "does not freeze the new Set when called from clone(freeze: false)" do
+ set1 = Set[1, 2]
+ set1.freeze
+ set2 = set1.clone(freeze: false)
+ set1.frozen?.should == true
+ set2.frozen?.should == false
+ set2.add 3
+ set1.should == Set[1, 2]
+ set2.should == Set[1, 2, 3]
end
end
diff --git a/spec/ruby/library/set/join_spec.rb b/spec/ruby/library/set/join_spec.rb
index 7498a91d98..3f511a84e4 100644
--- a/spec/ruby/library/set/join_spec.rb
+++ b/spec/ruby/library/set/join_spec.rb
@@ -1,31 +1,29 @@
require_relative '../../spec_helper'
require 'set'
-ruby_version_is "3.0" do
- describe "Set#join" do
- it "returns an empty string if the Set is empty" do
- Set[].join.should == ''
- end
+describe "Set#join" do
+ it "returns an empty string if the Set is empty" do
+ Set[].join.should == ''
+ end
- it "returns a new string formed by joining elements after conversion" do
- set = Set[:a, :b, :c]
- set.join.should == "abc"
- end
+ it "returns a new string formed by joining elements after conversion" do
+ set = Set[:a, :b, :c]
+ set.join.should == "abc"
+ end
- it "does not separate elements when the passed separator is nil" do
- set = Set[:a, :b, :c]
- set.join(nil).should == "abc"
- end
+ it "does not separate elements when the passed separator is nil" do
+ set = Set[:a, :b, :c]
+ set.join(nil).should == "abc"
+ end
- it "returns a string formed by concatenating each element separated by the separator" do
- set = Set[:a, :b, :c]
- set.join(' | ').should == "a | b | c"
- end
+ it "returns a string formed by concatenating each element separated by the separator" do
+ set = Set[:a, :b, :c]
+ set.join(' | ').should == "a | b | c"
+ end
- it "calls #to_a to convert the Set in to an Array" do
- set = Set[:a, :b, :c]
- set.should_receive(:to_a).and_return([:a, :b, :c])
- set.join.should == "abc"
- end
+ it "calls #to_a to convert the Set in to an Array" do
+ set = Set[:a, :b, :c]
+ set.should_receive(:to_a).and_return([:a, :b, :c])
+ set.join.should == "abc"
end
end
diff --git a/spec/ruby/library/set/proper_subset_spec.rb b/spec/ruby/library/set/proper_subset_spec.rb
index 1f496a6199..6b51dedc9f 100644
--- a/spec/ruby/library/set/proper_subset_spec.rb
+++ b/spec/ruby/library/set/proper_subset_spec.rb
@@ -1,6 +1,7 @@
require_relative '../../spec_helper'
require_relative 'fixtures/set_like'
require 'set'
+set_version = defined?(Set::VERSION) ? Set::VERSION : '1.0.0'
describe "Set#proper_subset?" do
before :each do
@@ -33,9 +34,11 @@ describe "Set#proper_subset?" do
-> { Set[].proper_subset?(Object.new) }.should raise_error(ArgumentError)
end
- context "when comparing to a Set-like object" do
- it "returns true if passed a Set-like object that self is a proper subset of" do
- Set[1, 2, 3].proper_subset?(SetSpecs::SetLike.new([1, 2, 3, 4])).should be_true
+ version_is(set_version, ""..."1.1.0") do #ruby_version_is ""..."3.3" do
+ context "when comparing to a Set-like object" do
+ it "returns true if passed a Set-like object that self is a proper subset of" do
+ Set[1, 2, 3].proper_subset?(SetSpecs::SetLike.new([1, 2, 3, 4])).should be_true
+ end
end
end
end
diff --git a/spec/ruby/library/set/set_spec.rb b/spec/ruby/library/set/set_spec.rb
new file mode 100644
index 0000000000..2a4edc6dfc
--- /dev/null
+++ b/spec/ruby/library/set/set_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+
+describe 'Set' do
+ ruby_version_is '3.2' do
+ it 'is available without explicit requiring' do
+ output = ruby_exe(<<~RUBY, options: '--disable-gems', args: '2>&1')
+ puts Set.new([1, 2, 3])
+ RUBY
+ output.chomp.should == "#<Set: {1, 2, 3}>"
+ end
+ end
+end
diff --git a/spec/ruby/library/set/shared/inspect.rb b/spec/ruby/library/set/shared/inspect.rb
index 564020e90e..adb6ddb4c9 100644
--- a/spec/ruby/library/set/shared/inspect.rb
+++ b/spec/ruby/library/set/shared/inspect.rb
@@ -1,4 +1,4 @@
-describe "set_inspect", shared: true do
+describe :set_inspect, shared: true do
it "returns a String representation of self" do
Set[].send(@method).should be_kind_of(String)
Set[nil, false, true].send(@method).should be_kind_of(String)
@@ -15,9 +15,11 @@ describe "set_inspect", shared: true do
Set["1", "2"].send(@method).should include('", "')
end
- it "correctly handles self-references" do
- (set = Set[]) << set
- set.send(@method).should be_kind_of(String)
- set.send(@method).should include("#<Set: {...}>")
+ it "correctly handles cyclic-references" do
+ set1 = Set[]
+ set2 = Set[set1]
+ set1 << set2
+ set1.send(@method).should be_kind_of(String)
+ set1.send(@method).should include("#<Set: {...}>")
end
end
diff --git a/spec/ruby/library/set/sortedset/add_spec.rb b/spec/ruby/library/set/sortedset/add_spec.rb
deleted file mode 100644
index 4f3bb252e1..0000000000
--- a/spec/ruby/library/set/sortedset/add_spec.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
- require_relative 'shared/add'
-
- describe "SortedSet#add" do
- it_behaves_like :sorted_set_add, :add
-
- it "takes only values which responds <=>" do
- obj = mock('no_comparison_operator')
- obj.stub!(:respond_to?).with(:<=>).and_return(false)
- -> { SortedSet["hello"].add(obj) }.should raise_error(ArgumentError)
- end
-
- it "raises on incompatible <=> comparison" do
- # Use #to_a here as elements are sorted only when needed.
- # Therefore the <=> incompatibility is only noticed on sorting.
- -> { SortedSet['1', '2'].add(3).to_a }.should raise_error(ArgumentError)
- end
- end
-
- describe "SortedSet#add?" do
- before :each do
- @set = SortedSet.new
- end
-
- it "adds the passed Object to self" do
- @set.add?("cat")
- @set.should include("cat")
- end
-
- it "returns self when the Object has not yet been added to self" do
- @set.add?("cat").should equal(@set)
- end
-
- it "returns nil when the Object has already been added to self" do
- @set.add?("cat")
- @set.add?("cat").should be_nil
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/append_spec.rb b/spec/ruby/library/set/sortedset/append_spec.rb
deleted file mode 100644
index d72d70b21f..0000000000
--- a/spec/ruby/library/set/sortedset/append_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
- require_relative 'shared/add'
-
- describe "SortedSet#<<" do
- it_behaves_like :sorted_set_add, :<<
- end
-end
diff --git a/spec/ruby/library/set/sortedset/case_equality_spec.rb b/spec/ruby/library/set/sortedset/case_equality_spec.rb
deleted file mode 100644
index d7c296b626..0000000000
--- a/spec/ruby/library/set/sortedset/case_equality_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require_relative 'shared/include'
- require 'set'
-
- describe "SortedSet#===" do
- it_behaves_like :sorted_set_include, :===
- end
-end
diff --git a/spec/ruby/library/set/sortedset/classify_spec.rb b/spec/ruby/library/set/sortedset/classify_spec.rb
deleted file mode 100644
index 4011e58b82..0000000000
--- a/spec/ruby/library/set/sortedset/classify_spec.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#classify" do
- before :each do
- @set = SortedSet["one", "two", "three", "four"]
- end
-
- it "yields each Object in self in sorted order" do
- res = []
- @set.classify { |x| res << x }
- res.should == ["one", "two", "three", "four"].sort
- end
-
- it "returns an Enumerator when passed no block" do
- enum = @set.classify
- enum.should be_an_instance_of(Enumerator)
-
- classified = enum.each { |x| x.length }
- classified.should == { 3 => SortedSet["one", "two"], 4 => SortedSet["four"], 5 => SortedSet["three"] }
- end
-
- it "classifies the Objects in self based on the block's return value" do
- classified = @set.classify { |x| x.length }
- classified.should == { 3 => SortedSet["one", "two"], 4 => SortedSet["four"], 5 => SortedSet["three"] }
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/clear_spec.rb b/spec/ruby/library/set/sortedset/clear_spec.rb
deleted file mode 100644
index 879aa824d8..0000000000
--- a/spec/ruby/library/set/sortedset/clear_spec.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#clear" do
- before :each do
- @set = SortedSet["one", "two", "three", "four"]
- end
-
- it "removes all elements from self" do
- @set.clear
- @set.should be_empty
- end
-
- it "returns self" do
- @set.clear.should equal(@set)
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/collect_spec.rb b/spec/ruby/library/set/sortedset/collect_spec.rb
deleted file mode 100644
index 0674f0d130..0000000000
--- a/spec/ruby/library/set/sortedset/collect_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
- require_relative 'shared/collect'
-
- describe "SortedSet#collect!" do
- it_behaves_like :sorted_set_collect_bang, :collect!
- end
-end
diff --git a/spec/ruby/library/set/sortedset/constructor_spec.rb b/spec/ruby/library/set/sortedset/constructor_spec.rb
deleted file mode 100644
index 31f30fd892..0000000000
--- a/spec/ruby/library/set/sortedset/constructor_spec.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet[]" do
- it "returns a new SortedSet populated with the passed Objects" do
- set = SortedSet[1, 2, 3]
-
- set.instance_of?(SortedSet).should be_true
- set.size.should eql(3)
-
- set.should include(1)
- set.should include(2)
- set.should include(3)
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/delete_if_spec.rb b/spec/ruby/library/set/sortedset/delete_if_spec.rb
deleted file mode 100644
index 787639ae12..0000000000
--- a/spec/ruby/library/set/sortedset/delete_if_spec.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#delete_if" do
- before :each do
- @set = SortedSet["one", "two", "three"]
- end
-
- it "yields each Object in self in sorted order" do
- ret = []
- @set.delete_if { |x| ret << x }
- ret.should == ["one", "two", "three"].sort
- end
-
- it "deletes every element from self for which the passed block returns true" do
- @set.delete_if { |x| x.size == 3 }
- @set.size.should eql(1)
-
- @set.should_not include("one")
- @set.should_not include("two")
- @set.should include("three")
- end
-
- it "returns self" do
- @set.delete_if { |x| x }.should equal(@set)
- end
-
- it "returns an Enumerator when passed no block" do
- enum = @set.delete_if
- enum.should be_an_instance_of(Enumerator)
-
- enum.each { |x| x.size == 3 }
-
- @set.should_not include("one")
- @set.should_not include("two")
- @set.should include("three")
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/delete_spec.rb b/spec/ruby/library/set/sortedset/delete_spec.rb
deleted file mode 100644
index 0e2a6accf3..0000000000
--- a/spec/ruby/library/set/sortedset/delete_spec.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#delete" do
- before :each do
- @set = SortedSet["a", "b", "c"]
- end
-
- it "deletes the passed Object from self" do
- @set.delete("a")
- @set.should_not include("a")
- end
-
- it "returns self" do
- @set.delete("a").should equal(@set)
- @set.delete("x").should equal(@set)
- end
- end
-
- describe "SortedSet#delete?" do
- before :each do
- @set = SortedSet["a", "b", "c"]
- end
-
- it "deletes the passed Object from self" do
- @set.delete?("a")
- @set.should_not include("a")
- end
-
- it "returns self when the passed Object is in self" do
- @set.delete?("a").should equal(@set)
- end
-
- it "returns nil when the passed Object is not in self" do
- @set.delete?("x").should be_nil
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/difference_spec.rb b/spec/ruby/library/set/sortedset/difference_spec.rb
deleted file mode 100644
index fb064bdff9..0000000000
--- a/spec/ruby/library/set/sortedset/difference_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
- require_relative 'shared/difference'
-
- describe "SortedSet#difference" do
- it_behaves_like :sorted_set_difference, :difference
- end
-end
diff --git a/spec/ruby/library/set/sortedset/divide_spec.rb b/spec/ruby/library/set/sortedset/divide_spec.rb
deleted file mode 100644
index 31ab6037e4..0000000000
--- a/spec/ruby/library/set/sortedset/divide_spec.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#divide" do
- it "divides self into a set of subsets based on the blocks return values" do
- set = SortedSet["one", "two", "three", "four", "five"].divide { |x| x.length }
- set.map { |x| x.to_a }.to_a.sort.should == [["five", "four"], ["one", "two"], ["three"]]
- end
-
- it "yields each Object in self in sorted order" do
- ret = []
- SortedSet["one", "two", "three", "four", "five"].divide { |x| ret << x }
- ret.should == ["one", "two", "three", "four", "five"].sort
- end
-
- # BUG: Does not raise a LocalJumpError, but a NoMethodError
- #
- # it "raises a LocalJumpError when not passed a block" do
- # lambda { SortedSet[1].divide }.should raise_error(LocalJumpError)
- # end
- end
-
- describe "SortedSet#divide when passed a block with an arity of 2" do
- it "divides self into a set of subsets based on the blocks return values" do
- set = SortedSet[1, 3, 4, 6, 9, 10, 11].divide { |x, y| (x - y).abs == 1 }
- set.map { |x| x.to_a }.to_a.sort.should == [[1], [3, 4], [6], [9, 10, 11]]
- end
-
- it "yields each two Objects to the block" do
- ret = []
- SortedSet[1, 2].divide { |x, y| ret << [x, y] }
- ret.should == [[1, 1], [1, 2], [2, 1], [2, 2]]
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/each_spec.rb b/spec/ruby/library/set/sortedset/each_spec.rb
deleted file mode 100644
index 79d8aee223..0000000000
--- a/spec/ruby/library/set/sortedset/each_spec.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#each" do
- before :each do
- @set = SortedSet[1, 2, 3]
- end
-
- it "yields each Object in self in sorted order" do
- ret = []
- SortedSet["one", "two", "three"].each { |x| ret << x }
- ret.should == ["one", "two", "three"].sort
- end
-
- it "returns self" do
- @set.each { |x| x }.should equal(@set)
- end
-
- it "returns an Enumerator when not passed a block" do
- enum = @set.each
-
- ret = []
- enum.each { |x| ret << x }
- ret.sort.should == [1, 2, 3]
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/empty_spec.rb b/spec/ruby/library/set/sortedset/empty_spec.rb
deleted file mode 100644
index 2e52c3e81a..0000000000
--- a/spec/ruby/library/set/sortedset/empty_spec.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#empty?" do
- it "returns true if self is empty" do
- SortedSet[].empty?.should be_true
- SortedSet[1].empty?.should be_false
- SortedSet[1,2,3].empty?.should be_false
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/eql_spec.rb b/spec/ruby/library/set/sortedset/eql_spec.rb
deleted file mode 100644
index 050464994b..0000000000
--- a/spec/ruby/library/set/sortedset/eql_spec.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#eql?" do
- it "returns true when the passed argument is a SortedSet and contains the same elements" do
- SortedSet[].should eql(SortedSet[])
- SortedSet[1, 2, 3].should eql(SortedSet[1, 2, 3])
- SortedSet[1, 2, 3].should eql(SortedSet[3, 2, 1])
-
- # SortedSet["a", :b, ?c].should eql(SortedSet[?c, :b, "a"])
-
- SortedSet[1, 2, 3].should_not eql(SortedSet[1.0, 2, 3])
- SortedSet[1, 2, 3].should_not eql(SortedSet[2, 3])
- SortedSet[1, 2, 3].should_not eql(SortedSet[])
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/equal_value_spec.rb b/spec/ruby/library/set/sortedset/equal_value_spec.rb
deleted file mode 100644
index 30422f5b95..0000000000
--- a/spec/ruby/library/set/sortedset/equal_value_spec.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#==" do
- it "returns true when the passed Object is a SortedSet and self and the Object contain the same elements" do
- SortedSet[].should == SortedSet[]
- SortedSet[1, 2, 3].should == SortedSet[1, 2, 3]
- SortedSet["1", "2", "3"].should == SortedSet["1", "2", "3"]
-
- SortedSet[1, 2, 3].should_not == SortedSet[1.0, 2, 3]
- SortedSet[1, 2, 3].should_not == [1, 2, 3]
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/exclusion_spec.rb b/spec/ruby/library/set/sortedset/exclusion_spec.rb
deleted file mode 100644
index 1967dfbfa6..0000000000
--- a/spec/ruby/library/set/sortedset/exclusion_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#^" do
- before :each do
- @set = SortedSet[1, 2, 3, 4]
- end
-
- it "returns a new SortedSet containing elements that are not in both self and the passed Enumerable" do
- (@set ^ SortedSet[3, 4, 5]).should == SortedSet[1, 2, 5]
- (@set ^ [3, 4, 5]).should == SortedSet[1, 2, 5]
- end
-
- it "raises an ArgumentError when passed a non-Enumerable" do
- -> { @set ^ 3 }.should raise_error(ArgumentError)
- -> { @set ^ Object.new }.should raise_error(ArgumentError)
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/filter_spec.rb b/spec/ruby/library/set/sortedset/filter_spec.rb
deleted file mode 100644
index 3b9dcb63c9..0000000000
--- a/spec/ruby/library/set/sortedset/filter_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require_relative 'shared/select'
- require 'set'
-
- describe "SortedSet#filter!" do
- it_behaves_like :sorted_set_select_bang, :filter!
- end
-end
diff --git a/spec/ruby/library/set/sortedset/flatten_merge_spec.rb b/spec/ruby/library/set/sortedset/flatten_merge_spec.rb
deleted file mode 100644
index 0d67cb331e..0000000000
--- a/spec/ruby/library/set/sortedset/flatten_merge_spec.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#flatten_merge" do
- it "is protected" do
- SortedSet.should have_protected_instance_method("flatten_merge")
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/flatten_spec.rb b/spec/ruby/library/set/sortedset/flatten_spec.rb
deleted file mode 100644
index e83ad1044a..0000000000
--- a/spec/ruby/library/set/sortedset/flatten_spec.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- # Note: Flatten make little sens on sorted sets, because SortedSets are not (by default)
- # comparable. For a SortedSet to be both valid and nested, we need to define a comparison operator:
- module SortedSet_FlattenSpecs
- class ComparableSortedSet < SortedSet
- def <=>(other)
- return puts "#{other} vs #{self}" unless other.is_a?(ComparableSortedSet)
- to_a <=> other.to_a
- end
- end
- end
-
- describe "SortedSet#flatten" do
- it "returns a copy of self with each included SortedSet flattened" do
- klass = SortedSet_FlattenSpecs::ComparableSortedSet
- set = klass[klass[1,2], klass[3,4], klass[5,6,7], klass[8]]
- flattened_set = set.flatten
-
- flattened_set.should_not equal(set)
- flattened_set.should == klass[1, 2, 3, 4, 5, 6, 7, 8]
- end
- end
-
- describe "SortedSet#flatten!" do
- it "flattens self" do
- klass = SortedSet_FlattenSpecs::ComparableSortedSet
- set = klass[klass[1,2], klass[3,4], klass[5,6,7], klass[8]]
- set.flatten!
- set.should == klass[1, 2, 3, 4, 5, 6, 7, 8]
- end
-
- it "returns self when self was modified" do
- klass = SortedSet_FlattenSpecs::ComparableSortedSet
- set = klass[klass[1,2], klass[3,4]]
- set.flatten!.should equal(set)
- end
-
- it "returns nil when self was not modified" do
- set = SortedSet[1, 2, 3, 4]
- set.flatten!.should be_nil
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/hash_spec.rb b/spec/ruby/library/set/sortedset/hash_spec.rb
deleted file mode 100644
index 40676de7fc..0000000000
--- a/spec/ruby/library/set/sortedset/hash_spec.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#hash" do
- it "is static" do
- SortedSet[].hash.should == SortedSet[].hash
- SortedSet[1, 2, 3].hash.should == SortedSet[1, 2, 3].hash
- SortedSet["a", "b", "c"].hash.should == SortedSet["c", "b", "a"].hash
-
- SortedSet[].hash.should_not == SortedSet[1, 2, 3].hash
- SortedSet[1, 2, 3].hash.should_not == SortedSet["a", "b", "c"].hash
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/include_spec.rb b/spec/ruby/library/set/sortedset/include_spec.rb
deleted file mode 100644
index ec2ad987d5..0000000000
--- a/spec/ruby/library/set/sortedset/include_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require_relative 'shared/include'
- require 'set'
-
- describe "SortedSet#include?" do
- it_behaves_like :sorted_set_include, :include?
- end
-end
diff --git a/spec/ruby/library/set/sortedset/initialize_spec.rb b/spec/ruby/library/set/sortedset/initialize_spec.rb
deleted file mode 100644
index 4d1707b72a..0000000000
--- a/spec/ruby/library/set/sortedset/initialize_spec.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#initialize" do
- it "is private" do
- SortedSet.should have_private_instance_method("initialize")
- end
-
- it "adds all elements of the passed Enumerable to self" do
- s = SortedSet.new([1, 2, 3])
- s.size.should eql(3)
- s.should include(1)
- s.should include(2)
- s.should include(3)
- end
-
- it "preprocesses all elements by a passed block before adding to self" do
- s = SortedSet.new([1, 2, 3]) { |x| x * x }
- s.size.should eql(3)
- s.should include(1)
- s.should include(4)
- s.should include(9)
- end
-
- it "raises on incompatible <=> comparison" do
- # Use #to_a here as elements are sorted only when needed.
- # Therefore the <=> incompatibility is only noticed on sorting.
- -> { SortedSet.new(['00', nil]).to_a }.should raise_error(ArgumentError)
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/inspect_spec.rb b/spec/ruby/library/set/sortedset/inspect_spec.rb
deleted file mode 100644
index 1c4dd9e6e2..0000000000
--- a/spec/ruby/library/set/sortedset/inspect_spec.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#inspect" do
- it "returns a String representation of self" do
- SortedSet[].inspect.should be_kind_of(String)
- SortedSet[1, 2, 3].inspect.should be_kind_of(String)
- SortedSet["1", "2", "3"].inspect.should be_kind_of(String)
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/intersection_spec.rb b/spec/ruby/library/set/sortedset/intersection_spec.rb
deleted file mode 100644
index 6daa271b73..0000000000
--- a/spec/ruby/library/set/sortedset/intersection_spec.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require_relative 'shared/intersection'
- require 'set'
-
- describe "SortedSet#intersection" do
- it_behaves_like :sorted_set_intersection, :intersection
- end
-
- describe "SortedSet#&" do
- it_behaves_like :sorted_set_intersection, :&
- end
-end
diff --git a/spec/ruby/library/set/sortedset/keep_if_spec.rb b/spec/ruby/library/set/sortedset/keep_if_spec.rb
deleted file mode 100644
index 3e5f3bbc47..0000000000
--- a/spec/ruby/library/set/sortedset/keep_if_spec.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#keep_if" do
- before :each do
- @set = SortedSet["one", "two", "three"]
- end
-
- it "yields each Object in self in sorted order" do
- ret = []
- @set.keep_if { |x| ret << x }
- ret.should == ["one", "two", "three"].sort
- end
-
- it "keeps every element from self for which the passed block returns true" do
- @set.keep_if { |x| x.size != 3 }
- @set.to_a.should == ["three"]
- end
-
- it "returns self" do
- @set.keep_if {}.should equal(@set)
- end
-
- it "returns an Enumerator when passed no block" do
- enum = @set.keep_if
- enum.should be_an_instance_of(Enumerator)
-
- enum.each { |x| x.size != 3 }
- @set.to_a.should == ["three"]
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/length_spec.rb b/spec/ruby/library/set/sortedset/length_spec.rb
deleted file mode 100644
index de6791f6bb..0000000000
--- a/spec/ruby/library/set/sortedset/length_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require_relative 'shared/length'
- require 'set'
-
- describe "SortedSet#length" do
- it_behaves_like :sorted_set_length, :length
- end
-end
diff --git a/spec/ruby/library/set/sortedset/map_spec.rb b/spec/ruby/library/set/sortedset/map_spec.rb
deleted file mode 100644
index 4971b9529b..0000000000
--- a/spec/ruby/library/set/sortedset/map_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
- require_relative 'shared/collect'
-
- describe "SortedSet#map!" do
- it_behaves_like :sorted_set_collect_bang, :map!
- end
-end
diff --git a/spec/ruby/library/set/sortedset/member_spec.rb b/spec/ruby/library/set/sortedset/member_spec.rb
deleted file mode 100644
index 142b09b651..0000000000
--- a/spec/ruby/library/set/sortedset/member_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require_relative 'shared/include'
- require 'set'
-
- describe "SortedSet#member?" do
- it_behaves_like :sorted_set_include, :member?
- end
-end
diff --git a/spec/ruby/library/set/sortedset/merge_spec.rb b/spec/ruby/library/set/sortedset/merge_spec.rb
deleted file mode 100644
index c4cbc6d2b4..0000000000
--- a/spec/ruby/library/set/sortedset/merge_spec.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#merge" do
- it "adds the elements of the passed Enumerable to self" do
- SortedSet["a", "b"].merge(SortedSet["b", "c", "d"]).should == SortedSet["a", "b", "c", "d"]
- SortedSet[1, 2].merge([3, 4]).should == SortedSet[1, 2, 3, 4]
- end
-
- it "returns self" do
- set = SortedSet[1, 2]
- set.merge([3, 4]).should equal(set)
- end
-
- it "raises an ArgumentError when passed a non-Enumerable" do
- -> { SortedSet[1, 2].merge(1) }.should raise_error(ArgumentError)
- -> { SortedSet[1, 2].merge(Object.new) }.should raise_error(ArgumentError)
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/minus_spec.rb b/spec/ruby/library/set/sortedset/minus_spec.rb
deleted file mode 100644
index d6abc5e204..0000000000
--- a/spec/ruby/library/set/sortedset/minus_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
- require_relative 'shared/difference'
-
- describe "SortedSet#-" do
- it_behaves_like :sorted_set_difference, :-
- end
-end
diff --git a/spec/ruby/library/set/sortedset/plus_spec.rb b/spec/ruby/library/set/sortedset/plus_spec.rb
deleted file mode 100644
index 13fc873ad1..0000000000
--- a/spec/ruby/library/set/sortedset/plus_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require_relative 'shared/union'
- require 'set'
-
- describe "SortedSet#+" do
- it_behaves_like :sorted_set_union, :+
- end
-end
diff --git a/spec/ruby/library/set/sortedset/pretty_print_cycle_spec.rb b/spec/ruby/library/set/sortedset/pretty_print_cycle_spec.rb
deleted file mode 100644
index e97f509406..0000000000
--- a/spec/ruby/library/set/sortedset/pretty_print_cycle_spec.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#pretty_print_cycle" do
- it "passes the 'pretty print' representation of a self-referencing SortedSet to the pretty print writer" do
- pp = mock("PrettyPrint")
- pp.should_receive(:text).with("#<SortedSet: {...}>")
- SortedSet[1, 2, 3].pretty_print_cycle(pp)
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/pretty_print_spec.rb b/spec/ruby/library/set/sortedset/pretty_print_spec.rb
deleted file mode 100644
index a8088bf797..0000000000
--- a/spec/ruby/library/set/sortedset/pretty_print_spec.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#pretty_print" do
- it "passes the 'pretty print' representation of self to the pretty print writer" do
- pp = mock("PrettyPrint")
- set = SortedSet[1, 2, 3]
-
- pp.should_receive(:text).with("#<SortedSet: {")
- pp.should_receive(:text).with("}>")
-
- pp.should_receive(:nest).with(1).and_yield
- pp.should_receive(:seplist).with(set)
-
- set.pretty_print(pp)
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/proper_subset_spec.rb b/spec/ruby/library/set/sortedset/proper_subset_spec.rb
deleted file mode 100644
index 34fb89d13d..0000000000
--- a/spec/ruby/library/set/sortedset/proper_subset_spec.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#proper_subset?" do
- before :each do
- @set = SortedSet[1, 2, 3, 4]
- end
-
- it "returns true if passed a SortedSet that self is a proper subset of" do
- SortedSet[].proper_subset?(@set).should be_true
- SortedSet[].proper_subset?(SortedSet[1, 2, 3]).should be_true
- SortedSet[].proper_subset?(SortedSet["a", "b", "c"]).should be_true
-
- SortedSet[1, 2, 3].proper_subset?(@set).should be_true
- SortedSet[1, 3].proper_subset?(@set).should be_true
- SortedSet[1, 2].proper_subset?(@set).should be_true
- SortedSet[1].proper_subset?(@set).should be_true
-
- SortedSet[5].proper_subset?(@set).should be_false
- SortedSet[1, 5].proper_subset?(@set).should be_false
- SortedSet["test"].proper_subset?(@set).should be_false
-
- @set.proper_subset?(@set).should be_false
- SortedSet[].proper_subset?(SortedSet[]).should be_false
- end
-
- it "raises an ArgumentError when passed a non-SortedSet" do
- -> { SortedSet[].proper_subset?([]) }.should raise_error(ArgumentError)
- -> { SortedSet[].proper_subset?(1) }.should raise_error(ArgumentError)
- -> { SortedSet[].proper_subset?("test") }.should raise_error(ArgumentError)
- -> { SortedSet[].proper_subset?(Object.new) }.should raise_error(ArgumentError)
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/proper_superset_spec.rb b/spec/ruby/library/set/sortedset/proper_superset_spec.rb
deleted file mode 100644
index 8b92444f72..0000000000
--- a/spec/ruby/library/set/sortedset/proper_superset_spec.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#proper_superset?" do
- before :each do
- @set = SortedSet[1, 2, 3, 4]
- end
-
- it "returns true if passed a SortedSet that self is a proper superset of" do
- @set.proper_superset?(SortedSet[]).should be_true
- SortedSet[1, 2, 3].proper_superset?(SortedSet[]).should be_true
- SortedSet["a", "b", "c"].proper_superset?(SortedSet[]).should be_true
-
- @set.proper_superset?(SortedSet[1, 2, 3]).should be_true
- @set.proper_superset?(SortedSet[1, 3]).should be_true
- @set.proper_superset?(SortedSet[1, 2]).should be_true
- @set.proper_superset?(SortedSet[1]).should be_true
-
- @set.proper_superset?(SortedSet[5]).should be_false
- @set.proper_superset?(SortedSet[1, 5]).should be_false
- @set.proper_superset?(SortedSet["test"]).should be_false
-
- @set.proper_superset?(@set).should be_false
- SortedSet[].proper_superset?(SortedSet[]).should be_false
- end
-
- it "raises an ArgumentError when passed a non-SortedSet" do
- -> { SortedSet[].proper_superset?([]) }.should raise_error(ArgumentError)
- -> { SortedSet[].proper_superset?(1) }.should raise_error(ArgumentError)
- -> { SortedSet[].proper_superset?("test") }.should raise_error(ArgumentError)
- -> { SortedSet[].proper_superset?(Object.new) }.should raise_error(ArgumentError)
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/reject_spec.rb b/spec/ruby/library/set/sortedset/reject_spec.rb
deleted file mode 100644
index 396b864cc5..0000000000
--- a/spec/ruby/library/set/sortedset/reject_spec.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#reject!" do
- before :each do
- @set = SortedSet["one", "two", "three"]
- end
-
- it "yields each Object in self in sorted order" do
- res = []
- @set.reject! { |x| res << x }
- res.should == ["one", "two", "three"].sort
- end
-
- it "deletes every element from self for which the passed block returns true" do
- @set.reject! { |x| x.size == 3 }
- @set.size.should eql(1)
-
- @set.should_not include("one")
- @set.should_not include("two")
- @set.should include("three")
- end
-
- it "returns self when self was modified" do
- @set.reject! { |x| true }.should equal(@set)
- end
-
- it "returns nil when self was not modified" do
- @set.reject! { |x| false }.should be_nil
- end
-
- it "returns an Enumerator when passed no block" do
- enum = @set.reject!
- enum.should be_an_instance_of(Enumerator)
-
- enum.each { |x| x.size == 3 }
-
- @set.should_not include("one")
- @set.should_not include("two")
- @set.should include("three")
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/replace_spec.rb b/spec/ruby/library/set/sortedset/replace_spec.rb
deleted file mode 100644
index 2900221c01..0000000000
--- a/spec/ruby/library/set/sortedset/replace_spec.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#replace" do
- before :each do
- @set = SortedSet["a", "b", "c"]
- end
-
- it "replaces the contents with other and returns self" do
- @set.replace(SortedSet[1, 2, 3]).should == @set
- @set.should == SortedSet[1, 2, 3]
- end
-
- it "accepts any enumerable as other" do
- @set.replace([1, 2, 3]).should == SortedSet[1, 2, 3]
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/select_spec.rb b/spec/ruby/library/set/sortedset/select_spec.rb
deleted file mode 100644
index fc4c15ee4d..0000000000
--- a/spec/ruby/library/set/sortedset/select_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require_relative 'shared/select'
- require 'set'
-
- describe "SortedSet#select!" do
- it_behaves_like :sorted_set_select_bang, :select!
- end
-end
diff --git a/spec/ruby/library/set/sortedset/shared/add.rb b/spec/ruby/library/set/sortedset/shared/add.rb
deleted file mode 100644
index 95ef1b090e..0000000000
--- a/spec/ruby/library/set/sortedset/shared/add.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-describe :sorted_set_add, shared: true do
- before :each do
- @set = SortedSet.new
- end
-
- it "adds the passed Object to self" do
- @set.send(@method, "dog")
- @set.should include("dog")
- end
-
- it "returns self" do
- @set.send(@method, "dog").should equal(@set)
- end
-end
diff --git a/spec/ruby/library/set/sortedset/shared/collect.rb b/spec/ruby/library/set/sortedset/shared/collect.rb
deleted file mode 100644
index e53304d427..0000000000
--- a/spec/ruby/library/set/sortedset/shared/collect.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-describe :sorted_set_collect_bang, shared: true do
- before :each do
- @set = SortedSet[1, 2, 3, 4, 5]
- end
-
- it "yields each Object in self in sorted order" do
- res = []
- SortedSet["one", "two", "three"].send(@method) { |x| res << x; x }
- res.should == ["one", "two", "three"].sort
- end
-
- it "returns self" do
- @set.send(@method) { |x| x }.should equal(@set)
- end
-
- it "replaces self with the return values of the block" do
- @set.send(@method) { |x| x * 2 }
- @set.should == SortedSet[2, 4, 6, 8, 10]
- end
-end
diff --git a/spec/ruby/library/set/sortedset/shared/difference.rb b/spec/ruby/library/set/sortedset/shared/difference.rb
deleted file mode 100644
index 688e23a7a7..0000000000
--- a/spec/ruby/library/set/sortedset/shared/difference.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-describe :sorted_set_difference, shared: true do
- before :each do
- @set = SortedSet["a", "b", "c"]
- end
-
- it "returns a new SortedSet containing self's elements excluding the elements in the passed Enumerable" do
- @set.send(@method, SortedSet["a", "b"]).should == SortedSet["c"]
- @set.send(@method, ["b", "c"]).should == SortedSet["a"]
- end
-
- it "raises an ArgumentError when passed a non-Enumerable" do
- -> { @set.send(@method, 1) }.should raise_error(ArgumentError)
- -> { @set.send(@method, Object.new) }.should raise_error(ArgumentError)
- end
-end
diff --git a/spec/ruby/library/set/sortedset/shared/include.rb b/spec/ruby/library/set/sortedset/shared/include.rb
deleted file mode 100644
index cd1758819d..0000000000
--- a/spec/ruby/library/set/sortedset/shared/include.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-describe :sorted_set_include, shared: true do
- it "returns true when self contains the passed Object" do
- set = SortedSet["a", "b", "c"]
- set.send(@method, "a").should be_true
- set.send(@method, "e").should be_false
- end
-end
diff --git a/spec/ruby/library/set/sortedset/shared/intersection.rb b/spec/ruby/library/set/sortedset/shared/intersection.rb
deleted file mode 100644
index 045716ad05..0000000000
--- a/spec/ruby/library/set/sortedset/shared/intersection.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-describe :sorted_set_intersection, shared: true do
- before :each do
- @set = SortedSet["a", "b", "c"]
- end
-
- it "returns a new SortedSet containing only elements shared by self and the passed Enumerable" do
- @set.send(@method, SortedSet["b", "c", "d", "e"]).should == SortedSet["b", "c"]
- @set.send(@method, ["b", "c", "d"]).should == SortedSet["b", "c"]
- end
-
- it "raises an ArgumentError when passed a non-Enumerable" do
- -> { @set.send(@method, 1) }.should raise_error(ArgumentError)
- -> { @set.send(@method, Object.new) }.should raise_error(ArgumentError)
- end
-end
diff --git a/spec/ruby/library/set/sortedset/shared/length.rb b/spec/ruby/library/set/sortedset/shared/length.rb
deleted file mode 100644
index d1dfee1cff..0000000000
--- a/spec/ruby/library/set/sortedset/shared/length.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-describe :sorted_set_length, shared: true do
- it "returns the number of elements in the set" do
- set = SortedSet["a", "b", "c"]
- set.send(@method).should == 3
- end
-end
diff --git a/spec/ruby/library/set/sortedset/shared/select.rb b/spec/ruby/library/set/sortedset/shared/select.rb
deleted file mode 100644
index e13311eda5..0000000000
--- a/spec/ruby/library/set/sortedset/shared/select.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-require_relative '../../../../spec_helper'
-require 'set'
-
-describe :sorted_set_select_bang, shared: true do
- before :each do
- @set = SortedSet["one", "two", "three"]
- end
-
- it "yields each Object in self in sorted order" do
- res = []
- @set.send(@method) { |x| res << x }
- res.should == ["one", "two", "three"].sort
- end
-
- it "keeps every element from self for which the passed block returns true" do
- @set.send(@method) { |x| x.size != 3 }
- @set.to_a.should == ["three"]
- end
-
- it "returns self when self was modified" do
- @set.send(@method) { false }.should equal(@set)
- end
-
- it "returns nil when self was not modified" do
- @set.send(@method) { true }.should be_nil
- end
-
- it "returns an Enumerator when passed no block" do
- enum = @set.send(@method)
- enum.should be_an_instance_of(Enumerator)
-
- enum.each { |x| x.size != 3 }
- @set.to_a.should == ["three"]
- end
-end
diff --git a/spec/ruby/library/set/sortedset/shared/union.rb b/spec/ruby/library/set/sortedset/shared/union.rb
deleted file mode 100644
index 9015bdc8e3..0000000000
--- a/spec/ruby/library/set/sortedset/shared/union.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-describe :sorted_set_union, shared: true do
- before :each do
- @set = SortedSet["a", "b", "c"]
- end
-
- it "returns a new SortedSet containing all elements of self and the passed Enumerable" do
- @set.send(@method, SortedSet["b", "d", "e"]).should == SortedSet["a", "b", "c", "d", "e"]
- @set.send(@method, ["b", "e"]).should == SortedSet["a", "b", "c", "e"]
- end
-
- it "raises an ArgumentError when passed a non-Enumerable" do
- -> { @set.send(@method, 1) }.should raise_error(ArgumentError)
- -> { @set.send(@method, Object.new) }.should raise_error(ArgumentError)
- end
-end
diff --git a/spec/ruby/library/set/sortedset/size_spec.rb b/spec/ruby/library/set/sortedset/size_spec.rb
deleted file mode 100644
index d908b33b53..0000000000
--- a/spec/ruby/library/set/sortedset/size_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require_relative 'shared/length'
- require 'set'
-
- describe "SortedSet#size" do
- it_behaves_like :sorted_set_length, :size
- end
-end
diff --git a/spec/ruby/library/set/sortedset/sortedset_spec.rb b/spec/ruby/library/set/sortedset/sortedset_spec.rb
index 3ead5495fc..67993dee29 100644
--- a/spec/ruby/library/set/sortedset/sortedset_spec.rb
+++ b/spec/ruby/library/set/sortedset/sortedset_spec.rb
@@ -1,22 +1,12 @@
require_relative '../../../spec_helper'
require 'set'
-ruby_version_is "3.0" do
- describe "SortedSet" do
- it "raises error including message that it has been extracted from the set stdlib" do
- -> {
- SortedSet
- }.should raise_error(RuntimeError) { |e|
- e.message.should.include?("The `SortedSet` class has been extracted from the `set` library")
- }
- end
- end
-end
-
-ruby_version_is ""..."3.0" do
- describe "SortedSet" do
- it "is part of the set stdlib" do
- SortedSet.superclass.should == Set
- end
+describe "SortedSet" do
+ it "raises error including message that it has been extracted from the set stdlib" do
+ -> {
+ SortedSet
+ }.should raise_error(RuntimeError) { |e|
+ e.message.should.include?("The `SortedSet` class has been extracted from the `set` library")
+ }
end
end
diff --git a/spec/ruby/library/set/sortedset/subset_spec.rb b/spec/ruby/library/set/sortedset/subset_spec.rb
deleted file mode 100644
index 272e3f985e..0000000000
--- a/spec/ruby/library/set/sortedset/subset_spec.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#subset?" do
- before :each do
- @set = SortedSet[1, 2, 3, 4]
- end
-
- it "returns true if passed a SortedSet that is equal to self or self is a subset of" do
- @set.subset?(@set).should be_true
- SortedSet[].subset?(SortedSet[]).should be_true
-
- SortedSet[].subset?(@set).should be_true
- SortedSet[].subset?(SortedSet[1, 2, 3]).should be_true
- SortedSet[].subset?(SortedSet["a", "b", "c"]).should be_true
-
- SortedSet[1, 2, 3].subset?(@set).should be_true
- SortedSet[1, 3].subset?(@set).should be_true
- SortedSet[1, 2].subset?(@set).should be_true
- SortedSet[1].subset?(@set).should be_true
-
- SortedSet[5].subset?(@set).should be_false
- SortedSet[1, 5].subset?(@set).should be_false
- SortedSet["test"].subset?(@set).should be_false
- end
-
- it "raises an ArgumentError when passed a non-SortedSet" do
- -> { SortedSet[].subset?([]) }.should raise_error(ArgumentError)
- -> { SortedSet[].subset?(1) }.should raise_error(ArgumentError)
- -> { SortedSet[].subset?("test") }.should raise_error(ArgumentError)
- -> { SortedSet[].subset?(Object.new) }.should raise_error(ArgumentError)
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/subtract_spec.rb b/spec/ruby/library/set/sortedset/subtract_spec.rb
deleted file mode 100644
index b2af127f89..0000000000
--- a/spec/ruby/library/set/sortedset/subtract_spec.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#subtract" do
- before :each do
- @set = SortedSet["a", "b", "c"]
- end
-
- it "deletes any elements contained in other and returns self" do
- @set.subtract(SortedSet["b", "c"]).should == @set
- @set.should == SortedSet["a"]
- end
-
- it "accepts any enumerable as other" do
- @set.subtract(["c"]).should == SortedSet["a", "b"]
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/superset_spec.rb b/spec/ruby/library/set/sortedset/superset_spec.rb
deleted file mode 100644
index a1bbacb966..0000000000
--- a/spec/ruby/library/set/sortedset/superset_spec.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#superset?" do
- before :each do
- @set = SortedSet[1, 2, 3, 4]
- end
-
- it "returns true if passed a SortedSet that equals self or self is a proper superset of" do
- @set.superset?(@set).should be_true
- SortedSet[].superset?(SortedSet[]).should be_true
-
- @set.superset?(SortedSet[]).should be_true
- SortedSet[1, 2, 3].superset?(SortedSet[]).should be_true
- SortedSet["a", "b", "c"].superset?(SortedSet[]).should be_true
-
- @set.superset?(SortedSet[1, 2, 3]).should be_true
- @set.superset?(SortedSet[1, 3]).should be_true
- @set.superset?(SortedSet[1, 2]).should be_true
- @set.superset?(SortedSet[1]).should be_true
-
- @set.superset?(SortedSet[5]).should be_false
- @set.superset?(SortedSet[1, 5]).should be_false
- @set.superset?(SortedSet["test"]).should be_false
- end
-
- it "raises an ArgumentError when passed a non-SortedSet" do
- -> { SortedSet[].superset?([]) }.should raise_error(ArgumentError)
- -> { SortedSet[].superset?(1) }.should raise_error(ArgumentError)
- -> { SortedSet[].superset?("test") }.should raise_error(ArgumentError)
- -> { SortedSet[].superset?(Object.new) }.should raise_error(ArgumentError)
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/to_a_spec.rb b/spec/ruby/library/set/sortedset/to_a_spec.rb
deleted file mode 100644
index bb54cd7cdb..0000000000
--- a/spec/ruby/library/set/sortedset/to_a_spec.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require 'set'
-
- describe "SortedSet#to_a" do
- it "returns an array containing elements" do
- set = SortedSet.new [1, 2, 3]
- set.to_a.should == [1, 2, 3]
- end
-
- it "returns a sorted array containing elements" do
- set = SortedSet[2, 3, 1]
- set.to_a.should == [1, 2, 3]
-
- set = SortedSet.new [5, 6, 4, 4]
- set.to_a.should == [4, 5, 6]
- end
- end
-end
diff --git a/spec/ruby/library/set/sortedset/union_spec.rb b/spec/ruby/library/set/sortedset/union_spec.rb
deleted file mode 100644
index c942f20d3e..0000000000
--- a/spec/ruby/library/set/sortedset/union_spec.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is ""..."3.0" do
- require_relative 'shared/union'
- require 'set'
-
- describe "SortedSet#union" do
- it_behaves_like :sorted_set_union, :union
- end
-
- describe "SortedSet#|" do
- it_behaves_like :sorted_set_union, :|
- end
-end
diff --git a/spec/ruby/library/set/subset_spec.rb b/spec/ruby/library/set/subset_spec.rb
index f375efa6df..85666d633f 100644
--- a/spec/ruby/library/set/subset_spec.rb
+++ b/spec/ruby/library/set/subset_spec.rb
@@ -1,6 +1,7 @@
require_relative '../../spec_helper'
require_relative 'fixtures/set_like'
require 'set'
+set_version = defined?(Set::VERSION) ? Set::VERSION : '1.0.0'
describe "Set#subset?" do
before :each do
@@ -33,9 +34,11 @@ describe "Set#subset?" do
-> { Set[].subset?(Object.new) }.should raise_error(ArgumentError)
end
- context "when comparing to a Set-like object" do
- it "returns true if passed a Set-like object that self is a subset of" do
- Set[1, 2, 3].subset?(SetSpecs::SetLike.new([1, 2, 3, 4])).should be_true
+ version_is(set_version, ""..."1.1.0") do #ruby_version_is ""..."3.3" do
+ context "when comparing to a Set-like object" do
+ it "returns true if passed a Set-like object that self is a subset of" do
+ Set[1, 2, 3].subset?(SetSpecs::SetLike.new([1, 2, 3, 4])).should be_true
+ end
end
end
end
diff --git a/spec/ruby/library/shellwords/shellwords_spec.rb b/spec/ruby/library/shellwords/shellwords_spec.rb
index 2975fd9974..fe86b6faab 100644
--- a/spec/ruby/library/shellwords/shellwords_spec.rb
+++ b/spec/ruby/library/shellwords/shellwords_spec.rb
@@ -1,34 +1,33 @@
require_relative '../../spec_helper'
require 'shellwords'
-include Shellwords
describe "Shellwords#shellwords" do
it "honors quoted strings" do
- shellwords('a "b b" a').should == ['a', 'b b', 'a']
+ Shellwords.shellwords('a "b b" a').should == ['a', 'b b', 'a']
end
it "honors escaped double quotes" do
- shellwords('a "\"b\" c" d').should == ['a', '"b" c', 'd']
+ Shellwords.shellwords('a "\"b\" c" d').should == ['a', '"b" c', 'd']
end
it "honors escaped single quotes" do
- shellwords("a \"'b' c\" d").should == ['a', "'b' c", 'd']
+ Shellwords.shellwords("a \"'b' c\" d").should == ['a', "'b' c", 'd']
end
it "honors escaped spaces" do
- shellwords('a b\ c d').should == ['a', 'b c', 'd']
+ Shellwords.shellwords('a b\ c d').should == ['a', 'b c', 'd']
end
it "raises ArgumentError when double quoted strings are misquoted" do
- -> { shellwords('a "b c d e') }.should raise_error(ArgumentError)
+ -> { Shellwords.shellwords('a "b c d e') }.should raise_error(ArgumentError)
end
it "raises ArgumentError when single quoted strings are misquoted" do
- -> { shellwords("a 'b c d e") }.should raise_error(ArgumentError)
+ -> { Shellwords.shellwords("a 'b c d e") }.should raise_error(ArgumentError)
end
# https://bugs.ruby-lang.org/issues/10055
it "matches POSIX sh behavior for backslashes within double quoted strings" do
- shellsplit('printf "%s\n"').should == ['printf', '%s\n']
+ Shellwords.shellsplit('printf "%s\n"').should == ['printf', '%s\n']
end
end
diff --git a/spec/ruby/library/socket/addrinfo/shared/to_sockaddr.rb b/spec/ruby/library/socket/addrinfo/shared/to_sockaddr.rb
index c32da5986d..4f7cf439a0 100644
--- a/spec/ruby/library/socket/addrinfo/shared/to_sockaddr.rb
+++ b/spec/ruby/library/socket/addrinfo/shared/to_sockaddr.rb
@@ -1,5 +1,4 @@
-describe :socket_addrinfo_to_sockaddr, :shared => true do
-
+describe :socket_addrinfo_to_sockaddr, shared: true do
describe "for an ipv4 socket" do
before :each do
@addrinfo = Addrinfo.tcp("127.0.0.1", 80)
@@ -47,5 +46,4 @@ describe :socket_addrinfo_to_sockaddr, :shared => true do
addr.send(@method).should == Socket.sockaddr_in(0, '')
end
end
-
end
diff --git a/spec/ruby/library/socket/basicsocket/read_nonblock_spec.rb b/spec/ruby/library/socket/basicsocket/read_nonblock_spec.rb
index df44a50afa..ea5e65da5c 100644
--- a/spec/ruby/library/socket/basicsocket/read_nonblock_spec.rb
+++ b/spec/ruby/library/socket/basicsocket/read_nonblock_spec.rb
@@ -18,7 +18,37 @@ describe "BasicSocket#read_nonblock" do
it "receives data after it's ready" do
IO.select([@r], nil, nil, 2)
- @r.recv_nonblock(5).should == "aaa"
+ @r.read_nonblock(5).should == "aaa"
+ end
+
+ platform_is_not :windows do
+ it 'returned data is binary encoded regardless of the external encoding' do
+ IO.select([@r], nil, nil, 2)
+ @r.read_nonblock(1).encoding.should == Encoding::BINARY
+
+ @w.send("bbb", 0, @r.getsockname)
+ @r.set_encoding(Encoding::ISO_8859_1)
+ IO.select([@r], nil, nil, 2)
+ buffer = @r.read_nonblock(3)
+ buffer.should == "bbb"
+ buffer.encoding.should == Encoding::BINARY
+ end
+ end
+
+ it 'replaces the content of the provided buffer without changing its encoding' do
+ buffer = "initial data".dup.force_encoding(Encoding::UTF_8)
+
+ IO.select([@r], nil, nil, 2)
+ @r.read_nonblock(3, buffer)
+ buffer.should == "aaa"
+ buffer.encoding.should == Encoding::UTF_8
+
+ @w.send("bbb", 0, @r.getsockname)
+ @r.set_encoding(Encoding::ISO_8859_1)
+ IO.select([@r], nil, nil, 2)
+ @r.read_nonblock(3, buffer)
+ buffer.should == "bbb"
+ buffer.encoding.should == Encoding::UTF_8
end
platform_is :linux do
diff --git a/spec/ruby/library/socket/basicsocket/read_spec.rb b/spec/ruby/library/socket/basicsocket/read_spec.rb
new file mode 100644
index 0000000000..ba9de7d5cf
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/read_spec.rb
@@ -0,0 +1,47 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "BasicSocket#read" do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before :each do
+ @r = Socket.new(family, :DGRAM)
+ @w = Socket.new(family, :DGRAM)
+
+ @r.bind(Socket.pack_sockaddr_in(0, ip_address))
+ @w.send("aaa", 0, @r.getsockname)
+ end
+
+ after :each do
+ @r.close unless @r.closed?
+ @w.close unless @w.closed?
+ end
+
+ it "receives data after it's ready" do
+ @r.read(3).should == "aaa"
+ end
+
+ it 'returned data is binary encoded regardless of the external encoding' do
+ @r.read(3).encoding.should == Encoding::BINARY
+
+ @w.send("bbb", 0, @r.getsockname)
+ @r.set_encoding(Encoding::UTF_8)
+ buffer = @r.read(3)
+ buffer.should == "bbb"
+ buffer.encoding.should == Encoding::BINARY
+ end
+
+ it 'replaces the content of the provided buffer without changing its encoding' do
+ buffer = "initial data".dup.force_encoding(Encoding::UTF_8)
+
+ @r.read(3, buffer)
+ buffer.should == "aaa"
+ buffer.encoding.should == Encoding::UTF_8
+
+ @w.send("bbb", 0, @r.getsockname)
+ @r.set_encoding(Encoding::ISO_8859_1)
+ @r.read(3, buffer)
+ buffer.should == "bbb"
+ buffer.encoding.should == Encoding::UTF_8
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/recv_nonblock_spec.rb b/spec/ruby/library/socket/basicsocket/recv_nonblock_spec.rb
index b6ab8a9cea..17c846054d 100644
--- a/spec/ruby/library/socket/basicsocket/recv_nonblock_spec.rb
+++ b/spec/ruby/library/socket/basicsocket/recv_nonblock_spec.rb
@@ -52,7 +52,7 @@ describe "Socket::BasicSocket#recv_nonblock" do
@s2.send("data", 0, @s1.getsockname)
IO.select([@s1], nil, nil, 2)
- buf = "foo"
+ buf = +"foo"
@s1.recv_nonblock(5, 0, buf)
buf.should == "data"
end
diff --git a/spec/ruby/library/socket/basicsocket/recv_spec.rb b/spec/ruby/library/socket/basicsocket/recv_spec.rb
index b6ccda5d00..9fe8c52f9a 100644
--- a/spec/ruby/library/socket/basicsocket/recv_spec.rb
+++ b/spec/ruby/library/socket/basicsocket/recv_spec.rb
@@ -32,6 +32,25 @@ describe "BasicSocket#recv" do
ScratchPad.recorded.should == 'hello'
end
+ ruby_version_is "3.3" do
+ it "returns nil on a closed stream socket" do
+ t = Thread.new do
+ client = @server.accept
+ packet = client.recv(10)
+ client.close
+ packet
+ end
+
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ socket = TCPSocket.new('127.0.0.1', @port)
+ socket.close
+
+ t.value.should be_nil
+ end
+ end
+
platform_is_not :solaris do
it "accepts flags to specify unusual receiving behaviour" do
t = Thread.new do
@@ -81,7 +100,7 @@ describe "BasicSocket#recv" do
socket.write("data")
client = @server.accept
- buf = "foo"
+ buf = +"foo"
begin
client.recv(4, 0, buf)
ensure
diff --git a/spec/ruby/library/socket/basicsocket/send_spec.rb b/spec/ruby/library/socket/basicsocket/send_spec.rb
index 868801df30..86b5567026 100644
--- a/spec/ruby/library/socket/basicsocket/send_spec.rb
+++ b/spec/ruby/library/socket/basicsocket/send_spec.rb
@@ -17,12 +17,12 @@ describe "BasicSocket#send" do
end
it "sends a message to another socket and returns the number of bytes sent" do
- data = ""
+ data = +""
t = Thread.new do
client = @server.accept
loop do
got = client.recv(5)
- break if got.empty?
+ break if got.nil? || got.empty?
data << got
end
client.close
@@ -62,12 +62,12 @@ describe "BasicSocket#send" do
end
it "accepts a sockaddr as recipient address" do
- data = ""
+ data = +""
t = Thread.new do
client = @server.accept
loop do
got = client.recv(5)
- break if got.empty?
+ break if got.nil? || got.empty?
data << got
end
client.close
diff --git a/spec/ruby/library/socket/basicsocket/shutdown_spec.rb b/spec/ruby/library/socket/basicsocket/shutdown_spec.rb
index 41d9581bde..c78b32de38 100644
--- a/spec/ruby/library/socket/basicsocket/shutdown_spec.rb
+++ b/spec/ruby/library/socket/basicsocket/shutdown_spec.rb
@@ -23,7 +23,7 @@ platform_is_not :windows do # hangs
it 'shuts down a socket for reading' do
@client.shutdown(Socket::SHUT_RD)
- @client.recv(1).should be_empty
+ @client.recv(1).to_s.should be_empty
end
it 'shuts down a socket for writing' do
@@ -35,7 +35,7 @@ platform_is_not :windows do # hangs
it 'shuts down a socket for reading and writing' do
@client.shutdown(Socket::SHUT_RDWR)
- @client.recv(1).should be_empty
+ @client.recv(1).to_s.should be_empty
-> { @client.write('hello') }.should raise_error(Errno::EPIPE)
end
@@ -49,13 +49,13 @@ platform_is_not :windows do # hangs
it 'shuts down a socket for reading using :RD' do
@client.shutdown(:RD)
- @client.recv(1).should be_empty
+ @client.recv(1).to_s.should be_empty
end
it 'shuts down a socket for reading using :SHUT_RD' do
@client.shutdown(:SHUT_RD)
- @client.recv(1).should be_empty
+ @client.recv(1).to_s.should be_empty
end
it 'shuts down a socket for writing using :WR' do
@@ -73,7 +73,7 @@ platform_is_not :windows do # hangs
it 'shuts down a socket for reading and writing' do
@client.shutdown(:RDWR)
- @client.recv(1).should be_empty
+ @client.recv(1).to_s.should be_empty
-> { @client.write('hello') }.should raise_error(Errno::EPIPE)
end
@@ -87,13 +87,13 @@ platform_is_not :windows do # hangs
it 'shuts down a socket for reading using "RD"' do
@client.shutdown('RD')
- @client.recv(1).should be_empty
+ @client.recv(1).to_s.should be_empty
end
it 'shuts down a socket for reading using "SHUT_RD"' do
@client.shutdown('SHUT_RD')
- @client.recv(1).should be_empty
+ @client.recv(1).to_s.should be_empty
end
it 'shuts down a socket for writing using "WR"' do
@@ -123,7 +123,7 @@ platform_is_not :windows do # hangs
@client.shutdown(@dummy)
- @client.recv(1).should be_empty
+ @client.recv(1).to_s.should be_empty
end
it 'shuts down a socket for reading using "SHUT_RD"' do
@@ -131,7 +131,7 @@ platform_is_not :windows do # hangs
@client.shutdown(@dummy)
- @client.recv(1).should be_empty
+ @client.recv(1).to_s.should be_empty
end
it 'shuts down a socket for reading and writing' do
@@ -139,7 +139,7 @@ platform_is_not :windows do # hangs
@client.shutdown(@dummy)
- @client.recv(1).should be_empty
+ @client.recv(1).to_s.should be_empty
-> { @client.write('hello') }.should raise_error(Errno::EPIPE)
end
diff --git a/spec/ruby/library/socket/fixtures/classes.rb b/spec/ruby/library/socket/fixtures/classes.rb
index 406bd7c710..786629d2ef 100644
--- a/spec/ruby/library/socket/fixtures/classes.rb
+++ b/spec/ruby/library/socket/fixtures/classes.rb
@@ -113,7 +113,7 @@ module SocketSpecs
begin
data = socket.recv(1024)
- return if data.empty?
+ return if data.nil? || data.empty?
log "SpecTCPServer received: #{data.inspect}"
return if data == "QUIT"
diff --git a/spec/ruby/library/socket/ipsocket/getaddress_spec.rb b/spec/ruby/library/socket/ipsocket/getaddress_spec.rb
index 746d2ab86b..96324982e5 100644
--- a/spec/ruby/library/socket/ipsocket/getaddress_spec.rb
+++ b/spec/ruby/library/socket/ipsocket/getaddress_spec.rb
@@ -19,7 +19,7 @@ describe "Socket::IPSocket#getaddress" do
# traditionally invalidly named ones.
it "raises an error on unknown hostnames" do
-> {
- IPSocket.getaddress("rubyspecdoesntexist.fallingsnow.net")
+ IPSocket.getaddress("rubyspecdoesntexist.ruby-lang.org")
}.should raise_error(SocketError)
end
end
diff --git a/spec/ruby/library/socket/shared/pack_sockaddr.rb b/spec/ruby/library/socket/shared/pack_sockaddr.rb
index 9f6238e7bc..26fdf682b1 100644
--- a/spec/ruby/library/socket/shared/pack_sockaddr.rb
+++ b/spec/ruby/library/socket/shared/pack_sockaddr.rb
@@ -17,6 +17,9 @@ describe :socket_pack_sockaddr_in, shared: true do
sockaddr_in = Socket.public_send(@method, nil, '127.0.0.1')
Socket.unpack_sockaddr_in(sockaddr_in).should == [0, '127.0.0.1']
+
+ sockaddr_in = Socket.public_send(@method, 80, Socket::INADDR_ANY)
+ Socket.unpack_sockaddr_in(sockaddr_in).should == [80, '0.0.0.0']
end
platform_is_not :solaris do
diff --git a/spec/ruby/library/socket/shared/partially_closable_sockets.rb b/spec/ruby/library/socket/shared/partially_closable_sockets.rb
index 1bdff08bf6..b1c2ebabe1 100644
--- a/spec/ruby/library/socket/shared/partially_closable_sockets.rb
+++ b/spec/ruby/library/socket/shared/partially_closable_sockets.rb
@@ -1,4 +1,4 @@
-describe "partially closable sockets", shared: true do
+describe :partially_closable_sockets, shared: true do
it "if the write end is closed then the other side can read past EOF without blocking" do
@s1.write("foo")
@s1.close_write
diff --git a/spec/ruby/library/socket/socket/getnameinfo_spec.rb b/spec/ruby/library/socket/socket/getnameinfo_spec.rb
index b406348aa8..4f13bf484d 100644
--- a/spec/ruby/library/socket/socket/getnameinfo_spec.rb
+++ b/spec/ruby/library/socket/socket/getnameinfo_spec.rb
@@ -70,7 +70,7 @@ describe 'Socket.getnameinfo' do
it 'raises SocketError or TypeError when using an invalid String' do
-> { Socket.getnameinfo('cats') }.should raise_error(Exception) { |e|
- [SocketError, TypeError].should include(e.class)
+ (e.is_a?(SocketError) || e.is_a?(TypeError)).should == true
}
end
diff --git a/spec/ruby/library/socket/socket/new_spec.rb b/spec/ruby/library/socket/socket/new_spec.rb
deleted file mode 100644
index b2ec607f6a..0000000000
--- a/spec/ruby/library/socket/socket/new_spec.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-require_relative '../spec_helper'
-require_relative '../fixtures/classes'
diff --git a/spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb b/spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb
index 63d4724453..ef2a2d4ba9 100644
--- a/spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb
+++ b/spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb
@@ -2,6 +2,6 @@ require_relative '../spec_helper'
require_relative '../fixtures/classes'
require_relative '../shared/pack_sockaddr'
-describe "Socket#pack_sockaddr_in" do
+describe "Socket.pack_sockaddr_in" do
it_behaves_like :socket_pack_sockaddr_in, :pack_sockaddr_in
end
diff --git a/spec/ruby/library/socket/socket/pair_spec.rb b/spec/ruby/library/socket/socket/pair_spec.rb
index 292eacd38d..8dd470a95e 100644
--- a/spec/ruby/library/socket/socket/pair_spec.rb
+++ b/spec/ruby/library/socket/socket/pair_spec.rb
@@ -2,6 +2,6 @@ require_relative '../spec_helper'
require_relative '../fixtures/classes'
require_relative '../shared/socketpair'
-describe "Socket#pair" do
+describe "Socket.pair" do
it_behaves_like :socket_socketpair, :pair
end
diff --git a/spec/ruby/library/socket/socket/socketpair_spec.rb b/spec/ruby/library/socket/socket/socketpair_spec.rb
index 5b8311124e..551c376d49 100644
--- a/spec/ruby/library/socket/socket/socketpair_spec.rb
+++ b/spec/ruby/library/socket/socket/socketpair_spec.rb
@@ -2,6 +2,6 @@ require_relative '../spec_helper'
require_relative '../fixtures/classes'
require_relative '../shared/socketpair'
-describe "Socket#socketpair" do
+describe "Socket.socketpair" do
it_behaves_like :socket_socketpair, :socketpair
end
diff --git a/spec/ruby/library/socket/tcpserver/accept_spec.rb b/spec/ruby/library/socket/tcpserver/accept_spec.rb
index d38d95e0e1..d8892cd5f0 100644
--- a/spec/ruby/library/socket/tcpserver/accept_spec.rb
+++ b/spec/ruby/library/socket/tcpserver/accept_spec.rb
@@ -69,7 +69,7 @@ describe "TCPServer#accept" do
Thread.pass while t.status and t.status != "sleep"
# Thread#backtrace uses SIGVTALRM on TruffleRuby and potentially other implementations.
# Sending a signal to a thread is not possible with Ruby APIs.
- t.backtrace.join("\n").should.include?("in `accept'")
+ t.backtrace.join("\n").should =~ /in [`'](?:TCPServer#)?accept'/
socket = TCPSocket.new('127.0.0.1', @port)
socket.write("OK")
@@ -114,6 +114,19 @@ describe 'TCPServer#accept' do
@socket = @server.accept
@socket.should be_an_instance_of(TCPSocket)
end
+
+ platform_is_not :windows do
+ it "returns a TCPSocket which is set to nonblocking" do
+ require 'io/nonblock'
+ @socket = @server.accept
+ @socket.should.nonblock?
+ end
+ end
+
+ it "returns a TCPSocket which is set to close on exec" do
+ @socket = @server.accept
+ @socket.should.close_on_exec?
+ end
end
end
end
diff --git a/spec/ruby/library/socket/tcpserver/new_spec.rb b/spec/ruby/library/socket/tcpserver/new_spec.rb
index 8d9696c9d8..dd1ba676bd 100644
--- a/spec/ruby/library/socket/tcpserver/new_spec.rb
+++ b/spec/ruby/library/socket/tcpserver/new_spec.rb
@@ -97,6 +97,12 @@ describe "TCPServer.new" do
addr[1].should be_kind_of(Integer)
end
+ it "does not use the given block and warns to use TCPServer::open" do
+ -> {
+ @server = TCPServer.new(0) { raise }
+ }.should complain(/warning: TCPServer::new\(\) does not take block; use TCPServer::open\(\) instead/)
+ end
+
it "raises Errno::EADDRNOTAVAIL when the address is unknown" do
-> { TCPServer.new("1.2.3.4", 0) }.should raise_error(Errno::EADDRNOTAVAIL)
end
diff --git a/spec/ruby/library/socket/tcpsocket/initialize_spec.rb b/spec/ruby/library/socket/tcpsocket/initialize_spec.rb
index 065c8f4190..d7feb9751b 100644
--- a/spec/ruby/library/socket/tcpsocket/initialize_spec.rb
+++ b/spec/ruby/library/socket/tcpsocket/initialize_spec.rb
@@ -4,6 +4,27 @@ require_relative 'shared/new'
describe 'TCPSocket#initialize' do
it_behaves_like :tcpsocket_new, :new
+
+ describe "with a running server" do
+ before :each do
+ @server = SocketSpecs::SpecTCPServer.new
+ @hostname = @server.hostname
+ end
+
+ after :each do
+ if @socket
+ @socket.write "QUIT"
+ @socket.close
+ end
+ @server.shutdown
+ end
+
+ it "does not use the given block and warns to use TCPSocket::open" do
+ -> {
+ @socket = TCPSocket.new(@hostname, @server.port, nil) { raise }
+ }.should complain(/warning: TCPSocket::new\(\) does not take block; use TCPSocket::open\(\) instead/)
+ end
+ end
end
describe 'TCPSocket#initialize' do
@@ -51,6 +72,19 @@ describe 'TCPSocket#initialize' do
@client.remote_address.ip_port.should == @server.local_address.ip_port
end
+ platform_is_not :windows do
+ it "creates a socket which is set to nonblocking" do
+ require 'io/nonblock'
+ @client = TCPSocket.new(ip_address, @port)
+ @client.should.nonblock?
+ end
+ end
+
+ it "creates a socket which is set to close on exec" do
+ @client = TCPSocket.new(ip_address, @port)
+ @client.should.close_on_exec?
+ end
+
describe 'using a local address and service' do
it 'binds the client socket to the local address and service' do
@client = TCPSocket.new(ip_address, @port, ip_address, 0)
diff --git a/spec/ruby/library/socket/tcpsocket/partially_closable_spec.rb b/spec/ruby/library/socket/tcpsocket/partially_closable_spec.rb
index a381627a39..d365ecd335 100644
--- a/spec/ruby/library/socket/tcpsocket/partially_closable_spec.rb
+++ b/spec/ruby/library/socket/tcpsocket/partially_closable_spec.rb
@@ -16,6 +16,6 @@ describe "TCPSocket partial closability" do
@s2.close
end
- it_should_behave_like "partially closable sockets"
+ it_should_behave_like :partially_closable_sockets
end
diff --git a/spec/ruby/library/socket/tcpsocket/shared/new.rb b/spec/ruby/library/socket/tcpsocket/shared/new.rb
index 5d91435a2a..90f8d7e6a2 100644
--- a/spec/ruby/library/socket/tcpsocket/shared/new.rb
+++ b/spec/ruby/library/socket/tcpsocket/shared/new.rb
@@ -14,11 +14,14 @@ describe :tcpsocket_new, shared: true do
}
end
- ruby_version_is "3.0"..."3.2" do
+ ruby_version_is ""..."3.2" do
it 'raises Errno::ETIMEDOUT with :connect_timeout when no server is listening on the given address' do
-> {
TCPSocket.send(@method, "192.0.2.1", 80, connect_timeout: 0)
}.should raise_error(Errno::ETIMEDOUT)
+ rescue Errno::ENETUNREACH
+ # In the case all network interfaces down.
+ # raise_error cannot deal with multiple expected exceptions
end
end
@@ -27,6 +30,9 @@ describe :tcpsocket_new, shared: true do
-> {
TCPSocket.send(@method, "192.0.2.1", 80, connect_timeout: 0)
}.should raise_error(IO::TimeoutError)
+ rescue Errno::ENETUNREACH
+ # In the case all network interfaces down.
+ # raise_error cannot deal with multiple expected exceptions
end
end
@@ -92,11 +98,9 @@ describe :tcpsocket_new, shared: true do
@socket.addr[2].should =~ /^#{@hostname}/
end
- ruby_version_is "3.0" do
- it "connects to a server when passed connect_timeout argument" do
- @socket = TCPSocket.send(@method, @hostname, @server.port, connect_timeout: 1)
- @socket.should be_an_instance_of(TCPSocket)
- end
+ it "connects to a server when passed connect_timeout argument" do
+ @socket = TCPSocket.send(@method, @hostname, @server.port, connect_timeout: 1)
+ @socket.should be_an_instance_of(TCPSocket)
end
end
end
diff --git a/spec/ruby/library/socket/udpsocket/initialize_spec.rb b/spec/ruby/library/socket/udpsocket/initialize_spec.rb
index 1d635149f7..ecf0043c10 100644
--- a/spec/ruby/library/socket/udpsocket/initialize_spec.rb
+++ b/spec/ruby/library/socket/udpsocket/initialize_spec.rb
@@ -30,6 +30,19 @@ describe 'UDPSocket#initialize' do
@socket.binmode?.should be_true
end
+ platform_is_not :windows do
+ it 'sets the socket to nonblock' do
+ require 'io/nonblock'
+ @socket = UDPSocket.new(:INET)
+ @socket.should.nonblock?
+ end
+ end
+
+ it 'sets the socket to close on exec' do
+ @socket = UDPSocket.new(:INET)
+ @socket.should.close_on_exec?
+ end
+
it 'raises Errno::EAFNOSUPPORT or Errno::EPROTONOSUPPORT when given an invalid address family' do
-> {
UDPSocket.new(666)
diff --git a/spec/ruby/library/socket/udpsocket/new_spec.rb b/spec/ruby/library/socket/udpsocket/new_spec.rb
index 6cc0cadbcb..79bfcb624d 100644
--- a/spec/ruby/library/socket/udpsocket/new_spec.rb
+++ b/spec/ruby/library/socket/udpsocket/new_spec.rb
@@ -26,6 +26,12 @@ describe 'UDPSocket.new' do
@socket.should be_an_instance_of(UDPSocket)
end
+ it "does not use the given block and warns to use UDPSocket::open" do
+ -> {
+ @socket = UDPSocket.new { raise }
+ }.should complain(/warning: UDPSocket::new\(\) does not take block; use UDPSocket::open\(\) instead/)
+ end
+
it 'raises Errno::EAFNOSUPPORT or Errno::EPROTONOSUPPORT if unsupported family passed' do
-> { UDPSocket.new(-1) }.should raise_error(SystemCallError) { |e|
[Errno::EAFNOSUPPORT, Errno::EPROTONOSUPPORT].should include(e.class)
diff --git a/spec/ruby/library/socket/udpsocket/send_spec.rb b/spec/ruby/library/socket/udpsocket/send_spec.rb
index 5d5de684af..6dd5f67bea 100644
--- a/spec/ruby/library/socket/udpsocket/send_spec.rb
+++ b/spec/ruby/library/socket/udpsocket/send_spec.rb
@@ -63,7 +63,7 @@ describe "UDPSocket#send" do
@msg[1][3].should == "127.0.0.1"
end
- it "raises EMSGSIZE if data is too too big" do
+ it "raises EMSGSIZE if data is too big" do
@socket = UDPSocket.open
begin
-> do
diff --git a/spec/ruby/library/socket/unixserver/accept_nonblock_spec.rb b/spec/ruby/library/socket/unixserver/accept_nonblock_spec.rb
index 30688b46b6..dba3de7359 100644
--- a/spec/ruby/library/socket/unixserver/accept_nonblock_spec.rb
+++ b/spec/ruby/library/socket/unixserver/accept_nonblock_spec.rb
@@ -1,9 +1,8 @@
require_relative '../spec_helper'
require_relative '../fixtures/classes'
-describe "UNIXServer#accept_nonblock" do
-
- platform_is_not :windows do
+with_feature :unix_socket do
+ describe "UNIXServer#accept_nonblock" do
before :each do
@path = SocketSpecs.socket_path
@server = UNIXServer.open(@path)
@@ -33,9 +32,7 @@ describe "UNIXServer#accept_nonblock" do
@server.accept_nonblock(exception: false).should == :wait_readable
end
end
-end
-with_feature :unix_socket do
describe 'UNIXServer#accept_nonblock' do
before do
@path = SocketSpecs.socket_path
diff --git a/spec/ruby/library/socket/unixserver/accept_spec.rb b/spec/ruby/library/socket/unixserver/accept_spec.rb
index c05fbe7f22..1305bc6220 100644
--- a/spec/ruby/library/socket/unixserver/accept_spec.rb
+++ b/spec/ruby/library/socket/unixserver/accept_spec.rb
@@ -1,7 +1,7 @@
require_relative '../spec_helper'
require_relative '../fixtures/classes'
-platform_is_not :windows do
+with_feature :unix_socket do
describe "UNIXServer#accept" do
before :each do
@path = SocketSpecs.socket_path
@@ -110,6 +110,17 @@ with_feature :unix_socket do
@socket = @server.accept
@socket.recv(5).should == 'hello'
end
+
+ it "is set to nonblocking" do
+ require 'io/nonblock'
+ @socket = @server.accept
+ @socket.should.nonblock?
+ end
+
+ it "is set to close on exec" do
+ @socket = @server.accept
+ @socket.should.close_on_exec?
+ end
end
end
end
diff --git a/spec/ruby/library/socket/unixserver/for_fd_spec.rb b/spec/ruby/library/socket/unixserver/for_fd_spec.rb
index 4f3816ad37..8cc55ef391 100644
--- a/spec/ruby/library/socket/unixserver/for_fd_spec.rb
+++ b/spec/ruby/library/socket/unixserver/for_fd_spec.rb
@@ -1,8 +1,8 @@
require_relative '../spec_helper'
require_relative '../fixtures/classes'
-platform_is_not :windows do
- describe "UNIXServer#for_fd" do
+with_feature :unix_socket do
+ describe "UNIXServer.for_fd" do
before :each do
@unix_path = SocketSpecs.socket_path
@unix = UNIXServer.new(@unix_path)
diff --git a/spec/ruby/library/socket/unixserver/new_spec.rb b/spec/ruby/library/socket/unixserver/new_spec.rb
index f831f40bc6..a160e3ce5c 100644
--- a/spec/ruby/library/socket/unixserver/new_spec.rb
+++ b/spec/ruby/library/socket/unixserver/new_spec.rb
@@ -1,6 +1,14 @@
require_relative '../spec_helper'
require_relative 'shared/new'
-describe "UNIXServer.new" do
- it_behaves_like :unixserver_new, :new
+with_feature :unix_socket do
+ describe "UNIXServer.new" do
+ it_behaves_like :unixserver_new, :new
+
+ it "does not use the given block and warns to use UNIXServer::open" do
+ -> {
+ @server = UNIXServer.new(@path) { raise }
+ }.should complain(/warning: UNIXServer::new\(\) does not take block; use UNIXServer::open\(\) instead/)
+ end
+ end
end
diff --git a/spec/ruby/library/socket/unixserver/open_spec.rb b/spec/ruby/library/socket/unixserver/open_spec.rb
index f2506d9f6f..16453dd3bd 100644
--- a/spec/ruby/library/socket/unixserver/open_spec.rb
+++ b/spec/ruby/library/socket/unixserver/open_spec.rb
@@ -2,10 +2,10 @@ require_relative '../spec_helper'
require_relative '../fixtures/classes'
require_relative 'shared/new'
-describe "UNIXServer.open" do
- it_behaves_like :unixserver_new, :open
+with_feature :unix_socket do
+ describe "UNIXServer.open" do
+ it_behaves_like :unixserver_new, :open
- platform_is_not :windows do
before :each do
@path = SocketSpecs.socket_path
end
diff --git a/spec/ruby/library/socket/unixserver/shared/new.rb b/spec/ruby/library/socket/unixserver/shared/new.rb
index 35395826c9..b537f2a871 100644
--- a/spec/ruby/library/socket/unixserver/shared/new.rb
+++ b/spec/ruby/library/socket/unixserver/shared/new.rb
@@ -2,21 +2,19 @@ require_relative '../../spec_helper'
require_relative '../../fixtures/classes'
describe :unixserver_new, shared: true do
- platform_is_not :windows do
- before :each do
- @path = SocketSpecs.socket_path
- end
+ before :each do
+ @path = SocketSpecs.socket_path
+ end
- after :each do
- @server.close if @server
- @server = nil
- SocketSpecs.rm_socket @path
- end
+ after :each do
+ @server.close if @server
+ @server = nil
+ SocketSpecs.rm_socket @path
+ end
- it "creates a new UNIXServer" do
- @server = UNIXServer.send(@method, @path)
- @server.path.should == @path
- @server.addr.should == ["AF_UNIX", @path]
- end
+ it "creates a new UNIXServer" do
+ @server = UNIXServer.send(@method, @path)
+ @server.path.should == @path
+ @server.addr.should == ["AF_UNIX", @path]
end
end
diff --git a/spec/ruby/library/socket/unixsocket/addr_spec.rb b/spec/ruby/library/socket/unixsocket/addr_spec.rb
index e8431bea16..d93e061312 100644
--- a/spec/ruby/library/socket/unixsocket/addr_spec.rb
+++ b/spec/ruby/library/socket/unixsocket/addr_spec.rb
@@ -1,9 +1,8 @@
require_relative '../spec_helper'
require_relative '../fixtures/classes'
-describe "UNIXSocket#addr" do
-
- platform_is_not :windows do
+with_feature :unix_socket do
+ describe "UNIXSocket#addr" do
before :each do
@path = SocketSpecs.socket_path
@server = UNIXServer.open(@path)
diff --git a/spec/ruby/library/socket/unixsocket/initialize_spec.rb b/spec/ruby/library/socket/unixsocket/initialize_spec.rb
index 13b6972f03..bf7896ab0e 100644
--- a/spec/ruby/library/socket/unixsocket/initialize_spec.rb
+++ b/spec/ruby/library/socket/unixsocket/initialize_spec.rb
@@ -33,6 +33,16 @@ with_feature :unix_socket do
it 'sets the socket to binmode' do
@socket.binmode?.should be_true
end
+
+ it 'sets the socket to nonblock' do
+ require 'io/nonblock'
+ @socket.should.nonblock?
+ end
+
+ it 'sets the socket to close on exec' do
+ @socket.should.close_on_exec?
+ end
+
end
end
end
diff --git a/spec/ruby/library/socket/unixsocket/inspect_spec.rb b/spec/ruby/library/socket/unixsocket/inspect_spec.rb
index d2e3cabbd3..a542ba6db5 100644
--- a/spec/ruby/library/socket/unixsocket/inspect_spec.rb
+++ b/spec/ruby/library/socket/unixsocket/inspect_spec.rb
@@ -1,8 +1,8 @@
require_relative '../spec_helper'
require_relative '../fixtures/classes'
-describe "UNIXSocket#inspect" do
- platform_is_not :windows do
+with_feature :unix_socket do
+ describe "UNIXSocket#inspect" do
it "returns sockets fd for unnamed sockets" do
begin
s1, s2 = UNIXSocket.socketpair
diff --git a/spec/ruby/library/socket/unixsocket/local_address_spec.rb b/spec/ruby/library/socket/unixsocket/local_address_spec.rb
index cbf315f9f4..734253e7f5 100644
--- a/spec/ruby/library/socket/unixsocket/local_address_spec.rb
+++ b/spec/ruby/library/socket/unixsocket/local_address_spec.rb
@@ -46,9 +46,7 @@ with_feature :unix_socket do
end
end
end
-end
-with_feature :unix_socket do
describe 'UNIXSocket#local_address with a UNIX socket pair' do
before :each do
@sock, @sock2 = Socket.pair(Socket::AF_UNIX, Socket::SOCK_STREAM)
diff --git a/spec/ruby/library/socket/unixsocket/new_spec.rb b/spec/ruby/library/socket/unixsocket/new_spec.rb
index 05a6b3eda2..6d8ea6dcfe 100644
--- a/spec/ruby/library/socket/unixsocket/new_spec.rb
+++ b/spec/ruby/library/socket/unixsocket/new_spec.rb
@@ -1,6 +1,14 @@
require_relative '../spec_helper'
require_relative 'shared/new'
-describe "UNIXSocket.new" do
- it_behaves_like :unixsocket_new, :new
+with_feature :unix_socket do
+ describe "UNIXSocket.new" do
+ it_behaves_like :unixsocket_new, :new
+
+ it "does not use the given block and warns to use UNIXSocket::open" do
+ -> {
+ @client = UNIXSocket.new(@path) { raise }
+ }.should complain(/warning: UNIXSocket::new\(\) does not take block; use UNIXSocket::open\(\) instead/)
+ end
+ end
end
diff --git a/spec/ruby/library/socket/unixsocket/open_spec.rb b/spec/ruby/library/socket/unixsocket/open_spec.rb
index 99ad151bb8..61def30abb 100644
--- a/spec/ruby/library/socket/unixsocket/open_spec.rb
+++ b/spec/ruby/library/socket/unixsocket/open_spec.rb
@@ -2,12 +2,12 @@ require_relative '../spec_helper'
require_relative '../fixtures/classes'
require_relative 'shared/new'
-describe "UNIXSocket.open" do
- it_behaves_like :unixsocket_new, :open
-end
+with_feature :unix_socket do
+ describe "UNIXSocket.open" do
+ it_behaves_like :unixsocket_new, :open
+ end
-describe "UNIXSocket.open" do
- platform_is_not :windows do
+ describe "UNIXSocket.open" do
before :each do
@path = SocketSpecs.socket_path
@server = UNIXServer.open(@path)
diff --git a/spec/ruby/library/socket/unixsocket/pair_spec.rb b/spec/ruby/library/socket/unixsocket/pair_spec.rb
index 845ff76ecc..d80b60894d 100644
--- a/spec/ruby/library/socket/unixsocket/pair_spec.rb
+++ b/spec/ruby/library/socket/unixsocket/pair_spec.rb
@@ -2,10 +2,9 @@ require_relative '../spec_helper'
require_relative '../fixtures/classes'
require_relative '../shared/partially_closable_sockets'
-describe "UNIXSocket#pair" do
- platform_is_not :windows do
-
- it_should_behave_like "partially closable sockets"
+with_feature :unix_socket do
+ describe "UNIXSocket.pair" do
+ it_should_behave_like :partially_closable_sockets
before :each do
@s1, @s2 = UNIXSocket.pair
diff --git a/spec/ruby/library/socket/unixsocket/partially_closable_spec.rb b/spec/ruby/library/socket/unixsocket/partially_closable_spec.rb
index 78a64fe6be..ef7d0f0b2a 100644
--- a/spec/ruby/library/socket/unixsocket/partially_closable_spec.rb
+++ b/spec/ruby/library/socket/unixsocket/partially_closable_spec.rb
@@ -2,9 +2,8 @@ require_relative '../spec_helper'
require_relative '../fixtures/classes'
require_relative '../shared/partially_closable_sockets'
-platform_is_not :windows do
+with_feature :unix_socket do
describe "UNIXSocket partial closability" do
-
before :each do
@path = SocketSpecs.socket_path
@server = UNIXServer.open(@path)
@@ -19,7 +18,6 @@ platform_is_not :windows do
SocketSpecs.rm_socket @path
end
- it_should_behave_like "partially closable sockets"
-
+ it_should_behave_like :partially_closable_sockets
end
end
diff --git a/spec/ruby/library/socket/unixsocket/path_spec.rb b/spec/ruby/library/socket/unixsocket/path_spec.rb
index 317ffc0975..a608378e4f 100644
--- a/spec/ruby/library/socket/unixsocket/path_spec.rb
+++ b/spec/ruby/library/socket/unixsocket/path_spec.rb
@@ -1,9 +1,8 @@
require_relative '../spec_helper'
require_relative '../fixtures/classes'
-describe "UNIXSocket#path" do
-
- platform_is_not :windows do
+with_feature :unix_socket do
+ describe "UNIXSocket#path" do
before :each do
@path = SocketSpecs.socket_path
@server = UNIXServer.open(@path)
@@ -24,5 +23,4 @@ describe "UNIXSocket#path" do
@client.path.should == ""
end
end
-
end
diff --git a/spec/ruby/library/socket/unixsocket/peeraddr_spec.rb b/spec/ruby/library/socket/unixsocket/peeraddr_spec.rb
index 0b6b1ccf04..72bc96b1fe 100644
--- a/spec/ruby/library/socket/unixsocket/peeraddr_spec.rb
+++ b/spec/ruby/library/socket/unixsocket/peeraddr_spec.rb
@@ -1,9 +1,8 @@
require_relative '../spec_helper'
require_relative '../fixtures/classes'
-describe "UNIXSocket#peeraddr" do
-
- platform_is_not :windows do
+with_feature :unix_socket do
+ describe "UNIXSocket#peeraddr" do
before :each do
@path = SocketSpecs.socket_path
@server = UNIXServer.open(@path)
@@ -26,5 +25,4 @@ describe "UNIXSocket#peeraddr" do
}.should raise_error(Errno::ENOTCONN)
end
end
-
end
diff --git a/spec/ruby/library/socket/unixsocket/recv_io_spec.rb b/spec/ruby/library/socket/unixsocket/recv_io_spec.rb
index 533f02a0fa..1dbc4538e3 100644
--- a/spec/ruby/library/socket/unixsocket/recv_io_spec.rb
+++ b/spec/ruby/library/socket/unixsocket/recv_io_spec.rb
@@ -1,9 +1,8 @@
require_relative '../spec_helper'
require_relative '../fixtures/classes'
-describe "UNIXSocket#recv_io" do
-
- platform_is_not :windows do
+with_feature :unix_socket do
+ describe "UNIXSocket#recv_io" do
before :each do
@path = SocketSpecs.socket_path
@server = UNIXServer.open(@path)
@@ -41,9 +40,7 @@ describe "UNIXSocket#recv_io" do
@io.should be_an_instance_of(File)
end
end
-end
-with_feature :unix_socket do
describe 'UNIXSocket#recv_io' do
before do
@file = File.open('/dev/null', 'w')
diff --git a/spec/ruby/library/socket/unixsocket/recvfrom_spec.rb b/spec/ruby/library/socket/unixsocket/recvfrom_spec.rb
index c0e1cf670b..fedf74bb2f 100644
--- a/spec/ruby/library/socket/unixsocket/recvfrom_spec.rb
+++ b/spec/ruby/library/socket/unixsocket/recvfrom_spec.rb
@@ -1,8 +1,8 @@
require_relative '../spec_helper'
require_relative '../fixtures/classes'
-describe "UNIXSocket#recvfrom" do
- platform_is_not :windows do
+with_feature :unix_socket do
+ describe "UNIXSocket#recvfrom" do
before :each do
@path = SocketSpecs.socket_path
@server = UNIXServer.open(@path)
@@ -42,10 +42,7 @@ describe "UNIXSocket#recvfrom" do
sock.close
end
end
-end
-
-with_feature :unix_socket do
describe 'UNIXSocket#recvfrom' do
describe 'using a socket pair' do
before do
diff --git a/spec/ruby/library/socket/unixsocket/send_io_spec.rb b/spec/ruby/library/socket/unixsocket/send_io_spec.rb
index a2a7d26539..80f3550c6d 100644
--- a/spec/ruby/library/socket/unixsocket/send_io_spec.rb
+++ b/spec/ruby/library/socket/unixsocket/send_io_spec.rb
@@ -1,9 +1,8 @@
require_relative '../spec_helper'
require_relative '../fixtures/classes'
-describe "UNIXSocket#send_io" do
-
- platform_is_not :windows do
+with_feature :unix_socket do
+ describe "UNIXSocket#send_io" do
before :each do
@path = SocketSpecs.socket_path
@server = UNIXServer.open(@path)
@@ -32,9 +31,7 @@ describe "UNIXSocket#send_io" do
@io.read.should == File.read(@send_io_path)
end
end
-end
-with_feature :unix_socket do
describe 'UNIXSocket#send_io' do
before do
@file = File.open('/dev/null', 'w')
diff --git a/spec/ruby/library/socket/unixsocket/shared/new.rb b/spec/ruby/library/socket/unixsocket/shared/new.rb
index bfb7ed3886..f075b03c5e 100644
--- a/spec/ruby/library/socket/unixsocket/shared/new.rb
+++ b/spec/ruby/library/socket/unixsocket/shared/new.rb
@@ -2,23 +2,21 @@ require_relative '../../spec_helper'
require_relative '../../fixtures/classes'
describe :unixsocket_new, shared: true do
- platform_is_not :windows do
- before :each do
- @path = SocketSpecs.socket_path
- @server = UNIXServer.open(@path)
- end
+ before :each do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.open(@path)
+ end
- after :each do
- @client.close if @client
- @server.close
- SocketSpecs.rm_socket @path
- end
+ after :each do
+ @client.close if @client
+ @server.close
+ SocketSpecs.rm_socket @path
+ end
- it "opens a unix socket on the specified file" do
- @client = UNIXSocket.send(@method, @path)
+ it "opens a unix socket on the specified file" do
+ @client = UNIXSocket.send(@method, @path)
- @client.addr[0].should == "AF_UNIX"
- @client.should_not.closed?
- end
+ @client.addr[0].should == "AF_UNIX"
+ @client.should_not.closed?
end
end
diff --git a/spec/ruby/library/stringio/append_spec.rb b/spec/ruby/library/stringio/append_spec.rb
index 981229fc10..cb50d73d1b 100644
--- a/spec/ruby/library/stringio/append_spec.rb
+++ b/spec/ruby/library/stringio/append_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/classes'
describe "StringIO#<< when passed [Object]" do
before :each do
- @io = StringIO.new("example")
+ @io = StringIO.new(+"example")
end
it "returns self" do
@@ -29,13 +29,6 @@ describe "StringIO#<< when passed [Object]" do
@io.string.should == "example\000\000\000\000\000\000\000\000just testing"
end
- ruby_version_is ""..."3.0" do
- it "does not taint self when the passed argument is tainted" do
- (@io << "test".taint)
- @io.tainted?.should be_false
- end
- end
-
it "updates self's position" do
@io << "test"
@io.pos.should eql(4)
@@ -51,10 +44,10 @@ end
describe "StringIO#<< when self is not writable" do
it "raises an IOError" do
- io = StringIO.new("test", "r")
+ io = StringIO.new(+"test", "r")
-> { io << "test" }.should raise_error(IOError)
- io = StringIO.new("test")
+ io = StringIO.new(+"test")
io.close_write
-> { io << "test" }.should raise_error(IOError)
end
@@ -62,7 +55,7 @@ end
describe "StringIO#<< when in append mode" do
before :each do
- @io = StringIO.new("example", "a")
+ @io = StringIO.new(+"example", "a")
end
it "appends the passed argument to the end of self, ignoring current position" do
diff --git a/spec/ruby/library/stringio/binmode_spec.rb b/spec/ruby/library/stringio/binmode_spec.rb
index 853d9c9bd6..9e92c63814 100644
--- a/spec/ruby/library/stringio/binmode_spec.rb
+++ b/spec/ruby/library/stringio/binmode_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/classes'
describe "StringIO#binmode" do
it "returns self" do
- io = StringIO.new("example")
+ io = StringIO.new(+"example")
io.binmode.should equal(io)
end
diff --git a/spec/ruby/library/stringio/bytes_spec.rb b/spec/ruby/library/stringio/bytes_spec.rb
deleted file mode 100644
index 4ef7a490a5..0000000000
--- a/spec/ruby/library/stringio/bytes_spec.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-require_relative '../../spec_helper'
-require 'stringio'
-require_relative 'shared/each_byte'
-
-ruby_version_is ''...'3.0' do
- describe "StringIO#bytes" do
- before :each do
- @verbose, $VERBOSE = $VERBOSE, nil
- end
-
- after :each do
- $VERBOSE = @verbose
- end
-
- it_behaves_like :stringio_each_byte, :bytes
- end
-
- describe "StringIO#bytes when self is not readable" do
- before :each do
- @verbose, $VERBOSE = $VERBOSE, nil
- end
-
- after :each do
- $VERBOSE = @verbose
- end
-
- it_behaves_like :stringio_each_byte_not_readable, :bytes
- end
-end
diff --git a/spec/ruby/library/stringio/chars_spec.rb b/spec/ruby/library/stringio/chars_spec.rb
deleted file mode 100644
index 58cba77634..0000000000
--- a/spec/ruby/library/stringio/chars_spec.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-require_relative '../../spec_helper'
-require 'stringio'
-require_relative 'shared/each_char'
-
-ruby_version_is ''...'3.0' do
- describe "StringIO#chars" do
- before :each do
- @verbose, $VERBOSE = $VERBOSE, nil
- end
-
- after :each do
- $VERBOSE = @verbose
- end
-
- it_behaves_like :stringio_each_char, :chars
- end
-
- describe "StringIO#chars when self is not readable" do
- before :each do
- @verbose, $VERBOSE = $VERBOSE, nil
- end
-
- after :each do
- $VERBOSE = @verbose
- end
-
- it_behaves_like :stringio_each_char_not_readable, :chars
- end
-end
diff --git a/spec/ruby/library/stringio/close_read_spec.rb b/spec/ruby/library/stringio/close_read_spec.rb
index 80bd547e85..0f08e1ff2e 100644
--- a/spec/ruby/library/stringio/close_read_spec.rb
+++ b/spec/ruby/library/stringio/close_read_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/classes'
describe "StringIO#close_read" do
before :each do
- @io = StringIO.new("example")
+ @io = StringIO.new(+"example")
end
it "returns nil" do
@@ -21,7 +21,7 @@ describe "StringIO#close_read" do
end
it "raises an IOError when in write-only mode" do
- io = StringIO.new("example", "w")
+ io = StringIO.new(+"example", "w")
-> { io.close_read }.should raise_error(IOError)
io = StringIO.new("example")
diff --git a/spec/ruby/library/stringio/close_write_spec.rb b/spec/ruby/library/stringio/close_write_spec.rb
index 1a4cfa113e..c86c3f9826 100644
--- a/spec/ruby/library/stringio/close_write_spec.rb
+++ b/spec/ruby/library/stringio/close_write_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/classes'
describe "StringIO#close_write" do
before :each do
- @io = StringIO.new("example")
+ @io = StringIO.new(+"example")
end
it "returns nil" do
@@ -21,10 +21,10 @@ describe "StringIO#close_write" do
end
it "raises an IOError when in read-only mode" do
- io = StringIO.new("example", "r")
+ io = StringIO.new(+"example", "r")
-> { io.close_write }.should raise_error(IOError)
- io = StringIO.new("example")
+ io = StringIO.new(+"example")
io.close_write
io.close_write.should == nil
end
diff --git a/spec/ruby/library/stringio/closed_read_spec.rb b/spec/ruby/library/stringio/closed_read_spec.rb
index cb4267ac98..b4dcadc3a4 100644
--- a/spec/ruby/library/stringio/closed_read_spec.rb
+++ b/spec/ruby/library/stringio/closed_read_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/classes'
describe "StringIO#closed_read?" do
it "returns true if self is not readable" do
- io = StringIO.new("example", "r+")
+ io = StringIO.new(+"example", "r+")
io.close_write
io.closed_read?.should be_false
io.close_read
diff --git a/spec/ruby/library/stringio/closed_spec.rb b/spec/ruby/library/stringio/closed_spec.rb
index ca8a2232a8..bf7ba63184 100644
--- a/spec/ruby/library/stringio/closed_spec.rb
+++ b/spec/ruby/library/stringio/closed_spec.rb
@@ -3,13 +3,13 @@ require_relative 'fixtures/classes'
describe "StringIO#closed?" do
it "returns true if self is completely closed" do
- io = StringIO.new("example", "r+")
+ io = StringIO.new(+"example", "r+")
io.close_read
io.closed?.should be_false
io.close_write
io.closed?.should be_true
- io = StringIO.new("example", "r+")
+ io = StringIO.new(+"example", "r+")
io.close
io.closed?.should be_true
end
diff --git a/spec/ruby/library/stringio/closed_write_spec.rb b/spec/ruby/library/stringio/closed_write_spec.rb
index 5c111affd8..2bd3e6fa8b 100644
--- a/spec/ruby/library/stringio/closed_write_spec.rb
+++ b/spec/ruby/library/stringio/closed_write_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/classes'
describe "StringIO#closed_write?" do
it "returns true if self is not writable" do
- io = StringIO.new("example", "r+")
+ io = StringIO.new(+"example", "r+")
io.close_read
io.closed_write?.should be_false
io.close_write
diff --git a/spec/ruby/library/stringio/codepoints_spec.rb b/spec/ruby/library/stringio/codepoints_spec.rb
deleted file mode 100644
index ceaadefc32..0000000000
--- a/spec/ruby/library/stringio/codepoints_spec.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- encoding: utf-8 -*-
-require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/codepoints'
-
-ruby_version_is ''...'3.0' do
- # See redmine #1667
- describe "StringIO#codepoints" do
- before :each do
- @verbose, $VERBOSE = $VERBOSE, nil
- end
-
- after :each do
- $VERBOSE = @verbose
- end
-
- it_behaves_like :stringio_codepoints, :codepoints
- end
-end
diff --git a/spec/ruby/library/stringio/flush_spec.rb b/spec/ruby/library/stringio/flush_spec.rb
index 17a16dfdd5..4dc58b1d48 100644
--- a/spec/ruby/library/stringio/flush_spec.rb
+++ b/spec/ruby/library/stringio/flush_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/classes'
describe "StringIO#flush" do
it "returns self" do
- io = StringIO.new("flush")
+ io = StringIO.new(+"flush")
io.flush.should equal(io)
end
end
diff --git a/spec/ruby/library/stringio/fsync_spec.rb b/spec/ruby/library/stringio/fsync_spec.rb
index 8fb2b59a24..85053cb2e5 100644
--- a/spec/ruby/library/stringio/fsync_spec.rb
+++ b/spec/ruby/library/stringio/fsync_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/classes'
describe "StringIO#fsync" do
it "returns zero" do
- io = StringIO.new("fsync")
+ io = StringIO.new(+"fsync")
io.fsync.should eql(0)
end
end
diff --git a/spec/ruby/library/stringio/gets_spec.rb b/spec/ruby/library/stringio/gets_spec.rb
index d597ec0e45..4af7704a41 100644
--- a/spec/ruby/library/stringio/gets_spec.rb
+++ b/spec/ruby/library/stringio/gets_spec.rb
@@ -233,7 +233,7 @@ end
describe "StringIO#gets when in write-only mode" do
it "raises an IOError" do
- io = StringIO.new("xyz", "w")
+ io = StringIO.new(+"xyz", "w")
-> { io.gets }.should raise_error(IOError)
io = StringIO.new("xyz")
diff --git a/spec/ruby/library/stringio/initialize_spec.rb b/spec/ruby/library/stringio/initialize_spec.rb
index c597e328d3..ad067a0be1 100644
--- a/spec/ruby/library/stringio/initialize_spec.rb
+++ b/spec/ruby/library/stringio/initialize_spec.rb
@@ -13,99 +13,99 @@ describe "StringIO#initialize when passed [Object, mode]" do
it "sets the mode based on the passed mode" do
io = StringIO.allocate
- io.send(:initialize, "example", "r")
+ io.send(:initialize, +"example", "r")
io.closed_read?.should be_false
io.closed_write?.should be_true
io = StringIO.allocate
- io.send(:initialize, "example", "rb")
+ io.send(:initialize, +"example", "rb")
io.closed_read?.should be_false
io.closed_write?.should be_true
io = StringIO.allocate
- io.send(:initialize, "example", "r+")
+ io.send(:initialize, +"example", "r+")
io.closed_read?.should be_false
io.closed_write?.should be_false
io = StringIO.allocate
- io.send(:initialize, "example", "rb+")
+ io.send(:initialize, +"example", "rb+")
io.closed_read?.should be_false
io.closed_write?.should be_false
io = StringIO.allocate
- io.send(:initialize, "example", "w")
+ io.send(:initialize, +"example", "w")
io.closed_read?.should be_true
io.closed_write?.should be_false
io = StringIO.allocate
- io.send(:initialize, "example", "wb")
+ io.send(:initialize, +"example", "wb")
io.closed_read?.should be_true
io.closed_write?.should be_false
io = StringIO.allocate
- io.send(:initialize, "example", "w+")
+ io.send(:initialize, +"example", "w+")
io.closed_read?.should be_false
io.closed_write?.should be_false
io = StringIO.allocate
- io.send(:initialize, "example", "wb+")
+ io.send(:initialize, +"example", "wb+")
io.closed_read?.should be_false
io.closed_write?.should be_false
io = StringIO.allocate
- io.send(:initialize, "example", "a")
+ io.send(:initialize, +"example", "a")
io.closed_read?.should be_true
io.closed_write?.should be_false
io = StringIO.allocate
- io.send(:initialize, "example", "ab")
+ io.send(:initialize, +"example", "ab")
io.closed_read?.should be_true
io.closed_write?.should be_false
io = StringIO.allocate
- io.send(:initialize, "example", "a+")
+ io.send(:initialize, +"example", "a+")
io.closed_read?.should be_false
io.closed_write?.should be_false
io = StringIO.allocate
- io.send(:initialize, "example", "ab+")
+ io.send(:initialize, +"example", "ab+")
io.closed_read?.should be_false
io.closed_write?.should be_false
end
it "allows passing the mode as an Integer" do
io = StringIO.allocate
- io.send(:initialize, "example", IO::RDONLY)
+ io.send(:initialize, +"example", IO::RDONLY)
io.closed_read?.should be_false
io.closed_write?.should be_true
io = StringIO.allocate
- io.send(:initialize, "example", IO::RDWR)
+ io.send(:initialize, +"example", IO::RDWR)
io.closed_read?.should be_false
io.closed_write?.should be_false
io = StringIO.allocate
- io.send(:initialize, "example", IO::WRONLY)
+ io.send(:initialize, +"example", IO::WRONLY)
io.closed_read?.should be_true
io.closed_write?.should be_false
io = StringIO.allocate
- io.send(:initialize, "example", IO::WRONLY | IO::TRUNC)
+ io.send(:initialize, +"example", IO::WRONLY | IO::TRUNC)
io.closed_read?.should be_true
io.closed_write?.should be_false
io = StringIO.allocate
- io.send(:initialize, "example", IO::RDWR | IO::TRUNC)
+ io.send(:initialize, +"example", IO::RDWR | IO::TRUNC)
io.closed_read?.should be_false
io.closed_write?.should be_false
io = StringIO.allocate
- io.send(:initialize, "example", IO::WRONLY | IO::APPEND)
+ io.send(:initialize, +"example", IO::WRONLY | IO::APPEND)
io.closed_read?.should be_true
io.closed_write?.should be_false
io = StringIO.allocate
- io.send(:initialize, "example", IO::RDWR | IO::APPEND)
+ io.send(:initialize, +"example", IO::RDWR | IO::APPEND)
io.closed_read?.should be_false
io.closed_write?.should be_false
end
@@ -118,7 +118,7 @@ describe "StringIO#initialize when passed [Object, mode]" do
it "tries to convert the passed mode to a String using #to_str" do
obj = mock('to_str')
obj.should_receive(:to_str).and_return("r")
- @io.send(:initialize, "example", obj)
+ @io.send(:initialize, +"example", obj)
@io.closed_read?.should be_false
@io.closed_write?.should be_true
@@ -142,12 +142,18 @@ describe "StringIO#initialize when passed [Object]" do
@io.string.should equal(str)
end
- it "sets the mode to read-write" do
- @io.send(:initialize, "example")
+ it "sets the mode to read-write if the string is mutable" do
+ @io.send(:initialize, +"example")
@io.closed_read?.should be_false
@io.closed_write?.should be_false
end
+ it "sets the mode to read if the string is frozen" do
+ @io.send(:initialize, -"example")
+ @io.closed_read?.should be_false
+ @io.closed_write?.should be_true
+ end
+
it "tries to convert the passed Object to a String using #to_str" do
obj = mock('to_str')
obj.should_receive(:to_str).and_return("example")
@@ -172,22 +178,22 @@ describe "StringIO#initialize when passed keyword arguments" do
end
it "accepts a mode argument set to nil with a valid :mode option" do
- @io = StringIO.new('', nil, mode: "w")
+ @io = StringIO.new(+'', nil, mode: "w")
@io.write("foo").should == 3
end
it "accepts a mode argument with a :mode option set to nil" do
- @io = StringIO.new('', "w", mode: nil)
+ @io = StringIO.new(+'', "w", mode: nil)
@io.write("foo").should == 3
end
it "sets binmode from :binmode option" do
- @io = StringIO.new('', 'w', binmode: true)
+ @io = StringIO.new(+'', 'w', binmode: true)
@io.external_encoding.to_s.should == "ASCII-8BIT" # #binmode? isn't implemented in StringIO
end
it "does not set binmode from false :binmode" do
- @io = StringIO.new('', 'w', binmode: false)
+ @io = StringIO.new(+'', 'w', binmode: false)
@io.external_encoding.to_s.should == "UTF-8" # #binmode? isn't implemented in StringIO
end
end
@@ -196,54 +202,54 @@ end
describe "StringIO#initialize when passed keyword arguments and error happens" do
it "raises an error if passed encodings two ways" do
-> {
- @io = StringIO.new('', 'w:ISO-8859-1', encoding: 'ISO-8859-1')
+ @io = StringIO.new(+'', 'w:ISO-8859-1', encoding: 'ISO-8859-1')
}.should raise_error(ArgumentError)
-> {
- @io = StringIO.new('', 'w:ISO-8859-1', external_encoding: 'ISO-8859-1')
+ @io = StringIO.new(+'', 'w:ISO-8859-1', external_encoding: 'ISO-8859-1')
}.should raise_error(ArgumentError)
-> {
- @io = StringIO.new('', 'w:ISO-8859-1:UTF-8', internal_encoding: 'ISO-8859-1')
+ @io = StringIO.new(+'', 'w:ISO-8859-1:UTF-8', internal_encoding: 'ISO-8859-1')
}.should raise_error(ArgumentError)
end
it "raises an error if passed matching binary/text mode two ways" do
-> {
- @io = StringIO.new('', "wb", binmode: true)
+ @io = StringIO.new(+'', "wb", binmode: true)
}.should raise_error(ArgumentError)
-> {
- @io = StringIO.new('', "wt", textmode: true)
+ @io = StringIO.new(+'', "wt", textmode: true)
}.should raise_error(ArgumentError)
-> {
- @io = StringIO.new('', "wb", textmode: false)
+ @io = StringIO.new(+'', "wb", textmode: false)
}.should raise_error(ArgumentError)
-> {
- @io = StringIO.new('', "wt", binmode: false)
+ @io = StringIO.new(+'', "wt", binmode: false)
}.should raise_error(ArgumentError)
end
it "raises an error if passed conflicting binary/text mode two ways" do
-> {
- @io = StringIO.new('', "wb", binmode: false)
+ @io = StringIO.new(+'', "wb", binmode: false)
}.should raise_error(ArgumentError)
-> {
- @io = StringIO.new('', "wt", textmode: false)
+ @io = StringIO.new(+'', "wt", textmode: false)
}.should raise_error(ArgumentError)
-> {
- @io = StringIO.new('', "wb", textmode: true)
+ @io = StringIO.new(+'', "wb", textmode: true)
}.should raise_error(ArgumentError)
-> {
- @io = StringIO.new('', "wt", binmode: true)
+ @io = StringIO.new(+'', "wt", binmode: true)
}.should raise_error(ArgumentError)
end
it "raises an error when trying to set both binmode and textmode" do
-> {
- @io = StringIO.new('', "w", textmode: true, binmode: true)
+ @io = StringIO.new(+'', "w", textmode: true, binmode: true)
}.should raise_error(ArgumentError)
-> {
- @io = StringIO.new('', File::Constants::WRONLY, textmode: true, binmode: true)
+ @io = StringIO.new(+'', File::Constants::WRONLY, textmode: true, binmode: true)
}.should raise_error(ArgumentError)
end
end
@@ -258,7 +264,7 @@ describe "StringIO#initialize when passed no arguments" do
end
it "sets the mode to read-write" do
- @io.send(:initialize, "example")
+ @io.send(:initialize)
@io.closed_read?.should be_false
@io.closed_write?.should be_false
end
@@ -289,19 +295,14 @@ describe "StringIO#initialize sets" do
end
it "the encoding to the encoding of the String when passed a String" do
- s = ''.force_encoding(Encoding::EUC_JP)
+ s = ''.dup.force_encoding(Encoding::EUC_JP)
io = StringIO.new(s)
io.string.encoding.should == Encoding::EUC_JP
end
- guard_not -> { # [Bug #16497]
- stringio_version = StringIO.const_defined?(:VERSION) ? StringIO::VERSION : "0.0.2"
- version_is(stringio_version, "0.0.3"..."0.1.1")
- } do
- it "the #external_encoding to the encoding of the String when passed a String" do
- s = ''.force_encoding(Encoding::EUC_JP)
- io = StringIO.new(s)
- io.external_encoding.should == Encoding::EUC_JP
- end
+ it "the #external_encoding to the encoding of the String when passed a String" do
+ s = ''.dup.force_encoding(Encoding::EUC_JP)
+ io = StringIO.new(s)
+ io.external_encoding.should == Encoding::EUC_JP
end
end
diff --git a/spec/ruby/library/stringio/lines_spec.rb b/spec/ruby/library/stringio/lines_spec.rb
deleted file mode 100644
index 42d11772ae..0000000000
--- a/spec/ruby/library/stringio/lines_spec.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-require_relative '../../spec_helper'
-require 'stringio'
-require_relative 'shared/each'
-
-ruby_version_is ''...'3.0' do
- describe "StringIO#lines when passed a separator" do
- before :each do
- @verbose, $VERBOSE = $VERBOSE, nil
- end
-
- after :each do
- $VERBOSE = @verbose
- end
-
- it_behaves_like :stringio_each_separator, :lines
- end
-
- describe "StringIO#lines when passed no arguments" do
- before :each do
- @verbose, $VERBOSE = $VERBOSE, nil
- end
-
- after :each do
- $VERBOSE = @verbose
- end
-
- it_behaves_like :stringio_each_no_arguments, :lines
- end
-
- describe "StringIO#lines when self is not readable" do
- before :each do
- @verbose, $VERBOSE = $VERBOSE, nil
- end
-
- after :each do
- $VERBOSE = @verbose
- end
-
- it_behaves_like :stringio_each_not_readable, :lines
- end
-
- describe "StringIO#lines when passed chomp" do
- before :each do
- @verbose, $VERBOSE = $VERBOSE, nil
- end
-
- after :each do
- $VERBOSE = @verbose
- end
-
- it_behaves_like :stringio_each_chomp, :lines
- end
-end
diff --git a/spec/ruby/library/stringio/new_spec.rb b/spec/ruby/library/stringio/new_spec.rb
index 328455134e..ec4b32aa11 100644
--- a/spec/ruby/library/stringio/new_spec.rb
+++ b/spec/ruby/library/stringio/new_spec.rb
@@ -2,7 +2,9 @@ require_relative '../../spec_helper'
require 'stringio'
describe "StringIO.new" do
- it "warns when called with a block" do
- -> { eval("StringIO.new {}") }.should complain(/StringIO::new\(\) does not take block; use StringIO::open\(\) instead/)
+ it "does not use the given block and warns to use StringIO::open" do
+ -> {
+ StringIO.new { raise }
+ }.should complain(/warning: StringIO::new\(\) does not take block; use StringIO::open\(\) instead/)
end
end
diff --git a/spec/ruby/library/stringio/open_spec.rb b/spec/ruby/library/stringio/open_spec.rb
index 3068e19435..b7c90661f9 100644
--- a/spec/ruby/library/stringio/open_spec.rb
+++ b/spec/ruby/library/stringio/open_spec.rb
@@ -8,26 +8,26 @@ describe "StringIO.open when passed [Object, mode]" do
end
it "returns the blocks return value when yielding" do
- ret = StringIO.open("example", "r") { :test }
+ ret = StringIO.open(+"example", "r") { :test }
ret.should equal(:test)
end
it "yields self to the passed block" do
io = nil
- StringIO.open("example", "r") { |strio| io = strio }
+ StringIO.open(+"example", "r") { |strio| io = strio }
io.should be_kind_of(StringIO)
end
it "closes self after yielding" do
io = nil
- StringIO.open("example", "r") { |strio| io = strio }
+ StringIO.open(+"example", "r") { |strio| io = strio }
io.closed?.should be_true
end
it "even closes self when an exception is raised while yielding" do
io = nil
begin
- StringIO.open("example", "r") do |strio|
+ StringIO.open(+"example", "r") do |strio|
io = strio
raise "Error"
end
@@ -38,14 +38,14 @@ describe "StringIO.open when passed [Object, mode]" do
it "sets self's string to nil after yielding" do
io = nil
- StringIO.open("example", "r") { |strio| io = strio }
+ StringIO.open(+"example", "r") { |strio| io = strio }
io.string.should be_nil
end
it "even sets self's string to nil when an exception is raised while yielding" do
io = nil
begin
- StringIO.open("example", "r") do |strio|
+ StringIO.open(+"example", "r") do |strio|
io = strio
raise "Error"
end
@@ -55,81 +55,81 @@ describe "StringIO.open when passed [Object, mode]" do
end
it "sets the mode based on the passed mode" do
- io = StringIO.open("example", "r")
+ io = StringIO.open(+"example", "r")
io.closed_read?.should be_false
io.closed_write?.should be_true
- io = StringIO.open("example", "rb")
+ io = StringIO.open(+"example", "rb")
io.closed_read?.should be_false
io.closed_write?.should be_true
- io = StringIO.open("example", "r+")
+ io = StringIO.open(+"example", "r+")
io.closed_read?.should be_false
io.closed_write?.should be_false
- io = StringIO.open("example", "rb+")
+ io = StringIO.open(+"example", "rb+")
io.closed_read?.should be_false
io.closed_write?.should be_false
- io = StringIO.open("example", "w")
+ io = StringIO.open(+"example", "w")
io.closed_read?.should be_true
io.closed_write?.should be_false
- io = StringIO.open("example", "wb")
+ io = StringIO.open(+"example", "wb")
io.closed_read?.should be_true
io.closed_write?.should be_false
- io = StringIO.open("example", "w+")
+ io = StringIO.open(+"example", "w+")
io.closed_read?.should be_false
io.closed_write?.should be_false
- io = StringIO.open("example", "wb+")
+ io = StringIO.open(+"example", "wb+")
io.closed_read?.should be_false
io.closed_write?.should be_false
- io = StringIO.open("example", "a")
+ io = StringIO.open(+"example", "a")
io.closed_read?.should be_true
io.closed_write?.should be_false
- io = StringIO.open("example", "ab")
+ io = StringIO.open(+"example", "ab")
io.closed_read?.should be_true
io.closed_write?.should be_false
- io = StringIO.open("example", "a+")
+ io = StringIO.open(+"example", "a+")
io.closed_read?.should be_false
io.closed_write?.should be_false
- io = StringIO.open("example", "ab+")
+ io = StringIO.open(+"example", "ab+")
io.closed_read?.should be_false
io.closed_write?.should be_false
end
it "allows passing the mode as an Integer" do
- io = StringIO.open("example", IO::RDONLY)
+ io = StringIO.open(+"example", IO::RDONLY)
io.closed_read?.should be_false
io.closed_write?.should be_true
- io = StringIO.open("example", IO::RDWR)
+ io = StringIO.open(+"example", IO::RDWR)
io.closed_read?.should be_false
io.closed_write?.should be_false
- io = StringIO.open("example", IO::WRONLY)
+ io = StringIO.open(+"example", IO::WRONLY)
io.closed_read?.should be_true
io.closed_write?.should be_false
- io = StringIO.open("example", IO::WRONLY | IO::TRUNC)
+ io = StringIO.open(+"example", IO::WRONLY | IO::TRUNC)
io.closed_read?.should be_true
io.closed_write?.should be_false
- io = StringIO.open("example", IO::RDWR | IO::TRUNC)
+ io = StringIO.open(+"example", IO::RDWR | IO::TRUNC)
io.closed_read?.should be_false
io.closed_write?.should be_false
- io = StringIO.open("example", IO::WRONLY | IO::APPEND)
+ io = StringIO.open(+"example", IO::WRONLY | IO::APPEND)
io.closed_read?.should be_true
io.closed_write?.should be_false
- io = StringIO.open("example", IO::RDWR | IO::APPEND)
+ io = StringIO.open(+"example", IO::RDWR | IO::APPEND)
io.closed_read?.should be_false
io.closed_write?.should be_false
end
@@ -141,7 +141,7 @@ describe "StringIO.open when passed [Object, mode]" do
it "tries to convert the passed mode to a String using #to_str" do
obj = mock('to_str')
obj.should_receive(:to_str).and_return("r")
- io = StringIO.open("example", obj)
+ io = StringIO.open(+"example", obj)
io.closed_read?.should be_false
io.closed_write?.should be_true
@@ -163,16 +163,16 @@ describe "StringIO.open when passed [Object]" do
it "yields self to the passed block" do
io = nil
- ret = StringIO.open("example") { |strio| io = strio }
+ ret = StringIO.open(+"example") { |strio| io = strio }
io.should equal(ret)
end
it "sets the mode to read-write (r+)" do
- io = StringIO.open("example")
+ io = StringIO.open(+"example")
io.closed_read?.should be_false
io.closed_write?.should be_false
- io = StringIO.new("example")
+ io = StringIO.new(+"example")
io.printf("%d", 123)
io.string.should == "123mple"
end
@@ -204,7 +204,7 @@ describe "StringIO.open when passed no arguments" do
io.closed_read?.should be_false
io.closed_write?.should be_false
- io = StringIO.new("example")
+ io = StringIO.new(+"example")
io.printf("%d", 123)
io.string.should == "123mple"
end
diff --git a/spec/ruby/library/stringio/print_spec.rb b/spec/ruby/library/stringio/print_spec.rb
index 6ac6430900..00c33367dc 100644
--- a/spec/ruby/library/stringio/print_spec.rb
+++ b/spec/ruby/library/stringio/print_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/classes'
describe "StringIO#print" do
before :each do
- @io = StringIO.new('example')
+ @io = StringIO.new(+'example')
end
it "prints $_ when passed no arguments" do
@@ -73,7 +73,7 @@ end
describe "StringIO#print when in append mode" do
before :each do
- @io = StringIO.new("example", "a")
+ @io = StringIO.new(+"example", "a")
end
it "appends the passed argument to the end of self" do
@@ -92,10 +92,10 @@ end
describe "StringIO#print when self is not writable" do
it "raises an IOError" do
- io = StringIO.new("test", "r")
+ io = StringIO.new(+"test", "r")
-> { io.print("test") }.should raise_error(IOError)
- io = StringIO.new("test")
+ io = StringIO.new(+"test")
io.close_write
-> { io.print("test") }.should raise_error(IOError)
end
diff --git a/spec/ruby/library/stringio/printf_spec.rb b/spec/ruby/library/stringio/printf_spec.rb
index f3f669a185..ca82e84757 100644
--- a/spec/ruby/library/stringio/printf_spec.rb
+++ b/spec/ruby/library/stringio/printf_spec.rb
@@ -41,7 +41,7 @@ end
describe "StringIO#printf when in read-write mode" do
before :each do
- @io = StringIO.new("example", "r+")
+ @io = StringIO.new(+"example", "r+")
end
it "starts from the beginning" do
@@ -62,7 +62,7 @@ end
describe "StringIO#printf when in append mode" do
before :each do
- @io = StringIO.new("example", "a")
+ @io = StringIO.new(+"example", "a")
end
it "appends the passed argument to the end of self" do
@@ -81,10 +81,10 @@ end
describe "StringIO#printf when self is not writable" do
it "raises an IOError" do
- io = StringIO.new("test", "r")
+ io = StringIO.new(+"test", "r")
-> { io.printf("test") }.should raise_error(IOError)
- io = StringIO.new("test")
+ io = StringIO.new(+"test")
io.close_write
-> { io.printf("test") }.should raise_error(IOError)
end
diff --git a/spec/ruby/library/stringio/putc_spec.rb b/spec/ruby/library/stringio/putc_spec.rb
index 1ce53b7ef2..9f1ac8ffb2 100644
--- a/spec/ruby/library/stringio/putc_spec.rb
+++ b/spec/ruby/library/stringio/putc_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/classes'
describe "StringIO#putc when passed [String]" do
before :each do
- @io = StringIO.new('example')
+ @io = StringIO.new(+'example')
end
it "overwrites the character at the current position" do
@@ -54,7 +54,7 @@ end
describe "StringIO#putc when passed [Object]" do
before :each do
- @io = StringIO.new('example')
+ @io = StringIO.new(+'example')
end
it "it writes the passed Integer % 256 to self" do
@@ -85,7 +85,7 @@ end
describe "StringIO#putc when in append mode" do
it "appends to the end of self" do
- io = StringIO.new("test", "a")
+ io = StringIO.new(+"test", "a")
io.putc(?t)
io.string.should == "testt"
end
@@ -93,10 +93,10 @@ end
describe "StringIO#putc when self is not writable" do
it "raises an IOError" do
- io = StringIO.new("test", "r")
+ io = StringIO.new(+"test", "r")
-> { io.putc(?a) }.should raise_error(IOError)
- io = StringIO.new("test")
+ io = StringIO.new(+"test")
io.close_write
-> { io.putc("t") }.should raise_error(IOError)
end
diff --git a/spec/ruby/library/stringio/puts_spec.rb b/spec/ruby/library/stringio/puts_spec.rb
index 9c890262dd..054ec8227f 100644
--- a/spec/ruby/library/stringio/puts_spec.rb
+++ b/spec/ruby/library/stringio/puts_spec.rb
@@ -145,7 +145,7 @@ end
describe "StringIO#puts when in append mode" do
before :each do
- @io = StringIO.new("example", "a")
+ @io = StringIO.new(+"example", "a")
end
it "appends the passed argument to the end of self" do
@@ -164,10 +164,10 @@ end
describe "StringIO#puts when self is not writable" do
it "raises an IOError" do
- io = StringIO.new("test", "r")
+ io = StringIO.new(+"test", "r")
-> { io.puts }.should raise_error(IOError)
- io = StringIO.new("test")
+ io = StringIO.new(+"test")
io.close_write
-> { io.puts }.should raise_error(IOError)
end
@@ -175,7 +175,7 @@ end
describe "StringIO#puts when passed an encoded string" do
it "stores the bytes unmodified" do
- io = StringIO.new("")
+ io = StringIO.new(+"")
io.puts "\x00\x01\x02"
io.puts "æåø"
diff --git a/spec/ruby/library/stringio/read_nonblock_spec.rb b/spec/ruby/library/stringio/read_nonblock_spec.rb
index d4ec56d9aa..74736f7792 100644
--- a/spec/ruby/library/stringio/read_nonblock_spec.rb
+++ b/spec/ruby/library/stringio/read_nonblock_spec.rb
@@ -8,7 +8,7 @@ describe "StringIO#read_nonblock when passed length, buffer" do
it "accepts :exception option" do
io = StringIO.new("example")
- io.read_nonblock(3, buffer = "", exception: true)
+ io.read_nonblock(3, buffer = +"", exception: true)
buffer.should == "exa"
end
end
@@ -40,7 +40,7 @@ describe "StringIO#read_nonblock" do
context "when exception option is set to false" do
context "when the end is reached" do
it "returns nil" do
- stringio = StringIO.new('')
+ stringio = StringIO.new(+'')
stringio << "hello"
stringio.rewind
diff --git a/spec/ruby/library/stringio/read_spec.rb b/spec/ruby/library/stringio/read_spec.rb
index 52ab3dcf47..e49f262127 100644
--- a/spec/ruby/library/stringio/read_spec.rb
+++ b/spec/ruby/library/stringio/read_spec.rb
@@ -53,7 +53,7 @@ describe "StringIO#read when passed length and a buffer" do
end
it "reads [length] characters into the buffer" do
- buf = "foo"
+ buf = +"foo"
result = @io.read(10, buf)
buf.should == "abcdefghij"
diff --git a/spec/ruby/library/stringio/readline_spec.rb b/spec/ruby/library/stringio/readline_spec.rb
index b794e5fade..b16a16e23f 100644
--- a/spec/ruby/library/stringio/readline_spec.rb
+++ b/spec/ruby/library/stringio/readline_spec.rb
@@ -113,7 +113,7 @@ end
describe "StringIO#readline when in write-only mode" do
it "raises an IOError" do
- io = StringIO.new("xyz", "w")
+ io = StringIO.new(+"xyz", "w")
-> { io.readline }.should raise_error(IOError)
io = StringIO.new("xyz")
diff --git a/spec/ruby/library/stringio/readlines_spec.rb b/spec/ruby/library/stringio/readlines_spec.rb
index c471d0fd73..ed7cc22b3d 100644
--- a/spec/ruby/library/stringio/readlines_spec.rb
+++ b/spec/ruby/library/stringio/readlines_spec.rb
@@ -83,7 +83,7 @@ end
describe "StringIO#readlines when in write-only mode" do
it "raises an IOError" do
- io = StringIO.new("xyz", "w")
+ io = StringIO.new(+"xyz", "w")
-> { io.readlines }.should raise_error(IOError)
io = StringIO.new("xyz")
diff --git a/spec/ruby/library/stringio/readpartial_spec.rb b/spec/ruby/library/stringio/readpartial_spec.rb
index 2601fe8c42..f25cef4014 100644
--- a/spec/ruby/library/stringio/readpartial_spec.rb
+++ b/spec/ruby/library/stringio/readpartial_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/classes'
describe "StringIO#readpartial" do
before :each do
- @string = StringIO.new('Stop, look, listen')
+ @string = StringIO.new(+'Stop, look, listen')
end
after :each do
@@ -48,7 +48,7 @@ describe "StringIO#readpartial" do
end
it "discards the existing buffer content upon successful read" do
- buffer = "existing"
+ buffer = +"existing"
@string.readpartial(11, buffer)
buffer.should == "Stop, look,"
end
@@ -59,7 +59,7 @@ describe "StringIO#readpartial" do
end
it "discards the existing buffer content upon error" do
- buffer = 'hello'
+ buffer = +'hello'
@string.readpartial(100)
-> { @string.readpartial(1, buffer) }.should raise_error(EOFError)
buffer.should be_empty
diff --git a/spec/ruby/library/stringio/reopen_spec.rb b/spec/ruby/library/stringio/reopen_spec.rb
index 4863a5332b..7021ff17e5 100644
--- a/spec/ruby/library/stringio/reopen_spec.rb
+++ b/spec/ruby/library/stringio/reopen_spec.rb
@@ -12,31 +12,20 @@ describe "StringIO#reopen when passed [Object, Integer]" do
@io.closed_write?.should be_true
@io.string.should == "reopened"
- @io.reopen("reopened, twice", IO::WRONLY)
+ @io.reopen(+"reopened, twice", IO::WRONLY)
@io.closed_read?.should be_true
@io.closed_write?.should be_false
@io.string.should == "reopened, twice"
- @io.reopen("reopened, another time", IO::RDWR)
+ @io.reopen(+"reopened, another time", IO::RDWR)
@io.closed_read?.should be_false
@io.closed_write?.should be_false
@io.string.should == "reopened, another time"
end
- ruby_version_is ""..."3.0" do
- # NOTE: WEIRD!
- it "does not taint self when the passed Object was tainted" do
- @io.reopen("reopened".taint, IO::RDONLY)
- @io.tainted?.should be_false
-
- @io.reopen("reopened".taint, IO::WRONLY)
- @io.tainted?.should be_false
- end
- end
-
it "tries to convert the passed Object to a String using #to_str" do
obj = mock("to_str")
- obj.should_receive(:to_str).and_return("to_str")
+ obj.should_receive(:to_str).and_return(+"to_str")
@io.reopen(obj, IO::RDWR)
@io.string.should == "to_str"
end
@@ -71,38 +60,27 @@ describe "StringIO#reopen when passed [Object, Object]" do
@io.closed_write?.should be_true
@io.string.should == "reopened"
- @io.reopen("reopened, twice", "r+")
+ @io.reopen(+"reopened, twice", "r+")
@io.closed_read?.should be_false
@io.closed_write?.should be_false
@io.string.should == "reopened, twice"
- @io.reopen("reopened, another", "w+")
+ @io.reopen(+"reopened, another", "w+")
@io.closed_read?.should be_false
@io.closed_write?.should be_false
@io.string.should == ""
- @io.reopen("reopened, another time", "r+")
+ @io.reopen(+"reopened, another time", "r+")
@io.closed_read?.should be_false
@io.closed_write?.should be_false
@io.string.should == "reopened, another time"
end
it "truncates the passed String when opened in truncate mode" do
- @io.reopen(str = "reopened", "w")
+ @io.reopen(str = +"reopened", "w")
str.should == ""
end
- ruby_version_is ""..."3.0" do
- # NOTE: WEIRD!
- it "does not taint self when the passed Object was tainted" do
- @io.reopen("reopened".taint, "r")
- @io.tainted?.should be_false
-
- @io.reopen("reopened".taint, "w")
- @io.tainted?.should be_false
- end
- end
-
it "tries to convert the passed Object to a String using #to_str" do
obj = mock("to_str")
obj.should_receive(:to_str).and_return("to_str")
@@ -116,13 +94,13 @@ describe "StringIO#reopen when passed [Object, Object]" do
it "resets self's position to 0" do
@io.read(5)
- @io.reopen("reopened")
+ @io.reopen(+"reopened")
@io.pos.should eql(0)
end
it "resets self's line number to 0" do
@io.gets
- @io.reopen("reopened")
+ @io.reopen(+"reopened")
@io.lineno.should eql(0)
end
@@ -156,7 +134,7 @@ describe "StringIO#reopen when passed [String]" do
it "reopens self with the passed String in read-write mode" do
@io.close
- @io.reopen("reopened")
+ @io.reopen(+"reopened")
@io.closed_write?.should be_false
@io.closed_read?.should be_false
@@ -164,23 +142,15 @@ describe "StringIO#reopen when passed [String]" do
@io.string.should == "reopened"
end
- ruby_version_is ""..."3.0" do
- # NOTE: WEIRD!
- it "does not taint self when the passed Object was tainted" do
- @io.reopen("reopened".taint)
- @io.tainted?.should be_false
- end
- end
-
it "resets self's position to 0" do
@io.read(5)
- @io.reopen("reopened")
+ @io.reopen(+"reopened")
@io.pos.should eql(0)
end
it "resets self's line number to 0" do
@io.gets
- @io.reopen("reopened")
+ @io.reopen(+"reopened")
@io.lineno.should eql(0)
end
end
@@ -202,7 +172,7 @@ describe "StringIO#reopen when passed [Object]" do
it "tries to convert the passed Object to a StringIO using #to_strio" do
obj = mock("to_strio")
- obj.should_receive(:to_strio).and_return(StringIO.new("to_strio"))
+ obj.should_receive(:to_strio).and_return(StringIO.new(+"to_strio"))
@io.reopen(obj)
@io.string.should == "to_strio"
end
@@ -238,40 +208,40 @@ end
# for details.
describe "StringIO#reopen" do
before :each do
- @io = StringIO.new('hello','a')
+ @io = StringIO.new(+'hello', 'a')
end
# TODO: find out if this is really a bug
it "reopens a stream when given a String argument" do
- @io.reopen('goodbye').should == @io
+ @io.reopen(+'goodbye').should == @io
@io.string.should == 'goodbye'
@io << 'x'
@io.string.should == 'xoodbye'
end
it "reopens a stream in append mode when flagged as such" do
- @io.reopen('goodbye', 'a').should == @io
+ @io.reopen(+'goodbye', 'a').should == @io
@io.string.should == 'goodbye'
@io << 'x'
@io.string.should == 'goodbyex'
end
it "reopens and truncate when reopened in write mode" do
- @io.reopen('goodbye', 'wb').should == @io
+ @io.reopen(+'goodbye', 'wb').should == @io
@io.string.should == ''
@io << 'x'
@io.string.should == 'x'
end
it "truncates the given string, not a copy" do
- str = 'goodbye'
+ str = +'goodbye'
@io.reopen(str, 'w')
@io.string.should == ''
str.should == ''
end
it "does not truncate the content even when the StringIO argument is in the truncate mode" do
- orig_io = StringIO.new("Original StringIO", IO::RDWR|IO::TRUNC)
+ orig_io = StringIO.new(+"Original StringIO", IO::RDWR|IO::TRUNC)
orig_io.write("BLAH") # make sure the content is not empty
@io.reopen(orig_io)
diff --git a/spec/ruby/library/stringio/shared/codepoints.rb b/spec/ruby/library/stringio/shared/codepoints.rb
index 9d84aa4919..25333bb0fd 100644
--- a/spec/ruby/library/stringio/shared/codepoints.rb
+++ b/spec/ruby/library/stringio/shared/codepoints.rb
@@ -27,7 +27,7 @@ describe :stringio_codepoints, shared: true do
@io.close_read
-> { @enum.to_a }.should raise_error(IOError)
- io = StringIO.new("xyz", "w")
+ io = StringIO.new(+"xyz", "w")
-> { io.send(@method).to_a }.should raise_error(IOError)
end
diff --git a/spec/ruby/library/stringio/shared/each.rb b/spec/ruby/library/stringio/shared/each.rb
index bf3265ee46..e0dd3f9b8f 100644
--- a/spec/ruby/library/stringio/shared/each.rb
+++ b/spec/ruby/library/stringio/shared/each.rb
@@ -36,7 +36,7 @@ describe :stringio_each_separator, shared: true do
seen.should == ["2 1 2 1 2"]
end
- ruby_version_is ''..."3.2" do
+ version_is StringIO::VERSION, ""..."3.0.4" do #ruby_version_is ""..."3.2" do
it "yields each paragraph with two separation characters when passed an empty String as separator" do
seen = []
io = StringIO.new("para1\n\npara2\n\n\npara3")
@@ -45,7 +45,7 @@ describe :stringio_each_separator, shared: true do
end
end
- ruby_version_is "3.2" do
+ version_is StringIO::VERSION, "3.0.4" do #ruby_version_is "3.2" do
it "yields each paragraph with all separation characters when passed an empty String as separator" do
seen = []
io = StringIO.new("para1\n\npara2\n\n\npara3")
@@ -107,7 +107,7 @@ end
describe :stringio_each_not_readable, shared: true do
it "raises an IOError" do
- io = StringIO.new("a b c d e", "w")
+ io = StringIO.new(+"a b c d e", "w")
-> { io.send(@method) { |b| b } }.should raise_error(IOError)
io = StringIO.new("a b c d e")
diff --git a/spec/ruby/library/stringio/shared/each_byte.rb b/spec/ruby/library/stringio/shared/each_byte.rb
index 56734ff99d..b51fa38f2f 100644
--- a/spec/ruby/library/stringio/shared/each_byte.rb
+++ b/spec/ruby/library/stringio/shared/each_byte.rb
@@ -38,7 +38,7 @@ end
describe :stringio_each_byte_not_readable, shared: true do
it "raises an IOError" do
- io = StringIO.new("xyz", "w")
+ io = StringIO.new(+"xyz", "w")
-> { io.send(@method) { |b| b } }.should raise_error(IOError)
io = StringIO.new("xyz")
diff --git a/spec/ruby/library/stringio/shared/each_char.rb b/spec/ruby/library/stringio/shared/each_char.rb
index bcdac53282..197237c1c8 100644
--- a/spec/ruby/library/stringio/shared/each_char.rb
+++ b/spec/ruby/library/stringio/shared/each_char.rb
@@ -26,7 +26,7 @@ end
describe :stringio_each_char_not_readable, shared: true do
it "raises an IOError" do
- io = StringIO.new("xyz", "w")
+ io = StringIO.new(+"xyz", "w")
-> { io.send(@method) { |b| b } }.should raise_error(IOError)
io = StringIO.new("xyz")
diff --git a/spec/ruby/library/stringio/shared/getc.rb b/spec/ruby/library/stringio/shared/getc.rb
index 6318bcc30f..ba65040bce 100644
--- a/spec/ruby/library/stringio/shared/getc.rb
+++ b/spec/ruby/library/stringio/shared/getc.rb
@@ -33,7 +33,7 @@ end
describe :stringio_getc_not_readable, shared: true do
it "raises an IOError" do
- io = StringIO.new("xyz", "w")
+ io = StringIO.new(+"xyz", "w")
-> { io.send(@method) }.should raise_error(IOError)
io = StringIO.new("xyz")
diff --git a/spec/ruby/library/stringio/shared/isatty.rb b/spec/ruby/library/stringio/shared/isatty.rb
index 3da5999953..c9e7ee7321 100644
--- a/spec/ruby/library/stringio/shared/isatty.rb
+++ b/spec/ruby/library/stringio/shared/isatty.rb
@@ -1,5 +1,5 @@
describe :stringio_isatty, shared: true do
it "returns false" do
- StringIO.new('tty').send(@method).should be_false
+ StringIO.new("tty").send(@method).should be_false
end
end
diff --git a/spec/ruby/library/stringio/shared/read.rb b/spec/ruby/library/stringio/shared/read.rb
index 252a85d89d..e3840786d9 100644
--- a/spec/ruby/library/stringio/shared/read.rb
+++ b/spec/ruby/library/stringio/shared/read.rb
@@ -5,19 +5,19 @@ describe :stringio_read, shared: true do
it "returns the passed buffer String" do
# Note: Rubinius bug:
- # @io.send(@method, 7, buffer = "").should equal(buffer)
- ret = @io.send(@method, 7, buffer = "")
+ # @io.send(@method, 7, buffer = +"").should equal(buffer)
+ ret = @io.send(@method, 7, buffer = +"")
ret.should equal(buffer)
end
it "reads length bytes and writes them to the buffer String" do
- @io.send(@method, 7, buffer = "")
+ @io.send(@method, 7, buffer = +"")
buffer.should == "example"
end
it "tries to convert the passed buffer Object to a String using #to_str" do
obj = mock("to_str")
- obj.should_receive(:to_str).and_return(buffer = "")
+ obj.should_receive(:to_str).and_return(buffer = +"")
@io.send(@method, 7, obj)
buffer.should == "example"
@@ -75,7 +75,7 @@ end
describe :stringio_read_no_arguments, shared: true do
before :each do
- @io = StringIO.new("example")
+ @io = StringIO.new(+"example")
end
it "reads the whole content starting from the current position" do
@@ -117,7 +117,7 @@ end
describe :stringio_read_not_readable, shared: true do
it "raises an IOError" do
- io = StringIO.new("test", "w")
+ io = StringIO.new(+"test", "w")
-> { io.send(@method) }.should raise_error(IOError)
io = StringIO.new("test")
diff --git a/spec/ruby/library/stringio/shared/readchar.rb b/spec/ruby/library/stringio/shared/readchar.rb
index 4248e75420..72d7446c36 100644
--- a/spec/ruby/library/stringio/shared/readchar.rb
+++ b/spec/ruby/library/stringio/shared/readchar.rb
@@ -19,7 +19,7 @@ end
describe :stringio_readchar_not_readable, shared: true do
it "raises an IOError" do
- io = StringIO.new("a b c d e", "w")
+ io = StringIO.new(+"a b c d e", "w")
-> { io.send(@method) }.should raise_error(IOError)
io = StringIO.new("a b c d e")
diff --git a/spec/ruby/library/stringio/shared/sysread.rb b/spec/ruby/library/stringio/shared/sysread.rb
index 3376bd9907..937bac705c 100644
--- a/spec/ruby/library/stringio/shared/sysread.rb
+++ b/spec/ruby/library/stringio/shared/sysread.rb
@@ -1,4 +1,4 @@
-describe :stringio_sysread_length, :shared => true do
+describe :stringio_sysread_length, shared: true do
before :each do
@io = StringIO.new("example")
end
diff --git a/spec/ruby/library/stringio/shared/write.rb b/spec/ruby/library/stringio/shared/write.rb
index b91e6ecec1..404e08b93d 100644
--- a/spec/ruby/library/stringio/shared/write.rb
+++ b/spec/ruby/library/stringio/shared/write.rb
@@ -1,6 +1,6 @@
describe :stringio_write, shared: true do
before :each do
- @io = StringIO.new('12345')
+ @io = StringIO.new(+'12345')
end
it "tries to convert the passed Object to a String using #to_s" do
@@ -13,7 +13,7 @@ end
describe :stringio_write_string, shared: true do
before :each do
- @io = StringIO.new('12345')
+ @io = StringIO.new(+'12345')
end
# TODO: RDoc says that #write appends at the current position.
@@ -60,13 +60,6 @@ describe :stringio_write_string, shared: true do
@io.string.size.should == n.times.map(&:to_s).join.size
end
- ruby_version_is ""..."3.0" do
- it "does not taint self when the passed argument is tainted" do
- @io.send(@method, "test".taint)
- @io.tainted?.should be_false
- end
- end
-
it "handles writing non-ASCII UTF-8 after seek" do
@io.binmode
@io << "\x80"
@@ -113,10 +106,10 @@ end
describe :stringio_write_not_writable, shared: true do
it "raises an IOError" do
- io = StringIO.new("test", "r")
+ io = StringIO.new(+"test", "r")
-> { io.send(@method, "test") }.should raise_error(IOError)
- io = StringIO.new("test")
+ io = StringIO.new(+"test")
io.close_write
-> { io.send(@method, "test") }.should raise_error(IOError)
end
@@ -124,7 +117,7 @@ end
describe :stringio_write_append, shared: true do
before :each do
- @io = StringIO.new("example", "a")
+ @io = StringIO.new(+"example", "a")
end
it "appends the passed argument to the end of self" do
diff --git a/spec/ruby/library/stringio/truncate_spec.rb b/spec/ruby/library/stringio/truncate_spec.rb
index e8d7f1a15d..592ca5a6e1 100644
--- a/spec/ruby/library/stringio/truncate_spec.rb
+++ b/spec/ruby/library/stringio/truncate_spec.rb
@@ -3,7 +3,7 @@ require "stringio"
describe "StringIO#truncate when passed [length]" do
before :each do
- @io = StringIO.new('123456789')
+ @io = StringIO.new(+'123456789')
end
it "returns an Integer" do
@@ -16,7 +16,7 @@ describe "StringIO#truncate when passed [length]" do
end
it "does not create a copy of the underlying string" do
- io = StringIO.new(str = "123456789")
+ io = StringIO.new(str = +"123456789")
io.truncate(4)
io.string.should equal(str)
end
@@ -52,10 +52,10 @@ end
describe "StringIO#truncate when self is not writable" do
it "raises an IOError" do
- io = StringIO.new("test", "r")
+ io = StringIO.new(+"test", "r")
-> { io.truncate(2) }.should raise_error(IOError)
- io = StringIO.new("test")
+ io = StringIO.new(+"test")
io.close_write
-> { io.truncate(2) }.should raise_error(IOError)
end
diff --git a/spec/ruby/library/stringio/ungetc_spec.rb b/spec/ruby/library/stringio/ungetc_spec.rb
index 91ef2100a1..bceafa79ff 100644
--- a/spec/ruby/library/stringio/ungetc_spec.rb
+++ b/spec/ruby/library/stringio/ungetc_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/classes'
describe "StringIO#ungetc when passed [char]" do
before :each do
- @io = StringIO.new('1234')
+ @io = StringIO.new(+'1234')
end
it "writes the passed char before the current position" do
@@ -45,11 +45,11 @@ end
describe "StringIO#ungetc when self is not readable" do
it "raises an IOError" do
- io = StringIO.new("test", "w")
+ io = StringIO.new(+"test", "w")
io.pos = 1
-> { io.ungetc(?A) }.should raise_error(IOError)
- io = StringIO.new("test")
+ io = StringIO.new(+"test")
io.pos = 1
io.close_read
-> { io.ungetc(?A) }.should raise_error(IOError)
@@ -60,11 +60,11 @@ end
#
# describe "StringIO#ungetc when self is not writable" do
# it "raises an IOError" do
-# io = StringIO.new("test", "r")
+# io = StringIO.new(+"test", "r")
# io.pos = 1
# lambda { io.ungetc(?A) }.should raise_error(IOError)
#
-# io = StringIO.new("test")
+# io = StringIO.new(+"test")
# io.pos = 1
# io.close_write
# lambda { io.ungetc(?A) }.should raise_error(IOError)
diff --git a/spec/ruby/library/stringio/write_nonblock_spec.rb b/spec/ruby/library/stringio/write_nonblock_spec.rb
index a457b97667..b48ef6698a 100644
--- a/spec/ruby/library/stringio/write_nonblock_spec.rb
+++ b/spec/ruby/library/stringio/write_nonblock_spec.rb
@@ -10,7 +10,7 @@ describe "StringIO#write_nonblock when passed [String]" do
it_behaves_like :stringio_write_string, :write_nonblock
it "accepts :exception option" do
- io = StringIO.new("12345", "a")
+ io = StringIO.new(+"12345", "a")
io.write_nonblock("67890", exception: true)
io.string.should == "1234567890"
end
diff --git a/spec/ruby/library/stringscanner/getch_spec.rb b/spec/ruby/library/stringscanner/getch_spec.rb
index a6be0d4221..449c20ad3b 100644
--- a/spec/ruby/library/stringscanner/getch_spec.rb
+++ b/spec/ruby/library/stringscanner/getch_spec.rb
@@ -13,7 +13,7 @@ describe "StringScanner#getch" do
it "is multi-byte character sensitive" do
# Japanese hiragana "A" in EUC-JP
- src = "\244\242".force_encoding("euc-jp")
+ src = "\244\242".dup.force_encoding("euc-jp")
s = StringScanner.new(src)
s.getch.should == src
diff --git a/spec/ruby/library/stringscanner/shared/concat.rb b/spec/ruby/library/stringscanner/shared/concat.rb
index cb884a5c01..1dbae11f7c 100644
--- a/spec/ruby/library/stringscanner/shared/concat.rb
+++ b/spec/ruby/library/stringscanner/shared/concat.rb
@@ -1,6 +1,6 @@
describe :strscan_concat, shared: true do
it "concatenates the given argument to self and returns self" do
- s = StringScanner.new("hello ")
+ s = StringScanner.new(+"hello ")
s.send(@method, 'world').should == s
s.string.should == "hello world"
s.eos?.should be_false
diff --git a/spec/ruby/library/stringscanner/string_spec.rb b/spec/ruby/library/stringscanner/string_spec.rb
index 28e2f0ed37..cba6bd51dd 100644
--- a/spec/ruby/library/stringscanner/string_spec.rb
+++ b/spec/ruby/library/stringscanner/string_spec.rb
@@ -3,7 +3,7 @@ require 'strscan'
describe "StringScanner#string" do
before :each do
- @string = "This is a test"
+ @string = +"This is a test"
@s = StringScanner.new(@string)
end
diff --git a/spec/ruby/library/time/to_datetime_spec.rb b/spec/ruby/library/time/to_datetime_spec.rb
index 6025950b59..9c44f38e5c 100644
--- a/spec/ruby/library/time/to_datetime_spec.rb
+++ b/spec/ruby/library/time/to_datetime_spec.rb
@@ -1,5 +1,7 @@
require_relative '../../spec_helper'
require 'time'
+require 'date'
+date_version = defined?(Date::VERSION) ? Date::VERSION : '3.1.0'
describe "Time#to_datetime" do
it "returns a DateTime representing the same instant" do
@@ -13,8 +15,7 @@ describe "Time#to_datetime" do
datetime.sec.should == 59
end
- date_version = defined?(Date::VERSION) ? Date::VERSION : '0.0.0'
- version_is(date_version, '3.2.3') do
+ version_is date_version, '3.2.3' do #ruby_version_is '3.2' do
it "returns a DateTime representing the same instant before Gregorian" do
time = Time.utc(1582, 10, 14, 23, 58, 59)
datetime = time.to_datetime
diff --git a/spec/ruby/library/uri/generic/host_spec.rb b/spec/ruby/library/uri/generic/host_spec.rb
index 2de0e7ec09..210124ef66 100644
--- a/spec/ruby/library/uri/generic/host_spec.rb
+++ b/spec/ruby/library/uri/generic/host_spec.rb
@@ -2,7 +2,7 @@ require_relative '../../../spec_helper'
require 'uri'
describe "URI::Generic#host" do
- ruby_version_is "3.2" do
+ version_is URI::VERSION, "0.12" do #ruby_version_is "3.2" do
# https://hackerone.com/reports/156615
it "returns empty string when host is empty" do
URI.parse('http:////foo.com').host.should == ''
diff --git a/spec/ruby/library/uri/generic/to_s_spec.rb b/spec/ruby/library/uri/generic/to_s_spec.rb
index 1dd1f2d134..8cebd374a1 100644
--- a/spec/ruby/library/uri/generic/to_s_spec.rb
+++ b/spec/ruby/library/uri/generic/to_s_spec.rb
@@ -2,7 +2,7 @@ require_relative '../../../spec_helper'
require 'uri'
describe "URI::Generic#to_s" do
- ruby_version_is "3.2" do
+ version_is URI::VERSION, "0.12" do #ruby_version_is "3.2" do
# https://hackerone.com/reports/156615
it "preserves / characters when host is empty" do
URI('http:///foo.com').to_s.should == 'http:///foo.com'
diff --git a/spec/ruby/library/yaml/dump_spec.rb b/spec/ruby/library/yaml/dump_spec.rb
index 3107a8f51d..ea94b2f856 100644
--- a/spec/ruby/library/yaml/dump_spec.rb
+++ b/spec/ruby/library/yaml/dump_spec.rb
@@ -1,17 +1,21 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/common'
-# TODO: WTF is this using a global?
+require 'yaml'
+
describe "YAML.dump" do
+ before :each do
+ @test_file = tmp("yaml_test_file")
+ end
+
after :each do
- rm_r $test_file
+ rm_r @test_file
end
it "converts an object to YAML and write result to io when io provided" do
- File.open($test_file, 'w' ) do |io|
+ File.open(@test_file, 'w' ) do |io|
YAML.dump( ['badger', 'elephant', 'tiger'], io )
end
- YAML.load_file($test_file).should == ['badger', 'elephant', 'tiger']
+ YAML.load_file(@test_file).should == ['badger', 'elephant', 'tiger']
end
it "returns a string containing dumped YAML when no io provided" do
diff --git a/spec/ruby/library/yaml/dump_stream_spec.rb b/spec/ruby/library/yaml/dump_stream_spec.rb
index 9d30fef819..f0578fa800 100644
--- a/spec/ruby/library/yaml/dump_stream_spec.rb
+++ b/spec/ruby/library/yaml/dump_stream_spec.rb
@@ -1,5 +1,6 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/common'
+
+require 'yaml'
describe "YAML.dump_stream" do
it "returns a YAML stream containing the objects passed" do
diff --git a/spec/ruby/library/yaml/fixtures/common.rb b/spec/ruby/library/yaml/fixtures/common.rb
deleted file mode 100644
index f7fb4037e7..0000000000
--- a/spec/ruby/library/yaml/fixtures/common.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-require 'yaml'
-
-$test_file = tmp("yaml_test_file")
-$test_parse_file = File.dirname(__FILE__) + "/test_yaml.yml"
diff --git a/spec/ruby/library/yaml/fixtures/strings.rb b/spec/ruby/library/yaml/fixtures/strings.rb
index 6f66dc3659..f478f89823 100644
--- a/spec/ruby/library/yaml/fixtures/strings.rb
+++ b/spec/ruby/library/yaml/fixtures/strings.rb
@@ -1,36 +1,26 @@
-$complex_key_1 = <<EOY
- ? # PLAY SCHEDULE
- - Detroit Tigers
- - Chicago Cubs
- :
- - 2001-07-23
+module YAMLSpecs
+ COMPLEX_KEY_1 = <<~EOY
+ ? # PLAY SCHEDULE
+ - Detroit Tigers
+ - Chicago Cubs
+ :
+ - 2001-07-23
- ? [ New York Yankees,
- Atlanta Braves ]
- : [ 2001-07-02, 2001-08-12,
- 2001-08-14 ]
-EOY
+ ? [ New York Yankees,
+ Atlanta Braves ]
+ : [ 2001-07-02, 2001-08-12,
+ 2001-08-14 ]
+ EOY
-$to_yaml_hash =
-<<EOY
--
- avg: 0.278
- hr: 65
- name: Mark McGwire
--
- avg: 0.288
- hr: 63
- name: Sammy Sosa
-EOY
+ MULTIDOCUMENT = <<~EOY
+ ---
+ - Mark McGwire
+ - Sammy Sosa
+ - Ken Griffey
-$multidocument = <<EOY
----
-- Mark McGwire
-- Sammy Sosa
-- Ken Griffey
-
-# Team ranking
----
-- Chicago Cubs
-- St Louis Cardinals
-EOY
+ # Team ranking
+ ---
+ - Chicago Cubs
+ - St Louis Cardinals
+ EOY
+end
diff --git a/spec/ruby/library/yaml/load_file_spec.rb b/spec/ruby/library/yaml/load_file_spec.rb
index 2363c08120..4941d0485b 100644
--- a/spec/ruby/library/yaml/load_file_spec.rb
+++ b/spec/ruby/library/yaml/load_file_spec.rb
@@ -1,13 +1,18 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/common'
+
+require 'yaml'
describe "YAML.load_file" do
+ before :each do
+ @test_file = tmp("yaml_test_file")
+ end
+
after :each do
- rm_r $test_file
+ rm_r @test_file
end
it "returns a hash" do
- File.open($test_file,'w' ){|io| YAML.dump( {"bar"=>2, "car"=>1}, io ) }
- YAML.load_file($test_file).should == {"bar"=>2, "car"=>1}
+ File.open(@test_file,'w' ){|io| YAML.dump( {"bar"=>2, "car"=>1}, io ) }
+ YAML.load_file(@test_file).should == {"bar"=>2, "car"=>1}
end
end
diff --git a/spec/ruby/library/yaml/load_stream_spec.rb b/spec/ruby/library/yaml/load_stream_spec.rb
index 689653c8cd..31bc862f5e 100644
--- a/spec/ruby/library/yaml/load_stream_spec.rb
+++ b/spec/ruby/library/yaml/load_stream_spec.rb
@@ -1,8 +1,9 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/common'
require_relative 'fixtures/strings'
require_relative 'shared/each_document'
+require 'yaml'
+
describe "YAML.load_stream" do
it_behaves_like :yaml_each_document, :load_stream
end
diff --git a/spec/ruby/library/yaml/parse_file_spec.rb b/spec/ruby/library/yaml/parse_file_spec.rb
index 8c59a2d7ef..7bffcdc62f 100644
--- a/spec/ruby/library/yaml/parse_file_spec.rb
+++ b/spec/ruby/library/yaml/parse_file_spec.rb
@@ -1,8 +1,10 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/common'
-describe "YAML#parse_file" do
+require 'yaml'
+
+describe "YAML.parse_file" do
it "returns a YAML::Syck::Map object after parsing a YAML file" do
- YAML.parse_file($test_parse_file).should be_kind_of(Psych::Nodes::Document)
+ test_parse_file = fixture __FILE__, "test_yaml.yml"
+ YAML.parse_file(test_parse_file).should be_kind_of(Psych::Nodes::Document)
end
end
diff --git a/spec/ruby/library/yaml/parse_spec.rb b/spec/ruby/library/yaml/parse_spec.rb
index d5dbfdcee2..37e2b7fa0a 100644
--- a/spec/ruby/library/yaml/parse_spec.rb
+++ b/spec/ruby/library/yaml/parse_spec.rb
@@ -1,13 +1,14 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/common'
-describe "YAML#parse with an empty string" do
+require 'yaml'
+
+describe "YAML.parse with an empty string" do
it "returns false" do
YAML.parse('').should be_false
end
end
-describe "YAML#parse" do
+describe "YAML.parse" do
before :each do
@string_yaml = "foo".to_yaml
end
diff --git a/spec/ruby/library/yaml/shared/each_document.rb b/spec/ruby/library/yaml/shared/each_document.rb
index 999123dc2a..6f00aee297 100644
--- a/spec/ruby/library/yaml/shared/each_document.rb
+++ b/spec/ruby/library/yaml/shared/each_document.rb
@@ -1,7 +1,7 @@
describe :yaml_each_document, shared: true do
it "calls the block on each successive document" do
documents = []
- YAML.send(@method, $multidocument) do |doc|
+ YAML.send(@method, YAMLSpecs::MULTIDOCUMENT) do |doc|
documents << doc
end
documents.should == [["Mark McGwire", "Sammy Sosa", "Ken Griffey"],
@@ -9,7 +9,8 @@ describe :yaml_each_document, shared: true do
end
it "works on files" do
- File.open($test_parse_file, "r") do |file|
+ test_parse_file = fixture __FILE__, "test_yaml.yml"
+ File.open(test_parse_file, "r") do |file|
YAML.send(@method, file) do |doc|
doc.should == {"project"=>{"name"=>"RubySpec"}}
end
diff --git a/spec/ruby/library/yaml/shared/load.rb b/spec/ruby/library/yaml/shared/load.rb
index 185a5a60cd..bd55332fe5 100644
--- a/spec/ruby/library/yaml/shared/load.rb
+++ b/spec/ruby/library/yaml/shared/load.rb
@@ -1,14 +1,16 @@
-require_relative '../fixtures/common'
require_relative '../fixtures/strings'
+require 'yaml'
+
describe :yaml_load_safe, shared: true do
it "returns a document from current io stream when io provided" do
- File.open($test_file, 'w') do |io|
+ @test_file = tmp("yaml_test_file")
+ File.open(@test_file, 'w') do |io|
YAML.dump( ['badger', 'elephant', 'tiger'], io )
end
- File.open($test_file) { |yf| YAML.send(@method, yf ) }.should == ['badger', 'elephant', 'tiger']
+ File.open(@test_file) { |yf| YAML.send(@method, yf ) }.should == ['badger', 'elephant', 'tiger']
ensure
- rm_r $test_file
+ rm_r @test_file
end
it "loads strings" do
@@ -104,7 +106,7 @@ describe :yaml_load_unsafe, shared: true do
Date.new( 2001, 8, 12 ),
Date.new( 2001, 8, 14 ) ]
}
- YAML.send(@method, $complex_key_1).should == expected
+ YAML.send(@method, YAMLSpecs::COMPLEX_KEY_1).should == expected
end
describe "with iso8601 timestamp" do
diff --git a/spec/ruby/library/yaml/to_yaml_spec.rb b/spec/ruby/library/yaml/to_yaml_spec.rb
index 8e80b02cb4..547009c942 100644
--- a/spec/ruby/library/yaml/to_yaml_spec.rb
+++ b/spec/ruby/library/yaml/to_yaml_spec.rb
@@ -1,7 +1,8 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/common'
require_relative 'fixtures/example_class'
+require 'yaml'
+
describe "Object#to_yaml" do
it "returns the YAML representation of an Array object" do
@@ -12,13 +13,21 @@ describe "Object#to_yaml" do
{ "a" => "b"}.to_yaml.should match_yaml("--- \na: b\n")
end
- it "returns the YAML representation of a Class object" do
+ it "returns the YAML representation of an object" do
YAMLSpecs::Example.new("baz").to_yaml.should match_yaml("--- !ruby/object:YAMLSpecs::Example\nname: baz\n")
end
+ it "returns the YAML representation of a Class object" do
+ YAMLSpecs::Example.to_yaml.should match_yaml("--- !ruby/class 'YAMLSpecs::Example'\n")
+ end
+
+ it "returns the YAML representation of a Module object" do
+ Enumerable.to_yaml.should match_yaml("--- !ruby/module 'Enumerable'\n")
+ end
+
it "returns the YAML representation of a Date object" do
require 'date'
- Date.parse('1997/12/30').to_yaml.should match_yaml("--- 1997-12-30\n")
+ Date.new(1997, 12, 30).to_yaml.should match_yaml("--- 1997-12-30\n")
end
it "returns the YAML representation of a FalseClass" do
@@ -58,6 +67,11 @@ describe "Object#to_yaml" do
Person.new("Jane", "female").to_yaml.should match_yaml("--- !ruby/struct:Person\nname: Jane\ngender: female\n")
end
+ it "returns the YAML representation of an unnamed Struct object" do
+ person = Struct.new(:name, :gender)
+ person.new("Jane", "female").to_yaml.should match_yaml("--- !ruby/struct\nname: Jane\ngender: female\n")
+ end
+
it "returns the YAML representation of a Symbol object" do
:symbol.to_yaml.should match_yaml("--- :symbol\n")
end
diff --git a/spec/ruby/library/zlib/deflate/deflate_spec.rb b/spec/ruby/library/zlib/deflate/deflate_spec.rb
index 50a563ef6f..e16e6ad0ef 100644
--- a/spec/ruby/library/zlib/deflate/deflate_spec.rb
+++ b/spec/ruby/library/zlib/deflate/deflate_spec.rb
@@ -23,7 +23,7 @@ describe "Zlib::Deflate.deflate" do
it "deflates chunked data" do
random_generator = Random.new(0)
- deflated = ''
+ deflated = +''
Zlib::Deflate.deflate(random_generator.bytes(20000)) do |chunk|
deflated << chunk
@@ -70,7 +70,7 @@ describe "Zlib::Deflate#deflate" do
before :each do
@deflator = Zlib::Deflate.new
@random_generator = Random.new(0)
- @original = ''
+ @original = +''
@chunks = []
end
diff --git a/spec/ruby/library/zlib/deflate/new_spec.rb b/spec/ruby/library/zlib/deflate/new_spec.rb
deleted file mode 100644
index e15f14f95f..0000000000
--- a/spec/ruby/library/zlib/deflate/new_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/deflate/params_spec.rb b/spec/ruby/library/zlib/deflate/params_spec.rb
index 0b1cca8c8a..0242653528 100644
--- a/spec/ruby/library/zlib/deflate/params_spec.rb
+++ b/spec/ruby/library/zlib/deflate/params_spec.rb
@@ -3,7 +3,7 @@ require 'zlib'
describe "Zlib::Deflate#params" do
it "changes the deflate parameters" do
- data = 'abcdefghijklm'
+ data = +'abcdefghijklm'
d = Zlib::Deflate.new Zlib::NO_COMPRESSION, Zlib::MAX_WBITS,
Zlib::DEF_MEM_LEVEL, Zlib::DEFAULT_STRATEGY
diff --git a/spec/ruby/library/zlib/gzipreader/new_spec.rb b/spec/ruby/library/zlib/gzipreader/new_spec.rb
deleted file mode 100644
index e15f14f95f..0000000000
--- a/spec/ruby/library/zlib/gzipreader/new_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/inflate/inflate_spec.rb b/spec/ruby/library/zlib/inflate/inflate_spec.rb
index 79b72bf91c..b308a4ba67 100644
--- a/spec/ruby/library/zlib/inflate/inflate_spec.rb
+++ b/spec/ruby/library/zlib/inflate/inflate_spec.rb
@@ -72,7 +72,7 @@ describe "Zlib::Inflate.inflate" do
data = [120, 156, 75, 203, 207, 7, 0, 2, 130, 1, 69].pack('C*')
z = Zlib::Inflate.new
# add bytes, one by one
- result = ""
+ result = +""
data.each_byte { |d| result << z.inflate(d.chr)}
result << z.finish
result.should == "foo"
@@ -82,7 +82,7 @@ describe "Zlib::Inflate.inflate" do
data = [120, 156, 75, 203, 207, 7, 0, 2, 130, 1, 69].pack('C*')[0,5]
z = Zlib::Inflate.new
# add bytes, one by one, but not all
- result = ""
+ result = +""
data.each_byte { |d| result << z.inflate(d.chr)}
-> { result << z.finish }.should raise_error(Zlib::BufError)
end
@@ -90,7 +90,7 @@ describe "Zlib::Inflate.inflate" do
it "properly handles excessive data, byte-by-byte" do
main_data = [120, 156, 75, 203, 207, 7, 0, 2, 130, 1, 69].pack('C*')
data = main_data * 2
- result = ""
+ result = +""
z = Zlib::Inflate.new
# add bytes, one by one
@@ -105,7 +105,7 @@ describe "Zlib::Inflate.inflate" do
it "properly handles excessive data, in one go" do
main_data = [120, 156, 75, 203, 207, 7, 0, 2, 130, 1, 69].pack('C*')
data = main_data * 2
- result = ""
+ result = +""
z = Zlib::Inflate.new
result << z.inflate(data)
diff --git a/spec/ruby/library/zlib/inflate/new_spec.rb b/spec/ruby/library/zlib/inflate/new_spec.rb
deleted file mode 100644
index e15f14f95f..0000000000
--- a/spec/ruby/library/zlib/inflate/new_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../../spec_helper'
diff --git a/spec/ruby/optional/capi/array_spec.rb b/spec/ruby/optional/capi/array_spec.rb
index 8e90980c6a..9c35017e21 100644
--- a/spec/ruby/optional/capi/array_spec.rb
+++ b/spec/ruby/optional/capi/array_spec.rb
@@ -343,6 +343,40 @@ describe "C-API Array function" do
end
end
+ describe "rb_iterate" do
+ it "calls an callback function as a block passed to an method" do
+ s = [1,2,3,4]
+ s2 = @s.rb_iterate(s)
+
+ s2.should == s
+
+ # Make sure they're different objects
+ s2.equal?(s).should be_false
+ end
+
+ it "calls a function with the other function available as a block" do
+ h = {a: 1, b: 2}
+
+ @s.rb_iterate_each_pair(h).sort.should == [1,2]
+ end
+
+ it "calls a function which can yield into the original block" do
+ s2 = []
+
+ o = Object.new
+ def o.each
+ yield 1
+ yield 2
+ yield 3
+ yield 4
+ end
+
+ @s.rb_iterate_then_yield(o) { |x| s2 << x }
+
+ s2.should == [1,2,3,4]
+ end
+ end
+
describe "rb_block_call" do
it "calls an callback function as a block passed to an method" do
s = [1,2,3,4]
diff --git a/spec/ruby/optional/capi/class_spec.rb b/spec/ruby/optional/capi/class_spec.rb
index 66af381243..d0a9913570 100644
--- a/spec/ruby/optional/capi/class_spec.rb
+++ b/spec/ruby/optional/capi/class_spec.rb
@@ -119,23 +119,21 @@ describe "C-API Class function" do
end
end
- ruby_version_is "3.0" do
- describe "rb_class_new_instance_kw" do
- it "passes arguments and keywords to the #initialize method" do
- obj = @s.rb_class_new_instance_kw([{pos: 1}, {kw: 2}], CApiClassSpecs::KeywordAlloc)
- obj.args.should == [{pos: 1}]
- obj.kwargs.should == {kw: 2}
-
- obj = @s.rb_class_new_instance_kw([{}], CApiClassSpecs::KeywordAlloc)
- obj.args.should == []
- obj.kwargs.should == {}
- end
-
- it "raises TypeError if the last argument is not a Hash" do
- -> {
- @s.rb_class_new_instance_kw([42], CApiClassSpecs::KeywordAlloc)
- }.should raise_error(TypeError, 'no implicit conversion of Integer into Hash')
- end
+ describe "rb_class_new_instance_kw" do
+ it "passes arguments and keywords to the #initialize method" do
+ obj = @s.rb_class_new_instance_kw([{pos: 1}, {kw: 2}], CApiClassSpecs::KeywordAlloc)
+ obj.args.should == [{pos: 1}]
+ obj.kwargs.should == {kw: 2}
+
+ obj = @s.rb_class_new_instance_kw([{}], CApiClassSpecs::KeywordAlloc)
+ obj.args.should == []
+ obj.kwargs.should == {}
+ end
+
+ it "raises TypeError if the last argument is not a Hash" do
+ -> {
+ @s.rb_class_new_instance_kw([42], CApiClassSpecs::KeywordAlloc)
+ }.should raise_error(TypeError, 'no implicit conversion of Integer into Hash')
end
end
diff --git a/spec/ruby/optional/capi/debug_spec.rb b/spec/ruby/optional/capi/debug_spec.rb
index c8c91417d1..148b8c38fb 100644
--- a/spec/ruby/optional/capi/debug_spec.rb
+++ b/spec/ruby/optional/capi/debug_spec.rb
@@ -17,6 +17,7 @@ describe "C-API Debug function" do
describe "rb_debug_inspector_frame_self_get" do
it "returns self" do
@o.rb_debug_inspector_frame_self_get(0).should == @o
+ @o.rb_debug_inspector_frame_self_get(1).should == self
end
end
@@ -35,10 +36,14 @@ describe "C-API Debug function" do
end
it "matches the locations in rb_debug_inspector_backtrace_locations" do
- frames = @o.rb_debug_inspector_open(42);
+ frames = @o.rb_debug_inspector_open(42)
frames.each do |_s, _klass, binding, _iseq, backtrace_location|
if binding
- "#{backtrace_location.path}:#{backtrace_location.lineno}".should == "#{binding.source_location[0]}:#{binding.source_location[1]}"
+ binding.source_location.should == [backtrace_location.path, backtrace_location.lineno]
+ method_name = binding.eval('__method__')
+ if method_name
+ method_name.should == backtrace_location.base_label.to_sym
+ end
end
end
end
diff --git a/spec/ruby/optional/capi/encoding_spec.rb b/spec/ruby/optional/capi/encoding_spec.rb
index aa632b963b..1529e012b0 100644
--- a/spec/ruby/optional/capi/encoding_spec.rb
+++ b/spec/ruby/optional/capi/encoding_spec.rb
@@ -1,8 +1,9 @@
# -*- encoding: utf-8 -*-
+# frozen_string_literal: false
require_relative 'spec_helper'
require_relative 'fixtures/encoding'
-load_extension('encoding')
+extension_path = load_extension('encoding')
describe :rb_enc_get_index, shared: true do
it "returns the index of the encoding of a String" do
@@ -559,19 +560,19 @@ describe "C-API Encoding function" do
describe "rb_ascii8bit_encindex" do
it "returns an index for the ASCII-8BIT encoding" do
- @s.rb_ascii8bit_encindex().should >= 0
+ @s.rb_ascii8bit_encindex().should == 0
end
end
describe "rb_utf8_encindex" do
it "returns an index for the UTF-8 encoding" do
- @s.rb_utf8_encindex().should >= 0
+ @s.rb_utf8_encindex().should == 1
end
end
describe "rb_usascii_encindex" do
it "returns an index for the US-ASCII encoding" do
- @s.rb_usascii_encindex().should >= 0
+ @s.rb_usascii_encindex().should == 2
end
end
@@ -657,6 +658,20 @@ describe "C-API Encoding function" do
end
end
+ describe "rb_enc_raise" do
+ it "forces exception message encoding to the specified one" do
+ utf_8_incompatible_string = "\x81".b
+
+ -> {
+ @s.rb_enc_raise(Encoding::UTF_8, RuntimeError, utf_8_incompatible_string)
+ }.should raise_error { |e|
+ e.message.encoding.should == Encoding::UTF_8
+ e.message.valid_encoding?.should == false
+ e.message.bytes.should == utf_8_incompatible_string.bytes
+ }
+ end
+ end
+
describe "rb_uv_to_utf8" do
it 'converts a Unicode codepoint to a UTF-8 C string' do
str = ' ' * 6
@@ -674,6 +689,22 @@ describe "C-API Encoding function" do
end
end
+ describe "rb_enc_left_char_head" do
+ it 'returns the head position of a character' do
+ @s.rb_enc_left_char_head("é", 1).should == 0
+ @s.rb_enc_left_char_head("éééé", 7).should == 6
+
+ @s.rb_enc_left_char_head("a", 0).should == 0
+
+ # unclear if this is intended to work
+ @s.rb_enc_left_char_head("a", 1).should == 1
+
+ # Works because for single-byte encodings rb_enc_left_char_head() just returns the pointer
+ @s.rb_enc_left_char_head("a".force_encoding(Encoding::US_ASCII), 88).should == 88
+ @s.rb_enc_left_char_head("a".b, 88).should == 88
+ end
+ end
+
describe "ONIGENC_MBC_CASE_FOLD" do
it "returns the correct case fold for the given string" do
@s.ONIGENC_MBC_CASE_FOLD("lower").should == ["l", 1]
@@ -691,4 +722,27 @@ describe "C-API Encoding function" do
str.bytes.should == [0, 0x24]
end
end
+
+ describe "rb_define_dummy_encoding" do
+ it "defines the dummy encoding" do
+ @s.rb_define_dummy_encoding("FOO")
+ enc = Encoding.find("FOO")
+ enc.should.dummy?
+ end
+
+ it "returns the index of the dummy encoding" do
+ index = @s.rb_define_dummy_encoding("BAR")
+ index.should == Encoding.list.size - 1
+ end
+
+ ruby_version_is "3.2" do
+ it "raises EncodingError if too many encodings" do
+ code = <<-RUBY
+ require #{extension_path.dump}
+ 1_000.times {|i| CApiEncodingSpecs.new.rb_define_dummy_encoding("R_\#{i}") }
+ RUBY
+ ruby_exe(code, args: "2>&1", exit_status: 1).should.include?('too many encoding (> 256) (EncodingError)')
+ end
+ end
+ end
end
diff --git a/spec/ruby/optional/capi/exception_spec.rb b/spec/ruby/optional/capi/exception_spec.rb
index b0a8a2860e..5bb60608b2 100644
--- a/spec/ruby/optional/capi/exception_spec.rb
+++ b/spec/ruby/optional/capi/exception_spec.rb
@@ -100,6 +100,40 @@ describe "C-API Exception function" do
end
end
+ describe "rb_syserr_new" do
+ it "returns system error with default message when passed message is NULL" do
+ exception = @s.rb_syserr_new(Errno::ENOENT::Errno, nil)
+ exception.class.should == Errno::ENOENT
+ exception.message.should include("No such file or directory")
+ exception.should.is_a?(SystemCallError)
+ end
+
+ it "returns system error with custom message" do
+ exception = @s.rb_syserr_new(Errno::ENOENT::Errno, "custom message")
+
+ exception.message.should include("custom message")
+ exception.class.should == Errno::ENOENT
+ exception.should.is_a?(SystemCallError)
+ end
+ end
+
+ describe "rb_syserr_new_str" do
+ it "returns system error with default message when passed message is nil" do
+ exception = @s.rb_syserr_new_str(Errno::ENOENT::Errno, nil)
+
+ exception.message.should include("No such file or directory")
+ exception.class.should == Errno::ENOENT
+ exception.should.is_a?(SystemCallError)
+ end
+
+ it "returns system error with custom message" do
+ exception = @s.rb_syserr_new_str(Errno::ENOENT::Errno, "custom message")
+ exception.message.should include("custom message")
+ exception.class.should == Errno::ENOENT
+ exception.should.is_a?(SystemCallError)
+ end
+ end
+
describe "rb_make_exception" do
it "returns a RuntimeError when given a String argument" do
e = @s.rb_make_exception(["Message"])
diff --git a/spec/ruby/optional/capi/ext/array_spec.c b/spec/ruby/optional/capi/ext/array_spec.c
index 9386239813..2347798bb4 100644
--- a/spec/ruby/optional/capi/ext/array_spec.c
+++ b/spec/ruby/optional/capi/ext/array_spec.c
@@ -196,6 +196,14 @@ static VALUE copy_ary(RB_BLOCK_CALL_FUNC_ARGLIST(el, new_ary)) {
return rb_ary_push(new_ary, el);
}
+static VALUE array_spec_rb_iterate(VALUE self, VALUE ary) {
+ VALUE new_ary = rb_ary_new();
+
+ rb_iterate(rb_each, ary, copy_ary, new_ary);
+
+ return new_ary;
+}
+
static VALUE array_spec_rb_block_call(VALUE self, VALUE ary) {
VALUE new_ary = rb_ary_new();
@@ -208,6 +216,18 @@ static VALUE sub_pair(RB_BLOCK_CALL_FUNC_ARGLIST(el, holder)) {
return rb_ary_push(holder, rb_ary_entry(el, 1));
}
+static VALUE each_pair(VALUE obj) {
+ return rb_funcall(obj, rb_intern("each_pair"), 0);
+}
+
+static VALUE array_spec_rb_iterate_each_pair(VALUE self, VALUE obj) {
+ VALUE new_ary = rb_ary_new();
+
+ rb_iterate(each_pair, obj, sub_pair, new_ary);
+
+ return new_ary;
+}
+
static VALUE array_spec_rb_block_call_each_pair(VALUE self, VALUE obj) {
VALUE new_ary = rb_ary_new();
@@ -221,6 +241,11 @@ static VALUE iter_yield(RB_BLOCK_CALL_FUNC_ARGLIST(el, ary)) {
return Qnil;
}
+static VALUE array_spec_rb_iterate_then_yield(VALUE self, VALUE obj) {
+ rb_iterate(rb_each, obj, iter_yield, obj);
+ return Qnil;
+}
+
static VALUE array_spec_rb_block_call_then_yield(VALUE self, VALUE obj) {
rb_block_call(obj, rb_intern("each"), 0, 0, iter_yield, obj);
return Qnil;
@@ -283,6 +308,9 @@ void Init_array_spec(void) {
rb_define_method(cls, "rb_ary_plus", array_spec_rb_ary_plus, 2);
rb_define_method(cls, "rb_ary_unshift", array_spec_rb_ary_unshift, 2);
rb_define_method(cls, "rb_assoc_new", array_spec_rb_assoc_new, 2);
+ rb_define_method(cls, "rb_iterate", array_spec_rb_iterate, 1);
+ rb_define_method(cls, "rb_iterate_each_pair", array_spec_rb_iterate_each_pair, 1);
+ rb_define_method(cls, "rb_iterate_then_yield", array_spec_rb_iterate_then_yield, 1);
rb_define_method(cls, "rb_block_call", array_spec_rb_block_call, 1);
rb_define_method(cls, "rb_block_call_each_pair", array_spec_rb_block_call_each_pair, 1);
rb_define_method(cls, "rb_block_call_then_yield", array_spec_rb_block_call_then_yield, 1);
diff --git a/spec/ruby/optional/capi/ext/class_spec.c b/spec/ruby/optional/capi/ext/class_spec.c
index 589025f677..f376534924 100644
--- a/spec/ruby/optional/capi/ext/class_spec.c
+++ b/spec/ruby/optional/capi/ext/class_spec.c
@@ -72,7 +72,7 @@ static VALUE class_spec_rb_class_new_instance_kw(VALUE self, VALUE args, VALUE k
#endif
static VALUE class_spec_rb_class_real(VALUE self, VALUE object) {
- if(rb_type_p(object, T_FIXNUM)) {
+ if (rb_type_p(object, T_FIXNUM)) {
return INT2FIX(rb_class_real(FIX2INT(object)));
} else {
return rb_class_real(CLASS_OF(object));
@@ -116,19 +116,19 @@ VALUE class_spec_define_attr(VALUE self, VALUE klass, VALUE sym, VALUE read, VAL
}
static VALUE class_spec_rb_define_class(VALUE self, VALUE name, VALUE super) {
- if(NIL_P(super)) super = 0;
+ if (NIL_P(super)) super = 0;
return rb_define_class(RSTRING_PTR(name), super);
}
static VALUE class_spec_rb_define_class_under(VALUE self, VALUE outer,
VALUE name, VALUE super) {
- if(NIL_P(super)) super = 0;
+ if (NIL_P(super)) super = 0;
return rb_define_class_under(outer, RSTRING_PTR(name), super);
}
static VALUE class_spec_rb_define_class_id_under(VALUE self, VALUE outer,
VALUE name, VALUE super) {
- if(NIL_P(super)) super = 0;
+ if (NIL_P(super)) super = 0;
return rb_define_class_id_under(outer, SYM2ID(name), super);
}
diff --git a/spec/ruby/optional/capi/ext/debug_spec.c b/spec/ruby/optional/capi/ext/debug_spec.c
index 344dfc33fa..9131eda78b 100644
--- a/spec/ruby/optional/capi/ext/debug_spec.c
+++ b/spec/ruby/optional/capi/ext/debug_spec.c
@@ -45,7 +45,7 @@ static VALUE rb_debug_inspector_frame_iseq_get_callback(const rb_debug_inspector
return rb_debug_inspector_frame_iseq_get(dc, NUM2LONG((VALUE) ptr));
}
-static VALUE debug_spec_callback_data(VALUE self){
+static VALUE debug_spec_callback_data(VALUE self) {
return callback_data;
}
diff --git a/spec/ruby/optional/capi/ext/encoding_spec.c b/spec/ruby/optional/capi/ext/encoding_spec.c
index a0136530f2..aa8662cfbd 100644
--- a/spec/ruby/optional/capi/ext/encoding_spec.c
+++ b/spec/ruby/optional/capi/ext/encoding_spec.c
@@ -12,7 +12,7 @@ static VALUE encoding_spec_MBCLEN_CHARFOUND_P(VALUE self, VALUE obj) {
}
static VALUE encoding_spec_ENC_CODERANGE_ASCIIONLY(VALUE self, VALUE obj) {
- if(ENC_CODERANGE_ASCIIONLY(obj)) {
+ if (ENC_CODERANGE_ASCIIONLY(obj)) {
return Qtrue;
} else {
return Qfalse;
@@ -61,13 +61,13 @@ static VALUE encoding_spec_rb_filesystem_encindex(VALUE self) {
static VALUE encoding_spec_rb_default_internal_encoding(VALUE self) {
rb_encoding* enc = rb_default_internal_encoding();
- if(enc == 0) return Qnil;
+ if (enc == 0) return Qnil;
return rb_str_new2(enc->name);
}
static VALUE encoding_spec_rb_default_external_encoding(VALUE self) {
rb_encoding* enc = rb_default_external_encoding();
- if(enc == 0) return Qnil;
+ if (enc == 0) return Qnil;
return rb_str_new2(enc->name);
}
@@ -86,7 +86,7 @@ static VALUE encoding_spec_rb_enc_associate_index(VALUE self, VALUE obj, VALUE i
static VALUE encoding_spec_rb_enc_compatible(VALUE self, VALUE a, VALUE b) {
rb_encoding* enc = rb_enc_compatible(a, b);
- if(!enc) return INT2FIX(0);
+ if (!enc) return INT2FIX(0);
return rb_enc_from_encoding(enc);
}
@@ -271,6 +271,13 @@ static VALUE encoding_spec_rb_enc_str_asciionly_p(VALUE self, VALUE str) {
}
}
+static VALUE encoding_spec_rb_enc_raise(VALUE self, VALUE encoding, VALUE exception_class, VALUE format) {
+ rb_encoding *e = rb_to_encoding(encoding);
+ const char *f = RSTRING_PTR(format);
+
+ rb_enc_raise(e, exception_class, "%s", f);
+}
+
static VALUE encoding_spec_rb_uv_to_utf8(VALUE self, VALUE buf, VALUE num) {
int len = rb_uv_to_utf8(RSTRING_PTR(buf), NUM2INT(num));
RB_ENC_CODERANGE_CLEAR(buf);
@@ -307,6 +314,16 @@ static VALUE encoding_spec_rb_enc_strlen(VALUE self, VALUE str, VALUE length, VA
return LONG2FIX(rb_enc_strlen(p, e, rb_to_encoding(encoding)));
}
+static VALUE encoding_spec_rb_enc_left_char_head(VALUE self, VALUE str, VALUE offset) {
+ char *ptr = RSTRING_PTR(str);
+ char *result = rb_enc_left_char_head(ptr, ptr + NUM2INT(offset), RSTRING_END(str), rb_enc_get(str));
+ return LONG2NUM(result - ptr);
+}
+
+static VALUE encoding_spec_rb_define_dummy_encoding(VALUE self, VALUE name) {
+ return INT2NUM(rb_define_dummy_encoding(RSTRING_PTR(name)));
+}
+
void Init_encoding_spec(void) {
VALUE cls;
native_rb_encoding_pointer = (rb_encoding**) malloc(sizeof(rb_encoding*));
@@ -362,8 +379,11 @@ void Init_encoding_spec(void) {
rb_define_method(cls, "rb_enc_nth", encoding_spec_rb_enc_nth, 2);
rb_define_method(cls, "rb_enc_codepoint_len", encoding_spec_rb_enc_codepoint_len, 1);
rb_define_method(cls, "rb_enc_str_asciionly_p", encoding_spec_rb_enc_str_asciionly_p, 1);
+ rb_define_method(cls, "rb_enc_raise", encoding_spec_rb_enc_raise, 3);
rb_define_method(cls, "rb_uv_to_utf8", encoding_spec_rb_uv_to_utf8, 2);
rb_define_method(cls, "ONIGENC_MBC_CASE_FOLD", encoding_spec_ONIGENC_MBC_CASE_FOLD, 1);
+ rb_define_method(cls, "rb_enc_left_char_head", encoding_spec_rb_enc_left_char_head, 2);
+ rb_define_method(cls, "rb_define_dummy_encoding", encoding_spec_rb_define_dummy_encoding, 1);
}
#ifdef __cplusplus
diff --git a/spec/ruby/optional/capi/ext/exception_spec.c b/spec/ruby/optional/capi/ext/exception_spec.c
index e1114aabb8..0e8347ab0d 100644
--- a/spec/ruby/optional/capi/ext/exception_spec.c
+++ b/spec/ruby/optional/capi/ext/exception_spec.c
@@ -27,7 +27,7 @@ VALUE exception_spec_rb_exc_new3(VALUE self, VALUE str) {
}
VALUE exception_spec_rb_exc_raise(VALUE self, VALUE exc) {
- if (self != Qundef) rb_exc_raise(exc);
+ if (self != Qundef) rb_exc_raise(exc);
return Qnil;
}
@@ -36,6 +36,21 @@ VALUE exception_spec_rb_set_errinfo(VALUE self, VALUE exc) {
return Qnil;
}
+VALUE exception_spec_rb_syserr_new(VALUE self, VALUE num, VALUE msg) {
+ int n = NUM2INT(num);
+ char *cstr = NULL;
+
+ if (msg != Qnil) {
+ cstr = StringValuePtr(msg);
+ }
+
+ return rb_syserr_new(n, cstr);
+}
+
+VALUE exception_spec_rb_syserr_new_str(VALUE self, VALUE num, VALUE msg) {
+ int n = NUM2INT(num);
+ return rb_syserr_new_str(n, msg);
+}
VALUE exception_spec_rb_make_exception(VALUE self, VALUE ary) {
int argc = RARRAY_LENINT(ary);
@@ -51,6 +66,8 @@ void Init_exception_spec(void) {
rb_define_method(cls, "rb_exc_new3", exception_spec_rb_exc_new3, 1);
rb_define_method(cls, "rb_exc_raise", exception_spec_rb_exc_raise, 1);
rb_define_method(cls, "rb_set_errinfo", exception_spec_rb_set_errinfo, 1);
+ rb_define_method(cls, "rb_syserr_new", exception_spec_rb_syserr_new, 2);
+ rb_define_method(cls, "rb_syserr_new_str", exception_spec_rb_syserr_new_str, 2);
rb_define_method(cls, "rb_make_exception", exception_spec_rb_make_exception, 1);
}
diff --git a/spec/ruby/optional/capi/ext/gc_spec.c b/spec/ruby/optional/capi/ext/gc_spec.c
index b323c2456d..1392bc6ee6 100644
--- a/spec/ruby/optional/capi/ext/gc_spec.c
+++ b/spec/ruby/optional/capi/ext/gc_spec.c
@@ -8,7 +8,12 @@ extern "C" {
VALUE registered_tagged_value;
VALUE registered_reference_value;
VALUE registered_before_rb_gc_register_address;
-VALUE registered_before_rb_global_variable;
+VALUE registered_before_rb_global_variable_string;
+VALUE registered_before_rb_global_variable_bignum;
+VALUE registered_before_rb_global_variable_float;
+VALUE registered_after_rb_global_variable_string;
+VALUE registered_after_rb_global_variable_bignum;
+VALUE registered_after_rb_global_variable_float;
VALUE rb_gc_register_address_outside_init;
static VALUE registered_tagged_address(VALUE self) {
@@ -23,8 +28,28 @@ static VALUE get_registered_before_rb_gc_register_address(VALUE self) {
return registered_before_rb_gc_register_address;
}
-static VALUE get_registered_before_rb_global_variable(VALUE self) {
- return registered_before_rb_global_variable;
+static VALUE get_registered_before_rb_global_variable_string(VALUE self) {
+ return registered_before_rb_global_variable_string;
+}
+
+static VALUE get_registered_before_rb_global_variable_bignum(VALUE self) {
+ return registered_before_rb_global_variable_bignum;
+}
+
+static VALUE get_registered_before_rb_global_variable_float(VALUE self) {
+ return registered_before_rb_global_variable_float;
+}
+
+static VALUE get_registered_after_rb_global_variable_string(VALUE self) {
+ return registered_after_rb_global_variable_string;
+}
+
+static VALUE get_registered_after_rb_global_variable_bignum(VALUE self) {
+ return registered_after_rb_global_variable_bignum;
+}
+
+static VALUE get_registered_after_rb_global_variable_float(VALUE self) {
+ return registered_after_rb_global_variable_float;
}
static VALUE gc_spec_rb_gc_register_address(VALUE self) {
@@ -51,7 +76,7 @@ static VALUE gc_spec_rb_gc(VALUE self) {
return Qnil;
}
-static VALUE gc_spec_rb_gc_latest_gc_info(VALUE self, VALUE hash_or_key){
+static VALUE gc_spec_rb_gc_latest_gc_info(VALUE self, VALUE hash_or_key) {
return rb_gc_latest_gc_info(hash_or_key);
}
@@ -71,17 +96,34 @@ void Init_gc_spec(void) {
rb_gc_register_address(&registered_tagged_value);
rb_gc_register_address(&registered_reference_value);
rb_gc_register_address(&registered_before_rb_gc_register_address);
- rb_global_variable(&registered_before_rb_global_variable);
+ rb_global_variable(&registered_before_rb_global_variable_string);
+ rb_global_variable(&registered_before_rb_global_variable_bignum);
+ rb_global_variable(&registered_before_rb_global_variable_float);
registered_tagged_value = INT2NUM(10);
registered_reference_value = rb_str_new2("Globally registered data");
registered_before_rb_gc_register_address = rb_str_new_cstr("registered before rb_gc_register_address()");
- registered_before_rb_global_variable = rb_str_new_cstr("registered before rb_global_variable()");
+
+ registered_before_rb_global_variable_string = rb_str_new_cstr("registered before rb_global_variable()");
+ registered_before_rb_global_variable_bignum = LL2NUM(INT64_MAX);
+ registered_before_rb_global_variable_float = DBL2NUM(3.14);
+
+ registered_after_rb_global_variable_string = rb_str_new_cstr("registered after rb_global_variable()");
+ rb_global_variable(&registered_after_rb_global_variable_string);
+ registered_after_rb_global_variable_bignum = LL2NUM(INT64_MAX);
+ rb_global_variable(&registered_after_rb_global_variable_bignum);
+ registered_after_rb_global_variable_float = DBL2NUM(6.28);
+ rb_global_variable(&registered_after_rb_global_variable_float);
rb_define_method(cls, "registered_tagged_address", registered_tagged_address, 0);
rb_define_method(cls, "registered_reference_address", registered_reference_address, 0);
rb_define_method(cls, "registered_before_rb_gc_register_address", get_registered_before_rb_gc_register_address, 0);
- rb_define_method(cls, "registered_before_rb_global_variable", get_registered_before_rb_global_variable, 0);
+ rb_define_method(cls, "registered_before_rb_global_variable_string", get_registered_before_rb_global_variable_string, 0);
+ rb_define_method(cls, "registered_before_rb_global_variable_bignum", get_registered_before_rb_global_variable_bignum, 0);
+ rb_define_method(cls, "registered_before_rb_global_variable_float", get_registered_before_rb_global_variable_float, 0);
+ rb_define_method(cls, "registered_after_rb_global_variable_string", get_registered_after_rb_global_variable_string, 0);
+ rb_define_method(cls, "registered_after_rb_global_variable_bignum", get_registered_after_rb_global_variable_bignum, 0);
+ rb_define_method(cls, "registered_after_rb_global_variable_float", get_registered_after_rb_global_variable_float, 0);
rb_define_method(cls, "rb_gc_register_address", gc_spec_rb_gc_register_address, 0);
rb_define_method(cls, "rb_gc_unregister_address", gc_spec_rb_gc_unregister_address, 0);
rb_define_method(cls, "rb_gc_enable", gc_spec_rb_gc_enable, 0);
diff --git a/spec/ruby/optional/capi/ext/hash_spec.c b/spec/ruby/optional/capi/ext/hash_spec.c
index 7f38708915..69ef02d5da 100644
--- a/spec/ruby/optional/capi/ext/hash_spec.c
+++ b/spec/ruby/optional/capi/ext/hash_spec.c
@@ -105,6 +105,12 @@ VALUE hash_spec_rb_hash_new(VALUE self) {
return rb_hash_new();
}
+#ifdef RUBY_VERSION_IS_3_2
+VALUE hash_spec_rb_hash_new_capa(VALUE self, VALUE capacity) {
+ return rb_hash_new_capa(NUM2LONG(capacity));
+}
+#endif
+
VALUE rb_ident_hash_new(void); /* internal.h, used in ripper */
VALUE hash_spec_rb_ident_hash_new(VALUE self) {
@@ -149,6 +155,9 @@ void Init_hash_spec(void) {
rb_define_method(cls, "rb_hash_lookup2", hash_spec_rb_hash_lookup2, 3);
rb_define_method(cls, "rb_hash_lookup2_default_undef", hash_spec_rb_hash_lookup2_default_undef, 2);
rb_define_method(cls, "rb_hash_new", hash_spec_rb_hash_new, 0);
+#ifdef RUBY_VERSION_IS_3_2
+ rb_define_method(cls, "rb_hash_new_capa", hash_spec_rb_hash_new_capa, 1);
+#endif
rb_define_method(cls, "rb_ident_hash_new", hash_spec_rb_ident_hash_new, 0);
rb_define_method(cls, "rb_hash_size", hash_spec_rb_hash_size, 1);
rb_define_method(cls, "rb_hash_set_ifnone", hash_spec_rb_hash_set_ifnone, 2);
diff --git a/spec/ruby/optional/capi/ext/integer_spec.c b/spec/ruby/optional/capi/ext/integer_spec.c
index 16cd95f111..792fc0652a 100644
--- a/spec/ruby/optional/capi/ext/integer_spec.c
+++ b/spec/ruby/optional/capi/ext/integer_spec.c
@@ -6,8 +6,7 @@ extern "C" {
#endif
static VALUE integer_spec_rb_integer_pack(VALUE self, VALUE value,
- VALUE words, VALUE numwords, VALUE wordsize, VALUE nails, VALUE flags)
-{
+ VALUE words, VALUE numwords, VALUE wordsize, VALUE nails, VALUE flags) {
int result = rb_integer_pack(value, (void*)RSTRING_PTR(words), FIX2INT(numwords),
FIX2INT(wordsize), FIX2INT(nails), FIX2INT(flags));
return INT2FIX(result);
@@ -15,7 +14,7 @@ static VALUE integer_spec_rb_integer_pack(VALUE self, VALUE value,
RUBY_EXTERN VALUE rb_int_positive_pow(long x, unsigned long y); /* internal.h, used in ripper */
-static VALUE integer_spec_rb_int_positive_pow(VALUE self, VALUE a, VALUE b){
+static VALUE integer_spec_rb_int_positive_pow(VALUE self, VALUE a, VALUE b) {
return rb_int_positive_pow(FIX2INT(a), FIX2INT(b));
}
diff --git a/spec/ruby/optional/capi/ext/io_spec.c b/spec/ruby/optional/capi/ext/io_spec.c
index 1c554a1a31..bcd3940e34 100644
--- a/spec/ruby/optional/capi/ext/io_spec.c
+++ b/spec/ruby/optional/capi/ext/io_spec.c
@@ -134,7 +134,7 @@ VALUE io_spec_rb_io_wait_readable(VALUE self, VALUE io, VALUE read_p) {
rb_sys_fail("set_non_blocking failed");
#ifndef SET_NON_BLOCKING_FAILS_ALWAYS
- if(RTEST(read_p)) {
+ if (RTEST(read_p)) {
if (read(fd, buf, RB_IO_WAIT_READABLE_BUF) != -1) {
return Qnil;
}
@@ -145,7 +145,7 @@ VALUE io_spec_rb_io_wait_readable(VALUE self, VALUE io, VALUE read_p) {
ret = rb_io_wait_readable(fd);
- if(RTEST(read_p)) {
+ if (RTEST(read_p)) {
ssize_t r = read(fd, buf, RB_IO_WAIT_READABLE_BUF);
if (r != RB_IO_WAIT_READABLE_BUF) {
perror("read");
@@ -185,7 +185,7 @@ VALUE io_spec_rb_io_maybe_wait_readable(VALUE self, VALUE error, VALUE io, VALUE
rb_sys_fail("set_non_blocking failed");
#ifndef SET_NON_BLOCKING_FAILS_ALWAYS
- if(RTEST(read_p)) {
+ if (RTEST(read_p)) {
if (read(fd, buf, RB_IO_WAIT_READABLE_BUF) != -1) {
return Qnil;
}
@@ -197,7 +197,7 @@ VALUE io_spec_rb_io_maybe_wait_readable(VALUE self, VALUE error, VALUE io, VALUE
// main part
ret = rb_io_maybe_wait_readable(NUM2INT(error), io, timeout);
- if(RTEST(read_p)) {
+ if (RTEST(read_p)) {
ssize_t r = read(fd, buf, RB_IO_WAIT_READABLE_BUF);
if (r != RB_IO_WAIT_READABLE_BUF) {
perror("read");
@@ -339,6 +339,16 @@ VALUE io_spec_mode_sync_flag(VALUE self, VALUE io) {
}
}
+#if defined(RUBY_VERSION_IS_3_3) || defined(TRUFFLERUBY)
+static VALUE io_spec_rb_io_mode(VALUE self, VALUE io) {
+ return INT2FIX(rb_io_mode(io));
+}
+
+static VALUE io_spec_rb_io_path(VALUE self, VALUE io) {
+ return rb_io_path(io);
+}
+#endif
+
void Init_io_spec(void) {
VALUE cls = rb_define_class("CApiIOSpecs", rb_cObject);
rb_define_method(cls, "GetOpenFile_fd", io_spec_GetOpenFile_fd, 1);
@@ -372,6 +382,10 @@ void Init_io_spec(void) {
rb_define_method(cls, "rb_cloexec_open", io_spec_rb_cloexec_open, 3);
rb_define_method(cls, "errno=", io_spec_errno_set, 1);
rb_define_method(cls, "rb_io_mode_sync_flag", io_spec_mode_sync_flag, 1);
+#if defined(RUBY_VERSION_IS_3_3) || defined(TRUFFLERUBY)
+ rb_define_method(cls, "rb_io_mode", io_spec_rb_io_mode, 1);
+ rb_define_method(cls, "rb_io_path", io_spec_rb_io_path, 1);
+#endif
}
#ifdef __cplusplus
diff --git a/spec/ruby/optional/capi/ext/kernel_spec.c b/spec/ruby/optional/capi/ext/kernel_spec.c
index e194ba8fde..1761599081 100644
--- a/spec/ruby/optional/capi/ext/kernel_spec.c
+++ b/spec/ruby/optional/capi/ext/kernel_spec.c
@@ -220,7 +220,7 @@ static VALUE kernel_spec_rb_eval_string_protect(VALUE self, VALUE str, VALUE ary
VALUE kernel_spec_rb_sys_fail(VALUE self, VALUE msg) {
errno = 1;
- if(msg == Qnil) {
+ if (msg == Qnil) {
rb_sys_fail(0);
} else if (self != Qundef) {
rb_sys_fail(StringValuePtr(msg));
@@ -229,7 +229,7 @@ VALUE kernel_spec_rb_sys_fail(VALUE self, VALUE msg) {
}
VALUE kernel_spec_rb_syserr_fail(VALUE self, VALUE err, VALUE msg) {
- if(msg == Qnil) {
+ if (msg == Qnil) {
rb_syserr_fail(NUM2INT(err), NULL);
} else if (self != Qundef) {
rb_syserr_fail(NUM2INT(err), StringValuePtr(msg));
@@ -292,9 +292,9 @@ static VALUE kernel_spec_rb_yield_values2(VALUE self, VALUE ary) {
}
static VALUE do_rec(VALUE obj, VALUE arg, int is_rec) {
- if(is_rec) {
+ if (is_rec) {
return obj;
- } else if(arg == Qtrue) {
+ } else if (arg == Qtrue) {
return rb_exec_recursive(do_rec, obj, Qnil);
} else {
return Qnil;
@@ -340,8 +340,12 @@ static VALUE kernel_spec_rb_funcallv_public(VALUE self, VALUE obj, VALUE method)
return rb_funcallv_public(obj, SYM2ID(method), 0, NULL);
}
-static VALUE kernel_spec_rb_funcall_with_block(VALUE self, VALUE obj, VALUE method, VALUE block) {
- return rb_funcall_with_block(obj, SYM2ID(method), 0, NULL, block);
+static VALUE kernel_spec_rb_funcall_with_block(VALUE self, VALUE obj, VALUE method, VALUE args, VALUE block) {
+ return rb_funcall_with_block(obj, SYM2ID(method), RARRAY_LENINT(args), RARRAY_PTR(args), block);
+}
+
+static VALUE kernel_spec_rb_funcall_with_block_kw(VALUE self, VALUE obj, VALUE method, VALUE args, VALUE block) {
+ return rb_funcall_with_block_kw(obj, SYM2ID(method), RARRAY_LENINT(args), RARRAY_PTR(args), block, RB_PASS_KEYWORDS);
}
static VALUE kernel_spec_rb_funcall_many_args(VALUE self, VALUE obj, VALUE method) {
@@ -351,6 +355,15 @@ static VALUE kernel_spec_rb_funcall_many_args(VALUE self, VALUE obj, VALUE metho
INT2FIX(5), INT2FIX(4), INT2FIX(3), INT2FIX(2), INT2FIX(1));
}
+static VALUE kernel_spec_rb_check_funcall(VALUE self, VALUE receiver, VALUE method, VALUE args) {
+ VALUE ret = rb_check_funcall(receiver, SYM2ID(method), RARRAY_LENINT(args), RARRAY_PTR(args));
+ if (ret == Qundef) {
+ return ID2SYM(rb_intern("Qundef"));
+ } else {
+ return ret;
+ }
+}
+
void Init_kernel_spec(void) {
VALUE cls = rb_define_class("CApiKernelSpecs", rb_cObject);
rb_define_method(cls, "rb_block_given_p", kernel_spec_rb_block_given_p, 0);
@@ -397,7 +410,9 @@ void Init_kernel_spec(void) {
#endif
rb_define_method(cls, "rb_funcallv_public", kernel_spec_rb_funcallv_public, 2);
rb_define_method(cls, "rb_funcall_many_args", kernel_spec_rb_funcall_many_args, 2);
- rb_define_method(cls, "rb_funcall_with_block", kernel_spec_rb_funcall_with_block, 3);
+ rb_define_method(cls, "rb_funcall_with_block", kernel_spec_rb_funcall_with_block, 4);
+ rb_define_method(cls, "rb_funcall_with_block_kw", kernel_spec_rb_funcall_with_block_kw, 4);
+ rb_define_method(cls, "rb_check_funcall", kernel_spec_rb_check_funcall, 3);
}
#ifdef __cplusplus
diff --git a/spec/ruby/optional/capi/ext/object_spec.c b/spec/ruby/optional/capi/ext/object_spec.c
index 30ac44cf1f..8aa98cc5ce 100644
--- a/spec/ruby/optional/capi/ext/object_spec.c
+++ b/spec/ruby/optional/capi/ext/object_spec.c
@@ -154,30 +154,12 @@ static VALUE object_specs_rb_obj_method(VALUE self, VALUE obj, VALUE method) {
return rb_obj_method(obj, method);
}
-#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#elif defined(__clang__) && defined(__has_warning)
-# if __has_warning("-Wdeprecated-declarations")
-# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wdeprecated-declarations"
-# endif
-#endif
-
#ifndef RUBY_VERSION_IS_3_2
static VALUE object_spec_rb_obj_taint(VALUE self, VALUE obj) {
return rb_obj_taint(obj);
}
#endif
-#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
-# pragma GCC diagnostic pop
-#elif defined(__clang__) && defined(__has_warning)
-# if __has_warning("-Wdeprecated-declarations")
-# pragma clang diagnostic pop
-# endif
-#endif
-
static VALUE so_require(VALUE self) {
rb_require("fixtures/foo");
return Qnil;
@@ -197,11 +179,7 @@ static VALUE object_spec_rb_method_boundp(VALUE self, VALUE obj, VALUE method, V
}
static VALUE object_spec_rb_special_const_p(VALUE self, VALUE value) {
- if (rb_special_const_p(value)) {
- return Qtrue;
- } else {
- return Qfalse;
- }
+ return rb_special_const_p(value);
}
static VALUE so_to_id(VALUE self, VALUE obj) {
@@ -218,126 +196,126 @@ static VALUE so_check_type(VALUE self, VALUE obj, VALUE other) {
}
static VALUE so_is_type_nil(VALUE self, VALUE obj) {
- if(TYPE(obj) == T_NIL) {
+ if (TYPE(obj) == T_NIL) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_type_object(VALUE self, VALUE obj) {
- if(TYPE(obj) == T_OBJECT) {
+ if (TYPE(obj) == T_OBJECT) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_type_array(VALUE self, VALUE obj) {
- if(TYPE(obj) == T_ARRAY) {
+ if (TYPE(obj) == T_ARRAY) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_type_module(VALUE self, VALUE obj) {
- if(TYPE(obj) == T_MODULE) {
+ if (TYPE(obj) == T_MODULE) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_type_class(VALUE self, VALUE obj) {
- if(TYPE(obj) == T_CLASS) {
+ if (TYPE(obj) == T_CLASS) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_type_data(VALUE self, VALUE obj) {
- if(TYPE(obj) == T_DATA) {
+ if (TYPE(obj) == T_DATA) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_rb_type_p_nil(VALUE self, VALUE obj) {
- if(rb_type_p(obj, T_NIL)) {
+ if (rb_type_p(obj, T_NIL)) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_rb_type_p_object(VALUE self, VALUE obj) {
- if(rb_type_p(obj, T_OBJECT)) {
+ if (rb_type_p(obj, T_OBJECT)) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_rb_type_p_array(VALUE self, VALUE obj) {
- if(rb_type_p(obj, T_ARRAY)) {
+ if (rb_type_p(obj, T_ARRAY)) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_rb_type_p_module(VALUE self, VALUE obj) {
- if(rb_type_p(obj, T_MODULE)) {
+ if (rb_type_p(obj, T_MODULE)) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_rb_type_p_class(VALUE self, VALUE obj) {
- if(rb_type_p(obj, T_CLASS)) {
+ if (rb_type_p(obj, T_CLASS)) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_rb_type_p_data(VALUE self, VALUE obj) {
- if(rb_type_p(obj, T_DATA)) {
+ if (rb_type_p(obj, T_DATA)) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_rb_type_p_file(VALUE self, VALUE obj) {
- if(rb_type_p(obj, T_FILE)) {
+ if (rb_type_p(obj, T_FILE)) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_builtin_type_object(VALUE self, VALUE obj) {
- if(BUILTIN_TYPE(obj) == T_OBJECT) {
+ if (BUILTIN_TYPE(obj) == T_OBJECT) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_builtin_type_array(VALUE self, VALUE obj) {
- if(BUILTIN_TYPE(obj) == T_ARRAY) {
+ if (BUILTIN_TYPE(obj) == T_ARRAY) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_builtin_type_module(VALUE self, VALUE obj) {
- if(BUILTIN_TYPE(obj) == T_MODULE) {
+ if (BUILTIN_TYPE(obj) == T_MODULE) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_builtin_type_class(VALUE self, VALUE obj) {
- if(BUILTIN_TYPE(obj) == T_CLASS) {
+ if (BUILTIN_TYPE(obj) == T_CLASS) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_builtin_type_data(VALUE self, VALUE obj) {
- if(BUILTIN_TYPE(obj) == T_DATA) {
+ if (BUILTIN_TYPE(obj) == T_DATA) {
return Qtrue;
}
return Qfalse;
@@ -406,16 +384,8 @@ static VALUE object_spec_rb_ivar_foreach(VALUE self, VALUE obj) {
}
static VALUE speced_allocator(VALUE klass) {
- VALUE flags = 0;
- VALUE instance;
- if (RTEST(rb_class_inherited_p(klass, rb_cString))) {
- flags = T_STRING;
- } else if (RTEST(rb_class_inherited_p(klass, rb_cArray))) {
- flags = T_ARRAY;
- } else {
- flags = T_OBJECT;
- }
- instance = rb_newobj_of(klass, flags);
+ VALUE super = rb_class_get_superclass(klass);
+ VALUE instance = rb_get_alloc_func(super)(klass);
rb_iv_set(instance, "@from_custom_allocator", Qtrue);
return instance;
}
diff --git a/spec/ruby/optional/capi/ext/proc_spec.c b/spec/ruby/optional/capi/ext/proc_spec.c
index 1137f4156b..b7cd5d6262 100644
--- a/spec/ruby/optional/capi/ext/proc_spec.c
+++ b/spec/ruby/optional/capi/ext/proc_spec.c
@@ -76,6 +76,18 @@ VALUE proc_spec_rb_proc_call(VALUE self, VALUE prc, VALUE args) {
return rb_proc_call(prc, args);
}
+VALUE proc_spec_rb_proc_call_kw(VALUE self, VALUE prc, VALUE args) {
+ return rb_proc_call_kw(prc, args, RB_PASS_KEYWORDS);
+}
+
+VALUE proc_spec_rb_proc_call_with_block(VALUE self, VALUE prc, VALUE args, VALUE block) {
+ return rb_proc_call_with_block(prc, RARRAY_LENINT(args), RARRAY_PTR(args), block);
+}
+
+static VALUE proc_spec_rb_proc_call_with_block_kw(VALUE self, VALUE prc, VALUE args, VALUE block) {
+ return rb_proc_call_with_block_kw(prc, RARRAY_LENINT(args), RARRAY_PTR(args), block, RB_PASS_KEYWORDS);
+}
+
VALUE proc_spec_rb_obj_is_proc(VALUE self, VALUE prc) {
return rb_obj_is_proc(prc);
}
@@ -123,6 +135,9 @@ void Init_proc_spec(void) {
rb_define_method(cls, "rb_proc_new_block_given_p", proc_spec_rb_proc_new_block_given_p, 0);
rb_define_method(cls, "rb_proc_arity", proc_spec_rb_proc_arity, 1);
rb_define_method(cls, "rb_proc_call", proc_spec_rb_proc_call, 2);
+ rb_define_method(cls, "rb_proc_call_kw", proc_spec_rb_proc_call_kw, 2);
+ rb_define_method(cls, "rb_proc_call_with_block", proc_spec_rb_proc_call_with_block, 3);
+ rb_define_method(cls, "rb_proc_call_with_block_kw", proc_spec_rb_proc_call_with_block_kw, 3);
rb_define_method(cls, "rb_Proc_new", proc_spec_rb_Proc_new, 1);
rb_define_method(cls, "rb_obj_is_proc", proc_spec_rb_obj_is_proc, 1);
}
diff --git a/spec/ruby/optional/capi/ext/range_spec.c b/spec/ruby/optional/capi/ext/range_spec.c
index 7a475ec695..b0cf1a8662 100644
--- a/spec/ruby/optional/capi/ext/range_spec.c
+++ b/spec/ruby/optional/capi/ext/range_spec.c
@@ -7,7 +7,7 @@ extern "C" {
VALUE range_spec_rb_range_new(int argc, VALUE* argv, VALUE self) {
int exclude_end = 0;
- if(argc == 3) {
+ if (argc == 3) {
exclude_end = RTEST(argv[2]);
}
return rb_range_new(argv[0], argv[1], exclude_end);
diff --git a/spec/ruby/optional/capi/ext/rbasic_spec.c b/spec/ruby/optional/capi/ext/rbasic_spec.c
index 9178e5f639..26be2fed6d 100644
--- a/spec/ruby/optional/capi/ext/rbasic_spec.c
+++ b/spec/ruby/optional/capi/ext/rbasic_spec.c
@@ -5,6 +5,14 @@
extern "C" {
#endif
+#ifndef RBASIC_FLAGS
+#define RBASIC_FLAGS(obj) (RBASIC(obj)->flags)
+#endif
+
+#ifndef RBASIC_SET_FLAGS
+#define RBASIC_SET_FLAGS(obj, flags_to_set) (RBASIC(obj)->flags = flags_to_set)
+#endif
+
#ifndef FL_SHAREABLE
static const VALUE VISIBLE_BITS = FL_TAINT | FL_FREEZE;
static const VALUE DATA_VISIBLE_BITS = FL_TAINT | FL_FREEZE | ~(FL_USER0 - 1);
@@ -34,47 +42,53 @@ VALUE rbasic_spec_freeze_flag(VALUE self) {
return VALUE2NUM(RUBY_FL_FREEZE);
}
- static VALUE spec_get_flags(const struct RBasic *b, VALUE visible_bits) {
- VALUE flags = b->flags & visible_bits;
+static VALUE spec_get_flags(VALUE obj, VALUE visible_bits) {
+ VALUE flags = RB_FL_TEST(obj, visible_bits);
return VALUE2NUM(flags);
}
-static VALUE spec_set_flags(struct RBasic *b, VALUE flags, VALUE visible_bits) {
+static VALUE spec_set_flags(VALUE obj, VALUE flags, VALUE visible_bits) {
flags &= visible_bits;
- b->flags = (b->flags & ~visible_bits) | flags;
+
+ // Could also be done like:
+ // RB_FL_UNSET(obj, visible_bits);
+ // RB_FL_SET(obj, flags);
+ // But that seems rather indirect
+ RBASIC_SET_FLAGS(obj, (RBASIC_FLAGS(obj) & ~visible_bits) | flags);
+
return VALUE2NUM(flags);
}
-VALUE rbasic_spec_get_flags(VALUE self, VALUE val) {
- return spec_get_flags(RBASIC(val), VISIBLE_BITS);
+static VALUE rbasic_spec_get_flags(VALUE self, VALUE obj) {
+ return spec_get_flags(obj, VISIBLE_BITS);
}
-VALUE rbasic_spec_set_flags(VALUE self, VALUE val, VALUE flags) {
- return spec_set_flags(RBASIC(val), NUM2VALUE(flags), VISIBLE_BITS);
+static VALUE rbasic_spec_set_flags(VALUE self, VALUE obj, VALUE flags) {
+ return spec_set_flags(obj, NUM2VALUE(flags), VISIBLE_BITS);
}
-VALUE rbasic_spec_copy_flags(VALUE self, VALUE to, VALUE from) {
- return spec_set_flags(RBASIC(to), RBASIC(from)->flags, VISIBLE_BITS);
+static VALUE rbasic_spec_copy_flags(VALUE self, VALUE to, VALUE from) {
+ return spec_set_flags(to, RBASIC_FLAGS(from), VISIBLE_BITS);
}
-VALUE rbasic_spec_get_klass(VALUE self, VALUE val) {
- return RBASIC(val)->klass;
+static VALUE rbasic_spec_get_klass(VALUE self, VALUE obj) {
+ return RBASIC_CLASS(obj);
}
-VALUE rbasic_rdata_spec_get_flags(VALUE self, VALUE structure) {
- return spec_get_flags(&RDATA(structure)->basic, DATA_VISIBLE_BITS);
+static VALUE rbasic_rdata_spec_get_flags(VALUE self, VALUE structure) {
+ return spec_get_flags(structure, DATA_VISIBLE_BITS);
}
-VALUE rbasic_rdata_spec_set_flags(VALUE self, VALUE structure, VALUE flags) {
- return spec_set_flags(&RDATA(structure)->basic, NUM2VALUE(flags), DATA_VISIBLE_BITS);
+static VALUE rbasic_rdata_spec_set_flags(VALUE self, VALUE structure, VALUE flags) {
+ return spec_set_flags(structure, NUM2VALUE(flags), DATA_VISIBLE_BITS);
}
-VALUE rbasic_rdata_spec_copy_flags(VALUE self, VALUE to, VALUE from) {
- return spec_set_flags(&RDATA(to)->basic, RDATA(from)->basic.flags, DATA_VISIBLE_BITS);
+static VALUE rbasic_rdata_spec_copy_flags(VALUE self, VALUE to, VALUE from) {
+ return spec_set_flags(to, RBASIC_FLAGS(from), DATA_VISIBLE_BITS);
}
-VALUE rbasic_rdata_spec_get_klass(VALUE self, VALUE structure) {
- return RDATA(structure)->basic.klass;
+static VALUE rbasic_rdata_spec_get_klass(VALUE self, VALUE structure) {
+ return RBASIC_CLASS(structure);
}
void Init_rbasic_spec(void) {
diff --git a/spec/ruby/optional/capi/ext/rubyspec.h b/spec/ruby/optional/capi/ext/rubyspec.h
index 80deca24c6..09135774af 100644
--- a/spec/ruby/optional/capi/ext/rubyspec.h
+++ b/spec/ruby/optional/capi/ext/rubyspec.h
@@ -11,6 +11,29 @@
# include <version.h>
#endif
+/* copied from ext/-test-/cxxanyargs/cxxanyargs.cpp */
+#if 0 /* Ignore deprecation warnings */
+
+#elif defined(_MSC_VER)
+#pragma warning(disable : 4996)
+
+#elif defined(__INTEL_COMPILER)
+#pragma warning(disable : 1786)
+
+#elif defined(__clang__)
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+#elif defined(__GNUC__)
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
+#elif defined(__SUNPRO_CC)
+#pragma error_messages (off,symdeprecated)
+
+#else
+// :FIXME: improve here for your compiler.
+
+#endif
+
#ifndef RUBY_VERSION_MAJOR
#define RUBY_VERSION_MAJOR RUBY_API_VERSION_MAJOR
#define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR
diff --git a/spec/ruby/optional/capi/ext/string_spec.c b/spec/ruby/optional/capi/ext/string_spec.c
index 9cbb50484d..cec3f65f45 100644
--- a/spec/ruby/optional/capi/ext/string_spec.c
+++ b/spec/ruby/optional/capi/ext/string_spec.c
@@ -51,18 +51,12 @@ VALUE string_spec_rb_str_set_len_RSTRING_LEN(VALUE self, VALUE str, VALUE len) {
return INT2FIX(RSTRING_LEN(str));
}
-VALUE rb_fstring(VALUE str); /* internal.h, used in ripper */
-
-VALUE string_spec_rb_str_fstring(VALUE self, VALUE str) {
- return rb_fstring(str);
-}
-
VALUE string_spec_rb_str_buf_new(VALUE self, VALUE len, VALUE str) {
VALUE buf;
buf = rb_str_buf_new(NUM2LONG(len));
- if(RTEST(str)) {
+ if (RTEST(str)) {
snprintf(RSTRING_PTR(buf), NUM2LONG(len), "%s", RSTRING_PTR(str));
}
@@ -129,7 +123,7 @@ VALUE string_spec_rb_str_conv_enc(VALUE self, VALUE str, VALUE from, VALUE to) {
from_enc = rb_to_encoding(from);
- if(NIL_P(to)) {
+ if (NIL_P(to)) {
to_enc = 0;
} else {
to_enc = rb_to_encoding(to);
@@ -139,14 +133,13 @@ VALUE string_spec_rb_str_conv_enc(VALUE self, VALUE str, VALUE from, VALUE to) {
}
VALUE string_spec_rb_str_conv_enc_opts(VALUE self, VALUE str, VALUE from, VALUE to,
- VALUE ecflags, VALUE ecopts)
-{
+ VALUE ecflags, VALUE ecopts) {
rb_encoding* from_enc;
rb_encoding* to_enc;
from_enc = rb_to_encoding(from);
- if(NIL_P(to)) {
+ if (NIL_P(to)) {
to_enc = 0;
} else {
to_enc = rb_to_encoding(to);
@@ -200,7 +193,7 @@ VALUE string_spec_rb_str_new_offset(VALUE self, VALUE str, VALUE offset, VALUE l
}
VALUE string_spec_rb_str_new2(VALUE self, VALUE str) {
- if(NIL_P(str)) {
+ if (NIL_P(str)) {
return rb_str_new2("");
} else {
return rb_str_new2(RSTRING_PTR(str));
@@ -216,7 +209,7 @@ VALUE string_spec_rb_str_export_to_enc(VALUE self, VALUE str, VALUE enc) {
}
VALUE string_spec_rb_str_new_cstr(VALUE self, VALUE str) {
- if(NIL_P(str)) {
+ if (NIL_P(str)) {
return rb_str_new_cstr("");
} else {
return rb_str_new_cstr(RSTRING_PTR(str));
@@ -255,16 +248,6 @@ VALUE string_spec_rb_str_new5(VALUE self, VALUE str, VALUE ptr, VALUE len) {
return rb_str_new5(str, RSTRING_PTR(ptr), FIX2INT(len));
}
-#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#elif defined(__clang__) && defined(__has_warning)
-# if __has_warning("-Wdeprecated-declarations")
-# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wdeprecated-declarations"
-# endif
-#endif
-
#ifndef RUBY_VERSION_IS_3_2
VALUE string_spec_rb_tainted_str_new(VALUE self, VALUE str, VALUE len) {
return rb_tainted_str_new(RSTRING_PTR(str), FIX2INT(len));
@@ -275,14 +258,6 @@ VALUE string_spec_rb_tainted_str_new2(VALUE self, VALUE str) {
}
#endif
-#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
-# pragma GCC diagnostic pop
-#elif defined(__clang__) && defined(__has_warning)
-# if __has_warning("-Wdeprecated-declarations")
-# pragma clang diagnostic pop
-# endif
-#endif
-
VALUE string_spec_rb_str_plus(VALUE self, VALUE str1, VALUE str2) {
return rb_str_plus(str1, str2);
}
@@ -390,7 +365,7 @@ VALUE string_spec_RSTRING_PTR_set(VALUE self, VALUE str, VALUE i, VALUE chr) {
VALUE string_spec_RSTRING_PTR_after_funcall(VALUE self, VALUE str, VALUE cb) {
/* Silence gcc 4.3.2 warning about computed value not used */
- if(RSTRING_PTR(str)) { /* force it out */
+ if (RSTRING_PTR(str)) { /* force it out */
rb_funcall(cb, rb_intern("call"), 1, str);
}
@@ -573,7 +548,7 @@ static VALUE string_spec_rb_utf8_str_new_cstr(VALUE self) {
}
PRINTF_ARGS(static VALUE call_rb_str_vcatf(VALUE mesg, const char *fmt, ...), 2, 3);
-static VALUE call_rb_str_vcatf(VALUE mesg, const char *fmt, ...){
+static VALUE call_rb_str_vcatf(VALUE mesg, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
VALUE result = rb_str_vcatf(mesg, fmt, ap);
@@ -597,11 +572,19 @@ static VALUE string_spec_rb_str_unlocktmp(VALUE self, VALUE str) {
return rb_str_unlocktmp(str);
}
+static VALUE string_spec_rb_enc_interned_str_cstr(VALUE self, VALUE str, VALUE enc) {
+ rb_encoding *e = NIL_P(enc) ? 0 : rb_to_encoding(enc);
+ return rb_enc_interned_str_cstr(RSTRING_PTR(str), e);
+}
+
+static VALUE string_spec_rb_str_to_interned_str(VALUE self, VALUE str) {
+ return rb_str_to_interned_str(str);
+}
+
void Init_string_spec(void) {
VALUE cls = rb_define_class("CApiStringSpecs", rb_cObject);
rb_define_method(cls, "rb_cstr2inum", string_spec_rb_cstr2inum, 2);
rb_define_method(cls, "rb_cstr_to_inum", string_spec_rb_cstr_to_inum, 3);
- rb_define_method(cls, "rb_fstring", string_spec_rb_str_fstring, 1);
rb_define_method(cls, "rb_str2inum", string_spec_rb_str2inum, 2);
rb_define_method(cls, "rb_str_append", string_spec_rb_str_append, 2);
rb_define_method(cls, "rb_str_buf_new", string_spec_rb_str_buf_new, 2);
@@ -698,6 +681,8 @@ void Init_string_spec(void) {
rb_define_method(cls, "rb_str_catf", string_spec_rb_str_catf, 1);
rb_define_method(cls, "rb_str_locktmp", string_spec_rb_str_locktmp, 1);
rb_define_method(cls, "rb_str_unlocktmp", string_spec_rb_str_unlocktmp, 1);
+ rb_define_method(cls, "rb_enc_interned_str_cstr", string_spec_rb_enc_interned_str_cstr, 2);
+ rb_define_method(cls, "rb_str_to_interned_str", string_spec_rb_str_to_interned_str, 1);
}
#ifdef __cplusplus
diff --git a/spec/ruby/optional/capi/ext/struct_spec.c b/spec/ruby/optional/capi/ext/struct_spec.c
index 0393d6937d..9c45bd5672 100644
--- a/spec/ruby/optional/capi/ext/struct_spec.c
+++ b/spec/ruby/optional/capi/ext/struct_spec.c
@@ -15,13 +15,11 @@ static VALUE struct_spec_rb_struct_getmember(VALUE self, VALUE st, VALUE key) {
return rb_struct_getmember(st, SYM2ID(key));
}
-static VALUE struct_spec_rb_struct_s_members(VALUE self, VALUE klass)
-{
+static VALUE struct_spec_rb_struct_s_members(VALUE self, VALUE klass) {
return rb_ary_dup(rb_struct_s_members(klass));
}
-static VALUE struct_spec_rb_struct_members(VALUE self, VALUE st)
-{
+static VALUE struct_spec_rb_struct_members(VALUE self, VALUE st) {
return rb_ary_dup(rb_struct_members(st));
}
@@ -56,14 +54,11 @@ static VALUE struct_spec_struct_define_under(VALUE self, VALUE outer,
}
static VALUE struct_spec_rb_struct_new(VALUE self, VALUE klass,
- VALUE a, VALUE b, VALUE c)
-{
-
+ VALUE a, VALUE b, VALUE c) {
return rb_struct_new(klass, a, b, c);
}
-static VALUE struct_spec_rb_struct_size(VALUE self, VALUE st)
-{
+static VALUE struct_spec_rb_struct_size(VALUE self, VALUE st) {
return rb_struct_size(st);
}
diff --git a/spec/ruby/optional/capi/ext/thread_spec.c b/spec/ruby/optional/capi/ext/thread_spec.c
index 6307e5cc99..3511c2fbcf 100644
--- a/spec/ruby/optional/capi/ext/thread_spec.c
+++ b/spec/ruby/optional/capi/ext/thread_spec.c
@@ -26,10 +26,6 @@ static VALUE thread_spec_rb_thread_alone(VALUE self) {
return rb_thread_alone() ? Qtrue : Qfalse;
}
-#if defined(__GNUC__)
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#endif
-
/* This is unblocked by unblock_func(). */
static void* blocking_gvl_func(void* data) {
int rfd = *(int *)data;
@@ -71,7 +67,7 @@ static VALUE thread_spec_rb_thread_call_without_gvl(VALUE self) {
}
/* This is unblocked by a signal. */
-static void* blocking_gvl_func_for_udf_io(void *data) {
+static void* blocking_gvl_func_for_ubf_io(void *data) {
int rfd = (int)(size_t)data;
char dummy;
@@ -91,7 +87,7 @@ static VALUE thread_spec_rb_thread_call_without_gvl_with_ubf_io(VALUE self) {
rb_raise(rb_eRuntimeError, "could not create pipe");
}
- ret = rb_thread_call_without_gvl(blocking_gvl_func_for_udf_io,
+ ret = rb_thread_call_without_gvl(blocking_gvl_func_for_ubf_io,
(void*)(size_t)fds[0], RUBY_UBF_IO, 0);
close(fds[0]);
close(fds[1]);
diff --git a/spec/ruby/optional/capi/ext/tracepoint_spec.c b/spec/ruby/optional/capi/ext/tracepoint_spec.c
index 78c459d6cb..6666c8f85c 100644
--- a/spec/ruby/optional/capi/ext/tracepoint_spec.c
+++ b/spec/ruby/optional/capi/ext/tracepoint_spec.c
@@ -17,7 +17,7 @@ static VALUE tracepoint_spec_rb_tracepoint_new(VALUE self, VALUE data) {
return rb_tracepoint_new(Qnil, RUBY_EVENT_LINE, callback, (void*) data);
}
-static VALUE tracepoint_spec_callback_called(VALUE self){
+static VALUE tracepoint_spec_callback_called(VALUE self) {
return callback_called;
}
diff --git a/spec/ruby/optional/capi/ext/typed_data_spec.c b/spec/ruby/optional/capi/ext/typed_data_spec.c
index eca2b667cc..38889ecf5c 100644
--- a/spec/ruby/optional/capi/ext/typed_data_spec.c
+++ b/spec/ruby/optional/capi/ext/typed_data_spec.c
@@ -106,6 +106,12 @@ VALUE sws_typed_wrap_struct(VALUE self, VALUE val) {
return TypedData_Wrap_Struct(rb_cObject, &sample_typed_wrapped_struct_data_type, bar);
}
+VALUE sws_untyped_wrap_struct(VALUE self, VALUE val) {
+ int* data = (int*) malloc(sizeof(int));
+ *data = FIX2INT(val);
+ return Data_Wrap_Struct(rb_cObject, NULL, free, data);
+}
+
VALUE sws_typed_get_struct(VALUE self, VALUE obj) {
struct sample_typed_wrapped_struct* bar;
TypedData_Get_Struct(obj, struct sample_typed_wrapped_struct, &sample_typed_wrapped_struct_data_type, bar);
@@ -165,12 +171,17 @@ VALUE sws_typed_rb_check_typeddata_different_type(VALUE self, VALUE obj) {
return rb_check_typeddata(obj, &sample_typed_wrapped_struct_other_data_type) == DATA_PTR(obj) ? Qtrue : Qfalse;
}
+VALUE sws_typed_RTYPEDDATA_P(VALUE self, VALUE obj) {
+ return RTYPEDDATA_P(obj) ? Qtrue : Qfalse;
+}
+
void Init_typed_data_spec(void) {
VALUE cls = rb_define_class("CApiAllocTypedSpecs", rb_cObject);
rb_define_alloc_func(cls, sdaf_alloc_typed_func);
rb_define_method(cls, "typed_wrapped_data", sdaf_typed_get_struct, 0);
cls = rb_define_class("CApiWrappedTypedStructSpecs", rb_cObject);
rb_define_method(cls, "typed_wrap_struct", sws_typed_wrap_struct, 1);
+ rb_define_method(cls, "untyped_wrap_struct", sws_untyped_wrap_struct, 1);
rb_define_method(cls, "typed_get_struct", sws_typed_get_struct, 1);
rb_define_method(cls, "typed_get_struct_other", sws_typed_get_struct_different_type, 1);
rb_define_method(cls, "typed_get_struct_parent", sws_typed_get_struct_parent_type, 1);
@@ -181,6 +192,7 @@ void Init_typed_data_spec(void) {
rb_define_method(cls, "rb_check_typeddata_same_type", sws_typed_rb_check_typeddata_same_type, 1);
rb_define_method(cls, "rb_check_typeddata_same_type_parent", sws_typed_rb_check_typeddata_same_type_parent, 1);
rb_define_method(cls, "rb_check_typeddata_different_type", sws_typed_rb_check_typeddata_different_type, 1);
+ rb_define_method(cls, "RTYPEDDATA_P", sws_typed_RTYPEDDATA_P, 1);
}
#ifdef __cplusplus
diff --git a/spec/ruby/optional/capi/ext/util_spec.c b/spec/ruby/optional/capi/ext/util_spec.c
index 95ba71ea9d..b5bde420d2 100644
--- a/spec/ruby/optional/capi/ext/util_spec.c
+++ b/spec/ruby/optional/capi/ext/util_spec.c
@@ -62,22 +62,17 @@ static VALUE util_spec_rb_get_kwargs(VALUE self, VALUE keyword_hash, VALUE keys,
int len = RARRAY_LENINT(keys);
int values_len = req + (opt < 0 ? -1 - opt : opt);
- int i = 0;
- ID *ids = (ID*) malloc(sizeof(VALUE) * len);
- VALUE *results = (VALUE*) malloc(sizeof(VALUE) * values_len);
- int extracted = 0;
- VALUE ary = Qundef;
+ ID *ids = (ID *)alloca(sizeof(VALUE) * len);
+ VALUE *results = (VALUE *)alloca(sizeof(VALUE) * values_len);
- for (i = 0; i < len; i++) {
+ for (int i = 0; i < len; i++) {
ids[i] = SYM2ID(rb_ary_entry(keys, i));
}
- extracted = rb_get_kwargs(keyword_hash, ids, req, opt, results);
- ary = rb_ary_new_from_values(extracted, results);
- free(results);
- free(ids);
- return ary;
+ int extracted = rb_get_kwargs(keyword_hash, ids, req, opt, results);
+
+ return rb_ary_new_from_values(extracted, results);
}
static VALUE util_spec_rb_long2int(VALUE self, VALUE n) {
diff --git a/spec/ruby/optional/capi/file_spec.rb b/spec/ruby/optional/capi/file_spec.rb
index 96d731e4fa..12449b4e34 100644
--- a/spec/ruby/optional/capi/file_spec.rb
+++ b/spec/ruby/optional/capi/file_spec.rb
@@ -69,7 +69,7 @@ describe "C-API File function" do
end
it "does not call #to_str on a String" do
- obj = "path"
+ obj = +"path"
obj.should_not_receive(:to_str)
@s.FilePathValue(obj).should eql(obj)
end
diff --git a/spec/ruby/optional/capi/fixtures/kernel.rb b/spec/ruby/optional/capi/fixtures/kernel.rb
new file mode 100644
index 0000000000..d3fc7c57e8
--- /dev/null
+++ b/spec/ruby/optional/capi/fixtures/kernel.rb
@@ -0,0 +1,19 @@
+class CApiKernelSpecs
+ class ClassWithPublicMethod
+ def public_method(*, **)
+ :public
+ end
+ end
+
+ class ClassWithPrivateMethod
+ private def private_method(*, **)
+ :private
+ end
+ end
+
+ class ClassWithProtectedMethod
+ protected def protected_method(*, **)
+ :protected
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/gc_spec.rb b/spec/ruby/optional/capi/gc_spec.rb
index d76ea7394f..aaced56483 100644
--- a/spec/ruby/optional/capi/gc_spec.rb
+++ b/spec/ruby/optional/capi/gc_spec.rb
@@ -27,9 +27,36 @@ describe "CApiGCSpecs" do
end
describe "rb_global_variable" do
- it "keeps the value alive even if the value is assigned after rb_global_variable() is called" do
+ before :all do
GC.start
- @f.registered_before_rb_global_variable.should == "registered before rb_global_variable()"
+ end
+
+ describe "keeps the value alive even if the value is assigned after rb_global_variable() is called" do
+ it "for a string" do
+ @f.registered_before_rb_global_variable_string.should == "registered before rb_global_variable()"
+ end
+
+ it "for a bignum" do
+ @f.registered_before_rb_global_variable_bignum.should == 2**63 - 1
+ end
+
+ it "for a Float" do
+ @f.registered_before_rb_global_variable_float.should == 3.14
+ end
+ end
+
+ describe "keeps the value alive when the value is assigned before rb_global_variable() is called" do
+ it "for a string" do
+ @f.registered_after_rb_global_variable_string.should == "registered after rb_global_variable()"
+ end
+
+ it "for a bignum" do
+ @f.registered_after_rb_global_variable_bignum.should == 2**63 - 1
+ end
+
+ it "for a Float" do
+ @f.registered_after_rb_global_variable_float.should == 6.28
+ end
end
end
diff --git a/spec/ruby/optional/capi/hash_spec.rb b/spec/ruby/optional/capi/hash_spec.rb
index a60467a66b..a0e49ffc4c 100644
--- a/spec/ruby/optional/capi/hash_spec.rb
+++ b/spec/ruby/optional/capi/hash_spec.rb
@@ -50,6 +50,22 @@ describe "C-API Hash function" do
end
end
+ ruby_version_is '3.2' do
+ describe "rb_hash_new_capa" do
+ it "returns a new hash" do
+ @s.rb_hash_new_capa(3).should == {}
+ end
+
+ it "creates a hash with no default proc" do
+ @s.rb_hash_new_capa(3) {}.default_proc.should be_nil
+ end
+
+ it "raises RuntimeError when negative index is provided" do
+ -> { @s.rb_hash_new_capa(-1) }.should raise_error(RuntimeError, "st_table too big")
+ end
+ end
+ end
+
describe "rb_ident_hash_new" do
it "returns a new compare by identity hash" do
result = @s.rb_ident_hash_new
diff --git a/spec/ruby/optional/capi/integer_spec.rb b/spec/ruby/optional/capi/integer_spec.rb
index e26735824e..089872381c 100644
--- a/spec/ruby/optional/capi/integer_spec.rb
+++ b/spec/ruby/optional/capi/integer_spec.rb
@@ -140,6 +140,23 @@ describe "CApiIntegerSpecs" do
result.should == -1
@words.should == "\x11\x32\x54\x76\x98\xBA\xDC\xFE"
end
+
+ it "converts numbers near the fixnum limit successfully" do
+ result = @s.rb_integer_pack(0x7123_4567_89ab_cdef, @words, 1, 8, 0,
+ CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP)
+ result.should == 1
+ @words.should == "\xEF\xCD\xAB\x89\x67\x45\x23\x71"
+
+ result = @s.rb_integer_pack(2**62-1, @words, 1, 8, 0,
+ CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP)
+ result.should == 1
+ @words.should == "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x3F"
+
+ result = @s.rb_integer_pack(2**63-1, @words, 1, 8, 0,
+ CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP)
+ result.should == 1
+ @words.should == "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x7F"
+ end
end
end
end
diff --git a/spec/ruby/optional/capi/io_spec.rb b/spec/ruby/optional/capi/io_spec.rb
index 92a95df60a..870abef3ea 100644
--- a/spec/ruby/optional/capi/io_spec.rb
+++ b/spec/ruby/optional/capi/io_spec.rb
@@ -175,13 +175,18 @@ describe "C-API IO function" do
end
end
- describe "GetOpenFile" do
+ describe "rb_io_descriptor or GetOpenFile" do
it "allows access to the system fileno" do
@o.GetOpenFile_fd($stdin).should == 0
@o.GetOpenFile_fd($stdout).should == 1
@o.GetOpenFile_fd($stderr).should == 2
@o.GetOpenFile_fd(@io).should == @io.fileno
end
+
+ it "raises IOError if the IO is closed" do
+ @io.close
+ -> { @o.GetOpenFile_fd(@io) }.should raise_error(IOError, "closed stream")
+ end
end
describe "rb_io_binmode" do
@@ -435,10 +440,27 @@ describe "C-API IO function" do
end
end
end
+
+ ruby_version_is "3.3" do
+ describe "rb_io_mode" do
+ it "returns the mode" do
+ (@o.rb_io_mode(@r_io) & 0b11).should == 0b01
+ (@o.rb_io_mode(@w_io) & 0b11).should == 0b10
+ (@o.rb_io_mode(@rw_io) & 0b11).should == 0b11
+ end
+ end
+
+ describe "rb_io_path" do
+ it "returns the IO#path" do
+ @o.rb_io_path(@r_io).should == @r_io.path
+ @o.rb_io_path(@rw_io).should == @rw_io.path
+ @o.rb_io_path(@rw_io).should == @name
+ end
+ end
+ end
end
describe "rb_fd_fix_cloexec" do
-
before :each do
@o = CApiIOSpecs.new
diff --git a/spec/ruby/optional/capi/kernel_spec.rb b/spec/ruby/optional/capi/kernel_spec.rb
index 54d8d8a8d3..3b61d4f0f1 100644
--- a/spec/ruby/optional/capi/kernel_spec.rb
+++ b/spec/ruby/optional/capi/kernel_spec.rb
@@ -1,4 +1,5 @@
require_relative 'spec_helper'
+require_relative 'fixtures/kernel'
kernel_path = load_extension("kernel")
@@ -502,6 +503,11 @@ describe "C-API Kernel function" do
it "evaluates a string of ruby code" do
@s.rb_eval_string("1+1").should == 2
end
+
+ it "captures local variables when called within a method" do
+ a = 2
+ @s.rb_eval_string("a+1").should == 3
+ end
end
describe "rb_eval_cmd_kw" do
@@ -597,47 +603,65 @@ describe "C-API Kernel function" do
@s.rb_funcallv(self, :empty, []).should == 42
@s.rb_funcallv(self, :sum, [1, 2]).should == 3
end
- end
- ruby_version_is "3.0" do
- describe "rb_funcallv_kw" do
- it "passes keyword arguments to the callee" do
- def m(*args, **kwargs)
- [args, kwargs]
- end
+ it "calls a private method" do
+ object = CApiKernelSpecs::ClassWithPrivateMethod.new
+ @s.rb_funcallv(object, :private_method, []).should == :private
+ end
+
+ it "calls a protected method" do
+ object = CApiKernelSpecs::ClassWithProtectedMethod.new
+ @s.rb_funcallv(object, :protected_method, []).should == :protected
+ end
+ end
- @s.rb_funcallv_kw(self, :m, [{}]).should == [[], {}]
- @s.rb_funcallv_kw(self, :m, [{a: 1}]).should == [[], {a: 1}]
- @s.rb_funcallv_kw(self, :m, [{b: 2}, {a: 1}]).should == [[{b: 2}], {a: 1}]
- @s.rb_funcallv_kw(self, :m, [{b: 2}, {}]).should == [[{b: 2}], {}]
+ describe "rb_funcallv_kw" do
+ it "passes keyword arguments to the callee" do
+ def m(*args, **kwargs)
+ [args, kwargs]
end
- it "raises TypeError if the last argument is not a Hash" do
- def m(*args, **kwargs)
- [args, kwargs]
- end
+ @s.rb_funcallv_kw(self, :m, [{}]).should == [[], {}]
+ @s.rb_funcallv_kw(self, :m, [{a: 1}]).should == [[], {a: 1}]
+ @s.rb_funcallv_kw(self, :m, [{b: 2}, {a: 1}]).should == [[{b: 2}], {a: 1}]
+ @s.rb_funcallv_kw(self, :m, [{b: 2}, {}]).should == [[{b: 2}], {}]
+ end
+
+ it "calls a private method" do
+ object = CApiKernelSpecs::ClassWithPrivateMethod.new
+ @s.rb_funcallv_kw(object, :private_method, [{}]).should == :private
+ end
- -> {
- @s.rb_funcallv_kw(self, :m, [42])
- }.should raise_error(TypeError, 'no implicit conversion of Integer into Hash')
+ it "calls a protected method" do
+ object = CApiKernelSpecs::ClassWithProtectedMethod.new
+ @s.rb_funcallv_kw(object, :protected_method, [{}]).should == :protected
+ end
+
+ it "raises TypeError if the last argument is not a Hash" do
+ def m(*args, **kwargs)
+ [args, kwargs]
end
+
+ -> {
+ @s.rb_funcallv_kw(self, :m, [42])
+ }.should raise_error(TypeError, 'no implicit conversion of Integer into Hash')
end
+ end
- describe "rb_keyword_given_p" do
- it "returns whether keywords were given to the C extension method" do
- h = {a: 1}
- empty = {}
- @s.rb_keyword_given_p(a: 1).should == true
- @s.rb_keyword_given_p("foo" => "bar").should == true
- @s.rb_keyword_given_p(**h).should == true
+ describe "rb_keyword_given_p" do
+ it "returns whether keywords were given to the C extension method" do
+ h = {a: 1}
+ empty = {}
+ @s.rb_keyword_given_p(a: 1).should == true
+ @s.rb_keyword_given_p("foo" => "bar").should == true
+ @s.rb_keyword_given_p(**h).should == true
- @s.rb_keyword_given_p(h).should == false
- @s.rb_keyword_given_p().should == false
- @s.rb_keyword_given_p(**empty).should == false
+ @s.rb_keyword_given_p(h).should == false
+ @s.rb_keyword_given_p().should == false
+ @s.rb_keyword_given_p(**empty).should == false
- @s.rb_funcallv_kw(@s, :rb_keyword_given_p, [{a: 1}]).should == true
- @s.rb_funcallv_kw(@s, :rb_keyword_given_p, [{}]).should == false
- end
+ @s.rb_funcallv_kw(@s, :rb_keyword_given_p, [{a: 1}]).should == true
+ @s.rb_funcallv_kw(@s, :rb_keyword_given_p, [{}]).should == false
end
end
@@ -676,21 +700,91 @@ describe "C-API Kernel function" do
end
describe 'rb_funcall_with_block' do
- before :each do
+ it "calls a method with block" do
@obj = Object.new
class << @obj
- def method_public; yield end
- def method_private; yield end
- private :method_private
+ def method_public(*args); [args, yield] end
end
+
+ @s.rb_funcall_with_block(@obj, :method_public, [1, 2], proc { :result }).should == [[1, 2], :result]
end
- it "calls a method with block" do
- @s.rb_funcall_with_block(@obj, :method_public, proc { :result }).should == :result
+ it "does not call a private method" do
+ object = CApiKernelSpecs::ClassWithPrivateMethod.new
+
+ -> {
+ @s.rb_funcall_with_block(object, :private_method, [], proc { })
+ }.should raise_error(NoMethodError, /private/)
+ end
+
+ it "does not call a protected method" do
+ object = CApiKernelSpecs::ClassWithProtectedMethod.new
+
+ -> {
+ @s.rb_funcall_with_block(object, :protected_method, [], proc { })
+ }.should raise_error(NoMethodError, /protected/)
+ end
+ end
+
+ describe 'rb_funcall_with_block_kw' do
+ it "calls a method with keyword arguments and a block" do
+ @obj = Object.new
+ class << @obj
+ def method_public(*args, **kw, &block); [args, kw, block.call] end
+ end
+
+ @s.rb_funcall_with_block_kw(@obj, :method_public, [1, 2, {a: 2}], proc { :result }).should == [[1, 2], {a: 2}, :result]
end
it "does not call a private method" do
- -> { @s.rb_funcall_with_block(@obj, :method_private, proc { :result }) }.should raise_error(NoMethodError, /private/)
+ object = CApiKernelSpecs::ClassWithPrivateMethod.new
+
+ -> {
+ @s.rb_funcall_with_block_kw(object, :private_method, [{}], proc { })
+ }.should raise_error(NoMethodError, /private/)
+ end
+
+ it "does not call a protected method" do
+ object = CApiKernelSpecs::ClassWithProtectedMethod.new
+
+ -> {
+ @s.rb_funcall_with_block_kw(object, :protected_method, [{}], proc { })
+ }.should raise_error(NoMethodError, /protected/)
+ end
+ end
+
+ describe "rb_check_funcall" do
+ it "calls a method" do
+ @s.rb_check_funcall(1, :+, [2]).should == 3
+ end
+
+ it "returns Qundef if the method is not defined" do
+ obj = Object.new
+ @s.rb_check_funcall(obj, :foo, []).should == :Qundef
+ end
+
+ it "uses #respond_to? to check if the method is defined" do
+ ScratchPad.record []
+ obj = Object.new
+ def obj.respond_to?(name, priv)
+ ScratchPad << name
+ name == :foo || super
+ end
+ def obj.method_missing(name, *args)
+ name == :foo ? [name, 42] : super
+ end
+ @s.rb_check_funcall(obj, :foo, []).should == [:foo, 42]
+ ScratchPad.recorded.should == [:foo]
+ end
+
+ it "calls a private method" do
+ object = CApiKernelSpecs::ClassWithPrivateMethod.new
+ @s.rb_check_funcall(object, :private_method, []).should == :private
+ end
+
+ it "calls a protected method" do
+ object = CApiKernelSpecs::ClassWithProtectedMethod.new
+ @s.rb_check_funcall(object, :protected_method, []).should == :protected
end
end
end
diff --git a/spec/ruby/optional/capi/object_spec.rb b/spec/ruby/optional/capi/object_spec.rb
index 9efc892202..7bc7bd992a 100644
--- a/spec/ruby/optional/capi/object_spec.rb
+++ b/spec/ruby/optional/capi/object_spec.rb
@@ -219,7 +219,7 @@ describe "CApiObject" do
end
it "requires a ruby file" do
- $:.unshift File.dirname(__FILE__)
+ $:.unshift __dir__
@o.rb_require()
$foo.should == 7
end
@@ -686,7 +686,7 @@ describe "CApiObject" do
end
it "returns false if object passed to it is not frozen" do
- obj = ""
+ obj = +""
@o.rb_obj_frozen_p(obj).should == false
end
end
@@ -700,7 +700,7 @@ describe "CApiObject" do
end
it "does nothing when object isn't frozen" do
- obj = ""
+ obj = +""
-> { @o.rb_check_frozen(obj) }.should_not raise_error(TypeError)
end
end
@@ -894,9 +894,9 @@ describe "CApiObject" do
describe "rb_copy_generic_ivar for objects which do not store ivars directly" do
it "copies the instance variables from one object to another" do
- original = "abc"
+ original = +"abc"
original.instance_variable_set(:@foo, :bar)
- clone = "def"
+ clone = +"def"
@o.rb_copy_generic_ivar(clone, original)
clone.instance_variable_get(:@foo).should == :bar
end
@@ -904,7 +904,7 @@ describe "CApiObject" do
describe "rb_free_generic_ivar for objects which do not store ivars directly" do
it "removes the instance variables from an object" do
- o = "abc"
+ o = +"abc"
o.instance_variable_set(:@baz, :flibble)
@o.rb_free_generic_ivar(o)
o.instance_variables.should == []
diff --git a/spec/ruby/optional/capi/proc_spec.rb b/spec/ruby/optional/capi/proc_spec.rb
index c839665ae9..8b94432f3e 100644
--- a/spec/ruby/optional/capi/proc_spec.rb
+++ b/spec/ruby/optional/capi/proc_spec.rb
@@ -82,6 +82,59 @@ describe "C-API Proc function" do
end
end
+ describe "rb_proc_call_kw" do
+ it "passes keyword arguments to the proc" do
+ prc = proc { |*args, **kw| [args, kw] }
+
+ @p.rb_proc_call_kw(prc, [{}]).should == [[], {}]
+ @p.rb_proc_call_kw(prc, [{a: 1}]).should == [[], {a: 1}]
+ @p.rb_proc_call_kw(prc, [{b: 2}, {a: 1}]).should == [[{b: 2}], {a: 1}]
+ @p.rb_proc_call_kw(prc, [{b: 2}, {}]).should == [[{b: 2}], {}]
+ end
+
+ it "raises TypeError if the last argument is not a Hash" do
+ -> {
+ @p.rb_proc_call_kw(proc {}, [42])
+ }.should raise_error(TypeError, 'no implicit conversion of Integer into Hash')
+ end
+ end
+
+ describe "rb_proc_call_with_block" do
+ it "calls the Proc and passes arguments and a block" do
+ prc = Proc.new { |a, b, &block| block.call(a * b) }
+ @p.rb_proc_call_with_block(prc, [6, 7], proc { |n| n * 2 }).should == 6 * 7 * 2
+ end
+
+ it "calls the Proc and passes arguments when a block is nil" do
+ prc = Proc.new { |a, b| a * b }
+ @p.rb_proc_call_with_block(prc, [6, 7], nil).should == 6 * 7
+ end
+ end
+
+ describe "rb_proc_call_with_block_kw" do
+ it "passes keyword arguments and a block to the proc" do
+ prc = proc { |*args, **kw, &block| [args, kw, block.call(42)] }
+ block = proc { |n| n }
+
+ @p.rb_proc_call_with_block_kw(prc, [{}], block).should == [[], {}, 42]
+ @p.rb_proc_call_with_block_kw(prc, [{a: 1}], block).should == [[], {a: 1}, 42]
+ @p.rb_proc_call_with_block_kw(prc, [{b: 2}, {a: 1}], block).should == [[{b: 2}], {a: 1}, 42]
+ @p.rb_proc_call_with_block_kw(prc, [{b: 2}, {}], block).should == [[{b: 2}], {}, 42]
+ end
+
+ it "raises TypeError if the last argument is not a Hash" do
+ -> {
+ @p.rb_proc_call_with_block_kw(proc {}, [42], proc {})
+ }.should raise_error(TypeError, 'no implicit conversion of Integer into Hash')
+ end
+
+ it "passes keyword arguments to the proc when a block is nil" do
+ prc = proc { |*args, **kw| [args, kw] }
+
+ @p.rb_proc_call_with_block_kw(prc, [{}], nil).should == [[], {}]
+ end
+ end
+
describe "rb_obj_is_proc" do
it "returns true for Proc" do
prc = Proc.new {|a,b| a * b }
diff --git a/spec/ruby/optional/capi/rbasic_spec.rb b/spec/ruby/optional/capi/rbasic_spec.rb
index 577f2060da..f3367e05ff 100644
--- a/spec/ruby/optional/capi/rbasic_spec.rb
+++ b/spec/ruby/optional/capi/rbasic_spec.rb
@@ -33,6 +33,8 @@ describe "RBasic support for RData" do
initial = @specs.get_flags(obj1)
@specs.get_flags(obj2).should == initial
@specs.set_flags(obj1, 1 << 14 | 1 << 16 | initial)
+ @specs.get_flags(obj1).should == 1 << 14 | 1 << 16 | initial
+
@specs.copy_flags(obj2, obj1)
@specs.get_flags(obj2).should == 1 << 14 | 1 << 16 | initial
@specs.set_flags(obj1, initial)
diff --git a/spec/ruby/optional/capi/shared/rbasic.rb b/spec/ruby/optional/capi/shared/rbasic.rb
index 95c3137143..9d80a93e1d 100644
--- a/spec/ruby/optional/capi/shared/rbasic.rb
+++ b/spec/ruby/optional/capi/shared/rbasic.rb
@@ -1,5 +1,4 @@
describe :rbasic, shared: true do
-
before :all do
specs = CApiRBasicSpecs.new
@taint = ruby_version_is(''...'3.1') ? specs.taint_flag : 0
diff --git a/spec/ruby/optional/capi/string_spec.rb b/spec/ruby/optional/capi/string_spec.rb
index 3a47fa9762..a8edf998b5 100644
--- a/spec/ruby/optional/capi/string_spec.rb
+++ b/spec/ruby/optional/capi/string_spec.rb
@@ -1,4 +1,5 @@
# encoding: utf-8
+# frozen_string_literal: false
require_relative 'spec_helper'
require_relative '../../shared/string/times'
@@ -47,7 +48,7 @@ describe "C-API String function" do
[Encoding::BINARY, Encoding::UTF_8].each do |enc|
describe "rb_str_set_len on a #{enc.name} String" do
before :each do
- @str = "abcdefghij".force_encoding(enc)
+ @str = "abcdefghij".dup.force_encoding(enc)
# Make sure to unshare the string
@s.rb_str_modify(@str)
end
@@ -99,7 +100,7 @@ describe "C-API String function" do
describe "rb_str_set_len on a UTF-16 String" do
before :each do
- @str = "abcdefghij".force_encoding(Encoding::UTF_16BE)
+ @str = "abcdefghij".dup.force_encoding(Encoding::UTF_16BE)
# Make sure to unshare the string
@s.rb_str_modify(@str)
end
@@ -112,7 +113,7 @@ describe "C-API String function" do
describe "rb_str_set_len on a UTF-32 String" do
before :each do
- @str = "abcdefghijkl".force_encoding(Encoding::UTF_32BE)
+ @str = "abcdefghijkl".dup.force_encoding(Encoding::UTF_32BE)
# Make sure to unshare the string
@s.rb_str_modify(@str)
end
@@ -231,7 +232,7 @@ describe "C-API String function" do
describe "rb_usascii_str_new" do
it "creates a new String with US-ASCII Encoding from a char buffer of len characters" do
- str = "abc".force_encoding("us-ascii")
+ str = "abc".dup.force_encoding("us-ascii")
result = @s.rb_usascii_str_new("abcdef", 3)
result.should == str
result.encoding.should == Encoding::US_ASCII
@@ -247,14 +248,14 @@ describe "C-API String function" do
it "returns US-ASCII string for non-US-ASCII string literal" do
str = @s.rb_usascii_str_new_lit_non_ascii
- str.should == "r\xC3\xA9sum\xC3\xA9".force_encoding(Encoding::US_ASCII)
+ str.should == "r\xC3\xA9sum\xC3\xA9".dup.force_encoding(Encoding::US_ASCII)
str.encoding.should == Encoding::US_ASCII
end
end
describe "rb_usascii_str_new_cstr" do
it "creates a new String with US-ASCII Encoding" do
- str = "abc".force_encoding("us-ascii")
+ str = "abc".dup.force_encoding("us-ascii")
result = @s.rb_usascii_str_new_cstr("abc")
result.should == str
result.encoding.should == Encoding::US_ASCII
@@ -418,7 +419,7 @@ describe "C-API String function" do
describe "rb_enc_str_buf_cat" do
it "concatenates a C string literal to a ruby string with the given encoding" do
- input = "hello ".force_encoding(Encoding::US_ASCII)
+ input = "hello ".dup.force_encoding(Encoding::US_ASCII)
result = @s.rb_enc_str_buf_cat(input, "résumé", Encoding::UTF_8)
result.should == "hello résumé"
result.encoding.should == Encoding::UTF_8
@@ -498,29 +499,10 @@ describe "C-API String function" do
end
end
- describe "rb_fstring" do
- it 'returns self if the String is frozen' do
- input = 'foo'.freeze
- output = @s.rb_fstring(input)
-
- output.should equal(input)
- output.should.frozen?
- end
-
- it 'returns a frozen copy if the String is not frozen' do
- input = 'foo'
- output = @s.rb_fstring(input)
-
- output.should.frozen?
- output.should_not equal(input)
- output.should == 'foo'
- end
- end
-
describe "rb_str_subseq" do
it "returns a byte-indexed substring" do
- str = "\x00\x01\x02\x03\x04".force_encoding("binary")
- @s.rb_str_subseq(str, 1, 2).should == "\x01\x02".force_encoding("binary")
+ str = "\x00\x01\x02\x03\x04".dup.force_encoding("binary")
+ @s.rb_str_subseq(str, 1, 2).should == "\x01\x02".dup.force_encoding("binary")
end
end
@@ -731,7 +713,7 @@ describe "C-API String function" do
end
it "increases the size of the string" do
- expected = "test".force_encoding("US-ASCII")
+ expected = "test".dup.force_encoding("US-ASCII")
str = @s.rb_str_resize(expected.dup, 12)
str.size.should == 12
str.bytesize.should == 12
@@ -862,11 +844,11 @@ describe "C-API String function" do
# it "transcodes a String to Encoding.default_internal if it is set" do
# Encoding.default_internal = Encoding::EUC_JP
#
-# - a = "\xE3\x81\x82\xe3\x82\x8c".force_encoding("utf-8")
+# - a = "\xE3\x81\x82\xe3\x82\x8c".dup.force_encoding("utf-8")
# + a = [0xE3, 0x81, 0x82, 0xe3, 0x82, 0x8c].pack('C6').force_encoding("utf-8")
# s = @s.rb_external_str_new_with_enc(a, a.bytesize, Encoding::UTF_8)
# -
-# - s.should == "\xA4\xA2\xA4\xEC".force_encoding("euc-jp")
+# - s.should == "\xA4\xA2\xA4\xEC".dup.force_encoding("euc-jp")
# + x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4')#.force_encoding('binary')
# + s.should == x
# s.encoding.should equal(Encoding::EUC_JP)
@@ -886,7 +868,7 @@ describe "C-API String function" do
describe "rb_locale_str_new" do
it "returns a String with 'locale' encoding" do
s = @s.rb_locale_str_new("abc", 3)
- s.should == "abc".force_encoding(Encoding.find("locale"))
+ s.should == "abc".dup.force_encoding(Encoding.find("locale"))
s.encoding.should equal(Encoding.find("locale"))
end
end
@@ -894,14 +876,14 @@ describe "C-API String function" do
describe "rb_locale_str_new_cstr" do
it "returns a String with 'locale' encoding" do
s = @s.rb_locale_str_new_cstr("abc")
- s.should == "abc".force_encoding(Encoding.find("locale"))
+ s.should == "abc".dup.force_encoding(Encoding.find("locale"))
s.encoding.should equal(Encoding.find("locale"))
end
end
describe "rb_str_conv_enc" do
it "returns the original String when to encoding is not specified" do
- a = "abc".force_encoding("us-ascii")
+ a = "abc".dup.force_encoding("us-ascii")
@s.rb_str_conv_enc(a, Encoding::US_ASCII, nil).should equal(a)
end
@@ -911,7 +893,7 @@ describe "C-API String function" do
end
it "returns a transcoded String" do
- a = "\xE3\x81\x82\xE3\x82\x8C".force_encoding("utf-8")
+ a = "\xE3\x81\x82\xE3\x82\x8C".dup.force_encoding("utf-8")
result = @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::EUC_JP)
x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4').force_encoding('utf-8')
result.should == x.force_encoding("euc-jp")
@@ -920,7 +902,7 @@ describe "C-API String function" do
describe "when the String encoding is equal to the destination encoding" do
it "returns the original String" do
- a = "abc".force_encoding("us-ascii")
+ a = "abc".dup.force_encoding("us-ascii")
@s.rb_str_conv_enc(a, Encoding::US_ASCII, Encoding::US_ASCII).should equal(a)
end
@@ -930,7 +912,7 @@ describe "C-API String function" do
end
it "returns the origin String if the destination encoding is BINARY" do
- a = "abc".force_encoding("binary")
+ a = "abc".dup.force_encoding("binary")
@s.rb_str_conv_enc(a, Encoding::US_ASCII, Encoding::BINARY).should equal(a)
end
end
@@ -938,7 +920,7 @@ describe "C-API String function" do
describe "rb_str_conv_enc_opts" do
it "returns the original String when to encoding is not specified" do
- a = "abc".force_encoding("us-ascii")
+ a = "abc".dup.force_encoding("us-ascii")
@s.rb_str_conv_enc_opts(a, Encoding::US_ASCII, nil, 0, nil).should equal(a)
end
@@ -949,7 +931,7 @@ describe "C-API String function" do
end
it "returns a transcoded String" do
- a = "\xE3\x81\x82\xE3\x82\x8C".force_encoding("utf-8")
+ a = "\xE3\x81\x82\xE3\x82\x8C".dup.force_encoding("utf-8")
result = @s.rb_str_conv_enc_opts(a, Encoding::UTF_8, Encoding::EUC_JP, 0, nil)
x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4').force_encoding('utf-8')
result.should == x.force_encoding("euc-jp")
@@ -958,7 +940,7 @@ describe "C-API String function" do
describe "when the String encoding is equal to the destination encoding" do
it "returns the original String" do
- a = "abc".force_encoding("us-ascii")
+ a = "abc".dup.force_encoding("us-ascii")
@s.rb_str_conv_enc_opts(a, Encoding::US_ASCII,
Encoding::US_ASCII, 0, nil).should equal(a)
end
@@ -970,7 +952,7 @@ describe "C-API String function" do
end
it "returns the origin String if the destination encoding is BINARY" do
- a = "abc".force_encoding("binary")
+ a = "abc".dup.force_encoding("binary")
@s.rb_str_conv_enc_opts(a, Encoding::US_ASCII,
Encoding::BINARY, 0, nil).should equal(a)
end
@@ -988,7 +970,7 @@ describe "C-API String function" do
describe "rb_str_export_locale" do
it "returns the original String with the locale encoding" do
s = @s.rb_str_export_locale("abc")
- s.should == "abc".force_encoding(Encoding.find("locale"))
+ s.should == "abc".dup.force_encoding(Encoding.find("locale"))
s.encoding.should equal(Encoding.find("locale"))
end
end
@@ -1227,4 +1209,67 @@ end
-> { @s.rb_str_unlocktmp("test") }.should raise_error(RuntimeError, 'temporal unlocking already unlocked string')
end
end
+
+ describe "rb_enc_interned_str_cstr" do
+ it "returns a frozen string" do
+ str = "hello"
+ val = @s.rb_enc_interned_str_cstr(str, Encoding::US_ASCII)
+
+ val.should.is_a?(String)
+ val.encoding.should == Encoding::US_ASCII
+ val.should.frozen?
+ end
+
+ it "returns the same frozen string" do
+ str = "hello"
+ result1 = @s.rb_enc_interned_str_cstr(str, Encoding::US_ASCII)
+ result2 = @s.rb_enc_interned_str_cstr(str, Encoding::US_ASCII)
+ result1.should.equal?(result2)
+ end
+
+ it "returns different frozen strings for different encodings" do
+ str = "hello"
+ result1 = @s.rb_enc_interned_str_cstr(str, Encoding::US_ASCII)
+ result2 = @s.rb_enc_interned_str_cstr(str, Encoding::UTF_8)
+ result1.should_not.equal?(result2)
+ end
+
+ it "returns the same string as String#-@" do
+ @s.rb_enc_interned_str_cstr("hello", Encoding::UTF_8).should.equal?(-"hello")
+ end
+
+ ruby_bug "#20322", ""..."3.4" do
+ it "uses the default encoding if encoding is null" do
+ str = "hello"
+ val = @s.rb_enc_interned_str_cstr(str, nil)
+ val.encoding.should == Encoding::ASCII_8BIT
+ end
+ end
+ end
+
+ describe "rb_str_to_interned_str" do
+ it "returns a frozen string" do
+ str = "hello"
+ result = @s.rb_str_to_interned_str(str)
+ result.should.is_a?(String)
+ result.should.frozen?
+ end
+
+ it "returns the same frozen string" do
+ str = "hello"
+ result1 = @s.rb_str_to_interned_str(str)
+ result2 = @s.rb_str_to_interned_str(str)
+ result1.should.equal?(result2)
+ end
+
+ it "returns different frozen strings for different encodings" do
+ result1 = @s.rb_str_to_interned_str("hello".dup.force_encoding(Encoding::US_ASCII))
+ result2 = @s.rb_str_to_interned_str("hello".dup.force_encoding(Encoding::UTF_8))
+ result1.should_not.equal?(result2)
+ end
+
+ it "returns the same string as String#-@" do
+ @s.rb_str_to_interned_str("hello").should.equal?(-"hello")
+ end
+ end
end
diff --git a/spec/ruby/optional/capi/typed_data_spec.rb b/spec/ruby/optional/capi/typed_data_spec.rb
index 23b7c157ef..6d1398a1a0 100644
--- a/spec/ruby/optional/capi/typed_data_spec.rb
+++ b/spec/ruby/optional/capi/typed_data_spec.rb
@@ -85,4 +85,16 @@ describe "CApiWrappedTypedStruct" do
-> { @s.rb_check_typeddata_different_type(a) }.should raise_error(TypeError)
end
end
+
+ describe "RTYPEDDATA_P" do
+ it "returns true for a typed data" do
+ a = @s.typed_wrap_struct(1024)
+ @s.RTYPEDDATA_P(a).should == true
+ end
+
+ it "returns false for an untyped data object" do
+ a = @s.untyped_wrap_struct(1024)
+ @s.RTYPEDDATA_P(a).should == false
+ end
+ end
end
diff --git a/spec/ruby/optional/capi/util_spec.rb b/spec/ruby/optional/capi/util_spec.rb
index 38f6f47b1a..9ff8b4760a 100644
--- a/spec/ruby/optional/capi/util_spec.rb
+++ b/spec/ruby/optional/capi/util_spec.rb
@@ -48,7 +48,7 @@ describe "C-API Util function" do
ScratchPad.recorded.should == [1, 2, [3, 4]]
end
- it "assigns the required and optional arguments and and empty Array when there are no arguments to splat" do
+ it "assigns the required and optional arguments and empty Array when there are no arguments to splat" do
@o.rb_scan_args([1, 2], "11*", 3, @acc).should == 2
ScratchPad.recorded.should == [1, 2, []]
end
@@ -115,22 +115,11 @@ describe "C-API Util function" do
ScratchPad.recorded.should == [1, nil]
end
- ruby_version_is ''...'3.0' do
- it "assigns required and Hash arguments with nil Hash" do
- suppress_warning do
- @o.rb_scan_args([1, nil], "1:", 2, @acc).should == 1
- end
- ScratchPad.recorded.should == [1, nil]
- end
- end
-
- ruby_version_is '3.0' do
- it "rejects the use of nil as a hash" do
- -> {
- @o.rb_scan_args([1, nil], "1:", 2, @acc).should == 1
- }.should raise_error(ArgumentError)
- ScratchPad.recorded.should == []
- end
+ it "rejects the use of nil as a hash" do
+ -> {
+ @o.rb_scan_args([1, nil], "1:", 2, @acc).should == 1
+ }.should raise_error(ArgumentError)
+ ScratchPad.recorded.should == []
end
it "assigns required and optional arguments with no hash argument given" do
@@ -138,61 +127,41 @@ describe "C-API Util function" do
ScratchPad.recorded.should == [1, 7, 4]
end
+ it "assigns optional arguments with no hash argument given" do
+ @o.rb_scan_args([1, 7], "02:", 3, @acc).should == 2
+ ScratchPad.recorded.should == [1, 7, nil]
+ end
+
+ it "assigns optional arguments with no hash argument given and rejects the use of optional nil argument as a hash" do
+ -> {
+ @o.rb_scan_args([1, nil], "02:", 3, @acc).should == 2
+ }.should_not complain
+
+ ScratchPad.recorded.should == [1, nil, nil]
+ end
+
it "assigns required, optional, splat, post-splat, Hash and block arguments" do
h = {a: 1, b: 2}
@o.rb_scan_args([1, 2, 3, 4, 5, h], "k11*1:&", 6, @acc, &@prc).should == 5
ScratchPad.recorded.should == [1, 2, [3, 4], 5, h, @prc]
end
- ruby_version_is ''...'3.0' do
- # r43934
- it "rejects non-keyword arguments" do
- h = {1 => 2, 3 => 4}
- -> {
- suppress_warning do
- @o.rb_scan_args([h], "k0:", 1, @acc)
- end
- }.should raise_error(ArgumentError)
- ScratchPad.recorded.should == []
- end
-
- it "rejects required and non-keyword arguments" do
- h = {1 => 2, 3 => 4}
- -> {
- suppress_warning do
- @o.rb_scan_args([1, h], "k1:", 2, @acc)
- end
- }.should raise_error(ArgumentError)
- ScratchPad.recorded.should == []
- end
-
- it "considers the hash as a post argument when there is a splat" do
- h = {1 => 2, 3 => 4}
- suppress_warning do
- @o.rb_scan_args([1, 2, 3, 4, 5, h], "k11*1:&", 6, @acc, &@prc).should == 6
- end
- ScratchPad.recorded.should == [1, 2, [3, 4, 5], h, nil, @prc]
- end
+ it "does not reject non-symbol keys in keyword arguments" do
+ h = {1 => 2, 3 => 4}
+ @o.rb_scan_args([h], "k0:", 1, @acc).should == 0
+ ScratchPad.recorded.should == [h]
end
- ruby_version_is '3.0' do
- it "does not reject non-symbol keys in keyword arguments" do
- h = {1 => 2, 3 => 4}
- @o.rb_scan_args([h], "k0:", 1, @acc).should == 0
- ScratchPad.recorded.should == [h]
- end
-
- it "does not reject non-symbol keys in keyword arguments with required argument" do
- h = {1 => 2, 3 => 4}
- @o.rb_scan_args([1, h], "k1:", 2, @acc).should == 1
- ScratchPad.recorded.should == [1, h]
- end
+ it "does not reject non-symbol keys in keyword arguments with required argument" do
+ h = {1 => 2, 3 => 4}
+ @o.rb_scan_args([1, h], "k1:", 2, @acc).should == 1
+ ScratchPad.recorded.should == [1, h]
+ end
- it "considers keyword arguments with non-symbol keys as keywords when using splat and post arguments" do
- h = {1 => 2, 3 => 4}
- @o.rb_scan_args([1, 2, 3, 4, 5, h], "k11*1:&", 6, @acc, &@prc).should == 5
- ScratchPad.recorded.should == [1, 2, [3, 4], 5, h, @prc]
- end
+ it "considers keyword arguments with non-symbol keys as keywords when using splat and post arguments" do
+ h = {1 => 2, 3 => 4}
+ @o.rb_scan_args([1, 2, 3, 4, 5, h], "k11*1:&", 6, @acc, &@prc).should == 5
+ ScratchPad.recorded.should == [1, 2, [3, 4], 5, h, @prc]
end
end
diff --git a/spec/ruby/security/cve_2010_1330_spec.rb b/spec/ruby/security/cve_2010_1330_spec.rb
index 33e88d652e..2594439550 100644
--- a/spec/ruby/security/cve_2010_1330_spec.rb
+++ b/spec/ruby/security/cve_2010_1330_spec.rb
@@ -8,7 +8,7 @@ describe "String#gsub" do
# #gsub on a string in the UTF-8 encoding but with invalid an UTF-8 byte
# sequence.
- str = "\xF6<script>"
+ str = +"\xF6<script>"
str.force_encoding Encoding::BINARY
str.gsub(/</, "&lt;").should == "\xF6&lt;script>".b
str.force_encoding Encoding::UTF_8
diff --git a/spec/ruby/security/cve_2014_8080_spec.rb b/spec/ruby/security/cve_2014_8080_spec.rb
deleted file mode 100644
index 23770f94b1..0000000000
--- a/spec/ruby/security/cve_2014_8080_spec.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-require_relative '../spec_helper'
-
-ruby_version_is ''...'3.0' do
- require 'rexml/document'
-
- describe "REXML::Document.new" do
-
- it "resists CVE-2014-8080 by raising an exception when entity expansion has grown too large" do
- xml = <<XML
- <?xml version="1.0" encoding="UTF-8" ?>
- <!DOCTYPE x [
- <!ENTITY % x0 "xxxxxxxxxx">
- <!ENTITY % x1 "%x0;%x0;%x0;%x0;%x0;%x0;%x0;%x0;%x0;%x0;">
- <!ENTITY % x2 "%x1;%x1;%x1;%x1;%x1;%x1;%x1;%x1;%x1;%x1;">
- <!ENTITY % x3 "%x2;%x2;%x2;%x2;%x2;%x2;%x2;%x2;%x2;%x2;">
- <!ENTITY % x4 "%x3;%x3;%x3;%x3;%x3;%x3;%x3;%x3;%x3;%x3;">
- <!ENTITY % x5 "%x4;%x4;%x4;%x4;%x4;%x4;%x4;%x4;%x4;%x4;">
- <!ENTITY % x6 "%x5;%x5;%x5;%x5;%x5;%x5;%x5;%x5;%x5;%x5;">
- <!ENTITY % x7 "%x6;%x6;%x6;%x6;%x6;%x6;%x6;%x6;%x6;%x6;">
- <!ENTITY % x8 "%x7;%x7;%x7;%x7;%x7;%x7;%x7;%x7;%x7;%x7;">
- <!ENTITY % x9 "%x8;%x8;%x8;%x8;%x8;%x8;%x8;%x8;%x8;%x8;">
- ]>
- <x>
- %x9;%x9;%x9;%x9;%x9;%x9;%x9;%x9;%x9;%x9;
- </x>
-XML
-
- -> {
- REXML::Document.new(xml).doctype.entities['x9'].value
- }.should raise_error(REXML::ParseException, /entity expansion has grown too large/)
- end
-
- end
-end
diff --git a/spec/ruby/security/cve_2017_17742_spec.rb b/spec/ruby/security/cve_2017_17742_spec.rb
deleted file mode 100644
index b0d93e42b8..0000000000
--- a/spec/ruby/security/cve_2017_17742_spec.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-require_relative '../spec_helper'
-
-# webrick is no longer in stdlib in Ruby 3+
-ruby_version_is ""..."3.0" do
- require "webrick"
- require "stringio"
- require "net/http"
-
- describe "WEBrick" do
- describe "resists CVE-2017-17742" do
- it "for a response splitting headers" do
- config = WEBrick::Config::HTTP
- res = WEBrick::HTTPResponse.new config
- res['X-header'] = "malicious\r\nCookie: hack"
- io = StringIO.new
- res.send_response io
- io.rewind
- res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io))
- res.code.should == '500'
- io.string.should_not =~ /hack/
- end
-
- it "for a response splitting cookie headers" do
- user_input = "malicious\r\nCookie: hack"
- config = WEBrick::Config::HTTP
- res = WEBrick::HTTPResponse.new config
- res.cookies << WEBrick::Cookie.new('author', user_input)
- io = StringIO.new
- res.send_response io
- io.rewind
- res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io))
- res.code.should == '500'
- io.string.should_not =~ /hack/
- end
- end
- end
-end
diff --git a/spec/ruby/security/cve_2019_8323_spec.rb b/spec/ruby/security/cve_2019_8323_spec.rb
index d4606de054..49a31a6682 100644
--- a/spec/ruby/security/cve_2019_8323_spec.rb
+++ b/spec/ruby/security/cve_2019_8323_spec.rb
@@ -11,7 +11,12 @@ describe "CVE-2019-8323 is resisted by" do
cutter = Class.new {
include Gem::GemcutterUtilities
}.new
- response = Net::HTTPSuccess.new(nil, nil, nil)
+ klass = if defined?(Gem::Net::HTTPSuccess)
+ Gem::Net::HTTPSuccess
+ else
+ Net::HTTPSuccess
+ end
+ response = klass.new(nil, nil, nil)
def response.body
"\e]2;nyan\a"
end
@@ -25,7 +30,12 @@ describe "CVE-2019-8323 is resisted by" do
}.new
def cutter.terminate_interaction(n)
end
- response = Net::HTTPNotFound.new(nil, nil, nil)
+ klass = if defined?(Gem::Net::HTTPNotFound)
+ Gem::Net::HTTPNotFound
+ else
+ Net::HTTPNotFound
+ end
+ response = klass.new(nil, nil, nil)
def response.body
"\e]2;nyan\a"
end
diff --git a/spec/ruby/shared/file/exist.rb b/spec/ruby/shared/file/exist.rb
index 3bd97711b4..67424146c5 100644
--- a/spec/ruby/shared/file/exist.rb
+++ b/spec/ruby/shared/file/exist.rb
@@ -4,11 +4,6 @@ describe :file_exist, shared: true do
@object.send(@method, 'a_fake_file').should == false
end
- it "returns true if the file exist using the alias exists?" do
- @object.send(@method, __FILE__).should == true
- @object.send(@method, 'a_fake_file').should == false
- end
-
it "raises an ArgumentError if not passed one argument" do
-> { @object.send(@method) }.should raise_error(ArgumentError)
-> { @object.send(@method, __FILE__, __FILE__) }.should raise_error(ArgumentError)
diff --git a/spec/ruby/shared/kernel/at_exit.rb b/spec/ruby/shared/kernel/at_exit.rb
index 26ad361a5b..16d41cb01c 100644
--- a/spec/ruby/shared/kernel/at_exit.rb
+++ b/spec/ruby/shared/kernel/at_exit.rb
@@ -54,7 +54,10 @@ describe :kernel_at_exit, shared: true do
result = ruby_exe('{', options: "-r#{script}", args: "2>&1", exit_status: 1)
$?.should_not.success?
result.should.include?("handler ran\n")
- result.should.include?("syntax error")
+
+ # it's tempting not to rely on error message and rely only on exception class name,
+ # but CRuby before 3.2 doesn't print class name for syntax error
+ result.should include_any_of("syntax error", "SyntaxError")
end
it "calls the nested handler right after the outer one if a handler is nested into another handler" do
diff --git a/spec/ruby/shared/kernel/object_id.rb b/spec/ruby/shared/kernel/object_id.rb
index 7acdb27554..099df8ff94 100644
--- a/spec/ruby/shared/kernel/object_id.rb
+++ b/spec/ruby/shared/kernel/object_id.rb
@@ -52,10 +52,30 @@ describe :object_id, shared: true do
o1.send(@method).should_not == o2.send(@method)
end
- it "returns a different value for two String literals" do
- o1 = "hello"
- o2 = "hello"
- o1.send(@method).should_not == o2.send(@method)
+ guard -> { "test".frozen? && "test".equal?("test") } do # --enable-frozen-string-literal in $RUBYOPT
+ it "returns the same value for two identical String literals" do
+ o1 = "hello"
+ o2 = "hello"
+ o1.send(@method).should == o2.send(@method)
+ end
+ end
+
+ guard -> { "test".frozen? && !"test".equal?("test") } do # chilled string literals
+ it "returns a different frozen value for two String literals" do
+ o1 = "hello"
+ o2 = "hello"
+ o1.send(@method).should_not == o2.send(@method)
+ o1.frozen?.should == true
+ o2.frozen?.should == true
+ end
+ end
+
+ guard -> { !"test".frozen? } do
+ it "returns a different value for two String literals" do
+ o1 = "hello"
+ o2 = "hello"
+ o1.send(@method).should_not == o2.send(@method)
+ end
end
it "returns a different value for an object and its dup" do
diff --git a/spec/ruby/shared/kernel/raise.rb b/spec/ruby/shared/kernel/raise.rb
index 82fb0333c8..1917a4c923 100644
--- a/spec/ruby/shared/kernel/raise.rb
+++ b/spec/ruby/shared/kernel/raise.rb
@@ -146,4 +146,15 @@ describe :kernel_raise, shared: true do
@object.raise(ArgumentError, "message", caller)
end.should raise_error(ArgumentError, "message")
end
+
+ ruby_version_is "3.4" do
+ locations = caller_locations(1, 2)
+ it "allows Exception, message, and backtrace_locations parameters" do
+ -> do
+ @object.raise(ArgumentError, "message", locations)
+ end.should raise_error(ArgumentError, "message") { |error|
+ error.backtrace_locations.map(&:to_s).should == locations.map(&:to_s)
+ }
+ end
+ end
end
diff --git a/spec/ruby/shared/queue/deque.rb b/spec/ruby/shared/queue/deque.rb
index 616e56ec8a..0abba5301e 100644
--- a/spec/ruby/shared/queue/deque.rb
+++ b/spec/ruby/shared/queue/deque.rb
@@ -37,6 +37,15 @@ describe :queue_deq, shared: true do
q.send(@method).should == 1
end
+ it "converts false-ish for non_blocking to boolean" do
+ q = @object.call
+ q << 1
+ q << 2
+
+ q.send(@method, false).should == 1
+ q.send(@method, nil).should == 2
+ end
+
it "returns nil for a closed empty queue" do
q = @object.call
q.close
@@ -61,7 +70,7 @@ describe :queue_deq, shared: true do
q = @object.call
t = Thread.new {
- q.send(@method, timeout: 1).should == 1
+ q.send(@method, timeout: TIME_TOLERANCE).should == 1
}
Thread.pass until t.status == "sleep" && q.num_waiting == 1
q << 1
@@ -71,10 +80,9 @@ describe :queue_deq, shared: true do
it "returns nil if no item is available in time" do
q = @object.call
- t = Thread.new {
- q.send(@method, timeout: 0.1).should == nil
- }
- t.join
+ Thread.new {
+ q.send(@method, timeout: 0.001).should == nil
+ }.join
end
it "does nothing if the timeout is nil" do
@@ -82,7 +90,7 @@ describe :queue_deq, shared: true do
t = Thread.new {
q.send(@method, timeout: nil).should == 1
}
- t.join(0.2).should == nil
+ Thread.pass until t.status == "sleep" && q.num_waiting == 1
q << 1
t.join
end
@@ -96,23 +104,26 @@ describe :queue_deq, shared: true do
it "raise TypeError if timeout is not a valid numeric" do
q = @object.call
- -> { q.send(@method, timeout: "1") }.should raise_error(
- TypeError,
- "no implicit conversion to float from string",
- )
-
- -> { q.send(@method, timeout: false) }.should raise_error(
- TypeError,
- "no implicit conversion to float from false",
- )
+ -> {
+ q.send(@method, timeout: "1")
+ }.should raise_error(TypeError, "no implicit conversion to float from string")
+
+ -> {
+ q.send(@method, timeout: false)
+ }.should raise_error(TypeError, "no implicit conversion to float from false")
end
it "raise ArgumentError if non_block = true is passed too" do
q = @object.call
- -> { q.send(@method, true, timeout: 1) }.should raise_error(
- ArgumentError,
- "can't set a timeout if non_block is enabled",
- )
+ -> {
+ q.send(@method, true, timeout: 1)
+ }.should raise_error(ArgumentError, "can't set a timeout if non_block is enabled")
+ end
+
+ it "returns nil for a closed empty queue" do
+ q = @object.call
+ q.close
+ q.send(@method, timeout: 0).should == nil
end
end
end
@@ -143,5 +154,13 @@ describe :queue_deq, shared: true do
q.close
-> { q.send(@method, true) }.should raise_error(ThreadError)
end
+
+ it "converts true-ish non_blocking argument to true" do
+ q = @object.call
+
+ -> { q.send(@method, true) }.should raise_error(ThreadError)
+ -> { q.send(@method, 1) }.should raise_error(ThreadError)
+ -> { q.send(@method, "") }.should raise_error(ThreadError)
+ end
end
end
diff --git a/spec/ruby/shared/rational/coerce.rb b/spec/ruby/shared/rational/coerce.rb
deleted file mode 100644
index ccc8901ba0..0000000000
--- a/spec/ruby/shared/rational/coerce.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-require_relative '../../spec_helper'
-
-require 'bigdecimal'
-
-describe :rational_coerce, shared: true do
- it "returns the passed argument, self as Float, when given a Float" do
- result = Rational(3, 4).coerce(1.0)
- result.should == [1.0, 0.75]
- result.first.is_a?(Float).should be_true
- result.last.is_a?(Float).should be_true
- end
-
- it "returns the passed argument, self as Rational, when given an Integer" do
- result = Rational(3, 4).coerce(10)
- result.should == [Rational(10, 1), Rational(3, 4)]
- result.first.is_a?(Rational).should be_true
- result.last.is_a?(Rational).should be_true
- end
-
- it "coerces to Rational, when given a Complex" do
- Rational(3, 4).coerce(Complex(5)).should == [Rational(5, 1), Rational(3, 4)]
- Rational(12, 4).coerce(Complex(5, 1)).should == [Complex(5, 1), Complex(3)]
- end
-
- it "returns [argument, self] when given a Rational" do
- Rational(3, 7).coerce(Rational(9, 2)).should == [Rational(9, 2), Rational(3, 7)]
- end
-
- it "raises an error when passed a BigDecimal" do
- -> {
- Rational(500, 3).coerce(BigDecimal('166.666666666'))
- }.should raise_error(TypeError, /BigDecimal can't be coerced into Rational/)
- end
-end
diff --git a/spec/ruby/shared/rational/marshal_dump.rb b/spec/ruby/shared/rational/marshal_dump.rb
deleted file mode 100644
index 09782b45a5..0000000000
--- a/spec/ruby/shared/rational/marshal_dump.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require_relative '../../spec_helper'
-
-describe :rational_marshal_dump, shared: true do
- it "needs to be reviewed for spec completeness"
-end
diff --git a/spec/ruby/shared/rational/marshal_load.rb b/spec/ruby/shared/rational/marshal_load.rb
deleted file mode 100644
index 20bdd6fdf4..0000000000
--- a/spec/ruby/shared/rational/marshal_load.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require_relative '../../spec_helper'
-
-describe :rational_marshal_load, shared: true do
- it "needs to be reviewed for spec completeness"
-end
diff --git a/spec/ruby/shared/rational/quo.rb b/spec/ruby/shared/rational/quo.rb
deleted file mode 100644
index 53b32fed2f..0000000000
--- a/spec/ruby/shared/rational/quo.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require_relative '../../spec_helper'
-
-describe :rational_quo, shared: true do
- it "needs to be reviewed for spec completeness"
-end
diff --git a/spec/ruby/shared/sizedqueue/enque.rb b/spec/ruby/shared/sizedqueue/enque.rb
index 059f1025a7..2f25517675 100644
--- a/spec/ruby/shared/sizedqueue/enque.rb
+++ b/spec/ruby/shared/sizedqueue/enque.rb
@@ -37,7 +37,7 @@ describe :sizedqueue_enq, shared: true do
q << 1
t = Thread.new {
- -> { q.send(@method, 2) }.should raise_error(ClosedQueueError)
+ -> { q.send(@method, 2) }.should raise_error(ClosedQueueError, "queue closed")
}
Thread.pass until q.num_waiting == 1
@@ -55,7 +55,7 @@ describe :sizedqueue_enq, shared: true do
q << 1
t = Thread.new {
- q.send(@method, 2, timeout: 1).should == q
+ q.send(@method, 2, timeout: TIME_TOLERANCE).should == q
}
Thread.pass until t.status == "sleep" && q.num_waiting == 1
q.pop
@@ -73,7 +73,7 @@ describe :sizedqueue_enq, shared: true do
t.join
end
- it "returns nil if no space is avialable and timeout is 0" do
+ it "returns nil if no space is available and timeout is 0" do
q = @object.call(1)
q.send(@method, 1, timeout: 0).should == q
q.send(@method, 2, timeout: 0).should == nil
@@ -82,31 +82,49 @@ describe :sizedqueue_enq, shared: true do
it "returns nil if no space is available in time" do
q = @object.call(1)
q << 1
- t = Thread.new {
- q.send(@method, 2, timeout: 0.1).should == nil
- }
- t.join
+ Thread.new {
+ q.send(@method, 2, timeout: 0.001).should == nil
+ }.join
end
it "raise TypeError if timeout is not a valid numeric" do
q = @object.call(1)
- -> { q.send(@method, 2, timeout: "1") }.should raise_error(
- TypeError,
- "no implicit conversion to float from string",
- )
-
- -> { q.send(@method, 2, timeout: false) }.should raise_error(
- TypeError,
- "no implicit conversion to float from false",
- )
+ -> {
+ q.send(@method, 2, timeout: "1")
+ }.should raise_error(TypeError, "no implicit conversion to float from string")
+
+ -> {
+ q.send(@method, 2, timeout: false)
+ }.should raise_error(TypeError, "no implicit conversion to float from false")
end
it "raise ArgumentError if non_block = true is passed too" do
q = @object.call(1)
- -> { q.send(@method, 2, true, timeout: 1) }.should raise_error(
- ArgumentError,
- "can't set a timeout if non_block is enabled",
- )
+ -> {
+ q.send(@method, 2, true, timeout: 1)
+ }.should raise_error(ArgumentError, "can't set a timeout if non_block is enabled")
+ end
+
+ it "raise ClosedQueueError when closed before enqueued" do
+ q = @object.call(1)
+ q.close
+ -> { q.send(@method, 2, timeout: 1) }.should raise_error(ClosedQueueError, "queue closed")
+ end
+
+ it "interrupts enqueuing threads with ClosedQueueError when the queue is closed" do
+ q = @object.call(1)
+ q << 1
+
+ t = Thread.new {
+ -> { q.send(@method, 1, timeout: TIME_TOLERANCE) }.should raise_error(ClosedQueueError, "queue closed")
+ }
+
+ Thread.pass until q.num_waiting == 1
+
+ q.close
+
+ t.join
+ q.pop.should == 1
end
end
end
diff --git a/spec/ruby/shared/string/end_with.rb b/spec/ruby/shared/string/end_with.rb
index 0e4c1386e8..94a7f97513 100644
--- a/spec/ruby/shared/string/end_with.rb
+++ b/spec/ruby/shared/string/end_with.rb
@@ -55,7 +55,7 @@ describe :end_with, shared: true do
it "checks that we are starting to match at the head of a character" do
"\xC3\xA9".send(@method).should_not.end_with?("\xA9")
"\xe3\x81\x82".send(@method).should_not.end_with?("\x82")
- "ab".force_encoding("UTF-16BE").send(@method).should_not.end_with?(
- "b".force_encoding("UTF-16BE"))
+ "ab".dup.force_encoding("UTF-16BE").send(@method).should_not.end_with?(
+ "b".dup.force_encoding("UTF-16BE"))
end
end
diff --git a/spec/ruby/shared/string/start_with.rb b/spec/ruby/shared/string/start_with.rb
index 6932a017b6..4b947a3bbf 100644
--- a/spec/ruby/shared/string/start_with.rb
+++ b/spec/ruby/shared/string/start_with.rb
@@ -70,7 +70,15 @@ describe :start_with, shared: true do
$1.should be_nil
end
- it "does not check that we are not matching part of a character" do
- "\xC3\xA9".send(@method).should.start_with?("\xC3")
+ ruby_version_is ""..."3.3" do
+ it "does not check that we are not matching part of a character" do
+ "\xC3\xA9".send(@method).should.start_with?("\xC3")
+ end
+ end
+
+ ruby_version_is "3.3" do # #19784
+ it "checks that we are not matching part of a character" do
+ "\xC3\xA9".send(@method).should_not.start_with?("\xC3")
+ end
end
end
diff --git a/spec/ruby/shared/string/times.rb b/spec/ruby/shared/string/times.rb
index 8ca9507570..aaf748bad9 100644
--- a/spec/ruby/shared/string/times.rb
+++ b/spec/ruby/shared/string/times.rb
@@ -32,24 +32,14 @@ describe :string_times, shared: true do
@object.call("", max_long).should == ""
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances" do
- @object.call(MyString.new("cool"), 0).should be_an_instance_of(MyString)
- @object.call(MyString.new("cool"), 1).should be_an_instance_of(MyString)
- @object.call(MyString.new("cool"), 2).should be_an_instance_of(MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns String instances" do
- @object.call(MyString.new("cool"), 0).should be_an_instance_of(String)
- @object.call(MyString.new("cool"), 1).should be_an_instance_of(String)
- @object.call(MyString.new("cool"), 2).should be_an_instance_of(String)
- end
+ it "returns String instances" do
+ @object.call(MyString.new("cool"), 0).should be_an_instance_of(String)
+ @object.call(MyString.new("cool"), 1).should be_an_instance_of(String)
+ @object.call(MyString.new("cool"), 2).should be_an_instance_of(String)
end
it "returns a String in the same encoding as self" do
- str = "\xE3\x81\x82".force_encoding Encoding::UTF_8
+ str = "\xE3\x81\x82".dup.force_encoding Encoding::UTF_8
result = @object.call(str, 2)
result.encoding.should equal(Encoding::UTF_8)
end
diff --git a/spec/ruby/shared/types/rb_num2dbl_fails.rb b/spec/ruby/shared/types/rb_num2dbl_fails.rb
new file mode 100644
index 0000000000..ec7cc11986
--- /dev/null
+++ b/spec/ruby/shared/types/rb_num2dbl_fails.rb
@@ -0,0 +1,17 @@
+#
+# Shared tests for rb_num2dbl related conversion failures.
+#
+# Usage example:
+# it_behaves_like :rb_num2dbl_fails, nil, -> v { o = A.new; o.foo(v) }
+#
+
+describe :rb_num2dbl_fails, shared: true do
+ it "fails if string is provided" do
+ -> { @object.call("123") }.should raise_error(TypeError, "no implicit conversion to float from string")
+ end
+
+ it "fails if boolean is provided" do
+ -> { @object.call(true) }.should raise_error(TypeError, "no implicit conversion to float from true")
+ -> { @object.call(false) }.should raise_error(TypeError, "no implicit conversion to float from false")
+ end
+end
diff --git a/spec/ruby/spec_helper.rb b/spec/ruby/spec_helper.rb
index 3404521c03..af1c385878 100644
--- a/spec/ruby/spec_helper.rb
+++ b/spec/ruby/spec_helper.rb
@@ -1,5 +1,5 @@
use_realpath = File.respond_to?(:realpath)
-root = File.dirname(__FILE__)
+root = __dir__
dir = "fixtures/code"
CODE_LOADING_DIR = use_realpath ? File.realpath(dir, root) : File.expand_path(dir, root)
diff --git a/spec/syntax_suggest/integration/ruby_command_line_spec.rb b/spec/syntax_suggest/integration/ruby_command_line_spec.rb
index b41a4c86e3..c1ec4be54e 100644
--- a/spec/syntax_suggest/integration/ruby_command_line_spec.rb
+++ b/spec/syntax_suggest/integration/ruby_command_line_spec.rb
@@ -9,7 +9,7 @@ module SyntaxSuggest
Dir.mktmpdir do |dir|
tmpdir = Pathname(dir)
script = tmpdir.join("script.rb")
- script.write <<~'EOM'
+ script.write <<~EOM
puts Kernel.private_methods
EOM
@@ -159,7 +159,7 @@ module SyntaxSuggest
Dir.mktmpdir do |dir|
tmpdir = Pathname(dir)
script = tmpdir.join("script.rb")
- script.write <<~'EOM'
+ script.write <<~EOM
$stderr = STDOUT
eval("def lol")
EOM
@@ -167,7 +167,7 @@ module SyntaxSuggest
out = `#{ruby} -I#{lib_dir} -rsyntax_suggest #{script} 2>&1`
expect($?.success?).to be_falsey
- expect(out).to include("(eval):1")
+ expect(out).to match(/\(eval.*\):1/)
expect(out).to_not include("SyntaxSuggest")
expect(out).to_not include("Could not find filename")
@@ -178,7 +178,7 @@ module SyntaxSuggest
Dir.mktmpdir do |dir|
tmpdir = Pathname(dir)
script = tmpdir.join("script.rb")
- script.write <<~'EOM'
+ script.write <<~EOM
break
EOM
diff --git a/spec/syntax_suggest/integration/syntax_suggest_spec.rb b/spec/syntax_suggest/integration/syntax_suggest_spec.rb
index 64dafabcdd..9071d37c1b 100644
--- a/spec/syntax_suggest/integration/syntax_suggest_spec.rb
+++ b/spec/syntax_suggest/integration/syntax_suggest_spec.rb
@@ -26,7 +26,7 @@ module SyntaxSuggest
debug_display(io.string)
debug_display(benchmark)
- expect(io.string).to include(<<~'EOM')
+ expect(io.string).to include(<<~EOM)
6 class SyntaxTree < Ripper
170 def self.parse(source)
174 end
@@ -54,7 +54,7 @@ module SyntaxSuggest
end
expect(io.string).to_not include("def ruby_install_binstub_path")
- expect(io.string).to include(<<~'EOM')
+ expect(io.string).to include(<<~EOM)
> 1067 def add_yarn_binary
> 1068 return [] if yarn_preinstalled?
> 1069 |
@@ -72,7 +72,7 @@ module SyntaxSuggest
)
debug_display(io.string)
- expect(io.string).to include(<<~'EOM')
+ expect(io.string).to include(<<~EOM)
1 Rails.application.routes.draw do
> 113 namespace :admin do
> 116 match "/foobar(*path)", via: :all, to: redirect { |_params, req|
@@ -91,7 +91,7 @@ module SyntaxSuggest
)
debug_display(io.string)
- expect(io.string).to include(<<~'EOM')
+ expect(io.string).to include(<<~EOM)
1 describe "webmock tests" do
22 it "body" do
27 query = Cutlass::FunctionQuery.new(
@@ -113,7 +113,7 @@ module SyntaxSuggest
)
debug_display(io.string)
- expect(io.string).to include(<<~'EOM')
+ expect(io.string).to include(<<~EOM)
5 module DerailedBenchmarks
6 class RequireTree
> 13 def initialize(name)
@@ -166,7 +166,7 @@ module SyntaxSuggest
end
it "ambiguous end" do
- source = <<~'EOM'
+ source = <<~EOM
def call # 0
print "lol" # 1
end # one # 2
@@ -186,7 +186,7 @@ module SyntaxSuggest
end
it "simple regression" do
- source = <<~'EOM'
+ source = <<~EOM
class Dog
def bark
puts "woof"
@@ -206,7 +206,7 @@ module SyntaxSuggest
end
it "empty else" do
- source = <<~'EOM'
+ source = <<~EOM
class Foo
def foo
if cond?
diff --git a/spec/syntax_suggest/unit/api_spec.rb b/spec/syntax_suggest/unit/api_spec.rb
index 079a91e46d..e900b9e10b 100644
--- a/spec/syntax_suggest/unit/api_spec.rb
+++ b/spec/syntax_suggest/unit/api_spec.rb
@@ -8,6 +8,12 @@ end
module SyntaxSuggest
RSpec.describe "Top level SyntaxSuggest api" do
+ it "doesn't load prism if env var is set" do
+ skip("SYNTAX_SUGGEST_DISABLE_PRISM not set") unless ENV["SYNTAX_SUGGEST_DISABLE_PRISM"]
+
+ expect(SyntaxSuggest.use_prism_parser?).to be_falsey
+ end
+
it "has a `handle_error` interface" do
fake_error = Object.new
def fake_error.message
diff --git a/spec/syntax_suggest/unit/around_block_scan_spec.rb b/spec/syntax_suggest/unit/around_block_scan_spec.rb
index d6756448bd..6c940a5919 100644
--- a/spec/syntax_suggest/unit/around_block_scan_spec.rb
+++ b/spec/syntax_suggest/unit/around_block_scan_spec.rb
@@ -5,7 +5,7 @@ require_relative "../spec_helper"
module SyntaxSuggest
RSpec.describe AroundBlockScan do
it "continues scan from last location even if scan is false" do
- source = <<~'EOM'
+ source = <<~EOM
print 'omg'
print 'lol'
print 'haha'
diff --git a/spec/syntax_suggest/unit/block_expand_spec.rb b/spec/syntax_suggest/unit/block_expand_spec.rb
index 5cff73621d..fde0360775 100644
--- a/spec/syntax_suggest/unit/block_expand_spec.rb
+++ b/spec/syntax_suggest/unit/block_expand_spec.rb
@@ -146,7 +146,7 @@ module SyntaxSuggest
EOM
end
- it "expand until next boundry (indentation)" do
+ it "expand until next boundary (indentation)" do
source_string = <<~EOM
describe "what" do
Foo.call
@@ -188,7 +188,7 @@ module SyntaxSuggest
EOM
end
- it "expand until next boundry (empty lines)" do
+ it "expand until next boundary (empty lines)" do
source_string = <<~EOM
describe "what" do
end
diff --git a/spec/syntax_suggest/unit/capture/before_after_keyword_ends_spec.rb b/spec/syntax_suggest/unit/capture/before_after_keyword_ends_spec.rb
index 02d9be4387..09f8d90d33 100644
--- a/spec/syntax_suggest/unit/capture/before_after_keyword_ends_spec.rb
+++ b/spec/syntax_suggest/unit/capture/before_after_keyword_ends_spec.rb
@@ -5,7 +5,7 @@ require_relative "../../spec_helper"
module SyntaxSuggest
RSpec.describe Capture::BeforeAfterKeywordEnds do
it "before after keyword ends" do
- source = <<~'EOM'
+ source = <<~EOM
def nope
print 'not me'
end
@@ -36,7 +36,7 @@ module SyntaxSuggest
).call
lines.sort!
- expect(lines.join).to include(<<~'EOM')
+ expect(lines.join).to include(<<~EOM)
def lol
end
def yolo
diff --git a/spec/syntax_suggest/unit/capture/falling_indent_lines_spec.rb b/spec/syntax_suggest/unit/capture/falling_indent_lines_spec.rb
index 61d1642d97..ed2265539a 100644
--- a/spec/syntax_suggest/unit/capture/falling_indent_lines_spec.rb
+++ b/spec/syntax_suggest/unit/capture/falling_indent_lines_spec.rb
@@ -5,7 +5,7 @@ require_relative "../../spec_helper"
module SyntaxSuggest
RSpec.describe Capture::FallingIndentLines do
it "on_falling_indent" do
- source = <<~'EOM'
+ source = <<~EOM
class OH
def lol
print 'lol
@@ -33,7 +33,7 @@ module SyntaxSuggest
end
lines.sort!
- expect(lines.join).to eq(<<~'EOM')
+ expect(lines.join).to eq(<<~EOM)
class OH
def hello
end
diff --git a/spec/syntax_suggest/unit/capture_code_context_spec.rb b/spec/syntax_suggest/unit/capture_code_context_spec.rb
index 46f13e8961..d9379d0ce7 100644
--- a/spec/syntax_suggest/unit/capture_code_context_spec.rb
+++ b/spec/syntax_suggest/unit/capture_code_context_spec.rb
@@ -5,7 +5,7 @@ require_relative "../spec_helper"
module SyntaxSuggest
RSpec.describe CaptureCodeContext do
it "capture_before_after_kws two" do
- source = <<~'EOM'
+ source = <<~EOM
class OH
def hello
@@ -23,7 +23,7 @@ module SyntaxSuggest
code_lines: code_lines
)
display.capture_before_after_kws(block)
- expect(display.sorted_lines.join).to eq(<<~'EOM'.indent(2))
+ expect(display.sorted_lines.join).to eq(<<~EOM.indent(2))
def hello
def hai
end
@@ -31,7 +31,7 @@ module SyntaxSuggest
end
it "capture_before_after_kws" do
- source = <<~'EOM'
+ source = <<~EOM
def sit
end
@@ -50,7 +50,7 @@ module SyntaxSuggest
)
lines = display.capture_before_after_kws(block).sort
- expect(lines.join).to eq(<<~'EOM')
+ expect(lines.join).to eq(<<~EOM)
def sit
end
def bark
@@ -60,7 +60,7 @@ module SyntaxSuggest
end
it "handles ambiguous end" do
- source = <<~'EOM'
+ source = <<~EOM
def call # 0
print "lol" # 1
end # one # 2
@@ -79,7 +79,7 @@ module SyntaxSuggest
lines = lines.sort.map(&:original)
- expect(lines.join).to eq(<<~'EOM')
+ expect(lines.join).to eq(<<~EOM)
def call # 0
end # one # 2
end # two # 3
@@ -94,7 +94,7 @@ module SyntaxSuggest
code_lines = CleanDocument.new(source: source).call.lines
code_lines[0..75].each(&:mark_invisible)
- code_lines[77..-1].each(&:mark_invisible)
+ code_lines[77..].each(&:mark_invisible)
expect(code_lines.join.strip).to eq("class Lookups")
block = CodeBlock.new(lines: code_lines[76..149])
@@ -106,7 +106,7 @@ module SyntaxSuggest
lines = display.call
lines = lines.sort.map(&:original)
- expect(lines.join).to include(<<~'EOM'.indent(2))
+ expect(lines.join).to include(<<~EOM.indent(2))
class Lookups
def format_requires
end
@@ -114,7 +114,7 @@ module SyntaxSuggest
end
it "shows ends of captured block" do
- source = <<~'EOM'
+ source = <<~EOM
class Dog
def bark
puts "woof"
@@ -123,7 +123,7 @@ module SyntaxSuggest
code_lines = CleanDocument.new(source: source).call.lines
block = CodeBlock.new(lines: code_lines)
- code_lines[1..-1].each(&:mark_invisible)
+ code_lines[1..].each(&:mark_invisible)
expect(block.to_s.strip).to eq("class Dog")
@@ -132,7 +132,7 @@ module SyntaxSuggest
code_lines: code_lines
)
lines = display.call.sort.map(&:original)
- expect(lines.join).to eq(<<~'EOM')
+ expect(lines.join).to eq(<<~EOM)
class Dog
def bark
end
@@ -140,7 +140,7 @@ module SyntaxSuggest
end
it "captures surrounding context on falling indent" do
- source = <<~'EOM'
+ source = <<~EOM
class Blerg
end
@@ -164,7 +164,7 @@ module SyntaxSuggest
code_lines: code_lines
)
lines = display.call.sort.map(&:original)
- expect(lines.join).to eq(<<~'EOM')
+ expect(lines.join).to eq(<<~EOM)
class OH
def hello
it "foo" do
@@ -174,7 +174,7 @@ module SyntaxSuggest
end
it "captures surrounding context on same indent" do
- source = <<~'EOM'
+ source = <<~EOM
class Blerg
end
class OH
@@ -200,7 +200,7 @@ module SyntaxSuggest
code_lines = CleanDocument.new(source: source).call.lines
block = CodeBlock.new(lines: code_lines[7..10])
- expect(block.to_s).to eq(<<~'EOM'.indent(2))
+ expect(block.to_s).to eq(<<~EOM.indent(2))
def lol
end
@@ -217,7 +217,7 @@ module SyntaxSuggest
lines: lines
).call
- expect(out).to eq(<<~'EOM'.indent(2))
+ expect(out).to eq(<<~EOM.indent(2))
3 class OH
8 def lol
9 end
diff --git a/spec/syntax_suggest/unit/clean_document_spec.rb b/spec/syntax_suggest/unit/clean_document_spec.rb
index 25a62e4454..5b5ca04cfd 100644
--- a/spec/syntax_suggest/unit/clean_document_spec.rb
+++ b/spec/syntax_suggest/unit/clean_document_spec.rb
@@ -8,7 +8,7 @@ module SyntaxSuggest
source = fixtures_dir.join("this_project_extra_def.rb.txt").read
code_lines = CleanDocument.new(source: source).call.lines
- expect(code_lines[18 - 1].to_s).to eq(<<-'EOL')
+ expect(code_lines[18 - 1].to_s).to eq(<<-EOL)
@io.puts <<~EOM
SyntaxSuggest: A syntax error was detected
@@ -54,7 +54,7 @@ module SyntaxSuggest
DisplayCodeWithLineNumbers.new(
lines: lines
).call
- ).to eq(<<~'EOM'.indent(2))
+ ).to eq(<<~EOM.indent(2))
1 User
2 .where(name: 'schneems')
3 .first
@@ -65,7 +65,7 @@ module SyntaxSuggest
lines: lines,
highlight_lines: lines[0]
).call
- ).to eq(<<~'EOM')
+ ).to eq(<<~EOM)
> 1 User
> 2 .where(name: 'schneems')
> 3 .first
@@ -85,7 +85,7 @@ module SyntaxSuggest
code_lines = doc.lines
expect(code_lines[0].to_s.count($/)).to eq(5)
- code_lines[1..-1].each do |line|
+ code_lines[1..].each do |line|
expect(line.to_s.strip.length).to eq(0)
end
end
@@ -139,7 +139,7 @@ module SyntaxSuggest
source = <<~'EOM'
context "timezones workaround" do
it "should receive a time in UTC format and return the time with the"\
- "office's UTC offset substracted from it" do
+ "office's UTC offset subtracted from it" do
travel_to DateTime.new(2020, 10, 1, 10, 0, 0) do
office = build(:office)
end
@@ -155,7 +155,7 @@ module SyntaxSuggest
).to eq(<<~'EOM'.indent(2))
1 context "timezones workaround" do
2 it "should receive a time in UTC format and return the time with the"\
- 3 "office's UTC offset substracted from it" do
+ 3 "office's UTC offset subtracted from it" do
4 travel_to DateTime.new(2020, 10, 1, 10, 0, 0) do
5 office = build(:office)
6 end
@@ -171,7 +171,7 @@ module SyntaxSuggest
).to eq(<<~'EOM')
1 context "timezones workaround" do
> 2 it "should receive a time in UTC format and return the time with the"\
- > 3 "office's UTC offset substracted from it" do
+ > 3 "office's UTC offset subtracted from it" do
4 travel_to DateTime.new(2020, 10, 1, 10, 0, 0) do
5 office = build(:office)
6 end
diff --git a/spec/syntax_suggest/unit/code_line_spec.rb b/spec/syntax_suggest/unit/code_line_spec.rb
index d5b568fd19..5b62cc2757 100644
--- a/spec/syntax_suggest/unit/code_line_spec.rb
+++ b/spec/syntax_suggest/unit/code_line_spec.rb
@@ -5,7 +5,7 @@ require_relative "../spec_helper"
module SyntaxSuggest
RSpec.describe CodeLine do
it "bug in keyword detection" do
- lines = CodeLine.from_source(<<~'EOM')
+ lines = CodeLine.from_source(<<~EOM)
def to_json(*opts)
{
type: :module,
@@ -19,7 +19,7 @@ module SyntaxSuggest
it "supports endless method definitions" do
skip("Unsupported ruby version") unless Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3")
- line = CodeLine.from_source(<<~'EOM').first
+ line = CodeLine.from_source(<<~EOM).first
def square(x) = x * x
EOM
@@ -28,7 +28,7 @@ module SyntaxSuggest
end
it "retains original line value, after being marked invisible" do
- line = CodeLine.from_source(<<~'EOM').first
+ line = CodeLine.from_source(<<~EOM).first
puts "lol"
EOM
expect(line.line).to match('puts "lol"')
@@ -38,7 +38,7 @@ module SyntaxSuggest
end
it "knows which lines can be joined" do
- code_lines = CodeLine.from_source(<<~'EOM')
+ code_lines = CodeLine.from_source(<<~EOM)
user = User.
where(name: 'schneems').
first
@@ -50,7 +50,7 @@ module SyntaxSuggest
end
it "trailing if" do
- code_lines = CodeLine.from_source(<<~'EOM')
+ code_lines = CodeLine.from_source(<<~EOM)
puts "lol" if foo
if foo
end
@@ -60,7 +60,7 @@ module SyntaxSuggest
end
it "trailing unless" do
- code_lines = CodeLine.from_source(<<~'EOM')
+ code_lines = CodeLine.from_source(<<~EOM)
puts "lol" unless foo
unless foo
end
diff --git a/spec/syntax_suggest/unit/code_search_spec.rb b/spec/syntax_suggest/unit/code_search_spec.rb
index f836ba36f3..502de14d7f 100644
--- a/spec/syntax_suggest/unit/code_search_spec.rb
+++ b/spec/syntax_suggest/unit/code_search_spec.rb
@@ -12,13 +12,13 @@ module SyntaxSuggest
search = CodeSearch.new(source)
search.call
- expect(search.invalid_blocks.join.strip).to eq(<<~'EOM'.strip)
+ expect(search.invalid_blocks.join.strip).to eq(<<~EOM.strip)
class Lookups
EOM
end
it "squished do regression" do
- source = <<~'EOM'
+ source = <<~EOM
def call
trydo
@@ -47,14 +47,14 @@ module SyntaxSuggest
search = CodeSearch.new(source)
search.call
- expect(search.invalid_blocks.join).to eq(<<~'EOM'.indent(2))
+ expect(search.invalid_blocks.join).to eq(<<~EOM.indent(2))
trydo
end # one
EOM
end
it "regression test ambiguous end" do
- source = <<~'EOM'
+ source = <<~EOM
def call # 0
print "lol" # 1
end # one # 2
@@ -64,13 +64,13 @@ module SyntaxSuggest
search = CodeSearch.new(source)
search.call
- expect(search.invalid_blocks.join).to eq(<<~'EOM')
+ expect(search.invalid_blocks.join).to eq(<<~EOM)
end # two # 3
EOM
end
it "regression dog test" do
- source = <<~'EOM'
+ source = <<~EOM
class Dog
def bark
puts "woof"
@@ -79,7 +79,7 @@ module SyntaxSuggest
search = CodeSearch.new(source)
search.call
- expect(search.invalid_blocks.join).to eq(<<~'EOM')
+ expect(search.invalid_blocks.join).to eq(<<~EOM)
class Dog
EOM
expect(search.invalid_blocks.first.lines.length).to eq(4)
@@ -99,7 +99,7 @@ module SyntaxSuggest
search = CodeSearch.new(source)
search.call
- expect(search.invalid_blocks.join).to eq(<<~'EOM'.indent(2))
+ expect(search.invalid_blocks.join).to eq(<<~EOM.indent(2))
Foo.call do |a
end # one
EOM
@@ -118,7 +118,7 @@ module SyntaxSuggest
search = CodeSearch.new(source)
search.call
- expect(search.invalid_blocks.join).to eq(<<~'EOM'.indent(2))
+ expect(search.invalid_blocks.join).to eq(<<~EOM.indent(2))
Foo.call do {
EOM
end
@@ -152,7 +152,7 @@ module SyntaxSuggest
end
it "handles no spaces between blocks" do
- source = <<~'EOM'
+ source = <<~EOM
context "foo bar" do
it "bars the foo" do
travel_to DateTime.new(2020, 10, 1, 10, 0, 0) do
@@ -172,7 +172,7 @@ module SyntaxSuggest
it "records debugging steps to a directory" do
Dir.mktmpdir do |dir|
dir = Pathname(dir)
- search = CodeSearch.new(<<~'EOM', record_dir: dir)
+ search = CodeSearch.new(<<~EOM, record_dir: dir)
class OH
def hello
def hai
@@ -193,7 +193,7 @@ module SyntaxSuggest
end
it "def with missing end" do
- search = CodeSearch.new(<<~'EOM')
+ search = CodeSearch.new(<<~EOM)
class OH
def hello
@@ -206,7 +206,7 @@ module SyntaxSuggest
expect(search.invalid_blocks.join.strip).to eq("def hello")
- search = CodeSearch.new(<<~'EOM')
+ search = CodeSearch.new(<<~EOM)
class OH
def hello
@@ -218,7 +218,7 @@ module SyntaxSuggest
expect(search.invalid_blocks.join.strip).to eq("def hello")
- search = CodeSearch.new(<<~'EOM')
+ search = CodeSearch.new(<<~EOM)
class OH
def hello
def hai
@@ -227,7 +227,7 @@ module SyntaxSuggest
EOM
search.call
- expect(search.invalid_blocks.join).to eq(<<~'EOM'.indent(2))
+ expect(search.invalid_blocks.join).to eq(<<~EOM.indent(2))
def hello
EOM
end
@@ -244,13 +244,13 @@ module SyntaxSuggest
highlight_lines: search.invalid_blocks.flat_map(&:lines)
).call
- expect(document).to include(<<~'EOM')
+ expect(document).to include(<<~EOM)
> 36 def filename
EOM
end
it "Format Code blocks real world example" do
- search = CodeSearch.new(<<~'EOM')
+ search = CodeSearch.new(<<~EOM)
require 'rails_helper'
RSpec.describe AclassNameHere, type: :worker do
@@ -291,7 +291,7 @@ module SyntaxSuggest
highlight_lines: search.invalid_blocks.flat_map(&:lines)
).call
- expect(document).to include(<<~'EOM')
+ expect(document).to include(<<~EOM)
1 require 'rails_helper'
2
3 RSpec.describe AclassNameHere, type: :worker do
@@ -308,7 +308,7 @@ module SyntaxSuggest
describe "needs improvement" do
describe "mis-matched-indentation" do
it "extra space before end" do
- search = CodeSearch.new(<<~'EOM')
+ search = CodeSearch.new(<<~EOM)
Foo.call
def foo
puts "lol"
@@ -318,14 +318,14 @@ module SyntaxSuggest
EOM
search.call
- expect(search.invalid_blocks.join).to eq(<<~'EOM')
+ expect(search.invalid_blocks.join).to eq(<<~EOM)
Foo.call
end # two
EOM
end
it "stacked ends 2" do
- search = CodeSearch.new(<<~'EOM')
+ search = CodeSearch.new(<<~EOM)
def cat
blerg
end
@@ -339,7 +339,7 @@ module SyntaxSuggest
EOM
search.call
- expect(search.invalid_blocks.join).to eq(<<~'EOM')
+ expect(search.invalid_blocks.join).to eq(<<~EOM)
Foo.call do
end # one
end # two
@@ -348,7 +348,7 @@ module SyntaxSuggest
end
it "stacked ends " do
- search = CodeSearch.new(<<~'EOM')
+ search = CodeSearch.new(<<~EOM)
Foo.call
def foo
puts "lol"
@@ -358,14 +358,14 @@ module SyntaxSuggest
EOM
search.call
- expect(search.invalid_blocks.join).to eq(<<~'EOM')
+ expect(search.invalid_blocks.join).to eq(<<~EOM)
Foo.call
end
EOM
end
it "missing space before end" do
- search = CodeSearch.new(<<~'EOM')
+ search = CodeSearch.new(<<~EOM)
Foo.call
def foo
@@ -377,7 +377,7 @@ module SyntaxSuggest
search.call
# expand-1 and expand-2 seem to be broken?
- expect(search.invalid_blocks.join).to eq(<<~'EOM')
+ expect(search.invalid_blocks.join).to eq(<<~EOM)
Foo.call
end
EOM
@@ -386,7 +386,7 @@ module SyntaxSuggest
end
it "returns syntax error in outer block without inner block" do
- search = CodeSearch.new(<<~'EOM')
+ search = CodeSearch.new(<<~EOM)
Foo.call
def foo
puts "lol"
@@ -396,27 +396,27 @@ module SyntaxSuggest
EOM
search.call
- expect(search.invalid_blocks.join).to eq(<<~'EOM')
+ expect(search.invalid_blocks.join).to eq(<<~EOM)
Foo.call
end # two
EOM
end
it "doesn't just return an empty `end`" do
- search = CodeSearch.new(<<~'EOM')
+ search = CodeSearch.new(<<~EOM)
Foo.call
end
EOM
search.call
- expect(search.invalid_blocks.join).to eq(<<~'EOM')
+ expect(search.invalid_blocks.join).to eq(<<~EOM)
Foo.call
end
EOM
end
it "finds multiple syntax errors" do
- search = CodeSearch.new(<<~'EOM')
+ search = CodeSearch.new(<<~EOM)
describe "hi" do
Foo.call
end
@@ -429,7 +429,7 @@ module SyntaxSuggest
EOM
search.call
- expect(search.invalid_blocks.join).to eq(<<~'EOM'.indent(2))
+ expect(search.invalid_blocks.join).to eq(<<~EOM.indent(2))
Foo.call
end
Bar.call
@@ -438,47 +438,47 @@ module SyntaxSuggest
end
it "finds a typo def" do
- search = CodeSearch.new(<<~'EOM')
+ search = CodeSearch.new(<<~EOM)
defzfoo
puts "lol"
end
EOM
search.call
- expect(search.invalid_blocks.join).to eq(<<~'EOM')
+ expect(search.invalid_blocks.join).to eq(<<~EOM)
defzfoo
end
EOM
end
it "finds a mis-matched def" do
- search = CodeSearch.new(<<~'EOM')
+ search = CodeSearch.new(<<~EOM)
def foo
def blerg
end
EOM
search.call
- expect(search.invalid_blocks.join).to eq(<<~'EOM'.indent(2))
+ expect(search.invalid_blocks.join).to eq(<<~EOM.indent(2))
def blerg
EOM
end
it "finds a naked end" do
- search = CodeSearch.new(<<~'EOM')
+ search = CodeSearch.new(<<~EOM)
def foo
end # one
end # two
EOM
search.call
- expect(search.invalid_blocks.join).to eq(<<~'EOM'.indent(2))
+ expect(search.invalid_blocks.join).to eq(<<~EOM.indent(2))
end # one
EOM
end
it "returns when no invalid blocks are found" do
- search = CodeSearch.new(<<~'EOM')
+ search = CodeSearch.new(<<~EOM)
def foo
puts 'lol'
end
@@ -489,14 +489,14 @@ module SyntaxSuggest
end
it "expands frontier by eliminating valid lines" do
- search = CodeSearch.new(<<~'EOM')
+ search = CodeSearch.new(<<~EOM)
def foo
puts 'lol'
end
EOM
search.create_blocks_from_untracked_lines
- expect(search.code_lines.join).to eq(<<~'EOM')
+ expect(search.code_lines.join).to eq(<<~EOM)
def foo
end
EOM
diff --git a/spec/syntax_suggest/unit/core_ext_spec.rb b/spec/syntax_suggest/unit/core_ext_spec.rb
index 802d03ecc0..499c38a240 100644
--- a/spec/syntax_suggest/unit/core_ext_spec.rb
+++ b/spec/syntax_suggest/unit/core_ext_spec.rb
@@ -8,7 +8,7 @@ module SyntaxSuggest
Dir.mktmpdir do |dir|
tmpdir = Pathname(dir)
file = tmpdir.join("file.rb")
- file.write(<<~'EOM'.strip)
+ file.write(<<~EOM.strip)
print 'no newline
EOM
diff --git a/spec/syntax_suggest/unit/explain_syntax_spec.rb b/spec/syntax_suggest/unit/explain_syntax_spec.rb
index 394981dcf6..c62a42b925 100644
--- a/spec/syntax_suggest/unit/explain_syntax_spec.rb
+++ b/spec/syntax_suggest/unit/explain_syntax_spec.rb
@@ -14,7 +14,7 @@ module SyntaxSuggest
).call
expect(explain.missing).to eq([])
- expect(explain.errors.join).to include("unterminated string")
+ expect(explain.errors.join.strip).to_not be_empty
end
it "handles %w[]" do
@@ -191,7 +191,7 @@ module SyntaxSuggest
).call
expect(explain.missing).to eq([])
- expect(explain.errors).to eq(RipperErrors.new(source).call.errors)
+ expect(explain.errors).to eq(GetParseErrors.errors(source))
end
it "handles an unexpected rescue" do
diff --git a/spec/syntax_suggest/unit/lex_all_spec.rb b/spec/syntax_suggest/unit/lex_all_spec.rb
index 0c0df7cfaa..9621c9ecec 100644
--- a/spec/syntax_suggest/unit/lex_all_spec.rb
+++ b/spec/syntax_suggest/unit/lex_all_spec.rb
@@ -17,9 +17,6 @@ module SyntaxSuggest
end # 9
EOM
- # raw_lex = Ripper.lex(source)
- # expect(raw_lex.to_s).to_not include("dog")
-
lex = LexAll.new(source: source)
expect(lex.map(&:token).to_s).to include("dog")
expect(lex.first.line).to eq(1)
diff --git a/spec/syntax_suggest/unit/pathname_from_message_spec.rb b/spec/syntax_suggest/unit/pathname_from_message_spec.rb
index 76756efda9..de58acebaa 100644
--- a/spec/syntax_suggest/unit/pathname_from_message_spec.rb
+++ b/spec/syntax_suggest/unit/pathname_from_message_spec.rb
@@ -43,6 +43,15 @@ module SyntaxSuggest
expect(file).to be_falsey
end
+ it "does not output error message on syntax error inside of an (eval at __FILE__:__LINE__)" do
+ message = "(eval at #{__FILE__}:#{__LINE__}):1: invalid multibyte char (UTF-8) (SyntaxError)\n"
+ io = StringIO.new
+ file = PathnameFromMessage.new(message, io: io).call.name
+
+ expect(io.string).to eq("")
+ expect(file).to be_falsey
+ end
+
it "does not output error message on syntax error inside of streamed code" do
# An example of streamed code is: $ echo "def foo" | ruby
message = "-:1: syntax error, unexpected end-of-input\n"
diff --git a/spec/syntax_suggest/unit/scan_history_spec.rb b/spec/syntax_suggest/unit/scan_history_spec.rb
index 0e75ac66ce..d8b0a54ba6 100644
--- a/spec/syntax_suggest/unit/scan_history_spec.rb
+++ b/spec/syntax_suggest/unit/scan_history_spec.rb
@@ -5,7 +5,7 @@ require_relative "../spec_helper"
module SyntaxSuggest
RSpec.describe ScanHistory do
it "retains commits" do
- source = <<~'EOM'
+ source = <<~EOM
class OH # 0
def lol # 1
print 'lol # 2
@@ -42,7 +42,7 @@ module SyntaxSuggest
end
it "is stashable" do
- source = <<~'EOM'
+ source = <<~EOM
class OH # 0
def lol # 1
print 'lol # 2
@@ -79,7 +79,7 @@ module SyntaxSuggest
end
it "doesnt change if you dont't change it" do
- source = <<~'EOM'
+ source = <<~EOM
class OH # 0
def lol # 1
print 'lol # 2
diff --git a/st.c b/st.c
index b8ad6ab6c2..4f0259a237 100644
--- a/st.c
+++ b/st.c
@@ -103,11 +103,12 @@
#ifdef NOT_RUBY
#include "regint.h"
#include "st.h"
-#else
+#elif defined RUBY_EXPORT
#include "internal.h"
#include "internal/bits.h"
#include "internal/hash.h"
#include "internal/sanitizers.h"
+#include "internal/st.h"
#endif
#include <stdio.h>
@@ -342,7 +343,7 @@ get_power2(st_index_t size)
unsigned int n = ST_INDEX_BITS - nlz_intptr(size);
if (n <= MAX_POWER2)
return n < MINIMAL_POWER2 ? MINIMAL_POWER2 : n;
-#ifndef NOT_RUBY
+#ifdef RUBY
/* Ran out of the table entries */
rb_raise(rb_eRuntimeError, "st_table too big");
#endif
@@ -657,8 +658,7 @@ st_clear(st_table *tab)
void
st_free_table(st_table *tab)
{
- if (tab->bins != NULL)
- free(tab->bins);
+ free(tab->bins);
free(tab->entries);
free(tab);
}
@@ -718,6 +718,10 @@ count_collision(const struct st_hash_type *type)
#error "REBUILD_THRESHOLD should be >= 2"
#endif
+static void rebuild_table_with(st_table *const new_tab, st_table *const tab);
+static void rebuild_move_table(st_table *const new_tab, st_table *const tab);
+static void rebuild_cleanup(st_table *const tab);
+
/* Rebuild table TAB. Rebuilding removes all deleted bins and entries
and can change size of the table entries and bins arrays.
Rebuilding is implemented by creation of a new table or by
@@ -725,14 +729,6 @@ count_collision(const struct st_hash_type *type)
static void
rebuild_table(st_table *tab)
{
- st_index_t i, ni;
- unsigned int size_ind;
- st_table *new_tab;
- st_table_entry *new_entries;
- st_table_entry *curr_entry_ptr;
- st_index_t *bins;
- st_index_t bin_ind;
-
if ((2 * tab->num_entries <= get_allocated_entries(tab)
&& REBUILD_THRESHOLD * tab->num_entries > get_allocated_entries(tab))
|| tab->num_entries < (1 << MINIMAL_POWER2)) {
@@ -740,17 +736,32 @@ rebuild_table(st_table *tab)
tab->num_entries = 0;
if (tab->bins != NULL)
initialize_bins(tab);
- new_tab = tab;
- new_entries = tab->entries;
+ rebuild_table_with(tab, tab);
}
else {
+ st_table *new_tab;
/* This allocation could trigger GC and compaction. If tab is the
* gen_iv_tbl, then tab could have changed in size due to objects being
* freed and/or moved. Do not store attributes of tab before this line. */
new_tab = st_init_table_with_size(tab->type,
2 * tab->num_entries - 1);
- new_entries = new_tab->entries;
+ rebuild_table_with(new_tab, tab);
+ rebuild_move_table(new_tab, tab);
}
+ rebuild_cleanup(tab);
+}
+
+static void
+rebuild_table_with(st_table *const new_tab, st_table *const tab)
+{
+ st_index_t i, ni;
+ unsigned int size_ind;
+ st_table_entry *new_entries;
+ st_table_entry *curr_entry_ptr;
+ st_index_t *bins;
+ st_index_t bin_ind;
+
+ new_entries = new_tab->entries;
ni = 0;
bins = new_tab->bins;
@@ -773,17 +784,24 @@ rebuild_table(st_table *tab)
new_tab->num_entries++;
ni++;
}
- if (new_tab != tab) {
- tab->entry_power = new_tab->entry_power;
- tab->bin_power = new_tab->bin_power;
- tab->size_ind = new_tab->size_ind;
- if (tab->bins != NULL)
- free(tab->bins);
- tab->bins = new_tab->bins;
- free(tab->entries);
- tab->entries = new_tab->entries;
- free(new_tab);
- }
+}
+
+static void
+rebuild_move_table(st_table *const new_tab, st_table *const tab)
+{
+ tab->entry_power = new_tab->entry_power;
+ tab->bin_power = new_tab->bin_power;
+ tab->size_ind = new_tab->size_ind;
+ free(tab->bins);
+ tab->bins = new_tab->bins;
+ free(tab->entries);
+ tab->entries = new_tab->entries;
+ free(new_tab);
+}
+
+static void
+rebuild_cleanup(st_table *const tab)
+{
tab->entries_start = 0;
tab->entries_bound = tab->num_entries;
tab->rebuilds_num++;
@@ -1168,6 +1186,13 @@ st_add_direct_with_hash(st_table *tab,
}
}
+void
+rb_st_add_direct_with_hash(st_table *tab,
+ st_data_t key, st_data_t value, st_hash_t hash)
+{
+ st_add_direct_with_hash(tab, key, value, hash);
+}
+
/* Insert (KEY, VALUE) into table TAB. The table should not have
entry with KEY before the insertion. */
void
@@ -1228,17 +1253,10 @@ st_insert2(st_table *tab, st_data_t key, st_data_t value,
return 1;
}
-/* Create and return a copy of table OLD_TAB. */
+/* Create a copy of old_tab into new_tab. */
st_table *
-st_copy(st_table *old_tab)
+st_replace(st_table *new_tab, st_table *old_tab)
{
- st_table *new_tab;
-
- new_tab = (st_table *) malloc(sizeof(st_table));
-#ifndef RUBY
- if (new_tab == NULL)
- return NULL;
-#endif
*new_tab = *old_tab;
if (old_tab->bins == NULL)
new_tab->bins = NULL;
@@ -1246,7 +1264,6 @@ st_copy(st_table *old_tab)
new_tab->bins = (st_index_t *) malloc(bins_size(old_tab));
#ifndef RUBY
if (new_tab->bins == NULL) {
- free(new_tab);
return NULL;
}
#endif
@@ -1255,7 +1272,6 @@ st_copy(st_table *old_tab)
* sizeof(st_table_entry));
#ifndef RUBY
if (new_tab->entries == NULL) {
- st_free_table(new_tab);
return NULL;
}
#endif
@@ -1263,6 +1279,27 @@ st_copy(st_table *old_tab)
get_allocated_entries(old_tab));
if (old_tab->bins != NULL)
MEMCPY(new_tab->bins, old_tab->bins, char, bins_size(old_tab));
+
+ return new_tab;
+}
+
+/* Create and return a copy of table OLD_TAB. */
+st_table *
+st_copy(st_table *old_tab)
+{
+ st_table *new_tab;
+
+ new_tab = (st_table *) malloc(sizeof(st_table));
+#ifndef RUBY
+ if (new_tab == NULL)
+ return NULL;
+#endif
+
+ if (st_replace(new_tab, old_tab) == NULL) {
+ st_free_table(new_tab);
+ return NULL;
+ }
+
return new_tab;
}
@@ -2061,6 +2098,7 @@ st_numhash(st_data_t n)
return (st_index_t)((n>>s1|(n<<s2)) ^ (n>>s2));
}
+#ifdef RUBY
/* Expand TAB to be suitable for holding SIZ entries in total.
Pre-existing entries remain not deleted inside of TAB, but its bins
are cleared to expect future reconstruction. See rehash below. */
@@ -2077,10 +2115,8 @@ st_expand_table(st_table *tab, st_index_t siz)
n = get_allocated_entries(tab);
MEMCPY(tmp->entries, tab->entries, st_table_entry, n);
free(tab->entries);
- if (tab->bins != NULL)
- free(tab->bins);
- if (tmp->bins != NULL)
- free(tmp->bins);
+ free(tab->bins);
+ free(tmp->bins);
tab->entry_power = tmp->entry_power;
tab->bin_power = tmp->bin_power;
tab->size_ind = tmp->size_ind;
@@ -2098,10 +2134,10 @@ st_rehash_linear(st_table *tab)
int eq_p, rebuilt_p;
st_index_t i, j;
st_table_entry *p, *q;
- if (tab->bins) {
- free(tab->bins);
- tab->bins = NULL;
- }
+
+ free(tab->bins);
+ tab->bins = NULL;
+
for (i = tab->entries_start; i < tab->entries_bound; i++) {
p = &tab->entries[i];
if (DELETED_ENTRY_P(p))
@@ -2200,7 +2236,6 @@ st_rehash(st_table *tab)
} while (rebuilt_p);
}
-#ifdef RUBY
static st_data_t
st_stringify(VALUE key)
{
@@ -2288,4 +2323,17 @@ rb_st_nth_key(st_table *tab, st_index_t index)
}
}
+void
+rb_st_compact_table(st_table *tab)
+{
+ st_index_t num = tab->num_entries;
+ if (REBUILD_THRESHOLD * num <= get_allocated_entries(tab)) {
+ /* Compaction: */
+ st_table *new_tab = st_init_table_with_size(tab->type, 2 * num);
+ rebuild_table_with(new_tab, tab);
+ rebuild_move_table(new_tab, tab);
+ rebuild_cleanup(tab);
+ }
+}
+
#endif
diff --git a/string.c b/string.c
index 470bc59b76..9f7c163a81 100644
--- a/string.c
+++ b/string.c
@@ -78,24 +78,41 @@
VALUE rb_cString;
VALUE rb_cSymbol;
-/* FLAGS of RString
+/* Flags of RString
*
* 1: RSTRING_NOEMBED
- * 2: STR_SHARED (== ELTS_SHARED)
- * 5: STR_SHARED_ROOT (RSTRING_NOEMBED==1 && STR_SHARED == 0, there may be
- * other strings that rely on this string's buffer)
- * 6: STR_BORROWED (when RSTRING_NOEMBED==1 && klass==0, unsafe to recycle
- * early, specific to rb_str_tmp_frozen_{acquire,release})
- * 7: STR_TMPLOCK (set when a pointer to the buffer is passed to syscall
- * such as read(2). Any modification and realloc is prohibited)
- *
- * 8-9: ENC_CODERANGE (2 bits)
- * 10-16: ENCODING (7 bits == 128)
+ * The string is not embedded. When a string is embedded, the contents
+ * follow the header. When a string is not embedded, the contents is
+ * on a separately allocated buffer.
+ * 2: STR_SHARED (equal to ELTS_SHARED)
+ * The string is shared. The buffer this string points to is owned by
+ * another string (the shared root).
+ * 3: STR_CHILLED (will be frozen in a future version)
+ * The string appears frozen but can be mutated with a warning.
+ * 5: STR_SHARED_ROOT
+ * Other strings may point to the contents of this string. When this
+ * flag is set, STR_SHARED must not be set.
+ * 6: STR_BORROWED
+ * When RSTRING_NOEMBED is set and klass is 0, this string is unsafe
+ * to be unshared by rb_str_tmp_frozen_release.
+ * 7: STR_TMPLOCK
+ * The pointer to the buffer is passed to a system call such as
+ * read(2). Any modification and realloc is prohibited.
+ * 8-9: ENC_CODERANGE
+ * Stores the coderange of the string.
+ * 10-16: ENCODING
+ * Stores the encoding of the string.
* 17: RSTRING_FSTR
- * 18: STR_NOFREE (do not free this string's buffer when a String is freed.
- * used for a string object based on C string literal)
- * 19: STR_FAKESTR (when RVALUE is not managed by GC. Typically, the string
- * object header is temporarily allocated on C stack)
+ * The string is a fstring. The string is deduplicated in the fstring
+ * table.
+ * 18: STR_NOFREE
+ * Do not free this string's buffer when the string is reclaimed
+ * by the garbage collector. Used for when the string buffer is a C
+ * string literal.
+ * 19: STR_FAKESTR
+ * The string is not allocated or managed by the garbage collector.
+ * Typically, the string object header (struct RString) is temporarily
+ * allocated on C stack.
*/
#define RUBY_MAX_CHAR_LEN 16
@@ -109,19 +126,10 @@ VALUE rb_cSymbol;
FL_SET((str), STR_NOEMBED);\
FL_UNSET((str), STR_SHARED | STR_SHARED_ROOT | STR_BORROWED);\
} while (0)
-#define STR_SET_EMBED(str) FL_UNSET((str), (STR_NOEMBED|STR_NOFREE))
-# define STR_SET_EMBED_LEN(str, n) do { \
- assert(str_embed_capa(str) > (n));\
- RSTRING(str)->as.embed.len = (n);\
-} while (0)
+#define STR_SET_EMBED(str) FL_UNSET((str), STR_NOEMBED | STR_SHARED | STR_NOFREE)
#define STR_SET_LEN(str, n) do { \
- if (STR_EMBED_P(str)) {\
- STR_SET_EMBED_LEN((str), (n));\
- }\
- else {\
- RSTRING(str)->as.heap.len = (n);\
- }\
+ RSTRING(str)->len = (n); \
} while (0)
static inline bool
@@ -158,13 +166,13 @@ str_enc_fastpath(VALUE str)
const long tlen = RSTRING_LEN(str);\
memcpy(tmp, RSTRING_PTR(str), tlen);\
RSTRING(str)->as.heap.ptr = tmp;\
- RSTRING(str)->as.heap.len = tlen;\
+ RSTRING(str)->len = tlen;\
STR_SET_NOEMBED(str);\
RSTRING(str)->as.heap.aux.capa = (capacity);\
}\
}\
else {\
- assert(!FL_TEST((str), STR_SHARED)); \
+ RUBY_ASSERT(!FL_TEST((str), STR_SHARED)); \
SIZED_REALLOC_N(RSTRING(str)->as.heap.ptr, char, \
(size_t)(capacity) + (termlen), STR_HEAP_SIZE(str)); \
RSTRING(str)->as.heap.aux.capa = (capacity);\
@@ -173,8 +181,8 @@ str_enc_fastpath(VALUE str)
#define STR_SET_SHARED(str, shared_str) do { \
if (!FL_TEST(str, STR_FAKESTR)) { \
- assert(RSTRING_PTR(shared_str) <= RSTRING_PTR(str)); \
- assert(RSTRING_PTR(str) <= RSTRING_PTR(shared_str) + RSTRING_LEN(shared_str)); \
+ RUBY_ASSERT(RSTRING_PTR(shared_str) <= RSTRING_PTR(str)); \
+ RUBY_ASSERT(RSTRING_PTR(str) <= RSTRING_PTR(shared_str) + RSTRING_LEN(shared_str)); \
RB_OBJ_WRITE((str), &RSTRING(str)->as.heap.aux.shared, (shared_str)); \
FL_SET((str), STR_SHARED); \
FL_SET((shared_str), STR_SHARED_ROOT); \
@@ -222,7 +230,7 @@ rb_str_size_as_embedded(VALUE str)
{
size_t real_size;
if (STR_EMBED_P(str)) {
- real_size = rb_str_embed_size(RSTRING(str)->as.embed.len) + TERM_LEN(str);
+ real_size = rb_str_embed_size(RSTRING(str)->len) + TERM_LEN(str);
}
/* if the string is not currently embedded, but it can be embedded, how
* much space would it require */
@@ -275,10 +283,10 @@ rb_str_make_embedded(VALUE str)
RUBY_ASSERT(!STR_EMBED_P(str));
char *buf = RSTRING(str)->as.heap.ptr;
- long len = RSTRING(str)->as.heap.len;
+ long len = RSTRING(str)->len;
STR_SET_EMBED(str);
- STR_SET_EMBED_LEN(str, len);
+ STR_SET_LEN(str, len);
if (len > 0) {
memcpy(RSTRING_PTR(str), buf, len);
@@ -289,26 +297,6 @@ rb_str_make_embedded(VALUE str)
}
void
-rb_str_update_shared_ary(VALUE str, VALUE old_root, VALUE new_root)
-{
- // if the root location hasn't changed, we don't need to update
- if (new_root == old_root) {
- return;
- }
-
- // if the root string isn't embedded, we don't need to touch the pointer.
- // it already points to the shame shared buffer
- if (!STR_EMBED_P(new_root)) {
- return;
- }
-
- size_t offset = (size_t)((uintptr_t)RSTRING(str)->as.heap.ptr - (uintptr_t)RSTRING(old_root)->as.embed.ary);
-
- RUBY_ASSERT(RSTRING(str)->as.heap.ptr >= RSTRING(old_root)->as.embed.ary);
- RSTRING(str)->as.heap.ptr = RSTRING(new_root)->as.embed.ary + offset;
-}
-
-void
rb_debug_rstring_null_ptr(const char *func)
{
fprintf(stderr, "%s is returning NULL!! "
@@ -382,24 +370,25 @@ fstr_update_callback(st_data_t *key, st_data_t *value, st_data_t data, int exist
else {
if (FL_TEST_RAW(str, STR_FAKESTR)) {
if (arg->copy) {
- VALUE new_str = str_new(rb_cString, RSTRING(str)->as.heap.ptr, RSTRING(str)->as.heap.len);
+ VALUE new_str = str_new(rb_cString, RSTRING(str)->as.heap.ptr, RSTRING(str)->len);
rb_enc_copy(new_str, str);
str = new_str;
}
else {
str = str_new_static(rb_cString, RSTRING(str)->as.heap.ptr,
- RSTRING(str)->as.heap.len,
+ RSTRING(str)->len,
ENCODING_GET(str));
}
- OBJ_FREEZE_RAW(str);
+ OBJ_FREEZE(str);
}
else {
- if (!OBJ_FROZEN(str))
+ if (!OBJ_FROZEN(str) || CHILLED_STRING_P(str)) {
str = str_new_frozen(rb_cString, str);
+ }
if (STR_SHARED_P(str)) { /* str should not be shared */
/* shared substring */
str_make_independent(str);
- assert(OBJ_FROZEN(str));
+ RUBY_ASSERT(OBJ_FROZEN(str));
}
if (!BARE_STRING_P(str)) {
str = str_new_frozen(rb_cString, str);
@@ -412,7 +401,6 @@ fstr_update_callback(st_data_t *key, st_data_t *value, st_data_t data, int exist
}
}
-RUBY_FUNC_EXPORTED
VALUE
rb_fstring(VALUE str)
{
@@ -427,23 +415,24 @@ rb_fstring(VALUE str)
bare = BARE_STRING_P(str);
if (!bare) {
if (STR_EMBED_P(str)) {
- OBJ_FREEZE_RAW(str);
+ OBJ_FREEZE(str);
return str;
}
- if (FL_TEST_RAW(str, STR_NOEMBED|STR_SHARED_ROOT|STR_SHARED) == (STR_NOEMBED|STR_SHARED_ROOT)) {
- assert(OBJ_FROZEN(str));
+
+ if (FL_TEST_RAW(str, STR_SHARED_ROOT | STR_SHARED) == STR_SHARED_ROOT) {
+ RUBY_ASSERT(OBJ_FROZEN(str));
return str;
}
}
- if (!OBJ_FROZEN(str))
+ if (!FL_TEST_RAW(str, FL_FREEZE | STR_NOFREE | STR_CHILLED))
rb_str_resize(str, RSTRING_LEN(str));
fstr = register_fstring(str, FALSE);
if (!bare) {
str_replace_shared_without_enc(str, fstr);
- OBJ_FREEZE_RAW(str);
+ OBJ_FREEZE(str);
return str;
}
return fstr;
@@ -465,10 +454,11 @@ register_fstring(VALUE str, bool copy)
}
RB_VM_LOCK_LEAVE();
- assert(OBJ_FROZEN(args.fstr));
- assert(!FL_TEST_RAW(args.fstr, STR_FAKESTR));
- assert(!FL_TEST_RAW(args.fstr, FL_EXIVAR));
- assert(RBASIC_CLASS(args.fstr) == rb_cString);
+ RUBY_ASSERT(OBJ_FROZEN(args.fstr));
+ RUBY_ASSERT(!FL_TEST_RAW(args.fstr, STR_FAKESTR));
+ RUBY_ASSERT(!FL_TEST_RAW(args.fstr, FL_EXIVAR));
+ RUBY_ASSERT(RBASIC_CLASS(args.fstr) == rb_cString);
+
return args.fstr;
}
@@ -486,7 +476,7 @@ setup_fake_str(struct RString *fake_str, const char *name, long len, int encidx)
ENCODING_SET_INLINED((VALUE)fake_str, encidx);
RBASIC_SET_CLASS_RAW((VALUE)fake_str, rb_cString);
- fake_str->as.heap.len = len;
+ fake_str->len = len;
fake_str->as.heap.ptr = (char *)name;
fake_str->as.heap.aux.capa = len;
return (VALUE)fake_str;
@@ -832,7 +822,7 @@ str_capacity(VALUE str, const int termlen)
return str_embed_capa(str) - termlen;
}
else if (FL_TEST(str, STR_SHARED|STR_NOFREE)) {
- return RSTRING(str)->as.heap.len;
+ return RSTRING(str)->len;
}
else {
return RSTRING(str)->as.heap.aux.capa;
@@ -857,8 +847,8 @@ static inline VALUE
str_alloc_embed(VALUE klass, size_t capa)
{
size_t size = rb_str_embed_size(capa);
- assert(size > 0);
- assert(rb_gc_size_allocatable_p(size));
+ RUBY_ASSERT(size > 0);
+ RUBY_ASSERT(rb_gc_size_allocatable_p(size));
NEWOBJ_OF(str, struct RString, klass,
T_STRING | (RGENGC_WB_PROTECTED_STRING ? FL_WB_PROTECTED : 0), size, 0);
@@ -1012,7 +1002,7 @@ str_new_static(VALUE klass, const char *ptr, long len, int encindex)
else {
RUBY_DTRACE_CREATE_HOOK(STRING, len);
str = str_alloc_heap(klass);
- RSTRING(str)->as.heap.len = len;
+ RSTRING(str)->len = len;
RSTRING(str)->as.heap.ptr = (char *)ptr;
RSTRING(str)->as.heap.aux.capa = len;
RBASIC(str)->flags |= STR_NOFREE;
@@ -1160,6 +1150,7 @@ str_cat_conv_enc_opts(VALUE newstr, long ofs, const char *ptr, long len,
rb_str_resize(newstr, olen);
}
DATA_PTR(econv_wrapper) = 0;
+ RB_GC_GUARD(econv_wrapper);
rb_econv_close(ec);
switch (ret) {
case econv_finished:
@@ -1296,7 +1287,6 @@ str_replace_shared_without_enc(VALUE str2, VALUE str)
char *ptr2 = RSTRING(str2)->as.embed.ary;
STR_SET_EMBED(str2);
memcpy(ptr2, RSTRING_PTR(str), len);
- STR_SET_EMBED_LEN(str2, len);
TERM_FILL(ptr2+len, termlen);
}
else {
@@ -1309,7 +1299,8 @@ str_replace_shared_without_enc(VALUE str2, VALUE str)
root = rb_str_new_frozen(str);
RSTRING_GETMEM(root, ptr, len);
}
- assert(OBJ_FROZEN(root));
+ RUBY_ASSERT(OBJ_FROZEN(root));
+
if (!STR_EMBED_P(str2) && !FL_TEST_RAW(str2, STR_SHARED|STR_NOFREE)) {
if (FL_TEST_RAW(str2, STR_SHARED_ROOT)) {
rb_fatal("about to free a possible shared root");
@@ -1320,10 +1311,12 @@ str_replace_shared_without_enc(VALUE str2, VALUE str)
}
}
FL_SET(str2, STR_NOEMBED);
- RSTRING(str2)->as.heap.len = len;
RSTRING(str2)->as.heap.ptr = ptr;
STR_SET_SHARED(str2, root);
}
+
+ STR_SET_LEN(str2, len);
+
return str2;
}
@@ -1350,7 +1343,7 @@ rb_str_new_shared(VALUE str)
VALUE
rb_str_new_frozen(VALUE orig)
{
- if (OBJ_FROZEN(orig)) return orig;
+ if (RB_FL_TEST_RAW(orig, FL_FREEZE | STR_CHILLED) == FL_FREEZE) return orig;
return str_new_frozen(rb_obj_class(orig), orig);
}
@@ -1368,6 +1361,42 @@ rb_str_tmp_frozen_acquire(VALUE orig)
return str_new_frozen_buffer(0, orig, FALSE);
}
+VALUE
+rb_str_tmp_frozen_no_embed_acquire(VALUE orig)
+{
+ if (OBJ_FROZEN_RAW(orig) && !STR_EMBED_P(orig) && !rb_str_reembeddable_p(orig)) return orig;
+ if (STR_SHARED_P(orig) && !STR_EMBED_P(RSTRING(orig)->as.heap.aux.shared)) return rb_str_tmp_frozen_acquire(orig);
+
+ VALUE str = str_alloc_heap(0);
+ OBJ_FREEZE(str);
+ /* Always set the STR_SHARED_ROOT to ensure it does not get re-embedded. */
+ FL_SET(str, STR_SHARED_ROOT);
+
+ size_t capa = str_capacity(orig, TERM_LEN(orig));
+
+ /* If the string is embedded then we want to create a copy that is heap
+ * allocated. If the string is shared then the shared root must be
+ * embedded, so we want to create a copy. If the string is a shared root
+ * then it must be embedded, so we want to create a copy. */
+ if (STR_EMBED_P(orig) || FL_TEST_RAW(orig, STR_SHARED | STR_SHARED_ROOT)) {
+ RSTRING(str)->as.heap.ptr = rb_xmalloc_mul_add_mul(sizeof(char), capa, sizeof(char), TERM_LEN(orig));
+ memcpy(RSTRING(str)->as.heap.ptr, RSTRING_PTR(orig), capa);
+ }
+ else {
+ /* orig must be heap allocated and not shared, so we can safely transfer
+ * the pointer to str. */
+ RSTRING(str)->as.heap.ptr = RSTRING(orig)->as.heap.ptr;
+ RBASIC(str)->flags |= RBASIC(orig)->flags & STR_NOFREE;
+ RBASIC(orig)->flags &= ~STR_NOFREE;
+ STR_SET_SHARED(orig, str);
+ }
+
+ RSTRING(str)->len = RSTRING(orig)->len;
+ RSTRING(str)->as.heap.aux.capa = capa;
+
+ return str;
+}
+
void
rb_str_tmp_frozen_release(VALUE orig, VALUE tmp)
{
@@ -1375,25 +1404,25 @@ rb_str_tmp_frozen_release(VALUE orig, VALUE tmp)
return;
if (STR_EMBED_P(tmp)) {
- assert(OBJ_FROZEN_RAW(tmp));
+ RUBY_ASSERT(OBJ_FROZEN_RAW(tmp));
}
else if (FL_TEST_RAW(orig, STR_SHARED) &&
!FL_TEST_RAW(orig, STR_TMPLOCK|RUBY_FL_FREEZE)) {
VALUE shared = RSTRING(orig)->as.heap.aux.shared;
if (shared == tmp && !FL_TEST_RAW(tmp, STR_BORROWED)) {
- assert(RSTRING(orig)->as.heap.ptr == RSTRING(tmp)->as.heap.ptr);
- assert(RSTRING(orig)->as.heap.len == RSTRING(tmp)->as.heap.len);
+ RUBY_ASSERT(RSTRING(orig)->as.heap.ptr == RSTRING(tmp)->as.heap.ptr);
+ RUBY_ASSERT(RSTRING_LEN(orig) == RSTRING_LEN(tmp));
/* Unshare orig since the root (tmp) only has this one child. */
FL_UNSET_RAW(orig, STR_SHARED);
RSTRING(orig)->as.heap.aux.capa = RSTRING(tmp)->as.heap.aux.capa;
RBASIC(orig)->flags |= RBASIC(tmp)->flags & STR_NOFREE;
- assert(OBJ_FROZEN_RAW(tmp));
+ RUBY_ASSERT(OBJ_FROZEN_RAW(tmp));
/* Make tmp embedded and empty so it is safe for sweeping. */
STR_SET_EMBED(tmp);
- STR_SET_EMBED_LEN(tmp, 0);
+ STR_SET_LEN(tmp, 0);
}
}
}
@@ -1407,11 +1436,11 @@ str_new_frozen(VALUE klass, VALUE orig)
static VALUE
heap_str_make_shared(VALUE klass, VALUE orig)
{
- assert(!STR_EMBED_P(orig));
- assert(!STR_SHARED_P(orig));
+ RUBY_ASSERT(!STR_EMBED_P(orig));
+ RUBY_ASSERT(!STR_SHARED_P(orig));
VALUE str = str_alloc_heap(klass);
- RSTRING(str)->as.heap.len = RSTRING_LEN(orig);
+ STR_SET_LEN(str, RSTRING_LEN(orig));
RSTRING(str)->as.heap.ptr = RSTRING_PTR(orig);
RSTRING(str)->as.heap.aux.capa = RSTRING(orig)->as.heap.aux.capa;
RBASIC(str)->flags |= RBASIC(orig)->flags & STR_NOFREE;
@@ -1432,25 +1461,25 @@ str_new_frozen_buffer(VALUE klass, VALUE orig, int copy_encoding)
if (STR_EMBED_P(orig) || STR_EMBEDDABLE_P(len, termlen)) {
str = str_new0(klass, RSTRING_PTR(orig), len, termlen);
- assert(STR_EMBED_P(str));
+ RUBY_ASSERT(STR_EMBED_P(str));
}
else {
if (FL_TEST_RAW(orig, STR_SHARED)) {
VALUE shared = RSTRING(orig)->as.heap.aux.shared;
long ofs = RSTRING(orig)->as.heap.ptr - RSTRING_PTR(shared);
- long rest = RSTRING_LEN(shared) - ofs - RSTRING(orig)->as.heap.len;
- assert(ofs >= 0);
- assert(rest >= 0);
- assert(ofs + rest <= RSTRING_LEN(shared));
- assert(OBJ_FROZEN(shared));
+ long rest = RSTRING_LEN(shared) - ofs - RSTRING_LEN(orig);
+ RUBY_ASSERT(ofs >= 0);
+ RUBY_ASSERT(rest >= 0);
+ RUBY_ASSERT(ofs + rest <= RSTRING_LEN(shared));
+ RUBY_ASSERT(OBJ_FROZEN(shared));
if ((ofs > 0) || (rest > 0) ||
(klass != RBASIC(shared)->klass) ||
ENCODING_GET(shared) != ENCODING_GET(orig)) {
str = str_new_shared(klass, shared);
- assert(!STR_EMBED_P(str));
+ RUBY_ASSERT(!STR_EMBED_P(str));
RSTRING(str)->as.heap.ptr += ofs;
- RSTRING(str)->as.heap.len -= ofs + rest;
+ STR_SET_LEN(str, RSTRING_LEN(str) - (ofs + rest));
}
else {
if (RBASIC_CLASS(shared) == 0)
@@ -1462,7 +1491,7 @@ str_new_frozen_buffer(VALUE klass, VALUE orig, int copy_encoding)
str = str_alloc_embed(klass, RSTRING_LEN(orig) + TERM_LEN(orig));
STR_SET_EMBED(str);
memcpy(RSTRING_PTR(str), RSTRING_PTR(orig), RSTRING_LEN(orig));
- STR_SET_EMBED_LEN(str, RSTRING_LEN(orig));
+ STR_SET_LEN(str, RSTRING_LEN(orig));
TERM_FILL(RSTRING_END(str), TERM_LEN(orig));
}
else {
@@ -1552,7 +1581,7 @@ rb_str_free(VALUE str)
}
}
-RUBY_FUNC_EXPORTED size_t
+size_t
rb_str_memsize(VALUE str)
{
if (FL_TEST(str, STR_NOEMBED|STR_SHARED|STR_NOFREE) == STR_NOEMBED) {
@@ -1591,23 +1620,24 @@ str_shared_replace(VALUE str, VALUE str2)
str_discard(str);
termlen = rb_enc_mbminlen(enc);
+ STR_SET_LEN(str, RSTRING_LEN(str2));
+
if (str_embed_capa(str) >= RSTRING_LEN(str2) + termlen) {
STR_SET_EMBED(str);
memcpy(RSTRING_PTR(str), RSTRING_PTR(str2), (size_t)RSTRING_LEN(str2) + termlen);
- STR_SET_EMBED_LEN(str, RSTRING_LEN(str2));
rb_enc_associate(str, enc);
ENC_CODERANGE_SET(str, cr);
}
else {
if (STR_EMBED_P(str2)) {
- assert(!FL_TEST(str2, STR_SHARED));
- long len = RSTRING(str2)->as.embed.len;
- assert(len + termlen <= str_embed_capa(str2));
+ RUBY_ASSERT(!FL_TEST(str2, STR_SHARED));
+ long len = RSTRING_LEN(str2);
+ RUBY_ASSERT(len + termlen <= str_embed_capa(str2));
char *new_ptr = ALLOC_N(char, len + termlen);
memcpy(new_ptr, RSTRING(str2)->as.embed.ary, len + termlen);
RSTRING(str2)->as.heap.ptr = new_ptr;
- RSTRING(str2)->as.heap.len = len;
+ STR_SET_LEN(str2, len);
RSTRING(str2)->as.heap.aux.capa = len;
STR_SET_NOEMBED(str2);
}
@@ -1615,7 +1645,6 @@ str_shared_replace(VALUE str, VALUE str2)
STR_SET_NOEMBED(str);
FL_UNSET(str, STR_SHARED);
RSTRING(str)->as.heap.ptr = RSTRING_PTR(str2);
- RSTRING(str)->as.heap.len = RSTRING_LEN(str2);
if (FL_TEST(str2, STR_SHARED)) {
VALUE shared = RSTRING(str2)->as.heap.aux.shared;
@@ -1628,7 +1657,7 @@ str_shared_replace(VALUE str, VALUE str2)
/* abandon str2 */
STR_SET_EMBED(str2);
RSTRING_PTR(str2)[0] = 0;
- STR_SET_EMBED_LEN(str2, 0);
+ STR_SET_LEN(str2, 0);
rb_enc_associate(str, enc);
ENC_CODERANGE_SET(str, cr);
}
@@ -1662,9 +1691,9 @@ str_replace(VALUE str, VALUE str2)
len = RSTRING_LEN(str2);
if (STR_SHARED_P(str2)) {
VALUE shared = RSTRING(str2)->as.heap.aux.shared;
- assert(OBJ_FROZEN(shared));
+ RUBY_ASSERT(OBJ_FROZEN(shared));
STR_SET_NOEMBED(str);
- RSTRING(str)->as.heap.len = len;
+ STR_SET_LEN(str, len);
RSTRING(str)->as.heap.ptr = RSTRING_PTR(str2);
STR_SET_SHARED(str, shared);
rb_enc_cr_str_exact_copy(str, str2);
@@ -1680,8 +1709,8 @@ static inline VALUE
ec_str_alloc_embed(struct rb_execution_context_struct *ec, VALUE klass, size_t capa)
{
size_t size = rb_str_embed_size(capa);
- assert(size > 0);
- assert(rb_gc_size_allocatable_p(size));
+ RUBY_ASSERT(size > 0);
+ RUBY_ASSERT(rb_gc_size_allocatable_p(size));
NEWOBJ_OF(str, struct RString, klass,
T_STRING | (RGENGC_WB_PROTECTED_STRING ? FL_WB_PROTECTED : 0), size, ec);
@@ -1708,11 +1737,10 @@ str_duplicate_setup(VALUE klass, VALUE str, VALUE dup)
VALUE flags = FL_TEST_RAW(str, flag_mask);
int encidx = 0;
if (STR_EMBED_P(str)) {
- long len = RSTRING_EMBED_LEN(str);
+ long len = RSTRING_LEN(str);
- assert(STR_EMBED_P(dup));
- assert(str_embed_capa(dup) >= len + 1);
- STR_SET_EMBED_LEN(dup, len);
+ RUBY_ASSERT(STR_EMBED_P(dup));
+ RUBY_ASSERT(str_embed_capa(dup) >= len + 1);
MEMCPY(RSTRING(dup)->as.embed.ary, RSTRING(str)->as.embed.ary, char, len + 1);
}
else {
@@ -1724,16 +1752,17 @@ str_duplicate_setup(VALUE klass, VALUE str, VALUE dup)
root = str = str_new_frozen(klass, str);
flags = FL_TEST_RAW(str, flag_mask);
}
- assert(!STR_SHARED_P(root));
- assert(RB_OBJ_FROZEN_RAW(root));
+ RUBY_ASSERT(!STR_SHARED_P(root));
+ RUBY_ASSERT(RB_OBJ_FROZEN_RAW(root));
- RSTRING(dup)->as.heap.len = RSTRING_LEN(str);
RSTRING(dup)->as.heap.ptr = RSTRING_PTR(str);
FL_SET(root, STR_SHARED_ROOT);
RB_OBJ_WRITE(dup, &RSTRING(dup)->as.heap.aux.shared, root);
flags |= RSTRING_NOEMBED | STR_SHARED;
}
+ STR_SET_LEN(dup, RSTRING_LEN(str));
+
if ((flags & ENCODING_MASK) == (ENCODING_INLINE_MAX<<ENCODING_SHIFT)) {
encidx = rb_enc_get_index(str);
flags &= ~ENCODING_MASK;
@@ -1747,11 +1776,11 @@ static inline VALUE
ec_str_duplicate(struct rb_execution_context_struct *ec, VALUE klass, VALUE str)
{
VALUE dup;
- if (FL_TEST(str, STR_NOEMBED)) {
- dup = ec_str_alloc_heap(ec, klass);
+ if (STR_EMBED_P(str)) {
+ dup = ec_str_alloc_embed(ec, klass, RSTRING_LEN(str) + TERM_LEN(str));
}
else {
- dup = ec_str_alloc_embed(ec, klass, RSTRING_EMBED_LEN(str) + TERM_LEN(str));
+ dup = ec_str_alloc_heap(ec, klass);
}
return str_duplicate_setup(klass, str, dup);
@@ -1761,11 +1790,11 @@ static inline VALUE
str_duplicate(VALUE klass, VALUE str)
{
VALUE dup;
- if (FL_TEST(str, STR_NOEMBED)) {
- dup = str_alloc_heap(klass);
+ if (STR_EMBED_P(str)) {
+ dup = str_alloc_embed(klass, RSTRING_LEN(str) + TERM_LEN(str));
}
else {
- dup = str_alloc_embed(klass, RSTRING_EMBED_LEN(str) + TERM_LEN(str));
+ dup = str_alloc_heap(klass);
}
return str_duplicate_setup(klass, str, dup);
@@ -1777,6 +1806,18 @@ rb_str_dup(VALUE str)
return str_duplicate(rb_obj_class(str), str);
}
+/* :nodoc: */
+VALUE
+rb_str_dup_m(VALUE str)
+{
+ if (LIKELY(BARE_STRING_P(str))) {
+ return str_duplicate(rb_obj_class(str), str);
+ }
+ else {
+ return rb_obj_dup(str);
+ }
+}
+
VALUE
rb_str_resurrect(VALUE str)
{
@@ -1785,10 +1826,20 @@ rb_str_resurrect(VALUE str)
}
VALUE
-rb_ec_str_resurrect(struct rb_execution_context_struct *ec, VALUE str)
+rb_ec_str_resurrect(struct rb_execution_context_struct *ec, VALUE str, bool chilled)
{
RUBY_DTRACE_CREATE_HOOK(STRING, RSTRING_LEN(str));
- return ec_str_duplicate(ec, rb_cString, str);
+ VALUE new_str = ec_str_duplicate(ec, rb_cString, str);
+ if (chilled) {
+ STR_CHILL_RAW(new_str);
+ }
+ return new_str;
+}
+
+bool
+rb_str_chilled_p(VALUE str)
+{
+ return CHILLED_STRING_P(str);
}
/*
@@ -1839,17 +1890,13 @@ rb_str_init(int argc, VALUE *argv, VALUE str)
if (orig == str) n = 0;
}
str_modifiable(str);
- if (STR_EMBED_P(str)) { /* make noembed always */
- char *new_ptr = ALLOC_N(char, (size_t)capa + termlen);
- assert(RSTRING(str)->as.embed.len + 1 <= str_embed_capa(str));
- memcpy(new_ptr, RSTRING(str)->as.embed.ary, RSTRING(str)->as.embed.len + 1);
- RSTRING(str)->as.heap.ptr = new_ptr;
- }
- else if (FL_TEST(str, STR_SHARED|STR_NOFREE)) {
+ if (STR_EMBED_P(str) || FL_TEST(str, STR_SHARED|STR_NOFREE)) {
+ /* make noembed always */
const size_t size = (size_t)capa + termlen;
const char *const old_ptr = RSTRING_PTR(str);
- const size_t osize = RSTRING(str)->as.heap.len + TERM_LEN(str);
- char *new_ptr = ALLOC_N(char, (size_t)capa + termlen);
+ const size_t osize = RSTRING_LEN(str) + TERM_LEN(str);
+ char *new_ptr = ALLOC_N(char, size);
+ if (STR_EMBED_P(str)) RUBY_ASSERT((long)osize <= str_embed_capa(str));
memcpy(new_ptr, old_ptr, osize < size ? osize : size);
FL_UNSET_RAW(str, STR_SHARED|STR_NOFREE);
RSTRING(str)->as.heap.ptr = new_ptr;
@@ -1858,7 +1905,7 @@ rb_str_init(int argc, VALUE *argv, VALUE str)
SIZED_REALLOC_N(RSTRING(str)->as.heap.ptr, char,
(size_t)capa + termlen, STR_HEAP_SIZE(str));
}
- RSTRING(str)->as.heap.len = len;
+ STR_SET_LEN(str, len);
TERM_FILL(&RSTRING(str)->as.heap.ptr[len], termlen);
if (n == 1) {
memcpy(RSTRING(str)->as.heap.ptr, RSTRING_PTR(orig), len);
@@ -1881,6 +1928,98 @@ rb_str_init(int argc, VALUE *argv, VALUE str)
return str;
}
+/* :nodoc: */
+static VALUE
+rb_str_s_new(int argc, VALUE *argv, VALUE klass)
+{
+ if (klass != rb_cString) {
+ return rb_class_new_instance_pass_kw(argc, argv, klass);
+ }
+
+ static ID keyword_ids[2];
+ VALUE orig, opt, encoding = Qnil, capacity = Qnil;
+ VALUE kwargs[2];
+ rb_encoding *enc = NULL;
+
+ int n = rb_scan_args(argc, argv, "01:", &orig, &opt);
+ if (NIL_P(opt)) {
+ return rb_class_new_instance_pass_kw(argc, argv, klass);
+ }
+
+ keyword_ids[0] = rb_id_encoding();
+ CONST_ID(keyword_ids[1], "capacity");
+ rb_get_kwargs(opt, keyword_ids, 0, 2, kwargs);
+ encoding = kwargs[0];
+ capacity = kwargs[1];
+
+ int termlen = 1;
+
+ if (n == 1) {
+ orig = StringValue(orig);
+ }
+ else {
+ orig = Qnil;
+ }
+
+ if (UNDEF_P(encoding)) {
+ if (!NIL_P(orig)) {
+ encoding = rb_obj_encoding(orig);
+ }
+ }
+
+ if (!UNDEF_P(encoding)) {
+ enc = rb_to_encoding(encoding);
+ termlen = rb_enc_mbminlen(enc);
+ }
+
+ // If capacity is nil, we're basically just duping `orig`.
+ if (UNDEF_P(capacity)) {
+ if (NIL_P(orig)) {
+ VALUE empty_str = str_new(klass, "", 0);
+ if (enc) {
+ rb_enc_associate(empty_str, enc);
+ }
+ return empty_str;
+ }
+ VALUE copy = str_duplicate(klass, orig);
+ rb_enc_associate(copy, enc);
+ ENC_CODERANGE_CLEAR(copy);
+ return copy;
+ }
+
+ long capa = 0;
+ capa = NUM2LONG(capacity);
+ if (capa < 0) {
+ capa = 0;
+ }
+
+ if (!NIL_P(orig)) {
+ long orig_capa = rb_str_capacity(orig);
+ if (orig_capa > capa) {
+ capa = orig_capa;
+ }
+ }
+
+ long fake_len = capa - termlen;
+ if (fake_len < 0) {
+ fake_len = 0;
+ }
+
+ VALUE str = str_new0(klass, NULL, fake_len, termlen);
+ STR_SET_LEN(str, 0);
+ TERM_FILL(RSTRING_PTR(str), termlen);
+
+ if (enc) {
+ rb_enc_associate(str, enc);
+ }
+
+ if (!NIL_P(orig)) {
+ rb_str_buf_append(str, orig);
+ }
+
+ return str;
+}
+
#ifdef NONASCII_MASK
#define is_utf8_lead_byte(c) (((c)&0xC0) != 0x80)
@@ -2138,7 +2277,7 @@ rb_str_empty(VALUE str)
* call-seq:
* string + other_string -> new_string
*
- * Returns a new \String containing +other_string+ concatenated to +self+:
+ * Returns a new +String+ containing +other_string+ concatenated to +self+:
*
* "Hello from " + self.to_s # => "Hello from main"
*
@@ -2178,8 +2317,8 @@ rb_str_plus(VALUE str1, VALUE str2)
VALUE
rb_str_opt_plus(VALUE str1, VALUE str2)
{
- assert(RBASIC_CLASS(str1) == rb_cString);
- assert(RBASIC_CLASS(str2) == rb_cString);
+ RUBY_ASSERT(RBASIC_CLASS(str1) == rb_cString);
+ RUBY_ASSERT(RBASIC_CLASS(str2) == rb_cString);
long len1, len2;
MAYBE_UNUSED(char) *ptr1, *ptr2;
RSTRING_GETMEM(str1, ptr1, len1);
@@ -2209,7 +2348,7 @@ rb_str_opt_plus(VALUE str1, VALUE str2)
* call-seq:
* string * integer -> new_string
*
- * Returns a new \String containing +integer+ copies of +self+:
+ * Returns a new +String+ containing +integer+ copies of +self+:
*
* "Ho! " * 3 # => "Ho! Ho! Ho! "
* "Ho! " * 0 # => ""
@@ -2284,7 +2423,7 @@ rb_str_times(VALUE str, VALUE times)
* "%05d" % 123 # => "00123"
*
* If +self+ contains multiple substitutions, +object+ must be
- * an \Array or \Hash containing the values to be substituted:
+ * an Array or Hash containing the values to be substituted:
*
* "%-5s: %016x" % [ "ID", self.object_id ] # => "ID : 00002b054ec93168"
* "foo = %{foo}" % {foo: 'bar'} # => "foo = bar"
@@ -2350,7 +2489,7 @@ str_make_independent_expand(VALUE str, long len, long expand, const int termlen)
STR_SET_EMBED(str);
memcpy(RSTRING(str)->as.embed.ary, ptr, len);
TERM_FILL(RSTRING(str)->as.embed.ary + len, termlen);
- STR_SET_EMBED_LEN(str, len);
+ STR_SET_LEN(str, len);
return;
}
@@ -2366,7 +2505,7 @@ str_make_independent_expand(VALUE str, long len, long expand, const int termlen)
FL_UNSET(str, STR_SHARED|STR_NOFREE);
TERM_FILL(ptr + len, termlen);
RSTRING(str)->as.heap.ptr = ptr;
- RSTRING(str)->as.heap.len = len;
+ STR_SET_LEN(str, len);
RSTRING(str)->as.heap.aux.capa = capa;
}
@@ -2418,7 +2557,7 @@ str_discard(VALUE str)
if (!STR_EMBED_P(str) && !FL_TEST(str, STR_SHARED|STR_NOFREE)) {
ruby_sized_xfree(STR_HEAP_PTR(str), STR_HEAP_SIZE(str));
RSTRING(str)->as.heap.ptr = 0;
- RSTRING(str)->as.heap.len = 0;
+ STR_SET_LEN(str, 0);
}
}
@@ -2495,7 +2634,7 @@ rb_str_change_terminator_length(VALUE str, const int oldtermlen, const int terml
long capa = str_capacity(str, oldtermlen) + oldtermlen;
long len = RSTRING_LEN(str);
- assert(capa >= len);
+ RUBY_ASSERT(capa >= len);
if (capa - len < termlen) {
rb_check_lockedtmp(str);
str_make_independent_expand(str, len, 0L, termlen);
@@ -2507,7 +2646,7 @@ rb_str_change_terminator_length(VALUE str, const int oldtermlen, const int terml
else {
if (!STR_EMBED_P(str)) {
/* modify capa instead of realloc */
- assert(!FL_TEST((str), STR_SHARED));
+ RUBY_ASSERT(!FL_TEST((str), STR_SHARED));
RSTRING(str)->as.heap.aux.capa = capa - termlen;
}
if (termlen > oldtermlen) {
@@ -2584,14 +2723,14 @@ rb_check_string_type(VALUE str)
* call-seq:
* String.try_convert(object) -> object, new_string, or nil
*
- * If +object+ is a \String object, returns +object+.
+ * If +object+ is a +String+ object, returns +object+.
*
* Otherwise if +object+ responds to <tt>:to_str</tt>,
* calls <tt>object.to_str</tt> and returns the result.
*
* Returns +nil+ if +object+ does not respond to <tt>:to_str</tt>.
*
- * Raises an exception unless <tt>object.to_str</tt> returns a \String object.
+ * Raises an exception unless <tt>object.to_str</tt> returns a +String+ object.
*/
static VALUE
rb_str_s_try_convert(VALUE dummy, VALUE str)
@@ -2740,19 +2879,34 @@ str_subseq(VALUE str, long beg, long len)
{
VALUE str2;
- const long rstring_embed_capa_max = ((sizeof(struct RString) - offsetof(struct RString, as.embed.ary)) / sizeof(char)) - 1;
+ RUBY_ASSERT(beg >= 0);
+ RUBY_ASSERT(len >= 0);
+ RUBY_ASSERT(beg+len <= RSTRING_LEN(str));
- if (!SHARABLE_SUBSTRING_P(beg, len, RSTRING_LEN(str)) ||
- len <= rstring_embed_capa_max) {
+ const int termlen = TERM_LEN(str);
+ if (!SHARABLE_SUBSTRING_P(beg, len, RSTRING_LEN(str))) {
str2 = rb_str_new(RSTRING_PTR(str) + beg, len);
RB_GC_GUARD(str);
+ return str2;
+ }
+
+ str2 = str_alloc_heap(rb_cString);
+ if (str_embed_capa(str2) >= len + termlen) {
+ char *ptr2 = RSTRING(str2)->as.embed.ary;
+ STR_SET_EMBED(str2);
+ memcpy(ptr2, RSTRING_PTR(str) + beg, len);
+ TERM_FILL(ptr2+len, termlen);
+
+ STR_SET_LEN(str2, len);
+ RB_GC_GUARD(str);
}
else {
- str2 = str_new_shared(rb_cString, str);
+ str_replace_shared(str2, str);
+ RUBY_ASSERT(!STR_EMBED_P(str2));
ENC_CODERANGE_CLEAR(str2);
RSTRING(str2)->as.heap.ptr += beg;
- if (RSTRING(str2)->as.heap.len > len) {
- RSTRING(str2)->as.heap.len = len;
+ if (RSTRING_LEN(str2) > len) {
+ STR_SET_LEN(str2, len);
}
}
@@ -2879,12 +3033,15 @@ str_substr(VALUE str, long beg, long len, int empty)
VALUE
rb_str_freeze(VALUE str)
{
+ if (CHILLED_STRING_P(str)) {
+ FL_UNSET_RAW(str, STR_CHILLED);
+ }
+
if (OBJ_FROZEN(str)) return str;
rb_str_resize(str, RSTRING_LEN(str));
return rb_obj_freeze(str);
}
-
/*
* call-seq:
* +string -> new_string or self
@@ -2911,7 +3068,7 @@ str_uplus(VALUE str)
*
* Returns a frozen, possibly pre-existing copy of the string.
*
- * The returned \String will be deduplicated as long as it does not have
+ * The returned +String+ will be deduplicated as long as it does not have
* any instance variables set on it and is not a String subclass.
*
* Note that <tt>-string</tt> variant is more convenient for defining
@@ -2957,7 +3114,7 @@ rb_str_unlocktmp(VALUE str)
return str;
}
-RUBY_FUNC_EXPORTED VALUE
+VALUE
rb_str_locktmp_ensure(VALUE str, VALUE (*func)(VALUE), VALUE arg)
{
rb_str_locktmp(str);
@@ -2977,6 +3134,33 @@ rb_str_set_len(VALUE str, long len)
if (len > (capa = (long)str_capacity(str, termlen)) || len < 0) {
rb_bug("probable buffer overflow: %ld for %ld", len, capa);
}
+
+ int cr = ENC_CODERANGE(str);
+ if (cr == ENC_CODERANGE_UNKNOWN) {
+ /* Leave unknown. */
+ }
+ else if (len > RSTRING_LEN(str)) {
+ if (ENC_CODERANGE_CLEAN_P(cr)) {
+ /* Update the coderange regarding the extended part. */
+ const char *const prev_end = RSTRING_END(str);
+ const char *const new_end = RSTRING_PTR(str) + len;
+ rb_encoding *enc = rb_enc_get(str);
+ rb_str_coderange_scan_restartable(prev_end, new_end, enc, &cr);
+ ENC_CODERANGE_SET(str, cr);
+ }
+ else if (cr == ENC_CODERANGE_BROKEN) {
+ /* May be valid now, by appended part. */
+ ENC_CODERANGE_SET(str, ENC_CODERANGE_UNKNOWN);
+ }
+ }
+ else if (len < RSTRING_LEN(str)) {
+ if (cr != ENC_CODERANGE_7BIT) {
+ /* ASCII-only string is keeping after truncated. Valid
+ * and broken may be invalid or valid, leave unknown. */
+ ENC_CODERANGE_SET(str, ENC_CODERANGE_UNKNOWN);
+ }
+ }
+
STR_SET_LEN(str, len);
TERM_FILL(&RSTRING_PTR(str)[len], termlen);
}
@@ -3001,7 +3185,7 @@ rb_str_resize(VALUE str, long len)
if (STR_EMBED_P(str)) {
if (len == slen) return str;
if (str_embed_capa(str) >= len + termlen) {
- STR_SET_EMBED_LEN(str, len);
+ STR_SET_LEN(str, len);
TERM_FILL(RSTRING(str)->as.embed.ary + len, termlen);
return str;
}
@@ -3013,7 +3197,7 @@ rb_str_resize(VALUE str, long len)
if (slen > len) slen = len;
if (slen > 0) MEMCPY(RSTRING(str)->as.embed.ary, ptr, char, slen);
TERM_FILL(RSTRING(str)->as.embed.ary + len, termlen);
- STR_SET_EMBED_LEN(str, len);
+ STR_SET_LEN(str, len);
if (independent) ruby_xfree(ptr);
return str;
}
@@ -3028,7 +3212,7 @@ rb_str_resize(VALUE str, long len)
RSTRING(str)->as.heap.aux.capa = len;
}
else if (len == slen) return str;
- RSTRING(str)->as.heap.len = len;
+ STR_SET_LEN(str, len);
TERM_FILL(RSTRING(str)->as.heap.ptr + len, termlen); /* sentinel */
}
return str;
@@ -3190,7 +3374,7 @@ rb_enc_cr_str_buf_cat(VALUE str, const char *ptr, long len,
incompatible:
rb_raise(rb_eEncCompatError, "incompatible character encodings: %s and %s",
- rb_enc_name(str_enc), rb_enc_name(ptr_enc));
+ rb_enc_inspect_name(str_enc), rb_enc_inspect_name(ptr_enc));
UNREACHABLE_RETURN(Qundef);
}
@@ -3235,6 +3419,7 @@ rb_str_buf_append(VALUE str, VALUE str2)
case ENC_CODERANGE_7BIT:
// If RHS is 7bit we can do simple concatenation
str_buf_cat4(str, RSTRING_PTR(str2), RSTRING_LEN(str2), true);
+ RB_GC_GUARD(str2);
return str;
case ENC_CODERANGE_VALID:
// If RHS is valid, we can do simple concatenation if encodings are the same
@@ -3244,6 +3429,7 @@ rb_str_buf_append(VALUE str, VALUE str2)
if (UNLIKELY(str_cr != ENC_CODERANGE_VALID)) {
ENC_CODERANGE_SET(str, RB_ENC_CODERANGE_AND(str_cr, str2_cr));
}
+ RB_GC_GUARD(str2);
return str;
}
}
@@ -3301,7 +3487,7 @@ rb_str_concat_literals(size_t num, const VALUE *strary)
* s.concat('bar', 'baz') # => "foobarbaz"
* s # => "foobarbaz"
*
- * For each given object +object+ that is an \Integer,
+ * For each given object +object+ that is an Integer,
* the value is considered a codepoint and converted to a character before concatenation:
*
* s = 'foo'
@@ -3340,7 +3526,7 @@ rb_str_concat_multi(int argc, VALUE *argv, VALUE str)
* s << 'bar' # => "foobar"
* s # => "foobar"
*
- * If +object+ is an \Integer,
+ * If +object+ is an Integer,
* the value is considered a codepoint and converted to a character before concatenation:
*
* s = 'foo'
@@ -3401,8 +3587,12 @@ rb_str_concat(VALUE str1, VALUE str2)
}
rb_str_resize(str1, pos+len);
memcpy(RSTRING_PTR(str1) + pos, buf, len);
- if (cr == ENC_CODERANGE_7BIT && code > 127)
+ if (cr == ENC_CODERANGE_7BIT && code > 127) {
cr = ENC_CODERANGE_VALID;
+ }
+ else if (cr == ENC_CODERANGE_BROKEN) {
+ cr = ENC_CODERANGE_UNKNOWN;
+ }
ENC_CODERANGE_SET(str1, cr);
}
return str1;
@@ -3465,11 +3655,12 @@ rb_str_prepend_multi(int argc, VALUE *argv, VALUE str)
st_index_t
rb_str_hash(VALUE str)
{
- int e = ENCODING_GET(str);
- if (e && is_ascii_string(str)) {
- e = 0;
+ st_index_t h = rb_memhash((const void *)RSTRING_PTR(str), RSTRING_LEN(str));
+ int e = RSTRING_LEN(str) ? ENCODING_GET(str) : 0;
+ if (e && !is_ascii_string(str)) {
+ h = rb_hash_end(rb_hash_uint32(h, (uint32_t)e));
}
- return rb_memhash((const void *)RSTRING_PTR(str), RSTRING_LEN(str)) ^ e;
+ return h;
}
int
@@ -3570,7 +3761,7 @@ rb_str_cmp(VALUE str1, VALUE str2)
* Returns +false+ if the two strings' encodings are not compatible:
* "\u{e4 f6 fc}".encode("ISO-8859-1") == ("\u{c4 d6 dc}") # => false
*
- * If +object+ is not an instance of \String but responds to +to_str+, then the
+ * If +object+ is not an instance of +String+ but responds to +to_str+, then the
* two strings are compared using <code>object.==</code>.
*/
@@ -3815,7 +4006,9 @@ strseq_core(const char *str_ptr, const char *str_ptr_end, long str_len,
return pos + offset;
}
+/* found index in byte */
#define rb_str_index(str, sub, offset) rb_strseq_index(str, sub, offset, 0)
+#define rb_str_byteindex(str, sub, offset) rb_strseq_index(str, sub, offset, 1)
static long
rb_strseq_index(VALUE str, VALUE sub, long offset, int in_byte)
@@ -3869,34 +4062,28 @@ rb_str_index_m(int argc, VALUE *argv, VALUE str)
{
VALUE sub;
VALUE initpos;
+ rb_encoding *enc = STR_ENC_GET(str);
long pos;
if (rb_scan_args(argc, argv, "11", &sub, &initpos) == 2) {
+ long slen = str_strlen(str, enc); /* str's enc */
pos = NUM2LONG(initpos);
- }
- else {
- pos = 0;
- }
- if (pos < 0) {
- pos += str_strlen(str, NULL);
- if (pos < 0) {
+ if (pos < 0 ? (pos += slen) < 0 : pos > slen) {
if (RB_TYPE_P(sub, T_REGEXP)) {
rb_backref_set(Qnil);
}
return Qnil;
}
}
+ else {
+ pos = 0;
+ }
if (RB_TYPE_P(sub, T_REGEXP)) {
- if (pos > str_strlen(str, NULL))
- return Qnil;
pos = str_offset(RSTRING_PTR(str), RSTRING_END(str), pos,
- rb_enc_check(str, sub), single_byte_optimizable(str));
+ enc, single_byte_optimizable(str));
- if (rb_reg_search(sub, str, pos, 0) < 0) {
- return Qnil;
- }
- else {
+ if (rb_reg_search(sub, str, pos, 0) >= 0) {
VALUE match = rb_backref_get();
struct re_registers *regs = RMATCH_REGS(match);
pos = rb_str_sublen(str, BEG(0));
@@ -3906,25 +4093,28 @@ rb_str_index_m(int argc, VALUE *argv, VALUE str)
else {
StringValue(sub);
pos = rb_str_index(str, sub, pos);
- pos = rb_str_sublen(str, pos);
+ if (pos >= 0) {
+ pos = rb_str_sublen(str, pos);
+ return LONG2NUM(pos);
+ }
}
-
- if (pos == -1) return Qnil;
- return LONG2NUM(pos);
+ return Qnil;
}
-/* whether given pos is valid character boundary or not
+/* Ensure that the given pos is a valid character boundary.
* Note that in this function, "character" means a code point
* (Unicode scalar value), not a grapheme cluster.
*/
-static bool
-str_check_byte_pos(VALUE str, long pos)
+static void
+str_ensure_byte_pos(VALUE str, long pos)
{
const char *s = RSTRING_PTR(str);
const char *e = RSTRING_END(str);
const char *p = s + pos;
- const char *pp = rb_enc_left_char_head(s, p, e, rb_enc_get(str));
- return p == pp;
+ if (!at_char_boundary(s, p, e, rb_enc_get(str))) {
+ rb_raise(rb_eIndexError,
+ "offset %ld does not land on character boundary", pos);
+ }
}
/*
@@ -3932,7 +4122,7 @@ str_check_byte_pos(VALUE str, long pos)
* byteindex(substring, offset = 0) -> integer or nil
* byteindex(regexp, offset = 0) -> integer or nil
*
- * Returns the \Integer byte-based index of the first occurrence of the given +substring+,
+ * Returns the Integer byte-based index of the first occurrence of the given +substring+,
* or +nil+ if none found:
*
* 'foo'.byteindex('f') # => 0
@@ -3940,7 +4130,7 @@ str_check_byte_pos(VALUE str, long pos)
* 'foo'.byteindex('oo') # => 1
* 'foo'.byteindex('ooo') # => nil
*
- * Returns the \Integer byte-based index of the first match for the given \Regexp +regexp+,
+ * Returns the Integer byte-based index of the first match for the given Regexp +regexp+,
* or +nil+ if none found:
*
* 'foo'.byteindex(/f/) # => 0
@@ -3948,7 +4138,7 @@ str_check_byte_pos(VALUE str, long pos)
* 'foo'.byteindex(/oo/) # => 1
* 'foo'.byteindex(/ooo/) # => nil
*
- * \Integer argument +offset+, if given, specifies the byte-based position in the
+ * Integer argument +offset+, if given, specifies the byte-based position in the
* string to begin the search:
*
* 'foo'.byteindex('o', 1) # => 1
@@ -3976,33 +4166,23 @@ rb_str_byteindex_m(int argc, VALUE *argv, VALUE str)
long pos;
if (rb_scan_args(argc, argv, "11", &sub, &initpos) == 2) {
+ long slen = RSTRING_LEN(str);
pos = NUM2LONG(initpos);
- }
- else {
- pos = 0;
- }
- if (pos < 0) {
- pos += RSTRING_LEN(str);
- if (pos < 0) {
+ if (pos < 0 ? (pos += slen) < 0 : pos > slen) {
if (RB_TYPE_P(sub, T_REGEXP)) {
rb_backref_set(Qnil);
}
return Qnil;
}
}
-
- if (!str_check_byte_pos(str, pos)) {
- rb_raise(rb_eIndexError,
- "offset %ld does not land on character boundary", pos);
+ else {
+ pos = 0;
}
+ str_ensure_byte_pos(str, pos);
+
if (RB_TYPE_P(sub, T_REGEXP)) {
- if (pos > RSTRING_LEN(str))
- return Qnil;
- if (rb_reg_search(sub, str, pos, 0) < 0) {
- return Qnil;
- }
- else {
+ if (rb_reg_search(sub, str, pos, 0) >= 0) {
VALUE match = rb_backref_get();
struct re_registers *regs = RMATCH_REGS(match);
pos = BEG(0);
@@ -4011,11 +4191,10 @@ rb_str_byteindex_m(int argc, VALUE *argv, VALUE str)
}
else {
StringValue(sub);
- pos = rb_strseq_index(str, sub, pos, 1);
+ pos = rb_str_byteindex(str, sub, pos);
+ if (pos >= 0) return LONG2NUM(pos);
}
-
- if (pos == -1) return Qnil;
- return LONG2NUM(pos);
+ return Qnil;
}
#ifdef HAVE_MEMRCHR
@@ -4074,6 +4253,7 @@ str_rindex(VALUE str, VALUE sub, const char *s, rb_encoding *enc)
}
#endif
+/* found index in byte */
static long
rb_str_rindex(VALUE str, VALUE sub, long pos)
{
@@ -4103,7 +4283,7 @@ rb_str_rindex(VALUE str, VALUE sub, long pos)
}
s = str_nth(sbeg, RSTRING_END(str), pos, enc, singlebyte);
- return rb_str_sublen(str, str_rindex(str, sub, s, enc));
+ return str_rindex(str, sub, s, enc);
}
/*
@@ -4111,7 +4291,7 @@ rb_str_rindex(VALUE str, VALUE sub, long pos)
* rindex(substring, offset = self.length) -> integer or nil
* rindex(regexp, offset = self.length) -> integer or nil
*
- * Returns the \Integer index of the _last_ occurrence of the given +substring+,
+ * Returns the Integer index of the _last_ occurrence of the given +substring+,
* or +nil+ if none found:
*
* 'foo'.rindex('f') # => 0
@@ -4119,7 +4299,7 @@ rb_str_rindex(VALUE str, VALUE sub, long pos)
* 'foo'.rindex('oo') # => 1
* 'foo'.rindex('ooo') # => nil
*
- * Returns the \Integer index of the _last_ match for the given \Regexp +regexp+,
+ * Returns the Integer index of the _last_ match for the given Regexp +regexp+,
* or +nil+ if none found:
*
* 'foo'.rindex(/f/) # => 0
@@ -4144,7 +4324,7 @@ rb_str_rindex(VALUE str, VALUE sub, long pos)
* 'foo'.index(/o+(?!.*o)/) # => 1
* $~ #=> #<MatchData "oo">
*
- * \Integer argument +offset+, if given and non-negative, specifies the maximum starting position in the
+ * Integer argument +offset+, if given and non-negative, specifies the maximum starting position in the
* string to _end_ the search:
*
* 'foo'.rindex('o', 0) # => nil
@@ -4152,7 +4332,7 @@ rb_str_rindex(VALUE str, VALUE sub, long pos)
* 'foo'.rindex('o', 2) # => 2
* 'foo'.rindex('o', 3) # => 2
*
- * If +offset+ is a negative \Integer, the maximum starting position in the
+ * If +offset+ is a negative Integer, the maximum starting position in the
* string to _end_ the search is the sum of the string's length and +offset+:
*
* 'foo'.rindex('o', -1) # => 2
@@ -4167,20 +4347,17 @@ static VALUE
rb_str_rindex_m(int argc, VALUE *argv, VALUE str)
{
VALUE sub;
- VALUE vpos;
+ VALUE initpos;
rb_encoding *enc = STR_ENC_GET(str);
long pos, len = str_strlen(str, enc); /* str's enc */
- if (rb_scan_args(argc, argv, "11", &sub, &vpos) == 2) {
- pos = NUM2LONG(vpos);
- if (pos < 0) {
- pos += len;
- if (pos < 0) {
- if (RB_TYPE_P(sub, T_REGEXP)) {
- rb_backref_set(Qnil);
- }
- return Qnil;
+ if (rb_scan_args(argc, argv, "11", &sub, &initpos) == 2) {
+ pos = NUM2LONG(initpos);
+ if (pos < 0 && (pos += len) < 0) {
+ if (RB_TYPE_P(sub, T_REGEXP)) {
+ rb_backref_set(Qnil);
}
+ return Qnil;
}
if (pos > len) pos = len;
}
@@ -4189,7 +4366,7 @@ rb_str_rindex_m(int argc, VALUE *argv, VALUE str)
}
if (RB_TYPE_P(sub, T_REGEXP)) {
- /* enc = rb_get_check(str, sub); */
+ /* enc = rb_enc_check(str, sub); */
pos = str_offset(RSTRING_PTR(str), RSTRING_END(str), pos,
enc, single_byte_optimizable(str));
@@ -4203,7 +4380,10 @@ rb_str_rindex_m(int argc, VALUE *argv, VALUE str)
else {
StringValue(sub);
pos = rb_str_rindex(str, sub, pos);
- if (pos >= 0) return LONG2NUM(pos);
+ if (pos >= 0) {
+ pos = rb_str_sublen(str, pos);
+ return LONG2NUM(pos);
+ }
}
return Qnil;
}
@@ -4244,7 +4424,7 @@ rb_str_byterindex(VALUE str, VALUE sub, long pos)
* byterindex(substring, offset = self.bytesize) -> integer or nil
* byterindex(regexp, offset = self.bytesize) -> integer or nil
*
- * Returns the \Integer byte-based index of the _last_ occurrence of the given +substring+,
+ * Returns the Integer byte-based index of the _last_ occurrence of the given +substring+,
* or +nil+ if none found:
*
* 'foo'.byterindex('f') # => 0
@@ -4252,7 +4432,7 @@ rb_str_byterindex(VALUE str, VALUE sub, long pos)
* 'foo'.byterindex('oo') # => 1
* 'foo'.byterindex('ooo') # => nil
*
- * Returns the \Integer byte-based index of the _last_ match for the given \Regexp +regexp+,
+ * Returns the Integer byte-based index of the _last_ match for the given Regexp +regexp+,
* or +nil+ if none found:
*
* 'foo'.byterindex(/f/) # => 0
@@ -4277,7 +4457,7 @@ rb_str_byterindex(VALUE str, VALUE sub, long pos)
* 'foo'.byteindex(/o+(?!.*o)/) # => 1
* $~ #=> #<MatchData "oo">
*
- * \Integer argument +offset+, if given and non-negative, specifies the maximum starting byte-based position in the
+ * Integer argument +offset+, if given and non-negative, specifies the maximum starting byte-based position in the
* string to _end_ the search:
*
* 'foo'.byterindex('o', 0) # => nil
@@ -4285,7 +4465,7 @@ rb_str_byterindex(VALUE str, VALUE sub, long pos)
* 'foo'.byterindex('o', 2) # => 2
* 'foo'.byterindex('o', 3) # => 2
*
- * If +offset+ is a negative \Integer, the maximum starting position in the
+ * If +offset+ is a negative Integer, the maximum starting position in the
* string to _end_ the search is the sum of the string's length and +offset+:
*
* 'foo'.byterindex('o', -1) # => 2
@@ -4303,19 +4483,16 @@ static VALUE
rb_str_byterindex_m(int argc, VALUE *argv, VALUE str)
{
VALUE sub;
- VALUE vpos;
+ VALUE initpos;
long pos, len = RSTRING_LEN(str);
- if (rb_scan_args(argc, argv, "11", &sub, &vpos) == 2) {
- pos = NUM2LONG(vpos);
- if (pos < 0) {
- pos += len;
- if (pos < 0) {
- if (RB_TYPE_P(sub, T_REGEXP)) {
- rb_backref_set(Qnil);
- }
- return Qnil;
+ if (rb_scan_args(argc, argv, "11", &sub, &initpos) == 2) {
+ pos = NUM2LONG(initpos);
+ if (pos < 0 && (pos += len) < 0) {
+ if (RB_TYPE_P(sub, T_REGEXP)) {
+ rb_backref_set(Qnil);
}
+ return Qnil;
}
if (pos > len) pos = len;
}
@@ -4323,10 +4500,7 @@ rb_str_byterindex_m(int argc, VALUE *argv, VALUE str)
pos = len;
}
- if (!str_check_byte_pos(str, pos)) {
- rb_raise(rb_eIndexError,
- "offset %ld does not land on character boundary", pos);
- }
+ str_ensure_byte_pos(str, pos);
if (RB_TYPE_P(sub, T_REGEXP)) {
if (rb_reg_search(sub, str, pos, 1) >= 0) {
@@ -4349,16 +4523,16 @@ rb_str_byterindex_m(int argc, VALUE *argv, VALUE str)
* string =~ regexp -> integer or nil
* string =~ object -> integer or nil
*
- * Returns the \Integer index of the first substring that matches
+ * Returns the Integer index of the first substring that matches
* the given +regexp+, or +nil+ if no match found:
*
* 'foo' =~ /f/ # => 0
* 'foo' =~ /o/ # => 1
* 'foo' =~ /x/ # => nil
*
- * Note: also updates Regexp@Special+global+variables.
+ * Note: also updates Regexp@Global+Variables.
*
- * If the given +object+ is not a \Regexp, returns the value
+ * If the given +object+ is not a Regexp, returns the value
* returned by <tt>object =~ self</tt>.
*
* Note that <tt>string =~ regexp</tt> is different from <tt>regexp =~ string</tt>
@@ -4396,13 +4570,13 @@ static VALUE get_pat(VALUE);
* match(pattern, offset = 0) -> matchdata or nil
* match(pattern, offset = 0) {|matchdata| ... } -> object
*
- * Returns a \MatchData object (or +nil+) based on +self+ and the given +pattern+.
+ * Returns a MatchData object (or +nil+) based on +self+ and the given +pattern+.
*
- * Note: also updates Regexp@Special+global+variables.
+ * Note: also updates Regexp@Global+Variables.
*
- * - Computes +regexp+ by converting +pattern+ (if not already a \Regexp).
+ * - Computes +regexp+ by converting +pattern+ (if not already a Regexp).
* regexp = Regexp.new(pattern)
- * - Computes +matchdata+, which will be either a \MatchData object or +nil+
+ * - Computes +matchdata+, which will be either a MatchData object or +nil+
* (see Regexp#match):
* matchdata = <tt>regexp.match(self)
*
@@ -4412,7 +4586,7 @@ static VALUE get_pat(VALUE);
* 'foo'.match('o') # => #<MatchData "o">
* 'foo'.match('x') # => nil
*
- * If \Integer argument +offset+ is given, the search begins at index +offset+:
+ * If Integer argument +offset+ is given, the search begins at index +offset+:
*
* 'foo'.match('f', 1) # => nil
* 'foo'.match('o', 1) # => #<MatchData "o">
@@ -4447,19 +4621,19 @@ rb_str_match_m(int argc, VALUE *argv, VALUE str)
*
* Returns +true+ or +false+ based on whether a match is found for +self+ and +pattern+.
*
- * Note: does not update Regexp@Special+global+variables.
+ * Note: does not update Regexp@Global+Variables.
*
- * Computes +regexp+ by converting +pattern+ (if not already a \Regexp).
+ * Computes +regexp+ by converting +pattern+ (if not already a Regexp).
* regexp = Regexp.new(pattern)
*
- * Returns +true+ if <tt>self+.match(regexp)</tt> returns a \MatchData object,
+ * Returns +true+ if <tt>self+.match(regexp)</tt> returns a MatchData object,
* +false+ otherwise:
*
* 'foo'.match?(/o/) # => true
* 'foo'.match?('o') # => true
* 'foo'.match?(/x/) # => false
*
- * If \Integer argument +offset+ is given, the search begins at index +offset+:
+ * If Integer argument +offset+ is given, the search begins at index +offset+:
* 'foo'.match?('f', 1) # => false
* 'foo'.match?('o', 1) # => true
*
@@ -4711,7 +4885,7 @@ static VALUE str_succ(VALUE str);
* s = '99zz99zz'
* s.succ # => "100aa00aa"
*
- * The successor to an empty \String is a new empty \String:
+ * The successor to an empty +String+ is a new empty +String+:
*
* ''.succ # => ""
*
@@ -4851,7 +5025,7 @@ str_upto_i(VALUE str, VALUE arg)
* upto(other_string, exclusive = false) {|string| ... } -> self
* upto(other_string, exclusive = false) -> new_enumerator
*
- * With a block given, calls the block with each \String value
+ * With a block given, calls the block with each +String+ value
* returned by successive calls to String#succ;
* the first value is +self+, the next is <tt>self.succ</tt>, and so on;
* the sequence terminates when value +other_string+ is reached;
@@ -4875,7 +5049,7 @@ str_upto_i(VALUE str, VALUE arg)
* '25'.upto('5') {|s| fail s }
* 'aa'.upto('a') {|s| fail s }
*
- * With no block given, returns a new \Enumerator:
+ * With no block given, returns a new Enumerator:
*
* 'a8'.upto('b6') # => #<Enumerator: "a8":upto("b6")>
*
@@ -5158,7 +5332,6 @@ rb_str_drop_bytes(VALUE str, long len)
char *oldptr = ptr;
int fl = (int)(RBASIC(str)->flags & (STR_NOEMBED|STR_SHARED|STR_NOFREE));
STR_SET_EMBED(str);
- STR_SET_EMBED_LEN(str, nlen);
ptr = RSTRING(str)->as.embed.ary;
memmove(ptr, oldptr + len, nlen);
if (fl == STR_NOEMBED) xfree(oldptr);
@@ -5170,9 +5343,12 @@ rb_str_drop_bytes(VALUE str, long len)
OBJ_FREEZE(shared);
}
ptr = RSTRING(str)->as.heap.ptr += len;
- RSTRING(str)->as.heap.len = nlen;
}
- ptr[nlen] = 0;
+ STR_SET_LEN(str, nlen);
+
+ if (!SHARABLE_MIDDLE_SUBSTRING) {
+ TERM_FILL(ptr + nlen, TERM_LEN(str));
+ }
ENC_CODERANGE_CLEAR(str);
return str;
}
@@ -5246,8 +5422,9 @@ rb_str_update(VALUE str, long beg, long len, VALUE val)
if (beg < 0) {
beg += slen;
}
- assert(beg >= 0);
- assert(beg <= slen);
+ RUBY_ASSERT(beg >= 0);
+ RUBY_ASSERT(beg <= slen);
+
if (len > slen - beg) {
len = slen - beg;
}
@@ -5385,11 +5562,11 @@ rb_str_aset_m(int argc, VALUE *argv, VALUE str)
*
* Inserts the given +other_string+ into +self+; returns +self+.
*
- * If the \Integer +index+ is positive, inserts +other_string+ at offset +index+:
+ * If the Integer +index+ is positive, inserts +other_string+ at offset +index+:
*
* 'foo'.insert(1, 'bar') # => "fbaroo"
*
- * If the \Integer +index+ is negative, counts backward from the end of +self+
+ * If the Integer +index+ is negative, counts backward from the end of +self+
* and inserts +other_string+ at offset <tt>index+1</tt>
* (that is, _after_ <tt>self[index]</tt>):
*
@@ -5574,7 +5751,7 @@ static long
rb_pat_search(VALUE pat, VALUE str, long pos, int set_backref_str)
{
if (BUILTIN_TYPE(pat) == T_STRING) {
- pos = rb_strseq_index(str, pat, pos, 1);
+ pos = rb_str_byteindex(str, pat, pos);
if (set_backref_str) {
if (pos >= 0) {
str = rb_str_new_frozen_String(str);
@@ -5677,8 +5854,8 @@ rb_str_sub_bang(int argc, VALUE *argv, VALUE str)
if (coderange_scan(p, beg0, str_enc) != ENC_CODERANGE_7BIT ||
coderange_scan(p+end0, len-end0, str_enc) != ENC_CODERANGE_7BIT) {
rb_raise(rb_eEncCompatError, "incompatible character encodings: %s and %s",
- rb_enc_name(str_enc),
- rb_enc_name(STR_ENC_GET(repl)));
+ rb_enc_inspect_name(str_enc),
+ rb_enc_inspect_name(STR_ENC_GET(repl)));
}
enc = STR_ENC_GET(repl);
}
@@ -5709,6 +5886,8 @@ rb_str_sub_bang(int argc, VALUE *argv, VALUE str)
TERM_FILL(&RSTRING_PTR(str)[len], TERM_LEN(str));
ENC_CODERANGE_SET(str, cr);
+ RB_GC_GUARD(match);
+
return str;
}
return Qnil;
@@ -5740,8 +5919,7 @@ rb_str_sub(int argc, VALUE *argv, VALUE str)
static VALUE
str_gsub(int argc, VALUE *argv, VALUE str, int bang)
{
- VALUE pat, val = Qnil, repl, match, match0 = Qnil, dest, hash = Qnil;
- struct re_registers *regs;
+ VALUE pat, val = Qnil, repl, match0 = Qnil, dest, hash = Qnil;
long beg, beg0, end0;
long offset, blen, slen, len, last;
enum {STR, ITER, MAP} mode = STR;
@@ -5786,8 +5964,8 @@ str_gsub(int argc, VALUE *argv, VALUE str, int bang)
ENC_CODERANGE_SET(dest, rb_enc_asciicompat(str_enc) ? ENC_CODERANGE_7BIT : ENC_CODERANGE_VALID);
do {
- match = rb_backref_get();
- regs = RMATCH_REGS(match);
+ VALUE match = rb_backref_get();
+ struct re_registers *regs = RMATCH_REGS(match);
if (RB_TYPE_P(pat, T_STRING)) {
beg0 = beg;
end0 = beg0 + RSTRING_LEN(pat);
@@ -5844,6 +6022,8 @@ str_gsub(int argc, VALUE *argv, VALUE str, int bang)
cp = RSTRING_PTR(str) + offset;
if (offset > RSTRING_LEN(str)) break;
beg = rb_pat_search(pat, str, offset, need_backref);
+
+ RB_GC_GUARD(match);
} while (beg >= 0);
if (RSTRING_LEN(str) > offset) {
rb_enc_str_buf_cat(dest, cp, RSTRING_LEN(str) - offset, str_enc);
@@ -5946,7 +6126,7 @@ rb_str_clear(VALUE str)
{
str_discard(str);
STR_SET_EMBED(str);
- STR_SET_EMBED_LEN(str, 0);
+ STR_SET_LEN(str, 0);
RSTRING_PTR(str)[0] = 0;
if (rb_enc_asciicompat(STR_ENC_GET(str)))
ENC_CODERANGE_SET(str, ENC_CODERANGE_7BIT);
@@ -6010,7 +6190,7 @@ rb_str_getbyte(VALUE str, VALUE index)
*
* Related: String#getbyte.
*/
-static VALUE
+VALUE
rb_str_setbyte(VALUE str, VALUE index, VALUE value)
{
long pos = NUM2LONG(index);
@@ -6106,6 +6286,12 @@ str_byte_substr(VALUE str, long beg, long len, int empty)
return str2;
}
+VALUE
+rb_str_byte_substr(VALUE str, VALUE beg, VALUE len)
+{
+ return str_byte_substr(str, NUM2LONG(beg), NUM2LONG(len), TRUE);
+}
+
static VALUE
str_byte_aref(VALUE str, VALUE indx)
{
@@ -6198,20 +6384,15 @@ str_check_beg_len(VALUE str, long *beg, long *len)
if (*beg < 0) {
*beg += slen;
}
- assert(*beg >= 0);
- assert(*beg <= slen);
+ RUBY_ASSERT(*beg >= 0);
+ RUBY_ASSERT(*beg <= slen);
+
if (*len > slen - *beg) {
*len = slen - *beg;
}
end = *beg + *len;
- if (!str_check_byte_pos(str, *beg)) {
- rb_raise(rb_eIndexError,
- "offset %ld does not land on character boundary", *beg);
- }
- if (!str_check_byte_pos(str, end)) {
- rb_raise(rb_eIndexError,
- "offset %ld does not land on character boundary", end);
- }
+ str_ensure_byte_pos(str, *beg);
+ str_ensure_byte_pos(str, end);
}
/*
@@ -6397,7 +6578,7 @@ rb_str_reverse_bang(VALUE str)
/*
* call-seq:
- * include? other_string -> true or false
+ * include?(other_string) -> true or false
*
* Returns +true+ if +self+ contains +other_string+, +false+ otherwise:
*
@@ -6471,7 +6652,7 @@ rb_str_to_i(int argc, VALUE *argv, VALUE str)
* Returns the result of interpreting leading characters in +self+ as a Float:
*
* '3.14159'.to_f # => 3.14159
- '1.234e-2'.to_f # => 0.01234
+ * '1.234e-2'.to_f # => 0.01234
*
* Characters past a leading valid number (in the given +base+) are ignored:
*
@@ -6494,8 +6675,8 @@ rb_str_to_f(VALUE str)
* call-seq:
* to_s -> self or string
*
- * Returns +self+ if +self+ is a \String,
- * or +self+ converted to a \String if +self+ is a subclass of \String.
+ * Returns +self+ if +self+ is a +String+,
+ * or +self+ converted to a +String+ if +self+ is a subclass of +String+.
*/
static VALUE
@@ -7130,6 +7311,8 @@ str_undump(VALUE str)
}
}
+ RB_GC_GUARD(str);
+
return undumped;
invalid_format:
rb_raise(rb_eRuntimeError, "invalid dumped string; not wrapped with '\"' nor '\"...\".force_encoding(\"...\")' form");
@@ -7744,8 +7927,10 @@ trnext(struct tr *t, rb_encoding *enc)
}
continue; /* not reached */
}
- t->gen = 1;
- t->max = c;
+ else if (t->now < c) {
+ t->gen = 1;
+ t->max = c;
+ }
}
}
return t->now;
@@ -7875,7 +8060,14 @@ tr_trans(VALUE str, VALUE src, VALUE repl, int sflag)
while (s < send) {
int may_modify = 0;
- c0 = c = rb_enc_codepoint_len((char *)s, (char *)send, &clen, e1);
+ int r = rb_enc_precise_mbclen((char *)s, (char *)send, e1);
+ if (!MBCLEN_CHARFOUND_P(r)) {
+ xfree(buf);
+ rb_raise(rb_eArgError, "invalid byte sequence in %s", rb_enc_name(e1));
+ }
+ clen = MBCLEN_CHARFOUND_LEN(r);
+ c0 = c = rb_enc_mbc_to_codepoint((char *)s, (char *)send, e1);
+
tlen = enc == e1 ? clen : rb_enc_codelen(c, enc);
s += clen;
@@ -7926,7 +8118,7 @@ tr_trans(VALUE str, VALUE src, VALUE repl, int sflag)
}
TERM_FILL((char *)t, termlen);
RSTRING(str)->as.heap.ptr = (char *)buf;
- RSTRING(str)->as.heap.len = t - buf;
+ STR_SET_LEN(str, t - buf);
STR_SET_NOEMBED(str);
RSTRING(str)->as.heap.aux.capa = max;
}
@@ -7955,7 +8147,15 @@ tr_trans(VALUE str, VALUE src, VALUE repl, int sflag)
while (s < send) {
int may_modify = 0;
- c0 = c = rb_enc_codepoint_len((char *)s, (char *)send, &clen, e1);
+
+ int r = rb_enc_precise_mbclen((char *)s, (char *)send, e1);
+ if (!MBCLEN_CHARFOUND_P(r)) {
+ xfree(buf);
+ rb_raise(rb_eArgError, "invalid byte sequence in %s", rb_enc_name(e1));
+ }
+ clen = MBCLEN_CHARFOUND_LEN(r);
+ c0 = c = rb_enc_mbc_to_codepoint((char *)s, (char *)send, e1);
+
tlen = enc == e1 ? clen : rb_enc_codelen(c, enc);
if (c < 256) {
@@ -8002,7 +8202,7 @@ tr_trans(VALUE str, VALUE src, VALUE repl, int sflag)
}
TERM_FILL((char *)t, termlen);
RSTRING(str)->as.heap.ptr = (char *)buf;
- RSTRING(str)->as.heap.len = t - buf;
+ STR_SET_LEN(str, t - buf);
STR_SET_NOEMBED(str);
RSTRING(str)->as.heap.aux.capa = max;
}
@@ -8670,7 +8870,6 @@ rb_str_split_m(int argc, VALUE *argv, VALUE str)
#define SPLIT_STR(beg, len) (empty_count = split_string(result, str, beg, len, empty_count))
- if (result) result = rb_ary_new();
beg = 0;
char *ptr = RSTRING_PTR(str);
char *eptr = RSTRING_END(str);
@@ -8679,6 +8878,7 @@ rb_str_split_m(int argc, VALUE *argv, VALUE str)
int skip = 1;
unsigned int c;
+ if (result) result = rb_ary_new();
end = beg;
if (is_ascii_string(str)) {
while (ptr < eptr) {
@@ -8738,6 +8938,7 @@ rb_str_split_m(int argc, VALUE *argv, VALUE str)
char *sptr = RSTRING_PTR(spat);
long slen = RSTRING_LEN(spat);
+ if (result) result = rb_ary_new();
mustnot_broken(str);
enc = rb_enc_check(str, spat);
while (ptr < eptr &&
@@ -8759,6 +8960,7 @@ rb_str_split_m(int argc, VALUE *argv, VALUE str)
char *str_start = ptr;
int n;
+ if (result) result = rb_ary_new_capa(RSTRING_LEN(str));
mustnot_broken(str);
enc = rb_enc_get(str);
while (ptr < eptr &&
@@ -8770,6 +8972,7 @@ rb_str_split_m(int argc, VALUE *argv, VALUE str)
beg = ptr - str_start;
}
else {
+ if (result) result = rb_ary_new();
long len = RSTRING_LEN(str);
long start = beg;
long idx;
@@ -9233,56 +9436,65 @@ static regex_t *
get_reg_grapheme_cluster(rb_encoding *enc)
{
int encidx = rb_enc_to_index(enc);
- regex_t *reg_grapheme_cluster = NULL;
- static regex_t *reg_grapheme_cluster_utf8 = NULL;
- /* synchronize */
- if (encidx == rb_utf8_encindex() && reg_grapheme_cluster_utf8) {
- reg_grapheme_cluster = reg_grapheme_cluster_utf8;
- }
- if (!reg_grapheme_cluster) {
- const OnigUChar source_ascii[] = "\\X";
- OnigErrorInfo einfo;
- const OnigUChar *source = source_ascii;
- size_t source_len = sizeof(source_ascii) - 1;
- switch (encidx) {
+ const OnigUChar source_ascii[] = "\\X";
+ const OnigUChar *source = source_ascii;
+ size_t source_len = sizeof(source_ascii) - 1;
+
+ switch (encidx) {
#define CHARS_16BE(x) (OnigUChar)((x)>>8), (OnigUChar)(x)
#define CHARS_16LE(x) (OnigUChar)(x), (OnigUChar)((x)>>8)
#define CHARS_32BE(x) CHARS_16BE((x)>>16), CHARS_16BE(x)
#define CHARS_32LE(x) CHARS_16LE(x), CHARS_16LE((x)>>16)
#define CASE_UTF(e) \
- case ENCINDEX_UTF_##e: { \
- static const OnigUChar source_UTF_##e[] = {CHARS_##e('\\'), CHARS_##e('X')}; \
- source = source_UTF_##e; \
- source_len = sizeof(source_UTF_##e); \
- break; \
- }
- CASE_UTF(16BE); CASE_UTF(16LE); CASE_UTF(32BE); CASE_UTF(32LE);
+ case ENCINDEX_UTF_##e: { \
+ static const OnigUChar source_UTF_##e[] = {CHARS_##e('\\'), CHARS_##e('X')}; \
+ source = source_UTF_##e; \
+ source_len = sizeof(source_UTF_##e); \
+ break; \
+ }
+ CASE_UTF(16BE); CASE_UTF(16LE); CASE_UTF(32BE); CASE_UTF(32LE);
#undef CASE_UTF
#undef CHARS_16BE
#undef CHARS_16LE
#undef CHARS_32BE
#undef CHARS_32LE
- }
- int r = onig_new(&reg_grapheme_cluster, source, source + source_len,
- ONIG_OPTION_DEFAULT, enc, OnigDefaultSyntax, &einfo);
- if (r) {
- UChar message[ONIG_MAX_ERROR_MESSAGE_LEN];
- onig_error_code_to_str(message, r, &einfo);
- rb_fatal("cannot compile grapheme cluster regexp: %s", (char *)message);
- }
- if (encidx == rb_utf8_encindex()) {
- reg_grapheme_cluster_utf8 = reg_grapheme_cluster;
- }
}
+
+ regex_t *reg_grapheme_cluster;
+ OnigErrorInfo einfo;
+ int r = onig_new(&reg_grapheme_cluster, source, source + source_len,
+ ONIG_OPTION_DEFAULT, enc, OnigDefaultSyntax, &einfo);
+ if (r) {
+ UChar message[ONIG_MAX_ERROR_MESSAGE_LEN];
+ onig_error_code_to_str(message, r, &einfo);
+ rb_fatal("cannot compile grapheme cluster regexp: %s", (char *)message);
+ }
+
return reg_grapheme_cluster;
}
+static regex_t *
+get_cached_reg_grapheme_cluster(rb_encoding *enc)
+{
+ int encidx = rb_enc_to_index(enc);
+ static regex_t *reg_grapheme_cluster_utf8 = NULL;
+
+ if (encidx == rb_utf8_encindex()) {
+ if (!reg_grapheme_cluster_utf8) {
+ reg_grapheme_cluster_utf8 = get_reg_grapheme_cluster(enc);
+ }
+
+ return reg_grapheme_cluster_utf8;
+ }
+
+ return NULL;
+}
+
static VALUE
rb_str_each_grapheme_cluster_size(VALUE str, VALUE args, VALUE eobj)
{
size_t grapheme_cluster_count = 0;
- regex_t *reg_grapheme_cluster = NULL;
rb_encoding *enc = get_encoding(str);
const char *ptr, *end;
@@ -9290,7 +9502,13 @@ rb_str_each_grapheme_cluster_size(VALUE str, VALUE args, VALUE eobj)
return rb_str_length(str);
}
- reg_grapheme_cluster = get_reg_grapheme_cluster(enc);
+ bool cached_reg_grapheme_cluster = true;
+ regex_t *reg_grapheme_cluster = get_cached_reg_grapheme_cluster(enc);
+ if (!reg_grapheme_cluster) {
+ reg_grapheme_cluster = get_reg_grapheme_cluster(enc);
+ cached_reg_grapheme_cluster = false;
+ }
+
ptr = RSTRING_PTR(str);
end = RSTRING_END(str);
@@ -9303,6 +9521,10 @@ rb_str_each_grapheme_cluster_size(VALUE str, VALUE args, VALUE eobj)
ptr += len;
}
+ if (!cached_reg_grapheme_cluster) {
+ onig_free(reg_grapheme_cluster);
+ }
+
return SIZET2NUM(grapheme_cluster_count);
}
@@ -9310,7 +9532,6 @@ static VALUE
rb_str_enumerate_grapheme_clusters(VALUE str, VALUE ary)
{
VALUE orig = str;
- regex_t *reg_grapheme_cluster = NULL;
rb_encoding *enc = get_encoding(str);
const char *ptr0, *ptr, *end;
@@ -9319,7 +9540,14 @@ rb_str_enumerate_grapheme_clusters(VALUE str, VALUE ary)
}
if (!ary) str = rb_str_new_frozen(str);
- reg_grapheme_cluster = get_reg_grapheme_cluster(enc);
+
+ bool cached_reg_grapheme_cluster = true;
+ regex_t *reg_grapheme_cluster = get_cached_reg_grapheme_cluster(enc);
+ if (!reg_grapheme_cluster) {
+ reg_grapheme_cluster = get_reg_grapheme_cluster(enc);
+ cached_reg_grapheme_cluster = false;
+ }
+
ptr0 = ptr = RSTRING_PTR(str);
end = RSTRING_END(str);
@@ -9331,6 +9559,11 @@ rb_str_enumerate_grapheme_clusters(VALUE str, VALUE ary)
ENUM_ELEM(ary, rb_str_subseq(str, ptr-ptr0, len));
ptr += len;
}
+
+ if (!cached_reg_grapheme_cluster) {
+ onig_free(reg_grapheme_cluster);
+ }
+
RB_GC_GUARD(str);
if (ary)
return ary;
@@ -9526,7 +9759,7 @@ chompped_length(VALUE str, VALUE rs)
if (p[len-1] == newline &&
(rslen <= 1 ||
memcmp(rsptr, pp, rslen) == 0)) {
- if (rb_enc_left_char_head(p, pp, e, enc) == pp)
+ if (at_char_boundary(p, pp, e, enc))
return len - rslen;
RB_GC_GUARD(rs);
}
@@ -9581,7 +9814,7 @@ rb_str_chomp_bang(int argc, VALUE *argv, VALUE str)
{
VALUE rs;
str_modifiable(str);
- if (RSTRING_LEN(str) == 0) return Qnil;
+ if (RSTRING_LEN(str) == 0 && argc < 2) return Qnil;
rs = chomp_rs(argc, argv);
if (NIL_P(rs)) return Qnil;
return rb_str_chomp_string(str, rs);
@@ -9848,11 +10081,11 @@ rb_str_strip(VALUE str)
static VALUE
scan_once(VALUE str, VALUE pat, long *start, int set_backref_str)
{
- VALUE result, match;
- struct re_registers *regs;
- int i;
+ VALUE result = Qnil;
long end, pos = rb_pat_search(pat, str, *start, set_backref_str);
if (pos >= 0) {
+ VALUE match;
+ struct re_registers *regs;
if (BUILTIN_TYPE(pat) == T_STRING) {
regs = NULL;
end = pos + RSTRING_LEN(pat);
@@ -9863,6 +10096,7 @@ scan_once(VALUE str, VALUE pat, long *start, int set_backref_str)
pos = BEG(0);
end = END(0);
}
+
if (pos == end) {
rb_encoding *enc = STR_ENC_GET(str);
/*
@@ -9877,22 +10111,27 @@ scan_once(VALUE str, VALUE pat, long *start, int set_backref_str)
else {
*start = end;
}
+
if (!regs || regs->num_regs == 1) {
result = rb_str_subseq(str, pos, end - pos);
return result;
}
- result = rb_ary_new2(regs->num_regs);
- for (i=1; i < regs->num_regs; i++) {
- VALUE s = Qnil;
- if (BEG(i) >= 0) {
- s = rb_str_subseq(str, BEG(i), END(i)-BEG(i));
+ else {
+ result = rb_ary_new2(regs->num_regs);
+ for (int i = 1; i < regs->num_regs; i++) {
+ VALUE s = Qnil;
+ if (BEG(i) >= 0) {
+ s = rb_str_subseq(str, BEG(i), END(i)-BEG(i));
+ }
+
+ rb_ary_push(result, s);
}
- rb_ary_push(result, s);
}
- return result;
+ RB_GC_GUARD(match);
}
- return Qnil;
+
+ return result;
}
@@ -10437,7 +10676,6 @@ rb_str_rpartition(VALUE str, VALUE sep)
if (pos < 0) {
goto failed;
}
- pos = rb_str_offset(str, pos);
}
return rb_ary_new3(3, rb_str_subseq(str, 0, pos),
@@ -10468,10 +10706,20 @@ rb_str_start_with(int argc, VALUE *argv, VALUE str)
return Qtrue;
}
else {
+ const char *p, *s, *e;
+ long slen, tlen;
+ rb_encoding *enc;
+
StringValue(tmp);
- rb_enc_check(str, tmp);
- if (RSTRING_LEN(str) < RSTRING_LEN(tmp)) continue;
- if (memcmp(RSTRING_PTR(str), RSTRING_PTR(tmp), RSTRING_LEN(tmp)) == 0)
+ enc = rb_enc_check(str, tmp);
+ if ((tlen = RSTRING_LEN(tmp)) == 0) return Qtrue;
+ if ((slen = RSTRING_LEN(str)) < tlen) continue;
+ p = RSTRING_PTR(str);
+ e = p + slen;
+ s = p + tlen;
+ if (!at_char_right_boundary(p, s, e, enc))
+ continue;
+ if (memcmp(p, RSTRING_PTR(tmp), tlen) == 0)
return Qtrue;
}
}
@@ -10490,12 +10738,13 @@ static VALUE
rb_str_end_with(int argc, VALUE *argv, VALUE str)
{
int i;
- char *p, *s, *e;
- rb_encoding *enc;
for (i=0; i<argc; i++) {
VALUE tmp = argv[i];
+ const char *p, *s, *e;
long slen, tlen;
+ rb_encoding *enc;
+
StringValue(tmp);
enc = rb_enc_check(str, tmp);
if ((tlen = RSTRING_LEN(tmp)) == 0) return Qtrue;
@@ -10503,9 +10752,9 @@ rb_str_end_with(int argc, VALUE *argv, VALUE str)
p = RSTRING_PTR(str);
e = p + slen;
s = e - tlen;
- if (rb_enc_left_char_head(p, s, e, enc) != s)
+ if (!at_char_boundary(p, s, e, enc))
continue;
- if (memcmp(s, RSTRING_PTR(tmp), RSTRING_LEN(tmp)) == 0)
+ if (memcmp(s, RSTRING_PTR(tmp), tlen) == 0)
return Qtrue;
}
return Qfalse;
@@ -10523,12 +10772,17 @@ rb_str_end_with(int argc, VALUE *argv, VALUE str)
static long
deleted_prefix_length(VALUE str, VALUE prefix)
{
- char *strptr, *prefixptr;
+ const char *strptr, *prefixptr;
long olen, prefixlen;
+ rb_encoding *enc = rb_enc_get(str);
StringValue(prefix);
- if (is_broken_string(prefix)) return 0;
- rb_enc_check(str, prefix);
+
+ if (!is_broken_string(prefix) ||
+ !rb_enc_asciicompat(enc) ||
+ !rb_enc_asciicompat(rb_enc_get(prefix))) {
+ enc = rb_enc_check(str, prefix);
+ }
/* return 0 if not start with prefix */
prefixlen = RSTRING_LEN(prefix);
@@ -10538,6 +10792,19 @@ deleted_prefix_length(VALUE str, VALUE prefix)
strptr = RSTRING_PTR(str);
prefixptr = RSTRING_PTR(prefix);
if (memcmp(strptr, prefixptr, prefixlen) != 0) return 0;
+ if (is_broken_string(prefix)) {
+ if (!is_broken_string(str)) {
+ /* prefix in a valid string cannot be broken */
+ return 0;
+ }
+ const char *strend = strptr + olen;
+ const char *after_prefix = strptr + prefixlen;
+ if (!at_char_right_boundary(strptr, after_prefix, strend, enc)) {
+ /* prefix does not end at char-boundary */
+ return 0;
+ }
+ }
+ /* prefix part in `str` also should be valid. */
return prefixlen;
}
@@ -10594,7 +10861,7 @@ rb_str_delete_prefix(VALUE str, VALUE prefix)
static long
deleted_suffix_length(VALUE str, VALUE suffix)
{
- char *strptr, *suffixptr, *s;
+ const char *strptr, *suffixptr;
long olen, suffixlen;
rb_encoding *enc;
@@ -10609,9 +10876,10 @@ deleted_suffix_length(VALUE str, VALUE suffix)
if (olen < suffixlen) return 0;
strptr = RSTRING_PTR(str);
suffixptr = RSTRING_PTR(suffix);
- s = strptr + olen - suffixlen;
- if (memcmp(s, suffixptr, suffixlen) != 0) return 0;
- if (rb_enc_left_char_head(strptr, s, strptr + olen, enc) != s) return 0;
+ const char *strend = strptr + olen;
+ const char *before_suffix = strend - suffixlen;
+ if (memcmp(before_suffix, suffixptr, suffixlen) != 0) return 0;
+ if (!at_char_boundary(strptr, before_suffix, strend, enc)) return 0;
return suffixlen;
}
@@ -10683,7 +10951,7 @@ rb_fs_setter(VALUE val, ID id, VALUE *var)
rb_id2str(id));
}
if (!NIL_P(val)) {
- rb_warn_deprecated("`$;'", NULL);
+ rb_warn_deprecated("'$;'", NULL);
}
*var = val;
}
@@ -10701,7 +10969,23 @@ static VALUE
rb_str_force_encoding(VALUE str, VALUE enc)
{
str_modifiable(str);
- rb_enc_associate(str, rb_to_encoding(enc));
+
+ rb_encoding *encoding = rb_to_encoding(enc);
+ int idx = rb_enc_to_index(encoding);
+
+ // If the encoding is unchanged, we do nothing.
+ if (ENCODING_GET(str) == idx) {
+ return str;
+ }
+
+ rb_enc_associate_index(str, idx);
+
+ // If the coderange was 7bit and the new encoding is ASCII-compatible
+ // we can keep the coderange.
+ if (ENC_CODERANGE(str) == ENC_CODERANGE_7BIT && encoding && rb_enc_asciicompat(encoding)) {
+ return str;
+ }
+
ENC_CODERANGE_CLEAR(str);
return str;
}
@@ -10718,11 +11002,11 @@ static VALUE
rb_str_b(VALUE str)
{
VALUE str2;
- if (FL_TEST(str, STR_NOEMBED)) {
- str2 = str_alloc_heap(rb_cString);
+ if (STR_EMBED_P(str)) {
+ str2 = str_alloc_embed(rb_cString, RSTRING_LEN(str) + TERM_LEN(str));
}
else {
- str2 = str_alloc_embed(rb_cString, RSTRING_EMBED_LEN(str) + TERM_LEN(str));
+ str2 = str_alloc_heap(rb_cString);
}
str_replace_shared_without_enc(str2, str);
@@ -10836,7 +11120,7 @@ str_compat_and_valid(VALUE str, rb_encoding *enc)
rb_encoding *e = STR_ENC_GET(str);
if (cr == ENC_CODERANGE_7BIT ? rb_enc_mbminlen(enc) != 1 : enc != e) {
rb_raise(rb_eEncCompatError, "incompatible character encodings: %s and %s",
- rb_enc_name(enc), rb_enc_name(e));
+ rb_enc_inspect_name(enc), rb_enc_inspect_name(e));
}
}
return str;
@@ -11245,17 +11529,17 @@ rb_str_unicode_normalized_p(int argc, VALUE *argv, VALUE str)
/**********************************************************************
* Document-class: Symbol
*
- * Symbol objects represent named identifiers inside the Ruby interpreter.
+ * A +Symbol+ object represents a named identifier inside the Ruby interpreter.
*
- * You can create a \Symbol object explicitly with:
+ * You can create a +Symbol+ object explicitly with:
*
* - A {symbol literal}[rdoc-ref:syntax/literals.rdoc@Symbol+Literals].
*
- * The same Symbol object will be
+ * The same +Symbol+ object will be
* created for a given name or string for the duration of a program's
* execution, regardless of the context or meaning of that name. Thus
* if <code>Fred</code> is a constant in one context, a method in
- * another, and a class in a third, the Symbol <code>:Fred</code>
+ * another, and a class in a third, the +Symbol+ <code>:Fred</code>
* will be the same object in all three contexts.
*
* module One
@@ -11298,18 +11582,18 @@ rb_str_unicode_normalized_p(int argc, VALUE *argv, VALUE str)
* local_variables
* # => [:seven]
*
- * Symbol objects are different from String objects in that
- * Symbol objects represent identifiers, while String objects
- * represent text or data.
+ * A +Symbol+ object differs from a String object in that
+ * a +Symbol+ object represents an identifier, while a String object
+ * represents text or data.
*
* == What's Here
*
- * First, what's elsewhere. \Class \Symbol:
+ * First, what's elsewhere. \Class +Symbol+:
*
* - Inherits from {class Object}[rdoc-ref:Object@What-27s+Here].
* - Includes {module Comparable}[rdoc-ref:Comparable@What-27s+Here].
*
- * Here, class \Symbol provides methods that are useful for:
+ * Here, class +Symbol+ provides methods that are useful for:
*
* - {Querying}[rdoc-ref:Symbol@Methods+for+Querying]
* - {Comparing}[rdoc-ref:Symbol@Methods+for+Comparing]
@@ -11468,26 +11752,25 @@ sym_inspect(VALUE sym)
}
else {
rb_encoding *enc = STR_ENC_GET(str);
- RSTRING_GETMEM(str, ptr, len);
+ VALUE orig_str = str;
+
+ len = RSTRING_LEN(orig_str);
str = rb_enc_str_new(0, len + 1, enc);
+
+ // Get data pointer after allocation
+ ptr = RSTRING_PTR(orig_str);
dest = RSTRING_PTR(str);
memcpy(dest + 1, ptr, len);
+
+ RB_GC_GUARD(orig_str);
}
dest[0] = ':';
+
+ RUBY_ASSERT_BUILTIN_TYPE(str, T_STRING);
+
return str;
}
-/*
- * call-seq:
- * to_s -> string
- *
- * Returns a string representation of +self+ (not including the leading colon):
- *
- * :foo.to_s # => "foo"
- *
- * Related: Symbol#inspect, Symbol#name.
- */
-
VALUE
rb_sym_to_s(VALUE sym)
{
@@ -11859,7 +12142,7 @@ rb_interned_str_cstr(const char *ptr)
VALUE
rb_enc_interned_str(const char *ptr, long len, rb_encoding *enc)
{
- if (UNLIKELY(rb_enc_autoload_p(enc))) {
+ if (enc != NULL && UNLIKELY(rb_enc_autoload_p(enc))) {
rb_enc_autoload(enc);
}
@@ -11877,10 +12160,11 @@ void
Init_String(void)
{
rb_cString = rb_define_class("String", rb_cObject);
- assert(rb_vm_fstring_table());
+ RUBY_ASSERT(rb_vm_fstring_table());
st_foreach(rb_vm_fstring_table(), fstring_set_class_i, rb_cString);
rb_include_module(rb_cString, rb_mComparable);
rb_define_alloc_func(rb_cString, empty_str_alloc);
+ rb_define_singleton_method(rb_cString, "new", rb_str_s_new, -1);
rb_define_singleton_method(rb_cString, "try_convert", rb_str_s_try_convert, 1);
rb_define_method(rb_cString, "initialize", rb_str_init, -1);
rb_define_method(rb_cString, "initialize_copy", rb_str_replace, 1);
@@ -11925,6 +12209,7 @@ Init_String(void)
rb_define_method(rb_cString, "freeze", rb_str_freeze, 0);
rb_define_method(rb_cString, "+@", str_uplus, 0);
rb_define_method(rb_cString, "-@", str_uminus, 0);
+ rb_define_method(rb_cString, "dup", rb_str_dup_m, 0);
rb_define_alias(rb_cString, "dedup", "-@");
rb_define_method(rb_cString, "to_i", rb_str_to_i, -1);
@@ -12052,8 +12337,6 @@ Init_String(void)
rb_define_method(rb_cSymbol, "==", sym_equal, 1);
rb_define_method(rb_cSymbol, "===", sym_equal, 1);
rb_define_method(rb_cSymbol, "inspect", sym_inspect, 0);
- rb_define_method(rb_cSymbol, "to_s", rb_sym_to_s, 0);
- rb_define_method(rb_cSymbol, "id2name", rb_sym_to_s, 0);
rb_define_method(rb_cSymbol, "name", rb_sym2str, 0); /* in symbol.c */
rb_define_method(rb_cSymbol, "to_proc", rb_sym_to_proc, 0); /* in proc.c */
rb_define_method(rb_cSymbol, "succ", sym_succ, 0);
diff --git a/string.rb b/string.rb
index be10b407b0..00d76cbe03 100644
--- a/string.rb
+++ b/string.rb
@@ -1,11 +1,11 @@
-# A \String object has an arbitrary sequence of bytes,
+# A +String+ object has an arbitrary sequence of bytes,
# typically representing text or binary data.
-# A \String object may be created using String::new or as literals.
+# A +String+ object may be created using String::new or as literals.
#
# String objects differ from Symbol objects in that Symbol objects are
# designed to be used as identifiers, instead of text or data.
#
-# You can create a \String object explicitly with:
+# You can create a +String+ object explicitly with:
#
# - A {string literal}[rdoc-ref:syntax/literals.rdoc@String+Literals].
# - A {heredoc literal}[rdoc-ref:syntax/literals.rdoc@Here+Document+Literals].
@@ -14,7 +14,7 @@
#
# - \Method #String.
#
-# Some \String methods modify +self+.
+# Some +String+ methods modify +self+.
# Typically, a method whose name ends with <tt>!</tt> modifies +self+
# and returns +self+;
# often a similarly named method (without the <tt>!</tt>)
@@ -64,19 +64,19 @@
#
# 'THX1138'.gsub('\d+', '00') # => "THX1138"
#
-# <b>\String +replacement+</b>
+# <b>+String+ +replacement+</b>
#
# If +replacement+ is a string, that string will determine
# the replacing string that is to be substituted for the matched text.
#
# Each of the examples above uses a simple string as the replacing string.
#
-# \String +replacement+ may contain back-references to the pattern's captures:
+# +String+ +replacement+ may contain back-references to the pattern's captures:
#
# - <tt>\n</tt> (_n_ a non-negative integer) refers to <tt>$n</tt>.
# - <tt>\k<name></tt> refers to the named capture +name+.
#
-# See rdoc-ref:regexp.rdoc for details.
+# See Regexp for details.
#
# Note that within the string +replacement+, a character combination
# such as <tt>$&</tt> is treated as ordinary text, and not as
@@ -93,7 +93,7 @@
# - <tt>\\+</tt> corresponds to <tt>$+</tt>,
# which contains last capture group.
#
-# See rdoc-ref:regexp.rdoc for details.
+# See Regexp for details.
#
# Note that <tt>\\\\</tt> is interpreted as an escape, i.e., a single backslash.
#
@@ -139,7 +139,7 @@
#
# == Whitespace in Strings
#
-# In class \String, _whitespace_ is defined as a contiguous sequence of characters
+# In class +String+, _whitespace_ is defined as a contiguous sequence of characters
# consisting of any mixture of the following:
#
# - NL (null): <tt>"\x00"</tt>, <tt>"\u0000"</tt>.
@@ -157,7 +157,7 @@
# - #rstrip, #rstrip!: strip trailing whitespace.
# - #strip, #strip!: strip leading and trailing whitespace.
#
-# == \String Slices
+# == +String+ Slices
#
# A _slice_ of a string is a substring that is selected by certain criteria.
#
@@ -265,7 +265,7 @@
#
# <b><tt>string[regexp, capture = 0]</tt></b>
#
-# When the \Regexp argument +regexp+ is given,
+# When the Regexp argument +regexp+ is given,
# and the +capture+ argument is <tt>0</tt>,
# the slice is the first matching substring found in +self+:
#
@@ -278,7 +278,7 @@
# If argument +capture+ is given and not <tt>0</tt>,
# it should be either an capture group index (integer)
# or a capture group name (string or symbol);
-# the slice is the specified capture (see Regexp@Capturing):
+# the slice is the specified capture (see Regexp@Groups+and+Captures):
#
# s = 'hello there'
# s[/[aeiou](.)\1/, 1] # => "l"
@@ -290,7 +290,7 @@
#
# <b><tt>string[substring]</tt></b>
#
-# When the single \String argument +substring+ is given,
+# When the single +String+ argument +substring+ is given,
# returns the substring from +self+ if found, otherwise +nil+:
#
# 'foo'['oo'] # => "oo"
@@ -298,12 +298,12 @@
#
# == What's Here
#
-# First, what's elsewhere. \Class \String:
+# First, what's elsewhere. \Class +String+:
#
# - Inherits from {class Object}[rdoc-ref:Object@What-27s+Here].
# - Includes {module Comparable}[rdoc-ref:Comparable@What-27s+Here].
#
-# Here, class \String provides methods that are useful for:
+# Here, class +String+ provides methods that are useful for:
#
# - {Creating a String}[rdoc-ref:String@Methods+for+Creating+a+String]
# - {Frozen/Unfrozen Strings}[rdoc-ref:String@Methods+for+a+Frozen-2FUnfrozen+String]
@@ -314,7 +314,7 @@
# - {Converting to Non-String}[rdoc-ref:String@Methods+for+Converting+to+Non--5CString]
# - {Iterating}[rdoc-ref:String@Methods+for+Iterating]
#
-# === Methods for Creating a \String
+# === Methods for Creating a +String+
#
# - ::new: Returns a new string.
# - ::try_convert: Returns a new string created from a given object.
@@ -374,7 +374,7 @@
# - #casecmp?: Returns +true+ if the string is equal to a given string after Unicode case folding;
# +false+ otherwise.
#
-# === Methods for Modifying a \String
+# === Methods for Modifying a +String+
#
# Each of these methods modifies +self+.
#
@@ -428,9 +428,9 @@
# - #chop!: Removes trailing newline characters if found; otherwise removes the last character;
# returns +self+ if any changes, +nil+ otherwise.
#
-# === Methods for Converting to New \String
+# === Methods for Converting to New +String+
#
-# Each of these methods returns a new \String based on +self+,
+# Each of these methods returns a new +String+ based on +self+,
# often just a modified copy of +self+.
#
# _Extension_
@@ -494,12 +494,12 @@
#
# _Duplication_
#
-# - #to_s, $to_str: If +self+ is a subclass of \String, returns +self+ copied into a \String;
+# - #to_s, $to_str: If +self+ is a subclass of +String+, returns +self+ copied into a +String+;
# otherwise, returns +self+.
#
-# === Methods for Converting to Non-\String
+# === Methods for Converting to Non-+String+
#
-# Each of these methods converts the contents of +self+ to a non-\String.
+# Each of these methods converts the contents of +self+ to a non-+String+.
#
# <em>Characters, Bytes, and Clusters</em>
#
diff --git a/struct.c b/struct.c
index 3ee4408393..544228f76b 100644
--- a/struct.c
+++ b/struct.c
@@ -18,7 +18,6 @@
#include "internal/proc.h"
#include "internal/struct.h"
#include "internal/symbol.h"
-#include "transient_heap.h"
#include "vm_core.h"
#include "builtin.h"
@@ -137,7 +136,7 @@ struct_set_members(VALUE klass, VALUE /* frozen hidden array */ members)
j = struct_member_pos_probe(j, mask);
}
}
- OBJ_FREEZE_RAW(back);
+ OBJ_FREEZE(back);
}
rb_ivar_set(klass, id_members, members);
rb_ivar_set(klass, id_back_members, back);
@@ -237,7 +236,7 @@ rb_struct_getmember(VALUE obj, ID id)
if (i != -1) {
return RSTRUCT_GET(obj, i);
}
- rb_name_err_raise("`%1$s' is not a struct member", obj, ID2SYM(id));
+ rb_name_err_raise("'%1$s' is not a struct member", obj, ID2SYM(id));
UNREACHABLE_RETURN(Qnil);
}
@@ -274,7 +273,7 @@ new_struct(VALUE name, VALUE super)
rb_warn("redefining constant %"PRIsVALUE"::%"PRIsVALUE, super, name);
rb_mod_remove_const(super, ID2SYM(id));
}
- return rb_define_class_id_under(super, id, super);
+ return rb_define_class_id_under_no_pin(super, id, super);
}
NORETURN(static void invalid_struct_pos(VALUE s, VALUE idx));
@@ -423,7 +422,7 @@ struct_make_members_list(va_list ar)
}
ary = rb_hash_keys(list);
RBASIC_CLEAR_CLASS(ary);
- OBJ_FREEZE_RAW(ary);
+ OBJ_FREEZE(ary);
return ary;
}
@@ -492,8 +491,13 @@ rb_struct_define(const char *name, ...)
ary = struct_make_members_list(ar);
va_end(ar);
- if (!name) st = anonymous_struct(rb_cStruct);
- else st = new_struct(rb_str_new2(name), rb_cStruct);
+ if (!name) {
+ st = anonymous_struct(rb_cStruct);
+ }
+ else {
+ st = new_struct(rb_str_new2(name), rb_cStruct);
+ rb_vm_register_global_object(st);
+ }
return setup_struct(st, ary);
}
@@ -507,7 +511,7 @@ rb_struct_define_under(VALUE outer, const char *name, ...)
ary = struct_make_members_list(ar);
va_end(ar);
- return setup_struct(rb_define_class_under(outer, name, rb_cStruct), ary);
+ return setup_struct(rb_define_class_id_under(outer, rb_intern(name), rb_cStruct), ary);
}
/*
@@ -562,7 +566,7 @@ rb_struct_define_under(VALUE outer, const char *name, ...)
*
* <b>Member Names</b>
*
- * \Symbol arguments +member_names+
+ * Symbol arguments +member_names+
* determines the members of the new subclass:
*
* Struct.new(:foo, :bar).members # => [:foo, :bar]
@@ -678,7 +682,7 @@ rb_struct_s_def(int argc, VALUE *argv, VALUE klass)
}
rest = rb_hash_keys(rest);
RBASIC_CLEAR_CLASS(rest);
- OBJ_FREEZE_RAW(rest);
+ OBJ_FREEZE(rest);
if (NIL_P(name)) {
st = anonymous_struct(klass);
}
@@ -790,7 +794,7 @@ VALUE
rb_struct_initialize(VALUE self, VALUE values)
{
rb_struct_initialize_m(RARRAY_LENINT(values), RARRAY_CONST_PTR(values), self);
- if (rb_obj_is_kind_of(self, rb_cData)) OBJ_FREEZE_RAW(self);
+ if (rb_obj_is_kind_of(self, rb_cData)) OBJ_FREEZE(self);
RB_GC_GUARD(values);
return Qnil;
}
@@ -798,40 +802,9 @@ rb_struct_initialize(VALUE self, VALUE values)
static VALUE *
struct_heap_alloc(VALUE st, size_t len)
{
- VALUE *ptr = rb_transient_heap_alloc((VALUE)st, sizeof(VALUE) * len);
-
- if (ptr) {
- RSTRUCT_TRANSIENT_SET(st);
- return ptr;
- }
- else {
- RSTRUCT_TRANSIENT_UNSET(st);
- return ALLOC_N(VALUE, len);
- }
+ return ALLOC_N(VALUE, len);
}
-#if USE_TRANSIENT_HEAP
-void
-rb_struct_transient_heap_evacuate(VALUE obj, int promote)
-{
- if (RSTRUCT_TRANSIENT_P(obj)) {
- const VALUE *old_ptr = rb_struct_const_heap_ptr(obj);
- VALUE *new_ptr;
- long len = RSTRUCT_LEN(obj);
-
- if (promote) {
- new_ptr = ALLOC_N(VALUE, len);
- FL_UNSET_RAW(obj, RSTRUCT_TRANSIENT_FLAG);
- }
- else {
- new_ptr = struct_heap_alloc(obj, len);
- }
- MEMCPY(new_ptr, old_ptr, VALUE, len);
- RSTRUCT(obj)->as.heap.ptr = new_ptr;
- }
-}
-#endif
-
static VALUE
struct_alloc(VALUE klass)
{
@@ -849,7 +822,7 @@ struct_alloc(VALUE klass)
return (VALUE)st;
}
else {
- NEWOBJ_OF(st, struct RStruct, klass, flags, embedded_size, 0);
+ NEWOBJ_OF(st, struct RStruct, klass, flags, sizeof(struct RStruct), 0);
st->as.heap.ptr = struct_heap_alloc((VALUE)st, n);
rb_mem_clear((VALUE *)st->as.heap.ptr, n);
@@ -1712,7 +1685,7 @@ rb_data_s_def(int argc, VALUE *argv, VALUE klass)
}
rest = rb_hash_keys(rest);
RBASIC_CLEAR_CLASS(rest);
- OBJ_FREEZE_RAW(rest);
+ OBJ_FREEZE(rest);
data_class = anonymous_struct(klass);
setup_data(data_class, rest);
if (rb_block_given_p()) {
@@ -1722,6 +1695,20 @@ rb_data_s_def(int argc, VALUE *argv, VALUE klass)
return data_class;
}
+VALUE
+rb_data_define(VALUE super, ...)
+{
+ va_list ar;
+ VALUE ary;
+ va_start(ar, super);
+ ary = struct_make_members_list(ar);
+ va_end(ar);
+ if (!super) super = rb_cData;
+ VALUE klass = setup_data(anonymous_struct(super), ary);
+ rb_vm_register_global_object(klass);
+ return klass;
+}
+
/*
* call-seq:
* DataClass::members -> array_of_symbols
@@ -1815,7 +1802,7 @@ rb_data_initialize_m(int argc, const VALUE *argv, VALUE self)
rb_hash_foreach(argv[0], struct_hash_set_i, (VALUE)&arg);
// Freeze early before potentially raising, so that we don't leave an
// unfrozen copy on the heap, which could get exposed via ObjectSpace.
- OBJ_FREEZE_RAW(self);
+ OBJ_FREEZE(self);
if (arg.unknown_keywords != Qnil) {
rb_exc_raise(rb_keyword_error_new("unknown", arg.unknown_keywords));
}
@@ -1827,7 +1814,7 @@ static VALUE
rb_data_init_copy(VALUE copy, VALUE s)
{
copy = rb_struct_init_copy(copy, s);
- RB_OBJ_FREEZE_RAW(copy);
+ RB_OBJ_FREEZE(copy);
return copy;
}
@@ -1871,7 +1858,7 @@ rb_data_with(int argc, const VALUE *argv, VALUE self)
}
VALUE h = rb_struct_to_h(self);
- rb_hash_update_by(h, kwargs, NULL);
+ rb_hash_update_by(h, kwargs, 0);
return rb_class_new_instance_kw(1, &h, rb_obj_class(self), TRUE);
}
diff --git a/symbol.c b/symbol.c
index 5ccda88a6e..7126154bf8 100644
--- a/symbol.c
+++ b/symbol.c
@@ -23,10 +23,18 @@
#include "vm_sync.h"
#include "builtin.h"
-#ifndef USE_SYMBOL_GC
+#if defined(USE_SYMBOL_GC) && !(USE_SYMBOL_GC+0)
+# undef USE_SYMBOL_GC
+# define USE_SYMBOL_GC 0
+#else
+# undef USE_SYMBOL_GC
# define USE_SYMBOL_GC 1
#endif
-#ifndef SYMBOL_DEBUG
+#if defined(SYMBOL_DEBUG) && (SYMBOL_DEBUG+0)
+# undef SYMBOL_DEBUG
+# define SYMBOL_DEBUG 1
+#else
+# undef SYMBOL_DEBUG
# define SYMBOL_DEBUG 0
#endif
#ifndef CHECK_ID_SERIAL
@@ -87,12 +95,12 @@ Init_sym(void)
VALUE dsym_fstrs = rb_ident_hash_new();
symbols->dsymbol_fstr_hash = dsym_fstrs;
- rb_gc_register_mark_object(dsym_fstrs);
+ rb_vm_register_global_object(dsym_fstrs);
rb_obj_hide(dsym_fstrs);
symbols->str_sym = st_init_table_with_size(&symhash, 1000);
symbols->ids = rb_ary_hidden_new(0);
- rb_gc_register_mark_object(symbols->ids);
+ rb_vm_register_global_object(symbols->ids);
Init_op_tbl();
Init_id();
@@ -217,10 +225,10 @@ rb_sym_constant_char_p(const char *name, long nlen, rb_encoding *enc)
if (!MBCLEN_CHARFOUND_P(c)) return FALSE;
len = MBCLEN_CHARFOUND_LEN(c);
c = rb_enc_mbc_to_codepoint(name, end, enc);
+ if (rb_enc_isupper(c, enc)) return TRUE;
+ if (rb_enc_islower(c, enc)) return FALSE;
if (ONIGENC_IS_UNICODE(enc)) {
static int ctype_titlecase = 0;
- if (rb_enc_isupper(c, enc)) return TRUE;
- if (rb_enc_islower(c, enc)) return FALSE;
if (!ctype_titlecase) {
static const UChar cname[] = "titlecaseletter";
static const UChar *const end = cname + sizeof(cname) - 1;
@@ -422,6 +430,9 @@ static void
set_id_entry(rb_symbols_t *symbols, rb_id_serial_t num, VALUE str, VALUE sym)
{
ASSERT_vm_locking();
+ RUBY_ASSERT_BUILTIN_TYPE(str, T_STRING);
+ RUBY_ASSERT_BUILTIN_TYPE(sym, T_SYMBOL);
+
size_t idx = num / ID_ENTRY_UNIT;
VALUE ary, ids = symbols->ids;
@@ -452,8 +463,7 @@ get_id_serial_entry(rb_id_serial_t num, ID id, const enum id_entry_type t)
if (NIL_P(result)) {
result = 0;
}
- else {
-#if CHECK_ID_SERIAL
+ else if (CHECK_ID_SERIAL) {
if (id) {
VALUE sym = result;
if (t != ID_ENTRY_SYM)
@@ -465,13 +475,25 @@ get_id_serial_entry(rb_id_serial_t num, ID id, const enum id_entry_type t)
if (RSYMBOL(sym)->id != id) result = 0;
}
}
-#endif
}
}
}
}
GLOBAL_SYMBOLS_LEAVE();
+ if (result) {
+ switch (t) {
+ case ID_ENTRY_STR:
+ RUBY_ASSERT_BUILTIN_TYPE(result, T_STRING);
+ break;
+ case ID_ENTRY_SYM:
+ RUBY_ASSERT_BUILTIN_TYPE(result, T_SYMBOL);
+ break;
+ default:
+ break;
+ }
+ }
+
return result;
}
@@ -500,7 +522,6 @@ rb_id_serial_to_id(rb_id_serial_t num)
}
}
-#if SYMBOL_DEBUG
static int
register_sym_update_callback(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
{
@@ -511,19 +532,29 @@ register_sym_update_callback(st_data_t *key, st_data_t *value, st_data_t arg, in
*value = arg;
return ST_CONTINUE;
}
-#endif
static void
register_sym(rb_symbols_t *symbols, VALUE str, VALUE sym)
{
ASSERT_vm_locking();
-#if SYMBOL_DEBUG
- st_update(symbols->str_sym, (st_data_t)str,
- register_sym_update_callback, (st_data_t)sym);
-#else
- st_add_direct(symbols->str_sym, (st_data_t)str, (st_data_t)sym);
-#endif
+ if (SYMBOL_DEBUG) {
+ st_update(symbols->str_sym, (st_data_t)str,
+ register_sym_update_callback, (st_data_t)sym);
+ }
+ else {
+ st_add_direct(symbols->str_sym, (st_data_t)str, (st_data_t)sym);
+ }
+}
+
+void
+rb_free_static_symid_str(void)
+{
+ GLOBAL_SYMBOLS_ENTER(symbols)
+ {
+ st_free_table(symbols->str_sym);
+ }
+ GLOBAL_SYMBOLS_LEAVE();
}
static void
@@ -566,11 +597,14 @@ register_static_symid_str(ID id, VALUE str)
}
static int
-sym_check_asciionly(VALUE str)
+sym_check_asciionly(VALUE str, bool fake_str)
{
if (!rb_enc_asciicompat(rb_enc_get(str))) return FALSE;
switch (rb_enc_str_coderange(str)) {
case ENC_CODERANGE_BROKEN:
+ if (fake_str) {
+ str = rb_enc_str_new(RSTRING_PTR(str), RSTRING_LEN(str), rb_enc_get(str));
+ }
rb_raise(rb_eEncodingError, "invalid symbol in encoding %s :%+"PRIsVALUE,
rb_enc_name(rb_enc_get(str)), str);
case ENC_CODERANGE_7BIT:
@@ -612,22 +646,23 @@ dsymbol_alloc(rb_symbols_t *symbols, const VALUE klass, const VALUE str, rb_enco
{
ASSERT_vm_locking();
- const VALUE dsym = rb_newobj_of(klass, T_SYMBOL | FL_WB_PROTECTED);
+ NEWOBJ_OF(obj, struct RSymbol, klass, T_SYMBOL | FL_WB_PROTECTED, sizeof(struct RSymbol), 0);
+
long hashval;
- rb_enc_set_index(dsym, rb_enc_to_index(enc));
- OBJ_FREEZE(dsym);
- RB_OBJ_WRITE(dsym, &RSYMBOL(dsym)->fstr, str);
- RSYMBOL(dsym)->id = type;
+ rb_enc_set_index((VALUE)obj, rb_enc_to_index(enc));
+ OBJ_FREEZE((VALUE)obj);
+ RB_OBJ_WRITE((VALUE)obj, &obj->fstr, str);
+ obj->id = type;
/* we want hashval to be in Fixnum range [ruby-core:15713] r15672 */
hashval = (long)rb_str_hash(str);
- RSYMBOL(dsym)->hashval = RSHIFT((long)hashval, 1);
- register_sym(symbols, str, dsym);
+ obj->hashval = RSHIFT((long)hashval, 1);
+ register_sym(symbols, str, (VALUE)obj);
rb_hash_aset(symbols->dsymbol_fstr_hash, str, Qtrue);
- RUBY_DTRACE_CREATE_HOOK(SYMBOL, RSTRING_PTR(RSYMBOL(dsym)->fstr));
+ RUBY_DTRACE_CREATE_HOOK(SYMBOL, RSTRING_PTR(obj->fstr));
- return dsym;
+ return (VALUE)obj;
}
static inline VALUE
@@ -763,7 +798,7 @@ intern_str(VALUE str, int mutable)
id = rb_str_symname_type(str, IDSET_ATTRSET_FOR_INTERN);
if (id == (ID)-1) id = ID_JUNK;
- if (sym_check_asciionly(str)) {
+ if (sym_check_asciionly(str, false)) {
if (!mutable) str = rb_str_dup(str);
rb_enc_associate(str, rb_usascii_encoding());
}
@@ -824,7 +859,7 @@ rb_gc_free_dsymbol(VALUE sym)
* str.intern -> symbol
* str.to_sym -> symbol
*
- * Returns the Symbol corresponding to <i>str</i>, creating the
+ * Returns the +Symbol+ corresponding to <i>str</i>, creating the
* symbol if it did not previously exist. See Symbol#id2name.
*
* "Koala".intern #=> :Koala
@@ -843,12 +878,7 @@ VALUE
rb_str_intern(VALUE str)
{
VALUE sym;
-#if USE_SYMBOL_GC
- rb_encoding *enc, *ascii;
- int type;
-#else
- ID id;
-#endif
+
GLOBAL_SYMBOLS_ENTER(symbols);
{
sym = lookup_str_sym_with_lock(symbols, str);
@@ -856,11 +886,10 @@ rb_str_intern(VALUE str)
if (sym) {
// ok
}
- else {
-#if USE_SYMBOL_GC
- enc = rb_enc_get(str);
- ascii = rb_usascii_encoding();
- if (enc != ascii && sym_check_asciionly(str)) {
+ else if (USE_SYMBOL_GC) {
+ rb_encoding *enc = rb_enc_get(str);
+ rb_encoding *ascii = rb_usascii_encoding();
+ if (enc != ascii && sym_check_asciionly(str, false)) {
str = rb_str_dup(str);
rb_enc_associate(str, ascii);
OBJ_FREEZE(str);
@@ -871,13 +900,13 @@ rb_str_intern(VALUE str)
OBJ_FREEZE(str);
}
str = rb_fstring(str);
- type = rb_str_symname_type(str, IDSET_ATTRSET_FOR_INTERN);
+ int type = rb_str_symname_type(str, IDSET_ATTRSET_FOR_INTERN);
if (type < 0) type = ID_JUNK;
sym = dsymbol_alloc(symbols, rb_cSymbol, str, enc, type);
-#else
- id = intern_str(str, 0);
+ }
+ else {
+ ID id = intern_str(str, 0);
sym = ID2SYM(id);
-#endif
}
}
GLOBAL_SYMBOLS_LEAVE();
@@ -940,12 +969,17 @@ rb_id2sym(ID x)
VALUE
rb_sym2str(VALUE sym)
{
+ VALUE str;
if (DYNAMIC_SYM_P(sym)) {
- return RSYMBOL(sym)->fstr;
+ str = RSYMBOL(sym)->fstr;
+ RUBY_ASSERT_BUILTIN_TYPE(str, T_STRING);
}
else {
- return rb_id2str(STATIC_SYM2ID(sym));
+ str = rb_id2str(STATIC_SYM2ID(sym));
+ if (str) RUBY_ASSERT_BUILTIN_TYPE(str, T_STRING);
}
+
+ return str;
}
VALUE
@@ -1107,7 +1141,7 @@ rb_check_id(volatile VALUE *namep)
*namep = name;
}
- sym_check_asciionly(name);
+ sym_check_asciionly(name, false);
return lookup_str_id(name);
}
@@ -1127,10 +1161,12 @@ rb_get_symbol_id(VALUE name)
return 0;
}
}
- else {
- RUBY_ASSERT_ALWAYS(RB_TYPE_P(name, T_STRING));
+ else if (RB_TYPE_P(name, T_STRING)) {
return lookup_str_id(name);
}
+ else {
+ return 0;
+ }
}
@@ -1166,7 +1202,7 @@ rb_check_symbol(volatile VALUE *namep)
*namep = name;
}
- sym_check_asciionly(name);
+ sym_check_asciionly(name, false);
if ((sym = lookup_str_sym(name)) != 0) {
return sym;
@@ -1181,7 +1217,7 @@ rb_check_id_cstr(const char *ptr, long len, rb_encoding *enc)
struct RString fake_str;
const VALUE name = rb_setup_fake_str(&fake_str, ptr, len, enc);
- sym_check_asciionly(name);
+ sym_check_asciionly(name, true);
return lookup_str_id(name);
}
@@ -1193,7 +1229,7 @@ rb_check_symbol_cstr(const char *ptr, long len, rb_encoding *enc)
struct RString fake_str;
const VALUE name = rb_setup_fake_str(&fake_str, ptr, len, enc);
- sym_check_asciionly(name);
+ sym_check_asciionly(name, true);
if ((sym = lookup_str_sym(name)) != 0) {
return sym;
diff --git a/symbol.h b/symbol.h
index ffbf7ce198..3649f125bf 100644
--- a/symbol.h
+++ b/symbol.h
@@ -100,7 +100,7 @@ sym_type(VALUE sym)
#define is_class_sym(sym) (sym_type(sym)==ID_CLASS)
#define is_junk_sym(sym) (sym_type(sym)==ID_JUNK)
-RUBY_FUNC_EXPORTED const unsigned int ruby_global_name_punct_bits[(0x7e - 0x20 + 31) / 32];
+RUBY_FUNC_EXPORTED const uint_least32_t ruby_global_name_punct_bits[(0x7e - 0x20 + 31) / 32];
static inline int
is_global_name_punct(const int c)
@@ -109,10 +109,9 @@ is_global_name_punct(const int c)
return (ruby_global_name_punct_bits[(c - 0x20) / 32] >> (c % 32)) & 1;
}
-int rb_enc_symname_type(const char *name, long len, rb_encoding *enc, unsigned int allowed_attrset);
-
RUBY_SYMBOL_EXPORT_BEGIN
+int rb_enc_symname_type(const char *name, long len, rb_encoding *enc, unsigned int allowed_attrset);
size_t rb_sym_immortal_count(void);
RUBY_SYMBOL_EXPORT_END
diff --git a/symbol.rb b/symbol.rb
index f31bf0bee3..bfac11ae21 100644
--- a/symbol.rb
+++ b/symbol.rb
@@ -1,5 +1,20 @@
class Symbol
# call-seq:
+ # to_s -> string
+ #
+ # Returns a string representation of +self+ (not including the leading colon):
+ #
+ # :foo.to_s # => "foo"
+ #
+ # Related: Symbol#inspect, Symbol#name.
+ def to_s
+ Primitive.attr! :leaf
+ Primitive.cexpr! 'rb_sym_to_s(self)'
+ end
+
+ alias id2name to_s
+
+ # call-seq:
# to_sym -> self
#
# Returns +self+.
diff --git a/template/Doxyfile.tmpl b/template/Doxyfile.tmpl
index 87fe5b8f48..cea28d04c7 100644
--- a/template/Doxyfile.tmpl
+++ b/template/Doxyfile.tmpl
@@ -1,4 +1,4 @@
-# Doxyfile 1.9.3
+# Doxyfile 1.9.7
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@@ -12,6 +12,16 @@
# For lists, items can also be appended using:
# TAG += value [value, ...]
# Values that contain spaces should be placed between quotes (\" \").
+#
+# Note:
+#
+# Use doxygen to compare the used configuration file with the template
+# configuration file:
+# doxygen -x [configFile]
+# Use doxygen to compare the used configuration file with the template
+# configuration file without replacing the environment variables or CMake type
+# replacement variables:
+# doxygen -x_noenv [configFile]
<%
srcdir = miniruby = nil
@@ -77,16 +87,28 @@ PROJECT_LOGO =
OUTPUT_DIRECTORY = doc/capi
-# 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
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 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.
+# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to
+# control the number of sub-directories.
# The default value is: NO.
CREATE_SUBDIRS = YES
+# Controls the number of sub-directories that will be created when
+# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every
+# level increment doubles the number of directories, resulting in 4096
+# directories at level 8 which is the default and also the maximum value. The
+# sub-directories are organized in 2 levels, the first level always has a fixed
+# number of 16 directories.
+# Minimum value: 0, maximum value: 8, default value: 8.
+# This tag requires that the tag CREATE_SUBDIRS is set to YES.
+
+CREATE_SUBDIRS_LEVEL = 8
+
# 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
@@ -98,14 +120,14 @@ 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.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian,
+# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English
+# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek,
+# Hindi, 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
@@ -354,6 +376,17 @@ MARKDOWN_SUPPORT = YES
TOC_INCLUDE_HEADINGS = 5
+# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to
+# generate identifiers for the Markdown headings. Note: Every identifier is
+# unique.
+# Possible values are: DOXYGEN Use a fixed 'autotoc_md' string followed by a
+# sequence number starting at 0. and GITHUB Use the lower case version of title
+# with any whitespace replaced by '-' and punctations characters removed..
+# The default value is: DOXYGEN.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+MARKDOWN_ID_STYLE = GITHUB
+
# 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
@@ -465,7 +498,7 @@ TYPEDEF_HIDES_STRUCT = NO
LOOKUP_CACHE_SIZE = 0
-# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use
+# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use
# during processing. When set to 0 doxygen will based this on the number of
# cores available in the system. You can set it explicitly to a value larger
# than 0 to get more control over the balance between CPU load and processing
@@ -478,6 +511,14 @@ LOOKUP_CACHE_SIZE = 0
NUM_PROC_THREADS = 1
+# If the TIMESTAMP tag is set different from NO then each generated page will
+# contain the date or date and time when the page was generated. Setting this to
+# NO can help when comparing the output of multiple runs.
+# Possible values are: YES, NO, DATETIME and DATE.
+# The default value is: NO.
+
+TIMESTAMP = NO
+
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
@@ -559,7 +600,8 @@ 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.
+# will also hide undocumented C++ concepts if enabled. This option has no effect
+# if EXTRACT_ALL is enabled.
# The default value is: NO.
HIDE_UNDOC_CLASSES = NO
@@ -579,7 +621,7 @@ HIDE_FRIEND_COMPOUNDS = NO
HIDE_IN_BODY_DOCS = YES
# 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
+# @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.
@@ -590,14 +632,15 @@ INTERNAL_DOCS = NO
# filesystem is case sensitive (i.e. it supports files in the same directory
# whose names only differ in casing), the option must be set to YES to properly
# deal with such files in case they appear in the input. For filesystems that
-# are not case sensitive the option should be be set to NO to properly deal with
+# are not case sensitive the option should be set to NO to properly deal with
# output files written for symbols that only differ in casing, such as for two
# classes, one named CLASS and the other named Class, and to also support
# references to files without having to specify the exact matching casing. On
# Windows (including Cygwin) and MacOS, users should typically set this option
# to NO, whereas on Linux or other Unix flavors it should typically be set to
# YES.
-# The default value is: system dependent.
+# Possible values are: SYSTEM, NO and YES.
+# The default value is: SYSTEM.
CASE_SENSE_NAMES = NO
@@ -849,11 +892,26 @@ WARN_IF_INCOMPLETE_DOC = YES
WARN_NO_PARAMDOC = NO
+# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about
+# undocumented enumeration values. If set to NO, doxygen will accept
+# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: NO.
+
+WARN_IF_UNDOC_ENUM_VAL = NO
+
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
# at the end of the doxygen process doxygen will return with a non-zero status.
-# Possible values are: NO, YES and FAIL_ON_WARNINGS.
+# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves
+# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not
+# write the warning messages in between other messages but write them at the end
+# of a run, in case a WARN_LOGFILE is defined the warning messages will be
+# besides being in the defined file also be shown at the end of a run, unless
+# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case
+# the behavior will remain as with the setting FAIL_ON_WARNINGS.
+# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.
# The default value is: NO.
WARN_AS_ERROR = NO
@@ -864,13 +922,27 @@ WARN_AS_ERROR = NO
# 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)
+# See also: WARN_LINE_FORMAT
# The default value is: $file:$line: $text.
WARN_FORMAT = "$file:$line: $text"
+# In the $text part of the WARN_FORMAT command it is possible that a reference
+# to a more specific place is given. To make it easier to jump to this place
+# (outside of doxygen) the user can define a custom "cut" / "paste" string.
+# Example:
+# WARN_LINE_FORMAT = "'vi $file +$line'"
+# See also: WARN_FORMAT
+# The default value is: at line $line of file $file.
+
+WARN_LINE_FORMAT = "at line $line of file $file"
+
# 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).
+# error (stderr). In case the file specified cannot be opened for writing the
+# warning and error messages are written to standard error. When as file - is
+# specified the warning and error messages are written to standard output
+# (stdout).
WARN_LOGFILE =
@@ -893,10 +965,21 @@ INPUT += .
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
# documentation (see:
# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
+# See also: INPUT_FILE_ENCODING
# The default value is: UTF-8.
INPUT_ENCODING = UTF-8
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify
+# character encoding on a per file pattern basis. Doxygen will compare the file
+# name with each pattern and apply the encoding instead of the default
+# INPUT_ENCODING) if there is a match. The character encodings are a list of the
+# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding
+# "INPUT_ENCODING" for further information on supported encodings.
+
+INPUT_FILE_ENCODING =
+
# 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.
@@ -978,10 +1061,7 @@ EXCLUDE_PATTERNS += yarvtest
# (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/*
+# ANamespace::AClass, ANamespace::*Test
EXCLUDE_SYMBOLS =
@@ -1026,6 +1106,11 @@ IMAGE_PATH = <%=srcdir%>/doc/images
# 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 doxygen will use the data processed and written to standard output
+# for further processing, therefore nothing else, like debug statements or used
+# commands (so in case of a Windows batch file always use @echo OFF), should be
+# written to standard output.
+#
# 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.
@@ -1067,6 +1152,15 @@ FILTER_SOURCE_PATTERNS =
USE_MDFILE_AS_MAINPAGE =
+# The Fortran standard specifies that for fixed formatted Fortran code all
+# characters from position 72 are to be considered as comment. A common
+# extension is to allow longer lines before the automatic comment starts. The
+# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can
+# be processed before the automatic comment starts.
+# Minimum value: 7, maximum value: 10000, default value: 72.
+
+FORTRAN_COMMENT_AFTER = 72
+
#---------------------------------------------------------------------------
# Configuration options related to source browsing
#---------------------------------------------------------------------------
@@ -1164,10 +1258,11 @@ VERBATIM_HEADERS = YES
ALPHABETICAL_INDEX = YES
-# 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.
+# 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. The IGNORE_PREFIX
+# tag works for classes, function and member names. The entity will be placed in
+# the alphabetical list under the first letter of the entity name that remains
+# after removing the prefix.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
IGNORE_PREFIX =
@@ -1246,7 +1341,12 @@ HTML_STYLESHEET =
# 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.
+# list).
+# Note: Since the styling of scrollbars can currently not be overruled in
+# Webkit/Chromium, the styling will be left out of the default doxygen.css if
+# one or more extra stylesheets have been specified. So if scrollbar
+# customization is desired it has to be added explicitly. For an example see the
+# documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET =
@@ -1261,6 +1361,19 @@ HTML_EXTRA_STYLESHEET =
HTML_EXTRA_FILES =
+# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
+# should be rendered with a dark or light theme.
+# Possible values are: LIGHT always generate light mode output, DARK always
+# generate dark mode output, AUTO_LIGHT automatically set the mode according to
+# the user preference, use light mode if no preference is set (the default),
+# AUTO_DARK automatically set the mode according to the user preference, use
+# dark mode if no preference is set and TOGGLE allow to user to switch between
+# light and dark mode via a button.
+# The default value is: AUTO_LIGHT.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE = AUTO_LIGHT
+
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the style sheet and background images according to
# this color. Hue is specified as an angle on a color-wheel, see
@@ -1291,15 +1404,6 @@ HTML_COLORSTYLE_SAT = 100
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_MENUS tag is set to YES then the generated HTML
# documentation will contain a main index with vertical navigation menus that
# are dynamically created via JavaScript. If disabled, the navigation index will
@@ -1449,6 +1553,16 @@ BINARY_TOC = NO
TOC_EXPAND = NO
+# The SITEMAP_URL tag is used to specify the full URL of the place where the
+# generated documentation will be placed on the server by the user during the
+# deployment of the documentation. The generated sitemap is called sitemap.xml
+# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL
+# is specified no sitemap is generated. For information about the sitemap
+# protocol see https://www.sitemaps.org
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SITEMAP_URL =
+
# 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
@@ -1566,7 +1680,7 @@ GENERATE_TREEVIEW = NO
# area (value NO) or if it should extend to the full height of the window (value
# YES). Setting this to YES gives a layout similar to
# https://docs.readthedocs.io with more room for contents, but less room for the
-# project logo, title, and description. If either GENERATOR_TREEVIEW or
+# project logo, title, and description. If either GENERATE_TREEVIEW or
# DISABLE_INDEX is set to NO, this option has no effect.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
@@ -1597,6 +1711,13 @@ TREEVIEW_WIDTH = 250
EXT_LINKS_IN_WINDOW = NO
+# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email
+# addresses.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+OBFUSCATE_EMAILS = YES
+
# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
@@ -1617,17 +1738,6 @@ HTML_FORMULA_FORMAT = png
FORMULA_FONTSIZE = 10
-# Use the FORMULA_TRANSPARENT 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
-
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
# to create new LaTeX commands to be used in formulas as building blocks. See
# the section "Including formulas" for details.
@@ -1941,9 +2051,16 @@ PDF_HYPERLINKS = 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.
+# The LATEX_BATCHMODE tag ignals the behavior of LaTeX in case of an error.
+# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch
+# mode nothing is printed on the terminal, errors are scrolled as if <return> is
+# hit at every error; missing files that TeX tries to input or request from
+# keyboard input (\read on a not open input stream) cause the job to abort,
+# NON_STOP In nonstop mode the diagnostic message will appear on the terminal,
+# but there is no possibility of user interaction just like in batch mode,
+# SCROLL In scroll mode, TeX will stop only for missing files to input or if
+# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at
+# each error, asking for user intervention.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
@@ -1964,14 +2081,6 @@ LATEX_HIDE_INDICES = NO
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
-
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
# path from which the emoji images will be read. If a relative path is entered,
# it will be relative to the LATEX_OUTPUT directory. If left blank the
@@ -2137,7 +2246,7 @@ DOCBOOK_OUTPUT = docbook
#---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
-# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
+# AutoGen Definitions (see https://autogen.sourceforge.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.
@@ -2218,7 +2327,8 @@ SEARCH_INCLUDES = YES
# The INCLUDE_PATH tag can be used to specify one or more directories that
# contain include files that are not input files but should be processed by the
-# preprocessor.
+# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of
+# RECURSIVE has no effect here.
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
INCLUDE_PATH =
@@ -2249,10 +2359,6 @@ PREDEFINED += DEPRECATED(_)=_
PREDEFINED += DEPRECATED_BY(__,_)=_
PREDEFINED += ENUM_OVER_INT=1
PREDEFINED += ERRORFUNC(__,_)=_
-PREDEFINED += RJIT_FUNC_EXPORTED=
-PREDEFINED += RJIT_STATIC=extern
-PREDEFINED += RJIT_SYMBOL_EXPORT_BEGIN=
-PREDEFINED += RJIT_SYMBOL_EXPORT_END=
PREDEFINED += NOINLINE(_)=_
PREDEFINED += NORETURN(_)=_
PREDEFINED += PACKED_STRUCT_UNALIGNED(_)=_
@@ -2352,16 +2458,9 @@ EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES
#---------------------------------------------------------------------------
-# Configuration options related to the dot tool
+# Configuration options related to diagram generator tools
#---------------------------------------------------------------------------
-# 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.
@@ -2370,7 +2469,7 @@ 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
+# https://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: NO.
@@ -2387,37 +2486,51 @@ HAVE_DOT = <%= have_dot %>
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.
+# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of
+# subgraphs. When you want a differently looking font in the dot files that
+# doxygen generates you can specify fontname, fontcolor and fontsize attributes.
+# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,
+# Edge and Graph Attributes specification</a> 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. Default graphviz fontsize is 14.
+# The default value is: fontname=Helvetica,fontsize=10.
# This tag requires that the tag HAVE_DOT is set to YES.
-DOT_FONTNAME = FreeSans
+DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10"
-# 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.
+# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can
+# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a
+# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about
+# arrows shapes.</a>
+# The default value is: labelfontname=Helvetica,labelfontsize=10.
# This tag requires that the tag HAVE_DOT is set to YES.
-DOT_FONTSIZE = 10
+DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=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.
+# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes
+# around nodes set 'shape=plain' or 'shape=plaintext' <a
+# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>
+# The default value is: shape=box,height=0.2,width=0.4.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4"
+
+# You can set the path where dot can find font specified with fontname in
+# DOT_COMMON_ATTR and others dot attributes.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTPATH =
-# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a
-# graph for each documented class showing the direct and indirect inheritance
-# relations. In case HAVE_DOT is set as well dot will be used to draw the graph,
-# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set
-# to TEXT the direct and indirect inheritance relations will be shown as texts /
-# links.
-# Possible values are: NO, YES, TEXT and GRAPH.
+# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will
+# generate a graph for each documented class showing the direct and indirect
+# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and
+# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case
+# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the
+# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used.
+# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance
+# relations will be shown as texts / links.
+# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.
# The default value is: YES.
CLASS_GRAPH = NO
@@ -2432,7 +2545,8 @@ CLASS_GRAPH = NO
COLLABORATION_GRAPH = NO
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
-# groups, showing the direct groups dependencies.
+# groups, showing the direct groups dependencies. See also the chapter Grouping
+# in the manual.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2557,7 +2671,7 @@ DIR_GRAPH_MAX_DEPTH = 1
# 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/)).
+# https://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).
@@ -2594,11 +2708,12 @@ DOT_PATH =
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).
+# 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.
-MSCFILE_DIRS =
+DIA_PATH =
# 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
@@ -2607,10 +2722,10 @@ MSCFILE_DIRS =
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.
+# path where java can find the plantuml.jar file or to the filename of jar file
+# to be used. 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 =
@@ -2648,18 +2763,6 @@ DOT_GRAPH_MAX_NODES = 128
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
@@ -2672,6 +2775,8 @@ 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.
+# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal
+# graphical representation for inheritance and collaboration diagrams is used.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2685,3 +2790,19 @@ GENERATE_LEGEND = YES
# The default value is: YES.
DOT_CLEANUP = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will
+# use a built-in version of mscgen tool to produce the charts. Alternatively,
+# the MSCGEN_TOOL tag can also specify the name an external tool. For instance,
+# specifying prog as the value, doxygen will call the tool as prog -T
+# <outfile_format> -o <outputfile> <inputfile>. The external tool should support
+# output file formats "png", "eps", "svg", and "ismap".
+
+MSCGEN_TOOL =
+
+# 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 =
diff --git a/template/GNUmakefile.in b/template/GNUmakefile.in
index 0c7f4e9dd0..22ff1078dc 100644
--- a/template/GNUmakefile.in
+++ b/template/GNUmakefile.in
@@ -1,10 +1,14 @@
gnumake = yes
+ifneq ($(filter notintermediate,$(.FEATURES)),)
+DOT_WAIT = .WAIT
+endif
+
ifeq ($(filter Makefile,$(MAKEFILE_LIST)),)
include Makefile
+endif
GNUmakefile: $(srcdir)/template/GNUmakefile.in
-endif
override silence := $(if $(findstring s,$(firstword $(MFLAGS))),yes,no)
@@ -14,10 +18,6 @@ endif
override order_only := |
-ifneq ($(filter notintermediate,$(.FEATURES)),)
-DOT_WAIT = .WAIT
-endif
-
# Needs the dependency when any Unicode tables data files exist.
override UNICODE_TABLES_DEPENDENTS = \
$(if $(or \
@@ -29,5 +29,3 @@ override UNICODE_TABLES_DEPENDENTS = \
-include uncommon.mk
include $(srcdir)/defs/gmake.mk
-
-GNUmakefile: $(srcdir)/template/GNUmakefile.in
diff --git a/template/Makefile.in b/template/Makefile.in
index 76648bb661..d9a3cbc065 100644
--- a/template/Makefile.in
+++ b/template/Makefile.in
@@ -65,6 +65,7 @@ rubyarchhdrdir = @rubyarchhdrdir@
ruby_version = @ruby_version@
RUBY_VERSION_NAME = @RUBY_VERSION_NAME@
UNIVERSAL_ARCHNAMES = @UNIVERSAL_ARCHNAMES@
+BUILTIN_BINARY = @X_BUILTIN_BINARY@
TESTUI = console
TESTS =
@@ -89,6 +90,7 @@ optflags = @optflags@
debugflags = @debugflags@
warnflags = @warnflags@ @strict_warnflags@
cppflags = @cppflags@
+incflags = @incflags@
RUBY_DEVEL = @RUBY_DEVEL@ # "yes" or empty
_RUBY_DEVEL_enabled = $(RUBY_DEVEL:no=)
XCFLAGS = @XCFLAGS@ $(INCFLAGS) $(_RUBY_DEVEL_enabled:yes=-DRUBY_DEVEL=1)
@@ -96,12 +98,6 @@ USE_RUBYGEMS = @USE_RUBYGEMS@
USE_RUBYGEMS_ = $(USE_RUBYGEMS:yes=)
CPPFLAGS = @CPPFLAGS@ $(USE_RUBYGEMS_:no=-DDISABLE_RUBYGEMS=1)
RJIT_SUPPORT = @RJIT_SUPPORT@
-RJIT_CC = @RJIT_CC@
-RJIT_CFLAGS = @RJIT_CFLAGS@
-RJIT_OPTFLAGS = @RJIT_OPTFLAGS@
-RJIT_DEBUGFLAGS = @RJIT_DEBUGFLAGS@
-RJIT_LDSHARED = @RJIT_LDSHARED@
-RJIT_DLDFLAGS = $(XDLDFLAGS)
YJIT_SUPPORT=@YJIT_SUPPORT@
YJIT_LIBS=@YJIT_LIBS@
YJIT_OBJ=@YJIT_OBJ@
@@ -132,6 +128,9 @@ BUILTIN_TRANSSRCS = @BUILTIN_TRANSSRCS@
BUILTIN_TRANSOBJS = @BUILTIN_TRANSOBJS@
POSTLINK = @POSTLINK@
+LIBPATHENV = @LIBPATHENV@
+PRELOADENV = @PRELOADENV@
+
RUBY_BASE_NAME=@RUBY_BASE_NAME@
RUBY_API_VERSION=@RUBY_API_VERSION@
RUBY_INSTALL_NAME=@RUBY_INSTALL_NAME@
@@ -270,6 +269,8 @@ ACTIONS_ENDGROUP = @$(NULLCMD)
DESTDIR = @DESTDIR@
+cleanlibs = @cleanlibs@
+
configure_args = @configure_args@
#### End of variables
@@ -291,13 +292,13 @@ all:
miniruby$(EXEEXT):
@-if test -f $@; then $(MV) -f $@ $@.old; $(RM) $@.old; fi
$(ECHO) linking $@
- $(Q) $(PURIFY) $(CC) $(EXE_LDFLAGS) $(XLDFLAGS) $(NORMALMAINOBJ) $(MINIOBJS) $(COMMONOBJS) $(MAINLIBS) $(LIBS) $(OUTFLAG)$@
+ $(Q) $(PURIFY) $(CC) $(EXE_LDFLAGS) $(XLDFLAGS) $(NORMALMAINOBJ) $(MINIOBJS) $(COMMONOBJS) $(MAINLIBS) $(OUTFLAG)$@
$(Q) $(POSTLINK)
$(PROGRAM):
@$(RM) $@
$(ECHO) linking $@
- $(Q) $(PURIFY) $(CC) $(EXE_LDFLAGS) $(XLDFLAGS) $(MAINOBJ) $(EXTOBJS) $(LIBRUBYARG) $(MAINLIBS) $(LIBS) $(EXTLIBS) $(OUTFLAG)$@
+ $(Q) $(PURIFY) $(CC) $(EXE_LDFLAGS) $(XLDFLAGS) $(MAINOBJ) $(EXTOBJS) $(LIBRUBYARG) $(MAINLIBS) $(EXTLIBS) $(OUTFLAG)$@
$(Q) $(POSTLINK)
$(PROGRAM): @XRUBY_LIBPATHENV_WRAPPER@
@@ -318,7 +319,7 @@ $(LIBRUBY_A):
verify-static-library: $(LIBRUBY_A)
$(ECHO) verifying static-library $@
- @$(PURIFY) $(CC) $(EXE_LDFLAGS) $(XLDFLAGS) $(MAINOBJ) $(LIBRUBY_A) $(MAINLIBS) $(EXTLIBS) $(LIBS) $(OUTFLAG)conftest$(EXEEXT)
+ @$(PURIFY) $(CC) $(EXE_LDFLAGS) $(XLDFLAGS) $(MAINOBJ) $(LIBRUBY_A) $(MAINLIBS) $(EXTLIBS) $(OUTFLAG)conftest$(EXEEXT)
@$(RMALL) conftest$(EXEEXT) conftest.c conftest.dSYM
$(LIBRUBY_SO):
@@ -492,8 +493,9 @@ clean-local::
enc/encinit.c enc/encinit.$(OBJEXT) $(pkgconfig_DATA) \
ruby-runner.$(OBJEXT) ruby-runner.h \
|| $(NULLCMD)
- $(Q)find . \( -name '*.bc' -o -name '*.[is]' \) -delete
- -$(Q)$(RMALL) exe/ *.dSYM
+ @$(RM) $(ALLOBJS:.$(OBJEXT)=.bc)
+ @$(RM) $(ALLOBJS:.$(OBJEXT)=.i)
+ @$(RM) $(ALLOBJS:.$(OBJEXT)=.s)
distclean-local::
$(Q)$(RM) \
@@ -516,9 +518,9 @@ ext/realclean:: ext/realclean.sub
.bundle/distclean:: .bundle/distclean.sub
.bundle/realclean:: .bundle/realclean.sub
-ext/clean.sub .bundle/clean.sub:: ext/clean.mk
-ext/distclean.sub .bundle/distclean.sub:: ext/distclean.mk
-ext/realclean.sub .bundle/realclean.sub:: ext/realclean.mk
+ext/clean.sub:: ext/clean.mk
+ext/distclean.sub:: ext/distclean.mk
+ext/realclean.sub:: ext/realclean.mk
ext/clean.sub ext/distclean.sub ext/realclean.sub \
.bundle/clean.sub .bundle/distclean.sub .bundle/realclean.sub::
@@ -544,6 +546,9 @@ ext/distclean ext/realclean .bundle/distclean .bundle/realclean::
find "$$@" -type d -empty -exec $(RMDIRS) {} + 2> /dev/null || true
$(Q) $(RMDIRS) $(@D) 2> /dev/null || true
+.bundle/realclean::
+ @$(RMALL) $(tooldir)/bunlder/*.lock $(srcdir)/.bundle
+
clean-enc distclean-enc realclean-enc:
@test -f "$(ENC_MK)" || exit 0; \
echo $(@:-enc=ing) encodings; \
@@ -576,10 +581,19 @@ update-benchmark-driver:
$(BENCHMARK_DRIVER_GIT_URL) benchmark-driver $(GIT_OPTS)
update-known-errors:
- errno --list | cut -d' ' -f1 | sort -u - $(srcdir)/defs/known_errors.def | \
+ errno --list | \
+ $(BASERUBY) -nl -e 'BEGIN {errs = {}}' \
+ -e '/^(E[A-Z_0-9]+)(?: +(?:\d+ +)?(.+))?/ =~ $$_ && errs[$$1] ||= $$2' \
+ -e 'END {' \
+ -e 'errs.delete("ELAST")' \
+ -e 'errs = errs.sort' \
+ -e 'errs << ["ELAST", "Largest errno"]' \
+ -e 'errs.each {|e,d| puts sprintf("%-15s %s", e, d).strip}' \
+ -e '}' \
+ $(srcdir)/defs/known_errors.def - | \
$(IFCHANGE) $(srcdir)/defs/known_errors.def -
-INSNS = opt_sc.inc optinsn.inc optunifs.inc insns.inc insns_info.inc \
+INSNS = optinsn.inc optunifs.inc insns.inc insns_info.inc \
vmtc.inc vm.inc
$(INSNS): $(srcdir)/insns.def vm_opts.h \
@@ -619,7 +633,6 @@ $(INSNS): $(srcdir)/insns.def vm_opts.h \
$(tooldir)/ruby_vm/views/_trace_instruction.erb \
$(tooldir)/ruby_vm/views/insns.inc.erb \
$(tooldir)/ruby_vm/views/insns_info.inc.erb \
- $(tooldir)/ruby_vm/views/opt_sc.inc.erb \
$(tooldir)/ruby_vm/views/optinsn.inc.erb \
$(tooldir)/ruby_vm/views/optunifs.inc.erb \
$(tooldir)/ruby_vm/views/vm.inc.erb \
@@ -636,6 +649,17 @@ un-runnable:
$(ECHO) cannot make runnable, configure with --enable-load-relative.
$(Q) exit 1
-yes-test-basic: $(DOT_WAIT) leaked-globals
-leaked-globals: $(COMMONOBJS) prog $(tooldir)/leaked-globals PHONY
- $(Q) $(XRUBY) $(tooldir)/leaked-globals NM="$(NM) -Pgp" SYMBOL_PREFIX=$(SYMBOL_PREFIX) PLATFORM=$(hdrdir)/ruby/$(PLATFORM_DIR).h $(srcdir)/configure.ac $(COMMONOBJS)
+LIBRUBY_FOR_LEAKED_GLOBALS = $(enable_shared:no=)
+yes-test-basic: $(DOT_WAIT) test-leaked-globals
+leaked-globals: test-leaked-globals
+yes-test-leaked-globals-precheck: $(COMMONOBJS) prog $(tooldir)/leaked-globals
+test-leaked-globals: yes-test-leaked-globals
+yes-test-leaked-globals: yes-test-leaked-globals-precheck
+ $(ACTIONS_GROUP)
+ $(Q) $(XRUBY) $(tooldir)/leaked-globals \
+ SOEXT=$(SOEXT) NM="$(NM) -Pgp" SYMBOL_PREFIX=$(SYMBOL_PREFIX) \
+ SYMBOLS_IN_EMPTYLIB="@XSYMBOLS_IN_EMPTYLIB@" \
+ EXTSTATIC="$(EXTSTATIC)" \
+ PLATFORM=$(hdrdir)/ruby/$(PLATFORM_DIR).h $(srcdir)/configure.ac \
+ $(COMMONOBJS) $(LIBRUBY_FOR_LEAKED_GLOBALS:yes=$(LIBRUBY_SO))
+ $(ACTIONS_ENDGROUP)
diff --git a/template/configure-ext.mk.tmpl b/template/configure-ext.mk.tmpl
index 8ba6b963e3..bc192a37ce 100644
--- a/template/configure-ext.mk.tmpl
+++ b/template/configure-ext.mk.tmpl
@@ -23,7 +23,7 @@ exts = {}
end
%>
MINIRUBY = <%=miniruby%>
-SCRIPT_ARGS = <%=script_args.gsub("#", "\\#")%>
+SCRIPT_ARGS = <%=script_args.gsub("#", "\\#").gsub(/\A|[\s"']\K--jobserver-auth=[^\s'"]*/, "")%>
EXTMK_ARGS = $(SCRIPT_ARGS) --gnumake=$(gnumake) --extflags="$(EXTLDFLAGS)" \
--make-flags="MINIRUBY='$(MINIRUBY)'"
diff --git a/template/encdb.h.tmpl b/template/encdb.h.tmpl
index 8e658f7985..fe6af95747 100644
--- a/template/encdb.h.tmpl
+++ b/template/encdb.h.tmpl
@@ -33,35 +33,27 @@ encdirs << 'enc' if encdirs.empty?
files = {}
encdirs.each do |encdir|
next unless File.directory?(encdir)
- Dir.open(encdir) {|d| d.grep(/.+\.[ch]\z/)}.sort_by {|e|
+ Dir.glob("*.[ch]", base: encdir).sort_by {|e|
e.scan(/(\d+)|(\D+)/).map {|n,a| a||[n.size,n.to_i]}.flatten
}.each do |fn|
next if files[fn]
files[fn] = true
- open(File.join(encdir,fn)) do |f|
+ File.open(File.join(encdir, fn)) do |f|
name = nil
- skip_ifndef_ruby = false
- encoding_def = false
f.each_line do |line|
- case line
- when /^#ifndef RUBY/
- skip_ifndef_ruby = true
- when /^#endif/
- skip_ifndef_ruby = false
- end
- next if skip_ifndef_ruby
- encoding_def = true if /^OnigEncodingDefine/ =~ line
- if encoding_def && /"(.*?)"/ =~ line
- encoding_def = false
- if name
- lines << %[ENC_SET_BASE("#$1", "#{name}");]
- else
- name = $1
+ if (/^#ifndef RUBY/ =~ line)..(/^#endif/ =~ line)
+ elsif /^OnigEncodingDefine/.match?(line)
+ if (n = f.gets("\n\};")[/"(.*?)"/, 1]) # swallow the initializer block
+ if name
+ lines << %[ENC_SET_BASE("#{n}", "#{name}");]
+ else
+ name = n
+ end
+ check_duplication(defs, n, fn, f.lineno)
+ next if BUILTIN_ENCODINGS[name]
+ encodings << n
+ count += 1
end
- check_duplication(defs, $1, fn, f.lineno)
- next if BUILTIN_ENCODINGS[name]
- encodings << $1
- count += 1
else
case line
when /^\s*rb_enc_register\(\s*"([^"]+)"/
diff --git a/template/extinit.c.tmpl b/template/extinit.c.tmpl
index 7a9c910633..e0b076b03c 100644
--- a/template/extinit.c.tmpl
+++ b/template/extinit.c.tmpl
@@ -1,5 +1,5 @@
%# -*- C -*-
-% extinits = ARGV.map {|n| [n[%r[[^/.]+(?=\.[^/]*)?\z]], n]}
+% extinits = ARGV.map {|n| [n.tr('/', '_'), n]}
#include "ruby/ruby.h"
#define init(func, name) { \
diff --git a/template/exts.mk.tmpl b/template/exts.mk.tmpl
index 29c29df37c..ac6b280188 100644
--- a/template/exts.mk.tmpl
+++ b/template/exts.mk.tmpl
@@ -69,6 +69,13 @@ Dir.glob("{ext,.bundle/gems}/*/exts.mk") do |e|
end
end
deps.uniq!
+
+# NOTE: Only if extensions are configured as static and dynamic heterogeneously
+# (e.g. --with-static-linked-ext=foo or ext/Setup can mix static and dynamic
+# extensions), EXTOBJS may contain both extinit.o and dmyext.o. In such case,
+# prefer extinit.o, which does actual Init_${ext} function calls for statically
+# linked extensions, and drop dmyext.o, which does nothing but just to make the
+# linker happy.
if objs = macros["EXTOBJS"] and objs.any? {|e|e.start_with?("ext/extinit.")}
objs.delete_if {|e|e.start_with?("dmyext.")}
end
@@ -155,7 +162,7 @@ ext/extinit.<%=objext%>:
$(Q)<%= submake %><%=mflags%> V=$(V) $(@F)
% if /^(dist|real)clean$/ =~ tgt
$(Q)$(RM) <%=t[%r[\A(?:\.[^/]+/)?(?:[^/]+/){2}]]%>exts.mk
- $(Q)$(RMDIRS) $(@D)
+ -$(Q)$(RMDIRS) $(@D)
% end
% end
% end
diff --git a/template/fake.rb.in b/template/fake.rb.in
index c7b11eff76..5e52d95594 100644
--- a/template/fake.rb.in
+++ b/template/fake.rb.in
@@ -9,6 +9,7 @@ while /\A(\w+)=(.*)/ =~ ARGV[0]
end
if inc = arg['i']
src = inc == '-' ? STDIN.read : File.read(inc)
+ src.gsub!(/^#.*\n/, '')
else
src = ""
end
@@ -51,6 +52,9 @@ class Object
% }
end
builddir = File.dirname(File.expand_path(__FILE__))
+libpathenv = libpathenv = "<%=arg['LIBPATHENV']%>"
+preloadenv = preloadenv = "<%=arg['PRELOADENV']%>"
+libruby_so = libruby_so = "<%=arg['LIBRUBY_SO']%>"
srcdir = "<%=arg['srcdir']%>"
top_srcdir = File.realpath(srcdir, builddir)
fake = File.join(top_srcdir, "tool/fake.rb")
diff --git a/template/id.h.tmpl b/template/id.h.tmpl
index 9c588305eb..4f7053885f 100644
--- a/template/id.h.tmpl
+++ b/template/id.h.tmpl
@@ -67,24 +67,33 @@ enum ruby_method_ids {
id<%=token%>,
% end
tPRESERVED_ID_END,
+
% prev = 'tPRESERVED_ID_END'
% types.each do |type|
% tokens = ids[type]
+ /* <%= type %> tokens {{{ */
tTOKEN_<%=type%>_BEGIN = <%=prev%>-1,
% tokens.each do |token|
t<%=token%>,
% end
% prev = "tTOKEN_#{type}_END"
<%=prev%>,
+ /* <%= type %> tokens }}} */
+
% end
tNEXT_ID = <%=prev%>,
+
% types.each do |type|
% tokens = ids[type]
+ /* <%= type %> IDs {{{ */
#define DEFINE_<%=type%>ID_FROM_TOKEN(n) id##n = TOKEN2<%=type%>ID(t##n)
% tokens or next
% tokens.each do |token|
DEFINE_<%=type%>ID_FROM_TOKEN(<%=token%>),
% end
+#undef DEFINE_<%=type%>ID_FROM_TOKEN
+ /* <%= type %> IDs }}} */
+
% end
tLAST_OP_ID = tPRESERVED_ID_END-1,
idLAST_OP_ID = tLAST_OP_ID >> ID_SCOPE_SHIFT
diff --git a/template/known_errors.inc.tmpl b/template/known_errors.inc.tmpl
index c3aee77477..4d453395ca 100644
--- a/template/known_errors.inc.tmpl
+++ b/template/known_errors.inc.tmpl
@@ -4,8 +4,12 @@
* template/known_errors.inc.tmpl and defs/known_errors.def.
*/
-% error_names = ARGF.read.split(/\s+/)
-% error_names.each do |name|
+% error_names = ARGF.readlines.map {|line| [$1, $2] if /\A([A-Z]\S+)(?:\s+(\S.*))?/ =~ line}.compact
+% error_names.each do |name, doc|
+#if 0
+ /* <% if doc %>"<%= doc %>"<% else %>\<%= name %><% end %> error */
+ rb_define_const(rb_mErrno, "<%=name%>", e<%=name%>);
+#endif
#ifdef <%=name%>
defined_error("<%=name%>", <%=name%>)
#else
diff --git a/template/prelude.c.tmpl b/template/prelude.c.tmpl
index ebf9bc0693..386511f20c 100644
--- a/template/prelude.c.tmpl
+++ b/template/prelude.c.tmpl
@@ -43,7 +43,10 @@ class Prelude
lineno = 0
result = [@preludes.size, @vpath.strip(filename), lines, sub]
@vpath.foreach(filename) do |line|
- line.force_encoding("ASCII-8BIT") if line.respond_to?(:force_encoding)
+ if line.respond_to?(:force_encoding)
+ enc = line.encoding
+ line.force_encoding("ASCII-8BIT")
+ end
line.rstrip!
lineno += 1
@preludes[filename] ||= result
@@ -71,6 +74,7 @@ class Prelude
orig
end
end
+ comment.force_encoding(enc) if enc and comment
lines << [line, comment]
end
result << (start_line || 1)
@@ -86,6 +90,7 @@ Prelude.new(output, ARGV, vpath).instance_eval do
*/
%unless @preludes.empty?
#include "internal.h"
+#include "internal/ruby_parser.h"
#include "internal/warnings.h"
#include "iseq.h"
#include "ruby/ruby.h"
@@ -136,37 +141,39 @@ COMPILER_WARNING_POP
#define PRELUDE_NAME(n) rb_usascii_str_new_static(prelude_name##n, sizeof(prelude_name##n)-1)
#define PRELUDE_CODE(n) rb_utf8_str_new_static(prelude_code##n.L0, sizeof(prelude_code##n))
-static rb_ast_t *
-prelude_ast(VALUE name, VALUE code, int line)
+static VALUE
+prelude_vast(VALUE name, VALUE code, int line)
{
- rb_ast_t *ast = rb_parser_compile_string_path(rb_parser_new(), name, code, line);
+ rb_ast_t *ast;
+ VALUE vast = rb_parser_compile_string_path(rb_parser_new(), name, code, line);
+ ast = rb_ruby_ast_data_get(vast);
if (!ast || !ast->body.root) {
if (ast) rb_ast_dispose(ast);
rb_exc_raise(rb_errinfo());
}
- return ast;
+ return vast;
}
% end
% if @builtin_count > 0
-#define PRELUDE_AST(n, name_str, start_line) \
+#define PRELUDE_VAST(n, name_str, start_line) \
(((sizeof(prelude_name<%='##'%><%=%>n) - prefix_len - 2) == namelen) && \
(strncmp(prelude_name<%='##'%><%=%>n + prefix_len, feature_name, namelen) == 0) ? \
- prelude_ast((name_str) = PRELUDE_NAME(n), PRELUDE_CODE(n), start_line) : 0)
+ prelude_vast((name_str) = PRELUDE_NAME(n), PRELUDE_CODE(n), start_line) : Qnil)
-rb_ast_t *
-rb_builtin_ast(const char *feature_name, VALUE *name_str)
+VALUE
+rb_builtin_vast(const char *feature_name, VALUE *name_str)
{
const size_t prefix_len = rb_strlen_lit("<internal:");
size_t namelen = strlen(feature_name);
- rb_ast_t *ast = 0;
+ VALUE vast = Qnil;
% @preludes.each_value do |i, prelude, lines, sub, start_line|
% if sub
- if ((ast = PRELUDE_AST(<%=i%><%=%>, *name_str, <%=start_line%>)) != 0) return ast;
+ if (!NIL_P(vast = PRELUDE_VAST(<%=i%><%=%>, *name_str, <%=start_line%>))) return vast;
% end
% end
- return ast;
+ return vast;
}
% end
@@ -179,22 +186,24 @@ static void
prelude_eval(VALUE code, VALUE name, int line)
{
static const rb_compile_option_t optimization = {
- TRUE, /* int inline_const_cache; */
- TRUE, /* int peephole_optimization; */
- FALSE,/* int tailcall_optimization; */
- TRUE, /* int specialized_instruction; */
- TRUE, /* int operands_unification; */
- TRUE, /* int instructions_unification; */
- TRUE, /* int stack_caching; */
- TRUE, /* int frozen_string_literal; */
- FALSE, /* int debug_frozen_string_literal; */
+ TRUE, /* unsigned int inline_const_cache; */
+ TRUE, /* unsigned int peephole_optimization; */
+ FALSE,/* unsigned int tailcall_optimization; */
+ TRUE, /* unsigned int specialized_instruction; */
+ TRUE, /* unsigned int operands_unification; */
+ TRUE, /* unsigned int instructions_unification; */
+ TRUE, /* unsigned int frozen_string_literal; */
+ FALSE, /* unsigned int debug_frozen_string_literal; */
FALSE, /* unsigned int coverage_enabled; */
0, /* int debug_level; */
};
- rb_ast_t *ast = prelude_ast(name, code, line);
- rb_iseq_eval(rb_iseq_new_with_opt(&ast->body, name, name, Qnil, line,
- NULL, 0, ISEQ_TYPE_TOP, &optimization));
+ rb_ast_t *ast;
+ VALUE vast = prelude_vast(name, code, line);
+ ast = rb_ruby_ast_data_get(vast);
+ rb_iseq_eval(rb_iseq_new_with_opt(vast, name, name, Qnil, line,
+ NULL, 0, ISEQ_TYPE_TOP, &optimization,
+ Qnil));
rb_ast_dispose(ast);
}
COMPILER_WARNING_POP
diff --git a/template/transdb.h.tmpl b/template/transdb.h.tmpl
index 990a8639d0..22b5960cd8 100644
--- a/template/transdb.h.tmpl
+++ b/template/transdb.h.tmpl
@@ -1,4 +1,4 @@
-<%
+<% #-*- mode: ruby -*-
#
# static const rb_transcoder
# rb_from_US_ASCII = {
@@ -22,39 +22,34 @@ transdirs = transdirs.sort_by {|td|
files = {}
names_t = []
-converter_list = []
transdirs.each do |transdir|
names = Dir.entries(transdir)
- names_t += names.map {|n| /(?!\A)\.trans\z/ =~ n ? $` : nil }.compact
- names_c = names.map {|n| /(?!\A)\.c\z/ =~ n ? $` : nil }.compact
- (names_t & names_c).map {|n|
- "#{n}.c"
- }.sort_by {|e|
+ names_t += names.map {|n| n[/.+(?=\.trans\z)/]}.compact
+ names_c = names.map {|n| n[/.+(?=\.c\z)/]}.compact
+ (names_t & names_c).sort_by {|e|
e.scan(/(\d+)|(\D+)/).map {|n,a| a||[n.size,n.to_i]}.flatten
}.each do |fn|
next if files[fn]
files[fn] = true
- path = File.join(transdir,fn)
- open(path) do |f|
- transcoder_def = false
+ path = File.join(transdir, "#{fn}.c")
+ File.open(path) do |f|
f.each_line do |line|
- transcoder_def = true if /^static const rb_transcoder/ =~ line
- if transcoder_def && /"(.*?)"\s*,\s*"(.*?)"/ =~ line
- transcoder_def = false
- from_to = "%s to %s" % [$1, $2]
- if converters[from_to]
- raise ArgumentError, '%s:%d: transcode "%s" is already registered at %s:%d' %
- [path, f.lineno, from_to, *converters[from_to].values_at(3, 4)]
- else
- converters[from_to] = [$1, $2, fn[0..-3], path, f.lineno]
- converter_list << from_to
+ if (/^static const rb_transcoder/ =~ line)
+ if (/"(.*?)"\s*,\s*"(.*?)"/ =~ f.gets("\n\};")) # swallow the initializer block
+ from_to = [$1.freeze, $2.freeze].freeze
+ if converters[from_to]
+ raise ArgumentError,
+ '%s:%d: transcode "%s to %s" is already registered at %s:%d' %
+ [path, f.lineno, *from_to, *converters[from_to].values_at(3, 4)]
+ else
+ converters[from_to] = [fn, path, f.lineno]
+ end
end
end
end
end
end
end
-converter_list.each do |from_to|
- from, to, fn = *converters[from_to]
+converters.each do |(from, to), (fn)|
%>rb_declare_transcoder("<%=from%>", "<%=to%>", "<%=fn%>");
% end
diff --git a/template/unicode_properties.rdoc.tmpl b/template/unicode_properties.rdoc.tmpl
new file mode 100755
index 0000000000..7bbedc780c
--- /dev/null
+++ b/template/unicode_properties.rdoc.tmpl
@@ -0,0 +1,59 @@
+== \Regexps Based on Unicode Properties
+
+The properties shown here are those currently supported in Ruby.
+Older versions may not support all of these.
+<%
+# Generate a documentation file for the unicode properties.
+#
+# Usage:
+#
+# Get PropertyAliases.txt, PropertyValueAliases.txt from unicode.org
+# (http://unicode.org/Public/UNIDATA/) and run
+# ```
+# ruby tool/generic_erb.rb template/unicode_properties.rdoc.tmpl data_dir name2ctype.h
+# ```
+
+data_dir = ARGV.shift&.tap { |d| Dir.exist?(d) } ||
+ abort("Usage: #{$0} data_directory [name2ctype.h]")
+
+# Map group names, given as last argument to #make_const in enc-unicode.rb,
+# to sections in the doc. The order in this hash controls the order in the doc.
+map = {
+ /\[\[:/ => 'POSIX brackets',
+ '-' => 'Special',
+ /.+ Category/ => 'Major and General Categories',
+ 'Binary Property' => 'Prop List',
+ /Derived Property/ => 'Derived Core Properties',
+ 'Script' => 'Scripts',
+ 'Block' => 'Blocks',
+ 'Emoji' => 'Emoji',
+ /Grapheme/ => 'Graphemes',
+ /Derived Age/ => 'Derived Ages',
+}
+
+# aliases in the form { short => long }, e.g. { 'Hex' => 'Hex_Digit', 'L' => 'Letter' }
+aliases = (
+ File.binread(File.join(data_dir, 'PropertyAliases.txt')).scan(/^(\w+)\s*; (\w+)/) +
+ File.binread(File.join(data_dir, 'PropertyValueAliases.txt')).scan(/^(?:gc|sc)\s*; (\w+)\s*; (\w+)/)
+).to_h
+
+props_by_section = {}
+ARGF.each_line do |line|
+ next unless /'(?<prop>[^']+)': (?<name>.+) \*/ =~ line
+ next if prop == 'NEWLINE' # ignore custom internal prop
+
+ section = map.find { |k, v| k === name }&.last || warn("no doc section for #{name}")
+
+ # normalize prop names - the header file uses a mix of short and long names
+ long_prop_name = aliases[prop] || prop
+ (props_by_section[section] ||= []) << long_prop_name
+end
+
+map.each_value do |section| -%>
+
+=== <%=section%>
+
+% props_by_section[section].sort.each do |prop|
+- <%= [prop, aliases.key(prop)].compact.uniq.map { |v| "<tt>\\p{#{v}}</tt>" }.join(', ') %>
+% end
+% end
diff --git a/test/-ext-/bug_reporter/test_bug_reporter.rb b/test/-ext-/bug_reporter/test_bug_reporter.rb
index 6e44337d41..76f913c275 100644
--- a/test/-ext-/bug_reporter/test_bug_reporter.rb
+++ b/test/-ext-/bug_reporter/test_bug_reporter.rb
@@ -4,14 +4,9 @@ require 'tmpdir'
require_relative '../../lib/jit_support'
class TestBugReporter < Test::Unit::TestCase
- def yjit_enabled?
- defined?(RubyVM::YJIT.enabled?) && RubyVM::YJIT.enabled?
- end
-
def test_bug_reporter_add
- omit if ENV['RUBY_ON_BUG']
-
- description = RUBY_DESCRIPTION
+ omit "flaky with RJIT" if JITSupport.rjit_enabled?
+ description = RUBY_DESCRIPTION.sub(/\+PRISM /, '')
description = description.sub(/\+RJIT /, '') unless JITSupport.rjit_force_enabled?
expected_stderr = [
:*,
@@ -24,9 +19,9 @@ class TestBugReporter < Test::Unit::TestCase
tmpdir = Dir.mktmpdir
no_core = "Process.setrlimit(Process::RLIMIT_CORE, 0); " if defined?(Process.setrlimit) && defined?(Process::RLIMIT_CORE)
- args = ["--disable-gems", "-r-test-/bug_reporter",
- "-C", tmpdir]
- args.push("--yjit") if yjit_enabled? # We want the printed description to match this process's RUBY_DESCRIPTION
+ args = ["-r-test-/bug_reporter", "-C", tmpdir]
+ args.push("--yjit") if JITSupport.yjit_enabled? # We want the printed description to match this process's RUBY_DESCRIPTION
+ args.unshift({"RUBY_ON_BUG" => nil})
stdin = "#{no_core}register_sample_bug_reporter(12345); Process.kill :SEGV, $$"
assert_in_out_err(args, stdin, [], expected_stderr, encoding: "ASCII-8BIT")
ensure
diff --git a/test/-ext-/debug/test_debug.rb b/test/-ext-/debug/test_debug.rb
index 8a351d74fa..b244eb41ea 100644
--- a/test/-ext-/debug/test_debug.rb
+++ b/test/-ext-/debug/test_debug.rb
@@ -29,7 +29,7 @@ class TestDebug < Test::Unit::TestCase
# check same location
assert_equal(loc.path, iseq.path, msg)
assert_equal(loc.absolute_path, iseq.absolute_path, msg)
- assert_equal(loc.label, iseq.label, msg)
+ #assert_equal(loc.label, iseq.label, msg)
assert_operator(loc.lineno, :>=, iseq.first_lineno, msg)
end
diff --git a/test/-ext-/debug/test_profile_frames.rb b/test/-ext-/debug/test_profile_frames.rb
index d6ae953dd2..bd819266df 100644
--- a/test/-ext-/debug/test_profile_frames.rb
+++ b/test/-ext-/debug/test_profile_frames.rb
@@ -14,6 +14,8 @@ class SampleClassForTestProfileFrames
end
class Sample2
+ EVAL_LINE = __LINE__ + 3
+
def baz(block)
instance_eval "def zab(block) block.call end"
[self, zab(block)]
@@ -37,6 +39,20 @@ class SampleClassForTestProfileFrames
end
end
+class SampleClassForTestProfileThreadFrames
+ def initialize(mutex)
+ @mutex = mutex
+ end
+
+ def foo(block)
+ bar(block)
+ end
+
+ def bar(block)
+ block.call
+ end
+end
+
class TestProfileFrames < Test::Unit::TestCase
def test_profile_frames
obj, frames = Fiber.new{
@@ -112,7 +128,7 @@ class TestProfileFrames < Test::Unit::TestCase
"SampleClassForTestProfileFrames#foo",
"TestProfileFrames#test_profile_frames",
]
- paths = [ nil, file=__FILE__, "(eval)", file, file, file, file, file, file, nil ]
+ paths = [ nil, file=__FILE__, "(eval at #{__FILE__}:#{SampleClassForTestProfileFrames::Sample2::EVAL_LINE})", file, file, file, file, file, file, nil ]
absolute_paths = [ "<cfunc>", file, nil, file, file, file, file, file, file, nil ]
assert_equal(labels.size, frames.size)
@@ -137,13 +153,56 @@ class TestProfileFrames < Test::Unit::TestCase
}
end
+ def test_profile_thread_frames
+ mutex = Mutex.new
+ th = Thread.new do
+ mutex.lock
+ Thread.stop
+ SampleClassForTestProfileThreadFrames.new(mutex).foo(lambda { mutex.unlock; loop { sleep(1) } } )
+ end
+
+ # ensure execution has reached SampleClassForTestProfileThreadFrames#bar before running profile_thread_frames
+ loop { break if th.status == "sleep"; sleep 0.1 }
+ th.run
+ mutex.lock # wait until SampleClassForTestProfileThreadFrames#bar has been called
+
+ frames = Bug::Debug.profile_thread_frames(th, 0, 10)
+
+ full_labels = [
+ "Kernel#sleep",
+ "TestProfileFrames#test_profile_thread_frames",
+ "Kernel#loop",
+ "TestProfileFrames#test_profile_thread_frames",
+ "SampleClassForTestProfileThreadFrames#bar",
+ "SampleClassForTestProfileThreadFrames#foo",
+ "TestProfileFrames#test_profile_thread_frames",
+ ]
+
+ frames.each.with_index do |frame, i|
+ assert_equal(full_labels[i], frame)
+ end
+
+ ensure
+ th.kill
+ th.join
+ end
+
+
def test_matches_backtrace_locations_main_thread
assert_equal(Thread.current, Thread.main)
# Keep these in the same line, so the backtraces match exactly
backtrace_locations, profile_frames = [Thread.current.backtrace_locations, Bug::Debug.profile_frames(0, 100)]
- assert_equal(backtrace_locations.size, profile_frames.size)
+ errmsg = "backtrace_locations:\n " + backtrace_locations.map.with_index{|loc, i| "#{i} #{loc}"}.join("\n ")
+ errmsg += "\n\nprofile_frames:\n " + profile_frames.map.with_index{|(path, absolute_path, _, base_label, _, _, _, _, _, full_label, lineno), i|
+ if lineno
+ "#{i} #{absolute_path}:#{lineno} // #{full_label}"
+ else
+ "#{i} #{absolute_path} #{full_label}"
+ end
+ }.join("\n ")
+ assert_equal(backtrace_locations.size, profile_frames.size, errmsg)
# The first entries are not going to match, since one is #backtrace_locations and the other #profile_frames
backtrace_locations.shift
@@ -177,4 +236,8 @@ class TestProfileFrames < Test::Unit::TestCase
a
end;
end
+
+ def test_start
+ assert_equal Bug::Debug.profile_frames(0, 10).tap(&:shift), Bug::Debug.profile_frames(1, 9)
+ end
end
diff --git a/test/-ext-/load/test_resolve_symbol.rb b/test/-ext-/load/test_resolve_symbol.rb
new file mode 100644
index 0000000000..471d3acebd
--- /dev/null
+++ b/test/-ext-/load/test_resolve_symbol.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+require 'test/unit'
+
+class Test_Load_ResolveSymbol < Test::Unit::TestCase
+ def test_load_resolve_symbol_resolver
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ feature = "Feature #20005"
+ assert_raise(LoadError, "resolve_symbol_target is not loaded") {
+ require '-test-/load/resolve_symbol_resolver'
+ }
+ require '-test-/load/resolve_symbol_target'
+ assert_nothing_raised(LoadError, "#{feature} resolver can be loaded") {
+ require '-test-/load/resolve_symbol_resolver'
+ }
+ assert_not_nil ResolveSymbolResolver
+ assert_equal "from target", ResolveSymbolResolver.any_method
+
+ assert_raise(LoadError, "tries to resolve missing feature name, and it should raise LoadError") {
+ ResolveSymbolResolver.try_resolve_fname
+ }
+ assert_raise(LoadError, "tries to resolve missing symbol name, and it should raise LoadError") {
+ ResolveSymbolResolver.try_resolve_sname
+ }
+ end;
+ end
+end
diff --git a/test/-ext-/load/test_stringify_symbols.rb b/test/-ext-/load/test_stringify_symbols.rb
new file mode 100644
index 0000000000..0d9736b591
--- /dev/null
+++ b/test/-ext-/load/test_stringify_symbols.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+require 'test/unit'
+
+class Test_Load_stringify_symbols < Test::Unit::TestCase
+ def test_load_stringify_symbol_required_extensions
+ require '-test-/load/stringify_symbols'
+ require '-test-/load/stringify_target'
+ r1 = StringifySymbols.stringify_symbol("-test-/load/stringify_target", "stt_any_method")
+ assert_not_nil r1
+ r2 = StringifySymbols.stringify_symbol("-test-/load/stringify_target.so", "stt_any_method")
+ assert_equal r1, r2, "resolved symbols should be equal even with or without .so suffix"
+ end
+
+ def test_load_stringify_symbol_statically_linked
+ require '-test-/load/stringify_symbols'
+ # "complex.so" is actually not a statically linked extension.
+ # But it is registered in $LOADED_FEATURES, so it can be a target of this test.
+ r1 = StringifySymbols.stringify_symbol("complex", "rb_complex_minus")
+ assert_not_nil r1
+ r2 = StringifySymbols.stringify_symbol("complex.so", "rb_complex_minus")
+ assert_equal r1, r2
+ end
+
+ def test_load_stringify_symbol_missing_target
+ require '-test-/load/stringify_symbols'
+ r1 = assert_nothing_raised {
+ StringifySymbols.stringify_symbol("something_missing", "unknown_method")
+ }
+ assert_nil r1
+ r2 = assert_nothing_raised {
+ StringifySymbols.stringify_symbol("complex.so", "unknown_method")
+ }
+ assert_nil r2
+ end
+end
diff --git a/test/-ext-/marshal/test_internal_ivar.rb b/test/-ext-/marshal/test_internal_ivar.rb
index a32138f6e8..faabe14ab2 100644
--- a/test/-ext-/marshal/test_internal_ivar.rb
+++ b/test/-ext-/marshal/test_internal_ivar.rb
@@ -11,7 +11,7 @@ module Bug::Marshal
assert_equal("hello", v.normal)
assert_equal("world", v.internal)
assert_equal("bye", v.encoding_short)
- dump = assert_warn(/instance variable `E' on class \S+ is not dumped/) {
+ dump = assert_warn(/instance variable 'E' on class \S+ is not dumped/) {
::Marshal.dump(v)
}
v = assert_nothing_raised {break ::Marshal.load(dump)}
diff --git a/test/-ext-/postponed_job/test_postponed_job.rb b/test/-ext-/postponed_job/test_postponed_job.rb
index fee0172d11..8c2b3e95d1 100644
--- a/test/-ext-/postponed_job/test_postponed_job.rb
+++ b/test/-ext-/postponed_job/test_postponed_job.rb
@@ -2,34 +2,70 @@
require 'test/unit'
require '-test-/postponed_job'
-module Bug
- def self.postponed_job_call_direct_wrapper(*args)
- postponed_job_call_direct(*args)
+class TestPostponed_job < Test::Unit::TestCase
+ def test_preregister_and_trigger
+ assert_separately([], __FILE__, __LINE__, <<-'RUBY')
+ require '-test-/postponed_job'
+ Bug.postponed_job_preregister_and_call_without_sleep(counters = [])
+ # i.e. rb_postponed_job_trigger performs coalescing
+ assert_equal([3], counters)
+
+ # i.e. rb_postponed_job_trigger resets after interrupts are checked
+ Bug.postponed_job_preregister_and_call_with_sleep(counters = [])
+ assert_equal([1, 2, 3], counters)
+ RUBY
end
- def self.postponed_job_register_wrapper(*args)
- postponed_job_register(*args)
+ def test_multiple_preregistration
+ assert_separately([], __FILE__, __LINE__, <<-'RUBY')
+ require '-test-/postponed_job'
+ handles = Bug.postponed_job_preregister_multiple_times
+ # i.e. rb_postponed_job_preregister returns the same handle if preregistered multiple times
+ assert_equal [handles[0]], handles.uniq
+ RUBY
end
-end
-class TestPostponed_job < Test::Unit::TestCase
- def test_register
- direct, registered = [], []
+ def test_multiple_preregistration_with_new_data
+ assert_separately([], __FILE__, __LINE__, <<-'RUBY')
+ require '-test-/postponed_job'
+ values = Bug.postponed_job_preregister_calls_with_last_argument
+ # i.e. the callback is called with the last argument it was preregistered with
+ assert_equal [3, 4], values
+ RUBY
+ end
- Bug.postponed_job_call_direct_wrapper(direct)
- Bug.postponed_job_register_wrapper(registered)
+ def test_legacy_register
+ assert_separately([], __FILE__, __LINE__, <<-'RUBY')
+ require '-test-/postponed_job'
+ direct, registered = [], []
- assert_equal([0], direct)
- assert_equal([3], registered)
+ Bug.postponed_job_call_direct(direct)
+ Bug.postponed_job_register(registered)
- Bug.postponed_job_register_one(ary = [])
- assert_equal [1], ary
+ assert_equal([0], direct)
+ assert_equal([3], registered)
+
+ Bug.postponed_job_register_one(ary = [])
+ assert_equal [1], ary
+ RUBY
+ end
+
+ def test_legacy_register_one_same
+ assert_separately([], __FILE__, __LINE__, <<-'RUBY')
+ require '-test-/postponed_job'
+ # Registering the same job three times should result in three of the same handle
+ handles = Bug.postponed_job_register_one_same
+ assert_equal [handles[0]], handles.uniq
+ RUBY
end
if Bug.respond_to?(:postponed_job_register_in_c_thread)
- def test_register_in_c_thread
- assert Bug.postponed_job_register_in_c_thread(ary = [])
- assert_equal [1], ary
+ def test_legacy_register_in_c_thread
+ assert_separately([], __FILE__, __LINE__, <<-'RUBY')
+ require '-test-/postponed_job'
+ assert Bug.postponed_job_register_in_c_thread(ary = [])
+ assert_equal [1], ary
+ RUBY
end
end
end
diff --git a/test/-ext-/string/test_capacity.rb b/test/-ext-/string/test_capacity.rb
index 50f8c10f58..2c6c51fdda 100644
--- a/test/-ext-/string/test_capacity.rb
+++ b/test/-ext-/string/test_capacity.rb
@@ -23,7 +23,7 @@ class Test_StringCapacity < Test::Unit::TestCase
def test_s_new_capacity
assert_equal("", String.new(capacity: 1000))
assert_equal(String, String.new(capacity: 1000).class)
- assert_equal(10000, capa(String.new(capacity: 10000)))
+ assert_equal(10_000 - 1, capa(String.new(capacity: 10_000))) # Real capa doesn't account for termlen
assert_equal("", String.new(capacity: -1000))
assert_equal(capa(String.new(capacity: -10000)), capa(String.new(capacity: -1000)))
@@ -66,7 +66,7 @@ class Test_StringCapacity < Test::Unit::TestCase
end
def embed_header_size
- 2 * RbConfig::SIZEOF['void*'] + RbConfig::SIZEOF['long']
+ 3 * RbConfig::SIZEOF['void*']
end
def max_embed_len
diff --git a/test/-ext-/string/test_chilled.rb b/test/-ext-/string/test_chilled.rb
new file mode 100644
index 0000000000..dccab61ced
--- /dev/null
+++ b/test/-ext-/string/test_chilled.rb
@@ -0,0 +1,19 @@
+require 'test/unit'
+require '-test-/string'
+
+class Test_String_ChilledString < Test::Unit::TestCase
+ def test_rb_str_chilled_p
+ str = ""
+ assert_equal true, Bug::String.rb_str_chilled_p(str)
+ end
+
+ def test_rb_str_chilled_p_frozen
+ str = "".freeze
+ assert_equal false, Bug::String.rb_str_chilled_p(str)
+ end
+
+ def test_rb_str_chilled_p_mutable
+ str = "".dup
+ assert_equal false, Bug::String.rb_str_chilled_p(str)
+ end
+end
diff --git a/test/-ext-/string/test_fstring.rb b/test/-ext-/string/test_fstring.rb
index 5a3456c566..fcec6be543 100644
--- a/test/-ext-/string/test_fstring.rb
+++ b/test/-ext-/string/test_fstring.rb
@@ -15,19 +15,27 @@ class Test_String_Fstring < Test::Unit::TestCase
def test_rb_enc_interned_str_autoloaded_encoding
assert_separately([], <<~RUBY)
require '-test-/string'
- assert_include(Encoding::Windows_31J.inspect, 'autoload')
- Bug::String.rb_enc_interned_str(Encoding::Windows_31J)
+ assert_include(Encoding::CESU_8.inspect, 'autoload')
+ Bug::String.rb_enc_interned_str(Encoding::CESU_8)
RUBY
end
+ def test_rb_enc_interned_str_null_encoding
+ assert_equal Encoding::ASCII_8BIT, Bug::String.rb_enc_interned_str(nil).encoding
+ end
+
def test_rb_enc_str_new_autoloaded_encoding
assert_separately([], <<~RUBY)
require '-test-/string'
- assert_include(Encoding::Windows_31J.inspect, 'autoload')
- Bug::String.rb_enc_str_new(Encoding::Windows_31J)
+ assert_include(Encoding::CESU_8.inspect, 'autoload')
+ Bug::String.rb_enc_str_new(Encoding::CESU_8)
RUBY
end
+ def test_rb_enc_str_new_null_encoding
+ assert_equal Encoding::ASCII_8BIT, Bug::String.rb_enc_str_new(nil).encoding
+ end
+
def test_instance_variable
str = __method__.to_s * 3
str.instance_variable_set(:@test, 42)
@@ -49,6 +57,10 @@ class Test_String_Fstring < Test::Unit::TestCase
assert_raise(TypeError) {fstr.singleton_class}
end
+ def test_fake_str
+ assert_equal([*"a".."z"].join(""), Bug::String.fstring_fake_str)
+ end
+
class S < String
end
diff --git a/test/-ext-/string/test_set_len.rb b/test/-ext-/string/test_set_len.rb
index 67ba961194..e3eff75d9b 100644
--- a/test/-ext-/string/test_set_len.rb
+++ b/test/-ext-/string/test_set_len.rb
@@ -34,4 +34,33 @@ class Test_StrSetLen < Test::Unit::TestCase
assert_equal 128, Bug::String.capacity(str)
assert_equal 127, str.set_len(127).bytesize, bug12757
end
+
+ def test_coderange_after_append
+ u = -"\u3042"
+ str = Bug::String.new(encoding: Encoding::UTF_8)
+ bsize = u.bytesize
+ str.append(u)
+ assert_equal 0, str.bytesize
+ str.set_len(bsize)
+ assert_equal bsize, str.bytesize
+ assert_predicate str, :valid_encoding?
+ assert_not_predicate str, :ascii_only?
+ assert_equal u, str
+ end
+
+ def test_coderange_after_trunc
+ u = -"\u3042"
+ bsize = u.bytesize
+ str = Bug::String.new(u)
+ str.set_len(bsize - 1)
+ assert_equal bsize - 1, str.bytesize
+ assert_not_predicate str, :valid_encoding?
+ assert_not_predicate str, :ascii_only?
+ str.append(u.byteslice(-1))
+ str.set_len(bsize)
+ assert_equal bsize, str.bytesize
+ assert_predicate str, :valid_encoding?
+ assert_not_predicate str, :ascii_only?
+ assert_equal u, str
+ end
end
diff --git a/test/-ext-/string/test_too_many_dummy_encodings.rb b/test/-ext-/string/test_too_many_dummy_encodings.rb
index 4d71fd1d72..b96b40db7b 100644
--- a/test/-ext-/string/test_too_many_dummy_encodings.rb
+++ b/test/-ext-/string/test_too_many_dummy_encodings.rb
@@ -4,7 +4,7 @@ require "-test-/string"
class Test_TooManyDummyEncodings < Test::Unit::TestCase
def test_exceed_encoding_table_size
- assert_separately(%w[--disable=gems], "#{<<~"begin;"}\n#{<<~'end;'}")
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
begin;
require "-test-/string"
assert_raise_with_message(EncodingError, /too many encoding/) do
diff --git a/test/-ext-/struct/test_data.rb b/test/-ext-/struct/test_data.rb
new file mode 100644
index 0000000000..8dbc9113a5
--- /dev/null
+++ b/test/-ext-/struct/test_data.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: false
+require 'test/unit'
+require "-test-/struct"
+
+class Bug::Struct::Test_Data < Test::Unit::TestCase
+ def test_data_new_default
+ klass = Bug::Struct.data_new(false)
+ assert_equal Data, klass.superclass
+ assert_equal %i[mem1 mem2], klass.members
+ end
+
+ def test_data_new_superclass
+ superclass = Data.define
+ klass = Bug::Struct.data_new(superclass)
+ assert_equal superclass, klass.superclass
+ assert_equal %i[mem1 mem2], klass.members
+ end
+end
diff --git a/test/-ext-/symbol/test_type.rb b/test/-ext-/symbol/test_type.rb
index fdee692fe4..2b0fbe5b79 100644
--- a/test/-ext-/symbol/test_type.rb
+++ b/test/-ext-/symbol/test_type.rb
@@ -134,5 +134,10 @@ module Test_Symbol
Bug::Symbol.find(cx)
}
end
+
+ def test_const_name_type
+ sym = "\xb5".force_encoding(Encoding::Windows_1253)
+ assert_not_operator Bug::Symbol, :const?, sym, sym.encode(Encoding::UTF_8)
+ end
end
end
diff --git a/test/-ext-/test_bug-3571.rb b/test/-ext-/test_bug-3571.rb
index c75d2e8523..5952ce2a33 100644
--- a/test/-ext-/test_bug-3571.rb
+++ b/test/-ext-/test_bug-3571.rb
@@ -13,8 +13,8 @@ end
SRC
out = [
"start() function is unimplemented on this machine",
- "-:2:in `start'",
- "-:2:in `<main>'",
+ "-:2:in 'Bug.start'",
+ "-:2:in '<main>'",
]
assert_in_out_err(%w"-r-test-/bug_3571", src, [], out, bug3571)
end
diff --git a/test/-ext-/thread/helper.rb b/test/-ext-/thread/helper.rb
new file mode 100644
index 0000000000..3ea2057d15
--- /dev/null
+++ b/test/-ext-/thread/helper.rb
@@ -0,0 +1,51 @@
+module ThreadInstrumentation
+ module TestHelper
+ private
+
+ def record
+ Bug::ThreadInstrumentation.register_callback(!ENV["GVL_DEBUG"])
+ yield
+ ensure
+ timeline = Bug::ThreadInstrumentation.unregister_callback
+ if $!
+ raise
+ else
+ return timeline
+ end
+ end
+
+ def timeline_for(thread, timeline)
+ timeline.select { |t, _| t == thread }.map(&:last)
+ end
+
+ def assert_consistent_timeline(events)
+ refute_predicate events, :empty?
+
+ previous_event = nil
+ events.each do |event|
+ refute_equal :exited, previous_event, "`exited` must be the final event: #{events.inspect}"
+ case event
+ when :started
+ assert_nil previous_event, "`started` must be the first event: #{events.inspect}"
+ when :ready
+ unless previous_event.nil?
+ assert %i(started suspended).include?(previous_event), "`ready` must be preceded by `started` or `suspended`: #{events.inspect}"
+ end
+ when :resumed
+ unless previous_event.nil?
+ assert_equal :ready, previous_event, "`resumed` must be preceded by `ready`: #{events.inspect}"
+ end
+ when :suspended
+ unless previous_event.nil?
+ assert_equal :resumed, previous_event, "`suspended` must be preceded by `resumed`: #{events.inspect}"
+ end
+ when :exited
+ unless previous_event.nil?
+ assert %i(resumed suspended).include?(previous_event), "`exited` must be preceded by `resumed` or `suspended`: #{events.inspect}"
+ end
+ end
+ previous_event = event
+ end
+ end
+ end
+end
diff --git a/test/-ext-/thread/test_instrumentation_api.rb b/test/-ext-/thread/test_instrumentation_api.rb
index dd620e7380..9a3b67fa10 100644
--- a/test/-ext-/thread/test_instrumentation_api.rb
+++ b/test/-ext-/thread/test_instrumentation_api.rb
@@ -1,91 +1,289 @@
# frozen_string_literal: false
require 'envutil'
+require_relative "helper"
class TestThreadInstrumentation < Test::Unit::TestCase
+ include ThreadInstrumentation::TestHelper
+
def setup
pend("No windows support") if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM
require '-test-/thread/instrumentation'
- Thread.list.each do |thread|
- if thread != Thread.current
- thread.kill
- thread.join rescue nil
- end
- end
- assert_equal [Thread.current], Thread.list
-
- Bug::ThreadInstrumentation.reset_counters
- Bug::ThreadInstrumentation::register_callback
+ cleanup_threads
end
def teardown
return if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM
- Bug::ThreadInstrumentation::unregister_callback
+ Bug::ThreadInstrumentation.unregister_callback
+ cleanup_threads
end
THREADS_COUNT = 3
- def test_thread_instrumentation
- threads = threaded_cpu_work
- assert_equal [false] * THREADS_COUNT, threads.map(&:status)
- counters = Bug::ThreadInstrumentation.counters
- assert_join_counters(counters)
- assert_global_join_counters(counters)
+ def test_single_thread_timeline
+ thread = nil
+ full_timeline = record do
+ thread = Thread.new { 1 + 1 }
+ thread.join
+ end
+ assert_equal %i(started ready resumed suspended exited), timeline_for(thread, full_timeline)
+ ensure
+ thread&.kill
+ end
+
+ def test_thread_pass_single_thread
+ full_timeline = record do
+ Thread.pass
+ end
+ assert_equal [], timeline_for(Thread.current, full_timeline)
+ end
+
+ def test_thread_pass_multi_thread
+ thread = Thread.new do
+ cpu_bound_work(0.5)
+ end
+
+ full_timeline = record do
+ Thread.pass
+ end
+
+ assert_equal %i(suspended ready resumed), timeline_for(Thread.current, full_timeline)
+ ensure
+ thread&.kill
+ thread&.join
+ end
+
+ def test_muti_thread_timeline
+ threads = nil
+ full_timeline = record do
+ threads = threaded_cpu_bound_work(1.0)
+ results = threads.map(&:value)
+ results.each do |r|
+ refute_equal false, r
+ end
+ assert_equal [false] * THREADS_COUNT, threads.map(&:status)
+ end
+
+ threads.each do |thread|
+ timeline = timeline_for(thread, full_timeline)
+ assert_consistent_timeline(timeline)
+ assert timeline.count(:suspended) > 1, "Expected threads to yield suspended at least once: #{timeline.inspect}"
+ end
+
+ timeline = timeline_for(Thread.current, full_timeline)
+ assert_consistent_timeline(timeline)
+ ensure
+ threads&.each(&:kill)
+ end
+
+ def test_join_suspends # Bug #18900
+ thread = other_thread = nil
+ full_timeline = record do
+ other_thread = Thread.new { sleep 0.3 }
+ thread = Thread.new { other_thread.join }
+ thread.join
+ end
+
+ timeline = timeline_for(thread, full_timeline)
+ assert_consistent_timeline(timeline)
+ assert_equal %i(started ready resumed suspended ready resumed suspended exited), timeline
+ ensure
+ other_thread&.kill
+ thread&.kill
end
- def test_join_counters # Bug #18900
- thr = Thread.new { fib(30) }
- Bug::ThreadInstrumentation.reset_counters
- thr.join
- assert_join_counters(Bug::ThreadInstrumentation.local_counters)
+ def test_io_release_gvl
+ r, w = IO.pipe
+ thread = nil
+ full_timeline = record do
+ thread = Thread.new do
+ w.write("Hello\n")
+ end
+ thread.join
+ end
+
+ timeline = timeline_for(thread, full_timeline)
+ assert_consistent_timeline(timeline)
+ assert_equal %i(started ready resumed suspended ready resumed suspended exited), timeline
+ ensure
+ r&.close
+ w&.close
+ end
+
+ def test_queue_releases_gvl
+ queue1 = Queue.new
+ queue2 = Queue.new
+
+ thread = nil
+
+ full_timeline = record do
+ thread = Thread.new do
+ queue1 << true
+ queue2.pop
+ end
+
+ queue1.pop
+ queue2 << true
+ thread.join
+ end
+
+ timeline = timeline_for(thread, full_timeline)
+ assert_consistent_timeline(timeline)
+ assert_equal %i(started ready resumed suspended ready resumed suspended exited), timeline
+ end
+
+ def test_blocking_on_ractor
+ assert_ractor(<<-"RUBY", require_relative: "helper", require: "-test-/thread/instrumentation")
+ include ThreadInstrumentation::TestHelper
+
+ ractor = Ractor.new {
+ Ractor.receive # wait until woke
+ Thread.current
+ }
+
+ # Wait for the main thread to block, then wake the ractor
+ Thread.new do
+ while Thread.main.status != "sleep"
+ Thread.pass
+ end
+ ractor.send true
+ end
+
+ full_timeline = record do
+ ractor.take
+ end
+
+ timeline = timeline_for(Thread.current, full_timeline)
+ assert_consistent_timeline(timeline)
+ assert_equal %i(suspended ready resumed), timeline
+ RUBY
+ end
+
+ def test_sleeping_inside_ractor
+ assert_ractor(<<-"RUBY", require_relative: "helper", require: "-test-/thread/instrumentation")
+ include ThreadInstrumentation::TestHelper
+
+ thread = nil
+
+ full_timeline = record do
+ thread = Ractor.new{
+ sleep 0.1
+ Thread.current
+ }.take
+ sleep 0.1
+ end
+
+ timeline = timeline_for(thread, full_timeline)
+ assert_consistent_timeline(timeline)
+ assert_equal %i(started ready resumed suspended ready resumed suspended exited), timeline
+ RUBY
+ end
+
+ def test_thread_blocked_forever_on_mutex
+ mutex = Mutex.new
+ mutex.lock
+ thread = nil
+
+ full_timeline = record do
+ thread = Thread.new do
+ mutex.lock
+ end
+ 10.times { Thread.pass }
+ sleep 0.1
+ end
+
+ mutex.unlock
+ thread.join
+
+ timeline = timeline_for(thread, full_timeline)
+ assert_consistent_timeline(timeline)
+ assert_equal %i(started ready resumed suspended), timeline
+ end
+
+ def test_thread_blocked_temporarily_on_mutex
+ mutex = Mutex.new
+ mutex.lock
+ thread = nil
+
+ full_timeline = record do
+ thread = Thread.new do
+ mutex.lock
+ end
+ 10.times { Thread.pass }
+ sleep 0.1
+ mutex.unlock
+ 10.times { Thread.pass }
+ sleep 0.1
+ end
+
+ thread.join
+
+ timeline = timeline_for(thread, full_timeline)
+ assert_consistent_timeline(timeline)
+ assert_equal %i(started ready resumed suspended ready resumed suspended exited), timeline
end
def test_thread_instrumentation_fork_safe
skip "No fork()" unless Process.respond_to?(:fork)
- thread_statuses = counters = nil
+ thread_statuses = full_timeline = nil
IO.popen("-") do |read_pipe|
if read_pipe
thread_statuses = Marshal.load(read_pipe)
- counters = Marshal.load(read_pipe)
+ full_timeline = Marshal.load(read_pipe)
else
- Bug::ThreadInstrumentation.reset_counters
- threads = threaded_cpu_work
+ threads = threaded_cpu_bound_work.each(&:join)
Marshal.dump(threads.map(&:status), STDOUT)
- Marshal.dump(Bug::ThreadInstrumentation.counters, STDOUT)
+ full_timeline = Bug::ThreadInstrumentation.unregister_callback.map { |t, e| [t.to_s, e ] }
+ Marshal.dump(full_timeline, STDOUT)
end
end
assert_predicate $?, :success?
assert_equal [false] * THREADS_COUNT, thread_statuses
- assert_join_counters(counters)
- assert_global_join_counters(counters)
+ thread_names = full_timeline.map(&:first).uniq
+ thread_names.each do |thread_name|
+ assert_consistent_timeline(timeline_for(thread_name, full_timeline))
+ end
end
def test_thread_instrumentation_unregister
- Bug::ThreadInstrumentation::unregister_callback
assert Bug::ThreadInstrumentation::register_and_unregister_callbacks
end
private
- def fib(n = 20)
+ def fib(n = 30)
return n if n <= 1
fib(n-1) + fib(n-2)
end
- def threaded_cpu_work(size = 20)
- THREADS_COUNT.times.map { Thread.new { fib(size) } }.each(&:join)
+ def cpu_bound_work(duration)
+ deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + duration
+ i = 0
+ while deadline > Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ fib(25)
+ i += 1
+ end
+ i > 0 ? i : false
end
- def assert_join_counters(counters)
- counters.each_with_index do |c, i|
- assert_operator c, :>, 0, "Call counters[#{i}]: #{counters.inspect}"
+ def threaded_cpu_bound_work(duration = 0.5)
+ THREADS_COUNT.times.map do
+ Thread.new do
+ cpu_bound_work(duration)
+ end
end
end
- def assert_global_join_counters(counters)
- assert_equal THREADS_COUNT, counters.first
+ def cleanup_threads
+ Thread.list.each do |thread|
+ if thread != Thread.current
+ thread.kill
+ thread.join rescue nil
+ end
+ end
+ assert_equal [Thread.current], Thread.list
end
end
diff --git a/test/-ext-/thread/test_lock_native_thread.rb b/test/-ext-/thread/test_lock_native_thread.rb
new file mode 100644
index 0000000000..8a5ba78838
--- /dev/null
+++ b/test/-ext-/thread/test_lock_native_thread.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: false
+
+require 'envutil'
+
+mn_supported_p = -> do
+ out, *_ = EnvUtil.invoke_ruby([{'RUBY_MN_THREADS' => '1'}, '-v'], '', true)
+ return /\+MN/ =~ out
+end
+
+if mn_supported_p.call
+ # test only on MN threads
+else
+ return
+end
+
+class TestThreadLockNativeThread < Test::Unit::TestCase
+ def test_lock_native_thread
+ assert_separately([{'RUBY_MN_THREADS' => '1'}], <<-RUBY)
+ require '-test-/thread/lock_native_thread'
+
+ Thread.new{
+ assert_equal true, Thread.current.lock_native_thread
+ }.join
+
+ # main thread already has DNT
+ assert_equal false, Thread.current.lock_native_thread
+ RUBY
+ end
+
+ def test_lock_native_thread_tls
+ assert_separately([{'RUBY_MN_THREADS' => '1'}], <<-RUBY)
+ require '-test-/thread/lock_native_thread'
+ tn = 10
+ ln = 1_000
+
+ ts = tn.times.map{|i|
+ Thread.new(i){|i|
+ Thread.current.set_tls i
+ assert_equal true, Thread.current.lock_native_thread
+
+ ln.times{
+ assert_equal i, Thread.current.get_tls
+ Thread.pass
+ }
+ }
+ }
+ ts.each(&:join)
+ RUBY
+ end
+end
diff --git a/test/-ext-/tracepoint/test_tracepoint.rb b/test/-ext-/tracepoint/test_tracepoint.rb
index 9d1679602a..48ffe2605c 100644
--- a/test/-ext-/tracepoint/test_tracepoint.rb
+++ b/test/-ext-/tracepoint/test_tracepoint.rb
@@ -11,6 +11,7 @@ class TestTracepointObj < Test::Unit::TestCase
def test_tracks_objspace_events
result = EnvUtil.suppress_warning {eval(<<-EOS, nil, __FILE__, __LINE__+1)}
+ # frozen_string_literal: false
Bug.tracepoint_track_objspace_events {
99
'abc'
diff --git a/test/.excludes-prism/TestAssignment.rb b/test/.excludes-prism/TestAssignment.rb
new file mode 100644
index 0000000000..23a1b4c5e7
--- /dev/null
+++ b/test/.excludes-prism/TestAssignment.rb
@@ -0,0 +1 @@
+exclude(:test_massign_order, "https://github.com/ruby/prism/issues/2370")
diff --git a/test/.excludes-prism/TestAssignmentGen.rb b/test/.excludes-prism/TestAssignmentGen.rb
new file mode 100644
index 0000000000..5f3bb12ef7
--- /dev/null
+++ b/test/.excludes-prism/TestAssignmentGen.rb
@@ -0,0 +1 @@
+exclude(:test_assignment, "https://github.com/ruby/prism/issues/2370")
diff --git a/test/.excludes-prism/TestCall.rb b/test/.excludes-prism/TestCall.rb
new file mode 100644
index 0000000000..969e32ea5a
--- /dev/null
+++ b/test/.excludes-prism/TestCall.rb
@@ -0,0 +1,3 @@
+exclude(:test_call_op_asgn_keywords, "https://github.com/ruby/prism/issues/2438")
+exclude(:test_call_op_asgn_keywords_mutable, "https://github.com/ruby/prism/issues/2438")
+exclude(:test_kwsplat_block_order_op_asgn, "https://github.com/ruby/prism/issues/2438")
diff --git a/test/.excludes-prism/TestCoverage.rb b/test/.excludes-prism/TestCoverage.rb
new file mode 100644
index 0000000000..20f9972f89
--- /dev/null
+++ b/test/.excludes-prism/TestCoverage.rb
@@ -0,0 +1 @@
+exclude(:test_eval, "unknown")
diff --git a/test/.excludes-prism/TestIRB/RubyLexTest.rb b/test/.excludes-prism/TestIRB/RubyLexTest.rb
new file mode 100644
index 0000000000..2274ae62cf
--- /dev/null
+++ b/test/.excludes-prism/TestIRB/RubyLexTest.rb
@@ -0,0 +1 @@
+exclude(:test_code_block_open_with_should_continue, "symbol encoding")
diff --git a/test/.excludes-prism/TestISeq.rb b/test/.excludes-prism/TestISeq.rb
new file mode 100644
index 0000000000..3768d8fc05
--- /dev/null
+++ b/test/.excludes-prism/TestISeq.rb
@@ -0,0 +1,3 @@
+exclude(:test_each_child, "https://github.com/ruby/prism/issues/2660")
+exclude(:test_syntax_error_message, "Assertion checks against specific error format")
+exclude(:test_trace_points, "https://github.com/ruby/prism/issues/2660")
diff --git a/test/.excludes-prism/TestM17N.rb b/test/.excludes-prism/TestM17N.rb
new file mode 100644
index 0000000000..15ed504fc9
--- /dev/null
+++ b/test/.excludes-prism/TestM17N.rb
@@ -0,0 +1,7 @@
+exclude(:test_dynamic_eucjp_regexp, "https://github.com/ruby/prism/issues/2664")
+exclude(:test_dynamic_sjis_regexp, "https://github.com/ruby/prism/issues/2664")
+exclude(:test_dynamic_utf8_regexp, "https://github.com/ruby/prism/issues/2664")
+exclude(:test_regexp_ascii, "https://github.com/ruby/prism/issues/2664")
+exclude(:test_regexp_embed, "https://github.com/ruby/prism/issues/2664")
+exclude(:test_regexp_usascii, "unknown")
+exclude(:test_string_mixed_unicode, "unknown")
diff --git a/test/.excludes-prism/TestMixedUnicodeEscape.rb b/test/.excludes-prism/TestMixedUnicodeEscape.rb
new file mode 100644
index 0000000000..09e3cc168b
--- /dev/null
+++ b/test/.excludes-prism/TestMixedUnicodeEscape.rb
@@ -0,0 +1 @@
+exclude(:test_basic, "unknown")
diff --git a/test/.excludes-prism/TestParse.rb b/test/.excludes-prism/TestParse.rb
new file mode 100644
index 0000000000..83af593b15
--- /dev/null
+++ b/test/.excludes-prism/TestParse.rb
@@ -0,0 +1,28 @@
+exclude(:test_dynamic_constant_assignment, "unknown")
+exclude(:test_else_without_rescue, "unknown")
+exclude(:test_error_def_in_argument, "unknown")
+exclude(:test_float, "unknown")
+exclude(:test_global_variable, "unknown")
+exclude(:test_here_document, "unknown")
+exclude(:test_heredoc_unterminated_interpolation, "unknown")
+exclude(:test_invalid_char, "unknown")
+exclude(:test_location_of_invalid_token, "unknown")
+exclude(:test_op_asgn1_with_block, "unknown")
+exclude(:test_parse_string, "unknown")
+exclude(:test_percent, "unknown")
+exclude(:test_question, "unknown")
+exclude(:test_shareable_constant_value_ignored, "unknown")
+exclude(:test_shareable_constant_value_nested, "ractor support")
+exclude(:test_shareable_constant_value_nonliteral, "ractor support")
+exclude(:test_shareable_constant_value_simple, "ractor support")
+exclude(:test_shareable_constant_value_unfrozen, "ractor support")
+exclude(:test_shareable_constant_value_unshareable_literal, "ractor support")
+exclude(:test_string, "unknown")
+exclude(:test_truncated_source_line, "unknown")
+exclude(:test_unassignable, "unknown")
+exclude(:test_unexpected_eof, "unknown")
+exclude(:test_unexpected_token_after_numeric, "unknown")
+exclude(:test_unterminated_regexp_error, "unknown")
+exclude(:test_unused_variable, "missing warning")
+exclude(:test_void_value_in_rhs, "unknown")
+exclude(:test_words, "unknown")
diff --git a/test/.excludes-prism/TestPatternMatching.rb b/test/.excludes-prism/TestPatternMatching.rb
new file mode 100644
index 0000000000..cfd0c6bed9
--- /dev/null
+++ b/test/.excludes-prism/TestPatternMatching.rb
@@ -0,0 +1,2 @@
+exclude(:test_hash_pattern, "useless literal warning missing")
+exclude(:test_invalid_syntax, "[a:] is disallowed")
diff --git a/test/.excludes-prism/TestRegexp.rb b/test/.excludes-prism/TestRegexp.rb
new file mode 100644
index 0000000000..68ad1414a9
--- /dev/null
+++ b/test/.excludes-prism/TestRegexp.rb
@@ -0,0 +1,6 @@
+exclude(:test_invalid_escape_error, "unknown")
+exclude(:test_invalid_fragment, "https://github.com/ruby/prism/issues/2664")
+exclude(:test_unescape, "unknown")
+exclude(:test_unicode_age_14_0, "https://github.com/ruby/prism/issues/2664")
+exclude(:test_unicode_age_15_0, "https://github.com/ruby/prism/issues/2664")
+exclude(:test_unicode_age, "https://github.com/ruby/prism/issues/2664")
diff --git a/test/.excludes-prism/TestRequire.rb b/test/.excludes-prism/TestRequire.rb
new file mode 100644
index 0000000000..a7f66c5d80
--- /dev/null
+++ b/test/.excludes-prism/TestRequire.rb
@@ -0,0 +1 @@
+exclude(:test_require_nonascii_path_shift_jis, "encoding")
diff --git a/test/.excludes-prism/TestRubyLiteral.rb b/test/.excludes-prism/TestRubyLiteral.rb
new file mode 100644
index 0000000000..926f0c5a5e
--- /dev/null
+++ b/test/.excludes-prism/TestRubyLiteral.rb
@@ -0,0 +1,5 @@
+exclude(:test_debug_frozen_string_in_array_literal, "unknown")
+exclude(:test_debug_frozen_string, "unknown")
+exclude(:test_dregexp, "https://github.com/ruby/prism/issues/2664")
+exclude(:test_hash_value_omission, "unknown")
+exclude(:test_string, "https://github.com/ruby/prism/issues/2331")
diff --git a/test/.excludes-prism/TestRubyOptimization.rb b/test/.excludes-prism/TestRubyOptimization.rb
new file mode 100644
index 0000000000..df22ca4f71
--- /dev/null
+++ b/test/.excludes-prism/TestRubyOptimization.rb
@@ -0,0 +1 @@
+exclude(:test_peephole_string_literal_range, "unknown")
diff --git a/test/.excludes-prism/TestRubyVM.rb b/test/.excludes-prism/TestRubyVM.rb
new file mode 100644
index 0000000000..6d4c3ca6fe
--- /dev/null
+++ b/test/.excludes-prism/TestRubyVM.rb
@@ -0,0 +1 @@
+exclude(:test_keep_script_lines, "unknown")
diff --git a/test/.excludes-prism/TestSetTraceFunc.rb b/test/.excludes-prism/TestSetTraceFunc.rb
new file mode 100644
index 0000000000..036faef650
--- /dev/null
+++ b/test/.excludes-prism/TestSetTraceFunc.rb
@@ -0,0 +1 @@
+exclude(:test_return, "unknown")
diff --git a/test/.excludes-prism/TestSyntax.rb b/test/.excludes-prism/TestSyntax.rb
new file mode 100644
index 0000000000..61051e2f37
--- /dev/null
+++ b/test/.excludes-prism/TestSyntax.rb
@@ -0,0 +1,29 @@
+exclude(:test__END___cr, "unknown")
+exclude(:test_anonymous_block_forwarding, "unknown")
+exclude(:test_anonymous_keyword_rest_forwarding, "unknown")
+exclude(:test_anonymous_rest_forwarding, "unknown")
+exclude(:test_argument_forwarding_with_super, "unknown")
+exclude(:test_argument_forwarding, "unknown")
+exclude(:test_brace_after_literal_argument, "unknown")
+exclude(:test_dedented_heredoc_concatenation, "unknown")
+exclude(:test_dedented_heredoc_continued_line, "unknown")
+exclude(:test_dedented_heredoc_invalid_identifer, "unknown")
+exclude(:test_duplicated_when, "unknown")
+exclude(:test_error_message_encoding, "unknown")
+exclude(:test_heredoc_cr, "unknown")
+exclude(:test_heredoc_no_terminator, "unknown")
+exclude(:test_invalid_encoding_symbol, "unknown")
+exclude(:test_it, "https://github.com/ruby/prism/issues/2323")
+exclude(:test_keyword_invalid_name, "unknown")
+exclude(:test_keyword_self_reference, "unknown")
+exclude(:test_keywords_specified_and_not_accepted, "unknown")
+exclude(:test_methoddef_endless_command, "unknown")
+exclude(:test_numbered_parameter, "unknown")
+exclude(:test_optional_self_reference, "unknown")
+exclude(:test_safe_call_in_massign_lhs, "unknown")
+exclude(:test_syntax_error_at_newline, "unknown")
+exclude(:test_unexpected_fraction, "unknown")
+exclude(:test_unterminated_heredoc_cr, "unknown")
+exclude(:test_unterminated_heredoc, "unknown")
+exclude(:test_warn_balanced, "unknown")
+exclude(:test_warn_unreachable, "unknown")
diff --git a/test/.excludes-prism/TestUnicodeEscape.rb b/test/.excludes-prism/TestUnicodeEscape.rb
new file mode 100644
index 0000000000..add4911bc2
--- /dev/null
+++ b/test/.excludes-prism/TestUnicodeEscape.rb
@@ -0,0 +1 @@
+exclude(:test_fail, "unknown")
diff --git a/test/excludes/TestArray.rb b/test/.excludes/TestArray.rb
index 73da272007..73da272007 100644
--- a/test/excludes/TestArray.rb
+++ b/test/.excludes/TestArray.rb
diff --git a/test/excludes/TestArraySubclass.rb b/test/.excludes/TestArraySubclass.rb
index 73da272007..73da272007 100644
--- a/test/excludes/TestArraySubclass.rb
+++ b/test/.excludes/TestArraySubclass.rb
diff --git a/test/excludes/TestException.rb b/test/.excludes/TestException.rb
index 38d66d63d2..38d66d63d2 100644
--- a/test/excludes/TestException.rb
+++ b/test/.excludes/TestException.rb
diff --git a/test/excludes/TestGem.rb b/test/.excludes/TestGem.rb
index 042af26eff..042af26eff 100644
--- a/test/excludes/TestGem.rb
+++ b/test/.excludes/TestGem.rb
diff --git a/test/excludes/TestIO_Console.rb b/test/.excludes/TestIO_Console.rb
index caf1935fec..caf1935fec 100644
--- a/test/excludes/TestIO_Console.rb
+++ b/test/.excludes/TestIO_Console.rb
diff --git a/test/excludes/TestISeq.rb b/test/.excludes/TestISeq.rb
index b99181eeaf..b99181eeaf 100644
--- a/test/excludes/TestISeq.rb
+++ b/test/.excludes/TestISeq.rb
diff --git a/test/.excludes/TestThread.rb b/test/.excludes/TestThread.rb
new file mode 100644
index 0000000000..f26ea420a6
--- /dev/null
+++ b/test/.excludes/TestThread.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: false
+exclude(/_stack_size$/, 'often too expensive')
+if /freebsd13/ =~ RUBY_PLATFORM
+ # http://rubyci.s3.amazonaws.com/freebsd13/ruby-master/log/20220216T143001Z.fail.html.gz
+ #
+ # 1) Error:
+ # TestThread#test_signal_at_join:
+ # Timeout::Error: execution of assert_separately expired timeout (120 sec)
+ # pid 30743 killed by SIGABRT (signal 6) (core dumped)
+ # |
+ #
+ # /usr/home/chkbuild/chkbuild/tmp/build/20220216T143001Z/ruby/test/ruby/test_thread.rb:1390:in `test_signal_at_join'
+ exclude(:test_signal_at_join, 'gets stuck somewhere')
+end
+if /mswin/ =~ RUBY_PLATFORM && ENV.key?('GITHUB_ACTIONS')
+ # to avoid "`failed to allocate memory (NoMemoryError)" error
+ exclude(:test_thread_interrupt_for_killed_thread, 'TODO')
+end
diff --git a/test/excludes/TestThreadQueue.rb b/test/.excludes/TestThreadQueue.rb
index c8231e372a..c8231e372a 100644
--- a/test/excludes/TestThreadQueue.rb
+++ b/test/.excludes/TestThreadQueue.rb
diff --git a/test/excludes/_appveyor/TestArray.rb b/test/.excludes/_appveyor/TestArray.rb
index 7d03833f07..7d03833f07 100644
--- a/test/excludes/_appveyor/TestArray.rb
+++ b/test/.excludes/_appveyor/TestArray.rb
diff --git a/test/base64/test_base64.rb b/test/base64/test_base64.rb
deleted file mode 100644
index ce716043a8..0000000000
--- a/test/base64/test_base64.rb
+++ /dev/null
@@ -1,115 +0,0 @@
-# coding: US-ASCII
-# frozen_string_literal: true
-require "test/unit"
-require "base64"
-
-class TestBase64 < Test::Unit::TestCase
- def test_sample
- assert_equal("U2VuZCByZWluZm9yY2VtZW50cw==\n", Base64.encode64('Send reinforcements'))
- assert_equal('Send reinforcements', Base64.decode64("U2VuZCByZWluZm9yY2VtZW50cw==\n"))
- assert_equal(
- "Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g\nUnVieQ==\n",
- Base64.encode64("Now is the time for all good coders\nto learn Ruby"))
- assert_equal(
- "Now is the time for all good coders\nto learn Ruby",
- Base64.decode64("Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g\nUnVieQ==\n"))
- assert_equal(
- "VGhpcyBpcyBsaW5lIG9uZQpUaGlzIGlzIGxpbmUgdHdvClRoaXMgaXMgbGlu\nZSB0aHJlZQpBbmQgc28gb24uLi4K\n",
- Base64.encode64("This is line one\nThis is line two\nThis is line three\nAnd so on...\n"))
- assert_equal(
- "This is line one\nThis is line two\nThis is line three\nAnd so on...\n",
- Base64.decode64("VGhpcyBpcyBsaW5lIG9uZQpUaGlzIGlzIGxpbmUgdHdvClRoaXMgaXMgbGluZSB0aHJlZQpBbmQgc28gb24uLi4K"))
- end
-
- def test_encode64
- assert_equal("", Base64.encode64(""))
- assert_equal("AA==\n", Base64.encode64("\0"))
- assert_equal("AAA=\n", Base64.encode64("\0\0"))
- assert_equal("AAAA\n", Base64.encode64("\0\0\0"))
- assert_equal("/w==\n", Base64.encode64("\377"))
- assert_equal("//8=\n", Base64.encode64("\377\377"))
- assert_equal("////\n", Base64.encode64("\377\377\377"))
- assert_equal("/+8=\n", Base64.encode64("\xff\xef"))
- end
-
- def test_decode64
- assert_equal("", Base64.decode64(""))
- assert_equal("\0", Base64.decode64("AA==\n"))
- assert_equal("\0\0", Base64.decode64("AAA=\n"))
- assert_equal("\0\0\0", Base64.decode64("AAAA\n"))
- assert_equal("\377", Base64.decode64("/w==\n"))
- assert_equal("\377\377", Base64.decode64("//8=\n"))
- assert_equal("\377\377\377", Base64.decode64("////\n"))
- assert_equal("\xff\xef", Base64.decode64("/+8=\n"))
- end
-
- def test_strict_encode64
- assert_equal("", Base64.strict_encode64(""))
- assert_equal("AA==", Base64.strict_encode64("\0"))
- assert_equal("AAA=", Base64.strict_encode64("\0\0"))
- assert_equal("AAAA", Base64.strict_encode64("\0\0\0"))
- assert_equal("/w==", Base64.strict_encode64("\377"))
- assert_equal("//8=", Base64.strict_encode64("\377\377"))
- assert_equal("////", Base64.strict_encode64("\377\377\377"))
- assert_equal("/+8=", Base64.strict_encode64("\xff\xef"))
- end
-
- def test_strict_decode64
- assert_equal("", Base64.strict_decode64(""))
- assert_equal("\0", Base64.strict_decode64("AA=="))
- assert_equal("\0\0", Base64.strict_decode64("AAA="))
- assert_equal("\0\0\0", Base64.strict_decode64("AAAA"))
- assert_equal("\377", Base64.strict_decode64("/w=="))
- assert_equal("\377\377", Base64.strict_decode64("//8="))
- assert_equal("\377\377\377", Base64.strict_decode64("////"))
- assert_equal("\xff\xef", Base64.strict_decode64("/+8="))
-
- assert_raise(ArgumentError) { Base64.strict_decode64("^") }
- assert_raise(ArgumentError) { Base64.strict_decode64("A") }
- assert_raise(ArgumentError) { Base64.strict_decode64("A^") }
- assert_raise(ArgumentError) { Base64.strict_decode64("AA") }
- assert_raise(ArgumentError) { Base64.strict_decode64("AA=") }
- assert_raise(ArgumentError) { Base64.strict_decode64("AA===") }
- assert_raise(ArgumentError) { Base64.strict_decode64("AA=x") }
- assert_raise(ArgumentError) { Base64.strict_decode64("AAA") }
- assert_raise(ArgumentError) { Base64.strict_decode64("AAA^") }
- assert_raise(ArgumentError) { Base64.strict_decode64("AB==") }
- assert_raise(ArgumentError) { Base64.strict_decode64("AAB=") }
- end
-
- def test_urlsafe_encode64
- assert_equal("", Base64.urlsafe_encode64(""))
- assert_equal("AA==", Base64.urlsafe_encode64("\0"))
- assert_equal("AAA=", Base64.urlsafe_encode64("\0\0"))
- assert_equal("AAAA", Base64.urlsafe_encode64("\0\0\0"))
- assert_equal("_w==", Base64.urlsafe_encode64("\377"))
- assert_equal("__8=", Base64.urlsafe_encode64("\377\377"))
- assert_equal("____", Base64.urlsafe_encode64("\377\377\377"))
- assert_equal("_-8=", Base64.urlsafe_encode64("\xff\xef"))
- end
-
- def test_urlsafe_encode64_unpadded
- assert_equal("", Base64.urlsafe_encode64("", padding: false))
- assert_equal("AA", Base64.urlsafe_encode64("\0", padding: false))
- assert_equal("AAA", Base64.urlsafe_encode64("\0\0", padding: false))
- assert_equal("AAAA", Base64.urlsafe_encode64("\0\0\0", padding: false))
- end
-
- def test_urlsafe_decode64
- assert_equal("", Base64.urlsafe_decode64(""))
- assert_equal("\0", Base64.urlsafe_decode64("AA=="))
- assert_equal("\0\0", Base64.urlsafe_decode64("AAA="))
- assert_equal("\0\0\0", Base64.urlsafe_decode64("AAAA"))
- assert_equal("\377", Base64.urlsafe_decode64("_w=="))
- assert_equal("\377\377", Base64.urlsafe_decode64("__8="))
- assert_equal("\377\377\377", Base64.urlsafe_decode64("____"))
- assert_equal("\xff\xef", Base64.urlsafe_decode64("_+8="))
- end
-
- def test_urlsafe_decode64_unpadded
- assert_equal("\0", Base64.urlsafe_decode64("AA"))
- assert_equal("\0\0", Base64.urlsafe_decode64("AAA"))
- assert_equal("\0\0\0", Base64.urlsafe_decode64("AAAA"))
- assert_raise(ArgumentError) { Base64.urlsafe_decode64("AA=") }
- end
-end
diff --git a/test/bigdecimal/helper.rb b/test/bigdecimal/helper.rb
deleted file mode 100644
index 46721fb9a8..0000000000
--- a/test/bigdecimal/helper.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-# frozen_string_literal: false
-require "test/unit"
-require "bigdecimal"
-require 'rbconfig/sizeof'
-
-module TestBigDecimalBase
- if RbConfig::SIZEOF.key?("int64_t")
- SIZEOF_DECDIG = RbConfig::SIZEOF["int32_t"]
- BASE = 1_000_000_000
- BASE_FIG = 9
- else
- SIZEOF_DECDIG = RbConfig::SIZEOF["int16_t"]
- BASE = 1000
- BASE_FIG = 4
- end
-
- def setup
- @mode = BigDecimal.mode(BigDecimal::EXCEPTION_ALL)
- BigDecimal.mode(BigDecimal::EXCEPTION_ALL, true)
- BigDecimal.mode(BigDecimal::EXCEPTION_UNDERFLOW, true)
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, true)
- BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_UP)
- BigDecimal.limit(0)
- end
-
- def teardown
- [BigDecimal::EXCEPTION_INFINITY, BigDecimal::EXCEPTION_NaN,
- BigDecimal::EXCEPTION_UNDERFLOW, BigDecimal::EXCEPTION_OVERFLOW].each do |mode|
- BigDecimal.mode(mode, !(@mode & mode).zero?)
- end
- end
-
- def under_gc_stress
- stress, GC.stress = GC.stress, true
- yield
- ensure
- GC.stress = stress
- end
-end
diff --git a/test/bigdecimal/test_bigdecimal.rb b/test/bigdecimal/test_bigdecimal.rb
deleted file mode 100644
index 2fc22d224f..0000000000
--- a/test/bigdecimal/test_bigdecimal.rb
+++ /dev/null
@@ -1,2300 +0,0 @@
-# frozen_string_literal: false
-require_relative "helper"
-require 'bigdecimal/math'
-
-class TestBigDecimal < Test::Unit::TestCase
- include TestBigDecimalBase
-
- if defined? RbConfig::LIMITS
- LIMITS = RbConfig::LIMITS
- else
- require 'fiddle'
- LONG_MAX = (1 << (Fiddle::SIZEOF_LONG*8 - 1)) - 1
- LONG_MIN = [LONG_MAX + 1].pack("L!").unpack("l!")[0]
- LLONG_MAX = (1 << (Fiddle::SIZEOF_LONG_LONG*8 - 1)) - 1
- LLONG_MIN = [LLONG_MAX + 1].pack("Q!").unpack("q!")[0]
- ULLONG_MAX = (1 << Fiddle::SIZEOF_LONG_LONG*8) - 1
- LIMITS = {
- "LLONG_MIN" => LLONG_MIN,
- "ULLONG_MAX" => ULLONG_MAX,
- "FIXNUM_MIN" => LONG_MIN / 2,
- "FIXNUM_MAX" => LONG_MAX / 2,
- "INT64_MIN" => -9223372036854775808,
- "INT64_MAX" => 9223372036854775807,
- "UINT64_MAX" => 18446744073709551615,
- }.freeze
- end
-
- ROUNDING_MODE_MAP = [
- [ BigDecimal::ROUND_UP, :up],
- [ BigDecimal::ROUND_DOWN, :down],
- [ BigDecimal::ROUND_DOWN, :truncate],
- [ BigDecimal::ROUND_HALF_UP, :half_up],
- [ BigDecimal::ROUND_HALF_UP, :default],
- [ BigDecimal::ROUND_HALF_DOWN, :half_down],
- [ BigDecimal::ROUND_HALF_EVEN, :half_even],
- [ BigDecimal::ROUND_HALF_EVEN, :banker],
- [ BigDecimal::ROUND_CEILING, :ceiling],
- [ BigDecimal::ROUND_CEILING, :ceil],
- [ BigDecimal::ROUND_FLOOR, :floor],
- ]
-
- def assert_nan(x)
- assert(x.nan?, "Expected #{x.inspect} to be NaN")
- end
-
- def assert_positive_infinite(x)
- assert(x.infinite?, "Expected #{x.inspect} to be positive infinite")
- assert_operator(x, :>, 0)
- end
-
- def assert_negative_infinite(x)
- assert(x.infinite?, "Expected #{x.inspect} to be negative infinite")
- assert_operator(x, :<, 0)
- end
-
- def assert_positive_zero(x)
- assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, x.sign,
- "Expected #{x.inspect} to be positive zero")
- end
-
- def assert_negative_zero(x)
- assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, x.sign,
- "Expected #{x.inspect} to be negative zero")
- end
-
- def test_not_equal
- assert_not_equal BigDecimal("1"), BigDecimal("2")
- end
-
- def test_BigDecimal
- assert_equal(1, BigDecimal("1"))
- assert_equal(1, BigDecimal("1", 1))
- assert_equal(1, BigDecimal(" 1 "))
- assert_equal(111, BigDecimal("1_1_1_"))
- assert_equal(10**(-1), BigDecimal("1E-1"), '#4825')
- assert_equal(1234, BigDecimal(" \t\n\r \r1234 \t\n\r \r"))
-
- assert_raise(ArgumentError) { BigDecimal("1", -1) }
- assert_raise_with_message(ArgumentError, /"1__1_1"/) { BigDecimal("1__1_1") }
- assert_raise_with_message(ArgumentError, /"_1_1_1"/) { BigDecimal("_1_1_1") }
-
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- assert_positive_infinite(BigDecimal("Infinity"))
- assert_positive_infinite(BigDecimal("1E1111111111111111111"))
- assert_positive_infinite(BigDecimal(" \t\n\r \rInfinity \t\n\r \r"))
- assert_negative_infinite(BigDecimal("-Infinity"))
- assert_negative_infinite(BigDecimal(" \t\n\r \r-Infinity \t\n\r \r"))
- assert_nan(BigDecimal("NaN"))
- assert_nan(BigDecimal(" \t\n\r \rNaN \t\n\r \r"))
- end
- end
-
- def test_BigDecimal_bug7522
- bd = BigDecimal("1.12", 1)
- assert_same(bd, BigDecimal(bd))
- assert_same(bd, BigDecimal(bd, exception: false))
- assert_not_same(bd, BigDecimal(bd, 1))
- assert_not_same(bd, BigDecimal(bd, 1, exception: false))
- end
-
- def test_BigDecimal_issue_192
- # https://github.com/ruby/bigdecimal/issues/192
- # https://github.com/rails/rails/pull/42125
- if BASE_FIG == 9
- int = 1_000_000_000_12345_0000
- big = BigDecimal("0.100000000012345e19")
- else # BASE_FIG == 4
- int = 1_0000_12_00
- big = BigDecimal("0.1000012e9")
- end
- assert_equal(BigDecimal(int), big, "[ruby/bigdecimal#192]")
- end
-
- def test_BigDecimal_with_invalid_string
- [
- '', '.', 'e1', 'd1', '.e', '.d', '1.e', '1.d', '.1e', '.1d',
- '2,30', '19,000.0', '-2,30', '-19,000.0', '+2,30', '+19,000.0',
- '2.3,0', '19.000,0', '-2.3,0', '-19.000,0', '+2.3,0', '+19.000,0',
- '2.3.0', '19.000.0', '-2.3.0', '-19.000.0', '+2.3.0', '+19.000.0',
- 'invlaid value', '123 xyz'
- ].each do |invalid_string|
- assert_raise_with_message(ArgumentError, %Q[invalid value for BigDecimal(): "#{invalid_string}"]) do
- BigDecimal(invalid_string)
- end
- end
-
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- assert_raise_with_message(ArgumentError, /"Infinity_"/) { BigDecimal("Infinity_") }
- assert_raise_with_message(ArgumentError, /"\+Infinity_"/) { BigDecimal("+Infinity_") }
- assert_raise_with_message(ArgumentError, /"-Infinity_"/) { BigDecimal("-Infinity_") }
- assert_raise_with_message(ArgumentError, /"NaN_"/) { BigDecimal("NaN_") }
- end
- end
-
- def test_BigDecimal_with_integer
- assert_equal(BigDecimal("0"), BigDecimal(0))
- assert_equal(BigDecimal("1"), BigDecimal(1))
- assert_equal(BigDecimal("-1"), BigDecimal(-1))
- assert_equal(BigDecimal((2**100).to_s), BigDecimal(2**100))
- assert_equal(BigDecimal((-2**100).to_s), BigDecimal(-2**100))
-
- assert_equal(BigDecimal(LIMITS["FIXNUM_MIN"].to_s), BigDecimal(LIMITS["FIXNUM_MIN"]))
-
- assert_equal(BigDecimal(LIMITS["FIXNUM_MAX"].to_s), BigDecimal(LIMITS["FIXNUM_MAX"]))
-
- assert_equal(BigDecimal(LIMITS["INT64_MIN"].to_s), BigDecimal(LIMITS["INT64_MIN"]))
-
- assert_equal(BigDecimal(LIMITS["INT64_MAX"].to_s), BigDecimal(LIMITS["INT64_MAX"]))
-
- assert_equal(BigDecimal(LIMITS["UINT64_MAX"].to_s), BigDecimal(LIMITS["UINT64_MAX"]))
- end
-
- def test_BigDecimal_with_rational
- assert_equal(BigDecimal("0.333333333333333333333"), BigDecimal(1.quo(3), 21))
- assert_equal(BigDecimal("-0.333333333333333333333"), BigDecimal(-1.quo(3), 21))
- assert_raise_with_message(ArgumentError, "can't omit precision for a Rational.") { BigDecimal(42.quo(7)) }
- end
-
- def test_BigDecimal_with_float
- assert_equal(BigDecimal("0.1235"), BigDecimal(0.1234567, 4))
- assert_equal(BigDecimal("-0.1235"), BigDecimal(-0.1234567, 4))
- assert_equal(BigDecimal("0.01"), BigDecimal(0.01, Float::DIG + 1))
- assert_raise_with_message(ArgumentError, "can't omit precision for a Float.") { BigDecimal(4.2) }
- assert_raise(ArgumentError) { BigDecimal(0.1, Float::DIG + 2) }
- assert_nothing_raised { BigDecimal(0.1, Float::DIG + 1) }
-
- assert_same(BigDecimal(0.0), BigDecimal(0.0))
- assert_same(BigDecimal(-0.0), BigDecimal(-0.0))
-
- bug9214 = '[ruby-core:58858]'
- assert_equal(BigDecimal(-0.0).sign, -1, bug9214)
-
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- assert_nan(BigDecimal(Float::NAN))
- assert_same(BigDecimal(Float::NAN), BigDecimal(Float::NAN))
- end
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
- assert_positive_infinite(BigDecimal(Float::INFINITY))
- assert_same(BigDecimal(Float::INFINITY), BigDecimal(Float::INFINITY))
- assert_negative_infinite(BigDecimal(-Float::INFINITY))
- assert_same(BigDecimal(-Float::INFINITY), BigDecimal(-Float::INFINITY))
- end
- end
-
- def test_BigDecimal_with_complex
- assert_equal(BigDecimal("1"), BigDecimal(Complex(1, 0)))
- assert_equal(BigDecimal("0.333333333333333333333"), BigDecimal(Complex(1.quo(3), 0), 21))
- assert_equal(BigDecimal("0.1235"), BigDecimal(Complex(0.1234567, 0), 4))
-
- assert_raise_with_message(ArgumentError, "Unable to make a BigDecimal from non-zero imaginary number") { BigDecimal(Complex(1, 1)) }
- end
-
- def test_BigDecimal_with_big_decimal
- assert_equal(BigDecimal(1), BigDecimal(BigDecimal(1)))
- assert_equal(BigDecimal('+0'), BigDecimal(BigDecimal('+0')))
- assert_equal(BigDecimal('-0'), BigDecimal(BigDecimal('-0')))
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- assert_positive_infinite(BigDecimal(BigDecimal('Infinity')))
- assert_negative_infinite(BigDecimal(BigDecimal('-Infinity')))
- assert_nan(BigDecimal(BigDecimal('NaN')))
- end
- end
-
- if RUBY_VERSION < '2.7'
- def test_BigDecimal_with_tainted_string
- Thread.new {
- $SAFE = 1
- BigDecimal('1'.taint)
- }.join
- ensure
- $SAFE = 0
- end
- end
-
- def test_BigDecimal_with_exception_keyword
- assert_raise(ArgumentError) {
- BigDecimal('.', exception: true)
- }
- assert_nothing_raised(ArgumentError) {
- assert_equal(nil, BigDecimal(".", exception: false))
- }
- assert_raise(ArgumentError) {
- BigDecimal("1", -1, exception: true)
- }
- assert_nothing_raised(ArgumentError) {
- assert_equal(nil, BigDecimal("1", -1, exception: false))
- }
- assert_raise(ArgumentError) {
- BigDecimal(42.quo(7), exception: true)
- }
- assert_nothing_raised(ArgumentError) {
- assert_equal(nil, BigDecimal(42.quo(7), exception: false))
- }
- assert_raise(ArgumentError) {
- BigDecimal(4.2, exception: true)
- }
- assert_nothing_raised(ArgumentError) {
- assert_equal(nil, BigDecimal(4.2, exception: false))
- }
- # TODO: support conversion from complex
- # assert_raise(RangeError) {
- # BigDecimal(1i, exception: true)
- # }
- # assert_nothing_raised(RangeError) {
- # assert_equal(nil, BigDecimal(1i, exception: false))
- # }
- assert_raise_with_message(TypeError, "can't convert nil into BigDecimal") {
- BigDecimal(nil, exception: true)
- }
- assert_raise_with_message(TypeError, "can't convert true into BigDecimal") {
- BigDecimal(true, exception: true)
- }
- assert_raise_with_message(TypeError, "can't convert false into BigDecimal") {
- BigDecimal(false, exception: true)
- }
- assert_raise_with_message(TypeError, "can't convert Object into BigDecimal") {
- BigDecimal(Object.new, exception: true)
- }
- assert_nothing_raised(TypeError) {
- assert_equal(nil, BigDecimal(nil, exception: false))
- }
- assert_nothing_raised(TypeError) {
- assert_equal(nil, BigDecimal(:test, exception: false))
- }
- assert_nothing_raised(TypeError) {
- assert_equal(nil, BigDecimal(Object.new, exception: false))
- }
- assert_nothing_raised(TypeError) {
- assert_equal(nil, BigDecimal(Object.new, exception: false))
- }
- # TODO: support to_d
- # assert_nothing_raised(TypeError) {
- # o = Object.new
- # def o.to_d; 3.14; end
- # assert_equal(3.14, BigDecimal(o, exception: false))
- # }
- # assert_nothing_raised(RuntimeError) {
- # o = Object.new
- # def o.to_d; raise; end
- # assert_equal(nil, BigDecimal(o, exception: false))
- # }
- end
-
- def test_s_ver
- assert_raise_with_message(NoMethodError, /undefined method `ver'/) { BigDecimal.ver }
- end
-
- def test_s_allocate
- if RUBY_ENGINE == "truffleruby"
- assert_raise_with_message(NoMethodError, /undefined.+allocate.+for BigDecimal/) { BigDecimal.allocate }
- else
- assert_raise_with_message(TypeError, /allocator undefined for BigDecimal/) { BigDecimal.allocate }
- end
- end
-
- def test_s_new
- assert_raise_with_message(NoMethodError, /undefined method `new'/) { BigDecimal.new("1") }
- end
-
- def test_s_interpret_loosely
- assert_equal(BigDecimal('1'), BigDecimal.interpret_loosely("1__1_1"))
- assert_equal(BigDecimal('2.5'), BigDecimal.interpret_loosely("2.5"))
- assert_equal(BigDecimal('2.5'), BigDecimal.interpret_loosely("2.5 degrees"))
- assert_equal(BigDecimal('2.5e1'), BigDecimal.interpret_loosely("2.5e1 degrees"))
- assert_equal(BigDecimal('0'), BigDecimal.interpret_loosely("degrees 100.0"))
- assert_equal(BigDecimal('0.125'), BigDecimal.interpret_loosely("0.1_2_5"))
- assert_equal(BigDecimal('0.125'), BigDecimal.interpret_loosely("0.1_2_5__"))
- assert_equal(BigDecimal('1'), BigDecimal.interpret_loosely("1_.125"))
- assert_equal(BigDecimal('1'), BigDecimal.interpret_loosely("1._125"))
- assert_equal(BigDecimal('0.1'), BigDecimal.interpret_loosely("0.1__2_5"))
- assert_equal(BigDecimal('0.1'), BigDecimal.interpret_loosely("0.1_e10"))
- assert_equal(BigDecimal('0.1'), BigDecimal.interpret_loosely("0.1e_10"))
- assert_equal(BigDecimal('1'), BigDecimal.interpret_loosely("0.1e1__0"))
- assert_equal(BigDecimal('1.2'), BigDecimal.interpret_loosely("1.2.3"))
- assert_equal(BigDecimal('1'), BigDecimal.interpret_loosely("1."))
- assert_equal(BigDecimal('1'), BigDecimal.interpret_loosely("1e"))
-
- assert_equal(BigDecimal('0.0'), BigDecimal.interpret_loosely("invalid"))
-
- assert(BigDecimal.interpret_loosely("2.5").frozen?)
- end
-
- def _test_mode(type)
- BigDecimal.mode(type, true)
- assert_raise(FloatDomainError) { yield }
-
- BigDecimal.mode(type, false)
- assert_nothing_raised { yield }
- end
-
- def test_mode
- assert_raise(ArgumentError) { BigDecimal.mode(BigDecimal::EXCEPTION_ALL, 1) }
- assert_raise(ArgumentError) { BigDecimal.mode(BigDecimal::ROUND_MODE, 256) }
- assert_raise(ArgumentError) { BigDecimal.mode(BigDecimal::ROUND_MODE, :xyzzy) }
- assert_raise(TypeError) { BigDecimal.mode(0xf000, true) }
-
- begin
- saved_mode = BigDecimal.mode(BigDecimal::ROUND_MODE)
-
- [ BigDecimal::ROUND_UP,
- BigDecimal::ROUND_DOWN,
- BigDecimal::ROUND_HALF_UP,
- BigDecimal::ROUND_HALF_DOWN,
- BigDecimal::ROUND_CEILING,
- BigDecimal::ROUND_FLOOR,
- BigDecimal::ROUND_HALF_EVEN,
- ].each do |mode|
- BigDecimal.mode(BigDecimal::ROUND_MODE, mode)
- assert_equal(mode, BigDecimal.mode(BigDecimal::ROUND_MODE))
- end
- ensure
- BigDecimal.mode(BigDecimal::ROUND_MODE, saved_mode)
- end
-
- BigDecimal.save_rounding_mode do
- ROUNDING_MODE_MAP.each do |const, sym|
- BigDecimal.mode(BigDecimal::ROUND_MODE, sym)
- assert_equal(const, BigDecimal.mode(BigDecimal::ROUND_MODE))
- end
- end
- end
-
- def test_thread_local_mode
- begin
- saved_mode = BigDecimal.mode(BigDecimal::ROUND_MODE)
-
- BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_UP)
- Thread.start {
- BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_EVEN)
- assert_equal(BigDecimal::ROUND_HALF_EVEN, BigDecimal.mode(BigDecimal::ROUND_MODE))
- }.join
- assert_equal(BigDecimal::ROUND_UP, BigDecimal.mode(BigDecimal::ROUND_MODE))
- ensure
- BigDecimal.mode(BigDecimal::ROUND_MODE, saved_mode)
- end
- end
-
- def test_save_exception_mode
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- mode = BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW)
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, true)
- end
- assert_equal(mode, BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW))
-
- BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_FLOOR)
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_EVEN)
- end
- assert_equal(BigDecimal::ROUND_HALF_EVEN, BigDecimal.mode(BigDecimal::ROUND_MODE))
-
- assert_equal(42, BigDecimal.save_exception_mode { 42 })
- end
-
- def test_save_rounding_mode
- saved_mode = BigDecimal.mode(BigDecimal::ROUND_MODE)
-
- BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_FLOOR)
- BigDecimal.save_rounding_mode do
- BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_EVEN)
- end
- assert_equal(BigDecimal::ROUND_FLOOR, BigDecimal.mode(BigDecimal::ROUND_MODE))
-
- assert_equal(42, BigDecimal.save_rounding_mode { 42 })
- ensure
- BigDecimal.mode(BigDecimal::ROUND_MODE, saved_mode)
- end
-
- def test_save_limit
- begin
- old = BigDecimal.limit
- BigDecimal.limit(100)
- BigDecimal.save_limit do
- BigDecimal.limit(200)
- end
- assert_equal(100, BigDecimal.limit);
- ensure
- BigDecimal.limit(old)
- end
-
- assert_equal(42, BigDecimal.save_limit { 42 })
- end
-
- def test_exception_nan
- _test_mode(BigDecimal::EXCEPTION_NaN) { BigDecimal("NaN") }
- end
-
- def test_exception_infinity
- _test_mode(BigDecimal::EXCEPTION_INFINITY) { BigDecimal("Infinity") }
- end
-
- def test_exception_underflow
- _test_mode(BigDecimal::EXCEPTION_UNDERFLOW) do
- x = BigDecimal("0.1")
- 100.times do
- x *= x
- end
- end
- end
-
- def test_exception_overflow
- _test_mode(BigDecimal::EXCEPTION_OVERFLOW) do
- x = BigDecimal("10")
- 100.times do
- x *= x
- end
- end
- end
-
- def test_exception_zerodivide
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- _test_mode(BigDecimal::EXCEPTION_ZERODIVIDE) { 1 / BigDecimal("0") }
- _test_mode(BigDecimal::EXCEPTION_ZERODIVIDE) { -1 / BigDecimal("0") }
- end
-
- def test_round_up
- n4 = BigDecimal("4") # n4 / 9 = 0.44444...
- n5 = BigDecimal("5") # n5 / 9 = 0.55555...
- n6 = BigDecimal("6") # n6 / 9 = 0.66666...
- m4, m5, m6 = -n4, -n5, -n6
- n2h = BigDecimal("2.5")
- n3h = BigDecimal("3.5")
- m2h, m3h = -n2h, -n3h
-
- BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_UP)
- assert_operator(n4, :<, n4 / 9 * 9)
- assert_operator(n5, :<, n5 / 9 * 9)
- assert_operator(n6, :<, n6 / 9 * 9)
- assert_operator(m4, :>, m4 / 9 * 9)
- assert_operator(m5, :>, m5 / 9 * 9)
- assert_operator(m6, :>, m6 / 9 * 9)
- assert_equal(3, n2h.round)
- assert_equal(4, n3h.round)
- assert_equal(-3, m2h.round)
- assert_equal(-4, m3h.round)
-
- BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_DOWN)
- assert_operator(n4, :>, n4 / 9 * 9)
- assert_operator(n5, :>, n5 / 9 * 9)
- assert_operator(n6, :>, n6 / 9 * 9)
- assert_operator(m4, :<, m4 / 9 * 9)
- assert_operator(m5, :<, m5 / 9 * 9)
- assert_operator(m6, :<, m6 / 9 * 9)
- assert_equal(2, n2h.round)
- assert_equal(3, n3h.round)
- assert_equal(-2, m2h.round)
- assert_equal(-3, m3h.round)
-
- BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_UP)
- assert_operator(n4, :>, n4 / 9 * 9)
- assert_operator(n5, :<, n5 / 9 * 9)
- assert_operator(n6, :<, n6 / 9 * 9)
- assert_operator(m4, :<, m4 / 9 * 9)
- assert_operator(m5, :>, m5 / 9 * 9)
- assert_operator(m6, :>, m6 / 9 * 9)
- assert_equal(3, n2h.round)
- assert_equal(4, n3h.round)
- assert_equal(-3, m2h.round)
- assert_equal(-4, m3h.round)
-
- BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_DOWN)
- assert_operator(n4, :>, n4 / 9 * 9)
- assert_operator(n5, :>, n5 / 9 * 9)
- assert_operator(n6, :<, n6 / 9 * 9)
- assert_operator(m4, :<, m4 / 9 * 9)
- assert_operator(m5, :<, m5 / 9 * 9)
- assert_operator(m6, :>, m6 / 9 * 9)
- assert_equal(2, n2h.round)
- assert_equal(3, n3h.round)
- assert_equal(-2, m2h.round)
- assert_equal(-3, m3h.round)
-
- BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_EVEN)
- assert_operator(n4, :>, n4 / 9 * 9)
- assert_operator(n5, :<, n5 / 9 * 9)
- assert_operator(n6, :<, n6 / 9 * 9)
- assert_operator(m4, :<, m4 / 9 * 9)
- assert_operator(m5, :>, m5 / 9 * 9)
- assert_operator(m6, :>, m6 / 9 * 9)
- assert_equal(2, n2h.round)
- assert_equal(4, n3h.round)
- assert_equal(-2, m2h.round)
- assert_equal(-4, m3h.round)
-
- BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_CEILING)
- assert_operator(n4, :<, n4 / 9 * 9)
- assert_operator(n5, :<, n5 / 9 * 9)
- assert_operator(n6, :<, n6 / 9 * 9)
- assert_operator(m4, :<, m4 / 9 * 9)
- assert_operator(m5, :<, m5 / 9 * 9)
- assert_operator(m6, :<, m6 / 9 * 9)
- assert_equal(3, n2h.round)
- assert_equal(4, n3h.round)
- assert_equal(-2, m2h.round)
- assert_equal(-3, m3h.round)
-
- BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_FLOOR)
- assert_operator(n4, :>, n4 / 9 * 9)
- assert_operator(n5, :>, n5 / 9 * 9)
- assert_operator(n6, :>, n6 / 9 * 9)
- assert_operator(m4, :>, m4 / 9 * 9)
- assert_operator(m5, :>, m5 / 9 * 9)
- assert_operator(m6, :>, m6 / 9 * 9)
- assert_equal(2, n2h.round)
- assert_equal(3, n3h.round)
- assert_equal(-3, m2h.round)
- assert_equal(-4, m3h.round)
- end
-
- def test_zero_p
- BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
-
- assert_equal(true, BigDecimal("0").zero?)
- assert_equal(true, BigDecimal("-0").zero?)
- assert_equal(false, BigDecimal("1").zero?)
- assert_equal(true, BigDecimal("0E200000000000000").zero?)
- assert_equal(false, BigDecimal("Infinity").zero?)
- assert_equal(false, BigDecimal("-Infinity").zero?)
- assert_equal(false, BigDecimal("NaN").zero?)
- end
-
- def test_nonzero_p
- BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
-
- assert_equal(nil, BigDecimal("0").nonzero?)
- assert_equal(nil, BigDecimal("-0").nonzero?)
- assert_equal(BigDecimal("1"), BigDecimal("1").nonzero?)
- assert_positive_infinite(BigDecimal("Infinity").nonzero?)
- assert_negative_infinite(BigDecimal("-Infinity").nonzero?)
- assert_nan(BigDecimal("NaN").nonzero?)
- end
-
- def test_double_fig
- assert_kind_of(Integer, BigDecimal.double_fig)
- end
-
- def test_cmp
- n1 = BigDecimal("1")
- n2 = BigDecimal("2")
- assert_equal( 0, n1 <=> n1)
- assert_equal( 1, n2 <=> n1)
- assert_equal(-1, n1 <=> n2)
- assert_operator(n1, :==, n1)
- assert_operator(n1, :!=, n2)
- assert_operator(n1, :<, n2)
- assert_operator(n1, :<=, n1)
- assert_operator(n1, :<=, n2)
- assert_operator(n2, :>, n1)
- assert_operator(n2, :>=, n1)
- assert_operator(n1, :>=, n1)
-
- assert_operator(BigDecimal("-0"), :==, BigDecimal("0"))
- assert_operator(BigDecimal("0"), :<, BigDecimal("1"))
- assert_operator(BigDecimal("1"), :>, BigDecimal("0"))
- assert_operator(BigDecimal("1"), :>, BigDecimal("-1"))
- assert_operator(BigDecimal("-1"), :<, BigDecimal("1"))
- assert_operator(BigDecimal((2**100).to_s), :>, BigDecimal("1"))
- assert_operator(BigDecimal("1"), :<, BigDecimal((2**100).to_s))
-
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- inf = BigDecimal("Infinity")
- assert_operator(inf, :>, 1)
- assert_operator(1, :<, inf)
-
- assert_operator(BigDecimal("1E-1"), :==, 10**(-1), '#4825')
- assert_equal(0, BigDecimal("1E-1") <=> 10**(-1), '#4825')
- end
-
- def test_cmp_issue9192
- bug9192 = '[ruby-core:58756] [#9192]'
- operators = { :== => :==, :< => :>, :> => :<, :<= => :>=, :>= => :<= }
- 5.upto(8) do |i|
- s = "706.0#{i}"
- d = BigDecimal(s)
- f = s.to_f
- operators.each do |op, inv|
- assert_equal(d.send(op, f), f.send(inv, d),
- "(BigDecimal(#{s.inspect}) #{op} #{s}) and (#{s} #{inv} BigDecimal(#{s.inspect})) is different #{bug9192}")
- end
- end
- end
-
- def test_cmp_nan
- n1 = BigDecimal("1")
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- assert_equal(nil, BigDecimal("NaN") <=> n1)
- assert_equal(false, BigDecimal("NaN") > n1)
- assert_equal(nil, BigDecimal("NaN") <=> BigDecimal("NaN"))
- assert_equal(false, BigDecimal("NaN") == BigDecimal("NaN"))
- end
-
- def test_cmp_failing_coercion
- n1 = BigDecimal("1")
- assert_equal(nil, n1 <=> nil)
- assert_raise(ArgumentError){n1 > nil}
- end
-
- def test_cmp_coerce
- n1 = BigDecimal("1")
- n2 = BigDecimal("2")
- o1 = Object.new; def o1.coerce(x); [x, BigDecimal("1")]; end
- o2 = Object.new; def o2.coerce(x); [x, BigDecimal("2")]; end
- assert_equal( 0, n1 <=> o1)
- assert_equal( 1, n2 <=> o1)
- assert_equal(-1, n1 <=> o2)
- assert_operator(n1, :==, o1)
- assert_operator(n1, :!=, o2)
- assert_operator(n1, :<, o2)
- assert_operator(n1, :<=, o1)
- assert_operator(n1, :<=, o2)
- assert_operator(n2, :>, o1)
- assert_operator(n2, :>=, o1)
- assert_operator(n1, :>=, 1)
-
- bug10109 = '[ruby-core:64190]'
- BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
- assert_operator(BigDecimal(0), :<, Float::INFINITY, bug10109)
- assert_operator(Float::INFINITY, :>, BigDecimal(0), bug10109)
- end
-
- def test_cmp_bignum
- assert_operator(BigDecimal((2**100).to_s), :==, 2**100)
- end
-
- def test_cmp_data
- d = Time.now; def d.coerce(x); [x, x]; end
- assert_operator(BigDecimal((2**100).to_s), :==, d)
- end
-
- def test_precs_deprecated
- assert_warn(/BigDecimal#precs is deprecated and will be removed in the future/) do
- Warning[:deprecated] = true if defined?(Warning.[])
- BigDecimal("1").precs
- end
- end
-
- def test_precs
- assert_separately(["-rbigdecimal"], "#{<<~"begin;"}\n#{<<~'end;'}")
- begin;
- $VERBOSE = nil
- a = BigDecimal("1").precs
- assert_instance_of(Array, a)
- assert_equal(2, a.size)
- assert_kind_of(Integer, a[0])
- assert_kind_of(Integer, a[1])
- end;
- end
-
- def test_hash
- a = []
- b = BigDecimal("1")
- 10.times { a << b *= 10 }
- h = {}
- a.each_with_index {|x, i| h[x] = i }
- a.each_with_index do |x, i|
- assert_equal(i, h[x])
- end
- end
-
- def test_marshal
- s = Marshal.dump(BigDecimal("1", 1))
- assert_equal(BigDecimal("1", 1), Marshal.load(s))
-
- # corrupt data
- s = s.gsub(/BigDecimal.*\z/m) {|x| x.gsub(/\d/m, "-") }
- assert_raise(TypeError) { Marshal.load(s) }
- end
-
- def test_finite_infinite_nan
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- BigDecimal.mode(BigDecimal::EXCEPTION_ZERODIVIDE, false)
-
- x = BigDecimal("0")
- assert_equal(true, x.finite?)
- assert_equal(nil, x.infinite?)
- assert_equal(false, x.nan?)
- y = 1 / x
- assert_equal(false, y.finite?)
- assert_equal(1, y.infinite?)
- assert_equal(false, y.nan?)
- y = -1 / x
- assert_equal(false, y.finite?)
- assert_equal(-1, y.infinite?)
- assert_equal(false, y.nan?)
-
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- y = 0 / x
- assert_equal(false, y.finite?)
- assert_equal(nil, y.infinite?)
- assert_equal(true, y.nan?)
- end
-
- def test_to_i
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
-
- x = BigDecimal("0")
- assert_kind_of(Integer, x.to_i)
- assert_equal(0, x.to_i)
- assert_raise(FloatDomainError){( 1 / x).to_i}
- assert_raise(FloatDomainError){(-1 / x).to_i}
- assert_raise(FloatDomainError) {( 0 / x).to_i}
- x = BigDecimal("1")
- assert_equal(1, x.to_i)
- x = BigDecimal((2**100).to_s)
- assert_equal(2**100, x.to_i)
- end
-
- def test_to_f
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- BigDecimal.mode(BigDecimal::EXCEPTION_ZERODIVIDE, false)
-
- x = BigDecimal("0")
- assert_instance_of(Float, x.to_f)
- assert_equal(0.0, x.to_f)
- assert_equal( 1.0 / 0.0, ( 1 / x).to_f)
- assert_equal(-1.0 / 0.0, (-1 / x).to_f)
- assert_nan(( 0 / x).to_f)
- x = BigDecimal("1")
- assert_equal(1.0, x.to_f)
- x = BigDecimal((2**100).to_s)
- assert_equal((2**100).to_f, x.to_f)
- x = BigDecimal("1" + "0" * 10000)
- assert_equal(0, BigDecimal("-0").to_f)
-
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, true)
- assert_raise(FloatDomainError) { x.to_f }
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- assert_kind_of(Float, x .to_f)
- assert_kind_of(Float, (-x).to_f)
-
- bug6944 = '[ruby-core:47342]'
-
- BigDecimal.mode(BigDecimal::EXCEPTION_UNDERFLOW, true)
- x = "1e#{Float::MIN_10_EXP - 2*Float::DIG}"
- assert_raise(FloatDomainError, x) {BigDecimal(x).to_f}
- x = "-#{x}"
- assert_raise(FloatDomainError, x) {BigDecimal(x).to_f}
- x = "1e#{Float::MIN_10_EXP - Float::DIG}"
- assert_nothing_raised(FloatDomainError, x) {
- assert_in_delta(0.0, BigDecimal(x).to_f, 10**Float::MIN_10_EXP, bug6944)
- }
- x = "-#{x}"
- assert_nothing_raised(FloatDomainError, x) {
- assert_in_delta(0.0, BigDecimal(x).to_f, 10**Float::MIN_10_EXP, bug6944)
- }
-
- BigDecimal.mode(BigDecimal::EXCEPTION_UNDERFLOW, false)
- x = "1e#{Float::MIN_10_EXP - 2*Float::DIG}"
- assert_equal( 0.0, BigDecimal(x).to_f, x)
- x = "-#{x}"
- assert_equal(-0.0, BigDecimal(x).to_f, x)
- x = "1e#{Float::MIN_10_EXP - Float::DIG}"
- assert_nothing_raised(FloatDomainError, x) {
- assert_in_delta(0.0, BigDecimal(x).to_f, 10**Float::MIN_10_EXP, bug6944)
- }
- x = "-#{x}"
- assert_nothing_raised(FloatDomainError, x) {
- assert_in_delta(0.0, BigDecimal(x).to_f, 10**Float::MIN_10_EXP, bug6944)
- }
-
- assert_equal( 0.0, BigDecimal( '9e-325').to_f)
- assert_equal( 0.0, BigDecimal( '10e-325').to_f)
- assert_equal(-0.0, BigDecimal( '-9e-325').to_f)
- assert_equal(-0.0, BigDecimal('-10e-325').to_f)
- end
-
- def test_to_r
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
-
- x = BigDecimal("0")
- assert_kind_of(Rational, x.to_r)
- assert_equal(0, x.to_r)
- assert_raise(FloatDomainError) {( 1 / x).to_r}
- assert_raise(FloatDomainError) {(-1 / x).to_r}
- assert_raise(FloatDomainError) {( 0 / x).to_r}
-
- assert_equal(1, BigDecimal("1").to_r)
- assert_equal(Rational(3, 2), BigDecimal("1.5").to_r)
- assert_equal((2**100).to_r, BigDecimal((2**100).to_s).to_r)
- end
-
- def test_coerce
- a, b = BigDecimal("1").coerce(1.0)
- assert_instance_of(BigDecimal, a)
- assert_instance_of(BigDecimal, b)
- assert_equal(2, 1 + BigDecimal("1"), '[ruby-core:25697]')
-
- a, b = BigDecimal("1").coerce(1.quo(10))
- assert_equal(BigDecimal("0.1"), a, '[ruby-core:34318]')
-
- a, b = BigDecimal("0.11111").coerce(1.quo(3))
- assert_equal(BigDecimal("0." + "3"*a.precision), a)
-
- assert_nothing_raised(TypeError, '#7176') do
- BigDecimal('1') + Rational(1)
- end
- end
-
- def test_uplus
- x = BigDecimal("1")
- assert_equal(x, x.send(:+@))
- end
-
- def test_neg
- BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
-
- assert_equal(BigDecimal("-1"), BigDecimal("1").send(:-@))
- assert_equal(BigDecimal("-0"), BigDecimal("0").send(:-@))
- assert_equal(BigDecimal("0"), BigDecimal("-0").send(:-@))
- assert_equal(BigDecimal("-Infinity"), BigDecimal("Infinity").send(:-@))
- assert_equal(BigDecimal("Infinity"), BigDecimal("-Infinity").send(:-@))
- assert_equal(true, BigDecimal("NaN").send(:-@).nan?)
- end
-
- def test_add
- x = BigDecimal("1")
- assert_equal(BigDecimal("2"), x + x)
- assert_equal(1, BigDecimal("0") + 1)
- assert_equal(1, x + 0)
-
- assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, (BigDecimal("0") + 0).sign)
- assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, (BigDecimal("-0") + 0).sign)
- assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, (BigDecimal("-0") + BigDecimal("-0")).sign)
-
- x = BigDecimal((2**100).to_s)
- assert_equal(BigDecimal((2**100+1).to_s), x + 1)
-
- BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
- inf = BigDecimal("Infinity")
- neginf = BigDecimal("-Infinity")
-
- BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, true)
- assert_raise_with_message(FloatDomainError, "Computation results to 'Infinity'") { inf + inf }
- assert_raise_with_message(FloatDomainError, "Computation results to '-Infinity'") { neginf + neginf }
- end
-
- def test_sub
- x = BigDecimal("1")
- assert_equal(BigDecimal("0"), x - x)
- assert_equal(-1, BigDecimal("0") - 1)
- assert_equal(1, x - 0)
-
- assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, (BigDecimal("0") - 0).sign)
- assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, (BigDecimal("-0") - 0).sign)
- assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, (BigDecimal("-0") - BigDecimal("-0")).sign)
-
- x = BigDecimal((2**100).to_s)
- assert_equal(BigDecimal((2**100-1).to_s), x - 1)
-
- BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
- inf = BigDecimal("Infinity")
- neginf = BigDecimal("-Infinity")
-
- BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, true)
- assert_raise_with_message(FloatDomainError, "Computation results to 'Infinity'") { inf - neginf }
- assert_raise_with_message(FloatDomainError, "Computation results to '-Infinity'") { neginf - inf }
- end
-
- def test_sub_with_float
- assert_kind_of(BigDecimal, BigDecimal("3") - 1.0)
- end
-
- def test_sub_with_rational
- assert_kind_of(BigDecimal, BigDecimal("3") - 1.quo(3))
- end
-
- def test_mult
- x = BigDecimal((2**100).to_s)
- assert_equal(BigDecimal((2**100 * 3).to_s), (x * 3).to_i)
- assert_equal(x, (x * 1).to_i)
- assert_equal(x, (BigDecimal("1") * x).to_i)
- assert_equal(BigDecimal((2**200).to_s), (x * x).to_i)
-
- BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
- inf = BigDecimal("Infinity")
- neginf = BigDecimal("-Infinity")
-
- BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, true)
- assert_raise_with_message(FloatDomainError, "Computation results to 'Infinity'") { inf * inf }
- assert_raise_with_message(FloatDomainError, "Computation results to '-Infinity'") { neginf * inf }
- end
-
- def test_mult_with_float
- assert_kind_of(BigDecimal, BigDecimal("3") * 1.5)
- assert_equal(BigDecimal("64.4"), BigDecimal(1) * 64.4)
- end
-
- def test_mult_with_rational
- assert_kind_of(BigDecimal, BigDecimal("3") * 1.quo(3))
- end
-
- def test_mult_with_nil
- assert_raise(TypeError) {
- BigDecimal('1.1') * nil
- }
- end
-
- def test_div
- x = BigDecimal((2**100).to_s)
- assert_equal(BigDecimal((2**100 / 3).to_s), (x / 3).to_i)
- assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, (BigDecimal("0") / 1).sign)
- assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, (BigDecimal("-0") / 1).sign)
- assert_equal(2, BigDecimal("2") / 1)
- assert_equal(-2, BigDecimal("2") / -1)
-
- assert_equal(BigDecimal('1486.868686869'),
- (BigDecimal('1472.0') / BigDecimal('0.99')).round(9),
- '[ruby-core:59365] [#9316]')
-
- assert_in_delta(4.124045235,
- (BigDecimal('0.9932') / (700 * BigDecimal('0.344045') / BigDecimal('1000.0'))).round(9, half: :up),
- 10**Float::MIN_10_EXP, '[#9305]')
-
- BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
- assert_positive_zero(BigDecimal("1.0") / BigDecimal("Infinity"))
- assert_negative_zero(BigDecimal("-1.0") / BigDecimal("Infinity"))
- assert_negative_zero(BigDecimal("1.0") / BigDecimal("-Infinity"))
- assert_positive_zero(BigDecimal("-1.0") / BigDecimal("-Infinity"))
-
- BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, true)
- BigDecimal.mode(BigDecimal::EXCEPTION_ZERODIVIDE, false)
- assert_raise_with_message(FloatDomainError, "Computation results in 'Infinity'") { BigDecimal("1") / 0 }
- assert_raise_with_message(FloatDomainError, "Computation results in '-Infinity'") { BigDecimal("-1") / 0 }
- end
-
- def test_div_gh220
- x = BigDecimal("1.0")
- y = BigDecimal("3672577333.6608990499165058135986328125")
- c = BigDecimal("0.272288343892592687909520102748926752911779209181321744700032723729015151607289998e-9")
- assert_equal(c, x / y, "[GH-220]")
- end
-
- def test_div_precision
- bug13754 = '[ruby-core:82107] [Bug #13754]'
- a = BigDecimal('101')
- b = BigDecimal('0.9163472602589686')
- c = a/b
- assert(c.precision > b.precision,
- "(101/0.9163472602589686).precision >= (0.9163472602589686).precision #{bug13754}")
- end
-
- def test_div_with_float
- assert_kind_of(BigDecimal, BigDecimal("3") / 1.5)
- assert_equal(BigDecimal("0.5"), BigDecimal(1) / 2.0)
- end
-
- def test_div_with_rational
- assert_kind_of(BigDecimal, BigDecimal("3") / 1.quo(3))
- end
-
- def test_div_with_complex
- q = BigDecimal("3") / 1i
- assert_kind_of(Complex, q)
- end
-
- def test_div_error
- assert_raise(TypeError) { BigDecimal(20) / '2' }
- end
-
- def test_mod
- x = BigDecimal((2**100).to_s)
- assert_equal(1, x % 3)
- assert_equal(2, (-x) % 3)
- assert_equal(-2, x % -3)
- assert_equal(-1, (-x) % -3)
- end
-
- def test_mod_with_float
- assert_kind_of(BigDecimal, BigDecimal("3") % 1.5)
- end
-
- def test_mod_with_rational
- assert_kind_of(BigDecimal, BigDecimal("3") % 1.quo(3))
- end
-
- def test_remainder
- x = BigDecimal((2**100).to_s)
- assert_equal(1, x.remainder(3))
- assert_equal(-1, (-x).remainder(3))
- assert_equal(1, x.remainder(-3))
- assert_equal(-1, (-x).remainder(-3))
- end
-
- def test_remainder_with_float
- assert_kind_of(BigDecimal, BigDecimal("3").remainder(1.5))
- end
-
- def test_remainder_with_rational
- assert_kind_of(BigDecimal, BigDecimal("3").remainder(1.quo(3)))
- end
-
- def test_divmod
- x = BigDecimal((2**100).to_s)
- assert_equal([(x / 3).floor, 1], x.divmod(3))
- assert_equal([(-x / 3).floor, 2], (-x).divmod(3))
-
- assert_equal([0, 0], BigDecimal("0").divmod(2))
-
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- assert_raise(ZeroDivisionError){BigDecimal("0").divmod(0)}
- end
-
- def test_divmod_precision
- a = BigDecimal('2e55')
- b = BigDecimal('1.23456789e10')
- q, r = a.divmod(b)
- assert_equal((a/b).round(0, :down), q)
- assert_equal((a - q*b), r)
-
- b = BigDecimal('-1.23456789e10')
- q, r = a.divmod(b)
- assert_equal((a/b).round(0, :down) - 1, q)
- assert_equal((a - q*b), r)
- end
-
- def test_divmod_error
- assert_raise(TypeError) { BigDecimal(20).divmod('2') }
- end
-
- def test_add_bigdecimal
- x = BigDecimal((2**100).to_s)
- assert_equal(3000000000000000000000000000000, x.add(x, 1))
- assert_equal(2500000000000000000000000000000, x.add(x, 2))
- assert_equal(2540000000000000000000000000000, x.add(x, 3))
- end
-
- def test_sub_bigdecimal
- x = BigDecimal((2**100).to_s)
- assert_equal(1000000000000000000000000000000, x.sub(1, 1))
- assert_equal(1300000000000000000000000000000, x.sub(1, 2))
- assert_equal(1270000000000000000000000000000, x.sub(1, 3))
- end
-
- def test_mult_bigdecimal
- x = BigDecimal((2**100).to_s)
- assert_equal(4000000000000000000000000000000, x.mult(3, 1))
- assert_equal(3800000000000000000000000000000, x.mult(3, 2))
- assert_equal(3800000000000000000000000000000, x.mult(3, 3))
- end
-
- def test_div_bigdecimal
- x = BigDecimal((2**100).to_s)
- assert_equal(422550200076076467165567735125, x.div(3))
- assert_equal(400000000000000000000000000000, x.div(3, 1))
- assert_equal(420000000000000000000000000000, x.div(3, 2))
- assert_equal(423000000000000000000000000000, x.div(3, 3))
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
- assert_equal(0, BigDecimal("0").div(BigDecimal("Infinity")))
- end
- end
-
- def test_div_bigdecimal_with_float_and_precision
- x = BigDecimal(5)
- y = 5.1
- assert_equal(x.div(BigDecimal(y, 0), 8),
- x.div(y, 8))
-
- assert_equal(x.div(BigDecimal(y, 0), 100),
- x.div(y, 100))
- end
-
- def test_quo_without_prec
- x = BigDecimal(5)
- y = BigDecimal(229)
- assert_equal(BigDecimal("0.021834061135371179039301310043668122"), x.quo(y))
- end
-
- def test_quo_with_prec
- begin
- saved_mode = BigDecimal.mode(BigDecimal::ROUND_MODE)
- BigDecimal.mode(BigDecimal::ROUND_MODE, :half_up)
-
- x = BigDecimal(5)
- y = BigDecimal(229)
- assert_equal(BigDecimal("0.021834061135371179039301310043668122"), x.quo(y, 0))
- assert_equal(BigDecimal("0.022"), x.quo(y, 2))
- assert_equal(BigDecimal("0.0218"), x.quo(y, 3))
- assert_equal(BigDecimal("0.0218341"), x.quo(y, 6))
- assert_equal(BigDecimal("0.02183406114"), x.quo(y, 10))
- assert_equal(BigDecimal("0.021834061135371179039301310043668122270742358078603"), x.quo(y, 50))
- ensure
- BigDecimal.mode(BigDecimal::ROUND_MODE, saved_mode)
- end
- end
-
- def test_abs_bigdecimal
- x = BigDecimal((2**100).to_s)
- assert_equal(1267650600228229401496703205376, x.abs)
- x = BigDecimal("-" + (2**100).to_s)
- assert_equal(1267650600228229401496703205376, x.abs)
- x = BigDecimal("0")
- assert_equal(0, x.abs)
- x = BigDecimal("-0")
- assert_equal(0, x.abs)
-
- BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
- x = BigDecimal("Infinity")
- assert_equal(BigDecimal("Infinity"), x.abs)
- x = BigDecimal("-Infinity")
- assert_equal(BigDecimal("Infinity"), x.abs)
-
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- x = BigDecimal("NaN")
- assert_nan(x.abs)
- end
-
- def test_sqrt_bigdecimal
- x = BigDecimal("0.09")
- assert_in_delta(0.3, x.sqrt(1), 0.001)
- x = BigDecimal((2**100).to_s)
- y = BigDecimal("1125899906842624")
- e = y.exponent
- assert_equal(true, (x.sqrt(100) - y).abs < BigDecimal("1E#{e-100}"))
- assert_equal(true, (x.sqrt(200) - y).abs < BigDecimal("1E#{e-200}"))
- assert_equal(true, (x.sqrt(300) - y).abs < BigDecimal("1E#{e-300}"))
- x = BigDecimal("-" + (2**100).to_s)
- assert_raise_with_message(FloatDomainError, "sqrt of negative value") { x.sqrt(1) }
- x = BigDecimal((2**200).to_s)
- assert_equal(2**100, x.sqrt(1))
-
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- assert_raise_with_message(FloatDomainError, "sqrt of 'NaN'(Not a Number)") { BigDecimal("NaN").sqrt(1) }
- assert_raise_with_message(FloatDomainError, "sqrt of negative value") { BigDecimal("-Infinity").sqrt(1) }
-
- assert_equal(0, BigDecimal("0").sqrt(1))
- assert_equal(0, BigDecimal("-0").sqrt(1))
- assert_equal(1, BigDecimal("1").sqrt(1))
- assert_positive_infinite(BigDecimal("Infinity").sqrt(1))
- end
-
- def test_sqrt_5266
- x = BigDecimal('2' + '0'*100)
- assert_equal('0.14142135623730950488016887242096980785696718753769480731',
- x.sqrt(56).to_s(56).split(' ')[0])
- assert_equal('0.1414213562373095048801688724209698078569671875376948073',
- x.sqrt(55).to_s(55).split(' ')[0])
-
- x = BigDecimal('2' + '0'*200)
- assert_equal('0.14142135623730950488016887242096980785696718753769480731766797379907324784621070388503875343276415727350138462',
- x.sqrt(110).to_s(110).split(' ')[0])
- assert_equal('0.1414213562373095048801688724209698078569671875376948073176679737990732478462107038850387534327641572735013846',
- x.sqrt(109).to_s(109).split(' ')[0])
- end
-
- def test_fix
- x = BigDecimal("1.1")
- assert_equal(1, x.fix)
- assert_kind_of(BigDecimal, x.fix)
- end
-
- def test_frac
- x = BigDecimal("1.1")
- assert_equal(0.1, x.frac)
- assert_equal(0.1, BigDecimal("0.1").frac)
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- assert_nan(BigDecimal("NaN").frac)
- end
-
- def test_round
- assert_equal(3, BigDecimal("3.14159").round)
- assert_equal(9, BigDecimal("8.7").round)
- assert_equal(3.142, BigDecimal("3.14159").round(3))
- assert_equal(13300.0, BigDecimal("13345.234").round(-2))
-
- x = BigDecimal("111.111")
- assert_equal(111 , x.round)
- assert_equal(111.1 , x.round(1))
- assert_equal(111.11 , x.round(2))
- assert_equal(111.111, x.round(3))
- assert_equal(111.111, x.round(4))
- assert_equal(110 , x.round(-1))
- assert_equal(100 , x.round(-2))
- assert_equal( 0 , x.round(-3))
- assert_equal( 0 , x.round(-4))
-
- x = BigDecimal("2.5")
- assert_equal(3, x.round(0, BigDecimal::ROUND_UP))
- assert_equal(2, x.round(0, BigDecimal::ROUND_DOWN))
- assert_equal(3, x.round(0, BigDecimal::ROUND_HALF_UP))
- assert_equal(2, x.round(0, BigDecimal::ROUND_HALF_DOWN))
- assert_equal(2, x.round(0, BigDecimal::ROUND_HALF_EVEN))
- assert_equal(3, x.round(0, BigDecimal::ROUND_CEILING))
- assert_equal(2, x.round(0, BigDecimal::ROUND_FLOOR))
- assert_raise(ArgumentError) { x.round(0, 256) }
-
- x = BigDecimal("-2.5")
- assert_equal(-3, x.round(0, BigDecimal::ROUND_UP))
- assert_equal(-2, x.round(0, BigDecimal::ROUND_DOWN))
- assert_equal(-3, x.round(0, BigDecimal::ROUND_HALF_UP))
- assert_equal(-2, x.round(0, BigDecimal::ROUND_HALF_DOWN))
- assert_equal(-2, x.round(0, BigDecimal::ROUND_HALF_EVEN))
- assert_equal(-2, x.round(0, BigDecimal::ROUND_CEILING))
- assert_equal(-3, x.round(0, BigDecimal::ROUND_FLOOR))
-
- ROUNDING_MODE_MAP.each do |const, sym|
- assert_equal(x.round(0, const), x.round(0, sym))
- end
-
- bug3803 = '[ruby-core:32136]'
- 15.times do |n|
- x = BigDecimal("5#{'0'*n}1")
- assert_equal(10**(n+2), x.round(-(n+2), BigDecimal::ROUND_HALF_DOWN), bug3803)
- assert_equal(10**(n+2), x.round(-(n+2), BigDecimal::ROUND_HALF_EVEN), bug3803)
- x = BigDecimal("0.5#{'0'*n}1")
- assert_equal(1, x.round(0, BigDecimal::ROUND_HALF_DOWN), bug3803)
- assert_equal(1, x.round(0, BigDecimal::ROUND_HALF_EVEN), bug3803)
- x = BigDecimal("-0.5#{'0'*n}1")
- assert_equal(-1, x.round(0, BigDecimal::ROUND_HALF_DOWN), bug3803)
- assert_equal(-1, x.round(0, BigDecimal::ROUND_HALF_EVEN), bug3803)
- end
-
- assert_instance_of(Integer, x.round)
- assert_instance_of(Integer, x.round(0))
- assert_instance_of(Integer, x.round(-1))
- assert_instance_of(BigDecimal, x.round(1))
- end
-
- def test_round_half_even
- assert_equal(BigDecimal('12.0'), BigDecimal('12.5').round(half: :even))
- assert_equal(BigDecimal('14.0'), BigDecimal('13.5').round(half: :even))
-
- assert_equal(BigDecimal('2.2'), BigDecimal('2.15').round(1, half: :even))
- assert_equal(BigDecimal('2.2'), BigDecimal('2.25').round(1, half: :even))
- assert_equal(BigDecimal('2.4'), BigDecimal('2.35').round(1, half: :even))
-
- assert_equal(BigDecimal('-2.2'), BigDecimal('-2.15').round(1, half: :even))
- assert_equal(BigDecimal('-2.2'), BigDecimal('-2.25').round(1, half: :even))
- assert_equal(BigDecimal('-2.4'), BigDecimal('-2.35').round(1, half: :even))
-
- assert_equal(BigDecimal('7.1364'), BigDecimal('7.13645').round(4, half: :even))
- assert_equal(BigDecimal('7.1365'), BigDecimal('7.1364501').round(4, half: :even))
- assert_equal(BigDecimal('7.1364'), BigDecimal('7.1364499').round(4, half: :even))
-
- assert_equal(BigDecimal('-7.1364'), BigDecimal('-7.13645').round(4, half: :even))
- assert_equal(BigDecimal('-7.1365'), BigDecimal('-7.1364501').round(4, half: :even))
- assert_equal(BigDecimal('-7.1364'), BigDecimal('-7.1364499').round(4, half: :even))
- end
-
- def test_round_half_up
- assert_equal(BigDecimal('13.0'), BigDecimal('12.5').round(half: :up))
- assert_equal(BigDecimal('14.0'), BigDecimal('13.5').round(half: :up))
-
- assert_equal(BigDecimal('2.2'), BigDecimal('2.15').round(1, half: :up))
- assert_equal(BigDecimal('2.3'), BigDecimal('2.25').round(1, half: :up))
- assert_equal(BigDecimal('2.4'), BigDecimal('2.35').round(1, half: :up))
-
- assert_equal(BigDecimal('-2.2'), BigDecimal('-2.15').round(1, half: :up))
- assert_equal(BigDecimal('-2.3'), BigDecimal('-2.25').round(1, half: :up))
- assert_equal(BigDecimal('-2.4'), BigDecimal('-2.35').round(1, half: :up))
-
- assert_equal(BigDecimal('7.1365'), BigDecimal('7.13645').round(4, half: :up))
- assert_equal(BigDecimal('7.1365'), BigDecimal('7.1364501').round(4, half: :up))
- assert_equal(BigDecimal('7.1364'), BigDecimal('7.1364499').round(4, half: :up))
-
- assert_equal(BigDecimal('-7.1365'), BigDecimal('-7.13645').round(4, half: :up))
- assert_equal(BigDecimal('-7.1365'), BigDecimal('-7.1364501').round(4, half: :up))
- assert_equal(BigDecimal('-7.1364'), BigDecimal('-7.1364499').round(4, half: :up))
- end
-
- def test_round_half_down
- assert_equal(BigDecimal('12.0'), BigDecimal('12.5').round(half: :down))
- assert_equal(BigDecimal('13.0'), BigDecimal('13.5').round(half: :down))
-
- assert_equal(BigDecimal('2.1'), BigDecimal('2.15').round(1, half: :down))
- assert_equal(BigDecimal('2.2'), BigDecimal('2.25').round(1, half: :down))
- assert_equal(BigDecimal('2.3'), BigDecimal('2.35').round(1, half: :down))
-
- assert_equal(BigDecimal('-2.1'), BigDecimal('-2.15').round(1, half: :down))
- assert_equal(BigDecimal('-2.2'), BigDecimal('-2.25').round(1, half: :down))
- assert_equal(BigDecimal('-2.3'), BigDecimal('-2.35').round(1, half: :down))
-
- assert_equal(BigDecimal('7.1364'), BigDecimal('7.13645').round(4, half: :down))
- assert_equal(BigDecimal('7.1365'), BigDecimal('7.1364501').round(4, half: :down))
- assert_equal(BigDecimal('7.1364'), BigDecimal('7.1364499').round(4, half: :down))
-
- assert_equal(BigDecimal('-7.1364'), BigDecimal('-7.13645').round(4, half: :down))
- assert_equal(BigDecimal('-7.1365'), BigDecimal('-7.1364501').round(4, half: :down))
- assert_equal(BigDecimal('-7.1364'), BigDecimal('-7.1364499').round(4, half: :down))
- end
-
- def test_round_half_nil
- x = BigDecimal("2.5")
-
- BigDecimal.save_rounding_mode do
- BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_UP)
- assert_equal(3, x.round(0, half: nil))
- end
-
- BigDecimal.save_rounding_mode do
- BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_DOWN)
- assert_equal(2, x.round(0, half: nil))
- end
-
- BigDecimal.save_rounding_mode do
- BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_UP)
- assert_equal(3, x.round(0, half: nil))
- end
-
- BigDecimal.save_rounding_mode do
- BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_DOWN)
- assert_equal(2, x.round(0, half: nil))
- end
-
- BigDecimal.save_rounding_mode do
- BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_EVEN)
- assert_equal(2, x.round(0, half: nil))
- end
-
- BigDecimal.save_rounding_mode do
- BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_CEILING)
- assert_equal(3, x.round(0, half: nil))
- end
-
- BigDecimal.save_rounding_mode do
- BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_FLOOR)
- assert_equal(2, x.round(0, half: nil))
- end
- end
-
- def test_round_half_invalid_option
- assert_raise_with_message(ArgumentError, "invalid rounding mode (upp)") do
- BigDecimal('12.5').round(half: :upp)
- end
- assert_raise_with_message(ArgumentError, "invalid rounding mode (evenn)") do
- BigDecimal('2.15').round(1, half: :evenn)
- end
- assert_raise_with_message(ArgumentError, "invalid rounding mode (downn)") do
- BigDecimal('2.15').round(1, half: :downn)
- end
- assert_raise_with_message(ArgumentError, "invalid rounding mode (42)") do
- BigDecimal('2.15').round(1, half: 42)
- end
- end
-
- def test_truncate
- assert_equal(3, BigDecimal("3.14159").truncate)
- assert_equal(8, BigDecimal("8.7").truncate)
- assert_equal(3.141, BigDecimal("3.14159").truncate(3))
- assert_equal(13300.0, BigDecimal("13345.234").truncate(-2))
-
- assert_equal(-3, BigDecimal("-3.14159").truncate)
- assert_equal(-8, BigDecimal("-8.7").truncate)
- assert_equal(-3.141, BigDecimal("-3.14159").truncate(3))
- assert_equal(-13300.0, BigDecimal("-13345.234").truncate(-2))
- end
-
- def test_floor
- assert_equal(3, BigDecimal("3.14159").floor)
- assert_equal(-10, BigDecimal("-9.1").floor)
- assert_equal(3.141, BigDecimal("3.14159").floor(3))
- assert_equal(13300.0, BigDecimal("13345.234").floor(-2))
- end
-
- def test_ceil
- assert_equal(4, BigDecimal("3.14159").ceil)
- assert_equal(-9, BigDecimal("-9.1").ceil)
- assert_equal(3.142, BigDecimal("3.14159").ceil(3))
- assert_equal(13400.0, BigDecimal("13345.234").ceil(-2))
- end
-
- def test_to_s
- assert_equal('-123.45678 90123 45678 9', BigDecimal('-123.45678901234567890').to_s('5F'))
- assert_equal('+123.45678901 23456789', BigDecimal('123.45678901234567890').to_s('+8F'))
- assert_equal(' 123.4567890123456789', BigDecimal('123.45678901234567890').to_s(' F'))
- assert_equal('0.1234567890123456789e3', BigDecimal('123.45678901234567890').to_s)
- assert_equal('0.12345 67890 12345 6789e3', BigDecimal('123.45678901234567890').to_s(5))
- end
-
- def test_split
- x = BigDecimal('-123.45678901234567890')
- assert_equal([-1, "1234567890123456789", 10, 3], x.split)
- assert_equal([1, "0", 10, 0], BigDecimal("0").split)
- assert_equal([-1, "0", 10, 0], BigDecimal("-0").split)
-
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- assert_equal([0, "NaN", 10, 0], BigDecimal("NaN").split)
- assert_equal([1, "Infinity", 10, 0], BigDecimal("Infinity").split)
- assert_equal([-1, "Infinity", 10, 0], BigDecimal("-Infinity").split)
- end
-
- def test_exponent
- x = BigDecimal('-123.45678901234567890')
- assert_equal(3, x.exponent)
- end
-
- def test_inspect
- assert_equal("0.123456789012e0", BigDecimal("0.123456789012").inspect)
- assert_equal("0.123456789012e4", BigDecimal("1234.56789012").inspect)
- assert_equal("0.123456789012e-4", BigDecimal("0.0000123456789012").inspect)
- end
-
- def test_power
- assert_nothing_raised(TypeError, '[ruby-core:47632]') do
- 1000.times { BigDecimal('1001.10')**0.75 }
- end
- end
-
- def test_power_with_nil
- assert_raise(TypeError) do
- BigDecimal(3) ** nil
- end
- end
-
- def test_power_of_nan
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- assert_nan(BigDecimal::NAN ** 0)
- assert_nan(BigDecimal::NAN ** 1)
- assert_nan(BigDecimal::NAN ** 42)
- assert_nan(BigDecimal::NAN ** -42)
- assert_nan(BigDecimal::NAN ** 42.0)
- assert_nan(BigDecimal::NAN ** -42.0)
- assert_nan(BigDecimal::NAN ** BigDecimal(42))
- assert_nan(BigDecimal::NAN ** BigDecimal(-42))
- assert_nan(BigDecimal::NAN ** BigDecimal::INFINITY)
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
- assert_nan(BigDecimal::NAN ** (-BigDecimal::INFINITY))
- end
- end
- end
-
- def test_power_with_Bignum
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
- assert_equal(0, BigDecimal(0) ** (2**100))
-
- assert_positive_infinite(BigDecimal(0) ** -(2**100))
- assert_positive_infinite((-BigDecimal(0)) ** -(2**100))
- assert_negative_infinite((-BigDecimal(0)) ** -(2**100 + 1))
-
- assert_equal(1, BigDecimal(1) ** (2**100))
-
- assert_positive_infinite(BigDecimal(3) ** (2**100))
- assert_positive_zero(BigDecimal(3) ** (-2**100))
-
- assert_negative_infinite(BigDecimal(-3) ** (2**100))
- assert_positive_infinite(BigDecimal(-3) ** (2**100 + 1))
- assert_negative_zero(BigDecimal(-3) ** (-2**100))
- assert_positive_zero(BigDecimal(-3) ** (-2**100 - 1))
-
- assert_positive_zero(BigDecimal(0.5, Float::DIG) ** (2**100))
- assert_positive_infinite(BigDecimal(0.5, Float::DIG) ** (-2**100))
-
- assert_negative_zero(BigDecimal(-0.5, Float::DIG) ** (2**100))
- assert_positive_zero(BigDecimal(-0.5, Float::DIG) ** (2**100 - 1))
- assert_negative_infinite(BigDecimal(-0.5, Float::DIG) ** (-2**100))
- assert_positive_infinite(BigDecimal(-0.5, Float::DIG) ** (-2**100 - 1))
- end
- end
-
- def test_power_with_BigDecimal
- assert_nothing_raised do
- assert_in_delta(3 ** 3, BigDecimal(3) ** BigDecimal(3))
- end
- end
-
- def test_power_of_finite_with_zero
- x = BigDecimal(1)
- assert_equal(1, x ** 0)
- assert_equal(1, x ** 0.quo(1))
- assert_equal(1, x ** 0.0)
- assert_equal(1, x ** BigDecimal(0))
-
- x = BigDecimal(42)
- assert_equal(1, x ** 0)
- assert_equal(1, x ** 0.quo(1))
- assert_equal(1, x ** 0.0)
- assert_equal(1, x ** BigDecimal(0))
-
- x = BigDecimal(-42)
- assert_equal(1, x ** 0)
- assert_equal(1, x ** 0.quo(1))
- assert_equal(1, x ** 0.0)
- assert_equal(1, x ** BigDecimal(0))
- end
-
- def test_power_of_three
- x = BigDecimal(3)
- assert_equal(81, x ** 4)
- assert_equal(1.quo(81), x ** -4)
- assert_in_delta(1.0/81, x ** -4)
- end
-
- def test_power_of_zero
- zero = BigDecimal(0)
- assert_equal(0, zero ** 4)
- assert_equal(0, zero ** 4.quo(1))
- assert_equal(0, zero ** 4.0)
- assert_equal(0, zero ** BigDecimal(4))
- assert_equal(1, zero ** 0)
- assert_equal(1, zero ** 0.quo(1))
- assert_equal(1, zero ** 0.0)
- assert_equal(1, zero ** BigDecimal(0))
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- assert_positive_infinite(zero ** -1)
- assert_positive_infinite(zero ** -1.quo(1))
- assert_positive_infinite(zero ** -1.0)
- assert_positive_infinite(zero ** BigDecimal(-1))
-
- m_zero = BigDecimal("-0")
- assert_negative_infinite(m_zero ** -1)
- assert_negative_infinite(m_zero ** -1.quo(1))
- assert_negative_infinite(m_zero ** -1.0)
- assert_negative_infinite(m_zero ** BigDecimal(-1))
- assert_positive_infinite(m_zero ** -2)
- assert_positive_infinite(m_zero ** -2.quo(1))
- assert_positive_infinite(m_zero ** -2.0)
- assert_positive_infinite(m_zero ** BigDecimal(-2))
- end
- end
-
- def test_power_of_positive_infinity
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- assert_positive_infinite(BigDecimal::INFINITY ** 3)
- assert_positive_infinite(BigDecimal::INFINITY ** 3.quo(1))
- assert_positive_infinite(BigDecimal::INFINITY ** 3.0)
- assert_positive_infinite(BigDecimal::INFINITY ** BigDecimal(3))
- assert_positive_infinite(BigDecimal::INFINITY ** 2)
- assert_positive_infinite(BigDecimal::INFINITY ** 2.quo(1))
- assert_positive_infinite(BigDecimal::INFINITY ** 2.0)
- assert_positive_infinite(BigDecimal::INFINITY ** BigDecimal(2))
- assert_positive_infinite(BigDecimal::INFINITY ** 1)
- assert_positive_infinite(BigDecimal::INFINITY ** 1.quo(1))
- assert_positive_infinite(BigDecimal::INFINITY ** 1.0)
- assert_positive_infinite(BigDecimal::INFINITY ** BigDecimal(1))
- assert_equal(1, BigDecimal::INFINITY ** 0)
- assert_equal(1, BigDecimal::INFINITY ** 0.quo(1))
- assert_equal(1, BigDecimal::INFINITY ** 0.0)
- assert_equal(1, BigDecimal::INFINITY ** BigDecimal(0))
- assert_positive_zero(BigDecimal::INFINITY ** -1)
- assert_positive_zero(BigDecimal::INFINITY ** -1.quo(1))
- assert_positive_zero(BigDecimal::INFINITY ** -1.0)
- assert_positive_zero(BigDecimal::INFINITY ** BigDecimal(-1))
- assert_positive_zero(BigDecimal::INFINITY ** -2)
- assert_positive_zero(BigDecimal::INFINITY ** -2.0)
- assert_positive_zero(BigDecimal::INFINITY ** BigDecimal(-2))
- end
- end
-
- def test_power_of_negative_infinity
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- assert_negative_infinite((-BigDecimal::INFINITY) ** 3)
- assert_negative_infinite((-BigDecimal::INFINITY) ** 3.quo(1))
- assert_negative_infinite((-BigDecimal::INFINITY) ** 3.0)
- assert_negative_infinite((-BigDecimal::INFINITY) ** BigDecimal(3))
- assert_positive_infinite((-BigDecimal::INFINITY) ** 2)
- assert_positive_infinite((-BigDecimal::INFINITY) ** 2.quo(1))
- assert_positive_infinite((-BigDecimal::INFINITY) ** 2.0)
- assert_positive_infinite((-BigDecimal::INFINITY) ** BigDecimal(2))
- assert_negative_infinite((-BigDecimal::INFINITY) ** 1)
- assert_negative_infinite((-BigDecimal::INFINITY) ** 1.quo(1))
- assert_negative_infinite((-BigDecimal::INFINITY) ** 1.0)
- assert_negative_infinite((-BigDecimal::INFINITY) ** BigDecimal(1))
- assert_equal(1, (-BigDecimal::INFINITY) ** 0)
- assert_equal(1, (-BigDecimal::INFINITY) ** 0.quo(1))
- assert_equal(1, (-BigDecimal::INFINITY) ** 0.0)
- assert_equal(1, (-BigDecimal::INFINITY) ** BigDecimal(0))
- assert_negative_zero((-BigDecimal::INFINITY) ** -1)
- assert_negative_zero((-BigDecimal::INFINITY) ** -1.quo(1))
- assert_negative_zero((-BigDecimal::INFINITY) ** -1.0)
- assert_negative_zero((-BigDecimal::INFINITY) ** BigDecimal(-1))
- assert_positive_zero((-BigDecimal::INFINITY) ** -2)
- assert_positive_zero((-BigDecimal::INFINITY) ** -2.quo(1))
- assert_positive_zero((-BigDecimal::INFINITY) ** -2.0)
- assert_positive_zero((-BigDecimal::INFINITY) ** BigDecimal(-2))
- end
- end
-
- def test_power_without_prec
- pi = BigDecimal("3.14159265358979323846264338327950288419716939937511")
- e = BigDecimal("2.71828182845904523536028747135266249775724709369996")
- pow = BigDecimal("0.2245915771836104547342715220454373502758931513399678438732330680117143493477164265678321738086407229773690574073268002736527e2")
- assert_equal(pow, pi.power(e))
-
- n = BigDecimal("2222")
- assert_equal(BigDecimal("0.5171353084572525892492416e12"), (n ** 3.5))
- assert_equal(BigDecimal("0.517135308457252592e12"), (n ** 3.5r))
- assert_equal(BigDecimal("0.517135308457252589249241582e12"), (n ** BigDecimal("3.5",15)))
- end
-
- def test_power_with_prec
- pi = BigDecimal("3.14159265358979323846264338327950288419716939937511")
- e = BigDecimal("2.71828182845904523536028747135266249775724709369996")
- pow = BigDecimal("22.459157718361045473")
- assert_equal(pow, pi.power(e, 20))
-
- b = BigDecimal('1.034482758620689655172413793103448275862068965517241379310344827586206896551724')
- assert_equal(BigDecimal('0.114523E1'), b.power(4, 5), '[Bug #8818] [ruby-core:56802]')
- end
-
- def test_limit
- BigDecimal.save_limit do
- BigDecimal.limit(1)
- x = BigDecimal("3")
- assert_equal(90, x ** 4) # OK? must it be 80?
- # 3 * 3 * 3 * 3 = 10 * 3 * 3 = 30 * 3 = 90 ???
- assert_raise(ArgumentError) { BigDecimal.limit(-1) }
-
- bug7458 = '[ruby-core:50269] [#7458]'
- one = BigDecimal('1')
- epsilon = BigDecimal('0.7E-18')
-
- BigDecimal.limit(0)
- assert_equal(BigDecimal("1.0000000000000000007"), one + epsilon, "limit(0) #{bug7458}")
-
- 1.upto(18) do |lim|
- BigDecimal.limit(lim)
- assert_equal(BigDecimal("1.0"), one + epsilon, "limit(#{lim}) #{bug7458}")
- end
-
- BigDecimal.limit(19)
- assert_equal(BigDecimal("1.000000000000000001"), one + epsilon, "limit(19) #{bug7458}")
-
- BigDecimal.limit(20)
- assert_equal(BigDecimal("1.0000000000000000007"), one + epsilon, "limit(20) #{bug7458}")
- end
- end
-
- def test_sign
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- BigDecimal.mode(BigDecimal::EXCEPTION_ZERODIVIDE, false)
-
- assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, BigDecimal("0").sign)
- assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, BigDecimal("-0").sign)
- assert_equal(BigDecimal::SIGN_POSITIVE_FINITE, BigDecimal("1").sign)
- assert_equal(BigDecimal::SIGN_NEGATIVE_FINITE, BigDecimal("-1").sign)
- assert_equal(BigDecimal::SIGN_POSITIVE_INFINITE, (BigDecimal("1") / 0).sign)
- assert_equal(BigDecimal::SIGN_NEGATIVE_INFINITE, (BigDecimal("-1") / 0).sign)
- assert_equal(BigDecimal::SIGN_NaN, (BigDecimal("0") / 0).sign)
- end
-
- def test_inf
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- inf = BigDecimal("Infinity")
-
- assert_equal(inf, inf + inf)
- assert_nan((inf + (-inf)))
- assert_nan((inf - inf))
- assert_equal(inf, inf - (-inf))
- assert_equal(inf, inf * inf)
- assert_nan((inf / inf))
-
- assert_equal(inf, inf + 1)
- assert_equal(inf, inf - 1)
- assert_equal(inf, inf * 1)
- assert_nan((inf * 0))
- assert_equal(inf, inf / 1)
-
- assert_equal(inf, 1 + inf)
- assert_equal(-inf, 1 - inf)
- assert_equal(inf, 1 * inf)
- assert_equal(-inf, -1 * inf)
- assert_nan((0 * inf))
- assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, (1 / inf).sign)
- assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, (-1 / inf).sign)
- end
-
- def assert_equal_us_ascii_string(a, b)
- assert_equal(a, b)
- assert_equal(Encoding::US_ASCII, b.encoding)
- end
-
- def test_to_special_string
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- nan = BigDecimal("NaN")
- assert_equal_us_ascii_string("NaN", nan.to_s)
- inf = BigDecimal("Infinity")
- assert_equal_us_ascii_string("Infinity", inf.to_s)
- assert_equal_us_ascii_string(" Infinity", inf.to_s(" "))
- assert_equal_us_ascii_string("+Infinity", inf.to_s("+"))
- assert_equal_us_ascii_string("-Infinity", (-inf).to_s)
- pzero = BigDecimal("0")
- assert_equal_us_ascii_string("0.0", pzero.to_s)
- assert_equal_us_ascii_string(" 0.0", pzero.to_s(" "))
- assert_equal_us_ascii_string("+0.0", pzero.to_s("+"))
- assert_equal_us_ascii_string("-0.0", (-pzero).to_s)
- end
-
- def test_to_string
- assert_equal_us_ascii_string("0.01", BigDecimal("0.01").to_s("F"))
- s = "0." + "0" * 100 + "1"
- assert_equal_us_ascii_string(s, BigDecimal(s).to_s("F"))
- s = "1" + "0" * 100 + ".0"
- assert_equal_us_ascii_string(s, BigDecimal(s).to_s("F"))
- end
-
- def test_ctov
- assert_equal(0.1, BigDecimal("1E-1"))
- assert_equal(10, BigDecimal("1E+1"))
- assert_equal(1, BigDecimal("+1"))
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
-
- assert_equal(BigDecimal::SIGN_POSITIVE_INFINITE, BigDecimal("1E1" + "0" * 10000).sign)
- assert_equal(BigDecimal::SIGN_NEGATIVE_INFINITE, BigDecimal("-1E1" + "0" * 10000).sign)
- assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, BigDecimal("1E-1" + "0" * 10000).sign)
- assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, BigDecimal("-1E-1" + "0" * 10000).sign)
- end
-
- def test_split_under_gc_stress
- bug3258 = '[ruby-dev:41213]'
- expect = 10.upto(20).map{|i|[1, "1", 10, i+1].inspect}
- assert_in_out_err(%w[-rbigdecimal --disable-gems], <<-EOS, expect, [], bug3258)
- GC.stress = true
- 10.upto(20) do |i|
- p BigDecimal("1"+"0"*i).split
- end
- EOS
- end
-
- def test_coerce_under_gc_stress
- assert_in_out_err(%w[-rbigdecimal --disable-gems], <<-EOS, [], [])
- expect = ":too_long_to_embed_as_string can't be coerced into BigDecimal"
- b = BigDecimal("1")
- GC.stress = true
- 10.times do
- begin
- b.coerce(:too_long_to_embed_as_string)
- rescue => e
- raise unless e.is_a?(TypeError)
- raise "'\#{expect}' is expected, but '\#{e.message}'" unless e.message == expect
- end
- end
- EOS
- end
-
- def test_INFINITY
- assert_positive_infinite(BigDecimal::INFINITY)
- end
-
- def test_NAN
- assert_nan(BigDecimal::NAN)
- end
-
- def test_exp_with_zero_precision
- assert_raise(ArgumentError) do
- BigMath.exp(1, 0)
- end
- end
-
- def test_exp_with_negative_precision
- assert_raise(ArgumentError) do
- BigMath.exp(1, -42)
- end
- end
-
- def test_exp_with_complex
- assert_raise(ArgumentError) do
- BigMath.exp(Complex(1, 2), 20)
- end
- end
-
- def test_exp_with_negative
- x = BigDecimal(-1)
- y = BigMath.exp(x, 20)
- assert_equal(y, BigMath.exp(-1, 20))
- assert_equal(BigDecimal(-1), x)
- end
-
- def test_exp_with_negative_infinite
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
- assert_equal(0, BigMath.exp(-BigDecimal::INFINITY, 20))
- end
- end
-
- def test_exp_with_positive_infinite
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
- assert(BigMath.exp(BigDecimal::INFINITY, 20) > 0)
- assert_positive_infinite(BigMath.exp(BigDecimal::INFINITY, 20))
- end
- end
-
- def test_exp_with_nan
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- assert_nan(BigMath.exp(BigDecimal::NAN, 20))
- end
- end
-
- def test_exp_with_1
- assert_in_epsilon(Math::E, BigMath.exp(1, 20))
- end
-
- def test_BigMath_exp
- prec = 20
- assert_in_epsilon(Math.exp(20), BigMath.exp(BigDecimal("20"), prec))
- assert_in_epsilon(Math.exp(40), BigMath.exp(BigDecimal("40"), prec))
- assert_in_epsilon(Math.exp(-20), BigMath.exp(BigDecimal("-20"), prec))
- assert_in_epsilon(Math.exp(-40), BigMath.exp(BigDecimal("-40"), prec))
- end
-
- def test_BigMath_exp_with_float
- prec = 20
- assert_in_epsilon(Math.exp(20), BigMath.exp(20.0, prec))
- assert_in_epsilon(Math.exp(40), BigMath.exp(40.0, prec))
- assert_in_epsilon(Math.exp(-20), BigMath.exp(-20.0, prec))
- assert_in_epsilon(Math.exp(-40), BigMath.exp(-40.0, prec))
- end
-
- def test_BigMath_exp_with_fixnum
- prec = 20
- assert_in_epsilon(Math.exp(20), BigMath.exp(20, prec))
- assert_in_epsilon(Math.exp(40), BigMath.exp(40, prec))
- assert_in_epsilon(Math.exp(-20), BigMath.exp(-20, prec))
- assert_in_epsilon(Math.exp(-40), BigMath.exp(-40, prec))
- end
-
- def test_BigMath_exp_with_rational
- prec = 20
- assert_in_epsilon(Math.exp(20), BigMath.exp(Rational(40,2), prec))
- assert_in_epsilon(Math.exp(40), BigMath.exp(Rational(80,2), prec))
- assert_in_epsilon(Math.exp(-20), BigMath.exp(Rational(-40,2), prec))
- assert_in_epsilon(Math.exp(-40), BigMath.exp(Rational(-80,2), prec))
- end
-
- def test_BigMath_exp_under_gc_stress
- assert_in_out_err(%w[-rbigdecimal --disable-gems], <<-EOS, [], [])
- expect = ":too_long_to_embed_as_string can't be coerced into BigDecimal"
- 10.times do
- begin
- BigMath.exp(:too_long_to_embed_as_string, 6)
- rescue => e
- raise unless e.is_a?(ArgumentError)
- raise "'\#{expect}' is expected, but '\#{e.message}'" unless e.message == expect
- end
- end
- EOS
- end
-
- def test_BigMath_log_with_string
- assert_raise(ArgumentError) do
- BigMath.log("foo", 20)
- end
- end
-
- def test_BigMath_log_with_nil
- assert_raise(ArgumentError) do
- BigMath.log(nil, 20)
- end
- end
-
- def test_BigMath_log_with_non_integer_precision
- assert_raise(ArgumentError) do
- BigMath.log(1, 0.5)
- end
- end
-
- def test_BigMath_log_with_nil_precision
- assert_raise(ArgumentError) do
- BigMath.log(1, nil)
- end
- end
-
- def test_BigMath_log_with_complex
- assert_raise(Math::DomainError) do
- BigMath.log(Complex(1, 2), 20)
- end
- end
-
- def test_BigMath_log_with_zero_arg
- assert_raise(Math::DomainError) do
- BigMath.log(0, 20)
- end
- end
-
- def test_BigMath_log_with_negative_arg
- assert_raise(Math::DomainError) do
- BigMath.log(-1, 20)
- end
- end
-
- def test_BigMath_log_with_zero_precision
- assert_raise(ArgumentError) do
- BigMath.log(1, 0)
- end
- end
-
- def test_BigMath_log_with_negative_precision
- assert_raise(ArgumentError) do
- BigMath.log(1, -42)
- end
- end
-
- def test_BigMath_log_with_negative_infinite
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
- assert_raise(Math::DomainError) do
- BigMath.log(-BigDecimal::INFINITY, 20)
- end
- end
- end
-
- def test_BigMath_log_with_positive_infinite
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
- assert(BigMath.log(BigDecimal::INFINITY, 20) > 0)
- assert_positive_infinite(BigMath.log(BigDecimal::INFINITY, 20))
- end
- end
-
- def test_BigMath_log_with_nan
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- assert_nan(BigMath.log(BigDecimal::NAN, 20))
- end
- end
-
- def test_BigMath_log_with_float_nan
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- assert_nan(BigMath.log(Float::NAN, 20))
- end
- end
-
- def test_BigMath_log_with_1
- assert_in_delta(0.0, BigMath.log(1, 20))
- assert_in_delta(0.0, BigMath.log(1.0, 20))
- assert_in_delta(0.0, BigMath.log(BigDecimal(1), 20))
- end
-
- def test_BigMath_log_with_exp_1
- assert_in_delta(1.0, BigMath.log(BigMath.E(10), 10))
- end
-
- def test_BigMath_log_with_2
- assert_in_delta(Math.log(2), BigMath.log(2, 20))
- assert_in_delta(Math.log(2), BigMath.log(2.0, 20))
- assert_in_delta(Math.log(2), BigMath.log(BigDecimal(2), 20))
- end
-
- def test_BigMath_log_with_square_of_E
- assert_in_delta(2, BigMath.log(BigMath.E(20)**2, 20))
- end
-
- def test_BigMath_log_with_high_precision_case
- e = BigDecimal('2.71828182845904523536028747135266249775724709369996')
- e_3 = e.mult(e, 50).mult(e, 50)
- log_3 = BigMath.log(e_3, 50)
- assert_in_delta(3, log_3, 0.0000000000_0000000000_0000000000_0000000000_0000000001)
- end
-
- def test_BigMath_log_with_42
- assert_in_delta(Math.log(42), BigMath.log(42, 20))
- assert_in_delta(Math.log(42), BigMath.log(42.0, 20))
- assert_in_delta(Math.log(42), BigMath.log(BigDecimal(42), 20))
- end
-
- def test_BigMath_log_with_101
- # this is mainly a performance test (should be very fast, not the 0.3 s)
- assert_in_delta(Math.log(101), BigMath.log(101, 20), 1E-15)
- end
-
- def test_BigMath_log_with_reciprocal_of_42
- assert_in_delta(Math.log(1e-42), BigMath.log(1e-42, 20))
- assert_in_delta(Math.log(1e-42), BigMath.log(BigDecimal("1e-42"), 20))
- end
-
- def test_BigMath_log_under_gc_stress
- assert_in_out_err(%w[-rbigdecimal --disable-gems], <<-EOS, [], [])
- expect = ":too_long_to_embed_as_string can't be coerced into BigDecimal"
- 10.times do
- begin
- BigMath.log(:too_long_to_embed_as_string, 6)
- rescue => e
- raise unless e.is_a?(ArgumentError)
- raise "'\#{expect}' is expected, but '\#{e.message}'" unless e.message == expect
- end
- end
- EOS
- end
-
- def test_frozen_p
- x = BigDecimal(1)
- assert(x.frozen?)
- assert((x + x).frozen?)
- end
-
- def test_clone
- assert_warning(/^$/) do
- x = BigDecimal(0)
- assert_same(x, x.clone)
- end
- end
-
- def test_dup
- assert_warning(/^$/) do
- [1, -1, 2**100, -2**100].each do |i|
- x = BigDecimal(i)
- assert_same(x, x.dup)
- end
- end
- end
-
- def test_new_subclass
- c = Class.new(BigDecimal)
- assert_raise_with_message(NoMethodError, /undefined method `new'/) { c.new(1) }
- end
-
- def test_to_d
- bug6093 = '[ruby-core:42969]'
- code = "exit(BigDecimal('10.0') == 10.0.to_d)"
- assert_ruby_status(%w[-rbigdecimal -rbigdecimal/util -rmathn -], code, bug6093)
- end if RUBY_VERSION < '2.5' # mathn was removed from Ruby 2.5
-
- def test_bug6406
- assert_in_out_err(%w[-rbigdecimal --disable-gems], <<-EOS, [], [])
- Thread.current.keys.to_s
- EOS
- end
-
- def test_precision_only_integer
- assert_equal(0, BigDecimal(0).precision)
- assert_equal(1, BigDecimal(1).precision)
- assert_equal(1, BigDecimal(-1).precision)
- assert_equal(2, BigDecimal(10).precision)
- assert_equal(2, BigDecimal(-10).precision)
- assert_equal(9, BigDecimal(100_000_000).precision)
- assert_equal(9, BigDecimal(-100_000_000).precision)
- assert_equal(12, BigDecimal(100_000_000_000).precision)
- assert_equal(12, BigDecimal(-100_000_000_000).precision)
- assert_equal(21, BigDecimal(100_000_000_000_000_000_000).precision)
- assert_equal(21, BigDecimal(-100_000_000_000_000_000_000).precision)
- assert_equal(103, BigDecimal("111e100").precision)
- assert_equal(103, BigDecimal("-111e100").precision)
- end
-
- def test_precision_only_fraction
- assert_equal(1, BigDecimal("0.1").precision)
- assert_equal(1, BigDecimal("-0.1").precision)
- assert_equal(2, BigDecimal("0.01").precision)
- assert_equal(2, BigDecimal("-0.01").precision)
- assert_equal(2, BigDecimal("0.11").precision)
- assert_equal(2, BigDecimal("-0.11").precision)
- assert_equal(9, BigDecimal("0.000_000_001").precision)
- assert_equal(9, BigDecimal("-0.000_000_001").precision)
- assert_equal(10, BigDecimal("0.000_000_000_1").precision)
- assert_equal(10, BigDecimal("-0.000_000_000_1").precision)
- assert_equal(21, BigDecimal("0.000_000_000_000_000_000_001").precision)
- assert_equal(21, BigDecimal("-0.000_000_000_000_000_000_001").precision)
- assert_equal(100, BigDecimal("111e-100").precision)
- assert_equal(100, BigDecimal("-111e-100").precision)
- end
-
- def test_precision_full
- assert_equal(5, BigDecimal("11111e-2").precision)
- assert_equal(5, BigDecimal("-11111e-2").precision)
- assert_equal(5, BigDecimal("11111e-2").precision)
- assert_equal(5, BigDecimal("-11111e-2").precision)
- assert_equal(21, BigDecimal("100.000_000_000_000_000_001").precision)
- assert_equal(21, BigDecimal("-100.000_000_000_000_000_001").precision)
- end
-
- def test_precision_special
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
-
- assert_equal(0, BigDecimal("Infinity").precision)
- assert_equal(0, BigDecimal("-Infinity").precision)
- assert_equal(0, BigDecimal("NaN").precision)
- end
- end
-
- def test_scale_only_integer
- assert_equal(0, BigDecimal(0).scale)
- assert_equal(0, BigDecimal(1).scale)
- assert_equal(0, BigDecimal(-1).scale)
- assert_equal(0, BigDecimal(10).scale)
- assert_equal(0, BigDecimal(-10).scale)
- assert_equal(0, BigDecimal(100_000_000).scale)
- assert_equal(0, BigDecimal(-100_000_000).scale)
- assert_equal(0, BigDecimal(100_000_000_000).scale)
- assert_equal(0, BigDecimal(-100_000_000_000).scale)
- assert_equal(0, BigDecimal(100_000_000_000_000_000_000).scale)
- assert_equal(0, BigDecimal(-100_000_000_000_000_000_000).scale)
- assert_equal(0, BigDecimal("111e100").scale)
- assert_equal(0, BigDecimal("-111e100").scale)
- end
-
- def test_scale_only_fraction
- assert_equal(1, BigDecimal("0.1").scale)
- assert_equal(1, BigDecimal("-0.1").scale)
- assert_equal(2, BigDecimal("0.01").scale)
- assert_equal(2, BigDecimal("-0.01").scale)
- assert_equal(2, BigDecimal("0.11").scale)
- assert_equal(2, BigDecimal("-0.11").scale)
- assert_equal(21, BigDecimal("0.000_000_000_000_000_000_001").scale)
- assert_equal(21, BigDecimal("-0.000_000_000_000_000_000_001").scale)
- assert_equal(100, BigDecimal("111e-100").scale)
- assert_equal(100, BigDecimal("-111e-100").scale)
- end
-
- def test_scale_full
- assert_equal(1, BigDecimal("0.1").scale)
- assert_equal(1, BigDecimal("-0.1").scale)
- assert_equal(2, BigDecimal("0.01").scale)
- assert_equal(2, BigDecimal("-0.01").scale)
- assert_equal(2, BigDecimal("0.11").scale)
- assert_equal(2, BigDecimal("-0.11").scale)
- assert_equal(2, BigDecimal("11111e-2").scale)
- assert_equal(2, BigDecimal("-11111e-2").scale)
- assert_equal(18, BigDecimal("100.000_000_000_000_000_001").scale)
- assert_equal(18, BigDecimal("-100.000_000_000_000_000_001").scale)
- end
-
- def test_scale_special
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
-
- assert_equal(0, BigDecimal("Infinity").scale)
- assert_equal(0, BigDecimal("-Infinity").scale)
- assert_equal(0, BigDecimal("NaN").scale)
- end
- end
-
- def test_precision_scale
- assert_equal([2, 0], BigDecimal("11.0").precision_scale)
- assert_equal([2, 1], BigDecimal("1.1").precision_scale)
- assert_equal([2, 2], BigDecimal("0.11").precision_scale)
-
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- assert_equal([0, 0], BigDecimal("Infinity").precision_scale)
- end
- end
-
- def test_n_significant_digits_only_integer
- assert_equal(0, BigDecimal(0).n_significant_digits)
- assert_equal(1, BigDecimal(1).n_significant_digits)
- assert_equal(1, BigDecimal(-1).n_significant_digits)
- assert_equal(1, BigDecimal(10).n_significant_digits)
- assert_equal(1, BigDecimal(-10).n_significant_digits)
- assert_equal(3, BigDecimal(101).n_significant_digits)
- assert_equal(3, BigDecimal(-101).n_significant_digits)
- assert_equal(1, BigDecimal(100_000_000_000_000_000_000).n_significant_digits)
- assert_equal(1, BigDecimal(-100_000_000_000_000_000_000).n_significant_digits)
- assert_equal(21, BigDecimal(100_000_000_000_000_000_001).n_significant_digits)
- assert_equal(21, BigDecimal(-100_000_000_000_000_000_001).n_significant_digits)
- assert_equal(3, BigDecimal("111e100").n_significant_digits)
- assert_equal(3, BigDecimal("-111e100").n_significant_digits)
- end
-
- def test_n_significant_digits_only_fraction
- assert_equal(1, BigDecimal("0.1").n_significant_digits)
- assert_equal(1, BigDecimal("-0.1").n_significant_digits)
- assert_equal(1, BigDecimal("0.01").n_significant_digits)
- assert_equal(1, BigDecimal("-0.01").n_significant_digits)
- assert_equal(2, BigDecimal("0.11").n_significant_digits)
- assert_equal(2, BigDecimal("-0.11").n_significant_digits)
- assert_equal(1, BigDecimal("0.000_000_000_000_000_000_001").n_significant_digits)
- assert_equal(1, BigDecimal("-0.000_000_000_000_000_000_001").n_significant_digits)
- assert_equal(3, BigDecimal("111e-100").n_significant_digits)
- assert_equal(3, BigDecimal("-111e-100").n_significant_digits)
- end
-
- def test_n_significant_digits_full
- assert_equal(2, BigDecimal("1.1").n_significant_digits)
- assert_equal(2, BigDecimal("-1.1").n_significant_digits)
- assert_equal(3, BigDecimal("1.01").n_significant_digits)
- assert_equal(3, BigDecimal("-1.01").n_significant_digits)
- assert_equal(5, BigDecimal("11111e-2").n_significant_digits)
- assert_equal(5, BigDecimal("-11111e-2").n_significant_digits)
- assert_equal(21, BigDecimal("100.000_000_000_000_000_001").n_significant_digits)
- assert_equal(21, BigDecimal("-100.000_000_000_000_000_001").n_significant_digits)
- end
-
- def test_n_significant_digits_special
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
-
- assert_equal(0, BigDecimal("Infinity").n_significant_digits)
- assert_equal(0, BigDecimal("-Infinity").n_significant_digits)
- assert_equal(0, BigDecimal("NaN").n_significant_digits)
- end
- end
-
- def test_initialize_copy_dup_clone_frozen_error
- bd = BigDecimal(1)
- bd2 = BigDecimal(2)
- err = RUBY_VERSION >= '2.5' ? FrozenError : TypeError
- assert_raise(err) { bd.send(:initialize_copy, bd2) }
- assert_raise(err) { bd.send(:initialize_clone, bd2) }
- assert_raise(err) { bd.send(:initialize_dup, bd2) }
- end
-
- def test_llong_min_gh_200
- # https://github.com/ruby/bigdecimal/issues/199
- # Between LLONG_MIN and -ULLONG_MAX
- assert_equal(BigDecimal(LIMITS["LLONG_MIN"].to_s), BigDecimal(LIMITS["LLONG_MIN"]), "[GH-200]")
-
- minus_ullong_max = -LIMITS["ULLONG_MAX"]
- assert_equal(BigDecimal(minus_ullong_max.to_s), BigDecimal(minus_ullong_max), "[GH-200]")
- end
-
- def test_reminder_infinity_gh_187
- # https://github.com/ruby/bigdecimal/issues/187
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- bd = BigDecimal("4.2")
- assert_equal(bd.remainder(BigDecimal("+Infinity")), bd)
- assert_equal(bd.remainder(BigDecimal("-Infinity")), bd)
- end
- end
-
- def assert_no_memory_leak(code, *rest, **opt)
- code = "8.times {20_000.times {begin #{code}; rescue NoMemoryError; end}; GC.start}"
- super(["-rbigdecimal"],
- "b = BigDecimal('10'); b.nil?; " \
- "GC.add_stress_to_class(BigDecimal); "\
- "#{code}", code, *rest, rss: true, limit: 1.1, **opt)
- end
-
- if EnvUtil.gc_stress_to_class?
- def test_no_memory_leak_allocate
- assert_no_memory_leak("BigDecimal.allocate")
- end
-
- def test_no_memory_leak_initialize
- assert_no_memory_leak("BigDecimal()")
- end
-
- def test_no_memory_leak_BigDecimal
- assert_no_memory_leak("BigDecimal('10')")
- assert_no_memory_leak("BigDecimal(b)")
- end
-
- def test_no_memory_leak_create
- assert_no_memory_leak("b + 10")
- end
- end
-end
diff --git a/test/bigdecimal/test_bigdecimal_util.rb b/test/bigdecimal/test_bigdecimal_util.rb
deleted file mode 100644
index 2f27163ebf..0000000000
--- a/test/bigdecimal/test_bigdecimal_util.rb
+++ /dev/null
@@ -1,141 +0,0 @@
-# frozen_string_literal: false
-require_relative "helper"
-require 'bigdecimal/util'
-
-class TestBigDecimalUtil < Test::Unit::TestCase
- include TestBigDecimalBase
-
- def test_BigDecimal_to_d
- x = BigDecimal(1)
- assert_same(x, x.to_d)
- end
-
- def test_Integer_to_d
- assert_equal(BigDecimal(1), 1.to_d)
- assert_equal(BigDecimal(2<<100), (2<<100).to_d)
-
- assert(1.to_d.frozen?)
- end
-
- def test_Float_to_d_without_precision
- delta = 1.0/10**(Float::DIG+1)
- assert_in_delta(BigDecimal(0.5, 0), 0.5.to_d, delta)
- assert_in_delta(BigDecimal(355.0/113.0, 0), (355.0/113.0).to_d, delta)
-
- assert_equal(9.05, 9.05.to_d.to_f)
- assert_equal("9.05", 9.05.to_d.to_s('F'))
-
- assert_equal("65.6", 65.6.to_d.to_s("F"))
-
- assert_equal(Math::PI, Math::PI.to_d.to_f)
-
- bug9214 = '[ruby-core:58858]'
- assert_equal((-0.0).to_d.sign, -1, bug9214)
-
- assert_raise(TypeError) { 0.3.to_d(nil) }
- assert_raise(TypeError) { 0.3.to_d(false) }
-
- assert(1.1.to_d.frozen?)
-
- assert_equal(BigDecimal("999_999.9999"), 999_999.9999.to_d)
- end
-
- def test_Float_to_d_with_precision
- digits = 5
- delta = 1.0/10**(digits)
- assert_in_delta(BigDecimal(0.5, 5), 0.5.to_d(digits), delta)
- assert_in_delta(BigDecimal(355.0/113.0, 5), (355.0/113.0).to_d(digits), delta)
-
- bug9214 = '[ruby-core:58858]'
- assert_equal((-0.0).to_d(digits).sign, -1, bug9214)
-
- assert(1.1.to_d(digits).frozen?)
- end
-
- def test_Float_to_d_bug13331
- assert_equal(64.4.to_d,
- 1.to_d * 64.4,
- "[ruby-core:80234] [Bug #13331]")
-
- assert_equal((2*Math::PI).to_d,
- 2.to_d * Math::PI,
- "[ruby-core:80234] [Bug #13331]")
- end
-
- def test_Float_to_d_issue_192
- # https://github.com/ruby/bigdecimal/issues/192
- # https://github.com/rails/rails/pull/42125
- if BASE_FIG == 9
- flo = 1_000_000_000.12345
- big = BigDecimal("0.100000000012345e10")
- else # BASE_FIG == 4
- flo = 1_0000.12
- big = BigDecimal("0.1000012e5")
- end
- assert_equal(flo.to_d, big, "[ruby/bigdecimal#192]")
- end
-
- def test_Rational_to_d
- digits = 100
- delta = 1.0/10**(digits)
- assert_in_delta(BigDecimal(1.quo(2), digits), 1.quo(2).to_d(digits), delta)
- assert_in_delta(BigDecimal(355.quo(113), digits), 355.quo(113).to_d(digits), delta)
-
- assert(355.quo(113).to_d(digits).frozen?)
- end
-
- def test_Rational_to_d_with_zero_precision
- assert_equal(BigDecimal(355.quo(113), 0), 355.quo(113).to_d(0))
- end
-
- def test_Rational_to_d_with_negative_precision
- assert_raise(ArgumentError) { 355.quo(113).to_d(-42) }
- end
-
- def test_Complex_to_d
- BigDecimal.save_rounding_mode do
- BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_EVEN)
-
- assert_equal(BigDecimal("1"), Complex(1, 0).to_d)
- assert_equal(BigDecimal("0.333333333333333333333"),
- Complex(1.quo(3), 0).to_d(21))
- assert_equal(BigDecimal("0.1234567"), Complex(0.1234567, 0).to_d)
- assert_equal(BigDecimal("0.1235"), Complex(0.1234567, 0).to_d(4))
-
- assert_raise_with_message(ArgumentError, "can't omit precision for a Rational.") { Complex(1.quo(3), 0).to_d }
-
- assert_raise_with_message(ArgumentError, "Unable to make a BigDecimal from non-zero imaginary number") { Complex(1, 1).to_d }
- end
- end
-
- def test_String_to_d
- assert_equal(BigDecimal('1'), "1__1_1".to_d)
- assert_equal(BigDecimal('2.5'), "2.5".to_d)
- assert_equal(BigDecimal('2.5'), "2.5 degrees".to_d)
- assert_equal(BigDecimal('2.5e1'), "2.5e1 degrees".to_d)
- assert_equal(BigDecimal('0'), "degrees 100.0".to_d)
- assert_equal(BigDecimal('0.125'), "0.1_2_5".to_d)
- assert_equal(BigDecimal('0.125'), "0.1_2_5__".to_d)
- assert_equal(BigDecimal('1'), "1_.125".to_d)
- assert_equal(BigDecimal('1'), "1._125".to_d)
- assert_equal(BigDecimal('0.1'), "0.1__2_5".to_d)
- assert_equal(BigDecimal('0.1'), "0.1_e10".to_d)
- assert_equal(BigDecimal('0.1'), "0.1e_10".to_d)
- assert_equal(BigDecimal('1'), "0.1e1__0".to_d)
- assert_equal(BigDecimal('1.2'), "1.2.3".to_d)
- assert_equal(BigDecimal('1'), "1.".to_d)
- assert_equal(BigDecimal('1'), "1e".to_d)
-
- assert("2.5".to_d.frozen?)
- end
-
- def test_invalid_String_to_d
- assert_equal("invalid".to_d, BigDecimal('0.0'))
- end
-
- def test_Nil_to_d
- assert_equal(nil.to_d, BigDecimal('0.0'))
-
- assert(nil.to_d)
- end
-end
diff --git a/test/bigdecimal/test_bigmath.rb b/test/bigdecimal/test_bigmath.rb
deleted file mode 100644
index 5bf1fbf318..0000000000
--- a/test/bigdecimal/test_bigmath.rb
+++ /dev/null
@@ -1,81 +0,0 @@
-# frozen_string_literal: false
-require_relative "helper"
-require "bigdecimal/math"
-
-class TestBigMath < Test::Unit::TestCase
- include TestBigDecimalBase
- include BigMath
- N = 20
- PINF = BigDecimal("+Infinity")
- MINF = BigDecimal("-Infinity")
- NAN = BigDecimal("NaN")
-
- def test_const
- assert_in_delta(Math::PI, PI(N))
- assert_in_delta(Math::E, E(N))
- end
-
- def test_sqrt
- assert_in_delta(2**0.5, sqrt(BigDecimal("2"), N))
- assert_equal(10, sqrt(BigDecimal("100"), N))
- assert_equal(0.0, sqrt(BigDecimal("0"), N))
- assert_equal(0.0, sqrt(BigDecimal("-0"), N))
- assert_raise(FloatDomainError) {sqrt(BigDecimal("-1.0"), N)}
- assert_raise(FloatDomainError) {sqrt(NAN, N)}
- assert_raise(FloatDomainError) {sqrt(PINF, N)}
- end
-
- def test_sin
- assert_in_delta(0.0, sin(BigDecimal("0.0"), N))
- assert_in_delta(Math.sqrt(2.0) / 2, sin(PI(N) / 4, N))
- assert_in_delta(1.0, sin(PI(N) / 2, N))
- assert_in_delta(0.0, sin(PI(N) * 2, N))
- assert_in_delta(0.0, sin(PI(N), N))
- assert_in_delta(-1.0, sin(PI(N) / -2, N))
- assert_in_delta(0.0, sin(PI(N) * -2, N))
- assert_in_delta(0.0, sin(-PI(N), N))
- assert_in_delta(0.0, sin(PI(N) * 21, N))
- assert_in_delta(0.0, sin(PI(N) * 30, N))
- assert_in_delta(-1.0, sin(PI(N) * BigDecimal("301.5"), N))
- end
-
- def test_cos
- assert_in_delta(1.0, cos(BigDecimal("0.0"), N))
- assert_in_delta(Math.sqrt(2.0) / 2, cos(PI(N) / 4, N))
- assert_in_delta(0.0, cos(PI(N) / 2, N))
- assert_in_delta(1.0, cos(PI(N) * 2, N))
- assert_in_delta(-1.0, cos(PI(N), N))
- assert_in_delta(0.0, cos(PI(N) / -2, N))
- assert_in_delta(1.0, cos(PI(N) * -2, N))
- assert_in_delta(-1.0, cos(-PI(N), N))
- assert_in_delta(-1.0, cos(PI(N) * 21, N))
- assert_in_delta(1.0, cos(PI(N) * 30, N))
- assert_in_delta(0.0, cos(PI(N) * BigDecimal("301.5"), N))
- end
-
- def test_atan
- assert_equal(0.0, atan(BigDecimal("0.0"), N))
- assert_in_delta(Math::PI/4, atan(BigDecimal("1.0"), N))
- assert_in_delta(Math::PI/6, atan(sqrt(BigDecimal("3.0"), N) / 3, N))
- assert_in_delta(Math::PI/2, atan(PINF, N))
- assert_equal(BigDecimal("0.823840753418636291769355073102514088959345624027952954058347023122539489"),
- atan(BigDecimal("1.08"), 72).round(72), '[ruby-dev:41257]')
- end
-
- def test_log
- assert_equal(0, BigMath.log(BigDecimal("1.0"), 10))
- assert_in_epsilon(Math.log(10)*1000, BigMath.log(BigDecimal("1e1000"), 10))
- assert_raise(Math::DomainError) {BigMath.log(BigDecimal("0"), 10)}
- assert_raise(Math::DomainError) {BigMath.log(BigDecimal("-1"), 10)}
- assert_separately(%w[-rbigdecimal], <<-SRC)
- begin
- x = BigMath.log(BigDecimal("1E19999999999999"), 10)
- rescue FloatDomainError
- else
- unless x.infinite?
- assert_in_epsilon(Math.log(10)*19999999999999, x)
- end
- end
- SRC
- end
-end
diff --git a/test/bigdecimal/test_ractor.rb b/test/bigdecimal/test_ractor.rb
deleted file mode 100644
index 798cc494e1..0000000000
--- a/test/bigdecimal/test_ractor.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-require_relative "helper"
-
-class TestBigDecimalRactor < Test::Unit::TestCase
- include TestBigDecimalBase
-
- def setup
- super
- omit unless defined? Ractor
- end
-
- def test_ractor_shareable
- assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
- begin;
- $VERBOSE = nil
- require "bigdecimal"
- r = Ractor.new BigDecimal(Math::PI, Float::DIG+1) do |pi|
- BigDecimal('2.0')*pi
- end
- assert_equal(2*Math::PI, r.take)
- end;
- end
-end
diff --git a/test/cgi/test_cgi_util.rb b/test/cgi/test_cgi_util.rb
index b3a46a1c25..b0612fc87d 100644
--- a/test/cgi/test_cgi_util.rb
+++ b/test/cgi/test_cgi_util.rb
@@ -74,6 +74,10 @@ class CGIUtilTest < Test::Unit::TestCase
assert_equal('%26%3C%3E%22%20%E3%82%86%E3%82%93%E3%82%86%E3%82%93'.ascii_only?, CGI.escapeURIComponent(@str1).ascii_only?) if defined?(::Encoding)
end
+ def test_cgi_escape_uri_component
+ assert_equal('%26%3C%3E%22%20%E3%82%86%E3%82%93%E3%82%86%E3%82%93', CGI.escape_uri_component(@str1))
+ end
+
def test_cgi_escapeURIComponent_with_unreserved_characters
assert_equal("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~",
CGI.escapeURIComponent("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"),
@@ -101,6 +105,11 @@ class CGIUtilTest < Test::Unit::TestCase
assert_equal("\u{30E1 30E2 30EA 691C 7D22}", CGI.unescapeURIComponent("\u{30E1 30E2 30EA}%E6%A4%9C%E7%B4%A2"))
end
+ def test_cgi_unescape_uri_component
+ str = CGI.unescape_uri_component('%26%3C%3E%22%20%E3%82%86%E3%82%93%E3%82%86%E3%82%93')
+ assert_equal(@str1, str)
+ end
+
def test_cgi_unescapeURIComponent_preserve_encoding
assert_equal(Encoding::US_ASCII, CGI.unescapeURIComponent("%C0%3C%3C".dup.force_encoding("US-ASCII")).encoding)
assert_equal(Encoding::ASCII_8BIT, CGI.unescapeURIComponent("%C0%3C%3C".dup.force_encoding("ASCII-8BIT")).encoding)
@@ -177,6 +186,22 @@ class CGIUtilTest < Test::Unit::TestCase
assert_equal('&<&amp>&quot&abcdefghijklmn', CGI.unescapeHTML('&&lt;&amp&gt;&quot&abcdefghijklmn'))
end
+ module UnescapeHTMLTests
+ def test_cgi_unescapeHTML_following_known_first_letter
+ assert_equal('&a>&q>&l>&g>', CGI.unescapeHTML('&a&gt;&q&gt;&l&gt;&g&gt;'))
+ end
+
+ def test_cgi_unescapeHTML_following_number_sign
+ assert_equal('&#>&#x>', CGI.unescapeHTML('&#&gt;&#x&gt;'))
+ end
+
+ def test_cgi_unescapeHTML_following_invalid_numeric
+ assert_equal('&#1114112>&#x110000>', CGI.unescapeHTML('&#1114112&gt;&#x110000&gt;'))
+ end
+ end
+
+ include UnescapeHTMLTests
+
Encoding.list.each do |enc|
begin
escaped = "&#39;&amp;&quot;&gt;&lt;".encode(enc)
@@ -274,6 +299,8 @@ class CGIUtilPureRubyTest < Test::Unit::TestCase
end if defined?(CGI::Escape)
end
+ include CGIUtilTest::UnescapeHTMLTests
+
def test_cgi_escapeHTML_with_invalid_byte_sequence
assert_equal("&lt;\xA4??&gt;", CGI.escapeHTML(%[<\xA4??>]))
end
diff --git a/test/coverage/test_coverage.rb b/test/coverage/test_coverage.rb
index 6afef8ce1b..4773280cae 100644
--- a/test/coverage/test_coverage.rb
+++ b/test/coverage/test_coverage.rb
@@ -5,22 +5,33 @@ require "tmpdir"
require "envutil"
class TestCoverage < Test::Unit::TestCase
+ # The command-line arguments that we will pass to the ruby subprocess invoked
+ # by assert_in_out_err. In general this is just requiring the coverage
+ # library, but if prism is enabled we want to additionally pass that option
+ # through.
+ ARGV = ["-rcoverage"]
+
+ if RubyVM::InstructionSequence.compile('').to_a[4][:parser] == :prism
+ ARGV << "-W:no-experimental"
+ ARGV << "--parser=prism"
+ end
+
def test_result_without_start
- assert_in_out_err(%w[-rcoverage], <<-"end;", [], /coverage measurement is not enabled/)
+ assert_in_out_err(ARGV, <<-"end;", [], /coverage measurement is not enabled/)
Coverage.result
p :NG
end;
end
def test_peek_result_without_start
- assert_in_out_err(%w[-rcoverage], <<-"end;", [], /coverage measurement is not enabled/)
+ assert_in_out_err(ARGV, <<-"end;", [], /coverage measurement is not enabled/)
Coverage.peek_result
p :NG
end;
end
def test_result_with_nothing
- assert_in_out_err(%w[-rcoverage], <<-"end;", ["{}"], [])
+ assert_in_out_err(ARGV, <<-"end;", ["{}"], [])
Coverage.start
p Coverage.result
end;
@@ -34,7 +45,7 @@ class TestCoverage < Test::Unit::TestCase
end
def test_coverage_running?
- assert_in_out_err(%w[-rcoverage], <<-"end;", ["false", "true", "true", "false"], [])
+ assert_in_out_err(ARGV, <<-"end;", ["false", "true", "true", "false"], [])
p Coverage.running?
Coverage.start
p Coverage.running?
@@ -56,7 +67,7 @@ class TestCoverage < Test::Unit::TestCase
EOS
end
- assert_in_out_err(%w[-rcoverage], <<-"end;", ["[1, 0, nil]", "[1, 1, nil]", "[1, 1, nil]"], [])
+ assert_in_out_err(ARGV, <<-"end;", ["[1, 0, nil]", "[1, 1, nil]", "[1, 1, nil]"], [])
Coverage.start
tmp = Dir.pwd
require tmp + "/test.rb"
@@ -92,7 +103,7 @@ class TestCoverage < Test::Unit::TestCase
exp1 = { "#{tmp}/test.rb" => [1, 0, nil] }.inspect
exp2 = {}.inspect
exp3 = { "#{tmp}/test2.rb" => [1] }.inspect
- assert_in_out_err(%w[-rcoverage], <<-"end;", [exp1, exp2, exp3], [])
+ assert_in_out_err(ARGV, <<-"end;", [exp1, exp2, exp3], [])
Coverage.start
tmp = Dir.pwd
require tmp + "/test.rb"
@@ -124,7 +135,7 @@ class TestCoverage < Test::Unit::TestCase
f.puts "])"
end
- assert_in_out_err(%w[-rcoverage], <<-"end;", ["10003"], [])
+ assert_in_out_err(ARGV, <<-"end;", ["10003"], [])
Coverage.start
tmp = Dir.pwd
require tmp + '/test.rb'
@@ -153,7 +164,7 @@ class TestCoverage < Test::Unit::TestCase
f.puts 'end'
end
- assert_in_out_err(%w[-W0 -rcoverage], <<-"end;", ["[1, 1, 1, 400, nil, nil, nil, nil, nil, nil, nil]"], [], bug13305)
+ assert_in_out_err(["-W0", *ARGV], <<-"end;", ["[1, 1, 1, 400, nil, nil, nil, nil, nil, nil, nil]"], [], bug13305)
Coverage.start(:all)
tmp = Dir.pwd
require tmp + '/test.rb'
@@ -165,7 +176,7 @@ class TestCoverage < Test::Unit::TestCase
end
def test_eval_coverage
- assert_in_out_err(%w[-rcoverage], <<-"end;", ["[1, 1, 1, nil, 0, nil]"], [])
+ assert_in_out_err(ARGV, <<-"end;", ["[1, 1, 1, nil, 0, nil]"], [])
Coverage.start(eval: true, lines: true)
eval(<<-RUBY, TOPLEVEL_BINDING, "test.rb")
@@ -242,7 +253,7 @@ class TestCoverage < Test::Unit::TestCase
Dir.chdir(tmp) {
File.write("test.rb", code)
- assert_in_out_err(%w[-W0 -rcoverage], <<-"end;", stdout, [])
+ assert_in_out_err(["-W0", *ARGV], <<-"end;", stdout, [])
Coverage.start(#{ opt })
tmp = Dir.pwd
require tmp + '/test.rb'
@@ -642,7 +653,7 @@ class TestCoverage < Test::Unit::TestCase
"{:lines=>[0, 1, 1, nil, 0, nil, nil]}",
"{:lines=>[0, 1, 0, nil, 1, nil, nil]}",
]
- assert_in_out_err(%w[-rcoverage], <<-"end;", exp, [])
+ assert_in_out_err(ARGV, <<-"end;", exp, [])
Coverage.start(lines: true)
tmp = Dir.pwd
f = tmp + "/test.rb"
@@ -676,7 +687,7 @@ class TestCoverage < Test::Unit::TestCase
"{:branches=>{[:if, 0, 2, 2, 6, 5]=>{[:then, 1, 3, 4, 3, 8]=>0, [:else, 2, 5, 4, 5, 12]=>1}}}",
"{:branches=>{[:if, 0, 2, 2, 6, 5]=>{[:then, 1, 3, 4, 3, 8]=>0, [:else, 2, 5, 4, 5, 12]=>1}}}",
]
- assert_in_out_err(%w[-rcoverage], <<-"end;", exp, [])
+ assert_in_out_err(ARGV, <<-"end;", exp, [])
Coverage.start(branches: true)
tmp = Dir.pwd
f = tmp + "/test.rb"
@@ -712,7 +723,7 @@ class TestCoverage < Test::Unit::TestCase
"{:methods=>{[Object, :foo, 1, 0, 7, 3]=>1}}",
"{:methods=>{[Object, :foo, 1, 0, 7, 3]=>1}}"
]
- assert_in_out_err(%w[-rcoverage], <<-"end;", exp, [])
+ assert_in_out_err(ARGV, <<-"end;", exp, [])
Coverage.start(methods: true)
tmp = Dir.pwd
f = tmp + "/test.rb"
@@ -748,7 +759,7 @@ class TestCoverage < Test::Unit::TestCase
"{:oneshot_lines=>[5]}",
"{:oneshot_lines=>[]}",
]
- assert_in_out_err(%w[-rcoverage], <<-"end;", exp, [])
+ assert_in_out_err(ARGV, <<-"end;", exp, [])
Coverage.start(oneshot_lines: true)
tmp = Dir.pwd
f = tmp + "/test.rb"
@@ -848,9 +859,10 @@ class TestCoverage < Test::Unit::TestCase
EOS
end
- cov1 = "[0, 0, nil, nil, 0, 1, nil, nil, 0, 0, nil]"
- cov2 = "[0, 0, nil, nil, 0, 1, nil, nil, 0, 1, nil]"
- assert_in_out_err(%w[-rcoverage], <<-"end;", [cov1, cov2], [])
+ assert_separately(%w[-rcoverage], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ cov1 = [0, 0, nil, nil, 0, 1, nil, nil, 0, 0, nil]
+ cov2 = [0, 0, nil, nil, 0, 1, nil, nil, 0, 1, nil]
Coverage.setup
tmp = Dir.pwd
require tmp + "/test.rb"
@@ -859,15 +871,34 @@ class TestCoverage < Test::Unit::TestCase
bar
Coverage.suspend
baz
- p Coverage.peek_result[tmp + "/test.rb"]
+ assert_equal cov1, Coverage.peek_result[tmp + "/test.rb"]
Coverage.resume
baz
- p Coverage.result[tmp + "/test.rb"]
+ assert_equal cov2, Coverage.result[tmp + "/test.rb"]
end;
- cov1 = "{:lines=>[0, 0, nil, nil, 0, 1, nil, nil, 0, 0, nil], :branches=>{}, :methods=>{[Object, :baz, 9, 12, 11, 15]=>0, [Object, :bar, 5, 12, 7, 15]=>1, [Object, :foo, 1, 12, 3, 15]=>0}}"
- cov2 = "{:lines=>[0, 0, nil, nil, 0, 1, nil, nil, 0, 1, nil], :branches=>{}, :methods=>{[Object, :baz, 9, 12, 11, 15]=>1, [Object, :bar, 5, 12, 7, 15]=>1, [Object, :foo, 1, 12, 3, 15]=>0}}"
- assert_in_out_err(%w[-rcoverage], <<-"end;", [cov1, cov2], [])
+ assert_separately(%w[-rcoverage], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ cov1 = {
+ lines: [0, 0, nil, nil, 0, 1, nil, nil, 0, 0, nil],
+ branches: {},
+ methods: {
+ [Object, :baz, 9, 12, 11, 15]=>0,
+ [Object, :bar, 5, 12, 7, 15]=>1,
+ [Object, :foo, 1, 12, 3, 15]=>0,
+ }
+ }
+
+ cov2 = {
+ lines: [0, 0, nil, nil, 0, 1, nil, nil, 0, 1, nil],
+ branches: {},
+ methods: {
+ [Object, :baz, 9, 12, 11, 15]=>1,
+ [Object, :bar, 5, 12, 7, 15]=>1,
+ [Object, :foo, 1, 12, 3, 15]=>0,
+ }
+ }
+
Coverage.setup(:all)
tmp = Dir.pwd
require tmp + "/test.rb"
@@ -876,15 +907,16 @@ class TestCoverage < Test::Unit::TestCase
bar
Coverage.suspend
baz
- p Coverage.peek_result[tmp + "/test.rb"]
+ assert_equal cov1, Coverage.peek_result[tmp + "/test.rb"]
Coverage.resume
baz
- p Coverage.result[tmp + "/test.rb"]
+ assert_equal cov2, Coverage.result[tmp + "/test.rb"]
end;
- cov1 = "{:oneshot_lines=>[6]}"
- cov2 = "{:oneshot_lines=>[6, 10]}"
- assert_in_out_err(%w[-rcoverage], <<-"end;", [cov1, cov2], [])
+ assert_separately(%w[-rcoverage], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ cov1 = {:oneshot_lines=>[6]}
+ cov2 = {:oneshot_lines=>[6, 10]}
Coverage.setup(oneshot_lines: true)
tmp = Dir.pwd
require tmp + "/test.rb"
@@ -893,17 +925,17 @@ class TestCoverage < Test::Unit::TestCase
bar
Coverage.suspend
baz
- p Coverage.peek_result[tmp + "/test.rb"]
+ assert_equal cov1, Coverage.peek_result[tmp + "/test.rb"]
Coverage.resume
baz
- p Coverage.result[tmp + "/test.rb"]
+ assert_equal cov2, Coverage.result[tmp + "/test.rb"]
end;
}
}
end
def test_coverage_state
- assert_in_out_err(%w[-rcoverage], <<-"end;", [":idle", ":running", ":running", ":idle"], [])
+ assert_in_out_err(ARGV, <<-"end;", [":idle", ":running", ":running", ":idle"], [])
p Coverage.state
Coverage.start
p Coverage.state
@@ -913,7 +945,7 @@ class TestCoverage < Test::Unit::TestCase
p Coverage.state
end;
- assert_in_out_err(%w[-rcoverage], <<-"end;", [":idle", ":suspended", ":running", ":suspended", ":running", ":suspended", ":idle"], [])
+ assert_in_out_err(ARGV, <<-"end;", [":idle", ":suspended", ":running", ":suspended", ":running", ":suspended", ":idle"], [])
p Coverage.state
Coverage.setup
p Coverage.state
@@ -931,14 +963,14 @@ class TestCoverage < Test::Unit::TestCase
end
def test_result_without_resume
- assert_in_out_err(%w[-rcoverage], <<-"end;", ["{}"], [])
+ assert_in_out_err(ARGV, <<-"end;", ["{}"], [])
Coverage.setup
p Coverage.result
end;
end
def test_result_after_suspend
- assert_in_out_err(%w[-rcoverage], <<-"end;", ["{}"], [])
+ assert_in_out_err(ARGV, <<-"end;", ["{}"], [])
Coverage.start
Coverage.suspend
p Coverage.result
@@ -946,21 +978,21 @@ class TestCoverage < Test::Unit::TestCase
end
def test_resume_without_setup
- assert_in_out_err(%w[-rcoverage], <<-"end;", [], /coverage measurement is not set up yet/)
+ assert_in_out_err(ARGV, <<-"end;", [], /coverage measurement is not set up yet/)
Coverage.resume
p :NG
end;
end
def test_suspend_without_setup
- assert_in_out_err(%w[-rcoverage], <<-"end;", [], /coverage measurement is not running/)
+ assert_in_out_err(ARGV, <<-"end;", [], /coverage measurement is not running/)
Coverage.suspend
p :NG
end;
end
def test_double_resume
- assert_in_out_err(%w[-rcoverage], <<-"end;", [], /coverage measurement is already running/)
+ assert_in_out_err(ARGV, <<-"end;", [], /coverage measurement is already running/)
Coverage.start
Coverage.resume
p :NG
@@ -968,7 +1000,7 @@ class TestCoverage < Test::Unit::TestCase
end
def test_double_suspend
- assert_in_out_err(%w[-rcoverage], <<-"end;", [], /coverage measurement is not running/)
+ assert_in_out_err(ARGV, <<-"end;", [], /coverage measurement is not running/)
Coverage.setup
Coverage.suspend
p :NG
diff --git a/test/csv/helper.rb b/test/csv/helper.rb
deleted file mode 100644
index ff3aa38b10..0000000000
--- a/test/csv/helper.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-require "tempfile"
-require "test/unit"
-
-require "csv"
-
-require_relative "../lib/with_different_ofs"
-
-module CSVHelper
- def with_chunk_size(chunk_size)
- chunk_size_keep = ENV["CSV_PARSER_SCANNER_TEST_CHUNK_SIZE"]
- begin
- ENV["CSV_PARSER_SCANNER_TEST_CHUNK_SIZE"] = chunk_size
- yield
- ensure
- ENV["CSV_PARSER_SCANNER_TEST_CHUNK_SIZE"] = chunk_size_keep
- end
- end
-
- def with_verbose(verbose)
- original = $VERBOSE
- begin
- $VERBOSE = verbose
- yield
- ensure
- $VERBOSE = original
- end
- end
-
- def with_default_internal(encoding)
- original = Encoding.default_internal
- begin
- with_verbose(false) do
- Encoding.default_internal = encoding
- end
- yield
- ensure
- with_verbose(false) do
- Encoding.default_internal = original
- end
- end
- end
-end
diff --git a/test/csv/interface/test_delegation.rb b/test/csv/interface/test_delegation.rb
deleted file mode 100644
index 349257633b..0000000000
--- a/test/csv/interface/test_delegation.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-# frozen_string_literal: false
-
-require_relative "../helper"
-
-class TestCSVInterfaceDelegation < Test::Unit::TestCase
- class TestStringIO < self
- def setup
- @csv = CSV.new("h1,h2")
- end
-
- def test_flock
- assert_raise(NotImplementedError) do
- @csv.flock(File::LOCK_EX)
- end
- end
-
- def test_ioctl
- assert_raise(NotImplementedError) do
- @csv.ioctl(0)
- end
- end
-
- def test_stat
- assert_raise(NotImplementedError) do
- @csv.stat
- end
- end
-
- def test_to_i
- assert_raise(NotImplementedError) do
- @csv.to_i
- end
- end
-
- def test_binmode?
- assert_equal(false, @csv.binmode?)
- end
-
- def test_path
- assert_equal(nil, @csv.path)
- end
-
- def test_to_io
- assert_instance_of(StringIO, @csv.to_io)
- end
- end
-end
diff --git a/test/csv/interface/test_read.rb b/test/csv/interface/test_read.rb
deleted file mode 100644
index 001177036a..0000000000
--- a/test/csv/interface/test_read.rb
+++ /dev/null
@@ -1,381 +0,0 @@
-# frozen_string_literal: false
-
-require_relative "../helper"
-
-class TestCSVInterfaceRead < Test::Unit::TestCase
- extend DifferentOFS
-
- def setup
- super
- @data = ""
- @data << "1\t2\t3\r\n"
- @data << "4\t5\r\n"
- @input = Tempfile.new(["interface-read", ".csv"], binmode: true)
- @input << @data
- @input.rewind
- @rows = [
- ["1", "2", "3"],
- ["4", "5"],
- ]
- end
-
- def teardown
- @input.close(true)
- super
- end
-
- def test_foreach
- rows = []
- CSV.foreach(@input.path, col_sep: "\t", row_sep: "\r\n") do |row|
- rows << row
- end
- assert_equal(@rows, rows)
- end
-
- if respond_to?(:ractor)
- ractor
- def test_foreach_in_ractor
- ractor = Ractor.new(@input.path) do |path|
- rows = []
- CSV.foreach(path, col_sep: "\t", row_sep: "\r\n") do |row|
- rows << row
- end
- rows
- end
- rows = [
- ["1", "2", "3"],
- ["4", "5"],
- ]
- assert_equal(rows, ractor.take)
- end
- end
-
- def test_foreach_mode
- rows = []
- CSV.foreach(@input.path, "r", col_sep: "\t", row_sep: "\r\n") do |row|
- rows << row
- end
- assert_equal(@rows, rows)
- end
-
- def test_foreach_enumerator
- rows = CSV.foreach(@input.path, col_sep: "\t", row_sep: "\r\n").to_a
- assert_equal(@rows, rows)
- end
-
- def test_closed?
- csv = CSV.open(@input.path, "r+", col_sep: "\t", row_sep: "\r\n")
- assert_not_predicate(csv, :closed?)
- csv.close
- assert_predicate(csv, :closed?)
- end
-
- def test_open_auto_close
- csv = nil
- CSV.open(@input.path) do |_csv|
- csv = _csv
- end
- assert_predicate(csv, :closed?)
- end
-
- def test_open_closed
- csv = nil
- CSV.open(@input.path) do |_csv|
- csv = _csv
- csv.close
- end
- assert_predicate(csv, :closed?)
- end
-
- def test_open_block_return_value
- return_value = CSV.open(@input.path) do
- "Return value."
- end
- assert_equal("Return value.", return_value)
- end
-
- def test_open_encoding_valid
- # U+1F600 GRINNING FACE
- # U+1F601 GRINNING FACE WITH SMILING EYES
- File.open(@input.path, "w") do |file|
- file << "\u{1F600},\u{1F601}"
- end
- CSV.open(@input.path, encoding: "utf-8") do |csv|
- assert_equal([["\u{1F600}", "\u{1F601}"]],
- csv.to_a)
- end
- end
-
- def test_open_encoding_invalid
- # U+1F600 GRINNING FACE
- # U+1F601 GRINNING FACE WITH SMILING EYES
- File.open(@input.path, "w") do |file|
- file << "\u{1F600},\u{1F601}"
- end
- CSV.open(@input.path, encoding: "EUC-JP") do |csv|
- error = assert_raise(CSV::MalformedCSVError) do
- csv.shift
- end
- assert_equal("Invalid byte sequence in EUC-JP in line 1.",
- error.message)
- end
- end
-
- def test_open_encoding_nonexistent
- _output, error = capture_output do
- CSV.open(@input.path, encoding: "nonexistent") do
- end
- end
- assert_equal("path:0: warning: Unsupported encoding nonexistent ignored\n",
- error.gsub(/\A.+:\d+: /, "path:0: "))
- end
-
- def test_open_encoding_utf_8_with_bom
- # U+FEFF ZERO WIDTH NO-BREAK SPACE, BOM
- # U+1F600 GRINNING FACE
- # U+1F601 GRINNING FACE WITH SMILING EYES
- File.open(@input.path, "w") do |file|
- file << "\u{FEFF}\u{1F600},\u{1F601}"
- end
- CSV.open(@input.path, encoding: "bom|utf-8") do |csv|
- assert_equal([["\u{1F600}", "\u{1F601}"]],
- csv.to_a)
- end
- end
-
- def test_open_invalid_byte_sequence_in_utf_8
- CSV.open(@input.path, "w", encoding: Encoding::CP932) do |rows|
- error = assert_raise(Encoding::InvalidByteSequenceError) do
- rows << ["\x82\xa0"]
- end
- assert_equal('"\x82" on UTF-8',
- error.message)
- end
- end
-
- def test_open_with_invalid_nil
- CSV.open(@input.path, "w", encoding: Encoding::CP932, invalid: nil) do |rows|
- error = assert_raise(Encoding::InvalidByteSequenceError) do
- rows << ["\x82\xa0"]
- end
- assert_equal('"\x82" on UTF-8',
- error.message)
- end
- end
-
- def test_open_with_invalid_replace
- CSV.open(@input.path, "w", encoding: Encoding::CP932, invalid: :replace) do |rows|
- rows << ["\x82\xa0".force_encoding(Encoding::UTF_8)]
- end
- CSV.open(@input.path, encoding: Encoding::CP932) do |csv|
- assert_equal([["??"]],
- csv.to_a)
- end
- end
-
- def test_open_with_invalid_replace_and_replace_string
- CSV.open(@input.path, "w", encoding: Encoding::CP932, invalid: :replace, replace: "X") do |rows|
- rows << ["\x82\xa0".force_encoding(Encoding::UTF_8)]
- end
- CSV.open(@input.path, encoding: Encoding::CP932) do |csv|
- assert_equal([["XX"]],
- csv.to_a)
- end
- end
-
- def test_open_with_undef_replace
- # U+00B7 Middle Dot
- CSV.open(@input.path, "w", encoding: Encoding::CP932, undef: :replace) do |rows|
- rows << ["\u00B7"]
- end
- CSV.open(@input.path, encoding: Encoding::CP932) do |csv|
- assert_equal([["?"]],
- csv.to_a)
- end
- end
-
- def test_open_with_undef_replace_and_replace_string
- # U+00B7 Middle Dot
- CSV.open(@input.path, "w", encoding: Encoding::CP932, undef: :replace, replace: "X") do |rows|
- rows << ["\u00B7"]
- end
- CSV.open(@input.path, encoding: Encoding::CP932) do |csv|
- assert_equal([["X"]],
- csv.to_a)
- end
- end
-
- def test_open_with_newline
- CSV.open(@input.path, col_sep: "\t", universal_newline: true) do |csv|
- assert_equal(@rows, csv.to_a)
- end
- File.binwrite(@input.path, "1,2,3\r\n" "4,5\n")
- CSV.open(@input.path, newline: :universal) do |csv|
- assert_equal(@rows, csv.to_a)
- end
- end
-
- def test_parse
- assert_equal(@rows,
- CSV.parse(@data, col_sep: "\t", row_sep: "\r\n"))
- end
-
- def test_parse_block
- rows = []
- CSV.parse(@data, col_sep: "\t", row_sep: "\r\n") do |row|
- rows << row
- end
- assert_equal(@rows, rows)
- end
-
- def test_parse_enumerator
- rows = CSV.parse(@data, col_sep: "\t", row_sep: "\r\n").to_a
- assert_equal(@rows, rows)
- end
-
- def test_parse_headers_only
- table = CSV.parse("a,b,c", headers: true)
- assert_equal([
- ["a", "b", "c"],
- [],
- ],
- [
- table.headers,
- table.each.to_a,
- ])
- end
-
- def test_parse_line
- assert_equal(["1", "2", "3"],
- CSV.parse_line("1;2;3", col_sep: ";"))
- end
-
- def test_parse_line_shortcut
- assert_equal(["1", "2", "3"],
- "1;2;3".parse_csv(col_sep: ";"))
- end
-
- def test_parse_line_empty
- assert_equal(nil, CSV.parse_line("")) # to signal eof
- end
-
- def test_parse_line_empty_line
- assert_equal([], CSV.parse_line("\n1,2,3"))
- end
-
- def test_read
- assert_equal(@rows,
- CSV.read(@input.path, col_sep: "\t", row_sep: "\r\n"))
- end
-
- if respond_to?(:ractor)
- ractor
- def test_read_in_ractor
- ractor = Ractor.new(@input.path) do |path|
- CSV.read(path, col_sep: "\t", row_sep: "\r\n")
- end
- rows = [
- ["1", "2", "3"],
- ["4", "5"],
- ]
- assert_equal(rows, ractor.take)
- end
- end
-
- def test_readlines
- assert_equal(@rows,
- CSV.readlines(@input.path, col_sep: "\t", row_sep: "\r\n"))
- end
-
- def test_open_read
- rows = CSV.open(@input.path, col_sep: "\t", row_sep: "\r\n") do |csv|
- csv.read
- end
- assert_equal(@rows, rows)
- end
-
- def test_open_readlines
- rows = CSV.open(@input.path, col_sep: "\t", row_sep: "\r\n") do |csv|
- csv.readlines
- end
- assert_equal(@rows, rows)
- end
-
- def test_table
- table = CSV.table(@input.path, col_sep: "\t", row_sep: "\r\n")
- assert_equal(CSV::Table.new([
- CSV::Row.new([:"1", :"2", :"3"], [4, 5, nil]),
- ]),
- table)
- end
-
- def test_shift # aliased as gets() and readline()
- CSV.open(@input.path, "rb+", col_sep: "\t", row_sep: "\r\n") do |csv|
- rows = [
- csv.shift,
- csv.shift,
- csv.shift,
- ]
- assert_equal(@rows + [nil],
- rows)
- end
- end
-
- def test_enumerator
- CSV.open(@input.path, col_sep: "\t", row_sep: "\r\n") do |csv|
- assert_equal(@rows, csv.each.to_a)
- end
- end
-
- def test_shift_and_each
- CSV.open(@input.path, col_sep: "\t", row_sep: "\r\n") do |csv|
- rows = []
- rows << csv.shift
- rows.concat(csv.each.to_a)
- assert_equal(@rows, rows)
- end
- end
-
- def test_each_twice
- CSV.open(@input.path, col_sep: "\t", row_sep: "\r\n") do |csv|
- assert_equal([
- @rows,
- [],
- ],
- [
- csv.each.to_a,
- csv.each.to_a,
- ])
- end
- end
-
- def test_eof?
- eofs = []
- CSV.open(@input.path, col_sep: "\t", row_sep: "\r\n") do |csv|
- eofs << csv.eof?
- csv.shift
- eofs << csv.eof?
- csv.shift
- eofs << csv.eof?
- end
- assert_equal([false, false, true],
- eofs)
- end
-
- def test_new_nil
- assert_raise_with_message ArgumentError, "Cannot parse nil as CSV" do
- CSV.new(nil)
- end
- end
-
- def test_options_not_modified
- options = {}.freeze
- CSV.foreach(@input.path, **options)
- CSV.open(@input.path, **options) {}
- CSV.parse("", **options)
- CSV.parse_line("", **options)
- CSV.read(@input.path, **options)
- CSV.readlines(@input.path, **options)
- CSV.table(@input.path, **options)
- end
-end
diff --git a/test/csv/interface/test_read_write.rb b/test/csv/interface/test_read_write.rb
deleted file mode 100644
index c371e9c5fc..0000000000
--- a/test/csv/interface/test_read_write.rb
+++ /dev/null
@@ -1,124 +0,0 @@
-# frozen_string_literal: false
-
-require_relative "../helper"
-
-class TestCSVInterfaceReadWrite < Test::Unit::TestCase
- extend DifferentOFS
-
- def test_filter
- input = <<-CSV.freeze
-1;2;3
-4;5
- CSV
- output = ""
- CSV.filter(input, output,
- in_col_sep: ";",
- out_col_sep: ",",
- converters: :all) do |row|
- row.map! {|n| n * 2}
- row << "Added\r"
- end
- assert_equal(<<-CSV, output)
-2,4,6,"Added\r"
-8,10,"Added\r"
- CSV
- end
-
- def test_filter_headers_true
- input = <<-CSV.freeze
-Name,Value
-foo,0
-bar,1
-baz,2
- CSV
- output = ""
- CSV.filter(input, output, headers: true) do |row|
- row[0] += "X"
- row[1] = row[1].to_i + 1
- end
- assert_equal(<<-CSV, output)
-fooX,1
-barX,2
-bazX,3
- CSV
- end
-
- def test_filter_headers_true_write_headers
- input = <<-CSV.freeze
-Name,Value
-foo,0
-bar,1
-baz,2
- CSV
- output = ""
- CSV.filter(input, output, headers: true, out_write_headers: true) do |row|
- if row.is_a?(Array)
- row[0] += "X"
- row[1] += "Y"
- else
- row[0] += "X"
- row[1] = row[1].to_i + 1
- end
- end
- assert_equal(<<-CSV, output)
-NameX,ValueY
-fooX,1
-barX,2
-bazX,3
- CSV
- end
-
- def test_filter_headers_array_write_headers
- input = <<-CSV.freeze
-foo,0
-bar,1
-baz,2
- CSV
- output = ""
- CSV.filter(input, output,
- headers: ["Name", "Value"],
- out_write_headers: true) do |row|
- row[0] += "X"
- row[1] = row[1].to_i + 1
- end
- assert_equal(<<-CSV, output)
-Name,Value
-fooX,1
-barX,2
-bazX,3
- CSV
- end
-
- def test_instance_same
- data = ""
- assert_equal(CSV.instance(data, col_sep: ";").object_id,
- CSV.instance(data, col_sep: ";").object_id)
- end
-
- def test_instance_append
- output = ""
- CSV.instance(output, col_sep: ";") << ["a", "b", "c"]
- assert_equal(<<-CSV, output)
-a;b;c
- CSV
- CSV.instance(output, col_sep: ";") << [1, 2, 3]
- assert_equal(<<-CSV, output)
-a;b;c
-1;2;3
- CSV
- end
-
- def test_instance_shortcut
- assert_equal(CSV.instance,
- CSV {|csv| csv})
- end
-
- def test_instance_shortcut_with_io
- io = StringIO.new
- from_instance = CSV.instance(io, col_sep: ";") { |csv| csv << ["a", "b", "c"] }
- from_shortcut = CSV(io, col_sep: ";") { |csv| csv << ["e", "f", "g"] }
-
- assert_equal(from_instance, from_shortcut)
- assert_equal(from_instance.string, "a;b;c\ne;f;g\n")
- end
-end
diff --git a/test/csv/interface/test_write.rb b/test/csv/interface/test_write.rb
deleted file mode 100644
index 0cd39a7663..0000000000
--- a/test/csv/interface/test_write.rb
+++ /dev/null
@@ -1,217 +0,0 @@
-# frozen_string_literal: false
-
-require_relative "../helper"
-
-class TestCSVInterfaceWrite < Test::Unit::TestCase
- extend DifferentOFS
-
- def setup
- super
- @output = Tempfile.new(["interface-write", ".csv"])
- end
-
- def teardown
- @output.close(true)
- super
- end
-
- def test_generate_default
- csv_text = CSV.generate do |csv|
- csv << [1, 2, 3] << [4, nil, 5]
- end
- assert_equal(<<-CSV, csv_text)
-1,2,3
-4,,5
- CSV
- end
-
- if respond_to?(:ractor)
- ractor
- def test_generate_default_in_ractor
- ractor = Ractor.new do
- CSV.generate do |csv|
- csv << [1, 2, 3] << [4, nil, 5]
- end
- end
- assert_equal(<<-CSV, ractor.take)
-1,2,3
-4,,5
- CSV
- end
- end
-
- def test_generate_append
- csv_text = <<-CSV
-1,2,3
-4,,5
- CSV
- CSV.generate(csv_text) do |csv|
- csv << ["last", %Q{"row"}]
- end
- assert_equal(<<-CSV, csv_text)
-1,2,3
-4,,5
-last,"""row"""
- CSV
- end
-
- def test_generate_no_new_line
- csv_text = CSV.generate("test") do |csv|
- csv << ["row"]
- end
- assert_equal(<<-CSV, csv_text)
-testrow
- CSV
- end
-
- def test_generate_line_col_sep
- line = CSV.generate_line(["1", "2", "3"], col_sep: ";")
- assert_equal(<<-LINE, line)
-1;2;3
- LINE
- end
-
- def test_generate_line_row_sep
- line = CSV.generate_line(["1", "2"], row_sep: nil)
- assert_equal(<<-LINE.chomp, line)
-1,2
- LINE
- end
-
- def test_generate_line_shortcut
- line = ["1", "2", "3"].to_csv(col_sep: ";")
- assert_equal(<<-LINE, line)
-1;2;3
- LINE
- end
-
- def test_generate_lines
- lines = CSV.generate_lines([["foo", "bar"], [1, 2], [3, 4]])
- assert_equal(<<-LINES, lines)
-foo,bar
-1,2
-3,4
- LINES
- end
-
- def test_headers_detection
- headers = ["a", "b", "c"]
- CSV.open(@output.path, "w", headers: true) do |csv|
- csv << headers
- csv << ["1", "2", "3"]
- assert_equal(headers, csv.headers)
- end
- end
-
- def test_lineno
- CSV.open(@output.path, "w") do |csv|
- n_lines = 20
- n_lines.times do
- csv << ["a", "b", "c"]
- end
- assert_equal(n_lines, csv.lineno)
- end
- end
-
- def test_append_row
- CSV.open(@output.path, "wb") do |csv|
- csv <<
- CSV::Row.new([], ["1", "2", "3"]) <<
- CSV::Row.new([], ["a", "b", "c"])
- end
- assert_equal(<<-CSV, File.read(@output.path, mode: "rb"))
-1,2,3
-a,b,c
- CSV
- end
-
-
- if respond_to?(:ractor)
- ractor
- def test_append_row_in_ractor
- ractor = Ractor.new(@output.path) do |path|
- CSV.open(path, "wb") do |csv|
- csv <<
- CSV::Row.new([], ["1", "2", "3"]) <<
- CSV::Row.new([], ["a", "b", "c"])
- end
- end
- ractor.take
- assert_equal(<<-CSV, File.read(@output.path, mode: "rb"))
-1,2,3
-a,b,c
- CSV
- end
- end
-
- def test_append_hash
- CSV.open(@output.path, "wb", headers: true) do |csv|
- csv << [:a, :b, :c]
- csv << {a: 1, b: 2, c: 3}
- csv << {a: 4, b: 5, c: 6}
- end
- assert_equal(<<-CSV, File.read(@output.path, mode: "rb"))
-a,b,c
-1,2,3
-4,5,6
- CSV
- end
-
- def test_append_hash_headers_array
- CSV.open(@output.path, "wb", headers: [:b, :a, :c]) do |csv|
- csv << {a: 1, b: 2, c: 3}
- csv << {a: 4, b: 5, c: 6}
- end
- assert_equal(<<-CSV, File.read(@output.path, mode: "rb"))
-2,1,3
-5,4,6
- CSV
- end
-
- def test_append_hash_headers_string
- CSV.open(@output.path, "wb", headers: "b|a|c", col_sep: "|") do |csv|
- csv << {"a" => 1, "b" => 2, "c" => 3}
- csv << {"a" => 4, "b" => 5, "c" => 6}
- end
- assert_equal(<<-CSV, File.read(@output.path, mode: "rb"))
-2|1|3
-5|4|6
- CSV
- end
-
- def test_write_headers
- CSV.open(@output.path,
- "wb",
- headers: "b|a|c",
- write_headers: true,
- col_sep: "|" ) do |csv|
- csv << {"a" => 1, "b" => 2, "c" => 3}
- csv << {"a" => 4, "b" => 5, "c" => 6}
- end
- assert_equal(<<-CSV, File.read(@output.path, mode: "rb"))
-b|a|c
-2|1|3
-5|4|6
- CSV
- end
-
- def test_write_headers_empty
- CSV.open(@output.path,
- "wb",
- headers: "b|a|c",
- write_headers: true,
- col_sep: "|" ) do |csv|
- end
- assert_equal(<<-CSV, File.read(@output.path, mode: "rb"))
-b|a|c
- CSV
- end
-
- def test_options_not_modified
- options = {}.freeze
- CSV.generate(**options) {}
- CSV.generate_line([], **options)
- CSV.filter("", "", **options)
- CSV.instance("", **options)
- end
-end
diff --git a/test/csv/line_endings.gz b/test/csv/line_endings.gz
deleted file mode 100644
index 39e1729ee4..0000000000
--- a/test/csv/line_endings.gz
+++ /dev/null
Binary files differ
diff --git a/test/csv/parse/test_column_separator.rb b/test/csv/parse/test_column_separator.rb
deleted file mode 100644
index d6eaa7b6de..0000000000
--- a/test/csv/parse/test_column_separator.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# -*- coding: utf-8 -*-
-# frozen_string_literal: false
-
-require_relative "../helper"
-
-class TestCSVParseColumnSeparator < Test::Unit::TestCase
- extend DifferentOFS
-
- def test_comma
- assert_equal([["a", "b", nil, "d"]],
- CSV.parse("a,b,,d", col_sep: ","))
- end
-
- def test_space
- assert_equal([["a", "b", nil, "d"]],
- CSV.parse("a b d", col_sep: " "))
- end
-
- def test_tab
- assert_equal([["a", "b", nil, "d"]],
- CSV.parse("a\tb\t\td", col_sep: "\t"))
- end
-
- def test_multiple_characters_include_sub_separator
- assert_equal([["a b", nil, "d"]],
- CSV.parse("a b d", col_sep: " "))
- end
-
- def test_multiple_characters_leading_empty_fields
- data = <<-CSV
-<=><=>A<=>B<=>C
-1<=>2<=>3
- CSV
- assert_equal([
- [nil, nil, "A", "B", "C"],
- ["1", "2", "3"],
- ],
- CSV.parse(data, col_sep: "<=>"))
- end
-end
diff --git a/test/csv/parse/test_convert.rb b/test/csv/parse/test_convert.rb
deleted file mode 100644
index c9195c71d9..0000000000
--- a/test/csv/parse/test_convert.rb
+++ /dev/null
@@ -1,165 +0,0 @@
-# -*- coding: utf-8 -*-
-# frozen_string_literal: false
-
-require_relative "../helper"
-
-class TestCSVParseConvert < Test::Unit::TestCase
- extend DifferentOFS
-
- def setup
- super
- @data = "Numbers,:integer,1,:float,3.015"
- @parser = CSV.new(@data)
-
- @custom = lambda {|field| /\A:(\S.*?)\s*\Z/ =~ field ? $1.to_sym : field}
-
- @time = Time.utc(2018, 12, 30, 6, 41, 29)
- @windows_safe_time_data = @time.strftime("%a %b %d %H:%M:%S %Y")
-
- @preserving_converter = lambda do |field, info|
- f = field.encode(CSV::ConverterEncoding)
- return f if info.quoted?
- begin
- Integer(f, 10)
- rescue
- f
- end
- end
-
- @quoted_header_converter = lambda do |field, info|
- f = field.encode(CSV::ConverterEncoding)
- return f if info.quoted?
- f.to_sym
- end
- end
-
- def test_integer
- @parser.convert(:integer)
- assert_equal(["Numbers", ":integer", 1, ":float", "3.015"],
- @parser.shift)
- end
-
- def test_float
- @parser.convert(:float)
- assert_equal(["Numbers", ":integer", 1.0, ":float", 3.015],
- @parser.shift)
- end
-
- def test_float_integer
- @parser.convert(:float)
- @parser.convert(:integer)
- assert_equal(["Numbers", ":integer", 1.0, ":float", 3.015],
- @parser.shift)
- end
-
- def test_integer_float
- @parser.convert(:integer)
- @parser.convert(:float)
- assert_equal(["Numbers", ":integer", 1, ":float", 3.015],
- @parser.shift)
- end
-
- def test_numeric
- @parser.convert(:numeric)
- assert_equal(["Numbers", ":integer", 1, ":float", 3.015],
- @parser.shift)
- end
-
- def test_all
- @data << ",#{@windows_safe_time_data}"
- @parser = CSV.new(@data)
- @parser.convert(:all)
- assert_equal(["Numbers", ":integer", 1, ":float", 3.015, @time.to_datetime],
- @parser.shift)
- end
-
- def test_custom
- @parser.convert do |field|
- /\A:(\S.*?)\s*\Z/ =~ field ? $1.to_sym : field
- end
- assert_equal(["Numbers", :integer, "1", :float, "3.015"],
- @parser.shift)
- end
-
- def test_builtin_custom
- @parser.convert(:numeric)
- @parser.convert(&@custom)
- assert_equal(["Numbers", :integer, 1, :float, 3.015],
- @parser.shift)
- end
-
- def test_custom_field_info_line
- @parser.convert do |field, info|
- assert_equal(1, info.line)
- info.index == 4 ? Float(field).floor : field
- end
- assert_equal(["Numbers", ":integer", "1", ":float", 3],
- @parser.shift)
- end
-
- def test_custom_field_info_header
- headers = ["one", "two", "three", "four", "five"]
- @parser = CSV.new(@data, headers: headers)
- @parser.convert do |field, info|
- info.header == "three" ? Integer(field) * 100 : field
- end
- assert_equal(CSV::Row.new(headers,
- ["Numbers", ":integer", 100, ":float", "3.015"]),
- @parser.shift)
- end
-
- def test_custom_blank_field
- converter = lambda {|field| field.nil?}
- row = CSV.parse_line('nil,', converters: converter)
- assert_equal([false, true], row)
- end
-
- def test_nil_value
- assert_equal(["nil", "", "a"],
- CSV.parse_line(',"",a', nil_value: "nil"))
- end
-
- def test_empty_value
- assert_equal([nil, "empty", "a"],
- CSV.parse_line(',"",a', empty_value: "empty"))
- end
-
- def test_quoted_parse_line
- row = CSV.parse_line('1,"2",3', converters: @preserving_converter)
- assert_equal([1, "2", 3], row)
- end
-
- def test_quoted_parse
- expected = [["quoted", "unquoted"], ["109", 1], ["10A", 2]]
- rows = CSV.parse(<<~CSV, converters: @preserving_converter)
- "quoted",unquoted
- "109",1
- "10A",2
- CSV
- assert_equal(expected, rows)
- end
-
- def test_quoted_alternating_quote
- row = CSV.parse_line('"1",2,"3"', converters: @preserving_converter)
- assert_equal(['1', 2, '3'], row)
- end
-
- def test_quoted_parse_headers
- expected = [["quoted", :unquoted], ["109", "1"], ["10A", "2"]]
- table = CSV.parse(<<~CSV, headers: true, header_converters: @quoted_header_converter)
- "quoted",unquoted
- "109",1
- "10A",2
- CSV
- assert_equal(expected, table.to_a)
- end
-
- def test_quoted_parse_with_string_headers
- expected = [["quoted", :unquoted], %w[109 1], %w[10A 2]]
- table = CSV.parse(<<~CSV, headers: '"quoted",unquoted', header_converters: @quoted_header_converter)
- "109",1
- "10A",2
- CSV
- assert_equal(expected, table.to_a)
- end
-end
diff --git a/test/csv/parse/test_each.rb b/test/csv/parse/test_each.rb
deleted file mode 100644
index ce0b71d058..0000000000
--- a/test/csv/parse/test_each.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- coding: utf-8 -*-
-# frozen_string_literal: false
-
-require_relative "../helper"
-
-class TestCSVParseEach < Test::Unit::TestCase
- extend DifferentOFS
-
- def test_twice
- data = <<-CSV
-Ruby,2.6.0,script
- CSV
- csv = CSV.new(data)
- assert_equal([
- [["Ruby", "2.6.0", "script"]],
- [],
- ],
- [
- csv.to_a,
- csv.to_a,
- ])
- end
-end
diff --git a/test/csv/parse/test_general.rb b/test/csv/parse/test_general.rb
deleted file mode 100644
index a565ff2ef8..0000000000
--- a/test/csv/parse/test_general.rb
+++ /dev/null
@@ -1,348 +0,0 @@
-# -*- coding: utf-8 -*-
-# frozen_string_literal: false
-
-require "timeout"
-
-require_relative "../helper"
-
-#
-# Following tests are my interpretation of the
-# {CSV RCF}[https://www.ietf.org/rfc/rfc4180.txt]. I only deviate from that
-# document in one place (intentionally) and that is to make the default row
-# separator <tt>$/</tt>.
-#
-class TestCSVParseGeneral < Test::Unit::TestCase
- extend DifferentOFS
-
- BIG_DATA = "123456789\n" * 512
-
- def test_mastering_regex_example
- ex = %Q{Ten Thousand,10000, 2710 ,,"10,000","It's ""10 Grand"", baby",10K}
- assert_equal( [ "Ten Thousand", "10000", " 2710 ", nil, "10,000",
- "It's \"10 Grand\", baby", "10K" ],
- CSV.parse_line(ex) )
- end
-
- # Old Ruby 1.8 CSV library tests.
- def test_std_lib_csv
- [ ["\t", ["\t"]],
- ["foo,\"\"\"\"\"\",baz", ["foo", "\"\"", "baz"]],
- ["foo,\"\"\"bar\"\"\",baz", ["foo", "\"bar\"", "baz"]],
- ["\"\"\"\n\",\"\"\"\n\"", ["\"\n", "\"\n"]],
- ["foo,\"\r\n\",baz", ["foo", "\r\n", "baz"]],
- ["\"\"", [""]],
- ["foo,\"\"\"\",baz", ["foo", "\"", "baz"]],
- ["foo,\"\r.\n\",baz", ["foo", "\r.\n", "baz"]],
- ["foo,\"\r\",baz", ["foo", "\r", "baz"]],
- ["foo,\"\",baz", ["foo", "", "baz"]],
- ["\",\"", [","]],
- ["foo", ["foo"]],
- [",,", [nil, nil, nil]],
- [",", [nil, nil]],
- ["foo,\"\n\",baz", ["foo", "\n", "baz"]],
- ["foo,,baz", ["foo", nil, "baz"]],
- ["\"\"\"\r\",\"\"\"\r\"", ["\"\r", "\"\r"]],
- ["\",\",\",\"", [",", ","]],
- ["foo,bar,", ["foo", "bar", nil]],
- [",foo,bar", [nil, "foo", "bar"]],
- ["foo,bar", ["foo", "bar"]],
- [";", [";"]],
- ["\t,\t", ["\t", "\t"]],
- ["foo,\"\r\n\r\",baz", ["foo", "\r\n\r", "baz"]],
- ["foo,\"\r\n\n\",baz", ["foo", "\r\n\n", "baz"]],
- ["foo,\"foo,bar\",baz", ["foo", "foo,bar", "baz"]],
- [";,;", [";", ";"]] ].each do |csv_test|
- assert_equal(csv_test.last, CSV.parse_line(csv_test.first))
- end
-
- [ ["foo,\"\"\"\"\"\",baz", ["foo", "\"\"", "baz"]],
- ["foo,\"\"\"bar\"\"\",baz", ["foo", "\"bar\"", "baz"]],
- ["foo,\"\r\n\",baz", ["foo", "\r\n", "baz"]],
- ["\"\"", [""]],
- ["foo,\"\"\"\",baz", ["foo", "\"", "baz"]],
- ["foo,\"\r.\n\",baz", ["foo", "\r.\n", "baz"]],
- ["foo,\"\r\",baz", ["foo", "\r", "baz"]],
- ["foo,\"\",baz", ["foo", "", "baz"]],
- ["foo", ["foo"]],
- [",,", [nil, nil, nil]],
- [",", [nil, nil]],
- ["foo,\"\n\",baz", ["foo", "\n", "baz"]],
- ["foo,,baz", ["foo", nil, "baz"]],
- ["foo,bar", ["foo", "bar"]],
- ["foo,\"\r\n\n\",baz", ["foo", "\r\n\n", "baz"]],
- ["foo,\"foo,bar\",baz", ["foo", "foo,bar", "baz"]] ].each do |csv_test|
- assert_equal(csv_test.last, CSV.parse_line(csv_test.first))
- end
- end
-
- # From: [ruby-core:6496]
- def test_aras_edge_cases
- [ [%Q{a,b}, ["a", "b"]],
- [%Q{a,"""b"""}, ["a", "\"b\""]],
- [%Q{a,"""b"}, ["a", "\"b"]],
- [%Q{a,"b"""}, ["a", "b\""]],
- [%Q{a,"\nb"""}, ["a", "\nb\""]],
- [%Q{a,"""\nb"}, ["a", "\"\nb"]],
- [%Q{a,"""\nb\n"""}, ["a", "\"\nb\n\""]],
- [%Q{a,"""\nb\n""",\nc}, ["a", "\"\nb\n\"", nil]],
- [%Q{a,,,}, ["a", nil, nil, nil]],
- [%Q{,}, [nil, nil]],
- [%Q{"",""}, ["", ""]],
- [%Q{""""}, ["\""]],
- [%Q{"""",""}, ["\"",""]],
- [%Q{,""}, [nil,""]],
- [%Q{,"\r"}, [nil,"\r"]],
- [%Q{"\r\n,"}, ["\r\n,"]],
- [%Q{"\r\n,",}, ["\r\n,", nil]] ].each do |edge_case|
- assert_equal(edge_case.last, CSV.parse_line(edge_case.first))
- end
- end
-
- def test_james_edge_cases
- # A read at eof? should return nil.
- assert_equal(nil, CSV.parse_line(""))
- #
- # With Ruby 1.8 CSV it's impossible to tell an empty line from a line
- # containing a single +nil+ field. The old CSV library returns
- # <tt>[nil]</tt> in these cases, but <tt>Array.new</tt> makes more sense to
- # me.
- #
- assert_equal(Array.new, CSV.parse_line("\n1,2,3\n"))
- end
-
- def test_rob_edge_cases
- [ [%Q{"a\nb"}, ["a\nb"]],
- [%Q{"\n\n\n"}, ["\n\n\n"]],
- [%Q{a,"b\n\nc"}, ['a', "b\n\nc"]],
- [%Q{,"\r\n"}, [nil,"\r\n"]],
- [%Q{,"\r\n."}, [nil,"\r\n."]],
- [%Q{"a\na","one newline"}, ["a\na", 'one newline']],
- [%Q{"a\n\na","two newlines"}, ["a\n\na", 'two newlines']],
- [%Q{"a\r\na","one CRLF"}, ["a\r\na", 'one CRLF']],
- [%Q{"a\r\n\r\na","two CRLFs"}, ["a\r\n\r\na", 'two CRLFs']],
- [%Q{with blank,"start\n\nfinish"\n}, ['with blank', "start\n\nfinish"]],
- ].each do |edge_case|
- assert_equal(edge_case.last, CSV.parse_line(edge_case.first))
- end
- end
-
- def test_non_regex_edge_cases
- # An early version of the non-regex parser fails this test
- [ [ "foo,\"foo,bar,baz,foo\",\"foo\"",
- ["foo", "foo,bar,baz,foo", "foo"] ] ].each do |edge_case|
- assert_equal(edge_case.last, CSV.parse_line(edge_case.first))
- end
-
- assert_raise(CSV::MalformedCSVError) do
- CSV.parse_line("1,\"23\"4\"5\", 6")
- end
- end
-
- def test_malformed_csv_cr_first_line
- error = assert_raise(CSV::MalformedCSVError) do
- CSV.parse_line("1,2\r,3", row_sep: "\n")
- end
- assert_equal("Unquoted fields do not allow new line <\"\\r\"> in line 1.",
- error.message)
- end
-
- def test_malformed_csv_cr_middle_line
- csv = <<-CSV
-line,1,abc
-line,2,"def\nghi"
-
-line,4,some\rjunk
-line,5,jkl
- CSV
-
- error = assert_raise(CSV::MalformedCSVError) do
- CSV.parse(csv)
- end
- assert_equal("Unquoted fields do not allow new line <\"\\r\"> in line 4.",
- error.message)
- end
-
- def test_malformed_csv_unclosed_quote
- error = assert_raise(CSV::MalformedCSVError) do
- CSV.parse_line('1,2,"3...')
- end
- assert_equal("Unclosed quoted field in line 1.",
- error.message)
- end
-
- def test_malformed_csv_illegal_quote_middle_line
- csv = <<-CSV
-line,1,abc
-line,2,"def\nghi"
-
-line,4,8'10"
-line,5,jkl
- CSV
-
- error = assert_raise(CSV::MalformedCSVError) do
- CSV.parse(csv)
- end
- assert_equal("Illegal quoting in line 4.",
- error.message)
- end
-
- def test_the_parse_fails_fast_when_it_can_for_unquoted_fields
- assert_parse_errors_out('valid,fields,bad start"' + BIG_DATA)
- end
-
- def test_the_parse_fails_fast_when_it_can_for_unescaped_quotes
- assert_parse_errors_out('valid,fields,"bad start"unescaped' + BIG_DATA)
- end
-
- def test_field_size_limit_controls_lookahead
- assert_parse_errors_out( 'valid,fields,"' + BIG_DATA + '"',
- field_size_limit: 2048 )
- end
-
- def test_field_size_limit_max_allowed
- column = "abcde"
- assert_equal([[column]],
- CSV.parse("\"#{column}\"",
- field_size_limit: column.size + 1))
- end
-
- def test_field_size_limit_quote_simple
- column = "abcde"
- assert_parse_errors_out("\"#{column}\"",
- field_size_limit: column.size)
- end
-
- def test_field_size_limit_no_quote_implicitly
- column = "abcde"
- assert_parse_errors_out("#{column}",
- field_size_limit: column.size)
- end
-
- def test_field_size_limit_no_quote_explicitly
- column = "abcde"
- assert_parse_errors_out("#{column}",
- field_size_limit: column.size,
- quote_char: nil)
- end
-
- def test_field_size_limit_in_extended_column_not_exceeding
- data = <<~DATA
- "a","b"
- "
- 2
- ",""
- DATA
- assert_nothing_raised(CSV::MalformedCSVError) do
- CSV.parse(data, field_size_limit: 4)
- end
- end
-
- def test_field_size_limit_in_extended_column_exceeding
- data = <<~DATA
- "a","b"
- "
- 2345
- ",""
- DATA
- assert_parse_errors_out(data, field_size_limit: 5)
- end
-
- def test_max_field_size_controls_lookahead
- assert_parse_errors_out( 'valid,fields,"' + BIG_DATA + '"',
- max_field_size: 2048 )
- end
-
- def test_max_field_size_max_allowed
- column = "abcde"
- assert_equal([[column]],
- CSV.parse("\"#{column}\"",
- max_field_size: column.size))
- end
-
- def test_max_field_size_quote_simple
- column = "abcde"
- assert_parse_errors_out("\"#{column}\"",
- max_field_size: column.size - 1)
- end
-
- def test_max_field_size_no_quote_implicitly
- column = "abcde"
- assert_parse_errors_out("#{column}",
- max_field_size: column.size - 1)
- end
-
- def test_max_field_size_no_quote_explicitly
- column = "abcde"
- assert_parse_errors_out("#{column}",
- max_field_size: column.size - 1,
- quote_char: nil)
- end
-
- def test_max_field_size_in_extended_column_not_exceeding
- data = <<~DATA
- "a","b"
- "
- 2
- ",""
- DATA
- assert_nothing_raised(CSV::MalformedCSVError) do
- CSV.parse(data, max_field_size: 3)
- end
- end
-
- def test_max_field_size_in_extended_column_exceeding
- data = <<~DATA
- "a","b"
- "
- 2345
- ",""
- DATA
- assert_parse_errors_out(data, max_field_size: 4)
- end
-
- def test_row_sep_auto_cr
- assert_equal([["a"]], CSV.parse("a\r"))
- end
-
- def test_row_sep_auto_lf
- assert_equal([["a"]], CSV.parse("a\n"))
- end
-
- def test_row_sep_auto_cr_lf
- assert_equal([["a"]], CSV.parse("a\r\n"))
- end
-
- def test_seeked_string_io
- input_with_bom = StringIO.new("\ufeffã‚,ã„,ã†\r\na,b,c\r\n")
- input_with_bom.read(3)
- assert_equal([
- ["ã‚", "ã„", "ã†"],
- ["a", "b", "c"],
- ],
- CSV.new(input_with_bom).each.to_a)
- end
-
- private
-
- {
- "YJIT"=>1, # for --yjit-call-threshold=1
- "MJIT"=>5, "RJIT"=>5, # for --jit-wait
- }.any? do |jit, timeout|
- if (RubyVM.const_defined?(jit) and
- jit = RubyVM.const_get(jit) and
- jit.respond_to?(:enabled?) and
- jit.enabled?)
- PARSE_ERROR_TIMEOUT = timeout
- end
- end
- PARSE_ERROR_TIMEOUT ||= 0.2
-
- def assert_parse_errors_out(data, timeout: PARSE_ERROR_TIMEOUT, **options)
- assert_raise(CSV::MalformedCSVError) do
- Timeout.timeout(timeout) do
- CSV.parse(data, **options)
- fail("Parse didn't error out")
- end
- end
- end
-end
diff --git a/test/csv/parse/test_header.rb b/test/csv/parse/test_header.rb
deleted file mode 100644
index e8c3786d68..0000000000
--- a/test/csv/parse/test_header.rb
+++ /dev/null
@@ -1,342 +0,0 @@
-# -*- coding: utf-8 -*-
-# frozen_string_literal: false
-
-require_relative "../helper"
-
-class TestCSVHeaders < Test::Unit::TestCase
- extend DifferentOFS
-
- def setup
- super
- @data = <<-CSV
-first,second,third
-A,B,C
-1,2,3
- CSV
- end
-
- def test_first_row
- [:first_row, true].each do |setting| # two names for the same setting
- # activate headers
- csv = nil
- assert_nothing_raised(Exception) do
- csv = CSV.parse(@data, headers: setting)
- end
-
- # first data row - skipping headers
- row = csv[0]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([%w{first A}, %w{second B}, %w{third C}], row.to_a)
-
- # second data row
- row = csv[1]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([%w{first 1}, %w{second 2}, %w{third 3}], row.to_a)
-
- # empty
- assert_nil(csv[2])
- end
- end
-
- def test_array_of_headers
- # activate headers
- csv = nil
- assert_nothing_raised(Exception) do
- csv = CSV.parse(@data, headers: [:my, :new, :headers])
- end
-
- # first data row - skipping headers
- row = csv[0]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal( [[:my, "first"], [:new, "second"], [:headers, "third"]],
- row.to_a )
-
- # second data row
- row = csv[1]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([[:my, "A"], [:new, "B"], [:headers, "C"]], row.to_a)
-
- # third data row
- row = csv[2]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([[:my, "1"], [:new, "2"], [:headers, "3"]], row.to_a)
-
- # empty
- assert_nil(csv[3])
-
- # with return and convert
- assert_nothing_raised(Exception) do
- csv = CSV.parse( @data, headers: [:my, :new, :headers],
- return_headers: true,
- header_converters: lambda { |h| h.to_s } )
- end
- row = csv[0]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([["my", :my], ["new", :new], ["headers", :headers]], row.to_a)
- assert_predicate(row, :header_row?)
- assert_not_predicate(row, :field_row?)
- end
-
- def test_csv_header_string
- # activate headers
- csv = nil
- assert_nothing_raised(Exception) do
- csv = CSV.parse(@data, headers: "my,new,headers")
- end
-
- # first data row - skipping headers
- row = csv[0]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([%w{my first}, %w{new second}, %w{headers third}], row.to_a)
-
- # second data row
- row = csv[1]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([%w{my A}, %w{new B}, %w{headers C}], row.to_a)
-
- # third data row
- row = csv[2]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([%w{my 1}, %w{new 2}, %w{headers 3}], row.to_a)
-
- # empty
- assert_nil(csv[3])
-
- # with return and convert
- assert_nothing_raised(Exception) do
- csv = CSV.parse( @data, headers: "my,new,headers",
- return_headers: true,
- header_converters: :symbol )
- end
- row = csv[0]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([[:my, "my"], [:new, "new"], [:headers, "headers"]], row.to_a)
- assert_predicate(row, :header_row?)
- assert_not_predicate(row, :field_row?)
- end
-
- def test_csv_header_string_inherits_separators
- # parse with custom col_sep
- csv = nil
- assert_nothing_raised(Exception) do
- csv = CSV.parse( @data.tr(",", "|"), col_sep: "|",
- headers: "my|new|headers" )
- end
-
- # verify headers were recognized
- row = csv[0]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([%w{my first}, %w{new second}, %w{headers third}], row.to_a)
- end
-
- def test_return_headers
- # activate headers and request they are returned
- csv = nil
- assert_nothing_raised(Exception) do
- csv = CSV.parse(@data, headers: true, return_headers: true)
- end
-
- # header row
- row = csv[0]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal( [%w{first first}, %w{second second}, %w{third third}],
- row.to_a )
- assert_predicate(row, :header_row?)
- assert_not_predicate(row, :field_row?)
-
- # first data row - skipping headers
- row = csv[1]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([%w{first A}, %w{second B}, %w{third C}], row.to_a)
- assert_not_predicate(row, :header_row?)
- assert_predicate(row, :field_row?)
-
- # second data row
- row = csv[2]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([%w{first 1}, %w{second 2}, %w{third 3}], row.to_a)
- assert_not_predicate(row, :header_row?)
- assert_predicate(row, :field_row?)
-
- # empty
- assert_nil(csv[3])
- end
-
- def test_converters
- # create test data where headers and fields look alike
- data = <<-CSV
-1,2,3
-1,2,3
- CSV
-
- # normal converters do not affect headers
- csv = CSV.parse( data, headers: true,
- return_headers: true,
- converters: :numeric )
- assert_equal([%w{1 1}, %w{2 2}, %w{3 3}], csv[0].to_a)
- assert_equal([["1", 1], ["2", 2], ["3", 3]], csv[1].to_a)
- assert_nil(csv[2])
-
- # header converters do affect headers (only)
- assert_nothing_raised(Exception) do
- csv = CSV.parse( data, headers: true,
- return_headers: true,
- converters: :numeric,
- header_converters: :symbol )
- end
- assert_equal([[:"1", "1"], [:"2", "2"], [:"3", "3"]], csv[0].to_a)
- assert_equal([[:"1", 1], [:"2", 2], [:"3", 3]], csv[1].to_a)
- assert_nil(csv[2])
- end
-
- def test_builtin_downcase_converter
- csv = CSV.parse( "One,TWO Three", headers: true,
- return_headers: true,
- header_converters: :downcase )
- assert_equal(%w{one two\ three}, csv.headers)
- end
-
- def test_builtin_symbol_converter
- # Note that the trailing space is intentional
- csv = CSV.parse( "One,TWO Three ", headers: true,
- return_headers: true,
- header_converters: :symbol )
- assert_equal([:one, :two_three], csv.headers)
- end
-
- def test_builtin_symbol_raw_converter
- csv = CSV.parse( "a b,c d", headers: true,
- return_headers: true,
- header_converters: :symbol_raw )
- assert_equal([:"a b", :"c d"], csv.headers)
- end
-
- def test_builtin_symbol_converter_with_punctuation
- csv = CSV.parse( "One, Two & Three ($)", headers: true,
- return_headers: true,
- header_converters: :symbol )
- assert_equal([:one, :two_three], csv.headers)
- end
-
- def test_builtin_converters_with_blank_header
- csv = CSV.parse( "one,,three", headers: true,
- return_headers: true,
- header_converters: [:downcase, :symbol, :symbol_raw] )
- assert_equal([:one, nil, :three], csv.headers)
- end
-
- def test_custom_converter
- converter = lambda { |header| header.tr(" ", "_") }
- csv = CSV.parse( "One,TWO Three",
- headers: true,
- return_headers: true,
- header_converters: converter )
- assert_equal(%w{One TWO_Three}, csv.headers)
- end
-
- def test_table_support
- csv = nil
- assert_nothing_raised(Exception) do
- csv = CSV.parse(@data, headers: true)
- end
-
- assert_instance_of(CSV::Table, csv)
- end
-
- def test_skip_blanks
- @data = <<-CSV
-
-
-A,B,C
-
-1,2,3
-
-
-
- CSV
-
- expected = [%w[1 2 3]]
- CSV.parse(@data, headers: true, skip_blanks: true) do |row|
- assert_equal(expected.shift, row.fields)
- end
-
- expected = [%w[A B C], %w[1 2 3]]
- CSV.parse( @data,
- headers: true,
- return_headers: true,
- skip_blanks: true ) do |row|
- assert_equal(expected.shift, row.fields)
- end
- end
-
- def test_headers_reader
- # no headers
- assert_nil(CSV.new(@data).headers)
-
- # headers
- csv = CSV.new(@data, headers: true)
- assert_equal(true, csv.headers) # before headers are read
- csv.shift # set headers
- assert_equal(%w[first second third], csv.headers) # after headers are read
- end
-
- def test_blank_row
- @data += "\n#{@data}" # add a blank row
-
- # ensure that everything returned is a Row object
- CSV.parse(@data, headers: true) do |row|
- assert_instance_of(CSV::Row, row)
- end
- end
-
- def test_nil_row_header
- @data = <<-CSV
-A
-
-1
- CSV
-
- csv = CSV.parse(@data, headers: true)
-
- # ensure nil row creates Row object with headers
- row = csv[0]
- assert_equal([["A"], [nil]],
- [row.headers, row.fields])
- end
-
- def test_parse_empty
- assert_equal(CSV::Table.new([]),
- CSV.parse("", headers: true))
- end
-
- def test_parse_empty_line
- assert_equal(CSV::Table.new([]),
- CSV.parse("\n", headers: true))
- end
-
- def test_specified_empty
- assert_equal(CSV::Table.new([],
- headers: ["header1"]),
- CSV.parse("", headers: ["header1"]))
- end
-
- def test_specified_empty_line
- assert_equal(CSV::Table.new([CSV::Row.new(["header1"], [])],
- headers: ["header1"]),
- CSV.parse("\n", headers: ["header1"]))
- end
-end
diff --git a/test/csv/parse/test_inputs_scanner.rb b/test/csv/parse/test_inputs_scanner.rb
deleted file mode 100644
index 81327c187f..0000000000
--- a/test/csv/parse/test_inputs_scanner.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-require_relative "../helper"
-
-class TestCSVParseInputsScanner < Test::Unit::TestCase
- include CSVHelper
-
- def test_scan_keep_over_chunks_nested_back
- input = CSV::Parser::UnoptimizedStringIO.new("abcdefghijklmnl")
- scanner = CSV::Parser::InputsScanner.new([input],
- Encoding::UTF_8,
- nil,
- chunk_size: 2)
- scanner.keep_start
- assert_equal("abc", scanner.scan_all(/[a-c]+/))
- scanner.keep_start
- assert_equal("def", scanner.scan_all(/[d-f]+/))
- scanner.keep_back
- scanner.keep_back
- assert_equal("abcdefg", scanner.scan_all(/[a-g]+/))
- end
-
- def test_scan_keep_over_chunks_nested_drop_back
- input = CSV::Parser::UnoptimizedStringIO.new("abcdefghijklmnl")
- scanner = CSV::Parser::InputsScanner.new([input],
- Encoding::UTF_8,
- nil,
- chunk_size: 3)
- scanner.keep_start
- assert_equal("ab", scanner.scan(/../))
- scanner.keep_start
- assert_equal("c", scanner.scan(/./))
- assert_equal("d", scanner.scan(/./))
- scanner.keep_drop
- scanner.keep_back
- assert_equal("abcdefg", scanner.scan_all(/[a-g]+/))
- end
-
- def test_each_line_keep_over_chunks_multibyte
- input = CSV::Parser::UnoptimizedStringIO.new("ab\n\u{3000}a\n")
- scanner = CSV::Parser::InputsScanner.new([input],
- Encoding::UTF_8,
- nil,
- chunk_size: 1)
- each_line = scanner.each_line("\n")
- assert_equal("ab\n", each_line.next)
- scanner.keep_start
- assert_equal("\u{3000}a\n", each_line.next)
- scanner.keep_back
- assert_equal("\u{3000}a\n", scanner.scan_all(/[^,]+/))
- end
-
- def test_each_line_keep_over_chunks_fit_chunk_size
- input = CSV::Parser::UnoptimizedStringIO.new("\na")
- scanner = CSV::Parser::InputsScanner.new([input],
- Encoding::UTF_8,
- nil,
- chunk_size: 1)
- each_line = scanner.each_line("\n")
- assert_equal("\n", each_line.next)
- scanner.keep_start
- assert_equal("a", each_line.next)
- scanner.keep_back
- end
-end
diff --git a/test/csv/parse/test_invalid.rb b/test/csv/parse/test_invalid.rb
deleted file mode 100644
index ddb59e2b9a..0000000000
--- a/test/csv/parse/test_invalid.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-# -*- coding: utf-8 -*-
-# frozen_string_literal: false
-
-require_relative "../helper"
-
-class TestCSVParseInvalid < Test::Unit::TestCase
- def test_no_column_mixed_new_lines
- error = assert_raise(CSV::MalformedCSVError) do
- CSV.parse("\n" +
- "\r")
- end
- assert_equal("New line must be <\"\\n\"> not <\"\\r\"> in line 2.",
- error.message)
- end
-
- def test_ignore_invalid_line
- csv = CSV.new(<<-CSV, headers: true, return_headers: true)
-head1,head2,head3
-aaa,bbb,ccc
-ddd,ee"e.fff
-ggg,hhh,iii
- CSV
- headers = ["head1", "head2", "head3"]
- assert_equal(CSV::Row.new(headers, headers),
- csv.shift)
- assert_equal(CSV::Row.new(headers, ["aaa", "bbb", "ccc"]),
- csv.shift)
- assert_equal(false, csv.eof?)
- error = assert_raise(CSV::MalformedCSVError) do
- csv.shift
- end
- assert_equal("Illegal quoting in line 3.",
- error.message)
- assert_equal(false, csv.eof?)
- assert_equal(CSV::Row.new(headers, ["ggg", "hhh", "iii"]),
- csv.shift)
- assert_equal(true, csv.eof?)
- end
-
- def test_ignore_invalid_line_cr_lf
- data = <<-CSV
-"1","OK"\r
-"2",""NOT" OK"\r
-"3","OK"\r
-CSV
- csv = CSV.new(data)
-
- assert_equal(['1', 'OK'], csv.shift)
- assert_raise(CSV::MalformedCSVError) { csv.shift }
- assert_equal(['3', 'OK'], csv.shift)
- end
-end
diff --git a/test/csv/parse/test_liberal_parsing.rb b/test/csv/parse/test_liberal_parsing.rb
deleted file mode 100644
index 5796d10828..0000000000
--- a/test/csv/parse/test_liberal_parsing.rb
+++ /dev/null
@@ -1,171 +0,0 @@
-# -*- coding: utf-8 -*-
-# frozen_string_literal: false
-
-require_relative "../helper"
-
-class TestCSVParseLiberalParsing < Test::Unit::TestCase
- extend DifferentOFS
-
- def test_middle_quote_start
- input = '"Johnson, Dwayne",Dwayne "The Rock" Johnson'
- error = assert_raise(CSV::MalformedCSVError) do
- CSV.parse_line(input)
- end
- assert_equal("Illegal quoting in line 1.",
- error.message)
- assert_equal(["Johnson, Dwayne", 'Dwayne "The Rock" Johnson'],
- CSV.parse_line(input, liberal_parsing: true))
- end
-
- def test_middle_quote_end
- input = '"quoted" field'
- error = assert_raise(CSV::MalformedCSVError) do
- CSV.parse_line(input)
- end
- assert_equal("Any value after quoted field isn't allowed in line 1.",
- error.message)
- assert_equal(['"quoted" field'],
- CSV.parse_line(input, liberal_parsing: true))
- end
-
- def test_endline_after_quoted_field_end
- csv = CSV.new("A\r\n\"B\"\nC\r\n", liberal_parsing: true)
- assert_equal(["A"], csv.gets)
- error = assert_raise(CSV::MalformedCSVError) do
- csv.gets
- end
- assert_equal('Illegal end-of-line sequence outside of a quoted field <"\n"> in line 2.',
- error.message)
- assert_equal(["C"], csv.gets)
- end
-
- def test_quote_after_column_separator
- error = assert_raise(CSV::MalformedCSVError) do
- CSV.parse_line('is,this "three," or four,fields', liberal_parsing: true)
- end
- assert_equal("Unclosed quoted field in line 1.",
- error.message)
- end
-
- def test_quote_before_column_separator
- assert_equal(["is", 'this "three', ' or four"', "fields"],
- CSV.parse_line('is,this "three, or four",fields',
- liberal_parsing: true))
- end
-
- def test_backslash_quote
- assert_equal([
- "1",
- "\"Hamlet says, \\\"Seems",
- "\\\" madam! Nay it is; I know not \\\"seems.\\\"\"",
- ],
- CSV.parse_line('1,' +
- '"Hamlet says, \"Seems,' +
- '\" madam! Nay it is; I know not \"seems.\""',
- liberal_parsing: true))
- end
-
- def test_space_quote
- input = <<~CSV
- Los Angeles, 34°03'N, 118°15'W
- New York City, 40°42'46"N, 74°00'21"W
- Paris, 48°51'24"N, 2°21'03"E
- CSV
- assert_equal(
- [
- ["Los Angeles", " 34°03'N", " 118°15'W"],
- ["New York City", " 40°42'46\"N", " 74°00'21\"W"],
- ["Paris", " 48°51'24\"N", " 2°21'03\"E"],
- ],
- CSV.parse(input, liberal_parsing: true))
- end
-
- def test_double_quote_outside_quote
- data = %Q{a,""b""}
- error = assert_raise(CSV::MalformedCSVError) do
- CSV.parse(data)
- end
- assert_equal("Any value after quoted field isn't allowed in line 1.",
- error.message)
- assert_equal([
- [["a", %Q{""b""}]],
- [["a", %Q{"b"}]],
- ],
- [
- CSV.parse(data, liberal_parsing: true),
- CSV.parse(data,
- liberal_parsing: {
- double_quote_outside_quote: true,
- }),
- ])
- end
-
- class TestBackslashQuote < Test::Unit::TestCase
- extend ::DifferentOFS
-
- def test_double_quote_outside_quote
- data = %Q{a,""b""}
- assert_equal([
- [["a", %Q{""b""}]],
- [["a", %Q{"b"}]],
- ],
- [
- CSV.parse(data,
- liberal_parsing: {
- backslash_quote: true
- }),
- CSV.parse(data,
- liberal_parsing: {
- backslash_quote: true,
- double_quote_outside_quote: true
- }),
- ])
- end
-
- def test_unquoted_value
- data = %q{\"\"a\"\"}
- assert_equal([
- [[%q{\"\"a\"\"}]],
- [[%q{""a""}]],
- ],
- [
- CSV.parse(data, liberal_parsing: true),
- CSV.parse(data,
- liberal_parsing: {
- backslash_quote: true
- }),
- ])
- end
-
- def test_unquoted_value_multiple_characters_col_sep
- data = %q{a<\\"b<=>x}
- assert_equal([[%Q{a<"b}, "x"]],
- CSV.parse(data,
- col_sep: "<=>",
- liberal_parsing: {
- backslash_quote: true
- }))
- end
-
- def test_quoted_value
- data = %q{"\"\"a\"\""}
- assert_equal([
- [[%q{"\"\"a\"\""}]],
- [[%q{""a""}]],
- [[%q{""a""}]],
- ],
- [
- CSV.parse(data, liberal_parsing: true),
- CSV.parse(data,
- liberal_parsing: {
- backslash_quote: true
- }),
- CSV.parse(data,
- liberal_parsing: {
- backslash_quote: true,
- double_quote_outside_quote: true
- }),
- ])
- end
- end
-end
diff --git a/test/csv/parse/test_quote_char_nil.rb b/test/csv/parse/test_quote_char_nil.rb
deleted file mode 100644
index fc3b646759..0000000000
--- a/test/csv/parse/test_quote_char_nil.rb
+++ /dev/null
@@ -1,93 +0,0 @@
-# -*- coding: utf-8 -*-
-# frozen_string_literal: false
-
-require_relative "../helper"
-
-class TestCSVParseQuoteCharNil < Test::Unit::TestCase
- extend DifferentOFS
-
- def test_full
- assert_equal(["a", "b"], CSV.parse_line(%Q{a,b}, quote_char: nil))
- end
-
- def test_end_with_nil
- assert_equal(["a", nil, nil, nil], CSV.parse_line(%Q{a,,,}, quote_char: nil))
- end
-
- def test_nil_nil
- assert_equal([nil, nil], CSV.parse_line(%Q{,}, quote_char: nil))
- end
-
- def test_unquoted_value_multiple_characters_col_sep
- data = %q{a<b<=>x}
- assert_equal([[%Q{a<b}, "x"]], CSV.parse(data, col_sep: "<=>", quote_char: nil))
- end
-
- def test_csv_header_string
- data = <<~DATA
- first,second,third
- A,B,C
- 1,2,3
- DATA
- assert_equal(
- CSV::Table.new([
- CSV::Row.new(["my", "new", "headers"], ["first", "second", "third"]),
- CSV::Row.new(["my", "new", "headers"], ["A", "B", "C"]),
- CSV::Row.new(["my", "new", "headers"], ["1", "2", "3"])
- ]),
- CSV.parse(data, headers: "my,new,headers", quote_char: nil)
- )
- end
-
- def test_comma
- assert_equal([["a", "b", nil, "d"]],
- CSV.parse("a,b,,d", col_sep: ",", quote_char: nil))
- end
-
- def test_space
- assert_equal([["a", "b", nil, "d"]],
- CSV.parse("a b d", col_sep: " ", quote_char: nil))
- end
-
- def encode_array(array, encoding)
- array.collect do |element|
- element ? element.encode(encoding) : element
- end
- end
-
- def test_space_no_ascii
- encoding = Encoding::UTF_16LE
- assert_equal([encode_array(["a", "b", nil, "d"], encoding)],
- CSV.parse("a b d".encode(encoding),
- col_sep: " ".encode(encoding),
- quote_char: nil))
- end
-
- def test_multiple_space
- assert_equal([["a b", nil, "d"]],
- CSV.parse("a b d", col_sep: " ", quote_char: nil))
- end
-
- def test_multiple_characters_leading_empty_fields
- data = <<-CSV
-<=><=>A<=>B<=>C
-1<=>2<=>3
- CSV
- assert_equal([
- [nil, nil, "A", "B", "C"],
- ["1", "2", "3"],
- ],
- CSV.parse(data, col_sep: "<=>", quote_char: nil))
- end
-
- def test_line
- lines = [
- "abc,def\n",
- ]
- csv = CSV.new(lines.join(""), quote_char: nil)
- lines.each do |line|
- csv.shift
- assert_equal(line, csv.line)
- end
- end
-end
diff --git a/test/csv/parse/test_read.rb b/test/csv/parse/test_read.rb
deleted file mode 100644
index ba6fe985a9..0000000000
--- a/test/csv/parse/test_read.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# -*- coding: utf-8 -*-
-# frozen_string_literal: false
-
-require_relative "../helper"
-
-class TestCSVParseRead < Test::Unit::TestCase
- extend DifferentOFS
-
- def test_shift
- data = <<-CSV
-1
-2
-3
- CSV
- csv = CSV.new(data)
- assert_equal([
- ["1"],
- [["2"], ["3"]],
- nil,
- ],
- [
- csv.shift,
- csv.read,
- csv.shift,
- ])
- end
-end
diff --git a/test/csv/parse/test_rewind.rb b/test/csv/parse/test_rewind.rb
deleted file mode 100644
index 0aa403b756..0000000000
--- a/test/csv/parse/test_rewind.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# -*- coding: utf-8 -*-
-# frozen_string_literal: false
-
-require_relative "../helper"
-
-class TestCSVParseRewind < Test::Unit::TestCase
- extend DifferentOFS
-
- def parse(data, **options)
- csv = CSV.new(data, **options)
- records = csv.to_a
- csv.rewind
- [records, csv.to_a]
- end
-
- def test_default
- data = <<-CSV
-Ruby,2.6.0,script
- CSV
- assert_equal([
- [["Ruby", "2.6.0", "script"]],
- [["Ruby", "2.6.0", "script"]],
- ],
- parse(data))
- end
-
- def test_have_headers
- data = <<-CSV
-Language,Version,Type
-Ruby,2.6.0,script
- CSV
- assert_equal([
- [CSV::Row.new(["Language", "Version", "Type"],
- ["Ruby", "2.6.0", "script"])],
- [CSV::Row.new(["Language", "Version", "Type"],
- ["Ruby", "2.6.0", "script"])],
- ],
- parse(data, headers: true))
- end
-end
diff --git a/test/csv/parse/test_row_separator.rb b/test/csv/parse/test_row_separator.rb
deleted file mode 100644
index 5fd8e75152..0000000000
--- a/test/csv/parse/test_row_separator.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
-# frozen_string_literal: false
-
-require_relative "../helper"
-
-class TestCSVParseRowSeparator < Test::Unit::TestCase
- extend DifferentOFS
- include CSVHelper
-
- def test_multiple_characters
- with_chunk_size("1") do
- assert_equal([["a"], ["b"]],
- CSV.parse("a\r\nb\r\n", row_sep: "\r\n"))
- end
- end
-end
diff --git a/test/csv/parse/test_skip_lines.rb b/test/csv/parse/test_skip_lines.rb
deleted file mode 100644
index d231cc65c3..0000000000
--- a/test/csv/parse/test_skip_lines.rb
+++ /dev/null
@@ -1,118 +0,0 @@
-# frozen_string_literal: false
-
-require_relative "../helper"
-
-class TestCSVParseSkipLines < Test::Unit::TestCase
- extend DifferentOFS
- include CSVHelper
-
- def test_default
- csv = CSV.new("a,b,c\n")
- assert_nil(csv.skip_lines)
- end
-
- def test_regexp
- csv = <<-CSV
-1
-#2
- #3
-4
- CSV
- assert_equal([
- ["1"],
- ["4"],
- ],
- CSV.parse(csv, :skip_lines => /\A\s*#/))
- end
-
- def test_regexp_quoted
- csv = <<-CSV
-1
-#2
-"#3"
-4
- CSV
- assert_equal([
- ["1"],
- ["#3"],
- ["4"],
- ],
- CSV.parse(csv, :skip_lines => /\A\s*#/))
- end
-
- def test_string
- csv = <<-CSV
-1
-.2
-3.
-4
- CSV
- assert_equal([
- ["1"],
- ["4"],
- ],
- CSV.parse(csv, :skip_lines => "."))
- end
-
- class RegexStub
- end
-
- def test_not_matchable
- regex_stub = RegexStub.new
- csv = CSV.new("1\n", :skip_lines => regex_stub)
- error = assert_raise(ArgumentError) do
- csv.shift
- end
- assert_equal(":skip_lines has to respond to #match: #{regex_stub.inspect}",
- error.message)
- end
-
- class Matchable
- def initialize(pattern)
- @pattern = pattern
- end
-
- def match(line)
- @pattern.match(line)
- end
- end
-
- def test_matchable
- csv = <<-CSV
-1
-# 2
-3
-# 4
- CSV
- assert_equal([
- ["1"],
- ["3"],
- ],
- CSV.parse(csv, :skip_lines => Matchable.new(/\A#/)))
- end
-
- def test_multibyte_data
- # U+3042 HIRAGANA LETTER A
- # U+3044 HIRAGANA LETTER I
- # U+3046 HIRAGANA LETTER U
- value = "\u3042\u3044\u3046"
- with_chunk_size("5") do
- assert_equal([[value], [value]],
- CSV.parse("#{value}\n#{value}\n",
- :skip_lines => /\A#/))
- end
- end
-
- def test_empty_line_and_liberal_parsing
- assert_equal([["a", "b"]],
- CSV.parse("a,b\n",
- :liberal_parsing => true,
- :skip_lines => /^$/))
- end
-
- def test_crlf
- assert_equal([["a", "b"]],
- CSV.parse("a,b\r\n,\r\n",
- :skip_lines => /^,+$/))
- end
-end
diff --git a/test/csv/parse/test_strip.rb b/test/csv/parse/test_strip.rb
deleted file mode 100644
index c5e35209cc..0000000000
--- a/test/csv/parse/test_strip.rb
+++ /dev/null
@@ -1,112 +0,0 @@
-# -*- coding: utf-8 -*-
-# frozen_string_literal: false
-
-require_relative "../helper"
-
-class TestCSVParseStrip < Test::Unit::TestCase
- extend DifferentOFS
-
- def test_both
- assert_equal(["a", "b"],
- CSV.parse_line(%Q{ a , b }, strip: true))
- end
-
- def test_left
- assert_equal(["a", "b"],
- CSV.parse_line(%Q{ a, b}, strip: true))
- end
-
- def test_right
- assert_equal(["a", "b"],
- CSV.parse_line(%Q{a ,b }, strip: true))
- end
-
- def test_middle
- assert_equal(["a b"],
- CSV.parse_line(%Q{a b}, strip: true))
- end
-
- def test_quoted
- assert_equal([" a ", " b "],
- CSV.parse_line(%Q{" a "," b "}, strip: true))
- end
-
- def test_liberal_parsing
- assert_equal([" a ", "b", " c ", " d "],
- CSV.parse_line(%Q{" a ", b , " c "," d " },
- strip: true,
- liberal_parsing: true))
- end
-
- def test_string
- assert_equal(["a", " b"],
- CSV.parse_line(%Q{ a , " b" },
- strip: " "))
- end
-
- def test_no_quote
- assert_equal([" a ", " b "],
- CSV.parse_line(%Q{" a ", b },
- strip: %Q{"},
- quote_char: nil))
- end
-
- def test_do_not_strip_cr
- assert_equal([
- ["a", "b "],
- ["a", "b "],
- ],
- CSV.parse(%Q{"a" ,"b " \r} +
- %Q{"a" ,"b " \r},
- strip: true))
- end
-
- def test_do_not_strip_lf
- assert_equal([
- ["a", "b "],
- ["a", "b "],
- ],
- CSV.parse(%Q{"a" ,"b " \n} +
- %Q{"a" ,"b " \n},
- strip: true))
- end
-
- def test_do_not_strip_crlf
- assert_equal([
- ["a", "b "],
- ["a", "b "],
- ],
- CSV.parse(%Q{"a" ,"b " \r\n} +
- %Q{"a" ,"b " \r\n},
- strip: true))
- end
-
- def test_col_sep_incompatible_true
- message = "The provided strip (true) and " \
- "col_sep (\\t) options are incompatible."
- assert_raise_with_message(ArgumentError, message) do
- CSV.parse_line(%Q{"a"\t"b"\n},
- col_sep: "\t",
- strip: true)
- end
- end
-
- def test_col_sep_incompatible_string
- message = "The provided strip (\\t) and " \
- "col_sep (\\t) options are incompatible."
- assert_raise_with_message(ArgumentError, message) do
- CSV.parse_line(%Q{"a"\t"b"\n},
- col_sep: "\t",
- strip: "\t")
- end
- end
-
- def test_col_sep_compatible_string
- assert_equal(
- ["a", "b"],
- CSV.parse_line(%Q{\va\tb\v\n},
- col_sep: "\t",
- strip: "\v")
- )
- end
-end
diff --git a/test/csv/parse/test_unconverted_fields.rb b/test/csv/parse/test_unconverted_fields.rb
deleted file mode 100644
index 437124ebd3..0000000000
--- a/test/csv/parse/test_unconverted_fields.rb
+++ /dev/null
@@ -1,117 +0,0 @@
-# -*- coding: utf-8 -*-
-# frozen_string_literal: false
-
-require_relative "../helper"
-
-class TestCSVParseUnconvertedFields < Test::Unit::TestCase
- extend DifferentOFS
-
- def setup
- super
- @custom = lambda {|field| /\A:(\S.*?)\s*\Z/ =~ field ? $1.to_sym : field}
-
- @headers = ["first", "second", "third"]
- @data = <<-CSV
-first,second,third
-1,2,3
- CSV
- end
-
-
- def test_custom
- row = CSV.parse_line("Numbers,:integer,1,:float,3.015",
- converters: [:numeric, @custom],
- unconverted_fields: true)
- assert_equal([
- ["Numbers", :integer, 1, :float, 3.015],
- ["Numbers", ":integer", "1", ":float", "3.015"],
- ],
- [
- row,
- row.unconverted_fields,
- ])
- end
-
- def test_no_fields
- row = CSV.parse_line("\n",
- converters: [:numeric, @custom],
- unconverted_fields: true)
- assert_equal([
- [],
- [],
- ],
- [
- row,
- row.unconverted_fields,
- ])
- end
-
- def test_parsed_header
- row = CSV.parse_line(@data,
- converters: :numeric,
- unconverted_fields: true,
- headers: :first_row)
- assert_equal([
- CSV::Row.new(@headers,
- [1, 2, 3]),
- ["1", "2", "3"],
- ],
- [
- row,
- row.unconverted_fields,
- ])
- end
-
- def test_return_headers
- row = CSV.parse_line(@data,
- converters: :numeric,
- unconverted_fields: true,
- headers: :first_row,
- return_headers: true)
- assert_equal([
- CSV::Row.new(@headers,
- @headers),
- @headers,
- ],
- [
- row,
- row.unconverted_fields,
- ])
- end
-
- def test_header_converters
- row = CSV.parse_line(@data,
- converters: :numeric,
- unconverted_fields: true,
- headers: :first_row,
- return_headers: true,
- header_converters: :symbol)
- assert_equal([
- CSV::Row.new(@headers.collect(&:to_sym),
- @headers),
- @headers,
- ],
- [
- row,
- row.unconverted_fields,
- ])
- end
-
- def test_specified_headers
- row = CSV.parse_line("\n",
- converters: :numeric,
- unconverted_fields: true,
- headers: %w{my new headers},
- return_headers: true,
- header_converters: :symbol)
- assert_equal([
- CSV::Row.new([:my, :new, :headers],
- ["my", "new", "headers"]),
- [],
- ],
- [
- row,
- row.unconverted_fields,
- ])
- end
-end
diff --git a/test/csv/test_data_converters.rb b/test/csv/test_data_converters.rb
deleted file mode 100644
index c20a5d1f4b..0000000000
--- a/test/csv/test_data_converters.rb
+++ /dev/null
@@ -1,190 +0,0 @@
-# -*- coding: utf-8 -*-
-# frozen_string_literal: false
-
-require_relative "helper"
-
-class TestCSVDataConverters < Test::Unit::TestCase
- extend DifferentOFS
-
- def setup
- super
- @win_safe_time_str = Time.now.strftime("%a %b %d %H:%M:%S %Y")
- end
-
- def test_builtin_integer_converter
- # does convert
- [-5, 1, 10000000000].each do |n|
- assert_equal(n, CSV::Converters[:integer][n.to_s])
- end
-
- # does not convert
- (%w{junk 1.0} + [""]).each do |str|
- assert_equal(str, CSV::Converters[:integer][str])
- end
- end
-
- def test_builtin_float_converter
- # does convert
- [-5.1234, 0, 2.3e-11].each do |n|
- assert_equal(n, CSV::Converters[:float][n.to_s])
- end
-
- # does not convert
- (%w{junk 1..0 .015F} + [""]).each do |str|
- assert_equal(str, CSV::Converters[:float][str])
- end
- end
-
- def test_builtin_date_converter
- # does convert
- assert_instance_of(
- Date,
- CSV::Converters[:date][@win_safe_time_str.sub(/\d+:\d+:\d+ /, "")]
- )
-
- # does not convert
- assert_instance_of(String, CSV::Converters[:date]["junk"])
- end
-
- def test_builtin_date_time_converter
- # does convert
- assert_instance_of( DateTime,
- CSV::Converters[:date_time][@win_safe_time_str] )
-
- # does not convert
- assert_instance_of(String, CSV::Converters[:date_time]["junk"])
- end
-
- def test_builtin_date_time_converter_iso8601_date
- iso8601_string = "2018-01-14"
- datetime = DateTime.new(2018, 1, 14)
- assert_equal(datetime,
- CSV::Converters[:date_time][iso8601_string])
- end
-
- def test_builtin_date_time_converter_iso8601_minute
- iso8601_string = "2018-01-14T22:25"
- datetime = DateTime.new(2018, 1, 14, 22, 25)
- assert_equal(datetime,
- CSV::Converters[:date_time][iso8601_string])
- end
-
- def test_builtin_date_time_converter_iso8601_second
- iso8601_string = "2018-01-14T22:25:19"
- datetime = DateTime.new(2018, 1, 14, 22, 25, 19)
- assert_equal(datetime,
- CSV::Converters[:date_time][iso8601_string])
- end
-
- def test_builtin_date_time_converter_iso8601_under_second
- iso8601_string = "2018-01-14T22:25:19.1"
- datetime = DateTime.new(2018, 1, 14, 22, 25, 19.1)
- assert_equal(datetime,
- CSV::Converters[:date_time][iso8601_string])
- end
-
- def test_builtin_date_time_converter_iso8601_under_second_offset
- iso8601_string = "2018-01-14T22:25:19.1+09:00"
- datetime = DateTime.new(2018, 1, 14, 22, 25, 19.1, "+9")
- assert_equal(datetime,
- CSV::Converters[:date_time][iso8601_string])
- end
-
- def test_builtin_date_time_converter_iso8601_offset
- iso8601_string = "2018-01-14T22:25:19+09:00"
- datetime = DateTime.new(2018, 1, 14, 22, 25, 19, "+9")
- assert_equal(datetime,
- CSV::Converters[:date_time][iso8601_string])
- end
-
- def test_builtin_date_time_converter_iso8601_utc
- iso8601_string = "2018-01-14T22:25:19Z"
- datetime = DateTime.new(2018, 1, 14, 22, 25, 19)
- assert_equal(datetime,
- CSV::Converters[:date_time][iso8601_string])
- end
-
- def test_builtin_date_time_converter_rfc3339_minute
- rfc3339_string = "2018-01-14 22:25"
- datetime = DateTime.new(2018, 1, 14, 22, 25)
- assert_equal(datetime,
- CSV::Converters[:date_time][rfc3339_string])
- end
-
- def test_builtin_date_time_converter_rfc3339_second
- rfc3339_string = "2018-01-14 22:25:19"
- datetime = DateTime.new(2018, 1, 14, 22, 25, 19)
- assert_equal(datetime,
- CSV::Converters[:date_time][rfc3339_string])
- end
-
- def test_builtin_date_time_converter_rfc3339_under_second
- rfc3339_string = "2018-01-14 22:25:19.1"
- datetime = DateTime.new(2018, 1, 14, 22, 25, 19.1)
- assert_equal(datetime,
- CSV::Converters[:date_time][rfc3339_string])
- end
-
- def test_builtin_date_time_converter_rfc3339_under_second_offset
- rfc3339_string = "2018-01-14 22:25:19.1+09:00"
- datetime = DateTime.new(2018, 1, 14, 22, 25, 19.1, "+9")
- assert_equal(datetime,
- CSV::Converters[:date_time][rfc3339_string])
- end
-
- def test_builtin_date_time_converter_rfc3339_offset
- rfc3339_string = "2018-01-14 22:25:19+09:00"
- datetime = DateTime.new(2018, 1, 14, 22, 25, 19, "+9")
- assert_equal(datetime,
- CSV::Converters[:date_time][rfc3339_string])
- end
-
- def test_builtin_date_time_converter_rfc3339_utc
- rfc3339_string = "2018-01-14 22:25:19Z"
- datetime = DateTime.new(2018, 1, 14, 22, 25, 19)
- assert_equal(datetime,
- CSV::Converters[:date_time][rfc3339_string])
- end
-
- def test_builtin_date_time_converter_rfc3339_tab_minute
- rfc3339_string = "2018-01-14\t22:25"
- datetime = DateTime.new(2018, 1, 14, 22, 25)
- assert_equal(datetime,
- CSV::Converters[:date_time][rfc3339_string])
- end
-
- def test_builtin_date_time_converter_rfc3339_tab_second
- rfc3339_string = "2018-01-14\t22:25:19"
- datetime = DateTime.new(2018, 1, 14, 22, 25, 19)
- assert_equal(datetime,
- CSV::Converters[:date_time][rfc3339_string])
- end
-
- def test_builtin_date_time_converter_rfc3339_tab_under_second
- rfc3339_string = "2018-01-14\t22:25:19.1"
- datetime = DateTime.new(2018, 1, 14, 22, 25, 19.1)
- assert_equal(datetime,
- CSV::Converters[:date_time][rfc3339_string])
- end
-
- def test_builtin_date_time_converter_rfc3339_tab_under_second_offset
- rfc3339_string = "2018-01-14\t22:25:19.1+09:00"
- datetime = DateTime.new(2018, 1, 14, 22, 25, 19.1, "+9")
- assert_equal(datetime,
- CSV::Converters[:date_time][rfc3339_string])
- end
-
- def test_builtin_date_time_converter_rfc3339_tab_offset
- rfc3339_string = "2018-01-14\t22:25:19+09:00"
- datetime = DateTime.new(2018, 1, 14, 22, 25, 19, "+9")
- assert_equal(datetime,
- CSV::Converters[:date_time][rfc3339_string])
- end
-
- def test_builtin_date_time_converter_rfc3339_tab_utc
- rfc3339_string = "2018-01-14\t22:25:19Z"
- datetime = DateTime.new(2018, 1, 14, 22, 25, 19)
- assert_equal(datetime,
- CSV::Converters[:date_time][rfc3339_string])
- end
-end
diff --git a/test/csv/test_encodings.rb b/test/csv/test_encodings.rb
deleted file mode 100644
index 032569da7a..0000000000
--- a/test/csv/test_encodings.rb
+++ /dev/null
@@ -1,403 +0,0 @@
-# -*- coding: utf-8 -*-
-# frozen_string_literal: false
-
-require_relative "helper"
-
-class TestCSVEncodings < Test::Unit::TestCase
- extend DifferentOFS
- include CSVHelper
-
- def setup
- super
- require 'tempfile'
- @temp_csv_file = Tempfile.new(%w"test_csv. .csv")
- @temp_csv_path = @temp_csv_file.path
- @temp_csv_file.close
- end
-
- def teardown
- @temp_csv_file.close!
- super
- end
-
- ########################################
- ### Hand Test Some Popular Encodings ###
- ########################################
-
- def test_parses_utf8_encoding
- assert_parses( [ %w[ one two … ],
- %w[ 1 … 3 ],
- %w[ … 5 6 ] ], "UTF-8" )
- end
-
- def test_parses_latin1_encoding
- assert_parses( [ %w[ one two Résumé ],
- %w[ 1 Résumé 3 ],
- %w[ Résumé 5 6 ] ], "ISO-8859-1" )
- end
-
- def test_parses_utf16be_encoding
- assert_parses( [ %w[ one two … ],
- %w[ 1 … 3 ],
- %w[ … 5 6 ] ], "UTF-16BE" )
- end
-
- def test_parses_shift_jis_encoding
- assert_parses( [ %w[ 一 二 三 ],
- %w[ 四 五 六 ],
- %w[ 七 å…« ä¹ ] ], "Shift_JIS" )
- end
-
- ###########################################################
- ### Try Simple Reading for All Non-dummy Ruby Encodings ###
- ###########################################################
-
- def test_reading_with_most_encodings
- each_encoding do |encoding|
- begin
- assert_parses( [ %w[ abc def ],
- %w[ ghi jkl ] ], encoding )
- rescue Encoding::ConverterNotFoundError
- fail("Failed to support #{encoding.name}.")
- end
- end
- end
-
- def test_regular_expression_escaping
- each_encoding do |encoding|
- begin
- assert_parses( [ %w[ abc def ],
- %w[ ghi jkl ] ], encoding, col_sep: "|" )
- rescue Encoding::ConverterNotFoundError
- fail("Failed to properly escape #{encoding.name}.")
- end
- end
- end
-
- def test_read_with_default_encoding
- data = "abc"
- default_external = Encoding.default_external
- each_encoding do |encoding|
- File.open(@temp_csv_path, "wb", encoding: encoding) {|f| f << data}
- begin
- no_warnings do
- Encoding.default_external = encoding
- end
- result = CSV.read(@temp_csv_path)[0][0]
- ensure
- no_warnings do
- Encoding.default_external = default_external
- end
- end
- assert_equal(encoding, result.encoding)
- end
- end
-
- #######################################################################
- ### Stress Test ASCII Compatible and Non-ASCII Compatible Encodings ###
- #######################################################################
-
- def test_auto_line_ending_detection
- # arrange data to place a \r at the end of CSV's read ahead point
- encode_for_tests([["a" * 509]], row_sep: "\r\n") do |data|
- assert_equal("\r\n".encode(data.encoding), CSV.new(data).row_sep)
- end
- end
-
- def test_csv_chars_are_transcoded
- encode_for_tests([%w[abc def]]) do |data|
- %w[col_sep row_sep quote_char].each do |csv_char|
- assert_equal( "|".encode(data.encoding),
- CSV.new(data, csv_char.to_sym => "|").send(csv_char) )
- end
- end
- end
-
- def test_parser_works_with_encoded_headers
- encode_for_tests([%w[one two three], %w[1 2 3]]) do |data|
- parsed = CSV.parse(data, headers: true)
- assert_all?(parsed.headers, "Wrong data encoding.") {|h| h.encoding == data.encoding}
- parsed.each do |row|
- assert_all?(row.fields, "Wrong data encoding.") {|f| f.encoding == data.encoding}
- end
- end
- end
-
- def test_built_in_converters_transcode_to_utf_8_then_convert
- encode_for_tests([%w[one two three], %w[1 2 3]]) do |data|
- parsed = CSV.parse(data, converters: :integer)
- assert_all?(parsed[0], "Wrong data encoding.") {|f| f.encoding == data.encoding}
- assert_equal([1, 2, 3], parsed[1])
- end
- end
-
- def test_built_in_header_converters_transcode_to_utf_8_then_convert
- encode_for_tests([%w[one two three], %w[1 2 3]]) do |data|
- parsed = CSV.parse( data, headers: true,
- header_converters: :downcase )
- assert_all?(parsed.headers, "Wrong data encoding.") {|h| h.encoding.name == "UTF-8"}
- assert_all?(parsed[0].fields, "Wrong data encoding.") {|f| f.encoding == data.encoding}
- end
- end
-
- def test_open_allows_you_to_set_encodings
- encode_for_tests([%w[abc def]]) do |data|
- # read and write in encoding
- File.open(@temp_csv_path, "wb:#{data.encoding.name}") { |f| f << data }
- CSV.open(@temp_csv_path, "rb:#{data.encoding.name}") do |csv|
- csv.each do |row|
- assert_all?(row, "Wrong data encoding.") {|f| f.encoding == data.encoding}
- end
- end
-
- # read and write with transcoding
- File.open(@temp_csv_path, "wb:UTF-32BE:#{data.encoding.name}") do |f|
- f << data
- end
- CSV.open(@temp_csv_path, "rb:UTF-32BE:#{data.encoding.name}") do |csv|
- csv.each do |row|
- assert_all?(row, "Wrong data encoding.") {|f| f.encoding == data.encoding}
- end
- end
- end
- end
-
- def test_foreach_allows_you_to_set_encodings
- encode_for_tests([%w[abc def]]) do |data|
- # read and write in encoding
- File.open(@temp_csv_path, "wb", encoding: data.encoding) { |f| f << data }
- CSV.foreach(@temp_csv_path, encoding: data.encoding) do |row|
- row.each {|f| assert_equal(f.encoding, data.encoding)}
- end
-
- # read and write with transcoding
- File.open(@temp_csv_path, "wb:UTF-32BE:#{data.encoding.name}") do |f|
- f << data
- end
- CSV.foreach( @temp_csv_path,
- encoding: "UTF-32BE:#{data.encoding.name}" ) do |row|
- assert_all?(row, "Wrong data encoding.") {|f| f.encoding == data.encoding}
- end
- end
- end
-
- def test_read_allows_you_to_set_encodings
- encode_for_tests([%w[abc def]]) do |data|
- # read and write in encoding
- File.open(@temp_csv_path, "wb:#{data.encoding.name}") { |f| f << data }
- rows = CSV.read(@temp_csv_path, encoding: data.encoding.name)
- assert_all?(rows.flatten, "Wrong data encoding.") {|f| f.encoding == data.encoding}
-
- # read and write with transcoding
- File.open(@temp_csv_path, "wb:UTF-32BE:#{data.encoding.name}") do |f|
- f << data
- end
- rows = CSV.read( @temp_csv_path,
- encoding: "UTF-32BE:#{data.encoding.name}" )
- assert_all?(rows.flatten, "Wrong data encoding.") {|f| f.encoding == data.encoding}
- end
- end
-
- #################################
- ### Write CSV in any Encoding ###
- #################################
-
- def test_can_write_csv_in_any_encoding
- each_encoding do |encoding|
- # test generate_line with encoding hint
- begin
- csv = %w[abc d|ef].map { |f| f.encode(encoding) }.
- to_csv(col_sep: "|", encoding: encoding.name)
- rescue Encoding::ConverterNotFoundError
- next
- end
- assert_equal(encoding, csv.encoding)
-
- # test generate_line with encoding guessing from fields
- csv = %w[abc d|ef].map { |f| f.encode(encoding) }.to_csv(col_sep: "|")
- assert_equal(encoding, csv.encoding)
-
- # writing to files
- data = encode_ary([%w[abc d,ef], %w[123 456 ]], encoding)
- CSV.open(@temp_csv_path, "wb:#{encoding.name}") do |f|
- data.each { |row| f << row }
- end
- assert_equal(data, CSV.read(@temp_csv_path, encoding: encoding.name))
- end
- end
-
- def test_encoding_is_upgraded_during_writing_as_needed
- data = ["foo".force_encoding("US-ASCII"), "\u3042"]
- assert_equal("US-ASCII", data.first.encoding.name)
- assert_equal("UTF-8", data.last.encoding.name)
- assert_equal("UTF-8", data.join('').encoding.name)
- assert_equal("UTF-8", data.to_csv.encoding.name)
- end
-
- def test_encoding_is_upgraded_for_ascii_content_during_writing_as_needed
- data = ["foo".force_encoding("ISO-8859-1"), "\u3042"]
- assert_equal("ISO-8859-1", data.first.encoding.name)
- assert_equal("UTF-8", data.last.encoding.name)
- assert_equal("UTF-8", data.join('').encoding.name)
- assert_equal("UTF-8", data.to_csv.encoding.name)
- end
-
- def test_encoding_is_not_upgraded_for_non_ascii_content_during_writing_as_needed
- data = ["\u00c0".encode("ISO-8859-1"), "\u3042"]
- assert_equal([
- "ISO-8859-1",
- "UTF-8",
- ],
- data.collect {|field| field.encoding.name})
- assert_raise(Encoding::CompatibilityError) do
- data.to_csv
- end
- end
-
- def test_explicit_encoding
- bug9766 = '[ruby-core:62113] [Bug #9766]'
- s = CSV.generate(encoding: "Windows-31J") do |csv|
- csv << ["foo".force_encoding("ISO-8859-1"), "\u3042"]
- end
- assert_equal(["foo,\u3042\n".encode(Encoding::Windows_31J), Encoding::Windows_31J], [s, s.encoding], bug9766)
- end
-
- def test_encoding_with_default_internal
- with_default_internal(Encoding::UTF_8) do
- s = CSV.generate(String.new(encoding: Encoding::Big5), encoding: Encoding::Big5) do |csv|
- csv << ["漢字"]
- end
- assert_equal(["漢字\n".encode(Encoding::Big5), Encoding::Big5], [s, s.encoding])
- end
- end
-
- def test_row_separator_detection_with_invalid_encoding
- csv = CSV.new("invalid,\xF8\r\nvalid,x\r\n".force_encoding("UTF-8"),
- encoding: "UTF-8")
- assert_equal("\r\n", csv.row_sep)
- end
-
- def test_invalid_encoding_row_error
- csv = CSV.new("valid,x\rinvalid,\xF8\r".force_encoding("UTF-8"),
- encoding: "UTF-8", row_sep: "\r")
- error = assert_raise(CSV::MalformedCSVError) do
- csv.shift
- csv.shift
- end
- assert_equal("Invalid byte sequence in UTF-8 in line 2.",
- error.message)
- end
-
- def test_string_input_transcode
- # U+3042 HIRAGANA LETTER A
- # U+3044 HIRAGANA LETTER I
- # U+3046 HIRAGANA LETTER U
- value = "\u3042\u3044\u3046"
- csv = CSV.new(value, encoding: "UTF-8:EUC-JP")
- assert_equal([[value.encode("EUC-JP")]],
- csv.read)
- end
-
- def test_string_input_set_encoding_string
- # U+3042 HIRAGANA LETTER A
- # U+3044 HIRAGANA LETTER I
- # U+3046 HIRAGANA LETTER U
- value = "\u3042\u3044\u3046".encode("EUC-JP")
- csv = CSV.new(value.dup.force_encoding("UTF-8"), encoding: "EUC-JP")
- assert_equal([[value.encode("EUC-JP")]],
- csv.read)
- end
-
- def test_string_input_set_encoding_encoding
- # U+3042 HIRAGANA LETTER A
- # U+3044 HIRAGANA LETTER I
- # U+3046 HIRAGANA LETTER U
- value = "\u3042\u3044\u3046".encode("EUC-JP")
- csv = CSV.new(value.dup.force_encoding("UTF-8"),
- encoding: Encoding.find("EUC-JP"))
- assert_equal([[value.encode("EUC-JP")]],
- csv.read)
- end
-
- private
-
- def assert_parses(fields, encoding, **options)
- encoding = Encoding.find(encoding) unless encoding.is_a? Encoding
- orig_fields = fields
- fields = encode_ary(fields, encoding)
- data = ary_to_data(fields, **options)
- parsed = CSV.parse(data, **options)
- assert_equal(fields, parsed)
- parsed.flatten.each_with_index do |field, i|
- assert_equal(encoding, field.encoding, "Field[#{i + 1}] was transcoded.")
- end
- File.open(@temp_csv_path, "wb") {|f| f.print(data)}
- CSV.open(@temp_csv_path, "rb:#{encoding}", **options) do |csv|
- csv.each_with_index do |row, i|
- assert_equal(fields[i], row)
- end
- end
- begin
- CSV.open(@temp_csv_path,
- "rb:#{encoding}:#{__ENCODING__}",
- **options) do |csv|
- csv.each_with_index do |row, i|
- assert_equal(orig_fields[i], row)
- end
- end unless encoding == __ENCODING__
- rescue Encoding::ConverterNotFoundError
- end
- options[:encoding] = encoding.name
- CSV.open(@temp_csv_path, **options) do |csv|
- csv.each_with_index do |row, i|
- assert_equal(fields[i], row)
- end
- end
- options.delete(:encoding)
- options[:external_encoding] = encoding.name
- options[:internal_encoding] = __ENCODING__.name
- begin
- CSV.open(@temp_csv_path, **options) do |csv|
- csv.each_with_index do |row, i|
- assert_equal(orig_fields[i], row)
- end
- end unless encoding == __ENCODING__
- rescue Encoding::ConverterNotFoundError
- end
- end
-
- def encode_ary(ary, encoding)
- ary.map { |row| row.map { |field| field.encode(encoding) } }
- end
-
- def ary_to_data(ary, **options)
- encoding = ary.flatten.first.encoding
- quote_char = (options[:quote_char] || '"').encode(encoding)
- col_sep = (options[:col_sep] || ",").encode(encoding)
- row_sep = (options[:row_sep] || "\n").encode(encoding)
- ary.map { |row|
- row.map { |field|
- [quote_char, field.encode(encoding), quote_char].join('')
- }.join(col_sep) + row_sep
- }.join('').encode(encoding)
- end
-
- def encode_for_tests(data, **options)
- yield ary_to_data(encode_ary(data, "UTF-8"), **options)
- yield ary_to_data(encode_ary(data, "UTF-16BE"), **options)
- end
-
- def each_encoding
- Encoding.list.each do |encoding|
- next if encoding.dummy? # skip "dummy" encodings
- yield encoding
- end
- end
-
- def no_warnings
- old_verbose, $VERBOSE = $VERBOSE, nil
- yield
- ensure
- $VERBOSE = old_verbose
- end
-end
diff --git a/test/csv/test_features.rb b/test/csv/test_features.rb
deleted file mode 100644
index d6eb2dc13b..0000000000
--- a/test/csv/test_features.rb
+++ /dev/null
@@ -1,359 +0,0 @@
-# -*- coding: utf-8 -*-
-# frozen_string_literal: false
-
-begin
- require "zlib"
-rescue LoadError
-end
-
-require_relative "helper"
-require "tempfile"
-
-class TestCSVFeatures < Test::Unit::TestCase
- extend DifferentOFS
-
- TEST_CASES = [ [%Q{a,b}, ["a", "b"]],
- [%Q{a,"""b"""}, ["a", "\"b\""]],
- [%Q{a,"""b"}, ["a", "\"b"]],
- [%Q{a,"b"""}, ["a", "b\""]],
- [%Q{a,"\nb"""}, ["a", "\nb\""]],
- [%Q{a,"""\nb"}, ["a", "\"\nb"]],
- [%Q{a,"""\nb\n"""}, ["a", "\"\nb\n\""]],
- [%Q{a,"""\nb\n""",\nc}, ["a", "\"\nb\n\"", nil]],
- [%Q{a,,,}, ["a", nil, nil, nil]],
- [%Q{,}, [nil, nil]],
- [%Q{"",""}, ["", ""]],
- [%Q{""""}, ["\""]],
- [%Q{"""",""}, ["\"",""]],
- [%Q{,""}, [nil,""]],
- [%Q{,"\r"}, [nil,"\r"]],
- [%Q{"\r\n,"}, ["\r\n,"]],
- [%Q{"\r\n,",}, ["\r\n,", nil]] ]
-
- def setup
- super
- @sample_data = <<-CSV
-line,1,abc
-line,2,"def\nghi"
-
-line,4,jkl
- CSV
- @csv = CSV.new(@sample_data)
- end
-
- def test_col_sep
- [";", "\t"].each do |sep|
- TEST_CASES.each do |test_case|
- assert_equal( test_case.last.map { |t| t.tr(",", sep) unless t.nil? },
- CSV.parse_line( test_case.first.tr(",", sep),
- col_sep: sep ) )
- end
- end
- assert_equal([",,,", nil], CSV.parse_line(",,,;", col_sep: ";"))
- end
-
- def test_col_sep_nil
- assert_raise_with_message(ArgumentError,
- ":col_sep must be 1 or more characters: nil") do
- CSV.parse(@sample_data, col_sep: nil)
- end
- end
-
- def test_col_sep_empty
- assert_raise_with_message(ArgumentError,
- ":col_sep must be 1 or more characters: \"\"") do
- CSV.parse(@sample_data, col_sep: "")
- end
- end
-
- def test_row_sep
- error = assert_raise(CSV::MalformedCSVError) do
- CSV.parse_line("1,2,3\n,4,5\r\n", row_sep: "\r\n")
- end
- assert_equal("Unquoted fields do not allow new line <\"\\n\"> in line 1.",
- error.message)
- assert_equal( ["1", "2", "3\n", "4", "5"],
- CSV.parse_line(%Q{1,2,"3\n",4,5\r\n}, row_sep: "\r\n"))
- end
-
- def test_quote_char
- TEST_CASES.each do |test_case|
- assert_equal(test_case.last.map {|t| t.tr('"', "'") unless t.nil?},
- CSV.parse_line(test_case.first.tr('"', "'"),
- quote_char: "'" ))
- end
- end
-
- def test_quote_char_special_regexp_char
- TEST_CASES.each do |test_case|
- assert_equal(test_case.last.map {|t| t.tr('"', "|") unless t.nil?},
- CSV.parse_line(test_case.first.tr('"', "|"),
- quote_char: "|"))
- end
- end
-
- def test_quote_char_special_regexp_char_liberal_parsing
- TEST_CASES.each do |test_case|
- assert_equal(test_case.last.map {|t| t.tr('"', "|") unless t.nil?},
- CSV.parse_line(test_case.first.tr('"', "|"),
- quote_char: "|",
- liberal_parsing: true))
- end
- end
-
- def test_csv_char_readers
- %w[col_sep row_sep quote_char].each do |reader|
- csv = CSV.new("abc,def", reader.to_sym => "|")
- assert_equal("|", csv.send(reader))
- end
- end
-
- def test_row_sep_auto_discovery
- ["\r\n", "\n", "\r"].each do |line_end|
- data = "1,2,3#{line_end}4,5#{line_end}"
- discovered = CSV.new(data).row_sep
- assert_equal(line_end, discovered)
- end
-
- assert_equal("\n", CSV.new("\n\r\n\r").row_sep)
-
- assert_equal($/, CSV.new("").row_sep)
-
- assert_equal($/, CSV.new(STDERR).row_sep)
- end
-
- def test_line
- lines = [
- %Q(\u{3000}abc,def\n),
- %Q(\u{3000}abc,"d\nef"\n),
- %Q(\u{3000}abc,"d\r\nef"\n),
- %Q(\u{3000}abc,"d\ref")
- ]
- csv = CSV.new(lines.join(''))
- lines.each do |line|
- csv.shift
- assert_equal(line, csv.line)
- end
- end
-
- def test_lineno
- assert_equal(5, @sample_data.lines.to_a.size)
-
- 4.times do |line_count|
- assert_equal(line_count, @csv.lineno)
- assert_not_nil(@csv.shift)
- assert_equal(line_count + 1, @csv.lineno)
- end
- assert_nil(@csv.shift)
- end
-
- def test_readline
- test_lineno
-
- @csv.rewind
-
- test_lineno
- end
-
- def test_unknown_options
- assert_raise_with_message(ArgumentError, /unknown keyword/) {
- CSV.new(@sample_data, unknown: :error)
- }
- assert_raise_with_message(ArgumentError, /unknown keyword/) {
- CSV.new(@sample_data, universal_newline: true)
- }
- end
-
- def test_skip_blanks
- assert_equal(4, @csv.to_a.size)
-
- @csv = CSV.new(@sample_data, skip_blanks: true)
-
- count = 0
- @csv.each do |row|
- count += 1
- assert_equal("line", row.first)
- end
- assert_equal(3, count)
- end
-
- def test_csv_behavior_readers
- %w[ unconverted_fields return_headers write_headers
- skip_blanks force_quotes ].each do |behavior|
- assert_not_predicate(CSV.new("abc,def"), "#{behavior}?", "Behavior defaulted to on.")
- csv = CSV.new("abc,def", behavior.to_sym => true)
- assert_predicate(csv, "#{behavior}?", "Behavior change now registered.")
- end
- end
-
- def test_converters_reader
- # no change
- assert_equal( [:integer],
- CSV.new("abc,def", converters: [:integer]).converters )
-
- # just one
- assert_equal( [:integer],
- CSV.new("abc,def", converters: :integer).converters )
-
- # expanded
- assert_equal( [:integer, :float],
- CSV.new("abc,def", converters: :numeric).converters )
-
- # custom
- csv = CSV.new("abc,def", converters: [:integer, lambda { }])
- assert_equal(2, csv.converters.size)
- assert_equal(:integer, csv.converters.first)
- assert_instance_of(Proc, csv.converters.last)
- end
-
- def test_header_converters_reader
- # no change
- hc = :header_converters
- assert_equal([:downcase], CSV.new("abc,def", hc => [:downcase]).send(hc))
-
- # just one
- assert_equal([:downcase], CSV.new("abc,def", hc => :downcase).send(hc))
-
- # custom
- csv = CSV.new("abc,def", hc => [:symbol, lambda { }])
- assert_equal(2, csv.send(hc).size)
- assert_equal(:symbol, csv.send(hc).first)
- assert_instance_of(Proc, csv.send(hc).last)
- end
-
- # reported by Kev Jackson
- def test_failing_to_escape_col_sep
- assert_nothing_raised(Exception) { CSV.new(String.new, col_sep: "|") }
- end
-
- # reported by Chris Roos
- def test_failing_to_reset_headers_in_rewind
- csv = CSV.new("forename,surname", headers: true, return_headers: true)
- csv.each {|row| assert_predicate row, :header_row?}
- csv.rewind
- csv.each {|row| assert_predicate row, :header_row?}
- end
-
- def test_gzip_reader
- zipped = nil
- assert_nothing_raised(NoMethodError) do
- zipped = CSV.new(
- Zlib::GzipReader.open(
- File.join(File.dirname(__FILE__), "line_endings.gz")
- )
- )
- end
- assert_equal("\r\n", zipped.row_sep)
- ensure
- zipped.close
- end if defined?(Zlib::GzipReader)
-
- def test_gzip_writer
- Tempfile.create(%w"temp .gz") {|tempfile|
- tempfile.close
- file = tempfile.path
- zipped = nil
- assert_nothing_raised(NoMethodError) do
- zipped = CSV.new(Zlib::GzipWriter.open(file))
- end
- zipped << %w[one two three]
- zipped << [1, 2, 3]
- zipped.close
-
- assert_include(Zlib::GzipReader.open(file) {|f| f.read},
- $INPUT_RECORD_SEPARATOR, "@row_sep did not default")
- }
- end if defined?(Zlib::GzipWriter)
-
- def test_inspect_is_smart_about_io_types
- str = CSV.new("string,data").inspect
- assert_include(str, "io_type:StringIO", "IO type not detected.")
-
- str = CSV.new($stderr).inspect
- assert_include(str, "io_type:$stderr", "IO type not detected.")
-
- Tempfile.create(%w"temp .csv") {|tempfile|
- tempfile.close
- path = tempfile.path
- File.open(path, "w") { |csv| csv << "one,two,three\n1,2,3\n" }
- str = CSV.open(path) { |csv| csv.inspect }
- assert_include(str, "io_type:File", "IO type not detected.")
- }
- end
-
- def test_inspect_shows_key_attributes
- str = @csv.inspect
- %w[lineno col_sep row_sep quote_char].each do |attr_name|
- assert_match(/\b#{attr_name}:[^\s>]+/, str)
- end
- end
-
- def test_inspect_shows_headers_when_available
- csv = CSV.new("one,two,three\n1,2,3\n", headers: true)
- assert_include(csv.inspect, "headers:true", "Header hint not shown.")
- csv.shift # load headers
- assert_match(/headers:\[[^\]]+\]/, csv.inspect)
- end
-
- def test_inspect_encoding_is_ascii_compatible
- csv = CSV.new("one,two,three\n1,2,3\n".encode("UTF-16BE"))
- assert_send([Encoding, :compatible?,
- Encoding.find("US-ASCII"), csv.inspect.encoding],
- "inspect() was not ASCII compatible.")
- end
-
- def test_version
- assert_not_nil(CSV::VERSION)
- assert_instance_of(String, CSV::VERSION)
- assert_predicate(CSV::VERSION, :frozen?)
- assert_match(/\A\d\.\d\.\d\z/, CSV::VERSION)
- end
-
- def test_table_nil_equality
- assert_nothing_raised(NoMethodError) { CSV.parse("test", headers: true) == nil }
- end
-
- # non-seekable input stream for testing https://github.com/ruby/csv/issues/44
- class DummyIO
- extend Forwardable
- def_delegators :@io, :gets, :read, :pos, :eof? # no seek or rewind!
- def initialize(data)
- @io = StringIO.new(data)
- end
- end
-
- def test_line_separator_autodetection_for_non_seekable_input_lf
- c = CSV.new(DummyIO.new("one,two,three\nfoo,bar,baz\n"))
- assert_equal [["one", "two", "three"], ["foo", "bar", "baz"]], c.each.to_a
- end
-
- def test_line_separator_autodetection_for_non_seekable_input_cr
- c = CSV.new(DummyIO.new("one,two,three\rfoo,bar,baz\r"))
- assert_equal [["one", "two", "three"], ["foo", "bar", "baz"]], c.each.to_a
- end
-
- def test_line_separator_autodetection_for_non_seekable_input_cr_lf
- c = CSV.new(DummyIO.new("one,two,three\r\nfoo,bar,baz\r\n"))
- assert_equal [["one", "two", "three"], ["foo", "bar", "baz"]], c.each.to_a
- end
-
- def test_line_separator_autodetection_for_non_seekable_input_1024_over_lf
- table = (1..10).map { |row| (1..200).map { |col| "row#{row}col#{col}" }.to_a }.to_a
- input = table.map { |line| line.join(",") }.join("\n")
- c = CSV.new(DummyIO.new(input))
- assert_equal table, c.each.to_a
- end
-
- def test_line_separator_autodetection_for_non_seekable_input_1024_over_cr_lf
- table = (1..10).map { |row| (1..200).map { |col| "row#{row}col#{col}" }.to_a }.to_a
- input = table.map { |line| line.join(",") }.join("\r\n")
- c = CSV.new(DummyIO.new(input))
- assert_equal table, c.each.to_a
- end
-
- def test_line_separator_autodetection_for_non_seekable_input_many_cr_only
- # input with lots of CRs (to make sure no bytes are lost due to look-ahead)
- c = CSV.new(DummyIO.new("foo\r" + "\r" * 9999 + "bar\r"))
- assert_equal [["foo"]] + [[]] * 9999 + [["bar"]], c.each.to_a
- end
-end
diff --git a/test/csv/test_patterns.rb b/test/csv/test_patterns.rb
deleted file mode 100644
index 881f03a3a4..0000000000
--- a/test/csv/test_patterns.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "helper"
-
-class TestCSVPatternMatching < Test::Unit::TestCase
-
- def test_hash
- case CSV::Row.new(%i{A B C}, [1, 2, 3])
- in B: b, C: c
- assert_equal([2, 3], [b, c])
- end
- end
-
- def test_hash_rest
- case CSV::Row.new(%i{A B C}, [1, 2, 3])
- in B: b, **rest
- assert_equal([2, { A: 1, C: 3 }], [b, rest])
- end
- end
-
- def test_array
- case CSV::Row.new(%i{A B C}, [1, 2, 3])
- in *, matched
- assert_equal(3, matched)
- end
- end
-end
diff --git a/test/csv/test_row.rb b/test/csv/test_row.rb
deleted file mode 100644
index b717945041..0000000000
--- a/test/csv/test_row.rb
+++ /dev/null
@@ -1,435 +0,0 @@
-# -*- coding: utf-8 -*-
-# frozen_string_literal: false
-
-require_relative "helper"
-
-class TestCSVRow < Test::Unit::TestCase
- extend DifferentOFS
-
- def setup
- super
- @row = CSV::Row.new(%w{A B C A A}, [1, 2, 3, 4])
- end
-
- def test_initialize
- # basic
- row = CSV::Row.new(%w{A B C}, [1, 2, 3])
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([["A", 1], ["B", 2], ["C", 3]], row.to_a)
-
- # missing headers
- row = CSV::Row.new(%w{A}, [1, 2, 3])
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([["A", 1], [nil, 2], [nil, 3]], row.to_a)
-
- # missing fields
- row = CSV::Row.new(%w{A B C}, [1, 2])
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([["A", 1], ["B", 2], ["C", nil]], row.to_a)
- end
-
- def test_row_type
- # field rows
- row = CSV::Row.new(%w{A B C}, [1, 2, 3]) # implicit
- assert_not_predicate(row, :header_row?)
- assert_predicate(row, :field_row?)
- row = CSV::Row.new(%w{A B C}, [1, 2, 3], false) # explicit
- assert_not_predicate(row, :header_row?)
- assert_predicate(row, :field_row?)
-
- # header row
- row = CSV::Row.new(%w{A B C}, [1, 2, 3], true)
- assert_predicate(row, :header_row?)
- assert_not_predicate(row, :field_row?)
- end
-
- def test_headers
- assert_equal(%w{A B C A A}, @row.headers)
- end
-
- def test_field
- # by name
- assert_equal(2, @row.field("B"))
- assert_equal(2, @row["B"]) # alias
-
- # by index
- assert_equal(3, @row.field(2))
-
- # by range
- assert_equal([2,3], @row.field(1..2))
-
- # missing
- assert_nil(@row.field("Missing"))
- assert_nil(@row.field(10))
-
- # minimum index
- assert_equal(1, @row.field("A"))
- assert_equal(1, @row.field("A", 0))
- assert_equal(4, @row.field("A", 1))
- assert_equal(4, @row.field("A", 2))
- assert_equal(4, @row.field("A", 3))
- assert_equal(nil, @row.field("A", 4))
- assert_equal(nil, @row.field("A", 5))
- end
-
- def test_fetch
- # only by name
- assert_equal(2, @row.fetch('B'))
-
- # missing header raises KeyError
- assert_raise KeyError do
- @row.fetch('foo')
- end
-
- # missing header yields itself to block
- assert_equal 'bar', @row.fetch('foo') { |header|
- header == 'foo' ? 'bar' : false }
-
- # missing header returns the given default value
- assert_equal 'bar', @row.fetch('foo', 'bar')
-
- # more than one vararg raises ArgumentError
- assert_raise ArgumentError do
- @row.fetch('foo', 'bar', 'baz')
- end
- end
-
- def test_has_key?
- assert_equal(true, @row.has_key?('B'))
- assert_equal(false, @row.has_key?('foo'))
-
- # aliases
- assert_equal(true, @row.header?('B'))
- assert_equal(false, @row.header?('foo'))
-
- assert_equal(true, @row.include?('B'))
- assert_equal(false, @row.include?('foo'))
-
- assert_equal(true, @row.member?('B'))
- assert_equal(false, @row.member?('foo'))
-
- assert_equal(true, @row.key?('B'))
- assert_equal(false, @row.key?('foo'))
- end
-
- def test_set_field
- # set field by name
- assert_equal(100, @row["A"] = 100)
-
- # set field by index
- assert_equal(300, @row[3] = 300)
-
- # set field by name and minimum index
- assert_equal([:a, :b, :c], @row["A", 4] = [:a, :b, :c])
-
- # verify the changes
- assert_equal( [ ["A", 100],
- ["B", 2],
- ["C", 3],
- ["A", 300],
- ["A", [:a, :b, :c]] ], @row.to_a )
-
- # assigning an index past the end
- assert_equal("End", @row[10] = "End")
- assert_equal( [ ["A", 100],
- ["B", 2],
- ["C", 3],
- ["A", 300],
- ["A", [:a, :b, :c]],
- [nil, nil],
- [nil, nil],
- [nil, nil],
- [nil, nil],
- [nil, nil],
- [nil, "End"] ], @row.to_a )
-
- # assigning a new field by header
- assert_equal("New", @row[:new] = "New")
- assert_equal( [ ["A", 100],
- ["B", 2],
- ["C", 3],
- ["A", 300],
- ["A", [:a, :b, :c]],
- [nil, nil],
- [nil, nil],
- [nil, nil],
- [nil, nil],
- [nil, nil],
- [nil, "End"],
- [:new, "New"] ], @row.to_a )
- end
-
- def test_append
- # add a value
- assert_equal(@row, @row << "Value")
- assert_equal( [ ["A", 1],
- ["B", 2],
- ["C", 3],
- ["A", 4],
- ["A", nil],
- [nil, "Value"] ], @row.to_a )
-
- # add a pair
- assert_equal(@row, @row << %w{Header Field})
- assert_equal( [ ["A", 1],
- ["B", 2],
- ["C", 3],
- ["A", 4],
- ["A", nil],
- [nil, "Value"],
- %w{Header Field} ], @row.to_a )
-
- # a pair with Hash syntax
- assert_equal(@row, @row << {key: :value})
- assert_equal( [ ["A", 1],
- ["B", 2],
- ["C", 3],
- ["A", 4],
- ["A", nil],
- [nil, "Value"],
- %w{Header Field},
- [:key, :value] ], @row.to_a )
-
- # multiple fields at once
- assert_equal(@row, @row.push(100, 200, [:last, 300]))
- assert_equal( [ ["A", 1],
- ["B", 2],
- ["C", 3],
- ["A", 4],
- ["A", nil],
- [nil, "Value"],
- %w{Header Field},
- [:key, :value],
- [nil, 100],
- [nil, 200],
- [:last, 300] ], @row.to_a )
- end
-
- def test_delete
- # by index
- assert_equal(["B", 2], @row.delete(1))
-
- # by header
- assert_equal(["C", 3], @row.delete("C"))
-
- end
-
- def test_delete_if
- assert_equal(@row, @row.delete_if { |h, f| h == "A" and not f.nil? })
- assert_equal([["B", 2], ["C", 3], ["A", nil]], @row.to_a)
- end
-
- def test_delete_if_without_block
- enum = @row.delete_if
- assert_instance_of(Enumerator, enum)
- assert_equal(@row.size, enum.size)
-
- assert_equal(@row, enum.each { |h, f| h == "A" and not f.nil? })
- assert_equal([["B", 2], ["C", 3], ["A", nil]], @row.to_a)
- end
-
- def test_fields
- # all fields
- assert_equal([1, 2, 3, 4, nil], @row.fields)
-
- # by header
- assert_equal([1, 3], @row.fields("A", "C"))
-
- # by index
- assert_equal([2, 3, nil], @row.fields(1, 2, 10))
-
- # by both
- assert_equal([2, 3, 4], @row.fields("B", "C", 3))
-
- # with minimum indices
- assert_equal([2, 3, 4], @row.fields("B", "C", ["A", 3]))
-
- # by header range
- assert_equal([2, 3], @row.values_at("B".."C"))
- end
-
- def test_index
- # basic usage
- assert_equal(0, @row.index("A"))
- assert_equal(1, @row.index("B"))
- assert_equal(2, @row.index("C"))
- assert_equal(nil, @row.index("Z"))
-
- # with minimum index
- assert_equal(0, @row.index("A"))
- assert_equal(0, @row.index("A", 0))
- assert_equal(3, @row.index("A", 1))
- assert_equal(3, @row.index("A", 2))
- assert_equal(3, @row.index("A", 3))
- assert_equal(4, @row.index("A", 4))
- assert_equal(nil, @row.index("A", 5))
- end
-
- def test_queries
- # fields
- assert(@row.field?(4))
- assert(@row.field?(nil))
- assert(!@row.field?(10))
- end
-
- def test_each
- # array style
- ary = @row.to_a
- @row.each do |pair|
- assert_equal(ary.first.first, pair.first)
- assert_equal(ary.shift.last, pair.last)
- end
-
- # hash style
- ary = @row.to_a
- @row.each do |header, field|
- assert_equal(ary.first.first, header)
- assert_equal(ary.shift.last, field)
- end
-
- # verify that we can chain the call
- assert_equal(@row, @row.each { })
-
- # without block
- ary = @row.to_a
- enum = @row.each
- assert_instance_of(Enumerator, enum)
- assert_equal(@row.size, enum.size)
- enum.each do |pair|
- assert_equal(ary.first.first, pair.first)
- assert_equal(ary.shift.last, pair.last)
- end
- end
-
- def test_each_pair
- assert_equal([
- ["A", 1],
- ["B", 2],
- ["C", 3],
- ["A", 4],
- ["A", nil],
- ],
- @row.each_pair.to_a)
- end
-
- def test_enumerable
- assert_equal( [["A", 1], ["A", 4], ["A", nil]],
- @row.select { |pair| pair.first == "A" } )
-
- assert_equal(10, @row.inject(0) { |sum, (_, n)| sum + (n || 0) })
- end
-
- def test_to_a
- row = CSV::Row.new(%w{A B C}, [1, 2, 3]).to_a
- assert_instance_of(Array, row)
- row.each do |pair|
- assert_instance_of(Array, pair)
- assert_equal(2, pair.size)
- end
- assert_equal([["A", 1], ["B", 2], ["C", 3]], row)
- end
-
- def test_to_hash
- hash = @row.to_hash
- assert_equal({"A" => @row["A"], "B" => @row["B"], "C" => @row["C"]}, hash)
- hash.keys.each_with_index do |string_key, h|
- assert_predicate(string_key, :frozen?)
- assert_same(string_key, @row.headers[h])
- end
- end
-
- def test_to_csv
- # normal conversion
- assert_equal("1,2,3,4,\n", @row.to_csv)
- assert_equal("1,2,3,4,\n", @row.to_s) # alias
-
- # with options
- assert_equal( "1|2|3|4|\r\n",
- @row.to_csv(col_sep: "|", row_sep: "\r\n") )
- end
-
- def test_array_delegation
- assert_not_empty(@row, "Row was empty.")
-
- assert_equal([@row.headers.size, @row.fields.size].max, @row.size)
- end
-
- def test_inspect_shows_header_field_pairs
- str = @row.inspect
- @row.each do |header, field|
- assert_include(str, "#{header.inspect}:#{field.inspect}",
- "Header field pair not found.")
- end
- end
-
- def test_inspect_encoding_is_ascii_compatible
- assert_send([Encoding, :compatible?,
- Encoding.find("US-ASCII"),
- @row.inspect.encoding],
- "inspect() was not ASCII compatible.")
- end
-
- def test_inspect_shows_symbol_headers_as_bare_attributes
- str = CSV::Row.new(@row.headers.map { |h| h.to_sym }, @row.fields).inspect
- @row.each do |header, field|
- assert_include(str, "#{header}:#{field.inspect}",
- "Header field pair not found.")
- end
- end
-
- def test_can_be_compared_with_other_classes
- assert_not_nil(CSV::Row.new([ ], [ ]), "The row was nil")
- end
-
- def test_can_be_compared_when_not_a_row
- r = @row == []
- assert_equal false, r
- end
-
- def test_dig_by_index
- assert_equal(2, @row.dig(1))
-
- assert_nil(@row.dig(100))
- end
-
- def test_dig_by_header
- assert_equal(2, @row.dig("B"))
-
- assert_nil(@row.dig("Missing"))
- end
-
- def test_dig_cell
- row = CSV::Row.new(%w{A}, [["foo", ["bar", ["baz"]]]])
-
- assert_equal("foo", row.dig(0, 0))
- assert_equal("bar", row.dig(0, 1, 0))
-
- assert_equal("foo", row.dig("A", 0))
- assert_equal("bar", row.dig("A", 1, 0))
- end
-
- def test_dig_cell_no_dig
- row = CSV::Row.new(%w{A}, ["foo"])
-
- assert_raise(TypeError) do
- row.dig(0, 0)
- end
- assert_raise(TypeError) do
- row.dig("A", 0)
- end
- end
-
- def test_dup
- row = CSV::Row.new(["A"], ["foo"])
- dupped_row = row.dup
- dupped_row["A"] = "bar"
- assert_equal(["foo", "bar"],
- [row["A"], dupped_row["A"]])
- dupped_row.delete("A")
- assert_equal(["foo", nil],
- [row["A"], dupped_row["A"]])
- end
-end
diff --git a/test/csv/test_table.rb b/test/csv/test_table.rb
deleted file mode 100644
index e8ab74044e..0000000000
--- a/test/csv/test_table.rb
+++ /dev/null
@@ -1,691 +0,0 @@
-# -*- coding: utf-8 -*-
-# frozen_string_literal: false
-
-require_relative "helper"
-
-class TestCSVTable < Test::Unit::TestCase
- extend DifferentOFS
-
- def setup
- super
- @rows = [ CSV::Row.new(%w{A B C}, [1, 2, 3]),
- CSV::Row.new(%w{A B C}, [4, 5, 6]),
- CSV::Row.new(%w{A B C}, [7, 8, 9]) ]
- @table = CSV::Table.new(@rows)
-
- @header_table = CSV::Table.new(
- [CSV::Row.new(%w{A B C}, %w{A B C}, true)] + @rows
- )
-
- @header_only_table = CSV::Table.new([], headers: %w{A B C})
- end
-
- def test_initialize
- assert_not_nil(@table)
- assert_instance_of(CSV::Table, @table)
- end
-
- def test_modes
- assert_equal(:col_or_row, @table.mode)
-
- # non-destructive changes, intended for one shot calls
- cols = @table.by_col
- assert_equal(:col_or_row, @table.mode)
- assert_equal(:col, cols.mode)
- assert_equal(@table, cols)
-
- rows = @table.by_row
- assert_equal(:col_or_row, @table.mode)
- assert_equal(:row, rows.mode)
- assert_equal(@table, rows)
-
- col_or_row = rows.by_col_or_row
- assert_equal(:row, rows.mode)
- assert_equal(:col_or_row, col_or_row.mode)
- assert_equal(@table, col_or_row)
-
- # destructive mode changing calls
- assert_equal(@table, @table.by_row!)
- assert_equal(:row, @table.mode)
- assert_equal(@table, @table.by_col_or_row!)
- assert_equal(:col_or_row, @table.mode)
- end
-
- def test_headers
- assert_equal(@rows.first.headers, @table.headers)
- end
-
- def test_headers_empty
- t = CSV::Table.new([])
- assert_equal Array.new, t.headers
- end
-
- def test_headers_only
- assert_equal(%w[A B C], @header_only_table.headers)
- end
-
- def test_headers_modified_by_row
- table = CSV::Table.new([], headers: ["A", "B"])
- table << ["a", "b"]
- table.first << {"C" => "c"}
- assert_equal(["A", "B", "C"], table.headers)
- end
-
- def test_index
- ##################
- ### Mixed Mode ###
- ##################
- # by row
- @rows.each_index { |i| assert_equal(@rows[i], @table[i]) }
- assert_equal(nil, @table[100]) # empty row
-
- # by row with Range
- assert_equal([@table[1], @table[2]], @table[1..2])
-
- # by col
- @rows.first.headers.each do |header|
- assert_equal(@rows.map { |row| row[header] }, @table[header])
- end
- assert_equal([nil] * @rows.size, @table["Z"]) # empty col
-
- # by cell, row then col
- assert_equal(2, @table[0][1])
- assert_equal(6, @table[1]["C"])
-
- # by cell, col then row
- assert_equal(5, @table["B"][1])
- assert_equal(9, @table["C"][2])
-
- # with headers (by col)
- assert_equal(["B", 2, 5, 8], @header_table["B"])
-
- ###################
- ### Column Mode ###
- ###################
- @table.by_col!
-
- assert_equal([2, 5, 8], @table[1])
- assert_equal([2, 5, 8], @table["B"])
-
- ################
- ### Row Mode ###
- ################
- @table.by_row!
-
- assert_equal(@rows[1], @table[1])
- assert_raise(TypeError) { @table["B"] }
-
- ############################
- ### One Shot Mode Change ###
- ############################
- assert_equal(@rows[1], @table[1])
- assert_equal([2, 5, 8], @table.by_col[1])
- assert_equal(@rows[1], @table[1])
- end
-
- def test_set_row_or_column
- ##################
- ### Mixed Mode ###
- ##################
- # set row
- @table[2] = [10, 11, 12]
- assert_equal([%w[A B C], [1, 2, 3], [4, 5, 6], [10, 11, 12]], @table.to_a)
-
- @table[3] = CSV::Row.new(%w[A B C], [13, 14, 15])
- assert_equal( [%w[A B C], [1, 2, 3], [4, 5, 6], [10, 11, 12], [13, 14, 15]],
- @table.to_a )
-
- # set col
- @table["Type"] = "data"
- assert_equal( [ %w[A B C Type],
- [1, 2, 3, "data"],
- [4, 5, 6, "data"],
- [10, 11, 12, "data"],
- [13, 14, 15, "data"] ],
- @table.to_a )
-
- @table["Index"] = [1, 2, 3]
- assert_equal( [ %w[A B C Type Index],
- [1, 2, 3, "data", 1],
- [4, 5, 6, "data", 2],
- [10, 11, 12, "data", 3],
- [13, 14, 15, "data", nil] ],
- @table.to_a )
-
- @table["B"] = [100, 200]
- assert_equal( [ %w[A B C Type Index],
- [1, 100, 3, "data", 1],
- [4, 200, 6, "data", 2],
- [10, nil, 12, "data", 3],
- [13, nil, 15, "data", nil] ],
- @table.to_a )
-
- # verify resulting table
- assert_equal(<<-CSV, @table.to_csv)
-A,B,C,Type,Index
-1,100,3,data,1
-4,200,6,data,2
-10,,12,data,3
-13,,15,data,
- CSV
-
- # with headers
- @header_table["Type"] = "data"
- assert_equal(%w[Type data data data], @header_table["Type"])
-
- ###################
- ### Column Mode ###
- ###################
- @table.by_col!
-
- @table[1] = [2, 5, 11, 14]
- assert_equal( [ %w[A B C Type Index],
- [1, 2, 3, "data", 1],
- [4, 5, 6, "data", 2],
- [10, 11, 12, "data", 3],
- [13, 14, 15, "data", nil] ],
- @table.to_a )
-
- @table["Extra"] = "new stuff"
- assert_equal( [ %w[A B C Type Index Extra],
- [1, 2, 3, "data", 1, "new stuff"],
- [4, 5, 6, "data", 2, "new stuff"],
- [10, 11, 12, "data", 3, "new stuff"],
- [13, 14, 15, "data", nil, "new stuff"] ],
- @table.to_a )
-
- ################
- ### Row Mode ###
- ################
- @table.by_row!
-
- @table[1] = (1..6).to_a
- assert_equal( [ %w[A B C Type Index Extra],
- [1, 2, 3, "data", 1, "new stuff"],
- [1, 2, 3, 4, 5, 6],
- [10, 11, 12, "data", 3, "new stuff"],
- [13, 14, 15, "data", nil, "new stuff"] ],
- @table.to_a )
-
- assert_raise(TypeError) { @table["Extra"] = nil }
- end
-
- def test_set_by_col_with_header_row
- r = [ CSV::Row.new(%w{X Y Z}, [97, 98, 99], true) ]
- t = CSV::Table.new(r)
- t.by_col!
- t['A'] = [42]
- assert_equal(['A'], t['A'])
- end
-
- def test_each
- ######################
- ### Mixed/Row Mode ###
- ######################
- i = 0
- @table.each do |row|
- assert_equal(@rows[i], row)
- i += 1
- end
-
- # verify that we can chain the call
- assert_equal(@table, @table.each { })
-
- # without block
- enum = @table.each
- assert_instance_of(Enumerator, enum)
- assert_equal(@table.size, enum.size)
-
- i = 0
- enum.each do |row|
- assert_equal(@rows[i], row)
- i += 1
- end
-
- ###################
- ### Column Mode ###
- ###################
- @table.by_col!
-
- headers = @table.headers
- @table.each do |header, column|
- assert_equal(headers.shift, header)
- assert_equal(@table[header], column)
- end
-
- # without block
- enum = @table.each
- assert_instance_of(Enumerator, enum)
- assert_equal(@table.headers.size, enum.size)
-
- headers = @table.headers
- enum.each do |header, column|
- assert_equal(headers.shift, header)
- assert_equal(@table[header], column)
- end
-
- ############################
- ### One Shot Mode Change ###
- ############################
- @table.by_col_or_row!
-
- @table.each { |row| assert_instance_of(CSV::Row, row) }
- @table.by_col.each { |tuple| assert_instance_of(Array, tuple) }
- @table.each { |row| assert_instance_of(CSV::Row, row) }
- end
-
- def test_each_by_col_duplicated_headers
- table = CSV.parse(<<-CSV, headers: true)
-a,a,,,b
-1,2,3,4,5
-11,12,13,14,15
- CSV
- assert_equal([
- ["a", ["1", "11"]],
- ["a", ["2", "12"]],
- [nil, ["3", "13"]],
- [nil, ["4", "14"]],
- ["b", ["5", "15"]],
- ],
- table.by_col.each.to_a)
- end
-
- def test_each_split
- yielded_values = []
- @table.each do |column1, column2, column3|
- yielded_values << [column1, column2, column3]
- end
- assert_equal(@rows.collect(&:to_a),
- yielded_values)
- end
-
- def test_enumerable
- assert_equal( @rows.values_at(0, 2),
- @table.select { |row| (row["B"] % 2).zero? } )
-
- assert_equal(@rows[1], @table.find { |row| row["C"] > 5 })
- end
-
- def test_to_a
- assert_equal([%w[A B C], [1, 2, 3], [4, 5, 6], [7, 8, 9]], @table.to_a)
-
- # with headers
- assert_equal( [%w[A B C], [1, 2, 3], [4, 5, 6], [7, 8, 9]],
- @header_table.to_a )
- end
-
- def test_to_csv
- csv = <<-CSV
-A,B,C
-1,2,3
-4,5,6
-7,8,9
- CSV
-
- # normal conversion
- assert_equal(csv, @table.to_csv)
- assert_equal(csv, @table.to_s) # alias
-
- # with options
- assert_equal( csv.gsub(",", "|").gsub("\n", "\r\n"),
- @table.to_csv(col_sep: "|", row_sep: "\r\n") )
- assert_equal( csv.lines.to_a[1..-1].join(''),
- @table.to_csv(:write_headers => false) )
-
- # with headers
- assert_equal(csv, @header_table.to_csv)
- end
-
- def test_to_csv_limit_positive
- assert_equal(<<-CSV, @table.to_csv(limit: 2))
-A,B,C
-1,2,3
-4,5,6
- CSV
- end
-
- def test_to_csv_limit_positive_over
- assert_equal(<<-CSV, @table.to_csv(limit: 5))
-A,B,C
-1,2,3
-4,5,6
-7,8,9
- CSV
- end
-
- def test_to_csv_limit_zero
- assert_equal(<<-CSV, @table.to_csv(limit: 0))
-A,B,C
- CSV
- end
-
- def test_to_csv_limit_negative
- assert_equal(<<-CSV, @table.to_csv(limit: -2))
-A,B,C
-1,2,3
-4,5,6
- CSV
- end
-
- def test_to_csv_limit_negative_over
- assert_equal(<<-CSV, @table.to_csv(limit: -5))
-A,B,C
- CSV
- end
-
- def test_append
- # verify that we can chain the call
- assert_equal(@table, @table << [10, 11, 12])
-
- # Array append
- assert_equal(CSV::Row.new(%w[A B C], [10, 11, 12]), @table[-1])
-
- # Row append
- assert_equal(@table, @table << CSV::Row.new(%w[A B C], [13, 14, 15]))
- assert_equal(CSV::Row.new(%w[A B C], [13, 14, 15]), @table[-1])
- end
-
- def test_delete_mixed_one
- ##################
- ### Mixed Mode ###
- ##################
- # delete a row
- assert_equal(@rows[1], @table.delete(1))
-
- # delete a col
- assert_equal(@rows.map { |row| row["A"] }, @table.delete("A"))
-
- # verify resulting table
- assert_equal(<<-CSV, @table.to_csv)
-B,C
-2,3
-8,9
- CSV
- end
-
- def test_delete_mixed_multiple
- ##################
- ### Mixed Mode ###
- ##################
- # delete row and col
- second_row = @rows[1]
- a_col = @rows.map { |row| row["A"] }
- a_col_without_second_row = a_col[0..0] + a_col[2..-1]
- assert_equal([
- second_row,
- a_col_without_second_row,
- ],
- @table.delete(1, "A"))
-
- # verify resulting table
- assert_equal(<<-CSV, @table.to_csv)
-B,C
-2,3
-8,9
- CSV
- end
-
- def test_delete_column
- ###################
- ### Column Mode ###
- ###################
- @table.by_col!
-
- assert_equal(@rows.map { |row| row[0] }, @table.delete(0))
- assert_equal(@rows.map { |row| row["C"] }, @table.delete("C"))
-
- # verify resulting table
- assert_equal(<<-CSV, @table.to_csv)
-B
-2
-5
-8
- CSV
- end
-
- def test_delete_row
- ################
- ### Row Mode ###
- ################
- @table.by_row!
-
- assert_equal(@rows[1], @table.delete(1))
- assert_raise(TypeError) { @table.delete("C") }
-
- # verify resulting table
- assert_equal(<<-CSV, @table.to_csv)
-A,B,C
-1,2,3
-7,8,9
- CSV
- end
-
- def test_delete_with_blank_rows
- data = "col1,col2\nra1,ra2\n\nrb1,rb2"
- table = CSV.parse(data, :headers => true)
- assert_equal(["ra2", nil, "rb2"], table.delete("col2"))
- end
-
- def test_delete_if_row
- ######################
- ### Mixed/Row Mode ###
- ######################
- # verify that we can chain the call
- assert_equal(@table, @table.delete_if { |row| (row["B"] % 2).zero? })
-
- # verify resulting table
- assert_equal(<<-CSV, @table.to_csv)
-A,B,C
-4,5,6
- CSV
- end
-
- def test_delete_if_row_without_block
- ######################
- ### Mixed/Row Mode ###
- ######################
- enum = @table.delete_if
- assert_instance_of(Enumerator, enum)
- assert_equal(@table.size, enum.size)
-
- # verify that we can chain the call
- assert_equal(@table, enum.each { |row| (row["B"] % 2).zero? })
-
- # verify resulting table
- assert_equal(<<-CSV, @table.to_csv)
-A,B,C
-4,5,6
- CSV
- end
-
- def test_delete_if_column
- ###################
- ### Column Mode ###
- ###################
- @table.by_col!
-
- assert_equal(@table, @table.delete_if { |h, v| h > "A" })
- assert_equal(<<-CSV, @table.to_csv)
-A
-1
-4
-7
- CSV
- end
-
- def test_delete_if_column_without_block
- ###################
- ### Column Mode ###
- ###################
- @table.by_col!
-
- enum = @table.delete_if
- assert_instance_of(Enumerator, enum)
- assert_equal(@table.headers.size, enum.size)
-
- assert_equal(@table, enum.each { |h, v| h > "A" })
- assert_equal(<<-CSV, @table.to_csv)
-A
-1
-4
-7
- CSV
- end
-
- def test_delete_headers_only
- ###################
- ### Column Mode ###
- ###################
- @header_only_table.by_col!
-
- # delete by index
- assert_equal([], @header_only_table.delete(0))
- assert_equal(%w[B C], @header_only_table.headers)
-
- # delete by header
- assert_equal([], @header_only_table.delete("C"))
- assert_equal(%w[B], @header_only_table.headers)
- end
-
- def test_values_at
- ##################
- ### Mixed Mode ###
- ##################
- # rows
- assert_equal(@rows.values_at(0, 2), @table.values_at(0, 2))
- assert_equal(@rows.values_at(1..2), @table.values_at(1..2))
-
- # cols
- assert_equal([[1, 3], [4, 6], [7, 9]], @table.values_at("A", "C"))
- assert_equal([[2, 3], [5, 6], [8, 9]], @table.values_at("B".."C"))
-
- ###################
- ### Column Mode ###
- ###################
- @table.by_col!
-
- assert_equal([[1, 3], [4, 6], [7, 9]], @table.values_at(0, 2))
- assert_equal([[1, 3], [4, 6], [7, 9]], @table.values_at("A", "C"))
-
- ################
- ### Row Mode ###
- ################
- @table.by_row!
-
- assert_equal(@rows.values_at(0, 2), @table.values_at(0, 2))
- assert_raise(TypeError) { @table.values_at("A", "C") }
-
- ############################
- ### One Shot Mode Change ###
- ############################
- assert_equal(@rows.values_at(0, 2), @table.values_at(0, 2))
- assert_equal([[1, 3], [4, 6], [7, 9]], @table.by_col.values_at(0, 2))
- assert_equal(@rows.values_at(0, 2), @table.values_at(0, 2))
- end
-
- def test_array_delegation
- assert_not_empty(@table, "Table was empty.")
-
- assert_equal(@rows.size, @table.size)
- end
-
- def test_inspect_shows_current_mode
- str = @table.inspect
- assert_include(str, "mode:#{@table.mode}", "Mode not shown.")
-
- @table.by_col!
- str = @table.inspect
- assert_include(str, "mode:#{@table.mode}", "Mode not shown.")
- end
-
- def test_inspect_encoding_is_ascii_compatible
- assert_send([Encoding, :compatible?,
- Encoding.find("US-ASCII"),
- @table.inspect.encoding],
- "inspect() was not ASCII compatible." )
- end
-
- def test_inspect_with_rows
- additional_rows = [ CSV::Row.new(%w{A B C}, [101, 102, 103]),
- CSV::Row.new(%w{A B C}, [104, 105, 106]),
- CSV::Row.new(%w{A B C}, [107, 108, 109]) ]
- table = CSV::Table.new(@rows + additional_rows)
- str_table = table.inspect
-
- assert_equal(<<-CSV, str_table)
-#<CSV::Table mode:col_or_row row_count:7>
-A,B,C
-1,2,3
-4,5,6
-7,8,9
-101,102,103
-104,105,106
- CSV
- end
-
- def test_dig_mixed
- # by row
- assert_equal(@rows[0], @table.dig(0))
- assert_nil(@table.dig(100)) # empty row
-
- # by col
- assert_equal([2, 5, 8], @table.dig("B"))
- assert_equal([nil] * @rows.size, @table.dig("Z")) # empty col
-
- # by row then col
- assert_equal(2, @table.dig(0, 1))
- assert_equal(6, @table.dig(1, "C"))
-
- # by col then row
- assert_equal(5, @table.dig("B", 1))
- assert_equal(9, @table.dig("C", 2))
- end
-
- def test_dig_by_column
- @table.by_col!
-
- assert_equal([2, 5, 8], @table.dig(1))
- assert_equal([2, 5, 8], @table.dig("B"))
-
- # by col then row
- assert_equal(5, @table.dig("B", 1))
- assert_equal(9, @table.dig("C", 2))
- end
-
- def test_dig_by_row
- @table.by_row!
-
- assert_equal(@rows[1], @table.dig(1))
- assert_raise(TypeError) { @table.dig("B") }
-
- # by row then col
- assert_equal(2, @table.dig(0, 1))
- assert_equal(6, @table.dig(1, "C"))
- end
-
- def test_dig_cell
- table = CSV::Table.new([CSV::Row.new(["A"], [["foo", ["bar", ["baz"]]]])])
-
- # by row, col then cell
- assert_equal("foo", table.dig(0, "A", 0))
- assert_equal(["baz"], table.dig(0, "A", 1, 1))
-
- # by col, row then cell
- assert_equal("foo", table.dig("A", 0, 0))
- assert_equal(["baz"], table.dig("A", 0, 1, 1))
- end
-
- def test_dig_cell_no_dig
- table = CSV::Table.new([CSV::Row.new(["A"], ["foo"])])
-
- # by row, col then cell
- assert_raise(TypeError) do
- table.dig(0, "A", 0)
- end
-
- # by col, row then cell
- assert_raise(TypeError) do
- table.dig("A", 0, 0)
- end
- end
-end
diff --git a/test/csv/write/test_converters.rb b/test/csv/write/test_converters.rb
deleted file mode 100644
index 0e0080b4c5..0000000000
--- a/test/csv/write/test_converters.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-# -*- coding: utf-8 -*-
-# frozen_string_literal: false
-
-require_relative "../helper"
-
-module TestCSVWriteConverters
- def test_one
- assert_equal(%Q[=a,=b,=c\n],
- generate_line(["a", "b", "c"],
- write_converters: ->(value) {"=" + value}))
- end
-
- def test_multiple
- assert_equal(%Q[=a_,=b_,=c_\n],
- generate_line(["a", "b", "c"],
- write_converters: [
- ->(value) {"=" + value},
- ->(value) {value + "_"},
- ]))
- end
-
- def test_nil_value
- assert_equal(%Q[a,NaN,29\n],
- generate_line(["a", nil, 29],
- write_nil_value: "NaN"))
- end
-
- def test_empty_value
- assert_equal(%Q[a,,29\n],
- generate_line(["a", "", 29],
- write_empty_value: nil))
- end
-end
-
-class TestCSVWriteConvertersGenerateLine < Test::Unit::TestCase
- include TestCSVWriteConverters
- extend DifferentOFS
-
- def generate_line(row, **kwargs)
- CSV.generate_line(row, **kwargs)
- end
-end
-
-class TestCSVWriteConvertersGenerate < Test::Unit::TestCase
- include TestCSVWriteConverters
- extend DifferentOFS
-
- def generate_line(row, **kwargs)
- CSV.generate(**kwargs) do |csv|
- csv << row
- end
- end
-end
diff --git a/test/csv/write/test_force_quotes.rb b/test/csv/write/test_force_quotes.rb
deleted file mode 100644
index 622dcb021b..0000000000
--- a/test/csv/write/test_force_quotes.rb
+++ /dev/null
@@ -1,78 +0,0 @@
-# frozen_string_literal: false
-
-require_relative "../helper"
-
-module TestCSVWriteForceQuotes
- def test_default
- assert_equal(%Q[1,2,3#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["1", "2", "3"]))
- end
-
- def test_true
- assert_equal(%Q["1","2","3"#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["1", "2", "3"],
- force_quotes: true))
- end
-
- def test_false
- assert_equal(%Q[1,2,3#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["1", "2", "3"],
- force_quotes: false))
- end
-
- def test_field_name
- assert_equal(%Q["1",2,"3"#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["1", "2", "3"],
- headers: ["a", "b", "c"],
- force_quotes: ["a", :c]))
- end
-
- def test_field_name_without_headers
- force_quotes = ["a", "c"]
- error = assert_raise(ArgumentError) do
- generate_line(["1", "2", "3"],
- force_quotes: force_quotes)
- end
- assert_equal(":headers is required when you use field name " +
- "in :force_quotes: " +
- "#{force_quotes.first.inspect}: #{force_quotes.inspect}",
- error.message)
- end
-
- def test_field_index
- assert_equal(%Q["1",2,"3"#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["1", "2", "3"],
- force_quotes: [0, 2]))
- end
-
- def test_field_unknown
- force_quotes = [1.1]
- error = assert_raise(ArgumentError) do
- generate_line(["1", "2", "3"],
- force_quotes: force_quotes)
- end
- assert_equal(":force_quotes element must be field index or field name: " +
- "#{force_quotes.first.inspect}: #{force_quotes.inspect}",
- error.message)
- end
-end
-
-class TestCSVWriteForceQuotesGenerateLine < Test::Unit::TestCase
- include TestCSVWriteForceQuotes
- extend DifferentOFS
-
- def generate_line(row, **kwargs)
- CSV.generate_line(row, **kwargs)
- end
-end
-
-class TestCSVWriteForceQuotesGenerate < Test::Unit::TestCase
- include TestCSVWriteForceQuotes
- extend DifferentOFS
-
- def generate_line(row, **kwargs)
- CSV.generate(**kwargs) do |csv|
- csv << row
- end
- end
-end
diff --git a/test/csv/write/test_general.rb b/test/csv/write/test_general.rb
deleted file mode 100644
index 4788d99e81..0000000000
--- a/test/csv/write/test_general.rb
+++ /dev/null
@@ -1,246 +0,0 @@
-# -*- coding: utf-8 -*-
-# frozen_string_literal: false
-
-require_relative "../helper"
-
-module TestCSVWriteGeneral
- include CSVHelper
-
- def test_tab
- assert_equal("\t#{$INPUT_RECORD_SEPARATOR}",
- generate_line(["\t"]))
- end
-
- def test_quote_character
- assert_equal(%Q[foo,"""",baz#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["foo", %Q["], "baz"]))
- end
-
- def test_quote_character_double
- assert_equal(%Q[foo,"""""",baz#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["foo", %Q[""], "baz"]))
- end
-
- def test_quote
- assert_equal(%Q[foo,"""bar""",baz#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["foo", %Q["bar"], "baz"]))
- end
-
- def test_quote_lf
- assert_equal(%Q["""\n","""\n"#{$INPUT_RECORD_SEPARATOR}],
- generate_line([%Q["\n], %Q["\n]]))
- end
-
- def test_quote_cr
- assert_equal(%Q["""\r","""\r"#{$INPUT_RECORD_SEPARATOR}],
- generate_line([%Q["\r], %Q["\r]]))
- end
-
- def test_quote_last
- assert_equal(%Q[foo,"bar"""#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["foo", %Q[bar"]]))
- end
-
- def test_quote_lf_last
- assert_equal(%Q[foo,"\nbar"""#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["foo", %Q[\nbar"]]))
- end
-
- def test_quote_lf_value_lf
- assert_equal(%Q[foo,"""\nbar\n"""#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["foo", %Q["\nbar\n"]]))
- end
-
- def test_quote_lf_value_lf_nil
- assert_equal(%Q[foo,"""\nbar\n""",#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["foo", %Q["\nbar\n"], nil]))
- end
-
- def test_cr
- assert_equal(%Q[foo,"\r",baz#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["foo", "\r", "baz"]))
- end
-
- def test_lf
- assert_equal(%Q[foo,"\n",baz#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["foo", "\n", "baz"]))
- end
-
- def test_cr_lf
- assert_equal(%Q[foo,"\r\n",baz#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["foo", "\r\n", "baz"]))
- end
-
- def test_cr_dot_lf
- assert_equal(%Q[foo,"\r.\n",baz#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["foo", "\r.\n", "baz"]))
- end
-
- def test_cr_lf_cr
- assert_equal(%Q[foo,"\r\n\r",baz#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["foo", "\r\n\r", "baz"]))
- end
-
- def test_cr_lf_lf
- assert_equal(%Q[foo,"\r\n\n",baz#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["foo", "\r\n\n", "baz"]))
- end
-
- def test_cr_lf_comma
- assert_equal(%Q["\r\n,"#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["\r\n,"]))
- end
-
- def test_cr_lf_comma_nil
- assert_equal(%Q["\r\n,",#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["\r\n,", nil]))
- end
-
- def test_comma
- assert_equal(%Q[","#{$INPUT_RECORD_SEPARATOR}],
- generate_line([","]))
- end
-
- def test_comma_double
- assert_equal(%Q[",",","#{$INPUT_RECORD_SEPARATOR}],
- generate_line([",", ","]))
- end
-
- def test_comma_and_value
- assert_equal(%Q[foo,"foo,bar",baz#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["foo", "foo,bar", "baz"]))
- end
-
- def test_one_element
- assert_equal(%Q[foo#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["foo"]))
- end
-
- def test_nil_values_only
- assert_equal(%Q[,,#{$INPUT_RECORD_SEPARATOR}],
- generate_line([nil, nil, nil]))
- end
-
- def test_nil_double_only
- assert_equal(%Q[,#{$INPUT_RECORD_SEPARATOR}],
- generate_line([nil, nil]))
- end
-
- def test_nil_values
- assert_equal(%Q[foo,,,#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["foo", nil, nil, nil]))
- end
-
- def test_nil_value_first
- assert_equal(%Q[,foo,baz#{$INPUT_RECORD_SEPARATOR}],
- generate_line([nil, "foo", "baz"]))
- end
-
- def test_nil_value_middle
- assert_equal(%Q[foo,,baz#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["foo", nil, "baz"]))
- end
-
- def test_nil_value_last
- assert_equal(%Q[foo,baz,#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["foo", "baz", nil]))
- end
-
- def test_nil_empty
- assert_equal(%Q[,""#{$INPUT_RECORD_SEPARATOR}],
- generate_line([nil, ""]))
- end
-
- def test_nil_cr
- assert_equal(%Q[,"\r"#{$INPUT_RECORD_SEPARATOR}],
- generate_line([nil, "\r"]))
- end
-
- def test_values
- assert_equal(%Q[foo,bar#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["foo", "bar"]))
- end
-
- def test_semi_colon
- assert_equal(%Q[;#{$INPUT_RECORD_SEPARATOR}],
- generate_line([";"]))
- end
-
- def test_semi_colon_values
- assert_equal(%Q[;,;#{$INPUT_RECORD_SEPARATOR}],
- generate_line([";", ";"]))
- end
-
- def test_tab_values
- assert_equal(%Q[\t,\t#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["\t", "\t"]))
- end
-
- def test_col_sep
- assert_equal(%Q[a;b;;c#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["a", "b", nil, "c"],
- col_sep: ";"))
- assert_equal(%Q[a\tb\t\tc#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["a", "b", nil, "c"],
- col_sep: "\t"))
- end
-
- def test_row_sep
- assert_equal(%Q[a,b,,c\r\n],
- generate_line(["a", "b", nil, "c"],
- row_sep: "\r\n"))
- end
-
- def test_force_quotes
- assert_equal(%Q["1","b","","already ""quoted"""#{$INPUT_RECORD_SEPARATOR}],
- generate_line([1, "b", nil, %Q{already "quoted"}],
- force_quotes: true))
- end
-
- def test_encoding_utf8
- assert_equal(%Q[ã‚,ã„,ã†#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["ã‚" , "ã„", "ã†"]))
- end
-
- def test_encoding_euc_jp
- row = ["ã‚", "ã„", "ã†"].collect {|field| field.encode("EUC-JP")}
- assert_equal(%Q[ã‚,ã„,ã†#{$INPUT_RECORD_SEPARATOR}].encode("EUC-JP"),
- generate_line(row))
- end
-
- def test_encoding_with_default_internal
- with_default_internal(Encoding::UTF_8) do
- row = ["ã‚", "ã„", "ã†"].collect {|field| field.encode("EUC-JP")}
- assert_equal(%Q[ã‚,ã„,ã†#{$INPUT_RECORD_SEPARATOR}].encode("EUC-JP"),
- generate_line(row, encoding: Encoding::EUC_JP))
- end
- end
-
- def test_with_default_internal
- with_default_internal(Encoding::UTF_8) do
- row = ["ã‚", "ã„", "ã†"].collect {|field| field.encode("EUC-JP")}
- assert_equal(%Q[ã‚,ã„,ã†#{$INPUT_RECORD_SEPARATOR}].encode("EUC-JP"),
- generate_line(row))
- end
- end
-end
-
-class TestCSVWriteGeneralGenerateLine < Test::Unit::TestCase
- include TestCSVWriteGeneral
- extend DifferentOFS
-
- def generate_line(row, **kwargs)
- CSV.generate_line(row, **kwargs)
- end
-end
-
-class TestCSVWriteGeneralGenerate < Test::Unit::TestCase
- include TestCSVWriteGeneral
- extend DifferentOFS
-
- def generate_line(row, **kwargs)
- CSV.generate(**kwargs) do |csv|
- csv << row
- end
- end
-end
diff --git a/test/csv/write/test_quote_empty.rb b/test/csv/write/test_quote_empty.rb
deleted file mode 100644
index 70f73dad4a..0000000000
--- a/test/csv/write/test_quote_empty.rb
+++ /dev/null
@@ -1,70 +0,0 @@
-# -*- coding: utf-8 -*-
-# frozen_string_literal: false
-
-require_relative "../helper"
-
-module TestCSVWriteQuoteEmpty
- def test_quote_empty_default
- assert_equal(%Q["""",""#{$INPUT_RECORD_SEPARATOR}],
- generate_line([%Q["], ""]))
- end
-
- def test_quote_empty_false
- assert_equal(%Q["""",#{$INPUT_RECORD_SEPARATOR}],
- generate_line([%Q["], ""],
- quote_empty: false))
- end
-
- def test_empty_default
- assert_equal(%Q[foo,"",baz#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["foo", "", "baz"]))
- end
-
- def test_empty_false
- assert_equal(%Q[foo,,baz#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["foo", "", "baz"],
- quote_empty: false))
- end
-
- def test_empty_only_default
- assert_equal(%Q[""#{$INPUT_RECORD_SEPARATOR}],
- generate_line([""]))
- end
-
- def test_empty_only_false
- assert_equal(%Q[#{$INPUT_RECORD_SEPARATOR}],
- generate_line([""],
- quote_empty: false))
- end
-
- def test_empty_double_default
- assert_equal(%Q["",""#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["", ""]))
- end
-
- def test_empty_double_false
- assert_equal(%Q[,#{$INPUT_RECORD_SEPARATOR}],
- generate_line(["", ""],
- quote_empty: false))
- end
-end
-
-class TestCSVWriteQuoteEmptyGenerateLine < Test::Unit::TestCase
- include TestCSVWriteQuoteEmpty
- extend DifferentOFS
-
- def generate_line(row, **kwargs)
- CSV.generate_line(row, **kwargs)
- end
-end
-
-class TestCSVWriteQuoteEmptyGenerate < Test::Unit::TestCase
- include TestCSVWriteQuoteEmpty
- extend DifferentOFS
-
- def generate_line(row, **kwargs)
- CSV.generate(**kwargs) do |csv|
- csv << row
- end
- end
-end
diff --git a/test/date/test_date_parse.rb b/test/date/test_date_parse.rb
index 0b4a383630..16362e3bff 100644
--- a/test/date/test_date_parse.rb
+++ b/test/date/test_date_parse.rb
@@ -867,9 +867,7 @@ class TestDateParse < Test::Unit::TestCase
h = Date._iso8601(nil)
assert_equal({}, h)
- h = assert_deprecated_warn {Date._iso8601('01-02-03T04:05:06Z'.to_sym)}
- assert_equal([2001, 2, 3, 4, 5, 6, 0],
- h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ assert_raise(TypeError) {Date._iso8601('01-02-03T04:05:06Z'.to_sym)}
end
def test__rfc3339
@@ -889,9 +887,7 @@ class TestDateParse < Test::Unit::TestCase
h = Date._rfc3339(nil)
assert_equal({}, h)
- h = assert_deprecated_warn {Date._rfc3339('2001-02-03T04:05:06Z'.to_sym)}
- assert_equal([2001, 2, 3, 4, 5, 6, 0],
- h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ assert_raise(TypeError) {Date._rfc3339('2001-02-03T04:05:06Z'.to_sym)}
end
def test__xmlschema
@@ -978,9 +974,7 @@ class TestDateParse < Test::Unit::TestCase
h = Date._xmlschema(nil)
assert_equal({}, h)
- h = assert_deprecated_warn {Date._xmlschema('2001-02-03'.to_sym)}
- assert_equal([2001, 2, 3, nil, nil, nil, nil],
- h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ assert_raise(TypeError) {Date._xmlschema('2001-02-03'.to_sym)}
end
def test__rfc2822
@@ -1017,9 +1011,7 @@ class TestDateParse < Test::Unit::TestCase
h = Date._rfc2822(nil)
assert_equal({}, h)
- h = assert_deprecated_warn {Date._rfc2822('Sat, 3 Feb 2001 04:05:06 UT'.to_sym)}
- assert_equal([2001, 2, 3, 4, 5, 6, 0],
- h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ assert_raise(TypeError) {Date._rfc2822('Sat, 3 Feb 2001 04:05:06 UT'.to_sym)}
end
def test__httpdate
@@ -1044,9 +1036,7 @@ class TestDateParse < Test::Unit::TestCase
h = Date._httpdate(nil)
assert_equal({}, h)
- h = assert_deprecated_warn {Date._httpdate('Sat, 03 Feb 2001 04:05:06 GMT'.to_sym)}
- assert_equal([2001, 2, 3, 4, 5, 6, 0],
- h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ assert_raise(TypeError) {Date._httpdate('Sat, 03 Feb 2001 04:05:06 GMT'.to_sym)}
end
def test__jisx0301
@@ -1127,9 +1117,7 @@ class TestDateParse < Test::Unit::TestCase
h = Date._jisx0301(nil)
assert_equal({}, h)
- h = assert_deprecated_warn {Date._jisx0301('H13.02.03T04:05:06.07+0100'.to_sym)}
- assert_equal([2001, 2, 3, 4, 5, 6, 3600],
- h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ assert_raise(TypeError) {Date._jisx0301('H13.02.03T04:05:06.07+0100'.to_sym)}
end
def test_iso8601
diff --git a/test/did_you_mean/core_ext/test_name_error_extension.rb b/test/did_you_mean/core_ext/test_name_error_extension.rb
index c58b78455f..116c7cd7b9 100644
--- a/test/did_you_mean/core_ext/test_name_error_extension.rb
+++ b/test/did_you_mean/core_ext/test_name_error_extension.rb
@@ -49,7 +49,7 @@ class NameErrorExtensionTest < Test::Unit::TestCase
get_message(error)
- assert_match(/^undefined method `sizee' for /,
+ assert_match(/^undefined method [`']sizee' for /,
Marshal.load(Marshal.dump(error)).original_message)
end
end
diff --git a/test/drb/drbtest.rb b/test/drb/drbtest.rb
deleted file mode 100644
index 72220f32ef..0000000000
--- a/test/drb/drbtest.rb
+++ /dev/null
@@ -1,396 +0,0 @@
-# frozen_string_literal: false
-require 'test/unit'
-require 'envutil'
-require 'drb/drb'
-require 'drb/extservm'
-require 'timeout'
-
-module DRbTests
-
-class DRbService
- @@ruby = [EnvUtil.rubybin]
- @@ruby << "-d" if $DEBUG
- def self.add_service_command(nm)
- dir = File.dirname(File.expand_path(__FILE__))
- DRb::ExtServManager.command[nm] = @@ruby + ["#{dir}/#{nm}"]
- end
-
- %w(ut_drb.rb ut_array.rb ut_port.rb ut_large.rb ut_safe1.rb ut_eq.rb).each do |nm|
- add_service_command(nm)
- end
-
- def initialize
- @manager = DRb::ExtServManager.new
- start
- @manager.uri = server.uri
- end
-
- def start
- @server = DRb::DRbServer.new('druby://localhost:0', manager, {})
- end
-
- attr_reader :manager
- attr_reader :server
-
- def ext_service(name)
- EnvUtil.timeout(100, RuntimeError) do
- manager.service(name)
- end
- end
-
- def finish
- server.instance_variable_get(:@grp).list.each {|th| th.join }
- server.stop_service
- manager.instance_variable_get(:@queue)&.push(nil)
- manager.instance_variable_get(:@thread)&.join
- DRb::DRbConn.stop_pool
- end
-end
-
-class Onecky
- include DRbUndumped
- def initialize(n)
- @num = n
- end
-
- def to_i
- @num.to_i
- end
-
- def sleep(n)
- Kernel.sleep(n)
- to_i
- end
-end
-
-class FailOnecky < Onecky
- class OneckyError < RuntimeError; end
- def to_i
- raise(OneckyError, @num.to_s)
- end
-end
-
-class XArray < Array
- def initialize(ary)
- ary.each do |x|
- self.push(x)
- end
- end
-end
-
-module DRbBase
- def setup
- @drb_service ||= DRbService.new
- end
-
- def setup_service(service_name)
- @service_name = service_name
- @ext = @drb_service.ext_service(@service_name)
- @there = @ext.front
- end
-
- def teardown
- return if @omitted
- @ext.stop_service if defined?(@ext) && @ext
- if defined?(@service_name) && @service_name
- @drb_service.manager.unregist(@service_name)
- while (@there&&@there.to_s rescue nil)
- # nop
- end
- signal = /mswin|mingw/ =~ RUBY_PLATFORM ? :KILL : :TERM
- Thread.list.each {|th|
- if th.respond_to?(:pid) && th[:drb_service] == @service_name
- 10.times do
- begin
- Process.kill signal, th.pid
- break
- rescue Errno::ESRCH
- break
- rescue Errno::EPERM # on Windows
- sleep 0.1
- retry
- end
- end
- th.join
- end
- }
- end
- @drb_service.finish
- DRb::DRbConn.stop_pool
- end
-end
-
-module DRbCore
- include DRbBase
-
- def test_00_DRbObject
- ro = DRbObject.new(nil, 'druby://localhost:12345')
- assert_equal('druby://localhost:12345', ro.__drburi)
- assert_equal(nil, ro.__drbref)
-
- ro = DRbObject.new_with_uri('druby://localhost:12345')
- assert_equal('druby://localhost:12345', ro.__drburi)
- assert_equal(nil, ro.__drbref)
-
- ro = DRbObject.new_with_uri('druby://localhost:12345?foobar')
- assert_equal('druby://localhost:12345', ro.__drburi)
- assert_equal(DRb::DRbURIOption.new('foobar'), ro.__drbref)
- end
-
- def test_01
- assert_equal("hello", @there.hello)
- onecky = Onecky.new('3')
- assert_equal(6, @there.sample(onecky, 1, 2))
- ary = @there.to_a
- assert_kind_of(DRb::DRbObject, ary)
-
- assert_respond_to(@there, [:to_a, true])
- assert_respond_to(@there, [:eval, true])
- assert_not_respond_to(@there, [:eval, false])
- assert_not_respond_to(@there, :eval)
- end
-
- def test_01_02_loop
- onecky = Onecky.new('3')
- 50.times do
- assert_equal(6, @there.sample(onecky, 1, 2))
- ary = @there.to_a
- assert_kind_of(DRb::DRbObject, ary)
- end
- end
-
- def test_02_basic_object
- obj = @there.basic_object
- assert_kind_of(DRb::DRbObject, obj)
- assert_equal(1, obj.foo)
- assert_raise(NoMethodError){obj.prot}
- assert_raise(NoMethodError){obj.priv}
- end
-
- def test_02_unknown
- obj = @there.unknown_class
- assert_kind_of(DRb::DRbUnknown, obj)
- assert_equal('DRbTests::Unknown2', obj.name)
-
- obj = @there.unknown_module
- assert_kind_of(DRb::DRbUnknown, obj)
- assert_equal('DRbTests::DRbEx::', obj.name)
-
- assert_raise(DRb::DRbUnknownError) do
- @there.unknown_error
- end
-
- onecky = FailOnecky.new('3')
-
- assert_raise(FailOnecky::OneckyError) do
- @there.sample(onecky, 1, 2)
- end
- end
-
- def test_03
- assert_equal(8, @there.sum(1, 1, 1, 1, 1, 1, 1, 1))
- assert_raise(DRb::DRbConnError) do
- @there.sum(1, 1, 1, 1, 1, 1, 1, 1, 1)
- end
- assert_raise(DRb::DRbConnError) do
- @there.sum('1' * 4096)
- end
- end
-
- def test_04
- assert_respond_to(@there, 'sum')
- assert_not_respond_to(@there, "foobar")
- end
-
- def test_05_eq
- a = @there.to_a[0]
- b = @there.to_a[0]
- assert_not_same(a, b)
- assert_equal(a, b)
- assert_equal(a, @there)
- assert_equal(a.hash, b.hash)
- assert_equal(a.hash, @there.hash)
- assert_operator(a, :eql?, b)
- assert_operator(a, :eql?, @there)
- end
-
- def test_06_timeout
- omit if RUBY_PLATFORM.include?("armv7l-linux")
- omit if RUBY_PLATFORM.include?("sparc-solaris2.10")
- omit if RUBY_PLATFORM.include?("freebsd")
- omit if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? # expecting a certain delay is difficult for --jit-wait CI
- Timeout.timeout(60) do
- ten = Onecky.new(10)
- assert_raise(Timeout::Error) do
- @there.do_timeout(ten)
- end
- assert_raise(Timeout::Error) do
- @there.do_timeout(ten)
- end
- end
- end
-
- def test_07_private_missing
- e = assert_raise(NoMethodError) {
- @there.method_missing(:eval, 'nil')
- }
- assert_match(/^private method \`eval\'/, e.message)
-
- e = assert_raise(NoMethodError) {
- @there.call_private_method
- }
- assert_match(/^private method \`call_private_method\'/, e.message)
- end
-
- def test_07_protected_missing
- e = assert_raise(NoMethodError) {
- @there.call_protected_method
- }
- assert_match(/^protected method \`call_protected_method\'/, e.message)
- end
-
- def test_07_public_missing
- e = assert_raise(NoMethodError) {
- @there.method_missing(:undefined_method_test)
- }
- assert_match(/^undefined method \`undefined_method_test\'/, e.message)
- end
-
- def test_07_send_missing
- assert_raise(DRb::DRbConnError) do
- @there.method_missing(:__send__, :to_s)
- end
- assert_equal(true, @there.missing)
- end
-
- def test_08_here
- ro = DRbObject.new(nil, DRb.uri)
- assert_kind_of(String, ro.to_s)
-
- ro = DRbObject.new_with_uri(DRb.uri)
- assert_kind_of(String, ro.to_s)
- end
-
- def uri_concat_option(uri, opt)
- "#{uri}?#{opt}"
- end
-
- def test_09_option
- uri = uri_concat_option(@there.__drburi, "foo")
- ro = DRbObject.new_with_uri(uri)
- assert_equal(ro.__drburi, @there.__drburi)
- assert_equal(3, ro.size)
-
- uri = uri_concat_option(@there.__drburi, "")
- ro = DRbObject.new_with_uri(uri)
- assert_equal(ro.__drburi, @there.__drburi)
- assert_equal(DRb::DRbURIOption.new(''), ro.__drbref)
-
- uri = uri_concat_option(@there.__drburi, "hello?world")
- ro = DRbObject.new_with_uri(uri)
- assert_equal(DRb::DRbURIOption.new('hello?world'), ro.__drbref)
-
- uri = uri_concat_option(@there.__drburi, "?hello?world")
- ro = DRbObject.new_with_uri(uri)
- assert_equal(DRb::DRbURIOption.new('?hello?world'), ro.__drbref)
- end
-
- def test_10_yield
- @there.simple_hash.each do |k, v|
- assert_kind_of(String, k)
- assert_kind_of(Symbol, v)
- end
- end
-
- def test_10_yield_undumped
- @there.xarray2_hash.each do |k, v|
- assert_kind_of(String, k)
- assert_kind_of(DRbObject, v)
- end
- end
-
- def test_11_remote_no_method_error
- assert_raise(DRb::DRbRemoteError) do
- @there.remote_no_method_error
- end
- begin
- @there.remote_no_method_error
- rescue
- error = $!
- assert_match(/^undefined method .*\(NoMethodError\)/, error.message)
- assert_equal('NoMethodError', error.reason)
- end
- end
-end
-
-module DRbAry
- include DRbBase
-
- def test_01
- assert_kind_of(DRb::DRbObject, @there)
- end
-
- def test_02_collect
- ary = @there.collect do |x| x + x end
- assert_kind_of(Array, ary)
- assert_equal([2, 4, 'IIIIII', 8, 'fivefive', 12], ary)
- end
-
- def test_03_redo
- ary = []
- count = 0
- @there.each do |x|
- count += 1
- ary.push x
- redo if count == 3
- end
- assert_equal([1, 2, 'III', 'III', 4, 'five', 6], ary)
- end
-
- # retry in block is not supported on ruby 1.9
- #def test_04_retry
- # retried = false
- # ary = []
- # @there.each do |x|
- # ary.push x
- # if x == 4 && !retried
- # retried = true
- # retry
- # end
- # end
- # assert_equal([1, 2, 'III', 4, 1, 2, 'III', 4, 'five', 6], ary)
- #end
-
- def test_05_break
- ary = []
- @there.each do |x|
- ary.push x
- break if x == 4
- end
- assert_equal([1, 2, 'III', 4], ary)
- end
-
- def test_06_next
- ary = []
- @there.each do |x|
- next if String === x
- ary.push x
- end
- assert_equal([1, 2, 4, 6], ary)
- end
-
- class_eval <<EOS
- def test_07_break_18
- ary = []
- result = @there.each do |x|
- ary.push x
- break(:done) if x == 4
- end
- assert_equal([1, 2, 'III', 4], ary)
- assert_equal(:done, result)
- end
-EOS
-
-end
-
-end
diff --git a/test/drb/ignore_test_drb.rb b/test/drb/ignore_test_drb.rb
deleted file mode 100644
index 996a554c0f..0000000000
--- a/test/drb/ignore_test_drb.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-# frozen_string_literal: false
-require 'drbtest'
-
-module DRbTests
-
-class TestDRbReusePort < Test::Unit::TestCase
- include DRbAry
-
- def setup
- setup_service 'ut_port.rb'
- end
-end
-
-end
diff --git a/test/drb/test_acl.rb b/test/drb/test_acl.rb
deleted file mode 100644
index ea7b32e76f..0000000000
--- a/test/drb/test_acl.rb
+++ /dev/null
@@ -1,207 +0,0 @@
-# frozen_string_literal: false
-# acltest.rb - ACL unit test
-# Copyright (c) 2000 Masatoshi SEKI
-#
-# acltest.rb is copyrighted free software by Masatoshi SEKI.
-# You can redistribute it and/or modify it under the same terms as Ruby.
-
-require 'test/unit'
-require 'drb/acl'
-
-module DRbTests
-
-class SampleHosts
- def initialize
- list = %w(127.0.0.1 localhost
- 192.168.1.1 x68k.linux.or.jp
- 192.168.1.2 lc630.macos.or.jp
- 192.168.1.3 lib30.win32.or.jp
- 192.168.1.4 ns00.linux.or.jp
- 192.168.1.5 yum.macos.or.jp
- ::ffff:192.168.1.5 ipv6.macos.or.jp
- ::192.168.1.5 too.yumipv6.macos.or.jp
- 192.168.1.254 comstarz.foo.or.jp)
-
- @hostlist = Array.new(list.size / 2)
- @hostlist.each_index do |idx|
- @hostlist[idx] = ["AF_INET", 10000, list[idx * 2 + 1], list[idx * 2]]
- end
-
- @hosts = Hash.new
- @hostlist.each do |h|
- @hosts[h[2].split('.')[0]] = h
- end
- end
- attr_reader(:hostlist, :hosts)
-end
-
-
-class ACLEntryTest < Test::Unit::TestCase
- HOSTS = SampleHosts.new
-
- def setup
- @hostlist = HOSTS.hostlist
- @hosts = HOSTS.hosts
- end
-
- def test_all
- a = ACL::ACLEntry.new("*")
- b = ACL::ACLEntry.new("all")
- @hostlist.each do |h|
- assert_operator(a, :match, h)
- assert_operator(b, :match, h)
- end
- end
-
- def test_ip_v6
- a = ACL::ACLEntry.new('::ffff:192.0.0.0/104')
- assert_not_operator(a, :match, @hosts['localhost'])
- assert_operator(a, :match, @hosts['yum'])
- assert_operator(a, :match, @hosts['ipv6'])
- assert_not_operator(a, :match, @hosts['too'])
- end
-
- def test_ip
- a = ACL::ACLEntry.new('192.0.0.0/8')
- assert_not_operator(a, :match, @hosts['localhost'])
- assert_operator(a, :match, @hosts['yum'])
-
- a = ACL::ACLEntry.new('192.168.1.0/255.255.255.0')
- assert_not_operator(a, :match, @hosts['localhost'])
- assert_operator(a, :match, @hosts['yum'])
- assert_operator(a, :match, @hosts['x68k'])
-
- a = ACL::ACLEntry.new('192.168.1.0/24')
- assert_not_operator(a, :match, @hosts['localhost'])
- assert_operator(a, :match, @hosts['yum'])
- assert_operator(a, :match, @hosts['x68k'])
-
- a = ACL::ACLEntry.new('92.0.0.0/8')
- assert_not_operator(a, :match, @hosts['localhost'])
- assert_not_operator(a, :match, @hosts['yum'])
- assert_not_operator(a, :match, @hosts['x68k'])
-
- a = ACL::ACLEntry.new('127.0.0.0/255.0.0.0')
- assert_operator(a, :match, @hosts['localhost'])
- assert_not_operator(a, :match, @hosts['yum'])
- assert_not_operator(a, :match, @hosts['x68k'])
-
- assert_raise(IPAddr::InvalidPrefixError) {
- ACL::ACLEntry.new('192.168.0.0/33')
- }
- assert_raise(IPAddr::InvalidPrefixError) {
- ACL::ACLEntry.new('192.168.0.0/255.255.0.255')
- }
- end
-
- def test_name
- a = ACL::ACLEntry.new('*.jp')
- assert_not_operator(a, :match, @hosts['localhost'])
- assert_operator(a, :match, @hosts['yum'])
-
- a = ACL::ACLEntry.new('yum.*.jp')
- assert_operator(a, :match, @hosts['yum'])
- assert_not_operator(a, :match, @hosts['lc630'])
-
- a = ACL::ACLEntry.new('*.macos.or.jp')
- assert_operator(a, :match, @hosts['yum'])
- assert_operator(a, :match, @hosts['lc630'])
- assert_not_operator(a, :match, @hosts['lib30'])
- end
-end
-
-class ACLListTest < Test::Unit::TestCase
- HOSTS = SampleHosts.new
-
- def setup
- @hostlist = HOSTS.hostlist
- @hosts = HOSTS.hosts
- end
-
- private
- def build(list)
- acl= ACL::ACLList.new
- list.each do |s|
- acl.add s
- end
- acl
- end
-
- public
- def test_all_1
- a = build(%w(all))
- @hostlist.each do |h|
- assert_operator(a, :match, h)
- end
- end
-
- def test_all_2
- a = build(%w(localhost 127.0.0.0/8 yum.* *))
- @hostlist.each do |h|
- assert_operator(a, :match, h)
- end
- end
-
- def test_1
- a = build(%w(192.168.1.0/255.255.255.252 yum.*.jp))
- assert_operator(a, :match, @hosts['x68k'])
- assert_operator(a, :match, @hosts['lc630'])
- assert_operator(a, :match, @hosts['lib30'])
- assert_not_operator(a, :match, @hosts['ns00'])
- assert_operator(a, :match, @hosts['yum'])
- end
-
- def test_2
- a = build(%w(*.linux.or.jp))
- assert_not_operator(a, :match, @hosts['yum'])
- assert_operator(a, :match, @hosts['x68k'])
- assert_not_operator(a, :match, @hosts['lc630'])
- end
-end
-
-class ACLTest < Test::Unit::TestCase
- HOSTS = SampleHosts.new
-
- def setup
- @hostlist = HOSTS.hostlist
- @hosts = HOSTS.hosts
- end
-
- def test_0
- a = ACL.new
- @hostlist.each do |h|
- assert_operator(a, :allow_addr?, h)
- end
- end
-
- def test_not_0
- a = ACL.new([], ACL::ALLOW_DENY)
- @hostlist.each do |h|
- assert_not_operator(a, :allow_addr?, h)
- end
- end
-
- def test_1
- data = %w(deny all
- allow localhost
- allow x68k.*)
-
- a = ACL.new(data)
- assert_operator(a, :allow_addr?, @hosts['x68k'])
- assert_operator(a, :allow_addr?, @hosts['localhost'])
- assert_not_operator(a, :allow_addr?, @hosts['lc630'])
- end
-
- def test_not_1
- data = %w(deny 192.0.0.0/8
- allow localhost
- allow x68k.*)
-
- a = ACL.new(data, ACL::ALLOW_DENY)
- assert_not_operator(a, :allow_addr?, @hosts['x68k'])
- assert_operator(a, :allow_addr?, @hosts['localhost'])
- assert_not_operator(a, :allow_addr?, @hosts['lc630'])
- end
-end
-
-end
diff --git a/test/drb/test_drb.rb b/test/drb/test_drb.rb
deleted file mode 100644
index 11e2219bad..0000000000
--- a/test/drb/test_drb.rb
+++ /dev/null
@@ -1,371 +0,0 @@
-# frozen_string_literal: false
-require_relative 'drbtest'
-
-module DRbTests
-
-class TestDRbCore < Test::Unit::TestCase
- include DRbCore
-
- def setup
- super
- setup_service 'ut_drb.rb'
- end
-end
-
-module DRbYield
- include DRbBase
-
- def test_01_one
- @there.echo_yield_1([]) {|one|
- assert_equal([], one)
- }
-
- @there.echo_yield_1(1) {|one|
- assert_equal(1, one)
- }
-
- @there.echo_yield_1(nil) {|one|
- assert_equal(nil, one)
- }
- end
-
- def test_02_two
- @there.echo_yield_2([], []) {|one, two|
- assert_equal([], one)
- assert_equal([], two)
- }
-
- @there.echo_yield_2(1, 2) {|one, two|
- assert_equal(1, one)
- assert_equal(2, two)
- }
-
- @there.echo_yield_2(3, nil) {|one, two|
- assert_equal(3, one)
- assert_equal(nil, two)
- }
-
- @there.echo_yield_1([:key, :value]) {|one, two|
- assert_equal(:key, one)
- assert_equal(:value, two)
- }
- end
-
- def test_03_many
- @there.echo_yield_0 {|*s|
- assert_equal([], s)
- }
- @there.echo_yield(nil) {|*s|
- assert_equal([nil], s)
- }
- @there.echo_yield(1) {|*s|
- assert_equal([1], s)
- }
- @there.echo_yield(1, 2) {|*s|
- assert_equal([1, 2], s)
- }
- @there.echo_yield(1, 2, 3) {|*s|
- assert_equal([1, 2, 3], s)
- }
- @there.echo_yield([], []) {|*s|
- assert_equal([[], []], s)
- }
- @there.echo_yield([]) {|*s|
- assert_equal([[]], s) # !
- }
- end
-
- def test_04_many_to_one
- @there.echo_yield_0 {|*s|
- assert_equal([], s)
- }
- @there.echo_yield(nil) {|*s|
- assert_equal([nil], s)
- }
- @there.echo_yield(1) {|*s|
- assert_equal([1], s)
- }
- @there.echo_yield(1, 2) {|*s|
- assert_equal([1, 2], s)
- }
- @there.echo_yield(1, 2, 3) {|*s|
- assert_equal([1, 2, 3], s)
- }
- @there.echo_yield([], []) {|*s|
- assert_equal([[], []], s)
- }
- @there.echo_yield([]) {|*s|
- assert_equal([[]], s)
- }
- end
-
- def test_05_array_subclass
- @there.xarray_each {|x| assert_kind_of(XArray, x)}
- @there.xarray_each {|*x| assert_kind_of(XArray, x[0])}
- end
-end
-
-class TestDRbYield < Test::Unit::TestCase
- include DRbYield
-
- def setup
- super
- setup_service 'ut_drb.rb'
- end
-end
-
-class TestDRbRubyYield < Test::Unit::TestCase
- include DRbYield
-
- def setup
- @there = self
- super
- end
-
- def echo_yield(*arg)
- yield(*arg)
- end
-
- def echo_yield_0
- yield
- end
-
- def echo_yield_1(a)
- yield(a)
- end
-
- def echo_yield_2(a, b)
- yield(a, b)
- end
-
- def xarray_each
- xary = [XArray.new([0])]
- xary.each do |x|
- yield(x)
- end
- end
-
-end
-
-class TestDRbRuby18Yield < Test::Unit::TestCase
- include DRbYield
-
- class YieldTest18
- def echo_yield(*arg, &proc)
- proc.call(*arg)
- end
-
- def echo_yield_0(&proc)
- proc.call
- end
-
- def echo_yield_1(a, &proc)
- proc.call(a)
- end
-
- def echo_yield_2(a, b, &proc)
- proc.call(a, b)
- end
-
- def xarray_each(&proc)
- xary = [XArray.new([0])]
- xary.each(&proc)
- end
-
- end
-
- def setup
- @there = YieldTest18.new
- super
- end
-end
-
-class TestDRbAry < Test::Unit::TestCase
- include DRbAry
-
- def setup
- super
- setup_service 'ut_array.rb'
- end
-end
-
-class TestDRbMServer < Test::Unit::TestCase
- include DRbBase
-
- def setup
- super
- setup_service 'ut_drb.rb'
- @server = (1..3).collect do |n|
- DRb::DRbServer.new("druby://localhost:0", Onecky.new(n.to_s))
- end
- end
-
- def teardown
- @server.each do |s|
- s.stop_service
- end
- super
- end
-
- def test_01
- assert_equal(6, @there.sample(@server[0].front, @server[1].front, @server[2].front))
- end
-end
-
-class TestDRbSafe1 < Test::Unit::TestCase
- include DRbAry
- def setup
- super
- setup_service 'ut_safe1.rb'
- end
-end
-
-class TestDRbLarge < Test::Unit::TestCase
- include DRbBase
-
- def setup
- super
- setup_service 'ut_large.rb'
- end
-
- def test_01_large_ary
- ary = [2] * 10240
- assert_equal(10240, @there.size(ary))
- assert_equal(20480, @there.sum(ary))
- assert_equal(2 ** 10240, @there.multiply(ary))
- assert_equal(2, @there.avg(ary))
- assert_equal(2, @there.median(ary))
- end
-
- def test_02_large_ary
- ary = ["Hello, World"] * 10240
- assert_equal(10240, @there.size(ary))
- assert_equal(ary[0..ary.length].inject(:+), @there.sum(ary))
- assert_raise(TypeError) {@there.multiply(ary)}
- assert_raise(TypeError) {@there.avg(ary)}
- assert_raise(TypeError) {@there.median(ary)}
- end
-
- def test_03_large_ary
- ary = [Thread.current] * 10240
- assert_equal(10240, @there.size(ary))
- end
-
- def test_04_many_arg
- assert_raise(DRb::DRbConnError) {
- @there.arg_test(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)
- }
- end
-
- def test_05_too_large_ary
- ary = ["Hello, World"] * 102400
- exception = nil
- begin
- @there.size(ary)
- rescue StandardError
- exception = $!
- end
- assert_kind_of(StandardError, exception)
- end
-
- def test_06_array_operations
- ary = [1,50,3,844,7,45,23]
- assert_equal(7, @there.size(ary))
- assert_equal(973, @there.sum(ary))
- assert_equal(917217000, @there.multiply(ary))
- assert_equal(139.0, @there.avg(ary))
- assert_equal(23.0, @there.median(ary))
-
- ary2 = [1,2,3,4]
- assert_equal(4, @there.size(ary2))
- assert_equal(10, @there.sum(ary2))
- assert_equal(24, @there.multiply(ary2))
- assert_equal(2.5, @there.avg(ary2))
- assert_equal(2.5, @there.median(ary2))
-
- end
-
- def test_07_one_element_array
- ary = [50]
- assert_equal(1, @there.size(ary))
- assert_equal(50, @there.sum(ary))
- assert_equal(50, @there.multiply(ary))
- assert_equal(50.0, @there.avg(ary))
- assert_equal(50.0, @there.median(ary))
- end
-
- def test_08_empty_array
- ary = []
- assert_equal(0, @there.size(ary))
- assert_equal(nil, @there.sum(ary))
- assert_equal(nil, @there.multiply(ary))
- assert_equal(nil, @there.avg(ary))
- assert_equal(nil, @there.median(ary))
- end
-end
-
-class TestBug4409 < Test::Unit::TestCase
- include DRbBase
-
- def setup
- super
- setup_service 'ut_eq.rb'
- end
-
- def test_bug4409
- foo = @there.foo
- assert_operator(@there, :foo?, foo)
- end
-end
-
-class TestDRbAnyToS < Test::Unit::TestCase
- class BO < BasicObject
- end
-
- def test_any_to_s
- server = DRb::DRbServer.new('druby://localhost:0')
- server.singleton_class.send(:public, :any_to_s)
- assert_equal("foo:String", server.any_to_s("foo"))
- assert_match(/\A#<DRbTests::TestDRbAnyToS::BO:0x[0-9a-f]+>\z/, server.any_to_s(BO.new))
- server.stop_service
- server.thread.join
- DRb::DRbConn.stop_pool
- end
-end
-
-class TestDRbTCP < Test::Unit::TestCase
- def test_immediate_close
- omit 'MinGW leaks a thread in this test' if /mingw/ =~ RUBY_PLATFORM
- server = DRb::DRbServer.new('druby://localhost:0')
- host, port, = DRb::DRbTCPSocket.send(:parse_uri, server.uri)
- socket = TCPSocket.open host, port
- socket.shutdown
- socket.close
- client = DRb::DRbTCPSocket.new(server.uri, socket)
- assert client
- ensure
- client&.close
- socket&.close
- server&.stop_service
- server&.thread&.join
- DRb::DRbConn.stop_pool
- end
-end
-
-class TestBug16634 < Test::Unit::TestCase
- include DRbBase
-
- def setup
- super
- setup_service 'ut_drb.rb'
- end
-
- def test_bug16634
- assert_equal(42, @there.keyword_test1(a: 42))
- assert_equal("default", @there.keyword_test2)
- assert_equal(42, @there.keyword_test2(b: 42))
- assert_equal({:a=>42, :b=>42}, @there.keyword_test3(a: 42, b: 42))
- end
-end
-
-end
diff --git a/test/drb/test_drbobject.rb b/test/drb/test_drbobject.rb
deleted file mode 100644
index 2b0e2061ee..0000000000
--- a/test/drb/test_drbobject.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-require 'test/unit'
-require 'drb'
-require 'drb/timeridconv'
-require 'drb/weakidconv'
-
-module DRbObjectTest
- class Foo
- def initialize
- @foo = 'foo'
- end
- end
-
- def teardown
- DRb.stop_service
- DRb::DRbConn.stop_pool
- end
-
- def drb_eq(obj)
- proxy = DRbObject.new(obj)
- assert_equal(obj, DRb.to_obj(proxy.__drbref))
- end
-
- def test_DRbObject_id_dereference
- drb_eq(Foo.new)
- drb_eq(Foo)
- drb_eq(File)
- drb_eq(Enumerable)
- drb_eq(nil)
- drb_eq(1)
- drb_eq($stdout)
- drb_eq([])
- end
-end
-
-class TestDRbObject < Test::Unit::TestCase
- include DRbObjectTest
-
- def setup
- DRb.start_service
- end
-end
-
-class TestDRbObjectTimerIdConv < Test::Unit::TestCase
- include DRbObjectTest
-
- def setup
- @idconv = DRb::TimerIdConv.new
- DRb.start_service(nil, nil, {:idconv => @idconv})
- end
-
- def teardown
- super
- # stop DRb::TimerIdConv::TimerHolder2#on_gc
- @idconv.instance_eval do
- @holder.instance_eval do
- @expires = nil
- end
- end
- GC.start
- end
-end
-
-class TestDRbObjectWeakIdConv < Test::Unit::TestCase
- include DRbObjectTest
-
- def setup
- DRb.start_service(nil, nil, {:idconv => DRb::WeakIdConv.new})
- end
-end
diff --git a/test/drb/test_drbssl.rb b/test/drb/test_drbssl.rb
deleted file mode 100644
index 0c7e39ca70..0000000000
--- a/test/drb/test_drbssl.rb
+++ /dev/null
@@ -1,80 +0,0 @@
-# frozen_string_literal: false
-require_relative 'drbtest'
-
-begin
- require 'drb/ssl'
-rescue LoadError
-end
-
-module DRbTests
-
-if Object.const_defined?("OpenSSL")
-
-
-class DRbSSLService < DRbService
- %w(ut_drb_drbssl.rb ut_array_drbssl.rb).each do |nm|
- add_service_command(nm)
- end
-
- def start
- config = Hash.new
-
- config[:SSLVerifyMode] = OpenSSL::SSL::VERIFY_PEER
- config[:SSLVerifyCallback] = lambda{ |ok,x509_store|
- true
- }
- begin
- data = open("sample.key"){|io| io.read }
- config[:SSLPrivateKey] = OpenSSL::PKey::RSA.new(data)
- data = open("sample.crt"){|io| io.read }
- config[:SSLCertificate] = OpenSSL::X509::Certificate.new(data)
- rescue
- # $stderr.puts "Switching to use self-signed certificate"
- config[:SSLCertName] =
- [ ["C","JP"], ["O","Foo.DRuby.Org"], ["CN", "Sample"] ]
- end
-
- @server = DRb::DRbServer.new('drbssl://localhost:0', manager, config)
- end
-end
-
-class TestDRbSSLCore < Test::Unit::TestCase
- include DRbCore
- def setup
- if RUBY_PLATFORM.match?(/mswin|mingw/)
- @omitted = true
- omit 'This test seems to randomly hang on Windows'
- end
- @drb_service = DRbSSLService.new
- super
- setup_service 'ut_drb_drbssl.rb'
- end
-
- def test_02_unknown
- end
-
- def test_01_02_loop
- end
-
- def test_05_eq
- end
-end
-
-class TestDRbSSLAry < Test::Unit::TestCase
- include DRbAry
- def setup
- if RUBY_PLATFORM.match?(/mswin|mingw/)
- @omitted = true
- omit 'This test seems to randomly hang on Windows'
- end
- LeakChecker.skip if defined?(LeakChecker)
- @drb_service = DRbSSLService.new
- super
- setup_service 'ut_array_drbssl.rb'
- end
-end
-
-
-end
-
-end
diff --git a/test/drb/test_drbunix.rb b/test/drb/test_drbunix.rb
deleted file mode 100644
index 95b3c3ca91..0000000000
--- a/test/drb/test_drbunix.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-# frozen_string_literal: false
-require_relative 'drbtest'
-
-begin
- require 'drb/unix'
-rescue LoadError
-end
-
-module DRbTests
-
-if Object.const_defined?("UNIXServer")
-
-
-class DRbUNIXService < DRbService
- %w(ut_drb_drbunix.rb ut_array_drbunix.rb).each do |nm|
- add_service_command(nm)
- end
-
- def start
- @server = DRb::DRbServer.new('drbunix:', manager, {})
- end
-end
-
-class TestDRbUNIXCore < Test::Unit::TestCase
- include DRbCore
- def setup
- @drb_service = DRbUNIXService.new
- super
- setup_service 'ut_drb_drbunix.rb'
- end
-
- def test_02_unknown
- end
-
- def test_01_02_loop
- end
-
- def test_05_eq
- end
-
- def test_bad_uri
- assert_raise(DRb::DRbBadURI) do
- DRb::DRbServer.new("badfile\n""drbunix:")
- end
- end
-end
-
-class TestDRbUNIXAry < Test::Unit::TestCase
- include DRbAry
- def setup
- @drb_service = DRbUNIXService.new
- super
- setup_service 'ut_array_drbunix.rb'
- end
-end
-
-
-end
-
-end
diff --git a/test/drb/ut_array.rb b/test/drb/ut_array.rb
deleted file mode 100644
index d13dda3d8e..0000000000
--- a/test/drb/ut_array.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: false
-require 'drb/drb'
-require 'drb/extserv'
-
-if __FILE__ == $0
- def ARGV.shift
- it = super()
- raise "usage: #{$0} <uri> <name>" unless it
- it
- end
-
- DRb.start_service('druby://localhost:0', [1, 2, 'III', 4, "five", 6])
- es = DRb::ExtServ.new(ARGV.shift, ARGV.shift)
- DRb.thread.join
- es.stop_service if es.alive?
-end
-
diff --git a/test/drb/ut_array_drbssl.rb b/test/drb/ut_array_drbssl.rb
deleted file mode 100644
index 5938d9ff3d..0000000000
--- a/test/drb/ut_array_drbssl.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-# frozen_string_literal: false
-require 'drb/drb'
-require 'drb/extserv'
-require 'drb/ssl'
-
-if __FILE__ == $0
- def ARGV.shift
- it = super()
- raise "usage: #{$0} <uri> <name>" unless it
- it
- end
-
- module DRbTests
-
- TEST_KEY_DH1024 = OpenSSL::PKey::DH.new <<-_end_of_pem_
------BEGIN DH PARAMETERS-----
-MIGHAoGBAKnKQ8MNK6nYZzLrrcuTsLxuiJGXoOO5gT+tljOTbHBuiktdMTITzIY0
-pFxIvjG05D7HoBZQfrR0c92NGWPkAiCkhQKB8JCbPVzwNLDy6DZ0pmofDKrEsYHG
-AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC
------END DH PARAMETERS-----
- _end_of_pem_
-
- end
-
- config = Hash.new
- config[:SSLTmpDhCallback] = proc { DRbTests::TEST_KEY_DH1024 }
- config[:SSLVerifyMode] = OpenSSL::SSL::VERIFY_PEER
- config[:SSLVerifyCallback] = lambda{|ok,x509_store|
- true
- }
- config[:SSLCertName] =
- [ ["C","JP"], ["O","Foo.DRuby.Org"], ["CN", "Sample"] ]
-
- DRb.start_service('drbssl://localhost:0', [1, 2, 'III', 4, "five", 6], config)
- es = DRb::ExtServ.new(ARGV.shift, ARGV.shift)
- DRb.thread.join
- es.stop_service if es.alive?
-end
-
diff --git a/test/drb/ut_array_drbunix.rb b/test/drb/ut_array_drbunix.rb
deleted file mode 100644
index b656cdaddd..0000000000
--- a/test/drb/ut_array_drbunix.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: false
-require 'drb/drb'
-require 'drb/extserv'
-
-if __FILE__ == $0
- def ARGV.shift
- it = super()
- raise "usage: #{$0} <uri> <name>" unless it
- it
- end
-
- DRb.start_service('drbunix:', [1, 2, 'III', 4, "five", 6])
- es = DRb::ExtServ.new(ARGV.shift, ARGV.shift)
- DRb.thread.join
- es.stop_service if es.alive?
-end
-
diff --git a/test/drb/ut_drb.rb b/test/drb/ut_drb.rb
deleted file mode 100644
index 7c0603b009..0000000000
--- a/test/drb/ut_drb.rb
+++ /dev/null
@@ -1,189 +0,0 @@
-# frozen_string_literal: false
-require 'drb/drb'
-require 'drb/extserv'
-require 'timeout'
-
-module DRbTests
-
-class XArray < Array
- def initialize(ary)
- ary.each do |x|
- self.push(x)
- end
- end
-end
-
-class XArray2 < XArray
- include DRbUndumped
-end
-
-class Unknown2
- def initialize
- @foo = 'unknown2'
- end
-end
-
-class DRbEx
- include DRbUndumped
-
- class FooBar
- def initialize
- @foo = 'bar'
- end
- end
-
- class UError < RuntimeError; end
-
- def initialize
- @xary2_hash = nil
- @hash = nil
- @hello = 'hello'
- end
- attr_reader :hello
-
- def sample(a, b, c)
- a.to_i + b.to_i + c.to_i
- end
-
- def sum(*a)
- s = 0
- a.each do |e|
- s += e.to_i
- end
- s
- end
-
- def do_timeout(n)
- Timeout.timeout(0.1) do
- n.sleep(2)
- end
- end
-
- def unknown_module
- FooBar.new
- end
-
- class BO < ::BasicObject
- def foo; 1 end
- protected def prot; 2; end
- private def priv; 3; end
- end
- def basic_object
- @basic_object = BO.new
- end
-
- def unknown_class
- Unknown2.new
- end
-
- def unknown_error
- raise UError
- end
-
- def remote_no_method_error
- invoke_no_method(self)
- end
-
- def test_yield
- yield
- yield([])
- yield(*[])
- end
-
- def echo_yield(*arg)
- yield(*arg)
- nil
- end
-
- def echo_yield_0
- yield
- nil
- end
-
- def echo_yield_1(one)
- yield(one)
- nil
- end
-
- def echo_yield_2(one, two)
- yield(one, two)
- nil
- end
-
- def xarray_each
- xary = [XArray.new([0])]
- xary.each do |x|
- yield(x)
- end
- nil
- end
-
- def xarray2_hash
- unless @xary2_hash
- @xary2_hash = { "a" => XArray2.new([0]), "b" => XArray2.new([1]) }
- end
- DRbObject.new(@xary2_hash)
- end
-
- def simple_hash
- unless @hash
- @hash = { 'a'=>:a, 'b'=>:b }
- end
- DRbObject.new(@hash)
- end
-
- def [](key)
- key.to_s
- end
-
- def to_a
- [self]
- end
-
- def method_missing(msg, *a, &b)
- if msg == :missing
- return true
- else
- super(msg, *a, &b)
- end
- end
-
- def keyword_test1(a:)
- a
- end
-
- def keyword_test2(b: "default")
- b
- end
-
- def keyword_test3(**opt)
- opt
- end
-
- private
- def call_private_method
- true
- end
-
- protected
- def call_protected_method
- true
- end
-end
-
-end
-
-if __FILE__ == $0
- def ARGV.shift
- it = super()
- raise "usage: #{$0} <manager-uri> <name>" unless it
- it
- end
-
- DRb::DRbServer.default_argc_limit(8)
- DRb::DRbServer.default_load_limit(4096)
- DRb.start_service('druby://localhost:0', DRbTests::DRbEx.new)
- es = DRb::ExtServ.new(ARGV.shift, ARGV.shift)
- DRb.thread.join
- es.stop_service if es.alive?
-end
diff --git a/test/drb/ut_drb_drbssl.rb b/test/drb/ut_drb_drbssl.rb
deleted file mode 100644
index c8251716d6..0000000000
--- a/test/drb/ut_drb_drbssl.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# frozen_string_literal: false
-require_relative "ut_drb"
-require 'drb/ssl'
-
-if __FILE__ == $0
- def ARGV.shift
- it = super()
- raise "usage: #{$0} <manager-uri> <name>" unless it
- it
- end
-
- module DRbTests
-
- TEST_KEY_DH1024 = OpenSSL::PKey::DH.new <<-_end_of_pem_
------BEGIN DH PARAMETERS-----
-MIGHAoGBAKnKQ8MNK6nYZzLrrcuTsLxuiJGXoOO5gT+tljOTbHBuiktdMTITzIY0
-pFxIvjG05D7HoBZQfrR0c92NGWPkAiCkhQKB8JCbPVzwNLDy6DZ0pmofDKrEsYHG
-AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC
------END DH PARAMETERS-----
- _end_of_pem_
-
- end
-
- config = Hash.new
- config[:SSLTmpDhCallback] = proc { DRbTests::TEST_KEY_DH1024 }
- config[:SSLVerifyMode] = OpenSSL::SSL::VERIFY_PEER
- config[:SSLVerifyCallback] = lambda{|ok,x509_store|
- true
- }
- config[:SSLCertName] =
- [ ["C","JP"], ["O","Foo.DRuby.Org"], ["CN", "Sample"] ]
-
- DRb::DRbServer.default_argc_limit(8)
- DRb::DRbServer.default_load_limit(4096)
- DRb.start_service('drbssl://localhost:0', DRbTests::DRbEx.new, config)
- es = DRb::ExtServ.new(ARGV.shift, ARGV.shift)
- DRb.thread.join
- es.stop_service if es.alive?
-end
-
diff --git a/test/drb/ut_drb_drbunix.rb b/test/drb/ut_drb_drbunix.rb
deleted file mode 100644
index ecf0920451..0000000000
--- a/test/drb/ut_drb_drbunix.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# frozen_string_literal: false
-require "#{File.dirname(File.expand_path(__FILE__))}/ut_drb"
-
-if __FILE__ == $0
- def ARGV.shift
- it = super()
- raise "usage: #{$0} <manager-uri> <name>" unless it
- it
- end
-
- DRb::DRbServer.default_argc_limit(8)
- DRb::DRbServer.default_load_limit(4096)
- DRb.start_service('drbunix:', DRbTests::DRbEx.new)
- es = DRb::ExtServ.new(ARGV.shift, ARGV.shift)
- DRb.thread.join
- es.stop_service if es.alive?
-end
-
diff --git a/test/drb/ut_eq.rb b/test/drb/ut_eq.rb
deleted file mode 100644
index 56285a384f..0000000000
--- a/test/drb/ut_eq.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-# frozen_string_literal: false
-require 'drb/drb'
-require 'drb/extserv'
-
-module DRbTests
-
-class Foo
- include DRbUndumped
-end
-
-class Bar
- include DRbUndumped
- def initialize
- @foo = Foo.new
- end
- attr_reader :foo
-
- def foo?(foo)
- @foo == foo
- end
-end
-
-end
-
-if __FILE__ == $0
- def ARGV.shift
- it = super()
- raise "usage: #{$0} <uri> <name>" unless it
- it
- end
-
- DRb.start_service('druby://localhost:0', DRbTests::Bar.new)
- es = DRb::ExtServ.new(ARGV.shift, ARGV.shift)
- DRb.thread.join
- es.stop_service if es.alive?
-end
-
diff --git a/test/drb/ut_large.rb b/test/drb/ut_large.rb
deleted file mode 100644
index 9376ff119d..0000000000
--- a/test/drb/ut_large.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-# frozen_string_literal: false
-require 'drb/drb'
-require 'drb/extserv'
-require 'timeout'
-
-module DRbTests
-
-class DRbLarge
- include DRbUndumped
-
- def size(ary)
- ary.size
- end
-
- def sum(ary)
- ary.inject(:+)
- end
-
- def multiply(ary)
- ary.inject(:*)
- end
-
- def avg(ary)
- return if ary.empty?
- if ary.any? {|n| n.is_a? String}
- raise TypeError
- else
- sum(ary).to_f / ary.count
- end
- end
-
- def median(ary)
- return if ary.empty?
- if ary.any? {|n| n.is_a? String}
- raise TypeError
- else
- avg ary.sort[((ary.length - 1) / 2)..(ary.length / 2)]
- end
- end
-
- def arg_test(*arg)
- # nop
- end
-end
-
-end
-
-if __FILE__ == $0
- def ARGV.shift
- it = super()
- raise "usage: #{$0} <manager-uri> <name>" unless it
- it
- end
-
- DRb::DRbServer.default_argc_limit(3)
- DRb::DRbServer.default_load_limit(100000)
- DRb.start_service('druby://localhost:0', DRbTests::DRbLarge.new)
- es = DRb::ExtServ.new(ARGV.shift, ARGV.shift)
- DRb.thread.join
- es.stop_service if es.alive?
-end
-
diff --git a/test/drb/ut_port.rb b/test/drb/ut_port.rb
deleted file mode 100644
index d317a307cc..0000000000
--- a/test/drb/ut_port.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# frozen_string_literal: false
-require 'drb/drb'
-require 'drb/extserv'
-
-if __FILE__ == $0
- def ARGV.shift
- it = super()
- raise "usage: #{$0} <uri> <name>" unless it
- it
- end
-
- DRb.start_service('druby://:8473', [1, 2, 'III', 4, "five", 6])
- es = DRb::ExtServ.new(ARGV.shift, ARGV.shift)
- DRb.thread.join
- es.stop_service if es.alive?
-end
diff --git a/test/drb/ut_safe1.rb b/test/drb/ut_safe1.rb
deleted file mode 100644
index 4b16fa7d6d..0000000000
--- a/test/drb/ut_safe1.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: false
-require 'drb/drb'
-require 'drb/extserv'
-
-if __FILE__ == $0
- def ARGV.shift
- it = super()
- raise "usage: #{$0} <uri> <name>" unless it
- it
- end
-
- DRb.start_service('druby://localhost:0', [1, 2, 'III', 4, "five", 6],
- {:safe_level => 1})
- es = DRb::ExtServ.new(ARGV.shift, ARGV.shift)
- DRb.thread.join
- es.stop_service if es.alive?
-end
diff --git a/test/drb/ut_timerholder.rb b/test/drb/ut_timerholder.rb
deleted file mode 100644
index 1753b30c74..0000000000
--- a/test/drb/ut_timerholder.rb
+++ /dev/null
@@ -1,74 +0,0 @@
-# frozen_string_literal: false
-require 'test/unit'
-require 'drb/timeridconv'
-
-module DRbTests
-
-class TimerIdConvTest < Test::Unit::TestCase
- def test_usecase_01
- keeping = 0.1
- idconv = DRb::TimerIdConv.new(keeping)
-
- key = idconv.to_id(self)
- assert_equal(key, self.__id__)
- sleep(keeping)
- assert_equal(idconv.to_id(false), false.__id__)
- assert_equal(idconv.to_obj(key), self)
- sleep(keeping)
-
- assert_equal(idconv.to_obj(key), self)
- sleep(keeping)
-
- assert_equal(idconv.to_id(true), true.__id__)
- sleep(keeping)
-
- assert_raise do
- assert_equal(idconv.to_obj(key), self)
- end
-
- assert_raise do
- assert_equal(idconv.to_obj(false.__id__), false)
- end
-
- key = idconv.to_id(self)
- assert_equal(key, self.__id__)
- assert_equal(idconv.to_id(true), true.__id__)
- sleep(keeping)
- GC.start
- sleep(keeping)
- GC.start
- assert_raise do
- assert_equal(idconv.to_obj(key), self)
- end
- end
-
- def test_usecase_02
- keeping = 0.1
- idconv = DRb::TimerIdConv.new(keeping)
-
- key = idconv.to_id(self)
- assert_equal(key, self.__id__)
- sleep(keeping)
- GC.start
- sleep(keeping)
- GC.start
- assert_raise do
- assert_equal(idconv.to_obj(key), self)
- end
- GC.start
-
- key = idconv.to_id(self)
- assert_equal(key, self.__id__)
- sleep(keeping)
- GC.start
- sleep(keeping)
- GC.start
- assert_raise do
- assert_equal(idconv.to_obj(key), self)
- end
- end
-end
-
-
-end
-
diff --git a/test/error_highlight/test_error_highlight.rb b/test/error_highlight/test_error_highlight.rb
index 006ecb05bf..2095970af1 100644
--- a/test/error_highlight/test_error_highlight.rb
+++ b/test/error_highlight/test_error_highlight.rb
@@ -23,17 +23,31 @@ class ErrorHighlightTest < Test::Unit::TestCase
end
end
+ begin
+ method_not_exist
+ rescue NameError
+ if $!.message.include?("`")
+ def preprocess(msg)
+ msg
+ end
+ else
+ def preprocess(msg)
+ msg.sub("`", "'")
+ end
+ end
+ end
+
if Exception.method_defined?(:detailed_message)
def assert_error_message(klass, expected_msg, &blk)
omit unless klass < ErrorHighlight::CoreExt
err = assert_raise(klass, &blk)
- assert_equal(expected_msg.chomp, err.detailed_message(highlight: false).sub(/ \((?:NoMethod|Name)Error\)/, ""))
+ assert_equal(preprocess(expected_msg).chomp, err.detailed_message(highlight: false).sub(/ \((?:NoMethod|Name)Error\)/, ""))
end
else
def assert_error_message(klass, expected_msg, &blk)
omit unless klass < ErrorHighlight::CoreExt
err = assert_raise(klass, &blk)
- assert_equal(expected_msg.chomp, err.message)
+ assert_equal(preprocess(expected_msg).chomp, err.message)
end
end
diff --git a/test/excludes/TestThread.rb b/test/excludes/TestThread.rb
deleted file mode 100644
index cf7e88427e..0000000000
--- a/test/excludes/TestThread.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-# frozen_string_literal: false
-exclude(/_stack_size$/, 'often too expensive')
-if /freebsd13/ =~ RUBY_PLATFORM
- # http://rubyci.s3.amazonaws.com/freebsd13/ruby-master/log/20220216T143001Z.fail.html.gz
- #
- # 1) Error:
- # TestThread#test_signal_at_join:
- # Timeout::Error: execution of assert_separately expired timeout (120 sec)
- # pid 30743 killed by SIGABRT (signal 6) (core dumped)
- # |
- #
- # /usr/home/chkbuild/chkbuild/tmp/build/20220216T143001Z/ruby/test/ruby/test_thread.rb:1390:in `test_signal_at_join'
- exclude(:test_signal_at_join, 'gets stuck somewhere')
-end
diff --git a/test/fiber/scheduler.rb b/test/fiber/scheduler.rb
index 5090271db1..3926226ca3 100644
--- a/test/fiber/scheduler.rb
+++ b/test/fiber/scheduler.rb
@@ -27,7 +27,9 @@ class Scheduler
Warning[:experimental] = experimental
end
- def initialize
+ def initialize(fiber = Fiber.current)
+ @fiber = fiber
+
@readable = {}
@writable = {}
@waiting = {}
@@ -45,6 +47,10 @@ class Scheduler
attr :writable
attr :waiting
+ def transfer
+ @fiber.transfer
+ end
+
def next_timeout
_fiber, timeout = @waiting.min_by{|key, value| value}
@@ -88,7 +94,7 @@ class Scheduler
end
selected.each do |fiber, events|
- fiber.resume(events)
+ fiber.transfer(events)
end
if @waiting.any?
@@ -98,7 +104,7 @@ class Scheduler
waiting.each do |fiber, timeout|
if fiber.alive?
if timeout <= time
- fiber.resume
+ fiber.transfer
else
@waiting[fiber] = timeout
end
@@ -114,7 +120,7 @@ class Scheduler
end
ready.each do |fiber|
- fiber.resume
+ fiber.transfer
end
end
end
@@ -217,7 +223,7 @@ class Scheduler
@waiting[fiber] = current_time + duration
end
- Fiber.yield
+ @fiber.transfer
ensure
@waiting.delete(fiber) if duration
@readable.delete(io) if readable
@@ -254,7 +260,7 @@ class Scheduler
if timeout
@waiting[fiber] = current_time + timeout
begin
- Fiber.yield
+ @fiber.transfer
ensure
# Remove from @waiting in the case #unblock was called before the timeout expired:
@waiting.delete(fiber)
@@ -262,7 +268,7 @@ class Scheduler
else
@blocking[fiber] = true
begin
- Fiber.yield
+ @fiber.transfer
ensure
@blocking.delete(fiber)
end
@@ -290,7 +296,7 @@ class Scheduler
def fiber(&block)
fiber = Fiber.new(blocking: false, &block)
- fiber.resume
+ fiber.transfer
return fiber
end
diff --git a/test/fiber/test_address_resolve.rb b/test/fiber/test_address_resolve.rb
index 12223bcc06..09c8db6049 100644
--- a/test/fiber/test_address_resolve.rb
+++ b/test/fiber/test_address_resolve.rb
@@ -179,7 +179,7 @@ class TestAddressResolve < Test::Unit::TestCase
Fiber.set_scheduler scheduler
Fiber.schedule do
- assert_raise(SocketError) {
+ assert_raise(Socket::ResolutionError) {
Addrinfo.getaddrinfo("non-existing-domain.abc", nil)
}
end
diff --git a/test/fiber/test_enumerator.rb b/test/fiber/test_enumerator.rb
index 40f7d01725..e9410f925c 100644
--- a/test/fiber/test_enumerator.rb
+++ b/test/fiber/test_enumerator.rb
@@ -42,4 +42,12 @@ class TestFiberEnumerator < Test::Unit::TestCase
assert_predicate(i, :closed?)
assert_predicate(o, :closed?)
end
+
+ def enumerator_fiber_is_nonblocking
+ enumerator = Enumerator.new do |yielder|
+ yielder << Fiber.current.blocking?
+ end
+
+ assert_equal(false, enumerator.next)
+ end
end
diff --git a/test/fiber/test_io.rb b/test/fiber/test_io.rb
index c1ad56a8cf..0e3e086d5a 100644
--- a/test/fiber/test_io.rb
+++ b/test/fiber/test_io.rb
@@ -219,4 +219,19 @@ class TestFiberIO < Test::Unit::TestCase
assert_equal [[r], [w], []], result
end
end
+
+ def test_backquote
+ result = nil
+
+ thread = Thread.new do
+ scheduler = Scheduler.new
+ Fiber.set_scheduler scheduler
+ Fiber.schedule do
+ result = `#{EnvUtil.rubybin} -e "sleep 0.1;puts %[ok]"`
+ end
+ end
+ thread.join
+
+ assert_equal "ok\n", result
+ end
end
diff --git a/test/fiber/test_mutex.rb b/test/fiber/test_mutex.rb
index 449c49f38b..2cee2cc235 100644
--- a/test/fiber/test_mutex.rb
+++ b/test/fiber/test_mutex.rb
@@ -207,7 +207,7 @@ class TestFiberMutex < Test::Unit::TestCase
Fiber.schedule do
mutex.synchronize do
puts 'in synchronize'
- Fiber.yield
+ scheduler.transfer
end
end
diff --git a/test/fiber/test_process.rb b/test/fiber/test_process.rb
index a5990be204..a09b070c0a 100644
--- a/test/fiber/test_process.rb
+++ b/test/fiber/test_process.rb
@@ -3,13 +3,15 @@ require 'test/unit'
require_relative 'scheduler'
class TestFiberProcess < Test::Unit::TestCase
+ TRUE_CMD = RUBY_PLATFORM =~ /mswin|mingw/ ? "exit 0" : "true"
+
def test_process_wait
Thread.new do
scheduler = Scheduler.new
Fiber.set_scheduler scheduler
Fiber.schedule do
- pid = Process.spawn("true")
+ pid = Process.spawn(TRUE_CMD)
Process.wait(pid)
# TODO test that scheduler was invoked.
@@ -25,7 +27,7 @@ class TestFiberProcess < Test::Unit::TestCase
Fiber.set_scheduler scheduler
Fiber.schedule do
- system("true")
+ system(TRUE_CMD)
# TODO test that scheduler was invoked (currently it's not).
@@ -34,6 +36,27 @@ class TestFiberProcess < Test::Unit::TestCase
end.join
end
+ def test_system_faulty_process_wait
+ Thread.new do
+ scheduler = Scheduler.new
+
+ def scheduler.process_wait(pid, flags)
+ Fiber.blocking{Process.wait(pid, flags)}
+
+ # Don't return `Process::Status` instance.
+ return false
+ end
+
+ Fiber.set_scheduler scheduler
+
+ Fiber.schedule do
+ assert_raise TypeError do
+ system(TRUE_CMD)
+ end
+ end
+ end.join
+ end
+
def test_fork
omit 'fork not supported' unless Process.respond_to?(:fork)
Thread.new do
diff --git a/test/fiber/test_queue.rb b/test/fiber/test_queue.rb
index 10d9540492..d78b026f11 100644
--- a/test/fiber/test_queue.rb
+++ b/test/fiber/test_queue.rb
@@ -5,9 +5,10 @@ require_relative 'scheduler'
class TestFiberQueue < Test::Unit::TestCase
def test_pop_with_timeout
queue = Thread::Queue.new
+ kill = false
result = :unspecified
- Thread.new do
+ thread = Thread.new do
scheduler = Scheduler.new
Fiber.set_scheduler(scheduler)
@@ -16,17 +17,23 @@ class TestFiberQueue < Test::Unit::TestCase
end
scheduler.run
- end.join
+ end
+ until thread.join(2)
+ kill = true
+ thread.kill
+ end
+ assert_false(kill, 'Getting stuck due to a possible compiler bug.')
assert_nil result
end
def test_pop_with_timeout_and_value
queue = Thread::Queue.new
queue.push(:something)
+ kill = false
result = :unspecified
- Thread.new do
+ thread = Thread.new do
scheduler = Scheduler.new
Fiber.set_scheduler(scheduler)
@@ -35,8 +42,13 @@ class TestFiberQueue < Test::Unit::TestCase
end
scheduler.run
- end.join
+ end
+ until thread.join(2)
+ kill = true
+ thread.kill
+ end
+ assert_false(kill, 'Getting stuck due to a possible compiler bug.')
assert_equal :something, result
end
end
diff --git a/test/fiber/test_scheduler.rb b/test/fiber/test_scheduler.rb
index f8993bb18f..34effad816 100644
--- a/test/fiber/test_scheduler.rb
+++ b/test/fiber/test_scheduler.rb
@@ -118,7 +118,7 @@ class TestFiberScheduler < Test::Unit::TestCase
end
def test_autoload
- 100.times do
+ 10.times do
Object.autoload(:TestFiberSchedulerAutoload, File.expand_path("autoload.rb", __dir__))
thread = Thread.new do
diff --git a/test/fiber/test_thread.rb b/test/fiber/test_thread.rb
index 6fae266955..5e3cc6d0e1 100644
--- a/test/fiber/test_thread.rb
+++ b/test/fiber/test_thread.rb
@@ -39,7 +39,7 @@ class TestFiberThread < Test::Unit::TestCase
assert_predicate sleeper, :alive?
ensure
- sleeper&.kill
+ sleeper&.kill&.join
end
def test_thread_join_implicit
diff --git a/test/fiddle/test_c_struct_entry.rb b/test/fiddle/test_c_struct_entry.rb
index 9fd16d7101..45de2efe21 100644
--- a/test/fiddle/test_c_struct_entry.rb
+++ b/test/fiddle/test_c_struct_entry.rb
@@ -8,7 +8,7 @@ end
module Fiddle
class TestCStructEntity < TestCase
def test_class_size
- types = [TYPE_DOUBLE, TYPE_CHAR]
+ types = [TYPE_DOUBLE, TYPE_CHAR, TYPE_DOUBLE, TYPE_BOOL]
size = CStructEntity.size types
@@ -20,6 +20,12 @@ module Fiddle
expected = PackInfo.align expected, alignments[1]
expected += PackInfo::SIZE_MAP[TYPE_CHAR]
+ expected = PackInfo.align expected, alignments[2]
+ expected += PackInfo::SIZE_MAP[TYPE_DOUBLE]
+
+ expected = PackInfo.align expected, alignments[3]
+ expected += PackInfo::SIZE_MAP[TYPE_BOOL]
+
expected = PackInfo.align expected, alignments.max
assert_equal expected, size
diff --git a/test/fiddle/test_closure.rb b/test/fiddle/test_closure.rb
index 825ea9651d..abb6bdbd32 100644
--- a/test/fiddle/test_closure.rb
+++ b/test/fiddle/test_closure.rb
@@ -81,6 +81,18 @@ module Fiddle
end
end
+ def test_bool
+ closure_class = Class.new(Closure) do
+ def call(bool)
+ not bool
+ end
+ end
+ closure_class.create(:bool, [:bool]) do |closure|
+ func = Function.new(closure, [:bool], :bool)
+ assert_equal(false, func.call(true))
+ end
+ end
+
def test_free
Closure.create(:int, [:void]) do |closure|
assert(!closure.freed?)
diff --git a/test/fiddle/test_cparser.rb b/test/fiddle/test_cparser.rb
index ae319197a4..f1b67476ba 100644
--- a/test/fiddle/test_cparser.rb
+++ b/test/fiddle/test_cparser.rb
@@ -24,14 +24,32 @@ module Fiddle
assert_equal(TYPE_SHORT, parse_ctype('const short'))
assert_equal(TYPE_SHORT, parse_ctype('short int'))
assert_equal(TYPE_SHORT, parse_ctype('const short int'))
+ assert_equal(TYPE_SHORT, parse_ctype('int short'))
+ assert_equal(TYPE_SHORT, parse_ctype('const int short'))
assert_equal(TYPE_SHORT, parse_ctype('signed short'))
assert_equal(TYPE_SHORT, parse_ctype('const signed short'))
+ assert_equal(TYPE_SHORT, parse_ctype('short signed'))
+ assert_equal(TYPE_SHORT, parse_ctype('const short signed'))
assert_equal(TYPE_SHORT, parse_ctype('signed short int'))
assert_equal(TYPE_SHORT, parse_ctype('const signed short int'))
+ assert_equal(TYPE_SHORT, parse_ctype('signed int short'))
+ assert_equal(TYPE_SHORT, parse_ctype('const signed int short'))
+ assert_equal(TYPE_SHORT, parse_ctype('int signed short'))
+ assert_equal(TYPE_SHORT, parse_ctype('const int signed short'))
+ assert_equal(TYPE_SHORT, parse_ctype('int short signed'))
+ assert_equal(TYPE_SHORT, parse_ctype('const int short signed'))
assert_equal(-TYPE_SHORT, parse_ctype('unsigned short'))
assert_equal(-TYPE_SHORT, parse_ctype('const unsigned short'))
assert_equal(-TYPE_SHORT, parse_ctype('unsigned short int'))
assert_equal(-TYPE_SHORT, parse_ctype('const unsigned short int'))
+ assert_equal(-TYPE_SHORT, parse_ctype('unsigned int short'))
+ assert_equal(-TYPE_SHORT, parse_ctype('const unsigned int short'))
+ assert_equal(-TYPE_SHORT, parse_ctype('short int unsigned'))
+ assert_equal(-TYPE_SHORT, parse_ctype('const short int unsigned'))
+ assert_equal(-TYPE_SHORT, parse_ctype('int unsigned short'))
+ assert_equal(-TYPE_SHORT, parse_ctype('const int unsigned short'))
+ assert_equal(-TYPE_SHORT, parse_ctype('int short unsigned'))
+ assert_equal(-TYPE_SHORT, parse_ctype('const int short unsigned'))
end
def test_int_ctype
@@ -50,14 +68,32 @@ module Fiddle
assert_equal(TYPE_LONG, parse_ctype('const long'))
assert_equal(TYPE_LONG, parse_ctype('long int'))
assert_equal(TYPE_LONG, parse_ctype('const long int'))
+ assert_equal(TYPE_LONG, parse_ctype('int long'))
+ assert_equal(TYPE_LONG, parse_ctype('const int long'))
assert_equal(TYPE_LONG, parse_ctype('signed long'))
assert_equal(TYPE_LONG, parse_ctype('const signed long'))
assert_equal(TYPE_LONG, parse_ctype('signed long int'))
assert_equal(TYPE_LONG, parse_ctype('const signed long int'))
+ assert_equal(TYPE_LONG, parse_ctype('signed int long'))
+ assert_equal(TYPE_LONG, parse_ctype('const signed int long'))
+ assert_equal(TYPE_LONG, parse_ctype('long signed'))
+ assert_equal(TYPE_LONG, parse_ctype('const long signed'))
+ assert_equal(TYPE_LONG, parse_ctype('long int signed'))
+ assert_equal(TYPE_LONG, parse_ctype('const long int signed'))
+ assert_equal(TYPE_LONG, parse_ctype('int long signed'))
+ assert_equal(TYPE_LONG, parse_ctype('const int long signed'))
assert_equal(-TYPE_LONG, parse_ctype('unsigned long'))
assert_equal(-TYPE_LONG, parse_ctype('const unsigned long'))
assert_equal(-TYPE_LONG, parse_ctype('unsigned long int'))
assert_equal(-TYPE_LONG, parse_ctype('const unsigned long int'))
+ assert_equal(-TYPE_LONG, parse_ctype('long int unsigned'))
+ assert_equal(-TYPE_LONG, parse_ctype('const long int unsigned'))
+ assert_equal(-TYPE_LONG, parse_ctype('unsigned int long'))
+ assert_equal(-TYPE_LONG, parse_ctype('const unsigned int long'))
+ assert_equal(-TYPE_LONG, parse_ctype('int unsigned long'))
+ assert_equal(-TYPE_LONG, parse_ctype('const int unsigned long'))
+ assert_equal(-TYPE_LONG, parse_ctype('int long unsigned'))
+ assert_equal(-TYPE_LONG, parse_ctype('const int long unsigned'))
end
def test_size_t_ctype
@@ -85,6 +121,10 @@ module Fiddle
assert_equal(TYPE_UINTPTR_T, parse_ctype("const uintptr_t"))
end
+ def test_bool_ctype
+ assert_equal(TYPE_BOOL, parse_ctype('bool'))
+ end
+
def test_undefined_ctype
assert_raise(DLError) { parse_ctype('DWORD') }
end
diff --git a/test/fiddle/test_func.rb b/test/fiddle/test_func.rb
index ff52f727d0..df79539e76 100644
--- a/test/fiddle/test_func.rb
+++ b/test/fiddle/test_func.rb
@@ -145,5 +145,22 @@ module Fiddle
assert_equal("string: He, const string: World, uint: 29\n",
output_buffer[0, written])
end
+
+ def test_rb_memory_view_available_p
+ omit "MemoryView is unavailable" unless defined? Fiddle::MemoryView
+ libruby = Fiddle.dlopen(nil)
+ case Fiddle::SIZEOF_VOIDP
+ when Fiddle::SIZEOF_LONG_LONG
+ value_type = -Fiddle::TYPE_LONG_LONG
+ else
+ value_type = -Fiddle::TYPE_LONG
+ end
+ rb_memory_view_available_p =
+ Function.new(libruby["rb_memory_view_available_p"],
+ [value_type],
+ :bool,
+ need_gvl: true)
+ assert_equal(false, rb_memory_view_available_p.call(Fiddle::Qnil))
+ end
end
end if defined?(Fiddle)
diff --git a/test/fiddle/test_handle.rb b/test/fiddle/test_handle.rb
index c19bcc6623..412c10e09d 100644
--- a/test/fiddle/test_handle.rb
+++ b/test/fiddle/test_handle.rb
@@ -183,9 +183,12 @@ module Fiddle
# it calls _nss_cache_cycle_prevention_function with dlsym(3).
# So our Fiddle::Handle#sym must call dlerror(3) before call dlsym.
# In general uses of dlerror(3) should call it before use it.
+ verbose, $VERBOSE = $VERBOSE, nil
require 'socket'
Socket.gethostbyname("localhost")
Fiddle.dlopen("/lib/libc.so.7").sym('strcpy')
+ ensure
+ $VERBOSE = verbose
end if /freebsd/=~ RUBY_PLATFORM
def test_no_memory_leak
diff --git a/test/fiddle/test_pointer.rb b/test/fiddle/test_pointer.rb
index d6cba04bbe..f2c1d285ad 100644
--- a/test/fiddle/test_pointer.rb
+++ b/test/fiddle/test_pointer.rb
@@ -10,6 +10,22 @@ module Fiddle
Fiddle.dlwrap arg
end
+ def test_can_read_write_memory
+ # Allocate some memory
+ address = Fiddle.malloc(Fiddle::SIZEOF_VOIDP)
+
+ bytes_to_write = Fiddle::SIZEOF_VOIDP.times.to_a.pack("C*")
+
+ # Write to the memory
+ Fiddle::Pointer.write(address, bytes_to_write)
+
+ # Read the bytes out again
+ bytes = Fiddle::Pointer.read(address, Fiddle::SIZEOF_VOIDP)
+ assert_equal bytes_to_write, bytes
+ ensure
+ Fiddle.free address
+ end
+
def test_cptr_to_int
null = Fiddle::NULL
assert_equal(null.to_i, null.to_int)
diff --git a/test/io/console/test_io_console.rb b/test/io/console/test_io_console.rb
index 3f4b64bbcd..0a113ebd2f 100644
--- a/test/io/console/test_io_console.rb
+++ b/test/io/console/test_io_console.rb
@@ -15,6 +15,7 @@ class TestIO_Console < Test::Unit::TestCase
raise
end
PATHS.uniq!
+ INCLUDE_OPTS = "-I#{PATHS.join(File::PATH_SEPARATOR)}"
# FreeBSD seems to hang on TTOU when running parallel tests
# tested on FreeBSD 11.x.
@@ -36,22 +37,22 @@ class TestIO_Console < Test::Unit::TestCase
trap(:TTOU, @old_ttou) if defined?(@old_ttou) and @old_ttou
end
+ exceptions = %w[ENODEV ENOTTY EBADF ENXIO].map {|e|
+ Errno.const_get(e) if Errno.const_defined?(e)
+ }
+ exceptions.compact!
+ FailedPathExceptions = (exceptions unless exceptions.empty?)
+
def test_failed_path
- exceptions = %w[ENODEV ENOTTY EBADF ENXIO].map {|e|
- Errno.const_get(e) if Errno.const_defined?(e)
- }
- exceptions.compact!
- omit if exceptions.empty?
File.open(IO::NULL) do |f|
- e = assert_raise(*exceptions) do
+ e = assert_raise(*FailedPathExceptions) do
f.echo?
end
assert_include(e.message, IO::NULL)
end
- end
+ end if FailedPathExceptions
def test_bad_keyword
- omit if RUBY_ENGINE == 'jruby'
assert_raise_with_message(ArgumentError, /unknown keyword:.*bad/) do
File.open(IO::NULL) do |f|
f.raw(bad: 0)
@@ -241,7 +242,6 @@ defined?(PTY) and defined?(IO.console) and TestIO_Console.class_eval do
end
def test_getpass
- omit unless IO.method_defined?("getpass")
run_pty("p IO.console.getpass('> ')") do |r, w|
assert_equal("> ", r.readpartial(10))
sleep 0.1
@@ -259,6 +259,15 @@ defined?(PTY) and defined?(IO.console) and TestIO_Console.class_eval do
assert_equal("\r\n", r.gets)
assert_equal("\"asdf\"", r.gets.chomp)
end
+
+ run_pty("$VERBOSE, $/ = nil, '.'; p IO.console.getpass('> ')") do |r, w|
+ assert_equal("> ", r.readpartial(10))
+ sleep 0.1
+ w.print "asdf\n"
+ sleep 0.1
+ assert_equal("\r\n", r.gets)
+ assert_equal("\"asdf\"", r.gets.chomp)
+ end
end
def test_iflush
@@ -449,7 +458,7 @@ defined?(PTY) and defined?(IO.console) and TestIO_Console.class_eval do
def run_pty(src, n = 1)
pend("PTY.spawn cannot control terminal on JRuby") if RUBY_ENGINE == 'jruby'
- args = ["-I#{TestIO_Console::PATHS.join(File::PATH_SEPARATOR)}", "-rio/console", "-e", src]
+ args = [TestIO_Console::INCLUDE_OPTS, "-rio/console", "-e", src]
args.shift if args.first == "-I" # statically linked
r, w, pid = PTY.spawn(EnvUtil.rubybin, *args)
rescue RuntimeError
@@ -543,6 +552,7 @@ defined?(IO.console) and TestIO_Console.class_eval do
t2 = Tempfile.new("noctty_run")
t2.close
cmd = [*NOCTTY[1..-1],
+ TestIO_Console::INCLUDE_OPTS,
'-e', 'open(ARGV[0], "w") {|f|',
'-e', 'STDOUT.reopen(f)',
'-e', 'STDERR.reopen(f)',
diff --git a/test/io/wait/test_io_wait.rb b/test/io/wait/test_io_wait.rb
index 7997a4814f..cbc01f9622 100644
--- a/test/io/wait/test_io_wait.rb
+++ b/test/io/wait/test_io_wait.rb
@@ -36,6 +36,7 @@ class TestIOWait < Test::Unit::TestCase
end
def test_ready?
+ omit 'unstable on MinGW' if /mingw/ =~ RUBY_PLATFORM
assert_not_predicate @r, :ready?, "shouldn't ready, but ready"
@w.syswrite "."
sleep 0.1
diff --git a/test/irb/command/test_custom_command.rb b/test/irb/command/test_custom_command.rb
new file mode 100644
index 0000000000..6642d2b160
--- /dev/null
+++ b/test/irb/command/test_custom_command.rb
@@ -0,0 +1,127 @@
+# frozen_string_literal: true
+require "irb"
+
+require_relative "../helper"
+
+module TestIRB
+ class CustomCommandIntegrationTest < TestIRB::IntegrationTestCase
+ def test_command_regsitration_can_happen_after_irb_require
+ write_ruby <<~RUBY
+ require "irb"
+ require "irb/command"
+
+ class PrintCommand < IRB::Command::Base
+ category 'CommandTest'
+ description 'print_command'
+ def execute(*)
+ puts "Hello from PrintCommand"
+ nil
+ end
+ end
+
+ IRB::Command.register(:print!, PrintCommand)
+
+ binding.irb
+ RUBY
+
+ output = run_ruby_file do
+ type "print!\n"
+ type "exit"
+ end
+
+ assert_include(output, "Hello from PrintCommand")
+ end
+
+ def test_command_regsitration_accepts_string_too
+ write_ruby <<~RUBY
+ require "irb/command"
+
+ class PrintCommand < IRB::Command::Base
+ category 'CommandTest'
+ description 'print_command'
+ def execute(*)
+ puts "Hello from PrintCommand"
+ nil
+ end
+ end
+
+ IRB::Command.register("print!", PrintCommand)
+
+ binding.irb
+ RUBY
+
+ output = run_ruby_file do
+ type "print!\n"
+ type "exit"
+ end
+
+ assert_include(output, "Hello from PrintCommand")
+ end
+
+ def test_arguments_propogation
+ write_ruby <<~RUBY
+ require "irb/command"
+
+ class PrintArgCommand < IRB::Command::Base
+ category 'CommandTest'
+ description 'print_command_arg'
+ def execute(arg)
+ $nth_execution ||= 0
+ puts "\#{$nth_execution} arg=\#{arg.inspect}"
+ $nth_execution += 1
+ nil
+ end
+ end
+
+ IRB::Command.register(:print_arg, PrintArgCommand)
+
+ binding.irb
+ RUBY
+
+ output = run_ruby_file do
+ type "print_arg\n"
+ type "print_arg \n"
+ type "print_arg a r g\n"
+ type "print_arg a r g \n"
+ type "exit"
+ end
+
+ assert_include(output, "0 arg=\"\"")
+ assert_include(output, "1 arg=\"\"")
+ assert_include(output, "2 arg=\"a r g\"")
+ assert_include(output, "3 arg=\"a r g\"")
+ end
+
+ def test_def_extend_command_still_works
+ write_ruby <<~RUBY
+ require "irb"
+
+ class FooBarCommand < IRB::Command::Base
+ category 'FooBarCategory'
+ description 'foobar_description'
+ def execute(*)
+ $nth_execution ||= 1
+ puts "\#{$nth_execution} FooBar executed"
+ $nth_execution += 1
+ nil
+ end
+ end
+
+ IRB::ExtendCommandBundle.def_extend_command(:foobar, FooBarCommand, nil, [:fbalias, IRB::Command::OVERRIDE_ALL])
+
+ binding.irb
+ RUBY
+
+ output = run_ruby_file do
+ type "foobar"
+ type "fbalias"
+ type "help foobar"
+ type "exit"
+ end
+
+ assert_include(output, "1 FooBar executed")
+ assert_include(output, "2 FooBar executed")
+ assert_include(output, "foobar_description")
+ end
+ end
+end
diff --git a/test/irb/command/test_force_exit.rb b/test/irb/command/test_force_exit.rb
new file mode 100644
index 0000000000..191a786872
--- /dev/null
+++ b/test/irb/command/test_force_exit.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: false
+require 'irb'
+
+require_relative "../helper"
+
+module TestIRB
+ class ForceExitTest < IntegrationTestCase
+ def test_forced_exit_finishes_process_immediately
+ write_ruby <<~'ruby'
+ puts "First line"
+ puts "Second line"
+ binding.irb
+ puts "Third line"
+ binding.irb
+ puts "Fourth line"
+ ruby
+
+ output = run_ruby_file do
+ type "123"
+ type "456"
+ type "exit!"
+ end
+
+ assert_match(/First line\r\n/, output)
+ assert_match(/Second line\r\n/, output)
+ assert_match(/irb\(main\):001> 123/, output)
+ assert_match(/irb\(main\):002> 456/, output)
+ refute_match(/Third line\r\n/, output)
+ refute_match(/Fourth line\r\n/, output)
+ end
+
+ def test_forced_exit_in_nested_sessions
+ write_ruby <<~'ruby'
+ def foo
+ binding.irb
+ end
+
+ binding.irb
+ binding.irb
+ ruby
+
+ output = run_ruby_file do
+ type "123"
+ type "foo"
+ type "exit!"
+ end
+
+ assert_match(/irb\(main\):001> 123/, output)
+ end
+ end
+end
diff --git a/test/irb/command/test_help.rb b/test/irb/command/test_help.rb
new file mode 100644
index 0000000000..df3753dae7
--- /dev/null
+++ b/test/irb/command/test_help.rb
@@ -0,0 +1,75 @@
+require "tempfile"
+require_relative "../helper"
+
+module TestIRB
+ class HelpTest < IntegrationTestCase
+ def setup
+ super
+
+ write_rc <<~'RUBY'
+ IRB.conf[:USE_PAGER] = false
+ RUBY
+
+ write_ruby <<~'RUBY'
+ binding.irb
+ RUBY
+ end
+
+ def test_help
+ out = run_ruby_file do
+ type "help"
+ type "exit"
+ end
+
+ assert_match(/List all available commands/, out)
+ assert_match(/Start the debugger of debug\.gem/, out)
+ end
+
+ def test_command_help
+ out = run_ruby_file do
+ type "help ls"
+ type "exit"
+ end
+
+ assert_match(/Usage: ls \[obj\]/, out)
+ end
+
+ def test_command_help_not_found
+ out = run_ruby_file do
+ type "help foo"
+ type "exit"
+ end
+
+ assert_match(/Can't find command `foo`\. Please check the command name and try again\./, out)
+ end
+
+ def test_show_cmds
+ out = run_ruby_file do
+ type "help"
+ type "exit"
+ end
+
+ assert_match(/List all available commands/, out)
+ assert_match(/Start the debugger of debug\.gem/, out)
+ end
+
+ def test_help_lists_user_aliases
+ out = run_ruby_file do
+ type "help"
+ type "exit"
+ end
+
+ assert_match(/\$\s+Alias for `show_source`/, out)
+ assert_match(/@\s+Alias for `whereami`/, out)
+ end
+
+ def test_help_lists_helper_methods
+ out = run_ruby_file do
+ type "help"
+ type "exit"
+ end
+
+ assert_match(/Helper methods\s+conf\s+Returns the current context/, out)
+ end
+ end
+end
diff --git a/test/irb/command/test_multi_irb_commands.rb b/test/irb/command/test_multi_irb_commands.rb
new file mode 100644
index 0000000000..e313c0c5d2
--- /dev/null
+++ b/test/irb/command/test_multi_irb_commands.rb
@@ -0,0 +1,50 @@
+require "tempfile"
+require_relative "../helper"
+
+module TestIRB
+ class MultiIRBTest < IntegrationTestCase
+ def setup
+ super
+
+ write_ruby <<~'RUBY'
+ binding.irb
+ RUBY
+ end
+
+ def test_jobs_command_with_print_deprecated_warning
+ out = run_ruby_file do
+ type "jobs"
+ type "exit"
+ end
+
+ assert_match(/Multi-irb commands are deprecated and will be removed in IRB 2\.0\.0\. Please use workspace commands instead\./, out)
+ assert_match(%r|If you have any use case for multi-irb, please leave a comment at https://github.com/ruby/irb/issues/653|, out)
+ assert_match(/#0->irb on main \(#<Thread:0x.+ run>: running\)/, out)
+ end
+
+ def test_irb_jobs_and_kill_commands
+ out = run_ruby_file do
+ type "irb"
+ type "jobs"
+ type "kill 1"
+ type "exit"
+ end
+
+ assert_match(/#0->irb on main \(#<Thread:0x.+ sleep_forever>: stop\)/, out)
+ assert_match(/#1->irb#1 on main \(#<Thread:0x.+ run>: running\)/, out)
+ end
+
+ def test_irb_fg_jobs_and_kill_commands
+ out = run_ruby_file do
+ type "irb"
+ type "fg 0"
+ type "jobs"
+ type "kill 1"
+ type "exit"
+ end
+
+ assert_match(/#0->irb on main \(#<Thread:0x.+ run>: running\)/, out)
+ assert_match(/#1->irb#1 on main \(#<Thread:0x.+ sleep_forever>: stop\)/, out)
+ end
+ end
+end
diff --git a/test/irb/command/test_show_source.rb b/test/irb/command/test_show_source.rb
new file mode 100644
index 0000000000..d014c78fc4
--- /dev/null
+++ b/test/irb/command/test_show_source.rb
@@ -0,0 +1,397 @@
+# frozen_string_literal: false
+require 'irb'
+
+require_relative "../helper"
+
+module TestIRB
+ class ShowSourceTest < IntegrationTestCase
+ def setup
+ super
+
+ write_rc <<~'RUBY'
+ IRB.conf[:USE_PAGER] = false
+ RUBY
+ end
+
+ def test_show_source
+ write_ruby <<~'RUBY'
+ binding.irb
+ RUBY
+
+ out = run_ruby_file do
+ type "show_source IRB.conf"
+ type "exit"
+ end
+
+ assert_match(%r[/irb\/init\.rb], out)
+ end
+
+ def test_show_source_alias
+ write_ruby <<~'RUBY'
+ binding.irb
+ RUBY
+
+ out = run_ruby_file do
+ type "$ IRB.conf"
+ type "exit"
+ end
+
+ assert_match(%r[/irb\/init\.rb], out)
+ end
+
+ def test_show_source_with_missing_signature
+ write_ruby <<~'RUBY'
+ binding.irb
+ RUBY
+
+ out = run_ruby_file do
+ type "show_source foo"
+ type "exit"
+ end
+
+ assert_match(%r[Couldn't locate a definition for foo], out)
+ end
+
+ def test_show_source_with_missing_constant
+ write_ruby <<~'RUBY'
+ binding.irb
+ RUBY
+
+ out = run_ruby_file do
+ type "show_source Foo"
+ type "exit"
+ end
+
+ assert_match(%r[Couldn't locate a definition for Foo], out)
+ end
+
+ def test_show_source_string
+ write_ruby <<~'RUBY'
+ binding.irb
+ RUBY
+
+ out = run_ruby_file do
+ type "show_source 'IRB.conf'"
+ type "exit"
+ end
+
+ assert_match(%r[/irb\/init\.rb], out)
+ end
+
+ def test_show_source_method_s
+ write_ruby <<~RUBY
+ class Baz
+ def foo
+ end
+ end
+
+ class Bar < Baz
+ def foo
+ super
+ end
+ end
+
+ binding.irb
+ RUBY
+
+ out = run_ruby_file do
+ type "show_source Bar#foo -s"
+ type "exit"
+ end
+
+ assert_match(%r[#{@ruby_file.to_path}:2\s+def foo\r\n end\r\n], out)
+ end
+
+ def test_show_source_method_s_with_incorrect_signature
+ write_ruby <<~RUBY
+ class Baz
+ def foo
+ end
+ end
+
+ class Bar < Baz
+ def foo
+ super
+ end
+ end
+
+ binding.irb
+ RUBY
+
+ out = run_ruby_file do
+ type "show_source Bar#fooo -s"
+ type "exit"
+ end
+
+ assert_match(%r[Error: Couldn't locate a super definition for Bar#fooo], out)
+ end
+
+ def test_show_source_private_method
+ write_ruby <<~RUBY
+ class Bar
+ private def foo
+ end
+ end
+ binding.irb
+ RUBY
+
+ out = run_ruby_file do
+ type "show_source Bar#foo"
+ type "exit"
+ end
+
+ assert_match(%r[#{@ruby_file.to_path}:2\s+private def foo\r\n end\r\n], out)
+ end
+
+ def test_show_source_private_singleton_method
+ write_ruby <<~RUBY
+ class Bar
+ private def foo
+ end
+ end
+ binding.irb
+ RUBY
+
+ out = run_ruby_file do
+ type "bar = Bar.new"
+ type "show_source bar.foo"
+ type "exit"
+ end
+
+ assert_match(%r[#{@ruby_file.to_path}:2\s+private def foo\r\n end\r\n], out)
+ end
+
+ def test_show_source_method_multiple_s
+ write_ruby <<~RUBY
+ class Baz
+ def foo
+ end
+ end
+
+ class Bar < Baz
+ def foo
+ super
+ end
+ end
+
+ class Bob < Bar
+ def foo
+ super
+ end
+ end
+
+ binding.irb
+ RUBY
+
+ out = run_ruby_file do
+ type "show_source Bob#foo -ss"
+ type "exit"
+ end
+
+ assert_match(%r[#{@ruby_file.to_path}:2\s+def foo\r\n end\r\n], out)
+ end
+
+ def test_show_source_method_no_instance_method
+ write_ruby <<~RUBY
+ class Baz
+ end
+
+ class Bar < Baz
+ def foo
+ super
+ end
+ end
+
+ binding.irb
+ RUBY
+
+ out = run_ruby_file do
+ type "show_source Bar#foo -s"
+ type "exit"
+ end
+
+ assert_match(%r[Error: Couldn't locate a super definition for Bar#foo], out)
+ end
+
+ def test_show_source_method_exceeds_super_chain
+ write_ruby <<~RUBY
+ class Baz
+ def foo
+ end
+ end
+
+ class Bar < Baz
+ def foo
+ super
+ end
+ end
+
+ binding.irb
+ RUBY
+
+ out = run_ruby_file do
+ type "show_source Bar#foo -ss"
+ type "exit"
+ end
+
+ assert_match(%r[Error: Couldn't locate a super definition for Bar#foo], out)
+ end
+
+ def test_show_source_method_accidental_characters
+ write_ruby <<~'RUBY'
+ class Baz
+ def foo
+ end
+ end
+
+ class Bar < Baz
+ def foo
+ super
+ end
+ end
+
+ binding.irb
+ RUBY
+
+ out = run_ruby_file do
+ type "show_source Bar#foo -sddddd"
+ type "exit"
+ end
+
+ assert_match(%r[#{@ruby_file.to_path}:2\s+def foo\r\n end], out)
+ end
+
+ def test_show_source_receiver_super
+ write_ruby <<~RUBY
+ class Baz
+ def foo
+ end
+ end
+
+ class Bar < Baz
+ def foo
+ super
+ end
+ end
+
+ binding.irb
+ RUBY
+
+ out = run_ruby_file do
+ type "bar = Bar.new"
+ type "show_source bar.foo -s"
+ type "exit"
+ end
+
+ assert_match(%r[#{@ruby_file.to_path}:2\s+def foo\r\n end], out)
+ end
+
+ def test_show_source_with_double_colons
+ write_ruby <<~RUBY
+ class Foo
+ end
+
+ class Foo
+ class Bar
+ end
+ end
+
+ binding.irb
+ RUBY
+
+ out = run_ruby_file do
+ type "show_source ::Foo"
+ type "exit"
+ end
+
+ assert_match(%r[#{@ruby_file.to_path}:1\s+class Foo\r\nend], out)
+
+ out = run_ruby_file do
+ type "show_source ::Foo::Bar"
+ type "exit"
+ end
+
+ assert_match(%r[#{@ruby_file.to_path}:5\s+class Bar\r\n end], out)
+ end
+
+ def test_show_source_keep_script_lines
+ pend unless defined?(RubyVM.keep_script_lines)
+
+ write_ruby <<~RUBY
+ binding.irb
+ RUBY
+
+ out = run_ruby_file do
+ type "def foo; end"
+ type "show_source foo"
+ type "exit"
+ end
+
+ assert_match(%r[#{@ruby_file.to_path}\(irb\):1\s+def foo; end], out)
+ end
+
+ def test_show_source_unavailable_source
+ write_ruby <<~RUBY
+ binding.irb
+ RUBY
+
+ out = run_ruby_file do
+ type "RubyVM.keep_script_lines = false if defined?(RubyVM.keep_script_lines)"
+ type "def foo; end"
+ type "show_source foo"
+ type "exit"
+ end
+ assert_match(%r[#{@ruby_file.to_path}\(irb\):2\s+Source not available], out)
+ end
+
+ def test_show_source_shows_binary_source
+ write_ruby <<~RUBY
+ # io-console is an indirect dependency of irb
+ require "io/console"
+
+ binding.irb
+ RUBY
+
+ out = run_ruby_file do
+ # IO::ConsoleMode is defined in io-console gem's C extension
+ type "show_source IO::ConsoleMode"
+ type "exit"
+ end
+
+ # A safeguard to make sure the test subject is actually defined
+ refute_match(/NameError/, out)
+ assert_match(%r[Defined in binary file:.+io/console], out)
+ end
+
+ def test_show_source_with_constant_lookup
+ write_ruby <<~RUBY
+ X = 1
+ module M
+ Y = 1
+ Z = 2
+ end
+ class A
+ Z = 1
+ Array = 1
+ class B
+ include M
+ Object.new.instance_eval { binding.irb }
+ end
+ end
+ RUBY
+
+ out = run_ruby_file do
+ type "show_source X"
+ type "show_source Y"
+ type "show_source Z"
+ type "show_source Array"
+ type "exit"
+ end
+
+ assert_match(%r[#{@ruby_file.to_path}:1\s+X = 1], out)
+ assert_match(%r[#{@ruby_file.to_path}:3\s+Y = 1], out)
+ assert_match(%r[#{@ruby_file.to_path}:7\s+Z = 1], out)
+ assert_match(%r[#{@ruby_file.to_path}:8\s+Array = 1], out)
+ end
+ end
+end
diff --git a/test/irb/helper.rb b/test/irb/helper.rb
index 55f9e083eb..1614b42adb 100644
--- a/test/irb/helper.rb
+++ b/test/irb/helper.rb
@@ -1,5 +1,6 @@
require "test/unit"
require "pathname"
+require "rubygems"
begin
require_relative "../lib/helper"
@@ -7,17 +8,22 @@ begin
rescue LoadError # ruby/ruby defines helpers differently
end
+begin
+ require "pty"
+rescue LoadError # some platforms don't support PTY
+end
+
module IRB
class InputMethod; end
end
module TestIRB
+ RUBY_3_4 = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0.dev")
class TestCase < Test::Unit::TestCase
class TestInputMethod < ::IRB::InputMethod
attr_reader :list, :line_no
def initialize(list = [])
- super("test")
@line_no = 0
@list = list
end
@@ -73,4 +79,141 @@ module TestIRB
}
end
end
+
+ class IntegrationTestCase < TestCase
+ LIB = File.expand_path("../../lib", __dir__)
+ TIMEOUT_SEC = 3
+
+ def setup
+ @envs = {}
+ @tmpfiles = []
+
+ unless defined?(PTY)
+ omit "Integration tests require PTY."
+ end
+
+ if ruby_core?
+ omit "This test works only under ruby/irb"
+ end
+
+ write_rc <<~RUBY
+ IRB.conf[:USE_PAGER] = false
+ RUBY
+ end
+
+ def teardown
+ @tmpfiles.each do |tmpfile|
+ File.unlink(tmpfile)
+ end
+ end
+
+ def run_ruby_file(&block)
+ cmd = [EnvUtil.rubybin, "-I", LIB, @ruby_file.to_path]
+ tmp_dir = Dir.mktmpdir
+
+ @commands = []
+ lines = []
+
+ yield
+
+ # Test should not depend on user's irbrc file
+ @envs["HOME"] ||= tmp_dir
+ @envs["XDG_CONFIG_HOME"] ||= tmp_dir
+ @envs["IRBRC"] = nil unless @envs.key?("IRBRC")
+
+ PTY.spawn(@envs.merge("TERM" => "dumb"), *cmd) do |read, write, pid|
+ Timeout.timeout(TIMEOUT_SEC) do
+ while line = safe_gets(read)
+ lines << line
+
+ # means the breakpoint is triggered
+ if line.match?(/binding\.irb/)
+ while command = @commands.shift
+ write.puts(command)
+ end
+ end
+ end
+ end
+ ensure
+ read.close
+ write.close
+ kill_safely(pid)
+ end
+
+ lines.join
+ rescue Timeout::Error
+ message = <<~MSG
+ Test timedout.
+
+ #{'=' * 30} OUTPUT #{'=' * 30}
+ #{lines.map { |l| " #{l}" }.join}
+ #{'=' * 27} END OF OUTPUT #{'=' * 27}
+ MSG
+ assert_block(message) { false }
+ ensure
+ FileUtils.remove_entry tmp_dir
+ end
+
+ # read.gets could raise exceptions on some platforms
+ # https://github.com/ruby/ruby/blob/master/ext/pty/pty.c#L721-L728
+ def safe_gets(read)
+ read.gets
+ rescue Errno::EIO
+ nil
+ end
+
+ def kill_safely pid
+ return if wait_pid pid, TIMEOUT_SEC
+
+ Process.kill :TERM, pid
+ return if wait_pid pid, 0.2
+
+ Process.kill :KILL, pid
+ Process.waitpid(pid)
+ rescue Errno::EPERM, Errno::ESRCH
+ end
+
+ def wait_pid pid, sec
+ total_sec = 0.0
+ wait_sec = 0.001 # 1ms
+
+ while total_sec < sec
+ if Process.waitpid(pid, Process::WNOHANG) == pid
+ return true
+ end
+ sleep wait_sec
+ total_sec += wait_sec
+ wait_sec *= 2
+ end
+
+ false
+ rescue Errno::ECHILD
+ true
+ end
+
+ def type(command)
+ @commands << command
+ end
+
+ def write_ruby(program)
+ @ruby_file = Tempfile.create(%w{irb- .rb})
+ @tmpfiles << @ruby_file
+ @ruby_file.write(program)
+ @ruby_file.close
+ end
+
+ def write_rc(content)
+ # Append irbrc content if a tempfile for it already exists
+ if @irbrc
+ @irbrc = File.open(@irbrc, "a")
+ else
+ @irbrc = Tempfile.new('irbrc')
+ @tmpfiles << @irbrc
+ end
+
+ @irbrc.write(content)
+ @irbrc.close
+ @envs['IRBRC'] = @irbrc.path
+ end
+ end
end
diff --git a/test/irb/test_cmd.rb b/test/irb/test_cmd.rb
deleted file mode 100644
index 448f42587a..0000000000
--- a/test/irb/test_cmd.rb
+++ /dev/null
@@ -1,947 +0,0 @@
-# frozen_string_literal: false
-require "rubygems"
-require "irb"
-require "irb/extend-command"
-
-require_relative "helper"
-
-module TestIRB
- class CommandTestCase < TestCase
- def setup
- @pwd = Dir.pwd
- @tmpdir = File.join(Dir.tmpdir, "test_reline_config_#{$$}")
- begin
- Dir.mkdir(@tmpdir)
- rescue Errno::EEXIST
- FileUtils.rm_rf(@tmpdir)
- Dir.mkdir(@tmpdir)
- end
- Dir.chdir(@tmpdir)
- @home_backup = ENV["HOME"]
- ENV["HOME"] = @tmpdir
- @xdg_config_home_backup = ENV.delete("XDG_CONFIG_HOME")
- save_encodings
- IRB.instance_variable_get(:@CONF).clear
- @is_win = (RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/)
- end
-
- def teardown
- ENV["XDG_CONFIG_HOME"] = @xdg_config_home_backup
- ENV["HOME"] = @home_backup
- Dir.chdir(@pwd)
- FileUtils.rm_rf(@tmpdir)
- restore_encodings
- end
-
- def execute_lines(*lines, conf: {}, main: self, irb_path: nil)
- IRB.init_config(nil)
- IRB.conf[:VERBOSE] = false
- IRB.conf[:PROMPT_MODE] = :SIMPLE
- IRB.conf.merge!(conf)
- input = TestInputMethod.new(lines)
- irb = IRB::Irb.new(IRB::WorkSpace.new(main), input)
- irb.context.return_format = "=> %s\n"
- irb.context.irb_path = irb_path if irb_path
- IRB.conf[:MAIN_CONTEXT] = irb.context
- capture_output do
- irb.eval_input
- end
- end
- end
-
- class FrozenObjectTest < CommandTestCase
- def test_calling_command_on_a_frozen_main
- main = Object.new.freeze
-
- out, err = execute_lines(
- "irb_info",
- main: main
- )
- assert_empty err
- assert_match(/RUBY_PLATFORM/, out)
- end
- end
-
- class CommnadAliasTest < CommandTestCase
- def test_vars_with_aliases
- @foo = "foo"
- $bar = "bar"
- out, err = execute_lines(
- "@foo\n",
- "$bar\n",
- )
- assert_empty err
- assert_match(/"foo"/, out)
- assert_match(/"bar"/, out)
- ensure
- remove_instance_variable(:@foo)
- $bar = nil
- end
- end
-
- class InfoTest < CommandTestCase
- def setup
- super
- @locals_backup = ENV.delete("LANG"), ENV.delete("LC_ALL")
- end
-
- def teardown
- super
- ENV["LANG"], ENV["LC_ALL"] = @locals_backup
- end
-
- def test_irb_info_multiline
- FileUtils.touch("#{@tmpdir}/.inputrc")
- FileUtils.touch("#{@tmpdir}/.irbrc")
-
- out, err = execute_lines(
- "irb_info",
- conf: { USE_MULTILINE: true, USE_SINGLELINE: false }
- )
-
- expected = %r{
- Ruby\sversion:\s.+\n
- IRB\sversion:\sirb\s.+\n
- InputMethod:\sAbstract\sInputMethod\n
- \.irbrc\spath:\s.+\n
- RUBY_PLATFORM:\s.+\n
- East\sAsian\sAmbiguous\sWidth:\s\d\n
- #{@is_win ? 'Code\spage:\s\d+\n' : ''}
- }x
-
- assert_empty err
- assert_match expected, out
- end
-
- def test_irb_info_singleline
- FileUtils.touch("#{@tmpdir}/.inputrc")
- FileUtils.touch("#{@tmpdir}/.irbrc")
-
- out, err = execute_lines(
- "irb_info",
- conf: { USE_MULTILINE: false, USE_SINGLELINE: true }
- )
-
- expected = %r{
- Ruby\sversion:\s.+\n
- IRB\sversion:\sirb\s.+\n
- InputMethod:\sAbstract\sInputMethod\n
- \.irbrc\spath:\s.+\n
- RUBY_PLATFORM:\s.+\n
- East\sAsian\sAmbiguous\sWidth:\s\d\n
- #{@is_win ? 'Code\spage:\s\d+\n' : ''}
- }x
-
- assert_empty err
- assert_match expected, out
- end
-
- def test_irb_info_multiline_without_rc_files
- inputrc_backup = ENV["INPUTRC"]
- ENV["INPUTRC"] = "unknown_inpurc"
- ext_backup = IRB::IRBRC_EXT
- IRB.__send__(:remove_const, :IRBRC_EXT)
- IRB.const_set(:IRBRC_EXT, "unknown_ext")
-
- out, err = execute_lines(
- "irb_info",
- conf: { USE_MULTILINE: true, USE_SINGLELINE: false }
- )
-
- expected = %r{
- Ruby\sversion:\s.+\n
- IRB\sversion:\sirb\s.+\n
- InputMethod:\sAbstract\sInputMethod\n
- RUBY_PLATFORM:\s.+\n
- East\sAsian\sAmbiguous\sWidth:\s\d\n
- #{@is_win ? 'Code\spage:\s\d+\n' : ''}
- }x
-
- assert_empty err
- assert_match expected, out
- ensure
- ENV["INPUTRC"] = inputrc_backup
- IRB.__send__(:remove_const, :IRBRC_EXT)
- IRB.const_set(:IRBRC_EXT, ext_backup)
- end
-
- def test_irb_info_singleline_without_rc_files
- inputrc_backup = ENV["INPUTRC"]
- ENV["INPUTRC"] = "unknown_inpurc"
- ext_backup = IRB::IRBRC_EXT
- IRB.__send__(:remove_const, :IRBRC_EXT)
- IRB.const_set(:IRBRC_EXT, "unknown_ext")
-
- out, err = execute_lines(
- "irb_info",
- conf: { USE_MULTILINE: false, USE_SINGLELINE: true }
- )
-
- expected = %r{
- Ruby\sversion:\s.+\n
- IRB\sversion:\sirb\s.+\n
- InputMethod:\sAbstract\sInputMethod\n
- RUBY_PLATFORM:\s.+\n
- East\sAsian\sAmbiguous\sWidth:\s\d\n
- #{@is_win ? 'Code\spage:\s\d+\n' : ''}
- }x
-
- assert_empty err
- assert_match expected, out
- ensure
- ENV["INPUTRC"] = inputrc_backup
- IRB.__send__(:remove_const, :IRBRC_EXT)
- IRB.const_set(:IRBRC_EXT, ext_backup)
- end
-
- def test_irb_info_lang
- FileUtils.touch("#{@tmpdir}/.inputrc")
- FileUtils.touch("#{@tmpdir}/.irbrc")
- ENV["LANG"] = "ja_JP.UTF-8"
- ENV["LC_ALL"] = "en_US.UTF-8"
-
- out, err = execute_lines(
- "irb_info",
- conf: { USE_MULTILINE: true, USE_SINGLELINE: false }
- )
-
- expected = %r{
- Ruby\sversion: .+\n
- IRB\sversion:\sirb .+\n
- InputMethod:\sAbstract\sInputMethod\n
- \.irbrc\spath: .+\n
- RUBY_PLATFORM: .+\n
- LANG\senv:\sja_JP\.UTF-8\n
- LC_ALL\senv:\sen_US\.UTF-8\n
- East\sAsian\sAmbiguous\sWidth:\s\d\n
- }x
-
- assert_empty err
- assert_match expected, out
- end
- end
-
- class MeasureTest < CommandTestCase
- def test_measure
- conf = {
- PROMPT: {
- DEFAULT: {
- PROMPT_I: '> ',
- PROMPT_S: '> ',
- PROMPT_C: '> ',
- PROMPT_N: '> '
- }
- },
- PROMPT_MODE: :DEFAULT,
- MEASURE: false
- }
-
- c = Class.new(Object)
- out, err = execute_lines(
- "3\n",
- "measure\n",
- "3\n",
- "measure :off\n",
- "3\n",
- conf: conf,
- main: c
- )
-
- assert_empty err
- assert_match(/\A=> 3\nTIME is added\.\n=> nil\nprocessing time: .+\n=> 3\n=> nil\n=> 3\n/, out)
- assert_empty(c.class_variables)
- end
-
- def test_measure_enabled_by_rc
- conf = {
- PROMPT: {
- DEFAULT: {
- PROMPT_I: '> ',
- PROMPT_S: '> ',
- PROMPT_C: '> ',
- PROMPT_N: '> '
- }
- },
- PROMPT_MODE: :DEFAULT,
- MEASURE: true
- }
-
- out, err = execute_lines(
- "3\n",
- "measure :off\n",
- "3\n",
- conf: conf,
- )
-
- assert_empty err
- assert_match(/\Aprocessing time: .+\n=> 3\n=> nil\n=> 3\n/, out)
- end
-
- def test_measure_enabled_by_rc_with_custom
- measuring_proc = proc { |line, line_no, &block|
- time = Time.now
- result = block.()
- puts 'custom processing time: %fs' % (Time.now - time) if IRB.conf[:MEASURE]
- result
- }
- conf = {
- PROMPT: {
- DEFAULT: {
- PROMPT_I: '> ',
- PROMPT_S: '> ',
- PROMPT_C: '> ',
- PROMPT_N: '> '
- }
- },
- PROMPT_MODE: :DEFAULT,
- MEASURE: true,
- MEASURE_PROC: { CUSTOM: measuring_proc }
- }
-
- out, err = execute_lines(
- "3\n",
- "measure :off\n",
- "3\n",
- conf: conf,
- )
- assert_empty err
- assert_match(/\Acustom processing time: .+\n=> 3\n=> nil\n=> 3\n/, out)
- end
-
- def test_measure_with_custom
- measuring_proc = proc { |line, line_no, &block|
- time = Time.now
- result = block.()
- puts 'custom processing time: %fs' % (Time.now - time) if IRB.conf[:MEASURE]
- result
- }
- conf = {
- PROMPT: {
- DEFAULT: {
- PROMPT_I: '> ',
- PROMPT_S: '> ',
- PROMPT_C: '> ',
- PROMPT_N: '> '
- }
- },
- PROMPT_MODE: :DEFAULT,
- MEASURE: false,
- MEASURE_PROC: { CUSTOM: measuring_proc }
- }
- out, err = execute_lines(
- "3\n",
- "measure\n",
- "3\n",
- "measure :off\n",
- "3\n",
- conf: conf
- )
-
- assert_empty err
- assert_match(/\A=> 3\nCUSTOM is added\.\n=> nil\ncustom processing time: .+\n=> 3\n=> nil\n=> 3\n/, out)
- end
-
- def test_measure_with_proc
- conf = {
- PROMPT: {
- DEFAULT: {
- PROMPT_I: '> ',
- PROMPT_S: '> ',
- PROMPT_C: '> ',
- PROMPT_N: '> '
- }
- },
- PROMPT_MODE: :DEFAULT,
- MEASURE: false,
- }
- c = Class.new(Object)
- out, err = execute_lines(
- "3\n",
- "measure { |context, code, line_no, &block|\n",
- " result = block.()\n",
- " puts 'aaa' if IRB.conf[:MEASURE]\n",
- " result\n",
- "}\n",
- "3\n",
- "measure { |context, code, line_no, &block|\n",
- " result = block.()\n",
- " puts 'bbb' if IRB.conf[:MEASURE]\n",
- " result\n",
- "}\n",
- "3\n",
- "measure :off\n",
- "3\n",
- conf: conf,
- main: c
- )
-
- assert_empty err
- assert_match(/\A=> 3\nBLOCK is added\.\n=> nil\naaa\n=> 3\nBLOCK is added.\naaa\n=> nil\nbbb\n=> 3\n=> nil\n=> 3\n/, out)
- assert_empty(c.class_variables)
- end
- end
-
- class IrbSourceTest < CommandTestCase
- def test_irb_source
- File.write("#{@tmpdir}/a.rb", "a = 'hi'\n")
- out, err = execute_lines(
- "a = 'bug17564'\n",
- "a\n",
- "irb_source '#{@tmpdir}/a.rb'\n",
- "a\n",
- )
- assert_empty err
- assert_pattern_list([
- /=> "bug17564"\n/,
- /=> "bug17564"\n/,
- / => "hi"\n/,
- / => nil\n/,
- /=> "hi"\n/,
- ], out)
- end
-
- def test_irb_source_without_argument
- out, err = execute_lines(
- "irb_source\n",
- )
- assert_empty err
- assert_match(/Please specify the file name./, out)
- end
- end
-
- class IrbLoadTest < CommandTestCase
- def test_irb_load
- File.write("#{@tmpdir}/a.rb", "a = 'hi'\n")
- out, err = execute_lines(
- "a = 'bug17564'\n",
- "a\n",
- "irb_load '#{@tmpdir}/a.rb'\n",
- "a\n",
- )
- assert_empty err
- assert_pattern_list([
- /=> "bug17564"\n/,
- /=> "bug17564"\n/,
- / => "hi"\n/,
- / => nil\n/,
- /=> "bug17564"\n/,
- ], out)
- end
-
- def test_irb_load_without_argument
- out, err = execute_lines(
- "irb_load\n",
- )
-
- assert_empty err
- assert_match(/Please specify the file name./, out)
- end
- end
-
- class ShowSourceTest < CommandTestCase
- def test_show_source
- out, err = execute_lines(
- "show_source IRB.conf\n",
- )
- assert_empty err
- assert_match(%r[/irb\.rb], out)
- end
-
- def test_show_source_method
- out, err = execute_lines(
- "p show_source('IRB.conf')\n",
- )
- assert_empty err
- assert_match(%r[/irb\.rb], out)
- end
-
- def test_show_source_string
- out, err = execute_lines(
- "show_source 'IRB.conf'\n",
- )
- assert_empty err
- assert_match(%r[/irb\.rb], out)
- end
-
- def test_show_source_alias
- out, err = execute_lines(
- "$ 'IRB.conf'\n",
- conf: { COMMAND_ALIASES: { :'$' => :show_source } }
- )
- assert_empty err
- assert_match(%r[/irb\.rb], out)
- end
-
- def test_show_source_end_finder
- pend if RUBY_ENGINE == 'truffleruby'
- eval(code = <<-EOS, binding, __FILE__, __LINE__ + 1)
- def show_source_test_method
- unless true
- end
- end unless defined?(show_source_test_method)
- EOS
-
- out, err = execute_lines(
- "show_source '#{self.class.name}#show_source_test_method'\n",
- )
-
- assert_empty err
- assert_include(out, code)
- end
-
- def test_show_source_private_instance
- pend if RUBY_ENGINE == 'truffleruby'
- eval(code = <<-EOS, binding, __FILE__, __LINE__ + 1)
- class PrivateInstanceTest
- private def show_source_test_method
- unless true
- end
- end unless private_method_defined?(:show_source_test_method)
- end
- EOS
-
- out, err = execute_lines(
- "show_source '#{self.class.name}::PrivateInstanceTest#show_source_test_method'\n",
- )
-
- assert_empty err
- assert_include(out, code.lines[1..-2].join)
- end
-
-
- def test_show_source_private
- pend if RUBY_ENGINE == 'truffleruby'
- eval(code = <<-EOS, binding, __FILE__, __LINE__ + 1)
- class PrivateTest
- private def show_source_test_method
- unless true
- end
- end unless private_method_defined?(:show_source_test_method)
- end
-
- Instance = PrivateTest.new unless defined?(Instance)
- EOS
-
- out, err = execute_lines(
- "show_source '#{self.class.name}::Instance.show_source_test_method'\n",
- )
-
- assert_empty err
- assert_include(out, code.lines[1..-4].join)
- end
- end
-
- class WorkspaceCommandTestCase < CommandTestCase
- def setup
- super
- # create Foo under the test class's namespace so it doesn't pollute global namespace
- self.class.class_eval <<~RUBY
- class Foo; end
- RUBY
- end
- end
-
- class CwwsTest < WorkspaceCommandTestCase
- def test_cwws_returns_the_current_workspace_object
- out, err = execute_lines(
- "cwws.class",
- )
-
- assert_empty err
- assert_include(out, self.class.name)
- end
- end
-
- class PushwsTest < WorkspaceCommandTestCase
- def test_pushws_switches_to_new_workspace_and_pushes_the_current_one_to_the_stack
- out, err = execute_lines(
- "pushws #{self.class}::Foo.new\n",
- "cwws.class",
- )
- assert_empty err
- assert_include(out, "#{self.class}::Foo")
- end
-
- def test_pushws_extends_the_new_workspace_with_command_bundle
- out, err = execute_lines(
- "pushws Object.new\n",
- "self.singleton_class.ancestors"
- )
- assert_empty err
- assert_include(out, "IRB::ExtendCommandBundle")
- end
-
- def test_pushws_prints_help_message_when_no_arg_is_given
- out, err = execute_lines(
- "pushws\n",
- )
- assert_empty err
- assert_match(/No other workspace/, out)
- end
- end
-
- class WorkspacesTest < WorkspaceCommandTestCase
- def test_workspaces_returns_the_array_of_non_main_workspaces
- out, err = execute_lines(
- "pushws #{self.class}::Foo.new\n",
- "workspaces.map { |w| w.class.name }",
- )
-
- assert_empty err
- # self.class::Foo would be the current workspace
- # self.class would be the old workspace that's pushed to the stack
- assert_include(out, "=> [\"#{self.class}\"]")
- end
-
- def test_workspaces_returns_empty_array_when_no_workspaces_were_added
- out, err = execute_lines(
- "workspaces.map(&:to_s)",
- )
-
- assert_empty err
- assert_include(out, "=> []")
- end
- end
-
- class PopwsTest < WorkspaceCommandTestCase
- def test_popws_replaces_the_current_workspace_with_the_previous_one
- out, err = execute_lines(
- "pushws Foo.new\n",
- "popws\n",
- "cwws.class",
- )
- assert_empty err
- assert_include(out, "=> #{self.class}")
- end
-
- def test_popws_prints_help_message_if_the_workspace_is_empty
- out, err = execute_lines(
- "popws\n",
- )
- assert_empty err
- assert_match(/workspace stack empty/, out)
- end
- end
-
- class ChwsTest < WorkspaceCommandTestCase
- def test_chws_replaces_the_current_workspace
- out, err = execute_lines(
- "chws #{self.class}::Foo.new\n",
- "cwws.class",
- )
- assert_empty err
- assert_include(out, "=> #{self.class}::Foo")
- end
-
- def test_chws_does_nothing_when_receiving_no_argument
- out, err = execute_lines(
- "chws\n",
- "cwws.class",
- )
- assert_empty err
- assert_include(out, "=> #{self.class}")
- end
- end
-
- class WhereamiTest < CommandTestCase
- def test_whereami
- out, err = execute_lines(
- "whereami\n",
- )
- assert_empty err
- assert_match(/^From: .+ @ line \d+ :\n/, out)
- end
-
- def test_whereami_alias
- out, err = execute_lines(
- "@\n",
- )
- assert_empty err
- assert_match(/^From: .+ @ line \d+ :\n/, out)
- end
- end
-
-
- class ShowCmdsTest < CommandTestCase
- def test_show_cmds
- out, err = execute_lines(
- "show_cmds\n"
- )
-
- assert_empty err
- assert_match(/List all available commands and their description/, out)
- assert_match(/Start the debugger of debug\.gem/, out)
- end
- end
-
- class LsTest < CommandTestCase
- def test_ls
- out, err = execute_lines(
- "class P\n",
- " def m() end\n",
- " def m2() end\n",
- "end\n",
-
- "class C < P\n",
- " def m1() end\n",
- " def m2() end\n",
- "end\n",
-
- "module M\n",
- " def m1() end\n",
- " def m3() end\n",
- "end\n",
-
- "module M2\n",
- " include M\n",
- " def m4() end\n",
- "end\n",
-
- "obj = C.new\n",
- "obj.instance_variable_set(:@a, 1)\n",
- "obj.extend M2\n",
- "def obj.m5() end\n",
- "ls obj\n",
- )
-
- assert_empty err
- assert_match(/^instance variables:\s+@a\n/m, out)
- assert_match(/P#methods:\s+m\n/m, out)
- assert_match(/C#methods:\s+m2\n/m, out)
- assert_match(/M#methods:\s+m1\s+m3\n/m, out)
- assert_match(/M2#methods:\s+m4\n/m, out)
- assert_match(/C.methods:\s+m5\n/m, out)
- end
-
- def test_ls_class
- out, err = execute_lines(
- "module M1\n",
- " def m2; end\n",
- " def m3; end\n",
- "end\n",
-
- "class C1\n",
- " def m1; end\n",
- " def m2; end\n",
- "end\n",
-
- "class C2 < C1\n",
- " include M1\n",
- " def m3; end\n",
- " def m4; end\n",
- " def self.m3; end\n",
- " def self.m5; end\n",
- "end\n",
- "ls C2"
- )
-
- assert_empty err
- assert_match(/C2.methods:\s+m3\s+m5\n/, out)
- assert_match(/C2#methods:\s+m3\s+m4\n.*M1#methods:\s+m2\n.*C1#methods:\s+m1\n/, out)
- assert_not_match(/Module#methods/, out)
- assert_not_match(/Class#methods/, out)
- end
-
- def test_ls_module
- out, err = execute_lines(
- "module M1\n",
- " def m1; end\n",
- " def m2; end\n",
- "end\n",
-
- "module M2\n",
- " include M1\n",
- " def m1; end\n",
- " def m3; end\n",
- " def self.m4; end\n",
- "end\n",
- "ls M2"
- )
-
- assert_empty err
- assert_match(/M2\.methods:\s+m4\n/, out)
- assert_match(/M2#methods:\s+m1\s+m3\n.*M1#methods:\s+m2\n/, out)
- assert_not_match(/Module#methods/, out)
- end
-
- def test_ls_instance
- out, err = execute_lines(
- "class Foo; def bar; end; end\n",
- "ls Foo.new"
- )
-
- assert_empty err
- assert_match(/Foo#methods:\s+bar/, out)
- # don't duplicate
- assert_not_match(/Foo#methods:\s+bar\n.*Foo#methods/, out)
- end
-
- def test_ls_grep
- out, err = execute_lines("ls 42\n")
- assert_empty err
- assert_match(/times/, out)
- assert_match(/polar/, out)
-
- [
- "ls 42, grep: /times/\n",
- "ls 42 -g times\n",
- "ls 42 -G times\n",
- ].each do |line|
- out, err = execute_lines(line)
- assert_empty err
- assert_match(/times/, out)
- assert_not_match(/polar/, out)
- end
- end
-
- def test_ls_grep_empty
- out, err = execute_lines("ls\n")
- assert_empty err
- assert_match(/whereami/, out)
- assert_match(/show_source/, out)
-
- [
- "ls grep: /whereami/\n",
- "ls -g whereami\n",
- "ls -G whereami\n",
- ].each do |line|
- out, err = execute_lines(line)
- assert_empty err
- assert_match(/whereami/, out)
- assert_not_match(/show_source/, out)
- end
- end
-
- def test_ls_with_no_singleton_class
- out, err = execute_lines(
- "ls 42",
- )
- assert_empty err
- assert_match(/Comparable#methods:\s+/, out)
- assert_match(/Numeric#methods:\s+/, out)
- assert_match(/Integer#methods:\s+/, out)
- end
- end
-
- class ShowDocTest < CommandTestCase
- def test_help
- out, err = execute_lines(
- "help String#gsub\n",
- "\n",
- )
-
- # the former is what we'd get without document content installed, like on CI
- # the latter is what we may get locally
- possible_rdoc_output = [/Nothing known about String#gsub/, /gsub\(pattern\)/]
- assert_include err, "[Deprecation] The `help` command will be repurposed to display command help in the future.\n"
- assert(possible_rdoc_output.any? { |output| output.match?(out) }, "Expect the `help` command to match one of the possible outputs. Got:\n#{out}")
- ensure
- # this is the only way to reset the redefined method without coupling the test with its implementation
- EnvUtil.suppress_warning { load "irb/cmd/help.rb" }
- end
-
- def test_show_doc
- out, err = execute_lines(
- "show_doc String#gsub\n",
- "\n",
- )
-
- # the former is what we'd get without document content installed, like on CI
- # the latter is what we may get locally
- possible_rdoc_output = [/Nothing known about String#gsub/, /gsub\(pattern\)/]
- assert_not_include err, "[Deprecation]"
- assert(possible_rdoc_output.any? { |output| output.match?(out) }, "Expect the `show_doc` command to match one of the possible outputs. Got:\n#{out}")
- ensure
- # this is the only way to reset the redefined method without coupling the test with its implementation
- EnvUtil.suppress_warning { load "irb/cmd/help.rb" }
- end
-
- def test_show_doc_without_rdoc
- out, err = without_rdoc do
- execute_lines(
- "show_doc String#gsub\n",
- "\n",
- )
- end
-
- # if it fails to require rdoc, it only returns the command object
- assert_match(/=> nil\n/, out)
- assert_include(err, "Can't display document because `rdoc` is not installed.\n")
- ensure
- # this is the only way to reset the redefined method without coupling the test with its implementation
- EnvUtil.suppress_warning { load "irb/cmd/help.rb" }
- end
- end
-
- class EditTest < CommandTestCase
- def setup
- @original_editor = ENV["EDITOR"]
- # noop the command so nothing gets executed
- ENV["EDITOR"] = ": code"
- end
-
- def teardown
- ENV["EDITOR"] = @original_editor
- end
-
- def test_edit_without_arg
- out, err = execute_lines(
- "edit",
- irb_path: __FILE__
- )
-
- assert_empty err
- assert_match("path: #{__FILE__}", out)
- assert_match("command: ': code'", out)
- end
-
- def test_edit_with_path
- out, err = execute_lines(
- "edit #{__FILE__}"
- )
-
- assert_empty err
- assert_match("path: #{__FILE__}", out)
- assert_match("command: ': code'", out)
- end
-
- def test_edit_with_non_existing_path
- out, err = execute_lines(
- "edit test_cmd_non_existing_path.rb"
- )
-
- assert_empty err
- assert_match(/Can not find file: test_cmd_non_existing_path\.rb/, out)
- end
-
- def test_edit_with_constant
- out, err = execute_lines(
- "edit IRB::Irb"
- )
-
- assert_empty err
- assert_match(/path: .*\/lib\/irb\.rb/, out)
- assert_match("command: ': code'", out)
- end
-
- def test_edit_with_class_method
- out, err = execute_lines(
- "edit IRB.start"
- )
-
- assert_empty err
- assert_match(/path: .*\/lib\/irb\.rb/, out)
- assert_match("command: ': code'", out)
- end
-
- def test_edit_with_instance_method
- out, err = execute_lines(
- "edit IRB::Irb#run"
- )
-
- assert_empty err
- assert_match(/path: .*\/lib\/irb\.rb/, out)
- assert_match("command: ': code'", out)
- end
- end
-end
diff --git a/test/irb/test_color.rb b/test/irb/test_color.rb
index 652396c89e..9d78f5233e 100644
--- a/test/irb/test_color.rb
+++ b/test/irb/test_color.rb
@@ -1,12 +1,11 @@
# frozen_string_literal: false
require 'irb/color'
-require 'rubygems'
require 'stringio'
require_relative "helper"
module TestIRB
- class TestColor < TestCase
+ class ColorTest < TestCase
CLEAR = "\e[0m"
BOLD = "\e[1m"
UNDERLINE = "\e[4m"
@@ -100,7 +99,7 @@ module TestIRB
"foo %i[bar]" => "foo #{YELLOW}%i[#{CLEAR}#{YELLOW}bar#{CLEAR}#{YELLOW}]#{CLEAR}",
"foo :@bar, baz, :@@qux, :$quux" => "foo #{YELLOW}:#{CLEAR}#{YELLOW}@bar#{CLEAR}, baz, #{YELLOW}:#{CLEAR}#{YELLOW}@@qux#{CLEAR}, #{YELLOW}:#{CLEAR}#{YELLOW}$quux#{CLEAR}",
"`echo`" => "#{RED}#{BOLD}`#{CLEAR}#{RED}echo#{CLEAR}#{RED}#{BOLD}`#{CLEAR}",
- "\t" => "\t", # not ^I
+ "\t" => Reline::Unicode.escape_for_print("\t") == ' ' ? ' ' : "\t", # not ^I
"foo(*%W(bar))" => "foo(*#{RED}#{BOLD}%W(#{CLEAR}#{RED}bar#{CLEAR}#{RED}#{BOLD})#{CLEAR})",
"$stdout" => "#{GREEN}#{BOLD}$stdout#{CLEAR}",
"__END__" => "#{GREEN}__END__#{CLEAR}",
diff --git a/test/irb/test_color_printer.rb b/test/irb/test_color_printer.rb
index a717940d81..c2c624d868 100644
--- a/test/irb/test_color_printer.rb
+++ b/test/irb/test_color_printer.rb
@@ -1,12 +1,11 @@
# frozen_string_literal: false
require 'irb/color_printer'
-require 'rubygems'
require 'stringio'
require_relative "helper"
module TestIRB
- class TestColorPrinter < TestCase
+ class ColorPrinterTest < TestCase
CLEAR = "\e[0m"
BOLD = "\e[1m"
RED = "\e[31m"
@@ -41,7 +40,7 @@ module TestIRB
{
1 => "#{BLUE}#{BOLD}1#{CLEAR}\n",
"a\nb" => %[#{RED}#{BOLD}"#{CLEAR}#{RED}a\\nb#{CLEAR}#{RED}#{BOLD}"#{CLEAR}\n],
- IRBTestColorPrinter.new('test') => "#{GREEN}#<struct TestIRB::TestColorPrinter::IRBTestColorPrinter#{CLEAR} a#{GREEN}=#{CLEAR}#{RED}#{BOLD}\"#{CLEAR}#{RED}test#{CLEAR}#{RED}#{BOLD}\"#{CLEAR}#{GREEN}>#{CLEAR}\n",
+ IRBTestColorPrinter.new('test') => "#{GREEN}#<struct TestIRB::ColorPrinterTest::IRBTestColorPrinter#{CLEAR} a#{GREEN}=#{CLEAR}#{RED}#{BOLD}\"#{CLEAR}#{RED}test#{CLEAR}#{RED}#{BOLD}\"#{CLEAR}#{GREEN}>#{CLEAR}\n",
Ripper::Lexer.new('1').scan => "[#{GREEN}#<Ripper::Lexer::Elem:#{CLEAR} on_int@1:0 END token: #{RED}#{BOLD}\"#{CLEAR}#{RED}1#{CLEAR}#{RED}#{BOLD}\"#{CLEAR}#{GREEN}>#{CLEAR}]\n",
Class.new{define_method(:pretty_print){|q| q.text("[__FILE__, __LINE__, __ENCODING__]")}}.new => "[#{CYAN}#{BOLD}__FILE__#{CLEAR}, #{CYAN}#{BOLD}__LINE__#{CLEAR}, #{CYAN}#{BOLD}__ENCODING__#{CLEAR}]\n",
}.each do |object, result|
diff --git a/test/irb/test_command.rb b/test/irb/test_command.rb
new file mode 100644
index 0000000000..8cb8928adb
--- /dev/null
+++ b/test/irb/test_command.rb
@@ -0,0 +1,971 @@
+# frozen_string_literal: false
+require "irb"
+
+require_relative "helper"
+
+module TestIRB
+ class CommandTestCase < TestCase
+ def setup
+ @pwd = Dir.pwd
+ @tmpdir = File.join(Dir.tmpdir, "test_reline_config_#{$$}")
+ begin
+ Dir.mkdir(@tmpdir)
+ rescue Errno::EEXIST
+ FileUtils.rm_rf(@tmpdir)
+ Dir.mkdir(@tmpdir)
+ end
+ Dir.chdir(@tmpdir)
+ @home_backup = ENV["HOME"]
+ ENV["HOME"] = @tmpdir
+ @xdg_config_home_backup = ENV.delete("XDG_CONFIG_HOME")
+ save_encodings
+ IRB.instance_variable_get(:@CONF).clear
+ IRB.instance_variable_set(:@existing_rc_name_generators, nil)
+ @is_win = (RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/)
+ end
+
+ def teardown
+ ENV["XDG_CONFIG_HOME"] = @xdg_config_home_backup
+ ENV["HOME"] = @home_backup
+ Dir.chdir(@pwd)
+ FileUtils.rm_rf(@tmpdir)
+ restore_encodings
+ end
+
+ def execute_lines(*lines, conf: {}, main: self, irb_path: nil)
+ capture_output do
+ IRB.init_config(nil)
+ IRB.conf[:VERBOSE] = false
+ IRB.conf[:PROMPT_MODE] = :SIMPLE
+ IRB.conf[:USE_PAGER] = false
+ IRB.conf.merge!(conf)
+ input = TestInputMethod.new(lines)
+ irb = IRB::Irb.new(IRB::WorkSpace.new(main), input)
+ irb.context.return_format = "=> %s\n"
+ irb.context.irb_path = irb_path if irb_path
+ IRB.conf[:MAIN_CONTEXT] = irb.context
+ irb.eval_input
+ end
+ end
+ end
+
+ class FrozenObjectTest < CommandTestCase
+ def test_calling_command_on_a_frozen_main
+ main = Object.new.freeze
+
+ out, err = execute_lines(
+ "irb_info",
+ main: main
+ )
+ assert_empty(err)
+ assert_match(/RUBY_PLATFORM/, out)
+ end
+ end
+
+ class InfoTest < CommandTestCase
+ def setup
+ super
+ @locals_backup = ENV.delete("LANG"), ENV.delete("LC_ALL")
+ end
+
+ def teardown
+ super
+ ENV["LANG"], ENV["LC_ALL"] = @locals_backup
+ end
+
+ def test_irb_info_multiline
+ FileUtils.touch("#{@tmpdir}/.inputrc")
+ FileUtils.touch("#{@tmpdir}/.irbrc")
+ FileUtils.touch("#{@tmpdir}/_irbrc")
+
+ out, err = execute_lines(
+ "irb_info",
+ conf: { USE_MULTILINE: true, USE_SINGLELINE: false }
+ )
+
+ expected = %r{
+ Ruby\sversion:\s.+\n
+ IRB\sversion:\sirb\s.+\n
+ InputMethod:\sAbstract\sInputMethod\n
+ Completion: .+\n
+ \.irbrc\spaths:.*\.irbrc.*_irbrc\n
+ RUBY_PLATFORM:\s.+\n
+ East\sAsian\sAmbiguous\sWidth:\s\d\n
+ #{@is_win ? 'Code\spage:\s\d+\n' : ''}
+ }x
+
+ assert_empty err
+ assert_match expected, out
+ end
+
+ def test_irb_info_singleline
+ FileUtils.touch("#{@tmpdir}/.inputrc")
+ FileUtils.touch("#{@tmpdir}/.irbrc")
+
+ out, err = execute_lines(
+ "irb_info",
+ conf: { USE_MULTILINE: false, USE_SINGLELINE: true }
+ )
+
+ expected = %r{
+ Ruby\sversion:\s.+\n
+ IRB\sversion:\sirb\s.+\n
+ InputMethod:\sAbstract\sInputMethod\n
+ Completion: .+\n
+ \.irbrc\spaths:\s.+\n
+ RUBY_PLATFORM:\s.+\n
+ East\sAsian\sAmbiguous\sWidth:\s\d\n
+ #{@is_win ? 'Code\spage:\s\d+\n' : ''}
+ }x
+
+ assert_empty err
+ assert_match expected, out
+ end
+
+ def test_irb_info_multiline_without_rc_files
+ inputrc_backup = ENV["INPUTRC"]
+ ENV["INPUTRC"] = "unknown_inpurc"
+ ext_backup = IRB::IRBRC_EXT
+ IRB.__send__(:remove_const, :IRBRC_EXT)
+ IRB.const_set(:IRBRC_EXT, "unknown_ext")
+
+ out, err = execute_lines(
+ "irb_info",
+ conf: { USE_MULTILINE: true, USE_SINGLELINE: false }
+ )
+
+ expected = %r{
+ Ruby\sversion:\s.+\n
+ IRB\sversion:\sirb\s.+\n
+ InputMethod:\sAbstract\sInputMethod\n
+ Completion: .+\n
+ RUBY_PLATFORM:\s.+\n
+ East\sAsian\sAmbiguous\sWidth:\s\d\n
+ #{@is_win ? 'Code\spage:\s\d+\n' : ''}
+ }x
+
+ assert_empty err
+ assert_match expected, out
+ ensure
+ ENV["INPUTRC"] = inputrc_backup
+ IRB.__send__(:remove_const, :IRBRC_EXT)
+ IRB.const_set(:IRBRC_EXT, ext_backup)
+ end
+
+ def test_irb_info_singleline_without_rc_files
+ inputrc_backup = ENV["INPUTRC"]
+ ENV["INPUTRC"] = "unknown_inpurc"
+ ext_backup = IRB::IRBRC_EXT
+ IRB.__send__(:remove_const, :IRBRC_EXT)
+ IRB.const_set(:IRBRC_EXT, "unknown_ext")
+
+ out, err = execute_lines(
+ "irb_info",
+ conf: { USE_MULTILINE: false, USE_SINGLELINE: true }
+ )
+
+ expected = %r{
+ Ruby\sversion:\s.+\n
+ IRB\sversion:\sirb\s.+\n
+ InputMethod:\sAbstract\sInputMethod\n
+ Completion: .+\n
+ RUBY_PLATFORM:\s.+\n
+ East\sAsian\sAmbiguous\sWidth:\s\d\n
+ #{@is_win ? 'Code\spage:\s\d+\n' : ''}
+ }x
+
+ assert_empty err
+ assert_match expected, out
+ ensure
+ ENV["INPUTRC"] = inputrc_backup
+ IRB.__send__(:remove_const, :IRBRC_EXT)
+ IRB.const_set(:IRBRC_EXT, ext_backup)
+ end
+
+ def test_irb_info_lang
+ FileUtils.touch("#{@tmpdir}/.inputrc")
+ FileUtils.touch("#{@tmpdir}/.irbrc")
+ ENV["LANG"] = "ja_JP.UTF-8"
+ ENV["LC_ALL"] = "en_US.UTF-8"
+
+ out, err = execute_lines(
+ "irb_info",
+ conf: { USE_MULTILINE: true, USE_SINGLELINE: false }
+ )
+
+ expected = %r{
+ Ruby\sversion: .+\n
+ IRB\sversion:\sirb .+\n
+ InputMethod:\sAbstract\sInputMethod\n
+ Completion: .+\n
+ \.irbrc\spaths: .+\n
+ RUBY_PLATFORM: .+\n
+ LANG\senv:\sja_JP\.UTF-8\n
+ LC_ALL\senv:\sen_US\.UTF-8\n
+ East\sAsian\sAmbiguous\sWidth:\s\d\n
+ }x
+
+ assert_empty err
+ assert_match expected, out
+ end
+ end
+
+ class MeasureTest < CommandTestCase
+ def test_measure
+ conf = {
+ PROMPT: {
+ DEFAULT: {
+ PROMPT_I: '> ',
+ PROMPT_S: '> ',
+ PROMPT_C: '> '
+ }
+ },
+ PROMPT_MODE: :DEFAULT,
+ MEASURE: false
+ }
+
+ c = Class.new(Object)
+ out, err = execute_lines(
+ "measure\n",
+ "3\n",
+ "measure :off\n",
+ "3\n",
+ "measure :on\n",
+ "3\n",
+ "measure :off\n",
+ "3\n",
+ conf: conf,
+ main: c
+ )
+
+ assert_empty err
+ assert_match(/\A(TIME is added\.\n=> nil\nprocessing time: .+\n=> 3\n=> nil\n=> 3\n){2}/, out)
+ assert_empty(c.class_variables)
+ end
+
+ def test_measure_keeps_previous_value
+ conf = {
+ PROMPT: {
+ DEFAULT: {
+ PROMPT_I: '> ',
+ PROMPT_S: '> ',
+ PROMPT_C: '> '
+ }
+ },
+ PROMPT_MODE: :DEFAULT,
+ MEASURE: false
+ }
+
+ c = Class.new(Object)
+ out, err = execute_lines(
+ "measure\n",
+ "3\n",
+ "_\n",
+ conf: conf,
+ main: c
+ )
+
+ assert_empty err
+ assert_match(/\ATIME is added\.\n=> nil\nprocessing time: .+\n=> 3\nprocessing time: .+\n=> 3/, out)
+ assert_empty(c.class_variables)
+ end
+
+ def test_measure_enabled_by_rc
+ conf = {
+ PROMPT: {
+ DEFAULT: {
+ PROMPT_I: '> ',
+ PROMPT_S: '> ',
+ PROMPT_C: '> '
+ }
+ },
+ PROMPT_MODE: :DEFAULT,
+ MEASURE: true
+ }
+
+ out, err = execute_lines(
+ "3\n",
+ "measure :off\n",
+ "3\n",
+ conf: conf,
+ )
+
+ assert_empty err
+ assert_match(/\Aprocessing time: .+\n=> 3\n=> nil\n=> 3\n/, out)
+ end
+
+ def test_measure_enabled_by_rc_with_custom
+ measuring_proc = proc { |line, line_no, &block|
+ time = Time.now
+ result = block.()
+ puts 'custom processing time: %fs' % (Time.now - time) if IRB.conf[:MEASURE]
+ result
+ }
+ conf = {
+ PROMPT: {
+ DEFAULT: {
+ PROMPT_I: '> ',
+ PROMPT_S: '> ',
+ PROMPT_C: '> '
+ }
+ },
+ PROMPT_MODE: :DEFAULT,
+ MEASURE: true,
+ MEASURE_PROC: { CUSTOM: measuring_proc }
+ }
+
+ out, err = execute_lines(
+ "3\n",
+ "measure :off\n",
+ "3\n",
+ conf: conf,
+ )
+ assert_empty err
+ assert_match(/\Acustom processing time: .+\n=> 3\n=> nil\n=> 3\n/, out)
+ end
+
+ def test_measure_with_custom
+ measuring_proc = proc { |line, line_no, &block|
+ time = Time.now
+ result = block.()
+ puts 'custom processing time: %fs' % (Time.now - time) if IRB.conf[:MEASURE]
+ result
+ }
+ conf = {
+ PROMPT: {
+ DEFAULT: {
+ PROMPT_I: '> ',
+ PROMPT_S: '> ',
+ PROMPT_C: '> '
+ }
+ },
+ PROMPT_MODE: :DEFAULT,
+ MEASURE: false,
+ MEASURE_PROC: { CUSTOM: measuring_proc }
+ }
+ out, err = execute_lines(
+ "3\n",
+ "measure\n",
+ "3\n",
+ "measure :off\n",
+ "3\n",
+ conf: conf
+ )
+
+ assert_empty err
+ assert_match(/\A=> 3\nCUSTOM is added\.\n=> nil\ncustom processing time: .+\n=> 3\n=> nil\n=> 3\n/, out)
+ end
+
+ def test_measure_toggle
+ conf = {
+ PROMPT: {
+ DEFAULT: {
+ PROMPT_I: '> ',
+ PROMPT_S: '> ',
+ PROMPT_C: '> '
+ }
+ },
+ PROMPT_MODE: :DEFAULT,
+ MEASURE: false,
+ MEASURE_PROC: {
+ FOO: proc { |&block| puts 'foo'; block.call },
+ BAR: proc { |&block| puts 'bar'; block.call }
+ }
+ }
+ out, err = execute_lines(
+ "measure :foo\n",
+ "1\n",
+ "measure :on, :bar\n",
+ "2\n",
+ "measure :off, :foo\n",
+ "3\n",
+ "measure :off, :bar\n",
+ "4\n",
+ conf: conf
+ )
+
+ assert_empty err
+ assert_match(/\AFOO is added\.\n=> nil\nfoo\n=> 1\nBAR is added\.\n=> nil\nbar\nfoo\n=> 2\n=> nil\nbar\n=> 3\n=> nil\n=> 4\n/, out)
+ end
+
+ def test_measure_with_proc_warning
+ conf = {
+ PROMPT: {
+ DEFAULT: {
+ PROMPT_I: '> ',
+ PROMPT_S: '> ',
+ PROMPT_C: '> '
+ }
+ },
+ PROMPT_MODE: :DEFAULT,
+ MEASURE: false,
+ }
+ c = Class.new(Object)
+ out, err = execute_lines(
+ "3\n",
+ "measure do\n",
+ "3\n",
+ conf: conf,
+ main: c
+ )
+
+ assert_match(/to add custom measure/, err)
+ assert_match(/\A=> 3\n=> nil\n=> 3\n/, out)
+ assert_empty(c.class_variables)
+ end
+ end
+
+ class IrbSourceTest < CommandTestCase
+ def test_irb_source
+ File.write("#{@tmpdir}/a.rb", "a = 'hi'\n")
+ out, err = execute_lines(
+ "a = 'bug17564'\n",
+ "a\n",
+ "irb_source '#{@tmpdir}/a.rb'\n",
+ "a\n",
+ )
+ assert_empty err
+ assert_pattern_list([
+ /=> "bug17564"\n/,
+ /=> "bug17564"\n/,
+ / => "hi"\n/,
+ / => nil\n/,
+ /=> "hi"\n/,
+ ], out)
+ end
+
+ def test_irb_source_without_argument
+ out, err = execute_lines(
+ "irb_source\n",
+ )
+ assert_empty err
+ assert_match(/Please specify the file name./, out)
+ end
+ end
+
+ class IrbLoadTest < CommandTestCase
+ def test_irb_load
+ File.write("#{@tmpdir}/a.rb", "a = 'hi'\n")
+ out, err = execute_lines(
+ "a = 'bug17564'\n",
+ "a\n",
+ "irb_load '#{@tmpdir}/a.rb'\n",
+ "a\n",
+ )
+ assert_empty err
+ assert_pattern_list([
+ /=> "bug17564"\n/,
+ /=> "bug17564"\n/,
+ / => "hi"\n/,
+ / => nil\n/,
+ /=> "bug17564"\n/,
+ ], out)
+ end
+
+ def test_irb_load_without_argument
+ out, err = execute_lines(
+ "irb_load\n",
+ )
+
+ assert_empty err
+ assert_match(/Please specify the file name./, out)
+ end
+ end
+
+ class WorkspaceCommandTestCase < CommandTestCase
+ def setup
+ super
+ # create Foo under the test class's namespace so it doesn't pollute global namespace
+ self.class.class_eval <<~RUBY
+ class Foo; end
+ RUBY
+ end
+ end
+
+ class CwwsTest < WorkspaceCommandTestCase
+ def test_cwws_returns_the_current_workspace_object
+ out, err = execute_lines(
+ "cwws"
+ )
+
+ assert_empty err
+ assert_include(out, "Current workspace: #{self}")
+ end
+ end
+
+ class PushwsTest < WorkspaceCommandTestCase
+ def test_pushws_switches_to_new_workspace_and_pushes_the_current_one_to_the_stack
+ out, err = execute_lines(
+ "pushws #{self.class}::Foo.new",
+ "self.class",
+ "popws",
+ "self.class"
+ )
+ assert_empty err
+
+ assert_match(/=> #{self.class}::Foo\n/, out)
+ assert_match(/=> #{self.class}\n$/, out)
+ end
+
+ def test_pushws_extends_the_new_workspace_with_command_bundle
+ out, err = execute_lines(
+ "pushws Object.new",
+ "self.singleton_class.ancestors"
+ )
+ assert_empty err
+ assert_include(out, "IRB::ExtendCommandBundle")
+ end
+
+ def test_pushws_prints_workspace_stack_when_no_arg_is_given
+ out, err = execute_lines(
+ "pushws",
+ )
+ assert_empty err
+ assert_include(out, "[#<TestIRB::PushwsTe...>]")
+ end
+
+ def test_pushws_without_argument_swaps_the_top_two_workspaces
+ out, err = execute_lines(
+ "pushws #{self.class}::Foo.new",
+ "self.class",
+ "pushws",
+ "self.class"
+ )
+ assert_empty err
+ assert_match(/=> #{self.class}::Foo\n/, out)
+ assert_match(/=> #{self.class}\n$/, out)
+ end
+ end
+
+ class WorkspacesTest < WorkspaceCommandTestCase
+ def test_workspaces_returns_the_stack_of_workspaces
+ out, err = execute_lines(
+ "pushws #{self.class}::Foo.new\n",
+ "workspaces",
+ )
+
+ assert_empty err
+ assert_match(/\[#<TestIRB::Workspac...>, #<TestIRB::Workspac...>\]\n/, out)
+ end
+ end
+
+ class PopwsTest < WorkspaceCommandTestCase
+ def test_popws_replaces_the_current_workspace_with_the_previous_one
+ out, err = execute_lines(
+ "pushws Foo.new\n",
+ "popws\n",
+ "cwws\n",
+ "self.class",
+ )
+ assert_empty err
+ assert_include(out, "=> #{self.class}")
+ end
+
+ def test_popws_prints_help_message_if_the_workspace_is_empty
+ out, err = execute_lines(
+ "popws\n",
+ )
+ assert_empty err
+ assert_match(/\[#<TestIRB::PopwsTes...>\]\n/, out)
+ end
+ end
+
+ class ChwsTest < WorkspaceCommandTestCase
+ def test_chws_replaces_the_current_workspace
+ out, err = execute_lines(
+ "chws #{self.class}::Foo.new\n",
+ "cwws\n",
+ "self.class\n"
+ )
+ assert_empty err
+ assert_include(out, "Current workspace: #<#{self.class.name}::Foo")
+ assert_include(out, "=> #{self.class}::Foo")
+ end
+
+ def test_chws_does_nothing_when_receiving_no_argument
+ out, err = execute_lines(
+ "chws\n",
+ )
+ assert_empty err
+ assert_include(out, "Current workspace: #{self}")
+ end
+ end
+
+ class WhereamiTest < CommandTestCase
+ def test_whereami
+ out, err = execute_lines(
+ "whereami\n",
+ )
+ assert_empty err
+ assert_match(/^From: .+ @ line \d+ :\n/, out)
+ end
+
+ def test_whereami_alias
+ out, err = execute_lines(
+ "@\n",
+ )
+ assert_empty err
+ assert_match(/^From: .+ @ line \d+ :\n/, out)
+ end
+ end
+
+ class LsTest < CommandTestCase
+ def test_ls
+ out, err = execute_lines(
+ "class P\n",
+ " def m() end\n",
+ " def m2() end\n",
+ "end\n",
+
+ "class C < P\n",
+ " def m1() end\n",
+ " def m2() end\n",
+ "end\n",
+
+ "module M\n",
+ " def m1() end\n",
+ " def m3() end\n",
+ "end\n",
+
+ "module M2\n",
+ " include M\n",
+ " def m4() end\n",
+ "end\n",
+
+ "obj = C.new\n",
+ "obj.instance_variable_set(:@a, 1)\n",
+ "obj.extend M2\n",
+ "def obj.m5() end\n",
+ "ls obj\n",
+ )
+
+ assert_empty err
+ assert_match(/^instance variables:\s+@a\n/m, out)
+ assert_match(/P#methods:\s+m\n/m, out)
+ assert_match(/C#methods:\s+m2\n/m, out)
+ assert_match(/M#methods:\s+m1\s+m3\n/m, out)
+ assert_match(/M2#methods:\s+m4\n/m, out)
+ assert_match(/C.methods:\s+m5\n/m, out)
+ end
+
+ def test_ls_class
+ out, err = execute_lines(
+ "module M1\n",
+ " def m2; end\n",
+ " def m3; end\n",
+ "end\n",
+
+ "class C1\n",
+ " def m1; end\n",
+ " def m2; end\n",
+ "end\n",
+
+ "class C2 < C1\n",
+ " include M1\n",
+ " def m3; end\n",
+ " def m4; end\n",
+ " def self.m3; end\n",
+ " def self.m5; end\n",
+ "end\n",
+ "ls C2"
+ )
+
+ assert_empty err
+ assert_match(/C2.methods:\s+m3\s+m5\n/, out)
+ assert_match(/C2#methods:\s+m3\s+m4\n.*M1#methods:\s+m2\n.*C1#methods:\s+m1\n/, out)
+ assert_not_match(/Module#methods/, out)
+ assert_not_match(/Class#methods/, out)
+ end
+
+ def test_ls_module
+ out, err = execute_lines(
+ "module M1\n",
+ " def m1; end\n",
+ " def m2; end\n",
+ "end\n",
+
+ "module M2\n",
+ " include M1\n",
+ " def m1; end\n",
+ " def m3; end\n",
+ " def self.m4; end\n",
+ "end\n",
+ "ls M2"
+ )
+
+ assert_empty err
+ assert_match(/M2\.methods:\s+m4\n/, out)
+ assert_match(/M2#methods:\s+m1\s+m3\n.*M1#methods:\s+m2\n/, out)
+ assert_not_match(/Module#methods/, out)
+ end
+
+ def test_ls_instance
+ out, err = execute_lines(
+ "class Foo; def bar; end; end\n",
+ "ls Foo.new"
+ )
+
+ assert_empty err
+ assert_match(/Foo#methods:\s+bar/, out)
+ # don't duplicate
+ assert_not_match(/Foo#methods:\s+bar\n.*Foo#methods/, out)
+ end
+
+ def test_ls_grep
+ out, err = execute_lines("ls 42\n")
+ assert_empty err
+ assert_match(/times/, out)
+ assert_match(/polar/, out)
+
+ [
+ "ls 42, grep: /times/\n",
+ "ls 42 -g times\n",
+ "ls 42 -G times\n",
+ ].each do |line|
+ out, err = execute_lines(line)
+ assert_empty err
+ assert_match(/times/, out)
+ assert_not_match(/polar/, out)
+ end
+ end
+
+ def test_ls_grep_empty
+ out, err = execute_lines("ls\n")
+ assert_empty err
+ assert_match(/assert/, out)
+ assert_match(/refute/, out)
+
+ [
+ "ls grep: /assert/\n",
+ "ls -g assert\n",
+ "ls -G assert\n",
+ ].each do |line|
+ out, err = execute_lines(line)
+ assert_empty err
+ assert_match(/assert/, out)
+ assert_not_match(/refute/, out)
+ end
+ end
+
+ def test_ls_with_no_singleton_class
+ out, err = execute_lines(
+ "ls 42",
+ )
+ assert_empty err
+ assert_match(/Comparable#methods:\s+/, out)
+ assert_match(/Numeric#methods:\s+/, out)
+ assert_match(/Integer#methods:\s+/, out)
+ end
+ end
+
+ class ShowDocTest < CommandTestCase
+ def test_show_doc
+ out, err = execute_lines(
+ "show_doc String#gsub\n",
+ "\n",
+ )
+
+ # the former is what we'd get without document content installed, like on CI
+ # the latter is what we may get locally
+ possible_rdoc_output = [/Nothing known about String#gsub/, /gsub\(pattern\)/]
+ assert_not_include err, "[Deprecation]"
+ assert(possible_rdoc_output.any? { |output| output.match?(out) }, "Expect the `show_doc` command to match one of the possible outputs. Got:\n#{out}")
+ ensure
+ # this is the only way to reset the redefined method without coupling the test with its implementation
+ EnvUtil.suppress_warning { load "irb/command/help.rb" }
+ end
+
+ def test_show_doc_without_rdoc
+ out, err = without_rdoc do
+ execute_lines(
+ "show_doc String#gsub\n",
+ "\n",
+ )
+ end
+
+ # if it fails to require rdoc, it only returns the command object
+ assert_match(/=> nil\n/, out)
+ assert_include(err, "Can't display document because `rdoc` is not installed.\n")
+ ensure
+ # this is the only way to reset the redefined method without coupling the test with its implementation
+ EnvUtil.suppress_warning { load "irb/command/help.rb" }
+ end
+ end
+
+ class EditTest < CommandTestCase
+ def setup
+ @original_visual = ENV["VISUAL"]
+ @original_editor = ENV["EDITOR"]
+ # noop the command so nothing gets executed
+ ENV["VISUAL"] = ": code"
+ ENV["EDITOR"] = ": code2"
+ end
+
+ def teardown
+ ENV["VISUAL"] = @original_visual
+ ENV["EDITOR"] = @original_editor
+ end
+
+ def test_edit_without_arg
+ out, err = execute_lines(
+ "edit",
+ irb_path: __FILE__
+ )
+
+ assert_empty err
+ assert_match("path: #{__FILE__}", out)
+ assert_match("command: ': code'", out)
+ end
+
+ def test_edit_without_arg_and_non_existing_irb_path
+ out, err = execute_lines(
+ "edit",
+ irb_path: '/path/to/file.rb(irb)'
+ )
+
+ assert_empty err
+ assert_match(/Can not find file: \/path\/to\/file\.rb\(irb\)/, out)
+ end
+
+ def test_edit_with_path
+ out, err = execute_lines(
+ "edit #{__FILE__}"
+ )
+
+ assert_empty err
+ assert_match("path: #{__FILE__}", out)
+ assert_match("command: ': code'", out)
+ end
+
+ def test_edit_with_non_existing_path
+ out, err = execute_lines(
+ "edit test_cmd_non_existing_path.rb"
+ )
+
+ assert_empty err
+ assert_match(/Can not find file: test_cmd_non_existing_path\.rb/, out)
+ end
+
+ def test_edit_with_constant
+ out, err = execute_lines(
+ "edit IRB::Irb"
+ )
+
+ assert_empty err
+ assert_match(/path: .*\/lib\/irb\.rb/, out)
+ assert_match("command: ': code'", out)
+ end
+
+ def test_edit_with_class_method
+ out, err = execute_lines(
+ "edit IRB.start"
+ )
+
+ assert_empty err
+ assert_match(/path: .*\/lib\/irb\.rb/, out)
+ assert_match("command: ': code'", out)
+ end
+
+ def test_edit_with_instance_method
+ out, err = execute_lines(
+ "edit IRB::Irb#run"
+ )
+
+ assert_empty err
+ assert_match(/path: .*\/lib\/irb\.rb/, out)
+ assert_match("command: ': code'", out)
+ end
+
+ def test_edit_with_editor_env_var
+ ENV.delete("VISUAL")
+
+ out, err = execute_lines(
+ "edit",
+ irb_path: __FILE__
+ )
+
+ assert_empty err
+ assert_match("path: #{__FILE__}", out)
+ assert_match("command: ': code2'", out)
+ end
+ end
+
+ class HistoryCmdTest < CommandTestCase
+ def teardown
+ TestInputMethod.send(:remove_const, "HISTORY") if defined?(TestInputMethod::HISTORY)
+ super
+ end
+
+ def test_history
+ TestInputMethod.const_set("HISTORY", %w[foo bar baz])
+
+ out, err = without_rdoc do
+ execute_lines("history")
+ end
+
+ assert_include(out, <<~EOF)
+ 2: baz
+ 1: bar
+ 0: foo
+ EOF
+ assert_empty err
+ end
+
+ def test_multiline_history_with_truncation
+ TestInputMethod.const_set("HISTORY", ["foo", "bar", <<~INPUT])
+ [].each do |x|
+ puts x
+ end
+ INPUT
+
+ out, err = without_rdoc do
+ execute_lines("hist")
+ end
+
+ assert_include(out, <<~EOF)
+ 2: [].each do |x|
+ puts x
+ ...
+ 1: bar
+ 0: foo
+ EOF
+ assert_empty err
+ end
+
+ def test_history_grep
+ TestInputMethod.const_set("HISTORY", ["foo", "bar", <<~INPUT])
+ [].each do |x|
+ puts x
+ end
+ INPUT
+
+ out, err = without_rdoc do
+ execute_lines("hist -g each\n")
+ end
+
+ assert_include(out, <<~EOF)
+ 2: [].each do |x|
+ puts x
+ ...
+ EOF
+ assert_empty err
+ end
+
+ end
+
+ class HelperMethodInsallTest < CommandTestCase
+ def test_helper_method_install
+ IRB::ExtendCommandBundle.module_eval do
+ def foobar
+ "test_helper_method_foobar"
+ end
+ end
+
+ out, err = execute_lines("foobar.upcase")
+ assert_empty err
+ assert_include(out, '=> "TEST_HELPER_METHOD_FOOBAR"')
+ ensure
+ IRB::ExtendCommandBundle.remove_method :foobar
+ end
+ end
+end
diff --git a/test/irb/test_completion.rb b/test/irb/test_completion.rb
index 6a24f0f9ba..5fe7952b3d 100644
--- a/test/irb/test_completion.rb
+++ b/test/irb/test_completion.rb
@@ -5,77 +5,90 @@ require "irb"
require_relative "helper"
module TestIRB
- class TestCompletion < TestCase
- def setup
- # make sure require completion candidates are not cached
- IRB::InputCompletor.class_variable_set(:@@files_from_load_path, nil)
+ class CompletionTest < TestCase
+ def completion_candidates(target, bind)
+ IRB::RegexpCompletor.new.completion_candidates('', target, '', bind: bind)
+ end
+
+ def doc_namespace(target, bind)
+ IRB::RegexpCompletor.new.doc_namespace('', target, '', bind: bind)
+ end
+
+ class CommandCompletionTest < CompletionTest
+ def test_command_completion
+ assert_include(IRB::RegexpCompletor.new.completion_candidates('', 'show_s', '', bind: binding), 'show_source')
+ assert_not_include(IRB::RegexpCompletor.new.completion_candidates(';', 'show_s', '', bind: binding), 'show_source')
+ end
end
- class TestMethodCompletion < TestCompletion
+ class MethodCompletionTest < CompletionTest
def test_complete_string
- assert_include(IRB::InputCompletor.retrieve_completion_data("'foo'.up", bind: binding), "'foo'.upcase")
+ assert_include(completion_candidates("'foo'.up", binding), "'foo'.upcase")
# completing 'foo bar'.up
- assert_include(IRB::InputCompletor.retrieve_completion_data("bar'.up", bind: binding), "bar'.upcase")
- assert_equal("String.upcase", IRB::InputCompletor.retrieve_completion_data("'foo'.upcase", bind: binding, doc_namespace: true))
+ assert_include(completion_candidates("bar'.up", binding), "bar'.upcase")
+ assert_equal("String.upcase", doc_namespace("'foo'.upcase", binding))
end
def test_complete_regexp
- assert_include(IRB::InputCompletor.retrieve_completion_data("/foo/.ma", bind: binding), "/foo/.match")
+ assert_include(completion_candidates("/foo/.ma", binding), "/foo/.match")
# completing /foo bar/.ma
- assert_include(IRB::InputCompletor.retrieve_completion_data("bar/.ma", bind: binding), "bar/.match")
- assert_equal("Regexp.match", IRB::InputCompletor.retrieve_completion_data("/foo/.match", bind: binding, doc_namespace: true))
+ assert_include(completion_candidates("bar/.ma", binding), "bar/.match")
+ assert_equal("Regexp.match", doc_namespace("/foo/.match", binding))
end
def test_complete_array
- assert_include(IRB::InputCompletor.retrieve_completion_data("[].an", bind: binding), "[].any?")
- assert_equal("Array.any?", IRB::InputCompletor.retrieve_completion_data("[].any?", bind: binding, doc_namespace: true))
+ assert_include(completion_candidates("[].an", binding), "[].any?")
+ assert_equal("Array.any?", doc_namespace("[].any?", binding))
end
def test_complete_hash_and_proc
# hash
- assert_include(IRB::InputCompletor.retrieve_completion_data("{}.an", bind: binding), "{}.any?")
- assert_equal(["Proc.any?", "Hash.any?"], IRB::InputCompletor.retrieve_completion_data("{}.any?", bind: binding, doc_namespace: true))
+ assert_include(completion_candidates("{}.an", binding), "{}.any?")
+ assert_equal(["Hash.any?", "Proc.any?"], doc_namespace("{}.any?", binding))
# proc
- assert_include(IRB::InputCompletor.retrieve_completion_data("{}.bin", bind: binding), "{}.binding")
- assert_equal(["Proc.binding", "Hash.binding"], IRB::InputCompletor.retrieve_completion_data("{}.binding", bind: binding, doc_namespace: true))
+ assert_include(completion_candidates("{}.bin", binding), "{}.binding")
+ assert_equal(["Hash.binding", "Proc.binding"], doc_namespace("{}.binding", binding))
end
def test_complete_numeric
- assert_include(IRB::InputCompletor.retrieve_completion_data("1.positi", bind: binding), "1.positive?")
- assert_equal("Integer.positive?", IRB::InputCompletor.retrieve_completion_data("1.positive?", bind: binding, doc_namespace: true))
+ assert_include(completion_candidates("1.positi", binding), "1.positive?")
+ assert_equal("Integer.positive?", doc_namespace("1.positive?", binding))
- assert_include(IRB::InputCompletor.retrieve_completion_data("1r.positi", bind: binding), "1r.positive?")
- assert_equal("Rational.positive?", IRB::InputCompletor.retrieve_completion_data("1r.positive?", bind: binding, doc_namespace: true))
+ assert_include(completion_candidates("1r.positi", binding), "1r.positive?")
+ assert_equal("Rational.positive?", doc_namespace("1r.positive?", binding))
- assert_include(IRB::InputCompletor.retrieve_completion_data("0xFFFF.positi", bind: binding), "0xFFFF.positive?")
- assert_equal("Integer.positive?", IRB::InputCompletor.retrieve_completion_data("0xFFFF.positive?", bind: binding, doc_namespace: true))
+ assert_include(completion_candidates("0xFFFF.positi", binding), "0xFFFF.positive?")
+ assert_equal("Integer.positive?", doc_namespace("0xFFFF.positive?", binding))
- assert_empty(IRB::InputCompletor.retrieve_completion_data("1i.positi", bind: binding))
+ assert_empty(completion_candidates("1i.positi", binding))
end
def test_complete_symbol
- assert_include(IRB::InputCompletor.retrieve_completion_data(":foo.to_p", bind: binding), ":foo.to_proc")
- assert_equal("Symbol.to_proc", IRB::InputCompletor.retrieve_completion_data(":foo.to_proc", bind: binding, doc_namespace: true))
+ assert_include(completion_candidates(":foo.to_p", binding), ":foo.to_proc")
+ assert_equal("Symbol.to_proc", doc_namespace(":foo.to_proc", binding))
end
def test_complete_class
- assert_include(IRB::InputCompletor.retrieve_completion_data("String.ne", bind: binding), "String.new")
- assert_equal("String.new", IRB::InputCompletor.retrieve_completion_data("String.new", bind: binding, doc_namespace: true))
+ assert_include(completion_candidates("String.ne", binding), "String.new")
+ assert_equal("String.new", doc_namespace("String.new", binding))
end
end
- class TestRequireComepletion < TestCompletion
+ class RequireComepletionTest < CompletionTest
def test_complete_require
- candidates = IRB::InputCompletor::CompletionProc.("'irb", "require ", "")
+ candidates = IRB::RegexpCompletor.new.completion_candidates("require ", "'irb", "", bind: binding)
%w['irb/init 'irb/ruby-lex].each do |word|
assert_include candidates, word
end
# Test cache
- candidates = IRB::InputCompletor::CompletionProc.("'irb", "require ", "")
+ candidates = IRB::RegexpCompletor.new.completion_candidates("require ", "'irb", "", bind: binding)
%w['irb/init 'irb/ruby-lex].each do |word|
assert_include candidates, word
end
+ # Test string completion not disturbed by require completion
+ candidates = IRB::RegexpCompletor.new.completion_candidates("'string ", "'.", "", bind: binding)
+ assert_include candidates, "'.upcase"
end
def test_complete_require_with_pathname_in_load_path
@@ -84,7 +97,7 @@ module TestIRB
test_path = Pathname.new(temp_dir)
$LOAD_PATH << test_path
- candidates = IRB::InputCompletor::CompletionProc.("'foo", "require ", "")
+ candidates = IRB::RegexpCompletor.new.completion_candidates("require ", "'foo", "", bind: binding)
assert_include candidates, "'foo"
ensure
$LOAD_PATH.pop if test_path
@@ -98,7 +111,7 @@ module TestIRB
object.define_singleton_method(:to_s) { temp_dir }
$LOAD_PATH << object
- candidates = IRB::InputCompletor::CompletionProc.("'foo", "require ", "")
+ candidates = IRB::RegexpCompletor.new.completion_candidates("require ", "'foo", "", bind: binding)
assert_include candidates, "'foo"
ensure
$LOAD_PATH.pop if object
@@ -111,27 +124,28 @@ module TestIRB
$LOAD_PATH << object
assert_nothing_raised do
- IRB::InputCompletor::CompletionProc.("'foo", "require ", "")
+ IRB::RegexpCompletor.new.completion_candidates("require ", "'foo", "", bind: binding)
end
ensure
$LOAD_PATH.pop if object
end
def test_complete_require_library_name_first
- candidates = IRB::InputCompletor::CompletionProc.("'csv", "require ", "")
- assert_equal "'csv", candidates.first
+ # Test that library name is completed first with subdirectories
+ candidates = IRB::RegexpCompletor.new.completion_candidates("require ", "'irb", "", bind: binding)
+ assert_equal "'irb", candidates.first
end
def test_complete_require_relative
candidates = Dir.chdir(__dir__ + "/../..") do
- IRB::InputCompletor::CompletionProc.("'lib/irb", "require_relative ", "")
+ IRB::RegexpCompletor.new.completion_candidates("require_relative ", "'lib/irb", "", bind: binding)
end
%w['lib/irb/init 'lib/irb/ruby-lex].each do |word|
assert_include candidates, word
end
# Test cache
candidates = Dir.chdir(__dir__ + "/../..") do
- IRB::InputCompletor::CompletionProc.("'lib/irb", "require_relative ", "")
+ IRB::RegexpCompletor.new.completion_candidates("require_relative ", "'lib/irb", "", bind: binding)
end
%w['lib/irb/init 'lib/irb/ruby-lex].each do |word|
assert_include candidates, word
@@ -139,7 +153,7 @@ module TestIRB
end
end
- class TestVariableCompletion < TestCompletion
+ class VariableCompletionTest < CompletionTest
def test_complete_variable
# Bug fix issues https://github.com/ruby/irb/issues/368
# Variables other than `str_example` and `@str_example` are defined to ensure that irb completion does not cause unintended behavior
@@ -160,13 +174,13 @@ module TestIRB
local_variables.clear
instance_variables.clear
- assert_include(IRB::InputCompletor.retrieve_completion_data("str_examp", bind: binding), "str_example")
- assert_equal("String", IRB::InputCompletor.retrieve_completion_data("str_example", bind: binding, doc_namespace: true))
- assert_equal("String.to_s", IRB::InputCompletor.retrieve_completion_data("str_example.to_s", bind: binding, doc_namespace: true))
+ assert_include(completion_candidates("str_examp", binding), "str_example")
+ assert_equal("String", doc_namespace("str_example", binding))
+ assert_equal("String.to_s", doc_namespace("str_example.to_s", binding))
- assert_include(IRB::InputCompletor.retrieve_completion_data("@str_examp", bind: binding), "@str_example")
- assert_equal("String", IRB::InputCompletor.retrieve_completion_data("@str_example", bind: binding, doc_namespace: true))
- assert_equal("String.to_s", IRB::InputCompletor.retrieve_completion_data("@str_example.to_s", bind: binding, doc_namespace: true))
+ assert_include(completion_candidates("@str_examp", binding), "@str_example")
+ assert_equal("String", doc_namespace("@str_example", binding))
+ assert_equal("String.to_s", doc_namespace("@str_example.to_s", binding))
end
def test_complete_sort_variables
@@ -176,12 +190,12 @@ module TestIRB
xzy_1.clear
xzy2.clear
- candidates = IRB::InputCompletor.retrieve_completion_data("xz", bind: binding, doc_namespace: false)
+ candidates = completion_candidates("xz", binding)
assert_equal(%w[xzy xzy2 xzy_1], candidates)
end
end
- class TestConstantCompletion < TestCompletion
+ class ConstantCompletionTest < CompletionTest
class Foo
B3 = 1
B1 = 1
@@ -189,137 +203,56 @@ module TestIRB
end
def test_complete_constants
- assert_equal(["Foo"], IRB::InputCompletor.retrieve_completion_data("Fo", bind: binding))
- assert_equal(["Foo::B1", "Foo::B2", "Foo::B3"], IRB::InputCompletor.retrieve_completion_data("Foo::B", bind: binding))
- assert_equal(["Foo::B1.positive?"], IRB::InputCompletor.retrieve_completion_data("Foo::B1.pos", bind: binding))
+ assert_equal(["Foo"], completion_candidates("Fo", binding))
+ assert_equal(["Foo::B1", "Foo::B2", "Foo::B3"], completion_candidates("Foo::B", binding))
+ assert_equal(["Foo::B1.positive?"], completion_candidates("Foo::B1.pos", binding))
- assert_equal(["::Forwardable"], IRB::InputCompletor.retrieve_completion_data("::Fo", bind: binding))
- assert_equal("Forwardable", IRB::InputCompletor.retrieve_completion_data("::Forwardable", bind: binding, doc_namespace: true))
+ assert_equal(["::Forwardable"], completion_candidates("::Fo", binding))
+ assert_equal("Forwardable", doc_namespace("::Forwardable", binding))
end
end
- class TestPerfectMatching < TestCompletion
- def setup
- # trigger PerfectMatchedProc to set up RDocRIDriver constant
- IRB::InputCompletor::PerfectMatchedProc.("foo", bind: binding)
-
- @original_use_stdout = IRB::InputCompletor::RDocRIDriver.use_stdout
- # force the driver to use stdout so it doesn't start a pager and interrupt tests
- IRB::InputCompletor::RDocRIDriver.use_stdout = true
- end
-
- def teardown
- IRB::InputCompletor::RDocRIDriver.use_stdout = @original_use_stdout
- end
-
- def test_perfectly_matched_namespace_triggers_document_display
- omit unless has_rdoc_content?
-
- out, err = capture_output do
- IRB::InputCompletor::PerfectMatchedProc.("String", bind: binding)
- end
-
- assert_empty(err)
-
- assert_include(out, " S\bSt\btr\bri\bin\bng\bg")
- end
-
- def test_perfectly_matched_multiple_namespaces_triggers_document_display
- result = nil
- out, err = capture_output do
- result = IRB::InputCompletor::PerfectMatchedProc.("{}.nil?", bind: binding)
- end
-
- assert_empty(err)
-
- # check if there're rdoc contents (e.g. CI doesn't generate them)
- if has_rdoc_content?
- # if there's rdoc content, we can verify by checking stdout
- # rdoc generates control characters for formatting method names
- assert_include(out, "P\bPr\bro\boc\bc.\b.n\bni\bil\bl?\b?") # Proc.nil?
- assert_include(out, "H\bHa\bas\bsh\bh.\b.n\bni\bil\bl?\b?") # Hash.nil?
- else
- # this is a hacky way to verify the rdoc rendering code path because CI doesn't have rdoc content
- # if there are multiple namespaces to be rendered, PerfectMatchedProc renders the result with a document
- # which always returns the bytes rendered, even if it's 0
- assert_equal(0, result)
- end
- end
-
- def test_not_matched_namespace_triggers_nothing
- result = nil
- out, err = capture_output do
- result = IRB::InputCompletor::PerfectMatchedProc.("Stri", bind: binding)
- end
-
- assert_empty(err)
- assert_empty(out)
- assert_nil(result)
- end
-
- def test_perfect_matching_stops_without_rdoc
- result = nil
-
- out, err = capture_output do
- without_rdoc do
- result = IRB::InputCompletor::PerfectMatchedProc.("String", bind: binding)
- end
- end
-
- assert_empty(err)
- assert_not_match(/from ruby core/, out)
- assert_nil(result)
- end
-
- def test_perfect_matching_handles_nil_namespace
- out, err = capture_output do
- # symbol literal has `nil` doc namespace so it's a good test subject
- assert_nil(IRB::InputCompletor::PerfectMatchedProc.(":aiueo", bind: binding))
- end
-
- assert_empty(err)
- assert_empty(out)
- end
-
- private
-
- def has_rdoc_content?
- File.exist?(RDoc::RI::Paths::BASE)
- end
+ def test_not_completing_empty_string
+ assert_equal([], completion_candidates("", binding))
+ assert_equal([], completion_candidates(" ", binding))
+ assert_equal([], completion_candidates("\t", binding))
+ assert_equal(nil, doc_namespace("", binding))
end
def test_complete_symbol
- %w"UTF-16LE UTF-7".each do |enc|
+ symbols = %w"UTF-16LE UTF-7".map do |enc|
"K".force_encoding(enc).to_sym
rescue
end
- _ = :aiueo
- assert_include(IRB::InputCompletor.retrieve_completion_data(":a", bind: binding), ":aiueo")
- assert_empty(IRB::InputCompletor.retrieve_completion_data(":irb_unknown_symbol_abcdefg", bind: binding))
+ symbols += [:aiueo, :"aiu eo"]
+ candidates = completion_candidates(":a", binding)
+ assert_include(candidates, ":aiueo")
+ assert_not_include(candidates, ":aiu eo")
+ assert_empty(completion_candidates(":irb_unknown_symbol_abcdefg", binding))
# Do not complete empty symbol for performance reason
- assert_empty(IRB::InputCompletor.retrieve_completion_data(":", bind: binding))
+ assert_empty(completion_candidates(":", binding))
end
def test_complete_invalid_three_colons
- assert_empty(IRB::InputCompletor.retrieve_completion_data(":::A", bind: binding))
- assert_empty(IRB::InputCompletor.retrieve_completion_data(":::", bind: binding))
+ assert_empty(completion_candidates(":::A", binding))
+ assert_empty(completion_candidates(":::", binding))
end
def test_complete_absolute_constants_with_special_characters
- assert_empty(IRB::InputCompletor.retrieve_completion_data("::A:", bind: binding))
- assert_empty(IRB::InputCompletor.retrieve_completion_data("::A.", bind: binding))
- assert_empty(IRB::InputCompletor.retrieve_completion_data("::A(", bind: binding))
- assert_empty(IRB::InputCompletor.retrieve_completion_data("::A)", bind: binding))
- assert_empty(IRB::InputCompletor.retrieve_completion_data("::A[", bind: binding))
+ assert_empty(completion_candidates("::A:", binding))
+ assert_empty(completion_candidates("::A.", binding))
+ assert_empty(completion_candidates("::A(", binding))
+ assert_empty(completion_candidates("::A)", binding))
+ assert_empty(completion_candidates("::A[", binding))
end
def test_complete_reserved_words
- candidates = IRB::InputCompletor.retrieve_completion_data("de", bind: binding)
+ candidates = completion_candidates("de", binding)
%w[def defined?].each do |word|
assert_include candidates, word
end
- candidates = IRB::InputCompletor.retrieve_completion_data("__", bind: binding)
+ candidates = completion_candidates("__", binding)
%w[__ENCODING__ __LINE__ __FILE__].each do |word|
assert_include candidates, word
end
@@ -340,11 +273,39 @@ module TestIRB
}
bind = obj.instance_exec { binding }
- assert_include(IRB::InputCompletor.retrieve_completion_data("public_hog", bind: bind), "public_hoge")
- assert_include(IRB::InputCompletor.retrieve_completion_data("public_hoge", bind: bind, doc_namespace: true), "public_hoge")
+ assert_include(completion_candidates("public_hog", bind), "public_hoge")
+ assert_include(doc_namespace("public_hoge", bind), "public_hoge")
+
+ assert_include(completion_candidates("private_hog", bind), "private_hoge")
+ assert_include(doc_namespace("private_hoge", bind), "private_hoge")
+ end
+ end
+
+ class DeprecatedInputCompletorTest < TestCase
+ def setup
+ save_encodings
+ @verbose, $VERBOSE = $VERBOSE, nil
+ IRB.init_config(nil)
+ IRB.conf[:VERBOSE] = false
+ IRB.conf[:MAIN_CONTEXT] = IRB::Context.new(IRB::WorkSpace.new(binding))
+ end
+
+ def teardown
+ restore_encodings
+ $VERBOSE = @verbose
+ end
+
+ def test_completion_proc
+ assert_include(IRB::InputCompletor::CompletionProc.call('1.ab'), '1.abs')
+ assert_include(IRB::InputCompletor::CompletionProc.call('1.ab', '', ''), '1.abs')
+ end
- assert_include(IRB::InputCompletor.retrieve_completion_data("private_hog", bind: bind), "private_hoge")
- assert_include(IRB::InputCompletor.retrieve_completion_data("private_hoge", bind: bind, doc_namespace: true), "private_hoge")
+ def test_retrieve_completion_data
+ assert_include(IRB::InputCompletor.retrieve_completion_data('1.ab'), '1.abs')
+ assert_equal(IRB::InputCompletor.retrieve_completion_data('1.abs', doc_namespace: true), 'Integer.abs')
+ bind = eval('a = 1; binding')
+ assert_include(IRB::InputCompletor.retrieve_completion_data('a.ab', bind: bind), 'a.abs')
+ assert_equal(IRB::InputCompletor.retrieve_completion_data('a.abs', bind: bind, doc_namespace: true), 'Integer.abs')
end
end
end
diff --git a/test/irb/test_context.rb b/test/irb/test_context.rb
index c4ca56aba6..cd3f2c8f62 100644
--- a/test/irb/test_context.rb
+++ b/test/irb/test_context.rb
@@ -1,16 +1,16 @@
# frozen_string_literal: false
require 'tempfile'
require 'irb'
-require 'rubygems' if defined?(Gem)
require_relative "helper"
module TestIRB
- class TestContext < TestCase
+ class ContextTest < TestCase
def setup
IRB.init_config(nil)
IRB.conf[:USE_SINGLELINE] = false
IRB.conf[:VERBOSE] = false
+ IRB.conf[:USE_PAGER] = false
workspace = IRB::WorkSpace.new(Object.new)
@context = IRB::Context.new(nil, workspace, TestInputMethod.new)
@@ -28,41 +28,6 @@ module TestIRB
restore_encodings
end
- def test_last_value
- assert_nil(@context.last_value)
- assert_nil(@context.evaluate('_', 1))
- obj = Object.new
- @context.set_last_value(obj)
- assert_same(obj, @context.last_value)
- assert_same(obj, @context.evaluate('_', 1))
- end
-
- def test_evaluate_with_exception
- assert_nil(@context.evaluate("$!", 1))
- e = assert_raise_with_message(RuntimeError, 'foo') {
- @context.evaluate("raise 'foo'", 1)
- }
- assert_equal('foo', e.message)
- assert_same(e, @context.evaluate('$!', 1, exception: e))
- e = assert_raise(SyntaxError) {
- @context.evaluate("1,2,3", 1, exception: e)
- }
- assert_match(/\A\(irb\):1:/, e.message)
- assert_not_match(/rescue _\.class/, e.message)
- end
-
- def test_evaluate_with_encoding_error_without_lineno
- assert_raise_with_message(EncodingError, /invalid symbol/) {
- @context.evaluate(%q[:"\xAE"], 1)
- # The backtrace of this invalid encoding hash doesn't contain lineno.
- }
- end
-
- def test_evaluate_still_emits_warning
- assert_warning("(irb):1: warning: END in method; use at_exit\n") do
- @context.evaluate(%q[def foo; END {}; end], 1)
- end
- end
def test_eval_input
verbose, $VERBOSE = $VERBOSE, nil
@@ -77,11 +42,27 @@ module TestIRB
irb.eval_input
end
assert_empty err
- assert_pattern_list([:*, /\(irb\):1:in `<main>': Foo \(RuntimeError\)\n/,
- :*, /#<RuntimeError: Foo>\n/,
- :*, /0$/,
- :*, /0$/,
- /\s*/], out)
+
+ expected_output =
+ if RUBY_3_4
+ [
+ :*, /\(irb\):1:in '<main>': Foo \(RuntimeError\)\n/,
+ :*, /#<RuntimeError: Foo>\n/,
+ :*, /0$/,
+ :*, /0$/,
+ /\s*/
+ ]
+ else
+ [
+ :*, /\(irb\):1:in `<main>': Foo \(RuntimeError\)\n/,
+ :*, /#<RuntimeError: Foo>\n/,
+ :*, /0$/,
+ :*, /0$/,
+ /\s*/
+ ]
+ end
+
+ assert_pattern_list(expected_output, out)
ensure
$VERBOSE = verbose
end
@@ -97,11 +78,33 @@ module TestIRB
irb.eval_input
end
assert_empty err
- assert_pattern_list([
- :*, /\(irb\):1:in `<main>': Foo \(RuntimeError\)\n/,
- :*, /\(irb\):2:in `<main>': Bar \(RuntimeError\)\n/,
- :*, /#<RuntimeError: Bar>\n/,
- ], out)
+ expected_output =
+ if RUBY_3_4
+ [
+ :*, /\(irb\):1:in '<main>': Foo \(RuntimeError\)\n/,
+ :*, /\(irb\):2:in '<main>': Bar \(RuntimeError\)\n/,
+ :*, /#<RuntimeError: Bar>\n/,
+ ]
+ else
+ [
+ :*, /\(irb\):1:in `<main>': Foo \(RuntimeError\)\n/,
+ :*, /\(irb\):2:in `<main>': Bar \(RuntimeError\)\n/,
+ :*, /#<RuntimeError: Bar>\n/,
+ ]
+ end
+ assert_pattern_list(expected_output, out)
+ end
+
+ def test_prompt_n_deprecation
+ irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), TestInputMethod.new)
+
+ _, err = capture_output do
+ irb.context.prompt_n = "foo"
+ irb.context.prompt_n
+ end
+
+ assert_include err, "IRB::Context#prompt_n is deprecated"
+ assert_include err, "IRB::Context#prompt_n= is deprecated"
end
def test_output_to_pipe
@@ -127,9 +130,9 @@ module TestIRB
[:marshal, "123", Marshal.dump(123)],
],
failed: [
- [false, "BasicObject.new", /#<NoMethodError: undefined method `to_s' for/],
- [:p, "class Foo; undef inspect ;end; Foo.new", /#<NoMethodError: undefined method `inspect' for/],
- [:yaml, "BasicObject.new", /#<NoMethodError: undefined method `inspect' for/],
+ [false, "BasicObject.new", /#<NoMethodError: undefined method (`|')to_s' for/],
+ [:p, "class Foo; undef inspect ;end; Foo.new", /#<NoMethodError: undefined method (`|')inspect' for/],
+ [:yaml, "BasicObject.new", /#<NoMethodError: undefined method (`|')inspect' for/],
[:marshal, "[Object.new, Class.new]", /#<TypeError: can't dump anonymous class #<Class:/]
]
}.each do |scenario, cases|
@@ -207,7 +210,7 @@ module TestIRB
end
assert_empty err
assert_match(/An error occurred when inspecting the object: #<RuntimeError: foo>/, out)
- assert_match(/An error occurred when running Kernel#inspect: #<NoMethodError: undefined method `inspect' for/, out)
+ assert_match(/An error occurred when running Kernel#inspect: #<NoMethodError: undefined method (`|')inspect' for/, out)
ensure
$VERBOSE = verbose
end
@@ -216,59 +219,6 @@ module TestIRB
assert_equal(true, @context.use_autocomplete?)
end
- def test_assignment_expression
- input = TestInputMethod.new
- irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
- [
- "foo = bar",
- "@foo = bar",
- "$foo = bar",
- "@@foo = bar",
- "::Foo = bar",
- "a::Foo = bar",
- "Foo = bar",
- "foo.bar = 1",
- "foo[1] = bar",
- "foo += bar",
- "foo -= bar",
- "foo ||= bar",
- "foo &&= bar",
- "foo, bar = 1, 2",
- "foo.bar=(1)",
- "foo; foo = bar",
- "foo; foo = bar; ;\n ;",
- "foo\nfoo = bar",
- ].each do |exp|
- assert(
- irb.assignment_expression?(exp),
- "#{exp.inspect}: should be an assignment expression"
- )
- end
-
- [
- "foo",
- "foo.bar",
- "foo[0]",
- "foo = bar; foo",
- "foo = bar\nfoo",
- ].each do |exp|
- refute(
- irb.assignment_expression?(exp),
- "#{exp.inspect}: should not be an assignment expression"
- )
- end
- end
-
- def test_assignment_expression_with_local_variable
- input = TestInputMethod.new
- irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
- code = "a /1;x=1#/"
- refute(irb.assignment_expression?(code), "#{code}: should not be an assignment expression")
- irb.context.workspace.binding.eval('a = 1')
- assert(irb.assignment_expression?(code), "#{code}: should be an assignment expression")
- refute(irb.assignment_expression?(""), "empty code should not be an assignment expression")
- end
-
def test_echo_on_assignment
input = TestInputMethod.new([
"a = 1\n",
@@ -403,7 +353,7 @@ module TestIRB
end
assert_empty err
assert_equal("=> \n#{value}\n", out)
- irb.context.evaluate('A.remove_method(:inspect)', 0)
+ irb.context.evaluate_expression('A.remove_method(:inspect)', 0)
input.reset
irb.context.echo = true
@@ -413,7 +363,7 @@ module TestIRB
end
assert_empty err
assert_equal("=> #{value_first_line[0..(input.winsize.last - 9)]}...\n=> \n#{value}\n", out)
- irb.context.evaluate('A.remove_method(:inspect)', 0)
+ irb.context.evaluate_expression('A.remove_method(:inspect)', 0)
input.reset
irb.context.echo = true
@@ -423,7 +373,7 @@ module TestIRB
end
assert_empty err
assert_equal("=> \n#{value}\n=> \n#{value}\n", out)
- irb.context.evaluate('A.remove_method(:inspect)', 0)
+ irb.context.evaluate_expression('A.remove_method(:inspect)', 0)
input.reset
irb.context.echo = false
@@ -433,7 +383,7 @@ module TestIRB
end
assert_empty err
assert_equal("", out)
- irb.context.evaluate('A.remove_method(:inspect)', 0)
+ irb.context.evaluate_expression('A.remove_method(:inspect)', 0)
input.reset
irb.context.echo = false
@@ -443,7 +393,7 @@ module TestIRB
end
assert_empty err
assert_equal("", out)
- irb.context.evaluate('A.remove_method(:inspect)', 0)
+ irb.context.evaluate_expression('A.remove_method(:inspect)', 0)
input.reset
irb.context.echo = false
@@ -453,7 +403,7 @@ module TestIRB
end
assert_empty err
assert_equal("", out)
- irb.context.evaluate('A.remove_method(:inspect)', 0)
+ irb.context.evaluate_expression('A.remove_method(:inspect)', 0)
end
end
@@ -521,7 +471,6 @@ module TestIRB
def test_default_return_format
IRB.conf[:PROMPT][:MY_PROMPT] = {
:PROMPT_I => "%03n> ",
- :PROMPT_N => "%03n> ",
:PROMPT_S => "%03n> ",
:PROMPT_C => "%03n> "
# without :RETURN
@@ -551,22 +500,30 @@ module TestIRB
irb.eval_input
end
assert_empty err
- if RUBY_VERSION < '3.0.0' && STDOUT.tty?
- expected = [
- :*, /Traceback \(most recent call last\):\n/,
- :*, /\t 2: from \(irb\):1:in `<main>'\n/,
- :*, /\t 1: from \(irb\):1:in `hoge'\n/,
- :*, /\(irb\):1:in `fuga': unhandled exception\n/,
- ]
- else
- expected = [
- :*, /\(irb\):1:in `fuga': unhandled exception\n/,
- :*, /\tfrom \(irb\):1:in `hoge'\n/,
- :*, /\tfrom \(irb\):1:in `<main>'\n/,
- :*
- ]
- end
- assert_pattern_list(expected, out)
+ expected_output =
+ if RUBY_3_4
+ [
+ :*, /\(irb\):1:in 'fuga': unhandled exception\n/,
+ :*, /\tfrom \(irb\):1:in 'hoge'\n/,
+ :*, /\tfrom \(irb\):1:in '<main>'\n/,
+ :*
+ ]
+ elsif RUBY_VERSION < '3.0.0' && STDOUT.tty?
+ [
+ :*, /Traceback \(most recent call last\):\n/,
+ :*, /\t 2: from \(irb\):1:in `<main>'\n/,
+ :*, /\t 1: from \(irb\):1:in `hoge'\n/,
+ :*, /\(irb\):1:in `fuga': unhandled exception\n/,
+ ]
+ else
+ [
+ :*, /\(irb\):1:in `fuga': unhandled exception\n/,
+ :*, /\tfrom \(irb\):1:in `hoge'\n/,
+ :*, /\tfrom \(irb\):1:in `<main>'\n/,
+ :*
+ ]
+ end
+ assert_pattern_list(expected_output, out)
ensure
$VERBOSE = verbose
end
@@ -581,22 +538,31 @@ module TestIRB
irb.eval_input
end
assert_empty err
- if RUBY_VERSION < '3.0.0' && STDOUT.tty?
- expected = [
- :*, /Traceback \(most recent call last\):\n/,
- :*, /\t 2: from \(irb\):1:in `<main>'\n/,
- :*, /\t 1: from \(irb\):1:in `hoge'\n/,
- :*, /\(irb\):1:in `fuga': A\\xF3B \(RuntimeError\)\n/,
- ]
- else
- expected = [
- :*, /\(irb\):1:in `fuga': A\\xF3B \(RuntimeError\)\n/,
- :*, /\tfrom \(irb\):1:in `hoge'\n/,
- :*, /\tfrom \(irb\):1:in `<main>'\n/,
- :*
- ]
- end
- assert_pattern_list(expected, out)
+ expected_output =
+ if RUBY_3_4
+ [
+ :*, /\(irb\):1:in 'fuga': A\\xF3B \(RuntimeError\)\n/,
+ :*, /\tfrom \(irb\):1:in 'hoge'\n/,
+ :*, /\tfrom \(irb\):1:in '<main>'\n/,
+ :*
+ ]
+ elsif RUBY_VERSION < '3.0.0' && STDOUT.tty?
+ [
+ :*, /Traceback \(most recent call last\):\n/,
+ :*, /\t 2: from \(irb\):1:in `<main>'\n/,
+ :*, /\t 1: from \(irb\):1:in `hoge'\n/,
+ :*, /\(irb\):1:in `fuga': A\\xF3B \(RuntimeError\)\n/,
+ ]
+ else
+ [
+ :*, /\(irb\):1:in `fuga': A\\xF3B \(RuntimeError\)\n/,
+ :*, /\tfrom \(irb\):1:in `hoge'\n/,
+ :*, /\tfrom \(irb\):1:in `<main>'\n/,
+ :*
+ ]
+ end
+
+ assert_pattern_list(expected_output, out)
ensure
$VERBOSE = verbose
end
@@ -622,43 +588,43 @@ module TestIRB
expected = [
:*, /Traceback \(most recent call last\):\n/,
:*, /\t... \d+ levels...\n/,
- :*, /\t16: from \(irb\):1:in `a4'\n/,
- :*, /\t15: from \(irb\):1:in `a5'\n/,
- :*, /\t14: from \(irb\):1:in `a6'\n/,
- :*, /\t13: from \(irb\):1:in `a7'\n/,
- :*, /\t12: from \(irb\):1:in `a8'\n/,
- :*, /\t11: from \(irb\):1:in `a9'\n/,
- :*, /\t10: from \(irb\):1:in `a10'\n/,
- :*, /\t 9: from \(irb\):1:in `a11'\n/,
- :*, /\t 8: from \(irb\):1:in `a12'\n/,
- :*, /\t 7: from \(irb\):1:in `a13'\n/,
- :*, /\t 6: from \(irb\):1:in `a14'\n/,
- :*, /\t 5: from \(irb\):1:in `a15'\n/,
- :*, /\t 4: from \(irb\):1:in `a16'\n/,
- :*, /\t 3: from \(irb\):1:in `a17'\n/,
- :*, /\t 2: from \(irb\):1:in `a18'\n/,
- :*, /\t 1: from \(irb\):1:in `a19'\n/,
- :*, /\(irb\):1:in `a20': unhandled exception\n/,
+ :*, /\t16: from \(irb\):1:in (`|')a4'\n/,
+ :*, /\t15: from \(irb\):1:in (`|')a5'\n/,
+ :*, /\t14: from \(irb\):1:in (`|')a6'\n/,
+ :*, /\t13: from \(irb\):1:in (`|')a7'\n/,
+ :*, /\t12: from \(irb\):1:in (`|')a8'\n/,
+ :*, /\t11: from \(irb\):1:in (`|')a9'\n/,
+ :*, /\t10: from \(irb\):1:in (`|')a10'\n/,
+ :*, /\t 9: from \(irb\):1:in (`|')a11'\n/,
+ :*, /\t 8: from \(irb\):1:in (`|')a12'\n/,
+ :*, /\t 7: from \(irb\):1:in (`|')a13'\n/,
+ :*, /\t 6: from \(irb\):1:in (`|')a14'\n/,
+ :*, /\t 5: from \(irb\):1:in (`|')a15'\n/,
+ :*, /\t 4: from \(irb\):1:in (`|')a16'\n/,
+ :*, /\t 3: from \(irb\):1:in (`|')a17'\n/,
+ :*, /\t 2: from \(irb\):1:in (`|')a18'\n/,
+ :*, /\t 1: from \(irb\):1:in (`|')a19'\n/,
+ :*, /\(irb\):1:in (`|')a20': unhandled exception\n/,
]
else
expected = [
- :*, /\(irb\):1:in `a20': unhandled exception\n/,
- :*, /\tfrom \(irb\):1:in `a19'\n/,
- :*, /\tfrom \(irb\):1:in `a18'\n/,
- :*, /\tfrom \(irb\):1:in `a17'\n/,
- :*, /\tfrom \(irb\):1:in `a16'\n/,
- :*, /\tfrom \(irb\):1:in `a15'\n/,
- :*, /\tfrom \(irb\):1:in `a14'\n/,
- :*, /\tfrom \(irb\):1:in `a13'\n/,
- :*, /\tfrom \(irb\):1:in `a12'\n/,
- :*, /\tfrom \(irb\):1:in `a11'\n/,
- :*, /\tfrom \(irb\):1:in `a10'\n/,
- :*, /\tfrom \(irb\):1:in `a9'\n/,
- :*, /\tfrom \(irb\):1:in `a8'\n/,
- :*, /\tfrom \(irb\):1:in `a7'\n/,
- :*, /\tfrom \(irb\):1:in `a6'\n/,
- :*, /\tfrom \(irb\):1:in `a5'\n/,
- :*, /\tfrom \(irb\):1:in `a4'\n/,
+ :*, /\(irb\):1:in (`|')a20': unhandled exception\n/,
+ :*, /\tfrom \(irb\):1:in (`|')a19'\n/,
+ :*, /\tfrom \(irb\):1:in (`|')a18'\n/,
+ :*, /\tfrom \(irb\):1:in (`|')a17'\n/,
+ :*, /\tfrom \(irb\):1:in (`|')a16'\n/,
+ :*, /\tfrom \(irb\):1:in (`|')a15'\n/,
+ :*, /\tfrom \(irb\):1:in (`|')a14'\n/,
+ :*, /\tfrom \(irb\):1:in (`|')a13'\n/,
+ :*, /\tfrom \(irb\):1:in (`|')a12'\n/,
+ :*, /\tfrom \(irb\):1:in (`|')a11'\n/,
+ :*, /\tfrom \(irb\):1:in (`|')a10'\n/,
+ :*, /\tfrom \(irb\):1:in (`|')a9'\n/,
+ :*, /\tfrom \(irb\):1:in (`|')a8'\n/,
+ :*, /\tfrom \(irb\):1:in (`|')a7'\n/,
+ :*, /\tfrom \(irb\):1:in (`|')a6'\n/,
+ :*, /\tfrom \(irb\):1:in (`|')a5'\n/,
+ :*, /\tfrom \(irb\):1:in (`|')a4'\n/,
:*, /\t... \d+ levels...\n/,
]
end
@@ -670,21 +636,38 @@ module TestIRB
def test_prompt_main_escape
main = Struct.new(:to_s).new("main\a\t\r\n")
irb = IRB::Irb.new(IRB::WorkSpace.new(main), TestInputMethod.new)
- assert_equal("irb(main )>", irb.prompt('irb(%m)>', nil, 1, 1))
+ assert_equal("irb(main )>", irb.send(:format_prompt, 'irb(%m)>', nil, 1, 1))
end
def test_prompt_main_inspect_escape
main = Struct.new(:inspect).new("main\\n\nmain")
irb = IRB::Irb.new(IRB::WorkSpace.new(main), TestInputMethod.new)
- assert_equal("irb(main\\n main)>", irb.prompt('irb(%M)>', nil, 1, 1))
+ assert_equal("irb(main\\n main)>", irb.send(:format_prompt, 'irb(%M)>', nil, 1, 1))
end
def test_prompt_main_truncate
main = Struct.new(:to_s).new("a" * 100)
def main.inspect; to_s.inspect; end
irb = IRB::Irb.new(IRB::WorkSpace.new(main), TestInputMethod.new)
- assert_equal('irb(aaaaaaaaaaaaaaaaaaaaaaaaaaaaa...)>', irb.prompt('irb(%m)>', nil, 1, 1))
- assert_equal('irb("aaaaaaaaaaaaaaaaaaaaaaaaaaaa...)>', irb.prompt('irb(%M)>', nil, 1, 1))
+ assert_equal('irb(aaaaaaaaaaaaaaaaaaaaaaaaaaaaa...)>', irb.send(:format_prompt, 'irb(%m)>', nil, 1, 1))
+ assert_equal('irb("aaaaaaaaaaaaaaaaaaaaaaaaaaaa...)>', irb.send(:format_prompt, 'irb(%M)>', nil, 1, 1))
+ end
+
+ def test_prompt_main_raise
+ main = Object.new
+ def main.to_s; raise TypeError; end
+ def main.inspect; raise ArgumentError; end
+ irb = IRB::Irb.new(IRB::WorkSpace.new(main), TestInputMethod.new)
+ assert_equal("irb(!TypeError)>", irb.send(:format_prompt, 'irb(%m)>', nil, 1, 1))
+ assert_equal("irb(!ArgumentError)>", irb.send(:format_prompt, 'irb(%M)>', nil, 1, 1))
+ end
+
+ def test_prompt_format
+ main = 'main'
+ irb = IRB::Irb.new(IRB::WorkSpace.new(main), TestInputMethod.new)
+ assert_equal('%% main %m %main %%m >', irb.send(:format_prompt, '%%%% %m %%m %%%m %%%%m %l', '>', 1, 1))
+ assert_equal('42,%i, 42,%3i,042,%03i', irb.send(:format_prompt, '%i,%%i,%3i,%%3i,%03i,%%03i', nil, 42, 1))
+ assert_equal('42,%n, 42,%3n,042,%03n', irb.send(:format_prompt, '%n,%%n,%3n,%%3n,%03n,%%03n', nil, 1, 42))
end
def test_lineno
@@ -708,6 +691,31 @@ module TestIRB
], out)
end
+ def test_irb_path_setter
+ @context.irb_path = __FILE__
+ assert_equal(__FILE__, @context.irb_path)
+ assert_equal("#{__FILE__}(irb)", @context.instance_variable_get(:@eval_path))
+ @context.irb_path = 'file/does/not/exist'
+ assert_equal('file/does/not/exist', @context.irb_path)
+ assert_equal('file/does/not/exist', @context.instance_variable_get(:@eval_path))
+ @context.irb_path = "#{__FILE__}(irb)"
+ assert_equal("#{__FILE__}(irb)", @context.irb_path)
+ assert_equal("#{__FILE__}(irb)", @context.instance_variable_get(:@eval_path))
+ end
+
+ def test_build_completor
+ verbose, $VERBOSE = $VERBOSE, nil
+ original_completor = IRB.conf[:COMPLETOR]
+ IRB.conf[:COMPLETOR] = :regexp
+ assert_equal 'IRB::RegexpCompletor', @context.send(:build_completor).class.name
+ IRB.conf[:COMPLETOR] = :unknown
+ assert_equal 'IRB::RegexpCompletor', @context.send(:build_completor).class.name
+ # :type is tested in test_type_completor.rb
+ ensure
+ $VERBOSE = verbose
+ IRB.conf[:COMPLETOR] = original_completor
+ end
+
private
def without_colorize
diff --git a/test/irb/test_debug_cmd.rb b/test/irb/test_debug_cmd.rb
deleted file mode 100644
index 3bc00638dd..0000000000
--- a/test/irb/test_debug_cmd.rb
+++ /dev/null
@@ -1,299 +0,0 @@
-# frozen_string_literal: true
-
-begin
- require "pty"
-rescue LoadError
- return
-end
-
-require "tempfile"
-require "tmpdir"
-
-require_relative "helper"
-
-module TestIRB
- LIB = File.expand_path("../../lib", __dir__)
-
- class DebugCommandTestCase < TestCase
- IRB_AND_DEBUGGER_OPTIONS = {
- "NO_COLOR" => "true", "RUBY_DEBUG_HISTORY_FILE" => ''
- }
-
- def setup
- if ruby_core?
- omit "This test works only under ruby/irb"
- end
-
- if RUBY_ENGINE == 'truffleruby'
- omit "This test runs with ruby/debug, which doesn't work with truffleruby"
- end
- end
-
- def test_backtrace
- write_ruby <<~'RUBY'
- def foo
- binding.irb
- end
- foo
- RUBY
-
- output = run_ruby_file do
- type "backtrace"
- type "q!"
- end
-
- assert_match(/\(rdbg:irb\) backtrace/, output)
- assert_match(/Object#foo at #{@ruby_file.to_path}/, output)
- end
-
- def test_debug
- write_ruby <<~'ruby'
- binding.irb
- puts "hello"
- ruby
-
- output = run_ruby_file do
- type "debug"
- type "next"
- type "continue"
- end
-
- assert_match(/\(rdbg\) next/, output)
- assert_match(/=> 2\| puts "hello"/, output)
- end
-
- def test_next
- write_ruby <<~'ruby'
- binding.irb
- puts "hello"
- ruby
-
- output = run_ruby_file do
- type "next"
- type "continue"
- end
-
- assert_match(/\(rdbg:irb\) next/, output)
- assert_match(/=> 2\| puts "hello"/, output)
- end
-
- def test_break
- write_ruby <<~'RUBY'
- binding.irb
- puts "Hello"
- RUBY
-
- output = run_ruby_file do
- type "break 2"
- type "continue"
- type "continue"
- end
-
- assert_match(/\(rdbg:irb\) break/, output)
- assert_match(/=> 2\| puts "Hello"/, output)
- end
-
- def test_delete
- write_ruby <<~'RUBY'
- binding.irb
- puts "Hello"
- binding.irb
- puts "World"
- RUBY
-
- output = run_ruby_file do
- type "break 4"
- type "continue"
- type "delete 0"
- type "continue"
- end
-
- assert_match(/\(rdbg:irb\) delete/, output)
- assert_match(/deleted: #0 BP - Line/, output)
- end
-
- def test_step
- write_ruby <<~'RUBY'
- def foo
- puts "Hello"
- end
- binding.irb
- foo
- RUBY
-
- output = run_ruby_file do
- type "step"
- type "step"
- type "continue"
- end
-
- assert_match(/\(rdbg:irb\) step/, output)
- assert_match(/=> 5\| foo/, output)
- assert_match(/=> 2\| puts "Hello"/, output)
- end
-
- def test_continue
- write_ruby <<~'RUBY'
- binding.irb
- puts "Hello"
- binding.irb
- puts "World"
- RUBY
-
- output = run_ruby_file do
- type "continue"
- type "continue"
- end
-
- assert_match(/\(rdbg:irb\) continue/, output)
- assert_match(/=> 3: binding.irb/, output)
- end
-
- def test_finish
- write_ruby <<~'RUBY'
- def foo
- binding.irb
- puts "Hello"
- end
- foo
- RUBY
-
- output = run_ruby_file do
- type "finish"
- type "continue"
- end
-
- assert_match(/\(rdbg:irb\) finish/, output)
- assert_match(/=> 4\| end/, output)
- end
-
- def test_info
- write_ruby <<~'RUBY'
- def foo
- a = "He" + "llo"
- binding.irb
- end
- foo
- RUBY
-
- output = run_ruby_file do
- type "info"
- type "continue"
- end
-
- assert_match(/\(rdbg:irb\) info/, output)
- assert_match(/%self = main/, output)
- assert_match(/a = "Hello"/, output)
- end
-
- def test_catch
- write_ruby <<~'RUBY'
- binding.irb
- 1 / 0
- RUBY
-
- output = run_ruby_file do
- type "catch ZeroDivisionError"
- type "continue"
- type "continue"
- end
-
- assert_match(/\(rdbg:irb\) catch/, output)
- assert_match(/Stop by #0 BP - Catch "ZeroDivisionError"/, output)
- end
-
- private
-
- TIMEOUT_SEC = 3
-
- def run_ruby_file(&block)
- cmd = [EnvUtil.rubybin, "-I", LIB, @ruby_file.to_path]
- tmp_dir = Dir.mktmpdir
-
- @commands = []
- lines = []
-
- yield
-
- PTY.spawn(IRB_AND_DEBUGGER_OPTIONS.merge("TERM" => "dumb"), *cmd) do |read, write, pid|
- Timeout.timeout(TIMEOUT_SEC) do
- while line = safe_gets(read)
- lines << line
-
- # means the breakpoint is triggered
- if line.match?(/binding\.irb/)
- while command = @commands.shift
- write.puts(command)
- end
- end
- end
- end
- ensure
- read.close
- write.close
- kill_safely(pid)
- end
-
- lines.join
- rescue Timeout::Error
- message = <<~MSG
- Test timedout.
-
- #{'=' * 30} OUTPUT #{'=' * 30}
- #{lines.map { |l| " #{l}" }.join}
- #{'=' * 27} END OF OUTPUT #{'=' * 27}
- MSG
- assert_block(message) { false }
- ensure
- File.unlink(@ruby_file) if @ruby_file
- FileUtils.remove_entry tmp_dir
- end
-
- # read.gets could raise exceptions on some platforms
- # https://github.com/ruby/ruby/blob/master/ext/pty/pty.c#L729-L736
- def safe_gets(read)
- read.gets
- rescue Errno::EIO
- nil
- end
-
- def kill_safely pid
- return if wait_pid pid, TIMEOUT_SEC
-
- Process.kill :TERM, pid
- return if wait_pid pid, 0.2
-
- Process.kill :KILL, pid
- Process.waitpid(pid)
- rescue Errno::EPERM, Errno::ESRCH
- end
-
- def wait_pid pid, sec
- total_sec = 0.0
- wait_sec = 0.001 # 1ms
-
- while total_sec < sec
- if Process.waitpid(pid, Process::WNOHANG) == pid
- return true
- end
- sleep wait_sec
- total_sec += wait_sec
- wait_sec *= 2
- end
-
- false
- rescue Errno::ECHILD
- true
- end
-
- def type(command)
- @commands << command
- end
-
- def write_ruby(program)
- @ruby_file = Tempfile.create(%w{irb- .rb})
- @ruby_file.write(program)
- @ruby_file.close
- end
- end
-end
diff --git a/test/irb/test_debugger_integration.rb b/test/irb/test_debugger_integration.rb
new file mode 100644
index 0000000000..eca40c5702
--- /dev/null
+++ b/test/irb/test_debugger_integration.rb
@@ -0,0 +1,480 @@
+# frozen_string_literal: true
+
+require "tempfile"
+require "tmpdir"
+
+require_relative "helper"
+
+module TestIRB
+ class DebuggerIntegrationTest < IntegrationTestCase
+ def setup
+ super
+
+ if RUBY_ENGINE == 'truffleruby'
+ omit "This test runs with ruby/debug, which doesn't work with truffleruby"
+ end
+
+ @envs.merge!("NO_COLOR" => "true", "RUBY_DEBUG_HISTORY_FILE" => '')
+ end
+
+ def test_backtrace
+ write_ruby <<~'RUBY'
+ def foo
+ binding.irb
+ end
+ foo
+ RUBY
+
+ output = run_ruby_file do
+ type "backtrace"
+ type "exit!"
+ end
+
+ assert_match(/irb\(main\):001> backtrace/, output)
+ assert_match(/Object#foo at #{@ruby_file.to_path}/, output)
+ end
+
+ def test_debug
+ write_ruby <<~'ruby'
+ binding.irb
+ puts "hello"
+ ruby
+
+ output = run_ruby_file do
+ type "debug"
+ type "next"
+ type "continue"
+ end
+
+ assert_match(/irb\(main\):001> debug/, output)
+ assert_match(/irb:rdbg\(main\):002> next/, output)
+ assert_match(/=> 2\| puts "hello"/, output)
+ end
+
+ def test_debug_command_only_runs_once
+ write_ruby <<~'ruby'
+ binding.irb
+ ruby
+
+ output = run_ruby_file do
+ type "debug"
+ type "debug"
+ type "continue"
+ end
+
+ assert_match(/irb\(main\):001> debug/, output)
+ assert_match(/irb:rdbg\(main\):002> debug/, output)
+ assert_match(/IRB is already running with a debug session/, output)
+ end
+
+ def test_next
+ write_ruby <<~'ruby'
+ binding.irb
+ puts "hello"
+ ruby
+
+ output = run_ruby_file do
+ type "next"
+ type "continue"
+ end
+
+ assert_match(/irb\(main\):001> next/, output)
+ assert_match(/=> 2\| puts "hello"/, output)
+ end
+
+ def test_break
+ write_ruby <<~'RUBY'
+ binding.irb
+ puts "Hello"
+ RUBY
+
+ output = run_ruby_file do
+ type "break 2"
+ type "continue"
+ type "continue"
+ end
+
+ assert_match(/irb\(main\):001> break/, output)
+ assert_match(/=> 2\| puts "Hello"/, output)
+ end
+
+ def test_delete
+ write_ruby <<~'RUBY'
+ binding.irb
+ puts "Hello"
+ binding.irb
+ puts "World"
+ RUBY
+
+ output = run_ruby_file do
+ type "break 4"
+ type "continue"
+ type "delete 0"
+ type "continue"
+ end
+
+ assert_match(/irb:rdbg\(main\):003> delete/, output)
+ assert_match(/deleted: #0 BP - Line/, output)
+ end
+
+ def test_step
+ write_ruby <<~'RUBY'
+ def foo
+ puts "Hello"
+ end
+ binding.irb
+ foo
+ RUBY
+
+ output = run_ruby_file do
+ type "step"
+ type "step"
+ type "continue"
+ end
+
+ assert_match(/irb\(main\):001> step/, output)
+ assert_match(/=> 5\| foo/, output)
+ assert_match(/=> 2\| puts "Hello"/, output)
+ end
+
+ def test_long_stepping
+ write_ruby <<~'RUBY'
+ class Foo
+ def foo(num)
+ bar(num + 10)
+ end
+
+ def bar(num)
+ num
+ end
+ end
+
+ binding.irb
+ Foo.new.foo(100)
+ RUBY
+
+ output = run_ruby_file do
+ type "step"
+ type "step"
+ type "step"
+ type "step"
+ type "num"
+ type "continue"
+ end
+
+ assert_match(/irb\(main\):001> step/, output)
+ assert_match(/irb:rdbg\(main\):002> step/, output)
+ assert_match(/irb:rdbg\(#<Foo:.*>\):003> step/, output)
+ assert_match(/irb:rdbg\(#<Foo:.*>\):004> step/, output)
+ assert_match(/irb:rdbg\(#<Foo:.*>\):005> num/, output)
+ assert_match(/=> 110/, output)
+ end
+
+ def test_continue
+ write_ruby <<~'RUBY'
+ binding.irb
+ puts "Hello"
+ binding.irb
+ puts "World"
+ RUBY
+
+ output = run_ruby_file do
+ type "continue"
+ type "continue"
+ end
+
+ assert_match(/irb\(main\):001> continue/, output)
+ assert_match(/=> 3: binding.irb/, output)
+ assert_match(/irb:rdbg\(main\):002> continue/, output)
+ end
+
+ def test_finish
+ write_ruby <<~'RUBY'
+ def foo
+ binding.irb
+ puts "Hello"
+ end
+ foo
+ RUBY
+
+ output = run_ruby_file do
+ type "finish"
+ type "continue"
+ end
+
+ assert_match(/irb\(main\):001> finish/, output)
+ assert_match(/=> 4\| end/, output)
+ end
+
+ def test_info
+ write_ruby <<~'RUBY'
+ def foo
+ a = "He" + "llo"
+ binding.irb
+ end
+ foo
+ RUBY
+
+ output = run_ruby_file do
+ type "info"
+ type "continue"
+ end
+
+ assert_match(/irb\(main\):001> info/, output)
+ assert_match(/%self = main/, output)
+ assert_match(/a = "Hello"/, output)
+ end
+
+ def test_catch
+ write_ruby <<~'RUBY'
+ binding.irb
+ 1 / 0
+ RUBY
+
+ output = run_ruby_file do
+ type "catch ZeroDivisionError"
+ type "continue"
+ type "continue"
+ end
+
+ assert_match(/irb\(main\):001> catch/, output)
+ assert_match(/Stop by #0 BP - Catch "ZeroDivisionError"/, output)
+ end
+
+ def test_exit
+ write_ruby <<~'RUBY'
+ binding.irb
+ puts "he" + "llo"
+ RUBY
+
+ output = run_ruby_file do
+ type "debug"
+ type "exit"
+ end
+
+ assert_match(/irb:rdbg\(main\):002>/, output)
+ assert_match(/hello/, output)
+ end
+
+ def test_force_exit
+ write_ruby <<~'RUBY'
+ binding.irb
+ puts "he" + "llo"
+ RUBY
+
+ output = run_ruby_file do
+ type "debug"
+ type "exit!"
+ end
+
+ assert_match(/irb:rdbg\(main\):002>/, output)
+ assert_not_match(/hello/, output)
+ end
+
+ def test_quit
+ write_ruby <<~'RUBY'
+ binding.irb
+ puts "he" + "llo"
+ RUBY
+
+ output = run_ruby_file do
+ type "debug"
+ type "quit!"
+ end
+
+ assert_match(/irb:rdbg\(main\):002>/, output)
+ assert_not_match(/hello/, output)
+ end
+
+ def test_prompt_line_number_continues
+ write_ruby <<~'ruby'
+ binding.irb
+ puts "Hello"
+ puts "World"
+ ruby
+
+ output = run_ruby_file do
+ type "123"
+ type "456"
+ type "next"
+ type "info"
+ type "next"
+ type "continue"
+ end
+
+ assert_match(/irb\(main\):003> next/, output)
+ assert_match(/irb:rdbg\(main\):004> info/, output)
+ assert_match(/irb:rdbg\(main\):005> next/, output)
+ end
+
+ def test_prompt_irb_name_is_kept
+ write_rc <<~RUBY
+ IRB.conf[:IRB_NAME] = "foo"
+ RUBY
+
+ write_ruby <<~'ruby'
+ binding.irb
+ puts "Hello"
+ ruby
+
+ output = run_ruby_file do
+ type "next"
+ type "continue"
+ end
+
+ assert_match(/foo\(main\):001> next/, output)
+ assert_match(/foo:rdbg\(main\):002> continue/, output)
+ end
+
+ def test_irb_commands_are_available_after_moving_around_with_the_debugger
+ write_ruby <<~'ruby'
+ class Foo
+ def bar
+ puts "bar"
+ end
+ end
+
+ binding.irb
+ Foo.new.bar
+ ruby
+
+ output = run_ruby_file do
+ # Due to the way IRB defines its commands, moving into the Foo instance from main is necessary for proper testing.
+ type "next"
+ type "step"
+ type "irb_info"
+ type "continue"
+ end
+
+ assert_include(output, "InputMethod: RelineInputMethod")
+ end
+
+ def test_help_command_is_delegated_to_the_debugger
+ write_ruby <<~'ruby'
+ binding.irb
+ ruby
+
+ output = run_ruby_file do
+ type "debug"
+ type "help"
+ type "continue"
+ end
+
+ assert_include(output, "### Frame control")
+ end
+
+ def test_help_display_different_content_when_debugger_is_enabled
+ write_ruby <<~'ruby'
+ binding.irb
+ ruby
+
+ output = run_ruby_file do
+ type "debug"
+ type "help"
+ type "continue"
+ end
+
+ # IRB's commands should still be listed
+ assert_match(/help\s+List all available commands/, output)
+ # debug gem's commands should be appended at the end
+ assert_match(/Debugging \(from debug\.gem\)\s+### Control flow/, output)
+ end
+
+ def test_input_is_evaluated_in_the_context_of_the_current_thread
+ write_ruby <<~'ruby'
+ current_thread = Thread.current
+ binding.irb
+ ruby
+
+ output = run_ruby_file do
+ type "debug"
+ type '"Threads match: #{current_thread == Thread.current}"'
+ type "continue"
+ end
+
+ assert_match(/irb\(main\):001> debug/, output)
+ assert_match(/Threads match: true/, output)
+ end
+
+ def test_irb_switches_debugger_interface_if_debug_was_already_activated
+ write_ruby <<~'ruby'
+ require 'debug'
+ class Foo
+ def bar
+ puts "bar"
+ end
+ end
+
+ binding.irb
+ Foo.new.bar
+ ruby
+
+ output = run_ruby_file do
+ # Due to the way IRB defines its commands, moving into the Foo instance from main is necessary for proper testing.
+ type "next"
+ type "step"
+ type 'irb_info'
+ type "continue"
+ end
+
+ assert_match(/irb\(main\):001> next/, output)
+ assert_include(output, "InputMethod: RelineInputMethod")
+ end
+
+ def test_debugger_cant_be_activated_while_multi_irb_is_active
+ write_ruby <<~'ruby'
+ binding.irb
+ a = 1
+ ruby
+
+ output = run_ruby_file do
+ type "jobs"
+ type "next"
+ type "exit"
+ end
+
+ assert_match(/irb\(main\):001> jobs/, output)
+ assert_include(output, "Can't start the debugger when IRB is running in a multi-IRB session.")
+ end
+
+ def test_multi_irb_commands_are_not_available_after_activating_the_debugger
+ write_ruby <<~'ruby'
+ binding.irb
+ a = 1
+ ruby
+
+ output = run_ruby_file do
+ type "next"
+ type "jobs"
+ type "continue"
+ end
+
+ assert_match(/irb\(main\):001> next/, output)
+ assert_include(output, "Multi-IRB commands are not available when the debugger is enabled.")
+ end
+
+ def test_irb_passes_empty_input_to_debugger_to_repeat_the_last_command
+ write_ruby <<~'ruby'
+ binding.irb
+ puts "foo"
+ puts "bar"
+ puts "baz"
+ ruby
+
+ output = run_ruby_file do
+ type "next"
+ type ""
+ # Test that empty input doesn't repeat expressions
+ type "123"
+ type ""
+ type "next"
+ type ""
+ type ""
+ end
+
+ assert_include(output, "=> 2\| puts \"foo\"")
+ assert_include(output, "=> 3\| puts \"bar\"")
+ assert_include(output, "=> 4\| puts \"baz\"")
+ end
+ end
+end
diff --git a/test/irb/test_eval_history.rb b/test/irb/test_eval_history.rb
new file mode 100644
index 0000000000..54913ceff5
--- /dev/null
+++ b/test/irb/test_eval_history.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+require "irb"
+
+require_relative "helper"
+
+module TestIRB
+ class EvalHistoryTest < TestCase
+ def setup
+ save_encodings
+ IRB.instance_variable_get(:@CONF).clear
+ end
+
+ def teardown
+ restore_encodings
+ end
+
+ def execute_lines(*lines, conf: {}, main: self, irb_path: nil)
+ IRB.init_config(nil)
+ IRB.conf[:VERBOSE] = false
+ IRB.conf[:PROMPT_MODE] = :SIMPLE
+ IRB.conf[:USE_PAGER] = false
+ IRB.conf.merge!(conf)
+ input = TestInputMethod.new(lines)
+ irb = IRB::Irb.new(IRB::WorkSpace.new(main), input)
+ irb.context.return_format = "=> %s\n"
+ irb.context.irb_path = irb_path if irb_path
+ IRB.conf[:MAIN_CONTEXT] = irb.context
+ capture_output do
+ irb.eval_input
+ end
+ end
+
+ def test_eval_history_is_disabled_by_default
+ out, err = execute_lines(
+ "a = 1",
+ "__"
+ )
+
+ assert_empty(err)
+ assert_match(/undefined local variable or method (`|')__'/, out)
+ end
+
+ def test_eval_history_can_be_retrieved_with_double_underscore
+ out, err = execute_lines(
+ "a = 1",
+ "__",
+ conf: { EVAL_HISTORY: 5 }
+ )
+
+ assert_empty(err)
+ assert_match("=> 1\n" + "=> 1 1\n", out)
+ end
+
+ def test_eval_history_respects_given_limit
+ out, err = execute_lines(
+ "'foo'\n",
+ "'bar'\n",
+ "'baz'\n",
+ "'xyz'\n",
+ "__",
+ conf: { EVAL_HISTORY: 4 }
+ )
+
+ assert_empty(err)
+ # Because eval_history injects `__` into the history AND decide to ignore it, we only get <limit> - 1 results
+ assert_match("2 \"bar\"\n" + "3 \"baz\"\n" + "4 \"xyz\"\n", out)
+ end
+ end
+end
diff --git a/test/irb/test_evaluation.rb b/test/irb/test_evaluation.rb
new file mode 100644
index 0000000000..adb69b2067
--- /dev/null
+++ b/test/irb/test_evaluation.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require "tempfile"
+
+require_relative "helper"
+
+module TestIRB
+ class EchoingTest < IntegrationTestCase
+ def test_irb_echos_by_default
+ write_ruby <<~'RUBY'
+ binding.irb
+ RUBY
+
+ output = run_ruby_file do
+ type "123123"
+ type "exit"
+ end
+
+ assert_include(output, "=> 123123")
+ end
+
+ def test_irb_doesnt_echo_line_with_semicolon
+ write_ruby <<~'RUBY'
+ binding.irb
+ RUBY
+
+ output = run_ruby_file do
+ type "123123;"
+ type "123123 ;"
+ type "123123; "
+ type <<~RUBY
+ if true
+ 123123
+ end;
+ RUBY
+ type "'evaluation ends'"
+ type "exit"
+ end
+
+ assert_include(output, "=> \"evaluation ends\"")
+ assert_not_include(output, "=> 123123")
+ end
+ end
+end
diff --git a/test/irb/test_helper_method.rb b/test/irb/test_helper_method.rb
new file mode 100644
index 0000000000..291278c16a
--- /dev/null
+++ b/test/irb/test_helper_method.rb
@@ -0,0 +1,134 @@
+# frozen_string_literal: true
+require "irb"
+
+require_relative "helper"
+
+module TestIRB
+ class HelperMethodTestCase < TestCase
+ def setup
+ $VERBOSE = nil
+ @verbosity = $VERBOSE
+ save_encodings
+ IRB.instance_variable_get(:@CONF).clear
+ end
+
+ def teardown
+ $VERBOSE = @verbosity
+ restore_encodings
+ end
+
+ def execute_lines(*lines, conf: {}, main: self, irb_path: nil)
+ IRB.init_config(nil)
+ IRB.conf[:VERBOSE] = false
+ IRB.conf[:PROMPT_MODE] = :SIMPLE
+ IRB.conf.merge!(conf)
+ input = TestInputMethod.new(lines)
+ irb = IRB::Irb.new(IRB::WorkSpace.new(main), input)
+ irb.context.return_format = "=> %s\n"
+ irb.context.irb_path = irb_path if irb_path
+ IRB.conf[:MAIN_CONTEXT] = irb.context
+ IRB.conf[:USE_PAGER] = false
+ capture_output do
+ irb.eval_input
+ end
+ end
+ end
+
+ module TestHelperMethod
+ class ConfTest < HelperMethodTestCase
+ def test_conf_returns_the_context_object
+ out, err = execute_lines("conf.ap_name")
+
+ assert_empty err
+ assert_include out, "=> \"irb\""
+ end
+ end
+ end
+
+ class HelperMethodIntegrationTest < IntegrationTestCase
+ def test_arguments_propogation
+ write_ruby <<~RUBY
+ require "irb/helper_method"
+
+ class MyHelper < IRB::HelperMethod::Base
+ description "This is a test helper"
+
+ def execute(
+ required_arg, optional_arg = nil, *splat_arg, required_keyword_arg:,
+ optional_keyword_arg: nil, **double_splat_arg, &block_arg
+ )
+ puts [required_arg, optional_arg, splat_arg, required_keyword_arg, optional_keyword_arg, double_splat_arg, block_arg.call].to_s
+ end
+ end
+
+ IRB::HelperMethod.register(:my_helper, MyHelper)
+
+ binding.irb
+ RUBY
+
+ output = run_ruby_file do
+ type <<~INPUT
+ my_helper(
+ "required", "optional", "splat", required_keyword_arg: "required",
+ optional_keyword_arg: "optional", a: 1, b: 2
+ ) { "block" }
+ INPUT
+ type "exit"
+ end
+
+ assert_include(output, '["required", "optional", ["splat"], "required", "optional", {:a=>1, :b=>2}, "block"]')
+ end
+
+ def test_helper_method_injection_can_happen_after_irb_require
+ write_ruby <<~RUBY
+ require "irb"
+
+ class MyHelper < IRB::HelperMethod::Base
+ description "This is a test helper"
+
+ def execute
+ puts "Hello from MyHelper"
+ end
+ end
+
+ IRB::HelperMethod.register(:my_helper, MyHelper)
+
+ binding.irb
+ RUBY
+
+ output = run_ruby_file do
+ type "my_helper"
+ type "exit"
+ end
+
+ assert_include(output, 'Hello from MyHelper')
+ end
+
+ def test_helper_method_instances_are_memoized
+ write_ruby <<~RUBY
+ require "irb/helper_method"
+
+ class MyHelper < IRB::HelperMethod::Base
+ description "This is a test helper"
+
+ def execute(val)
+ @val ||= val
+ end
+ end
+
+ IRB::HelperMethod.register(:my_helper, MyHelper)
+
+ binding.irb
+ RUBY
+
+ output = run_ruby_file do
+ type "my_helper(100)"
+ type "my_helper(200)"
+ type "exit"
+ end
+
+ assert_include(output, '=> 100')
+ assert_not_include(output, '=> 200')
+ end
+ end
+end
diff --git a/test/irb/test_history.rb b/test/irb/test_history.rb
index c74a3a2cb9..63be35fdaa 100644
--- a/test/irb/test_history.rb
+++ b/test/irb/test_history.rb
@@ -1,22 +1,45 @@
# frozen_string_literal: false
require 'irb'
-require 'irb/ext/save-history'
require 'readline'
+require "tempfile"
require_relative "helper"
+return if RUBY_PLATFORM.match?(/solaris|mswin|mingw/i)
+
module TestIRB
- class TestHistory < TestCase
+ class HistoryTest < TestCase
def setup
- IRB.conf[:RC_NAME_GENERATOR] = nil
+ @original_verbose, $VERBOSE = $VERBOSE, nil
+ @tmpdir = Dir.mktmpdir("test_irb_history_")
+ @backup_home = ENV["HOME"]
+ @backup_xdg_config_home = ENV.delete("XDG_CONFIG_HOME")
+ @backup_irbrc = ENV.delete("IRBRC")
+ @backup_default_external = Encoding.default_external
+ ENV["HOME"] = @tmpdir
+ IRB.instance_variable_set(:@existing_rc_name_generators, nil)
end
def teardown
- IRB.conf[:RC_NAME_GENERATOR] = nil
+ IRB.instance_variable_set(:@existing_rc_name_generators, nil)
+ ENV["HOME"] = @backup_home
+ ENV["XDG_CONFIG_HOME"] = @backup_xdg_config_home
+ ENV["IRBRC"] = @backup_irbrc
+ Encoding.default_external = @backup_default_external
+ $VERBOSE = @original_verbose
+ FileUtils.rm_rf(@tmpdir)
+ end
+
+ class TestInputMethodWithRelineHistory < TestInputMethod
+ # When IRB.conf[:USE_MULTILINE] is true, IRB::RelineInputMethod uses Reline::History
+ HISTORY = Reline::History.new(Reline.core.config)
+
+ include IRB::HistorySavingAbility
end
- class TestInputMethodWithHistory < TestInputMethod
- HISTORY = Array.new
+ class TestInputMethodWithReadlineHistory < TestInputMethod
+ # When IRB.conf[:USE_MULTILINE] is false, IRB::ReadlineInputMethod uses Readline::HISTORY
+ HISTORY = Readline::HISTORY
include IRB::HistorySavingAbility
end
@@ -100,10 +123,80 @@ module TestIRB
INPUT
end
- def test_history_concurrent_use
+ def test_history_concurrent_use_reline
+ omit "Skip Editline" if /EditLine/n.match(Readline::VERSION)
+ IRB.conf[:SAVE_HISTORY] = 1
+ history_concurrent_use_for_input_method(TestInputMethodWithRelineHistory)
+ end
+
+ def test_history_concurrent_use_readline
omit "Skip Editline" if /EditLine/n.match(Readline::VERSION)
IRB.conf[:SAVE_HISTORY] = 1
- assert_history(<<~EXPECTED_HISTORY, <<~INITIAL_HISTORY, <<~INPUT) do |history_file|
+ history_concurrent_use_for_input_method(TestInputMethodWithReadlineHistory)
+ end
+
+ def test_history_concurrent_use_not_present
+ IRB.conf[:LC_MESSAGES] = IRB::Locale.new
+ IRB.conf[:SAVE_HISTORY] = 1
+ io = TestInputMethodWithRelineHistory.new
+ io.class::HISTORY.clear
+ io.load_history
+ io.class::HISTORY << 'line1'
+ io.class::HISTORY << 'line2'
+
+ history_file = IRB.rc_file("_history")
+ assert_not_send [File, :file?, history_file]
+ File.write(history_file, "line0\n")
+ io.save_history
+ assert_equal(%w"line0 line1 line2", File.read(history_file).split)
+ end
+
+ def test_history_different_encodings
+ IRB.conf[:SAVE_HISTORY] = 2
+ Encoding.default_external = Encoding::US_ASCII
+ locale = IRB::Locale.new("C")
+ assert_history(<<~EXPECTED_HISTORY.encode(Encoding::US_ASCII), <<~INITIAL_HISTORY.encode(Encoding::UTF_8), <<~INPUT, locale: locale)
+ ????
+ exit
+ EXPECTED_HISTORY
+ 😀
+ INITIAL_HISTORY
+ exit
+ INPUT
+ end
+
+ def test_history_does_not_raise_when_history_file_directory_does_not_exist
+ backup_history_file = IRB.conf[:HISTORY_FILE]
+ IRB.conf[:SAVE_HISTORY] = 1
+ IRB.conf[:HISTORY_FILE] = "fake/fake/fake/history_file"
+ io = TestInputMethodWithRelineHistory.new
+
+ assert_warn(/history file does not exist/) do
+ io.save_history
+ end
+
+ # assert_warn reverts $VERBOSE to EnvUtil.original_verbose, which is true in some cases
+ # We want to keep $VERBOSE as nil until teardown is called
+ # TODO: check if this is an assert_warn issue
+ $VERBOSE = nil
+ ensure
+ IRB.conf[:HISTORY_FILE] = backup_history_file
+ end
+
+ def test_no_home_no_history_file_does_not_raise_history_save
+ ENV['HOME'] = nil
+ io = TestInputMethodWithRelineHistory.new
+ assert_nil(IRB.rc_file('_history'))
+ assert_nothing_raised do
+ io.load_history
+ io.save_history
+ end
+ end
+
+ private
+
+ def history_concurrent_use_for_input_method(input_method)
+ assert_history(<<~EXPECTED_HISTORY, <<~INITIAL_HISTORY, <<~INPUT, input_method) do |history_file|
exit
5
exit
@@ -116,7 +209,7 @@ module TestIRB
5
exit
INPUT
- assert_history(<<~EXPECTED_HISTORY2, <<~INITIAL_HISTORY2, <<~INPUT2)
+ assert_history(<<~EXPECTED_HISTORY2, <<~INITIAL_HISTORY2, <<~INPUT2, input_method)
exit
EXPECTED_HISTORY2
1
@@ -131,60 +224,31 @@ module TestIRB
end
end
- def test_history_concurrent_use_not_present
- backup_home = ENV["HOME"]
- backup_xdg_config_home = ENV.delete("XDG_CONFIG_HOME")
- backup_irbrc = ENV.delete("IRBRC")
- IRB.conf[:LC_MESSAGES] = IRB::Locale.new
- IRB.conf[:SAVE_HISTORY] = 1
- Dir.mktmpdir("test_irb_history_") do |tmpdir|
- ENV["HOME"] = tmpdir
- io = TestInputMethodWithHistory.new
- io.class::HISTORY.clear
- io.load_history
- io.class::HISTORY.concat(%w"line1 line2")
-
- history_file = IRB.rc_file("_history")
- assert_not_send [File, :file?, history_file]
- File.write(history_file, "line0\n")
- io.save_history
- assert_equal(%w"line0 line1 line2", File.read(history_file).split)
- end
- ensure
- ENV["HOME"] = backup_home
- ENV["XDG_CONFIG_HOME"] = backup_xdg_config_home
- ENV["IRBRC"] = backup_irbrc
- end
-
- private
-
- def assert_history(expected_history, initial_irb_history, input)
- backup_verbose, $VERBOSE = $VERBOSE, nil
- backup_home = ENV["HOME"]
- backup_xdg_config_home = ENV.delete("XDG_CONFIG_HOME")
- IRB.conf[:LC_MESSAGES] = IRB::Locale.new
+ def assert_history(expected_history, initial_irb_history, input, input_method = TestInputMethodWithRelineHistory, locale: IRB::Locale.new)
+ IRB.conf[:LC_MESSAGES] = locale
actual_history = nil
- Dir.mktmpdir("test_irb_history_") do |tmpdir|
- ENV["HOME"] = tmpdir
- File.open(IRB.rc_file("_history"), "w") do |f|
- f.write(initial_irb_history)
- end
+ history_file = IRB.rc_file("_history")
+ ENV["HOME"] = @tmpdir
+ File.open(history_file, "w") do |f|
+ f.write(initial_irb_history)
+ end
- io = TestInputMethodWithHistory.new
+ io = input_method.new
+ io.class::HISTORY.clear
+ io.load_history
+ if block_given?
+ previous_history = []
+ io.class::HISTORY.each { |line| previous_history << line }
+ yield history_file
io.class::HISTORY.clear
- io.load_history
- if block_given?
- history = io.class::HISTORY.dup
- yield IRB.rc_file("_history")
- io.class::HISTORY.replace(history)
- end
- io.class::HISTORY.concat(input.split)
- io.save_history
+ previous_history.each { |line| io.class::HISTORY << line }
+ end
+ input.split.each { |line| io.class::HISTORY << line }
+ io.save_history
- io.load_history
- File.open(IRB.rc_file("_history"), "r") do |f|
- actual_history = f.read
- end
+ io.load_history
+ File.open(history_file, "r") do |f|
+ actual_history = f.read
end
assert_equal(expected_history, actual_history, <<~MESSAGE)
expected:
@@ -192,10 +256,6 @@ module TestIRB
but actual:
#{actual_history}
MESSAGE
- ensure
- $VERBOSE = backup_verbose
- ENV["HOME"] = backup_home
- ENV["XDG_CONFIG_HOME"] = backup_xdg_config_home
end
def with_temp_stdio
@@ -206,4 +266,226 @@ module TestIRB
end
end
end
-end if not RUBY_PLATFORM.match?(/solaris|mswin|mingw/i)
+
+ class IRBHistoryIntegrationTest < IntegrationTestCase
+ def test_history_saving_with_debug
+ write_history ""
+
+ write_ruby <<~'RUBY'
+ def foo
+ end
+
+ binding.irb
+
+ foo
+ RUBY
+
+ output = run_ruby_file do
+ type "'irb session'"
+ type "next"
+ type "'irb:debug session'"
+ type "step"
+ type "irb_info"
+ type "puts Reline::HISTORY.to_a.to_s"
+ type "q!"
+ end
+
+ assert_include(output, "InputMethod: RelineInputMethod")
+ # check that in-memory history is preserved across sessions
+ assert_include output, %q(
+ ["'irb session'", "next", "'irb:debug session'", "step", "irb_info", "puts Reline::HISTORY.to_a.to_s"]
+ ).strip
+
+ assert_equal <<~HISTORY, @history_file.open.read
+ 'irb session'
+ next
+ 'irb:debug session'
+ step
+ irb_info
+ puts Reline::HISTORY.to_a.to_s
+ q!
+ HISTORY
+ end
+
+ def test_history_saving_with_debug_without_prior_history
+ tmpdir = Dir.mktmpdir("test_irb_history_")
+ # Intentionally not creating the file so we test the reset counter logic
+ history_file = File.join(tmpdir, "irb_history")
+
+ write_rc <<~RUBY
+ IRB.conf[:HISTORY_FILE] = "#{history_file}"
+ RUBY
+
+ write_ruby <<~'RUBY'
+ def foo
+ end
+
+ binding.irb
+
+ foo
+ RUBY
+
+ output = run_ruby_file do
+ type "'irb session'"
+ type "next"
+ type "'irb:debug session'"
+ type "step"
+ type "irb_info"
+ type "puts Reline::HISTORY.to_a.to_s"
+ type "q!"
+ end
+
+ assert_include(output, "InputMethod: RelineInputMethod")
+ # check that in-memory history is preserved across sessions
+ assert_include output, %q(
+ ["'irb session'", "next", "'irb:debug session'", "step", "irb_info", "puts Reline::HISTORY.to_a.to_s"]
+ ).strip
+
+ assert_equal <<~HISTORY, File.read(history_file)
+ 'irb session'
+ next
+ 'irb:debug session'
+ step
+ irb_info
+ puts Reline::HISTORY.to_a.to_s
+ q!
+ HISTORY
+ ensure
+ FileUtils.rm_rf(tmpdir)
+ end
+
+ def test_history_saving_with_nested_sessions
+ write_history ""
+
+ write_ruby <<~'RUBY'
+ def foo
+ binding.irb
+ end
+
+ binding.irb
+ RUBY
+
+ run_ruby_file do
+ type "'outer session'"
+ type "foo"
+ type "'inner session'"
+ type "exit"
+ type "'outer session again'"
+ type "exit"
+ end
+
+ assert_equal <<~HISTORY, @history_file.open.read
+ 'outer session'
+ foo
+ 'inner session'
+ exit
+ 'outer session again'
+ exit
+ HISTORY
+ end
+
+ def test_nested_history_saving_from_inner_session_with_exit!
+ write_history ""
+
+ write_ruby <<~'RUBY'
+ def foo
+ binding.irb
+ end
+
+ binding.irb
+ RUBY
+
+ run_ruby_file do
+ type "'outer session'"
+ type "foo"
+ type "'inner session'"
+ type "exit!"
+ end
+
+ assert_equal <<~HISTORY, @history_file.open.read
+ 'outer session'
+ foo
+ 'inner session'
+ exit!
+ HISTORY
+ end
+
+ def test_nested_history_saving_from_outer_session_with_exit!
+ write_history ""
+
+ write_ruby <<~'RUBY'
+ def foo
+ binding.irb
+ end
+
+ binding.irb
+ RUBY
+
+ run_ruby_file do
+ type "'outer session'"
+ type "foo"
+ type "'inner session'"
+ type "exit"
+ type "'outer session again'"
+ type "exit!"
+ end
+
+ assert_equal <<~HISTORY, @history_file.open.read
+ 'outer session'
+ foo
+ 'inner session'
+ exit
+ 'outer session again'
+ exit!
+ HISTORY
+ end
+
+ def test_history_saving_with_nested_sessions_and_prior_history
+ write_history <<~HISTORY
+ old_history_1
+ old_history_2
+ old_history_3
+ HISTORY
+
+ write_ruby <<~'RUBY'
+ def foo
+ binding.irb
+ end
+
+ binding.irb
+ RUBY
+
+ run_ruby_file do
+ type "'outer session'"
+ type "foo"
+ type "'inner session'"
+ type "exit"
+ type "'outer session again'"
+ type "exit"
+ end
+
+ assert_equal <<~HISTORY, @history_file.open.read
+ old_history_1
+ old_history_2
+ old_history_3
+ 'outer session'
+ foo
+ 'inner session'
+ exit
+ 'outer session again'
+ exit
+ HISTORY
+ end
+
+ private
+
+ def write_history(history)
+ @history_file = Tempfile.new('irb_history')
+ @history_file.write(history)
+ @history_file.close
+ write_rc <<~RUBY
+ IRB.conf[:HISTORY_FILE] = "#{@history_file.path}"
+ RUBY
+ end
+ end
+end
diff --git a/test/irb/test_init.rb b/test/irb/test_init.rb
index d9e338da8d..f11d7398c8 100644
--- a/test/irb/test_init.rb
+++ b/test/irb/test_init.rb
@@ -5,19 +5,24 @@ require "fileutils"
require_relative "helper"
module TestIRB
- class TestInit < TestCase
+ class InitTest < TestCase
def setup
# IRBRC is for RVM...
@backup_env = %w[HOME XDG_CONFIG_HOME IRBRC].each_with_object({}) do |env, hash|
hash[env] = ENV.delete(env)
end
- ENV["HOME"] = @tmpdir = Dir.mktmpdir("test_irb_init_#{$$}")
+ ENV["HOME"] = @tmpdir = File.realpath(Dir.mktmpdir("test_irb_init_#{$$}"))
+ end
+
+ def reset_rc_name_generators
+ IRB.instance_variable_set(:@existing_rc_name_generators, nil)
end
def teardown
ENV.update(@backup_env)
FileUtils.rm_rf(@tmpdir)
IRB.conf.delete(:SCRIPT)
+ reset_rc_name_generators
end
def test_setup_with_argv_preserves_global_argv
@@ -34,42 +39,80 @@ module TestIRB
assert_equal orig, $0
end
- def test_rc_file
+ def test_rc_files
tmpdir = @tmpdir
Dir.chdir(tmpdir) do
- ENV["XDG_CONFIG_HOME"] = "#{tmpdir}/xdg"
- IRB.conf[:RC_NAME_GENERATOR] = nil
- assert_equal(tmpdir+"/.irb#{IRB::IRBRC_EXT}", IRB.rc_file)
- assert_equal(tmpdir+"/.irb_history", IRB.rc_file("_history"))
- assert_file.not_exist?(tmpdir+"/xdg")
- IRB.conf[:RC_NAME_GENERATOR] = nil
- FileUtils.touch(tmpdir+"/.irb#{IRB::IRBRC_EXT}")
- assert_equal(tmpdir+"/.irb#{IRB::IRBRC_EXT}", IRB.rc_file)
- assert_equal(tmpdir+"/.irb_history", IRB.rc_file("_history"))
- assert_file.not_exist?(tmpdir+"/xdg")
+ home = ENV['HOME'] = "#{tmpdir}/home"
+ xdg_config_home = ENV['XDG_CONFIG_HOME'] = "#{tmpdir}/xdg"
+ reset_rc_name_generators
+ assert_empty(IRB.irbrc_files)
+ assert_equal("#{home}/.irb_history", IRB.rc_file('_history'))
+ FileUtils.mkdir_p(home)
+ FileUtils.mkdir_p("#{xdg_config_home}/irb")
+ FileUtils.mkdir_p("#{home}/.config/irb")
+ reset_rc_name_generators
+ assert_empty(IRB.irbrc_files)
+ assert_equal("#{xdg_config_home}/irb/irb_history", IRB.rc_file('_history'))
+ home_irbrc = "#{home}/.irbrc"
+ config_irbrc = "#{home}/.config/irb/irbrc"
+ xdg_config_irbrc = "#{xdg_config_home}/irb/irbrc"
+ [home_irbrc, config_irbrc, xdg_config_irbrc].each do |file|
+ FileUtils.touch(file)
+ end
+ current_dir_irbrcs = %w[.irbrc irbrc _irbrc $irbrc].map { |file| "#{tmpdir}/#{file}" }
+ current_dir_irbrcs.each { |file| FileUtils.touch(file) }
+ reset_rc_name_generators
+ assert_equal([xdg_config_irbrc, home_irbrc, *current_dir_irbrcs], IRB.irbrc_files)
+ assert_equal(xdg_config_irbrc.sub(/rc$/, '_history'), IRB.rc_file('_history'))
+ ENV['XDG_CONFIG_HOME'] = nil
+ reset_rc_name_generators
+ assert_equal([home_irbrc, config_irbrc, *current_dir_irbrcs], IRB.irbrc_files)
+ assert_equal(home_irbrc.sub(/rc$/, '_history'), IRB.rc_file('_history'))
+ ENV['XDG_CONFIG_HOME'] = ''
+ reset_rc_name_generators
+ assert_equal([home_irbrc, config_irbrc] + current_dir_irbrcs, IRB.irbrc_files)
+ assert_equal(home_irbrc.sub(/rc$/, '_history'), IRB.rc_file('_history'))
+ ENV['XDG_CONFIG_HOME'] = xdg_config_home
+ ENV['IRBRC'] = "#{tmpdir}/.irbrc"
+ reset_rc_name_generators
+ assert_equal([ENV['IRBRC'], xdg_config_irbrc, home_irbrc] + (current_dir_irbrcs - [ENV['IRBRC']]), IRB.irbrc_files)
+ assert_equal(ENV['IRBRC'] + '_history', IRB.rc_file('_history'))
+ ENV['IRBRC'] = ENV['HOME'] = ENV['XDG_CONFIG_HOME'] = nil
+ reset_rc_name_generators
+ assert_equal(current_dir_irbrcs, IRB.irbrc_files)
+ assert_nil(IRB.rc_file('_history'))
end
end
- def test_rc_file_in_subdir
+ def test_duplicated_rc_files
tmpdir = @tmpdir
Dir.chdir(tmpdir) do
- FileUtils.mkdir_p("#{tmpdir}/mydir")
- Dir.chdir("#{tmpdir}/mydir") do
- IRB.conf[:RC_NAME_GENERATOR] = nil
- assert_equal(tmpdir+"/.irb#{IRB::IRBRC_EXT}", IRB.rc_file)
- assert_equal(tmpdir+"/.irb_history", IRB.rc_file("_history"))
- IRB.conf[:RC_NAME_GENERATOR] = nil
- FileUtils.touch(tmpdir+"/.irb#{IRB::IRBRC_EXT}")
- assert_equal(tmpdir+"/.irb#{IRB::IRBRC_EXT}", IRB.rc_file)
- assert_equal(tmpdir+"/.irb_history", IRB.rc_file("_history"))
+ ENV['XDG_CONFIG_HOME'] = "#{ENV['HOME']}/.config"
+ FileUtils.mkdir_p("#{ENV['XDG_CONFIG_HOME']}/irb")
+ env_irbrc = ENV['IRBRC'] = "#{tmpdir}/_irbrc"
+ xdg_config_irbrc = "#{ENV['XDG_CONFIG_HOME']}/irb/irbrc"
+ home_irbrc = "#{ENV['HOME']}/.irbrc"
+ current_dir_irbrc = "#{tmpdir}/irbrc"
+ [env_irbrc, xdg_config_irbrc, home_irbrc, current_dir_irbrc].each do |file|
+ FileUtils.touch(file)
end
+ reset_rc_name_generators
+ assert_equal([env_irbrc, xdg_config_irbrc, home_irbrc, current_dir_irbrc], IRB.irbrc_files)
end
end
- def test_recovery_sigint
+ def test_sigint_restore_default
pend "This test gets stuck on Solaris for unknown reason; contribution is welcome" if RUBY_PLATFORM =~ /solaris/
bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : []
- status = assert_in_out_err(bundle_exec + %w[-W0 -rirb -e binding.irb;loop{Process.kill("SIGINT",$$)} -- -f --], "exit\n", //, //)
+ # IRB should restore SIGINT handler
+ status = assert_in_out_err(bundle_exec + %w[-W0 -rirb -e Signal.trap("SIGINT","DEFAULT");binding.irb;loop{Process.kill("SIGINT",$$)} -- -f --], "exit\n", //, //)
+ Process.kill("SIGKILL", status.pid) if !status.exited? && !status.stopped? && !status.signaled?
+ end
+
+ def test_sigint_restore_block
+ bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : []
+ # IRB should restore SIGINT handler
+ status = assert_in_out_err(bundle_exec + %w[-W0 -rirb -e x=false;Signal.trap("SIGINT"){x=true};binding.irb;loop{Process.kill("SIGINT",$$);if(x);break;end} -- -f --], "exit\n", //, //)
Process.kill("SIGKILL", status.pid) if !status.exited? && !status.stopped? && !status.signaled?
end
@@ -120,6 +163,50 @@ module TestIRB
IRB.conf[:USE_AUTOCOMPLETE] = orig_use_autocomplete_conf
end
+ def test_completor_environment_variable
+ orig_use_autocomplete_env = ENV['IRB_COMPLETOR']
+ orig_use_autocomplete_conf = IRB.conf[:COMPLETOR]
+
+ ENV['IRB_COMPLETOR'] = nil
+ IRB.setup(__FILE__)
+ assert_equal(:regexp, IRB.conf[:COMPLETOR])
+
+ ENV['IRB_COMPLETOR'] = 'regexp'
+ IRB.setup(__FILE__)
+ assert_equal(:regexp, IRB.conf[:COMPLETOR])
+
+ ENV['IRB_COMPLETOR'] = 'type'
+ IRB.setup(__FILE__)
+ assert_equal(:type, IRB.conf[:COMPLETOR])
+
+ ENV['IRB_COMPLETOR'] = 'regexp'
+ IRB.setup(__FILE__, argv: ['--type-completor'])
+ assert_equal :type, IRB.conf[:COMPLETOR]
+
+ ENV['IRB_COMPLETOR'] = 'type'
+ IRB.setup(__FILE__, argv: ['--regexp-completor'])
+ assert_equal :regexp, IRB.conf[:COMPLETOR]
+ ensure
+ ENV['IRB_COMPLETOR'] = orig_use_autocomplete_env
+ IRB.conf[:COMPLETOR] = orig_use_autocomplete_conf
+ end
+
+ def test_completor_setup_with_argv
+ orig_completor_conf = IRB.conf[:COMPLETOR]
+
+ # Default is :regexp
+ IRB.setup(__FILE__, argv: [])
+ assert_equal :regexp, IRB.conf[:COMPLETOR]
+
+ IRB.setup(__FILE__, argv: ['--type-completor'])
+ assert_equal :type, IRB.conf[:COMPLETOR]
+
+ IRB.setup(__FILE__, argv: ['--regexp-completor'])
+ assert_equal :regexp, IRB.conf[:COMPLETOR]
+ ensure
+ IRB.conf[:COMPLETOR] = orig_completor_conf
+ end
+
def test_noscript
argv = %w[--noscript -- -f]
IRB.setup(eval("__FILE__"), argv: argv)
@@ -164,6 +251,12 @@ module TestIRB
assert_equal(['-f'], argv)
end
+ def test_option_tracer
+ argv = %w[--tracer]
+ IRB.setup(eval("__FILE__"), argv: argv)
+ assert_equal(true, IRB.conf[:USE_TRACER])
+ end
+
private
def with_argv(argv)
@@ -174,4 +267,44 @@ module TestIRB
ARGV.replace(orig)
end
end
+
+ class InitIntegrationTest < IntegrationTestCase
+ def test_load_error_in_rc_file_is_warned
+ write_rc <<~'IRBRC'
+ require "file_that_does_not_exist"
+ IRBRC
+
+ write_ruby <<~'RUBY'
+ binding.irb
+ RUBY
+
+ output = run_ruby_file do
+ type "'foobar'"
+ type "exit"
+ end
+
+ # IRB session should still be started
+ assert_includes output, "foobar"
+ assert_includes output, 'cannot load such file -- file_that_does_not_exist (LoadError)'
+ end
+
+ def test_normal_errors_in_rc_file_is_warned
+ write_rc <<~'IRBRC'
+ raise "I'm an error"
+ IRBRC
+
+ write_ruby <<~'RUBY'
+ binding.irb
+ RUBY
+
+ output = run_ruby_file do
+ type "'foobar'"
+ type "exit"
+ end
+
+ # IRB session should still be started
+ assert_includes output, "foobar"
+ assert_includes output, 'I\'m an error (RuntimeError)'
+ end
+ end
end
diff --git a/test/irb/test_input_method.rb b/test/irb/test_input_method.rb
index fdfb566390..ce317b4b32 100644
--- a/test/irb/test_input_method.rb
+++ b/test/irb/test_input_method.rb
@@ -1,11 +1,11 @@
# frozen_string_literal: false
require "irb"
-
+require "rdoc"
require_relative "helper"
module TestIRB
- class TestRelineInputMethod < TestCase
+ class InputMethodTest < TestCase
def setup
@conf_backup = IRB.conf.dup
IRB.conf[:LC_MESSAGES] = IRB::Locale.new
@@ -15,16 +15,22 @@ module TestIRB
def teardown
IRB.conf.replace(@conf_backup)
restore_encodings
+ # Reset Reline configuration overridden by RelineInputMethod.
+ Reline.instance_variable_set(:@core, nil)
end
+ end
+ class RelineInputMethodTest < InputMethodTest
def test_initialization
- IRB::RelineInputMethod.new
+ Reline.completion_proc = nil
+ Reline.dig_perfect_match_proc = nil
+ IRB::RelineInputMethod.new(IRB::RegexpCompletor.new)
assert_nil Reline.completion_append_character
assert_equal '', Reline.completer_quote_characters
- assert_equal IRB::InputCompletor::BASIC_WORD_BREAK_CHARACTERS, Reline.basic_word_break_characters
- assert_equal IRB::InputCompletor::CompletionProc, Reline.completion_proc
- assert_equal IRB::InputCompletor::PerfectMatchedProc, Reline.dig_perfect_match_proc
+ assert_equal IRB::InputMethod::BASIC_WORD_BREAK_CHARACTERS, Reline.basic_word_break_characters
+ assert_not_nil Reline.completion_proc
+ assert_not_nil Reline.dig_perfect_match_proc
end
def test_initialization_without_use_autocomplete
@@ -34,7 +40,7 @@ module TestIRB
IRB.conf[:USE_AUTOCOMPLETE] = false
- IRB::RelineInputMethod.new
+ IRB::RelineInputMethod.new(IRB::RegexpCompletor.new)
refute Reline.autocompletion
assert_equal empty_proc, Reline.dialog_proc(:show_doc).dialog_proc
@@ -49,10 +55,10 @@ module TestIRB
IRB.conf[:USE_AUTOCOMPLETE] = true
- IRB::RelineInputMethod.new
+ IRB::RelineInputMethod.new(IRB::RegexpCompletor.new)
assert Reline.autocompletion
- assert_equal IRB::RelineInputMethod::SHOW_DOC_DIALOG, Reline.dialog_proc(:show_doc).dialog_proc
+ assert_not_equal empty_proc, Reline.dialog_proc(:show_doc).dialog_proc
ensure
Reline.add_dialog_proc(:show_doc, original_show_doc_proc, Reline::DEFAULT_DIALOG_CONTEXT)
end
@@ -65,7 +71,7 @@ module TestIRB
IRB.conf[:USE_AUTOCOMPLETE] = true
without_rdoc do
- IRB::RelineInputMethod.new
+ IRB::RelineInputMethod.new(IRB::RegexpCompletor.new)
end
assert Reline.autocompletion
@@ -75,5 +81,93 @@ module TestIRB
Reline.add_dialog_proc(:show_doc, original_show_doc_proc, Reline::DEFAULT_DIALOG_CONTEXT)
end
end
-end
+ class DisplayDocumentTest < InputMethodTest
+ def setup
+ super
+ @driver = RDoc::RI::Driver.new(use_stdout: true)
+ end
+
+ def display_document(target, bind, driver = nil)
+ input_method = IRB::RelineInputMethod.new(IRB::RegexpCompletor.new)
+ input_method.instance_variable_set(:@rdoc_ri_driver, driver) if driver
+ input_method.instance_variable_set(:@completion_params, ['', target, '', bind])
+ input_method.display_document(target)
+ end
+
+ def test_perfectly_matched_namespace_triggers_document_display
+ omit unless has_rdoc_content?
+
+ out, err = capture_output do
+ display_document("String", binding, @driver)
+ end
+
+ assert_empty(err)
+
+ assert_include(out, " S\bSt\btr\bri\bin\bng\bg")
+ end
+
+ def test_perfectly_matched_multiple_namespaces_triggers_document_display
+ result = nil
+ out, err = capture_output do
+ result = display_document("{}.nil?", binding, @driver)
+ end
+
+ assert_empty(err)
+
+ # check if there're rdoc contents (e.g. CI doesn't generate them)
+ if has_rdoc_content?
+ # if there's rdoc content, we can verify by checking stdout
+ # rdoc generates control characters for formatting method names
+ assert_include(out, "P\bPr\bro\boc\bc.\b.n\bni\bil\bl?\b?") # Proc.nil?
+ assert_include(out, "H\bHa\bas\bsh\bh.\b.n\bni\bil\bl?\b?") # Hash.nil?
+ else
+ # this is a hacky way to verify the rdoc rendering code path because CI doesn't have rdoc content
+ # if there are multiple namespaces to be rendered, PerfectMatchedProc renders the result with a document
+ # which always returns the bytes rendered, even if it's 0
+ assert_equal(0, result)
+ end
+ end
+
+ def test_not_matched_namespace_triggers_nothing
+ result = nil
+ out, err = capture_output do
+ result = display_document("Stri", binding, @driver)
+ end
+
+ assert_empty(err)
+ assert_empty(out)
+ assert_nil(result)
+ end
+
+ def test_perfect_matching_stops_without_rdoc
+ result = nil
+
+ out, err = capture_output do
+ without_rdoc do
+ result = display_document("String", binding)
+ end
+ end
+
+ assert_empty(err)
+ assert_not_match(/from ruby core/, out)
+ assert_nil(result)
+ end
+
+ def test_perfect_matching_handles_nil_namespace
+ out, err = capture_output do
+ # symbol literal has `nil` doc namespace so it's a good test subject
+ assert_nil(display_document(":aiueo", binding, @driver))
+ end
+
+ assert_empty(err)
+ assert_empty(out)
+ end
+
+ private
+
+ def has_rdoc_content?
+ File.exist?(RDoc::RI::Paths::BASE)
+ end
+ end
+end
diff --git a/test/irb/test_irb.rb b/test/irb/test_irb.rb
new file mode 100644
index 0000000000..b05fc80f77
--- /dev/null
+++ b/test/irb/test_irb.rb
@@ -0,0 +1,826 @@
+# frozen_string_literal: true
+require "irb"
+
+require_relative "helper"
+
+module TestIRB
+ class InputTest < IntegrationTestCase
+ def test_symbol_aliases_are_handled_correctly
+ write_ruby <<~'RUBY'
+ class Foo
+ end
+ binding.irb
+ RUBY
+
+ output = run_ruby_file do
+ type "$ Foo"
+ type "exit!"
+ end
+
+ assert_include output, "From: #{@ruby_file.path}:1"
+ end
+
+ def test_symbol_aliases_are_handled_correctly_with_singleline_mode
+ write_rc <<~RUBY
+ IRB.conf[:USE_SINGLELINE] = true
+ RUBY
+
+ write_ruby <<~'RUBY'
+ class Foo
+ end
+ binding.irb
+ RUBY
+
+ output = run_ruby_file do
+ type "irb_info"
+ type "$ Foo"
+ type "exit!"
+ end
+
+ # Make sure it's tested in singleline mode
+ assert_include output, "InputMethod: ReadlineInputMethod"
+ assert_include output, "From: #{@ruby_file.path}:1"
+ end
+
+ def test_underscore_stores_last_result
+ write_ruby <<~'RUBY'
+ binding.irb
+ RUBY
+
+ output = run_ruby_file do
+ type "1 + 1"
+ type "_ + 10"
+ type "exit!"
+ end
+
+ assert_include output, "=> 12"
+ end
+
+ def test_evaluate_with_encoding_error_without_lineno
+ if RUBY_ENGINE == 'truffleruby'
+ omit "Remove me after https://github.com/ruby/prism/issues/2129 is addressed and adopted in TruffleRuby"
+ end
+
+ if RUBY_VERSION >= "3.4."
+ omit "Now raises SyntaxError"
+ end
+
+ write_ruby <<~'RUBY'
+ binding.irb
+ RUBY
+
+ output = run_ruby_file do
+ type %q[:"\xAE"]
+ type "exit!"
+ end
+
+ assert_include output, 'invalid symbol in encoding UTF-8 :"\xAE"'
+ # EncodingError would be wrapped with ANSI escape sequences, so we assert it separately
+ assert_include output, "EncodingError"
+ end
+
+ def test_evaluate_still_emits_warning
+ write_ruby <<~'RUBY'
+ binding.irb
+ RUBY
+
+ output = run_ruby_file do
+ type %q[def foo; END {}; end]
+ type "exit!"
+ end
+
+ assert_include output, '(irb):1: warning: END in method; use at_exit'
+ end
+
+ def test_symbol_aliases_dont_affect_ruby_syntax
+ write_ruby <<~'RUBY'
+ $foo = "It's a foo"
+ @bar = "It's a bar"
+ binding.irb
+ RUBY
+
+ output = run_ruby_file do
+ type "$foo"
+ type "@bar"
+ type "exit!"
+ end
+
+ assert_include output, "=> \"It's a foo\""
+ assert_include output, "=> \"It's a bar\""
+ end
+
+ def test_empty_input_echoing_behaviour
+ write_ruby <<~'RUBY'
+ binding.irb
+ RUBY
+
+ output = run_ruby_file do
+ type ""
+ type " "
+ type "exit"
+ end
+
+ assert_not_match(/irb\(main\):001> (\r*\n)?=> nil/, output)
+ assert_match(/irb\(main\):002> (\r*\n)?=> nil/, output)
+ end
+ end
+
+ class NestedBindingIrbTest < IntegrationTestCase
+ def test_current_context_restore
+ write_ruby <<~'RUBY'
+ binding.irb
+ RUBY
+
+ output = run_ruby_file do
+ type '$ctx = IRB.CurrentContext'
+ type 'binding.irb'
+ type 'p context_changed: IRB.CurrentContext != $ctx'
+ type 'exit'
+ type 'p context_restored: IRB.CurrentContext == $ctx'
+ type 'exit'
+ end
+
+ assert_include output, '{:context_changed=>true}'
+ assert_include output, '{:context_restored=>true}'
+ end
+ end
+
+ class IrbIOConfigurationTest < TestCase
+ Row = Struct.new(:content, :current_line_spaces, :new_line_spaces, :indent_level)
+
+ class MockIO_AutoIndent
+ attr_reader :calculated_indent
+
+ def initialize(*params)
+ @params = params
+ end
+
+ def auto_indent(&block)
+ @calculated_indent = block.call(*@params)
+ end
+ end
+
+ class MockIO_DynamicPrompt
+ attr_reader :prompt_list
+
+ def initialize(params, &assertion)
+ @params = params
+ end
+
+ def dynamic_prompt(&block)
+ @prompt_list = block.call(@params)
+ end
+ end
+
+ def setup
+ save_encodings
+ @irb = build_irb
+ end
+
+ def teardown
+ restore_encodings
+ end
+
+ class AutoIndentationTest < IrbIOConfigurationTest
+ def test_auto_indent
+ input_with_correct_indents = [
+ [%q(def each_top_level_statement), 0, 2],
+ [%q( initialize_input), 2, 2],
+ [%q( catch(:TERM_INPUT) do), 2, 4],
+ [%q( loop do), 4, 6],
+ [%q( begin), 6, 8],
+ [%q( prompt), 8, 8],
+ [%q( unless l = lex), 8, 10],
+ [%q( throw :TERM_INPUT if @line == ''), 10, 10],
+ [%q( else), 8, 10],
+ [%q( @line_no += l.count("\n")), 10, 10],
+ [%q( next if l == "\n"), 10, 10],
+ [%q( @line.concat l), 10, 10],
+ [%q( if @code_block_open or @ltype or @continue or @indent > 0), 10, 12],
+ [%q( next), 12, 12],
+ [%q( end), 10, 10],
+ [%q( end), 8, 8],
+ [%q( if @line != "\n"), 8, 10],
+ [%q( @line.force_encoding(@io.encoding)), 10, 10],
+ [%q( yield @line, @exp_line_no), 10, 10],
+ [%q( end), 8, 8],
+ [%q( break if @io.eof?), 8, 8],
+ [%q( @line = ''), 8, 8],
+ [%q( @exp_line_no = @line_no), 8, 8],
+ [%q( ), nil, 8],
+ [%q( @indent = 0), 8, 8],
+ [%q( rescue TerminateLineInput), 6, 8],
+ [%q( initialize_input), 8, 8],
+ [%q( prompt), 8, 8],
+ [%q( end), 6, 6],
+ [%q( end), 4, 4],
+ [%q( end), 2, 2],
+ [%q(end), 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents)
+ end
+
+ def test_braces_on_their_own_line
+ input_with_correct_indents = [
+ [%q(if true), 0, 2],
+ [%q( [), 2, 4],
+ [%q( ]), 2, 2],
+ [%q(end), 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents)
+ end
+
+ def test_multiple_braces_in_a_line
+ input_with_correct_indents = [
+ [%q([[[), 0, 6],
+ [%q( ]), 4, 4],
+ [%q( ]), 2, 2],
+ [%q(]), 0, 0],
+ [%q([<<FOO]), 0, 0],
+ [%q(hello), 0, 0],
+ [%q(FOO), 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents)
+ end
+
+ def test_a_closed_brace_and_not_closed_brace_in_a_line
+ input_with_correct_indents = [
+ [%q(p() {), 0, 2],
+ [%q(}), 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents)
+ end
+
+ def test_symbols
+ input_with_correct_indents = [
+ [%q(:a), 0, 0],
+ [%q(:A), 0, 0],
+ [%q(:+), 0, 0],
+ [%q(:@@a), 0, 0],
+ [%q(:@a), 0, 0],
+ [%q(:$a), 0, 0],
+ [%q(:def), 0, 0],
+ [%q(:`), 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents)
+ end
+
+ def test_incomplete_coding_magic_comment
+ input_with_correct_indents = [
+ [%q(#coding:u), 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents)
+ end
+
+ def test_incomplete_encoding_magic_comment
+ input_with_correct_indents = [
+ [%q(#encoding:u), 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents)
+ end
+
+ def test_incomplete_emacs_coding_magic_comment
+ input_with_correct_indents = [
+ [%q(# -*- coding: u), 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents)
+ end
+
+ def test_incomplete_vim_coding_magic_comment
+ input_with_correct_indents = [
+ [%q(# vim:set fileencoding=u), 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents)
+ end
+
+ def test_mixed_rescue
+ input_with_correct_indents = [
+ [%q(def m), 0, 2],
+ [%q( begin), 2, 4],
+ [%q( begin), 4, 6],
+ [%q( x = a rescue 4), 6, 6],
+ [%q( y = [(a rescue 5)]), 6, 6],
+ [%q( [x, y]), 6, 6],
+ [%q( rescue => e), 4, 6],
+ [%q( raise e rescue 8), 6, 6],
+ [%q( end), 4, 4],
+ [%q( rescue), 2, 4],
+ [%q( raise rescue 11), 4, 4],
+ [%q( end), 2, 2],
+ [%q(rescue => e), 0, 2],
+ [%q( raise e rescue 14), 2, 2],
+ [%q(end), 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents)
+ end
+
+ def test_oneliner_method_definition
+ input_with_correct_indents = [
+ [%q(class A), 0, 2],
+ [%q( def foo0), 2, 4],
+ [%q( 3), 4, 4],
+ [%q( end), 2, 2],
+ [%q( def foo1()), 2, 4],
+ [%q( 3), 4, 4],
+ [%q( end), 2, 2],
+ [%q( def foo2(a, b)), 2, 4],
+ [%q( a + b), 4, 4],
+ [%q( end), 2, 2],
+ [%q( def foo3 a, b), 2, 4],
+ [%q( a + b), 4, 4],
+ [%q( end), 2, 2],
+ [%q( def bar0() = 3), 2, 2],
+ [%q( def bar1(a) = a), 2, 2],
+ [%q( def bar2(a, b) = a + b), 2, 2],
+ [%q( def bar3() = :s), 2, 2],
+ [%q( def bar4() = Time.now), 2, 2],
+ [%q(end), 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents)
+ end
+
+ def test_tlambda
+ input_with_correct_indents = [
+ [%q(if true), 0, 2, 1],
+ [%q( -> {), 2, 4, 2],
+ [%q( }), 2, 2, 1],
+ [%q(end), 0, 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents, assert_indent_level: true)
+ end
+
+ def test_corresponding_syntax_to_keyword_do_in_class
+ input_with_correct_indents = [
+ [%q(class C), 0, 2, 1],
+ [%q( while method_name do), 2, 4, 2],
+ [%q( 3), 4, 4, 2],
+ [%q( end), 2, 2, 1],
+ [%q( foo do), 2, 4, 2],
+ [%q( 3), 4, 4, 2],
+ [%q( end), 2, 2, 1],
+ [%q(end), 0, 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents, assert_indent_level: true)
+ end
+
+ def test_corresponding_syntax_to_keyword_do
+ input_with_correct_indents = [
+ [%q(while i > 0), 0, 2, 1],
+ [%q( 3), 2, 2, 1],
+ [%q(end), 0, 0, 0],
+ [%q(while true), 0, 2, 1],
+ [%q( 3), 2, 2, 1],
+ [%q(end), 0, 0, 0],
+ [%q(while ->{i > 0}.call), 0, 2, 1],
+ [%q( 3), 2, 2, 1],
+ [%q(end), 0, 0, 0],
+ [%q(while ->{true}.call), 0, 2, 1],
+ [%q( 3), 2, 2, 1],
+ [%q(end), 0, 0, 0],
+ [%q(while i > 0 do), 0, 2, 1],
+ [%q( 3), 2, 2, 1],
+ [%q(end), 0, 0, 0],
+ [%q(while true do), 0, 2, 1],
+ [%q( 3), 2, 2, 1],
+ [%q(end), 0, 0, 0],
+ [%q(while ->{i > 0}.call do), 0, 2, 1],
+ [%q( 3), 2, 2, 1],
+ [%q(end), 0, 0, 0],
+ [%q(while ->{true}.call do), 0, 2, 1],
+ [%q( 3), 2, 2, 1],
+ [%q(end), 0, 0, 0],
+ [%q(foo do), 0, 2, 1],
+ [%q( 3), 2, 2, 1],
+ [%q(end), 0, 0, 0],
+ [%q(foo true do), 0, 2, 1],
+ [%q( 3), 2, 2, 1],
+ [%q(end), 0, 0, 0],
+ [%q(foo ->{true} do), 0, 2, 1],
+ [%q( 3), 2, 2, 1],
+ [%q(end), 0, 0, 0],
+ [%q(foo ->{i > 0} do), 0, 2, 1],
+ [%q( 3), 2, 2, 1],
+ [%q(end), 0, 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents, assert_indent_level: true)
+ end
+
+ def test_corresponding_syntax_to_keyword_for
+ input_with_correct_indents = [
+ [%q(for i in [1]), 0, 2, 1],
+ [%q( puts i), 2, 2, 1],
+ [%q(end), 0, 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents, assert_indent_level: true)
+ end
+
+ def test_corresponding_syntax_to_keyword_for_with_do
+ input_with_correct_indents = [
+ [%q(for i in [1] do), 0, 2, 1],
+ [%q( puts i), 2, 2, 1],
+ [%q(end), 0, 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents, assert_indent_level: true)
+ end
+
+ def test_typing_incomplete_include_interpreted_as_keyword_in
+ input_with_correct_indents = [
+ [%q(module E), 0, 2, 1],
+ [%q(end), 0, 0, 0],
+ [%q(class A), 0, 2, 1],
+ [%q( in), 2, 2, 1] # scenario typing `include E`
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents, assert_indent_level: true)
+
+ end
+
+ def test_bracket_corresponding_to_times
+ input_with_correct_indents = [
+ [%q(3.times { |i|), 0, 2, 1],
+ [%q( puts i), 2, 2, 1],
+ [%q(}), 0, 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents, assert_indent_level: true)
+ end
+
+ def test_do_corresponding_to_times
+ input_with_correct_indents = [
+ [%q(3.times do |i|), 0, 2, 1],
+ [%q( puts i), 2, 2, 1],
+ [%q(end), 0, 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents, assert_indent_level: true)
+ end
+
+ def test_bracket_corresponding_to_loop
+ input_with_correct_indents = [
+ ['loop {', 0, 2, 1],
+ [' 3', 2, 2, 1],
+ ['}', 0, 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents, assert_indent_level: true)
+ end
+
+ def test_do_corresponding_to_loop
+ input_with_correct_indents = [
+ [%q(loop do), 0, 2, 1],
+ [%q( 3), 2, 2, 1],
+ [%q(end), 0, 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents, assert_indent_level: true)
+ end
+
+ def test_embdoc_indent
+ input_with_correct_indents = [
+ [%q(=begin), 0, 0, 0],
+ [%q(a), 0, 0, 0],
+ [%q( b), 1, 1, 0],
+ [%q(=end), 0, 0, 0],
+ [%q(if 1), 0, 2, 1],
+ [%q( 2), 2, 2, 1],
+ [%q(=begin), 0, 0, 0],
+ [%q(a), 0, 0, 0],
+ [%q( b), 1, 1, 0],
+ [%q(=end), 0, 2, 1],
+ [%q( 3), 2, 2, 1],
+ [%q(end), 0, 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents, assert_indent_level: true)
+ end
+
+ def test_heredoc_with_indent
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.7.0')
+ pend 'This test needs Ripper::Lexer#scan to take broken tokens'
+ end
+ input_with_correct_indents = [
+ [%q(<<~Q+<<~R), 0, 2, 1],
+ [%q(a), 2, 2, 1],
+ [%q(a), 2, 2, 1],
+ [%q( b), 2, 2, 1],
+ [%q( b), 2, 2, 1],
+ [%q( Q), 0, 2, 1],
+ [%q( c), 4, 4, 1],
+ [%q( c), 4, 4, 1],
+ [%q( R), 0, 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents, assert_indent_level: true)
+ end
+
+ def test_oneliner_def_in_multiple_lines
+ input_with_correct_indents = [
+ [%q(def a()=[), 0, 2, 1],
+ [%q( 1,), 2, 2, 1],
+ [%q(].), 0, 0, 0],
+ [%q(to_s), 0, 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents, assert_indent_level: true)
+ end
+
+ def test_broken_heredoc
+ input_with_correct_indents = [
+ [%q(def foo), 0, 2, 1],
+ [%q( <<~Q), 2, 4, 2],
+ [%q( Qend), 4, 4, 2],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents, assert_indent_level: true)
+ end
+
+ def test_pasted_code_keep_base_indent_spaces
+ input_with_correct_indents = [
+ [%q( def foo), 0, 6, 1],
+ [%q( if bar), 6, 10, 2],
+ [%q( [1), 10, 12, 3],
+ [%q( ]+[["a), 10, 14, 4],
+ [%q(b" + `c), 0, 14, 4],
+ [%q(d` + /e), 0, 14, 4],
+ [%q(f/ + :"g), 0, 14, 4],
+ [%q(h".tap do), 0, 16, 5],
+ [%q( 1), 16, 16, 5],
+ [%q( end), 14, 14, 4],
+ [%q( ]), 12, 12, 3],
+ [%q( ]), 10, 10, 2],
+ [%q( end), 8, 6, 1],
+ [%q( end), 4, 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents, assert_indent_level: true)
+ end
+
+ def test_pasted_code_keep_base_indent_spaces_with_heredoc
+ input_with_correct_indents = [
+ [%q( def foo), 0, 6, 1],
+ [%q( if bar), 6, 10, 2],
+ [%q( [1), 10, 12, 3],
+ [%q( ]+[["a), 10, 14, 4],
+ [%q(b" + <<~A + <<-B + <<C), 0, 16, 5],
+ [%q( a#{), 16, 18, 6],
+ [%q( 1), 18, 18, 6],
+ [%q( }), 16, 16, 5],
+ [%q( A), 14, 16, 5],
+ [%q( b#{), 16, 18, 6],
+ [%q( 1), 18, 18, 6],
+ [%q( }), 16, 16, 5],
+ [%q( B), 14, 0, 0],
+ [%q(c#{), 0, 2, 1],
+ [%q(1), 2, 2, 1],
+ [%q(}), 0, 0, 0],
+ [%q(C), 0, 14, 4],
+ [%q( ]), 12, 12, 3],
+ [%q( ]), 10, 10, 2],
+ [%q( end), 8, 6, 1],
+ [%q( end), 4, 0, 0],
+ ]
+
+ assert_rows_with_correct_indents(input_with_correct_indents, assert_indent_level: true)
+ end
+
+ def test_heredoc_keep_indent_spaces
+ (1..4).each do |indent|
+ row = Row.new(' ' * indent, nil, [4, indent].max, 2)
+ lines = ['def foo', ' <<~Q', row.content]
+ assert_row_indenting(lines, row)
+ assert_indent_level(lines, row.indent_level)
+ end
+ end
+
+ private
+
+ def assert_row_indenting(lines, row)
+ actual_current_line_spaces = calculate_indenting(lines, false)
+
+ error_message = <<~MSG
+ Incorrect spaces calculation for line:
+
+ ```
+ > #{lines.last}
+ ```
+
+ All lines:
+
+ ```
+ #{lines.join("\n")}
+ ```
+ MSG
+ assert_equal(row.current_line_spaces, actual_current_line_spaces, error_message)
+
+ error_message = <<~MSG
+ Incorrect spaces calculation for line after the current line:
+
+ ```
+ #{lines.last}
+ >
+ ```
+
+ All lines:
+
+ ```
+ #{lines.join("\n")}
+ ```
+ MSG
+ actual_next_line_spaces = calculate_indenting(lines, true)
+ assert_equal(row.new_line_spaces, actual_next_line_spaces, error_message)
+ end
+
+ def assert_rows_with_correct_indents(rows_with_spaces, assert_indent_level: false)
+ lines = []
+ rows_with_spaces.map do |row|
+ row = Row.new(*row)
+ lines << row.content
+ assert_row_indenting(lines, row)
+
+ if assert_indent_level
+ assert_indent_level(lines, row.indent_level)
+ end
+ end
+ end
+
+ def assert_indent_level(lines, expected)
+ code = lines.map { |l| "#{l}\n" }.join # code should end with "\n"
+ _tokens, opens, _ = @irb.scanner.check_code_state(code, local_variables: [])
+ indent_level = @irb.scanner.calc_indent_level(opens)
+ error_message = "Calculated the wrong number of indent level for:\n #{lines.join("\n")}"
+ assert_equal(expected, indent_level, error_message)
+ end
+
+ def calculate_indenting(lines, add_new_line)
+ lines = lines + [""] if add_new_line
+ last_line_index = lines.length - 1
+ byte_pointer = lines.last.length
+
+ mock_io = MockIO_AutoIndent.new(lines, last_line_index, byte_pointer, add_new_line)
+ @irb.context.auto_indent_mode = true
+ @irb.context.io = mock_io
+ @irb.configure_io
+
+ mock_io.calculated_indent
+ end
+ end
+
+ class DynamicPromptTest < IrbIOConfigurationTest
+ def test_endless_range_at_end_of_line
+ input_with_prompt = [
+ ['001:0: :> ', %q(a = 3..)],
+ ['002:0: :> ', %q()],
+ ]
+
+ assert_dynamic_prompt(input_with_prompt)
+ end
+
+ def test_heredoc_with_embexpr
+ input_with_prompt = [
+ ['001:0:":* ', %q(<<A+%W[#{<<B)],
+ ['002:0:":* ', %q(#{<<C+%W[)],
+ ['003:0:":* ', %q(a)],
+ ['004:2:]:* ', %q(C)],
+ ['005:2:]:* ', %q(a)],
+ ['006:0:":* ', %q(]})],
+ ['007:0:":* ', %q(})],
+ ['008:0:":* ', %q(A)],
+ ['009:2:]:* ', %q(B)],
+ ['010:1:]:* ', %q(})],
+ ['011:0: :> ', %q(])],
+ ['012:0: :> ', %q()],
+ ]
+
+ assert_dynamic_prompt(input_with_prompt)
+ end
+
+ def test_heredoc_prompt_with_quotes
+ input_with_prompt = [
+ ["001:1:':* ", %q(<<~'A')],
+ ["002:1:':* ", %q(#{foobar})],
+ ["003:0: :> ", %q(A)],
+ ["004:1:`:* ", %q(<<~`A`)],
+ ["005:1:`:* ", %q(whoami)],
+ ["006:0: :> ", %q(A)],
+ ['007:1:":* ', %q(<<~"A")],
+ ['008:1:":* ', %q(foobar)],
+ ['009:0: :> ', %q(A)],
+ ]
+
+ assert_dynamic_prompt(input_with_prompt)
+ end
+
+ def test_backtick_method
+ input_with_prompt = [
+ ['001:0: :> ', %q(self.`(arg))],
+ ['002:0: :> ', %q()],
+ ['003:0: :> ', %q(def `(); end)],
+ ['004:0: :> ', %q()],
+ ]
+
+ assert_dynamic_prompt(input_with_prompt)
+ end
+
+ def test_dynamic_prompt
+ input_with_prompt = [
+ ['001:1: :* ', %q(def hoge)],
+ ['002:1: :* ', %q( 3)],
+ ['003:0: :> ', %q(end)],
+ ]
+
+ assert_dynamic_prompt(input_with_prompt)
+ end
+
+ def test_dynamic_prompt_with_double_newline_breaking_code
+ input_with_prompt = [
+ ['001:1: :* ', %q(if true)],
+ ['002:2: :* ', %q(%)],
+ ['003:1: :* ', %q(;end)],
+ ['004:1: :* ', %q(;hello)],
+ ['005:0: :> ', %q(end)],
+ ]
+
+ assert_dynamic_prompt(input_with_prompt)
+ end
+
+ def test_dynamic_prompt_with_multiline_literal
+ input_with_prompt = [
+ ['001:1: :* ', %q(if true)],
+ ['002:2:]:* ', %q( %w[)],
+ ['003:2:]:* ', %q( a)],
+ ['004:1: :* ', %q( ])],
+ ['005:1: :* ', %q( b)],
+ ['006:2:]:* ', %q( %w[)],
+ ['007:2:]:* ', %q( c)],
+ ['008:1: :* ', %q( ])],
+ ['009:0: :> ', %q(end)],
+ ]
+
+ assert_dynamic_prompt(input_with_prompt)
+ end
+
+ def test_dynamic_prompt_with_blank_line
+ input_with_prompt = [
+ ['001:1:]:* ', %q(%w[)],
+ ['002:1:]:* ', %q()],
+ ['003:0: :> ', %q(])],
+ ]
+
+ assert_dynamic_prompt(input_with_prompt)
+ end
+
+ def assert_dynamic_prompt(input_with_prompt)
+ expected_prompt_list, lines = input_with_prompt.transpose
+ def @irb.generate_prompt(opens, continue, line_offset)
+ ltype = @scanner.ltype_from_open_tokens(opens)
+ indent = @scanner.calc_indent_level(opens)
+ continue = opens.any? || continue
+ line_no = @line_no + line_offset
+ '%03d:%01d:%1s:%s ' % [line_no, indent, ltype, continue ? '*' : '>']
+ end
+ io = MockIO_DynamicPrompt.new(lines)
+ @irb.context.io = io
+ @irb.configure_io
+
+ error_message = <<~EOM
+ Expected dynamic prompt:
+ #{expected_prompt_list.join("\n")}
+
+ Actual dynamic prompt:
+ #{io.prompt_list.join("\n")}
+ EOM
+ assert_equal(expected_prompt_list, io.prompt_list, error_message)
+ end
+ end
+
+ private
+
+ def build_binding
+ Object.new.instance_eval { binding }
+ end
+
+ def build_irb
+ IRB.init_config(nil)
+ workspace = IRB::WorkSpace.new(build_binding)
+
+ IRB.conf[:VERBOSE] = false
+ IRB::Irb.new(workspace, TestInputMethod.new)
+ end
+ end
+end
diff --git a/test/irb/test_nesting_parser.rb b/test/irb/test_nesting_parser.rb
new file mode 100644
index 0000000000..2482d40081
--- /dev/null
+++ b/test/irb/test_nesting_parser.rb
@@ -0,0 +1,341 @@
+# frozen_string_literal: false
+require 'irb'
+
+require_relative "helper"
+
+module TestIRB
+ class NestingParserTest < TestCase
+ def setup
+ save_encodings
+ end
+
+ def teardown
+ restore_encodings
+ end
+
+ def parse_by_line(code)
+ IRB::NestingParser.parse_by_line(IRB::RubyLex.ripper_lex_without_warning(code))
+ end
+
+ def test_open_tokens
+ code = <<~'EOS'
+ class A
+ def f
+ if true
+ tap do
+ {
+ x: "
+ #{p(1, 2, 3
+ EOS
+ opens = IRB::NestingParser.open_tokens(IRB::RubyLex.ripper_lex_without_warning(code))
+ assert_equal(%w[class def if do { " #{ (], opens.map(&:tok))
+ end
+
+ def test_parse_by_line
+ code = <<~EOS
+ (((((1+2
+ ).to_s())).tap do (((
+ EOS
+ _tokens, prev_opens, next_opens, min_depth = parse_by_line(code).last
+ assert_equal(%w[( ( ( ( (], prev_opens.map(&:tok))
+ assert_equal(%w[( ( do ( ( (], next_opens.map(&:tok))
+ assert_equal(2, min_depth)
+ end
+
+ def test_ruby_syntax
+ code = <<~'EOS'
+ class A
+ 1 if 2
+ 1 while 2
+ 1 until 2
+ 1 unless 2
+ 1 rescue 2
+ begin; rescue; ensure; end
+ tap do; rescue; ensure; end
+ class B; end
+ module C; end
+ def f; end
+ def `; end
+ def f() = 1
+ %(); %w[]; %q(); %r{}; %i[]
+ "#{1}"; ''; /#{1}/; `#{1}`
+ :sym; :"sym"; :+; :`; :if
+ [1, 2, 3]
+ { x: 1, y: 2 }
+ (a, (*b, c), d), e = 1, 2, 3
+ ->(a){}; ->(a) do end
+ -> a = -> b = :do do end do end
+ if 1; elsif 2; else; end
+ unless 1; end
+ while 1; end
+ until 1; end
+ for i in j; end
+ case 1; when 2; end
+ puts(1, 2, 3)
+ loop{|i|}
+ loop do |i| end
+ end
+ EOS
+ line_results = parse_by_line(code)
+ assert_equal(code.lines.size, line_results.size)
+ class_open, *inner_line_results, class_close = line_results
+ assert_equal(['class'], class_open[2].map(&:tok))
+ inner_line_results.each {|result| assert_equal(['class'], result[2].map(&:tok)) }
+ assert_equal([], class_close[2].map(&:tok))
+ end
+
+ def test_multiline_string
+ code = <<~EOS
+ "
+ aaa
+ bbb
+ "
+ <<A
+ aaa
+ bbb
+ A
+ EOS
+ line_results = parse_by_line(code)
+ assert_equal(code.lines.size, line_results.size)
+ string_content_line, string_opens = line_results[1]
+ assert_equal("\naaa\nbbb\n", string_content_line.first.first.tok)
+ assert_equal("aaa\n", string_content_line.first.last)
+ assert_equal(['"'], string_opens.map(&:tok))
+ heredoc_content_line, heredoc_opens = line_results[6]
+ assert_equal("aaa\nbbb\n", heredoc_content_line.first.first.tok)
+ assert_equal("bbb\n", heredoc_content_line.first.last)
+ assert_equal(['<<A'], heredoc_opens.map(&:tok))
+ _line, _prev_opens, next_opens, _min_depth = line_results.last
+ assert_equal([], next_opens)
+ end
+
+ def test_backslash_continued_nested_symbol
+ code = <<~'EOS'
+ x = <<A, :\
+ heredoc #{
+ here
+ }
+ A
+ =begin
+ embdoc
+ =end
+ # comment
+
+ if # this is symbol :if
+ while
+ EOS
+ line_results = parse_by_line(code)
+ assert_equal(%w[: <<A #{], line_results[2][2].map(&:tok))
+ assert_equal(%w[while], line_results.last[2].map(&:tok))
+ end
+
+ def test_oneliner_def
+ code = <<~EOC
+ if true
+ # normal oneliner def
+ def f = 1
+ def f() = 1
+ def f(*) = 1
+ # keyword, backtick, op
+ def * = 1
+ def ` = 1
+ def if = 1
+ def *() = 1
+ def `() = 1
+ def if() = 1
+ # oneliner def with receiver
+ def a.* = 1
+ def $a.* = 1
+ def @a.` = 1
+ def A.` = 1
+ def ((a;b;c)).*() = 1
+ def ((a;b;c)).if() = 1
+ def ((a;b;c)).end() = 1
+ # multiline oneliner def
+ def f =
+ 1
+ def f()
+ =
+ 1
+ # oneliner def with comment and embdoc
+ def # comment
+ =begin
+ embdoc
+ =end
+ ((a;b;c))
+ . # comment
+ =begin
+ embdoc
+ =end
+ f (*) # comment
+ =begin
+ embdoc
+ =end
+ =
+ 1
+ # nested oneliner def
+ def f(x = def f() = 1) = def f() = 1
+ EOC
+ _tokens, _prev_opens, next_opens, min_depth = parse_by_line(code).last
+ assert_equal(['if'], next_opens.map(&:tok))
+ assert_equal(1, min_depth)
+ end
+
+ def test_heredoc_embexpr
+ code = <<~'EOS'
+ <<A+<<B+<<C+(<<D+(<<E)
+ #{
+ <<~F+"#{<<~G}
+ #{
+ here
+ }
+ F
+ G
+ "
+ }
+ A
+ B
+ C
+ D
+ E
+ )
+ EOS
+ line_results = parse_by_line(code)
+ last_opens = line_results.last[-2]
+ assert_equal([], last_opens)
+ _tokens, _prev_opens, next_opens, _min_depth = line_results[4]
+ assert_equal(%w[( <<E <<D <<C <<B <<A #{ " <<~G <<~F #{], next_opens.map(&:tok))
+ end
+
+ def test_for_in
+ code = <<~EOS
+ for i in j
+ here
+ end
+ for i in j do
+ here
+ end
+ for i in
+ j do
+ here
+ end
+ for
+ # comment
+ i in j do
+ here
+ end
+ for (a;b;c).d in (a;b;c) do
+ here
+ end
+ for i in :in + :do do
+ here
+ end
+ for i in -> do end do
+ here
+ end
+ EOS
+ line_results = parse_by_line(code).select { |tokens,| tokens.map(&:last).include?('here') }
+ assert_equal(7, line_results.size)
+ line_results.each do |_tokens, _prev_opens, next_opens, _min_depth|
+ assert_equal(['for'], next_opens.map(&:tok))
+ end
+ end
+
+ def test_while_until
+ base_code = <<~'EOS'
+ while_or_until true
+ here
+ end
+ while_or_until a < c
+ here
+ end
+ while_or_until true do
+ here
+ end
+ while_or_until
+ # comment
+ (a + b) <
+ # comment
+ c do
+ here
+ end
+ while_or_until :\
+ do do
+ here
+ end
+ while_or_until def do; end == :do do
+ here
+ end
+ while_or_until -> do end do
+ here
+ end
+ EOS
+ %w[while until].each do |keyword|
+ code = base_code.gsub('while_or_until', keyword)
+ line_results = parse_by_line(code).select { |tokens,| tokens.map(&:last).include?('here') }
+ assert_equal(7, line_results.size)
+ line_results.each do |_tokens, _prev_opens, next_opens, _min_depth|
+ assert_equal([keyword], next_opens.map(&:tok) )
+ end
+ end
+ end
+
+ def test_undef_alias
+ codes = [
+ 'undef foo',
+ 'alias foo bar',
+ 'undef !',
+ 'alias + -',
+ 'alias $a $b',
+ 'undef do',
+ 'alias do do',
+ 'undef :do',
+ 'alias :do :do',
+ 'undef :"#{alias do do}"',
+ 'alias :"#{undef do}" do',
+ 'alias do :"#{undef do}"'
+ ]
+ code_with_comment = <<~EOS
+ undef #
+ #
+ do #
+ alias #
+ #
+ do #
+ #
+ do #
+ EOS
+ code_with_heredoc = <<~EOS
+ <<~A; alias
+ A
+ :"#{<<~A}"
+ A
+ do
+ EOS
+ [*codes, code_with_comment, code_with_heredoc].each do |code|
+ opens = IRB::NestingParser.open_tokens(IRB::RubyLex.ripper_lex_without_warning('(' + code + "\nif"))
+ assert_equal(%w[( if], opens.map(&:tok))
+ end
+ end
+
+ def test_case_in
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.7.0')
+ pend 'This test requires ruby version that supports case-in syntax'
+ end
+ code = <<~EOS
+ case 1
+ in 1
+ here
+ in
+ 2
+ here
+ end
+ EOS
+ line_results = parse_by_line(code).select { |tokens,| tokens.map(&:last).include?('here') }
+ assert_equal(2, line_results.size)
+ line_results.each do |_tokens, _prev_opens, next_opens, _min_depth|
+ assert_equal(['in'], next_opens.map(&:tok))
+ end
+ end
+ end
+end
diff --git a/test/irb/test_option.rb b/test/irb/test_option.rb
index e334cee6dd..fec31f384f 100644
--- a/test/irb/test_option.rb
+++ b/test/irb/test_option.rb
@@ -2,7 +2,7 @@
require_relative "helper"
module TestIRB
- class TestOption < TestCase
+ class OptionTest < TestCase
def test_end_of_option
bug4117 = '[ruby-core:33574]'
bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : []
diff --git a/test/irb/test_raise_exception.rb b/test/irb/test_raise_exception.rb
new file mode 100644
index 0000000000..44a5ae87e1
--- /dev/null
+++ b/test/irb/test_raise_exception.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: false
+require "tmpdir"
+
+require_relative "helper"
+
+module TestIRB
+ class RaiseExceptionTest < TestCase
+ def test_raise_exception_with_nil_backtrace
+ bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : []
+ assert_in_out_err(bundle_exec + %w[-rirb -W0 -e IRB.start(__FILE__) -- -f --], <<-IRB, /#<Exception: foo>/, [])
+ raise Exception.new("foo").tap {|e| def e.backtrace; nil; end }
+IRB
+ end
+
+ def test_raise_exception_with_message_exception
+ bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : []
+ expected = /#<Exception: foo>\nbacktraces are hidden because bar was raised when processing them/
+ assert_in_out_err(bundle_exec + %w[-rirb -W0 -e IRB.start(__FILE__) -- -f --], <<-IRB, expected, [])
+ e = Exception.new("foo")
+ def e.message; raise 'bar'; end
+ raise e
+IRB
+ end
+
+ def test_raise_exception_with_message_inspect_exception
+ bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : []
+ expected = /Uninspectable exception occurred/
+ assert_in_out_err(bundle_exec + %w[-rirb -W0 -e IRB.start(__FILE__) -- -f --], <<-IRB, expected, [])
+ e = Exception.new("foo")
+ def e.message; raise; end
+ def e.inspect; raise; end
+ raise e
+IRB
+ end
+
+ def test_raise_exception_with_invalid_byte_sequence
+ pend if RUBY_ENGINE == 'truffleruby' || /mswin|mingw/ =~ RUBY_PLATFORM
+ bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : []
+ assert_in_out_err(bundle_exec + %w[-rirb -W0 -e IRB.start(__FILE__) -- -f --], <<~IRB, /A\\xF3B \(StandardError\)/, [])
+ raise StandardError, "A\\xf3B"
+ IRB
+ end
+
+ def test_raise_exception_with_different_encoding_containing_invalid_byte_sequence
+ backup_home = ENV["HOME"]
+ Dir.mktmpdir("test_irb_raise_no_backtrace_exception_#{$$}") do |tmpdir|
+ ENV["HOME"] = tmpdir
+
+ bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : []
+ File.open("#{tmpdir}/euc.rb", 'w') do |f|
+ f.write(<<~EOF)
+ # encoding: euc-jp
+
+ def raise_euc_with_invalid_byte_sequence
+ raise "\xA4\xA2\\xFF"
+ end
+ EOF
+ end
+ env = {}
+ %w(LC_MESSAGES LC_ALL LC_CTYPE LANG).each {|n| env[n] = "ja_JP.UTF-8" }
+ # TruffleRuby warns when the locale does not exist
+ env['TRUFFLERUBYOPT'] = "#{ENV['TRUFFLERUBYOPT']} --log.level=SEVERE" if RUBY_ENGINE == 'truffleruby'
+ args = [env] + bundle_exec + %W[-rirb -C #{tmpdir} -W0 -e IRB.start(__FILE__) -- -f --]
+ error = /raise_euc_with_invalid_byte_sequence': ã‚\\xFF \(RuntimeError\)/
+ assert_in_out_err(args, <<~IRB, error, [], encoding: "UTF-8")
+ require_relative 'euc'
+ raise_euc_with_invalid_byte_sequence
+ IRB
+ end
+ ensure
+ ENV["HOME"] = backup_home
+ end
+ end
+end
diff --git a/test/irb/test_raise_no_backtrace_exception.rb b/test/irb/test_raise_no_backtrace_exception.rb
deleted file mode 100644
index 9565419cdd..0000000000
--- a/test/irb/test_raise_no_backtrace_exception.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-# frozen_string_literal: false
-require "tmpdir"
-
-require_relative "helper"
-
-module TestIRB
- class TestRaiseNoBacktraceException < TestCase
- def test_raise_exception
- bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : []
- assert_in_out_err(bundle_exec + %w[-rirb -W0 -e IRB.start(__FILE__) -- -f --], <<-IRB, /Exception: foo/, [])
- e = Exception.new("foo")
- puts e.inspect
- def e.backtrace; nil; end
- raise e
-IRB
- end
-
- def test_raise_exception_with_invalid_byte_sequence
- pend if RUBY_ENGINE == 'truffleruby' || /mswin|mingw/ =~ RUBY_PLATFORM
- bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : []
- assert_in_out_err(bundle_exec + %w[-rirb -W0 -e IRB.start(__FILE__) -- -f --], <<~IRB, /A\\xF3B \(StandardError\)/, [])
- raise StandardError, "A\\xf3B"
- IRB
- end
-
- def test_raise_exception_with_different_encoding_containing_invalid_byte_sequence
- backup_home = ENV["HOME"]
- Dir.mktmpdir("test_irb_raise_no_backtrace_exception_#{$$}") do |tmpdir|
- ENV["HOME"] = tmpdir
-
- bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : []
- File.open("#{tmpdir}/euc.rb", 'w') do |f|
- f.write(<<~EOF)
- # encoding: euc-jp
-
- def raise_euc_with_invalid_byte_sequence
- raise "\xA4\xA2\\xFF"
- end
- EOF
- end
- env = {}
- %w(LC_MESSAGES LC_ALL LC_CTYPE LANG).each {|n| env[n] = "ja_JP.UTF-8" }
- # TruffleRuby warns when the locale does not exist
- env['TRUFFLERUBYOPT'] = "#{ENV['TRUFFLERUBYOPT']} --log.level=SEVERE" if RUBY_ENGINE == 'truffleruby'
- args = [env] + bundle_exec + %W[-rirb -C #{tmpdir} -W0 -e IRB.start(__FILE__) -- -f --]
- error = /`raise_euc_with_invalid_byte_sequence': ã‚\\xFF \(RuntimeError\)/
- assert_in_out_err(args, <<~IRB, error, [], encoding: "UTF-8")
- require_relative 'euc'
- raise_euc_with_invalid_byte_sequence
- IRB
- end
- ensure
- ENV["HOME"] = backup_home
- end
- end
-end
diff --git a/test/irb/test_ruby_lex.rb b/test/irb/test_ruby_lex.rb
index 1530a16d6a..4e406a8ce0 100644
--- a/test/irb/test_ruby_lex.rb
+++ b/test/irb/test_ruby_lex.rb
@@ -1,27 +1,10 @@
-$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
-require 'irb'
-require 'rubygems'
-require 'ostruct'
+# frozen_string_literal: true
+require "irb"
require_relative "helper"
module TestIRB
- class TestRubyLex < TestCase
- Row = Struct.new(:content, :current_line_spaces, :new_line_spaces, :nesting_level)
-
- class MockIO_AutoIndent
- attr_reader :calculated_indent
-
- def initialize(*params)
- @params = params
- @calculated_indent
- end
-
- def auto_indent(&block)
- @calculated_indent = block.call(*@params)
- end
- end
-
+ class RubyLexTest < TestCase
def setup
save_encodings
end
@@ -30,692 +13,77 @@ module TestIRB
restore_encodings
end
- def calculate_indenting(lines, add_new_line)
- lines = lines + [""] if add_new_line
- last_line_index = lines.length - 1
- byte_pointer = lines.last.length
-
- context = build_context
- context.auto_indent_mode = true
-
- ruby_lex = RubyLex.new(context)
- mock_io = MockIO_AutoIndent.new(lines, last_line_index, byte_pointer, add_new_line)
-
- ruby_lex.configure_io(mock_io)
- mock_io.calculated_indent
- end
-
- def assert_row_indenting(lines, row)
- actual_current_line_spaces = calculate_indenting(lines, false)
-
- error_message = <<~MSG
- Incorrect spaces calculation for line:
-
- ```
- > #{lines.last}
- ```
-
- All lines:
-
- ```
- #{lines.join("\n")}
- ```
- MSG
- assert_equal(row.current_line_spaces, actual_current_line_spaces, error_message)
-
- error_message = <<~MSG
- Incorrect spaces calculation for line after the current line:
-
- ```
- #{lines.last}
- >
- ```
-
- All lines:
-
- ```
- #{lines.join("\n")}
- ```
- MSG
- actual_next_line_spaces = calculate_indenting(lines, true)
- assert_equal(row.new_line_spaces, actual_next_line_spaces, error_message)
- end
-
- def assert_nesting_level(lines, expected, local_variables: [])
- indent, _code_block_open = check_state(lines, local_variables: local_variables)
- error_message = "Calculated the wrong number of nesting level for:\n #{lines.join("\n")}"
- assert_equal(expected, indent, error_message)
- end
-
- def assert_code_block_open(lines, expected, local_variables: [])
- _indent, code_block_open = check_state(lines, local_variables: local_variables)
- error_message = "Wrong result of code_block_open for:\n #{lines.join("\n")}"
- assert_equal(expected, code_block_open, error_message)
- end
-
- def check_state(lines, local_variables: [])
- context = build_context(local_variables)
- ruby_lex = RubyLex.new(context)
- _ltype, indent, _continue, code_block_open = ruby_lex.check_code_state(lines.join("\n"))
- [indent, code_block_open]
- end
-
- def test_auto_indent
- input_with_correct_indents = [
- Row.new(%q(def each_top_level_statement), nil, 2),
- Row.new(%q( initialize_input), nil, 2),
- Row.new(%q( catch(:TERM_INPUT) do), nil, 4),
- Row.new(%q( loop do), nil, 6),
- Row.new(%q( begin), nil, 8),
- Row.new(%q( prompt), nil, 8),
- Row.new(%q( unless l = lex), nil, 10),
- Row.new(%q( throw :TERM_INPUT if @line == ''), nil, 10),
- Row.new(%q( else), 8, 10),
- Row.new(%q( @line_no += l.count("\n")), nil, 10),
- Row.new(%q( next if l == "\n"), nil, 10),
- Row.new(%q( @line.concat l), nil, 10),
- Row.new(%q( if @code_block_open or @ltype or @continue or @indent > 0), nil, 12),
- Row.new(%q( next), nil, 12),
- Row.new(%q( end), 10, 10),
- Row.new(%q( end), 8, 8),
- Row.new(%q( if @line != "\n"), nil, 10),
- Row.new(%q( @line.force_encoding(@io.encoding)), nil, 10),
- Row.new(%q( yield @line, @exp_line_no), nil, 10),
- Row.new(%q( end), 8, 8),
- Row.new(%q( break if @io.eof?), nil, 8),
- Row.new(%q( @line = ''), nil, 8),
- Row.new(%q( @exp_line_no = @line_no), nil, 8),
- Row.new(%q( ), nil, 8),
- Row.new(%q( @indent = 0), nil, 8),
- Row.new(%q( rescue TerminateLineInput), 6, 8),
- Row.new(%q( initialize_input), nil, 8),
- Row.new(%q( prompt), nil, 8),
- Row.new(%q( end), 6, 6),
- Row.new(%q( end), 4, 4),
- Row.new(%q( end), 2, 2),
- Row.new(%q(end), 0, 0),
- ]
-
- lines = []
- input_with_correct_indents.each do |row|
- lines << row.content
- assert_row_indenting(lines, row)
- end
- end
-
- def test_braces_on_their_own_line
- input_with_correct_indents = [
- Row.new(%q(if true), nil, 2),
- Row.new(%q( [), nil, 4),
- Row.new(%q( ]), 2, 2),
- Row.new(%q(end), 0, 0),
- ]
-
- lines = []
- input_with_correct_indents.each do |row|
- lines << row.content
- assert_row_indenting(lines, row)
- end
- end
-
- def test_multiple_braces_in_a_line
- input_with_correct_indents = [
- Row.new(%q([[[), nil, 6),
- Row.new(%q( ]), 4, 4),
- Row.new(%q( ]), 2, 2),
- Row.new(%q(]), 0, 0),
- Row.new(%q([<<FOO]), 0, 0),
- Row.new(%q(hello), 0, 0),
- Row.new(%q(FOO), nil, 0),
- ]
-
- lines = []
- input_with_correct_indents.each do |row|
- lines << row.content
- assert_row_indenting(lines, row)
- end
- end
-
- def test_a_closed_brace_and_not_closed_brace_in_a_line
- input_with_correct_indents = [
- Row.new(%q(p() {), nil, 2),
- Row.new(%q(}), 0, 0),
- ]
-
- lines = []
- input_with_correct_indents.each do |row|
- lines << row.content
- assert_row_indenting(lines, row)
- end
- end
-
- def test_symbols
- input_with_correct_indents = [
- Row.new(%q(:a), nil, 0),
- Row.new(%q(:A), nil, 0),
- Row.new(%q(:+), nil, 0),
- Row.new(%q(:@@a), nil, 0),
- Row.new(%q(:@a), nil, 0),
- Row.new(%q(:$a), nil, 0),
- Row.new(%q(:def), nil, 0),
- Row.new(%q(:`), nil, 0),
- ]
-
- lines = []
- input_with_correct_indents.each do |row|
- lines << row.content
- assert_row_indenting(lines, row)
- end
- end
-
- def test_endless_range_at_end_of_line
- input_with_prompt = [
- PromptRow.new('001:0: :> ', %q(a = 3..)),
- PromptRow.new('002:0: :* ', %q()),
- ]
-
- lines = input_with_prompt.map(&:content)
- expected_prompt_list = input_with_prompt.map(&:prompt)
- assert_dynamic_prompt(lines, expected_prompt_list)
- end
-
- def test_heredoc_with_embexpr
- input_with_prompt = [
- PromptRow.new('001:0:":* ', %q(<<A+%W[#{<<B)),
- PromptRow.new('002:0:":* ', %q(#{<<C+%W[)),
- PromptRow.new('003:0:":* ', %q(a)),
- PromptRow.new('004:0:]:* ', %q(C)),
- PromptRow.new('005:0:]:* ', %q(a)),
- PromptRow.new('006:0:":* ', %q(]})),
- PromptRow.new('007:0:":* ', %q(})),
- PromptRow.new('008:0:":* ', %q(A)),
- PromptRow.new('009:0:]:* ', %q(B)),
- PromptRow.new('010:0:]:* ', %q(})),
- PromptRow.new('011:0: :> ', %q(])),
- PromptRow.new('012:0: :* ', %q()),
- ]
-
- lines = input_with_prompt.map(&:content)
- expected_prompt_list = input_with_prompt.map(&:prompt)
- assert_dynamic_prompt(lines, expected_prompt_list)
- end
-
- def test_heredoc_prompt_with_quotes
- input_with_prompt = [
- PromptRow.new("001:0:':* ", %q(<<~'A')),
- PromptRow.new("002:0:':* ", %q(#{foobar})),
- PromptRow.new("003:0: :> ", %q(A)),
- PromptRow.new("004:0:`:* ", %q(<<~`A`)),
- PromptRow.new("005:0:`:* ", %q(whoami)),
- PromptRow.new("006:0: :> ", %q(A)),
- PromptRow.new('007:0:":* ', %q(<<~"A")),
- PromptRow.new('008:0:":* ', %q(foobar)),
- PromptRow.new('009:0: :> ', %q(A)),
- ]
-
- lines = input_with_prompt.map(&:content)
- expected_prompt_list = input_with_prompt.map(&:prompt)
- assert_dynamic_prompt(lines, expected_prompt_list)
- end
-
- def test_backtick_method
- input_with_prompt = [
- PromptRow.new('001:0: :> ', %q(self.`(arg))),
- PromptRow.new('002:0: :* ', %q()),
- PromptRow.new('003:0: :> ', %q(def `(); end)),
- PromptRow.new('004:0: :* ', %q()),
- ]
-
- lines = input_with_prompt.map(&:content)
- expected_prompt_list = input_with_prompt.map(&:prompt)
- assert_dynamic_prompt(lines, expected_prompt_list)
- end
-
- def test_incomplete_coding_magic_comment
- input_with_correct_indents = [
- Row.new(%q(#coding:u), nil, 0),
- ]
-
- lines = []
- input_with_correct_indents.each do |row|
- lines << row.content
- assert_row_indenting(lines, row)
- end
- end
-
- def test_incomplete_encoding_magic_comment
- input_with_correct_indents = [
- Row.new(%q(#encoding:u), nil, 0),
- ]
-
- lines = []
- input_with_correct_indents.each do |row|
- lines << row.content
- assert_row_indenting(lines, row)
- end
- end
-
- def test_incomplete_emacs_coding_magic_comment
- input_with_correct_indents = [
- Row.new(%q(# -*- coding: u), nil, 0),
- ]
-
- lines = []
- input_with_correct_indents.each do |row|
- lines << row.content
- assert_row_indenting(lines, row)
- end
- end
-
- def test_incomplete_vim_coding_magic_comment
- input_with_correct_indents = [
- Row.new(%q(# vim:set fileencoding=u), nil, 0),
- ]
-
- lines = []
- input_with_correct_indents.each do |row|
- lines << row.content
- assert_row_indenting(lines, row)
- end
- end
-
- def test_mixed_rescue
- input_with_correct_indents = [
- Row.new(%q(def m), nil, 2),
- Row.new(%q( begin), nil, 4),
- Row.new(%q( begin), nil, 6),
- Row.new(%q( x = a rescue 4), nil, 6),
- Row.new(%q( y = [(a rescue 5)]), nil, 6),
- Row.new(%q( [x, y]), nil, 6),
- Row.new(%q( rescue => e), 4, 6),
- Row.new(%q( raise e rescue 8), nil, 6),
- Row.new(%q( end), 4, 4),
- Row.new(%q( rescue), 2, 4),
- Row.new(%q( raise rescue 11), nil, 4),
- Row.new(%q( end), 2, 2),
- Row.new(%q(rescue => e), 0, 2),
- Row.new(%q( raise e rescue 14), nil, 2),
- Row.new(%q(end), 0, 0),
- ]
-
- lines = []
- input_with_correct_indents.each do |row|
- lines << row.content
- assert_row_indenting(lines, row)
- end
- end
-
- def test_oneliner_method_definition
- input_with_correct_indents = [
- Row.new(%q(class A), nil, 2),
- Row.new(%q( def foo0), nil, 4),
- Row.new(%q( 3), nil, 4),
- Row.new(%q( end), 2, 2),
- Row.new(%q( def foo1()), nil, 4),
- Row.new(%q( 3), nil, 4),
- Row.new(%q( end), 2, 2),
- Row.new(%q( def foo2(a, b)), nil, 4),
- Row.new(%q( a + b), nil, 4),
- Row.new(%q( end), 2, 2),
- Row.new(%q( def foo3 a, b), nil, 4),
- Row.new(%q( a + b), nil, 4),
- Row.new(%q( end), 2, 2),
- Row.new(%q( def bar0() = 3), nil, 2),
- Row.new(%q( def bar1(a) = a), nil, 2),
- Row.new(%q( def bar2(a, b) = a + b), nil, 2),
- Row.new(%q( def bar3() = :s), nil, 2),
- Row.new(%q( def bar4() = Time.now), nil, 2),
- Row.new(%q(end), 0, 0),
- ]
-
- lines = []
- input_with_correct_indents.each do |row|
- lines << row.content
- assert_row_indenting(lines, row)
- end
- end
-
- def test_tlambda
- input_with_correct_indents = [
- Row.new(%q(if true), nil, 2, 1),
- Row.new(%q( -> {), nil, 4, 2),
- Row.new(%q( }), 2, 2, 1),
- Row.new(%q(end), 0, 0, 0),
- ]
-
- lines = []
- input_with_correct_indents.each do |row|
- lines << row.content
- assert_row_indenting(lines, row)
- assert_nesting_level(lines, row.nesting_level)
- end
- end
-
- def test_corresponding_syntax_to_keyword_do_in_class
- input_with_correct_indents = [
- Row.new(%q(class C), nil, 2, 1),
- Row.new(%q( while method_name do), nil, 4, 2),
- Row.new(%q( 3), nil, 4, 2),
- Row.new(%q( end), 2, 2, 1),
- Row.new(%q( foo do), nil, 4, 2),
- Row.new(%q( 3), nil, 4, 2),
- Row.new(%q( end), 2, 2, 1),
- Row.new(%q(end), 0, 0, 0),
- ]
-
- lines = []
- input_with_correct_indents.each do |row|
- lines << row.content
- assert_row_indenting(lines, row)
- assert_nesting_level(lines, row.nesting_level)
- end
- end
-
- def test_corresponding_syntax_to_keyword_do
- input_with_correct_indents = [
- Row.new(%q(while i > 0), nil, 2, 1),
- Row.new(%q( 3), nil, 2, 1),
- Row.new(%q(end), 0, 0, 0),
- Row.new(%q(while true), nil, 2, 1),
- Row.new(%q( 3), nil, 2, 1),
- Row.new(%q(end), 0, 0, 0),
- Row.new(%q(while ->{i > 0}.call), nil, 2, 1),
- Row.new(%q( 3), nil, 2, 1),
- Row.new(%q(end), 0, 0, 0),
- Row.new(%q(while ->{true}.call), nil, 2, 1),
- Row.new(%q( 3), nil, 2, 1),
- Row.new(%q(end), 0, 0, 0),
- Row.new(%q(while i > 0 do), nil, 2, 1),
- Row.new(%q( 3), nil, 2, 1),
- Row.new(%q(end), 0, 0, 0),
- Row.new(%q(while true do), nil, 2, 1),
- Row.new(%q( 3), nil, 2, 1),
- Row.new(%q(end), 0, 0, 0),
- Row.new(%q(while ->{i > 0}.call do), nil, 2, 1),
- Row.new(%q( 3), nil, 2, 1),
- Row.new(%q(end), 0, 0, 0),
- Row.new(%q(while ->{true}.call do), nil, 2, 1),
- Row.new(%q( 3), nil, 2, 1),
- Row.new(%q(end), 0, 0, 0),
- Row.new(%q(foo do), nil, 2, 1),
- Row.new(%q( 3), nil, 2, 1),
- Row.new(%q(end), 0, 0, 0),
- Row.new(%q(foo true do), nil, 2, 1),
- Row.new(%q( 3), nil, 2, 1),
- Row.new(%q(end), 0, 0, 0),
- Row.new(%q(foo ->{true} do), nil, 2, 1),
- Row.new(%q( 3), nil, 2, 1),
- Row.new(%q(end), 0, 0, 0),
- Row.new(%q(foo ->{i > 0} do), nil, 2, 1),
- Row.new(%q( 3), nil, 2, 1),
- Row.new(%q(end), 0, 0, 0),
- ]
-
- lines = []
- input_with_correct_indents.each do |row|
- lines << row.content
- assert_row_indenting(lines, row)
- assert_nesting_level(lines, row.nesting_level)
- end
- end
-
- def test_corresponding_syntax_to_keyword_for
- input_with_correct_indents = [
- Row.new(%q(for i in [1]), nil, 2, 1),
- Row.new(%q( puts i), nil, 2, 1),
- Row.new(%q(end), 0, 0, 0),
- ]
-
- lines = []
- input_with_correct_indents.each do |row|
- lines << row.content
- assert_row_indenting(lines, row)
- assert_nesting_level(lines, row.nesting_level)
- end
- end
-
- def test_corresponding_syntax_to_keyword_for_with_do
- input_with_correct_indents = [
- Row.new(%q(for i in [1] do), nil, 2, 1),
- Row.new(%q( puts i), nil, 2, 1),
- Row.new(%q(end), 0, 0, 0),
- ]
-
- lines = []
- input_with_correct_indents.each do |row|
- lines << row.content
- assert_row_indenting(lines, row)
- assert_nesting_level(lines, row.nesting_level)
- end
- end
-
- def test_corresponding_syntax_to_keyword_in
- input_with_correct_indents = [
- Row.new(%q(module E), nil, 2, 1),
- Row.new(%q(end), 0, 0, 0),
- Row.new(%q(class A), nil, 2, 1),
- Row.new(%q( in), nil, 4, 1)
- ]
-
- lines = []
- input_with_correct_indents.each do |row|
- lines << row.content
- assert_row_indenting(lines, row)
- assert_nesting_level(lines, row.nesting_level)
- end
- end
-
- def test_bracket_corresponding_to_times
- input_with_correct_indents = [
- Row.new(%q(3.times { |i|), nil, 2, 1),
- Row.new(%q( puts i), nil, 2, 1),
- Row.new(%q(}), 0, 0, 0),
- ]
-
- lines = []
- input_with_correct_indents.each do |row|
- lines << row.content
- assert_row_indenting(lines, row)
- assert_nesting_level(lines, row.nesting_level)
- end
- end
-
- def test_do_corresponding_to_times
- input_with_correct_indents = [
- Row.new(%q(3.times do |i|), nil, 2, 1),
- #Row.new(%q( puts i), nil, 2, 1),
- #Row.new(%q(end), 0, 0, 0),
- ]
-
- lines = []
- input_with_correct_indents.each do |row|
- lines << row.content
- assert_row_indenting(lines, row)
- assert_nesting_level(lines, row.nesting_level)
- end
- end
-
- def test_bracket_corresponding_to_loop
- input_with_correct_indents = [
- Row.new(%q(loop {), nil, 2, 1),
- Row.new(%q( 3), nil, 2, 1),
- Row.new(%q(}), 0, 0, 0),
- ]
-
- lines = []
- input_with_correct_indents.each do |row|
- lines << row.content
- assert_row_indenting(lines, row)
- assert_nesting_level(lines, row.nesting_level)
+ def test_interpolate_token_with_heredoc_and_unclosed_embexpr
+ code = <<~'EOC'
+ â‘ +<<A-â‘¡
+ #{â‘¢*<<B/â‘£
+ #{⑤&<<C|⑥
+ EOC
+ ripper_tokens = Ripper.tokenize(code)
+ rubylex_tokens = IRB::RubyLex.ripper_lex_without_warning(code)
+ # Assert no missing part
+ assert_equal(code, rubylex_tokens.map(&:tok).join)
+ # Assert ripper tokens are not removed
+ ripper_tokens.each do |tok|
+ assert(rubylex_tokens.any? { |t| t.tok == tok && t.tok != :on_ignored_by_ripper })
end
- end
-
- def test_do_corresponding_to_loop
- input_with_correct_indents = [
- Row.new(%q(loop do), nil, 2, 1),
- Row.new(%q( 3), nil, 2, 1),
- Row.new(%q(end), 0, 0, 0),
- ]
-
- lines = []
- input_with_correct_indents.each do |row|
- lines << row.content
- assert_row_indenting(lines, row)
- assert_nesting_level(lines, row.nesting_level)
+ # Assert interpolated token position
+ rubylex_tokens.each do |t|
+ row, col = t.pos
+ assert_equal t.tok, code.lines[row - 1].byteslice(col, t.tok.bytesize)
end
end
def test_local_variables_dependent_code
- pend if RUBY_ENGINE == 'truffleruby'
lines = ["a /1#/ do", "2"]
- assert_nesting_level(lines, 1)
+ assert_indent_level(lines, 1)
assert_code_block_open(lines, true)
- assert_nesting_level(lines, 0, local_variables: ['a'])
+ assert_indent_level(lines, 0, local_variables: ['a'])
assert_code_block_open(lines, false, local_variables: ['a'])
end
- def test_heredoc_with_indent
- input_with_correct_indents = [
- Row.new(%q(<<~Q), 0, 0, 0),
- Row.new(%q({), 0, 0, 0),
- Row.new(%q( #), 2, 0, 0),
- Row.new(%q(}), 0, 0, 0)
- ]
-
- lines = []
- input_with_correct_indents.each do |row|
- lines << row.content
- assert_row_indenting(lines, row)
- assert_nesting_level(lines, row.nesting_level)
- end
+ def test_literal_ends_with_space
+ assert_code_block_open(['% a'], true)
+ assert_code_block_open(['% a '], false)
end
- def test_oneliner_def_in_multiple_lines
- input_with_correct_indents = [
- Row.new(%q(def a()=[), nil, 4, 2),
- Row.new(%q( 1,), nil, 4, 1),
- Row.new(%q(].), 0, 0, 0),
- Row.new(%q(to_s), nil, 0, 0),
- ]
-
- lines = []
- input_with_correct_indents.each do |row|
- lines << row.content
- assert_row_indenting(lines, row)
- assert_nesting_level(lines, row.nesting_level)
- end
+ def test_literal_ends_with_newline
+ assert_code_block_open(['%'], true)
+ assert_code_block_open(['%', ''], false)
end
- def test_broken_heredoc
- input_with_correct_indents = [
- Row.new(%q(def foo), nil, 2, 1),
- Row.new(%q( <<~Q), 2, 2, 1),
- Row.new(%q( Qend), 2, 2, 1),
- ]
-
- lines = []
- input_with_correct_indents.each do |row|
- lines << row.content
- assert_row_indenting(lines, row)
- assert_nesting_level(lines, row.nesting_level)
- end
+ def test_should_continue
+ assert_should_continue(['a'], false)
+ assert_should_continue(['/a/'], false)
+ assert_should_continue(['a;'], false)
+ assert_should_continue(['<<A', 'A'], false)
+ assert_should_continue(['a...'], false)
+ assert_should_continue(['a\\'], true)
+ assert_should_continue(['a.'], true)
+ assert_should_continue(['a+'], true)
+ assert_should_continue(['a; #comment', '', '=begin', 'embdoc', '=end', ''], false)
+ assert_should_continue(['a+ #comment', '', '=begin', 'embdoc', '=end', ''], true)
end
- PromptRow = Struct.new(:prompt, :content)
+ def test_code_block_open_with_should_continue
+ # syntax ok
+ assert_code_block_open(['a'], false) # continue: false
+ assert_code_block_open(['a\\'], true) # continue: true
- class MockIO_DynamicPrompt
- def initialize(params, &assertion)
- @params = params
- @assertion = assertion
- end
-
- def dynamic_prompt(&block)
- result = block.call(@params)
- @assertion.call(result)
- end
- end
-
- def assert_dynamic_prompt(lines, expected_prompt_list)
- pend if RUBY_ENGINE == 'truffleruby'
- context = build_context
- ruby_lex = RubyLex.new(context)
- dynamic_prompt_executed = false
- io = MockIO_DynamicPrompt.new(lines) do |prompt_list|
- error_message = <<~EOM
- Expected dynamic prompt:
- #{expected_prompt_list.join("\n")}
-
- Actual dynamic prompt:
- #{prompt_list.join("\n")}
- EOM
- dynamic_prompt_executed = true
- assert_equal(expected_prompt_list, prompt_list, error_message)
- end
- ruby_lex.set_prompt do |ltype, indent, continue, line_no|
- '%03d:%01d:%1s:%s ' % [line_no, indent, ltype, continue ? '*' : '>']
- end
- ruby_lex.configure_io(io)
- assert dynamic_prompt_executed, "dynamic_prompt's assertions were not executed."
- end
-
- def test_dynamic_prompt
- input_with_prompt = [
- PromptRow.new('001:1: :* ', %q(def hoge)),
- PromptRow.new('002:1: :* ', %q( 3)),
- PromptRow.new('003:0: :> ', %q(end)),
- ]
-
- lines = input_with_prompt.map(&:content)
- expected_prompt_list = input_with_prompt.map(&:prompt)
- assert_dynamic_prompt(lines, expected_prompt_list)
- end
-
- def test_dynamic_prompt_with_double_newline_breaking_code
- input_with_prompt = [
- PromptRow.new('001:1: :* ', %q(if true)),
- PromptRow.new('002:1: :* ', %q(%)),
- PromptRow.new('003:1: :* ', %q(;end)),
- PromptRow.new('004:1: :* ', %q(;hello)),
- PromptRow.new('005:0: :> ', %q(end)),
- ]
-
- lines = input_with_prompt.map(&:content)
- expected_prompt_list = input_with_prompt.map(&:prompt)
- assert_dynamic_prompt(lines, expected_prompt_list)
- end
+ # recoverable syntax error code is not terminated
+ assert_code_block_open(['a+'], true)
- def test_dynamic_prompt_with_multiline_literal
- input_with_prompt = [
- PromptRow.new('001:1: :* ', %q(if true)),
- PromptRow.new('002:1:]:* ', %q( %w[)),
- PromptRow.new('003:1:]:* ', %q( a)),
- PromptRow.new('004:1: :* ', %q( ])),
- PromptRow.new('005:1: :* ', %q( b)),
- PromptRow.new('006:1:]:* ', %q( %w[)),
- PromptRow.new('007:1:]:* ', %q( c)),
- PromptRow.new('008:1: :* ', %q( ])),
- PromptRow.new('009:0: :> ', %q(end)),
- ]
+ # unrecoverable syntax error code is terminated
+ assert_code_block_open(['.; a+'], false)
- lines = input_with_prompt.map(&:content)
- expected_prompt_list = input_with_prompt.map(&:prompt)
- assert_dynamic_prompt(lines, expected_prompt_list)
- end
-
- def test_dynamic_prompt_with_blank_line
- input_with_prompt = [
- PromptRow.new('001:0:]:* ', %q(%w[)),
- PromptRow.new('002:0:]:* ', %q()),
- PromptRow.new('003:0: :> ', %q(])),
- ]
-
- lines = input_with_prompt.map(&:content)
- expected_prompt_list = input_with_prompt.map(&:prompt)
- assert_dynamic_prompt(lines, expected_prompt_list)
+ # other syntax error that failed to determine if it is recoverable or not
+ assert_code_block_open(['@; a'], false)
+ assert_code_block_open(['@; a+'], true)
+ assert_code_block_open(['@; (a'], true)
end
def test_broken_percent_literal
- tokens = RubyLex.ripper_lex_without_warning('%wwww')
+ tokens = IRB::RubyLex.ripper_lex_without_warning('%wwww')
pos_to_index = {}
tokens.each_with_index { |t, i|
assert_nil(pos_to_index[t.pos], "There is already another token in the position of #{t.inspect}.")
@@ -724,7 +92,7 @@ module TestIRB
end
def test_broken_percent_literal_in_method
- tokens = RubyLex.ripper_lex_without_warning(<<~EOC.chomp)
+ tokens = IRB::RubyLex.ripper_lex_without_warning(<<~EOC.chomp)
def foo
%wwww
end
@@ -738,7 +106,7 @@ module TestIRB
def test_unterminated_code
['do', '<<A'].each do |code|
- tokens = RubyLex.ripper_lex_without_warning(code)
+ tokens = IRB::RubyLex.ripper_lex_without_warning(code)
assert_equal(code, tokens.map(&:tok).join, "Cannot reconstruct code from tokens")
error_tokens = tokens.map(&:event).grep(/error/)
assert_empty(error_tokens, 'Error tokens must be ignored if there is corresponding non-error token')
@@ -746,15 +114,14 @@ module TestIRB
end
def test_unterminated_heredoc_string_literal
- context = build_context
['<<A;<<B', "<<A;<<B\n", "%W[\#{<<A;<<B", "%W[\#{<<A;<<B\n"].each do |code|
- tokens = RubyLex.ripper_lex_without_warning(code)
- string_literal = RubyLex.new(context).check_string_literal(tokens)
+ tokens = IRB::RubyLex.ripper_lex_without_warning(code)
+ string_literal = IRB::NestingParser.open_tokens(tokens).last
assert_equal('<<A', string_literal&.tok)
end
end
- def test_corresponding_token_depth_with_heredoc_and_embdoc
+ def test_indent_level_with_heredoc_and_embdoc
reference_code = <<~EOC.chomp
if true
hello
@@ -775,64 +142,101 @@ module TestIRB
p(
)
EOC
- context = build_context
- [reference_code, code_with_heredoc, code_with_embdoc].each do |code|
- lex = RubyLex.new(context)
- lines = code.lines
- lex.instance_variable_set('@tokens', RubyLex.ripper_lex_without_warning(code))
- assert_equal 2, lex.check_corresponding_token_depth(lines, lines.size)
- end
- end
-
- def test_find_prev_spaces_with_multiline_literal
- lex = RubyLex.new(build_context)
- reference_code = <<~EOC.chomp
- if true
- 1
- hello
- 1
- world
- end
- EOC
- code_with_percent_string = <<~EOC.chomp
- if true
- %w[
- hello
- ]
- world
- end
- EOC
- code_with_quoted_string = <<~EOC.chomp
- if true
- '
- hello
- '
- world
- end
- EOC
- context = build_context
- [reference_code, code_with_percent_string, code_with_quoted_string].each do |code|
- lex = RubyLex.new(context)
- lex.instance_variable_set('@tokens', RubyLex.ripper_lex_without_warning(code))
- prev_spaces = (1..code.lines.size).map { |index| lex.find_prev_spaces index }
- assert_equal [0, 2, 2, 2, 2, 0], prev_spaces
- end
+ expected = 1
+ assert_indent_level(reference_code.lines, expected)
+ assert_indent_level(code_with_heredoc.lines, expected)
+ assert_indent_level(code_with_embdoc.lines, expected)
+ end
+
+ def test_assignment_expression
+ ruby_lex = IRB::RubyLex.new
+
+ [
+ "foo = bar",
+ "@foo = bar",
+ "$foo = bar",
+ "@@foo = bar",
+ "::Foo = bar",
+ "a::Foo = bar",
+ "Foo = bar",
+ "foo.bar = 1",
+ "foo[1] = bar",
+ "foo += bar",
+ "foo -= bar",
+ "foo ||= bar",
+ "foo &&= bar",
+ "foo, bar = 1, 2",
+ "foo.bar=(1)",
+ "foo; foo = bar",
+ "foo; foo = bar; ;\n ;",
+ "foo\nfoo = bar",
+ ].each do |exp|
+ assert(
+ ruby_lex.assignment_expression?(exp, local_variables: []),
+ "#{exp.inspect}: should be an assignment expression"
+ )
+ end
+
+ [
+ "foo",
+ "foo.bar",
+ "foo[0]",
+ "foo = bar; foo",
+ "foo = bar\nfoo",
+ ].each do |exp|
+ refute(
+ ruby_lex.assignment_expression?(exp, local_variables: []),
+ "#{exp.inspect}: should not be an assignment expression"
+ )
+ end
+ end
+
+ def test_assignment_expression_with_local_variable
+ ruby_lex = IRB::RubyLex.new
+ code = "a /1;x=1#/"
+ refute(ruby_lex.assignment_expression?(code, local_variables: []), "#{code}: should not be an assignment expression")
+ assert(ruby_lex.assignment_expression?(code, local_variables: [:a]), "#{code}: should be an assignment expression")
+ refute(ruby_lex.assignment_expression?("", local_variables: [:a]), "empty code should not be an assignment expression")
+ end
+
+ def test_initialising_the_old_top_level_ruby_lex
+ assert_in_out_err(["--disable-gems", "-W:deprecated"], <<~RUBY, [], /warning: constant ::RubyLex is deprecated/)
+ require "irb"
+ ::RubyLex.new(nil)
+ RUBY
end
private
- def build_context(local_variables = nil)
- IRB.init_config(nil)
- workspace = IRB::WorkSpace.new(TOPLEVEL_BINDING.dup)
+ def assert_indent_level(lines, expected, local_variables: [])
+ indent_level, _continue, _code_block_open = check_state(lines, local_variables: local_variables)
+ error_message = "Calculated the wrong number of indent level for:\n #{lines.join("\n")}"
+ assert_equal(expected, indent_level, error_message)
+ end
- if local_variables
- local_variables.each do |n|
- workspace.binding.local_variable_set(n, nil)
- end
+ def assert_should_continue(lines, expected, local_variables: [])
+ _indent_level, continue, _code_block_open = check_state(lines, local_variables: local_variables)
+ error_message = "Wrong result of should_continue for:\n #{lines.join("\n")}"
+ assert_equal(expected, continue, error_message)
+ end
+
+ def assert_code_block_open(lines, expected, local_variables: [])
+ if RUBY_ENGINE == 'truffleruby'
+ omit "Remove me after https://github.com/ruby/prism/issues/2129 is addressed and adopted in TruffleRuby"
end
- IRB.conf[:VERBOSE] = false
- IRB::Context.new(nil, workspace)
+ _indent_level, _continue, code_block_open = check_state(lines, local_variables: local_variables)
+ error_message = "Wrong result of code_block_open for:\n #{lines.join("\n")}"
+ assert_equal(expected, code_block_open, error_message)
+ end
+
+ def check_state(lines, local_variables: [])
+ code = lines.map { |l| "#{l}\n" }.join # code should end with "\n"
+ ruby_lex = IRB::RubyLex.new
+ tokens, opens, terminated = ruby_lex.check_code_state(code, local_variables: local_variables)
+ indent_level = ruby_lex.calc_indent_level(opens)
+ continue = ruby_lex.should_continue?(tokens)
+ [indent_level, continue, !terminated]
end
end
end
diff --git a/test/irb/test_tracer.rb b/test/irb/test_tracer.rb
new file mode 100644
index 0000000000..540f8be131
--- /dev/null
+++ b/test/irb/test_tracer.rb
@@ -0,0 +1,90 @@
+# frozen_string_literal: false
+require 'tempfile'
+require 'irb'
+
+require_relative "helper"
+
+module TestIRB
+ class ContextWithTracerIntegrationTest < IntegrationTestCase
+ def setup
+ super
+
+ omit "Tracer gem is not available when running on TruffleRuby" if RUBY_ENGINE == "truffleruby"
+
+ @envs.merge!("NO_COLOR" => "true")
+ end
+
+ def example_ruby_file
+ <<~'RUBY'
+ class Foo
+ def self.foo
+ 100
+ end
+ end
+
+ def bar(obj)
+ obj.foo
+ end
+
+ binding.irb
+ RUBY
+ end
+
+ def test_use_tracer_enabled_when_gem_is_unavailable
+ write_rc <<~RUBY
+ # Simulate the absence of the tracer gem
+ ::Kernel.send(:alias_method, :irb_original_require, :require)
+
+ ::Kernel.define_method(:require) do |name|
+ raise LoadError, "cannot load such file -- tracer (test)" if name.match?("tracer")
+ ::Kernel.send(:irb_original_require, name)
+ end
+
+ IRB.conf[:USE_TRACER] = true
+ RUBY
+
+ write_ruby example_ruby_file
+
+ output = run_ruby_file do
+ type "bar(Foo)"
+ type "exit"
+ end
+
+ assert_include(output, "Tracer extension of IRB is enabled but tracer gem wasn't found.")
+ end
+
+ def test_use_tracer_enabled_when_gem_is_available
+ write_rc <<~RUBY
+ IRB.conf[:USE_TRACER] = true
+ RUBY
+
+ write_ruby example_ruby_file
+
+ output = run_ruby_file do
+ type "bar(Foo)"
+ type "exit"
+ end
+
+ assert_include(output, "Object#bar at")
+ assert_include(output, "Foo.foo at")
+ assert_include(output, "Foo.foo #=> 100")
+ assert_include(output, "Object#bar #=> 100")
+
+ # Test that the tracer output does not include IRB's own files
+ assert_not_include(output, "irb/workspace.rb")
+ end
+
+ def test_use_tracer_is_disabled_by_default
+ write_ruby example_ruby_file
+
+ output = run_ruby_file do
+ type "bar(Foo)"
+ type "exit"
+ end
+
+ assert_not_include(output, "#depth:")
+ assert_not_include(output, "Foo.foo")
+ end
+
+ end
+end
diff --git a/test/irb/test_type_completor.rb b/test/irb/test_type_completor.rb
new file mode 100644
index 0000000000..5ed8988b34
--- /dev/null
+++ b/test/irb/test_type_completor.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+
+# Run test only when Ruby >= 3.0 and repl_type_completor is available
+return unless RUBY_VERSION >= '3.0.0'
+return if RUBY_ENGINE == 'truffleruby' # needs endless method definition
+begin
+ require 'repl_type_completor'
+rescue LoadError
+ return
+end
+
+require 'irb'
+require 'tempfile'
+require_relative './helper'
+
+module TestIRB
+ class TypeCompletorTest < TestCase
+ DummyContext = Struct.new(:irb_path)
+
+ def setup
+ ReplTypeCompletor.load_rbs unless ReplTypeCompletor.rbs_loaded?
+ context = DummyContext.new('(irb)')
+ @completor = IRB::TypeCompletor.new(context)
+ end
+
+ def empty_binding
+ binding
+ end
+
+ def assert_completion(preposing, target, binding: empty_binding, include: nil, exclude: nil)
+ raise ArgumentError if include.nil? && exclude.nil?
+ candidates = @completor.completion_candidates(preposing, target, '', bind: binding)
+ assert ([*include] - candidates).empty?, "Expected #{candidates} to include #{include}" if include
+ assert (candidates & [*exclude]).empty?, "Expected #{candidates} not to include #{exclude}" if exclude
+ end
+
+ def assert_doc_namespace(preposing, target, namespace, binding: empty_binding)
+ @completor.completion_candidates(preposing, target, '', bind: binding)
+ assert_equal namespace, @completor.doc_namespace(preposing, target, '', bind: binding)
+ end
+
+ def test_type_completion
+ bind = eval('num = 1; binding')
+ assert_completion('num.times.map(&:', 'ab', binding: bind, include: 'abs')
+ assert_doc_namespace('num.chr.', 'upcase', 'String#upcase', binding: bind)
+ end
+
+ def test_inspect
+ assert_match(/\AReplTypeCompletor.*\z/, @completor.inspect)
+ end
+
+ def test_empty_completion
+ candidates = @completor.completion_candidates('(', ')', '', bind: binding)
+ assert_equal [], candidates
+ assert_doc_namespace('(', ')', nil)
+ end
+
+ def test_command_completion
+ assert_include(@completor.completion_candidates('', 'show_s', '', bind: binding), 'show_source')
+ assert_not_include(@completor.completion_candidates(';', 'show_s', '', bind: binding), 'show_source')
+ end
+ end
+
+ class TypeCompletorIntegrationTest < IntegrationTestCase
+ def test_type_completor
+ write_rc <<~RUBY
+ IRB.conf[:COMPLETOR] = :type
+ RUBY
+
+ write_ruby <<~'RUBY'
+ binding.irb
+ RUBY
+
+ output = run_ruby_file do
+ type "irb_info"
+ type "sleep 0.01 until ReplTypeCompletor.rbs_loaded?"
+ type "completor = IRB.CurrentContext.io.instance_variable_get(:@completor);"
+ type "n = 10"
+ type "puts completor.completion_candidates 'a = n.abs;', 'a.b', '', bind: binding"
+ type "puts completor.doc_namespace 'a = n.chr;', 'a.encoding', '', bind: binding"
+ type "exit!"
+ end
+ assert_match(/Completion: Autocomplete, ReplTypeCompletor/, output)
+ assert_match(/a\.bit_length/, output)
+ assert_match(/String#encoding/, output)
+ end
+ end
+end
diff --git a/test/irb/test_workspace.rb b/test/irb/test_workspace.rb
index c6b8aa1e99..199ce95a37 100644
--- a/test/irb/test_workspace.rb
+++ b/test/irb/test_workspace.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: false
require 'tempfile'
-require 'rubygems'
require 'irb'
require 'irb/workspace'
require 'irb/color'
@@ -8,7 +7,7 @@ require 'irb/color'
require_relative "helper"
module TestIRB
- class TestWorkSpace < TestCase
+ class WorkSpaceTest < TestCase
def test_code_around_binding
IRB.conf[:USE_COLORIZE] = false
Tempfile.create('irb') do |f|
@@ -91,7 +90,7 @@ module TestIRB
irb_path = "#{top_srcdir}/#{dir}/irb"
File.exist?(irb_path)
end or omit 'irb command not found'
- assert_in_out_err(bundle_exec + ['-W0', "-C#{top_srcdir}", '-e', <<~RUBY , '--', '-f', '--'], 'binding.local_variables', /\[:_\]/, [], bug17623)
+ assert_in_out_err(bundle_exec + ['-W0', "-C#{top_srcdir}", '-e', <<~RUBY, '--', '-f', '--'], 'binding.local_variables', /\[:_\]/, [], bug17623)
version = 'xyz' # typical rubygems loading file
load('#{irb_path}')
RUBY
diff --git a/test/irb/yamatanooroti/test_rendering.rb b/test/irb/yamatanooroti/test_rendering.rb
index 1586dc6504..44e07a3a12 100644
--- a/test/irb/yamatanooroti/test_rendering.rb
+++ b/test/irb/yamatanooroti/test_rendering.rb
@@ -8,8 +8,12 @@ rescue LoadError, NameError
return
end
-class IRB::TestRendering < Yamatanooroti::TestCase
+class IRB::RenderingTest < Yamatanooroti::TestCase
def setup
+ @original_term = ENV['TERM']
+ @home_backup = ENV['HOME']
+ @xdg_config_home_backup = ENV['XDG_CONFIG_HOME']
+ ENV['TERM'] = "xterm-256color"
@pwd = Dir.pwd
suffix = '%010d' % Random.rand(0..65535)
@tmpdir = File.join(File.expand_path(Dir.tmpdir), "test_irb_#{$$}_#{suffix}")
@@ -22,12 +26,16 @@ class IRB::TestRendering < Yamatanooroti::TestCase
@irbrc_backup = ENV['IRBRC']
@irbrc_file = ENV['IRBRC'] = File.join(@tmpdir, 'temporaty_irbrc')
File.unlink(@irbrc_file) if File.exist?(@irbrc_file)
+ ENV['HOME'] = File.join(@tmpdir, 'home')
+ ENV['XDG_CONFIG_HOME'] = File.join(@tmpdir, 'xdg_config_home')
end
def teardown
FileUtils.rm_rf(@tmpdir)
ENV['IRBRC'] = @irbrc_backup
- ENV.delete('RELINE_TEST_PROMPT') if ENV['RELINE_TEST_PROMPT']
+ ENV['TERM'] = @original_term
+ ENV['HOME'] = @home_backup
+ ENV['XDG_CONFIG_HOME'] = @xdg_config_home_backup
end
def test_launch
@@ -41,9 +49,74 @@ class IRB::TestRendering < Yamatanooroti::TestCase
close
assert_screen(<<~EOC)
start IRB
- irb(main):001:0> 'Hello, World!'
+ irb(main):001> 'Hello, World!'
=> "Hello, World!"
- irb(main):002:0>
+ irb(main):002>
+ EOC
+ end
+
+ def test_configuration_file_is_skipped_with_dash_f
+ write_irbrc <<~'LINES'
+ puts '.irbrc file should be ignored when -f is used'
+ LINES
+ start_terminal(25, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb -f}, startup_message: '')
+ write(<<~EOC)
+ 'Hello, World!'
+ EOC
+ close
+ assert_screen(<<~EOC)
+ irb(main):001> 'Hello, World!'
+ => "Hello, World!"
+ irb(main):002>
+ EOC
+ end
+
+ def test_configuration_file_is_skipped_with_dash_f_for_nested_sessions
+ write_irbrc <<~'LINES'
+ puts '.irbrc file should be ignored when -f is used'
+ LINES
+ start_terminal(25, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb -f}, startup_message: '')
+ write(<<~EOC)
+ 'Hello, World!'
+ binding.irb
+ exit!
+ EOC
+ close
+ assert_screen(<<~EOC)
+ irb(main):001> 'Hello, World!'
+ => "Hello, World!"
+ irb(main):002> binding.irb
+ irb(main):003> exit!
+ irb(main):001>
+ EOC
+ end
+
+ def test_nomultiline
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ LINES
+ start_terminal(25, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb --nomultiline}, startup_message: 'start IRB')
+ write(<<~EOC)
+ if true
+ if false
+ a = "hello
+ world"
+ puts a
+ end
+ end
+ EOC
+ close
+ assert_screen(<<~EOC)
+ start IRB
+ irb(main):001> if true
+ irb(main):002* if false
+ irb(main):003* a = "hello
+ irb(main):004" world"
+ irb(main):005* puts a
+ irb(main):006* end
+ irb(main):007* end
+ => nil
+ irb(main):008>
EOC
end
@@ -64,25 +137,27 @@ class IRB::TestRendering < Yamatanooroti::TestCase
a
.a
.b
+ .itself
EOC
close
assert_screen(<<~EOC)
start IRB
- irb(main):001:1* class A
- irb(main):002:1* def inspect; '#<A>'; end
- irb(main):003:1* def a; self; end
- irb(main):004:1* def b; true; end
- irb(main):005:0> end
+ irb(main):001* class A
+ irb(main):002* def inspect; '#<A>'; end
+ irb(main):003* def a; self; end
+ irb(main):004* def b; true; end
+ irb(main):005> end
=> :b
- irb(main):006:0>
- irb(main):007:0> a = A.new
+ irb(main):006>
+ irb(main):007> a = A.new
=> #<A>
- irb(main):008:0>
- irb(main):009:0> a
- irb(main):010:0> .a
- irb(main):011:0> .b
+ irb(main):008>
+ irb(main):009> a
+ irb(main):010> .a
+ irb(main):011> .b
+ irb(main):012> .itself
=> true
- irb(main):012:0>
+ irb(main):013>
EOC
end
@@ -108,7 +183,6 @@ class IRB::TestRendering < Yamatanooroti::TestCase
(a)
&.b()
-
class A def b; self; end; def c; true; end; end;
a = A.new
a
@@ -117,44 +191,44 @@ class IRB::TestRendering < Yamatanooroti::TestCase
.c
(a)
&.b()
+ .itself
EOC
close
assert_screen(<<~EOC)
start IRB
- irb(main):001:1* class A
- irb(main):002:1* def inspect; '#<A>'; end
- irb(main):003:1* def b; self; end
- irb(main):004:1* def c; true; end
- irb(main):005:0> end
+ irb(main):001* class A
+ irb(main):002* def inspect; '#<A>'; end
+ irb(main):003* def b; self; end
+ irb(main):004* def c; true; end
+ irb(main):005> end
=> :c
- irb(main):006:0>
- irb(main):007:0> a = A.new
+ irb(main):006>
+ irb(main):007> a = A.new
=> #<A>
- irb(main):008:0>
- irb(main):009:0> a
- irb(main):010:0> .b
- irb(main):011:0> # aaa
- irb(main):012:0> .c
+ irb(main):008>
+ irb(main):009> a
+ irb(main):010> .b
+ irb(main):011> # aaa
+ irb(main):012> .c
=> true
- irb(main):013:0>
- irb(main):014:0> (a)
- irb(main):015:0> &.b()
+ irb(main):013>
+ irb(main):014> (a)
+ irb(main):015> &.b()
=> #<A>
- irb(main):016:0>
- irb(main):017:0>
- irb(main):018:0> class A def b; self; end; def c; true; end; end;
- => :c
- irb(main):019:0> a = A.new
+ irb(main):016>
+ irb(main):017> class A def b; self; end; def c; true; end; end;
+ irb(main):018> a = A.new
=> #<A>
- irb(main):020:0> a
- irb(main):021:0> .b
- irb(main):022:0> # aaa
- irb(main):023:0> .c
+ irb(main):019> a
+ irb(main):020> .b
+ irb(main):021> # aaa
+ irb(main):022> .c
=> true
- irb(main):024:0> (a)
- irb(main):025:0> &.b()
+ irb(main):023> (a)
+ irb(main):024> &.b()
+ irb(main):025> .itself
=> #<A>
- irb(main):026:0>
+ irb(main):026>
EOC
end
@@ -169,41 +243,70 @@ class IRB::TestRendering < Yamatanooroti::TestCase
close
assert_screen(<<~EOC)
start IRB
- irb(main):001:0> :`
+ irb(main):001> :`
=> :`
- irb(main):002:0>
+ irb(main):002>
EOC
end
- def test_autocomplete_with_showdoc_in_gaps_on_narrow_screen_right
- pend "Needs a dummy document to show doc"
+ def test_autocomplete_with_multiple_doc_namespaces
write_irbrc <<~'LINES'
+ puts 'start IRB'
+ LINES
+ start_terminal(3, 50, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ write("{}.__id_")
+ write("\C-i")
+ sleep 0.2
+ close
+ screen = result.join("\n").sub(/\n*\z/, "\n")
+ assert_match(/start\ IRB\nirb\(main\):001> {}\.__id__\n }\.__id__(?:Press )?/, screen)
+ end
+
+ def test_autocomplete_with_showdoc_in_gaps_on_narrow_screen_right
+ rdoc_dir = File.join(@tmpdir, 'rdoc')
+ system("bundle exec rdoc -r -o #{rdoc_dir}")
+ write_irbrc <<~LINES
+ IRB.conf[:EXTRA_DOC_DIRS] = ['#{rdoc_dir}']
IRB.conf[:PROMPT][:MY_PROMPT] = {
:PROMPT_I => "%03n> ",
- :PROMPT_N => "%03n> ",
:PROMPT_S => "%03n> ",
:PROMPT_C => "%03n> "
}
IRB.conf[:PROMPT_MODE] = :MY_PROMPT
puts 'start IRB'
LINES
- start_terminal(4, 19, %W{ruby -I/home/aycabta/ruby/reline/lib -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
- write("Str\C-i")
+ start_terminal(4, 19, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ write("IR")
+ write("\C-i")
+ sleep 0.2
close
- assert_screen(<<~EOC)
- 001> String
- StringPress A
- StructString
- of byte
- EOC
+
+ # This is because on macOS we display different shortcut for displaying the full doc
+ # 'O' is for 'Option' and 'A' is for 'Alt'
+ if RUBY_PLATFORM =~ /darwin/
+ assert_screen(<<~EOC)
+ start IRB
+ 001> IRB
+ IRBPress Opti
+ IRB
+ EOC
+ else
+ assert_screen(<<~EOC)
+ start IRB
+ 001> IRB
+ IRBPress Alt+
+ IRB
+ EOC
+ end
end
def test_autocomplete_with_showdoc_in_gaps_on_narrow_screen_left
- pend "Needs a dummy document to show doc"
- write_irbrc <<~'LINES'
+ rdoc_dir = File.join(@tmpdir, 'rdoc')
+ system("bundle exec rdoc -r -o #{rdoc_dir}")
+ write_irbrc <<~LINES
+ IRB.conf[:EXTRA_DOC_DIRS] = ['#{rdoc_dir}']
IRB.conf[:PROMPT][:MY_PROMPT] = {
:PROMPT_I => "%03n> ",
- :PROMPT_N => "%03n> ",
:PROMPT_S => "%03n> ",
:PROMPT_C => "%03n> "
}
@@ -211,13 +314,15 @@ class IRB::TestRendering < Yamatanooroti::TestCase
puts 'start IRB'
LINES
start_terminal(4, 12, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
- write("Str\C-i")
+ write("IR")
+ write("\C-i")
+ sleep 0.2
close
assert_screen(<<~EOC)
- 001> String
- PressString
- StrinStruct
- of by
+ start IRB
+ 001> IRB
+ PressIRB
+ IRB
EOC
end
@@ -232,14 +337,176 @@ class IRB::TestRendering < Yamatanooroti::TestCase
close
assert_screen(<<~EOC)
start IRB
- irb(main):001:0> #{code}
+ irb(main):001> #{code}
=>
[0,
...
- irb(main):002:0>
+ irb(main):002>
+ EOC
+ end
+
+ def test_ctrl_c_is_handled
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ LINES
+ start_terminal(40, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ # Assignment expression code that turns into non-assignment expression after evaluation
+ write("\C-c")
+ close
+ assert_screen(<<~EOC)
+ start IRB
+ irb(main):001>
+ ^C
+ irb(main):001>
EOC
end
+ def test_show_cmds_with_pager_can_quit_with_ctrl_c
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ LINES
+ start_terminal(40, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ write("help\n")
+ write("G") # move to the end of the screen
+ write("\C-c") # quit pager
+ write("'foo' + 'bar'\n") # eval something to make sure IRB resumes
+ close
+
+ screen = result.join("\n").sub(/\n*\z/, "\n")
+ # IRB::Abort should be rescued
+ assert_not_match(/IRB::Abort/, screen)
+ # IRB should resume
+ assert_match(/foobar/, screen)
+ end
+
+ def test_pager_page_content_pages_output_when_it_does_not_fit_in_the_screen_because_of_total_length
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ require "irb/pager"
+ LINES
+ start_terminal(10, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ write("IRB::Pager.page_content('a' * (80 * 8))\n")
+ write("'foo' + 'bar'\n") # eval something to make sure IRB resumes
+ close
+
+ screen = result.join("\n").sub(/\n*\z/, "\n")
+ assert_match(/a{80}/, screen)
+ # because pager is invoked, foobar will not be evaluated
+ assert_not_match(/foobar/, screen)
+ end
+
+ def test_pager_page_content_pages_output_when_it_does_not_fit_in_the_screen_because_of_screen_height
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ require "irb/pager"
+ LINES
+ start_terminal(10, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ write("IRB::Pager.page_content('a\n' * 8)\n")
+ write("'foo' + 'bar'\n") # eval something to make sure IRB resumes
+ close
+
+ screen = result.join("\n").sub(/\n*\z/, "\n")
+ assert_match(/(a\n){8}/, screen)
+ # because pager is invoked, foobar will not be evaluated
+ assert_not_match(/foobar/, screen)
+ end
+
+ def test_pager_page_content_doesnt_page_output_when_it_fits_in_the_screen
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ require "irb/pager"
+ LINES
+ start_terminal(10, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ write("IRB::Pager.page_content('a' * (80 * 7))\n")
+ write("'foo' + 'bar'\n") # eval something to make sure IRB resumes
+ close
+
+ screen = result.join("\n").sub(/\n*\z/, "\n")
+ assert_match(/a{80}/, screen)
+ # because pager is not invoked, foobar will be evaluated
+ assert_match(/foobar/, screen)
+ end
+
+ def test_long_evaluation_output_is_paged
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ require "irb/pager"
+ LINES
+ start_terminal(10, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ write("'a' * 80 * 11\n")
+ write("'foo' + 'bar'\n") # eval something to make sure IRB resumes
+ close
+
+ screen = result.join("\n").sub(/\n*\z/, "\n")
+ assert_match(/(a{80}\n){8}/, screen)
+ # because pager is invoked, foobar will not be evaluated
+ assert_not_match(/foobar/, screen)
+ end
+
+ def test_long_evaluation_output_is_preserved_after_paging
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ require "irb/pager"
+ LINES
+ start_terminal(10, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ write("'a' * 80 * 11\n")
+ write("q") # quit pager
+ write("'foo' + 'bar'\n") # eval something to make sure IRB resumes
+ close
+
+ screen = result.join("\n").sub(/\n*\z/, "\n")
+ # confirm pager has exited
+ assert_match(/foobar/, screen)
+ # confirm output is preserved
+ assert_match(/(a{80}\n){6}/, screen)
+ end
+
+ def test_debug_integration_hints_debugger_commands
+ write_irbrc <<~'LINES'
+ IRB.conf[:USE_COLORIZE] = false
+ LINES
+ script = Tempfile.create(["debug", ".rb"])
+ script.write <<~RUBY
+ puts 'start IRB'
+ binding.irb
+ RUBY
+ script.close
+ start_terminal(40, 80, %W{ruby -I#{@pwd}/lib #{script.to_path}}, startup_message: 'start IRB')
+ write("debug\n")
+ write("pp 1\n")
+ write("pp 1")
+ close
+
+ screen = result.join("\n").sub(/\n*\z/, "\n")
+ # submitted input shouldn't contain hint
+ assert_include(screen, "irb:rdbg(main):002> pp 1\n")
+ # unsubmitted input should contain hint
+ assert_include(screen, "irb:rdbg(main):003> pp 1 # debug command\n")
+ ensure
+ File.unlink(script) if script
+ end
+
+ def test_debug_integration_doesnt_hint_non_debugger_commands
+ write_irbrc <<~'LINES'
+ IRB.conf[:USE_COLORIZE] = false
+ LINES
+ script = Tempfile.create(["debug", ".rb"])
+ script.write <<~RUBY
+ puts 'start IRB'
+ binding.irb
+ RUBY
+ script.close
+ start_terminal(40, 80, %W{ruby -I#{@pwd}/lib #{script.to_path}}, startup_message: 'start IRB')
+ write("debug\n")
+ write("foo")
+ close
+
+ screen = result.join("\n").sub(/\n*\z/, "\n")
+ assert_include(screen, "irb:rdbg(main):002> foo\n")
+ ensure
+ File.unlink(script) if script
+ end
+
private
def write_irbrc(content)
diff --git a/test/json/json_addition_test.rb b/test/json/json_addition_test.rb
index 614c735567..3a7a58176a 100644
--- a/test/json/json_addition_test.rb
+++ b/test/json/json_addition_test.rb
@@ -1,5 +1,5 @@
#frozen_string_literal: false
-require 'test_helper'
+require_relative 'test_helper'
require 'json/add/core'
require 'json/add/complex'
require 'json/add/rational'
@@ -183,14 +183,14 @@ class JSONAdditionTest < Test::Unit::TestCase
def test_bigdecimal
assert_equal BigDecimal('3.141', 23), JSON(JSON(BigDecimal('3.141', 23)), :create_additions => true)
assert_equal BigDecimal('3.141', 666), JSON(JSON(BigDecimal('3.141', 666)), :create_additions => true)
- end
+ end if defined?(::BigDecimal)
def test_ostruct
o = OpenStruct.new
# XXX this won't work; o.foo = { :bar => true }
o.foo = { 'bar' => true }
assert_equal o, parse(JSON(o), :create_additions => true)
- end
+ end if defined?(::OpenStruct)
def test_set
s = Set.new([:a, :b, :c, :a])
diff --git a/test/json/json_common_interface_test.rb b/test/json/json_common_interface_test.rb
index 9148b78c8b..c16f6ceaaf 100644
--- a/test/json/json_common_interface_test.rb
+++ b/test/json/json_common_interface_test.rb
@@ -1,5 +1,5 @@
#frozen_string_literal: false
-require 'test_helper'
+require_relative 'test_helper'
require 'stringio'
require 'tempfile'
@@ -99,18 +99,26 @@ class JSONCommonInterfaceTest < Test::Unit::TestCase
def test_dump
too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'
- assert_equal too_deep, dump(eval(too_deep))
- assert_kind_of String, Marshal.dump(eval(too_deep))
- assert_raise(ArgumentError) { dump(eval(too_deep), 100) }
- assert_raise(ArgumentError) { Marshal.dump(eval(too_deep), 100) }
- assert_equal too_deep, dump(eval(too_deep), 101)
- assert_kind_of String, Marshal.dump(eval(too_deep), 101)
- output = StringIO.new
- dump(eval(too_deep), output)
- assert_equal too_deep, output.string
- output = StringIO.new
- dump(eval(too_deep), output, 101)
- assert_equal too_deep, output.string
+ obj = eval(too_deep)
+ assert_equal too_deep, dump(obj)
+ assert_kind_of String, Marshal.dump(obj)
+ assert_raise(ArgumentError) { dump(obj, 100) }
+ assert_raise(ArgumentError) { Marshal.dump(obj, 100) }
+ assert_equal too_deep, dump(obj, 101)
+ assert_kind_of String, Marshal.dump(obj, 101)
+
+ assert_equal too_deep, JSON.dump(obj, StringIO.new, 101, strict: false).string
+ assert_equal too_deep, dump(obj, StringIO.new, 101, strict: false).string
+ assert_raise(JSON::GeneratorError) { JSON.dump(Object.new, StringIO.new, 101, strict: true).string }
+ assert_raise(JSON::GeneratorError) { dump(Object.new, StringIO.new, 101, strict: true).string }
+
+ assert_equal too_deep, dump(obj, nil, nil, strict: false)
+ assert_equal too_deep, dump(obj, nil, 101, strict: false)
+ assert_equal too_deep, dump(obj, StringIO.new, nil, strict: false).string
+ assert_equal too_deep, dump(obj, nil, strict: false)
+ assert_equal too_deep, dump(obj, 101, strict: false)
+ assert_equal too_deep, dump(obj, StringIO.new, strict: false).string
+ assert_equal too_deep, dump(obj, strict: false)
end
def test_dump_should_modify_defaults
diff --git a/test/json/json_encoding_test.rb b/test/json/json_encoding_test.rb
index cc7b71553a..be87f3c3d6 100644
--- a/test/json/json_encoding_test.rb
+++ b/test/json/json_encoding_test.rb
@@ -1,6 +1,5 @@
-# encoding: utf-8
-#frozen_string_literal: false
-require 'test_helper'
+# frozen_string_literal: false
+require_relative 'test_helper'
class JSONEncodingTest < Test::Unit::TestCase
include JSON
@@ -86,9 +85,7 @@ class JSONEncodingTest < Test::Unit::TestCase
def test_chars
(0..0x7f).each do |i|
json = '["\u%04x"]' % i
- if RUBY_VERSION >= "1.9."
- i = i.chr
- end
+ i = i.chr
assert_equal i, parse(json).first[0]
if i == ?\b
generated = generate(["" << i])
diff --git a/test/json/json_ext_parser_test.rb b/test/json/json_ext_parser_test.rb
index c5a030ea8a..f49f88b596 100644
--- a/test/json/json_ext_parser_test.rb
+++ b/test/json/json_ext_parser_test.rb
@@ -1,8 +1,10 @@
#frozen_string_literal: false
-require 'test_helper'
+require_relative 'test_helper'
class JSONExtParserTest < Test::Unit::TestCase
if defined?(JSON::Ext::Parser)
+ include JSON
+
def test_allocate
parser = JSON::Ext::Parser.new("{}")
assert_raise(TypeError, '[ruby-core:35079]') do
@@ -11,5 +13,22 @@ class JSONExtParserTest < Test::Unit::TestCase
parser = JSON::Ext::Parser.allocate
assert_raise(TypeError, '[ruby-core:35079]') { parser.source }
end
+
+ def test_error_messages
+ ex = assert_raise(ParserError) { parse('Infinity') }
+ assert_equal "unexpected token at 'Infinity'", ex.message
+
+ unless RUBY_PLATFORM =~ /java/
+ ex = assert_raise(ParserError) { parse('-Infinity') }
+ assert_equal "unexpected token at '-Infinity'", ex.message
+ end
+
+ ex = assert_raise(ParserError) { parse('NaN') }
+ assert_equal "unexpected token at 'NaN'", ex.message
+ end
+
+ def parse(json)
+ JSON::Ext::Parser.new(json).parse
+ end
end
end
diff --git a/test/json/json_fixtures_test.rb b/test/json/json_fixtures_test.rb
index 845abb4867..acc8749965 100644
--- a/test/json/json_fixtures_test.rb
+++ b/test/json/json_fixtures_test.rb
@@ -1,5 +1,5 @@
#frozen_string_literal: false
-require 'test_helper'
+require_relative 'test_helper'
class JSONFixturesTest < Test::Unit::TestCase
def setup
diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb
index b0f9a01759..526bb8c1fe 100644..100755
--- a/test/json/json_generator_test.rb
+++ b/test/json/json_generator_test.rb
@@ -1,8 +1,7 @@
#!/usr/bin/env ruby
-# encoding: utf-8
# frozen_string_literal: false
-require 'test_helper'
+require_relative 'test_helper'
class JSONGeneratorTest < Test::Unit::TestCase
include JSON
@@ -62,6 +61,14 @@ EOT
assert_equal '666', generate(666)
end
+ def test_dump_unenclosed_hash
+ assert_equal '{"a":1,"b":2}', dump(a: 1, b: 2)
+ end
+
+ def test_dump_strict
+ assert_equal '{}', dump({}, strict: true)
+ end
+
def test_generate_pretty
json = pretty_generate({})
assert_equal(<<'EOT'.chomp, json)
@@ -149,7 +156,8 @@ EOT
:ascii_only => false,
:buffer_initial_length => 1024,
:depth => 0,
- :escape_slash => false,
+ :script_safe => false,
+ :strict => false,
:indent => " ",
:max_nesting => 100,
:object_nl => "\n",
@@ -166,7 +174,8 @@ EOT
:ascii_only => false,
:buffer_initial_length => 1024,
:depth => 0,
- :escape_slash => false,
+ :script_safe => false,
+ :strict => false,
:indent => "",
:max_nesting => 100,
:object_nl => "",
@@ -183,7 +192,8 @@ EOT
:ascii_only => false,
:buffer_initial_length => 1024,
:depth => 0,
- :escape_slash => false,
+ :script_safe => false,
+ :strict => false,
:indent => "",
:max_nesting => 0,
:object_nl => "",
@@ -233,7 +243,7 @@ EOT
def test_gc
if respond_to?(:assert_in_out_err) && !(RUBY_PLATFORM =~ /java/)
- assert_in_out_err(%w[-rjson --disable-gems], <<-EOS, [], [])
+ assert_in_out_err(%w[-rjson], <<-EOS, [], [])
bignum_too_long_to_embed_as_string = 1234567890123456789012345
expect = bignum_too_long_to_embed_as_string.to_s
GC.stress = true
@@ -336,7 +346,13 @@ EOT
def test_json_generate
assert_raise JSON::GeneratorError do
- assert_equal true, generate(["\xea"])
+ generate(["\xea"])
+ end
+ end
+
+ def test_json_generate_unsupported_types
+ assert_raise JSON::GeneratorError do
+ generate(Object.new, strict: true)
end
end
@@ -370,6 +386,18 @@ EOT
#
data = [ '/' ]
json = '["\/"]'
+ assert_equal json, generate(data, :script_safe => true)
+ #
+ data = [ "\u2028\u2029" ]
+ json = '["\u2028\u2029"]'
+ assert_equal json, generate(data, :script_safe => true)
+ #
+ data = [ "ABC \u2028 DEF \u2029 GHI" ]
+ json = '["ABC \u2028 DEF \u2029 GHI"]'
+ assert_equal json, generate(data, :script_safe => true)
+ #
+ data = [ "/\u2028\u2029" ]
+ json = '["\/\u2028\u2029"]'
assert_equal json, generate(data, :escape_slash => true)
#
data = ['"']
@@ -391,12 +419,13 @@ EOT
end
end
- if defined?(JSON::Ext::Generator)
+ if defined?(JSON::Ext::Generator) and RUBY_PLATFORM != "java"
def test_string_ext_included_calls_super
included = false
- Module.alias_method(:included_orig, :included)
- Module.define_method(:included) do |base|
+ Module.send(:alias_method, :included_orig, :included)
+ Module.send(:remove_method, :included)
+ Module.send(:define_method, :included) do |base|
included_orig(base)
included = true
end
@@ -408,8 +437,9 @@ EOT
assert included
ensure
if Module.private_method_defined?(:included_orig)
- Module.alias_method(:included, :included_orig)
- Module.remove_method(:included_orig)
+ Module.send(:remove_method, :included) if Module.method_defined?(:included)
+ Module.send(:alias_method, :included, :included_orig)
+ Module.send(:remove_method, :included_orig)
end
end
end
diff --git a/test/json/json_generic_object_test.rb b/test/json/json_generic_object_test.rb
index 82742dcd63..d6d7e30816 100644
--- a/test/json/json_generic_object_test.rb
+++ b/test/json/json_generic_object_test.rb
@@ -1,5 +1,5 @@
#frozen_string_literal: false
-require 'test_helper'
+require_relative 'test_helper'
class JSONGenericObjectTest < Test::Unit::TestCase
include JSON
@@ -79,4 +79,4 @@ class JSONGenericObjectTest < Test::Unit::TestCase
ensure
JSON::GenericObject.json_creatable = false
end
-end
+end if defined?(JSON::GenericObject)
diff --git a/test/json/json_parser_test.rb b/test/json/json_parser_test.rb
index 146ff7c047..49c7c8565d 100644
--- a/test/json/json_parser_test.rb
+++ b/test/json/json_parser_test.rb
@@ -1,10 +1,16 @@
# encoding: utf-8
# frozen_string_literal: false
-require 'test_helper'
+require_relative 'test_helper'
require 'stringio'
require 'tempfile'
-require 'ostruct'
-require 'bigdecimal'
+begin
+ require 'ostruct'
+rescue LoadError
+end
+begin
+ require 'bigdecimal'
+rescue LoadError
+end
class JSONParserTest < Test::Unit::TestCase
include JSON
@@ -21,6 +27,9 @@ class JSONParserTest < Test::Unit::TestCase
end if defined?(Encoding::UTF_16)
def test_error_message_encoding
+ # https://github.com/flori/json/actions/runs/6478148162/job/17589572890
+ pend if RUBY_ENGINE == 'truffleruby'
+
bug10705 = '[ruby-core:67386] [Bug #10705]'
json = ".\"\xE2\x88\x9A\"".force_encoding(Encoding::UTF_8)
e = assert_raise(JSON::ParserError) {
@@ -113,7 +122,7 @@ class JSONParserTest < Test::Unit::TestCase
def test_parse_bigdecimals
assert_equal(BigDecimal, JSON.parse('{"foo": 9.01234567890123456789}', decimal_class: BigDecimal)["foo"].class)
assert_equal(BigDecimal("0.901234567890123456789E1"),JSON.parse('{"foo": 9.01234567890123456789}', decimal_class: BigDecimal)["foo"] )
- end
+ end if defined?(::BigDecimal)
def test_parse_string_mixed_unicode
assert_equal(["éé"], JSON.parse("[\"\\u00e9é\"]"))
@@ -406,21 +415,6 @@ EOT
end
end
- class SubOpenStruct < OpenStruct
- def [](k)
- __send__(k)
- end
-
- def []=(k, v)
- @item_set = true
- __send__("#{k}=", v)
- end
-
- def item_set?
- @item_set
- end
- end
-
def test_parse_object_custom_hash_derived_class
res = parse('{"foo":"bar"}', :object_class => SubHash)
assert_equal({"foo" => "bar"}, res)
@@ -428,24 +422,41 @@ EOT
assert res.item_set?
end
- def test_parse_object_custom_non_hash_derived_class
- res = parse('{"foo":"bar"}', :object_class => SubOpenStruct)
- assert_equal "bar", res.foo
- assert_equal(SubOpenStruct, res.class)
- assert res.item_set?
- end
+ if defined?(::OpenStruct)
+ class SubOpenStruct < OpenStruct
+ def [](k)
+ __send__(k)
+ end
- def test_parse_generic_object
- res = parse(
- '{"foo":"bar", "baz":{}}',
- :object_class => JSON::GenericObject
- )
- assert_equal(JSON::GenericObject, res.class)
- assert_equal "bar", res.foo
- assert_equal "bar", res["foo"]
- assert_equal "bar", res[:foo]
- assert_equal "bar", res.to_hash[:foo]
- assert_equal(JSON::GenericObject, res.baz.class)
+ def []=(k, v)
+ @item_set = true
+ __send__("#{k}=", v)
+ end
+
+ def item_set?
+ @item_set
+ end
+ end
+
+ def test_parse_object_custom_non_hash_derived_class
+ res = parse('{"foo":"bar"}', :object_class => SubOpenStruct)
+ assert_equal "bar", res.foo
+ assert_equal(SubOpenStruct, res.class)
+ assert res.item_set?
+ end
+
+ def test_parse_generic_object
+ res = parse(
+ '{"foo":"bar", "baz":{}}',
+ :object_class => JSON::GenericObject
+ )
+ assert_equal(JSON::GenericObject, res.class)
+ assert_equal "bar", res.foo
+ assert_equal "bar", res["foo"]
+ assert_equal "bar", res[:foo]
+ assert_equal "bar", res.to_hash[:foo]
+ assert_equal(JSON::GenericObject, res.baz.class)
+ end
end
def test_generate_core_subclasses_with_new_to_json
diff --git a/test/json/json_string_matching_test.rb b/test/json/json_string_matching_test.rb
index 5d55dc31b0..b9cf904aaa 100644
--- a/test/json/json_string_matching_test.rb
+++ b/test/json/json_string_matching_test.rb
@@ -1,5 +1,5 @@
#frozen_string_literal: false
-require 'test_helper'
+require_relative 'test_helper'
require 'time'
class JSONStringMatchingTest < Test::Unit::TestCase
diff --git a/test/json/ractor_test.rb b/test/json/ractor_test.rb
index 71105e55ec..ba9bf7a7f6 100644
--- a/test/json/ractor_test.rb
+++ b/test/json/ractor_test.rb
@@ -1,7 +1,11 @@
-# encoding: utf-8
# frozen_string_literal: false
-require 'test_helper'
+require_relative 'test_helper'
+
+begin
+ require_relative './lib/helper'
+rescue LoadError
+end
class JSONInRactorTest < Test::Unit::TestCase
def test_generate
diff --git a/test/lib/!Nothing_to_test.rb b/test/lib/!Nothing_to_test.rb
new file mode 100644
index 0000000000..c1f4c439d3
--- /dev/null
+++ b/test/lib/!Nothing_to_test.rb
@@ -0,0 +1,5 @@
+### Empty test file for chkbuild ###
+
+# If chkbuild fails with `test-all`, each child file/directory is
+# executed individually, but test-unit fails if there are no test
+# files in the specified directory.
diff --git a/test/lib/jit_support.rb b/test/lib/jit_support.rb
index 3f21367987..cf3baaaeb7 100644
--- a/test/lib/jit_support.rb
+++ b/test/lib/jit_support.rb
@@ -9,6 +9,10 @@ module JITSupport
@yjit_supported = ![nil, 'no'].include?(RbConfig::CONFIG['YJIT_SUPPORT'])
end
+ def yjit_enabled?
+ defined?(RubyVM::YJIT.enabled?) && RubyVM::YJIT.enabled?
+ end
+
def yjit_force_enabled?
"#{RbConfig::CONFIG['CFLAGS']} #{RbConfig::CONFIG['CPPFLAGS']}".match?(/(\A|\s)-D ?YJIT_FORCE_ENABLE\b/)
end
@@ -19,6 +23,10 @@ module JITSupport
@rjit_supported = ![nil, 'no'].include?(RbConfig::CONFIG['RJIT_SUPPORT'])
end
+ def rjit_enabled?
+ defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?
+ end
+
def rjit_force_enabled?
"#{RbConfig::CONFIG['CFLAGS']} #{RbConfig::CONFIG['CPPFLAGS']}".match?(/(\A|\s)-D ?RJIT_FORCE_ENABLE\b/)
end
diff --git a/test/mkmf/test_config.rb b/test/mkmf/test_config.rb
index 7211c61d53..0d2cb3751c 100644
--- a/test/mkmf/test_config.rb
+++ b/test/mkmf/test_config.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: false
require_relative 'base'
+require 'tempfile'
class TestMkmfConfig < TestMkmf
def test_dir_config
@@ -27,4 +28,30 @@ class TestMkmfConfig < TestMkmf
assert_equal(false, with_config("foo"))
end;
end
+
+ def test_with_target_rbconfig
+ Tempfile.create(%w"rbconfig .rb", ".") do |tmp|
+ tmp.puts <<~'end;'
+ module RbConfig
+ CONFIG = {}
+ MAKEFILE_CONFIG = {}
+
+ def self.fire_update!(key, value); end
+ def self.expand(val, config = CONFIG); val; end
+ end;
+ ::RbConfig::CONFIG.each do |k, v|
+ tmp.puts " CONFIG[#{k.dump}] = #{v.dump}"
+ end
+ ::RbConfig::MAKEFILE_CONFIG.each do |k, v|
+ tmp.puts " MAKEFILE_CONFIG[#{k.dump}] = #{v.dump}"
+ end
+ tmp.puts " CONFIG['testing-only'] = 'ok'"
+ tmp.puts "end"
+ tmp.close
+ assert_separately([], ["--target-rbconfig=#{tmp.path}"], <<-'end;')
+ assert_equal("ok", MakeMakefile::RbConfig::CONFIG["testing-only"])
+ assert_not_equal(::RbConfig, MakeMakefile::RbConfig)
+ end;
+ end
+ end
end
diff --git a/test/monitor/test_monitor.rb b/test/monitor/test_monitor.rb
index 8ff6d006df..4c55afca6c 100644
--- a/test/monitor/test_monitor.rb
+++ b/test/monitor/test_monitor.rb
@@ -300,6 +300,8 @@ class TestMonitor < Test::Unit::TestCase
result4 = cond.wait
assert_equal(true, result4)
assert_equal("bar", c)
+ ensure
+ queue3.enq(nil)
end
end
assert_join_threads([th, th2])
diff --git a/test/net/fixtures/Makefile b/test/net/fixtures/Makefile
index b2bc9c7368..88c232e3b6 100644
--- a/test/net/fixtures/Makefile
+++ b/test/net/fixtures/Makefile
@@ -5,11 +5,11 @@ regen_certs:
make server.crt
cacert.pem: server.key
- openssl req -new -x509 -days 1825 -key server.key -out cacert.pem -text -subj "/C=JP/ST=Shimane/L=Matz-e city/O=Ruby Core Team/CN=Ruby Test CA/emailAddress=security@ruby-lang.org"
+ openssl req -new -x509 -days 3650 -key server.key -out cacert.pem -subj "/C=JP/ST=Shimane/L=Matz-e city/O=Ruby Core Team/CN=Ruby Test CA/emailAddress=security@ruby-lang.org"
server.csr:
- openssl req -new -key server.key -out server.csr -text -subj "/C=JP/ST=Shimane/O=Ruby Core Team/OU=Ruby Test/CN=localhost"
+ openssl req -new -key server.key -out server.csr -subj "/C=JP/ST=Shimane/O=Ruby Core Team/OU=Ruby Test/CN=localhost"
server.crt: server.csr cacert.pem
- openssl x509 -days 1825 -CA cacert.pem -CAkey server.key -set_serial 00 -in server.csr -req -text -out server.crt
+ openssl x509 -days 3650 -CA cacert.pem -CAkey server.key -set_serial 00 -in server.csr -req -out server.crt
rm server.csr
diff --git a/test/net/fixtures/cacert.pem b/test/net/fixtures/cacert.pem
index f623bd62ed..24c83f1c65 100644
--- a/test/net/fixtures/cacert.pem
+++ b/test/net/fixtures/cacert.pem
@@ -1,24 +1,24 @@
-----BEGIN CERTIFICATE-----
-MIID7TCCAtWgAwIBAgIJAIltvxrFAuSnMA0GCSqGSIb3DQEBCwUAMIGMMQswCQYD
-VQQGEwJKUDEQMA4GA1UECAwHU2hpbWFuZTEUMBIGA1UEBwwLTWF0ei1lIGNpdHkx
-FzAVBgNVBAoMDlJ1YnkgQ29yZSBUZWFtMRUwEwYDVQQDDAxSdWJ5IFRlc3QgQ0Ex
-JTAjBgkqhkiG9w0BCQEWFnNlY3VyaXR5QHJ1YnktbGFuZy5vcmcwHhcNMTkwMTAy
-MDI1ODI4WhcNMjQwMTAxMDI1ODI4WjCBjDELMAkGA1UEBhMCSlAxEDAOBgNVBAgM
-B1NoaW1hbmUxFDASBgNVBAcMC01hdHotZSBjaXR5MRcwFQYDVQQKDA5SdWJ5IENv
-cmUgVGVhbTEVMBMGA1UEAwwMUnVieSBUZXN0IENBMSUwIwYJKoZIhvcNAQkBFhZz
-ZWN1cml0eUBydWJ5LWxhbmcub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
-CgKCAQEAznlbjRVhz1NlutHVrhcGnK8W0qug2ujKXv1njSC4U6nJF6py7I9EeehV
-SaKePyv+I9z3K1LnfUHOtUbdwdKC77yN66A6q2aqzu5q09/NSykcZGOIF0GuItYI
-3nvW3IqBddff2ffsyR+9pBjfb5AIPP08WowF9q4s1eGULwZc4w2B8PFhtxYANd7d
-BvGLXFlcufv9tDtzyRi4t7eqxCRJkZQIZNZ6DHHIJrNxejOILfHLarI12yk8VK6L
-2LG4WgGqyeePiRyd1o1MbuiAFYqAwpXNUbRKg5NaZGwBHZk8UZ+uFKt1QMBURO5R
-WFy1c349jbWszTqFyL4Lnbg9HhAowQIDAQABo1AwTjAdBgNVHQ4EFgQU9tEiKdU9
-I9derQyc5nWPnc34nVMwHwYDVR0jBBgwFoAU9tEiKdU9I9derQyc5nWPnc34nVMw
-DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAxj7F/u3C3fgq24N7hGRA
-of7ClFQxGmo/IGT0AISzW3HiVYiFaikKhbO1NwD9aBpD8Zwe62sCqMh8jGV/b0+q
-aOORnWYNy2R6r9FkASAglmdF6xn3bhgGD5ls4pCvcG9FynGnGc24g6MrjFNrBYUS
-2iIZsg36i0IJswo/Dy6HLphCms2BMCD3DeWtfjePUiTmQHJo6HsQIKP/u4N4Fvee
-uMBInei2M4VU74fLXbmKl1F9AEX7JDP3BKSZG19Ch5pnUo4uXM1uNTGsi07P4Y0s
-K44+SKBC0bYEFbDK0eQWMrX3kIhkPxyIWhxdq9/NqPYjShuSEAhA6CSpmRg0pqc+
-mA==
+MIID+zCCAuOgAwIBAgIUGMvHl3EhtKPKcgc3NQSAYfFuC+8wDQYJKoZIhvcNAQEL
+BQAwgYwxCzAJBgNVBAYTAkpQMRAwDgYDVQQIDAdTaGltYW5lMRQwEgYDVQQHDAtN
+YXR6LWUgY2l0eTEXMBUGA1UECgwOUnVieSBDb3JlIFRlYW0xFTATBgNVBAMMDFJ1
+YnkgVGVzdCBDQTElMCMGCSqGSIb3DQEJARYWc2VjdXJpdHlAcnVieS1sYW5nLm9y
+ZzAeFw0yNDAxMDExMTQ3MjNaFw0zMzEyMjkxMTQ3MjNaMIGMMQswCQYDVQQGEwJK
+UDEQMA4GA1UECAwHU2hpbWFuZTEUMBIGA1UEBwwLTWF0ei1lIGNpdHkxFzAVBgNV
+BAoMDlJ1YnkgQ29yZSBUZWFtMRUwEwYDVQQDDAxSdWJ5IFRlc3QgQ0ExJTAjBgkq
+hkiG9w0BCQEWFnNlY3VyaXR5QHJ1YnktbGFuZy5vcmcwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQCw+egZQ6eumJKq3hfKfED4dE/tL4FI5sjqont9ABVI
++1GSqyi1bFBgsRjM0THllIdMbKmJtWwnKW8J+5OgNN8y6Xxv8JmM/Y5vQt2lis0f
+qXmG8UTz0VTWdlAXXmhUs6lSADvAaIe4RVrCsZ97L3ZQTryY7JRVcbB4khUN3Gp0
+yg+801SXzoFTTa+UGIRLE66jH51aa5VXu99hnv1OiH8tQrjdi8mH6uG/icq4XuIe
+NWMF32wHqIOOPvQcWV3M5D2vxJEj702Ku6k9OQXkAo17qRSEonWW4HtLbtmS8He1
+JNPc/n3dVUm+fM6NoDXPoLP7j55G9zKyqGtGAWXAj1MTAgMBAAGjUzBRMB0GA1Ud
+DgQWBBSJGVleDvFp9cu9R+E0/OKYzGkwkTAfBgNVHSMEGDAWgBSJGVleDvFp9cu9
+R+E0/OKYzGkwkTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBl
+8GLB8skAWlkSw/FwbUmEV3zyqu+p7PNP5YIYoZs0D74e7yVulGQ6PKMZH5hrZmHo
+orFSQU+VUUirG8nDGj7Rzce8WeWBxsaDGC8CE2dq6nC6LuUwtbdMnBrH0LRWAz48
+jGFF3jHtVz8VsGfoZTZCjukWqNXvU6hETT9GsfU+PZqbqcTVRPH52+XgYayKdIbD
+r97RM4X3+aXBHcUW0b76eyyi65RR/Xtvn8ioZt2AdX7T2tZzJyXJN3Hupp77s6Ui
+AZR35SToHCZeTZD12YBvLBdaTPLZN7O/Q/aAO9ZiJaZ7SbFOjz813B2hxXab4Fob
+2uJX6eMWTVxYK5D4M9lm
-----END CERTIFICATE-----
diff --git a/test/net/fixtures/server.crt b/test/net/fixtures/server.crt
index 5ca78a6d14..5d2923795d 100644
--- a/test/net/fixtures/server.crt
+++ b/test/net/fixtures/server.crt
@@ -1,82 +1,21 @@
-Certificate:
- Data:
- Version: 3 (0x2)
- Serial Number: 2 (0x2)
- Signature Algorithm: sha256WithRSAEncryption
- Issuer: C=JP, ST=Shimane, L=Matz-e city, O=Ruby Core Team, CN=Ruby Test CA/emailAddress=security@ruby-lang.org
- Validity
- Not Before: Jan 2 03:27:13 2019 GMT
- Not After : Jan 1 03:27:13 2024 GMT
- Subject: C=JP, ST=Shimane, O=Ruby Core Team, OU=Ruby Test, CN=localhost
- Subject Public Key Info:
- Public Key Algorithm: rsaEncryption
- Public-Key: (2048 bit)
- Modulus:
- 00:e8:da:9c:01:2e:2b:10:ec:49:cd:5e:07:13:07:
- 9c:70:9e:c6:74:bc:13:c2:e1:6f:c6:82:fd:e3:48:
- e0:2c:a5:68:c7:9e:42:de:60:54:65:e6:6a:14:57:
- 7a:30:d0:cc:b5:b6:d9:c3:d2:df:c9:25:97:54:67:
- cf:f6:be:5e:cb:8b:ee:03:c5:e1:e2:f9:e7:f7:d1:
- 0c:47:f0:b8:da:33:5a:ad:41:ad:e7:b5:a2:7b:b7:
- bf:30:da:60:f8:e3:54:a2:bc:3a:fd:1b:74:d9:dc:
- 74:42:e9:29:be:df:ac:b4:4f:eb:32:f4:06:f1:e1:
- 8c:4b:a8:8b:fb:29:e7:b1:bf:1d:01:ee:73:0f:f9:
- 40:dc:d5:15:79:d9:c6:73:d0:c0:dd:cb:e4:da:19:
- 47:80:c6:14:04:72:fd:9a:7c:8f:11:82:76:49:04:
- 79:cc:f2:5c:31:22:95:13:3e:5d:40:a6:4d:e0:a3:
- 02:26:7d:52:3b:bb:ed:65:a1:0f:ed:6b:b0:3c:d4:
- de:61:15:5e:d3:dd:68:09:9f:4a:57:a5:c2:a9:6d:
- 86:92:c5:f4:a4:d4:b7:13:3b:52:63:24:05:e2:cc:
- e3:8a:3c:d4:35:34:2b:10:bb:58:72:e7:e1:8d:1d:
- 74:8c:61:16:20:3d:d0:1c:4e:8f:6e:fd:fe:64:10:
- 4f:41
- Exponent: 65537 (0x10001)
- X509v3 extensions:
- X509v3 Basic Constraints:
- CA:FALSE
- Netscape Comment:
- OpenSSL Generated Certificate
- X509v3 Subject Key Identifier:
- ED:28:C2:7E:AB:4B:C8:E8:FE:55:6D:66:95:31:1C:2D:60:F9:02:36
- X509v3 Authority Key Identifier:
- keyid:F6:D1:22:29:D5:3D:23:D7:5E:AD:0C:9C:E6:75:8F:9D:CD:F8:9D:53
-
- Signature Algorithm: sha256WithRSAEncryption
- 1d:b8:c5:8b:72:41:20:65:ad:27:6f:15:63:06:26:12:8d:9c:
- ad:ca:f4:db:97:b4:90:cb:ff:35:94:bb:2a:a7:a1:ab:1e:35:
- 2d:a5:3f:c9:24:b0:1a:58:89:75:3e:81:0a:2c:4f:98:f9:51:
- fb:c0:a3:09:d0:0a:9b:e7:a2:b7:c3:60:40:c8:f4:6d:b2:6a:
- 56:12:17:4c:00:24:31:df:9c:60:ae:b1:68:54:a9:e6:b5:4a:
- 04:e6:92:05:86:d9:5a:dc:96:30:a5:58:de:14:99:0f:e5:15:
- 89:3e:9b:eb:80:e3:bd:83:c3:ea:33:35:4b:3e:2f:d3:0d:64:
- 93:67:7f:8d:f5:3f:0c:27:bc:37:5a:cc:d6:47:16:af:5a:62:
- d2:da:51:f8:74:06:6b:24:ad:28:68:08:98:37:7d:ed:0e:ab:
- 1e:82:61:05:d0:ba:75:a0:ab:21:b0:9a:fd:2b:54:86:1d:0d:
- 1f:c2:d4:77:1f:72:26:5e:ad:8a:9f:09:36:6d:44:be:74:c2:
- 5a:3e:ff:5c:9d:75:d6:38:7b:c5:39:f9:44:6e:a1:d1:8e:ff:
- 63:db:c4:bb:c6:91:92:ca:5c:60:9b:1d:eb:0a:de:08:ee:bf:
- da:76:03:65:62:29:8b:f8:7f:c7:86:73:1e:f6:1f:2d:89:69:
- fd:be:bd:6e
-----BEGIN CERTIFICATE-----
-MIID4zCCAsugAwIBAgIBAjANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UEBhMCSlAx
-EDAOBgNVBAgMB1NoaW1hbmUxFDASBgNVBAcMC01hdHotZSBjaXR5MRcwFQYDVQQK
-DA5SdWJ5IENvcmUgVGVhbTEVMBMGA1UEAwwMUnVieSBUZXN0IENBMSUwIwYJKoZI
-hvcNAQkBFhZzZWN1cml0eUBydWJ5LWxhbmcub3JnMB4XDTE5MDEwMjAzMjcxM1oX
-DTI0MDEwMTAzMjcxM1owYDELMAkGA1UEBhMCSlAxEDAOBgNVBAgMB1NoaW1hbmUx
-FzAVBgNVBAoMDlJ1YnkgQ29yZSBUZWFtMRIwEAYDVQQLDAlSdWJ5IFRlc3QxEjAQ
-BgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
-AOjanAEuKxDsSc1eBxMHnHCexnS8E8Lhb8aC/eNI4CylaMeeQt5gVGXmahRXejDQ
-zLW22cPS38kll1Rnz/a+XsuL7gPF4eL55/fRDEfwuNozWq1Bree1onu3vzDaYPjj
-VKK8Ov0bdNncdELpKb7frLRP6zL0BvHhjEuoi/sp57G/HQHucw/5QNzVFXnZxnPQ
-wN3L5NoZR4DGFARy/Zp8jxGCdkkEeczyXDEilRM+XUCmTeCjAiZ9Uju77WWhD+1r
-sDzU3mEVXtPdaAmfSlelwqlthpLF9KTUtxM7UmMkBeLM44o81DU0KxC7WHLn4Y0d
-dIxhFiA90BxOj279/mQQT0ECAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgBhvhC
-AQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFO0o
-wn6rS8jo/lVtZpUxHC1g+QI2MB8GA1UdIwQYMBaAFPbRIinVPSPXXq0MnOZ1j53N
-+J1TMA0GCSqGSIb3DQEBCwUAA4IBAQAduMWLckEgZa0nbxVjBiYSjZytyvTbl7SQ
-y/81lLsqp6GrHjUtpT/JJLAaWIl1PoEKLE+Y+VH7wKMJ0Aqb56K3w2BAyPRtsmpW
-EhdMACQx35xgrrFoVKnmtUoE5pIFhtla3JYwpVjeFJkP5RWJPpvrgOO9g8PqMzVL
-Pi/TDWSTZ3+N9T8MJ7w3WszWRxavWmLS2lH4dAZrJK0oaAiYN33tDqsegmEF0Lp1
-oKshsJr9K1SGHQ0fwtR3H3ImXq2Knwk2bUS+dMJaPv9cnXXWOHvFOflEbqHRjv9j
-28S7xpGSylxgmx3rCt4I7r/adgNlYimL+H/HhnMe9h8tiWn9vr1u
+MIIDYTCCAkkCAQAwDQYJKoZIhvcNAQELBQAwgYwxCzAJBgNVBAYTAkpQMRAwDgYD
+VQQIDAdTaGltYW5lMRQwEgYDVQQHDAtNYXR6LWUgY2l0eTEXMBUGA1UECgwOUnVi
+eSBDb3JlIFRlYW0xFTATBgNVBAMMDFJ1YnkgVGVzdCBDQTElMCMGCSqGSIb3DQEJ
+ARYWc2VjdXJpdHlAcnVieS1sYW5nLm9yZzAeFw0yNDAxMDExMTQ3MjNaFw0zMzEy
+MjkxMTQ3MjNaMGAxCzAJBgNVBAYTAkpQMRAwDgYDVQQIDAdTaGltYW5lMRcwFQYD
+VQQKDA5SdWJ5IENvcmUgVGVhbTESMBAGA1UECwwJUnVieSBUZXN0MRIwEAYDVQQD
+DAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCw+egZ
+Q6eumJKq3hfKfED4dE/tL4FI5sjqont9ABVI+1GSqyi1bFBgsRjM0THllIdMbKmJ
+tWwnKW8J+5OgNN8y6Xxv8JmM/Y5vQt2lis0fqXmG8UTz0VTWdlAXXmhUs6lSADvA
+aIe4RVrCsZ97L3ZQTryY7JRVcbB4khUN3Gp0yg+801SXzoFTTa+UGIRLE66jH51a
+a5VXu99hnv1OiH8tQrjdi8mH6uG/icq4XuIeNWMF32wHqIOOPvQcWV3M5D2vxJEj
+702Ku6k9OQXkAo17qRSEonWW4HtLbtmS8He1JNPc/n3dVUm+fM6NoDXPoLP7j55G
+9zKyqGtGAWXAj1MTAgMBAAEwDQYJKoZIhvcNAQELBQADggEBACtGNdj5TEtnJBYp
+M+LhBeU3oNteldfycEm993gJp6ghWZFg23oX8fVmyEeJr/3Ca9bAgDqg0t9a0npN
+oWKEY6wVKqcHgu3gSvThF5c9KhGbeDDmlTSVVNQmXWX0K2d4lS2cwZHH8mCm2mrY
+PDqlEkSc7k4qSiqigdS8i80Yk+lDXWsm8CjsiC93qaRM7DnS0WPQR0c16S95oM6G
+VklFKUSDAuFjw9aVWA/nahOucjn0w5fVW6lyIlkBslC1ChlaDgJmvhz+Ol3iMsE0
+kAmFNu2KKPVrpMWaBID49QwQTDyhetNLaVVFM88iUdA9JDoVMEuP1mm39JqyzHTu
+uBrdP4Q=
-----END CERTIFICATE-----
diff --git a/test/net/fixtures/server.key b/test/net/fixtures/server.key
index 7f2380e71e..6a83d5bcf4 100644
--- a/test/net/fixtures/server.key
+++ b/test/net/fixtures/server.key
@@ -1,28 +1,27 @@
------BEGIN PRIVATE KEY-----
-MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDo2pwBLisQ7EnN
-XgcTB5xwnsZ0vBPC4W/Ggv3jSOAspWjHnkLeYFRl5moUV3ow0My1ttnD0t/JJZdU
-Z8/2vl7Li+4DxeHi+ef30QxH8LjaM1qtQa3ntaJ7t78w2mD441SivDr9G3TZ3HRC
-6Sm+36y0T+sy9Abx4YxLqIv7Keexvx0B7nMP+UDc1RV52cZz0MDdy+TaGUeAxhQE
-cv2afI8RgnZJBHnM8lwxIpUTPl1Apk3gowImfVI7u+1loQ/ta7A81N5hFV7T3WgJ
-n0pXpcKpbYaSxfSk1LcTO1JjJAXizOOKPNQ1NCsQu1hy5+GNHXSMYRYgPdAcTo9u
-/f5kEE9BAgMBAAECggEBAOHkwhc7DLh8IhTDNSW26oMu5OP2WU1jmiYAigDmf+OQ
-DBgrZj+JQBci8qINQxL8XLukSZn5hvQCLc7Kbyu1/wyEEUFDxSGGwwzclodr9kho
-LX2LDASPZrOSzD2+fPi2wTKmXKuS6Uc44OjQfZkYMNkz9r4Vkm8xGgOD3VipjIYX
-QXlhhdqkXZcNABsihCV52GKkDFSVm8jv95YJc5xhoYCy/3a4/qPdF0aT2R7oYUej
-hKrxVDskyooe8Zg/JTydZNV5GQEDmW01/K3r6XGT26oPi1AqMU1gtv/jkW56CRQQ
-1got8smnqM+AV7Slf9R6DauIPdQJ2S8wsr/o8ISBsOECgYEA9YrqEP2gAYSGFXRt
-liw0WI2Ant8BqXS6yvq1jLo/qWhLw/ph4Di73OQ2mpycVTpgfGr2wFPQR1XJ+0Fd
-U+Ir/C3Q7FK4VIGHK7B0zNvZr5tEjlFfeRezo2JMVw5YWeSagIFcSwK+KqCTH9qc
-pw/Eb8nB/4XNcpTZu7Fg0Wc+ooUCgYEA8sVaicn1Wxkpb45a4qfrA6wOr5xdJ4cC
-A5qs7vjX2OdPIQOmoQhdI7bCWFXZzF33wA4YCws6j5wRaySLIJqdms8Gl9QnODy1
-ZlA5gwKToBC/jqPmWAXSKb8EH7cHilaxU9OKnQ7CfwlGLHqjMtjrhR7KHlt3CVRs
-oRmvsjZVXI0CgYAmPedslAO6mMhFSSfULrhMXmV82OCqYrrA6EEkVNGbcdnzAOkD
-gfKIWabDd8bFY10po4Mguy0CHzNhBXIioWQWV5BlbhC1YKMLw+S9DzSdLAKGY9gJ
-xQ4+UQ3wtRQ/k+IYR413RUsW2oFvgZ3KSyNeAb9MK6uuv84VdG/OzVSs/QKBgQDn
-kap//l2EbObiWyaERunckdVcW0lcN+KK75J/TGwPoOwQsLvTpPe65kxRGGrtDsEQ
-uCDk/+v3KkZPLgdrrTAih9FhJ+PVN8tMcb+6IM4SA4fFFr/UPJEwct0LJ3oQ0grJ
-y+HPWFHb/Uurh7t99/4H98uR02sjQh1wOeEmm78mzQKBgQDm+LzGH0se6CXQ6cdZ
-g1JRZeXkDEsrW3hfAsW62xJQmXcWxBoblP9OamMY+A06rM5og3JbDk5Zm6JsOaA8
-wS2gw4ilp46jors4eQey8ux7kB9LzdBoDBBElnsbjLO8oBNZlVcYXg+6BOl/CUi7
-2whRF0FEjKA8ehrNhAq+VFfFNw==
------END PRIVATE KEY-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAsPnoGUOnrpiSqt4XynxA+HRP7S+BSObI6qJ7fQAVSPtRkqso
+tWxQYLEYzNEx5ZSHTGypibVsJylvCfuToDTfMul8b/CZjP2Ob0LdpYrNH6l5hvFE
+89FU1nZQF15oVLOpUgA7wGiHuEVawrGfey92UE68mOyUVXGweJIVDdxqdMoPvNNU
+l86BU02vlBiESxOuox+dWmuVV7vfYZ79Toh/LUK43YvJh+rhv4nKuF7iHjVjBd9s
+B6iDjj70HFldzOQ9r8SRI+9NirupPTkF5AKNe6kUhKJ1luB7S27ZkvB3tSTT3P59
+3VVJvnzOjaA1z6Cz+4+eRvcysqhrRgFlwI9TEwIDAQABAoIBAEEYiyDP29vCzx/+
+dS3LqnI5BjUuJhXUnc6AWX/PCgVAO+8A+gZRgvct7PtZb0sM6P9ZcLrweomlGezI
+FrL0/6xQaa8bBr/ve/a8155OgcjFo6fZEw3Dz7ra5fbSiPmu4/b/kvrg+Br1l77J
+aun6uUAs1f5B9wW+vbR7tzbT/mxaUeDiBzKpe15GwcvbJtdIVMa2YErtRjc1/5B2
+BGVXyvlJv0SIlcIEMsHgnAFOp1ZgQ08aDzvilLq8XVMOahAhP1O2A3X8hKdXPyrx
+IVWE9bS9ptTo+eF6eNl+d7htpKGEZHUxinoQpWEBTv+iOoHsVunkEJ3vjLP3lyI/
+fY0NQ1ECgYEA3RBXAjgvIys2gfU3keImF8e/TprLge1I2vbWmV2j6rZCg5r/AS0u
+pii5CvJ5/T5vfJPNgPBy8B/yRDs+6PJO1GmnlhOkG9JAIPkv0RBZvR0PMBtbp6nT
+Y3yo1lwamBVBfY6rc0sLTzosZh2aGoLzrHNMQFMGaauORzBFpY5lU50CgYEAzPHl
+u5DI6Xgep1vr8QvCUuEesCOgJg8Yh1UqVoY/SmQh6MYAv1I9bLGwrb3WW/7kqIoD
+fj0aQV5buVZI2loMomtU9KY5SFIsPV+JuUpy7/+VE01ZQM5FdY8wiYCQiVZYju9X
+Wz5LxMNoz+gT7pwlLCsC4N+R8aoBk404aF1gum8CgYAJ7VTq7Zj4TFV7Soa/T1eE
+k9y8a+kdoYk3BASpCHJ29M5R2KEA7YV9wrBklHTz8VzSTFTbKHEQ5W5csAhoL5Fo
+qoHzFFi3Qx7MHESQb9qHyolHEMNx6QdsHUn7rlEnaTTyrXh3ifQtD6C0yTmFXUIS
+CW9wKApOrnyKJ9nI0HcuZQKBgQCMtoV6e9VGX4AEfpuHvAAnMYQFgeBiYTkBKltQ
+XwozhH63uMMomUmtSG87Sz1TmrXadjAhy8gsG6I0pWaN7QgBuFnzQ/HOkwTm+qKw
+AsrZt4zeXNwsH7QXHEJCFnCmqw9QzEoZTrNtHJHpNboBuVnYcoueZEJrP8OnUG3r
+UjmopwKBgAqB2KYYMUqAOvYcBnEfLDmyZv9BTVNHbR2lKkMYqv5LlvDaBxVfilE0
+2riO4p6BaAdvzXjKeRrGNEKoHNBpOSfYCOM16NjL8hIZB1CaV3WbT5oY+jp7Mzd5
+7d56RZOE+ERK2uz/7JX9VSsM/LbH9pJibd4e8mikDS9ntciqOH/3
+-----END RSA PRIVATE KEY-----
diff --git a/test/net/http/test_http.rb b/test/net/http/test_http.rb
index 0508645ac5..f0f1bc2d8f 100644
--- a/test/net/http/test_http.rb
+++ b/test/net/http/test_http.rb
@@ -126,10 +126,10 @@ class TestNetHTTP < Test::Unit::TestCase
def test_proxy_address_no_proxy
TestNetHTTPUtils.clean_http_proxy_env do
- http = Net::HTTP.new 'hostname.example', nil, 'proxy.example', nil, nil, nil, 'example'
+ http = Net::HTTP.new 'hostname.example', nil, 'proxy.com', nil, nil, nil, 'example'
assert_nil http.proxy_address
- http = Net::HTTP.new '10.224.1.1', nil, 'proxy.example', nil, nil, nil, 'example,10.224.0.0/22'
+ http = Net::HTTP.new '10.224.1.1', nil, 'proxy.com', nil, nil, nil, 'example,10.224.0.0/22'
assert_nil http.proxy_address
end
end
@@ -1234,6 +1234,16 @@ class TestNetHTTPKeepAlive < Test::Unit::TestCase
}
end
+ def test_http_retry_failed_with_block
+ start {|http|
+ http.max_retries = 10
+ called = 0
+ assert_raise(Errno::ECONNRESET){ http.get('/'){called += 1; raise Errno::ECONNRESET} }
+ assert_equal 1, called
+ }
+ @log_tester = nil
+ end
+
def test_keep_alive_server_close
def @server.run(sock)
sock.close
diff --git a/test/net/http/test_httpresponse.rb b/test/net/http/test_httpresponse.rb
index 99a946748c..01281063cd 100644
--- a/test/net/http/test_httpresponse.rb
+++ b/test/net/http/test_httpresponse.rb
@@ -589,6 +589,41 @@ EOS
assert_equal 'hello', body
end
+ def test_read_body_receiving_no_body
+ io = dummy_io(<<EOS)
+HTTP/1.1 204 OK
+Connection: close
+
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+ res.body_encoding = 'utf-8'
+
+ body = 'something to override'
+
+ res.reading_body io, true do
+ body = res.read_body
+ end
+
+ assert_equal nil, body
+ assert_equal nil, res.body
+ end
+
+ def test_read_body_outside_of_reading_body
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Connection: close
+Content-Length: 0
+
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+
+ assert_raise IOError do
+ res.read_body
+ end
+ end
+
def test_uri_equals
uri = URI 'http://example'
diff --git a/test/net/http/test_https.rb b/test/net/http/test_https.rb
index 72a69af1a5..cf297f3755 100644
--- a/test/net/http/test_https.rb
+++ b/test/net/http/test_https.rb
@@ -148,7 +148,7 @@ class TestNetHTTPS < Test::Unit::TestCase
# support session resuse. Limiting the version to the TLSv1.2 stack allows
# this test to continue to work on LibreSSL 3.2+. LibreSSL may eventually
# support session reuse, but there are no current plans to do so.
- http.ssl_version = :TLSv1
+ http.ssl_version = :TLSv1_2
end
http.start
@@ -167,6 +167,8 @@ class TestNetHTTPS < Test::Unit::TestCase
def test_session_reuse_but_expire
# FIXME: The new_session_cb is known broken for clients in OpenSSL 1.1.0h.
omit if OpenSSL::OPENSSL_LIBRARY_VERSION.include?('OpenSSL 1.1.0h')
+ omit if OpenSSL::OPENSSL_LIBRARY_VERSION.include?('OpenSSL 3.2.')
+ omit if OpenSSL::OPENSSL_LIBRARY_VERSION.include?('OpenSSL 3.3.')
http = Net::HTTP.new(HOST, config("port"))
http.use_ssl = true
@@ -181,7 +183,7 @@ class TestNetHTTPS < Test::Unit::TestCase
http.get("/")
socket = http.instance_variable_get(:@socket).io
- assert_equal false, socket.session_reused?
+ assert_equal false, socket.session_reused?, "NOTE: OpenSSL library version is #{OpenSSL::OPENSSL_LIBRARY_VERSION}"
http.finish
end
diff --git a/test/nkf/test_kconv.rb b/test/nkf/test_kconv.rb
deleted file mode 100644
index 3968cc47dc..0000000000
--- a/test/nkf/test_kconv.rb
+++ /dev/null
@@ -1,82 +0,0 @@
-# frozen_string_literal: false
-require 'test/unit'
-require 'kconv'
-
-class TestKconv < Test::Unit::TestCase
- def setup
- @euc_str = "\
-\xa5\xaa\xa5\xd6\xa5\xb8\xa5\xa7\xa5\xaf\xa5\xc8\xbb\xd8\xb8\xfe\
-\xa5\xd7\xa5\xed\xa5\xb0\xa5\xe9\xa5\xdf\xa5\xf3\xa5\xb0\xb8\xc0\xb8\xec\
-\x52\x75\x62\x79".force_encoding('EUC-JP')
- @utf8_str = "\
-\xe3\x82\xaa\xe3\x83\x96\xe3\x82\xb8\xe3\x82\xa7\
-\xe3\x82\xaf\xe3\x83\x88\xe6\x8c\x87\xe5\x90\x91\
-\xe3\x83\x97\xe3\x83\xad\xe3\x82\xb0\xe3\x83\xa9\xe3\x83\x9f\
-\xe3\x83\xb3\xe3\x82\xb0\xe8\xa8\x80\xe8\xaa\x9e\
-\x52\x75\x62\x79".force_encoding('UTF-8')
- @sjis_str = "\
-\x83\x49\x83\x75\x83\x57\x83\x46\x83\x4e\x83\x67\x8e\x77\x8c\xfc\
-\x83\x76\x83\x8d\x83\x4f\x83\x89\x83\x7e\x83\x93\x83\x4f\x8c\xbe\x8c\xea\
-\x52\x75\x62\x79".force_encoding('Shift_JIS')
- @jis_str = "\
-\x1b\x24\x42\x25\x2a\x25\x56\x25\x38\x25\x27\x25\x2f\x25\x48\x3b\x58\x38\x7e\
-\x25\x57\x25\x6d\x25\x30\x25\x69\x25\x5f\x25\x73\x25\x30\x38\x40\x38\x6c\x1b\x28\x42\
-\x52\x75\x62\x79".force_encoding('ISO-2022-JP')
- end
-
-
- def test_eucjp
- assert(@euc_str.iseuc)
- assert_equal(::Kconv::EUC, Kconv.guess(@euc_str))
- assert_equal(@euc_str, @euc_str.toeuc)
- assert_equal(@euc_str, @sjis_str.toeuc)
- assert_equal(@euc_str, @utf8_str.toeuc)
- assert_equal(@euc_str, @jis_str.toeuc)
- assert_equal(@euc_str, @euc_str.kconv(::NKF::EUC))
- assert_equal(@euc_str, @sjis_str.kconv(::NKF::EUC))
- assert_equal(@euc_str, @utf8_str.kconv(::NKF::EUC))
- assert_equal(@euc_str, @jis_str.kconv(::NKF::EUC))
- end
- def test_shiftjis
- assert(@sjis_str.issjis)
- assert_equal(::Kconv::SJIS, Kconv.guess(@sjis_str))
- assert_equal(@sjis_str, @euc_str.tosjis)
- assert_equal(@sjis_str, @sjis_str.tosjis)
- assert_equal(@sjis_str, @utf8_str.tosjis)
- assert_equal(@sjis_str, @jis_str.tosjis)
- assert_equal(@sjis_str, @euc_str.kconv(::NKF::SJIS))
- assert_equal(@sjis_str, @sjis_str.kconv(::NKF::SJIS))
- assert_equal(@sjis_str, @utf8_str.kconv(::NKF::SJIS))
- assert_equal(@sjis_str, @jis_str.kconv(::NKF::SJIS))
- end
- def test_utf8
- assert(@utf8_str.isutf8)
- assert_equal(::Kconv::UTF8, Kconv.guess(@utf8_str))
- assert_equal(@utf8_str, @euc_str.toutf8)
- assert_equal(@utf8_str, @sjis_str.toutf8)
- assert_equal(@utf8_str, @utf8_str.toutf8)
- assert_equal(@utf8_str, @jis_str.toutf8)
- assert_equal(@utf8_str, @euc_str.kconv(::NKF::UTF8))
- assert_equal(@utf8_str, @sjis_str.kconv(::NKF::UTF8))
- assert_equal(@utf8_str, @utf8_str.kconv(::NKF::UTF8))
- assert_equal(@utf8_str, @jis_str.kconv(::NKF::UTF8))
- end
- def test_jis
- assert_equal(::Kconv::JIS, Kconv.guess(@jis_str))
- assert_equal(@jis_str, @euc_str.tojis)
- assert_equal(@jis_str, @sjis_str.tojis)
- assert_equal(@jis_str, @utf8_str.tojis)
- assert_equal(@jis_str, @jis_str.tojis)
- assert_equal(@jis_str, @euc_str.kconv(::NKF::JIS))
- assert_equal(@jis_str, @sjis_str.kconv(::NKF::JIS))
- assert_equal(@jis_str, @utf8_str.kconv(::NKF::JIS))
- assert_equal(@jis_str, @jis_str.kconv(::NKF::JIS))
- end
- def test_kconv
- str = "\xc2\xa1"
- %w/UTF-8 EUC-JP/.each do |enc|
- s = str.dup.force_encoding(enc)
- assert_equal(s, s.kconv(enc))
- end
- end
-end
diff --git a/test/nkf/test_nkf.rb b/test/nkf/test_nkf.rb
deleted file mode 100644
index eb51bf8e7d..0000000000
--- a/test/nkf/test_nkf.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: false
-require 'test/unit'
-require 'nkf'
-
-class TestNKF < Test::Unit::TestCase
- EUC_STR = "\xa5\xaa\xa5\xd6\xa5\xb8\xa5\xa7\xa5\xaf\xa5\xc8\xbb\xd8\xb8\xfe\
-\xa5\xb9\xa5\xaf\xa5\xea\xa5\xd7\xa5\xc8\xb8\xc0\xb8\xec\
-Ruby"
-
- def test_guess
- str_euc = EUC_STR
- str_jis = NKF.nkf('-j', str_euc)
- assert_equal(::NKF::JIS, NKF.guess(str_jis))
- assert_equal(::NKF::EUC, NKF.guess(str_euc))
- end
-
- def test_ruby_dev_36909
- assert_nothing_raised do
- 100.times { NKF.nkf("--oc=eucJP-nkf", "foo") }
- end
- end
-
-end
diff --git a/test/objspace/test_objspace.rb b/test/objspace/test_objspace.rb
index 4757c26630..b798b897b4 100644
--- a/test/objspace/test_objspace.rb
+++ b/test/objspace/test_objspace.rb
@@ -141,6 +141,7 @@ class TestObjSpace < Test::Unit::TestCase
end
def test_reachable_objects_during_iteration
+ omit 'flaky on Visual Studio with: [BUG] Unnormalized Fixnum value' if /mswin/ =~ RUBY_PLATFORM
opts = %w[--disable-gem --disable=frozen-string-literal -robjspace]
assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}"
begin;
@@ -225,6 +226,13 @@ class TestObjSpace < Test::Unit::TestCase
assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(o4))
assert_equal(line4, ObjectSpace.allocation_sourceline(o4))
+ # The line number should be based on newarray instead of getinstancevariable.
+ line5 = __LINE__; o5 = [ # newarray (leaf)
+ @ivar, # getinstancevariable (not leaf)
+ ]
+ assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(o5))
+ assert_equal(line5, ObjectSpace.allocation_sourceline(o5))
+
# [Bug #19482]
EnvUtil.under_gc_stress do
100.times do
@@ -555,9 +563,30 @@ class TestObjSpace < Test::Unit::TestCase
end
end
+ def test_dump_callinfo_includes_mid
+ assert_in_out_err(%w[-robjspace --disable-gems], "#{<<-"begin;"}\n#{<<-'end;'}") do |output, error|
+ begin;
+ class Foo
+ def foo
+ super(bar: 123) # should not crash on 0 mid
+ end
+
+ def bar
+ baz(bar: 123) # mid: baz
+ end
+ end
+
+ ObjectSpace.dump_all(output: $stdout)
+ end;
+ assert_empty error
+ assert(output.count > 1)
+ assert_includes output.grep(/"imemo_type":"callinfo"/).join("\n"), '"mid":"baz"'
+ end
+ end
+
def test_dump_string_coderange
assert_includes ObjectSpace.dump("TEST STRING"), '"coderange":"7bit"'
- unknown = "TEST STRING".dup.force_encoding(Encoding::BINARY)
+ unknown = "TEST STRING".dup.force_encoding(Encoding::UTF_16BE)
2.times do # ensure that dumping the string doesn't mutate it
assert_includes ObjectSpace.dump(unknown), '"coderange":"unknown"'
end
@@ -632,13 +661,19 @@ class TestObjSpace < Test::Unit::TestCase
end
end
- entry_hash = JSON.parse(test_string_in_dump_all[1])
+ strs = test_string_in_dump_all.reject do |s|
+ s.include?("fstring")
+ end
+
+ assert_equal(1, strs.length)
+
+ entry_hash = JSON.parse(strs[0])
assert_equal(5, entry_hash["bytesize"], "bytesize is wrong")
assert_equal("TEST2", entry_hash["value"], "value is wrong")
assert_equal("UTF-8", entry_hash["encoding"], "encoding is wrong")
assert_equal("-", entry_hash["file"], "file is wrong")
- assert_equal(4, entry_hash["line"], "line is wrong")
+ assert_equal(5, entry_hash["line"], "line is wrong")
assert_equal("dump_my_heap_please", entry_hash["method"], "method is wrong")
assert_not_nil(entry_hash["generation"])
end
@@ -647,6 +682,7 @@ class TestObjSpace < Test::Unit::TestCase
opts = %w[--disable-gem --disable=frozen-string-literal -robjspace]
assert_in_out_err(opts, "#{<<-"begin;"}#{<<-'end;'}") do |output, error|
+ # frozen_string_literal: false
begin;
def dump_my_heap_please
ObjectSpace.trace_object_allocations_start
@@ -663,6 +699,7 @@ class TestObjSpace < Test::Unit::TestCase
assert_in_out_err(%w[-robjspace], "#{<<-"begin;"}#{<<-'end;'}") do |(output), (error)|
begin;
+ # frozen_string_literal: false
def dump_my_heap_please
ObjectSpace.trace_object_allocations_start
GC.start
@@ -793,6 +830,7 @@ class TestObjSpace < Test::Unit::TestCase
def test_objspace_trace
assert_in_out_err(%w[-robjspace/trace], "#{<<-"begin;"}\n#{<<-'end;'}") do |out, err|
begin;
+ # frozen_string_literal: false
a = "foo"
b = "b" + "a" + "r"
c = 42
@@ -800,8 +838,8 @@ class TestObjSpace < Test::Unit::TestCase
end;
assert_equal ["objspace/trace is enabled"], err
assert_equal 3, out.size
- assert_equal '"foo" @ -:2', out[0]
- assert_equal '"bar" @ -:3', out[1]
+ assert_equal '"foo" @ -:3', out[0]
+ assert_equal '"bar" @ -:4', out[1]
assert_equal '42', out[2]
end
end
diff --git a/test/open-uri/test_open-uri.rb b/test/open-uri/test_open-uri.rb
index fdd1969666..30e3b5c9c1 100644
--- a/test/open-uri/test_open-uri.rb
+++ b/test/open-uri/test_open-uri.rb
@@ -172,6 +172,19 @@ class TestOpenURI < Test::Unit::TestCase
assert_raise(ArgumentError) { URI.open("http://127.0.0.1/", :invalid_option=>true) {} }
end
+ def test_pass_keywords
+ begin
+ f = URI.open(File::NULL, mode: 0666)
+ assert_kind_of File, f
+ ensure
+ f&.close
+ end
+
+ o = Object.new
+ def o.open(foo: ) foo end
+ assert_equal 1, URI.open(o, foo: 1)
+ end
+
def test_mode
with_http {|srv, dr, url|
srv.mount_proc("/mode", lambda { |req, res| res.body = "mode" } )
@@ -545,6 +558,25 @@ class TestOpenURI < Test::Unit::TestCase
}
end
+ def test_max_redirects_success
+ with_http {|srv, dr, url|
+ srv.mount_proc("/r1/") {|req, res| res.status = 301; res["location"] = "#{url}/r2"; res.body = "r1" }
+ srv.mount_proc("/r2/") {|req, res| res.status = 301; res["location"] = "#{url}/r3"; res.body = "r2" }
+ srv.mount_proc("/r3/") {|req, res| res.body = "r3" }
+ URI.open("#{url}/r1/", max_redirects: 2) { |f| assert_equal("r3", f.read) }
+ }
+ end
+
+ def test_max_redirects_too_many
+ with_http {|srv, dr, url|
+ srv.mount_proc("/r1/") {|req, res| res.status = 301; res["location"] = "#{url}/r2"; res.body = "r1" }
+ srv.mount_proc("/r2/") {|req, res| res.status = 301; res["location"] = "#{url}/r3"; res.body = "r2" }
+ srv.mount_proc("/r3/") {|req, res| res.body = "r3" }
+ exc = assert_raise(OpenURI::TooManyRedirects) { URI.open("#{url}/r1/", max_redirects: 1) {} }
+ assert_equal("Too many redirects", exc.message)
+ }
+ end
+
def test_userinfo
assert_raise(ArgumentError) { URI.open("http://user:pass@127.0.0.1/") {} }
end
diff --git a/test/openssl/fixtures/pkey/dh1024.pem b/test/openssl/fixtures/pkey/dh1024.pem
deleted file mode 100644
index f99c757f21..0000000000
--- a/test/openssl/fixtures/pkey/dh1024.pem
+++ /dev/null
@@ -1,5 +0,0 @@
------BEGIN DH PARAMETERS-----
-MIGHAoGBAKnKQ8MNK6nYZzLrrcuTsLxuiJGXoOO5gT+tljOTbHBuiktdMTITzIY0
-pFxIvjG05D7HoBZQfrR0c92NGWPkAiCkhQKB8JCbPVzwNLDy6DZ0pmofDKrEsYHG
-AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC
------END DH PARAMETERS-----
diff --git a/test/openssl/fixtures/pkey/dh2048_ffdhe2048.pem b/test/openssl/fixtures/pkey/dh2048_ffdhe2048.pem
new file mode 100644
index 0000000000..9b182b7201
--- /dev/null
+++ b/test/openssl/fixtures/pkey/dh2048_ffdhe2048.pem
@@ -0,0 +1,8 @@
+-----BEGIN DH PARAMETERS-----
+MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
++8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
+87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
+YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
+7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
+ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
+-----END DH PARAMETERS-----
diff --git a/test/openssl/fixtures/pkey/dsa2048.pem b/test/openssl/fixtures/pkey/dsa2048.pem
new file mode 100644
index 0000000000..3f22b22b58
--- /dev/null
+++ b/test/openssl/fixtures/pkey/dsa2048.pem
@@ -0,0 +1,15 @@
+-----BEGIN PRIVATE KEY-----
+MIICXgIBADCCAjYGByqGSM44BAEwggIpAoIBAQDXZhJ/dQoWkQELzjzlx8FtIp96
+voCYe5NY0H8j0jz7GyHpXt41+MteqkZK3/Ah+cNR9uG8iEYArAZ71LcWotfee2Gz
+xdxozr9bRt0POYhO2YIsfMpBrEskPsDH2g/2nFV8l4OJgxU2qZUrF4PN5ha+Mu6u
+sVtN8hjvAvnbf4Pxn0b8NN9f4PJncroUa8acv5WsV85E1RW7NYCefggU4LytYIHg
+euRF9eY9gVCX5MkUgW2xODHIYJhwk/+5lJxG7qUsSahD/nPHO/yoWgdVHq2DkdTq
+KYXkAxx2PJcTBOHTglhE6mgCbEKp8vcfElnBWyCT6QykclZiPXXD2JV829J/Ah0A
+vYa+/G/gUZiomyejVje6UsGoCc+vInxmovOL8QKCAQEAhnKEigYPw6u8JY7v5iGo
+Ylz8qiMFYmaJCwevf3KCjWeEXuNO4OrKdfzkQl1tPuGLioYFfP1A2yGosjdUdLEB
+0JqnzlKxUp+G6RfBj+WYzbgc5hr7t0M+reAJh09/hDzqfxjcgiHstq7mpRXBP8Y7
+iu27s7TRYJNSAYRvWcXNSBEUym3mHBBbZn7VszYooSrn60/iZ8I+VY1UF/fgqhbj
+JfaaZNQCDO9K3Vb3rsXoYd8+bOZIen9uHB+pNjMqhpl4waysqrlpGFeeqdxivH6S
+vkrHLs6/eWVMnS08RdcryoCrI3Bm8mMBKQglDwKLnWLfzG565qEhslzyCd/l9k9a
+cwQfAh0Ao8/g72fSFmo04FizM7DZJSIPqDLjfZu9hLvUFA==
+-----END PRIVATE KEY-----
diff --git a/test/openssl/fixtures/ssl/openssl_fips.cnf.tmpl b/test/openssl/fixtures/ssl/openssl_fips.cnf.tmpl
deleted file mode 100644
index be0768d523..0000000000
--- a/test/openssl/fixtures/ssl/openssl_fips.cnf.tmpl
+++ /dev/null
@@ -1,19 +0,0 @@
-config_diagnostics = 1
-openssl_conf = openssl_init
-
-# It seems that the .include needs an absolute path.
-.include OPENSSL_DIR/ssl/fipsmodule.cnf
-
-[openssl_init]
-providers = provider_sect
-alg_section = algorithm_sect
-
-[provider_sect]
-fips = fips_sect
-base = base_sect
-
-[base_sect]
-activate = 1
-
-[algorithm_sect]
-default_properties = fips=yes
diff --git a/test/openssl/test_asn1.rb b/test/openssl/test_asn1.rb
index 6eb45094a2..7b1722e5df 100644
--- a/test/openssl/test_asn1.rb
+++ b/test/openssl/test_asn1.rb
@@ -406,10 +406,6 @@ class OpenSSL::TestASN1 < OpenSSL::TestCase
rescue OpenSSL::ASN1::ASN1Error
pend "No negative time_t support?"
end
- # Seconds is omitted. LibreSSL 3.6.0 requires it
- return if libressl?
- decode_test B(%w{ 17 0B }) + "1609082343Z".b,
- OpenSSL::ASN1::UTCTime.new(Time.utc(2016, 9, 8, 23, 43, 0))
# not implemented
# decode_test B(%w{ 17 11 }) + "500908234339+0930".b,
# OpenSSL::ASN1::UTCTime.new(Time.new(1950, 9, 8, 23, 43, 39, "+09:30"))
@@ -428,10 +424,6 @@ class OpenSSL::TestASN1 < OpenSSL::TestCase
OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 29))
encode_decode_test B(%w{ 18 0F }) + "99990908234339Z".b,
OpenSSL::ASN1::GeneralizedTime.new(Time.utc(9999, 9, 8, 23, 43, 39))
- # LibreSSL 3.6.0 requires the seconds element
- return if libressl?
- decode_test B(%w{ 18 0D }) + "201612081934Z".b,
- OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 0))
# not implemented
# decode_test B(%w{ 18 13 }) + "20161208193439+0930".b,
# OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 39, "+09:30"))
diff --git a/test/openssl/test_bn.rb b/test/openssl/test_bn.rb
index 77af14091e..ea88ff06ce 100644
--- a/test/openssl/test_bn.rb
+++ b/test/openssl/test_bn.rb
@@ -175,7 +175,9 @@ class OpenSSL::TestBN < OpenSSL::TestCase
end
def test_mod_sqrt
- assert_equal(3, 4.to_bn.mod_sqrt(5))
+ assert_equal(4, 4.to_bn.mod_sqrt(5).mod_sqr(5))
+ # One of 189484 or 326277 is returned as a square root of 2 (mod 515761).
+ assert_equal(2, 2.to_bn.mod_sqrt(515761).mod_sqr(515761))
assert_equal(0, 5.to_bn.mod_sqrt(5))
assert_raise(OpenSSL::BNError) { 3.to_bn.mod_sqrt(5) }
end
diff --git a/test/openssl/test_cipher.rb b/test/openssl/test_cipher.rb
index 1c8610b2a9..8faa570648 100644
--- a/test/openssl/test_cipher.rb
+++ b/test/openssl/test_cipher.rb
@@ -205,7 +205,7 @@ class OpenSSL::TestCipher < OpenSSL::TestCase
assert_raise(OpenSSL::Cipher::CipherError) { cipher.update(ct2) }
end if has_cipher?("aes-128-ccm") &&
OpenSSL::Cipher.new("aes-128-ccm").authenticated? &&
- OpenSSL::OPENSSL_VERSION_NUMBER >= 0x1010103f # version >= 1.1.1c
+ openssl?(1, 1, 1, 0x03, 0xf) # version >= 1.1.1c
def test_aes_gcm
# GCM spec Appendix B Test Case 4
diff --git a/test/openssl/test_config.rb b/test/openssl/test_config.rb
index 24a215a486..6dbb9c6138 100644
--- a/test/openssl/test_config.rb
+++ b/test/openssl/test_config.rb
@@ -91,22 +91,19 @@ __EOC__
assert_equal('123baz456bar798', c['dollar']['qux'])
assert_equal('123baz456bar798.123baz456bar798', c['dollar']['quxx'])
- excn = assert_raise(OpenSSL::ConfigError) do
+ assert_raise_with_message(OpenSSL::ConfigError, /error in line 1: variable has no value/) do
OpenSSL::Config.parse("foo = $bar")
end
- assert_equal("error in line 1: variable has no value", excn.message)
- excn = assert_raise(OpenSSL::ConfigError) do
+ assert_raise_with_message(OpenSSL::ConfigError, /error in line 1: no close brace/) do
OpenSSL::Config.parse("foo = $(bar")
end
- assert_equal("error in line 1: no close brace", excn.message)
- excn = assert_raise(OpenSSL::ConfigError) do
+ assert_raise_with_message(OpenSSL::ConfigError, /error in line 1: missing equal sign/) do
OpenSSL::Config.parse("f o =b ar # no space in key")
end
- assert_equal("error in line 1: missing equal sign", excn.message)
- excn = assert_raise(OpenSSL::ConfigError) do
+ assert_raise_with_message(OpenSSL::ConfigError, /error in line 7: missing close square bracket/) do
OpenSSL::Config.parse(<<__EOC__)
# comment 1 # comments
@@ -117,7 +114,6 @@ __EOC__
[third # section not terminated
__EOC__
end
- assert_equal("error in line 7: missing close square bracket", excn.message)
end
def test_s_parse_include
diff --git a/test/openssl/test_digest.rb b/test/openssl/test_digest.rb
index 84c128c12f..988330e405 100644
--- a/test/openssl/test_digest.rb
+++ b/test/openssl/test_digest.rb
@@ -67,7 +67,7 @@ class OpenSSL::TestDigest < OpenSSL::TestCase
end
def encode16(str)
- str.unpack("H*").first
+ str.unpack1("H*")
end
def test_sha2
@@ -88,7 +88,7 @@ class OpenSSL::TestDigest < OpenSSL::TestCase
end
def test_sha512_truncate
- pend "SHA512_224 is not implemented" unless digest_available?('SHA512-224')
+ pend "SHA512_224 is not implemented" unless digest_available?('sha512-224')
sha512_224_a = "d5cdb9ccc769a5121d4175f2bfdd13d6310e0d3d361ea75d82108327"
sha512_256_a = "455e518824bc0601f9fb858ff5c37d417d67c2f8e0df2babe4808858aea830f8"
@@ -100,7 +100,7 @@ class OpenSSL::TestDigest < OpenSSL::TestCase
end
def test_sha3
- pend "SHA3 is not implemented" unless digest_available?('SHA3-224')
+ pend "SHA3 is not implemented" unless digest_available?('sha3-224')
s224 = '6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7'
s256 = 'a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a'
s384 = '0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004'
@@ -126,6 +126,15 @@ class OpenSSL::TestDigest < OpenSSL::TestCase
end
end
+ def test_digests
+ digests = OpenSSL::Digest.digests
+ assert_kind_of Array, digests
+ assert_include digests, "md5"
+ assert_include digests, "sha1"
+ assert_include digests, "sha256"
+ assert_include digests, "sha512"
+ end
+
private
def check_digest(oid)
@@ -138,11 +147,8 @@ class OpenSSL::TestDigest < OpenSSL::TestCase
end
def digest_available?(name)
- begin
- OpenSSL::Digest.new(name)
- rescue RuntimeError
- false
- end
+ @digests ||= OpenSSL::Digest.digests
+ @digests.include?(name)
end
end
diff --git a/test/openssl/test_engine.rb b/test/openssl/test_engine.rb
index 15434218d9..b6025f915b 100644
--- a/test/openssl/test_engine.rb
+++ b/test/openssl/test_engine.rb
@@ -82,7 +82,7 @@ class OpenSSL::TestEngine < OpenSSL::TestCase
# this is required because OpenSSL::Engine methods change global state
def with_openssl(code, **opts)
- assert_separately([{ "OSSL_MDEBUG" => nil }, "-ropenssl"], <<~"end;", **opts)
+ assert_separately(["-ropenssl"], <<~"end;", **opts)
#{code}
end;
end
diff --git a/test/openssl/test_fips.rb b/test/openssl/test_fips.rb
index dfc1729b35..4a3dd43a41 100644
--- a/test/openssl/test_fips.rb
+++ b/test/openssl/test_fips.rb
@@ -9,7 +9,7 @@ class OpenSSL::TestFIPS < OpenSSL::TestCase
omit "Only for FIPS mode environment"
end
- assert_separately([{ "OSSL_MDEBUG" => nil }, "-ropenssl"], <<~"end;")
+ assert_separately(["-ropenssl"], <<~"end;")
assert OpenSSL.fips_mode == true, ".fips_mode should return true on FIPS mode enabled"
end;
end
@@ -19,7 +19,7 @@ class OpenSSL::TestFIPS < OpenSSL::TestCase
omit "Only for non-FIPS mode environment"
end
- assert_separately([{ "OSSL_MDEBUG" => nil }, "-ropenssl"], <<~"end;")
+ assert_separately(["-ropenssl"], <<~"end;")
message = ".fips_mode should return false on FIPS mode disabled. " \
"If you run the test on FIPS mode, please set " \
"TEST_RUBY_OPENSSL_FIPS_ENABLED=true"
@@ -28,14 +28,16 @@ class OpenSSL::TestFIPS < OpenSSL::TestCase
end
def test_fips_mode_is_reentrant
- OpenSSL.fips_mode = false
- OpenSSL.fips_mode = false
+ assert_separately(["-ropenssl"], <<~"end;")
+ OpenSSL.fips_mode = false
+ OpenSSL.fips_mode = false
+ end;
end
def test_fips_mode_get_with_fips_mode_set
omit('OpenSSL is not FIPS-capable') unless OpenSSL::OPENSSL_FIPS
- assert_separately([{ "OSSL_MDEBUG" => nil }, "-ropenssl"], <<~"end;")
+ assert_separately(["-ropenssl"], <<~"end;")
begin
OpenSSL.fips_mode = true
assert OpenSSL.fips_mode == true, ".fips_mode should return true when .fips_mode=true"
diff --git a/test/openssl/test_ns_spki.rb b/test/openssl/test_ns_spki.rb
index 383931b98b..d76fc9e5cf 100644
--- a/test/openssl/test_ns_spki.rb
+++ b/test/openssl/test_ns_spki.rb
@@ -38,13 +38,13 @@ class OpenSSL::TestNSSPI < OpenSSL::TestCase
def test_decode_data
spki = OpenSSL::Netscape::SPKI.new(@b64)
assert_equal(@b64, spki.to_pem)
- assert_equal(@b64.unpack("m").first, spki.to_der)
+ assert_equal(@b64.unpack1("m"), spki.to_der)
assert_equal("MozillaIsMyFriend", spki.challenge)
assert_equal(OpenSSL::PKey::RSA, spki.public_key.class)
- spki = OpenSSL::Netscape::SPKI.new(@b64.unpack("m").first)
+ spki = OpenSSL::Netscape::SPKI.new(@b64.unpack1("m"))
assert_equal(@b64, spki.to_pem)
- assert_equal(@b64.unpack("m").first, spki.to_der)
+ assert_equal(@b64.unpack1("m"), spki.to_der)
assert_equal("MozillaIsMyFriend", spki.challenge)
assert_equal(OpenSSL::PKey::RSA, spki.public_key.class)
end
diff --git a/test/openssl/test_ocsp.rb b/test/openssl/test_ocsp.rb
index 85f133752c..cf96fc22e5 100644
--- a/test/openssl/test_ocsp.rb
+++ b/test/openssl/test_ocsp.rb
@@ -228,7 +228,7 @@ class OpenSSL::TestOCSP < OpenSSL::TestCase
assert_equal OpenSSL::OCSP::V_CERTSTATUS_REVOKED, single.cert_status
assert_equal OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED, single.revocation_reason
assert_equal now - 400, single.revocation_time
- assert_in_delta (now - 301), single.this_update, 1
+ assert_in_delta (now - 300), single.this_update, 1
assert_equal nil, single.next_update
assert_equal [], single.extensions
diff --git a/test/openssl/test_ossl.rb b/test/openssl/test_ossl.rb
index e1d86bd40b..979669a003 100644
--- a/test/openssl/test_ossl.rb
+++ b/test/openssl/test_ossl.rb
@@ -60,6 +60,19 @@ class OpenSSL::OSSL < OpenSSL::SSLTestCase
assert_operator(a_b_time, :<, a_c_time * 10, "fixed_length_secure_compare timing test failed")
assert_operator(a_c_time, :<, a_b_time * 10, "fixed_length_secure_compare timing test failed")
end
+
+ def test_error_data
+ # X509V3_EXT_nconf_nid() called from OpenSSL::X509::ExtensionFactory#create_ext is a function
+ # that uses ERR_raise_data() to append additional information about the error.
+ #
+ # The generated message should look like:
+ # "subjectAltName = IP:not.a.valid.ip.address: bad ip address (value=not.a.valid.ip.address)"
+ # "subjectAltName = IP:not.a.valid.ip.address: error in extension (name=subjectAltName, value=IP:not.a.valid.ip.address)"
+ ef = OpenSSL::X509::ExtensionFactory.new
+ assert_raise_with_message(OpenSSL::X509::ExtensionError, /value=(IP:)?not.a.valid.ip.address\)/) {
+ ef.create_ext("subjectAltName", "IP:not.a.valid.ip.address")
+ }
+ end
end
end
diff --git a/test/openssl/test_pair.rb b/test/openssl/test_pair.rb
index 14786100df..b616883925 100644
--- a/test/openssl/test_pair.rb
+++ b/test/openssl/test_pair.rb
@@ -115,6 +115,17 @@ module OpenSSL::TestPairM
}
end
+ def test_gets_chomp
+ ssl_pair {|s1, s2|
+ s1 << "line1\r\nline2\r\nline3\r\n"
+ s1.close
+
+ assert_equal("line1", s2.gets("\r\n", chomp: true))
+ assert_equal("line2\r\n", s2.gets("\r\n", chomp: false))
+ assert_equal("line3", s2.gets(chomp: true))
+ }
+ end
+
def test_gets_eof_limit
ssl_pair {|s1, s2|
s1.write("hello")
diff --git a/test/openssl/test_pkcs12.rb b/test/openssl/test_pkcs12.rb
index ec676743bc..e6b91b52af 100644
--- a/test/openssl/test_pkcs12.rb
+++ b/test/openssl/test_pkcs12.rb
@@ -181,7 +181,7 @@ module OpenSSL
def test_new_with_no_keys
# generated with:
# openssl pkcs12 -certpbe PBE-SHA1-3DES -in <@mycert> -nokeys -export
- str = <<~EOF.unpack("m").first
+ str = <<~EOF.unpack1("m")
MIIGJAIBAzCCBeoGCSqGSIb3DQEHAaCCBdsEggXXMIIF0zCCBc8GCSqGSIb3
DQEHBqCCBcAwggW8AgEAMIIFtQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQMw
DgQIjv5c3OHvnBgCAggAgIIFiMJa8Z/w7errRvCQPXh9dGQz3eJaFq3S2gXD
@@ -230,7 +230,7 @@ AA==
def test_new_with_no_certs
# generated with:
# openssl pkcs12 -inkey fixtures/openssl/pkey/rsa-1.pem -nocerts -export
- str = <<~EOF.unpack("m").first
+ str = <<~EOF.unpack1("m")
MIIJ7wIBAzCCCbUGCSqGSIb3DQEHAaCCCaYEggmiMIIJnjCCCZoGCSqGSIb3
DQEHAaCCCYsEggmHMIIJgzCCCX8GCyqGSIb3DQEMCgECoIIJbjCCCWowHAYK
KoZIhvcNAQwBAzAOBAjX5nN8jyRKwQICCAAEgglIBIRLHfiY1mNHpl3FdX6+
diff --git a/test/openssl/test_pkey.rb b/test/openssl/test_pkey.rb
index 2b99e8f374..aee0546f63 100644
--- a/test/openssl/test_pkey.rb
+++ b/test/openssl/test_pkey.rb
@@ -82,6 +82,9 @@ class OpenSSL::TestPKey < OpenSSL::PKeyTestCase
end
def test_ed25519
+ # Ed25519 is not FIPS-approved.
+ omit_on_fips
+
# Test vector from RFC 8032 Section 7.1 TEST 2
priv_pem = <<~EOF
-----BEGIN PRIVATE KEY-----
@@ -96,9 +99,11 @@ class OpenSSL::TestPKey < OpenSSL::PKeyTestCase
begin
priv = OpenSSL::PKey.read(priv_pem)
pub = OpenSSL::PKey.read(pub_pem)
- rescue OpenSSL::PKey::PKeyError
+ rescue OpenSSL::PKey::PKeyError => e
# OpenSSL < 1.1.1
- pend "Ed25519 is not implemented"
+ pend "Ed25519 is not implemented" unless openssl?(1, 1, 1)
+
+ raise e
end
assert_instance_of OpenSSL::PKey::PKey, priv
assert_instance_of OpenSSL::PKey::PKey, pub
@@ -106,6 +111,19 @@ class OpenSSL::TestPKey < OpenSSL::PKeyTestCase
assert_equal pub_pem, priv.public_to_pem
assert_equal pub_pem, pub.public_to_pem
+ begin
+ assert_equal "4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb",
+ priv.raw_private_key.unpack1("H*")
+ assert_equal OpenSSL::PKey.new_raw_private_key("ED25519", priv.raw_private_key).private_to_pem,
+ priv.private_to_pem
+ assert_equal "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c",
+ priv.raw_public_key.unpack1("H*")
+ assert_equal OpenSSL::PKey.new_raw_public_key("ED25519", priv.raw_public_key).public_to_pem,
+ pub.public_to_pem
+ rescue NoMethodError
+ pend "running OpenSSL version does not have raw public key support"
+ end
+
sig = [<<~EOF.gsub(/[^0-9a-f]/, "")].pack("H*")
92a009a9f0d4cab8720e820b5f642540
a2b27b5416503f8fb3762223ebdb69da
@@ -126,6 +144,32 @@ class OpenSSL::TestPKey < OpenSSL::PKeyTestCase
assert_raise(OpenSSL::PKey::PKeyError) { priv.derive(pub) }
end
+ def test_ed25519_not_approved_on_fips
+ omit_on_non_fips
+ # Ed25519 is technically allowed in the OpenSSL 3.0 code as a kind of bug.
+ # So, we need to omit OpenSSL 3.0.
+ #
+ # See OpenSSL providers/fips/fipsprov.c PROV_NAMES_ED25519 entries with
+ # FIPS_DEFAULT_PROPERTIES on openssl-3.0 branch and
+ # FIPS_UNAPPROVED_PROPERTIES on openssl-3.1 branch.
+ #
+ # See also
+ # https://github.com/openssl/openssl/issues/20758#issuecomment-1639658102
+ # for details.
+ unless openssl?(3, 1, 0, 0)
+ omit 'Ed25519 is allowed in the OpenSSL 3.0 FIPS code as a kind of bug'
+ end
+
+ priv_pem = <<~EOF
+ -----BEGIN PRIVATE KEY-----
+ MC4CAQAwBQYDK2VwBCIEIEzNCJso/5banbbDRuwRTg9bijGfNaumJNqM9u1PuKb7
+ -----END PRIVATE KEY-----
+ EOF
+ assert_raise(OpenSSL::PKey::PKeyError) do
+ OpenSSL::PKey.read(priv_pem)
+ end
+ end
+
def test_x25519
# Test vector from RFC 7748 Section 6.1
alice_pem = <<~EOF
@@ -150,6 +194,32 @@ class OpenSSL::TestPKey < OpenSSL::PKeyTestCase
assert_equal alice_pem, alice.private_to_pem
assert_equal bob_pem, bob.public_to_pem
assert_equal [shared_secret].pack("H*"), alice.derive(bob)
+ begin
+ alice_private = OpenSSL::PKey.new_raw_private_key("X25519", alice.raw_private_key)
+ bob_public = OpenSSL::PKey.new_raw_public_key("X25519", bob.raw_public_key)
+ alice_private_raw = alice.raw_private_key.unpack1("H*")
+ bob_public_raw = bob.raw_public_key.unpack1("H*")
+ rescue NoMethodError
+ # OpenSSL < 1.1.1
+ pend "running OpenSSL version does not have raw public key support"
+ end
+ assert_equal alice_private.private_to_pem,
+ alice.private_to_pem
+ assert_equal bob_public.public_to_pem,
+ bob.public_to_pem
+ assert_equal "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a",
+ alice_private_raw
+ assert_equal "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f",
+ bob_public_raw
+ end
+
+ def raw_initialize
+ pend "Ed25519 is not implemented" unless openssl?(1, 1, 1) # >= v1.1.1
+
+ assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.new_raw_private_key("foo123", "xxx") }
+ assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.new_raw_private_key("ED25519", "xxx") }
+ assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.new_raw_public_key("foo123", "xxx") }
+ assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.new_raw_public_key("ED25519", "xxx") }
end
def test_compare?
diff --git a/test/openssl/test_pkey_dh.rb b/test/openssl/test_pkey_dh.rb
index 161af1897b..d32ffaf6b1 100644
--- a/test/openssl/test_pkey_dh.rb
+++ b/test/openssl/test_pkey_dh.rb
@@ -18,15 +18,26 @@ class OpenSSL::TestPKeyDH < OpenSSL::PKeyTestCase
assert_key(dh)
end if ENV["OSSL_TEST_ALL"]
- def test_new_break
+ def test_new_break_on_non_fips
+ omit_on_fips
+
assert_nil(OpenSSL::PKey::DH.new(NEW_KEYLEN) { break })
assert_raise(RuntimeError) do
OpenSSL::PKey::DH.new(NEW_KEYLEN) { raise }
end
end
+ def test_new_break_on_fips
+ omit_on_non_fips
+
+ # The block argument is not executed in FIPS case.
+ # See https://github.com/ruby/openssl/issues/692 for details.
+ assert(OpenSSL::PKey::DH.new(NEW_KEYLEN) { break })
+ assert(OpenSSL::PKey::DH.new(NEW_KEYLEN) { raise })
+ end
+
def test_derive_key
- params = Fixtures.pkey("dh1024")
+ params = Fixtures.pkey("dh2048_ffdhe2048")
dh1 = OpenSSL::PKey.generate_key(params)
dh2 = OpenSSL::PKey.generate_key(params)
dh1_pub = OpenSSL::PKey.read(dh1.public_to_der)
@@ -44,34 +55,38 @@ class OpenSSL::TestPKeyDH < OpenSSL::PKeyTestCase
end
def test_DHparams
- dh1024 = Fixtures.pkey("dh1024")
- dh1024params = dh1024.public_key
+ dh = Fixtures.pkey("dh2048_ffdhe2048")
+ dh_params = dh.public_key
asn1 = OpenSSL::ASN1::Sequence([
- OpenSSL::ASN1::Integer(dh1024.p),
- OpenSSL::ASN1::Integer(dh1024.g)
+ OpenSSL::ASN1::Integer(dh.p),
+ OpenSSL::ASN1::Integer(dh.g)
])
key = OpenSSL::PKey::DH.new(asn1.to_der)
- assert_same_dh dh1024params, key
+ assert_same_dh dh_params, key
pem = <<~EOF
-----BEGIN DH PARAMETERS-----
- MIGHAoGBAKnKQ8MNK6nYZzLrrcuTsLxuiJGXoOO5gT+tljOTbHBuiktdMTITzIY0
- pFxIvjG05D7HoBZQfrR0c92NGWPkAiCkhQKB8JCbPVzwNLDy6DZ0pmofDKrEsYHG
- AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC
+ MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
+ +8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
+ 87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
+ YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
+ 7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
+ ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
-----END DH PARAMETERS-----
EOF
+
key = OpenSSL::PKey::DH.new(pem)
- assert_same_dh dh1024params, key
+ assert_same_dh dh_params, key
key = OpenSSL::PKey.read(pem)
- assert_same_dh dh1024params, key
+ assert_same_dh dh_params, key
- assert_equal asn1.to_der, dh1024.to_der
- assert_equal pem, dh1024.export
+ assert_equal asn1.to_der, dh.to_der
+ assert_equal pem, dh.export
end
def test_public_key
- dh = Fixtures.pkey("dh1024")
+ dh = Fixtures.pkey("dh2048_ffdhe2048")
public_key = dh.public_key
assert_no_key(public_key) #implies public_key.public? is false!
assert_equal(dh.to_der, public_key.to_der)
@@ -80,7 +95,8 @@ class OpenSSL::TestPKeyDH < OpenSSL::PKeyTestCase
def test_generate_key
# Deprecated in v3.0.0; incompatible with OpenSSL 3.0
- dh = Fixtures.pkey("dh1024").public_key # creates a copy with params only
+ # Creates a copy with params only
+ dh = Fixtures.pkey("dh2048_ffdhe2048").public_key
assert_no_key(dh)
dh.generate_key!
assert_key(dh)
@@ -91,7 +107,15 @@ class OpenSSL::TestPKeyDH < OpenSSL::PKeyTestCase
end if !openssl?(3, 0, 0)
def test_params_ok?
- dh0 = Fixtures.pkey("dh1024")
+ # Skip the tests in old OpenSSL version 1.1.1c or early versions before
+ # applying the following commits in OpenSSL 1.1.1d to make `DH_check`
+ # function pass the RFC 7919 FFDHE group texts.
+ # https://github.com/openssl/openssl/pull/9435
+ unless openssl?(1, 1, 1, 4)
+ pend 'DH check for RFC 7919 FFDHE group texts is not implemented'
+ end
+
+ dh0 = Fixtures.pkey("dh2048_ffdhe2048")
dh1 = OpenSSL::PKey::DH.new(OpenSSL::ASN1::Sequence([
OpenSSL::ASN1::Integer(dh0.p),
@@ -108,7 +132,7 @@ class OpenSSL::TestPKeyDH < OpenSSL::PKeyTestCase
def test_dup
# Parameters only
- dh1 = Fixtures.pkey("dh1024")
+ dh1 = Fixtures.pkey("dh2048_ffdhe2048")
dh2 = dh1.dup
assert_equal dh1.to_der, dh2.to_der
assert_not_equal nil, dh1.p
@@ -125,7 +149,7 @@ class OpenSSL::TestPKeyDH < OpenSSL::PKeyTestCase
end
# With a key pair
- dh3 = OpenSSL::PKey.generate_key(Fixtures.pkey("dh1024"))
+ dh3 = OpenSSL::PKey.generate_key(Fixtures.pkey("dh2048_ffdhe2048"))
dh4 = dh3.dup
assert_equal dh3.to_der, dh4.to_der
assert_equal dh1.to_der, dh4.to_der # encodes parameters only
@@ -136,7 +160,7 @@ class OpenSSL::TestPKeyDH < OpenSSL::PKeyTestCase
end
def test_marshal
- dh = Fixtures.pkey("dh1024")
+ dh = Fixtures.pkey("dh2048_ffdhe2048")
deserialized = Marshal.load(Marshal.dump(dh))
assert_equal dh.to_der, deserialized.to_der
diff --git a/test/openssl/test_pkey_dsa.rb b/test/openssl/test_pkey_dsa.rb
index d1059093c5..4c93f2869d 100644
--- a/test/openssl/test_pkey_dsa.rb
+++ b/test/openssl/test_pkey_dsa.rb
@@ -31,11 +31,6 @@ class OpenSSL::TestPKeyDSA < OpenSSL::PKeyTestCase
def test_generate
# DSA.generate used to call DSA_generate_parameters_ex(), which adjusts the
# size of q according to the size of p
- key1024 = OpenSSL::PKey::DSA.generate(1024)
- assert_predicate key1024, :private?
- assert_equal 1024, key1024.p.num_bits
- assert_equal 160, key1024.q.num_bits
-
key2048 = OpenSSL::PKey::DSA.generate(2048)
assert_equal 2048, key2048.p.num_bits
assert_equal 256, key2048.q.num_bits
@@ -47,28 +42,41 @@ class OpenSSL::TestPKeyDSA < OpenSSL::PKeyTestCase
end
end
+ def test_generate_on_non_fips
+ # DSA with 1024 bits is invalid on FIPS 186-4.
+ # https://github.com/openssl/openssl/commit/49ed5ba8f62875074f04417189147fd3dda072ab
+ omit_on_fips
+
+ key1024 = OpenSSL::PKey::DSA.generate(1024)
+ assert_predicate key1024, :private?
+ assert_equal 1024, key1024.p.num_bits
+ assert_equal 160, key1024.q.num_bits
+ end
+
def test_sign_verify
- dsa512 = Fixtures.pkey("dsa512")
+ # The DSA valid size is 2048 or 3072 on FIPS.
+ # https://github.com/openssl/openssl/blob/7649b5548e5c0352b91d9d3ed695e42a2ac1e99c/providers/common/securitycheck.c#L185-L188
+ dsa = Fixtures.pkey("dsa2048")
data = "Sign me!"
if defined?(OpenSSL::Digest::DSS1)
- signature = dsa512.sign(OpenSSL::Digest.new('DSS1'), data)
- assert_equal true, dsa512.verify(OpenSSL::Digest.new('DSS1'), signature, data)
+ signature = dsa.sign(OpenSSL::Digest.new('DSS1'), data)
+ assert_equal true, dsa.verify(OpenSSL::Digest.new('DSS1'), signature, data)
end
- signature = dsa512.sign("SHA256", data)
- assert_equal true, dsa512.verify("SHA256", signature, data)
+ signature = dsa.sign("SHA256", data)
+ assert_equal true, dsa.verify("SHA256", signature, data)
- signature0 = (<<~'end;').unpack("m")[0]
- MCwCFH5h40plgU5Fh0Z4wvEEpz0eE9SnAhRPbkRB8ggsN/vsSEYMXvJwjGg/
- 6g==
+ signature0 = (<<~'end;').unpack1("m")
+ MD4CHQC0zmRkVOAHJTm28fS5PVUv+4LtBeNaKqr/yfmVAh0AsTcLqofWHoW8X5oWu8AOvngOcFVZ
+ cLTvhY3XNw==
end;
- assert_equal true, dsa512.verify("SHA256", signature0, data)
+ assert_equal true, dsa.verify("SHA256", signature0, data)
signature1 = signature0.succ
- assert_equal false, dsa512.verify("SHA256", signature1, data)
+ assert_equal false, dsa.verify("SHA256", signature1, data)
end
def test_sign_verify_raw
- key = Fixtures.pkey("dsa512")
+ key = Fixtures.pkey("dsa2048")
data = 'Sign me!'
digest = OpenSSL::Digest.digest('SHA1', data)
@@ -127,6 +135,8 @@ class OpenSSL::TestPKeyDSA < OpenSSL::PKeyTestCase
end
def test_DSAPrivateKey_encrypted
+ omit_on_fips
+
# key = abcdef
dsa512 = Fixtures.pkey("dsa512")
pem = <<~EOF
diff --git a/test/openssl/test_pkey_ec.rb b/test/openssl/test_pkey_ec.rb
index e5fef940a6..2cb8e287ab 100644
--- a/test/openssl/test_pkey_ec.rb
+++ b/test/openssl/test_pkey_ec.rb
@@ -5,20 +5,6 @@ if defined?(OpenSSL)
class OpenSSL::TestEC < OpenSSL::PKeyTestCase
def test_ec_key
- builtin_curves = OpenSSL::PKey::EC.builtin_curves
- assert_not_empty builtin_curves
-
- builtin_curves.each do |curve_name, comment|
- # Oakley curves and X25519 are not suitable for signing and causes
- # FIPS-selftest failure on some environment, so skip for now.
- next if ["Oakley", "X25519"].any? { |n| curve_name.start_with?(n) }
-
- key = OpenSSL::PKey::EC.generate(curve_name)
- assert_predicate key, :private?
- assert_predicate key, :public?
- assert_nothing_raised { key.check_key }
- end
-
key1 = OpenSSL::PKey::EC.generate("prime256v1")
# PKey is immutable in OpenSSL >= 3.0; constructing an empty EC object is
@@ -49,6 +35,17 @@ class OpenSSL::TestEC < OpenSSL::PKeyTestCase
end
end
+ def test_builtin_curves
+ builtin_curves = OpenSSL::PKey::EC.builtin_curves
+ assert_not_empty builtin_curves
+ assert_equal 2, builtin_curves[0].size
+ assert_kind_of String, builtin_curves[0][0]
+ assert_kind_of String, builtin_curves[0][1]
+
+ builtin_curve_names = builtin_curves.map { |name, comment| name }
+ assert_include builtin_curve_names, "prime256v1"
+ end
+
def test_generate
assert_raise(OpenSSL::PKey::ECError) { OpenSSL::PKey::EC.generate("non-existent") }
g = OpenSSL::PKey::EC::Group.new("prime256v1")
@@ -110,7 +107,7 @@ class OpenSSL::TestEC < OpenSSL::PKeyTestCase
signature = p256.sign("SHA256", data)
assert_equal true, p256.verify("SHA256", signature, data)
- signature0 = (<<~'end;').unpack("m")[0]
+ signature0 = (<<~'end;').unpack1("m")
MEQCIEOTY/hD7eI8a0qlzxkIt8LLZ8uwiaSfVbjX2dPAvN11AiAQdCYx56Fq
QdBp1B4sxJoA8jvODMMklMyBKVmudboA6A==
end;
@@ -232,6 +229,8 @@ class OpenSSL::TestEC < OpenSSL::PKeyTestCase
end
def test_ECPrivateKey_encrypted
+ omit_on_fips
+
p256 = Fixtures.pkey("p256")
# key = abcdef
pem = <<~EOF
diff --git a/test/openssl/test_pkey_rsa.rb b/test/openssl/test_pkey_rsa.rb
index b0ae5784b3..61c55c60b2 100644
--- a/test/openssl/test_pkey_rsa.rb
+++ b/test/openssl/test_pkey_rsa.rb
@@ -83,7 +83,7 @@ class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase
signature = rsa1024.sign("SHA256", data)
assert_equal true, rsa1024.verify("SHA256", signature, data)
- signature0 = (<<~'end;').unpack("m")[0]
+ signature0 = (<<~'end;').unpack1("m")
oLCgbprPvfhM4pjFQiDTFeWI9Sk+Og7Nh9TmIZ/xSxf2CGXQrptlwo7NQ28+
WA6YQo8jPH4hSuyWIM4Gz4qRYiYRkl5TDMUYob94zm8Si1HxEiS9354tzvqS
zS8MLW2BtNPuTubMxTItHGTnOzo9sUg0LAHVFt8kHG2NfKAw/gQ=
diff --git a/test/openssl/test_provider.rb b/test/openssl/test_provider.rb
new file mode 100644
index 0000000000..b0ffae9ce7
--- /dev/null
+++ b/test/openssl/test_provider.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+require_relative 'utils'
+if defined?(OpenSSL) && defined?(OpenSSL::Provider) && !OpenSSL.fips_mode
+
+class OpenSSL::TestProvider < OpenSSL::TestCase
+ def test_openssl_provider_name_inspect
+ with_openssl <<-'end;'
+ provider = OpenSSL::Provider.load("default")
+ assert_equal("default", provider.name)
+ assert_not_nil(provider.inspect)
+ end;
+ end
+
+ def test_openssl_provider_names
+ with_openssl <<-'end;'
+ base_provider = OpenSSL::Provider.load("base")
+ assert_equal(2, OpenSSL::Provider.provider_names.size)
+ assert_includes(OpenSSL::Provider.provider_names, "base")
+
+ assert_equal(true, base_provider.unload)
+ assert_equal(1, OpenSSL::Provider.provider_names.size)
+ assert_not_includes(OpenSSL::Provider.provider_names, "base")
+ end;
+ end
+
+ def test_unloaded_openssl_provider
+ with_openssl <<-'end;'
+ default_provider = OpenSSL::Provider.load("default")
+ assert_equal(true, default_provider.unload)
+ assert_raise(OpenSSL::Provider::ProviderError) { default_provider.name }
+ assert_raise(OpenSSL::Provider::ProviderError) { default_provider.unload }
+ end;
+ end
+
+ def test_openssl_legacy_provider
+ with_openssl(<<-'end;')
+ begin
+ OpenSSL::Provider.load("legacy")
+ rescue OpenSSL::Provider::ProviderError
+ omit "Only for OpenSSL with legacy provider"
+ end
+
+ algo = "RC4"
+ data = "a" * 1000
+ key = OpenSSL::Random.random_bytes(16)
+
+ # default provider does not support RC4
+ cipher = OpenSSL::Cipher.new(algo)
+ cipher.encrypt
+ cipher.key = key
+ encrypted = cipher.update(data) + cipher.final
+
+ other_cipher = OpenSSL::Cipher.new(algo)
+ other_cipher.decrypt
+ other_cipher.key = key
+ decrypted = other_cipher.update(encrypted) + other_cipher.final
+
+ assert_equal(data, decrypted)
+ end;
+ end
+
+ private
+
+ # this is required because OpenSSL::Provider methods change global state
+ def with_openssl(code, **opts)
+ assert_separately(["-ropenssl"], <<~"end;", **opts)
+ #{code}
+ end;
+ end
+end
+
+end
diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb
index db76f1dc4c..1471b0cb36 100644
--- a/test/openssl/test_ssl.rb
+++ b/test/openssl/test_ssl.rb
@@ -117,6 +117,30 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
}
end
+ def test_socket_close_write
+ server_proc = proc do |ctx, ssl|
+ message = ssl.read
+ ssl.write(message)
+ ssl.close_write
+ ensure
+ ssl.close
+ end
+
+ start_server(server_proc: server_proc) do |port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ssl = OpenSSL::SSL::SSLSocket.open("127.0.0.1", port, context: ctx)
+ ssl.sync_close = true
+ ssl.connect
+
+ message = "abc"*1024
+ ssl.write message
+ ssl.close_write
+ assert_equal message, ssl.read
+ ensure
+ ssl&.close
+ end
+ end
+
def test_add_certificate
ctx_proc = -> ctx {
# Unset values set by start_server
@@ -193,6 +217,24 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
}
end
+ def test_read_with_timeout
+ omit "does not support timeout" unless IO.method_defined?(:timeout)
+
+ start_server do |port|
+ server_connect(port) do |ssl|
+ str = +("x" * 100 + "\n")
+ ssl.syswrite(str)
+ assert_equal(str, ssl.sysread(str.bytesize))
+
+ ssl.timeout = 1
+ assert_raise(IO::TimeoutError) {ssl.read(1)}
+
+ ssl.syswrite(str)
+ assert_equal(str, ssl.sysread(str.bytesize))
+ end
+ end
+ end
+
def test_getbyte
start_server { |port|
server_connect(port) { |ssl|
@@ -481,6 +523,40 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
}
end
+ def test_ca_file
+ start_server(ignore_listener_error: true) { |port|
+ # X509_STORE is shared; setting ca_file to SSLContext affects store
+ store = OpenSSL::X509::Store.new
+ assert_equal false, store.verify(@svr_cert)
+
+ ctx = Tempfile.create("ca_cert.pem") { |f|
+ f.puts(@ca_cert.to_pem)
+ f.close
+
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ ctx.cert_store = store
+ ctx.ca_file = f.path
+ ctx.setup
+ ctx
+ }
+ assert_nothing_raised {
+ server_connect(port, ctx) { |ssl| ssl.puts("abc"); ssl.gets }
+ }
+ assert_equal true, store.verify(@svr_cert)
+ }
+ end
+
+ def test_ca_file_not_found
+ path = Tempfile.create("ca_cert.pem") { |f| f.path }
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ca_file = path
+ # OpenSSL >= 1.1.0: /no certificate or crl found/
+ assert_raise(OpenSSL::SSL::SSLError) {
+ ctx.setup
+ }
+ end
+
def test_finished_messages
server_finished = nil
server_peer_finished = nil
@@ -639,7 +715,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
assert_equal(true, OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "xn--qdk4b9b"))
end
- # Comments in this test is excerpted from http://tools.ietf.org/html/rfc6125#page-27
+ # Comments in this test is excerpted from https://www.rfc-editor.org/rfc/rfc6125#page-27
def test_post_connection_check_wildcard_san
# case-insensitive ASCII comparison
# RFC 6125, section 6.4.1
@@ -1046,7 +1122,9 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
start_server(ignore_listener_error: true) { |port|
ctx = OpenSSL::SSL::SSLContext.new
ctx.set_params
- assert_raise_with_message(OpenSSL::SSL::SSLError, /certificate/) {
+ # OpenSSL <= 1.1.0: "self signed certificate in certificate chain"
+ # OpenSSL >= 3.0.0: "self-signed certificate in certificate chain"
+ assert_raise_with_message(OpenSSL::SSL::SSLError, /self.signed/) {
server_connect(port, ctx)
}
}
diff --git a/test/openssl/test_ssl_session.rb b/test/openssl/test_ssl_session.rb
index b243201e18..89cf672a7b 100644
--- a/test/openssl/test_ssl_session.rb
+++ b/test/openssl/test_ssl_session.rb
@@ -22,7 +22,7 @@ class OpenSSL::TestSSLSession < OpenSSL::SSLTestCase
assert_match(/\A-----BEGIN SSL SESSION PARAMETERS-----/, pem)
assert_match(/-----END SSL SESSION PARAMETERS-----\Z/, pem)
pem.gsub!(/-----(BEGIN|END) SSL SESSION PARAMETERS-----/, '').gsub!(/[\r\n]+/m, '')
- assert_equal(session.to_der, pem.unpack('m*')[0])
+ assert_equal(session.to_der, pem.unpack1('m'))
assert_not_nil(session.to_text)
}
end
diff --git a/test/openssl/test_x509ext.rb b/test/openssl/test_x509ext.rb
index 7ad010d1ed..59a41ed736 100644
--- a/test/openssl/test_x509ext.rb
+++ b/test/openssl/test_x509ext.rb
@@ -50,24 +50,41 @@ class OpenSSL::TestX509Extension < OpenSSL::TestCase
cdp = ef.create_extension("crlDistributionPoints", "@crlDistPts")
assert_equal(false, cdp.critical?)
assert_equal("crlDistributionPoints", cdp.oid)
- assert_match(%{URI:http://www\.example\.com/crl}, cdp.value)
- assert_match(
- %r{URI:ldap://ldap\.example\.com/cn=ca\?certificateRevocationList;binary},
- cdp.value)
+ assert_include(cdp.value, "URI:http://www.example.com/crl")
+ assert_include(cdp.value,
+ "URI:ldap://ldap.example.com/cn=ca?certificateRevocationList;binary")
cdp = ef.create_extension("crlDistributionPoints", "critical, @crlDistPts")
assert_equal(true, cdp.critical?)
assert_equal("crlDistributionPoints", cdp.oid)
- assert_match(%{URI:http://www.example.com/crl}, cdp.value)
- assert_match(
- %r{URI:ldap://ldap.example.com/cn=ca\?certificateRevocationList;binary},
- cdp.value)
+ assert_include(cdp.value, "URI:http://www.example.com/crl")
+ assert_include(cdp.value,
+ "URI:ldap://ldap.example.com/cn=ca?certificateRevocationList;binary")
cp = ef.create_extension("certificatePolicies", "@certPolicies")
assert_equal(false, cp.critical?)
assert_equal("certificatePolicies", cp.oid)
- assert_match(%r{2.23.140.1.2.1}, cp.value)
- assert_match(%r{http://cps.example.com}, cp.value)
+ assert_include(cp.value, "2.23.140.1.2.1")
+ assert_include(cp.value, "http://cps.example.com")
+ end
+
+ def test_factory_create_extension_sn_ln
+ ef = OpenSSL::X509::ExtensionFactory.new
+ bc_sn = ef.create_extension("basicConstraints", "critical, CA:TRUE, pathlen:2")
+ bc_ln = ef.create_extension("X509v3 Basic Constraints", "critical, CA:TRUE, pathlen:2")
+ assert_equal(@basic_constraints.to_der, bc_sn.to_der)
+ assert_equal(@basic_constraints.to_der, bc_ln.to_der)
+ end
+
+ def test_factory_create_extension_oid
+ ef = OpenSSL::X509::ExtensionFactory.new
+ ef.config = OpenSSL::Config.parse(<<~_end_of_cnf_)
+ [basic_constraints]
+ cA = BOOLEAN:TRUE
+ pathLenConstraint = INTEGER:2
+ _end_of_cnf_
+ bc_oid = ef.create_extension("2.5.29.19", "ASN1:SEQUENCE:basic_constraints", true)
+ assert_equal(@basic_constraints.to_der, bc_oid.to_der)
end
def test_dup
diff --git a/test/openssl/test_x509req.rb b/test/openssl/test_x509req.rb
index ff17c41163..b98754b8c8 100644
--- a/test/openssl/test_x509req.rb
+++ b/test/openssl/test_x509req.rb
@@ -39,11 +39,6 @@ class OpenSSL::TestX509Request < OpenSSL::TestCase
assert_equal(0, req.version)
req = OpenSSL::X509::Request.new(req.to_der)
assert_equal(0, req.version)
-
- req = issue_csr(1, @dn, @rsa1024, OpenSSL::Digest.new('SHA256'))
- assert_equal(1, req.version)
- req = OpenSSL::X509::Request.new(req.to_der)
- assert_equal(1, req.version)
end
def test_subject
@@ -106,7 +101,7 @@ class OpenSSL::TestX509Request < OpenSSL::TestCase
assert_equal(false, req.verify(@rsa2048))
assert_equal(false, request_error_returns_false { req.verify(@dsa256) })
assert_equal(false, request_error_returns_false { req.verify(@dsa512) })
- req.version = 1
+ req.subject = OpenSSL::X509::Name.parse("/C=JP/CN=FooBarFooBar")
assert_equal(false, req.verify(@rsa1024))
rescue OpenSSL::X509::RequestError # RHEL 9 disables SHA1
end
diff --git a/test/openssl/utils.rb b/test/openssl/utils.rb
index e474fccaf8..f6c84eef67 100644
--- a/test/openssl/utils.rb
+++ b/test/openssl/utils.rb
@@ -1,44 +1,14 @@
# frozen_string_literal: true
begin
require "openssl"
-
- # Disable FIPS mode for tests for installations
- # where FIPS mode would be enabled by default.
- # Has no effect on all other installations.
- OpenSSL.fips_mode=false
rescue LoadError
end
-# Compile OpenSSL with crypto-mdebug and run this test suite with OSSL_MDEBUG=1
-# environment variable to enable memory leak check.
-if ENV["OSSL_MDEBUG"] == "1"
- if OpenSSL.respond_to?(:print_mem_leaks)
- OpenSSL.mem_check_start
-
- END {
- GC.start
- case OpenSSL.print_mem_leaks
- when nil
- warn "mdebug: check what is printed"
- when true
- raise "mdebug: memory leaks detected"
- end
- }
- else
- warn "OSSL_MDEBUG=1 is specified but OpenSSL is not built with crypto-mdebug"
- end
-end
-
require "test/unit"
+require "core_assertions"
require "tempfile"
require "socket"
-begin
- require_relative "../lib/core_assertions"
-rescue LoadError
- # for ruby/ruby repository
-end
-
if defined?(OpenSSL)
module OpenSSL::TestUtils
@@ -136,11 +106,12 @@ module OpenSSL::TestUtils
end
end
- def openssl?(major = nil, minor = nil, fix = nil, patch = 0)
+ def openssl?(major = nil, minor = nil, fix = nil, patch = 0, status = 0)
return false if OpenSSL::OPENSSL_VERSION.include?("LibreSSL")
return true unless major
OpenSSL::OPENSSL_VERSION_NUMBER >=
- major * 0x10000000 + minor * 0x100000 + fix * 0x1000 + patch * 0x10
+ major * 0x10000000 + minor * 0x100000 + fix * 0x1000 + patch * 0x10 +
+ status * 0x1
end
def libressl?(major = nil, minor = nil, fix = nil)
@@ -168,6 +139,30 @@ class OpenSSL::TestCase < Test::Unit::TestCase
# OpenSSL error stack must be empty
assert_equal([], OpenSSL.errors)
end
+
+ # Omit the tests in FIPS.
+ #
+ # For example, the password based encryption used in the PEM format uses MD5
+ # for deriving the encryption key from the password, and MD5 is not
+ # FIPS-approved.
+ #
+ # See https://github.com/openssl/openssl/discussions/21830#discussioncomment-6865636
+ # for details.
+ def omit_on_fips
+ return unless OpenSSL.fips_mode
+
+ omit <<~MESSAGE
+ Only for OpenSSL non-FIPS with the following possible reasons:
+ * A testing logic is non-FIPS specific.
+ * An encryption used in the test is not FIPS-approved.
+ MESSAGE
+ end
+
+ def omit_on_non_fips
+ return if OpenSSL.fips_mode
+
+ omit "Only for OpenSSL FIPS"
+ end
end
class OpenSSL::SSLTestCase < OpenSSL::TestCase
diff --git a/test/optparse/test_acceptable.rb b/test/optparse/test_acceptable.rb
index 12f5322726..c7ea2152fc 100644
--- a/test/optparse/test_acceptable.rb
+++ b/test/optparse/test_acceptable.rb
@@ -8,6 +8,7 @@ class TestOptionParserAcceptable < TestOptionParser
@opt.def_option("--integer VAL", Integer) { |v| @integer = v }
@opt.def_option("--float VAL", Float) { |v| @float = v }
@opt.def_option("--numeric VAL", Numeric) { |v| @numeric = v }
+ @opt.def_option("--array VAL", Array) { |v| @array = v }
@opt.def_option("--decimal-integer VAL",
OptionParser::DecimalInteger) { |i| @decimal_integer = i }
@@ -195,4 +196,8 @@ class TestOptionParserAcceptable < TestOptionParser
end
end
+ def test_array
+ assert_equal(%w"", no_error {@opt.parse!(%w"--array a,b,c")})
+ assert_equal(%w"a b c", @array)
+ end
end
diff --git a/test/optparse/test_optarg.rb b/test/optparse/test_optarg.rb
index 81127a8a37..f94460527f 100644
--- a/test/optparse/test_optarg.rb
+++ b/test/optparse/test_optarg.rb
@@ -9,6 +9,8 @@ class TestOptionParserOptArg < TestOptionParser
@opt.def_option("--regexp[=REGEXP]", Regexp) {|x| @reopt = x}
@opt.def_option "--with_underscore[=VAL]" do |x| @flag = x end
@opt.def_option "--with-hyphen[=VAL]" do |x| @flag = x end
+ @opt.def_option("--fallback[=VAL]") do |x = "fallback"| @flag = x end
+ @opt.def_option("--lambda[=VAL]", &->(x) {@flag = x})
@reopt = nil
end
@@ -57,4 +59,18 @@ class TestOptionParserOptArg < TestOptionParser
assert_equal(%w"", no_error {@opt.parse!(%w"--with_hyphen=foo4")})
assert_equal("foo4", @flag)
end
+
+ def test_default_argument
+ assert_equal(%w"", no_error {@opt.parse!(%w"--fallback=val1")})
+ assert_equal("val1", @flag)
+ assert_equal(%w"", no_error {@opt.parse!(%w"--fallback")})
+ assert_equal("fallback", @flag)
+ end
+
+ def test_lambda
+ assert_equal(%w"", no_error {@opt.parse!(%w"--lambda=lambda1")})
+ assert_equal("lambda1", @flag)
+ assert_equal(%w"", no_error {@opt.parse!(%w"--lambda")})
+ assert_equal(nil, @flag)
+ end
end
diff --git a/test/optparse/test_optparse.rb b/test/optparse/test_optparse.rb
index bfa705ad03..8d09e0f28b 100644
--- a/test/optparse/test_optparse.rb
+++ b/test/optparse/test_optparse.rb
@@ -73,10 +73,17 @@ class TestOptionParser < Test::Unit::TestCase
@opt.def_option "-p", "--port=PORT", "port", Integer
@opt.def_option "-v", "--verbose" do @verbose = true end
@opt.def_option "-q", "--quiet" do @quiet = true end
+ @opt.def_option "-o", "--option [OPT]" do |opt| @option = opt end
result = {}
@opt.parse %w(--host localhost --port 8000 -v), into: result
assert_equal({host: "localhost", port: 8000, verbose: true}, result)
assert_equal(true, @verbose)
+ result = {}
+ @opt.parse %w(--option -q), into: result
+ assert_equal({quiet: true, option: nil}, result)
+ result = {}
+ @opt.parse %w(--option OPTION -v), into: result
+ assert_equal({verbose: true, option: "OPTION"}, result)
end
def test_require_exact
@@ -88,9 +95,9 @@ class TestOptionParser < Test::Unit::TestCase
end
@opt.require_exact = true
- %w(--zrs -F -Ffoo).each do |arg|
+ [%w(--zrs foo), %w(--zrs=foo), %w(-F foo), %w(-Ffoo)].each do |args|
result = {}
- @opt.parse([arg, 'foo'], into: result)
+ @opt.parse(args, into: result)
assert_equal({zrs: 'foo'}, result)
end
@@ -99,6 +106,43 @@ class TestOptionParser < Test::Unit::TestCase
assert_raise(OptionParser::InvalidOption) {@opt.parse(%w(-zrs foo))}
assert_raise(OptionParser::InvalidOption) {@opt.parse(%w(-zr foo))}
assert_raise(OptionParser::InvalidOption) {@opt.parse(%w(-z foo))}
+
+ @opt.def_option('-f', '--[no-]foo', 'foo') {|arg| @foo = arg}
+ @opt.parse(%w[-f])
+ assert_equal(true, @foo)
+ @opt.parse(%w[--foo])
+ assert_equal(true, @foo)
+ @opt.parse(%w[--no-foo])
+ assert_equal(false, @foo)
+ end
+
+ def test_exact_option
+ @opt.def_option('-F', '--zrs=IRS', 'zrs')
+ %w(--zrs --zr --z -zfoo -z -F -Ffoo).each do |arg|
+ result = {}
+ @opt.parse([arg, 'foo'], into: result)
+ assert_equal({zrs: 'foo'}, result)
+ end
+
+ [%w(--zrs foo), %w(--zrs=foo), %w(-F foo), %w(-Ffoo)].each do |args|
+ result = {}
+ @opt.parse(args, into: result, exact: true)
+ assert_equal({zrs: 'foo'}, result)
+ end
+
+ assert_raise(OptionParser::InvalidOption) {@opt.parse(%w(--zr foo), exact: true)}
+ assert_raise(OptionParser::InvalidOption) {@opt.parse(%w(--z foo), exact: true)}
+ assert_raise(OptionParser::InvalidOption) {@opt.parse(%w(-zrs foo), exact: true)}
+ assert_raise(OptionParser::InvalidOption) {@opt.parse(%w(-zr foo), exact: true)}
+ assert_raise(OptionParser::InvalidOption) {@opt.parse(%w(-z foo), exact: true)}
+
+ @opt.def_option('-f', '--[no-]foo', 'foo') {|arg| @foo = arg}
+ @opt.parse(%w[-f], exact: true)
+ assert_equal(true, @foo)
+ @opt.parse(%w[--foo], exact: true)
+ assert_equal(true, @foo)
+ @opt.parse(%w[--no-foo], exact: true)
+ assert_equal(false, @foo)
end
def test_raise_unknown
@@ -120,4 +164,44 @@ class TestOptionParser < Test::Unit::TestCase
e = assert_raise(OptionParser::InvalidOption) {@opt.parse(%w(-t))}
assert_equal(["-t"], e.args)
end
+
+ def test_help_pager
+ require 'tmpdir'
+ Dir.mktmpdir do |dir|
+ File.open(File.join(dir, "options.rb"), "w") do |f|
+ f.puts "#{<<~"begin;"}\n#{<<~'end;'}"
+ begin;
+ stdout = STDOUT.dup
+ def stdout.tty?; true; end
+ Object.__send__(:remove_const, :STDOUT)
+ STDOUT = stdout
+ ARGV.options do |opt|
+ end;
+ 100.times {|i| f.puts " opt.on('--opt-#{i}') {}"}
+ f.puts "#{<<~"begin;"}\n#{<<~'end;'}"
+ begin;
+ opt.parse!
+ end
+ end;
+ end
+
+ optparse = $".find {|path| path.end_with?("/optparse.rb")}
+ args = ["-r#{optparse}", "options.rb", "--help"]
+ cmd = File.join(dir, "pager.cmd")
+ if RbConfig::CONFIG["EXECUTABLE_EXTS"]&.include?(".cmd")
+ command = "@echo off"
+ else # if File.executable?("/bin/sh")
+ # TruffleRuby just calls `posix_spawnp` and no fallback to `/bin/sh`.
+ command = "#!/bin/sh\n"
+ end
+
+ [
+ [{"RUBY_PAGER"=>cmd, "PAGER"=>"echo ng"}, "Executing RUBY_PAGER"],
+ [{"RUBY_PAGER"=>nil, "PAGER"=>cmd}, "Executing PAGER"],
+ ].each do |env, expected|
+ File.write(cmd, "#{command}\n" "echo #{expected}\n", perm: 0o700)
+ assert_in_out_err([env, *args], "", [expected], chdir: dir)
+ end
+ end
+ end
end
diff --git a/test/optparse/test_placearg.rb b/test/optparse/test_placearg.rb
index ed0e4d3e6c..a8a11e676b 100644
--- a/test/optparse/test_placearg.rb
+++ b/test/optparse/test_placearg.rb
@@ -13,6 +13,8 @@ class TestOptionParserPlaceArg < TestOptionParser
@reopt = nil
@opt.def_option "--with_underscore=VAL" do |x| @flag = x end
@opt.def_option "--with-hyphen=VAL" do |x| @flag = x end
+ @opt.def_option("--fallback [VAL]") do |x = "fallback"| @flag = x end
+ @opt.def_option("--lambda [VAL]", &->(x) {@flag = x})
end
def test_short
@@ -73,4 +75,22 @@ class TestOptionParserPlaceArg < TestOptionParser
assert_equal(%w"te.rb", no_error('[ruby-dev:38333]') {@opt.parse!(%w"-T1 te.rb")})
assert_equal(1, @topt)
end
+
+ def test_default_argument
+ assert_equal(%w"", no_error {@opt.parse!(%w"--fallback=val1")})
+ assert_equal("val1", @flag)
+ assert_equal(%w"", no_error {@opt.parse!(%w"--fallback val2")})
+ assert_equal("val2", @flag)
+ assert_equal(%w"", no_error {@opt.parse!(%w"--fallback")})
+ assert_equal("fallback", @flag)
+ end
+
+ def test_lambda
+ assert_equal(%w"", no_error {@opt.parse!(%w"--lambda=lambda1")})
+ assert_equal("lambda1", @flag)
+ assert_equal(%w"", no_error {@opt.parse!(%w"--lambda lambda2")})
+ assert_equal("lambda2", @flag)
+ assert_equal(%w"", no_error {@opt.parse!(%w"--lambda")})
+ assert_equal(nil, @flag)
+ end
end
diff --git a/test/optparse/test_reqarg.rb b/test/optparse/test_reqarg.rb
index d5686d13aa..31d4fef417 100644
--- a/test/optparse/test_reqarg.rb
+++ b/test/optparse/test_reqarg.rb
@@ -6,6 +6,7 @@ module TestOptionParserReqArg
super
@opt.def_option "--with_underscore=VAL" do |x| @flag = x end
@opt.def_option "--with-hyphen=VAL" do |x| @flag = x end
+ @opt.def_option("--lambda=VAL", &->(x) {@flag = x})
end
class Def1 < TestOptionParser
@@ -81,6 +82,11 @@ module TestOptionParserReqArg
assert_equal("foo4", @flag)
end
+ def test_lambda
+ assert_equal(%w"", no_error {@opt.parse!(%w"--lambda=lambda1")})
+ assert_equal("lambda1", @flag)
+ end
+
class TestOptionParser::WithPattern < TestOptionParser
def test_pattern
pat = num = nil
diff --git a/test/ostruct/test_ostruct.rb b/test/ostruct/test_ostruct.rb
index 256db7a0c7..19bb606145 100644
--- a/test/ostruct/test_ostruct.rb
+++ b/test/ostruct/test_ostruct.rb
@@ -412,4 +412,23 @@ class TC_OpenStruct < Test::Unit::TestCase
assert_equal('my-class', os.class)
assert_equal(OpenStruct, os.class!)
end
+
+ has_performance_warnings = begin
+ Warning[:performance]
+ true
+ rescue NoMethodError, ArgumentError
+ false
+ end
+
+ if has_performance_warnings
+ def test_performance_warning
+ assert_in_out_err(
+ %w(-Ilib -rostruct -w -W:performance -e) + ['OpenStruct.new(a: 1)'],
+ "",
+ [],
+ ["-e:1: warning: OpenStruct use is discouraged for performance reasons"],
+ success: true,
+ )
+ end
+ end
end
diff --git a/test/prism/attribute_write_test.rb b/test/prism/attribute_write_test.rb
new file mode 100644
index 0000000000..bd83d72da3
--- /dev/null
+++ b/test/prism/attribute_write_test.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+module Prism
+ class AttributeWriteTest < TestCase
+ module Target
+ def self.value
+ 2
+ end
+
+ def self.value=(value)
+ 2
+ end
+
+ def self.[]=(index, value)
+ 2
+ end
+ end
+
+ def test_named_call_with_operator
+ assert_attribute_write("Target.value = 1")
+ end
+
+ def test_named_call_without_operator
+ assert_attribute_write("Target.value=(1)")
+ end
+
+ def test_indexed_call_with_operator
+ assert_attribute_write("Target[0] = 1")
+ end
+
+ def test_indexed_call_without_operator
+ refute_attribute_write("Target.[]=(0, 1)")
+ end
+
+ def test_comparison_operators
+ refute_attribute_write("Target.value == 1")
+ refute_attribute_write("Target.value === 1")
+ end
+
+ private
+
+ def parse(source)
+ Prism.parse(source).value.statements.body.first
+ end
+
+ def assert_attribute_write(source)
+ call = parse(source)
+ assert(call.attribute_write?)
+ assert_equal(1, eval(source))
+ end
+
+ def refute_attribute_write(source)
+ call = parse(source)
+ refute(call.attribute_write?)
+ refute_equal(1, eval(source))
+ end
+ end
+end
diff --git a/test/prism/bom_test.rb b/test/prism/bom_test.rb
new file mode 100644
index 0000000000..1525caf458
--- /dev/null
+++ b/test/prism/bom_test.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+# Don't bother checking this on these engines, this is such a specific Ripper
+# test.
+return if RUBY_ENGINE == "jruby" || RUBY_ENGINE == "truffleruby"
+
+require_relative "test_helper"
+
+module Prism
+ class BOMTest < TestCase
+ def test_ident
+ assert_bom("foo")
+ end
+
+ def test_back_reference
+ assert_bom("$+")
+ end
+
+ def test_instance_variable
+ assert_bom("@foo")
+ end
+
+ def test_class_variable
+ assert_bom("@@foo")
+ end
+
+ def test_global_variable
+ assert_bom("$foo")
+ end
+
+ def test_numbered_reference
+ assert_bom("$1")
+ end
+
+ def test_percents
+ assert_bom("%i[]")
+ assert_bom("%r[]")
+ assert_bom("%s[]")
+ assert_bom("%q{}")
+ assert_bom("%w[]")
+ assert_bom("%x[]")
+ assert_bom("%I[]")
+ assert_bom("%W[]")
+ assert_bom("%Q{}")
+ end
+
+ def test_string
+ assert_bom("\"\"")
+ assert_bom("''")
+ end
+
+ private
+
+ def assert_bom(source)
+ bommed = "\xEF\xBB\xBF#{source}"
+ assert_equal Prism.lex_ripper(bommed), Prism.lex_compat(bommed).value
+ end
+ end
+end
diff --git a/test/prism/command_line_test.rb b/test/prism/command_line_test.rb
new file mode 100644
index 0000000000..4b04c36f3a
--- /dev/null
+++ b/test/prism/command_line_test.rb
@@ -0,0 +1,111 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+module Prism
+ class CommandLineTest < TestCase
+ def test_command_line_p
+ program = Prism.parse("1", command_line: "p").value
+ statements = program.statements.body
+
+ assert_equal 2, statements.length
+ assert_kind_of CallNode, statements.last
+ assert_equal :print, statements.last.name
+ end
+
+ def test_command_line_n
+ program = Prism.parse("1", command_line: "n").value
+ statements = program.statements.body
+
+ assert_equal 1, statements.length
+ assert_kind_of WhileNode, statements.first
+
+ predicate = statements.first.predicate
+ assert_kind_of CallNode, predicate
+ assert_equal :gets, predicate.name
+
+ arguments = predicate.arguments.arguments
+ assert_equal 1, arguments.length
+ assert_equal :$/, arguments.first.name
+ end
+
+ def test_command_line_a
+ program = Prism.parse("1", command_line: "na").value
+ statements = program.statements.body
+
+ assert_equal 1, statements.length
+ assert_kind_of WhileNode, statements.first
+
+ statement = statements.first.statements.body.first
+ assert_kind_of GlobalVariableWriteNode, statement
+ assert_equal :$F, statement.name
+ end
+
+ def test_command_line_l
+ program = Prism.parse("1", command_line: "nl").value
+ statements = program.statements.body
+
+ assert_equal 1, statements.length
+ assert_kind_of WhileNode, statements.first
+
+ predicate = statements.first.predicate
+ assert_kind_of CallNode, predicate
+ assert_equal :gets, predicate.name
+
+ arguments = predicate.arguments.arguments
+ assert_equal 2, arguments.length
+ assert_equal :$/, arguments.first.name
+ assert_equal "chomp", arguments.last.elements.first.key.unescaped
+ end
+
+ def test_command_line_e
+ result = Prism.parse("1 if 2..3")
+ assert_equal 2, result.warnings.length
+
+ result = Prism.parse("1 if 2..3", command_line: "e")
+ assert_equal 0, result.warnings.length
+ end
+
+ def test_command_line_x_implicit
+ result = Prism.parse(<<~RUBY)
+ #!/bin/bash
+ exit 1
+
+ #!/usr/bin/env ruby
+ 1
+ RUBY
+
+ assert_kind_of IntegerNode, result.value.statements.body.first
+ end
+
+ def test_command_line_x_explicit
+ result = Prism.parse(<<~RUBY, command_line: "x")
+ exit 1
+
+ #!/usr/bin/env ruby
+ 1
+ RUBY
+
+ assert_kind_of IntegerNode, result.value.statements.body.first
+ end
+
+ def test_command_line_x_implicit_fail
+ result = Prism.parse(<<~RUBY)
+ #!/bin/bash
+ exit 1
+ RUBY
+
+ assert_equal 1, result.errors.length
+ assert_equal :load, result.errors.first.level
+ end
+
+ def test_command_line_x_explicit_fail
+ result = Prism.parse(<<~RUBY, command_line: "x")
+ exit 1
+ RUBY
+
+ assert_equal 1, result.errors.length
+ assert_equal :load, result.errors.first.level
+ end
+ end
+end
diff --git a/test/prism/comments_test.rb b/test/prism/comments_test.rb
new file mode 100644
index 0000000000..b99c00268c
--- /dev/null
+++ b/test/prism/comments_test.rb
@@ -0,0 +1,138 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+module Prism
+ class CommentsTest < TestCase
+ def test_comment_inline
+ source = "# comment"
+ assert_equal [0], Debug.newlines(source)
+
+ assert_comment(
+ source,
+ InlineComment,
+ start_offset: 0,
+ end_offset: 9,
+ start_line: 1,
+ end_line: 1,
+ start_column: 0,
+ end_column: 9
+ )
+ end
+
+ def test_comment_inline_def
+ source = <<~RUBY
+ def foo
+ # a comment
+ end
+ RUBY
+
+ assert_comment(
+ source,
+ InlineComment,
+ start_offset: 10,
+ end_offset: 21,
+ start_line: 2,
+ end_line: 2,
+ start_column: 2,
+ end_column: 13
+ )
+ end
+
+ def test___END__
+ result = Prism.parse(<<~RUBY)
+ __END__
+ comment
+ RUBY
+
+ data_loc = result.data_loc
+ assert_equal 0, data_loc.start_offset
+ assert_equal 16, data_loc.end_offset
+ end
+
+ def test___END__crlf
+ result = Prism.parse("__END__\r\ncomment\r\n")
+
+ data_loc = result.data_loc
+ assert_equal 0, data_loc.start_offset
+ assert_equal 18, data_loc.end_offset
+ end
+
+ def test_comment_embedded_document
+ source = <<~RUBY
+ =begin
+ comment
+ =end
+ RUBY
+
+ assert_comment(
+ source,
+ EmbDocComment,
+ start_offset: 0,
+ end_offset: 20,
+ start_line: 1,
+ end_line: 4,
+ start_column: 0,
+ end_column: 0
+ )
+ end
+
+ def test_comment_embedded_document_with_content_on_same_line
+ source = <<~RUBY
+ =begin other stuff
+ =end
+ RUBY
+
+ assert_comment(
+ source,
+ EmbDocComment,
+ start_offset: 0,
+ end_offset: 24,
+ start_line: 1,
+ end_line: 3,
+ start_column: 0,
+ end_column: 0
+ )
+ end
+
+ def test_attaching_comments
+ source = <<~RUBY
+ # Foo class
+ class Foo
+ # bar method
+ def bar
+ # baz invocation
+ baz
+ end # bar end
+ end # Foo end
+ RUBY
+
+ result = Prism.parse(source)
+ result.attach_comments!
+ tree = result.value
+ class_node = tree.statements.body.first
+ method_node = class_node.body.body.first
+ call_node = method_node.body.body.first
+
+ assert_equal("# Foo class\n# Foo end", class_node.location.comments.map { |c| c.location.slice }.join("\n"))
+ assert_equal("# bar method\n# bar end", method_node.location.comments.map { |c| c.location.slice }.join("\n"))
+ assert_equal("# baz invocation", call_node.location.comments.map { |c| c.location.slice }.join("\n"))
+ end
+
+ private
+
+ def assert_comment(source, type, start_offset:, end_offset:, start_line:, end_line:, start_column:, end_column:)
+ result = Prism.parse(source)
+ assert result.errors.empty?, result.errors.map(&:message).join("\n")
+ assert_kind_of type, result.comments.first
+
+ location = result.comments.first.location
+ assert_equal start_offset, location.start_offset, -> { "Expected start_offset to be #{start_offset}" }
+ assert_equal end_offset, location.end_offset, -> { "Expected end_offset to be #{end_offset}" }
+ assert_equal start_line, location.start_line, -> { "Expected start_line to be #{start_line}" }
+ assert_equal end_line, location.end_line, -> { "Expected end_line to be #{end_line}" }
+ assert_equal start_column, location.start_column, -> { "Expected start_column to be #{start_column}" }
+ assert_equal end_column, location.end_column, -> { "Expected end_column to be #{end_column}" }
+ end
+ end
+end
diff --git a/test/prism/compiler_test.rb b/test/prism/compiler_test.rb
new file mode 100644
index 0000000000..9a326eb8d6
--- /dev/null
+++ b/test/prism/compiler_test.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+# typed: ignore
+
+require_relative "test_helper"
+
+module Prism
+ class CompilerTest < TestCase
+ class SExpressions < Prism::Compiler
+ def visit_arguments_node(node)
+ [:arguments, super]
+ end
+
+ def visit_call_node(node)
+ [:call, super]
+ end
+
+ def visit_integer_node(node)
+ [:integer]
+ end
+
+ def visit_program_node(node)
+ [:program, super]
+ end
+ end
+
+ def test_compiler
+ expected = [:program, [[[:call, [[:integer], [:arguments, [[:integer]]]]]]]]
+ assert_equal expected, Prism.parse("1 + 2").value.accept(SExpressions.new)
+ end
+ end
+end
diff --git a/test/prism/constant_path_node_test.rb b/test/prism/constant_path_node_test.rb
new file mode 100644
index 0000000000..dffb55c0ff
--- /dev/null
+++ b/test/prism/constant_path_node_test.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+module Prism
+ class ConstantPathNodeTest < TestCase
+ def test_full_name_for_constant_path
+ source = <<~RUBY
+ Foo:: # comment
+ Bar::Baz::
+ Qux
+ RUBY
+
+ constant_path = Prism.parse(source).value.statements.body.first
+ assert_equal("Foo::Bar::Baz::Qux", constant_path.full_name)
+ end
+
+ def test_full_name_for_constant_path_with_self
+ source = <<~RUBY
+ self:: # comment
+ Bar::Baz::
+ Qux
+ RUBY
+
+ constant_path = Prism.parse(source).value.statements.body.first
+ assert_raise(ConstantPathNode::DynamicPartsInConstantPathError) do
+ constant_path.full_name
+ end
+ end
+
+ def test_full_name_for_constant_path_with_variable
+ source = <<~RUBY
+ foo:: # comment
+ Bar::Baz::
+ Qux
+ RUBY
+
+ constant_path = Prism.parse(source).value.statements.body.first
+
+ assert_raise(ConstantPathNode::DynamicPartsInConstantPathError) do
+ constant_path.full_name
+ end
+ end
+
+ def test_full_name_for_constant_path_target
+ source = <<~RUBY
+ Foo:: # comment
+ Bar::Baz::
+ Qux, Something = [1, 2]
+ RUBY
+
+ node = Prism.parse(source).value.statements.body.first
+ assert_equal("Foo::Bar::Baz::Qux", node.lefts.first.full_name)
+ end
+
+ def test_full_name_for_constant_path_with_stovetop_start
+ source = <<~RUBY
+ ::Foo:: # comment
+ Bar::Baz::
+ Qux, Something = [1, 2]
+ RUBY
+
+ node = Prism.parse(source).value.statements.body.first
+ assert_equal("::Foo::Bar::Baz::Qux", node.lefts.first.full_name)
+ end
+
+ def test_full_name_for_constant_path_target_with_non_constant_parent
+ source = <<~RUBY
+ self::Foo, Bar = [1, 2]
+ RUBY
+
+ constant_target = Prism.parse(source).value.statements.body.first
+ dynamic, static = constant_target.lefts
+
+ assert_raise(ConstantPathNode::DynamicPartsInConstantPathError) do
+ dynamic.full_name
+ end
+
+ assert_equal("Bar", static.full_name)
+ end
+
+ def test_full_name_for_constant_read_node
+ source = <<~RUBY
+ Bar
+ RUBY
+
+ constant = Prism.parse(source).value.statements.body.first
+ assert_equal("Bar", constant.full_name)
+ end
+ end
+end
diff --git a/test/prism/desugar_compiler_test.rb b/test/prism/desugar_compiler_test.rb
new file mode 100644
index 0000000000..1a1d580d2d
--- /dev/null
+++ b/test/prism/desugar_compiler_test.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+module Prism
+ class DesugarCompilerTest < TestCase
+ def test_and_write
+ assert_desugars("(AndNode (ClassVariableReadNode) (ClassVariableWriteNode (CallNode)))", "@@foo &&= bar")
+ assert_not_desugared("Foo::Bar &&= baz", "Desugaring would execute Foo twice or need temporary variables")
+ assert_desugars("(AndNode (ConstantReadNode) (ConstantWriteNode (CallNode)))", "Foo &&= bar")
+ assert_desugars("(AndNode (GlobalVariableReadNode) (GlobalVariableWriteNode (CallNode)))", "$foo &&= bar")
+ assert_desugars("(AndNode (InstanceVariableReadNode) (InstanceVariableWriteNode (CallNode)))", "@foo &&= bar")
+ assert_desugars("(AndNode (LocalVariableReadNode) (LocalVariableWriteNode (CallNode)))", "foo &&= bar")
+ assert_desugars("(AndNode (LocalVariableReadNode) (LocalVariableWriteNode (CallNode)))", "foo = 1; foo &&= bar")
+ end
+
+ def test_or_write
+ assert_desugars("(IfNode (DefinedNode (ClassVariableReadNode)) (StatementsNode (ClassVariableReadNode)) (ElseNode (StatementsNode (ClassVariableWriteNode (CallNode)))))", "@@foo ||= bar")
+ assert_not_desugared("Foo::Bar ||= baz", "Desugaring would execute Foo twice or need temporary variables")
+ assert_desugars("(IfNode (DefinedNode (ConstantReadNode)) (StatementsNode (ConstantReadNode)) (ElseNode (StatementsNode (ConstantWriteNode (CallNode)))))", "Foo ||= bar")
+ assert_desugars("(IfNode (DefinedNode (GlobalVariableReadNode)) (StatementsNode (GlobalVariableReadNode)) (ElseNode (StatementsNode (GlobalVariableWriteNode (CallNode)))))", "$foo ||= bar")
+ assert_desugars("(OrNode (InstanceVariableReadNode) (InstanceVariableWriteNode (CallNode)))", "@foo ||= bar")
+ assert_desugars("(OrNode (LocalVariableReadNode) (LocalVariableWriteNode (CallNode)))", "foo ||= bar")
+ assert_desugars("(OrNode (LocalVariableReadNode) (LocalVariableWriteNode (CallNode)))", "foo = 1; foo ||= bar")
+ end
+
+ def test_operator_write
+ assert_desugars("(ClassVariableWriteNode (CallNode (ClassVariableReadNode) (ArgumentsNode (CallNode))))", "@@foo += bar")
+ assert_not_desugared("Foo::Bar += baz", "Desugaring would execute Foo twice or need temporary variables")
+ assert_desugars("(ConstantWriteNode (CallNode (ConstantReadNode) (ArgumentsNode (CallNode))))", "Foo += bar")
+ assert_desugars("(GlobalVariableWriteNode (CallNode (GlobalVariableReadNode) (ArgumentsNode (CallNode))))", "$foo += bar")
+ assert_desugars("(InstanceVariableWriteNode (CallNode (InstanceVariableReadNode) (ArgumentsNode (CallNode))))", "@foo += bar")
+ assert_desugars("(LocalVariableWriteNode (CallNode (LocalVariableReadNode) (ArgumentsNode (CallNode))))", "foo += bar")
+ assert_desugars("(LocalVariableWriteNode (CallNode (LocalVariableReadNode) (ArgumentsNode (CallNode))))", "foo = 1; foo += bar")
+ end
+
+ private
+
+ def ast_inspect(node)
+ parts = [node.class.name.split("::").last]
+
+ node.deconstruct_keys(nil).each do |_, value|
+ case value
+ when Node
+ parts << ast_inspect(value)
+ when Array
+ parts.concat(value.map { |element| ast_inspect(element) })
+ end
+ end
+
+ "(#{parts.join(" ")})"
+ end
+
+ # Ensure every node is only present once in the AST.
+ # If the same node is present twice it would most likely indicate it is executed twice, which is invalid semantically.
+ # This also acts as a sanity check that Node#child_nodes returns only nodes or nil (which caught a couple bugs).
+ def ensure_every_node_once_in_ast(node, all_nodes = {}.compare_by_identity)
+ if all_nodes.include?(node)
+ raise "#{node.inspect} is present multiple times in the desugared AST and likely executed multiple times"
+ else
+ all_nodes[node] = true
+ end
+ node.child_nodes.each do |child|
+ ensure_every_node_once_in_ast(child, all_nodes) unless child.nil?
+ end
+ end
+
+ def assert_desugars(expected, source)
+ ast = Prism.parse(source).value.accept(DesugarCompiler.new)
+ assert_equal expected, ast_inspect(ast.statements.body.last)
+
+ ensure_every_node_once_in_ast(ast)
+ end
+
+ def assert_not_desugared(source, reason)
+ ast = Prism.parse(source).value
+ assert_equal_nodes(ast, ast.accept(DesugarCompiler.new))
+ end
+ end
+end
diff --git a/test/prism/dispatcher_test.rb b/test/prism/dispatcher_test.rb
new file mode 100644
index 0000000000..0d8a6d35e9
--- /dev/null
+++ b/test/prism/dispatcher_test.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+module Prism
+ class DispatcherTest < TestCase
+ class TestListener
+ attr_reader :events_received
+
+ def initialize
+ @events_received = []
+ end
+
+ def on_call_node_enter(node)
+ events_received << :on_call_node_enter
+ end
+
+ def on_call_node_leave(node)
+ events_received << :on_call_node_leave
+ end
+
+ def on_integer_node_enter(node)
+ events_received << :on_integer_node_enter
+ end
+ end
+
+ def test_dispatching_events
+ listener = TestListener.new
+ dispatcher = Dispatcher.new
+ dispatcher.register(listener, :on_call_node_enter, :on_call_node_leave, :on_integer_node_enter)
+
+ root = Prism.parse(<<~RUBY).value
+ def foo
+ something(1, 2, 3)
+ end
+ RUBY
+
+ dispatcher.dispatch(root)
+ assert_equal([:on_call_node_enter, :on_integer_node_enter, :on_integer_node_enter, :on_integer_node_enter, :on_call_node_leave], listener.events_received)
+
+ listener.events_received.clear
+ dispatcher.dispatch_once(root.statements.body.first.body.body.first)
+ assert_equal([:on_call_node_enter, :on_call_node_leave], listener.events_received)
+ end
+ end
+end
diff --git a/test/prism/encoding_test.rb b/test/prism/encoding_test.rb
new file mode 100644
index 0000000000..2aee473ddf
--- /dev/null
+++ b/test/prism/encoding_test.rb
@@ -0,0 +1,577 @@
+# frozen_string_literal: true
+
+return if RUBY_ENGINE != "ruby"
+
+require_relative "test_helper"
+
+module Prism
+ class EncodingTest < TestCase
+ codepoints_1byte = 0...0x100
+ encodings = {
+ Encoding::ASCII_8BIT => codepoints_1byte,
+ Encoding::US_ASCII => codepoints_1byte
+ }
+
+ if !ENV["PRISM_BUILD_MINIMAL"]
+ encodings[Encoding::Windows_1253] = codepoints_1byte
+ end
+
+ # By default we don't test every codepoint in these encodings because it
+ # takes a very long time.
+ if ENV["PRISM_TEST_ALL_ENCODINGS"]
+ codepoints_2bytes = 0...0x10000
+ codepoints_unicode = (0...0x110000)
+
+ codepoints_eucjp = [
+ *(0...0x10000),
+ *(0...0x10000).map { |bytes| bytes | 0x8F0000 }
+ ]
+
+ codepoints_emacs_mule = [
+ *(0...0x80),
+ *((0x81...0x90).flat_map { |byte1| (0x90...0x100).map { |byte2| byte1 << 8 | byte2 } }),
+ *((0x90...0x9C).flat_map { |byte1| (0xA0...0x100).flat_map { |byte2| (0xA0...0x100).flat_map { |byte3| byte1 << 16 | byte2 << 8 | byte3 } } }),
+ *((0xF0...0xF5).flat_map { |byte2| (0xA0...0x100).flat_map { |byte3| (0xA0...0x100).flat_map { |byte4| 0x9C << 24 | byte3 << 16 | byte3 << 8 | byte4 } } }),
+ ]
+
+ codepoints_gb18030 = [
+ *(0...0x80),
+ *((0x81..0xFE).flat_map { |byte1| (0x40...0x100).map { |byte2| byte1 << 8 | byte2 } }),
+ *((0x81..0xFE).flat_map { |byte1| (0x30...0x40).flat_map { |byte2| (0x81..0xFE).flat_map { |byte3| (0x2F...0x41).map { |byte4| byte1 << 24 | byte2 << 16 | byte3 << 8 | byte4 } } } }),
+ ]
+
+ codepoints_euc_tw = [
+ *(0..0x7F),
+ *(0xA1..0xFF).flat_map { |byte1| (0xA1..0xFF).map { |byte2| (byte1 << 8) | byte2 } },
+ *(0xA1..0xB0).flat_map { |byte2| (0xA1..0xFF).flat_map { |byte3| (0xA1..0xFF).flat_map { |byte4| 0x8E << 24 | byte2 << 16 | byte3 << 8 | byte4 } } }
+ ]
+
+ encodings.merge!(
+ Encoding::CP850 => codepoints_1byte,
+ Encoding::CP852 => codepoints_1byte,
+ Encoding::CP855 => codepoints_1byte,
+ Encoding::GB1988 => codepoints_1byte,
+ Encoding::IBM437 => codepoints_1byte,
+ Encoding::IBM720 => codepoints_1byte,
+ Encoding::IBM737 => codepoints_1byte,
+ Encoding::IBM775 => codepoints_1byte,
+ Encoding::IBM852 => codepoints_1byte,
+ Encoding::IBM855 => codepoints_1byte,
+ Encoding::IBM857 => codepoints_1byte,
+ Encoding::IBM860 => codepoints_1byte,
+ Encoding::IBM861 => codepoints_1byte,
+ Encoding::IBM862 => codepoints_1byte,
+ Encoding::IBM863 => codepoints_1byte,
+ Encoding::IBM864 => codepoints_1byte,
+ Encoding::IBM865 => codepoints_1byte,
+ Encoding::IBM866 => codepoints_1byte,
+ Encoding::IBM869 => codepoints_1byte,
+ Encoding::ISO_8859_1 => codepoints_1byte,
+ Encoding::ISO_8859_2 => codepoints_1byte,
+ Encoding::ISO_8859_3 => codepoints_1byte,
+ Encoding::ISO_8859_4 => codepoints_1byte,
+ Encoding::ISO_8859_5 => codepoints_1byte,
+ Encoding::ISO_8859_6 => codepoints_1byte,
+ Encoding::ISO_8859_7 => codepoints_1byte,
+ Encoding::ISO_8859_8 => codepoints_1byte,
+ Encoding::ISO_8859_9 => codepoints_1byte,
+ Encoding::ISO_8859_10 => codepoints_1byte,
+ Encoding::ISO_8859_11 => codepoints_1byte,
+ Encoding::ISO_8859_13 => codepoints_1byte,
+ Encoding::ISO_8859_14 => codepoints_1byte,
+ Encoding::ISO_8859_15 => codepoints_1byte,
+ Encoding::ISO_8859_16 => codepoints_1byte,
+ Encoding::KOI8_R => codepoints_1byte,
+ Encoding::KOI8_U => codepoints_1byte,
+ Encoding::MACCENTEURO => codepoints_1byte,
+ Encoding::MACCROATIAN => codepoints_1byte,
+ Encoding::MACCYRILLIC => codepoints_1byte,
+ Encoding::MACGREEK => codepoints_1byte,
+ Encoding::MACICELAND => codepoints_1byte,
+ Encoding::MACROMAN => codepoints_1byte,
+ Encoding::MACROMANIA => codepoints_1byte,
+ Encoding::MACTHAI => codepoints_1byte,
+ Encoding::MACTURKISH => codepoints_1byte,
+ Encoding::MACUKRAINE => codepoints_1byte,
+ Encoding::TIS_620 => codepoints_1byte,
+ Encoding::Windows_1250 => codepoints_1byte,
+ Encoding::Windows_1251 => codepoints_1byte,
+ Encoding::Windows_1252 => codepoints_1byte,
+ Encoding::Windows_1254 => codepoints_1byte,
+ Encoding::Windows_1255 => codepoints_1byte,
+ Encoding::Windows_1256 => codepoints_1byte,
+ Encoding::Windows_1257 => codepoints_1byte,
+ Encoding::Windows_1258 => codepoints_1byte,
+ Encoding::Windows_874 => codepoints_1byte,
+ Encoding::Big5 => codepoints_2bytes,
+ Encoding::Big5_HKSCS => codepoints_2bytes,
+ Encoding::Big5_UAO => codepoints_2bytes,
+ Encoding::CP949 => codepoints_2bytes,
+ Encoding::CP950 => codepoints_2bytes,
+ Encoding::CP951 => codepoints_2bytes,
+ Encoding::EUC_KR => codepoints_2bytes,
+ Encoding::GBK => codepoints_2bytes,
+ Encoding::GB12345 => codepoints_2bytes,
+ Encoding::GB2312 => codepoints_2bytes,
+ Encoding::MACJAPANESE => codepoints_2bytes,
+ Encoding::Shift_JIS => codepoints_2bytes,
+ Encoding::SJIS_DoCoMo => codepoints_2bytes,
+ Encoding::SJIS_KDDI => codepoints_2bytes,
+ Encoding::SJIS_SoftBank => codepoints_2bytes,
+ Encoding::Windows_31J => codepoints_2bytes,
+ Encoding::UTF_8 => codepoints_unicode,
+ Encoding::UTF8_MAC => codepoints_unicode,
+ Encoding::UTF8_DoCoMo => codepoints_unicode,
+ Encoding::UTF8_KDDI => codepoints_unicode,
+ Encoding::UTF8_SoftBank => codepoints_unicode,
+ Encoding::CESU_8 => codepoints_unicode,
+ Encoding::CP51932 => codepoints_eucjp,
+ Encoding::EUC_JP => codepoints_eucjp,
+ Encoding::EUCJP_MS => codepoints_eucjp,
+ Encoding::EUC_JIS_2004 => codepoints_eucjp,
+ Encoding::EMACS_MULE => codepoints_emacs_mule,
+ Encoding::STATELESS_ISO_2022_JP => codepoints_emacs_mule,
+ Encoding::STATELESS_ISO_2022_JP_KDDI => codepoints_emacs_mule,
+ Encoding::GB18030 => codepoints_gb18030,
+ Encoding::EUC_TW => codepoints_euc_tw
+ )
+ end
+
+ # These test that we're correctly parsing codepoints for each alias of each
+ # encoding that prism supports.
+ encodings.each do |encoding, range|
+ (encoding.names - %w[external internal filesystem locale]).each do |name|
+ define_method(:"test_encoding_#{name}") do
+ assert_encoding(encoding, name, range)
+ end
+ end
+ end
+
+ # These test that we're correctly setting the flags on strings for each
+ # encoding that prism supports.
+ escapes = ["\\x00", "\\x7F", "\\x80", "\\xFF", "\\u{00}", "\\u{7F}", "\\u{80}", "\\M-\\C-?"]
+ escapes = escapes.concat(escapes.product(escapes).map(&:join))
+ symbols = [:a, :Ä…, :+]
+ regexps = [/a/, /Ä…/, //]
+
+ encodings.each_key do |encoding|
+ define_method(:"test_encoding_flags_#{encoding.name}") do
+ assert_encoding_flags(encoding, escapes)
+ end
+
+ define_method(:"test_symbol_encoding_flags_#{encoding.name}") do
+ assert_symbol_encoding_flags(encoding, symbols)
+ end
+
+ define_method(:"test_symbol_character_escape_encoding_flags_#{encoding.name}") do
+ assert_symbol_character_escape_encoding_flags(encoding, escapes)
+ end
+
+ define_method(:"test_regular_expression_encoding_flags_#{encoding.name}") do
+ assert_regular_expression_encoding_flags(encoding, regexps.map(&:inspect))
+ end
+
+ define_method(:"test_regular_expression_escape_encoding_flags_#{encoding.name}") do
+ assert_regular_expression_encoding_flags(encoding, escapes.map { |e| "/#{e}/" })
+ end
+ end
+
+ encoding_modifiers = { ascii_8bit: "n", utf_8: "u", euc_jp: "e", windows_31j: "s" }
+ regexp_sources = ["abc", "garçon", "\\x80", "gar\\xC3\\xA7on", "gar\\u{E7}on", "abc\\u{FFFFFF}", "\\x80\\u{80}" ]
+
+ encoding_modifiers.each_value do |modifier|
+ encodings.each_key do |encoding|
+ define_method(:"test_regular_expression_encoding_modifiers_/#{modifier}_#{encoding.name}") do
+ assert_regular_expression_encoding_flags(
+ encoding,
+ regexp_sources.product(encoding_modifiers.values).map { |r, modifier| "/#{r}/#{modifier}" }
+ )
+ end
+ end
+ end
+
+ def test_coding
+ result = Prism.parse("# coding: utf-8\n'string'")
+ actual = result.value.statements.body.first.unescaped.encoding
+ assert_equal Encoding.find("utf-8"), actual
+ end
+
+ def test_coding_with_whitespace
+ result = Prism.parse("# coding \t \r \v : \t \v \r ascii-8bit \n'string'")
+ actual = result.value.statements.body.first.unescaped.encoding
+ assert_equal Encoding.find("ascii-8bit"), actual
+ end
+
+ def test_emacs_style
+ result = Prism.parse("# -*- coding: utf-8 -*-\n'string'")
+ actual = result.value.statements.body.first.unescaped.encoding
+ assert_equal Encoding.find("utf-8"), actual
+ end
+
+ def test_utf_8_variations
+ %w[
+ utf-8-unix
+ utf-8-dos
+ utf-8-mac
+ utf-8-*
+ ].each do |encoding|
+ result = Prism.parse("# coding: #{encoding}\n'string'")
+ actual = result.value.statements.body.first.unescaped.encoding
+ assert_equal Encoding.find("utf-8"), actual
+ end
+ end
+
+ def test_first_lexed_token
+ encoding = Prism.lex("# encoding: ascii-8bit").value[0][0].value.encoding
+ assert_equal Encoding.find("ascii-8bit"), encoding
+ end
+
+ if !ENV["PRISM_BUILD_MINIMAL"]
+ # This test may be a little confusing. Basically when we use our strpbrk,
+ # it takes into account the encoding of the file.
+ def test_strpbrk_multibyte
+ result = Prism.parse(<<~RUBY)
+ # encoding: Shift_JIS
+ %w[\x81\x5c]
+ RUBY
+
+ assert(result.errors.empty?)
+ assert_equal(
+ (+"\x81\x5c").force_encoding(Encoding::Shift_JIS),
+ result.value.statements.body.first.elements.first.unescaped
+ )
+ end
+
+ def test_slice_encoding
+ slice = Prism.parse("# encoding: Shift_JIS\nã‚¢").value.slice
+ assert_equal (+"ã‚¢").force_encoding(Encoding::SHIFT_JIS), slice
+ assert_equal Encoding::SHIFT_JIS, slice.encoding
+ end
+
+ def test_multibyte_escapes
+ [
+ ["'", "'"],
+ ["\"", "\""],
+ ["`", "`"],
+ ["/", "/"],
+ ["<<'HERE'\n", "\nHERE"],
+ ["<<-HERE\n", "\nHERE"]
+ ].each do |opening, closing|
+ assert Prism.parse_success?("# encoding: shift_jis\n'\\\x82\xA0'\n")
+ end
+ end
+ end
+
+ private
+
+ class ConstantContext < BasicObject
+ def self.const_missing(const)
+ const
+ end
+ end
+
+ def constant_context
+ ConstantContext.new
+ end
+
+ class IdentifierContext < BasicObject
+ def method_missing(name, *)
+ name
+ end
+ end
+
+ def identifier_context
+ IdentifierContext.new
+ end
+
+ def assert_encoding_constant(name, character)
+ source = "# encoding: #{name}\n#{character}"
+ expected = constant_context.instance_eval(source)
+
+ result = Prism.parse(source)
+ assert result.success?
+
+ actual = result.value.statements.body.last
+ assert_kind_of ConstantReadNode, actual
+ assert_equal expected, actual.name
+ end
+
+ def assert_encoding_identifier(name, character)
+ source = "# encoding: #{name}\n#{character}"
+ expected = identifier_context.instance_eval(source)
+
+ result = Prism.parse(source)
+ assert result.success?
+
+ actual = result.value.statements.body.last
+ assert_kind_of CallNode, actual
+ assert_equal expected, actual.name
+ end
+
+ # Check that we can properly parse every codepoint in the given encoding.
+ def assert_encoding(encoding, name, range)
+ # I'm not entirely sure, but I believe these codepoints are incorrect in
+ # their parsing in CRuby. They all report as matching `[[:lower:]]` but
+ # then they are parsed as constants. This is because CRuby determines if
+ # an identifier is a constant or not by case folding it down to lowercase
+ # and checking if there is a difference. And even though they report
+ # themselves as lowercase, their case fold is different. I have reported
+ # this bug upstream.
+ case encoding
+ when Encoding::UTF_8, Encoding::UTF_8_MAC, Encoding::UTF8_DoCoMo, Encoding::UTF8_KDDI, Encoding::UTF8_SoftBank, Encoding::CESU_8
+ range = range.to_a - [
+ 0x01c5, 0x01c8, 0x01cb, 0x01f2, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b,
+ 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b,
+ 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab,
+ 0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fbc, 0x1fcc, 0x1ffc,
+ ]
+ when Encoding::Windows_1253
+ range = range.to_a - [0xb5]
+ end
+
+ range.each do |codepoint|
+ character = codepoint.chr(encoding)
+
+ if character.match?(/[[:alpha:]]/)
+ if character.match?(/[[:upper:]]/)
+ assert_encoding_constant(name, character)
+ else
+ assert_encoding_identifier(name, character)
+ end
+ elsif character.match?(/[[:alnum:]]/)
+ assert_encoding_identifier(name, "_#{character}")
+ else
+ next if ["/", "{"].include?(character)
+
+ source = "# encoding: #{name}\n/(?##{character})/\n"
+ assert Prism.parse(source).success?, "Expected #{source.inspect} to parse successfully."
+ end
+ rescue RangeError
+ source = "# encoding: #{name}\n\\x#{codepoint.to_s(16)}"
+ refute Prism.parse(source).success?
+ end
+ end
+
+ def assert_encoding_flags(encoding, escapes)
+ escapes.each do |escaped|
+ source = "# encoding: #{encoding.name}\n\"#{escaped}\""
+
+ expected =
+ begin
+ eval(source).encoding
+ rescue SyntaxError => error
+ if error.message.include?("UTF-8 mixed within")
+ error.message[/: (.+?)\n/, 1]
+ else
+ raise
+ end
+ end
+
+ actual =
+ Prism.parse(source).then do |result|
+ if result.success?
+ string = result.value.statements.body.first
+
+ if string.forced_utf8_encoding?
+ Encoding::UTF_8
+ elsif string.forced_binary_encoding?
+ Encoding::ASCII_8BIT
+ else
+ encoding
+ end
+ else
+ error = result.errors.first
+
+ if error.message.include?("mixed")
+ error.message
+ else
+ raise error.message
+ end
+ end
+ end
+
+ assert_equal expected, actual
+ end
+ end
+
+ # Test Symbol literals without any interpolation or escape sequences.
+ def assert_symbol_encoding_flags(encoding, symbols)
+ symbols.each do |symbol|
+ source = "# encoding: #{encoding.name}\n#{symbol.inspect}"
+
+ expected =
+ begin
+ eval(source).encoding
+ rescue SyntaxError => error
+ unless error.message.include?("invalid multibyte char")
+ raise
+ end
+ end
+
+ actual =
+ Prism.parse(source).then do |result|
+ if result.success?
+ symbol = result.value.statements.body.first
+
+ if symbol.forced_utf8_encoding?
+ Encoding::UTF_8
+ elsif symbol.forced_binary_encoding?
+ Encoding::ASCII_8BIT
+ elsif symbol.forced_us_ascii_encoding?
+ Encoding::US_ASCII
+ else
+ encoding
+ end
+ else
+ error = result.errors.last
+
+ unless error.message.include?("invalid symbol")
+ raise error.message
+ end
+ end
+ end
+
+ assert_equal expected, actual
+ end
+ end
+
+ def assert_symbol_character_escape_encoding_flags(encoding, escapes)
+ escapes.each do |escaped|
+ source = "# encoding: #{encoding.name}\n:\"#{escaped}\""
+
+ expected =
+ begin
+ eval(source).encoding
+ rescue SyntaxError => error
+ if error.message.include?("UTF-8 mixed within")
+ error.message[/: (.+?)\n/, 1]
+ else
+ raise
+ end
+ end
+
+ actual =
+ Prism.parse(source).then do |result|
+ if result.success?
+ symbol = result.value.statements.body.first
+
+ if symbol.forced_utf8_encoding?
+ Encoding::UTF_8
+ elsif symbol.forced_binary_encoding?
+ Encoding::ASCII_8BIT
+ elsif symbol.forced_us_ascii_encoding?
+ Encoding::US_ASCII
+ else
+ encoding
+ end
+ else
+ error = result.errors.first
+
+ if error.message.include?("mixed")
+ error.message
+ else
+ raise error.message
+ end
+ end
+ end
+
+ assert_equal expected, actual
+ end
+ end
+
+ def assert_regular_expression_encoding_flags(encoding, regexps)
+ regexps.each do |regexp|
+ regexp_modifier_used = regexp.end_with?("/u") || regexp.end_with?("/e") || regexp.end_with?("/s") || regexp.end_with?("/n")
+ source = "# encoding: #{encoding.name}\n#{regexp}"
+
+ encoding_errors = ["invalid multibyte char", "escaped non ASCII character in UTF-8 regexp", "differs from source encoding"]
+ skipped_errors = ["invalid multibyte escape", "incompatible character encoding", "UTF-8 character in non UTF-8 regexp", "invalid Unicode range", "invalid Unicode list"]
+
+ # TODO (nirvdrum 21-Feb-2024): Prism currently does not handle Regexp validation unless modifiers are used. So, skip processing those errors for now: https://github.com/ruby/prism/issues/2104
+ unless regexp_modifier_used
+ skipped_errors += encoding_errors
+ encoding_errors.clear
+ end
+
+ expected =
+ begin
+ eval(source).encoding
+ rescue SyntaxError => error
+ if encoding_errors.find { |e| error.message.include?(e) }
+ error.message.split("\n").map { |m| m[/: (.+?)$/, 1] }
+ elsif skipped_errors.find { |e| error.message.include?(e) }
+ next
+ else
+ raise
+ end
+ end
+
+ actual =
+ Prism.parse(source).then do |result|
+ if result.success?
+ regexp = result.value.statements.body.first
+
+ actual_encoding = if regexp.forced_utf8_encoding?
+ Encoding::UTF_8
+ elsif regexp.forced_binary_encoding?
+ Encoding::ASCII_8BIT
+ elsif regexp.forced_us_ascii_encoding?
+ Encoding::US_ASCII
+ elsif regexp.ascii_8bit?
+ Encoding::ASCII_8BIT
+ elsif regexp.utf_8?
+ Encoding::UTF_8
+ elsif regexp.euc_jp?
+ Encoding::EUC_JP
+ elsif regexp.windows_31j?
+ Encoding::Windows_31J
+ else
+ encoding
+ end
+
+ if regexp.utf_8? && actual_encoding != Encoding::UTF_8
+ raise "expected regexp encoding to be UTF-8 due to '/u' modifier, but got #{actual_encoding.name}"
+ elsif regexp.ascii_8bit? && (actual_encoding != Encoding::ASCII_8BIT && actual_encoding != Encoding::US_ASCII)
+ raise "expected regexp encoding to be ASCII-8BIT or US-ASCII due to '/n' modifier, but got #{actual_encoding.name}"
+ elsif regexp.euc_jp? && actual_encoding != Encoding::EUC_JP
+ raise "expected regexp encoding to be EUC-JP due to '/e' modifier, but got #{actual_encoding.name}"
+ elsif regexp.windows_31j? && actual_encoding != Encoding::Windows_31J
+ raise "expected regexp encoding to be Windows-31J due to '/s' modifier, but got #{actual_encoding.name}"
+ end
+
+ if regexp.utf_8? && regexp.forced_utf8_encoding?
+ raise "the forced_utf8 flag should not be set when the UTF-8 modifier (/u) is used"
+ elsif regexp.ascii_8bit? && regexp.forced_binary_encoding?
+ raise "the forced_ascii_8bit flag should not be set when the UTF-8 modifier (/u) is used"
+ end
+
+ actual_encoding
+ else
+ errors = result.errors.map(&:message)
+
+ if errors.last&.include?("UTF-8 mixed within")
+ nil
+ else
+ errors
+ end
+ end
+ end
+
+ # TODO (nirvdrum 22-Feb-2024): Remove this workaround once Prism better maps CRuby's error messages.
+ # This class of error message is tricky. The part not being compared is a representation of the regexp.
+ # Depending on the source encoding and any encoding modifiers being used, CRuby alters how the regexp is represented.
+ # Sometimes it's an MBC string. Other times it uses hexadecimal character escapes. And in other cases it uses
+ # the long-form Unicode escape sequences. This short-circuit checks that the error message is mostly correct.
+ if expected.is_a?(Array) && actual.is_a?(Array)
+ if expected.last.start_with?("/.../n has a non escaped non ASCII character in non ASCII-8BIT script:") &&
+ actual.last.start_with?("/.../n has a non escaped non ASCII character in non ASCII-8BIT script:")
+ expected.last.clear
+ actual.last.clear
+ end
+ end
+
+ assert_equal expected, actual
+ end
+ end
+ end
+end
diff --git a/test/prism/errors_test.rb b/test/prism/errors_test.rb
new file mode 100644
index 0000000000..f989daa361
--- /dev/null
+++ b/test/prism/errors_test.rb
@@ -0,0 +1,2241 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+module Prism
+ class ErrorsTest < TestCase
+ include DSL
+
+ def test_constant_path_with_invalid_token_after
+ assert_error_messages "A::$b", [
+ "expected a constant after the `::` operator",
+ "unexpected global variable, expecting end-of-input"
+ ]
+ end
+
+ def test_module_name_recoverable
+ expected = ModuleNode(
+ [],
+ Location(),
+ ConstantReadNode(:Parent),
+ StatementsNode(
+ [ModuleNode([], Location(), MissingNode(), nil, Location(), :"")]
+ ),
+ Location(),
+ :Parent
+ )
+
+ assert_errors expected, "module Parent module end", [
+ ["unexpected constant path after `module`; class/module name must be CONSTANT", 14..20],
+ ["unexpected 'end', assuming it is closing the parent module definition", 21..24]
+ ]
+ end
+
+ def test_for_loops_index_missing
+ expected = ForNode(
+ MissingNode(),
+ expression("1..10"),
+ StatementsNode([expression("i")]),
+ Location(),
+ Location(),
+ nil,
+ Location()
+ )
+
+ assert_errors expected, "for in 1..10\ni\nend", [
+ ["expected an index after `for`", 0..3]
+ ]
+ end
+
+ def test_for_loops_only_end
+ expected = ForNode(
+ MissingNode(),
+ MissingNode(),
+ nil,
+ Location(),
+ Location(),
+ nil,
+ Location()
+ )
+
+ assert_errors expected, "for end", [
+ ["expected an index after `for`", 0..3],
+ ["expected an `in` after the index in a `for` statement", 3..3],
+ ["expected a collection after the `in` in a `for` statement", 3..3]
+ ]
+ end
+
+ def test_pre_execution_missing_brace
+ expected = PreExecutionNode(
+ StatementsNode([expression("1")]),
+ Location(),
+ Location(),
+ Location()
+ )
+
+ assert_errors expected, "BEGIN 1 }", [
+ ["expected a `{` after `BEGIN`", 5..5]
+ ]
+ end
+
+ def test_pre_execution_context
+ expected = PreExecutionNode(
+ StatementsNode([
+ CallNode(
+ 0,
+ expression("1"),
+ nil,
+ :+,
+ Location(),
+ nil,
+ ArgumentsNode(0, [MissingNode()]),
+ nil,
+ nil
+ )
+ ]),
+ Location(),
+ Location(),
+ Location()
+ )
+
+ assert_errors expected, "BEGIN { 1 + }", [
+ ["expected an expression after the operator", 10..11],
+ ["unexpected '}', assuming it is closing the parent 'BEGIN' block", 12..13]
+ ]
+ end
+
+ def test_unterminated_embdoc
+ message = "embedded document meets end of file"
+ assert_error_messages "=begin", [message]
+ assert_error_messages "=begin\n", [message]
+
+ refute_error_messages "=begin\n=end"
+ refute_error_messages "=begin\n=end\0"
+ refute_error_messages "=begin\n=end\C-d"
+ refute_error_messages "=begin\n=end\C-z"
+ end
+
+ def test_unterminated_i_list
+ assert_errors expression("%i["), "%i[", [
+ ["expected a closing delimiter for the `%i` list", 0..3]
+ ]
+ end
+
+ def test_unterminated_w_list
+ assert_errors expression("%w["), "%w[", [
+ ["expected a closing delimiter for the `%w` list", 0..3]
+ ]
+ end
+
+ def test_unterminated_W_list
+ assert_errors expression("%W["), "%W[", [
+ ["expected a closing delimiter for the `%W` list", 0..3]
+ ]
+ end
+
+ def test_unterminated_regular_expression
+ assert_errors expression("/hello"), "/hello", [
+ ["expected a closing delimiter for the regular expression", 0..1]
+ ]
+ end
+
+ def test_unterminated_regular_expression_with_heredoc
+ source = "<<-END + /b\nEND\n"
+
+ assert_errors expression(source), source, [
+ ["expected a closing delimiter for the regular expression", 9..10]
+ ]
+ end
+
+ def test_unterminated_xstring
+ assert_errors expression("`hello"), "`hello", [
+ ["expected a closing delimiter for the `%x` or backtick string", 0..1]
+ ]
+ end
+
+ def test_unterminated_interpolated_string
+ expr = expression('"hello')
+ assert_errors expr, '"hello', [
+ ["unterminated string meets end of file", 6..6]
+ ]
+ assert_equal expr.unescaped, "hello"
+ assert_equal expr.closing, ""
+ end
+
+ def test_unterminated_string
+ expr = expression("'hello")
+ assert_errors expr, "'hello", [
+ ["unterminated string meets end of file", 0..1]
+ ]
+ assert_equal expr.unescaped, "hello"
+ assert_equal expr.closing, ""
+ end
+
+ def test_unterminated_empty_string
+ expr = expression('"')
+ assert_errors expr, '"', [
+ ["unterminated string meets end of file", 1..1]
+ ]
+ assert_equal expr.unescaped, ""
+ assert_equal expr.closing, ""
+ end
+
+ def test_incomplete_instance_var_string
+ assert_errors expression('%@#@@#'), '%@#@@#', [
+ ["'@' without identifiers is not allowed as an instance variable name", 4..5],
+ ["unexpected instance variable, expecting end-of-input", 4..5]
+ ]
+ end
+
+ def test_unterminated_s_symbol
+ assert_errors expression("%s[abc"), "%s[abc", [
+ ["expected a closing delimiter for the dynamic symbol", 0..3]
+ ]
+ end
+
+ def test_unterminated_parenthesized_expression
+ assert_errors expression('(1 + 2'), '(1 + 2', [
+ ["unexpected end of file, expecting end-of-input", 6..6],
+ ["unexpected end of file, assuming it is closing the parent top level context", 6..6],
+ ["expected a matching `)`", 6..6]
+ ]
+ end
+
+ def test_missing_terminator_in_parentheses
+ assert_error_messages "(0 0)", [
+ "unexpected integer, expecting end-of-input"
+ ]
+ end
+
+ def test_unterminated_argument_expression
+ assert_errors expression('a %'), 'a %', [
+ ["invalid `%` token", 2..3],
+ ["expected an expression after the operator", 2..3],
+ ["unexpected end of file, assuming it is closing the parent top level context", 3..3]
+ ]
+ end
+
+ def test_unterminated_interpolated_symbol
+ assert_error_messages ":\"#", [
+ "expected a closing delimiter for the interpolated symbol"
+ ]
+ end
+
+ def test_cr_without_lf_in_percent_expression
+ assert_errors expression("%\r"), "%\r", [
+ ["invalid `%` token", 0..2],
+ ]
+ end
+
+ def test_1_2_3
+ assert_errors expression("(1, 2, 3)"), "(1, 2, 3)", [
+ ["unexpected ',', expecting end-of-input", 2..3],
+ ["unexpected ',', ignoring it", 2..3],
+ ["expected a matching `)`", 2..2],
+ ["unexpected ',', expecting end-of-input", 2..3],
+ ["unexpected ',', ignoring it", 2..3],
+ ["unexpected ',', expecting end-of-input", 5..6],
+ ["unexpected ',', ignoring it", 5..6],
+ ["unexpected ')', expecting end-of-input", 8..9],
+ ["unexpected ')', ignoring it", 8..9]
+ ]
+ end
+
+ def test_return_1_2_3
+ assert_error_messages "return(1, 2, 3)", [
+ "unexpected ',', expecting end-of-input",
+ "unexpected ',', ignoring it",
+ "expected a matching `)`",
+ "unexpected ')', expecting end-of-input",
+ "unexpected ')', ignoring it"
+ ]
+ end
+
+ def test_return_1
+ assert_errors expression("return 1,;"), "return 1,;", [
+ ["expected an argument", 8..9]
+ ]
+ end
+
+ def test_next_1_2_3
+ assert_errors expression("next(1, 2, 3)"), "next(1, 2, 3)", [
+ ["unexpected ',', expecting end-of-input", 6..7],
+ ["unexpected ',', ignoring it", 6..7],
+ ["expected a matching `)`", 6..6],
+ ["Invalid next", 0..12],
+ ["unexpected ')', expecting end-of-input", 12..13],
+ ["unexpected ')', ignoring it", 12..13]
+ ]
+ end
+
+ def test_next_1
+ assert_errors expression("next 1,;"), "next 1,;", [
+ ["expected an argument", 6..7],
+ ["Invalid next", 0..7]
+ ]
+ end
+
+ def test_break_1_2_3
+ assert_errors expression("break(1, 2, 3)"), "break(1, 2, 3)", [
+ ["unexpected ',', expecting end-of-input", 7..8],
+ ["unexpected ',', ignoring it", 7..8],
+ ["expected a matching `)`", 7..7],
+ ["Invalid break", 0..13],
+ ["unexpected ')', expecting end-of-input", 13..14],
+ ["unexpected ')', ignoring it", 13..14]
+ ]
+ end
+
+ def test_break_1
+ assert_errors expression("break 1,;"), "break 1,;", [
+ ["expected an argument", 7..8],
+ ["Invalid break", 0..8]
+ ]
+ end
+
+ def test_argument_forwarding_when_parent_is_not_forwarding
+ assert_errors expression('def a(x, y, z); b(...); end'), 'def a(x, y, z); b(...); end', [
+ ["unexpected ... when the parent method is not forwarding", 18..21]
+ ]
+ end
+
+ def test_argument_forwarding_only_effects_its_own_internals
+ assert_errors expression('def a(...); b(...); end; def c(x, y, z); b(...); end'),
+ 'def a(...); b(...); end; def c(x, y, z); b(...); end', [
+ ["unexpected ... when the parent method is not forwarding", 43..46]
+ ]
+ end
+
+ def test_top_level_constant_with_downcased_identifier
+ assert_error_messages "::foo", [
+ "expected a constant after the `::` operator",
+ "unexpected local variable or method, expecting end-of-input"
+ ]
+ end
+
+ def test_top_level_constant_starting_with_downcased_identifier
+ assert_error_messages "::foo::A", [
+ "expected a constant after the `::` operator",
+ "unexpected local variable or method, expecting end-of-input"
+ ]
+ end
+
+ def test_aliasing_global_variable_with_non_global_variable
+ assert_errors expression("alias $a b"), "alias $a b", [
+ ["invalid argument being passed to `alias`; expected a bare word, symbol, constant, or global variable", 9..10]
+ ]
+ end
+
+ def test_aliasing_non_global_variable_with_global_variable
+ assert_errors expression("alias a $b"), "alias a $b", [
+ ["invalid argument being passed to `alias`; expected a bare word, symbol, constant, or global variable", 8..10]
+ ]
+ end
+
+ def test_aliasing_global_variable_with_global_number_variable
+ assert_errors expression("alias $a $1"), "alias $a $1", [
+ ["invalid argument being passed to `alias`; can't make alias for the number variables", 9..11]
+ ]
+ end
+
+ def test_def_with_expression_receiver_and_no_identifier
+ assert_errors expression("def (a); end"), "def (a); end", [
+ ["expected a `.` or `::` after the receiver in a method definition", 7..7],
+ ["unexpected ';'; expected a method name", 7..8]
+ ]
+ end
+
+ def test_def_with_multiple_statements_receiver
+ assert_errors expression("def (\na\nb\n).c; end"), "def (\na\nb\n).c; end", [
+ ["expected a matching `)`", 8..8],
+ ["expected a `.` or `::` after the receiver in a method definition", 8..8],
+ ["expected a delimiter to close the parameters", 9..9],
+ ["unexpected ')', ignoring it", 10..11],
+ ["unexpected '.', ignoring it", 11..12]
+ ]
+ end
+
+ def test_def_with_empty_expression_receiver
+ assert_errors expression("def ().a; end"), "def ().a; end", [
+ ["expected a receiver for the method definition", 4..5]
+ ]
+ end
+
+ def test_block_beginning_with_brace_and_ending_with_end
+ assert_error_messages "x.each { x end", [
+ "unexpected 'end', expecting end-of-input",
+ "unexpected 'end', ignoring it",
+ "unexpected end of file, assuming it is closing the parent top level context",
+ "expected a block beginning with `{` to end with `}`"
+ ]
+ end
+
+ def test_double_splat_followed_by_splat_argument
+ expected = CallNode(
+ CallNodeFlags::IGNORE_VISIBILITY,
+ nil,
+ nil,
+ :a,
+ Location(),
+ Location(),
+ ArgumentsNode(1, [
+ KeywordHashNode(0, [AssocSplatNode(expression("kwargs"), Location())]),
+ SplatNode(Location(), expression("args"))
+ ]),
+ Location(),
+ nil
+ )
+
+ assert_errors expected, "a(**kwargs, *args)", [
+ ["unexpected `*` splat argument after a `**` keyword splat argument", 12..17]
+ ]
+ end
+
+ def test_arguments_after_block
+ expected = CallNode(
+ CallNodeFlags::IGNORE_VISIBILITY,
+ nil,
+ nil,
+ :a,
+ Location(),
+ Location(),
+ ArgumentsNode(0, [expression("foo")]),
+ Location(),
+ BlockArgumentNode(expression("block"), Location())
+ )
+
+ assert_errors expected, "a(&block, foo)", [
+ ["unexpected argument after a block argument", 10..13]
+ ]
+ end
+
+ def test_arguments_binding_power_for_and
+ assert_error_messages "foo(*bar and baz)", [
+ "unexpected 'and'; expected a `)` to close the arguments",
+ "unexpected ')', expecting end-of-input",
+ "unexpected ')', ignoring it"
+ ]
+ end
+
+ def test_splat_argument_after_keyword_argument
+ expected = CallNode(
+ CallNodeFlags::IGNORE_VISIBILITY,
+ nil,
+ nil,
+ :a,
+ Location(),
+ Location(),
+ ArgumentsNode(0, [
+ KeywordHashNode(1, [
+ AssocNode(
+ SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, nil, Location(), Location(), "foo"),
+ expression("bar"),
+ nil
+ )
+ ]),
+ SplatNode(Location(), expression("args"))
+ ]),
+ Location(),
+ nil
+ )
+
+ assert_errors expected, "a(foo: bar, *args)", [
+ ["unexpected `*` splat argument after a `**` keyword splat argument", 12..17]
+ ]
+ end
+
+ def test_module_definition_in_method_body
+ expected = DefNode(
+ :foo,
+ Location(),
+ nil,
+ nil,
+ StatementsNode([ModuleNode([], Location(), ConstantReadNode(:A), nil, Location(), :A)]),
+ [],
+ Location(),
+ nil,
+ nil,
+ nil,
+ nil,
+ Location()
+ )
+
+ assert_errors expected, "def foo;module A;end;end", [
+ ["unexpected module definition in method body", 8..14]
+ ]
+ end
+
+ def test_module_definition_in_method_body_within_block
+ expected = DefNode(
+ :foo,
+ Location(),
+ nil,
+ nil,
+ StatementsNode(
+ [CallNode(
+ CallNodeFlags::IGNORE_VISIBILITY,
+ nil,
+ nil,
+ :bar,
+ Location(),
+ nil,
+ nil,
+ nil,
+ BlockNode(
+ [],
+ nil,
+ StatementsNode([ModuleNode([], Location(), ConstantReadNode(:Foo), nil, Location(), :Foo)]),
+ Location(),
+ Location()
+ )
+ )]
+ ),
+ [],
+ Location(),
+ nil,
+ nil,
+ nil,
+ nil,
+ Location()
+ )
+
+ assert_errors expected, <<~RUBY, [["unexpected module definition in method body", 21..27]]
+ def foo
+ bar do
+ module Foo;end
+ end
+ end
+ RUBY
+ end
+
+ def test_module_definition_in_method_defs
+ source = <<~RUBY
+ def foo(bar = module A;end);end
+ def foo;rescue;module A;end;end
+ def foo;ensure;module A;end;end
+ RUBY
+ message = "unexpected module definition in method body"
+ assert_errors expression(source), source, [
+ [message, 14..20],
+ [message, 47..53],
+ [message, 79..85],
+ ]
+ end
+
+ def test_class_definition_in_method_body
+ expected = DefNode(
+ :foo,
+ Location(),
+ nil,
+ nil,
+ StatementsNode(
+ [ClassNode(
+ [],
+ Location(),
+ ConstantReadNode(:A),
+ nil,
+ nil,
+ nil,
+ Location(),
+ :A
+ )]
+ ),
+ [],
+ Location(),
+ nil,
+ nil,
+ nil,
+ nil,
+ Location()
+ )
+
+ assert_errors expected, "def foo;class A;end;end", [
+ ["unexpected class definition in method body", 8..13]
+ ]
+ end
+
+ def test_class_definition_in_method_defs
+ source = <<~RUBY
+ def foo(bar = class A;end);end
+ def foo;rescue;class A;end;end
+ def foo;ensure;class A;end;end
+ RUBY
+ message = "unexpected class definition in method body"
+ assert_errors expression(source), source, [
+ [message, 14..19],
+ [message, 46..51],
+ [message, 77..82],
+ ]
+ end
+
+ def test_bad_arguments
+ expected = DefNode(
+ :foo,
+ Location(),
+ nil,
+ ParametersNode([
+ RequiredParameterNode(0, :A),
+ RequiredParameterNode(0, :@a),
+ RequiredParameterNode(0, :$A),
+ RequiredParameterNode(0, :@@a),
+ ], [], nil, [], [], nil, nil),
+ nil,
+ [:A, :@a, :$A, :@@a],
+ Location(),
+ nil,
+ Location(),
+ Location(),
+ nil,
+ Location()
+ )
+
+ assert_errors expected, "def foo(A, @a, $A, @@a);end", [
+ ["invalid formal argument; formal argument cannot be a constant", 8..9],
+ ["invalid formal argument; formal argument cannot be an instance variable", 11..13],
+ ["invalid formal argument; formal argument cannot be a global variable", 15..17],
+ ["invalid formal argument; formal argument cannot be a class variable", 19..22],
+ ]
+ end
+
+ if RUBY_VERSION >= "3.0"
+ def test_cannot_assign_to_a_reserved_numbered_parameter
+ expected = BeginNode(
+ Location(),
+ StatementsNode([
+ LocalVariableWriteNode(:_1, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()),
+ LocalVariableWriteNode(:_2, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()),
+ LocalVariableWriteNode(:_3, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()),
+ LocalVariableWriteNode(:_4, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()),
+ LocalVariableWriteNode(:_5, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()),
+ LocalVariableWriteNode(:_6, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()),
+ LocalVariableWriteNode(:_7, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()),
+ LocalVariableWriteNode(:_8, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()),
+ LocalVariableWriteNode(:_9, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()),
+ LocalVariableWriteNode(:_10, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location())
+ ]),
+ nil,
+ nil,
+ nil,
+ Location()
+ )
+ source = <<~RUBY
+ begin
+ _1=:a;_2=:a;_3=:a;_4=:a;_5=:a
+ _6=:a;_7=:a;_8=:a;_9=:a;_10=:a
+ end
+ RUBY
+ assert_errors expected, source, [
+ ["_1 is reserved for numbered parameters", 8..10],
+ ["_2 is reserved for numbered parameters", 14..16],
+ ["_3 is reserved for numbered parameters", 20..22],
+ ["_4 is reserved for numbered parameters", 26..28],
+ ["_5 is reserved for numbered parameters", 32..34],
+ ["_6 is reserved for numbered parameters", 40..42],
+ ["_7 is reserved for numbered parameters", 46..48],
+ ["_8 is reserved for numbered parameters", 52..54],
+ ["_9 is reserved for numbered parameters", 58..60],
+ ]
+ end
+ end
+
+ def test_do_not_allow_trailing_commas_in_method_parameters
+ expected = DefNode(
+ :foo,
+ Location(),
+ nil,
+ ParametersNode(
+ [RequiredParameterNode(0, :a), RequiredParameterNode(0, :b), RequiredParameterNode(0, :c)],
+ [],
+ nil,
+ [],
+ [],
+ nil,
+ nil
+ ),
+ nil,
+ [:a, :b, :c],
+ Location(),
+ nil,
+ Location(),
+ Location(),
+ nil,
+ Location()
+ )
+
+ assert_errors expected, "def foo(a,b,c,);end", [
+ ["unexpected `,` in parameters", 13..14]
+ ]
+ end
+
+ def test_do_not_allow_trailing_commas_in_lambda_parameters
+ expected = LambdaNode(
+ [:a, :b],
+ Location(),
+ Location(),
+ Location(),
+ BlockParametersNode(
+ ParametersNode([RequiredParameterNode(0, :a), RequiredParameterNode(0, :b)], [], nil, [], [], nil, nil),
+ [],
+ Location(),
+ Location()
+ ),
+ nil
+ )
+ assert_errors expected, "-> (a, b, ) {}", [
+ ["unexpected `,` in parameters", 8..9]
+ ]
+ end
+
+ def test_do_not_allow_multiple_codepoints_in_a_single_character_literal
+ expected = StringNode(StringFlags::FORCED_UTF8_ENCODING, Location(), Location(), nil, "\u0001\u0002")
+
+ assert_errors expected, '?\u{0001 0002}', [
+ ["invalid Unicode escape sequence; multiple codepoints are not allowed in a character literal", 9..12]
+ ]
+ end
+
+ def test_invalid_hex_escape
+ assert_errors expression('"\\xx"'), '"\\xx"', [
+ ["invalid hexadecimal escape sequence", 1..3],
+ ]
+ end
+
+ def test_do_not_allow_more_than_6_hexadecimal_digits_in_u_Unicode_character_notation
+ expected = StringNode(0, Location(), Location(), Location(), "\u0001")
+
+ assert_errors expected, '"\u{0000001}"', [
+ ["invalid Unicode escape sequence; maximum length is 6 digits", 4..11],
+ ]
+ end
+
+ def test_do_not_allow_characters_other_than_0_9_a_f_and_A_F_in_u_Unicode_character_notation
+ expected = StringNode(0, Location(), Location(), Location(), "\u0000z}")
+
+ assert_errors expected, '"\u{000z}"', [
+ ["invalid Unicode escape sequence", 7..7],
+ ]
+ end
+
+ def test_unterminated_unicode_brackets_should_be_a_syntax_error
+ assert_errors expression('?\\u{3'), '?\\u{3', [
+ ["invalid Unicode escape sequence; needs closing `}`", 1..5],
+ ]
+ end
+
+ def test_method_parameters_after_block
+ expected = DefNode(
+ :foo,
+ Location(),
+ nil,
+ ParametersNode(
+ [],
+ [],
+ nil,
+ [RequiredParameterNode(0, :a)],
+ [],
+ nil,
+ BlockParameterNode(0, :block, Location(), Location())
+ ),
+ nil,
+ [:block, :a],
+ Location(),
+ nil,
+ Location(),
+ Location(),
+ nil,
+ Location()
+ )
+ assert_errors expected, "def foo(&block, a)\nend", [
+ ["unexpected parameter order", 16..17]
+ ]
+ end
+
+ def test_method_with_arguments_after_anonymous_block
+ expected = DefNode(
+ :foo,
+ Location(),
+ nil,
+ ParametersNode([], [], nil, [RequiredParameterNode(0, :a)], [], nil, BlockParameterNode(0, nil, nil, Location())),
+ nil,
+ [:a],
+ Location(),
+ nil,
+ Location(),
+ Location(),
+ nil,
+ Location()
+ )
+
+ assert_errors expected, "def foo(&, a)\nend", [
+ ["unexpected parameter order", 11..12]
+ ]
+ end
+
+ def test_method_parameters_after_arguments_forwarding
+ expected = DefNode(
+ :foo,
+ Location(),
+ nil,
+ ParametersNode(
+ [],
+ [],
+ nil,
+ [RequiredParameterNode(0, :a)],
+ [],
+ ForwardingParameterNode(),
+ nil
+ ),
+ nil,
+ [:a],
+ Location(),
+ nil,
+ Location(),
+ Location(),
+ nil,
+ Location()
+ )
+ assert_errors expected, "def foo(..., a)\nend", [
+ ["unexpected parameter order", 13..14]
+ ]
+ end
+
+ def test_keywords_parameters_before_required_parameters
+ expected = DefNode(
+ :foo,
+ Location(),
+ nil,
+ ParametersNode(
+ [],
+ [],
+ nil,
+ [RequiredParameterNode(0, :a)],
+ [RequiredKeywordParameterNode(0, :b, Location())],
+ nil,
+ nil
+ ),
+ nil,
+ [:b, :a],
+ Location(),
+ nil,
+ Location(),
+ Location(),
+ nil,
+ Location()
+ )
+ assert_errors expected, "def foo(b:, a)\nend", [
+ ["unexpected parameter order", 12..13]
+ ]
+ end
+
+ def test_rest_keywords_parameters_before_required_parameters
+ expected = DefNode(
+ :foo,
+ Location(),
+ nil,
+ ParametersNode(
+ [],
+ [],
+ nil,
+ [],
+ [RequiredKeywordParameterNode(0, :b, Location())],
+ KeywordRestParameterNode(0, :rest, Location(), Location()),
+ nil
+ ),
+ nil,
+ [:rest, :b],
+ Location(),
+ nil,
+ Location(),
+ Location(),
+ nil,
+ Location()
+ )
+
+ assert_errors expected, "def foo(**rest, b:)\nend", [
+ ["unexpected parameter order", 16..18]
+ ]
+ end
+
+ def test_double_arguments_forwarding
+ expected = DefNode(
+ :foo,
+ Location(),
+ nil,
+ ParametersNode([], [], nil, [], [], ForwardingParameterNode(), nil),
+ nil,
+ [],
+ Location(),
+ nil,
+ Location(),
+ Location(),
+ nil,
+ Location()
+ )
+
+ assert_errors expected, "def foo(..., ...)\nend", [
+ ["unexpected parameter order", 13..16]
+ ]
+ end
+
+ def test_multiple_error_in_parameters_order
+ expected = DefNode(
+ :foo,
+ Location(),
+ nil,
+ ParametersNode(
+ [],
+ [],
+ nil,
+ [RequiredParameterNode(0, :a)],
+ [RequiredKeywordParameterNode(0, :b, Location())],
+ KeywordRestParameterNode(0, :args, Location(), Location()),
+ nil
+ ),
+ nil,
+ [:args, :a, :b],
+ Location(),
+ nil,
+ Location(),
+ Location(),
+ nil,
+ Location()
+ )
+
+ assert_errors expected, "def foo(**args, a, b:)\nend", [
+ ["unexpected parameter order", 16..17],
+ ["unexpected parameter order", 19..21]
+ ]
+ end
+
+ def test_switching_to_optional_arguments_twice
+ expected = DefNode(
+ :foo,
+ Location(),
+ nil,
+ ParametersNode(
+ [],
+ [],
+ nil,
+ [RequiredParameterNode(0, :a)],
+ [RequiredKeywordParameterNode(0, :b, Location())],
+ KeywordRestParameterNode(0, :args, Location(), Location()),
+ nil
+ ),
+ nil,
+ [:args, :a, :b],
+ Location(),
+ nil,
+ Location(),
+ Location(),
+ nil,
+ Location(),
+ )
+
+ assert_errors expected, "def foo(**args, a, b:)\nend", [
+ ["unexpected parameter order", 16..17],
+ ["unexpected parameter order", 19..21]
+ ]
+ end
+
+ def test_switching_to_named_arguments_twice
+ expected = DefNode(
+ :foo,
+ Location(),
+ nil,
+ ParametersNode(
+ [],
+ [],
+ nil,
+ [RequiredParameterNode(0, :a)],
+ [RequiredKeywordParameterNode(0, :b, Location())],
+ KeywordRestParameterNode(0, :args, Location(), Location()),
+ nil
+ ),
+ nil,
+ [:args, :a, :b],
+ Location(),
+ nil,
+ Location(),
+ Location(),
+ nil,
+ Location(),
+ )
+
+ assert_errors expected, "def foo(**args, a, b:)\nend", [
+ ["unexpected parameter order", 16..17],
+ ["unexpected parameter order", 19..21]
+ ]
+ end
+
+ def test_returning_to_optional_parameters_multiple_times
+ expected = DefNode(
+ :foo,
+ Location(),
+ nil,
+ ParametersNode(
+ [RequiredParameterNode(0, :a)],
+ [
+ OptionalParameterNode(0, :b, Location(), Location(), IntegerNode(IntegerBaseFlags::DECIMAL, 1)),
+ OptionalParameterNode(0, :d, Location(), Location(), IntegerNode(IntegerBaseFlags::DECIMAL, 2))
+ ],
+ nil,
+ [RequiredParameterNode(0, :c), RequiredParameterNode(0, :e)],
+ [],
+ nil,
+ nil
+ ),
+ nil,
+ [:a, :b, :c, :d, :e],
+ Location(),
+ nil,
+ Location(),
+ Location(),
+ nil,
+ Location(),
+ )
+
+ assert_errors expected, "def foo(a, b = 1, c, d = 2, e)\nend", [
+ ["unexpected parameter order", 23..24]
+ ]
+ end
+
+ def test_case_without_when_clauses_errors_on_else_clause
+ expected = CaseMatchNode(
+ SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"),
+ [],
+ ElseNode(Location(), nil, Location()),
+ Location(),
+ Location()
+ )
+
+ assert_errors expected, "case :a\nelse\nend", [
+ ["expected a `when` or `in` clause after `case`", 0..4]
+ ]
+ end
+
+ def test_case_without_clauses
+ expected = CaseNode(
+ SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"),
+ [],
+ nil,
+ Location(),
+ Location()
+ )
+
+ assert_errors expected, "case :a\nend", [
+ ["expected a `when` or `in` clause after `case`", 0..4]
+ ]
+ end
+
+ def test_setter_method_cannot_be_defined_in_an_endless_method_definition
+ expected = DefNode(
+ :a=,
+ Location(),
+ nil,
+ nil,
+ StatementsNode([IntegerNode(IntegerBaseFlags::DECIMAL, 42)]),
+ [],
+ Location(),
+ nil,
+ Location(),
+ Location(),
+ Location(),
+ nil
+ )
+
+ assert_errors expected, "def a=() = 42", [
+ ["invalid method name; a setter method cannot be defined in an endless method definition", 4..6]
+ ]
+ end
+
+ def test_do_not_allow_forward_arguments_in_lambda_literals
+ expected = LambdaNode(
+ [],
+ Location(),
+ Location(),
+ Location(),
+ BlockParametersNode(ParametersNode([], [], nil, [], [], ForwardingParameterNode(), nil), [], Location(), Location()),
+ nil
+ )
+
+ assert_errors expected, "->(...) {}", [
+ ["unexpected ... when the parent method is not forwarding", 3..6]
+ ]
+ end
+
+ def test_do_not_allow_forward_arguments_in_blocks
+ expected = CallNode(
+ CallNodeFlags::IGNORE_VISIBILITY,
+ nil,
+ nil,
+ :a,
+ Location(),
+ nil,
+ nil,
+ nil,
+ BlockNode(
+ [],
+ BlockParametersNode(ParametersNode([], [], nil, [], [], ForwardingParameterNode(), nil), [], Location(), Location()),
+ nil,
+ Location(),
+ Location()
+ )
+ )
+
+ assert_errors expected, "a {|...|}", [
+ ["unexpected ... when the parent method is not forwarding", 4..7]
+ ]
+ end
+
+ def test_dont_allow_return_inside_class_body
+ expected = ClassNode(
+ [],
+ Location(),
+ ConstantReadNode(:A),
+ nil,
+ nil,
+ StatementsNode([ReturnNode(0, Location(), nil)]),
+ Location(),
+ :A
+ )
+
+ assert_errors expected, "class A; return; end", [
+ ["Invalid return in class/module body", 9..15]
+ ]
+ end
+
+ def test_dont_allow_return_inside_module_body
+ expected = ModuleNode(
+ [],
+ Location(),
+ ConstantReadNode(:A),
+ StatementsNode([ReturnNode(0, Location(), nil)]),
+ Location(),
+ :A
+ )
+
+ assert_errors expected, "module A; return; end", [
+ ["Invalid return in class/module body", 10..16]
+ ]
+ end
+
+ def test_dont_allow_setting_to_back_and_nth_reference
+ expected = BeginNode(
+ Location(),
+ StatementsNode([
+ GlobalVariableWriteNode(:$+, Location(), NilNode(), Location()),
+ GlobalVariableWriteNode(:$1466, Location(), NilNode(), Location())
+ ]),
+ nil,
+ nil,
+ nil,
+ Location()
+ )
+
+ assert_errors expected, "begin\n$+ = nil\n$1466 = nil\nend", [
+ ["Can't set variable $+", 6..8],
+ ["Can't set variable $1466", 15..20]
+ ]
+ end
+
+ def test_duplicated_parameter_names
+ expected = DefNode(
+ :foo,
+ Location(),
+ nil,
+ ParametersNode([RequiredParameterNode(0, :a), RequiredParameterNode(0, :b), RequiredParameterNode(ParameterFlags::REPEATED_PARAMETER, :a)], [], nil, [], [], nil, nil),
+ nil,
+ [:a, :b],
+ Location(),
+ nil,
+ Location(),
+ Location(),
+ nil,
+ Location()
+ )
+
+ assert_errors expected, "def foo(a,b,a);end", [
+ ["duplicated argument name", 12..13]
+ ]
+
+ expected = DefNode(
+ :foo,
+ Location(),
+ nil,
+ ParametersNode([RequiredParameterNode(0, :a), RequiredParameterNode(0, :b)], [], RestParameterNode(ParameterFlags::REPEATED_PARAMETER, :a, Location(), Location()), [], [], nil, nil),
+ nil,
+ [:a, :b],
+ Location(),
+ nil,
+ Location(),
+ Location(),
+ nil,
+ Location()
+ )
+
+ assert_errors expected, "def foo(a,b,*a);end", [
+ ["duplicated argument name", 13..14]
+ ]
+
+ expected = DefNode(
+ :foo,
+ Location(),
+ nil,
+ ParametersNode([RequiredParameterNode(0, :a), RequiredParameterNode(0, :b)], [], nil, [], [], KeywordRestParameterNode(ParameterFlags::REPEATED_PARAMETER, :a, Location(), Location()), nil),
+ nil,
+ [:a, :b],
+ Location(),
+ nil,
+ Location(),
+ Location(),
+ nil,
+ Location()
+ )
+
+ assert_errors expected, "def foo(a,b,**a);end", [
+ ["duplicated argument name", 14..15]
+ ]
+
+ expected = DefNode(
+ :foo,
+ Location(),
+ nil,
+ ParametersNode([RequiredParameterNode(0, :a), RequiredParameterNode(0, :b)], [], nil, [], [], nil, BlockParameterNode(ParameterFlags::REPEATED_PARAMETER, :a, Location(), Location())),
+ nil,
+ [:a, :b],
+ Location(),
+ nil,
+ Location(),
+ Location(),
+ nil,
+ Location()
+ )
+
+ assert_errors expected, "def foo(a,b,&a);end", [
+ ["duplicated argument name", 13..14]
+ ]
+
+ expected = DefNode(
+ :foo,
+ Location(),
+ nil,
+ ParametersNode([], [OptionalParameterNode(0, :a, Location(), Location(), IntegerNode(IntegerBaseFlags::DECIMAL, 1))], RestParameterNode(0, :c, Location(), Location()), [RequiredParameterNode(0, :b)], [], nil, nil),
+ nil,
+ [:a, :b, :c],
+ Location(),
+ nil,
+ Location(),
+ Location(),
+ nil,
+ Location()
+ )
+
+ assert_errors expected, "def foo(a = 1,b,*c);end", [["unexpected parameter `*`", 16..17]]
+ end
+
+ def test_content_after_unterminated_heredoc
+ receiver = StringNode(0, Location(), Location(), Location(), "")
+ expected = CallNode(0, receiver, Location(), :foo, Location(), nil, nil, nil, nil)
+
+ assert_errors expected, "<<~FOO.foo\n", [
+ ["could not find a terminator for the heredoc", 11..11]
+ ]
+ end
+
+ def test_invalid_message_name
+ result = Prism.parse("+.@foo,+=foo")
+ assert_equal :"", result.value.statements.body.first.write_name
+ end
+
+ def test_invalid_operator_write_fcall
+ source = "foo! += 1"
+ assert_errors expression(source), source, [
+ ["unexpected write target", 0..4]
+ ]
+ end
+
+ def test_invalid_operator_write_dot
+ source = "foo.+= 1"
+ assert_errors expression(source), source, [
+ ["unexpected write target", 5..6]
+ ]
+ end
+
+ def test_unterminated_global_variable
+ message = "'$' without identifiers is not allowed as a global variable name"
+
+ assert_errors expression("$"), "$", [[message, 0..1]]
+ assert_errors expression("$ "), "$ ", [[message, 0..1]]
+ end
+
+ def test_invalid_global_variable_write
+ assert_errors expression("$',"), "$',", [
+ ["Can't set variable $'", 0..2],
+ ["unexpected write target", 0..2]
+ ]
+ end
+
+ def test_invalid_multi_target
+ error_messages = ["unexpected write target"]
+
+ assert_error_messages "foo,", error_messages
+ assert_error_messages "foo = 1; foo,", error_messages
+ assert_error_messages "foo.bar,", error_messages
+ assert_error_messages "*foo,", error_messages
+ assert_error_messages "@foo,", error_messages
+ assert_error_messages "@@foo,", error_messages
+ assert_error_messages "$foo,", error_messages
+ assert_error_messages "$1,", ["Can't set variable $1", *error_messages]
+ assert_error_messages "$+,", ["Can't set variable $+", *error_messages]
+ assert_error_messages "Foo,", error_messages
+ assert_error_messages "::Foo,", error_messages
+ assert_error_messages "Foo::Foo,", error_messages
+ assert_error_messages "Foo::foo,", error_messages
+ assert_error_messages "foo[foo],", error_messages
+ assert_error_messages "(foo, bar)", error_messages
+ assert_error_messages "foo((foo, bar))", error_messages
+ assert_error_messages "foo((*))", error_messages
+ assert_error_messages "foo(((foo, bar), *))", error_messages
+ assert_error_messages "(foo, bar) + 1", error_messages
+ assert_error_messages "(foo, bar) in baz", error_messages
+ end
+
+ def test_call_with_block_and_write
+ source = "foo {} &&= 1"
+ assert_errors expression(source), source, [
+ ["unexpected write target", 0..6],
+ ["unexpected operator after a call with a block", 7..10]
+ ]
+ end
+
+ def test_call_with_block_or_write
+ source = "foo {} ||= 1"
+ assert_errors expression(source), source, [
+ ["unexpected write target", 0..6],
+ ["unexpected operator after a call with a block", 7..10]
+ ]
+ end
+
+ def test_call_with_block_operator_write
+ source = "foo {} += 1"
+ assert_errors expression(source), source, [
+ ["unexpected write target", 0..6],
+ ["unexpected operator after a call with a block", 7..9]
+ ]
+ end
+
+ def test_index_call_with_block_and_write
+ source = "foo[1] {} &&= 1"
+ assert_errors expression(source), source, [
+ ["unexpected write target", 0..9],
+ ["unexpected operator after a call with arguments", 10..13],
+ ["unexpected operator after a call with a block", 10..13]
+ ]
+ end
+
+ def test_index_call_with_block_or_write
+ source = "foo[1] {} ||= 1"
+ assert_errors expression(source), source, [
+ ["unexpected write target", 0..9],
+ ["unexpected operator after a call with arguments", 10..13],
+ ["unexpected operator after a call with a block", 10..13]
+ ]
+ end
+
+ def test_index_call_with_block_operator_write
+ source = "foo[1] {} += 1"
+ assert_errors expression(source), source, [
+ ["unexpected write target", 0..9],
+ ["unexpected operator after a call with arguments", 10..12],
+ ["unexpected operator after a call with a block", 10..12]
+ ]
+ end
+
+ if RUBY_VERSION >= "3.0"
+ def test_writing_numbered_parameter
+ assert_errors expression("-> { _1 = 0 }"), "-> { _1 = 0 }", [
+ ["_1 is reserved for numbered parameters", 5..7]
+ ]
+ end
+
+ def test_targeting_numbered_parameter
+ assert_errors expression("-> { _1, = 0 }"), "-> { _1, = 0 }", [
+ ["_1 is reserved for numbered parameters", 5..7]
+ ]
+ end
+
+ def test_defining_numbered_parameter
+ error_messages = ["_1 is reserved for numbered parameters"]
+
+ assert_error_messages "def _1; end", error_messages
+ assert_error_messages "def self._1; end", error_messages
+ end
+ end
+
+ def test_double_scope_numbered_parameters
+ source = "-> { _1 + -> { _2 } }"
+ errors = [["numbered parameter is already used in outer scope", 15..17]]
+
+ assert_errors expression(source), source, errors
+ end
+
+ def test_invalid_number_underscores
+ error_messages = ["invalid underscore placement in number"]
+
+ assert_error_messages "1__1", error_messages
+ assert_error_messages "0b1__1", error_messages
+ assert_error_messages "0o1__1", error_messages
+ assert_error_messages "01__1", error_messages
+ assert_error_messages "0d1__1", error_messages
+ assert_error_messages "0x1__1", error_messages
+
+ assert_error_messages "1_1_", error_messages
+ assert_error_messages "0b1_1_", error_messages
+ assert_error_messages "0o1_1_", error_messages
+ assert_error_messages "01_1_", error_messages
+ assert_error_messages "0d1_1_", error_messages
+ assert_error_messages "0x1_1_", error_messages
+ end
+
+ def test_alnum_delimiters
+ error_messages = ["invalid `%` token"]
+
+ assert_error_messages "%qXfooX", error_messages
+ assert_error_messages "%QXfooX", error_messages
+ assert_error_messages "%wXfooX", error_messages
+ assert_error_messages "%WxfooX", error_messages
+ assert_error_messages "%iXfooX", error_messages
+ assert_error_messages "%IXfooX", error_messages
+ assert_error_messages "%xXfooX", error_messages
+ assert_error_messages "%rXfooX", error_messages
+ assert_error_messages "%sXfooX", error_messages
+ end
+
+ def test_begin_at_toplevel
+ source = "def foo; BEGIN {}; end"
+ assert_errors expression(source), source, [
+ ["BEGIN is permitted only at toplevel", 9..14],
+ ]
+ end
+
+ if RUBY_VERSION >= "3.0"
+ def test_numbered_parameters_in_block_arguments
+ source = "foo { |_1| }"
+ assert_errors expression(source), source, [
+ ["_1 is reserved for numbered parameters", 7..9],
+ ]
+ end
+ end
+
+ def test_conditional_predicate_closed
+ source = "if 0 0; elsif 0 0; end\nunless 0 0; end"
+ assert_errors expression(source), source, [
+ ["expected `then` or `;` or '\\n" + "'", 5..6],
+ ["expected `then` or `;` or '\\n" + "'", 16..17],
+ ["expected `then` or `;` or '\\n" + "'", 32..33],
+ ]
+ end
+
+ def test_parameter_name_ending_with_bang_or_question_mark
+ source = "def foo(x!,y?); end"
+ errors = [
+ ["unexpected name for a parameter", 8..10],
+ ["unexpected name for a parameter", 11..13]
+ ]
+ assert_errors expression(source), source, errors
+ end
+
+ def test_class_name
+ source = "class 0.X end"
+ assert_errors expression(source), source, [
+ ["unexpected constant path after `class`; class/module name must be CONSTANT", 6..9],
+ ]
+ end
+
+ def test_loop_conditional_is_closed
+ source = "while 0 0; foo; end; until 0 0; foo; end"
+ assert_errors expression(source), source, [
+ ["expected a predicate expression for the `while` statement", 7..7],
+ ["expected a predicate expression for the `until` statement", 28..28],
+ ]
+ end
+
+ def test_forwarding_arg_after_keyword_rest
+ source = "def f(**,...);end"
+ assert_errors expression(source), source, [
+ ["unexpected `...` in parameters", 9..12],
+ ]
+ end
+
+ def test_semicolon_after_inheritance_operator
+ source = "class Foo < Bar end"
+ assert_errors expression(source), source, [
+ ["unexpected `end`, expecting ';' or '\\n'", 15..15],
+ ]
+ end
+
+ def test_shadow_args_in_lambda
+ source = "->a;b{}"
+
+ assert_errors expression(source), source, [
+ ["expected a `do` keyword or a `{` to open the lambda block", 3..3],
+ ["unexpected end of file, expecting end-of-input", 7..7],
+ ["unexpected end of file, assuming it is closing the parent top level context", 7..7],
+ ["expected a lambda block beginning with `do` to end with `end`", 7..7]
+ ]
+ end
+
+ def test_shadow_args_in_block
+ source = "tap{|a;a|}"
+ assert_errors expression(source), source, [
+ ["duplicated argument name", 7..8],
+ ]
+ end
+
+ def test_repeated_parameter_name_in_destructured_params
+ source = "def f(a, (b, (a))); end"
+
+ assert_errors expression(source), source, [
+ ["duplicated argument name", 14..15],
+ ]
+ end
+
+ def test_assign_to_numbered_parameter
+ source = <<~RUBY
+ a in _1
+ a => _1
+ 1 => a, _1
+ 1 in a, _1
+ /(?<_1>)/ =~ a
+ RUBY
+
+ message = "_1 is reserved for numbered parameters"
+ assert_errors expression(source), source, [
+ [message, 5..7],
+ [message, 13..15],
+ [message, 24..26],
+ [message, 35..37],
+ [message, 42..44]
+ ]
+ end
+
+ def test_symbol_in_keyword_parameter
+ source = "def foo(x:'y':); end"
+ assert_errors expression(source), source, [
+ ["unexpected label terminator, expected a string literal terminator", 12..14]
+ ]
+ end
+
+ def test_symbol_in_hash
+ source = "{x:'y':}"
+ assert_errors expression(source), source, [
+ ["unexpected label terminator, expected a string literal terminator", 5..7]
+ ]
+ end
+
+ def test_while_endless_method
+ source = "while def f = g do end"
+
+ assert_errors expression(source), source, [
+ ["expected a predicate expression for the `while` statement", 22..22],
+ ["unexpected end of file, assuming it is closing the parent top level context", 22..22],
+ ["expected an `end` to close the `while` statement", 22..22]
+ ]
+ end
+
+ def test_match_plus
+ source = <<~RUBY
+ a in b + c
+ a => b + c
+ RUBY
+
+ assert_errors expression(source), source, [
+ ["unexpected '+', expecting end-of-input", 7..8],
+ ["unexpected '+', ignoring it", 7..8],
+ ["unexpected '+', expecting end-of-input", 18..19],
+ ["unexpected '+', ignoring it", 18..19]
+ ]
+ end
+
+ def test_rational_number_with_exponential_portion
+ source = '1e1r; 1e1ri'
+
+ assert_errors expression(source), source, [
+ ["unexpected local variable or method, expecting end-of-input", 3..4],
+ ["unexpected local variable or method, expecting end-of-input", 9..11]
+ ]
+ end
+
+ def test_check_value_expression
+ source = <<~RUBY
+ 1 => ^(return)
+ while true
+ 1 => ^(break)
+ 1 => ^(next)
+ 1 => ^(redo)
+ 1 => ^(retry)
+ 1 => ^(2 => a)
+ end
+ 1 => ^(if 1; (return) else (return) end)
+ 1 => ^(unless 1; (return) else (return) end)
+ RUBY
+
+ message = "unexpected void value expression"
+ assert_errors expression(source), source, [
+ [message, 7..13],
+ [message, 35..40],
+ [message, 51..55],
+ [message, 66..70],
+ ["Invalid retry without rescue", 81..86],
+ [message, 81..86],
+ [message, 97..103],
+ [message, 123..129],
+ [message, 168..174],
+ ]
+ end
+
+ def test_void_value_expression_in_statement
+ source = <<~RUBY
+ if (return)
+ end
+ unless (return)
+ end
+ while (return)
+ end
+ until (return)
+ end
+ case (return)
+ when 1
+ end
+ class A < (return)
+ end
+ class << (return)
+ end
+ for x in (return)
+ end
+ RUBY
+
+ message = 'unexpected void value expression'
+ assert_errors expression(source), source, [
+ [message, 4..10],
+ [message, 24..30],
+ [message, 43..49],
+ [message, 62..68],
+ [message, 80..86],
+ [message, 110..116],
+ [message, 132..138],
+ [message, 154..160],
+ ]
+ end
+
+ def test_void_value_expression_in_def
+ source = <<~RUBY
+ def (return).x
+ end
+ def x(a = return)
+ end
+ def x(a: return)
+ end
+ RUBY
+
+ message = 'unexpected void value expression'
+ assert_errors expression(source), source, [
+ [message, 5..11],
+ [message, 29..35],
+ [message, 50..56],
+ ]
+ end
+
+ def test_void_value_expression_in_assignment
+ source = <<~RUBY
+ a = return
+ a = 1, return
+ a, b = return, 1
+ a, b = 1, *return
+ RUBY
+
+ message = 'unexpected void value expression'
+ assert_errors expression(source), source, [
+ [message, 4..10],
+ [message, 18..24],
+ [message, 32..38],
+ [message, 53..59],
+ ]
+ end
+
+ def test_void_value_expression_in_modifier
+ source = <<~RUBY
+ 1 if (return)
+ 1 unless (return)
+ 1 while (return)
+ 1 until (return)
+ (return) => a
+ (return) in a
+ RUBY
+
+ message = 'unexpected void value expression'
+ assert_errors expression(source), source, [
+ [message, 6..12],
+ [message, 24..30],
+ [message, 41..47],
+ [message, 58..64],
+ [message, 67..73],
+ [message, 81..87]
+ ]
+ end
+
+ def test_void_value_expression_in_expression
+ source = <<~RUBY
+ (return) ? 1 : 1
+ (return)..1
+ 1..(return)
+ (return)...1
+ 1...(return)
+ (..(return))
+ (...(return))
+ ((return)..)
+ ((return)...)
+ RUBY
+
+ message = 'unexpected void value expression'
+ assert_errors expression(source), source, [
+ [message, 1..7],
+ [message, 18..24],
+ [message, 33..39],
+ [message, 42..48],
+ [message, 59..65],
+ [message, 71..77],
+ [message, 85..91],
+ [message, 96..102],
+ [message, 109..115]
+ ]
+ end
+
+ def test_void_value_expression_in_array
+ source = <<~RUBY
+ [return]
+ [1, return]
+ [ return => 1 ]
+ [ 1 => return ]
+ [ a: return ]
+ [ *return ]
+ [ **return ]
+ RUBY
+
+ message = 'unexpected void value expression'
+ assert_errors expression(source), source, [
+ [message, 1..7],
+ [message, 13..19],
+ [message, 23..29],
+ [message, 44..50],
+ [message, 58..64],
+ [message, 70..76],
+ [message, 83..89],
+ ]
+ end
+
+ def test_void_value_expression_in_hash
+ source = <<~RUBY
+ { return => 1 }
+ { 1 => return }
+ { a: return }
+ { **return }
+ RUBY
+
+ message = 'unexpected void value expression'
+ assert_errors expression(source), source, [
+ [message, 2..8],
+ [message, 23..29],
+ [message, 37..43],
+ [message, 50..56],
+ ]
+ end
+
+ def test_void_value_expression_in_call
+ source = <<~RUBY
+ (return).foo
+ (return).(1)
+ (return)[1]
+ (return)[1] = 2
+ (return)::foo
+ RUBY
+
+ message = 'unexpected void value expression'
+ assert_errors expression(source), source, [
+ [message, 1..7],
+ [message, 14..20],
+ [message, 27..33],
+ [message, 39..45],
+ [message, 55..61],
+ ]
+ end
+
+ def test_void_value_expression_in_constant_path
+ source = <<~RUBY
+ (return)::A
+ class (return)::A; end
+ RUBY
+
+ message = 'unexpected void value expression'
+ assert_errors expression(source), source, [
+ [message, 1..7],
+ [message, 19..25],
+ ]
+ end
+
+ def test_void_value_expression_in_arguments
+ source = <<~RUBY
+ foo(return)
+ foo(1, return)
+ foo(*return)
+ foo(**return)
+ foo(&return)
+ foo(return => 1)
+ foo(:a => return)
+ foo(a: return)
+ RUBY
+
+ message = 'unexpected void value expression'
+ assert_errors expression(source), source, [
+ [message, 4..10],
+ [message, 19..25],
+ [message, 32..38],
+ [message, 46..52],
+ [message, 59..65],
+ [message, 71..77],
+ [message, 94..100],
+ [message, 109..115],
+ ]
+ end
+
+ def test_void_value_expression_in_unary_call
+ source = <<~RUBY
+ +(return)
+ not return
+ RUBY
+
+ message = 'unexpected void value expression'
+ assert_errors expression(source), source, [
+ [message, 2..8],
+ [message, 14..20],
+ ]
+ end
+
+ def test_void_value_expression_in_binary_call
+ source = <<~RUBY
+ 1 + (return)
+ (return) + 1
+ 1 and (return)
+ (return) and 1
+ 1 or (return)
+ (return) or 1
+ RUBY
+
+ message = 'unexpected void value expression'
+ assert_errors expression(source), source, [
+ [message, 5..11],
+ [message, 14..20],
+ [message, 42..48],
+ [message, 71..77],
+ ]
+ end
+
+ def test_trailing_comma_in_calls
+ assert_errors expression("foo 1,"), "foo 1,", [
+ ["expected an argument", 5..6]
+ ]
+ end
+
+ def test_argument_after_ellipsis
+ source = 'def foo(...); foo(..., 1); end'
+ assert_errors expression(source), source, [
+ ['unexpected argument after `...`', 23..24]
+ ]
+ end
+
+ def test_ellipsis_in_no_paren_call
+ source = 'def foo(...); foo 1, ...; end'
+ assert_errors expression(source), source, [
+ ['unexpected `...` in an non-parenthesized call', 21..24]
+ ]
+ end
+
+ def test_non_assoc_range
+ source = '1....2'
+
+ assert_errors expression(source), source, [
+ ["unexpected '.', expecting end-of-input", 4..5],
+ ["unexpected '.', ignoring it", 4..5]
+ ]
+ end
+
+ def test_upcase_end_in_def
+ assert_warning_messages "def foo; END { }; end", [
+ "END in method; use at_exit"
+ ]
+ end
+
+ def test_warnings_verbosity
+ warning = Prism.parse("def foo; END { }; end").warnings[0]
+ assert_equal "END in method; use at_exit", warning.message
+ assert_equal :default, warning.level
+
+ warning = Prism.parse("foo /regexp/").warnings[0]
+ assert_equal "ambiguous `/`; wrap regexp in parentheses or add a space after `/` operator", warning.message
+ assert_equal :verbose, warning.level
+ end
+
+ def test_statement_operators
+ source = <<~RUBY
+ alias x y + 1
+ alias x y.z
+ BEGIN { bar } + 1
+ BEGIN { bar }.z
+ END { bar } + 1
+ END { bar }.z
+ undef x + 1
+ undef x.z
+ RUBY
+
+ assert_errors expression(source), source, [
+ ["unexpected '+', expecting end-of-input", 10..11],
+ ["unexpected '+', ignoring it", 10..11],
+ ["unexpected '.', expecting end-of-input", 23..24],
+ ["unexpected '.', ignoring it", 23..24],
+ ["unexpected '+', expecting end-of-input", 40..41],
+ ["unexpected '+', ignoring it", 40..41],
+ ["unexpected '.', expecting end-of-input", 57..58],
+ ["unexpected '.', ignoring it", 57..58],
+ ["unexpected '+', expecting end-of-input", 72..73],
+ ["unexpected '+', ignoring it", 72..73],
+ ["unexpected '.', expecting end-of-input", 87..88],
+ ["unexpected '.', ignoring it", 87..88],
+ ["unexpected '+', expecting end-of-input", 98..99],
+ ["unexpected '+', ignoring it", 98..99],
+ ["unexpected '.', expecting end-of-input", 109..110],
+ ["unexpected '.', ignoring it", 109..110]
+ ]
+ end
+
+ def test_statement_at_non_statement
+ source = <<~RUBY
+ foo(alias x y)
+ foo(BEGIN { bar })
+ foo(END { bar })
+ foo(undef x)
+ RUBY
+ assert_errors expression(source), source, [
+ ['unexpected an `alias` at a non-statement position', 4..9],
+ ['unexpected a `BEGIN` at a non-statement position', 19..24],
+ ['unexpected an `END` at a non-statement position', 38..41],
+ ['unexpected an `undef` at a non-statement position', 55..60],
+ ]
+ end
+
+ def test_binary_range_with_left_unary_range
+ source = <<~RUBY
+ ..1..
+ ...1..
+ RUBY
+
+ assert_errors expression(source), source, [
+ ["unexpected '..', expecting end-of-input", 3..5],
+ ["unexpected '..', ignoring it", 3..5],
+ ["unexpected '..', expecting end-of-input", 10..12],
+ ["unexpected '..', ignoring it", 10..12]
+ ]
+ end
+
+ def test_circular_param
+ source = <<~RUBY
+ def foo(bar = bar) = 42
+ def foo(bar: bar) = 42
+ proc { |foo = foo| }
+ proc { |foo: foo| }
+ RUBY
+
+ assert_errors expression(source), source, [
+ ["circular argument reference - bar", 8..11],
+ ["circular argument reference - bar", 32..35],
+ ["circular argument reference - foo", 55..58],
+ ["circular argument reference - foo", 76..79]
+ ]
+
+ refute_error_messages("def foo(bar: bar = 1); end")
+ end
+
+ def test_command_calls
+ sources = <<~RUBY.lines
+ [a b]
+ {a: b c}
+ ...a b
+ if ...a b; end
+ a b, c d
+ a(b, c d)
+ a(*b c)
+ a(**b c)
+ a(&b c)
+ +a b
+ a + b c
+ a && b c
+ a =~ b c
+ a = b, c d
+ a = *b c
+ a, b = c = d f
+ a ? b c : d e
+ defined? a b
+ ! ! a b
+ def f a = b c; end
+ def f(a = b c); end
+ a = b rescue c d
+ def a = b rescue c d
+ ->a=b c{}
+ ->(a=b c){}
+ case; when a b; end
+ case; in a if a b; end
+ case; in a unless a b; end
+ begin; rescue a b; end
+ begin; rescue a b => c; end
+ RUBY
+
+ sources.each do |source|
+ refute_valid_syntax(source)
+ assert_false(Prism.parse(source).success?)
+ end
+ end
+
+ def test_range_and_bin_op
+ sources = <<~RUBY.lines
+ 1..2..3
+ 1..2..
+ 1.. || 2
+ 1.. & 2
+ 1.. * 2
+ 1.. / 2
+ 1.. % 2
+ 1.. ** 2
+ RUBY
+
+ sources.each do |source|
+ refute_valid_syntax(source)
+ assert_false(Prism.parse(source).success?)
+ end
+ end
+
+ def test_command_call_in
+ source = <<~RUBY
+ foo 1 in a
+ a = foo 2 in b
+ RUBY
+
+ assert_errors expression(source), source, [
+ ["unexpected `in` keyword in arguments", 9..10],
+ ["unexpected local variable or method, expecting end-of-input", 9..10],
+ ["unexpected `in` keyword in arguments", 24..25],
+ ["unexpected local variable or method, expecting end-of-input", 24..25]
+ ]
+ end
+
+ def test_constant_assignment_in_method
+ source = 'def foo();A=1;end'
+ assert_errors expression(source), source, [
+ ['dynamic constant assignment', 10..13]
+ ]
+ end
+
+ def test_non_assoc_equality
+ source = <<~RUBY
+ 1 == 2 == 3
+ 1 != 2 != 3
+ 1 === 2 === 3
+ 1 =~ 2 =~ 3
+ 1 !~ 2 !~ 3
+ 1 <=> 2 <=> 3
+ RUBY
+
+ assert_errors expression(source), source, [
+ ["unexpected '==', expecting end-of-input", 7..9],
+ ["unexpected '==', ignoring it", 7..9],
+ ["unexpected '!=', expecting end-of-input", 19..21],
+ ["unexpected '!=', ignoring it", 19..21],
+ ["unexpected '===', expecting end-of-input", 32..35],
+ ["unexpected '===', ignoring it", 32..35],
+ ["unexpected '=~', expecting end-of-input", 45..47],
+ ["unexpected '=~', ignoring it", 45..47],
+ ["unexpected '!~', expecting end-of-input", 57..59],
+ ["unexpected '!~', ignoring it", 57..59],
+ ["unexpected '<=>', expecting end-of-input", 70..73],
+ ["unexpected '<=>', ignoring it", 70..73]
+ ]
+ end
+
+ def test_block_arg_and_block
+ source = 'foo(&1) { }'
+ assert_errors expression(source), source, [
+ ["both block arg and actual block given; only one block is allowed", 8..11]
+ ]
+ end
+
+ def test_forwarding_arg_and_block
+ source = 'def foo(...) = foo(...) { }'
+ assert_errors expression(source), source, [
+ ['both a block argument and a forwarding argument; only one block is allowed', 24..27]
+ ]
+ end
+
+ def test_it_with_ordinary_parameter
+ source = "proc { || it }"
+ errors = [["`it` is not allowed when an ordinary parameter is defined", 10..12]]
+
+ assert_errors expression(source), source, errors, check_valid_syntax: RUBY_VERSION >= "3.4.0"
+ end
+
+ def test_regular_expression_with_unknown_regexp_options
+ source = "/foo/AZaz"
+ errors = [["unknown regexp options: AZaz", 4..9]]
+
+ assert_errors expression(source), source, errors
+ end
+
+ def test_interpolated_regular_expression_with_unknown_regexp_options
+ source = "/\#{foo}/AZaz"
+ errors = [["unknown regexp options: AZaz", 7..12]]
+
+ assert_errors expression(source), source, errors
+ end
+
+ def test_singleton_method_for_literals
+ source = <<~'RUBY'
+ def (1).g; end
+ def ((a; 1)).foo; end
+ def ((return; 1)).bar; end
+ def (((1))).foo; end
+ def (__FILE__).foo; end
+ def (__ENCODING__).foo; end
+ def (__LINE__).foo; end
+ def ("foo").foo; end
+ def (3.14).foo; end
+ def (3.14i).foo; end
+ def (:foo).foo; end
+ def (:'foo').foo; end
+ def (:'f{o}').foo; end
+ def ('foo').foo; end
+ def ("foo").foo; end
+ def ("#{fo}o").foo; end
+ def (/foo/).foo; end
+ def (/f#{oo}/).foo; end
+ def ([1]).foo; end
+ RUBY
+ errors = [
+ ["cannot define singleton method for literals", 5..6],
+ ["cannot define singleton method for literals", 24..25],
+ ["cannot define singleton method for literals", 51..52],
+ ["cannot define singleton method for literals", 71..72],
+ ["cannot define singleton method for literals", 90..98],
+ ["cannot define singleton method for literals", 114..126],
+ ["cannot define singleton method for literals", 142..150],
+ ["cannot define singleton method for literals", 166..171],
+ ["cannot define singleton method for literals", 187..191],
+ ["cannot define singleton method for literals", 207..212],
+ ["cannot define singleton method for literals", 228..232],
+ ["cannot define singleton method for literals", 248..254],
+ ["cannot define singleton method for literals", 270..277],
+ ["cannot define singleton method for literals", 293..298],
+ ["cannot define singleton method for literals", 314..319],
+ ["cannot define singleton method for literals", 335..343],
+ ["cannot define singleton method for literals", 359..364],
+ ["cannot define singleton method for literals", 380..388],
+ ["cannot define singleton method for literals", 404..407]
+ ]
+ assert_errors expression(source), source, errors
+ end
+
+ def test_assignment_to_literal_in_conditionals
+ source = <<~RUBY
+ if (a = 2); end
+ if ($a = 2); end
+ if (@a = 2); end
+ if (@@a = 2); end
+ if a elsif b = 2; end
+ unless (a = 2); end
+ unless ($a = 2); end
+ unless (@a = 2); end
+ unless (@@a = 2); end
+ while (a = 2); end
+ while ($a = 2); end
+ while (@a = 2); end
+ while (@@a = 2); end
+ until (a = 2); end
+ until ($a = 2); end
+ until (@a = 2); end
+ until (@@a = 2); end
+ foo if a = 2
+ foo if (a, b = 2)
+ (@foo = 1) ? a : b
+ !(a = 2)
+ not a = 2
+ RUBY
+ assert_warning_messages source, [
+ "found '= literal' in conditional, should be =="
+ ] * source.lines.count
+ end
+
+ def test_duplicate_pattern_capture
+ source = <<~RUBY
+ case (); in [a, a]; end
+ case (); in [a, *a]; end
+ case (); in {a: a, b: a}; end
+ case (); in {a: a, **a}; end
+ case (); in [a, {a:}]; end
+ case (); in [a, {a: {a: {a: [a]}}}]; end
+ case (); in a => a; end
+ case (); in [A => a, {a: b => a}]; end
+ RUBY
+
+ assert_error_messages source, Array.new(source.lines.length, "duplicated variable name")
+ refute_error_messages "case (); in [_a, _a]; end"
+ end
+
+ def test_duplicate_pattern_hash_key
+ assert_error_messages "case (); in {a:, a:}; end", ["duplicated key name", "duplicated variable name"]
+ assert_error_messages "case (); in {a:1, a:2}; end", ["duplicated key name"]
+ refute_error_messages "case (); in [{a:1}, {a:2}]; end"
+ end
+
+ def test_unexpected_block
+ assert_error_messages "def foo = yield(&:+)", ["block argument should not be given"]
+ end
+
+ private
+
+ def assert_errors(expected, source, errors, check_valid_syntax: true)
+ refute_valid_syntax(source) if check_valid_syntax
+
+ result = Prism.parse(source)
+ node = result.value.statements.body.last
+
+ assert_equal_nodes(expected, node, compare_location: false)
+ assert_equal(errors, result.errors.map { |e| [e.message, e.location.start_offset..e.location.end_offset] })
+ end
+
+ def assert_error_messages(source, errors)
+ refute_valid_syntax(source)
+ result = Prism.parse(source)
+ assert_equal(errors, result.errors.map(&:message))
+ end
+
+ def refute_error_messages(source)
+ assert_valid_syntax(source)
+ assert Prism.parse_success?(source), "Expected #{source.inspect} to parse successfully"
+ end
+
+ def assert_warning_messages(source, warnings)
+ result = Prism.parse(source)
+ assert_equal(warnings, result.warnings.map(&:message))
+ end
+
+ def expression(source)
+ Prism.parse(source).value.statements.body.last
+ end
+ end
+end
diff --git a/test/prism/fixtures/alias.txt b/test/prism/fixtures/alias.txt
new file mode 100644
index 0000000000..376dacd7cc
--- /dev/null
+++ b/test/prism/fixtures/alias.txt
@@ -0,0 +1,23 @@
+alias :foo :bar
+
+alias %s[abc] %s[def]
+
+alias :'abc' :'def'
+
+alias :"abc#{1}" :'def'
+
+alias $a $'
+
+alias foo bar
+
+alias $foo $bar
+
+alias foo if
+
+alias foo <=>
+
+alias :== :eql?
+
+alias A B
+
+alias :A :B
diff --git a/test/prism/fixtures/arithmetic.txt b/test/prism/fixtures/arithmetic.txt
new file mode 100644
index 0000000000..b1e1267b95
--- /dev/null
+++ b/test/prism/fixtures/arithmetic.txt
@@ -0,0 +1,13 @@
+foo !bar
+
+-foo*bar
+
++foo**bar
+
+foo ~bar
+
+foo << bar << baz
+
+-1**2
+
+-1.zero?
diff --git a/test/prism/fixtures/arrays.txt b/test/prism/fixtures/arrays.txt
new file mode 100644
index 0000000000..2897189518
--- /dev/null
+++ b/test/prism/fixtures/arrays.txt
@@ -0,0 +1,122 @@
+[*a]
+
+foo[bar, baz] = 1, 2, 3
+
+[a: [:b, :c]]
+
+
+
+[:a, :b,
+:c,1,
+
+
+
+:d,
+]
+
+
+[:a, :b,
+:c,1,
+
+
+
+:d
+
+
+]
+
+[foo => bar]
+
+foo[bar][baz] = qux
+
+foo[bar][baz]
+
+[
+]
+
+foo[bar, baz]
+
+foo[bar, baz] = qux
+
+foo[0], bar[0] = 1, 2
+
+foo[bar[baz] = qux]
+
+foo[bar]
+
+foo[bar] = baz
+
+[**{}]
+
+[**kw]
+
+[1, **kw]
+
+[1, **kw, **{}, **kw]
+
+[
+ foo => bar,
+]
+
+
+%i#one two three#
+
+%w#one two three#
+
+%x#one two three#
+
+
+%i@one two three@
+
+%w@one two three@
+
+%x@one two three@
+
+
+%i{one two three}
+
+%w{one two three}
+
+%x{one two three}
+
+%w[\C:]
+
+foo[] += 1
+
+foo[] ||= 1
+
+foo[] &&= 1
+
+foo.foo[] += 1
+
+foo.foo[] ||= 1
+
+foo.foo[] &&= 1
+
+foo[bar] += 1
+
+foo[bar] ||= 1
+
+foo[bar] &&= 1
+
+foo.foo[bar] += 1
+
+foo.foo[bar] ||= 1
+
+foo.foo[bar] &&= 1
+
+def f(*); a[*]; end
+
+def f(*); a[1, *]; end
+
+def f(*); a[*] = 1; end
+
+def f(*); a[1, *] = 1; end
+
+def f(*); a[*] += 1; end
+
+def f(*); a[1, *] &&= 1; end
+
+def f(*); rescue => a[*]; end
+
+def f(*); rescue => a[1, *]; end
diff --git a/test/prism/fixtures/begin_ensure.txt b/test/prism/fixtures/begin_ensure.txt
new file mode 100644
index 0000000000..6cfb627453
--- /dev/null
+++ b/test/prism/fixtures/begin_ensure.txt
@@ -0,0 +1,21 @@
+begin
+a
+ensure
+b
+end
+
+begin; a; ensure; b; end
+
+begin a
+ ensure b
+ end
+
+begin a; ensure b; end
+
+begin begin:s.l begin ensure Module.new do
+ begin
+ break
+ ensure Module.new do
+ end
+ end
+end end end end
diff --git a/test/prism/fixtures/begin_rescue.txt b/test/prism/fixtures/begin_rescue.txt
new file mode 100644
index 0000000000..0a56fbef9f
--- /dev/null
+++ b/test/prism/fixtures/begin_rescue.txt
@@ -0,0 +1,79 @@
+begin; a; rescue; b; else; c; end
+
+begin; a; rescue; b; else; c; ensure; d; end
+
+begin
+a
+end
+
+begin; a; end
+
+begin a
+ end
+
+begin a; end
+
+begin
+a
+rescue
+b
+rescue
+c
+rescue
+d
+end
+
+begin
+ a
+rescue Exception => ex
+ b
+rescue AnotherException, OneMoreException => ex
+ c
+end
+
+begin
+ a
+rescue Exception => ex
+ b
+ensure
+ b
+end
+
+%!abc!
+
+begin
+a
+rescue
+b
+end
+
+begin;a;rescue;b;end
+
+begin
+a;rescue
+b;end
+
+begin
+a
+rescue Exception
+b
+end
+
+begin
+a
+rescue Exception, CustomException
+b
+end
+
+begin
+ a
+rescue Exception, CustomException => ex
+ b
+end
+
+begin
+ a
+rescue Exception => ex
+ b
+end
+
diff --git a/test/prism/fixtures/blocks.txt b/test/prism/fixtures/blocks.txt
new file mode 100644
index 0000000000..e33d95c150
--- /dev/null
+++ b/test/prism/fixtures/blocks.txt
@@ -0,0 +1,54 @@
+foo[bar] { baz }
+
+foo[bar] do
+baz
+end
+
+x.reduce(0) { |x, memo| memo += x }
+
+foo do end
+
+foo bar, (baz do end)
+
+foo bar do end
+
+foo bar baz do end
+
+foo do |a = b[1]|
+end
+
+foo do
+rescue
+end
+
+foo do
+ bar do
+ baz do
+ end
+ end
+end
+
+foo[bar] { baz }
+
+foo { |x, y = 2, z:| x }
+
+foo { |x| }
+
+fork = 1
+fork do |a|
+end
+
+fork { |a| }
+
+C do
+end
+
+C {}
+
+foo lambda { |
+ a: 1,
+ b: 2
+ |
+}
+
+foo do |bar,| end
diff --git a/test/prism/fixtures/boolean_operators.txt b/test/prism/fixtures/boolean_operators.txt
new file mode 100644
index 0000000000..dd0b9c9f01
--- /dev/null
+++ b/test/prism/fixtures/boolean_operators.txt
@@ -0,0 +1,5 @@
+a &&= b
+
+a += b
+
+a ||= b
diff --git a/test/prism/fixtures/booleans.txt b/test/prism/fixtures/booleans.txt
new file mode 100644
index 0000000000..d9417254b6
--- /dev/null
+++ b/test/prism/fixtures/booleans.txt
@@ -0,0 +1,3 @@
+false
+
+true
diff --git a/test/prism/fixtures/break.txt b/test/prism/fixtures/break.txt
new file mode 100644
index 0000000000..82fa45bdb4
--- /dev/null
+++ b/test/prism/fixtures/break.txt
@@ -0,0 +1,25 @@
+tap { break }
+
+tap { break (1), (2), (3) }
+
+tap { break 1 }
+
+tap { break 1, 2,
+3 }
+
+tap { break 1, 2, 3 }
+
+tap { break [1, 2, 3] }
+
+tap { break(
+ 1
+ 2
+) }
+
+tap { break() }
+
+tap { break(1) }
+
+foo { break 42 } == 42
+
+foo { |a| break } == 42
diff --git a/test/prism/fixtures/case.txt b/test/prism/fixtures/case.txt
new file mode 100644
index 0000000000..733e1e54d2
--- /dev/null
+++ b/test/prism/fixtures/case.txt
@@ -0,0 +1,55 @@
+case :hi
+when :hi
+end
+
+case true; when true; puts :hi; when false; puts :bye; end
+
+case; when *foo; end
+
+case :hi
+when :hi
+else
+:b
+end
+
+case this; when FooBar, BazBonk; end
+
+case
+when foo == bar
+end
+
+case
+when a
+else
+ # empty
+end
+
+case type;
+ ;when :b;
+ ; else;
+ end
+
+case ;;;;;;;; when 1; end
+
+case 1 in 2
+when 3
+end
+
+case 1 in 2; when 3; end
+
+case 1 in 2
+in 3
+end
+
+case 1 in 2; in 3; end
+
+case a
+in b if c and d
+ e
+end
+
+1.then do
+ case 1
+ in ^_1
+ end
+end
diff --git a/test/prism/fixtures/classes.txt b/test/prism/fixtures/classes.txt
new file mode 100644
index 0000000000..056cb00e82
--- /dev/null
+++ b/test/prism/fixtures/classes.txt
@@ -0,0 +1,35 @@
+class A a = 1 end
+
+class A; ensure; end
+
+class A; rescue; else; ensure; end
+
+class A < B
+a = 1
+end
+
+class << not foo
+end
+
+class A; class << self; ensure; end; end
+
+class A; class << self; rescue; else; ensure; end; end
+
+class << foo.bar
+end
+
+class << foo.bar;end
+
+class << self
+end
+
+class << self;end
+
+class << self
+1 + 2
+end
+
+class << self;1 + 2;end
+
+class A < B[1]
+end
diff --git a/test/prism/fixtures/command_method_call.txt b/test/prism/fixtures/command_method_call.txt
new file mode 100644
index 0000000000..182b87948b
--- /dev/null
+++ b/test/prism/fixtures/command_method_call.txt
@@ -0,0 +1,41 @@
+foo 1
+
+foo bar 1
+
+foo 1 if bar 2
+
+foo 1 unless bar 2
+
+foo 1 while bar 2
+
+foo 1 until bar 2
+
+foo 1 rescue bar 2
+
+foo[bar 1]
+
+foo 1 and bar 2
+
+foo 1 or bar 2
+
+not foo 1
+
+foo = bar = baz 1
+
+def foo = bar 1
+
+1.foo 2
+
+1.foo.bar 2
+
+1.foo[2].bar 3
+
+1.foo(2).bar 3
+
+1.foo(&2).bar 3
+
+!foo 1 and !bar 2
+
+!foo 1 or !bar 2
+
+not !foo 1
diff --git a/test/prism/fixtures/comments.txt b/test/prism/fixtures/comments.txt
new file mode 100644
index 0000000000..9bd853e927
--- /dev/null
+++ b/test/prism/fixtures/comments.txt
@@ -0,0 +1,24 @@
+a
+ # Comment
+b
+
+c # Comment
+d
+
+e
+# Comment
+ .f
+
+g
+ # Comment
+.h
+
+i # Comment
+.j
+
+k # Comment
+ .l
+
+m
+ # Comment
+ &.n
diff --git a/test/prism/fixtures/constants.txt b/test/prism/fixtures/constants.txt
new file mode 100644
index 0000000000..d86f8d3402
--- /dev/null
+++ b/test/prism/fixtures/constants.txt
@@ -0,0 +1,184 @@
+A::B
+
+A::B::C
+
+a::B
+
+A::B = 1
+
+A = 1
+
+ABC
+
+Foo 1
+
+Foo *bar
+
+Foo **bar
+
+Foo &bar
+
+Foo::Bar *baz
+
+Foo::Bar **baz
+
+Foo::Bar &baz
+
+::A::foo
+
+::A = 1
+
+::A::B = 1
+
+::A::B
+
+::A
+
+A::false
+
+A::B::true
+
+A::&
+
+A::`
+
+A::!
+
+A::!=
+
+A::^
+
+A::==
+
+A::===
+
+A::=~
+
+A::>
+
+A::>=
+
+A::>>
+
+A::<<
+
+A::\
+#
+C
+
+A::alias
+
+A::and
+
+A::begin
+
+A::BEGIN
+
+A::break
+
+A::class
+
+A::def
+
+A::defined
+
+A::do
+
+A::else
+
+A::elsif
+
+A::end
+
+A::END
+
+A::ensure
+
+A::false
+
+A::for
+
+A::if
+
+A::in
+
+A::next
+
+A::nil
+
+A::not
+
+A::or
+
+A::redo
+
+A::rescue
+
+A::retry
+
+A::return
+
+A::self
+
+A::super
+
+A::then
+
+A::true
+
+A::undef
+
+A::unless
+
+A::until
+
+A::when
+
+A::while
+
+A::yield
+
+A::__ENCODING__
+
+A::__FILE__
+
+A::__LINE__
+
+A::<
+
+A::<=>
+
+A::<<
+
+A::-
+
+A::%
+
+A::%i
+
+A::%w
+
+A::%x
+
+A::%I
+
+A::%W
+
+A::|
+
+A::+
+
+A::/
+
+A::*
+
+A::**
+
+A::~
+
+A::_::
+C
+
+A::_..
+
+A::__END__
diff --git a/test/prism/fixtures/dash_heredocs.txt b/test/prism/fixtures/dash_heredocs.txt
new file mode 100644
index 0000000000..3e663fae63
--- /dev/null
+++ b/test/prism/fixtures/dash_heredocs.txt
@@ -0,0 +1,63 @@
+<<-EOF
+ a
+EOF
+
+<<-FIRST + <<-SECOND
+ a
+FIRST
+ b
+SECOND
+
+<<-`EOF`
+ a
+#{b}
+EOF
+
+<<-EOF #comment
+ a
+EOF
+
+<<-EOF
+ a
+ b
+ EOF
+
+<<-"EOF"
+ a
+#{b}
+EOF
+
+<<-EOF
+ a
+#{b}
+EOF
+
+%#abc#
+
+<<-EOF
+ a
+ b
+EOF
+
+<<-''
+
+
+<<-'EOF'
+ a #{1}
+EOF
+
+<<-A + <<-B
+ a
+A
+ b
+ #{2
+ }
+B
+
+<<-A + <<-B
+ a
+A
+ b
+ #{
+ 2}
+B
diff --git a/test/prism/fixtures/defined.txt b/test/prism/fixtures/defined.txt
new file mode 100644
index 0000000000..247fa94e3a
--- /dev/null
+++ b/test/prism/fixtures/defined.txt
@@ -0,0 +1,10 @@
+defined? 1 and defined? 2
+
+defined?(x %= 2)
+
+defined?(foo and bar)
+
+defined? 1
+
+defined?("foo"
+)
diff --git a/test/prism/fixtures/dos_endings.txt b/test/prism/fixtures/dos_endings.txt
new file mode 100644
index 0000000000..c105522fa1
--- /dev/null
+++ b/test/prism/fixtures/dos_endings.txt
@@ -0,0 +1,20 @@
+puts "hi"\
+ "there"
+
+%I{a\
+b}
+
+<<-E
+ 1 \
+ 2
+ 3
+E
+
+x = %
+
+
+
+a = foo(<<~EOF.chop)
+
+ baz
+ EOF
diff --git a/test/prism/fixtures/dstring.txt b/test/prism/fixtures/dstring.txt
new file mode 100644
index 0000000000..085e0c6852
--- /dev/null
+++ b/test/prism/fixtures/dstring.txt
@@ -0,0 +1,29 @@
+"foo
+ bar"
+
+"foo
+ #{bar}"
+
+"fo
+o" "ba
+r"
+
+"
+foo\
+"
+
+"
+foo\\
+"
+
+"
+foo\\\
+"
+
+"
+foo\\\\
+"
+
+"
+foo\\\\\
+"
diff --git a/test/prism/fixtures/dsym_str.txt b/test/prism/fixtures/dsym_str.txt
new file mode 100644
index 0000000000..ee68dde88d
--- /dev/null
+++ b/test/prism/fixtures/dsym_str.txt
@@ -0,0 +1,2 @@
+:"foo
+ bar"
diff --git a/test/prism/fixtures/embdoc_no_newline_at_end.txt b/test/prism/fixtures/embdoc_no_newline_at_end.txt
new file mode 100644
index 0000000000..7dc2e32d73
--- /dev/null
+++ b/test/prism/fixtures/embdoc_no_newline_at_end.txt
@@ -0,0 +1,2 @@
+=begin
+=end \ No newline at end of file
diff --git a/test/prism/fixtures/emoji_method_calls.txt b/test/prism/fixtures/emoji_method_calls.txt
new file mode 100644
index 0000000000..96d0daba33
--- /dev/null
+++ b/test/prism/fixtures/emoji_method_calls.txt
@@ -0,0 +1 @@
+foo.🌊 = 1
diff --git a/test/prism/fixtures/endless_methods.txt b/test/prism/fixtures/endless_methods.txt
new file mode 100644
index 0000000000..8c2f2a30cc
--- /dev/null
+++ b/test/prism/fixtures/endless_methods.txt
@@ -0,0 +1,5 @@
+def foo = 1
+
+def bar = A ""
+
+def method = 1 + 2 + 3
diff --git a/test/prism/fixtures/endless_range_in_conditional.txt b/test/prism/fixtures/endless_range_in_conditional.txt
new file mode 100644
index 0000000000..6048008584
--- /dev/null
+++ b/test/prism/fixtures/endless_range_in_conditional.txt
@@ -0,0 +1,3 @@
+if 1..2 ; end
+if ..1 ; end
+if 1.. ; end
diff --git a/test/prism/fixtures/for.txt b/test/prism/fixtures/for.txt
new file mode 100644
index 0000000000..b6eb2cb24f
--- /dev/null
+++ b/test/prism/fixtures/for.txt
@@ -0,0 +1,19 @@
+for i in 1..10
+i
+end
+
+for i in 1..10; i; end
+
+for i,j in 1..10
+i
+end
+
+for i,j,k in 1..10
+i
+end
+
+for i in 1..10 do
+i
+end
+
+for i in 1..10; i; end
diff --git a/test/prism/fixtures/global_variables.txt b/test/prism/fixtures/global_variables.txt
new file mode 100644
index 0000000000..3dc52722a0
--- /dev/null
+++ b/test/prism/fixtures/global_variables.txt
@@ -0,0 +1,93 @@
+$global_variable
+
+$_
+
+$-w
+
+$LOAD_PATH
+
+$stdin
+
+$stdout
+
+$stderr
+
+$!
+
+$?
+
+$~
+
+$&
+
+$`
+
+$'
+
+$+
+
+$:
+
+$;
+
+$,
+
+$DEBUG
+
+$FILENAME
+
+$0
+
+$-0
+
+$LOADED_FEATURES
+
+$VERBOSE
+
+$-K
+
+:$global_variable
+
+:$_
+
+:$-w
+
+:$LOAD_PATH
+
+:$stdin
+
+:$stdout
+
+:$stderr
+
+:$!
+
+:$?
+
+:$~
+
+:$&
+
+:$`
+
+:$'
+
+:$+
+
+:$:
+
+:$;
+
+:$DEBUG
+
+:$FILENAME
+
+:$0
+
+:$-0
+
+:$LOADED_FEATURES
+
+:$VERBOSE
+
+:$-K
diff --git a/test/prism/fixtures/hashes.txt b/test/prism/fixtures/hashes.txt
new file mode 100644
index 0000000000..0afb8db496
--- /dev/null
+++ b/test/prism/fixtures/hashes.txt
@@ -0,0 +1,28 @@
+{}
+
+{
+}
+
+{ a => b, c => d }
+
+{ a => b, **c }
+
+{
+ a: b,
+ c: d
+
+
+
+ }
+
+{ a: b, c: d, **e, f: g }
+
+{ "a": !b? }
+
+a = 1
+tap do
+ b = 1
+ { a:, b:, c:, D: }
+end
+
+{ a: -1 }
diff --git a/test/prism/fixtures/heredoc.txt b/test/prism/fixtures/heredoc.txt
new file mode 100644
index 0000000000..f94fd912df
--- /dev/null
+++ b/test/prism/fixtures/heredoc.txt
@@ -0,0 +1,2 @@
+<<TEXT
+TEXT
diff --git a/test/prism/fixtures/heredoc_with_carriage_returns.txt b/test/prism/fixtures/heredoc_with_carriage_returns.txt
new file mode 100644
index 0000000000..32a2d87b24
--- /dev/null
+++ b/test/prism/fixtures/heredoc_with_carriage_returns.txt
@@ -0,0 +1,2 @@
+<<TEXT
+TEXT
diff --git a/test/prism/fixtures/heredoc_with_comment.txt b/test/prism/fixtures/heredoc_with_comment.txt
new file mode 100644
index 0000000000..8e7b7275bd
--- /dev/null
+++ b/test/prism/fixtures/heredoc_with_comment.txt
@@ -0,0 +1,3 @@
+<<-TARGET.chomp # the heredoc end token doesn't always precede the comment
+ content makes for an obvious error
+TARGET \ No newline at end of file
diff --git a/test/prism/fixtures/heredoc_with_escaped_newline_at_start.txt b/test/prism/fixtures/heredoc_with_escaped_newline_at_start.txt
new file mode 100644
index 0000000000..81f314e0b3
--- /dev/null
+++ b/test/prism/fixtures/heredoc_with_escaped_newline_at_start.txt
@@ -0,0 +1,7 @@
+<<-TARGET.gsub /^\s{/, ''\
+TARGET
+
+
+<<-TARGET.gsub /^\s{/, ''\
+TARGET
+
diff --git a/test/prism/fixtures/heredoc_with_trailing_newline.txt b/test/prism/fixtures/heredoc_with_trailing_newline.txt
new file mode 100644
index 0000000000..13771bf3ac
--- /dev/null
+++ b/test/prism/fixtures/heredoc_with_trailing_newline.txt
@@ -0,0 +1,2 @@
+<<-END
+END \ No newline at end of file
diff --git a/test/prism/fixtures/heredocs_leading_whitespace.txt b/test/prism/fixtures/heredocs_leading_whitespace.txt
new file mode 100644
index 0000000000..660ecb4543
--- /dev/null
+++ b/test/prism/fixtures/heredocs_leading_whitespace.txt
@@ -0,0 +1,29 @@
+<<-' FOO'
+a
+b
+ FOO
+
+<<-" FOO"
+a
+b
+ FOO
+
+<<-` FOO`
+a
+b
+ FOO
+
+<<-' FOO'
+a
+b
+ FOO
+
+<<~' FOO'
+a
+b
+ FOO
+
+<<~' FOO'
+a
+b
+ FOO
diff --git a/test/prism/fixtures/heredocs_nested.txt b/test/prism/fixtures/heredocs_nested.txt
new file mode 100644
index 0000000000..0802378278
--- /dev/null
+++ b/test/prism/fixtures/heredocs_nested.txt
@@ -0,0 +1,22 @@
+<<~RUBY
+pre
+#{
+<<RUBY
+ hello
+RUBY
+}
+post
+RUBY
+
+# depth greater than PM_LEX_STACK_SIZE
+<<-A
+#{
+<<-B
+#{
+<<-C
+#{3}
+C
+}
+B
+}
+A
diff --git a/test/prism/fixtures/heredocs_with_ignored_newlines.txt b/test/prism/fixtures/heredocs_with_ignored_newlines.txt
new file mode 100644
index 0000000000..8e12546ec7
--- /dev/null
+++ b/test/prism/fixtures/heredocs_with_ignored_newlines.txt
@@ -0,0 +1,14 @@
+<<-HERE\
+HERE
+
+<<~THERE\
+ way over
+ <<HERE
+ not here
+ HERE
+
+ <<~BUT\
+ but
+ BUT
+ there
+THERE
diff --git a/test/prism/fixtures/heredocs_with_ignored_newlines_and_non_empty.txt b/test/prism/fixtures/heredocs_with_ignored_newlines_and_non_empty.txt
new file mode 100644
index 0000000000..aeacbd8126
--- /dev/null
+++ b/test/prism/fixtures/heredocs_with_ignored_newlines_and_non_empty.txt
@@ -0,0 +1,4 @@
+<<-EOE
+ some
+ heredocs
+EOE \ No newline at end of file
diff --git a/test/prism/fixtures/if.txt b/test/prism/fixtures/if.txt
new file mode 100644
index 0000000000..4139bae5ed
--- /dev/null
+++ b/test/prism/fixtures/if.txt
@@ -0,0 +1,42 @@
+if true; 1; end
+
+if true
+1 else 2 end
+
+if true then true elsif false then false elsif nil then nil else self end
+
+1 if true
+
+tap { break if true }
+
+tap { next if true }
+
+return if true
+
+tap { if exit_loop then break 42 end }
+
+if foo
+then bar
+end
+
+a if b if c
+
+if true
+ a b:
+else
+end
+
+if type in 1
+elsif type in B
+end
+
+if f1
+ lambda do |_|
+ end
+elsif f2
+ lambda do |_|
+ end
+else
+ lambda do |_|
+ end
+end
diff --git a/test/prism/fixtures/indented_file_end.txt b/test/prism/fixtures/indented_file_end.txt
new file mode 100644
index 0000000000..5a96dcacf7
--- /dev/null
+++ b/test/prism/fixtures/indented_file_end.txt
@@ -0,0 +1,4 @@
+ def hi
+
+ end # hi there
+ \ No newline at end of file
diff --git a/test/prism/fixtures/integer_operations.txt b/test/prism/fixtures/integer_operations.txt
new file mode 100644
index 0000000000..37163acf30
--- /dev/null
+++ b/test/prism/fixtures/integer_operations.txt
@@ -0,0 +1,63 @@
+!1
+
+~1
+
+1 != 2
+
+1 !~ 2
+
+1 % 2
+
+1 & 2
+
+1 * 2
+
+1**2
+
+1 + 2
+
+1 - 2
+
+1 / 2
+
+1/2/3
+
+1 < 2
+
+1 << 2
+
+1 <= 2
+
+1 <=> 2
+
+1 == 2
+
+1 === 2
+
+1 =~ 2
+
+1 > 2
+
+1 >= 2
+
+1 >> 2
+
+1 ^ 2
+
+1 | 2
+
+1 && 2
+
+1 and 2
+
+1 * 2 ** 3
+
+1 * 2 + 3
+
+1 or 2
+
+1 || 2
+
+1 + 2 * 3
+
+(1 + 1)
diff --git a/test/prism/fixtures/keyword_method_names.txt b/test/prism/fixtures/keyword_method_names.txt
new file mode 100644
index 0000000000..9154469441
--- /dev/null
+++ b/test/prism/fixtures/keyword_method_names.txt
@@ -0,0 +1,29 @@
+def def
+end
+
+def self.ensure
+end
+
+private def foo
+ bar do
+ end
+end
+
+def m(a, **nil)
+end
+
+def __ENCODING__.a
+end
+
+%{abc}
+
+%"abc"
+
+def __FILE__.a
+end
+
+def __LINE__.a
+end
+
+def nil::a
+end
diff --git a/test/prism/fixtures/keywords.txt b/test/prism/fixtures/keywords.txt
new file mode 100644
index 0000000000..6d24a9f476
--- /dev/null
+++ b/test/prism/fixtures/keywords.txt
@@ -0,0 +1,11 @@
+tap { redo }
+
+begin; rescue; retry; end
+
+self
+
+__ENCODING__
+
+__FILE__
+
+__LINE__
diff --git a/test/prism/fixtures/lambda.txt b/test/prism/fixtures/lambda.txt
new file mode 100644
index 0000000000..5c21eafb24
--- /dev/null
+++ b/test/prism/fixtures/lambda.txt
@@ -0,0 +1,11 @@
+->(
+ foo
+) {}
+
+->(x: "b#{a}") { }
+
+->(a: b * 3) {}
+
+-> foo = bar do end
+
+-> foo: bar do end
diff --git a/test/prism/fixtures/method_calls.txt b/test/prism/fixtures/method_calls.txt
new file mode 100644
index 0000000000..987fb3f90f
--- /dev/null
+++ b/test/prism/fixtures/method_calls.txt
@@ -0,0 +1,156 @@
+foo.bar %{baz}
+
+a.b(c, d)
+
+a.b()
+
+foo
+ .bar
+ &.baz
+
+a!
+
+a.()
+
+a.(1, 2, 3)
+
+a::b
+
+a::b c
+
+foo.bar = 1
+
+a?
+
+a(&block)
+
+a(**kwargs)
+
+a.b.c
+
+a(b, c)
+
+a()
+
+a(*args)
+
+a b, c
+
+a.b c, d
+
+foo.foo, bar.bar = 1, 2
+
+a&.b
+
+a&.()
+
+a&.b(c)
+
+a&.b()
+
+foo :a, :b if bar? or baz and qux
+
+foo(:a,
+
+ :b
+)
+
+foo(*rest)
+
+foo(:a, :h => [:x, :y], :a => :b, &:bar)
+
+hi 123, { :there => :friend, **{}, whatup: :dog }
+
+foo :a, b: true do |a, b| puts a end
+
+hi there: :friend
+
+hi :there => :friend, **{}, whatup: :dog
+
+hi(:there => :friend, **{}, whatup: :dog)
+
+foo({ a: true, b: false, }, &:block)
+
+hi :there => :friend
+
+foo(:a,
+:b,
+)
+
+foo(
+:a,
+b: :c,
+)
+
+foo &:block
+
+foo a: true, b: false, &:block
+
+some_func 1, kwarg: 2
+
+Kernel.Integer(10)
+
+x.each { }
+
+foo.map { $& }
+
+A::B::C :foo
+
+A::B::C(:foo)
+
+A::B::C(:foo) { }
+
+foo("a": -1)
+
+foo bar: { baz: qux do end }
+
+foo bar: { **kw do end }
+
+foo "#{bar.map do "baz" end}" do end
+
+foo class Bar baz do end end
+
+foo module Bar baz do end end
+
+foo [baz do end]
+
+p begin 1.times do 1 end end
+
+foo :a,
+ if x
+ bar do |a|
+ a
+ end
+ end
+
+foo :a,
+ while x
+ bar do |a|
+ a
+ end
+ end,
+ until x
+ baz do
+ end
+ end
+
+{} + A {}
+
+{} + A { |a| a }
+
+A {} + A {}
+
+lst << A {}
+
+"#{ join (" ") }"
+
+"#{(v)}"
+
+def f(*); p *; end
+
+foo 1, Bar { 1 }
+
+foo = 1
+foo {}
+
+@a.b "c#{name}": 42
diff --git a/test/prism/fixtures/methods.txt b/test/prism/fixtures/methods.txt
new file mode 100644
index 0000000000..4bfd976eda
--- /dev/null
+++ b/test/prism/fixtures/methods.txt
@@ -0,0 +1,183 @@
+def foo((bar, baz))
+end
+
+def foo((bar, baz), optional = 1, (bin, bag))
+end
+
+
+def a; ensure; end
+
+def (b).a
+end
+
+def (a)::b
+end
+
+def false.a
+end
+
+def a(...)
+end
+
+def $var.a
+end
+
+def a.b
+end
+
+def @var.a
+end
+
+def a b:; end
+
+%,abc,
+
+def a(b:)
+end
+
+def a(**b)
+end
+
+def a(**)
+end
+
+a = 1; def a
+end
+
+def a b, c, d
+end
+
+def nil.a
+end
+
+def a b:, c: 1
+end
+
+def a(b:, c: 1)
+end
+
+def a(b:
+ 1, c:)
+end
+
+%.abc.
+
+def a b = 1, c = 2
+end
+
+def a()
+end
+
+def a b, c = 2
+end
+
+def a b
+end
+
+def a; rescue; else; ensure; end
+
+def a *b
+end
+
+def a(*)
+end
+
+def a
+b = 1
+end
+
+def self.a
+end
+
+def true.a
+end
+
+def a
+end
+
+def hi
+return :hi if true
+:bye
+end
+
+def foo = 1
+def bar = 2
+
+def foo(bar) = 123
+
+def foo = 123
+
+def a(*); b(*); end
+
+def a(...); b(...); end
+
+def a(...); b(1, 2, ...); end
+
+def (c = b).a
+end
+
+def a &b
+end
+
+def a(&)
+end
+
+def @@var.a
+end
+
+def (a = b).C
+end
+
+def self.Array_function; end
+
+Const = 1; def Const.a
+end
+
+def a(...); "foo#{b(...)}"; end
+
+def foo
+ {}.merge **bar, **baz, **qux
+end
+
+def bar(a: (1...10))
+end
+
+def bar(a: (...10))
+end
+
+def bar(a: (1...))
+end
+
+def bar(a = (1...10))
+end
+
+def bar(a = (...10))
+end
+
+def bar(a = (1...))
+end
+
+def method(a)
+ item >> a {}
+end
+
+foo = 1
+def foo.bar; end
+
+def f(*); [*]; end
+
+def f x:-a; end
+
+def f x:+a; end
+
+def f x:!a; end
+
+def foo x:%(xx); end
+
+def foo(...)
+ bar(...)
+end
+
+def foo(bar = (def baz(bar) = bar; 1)) = 2
+
+def (class Foo; end).foo(bar = 1) = 2
diff --git a/test/prism/fixtures/modules.txt b/test/prism/fixtures/modules.txt
new file mode 100644
index 0000000000..76bd9bea43
--- /dev/null
+++ b/test/prism/fixtures/modules.txt
@@ -0,0 +1,18 @@
+module A a = 1 end
+
+%Q{aaa #{bbb} ccc}
+
+module m::M
+end
+
+module A
+ x = 1; rescue; end
+
+module ::A
+end
+
+module A[]::B
+end
+
+module A[1]::B
+end
diff --git a/test/prism/fixtures/multi_write.txt b/test/prism/fixtures/multi_write.txt
new file mode 100644
index 0000000000..edbcafb969
--- /dev/null
+++ b/test/prism/fixtures/multi_write.txt
@@ -0,0 +1,4 @@
+foo = 1 rescue nil
+foo, bar = 1 rescue nil
+foo = 1, 2 rescue nil
+foo, bar = 1, 2 rescue nil
diff --git a/test/prism/fixtures/newline_terminated.txt b/test/prism/fixtures/newline_terminated.txt
new file mode 100644
index 0000000000..12f3bda229
--- /dev/null
+++ b/test/prism/fixtures/newline_terminated.txt
Binary files differ
diff --git a/test/prism/fixtures/next.txt b/test/prism/fixtures/next.txt
new file mode 100644
index 0000000000..2ef14c6304
--- /dev/null
+++ b/test/prism/fixtures/next.txt
@@ -0,0 +1,24 @@
+tap { next }
+
+tap { next (1), (2), (3) }
+
+tap { next 1 }
+
+tap { next 1, 2,
+3 }
+
+tap { next 1, 2, 3 }
+
+tap { next [1, 2, 3] }
+
+tap { next(
+ 1
+ 2
+) }
+
+tap { next
+1 }
+
+tap { next() }
+
+tap { next(1) }
diff --git a/test/prism/fixtures/nils.txt b/test/prism/fixtures/nils.txt
new file mode 100644
index 0000000000..8084db2534
--- /dev/null
+++ b/test/prism/fixtures/nils.txt
@@ -0,0 +1,13 @@
+nil
+
+()
+
+(
+;
+;
+)
+
+END { 1 }
+
+BEGIN { 1 }
+
diff --git a/test/prism/fixtures/non_alphanumeric_methods.txt b/test/prism/fixtures/non_alphanumeric_methods.txt
new file mode 100644
index 0000000000..1da3fd852b
--- /dev/null
+++ b/test/prism/fixtures/non_alphanumeric_methods.txt
@@ -0,0 +1,105 @@
+def !
+end
+
+def !=
+end
+
+def !~
+end
+
+def %
+end
+
+def self.+
+end
+
+def &
+end
+
+def *
+end
+
+def **
+end
+
+%|abc|
+
+def + **b
+end
+
+def +()
+end
+
+def + b
+end
+
+def self.+
+end
+
+def +
+end
+
+def +@
+end
+
+def -
+end
+
+def a.-;end
+
+def -@
+end
+
+def /
+end
+
+def <
+end
+
+def <<
+end
+
+def <=
+end
+
+def <=>
+end
+
+def ==
+end
+
+def ===
+end
+
+def =~
+end
+
+def >
+end
+
+def >=
+end
+
+def >>
+end
+
+def []
+end
+
+def []=
+end
+
+def ^
+end
+
+def `
+end
+
+def self.`
+end
+
+def |
+end
+
+def ~
+end
diff --git a/test/prism/fixtures/not.txt b/test/prism/fixtures/not.txt
new file mode 100644
index 0000000000..520b34fa37
--- /dev/null
+++ b/test/prism/fixtures/not.txt
@@ -0,0 +1,37 @@
+not foo and not bar
+
+not(foo and bar)
+
+not foo
+
+not foo and not
+ bar
+
+
+not foo and
+ not
+ bar
+
+
+not foo and
+ not
+
+
+ bar
+
+not(foo
+
+
+)
+
+not(
+
+
+foo
+
+
+ )
+
+not foo .. bar
+
+not (foo .. bar)
diff --git a/test/prism/fixtures/numbers.txt b/test/prism/fixtures/numbers.txt
new file mode 100644
index 0000000000..47f20dcb42
--- /dev/null
+++ b/test/prism/fixtures/numbers.txt
@@ -0,0 +1,67 @@
+0
+
+1
+
+1.0
+
+2
+
+0b0
+
+0b1
+
+0b10
+
+0d0
+
+0d1
+
+0d2
+
+00
+
+01
+
+02
+
+0o0
+
+0o1
+
+0o2
+
+0x0
+
+0x1
+
+0x2
+
+1i
+
+1r
+
+-1
+
+1ri
+
+1.2r
+
+1.2ri
+
+-1ri
+
+-1.2r
+
+-1.2ri
+
+0o1r
+
+0o1i
+
+0o1ri
+
+0d1r
+
+0d1i
+
+0b1ri
diff --git a/test/prism/fixtures/patterns.txt b/test/prism/fixtures/patterns.txt
new file mode 100644
index 0000000000..7ce3b9e4a8
--- /dev/null
+++ b/test/prism/fixtures/patterns.txt
@@ -0,0 +1,217 @@
+foo => bar
+foo => 1
+foo => 1.0
+foo => 1i
+foo => 1r
+foo => :foo
+foo => %s[foo]
+foo => :"foo"
+foo => /foo/
+foo => `foo`
+foo => %x[foo]
+foo => %i[foo]
+foo => %I[foo]
+foo => %w[foo]
+foo => %W[foo]
+foo => %q[foo]
+foo => %Q[foo]
+foo => "foo"
+foo => nil
+foo => self
+foo => true
+foo => false
+foo => __FILE__
+foo => __LINE__
+foo => __ENCODING__
+foo => -> { bar }
+
+foo => 1 .. 1
+foo => 1.0 .. 1.0
+foo => 1i .. 1i
+foo => 1r .. 1r
+foo => :foo .. :foo
+foo => %s[foo] .. %s[foo]
+foo => :"foo" .. :"foo"
+foo => /foo/ .. /foo/
+foo => `foo` .. `foo`
+foo => %x[foo] .. %x[foo]
+foo => %i[foo] .. %i[foo]
+foo => %I[foo] .. %I[foo]
+foo => %w[foo] .. %w[foo]
+foo => %W[foo] .. %W[foo]
+foo => %q[foo] .. %q[foo]
+foo => %Q[foo] .. %Q[foo]
+foo => "foo" .. "foo"
+foo => nil .. nil
+foo => self .. self
+foo => true .. true
+foo => false .. false
+foo => __FILE__ .. __FILE__
+foo => __LINE__ .. __LINE__
+foo => __ENCODING__ .. __ENCODING__
+foo => -> { bar } .. -> { bar }
+
+bar = 1; foo => ^bar
+foo => ^@bar
+foo => ^@@bar
+foo => ^$bar
+
+foo => ^(1)
+foo => ^(nil)
+foo => ^("bar" + "baz")
+
+foo => Foo
+foo => Foo::Bar::Baz
+foo => ::Foo
+foo => ::Foo::Bar::Baz
+
+foo => Foo()
+foo => Foo(1)
+foo => Foo(1, 2, 3)
+foo => Foo(bar)
+foo => Foo(*bar, baz)
+foo => Foo(bar, *baz)
+foo => Foo(*bar, baz, *qux)
+
+foo => Foo[]
+foo => Foo[1]
+foo => Foo[1, 2, 3]
+foo => Foo[Foo[]]
+foo => Foo[bar]
+foo => Foo[*bar, baz]
+foo => Foo[bar, *baz]
+foo => Foo[*bar, baz, *qux]
+
+foo => *bar
+foo => *bar, baz, qux
+foo => bar, *baz, qux
+foo => bar, baz, *qux
+foo => *bar, baz, *qux
+
+foo => bar,
+
+; # end the previous pattern for ParseTest#test_filepath_patterns.txt which parses the whole file at once
+
+foo => []
+foo => [[[[[]]]]]
+
+foo => [*bar]
+foo => [*bar, baz, qux]
+foo => [bar, *baz, qux]
+foo => [bar, baz, *qux]
+foo => [*bar, baz, *qux]
+
+foo in bar
+foo in 1
+foo in 1.0
+foo in 1i
+foo in 1r
+foo in :foo
+foo in %s[foo]
+foo in :"foo"
+foo in /foo/
+foo in `foo`
+foo in %x[foo]
+foo in %i[foo]
+foo in %I[foo]
+foo in %w[foo]
+foo in %W[foo]
+foo in %q[foo]
+foo in %Q[foo]
+foo in "foo"
+foo in nil
+foo in self
+foo in true
+foo in false
+foo in __FILE__
+foo in __LINE__
+foo in __ENCODING__
+foo in -> { bar }
+
+foo in bar,
+
+; # end the previous pattern for ParseTest#test_filepath_patterns.txt which parses the whole file at once
+
+case foo; in bar then end
+case foo; in 1 then end
+case foo; in 1.0 then end
+case foo; in 1i then end
+case foo; in 1r then end
+case foo; in :foo then end
+case foo; in %s[foo] then end
+case foo; in :"foo" then end
+case foo; in /foo/ then end
+case foo; in `foo` then end
+case foo; in %x[foo] then end
+case foo; in %i[foo] then end
+case foo; in %I[foo] then end
+case foo; in %w[foo] then end
+case foo; in %W[foo] then end
+case foo; in %q[foo] then end
+case foo; in %Q[foo] then end
+case foo; in "foo" then end
+case foo; in nil then end
+case foo; in self then end
+case foo; in true then end
+case foo; in false then end
+case foo; in __FILE__ then end
+case foo; in __LINE__ then end
+case foo; in __ENCODING__ then end
+case foo; in -> { bar } then end
+
+case foo; in bar if baz then end
+case foo; in 1 if baz then end
+case foo; in 1.0 if baz then end
+case foo; in 1i if baz then end
+case foo; in 1r if baz then end
+case foo; in :foo if baz then end
+case foo; in %s[foo] if baz then end
+case foo; in :"foo" if baz then end
+case foo; in /foo/ if baz then end
+case foo; in `foo` if baz then end
+case foo; in %x[foo] if baz then end
+case foo; in %i[foo] if baz then end
+case foo; in %I[foo] if baz then end
+case foo; in %w[foo] if baz then end
+case foo; in %W[foo] if baz then end
+case foo; in %q[foo] if baz then end
+case foo; in %Q[foo] if baz then end
+case foo; in "foo" if baz then end
+case foo; in nil if baz then end
+case foo; in self if baz then end
+case foo; in true if baz then end
+case foo; in false if baz then end
+case foo; in __FILE__ if baz then end
+case foo; in __LINE__ if baz then end
+case foo; in __ENCODING__ if baz then end
+case foo; in -> { bar } if baz then end
+
+if a in []
+end
+
+a => [
+ b
+]
+
+foo in A[
+ bar: B[
+ value: a
+ ]
+]
+
+foo in bar => baz
+foo => bar => baz
+
+foo, bar, baz = 1, 2
+foo do
+ [1, 2] => [foo, bar] => baz
+end
+
+foo => Object[{x:}]
+
+1.then { 1 in ^_1 }
+
+(
+ a,
+ b
+) = c
diff --git a/test/prism/fixtures/procs.txt b/test/prism/fixtures/procs.txt
new file mode 100644
index 0000000000..7ffb11e78a
--- /dev/null
+++ b/test/prism/fixtures/procs.txt
@@ -0,0 +1,27 @@
+-> (a; b, c, d) { b }
+
+-> do
+ensure
+end
+
+-> do
+rescue
+else
+ensure
+end
+
+-> { foo }
+
+-> do; foo; end
+
+-> a, b = 1, c:, d:, &e { a }
+
+-> (a, b = 1, *c, d:, e:, **f, &g) { a }
+
+-> (a, b = 1, *c, d:, e:, **f, &g) do
+ a
+end
+
+-> (a) { -> b { a * b } }
+
+-> ((a, b), *c) { }
diff --git a/test/prism/fixtures/range_begin_open_exclusive.txt b/test/prism/fixtures/range_begin_open_exclusive.txt
new file mode 100644
index 0000000000..3b12672b4f
--- /dev/null
+++ b/test/prism/fixtures/range_begin_open_exclusive.txt
@@ -0,0 +1 @@
+...2
diff --git a/test/prism/fixtures/range_begin_open_inclusive.txt b/test/prism/fixtures/range_begin_open_inclusive.txt
new file mode 100644
index 0000000000..052f45900b
--- /dev/null
+++ b/test/prism/fixtures/range_begin_open_inclusive.txt
@@ -0,0 +1 @@
+..2
diff --git a/test/prism/fixtures/range_end_open_exclusive.txt b/test/prism/fixtures/range_end_open_exclusive.txt
new file mode 100644
index 0000000000..2ffd68afdb
--- /dev/null
+++ b/test/prism/fixtures/range_end_open_exclusive.txt
@@ -0,0 +1 @@
+2...
diff --git a/test/prism/fixtures/range_end_open_inclusive.txt b/test/prism/fixtures/range_end_open_inclusive.txt
new file mode 100644
index 0000000000..48445391f6
--- /dev/null
+++ b/test/prism/fixtures/range_end_open_inclusive.txt
@@ -0,0 +1 @@
+2..
diff --git a/test/prism/fixtures/ranges.txt b/test/prism/fixtures/ranges.txt
new file mode 100644
index 0000000000..e2e4136ae9
--- /dev/null
+++ b/test/prism/fixtures/ranges.txt
@@ -0,0 +1,49 @@
+(...2)
+
+(..2)
+
+1...2
+
+foo[...2]
+
+{ foo: ...bar }
+
+(1...)
+
+1..2
+
+{ foo: ..bar }
+
+(1..)
+
+1 .. ..1
+
+1.. && 2
+
+1.. == 2
+
+1.. != 2
+
+1.. === 2
+
+1.. <=> 2
+
+1.. =~ 2
+
+1.. !~ 2
+
+1.. < 2
+
+1.. > 2
+
+1.. <= 2
+
+1.. >= 2
+
+1.. << 2
+
+1.. >> 2
+
+1.. + 2
+
+1.. - 2
diff --git a/test/prism/fixtures/regex.txt b/test/prism/fixtures/regex.txt
new file mode 100644
index 0000000000..84b5ca0600
--- /dev/null
+++ b/test/prism/fixtures/regex.txt
@@ -0,0 +1,46 @@
+foo /bar/
+
+%r{abc}i
+
+/a\b/
+
+/aaa #$bbb/
+
+/aaa #{bbb} ccc/
+
+[/(?<foo>bar)/ =~ baz, foo]
+
+/abc/i
+
+%r/[a-z$._?][\w$.?#@~]*:/i
+
+%r/([a-z$._?][\w$.?#@~]*)(\s+)(equ)/i
+
+%r/[a-z$._?][\w$.?#@~]*/i
+
+%r(
+(?:[#$%_']|\(\)|\(,\)|\[\]|[0-9])*
+ (?:[#$%_']+)
+)
+
+/(?#\))/ =~ "hi"
+
+%r#pound#
+
+/aaa #{bbb}/o
+
+/(?<a\
+b>)/ =~ ""; ab
+
+/(?<abc>)(?<abc>)/ =~ ""; abc
+
+/(?<a b>)/ =~ ""
+
+a = 1
+tap { /(?<a>)/ =~ to_s }
+
+/(?<foo>)/ =~ ""
+/(?<Foo>)/ =~ ""
+
+/(?<nil>)/ =~ ""
+def foo(nil:) = /(?<nil>)/ =~ ""
diff --git a/test/prism/fixtures/regex_char_width.txt b/test/prism/fixtures/regex_char_width.txt
new file mode 100644
index 0000000000..7096b71584
--- /dev/null
+++ b/test/prism/fixtures/regex_char_width.txt
@@ -0,0 +1,3 @@
+# encoding: sjis
+/â…§(?<a>.)â…©(?<b>.)/ =~ 'â…§aâ…©b'
+[a, b]
diff --git a/test/prism/fixtures/repeat_parameters.txt b/test/prism/fixtures/repeat_parameters.txt
new file mode 100644
index 0000000000..9c69e9718a
--- /dev/null
+++ b/test/prism/fixtures/repeat_parameters.txt
@@ -0,0 +1,38 @@
+def foo(a, _)
+end
+
+def foo(a, _, _)
+end
+
+def foo(a, _, _, _b)
+end
+
+def foo(a, _, _, _b, _b)
+end
+
+def foo(a, (b, *_c, d), (e, *_c, f))
+end
+
+def foo(_a, _a, b, c)
+end
+
+def foo((a, *_b, c), (d, *_b, e))
+end
+
+def foo(_a = 1, _a = 2)
+end
+
+def foo(_a:, _a:)
+end
+
+def foo(_a: 1, _a: 2)
+end
+
+def foo(_a, **_a)
+end
+
+def foo(_a, &_a)
+end
+
+def foo(_a, *_a)
+end
diff --git a/test/prism/fixtures/rescue.txt b/test/prism/fixtures/rescue.txt
new file mode 100644
index 0000000000..99170fbe0f
--- /dev/null
+++ b/test/prism/fixtures/rescue.txt
@@ -0,0 +1,35 @@
+foo rescue nil
+
+foo rescue
+nil
+
+tap { break rescue nil }
+
+tap { next rescue nil }
+
+return rescue nil
+
+foo rescue nil || 1
+
+foo rescue nil ? 1 : 2
+
+begin; a; rescue *b; end
+
+foo do |x|
+ bar(y) rescue ArgumentError fail "baz"
+end
+
+if a = foo rescue nil
+ bar
+end
+
+def some_method = other_method 42 rescue nil
+
+def a
+ a b:
+rescue
+end
+
+foo if bar rescue baz
+
+z = x y rescue c d
diff --git a/test/prism/fixtures/return.txt b/test/prism/fixtures/return.txt
new file mode 100644
index 0000000000..a8b5b95fab
--- /dev/null
+++ b/test/prism/fixtures/return.txt
@@ -0,0 +1,24 @@
+return
+
+return (1), (2), (3)
+
+return *1
+
+return 1
+
+return 1, 2,
+3
+
+return 1, 2, 3
+
+return [1, 2, 3]
+
+return(
+ 1
+ 2
+)
+
+return()
+
+return(1)
+
diff --git a/test/prism/fixtures/seattlerb/BEGIN.txt b/test/prism/fixtures/seattlerb/BEGIN.txt
new file mode 100644
index 0000000000..bed5755901
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/BEGIN.txt
@@ -0,0 +1 @@
+BEGIN { 42 }
diff --git a/test/prism/fixtures/seattlerb/README.rdoc b/test/prism/fixtures/seattlerb/README.rdoc
new file mode 100644
index 0000000000..1e5bfbdfe0
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/README.rdoc
@@ -0,0 +1,113 @@
+= ruby_parser
+
+home :: https://github.com/seattlerb/ruby_parser
+bugs :: https://github.com/seattlerb/ruby_parser/issues
+rdoc :: http://docs.seattlerb.org/ruby_parser
+
+== DESCRIPTION:
+
+ruby_parser (RP) is a ruby parser written in pure ruby (utilizing
+racc--which does by default use a C extension). It outputs
+s-expressions which can be manipulated and converted back to ruby via
+the ruby2ruby gem.
+
+As an example:
+
+ def conditional1 arg1
+ return 1 if arg1 == 0
+ return 0
+ end
+
+becomes:
+
+ s(:defn, :conditional1, s(:args, :arg1),
+ s(:if,
+ s(:call, s(:lvar, :arg1), :==, s(:lit, 0)),
+ s(:return, s(:lit, 1)),
+ nil),
+ s(:return, s(:lit, 0)))
+
+Tested against 801,039 files from the latest of all rubygems (as of 2013-05):
+
+* 1.8 parser is at 99.9739% accuracy, 3.651 sigma
+* 1.9 parser is at 99.9940% accuracy, 4.013 sigma
+* 2.0 parser is at 99.9939% accuracy, 4.008 sigma
+* 2.6 parser is at 99.9972% accuracy, 4.191 sigma
+* 3.0 parser has a 100% parse rate.
+ * Tested against 2,672,412 unique ruby files across 167k gems.
+ * As do all the others now, basically.
+
+== FEATURES/PROBLEMS:
+
+* Pure ruby, no compiles.
+* Includes preceding comment data for defn/defs/class/module nodes!
+* Incredibly simple interface.
+* Output is 100% equivalent to ParseTree.
+ * Can utilize PT's SexpProcessor and UnifiedRuby for language processing.
+* Known Issue: Speed is now pretty good, but can always improve:
+ * RP parses a corpus of 3702 files in 125s (avg 108 Kb/s)
+ * MRI+PT parsed the same in 67.38s (avg 200.89 Kb/s)
+* Known Issue: Code is much better, but still has a long way to go.
+* Known Issue: Totally awesome.
+* Known Issue: line number values can be slightly off. Parsing LR sucks.
+
+== SYNOPSIS:
+
+ RubyParser.new.parse "1+1"
+ # => s(:call, s(:lit, 1), :+, s(:lit, 1))
+
+You can also use Ruby19Parser, Ruby18Parser, or RubyParser.for_current_ruby:
+
+ RubyParser.for_current_ruby.parse "1+1"
+ # => s(:call, s(:lit, 1), :+, s(:lit, 1))
+
+== DEVELOPER NOTES:
+
+To add a new version:
+
+* New parser should be generated from lib/ruby_parser[23].yy.
+* Extend lib/ruby_parser[23].yy with new class name.
+* Add new version number to V2/V3 in Rakefile for rule creation.
+* Add new `ruby_parse "x.y.z"` line to Rakefile for rake compare (line ~300).
+* Require generated parser in lib/ruby_parser.rb.
+* Add new V## = ::Ruby##Parser; end to ruby_parser.rb (bottom of file).
+* Add empty TestRubyParserShared##Plus module and TestRubyParserV## to test/test_ruby_parser.rb.
+* Extend Manifest.txt with generated file names.
+* Add new version number to sexp_processor's pt_testcase.rb in all_versions.
+
+Until all of these are done, you won't have a clean test run.
+
+== REQUIREMENTS:
+
+* ruby. woot.
+* sexp_processor for Sexp and SexpProcessor classes, and testing.
+* racc full package for parser development (compiling .y to .rb).
+
+== INSTALL:
+
+* sudo gem install ruby_parser
+
+== LICENSE:
+
+(The MIT License)
+
+Copyright (c) Ryan Davis, seattle.rb
+
+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.
diff --git a/test/prism/fixtures/seattlerb/TestRubyParserShared.txt b/test/prism/fixtures/seattlerb/TestRubyParserShared.txt
new file mode 100644
index 0000000000..c55b3e1f70
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/TestRubyParserShared.txt
@@ -0,0 +1,92 @@
+%I[
+
+
+]
+
+%I[
+line2
+line3
+]
+
+%W[
+
+
+]
+
+%W[
+line2
+line3
+]
+
+%i[
+
+
+]
+
+%i[
+line2
+line3
+]
+
+%r[
+
+
+]
+
+%w[
+
+
+]
+
+%w[
+line2
+line3
+]
+
+[
+:line2,
+:line3
+]
+
+class X # line 1
+ def self.y(a, # line 2
+ b) # line 3
+ a + b # line 4
+ end # line 5
+end # line 6
+
+
+class X # line 1
+ class Y # line 2
+ Z = 42 # line 3
+ end # line 4
+end # line 5
+
+
+class X # line 1
+ def y(a, # line 2
+ b) # line 3
+ a + b # line 4
+ end # line 5
+end # line 6
+
+
+module X
+ X = [
+ :line3,
+ :line4,
+ ]
+end
+
+
+module X # line 1
+ module Y # line 2
+ Z = 42 # line 3
+ end # line 4
+end # line 5
+
+
+x(
+:line2,
+:line3
+)
diff --git a/test/prism/fixtures/seattlerb/__ENCODING__.txt b/test/prism/fixtures/seattlerb/__ENCODING__.txt
new file mode 100644
index 0000000000..d6debf2f92
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/__ENCODING__.txt
@@ -0,0 +1 @@
+__ENCODING__
diff --git a/test/prism/fixtures/seattlerb/alias_gvar_backref.txt b/test/prism/fixtures/seattlerb/alias_gvar_backref.txt
new file mode 100644
index 0000000000..016bd94fe0
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/alias_gvar_backref.txt
@@ -0,0 +1 @@
+alias $MATCH $&
diff --git a/test/prism/fixtures/seattlerb/alias_resword.txt b/test/prism/fixtures/seattlerb/alias_resword.txt
new file mode 100644
index 0000000000..63e782614b
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/alias_resword.txt
@@ -0,0 +1 @@
+alias in out
diff --git a/test/prism/fixtures/seattlerb/and_multi.txt b/test/prism/fixtures/seattlerb/and_multi.txt
new file mode 100644
index 0000000000..8902086cac
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/and_multi.txt
@@ -0,0 +1,3 @@
+true and
+not false and
+true
diff --git a/test/prism/fixtures/seattlerb/aref_args_assocs.txt b/test/prism/fixtures/seattlerb/aref_args_assocs.txt
new file mode 100644
index 0000000000..3244eebafc
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/aref_args_assocs.txt
@@ -0,0 +1 @@
+[1 => 2]
diff --git a/test/prism/fixtures/seattlerb/aref_args_lit_assocs.txt b/test/prism/fixtures/seattlerb/aref_args_lit_assocs.txt
new file mode 100644
index 0000000000..0b6ffa7e2c
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/aref_args_lit_assocs.txt
@@ -0,0 +1 @@
+[1, 2 => 3]
diff --git a/test/prism/fixtures/seattlerb/args_kw_block.txt b/test/prism/fixtures/seattlerb/args_kw_block.txt
new file mode 100644
index 0000000000..cb6ab39852
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/args_kw_block.txt
@@ -0,0 +1 @@
+def f(a: 1, &b); end
diff --git a/test/prism/fixtures/seattlerb/array_line_breaks.txt b/test/prism/fixtures/seattlerb/array_line_breaks.txt
new file mode 100644
index 0000000000..be9f2d9cb8
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/array_line_breaks.txt
@@ -0,0 +1,4 @@
+[
+'a',
+'b']
+1
diff --git a/test/prism/fixtures/seattlerb/array_lits_trailing_calls.txt b/test/prism/fixtures/seattlerb/array_lits_trailing_calls.txt
new file mode 100644
index 0000000000..868384a407
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/array_lits_trailing_calls.txt
@@ -0,0 +1,3 @@
+%w[].b
+
+[].b
diff --git a/test/prism/fixtures/seattlerb/assoc__bare.txt b/test/prism/fixtures/seattlerb/assoc__bare.txt
new file mode 100644
index 0000000000..96c2940f31
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/assoc__bare.txt
@@ -0,0 +1 @@
+{ y: }
diff --git a/test/prism/fixtures/seattlerb/assoc_label.txt b/test/prism/fixtures/seattlerb/assoc_label.txt
new file mode 100644
index 0000000000..372dc75031
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/assoc_label.txt
@@ -0,0 +1 @@
+a(b:1)
diff --git a/test/prism/fixtures/seattlerb/attr_asgn_colon_id.txt b/test/prism/fixtures/seattlerb/attr_asgn_colon_id.txt
new file mode 100644
index 0000000000..f63c2f5dcb
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/attr_asgn_colon_id.txt
@@ -0,0 +1 @@
+A::b = 1
diff --git a/test/prism/fixtures/seattlerb/attrasgn_array_arg.txt b/test/prism/fixtures/seattlerb/attrasgn_array_arg.txt
new file mode 100644
index 0000000000..db9e2db063
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/attrasgn_array_arg.txt
@@ -0,0 +1 @@
+a[[1, 2]] = 3
diff --git a/test/prism/fixtures/seattlerb/attrasgn_array_lhs.txt b/test/prism/fixtures/seattlerb/attrasgn_array_lhs.txt
new file mode 100644
index 0000000000..0b8e31632d
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/attrasgn_array_lhs.txt
@@ -0,0 +1 @@
+[1, 2, 3, 4][from .. to] = ["a", "b", "c"]
diff --git a/test/prism/fixtures/seattlerb/attrasgn_primary_dot_constant.txt b/test/prism/fixtures/seattlerb/attrasgn_primary_dot_constant.txt
new file mode 100644
index 0000000000..380a718963
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/attrasgn_primary_dot_constant.txt
@@ -0,0 +1 @@
+a.B = 1
diff --git a/test/prism/fixtures/seattlerb/backticks_interpolation_line.txt b/test/prism/fixtures/seattlerb/backticks_interpolation_line.txt
new file mode 100644
index 0000000000..b3ba31c72a
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/backticks_interpolation_line.txt
@@ -0,0 +1 @@
+x `#{y}`
diff --git a/test/prism/fixtures/seattlerb/bang_eq.txt b/test/prism/fixtures/seattlerb/bang_eq.txt
new file mode 100644
index 0000000000..0283460e46
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bang_eq.txt
@@ -0,0 +1 @@
+1 != 2
diff --git a/test/prism/fixtures/seattlerb/bdot2.txt b/test/prism/fixtures/seattlerb/bdot2.txt
new file mode 100644
index 0000000000..4fb2692299
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bdot2.txt
@@ -0,0 +1,3 @@
+..10
+; ..a
+; c
diff --git a/test/prism/fixtures/seattlerb/bdot3.txt b/test/prism/fixtures/seattlerb/bdot3.txt
new file mode 100644
index 0000000000..8079cf9872
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bdot3.txt
@@ -0,0 +1,3 @@
+...10
+; ...a
+; c
diff --git a/test/prism/fixtures/seattlerb/begin_ensure_no_bodies.txt b/test/prism/fixtures/seattlerb/begin_ensure_no_bodies.txt
new file mode 100644
index 0000000000..51dde02ea3
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/begin_ensure_no_bodies.txt
@@ -0,0 +1,3 @@
+begin
+ensure
+end
diff --git a/test/prism/fixtures/seattlerb/begin_rescue_else_ensure_bodies.txt b/test/prism/fixtures/seattlerb/begin_rescue_else_ensure_bodies.txt
new file mode 100644
index 0000000000..ae6e2c3636
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/begin_rescue_else_ensure_bodies.txt
@@ -0,0 +1,9 @@
+begin
+ 1
+rescue
+ 2
+else
+ 3
+ensure
+ 4
+end
diff --git a/test/prism/fixtures/seattlerb/begin_rescue_else_ensure_no_bodies.txt b/test/prism/fixtures/seattlerb/begin_rescue_else_ensure_no_bodies.txt
new file mode 100644
index 0000000000..456d9a5d6e
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/begin_rescue_else_ensure_no_bodies.txt
@@ -0,0 +1,9 @@
+begin
+
+rescue
+
+else
+
+ensure
+
+end
diff --git a/test/prism/fixtures/seattlerb/begin_rescue_ensure_no_bodies.txt b/test/prism/fixtures/seattlerb/begin_rescue_ensure_no_bodies.txt
new file mode 100644
index 0000000000..896e3c030a
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/begin_rescue_ensure_no_bodies.txt
@@ -0,0 +1,4 @@
+begin
+rescue
+ensure
+end
diff --git a/test/prism/fixtures/seattlerb/block_arg__bare.txt b/test/prism/fixtures/seattlerb/block_arg__bare.txt
new file mode 100644
index 0000000000..6454b00345
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_arg__bare.txt
@@ -0,0 +1 @@
+def x(&); end
diff --git a/test/prism/fixtures/seattlerb/block_arg_kwsplat.txt b/test/prism/fixtures/seattlerb/block_arg_kwsplat.txt
new file mode 100644
index 0000000000..a9c255cc66
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_arg_kwsplat.txt
@@ -0,0 +1 @@
+a { |**b| }
diff --git a/test/prism/fixtures/seattlerb/block_arg_opt_arg_block.txt b/test/prism/fixtures/seattlerb/block_arg_opt_arg_block.txt
new file mode 100644
index 0000000000..14cb02c68e
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_arg_opt_arg_block.txt
@@ -0,0 +1 @@
+a { |b, c=1, d, &e| }
diff --git a/test/prism/fixtures/seattlerb/block_arg_opt_splat.txt b/test/prism/fixtures/seattlerb/block_arg_opt_splat.txt
new file mode 100644
index 0000000000..a077ae1f34
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_arg_opt_splat.txt
@@ -0,0 +1 @@
+a { |b, c = 1, *d| }
diff --git a/test/prism/fixtures/seattlerb/block_arg_opt_splat_arg_block_omfg.txt b/test/prism/fixtures/seattlerb/block_arg_opt_splat_arg_block_omfg.txt
new file mode 100644
index 0000000000..5016a7c6b9
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_arg_opt_splat_arg_block_omfg.txt
@@ -0,0 +1 @@
+a { |b, c=1, *d, e, &f| }
diff --git a/test/prism/fixtures/seattlerb/block_arg_optional.txt b/test/prism/fixtures/seattlerb/block_arg_optional.txt
new file mode 100644
index 0000000000..966afc5640
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_arg_optional.txt
@@ -0,0 +1 @@
+a { |b = 1| }
diff --git a/test/prism/fixtures/seattlerb/block_arg_scope.txt b/test/prism/fixtures/seattlerb/block_arg_scope.txt
new file mode 100644
index 0000000000..2362e08559
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_arg_scope.txt
@@ -0,0 +1 @@
+a { |b; c| }
diff --git a/test/prism/fixtures/seattlerb/block_arg_scope2.txt b/test/prism/fixtures/seattlerb/block_arg_scope2.txt
new file mode 100644
index 0000000000..f7222dc7b1
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_arg_scope2.txt
@@ -0,0 +1 @@
+a {|b; c, d| }
diff --git a/test/prism/fixtures/seattlerb/block_arg_splat_arg.txt b/test/prism/fixtures/seattlerb/block_arg_splat_arg.txt
new file mode 100644
index 0000000000..d7c31aae6b
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_arg_splat_arg.txt
@@ -0,0 +1 @@
+a { |b, *c, d| }
diff --git a/test/prism/fixtures/seattlerb/block_args_kwargs.txt b/test/prism/fixtures/seattlerb/block_args_kwargs.txt
new file mode 100644
index 0000000000..467b577a7e
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_args_kwargs.txt
@@ -0,0 +1 @@
+f { |**kwargs| kwargs }
diff --git a/test/prism/fixtures/seattlerb/block_args_no_kwargs.txt b/test/prism/fixtures/seattlerb/block_args_no_kwargs.txt
new file mode 100644
index 0000000000..5c9bfa3a62
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_args_no_kwargs.txt
@@ -0,0 +1 @@
+f { |**nil| }
diff --git a/test/prism/fixtures/seattlerb/block_args_opt1.txt b/test/prism/fixtures/seattlerb/block_args_opt1.txt
new file mode 100644
index 0000000000..372689e36a
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_args_opt1.txt
@@ -0,0 +1 @@
+f { |a, b = 42| [a, b] }
diff --git a/test/prism/fixtures/seattlerb/block_args_opt2.txt b/test/prism/fixtures/seattlerb/block_args_opt2.txt
new file mode 100644
index 0000000000..10d0746646
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_args_opt2.txt
@@ -0,0 +1 @@
+a { | b=1, c=2 | }
diff --git a/test/prism/fixtures/seattlerb/block_args_opt2_2.txt b/test/prism/fixtures/seattlerb/block_args_opt2_2.txt
new file mode 100644
index 0000000000..563a9bf915
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_args_opt2_2.txt
@@ -0,0 +1 @@
+f { |a, b = 42, c = 24| [a, b, c] }
diff --git a/test/prism/fixtures/seattlerb/block_args_opt3.txt b/test/prism/fixtures/seattlerb/block_args_opt3.txt
new file mode 100644
index 0000000000..bb5a7c8458
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_args_opt3.txt
@@ -0,0 +1 @@
+f { |a, b = 42, c = 24, &d| [a, b, c, d] }
diff --git a/test/prism/fixtures/seattlerb/block_call_defn_call_block_call.txt b/test/prism/fixtures/seattlerb/block_call_defn_call_block_call.txt
new file mode 100644
index 0000000000..ff1b3a4c9f
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_call_defn_call_block_call.txt
@@ -0,0 +1,4 @@
+a def b(c)
+ d
+ end
+ e.f do end
diff --git a/test/prism/fixtures/seattlerb/block_call_dot_op2_brace_block.txt b/test/prism/fixtures/seattlerb/block_call_dot_op2_brace_block.txt
new file mode 100644
index 0000000000..63da9ee7af
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_call_dot_op2_brace_block.txt
@@ -0,0 +1 @@
+a.b c() do d end.e do |f| g end
diff --git a/test/prism/fixtures/seattlerb/block_call_dot_op2_cmd_args_do_block.txt b/test/prism/fixtures/seattlerb/block_call_dot_op2_cmd_args_do_block.txt
new file mode 100644
index 0000000000..24e7d0f1f1
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_call_dot_op2_cmd_args_do_block.txt
@@ -0,0 +1 @@
+a.b c() do d end.e f do |g| h end
diff --git a/test/prism/fixtures/seattlerb/block_call_operation_colon.txt b/test/prism/fixtures/seattlerb/block_call_operation_colon.txt
new file mode 100644
index 0000000000..593b9e1bc0
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_call_operation_colon.txt
@@ -0,0 +1 @@
+a.b c do end::d
diff --git a/test/prism/fixtures/seattlerb/block_call_operation_dot.txt b/test/prism/fixtures/seattlerb/block_call_operation_dot.txt
new file mode 100644
index 0000000000..20b8e4fcb8
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_call_operation_dot.txt
@@ -0,0 +1 @@
+a.b c do end.d
diff --git a/test/prism/fixtures/seattlerb/block_call_paren_call_block_call.txt b/test/prism/fixtures/seattlerb/block_call_paren_call_block_call.txt
new file mode 100644
index 0000000000..7f8be95100
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_call_paren_call_block_call.txt
@@ -0,0 +1,2 @@
+a (b)
+c.d do end
diff --git a/test/prism/fixtures/seattlerb/block_command_operation_colon.txt b/test/prism/fixtures/seattlerb/block_command_operation_colon.txt
new file mode 100644
index 0000000000..db221ad496
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_command_operation_colon.txt
@@ -0,0 +1 @@
+a :b do end::c :d
diff --git a/test/prism/fixtures/seattlerb/block_command_operation_dot.txt b/test/prism/fixtures/seattlerb/block_command_operation_dot.txt
new file mode 100644
index 0000000000..56b71677e8
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_command_operation_dot.txt
@@ -0,0 +1 @@
+a :b do end.c :d
diff --git a/test/prism/fixtures/seattlerb/block_decomp_anon_splat_arg.txt b/test/prism/fixtures/seattlerb/block_decomp_anon_splat_arg.txt
new file mode 100644
index 0000000000..96f5d5d2ec
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_decomp_anon_splat_arg.txt
@@ -0,0 +1 @@
+f { |(*, a)| }
diff --git a/test/prism/fixtures/seattlerb/block_decomp_arg_splat.txt b/test/prism/fixtures/seattlerb/block_decomp_arg_splat.txt
new file mode 100644
index 0000000000..f8db3874de
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_decomp_arg_splat.txt
@@ -0,0 +1 @@
+a { |(b, *)| }
diff --git a/test/prism/fixtures/seattlerb/block_decomp_arg_splat_arg.txt b/test/prism/fixtures/seattlerb/block_decomp_arg_splat_arg.txt
new file mode 100644
index 0000000000..e64f4e8c3d
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_decomp_arg_splat_arg.txt
@@ -0,0 +1 @@
+f { |(a, *b, c)| }
diff --git a/test/prism/fixtures/seattlerb/block_decomp_splat.txt b/test/prism/fixtures/seattlerb/block_decomp_splat.txt
new file mode 100644
index 0000000000..970f00a626
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_decomp_splat.txt
@@ -0,0 +1 @@
+f { |(*a)| }
diff --git a/test/prism/fixtures/seattlerb/block_kw.txt b/test/prism/fixtures/seattlerb/block_kw.txt
new file mode 100644
index 0000000000..bacfd32e80
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_kw.txt
@@ -0,0 +1 @@
+blah { |k:42| }
diff --git a/test/prism/fixtures/seattlerb/block_kw__required.txt b/test/prism/fixtures/seattlerb/block_kw__required.txt
new file mode 100644
index 0000000000..b84ab97037
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_kw__required.txt
@@ -0,0 +1 @@
+blah do |k:| end
diff --git a/test/prism/fixtures/seattlerb/block_kwarg_lvar.txt b/test/prism/fixtures/seattlerb/block_kwarg_lvar.txt
new file mode 100644
index 0000000000..390b9195e0
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_kwarg_lvar.txt
@@ -0,0 +1 @@
+bl { |kw: :val| kw }
diff --git a/test/prism/fixtures/seattlerb/block_kwarg_lvar_multiple.txt b/test/prism/fixtures/seattlerb/block_kwarg_lvar_multiple.txt
new file mode 100644
index 0000000000..df3e4afde8
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_kwarg_lvar_multiple.txt
@@ -0,0 +1 @@
+bl { |kw: :val, kw2: :val2 | kw }
diff --git a/test/prism/fixtures/seattlerb/block_opt_arg.txt b/test/prism/fixtures/seattlerb/block_opt_arg.txt
new file mode 100644
index 0000000000..5e312fdf41
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_opt_arg.txt
@@ -0,0 +1 @@
+a { |b=1, c| }
diff --git a/test/prism/fixtures/seattlerb/block_opt_splat.txt b/test/prism/fixtures/seattlerb/block_opt_splat.txt
new file mode 100644
index 0000000000..772a3fc412
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_opt_splat.txt
@@ -0,0 +1 @@
+a { |b = 1, *c| }
diff --git a/test/prism/fixtures/seattlerb/block_opt_splat_arg_block_omfg.txt b/test/prism/fixtures/seattlerb/block_opt_splat_arg_block_omfg.txt
new file mode 100644
index 0000000000..76466f9d54
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_opt_splat_arg_block_omfg.txt
@@ -0,0 +1 @@
+a { |b=1, *c, d, &e| }
diff --git a/test/prism/fixtures/seattlerb/block_optarg.txt b/test/prism/fixtures/seattlerb/block_optarg.txt
new file mode 100644
index 0000000000..a471554da1
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_optarg.txt
@@ -0,0 +1 @@
+a { |b = :c| }
diff --git a/test/prism/fixtures/seattlerb/block_paren_splat.txt b/test/prism/fixtures/seattlerb/block_paren_splat.txt
new file mode 100644
index 0000000000..3dd4bba1ed
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_paren_splat.txt
@@ -0,0 +1 @@
+a { |(b, *c)| }
diff --git a/test/prism/fixtures/seattlerb/block_reg_optarg.txt b/test/prism/fixtures/seattlerb/block_reg_optarg.txt
new file mode 100644
index 0000000000..c024651f78
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_reg_optarg.txt
@@ -0,0 +1 @@
+a { |b, c = :d| }
diff --git a/test/prism/fixtures/seattlerb/block_return.txt b/test/prism/fixtures/seattlerb/block_return.txt
new file mode 100644
index 0000000000..f30ba71d8f
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_return.txt
@@ -0,0 +1 @@
+return foo arg do |bar| end
diff --git a/test/prism/fixtures/seattlerb/block_scope.txt b/test/prism/fixtures/seattlerb/block_scope.txt
new file mode 100644
index 0000000000..7a83d8ab87
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_scope.txt
@@ -0,0 +1 @@
+a { |;b| }
diff --git a/test/prism/fixtures/seattlerb/block_splat_reg.txt b/test/prism/fixtures/seattlerb/block_splat_reg.txt
new file mode 100644
index 0000000000..58f0619e5d
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/block_splat_reg.txt
@@ -0,0 +1 @@
+a { |*b, c| }
diff --git a/test/prism/fixtures/seattlerb/bug169.txt b/test/prism/fixtures/seattlerb/bug169.txt
new file mode 100644
index 0000000000..db2e5ace5e
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug169.txt
@@ -0,0 +1 @@
+m () {}
diff --git a/test/prism/fixtures/seattlerb/bug179.txt b/test/prism/fixtures/seattlerb/bug179.txt
new file mode 100644
index 0000000000..02ae07a3be
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug179.txt
@@ -0,0 +1 @@
+p ()..nil
diff --git a/test/prism/fixtures/seattlerb/bug190.txt b/test/prism/fixtures/seattlerb/bug190.txt
new file mode 100644
index 0000000000..861b2d305f
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug190.txt
@@ -0,0 +1 @@
+%r'\''
diff --git a/test/prism/fixtures/seattlerb/bug191.txt b/test/prism/fixtures/seattlerb/bug191.txt
new file mode 100644
index 0000000000..03f7fd1228
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug191.txt
@@ -0,0 +1,3 @@
+a ? "": b
+
+a ? '': b
diff --git a/test/prism/fixtures/seattlerb/bug202.txt b/test/prism/fixtures/seattlerb/bug202.txt
new file mode 100644
index 0000000000..a3b06ffdfc
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug202.txt
@@ -0,0 +1,2 @@
+$测试 = 1
+测试 = 1
diff --git a/test/prism/fixtures/seattlerb/bug236.txt b/test/prism/fixtures/seattlerb/bug236.txt
new file mode 100644
index 0000000000..cefe1eb058
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug236.txt
@@ -0,0 +1,3 @@
+x{|a,|}
+
+x{|a|}
diff --git a/test/prism/fixtures/seattlerb/bug290.txt b/test/prism/fixtures/seattlerb/bug290.txt
new file mode 100644
index 0000000000..dbcd28cd48
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug290.txt
@@ -0,0 +1,3 @@
+begin
+ foo
+end
diff --git a/test/prism/fixtures/seattlerb/bug_187.txt b/test/prism/fixtures/seattlerb/bug_187.txt
new file mode 100644
index 0000000000..1e1ecd8202
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug_187.txt
@@ -0,0 +1,3 @@
+private def f
+a.b do end
+end
diff --git a/test/prism/fixtures/seattlerb/bug_215.txt b/test/prism/fixtures/seattlerb/bug_215.txt
new file mode 100644
index 0000000000..f0d09ba5ff
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug_215.txt
@@ -0,0 +1 @@
+undef %s(foo)
diff --git a/test/prism/fixtures/seattlerb/bug_249.txt b/test/prism/fixtures/seattlerb/bug_249.txt
new file mode 100644
index 0000000000..ccccdf5326
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug_249.txt
@@ -0,0 +1,4 @@
+mount (Class.new do
+def initialize
+end
+ end).new, :at => 'endpoint'
diff --git a/test/prism/fixtures/seattlerb/bug_and.txt b/test/prism/fixtures/seattlerb/bug_and.txt
new file mode 100644
index 0000000000..6243359a9e
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug_and.txt
@@ -0,0 +1,4 @@
+true and
+true
+
+true and []
diff --git a/test/prism/fixtures/seattlerb/bug_args__19.txt b/test/prism/fixtures/seattlerb/bug_args__19.txt
new file mode 100644
index 0000000000..08466554fd
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug_args__19.txt
@@ -0,0 +1 @@
+f { |(a, b)| d }
diff --git a/test/prism/fixtures/seattlerb/bug_args_masgn.txt b/test/prism/fixtures/seattlerb/bug_args_masgn.txt
new file mode 100644
index 0000000000..e0a71e9197
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug_args_masgn.txt
@@ -0,0 +1 @@
+f { |(a, b), c| }
diff --git a/test/prism/fixtures/seattlerb/bug_args_masgn2.txt b/test/prism/fixtures/seattlerb/bug_args_masgn2.txt
new file mode 100644
index 0000000000..2f12756bfe
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug_args_masgn2.txt
@@ -0,0 +1 @@
+f { |((a, b), c), d| }
diff --git a/test/prism/fixtures/seattlerb/bug_args_masgn_outer_parens__19.txt b/test/prism/fixtures/seattlerb/bug_args_masgn_outer_parens__19.txt
new file mode 100644
index 0000000000..a2b0178676
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug_args_masgn_outer_parens__19.txt
@@ -0,0 +1 @@
+f { |((k, v), i)| }
diff --git a/test/prism/fixtures/seattlerb/bug_call_arglist_parens.txt b/test/prism/fixtures/seattlerb/bug_call_arglist_parens.txt
new file mode 100644
index 0000000000..4f04368802
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug_call_arglist_parens.txt
@@ -0,0 +1,11 @@
+ def f
+ g ( 1), 2
+ end
+
+
+ def f()
+ g (1), 2
+ end
+
+
+g ( 1), 2
diff --git a/test/prism/fixtures/seattlerb/bug_case_when_regexp.txt b/test/prism/fixtures/seattlerb/bug_case_when_regexp.txt
new file mode 100644
index 0000000000..2536696a42
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug_case_when_regexp.txt
@@ -0,0 +1 @@
+case :x; when /x/ then end
diff --git a/test/prism/fixtures/seattlerb/bug_comma.txt b/test/prism/fixtures/seattlerb/bug_comma.txt
new file mode 100644
index 0000000000..d86f1ea9dd
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug_comma.txt
@@ -0,0 +1 @@
+if test ?d, dir then end
diff --git a/test/prism/fixtures/seattlerb/bug_cond_pct.txt b/test/prism/fixtures/seattlerb/bug_cond_pct.txt
new file mode 100644
index 0000000000..1b4f90058e
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug_cond_pct.txt
@@ -0,0 +1 @@
+case; when %r%blahblah%; end
diff --git a/test/prism/fixtures/seattlerb/bug_hash_args.txt b/test/prism/fixtures/seattlerb/bug_hash_args.txt
new file mode 100644
index 0000000000..b815f8a666
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug_hash_args.txt
@@ -0,0 +1 @@
+foo(:bar, baz: nil)
diff --git a/test/prism/fixtures/seattlerb/bug_hash_args_trailing_comma.txt b/test/prism/fixtures/seattlerb/bug_hash_args_trailing_comma.txt
new file mode 100644
index 0000000000..6057b245a5
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug_hash_args_trailing_comma.txt
@@ -0,0 +1 @@
+foo(:bar, baz: nil,)
diff --git a/test/prism/fixtures/seattlerb/bug_hash_interp_array.txt b/test/prism/fixtures/seattlerb/bug_hash_interp_array.txt
new file mode 100644
index 0000000000..01fe238056
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug_hash_interp_array.txt
@@ -0,0 +1 @@
+{ "#{}": [] }
diff --git a/test/prism/fixtures/seattlerb/bug_masgn_right.txt b/test/prism/fixtures/seattlerb/bug_masgn_right.txt
new file mode 100644
index 0000000000..12ea7b8d62
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug_masgn_right.txt
@@ -0,0 +1 @@
+f { |a, (b, c)| }
diff --git a/test/prism/fixtures/seattlerb/bug_not_parens.txt b/test/prism/fixtures/seattlerb/bug_not_parens.txt
new file mode 100644
index 0000000000..8847b7bec6
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug_not_parens.txt
@@ -0,0 +1 @@
+not(a)
diff --git a/test/prism/fixtures/seattlerb/bug_op_asgn_rescue.txt b/test/prism/fixtures/seattlerb/bug_op_asgn_rescue.txt
new file mode 100644
index 0000000000..6a0b25cbdc
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/bug_op_asgn_rescue.txt
@@ -0,0 +1 @@
+a ||= b rescue nil
diff --git a/test/prism/fixtures/seattlerb/call_and.txt b/test/prism/fixtures/seattlerb/call_and.txt
new file mode 100644
index 0000000000..c17be8f356
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_and.txt
@@ -0,0 +1 @@
+1 & 2
diff --git a/test/prism/fixtures/seattlerb/call_arg_assoc.txt b/test/prism/fixtures/seattlerb/call_arg_assoc.txt
new file mode 100644
index 0000000000..376c299be5
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_arg_assoc.txt
@@ -0,0 +1 @@
+f(1, 2=>3)
diff --git a/test/prism/fixtures/seattlerb/call_arg_assoc_kwsplat.txt b/test/prism/fixtures/seattlerb/call_arg_assoc_kwsplat.txt
new file mode 100644
index 0000000000..a4be0fb62c
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_arg_assoc_kwsplat.txt
@@ -0,0 +1 @@
+f(1, kw: 2, **3)
diff --git a/test/prism/fixtures/seattlerb/call_arg_kwsplat.txt b/test/prism/fixtures/seattlerb/call_arg_kwsplat.txt
new file mode 100644
index 0000000000..4848fd9e8d
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_arg_kwsplat.txt
@@ -0,0 +1 @@
+a(b, **1)
diff --git a/test/prism/fixtures/seattlerb/call_args_assoc_quoted.txt b/test/prism/fixtures/seattlerb/call_args_assoc_quoted.txt
new file mode 100644
index 0000000000..0af2259577
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_args_assoc_quoted.txt
@@ -0,0 +1,5 @@
+x "#{k}":42
+
+x "k":42
+
+x 'k':42
diff --git a/test/prism/fixtures/seattlerb/call_args_assoc_trailing_comma.txt b/test/prism/fixtures/seattlerb/call_args_assoc_trailing_comma.txt
new file mode 100644
index 0000000000..6ad08f3238
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_args_assoc_trailing_comma.txt
@@ -0,0 +1 @@
+f(1, 2=>3,)
diff --git a/test/prism/fixtures/seattlerb/call_args_command.txt b/test/prism/fixtures/seattlerb/call_args_command.txt
new file mode 100644
index 0000000000..dd6df29c7b
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_args_command.txt
@@ -0,0 +1 @@
+a.b c.d 1
diff --git a/test/prism/fixtures/seattlerb/call_array_arg.txt b/test/prism/fixtures/seattlerb/call_array_arg.txt
new file mode 100644
index 0000000000..5c724fa328
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_array_arg.txt
@@ -0,0 +1 @@
+1 == [:b, :c]
diff --git a/test/prism/fixtures/seattlerb/call_array_block_call.txt b/test/prism/fixtures/seattlerb/call_array_block_call.txt
new file mode 100644
index 0000000000..6d4c1b276e
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_array_block_call.txt
@@ -0,0 +1 @@
+a [ nil, b do end ]
diff --git a/test/prism/fixtures/seattlerb/call_array_lambda_block_call.txt b/test/prism/fixtures/seattlerb/call_array_lambda_block_call.txt
new file mode 100644
index 0000000000..dc5b5807b2
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_array_lambda_block_call.txt
@@ -0,0 +1,2 @@
+a [->() {}] do
+end
diff --git a/test/prism/fixtures/seattlerb/call_array_lit_inline_hash.txt b/test/prism/fixtures/seattlerb/call_array_lit_inline_hash.txt
new file mode 100644
index 0000000000..daba00947e
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_array_lit_inline_hash.txt
@@ -0,0 +1 @@
+a([:b, :c => 1])
diff --git a/test/prism/fixtures/seattlerb/call_assoc.txt b/test/prism/fixtures/seattlerb/call_assoc.txt
new file mode 100644
index 0000000000..2adc1eee1c
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_assoc.txt
@@ -0,0 +1 @@
+f(2=>3)
diff --git a/test/prism/fixtures/seattlerb/call_assoc_new.txt b/test/prism/fixtures/seattlerb/call_assoc_new.txt
new file mode 100644
index 0000000000..b8457bfdfc
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_assoc_new.txt
@@ -0,0 +1 @@
+f(a:3)
diff --git a/test/prism/fixtures/seattlerb/call_assoc_new_if_multiline.txt b/test/prism/fixtures/seattlerb/call_assoc_new_if_multiline.txt
new file mode 100644
index 0000000000..fe0a37dabb
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_assoc_new_if_multiline.txt
@@ -0,0 +1,5 @@
+a(b: if :c
+1
+else
+2
+end)
diff --git a/test/prism/fixtures/seattlerb/call_assoc_trailing_comma.txt b/test/prism/fixtures/seattlerb/call_assoc_trailing_comma.txt
new file mode 100644
index 0000000000..4d86a4541d
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_assoc_trailing_comma.txt
@@ -0,0 +1 @@
+f(1=>2,)
diff --git a/test/prism/fixtures/seattlerb/call_bang_command_call.txt b/test/prism/fixtures/seattlerb/call_bang_command_call.txt
new file mode 100644
index 0000000000..4f3ba4b93c
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_bang_command_call.txt
@@ -0,0 +1 @@
+! a.b 1
diff --git a/test/prism/fixtures/seattlerb/call_bang_squiggle.txt b/test/prism/fixtures/seattlerb/call_bang_squiggle.txt
new file mode 100644
index 0000000000..d7039f910a
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_bang_squiggle.txt
@@ -0,0 +1 @@
+1 !~ 2
diff --git a/test/prism/fixtures/seattlerb/call_begin_call_block_call.txt b/test/prism/fixtures/seattlerb/call_begin_call_block_call.txt
new file mode 100644
index 0000000000..e9b43491fe
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_begin_call_block_call.txt
@@ -0,0 +1,3 @@
+a begin
+b.c do end
+end
diff --git a/test/prism/fixtures/seattlerb/call_block_arg_named.txt b/test/prism/fixtures/seattlerb/call_block_arg_named.txt
new file mode 100644
index 0000000000..08fea89d11
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_block_arg_named.txt
@@ -0,0 +1 @@
+x(&blk)
diff --git a/test/prism/fixtures/seattlerb/call_carat.txt b/test/prism/fixtures/seattlerb/call_carat.txt
new file mode 100644
index 0000000000..3e88c09837
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_carat.txt
@@ -0,0 +1 @@
+1 ^ 2
diff --git a/test/prism/fixtures/seattlerb/call_colon2.txt b/test/prism/fixtures/seattlerb/call_colon2.txt
new file mode 100644
index 0000000000..47aab7e637
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_colon2.txt
@@ -0,0 +1 @@
+A::b
diff --git a/test/prism/fixtures/seattlerb/call_colon_parens.txt b/test/prism/fixtures/seattlerb/call_colon_parens.txt
new file mode 100644
index 0000000000..51ed4df11b
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_colon_parens.txt
@@ -0,0 +1 @@
+1::()
diff --git a/test/prism/fixtures/seattlerb/call_div.txt b/test/prism/fixtures/seattlerb/call_div.txt
new file mode 100644
index 0000000000..85b4d7b0b0
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_div.txt
@@ -0,0 +1 @@
+1 / 2
diff --git a/test/prism/fixtures/seattlerb/call_dot_parens.txt b/test/prism/fixtures/seattlerb/call_dot_parens.txt
new file mode 100644
index 0000000000..0270596a07
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_dot_parens.txt
@@ -0,0 +1 @@
+1.()
diff --git a/test/prism/fixtures/seattlerb/call_env.txt b/test/prism/fixtures/seattlerb/call_env.txt
new file mode 100644
index 0000000000..dadc0d3861
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_env.txt
@@ -0,0 +1 @@
+a.happy
diff --git a/test/prism/fixtures/seattlerb/call_eq3.txt b/test/prism/fixtures/seattlerb/call_eq3.txt
new file mode 100644
index 0000000000..6a2fb465cb
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_eq3.txt
@@ -0,0 +1 @@
+1 === 2
diff --git a/test/prism/fixtures/seattlerb/call_gt.txt b/test/prism/fixtures/seattlerb/call_gt.txt
new file mode 100644
index 0000000000..9d0a1caa90
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_gt.txt
@@ -0,0 +1 @@
+1 > 2
diff --git a/test/prism/fixtures/seattlerb/call_kwsplat.txt b/test/prism/fixtures/seattlerb/call_kwsplat.txt
new file mode 100644
index 0000000000..eda700c3e1
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_kwsplat.txt
@@ -0,0 +1 @@
+a(**1)
diff --git a/test/prism/fixtures/seattlerb/call_leading_dots.txt b/test/prism/fixtures/seattlerb/call_leading_dots.txt
new file mode 100644
index 0000000000..1e7b2e5179
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_leading_dots.txt
@@ -0,0 +1,3 @@
+a
+.b
+.c
diff --git a/test/prism/fixtures/seattlerb/call_leading_dots_comment.txt b/test/prism/fixtures/seattlerb/call_leading_dots_comment.txt
new file mode 100644
index 0000000000..c5deec5642
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_leading_dots_comment.txt
@@ -0,0 +1,4 @@
+a
+.b
+#.c
+.d
diff --git a/test/prism/fixtures/seattlerb/call_lt.txt b/test/prism/fixtures/seattlerb/call_lt.txt
new file mode 100644
index 0000000000..17e69c79b5
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_lt.txt
@@ -0,0 +1 @@
+1 < 2
diff --git a/test/prism/fixtures/seattlerb/call_lte.txt b/test/prism/fixtures/seattlerb/call_lte.txt
new file mode 100644
index 0000000000..7574027634
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_lte.txt
@@ -0,0 +1 @@
+1 <= 2
diff --git a/test/prism/fixtures/seattlerb/call_not.txt b/test/prism/fixtures/seattlerb/call_not.txt
new file mode 100644
index 0000000000..51e4742f55
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_not.txt
@@ -0,0 +1 @@
+not 42
diff --git a/test/prism/fixtures/seattlerb/call_pipe.txt b/test/prism/fixtures/seattlerb/call_pipe.txt
new file mode 100644
index 0000000000..1555910665
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_pipe.txt
@@ -0,0 +1 @@
+1 | 2
diff --git a/test/prism/fixtures/seattlerb/call_rshift.txt b/test/prism/fixtures/seattlerb/call_rshift.txt
new file mode 100644
index 0000000000..9ff1def4e0
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_rshift.txt
@@ -0,0 +1 @@
+1 >> 2
diff --git a/test/prism/fixtures/seattlerb/call_self_brackets.txt b/test/prism/fixtures/seattlerb/call_self_brackets.txt
new file mode 100644
index 0000000000..6533df2ce0
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_self_brackets.txt
@@ -0,0 +1 @@
+self[1]
diff --git a/test/prism/fixtures/seattlerb/call_spaceship.txt b/test/prism/fixtures/seattlerb/call_spaceship.txt
new file mode 100644
index 0000000000..905c3a1c46
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_spaceship.txt
@@ -0,0 +1 @@
+1 <=> 2
diff --git a/test/prism/fixtures/seattlerb/call_stabby_do_end_with_block.txt b/test/prism/fixtures/seattlerb/call_stabby_do_end_with_block.txt
new file mode 100644
index 0000000000..2d1afdad28
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_stabby_do_end_with_block.txt
@@ -0,0 +1 @@
+a -> do 1 end do 2 end
diff --git a/test/prism/fixtures/seattlerb/call_stabby_with_braces_block.txt b/test/prism/fixtures/seattlerb/call_stabby_with_braces_block.txt
new file mode 100644
index 0000000000..0d995a04d1
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_stabby_with_braces_block.txt
@@ -0,0 +1 @@
+a -> { 1 } do 2 end
diff --git a/test/prism/fixtures/seattlerb/call_star.txt b/test/prism/fixtures/seattlerb/call_star.txt
new file mode 100644
index 0000000000..096ec022d4
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_star.txt
@@ -0,0 +1 @@
+1 * 2
diff --git a/test/prism/fixtures/seattlerb/call_star2.txt b/test/prism/fixtures/seattlerb/call_star2.txt
new file mode 100644
index 0000000000..bef4719d3c
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_star2.txt
@@ -0,0 +1 @@
+1 ** 2
diff --git a/test/prism/fixtures/seattlerb/call_trailing_comma.txt b/test/prism/fixtures/seattlerb/call_trailing_comma.txt
new file mode 100644
index 0000000000..e9a3ca35be
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_trailing_comma.txt
@@ -0,0 +1 @@
+f(1,)
diff --git a/test/prism/fixtures/seattlerb/call_trailing_dots.txt b/test/prism/fixtures/seattlerb/call_trailing_dots.txt
new file mode 100644
index 0000000000..960cdbb45f
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_trailing_dots.txt
@@ -0,0 +1,3 @@
+a.
+b.
+c
diff --git a/test/prism/fixtures/seattlerb/call_unary_bang.txt b/test/prism/fixtures/seattlerb/call_unary_bang.txt
new file mode 100644
index 0000000000..91f702d4a9
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/call_unary_bang.txt
@@ -0,0 +1 @@
+!1
diff --git a/test/prism/fixtures/seattlerb/case_in.txt b/test/prism/fixtures/seattlerb/case_in.txt
new file mode 100644
index 0000000000..0835da0956
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in.txt
@@ -0,0 +1,111 @@
+case :a
+in "b":
+end
+
+case :a
+in %I[a b]
+end
+
+case :a
+in %W[a b]
+end
+
+case :a
+in %i[a b]
+end
+
+case :a
+in %w[a b]
+end
+
+case :a
+in (...10)
+end
+
+case :a
+in (..10)
+end
+
+case :a
+in (1...)
+end
+
+case :a
+in (1...3)
+end
+
+case :a
+in (42)
+end
+
+case :a
+in **nil
+end
+
+case :a
+in /regexp/
+end
+
+case :a
+in :b, *_, :c
+end
+
+case :a
+in :b, [:c]
+end
+
+case :a
+in Symbol()
+end
+
+case :a
+in Symbol(*lhs, x, *rhs)
+end
+
+case :a
+in Symbol[*lhs, x, *rhs]
+end
+
+case :a
+in [->(b) { true }, c]
+end
+
+case :a
+in [:a, b, c, [:d, *e, nil]]
+end
+
+case :a
+in [A, *, B]
+end
+
+case :a
+in [[:b, c], [:d, ^e]]
+end
+
+case :a
+in []
+end
+
+case :a
+in [^(a)]
+end
+
+case :a
+in [^@a, ^$b, ^@@c]
+end
+
+case :a
+in `echo hi`
+end
+
+case :a
+in nil, nil, nil
+end
+
+case :a
+in { "b": }
+end
+
+case :a
+in {}
+end
diff --git a/test/prism/fixtures/seattlerb/case_in_31.txt b/test/prism/fixtures/seattlerb/case_in_31.txt
new file mode 100644
index 0000000000..b9bf25b132
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in_31.txt
@@ -0,0 +1,4 @@
+case :a
+in [:b, *c]
+ :d
+end
diff --git a/test/prism/fixtures/seattlerb/case_in_37.txt b/test/prism/fixtures/seattlerb/case_in_37.txt
new file mode 100644
index 0000000000..25b6fb9261
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in_37.txt
@@ -0,0 +1,4 @@
+case :a
+in { b: [Hash, *] }
+ :c
+end
diff --git a/test/prism/fixtures/seattlerb/case_in_42.txt b/test/prism/fixtures/seattlerb/case_in_42.txt
new file mode 100644
index 0000000000..bc6a2233f5
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in_42.txt
@@ -0,0 +1,3 @@
+case :a
+in :b, *_ then nil
+end
diff --git a/test/prism/fixtures/seattlerb/case_in_42_2.txt b/test/prism/fixtures/seattlerb/case_in_42_2.txt
new file mode 100644
index 0000000000..ce4b65a5d0
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in_42_2.txt
@@ -0,0 +1,3 @@
+case :a
+in A(*list) then nil
+end
diff --git a/test/prism/fixtures/seattlerb/case_in_47.txt b/test/prism/fixtures/seattlerb/case_in_47.txt
new file mode 100644
index 0000000000..60f17ed7ce
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in_47.txt
@@ -0,0 +1,4 @@
+case :a
+in [*, :b, :c]
+ :d
+end
diff --git a/test/prism/fixtures/seattlerb/case_in_67.txt b/test/prism/fixtures/seattlerb/case_in_67.txt
new file mode 100644
index 0000000000..c1c55e68c7
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in_67.txt
@@ -0,0 +1,3 @@
+case :a
+in 1.. then nil
+end
diff --git a/test/prism/fixtures/seattlerb/case_in_86.txt b/test/prism/fixtures/seattlerb/case_in_86.txt
new file mode 100644
index 0000000000..63ba92e533
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in_86.txt
@@ -0,0 +1,3 @@
+case [:a, :b]
+in ::NilClass, * then nil
+end
diff --git a/test/prism/fixtures/seattlerb/case_in_86_2.txt b/test/prism/fixtures/seattlerb/case_in_86_2.txt
new file mode 100644
index 0000000000..4ad16c451a
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in_86_2.txt
@@ -0,0 +1,3 @@
+case [:a, :b]
+in *, ::NilClass then nil
+end
diff --git a/test/prism/fixtures/seattlerb/case_in_array_pat_const.txt b/test/prism/fixtures/seattlerb/case_in_array_pat_const.txt
new file mode 100644
index 0000000000..8551e48e2c
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in_array_pat_const.txt
@@ -0,0 +1,4 @@
+case :a
+in B[c]
+ :d
+end
diff --git a/test/prism/fixtures/seattlerb/case_in_array_pat_const2.txt b/test/prism/fixtures/seattlerb/case_in_array_pat_const2.txt
new file mode 100644
index 0000000000..fca423ea61
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in_array_pat_const2.txt
@@ -0,0 +1,4 @@
+case :a
+in B::C[d]
+ :e
+end
diff --git a/test/prism/fixtures/seattlerb/case_in_array_pat_paren_assign.txt b/test/prism/fixtures/seattlerb/case_in_array_pat_paren_assign.txt
new file mode 100644
index 0000000000..c56f728337
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in_array_pat_paren_assign.txt
@@ -0,0 +1,4 @@
+case :a
+in B(C => d)
+ :d
+end
diff --git a/test/prism/fixtures/seattlerb/case_in_const.txt b/test/prism/fixtures/seattlerb/case_in_const.txt
new file mode 100644
index 0000000000..5b0dcc18e2
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in_const.txt
@@ -0,0 +1,4 @@
+case Array
+in Class
+ :b
+end
diff --git a/test/prism/fixtures/seattlerb/case_in_else.txt b/test/prism/fixtures/seattlerb/case_in_else.txt
new file mode 100644
index 0000000000..6f096862c5
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in_else.txt
@@ -0,0 +1,7 @@
+case Array
+in Class
+ :b
+else
+ :c
+end
+
diff --git a/test/prism/fixtures/seattlerb/case_in_find.txt b/test/prism/fixtures/seattlerb/case_in_find.txt
new file mode 100644
index 0000000000..476fcabe11
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in_find.txt
@@ -0,0 +1,3 @@
+case :a
+ in *a, :+, *b
+end
diff --git a/test/prism/fixtures/seattlerb/case_in_find_array.txt b/test/prism/fixtures/seattlerb/case_in_find_array.txt
new file mode 100644
index 0000000000..5eb4010b7f
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in_find_array.txt
@@ -0,0 +1,3 @@
+case :a
+in [*, :b, c, *]
+end
diff --git a/test/prism/fixtures/seattlerb/case_in_hash_pat.txt b/test/prism/fixtures/seattlerb/case_in_hash_pat.txt
new file mode 100644
index 0000000000..cb012e8d99
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in_hash_pat.txt
@@ -0,0 +1,5 @@
+case :a
+in { b: 'c', d: "e" } then
+ :f
+end
+
diff --git a/test/prism/fixtures/seattlerb/case_in_hash_pat_assign.txt b/test/prism/fixtures/seattlerb/case_in_hash_pat_assign.txt
new file mode 100644
index 0000000000..58fd59ff9c
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in_hash_pat_assign.txt
@@ -0,0 +1,4 @@
+case :a
+in { b: Integer => x, d: "e", f: } then
+ :g
+end
diff --git a/test/prism/fixtures/seattlerb/case_in_hash_pat_paren_assign.txt b/test/prism/fixtures/seattlerb/case_in_hash_pat_paren_assign.txt
new file mode 100644
index 0000000000..de3a10740c
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in_hash_pat_paren_assign.txt
@@ -0,0 +1,4 @@
+case :a
+in B(a: 42)
+ :d
+end
diff --git a/test/prism/fixtures/seattlerb/case_in_hash_pat_paren_true.txt b/test/prism/fixtures/seattlerb/case_in_hash_pat_paren_true.txt
new file mode 100644
index 0000000000..449fd0d4d4
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in_hash_pat_paren_true.txt
@@ -0,0 +1,5 @@
+case :a
+in b: true then
+ :c
+end
+
diff --git a/test/prism/fixtures/seattlerb/case_in_hash_pat_rest.txt b/test/prism/fixtures/seattlerb/case_in_hash_pat_rest.txt
new file mode 100644
index 0000000000..6f67cb1d10
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in_hash_pat_rest.txt
@@ -0,0 +1,3 @@
+case :a
+in b: c, **rest then :d
+end
diff --git a/test/prism/fixtures/seattlerb/case_in_hash_pat_rest_solo.txt b/test/prism/fixtures/seattlerb/case_in_hash_pat_rest_solo.txt
new file mode 100644
index 0000000000..91d0592412
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in_hash_pat_rest_solo.txt
@@ -0,0 +1,3 @@
+case :a
+in **rest then :d
+end
diff --git a/test/prism/fixtures/seattlerb/case_in_if_unless_post_mod.txt b/test/prism/fixtures/seattlerb/case_in_if_unless_post_mod.txt
new file mode 100644
index 0000000000..dbe24a5c8a
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in_if_unless_post_mod.txt
@@ -0,0 +1,6 @@
+case :a
+in A if true
+ :C
+in D unless false
+ :E
+end
diff --git a/test/prism/fixtures/seattlerb/case_in_multiple.txt b/test/prism/fixtures/seattlerb/case_in_multiple.txt
new file mode 100644
index 0000000000..1b6dd06cfe
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in_multiple.txt
@@ -0,0 +1,6 @@
+case :a
+in A::B
+ :C
+in D::E
+ :F
+end
diff --git a/test/prism/fixtures/seattlerb/case_in_or.txt b/test/prism/fixtures/seattlerb/case_in_or.txt
new file mode 100644
index 0000000000..875e37749f
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/case_in_or.txt
@@ -0,0 +1,5 @@
+case :a
+in B | C
+ :d
+end
+
diff --git a/test/prism/fixtures/seattlerb/class_comments.txt b/test/prism/fixtures/seattlerb/class_comments.txt
new file mode 100644
index 0000000000..9701eca7e5
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/class_comments.txt
@@ -0,0 +1,9 @@
+# blah 1
+# blah 2
+
+class X
+ # blah 3
+ def blah
+ # blah 4
+ end
+end
diff --git a/test/prism/fixtures/seattlerb/cond_unary_minus.txt b/test/prism/fixtures/seattlerb/cond_unary_minus.txt
new file mode 100644
index 0000000000..80293115da
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/cond_unary_minus.txt
@@ -0,0 +1 @@
+if -1; end
diff --git a/test/prism/fixtures/seattlerb/const_2_op_asgn_or2.txt b/test/prism/fixtures/seattlerb/const_2_op_asgn_or2.txt
new file mode 100644
index 0000000000..6912c2d76b
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/const_2_op_asgn_or2.txt
@@ -0,0 +1 @@
+::X::Y ||= 1
diff --git a/test/prism/fixtures/seattlerb/const_3_op_asgn_or.txt b/test/prism/fixtures/seattlerb/const_3_op_asgn_or.txt
new file mode 100644
index 0000000000..bbcd25a369
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/const_3_op_asgn_or.txt
@@ -0,0 +1 @@
+::X ||= 1
diff --git a/test/prism/fixtures/seattlerb/const_op_asgn_and1.txt b/test/prism/fixtures/seattlerb/const_op_asgn_and1.txt
new file mode 100644
index 0000000000..3964df0ead
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/const_op_asgn_and1.txt
@@ -0,0 +1 @@
+::X &= 1
diff --git a/test/prism/fixtures/seattlerb/const_op_asgn_and2.txt b/test/prism/fixtures/seattlerb/const_op_asgn_and2.txt
new file mode 100644
index 0000000000..1bef4b4154
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/const_op_asgn_and2.txt
@@ -0,0 +1 @@
+::X &&= 1
diff --git a/test/prism/fixtures/seattlerb/const_op_asgn_or.txt b/test/prism/fixtures/seattlerb/const_op_asgn_or.txt
new file mode 100644
index 0000000000..729e425262
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/const_op_asgn_or.txt
@@ -0,0 +1 @@
+X::Y ||= 1
diff --git a/test/prism/fixtures/seattlerb/defined_eh_parens.txt b/test/prism/fixtures/seattlerb/defined_eh_parens.txt
new file mode 100644
index 0000000000..5ca5d9f4c4
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defined_eh_parens.txt
@@ -0,0 +1 @@
+defined?(42)
diff --git a/test/prism/fixtures/seattlerb/defn_arg_asplat_arg.txt b/test/prism/fixtures/seattlerb/defn_arg_asplat_arg.txt
new file mode 100644
index 0000000000..f629a5de60
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_arg_asplat_arg.txt
@@ -0,0 +1 @@
+def call(interp, *, args) end
diff --git a/test/prism/fixtures/seattlerb/defn_arg_forward_args.txt b/test/prism/fixtures/seattlerb/defn_arg_forward_args.txt
new file mode 100644
index 0000000000..500e2e1fe0
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_arg_forward_args.txt
@@ -0,0 +1 @@
+def a(x, ...); b(x, ...); end
diff --git a/test/prism/fixtures/seattlerb/defn_args_forward_args.txt b/test/prism/fixtures/seattlerb/defn_args_forward_args.txt
new file mode 100644
index 0000000000..fc1ee138de
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_args_forward_args.txt
@@ -0,0 +1 @@
+def a(x, y, z, ...); b(:get, z, ...); end
diff --git a/test/prism/fixtures/seattlerb/defn_comments.txt b/test/prism/fixtures/seattlerb/defn_comments.txt
new file mode 100644
index 0000000000..04c7ea1a10
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_comments.txt
@@ -0,0 +1,5 @@
+# blah 1
+# blah 2
+
+def blah
+end
diff --git a/test/prism/fixtures/seattlerb/defn_endless_command.txt b/test/prism/fixtures/seattlerb/defn_endless_command.txt
new file mode 100644
index 0000000000..172de2ca6c
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_endless_command.txt
@@ -0,0 +1 @@
+def some_method = other_method 42
diff --git a/test/prism/fixtures/seattlerb/defn_endless_command_rescue.txt b/test/prism/fixtures/seattlerb/defn_endless_command_rescue.txt
new file mode 100644
index 0000000000..05ed392e38
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_endless_command_rescue.txt
@@ -0,0 +1 @@
+def some_method = other_method 42 rescue 24
diff --git a/test/prism/fixtures/seattlerb/defn_forward_args.txt b/test/prism/fixtures/seattlerb/defn_forward_args.txt
new file mode 100644
index 0000000000..46ed199875
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_forward_args.txt
@@ -0,0 +1 @@
+def a(...); b(...); end
diff --git a/test/prism/fixtures/seattlerb/defn_forward_args__no_parens.txt b/test/prism/fixtures/seattlerb/defn_forward_args__no_parens.txt
new file mode 100644
index 0000000000..2d34077c93
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_forward_args__no_parens.txt
@@ -0,0 +1,3 @@
+def f ...
+ m(...)
+end
diff --git a/test/prism/fixtures/seattlerb/defn_kwarg_env.txt b/test/prism/fixtures/seattlerb/defn_kwarg_env.txt
new file mode 100644
index 0000000000..b512677195
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_kwarg_env.txt
@@ -0,0 +1 @@
+def test(**testing) test_splat(**testing) end
diff --git a/test/prism/fixtures/seattlerb/defn_kwarg_kwarg.txt b/test/prism/fixtures/seattlerb/defn_kwarg_kwarg.txt
new file mode 100644
index 0000000000..3962d2645c
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_kwarg_kwarg.txt
@@ -0,0 +1 @@
+def f(a, b: 1, c: 2) end
diff --git a/test/prism/fixtures/seattlerb/defn_kwarg_kwsplat.txt b/test/prism/fixtures/seattlerb/defn_kwarg_kwsplat.txt
new file mode 100644
index 0000000000..bd39819482
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_kwarg_kwsplat.txt
@@ -0,0 +1 @@
+def a(b: 1, **c) end
diff --git a/test/prism/fixtures/seattlerb/defn_kwarg_kwsplat_anon.txt b/test/prism/fixtures/seattlerb/defn_kwarg_kwsplat_anon.txt
new file mode 100644
index 0000000000..aba71cba07
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_kwarg_kwsplat_anon.txt
@@ -0,0 +1 @@
+def a(b: 1, **) end
diff --git a/test/prism/fixtures/seattlerb/defn_kwarg_lvar.txt b/test/prism/fixtures/seattlerb/defn_kwarg_lvar.txt
new file mode 100644
index 0000000000..9eac108cca
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_kwarg_lvar.txt
@@ -0,0 +1 @@
+def fun(kw: :val); kw; end
diff --git a/test/prism/fixtures/seattlerb/defn_kwarg_no_parens.txt b/test/prism/fixtures/seattlerb/defn_kwarg_no_parens.txt
new file mode 100644
index 0000000000..481457bf0e
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_kwarg_no_parens.txt
@@ -0,0 +1,2 @@
+def f a: 1
+end
diff --git a/test/prism/fixtures/seattlerb/defn_kwarg_val.txt b/test/prism/fixtures/seattlerb/defn_kwarg_val.txt
new file mode 100644
index 0000000000..1a2803926f
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_kwarg_val.txt
@@ -0,0 +1 @@
+def f(a, b:1) end
diff --git a/test/prism/fixtures/seattlerb/defn_no_kwargs.txt b/test/prism/fixtures/seattlerb/defn_no_kwargs.txt
new file mode 100644
index 0000000000..857ec8debb
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_no_kwargs.txt
@@ -0,0 +1 @@
+def x(**nil); end
diff --git a/test/prism/fixtures/seattlerb/defn_oneliner.txt b/test/prism/fixtures/seattlerb/defn_oneliner.txt
new file mode 100644
index 0000000000..4aef08ce46
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_oneliner.txt
@@ -0,0 +1 @@
+def exec(cmd) = system(cmd)
diff --git a/test/prism/fixtures/seattlerb/defn_oneliner_eq2.txt b/test/prism/fixtures/seattlerb/defn_oneliner_eq2.txt
new file mode 100644
index 0000000000..1b1ce27a15
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_oneliner_eq2.txt
@@ -0,0 +1,3 @@
+class X
+ def ==(o) = 42
+end
diff --git a/test/prism/fixtures/seattlerb/defn_oneliner_noargs.txt b/test/prism/fixtures/seattlerb/defn_oneliner_noargs.txt
new file mode 100644
index 0000000000..cb4f76d244
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_oneliner_noargs.txt
@@ -0,0 +1 @@
+def exec = system
diff --git a/test/prism/fixtures/seattlerb/defn_oneliner_noargs_parentheses.txt b/test/prism/fixtures/seattlerb/defn_oneliner_noargs_parentheses.txt
new file mode 100644
index 0000000000..c582e896c1
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_oneliner_noargs_parentheses.txt
@@ -0,0 +1 @@
+def exec() = system
diff --git a/test/prism/fixtures/seattlerb/defn_oneliner_rescue.txt b/test/prism/fixtures/seattlerb/defn_oneliner_rescue.txt
new file mode 100644
index 0000000000..ffe2228c9d
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_oneliner_rescue.txt
@@ -0,0 +1,13 @@
+def exec(cmd)
+ system(cmd)
+rescue
+ nil
+end
+
+
+def exec(cmd)
+ system(cmd) rescue nil
+end
+
+
+def exec(cmd) = system(cmd) rescue nil
diff --git a/test/prism/fixtures/seattlerb/defn_opt_last_arg.txt b/test/prism/fixtures/seattlerb/defn_opt_last_arg.txt
new file mode 100644
index 0000000000..91500bf137
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_opt_last_arg.txt
@@ -0,0 +1,2 @@
+def m arg = false
+end
diff --git a/test/prism/fixtures/seattlerb/defn_opt_reg.txt b/test/prism/fixtures/seattlerb/defn_opt_reg.txt
new file mode 100644
index 0000000000..c665674bc4
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_opt_reg.txt
@@ -0,0 +1 @@
+def f(a=nil, b) end
diff --git a/test/prism/fixtures/seattlerb/defn_opt_splat_arg.txt b/test/prism/fixtures/seattlerb/defn_opt_splat_arg.txt
new file mode 100644
index 0000000000..876398b478
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_opt_splat_arg.txt
@@ -0,0 +1 @@
+def f (a = 1, *b, c) end
diff --git a/test/prism/fixtures/seattlerb/defn_powarg.txt b/test/prism/fixtures/seattlerb/defn_powarg.txt
new file mode 100644
index 0000000000..73415f0db9
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_powarg.txt
@@ -0,0 +1 @@
+def f(**opts) end
diff --git a/test/prism/fixtures/seattlerb/defn_reg_opt_reg.txt b/test/prism/fixtures/seattlerb/defn_reg_opt_reg.txt
new file mode 100644
index 0000000000..69f501a38e
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_reg_opt_reg.txt
@@ -0,0 +1 @@
+def f(a, b = :c, d) end
diff --git a/test/prism/fixtures/seattlerb/defn_splat_arg.txt b/test/prism/fixtures/seattlerb/defn_splat_arg.txt
new file mode 100644
index 0000000000..a2a84bed30
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_splat_arg.txt
@@ -0,0 +1 @@
+def f(*, a) end
diff --git a/test/prism/fixtures/seattlerb/defn_unary_not.txt b/test/prism/fixtures/seattlerb/defn_unary_not.txt
new file mode 100644
index 0000000000..fb83c84a13
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defn_unary_not.txt
@@ -0,0 +1 @@
+def !@; true; end
diff --git a/test/prism/fixtures/seattlerb/defns_reserved.txt b/test/prism/fixtures/seattlerb/defns_reserved.txt
new file mode 100644
index 0000000000..7de9322f0d
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defns_reserved.txt
@@ -0,0 +1 @@
+def self.return; end
diff --git a/test/prism/fixtures/seattlerb/defs_as_arg_with_do_block_inside.txt b/test/prism/fixtures/seattlerb/defs_as_arg_with_do_block_inside.txt
new file mode 100644
index 0000000000..4d493d73dd
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defs_as_arg_with_do_block_inside.txt
@@ -0,0 +1 @@
+p def self.b; x.y do; end; end
diff --git a/test/prism/fixtures/seattlerb/defs_comments.txt b/test/prism/fixtures/seattlerb/defs_comments.txt
new file mode 100644
index 0000000000..52b9b4a6b3
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defs_comments.txt
@@ -0,0 +1,5 @@
+# blah 1
+# blah 2
+
+def self.blah
+end
diff --git a/test/prism/fixtures/seattlerb/defs_endless_command.txt b/test/prism/fixtures/seattlerb/defs_endless_command.txt
new file mode 100644
index 0000000000..3b605657de
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defs_endless_command.txt
@@ -0,0 +1 @@
+def x.some_method = other_method 42
diff --git a/test/prism/fixtures/seattlerb/defs_endless_command_rescue.txt b/test/prism/fixtures/seattlerb/defs_endless_command_rescue.txt
new file mode 100644
index 0000000000..6ece366db0
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defs_endless_command_rescue.txt
@@ -0,0 +1 @@
+def x.some_method = other_method 42 rescue 24
diff --git a/test/prism/fixtures/seattlerb/defs_kwarg.txt b/test/prism/fixtures/seattlerb/defs_kwarg.txt
new file mode 100644
index 0000000000..59970a371e
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defs_kwarg.txt
@@ -0,0 +1,2 @@
+def self.a b: 1
+end
diff --git a/test/prism/fixtures/seattlerb/defs_oneliner.txt b/test/prism/fixtures/seattlerb/defs_oneliner.txt
new file mode 100644
index 0000000000..1867edcfbf
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defs_oneliner.txt
@@ -0,0 +1 @@
+def self.exec(cmd) = system(cmd)
diff --git a/test/prism/fixtures/seattlerb/defs_oneliner_eq2.txt b/test/prism/fixtures/seattlerb/defs_oneliner_eq2.txt
new file mode 100644
index 0000000000..1e55f68bf3
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defs_oneliner_eq2.txt
@@ -0,0 +1,3 @@
+class X
+ def self.==(o) = 42
+end
diff --git a/test/prism/fixtures/seattlerb/defs_oneliner_rescue.txt b/test/prism/fixtures/seattlerb/defs_oneliner_rescue.txt
new file mode 100644
index 0000000000..7a04012b8f
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/defs_oneliner_rescue.txt
@@ -0,0 +1,13 @@
+def self.exec(cmd)
+ system(cmd)
+rescue
+ nil
+end
+
+
+def self.exec(cmd)
+ system(cmd) rescue nil
+end
+
+
+def self.exec(cmd) = system(cmd) rescue nil
diff --git a/test/prism/fixtures/seattlerb/difficult0_.txt b/test/prism/fixtures/seattlerb/difficult0_.txt
new file mode 100644
index 0000000000..5c73907cae
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/difficult0_.txt
@@ -0,0 +1,4 @@
+p <<-END+'b
+ a
+ END
+ c'+'d'
diff --git a/test/prism/fixtures/seattlerb/difficult1_line_numbers.txt b/test/prism/fixtures/seattlerb/difficult1_line_numbers.txt
new file mode 100644
index 0000000000..8008127dc9
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/difficult1_line_numbers.txt
@@ -0,0 +1,13 @@
+if true
+ p 1
+ a.b 2
+ c.d 3, 4
+ e.f 5
+ g.h 6, 7
+ p(1)
+ a.b(2)
+ c.d(3, 4)
+ e.f(5)
+ g.h(6, 7)
+end
+
diff --git a/test/prism/fixtures/seattlerb/difficult1_line_numbers2.txt b/test/prism/fixtures/seattlerb/difficult1_line_numbers2.txt
new file mode 100644
index 0000000000..1964562416
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/difficult1_line_numbers2.txt
@@ -0,0 +1,8 @@
+if true then
+ p("a")
+ b = 1
+ p b
+ c =1
+end
+a
+
diff --git a/test/prism/fixtures/seattlerb/difficult2_.txt b/test/prism/fixtures/seattlerb/difficult2_.txt
new file mode 100644
index 0000000000..3259097492
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/difficult2_.txt
@@ -0,0 +1,2 @@
+1 ? b('') : 2
+a d: 3
diff --git a/test/prism/fixtures/seattlerb/difficult3_.txt b/test/prism/fixtures/seattlerb/difficult3_.txt
new file mode 100644
index 0000000000..9f95860b82
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/difficult3_.txt
@@ -0,0 +1 @@
+f { |a, (b, *c)| }
diff --git a/test/prism/fixtures/seattlerb/difficult3_2.txt b/test/prism/fixtures/seattlerb/difficult3_2.txt
new file mode 100644
index 0000000000..8abfe3f634
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/difficult3_2.txt
@@ -0,0 +1 @@
+f { |*a, b| }
diff --git a/test/prism/fixtures/seattlerb/difficult3_3.txt b/test/prism/fixtures/seattlerb/difficult3_3.txt
new file mode 100644
index 0000000000..6f43ab7b1d
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/difficult3_3.txt
@@ -0,0 +1 @@
+f { |*a, b, &c| }
diff --git a/test/prism/fixtures/seattlerb/difficult3_4.txt b/test/prism/fixtures/seattlerb/difficult3_4.txt
new file mode 100644
index 0000000000..7070e1e964
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/difficult3_4.txt
@@ -0,0 +1 @@
+a=b ? true: false
diff --git a/test/prism/fixtures/seattlerb/difficult3_5.txt b/test/prism/fixtures/seattlerb/difficult3_5.txt
new file mode 100644
index 0000000000..6d52692481
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/difficult3_5.txt
@@ -0,0 +1 @@
+f ->() { g do end }
diff --git a/test/prism/fixtures/seattlerb/difficult3__10.txt b/test/prism/fixtures/seattlerb/difficult3__10.txt
new file mode 100644
index 0000000000..89974f5114
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/difficult3__10.txt
@@ -0,0 +1 @@
+f { |a, (*b, c)| }
diff --git a/test/prism/fixtures/seattlerb/difficult3__11.txt b/test/prism/fixtures/seattlerb/difficult3__11.txt
new file mode 100644
index 0000000000..911d037961
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/difficult3__11.txt
@@ -0,0 +1 @@
+f { |a, (*)| }
diff --git a/test/prism/fixtures/seattlerb/difficult3__12.txt b/test/prism/fixtures/seattlerb/difficult3__12.txt
new file mode 100644
index 0000000000..2405a80ec1
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/difficult3__12.txt
@@ -0,0 +1 @@
+f { |a, (*, b)| }
diff --git a/test/prism/fixtures/seattlerb/difficult3__6.txt b/test/prism/fixtures/seattlerb/difficult3__6.txt
new file mode 100644
index 0000000000..3a45ae86fb
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/difficult3__6.txt
@@ -0,0 +1 @@
+f { |a, (b, *c, d)| }
diff --git a/test/prism/fixtures/seattlerb/difficult3__7.txt b/test/prism/fixtures/seattlerb/difficult3__7.txt
new file mode 100644
index 0000000000..55272a1fc4
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/difficult3__7.txt
@@ -0,0 +1 @@
+f { |a, (b, *)| }
diff --git a/test/prism/fixtures/seattlerb/difficult3__8.txt b/test/prism/fixtures/seattlerb/difficult3__8.txt
new file mode 100644
index 0000000000..76740db4ff
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/difficult3__8.txt
@@ -0,0 +1 @@
+f { |a, (b, *, c)| }
diff --git a/test/prism/fixtures/seattlerb/difficult3__9.txt b/test/prism/fixtures/seattlerb/difficult3__9.txt
new file mode 100644
index 0000000000..b65f7fd052
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/difficult3__9.txt
@@ -0,0 +1 @@
+f { |a, (*b)| }
diff --git a/test/prism/fixtures/seattlerb/difficult4__leading_dots.txt b/test/prism/fixtures/seattlerb/difficult4__leading_dots.txt
new file mode 100644
index 0000000000..332dc8225c
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/difficult4__leading_dots.txt
@@ -0,0 +1,2 @@
+a
+.b
diff --git a/test/prism/fixtures/seattlerb/difficult4__leading_dots2.txt b/test/prism/fixtures/seattlerb/difficult4__leading_dots2.txt
new file mode 100644
index 0000000000..fe73f641fe
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/difficult4__leading_dots2.txt
@@ -0,0 +1,2 @@
+1
+..3
diff --git a/test/prism/fixtures/seattlerb/difficult6_.txt b/test/prism/fixtures/seattlerb/difficult6_.txt
new file mode 100644
index 0000000000..7396a9a76f
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/difficult6_.txt
@@ -0,0 +1 @@
+->(a, b=nil) { p [a, b] }
diff --git a/test/prism/fixtures/seattlerb/difficult6__7.txt b/test/prism/fixtures/seattlerb/difficult6__7.txt
new file mode 100644
index 0000000000..048358bbdc
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/difficult6__7.txt
@@ -0,0 +1 @@
+a.b (1) {c}
diff --git a/test/prism/fixtures/seattlerb/difficult6__8.txt b/test/prism/fixtures/seattlerb/difficult6__8.txt
new file mode 100644
index 0000000000..ba1cbc235d
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/difficult6__8.txt
@@ -0,0 +1 @@
+a::b (1) {c}
diff --git a/test/prism/fixtures/seattlerb/difficult7_.txt b/test/prism/fixtures/seattlerb/difficult7_.txt
new file mode 100644
index 0000000000..112b75c5f2
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/difficult7_.txt
@@ -0,0 +1,5 @@
+ {
+ a: lambda { b ? c() : d },
+ e: nil,
+ }
+
diff --git a/test/prism/fixtures/seattlerb/do_bug.txt b/test/prism/fixtures/seattlerb/do_bug.txt
new file mode 100644
index 0000000000..a274e72baf
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/do_bug.txt
@@ -0,0 +1,4 @@
+a 1
+a.b do |c|
+ # do nothing
+end
diff --git a/test/prism/fixtures/seattlerb/do_lambda.txt b/test/prism/fixtures/seattlerb/do_lambda.txt
new file mode 100644
index 0000000000..06d2a38d30
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/do_lambda.txt
@@ -0,0 +1 @@
+->() do end
diff --git a/test/prism/fixtures/seattlerb/dot2_nil__26.txt b/test/prism/fixtures/seattlerb/dot2_nil__26.txt
new file mode 100644
index 0000000000..cc070eb69f
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/dot2_nil__26.txt
@@ -0,0 +1 @@
+a..
diff --git a/test/prism/fixtures/seattlerb/dot3_nil__26.txt b/test/prism/fixtures/seattlerb/dot3_nil__26.txt
new file mode 100644
index 0000000000..7f4aef7af7
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/dot3_nil__26.txt
@@ -0,0 +1 @@
+a...
diff --git a/test/prism/fixtures/seattlerb/dstr_evstr.txt b/test/prism/fixtures/seattlerb/dstr_evstr.txt
new file mode 100644
index 0000000000..5fe4a858c1
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/dstr_evstr.txt
@@ -0,0 +1 @@
+"#{'a'}#{b}"
diff --git a/test/prism/fixtures/seattlerb/dstr_evstr_empty_end.txt b/test/prism/fixtures/seattlerb/dstr_evstr_empty_end.txt
new file mode 100644
index 0000000000..7a55030fa8
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/dstr_evstr_empty_end.txt
@@ -0,0 +1 @@
+:"#{field}"
diff --git a/test/prism/fixtures/seattlerb/dstr_lex_state.txt b/test/prism/fixtures/seattlerb/dstr_lex_state.txt
new file mode 100644
index 0000000000..6cac1d8e95
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/dstr_lex_state.txt
@@ -0,0 +1 @@
+"#{p:a}"
diff --git a/test/prism/fixtures/seattlerb/dstr_str.txt b/test/prism/fixtures/seattlerb/dstr_str.txt
new file mode 100644
index 0000000000..226ce2b191
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/dstr_str.txt
@@ -0,0 +1 @@
+"#{'a'} b"
diff --git a/test/prism/fixtures/seattlerb/dsym_esc_to_sym.txt b/test/prism/fixtures/seattlerb/dsym_esc_to_sym.txt
new file mode 100644
index 0000000000..e5781453c1
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/dsym_esc_to_sym.txt
@@ -0,0 +1 @@
+:"Variet\303\240"
diff --git a/test/prism/fixtures/seattlerb/dsym_to_sym.txt b/test/prism/fixtures/seattlerb/dsym_to_sym.txt
new file mode 100644
index 0000000000..813c90342c
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/dsym_to_sym.txt
@@ -0,0 +1,3 @@
+alias :"<<" :">>"
+
+alias :<< :>>
diff --git a/test/prism/fixtures/seattlerb/eq_begin_line_numbers.txt b/test/prism/fixtures/seattlerb/eq_begin_line_numbers.txt
new file mode 100644
index 0000000000..aae82e1207
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/eq_begin_line_numbers.txt
@@ -0,0 +1,6 @@
+1
+=begin
+comment
+comment
+=end
+2
diff --git a/test/prism/fixtures/seattlerb/eq_begin_why_wont_people_use_their_spacebar.txt b/test/prism/fixtures/seattlerb/eq_begin_why_wont_people_use_their_spacebar.txt
new file mode 100644
index 0000000000..88ff599e91
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/eq_begin_why_wont_people_use_their_spacebar.txt
@@ -0,0 +1,3 @@
+h[k]=begin
+ 42
+ end
diff --git a/test/prism/fixtures/seattlerb/evstr_evstr.txt b/test/prism/fixtures/seattlerb/evstr_evstr.txt
new file mode 100644
index 0000000000..cf0b5ee873
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/evstr_evstr.txt
@@ -0,0 +1 @@
+"#{a}#{b}"
diff --git a/test/prism/fixtures/seattlerb/evstr_str.txt b/test/prism/fixtures/seattlerb/evstr_str.txt
new file mode 100644
index 0000000000..5746909b19
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/evstr_str.txt
@@ -0,0 +1 @@
+"#{a} b"
diff --git a/test/prism/fixtures/seattlerb/expr_not_bang.txt b/test/prism/fixtures/seattlerb/expr_not_bang.txt
new file mode 100644
index 0000000000..6ed80c76d3
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/expr_not_bang.txt
@@ -0,0 +1 @@
+! a b
diff --git a/test/prism/fixtures/seattlerb/f_kw.txt b/test/prism/fixtures/seattlerb/f_kw.txt
new file mode 100644
index 0000000000..4dd42662b8
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/f_kw.txt
@@ -0,0 +1 @@
+def x k:42; end
diff --git a/test/prism/fixtures/seattlerb/f_kw__required.txt b/test/prism/fixtures/seattlerb/f_kw__required.txt
new file mode 100644
index 0000000000..2e1e258ff0
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/f_kw__required.txt
@@ -0,0 +1 @@
+def x k:; end
diff --git a/test/prism/fixtures/seattlerb/flip2_env_lvar.txt b/test/prism/fixtures/seattlerb/flip2_env_lvar.txt
new file mode 100644
index 0000000000..619b2c915e
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/flip2_env_lvar.txt
@@ -0,0 +1 @@
+if a..b then end
diff --git a/test/prism/fixtures/seattlerb/float_with_if_modifier.txt b/test/prism/fixtures/seattlerb/float_with_if_modifier.txt
new file mode 100644
index 0000000000..6a62d4a308
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/float_with_if_modifier.txt
@@ -0,0 +1 @@
+1.0if true
diff --git a/test/prism/fixtures/seattlerb/heredoc__backslash_dos_format.txt b/test/prism/fixtures/seattlerb/heredoc__backslash_dos_format.txt
new file mode 100644
index 0000000000..cfbcb2a11d
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc__backslash_dos_format.txt
@@ -0,0 +1,5 @@
+str = <<-XXX
+before\
+after
+XXX
+
diff --git a/test/prism/fixtures/seattlerb/heredoc_backslash_nl.txt b/test/prism/fixtures/seattlerb/heredoc_backslash_nl.txt
new file mode 100644
index 0000000000..0cc5b35fd5
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_backslash_nl.txt
@@ -0,0 +1,8 @@
+" why would someone do this? \
+ blah
+"
+
+<<-DESC
+ why would someone do this? \
+ blah
+DESC
diff --git a/test/prism/fixtures/seattlerb/heredoc_bad_hex_escape.txt b/test/prism/fixtures/seattlerb/heredoc_bad_hex_escape.txt
new file mode 100644
index 0000000000..2c386cc6a9
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_bad_hex_escape.txt
@@ -0,0 +1,3 @@
+s = <<eos
+a\xE9b
+eos
diff --git a/test/prism/fixtures/seattlerb/heredoc_bad_oct_escape.txt b/test/prism/fixtures/seattlerb/heredoc_bad_oct_escape.txt
new file mode 100644
index 0000000000..235a041e90
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_bad_oct_escape.txt
@@ -0,0 +1,5 @@
+s = <<-EOS
+a\247b
+cöd
+EOS
+
diff --git a/test/prism/fixtures/seattlerb/heredoc_comma_arg.txt b/test/prism/fixtures/seattlerb/heredoc_comma_arg.txt
new file mode 100644
index 0000000000..c230b12f65
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_comma_arg.txt
@@ -0,0 +1,7 @@
+[" some text
+",]
+
+[<<-FILE,
+ some text
+FILE
+]
diff --git a/test/prism/fixtures/seattlerb/heredoc_lineno.txt b/test/prism/fixtures/seattlerb/heredoc_lineno.txt
new file mode 100644
index 0000000000..73a2e3806b
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_lineno.txt
@@ -0,0 +1,7 @@
+c = <<'CCC'
+line2
+line3
+line4
+CCC
+
+d = 42
diff --git a/test/prism/fixtures/seattlerb/heredoc_nested.txt b/test/prism/fixtures/seattlerb/heredoc_nested.txt
new file mode 100644
index 0000000000..508d6d3c05
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_nested.txt
@@ -0,0 +1,7 @@
+[<<A,
+#{<<B}
+b
+B
+a
+A
+0]
diff --git a/test/prism/fixtures/seattlerb/heredoc_squiggly.txt b/test/prism/fixtures/seattlerb/heredoc_squiggly.txt
new file mode 100644
index 0000000000..e630ff62b4
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_squiggly.txt
@@ -0,0 +1,7 @@
+a = <<~"EOF"
+ x
+ y
+ z
+ EOF
+
+
diff --git a/test/prism/fixtures/seattlerb/heredoc_squiggly_blank_line_plus_interpolation.txt b/test/prism/fixtures/seattlerb/heredoc_squiggly_blank_line_plus_interpolation.txt
new file mode 100644
index 0000000000..61b62157db
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_squiggly_blank_line_plus_interpolation.txt
@@ -0,0 +1,4 @@
+a = foo(<<~EOF.chop)
+
+ #{bar}baz
+ EOF
diff --git a/test/prism/fixtures/seattlerb/heredoc_squiggly_blank_lines.txt b/test/prism/fixtures/seattlerb/heredoc_squiggly_blank_lines.txt
new file mode 100644
index 0000000000..0d89134c87
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_squiggly_blank_lines.txt
@@ -0,0 +1,7 @@
+a = <<~EOF
+ x
+
+ z
+EOF
+
+
diff --git a/test/prism/fixtures/seattlerb/heredoc_squiggly_empty.txt b/test/prism/fixtures/seattlerb/heredoc_squiggly_empty.txt
new file mode 100644
index 0000000000..4602d757fb
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_squiggly_empty.txt
@@ -0,0 +1,2 @@
+<<~A
+A
diff --git a/test/prism/fixtures/seattlerb/heredoc_squiggly_interp.txt b/test/prism/fixtures/seattlerb/heredoc_squiggly_interp.txt
new file mode 100644
index 0000000000..47ff3c9070
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_squiggly_interp.txt
@@ -0,0 +1,5 @@
+a = <<~EOF
+ w
+ x#{42} y
+ z
+ EOF
diff --git a/test/prism/fixtures/seattlerb/heredoc_squiggly_no_indent.txt b/test/prism/fixtures/seattlerb/heredoc_squiggly_no_indent.txt
new file mode 100644
index 0000000000..7076f6ef71
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_squiggly_no_indent.txt
@@ -0,0 +1,3 @@
+<<~A
+a
+A
diff --git a/test/prism/fixtures/seattlerb/heredoc_squiggly_tabs.txt b/test/prism/fixtures/seattlerb/heredoc_squiggly_tabs.txt
new file mode 100644
index 0000000000..b193f0473b
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_squiggly_tabs.txt
@@ -0,0 +1,6 @@
+a = <<~"EOF"
+ blah blah
+ blah blah
+ EOF
+
+
diff --git a/test/prism/fixtures/seattlerb/heredoc_squiggly_tabs_extra.txt b/test/prism/fixtures/seattlerb/heredoc_squiggly_tabs_extra.txt
new file mode 100644
index 0000000000..5b75dd2b59
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_squiggly_tabs_extra.txt
@@ -0,0 +1,6 @@
+a = <<~"EOF"
+ blah blah
+ blah blah
+ EOF
+
+
diff --git a/test/prism/fixtures/seattlerb/heredoc_squiggly_visually_blank_lines.txt b/test/prism/fixtures/seattlerb/heredoc_squiggly_visually_blank_lines.txt
new file mode 100644
index 0000000000..3f9198296d
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_squiggly_visually_blank_lines.txt
@@ -0,0 +1,7 @@
+a = <<~EOF
+ x
+
+ z
+EOF
+
+
diff --git a/test/prism/fixtures/seattlerb/heredoc_trailing_slash_continued_call.txt b/test/prism/fixtures/seattlerb/heredoc_trailing_slash_continued_call.txt
new file mode 100644
index 0000000000..12c8fac126
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_trailing_slash_continued_call.txt
@@ -0,0 +1,4 @@
+<<END\
+blah
+END
+.strip
diff --git a/test/prism/fixtures/seattlerb/heredoc_unicode.txt b/test/prism/fixtures/seattlerb/heredoc_unicode.txt
new file mode 100644
index 0000000000..216a5cfe24
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_unicode.txt
@@ -0,0 +1,4 @@
+<<OOTPÃœT
+.
+OOTPÃœT
+
diff --git a/test/prism/fixtures/seattlerb/heredoc_with_carriage_return_escapes.txt b/test/prism/fixtures/seattlerb/heredoc_with_carriage_return_escapes.txt
new file mode 100644
index 0000000000..cb4967d154
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_with_carriage_return_escapes.txt
@@ -0,0 +1,5 @@
+<<EOS
+foo\rbar
+baz\r
+EOS
+
diff --git a/test/prism/fixtures/seattlerb/heredoc_with_carriage_return_escapes_windows.txt b/test/prism/fixtures/seattlerb/heredoc_with_carriage_return_escapes_windows.txt
new file mode 100644
index 0000000000..75ed936b5d
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_with_carriage_return_escapes_windows.txt
@@ -0,0 +1,5 @@
+<<EOS
+foo\rbar
+baz\r
+EOS
+
diff --git a/test/prism/fixtures/seattlerb/heredoc_with_extra_carriage_horrible_mix.txt b/test/prism/fixtures/seattlerb/heredoc_with_extra_carriage_horrible_mix.txt
new file mode 100644
index 0000000000..1c58c05cc5
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_with_extra_carriage_horrible_mix.txt
@@ -0,0 +1,4 @@
+<<'eot'
+body
+eot
+
diff --git a/test/prism/fixtures/seattlerb/heredoc_with_extra_carriage_returns.txt b/test/prism/fixtures/seattlerb/heredoc_with_extra_carriage_returns.txt
new file mode 100644
index 0000000000..706cb0d5c0
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_with_extra_carriage_returns.txt
@@ -0,0 +1,5 @@
+<<EOS
+foo bar
+baz
+EOS
+
diff --git a/test/prism/fixtures/seattlerb/heredoc_with_extra_carriage_returns_windows.txt b/test/prism/fixtures/seattlerb/heredoc_with_extra_carriage_returns_windows.txt
new file mode 100644
index 0000000000..8ed84799b1
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_with_extra_carriage_returns_windows.txt
@@ -0,0 +1,5 @@
+<<EOS
+foo bar
+baz
+EOS
+
diff --git a/test/prism/fixtures/seattlerb/heredoc_with_interpolation_and_carriage_return_escapes.txt b/test/prism/fixtures/seattlerb/heredoc_with_interpolation_and_carriage_return_escapes.txt
new file mode 100644
index 0000000000..250e606f45
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_with_interpolation_and_carriage_return_escapes.txt
@@ -0,0 +1,4 @@
+<<EOS
+foo\r#@bar
+EOS
+
diff --git a/test/prism/fixtures/seattlerb/heredoc_with_interpolation_and_carriage_return_escapes_windows.txt b/test/prism/fixtures/seattlerb/heredoc_with_interpolation_and_carriage_return_escapes_windows.txt
new file mode 100644
index 0000000000..12f97aff5e
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_with_interpolation_and_carriage_return_escapes_windows.txt
@@ -0,0 +1,4 @@
+<<EOS
+foo\r#@bar
+EOS
+
diff --git a/test/prism/fixtures/seattlerb/heredoc_with_not_global_interpolation.txt b/test/prism/fixtures/seattlerb/heredoc_with_not_global_interpolation.txt
new file mode 100644
index 0000000000..f94c2c9e27
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_with_not_global_interpolation.txt
@@ -0,0 +1,3 @@
+<<-HEREDOC
+#${
+HEREDOC
diff --git a/test/prism/fixtures/seattlerb/heredoc_with_only_carriage_returns.txt b/test/prism/fixtures/seattlerb/heredoc_with_only_carriage_returns.txt
new file mode 100644
index 0000000000..468ba85cf7
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_with_only_carriage_returns.txt
@@ -0,0 +1,6 @@
+<<EOS
+
+
+\r
+EOS
+
diff --git a/test/prism/fixtures/seattlerb/heredoc_with_only_carriage_returns_windows.txt b/test/prism/fixtures/seattlerb/heredoc_with_only_carriage_returns_windows.txt
new file mode 100644
index 0000000000..e973eff110
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/heredoc_with_only_carriage_returns_windows.txt
@@ -0,0 +1,6 @@
+<<EOS
+
+
+\r
+EOS
+
diff --git a/test/prism/fixtures/seattlerb/if_elsif.txt b/test/prism/fixtures/seattlerb/if_elsif.txt
new file mode 100644
index 0000000000..bc1c0a2b3d
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/if_elsif.txt
@@ -0,0 +1 @@
+if 1; elsif 2; end
diff --git a/test/prism/fixtures/seattlerb/if_symbol.txt b/test/prism/fixtures/seattlerb/if_symbol.txt
new file mode 100644
index 0000000000..8d6e958ede
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/if_symbol.txt
@@ -0,0 +1 @@
+if f :x; end
diff --git a/test/prism/fixtures/seattlerb/in_expr_no_case.txt b/test/prism/fixtures/seattlerb/in_expr_no_case.txt
new file mode 100644
index 0000000000..40db7f868b
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/in_expr_no_case.txt
@@ -0,0 +1 @@
+'woot' in String
diff --git a/test/prism/fixtures/seattlerb/index_0.txt b/test/prism/fixtures/seattlerb/index_0.txt
new file mode 100644
index 0000000000..050d4566ba
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/index_0.txt
@@ -0,0 +1 @@
+a[] = b
diff --git a/test/prism/fixtures/seattlerb/index_0_opasgn.txt b/test/prism/fixtures/seattlerb/index_0_opasgn.txt
new file mode 100644
index 0000000000..7189f0c3ea
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/index_0_opasgn.txt
@@ -0,0 +1 @@
+a[] += b
diff --git a/test/prism/fixtures/seattlerb/integer_with_if_modifier.txt b/test/prism/fixtures/seattlerb/integer_with_if_modifier.txt
new file mode 100644
index 0000000000..bf2f621e92
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/integer_with_if_modifier.txt
@@ -0,0 +1 @@
+1_234if true
diff --git a/test/prism/fixtures/seattlerb/interpolated_symbol_array_line_breaks.txt b/test/prism/fixtures/seattlerb/interpolated_symbol_array_line_breaks.txt
new file mode 100644
index 0000000000..f4c7cb9662
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/interpolated_symbol_array_line_breaks.txt
@@ -0,0 +1,5 @@
+%I(
+a
+b
+)
+1
diff --git a/test/prism/fixtures/seattlerb/interpolated_word_array_line_breaks.txt b/test/prism/fixtures/seattlerb/interpolated_word_array_line_breaks.txt
new file mode 100644
index 0000000000..d52b4789cf
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/interpolated_word_array_line_breaks.txt
@@ -0,0 +1,5 @@
+%W(
+a
+b
+)
+1
diff --git a/test/prism/fixtures/seattlerb/iter_args_1.txt b/test/prism/fixtures/seattlerb/iter_args_1.txt
new file mode 100644
index 0000000000..c890ef62c3
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/iter_args_1.txt
@@ -0,0 +1 @@
+f { |a,b| }
diff --git a/test/prism/fixtures/seattlerb/iter_args_10_1.txt b/test/prism/fixtures/seattlerb/iter_args_10_1.txt
new file mode 100644
index 0000000000..3f558c5392
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/iter_args_10_1.txt
@@ -0,0 +1 @@
+f { |a, b = 42, *c| }
diff --git a/test/prism/fixtures/seattlerb/iter_args_10_2.txt b/test/prism/fixtures/seattlerb/iter_args_10_2.txt
new file mode 100644
index 0000000000..4158e79d14
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/iter_args_10_2.txt
@@ -0,0 +1 @@
+f { |a, b = 42, *c, &d| }
diff --git a/test/prism/fixtures/seattlerb/iter_args_11_1.txt b/test/prism/fixtures/seattlerb/iter_args_11_1.txt
new file mode 100644
index 0000000000..f86175c1a0
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/iter_args_11_1.txt
@@ -0,0 +1 @@
+f { |a, b = 42, *c, d| }
diff --git a/test/prism/fixtures/seattlerb/iter_args_11_2.txt b/test/prism/fixtures/seattlerb/iter_args_11_2.txt
new file mode 100644
index 0000000000..e4b017e44a
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/iter_args_11_2.txt
@@ -0,0 +1 @@
+f { |a, b = 42, *c, d, &e| }
diff --git a/test/prism/fixtures/seattlerb/iter_args_2__19.txt b/test/prism/fixtures/seattlerb/iter_args_2__19.txt
new file mode 100644
index 0000000000..84dd744243
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/iter_args_2__19.txt
@@ -0,0 +1 @@
+f { |(a, b)| }
diff --git a/test/prism/fixtures/seattlerb/iter_args_3.txt b/test/prism/fixtures/seattlerb/iter_args_3.txt
new file mode 100644
index 0000000000..261968ff13
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/iter_args_3.txt
@@ -0,0 +1 @@
+f { |a, (b, c), d| }
diff --git a/test/prism/fixtures/seattlerb/iter_args_4.txt b/test/prism/fixtures/seattlerb/iter_args_4.txt
new file mode 100644
index 0000000000..7db4d43ad3
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/iter_args_4.txt
@@ -0,0 +1 @@
+f { |a, *b, c| }
diff --git a/test/prism/fixtures/seattlerb/iter_args_5.txt b/test/prism/fixtures/seattlerb/iter_args_5.txt
new file mode 100644
index 0000000000..088fcdfcc5
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/iter_args_5.txt
@@ -0,0 +1 @@
+f { |a, &b| }
diff --git a/test/prism/fixtures/seattlerb/iter_args_6.txt b/test/prism/fixtures/seattlerb/iter_args_6.txt
new file mode 100644
index 0000000000..e980ec064b
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/iter_args_6.txt
@@ -0,0 +1 @@
+f { |a, b=42, c| }
diff --git a/test/prism/fixtures/seattlerb/iter_args_7_1.txt b/test/prism/fixtures/seattlerb/iter_args_7_1.txt
new file mode 100644
index 0000000000..e1dbf4b312
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/iter_args_7_1.txt
@@ -0,0 +1 @@
+f { |a = 42, *b| }
diff --git a/test/prism/fixtures/seattlerb/iter_args_7_2.txt b/test/prism/fixtures/seattlerb/iter_args_7_2.txt
new file mode 100644
index 0000000000..e46e78e00e
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/iter_args_7_2.txt
@@ -0,0 +1 @@
+f { |a = 42, *b, &c| }
diff --git a/test/prism/fixtures/seattlerb/iter_args_8_1.txt b/test/prism/fixtures/seattlerb/iter_args_8_1.txt
new file mode 100644
index 0000000000..a0ec82ea5b
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/iter_args_8_1.txt
@@ -0,0 +1 @@
+f { |a = 42, *b, c| }
diff --git a/test/prism/fixtures/seattlerb/iter_args_8_2.txt b/test/prism/fixtures/seattlerb/iter_args_8_2.txt
new file mode 100644
index 0000000000..a1839fe706
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/iter_args_8_2.txt
@@ -0,0 +1 @@
+f { |a = 42, *b, c, &d| }
diff --git a/test/prism/fixtures/seattlerb/iter_args_9_1.txt b/test/prism/fixtures/seattlerb/iter_args_9_1.txt
new file mode 100644
index 0000000000..13e5b20fe7
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/iter_args_9_1.txt
@@ -0,0 +1 @@
+f { |a = 42, b| }
diff --git a/test/prism/fixtures/seattlerb/iter_args_9_2.txt b/test/prism/fixtures/seattlerb/iter_args_9_2.txt
new file mode 100644
index 0000000000..83f6e1e029
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/iter_args_9_2.txt
@@ -0,0 +1 @@
+f { |a = 42, b, &c| }
diff --git a/test/prism/fixtures/seattlerb/iter_kwarg.txt b/test/prism/fixtures/seattlerb/iter_kwarg.txt
new file mode 100644
index 0000000000..d4296cad76
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/iter_kwarg.txt
@@ -0,0 +1 @@
+a { |b: 1| }
diff --git a/test/prism/fixtures/seattlerb/iter_kwarg_kwsplat.txt b/test/prism/fixtures/seattlerb/iter_kwarg_kwsplat.txt
new file mode 100644
index 0000000000..dd0ea13a6f
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/iter_kwarg_kwsplat.txt
@@ -0,0 +1 @@
+a { |b: 1, **c| }
diff --git a/test/prism/fixtures/seattlerb/label_vs_string.txt b/test/prism/fixtures/seattlerb/label_vs_string.txt
new file mode 100644
index 0000000000..27ba8b64de
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/label_vs_string.txt
@@ -0,0 +1,2 @@
+_buf << ':
+'
diff --git a/test/prism/fixtures/seattlerb/lambda_do_vs_brace.txt b/test/prism/fixtures/seattlerb/lambda_do_vs_brace.txt
new file mode 100644
index 0000000000..bbf663a46a
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/lambda_do_vs_brace.txt
@@ -0,0 +1,7 @@
+f -> do end
+
+f -> {}
+
+f ->() do end
+
+f ->() {}
diff --git a/test/prism/fixtures/seattlerb/lasgn_arg_rescue_arg.txt b/test/prism/fixtures/seattlerb/lasgn_arg_rescue_arg.txt
new file mode 100644
index 0000000000..0dad496c28
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/lasgn_arg_rescue_arg.txt
@@ -0,0 +1 @@
+a = 1 rescue 2
diff --git a/test/prism/fixtures/seattlerb/lasgn_call_bracket_rescue_arg.txt b/test/prism/fixtures/seattlerb/lasgn_call_bracket_rescue_arg.txt
new file mode 100644
index 0000000000..3f63c0b748
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/lasgn_call_bracket_rescue_arg.txt
@@ -0,0 +1 @@
+a = b(1) rescue 2
diff --git a/test/prism/fixtures/seattlerb/lasgn_call_nobracket_rescue_arg.txt b/test/prism/fixtures/seattlerb/lasgn_call_nobracket_rescue_arg.txt
new file mode 100644
index 0000000000..0e86f1587d
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/lasgn_call_nobracket_rescue_arg.txt
@@ -0,0 +1 @@
+a = b 1 rescue 2
diff --git a/test/prism/fixtures/seattlerb/lasgn_command.txt b/test/prism/fixtures/seattlerb/lasgn_command.txt
new file mode 100644
index 0000000000..aca35b880c
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/lasgn_command.txt
@@ -0,0 +1 @@
+a = b.c 1
diff --git a/test/prism/fixtures/seattlerb/lasgn_env.txt b/test/prism/fixtures/seattlerb/lasgn_env.txt
new file mode 100644
index 0000000000..aec10273e5
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/lasgn_env.txt
@@ -0,0 +1 @@
+a = 42
diff --git a/test/prism/fixtures/seattlerb/lasgn_ivar_env.txt b/test/prism/fixtures/seattlerb/lasgn_ivar_env.txt
new file mode 100644
index 0000000000..2fa8471c01
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/lasgn_ivar_env.txt
@@ -0,0 +1 @@
+@a = 42
diff --git a/test/prism/fixtures/seattlerb/lasgn_lasgn_command_call.txt b/test/prism/fixtures/seattlerb/lasgn_lasgn_command_call.txt
new file mode 100644
index 0000000000..5147131852
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/lasgn_lasgn_command_call.txt
@@ -0,0 +1 @@
+a = b = c 1
diff --git a/test/prism/fixtures/seattlerb/lasgn_middle_splat.txt b/test/prism/fixtures/seattlerb/lasgn_middle_splat.txt
new file mode 100644
index 0000000000..bb378ca680
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/lasgn_middle_splat.txt
@@ -0,0 +1 @@
+a = b, *c, d
diff --git a/test/prism/fixtures/seattlerb/magic_encoding_comment.txt b/test/prism/fixtures/seattlerb/magic_encoding_comment.txt
new file mode 100644
index 0000000000..a02711ea05
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/magic_encoding_comment.txt
@@ -0,0 +1,4 @@
+# encoding: utf-8
+class ExampleUTF8ClassNameVarietà; def self.è; così = :però; end
+end
+
diff --git a/test/prism/fixtures/seattlerb/masgn_anon_splat_arg.txt b/test/prism/fixtures/seattlerb/masgn_anon_splat_arg.txt
new file mode 100644
index 0000000000..b796a742ed
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/masgn_anon_splat_arg.txt
@@ -0,0 +1 @@
+*, a = b
diff --git a/test/prism/fixtures/seattlerb/masgn_arg_colon_arg.txt b/test/prism/fixtures/seattlerb/masgn_arg_colon_arg.txt
new file mode 100644
index 0000000000..e0919793d4
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/masgn_arg_colon_arg.txt
@@ -0,0 +1 @@
+a, b::c = d
diff --git a/test/prism/fixtures/seattlerb/masgn_arg_ident.txt b/test/prism/fixtures/seattlerb/masgn_arg_ident.txt
new file mode 100644
index 0000000000..45f248d854
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/masgn_arg_ident.txt
@@ -0,0 +1 @@
+a, b.C = d
diff --git a/test/prism/fixtures/seattlerb/masgn_arg_splat_arg.txt b/test/prism/fixtures/seattlerb/masgn_arg_splat_arg.txt
new file mode 100644
index 0000000000..05fe7c4d5f
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/masgn_arg_splat_arg.txt
@@ -0,0 +1 @@
+a, *b, c = d
diff --git a/test/prism/fixtures/seattlerb/masgn_colon2.txt b/test/prism/fixtures/seattlerb/masgn_colon2.txt
new file mode 100644
index 0000000000..4e4f838d7d
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/masgn_colon2.txt
@@ -0,0 +1 @@
+a, b::C = 1, 2
diff --git a/test/prism/fixtures/seattlerb/masgn_colon3.txt b/test/prism/fixtures/seattlerb/masgn_colon3.txt
new file mode 100644
index 0000000000..46098ba8c5
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/masgn_colon3.txt
@@ -0,0 +1 @@
+::A, ::B = 1, 2
diff --git a/test/prism/fixtures/seattlerb/masgn_command_call.txt b/test/prism/fixtures/seattlerb/masgn_command_call.txt
new file mode 100644
index 0000000000..6da01e8a31
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/masgn_command_call.txt
@@ -0,0 +1 @@
+a, = b.c 1
diff --git a/test/prism/fixtures/seattlerb/masgn_double_paren.txt b/test/prism/fixtures/seattlerb/masgn_double_paren.txt
new file mode 100644
index 0000000000..ffac0a85a3
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/masgn_double_paren.txt
@@ -0,0 +1 @@
+((a,b))=c
diff --git a/test/prism/fixtures/seattlerb/masgn_lhs_splat.txt b/test/prism/fixtures/seattlerb/masgn_lhs_splat.txt
new file mode 100644
index 0000000000..2419ef1671
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/masgn_lhs_splat.txt
@@ -0,0 +1 @@
+*a = 1, 2, 3
diff --git a/test/prism/fixtures/seattlerb/masgn_paren.txt b/test/prism/fixtures/seattlerb/masgn_paren.txt
new file mode 100644
index 0000000000..3889b9ff48
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/masgn_paren.txt
@@ -0,0 +1 @@
+(a, b) = c.d
diff --git a/test/prism/fixtures/seattlerb/masgn_splat_arg.txt b/test/prism/fixtures/seattlerb/masgn_splat_arg.txt
new file mode 100644
index 0000000000..a7c91425b0
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/masgn_splat_arg.txt
@@ -0,0 +1 @@
+*a, b = c
diff --git a/test/prism/fixtures/seattlerb/masgn_splat_arg_arg.txt b/test/prism/fixtures/seattlerb/masgn_splat_arg_arg.txt
new file mode 100644
index 0000000000..46196bd703
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/masgn_splat_arg_arg.txt
@@ -0,0 +1 @@
+*a, b, c = d
diff --git a/test/prism/fixtures/seattlerb/masgn_star.txt b/test/prism/fixtures/seattlerb/masgn_star.txt
new file mode 100644
index 0000000000..c5eea37de2
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/masgn_star.txt
@@ -0,0 +1 @@
+* = 1
diff --git a/test/prism/fixtures/seattlerb/masgn_var_star_var.txt b/test/prism/fixtures/seattlerb/masgn_var_star_var.txt
new file mode 100644
index 0000000000..04089c36ac
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/masgn_var_star_var.txt
@@ -0,0 +1 @@
+a, *, b = c
diff --git a/test/prism/fixtures/seattlerb/messy_op_asgn_lineno.txt b/test/prism/fixtures/seattlerb/messy_op_asgn_lineno.txt
new file mode 100644
index 0000000000..a7d1035ae3
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/messy_op_asgn_lineno.txt
@@ -0,0 +1 @@
+a (B::C *= d e)
diff --git a/test/prism/fixtures/seattlerb/method_call_assoc_trailing_comma.txt b/test/prism/fixtures/seattlerb/method_call_assoc_trailing_comma.txt
new file mode 100644
index 0000000000..86f0fbdfc9
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/method_call_assoc_trailing_comma.txt
@@ -0,0 +1 @@
+a.f(1=>2,)
diff --git a/test/prism/fixtures/seattlerb/method_call_trailing_comma.txt b/test/prism/fixtures/seattlerb/method_call_trailing_comma.txt
new file mode 100644
index 0000000000..1a155fba12
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/method_call_trailing_comma.txt
@@ -0,0 +1 @@
+a.f(1,)
diff --git a/test/prism/fixtures/seattlerb/mlhs_back_anonsplat.txt b/test/prism/fixtures/seattlerb/mlhs_back_anonsplat.txt
new file mode 100644
index 0000000000..7389b95563
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/mlhs_back_anonsplat.txt
@@ -0,0 +1 @@
+a, b, c, * = f
diff --git a/test/prism/fixtures/seattlerb/mlhs_back_splat.txt b/test/prism/fixtures/seattlerb/mlhs_back_splat.txt
new file mode 100644
index 0000000000..ec5d23889a
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/mlhs_back_splat.txt
@@ -0,0 +1 @@
+a, b, c, *s = f
diff --git a/test/prism/fixtures/seattlerb/mlhs_front_anonsplat.txt b/test/prism/fixtures/seattlerb/mlhs_front_anonsplat.txt
new file mode 100644
index 0000000000..67e569438c
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/mlhs_front_anonsplat.txt
@@ -0,0 +1 @@
+*, x, y, z = f
diff --git a/test/prism/fixtures/seattlerb/mlhs_front_splat.txt b/test/prism/fixtures/seattlerb/mlhs_front_splat.txt
new file mode 100644
index 0000000000..dabadc382d
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/mlhs_front_splat.txt
@@ -0,0 +1 @@
+*s, x, y, z = f
diff --git a/test/prism/fixtures/seattlerb/mlhs_keyword.txt b/test/prism/fixtures/seattlerb/mlhs_keyword.txt
new file mode 100644
index 0000000000..899e7f8ed3
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/mlhs_keyword.txt
@@ -0,0 +1 @@
+a.!=(true, true)
diff --git a/test/prism/fixtures/seattlerb/mlhs_mid_anonsplat.txt b/test/prism/fixtures/seattlerb/mlhs_mid_anonsplat.txt
new file mode 100644
index 0000000000..a70a7e531b
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/mlhs_mid_anonsplat.txt
@@ -0,0 +1 @@
+a, b, c, *, x, y, z = f
diff --git a/test/prism/fixtures/seattlerb/mlhs_mid_splat.txt b/test/prism/fixtures/seattlerb/mlhs_mid_splat.txt
new file mode 100644
index 0000000000..2d23fd3966
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/mlhs_mid_splat.txt
@@ -0,0 +1 @@
+a, b, c, *s, x, y, z = f
diff --git a/test/prism/fixtures/seattlerb/mlhs_rescue.txt b/test/prism/fixtures/seattlerb/mlhs_rescue.txt
new file mode 100644
index 0000000000..b4c79ae32e
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/mlhs_rescue.txt
@@ -0,0 +1 @@
+a, b = f rescue 42
diff --git a/test/prism/fixtures/seattlerb/module_comments.txt b/test/prism/fixtures/seattlerb/module_comments.txt
new file mode 100644
index 0000000000..cecb717c5b
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/module_comments.txt
@@ -0,0 +1,10 @@
+# blah 1
+
+ # blah 2
+
+module X
+ # blah 3
+ def blah
+ # blah 4
+ end
+end
diff --git a/test/prism/fixtures/seattlerb/multiline_hash_declaration.txt b/test/prism/fixtures/seattlerb/multiline_hash_declaration.txt
new file mode 100644
index 0000000000..21530307d2
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/multiline_hash_declaration.txt
@@ -0,0 +1,8 @@
+f(state:
+ {
+})
+
+f(state: {
+})
+
+f(state: {})
diff --git a/test/prism/fixtures/seattlerb/non_interpolated_symbol_array_line_breaks.txt b/test/prism/fixtures/seattlerb/non_interpolated_symbol_array_line_breaks.txt
new file mode 100644
index 0000000000..1e14673f4e
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/non_interpolated_symbol_array_line_breaks.txt
@@ -0,0 +1,5 @@
+%i(
+a
+b
+)
+1
diff --git a/test/prism/fixtures/seattlerb/non_interpolated_word_array_line_breaks.txt b/test/prism/fixtures/seattlerb/non_interpolated_word_array_line_breaks.txt
new file mode 100644
index 0000000000..79c1418770
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/non_interpolated_word_array_line_breaks.txt
@@ -0,0 +1,5 @@
+%w(
+a
+b
+)
+1
diff --git a/test/prism/fixtures/seattlerb/op_asgn_command_call.txt b/test/prism/fixtures/seattlerb/op_asgn_command_call.txt
new file mode 100644
index 0000000000..92c989cb0d
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/op_asgn_command_call.txt
@@ -0,0 +1 @@
+a ||= b.c 2
diff --git a/test/prism/fixtures/seattlerb/op_asgn_dot_ident_command_call.txt b/test/prism/fixtures/seattlerb/op_asgn_dot_ident_command_call.txt
new file mode 100644
index 0000000000..89cfccda66
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/op_asgn_dot_ident_command_call.txt
@@ -0,0 +1 @@
+A.B ||= c 1
diff --git a/test/prism/fixtures/seattlerb/op_asgn_index_command_call.txt b/test/prism/fixtures/seattlerb/op_asgn_index_command_call.txt
new file mode 100644
index 0000000000..2bfced81fe
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/op_asgn_index_command_call.txt
@@ -0,0 +1 @@
+a[:b] ||= c 1, 2
diff --git a/test/prism/fixtures/seattlerb/op_asgn_primary_colon_const_command_call.txt b/test/prism/fixtures/seattlerb/op_asgn_primary_colon_const_command_call.txt
new file mode 100644
index 0000000000..a567f60e83
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/op_asgn_primary_colon_const_command_call.txt
@@ -0,0 +1 @@
+A::B *= c d
diff --git a/test/prism/fixtures/seattlerb/op_asgn_primary_colon_identifier1.txt b/test/prism/fixtures/seattlerb/op_asgn_primary_colon_identifier1.txt
new file mode 100644
index 0000000000..0784b49167
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/op_asgn_primary_colon_identifier1.txt
@@ -0,0 +1 @@
+A::b += 1
diff --git a/test/prism/fixtures/seattlerb/op_asgn_primary_colon_identifier_command_call.txt b/test/prism/fixtures/seattlerb/op_asgn_primary_colon_identifier_command_call.txt
new file mode 100644
index 0000000000..c0f16eb3c1
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/op_asgn_primary_colon_identifier_command_call.txt
@@ -0,0 +1 @@
+A::b *= c d
diff --git a/test/prism/fixtures/seattlerb/op_asgn_val_dot_ident_command_call.txt b/test/prism/fixtures/seattlerb/op_asgn_val_dot_ident_command_call.txt
new file mode 100644
index 0000000000..69057abf04
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/op_asgn_val_dot_ident_command_call.txt
@@ -0,0 +1 @@
+a.b ||= c 1
diff --git a/test/prism/fixtures/seattlerb/parse_def_special_name.txt b/test/prism/fixtures/seattlerb/parse_def_special_name.txt
new file mode 100644
index 0000000000..8d7d06c688
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_def_special_name.txt
@@ -0,0 +1 @@
+def next; end
diff --git a/test/prism/fixtures/seattlerb/parse_if_not_canonical.txt b/test/prism/fixtures/seattlerb/parse_if_not_canonical.txt
new file mode 100644
index 0000000000..1fd9bb7327
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_if_not_canonical.txt
@@ -0,0 +1,2 @@
+if not var.nil? then 'foo' else 'bar'
+end
diff --git a/test/prism/fixtures/seattlerb/parse_if_not_noncanonical.txt b/test/prism/fixtures/seattlerb/parse_if_not_noncanonical.txt
new file mode 100644
index 0000000000..1fd9bb7327
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_if_not_noncanonical.txt
@@ -0,0 +1,2 @@
+if not var.nil? then 'foo' else 'bar'
+end
diff --git a/test/prism/fixtures/seattlerb/parse_line_block.txt b/test/prism/fixtures/seattlerb/parse_line_block.txt
new file mode 100644
index 0000000000..21664649db
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_block.txt
@@ -0,0 +1,2 @@
+a = 42
+p a
diff --git a/test/prism/fixtures/seattlerb/parse_line_block_inline_comment.txt b/test/prism/fixtures/seattlerb/parse_line_block_inline_comment.txt
new file mode 100644
index 0000000000..f55ced714f
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_block_inline_comment.txt
@@ -0,0 +1,3 @@
+a
+b # comment
+c
diff --git a/test/prism/fixtures/seattlerb/parse_line_block_inline_comment_leading_newlines.txt b/test/prism/fixtures/seattlerb/parse_line_block_inline_comment_leading_newlines.txt
new file mode 100644
index 0000000000..6f1fee62a0
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_block_inline_comment_leading_newlines.txt
@@ -0,0 +1,7 @@
+
+
+
+a
+b # comment
+# another comment
+c
diff --git a/test/prism/fixtures/seattlerb/parse_line_block_inline_multiline_comment.txt b/test/prism/fixtures/seattlerb/parse_line_block_inline_multiline_comment.txt
new file mode 100644
index 0000000000..b00de34dc0
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_block_inline_multiline_comment.txt
@@ -0,0 +1,4 @@
+a
+b # comment
+# another comment
+c
diff --git a/test/prism/fixtures/seattlerb/parse_line_call_ivar_arg_no_parens_line_break.txt b/test/prism/fixtures/seattlerb/parse_line_call_ivar_arg_no_parens_line_break.txt
new file mode 100644
index 0000000000..73785eb794
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_call_ivar_arg_no_parens_line_break.txt
@@ -0,0 +1,2 @@
+a @b
+
diff --git a/test/prism/fixtures/seattlerb/parse_line_call_ivar_line_break_paren.txt b/test/prism/fixtures/seattlerb/parse_line_call_ivar_line_break_paren.txt
new file mode 100644
index 0000000000..6f136e6d6f
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_call_ivar_line_break_paren.txt
@@ -0,0 +1,2 @@
+a(@b
+)
diff --git a/test/prism/fixtures/seattlerb/parse_line_call_no_args.txt b/test/prism/fixtures/seattlerb/parse_line_call_no_args.txt
new file mode 100644
index 0000000000..7900afd4b8
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_call_no_args.txt
@@ -0,0 +1,3 @@
+f do |x, y|
+ x + y
+end
diff --git a/test/prism/fixtures/seattlerb/parse_line_defn_complex.txt b/test/prism/fixtures/seattlerb/parse_line_defn_complex.txt
new file mode 100644
index 0000000000..244a8e862b
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_defn_complex.txt
@@ -0,0 +1,5 @@
+def x(y)
+ p(y)
+ y *= 2
+ return y;
+end
diff --git a/test/prism/fixtures/seattlerb/parse_line_defn_no_parens.txt b/test/prism/fixtures/seattlerb/parse_line_defn_no_parens.txt
new file mode 100644
index 0000000000..373ca7fbec
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_defn_no_parens.txt
@@ -0,0 +1,6 @@
+def f
+
+end
+
+def f
+end
diff --git a/test/prism/fixtures/seattlerb/parse_line_defn_no_parens_args.txt b/test/prism/fixtures/seattlerb/parse_line_defn_no_parens_args.txt
new file mode 100644
index 0000000000..10f004a149
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_defn_no_parens_args.txt
@@ -0,0 +1,2 @@
+def f a
+end
diff --git a/test/prism/fixtures/seattlerb/parse_line_dot2.txt b/test/prism/fixtures/seattlerb/parse_line_dot2.txt
new file mode 100644
index 0000000000..61c7554221
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_dot2.txt
@@ -0,0 +1,5 @@
+0..
+4
+a..
+b
+c
diff --git a/test/prism/fixtures/seattlerb/parse_line_dot2_open.txt b/test/prism/fixtures/seattlerb/parse_line_dot2_open.txt
new file mode 100644
index 0000000000..b3e1e5aaf9
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_dot2_open.txt
@@ -0,0 +1,3 @@
+0..
+; a..
+; c
diff --git a/test/prism/fixtures/seattlerb/parse_line_dot3.txt b/test/prism/fixtures/seattlerb/parse_line_dot3.txt
new file mode 100644
index 0000000000..d1866b41de
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_dot3.txt
@@ -0,0 +1,5 @@
+0...
+4
+a...
+b
+c
diff --git a/test/prism/fixtures/seattlerb/parse_line_dot3_open.txt b/test/prism/fixtures/seattlerb/parse_line_dot3_open.txt
new file mode 100644
index 0000000000..38e7634b21
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_dot3_open.txt
@@ -0,0 +1,3 @@
+0...
+; a...
+; c
diff --git a/test/prism/fixtures/seattlerb/parse_line_dstr_escaped_newline.txt b/test/prism/fixtures/seattlerb/parse_line_dstr_escaped_newline.txt
new file mode 100644
index 0000000000..29c1754915
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_dstr_escaped_newline.txt
@@ -0,0 +1,3 @@
+"a\n#{
+}"
+true
diff --git a/test/prism/fixtures/seattlerb/parse_line_dstr_soft_newline.txt b/test/prism/fixtures/seattlerb/parse_line_dstr_soft_newline.txt
new file mode 100644
index 0000000000..e4dbd7bcb2
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_dstr_soft_newline.txt
@@ -0,0 +1,4 @@
+"a
+#{
+}"
+true
diff --git a/test/prism/fixtures/seattlerb/parse_line_evstr_after_break.txt b/test/prism/fixtures/seattlerb/parse_line_evstr_after_break.txt
new file mode 100644
index 0000000000..c1d91a51c4
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_evstr_after_break.txt
@@ -0,0 +1,2 @@
+"a"\
+"#{b}"
diff --git a/test/prism/fixtures/seattlerb/parse_line_hash_lit.txt b/test/prism/fixtures/seattlerb/parse_line_hash_lit.txt
new file mode 100644
index 0000000000..25f8c90a06
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_hash_lit.txt
@@ -0,0 +1,3 @@
+{
+:s1 => 1,
+}
diff --git a/test/prism/fixtures/seattlerb/parse_line_heredoc.txt b/test/prism/fixtures/seattlerb/parse_line_heredoc.txt
new file mode 100644
index 0000000000..201339534c
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_heredoc.txt
@@ -0,0 +1,5 @@
+ string = <<-HEREDOC.strip
+ very long string
+ HEREDOC
+ puts string
+
diff --git a/test/prism/fixtures/seattlerb/parse_line_heredoc_evstr.txt b/test/prism/fixtures/seattlerb/parse_line_heredoc_evstr.txt
new file mode 100644
index 0000000000..d50844db4b
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_heredoc_evstr.txt
@@ -0,0 +1,4 @@
+<<-A
+a
+#{b}
+A
diff --git a/test/prism/fixtures/seattlerb/parse_line_heredoc_hardnewline.txt b/test/prism/fixtures/seattlerb/parse_line_heredoc_hardnewline.txt
new file mode 100644
index 0000000000..3fbf0f2c26
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_heredoc_hardnewline.txt
@@ -0,0 +1,7 @@
+<<-EOFOO
+\n\n\n\n\n\n\n\n\n
+EOFOO
+
+class Foo
+end
+
diff --git a/test/prism/fixtures/seattlerb/parse_line_heredoc_regexp_chars.txt b/test/prism/fixtures/seattlerb/parse_line_heredoc_regexp_chars.txt
new file mode 100644
index 0000000000..5dab9cf4e7
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_heredoc_regexp_chars.txt
@@ -0,0 +1,5 @@
+ string = <<-"^D"
+ very long string
+ ^D
+ puts string
+
diff --git a/test/prism/fixtures/seattlerb/parse_line_iter_call_no_parens.txt b/test/prism/fixtures/seattlerb/parse_line_iter_call_no_parens.txt
new file mode 100644
index 0000000000..bf1b33c8a2
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_iter_call_no_parens.txt
@@ -0,0 +1,3 @@
+f a do |x, y|
+ x + y
+end
diff --git a/test/prism/fixtures/seattlerb/parse_line_iter_call_parens.txt b/test/prism/fixtures/seattlerb/parse_line_iter_call_parens.txt
new file mode 100644
index 0000000000..25e9ea1c67
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_iter_call_parens.txt
@@ -0,0 +1,3 @@
+f(a) do |x, y|
+ x + y
+end
diff --git a/test/prism/fixtures/seattlerb/parse_line_multiline_str.txt b/test/prism/fixtures/seattlerb/parse_line_multiline_str.txt
new file mode 100644
index 0000000000..cdefb3c9b7
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_multiline_str.txt
@@ -0,0 +1,3 @@
+"a
+b"
+1
diff --git a/test/prism/fixtures/seattlerb/parse_line_multiline_str_literal_n.txt b/test/prism/fixtures/seattlerb/parse_line_multiline_str_literal_n.txt
new file mode 100644
index 0000000000..a179ba8c9c
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_multiline_str_literal_n.txt
@@ -0,0 +1,2 @@
+"a\nb"
+1
diff --git a/test/prism/fixtures/seattlerb/parse_line_newlines.txt b/test/prism/fixtures/seattlerb/parse_line_newlines.txt
new file mode 100644
index 0000000000..28b0c286e8
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_newlines.txt
@@ -0,0 +1,3 @@
+true
+
+
diff --git a/test/prism/fixtures/seattlerb/parse_line_op_asgn.txt b/test/prism/fixtures/seattlerb/parse_line_op_asgn.txt
new file mode 100644
index 0000000000..f2691c2ce4
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_op_asgn.txt
@@ -0,0 +1,4 @@
+ foo +=
+ bar
+ baz
+
diff --git a/test/prism/fixtures/seattlerb/parse_line_postexe.txt b/test/prism/fixtures/seattlerb/parse_line_postexe.txt
new file mode 100644
index 0000000000..fd8b318d19
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_postexe.txt
@@ -0,0 +1,3 @@
+END {
+foo
+}
diff --git a/test/prism/fixtures/seattlerb/parse_line_preexe.txt b/test/prism/fixtures/seattlerb/parse_line_preexe.txt
new file mode 100644
index 0000000000..b3eda77ebc
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_preexe.txt
@@ -0,0 +1,3 @@
+BEGIN {
+foo
+}
diff --git a/test/prism/fixtures/seattlerb/parse_line_rescue.txt b/test/prism/fixtures/seattlerb/parse_line_rescue.txt
new file mode 100644
index 0000000000..a583160ce2
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_rescue.txt
@@ -0,0 +1,8 @@
+begin
+ a
+rescue
+ b
+rescue
+ c
+end
+
diff --git a/test/prism/fixtures/seattlerb/parse_line_return.txt b/test/prism/fixtures/seattlerb/parse_line_return.txt
new file mode 100644
index 0000000000..81021c2644
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_return.txt
@@ -0,0 +1,6 @@
+ def blah
+ if true then
+ return 42
+ end
+ end
+
diff --git a/test/prism/fixtures/seattlerb/parse_line_str_with_newline_escape.txt b/test/prism/fixtures/seattlerb/parse_line_str_with_newline_escape.txt
new file mode 100644
index 0000000000..b2b6bb8234
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_str_with_newline_escape.txt
@@ -0,0 +1 @@
+a("\n", true)
diff --git a/test/prism/fixtures/seattlerb/parse_line_to_ary.txt b/test/prism/fixtures/seattlerb/parse_line_to_ary.txt
new file mode 100644
index 0000000000..590d0abd14
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_to_ary.txt
@@ -0,0 +1,3 @@
+a,
+b = c
+d
diff --git a/test/prism/fixtures/seattlerb/parse_line_trailing_newlines.txt b/test/prism/fixtures/seattlerb/parse_line_trailing_newlines.txt
new file mode 100644
index 0000000000..afa826fb50
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_line_trailing_newlines.txt
@@ -0,0 +1,2 @@
+a
+b
diff --git a/test/prism/fixtures/seattlerb/parse_opt_call_args_assocs_comma.txt b/test/prism/fixtures/seattlerb/parse_opt_call_args_assocs_comma.txt
new file mode 100644
index 0000000000..649c109ea1
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_opt_call_args_assocs_comma.txt
@@ -0,0 +1 @@
+1[2=>3,]
diff --git a/test/prism/fixtures/seattlerb/parse_opt_call_args_lit_comma.txt b/test/prism/fixtures/seattlerb/parse_opt_call_args_lit_comma.txt
new file mode 100644
index 0000000000..741cd4ffd1
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_opt_call_args_lit_comma.txt
@@ -0,0 +1 @@
+1[2,]
diff --git a/test/prism/fixtures/seattlerb/parse_pattern_019.txt b/test/prism/fixtures/seattlerb/parse_pattern_019.txt
new file mode 100644
index 0000000000..1e8a75902d
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_pattern_019.txt
@@ -0,0 +1,5 @@
+case 0
+in -1..1
+ true
+end
+
diff --git a/test/prism/fixtures/seattlerb/parse_pattern_044.txt b/test/prism/fixtures/seattlerb/parse_pattern_044.txt
new file mode 100644
index 0000000000..a6a0ac6c1c
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_pattern_044.txt
@@ -0,0 +1,5 @@
+case obj
+in Object[]
+ true
+end
+
diff --git a/test/prism/fixtures/seattlerb/parse_pattern_051.txt b/test/prism/fixtures/seattlerb/parse_pattern_051.txt
new file mode 100644
index 0000000000..b7cf769f50
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_pattern_051.txt
@@ -0,0 +1,5 @@
+case [0, 1, 2]
+in [0, 1,]
+ true
+end
+
diff --git a/test/prism/fixtures/seattlerb/parse_pattern_058.txt b/test/prism/fixtures/seattlerb/parse_pattern_058.txt
new file mode 100644
index 0000000000..bd7537098e
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_pattern_058.txt
@@ -0,0 +1,5 @@
+case {a: 0}
+in {a:, **rest}
+ [a, rest]
+end
+
diff --git a/test/prism/fixtures/seattlerb/parse_pattern_058_2.txt b/test/prism/fixtures/seattlerb/parse_pattern_058_2.txt
new file mode 100644
index 0000000000..eb1b3cd8ab
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_pattern_058_2.txt
@@ -0,0 +1,5 @@
+case {a: 0}
+in {a:, **}
+ [a]
+end
+
diff --git a/test/prism/fixtures/seattlerb/parse_pattern_069.txt b/test/prism/fixtures/seattlerb/parse_pattern_069.txt
new file mode 100644
index 0000000000..f43dff8959
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_pattern_069.txt
@@ -0,0 +1,5 @@
+case :a
+in Object[b: 1]
+ 1
+end
+
diff --git a/test/prism/fixtures/seattlerb/parse_pattern_076.txt b/test/prism/fixtures/seattlerb/parse_pattern_076.txt
new file mode 100644
index 0000000000..bb947605b3
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_pattern_076.txt
@@ -0,0 +1,5 @@
+case {a: 1}
+in {a: 1, **nil}
+ true
+end
+
diff --git a/test/prism/fixtures/seattlerb/parse_until_not_canonical.txt b/test/prism/fixtures/seattlerb/parse_until_not_canonical.txt
new file mode 100644
index 0000000000..4de38968dc
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_until_not_canonical.txt
@@ -0,0 +1,3 @@
+until not var.nil?
+ 'foo'
+end
diff --git a/test/prism/fixtures/seattlerb/parse_until_not_noncanonical.txt b/test/prism/fixtures/seattlerb/parse_until_not_noncanonical.txt
new file mode 100644
index 0000000000..4de38968dc
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_until_not_noncanonical.txt
@@ -0,0 +1,3 @@
+until not var.nil?
+ 'foo'
+end
diff --git a/test/prism/fixtures/seattlerb/parse_while_not_canonical.txt b/test/prism/fixtures/seattlerb/parse_while_not_canonical.txt
new file mode 100644
index 0000000000..5aa464167f
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_while_not_canonical.txt
@@ -0,0 +1,3 @@
+while not var.nil?
+ 'foo'
+end
diff --git a/test/prism/fixtures/seattlerb/parse_while_not_noncanonical.txt b/test/prism/fixtures/seattlerb/parse_while_not_noncanonical.txt
new file mode 100644
index 0000000000..5aa464167f
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/parse_while_not_noncanonical.txt
@@ -0,0 +1,3 @@
+while not var.nil?
+ 'foo'
+end
diff --git a/test/prism/fixtures/seattlerb/pctW_lineno.txt b/test/prism/fixtures/seattlerb/pctW_lineno.txt
new file mode 100644
index 0000000000..b222ff0174
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/pctW_lineno.txt
@@ -0,0 +1,5 @@
+%W(a\nb
+c d
+e\
+f
+gy h\y i\y)
diff --git a/test/prism/fixtures/seattlerb/pct_Q_backslash_nl.txt b/test/prism/fixtures/seattlerb/pct_Q_backslash_nl.txt
new file mode 100644
index 0000000000..d88e1fd21c
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/pct_Q_backslash_nl.txt
@@ -0,0 +1,2 @@
+%Q{ \
+}
diff --git a/test/prism/fixtures/seattlerb/pct_nl.txt b/test/prism/fixtures/seattlerb/pct_nl.txt
new file mode 100644
index 0000000000..2cee1cdd44
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/pct_nl.txt
@@ -0,0 +1,3 @@
+x = %
+
+
diff --git a/test/prism/fixtures/seattlerb/pct_w_heredoc_interp_nested.txt b/test/prism/fixtures/seattlerb/pct_w_heredoc_interp_nested.txt
new file mode 100644
index 0000000000..4e084661bf
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/pct_w_heredoc_interp_nested.txt
@@ -0,0 +1,4 @@
+%W( 1 #{<<A} 3
+2
+A
+ 4 5 )
diff --git a/test/prism/fixtures/seattlerb/pipe_semicolon.txt b/test/prism/fixtures/seattlerb/pipe_semicolon.txt
new file mode 100644
index 0000000000..e692cc434f
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/pipe_semicolon.txt
@@ -0,0 +1 @@
+a.b do | ; c | end
diff --git a/test/prism/fixtures/seattlerb/pipe_space.txt b/test/prism/fixtures/seattlerb/pipe_space.txt
new file mode 100644
index 0000000000..7f0df799b9
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/pipe_space.txt
@@ -0,0 +1 @@
+a.b do | | end
diff --git a/test/prism/fixtures/seattlerb/qWords_space.txt b/test/prism/fixtures/seattlerb/qWords_space.txt
new file mode 100644
index 0000000000..a8299ba3f8
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/qWords_space.txt
@@ -0,0 +1 @@
+%W( )
diff --git a/test/prism/fixtures/seattlerb/qsymbols.txt b/test/prism/fixtures/seattlerb/qsymbols.txt
new file mode 100644
index 0000000000..cb9ff09ae0
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/qsymbols.txt
@@ -0,0 +1 @@
+%I(a b c)
diff --git a/test/prism/fixtures/seattlerb/qsymbols_empty.txt b/test/prism/fixtures/seattlerb/qsymbols_empty.txt
new file mode 100644
index 0000000000..10a3279907
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/qsymbols_empty.txt
@@ -0,0 +1 @@
+%I()
diff --git a/test/prism/fixtures/seattlerb/qsymbols_empty_space.txt b/test/prism/fixtures/seattlerb/qsymbols_empty_space.txt
new file mode 100644
index 0000000000..819f16ad06
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/qsymbols_empty_space.txt
@@ -0,0 +1 @@
+%I( )
diff --git a/test/prism/fixtures/seattlerb/qsymbols_interp.txt b/test/prism/fixtures/seattlerb/qsymbols_interp.txt
new file mode 100644
index 0000000000..2f34883867
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/qsymbols_interp.txt
@@ -0,0 +1 @@
+%I(a b#{1+1} c)
diff --git a/test/prism/fixtures/seattlerb/quoted_symbol_hash_arg.txt b/test/prism/fixtures/seattlerb/quoted_symbol_hash_arg.txt
new file mode 100644
index 0000000000..4f1295ef18
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/quoted_symbol_hash_arg.txt
@@ -0,0 +1 @@
+puts 'a': {}
diff --git a/test/prism/fixtures/seattlerb/quoted_symbol_keys.txt b/test/prism/fixtures/seattlerb/quoted_symbol_keys.txt
new file mode 100644
index 0000000000..c6a946723d
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/quoted_symbol_keys.txt
@@ -0,0 +1 @@
+{ 'a': :b }
diff --git a/test/prism/fixtures/seattlerb/qw_escape.txt b/test/prism/fixtures/seattlerb/qw_escape.txt
new file mode 100644
index 0000000000..a94a0e5dcb
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/qw_escape.txt
@@ -0,0 +1 @@
+%q(\')
diff --git a/test/prism/fixtures/seattlerb/qw_escape_term.txt b/test/prism/fixtures/seattlerb/qw_escape_term.txt
new file mode 100644
index 0000000000..9734fc3421
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/qw_escape_term.txt
@@ -0,0 +1 @@
+%q|blah blah \| blah blah|
diff --git a/test/prism/fixtures/seattlerb/qwords_empty.txt b/test/prism/fixtures/seattlerb/qwords_empty.txt
new file mode 100644
index 0000000000..69cc6679d6
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/qwords_empty.txt
@@ -0,0 +1 @@
+%w()
diff --git a/test/prism/fixtures/seattlerb/read_escape_unicode_curlies.txt b/test/prism/fixtures/seattlerb/read_escape_unicode_curlies.txt
new file mode 100644
index 0000000000..427b94cc4d
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/read_escape_unicode_curlies.txt
@@ -0,0 +1 @@
+?\u{00a0}
diff --git a/test/prism/fixtures/seattlerb/read_escape_unicode_h4.txt b/test/prism/fixtures/seattlerb/read_escape_unicode_h4.txt
new file mode 100644
index 0000000000..71aa7a4347
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/read_escape_unicode_h4.txt
@@ -0,0 +1 @@
+?\u00a0
diff --git a/test/prism/fixtures/seattlerb/regexp.txt b/test/prism/fixtures/seattlerb/regexp.txt
new file mode 100644
index 0000000000..bc06458c5c
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/regexp.txt
@@ -0,0 +1,9 @@
+/wtf/
+
+/wtf/m
+
+/wtf/n
+
+/wtf/nm
+
+/wtf/nmnmnmnm
diff --git a/test/prism/fixtures/seattlerb/regexp_esc_C_slash.txt b/test/prism/fixtures/seattlerb/regexp_esc_C_slash.txt
new file mode 100644
index 0000000000..1fd9207c66
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/regexp_esc_C_slash.txt
@@ -0,0 +1 @@
+/\cC\d/
diff --git a/test/prism/fixtures/seattlerb/regexp_esc_u.txt b/test/prism/fixtures/seattlerb/regexp_esc_u.txt
new file mode 100644
index 0000000000..b91704fb0a
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/regexp_esc_u.txt
@@ -0,0 +1 @@
+/[\u0021-\u0027]/
diff --git a/test/prism/fixtures/seattlerb/regexp_escape_extended.txt b/test/prism/fixtures/seattlerb/regexp_escape_extended.txt
new file mode 100644
index 0000000000..73dcbab69c
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/regexp_escape_extended.txt
@@ -0,0 +1 @@
+/\“/
diff --git a/test/prism/fixtures/seattlerb/regexp_unicode_curlies.txt b/test/prism/fixtures/seattlerb/regexp_unicode_curlies.txt
new file mode 100644
index 0000000000..5a02bd92ca
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/regexp_unicode_curlies.txt
@@ -0,0 +1,3 @@
+/\u{c0de babe}/
+
+/\u{df}/
diff --git a/test/prism/fixtures/seattlerb/required_kwarg_no_value.txt b/test/prism/fixtures/seattlerb/required_kwarg_no_value.txt
new file mode 100644
index 0000000000..453bcbb33b
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/required_kwarg_no_value.txt
@@ -0,0 +1,2 @@
+def x a:, b:
+end
diff --git a/test/prism/fixtures/seattlerb/rescue_do_end_ensure_result.txt b/test/prism/fixtures/seattlerb/rescue_do_end_ensure_result.txt
new file mode 100644
index 0000000000..7049be66c5
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/rescue_do_end_ensure_result.txt
@@ -0,0 +1,5 @@
+proc do
+ :begin
+ensure
+ :ensure
+end.call
diff --git a/test/prism/fixtures/seattlerb/rescue_do_end_no_raise.txt b/test/prism/fixtures/seattlerb/rescue_do_end_no_raise.txt
new file mode 100644
index 0000000000..5f16ec2f15
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/rescue_do_end_no_raise.txt
@@ -0,0 +1,9 @@
+tap do
+ :begin
+rescue
+ :rescue
+else
+ :else
+ensure
+ :ensure
+end
diff --git a/test/prism/fixtures/seattlerb/rescue_do_end_raised.txt b/test/prism/fixtures/seattlerb/rescue_do_end_raised.txt
new file mode 100644
index 0000000000..d04215eb48
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/rescue_do_end_raised.txt
@@ -0,0 +1,5 @@
+tap do
+ raise
+ensure
+ :ensure
+end
diff --git a/test/prism/fixtures/seattlerb/rescue_do_end_rescued.txt b/test/prism/fixtures/seattlerb/rescue_do_end_rescued.txt
new file mode 100644
index 0000000000..4b377511f0
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/rescue_do_end_rescued.txt
@@ -0,0 +1,9 @@
+tap do
+ raise
+rescue
+ :rescue
+else
+ :else
+ensure
+ :ensure
+end
diff --git a/test/prism/fixtures/seattlerb/rescue_in_block.txt b/test/prism/fixtures/seattlerb/rescue_in_block.txt
new file mode 100644
index 0000000000..c6e834aa1e
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/rescue_in_block.txt
@@ -0,0 +1,4 @@
+blah do
+rescue
+ stuff
+end
diff --git a/test/prism/fixtures/seattlerb/rescue_parens.txt b/test/prism/fixtures/seattlerb/rescue_parens.txt
new file mode 100644
index 0000000000..f0eb4db417
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/rescue_parens.txt
@@ -0,0 +1 @@
+a (b rescue c)
diff --git a/test/prism/fixtures/seattlerb/return_call_assocs.txt b/test/prism/fixtures/seattlerb/return_call_assocs.txt
new file mode 100644
index 0000000000..34ea778f17
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/return_call_assocs.txt
@@ -0,0 +1,11 @@
+return 1, :z => 1
+
+return 1, :z => 1, :w => 2
+
+return y :z=>1
+
+return y z:1
+
+return y(z:1)
+
+return y(z=>1)
diff --git a/test/prism/fixtures/seattlerb/rhs_asgn.txt b/test/prism/fixtures/seattlerb/rhs_asgn.txt
new file mode 100644
index 0000000000..ca581031e2
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/rhs_asgn.txt
@@ -0,0 +1 @@
+42 => n
diff --git a/test/prism/fixtures/seattlerb/ruby21_numbers.txt b/test/prism/fixtures/seattlerb/ruby21_numbers.txt
new file mode 100644
index 0000000000..34ceb63a0c
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/ruby21_numbers.txt
@@ -0,0 +1 @@
+[1i, 2r, 3ri]
diff --git a/test/prism/fixtures/seattlerb/safe_attrasgn.txt b/test/prism/fixtures/seattlerb/safe_attrasgn.txt
new file mode 100644
index 0000000000..1279e02cfc
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/safe_attrasgn.txt
@@ -0,0 +1 @@
+a&.b = 1
diff --git a/test/prism/fixtures/seattlerb/safe_attrasgn_constant.txt b/test/prism/fixtures/seattlerb/safe_attrasgn_constant.txt
new file mode 100644
index 0000000000..3a17ac6bcf
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/safe_attrasgn_constant.txt
@@ -0,0 +1 @@
+a&.B = 1
diff --git a/test/prism/fixtures/seattlerb/safe_call.txt b/test/prism/fixtures/seattlerb/safe_call.txt
new file mode 100644
index 0000000000..8ecd27e0fe
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/safe_call.txt
@@ -0,0 +1 @@
+a&.b
diff --git a/test/prism/fixtures/seattlerb/safe_call_after_newline.txt b/test/prism/fixtures/seattlerb/safe_call_after_newline.txt
new file mode 100644
index 0000000000..58e3fba554
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/safe_call_after_newline.txt
@@ -0,0 +1,2 @@
+a
+&.b
diff --git a/test/prism/fixtures/seattlerb/safe_call_dot_parens.txt b/test/prism/fixtures/seattlerb/safe_call_dot_parens.txt
new file mode 100644
index 0000000000..5def076640
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/safe_call_dot_parens.txt
@@ -0,0 +1 @@
+a&.()
diff --git a/test/prism/fixtures/seattlerb/safe_call_newline.txt b/test/prism/fixtures/seattlerb/safe_call_newline.txt
new file mode 100644
index 0000000000..8778b46585
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/safe_call_newline.txt
@@ -0,0 +1,2 @@
+a&.b
+
diff --git a/test/prism/fixtures/seattlerb/safe_call_operator.txt b/test/prism/fixtures/seattlerb/safe_call_operator.txt
new file mode 100644
index 0000000000..f3fe2b0392
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/safe_call_operator.txt
@@ -0,0 +1 @@
+a&.> 1
diff --git a/test/prism/fixtures/seattlerb/safe_call_rhs_newline.txt b/test/prism/fixtures/seattlerb/safe_call_rhs_newline.txt
new file mode 100644
index 0000000000..d3b07b77b2
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/safe_call_rhs_newline.txt
@@ -0,0 +1,2 @@
+c = a&.b
+
diff --git a/test/prism/fixtures/seattlerb/safe_calls.txt b/test/prism/fixtures/seattlerb/safe_calls.txt
new file mode 100644
index 0000000000..eafeace500
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/safe_calls.txt
@@ -0,0 +1 @@
+a&.b&.c(1)
diff --git a/test/prism/fixtures/seattlerb/safe_op_asgn.txt b/test/prism/fixtures/seattlerb/safe_op_asgn.txt
new file mode 100644
index 0000000000..8915a1cccf
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/safe_op_asgn.txt
@@ -0,0 +1 @@
+a&.b += x 1
diff --git a/test/prism/fixtures/seattlerb/safe_op_asgn2.txt b/test/prism/fixtures/seattlerb/safe_op_asgn2.txt
new file mode 100644
index 0000000000..0960b2548b
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/safe_op_asgn2.txt
@@ -0,0 +1,2 @@
+a&.b ||=
+x;
diff --git a/test/prism/fixtures/seattlerb/slashy_newlines_within_string.txt b/test/prism/fixtures/seattlerb/slashy_newlines_within_string.txt
new file mode 100644
index 0000000000..421989c76f
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/slashy_newlines_within_string.txt
@@ -0,0 +1,7 @@
+puts "hello\
+ my\
+ dear\
+ friend"
+
+a + b
+
diff --git a/test/prism/fixtures/seattlerb/stabby_arg_no_paren.txt b/test/prism/fixtures/seattlerb/stabby_arg_no_paren.txt
new file mode 100644
index 0000000000..f16bed4ccf
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/stabby_arg_no_paren.txt
@@ -0,0 +1 @@
+->a{}
diff --git a/test/prism/fixtures/seattlerb/stabby_arg_opt_splat_arg_block_omfg.txt b/test/prism/fixtures/seattlerb/stabby_arg_opt_splat_arg_block_omfg.txt
new file mode 100644
index 0000000000..87a7c5dad3
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/stabby_arg_opt_splat_arg_block_omfg.txt
@@ -0,0 +1 @@
+->(b, c=1, *d, e, &f){}
diff --git a/test/prism/fixtures/seattlerb/stabby_block_iter_call.txt b/test/prism/fixtures/seattlerb/stabby_block_iter_call.txt
new file mode 100644
index 0000000000..5e9e3f5527
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/stabby_block_iter_call.txt
@@ -0,0 +1,4 @@
+x -> () do
+a.b do
+end
+end
diff --git a/test/prism/fixtures/seattlerb/stabby_block_iter_call_no_target_with_arg.txt b/test/prism/fixtures/seattlerb/stabby_block_iter_call_no_target_with_arg.txt
new file mode 100644
index 0000000000..7235394751
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/stabby_block_iter_call_no_target_with_arg.txt
@@ -0,0 +1,4 @@
+x -> () do
+a(1) do
+end
+end
diff --git a/test/prism/fixtures/seattlerb/stabby_block_kw.txt b/test/prism/fixtures/seattlerb/stabby_block_kw.txt
new file mode 100644
index 0000000000..74d9e0a328
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/stabby_block_kw.txt
@@ -0,0 +1 @@
+-> (k:42) { }
diff --git a/test/prism/fixtures/seattlerb/stabby_block_kw__required.txt b/test/prism/fixtures/seattlerb/stabby_block_kw__required.txt
new file mode 100644
index 0000000000..bd16ffa73c
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/stabby_block_kw__required.txt
@@ -0,0 +1 @@
+-> (k:) { }
diff --git a/test/prism/fixtures/seattlerb/stabby_proc_scope.txt b/test/prism/fixtures/seattlerb/stabby_proc_scope.txt
new file mode 100644
index 0000000000..1f7f9ff52b
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/stabby_proc_scope.txt
@@ -0,0 +1 @@
+->(a; b) {}
diff --git a/test/prism/fixtures/seattlerb/str_backslashes.txt b/test/prism/fixtures/seattlerb/str_backslashes.txt
new file mode 100644
index 0000000000..5fd6da361b
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/str_backslashes.txt
@@ -0,0 +1 @@
+x '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n'
diff --git a/test/prism/fixtures/seattlerb/str_double_double_escaped_newline.txt b/test/prism/fixtures/seattlerb/str_double_double_escaped_newline.txt
new file mode 100644
index 0000000000..2b022a55f6
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/str_double_double_escaped_newline.txt
@@ -0,0 +1 @@
+a "\\n";b
diff --git a/test/prism/fixtures/seattlerb/str_double_escaped_newline.txt b/test/prism/fixtures/seattlerb/str_double_escaped_newline.txt
new file mode 100644
index 0000000000..e439225344
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/str_double_escaped_newline.txt
@@ -0,0 +1 @@
+a "\n";b
diff --git a/test/prism/fixtures/seattlerb/str_double_newline.txt b/test/prism/fixtures/seattlerb/str_double_newline.txt
new file mode 100644
index 0000000000..2d439506ca
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/str_double_newline.txt
@@ -0,0 +1,2 @@
+a "
+";b
diff --git a/test/prism/fixtures/seattlerb/str_evstr.txt b/test/prism/fixtures/seattlerb/str_evstr.txt
new file mode 100644
index 0000000000..86c6d1526d
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/str_evstr.txt
@@ -0,0 +1 @@
+"a #{b}"
diff --git a/test/prism/fixtures/seattlerb/str_evstr_escape.txt b/test/prism/fixtures/seattlerb/str_evstr_escape.txt
new file mode 100644
index 0000000000..517dfd4778
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/str_evstr_escape.txt
@@ -0,0 +1 @@
+"a #{b}\302\275"
diff --git a/test/prism/fixtures/seattlerb/str_heredoc_interp.txt b/test/prism/fixtures/seattlerb/str_heredoc_interp.txt
new file mode 100644
index 0000000000..aa2613008c
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/str_heredoc_interp.txt
@@ -0,0 +1,5 @@
+<<""
+#{x}
+blah2
+
+
diff --git a/test/prism/fixtures/seattlerb/str_interp_ternary_or_label.txt b/test/prism/fixtures/seattlerb/str_interp_ternary_or_label.txt
new file mode 100644
index 0000000000..fe6637678f
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/str_interp_ternary_or_label.txt
@@ -0,0 +1 @@
+"#{a.b? ? ""+a+"": ""}"
diff --git a/test/prism/fixtures/seattlerb/str_lit_concat_bad_encodings.txt b/test/prism/fixtures/seattlerb/str_lit_concat_bad_encodings.txt
new file mode 100644
index 0000000000..f4eb3971bb
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/str_lit_concat_bad_encodings.txt
@@ -0,0 +1,2 @@
+"\xE3\xD3\x8B\xE3\x83\xBC\x83\xE3\x83\xE3\x82\xB3\xA3\x82\x99" \
+ "\xE3\x83\xB3\xE3\x83\x8F\xE3\x82\x9A\xC3\xBD;foo@bar.com"
diff --git a/test/prism/fixtures/seattlerb/str_newline_hash_line_number.txt b/test/prism/fixtures/seattlerb/str_newline_hash_line_number.txt
new file mode 100644
index 0000000000..9c8f702000
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/str_newline_hash_line_number.txt
@@ -0,0 +1,2 @@
+"\n\n\n\n#"
+1
diff --git a/test/prism/fixtures/seattlerb/str_pct_Q_nested.txt b/test/prism/fixtures/seattlerb/str_pct_Q_nested.txt
new file mode 100644
index 0000000000..1f3d0613e5
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/str_pct_Q_nested.txt
@@ -0,0 +1 @@
+%Q[before [#{nest}] after]
diff --git a/test/prism/fixtures/seattlerb/str_pct_nested_nested.txt b/test/prism/fixtures/seattlerb/str_pct_nested_nested.txt
new file mode 100644
index 0000000000..cb12415215
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/str_pct_nested_nested.txt
@@ -0,0 +1 @@
+%{ { #{ "#{1}" } } }
diff --git a/test/prism/fixtures/seattlerb/str_pct_q.txt b/test/prism/fixtures/seattlerb/str_pct_q.txt
new file mode 100644
index 0000000000..65d71197c9
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/str_pct_q.txt
@@ -0,0 +1 @@
+%q{a b c}
diff --git a/test/prism/fixtures/seattlerb/str_single_double_escaped_newline.txt b/test/prism/fixtures/seattlerb/str_single_double_escaped_newline.txt
new file mode 100644
index 0000000000..2ff0aec111
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/str_single_double_escaped_newline.txt
@@ -0,0 +1 @@
+a '\\n';b
diff --git a/test/prism/fixtures/seattlerb/str_single_escaped_newline.txt b/test/prism/fixtures/seattlerb/str_single_escaped_newline.txt
new file mode 100644
index 0000000000..5abb8d6334
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/str_single_escaped_newline.txt
@@ -0,0 +1 @@
+a '\n';b
diff --git a/test/prism/fixtures/seattlerb/str_single_newline.txt b/test/prism/fixtures/seattlerb/str_single_newline.txt
new file mode 100644
index 0000000000..1033cc7e96
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/str_single_newline.txt
@@ -0,0 +1,2 @@
+a '
+';b
diff --git a/test/prism/fixtures/seattlerb/str_str.txt b/test/prism/fixtures/seattlerb/str_str.txt
new file mode 100644
index 0000000000..388d777dc2
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/str_str.txt
@@ -0,0 +1 @@
+"a #{'b'}"
diff --git a/test/prism/fixtures/seattlerb/str_str_str.txt b/test/prism/fixtures/seattlerb/str_str_str.txt
new file mode 100644
index 0000000000..d64e01dc5d
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/str_str_str.txt
@@ -0,0 +1 @@
+"a #{'b'} c"
diff --git a/test/prism/fixtures/seattlerb/super_arg.txt b/test/prism/fixtures/seattlerb/super_arg.txt
new file mode 100644
index 0000000000..1b19ecd51c
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/super_arg.txt
@@ -0,0 +1 @@
+super 42
diff --git a/test/prism/fixtures/seattlerb/symbol_empty.txt b/test/prism/fixtures/seattlerb/symbol_empty.txt
new file mode 100644
index 0000000000..cbb260bb4e
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/symbol_empty.txt
@@ -0,0 +1 @@
+:''
diff --git a/test/prism/fixtures/seattlerb/symbol_list.txt b/test/prism/fixtures/seattlerb/symbol_list.txt
new file mode 100644
index 0000000000..d357195184
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/symbol_list.txt
@@ -0,0 +1 @@
+%I[#{a} #{b}]
diff --git a/test/prism/fixtures/seattlerb/symbols.txt b/test/prism/fixtures/seattlerb/symbols.txt
new file mode 100644
index 0000000000..3ec930ce66
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/symbols.txt
@@ -0,0 +1 @@
+%i(a b c)
diff --git a/test/prism/fixtures/seattlerb/symbols_empty.txt b/test/prism/fixtures/seattlerb/symbols_empty.txt
new file mode 100644
index 0000000000..840948efb2
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/symbols_empty.txt
@@ -0,0 +1 @@
+%i()
diff --git a/test/prism/fixtures/seattlerb/symbols_empty_space.txt b/test/prism/fixtures/seattlerb/symbols_empty_space.txt
new file mode 100644
index 0000000000..16c2e68a2b
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/symbols_empty_space.txt
@@ -0,0 +1 @@
+%i( )
diff --git a/test/prism/fixtures/seattlerb/symbols_interp.txt b/test/prism/fixtures/seattlerb/symbols_interp.txt
new file mode 100644
index 0000000000..63116eb632
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/symbols_interp.txt
@@ -0,0 +1 @@
+%i(a b#{1+1} c)
diff --git a/test/prism/fixtures/seattlerb/thingy.txt b/test/prism/fixtures/seattlerb/thingy.txt
new file mode 100644
index 0000000000..5aa598c4be
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/thingy.txt
@@ -0,0 +1,3 @@
+f.(42)
+
+f::(42)
diff --git a/test/prism/fixtures/seattlerb/uminus_float.txt b/test/prism/fixtures/seattlerb/uminus_float.txt
new file mode 100644
index 0000000000..1344bfd9db
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/uminus_float.txt
@@ -0,0 +1 @@
+-0.0
diff --git a/test/prism/fixtures/seattlerb/unary_minus.txt b/test/prism/fixtures/seattlerb/unary_minus.txt
new file mode 100644
index 0000000000..66af866f85
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/unary_minus.txt
@@ -0,0 +1 @@
+-a
diff --git a/test/prism/fixtures/seattlerb/unary_plus.txt b/test/prism/fixtures/seattlerb/unary_plus.txt
new file mode 100644
index 0000000000..daea40b71e
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/unary_plus.txt
@@ -0,0 +1 @@
++a
diff --git a/test/prism/fixtures/seattlerb/unary_plus_on_literal.txt b/test/prism/fixtures/seattlerb/unary_plus_on_literal.txt
new file mode 100644
index 0000000000..752331df47
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/unary_plus_on_literal.txt
@@ -0,0 +1 @@
++:a
diff --git a/test/prism/fixtures/seattlerb/unary_tilde.txt b/test/prism/fixtures/seattlerb/unary_tilde.txt
new file mode 100644
index 0000000000..f0a507b437
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/unary_tilde.txt
@@ -0,0 +1 @@
+~a
diff --git a/test/prism/fixtures/seattlerb/utf8_bom.txt b/test/prism/fixtures/seattlerb/utf8_bom.txt
new file mode 100644
index 0000000000..c8e9e1cbae
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/utf8_bom.txt
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby -w
+p 0
+
diff --git a/test/prism/fixtures/seattlerb/when_splat.txt b/test/prism/fixtures/seattlerb/when_splat.txt
new file mode 100644
index 0000000000..6b79f5dad0
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/when_splat.txt
@@ -0,0 +1 @@
+case a; when *b then; end
diff --git a/test/prism/fixtures/seattlerb/words_interp.txt b/test/prism/fixtures/seattlerb/words_interp.txt
new file mode 100644
index 0000000000..f71486495b
--- /dev/null
+++ b/test/prism/fixtures/seattlerb/words_interp.txt
@@ -0,0 +1 @@
+%W(#{1}b)
diff --git a/test/prism/fixtures/single_method_call_with_bang.txt b/test/prism/fixtures/single_method_call_with_bang.txt
new file mode 100644
index 0000000000..929efb3053
--- /dev/null
+++ b/test/prism/fixtures/single_method_call_with_bang.txt
@@ -0,0 +1 @@
+foo!
diff --git a/test/prism/fixtures/single_quote_heredocs.txt b/test/prism/fixtures/single_quote_heredocs.txt
new file mode 100644
index 0000000000..0122b2726c
--- /dev/null
+++ b/test/prism/fixtures/single_quote_heredocs.txt
@@ -0,0 +1,3 @@
+<<-'EOS'
+ cd L:\Work\MG3710IQPro\Develop
+EOS
diff --git a/test/prism/fixtures/spanning_heredoc.txt b/test/prism/fixtures/spanning_heredoc.txt
new file mode 100644
index 0000000000..d09cb11b1f
--- /dev/null
+++ b/test/prism/fixtures/spanning_heredoc.txt
@@ -0,0 +1,63 @@
+# test regex, string, and lists that span a heredoc thanks to an escaped newline
+
+# ripper incorrectly creates a "b\nb" token instead of two separate string tokens
+pp <<-A.gsub(/b\
+a
+A
+b/, "")
+
+# ripper incorrectly creates a "d\nd" token instead of two separate string tokens
+pp <<-A, "d\
+c
+A
+d"
+
+# ripper gets this right
+pp <<-A, %q[f\
+e
+A
+f]
+
+# ripper incorrectly creates a "h\nh" token instead of two separate string tokens
+pp <<-A, %Q[h\
+g
+A
+h]
+
+# ripper can't parse this successfully, though ruby runs it correctly
+pp <<-A, %w[j\
+i
+A
+j]
+
+# ripper can't parse this successfully, though ruby runs it correctly
+# TODO: prism does not include the "\n" in "l\nl" in the AST like ruby does
+pp <<-A, %W[l\
+k
+A
+l]
+
+# ripper can't parse this successfully, though ruby runs it correctly
+pp <<-A, %i[n\
+m
+A
+n]
+
+# ripper gets this one wrong in the same way that prism does ...
+# TODO: prism does not include the "\n" in "p\np" in the AST like ruby does
+pp <<-A, %I[p\
+o
+A
+p]
+
+<<A; /\
+A
+(?<a>)/ =~ ''
+
+<<A; :'a
+A
+b'
+
+<<A; :"a
+A
+b"
diff --git a/test/prism/fixtures/spanning_heredoc_newlines.txt b/test/prism/fixtures/spanning_heredoc_newlines.txt
new file mode 100644
index 0000000000..32c9943aeb
--- /dev/null
+++ b/test/prism/fixtures/spanning_heredoc_newlines.txt
@@ -0,0 +1,23 @@
+<<A+%
+A
+
+
+<<A+%r
+A
+
+
+<<A+%q
+A
+
+
+<<A+%Q
+A
+
+
+<<A+%s
+A
+
+
+<<A+%x
+A
+
diff --git a/test/prism/fixtures/strings.txt b/test/prism/fixtures/strings.txt
new file mode 100644
index 0000000000..2ce8b738a3
--- /dev/null
+++ b/test/prism/fixtures/strings.txt
@@ -0,0 +1,105 @@
+%%abc%
+
+%^abc^
+
+%&abc&
+
+%*abc*
+
+%_abc_
+
+%+abc+
+
+%-abc-
+
+%:abc:
+
+%;abc;
+
+%'abc'
+
+%~abc~
+
+%?abc?
+
+%w{ }
+
+%/abc/
+
+%`abc`
+
+"#@@foo"
+
+%\abc\
+
+%{aaa #{bbb} ccc}
+
+%[foo[]]
+
+"foo" +
+#
+"bar"
+
+%q{abc}
+
+%s[abc]
+
+%{abc}
+
+''
+
+"abc"
+
+"#@---"
+
+"aaa #{bbb} ccc"
+
+'abc'
+
+%w[a b c]
+
+%w[a[] b[[]] c[]]
+
+%w[foo\ bar \#{1}]
+
+%w[foo\ bar baz]
+
+%W[a b#{c}d e]
+
+%W[a b c]
+
+%w[
+ a
+ b
+ c
+]
+
+'\' foo \' bar'
+
+'\\ foo \\ bar'
+
+"#$foo"
+
+"#@foo"
+
+"\x7 \x23 \x61"
+
+"\7 \43 \141"
+
+%[abc]
+
+%(abc)
+
+%@abc@
+
+%$abc$
+
+?a
+
+?a "a"
+
+%Q{abc}
+
+%^#$^#
+
+%@#@#
diff --git a/test/prism/fixtures/super.txt b/test/prism/fixtures/super.txt
new file mode 100644
index 0000000000..cd7aaf992e
--- /dev/null
+++ b/test/prism/fixtures/super.txt
@@ -0,0 +1,17 @@
+super
+
+super()
+
+super(1)
+
+super(1, 2, 3)
+
+super &:foo
+
+super(&:foo)
+
+super {}
+
+super(1, 2, 3) {}
+
+super(1, 2, 3, &:foo)
diff --git a/test/prism/fixtures/symbols.txt b/test/prism/fixtures/symbols.txt
new file mode 100644
index 0000000000..7563eb874f
--- /dev/null
+++ b/test/prism/fixtures/symbols.txt
@@ -0,0 +1,93 @@
+:'abc'
+
+:"#{var}"
+
+:"abc#{1}"
+
+[:Î¥, :ά, :Å—, :Ï]
+
+:-@
+
+:-
+
+:%
+
+:|
+
+:+@
+
+:+
+
+:/
+
+:**
+
+:*
+
+:~@
+
+[1, 1.0, 1r, 1i]
+
+:~
+
+:a
+
+%i[a b c]
+
+%i[a b#{1} #{2}c d#{3}f]
+
+%I[a b#{1} #{2}c d#{3}f]
+
+:@@a
+
+:ðŸ‘
+
+%i[a\b]
+
+:$a
+
+:@a
+
+:do
+
+:&
+
+:`
+
+:!@
+
+:!~
+
+:!
+
+:[]
+
+:[]=
+
+:^
+
+:==
+
+:===
+
+:=~
+
+:>=
+
+:>>
+
+:>
+
+:<=>
+
+:<=
+
+:<<
+
+:<
+
+:__LINE__
+
+:__FILE__
+
+:__ENCODING__
diff --git a/test/prism/fixtures/ternary_operator.txt b/test/prism/fixtures/ternary_operator.txt
new file mode 100644
index 0000000000..79d2d7d837
--- /dev/null
+++ b/test/prism/fixtures/ternary_operator.txt
@@ -0,0 +1,15 @@
+a ? b : c
+
+a ? defined? b : defined? c
+
+empty??true:nil
+
+empty??false:nil
+
+empty??nil:nil
+
+a??nil:nil
+
+a ?var1 : var2
+
+nil??_a =2:1
diff --git a/test/prism/fixtures/tilde_heredocs.txt b/test/prism/fixtures/tilde_heredocs.txt
new file mode 100644
index 0000000000..cca47ef00b
--- /dev/null
+++ b/test/prism/fixtures/tilde_heredocs.txt
@@ -0,0 +1,97 @@
+<<~EOF
+ a
+#{1}
+ a
+EOF
+
+<<~EOF
+ a
+EOF
+
+<<~EOF
+ a
+ b
+ c
+EOF
+
+<<~EOF
+ #{1} a
+EOF
+
+<<~EOF
+ a #{1}
+EOF
+
+<<~EOF
+ a
+ #{1}
+EOF
+
+<<~EOF
+ a
+ #{1}
+EOF
+
+<<~EOF
+ a
+ b
+EOF
+
+<<~EOF
+ a
+ b
+EOF
+
+<<~EOF
+ a
+ b
+EOF
+
+<<~'EOF'
+ a #{1}
+EOF
+
+<<~EOF
+ a
+ b
+EOF
+
+<<~EOF
+ a
+ b
+EOF
+
+<<~EOF
+ a
+ b
+EOF
+
+<<~EOF
+ a
+
+ b
+EOF
+
+<<~EOF
+ a
+
+ b
+EOF
+
+<<~EOF
+ a
+
+
+
+ b
+EOF
+
+<<~EOF
+
+ #{1}a
+ EOF
+
+<<~EOT
+ #{1}
+ b
+EOT
diff --git a/test/prism/fixtures/undef.txt b/test/prism/fixtures/undef.txt
new file mode 100644
index 0000000000..129c349433
--- /dev/null
+++ b/test/prism/fixtures/undef.txt
@@ -0,0 +1,17 @@
+undef a
+
+undef a, b
+
+undef if
+
+undef <=>
+
+undef :a
+
+undef :a, :b, :c
+
+undef :'abc'
+
+undef :"abc#{1}"
+
+undef Constant
diff --git a/test/prism/fixtures/unescaping.txt b/test/prism/fixtures/unescaping.txt
new file mode 100644
index 0000000000..e2da5a696c
--- /dev/null
+++ b/test/prism/fixtures/unescaping.txt
@@ -0,0 +1,9 @@
+["\c#{1}"]
+
+/\c#{1}/
+
+"\c#{1}"
+
+<<~HERE
+ \c#{1}
+HERE
diff --git a/test/prism/fixtures/unless.txt b/test/prism/fixtures/unless.txt
new file mode 100644
index 0000000000..678d58991b
--- /dev/null
+++ b/test/prism/fixtures/unless.txt
@@ -0,0 +1,14 @@
+unless true; 1; end
+
+unless true
+1 else 2 end
+
+1 unless true
+
+tap { break unless true }
+
+tap { next unless true }
+
+return unless true
+
+foo :a, :b unless bar?
diff --git a/test/prism/fixtures/unparser/LICENSE b/test/prism/fixtures/unparser/LICENSE
new file mode 100644
index 0000000000..44863d7afb
--- /dev/null
+++ b/test/prism/fixtures/unparser/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2013 Markus Schirp
+
+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.
diff --git a/test/prism/fixtures/unparser/corpus/literal/alias.txt b/test/prism/fixtures/unparser/corpus/literal/alias.txt
new file mode 100644
index 0000000000..fb06a295e8
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/alias.txt
@@ -0,0 +1,2 @@
+alias $foo $bar
+alias :foo :bar
diff --git a/test/prism/fixtures/unparser/corpus/literal/assignment.txt b/test/prism/fixtures/unparser/corpus/literal/assignment.txt
new file mode 100644
index 0000000000..84a74e8928
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/assignment.txt
@@ -0,0 +1,53 @@
+$a = 1
+($a, $b) = [1, 2]
+((a,), b) = 1
+(*a) = []
+(*foo) = [1, 2]
+(@@a, @@b) = [1, 2]
+(@a, @b) = [1, 2]
+(a, (b, c)) = [1, [2, 3]]
+(a, *) = [1, 2]
+(a, *foo) = [1, 2]
+(a, b) = [1, 2]
+(a, b) = foo
+(a,) = foo
+(a.foo, a.bar) = [1, 2]
+(a[*foo], a[1]) = [1, 2]
+(a[0], a[1]) = [1, 2]
+(*c.foo) = 1
+::Foo = ::Bar
+@@a = 1
+@a = 1
+CONST = 1
+Name::Spaced::CONST = 1
+a = ((b, c) = 1)
+a = 1
+foo = foo()
+foo.[]=()
+foo.[]=(1, 2)
+foo.[]=true
+foo[*index] = value
+foo[1..2] = value
+foo[] = 1
+foo[a, b] = value
+foo[index] = value
+x = %()
+x.x=%()
+x[%()] = bar
+a[%()] ||= bar
+@a ||= %()
+x = <<-HEREDOC
+ #{}
+HEREDOC
+x.x=<<-HEREDOC
+ #{}
+HEREDOC
+x[] = <<-HEREDOC
+ #{}
+HEREDOC
+a[<<-HEREDOC] ||= bar
+ #{}
+HEREDOC
+@a ||= <<-HEREDOC
+ #{}
+HEREDOC
diff --git a/test/prism/fixtures/unparser/corpus/literal/block.txt b/test/prism/fixtures/unparser/corpus/literal/block.txt
new file mode 100644
index 0000000000..b2baf1dc12
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/block.txt
@@ -0,0 +1,96 @@
+foo {
+}
+foo { |a|
+}
+foo { |a,|
+}
+foo { |a,; x|
+}
+foo { |a, b|
+}
+foo(1) {
+ nil
+}
+foo { |a, *b|
+ nil
+}
+foo { |a, *|
+ nil
+}
+foo {
+ bar
+}
+foo.bar { |(a, b), c|
+ d
+}
+foo.bar { |*a; b|
+}
+foo.bar { |a; b|
+}
+foo.bar { |; a, b|
+}
+foo.bar { |*|
+ d
+}
+foo.bar { |(*)|
+ d
+}
+foo.bar { |((*))|
+ d
+}
+foo.bar { |(a, (*))|
+ d
+}
+foo.bar { |(a, b)|
+ d
+}
+foo.bar {
+}.baz
+m do
+rescue Exception => e
+end
+m do
+ foo
+rescue Exception => bar
+ bar
+end
+m do
+ bar
+rescue SomeError, *bar
+ baz
+end
+m do
+ bar
+rescue SomeError, *bar => exception
+ baz
+end
+m do
+ bar
+rescue *bar
+ baz
+end
+m do
+ bar
+rescue LoadError
+end
+m do
+ bar
+rescue
+else
+ baz
+end
+m do
+ bar
+rescue *bar => exception
+ baz
+end
+m do
+ensure
+end
+m do
+rescue
+ensure
+end
+bar {
+ _1 + _2
+}
diff --git a/test/prism/fixtures/unparser/corpus/literal/case.txt b/test/prism/fixtures/unparser/corpus/literal/case.txt
new file mode 100644
index 0000000000..c455fd7c39
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/case.txt
@@ -0,0 +1,37 @@
+case
+when bar
+ baz
+when baz
+ bar
+end
+case foo
+when bar
+when baz
+ bar
+end
+case foo
+when bar
+ baz
+when baz
+ bar
+end
+case foo
+when bar, baz
+ :other
+end
+case foo
+when *bar
+ :value
+end
+case foo
+when bar
+ baz
+else
+ :foo
+end
+case foo
+when *bar | baz
+end
+case foo
+when *bar.baz=1
+end
diff --git a/test/prism/fixtures/unparser/corpus/literal/class.txt b/test/prism/fixtures/unparser/corpus/literal/class.txt
new file mode 100644
index 0000000000..f0198625e9
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/class.txt
@@ -0,0 +1,35 @@
+class A
+end
+
+class << a
+end
+
+class << a
+ b
+end
+
+class A::B
+end
+
+class A::B::C
+end
+
+class A < B
+end
+
+class A < B::C
+end
+
+class A::B < C::D
+end
+
+class A
+ include(B.new)
+
+ def foo
+ :bar
+ end
+end
+
+class ::A
+end
diff --git a/test/prism/fixtures/unparser/corpus/literal/def.txt b/test/prism/fixtures/unparser/corpus/literal/def.txt
new file mode 100644
index 0000000000..61339bd4a6
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/def.txt
@@ -0,0 +1,134 @@
+def foo
+ a
+rescue
+ b
+else
+ c
+ensure
+ d
+end
+
+def foo
+ a rescue b
+rescue
+ b
+else
+ c
+ensure
+ d
+end
+
+def foo(bar:, baz:)
+end
+
+def foo
+end
+
+def foo
+ bar
+end
+
+def foo
+ foo
+rescue
+ bar
+ensure
+ baz
+end
+
+def foo
+ bar
+ensure
+ baz
+end
+
+def foo
+ bar
+rescue
+ baz
+end
+
+def foo(bar)
+ bar
+end
+
+def foo(bar, baz)
+ bar
+end
+
+def foo(bar = ())
+ bar
+end
+
+def foo(bar = (baz; nil))
+end
+
+def foo(bar = true)
+ bar
+end
+
+def foo(bar, baz = true)
+ bar
+end
+
+def foo(bar: 1)
+end
+
+def foo(bar: baz)
+end
+
+def foo(bar: bar())
+end
+
+def foo(*)
+ bar
+end
+
+def foo(*bar)
+ bar
+end
+
+def foo(bar, *baz)
+ bar
+end
+
+def foo(baz = true, *bor)
+ bar
+end
+
+def foo(baz = true, *bor, &block)
+ bar
+end
+
+def foo(bar, baz = true, *bor)
+ bar
+end
+
+def foo(&block)
+ bar
+end
+
+def foo(bar, &block)
+ bar
+end
+
+def foo
+ bar
+ baz
+end
+
+def f(((a)))
+end
+
+def foo(bar:, baz: "value")
+end
+
+def f
+ <<-HEREDOC
+ #{}
+ HEREDOC
+end
+
+def f
+ %()
+end
diff --git a/test/prism/fixtures/unparser/corpus/literal/defined.txt b/test/prism/fixtures/unparser/corpus/literal/defined.txt
new file mode 100644
index 0000000000..65e7c370fd
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/defined.txt
@@ -0,0 +1,3 @@
+defined?(@foo)
+defined?(Foo)
+defined?(((a, b) = [1, 2]))
diff --git a/test/prism/fixtures/unparser/corpus/literal/defs.txt b/test/prism/fixtures/unparser/corpus/literal/defs.txt
new file mode 100644
index 0000000000..b70aa9efc5
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/defs.txt
@@ -0,0 +1,40 @@
+def self.foo
+end
+
+def self.foo
+ bar
+end
+
+def self.foo
+ bar
+ baz
+end
+
+def Foo.bar
+ bar
+end
+
+def (foo { |bar|
+}).bar
+ bar
+end
+
+def (foo(1)).bar
+ bar
+end
+
+def (Foo::Bar.baz).bar
+ baz
+end
+
+def (Foo::Bar).bar
+ baz
+end
+
+def Foo.bar
+ baz
+end
+
+def foo.bar
+ baz
+end
diff --git a/test/prism/fixtures/unparser/corpus/literal/dstr.txt b/test/prism/fixtures/unparser/corpus/literal/dstr.txt
new file mode 100644
index 0000000000..8a912d28ed
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/dstr.txt
@@ -0,0 +1,37 @@
+if true
+ "#{}a"
+end
+if true
+ <<-HEREDOC
+a
+#{}a
+b
+ HEREDOC
+ x
+end
+<<-HEREDOC
+\#{}\#{}
+#{}
+#{}
+#{}
+HEREDOC
+<<-HEREDOC rescue nil
+#{}
+a
+HEREDOC
+"a#$1"
+"a#$a"
+"a#@a"
+"a#@@a"
+if true
+ return <<-HEREDOC
+ #{42}
+ HEREDOC
+end
+foo(<<-HEREDOC)
+ #{bar}
+HEREDOC
+foo(<<-HEREDOC) { |x|
+ #{bar}
+HEREDOC
+}
diff --git a/test/prism/fixtures/unparser/corpus/literal/empty.txt b/test/prism/fixtures/unparser/corpus/literal/empty.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/empty.txt
diff --git a/test/prism/fixtures/unparser/corpus/literal/empty_begin.txt b/test/prism/fixtures/unparser/corpus/literal/empty_begin.txt
new file mode 100644
index 0000000000..6a452c185a
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/empty_begin.txt
@@ -0,0 +1 @@
+()
diff --git a/test/prism/fixtures/unparser/corpus/literal/flipflop.txt b/test/prism/fixtures/unparser/corpus/literal/flipflop.txt
new file mode 100644
index 0000000000..139904a53f
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/flipflop.txt
@@ -0,0 +1,10 @@
+if ((i == 4)..(i == 4))
+ foo
+end
+if ((i == 4)...(i == 4))
+ foo
+end
+if ..foo
+end
+if foo..;
+end
diff --git a/test/prism/fixtures/unparser/corpus/literal/for.txt b/test/prism/fixtures/unparser/corpus/literal/for.txt
new file mode 100644
index 0000000000..4c19a352d9
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/for.txt
@@ -0,0 +1,12 @@
+bar(for a in bar do
+ baz
+end)
+for a in bar do
+ baz
+end
+for (a, *b) in bar do
+ baz
+end
+for (a, b) in bar do
+ baz
+end
diff --git a/test/prism/fixtures/unparser/corpus/literal/hookexe.txt b/test/prism/fixtures/unparser/corpus/literal/hookexe.txt
new file mode 100644
index 0000000000..08f14f47b3
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/hookexe.txt
@@ -0,0 +1,7 @@
+BEGIN {
+ foo
+}
+bar
+END {
+ baz
+}
diff --git a/test/prism/fixtures/unparser/corpus/literal/if.txt b/test/prism/fixtures/unparser/corpus/literal/if.txt
new file mode 100644
index 0000000000..0c13801f9e
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/if.txt
@@ -0,0 +1,36 @@
+if /foo/
+ bar
+end
+if 3
+ 9
+end
+if 4
+ 5
+else
+ 6
+end
+unless 3
+ nil
+end
+unless 3
+ 9
+end
+if foo
+end
+
+module A
+ foo = bar if foo
+end
+
+module B
+ foo = bar unless foo
+end
+unless foo
+ foo = bar
+end
+if foo { |pair|
+ pair
+}
+ pair = :foo
+ foo
+end
diff --git a/test/prism/fixtures/unparser/corpus/literal/kwbegin.txt b/test/prism/fixtures/unparser/corpus/literal/kwbegin.txt
new file mode 100644
index 0000000000..6cc1e74ca6
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/kwbegin.txt
@@ -0,0 +1,80 @@
+begin
+rescue
+end
+
+begin
+ensure
+end
+
+begin
+ a
+end
+
+begin
+ a
+rescue
+ b
+end
+
+begin
+ a
+ b
+rescue
+ b
+end
+
+begin
+rescue A
+end
+
+begin
+rescue A => foo
+end
+
+begin
+ a
+rescue A
+ b
+rescue B
+ c
+ensure
+ d
+end
+
+begin
+ begin
+ foo
+ bar
+ rescue
+ end
+rescue
+ baz
+ bar
+end
+
+begin
+ raise(Exception) rescue foo = bar
+rescue Exception
+end
+
+begin
+ foo
+rescue => bar
+ bar
+end
+
+begin
+ foo
+rescue Exception, Other => bar
+ bar
+end
+
+begin
+ bar
+rescue SomeError, *bar => exception
+ baz
+end
+
+class << self
+ undef :bar rescue nil
+end
diff --git a/test/prism/fixtures/unparser/corpus/literal/lambda.txt b/test/prism/fixtures/unparser/corpus/literal/lambda.txt
new file mode 100644
index 0000000000..4eb722dad1
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/lambda.txt
@@ -0,0 +1,13 @@
+lambda {
+}
+lambda { |a, b|
+ a
+}
+->() {
+}
+->(a) {
+}
+->(a, b) {
+}
+->(a, b; c) {
+}
diff --git a/test/prism/fixtures/unparser/corpus/literal/literal.txt b/test/prism/fixtures/unparser/corpus/literal/literal.txt
new file mode 100644
index 0000000000..2fc7cd1d79
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/literal.txt
@@ -0,0 +1,91 @@
+{ "foo" => <<-HEREDOC, "bar" => :baz }
+ #{}
+HEREDOC
+{ "foo" => %(), "bar" => :baz }
+["foo", %()]
+a(<<-HEREDOC).a
+ #{}
+HEREDOC
+a(%()).a
+{ "foo" => <<-HEREDOC, **baz }
+ #{}
+HEREDOC
+{ "foo" => %(), **baz }
+"#@a #@@a #$a"
+0
+++1
+1
+1
+1r
+1.5r
+1.3r
+5i
+-5i
+0.6i
+-0.6i
+1000000000000000000000000000000i
+1ri
+"foo" "bar"
+"foobar #{baz}"
+"foo#{1}bar"
+"\\\\#{}"
+"#{}\#{}"
+"\#{}#{}"
+"foo\\\#{@bar}"
+"\""
+"foo bar"
+"foo\nbar"
+`foo`
+`foo#{@bar}`
+`)`
+`\``
+`"`
+:foo
+:"A B"
+:foo
+:"A B"
+:"A\"B"
+:""
+/foo/
+/[^-+',.\/:@[:alnum:]\[\]]+/
+/foo#{@bar}/
+/foo#{@bar}/imx
+/#{"\u0000"}/
+/\n/
+/\n/
+/\n/x
+/\/\//x
+:"foo#{bar}baz"
+:"#{"foo"}"
+(0.0 / 0.0)..1
+1..(0.0 / 0.0)
+(0.0 / 0.0)..100
+-0.1
+0.1
+[1, 2]
+[1, (), n2]
+[1]
+[]
+[1, *@foo]
+[*@foo, 1]
+[*@foo, *@baz]
+{}
+{ () => () }
+{ 1 => 2 }
+{ 1 => 2, 3 => 4 }
+{ a: (1 rescue foo), b: 2 }
+{ a: 1, b: 2 }
+{ a: :a }
+{ :"a b" => 1 }
+{ :-@ => 1 }
+"#{}
+#{}\na"
+foo {
+ "#{}
+#{}\na"
+}
+:"a\\
+b"
+` x
+#{foo}
+#`
diff --git a/test/prism/fixtures/unparser/corpus/literal/module.txt b/test/prism/fixtures/unparser/corpus/literal/module.txt
new file mode 100644
index 0000000000..cec03f3bfd
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/module.txt
@@ -0,0 +1,16 @@
+module A
+end
+
+module A::B
+end
+
+module A::B::C
+end
+
+module A
+ include(B.new)
+
+ def foo
+ :bar
+ end
+end
diff --git a/test/prism/fixtures/unparser/corpus/literal/opasgn.txt b/test/prism/fixtures/unparser/corpus/literal/opasgn.txt
new file mode 100644
index 0000000000..5858d773d0
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/opasgn.txt
@@ -0,0 +1,24 @@
+a += 2
+a -= 2
+a **= 2
+a *= 2
+a /= 2
+a &&= b
+a ||= 2
+(a ||= 2).bar
+(h ||= {})[k] = v
+a.b += 2
+a.b -= 2
+a.b **= 2
+a.b *= 2
+a.b /= 2
+a.b &&= b
+a.b ||= 2
+a[b] += 2
+a[b] -= 2
+a[b] **= 2
+a[b] *= 2
+a[b] /= 2
+a[b] &&= b
+a[b] ||= 2
+foo.A += 1
diff --git a/test/prism/fixtures/unparser/corpus/literal/pattern.txt b/test/prism/fixtures/unparser/corpus/literal/pattern.txt
new file mode 100644
index 0000000000..7cfaa4dc67
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/pattern.txt
@@ -0,0 +1,41 @@
+case foo
+in A[1, 2, *a, 3] then
+ true
+in [1, 2, ] then
+ y
+in A(x:) then
+ true
+in {**a} then
+ true
+in {} if true then
+ true
+in [x, y, *] then
+ true
+in {a: 1, aa: 2} then
+ true
+in {} then
+ true
+in {**nil} then
+ true
+in {"a": 1} then
+ true
+in 1 | 2 then
+ true
+in 1 => a then
+ true
+in ^x then
+ true
+in 1
+in 2 then
+ true
+else
+ true
+end
+case foo
+in A[1, 2, *a, 3]
+end
+case foo
+in A
+else
+end
+1 in [a]
diff --git a/test/prism/fixtures/unparser/corpus/literal/pragma.txt b/test/prism/fixtures/unparser/corpus/literal/pragma.txt
new file mode 100644
index 0000000000..4f6dd71b38
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/pragma.txt
@@ -0,0 +1,4 @@
+__ENCODING__
+__FILE__
+__LINE__
+__dir__
diff --git a/test/prism/fixtures/unparser/corpus/literal/range.txt b/test/prism/fixtures/unparser/corpus/literal/range.txt
new file mode 100644
index 0000000000..eb1f3874c0
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/range.txt
@@ -0,0 +1,4 @@
+(1..)
+1..2
+(1...)
+1...2
diff --git a/test/prism/fixtures/unparser/corpus/literal/rescue.txt b/test/prism/fixtures/unparser/corpus/literal/rescue.txt
new file mode 100644
index 0000000000..a787816808
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/rescue.txt
@@ -0,0 +1,3 @@
+foo rescue bar
+foo rescue return bar
+x = (foo rescue return bar)
diff --git a/test/prism/fixtures/unparser/corpus/literal/send.txt b/test/prism/fixtures/unparser/corpus/literal/send.txt
new file mode 100644
index 0000000000..1e9c2a94be
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/send.txt
@@ -0,0 +1,84 @@
+module A
+ foo ||= ((a, _) = b)
+end
+
+module A
+ local = 1
+ local.bar
+end
+class A
+end.bar
+module A
+end.bar
+begin
+rescue
+end.bar
+case (def foo
+end; :bar)
+when bar
+end.baz
+case foo
+when bar
+end.baz
+class << self
+end.bar
+def self.foo
+end.bar
+def foo
+end.bar
+until foo
+end.bar
+while foo
+end.bar
+loop {
+}.bar
+if foo
+end.baz
+(/bar/ =~ :foo).foo
+(1..2).max
+(foo =~ /bar/).foo
+/bar/ =~ :foo
+/bar/ =~ foo
+1..2.max
+A.foo
+FOO()
+a&.b
+a.foo
+foo
+foo << (bar * baz)
+foo =~ /bar/
+foo(&(foo || bar))
+foo(&block)
+foo(*args, &block)
+foo(*arguments)
+foo(1, 2)
+foo(bar)
+foo(bar, *args)
+foo(foo =~ /bar/)
+foo.bar(&baz)
+foo.bar(*arga, foo, *argb)
+foo.bar(*args)
+foo.bar(*args, foo)
+foo.bar(:baz, &baz)
+foo.bar(baz: boz)
+foo.bar(foo, "baz" => boz)
+foo.bar(foo, *args)
+foo.bar(foo, *args, &block)
+foo.bar(foo, {})
+foo.bar({ foo: boz }, boz)
+foo.bar=:baz
+foo(a: b)
+foo.&(a: b)
+foo.&(**a)
+foo[*baz]
+foo[1, 2]
+foo[]
+self.foo
+self.foo=:bar
+(a + b) / (c - d)
+(a + b) / c.-(e, f)
+(a + b) / c.-(*f)
+x(**foo)
+foo&.!
+foo.~(b)
+a&.+(b)
diff --git a/test/prism/fixtures/unparser/corpus/literal/since/27.txt b/test/prism/fixtures/unparser/corpus/literal/since/27.txt
new file mode 100644
index 0000000000..c332f9e48e
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/since/27.txt
@@ -0,0 +1,4 @@
+-> {
+ _1 + _2
+}
+(..1)
diff --git a/test/prism/fixtures/unparser/corpus/literal/since/30.txt b/test/prism/fixtures/unparser/corpus/literal/since/30.txt
new file mode 100644
index 0000000000..b73328a4b0
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/since/30.txt
@@ -0,0 +1,4 @@
+1 => [a]
+1 => [*]
+1 in [*, 42, *]
+1 in [*, a, *foo]
diff --git a/test/prism/fixtures/unparser/corpus/literal/since/31.txt b/test/prism/fixtures/unparser/corpus/literal/since/31.txt
new file mode 100644
index 0000000000..504eb94d5b
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/since/31.txt
@@ -0,0 +1,7 @@
+def foo(&)
+ bar(&)
+end
+
+def foo(a, &)
+ bar(&)
+end
diff --git a/test/prism/fixtures/unparser/corpus/literal/since/32.txt b/test/prism/fixtures/unparser/corpus/literal/since/32.txt
new file mode 100644
index 0000000000..b8e096d8fc
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/since/32.txt
@@ -0,0 +1,11 @@
+def foo(argument, **)
+ bar(argument, **)
+end
+
+def foo(argument, *)
+ bar(argument, *)
+end
+
+def foo(**)
+ { default: 1, ** }
+end
diff --git a/test/prism/fixtures/unparser/corpus/literal/singletons.txt b/test/prism/fixtures/unparser/corpus/literal/singletons.txt
new file mode 100644
index 0000000000..496e6a41ce
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/singletons.txt
@@ -0,0 +1,4 @@
+false
+nil
+self
+true
diff --git a/test/prism/fixtures/unparser/corpus/literal/super.txt b/test/prism/fixtures/unparser/corpus/literal/super.txt
new file mode 100644
index 0000000000..0e73e6f052
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/super.txt
@@ -0,0 +1,21 @@
+super
+super()
+super(a)
+super(a, b)
+super(&block)
+super(a, &block)
+super(a {
+ foo
+})
+super {
+ foo
+}
+super(a) {
+ foo
+}
+super() {
+ foo
+}
+super(a, b) {
+ foo
+}
diff --git a/test/prism/fixtures/unparser/corpus/literal/unary.txt b/test/prism/fixtures/unparser/corpus/literal/unary.txt
new file mode 100644
index 0000000000..77685cb71d
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/unary.txt
@@ -0,0 +1,8 @@
+!1
+!(!1)
+!(!(foo || bar))
+!(!1).baz
+~a
+-a
++a
+-(-a).foo
diff --git a/test/prism/fixtures/unparser/corpus/literal/undef.txt b/test/prism/fixtures/unparser/corpus/literal/undef.txt
new file mode 100644
index 0000000000..a65d8d0cc4
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/undef.txt
@@ -0,0 +1,2 @@
+undef :foo
+undef :foo, :bar
diff --git a/test/prism/fixtures/unparser/corpus/literal/variables.txt b/test/prism/fixtures/unparser/corpus/literal/variables.txt
new file mode 100644
index 0000000000..1de938f376
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/variables.txt
@@ -0,0 +1,10 @@
+a
+@a
+@@a
+$a
+$1
+$`
+CONST
+SCOPED::CONST
+::TOPLEVEL
+::TOPLEVEL::CONST
diff --git a/test/prism/fixtures/unparser/corpus/literal/while.txt b/test/prism/fixtures/unparser/corpus/literal/while.txt
new file mode 100644
index 0000000000..19a60ef5ff
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/literal/while.txt
@@ -0,0 +1,73 @@
+module A
+ foo { |bar|
+ while foo
+ foo = bar
+ end
+ }
+end
+
+def foo
+ foo = bar while foo != baz
+end
+
+module A
+ foo = bar while foo
+end
+
+module A
+ foo = bar until foo
+end
+
+module A
+ while foo
+ foo = bar
+ end
+end
+
+module A
+ each { |baz|
+ while foo
+ foo = bar
+ end
+ }
+end
+
+module A
+ each { |foo|
+ while foo
+ foo = bar
+ end
+ }
+end
+x = (begin
+ foo
+end while baz)
+begin
+ foo
+end while baz
+begin
+ foo
+ bar
+end until baz
+begin
+ foo
+ bar
+end while baz
+while false
+end
+while false
+ 3
+end
+while (foo {
+})
+ :body
+end
+until false
+end
+until false
+ 3
+end
+until (foo {
+})
+ :body
+end
diff --git a/test/prism/fixtures/unparser/corpus/semantic/and.txt b/test/prism/fixtures/unparser/corpus/semantic/and.txt
new file mode 100644
index 0000000000..43d1712445
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/semantic/and.txt
@@ -0,0 +1,8 @@
+a...b or c...d
+a...b and c...d
+
+if a...b or c...d
+end
+
+if a...b and c...d
+end
diff --git a/test/prism/fixtures/unparser/corpus/semantic/block.txt b/test/prism/fixtures/unparser/corpus/semantic/block.txt
new file mode 100644
index 0000000000..5891690025
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/semantic/block.txt
@@ -0,0 +1,26 @@
+foo do
+end
+
+foo do
+rescue
+end
+
+foo do
+ nil rescue nil
+ nil
+end
+
+foo do |a|
+end
+
+foo(<<-DOC) do |a|
+ b
+DOC
+ a
+end
+
+foo(<<-DOC) do
+ b
+DOC
+ a
+end
diff --git a/test/prism/fixtures/unparser/corpus/semantic/def.txt b/test/prism/fixtures/unparser/corpus/semantic/def.txt
new file mode 100644
index 0000000000..7574619392
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/semantic/def.txt
@@ -0,0 +1,7 @@
+def foo
+ (a - b)
+end
+
+def foo
+ a rescue Exception
+end
diff --git a/test/prism/fixtures/unparser/corpus/semantic/dstr.txt b/test/prism/fixtures/unparser/corpus/semantic/dstr.txt
new file mode 100644
index 0000000000..919e736077
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/semantic/dstr.txt
@@ -0,0 +1,127 @@
+<<DOC
+DOC
+
+<<'DOC'
+DOC
+
+<<~DOC
+DOC
+
+<<~'DOC'
+DOC
+
+<<DOC
+ a
+DOC
+
+<<'DOC'
+ a
+DOC
+
+<<DOC
+ a
+ #{}
+DOC
+
+<<~DOC
+ a
+ #{}
+DOC
+
+<<~DOC
+ a
+ #{}
+ b
+DOC
+
+<<~DOC
+ a
+ b
+DOC
+
+<<'DOC'
+a
+
+b
+DOC
+
+<<'DOC'
+ a
+
+ b
+DOC
+
+<<'DOC'
+ a\nb
+DOC
+
+<<DOC
+#{}a
+ #{}a
+DOC
+
+<<DOC
+ #{}
+ \#{}
+DOC
+
+<<DOC
+ a#{}b
+ c
+DOC
+
+<<~DOC
+ #{}
+DOC
+
+if true
+ <<~DOC
+ #{}
+ DOC
+end
+
+if true
+ <<~DOC
+ b#{}
+ DOC
+end
+
+if true
+ <<~DOC
+ #{}a
+ DOC
+end
+
+if true
+ <<-'DOC'
+ a
+
+ b
+ DOC
+end
+
+"#{}a"
+
+%(\n"#{}"\n)
+
+%Q(-\n"#{}"\n)
+
+"a
+#{}
+b"
+
+"a\n#{}
+b"
+
+"a
+#{}\nb"
+
+'a' \
+"#{}"
+
+"" "" ""
+
+"a#{@a}" "b"
+"a#@a" "b"
+"a#$a" "b"
+"a#@@a" "b"
diff --git a/test/prism/fixtures/unparser/corpus/semantic/kwbegin.txt b/test/prism/fixtures/unparser/corpus/semantic/kwbegin.txt
new file mode 100644
index 0000000000..d275a96a5c
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/semantic/kwbegin.txt
@@ -0,0 +1,42 @@
+begin
+rescue
+end
+
+begin
+rescue
+else
+end
+
+begin
+ a
+end
+
+begin
+ a
+rescue
+ b
+end
+
+begin
+ a
+ b
+rescue
+ b
+end
+
+begin
+rescue A
+else
+end
+
+begin; rescue A; else; end
+
+begin
+ a
+rescue A
+ b
+rescue B
+ c
+ensure
+ d
+end
diff --git a/test/prism/fixtures/unparser/corpus/semantic/literal.txt b/test/prism/fixtures/unparser/corpus/semantic/literal.txt
new file mode 100644
index 0000000000..c424db5a53
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/semantic/literal.txt
@@ -0,0 +1,14 @@
+1.0r
+-0r
+0x1
+1_000
+1e10
+10e10000000000
+-10e10000000000
+?c
+%r(/)
+%r(\))
+%r(#{@bar}baz)
+10.2e10000000000
+-10.2e10000000000
+w(foo bar)
diff --git a/test/prism/fixtures/unparser/corpus/semantic/opasgn.txt b/test/prism/fixtures/unparser/corpus/semantic/opasgn.txt
new file mode 100644
index 0000000000..8b4bc5d239
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/semantic/opasgn.txt
@@ -0,0 +1 @@
+y["#{42}\n"] += "#{42}\n"
diff --git a/test/prism/fixtures/unparser/corpus/semantic/send.txt b/test/prism/fixtures/unparser/corpus/semantic/send.txt
new file mode 100644
index 0000000000..a65b27d2f2
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/semantic/send.txt
@@ -0,0 +1,6 @@
+foo
+foo(1)
+
+a.===(b).c == d
+
+a == d.c.===(c)
diff --git a/test/prism/fixtures/unparser/corpus/semantic/undef.txt b/test/prism/fixtures/unparser/corpus/semantic/undef.txt
new file mode 100644
index 0000000000..47debc3114
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/semantic/undef.txt
@@ -0,0 +1,2 @@
+undef foo
+undef foo, bar
diff --git a/test/prism/fixtures/unparser/corpus/semantic/while.txt b/test/prism/fixtures/unparser/corpus/semantic/while.txt
new file mode 100644
index 0000000000..a55dcc52fc
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/semantic/while.txt
@@ -0,0 +1,25 @@
+a until b? {}
+
+until b? {}
+ a
+end
+
+foo = bar while foo
+
+a until b && a { }
+
+while a = b
+ a
+end
+
+a until b(<<-FOO) do
+FOO
+ c
+end
+
+module A
+ foo = exp
+ while foo
+ foo = bar
+ end
+end
diff --git a/test/prism/fixtures/until.txt b/test/prism/fixtures/until.txt
new file mode 100644
index 0000000000..652fc8c5a7
--- /dev/null
+++ b/test/prism/fixtures/until.txt
@@ -0,0 +1,13 @@
+until true; 1; end
+
+1 until true
+
+tap { break until true }
+
+tap { next until true }
+
+return until true
+
+foo :a, :b until bar?
+
+foo while bar in baz
diff --git a/test/prism/fixtures/variables.txt b/test/prism/fixtures/variables.txt
new file mode 100644
index 0000000000..1545c30c80
--- /dev/null
+++ b/test/prism/fixtures/variables.txt
@@ -0,0 +1,47 @@
+@@abc
+
+@@abc = 1
+
+@@foo, @@bar = 1
+
+@@foo = 1, 2
+
+$abc = 1
+
+$abc
+
+@abc
+
+@abc = 1
+
+a
+
+abc = 1
+
+$foo, $bar = 1
+
+$foo = 1, 2
+
+@foo, @bar = 1
+
+@foo = 1, 2
+
+foo = 1; foo = 1, 2
+
+foo = 1, 2
+
+foo, * = 1, 2
+
+foo, = 1, 2
+
+foo, *bar = 1, 2
+
+foo, (bar, baz) = 1, [2, 3]
+
+foo = *bar
+
+Foo = 1, 2
+
+(a; b; c)
+
+a, (b, c), d = []
diff --git a/test/prism/fixtures/while.txt b/test/prism/fixtures/while.txt
new file mode 100644
index 0000000000..b776f755ee
--- /dev/null
+++ b/test/prism/fixtures/while.txt
@@ -0,0 +1,23 @@
+while true; 1; end
+
+1 while true
+
+tap { break while true }
+
+tap { next while true }
+
+return while true
+
+foo :a, :b while bar?
+
+tap { while def self.foo a = tap do end; end; break; end }
+
+tap { while class Foo a = tap do end; end; break; end }
+
+tap { while class << self; tap do end; end; break; end }
+
+tap { while class << self; a = tap do end; end; break; end }
+
+while def foo = bar do end; end
+
+foo while bar in baz
diff --git a/test/prism/fixtures/whitequark/LICENSE b/test/prism/fixtures/whitequark/LICENSE
new file mode 100644
index 0000000000..971310e3d6
--- /dev/null
+++ b/test/prism/fixtures/whitequark/LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2013-2016 whitequark <whitequark@whitequark.org>
+
+Parts of the source are derived from ruby_parser:
+Copyright (c) Ryan Davis, seattle.rb
+
+MIT License
+
+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.
diff --git a/test/prism/fixtures/whitequark/__ENCODING__.txt b/test/prism/fixtures/whitequark/__ENCODING__.txt
new file mode 100644
index 0000000000..d6debf2f92
--- /dev/null
+++ b/test/prism/fixtures/whitequark/__ENCODING__.txt
@@ -0,0 +1 @@
+__ENCODING__
diff --git a/test/prism/fixtures/whitequark/__ENCODING___legacy_.txt b/test/prism/fixtures/whitequark/__ENCODING___legacy_.txt
new file mode 100644
index 0000000000..d6debf2f92
--- /dev/null
+++ b/test/prism/fixtures/whitequark/__ENCODING___legacy_.txt
@@ -0,0 +1 @@
+__ENCODING__
diff --git a/test/prism/fixtures/whitequark/alias.txt b/test/prism/fixtures/whitequark/alias.txt
new file mode 100644
index 0000000000..e33b120022
--- /dev/null
+++ b/test/prism/fixtures/whitequark/alias.txt
@@ -0,0 +1 @@
+alias :foo bar
diff --git a/test/prism/fixtures/whitequark/alias_gvar.txt b/test/prism/fixtures/whitequark/alias_gvar.txt
new file mode 100644
index 0000000000..b975d97f8e
--- /dev/null
+++ b/test/prism/fixtures/whitequark/alias_gvar.txt
@@ -0,0 +1,3 @@
+alias $a $+
+
+alias $a $b
diff --git a/test/prism/fixtures/whitequark/ambiuous_quoted_label_in_ternary_operator.txt b/test/prism/fixtures/whitequark/ambiuous_quoted_label_in_ternary_operator.txt
new file mode 100644
index 0000000000..9b2e3afad5
--- /dev/null
+++ b/test/prism/fixtures/whitequark/ambiuous_quoted_label_in_ternary_operator.txt
@@ -0,0 +1 @@
+a ? b & '': nil
diff --git a/test/prism/fixtures/whitequark/and.txt b/test/prism/fixtures/whitequark/and.txt
new file mode 100644
index 0000000000..43fa6a65cd
--- /dev/null
+++ b/test/prism/fixtures/whitequark/and.txt
@@ -0,0 +1,3 @@
+foo && bar
+
+foo and bar
diff --git a/test/prism/fixtures/whitequark/and_asgn.txt b/test/prism/fixtures/whitequark/and_asgn.txt
new file mode 100644
index 0000000000..a979265914
--- /dev/null
+++ b/test/prism/fixtures/whitequark/and_asgn.txt
@@ -0,0 +1,3 @@
+foo.a &&= 1
+
+foo[0, 1] &&= 2
diff --git a/test/prism/fixtures/whitequark/and_or_masgn.txt b/test/prism/fixtures/whitequark/and_or_masgn.txt
new file mode 100644
index 0000000000..e346041604
--- /dev/null
+++ b/test/prism/fixtures/whitequark/and_or_masgn.txt
@@ -0,0 +1,3 @@
+foo && (a, b = bar)
+
+foo || (a, b = bar)
diff --git a/test/prism/fixtures/whitequark/anonymous_blockarg.txt b/test/prism/fixtures/whitequark/anonymous_blockarg.txt
new file mode 100644
index 0000000000..e3eaaad6ce
--- /dev/null
+++ b/test/prism/fixtures/whitequark/anonymous_blockarg.txt
@@ -0,0 +1 @@
+def foo(&); bar(&); end
diff --git a/test/prism/fixtures/whitequark/arg.txt b/test/prism/fixtures/whitequark/arg.txt
new file mode 100644
index 0000000000..b1984ad5c4
--- /dev/null
+++ b/test/prism/fixtures/whitequark/arg.txt
@@ -0,0 +1,3 @@
+def f(foo); end
+
+def f(foo, bar); end
diff --git a/test/prism/fixtures/whitequark/arg_duplicate_ignored.txt b/test/prism/fixtures/whitequark/arg_duplicate_ignored.txt
new file mode 100644
index 0000000000..0f5cc33961
--- /dev/null
+++ b/test/prism/fixtures/whitequark/arg_duplicate_ignored.txt
@@ -0,0 +1,3 @@
+def foo(_, _); end
+
+def foo(_a, _a); end
diff --git a/test/prism/fixtures/whitequark/arg_label.txt b/test/prism/fixtures/whitequark/arg_label.txt
new file mode 100644
index 0000000000..82db416cb4
--- /dev/null
+++ b/test/prism/fixtures/whitequark/arg_label.txt
@@ -0,0 +1,6 @@
+def foo
+ a:b end
+
+def foo() a:b end
+
+f { || a:b }
diff --git a/test/prism/fixtures/whitequark/arg_scope.txt b/test/prism/fixtures/whitequark/arg_scope.txt
new file mode 100644
index 0000000000..6c67ab72e3
--- /dev/null
+++ b/test/prism/fixtures/whitequark/arg_scope.txt
@@ -0,0 +1 @@
+lambda{|;a|a}
diff --git a/test/prism/fixtures/whitequark/args.txt b/test/prism/fixtures/whitequark/args.txt
new file mode 100644
index 0000000000..773be477d3
--- /dev/null
+++ b/test/prism/fixtures/whitequark/args.txt
@@ -0,0 +1,63 @@
+def f &b; end
+
+def f (((a))); end
+
+def f ((*)); end
+
+def f ((*, p)); end
+
+def f ((*r)); end
+
+def f ((*r, p)); end
+
+def f ((a, *)); end
+
+def f ((a, *, p)); end
+
+def f ((a, *r)); end
+
+def f ((a, *r, p)); end
+
+def f ((a, a1)); end
+
+def f (foo: 1, &b); end
+
+def f (foo: 1, bar: 2, **baz, &b); end
+
+def f **baz, &b; end
+
+def f *, **; end
+
+def f *r, &b; end
+
+def f *r, p, &b; end
+
+def f ; end
+
+def f a, &b; end
+
+def f a, *r, &b; end
+
+def f a, *r, p, &b; end
+
+def f a, o=1, &b; end
+
+def f a, o=1, *r, &b; end
+
+def f a, o=1, *r, p, &b; end
+
+def f a, o=1, p, &b; end
+
+def f foo:
+; end
+
+def f foo: -1
+; end
+
+def f o=1, &b; end
+
+def f o=1, *r, &b; end
+
+def f o=1, *r, p, &b; end
+
+def f o=1, p, &b; end
diff --git a/test/prism/fixtures/whitequark/args_args_assocs.txt b/test/prism/fixtures/whitequark/args_args_assocs.txt
new file mode 100644
index 0000000000..445f899442
--- /dev/null
+++ b/test/prism/fixtures/whitequark/args_args_assocs.txt
@@ -0,0 +1,3 @@
+fun(foo, :foo => 1)
+
+fun(foo, :foo => 1, &baz)
diff --git a/test/prism/fixtures/whitequark/args_args_assocs_comma.txt b/test/prism/fixtures/whitequark/args_args_assocs_comma.txt
new file mode 100644
index 0000000000..b566a59037
--- /dev/null
+++ b/test/prism/fixtures/whitequark/args_args_assocs_comma.txt
@@ -0,0 +1 @@
+foo[bar, :baz => 1,]
diff --git a/test/prism/fixtures/whitequark/args_args_comma.txt b/test/prism/fixtures/whitequark/args_args_comma.txt
new file mode 100644
index 0000000000..80770716dd
--- /dev/null
+++ b/test/prism/fixtures/whitequark/args_args_comma.txt
@@ -0,0 +1 @@
+foo[bar,]
diff --git a/test/prism/fixtures/whitequark/args_args_star.txt b/test/prism/fixtures/whitequark/args_args_star.txt
new file mode 100644
index 0000000000..d4dc9cc579
--- /dev/null
+++ b/test/prism/fixtures/whitequark/args_args_star.txt
@@ -0,0 +1,3 @@
+fun(foo, *bar)
+
+fun(foo, *bar, &baz)
diff --git a/test/prism/fixtures/whitequark/args_assocs_comma.txt b/test/prism/fixtures/whitequark/args_assocs_comma.txt
new file mode 100644
index 0000000000..15e5cd65dc
--- /dev/null
+++ b/test/prism/fixtures/whitequark/args_assocs_comma.txt
@@ -0,0 +1 @@
+foo[:baz => 1,]
diff --git a/test/prism/fixtures/whitequark/args_block_pass.txt b/test/prism/fixtures/whitequark/args_block_pass.txt
new file mode 100644
index 0000000000..35d7d23885
--- /dev/null
+++ b/test/prism/fixtures/whitequark/args_block_pass.txt
@@ -0,0 +1 @@
+fun(&bar)
diff --git a/test/prism/fixtures/whitequark/args_cmd.txt b/test/prism/fixtures/whitequark/args_cmd.txt
new file mode 100644
index 0000000000..dd0c8891d0
--- /dev/null
+++ b/test/prism/fixtures/whitequark/args_cmd.txt
@@ -0,0 +1 @@
+fun(f bar)
diff --git a/test/prism/fixtures/whitequark/args_star.txt b/test/prism/fixtures/whitequark/args_star.txt
new file mode 100644
index 0000000000..ce1e6f8465
--- /dev/null
+++ b/test/prism/fixtures/whitequark/args_star.txt
@@ -0,0 +1,3 @@
+fun(*bar)
+
+fun(*bar, &baz)
diff --git a/test/prism/fixtures/whitequark/array_assocs.txt b/test/prism/fixtures/whitequark/array_assocs.txt
new file mode 100644
index 0000000000..fcecfcdefc
--- /dev/null
+++ b/test/prism/fixtures/whitequark/array_assocs.txt
@@ -0,0 +1,3 @@
+[ 1 => 2 ]
+
+[ 1, 2 => 3 ]
diff --git a/test/prism/fixtures/whitequark/array_plain.txt b/test/prism/fixtures/whitequark/array_plain.txt
new file mode 100644
index 0000000000..44e2ace7e5
--- /dev/null
+++ b/test/prism/fixtures/whitequark/array_plain.txt
@@ -0,0 +1 @@
+[1, 2]
diff --git a/test/prism/fixtures/whitequark/array_splat.txt b/test/prism/fixtures/whitequark/array_splat.txt
new file mode 100644
index 0000000000..144c1eb124
--- /dev/null
+++ b/test/prism/fixtures/whitequark/array_splat.txt
@@ -0,0 +1,5 @@
+[*foo]
+
+[1, *foo, 2]
+
+[1, *foo]
diff --git a/test/prism/fixtures/whitequark/array_symbols.txt b/test/prism/fixtures/whitequark/array_symbols.txt
new file mode 100644
index 0000000000..a9f9df0404
--- /dev/null
+++ b/test/prism/fixtures/whitequark/array_symbols.txt
@@ -0,0 +1 @@
+%i[foo bar]
diff --git a/test/prism/fixtures/whitequark/array_symbols_empty.txt b/test/prism/fixtures/whitequark/array_symbols_empty.txt
new file mode 100644
index 0000000000..da3a89ee9b
--- /dev/null
+++ b/test/prism/fixtures/whitequark/array_symbols_empty.txt
@@ -0,0 +1,3 @@
+%I()
+
+%i[]
diff --git a/test/prism/fixtures/whitequark/array_symbols_interp.txt b/test/prism/fixtures/whitequark/array_symbols_interp.txt
new file mode 100644
index 0000000000..d4950d0c05
--- /dev/null
+++ b/test/prism/fixtures/whitequark/array_symbols_interp.txt
@@ -0,0 +1,3 @@
+%I[foo #{bar}]
+
+%I[foo#{bar}]
diff --git a/test/prism/fixtures/whitequark/array_words.txt b/test/prism/fixtures/whitequark/array_words.txt
new file mode 100644
index 0000000000..a07380cadc
--- /dev/null
+++ b/test/prism/fixtures/whitequark/array_words.txt
@@ -0,0 +1 @@
+%w[foo bar]
diff --git a/test/prism/fixtures/whitequark/array_words_empty.txt b/test/prism/fixtures/whitequark/array_words_empty.txt
new file mode 100644
index 0000000000..7568263f4a
--- /dev/null
+++ b/test/prism/fixtures/whitequark/array_words_empty.txt
@@ -0,0 +1,3 @@
+%W()
+
+%w[]
diff --git a/test/prism/fixtures/whitequark/array_words_interp.txt b/test/prism/fixtures/whitequark/array_words_interp.txt
new file mode 100644
index 0000000000..1460f7dc03
--- /dev/null
+++ b/test/prism/fixtures/whitequark/array_words_interp.txt
@@ -0,0 +1,3 @@
+%W[foo #{bar}]
+
+%W[foo #{bar}foo#@baz]
diff --git a/test/prism/fixtures/whitequark/asgn_cmd.txt b/test/prism/fixtures/whitequark/asgn_cmd.txt
new file mode 100644
index 0000000000..81f8cc1c8d
--- /dev/null
+++ b/test/prism/fixtures/whitequark/asgn_cmd.txt
@@ -0,0 +1,3 @@
+foo = bar = m foo
+
+foo = m foo
diff --git a/test/prism/fixtures/whitequark/asgn_mrhs.txt b/test/prism/fixtures/whitequark/asgn_mrhs.txt
new file mode 100644
index 0000000000..f0b0055e55
--- /dev/null
+++ b/test/prism/fixtures/whitequark/asgn_mrhs.txt
@@ -0,0 +1,5 @@
+foo = *bar
+
+foo = bar, 1
+
+foo = baz, *bar
diff --git a/test/prism/fixtures/whitequark/back_ref.txt b/test/prism/fixtures/whitequark/back_ref.txt
new file mode 100644
index 0000000000..03166e10ee
--- /dev/null
+++ b/test/prism/fixtures/whitequark/back_ref.txt
@@ -0,0 +1 @@
+$+
diff --git a/test/prism/fixtures/whitequark/bang.txt b/test/prism/fixtures/whitequark/bang.txt
new file mode 100644
index 0000000000..6cf9410cf5
--- /dev/null
+++ b/test/prism/fixtures/whitequark/bang.txt
@@ -0,0 +1 @@
+!foo
diff --git a/test/prism/fixtures/whitequark/bang_cmd.txt b/test/prism/fixtures/whitequark/bang_cmd.txt
new file mode 100644
index 0000000000..0a5252c001
--- /dev/null
+++ b/test/prism/fixtures/whitequark/bang_cmd.txt
@@ -0,0 +1 @@
+!m foo
diff --git a/test/prism/fixtures/whitequark/begin_cmdarg.txt b/test/prism/fixtures/whitequark/begin_cmdarg.txt
new file mode 100644
index 0000000000..a5873668e9
--- /dev/null
+++ b/test/prism/fixtures/whitequark/begin_cmdarg.txt
@@ -0,0 +1 @@
+p begin 1.times do 1 end end
diff --git a/test/prism/fixtures/whitequark/beginless_erange_after_newline.txt b/test/prism/fixtures/whitequark/beginless_erange_after_newline.txt
new file mode 100644
index 0000000000..ae6c75564a
--- /dev/null
+++ b/test/prism/fixtures/whitequark/beginless_erange_after_newline.txt
@@ -0,0 +1,2 @@
+foo
+...100
diff --git a/test/prism/fixtures/whitequark/beginless_irange_after_newline.txt b/test/prism/fixtures/whitequark/beginless_irange_after_newline.txt
new file mode 100644
index 0000000000..bfc8d5e5e8
--- /dev/null
+++ b/test/prism/fixtures/whitequark/beginless_irange_after_newline.txt
@@ -0,0 +1,2 @@
+foo
+..100
diff --git a/test/prism/fixtures/whitequark/beginless_range.txt b/test/prism/fixtures/whitequark/beginless_range.txt
new file mode 100644
index 0000000000..ef52703b8a
--- /dev/null
+++ b/test/prism/fixtures/whitequark/beginless_range.txt
@@ -0,0 +1,3 @@
+...100
+
+..100
diff --git a/test/prism/fixtures/whitequark/blockarg.txt b/test/prism/fixtures/whitequark/blockarg.txt
new file mode 100644
index 0000000000..63552e97be
--- /dev/null
+++ b/test/prism/fixtures/whitequark/blockarg.txt
@@ -0,0 +1 @@
+def f(&block); end
diff --git a/test/prism/fixtures/whitequark/blockargs.txt b/test/prism/fixtures/whitequark/blockargs.txt
new file mode 100644
index 0000000000..cdd2c4f331
--- /dev/null
+++ b/test/prism/fixtures/whitequark/blockargs.txt
@@ -0,0 +1,71 @@
+f{ }
+
+f{ | | }
+
+f{ |&b| }
+
+f{ |**baz, &b| }
+
+f{ |*, &b| }
+
+f{ |*r, p, &b| }
+
+f{ |*s, &b| }
+
+f{ |*s| }
+
+f{ |*| }
+
+f{ |;
+a
+| }
+
+f{ |;a| }
+
+f{ |a, &b| }
+
+f{ |a, *, &b| }
+
+f{ |a, *r, p, &b| }
+
+f{ |a, *s, &b| }
+
+f{ |a, *s| }
+
+f{ |a, *| }
+
+f{ |a, b,| }
+
+f{ |a, c| }
+
+f{ |a, o=1, &b| }
+
+f{ |a, o=1, *r, p, &b| }
+
+f{ |a, o=1, o1=2, *r, &b| }
+
+f{ |a, o=1, p, &b| }
+
+f{ |a,| }
+
+f{ |a| }
+
+f{ |a| }
+
+f{ |a| }
+
+f{ |foo: 1, &b| }
+
+f{ |foo: 1, bar: 2, **baz, &b| }
+
+f{ |foo:| }
+
+f{ |o=1, &b| }
+
+f{ |o=1, *r, &b| }
+
+f{ |o=1, *r, p, &b| }
+
+f{ |o=1, p, &b| }
+
+f{ || }
diff --git a/test/prism/fixtures/whitequark/bug_435.txt b/test/prism/fixtures/whitequark/bug_435.txt
new file mode 100644
index 0000000000..3e4e0d5abd
--- /dev/null
+++ b/test/prism/fixtures/whitequark/bug_435.txt
@@ -0,0 +1 @@
+"#{-> foo {}}"
diff --git a/test/prism/fixtures/whitequark/bug_447.txt b/test/prism/fixtures/whitequark/bug_447.txt
new file mode 100644
index 0000000000..7da59bbc2f
--- /dev/null
+++ b/test/prism/fixtures/whitequark/bug_447.txt
@@ -0,0 +1,3 @@
+m [] do end
+
+m [], 1 do end
diff --git a/test/prism/fixtures/whitequark/bug_452.txt b/test/prism/fixtures/whitequark/bug_452.txt
new file mode 100644
index 0000000000..8b41dd6027
--- /dev/null
+++ b/test/prism/fixtures/whitequark/bug_452.txt
@@ -0,0 +1 @@
+td (1_500).toString(); td.num do; end
diff --git a/test/prism/fixtures/whitequark/bug_466.txt b/test/prism/fixtures/whitequark/bug_466.txt
new file mode 100644
index 0000000000..ad02ad38ae
--- /dev/null
+++ b/test/prism/fixtures/whitequark/bug_466.txt
@@ -0,0 +1 @@
+foo "#{(1+1).to_i}" do; end
diff --git a/test/prism/fixtures/whitequark/bug_473.txt b/test/prism/fixtures/whitequark/bug_473.txt
new file mode 100644
index 0000000000..0d2ea883a1
--- /dev/null
+++ b/test/prism/fixtures/whitequark/bug_473.txt
@@ -0,0 +1 @@
+m "#{[]}"
diff --git a/test/prism/fixtures/whitequark/bug_480.txt b/test/prism/fixtures/whitequark/bug_480.txt
new file mode 100644
index 0000000000..0558b2c957
--- /dev/null
+++ b/test/prism/fixtures/whitequark/bug_480.txt
@@ -0,0 +1 @@
+m "#{}#{()}"
diff --git a/test/prism/fixtures/whitequark/bug_481.txt b/test/prism/fixtures/whitequark/bug_481.txt
new file mode 100644
index 0000000000..6328e9dbbd
--- /dev/null
+++ b/test/prism/fixtures/whitequark/bug_481.txt
@@ -0,0 +1 @@
+m def x(); end; 1.tap do end
diff --git a/test/prism/fixtures/whitequark/bug_ascii_8bit_in_literal.txt b/test/prism/fixtures/whitequark/bug_ascii_8bit_in_literal.txt
new file mode 100644
index 0000000000..85399bd19a
--- /dev/null
+++ b/test/prism/fixtures/whitequark/bug_ascii_8bit_in_literal.txt
@@ -0,0 +1,2 @@
+# coding:utf-8
+ "\xD0\xBF\xD1\x80\xD0\xBE\xD0\xB2\xD0\xB5\xD1\x80\xD0\xBA\xD0\xB0"
diff --git a/test/prism/fixtures/whitequark/bug_cmd_string_lookahead.txt b/test/prism/fixtures/whitequark/bug_cmd_string_lookahead.txt
new file mode 100644
index 0000000000..7e9e54c518
--- /dev/null
+++ b/test/prism/fixtures/whitequark/bug_cmd_string_lookahead.txt
@@ -0,0 +1 @@
+desc "foo" do end
diff --git a/test/prism/fixtures/whitequark/bug_cmdarg.txt b/test/prism/fixtures/whitequark/bug_cmdarg.txt
new file mode 100644
index 0000000000..0281cd6668
--- /dev/null
+++ b/test/prism/fixtures/whitequark/bug_cmdarg.txt
@@ -0,0 +1,5 @@
+assert do: true
+
+assert dogs
+
+f x: -> do meth do end end
diff --git a/test/prism/fixtures/whitequark/bug_def_no_paren_eql_begin.txt b/test/prism/fixtures/whitequark/bug_def_no_paren_eql_begin.txt
new file mode 100644
index 0000000000..615dc88217
--- /dev/null
+++ b/test/prism/fixtures/whitequark/bug_def_no_paren_eql_begin.txt
@@ -0,0 +1,4 @@
+def foo
+=begin
+=end
+end
diff --git a/test/prism/fixtures/whitequark/bug_do_block_in_call_args.txt b/test/prism/fixtures/whitequark/bug_do_block_in_call_args.txt
new file mode 100644
index 0000000000..21340fd6e8
--- /dev/null
+++ b/test/prism/fixtures/whitequark/bug_do_block_in_call_args.txt
@@ -0,0 +1 @@
+bar def foo; self.each do end end
diff --git a/test/prism/fixtures/whitequark/bug_do_block_in_cmdarg.txt b/test/prism/fixtures/whitequark/bug_do_block_in_cmdarg.txt
new file mode 100644
index 0000000000..7dece50408
--- /dev/null
+++ b/test/prism/fixtures/whitequark/bug_do_block_in_cmdarg.txt
@@ -0,0 +1 @@
+tap (proc do end)
diff --git a/test/prism/fixtures/whitequark/bug_do_block_in_hash_brace.txt b/test/prism/fixtures/whitequark/bug_do_block_in_hash_brace.txt
new file mode 100644
index 0000000000..6c4dd205d2
--- /dev/null
+++ b/test/prism/fixtures/whitequark/bug_do_block_in_hash_brace.txt
@@ -0,0 +1,9 @@
+p :foo, {"a": proc do end, b: proc do end}
+
+p :foo, {** proc do end, b: proc do end}
+
+p :foo, {:a => proc do end, b: proc do end}
+
+p :foo, {a: proc do end, b: proc do end}
+
+p :foo, {proc do end => proc do end, b: proc do end}
diff --git a/test/prism/fixtures/whitequark/bug_heredoc_do.txt b/test/prism/fixtures/whitequark/bug_heredoc_do.txt
new file mode 100644
index 0000000000..06fef0a99f
--- /dev/null
+++ b/test/prism/fixtures/whitequark/bug_heredoc_do.txt
@@ -0,0 +1,3 @@
+f <<-TABLE do
+TABLE
+end
diff --git a/test/prism/fixtures/whitequark/bug_interp_single.txt b/test/prism/fixtures/whitequark/bug_interp_single.txt
new file mode 100644
index 0000000000..be0b1a8542
--- /dev/null
+++ b/test/prism/fixtures/whitequark/bug_interp_single.txt
@@ -0,0 +1,3 @@
+"#{1}"
+
+%W"#{1}"
diff --git a/test/prism/fixtures/whitequark/bug_lambda_leakage.txt b/test/prism/fixtures/whitequark/bug_lambda_leakage.txt
new file mode 100644
index 0000000000..372b36929a
--- /dev/null
+++ b/test/prism/fixtures/whitequark/bug_lambda_leakage.txt
@@ -0,0 +1 @@
+->(scope) {}; scope
diff --git a/test/prism/fixtures/whitequark/bug_regex_verification.txt b/test/prism/fixtures/whitequark/bug_regex_verification.txt
new file mode 100644
index 0000000000..f8483c8b10
--- /dev/null
+++ b/test/prism/fixtures/whitequark/bug_regex_verification.txt
@@ -0,0 +1 @@
+/#)/x
diff --git a/test/prism/fixtures/whitequark/bug_rescue_empty_else.txt b/test/prism/fixtures/whitequark/bug_rescue_empty_else.txt
new file mode 100644
index 0000000000..e8c81edc57
--- /dev/null
+++ b/test/prism/fixtures/whitequark/bug_rescue_empty_else.txt
@@ -0,0 +1 @@
+begin; rescue LoadError; else; end
diff --git a/test/prism/fixtures/whitequark/bug_while_not_parens_do.txt b/test/prism/fixtures/whitequark/bug_while_not_parens_do.txt
new file mode 100644
index 0000000000..628c36ef13
--- /dev/null
+++ b/test/prism/fixtures/whitequark/bug_while_not_parens_do.txt
@@ -0,0 +1 @@
+while not (true) do end
diff --git a/test/prism/fixtures/whitequark/case_cond.txt b/test/prism/fixtures/whitequark/case_cond.txt
new file mode 100644
index 0000000000..a236b27ebd
--- /dev/null
+++ b/test/prism/fixtures/whitequark/case_cond.txt
@@ -0,0 +1 @@
+case; when foo; 'foo'; end
diff --git a/test/prism/fixtures/whitequark/case_cond_else.txt b/test/prism/fixtures/whitequark/case_cond_else.txt
new file mode 100644
index 0000000000..06eefa4a1b
--- /dev/null
+++ b/test/prism/fixtures/whitequark/case_cond_else.txt
@@ -0,0 +1 @@
+case; when foo; 'foo'; else 'bar'; end
diff --git a/test/prism/fixtures/whitequark/case_expr.txt b/test/prism/fixtures/whitequark/case_expr.txt
new file mode 100644
index 0000000000..472672c781
--- /dev/null
+++ b/test/prism/fixtures/whitequark/case_expr.txt
@@ -0,0 +1 @@
+case foo; when 'bar'; bar; end
diff --git a/test/prism/fixtures/whitequark/case_expr_else.txt b/test/prism/fixtures/whitequark/case_expr_else.txt
new file mode 100644
index 0000000000..3c55826134
--- /dev/null
+++ b/test/prism/fixtures/whitequark/case_expr_else.txt
@@ -0,0 +1 @@
+case foo; when 'bar'; bar; else baz; end
diff --git a/test/prism/fixtures/whitequark/casgn_scoped.txt b/test/prism/fixtures/whitequark/casgn_scoped.txt
new file mode 100644
index 0000000000..964d0f4c9b
--- /dev/null
+++ b/test/prism/fixtures/whitequark/casgn_scoped.txt
@@ -0,0 +1 @@
+Bar::Foo = 10
diff --git a/test/prism/fixtures/whitequark/casgn_toplevel.txt b/test/prism/fixtures/whitequark/casgn_toplevel.txt
new file mode 100644
index 0000000000..047a3b6745
--- /dev/null
+++ b/test/prism/fixtures/whitequark/casgn_toplevel.txt
@@ -0,0 +1 @@
+::Foo = 10
diff --git a/test/prism/fixtures/whitequark/casgn_unscoped.txt b/test/prism/fixtures/whitequark/casgn_unscoped.txt
new file mode 100644
index 0000000000..5632cf64ee
--- /dev/null
+++ b/test/prism/fixtures/whitequark/casgn_unscoped.txt
@@ -0,0 +1 @@
+Foo = 10
diff --git a/test/prism/fixtures/whitequark/character.txt b/test/prism/fixtures/whitequark/character.txt
new file mode 100644
index 0000000000..b22ffbb71a
--- /dev/null
+++ b/test/prism/fixtures/whitequark/character.txt
@@ -0,0 +1 @@
+?a
diff --git a/test/prism/fixtures/whitequark/class.txt b/test/prism/fixtures/whitequark/class.txt
new file mode 100644
index 0000000000..a30a248e96
--- /dev/null
+++ b/test/prism/fixtures/whitequark/class.txt
@@ -0,0 +1,3 @@
+class Foo end
+
+class Foo; end
diff --git a/test/prism/fixtures/whitequark/class_super.txt b/test/prism/fixtures/whitequark/class_super.txt
new file mode 100644
index 0000000000..8829d82d36
--- /dev/null
+++ b/test/prism/fixtures/whitequark/class_super.txt
@@ -0,0 +1 @@
+class Foo < Bar; end
diff --git a/test/prism/fixtures/whitequark/class_super_label.txt b/test/prism/fixtures/whitequark/class_super_label.txt
new file mode 100644
index 0000000000..5d47897ab6
--- /dev/null
+++ b/test/prism/fixtures/whitequark/class_super_label.txt
@@ -0,0 +1 @@
+class Foo < a:b; end
diff --git a/test/prism/fixtures/whitequark/comments_before_leading_dot__27.txt b/test/prism/fixtures/whitequark/comments_before_leading_dot__27.txt
new file mode 100644
index 0000000000..208f2d87fe
--- /dev/null
+++ b/test/prism/fixtures/whitequark/comments_before_leading_dot__27.txt
@@ -0,0 +1,19 @@
+a #
+ #
+&.foo
+
+
+a #
+ #
+.foo
+
+
+a #
+#
+&.foo
+
+
+a #
+#
+.foo
+
diff --git a/test/prism/fixtures/whitequark/complex.txt b/test/prism/fixtures/whitequark/complex.txt
new file mode 100644
index 0000000000..1dbf2c13f4
--- /dev/null
+++ b/test/prism/fixtures/whitequark/complex.txt
@@ -0,0 +1,7 @@
+42.1i
+
+42.1ri
+
+42i
+
+42ri
diff --git a/test/prism/fixtures/whitequark/cond_begin.txt b/test/prism/fixtures/whitequark/cond_begin.txt
new file mode 100644
index 0000000000..49d709b79c
--- /dev/null
+++ b/test/prism/fixtures/whitequark/cond_begin.txt
@@ -0,0 +1 @@
+if (bar); foo; end
diff --git a/test/prism/fixtures/whitequark/cond_begin_masgn.txt b/test/prism/fixtures/whitequark/cond_begin_masgn.txt
new file mode 100644
index 0000000000..f9b65026b4
--- /dev/null
+++ b/test/prism/fixtures/whitequark/cond_begin_masgn.txt
@@ -0,0 +1 @@
+if (bar; a, b = foo); end
diff --git a/test/prism/fixtures/whitequark/cond_eflipflop.txt b/test/prism/fixtures/whitequark/cond_eflipflop.txt
new file mode 100644
index 0000000000..1236c2dbb5
--- /dev/null
+++ b/test/prism/fixtures/whitequark/cond_eflipflop.txt
@@ -0,0 +1,3 @@
+!(foo...bar)
+
+if foo...bar; end
diff --git a/test/prism/fixtures/whitequark/cond_eflipflop_with_beginless_range.txt b/test/prism/fixtures/whitequark/cond_eflipflop_with_beginless_range.txt
new file mode 100644
index 0000000000..757863b12b
--- /dev/null
+++ b/test/prism/fixtures/whitequark/cond_eflipflop_with_beginless_range.txt
@@ -0,0 +1 @@
+if ...bar; end
diff --git a/test/prism/fixtures/whitequark/cond_eflipflop_with_endless_range.txt b/test/prism/fixtures/whitequark/cond_eflipflop_with_endless_range.txt
new file mode 100644
index 0000000000..8a3b815ec9
--- /dev/null
+++ b/test/prism/fixtures/whitequark/cond_eflipflop_with_endless_range.txt
@@ -0,0 +1 @@
+if foo...; end
diff --git a/test/prism/fixtures/whitequark/cond_iflipflop.txt b/test/prism/fixtures/whitequark/cond_iflipflop.txt
new file mode 100644
index 0000000000..84bee71a74
--- /dev/null
+++ b/test/prism/fixtures/whitequark/cond_iflipflop.txt
@@ -0,0 +1,3 @@
+!(foo..bar)
+
+if foo..bar; end
diff --git a/test/prism/fixtures/whitequark/cond_iflipflop_with_beginless_range.txt b/test/prism/fixtures/whitequark/cond_iflipflop_with_beginless_range.txt
new file mode 100644
index 0000000000..4ff5b09690
--- /dev/null
+++ b/test/prism/fixtures/whitequark/cond_iflipflop_with_beginless_range.txt
@@ -0,0 +1 @@
+if ..bar; end
diff --git a/test/prism/fixtures/whitequark/cond_iflipflop_with_endless_range.txt b/test/prism/fixtures/whitequark/cond_iflipflop_with_endless_range.txt
new file mode 100644
index 0000000000..2739ad0e2a
--- /dev/null
+++ b/test/prism/fixtures/whitequark/cond_iflipflop_with_endless_range.txt
@@ -0,0 +1 @@
+if foo..; end
diff --git a/test/prism/fixtures/whitequark/cond_match_current_line.txt b/test/prism/fixtures/whitequark/cond_match_current_line.txt
new file mode 100644
index 0000000000..21878c7bd8
--- /dev/null
+++ b/test/prism/fixtures/whitequark/cond_match_current_line.txt
@@ -0,0 +1,3 @@
+!/wat/
+
+if /wat/; end
diff --git a/test/prism/fixtures/whitequark/const_op_asgn.txt b/test/prism/fixtures/whitequark/const_op_asgn.txt
new file mode 100644
index 0000000000..e72e7adee8
--- /dev/null
+++ b/test/prism/fixtures/whitequark/const_op_asgn.txt
@@ -0,0 +1,9 @@
+::A += 1
+
+A += 1
+
+B::A += 1
+
+def x; ::A ||= 1; end
+
+def x; self::A ||= 1; end
diff --git a/test/prism/fixtures/whitequark/const_scoped.txt b/test/prism/fixtures/whitequark/const_scoped.txt
new file mode 100644
index 0000000000..4b03d85fc0
--- /dev/null
+++ b/test/prism/fixtures/whitequark/const_scoped.txt
@@ -0,0 +1 @@
+Bar::Foo
diff --git a/test/prism/fixtures/whitequark/const_toplevel.txt b/test/prism/fixtures/whitequark/const_toplevel.txt
new file mode 100644
index 0000000000..f783ae752b
--- /dev/null
+++ b/test/prism/fixtures/whitequark/const_toplevel.txt
@@ -0,0 +1 @@
+::Foo
diff --git a/test/prism/fixtures/whitequark/const_unscoped.txt b/test/prism/fixtures/whitequark/const_unscoped.txt
new file mode 100644
index 0000000000..bc56c4d894
--- /dev/null
+++ b/test/prism/fixtures/whitequark/const_unscoped.txt
@@ -0,0 +1 @@
+Foo
diff --git a/test/prism/fixtures/whitequark/cpath.txt b/test/prism/fixtures/whitequark/cpath.txt
new file mode 100644
index 0000000000..a4041692d1
--- /dev/null
+++ b/test/prism/fixtures/whitequark/cpath.txt
@@ -0,0 +1,3 @@
+module ::Foo; end
+
+module Bar::Foo; end
diff --git a/test/prism/fixtures/whitequark/cvar.txt b/test/prism/fixtures/whitequark/cvar.txt
new file mode 100644
index 0000000000..4997896e4a
--- /dev/null
+++ b/test/prism/fixtures/whitequark/cvar.txt
@@ -0,0 +1 @@
+@@foo
diff --git a/test/prism/fixtures/whitequark/cvasgn.txt b/test/prism/fixtures/whitequark/cvasgn.txt
new file mode 100644
index 0000000000..8f7e19c4fe
--- /dev/null
+++ b/test/prism/fixtures/whitequark/cvasgn.txt
@@ -0,0 +1 @@
+@@var = 10
diff --git a/test/prism/fixtures/whitequark/dedenting_heredoc.txt b/test/prism/fixtures/whitequark/dedenting_heredoc.txt
new file mode 100644
index 0000000000..84937d84ab
--- /dev/null
+++ b/test/prism/fixtures/whitequark/dedenting_heredoc.txt
@@ -0,0 +1,75 @@
+p <<~"E"
+ x
+ #{" y"}
+E
+
+p <<~"E"
+ x
+ #{foo}
+E
+
+p <<~E
+ x
+ y
+E
+
+p <<~E
+ x
+ y
+E
+
+p <<~E
+ x
+ y
+E
+
+p <<~E
+ x
+ y
+E
+
+p <<~E
+ x
+ \ y
+E
+
+p <<~E
+ x
+ \ y
+E
+
+p <<~E
+ E
+
+p <<~E
+ x
+
+y
+E
+
+p <<~E
+ x
+
+ y
+E
+
+p <<~E
+ x
+ y
+E
+
+p <<~E
+ x
+E
+
+p <<~E
+ ð
+E
+
+p <<~E
+E
+
+p <<~`E`
+ x
+ #{foo}
+E
diff --git a/test/prism/fixtures/whitequark/dedenting_interpolating_heredoc_fake_line_continuation.txt b/test/prism/fixtures/whitequark/dedenting_interpolating_heredoc_fake_line_continuation.txt
new file mode 100644
index 0000000000..0427715d61
--- /dev/null
+++ b/test/prism/fixtures/whitequark/dedenting_interpolating_heredoc_fake_line_continuation.txt
@@ -0,0 +1,4 @@
+<<~'FOO'
+ baz\\
+ qux
+FOO
diff --git a/test/prism/fixtures/whitequark/dedenting_non_interpolating_heredoc_line_continuation.txt b/test/prism/fixtures/whitequark/dedenting_non_interpolating_heredoc_line_continuation.txt
new file mode 100644
index 0000000000..fd7bc02419
--- /dev/null
+++ b/test/prism/fixtures/whitequark/dedenting_non_interpolating_heredoc_line_continuation.txt
@@ -0,0 +1,4 @@
+<<~'FOO'
+ baz\
+ qux
+FOO
diff --git a/test/prism/fixtures/whitequark/def.txt b/test/prism/fixtures/whitequark/def.txt
new file mode 100644
index 0000000000..d96a4238b3
--- /dev/null
+++ b/test/prism/fixtures/whitequark/def.txt
@@ -0,0 +1,11 @@
+def BEGIN; end
+
+def END; end
+
+def String; end
+
+def String=; end
+
+def foo; end
+
+def until; end
diff --git a/test/prism/fixtures/whitequark/defined.txt b/test/prism/fixtures/whitequark/defined.txt
new file mode 100644
index 0000000000..5cf93e22e3
--- /dev/null
+++ b/test/prism/fixtures/whitequark/defined.txt
@@ -0,0 +1,5 @@
+defined? @foo
+
+defined? foo
+
+defined?(foo)
diff --git a/test/prism/fixtures/whitequark/defs.txt b/test/prism/fixtures/whitequark/defs.txt
new file mode 100644
index 0000000000..e0b7aa86eb
--- /dev/null
+++ b/test/prism/fixtures/whitequark/defs.txt
@@ -0,0 +1,9 @@
+def (foo).foo; end
+
+def String.foo; end
+
+def String::foo; end
+
+def self.foo; end
+
+def self::foo; end
diff --git a/test/prism/fixtures/whitequark/empty_stmt.txt b/test/prism/fixtures/whitequark/empty_stmt.txt
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/test/prism/fixtures/whitequark/empty_stmt.txt
@@ -0,0 +1 @@
+
diff --git a/test/prism/fixtures/whitequark/endless_comparison_method.txt b/test/prism/fixtures/whitequark/endless_comparison_method.txt
new file mode 100644
index 0000000000..e10c3a6ced
--- /dev/null
+++ b/test/prism/fixtures/whitequark/endless_comparison_method.txt
@@ -0,0 +1,11 @@
+def !=(other) = do_something
+
+def !=(other) = do_something
+
+def <=(other) = do_something
+
+def ==(other) = do_something
+
+def ===(other) = do_something
+
+def >=(other) = do_something
diff --git a/test/prism/fixtures/whitequark/endless_method.txt b/test/prism/fixtures/whitequark/endless_method.txt
new file mode 100644
index 0000000000..7002526229
--- /dev/null
+++ b/test/prism/fixtures/whitequark/endless_method.txt
@@ -0,0 +1,7 @@
+def foo() = 42
+
+def inc(x) = x + 1
+
+def obj.foo() = 42
+
+def obj.inc(x) = x + 1
diff --git a/test/prism/fixtures/whitequark/endless_method_command_syntax.txt b/test/prism/fixtures/whitequark/endless_method_command_syntax.txt
new file mode 100644
index 0000000000..d9dad2d747
--- /dev/null
+++ b/test/prism/fixtures/whitequark/endless_method_command_syntax.txt
@@ -0,0 +1,15 @@
+def foo = puts "Hello"
+
+def foo() = puts "Hello"
+
+def foo(x) = puts x
+
+def obj.foo = puts "Hello"
+
+def obj.foo() = puts "Hello"
+
+def obj.foo(x) = puts x
+
+def rescued(x) = raise "to be caught" rescue "instance #{x}"
+
+def self.rescued(x) = raise "to be caught" rescue "class #{x}"
diff --git a/test/prism/fixtures/whitequark/endless_method_forwarded_args_legacy.txt b/test/prism/fixtures/whitequark/endless_method_forwarded_args_legacy.txt
new file mode 100644
index 0000000000..5955e40b5b
--- /dev/null
+++ b/test/prism/fixtures/whitequark/endless_method_forwarded_args_legacy.txt
@@ -0,0 +1 @@
+def foo(...) = bar(...)
diff --git a/test/prism/fixtures/whitequark/endless_method_with_rescue_mod.txt b/test/prism/fixtures/whitequark/endless_method_with_rescue_mod.txt
new file mode 100644
index 0000000000..93dc63a45d
--- /dev/null
+++ b/test/prism/fixtures/whitequark/endless_method_with_rescue_mod.txt
@@ -0,0 +1,3 @@
+def m() = 1 rescue 2
+
+def self.m() = 1 rescue 2
diff --git a/test/prism/fixtures/whitequark/endless_method_without_args.txt b/test/prism/fixtures/whitequark/endless_method_without_args.txt
new file mode 100644
index 0000000000..90ea8f7c6d
--- /dev/null
+++ b/test/prism/fixtures/whitequark/endless_method_without_args.txt
@@ -0,0 +1,7 @@
+def foo = 42
+
+def foo = 42 rescue nil
+
+def self.foo = 42
+
+def self.foo = 42 rescue nil
diff --git a/test/prism/fixtures/whitequark/ensure.txt b/test/prism/fixtures/whitequark/ensure.txt
new file mode 100644
index 0000000000..6c4b47c63c
--- /dev/null
+++ b/test/prism/fixtures/whitequark/ensure.txt
@@ -0,0 +1 @@
+begin; meth; ensure; bar; end
diff --git a/test/prism/fixtures/whitequark/ensure_empty.txt b/test/prism/fixtures/whitequark/ensure_empty.txt
new file mode 100644
index 0000000000..39a175c371
--- /dev/null
+++ b/test/prism/fixtures/whitequark/ensure_empty.txt
@@ -0,0 +1 @@
+begin ensure end
diff --git a/test/prism/fixtures/whitequark/false.txt b/test/prism/fixtures/whitequark/false.txt
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/test/prism/fixtures/whitequark/false.txt
@@ -0,0 +1 @@
+false
diff --git a/test/prism/fixtures/whitequark/float.txt b/test/prism/fixtures/whitequark/float.txt
new file mode 100644
index 0000000000..8c7592a192
--- /dev/null
+++ b/test/prism/fixtures/whitequark/float.txt
@@ -0,0 +1,3 @@
+-1.33
+
+1.33
diff --git a/test/prism/fixtures/whitequark/for.txt b/test/prism/fixtures/whitequark/for.txt
new file mode 100644
index 0000000000..a27ae578da
--- /dev/null
+++ b/test/prism/fixtures/whitequark/for.txt
@@ -0,0 +1,3 @@
+for a in foo do p a; end
+
+for a in foo; p a; end
diff --git a/test/prism/fixtures/whitequark/for_mlhs.txt b/test/prism/fixtures/whitequark/for_mlhs.txt
new file mode 100644
index 0000000000..53cd5229a4
--- /dev/null
+++ b/test/prism/fixtures/whitequark/for_mlhs.txt
@@ -0,0 +1 @@
+for a, b in foo; p a, b; end
diff --git a/test/prism/fixtures/whitequark/forward_arg.txt b/test/prism/fixtures/whitequark/forward_arg.txt
new file mode 100644
index 0000000000..c7590a8d53
--- /dev/null
+++ b/test/prism/fixtures/whitequark/forward_arg.txt
@@ -0,0 +1 @@
+def foo(...); bar(...); end
diff --git a/test/prism/fixtures/whitequark/forward_arg_with_open_args.txt b/test/prism/fixtures/whitequark/forward_arg_with_open_args.txt
new file mode 100644
index 0000000000..fd4c06bcb5
--- /dev/null
+++ b/test/prism/fixtures/whitequark/forward_arg_with_open_args.txt
@@ -0,0 +1,27 @@
+(def foo ...
+ bar(...)
+end)
+
+(def foo ...; bar(...); end)
+
+def foo ...
+end
+
+def foo ...; bar(...); end
+
+def foo a, ...
+ bar(...)
+end
+
+def foo a, ...; bar(...); end
+
+def foo a, b = 1, ...
+end
+
+def foo b = 1, ...
+ bar(...)
+end
+
+def foo b = 1, ...; bar(...); end
+
+def foo(a, ...) bar(...) end
diff --git a/test/prism/fixtures/whitequark/forward_args_legacy.txt b/test/prism/fixtures/whitequark/forward_args_legacy.txt
new file mode 100644
index 0000000000..0d9162457f
--- /dev/null
+++ b/test/prism/fixtures/whitequark/forward_args_legacy.txt
@@ -0,0 +1,5 @@
+def foo(...); bar(...); end
+
+def foo(...); end
+
+def foo(...); super(...); end
diff --git a/test/prism/fixtures/whitequark/forwarded_argument_with_kwrestarg.txt b/test/prism/fixtures/whitequark/forwarded_argument_with_kwrestarg.txt
new file mode 100644
index 0000000000..7dbf472232
--- /dev/null
+++ b/test/prism/fixtures/whitequark/forwarded_argument_with_kwrestarg.txt
@@ -0,0 +1 @@
+def foo(argument, **); bar(argument, **); end
diff --git a/test/prism/fixtures/whitequark/forwarded_argument_with_restarg.txt b/test/prism/fixtures/whitequark/forwarded_argument_with_restarg.txt
new file mode 100644
index 0000000000..f734bfd9ec
--- /dev/null
+++ b/test/prism/fixtures/whitequark/forwarded_argument_with_restarg.txt
@@ -0,0 +1 @@
+def foo(argument, *); bar(argument, *); end
diff --git a/test/prism/fixtures/whitequark/forwarded_kwrestarg.txt b/test/prism/fixtures/whitequark/forwarded_kwrestarg.txt
new file mode 100644
index 0000000000..16cd8b2913
--- /dev/null
+++ b/test/prism/fixtures/whitequark/forwarded_kwrestarg.txt
@@ -0,0 +1 @@
+def foo(**); bar(**); end
diff --git a/test/prism/fixtures/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt b/test/prism/fixtures/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt
new file mode 100644
index 0000000000..52759b838f
--- /dev/null
+++ b/test/prism/fixtures/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt
@@ -0,0 +1 @@
+def foo(**); bar(**, from_foo: true); end
diff --git a/test/prism/fixtures/whitequark/forwarded_restarg.txt b/test/prism/fixtures/whitequark/forwarded_restarg.txt
new file mode 100644
index 0000000000..65ac2cc1ed
--- /dev/null
+++ b/test/prism/fixtures/whitequark/forwarded_restarg.txt
@@ -0,0 +1 @@
+def foo(*); bar(*); end
diff --git a/test/prism/fixtures/whitequark/gvar.txt b/test/prism/fixtures/whitequark/gvar.txt
new file mode 100644
index 0000000000..bbf13489ad
--- /dev/null
+++ b/test/prism/fixtures/whitequark/gvar.txt
@@ -0,0 +1 @@
+$foo
diff --git a/test/prism/fixtures/whitequark/gvasgn.txt b/test/prism/fixtures/whitequark/gvasgn.txt
new file mode 100644
index 0000000000..2bcc22cdd1
--- /dev/null
+++ b/test/prism/fixtures/whitequark/gvasgn.txt
@@ -0,0 +1 @@
+$var = 10
diff --git a/test/prism/fixtures/whitequark/hash_empty.txt b/test/prism/fixtures/whitequark/hash_empty.txt
new file mode 100644
index 0000000000..ffcd4415b0
--- /dev/null
+++ b/test/prism/fixtures/whitequark/hash_empty.txt
@@ -0,0 +1 @@
+{ }
diff --git a/test/prism/fixtures/whitequark/hash_hashrocket.txt b/test/prism/fixtures/whitequark/hash_hashrocket.txt
new file mode 100644
index 0000000000..2cbb3bd96d
--- /dev/null
+++ b/test/prism/fixtures/whitequark/hash_hashrocket.txt
@@ -0,0 +1,3 @@
+{ 1 => 2 }
+
+{ 1 => 2, :foo => "bar" }
diff --git a/test/prism/fixtures/whitequark/hash_kwsplat.txt b/test/prism/fixtures/whitequark/hash_kwsplat.txt
new file mode 100644
index 0000000000..921aa97c7c
--- /dev/null
+++ b/test/prism/fixtures/whitequark/hash_kwsplat.txt
@@ -0,0 +1 @@
+{ foo: 2, **bar }
diff --git a/test/prism/fixtures/whitequark/hash_label.txt b/test/prism/fixtures/whitequark/hash_label.txt
new file mode 100644
index 0000000000..3aacae4b69
--- /dev/null
+++ b/test/prism/fixtures/whitequark/hash_label.txt
@@ -0,0 +1 @@
+{ foo: 2 }
diff --git a/test/prism/fixtures/whitequark/hash_label_end.txt b/test/prism/fixtures/whitequark/hash_label_end.txt
new file mode 100644
index 0000000000..ac9f7c4b41
--- /dev/null
+++ b/test/prism/fixtures/whitequark/hash_label_end.txt
@@ -0,0 +1,5 @@
+f(a ? "a":1)
+
+{ 'foo': 2 }
+
+{ 'foo': 2, 'bar': {}}
diff --git a/test/prism/fixtures/whitequark/hash_pair_value_omission.txt b/test/prism/fixtures/whitequark/hash_pair_value_omission.txt
new file mode 100644
index 0000000000..9d8ccb5877
--- /dev/null
+++ b/test/prism/fixtures/whitequark/hash_pair_value_omission.txt
@@ -0,0 +1,5 @@
+{BAR:}
+
+{a:, b:}
+
+{puts:}
diff --git a/test/prism/fixtures/whitequark/heredoc.txt b/test/prism/fixtures/whitequark/heredoc.txt
new file mode 100644
index 0000000000..1bfc963f94
--- /dev/null
+++ b/test/prism/fixtures/whitequark/heredoc.txt
@@ -0,0 +1,14 @@
+<<'HERE'
+foo
+bar
+HERE
+
+<<HERE
+foo
+bar
+HERE
+
+<<`HERE`
+foo
+bar
+HERE
diff --git a/test/prism/fixtures/whitequark/if.txt b/test/prism/fixtures/whitequark/if.txt
new file mode 100644
index 0000000000..3de3525286
--- /dev/null
+++ b/test/prism/fixtures/whitequark/if.txt
@@ -0,0 +1,3 @@
+if foo then bar; end
+
+if foo; bar; end
diff --git a/test/prism/fixtures/whitequark/if_else.txt b/test/prism/fixtures/whitequark/if_else.txt
new file mode 100644
index 0000000000..62bab8138c
--- /dev/null
+++ b/test/prism/fixtures/whitequark/if_else.txt
@@ -0,0 +1,3 @@
+if foo then bar; else baz; end
+
+if foo; bar; else baz; end
diff --git a/test/prism/fixtures/whitequark/if_elsif.txt b/test/prism/fixtures/whitequark/if_elsif.txt
new file mode 100644
index 0000000000..4d71e82214
--- /dev/null
+++ b/test/prism/fixtures/whitequark/if_elsif.txt
@@ -0,0 +1 @@
+if foo; bar; elsif baz; 1; else 2; end
diff --git a/test/prism/fixtures/whitequark/if_masgn__24.txt b/test/prism/fixtures/whitequark/if_masgn__24.txt
new file mode 100644
index 0000000000..0c2c8bb04f
--- /dev/null
+++ b/test/prism/fixtures/whitequark/if_masgn__24.txt
@@ -0,0 +1 @@
+if (a, b = foo); end
diff --git a/test/prism/fixtures/whitequark/if_mod.txt b/test/prism/fixtures/whitequark/if_mod.txt
new file mode 100644
index 0000000000..da177b5606
--- /dev/null
+++ b/test/prism/fixtures/whitequark/if_mod.txt
@@ -0,0 +1 @@
+bar if foo
diff --git a/test/prism/fixtures/whitequark/if_nl_then.txt b/test/prism/fixtures/whitequark/if_nl_then.txt
new file mode 100644
index 0000000000..b68107bad2
--- /dev/null
+++ b/test/prism/fixtures/whitequark/if_nl_then.txt
@@ -0,0 +1,2 @@
+if foo
+then bar end
diff --git a/test/prism/fixtures/whitequark/int.txt b/test/prism/fixtures/whitequark/int.txt
new file mode 100644
index 0000000000..6d419918c6
--- /dev/null
+++ b/test/prism/fixtures/whitequark/int.txt
@@ -0,0 +1,5 @@
++42
+
+-42
+
+42
diff --git a/test/prism/fixtures/whitequark/int___LINE__.txt b/test/prism/fixtures/whitequark/int___LINE__.txt
new file mode 100644
index 0000000000..05dcf7afe0
--- /dev/null
+++ b/test/prism/fixtures/whitequark/int___LINE__.txt
@@ -0,0 +1 @@
+__LINE__
diff --git a/test/prism/fixtures/whitequark/interp_digit_var.txt b/test/prism/fixtures/whitequark/interp_digit_var.txt
new file mode 100644
index 0000000000..1ae5a2e3e0
--- /dev/null
+++ b/test/prism/fixtures/whitequark/interp_digit_var.txt
@@ -0,0 +1,87 @@
+ "#@1"
+
+ "#@@1"
+
+ %I[#@1]
+
+ %I[#@@1]
+
+ %Q{#@1}
+
+ %Q{#@@1}
+
+ %W[#@1]
+
+ %W[#@@1]
+
+ %i[ #@1 ]
+
+ %i[ #@@1 ]
+
+ %q{#@1}
+
+ %q{#@@1}
+
+ %r{#@1}
+
+ %r{#@@1}
+
+ %s{#@1}
+
+ %s{#@@1}
+
+ %w[ #@1 ]
+
+ %w[ #@@1 ]
+
+ %x{#@1}
+
+ %x{#@@1}
+
+ %{#@1}
+
+ %{#@@1}
+
+ '#@1'
+
+ '#@@1'
+
+ /#@1/
+
+ /#@@1/
+
+ :"#@1"
+
+ :"#@@1"
+
+ :'#@1'
+
+ :'#@@1'
+
+ `#@1`
+
+ `#@@1`
+
+<<-"HERE"
+#@1
+HERE
+
+<<-"HERE"
+#@@1
+HERE
+
+<<-'HERE'
+#@1
+HERE
+
+<<-'HERE'
+#@@1
+HERE
+
+<<-`HERE`
+#@1
+HERE
+
+<<-`HERE`
+#@@1
+HERE
diff --git a/test/prism/fixtures/whitequark/ivar.txt b/test/prism/fixtures/whitequark/ivar.txt
new file mode 100644
index 0000000000..9149fc67f9
--- /dev/null
+++ b/test/prism/fixtures/whitequark/ivar.txt
@@ -0,0 +1 @@
+@foo
diff --git a/test/prism/fixtures/whitequark/ivasgn.txt b/test/prism/fixtures/whitequark/ivasgn.txt
new file mode 100644
index 0000000000..ac291799db
--- /dev/null
+++ b/test/prism/fixtures/whitequark/ivasgn.txt
@@ -0,0 +1 @@
+@var = 10
diff --git a/test/prism/fixtures/whitequark/keyword_argument_omission.txt b/test/prism/fixtures/whitequark/keyword_argument_omission.txt
new file mode 100644
index 0000000000..47883dc9a5
--- /dev/null
+++ b/test/prism/fixtures/whitequark/keyword_argument_omission.txt
@@ -0,0 +1 @@
+foo(a:, b:)
diff --git a/test/prism/fixtures/whitequark/kwarg.txt b/test/prism/fixtures/whitequark/kwarg.txt
new file mode 100644
index 0000000000..ca02a1fd2a
--- /dev/null
+++ b/test/prism/fixtures/whitequark/kwarg.txt
@@ -0,0 +1 @@
+def f(foo:); end
diff --git a/test/prism/fixtures/whitequark/kwbegin_compstmt.txt b/test/prism/fixtures/whitequark/kwbegin_compstmt.txt
new file mode 100644
index 0000000000..33ceff2489
--- /dev/null
+++ b/test/prism/fixtures/whitequark/kwbegin_compstmt.txt
@@ -0,0 +1 @@
+begin foo!; bar! end
diff --git a/test/prism/fixtures/whitequark/kwnilarg.txt b/test/prism/fixtures/whitequark/kwnilarg.txt
new file mode 100644
index 0000000000..8fc482d5af
--- /dev/null
+++ b/test/prism/fixtures/whitequark/kwnilarg.txt
@@ -0,0 +1,5 @@
+->(**nil) {}
+
+def f(**nil); end
+
+m { |**nil| }
diff --git a/test/prism/fixtures/whitequark/kwoptarg.txt b/test/prism/fixtures/whitequark/kwoptarg.txt
new file mode 100644
index 0000000000..dc3ce728d6
--- /dev/null
+++ b/test/prism/fixtures/whitequark/kwoptarg.txt
@@ -0,0 +1 @@
+def f(foo: 1); end
diff --git a/test/prism/fixtures/whitequark/kwoptarg_with_kwrestarg_and_forwarded_args.txt b/test/prism/fixtures/whitequark/kwoptarg_with_kwrestarg_and_forwarded_args.txt
new file mode 100644
index 0000000000..99dcc7239b
--- /dev/null
+++ b/test/prism/fixtures/whitequark/kwoptarg_with_kwrestarg_and_forwarded_args.txt
@@ -0,0 +1 @@
+def f(a: nil, **); b(**) end
diff --git a/test/prism/fixtures/whitequark/kwrestarg_named.txt b/test/prism/fixtures/whitequark/kwrestarg_named.txt
new file mode 100644
index 0000000000..e17a661001
--- /dev/null
+++ b/test/prism/fixtures/whitequark/kwrestarg_named.txt
@@ -0,0 +1 @@
+def f(**foo); end
diff --git a/test/prism/fixtures/whitequark/kwrestarg_unnamed.txt b/test/prism/fixtures/whitequark/kwrestarg_unnamed.txt
new file mode 100644
index 0000000000..1b26b1d2ac
--- /dev/null
+++ b/test/prism/fixtures/whitequark/kwrestarg_unnamed.txt
@@ -0,0 +1 @@
+def f(**); end
diff --git a/test/prism/fixtures/whitequark/lbrace_arg_after_command_args.txt b/test/prism/fixtures/whitequark/lbrace_arg_after_command_args.txt
new file mode 100644
index 0000000000..277c200c9f
--- /dev/null
+++ b/test/prism/fixtures/whitequark/lbrace_arg_after_command_args.txt
@@ -0,0 +1 @@
+let (:a) { m do; end }
diff --git a/test/prism/fixtures/whitequark/lparenarg_after_lvar__since_25.txt b/test/prism/fixtures/whitequark/lparenarg_after_lvar__since_25.txt
new file mode 100644
index 0000000000..dc3a98b1c8
--- /dev/null
+++ b/test/prism/fixtures/whitequark/lparenarg_after_lvar__since_25.txt
@@ -0,0 +1,3 @@
+foo (-1.3).abs
+
+meth (-1.3).abs
diff --git a/test/prism/fixtures/whitequark/lvar.txt b/test/prism/fixtures/whitequark/lvar.txt
new file mode 100644
index 0000000000..257cc5642c
--- /dev/null
+++ b/test/prism/fixtures/whitequark/lvar.txt
@@ -0,0 +1 @@
+foo
diff --git a/test/prism/fixtures/whitequark/lvar_injecting_match.txt b/test/prism/fixtures/whitequark/lvar_injecting_match.txt
new file mode 100644
index 0000000000..ba814a1088
--- /dev/null
+++ b/test/prism/fixtures/whitequark/lvar_injecting_match.txt
@@ -0,0 +1 @@
+/(?<match>bar)/ =~ 'bar'; match
diff --git a/test/prism/fixtures/whitequark/lvasgn.txt b/test/prism/fixtures/whitequark/lvasgn.txt
new file mode 100644
index 0000000000..330b8eff27
--- /dev/null
+++ b/test/prism/fixtures/whitequark/lvasgn.txt
@@ -0,0 +1 @@
+var = 10; var
diff --git a/test/prism/fixtures/whitequark/masgn.txt b/test/prism/fixtures/whitequark/masgn.txt
new file mode 100644
index 0000000000..032124287c
--- /dev/null
+++ b/test/prism/fixtures/whitequark/masgn.txt
@@ -0,0 +1,5 @@
+(foo, bar) = 1, 2
+
+foo, bar = 1, 2
+
+foo, bar, baz = 1, 2
diff --git a/test/prism/fixtures/whitequark/masgn_attr.txt b/test/prism/fixtures/whitequark/masgn_attr.txt
new file mode 100644
index 0000000000..91a703ef44
--- /dev/null
+++ b/test/prism/fixtures/whitequark/masgn_attr.txt
@@ -0,0 +1,5 @@
+self.A, foo = foo
+
+self.a, self[1, 2] = foo
+
+self::a, foo = foo
diff --git a/test/prism/fixtures/whitequark/masgn_cmd.txt b/test/prism/fixtures/whitequark/masgn_cmd.txt
new file mode 100644
index 0000000000..18e096f1ee
--- /dev/null
+++ b/test/prism/fixtures/whitequark/masgn_cmd.txt
@@ -0,0 +1 @@
+foo, bar = m foo
diff --git a/test/prism/fixtures/whitequark/masgn_const.txt b/test/prism/fixtures/whitequark/masgn_const.txt
new file mode 100644
index 0000000000..3b6fba588a
--- /dev/null
+++ b/test/prism/fixtures/whitequark/masgn_const.txt
@@ -0,0 +1,3 @@
+::A, foo = foo
+
+self::A, foo = foo
diff --git a/test/prism/fixtures/whitequark/masgn_nested.txt b/test/prism/fixtures/whitequark/masgn_nested.txt
new file mode 100644
index 0000000000..a1ccf4e385
--- /dev/null
+++ b/test/prism/fixtures/whitequark/masgn_nested.txt
@@ -0,0 +1,3 @@
+((b, )) = foo
+
+a, (b, c) = foo
diff --git a/test/prism/fixtures/whitequark/masgn_splat.txt b/test/prism/fixtures/whitequark/masgn_splat.txt
new file mode 100644
index 0000000000..a15dab10f2
--- /dev/null
+++ b/test/prism/fixtures/whitequark/masgn_splat.txt
@@ -0,0 +1,19 @@
+* = bar
+
+*, c, d = bar
+
+*b = bar
+
+*b, c = bar
+
+@foo, @@bar = *foo
+
+a, * = bar
+
+a, *, c = bar
+
+a, *b = bar
+
+a, *b, c = bar
+
+a, b = *foo, bar
diff --git a/test/prism/fixtures/whitequark/module.txt b/test/prism/fixtures/whitequark/module.txt
new file mode 100644
index 0000000000..f999df3791
--- /dev/null
+++ b/test/prism/fixtures/whitequark/module.txt
@@ -0,0 +1 @@
+module Foo; end
diff --git a/test/prism/fixtures/whitequark/multiple_pattern_matches.txt b/test/prism/fixtures/whitequark/multiple_pattern_matches.txt
new file mode 100644
index 0000000000..54a4bb4774
--- /dev/null
+++ b/test/prism/fixtures/whitequark/multiple_pattern_matches.txt
@@ -0,0 +1,5 @@
+{a: 0} => a:
+{a: 0} => a:
+
+{a: 0} in a:
+{a: 0} in a:
diff --git a/test/prism/fixtures/whitequark/newline_in_hash_argument.txt b/test/prism/fixtures/whitequark/newline_in_hash_argument.txt
new file mode 100644
index 0000000000..1681844331
--- /dev/null
+++ b/test/prism/fixtures/whitequark/newline_in_hash_argument.txt
@@ -0,0 +1,14 @@
+case foo
+in a:
+0
+true
+in "b":
+0
+true
+end
+
+obj.set "foo":
+1
+
+obj.set foo:
+1
diff --git a/test/prism/fixtures/whitequark/nil.txt b/test/prism/fixtures/whitequark/nil.txt
new file mode 100644
index 0000000000..607602cfc6
--- /dev/null
+++ b/test/prism/fixtures/whitequark/nil.txt
@@ -0,0 +1 @@
+nil
diff --git a/test/prism/fixtures/whitequark/nil_expression.txt b/test/prism/fixtures/whitequark/nil_expression.txt
new file mode 100644
index 0000000000..aabf53f005
--- /dev/null
+++ b/test/prism/fixtures/whitequark/nil_expression.txt
@@ -0,0 +1,3 @@
+()
+
+begin end
diff --git a/test/prism/fixtures/whitequark/non_lvar_injecting_match.txt b/test/prism/fixtures/whitequark/non_lvar_injecting_match.txt
new file mode 100644
index 0000000000..f1eb7d3c2c
--- /dev/null
+++ b/test/prism/fixtures/whitequark/non_lvar_injecting_match.txt
@@ -0,0 +1 @@
+/#{1}(?<match>bar)/ =~ 'bar'
diff --git a/test/prism/fixtures/whitequark/not.txt b/test/prism/fixtures/whitequark/not.txt
new file mode 100644
index 0000000000..d87f68f48c
--- /dev/null
+++ b/test/prism/fixtures/whitequark/not.txt
@@ -0,0 +1,5 @@
+not foo
+
+not()
+
+not(foo)
diff --git a/test/prism/fixtures/whitequark/not_cmd.txt b/test/prism/fixtures/whitequark/not_cmd.txt
new file mode 100644
index 0000000000..685ec209ee
--- /dev/null
+++ b/test/prism/fixtures/whitequark/not_cmd.txt
@@ -0,0 +1 @@
+not m foo
diff --git a/test/prism/fixtures/whitequark/not_masgn__24.txt b/test/prism/fixtures/whitequark/not_masgn__24.txt
new file mode 100644
index 0000000000..cb93b9103d
--- /dev/null
+++ b/test/prism/fixtures/whitequark/not_masgn__24.txt
@@ -0,0 +1 @@
+!(a, b = foo)
diff --git a/test/prism/fixtures/whitequark/nth_ref.txt b/test/prism/fixtures/whitequark/nth_ref.txt
new file mode 100644
index 0000000000..16ef65b105
--- /dev/null
+++ b/test/prism/fixtures/whitequark/nth_ref.txt
@@ -0,0 +1 @@
+$10
diff --git a/test/prism/fixtures/whitequark/numbered_args_after_27.txt b/test/prism/fixtures/whitequark/numbered_args_after_27.txt
new file mode 100644
index 0000000000..96fe7a32e2
--- /dev/null
+++ b/test/prism/fixtures/whitequark/numbered_args_after_27.txt
@@ -0,0 +1,7 @@
+-> do _1 + _9 end
+
+-> { _1 + _9}
+
+m do _1 + _9 end
+
+m { _1 + _9 }
diff --git a/test/prism/fixtures/whitequark/numparam_outside_block.txt b/test/prism/fixtures/whitequark/numparam_outside_block.txt
new file mode 100644
index 0000000000..37cbce182d
--- /dev/null
+++ b/test/prism/fixtures/whitequark/numparam_outside_block.txt
@@ -0,0 +1,9 @@
+_1
+
+class << foo; _1; end
+
+class A; _1; end
+
+def self.m; _1; end
+
+module A; _1; end
diff --git a/test/prism/fixtures/whitequark/numparam_ruby_bug_19025.txt b/test/prism/fixtures/whitequark/numparam_ruby_bug_19025.txt
new file mode 100644
index 0000000000..f9ffd4329a
--- /dev/null
+++ b/test/prism/fixtures/whitequark/numparam_ruby_bug_19025.txt
@@ -0,0 +1 @@
+p { [_1 **2] }
diff --git a/test/prism/fixtures/whitequark/op_asgn.txt b/test/prism/fixtures/whitequark/op_asgn.txt
new file mode 100644
index 0000000000..a5a28b1010
--- /dev/null
+++ b/test/prism/fixtures/whitequark/op_asgn.txt
@@ -0,0 +1,5 @@
+foo.A += 1
+
+foo.a += 1
+
+foo::a += 1
diff --git a/test/prism/fixtures/whitequark/op_asgn_cmd.txt b/test/prism/fixtures/whitequark/op_asgn_cmd.txt
new file mode 100644
index 0000000000..017aa9bef7
--- /dev/null
+++ b/test/prism/fixtures/whitequark/op_asgn_cmd.txt
@@ -0,0 +1,7 @@
+foo.A += m foo
+
+foo.a += m foo
+
+foo::A += m foo
+
+foo::a += m foo
diff --git a/test/prism/fixtures/whitequark/op_asgn_index.txt b/test/prism/fixtures/whitequark/op_asgn_index.txt
new file mode 100644
index 0000000000..92cfb225fc
--- /dev/null
+++ b/test/prism/fixtures/whitequark/op_asgn_index.txt
@@ -0,0 +1 @@
+foo[0, 1] += 2
diff --git a/test/prism/fixtures/whitequark/op_asgn_index_cmd.txt b/test/prism/fixtures/whitequark/op_asgn_index_cmd.txt
new file mode 100644
index 0000000000..161b244bd4
--- /dev/null
+++ b/test/prism/fixtures/whitequark/op_asgn_index_cmd.txt
@@ -0,0 +1 @@
+foo[0, 1] += m foo
diff --git a/test/prism/fixtures/whitequark/optarg.txt b/test/prism/fixtures/whitequark/optarg.txt
new file mode 100644
index 0000000000..9d60609961
--- /dev/null
+++ b/test/prism/fixtures/whitequark/optarg.txt
@@ -0,0 +1,3 @@
+def f foo = 1; end
+
+def f(foo=1, bar=2); end
diff --git a/test/prism/fixtures/whitequark/or.txt b/test/prism/fixtures/whitequark/or.txt
new file mode 100644
index 0000000000..c2042ebb41
--- /dev/null
+++ b/test/prism/fixtures/whitequark/or.txt
@@ -0,0 +1,3 @@
+foo or bar
+
+foo || bar
diff --git a/test/prism/fixtures/whitequark/or_asgn.txt b/test/prism/fixtures/whitequark/or_asgn.txt
new file mode 100644
index 0000000000..6e4ecc9f93
--- /dev/null
+++ b/test/prism/fixtures/whitequark/or_asgn.txt
@@ -0,0 +1,3 @@
+foo.a ||= 1
+
+foo[0, 1] ||= 2
diff --git a/test/prism/fixtures/whitequark/parser_bug_272.txt b/test/prism/fixtures/whitequark/parser_bug_272.txt
new file mode 100644
index 0000000000..2f2f2b84d2
--- /dev/null
+++ b/test/prism/fixtures/whitequark/parser_bug_272.txt
@@ -0,0 +1 @@
+a @b do |c|;end
diff --git a/test/prism/fixtures/whitequark/parser_bug_490.txt b/test/prism/fixtures/whitequark/parser_bug_490.txt
new file mode 100644
index 0000000000..cf544b1ff6
--- /dev/null
+++ b/test/prism/fixtures/whitequark/parser_bug_490.txt
@@ -0,0 +1,5 @@
+def m; class << self; A = nil; end; end
+
+def m; class << self; class C; end; end; end
+
+def m; class << self; module M; end; end; end
diff --git a/test/prism/fixtures/whitequark/parser_bug_507.txt b/test/prism/fixtures/whitequark/parser_bug_507.txt
new file mode 100644
index 0000000000..bf616b85c8
--- /dev/null
+++ b/test/prism/fixtures/whitequark/parser_bug_507.txt
@@ -0,0 +1 @@
+m = -> *args do end
diff --git a/test/prism/fixtures/whitequark/parser_bug_518.txt b/test/prism/fixtures/whitequark/parser_bug_518.txt
new file mode 100644
index 0000000000..22f4afe3c8
--- /dev/null
+++ b/test/prism/fixtures/whitequark/parser_bug_518.txt
@@ -0,0 +1,2 @@
+class A < B
+end
diff --git a/test/prism/fixtures/whitequark/parser_bug_525.txt b/test/prism/fixtures/whitequark/parser_bug_525.txt
new file mode 100644
index 0000000000..59d0e735b4
--- /dev/null
+++ b/test/prism/fixtures/whitequark/parser_bug_525.txt
@@ -0,0 +1 @@
+m1 :k => m2 do; m3() do end; end
diff --git a/test/prism/fixtures/whitequark/parser_bug_604.txt b/test/prism/fixtures/whitequark/parser_bug_604.txt
new file mode 100644
index 0000000000..7eb91c2f46
--- /dev/null
+++ b/test/prism/fixtures/whitequark/parser_bug_604.txt
@@ -0,0 +1 @@
+m a + b do end
diff --git a/test/prism/fixtures/whitequark/parser_bug_640.txt b/test/prism/fixtures/whitequark/parser_bug_640.txt
new file mode 100644
index 0000000000..fb62ded04c
--- /dev/null
+++ b/test/prism/fixtures/whitequark/parser_bug_640.txt
@@ -0,0 +1,4 @@
+<<~FOO
+ baz\
+ qux
+FOO
diff --git a/test/prism/fixtures/whitequark/parser_bug_645.txt b/test/prism/fixtures/whitequark/parser_bug_645.txt
new file mode 100644
index 0000000000..cb1e5a0dcf
--- /dev/null
+++ b/test/prism/fixtures/whitequark/parser_bug_645.txt
@@ -0,0 +1 @@
+-> (arg={}) {}
diff --git a/test/prism/fixtures/whitequark/parser_bug_830.txt b/test/prism/fixtures/whitequark/parser_bug_830.txt
new file mode 100644
index 0000000000..e5865e960d
--- /dev/null
+++ b/test/prism/fixtures/whitequark/parser_bug_830.txt
@@ -0,0 +1 @@
+/\(/
diff --git a/test/prism/fixtures/whitequark/parser_bug_989.txt b/test/prism/fixtures/whitequark/parser_bug_989.txt
new file mode 100644
index 0000000000..b7b99612b4
--- /dev/null
+++ b/test/prism/fixtures/whitequark/parser_bug_989.txt
@@ -0,0 +1,3 @@
+ <<-HERE
+ content
+ HERE
diff --git a/test/prism/fixtures/whitequark/parser_drops_truncated_parts_of_squiggly_heredoc.txt b/test/prism/fixtures/whitequark/parser_drops_truncated_parts_of_squiggly_heredoc.txt
new file mode 100644
index 0000000000..bddae8e153
--- /dev/null
+++ b/test/prism/fixtures/whitequark/parser_drops_truncated_parts_of_squiggly_heredoc.txt
@@ -0,0 +1,3 @@
+<<~HERE
+ #{}
+HERE
diff --git a/test/prism/fixtures/whitequark/parser_slash_slash_n_escaping_in_literals.txt b/test/prism/fixtures/whitequark/parser_slash_slash_n_escaping_in_literals.txt
new file mode 100644
index 0000000000..564cebdd74
--- /dev/null
+++ b/test/prism/fixtures/whitequark/parser_slash_slash_n_escaping_in_literals.txt
@@ -0,0 +1,62 @@
+"a\
+b"
+
+%I{a\
+b}
+
+%Q{a\
+b}
+
+%W{a\
+b}
+
+%i{a\
+b}
+
+%q{a\
+b}
+
+%r{a\
+b}
+
+%s{a\
+b}
+
+%w{a\
+b}
+
+%x{a\
+b}
+
+%{a\
+b}
+
+'a\
+b'
+
+/a\
+b/
+
+:"a\
+b"
+
+:'a\
+b'
+
+<<-"HERE"
+a\
+b
+HERE
+
+<<-'HERE'
+a\
+b
+HERE
+
+<<-`HERE`
+a\
+b
+HERE
+
+`a\
+b`
diff --git a/test/prism/fixtures/whitequark/pattern_matching__FILE__LINE_literals.txt b/test/prism/fixtures/whitequark/pattern_matching__FILE__LINE_literals.txt
new file mode 100644
index 0000000000..fe0edaed55
--- /dev/null
+++ b/test/prism/fixtures/whitequark/pattern_matching__FILE__LINE_literals.txt
@@ -0,0 +1,4 @@
+ case [__FILE__, __LINE__ + 1, __ENCODING__]
+ in [__FILE__, __LINE__, __ENCODING__]
+ end
+
diff --git a/test/prism/fixtures/whitequark/pattern_matching_blank_else.txt b/test/prism/fixtures/whitequark/pattern_matching_blank_else.txt
new file mode 100644
index 0000000000..6bf059af41
--- /dev/null
+++ b/test/prism/fixtures/whitequark/pattern_matching_blank_else.txt
@@ -0,0 +1 @@
+case 1; in 2; 3; else; end
diff --git a/test/prism/fixtures/whitequark/pattern_matching_else.txt b/test/prism/fixtures/whitequark/pattern_matching_else.txt
new file mode 100644
index 0000000000..29f14c91ab
--- /dev/null
+++ b/test/prism/fixtures/whitequark/pattern_matching_else.txt
@@ -0,0 +1 @@
+case 1; in 2; 3; else; 4; end
diff --git a/test/prism/fixtures/whitequark/pattern_matching_single_line.txt b/test/prism/fixtures/whitequark/pattern_matching_single_line.txt
new file mode 100644
index 0000000000..12279afa24
--- /dev/null
+++ b/test/prism/fixtures/whitequark/pattern_matching_single_line.txt
@@ -0,0 +1,3 @@
+1 => [a]; a
+
+1 in [a]; a
diff --git a/test/prism/fixtures/whitequark/pattern_matching_single_line_allowed_omission_of_parentheses.txt b/test/prism/fixtures/whitequark/pattern_matching_single_line_allowed_omission_of_parentheses.txt
new file mode 100644
index 0000000000..1e429335d0
--- /dev/null
+++ b/test/prism/fixtures/whitequark/pattern_matching_single_line_allowed_omission_of_parentheses.txt
@@ -0,0 +1,11 @@
+[1, 2] => a, b; a
+
+[1, 2] in a, b; a
+
+{a: 1} => a:; a
+
+{a: 1} in a:; a
+
+{key: :value} => key: value; value
+
+{key: :value} in key: value; value
diff --git a/test/prism/fixtures/whitequark/postexe.txt b/test/prism/fixtures/whitequark/postexe.txt
new file mode 100644
index 0000000000..baee33af66
--- /dev/null
+++ b/test/prism/fixtures/whitequark/postexe.txt
@@ -0,0 +1 @@
+END { 1 }
diff --git a/test/prism/fixtures/whitequark/preexe.txt b/test/prism/fixtures/whitequark/preexe.txt
new file mode 100644
index 0000000000..9e802f3f4e
--- /dev/null
+++ b/test/prism/fixtures/whitequark/preexe.txt
@@ -0,0 +1 @@
+BEGIN { 1 }
diff --git a/test/prism/fixtures/whitequark/procarg0.txt b/test/prism/fixtures/whitequark/procarg0.txt
new file mode 100644
index 0000000000..74cae2c2eb
--- /dev/null
+++ b/test/prism/fixtures/whitequark/procarg0.txt
@@ -0,0 +1,3 @@
+m { |(foo, bar)| }
+
+m { |foo| }
diff --git a/test/prism/fixtures/whitequark/range_exclusive.txt b/test/prism/fixtures/whitequark/range_exclusive.txt
new file mode 100644
index 0000000000..9e07faed27
--- /dev/null
+++ b/test/prism/fixtures/whitequark/range_exclusive.txt
@@ -0,0 +1 @@
+1...2
diff --git a/test/prism/fixtures/whitequark/range_inclusive.txt b/test/prism/fixtures/whitequark/range_inclusive.txt
new file mode 100644
index 0000000000..8c12abf7de
--- /dev/null
+++ b/test/prism/fixtures/whitequark/range_inclusive.txt
@@ -0,0 +1 @@
+1..2
diff --git a/test/prism/fixtures/whitequark/rational.txt b/test/prism/fixtures/whitequark/rational.txt
new file mode 100644
index 0000000000..e11cacc742
--- /dev/null
+++ b/test/prism/fixtures/whitequark/rational.txt
@@ -0,0 +1,3 @@
+42.1r
+
+42r
diff --git a/test/prism/fixtures/whitequark/regex_interp.txt b/test/prism/fixtures/whitequark/regex_interp.txt
new file mode 100644
index 0000000000..f9ad7fcbc8
--- /dev/null
+++ b/test/prism/fixtures/whitequark/regex_interp.txt
@@ -0,0 +1 @@
+/foo#{bar}baz/
diff --git a/test/prism/fixtures/whitequark/regex_plain.txt b/test/prism/fixtures/whitequark/regex_plain.txt
new file mode 100644
index 0000000000..b86faecf98
--- /dev/null
+++ b/test/prism/fixtures/whitequark/regex_plain.txt
@@ -0,0 +1 @@
+/source/im
diff --git a/test/prism/fixtures/whitequark/resbody_list.txt b/test/prism/fixtures/whitequark/resbody_list.txt
new file mode 100644
index 0000000000..e40d45fc45
--- /dev/null
+++ b/test/prism/fixtures/whitequark/resbody_list.txt
@@ -0,0 +1 @@
+begin; meth; rescue Exception; bar; end
diff --git a/test/prism/fixtures/whitequark/resbody_list_mrhs.txt b/test/prism/fixtures/whitequark/resbody_list_mrhs.txt
new file mode 100644
index 0000000000..92b8bb2c02
--- /dev/null
+++ b/test/prism/fixtures/whitequark/resbody_list_mrhs.txt
@@ -0,0 +1 @@
+begin; meth; rescue Exception, foo; bar; end
diff --git a/test/prism/fixtures/whitequark/resbody_list_var.txt b/test/prism/fixtures/whitequark/resbody_list_var.txt
new file mode 100644
index 0000000000..0a2fb90b6d
--- /dev/null
+++ b/test/prism/fixtures/whitequark/resbody_list_var.txt
@@ -0,0 +1 @@
+begin; meth; rescue foo => ex; bar; end
diff --git a/test/prism/fixtures/whitequark/resbody_var.txt b/test/prism/fixtures/whitequark/resbody_var.txt
new file mode 100644
index 0000000000..2104ac58e7
--- /dev/null
+++ b/test/prism/fixtures/whitequark/resbody_var.txt
@@ -0,0 +1,3 @@
+begin; meth; rescue => @ex; bar; end
+
+begin; meth; rescue => ex; bar; end
diff --git a/test/prism/fixtures/whitequark/rescue.txt b/test/prism/fixtures/whitequark/rescue.txt
new file mode 100644
index 0000000000..2d3be9dc56
--- /dev/null
+++ b/test/prism/fixtures/whitequark/rescue.txt
@@ -0,0 +1 @@
+begin; meth; rescue; foo; end
diff --git a/test/prism/fixtures/whitequark/rescue_else.txt b/test/prism/fixtures/whitequark/rescue_else.txt
new file mode 100644
index 0000000000..a22f8d100e
--- /dev/null
+++ b/test/prism/fixtures/whitequark/rescue_else.txt
@@ -0,0 +1 @@
+begin; meth; rescue; foo; else; bar; end
diff --git a/test/prism/fixtures/whitequark/rescue_else_ensure.txt b/test/prism/fixtures/whitequark/rescue_else_ensure.txt
new file mode 100644
index 0000000000..167eee194a
--- /dev/null
+++ b/test/prism/fixtures/whitequark/rescue_else_ensure.txt
@@ -0,0 +1 @@
+begin; meth; rescue; baz; else foo; ensure; bar end
diff --git a/test/prism/fixtures/whitequark/rescue_ensure.txt b/test/prism/fixtures/whitequark/rescue_ensure.txt
new file mode 100644
index 0000000000..8237257c41
--- /dev/null
+++ b/test/prism/fixtures/whitequark/rescue_ensure.txt
@@ -0,0 +1 @@
+begin; meth; rescue; baz; ensure; bar; end
diff --git a/test/prism/fixtures/whitequark/rescue_in_lambda_block.txt b/test/prism/fixtures/whitequark/rescue_in_lambda_block.txt
new file mode 100644
index 0000000000..ccd8ed72c5
--- /dev/null
+++ b/test/prism/fixtures/whitequark/rescue_in_lambda_block.txt
@@ -0,0 +1 @@
+-> do rescue; end
diff --git a/test/prism/fixtures/whitequark/rescue_mod.txt b/test/prism/fixtures/whitequark/rescue_mod.txt
new file mode 100644
index 0000000000..06375d3e9b
--- /dev/null
+++ b/test/prism/fixtures/whitequark/rescue_mod.txt
@@ -0,0 +1 @@
+meth rescue bar
diff --git a/test/prism/fixtures/whitequark/rescue_mod_asgn.txt b/test/prism/fixtures/whitequark/rescue_mod_asgn.txt
new file mode 100644
index 0000000000..abf7cd9a3d
--- /dev/null
+++ b/test/prism/fixtures/whitequark/rescue_mod_asgn.txt
@@ -0,0 +1 @@
+foo = meth rescue bar
diff --git a/test/prism/fixtures/whitequark/rescue_mod_masgn.txt b/test/prism/fixtures/whitequark/rescue_mod_masgn.txt
new file mode 100644
index 0000000000..0716eb1f67
--- /dev/null
+++ b/test/prism/fixtures/whitequark/rescue_mod_masgn.txt
@@ -0,0 +1 @@
+foo, bar = meth rescue [1, 2]
diff --git a/test/prism/fixtures/whitequark/rescue_mod_op_assign.txt b/test/prism/fixtures/whitequark/rescue_mod_op_assign.txt
new file mode 100644
index 0000000000..178efa3a20
--- /dev/null
+++ b/test/prism/fixtures/whitequark/rescue_mod_op_assign.txt
@@ -0,0 +1 @@
+foo += meth rescue bar
diff --git a/test/prism/fixtures/whitequark/rescue_without_begin_end.txt b/test/prism/fixtures/whitequark/rescue_without_begin_end.txt
new file mode 100644
index 0000000000..8416569e2c
--- /dev/null
+++ b/test/prism/fixtures/whitequark/rescue_without_begin_end.txt
@@ -0,0 +1 @@
+meth do; foo; rescue; bar; end
diff --git a/test/prism/fixtures/whitequark/restarg_named.txt b/test/prism/fixtures/whitequark/restarg_named.txt
new file mode 100644
index 0000000000..355cd8f493
--- /dev/null
+++ b/test/prism/fixtures/whitequark/restarg_named.txt
@@ -0,0 +1 @@
+def f(*foo); end
diff --git a/test/prism/fixtures/whitequark/restarg_unnamed.txt b/test/prism/fixtures/whitequark/restarg_unnamed.txt
new file mode 100644
index 0000000000..c9932dd30c
--- /dev/null
+++ b/test/prism/fixtures/whitequark/restarg_unnamed.txt
@@ -0,0 +1 @@
+def f(*); end
diff --git a/test/prism/fixtures/whitequark/return.txt b/test/prism/fixtures/whitequark/return.txt
new file mode 100644
index 0000000000..e3d966bda0
--- /dev/null
+++ b/test/prism/fixtures/whitequark/return.txt
@@ -0,0 +1,7 @@
+return
+
+return foo
+
+return()
+
+return(foo)
diff --git a/test/prism/fixtures/whitequark/return_block.txt b/test/prism/fixtures/whitequark/return_block.txt
new file mode 100644
index 0000000000..00723a7517
--- /dev/null
+++ b/test/prism/fixtures/whitequark/return_block.txt
@@ -0,0 +1 @@
+return fun foo do end
diff --git a/test/prism/fixtures/whitequark/ruby_bug_10279.txt b/test/prism/fixtures/whitequark/ruby_bug_10279.txt
new file mode 100644
index 0000000000..4d0fb213d8
--- /dev/null
+++ b/test/prism/fixtures/whitequark/ruby_bug_10279.txt
@@ -0,0 +1 @@
+{a: if true then 42 end}
diff --git a/test/prism/fixtures/whitequark/ruby_bug_10653.txt b/test/prism/fixtures/whitequark/ruby_bug_10653.txt
new file mode 100644
index 0000000000..51354a1f70
--- /dev/null
+++ b/test/prism/fixtures/whitequark/ruby_bug_10653.txt
@@ -0,0 +1,5 @@
+false ? raise do end : tap do end
+
+false ? raise {} : tap {}
+
+true ? 1.tap do |n| p n end : 0
diff --git a/test/prism/fixtures/whitequark/ruby_bug_11107.txt b/test/prism/fixtures/whitequark/ruby_bug_11107.txt
new file mode 100644
index 0000000000..1f28cb06f6
--- /dev/null
+++ b/test/prism/fixtures/whitequark/ruby_bug_11107.txt
@@ -0,0 +1 @@
+p ->() do a() do end end
diff --git a/test/prism/fixtures/whitequark/ruby_bug_11380.txt b/test/prism/fixtures/whitequark/ruby_bug_11380.txt
new file mode 100644
index 0000000000..1631548e3a
--- /dev/null
+++ b/test/prism/fixtures/whitequark/ruby_bug_11380.txt
@@ -0,0 +1 @@
+p -> { :hello }, a: 1 do end
diff --git a/test/prism/fixtures/whitequark/ruby_bug_11873.txt b/test/prism/fixtures/whitequark/ruby_bug_11873.txt
new file mode 100644
index 0000000000..a25aa9e267
--- /dev/null
+++ b/test/prism/fixtures/whitequark/ruby_bug_11873.txt
@@ -0,0 +1,23 @@
+a b(c d), "x" do end
+
+a b(c d), /x/ do end
+
+a b(c d), /x/m do end
+
+a b(c(d)), "x" do end
+
+a b(c(d)), /x/ do end
+
+a b(c(d)), /x/m do end
+
+a b{c d}, "x" do end
+
+a b{c d}, /x/ do end
+
+a b{c d}, /x/m do end
+
+a b{c(d)}, "x" do end
+
+a b{c(d)}, /x/ do end
+
+a b{c(d)}, /x/m do end
diff --git a/test/prism/fixtures/whitequark/ruby_bug_11873_a.txt b/test/prism/fixtures/whitequark/ruby_bug_11873_a.txt
new file mode 100644
index 0000000000..1856235ce5
--- /dev/null
+++ b/test/prism/fixtures/whitequark/ruby_bug_11873_a.txt
@@ -0,0 +1,39 @@
+a b(c d), 1 do end
+
+a b(c d), 1.0 do end
+
+a b(c d), 1.0i do end
+
+a b(c d), 1.0r do end
+
+a b(c d), :e do end
+
+a b(c(d)), 1 do end
+
+a b(c(d)), 1.0 do end
+
+a b(c(d)), 1.0i do end
+
+a b(c(d)), 1.0r do end
+
+a b(c(d)), :e do end
+
+a b{c d}, 1 do end
+
+a b{c d}, 1.0 do end
+
+a b{c d}, 1.0i do end
+
+a b{c d}, 1.0r do end
+
+a b{c d}, :e do end
+
+a b{c(d)}, 1 do end
+
+a b{c(d)}, 1.0 do end
+
+a b{c(d)}, 1.0i do end
+
+a b{c(d)}, 1.0r do end
+
+a b{c(d)}, :e do end
diff --git a/test/prism/fixtures/whitequark/ruby_bug_11873_b.txt b/test/prism/fixtures/whitequark/ruby_bug_11873_b.txt
new file mode 100644
index 0000000000..1b86662008
--- /dev/null
+++ b/test/prism/fixtures/whitequark/ruby_bug_11873_b.txt
@@ -0,0 +1 @@
+p p{p(p);p p}, tap do end
diff --git a/test/prism/fixtures/whitequark/ruby_bug_11989.txt b/test/prism/fixtures/whitequark/ruby_bug_11989.txt
new file mode 100644
index 0000000000..d49b8614ed
--- /dev/null
+++ b/test/prism/fixtures/whitequark/ruby_bug_11989.txt
@@ -0,0 +1,3 @@
+p <<~"E"
+ x\n y
+E
diff --git a/test/prism/fixtures/whitequark/ruby_bug_11990.txt b/test/prism/fixtures/whitequark/ruby_bug_11990.txt
new file mode 100644
index 0000000000..d0fe7b2739
--- /dev/null
+++ b/test/prism/fixtures/whitequark/ruby_bug_11990.txt
@@ -0,0 +1,3 @@
+p <<~E " y"
+ x
+E
diff --git a/test/prism/fixtures/whitequark/ruby_bug_12073.txt b/test/prism/fixtures/whitequark/ruby_bug_12073.txt
new file mode 100644
index 0000000000..b2e3784422
--- /dev/null
+++ b/test/prism/fixtures/whitequark/ruby_bug_12073.txt
@@ -0,0 +1,3 @@
+a = 1; a b: 1
+
+def foo raise; raise A::B, ''; end
diff --git a/test/prism/fixtures/whitequark/ruby_bug_12402.txt b/test/prism/fixtures/whitequark/ruby_bug_12402.txt
new file mode 100644
index 0000000000..060d5d95a1
--- /dev/null
+++ b/test/prism/fixtures/whitequark/ruby_bug_12402.txt
@@ -0,0 +1,27 @@
+foo += raise bar rescue nil
+
+foo += raise(bar) rescue nil
+
+foo = raise bar rescue nil
+
+foo = raise(bar) rescue nil
+
+foo.C += raise bar rescue nil
+
+foo.C += raise(bar) rescue nil
+
+foo.m += raise bar rescue nil
+
+foo.m += raise(bar) rescue nil
+
+foo::C ||= raise bar rescue nil
+
+foo::C ||= raise(bar) rescue nil
+
+foo::m += raise bar rescue nil
+
+foo::m += raise(bar) rescue nil
+
+foo[0] += raise bar rescue nil
+
+foo[0] += raise(bar) rescue nil
diff --git a/test/prism/fixtures/whitequark/ruby_bug_12669.txt b/test/prism/fixtures/whitequark/ruby_bug_12669.txt
new file mode 100644
index 0000000000..cd89689446
--- /dev/null
+++ b/test/prism/fixtures/whitequark/ruby_bug_12669.txt
@@ -0,0 +1,7 @@
+a += b += raise :x
+
+a += b = raise :x
+
+a = b += raise :x
+
+a = b = raise :x
diff --git a/test/prism/fixtures/whitequark/ruby_bug_12686.txt b/test/prism/fixtures/whitequark/ruby_bug_12686.txt
new file mode 100644
index 0000000000..7742e791b8
--- /dev/null
+++ b/test/prism/fixtures/whitequark/ruby_bug_12686.txt
@@ -0,0 +1 @@
+f (g rescue nil)
diff --git a/test/prism/fixtures/whitequark/ruby_bug_13547.txt b/test/prism/fixtures/whitequark/ruby_bug_13547.txt
new file mode 100644
index 0000000000..29eafc3a4c
--- /dev/null
+++ b/test/prism/fixtures/whitequark/ruby_bug_13547.txt
@@ -0,0 +1 @@
+meth[] {}
diff --git a/test/prism/fixtures/whitequark/ruby_bug_14690.txt b/test/prism/fixtures/whitequark/ruby_bug_14690.txt
new file mode 100644
index 0000000000..b73b1d8aad
--- /dev/null
+++ b/test/prism/fixtures/whitequark/ruby_bug_14690.txt
@@ -0,0 +1 @@
+let () { m(a) do; end }
diff --git a/test/prism/fixtures/whitequark/ruby_bug_15789.txt b/test/prism/fixtures/whitequark/ruby_bug_15789.txt
new file mode 100644
index 0000000000..6324db5110
--- /dev/null
+++ b/test/prism/fixtures/whitequark/ruby_bug_15789.txt
@@ -0,0 +1,3 @@
+m ->(a = ->{_1}) {a}
+
+m ->(a: ->{_1}) {a}
diff --git a/test/prism/fixtures/whitequark/ruby_bug_9669.txt b/test/prism/fixtures/whitequark/ruby_bug_9669.txt
new file mode 100644
index 0000000000..60dfa09d51
--- /dev/null
+++ b/test/prism/fixtures/whitequark/ruby_bug_9669.txt
@@ -0,0 +1,8 @@
+def a b:
+return
+end
+
+o = {
+a:
+1
+}
diff --git a/test/prism/fixtures/whitequark/sclass.txt b/test/prism/fixtures/whitequark/sclass.txt
new file mode 100644
index 0000000000..e6aadaef21
--- /dev/null
+++ b/test/prism/fixtures/whitequark/sclass.txt
@@ -0,0 +1 @@
+class << foo; nil; end
diff --git a/test/prism/fixtures/whitequark/self.txt b/test/prism/fixtures/whitequark/self.txt
new file mode 100644
index 0000000000..31f9efa4a1
--- /dev/null
+++ b/test/prism/fixtures/whitequark/self.txt
@@ -0,0 +1 @@
+self
diff --git a/test/prism/fixtures/whitequark/send_attr_asgn.txt b/test/prism/fixtures/whitequark/send_attr_asgn.txt
new file mode 100644
index 0000000000..b477966b2a
--- /dev/null
+++ b/test/prism/fixtures/whitequark/send_attr_asgn.txt
@@ -0,0 +1,7 @@
+foo.A = 1
+
+foo.a = 1
+
+foo::A = 1
+
+foo::a = 1
diff --git a/test/prism/fixtures/whitequark/send_attr_asgn_conditional.txt b/test/prism/fixtures/whitequark/send_attr_asgn_conditional.txt
new file mode 100644
index 0000000000..1279e02cfc
--- /dev/null
+++ b/test/prism/fixtures/whitequark/send_attr_asgn_conditional.txt
@@ -0,0 +1 @@
+a&.b = 1
diff --git a/test/prism/fixtures/whitequark/send_binary_op.txt b/test/prism/fixtures/whitequark/send_binary_op.txt
new file mode 100644
index 0000000000..3e3e9300b3
--- /dev/null
+++ b/test/prism/fixtures/whitequark/send_binary_op.txt
@@ -0,0 +1,41 @@
+foo != 1
+
+foo !~ 1
+
+foo % 1
+
+foo & 1
+
+foo * 1
+
+foo ** 1
+
+foo + 1
+
+foo - 1
+
+foo / 1
+
+foo < 1
+
+foo << 1
+
+foo <= 1
+
+foo <=> 1
+
+foo == 1
+
+foo === 1
+
+foo =~ 1
+
+foo > 1
+
+foo >= 1
+
+foo >> 1
+
+foo ^ 1
+
+foo | 1
diff --git a/test/prism/fixtures/whitequark/send_block_chain_cmd.txt b/test/prism/fixtures/whitequark/send_block_chain_cmd.txt
new file mode 100644
index 0000000000..c6fe1aab0e
--- /dev/null
+++ b/test/prism/fixtures/whitequark/send_block_chain_cmd.txt
@@ -0,0 +1,13 @@
+meth 1 do end.fun bar
+
+meth 1 do end.fun bar do end
+
+meth 1 do end.fun {}
+
+meth 1 do end.fun(bar)
+
+meth 1 do end.fun(bar) {}
+
+meth 1 do end::fun bar
+
+meth 1 do end::fun(bar)
diff --git a/test/prism/fixtures/whitequark/send_block_conditional.txt b/test/prism/fixtures/whitequark/send_block_conditional.txt
new file mode 100644
index 0000000000..dcc8361762
--- /dev/null
+++ b/test/prism/fixtures/whitequark/send_block_conditional.txt
@@ -0,0 +1 @@
+foo&.bar {}
diff --git a/test/prism/fixtures/whitequark/send_call.txt b/test/prism/fixtures/whitequark/send_call.txt
new file mode 100644
index 0000000000..99701270bb
--- /dev/null
+++ b/test/prism/fixtures/whitequark/send_call.txt
@@ -0,0 +1,3 @@
+foo.(1)
+
+foo::(1)
diff --git a/test/prism/fixtures/whitequark/send_conditional.txt b/test/prism/fixtures/whitequark/send_conditional.txt
new file mode 100644
index 0000000000..8ecd27e0fe
--- /dev/null
+++ b/test/prism/fixtures/whitequark/send_conditional.txt
@@ -0,0 +1 @@
+a&.b
diff --git a/test/prism/fixtures/whitequark/send_index.txt b/test/prism/fixtures/whitequark/send_index.txt
new file mode 100644
index 0000000000..f9c4dafc4e
--- /dev/null
+++ b/test/prism/fixtures/whitequark/send_index.txt
@@ -0,0 +1 @@
+foo[1, 2]
diff --git a/test/prism/fixtures/whitequark/send_index_asgn.txt b/test/prism/fixtures/whitequark/send_index_asgn.txt
new file mode 100644
index 0000000000..e232fa3b96
--- /dev/null
+++ b/test/prism/fixtures/whitequark/send_index_asgn.txt
@@ -0,0 +1 @@
+foo[1, 2] = 3
diff --git a/test/prism/fixtures/whitequark/send_index_asgn_legacy.txt b/test/prism/fixtures/whitequark/send_index_asgn_legacy.txt
new file mode 100644
index 0000000000..e232fa3b96
--- /dev/null
+++ b/test/prism/fixtures/whitequark/send_index_asgn_legacy.txt
@@ -0,0 +1 @@
+foo[1, 2] = 3
diff --git a/test/prism/fixtures/whitequark/send_index_cmd.txt b/test/prism/fixtures/whitequark/send_index_cmd.txt
new file mode 100644
index 0000000000..32090e7536
--- /dev/null
+++ b/test/prism/fixtures/whitequark/send_index_cmd.txt
@@ -0,0 +1 @@
+foo[m bar]
diff --git a/test/prism/fixtures/whitequark/send_index_legacy.txt b/test/prism/fixtures/whitequark/send_index_legacy.txt
new file mode 100644
index 0000000000..f9c4dafc4e
--- /dev/null
+++ b/test/prism/fixtures/whitequark/send_index_legacy.txt
@@ -0,0 +1 @@
+foo[1, 2]
diff --git a/test/prism/fixtures/whitequark/send_lambda.txt b/test/prism/fixtures/whitequark/send_lambda.txt
new file mode 100644
index 0000000000..eadd6c9c03
--- /dev/null
+++ b/test/prism/fixtures/whitequark/send_lambda.txt
@@ -0,0 +1,5 @@
+-> * { }
+
+-> do end
+
+->{ }
diff --git a/test/prism/fixtures/whitequark/send_lambda_args.txt b/test/prism/fixtures/whitequark/send_lambda_args.txt
new file mode 100644
index 0000000000..68392f2f34
--- /dev/null
+++ b/test/prism/fixtures/whitequark/send_lambda_args.txt
@@ -0,0 +1,3 @@
+-> (a) { }
+
+->(a) { }
diff --git a/test/prism/fixtures/whitequark/send_lambda_args_noparen.txt b/test/prism/fixtures/whitequark/send_lambda_args_noparen.txt
new file mode 100644
index 0000000000..c0ae077f95
--- /dev/null
+++ b/test/prism/fixtures/whitequark/send_lambda_args_noparen.txt
@@ -0,0 +1,3 @@
+-> a: 1 { }
+
+-> a: { }
diff --git a/test/prism/fixtures/whitequark/send_lambda_args_shadow.txt b/test/prism/fixtures/whitequark/send_lambda_args_shadow.txt
new file mode 100644
index 0000000000..230f35ab89
--- /dev/null
+++ b/test/prism/fixtures/whitequark/send_lambda_args_shadow.txt
@@ -0,0 +1 @@
+->(a; foo, bar) { }
diff --git a/test/prism/fixtures/whitequark/send_lambda_legacy.txt b/test/prism/fixtures/whitequark/send_lambda_legacy.txt
new file mode 100644
index 0000000000..a509407c43
--- /dev/null
+++ b/test/prism/fixtures/whitequark/send_lambda_legacy.txt
@@ -0,0 +1 @@
+->{ }
diff --git a/test/prism/fixtures/whitequark/send_op_asgn_conditional.txt b/test/prism/fixtures/whitequark/send_op_asgn_conditional.txt
new file mode 100644
index 0000000000..906088dcd1
--- /dev/null
+++ b/test/prism/fixtures/whitequark/send_op_asgn_conditional.txt
@@ -0,0 +1 @@
+a&.b &&= 1
diff --git a/test/prism/fixtures/whitequark/send_plain.txt b/test/prism/fixtures/whitequark/send_plain.txt
new file mode 100644
index 0000000000..ebaf1d18c7
--- /dev/null
+++ b/test/prism/fixtures/whitequark/send_plain.txt
@@ -0,0 +1,5 @@
+foo.fun
+
+foo::Fun()
+
+foo::fun
diff --git a/test/prism/fixtures/whitequark/send_plain_cmd.txt b/test/prism/fixtures/whitequark/send_plain_cmd.txt
new file mode 100644
index 0000000000..e3fd63f272
--- /dev/null
+++ b/test/prism/fixtures/whitequark/send_plain_cmd.txt
@@ -0,0 +1,5 @@
+foo.fun bar
+
+foo::Fun bar
+
+foo::fun bar
diff --git a/test/prism/fixtures/whitequark/send_self.txt b/test/prism/fixtures/whitequark/send_self.txt
new file mode 100644
index 0000000000..f084b9bca7
--- /dev/null
+++ b/test/prism/fixtures/whitequark/send_self.txt
@@ -0,0 +1,5 @@
+fun
+
+fun!
+
+fun(1)
diff --git a/test/prism/fixtures/whitequark/send_self_block.txt b/test/prism/fixtures/whitequark/send_self_block.txt
new file mode 100644
index 0000000000..1cd6703c82
--- /dev/null
+++ b/test/prism/fixtures/whitequark/send_self_block.txt
@@ -0,0 +1,7 @@
+fun do end
+
+fun { }
+
+fun() { }
+
+fun(1) { }
diff --git a/test/prism/fixtures/whitequark/send_unary_op.txt b/test/prism/fixtures/whitequark/send_unary_op.txt
new file mode 100644
index 0000000000..73814d199f
--- /dev/null
+++ b/test/prism/fixtures/whitequark/send_unary_op.txt
@@ -0,0 +1,5 @@
++foo
+
+-foo
+
+~foo
diff --git a/test/prism/fixtures/whitequark/slash_newline_in_heredocs.txt b/test/prism/fixtures/whitequark/slash_newline_in_heredocs.txt
new file mode 100644
index 0000000000..4962a058ea
--- /dev/null
+++ b/test/prism/fixtures/whitequark/slash_newline_in_heredocs.txt
@@ -0,0 +1,13 @@
+<<-E
+ 1 \
+ 2
+ 3
+E
+
+
+<<~E
+ 1 \
+ 2
+ 3
+E
+
diff --git a/test/prism/fixtures/whitequark/space_args_arg.txt b/test/prism/fixtures/whitequark/space_args_arg.txt
new file mode 100644
index 0000000000..47957cba54
--- /dev/null
+++ b/test/prism/fixtures/whitequark/space_args_arg.txt
@@ -0,0 +1 @@
+fun (1)
diff --git a/test/prism/fixtures/whitequark/space_args_arg_block.txt b/test/prism/fixtures/whitequark/space_args_arg_block.txt
new file mode 100644
index 0000000000..5560a82818
--- /dev/null
+++ b/test/prism/fixtures/whitequark/space_args_arg_block.txt
@@ -0,0 +1,5 @@
+foo.fun (1) {}
+
+foo::fun (1) {}
+
+fun (1) {}
diff --git a/test/prism/fixtures/whitequark/space_args_arg_call.txt b/test/prism/fixtures/whitequark/space_args_arg_call.txt
new file mode 100644
index 0000000000..3b0ae831fe
--- /dev/null
+++ b/test/prism/fixtures/whitequark/space_args_arg_call.txt
@@ -0,0 +1 @@
+fun (1).to_i
diff --git a/test/prism/fixtures/whitequark/space_args_arg_newline.txt b/test/prism/fixtures/whitequark/space_args_arg_newline.txt
new file mode 100644
index 0000000000..a6cdac6ed1
--- /dev/null
+++ b/test/prism/fixtures/whitequark/space_args_arg_newline.txt
@@ -0,0 +1,2 @@
+fun (1
+)
diff --git a/test/prism/fixtures/whitequark/space_args_block.txt b/test/prism/fixtures/whitequark/space_args_block.txt
new file mode 100644
index 0000000000..555a097605
--- /dev/null
+++ b/test/prism/fixtures/whitequark/space_args_block.txt
@@ -0,0 +1 @@
+fun () {}
diff --git a/test/prism/fixtures/whitequark/space_args_cmd.txt b/test/prism/fixtures/whitequark/space_args_cmd.txt
new file mode 100644
index 0000000000..a749695cb7
--- /dev/null
+++ b/test/prism/fixtures/whitequark/space_args_cmd.txt
@@ -0,0 +1 @@
+fun (f bar)
diff --git a/test/prism/fixtures/whitequark/string___FILE__.txt b/test/prism/fixtures/whitequark/string___FILE__.txt
new file mode 100644
index 0000000000..4815727d05
--- /dev/null
+++ b/test/prism/fixtures/whitequark/string___FILE__.txt
@@ -0,0 +1 @@
+__FILE__
diff --git a/test/prism/fixtures/whitequark/string_concat.txt b/test/prism/fixtures/whitequark/string_concat.txt
new file mode 100644
index 0000000000..30cc4f8116
--- /dev/null
+++ b/test/prism/fixtures/whitequark/string_concat.txt
@@ -0,0 +1 @@
+"foo#@a" "bar"
diff --git a/test/prism/fixtures/whitequark/string_dvar.txt b/test/prism/fixtures/whitequark/string_dvar.txt
new file mode 100644
index 0000000000..bbe5b617b2
--- /dev/null
+++ b/test/prism/fixtures/whitequark/string_dvar.txt
@@ -0,0 +1 @@
+"#@a #@@a #$a"
diff --git a/test/prism/fixtures/whitequark/string_interp.txt b/test/prism/fixtures/whitequark/string_interp.txt
new file mode 100644
index 0000000000..5fdd803341
--- /dev/null
+++ b/test/prism/fixtures/whitequark/string_interp.txt
@@ -0,0 +1 @@
+"foo#{bar}baz"
diff --git a/test/prism/fixtures/whitequark/string_plain.txt b/test/prism/fixtures/whitequark/string_plain.txt
new file mode 100644
index 0000000000..4aca78decb
--- /dev/null
+++ b/test/prism/fixtures/whitequark/string_plain.txt
@@ -0,0 +1,3 @@
+%q(foobar)
+
+'foobar'
diff --git a/test/prism/fixtures/whitequark/super.txt b/test/prism/fixtures/whitequark/super.txt
new file mode 100644
index 0000000000..9e68a9e988
--- /dev/null
+++ b/test/prism/fixtures/whitequark/super.txt
@@ -0,0 +1,5 @@
+super foo
+
+super()
+
+super(foo)
diff --git a/test/prism/fixtures/whitequark/super_block.txt b/test/prism/fixtures/whitequark/super_block.txt
new file mode 100644
index 0000000000..05a7d7fdd8
--- /dev/null
+++ b/test/prism/fixtures/whitequark/super_block.txt
@@ -0,0 +1,3 @@
+super do end
+
+super foo, bar do end
diff --git a/test/prism/fixtures/whitequark/symbol_interp.txt b/test/prism/fixtures/whitequark/symbol_interp.txt
new file mode 100644
index 0000000000..d5011b270d
--- /dev/null
+++ b/test/prism/fixtures/whitequark/symbol_interp.txt
@@ -0,0 +1 @@
+:"foo#{bar}baz"
diff --git a/test/prism/fixtures/whitequark/symbol_plain.txt b/test/prism/fixtures/whitequark/symbol_plain.txt
new file mode 100644
index 0000000000..fd1fd0017c
--- /dev/null
+++ b/test/prism/fixtures/whitequark/symbol_plain.txt
@@ -0,0 +1,3 @@
+:'foo'
+
+:foo
diff --git a/test/prism/fixtures/whitequark/ternary.txt b/test/prism/fixtures/whitequark/ternary.txt
new file mode 100644
index 0000000000..3a149d4e1c
--- /dev/null
+++ b/test/prism/fixtures/whitequark/ternary.txt
@@ -0,0 +1 @@
+foo ? 1 : 2
diff --git a/test/prism/fixtures/whitequark/ternary_ambiguous_symbol.txt b/test/prism/fixtures/whitequark/ternary_ambiguous_symbol.txt
new file mode 100644
index 0000000000..19aa523c07
--- /dev/null
+++ b/test/prism/fixtures/whitequark/ternary_ambiguous_symbol.txt
@@ -0,0 +1 @@
+t=1;(foo)?t:T
diff --git a/test/prism/fixtures/whitequark/trailing_forward_arg.txt b/test/prism/fixtures/whitequark/trailing_forward_arg.txt
new file mode 100644
index 0000000000..043870ade2
--- /dev/null
+++ b/test/prism/fixtures/whitequark/trailing_forward_arg.txt
@@ -0,0 +1 @@
+def foo(a, b, ...); bar(a, 42, ...); end
diff --git a/test/prism/fixtures/whitequark/true.txt b/test/prism/fixtures/whitequark/true.txt
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/test/prism/fixtures/whitequark/true.txt
@@ -0,0 +1 @@
+true
diff --git a/test/prism/fixtures/whitequark/unary_num_pow_precedence.txt b/test/prism/fixtures/whitequark/unary_num_pow_precedence.txt
new file mode 100644
index 0000000000..f0343e3c8b
--- /dev/null
+++ b/test/prism/fixtures/whitequark/unary_num_pow_precedence.txt
@@ -0,0 +1,5 @@
++2.0 ** 10
+
+-2 ** 10
+
+-2.0 ** 10
diff --git a/test/prism/fixtures/whitequark/undef.txt b/test/prism/fixtures/whitequark/undef.txt
new file mode 100644
index 0000000000..3e88ec7084
--- /dev/null
+++ b/test/prism/fixtures/whitequark/undef.txt
@@ -0,0 +1 @@
+undef foo, :bar, :"foo#{1}"
diff --git a/test/prism/fixtures/whitequark/unless.txt b/test/prism/fixtures/whitequark/unless.txt
new file mode 100644
index 0000000000..d04043ed90
--- /dev/null
+++ b/test/prism/fixtures/whitequark/unless.txt
@@ -0,0 +1,3 @@
+unless foo then bar; end
+
+unless foo; bar; end
diff --git a/test/prism/fixtures/whitequark/unless_else.txt b/test/prism/fixtures/whitequark/unless_else.txt
new file mode 100644
index 0000000000..8243d42031
--- /dev/null
+++ b/test/prism/fixtures/whitequark/unless_else.txt
@@ -0,0 +1,3 @@
+unless foo then bar; else baz; end
+
+unless foo; bar; else baz; end
diff --git a/test/prism/fixtures/whitequark/unless_mod.txt b/test/prism/fixtures/whitequark/unless_mod.txt
new file mode 100644
index 0000000000..2d0376a310
--- /dev/null
+++ b/test/prism/fixtures/whitequark/unless_mod.txt
@@ -0,0 +1 @@
+bar unless foo
diff --git a/test/prism/fixtures/whitequark/until.txt b/test/prism/fixtures/whitequark/until.txt
new file mode 100644
index 0000000000..06082233cb
--- /dev/null
+++ b/test/prism/fixtures/whitequark/until.txt
@@ -0,0 +1,3 @@
+until foo do meth end
+
+until foo; meth end
diff --git a/test/prism/fixtures/whitequark/until_mod.txt b/test/prism/fixtures/whitequark/until_mod.txt
new file mode 100644
index 0000000000..46a85d2ba9
--- /dev/null
+++ b/test/prism/fixtures/whitequark/until_mod.txt
@@ -0,0 +1 @@
+meth until foo
diff --git a/test/prism/fixtures/whitequark/until_post.txt b/test/prism/fixtures/whitequark/until_post.txt
new file mode 100644
index 0000000000..988e43b665
--- /dev/null
+++ b/test/prism/fixtures/whitequark/until_post.txt
@@ -0,0 +1 @@
+begin meth end until foo
diff --git a/test/prism/fixtures/whitequark/var_and_asgn.txt b/test/prism/fixtures/whitequark/var_and_asgn.txt
new file mode 100644
index 0000000000..25502968f9
--- /dev/null
+++ b/test/prism/fixtures/whitequark/var_and_asgn.txt
@@ -0,0 +1 @@
+a &&= 1
diff --git a/test/prism/fixtures/whitequark/var_op_asgn.txt b/test/prism/fixtures/whitequark/var_op_asgn.txt
new file mode 100644
index 0000000000..402d818a7e
--- /dev/null
+++ b/test/prism/fixtures/whitequark/var_op_asgn.txt
@@ -0,0 +1,7 @@
+@@var |= 10
+
+@a |= 1
+
+a += 1
+
+def a; @@var |= 10; end
diff --git a/test/prism/fixtures/whitequark/var_op_asgn_cmd.txt b/test/prism/fixtures/whitequark/var_op_asgn_cmd.txt
new file mode 100644
index 0000000000..33f4bc0e73
--- /dev/null
+++ b/test/prism/fixtures/whitequark/var_op_asgn_cmd.txt
@@ -0,0 +1 @@
+foo += m foo
diff --git a/test/prism/fixtures/whitequark/var_or_asgn.txt b/test/prism/fixtures/whitequark/var_or_asgn.txt
new file mode 100644
index 0000000000..aa30b3d5ca
--- /dev/null
+++ b/test/prism/fixtures/whitequark/var_or_asgn.txt
@@ -0,0 +1 @@
+a ||= 1
diff --git a/test/prism/fixtures/whitequark/when_multi.txt b/test/prism/fixtures/whitequark/when_multi.txt
new file mode 100644
index 0000000000..b4fbd33125
--- /dev/null
+++ b/test/prism/fixtures/whitequark/when_multi.txt
@@ -0,0 +1 @@
+case foo; when 'bar', 'baz'; bar; end
diff --git a/test/prism/fixtures/whitequark/when_splat.txt b/test/prism/fixtures/whitequark/when_splat.txt
new file mode 100644
index 0000000000..695e5da34e
--- /dev/null
+++ b/test/prism/fixtures/whitequark/when_splat.txt
@@ -0,0 +1 @@
+case foo; when 1, *baz; bar; when *foo; end
diff --git a/test/prism/fixtures/whitequark/when_then.txt b/test/prism/fixtures/whitequark/when_then.txt
new file mode 100644
index 0000000000..63293452b3
--- /dev/null
+++ b/test/prism/fixtures/whitequark/when_then.txt
@@ -0,0 +1 @@
+case foo; when 'bar' then bar; end
diff --git a/test/prism/fixtures/whitequark/while.txt b/test/prism/fixtures/whitequark/while.txt
new file mode 100644
index 0000000000..28b204f247
--- /dev/null
+++ b/test/prism/fixtures/whitequark/while.txt
@@ -0,0 +1,3 @@
+while foo do meth end
+
+while foo; meth end
diff --git a/test/prism/fixtures/whitequark/while_mod.txt b/test/prism/fixtures/whitequark/while_mod.txt
new file mode 100644
index 0000000000..ce3cf01d14
--- /dev/null
+++ b/test/prism/fixtures/whitequark/while_mod.txt
@@ -0,0 +1 @@
+meth while foo
diff --git a/test/prism/fixtures/whitequark/while_post.txt b/test/prism/fixtures/whitequark/while_post.txt
new file mode 100644
index 0000000000..ac6e05008b
--- /dev/null
+++ b/test/prism/fixtures/whitequark/while_post.txt
@@ -0,0 +1 @@
+begin meth end while foo
diff --git a/test/prism/fixtures/whitequark/xstring_interp.txt b/test/prism/fixtures/whitequark/xstring_interp.txt
new file mode 100644
index 0000000000..dfede8123f
--- /dev/null
+++ b/test/prism/fixtures/whitequark/xstring_interp.txt
@@ -0,0 +1 @@
+`foo#{bar}baz`
diff --git a/test/prism/fixtures/whitequark/xstring_plain.txt b/test/prism/fixtures/whitequark/xstring_plain.txt
new file mode 100644
index 0000000000..fce255049d
--- /dev/null
+++ b/test/prism/fixtures/whitequark/xstring_plain.txt
@@ -0,0 +1 @@
+`foobar`
diff --git a/test/prism/fixtures/whitequark/zsuper.txt b/test/prism/fixtures/whitequark/zsuper.txt
new file mode 100644
index 0000000000..16f5c2d3aa
--- /dev/null
+++ b/test/prism/fixtures/whitequark/zsuper.txt
@@ -0,0 +1 @@
+super
diff --git a/test/prism/fixtures/xstring.txt b/test/prism/fixtures/xstring.txt
new file mode 100644
index 0000000000..7ec09468d8
--- /dev/null
+++ b/test/prism/fixtures/xstring.txt
@@ -0,0 +1,13 @@
+%x[foo]
+
+`foo #{bar} baz`
+
+`foo`
+
+%x{
+ foo
+}
+
+``
+
+%x{}
diff --git a/test/prism/fixtures/xstring_with_backslash.txt b/test/prism/fixtures/xstring_with_backslash.txt
new file mode 100644
index 0000000000..b51bb0f6f9
--- /dev/null
+++ b/test/prism/fixtures/xstring_with_backslash.txt
@@ -0,0 +1 @@
+`f\oo`
diff --git a/test/prism/fixtures/yield.txt b/test/prism/fixtures/yield.txt
new file mode 100644
index 0000000000..752ba27a2e
--- /dev/null
+++ b/test/prism/fixtures/yield.txt
@@ -0,0 +1,7 @@
+def foo; yield; end
+
+def foo; yield(); end
+
+def foo; yield(1); end
+
+def foo; yield(1, 2, 3); end
diff --git a/test/prism/format_errors_test.rb b/test/prism/format_errors_test.rb
new file mode 100644
index 0000000000..a1edbef2e8
--- /dev/null
+++ b/test/prism/format_errors_test.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+return if Prism::BACKEND == :FFI
+
+module Prism
+ class FormatErrorsTest < TestCase
+ def test_format_errors
+ assert_equal <<~ERROR, Debug.format_errors("<>", false)
+ > 1 | <>
+ | ^ unexpected '<', ignoring it
+ | ^ unexpected '>', ignoring it
+ ERROR
+
+ assert_equal <<~'ERROR', Debug.format_errors('"%W"\u"', false)
+ > 1 | "%W"\u"
+ | ^ unexpected backslash, ignoring it
+ | ^ unexpected local variable or method, expecting end-of-input
+ | ^ unterminated string meets end of file
+ ERROR
+ end
+ end
+end
diff --git a/test/prism/fuzzer_test.rb b/test/prism/fuzzer_test.rb
new file mode 100644
index 0000000000..511210e7ee
--- /dev/null
+++ b/test/prism/fuzzer_test.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+return if ENV["PRISM_BUILD_MINIMAL"]
+
+require_relative "test_helper"
+
+module Prism
+ # These tests are simply to exercise snippets found by the fuzzer that caused
+ # invalid memory access.
+ class FuzzerTest < TestCase
+ def self.snippet(name, source)
+ define_method(:"test_fuzzer_#{name}") { Prism.dump(source) }
+ end
+
+ snippet "incomplete global variable", "$"
+ snippet "incomplete symbol", ":"
+ snippet "incomplete escaped string", '"\\'
+ snippet "trailing comment", "1\n#\n"
+ snippet "comment followed by whitespace at end of file", "1\n#\n "
+ snippet "trailing asterisk", "a *"
+ snippet "incomplete decimal number", "0d"
+ snippet "incomplete binary number", "0b"
+ snippet "incomplete octal number", "0o"
+ snippet "incomplete hex number", "0x"
+ snippet "incomplete escaped list", "%w[\\"
+ snippet "incomplete escaped regex", "/a\\"
+ snippet "unterminated heredoc with unterminated escape at end of file", "<<A\n\\"
+ snippet "escaped octal at end of file 1", '"\\3'
+ snippet "escaped octal at end of file 2", '"\\33'
+ snippet "escaped hex at end of file 1", '"\\x'
+ snippet "escaped hex at end of file 2", '"\\x3'
+ snippet "escaped unicode at end of file 1", '"\\u{3'
+ snippet "escaped unicode at end of file 2", '"\\u{33'
+ snippet "escaped unicode at end of file 3", '"\\u{333'
+ snippet "escaped unicode at end of file 4", '"\\u{3333'
+ snippet "escaped unicode at end of file 5", '"\\u{33333'
+ snippet "escaped unicode at end of file 6", '"\\u{333333'
+ snippet "escaped unicode at end of file 7", '"\\u3'
+ snippet "escaped unicode at end of file 8", '"\\u33'
+ snippet "escaped unicode at end of file 9", '"\\u333'
+ snippet "float suffix at end of file", "1e"
+
+ snippet "statements node with multiple heredocs", <<~EOF
+ for <<A + <<B
+ A
+ B
+ EOF
+ snippet "create a binary call node with arg before receiver", <<~EOF
+ <<-A.g/{/
+ A
+ /, ""\\
+ EOF
+ snippet "regular expression with start and end out of order", <<~RUBY
+ <<-A.g//,
+ A
+ /{/, ''\\
+ RUBY
+ snippet "interpolated regular expression with start and end out of order", <<~RUBY
+ <<-A.g/{/,
+ A
+ a
+ /{/, ''\\
+ RUBY
+
+ snippet "parameter name that is zero length", "a { |b;"
+ end
+end
diff --git a/test/prism/heredoc_dedent_test.rb b/test/prism/heredoc_dedent_test.rb
new file mode 100644
index 0000000000..9fbc4d936a
--- /dev/null
+++ b/test/prism/heredoc_dedent_test.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+module Prism
+ class HeredocDedentTest < TestCase
+ filepath = File.expand_path("fixtures/tilde_heredocs.txt", __dir__)
+
+ File.read(filepath).split(/(?=\n)\n(?=<)/).each_with_index do |heredoc, index|
+ # The first example in this file has incorrect dedent calculated by
+ # TruffleRuby so we skip it.
+ next if index == 0 && RUBY_ENGINE == "truffleruby"
+
+ define_method "test_heredoc_#{index}" do
+ node = Prism.parse(heredoc).value.statements.body.first
+
+ if node.is_a?(StringNode)
+ actual = node.unescaped
+ else
+ actual = node.parts.map { |part| part.is_a?(StringNode) ? part.unescaped : "1" }.join
+ end
+
+ assert_equal(eval(heredoc), actual, "Expected heredocs to match.")
+ end
+ end
+ end
+end
diff --git a/test/prism/index_write_test.rb b/test/prism/index_write_test.rb
new file mode 100644
index 0000000000..1c6f7bce89
--- /dev/null
+++ b/test/prism/index_write_test.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+module Prism
+ class IndexWriteTest < TestCase
+ def test_keywords_3_3_0
+ assert_parse_success(<<~RUBY, "3.3.0")
+ foo[bar: 1] = 1
+ foo[bar: 1] &&= 1
+ foo[bar: 1] ||= 1
+ foo[bar: 1] += 1
+ RUBY
+
+ assert_parse_success(<<~RUBY, "3.3.0")
+ def foo(**)
+ bar[**] = 1
+ bar[**] &&= 1
+ bar[**] ||= 1
+ bar[**] += 1
+ end
+ RUBY
+ end
+
+ def test_block_3_3_0
+ assert_parse_success(<<~RUBY, "3.3.0")
+ foo[&bar] = 1
+ foo[&bar] &&= 1
+ foo[&bar] ||= 1
+ foo[&bar] += 1
+ RUBY
+
+ assert_parse_success(<<~RUBY, "3.3.0")
+ def foo(&)
+ bar[&] = 1
+ bar[&] &&= 1
+ bar[&] ||= 1
+ bar[&] += 1
+ end
+ RUBY
+ end
+
+ # def test_keywords_latest
+ # assert_parse_failure(<<~RUBY)
+ # foo[bar: 1] = 1
+ # foo[bar: 1] &&= 1
+ # foo[bar: 1] ||= 1
+ # foo[bar: 1] += 1
+ # RUBY
+
+ # assert_parse_failure(<<~RUBY)
+ # def foo(**)
+ # bar[**] = 1
+ # bar[**] &&= 1
+ # bar[**] ||= 1
+ # bar[**] += 1
+ # end
+ # RUBY
+ # end
+
+ # def test_block_latest
+ # assert_parse_failure(<<~RUBY)
+ # foo[&bar] = 1
+ # foo[&bar] &&= 1
+ # foo[&bar] ||= 1
+ # foo[&bar] += 1
+ # RUBY
+
+ # assert_parse_failure(<<~RUBY)
+ # def foo(&)
+ # bar[&] = 1
+ # bar[&] &&= 1
+ # bar[&] ||= 1
+ # bar[&] += 1
+ # end
+ # RUBY
+ # end
+
+ private
+
+ def assert_parse_success(source, version = "latest")
+ assert Prism.parse_success?(source, version: version)
+ end
+
+ def assert_parse_failure(source, version = "latest")
+ assert Prism.parse_failure?(source, version: version)
+ end
+ end
+end
diff --git a/test/prism/integer_parse_test.rb b/test/prism/integer_parse_test.rb
new file mode 100644
index 0000000000..f42e817e79
--- /dev/null
+++ b/test/prism/integer_parse_test.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+return if Prism::BACKEND == :FFI
+
+module Prism
+ class IntegerParseTest < TestCase
+ def test_integer_parse
+ assert_integer_parse(1)
+ assert_integer_parse(50)
+ assert_integer_parse(100)
+ assert_integer_parse(100, "1_0_0")
+ assert_integer_parse(8, "0_1_0")
+
+ assert_integer_parse(10, "0b1010")
+ assert_integer_parse(10, "0B1010")
+ assert_integer_parse(10, "0o12")
+ assert_integer_parse(10, "0O12")
+ assert_integer_parse(10, "012")
+ assert_integer_parse(10, "0d10")
+ assert_integer_parse(10, "0D10")
+ assert_integer_parse(10, "0xA")
+ assert_integer_parse(10, "0XA")
+
+ assert_integer_parse(2**32)
+ assert_integer_parse(2**64 + 2**32)
+ assert_integer_parse(2**128 + 2**64 + 2**32)
+
+ num = 99 ** 99
+ assert_integer_parse(num, "0b#{num.to_s(2)}")
+ assert_integer_parse(num, "0o#{num.to_s(8)}")
+ assert_integer_parse(num, "0d#{num.to_s(10)}")
+ assert_integer_parse(num, "0x#{num.to_s(16)}")
+ end
+
+ private
+
+ def assert_integer_parse(expected, source = expected.to_s)
+ integer, string = Debug.integer_parse(source)
+ assert_equal expected, integer
+ assert_equal expected.to_s, string
+ end
+ end
+end
diff --git a/test/prism/library_symbols_test.rb b/test/prism/library_symbols_test.rb
new file mode 100644
index 0000000000..b10a367c18
--- /dev/null
+++ b/test/prism/library_symbols_test.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+return if RUBY_PLATFORM !~ /linux/
+
+# TODO: determine why these symbols are incorrect on ppc64le
+return if RUBY_PLATFORM =~ /powerpc64le/
+
+module Prism
+ #
+ # examine a prism dll or static archive for expected external symbols.
+ # these tests only work on a linux system right now.
+ #
+ class LibrarySymbolsTest < TestCase
+ def setup
+ super
+
+ @libprism_a = File.expand_path("../../build/libprism.a", __dir__)
+ @libprism_so = File.expand_path("../../build/libprism.so", __dir__)
+ @prism_so = File.expand_path("../../lib/prism/prism.so", __dir__)
+ end
+
+ # objdump runner and helpers
+ def objdump(path)
+ assert_path_exist(path)
+ %x(objdump --section=.text --syms #{path}).split("\n")
+ end
+
+ def global_objdump_symbols(path)
+ objdump(path).select { |line| line[17] == "g" }
+ end
+
+ def hidden_global_objdump_symbols(path)
+ global_objdump_symbols(path).select { |line| line =~ / \.hidden / }
+ end
+
+ def visible_global_objdump_symbols(path)
+ global_objdump_symbols(path).reject { |line| line =~ / \.hidden / }
+ end
+
+ # nm runner and helpers
+ def nm(path)
+ assert_path_exist(path)
+ %x(nm #{path}).split("\n")
+ end
+
+ def global_nm_symbols(path)
+ nm(path).select { |line| line[17] == "T" }
+ end
+
+ def local_nm_symbols(path)
+ nm(path).select { |line| line[17] == "t" }
+ end
+
+ # dig the symbol name out of each line. works for both `objdump` and `nm` output.
+ def names(symbol_lines)
+ symbol_lines.map { |line| line.split(/\s+/).last }
+ end
+
+ #
+ # static archive - libprism.a
+ #
+ def test_libprism_a_contains_nothing_globally_visible
+ omit("libprism.a is not built") unless File.exist?(@libprism_a)
+
+ assert_empty(names(visible_global_objdump_symbols(@libprism_a)))
+ end
+
+ def test_libprism_a_contains_hidden_pm_symbols
+ omit("libprism.a is not built") unless File.exist?(@libprism_a)
+
+ names(hidden_global_objdump_symbols(@libprism_a)).tap do |symbols|
+ assert_includes(symbols, "pm_parse")
+ assert_includes(symbols, "pm_version")
+ end
+ end
+
+ #
+ # shared object - libprism.so
+ #
+ def test_libprism_so_exports_only_the_necessary_functions
+ omit("libprism.so is not built") unless File.exist?(@libprism_so)
+
+ names(global_nm_symbols(@libprism_so)).tap do |symbols|
+ assert_includes(symbols, "pm_parse")
+ assert_includes(symbols, "pm_version")
+ end
+ names(local_nm_symbols(@libprism_so)).tap do |symbols|
+ assert_includes(symbols, "pm_encoding_utf_8_isupper_char")
+ end
+ # TODO: someone who uses this library needs to finish this test
+ end
+
+ #
+ # shared object - prism.so
+ #
+ def test_prism_so_exports_only_the_C_extension_init_function
+ omit("prism.so is not built") unless File.exist?(@prism_so)
+
+ names(global_nm_symbols(@prism_so)).tap do |symbols|
+ assert_equal(["Init_prism"], symbols)
+ end
+ end
+ end
+end
diff --git a/test/prism/locals_test.rb b/test/prism/locals_test.rb
new file mode 100644
index 0000000000..0eb73f1b9c
--- /dev/null
+++ b/test/prism/locals_test.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+# This test is going to use the RubyVM::InstructionSequence class to compile
+# local tables and compare against them to ensure we have the same locals in the
+# same order. This is important to guarantee that we compile indices correctly
+# on CRuby (in terms of compatibility).
+#
+# There have also been changes made in other versions of Ruby, so we only want
+# to test on the most recent versions.
+return if !defined?(RubyVM::InstructionSequence) || RUBY_VERSION < "3.4.0"
+
+# Omit tests if running on a 32-bit machine because there is a bug with how
+# Ruby is handling large ISeqs on 32-bit machines
+return if RUBY_PLATFORM =~ /i686/
+
+require_relative "test_helper"
+
+module Prism
+ class LocalsTest < TestCase
+ base = File.join(__dir__, "fixtures")
+ Dir["**/*.txt", base: base].each do |relative|
+ # Skip this fixture because it has a different number of locals because
+ # CRuby is eliminating dead code.
+ next if relative == "whitequark/ruby_bug_10653.txt"
+
+ filepath = File.join(base, relative)
+ define_method("test_#{relative}") { assert_locals(filepath) }
+ end
+
+ def setup
+ @previous_default_external = Encoding.default_external
+ ignore_warnings { Encoding.default_external = Encoding::UTF_8 }
+ end
+
+ def teardown
+ ignore_warnings { Encoding.default_external = @previous_default_external }
+ end
+
+ private
+
+ def assert_locals(filepath)
+ source = File.read(filepath)
+
+ expected = Debug.cruby_locals(source)
+ actual = Debug.prism_locals(source)
+
+ assert_equal(expected, actual)
+ end
+
+ def ignore_warnings
+ previous_verbosity = $VERBOSE
+ $VERBOSE = nil
+ yield
+ ensure
+ $VERBOSE = previous_verbosity
+ end
+ end
+end
diff --git a/test/prism/location_test.rb b/test/prism/location_test.rb
new file mode 100644
index 0000000000..81417fbcb3
--- /dev/null
+++ b/test/prism/location_test.rb
@@ -0,0 +1,947 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+module Prism
+ class LocationTest < TestCase
+ def test_AliasGlobalVariableNode
+ assert_location(AliasGlobalVariableNode, "alias $foo $bar")
+ end
+
+ def test_AliasMethodNode
+ assert_location(AliasMethodNode, "alias foo bar")
+ end
+
+ def test_AlternationPatternNode
+ assert_location(AlternationPatternNode, "foo => bar | baz", 7...16, &:pattern)
+ end
+
+ def test_AndNode
+ assert_location(AndNode, "foo and bar")
+ assert_location(AndNode, "foo && bar")
+ end
+
+ def test_ArgumentsNode
+ assert_location(ArgumentsNode, "foo(bar, baz, qux)", 4...17, &:arguments)
+ end
+
+ def test_ArrayNode
+ assert_location(ArrayNode, "[foo, bar, baz]")
+ assert_location(ArrayNode, "%i[foo bar baz]")
+ assert_location(ArrayNode, "%I[foo bar baz]")
+ assert_location(ArrayNode, "%w[foo bar baz]")
+ assert_location(ArrayNode, "%W[foo bar baz]")
+ end
+
+ def test_ArrayPatternNode
+ assert_location(ArrayPatternNode, "foo => bar, baz", 7...15, &:pattern)
+ assert_location(ArrayPatternNode, "foo => [bar, baz]", 7...17, &:pattern)
+ assert_location(ArrayPatternNode, "foo => *bar", 7...11, &:pattern)
+ assert_location(ArrayPatternNode, "foo => []", 7...9, &:pattern)
+ assert_location(ArrayPatternNode, "foo => Foo[]", 7...12, &:pattern)
+ assert_location(ArrayPatternNode, "foo => Foo[bar]", 7...15, &:pattern)
+ end
+
+ def test_AssocNode
+ assert_location(AssocNode, "{ '': 1 }", 2...7) { |node| node.elements.first }
+ assert_location(AssocNode, "{ foo: :bar }", 2...11) { |node| node.elements.first }
+ assert_location(AssocNode, "{ :foo => :bar }", 2...14) { |node| node.elements.first }
+ assert_location(AssocNode, "foo(bar: :baz)", 4...13) { |node| node.arguments.arguments.first.elements.first }
+ end
+
+ def test_AssocSplatNode
+ assert_location(AssocSplatNode, "{ **foo }", 2...7) { |node| node.elements.first }
+ assert_location(AssocSplatNode, "foo(**bar)", 4...9) { |node| node.arguments.arguments.first.elements.first }
+ end
+
+ def test_BackReferenceReadNode
+ assert_location(BackReferenceReadNode, "$+")
+ end
+
+ def test_BeginNode
+ assert_location(BeginNode, "begin foo end")
+ assert_location(BeginNode, "begin foo rescue bar end")
+ assert_location(BeginNode, "begin foo; rescue bar\nelse baz end")
+ assert_location(BeginNode, "begin foo; rescue bar\nelse baz\nensure qux end")
+
+ assert_location(BeginNode, "class Foo\nrescue then end", 0..25, &:body)
+ assert_location(BeginNode, "module Foo\nrescue then end", 0..26, &:body)
+ end
+
+ def test_BlockArgumentNode
+ assert_location(BlockArgumentNode, "foo(&bar)", 4...8, &:block)
+ end
+
+ def test_BlockLocalVariableNode
+ assert_location(BlockLocalVariableNode, "foo { |;bar| }", 8...11) do |node|
+ node.block.parameters.locals.first
+ end
+ end
+
+ def test_BlockNode
+ assert_location(BlockNode, "foo {}", 4...6, &:block)
+ assert_location(BlockNode, "foo do end", 4...10, &:block)
+ end
+
+ def test_BlockParameterNode
+ assert_location(BlockParameterNode, "def foo(&bar) end", 8...12) { |node| node.parameters.block }
+ end
+
+ def test_BlockParametersNode
+ assert_location(BlockParametersNode, "foo { || }", 6...8) { |node| node.block.parameters }
+ assert_location(BlockParametersNode, "foo { |bar| baz }", 6...11) { |node| node.block.parameters }
+ assert_location(BlockParametersNode, "foo { |bar; baz| baz }", 6...16) { |node| node.block.parameters }
+
+ assert_location(BlockParametersNode, "-> () {}", 3...5, &:parameters)
+ assert_location(BlockParametersNode, "-> (bar) { baz }", 3...8, &:parameters)
+ assert_location(BlockParametersNode, "-> (bar; baz) { baz }", 3...13, &:parameters)
+ end
+
+ def test_BreakNode
+ assert_location(BreakNode, "tap { break }", 6...11) { |node| node.block.body.body.first }
+ assert_location(BreakNode, "tap { break foo }", 6...15) { |node| node.block.body.body.first }
+ assert_location(BreakNode, "tap { break foo, bar }", 6...20) { |node| node.block.body.body.first }
+ assert_location(BreakNode, "tap { break(foo) }", 6...16) { |node| node.block.body.body.first }
+ end
+
+ def test_CallNode
+ assert_location(CallNode, "foo")
+ assert_location(CallNode, "foo?")
+ assert_location(CallNode, "foo!")
+
+ assert_location(CallNode, "foo()")
+ assert_location(CallNode, "foo?()")
+ assert_location(CallNode, "foo!()")
+
+ assert_location(CallNode, "foo(bar)")
+ assert_location(CallNode, "foo?(bar)")
+ assert_location(CallNode, "foo!(bar)")
+
+ assert_location(CallNode, "!foo")
+ assert_location(CallNode, "~foo")
+ assert_location(CallNode, "+foo")
+ assert_location(CallNode, "-foo")
+
+ assert_location(CallNode, "not foo")
+ assert_location(CallNode, "not(foo)")
+ assert_location(CallNode, "not()")
+
+ assert_location(CallNode, "foo + bar")
+ assert_location(CallNode, "foo -\n bar")
+
+ assert_location(CallNode, "Foo()")
+ assert_location(CallNode, "Foo(bar)")
+
+ assert_location(CallNode, "Foo::Bar()")
+ assert_location(CallNode, "Foo::Bar(baz)")
+
+ assert_location(CallNode, "Foo::bar")
+ assert_location(CallNode, "Foo::bar()")
+ assert_location(CallNode, "Foo::bar(baz)")
+
+ assert_location(CallNode, "Foo.bar")
+ assert_location(CallNode, "Foo.bar()")
+ assert_location(CallNode, "Foo.bar(baz)")
+
+ assert_location(CallNode, "foo::bar")
+ assert_location(CallNode, "foo::bar()")
+ assert_location(CallNode, "foo::bar(baz)")
+
+ assert_location(CallNode, "foo.bar")
+ assert_location(CallNode, "foo.bar()")
+ assert_location(CallNode, "foo.bar(baz)")
+
+ assert_location(CallNode, "foo&.bar")
+ assert_location(CallNode, "foo&.bar()")
+ assert_location(CallNode, "foo&.bar(baz)")
+
+ assert_location(CallNode, "foo[]")
+ assert_location(CallNode, "foo[bar]")
+ assert_location(CallNode, "foo[bar, baz]")
+
+ assert_location(CallNode, "foo[] = 1")
+ assert_location(CallNode, "foo[bar] = 1")
+ assert_location(CallNode, "foo[bar, baz] = 1")
+
+ assert_location(CallNode, "foo.()")
+ assert_location(CallNode, "foo.(bar)")
+
+ assert_location(CallNode, "foo&.()")
+ assert_location(CallNode, "foo&.(bar)")
+
+ assert_location(CallNode, "foo::()")
+ assert_location(CallNode, "foo::(bar)")
+ assert_location(CallNode, "foo::(bar, baz)")
+
+ assert_location(CallNode, "foo bar baz")
+ assert_location(CallNode, "foo bar('baz')")
+
+ assert_location(CallNode, "-> { it }", 5...7, version: "3.3.0") do |node|
+ node.body.body.first
+ end
+
+ assert_location(LocalVariableReadNode, "-> { it }", 5...7, version: "3.4.0") do |node|
+ node.body.body.first
+ end
+ end
+
+ def test_CallAndWriteNode
+ assert_location(CallAndWriteNode, "foo.foo &&= bar")
+ end
+
+ def test_CallOperatorWriteNode
+ assert_location(CallOperatorWriteNode, "foo.foo += bar")
+ end
+
+ def test_CallOrWriteNode
+ assert_location(CallOrWriteNode, "foo.foo ||= bar")
+ end
+
+ def test_CallTargetNode
+ assert_location(CallTargetNode, "foo.bar, = baz", 0...7) do |node|
+ node.lefts.first
+ end
+ end
+
+ def test_CapturePatternNode
+ assert_location(CapturePatternNode, "case foo; in bar => baz; end", 13...23) do |node|
+ node.conditions.first.pattern
+ end
+ end
+
+ def test_CaseNode
+ assert_location(CaseNode, "case foo; when bar; end")
+ assert_location(CaseNode, "case foo; when bar; else; end")
+ assert_location(CaseNode, "case foo; when bar; when baz; end")
+ assert_location(CaseNode, "case foo; when bar; when baz; else; end")
+ end
+
+ def test_CaseMatchNode
+ assert_location(CaseMatchNode, "case foo; in bar; end")
+ assert_location(CaseMatchNode, "case foo; in bar; else; end")
+ assert_location(CaseMatchNode, "case foo; in bar; in baz; end")
+ assert_location(CaseMatchNode, "case foo; in bar; in baz; else; end")
+ end
+
+ def test_ClassNode
+ assert_location(ClassNode, "class Foo end")
+ assert_location(ClassNode, "class Foo < Bar; end")
+ end
+
+ def test_ClassVariableAndWriteNode
+ assert_location(ClassVariableAndWriteNode, "@@foo &&= bar")
+ end
+
+ def test_ClassVariableOperatorWriteNode
+ assert_location(ClassVariableOperatorWriteNode, "@@foo += bar")
+ end
+
+ def test_ClassVariableOrWriteNode
+ assert_location(ClassVariableOrWriteNode, "@@foo ||= bar")
+ end
+
+ def test_ClassVariableReadNode
+ assert_location(ClassVariableReadNode, "@@foo")
+ end
+
+ def test_ClassVariableTargetNode
+ assert_location(ClassVariableTargetNode, "@@foo, @@bar = baz", 0...5) do |node|
+ node.lefts.first
+ end
+ end
+
+ def test_ClassVariableWriteNode
+ assert_location(ClassVariableWriteNode, "@@foo = bar")
+ end
+
+ def test_ConstantPathAndWriteNode
+ assert_location(ConstantPathAndWriteNode, "Parent::Child &&= bar")
+ end
+
+ def test_ConstantPathNode
+ assert_location(ConstantPathNode, "Foo::Bar")
+ assert_location(ConstantPathNode, "::Foo")
+ assert_location(ConstantPathNode, "::Foo::Bar")
+ end
+
+ def test_ConstantPathOperatorWriteNode
+ assert_location(ConstantPathOperatorWriteNode, "Parent::Child += bar")
+ end
+
+ def test_ConstantPathOrWriteNode
+ assert_location(ConstantPathOrWriteNode, "Parent::Child ||= bar")
+ end
+
+ def test_ConstantPathTargetNode
+ assert_location(ConstantPathTargetNode, "::Foo, ::Bar = baz", 0...5) do |node|
+ node.lefts.first
+ end
+ end
+
+ def test_ConstantPathWriteNode
+ assert_location(ConstantPathWriteNode, "Foo::Bar = baz")
+ assert_location(ConstantPathWriteNode, "::Foo = bar")
+ assert_location(ConstantPathWriteNode, "::Foo::Bar = baz")
+ end
+
+ def test_ConstantAndWriteNode
+ assert_location(ConstantAndWriteNode, "Foo &&= bar")
+ end
+
+ def test_ConstantOperatorWriteNode
+ assert_location(ConstantOperatorWriteNode, "Foo += bar")
+ end
+
+ def test_ConstantOrWriteNode
+ assert_location(ConstantOrWriteNode, "Foo ||= bar")
+ end
+
+ def test_ConstantReadNode
+ assert_location(ConstantReadNode, "Foo")
+ assert_location(ConstantReadNode, "Foo::Bar", 5...8, &:child)
+ end
+
+ def test_ConstantTargetNode
+ assert_location(ConstantTargetNode, "Foo, Bar = baz", 0...3) do |node|
+ node.lefts.first
+ end
+ end
+
+ def test_ConstantWriteNode
+ assert_location(ConstantWriteNode, "Foo = bar")
+ end
+
+ def test_DefNode
+ assert_location(DefNode, "def foo; bar; end")
+ assert_location(DefNode, "def foo = bar")
+ assert_location(DefNode, "def foo.bar; baz; end")
+ assert_location(DefNode, "def foo.bar = baz")
+ end
+
+ def test_DefinedNode
+ assert_location(DefinedNode, "defined? foo")
+ assert_location(DefinedNode, "defined?(foo)")
+ end
+
+ def test_ElseNode
+ assert_location(ElseNode, "if foo; bar; else; baz; end", 13...27, &:consequent)
+ assert_location(ElseNode, "foo ? bar : baz", 10...15, &:consequent)
+ end
+
+ def test_EmbeddedStatementsNode
+ assert_location(EmbeddedStatementsNode, '"foo #{bar} baz"', 5...11) { |node| node.parts[1] }
+ end
+
+ def test_EmbeddedVariableNode
+ assert_location(EmbeddedVariableNode, '"foo #@@bar baz"', 5...11) { |node| node.parts[1] }
+ end
+
+ def test_EnsureNode
+ assert_location(EnsureNode, "begin; foo; ensure; bar; end", 12...28, &:ensure_clause)
+ end
+
+ def test_FalseNode
+ assert_location(FalseNode, "false")
+ end
+
+ def test_FindPatternNode
+ assert_location(FindPatternNode, "case foo; in *, bar, *; end", 13...22) do |node|
+ node.conditions.first.pattern
+ end
+ end
+
+ def test_FlipFlopNode
+ assert_location(FlipFlopNode, "if foo..bar; end", 3..11, &:predicate)
+ end
+
+ def test_FloatNode
+ assert_location(FloatNode, "0.0")
+ assert_location(FloatNode, "1.0")
+ assert_location(FloatNode, "1.0e10")
+ assert_location(FloatNode, "1.0e-10")
+ end
+
+ def test_ForNode
+ assert_location(ForNode, "for foo in bar; end")
+ assert_location(ForNode, "for foo, bar in baz do end")
+ end
+
+ def test_ForwardingArgumentsNode
+ assert_location(ForwardingArgumentsNode, "def foo(...); bar(...); end", 18...21) do |node|
+ node.body.body.first.arguments.arguments.first
+ end
+ end
+
+ def test_ForwardingParameterNode
+ assert_location(ForwardingParameterNode, "def foo(...); end", 8...11) do |node|
+ node.parameters.keyword_rest
+ end
+ end
+
+ def test_ForwardingSuperNode
+ assert_location(ForwardingSuperNode, "super")
+ assert_location(ForwardingSuperNode, "super {}")
+ end
+
+ def test_GlobalVariableAndWriteNode
+ assert_location(GlobalVariableAndWriteNode, "$foo &&= bar")
+ end
+
+ def test_GlobalVariableOperatorWriteNode
+ assert_location(GlobalVariableOperatorWriteNode, "$foo += bar")
+ end
+
+ def test_GlobalVariableOrWriteNode
+ assert_location(GlobalVariableOrWriteNode, "$foo ||= bar")
+ end
+
+ def test_GlobalVariableReadNode
+ assert_location(GlobalVariableReadNode, "$foo")
+ end
+
+ def test_GlobalVariableTargetNode
+ assert_location(GlobalVariableTargetNode, "$foo, $bar = baz", 0...4) do |node|
+ node.lefts.first
+ end
+ end
+
+ def test_GlobalVariableWriteNode
+ assert_location(GlobalVariableWriteNode, "$foo = bar")
+ end
+
+ def test_HashNode
+ assert_location(HashNode, "{ foo: 2 }")
+ assert_location(HashNode, "{ \nfoo: 2, \nbar: 3 \n}")
+ end
+
+ def test_HashPatternNode
+ assert_location(HashPatternNode, "case foo; in bar: baz; end", 13...21) do |node|
+ node.conditions.first.pattern
+ end
+ end
+
+ def test_IfNode
+ assert_location(IfNode, "if type in 1;elsif type in B;end")
+ end
+
+ def test_ImaginaryNode
+ assert_location(ImaginaryNode, "1i")
+ assert_location(ImaginaryNode, "1ri")
+ end
+
+ def test_ImplicitNode
+ assert_location(ImplicitNode, "{ foo: }", 2...6) do |node|
+ node.elements.first.value
+ end
+
+ assert_location(ImplicitNode, "{ Foo: }", 2..6) do |node|
+ node.elements.first.value
+ end
+
+ assert_location(ImplicitNode, "foo = 1; { foo: }", 11..15) do |node|
+ node.elements.first.value
+ end
+ end
+
+ def test_ImplicitRestNode
+ assert_location(ImplicitRestNode, "foo, = bar", 3..4, &:rest)
+
+ assert_location(ImplicitRestNode, "for foo, in bar do end", 7..8) do |node|
+ node.index.rest
+ end
+
+ assert_location(ImplicitRestNode, "foo { |bar,| }", 10..11) do |node|
+ node.block.parameters.parameters.rest
+ end
+
+ assert_location(ImplicitRestNode, "foo in [bar,]", 11..12) do |node|
+ node.pattern.rest
+ end
+ end
+
+ def test_InNode
+ assert_location(InNode, "case foo; in bar; end", 10...16) do |node|
+ node.conditions.first
+ end
+ end
+
+ def test_IndexAndWriteNode
+ assert_location(IndexAndWriteNode, "foo[foo] &&= bar")
+ end
+
+ def test_IndexOperatorWriteNode
+ assert_location(IndexOperatorWriteNode, "foo[foo] += bar")
+ end
+
+ def test_IndexOrWriteNode
+ assert_location(IndexOrWriteNode, "foo[foo] ||= bar")
+ end
+
+ def test_IndexTargetNode
+ assert_location(IndexTargetNode, "foo[bar, &baz], = qux", 0...14) do |node|
+ node.lefts.first
+ end
+ end
+
+ def test_InstanceVariableAndWriteNode
+ assert_location(InstanceVariableAndWriteNode, "@foo &&= bar")
+ end
+
+ def test_InstanceVariableOperatorWriteNode
+ assert_location(InstanceVariableOperatorWriteNode, "@foo += bar")
+ end
+
+ def test_InstanceVariableOrWriteNode
+ assert_location(InstanceVariableOrWriteNode, "@foo ||= bar")
+ end
+
+ def test_InstanceVariableReadNode
+ assert_location(InstanceVariableReadNode, "@foo")
+ end
+
+ def test_InstanceVariableTargetNode
+ assert_location(InstanceVariableTargetNode, "@foo, @bar = baz", 0...4) do |node|
+ node.lefts.first
+ end
+ end
+
+ def test_InstanceVariableWriteNode
+ assert_location(InstanceVariableWriteNode, "@foo = bar")
+ end
+
+ def test_IntegerNode
+ assert_location(IntegerNode, "0")
+ assert_location(IntegerNode, "1")
+ assert_location(IntegerNode, "1_000")
+ assert_location(IntegerNode, "0x1")
+ assert_location(IntegerNode, "0x1_000")
+ assert_location(IntegerNode, "0b1")
+ assert_location(IntegerNode, "0b1_000")
+ assert_location(IntegerNode, "0o1")
+ assert_location(IntegerNode, "0o1_000")
+ end
+
+ def test_InterpolatedMatchLastLineNode
+ assert_location(InterpolatedMatchLastLineNode, "if /foo \#{bar}/ then end", 3...15, &:predicate)
+ end
+
+ def test_InterpolatedRegularExpressionNode
+ assert_location(InterpolatedRegularExpressionNode, "/\#{foo}/")
+ assert_location(InterpolatedRegularExpressionNode, "/\#{foo}/io")
+ end
+
+ def test_InterpolatedStringNode
+ assert_location(InterpolatedStringNode, "\"foo \#@bar baz\"")
+ assert_location(InterpolatedStringNode, "<<~A\nhello \#{1} world\nA", 0...4)
+ assert_location(InterpolatedStringNode, '"foo" "bar"')
+ end
+
+ def test_InterpolatedSymbolNode
+ assert_location(InterpolatedSymbolNode, ':"#{foo}bar"')
+ end
+
+ def test_InterpolatedXStringNode
+ assert_location(InterpolatedXStringNode, '`foo #{bar} baz`')
+ end
+
+ def test_ItParametersNode
+ assert_location(ItParametersNode, "-> { it }", &:parameters)
+ end
+
+ def test_KeywordHashNode
+ assert_location(KeywordHashNode, "foo(a, b: 1)", 7...11) { |node| node.arguments.arguments[1] }
+ end
+
+ def test_KeywordRestParameterNode
+ assert_location(KeywordRestParameterNode, "def foo(**); end", 8...10) do |node|
+ node.parameters.keyword_rest
+ end
+
+ assert_location(KeywordRestParameterNode, "def foo(**bar); end", 8...13) do |node|
+ node.parameters.keyword_rest
+ end
+ end
+
+ def test_LambdaNode
+ assert_location(LambdaNode, "-> { foo }")
+ assert_location(LambdaNode, "-> do foo end")
+ end
+
+ def test_LocalVariableAndWriteNode
+ assert_location(LocalVariableAndWriteNode, "foo &&= bar")
+ assert_location(LocalVariableAndWriteNode, "foo = 1; foo &&= bar", 9...20)
+ end
+
+ def test_LocalVariableOperatorWriteNode
+ assert_location(LocalVariableOperatorWriteNode, "foo += bar")
+ assert_location(LocalVariableOperatorWriteNode, "foo = 1; foo += bar", 9...19)
+ end
+
+ def test_LocalVariableOrWriteNode
+ assert_location(LocalVariableOrWriteNode, "foo ||= bar")
+ assert_location(LocalVariableOrWriteNode, "foo = 1; foo ||= bar", 9...20)
+ end
+
+ def test_LocalVariableReadNode
+ assert_location(LocalVariableReadNode, "foo = 1; foo", 9...12)
+ assert_location(LocalVariableReadNode, "-> { it }", 5...7) do |node|
+ node.body.body.first
+ end
+ assert_location(LocalVariableReadNode, "foo { it }", 6...8) do |node|
+ node.block.body.body.first
+ end
+ end
+
+ def test_LocalVariableTargetNode
+ assert_location(LocalVariableTargetNode, "foo, bar = baz", 0...3) do |node|
+ node.lefts.first
+ end
+ end
+
+ def test_LocalVariableWriteNode
+ assert_location(LocalVariableWriteNode, "foo = bar")
+ end
+
+ def test_MatchLastLineNode
+ assert_location(MatchLastLineNode, "if /foo/ then end", 3...8, &:predicate)
+ end
+
+ def test_MatchPredicateNode
+ assert_location(MatchPredicateNode, "foo in bar")
+ end
+
+ def test_MatchRequiredNode
+ assert_location(MatchRequiredNode, "foo => bar")
+ end
+
+ def test_MatchWriteNode
+ assert_location(MatchWriteNode, "/(?<foo>)/ =~ foo")
+ end
+
+ def test_ModuleNode
+ assert_location(ModuleNode, "module Foo end")
+ end
+
+ def test_MultiTargetNode
+ assert_location(MultiTargetNode, "for foo, bar in baz do end", 4...12, &:index)
+ assert_location(MultiTargetNode, "foo, (bar, baz) = qux", 5...15) { |node| node.lefts.last }
+ assert_location(MultiTargetNode, "def foo((bar)); end", 8...13) do |node|
+ node.parameters.requireds.first
+ end
+ end
+
+ def test_MultiWriteNode
+ assert_location(MultiWriteNode, "foo, bar = baz")
+ assert_location(MultiWriteNode, "(foo, bar) = baz")
+ assert_location(MultiWriteNode, "((foo, bar)) = baz")
+ end
+
+ def test_NextNode
+ assert_location(NextNode, "tap { next }", 6...10) { |node| node.block.body.body.first }
+ assert_location(NextNode, "tap { next foo }", 6...14) { |node| node.block.body.body.first }
+ assert_location(NextNode, "tap { next foo, bar }", 6...19) { |node| node.block.body.body.first }
+ assert_location(NextNode, "tap { next(foo) }", 6...15) { |node| node.block.body.body.first }
+ end
+
+ def test_NilNode
+ assert_location(NilNode, "nil")
+ end
+
+ def test_NoKeywordsParameterNode
+ assert_location(NoKeywordsParameterNode, "def foo(**nil); end", 8...13) { |node| node.parameters.keyword_rest }
+ end
+
+ def test_NumberedParametersNode
+ assert_location(NumberedParametersNode, "-> { _1 }", &:parameters)
+ assert_location(NumberedParametersNode, "foo { _1 }", 4...10) { |node| node.block.parameters }
+ end
+
+ def test_NumberedReferenceReadNode
+ assert_location(NumberedReferenceReadNode, "$1")
+ end
+
+ def test_OptionalKeywordParameterNode
+ assert_location(OptionalKeywordParameterNode, "def foo(bar: nil); end", 8...16) do |node|
+ node.parameters.keywords.first
+ end
+ end
+
+ def test_OptionalParameterNode
+ assert_location(OptionalParameterNode, "def foo(bar = nil); end", 8...17) do |node|
+ node.parameters.optionals.first
+ end
+ end
+
+ def test_OrNode
+ assert_location(OrNode, "foo || bar")
+ assert_location(OrNode, "foo or bar")
+ end
+
+ def test_ParametersNode
+ assert_location(ParametersNode, "def foo(bar, baz); end", 8...16, &:parameters)
+ end
+
+ def test_ParenthesesNode
+ assert_location(ParenthesesNode, "()")
+ assert_location(ParenthesesNode, "(foo)")
+ assert_location(ParenthesesNode, "foo (bar), baz", 4...9) { |node| node.arguments.arguments.first }
+ assert_location(ParenthesesNode, "def (foo).bar; end", 4...9, &:receiver)
+ end
+
+ def test_PinnedExpressionNode
+ assert_location(PinnedExpressionNode, "foo in ^(bar)", 7...13, &:pattern)
+ end
+
+ def test_PinnedVariableNode
+ assert_location(PinnedVariableNode, "bar = 1; foo in ^bar", 16...20, &:pattern)
+ assert_location(PinnedVariableNode, "proc { 1 in ^it }.call(1)", 12...15) do |node|
+ node.receiver.block.body.body.first.pattern
+ end
+ end
+
+ def test_PostExecutionNode
+ assert_location(PostExecutionNode, "END {}")
+ assert_location(PostExecutionNode, "END { foo }")
+ end
+
+ def test_PreExecutionNode
+ assert_location(PreExecutionNode, "BEGIN {}")
+ assert_location(PreExecutionNode, "BEGIN { foo }")
+ end
+
+ def test_RangeNode
+ assert_location(RangeNode, "1..2")
+ assert_location(RangeNode, "1...2")
+
+ assert_location(RangeNode, "..2")
+ assert_location(RangeNode, "...2")
+
+ assert_location(RangeNode, "1..")
+ assert_location(RangeNode, "1...")
+ end
+
+ def test_RationalNode
+ assert_location(RationalNode, "1r")
+ assert_location(RationalNode, "1.0r")
+ end
+
+ def test_RedoNode
+ assert_location(RedoNode, "tap { redo }", 6...10) { |node| node.block.body.body.first }
+ end
+
+ def test_RegularExpressionNode
+ assert_location(RegularExpressionNode, "/foo/")
+ assert_location(RegularExpressionNode, "/foo/io")
+ end
+
+ def test_RequiredKeywordParameterNode
+ assert_location(RequiredKeywordParameterNode, "def foo(bar:); end", 8...12) do |node|
+ node.parameters.keywords.first
+ end
+ end
+
+ def test_RequiredParameterNode
+ assert_location(RequiredParameterNode, "def foo(bar); end", 8...11) do |node|
+ node.parameters.requireds.first
+ end
+ end
+
+ def test_RescueNode
+ code = <<~RUBY
+ begin
+ body
+ rescue TypeError
+ rescue ArgumentError
+ end
+ RUBY
+ assert_location(RescueNode, code, 13...50) { |node| node.rescue_clause }
+ assert_location(RescueNode, code, 30...50) { |node| node.rescue_clause.consequent }
+ end
+
+ def test_RescueModifierNode
+ assert_location(RescueModifierNode, "foo rescue bar")
+ end
+
+ def test_RestParameterNode
+ assert_location(RestParameterNode, "def foo(*bar); end", 8...12) do |node|
+ node.parameters.rest
+ end
+ end
+
+ def test_RetryNode
+ assert_location(RetryNode, "begin; rescue; retry; end", 15...20) { |node| node.rescue_clause.statements.body.first }
+ end
+
+ def test_ReturnNode
+ assert_location(ReturnNode, "return")
+ assert_location(ReturnNode, "return foo")
+ assert_location(ReturnNode, "return foo, bar")
+ assert_location(ReturnNode, "return(foo)")
+ end
+
+ def test_SelfNode
+ assert_location(SelfNode, "self")
+ end
+
+ def test_ShareableConstantNode
+ source = <<~RUBY
+ # shareable_constant_value: literal
+ C = { foo: 1 }
+ RUBY
+
+ assert_location(ShareableConstantNode, source, 36...50)
+ end
+
+ def test_SingletonClassNode
+ assert_location(SingletonClassNode, "class << self; end")
+ end
+
+ def test_SourceEncodingNode
+ assert_location(SourceEncodingNode, "__ENCODING__")
+ end
+
+ def test_SourceFileNode
+ assert_location(SourceFileNode, "__FILE__")
+ end
+
+ def test_SourceLineNode
+ assert_location(SourceLineNode, "__LINE__")
+ end
+
+ def test_SplatNode
+ assert_location(SplatNode, "*foo = bar", 0...4, &:rest)
+ end
+
+ def test_StatementsNode
+ assert_location(StatementsNode, "foo { 1 }", 6...7) { |node| node.block.body }
+
+ assert_location(StatementsNode, "(1)", 1...2, &:body)
+
+ assert_location(StatementsNode, "def foo; 1; end", 9...10, &:body)
+ assert_location(StatementsNode, "def foo = 1", 10...11, &:body)
+ assert_location(StatementsNode, "def foo; 1\n2; end", 9...12, &:body)
+
+ assert_location(StatementsNode, "if foo; bar; end", 8...11, &:statements)
+ assert_location(StatementsNode, "foo if bar", 0...3, &:statements)
+
+ assert_location(StatementsNode, "if foo; foo; elsif bar; bar; end", 24...27) { |node| node.consequent.statements }
+ assert_location(StatementsNode, "if foo; foo; else; bar; end", 19...22) { |node| node.consequent.statements }
+
+ assert_location(StatementsNode, "unless foo; bar; end", 12...15, &:statements)
+ assert_location(StatementsNode, "foo unless bar", 0...3, &:statements)
+
+ assert_location(StatementsNode, "case; when foo; bar; end", 16...19) { |node| node.conditions.first.statements }
+
+ assert_location(StatementsNode, "while foo; bar; end", 11...14, &:statements)
+ assert_location(StatementsNode, "foo while bar", 0...3, &:statements)
+
+ assert_location(StatementsNode, "until foo; bar; end", 11...14, &:statements)
+ assert_location(StatementsNode, "foo until bar", 0...3, &:statements)
+
+ assert_location(StatementsNode, "for foo in bar; baz; end", 16...19, &:statements)
+
+ assert_location(StatementsNode, "begin; foo; end", 7...10, &:statements)
+ assert_location(StatementsNode, "begin; rescue; foo; end", 15...18) { |node| node.rescue_clause.statements }
+ assert_location(StatementsNode, "begin; ensure; foo; end", 15...18) { |node| node.ensure_clause.statements }
+ assert_location(StatementsNode, "begin; rescue; else; foo; end", 21...24) { |node| node.else_clause.statements }
+
+ assert_location(StatementsNode, "class Foo; foo; end", 11...14, &:body)
+ assert_location(StatementsNode, "module Foo; foo; end", 12...15, &:body)
+ assert_location(StatementsNode, "class << self; foo; end", 15...18, &:body)
+
+ assert_location(StatementsNode, "-> { foo }", 5...8, &:body)
+ assert_location(StatementsNode, "BEGIN { foo }", 8...11, &:statements)
+ assert_location(StatementsNode, "END { foo }", 6...9, &:statements)
+
+ assert_location(StatementsNode, "\"\#{foo}\"", 3...6) { |node| node.parts.first.statements }
+ end
+
+ def test_StringNode
+ assert_location(StringNode, '"foo"')
+ assert_location(StringNode, '%q[foo]')
+ end
+
+ def test_SuperNode
+ assert_location(SuperNode, "super foo")
+ assert_location(SuperNode, "super foo, bar")
+
+ assert_location(SuperNode, "super()")
+ assert_location(SuperNode, "super(foo)")
+ assert_location(SuperNode, "super(foo, bar)")
+
+ assert_location(SuperNode, "super() {}")
+ end
+
+ def test_SymbolNode
+ assert_location(SymbolNode, ":foo")
+ end
+
+ def test_TrueNode
+ assert_location(TrueNode, "true")
+ end
+
+ def test_UndefNode
+ assert_location(UndefNode, "undef foo")
+ assert_location(UndefNode, "undef foo, bar")
+ end
+
+ def test_UnlessNode
+ assert_location(UnlessNode, "foo unless bar")
+ assert_location(UnlessNode, "unless bar; foo; end")
+ end
+
+ def test_UntilNode
+ assert_location(UntilNode, "foo = bar until baz")
+ assert_location(UntilNode, "until bar;baz;end")
+ end
+
+ def test_WhenNode
+ assert_location(WhenNode, "case foo; when bar; end", 10...18) { |node| node.conditions.first }
+ end
+
+ def test_WhileNode
+ assert_location(WhileNode, "foo = bar while foo != baz")
+ assert_location(WhileNode, "while a;bar;baz;end")
+ end
+
+ def test_XStringNode
+ assert_location(XStringNode, "`foo`")
+ assert_location(XStringNode, "%x[foo]")
+ end
+
+ def test_YieldNode
+ assert_location(YieldNode, "def test; yield; end", 10...15) { |node| node.body.body.first }
+ assert_location(YieldNode, "def test; yield foo; end", 10...19) { |node| node.body.body.first }
+ assert_location(YieldNode, "def test; yield foo, bar; end", 10...24) { |node| node.body.body.first }
+ assert_location(YieldNode, "def test; yield(foo); end", 10...20) { |node| node.body.body.first }
+ end
+
+ def test_all_tested
+ expected = Prism.constants.grep(/.Node$/).sort - %i[MissingNode ProgramNode]
+ actual = LocationTest.instance_methods(false).grep(/.Node$/).map { |name| name[5..].to_sym }.sort
+ assert_equal expected, actual
+ end
+
+ private
+
+ def assert_location(kind, source, expected = 0...source.length, **options)
+ result = Prism.parse(source, **options)
+ assert result.success?
+
+ node = result.value.statements.body.last
+ node = yield node if block_given?
+
+ if expected.begin == 0
+ assert_equal 0, node.location.start_column
+ end
+
+ if expected.end == source.length
+ assert_equal source.split("\n").last.length, node.location.end_column
+ end
+
+ assert_kind_of kind, node
+ assert_equal expected.begin, node.location.start_offset
+ assert_equal expected.end, node.location.end_offset
+ end
+ end
+end
diff --git a/test/prism/magic_comment_test.rb b/test/prism/magic_comment_test.rb
new file mode 100644
index 0000000000..9e2e92af92
--- /dev/null
+++ b/test/prism/magic_comment_test.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+return if RUBY_ENGINE != "ruby"
+
+module Prism
+ class MagicCommentTest < TestCase
+ examples = [
+ "# encoding: ascii",
+ "# coding: ascii",
+ "# eNcOdInG: ascii",
+ "# CoDiNg: ascii",
+ "# \s\t\v encoding \s\t\v : \s\t\v ascii \s\t\v",
+ "# -*- encoding: ascii -*-",
+ "# -*- coding: ascii -*-",
+ "# -*- eNcOdInG: ascii -*-",
+ "# -*- CoDiNg: ascii -*-",
+ "# -*- \s\t\v encoding \s\t\v : \s\t\v ascii \s\t\v -*-",
+ "# -*- foo: bar; encoding: ascii -*-",
+ "# coding \t \r \v : \t \v \r ascii-8bit",
+ "# vim: filetype=ruby, fileencoding=windows-31j, tabsize=3, shiftwidth=3"
+ ]
+
+ examples.each.with_index(1) do |example, index|
+ define_method(:"test_magic_comment_#{index}") do
+ expected = RubyVM::InstructionSequence.compile(%Q{#{example}\n""}).eval.encoding
+ actual = Prism.parse(example).encoding
+ assert_equal expected, actual
+ end
+ end
+ end
+end
diff --git a/test/prism/memsize_test.rb b/test/prism/memsize_test.rb
new file mode 100644
index 0000000000..d7e1448dbc
--- /dev/null
+++ b/test/prism/memsize_test.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+return if Prism::BACKEND == :FFI
+
+module Prism
+ class MemsizeTest < TestCase
+ def test_memsize
+ result = Debug.memsize("2 + 3")
+
+ assert_equal 5, result[:length]
+ assert_kind_of Integer, result[:memsize]
+ assert_equal 6, result[:node_count]
+ end
+ end
+end
diff --git a/test/prism/newline_test.rb b/test/prism/newline_test.rb
new file mode 100644
index 0000000000..e9975b346e
--- /dev/null
+++ b/test/prism/newline_test.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+return unless defined?(RubyVM::InstructionSequence)
+
+module Prism
+ class NewlineTest < TestCase
+ base = File.expand_path("../", __FILE__)
+ filepaths = Dir["*.rb", base: base] - %w[encoding_test.rb errors_test.rb parser_test.rb static_literals_test.rb unescape_test.rb]
+
+ filepaths.each do |relative|
+ define_method("test_newline_flags_#{relative}") do
+ assert_newlines(base, relative)
+ end
+ end
+
+ private
+
+ def assert_newlines(base, relative)
+ filepath = File.join(base, relative)
+ source = File.read(filepath, binmode: true, external_encoding: Encoding::UTF_8)
+ expected = rubyvm_lines(source)
+
+ result = Prism.parse_file(filepath)
+ assert_empty result.errors
+ actual = prism_lines(result)
+
+ source.each_line.with_index(1) do |line, line_number|
+ # Lines like `while (foo = bar)` result in two line flags in the
+ # bytecode but only one newline flag in the AST. We need to remove the
+ # extra line flag from the bytecode to make the test pass.
+ if line.match?(/while \(/)
+ index = expected.index(line_number)
+ expected.delete_at(index) if index
+ end
+
+ # Lines like `foo =` where the value is on the next line result in
+ # another line flag in the bytecode but only one newline flag in the
+ # AST.
+ if line.match?(/^\s+\w+ =$/)
+ if source.lines[line_number].match?(/^\s+case/)
+ actual[actual.index(line_number)] += 1
+ else
+ actual.delete_at(actual.index(line_number))
+ end
+ end
+
+ if line.match?(/^\s+\w+ = \[$/)
+ if !expected.include?(line_number) && !expected.include?(line_number + 2)
+ actual[actual.index(line_number)] += 1
+ end
+ end
+ end
+
+ assert_equal expected, actual
+ end
+
+ def ignore_warnings
+ previous_verbosity = $VERBOSE
+ $VERBOSE = nil
+ yield
+ ensure
+ $VERBOSE = previous_verbosity
+ end
+
+ def rubyvm_lines(source)
+ queue = [ignore_warnings { RubyVM::InstructionSequence.compile(source) }]
+ lines = []
+
+ while iseq = queue.shift
+ lines.concat(iseq.trace_points.filter_map { |line, event| line if event == :line })
+ iseq.each_child { |insn| queue << insn unless insn.label.start_with?("ensure in ") }
+ end
+
+ lines.sort
+ end
+
+ def prism_lines(result)
+ result.mark_newlines!
+
+ queue = [result.value]
+ newlines = []
+
+ while node = queue.shift
+ queue.concat(node.compact_child_nodes)
+ newlines << result.source.line(node.location.start_offset) if node&.newline?
+ end
+
+ newlines.sort
+ end
+ end
+end
diff --git a/test/prism/parameters_signature_test.rb b/test/prism/parameters_signature_test.rb
new file mode 100644
index 0000000000..0eed8d993d
--- /dev/null
+++ b/test/prism/parameters_signature_test.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+return if RUBY_VERSION < "3.2"
+
+module Prism
+ class ParametersSignatureTest < TestCase
+ def test_req
+ assert_parameters([[:req, :a]], "a")
+ end
+
+ def test_req_destructure
+ assert_parameters([[:req]], "(a, b)")
+ end
+
+ def test_opt
+ assert_parameters([[:opt, :a]], "a = 1")
+ end
+
+ def test_rest
+ assert_parameters([[:rest, :a]], "*a")
+ end
+
+ def test_rest_anonymous
+ assert_parameters([[:rest, :*]], "*")
+ end
+
+ def test_post
+ assert_parameters([[:rest, :a], [:req, :b]], "*a, b")
+ end
+
+ def test_post_destructure
+ assert_parameters([[:rest, :a], [:req]], "*a, (b, c)")
+ end
+
+ def test_keyreq
+ assert_parameters([[:keyreq, :a]], "a:")
+ end
+
+ def test_key
+ assert_parameters([[:key, :a]], "a: 1")
+ end
+
+ def test_keyrest
+ assert_parameters([[:keyrest, :a]], "**a")
+ end
+
+ def test_nokey
+ assert_parameters([[:nokey]], "**nil")
+ end
+
+ def test_keyrest_anonymous
+ assert_parameters([[:keyrest, :**]], "**")
+ end
+
+ def test_key_ordering
+ omit("TruffleRuby returns keys in order they were declared") if RUBY_ENGINE == "truffleruby"
+
+ assert_parameters([[:keyreq, :a], [:keyreq, :b], [:key, :c], [:key, :d]], "a:, c: 1, b:, d: 2")
+ end
+
+ def test_block
+ assert_parameters([[:block, :a]], "&a")
+ end
+
+ def test_block_anonymous
+ assert_parameters([[:block, :&]], "&")
+ end
+
+ def test_forwarding
+ assert_parameters([[:rest, :*], [:keyrest, :**], [:block, :&]], "...")
+ end
+
+ private
+
+ def assert_parameters(expected, source)
+ eval("def self.m(#{source}); end")
+
+ begin
+ assert_equal(expected, method(:m).parameters)
+ assert_equal(expected, signature(source))
+ ensure
+ singleton_class.undef_method(:m)
+ end
+ end
+
+ def signature(source)
+ program = Prism.parse("def m(#{source}); end").value
+ program.statements.body.first.parameters.signature
+ end
+ end
+end
diff --git a/test/prism/parse_comments_test.rb b/test/prism/parse_comments_test.rb
new file mode 100644
index 0000000000..30086e3155
--- /dev/null
+++ b/test/prism/parse_comments_test.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+module Prism
+ class ParseCommentsTest < TestCase
+ def test_parse_comments
+ comments = Prism.parse_comments("# foo")
+
+ assert_kind_of Array, comments
+ assert_equal 1, comments.length
+ end
+
+ def test_parse_file_comments
+ comments = Prism.parse_file_comments(__FILE__)
+
+ assert_kind_of Array, comments
+ assert_equal 1, comments.length
+ end
+ end
+end
diff --git a/test/prism/parse_stream_test.rb b/test/prism/parse_stream_test.rb
new file mode 100644
index 0000000000..9e6347b92b
--- /dev/null
+++ b/test/prism/parse_stream_test.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+require "stringio"
+
+module Prism
+ class ParseStreamTest < TestCase
+ def test_single_line
+ io = StringIO.new("1 + 2")
+ result = Prism.parse_stream(io)
+
+ assert result.success?
+ assert_kind_of Prism::CallNode, result.value.statements.body.first
+ end
+
+ def test_multi_line
+ io = StringIO.new("1 + 2\n3 + 4")
+ result = Prism.parse_stream(io)
+
+ assert result.success?
+ assert_kind_of Prism::CallNode, result.value.statements.body.first
+ assert_kind_of Prism::CallNode, result.value.statements.body.last
+ end
+
+ def test_multi_read
+ io = StringIO.new("a" * 4096 * 4)
+ result = Prism.parse_stream(io)
+
+ assert result.success?
+ assert_kind_of Prism::CallNode, result.value.statements.body.first
+ end
+
+ def test___END__
+ io = StringIO.new("1 + 2\n3 + 4\n__END__\n5 + 6")
+ result = Prism.parse_stream(io)
+
+ assert result.success?
+ assert_equal 2, result.value.statements.body.length
+ assert_equal "5 + 6", io.read
+ end
+
+ def test_false___END___in_string
+ io = StringIO.new("1 + 2\n3 + 4\n\"\n__END__\n\"\n5 + 6")
+ result = Prism.parse_stream(io)
+
+ assert result.success?
+ assert_equal 4, result.value.statements.body.length
+ end
+
+ def test_false___END___in_regexp
+ io = StringIO.new("1 + 2\n3 + 4\n/\n__END__\n/\n5 + 6")
+ result = Prism.parse_stream(io)
+
+ assert result.success?
+ assert_equal 4, result.value.statements.body.length
+ end
+
+ def test_false___END___in_list
+ io = StringIO.new("1 + 2\n3 + 4\n%w[\n__END__\n]\n5 + 6")
+ result = Prism.parse_stream(io)
+
+ assert result.success?
+ assert_equal 4, result.value.statements.body.length
+ end
+
+ def test_false___END___in_heredoc
+ io = StringIO.new("1 + 2\n3 + 4\n<<-EOF\n__END__\nEOF\n5 + 6")
+ result = Prism.parse_stream(io)
+
+ assert result.success?
+ assert_equal 4, result.value.statements.body.length
+ end
+ end
+end
diff --git a/test/prism/parse_test.rb b/test/prism/parse_test.rb
new file mode 100644
index 0000000000..afb53e0668
--- /dev/null
+++ b/test/prism/parse_test.rb
@@ -0,0 +1,371 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+module Prism
+ class ParseTest < TestCase
+ # A subclass of Ripper that extracts out magic comments.
+ class MagicCommentRipper < Ripper
+ attr_reader :magic_comments
+
+ def initialize(*)
+ super
+ @magic_comments = []
+ end
+
+ def on_magic_comment(key, value)
+ @magic_comments << [key, value]
+ super
+ end
+ end
+
+ # When we pretty-print the trees to compare against the snapshots, we want to
+ # be certain that we print with the same external encoding. This is because
+ # methods like Symbol#inspect take into account external encoding and it could
+ # change how the snapshot is generated. On machines with certain settings
+ # (like LANG=C or -Eascii-8bit) this could have been changed. So here we're
+ # going to force it to be UTF-8 to keep the snapshots consistent.
+ def setup
+ @previous_default_external = Encoding.default_external
+ ignore_warnings { Encoding.default_external = Encoding::UTF_8 }
+ end
+
+ def teardown
+ ignore_warnings { Encoding.default_external = @previous_default_external }
+ end
+
+ def test_empty_string
+ result = Prism.parse("")
+ assert_equal [], result.value.statements.body
+ end
+
+ def test_parse_takes_file_path
+ filepath = "filepath.rb"
+ result = Prism.parse("def foo; __FILE__; end", filepath: filepath)
+
+ assert_equal filepath, find_source_file_node(result.value).filepath
+ end
+
+ def test_parse_takes_line
+ line = 4
+ result = Prism.parse("def foo\n __FILE__\nend", line: line)
+
+ assert_equal line, result.value.location.start_line
+ assert_equal line + 1, find_source_file_node(result.value).location.start_line
+
+ result = Prism.parse_lex("def foo\n __FILE__\nend", line: line)
+ assert_equal line, result.value.first.location.start_line
+ end
+
+ def test_parse_takes_negative_lines
+ line = -2
+ result = Prism.parse("def foo\n __FILE__\nend", line: line)
+
+ assert_equal line, result.value.location.start_line
+ assert_equal line + 1, find_source_file_node(result.value).location.start_line
+
+ result = Prism.parse_lex("def foo\n __FILE__\nend", line: line)
+ assert_equal line, result.value.first.location.start_line
+ end
+
+ def test_parse_lex
+ node, tokens = Prism.parse_lex("def foo; end").value
+
+ assert_kind_of ProgramNode, node
+ assert_equal 5, tokens.length
+ end
+
+ if !ENV["PRISM_BUILD_MINIMAL"]
+ def test_dump_file
+ assert_nothing_raised do
+ Prism.dump_file(__FILE__)
+ end
+
+ error = assert_raise Errno::ENOENT do
+ Prism.dump_file("idontexist.rb")
+ end
+
+ assert_equal "No such file or directory - idontexist.rb", error.message
+
+ assert_raise TypeError do
+ Prism.dump_file(nil)
+ end
+ end
+ end
+
+ def test_lex_file
+ assert_nothing_raised do
+ Prism.lex_file(__FILE__)
+ end
+
+ error = assert_raise Errno::ENOENT do
+ Prism.lex_file("idontexist.rb")
+ end
+
+ assert_equal "No such file or directory - idontexist.rb", error.message
+
+ assert_raise TypeError do
+ Prism.lex_file(nil)
+ end
+ end
+
+ def test_parse_lex_file
+ node, tokens = Prism.parse_lex_file(__FILE__).value
+
+ assert_kind_of ProgramNode, node
+ refute_empty tokens
+
+ error = assert_raise Errno::ENOENT do
+ Prism.parse_lex_file("idontexist.rb")
+ end
+
+ assert_equal "No such file or directory - idontexist.rb", error.message
+
+ assert_raise TypeError do
+ Prism.parse_lex_file(nil)
+ end
+ end
+
+ def test_parse_file
+ node = Prism.parse_file(__FILE__).value
+ assert_kind_of ProgramNode, node
+
+ error = assert_raise Errno::ENOENT do
+ Prism.parse_file("idontexist.rb")
+ end
+
+ assert_equal "No such file or directory - idontexist.rb", error.message
+
+ assert_raise TypeError do
+ Prism.parse_file(nil)
+ end
+ end
+
+ def test_parse_file_success
+ assert_predicate Prism.parse_file_comments(__FILE__), :any?
+
+ error = assert_raise Errno::ENOENT do
+ Prism.parse_file_comments("idontexist.rb")
+ end
+
+ assert_equal "No such file or directory - idontexist.rb", error.message
+
+ assert_raise TypeError do
+ Prism.parse_file_comments(nil)
+ end
+ end
+
+ def test_parse_file_comments
+ assert_predicate Prism.parse_file_comments(__FILE__), :any?
+
+ error = assert_raise Errno::ENOENT do
+ Prism.parse_file_comments("idontexist.rb")
+ end
+
+ assert_equal "No such file or directory - idontexist.rb", error.message
+
+ assert_raise TypeError do
+ Prism.parse_file_comments(nil)
+ end
+ end
+
+ # To accurately compare against Ripper, we need to make sure that we're
+ # running on CRuby 3.2+.
+ ripper_enabled = RUBY_ENGINE == "ruby" && RUBY_VERSION >= "3.2.0"
+
+ # The FOCUS environment variable allows you to specify one particular fixture
+ # to test, instead of all of them.
+ base = File.join(__dir__, "fixtures")
+ relatives = ENV["FOCUS"] ? [ENV["FOCUS"]] : Dir["**/*.txt", base: base]
+
+ relatives.each do |relative|
+ # These fail on TruffleRuby due to a difference in Symbol#inspect: :测试 vs :"测试"
+ next if RUBY_ENGINE == "truffleruby" and %w[emoji_method_calls.txt seattlerb/bug202.txt seattlerb/magic_encoding_comment.txt].include?(relative)
+
+ filepath = File.join(base, relative)
+ snapshot = File.expand_path(File.join("snapshots", relative), __dir__)
+
+ directory = File.dirname(snapshot)
+ FileUtils.mkdir_p(directory) unless File.directory?(directory)
+
+ ripper_should_match = ripper_enabled
+ check_valid_syntax = RUBY_VERSION >= "3.2.0"
+
+ case relative
+ when "seattlerb/pct_w_heredoc_interp_nested.txt"
+ # This file has changed behavior in Ripper in Ruby 3.3, so we skip it if
+ # we're on an earlier version.
+ ripper_should_match = false if RUBY_VERSION < "3.3.0"
+ when "seattlerb/heredoc_nested.txt", "whitequark/dedenting_heredoc.txt"
+ # It seems like there are some oddities with nested heredocs and ripper.
+ # Waiting for feedback on https://bugs.ruby-lang.org/issues/19838.
+ ripper_should_match = false
+ when "spanning_heredoc.txt", "spanning_heredoc_newlines.txt"
+ # Ripper seems to have a bug that the regex portions before and after
+ # the heredoc are combined into a single token. See
+ # https://bugs.ruby-lang.org/issues/19838.
+ ripper_should_match = false
+ when "heredocs_leading_whitespace.txt"
+ # Ruby < 3.3.0 cannot parse heredocs where there are leading whitespace
+ # characters in the heredoc start.
+ # Example: <<~' EOF' or <<-' EOF'
+ # https://bugs.ruby-lang.org/issues/19539
+ if RUBY_VERSION < "3.3.0"
+ ripper_should_match = false
+ check_valid_syntax = false
+ end
+ end
+
+ define_method "test_filepath_#{relative}" do
+ # First, read the source from the filepath. Use binmode to avoid
+ # converting CRLF on Windows, and explicitly set the external encoding
+ # to UTF-8 to override the binmode default.
+ source = File.read(filepath, binmode: true, external_encoding: Encoding::UTF_8)
+
+ # Make sure that the given source is valid syntax, otherwise we have an
+ # invalid fixture.
+ assert_valid_syntax(source) if check_valid_syntax
+
+ # Next, assert that there were no errors during parsing.
+ result = Prism.parse(source, filepath: relative)
+ assert_empty result.errors
+
+ # Next, pretty print the source.
+ printed = PP.pp(result.value, +"", 79)
+
+ if File.exist?(snapshot)
+ saved = File.read(snapshot)
+
+ # If the snapshot file exists, but the printed value does not match the
+ # snapshot, then update the snapshot file.
+ if printed != saved
+ File.write(snapshot, printed)
+ warn("Updated snapshot at #{snapshot}.")
+ end
+
+ # If the snapshot file exists, then assert that the printed value
+ # matches the snapshot.
+ assert_equal(saved, printed)
+ else
+ # If the snapshot file does not yet exist, then write it out now.
+ File.write(snapshot, printed)
+ warn("Created snapshot at #{snapshot}.")
+ end
+
+ if !ENV["PRISM_BUILD_MINIMAL"]
+ # Next, assert that the value can be serialized and deserialized
+ # without changing the shape of the tree.
+ assert_equal_nodes(result.value, Prism.load(source, Prism.dump(source, filepath: relative)).value)
+ end
+
+ # Next, check that the location ranges of each node in the tree are a
+ # superset of their respective child nodes.
+ assert_non_overlapping_locations(result.value)
+
+ # Next, assert that the newlines are in the expected places.
+ expected_newlines = [0]
+ source.b.scan("\n") { expected_newlines << $~.offset(0)[0] + 1 }
+ assert_equal expected_newlines, Debug.newlines(source)
+
+ if ripper_should_match
+ # Finally, assert that we can lex the source and get the same tokens as
+ # Ripper.
+ lex_result = Prism.lex_compat(source)
+ assert_equal [], lex_result.errors
+ tokens = lex_result.value
+
+ begin
+ Prism.lex_ripper(source).zip(tokens).each do |(ripper, prism)|
+ assert_equal ripper, prism
+ end
+ rescue SyntaxError
+ raise ArgumentError, "Test file has invalid syntax #{filepath}"
+ end
+
+ # Next, check that we get the correct number of magic comments when
+ # lexing with ripper.
+ expected = MagicCommentRipper.new(source).tap(&:parse).magic_comments
+ actual = result.magic_comments
+
+ assert_equal expected.length, actual.length
+ expected.zip(actual).each do |(expected_key, expected_value), magic_comment|
+ assert_equal expected_key, magic_comment.key
+ assert_equal expected_value, magic_comment.value
+ end
+ end
+ end
+ end
+
+ Dir["*.txt", base: base].each do |relative|
+ next if relative == "newline_terminated.txt" || relative == "spanning_heredoc_newlines.txt"
+
+ # We test every snippet (separated by \n\n) in isolation
+ # to ensure the parser does not try to read bytes further than the end of each snippet
+ define_method "test_individual_snippets_#{relative}" do
+ filepath = File.join(base, relative)
+
+ # First, read the source from the filepath. Use binmode to avoid converting CRLF on Windows,
+ # and explicitly set the external encoding to UTF-8 to override the binmode default.
+ file_contents = File.read(filepath, binmode: true, external_encoding: Encoding::UTF_8)
+
+ file_contents.split(/(?<=\S)\n\n(?=\S)/).each do |snippet|
+ snippet = snippet.rstrip
+ result = Prism.parse(snippet, filepath: relative)
+ assert_empty result.errors
+
+ if !ENV["PRISM_BUILD_MINIMAL"]
+ assert_equal_nodes(result.value, Prism.load(snippet, Prism.dump(snippet, filepath: relative)).value)
+ end
+ end
+ end
+ end
+
+ private
+
+ # Check that the location ranges of each node in the tree are a superset of
+ # their respective child nodes.
+ def assert_non_overlapping_locations(node)
+ queue = [node]
+
+ while (current = queue.shift)
+ # We only want to compare parent/child location overlap in the case that
+ # we are not looking at a heredoc. That's because heredoc locations are
+ # special in that they only use the declaration of the heredoc.
+ compare = !(current.is_a?(StringNode) ||
+ current.is_a?(XStringNode) ||
+ current.is_a?(InterpolatedStringNode) ||
+ current.is_a?(InterpolatedXStringNode)) ||
+ !current.opening&.start_with?("<<")
+
+ current.child_nodes.each do |child|
+ # child_nodes can return nil values, so we need to skip those.
+ next unless child
+
+ # Now that we know we have a child node, add that to the queue.
+ queue << child
+
+ if compare
+ assert_operator current.location.start_offset, :<=, child.location.start_offset
+ assert_operator current.location.end_offset, :>=, child.location.end_offset
+ end
+ end
+ end
+ end
+
+ def find_source_file_node(program)
+ queue = [program]
+ while (node = queue.shift)
+ return node if node.is_a?(SourceFileNode)
+ queue.concat(node.compact_child_nodes)
+ end
+ end
+
+ def ignore_warnings
+ previous_verbosity = $VERBOSE
+ $VERBOSE = nil
+ yield
+ ensure
+ $VERBOSE = previous_verbosity
+ end
+ end
+end
diff --git a/test/prism/parser_test.rb b/test/prism/parser_test.rb
new file mode 100644
index 0000000000..79b65cf75b
--- /dev/null
+++ b/test/prism/parser_test.rb
@@ -0,0 +1,186 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+begin
+ verbose, $VERBOSE = $VERBOSE, nil
+ require "parser/ruby33"
+ require "prism/translation/parser33"
+rescue LoadError
+ # In CRuby's CI, we're not going to test against the parser gem because we
+ # don't want to have to install it. So in this case we'll just skip this test.
+ return
+ensure
+ $VERBOSE = verbose
+end
+
+# First, opt in to every AST feature.
+Parser::Builders::Default.modernize
+
+# Modify the source map == check so that it doesn't check against the node
+# itself so we don't get into a recursive loop.
+Parser::Source::Map.prepend(
+ Module.new {
+ def ==(other)
+ self.class == other.class &&
+ (instance_variables - %i[@node]).map do |ivar|
+ instance_variable_get(ivar) == other.instance_variable_get(ivar)
+ end.reduce(:&)
+ end
+ }
+)
+
+# Next, ensure that we're comparing the nodes and also comparing the source
+# ranges so that we're getting all of the necessary information.
+Parser::AST::Node.prepend(
+ Module.new {
+ def ==(other)
+ super && (location == other.location)
+ end
+ }
+)
+
+module Prism
+ class ParserTest < TestCase
+ base = File.join(__dir__, "fixtures")
+
+ # These files are erroring because of the parser gem being wrong.
+ skip_incorrect = [
+ "embdoc_no_newline_at_end.txt"
+ ]
+
+ # These files are either failing to parse or failing to translate, so we'll
+ # skip them for now.
+ skip_all = skip_incorrect | [
+ "dash_heredocs.txt",
+ "dos_endings.txt",
+ "heredocs_with_ignored_newlines.txt",
+ "regex.txt",
+ "regex_char_width.txt",
+ "spanning_heredoc.txt",
+ "spanning_heredoc_newlines.txt",
+ "unescaping.txt"
+ ]
+
+ # Not sure why these files are failing on JRuby, but skipping them for now.
+ if RUBY_ENGINE == "jruby"
+ skip_all.push("emoji_method_calls.txt", "symbols.txt")
+ end
+
+ # These files are failing to translate their lexer output into the lexer
+ # output expected by the parser gem, so we'll skip them for now.
+ skip_tokens = [
+ "comments.txt",
+ "heredoc_with_comment.txt",
+ "indented_file_end.txt",
+ "methods.txt",
+ "strings.txt",
+ "tilde_heredocs.txt",
+ "xstring_with_backslash.txt"
+ ]
+
+ Dir["*.txt", base: base].each do |name|
+ next if skip_all.include?(name)
+
+ define_method("test_#{name}") do
+ assert_equal_parses(File.join(base, name), compare_tokens: !skip_tokens.include?(name))
+ end
+ end
+
+ private
+
+ def assert_equal_parses(filepath, compare_tokens: true)
+ buffer = Parser::Source::Buffer.new(filepath, 1)
+ buffer.source = File.read(filepath)
+
+ parser = Parser::Ruby33.new
+ parser.diagnostics.consumer = ->(*) {}
+ parser.diagnostics.all_errors_are_fatal = true
+
+ expected_ast, expected_comments, expected_tokens =
+ begin
+ parser.tokenize(buffer)
+ rescue ArgumentError, Parser::SyntaxError
+ return
+ end
+
+ actual_ast, actual_comments, actual_tokens =
+ Prism::Translation::Parser33.new.tokenize(buffer)
+
+ assert_equal expected_ast, actual_ast, -> { assert_equal_asts_message(expected_ast, actual_ast) }
+ assert_equal_tokens(expected_tokens, actual_tokens) if compare_tokens
+ assert_equal_comments(expected_comments, actual_comments)
+ end
+
+ def assert_equal_asts_message(expected_ast, actual_ast)
+ queue = [[expected_ast, actual_ast]]
+
+ while (left, right = queue.shift)
+ if left.type != right.type
+ return "expected: #{left.type}\nactual: #{right.type}"
+ end
+
+ if left.location != right.location
+ return "expected:\n#{left.inspect}\n#{left.location.inspect}\nactual:\n#{right.inspect}\n#{right.location.inspect}"
+ end
+
+ if left.type == :str && left.children[0] != right.children[0]
+ return "expected: #{left.inspect}\nactual: #{right.inspect}"
+ end
+
+ left.children.zip(right.children).each do |left_child, right_child|
+ queue << [left_child, right_child] if left_child.is_a?(Parser::AST::Node)
+ end
+ end
+
+ "expected: #{expected_ast.inspect}\nactual: #{actual_ast.inspect}"
+ end
+
+ def assert_equal_tokens(expected_tokens, actual_tokens)
+ if expected_tokens != actual_tokens
+ expected_index = 0
+ actual_index = 0
+
+ while expected_index < expected_tokens.length
+ expected_token = expected_tokens[expected_index]
+ actual_token = actual_tokens[actual_index]
+
+ expected_index += 1
+ actual_index += 1
+
+ # The parser gem always has a space before a string end in list
+ # literals, but we don't. So we'll skip over the space.
+ if expected_token[0] == :tSPACE && actual_token[0] == :tSTRING_END
+ expected_index += 1
+ next
+ end
+
+ # There are a lot of tokens that have very specific meaning according
+ # to the context of the parser. We don't expose that information in
+ # prism, so we need to normalize these tokens a bit.
+ case actual_token[0]
+ when :kDO
+ actual_token[0] = expected_token[0] if %i[kDO_BLOCK kDO_LAMBDA].include?(expected_token[0])
+ when :tLPAREN
+ actual_token[0] = expected_token[0] if expected_token[0] == :tLPAREN2
+ when :tPOW
+ actual_token[0] = expected_token[0] if expected_token[0] == :tDSTAR
+ end
+
+ # Now we can assert that the tokens are actually equal.
+ assert_equal expected_token, actual_token, -> {
+ "expected: #{expected_token.inspect}\n" \
+ "actual: #{actual_token.inspect}"
+ }
+ end
+ end
+ end
+
+ def assert_equal_comments(expected_comments, actual_comments)
+ assert_equal expected_comments, actual_comments, -> {
+ "expected: #{expected_comments.inspect}\n" \
+ "actual: #{actual_comments.inspect}"
+ }
+ end
+ end
+end
diff --git a/test/prism/pattern_test.rb b/test/prism/pattern_test.rb
new file mode 100644
index 0000000000..e0aa079cb9
--- /dev/null
+++ b/test/prism/pattern_test.rb
@@ -0,0 +1,132 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+module Prism
+ class PatternTest < TestCase
+ def test_invalid_syntax
+ assert_raise(Pattern::CompilationError) { scan("", "<>") }
+ end
+
+ def test_invalid_constant
+ assert_raise(Pattern::CompilationError) { scan("", "Foo") }
+ end
+
+ def test_invalid_nested_constant
+ assert_raise(Pattern::CompilationError) { scan("", "Foo::Bar") }
+ end
+
+ def test_regexp_with_interpolation
+ assert_raise(Pattern::CompilationError) { scan("", "/\#{foo}/") }
+ end
+
+ def test_string_with_interpolation
+ assert_raise(Pattern::CompilationError) { scan("", '"#{foo}"') }
+ end
+
+ def test_symbol_with_interpolation
+ assert_raise(Pattern::CompilationError) { scan("", ":\"\#{foo}\"") }
+ end
+
+ def test_invalid_node
+ assert_raise(Pattern::CompilationError) { scan("", "IntegerNode[^foo]") }
+ end
+
+ def test_self
+ assert_raise(Pattern::CompilationError) { scan("", "self") }
+ end
+
+ def test_array_pattern_no_constant
+ results = scan("1 + 2", "[IntegerNode]")
+
+ assert_equal 1, results.length
+ end
+
+ def test_array_pattern
+ results = scan("1 + 2", "CallNode[name: :+, receiver: IntegerNode, arguments: [IntegerNode]]")
+
+ assert_equal 1, results.length
+ end
+
+ def test_alternation_pattern
+ results = scan("Foo + Bar + 1", "ConstantReadNode | IntegerNode")
+
+ assert_equal 3, results.length
+ assert_equal 1, results.grep(IntegerNode).first.value
+ end
+
+ def test_constant_read_node
+ results = scan("Foo + Bar + Baz", "ConstantReadNode")
+
+ assert_equal 3, results.length
+ assert_equal %w[Bar Baz Foo], results.map(&:slice).sort
+ end
+
+ def test_object_const
+ results = scan("1 + 2 + 3", "IntegerNode[]")
+
+ assert_equal 3, results.length
+ end
+
+ def test_constant_path
+ results = scan("Foo + Bar + Baz", "Prism::ConstantReadNode")
+
+ assert_equal 3, results.length
+ end
+
+ def test_hash_pattern_no_constant
+ results = scan("Foo + Bar + Baz", "{ name: :+ }")
+
+ assert_equal 2, results.length
+ end
+
+ def test_hash_pattern_regexp
+ results = scan("Foo + Bar + Baz", "{ name: /^[[:punct:]]$/ }")
+
+ assert_equal 2, results.length
+ assert_equal ["Prism::CallNode"], results.map { |node| node.class.name }.uniq
+ end
+
+ def test_nil
+ results = scan("foo", "{ receiver: nil }")
+
+ assert_equal 1, results.length
+ end
+
+ def test_regexp_options
+ results = scan("@foo + @bar + @baz", "InstanceVariableReadNode[name: /^@B/i]")
+
+ assert_equal 2, results.length
+ end
+
+ def test_string_empty
+ results = scan("", "''")
+
+ assert_empty results
+ end
+
+ def test_symbol_empty
+ results = scan("", ":''")
+
+ assert_empty results
+ end
+
+ def test_symbol_plain
+ results = scan("@foo", "{ name: :\"@foo\" }")
+
+ assert_equal 1, results.length
+ end
+
+ def test_symbol
+ results = scan("@foo", "{ name: :@foo }")
+
+ assert_equal 1, results.length
+ end
+
+ private
+
+ def scan(source, query)
+ Prism::Pattern.new(query).scan(Prism.parse(source).value).to_a
+ end
+ end
+end
diff --git a/test/prism/redundant_return_test.rb b/test/prism/redundant_return_test.rb
new file mode 100644
index 0000000000..c668169245
--- /dev/null
+++ b/test/prism/redundant_return_test.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+module Prism
+ class RedundantReturnTest < TestCase
+ def test_statements
+ assert_redundant_return("def foo; return; end")
+ refute_redundant_return("def foo; return; 1; end")
+ end
+
+ def test_begin_implicit
+ assert_redundant_return("def foo; return; rescue; end")
+ refute_redundant_return("def foo; return; 1; rescue; end")
+ refute_redundant_return("def foo; return; rescue; else; end")
+ end
+
+ def test_begin_explicit
+ assert_redundant_return("def foo; begin; return; rescue; end; end")
+ refute_redundant_return("def foo; begin; return; 1; rescue; end; end")
+ refute_redundant_return("def foo; begin; return; rescue; else; end; end")
+ end
+
+ def test_if
+ assert_redundant_return("def foo; return if bar; end")
+ end
+
+ def test_unless
+ assert_redundant_return("def foo; return unless bar; end")
+ end
+
+ def test_else
+ assert_redundant_return("def foo; if bar; baz; else; return; end; end")
+ end
+
+ def test_case_when
+ assert_redundant_return("def foo; case bar; when baz; return; end; end")
+ end
+
+ def test_case_else
+ assert_redundant_return("def foo; case bar; when baz; else; return; end; end")
+ end
+
+ def test_case_match_in
+ assert_redundant_return("def foo; case bar; in baz; return; end; end")
+ end
+
+ def test_case_match_else
+ assert_redundant_return("def foo; case bar; in baz; else; return; end; end")
+ end
+
+ private
+
+ def assert_redundant_return(source)
+ assert find_return(source).redundant?
+ end
+
+ def refute_redundant_return(source)
+ refute find_return(source).redundant?
+ end
+
+ def find_return(source)
+ queue = [Prism.parse(source).value]
+
+ while (current = queue.shift)
+ return current if current.is_a?(ReturnNode)
+ queue.concat(current.compact_child_nodes)
+ end
+
+ flunk "Could not find return node in #{node.inspect}"
+ end
+ end
+end
diff --git a/test/prism/reflection_test.rb b/test/prism/reflection_test.rb
new file mode 100644
index 0000000000..869b68b1f8
--- /dev/null
+++ b/test/prism/reflection_test.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+module Prism
+ class ReflectionTest < TestCase
+ def test_fields_for
+ fields = Reflection.fields_for(CallNode)
+ methods = CallNode.instance_methods(false)
+
+ fields.each do |field|
+ if field.is_a?(Reflection::FlagsField)
+ field.flags.each do |flag|
+ assert_includes methods, flag
+ end
+ else
+ assert_includes methods, field.name
+ end
+ end
+ end
+ end
+end
diff --git a/test/prism/regexp_test.rb b/test/prism/regexp_test.rb
new file mode 100644
index 0000000000..0a5fc2b4fc
--- /dev/null
+++ b/test/prism/regexp_test.rb
@@ -0,0 +1,263 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+return if Prism::BACKEND == :FFI
+
+module Prism
+ class RegexpTest < TestCase
+ ##############################################################################
+ # These tests test the actual use case of extracting named capture groups
+ ##############################################################################
+
+ def test_named_captures_with_arrows
+ assert_equal(["foo"], named_captures("(?<foo>bar)"))
+ end
+
+ def test_named_captures_with_single_quotes
+ assert_equal(["foo"], named_captures("(?'foo'bar)"))
+ end
+
+ def test_nested_named_captures_with_arrows
+ assert_equal(["foo", "bar"], named_captures("(?<foo>(?<bar>baz))"))
+ end
+
+ def test_nested_named_captures_with_single_quotes
+ assert_equal(["foo", "bar"], named_captures("(?'foo'(?'bar'baz))"))
+ end
+
+ def test_allows_duplicate_named_captures
+ assert_equal(["foo", "foo"], named_captures("(?<foo>bar)(?<foo>baz)"))
+ end
+
+ def test_named_capture_inside_fake_range_quantifier
+ assert_equal(["foo"], named_captures("foo{1, (?<foo>2)}"))
+ end
+
+ ##############################################################################
+ # These tests test the rest of the AST. They are not exhaustive, but they
+ # should cover the most common cases. We test these to make sure we don't
+ # accidentally regress and stop being able to extract named captures.
+ ##############################################################################
+
+ def test_alternation
+ refute_nil(named_captures("foo|bar"))
+ end
+
+ def test_anchors
+ refute_nil(named_captures("^foo$"))
+ end
+
+ def test_any
+ refute_nil(named_captures("."))
+ end
+
+ def test_posix_character_classes
+ refute_nil(named_captures("[[:digit:]]"))
+ end
+
+ def test_negated_posix_character_classes
+ refute_nil(named_captures("[[:^digit:]]"))
+ end
+
+ def test_invalid_posix_character_classes_should_fall_back_to_regular_classes
+ refute_nil(named_captures("[[:foo]]"))
+ end
+
+ def test_character_sets
+ refute_nil(named_captures("[abc]"))
+ end
+
+ def test_nested_character_sets
+ refute_nil(named_captures("[[abc]]"))
+ end
+
+ def test_nested_character_sets_with_operators
+ refute_nil(named_captures("[[abc] && [def]]"))
+ end
+
+ def test_named_capture_inside_nested_character_set
+ assert_equal([], named_captures("[foo (?<foo>bar)]"))
+ end
+
+ def test_negated_character_sets
+ refute_nil(named_captures("[^abc]"))
+ end
+
+ def test_character_ranges
+ refute_nil(named_captures("[a-z]"))
+ end
+
+ def test_negated_character_ranges
+ refute_nil(named_captures("[^a-z]"))
+ end
+
+ def test_fake_named_captures_inside_character_sets
+ assert_equal([], named_captures("[a-z(?<foo>)]"))
+ end
+
+ def test_fake_named_capture_inside_character_set_with_escaped_ending
+ assert_equal([], named_captures("[a-z\\](?<foo>)]"))
+ end
+
+ def test_comments
+ refute_nil(named_captures("(?#foo)"))
+ end
+
+ def test_comments_with_escaped_parentheses
+ refute_nil(named_captures("(?#foo\\)\\))"))
+ end
+
+ def test_non_capturing_groups
+ refute_nil(named_captures("(?:foo)"))
+ end
+
+ def test_positive_lookaheads
+ refute_nil(named_captures("(?=foo)"))
+ end
+
+ def test_negative_lookaheads
+ refute_nil(named_captures("(?!foo)"))
+ end
+
+ def test_positive_lookbehinds
+ refute_nil(named_captures("(?<=foo)"))
+ end
+
+ def test_negative_lookbehinds
+ refute_nil(named_captures("(?<!foo)"))
+ end
+
+ def test_atomic_groups
+ refute_nil(named_captures("(?>foo)"))
+ end
+
+ def test_absence_operator
+ refute_nil(named_captures("(?~foo)"))
+ end
+
+ def test_conditional_expression_with_index
+ refute_nil(named_captures("(?(1)foo)"))
+ end
+
+ def test_conditional_expression_with_name
+ refute_nil(named_captures("(?(foo)bar)"))
+ end
+
+ def test_conditional_expression_with_group
+ refute_nil(named_captures("(?(<foo>)bar)"))
+ end
+
+ def test_options_on_groups
+ refute_nil(named_captures("(?imxdau:foo)"))
+ end
+
+ def test_options_on_groups_with_invalid_options
+ assert_nil(named_captures("(?z:bar)"))
+ end
+
+ def test_options_on_groups_getting_turned_off
+ refute_nil(named_captures("(?-imx:foo)"))
+ end
+
+ def test_options_on_groups_some_getting_turned_on_some_getting_turned_off
+ refute_nil(named_captures("(?im-x:foo)"))
+ end
+
+ def test_star_quantifier
+ refute_nil(named_captures("foo*"))
+ end
+
+ def test_plus_quantifier
+ refute_nil(named_captures("foo+"))
+ end
+
+ def test_question_mark_quantifier
+ refute_nil(named_captures("foo?"))
+ end
+
+ def test_endless_range_quantifier
+ refute_nil(named_captures("foo{1,}"))
+ end
+
+ def test_beginless_range_quantifier
+ refute_nil(named_captures("foo{,1}"))
+ end
+
+ def test_range_quantifier
+ refute_nil(named_captures("foo{1,2}"))
+ end
+
+ def test_fake_range_quantifier_because_of_spaces
+ refute_nil(named_captures("foo{1, 2}"))
+ end
+
+ ##############################################################################
+ # These test that flag values are correct.
+ ##############################################################################
+
+ def test_flag_ignorecase
+ assert_equal(Regexp::IGNORECASE, options("i"))
+ end
+
+ def test_flag_extended
+ assert_equal(Regexp::EXTENDED, options("x"))
+ end
+
+ def test_flag_multiline
+ assert_equal(Regexp::MULTILINE, options("m"))
+ end
+
+ def test_flag_fixedencoding
+ assert_equal(Regexp::FIXEDENCODING, options("e"))
+ assert_equal(Regexp::FIXEDENCODING, options("u"))
+ assert_equal(Regexp::FIXEDENCODING, options("s"))
+ end
+
+ def test_flag_noencoding
+ assert_equal(Regexp::NOENCODING, options("n"))
+ end
+
+ def test_flag_once
+ assert_equal(0, options("o"))
+ end
+
+ def test_flag_combined
+ value = Regexp::IGNORECASE | Regexp::MULTILINE | Regexp::EXTENDED
+ assert_equal(value, options("mix"))
+ end
+
+ def test_last_encoding_option_wins
+ regex = "/foo/nu"
+ option = Prism.parse(regex).value.statements.body.first.options
+
+ assert_equal Regexp::FIXEDENCODING, option
+
+ regex = "/foo/un"
+ option = Prism.parse(regex).value.statements.body.first.options
+
+ assert_equal Regexp::NOENCODING, option
+ end
+
+ private
+
+ def named_captures(source)
+ Debug.named_captures(source)
+ end
+
+ def options(flags)
+ options =
+ ["/foo/#{flags}", "/foo\#{1}/#{flags}"].map do |source|
+ Prism.parse(source).value.statements.body.first.options
+ end
+
+ # Check that we get the same set of options from both regular expressions
+ # and interpolated regular expressions.
+ assert_equal(1, options.uniq.length)
+
+ # Return the options from the first regular expression since we know they
+ # are the same.
+ options.first
+ end
+ end
+end
diff --git a/test/prism/ripper_test.rb b/test/prism/ripper_test.rb
new file mode 100644
index 0000000000..07238fc3d5
--- /dev/null
+++ b/test/prism/ripper_test.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+return if RUBY_VERSION < "3.3"
+
+require_relative "test_helper"
+
+module Prism
+ class RipperTest < TestCase
+ base = File.join(__dir__, "fixtures")
+ relatives = ENV["FOCUS"] ? [ENV["FOCUS"]] : Dir["**/*.txt", base: base]
+
+ incorrect = [
+ # Ripper incorrectly attributes the block to the keyword.
+ "seattlerb/block_break.txt",
+ "seattlerb/block_next.txt",
+ "seattlerb/block_return.txt",
+ "whitequark/break_block.txt",
+ "whitequark/next_block.txt",
+ "whitequark/return_block.txt",
+
+ # Ripper is not accounting for locals created by patterns using the **
+ # operator within an `in` clause.
+ "seattlerb/parse_pattern_058.txt",
+
+ # Ripper cannot handle named capture groups in regular expressions.
+ "regex.txt",
+ "regex_char_width.txt",
+ "whitequark/lvar_injecting_match.txt",
+
+ # Ripper fails to understand some structures that span across heredocs.
+ "spanning_heredoc.txt"
+ ]
+
+ omitted = [
+ "dos_endings.txt",
+ "heredocs_with_ignored_newlines.txt",
+ "seattlerb/block_call_dot_op2_brace_block.txt",
+ "seattlerb/block_command_operation_colon.txt",
+ "seattlerb/block_command_operation_dot.txt",
+ "seattlerb/heredoc__backslash_dos_format.txt",
+ "seattlerb/heredoc_backslash_nl.txt",
+ "seattlerb/heredoc_nested.txt",
+ "seattlerb/heredoc_squiggly_blank_line_plus_interpolation.txt",
+ "tilde_heredocs.txt",
+ "unparser/corpus/semantic/dstr.txt",
+ "whitequark/dedenting_heredoc.txt",
+ "whitequark/parser_drops_truncated_parts_of_squiggly_heredoc.txt",
+ "whitequark/parser_slash_slash_n_escaping_in_literals.txt",
+ "whitequark/send_block_chain_cmd.txt",
+ "whitequark/slash_newline_in_heredocs.txt"
+ ]
+
+ relatives.each do |relative|
+ # Skip the tests that Ripper is reporting the wrong results for.
+ next if incorrect.include?(relative)
+
+ # Skip the tests we haven't implemented yet.
+ next if omitted.include?(relative)
+
+ filepath = File.join(__dir__, "fixtures", relative)
+
+ define_method "test_ripper_#{relative}" do
+ source = File.read(filepath, binmode: true, external_encoding: Encoding::UTF_8)
+
+ case relative
+ when /break|next|redo|if|unless|rescue|control|keywords|retry/
+ source = "-> do\nrescue\n#{source}\nend"
+ end
+
+ case source
+ when /^ *yield/
+ source = "def __invalid_yield__\n#{source}\nend"
+ end
+
+ assert_ripper(source)
+ end
+ end
+
+ private
+
+ def assert_ripper(source)
+ assert_equal Ripper.sexp_raw(source), Prism::Translation::Ripper.sexp_raw(source)
+ end
+ end
+end
diff --git a/test/prism/ruby_api_test.rb b/test/prism/ruby_api_test.rb
new file mode 100644
index 0000000000..9e408d1edd
--- /dev/null
+++ b/test/prism/ruby_api_test.rb
@@ -0,0 +1,275 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+module Prism
+ class RubyAPITest < TestCase
+ if !ENV["PRISM_BUILD_MINIMAL"]
+ def test_ruby_api
+ filepath = __FILE__
+ source = File.read(filepath, binmode: true, external_encoding: Encoding::UTF_8)
+
+ assert_equal Prism.lex(source, filepath: filepath).value, Prism.lex_file(filepath).value
+ assert_equal Prism.dump(source, filepath: filepath), Prism.dump_file(filepath)
+
+ serialized = Prism.dump(source, filepath: filepath)
+ ast1 = Prism.load(source, serialized).value
+ ast2 = Prism.parse(source, filepath: filepath).value
+ ast3 = Prism.parse_file(filepath).value
+
+ assert_equal_nodes ast1, ast2
+ assert_equal_nodes ast2, ast3
+ end
+ end
+
+ def test_parse_success?
+ assert Prism.parse_success?("1")
+ refute Prism.parse_success?("<>")
+ end
+
+ def test_parse_file_success?
+ assert Prism.parse_file_success?(__FILE__)
+ end
+
+ def test_options
+ assert_equal "", Prism.parse("__FILE__").value.statements.body[0].filepath
+ assert_equal "foo.rb", Prism.parse("__FILE__", filepath: "foo.rb").value.statements.body[0].filepath
+
+ assert_equal 1, Prism.parse("foo").value.statements.body[0].location.start_line
+ assert_equal 10, Prism.parse("foo", line: 10).value.statements.body[0].location.start_line
+
+ refute Prism.parse("\"foo\"").value.statements.body[0].frozen?
+ assert Prism.parse("\"foo\"", frozen_string_literal: true).value.statements.body[0].frozen?
+ refute Prism.parse("\"foo\"", frozen_string_literal: false).value.statements.body[0].frozen?
+
+ assert_kind_of Prism::CallNode, Prism.parse("foo").value.statements.body[0]
+ assert_kind_of Prism::LocalVariableReadNode, Prism.parse("foo", scopes: [[:foo]]).value.statements.body[0]
+ assert_equal 1, Prism.parse("foo", scopes: [[:foo], []]).value.statements.body[0].depth
+
+ assert_equal [:foo], Prism.parse("foo", scopes: [[:foo]]).value.locals
+ end
+
+ def test_literal_value_method
+ assert_equal 123, parse_expression("123").value
+ assert_equal 3.14, parse_expression("3.14").value
+ assert_equal 42i, parse_expression("42i").value
+ assert_equal 42.1ri, parse_expression("42.1ri").value
+ assert_equal 3.14i, parse_expression("3.14i").value
+ assert_equal 42r, parse_expression("42r").value
+ assert_equal 0.5r, parse_expression("0.5r").value
+ assert_equal 42ri, parse_expression("42ri").value
+ assert_equal 0.5ri, parse_expression("0.5ri").value
+ assert_equal 0xFFr, parse_expression("0xFFr").value
+ assert_equal 0xFFri, parse_expression("0xFFri").value
+ end
+
+ def test_location_join
+ recv, args_node, _ = parse_expression("1234 + 567").child_nodes
+ arg = args_node.arguments[0]
+
+ joined = recv.location.join(arg.location)
+ assert_equal 0, joined.start_offset
+ assert_equal 10, joined.length
+
+ assert_raise RuntimeError, "Incompatible locations" do
+ arg.location.join(recv.location)
+ end
+
+ other_arg = parse_expression("1234 + 567").arguments.arguments[0]
+
+ assert_raise RuntimeError, "Incompatible sources" do
+ other_arg.location.join(recv.location)
+ end
+
+ assert_raise RuntimeError, "Incompatible sources" do
+ recv.location.join(other_arg.location)
+ end
+ end
+
+ def test_location_character_offsets
+ program = Prism.parse("😀 + 😀\n😠||= ðŸ˜").value
+
+ # first 😀
+ location = program.statements.body.first.receiver.location
+ assert_equal 0, location.start_character_offset
+ assert_equal 1, location.end_character_offset
+ assert_equal 0, location.start_character_column
+ assert_equal 1, location.end_character_column
+
+ # second 😀
+ location = program.statements.body.first.arguments.arguments.first.location
+ assert_equal 4, location.start_character_offset
+ assert_equal 5, location.end_character_offset
+ assert_equal 4, location.start_character_column
+ assert_equal 5, location.end_character_column
+
+ # first ðŸ˜
+ location = program.statements.body.last.name_loc
+ assert_equal 6, location.start_character_offset
+ assert_equal 7, location.end_character_offset
+ assert_equal 0, location.start_character_column
+ assert_equal 1, location.end_character_column
+
+ # second ðŸ˜
+ location = program.statements.body.last.value.location
+ assert_equal 12, location.start_character_offset
+ assert_equal 13, location.end_character_offset
+ assert_equal 6, location.start_character_column
+ assert_equal 7, location.end_character_column
+ end
+
+ def test_location_code_units
+ program = Prism.parse("😀 + 😀\n😠||= ðŸ˜").value
+
+ # first 😀
+ location = program.statements.body.first.receiver.location
+
+ assert_equal 0, location.start_code_units_offset(Encoding::UTF_8)
+ assert_equal 0, location.start_code_units_offset(Encoding::UTF_16LE)
+ assert_equal 0, location.start_code_units_offset(Encoding::UTF_32LE)
+
+ assert_equal 1, location.end_code_units_offset(Encoding::UTF_8)
+ assert_equal 2, location.end_code_units_offset(Encoding::UTF_16LE)
+ assert_equal 1, location.end_code_units_offset(Encoding::UTF_32LE)
+
+ assert_equal 0, location.start_code_units_column(Encoding::UTF_8)
+ assert_equal 0, location.start_code_units_column(Encoding::UTF_16LE)
+ assert_equal 0, location.start_code_units_column(Encoding::UTF_32LE)
+
+ assert_equal 1, location.end_code_units_column(Encoding::UTF_8)
+ assert_equal 2, location.end_code_units_column(Encoding::UTF_16LE)
+ assert_equal 1, location.end_code_units_column(Encoding::UTF_32LE)
+
+ # second 😀
+ location = program.statements.body.first.arguments.arguments.first.location
+
+ assert_equal 4, location.start_code_units_offset(Encoding::UTF_8)
+ assert_equal 5, location.start_code_units_offset(Encoding::UTF_16LE)
+ assert_equal 4, location.start_code_units_offset(Encoding::UTF_32LE)
+
+ assert_equal 5, location.end_code_units_offset(Encoding::UTF_8)
+ assert_equal 7, location.end_code_units_offset(Encoding::UTF_16LE)
+ assert_equal 5, location.end_code_units_offset(Encoding::UTF_32LE)
+
+ assert_equal 4, location.start_code_units_column(Encoding::UTF_8)
+ assert_equal 5, location.start_code_units_column(Encoding::UTF_16LE)
+ assert_equal 4, location.start_code_units_column(Encoding::UTF_32LE)
+
+ assert_equal 5, location.end_code_units_column(Encoding::UTF_8)
+ assert_equal 7, location.end_code_units_column(Encoding::UTF_16LE)
+ assert_equal 5, location.end_code_units_column(Encoding::UTF_32LE)
+
+ # first ðŸ˜
+ location = program.statements.body.last.name_loc
+
+ assert_equal 6, location.start_code_units_offset(Encoding::UTF_8)
+ assert_equal 8, location.start_code_units_offset(Encoding::UTF_16LE)
+ assert_equal 6, location.start_code_units_offset(Encoding::UTF_32LE)
+
+ assert_equal 7, location.end_code_units_offset(Encoding::UTF_8)
+ assert_equal 10, location.end_code_units_offset(Encoding::UTF_16LE)
+ assert_equal 7, location.end_code_units_offset(Encoding::UTF_32LE)
+
+ assert_equal 0, location.start_code_units_column(Encoding::UTF_8)
+ assert_equal 0, location.start_code_units_column(Encoding::UTF_16LE)
+ assert_equal 0, location.start_code_units_column(Encoding::UTF_32LE)
+
+ assert_equal 1, location.end_code_units_column(Encoding::UTF_8)
+ assert_equal 2, location.end_code_units_column(Encoding::UTF_16LE)
+ assert_equal 1, location.end_code_units_column(Encoding::UTF_32LE)
+
+ # second ðŸ˜
+ location = program.statements.body.last.value.location
+
+ assert_equal 12, location.start_code_units_offset(Encoding::UTF_8)
+ assert_equal 15, location.start_code_units_offset(Encoding::UTF_16LE)
+ assert_equal 12, location.start_code_units_offset(Encoding::UTF_32LE)
+
+ assert_equal 13, location.end_code_units_offset(Encoding::UTF_8)
+ assert_equal 17, location.end_code_units_offset(Encoding::UTF_16LE)
+ assert_equal 13, location.end_code_units_offset(Encoding::UTF_32LE)
+
+ assert_equal 6, location.start_code_units_column(Encoding::UTF_8)
+ assert_equal 7, location.start_code_units_column(Encoding::UTF_16LE)
+ assert_equal 6, location.start_code_units_column(Encoding::UTF_32LE)
+
+ assert_equal 7, location.end_code_units_column(Encoding::UTF_8)
+ assert_equal 9, location.end_code_units_column(Encoding::UTF_16LE)
+ assert_equal 7, location.end_code_units_column(Encoding::UTF_32LE)
+ end
+
+ def test_location_chop
+ location = Prism.parse("foo").value.location
+
+ assert_equal "fo", location.chop.slice
+ assert_equal "", location.chop.chop.chop.slice
+
+ # Check that we don't go negative.
+ 10.times { location = location.chop }
+ assert_equal "", location.slice
+ end
+
+ def test_location_slice_lines
+ result = Prism.parse("\nprivate def foo\nend\n")
+ method = result.value.statements.body.first.arguments.arguments.first
+
+ assert_equal "private def foo\nend\n", method.slice_lines
+ end
+
+ def test_heredoc?
+ refute parse_expression("\"foo\"").heredoc?
+ refute parse_expression("\"foo \#{1}\"").heredoc?
+ refute parse_expression("`foo`").heredoc?
+ refute parse_expression("`foo \#{1}`").heredoc?
+
+ assert parse_expression("<<~HERE\nfoo\nHERE\n").heredoc?
+ assert parse_expression("<<~HERE\nfoo \#{1}\nHERE\n").heredoc?
+ assert parse_expression("<<~`HERE`\nfoo\nHERE\n").heredoc?
+ assert parse_expression("<<~`HERE`\nfoo \#{1}\nHERE\n").heredoc?
+ end
+
+ # Through some bit hackery, we want to allow consumers to use the integer
+ # base flags as the base itself. It has a nice property that the current
+ # alignment provides them in the correct order. So here we test that our
+ # assumption holds so that it doesn't change out from under us.
+ #
+ # In C, this would look something like:
+ #
+ # ((flags & ~DECIMAL) << 1) || 10
+ #
+ # We have to do some other work in Ruby because 0 is truthy and ~ on an
+ # integer doesn't have a fixed width.
+ def test_integer_base_flags
+ base = -> (node) do
+ value = (node.send(:flags) & (0b1111 - IntegerBaseFlags::DECIMAL)) << 1
+ value == 0 ? 10 : value
+ end
+
+ assert_equal 2, base[parse_expression("0b1")]
+ assert_equal 8, base[parse_expression("0o1")]
+ assert_equal 10, base[parse_expression("0d1")]
+ assert_equal 16, base[parse_expression("0x1")]
+ end
+
+ def test_node_equality
+ assert_operator parse_expression("1"), :===, parse_expression("1")
+ assert_operator Prism.parse("1").value, :===, Prism.parse("1").value
+
+ complex_source = "class Something; @var = something.else { _1 }; end"
+ assert_operator parse_expression(complex_source), :===, parse_expression(complex_source)
+
+ refute_operator parse_expression("1"), :===, parse_expression("2")
+ refute_operator parse_expression("1"), :===, parse_expression("0x1")
+
+ complex_source_1 = "class Something; @var = something.else { _1 }; end"
+ complex_source_2 = "class Something; @var = something.else { _2 }; end"
+ refute_operator parse_expression(complex_source_1), :===, parse_expression(complex_source_2)
+ end
+
+ private
+
+ def parse_expression(source)
+ Prism.parse(source).value.statements.body.first
+ end
+ end
+end
diff --git a/test/prism/ruby_parser_test.rb b/test/prism/ruby_parser_test.rb
new file mode 100644
index 0000000000..8edeac4b4f
--- /dev/null
+++ b/test/prism/ruby_parser_test.rb
@@ -0,0 +1,144 @@
+# frozen_string_literal: true
+
+return if RUBY_ENGINE == "jruby"
+
+require_relative "test_helper"
+
+begin
+ require "ruby_parser"
+rescue LoadError
+ # In CRuby's CI, we're not going to test against the ruby_parser gem because
+ # we don't want to have to install it. So in this case we'll just skip this
+ # test.
+ return
+end
+
+# We want to also compare lines and files to make sure we're setting them
+# correctly.
+Sexp.prepend(
+ Module.new do
+ def ==(other)
+ super && line == other.line && line_max == other.line_max && file == other.file
+ end
+ end
+)
+
+module Prism
+ class RubyParserTest < TestCase
+ base = File.join(__dir__, "fixtures")
+
+ todos = %w[
+ heredocs_nested.txt
+ newline_terminated.txt
+ regex_char_width.txt
+ seattlerb/bug169.txt
+ seattlerb/dstr_evstr.txt
+ seattlerb/heredoc_squiggly_interp.txt
+ seattlerb/masgn_colon3.txt
+ seattlerb/messy_op_asgn_lineno.txt
+ seattlerb/op_asgn_primary_colon_const_command_call.txt
+ seattlerb/parse_line_evstr_after_break.txt
+ seattlerb/regexp_esc_C_slash.txt
+ seattlerb/str_lit_concat_bad_encodings.txt
+ seattlerb/str_pct_nested_nested.txt
+ unescaping.txt
+ unparser/corpus/literal/kwbegin.txt
+ unparser/corpus/literal/send.txt
+ unparser/corpus/semantic/dstr.txt
+ whitequark/masgn_const.txt
+ whitequark/ruby_bug_12402.txt
+ whitequark/ruby_bug_14690.txt
+ whitequark/space_args_block.txt
+ whitequark/string_concat.txt
+ ]
+
+ # https://github.com/seattlerb/ruby_parser/issues/344
+ failures = %w[
+ alias.txt
+ dos_endings.txt
+ heredocs_with_ignored_newlines.txt
+ method_calls.txt
+ methods.txt
+ multi_write.txt
+ not.txt
+ patterns.txt
+ regex.txt
+ seattlerb/and_multi.txt
+ seattlerb/heredoc__backslash_dos_format.txt
+ seattlerb/heredoc_bad_hex_escape.txt
+ seattlerb/heredoc_bad_oct_escape.txt
+ seattlerb/heredoc_with_extra_carriage_horrible_mix.txt
+ seattlerb/heredoc_with_extra_carriage_returns_windows.txt
+ seattlerb/heredoc_with_only_carriage_returns_windows.txt
+ seattlerb/heredoc_with_only_carriage_returns.txt
+ spanning_heredoc_newlines.txt
+ spanning_heredoc.txt
+ tilde_heredocs.txt
+ unparser/corpus/literal/literal.txt
+ while.txt
+ whitequark/class_definition_in_while_cond.txt
+ whitequark/cond_eflipflop.txt
+ whitequark/cond_iflipflop.txt
+ whitequark/cond_match_current_line.txt
+ whitequark/dedenting_heredoc.txt
+ whitequark/if_while_after_class__since_32.txt
+ whitequark/lvar_injecting_match.txt
+ whitequark/not.txt
+ whitequark/numparam_ruby_bug_19025.txt
+ whitequark/op_asgn_cmd.txt
+ whitequark/parser_bug_640.txt
+ whitequark/parser_slash_slash_n_escaping_in_literals.txt
+ whitequark/pattern_matching_single_line_allowed_omission_of_parentheses.txt
+ whitequark/pattern_matching_single_line.txt
+ whitequark/ruby_bug_11989.txt
+ whitequark/slash_newline_in_heredocs.txt
+ ]
+
+ Dir["**/*.txt", base: base].each do |name|
+ next if failures.include?(name)
+
+ define_method("test_#{name}") do
+ begin
+ # Parsing with ruby parser tends to be noisy with warnings, so we're
+ # turning those off.
+ previous_verbose, $VERBOSE = $VERBOSE, nil
+ assert_parse_file(base, name, todos.include?(name))
+ ensure
+ $VERBOSE = previous_verbose
+ end
+ end
+ end
+
+ private
+
+ def assert_parse_file(base, name, allowed_failure)
+ filepath = File.join(base, name)
+ expected = ::RubyParser.new.parse(File.read(filepath), filepath)
+ actual = Prism::Translation::RubyParser.parse_file(filepath)
+
+ if !allowed_failure
+ assert_equal_nodes expected, actual
+ elsif expected == actual
+ puts "#{name} now passes"
+ end
+ end
+
+ def assert_equal_nodes(left, right)
+ return if left == right
+
+ if left.is_a?(Sexp) && right.is_a?(Sexp)
+ if left.line != right.line
+ assert_equal "(#{left.inspect} line=#{left.line})", "(#{right.inspect} line=#{right.line})"
+ elsif left.file != right.file
+ assert_equal "(#{left.inspect} file=#{left.file})", "(#{right.inspect} file=#{right.file})"
+ elsif left.length != right.length
+ assert_equal "(#{left.inspect} length=#{left.length})", "(#{right.inspect} length=#{right.length})"
+ else
+ left.zip(right).each { |l, r| assert_equal_nodes(l, r) }
+ end
+ else
+ assert_equal left, right
+ end
+ end
+ end
+end
diff --git a/test/prism/snapshots/alias.txt b/test/prism/snapshots/alias.txt
new file mode 100644
index 0000000000..a952e96f67
--- /dev/null
+++ b/test/prism/snapshots/alias.txt
@@ -0,0 +1,194 @@
+@ ProgramNode (location: (1,0)-(23,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(23,11))
+ └── body: (length: 12)
+ ├── @ AliasMethodNode (location: (1,0)-(1,15))
+ │ ├── new_name:
+ │ │ @ SymbolNode (location: (1,6)-(1,10))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,6)-(1,7) = ":"
+ │ │ ├── value_loc: (1,7)-(1,10) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ ├── old_name:
+ │ │ @ SymbolNode (location: (1,11)-(1,15))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,11)-(1,12) = ":"
+ │ │ ├── value_loc: (1,12)-(1,15) = "bar"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "bar"
+ │ └── keyword_loc: (1,0)-(1,5) = "alias"
+ ├── @ AliasMethodNode (location: (3,0)-(3,21))
+ │ ├── new_name:
+ │ │ @ SymbolNode (location: (3,6)-(3,13))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (3,6)-(3,9) = "%s["
+ │ │ ├── value_loc: (3,9)-(3,12) = "abc"
+ │ │ ├── closing_loc: (3,12)-(3,13) = "]"
+ │ │ └── unescaped: "abc"
+ │ ├── old_name:
+ │ │ @ SymbolNode (location: (3,14)-(3,21))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (3,14)-(3,17) = "%s["
+ │ │ ├── value_loc: (3,17)-(3,20) = "def"
+ │ │ ├── closing_loc: (3,20)-(3,21) = "]"
+ │ │ └── unescaped: "def"
+ │ └── keyword_loc: (3,0)-(3,5) = "alias"
+ ├── @ AliasMethodNode (location: (5,0)-(5,19))
+ │ ├── new_name:
+ │ │ @ SymbolNode (location: (5,6)-(5,12))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (5,6)-(5,8) = ":'"
+ │ │ ├── value_loc: (5,8)-(5,11) = "abc"
+ │ │ ├── closing_loc: (5,11)-(5,12) = "'"
+ │ │ └── unescaped: "abc"
+ │ ├── old_name:
+ │ │ @ SymbolNode (location: (5,13)-(5,19))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (5,13)-(5,15) = ":'"
+ │ │ ├── value_loc: (5,15)-(5,18) = "def"
+ │ │ ├── closing_loc: (5,18)-(5,19) = "'"
+ │ │ └── unescaped: "def"
+ │ └── keyword_loc: (5,0)-(5,5) = "alias"
+ ├── @ AliasMethodNode (location: (7,0)-(7,23))
+ │ ├── new_name:
+ │ │ @ InterpolatedSymbolNode (location: (7,6)-(7,16))
+ │ │ ├── opening_loc: (7,6)-(7,8) = ":\""
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (7,8)-(7,11))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (7,8)-(7,11) = "abc"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "abc"
+ │ │ │ └── @ EmbeddedStatementsNode (location: (7,11)-(7,15))
+ │ │ │ ├── opening_loc: (7,11)-(7,13) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (7,13)-(7,14))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (7,13)-(7,14))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── closing_loc: (7,14)-(7,15) = "}"
+ │ │ └── closing_loc: (7,15)-(7,16) = "\""
+ │ ├── old_name:
+ │ │ @ SymbolNode (location: (7,17)-(7,23))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (7,17)-(7,19) = ":'"
+ │ │ ├── value_loc: (7,19)-(7,22) = "def"
+ │ │ ├── closing_loc: (7,22)-(7,23) = "'"
+ │ │ └── unescaped: "def"
+ │ └── keyword_loc: (7,0)-(7,5) = "alias"
+ ├── @ AliasGlobalVariableNode (location: (9,0)-(9,11))
+ │ ├── new_name:
+ │ │ @ GlobalVariableReadNode (location: (9,6)-(9,8))
+ │ │ └── name: :$a
+ │ ├── old_name:
+ │ │ @ BackReferenceReadNode (location: (9,9)-(9,11))
+ │ │ └── name: :$'
+ │ └── keyword_loc: (9,0)-(9,5) = "alias"
+ ├── @ AliasMethodNode (location: (11,0)-(11,13))
+ │ ├── new_name:
+ │ │ @ SymbolNode (location: (11,6)-(11,9))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (11,6)-(11,9) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ ├── old_name:
+ │ │ @ SymbolNode (location: (11,10)-(11,13))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (11,10)-(11,13) = "bar"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "bar"
+ │ └── keyword_loc: (11,0)-(11,5) = "alias"
+ ├── @ AliasGlobalVariableNode (location: (13,0)-(13,15))
+ │ ├── new_name:
+ │ │ @ GlobalVariableReadNode (location: (13,6)-(13,10))
+ │ │ └── name: :$foo
+ │ ├── old_name:
+ │ │ @ GlobalVariableReadNode (location: (13,11)-(13,15))
+ │ │ └── name: :$bar
+ │ └── keyword_loc: (13,0)-(13,5) = "alias"
+ ├── @ AliasMethodNode (location: (15,0)-(15,12))
+ │ ├── new_name:
+ │ │ @ SymbolNode (location: (15,6)-(15,9))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (15,6)-(15,9) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ ├── old_name:
+ │ │ @ SymbolNode (location: (15,10)-(15,12))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (15,10)-(15,12) = "if"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "if"
+ │ └── keyword_loc: (15,0)-(15,5) = "alias"
+ ├── @ AliasMethodNode (location: (17,0)-(17,13))
+ │ ├── new_name:
+ │ │ @ SymbolNode (location: (17,6)-(17,9))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (17,6)-(17,9) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ ├── old_name:
+ │ │ @ SymbolNode (location: (17,10)-(17,13))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (17,10)-(17,13) = "<=>"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "<=>"
+ │ └── keyword_loc: (17,0)-(17,5) = "alias"
+ ├── @ AliasMethodNode (location: (19,0)-(19,15))
+ │ ├── new_name:
+ │ │ @ SymbolNode (location: (19,6)-(19,9))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (19,6)-(19,7) = ":"
+ │ │ ├── value_loc: (19,7)-(19,9) = "=="
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "=="
+ │ ├── old_name:
+ │ │ @ SymbolNode (location: (19,10)-(19,15))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (19,10)-(19,11) = ":"
+ │ │ ├── value_loc: (19,11)-(19,15) = "eql?"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "eql?"
+ │ └── keyword_loc: (19,0)-(19,5) = "alias"
+ ├── @ AliasMethodNode (location: (21,0)-(21,9))
+ │ ├── new_name:
+ │ │ @ SymbolNode (location: (21,6)-(21,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (21,6)-(21,7) = "A"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "A"
+ │ ├── old_name:
+ │ │ @ SymbolNode (location: (21,8)-(21,9))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (21,8)-(21,9) = "B"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "B"
+ │ └── keyword_loc: (21,0)-(21,5) = "alias"
+ └── @ AliasMethodNode (location: (23,0)-(23,11))
+ ├── new_name:
+ │ @ SymbolNode (location: (23,6)-(23,8))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (23,6)-(23,7) = ":"
+ │ ├── value_loc: (23,7)-(23,8) = "A"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "A"
+ ├── old_name:
+ │ @ SymbolNode (location: (23,9)-(23,11))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (23,9)-(23,10) = ":"
+ │ ├── value_loc: (23,10)-(23,11) = "B"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "B"
+ └── keyword_loc: (23,0)-(23,5) = "alias"
diff --git a/test/prism/snapshots/arithmetic.txt b/test/prism/snapshots/arithmetic.txt
new file mode 100644
index 0000000000..c8a31c3d70
--- /dev/null
+++ b/test/prism/snapshots/arithmetic.txt
@@ -0,0 +1,255 @@
+@ ProgramNode (location: (1,0)-(13,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(13,8))
+ └── body: (length: 7)
+ ├── @ CallNode (location: (1,0)-(1,8))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,4)-(1,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (1,4)-(1,8))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (1,5)-(1,8))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (1,5)-(1,8) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :!
+ │ │ ├── message_loc: (1,4)-(1,5) = "!"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (3,0)-(3,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (3,0)-(3,4))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (3,1)-(3,4))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (3,1)-(3,4) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :-@
+ │ │ ├── message_loc: (3,0)-(3,1) = "-"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :*
+ │ ├── message_loc: (3,4)-(3,5) = "*"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,5)-(3,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (3,5)-(3,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (3,5)-(3,8) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (5,0)-(5,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (5,0)-(5,4))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (5,1)-(5,4))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (5,1)-(5,4) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :+@
+ │ │ ├── message_loc: (5,0)-(5,1) = "+"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :**
+ │ ├── message_loc: (5,4)-(5,6) = "**"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (5,6)-(5,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (5,6)-(5,9))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (5,6)-(5,9) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (7,0)-(7,8))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (7,0)-(7,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (7,4)-(7,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (7,4)-(7,8))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (7,5)-(7,8))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (7,5)-(7,8) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :~
+ │ │ ├── message_loc: (7,4)-(7,5) = "~"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (9,0)-(9,17))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (9,0)-(9,10))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (9,0)-(9,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (9,0)-(9,3) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :<<
+ │ │ ├── message_loc: (9,4)-(9,6) = "<<"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (9,7)-(9,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ CallNode (location: (9,7)-(9,10))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (9,7)-(9,10) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :<<
+ │ ├── message_loc: (9,11)-(9,13) = "<<"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (9,14)-(9,17))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (9,14)-(9,17))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (9,14)-(9,17) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (11,0)-(11,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (11,1)-(11,5))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ IntegerNode (location: (11,1)-(11,2))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :**
+ │ │ ├── message_loc: (11,2)-(11,4) = "**"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (11,4)-(11,5))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (11,4)-(11,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :-@
+ │ ├── message_loc: (11,0)-(11,1) = "-"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (13,0)-(13,8))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ IntegerNode (location: (13,0)-(13,2))
+ │ ├── flags: decimal
+ │ └── value: -1
+ ├── call_operator_loc: (13,2)-(13,3) = "."
+ ├── name: :zero?
+ ├── message_loc: (13,3)-(13,8) = "zero?"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/arrays.txt b/test/prism/snapshots/arrays.txt
new file mode 100644
index 0000000000..e8e53aacb9
--- /dev/null
+++ b/test/prism/snapshots/arrays.txt
@@ -0,0 +1,1837 @@
+@ ProgramNode (location: (1,0)-(122,32))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(122,32))
+ └── body: (length: 50)
+ ├── @ ArrayNode (location: (1,0)-(1,4))
+ │ ├── flags: contains_splat
+ │ ├── elements: (length: 1)
+ │ │ └── @ SplatNode (location: (1,1)-(1,3))
+ │ │ ├── operator_loc: (1,1)-(1,2) = "*"
+ │ │ └── expression:
+ │ │ @ CallNode (location: (1,2)-(1,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (1,2)-(1,3) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (1,0)-(1,1) = "["
+ │ └── closing_loc: (1,3)-(1,4) = "]"
+ ├── @ CallNode (location: (3,0)-(3,23))
+ │ ├── flags: attribute_write
+ │ ├── receiver:
+ │ │ @ CallNode (location: (3,0)-(3,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (3,0)-(3,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]=
+ │ ├── message_loc: (3,3)-(3,13) = "[bar, baz]"
+ │ ├── opening_loc: (3,3)-(3,4) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,4)-(3,23))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 3)
+ │ │ ├── @ CallNode (location: (3,4)-(3,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (3,4)-(3,7) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── @ CallNode (location: (3,9)-(3,12))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (3,9)-(3,12) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ ArrayNode (location: (3,16)-(3,23))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 3)
+ │ │ │ ├── @ IntegerNode (location: (3,16)-(3,17))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── @ IntegerNode (location: (3,19)-(3,20))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ └── @ IntegerNode (location: (3,22)-(3,23))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 3
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ ├── closing_loc: (3,12)-(3,13) = "]"
+ │ └── block: ∅
+ ├── @ ArrayNode (location: (5,0)-(5,13))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (5,1)-(5,12))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (5,1)-(5,12))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (5,1)-(5,3))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (5,1)-(5,2) = "a"
+ │ │ │ ├── closing_loc: (5,2)-(5,3) = ":"
+ │ │ │ └── unescaped: "a"
+ │ │ ├── value:
+ │ │ │ @ ArrayNode (location: (5,4)-(5,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 2)
+ │ │ │ │ ├── @ SymbolNode (location: (5,5)-(5,7))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: (5,5)-(5,6) = ":"
+ │ │ │ │ │ ├── value_loc: (5,6)-(5,7) = "b"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "b"
+ │ │ │ │ └── @ SymbolNode (location: (5,9)-(5,11))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (5,9)-(5,10) = ":"
+ │ │ │ │ ├── value_loc: (5,10)-(5,11) = "c"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "c"
+ │ │ │ ├── opening_loc: (5,4)-(5,5) = "["
+ │ │ │ └── closing_loc: (5,11)-(5,12) = "]"
+ │ │ └── operator_loc: ∅
+ │ ├── opening_loc: (5,0)-(5,1) = "["
+ │ └── closing_loc: (5,12)-(5,13) = "]"
+ ├── @ ArrayNode (location: (9,0)-(15,1))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 5)
+ │ │ ├── @ SymbolNode (location: (9,1)-(9,3))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (9,1)-(9,2) = ":"
+ │ │ │ ├── value_loc: (9,2)-(9,3) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ ├── @ SymbolNode (location: (9,5)-(9,7))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (9,5)-(9,6) = ":"
+ │ │ │ ├── value_loc: (9,6)-(9,7) = "b"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "b"
+ │ │ ├── @ SymbolNode (location: (10,0)-(10,2))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (10,0)-(10,1) = ":"
+ │ │ │ ├── value_loc: (10,1)-(10,2) = "c"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "c"
+ │ │ ├── @ IntegerNode (location: (10,3)-(10,4))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ SymbolNode (location: (14,0)-(14,2))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (14,0)-(14,1) = ":"
+ │ │ ├── value_loc: (14,1)-(14,2) = "d"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "d"
+ │ ├── opening_loc: (9,0)-(9,1) = "["
+ │ └── closing_loc: (15,0)-(15,1) = "]"
+ ├── @ ArrayNode (location: (18,0)-(26,1))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 5)
+ │ │ ├── @ SymbolNode (location: (18,1)-(18,3))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (18,1)-(18,2) = ":"
+ │ │ │ ├── value_loc: (18,2)-(18,3) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ ├── @ SymbolNode (location: (18,5)-(18,7))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (18,5)-(18,6) = ":"
+ │ │ │ ├── value_loc: (18,6)-(18,7) = "b"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "b"
+ │ │ ├── @ SymbolNode (location: (19,0)-(19,2))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (19,0)-(19,1) = ":"
+ │ │ │ ├── value_loc: (19,1)-(19,2) = "c"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "c"
+ │ │ ├── @ IntegerNode (location: (19,3)-(19,4))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ SymbolNode (location: (23,0)-(23,2))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (23,0)-(23,1) = ":"
+ │ │ ├── value_loc: (23,1)-(23,2) = "d"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "d"
+ │ ├── opening_loc: (18,0)-(18,1) = "["
+ │ └── closing_loc: (26,0)-(26,1) = "]"
+ ├── @ ArrayNode (location: (28,0)-(28,12))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (28,1)-(28,11))
+ │ │ ├── flags: ∅
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (28,1)-(28,11))
+ │ │ ├── key:
+ │ │ │ @ CallNode (location: (28,1)-(28,4))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (28,1)-(28,4) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (28,8)-(28,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (28,8)-(28,11) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (28,5)-(28,7) = "=>"
+ │ ├── opening_loc: (28,0)-(28,1) = "["
+ │ └── closing_loc: (28,11)-(28,12) = "]"
+ ├── @ CallNode (location: (30,0)-(30,19))
+ │ ├── flags: attribute_write
+ │ ├── receiver:
+ │ │ @ CallNode (location: (30,0)-(30,8))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (30,0)-(30,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (30,0)-(30,3) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :[]
+ │ │ ├── message_loc: (30,3)-(30,8) = "[bar]"
+ │ │ ├── opening_loc: (30,3)-(30,4) = "["
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (30,4)-(30,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ CallNode (location: (30,4)-(30,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (30,4)-(30,7) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── closing_loc: (30,7)-(30,8) = "]"
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]=
+ │ ├── message_loc: (30,8)-(30,13) = "[baz]"
+ │ ├── opening_loc: (30,8)-(30,9) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (30,9)-(30,19))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (30,9)-(30,12))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (30,9)-(30,12) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ CallNode (location: (30,16)-(30,19))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :qux
+ │ │ ├── message_loc: (30,16)-(30,19) = "qux"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (30,12)-(30,13) = "]"
+ │ └── block: ∅
+ ├── @ CallNode (location: (32,0)-(32,13))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (32,0)-(32,8))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (32,0)-(32,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (32,0)-(32,3) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :[]
+ │ │ ├── message_loc: (32,3)-(32,8) = "[bar]"
+ │ │ ├── opening_loc: (32,3)-(32,4) = "["
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (32,4)-(32,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ CallNode (location: (32,4)-(32,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (32,4)-(32,7) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── closing_loc: (32,7)-(32,8) = "]"
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]
+ │ ├── message_loc: (32,8)-(32,13) = "[baz]"
+ │ ├── opening_loc: (32,8)-(32,9) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (32,9)-(32,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (32,9)-(32,12))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (32,9)-(32,12) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (32,12)-(32,13) = "]"
+ │ └── block: ∅
+ ├── @ ArrayNode (location: (34,0)-(35,1))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 0)
+ │ ├── opening_loc: (34,0)-(34,1) = "["
+ │ └── closing_loc: (35,0)-(35,1) = "]"
+ ├── @ CallNode (location: (37,0)-(37,13))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (37,0)-(37,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (37,0)-(37,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]
+ │ ├── message_loc: (37,3)-(37,13) = "[bar, baz]"
+ │ ├── opening_loc: (37,3)-(37,4) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (37,4)-(37,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (37,4)-(37,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (37,4)-(37,7) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ CallNode (location: (37,9)-(37,12))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (37,9)-(37,12) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (37,12)-(37,13) = "]"
+ │ └── block: ∅
+ ├── @ CallNode (location: (39,0)-(39,19))
+ │ ├── flags: attribute_write
+ │ ├── receiver:
+ │ │ @ CallNode (location: (39,0)-(39,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (39,0)-(39,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]=
+ │ ├── message_loc: (39,3)-(39,13) = "[bar, baz]"
+ │ ├── opening_loc: (39,3)-(39,4) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (39,4)-(39,19))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 3)
+ │ │ ├── @ CallNode (location: (39,4)-(39,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (39,4)-(39,7) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── @ CallNode (location: (39,9)-(39,12))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (39,9)-(39,12) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ CallNode (location: (39,16)-(39,19))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :qux
+ │ │ ├── message_loc: (39,16)-(39,19) = "qux"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (39,12)-(39,13) = "]"
+ │ └── block: ∅
+ ├── @ MultiWriteNode (location: (41,0)-(41,21))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ IndexTargetNode (location: (41,0)-(41,6))
+ │ │ │ ├── flags: attribute_write
+ │ │ │ ├── receiver:
+ │ │ │ │ @ CallNode (location: (41,0)-(41,3))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :foo
+ │ │ │ │ ├── message_loc: (41,0)-(41,3) = "foo"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── opening_loc: (41,3)-(41,4) = "["
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (41,4)-(41,5))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (41,4)-(41,5))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 0
+ │ │ │ ├── closing_loc: (41,5)-(41,6) = "]"
+ │ │ │ └── block: ∅
+ │ │ └── @ IndexTargetNode (location: (41,8)-(41,14))
+ │ │ ├── flags: attribute_write
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (41,8)-(41,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (41,8)-(41,11) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (41,11)-(41,12) = "["
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (41,12)-(41,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (41,12)-(41,13))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 0
+ │ │ ├── closing_loc: (41,13)-(41,14) = "]"
+ │ │ └── block: ∅
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (41,15)-(41,16) = "="
+ │ └── value:
+ │ @ ArrayNode (location: (41,17)-(41,21))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (41,17)-(41,18))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (41,20)-(41,21))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── opening_loc: ∅
+ │ └── closing_loc: ∅
+ ├── @ CallNode (location: (43,0)-(43,19))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (43,0)-(43,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (43,0)-(43,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]
+ │ ├── message_loc: (43,3)-(43,19) = "[bar[baz] = qux]"
+ │ ├── opening_loc: (43,3)-(43,4) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (43,4)-(43,18))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (43,4)-(43,18))
+ │ │ ├── flags: attribute_write
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (43,4)-(43,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (43,4)-(43,7) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :[]=
+ │ │ ├── message_loc: (43,7)-(43,12) = "[baz]"
+ │ │ ├── opening_loc: (43,7)-(43,8) = "["
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (43,8)-(43,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 2)
+ │ │ │ ├── @ CallNode (location: (43,8)-(43,11))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ ├── message_loc: (43,8)-(43,11) = "baz"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── @ CallNode (location: (43,15)-(43,18))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :qux
+ │ │ │ ├── message_loc: (43,15)-(43,18) = "qux"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── closing_loc: (43,11)-(43,12) = "]"
+ │ │ └── block: ∅
+ │ ├── closing_loc: (43,18)-(43,19) = "]"
+ │ └── block: ∅
+ ├── @ CallNode (location: (45,0)-(45,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (45,0)-(45,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (45,0)-(45,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]
+ │ ├── message_loc: (45,3)-(45,8) = "[bar]"
+ │ ├── opening_loc: (45,3)-(45,4) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (45,4)-(45,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (45,4)-(45,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (45,4)-(45,7) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (45,7)-(45,8) = "]"
+ │ └── block: ∅
+ ├── @ CallNode (location: (47,0)-(47,14))
+ │ ├── flags: attribute_write
+ │ ├── receiver:
+ │ │ @ CallNode (location: (47,0)-(47,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (47,0)-(47,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]=
+ │ ├── message_loc: (47,3)-(47,8) = "[bar]"
+ │ ├── opening_loc: (47,3)-(47,4) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (47,4)-(47,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (47,4)-(47,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (47,4)-(47,7) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ CallNode (location: (47,11)-(47,14))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (47,11)-(47,14) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (47,7)-(47,8) = "]"
+ │ └── block: ∅
+ ├── @ ArrayNode (location: (49,0)-(49,6))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (49,1)-(49,5))
+ │ │ ├── flags: ∅
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocSplatNode (location: (49,1)-(49,5))
+ │ │ ├── value:
+ │ │ │ @ HashNode (location: (49,3)-(49,5))
+ │ │ │ ├── opening_loc: (49,3)-(49,4) = "{"
+ │ │ │ ├── elements: (length: 0)
+ │ │ │ └── closing_loc: (49,4)-(49,5) = "}"
+ │ │ └── operator_loc: (49,1)-(49,3) = "**"
+ │ ├── opening_loc: (49,0)-(49,1) = "["
+ │ └── closing_loc: (49,5)-(49,6) = "]"
+ ├── @ ArrayNode (location: (51,0)-(51,6))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (51,1)-(51,5))
+ │ │ ├── flags: ∅
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocSplatNode (location: (51,1)-(51,5))
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (51,3)-(51,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :kw
+ │ │ │ ├── message_loc: (51,3)-(51,5) = "kw"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (51,1)-(51,3) = "**"
+ │ ├── opening_loc: (51,0)-(51,1) = "["
+ │ └── closing_loc: (51,5)-(51,6) = "]"
+ ├── @ ArrayNode (location: (53,0)-(53,9))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (53,1)-(53,2))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ KeywordHashNode (location: (53,4)-(53,8))
+ │ │ ├── flags: ∅
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocSplatNode (location: (53,4)-(53,8))
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (53,6)-(53,8))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :kw
+ │ │ │ ├── message_loc: (53,6)-(53,8) = "kw"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (53,4)-(53,6) = "**"
+ │ ├── opening_loc: (53,0)-(53,1) = "["
+ │ └── closing_loc: (53,8)-(53,9) = "]"
+ ├── @ ArrayNode (location: (55,0)-(55,21))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (55,1)-(55,2))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ KeywordHashNode (location: (55,4)-(55,20))
+ │ │ ├── flags: ∅
+ │ │ └── elements: (length: 3)
+ │ │ ├── @ AssocSplatNode (location: (55,4)-(55,8))
+ │ │ │ ├── value:
+ │ │ │ │ @ CallNode (location: (55,6)-(55,8))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :kw
+ │ │ │ │ ├── message_loc: (55,6)-(55,8) = "kw"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── operator_loc: (55,4)-(55,6) = "**"
+ │ │ ├── @ AssocSplatNode (location: (55,10)-(55,14))
+ │ │ │ ├── value:
+ │ │ │ │ @ HashNode (location: (55,12)-(55,14))
+ │ │ │ │ ├── opening_loc: (55,12)-(55,13) = "{"
+ │ │ │ │ ├── elements: (length: 0)
+ │ │ │ │ └── closing_loc: (55,13)-(55,14) = "}"
+ │ │ │ └── operator_loc: (55,10)-(55,12) = "**"
+ │ │ └── @ AssocSplatNode (location: (55,16)-(55,20))
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (55,18)-(55,20))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :kw
+ │ │ │ ├── message_loc: (55,18)-(55,20) = "kw"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (55,16)-(55,18) = "**"
+ │ ├── opening_loc: (55,0)-(55,1) = "["
+ │ └── closing_loc: (55,20)-(55,21) = "]"
+ ├── @ ArrayNode (location: (57,0)-(59,1))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (58,2)-(58,12))
+ │ │ ├── flags: ∅
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (58,2)-(58,12))
+ │ │ ├── key:
+ │ │ │ @ CallNode (location: (58,2)-(58,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (58,2)-(58,5) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (58,9)-(58,12))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (58,9)-(58,12) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (58,6)-(58,8) = "=>"
+ │ ├── opening_loc: (57,0)-(57,1) = "["
+ │ └── closing_loc: (59,0)-(59,1) = "]"
+ ├── @ ArrayNode (location: (62,0)-(62,17))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 3)
+ │ │ ├── @ SymbolNode (location: (62,3)-(62,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (62,3)-(62,6) = "one"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "one"
+ │ │ ├── @ SymbolNode (location: (62,7)-(62,10))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (62,7)-(62,10) = "two"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "two"
+ │ │ └── @ SymbolNode (location: (62,11)-(62,16))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (62,11)-(62,16) = "three"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "three"
+ │ ├── opening_loc: (62,0)-(62,3) = "%i#"
+ │ └── closing_loc: (62,16)-(62,17) = "#"
+ ├── @ ArrayNode (location: (64,0)-(64,17))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 3)
+ │ │ ├── @ StringNode (location: (64,3)-(64,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (64,3)-(64,6) = "one"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "one"
+ │ │ ├── @ StringNode (location: (64,7)-(64,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (64,7)-(64,10) = "two"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "two"
+ │ │ └── @ StringNode (location: (64,11)-(64,16))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (64,11)-(64,16) = "three"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "three"
+ │ ├── opening_loc: (64,0)-(64,3) = "%w#"
+ │ └── closing_loc: (64,16)-(64,17) = "#"
+ ├── @ XStringNode (location: (66,0)-(66,17))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (66,0)-(66,3) = "%x#"
+ │ ├── content_loc: (66,3)-(66,16) = "one two three"
+ │ ├── closing_loc: (66,16)-(66,17) = "#"
+ │ └── unescaped: "one two three"
+ ├── @ ArrayNode (location: (69,0)-(69,17))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 3)
+ │ │ ├── @ SymbolNode (location: (69,3)-(69,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (69,3)-(69,6) = "one"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "one"
+ │ │ ├── @ SymbolNode (location: (69,7)-(69,10))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (69,7)-(69,10) = "two"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "two"
+ │ │ └── @ SymbolNode (location: (69,11)-(69,16))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (69,11)-(69,16) = "three"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "three"
+ │ ├── opening_loc: (69,0)-(69,3) = "%i@"
+ │ └── closing_loc: (69,16)-(69,17) = "@"
+ ├── @ ArrayNode (location: (71,0)-(71,17))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 3)
+ │ │ ├── @ StringNode (location: (71,3)-(71,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (71,3)-(71,6) = "one"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "one"
+ │ │ ├── @ StringNode (location: (71,7)-(71,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (71,7)-(71,10) = "two"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "two"
+ │ │ └── @ StringNode (location: (71,11)-(71,16))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (71,11)-(71,16) = "three"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "three"
+ │ ├── opening_loc: (71,0)-(71,3) = "%w@"
+ │ └── closing_loc: (71,16)-(71,17) = "@"
+ ├── @ XStringNode (location: (73,0)-(73,17))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (73,0)-(73,3) = "%x@"
+ │ ├── content_loc: (73,3)-(73,16) = "one two three"
+ │ ├── closing_loc: (73,16)-(73,17) = "@"
+ │ └── unescaped: "one two three"
+ ├── @ ArrayNode (location: (76,0)-(76,17))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 3)
+ │ │ ├── @ SymbolNode (location: (76,3)-(76,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (76,3)-(76,6) = "one"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "one"
+ │ │ ├── @ SymbolNode (location: (76,7)-(76,10))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (76,7)-(76,10) = "two"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "two"
+ │ │ └── @ SymbolNode (location: (76,11)-(76,16))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (76,11)-(76,16) = "three"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "three"
+ │ ├── opening_loc: (76,0)-(76,3) = "%i{"
+ │ └── closing_loc: (76,16)-(76,17) = "}"
+ ├── @ ArrayNode (location: (78,0)-(78,17))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 3)
+ │ │ ├── @ StringNode (location: (78,3)-(78,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (78,3)-(78,6) = "one"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "one"
+ │ │ ├── @ StringNode (location: (78,7)-(78,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (78,7)-(78,10) = "two"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "two"
+ │ │ └── @ StringNode (location: (78,11)-(78,16))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (78,11)-(78,16) = "three"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "three"
+ │ ├── opening_loc: (78,0)-(78,3) = "%w{"
+ │ └── closing_loc: (78,16)-(78,17) = "}"
+ ├── @ XStringNode (location: (80,0)-(80,17))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (80,0)-(80,3) = "%x{"
+ │ ├── content_loc: (80,3)-(80,16) = "one two three"
+ │ ├── closing_loc: (80,16)-(80,17) = "}"
+ │ └── unescaped: "one two three"
+ ├── @ ArrayNode (location: (82,0)-(82,7))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ StringNode (location: (82,3)-(82,6))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (82,3)-(82,6) = "\\C:"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\\C:"
+ │ ├── opening_loc: (82,0)-(82,3) = "%w["
+ │ └── closing_loc: (82,6)-(82,7) = "]"
+ ├── @ IndexOperatorWriteNode (location: (84,0)-(84,10))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (84,0)-(84,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (84,0)-(84,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── opening_loc: (84,3)-(84,4) = "["
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (84,4)-(84,5) = "]"
+ │ ├── block: ∅
+ │ ├── operator: :+
+ │ ├── operator_loc: (84,6)-(84,8) = "+="
+ │ └── value:
+ │ @ IntegerNode (location: (84,9)-(84,10))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ IndexOrWriteNode (location: (86,0)-(86,11))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (86,0)-(86,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (86,0)-(86,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── opening_loc: (86,3)-(86,4) = "["
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (86,4)-(86,5) = "]"
+ │ ├── block: ∅
+ │ ├── operator_loc: (86,6)-(86,9) = "||="
+ │ └── value:
+ │ @ IntegerNode (location: (86,10)-(86,11))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ IndexAndWriteNode (location: (88,0)-(88,11))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (88,0)-(88,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (88,0)-(88,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── opening_loc: (88,3)-(88,4) = "["
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (88,4)-(88,5) = "]"
+ │ ├── block: ∅
+ │ ├── operator_loc: (88,6)-(88,9) = "&&="
+ │ └── value:
+ │ @ IntegerNode (location: (88,10)-(88,11))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ IndexOperatorWriteNode (location: (90,0)-(90,14))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (90,0)-(90,7))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (90,0)-(90,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (90,0)-(90,3) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (90,3)-(90,4) = "."
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (90,4)-(90,7) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── opening_loc: (90,7)-(90,8) = "["
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (90,8)-(90,9) = "]"
+ │ ├── block: ∅
+ │ ├── operator: :+
+ │ ├── operator_loc: (90,10)-(90,12) = "+="
+ │ └── value:
+ │ @ IntegerNode (location: (90,13)-(90,14))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ IndexOrWriteNode (location: (92,0)-(92,15))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (92,0)-(92,7))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (92,0)-(92,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (92,0)-(92,3) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (92,3)-(92,4) = "."
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (92,4)-(92,7) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── opening_loc: (92,7)-(92,8) = "["
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (92,8)-(92,9) = "]"
+ │ ├── block: ∅
+ │ ├── operator_loc: (92,10)-(92,13) = "||="
+ │ └── value:
+ │ @ IntegerNode (location: (92,14)-(92,15))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ IndexAndWriteNode (location: (94,0)-(94,15))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (94,0)-(94,7))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (94,0)-(94,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (94,0)-(94,3) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (94,3)-(94,4) = "."
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (94,4)-(94,7) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── opening_loc: (94,7)-(94,8) = "["
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (94,8)-(94,9) = "]"
+ │ ├── block: ∅
+ │ ├── operator_loc: (94,10)-(94,13) = "&&="
+ │ └── value:
+ │ @ IntegerNode (location: (94,14)-(94,15))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ IndexOperatorWriteNode (location: (96,0)-(96,13))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (96,0)-(96,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (96,0)-(96,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── opening_loc: (96,3)-(96,4) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (96,4)-(96,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (96,4)-(96,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (96,4)-(96,7) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (96,7)-(96,8) = "]"
+ │ ├── block: ∅
+ │ ├── operator: :+
+ │ ├── operator_loc: (96,9)-(96,11) = "+="
+ │ └── value:
+ │ @ IntegerNode (location: (96,12)-(96,13))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ IndexOrWriteNode (location: (98,0)-(98,14))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (98,0)-(98,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (98,0)-(98,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── opening_loc: (98,3)-(98,4) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (98,4)-(98,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (98,4)-(98,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (98,4)-(98,7) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (98,7)-(98,8) = "]"
+ │ ├── block: ∅
+ │ ├── operator_loc: (98,9)-(98,12) = "||="
+ │ └── value:
+ │ @ IntegerNode (location: (98,13)-(98,14))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ IndexAndWriteNode (location: (100,0)-(100,14))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (100,0)-(100,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (100,0)-(100,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── opening_loc: (100,3)-(100,4) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (100,4)-(100,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (100,4)-(100,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (100,4)-(100,7) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (100,7)-(100,8) = "]"
+ │ ├── block: ∅
+ │ ├── operator_loc: (100,9)-(100,12) = "&&="
+ │ └── value:
+ │ @ IntegerNode (location: (100,13)-(100,14))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ IndexOperatorWriteNode (location: (102,0)-(102,17))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (102,0)-(102,7))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (102,0)-(102,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (102,0)-(102,3) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (102,3)-(102,4) = "."
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (102,4)-(102,7) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── opening_loc: (102,7)-(102,8) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (102,8)-(102,11))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (102,8)-(102,11))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (102,8)-(102,11) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (102,11)-(102,12) = "]"
+ │ ├── block: ∅
+ │ ├── operator: :+
+ │ ├── operator_loc: (102,13)-(102,15) = "+="
+ │ └── value:
+ │ @ IntegerNode (location: (102,16)-(102,17))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ IndexOrWriteNode (location: (104,0)-(104,18))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (104,0)-(104,7))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (104,0)-(104,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (104,0)-(104,3) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (104,3)-(104,4) = "."
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (104,4)-(104,7) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── opening_loc: (104,7)-(104,8) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (104,8)-(104,11))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (104,8)-(104,11))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (104,8)-(104,11) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (104,11)-(104,12) = "]"
+ │ ├── block: ∅
+ │ ├── operator_loc: (104,13)-(104,16) = "||="
+ │ └── value:
+ │ @ IntegerNode (location: (104,17)-(104,18))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ IndexAndWriteNode (location: (106,0)-(106,18))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (106,0)-(106,7))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (106,0)-(106,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (106,0)-(106,3) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (106,3)-(106,4) = "."
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (106,4)-(106,7) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── opening_loc: (106,7)-(106,8) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (106,8)-(106,11))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (106,8)-(106,11))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (106,8)-(106,11) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (106,11)-(106,12) = "]"
+ │ ├── block: ∅
+ │ ├── operator_loc: (106,13)-(106,16) = "&&="
+ │ └── value:
+ │ @ IntegerNode (location: (106,17)-(106,18))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ DefNode (location: (108,0)-(108,19))
+ │ ├── name: :f
+ │ ├── name_loc: (108,4)-(108,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (108,6)-(108,7))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (108,6)-(108,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: ∅
+ │ │ │ ├── name_loc: ∅
+ │ │ │ └── operator_loc: (108,6)-(108,7) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (108,10)-(108,14))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (108,10)-(108,14))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (108,10)-(108,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (108,10)-(108,11) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :[]
+ │ │ ├── message_loc: (108,11)-(108,14) = "[*]"
+ │ │ ├── opening_loc: (108,11)-(108,12) = "["
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (108,12)-(108,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ SplatNode (location: (108,12)-(108,13))
+ │ │ │ ├── operator_loc: (108,12)-(108,13) = "*"
+ │ │ │ └── expression: ∅
+ │ │ ├── closing_loc: (108,13)-(108,14) = "]"
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (108,0)-(108,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (108,5)-(108,6) = "("
+ │ ├── rparen_loc: (108,7)-(108,8) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (108,16)-(108,19) = "end"
+ ├── @ DefNode (location: (110,0)-(110,22))
+ │ ├── name: :f
+ │ ├── name_loc: (110,4)-(110,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (110,6)-(110,7))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (110,6)-(110,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: ∅
+ │ │ │ ├── name_loc: ∅
+ │ │ │ └── operator_loc: (110,6)-(110,7) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (110,10)-(110,17))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (110,10)-(110,17))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (110,10)-(110,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (110,10)-(110,11) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :[]
+ │ │ ├── message_loc: (110,11)-(110,17) = "[1, *]"
+ │ │ ├── opening_loc: (110,11)-(110,12) = "["
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (110,12)-(110,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 2)
+ │ │ │ ├── @ IntegerNode (location: (110,12)-(110,13))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── @ SplatNode (location: (110,15)-(110,16))
+ │ │ │ ├── operator_loc: (110,15)-(110,16) = "*"
+ │ │ │ └── expression: ∅
+ │ │ ├── closing_loc: (110,16)-(110,17) = "]"
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (110,0)-(110,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (110,5)-(110,6) = "("
+ │ ├── rparen_loc: (110,7)-(110,8) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (110,19)-(110,22) = "end"
+ ├── @ DefNode (location: (112,0)-(112,23))
+ │ ├── name: :f
+ │ ├── name_loc: (112,4)-(112,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (112,6)-(112,7))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (112,6)-(112,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: ∅
+ │ │ │ ├── name_loc: ∅
+ │ │ │ └── operator_loc: (112,6)-(112,7) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (112,10)-(112,18))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (112,10)-(112,18))
+ │ │ ├── flags: attribute_write
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (112,10)-(112,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (112,10)-(112,11) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :[]=
+ │ │ ├── message_loc: (112,11)-(112,14) = "[*]"
+ │ │ ├── opening_loc: (112,11)-(112,12) = "["
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (112,12)-(112,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 2)
+ │ │ │ ├── @ SplatNode (location: (112,12)-(112,13))
+ │ │ │ │ ├── operator_loc: (112,12)-(112,13) = "*"
+ │ │ │ │ └── expression: ∅
+ │ │ │ └── @ IntegerNode (location: (112,17)-(112,18))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: (112,13)-(112,14) = "]"
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (112,0)-(112,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (112,5)-(112,6) = "("
+ │ ├── rparen_loc: (112,7)-(112,8) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (112,20)-(112,23) = "end"
+ ├── @ DefNode (location: (114,0)-(114,26))
+ │ ├── name: :f
+ │ ├── name_loc: (114,4)-(114,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (114,6)-(114,7))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (114,6)-(114,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: ∅
+ │ │ │ ├── name_loc: ∅
+ │ │ │ └── operator_loc: (114,6)-(114,7) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (114,10)-(114,21))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (114,10)-(114,21))
+ │ │ ├── flags: attribute_write
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (114,10)-(114,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (114,10)-(114,11) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :[]=
+ │ │ ├── message_loc: (114,11)-(114,17) = "[1, *]"
+ │ │ ├── opening_loc: (114,11)-(114,12) = "["
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (114,12)-(114,21))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 3)
+ │ │ │ ├── @ IntegerNode (location: (114,12)-(114,13))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── @ SplatNode (location: (114,15)-(114,16))
+ │ │ │ │ ├── operator_loc: (114,15)-(114,16) = "*"
+ │ │ │ │ └── expression: ∅
+ │ │ │ └── @ IntegerNode (location: (114,20)-(114,21))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: (114,16)-(114,17) = "]"
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (114,0)-(114,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (114,5)-(114,6) = "("
+ │ ├── rparen_loc: (114,7)-(114,8) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (114,23)-(114,26) = "end"
+ ├── @ DefNode (location: (116,0)-(116,24))
+ │ ├── name: :f
+ │ ├── name_loc: (116,4)-(116,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (116,6)-(116,7))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (116,6)-(116,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: ∅
+ │ │ │ ├── name_loc: ∅
+ │ │ │ └── operator_loc: (116,6)-(116,7) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (116,10)-(116,19))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IndexOperatorWriteNode (location: (116,10)-(116,19))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (116,10)-(116,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (116,10)-(116,11) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── opening_loc: (116,11)-(116,12) = "["
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (116,12)-(116,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ SplatNode (location: (116,12)-(116,13))
+ │ │ │ ├── operator_loc: (116,12)-(116,13) = "*"
+ │ │ │ └── expression: ∅
+ │ │ ├── closing_loc: (116,13)-(116,14) = "]"
+ │ │ ├── block: ∅
+ │ │ ├── operator: :+
+ │ │ ├── operator_loc: (116,15)-(116,17) = "+="
+ │ │ └── value:
+ │ │ @ IntegerNode (location: (116,18)-(116,19))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (116,0)-(116,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (116,5)-(116,6) = "("
+ │ ├── rparen_loc: (116,7)-(116,8) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (116,21)-(116,24) = "end"
+ ├── @ DefNode (location: (118,0)-(118,28))
+ │ ├── name: :f
+ │ ├── name_loc: (118,4)-(118,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (118,6)-(118,7))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (118,6)-(118,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: ∅
+ │ │ │ ├── name_loc: ∅
+ │ │ │ └── operator_loc: (118,6)-(118,7) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (118,10)-(118,23))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IndexAndWriteNode (location: (118,10)-(118,23))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (118,10)-(118,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (118,10)-(118,11) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── opening_loc: (118,11)-(118,12) = "["
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (118,12)-(118,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 2)
+ │ │ │ ├── @ IntegerNode (location: (118,12)-(118,13))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── @ SplatNode (location: (118,15)-(118,16))
+ │ │ │ ├── operator_loc: (118,15)-(118,16) = "*"
+ │ │ │ └── expression: ∅
+ │ │ ├── closing_loc: (118,16)-(118,17) = "]"
+ │ │ ├── block: ∅
+ │ │ ├── operator_loc: (118,18)-(118,21) = "&&="
+ │ │ └── value:
+ │ │ @ IntegerNode (location: (118,22)-(118,23))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (118,0)-(118,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (118,5)-(118,6) = "("
+ │ ├── rparen_loc: (118,7)-(118,8) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (118,25)-(118,28) = "end"
+ ├── @ DefNode (location: (120,0)-(120,29))
+ │ ├── name: :f
+ │ ├── name_loc: (120,4)-(120,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (120,6)-(120,7))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (120,6)-(120,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: ∅
+ │ │ │ ├── name_loc: ∅
+ │ │ │ └── operator_loc: (120,6)-(120,7) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (120,0)-(120,29))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── rescue_clause:
+ │ │ │ @ RescueNode (location: (120,10)-(120,24))
+ │ │ │ ├── keyword_loc: (120,10)-(120,16) = "rescue"
+ │ │ │ ├── exceptions: (length: 0)
+ │ │ │ ├── operator_loc: (120,17)-(120,19) = "=>"
+ │ │ │ ├── reference:
+ │ │ │ │ @ IndexTargetNode (location: (120,20)-(120,24))
+ │ │ │ │ ├── flags: attribute_write
+ │ │ │ │ ├── receiver:
+ │ │ │ │ │ @ CallNode (location: (120,20)-(120,21))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :a
+ │ │ │ │ │ ├── message_loc: (120,20)-(120,21) = "a"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── opening_loc: (120,21)-(120,22) = "["
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (120,22)-(120,23))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ SplatNode (location: (120,22)-(120,23))
+ │ │ │ │ │ ├── operator_loc: (120,22)-(120,23) = "*"
+ │ │ │ │ │ └── expression: ∅
+ │ │ │ │ ├── closing_loc: (120,23)-(120,24) = "]"
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── statements: ∅
+ │ │ │ └── consequent: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause: ∅
+ │ │ └── end_keyword_loc: (120,26)-(120,29) = "end"
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (120,0)-(120,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (120,5)-(120,6) = "("
+ │ ├── rparen_loc: (120,7)-(120,8) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (120,26)-(120,29) = "end"
+ └── @ DefNode (location: (122,0)-(122,32))
+ ├── name: :f
+ ├── name_loc: (122,4)-(122,5) = "f"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (122,6)-(122,7))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest:
+ │ │ @ RestParameterNode (location: (122,6)-(122,7))
+ │ │ ├── flags: ∅
+ │ │ ├── name: ∅
+ │ │ ├── name_loc: ∅
+ │ │ └── operator_loc: (122,6)-(122,7) = "*"
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body:
+ │ @ BeginNode (location: (122,0)-(122,32))
+ │ ├── begin_keyword_loc: ∅
+ │ ├── statements: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (122,10)-(122,27))
+ │ │ ├── keyword_loc: (122,10)-(122,16) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: (122,17)-(122,19) = "=>"
+ │ │ ├── reference:
+ │ │ │ @ IndexTargetNode (location: (122,20)-(122,27))
+ │ │ │ ├── flags: attribute_write
+ │ │ │ ├── receiver:
+ │ │ │ │ @ CallNode (location: (122,20)-(122,21))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── message_loc: (122,20)-(122,21) = "a"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── opening_loc: (122,21)-(122,22) = "["
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (122,22)-(122,26))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 2)
+ │ │ │ │ ├── @ IntegerNode (location: (122,22)-(122,23))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ └── @ SplatNode (location: (122,25)-(122,26))
+ │ │ │ │ ├── operator_loc: (122,25)-(122,26) = "*"
+ │ │ │ │ └── expression: ∅
+ │ │ │ ├── closing_loc: (122,26)-(122,27) = "]"
+ │ │ │ └── block: ∅
+ │ │ ├── statements: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (122,29)-(122,32) = "end"
+ ├── locals: []
+ ├── def_keyword_loc: (122,0)-(122,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (122,5)-(122,6) = "("
+ ├── rparen_loc: (122,7)-(122,8) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (122,29)-(122,32) = "end"
diff --git a/test/prism/snapshots/begin_ensure.txt b/test/prism/snapshots/begin_ensure.txt
new file mode 100644
index 0000000000..9af9b9e573
--- /dev/null
+++ b/test/prism/snapshots/begin_ensure.txt
@@ -0,0 +1,251 @@
+@ ProgramNode (location: (1,0)-(21,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(21,15))
+ └── body: (length: 5)
+ ├── @ BeginNode (location: (1,0)-(5,3))
+ │ ├── begin_keyword_loc: (1,0)-(1,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (2,0)-(2,1))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (2,0)-(2,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (2,0)-(2,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause:
+ │ │ @ EnsureNode (location: (3,0)-(5,3))
+ │ │ ├── ensure_keyword_loc: (3,0)-(3,6) = "ensure"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (4,0)-(4,1))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (4,0)-(4,1))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (4,0)-(4,1) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── end_keyword_loc: (5,0)-(5,3) = "end"
+ │ └── end_keyword_loc: (5,0)-(5,3) = "end"
+ ├── @ BeginNode (location: (7,0)-(7,24))
+ │ ├── begin_keyword_loc: (7,0)-(7,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (7,7)-(7,8))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (7,7)-(7,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (7,7)-(7,8) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause:
+ │ │ @ EnsureNode (location: (7,10)-(7,24))
+ │ │ ├── ensure_keyword_loc: (7,10)-(7,16) = "ensure"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (7,18)-(7,19))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (7,18)-(7,19))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (7,18)-(7,19) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── end_keyword_loc: (7,21)-(7,24) = "end"
+ │ └── end_keyword_loc: (7,21)-(7,24) = "end"
+ ├── @ BeginNode (location: (9,0)-(11,4))
+ │ ├── begin_keyword_loc: (9,0)-(9,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (9,6)-(9,7))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (9,6)-(9,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (9,6)-(9,7) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause:
+ │ │ @ EnsureNode (location: (10,1)-(11,4))
+ │ │ ├── ensure_keyword_loc: (10,1)-(10,7) = "ensure"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (10,8)-(10,9))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (10,8)-(10,9))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (10,8)-(10,9) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── end_keyword_loc: (11,1)-(11,4) = "end"
+ │ └── end_keyword_loc: (11,1)-(11,4) = "end"
+ ├── @ BeginNode (location: (13,0)-(13,22))
+ │ ├── begin_keyword_loc: (13,0)-(13,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (13,6)-(13,7))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (13,6)-(13,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (13,6)-(13,7) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause:
+ │ │ @ EnsureNode (location: (13,9)-(13,22))
+ │ │ ├── ensure_keyword_loc: (13,9)-(13,15) = "ensure"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (13,16)-(13,17))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (13,16)-(13,17))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (13,16)-(13,17) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── end_keyword_loc: (13,19)-(13,22) = "end"
+ │ └── end_keyword_loc: (13,19)-(13,22) = "end"
+ └── @ BeginNode (location: (15,0)-(21,15))
+ ├── begin_keyword_loc: (15,0)-(15,5) = "begin"
+ ├── statements:
+ │ @ StatementsNode (location: (15,6)-(21,11))
+ │ └── body: (length: 1)
+ │ └── @ BeginNode (location: (15,6)-(21,11))
+ │ ├── begin_keyword_loc: (15,6)-(15,11) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (15,11)-(21,7))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (15,11)-(21,7))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ SymbolNode (location: (15,11)-(15,13))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (15,11)-(15,12) = ":"
+ │ │ │ ├── value_loc: (15,12)-(15,13) = "s"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "s"
+ │ │ ├── call_operator_loc: (15,13)-(15,14) = "."
+ │ │ ├── name: :l
+ │ │ ├── message_loc: (15,14)-(15,15) = "l"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (15,16)-(21,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ BeginNode (location: (15,16)-(21,7))
+ │ │ │ ├── begin_keyword_loc: (15,16)-(15,21) = "begin"
+ │ │ │ ├── statements: ∅
+ │ │ │ ├── rescue_clause: ∅
+ │ │ │ ├── else_clause: ∅
+ │ │ │ ├── ensure_clause:
+ │ │ │ │ @ EnsureNode (location: (15,22)-(21,7))
+ │ │ │ │ ├── ensure_keyword_loc: (15,22)-(15,28) = "ensure"
+ │ │ │ │ ├── statements:
+ │ │ │ │ │ @ StatementsNode (location: (15,29)-(21,3))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (15,29)-(21,3))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── receiver:
+ │ │ │ │ │ │ @ ConstantReadNode (location: (15,29)-(15,35))
+ │ │ │ │ │ │ └── name: :Module
+ │ │ │ │ │ ├── call_operator_loc: (15,35)-(15,36) = "."
+ │ │ │ │ │ ├── name: :new
+ │ │ │ │ │ ├── message_loc: (15,36)-(15,39) = "new"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block:
+ │ │ │ │ │ @ BlockNode (location: (15,40)-(21,3))
+ │ │ │ │ │ ├── locals: []
+ │ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ │ ├── body:
+ │ │ │ │ │ │ @ StatementsNode (location: (16,2)-(20,5))
+ │ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ │ └── @ BeginNode (location: (16,2)-(20,5))
+ │ │ │ │ │ │ ├── begin_keyword_loc: (16,2)-(16,7) = "begin"
+ │ │ │ │ │ │ ├── statements:
+ │ │ │ │ │ │ │ @ StatementsNode (location: (17,4)-(17,9))
+ │ │ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ │ │ └── @ BreakNode (location: (17,4)-(17,9))
+ │ │ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ │ │ └── keyword_loc: (17,4)-(17,9) = "break"
+ │ │ │ │ │ │ ├── rescue_clause: ∅
+ │ │ │ │ │ │ ├── else_clause: ∅
+ │ │ │ │ │ │ ├── ensure_clause:
+ │ │ │ │ │ │ │ @ EnsureNode (location: (18,4)-(20,5))
+ │ │ │ │ │ │ │ ├── ensure_keyword_loc: (18,4)-(18,10) = "ensure"
+ │ │ │ │ │ │ │ ├── statements:
+ │ │ │ │ │ │ │ │ @ StatementsNode (location: (18,11)-(19,7))
+ │ │ │ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ │ │ │ └── @ CallNode (location: (18,11)-(19,7))
+ │ │ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ │ │ ├── receiver:
+ │ │ │ │ │ │ │ │ │ @ ConstantReadNode (location: (18,11)-(18,17))
+ │ │ │ │ │ │ │ │ │ └── name: :Module
+ │ │ │ │ │ │ │ │ ├── call_operator_loc: (18,17)-(18,18) = "."
+ │ │ │ │ │ │ │ │ ├── name: :new
+ │ │ │ │ │ │ │ │ ├── message_loc: (18,18)-(18,21) = "new"
+ │ │ │ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ │ │ │ └── block:
+ │ │ │ │ │ │ │ │ @ BlockNode (location: (18,22)-(19,7))
+ │ │ │ │ │ │ │ │ ├── locals: []
+ │ │ │ │ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ │ │ │ │ ├── body: ∅
+ │ │ │ │ │ │ │ │ ├── opening_loc: (18,22)-(18,24) = "do"
+ │ │ │ │ │ │ │ │ └── closing_loc: (19,4)-(19,7) = "end"
+ │ │ │ │ │ │ │ └── end_keyword_loc: (20,2)-(20,5) = "end"
+ │ │ │ │ │ │ └── end_keyword_loc: (20,2)-(20,5) = "end"
+ │ │ │ │ │ ├── opening_loc: (15,40)-(15,42) = "do"
+ │ │ │ │ │ └── closing_loc: (21,0)-(21,3) = "end"
+ │ │ │ │ └── end_keyword_loc: (21,4)-(21,7) = "end"
+ │ │ │ └── end_keyword_loc: (21,4)-(21,7) = "end"
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (21,8)-(21,11) = "end"
+ ├── rescue_clause: ∅
+ ├── else_clause: ∅
+ ├── ensure_clause: ∅
+ └── end_keyword_loc: (21,12)-(21,15) = "end"
diff --git a/test/prism/snapshots/begin_rescue.txt b/test/prism/snapshots/begin_rescue.txt
new file mode 100644
index 0000000000..f624f85c07
--- /dev/null
+++ b/test/prism/snapshots/begin_rescue.txt
@@ -0,0 +1,699 @@
+@ ProgramNode (location: (1,0)-(78,3))
+├── locals: [:ex]
+└── statements:
+ @ StatementsNode (location: (1,0)-(78,3))
+ └── body: (length: 17)
+ ├── @ BeginNode (location: (1,0)-(1,33))
+ │ ├── begin_keyword_loc: (1,0)-(1,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,7)-(1,8))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,7)-(1,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (1,7)-(1,8) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (1,10)-(1,19))
+ │ │ ├── keyword_loc: (1,10)-(1,16) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,18)-(1,19))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (1,18)-(1,19))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (1,18)-(1,19) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause:
+ │ │ @ ElseNode (location: (1,21)-(1,33))
+ │ │ ├── else_keyword_loc: (1,21)-(1,25) = "else"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,27)-(1,28))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (1,27)-(1,28))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── message_loc: (1,27)-(1,28) = "c"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── end_keyword_loc: (1,30)-(1,33) = "end"
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (1,30)-(1,33) = "end"
+ ├── @ BeginNode (location: (3,0)-(3,44))
+ │ ├── begin_keyword_loc: (3,0)-(3,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,7)-(3,8))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (3,7)-(3,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (3,7)-(3,8) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (3,10)-(3,19))
+ │ │ ├── keyword_loc: (3,10)-(3,16) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (3,18)-(3,19))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (3,18)-(3,19))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (3,18)-(3,19) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause:
+ │ │ @ ElseNode (location: (3,21)-(3,36))
+ │ │ ├── else_keyword_loc: (3,21)-(3,25) = "else"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (3,27)-(3,28))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (3,27)-(3,28))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── message_loc: (3,27)-(3,28) = "c"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── end_keyword_loc: (3,30)-(3,36) = "ensure"
+ │ ├── ensure_clause:
+ │ │ @ EnsureNode (location: (3,30)-(3,44))
+ │ │ ├── ensure_keyword_loc: (3,30)-(3,36) = "ensure"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (3,38)-(3,39))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (3,38)-(3,39))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :d
+ │ │ │ ├── message_loc: (3,38)-(3,39) = "d"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── end_keyword_loc: (3,41)-(3,44) = "end"
+ │ └── end_keyword_loc: (3,41)-(3,44) = "end"
+ ├── @ BeginNode (location: (5,0)-(7,3))
+ │ ├── begin_keyword_loc: (5,0)-(5,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (6,0)-(6,1))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (6,0)-(6,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (6,0)-(6,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (7,0)-(7,3) = "end"
+ ├── @ BeginNode (location: (9,0)-(9,13))
+ │ ├── begin_keyword_loc: (9,0)-(9,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (9,7)-(9,8))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (9,7)-(9,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (9,7)-(9,8) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (9,10)-(9,13) = "end"
+ ├── @ BeginNode (location: (11,0)-(12,4))
+ │ ├── begin_keyword_loc: (11,0)-(11,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (11,6)-(11,7))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (11,6)-(11,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (11,6)-(11,7) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (12,1)-(12,4) = "end"
+ ├── @ BeginNode (location: (14,0)-(14,12))
+ │ ├── begin_keyword_loc: (14,0)-(14,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (14,6)-(14,7))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (14,6)-(14,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (14,6)-(14,7) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (14,9)-(14,12) = "end"
+ ├── @ BeginNode (location: (16,0)-(24,3))
+ │ ├── begin_keyword_loc: (16,0)-(16,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (17,0)-(17,1))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (17,0)-(17,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (17,0)-(17,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (18,0)-(23,1))
+ │ │ ├── keyword_loc: (18,0)-(18,6) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (19,0)-(19,1))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (19,0)-(19,1))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (19,0)-(19,1) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent:
+ │ │ @ RescueNode (location: (20,0)-(23,1))
+ │ │ ├── keyword_loc: (20,0)-(20,6) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (21,0)-(21,1))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (21,0)-(21,1))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── message_loc: (21,0)-(21,1) = "c"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent:
+ │ │ @ RescueNode (location: (22,0)-(23,1))
+ │ │ ├── keyword_loc: (22,0)-(22,6) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (23,0)-(23,1))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (23,0)-(23,1))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :d
+ │ │ │ ├── message_loc: (23,0)-(23,1) = "d"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (24,0)-(24,3) = "end"
+ ├── @ BeginNode (location: (26,0)-(32,3))
+ │ ├── begin_keyword_loc: (26,0)-(26,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (27,2)-(27,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (27,2)-(27,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (27,2)-(27,3) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (28,0)-(31,3))
+ │ │ ├── keyword_loc: (28,0)-(28,6) = "rescue"
+ │ │ ├── exceptions: (length: 1)
+ │ │ │ └── @ ConstantReadNode (location: (28,7)-(28,16))
+ │ │ │ └── name: :Exception
+ │ │ ├── operator_loc: (28,17)-(28,19) = "=>"
+ │ │ ├── reference:
+ │ │ │ @ LocalVariableTargetNode (location: (28,20)-(28,22))
+ │ │ │ ├── name: :ex
+ │ │ │ └── depth: 0
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (29,2)-(29,3))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (29,2)-(29,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (29,2)-(29,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent:
+ │ │ @ RescueNode (location: (30,0)-(31,3))
+ │ │ ├── keyword_loc: (30,0)-(30,6) = "rescue"
+ │ │ ├── exceptions: (length: 2)
+ │ │ │ ├── @ ConstantReadNode (location: (30,7)-(30,23))
+ │ │ │ │ └── name: :AnotherException
+ │ │ │ └── @ ConstantReadNode (location: (30,25)-(30,41))
+ │ │ │ └── name: :OneMoreException
+ │ │ ├── operator_loc: (30,42)-(30,44) = "=>"
+ │ │ ├── reference:
+ │ │ │ @ LocalVariableTargetNode (location: (30,45)-(30,47))
+ │ │ │ ├── name: :ex
+ │ │ │ └── depth: 0
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (31,2)-(31,3))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (31,2)-(31,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── message_loc: (31,2)-(31,3) = "c"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (32,0)-(32,3) = "end"
+ ├── @ BeginNode (location: (34,0)-(40,3))
+ │ ├── begin_keyword_loc: (34,0)-(34,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (35,2)-(35,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (35,2)-(35,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (35,2)-(35,3) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (36,0)-(37,3))
+ │ │ ├── keyword_loc: (36,0)-(36,6) = "rescue"
+ │ │ ├── exceptions: (length: 1)
+ │ │ │ └── @ ConstantReadNode (location: (36,7)-(36,16))
+ │ │ │ └── name: :Exception
+ │ │ ├── operator_loc: (36,17)-(36,19) = "=>"
+ │ │ ├── reference:
+ │ │ │ @ LocalVariableTargetNode (location: (36,20)-(36,22))
+ │ │ │ ├── name: :ex
+ │ │ │ └── depth: 0
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (37,2)-(37,3))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (37,2)-(37,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (37,2)-(37,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause:
+ │ │ @ EnsureNode (location: (38,0)-(40,3))
+ │ │ ├── ensure_keyword_loc: (38,0)-(38,6) = "ensure"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (39,2)-(39,3))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (39,2)-(39,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (39,2)-(39,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── end_keyword_loc: (40,0)-(40,3) = "end"
+ │ └── end_keyword_loc: (40,0)-(40,3) = "end"
+ ├── @ StringNode (location: (42,0)-(42,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (42,0)-(42,2) = "%!"
+ │ ├── content_loc: (42,2)-(42,5) = "abc"
+ │ ├── closing_loc: (42,5)-(42,6) = "!"
+ │ └── unescaped: "abc"
+ ├── @ BeginNode (location: (44,0)-(48,3))
+ │ ├── begin_keyword_loc: (44,0)-(44,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (45,0)-(45,1))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (45,0)-(45,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (45,0)-(45,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (46,0)-(47,1))
+ │ │ ├── keyword_loc: (46,0)-(46,6) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (47,0)-(47,1))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (47,0)-(47,1))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (47,0)-(47,1) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (48,0)-(48,3) = "end"
+ ├── @ BeginNode (location: (50,0)-(50,20))
+ │ ├── begin_keyword_loc: (50,0)-(50,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (50,6)-(50,7))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (50,6)-(50,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (50,6)-(50,7) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (50,8)-(50,16))
+ │ │ ├── keyword_loc: (50,8)-(50,14) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (50,15)-(50,16))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (50,15)-(50,16))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (50,15)-(50,16) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (50,17)-(50,20) = "end"
+ ├── @ BeginNode (location: (52,0)-(54,5))
+ │ ├── begin_keyword_loc: (52,0)-(52,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (53,0)-(53,1))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (53,0)-(53,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (53,0)-(53,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (53,2)-(54,1))
+ │ │ ├── keyword_loc: (53,2)-(53,8) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (54,0)-(54,1))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (54,0)-(54,1))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (54,0)-(54,1) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (54,2)-(54,5) = "end"
+ ├── @ BeginNode (location: (56,0)-(60,3))
+ │ ├── begin_keyword_loc: (56,0)-(56,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (57,0)-(57,1))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (57,0)-(57,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (57,0)-(57,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (58,0)-(59,1))
+ │ │ ├── keyword_loc: (58,0)-(58,6) = "rescue"
+ │ │ ├── exceptions: (length: 1)
+ │ │ │ └── @ ConstantReadNode (location: (58,7)-(58,16))
+ │ │ │ └── name: :Exception
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (59,0)-(59,1))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (59,0)-(59,1))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (59,0)-(59,1) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (60,0)-(60,3) = "end"
+ ├── @ BeginNode (location: (62,0)-(66,3))
+ │ ├── begin_keyword_loc: (62,0)-(62,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (63,0)-(63,1))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (63,0)-(63,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (63,0)-(63,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (64,0)-(65,1))
+ │ │ ├── keyword_loc: (64,0)-(64,6) = "rescue"
+ │ │ ├── exceptions: (length: 2)
+ │ │ │ ├── @ ConstantReadNode (location: (64,7)-(64,16))
+ │ │ │ │ └── name: :Exception
+ │ │ │ └── @ ConstantReadNode (location: (64,18)-(64,33))
+ │ │ │ └── name: :CustomException
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (65,0)-(65,1))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (65,0)-(65,1))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (65,0)-(65,1) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (66,0)-(66,3) = "end"
+ ├── @ BeginNode (location: (68,0)-(72,3))
+ │ ├── begin_keyword_loc: (68,0)-(68,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (69,2)-(69,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (69,2)-(69,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (69,2)-(69,3) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (70,0)-(71,3))
+ │ │ ├── keyword_loc: (70,0)-(70,6) = "rescue"
+ │ │ ├── exceptions: (length: 2)
+ │ │ │ ├── @ ConstantReadNode (location: (70,7)-(70,16))
+ │ │ │ │ └── name: :Exception
+ │ │ │ └── @ ConstantReadNode (location: (70,18)-(70,33))
+ │ │ │ └── name: :CustomException
+ │ │ ├── operator_loc: (70,34)-(70,36) = "=>"
+ │ │ ├── reference:
+ │ │ │ @ LocalVariableTargetNode (location: (70,37)-(70,39))
+ │ │ │ ├── name: :ex
+ │ │ │ └── depth: 0
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (71,2)-(71,3))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (71,2)-(71,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (71,2)-(71,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (72,0)-(72,3) = "end"
+ └── @ BeginNode (location: (74,0)-(78,3))
+ ├── begin_keyword_loc: (74,0)-(74,5) = "begin"
+ ├── statements:
+ │ @ StatementsNode (location: (75,2)-(75,3))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (75,2)-(75,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (75,2)-(75,3) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── rescue_clause:
+ │ @ RescueNode (location: (76,0)-(77,3))
+ │ ├── keyword_loc: (76,0)-(76,6) = "rescue"
+ │ ├── exceptions: (length: 1)
+ │ │ └── @ ConstantReadNode (location: (76,7)-(76,16))
+ │ │ └── name: :Exception
+ │ ├── operator_loc: (76,17)-(76,19) = "=>"
+ │ ├── reference:
+ │ │ @ LocalVariableTargetNode (location: (76,20)-(76,22))
+ │ │ ├── name: :ex
+ │ │ └── depth: 0
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (77,2)-(77,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (77,2)-(77,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (77,2)-(77,3) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── consequent: ∅
+ ├── else_clause: ∅
+ ├── ensure_clause: ∅
+ └── end_keyword_loc: (78,0)-(78,3) = "end"
diff --git a/test/prism/snapshots/blocks.txt b/test/prism/snapshots/blocks.txt
new file mode 100644
index 0000000000..0b1ec52e38
--- /dev/null
+++ b/test/prism/snapshots/blocks.txt
@@ -0,0 +1,774 @@
+@ ProgramNode (location: (1,0)-(54,17))
+├── locals: [:fork]
+└── statements:
+ @ StatementsNode (location: (1,0)-(54,17))
+ └── body: (length: 20)
+ ├── @ CallNode (location: (1,0)-(1,16))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,0)-(1,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]
+ │ ├── message_loc: (1,3)-(1,8) = "[bar]"
+ │ ├── opening_loc: (1,3)-(1,4) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,4)-(1,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (1,4)-(1,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,4)-(1,7) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (1,7)-(1,8) = "]"
+ │ └── block:
+ │ @ BlockNode (location: (1,9)-(1,16))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,11)-(1,14))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,11)-(1,14))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (1,11)-(1,14) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (1,9)-(1,10) = "{"
+ │ └── closing_loc: (1,15)-(1,16) = "}"
+ ├── @ CallNode (location: (3,0)-(5,3))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (3,0)-(3,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (3,0)-(3,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]
+ │ ├── message_loc: (3,3)-(3,8) = "[bar]"
+ │ ├── opening_loc: (3,3)-(3,4) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,4)-(3,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (3,4)-(3,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (3,4)-(3,7) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (3,7)-(3,8) = "]"
+ │ └── block:
+ │ @ BlockNode (location: (3,9)-(5,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (4,0)-(4,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (4,0)-(4,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (4,0)-(4,3) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (3,9)-(3,11) = "do"
+ │ └── closing_loc: (5,0)-(5,3) = "end"
+ ├── @ CallNode (location: (7,0)-(7,35))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (7,0)-(7,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :x
+ │ │ ├── message_loc: (7,0)-(7,1) = "x"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (7,1)-(7,2) = "."
+ │ ├── name: :reduce
+ │ ├── message_loc: (7,2)-(7,8) = "reduce"
+ │ ├── opening_loc: (7,8)-(7,9) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (7,9)-(7,10))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (7,9)-(7,10))
+ │ │ ├── flags: decimal
+ │ │ └── value: 0
+ │ ├── closing_loc: (7,10)-(7,11) = ")"
+ │ └── block:
+ │ @ BlockNode (location: (7,12)-(7,35))
+ │ ├── locals: [:x, :memo]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (7,14)-(7,23))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (7,15)-(7,22))
+ │ │ │ ├── requireds: (length: 2)
+ │ │ │ │ ├── @ RequiredParameterNode (location: (7,15)-(7,16))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :x
+ │ │ │ │ └── @ RequiredParameterNode (location: (7,18)-(7,22))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :memo
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (7,14)-(7,15) = "|"
+ │ │ └── closing_loc: (7,22)-(7,23) = "|"
+ │ ├── body:
+ │ │ @ StatementsNode (location: (7,24)-(7,33))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableOperatorWriteNode (location: (7,24)-(7,33))
+ │ │ ├── name_loc: (7,24)-(7,28) = "memo"
+ │ │ ├── operator_loc: (7,29)-(7,31) = "+="
+ │ │ ├── value:
+ │ │ │ @ LocalVariableReadNode (location: (7,32)-(7,33))
+ │ │ │ ├── name: :x
+ │ │ │ └── depth: 0
+ │ │ ├── name: :memo
+ │ │ ├── operator: :+
+ │ │ └── depth: 0
+ │ ├── opening_loc: (7,12)-(7,13) = "{"
+ │ └── closing_loc: (7,34)-(7,35) = "}"
+ ├── @ CallNode (location: (9,0)-(9,10))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (9,0)-(9,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (9,4)-(9,10))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (9,4)-(9,6) = "do"
+ │ └── closing_loc: (9,7)-(9,10) = "end"
+ ├── @ CallNode (location: (11,0)-(11,21))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (11,0)-(11,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (11,4)-(11,21))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (11,4)-(11,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (11,4)-(11,7) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ ParenthesesNode (location: (11,9)-(11,21))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (11,10)-(11,20))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (11,10)-(11,20))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (11,10)-(11,13) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (11,14)-(11,20))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body: ∅
+ │ │ │ ├── opening_loc: (11,14)-(11,16) = "do"
+ │ │ │ └── closing_loc: (11,17)-(11,20) = "end"
+ │ │ ├── opening_loc: (11,9)-(11,10) = "("
+ │ │ └── closing_loc: (11,20)-(11,21) = ")"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (13,0)-(13,14))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (13,0)-(13,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (13,4)-(13,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (13,4)-(13,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (13,4)-(13,7) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (13,8)-(13,14))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (13,8)-(13,10) = "do"
+ │ └── closing_loc: (13,11)-(13,14) = "end"
+ ├── @ CallNode (location: (15,0)-(15,18))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (15,0)-(15,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (15,4)-(15,11))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (15,4)-(15,11))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (15,4)-(15,7) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (15,8)-(15,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ CallNode (location: (15,8)-(15,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (15,8)-(15,11) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (15,12)-(15,18))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (15,12)-(15,14) = "do"
+ │ └── closing_loc: (15,15)-(15,18) = "end"
+ ├── @ CallNode (location: (17,0)-(18,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (17,0)-(17,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (17,4)-(18,3))
+ │ ├── locals: [:a]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (17,7)-(17,17))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (17,8)-(17,16))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 1)
+ │ │ │ │ └── @ OptionalParameterNode (location: (17,8)-(17,16))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── name_loc: (17,8)-(17,9) = "a"
+ │ │ │ │ ├── operator_loc: (17,10)-(17,11) = "="
+ │ │ │ │ └── value:
+ │ │ │ │ @ CallNode (location: (17,12)-(17,16))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── receiver:
+ │ │ │ │ │ @ CallNode (location: (17,12)-(17,13))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :b
+ │ │ │ │ │ ├── message_loc: (17,12)-(17,13) = "b"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :[]
+ │ │ │ │ ├── message_loc: (17,13)-(17,16) = "[1]"
+ │ │ │ │ ├── opening_loc: (17,13)-(17,14) = "["
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (17,14)-(17,15))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ IntegerNode (location: (17,14)-(17,15))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ ├── closing_loc: (17,15)-(17,16) = "]"
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (17,7)-(17,8) = "|"
+ │ │ └── closing_loc: (17,16)-(17,17) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (17,4)-(17,6) = "do"
+ │ └── closing_loc: (18,0)-(18,3) = "end"
+ ├── @ CallNode (location: (20,0)-(22,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (20,0)-(20,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (20,4)-(22,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (20,4)-(22,3))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── rescue_clause:
+ │ │ │ @ RescueNode (location: (21,0)-(21,6))
+ │ │ │ ├── keyword_loc: (21,0)-(21,6) = "rescue"
+ │ │ │ ├── exceptions: (length: 0)
+ │ │ │ ├── operator_loc: ∅
+ │ │ │ ├── reference: ∅
+ │ │ │ ├── statements: ∅
+ │ │ │ └── consequent: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause: ∅
+ │ │ └── end_keyword_loc: (22,0)-(22,3) = "end"
+ │ ├── opening_loc: (20,4)-(20,6) = "do"
+ │ └── closing_loc: (22,0)-(22,3) = "end"
+ ├── @ CallNode (location: (24,0)-(29,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (24,0)-(24,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (24,4)-(29,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (25,2)-(28,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (25,2)-(28,5))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (25,2)-(25,5) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (25,6)-(28,5))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (26,4)-(27,7))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (26,4)-(27,7))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (26,4)-(26,7) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (26,8)-(27,7))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body: ∅
+ │ │ │ ├── opening_loc: (26,8)-(26,10) = "do"
+ │ │ │ └── closing_loc: (27,4)-(27,7) = "end"
+ │ │ ├── opening_loc: (25,6)-(25,8) = "do"
+ │ │ └── closing_loc: (28,2)-(28,5) = "end"
+ │ ├── opening_loc: (24,4)-(24,6) = "do"
+ │ └── closing_loc: (29,0)-(29,3) = "end"
+ ├── @ CallNode (location: (31,0)-(31,16))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (31,0)-(31,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (31,0)-(31,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]
+ │ ├── message_loc: (31,3)-(31,8) = "[bar]"
+ │ ├── opening_loc: (31,3)-(31,4) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (31,4)-(31,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (31,4)-(31,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (31,4)-(31,7) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (31,7)-(31,8) = "]"
+ │ └── block:
+ │ @ BlockNode (location: (31,9)-(31,16))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (31,11)-(31,14))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (31,11)-(31,14))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (31,11)-(31,14) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (31,9)-(31,10) = "{"
+ │ └── closing_loc: (31,15)-(31,16) = "}"
+ ├── @ CallNode (location: (33,0)-(33,24))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (33,0)-(33,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (33,4)-(33,24))
+ │ ├── locals: [:x, :y, :z]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (33,6)-(33,20))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (33,7)-(33,19))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (33,7)-(33,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :x
+ │ │ │ ├── optionals: (length: 1)
+ │ │ │ │ └── @ OptionalParameterNode (location: (33,10)-(33,15))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :y
+ │ │ │ │ ├── name_loc: (33,10)-(33,11) = "y"
+ │ │ │ │ ├── operator_loc: (33,12)-(33,13) = "="
+ │ │ │ │ └── value:
+ │ │ │ │ @ IntegerNode (location: (33,14)-(33,15))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 1)
+ │ │ │ │ └── @ RequiredKeywordParameterNode (location: (33,17)-(33,19))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :z
+ │ │ │ │ └── name_loc: (33,17)-(33,19) = "z:"
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (33,6)-(33,7) = "|"
+ │ │ └── closing_loc: (33,19)-(33,20) = "|"
+ │ ├── body:
+ │ │ @ StatementsNode (location: (33,21)-(33,22))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (33,21)-(33,22))
+ │ │ ├── name: :x
+ │ │ └── depth: 0
+ │ ├── opening_loc: (33,4)-(33,5) = "{"
+ │ └── closing_loc: (33,23)-(33,24) = "}"
+ ├── @ CallNode (location: (35,0)-(35,11))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (35,0)-(35,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (35,4)-(35,11))
+ │ ├── locals: [:x]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (35,6)-(35,9))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (35,7)-(35,8))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (35,7)-(35,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :x
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (35,6)-(35,7) = "|"
+ │ │ └── closing_loc: (35,8)-(35,9) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (35,4)-(35,5) = "{"
+ │ └── closing_loc: (35,10)-(35,11) = "}"
+ ├── @ LocalVariableWriteNode (location: (37,0)-(37,8))
+ │ ├── name: :fork
+ │ ├── depth: 0
+ │ ├── name_loc: (37,0)-(37,4) = "fork"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (37,7)-(37,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (37,5)-(37,6) = "="
+ ├── @ CallNode (location: (38,0)-(39,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :fork
+ │ ├── message_loc: (38,0)-(38,4) = "fork"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (38,5)-(39,3))
+ │ ├── locals: [:a]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (38,8)-(38,11))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (38,9)-(38,10))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (38,9)-(38,10))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (38,8)-(38,9) = "|"
+ │ │ └── closing_loc: (38,10)-(38,11) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (38,5)-(38,7) = "do"
+ │ └── closing_loc: (39,0)-(39,3) = "end"
+ ├── @ CallNode (location: (41,0)-(41,12))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :fork
+ │ ├── message_loc: (41,0)-(41,4) = "fork"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (41,5)-(41,12))
+ │ ├── locals: [:a]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (41,7)-(41,10))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (41,8)-(41,9))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (41,8)-(41,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (41,7)-(41,8) = "|"
+ │ │ └── closing_loc: (41,9)-(41,10) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (41,5)-(41,6) = "{"
+ │ └── closing_loc: (41,11)-(41,12) = "}"
+ ├── @ CallNode (location: (43,0)-(44,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :C
+ │ ├── message_loc: (43,0)-(43,1) = "C"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (43,2)-(44,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (43,2)-(43,4) = "do"
+ │ └── closing_loc: (44,0)-(44,3) = "end"
+ ├── @ CallNode (location: (46,0)-(46,4))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :C
+ │ ├── message_loc: (46,0)-(46,1) = "C"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (46,2)-(46,4))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (46,2)-(46,3) = "{"
+ │ └── closing_loc: (46,3)-(46,4) = "}"
+ ├── @ CallNode (location: (48,0)-(52,1))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (48,0)-(48,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (48,4)-(52,1))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (48,4)-(52,1))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :lambda
+ │ │ ├── message_loc: (48,4)-(48,10) = "lambda"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (48,11)-(52,1))
+ │ │ ├── locals: [:a, :b]
+ │ │ ├── parameters:
+ │ │ │ @ BlockParametersNode (location: (48,13)-(51,3))
+ │ │ │ ├── parameters:
+ │ │ │ │ @ ParametersNode (location: (49,2)-(50,6))
+ │ │ │ │ ├── requireds: (length: 0)
+ │ │ │ │ ├── optionals: (length: 0)
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ ├── keywords: (length: 2)
+ │ │ │ │ │ ├── @ OptionalKeywordParameterNode (location: (49,2)-(49,6))
+ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ ├── name: :a
+ │ │ │ │ │ │ ├── name_loc: (49,2)-(49,4) = "a:"
+ │ │ │ │ │ │ └── value:
+ │ │ │ │ │ │ @ IntegerNode (location: (49,5)-(49,6))
+ │ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ │ └── value: 1
+ │ │ │ │ │ └── @ OptionalKeywordParameterNode (location: (50,2)-(50,6))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── name: :b
+ │ │ │ │ │ ├── name_loc: (50,2)-(50,4) = "b:"
+ │ │ │ │ │ └── value:
+ │ │ │ │ │ @ IntegerNode (location: (50,5)-(50,6))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 2
+ │ │ │ │ ├── keyword_rest: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── locals: (length: 0)
+ │ │ │ ├── opening_loc: (48,13)-(48,14) = "|"
+ │ │ │ └── closing_loc: (51,2)-(51,3) = "|"
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (48,11)-(48,12) = "{"
+ │ │ └── closing_loc: (52,0)-(52,1) = "}"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (54,0)-(54,17))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :foo
+ ├── message_loc: (54,0)-(54,3) = "foo"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (54,4)-(54,17))
+ ├── locals: [:bar]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (54,7)-(54,13))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (54,8)-(54,12))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (54,8)-(54,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :bar
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ ImplicitRestNode (location: (54,11)-(54,12))
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (54,7)-(54,8) = "|"
+ │ └── closing_loc: (54,12)-(54,13) = "|"
+ ├── body: ∅
+ ├── opening_loc: (54,4)-(54,6) = "do"
+ └── closing_loc: (54,14)-(54,17) = "end"
diff --git a/test/prism/snapshots/boolean_operators.txt b/test/prism/snapshots/boolean_operators.txt
new file mode 100644
index 0000000000..ace8047e18
--- /dev/null
+++ b/test/prism/snapshots/boolean_operators.txt
@@ -0,0 +1,54 @@
+@ ProgramNode (location: (1,0)-(5,7))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,7))
+ └── body: (length: 3)
+ ├── @ LocalVariableAndWriteNode (location: (1,0)-(1,7))
+ │ ├── name_loc: (1,0)-(1,1) = "a"
+ │ ├── operator_loc: (1,2)-(1,5) = "&&="
+ │ ├── value:
+ │ │ @ CallNode (location: (1,6)-(1,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (1,6)-(1,7) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── name: :a
+ │ └── depth: 0
+ ├── @ LocalVariableOperatorWriteNode (location: (3,0)-(3,6))
+ │ ├── name_loc: (3,0)-(3,1) = "a"
+ │ ├── operator_loc: (3,2)-(3,4) = "+="
+ │ ├── value:
+ │ │ @ CallNode (location: (3,5)-(3,6))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (3,5)-(3,6) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── name: :a
+ │ ├── operator: :+
+ │ └── depth: 0
+ └── @ LocalVariableOrWriteNode (location: (5,0)-(5,7))
+ ├── name_loc: (5,0)-(5,1) = "a"
+ ├── operator_loc: (5,2)-(5,5) = "||="
+ ├── value:
+ │ @ CallNode (location: (5,6)-(5,7))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :b
+ │ ├── message_loc: (5,6)-(5,7) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── name: :a
+ └── depth: 0
diff --git a/test/prism/snapshots/booleans.txt b/test/prism/snapshots/booleans.txt
new file mode 100644
index 0000000000..4731966243
--- /dev/null
+++ b/test/prism/snapshots/booleans.txt
@@ -0,0 +1,7 @@
+@ ProgramNode (location: (1,0)-(3,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,4))
+ └── body: (length: 2)
+ ├── @ FalseNode (location: (1,0)-(1,5))
+ └── @ TrueNode (location: (3,0)-(3,4))
diff --git a/test/prism/snapshots/break.txt b/test/prism/snapshots/break.txt
new file mode 100644
index 0000000000..c15a9e4675
--- /dev/null
+++ b/test/prism/snapshots/break.txt
@@ -0,0 +1,401 @@
+@ ProgramNode (location: (1,0)-(25,23))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(25,23))
+ └── body: (length: 11)
+ ├── @ CallNode (location: (1,0)-(1,13))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (1,0)-(1,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,4)-(1,13))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,6)-(1,11))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (1,6)-(1,11))
+ │ │ ├── arguments: ∅
+ │ │ └── keyword_loc: (1,6)-(1,11) = "break"
+ │ ├── opening_loc: (1,4)-(1,5) = "{"
+ │ └── closing_loc: (1,12)-(1,13) = "}"
+ ├── @ CallNode (location: (3,0)-(3,27))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (3,0)-(3,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (3,4)-(3,27))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (3,6)-(3,25))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (3,6)-(3,25))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (3,12)-(3,25))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 3)
+ │ │ │ ├── @ ParenthesesNode (location: (3,12)-(3,15))
+ │ │ │ │ ├── body:
+ │ │ │ │ │ @ StatementsNode (location: (3,13)-(3,14))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ IntegerNode (location: (3,13)-(3,14))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ ├── opening_loc: (3,12)-(3,13) = "("
+ │ │ │ │ └── closing_loc: (3,14)-(3,15) = ")"
+ │ │ │ ├── @ ParenthesesNode (location: (3,17)-(3,20))
+ │ │ │ │ ├── body:
+ │ │ │ │ │ @ StatementsNode (location: (3,18)-(3,19))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ IntegerNode (location: (3,18)-(3,19))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 2
+ │ │ │ │ ├── opening_loc: (3,17)-(3,18) = "("
+ │ │ │ │ └── closing_loc: (3,19)-(3,20) = ")"
+ │ │ │ └── @ ParenthesesNode (location: (3,22)-(3,25))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (3,23)-(3,24))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (3,23)-(3,24))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 3
+ │ │ │ ├── opening_loc: (3,22)-(3,23) = "("
+ │ │ │ └── closing_loc: (3,24)-(3,25) = ")"
+ │ │ └── keyword_loc: (3,6)-(3,11) = "break"
+ │ ├── opening_loc: (3,4)-(3,5) = "{"
+ │ └── closing_loc: (3,26)-(3,27) = "}"
+ ├── @ CallNode (location: (5,0)-(5,15))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (5,0)-(5,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (5,4)-(5,15))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (5,6)-(5,13))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (5,6)-(5,13))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (5,12)-(5,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (5,12)-(5,13))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── keyword_loc: (5,6)-(5,11) = "break"
+ │ ├── opening_loc: (5,4)-(5,5) = "{"
+ │ └── closing_loc: (5,14)-(5,15) = "}"
+ ├── @ CallNode (location: (7,0)-(8,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (7,0)-(7,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (7,4)-(8,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (7,6)-(8,1))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (7,6)-(8,1))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (7,12)-(8,1))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 3)
+ │ │ │ ├── @ IntegerNode (location: (7,12)-(7,13))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── @ IntegerNode (location: (7,15)-(7,16))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ └── @ IntegerNode (location: (8,0)-(8,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 3
+ │ │ └── keyword_loc: (7,6)-(7,11) = "break"
+ │ ├── opening_loc: (7,4)-(7,5) = "{"
+ │ └── closing_loc: (8,2)-(8,3) = "}"
+ ├── @ CallNode (location: (10,0)-(10,21))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (10,0)-(10,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (10,4)-(10,21))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (10,6)-(10,19))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (10,6)-(10,19))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (10,12)-(10,19))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 3)
+ │ │ │ ├── @ IntegerNode (location: (10,12)-(10,13))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── @ IntegerNode (location: (10,15)-(10,16))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ └── @ IntegerNode (location: (10,18)-(10,19))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 3
+ │ │ └── keyword_loc: (10,6)-(10,11) = "break"
+ │ ├── opening_loc: (10,4)-(10,5) = "{"
+ │ └── closing_loc: (10,20)-(10,21) = "}"
+ ├── @ CallNode (location: (12,0)-(12,23))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (12,0)-(12,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (12,4)-(12,23))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (12,6)-(12,21))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (12,6)-(12,21))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (12,12)-(12,21))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ ArrayNode (location: (12,12)-(12,21))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 3)
+ │ │ │ │ ├── @ IntegerNode (location: (12,13)-(12,14))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ ├── @ IntegerNode (location: (12,16)-(12,17))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 2
+ │ │ │ │ └── @ IntegerNode (location: (12,19)-(12,20))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 3
+ │ │ │ ├── opening_loc: (12,12)-(12,13) = "["
+ │ │ │ └── closing_loc: (12,20)-(12,21) = "]"
+ │ │ └── keyword_loc: (12,6)-(12,11) = "break"
+ │ ├── opening_loc: (12,4)-(12,5) = "{"
+ │ └── closing_loc: (12,22)-(12,23) = "}"
+ ├── @ CallNode (location: (14,0)-(17,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (14,0)-(14,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (14,4)-(17,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (14,6)-(17,1))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (14,6)-(17,1))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (14,11)-(17,1))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ ParenthesesNode (location: (14,11)-(17,1))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (15,2)-(16,3))
+ │ │ │ │ └── body: (length: 2)
+ │ │ │ │ ├── @ IntegerNode (location: (15,2)-(15,3))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ └── @ IntegerNode (location: (16,2)-(16,3))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ ├── opening_loc: (14,11)-(14,12) = "("
+ │ │ │ └── closing_loc: (17,0)-(17,1) = ")"
+ │ │ └── keyword_loc: (14,6)-(14,11) = "break"
+ │ ├── opening_loc: (14,4)-(14,5) = "{"
+ │ └── closing_loc: (17,2)-(17,3) = "}"
+ ├── @ CallNode (location: (19,0)-(19,15))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (19,0)-(19,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (19,4)-(19,15))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (19,6)-(19,13))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (19,6)-(19,13))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (19,11)-(19,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ ParenthesesNode (location: (19,11)-(19,13))
+ │ │ │ ├── body: ∅
+ │ │ │ ├── opening_loc: (19,11)-(19,12) = "("
+ │ │ │ └── closing_loc: (19,12)-(19,13) = ")"
+ │ │ └── keyword_loc: (19,6)-(19,11) = "break"
+ │ ├── opening_loc: (19,4)-(19,5) = "{"
+ │ └── closing_loc: (19,14)-(19,15) = "}"
+ ├── @ CallNode (location: (21,0)-(21,16))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (21,0)-(21,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (21,4)-(21,16))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (21,6)-(21,14))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (21,6)-(21,14))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (21,11)-(21,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ ParenthesesNode (location: (21,11)-(21,14))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (21,12)-(21,13))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (21,12)-(21,13))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── opening_loc: (21,11)-(21,12) = "("
+ │ │ │ └── closing_loc: (21,13)-(21,14) = ")"
+ │ │ └── keyword_loc: (21,6)-(21,11) = "break"
+ │ ├── opening_loc: (21,4)-(21,5) = "{"
+ │ └── closing_loc: (21,15)-(21,16) = "}"
+ ├── @ CallNode (location: (23,0)-(23,22))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (23,0)-(23,16))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (23,0)-(23,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (23,4)-(23,16))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (23,6)-(23,14))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ BreakNode (location: (23,6)-(23,14))
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (23,12)-(23,14))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (23,12)-(23,14))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 42
+ │ │ │ └── keyword_loc: (23,6)-(23,11) = "break"
+ │ │ ├── opening_loc: (23,4)-(23,5) = "{"
+ │ │ └── closing_loc: (23,15)-(23,16) = "}"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :==
+ │ ├── message_loc: (23,17)-(23,19) = "=="
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (23,20)-(23,22))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (23,20)-(23,22))
+ │ │ ├── flags: decimal
+ │ │ └── value: 42
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (25,0)-(25,23))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (25,0)-(25,17))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (25,0)-(25,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (25,4)-(25,17))
+ │ ├── locals: [:a]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (25,6)-(25,9))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (25,7)-(25,8))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (25,7)-(25,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (25,6)-(25,7) = "|"
+ │ │ └── closing_loc: (25,8)-(25,9) = "|"
+ │ ├── body:
+ │ │ @ StatementsNode (location: (25,10)-(25,15))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (25,10)-(25,15))
+ │ │ ├── arguments: ∅
+ │ │ └── keyword_loc: (25,10)-(25,15) = "break"
+ │ ├── opening_loc: (25,4)-(25,5) = "{"
+ │ └── closing_loc: (25,16)-(25,17) = "}"
+ ├── call_operator_loc: ∅
+ ├── name: :==
+ ├── message_loc: (25,18)-(25,20) = "=="
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (25,21)-(25,23))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (25,21)-(25,23))
+ │ ├── flags: decimal
+ │ └── value: 42
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/case.txt b/test/prism/snapshots/case.txt
new file mode 100644
index 0000000000..417bf9492a
--- /dev/null
+++ b/test/prism/snapshots/case.txt
@@ -0,0 +1,495 @@
+@ ProgramNode (location: (1,0)-(55,3))
+├── locals: [:b]
+└── statements:
+ @ StatementsNode (location: (1,0)-(55,3))
+ └── body: (length: 15)
+ ├── @ CaseNode (location: (1,0)-(3,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (1,5)-(1,8))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ │ ├── value_loc: (1,6)-(1,8) = "hi"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "hi"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ WhenNode (location: (2,0)-(2,8))
+ │ │ ├── keyword_loc: (2,0)-(2,4) = "when"
+ │ │ ├── conditions: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (2,5)-(2,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (2,5)-(2,6) = ":"
+ │ │ │ ├── value_loc: (2,6)-(2,8) = "hi"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "hi"
+ │ │ ├── then_keyword_loc: ∅
+ │ │ └── statements: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ │ └── end_keyword_loc: (3,0)-(3,3) = "end"
+ ├── @ CaseNode (location: (5,0)-(5,58))
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (5,5)-(5,9))
+ │ ├── conditions: (length: 2)
+ │ │ ├── @ WhenNode (location: (5,11)-(5,30))
+ │ │ │ ├── keyword_loc: (5,11)-(5,15) = "when"
+ │ │ │ ├── conditions: (length: 1)
+ │ │ │ │ └── @ TrueNode (location: (5,16)-(5,20))
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ └── statements:
+ │ │ │ @ StatementsNode (location: (5,22)-(5,30))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (5,22)-(5,30))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :puts
+ │ │ │ ├── message_loc: (5,22)-(5,26) = "puts"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (5,27)-(5,30))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ SymbolNode (location: (5,27)-(5,30))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (5,27)-(5,28) = ":"
+ │ │ │ │ ├── value_loc: (5,28)-(5,30) = "hi"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "hi"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ WhenNode (location: (5,32)-(5,53))
+ │ │ ├── keyword_loc: (5,32)-(5,36) = "when"
+ │ │ ├── conditions: (length: 1)
+ │ │ │ └── @ FalseNode (location: (5,37)-(5,42))
+ │ │ ├── then_keyword_loc: ∅
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (5,44)-(5,53))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (5,44)-(5,53))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :puts
+ │ │ ├── message_loc: (5,44)-(5,48) = "puts"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (5,49)-(5,53))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (5,49)-(5,53))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (5,49)-(5,50) = ":"
+ │ │ │ ├── value_loc: (5,50)-(5,53) = "bye"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "bye"
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (5,0)-(5,4) = "case"
+ │ └── end_keyword_loc: (5,55)-(5,58) = "end"
+ ├── @ CaseNode (location: (7,0)-(7,20))
+ │ ├── predicate: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ WhenNode (location: (7,6)-(7,15))
+ │ │ ├── keyword_loc: (7,6)-(7,10) = "when"
+ │ │ ├── conditions: (length: 1)
+ │ │ │ └── @ SplatNode (location: (7,11)-(7,15))
+ │ │ │ ├── operator_loc: (7,11)-(7,12) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ CallNode (location: (7,12)-(7,15))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (7,12)-(7,15) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── then_keyword_loc: ∅
+ │ │ └── statements: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (7,0)-(7,4) = "case"
+ │ └── end_keyword_loc: (7,17)-(7,20) = "end"
+ ├── @ CaseNode (location: (9,0)-(13,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (9,5)-(9,8))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (9,5)-(9,6) = ":"
+ │ │ ├── value_loc: (9,6)-(9,8) = "hi"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "hi"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ WhenNode (location: (10,0)-(10,8))
+ │ │ ├── keyword_loc: (10,0)-(10,4) = "when"
+ │ │ ├── conditions: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (10,5)-(10,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (10,5)-(10,6) = ":"
+ │ │ │ ├── value_loc: (10,6)-(10,8) = "hi"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "hi"
+ │ │ ├── then_keyword_loc: ∅
+ │ │ └── statements: ∅
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (11,0)-(13,3))
+ │ │ ├── else_keyword_loc: (11,0)-(11,4) = "else"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (12,0)-(12,2))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (12,0)-(12,2))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (12,0)-(12,1) = ":"
+ │ │ │ ├── value_loc: (12,1)-(12,2) = "b"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "b"
+ │ │ └── end_keyword_loc: (13,0)-(13,3) = "end"
+ │ ├── case_keyword_loc: (9,0)-(9,4) = "case"
+ │ └── end_keyword_loc: (13,0)-(13,3) = "end"
+ ├── @ CaseNode (location: (15,0)-(15,36))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (15,5)-(15,9))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :this
+ │ │ ├── message_loc: (15,5)-(15,9) = "this"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ WhenNode (location: (15,11)-(15,31))
+ │ │ ├── keyword_loc: (15,11)-(15,15) = "when"
+ │ │ ├── conditions: (length: 2)
+ │ │ │ ├── @ ConstantReadNode (location: (15,16)-(15,22))
+ │ │ │ │ └── name: :FooBar
+ │ │ │ └── @ ConstantReadNode (location: (15,24)-(15,31))
+ │ │ │ └── name: :BazBonk
+ │ │ ├── then_keyword_loc: ∅
+ │ │ └── statements: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (15,0)-(15,4) = "case"
+ │ └── end_keyword_loc: (15,33)-(15,36) = "end"
+ ├── @ CaseNode (location: (17,0)-(19,3))
+ │ ├── predicate: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ WhenNode (location: (18,0)-(18,15))
+ │ │ ├── keyword_loc: (18,0)-(18,4) = "when"
+ │ │ ├── conditions: (length: 1)
+ │ │ │ └── @ CallNode (location: (18,5)-(18,15))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ CallNode (location: (18,5)-(18,8))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :foo
+ │ │ │ │ ├── message_loc: (18,5)-(18,8) = "foo"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :==
+ │ │ │ ├── message_loc: (18,9)-(18,11) = "=="
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (18,12)-(18,15))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (18,12)-(18,15))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (18,12)-(18,15) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── then_keyword_loc: ∅
+ │ │ └── statements: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (17,0)-(17,4) = "case"
+ │ └── end_keyword_loc: (19,0)-(19,3) = "end"
+ ├── @ CaseNode (location: (21,0)-(25,3))
+ │ ├── predicate: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ WhenNode (location: (22,0)-(22,6))
+ │ │ ├── keyword_loc: (22,0)-(22,4) = "when"
+ │ │ ├── conditions: (length: 1)
+ │ │ │ └── @ CallNode (location: (22,5)-(22,6))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (22,5)-(22,6) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── then_keyword_loc: ∅
+ │ │ └── statements: ∅
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (23,0)-(25,3))
+ │ │ ├── else_keyword_loc: (23,0)-(23,4) = "else"
+ │ │ ├── statements: ∅
+ │ │ └── end_keyword_loc: (25,0)-(25,3) = "end"
+ │ ├── case_keyword_loc: (21,0)-(21,4) = "case"
+ │ └── end_keyword_loc: (25,0)-(25,3) = "end"
+ ├── @ CaseNode (location: (27,0)-(30,6))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (27,5)-(27,9))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :type
+ │ │ ├── message_loc: (27,5)-(27,9) = "type"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ WhenNode (location: (28,3)-(28,10))
+ │ │ ├── keyword_loc: (28,3)-(28,7) = "when"
+ │ │ ├── conditions: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (28,8)-(28,10))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (28,8)-(28,9) = ":"
+ │ │ │ ├── value_loc: (28,9)-(28,10) = "b"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "b"
+ │ │ ├── then_keyword_loc: ∅
+ │ │ └── statements: ∅
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (29,5)-(30,6))
+ │ │ ├── else_keyword_loc: (29,5)-(29,9) = "else"
+ │ │ ├── statements: ∅
+ │ │ └── end_keyword_loc: (30,3)-(30,6) = "end"
+ │ ├── case_keyword_loc: (27,0)-(27,4) = "case"
+ │ └── end_keyword_loc: (30,3)-(30,6) = "end"
+ ├── @ CaseNode (location: (32,0)-(32,25))
+ │ ├── predicate: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ WhenNode (location: (32,14)-(32,20))
+ │ │ ├── keyword_loc: (32,14)-(32,18) = "when"
+ │ │ ├── conditions: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (32,19)-(32,20))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── then_keyword_loc: ∅
+ │ │ └── statements: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (32,0)-(32,4) = "case"
+ │ └── end_keyword_loc: (32,22)-(32,25) = "end"
+ ├── @ CaseNode (location: (34,0)-(36,3))
+ │ ├── predicate:
+ │ │ @ MatchPredicateNode (location: (34,5)-(34,11))
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (34,5)-(34,6))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── pattern:
+ │ │ │ @ IntegerNode (location: (34,10)-(34,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── operator_loc: (34,7)-(34,9) = "in"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ WhenNode (location: (35,0)-(35,6))
+ │ │ ├── keyword_loc: (35,0)-(35,4) = "when"
+ │ │ ├── conditions: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (35,5)-(35,6))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 3
+ │ │ ├── then_keyword_loc: ∅
+ │ │ └── statements: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (34,0)-(34,4) = "case"
+ │ └── end_keyword_loc: (36,0)-(36,3) = "end"
+ ├── @ CaseNode (location: (38,0)-(38,24))
+ │ ├── predicate:
+ │ │ @ MatchPredicateNode (location: (38,5)-(38,11))
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (38,5)-(38,6))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── pattern:
+ │ │ │ @ IntegerNode (location: (38,10)-(38,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── operator_loc: (38,7)-(38,9) = "in"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ WhenNode (location: (38,13)-(38,19))
+ │ │ ├── keyword_loc: (38,13)-(38,17) = "when"
+ │ │ ├── conditions: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (38,18)-(38,19))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 3
+ │ │ ├── then_keyword_loc: ∅
+ │ │ └── statements: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (38,0)-(38,4) = "case"
+ │ └── end_keyword_loc: (38,21)-(38,24) = "end"
+ ├── @ CaseMatchNode (location: (40,0)-(42,3))
+ │ ├── predicate:
+ │ │ @ MatchPredicateNode (location: (40,5)-(40,11))
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (40,5)-(40,6))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── pattern:
+ │ │ │ @ IntegerNode (location: (40,10)-(40,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── operator_loc: (40,7)-(40,9) = "in"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (41,0)-(41,4))
+ │ │ ├── pattern:
+ │ │ │ @ IntegerNode (location: (41,3)-(41,4))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 3
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (41,0)-(41,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (40,0)-(40,4) = "case"
+ │ └── end_keyword_loc: (42,0)-(42,3) = "end"
+ ├── @ CaseMatchNode (location: (44,0)-(44,22))
+ │ ├── predicate:
+ │ │ @ MatchPredicateNode (location: (44,5)-(44,11))
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (44,5)-(44,6))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── pattern:
+ │ │ │ @ IntegerNode (location: (44,10)-(44,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── operator_loc: (44,7)-(44,9) = "in"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (44,13)-(44,17))
+ │ │ ├── pattern:
+ │ │ │ @ IntegerNode (location: (44,16)-(44,17))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 3
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (44,13)-(44,15) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (44,0)-(44,4) = "case"
+ │ └── end_keyword_loc: (44,19)-(44,22) = "end"
+ ├── @ CaseMatchNode (location: (46,0)-(49,3))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (46,5)-(46,6))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (46,5)-(46,6) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (47,0)-(48,3))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (47,3)-(47,15))
+ │ │ │ ├── if_keyword_loc: (47,5)-(47,7) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ AndNode (location: (47,8)-(47,15))
+ │ │ │ │ ├── left:
+ │ │ │ │ │ @ CallNode (location: (47,8)-(47,9))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :c
+ │ │ │ │ │ ├── message_loc: (47,8)-(47,9) = "c"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── right:
+ │ │ │ │ │ @ CallNode (location: (47,14)-(47,15))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (47,14)-(47,15) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ └── operator_loc: (47,10)-(47,13) = "and"
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (47,3)-(47,4))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ LocalVariableTargetNode (location: (47,3)-(47,4))
+ │ │ │ │ ├── name: :b
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (48,2)-(48,3))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (48,2)-(48,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :e
+ │ │ │ ├── message_loc: (48,2)-(48,3) = "e"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── in_loc: (47,0)-(47,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (46,0)-(46,4) = "case"
+ │ └── end_keyword_loc: (49,0)-(49,3) = "end"
+ └── @ CallNode (location: (51,0)-(55,3))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ IntegerNode (location: (51,0)-(51,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── call_operator_loc: (51,1)-(51,2) = "."
+ ├── name: :then
+ ├── message_loc: (51,2)-(51,6) = "then"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (51,7)-(55,3))
+ ├── locals: [:_1]
+ ├── parameters:
+ │ @ NumberedParametersNode (location: (51,7)-(55,3))
+ │ └── maximum: 1
+ ├── body:
+ │ @ StatementsNode (location: (52,2)-(54,5))
+ │ └── body: (length: 1)
+ │ └── @ CaseMatchNode (location: (52,2)-(54,5))
+ │ ├── predicate:
+ │ │ @ IntegerNode (location: (52,7)-(52,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (53,2)-(53,8))
+ │ │ ├── pattern:
+ │ │ │ @ PinnedVariableNode (location: (53,5)-(53,8))
+ │ │ │ ├── variable:
+ │ │ │ │ @ LocalVariableReadNode (location: (53,6)-(53,8))
+ │ │ │ │ ├── name: :_1
+ │ │ │ │ └── depth: 0
+ │ │ │ └── operator_loc: (53,5)-(53,6) = "^"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (53,2)-(53,4) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (52,2)-(52,6) = "case"
+ │ └── end_keyword_loc: (54,2)-(54,5) = "end"
+ ├── opening_loc: (51,7)-(51,9) = "do"
+ └── closing_loc: (55,0)-(55,3) = "end"
diff --git a/test/prism/snapshots/classes.txt b/test/prism/snapshots/classes.txt
new file mode 100644
index 0000000000..4a36bd5cdc
--- /dev/null
+++ b/test/prism/snapshots/classes.txt
@@ -0,0 +1,365 @@
+@ ProgramNode (location: (1,0)-(35,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(35,3))
+ └── body: (length: 14)
+ ├── @ ClassNode (location: (1,0)-(1,17))
+ │ ├── locals: [:a]
+ │ ├── class_keyword_loc: (1,0)-(1,5) = "class"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (1,6)-(1,7))
+ │ │ └── name: :A
+ │ ├── inheritance_operator_loc: ∅
+ │ ├── superclass: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,8)-(1,13))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableWriteNode (location: (1,8)-(1,13))
+ │ │ ├── name: :a
+ │ │ ├── depth: 0
+ │ │ ├── name_loc: (1,8)-(1,9) = "a"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (1,12)-(1,13))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (1,10)-(1,11) = "="
+ │ ├── end_keyword_loc: (1,14)-(1,17) = "end"
+ │ └── name: :A
+ ├── @ ClassNode (location: (3,0)-(3,20))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (3,0)-(3,5) = "class"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (3,6)-(3,7))
+ │ │ └── name: :A
+ │ ├── inheritance_operator_loc: ∅
+ │ ├── superclass: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (3,0)-(3,20))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── rescue_clause: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause:
+ │ │ │ @ EnsureNode (location: (3,9)-(3,20))
+ │ │ │ ├── ensure_keyword_loc: (3,9)-(3,15) = "ensure"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── end_keyword_loc: (3,17)-(3,20) = "end"
+ │ │ └── end_keyword_loc: (3,17)-(3,20) = "end"
+ │ ├── end_keyword_loc: (3,17)-(3,20) = "end"
+ │ └── name: :A
+ ├── @ ClassNode (location: (5,0)-(5,34))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (5,0)-(5,5) = "class"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (5,6)-(5,7))
+ │ │ └── name: :A
+ │ ├── inheritance_operator_loc: ∅
+ │ ├── superclass: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (5,0)-(5,34))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── rescue_clause:
+ │ │ │ @ RescueNode (location: (5,9)-(5,15))
+ │ │ │ ├── keyword_loc: (5,9)-(5,15) = "rescue"
+ │ │ │ ├── exceptions: (length: 0)
+ │ │ │ ├── operator_loc: ∅
+ │ │ │ ├── reference: ∅
+ │ │ │ ├── statements: ∅
+ │ │ │ └── consequent: ∅
+ │ │ ├── else_clause:
+ │ │ │ @ ElseNode (location: (5,17)-(5,29))
+ │ │ │ ├── else_keyword_loc: (5,17)-(5,21) = "else"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── end_keyword_loc: (5,23)-(5,29) = "ensure"
+ │ │ ├── ensure_clause:
+ │ │ │ @ EnsureNode (location: (5,23)-(5,34))
+ │ │ │ ├── ensure_keyword_loc: (5,23)-(5,29) = "ensure"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── end_keyword_loc: (5,31)-(5,34) = "end"
+ │ │ └── end_keyword_loc: (5,31)-(5,34) = "end"
+ │ ├── end_keyword_loc: (5,31)-(5,34) = "end"
+ │ └── name: :A
+ ├── @ ClassNode (location: (7,0)-(9,3))
+ │ ├── locals: [:a]
+ │ ├── class_keyword_loc: (7,0)-(7,5) = "class"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (7,6)-(7,7))
+ │ │ └── name: :A
+ │ ├── inheritance_operator_loc: (7,8)-(7,9) = "<"
+ │ ├── superclass:
+ │ │ @ ConstantReadNode (location: (7,10)-(7,11))
+ │ │ └── name: :B
+ │ ├── body:
+ │ │ @ StatementsNode (location: (8,0)-(8,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableWriteNode (location: (8,0)-(8,5))
+ │ │ ├── name: :a
+ │ │ ├── depth: 0
+ │ │ ├── name_loc: (8,0)-(8,1) = "a"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (8,4)-(8,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (8,2)-(8,3) = "="
+ │ ├── end_keyword_loc: (9,0)-(9,3) = "end"
+ │ └── name: :A
+ ├── @ SingletonClassNode (location: (11,0)-(12,3))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (11,0)-(11,5) = "class"
+ │ ├── operator_loc: (11,6)-(11,8) = "<<"
+ │ ├── expression:
+ │ │ @ CallNode (location: (11,9)-(11,16))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (11,13)-(11,16))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (11,13)-(11,16) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :!
+ │ │ ├── message_loc: (11,9)-(11,12) = "not"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ └── end_keyword_loc: (12,0)-(12,3) = "end"
+ ├── @ ClassNode (location: (14,0)-(14,40))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (14,0)-(14,5) = "class"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (14,6)-(14,7))
+ │ │ └── name: :A
+ │ ├── inheritance_operator_loc: ∅
+ │ ├── superclass: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (14,9)-(14,35))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SingletonClassNode (location: (14,9)-(14,35))
+ │ │ ├── locals: []
+ │ │ ├── class_keyword_loc: (14,9)-(14,14) = "class"
+ │ │ ├── operator_loc: (14,15)-(14,17) = "<<"
+ │ │ ├── expression:
+ │ │ │ @ SelfNode (location: (14,18)-(14,22))
+ │ │ ├── body:
+ │ │ │ @ BeginNode (location: (14,9)-(14,35))
+ │ │ │ ├── begin_keyword_loc: ∅
+ │ │ │ ├── statements: ∅
+ │ │ │ ├── rescue_clause: ∅
+ │ │ │ ├── else_clause: ∅
+ │ │ │ ├── ensure_clause:
+ │ │ │ │ @ EnsureNode (location: (14,24)-(14,35))
+ │ │ │ │ ├── ensure_keyword_loc: (14,24)-(14,30) = "ensure"
+ │ │ │ │ ├── statements: ∅
+ │ │ │ │ └── end_keyword_loc: (14,32)-(14,35) = "end"
+ │ │ │ └── end_keyword_loc: (14,32)-(14,35) = "end"
+ │ │ └── end_keyword_loc: (14,32)-(14,35) = "end"
+ │ ├── end_keyword_loc: (14,37)-(14,40) = "end"
+ │ └── name: :A
+ ├── @ ClassNode (location: (16,0)-(16,54))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (16,0)-(16,5) = "class"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (16,6)-(16,7))
+ │ │ └── name: :A
+ │ ├── inheritance_operator_loc: ∅
+ │ ├── superclass: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (16,9)-(16,49))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SingletonClassNode (location: (16,9)-(16,49))
+ │ │ ├── locals: []
+ │ │ ├── class_keyword_loc: (16,9)-(16,14) = "class"
+ │ │ ├── operator_loc: (16,15)-(16,17) = "<<"
+ │ │ ├── expression:
+ │ │ │ @ SelfNode (location: (16,18)-(16,22))
+ │ │ ├── body:
+ │ │ │ @ BeginNode (location: (16,9)-(16,49))
+ │ │ │ ├── begin_keyword_loc: ∅
+ │ │ │ ├── statements: ∅
+ │ │ │ ├── rescue_clause:
+ │ │ │ │ @ RescueNode (location: (16,24)-(16,30))
+ │ │ │ │ ├── keyword_loc: (16,24)-(16,30) = "rescue"
+ │ │ │ │ ├── exceptions: (length: 0)
+ │ │ │ │ ├── operator_loc: ∅
+ │ │ │ │ ├── reference: ∅
+ │ │ │ │ ├── statements: ∅
+ │ │ │ │ └── consequent: ∅
+ │ │ │ ├── else_clause:
+ │ │ │ │ @ ElseNode (location: (16,32)-(16,44))
+ │ │ │ │ ├── else_keyword_loc: (16,32)-(16,36) = "else"
+ │ │ │ │ ├── statements: ∅
+ │ │ │ │ └── end_keyword_loc: (16,38)-(16,44) = "ensure"
+ │ │ │ ├── ensure_clause:
+ │ │ │ │ @ EnsureNode (location: (16,38)-(16,49))
+ │ │ │ │ ├── ensure_keyword_loc: (16,38)-(16,44) = "ensure"
+ │ │ │ │ ├── statements: ∅
+ │ │ │ │ └── end_keyword_loc: (16,46)-(16,49) = "end"
+ │ │ │ └── end_keyword_loc: (16,46)-(16,49) = "end"
+ │ │ └── end_keyword_loc: (16,46)-(16,49) = "end"
+ │ ├── end_keyword_loc: (16,51)-(16,54) = "end"
+ │ └── name: :A
+ ├── @ SingletonClassNode (location: (18,0)-(19,3))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (18,0)-(18,5) = "class"
+ │ ├── operator_loc: (18,6)-(18,8) = "<<"
+ │ ├── expression:
+ │ │ @ CallNode (location: (18,9)-(18,16))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (18,9)-(18,12))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (18,9)-(18,12) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (18,12)-(18,13) = "."
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (18,13)-(18,16) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ └── end_keyword_loc: (19,0)-(19,3) = "end"
+ ├── @ SingletonClassNode (location: (21,0)-(21,20))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (21,0)-(21,5) = "class"
+ │ ├── operator_loc: (21,6)-(21,8) = "<<"
+ │ ├── expression:
+ │ │ @ CallNode (location: (21,9)-(21,16))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (21,9)-(21,12))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (21,9)-(21,12) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (21,12)-(21,13) = "."
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (21,13)-(21,16) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ └── end_keyword_loc: (21,17)-(21,20) = "end"
+ ├── @ SingletonClassNode (location: (23,0)-(24,3))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (23,0)-(23,5) = "class"
+ │ ├── operator_loc: (23,6)-(23,8) = "<<"
+ │ ├── expression:
+ │ │ @ SelfNode (location: (23,9)-(23,13))
+ │ ├── body: ∅
+ │ └── end_keyword_loc: (24,0)-(24,3) = "end"
+ ├── @ SingletonClassNode (location: (26,0)-(26,17))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (26,0)-(26,5) = "class"
+ │ ├── operator_loc: (26,6)-(26,8) = "<<"
+ │ ├── expression:
+ │ │ @ SelfNode (location: (26,9)-(26,13))
+ │ ├── body: ∅
+ │ └── end_keyword_loc: (26,14)-(26,17) = "end"
+ ├── @ SingletonClassNode (location: (28,0)-(30,3))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (28,0)-(28,5) = "class"
+ │ ├── operator_loc: (28,6)-(28,8) = "<<"
+ │ ├── expression:
+ │ │ @ SelfNode (location: (28,9)-(28,13))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (29,0)-(29,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (29,0)-(29,5))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ IntegerNode (location: (29,0)-(29,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :+
+ │ │ ├── message_loc: (29,2)-(29,3) = "+"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (29,4)-(29,5))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (29,4)-(29,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── end_keyword_loc: (30,0)-(30,3) = "end"
+ ├── @ SingletonClassNode (location: (32,0)-(32,23))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (32,0)-(32,5) = "class"
+ │ ├── operator_loc: (32,6)-(32,8) = "<<"
+ │ ├── expression:
+ │ │ @ SelfNode (location: (32,9)-(32,13))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (32,14)-(32,19))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (32,14)-(32,19))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ IntegerNode (location: (32,14)-(32,15))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :+
+ │ │ ├── message_loc: (32,16)-(32,17) = "+"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (32,18)-(32,19))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (32,18)-(32,19))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── end_keyword_loc: (32,20)-(32,23) = "end"
+ └── @ ClassNode (location: (34,0)-(35,3))
+ ├── locals: []
+ ├── class_keyword_loc: (34,0)-(34,5) = "class"
+ ├── constant_path:
+ │ @ ConstantReadNode (location: (34,6)-(34,7))
+ │ └── name: :A
+ ├── inheritance_operator_loc: (34,8)-(34,9) = "<"
+ ├── superclass:
+ │ @ CallNode (location: (34,10)-(34,14))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (34,10)-(34,11))
+ │ │ └── name: :B
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]
+ │ ├── message_loc: (34,11)-(34,14) = "[1]"
+ │ ├── opening_loc: (34,11)-(34,12) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (34,12)-(34,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (34,12)-(34,13))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: (34,13)-(34,14) = "]"
+ │ └── block: ∅
+ ├── body: ∅
+ ├── end_keyword_loc: (35,0)-(35,3) = "end"
+ └── name: :A
diff --git a/test/prism/snapshots/command_method_call.txt b/test/prism/snapshots/command_method_call.txt
new file mode 100644
index 0000000000..7fd6341304
--- /dev/null
+++ b/test/prism/snapshots/command_method_call.txt
@@ -0,0 +1,755 @@
+@ ProgramNode (location: (1,0)-(41,10))
+├── locals: [:foo, :bar]
+└── statements:
+ @ StatementsNode (location: (1,0)-(41,10))
+ └── body: (length: 21)
+ ├── @ CallNode (location: (1,0)-(1,5))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,4)-(1,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,4)-(1,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (3,0)-(3,9))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (3,0)-(3,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,4)-(3,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (3,4)-(3,9))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (3,4)-(3,7) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (3,8)-(3,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (3,8)-(3,9))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ IfNode (location: (5,0)-(5,14))
+ │ ├── if_keyword_loc: (5,6)-(5,8) = "if"
+ │ ├── predicate:
+ │ │ @ CallNode (location: (5,9)-(5,14))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (5,9)-(5,12) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (5,13)-(5,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (5,13)-(5,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (5,0)-(5,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (5,0)-(5,5))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (5,0)-(5,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (5,4)-(5,5))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (5,4)-(5,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: ∅
+ ├── @ UnlessNode (location: (7,0)-(7,18))
+ │ ├── keyword_loc: (7,6)-(7,12) = "unless"
+ │ ├── predicate:
+ │ │ @ CallNode (location: (7,13)-(7,18))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (7,13)-(7,16) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (7,17)-(7,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (7,17)-(7,18))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (7,0)-(7,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (7,0)-(7,5))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (7,0)-(7,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (7,4)-(7,5))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (7,4)-(7,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: ∅
+ ├── @ WhileNode (location: (9,0)-(9,17))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (9,6)-(9,11) = "while"
+ │ ├── closing_loc: ∅
+ │ ├── predicate:
+ │ │ @ CallNode (location: (9,12)-(9,17))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (9,12)-(9,15) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (9,16)-(9,17))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (9,16)-(9,17))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── statements:
+ │ @ StatementsNode (location: (9,0)-(9,5))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (9,0)-(9,5))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (9,0)-(9,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (9,4)-(9,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (9,4)-(9,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ UntilNode (location: (11,0)-(11,17))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (11,6)-(11,11) = "until"
+ │ ├── closing_loc: ∅
+ │ ├── predicate:
+ │ │ @ CallNode (location: (11,12)-(11,17))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (11,12)-(11,15) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (11,16)-(11,17))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (11,16)-(11,17))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── statements:
+ │ @ StatementsNode (location: (11,0)-(11,5))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (11,0)-(11,5))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (11,0)-(11,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (11,4)-(11,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (11,4)-(11,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ RescueModifierNode (location: (13,0)-(13,18))
+ │ ├── expression:
+ │ │ @ CallNode (location: (13,0)-(13,5))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (13,0)-(13,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (13,4)-(13,5))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (13,4)-(13,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (13,6)-(13,12) = "rescue"
+ │ └── rescue_expression:
+ │ @ CallNode (location: (13,13)-(13,18))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (13,13)-(13,16) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (13,17)-(13,18))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (13,17)-(13,18))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (15,0)-(15,10))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (15,0)-(15,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (15,0)-(15,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]
+ │ ├── message_loc: (15,3)-(15,10) = "[bar 1]"
+ │ ├── opening_loc: (15,3)-(15,4) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (15,4)-(15,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (15,4)-(15,9))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (15,4)-(15,7) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (15,8)-(15,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (15,8)-(15,9))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (15,9)-(15,10) = "]"
+ │ └── block: ∅
+ ├── @ AndNode (location: (17,0)-(17,15))
+ │ ├── left:
+ │ │ @ CallNode (location: (17,0)-(17,5))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (17,0)-(17,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (17,4)-(17,5))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (17,4)-(17,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right:
+ │ │ @ CallNode (location: (17,10)-(17,15))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (17,10)-(17,13) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (17,14)-(17,15))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (17,14)-(17,15))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (17,6)-(17,9) = "and"
+ ├── @ OrNode (location: (19,0)-(19,14))
+ │ ├── left:
+ │ │ @ CallNode (location: (19,0)-(19,5))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (19,0)-(19,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (19,4)-(19,5))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (19,4)-(19,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right:
+ │ │ @ CallNode (location: (19,9)-(19,14))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (19,9)-(19,12) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (19,13)-(19,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (19,13)-(19,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (19,6)-(19,8) = "or"
+ ├── @ CallNode (location: (21,0)-(21,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (21,4)-(21,9))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (21,4)-(21,7) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (21,8)-(21,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (21,8)-(21,9))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!
+ │ ├── message_loc: (21,0)-(21,3) = "not"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ LocalVariableWriteNode (location: (23,0)-(23,17))
+ │ ├── name: :foo
+ │ ├── depth: 0
+ │ ├── name_loc: (23,0)-(23,3) = "foo"
+ │ ├── value:
+ │ │ @ LocalVariableWriteNode (location: (23,6)-(23,17))
+ │ │ ├── name: :bar
+ │ │ ├── depth: 0
+ │ │ ├── name_loc: (23,6)-(23,9) = "bar"
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (23,12)-(23,17))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (23,12)-(23,15) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (23,16)-(23,17))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (23,16)-(23,17))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (23,10)-(23,11) = "="
+ │ └── operator_loc: (23,4)-(23,5) = "="
+ ├── @ DefNode (location: (25,0)-(25,15))
+ │ ├── name: :foo
+ │ ├── name_loc: (25,4)-(25,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (25,10)-(25,15))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (25,10)-(25,15))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (25,10)-(25,13) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (25,14)-(25,15))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (25,14)-(25,15))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (25,0)-(25,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: (25,8)-(25,9) = "="
+ │ └── end_keyword_loc: ∅
+ ├── @ CallNode (location: (27,0)-(27,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (27,0)-(27,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: (27,1)-(27,2) = "."
+ │ ├── name: :foo
+ │ ├── message_loc: (27,2)-(27,5) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (27,6)-(27,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (27,6)-(27,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (29,0)-(29,11))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (29,0)-(29,5))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ IntegerNode (location: (29,0)-(29,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── call_operator_loc: (29,1)-(29,2) = "."
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (29,2)-(29,5) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (29,5)-(29,6) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (29,6)-(29,9) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (29,10)-(29,11))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (29,10)-(29,11))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (31,0)-(31,14))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (31,0)-(31,8))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (31,0)-(31,5))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ IntegerNode (location: (31,0)-(31,1))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── call_operator_loc: (31,1)-(31,2) = "."
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (31,2)-(31,5) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :[]
+ │ │ ├── message_loc: (31,5)-(31,8) = "[2]"
+ │ │ ├── opening_loc: (31,5)-(31,6) = "["
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (31,6)-(31,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (31,6)-(31,7))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── closing_loc: (31,7)-(31,8) = "]"
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (31,8)-(31,9) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (31,9)-(31,12) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (31,13)-(31,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (31,13)-(31,14))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (33,0)-(33,14))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (33,0)-(33,8))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ IntegerNode (location: (33,0)-(33,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── call_operator_loc: (33,1)-(33,2) = "."
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (33,2)-(33,5) = "foo"
+ │ │ ├── opening_loc: (33,5)-(33,6) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (33,6)-(33,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (33,6)-(33,7))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── closing_loc: (33,7)-(33,8) = ")"
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (33,8)-(33,9) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (33,9)-(33,12) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (33,13)-(33,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (33,13)-(33,14))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (35,0)-(35,15))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (35,0)-(35,9))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ IntegerNode (location: (35,0)-(35,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── call_operator_loc: (35,1)-(35,2) = "."
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (35,2)-(35,5) = "foo"
+ │ │ ├── opening_loc: (35,5)-(35,6) = "("
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: (35,8)-(35,9) = ")"
+ │ │ └── block:
+ │ │ @ BlockArgumentNode (location: (35,6)-(35,8))
+ │ │ ├── expression:
+ │ │ │ @ IntegerNode (location: (35,7)-(35,8))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── operator_loc: (35,6)-(35,7) = "&"
+ │ ├── call_operator_loc: (35,9)-(35,10) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (35,10)-(35,13) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (35,14)-(35,15))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (35,14)-(35,15))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ AndNode (location: (37,0)-(37,17))
+ │ ├── left:
+ │ │ @ CallNode (location: (37,0)-(37,6))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (37,1)-(37,6))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (37,1)-(37,4) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (37,5)-(37,6))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (37,5)-(37,6))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :!
+ │ │ ├── message_loc: (37,0)-(37,1) = "!"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right:
+ │ │ @ CallNode (location: (37,11)-(37,17))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (37,12)-(37,17))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (37,12)-(37,15) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (37,16)-(37,17))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (37,16)-(37,17))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :!
+ │ │ ├── message_loc: (37,11)-(37,12) = "!"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (37,7)-(37,10) = "and"
+ ├── @ OrNode (location: (39,0)-(39,16))
+ │ ├── left:
+ │ │ @ CallNode (location: (39,0)-(39,6))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (39,1)-(39,6))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (39,1)-(39,4) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (39,5)-(39,6))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (39,5)-(39,6))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :!
+ │ │ ├── message_loc: (39,0)-(39,1) = "!"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right:
+ │ │ @ CallNode (location: (39,10)-(39,16))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (39,11)-(39,16))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (39,11)-(39,14) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (39,15)-(39,16))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (39,15)-(39,16))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :!
+ │ │ ├── message_loc: (39,10)-(39,11) = "!"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (39,7)-(39,9) = "or"
+ └── @ CallNode (location: (41,0)-(41,10))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (41,4)-(41,10))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (41,5)-(41,10))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (41,5)-(41,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (41,9)-(41,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (41,9)-(41,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!
+ │ ├── message_loc: (41,4)-(41,5) = "!"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :!
+ ├── message_loc: (41,0)-(41,3) = "not"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/comments.txt b/test/prism/snapshots/comments.txt
new file mode 100644
index 0000000000..b7088adcd5
--- /dev/null
+++ b/test/prism/snapshots/comments.txt
@@ -0,0 +1,145 @@
+@ ProgramNode (location: (1,0)-(24,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(24,5))
+ └── body: (length: 9)
+ ├── @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (3,0)-(3,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :b
+ │ ├── message_loc: (3,0)-(3,1) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (5,0)-(5,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :c
+ │ ├── message_loc: (5,0)-(5,1) = "c"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (6,0)-(6,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :d
+ │ ├── message_loc: (6,0)-(6,1) = "d"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (8,0)-(10,4))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (8,0)-(8,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :e
+ │ │ ├── message_loc: (8,0)-(8,1) = "e"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (10,2)-(10,3) = "."
+ │ ├── name: :f
+ │ ├── message_loc: (10,3)-(10,4) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (12,0)-(14,2))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (12,0)-(12,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :g
+ │ │ ├── message_loc: (12,0)-(12,1) = "g"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (14,0)-(14,1) = "."
+ │ ├── name: :h
+ │ ├── message_loc: (14,1)-(14,2) = "h"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (16,0)-(17,2))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (16,0)-(16,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :i
+ │ │ ├── message_loc: (16,0)-(16,1) = "i"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (17,0)-(17,1) = "."
+ │ ├── name: :j
+ │ ├── message_loc: (17,1)-(17,2) = "j"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (19,0)-(20,4))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (19,0)-(19,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :k
+ │ │ ├── message_loc: (19,0)-(19,1) = "k"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (20,2)-(20,3) = "."
+ │ ├── name: :l
+ │ ├── message_loc: (20,3)-(20,4) = "l"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (22,0)-(24,5))
+ ├── flags: safe_navigation
+ ├── receiver:
+ │ @ CallNode (location: (22,0)-(22,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (22,0)-(22,1) = "m"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (24,2)-(24,4) = "&."
+ ├── name: :n
+ ├── message_loc: (24,4)-(24,5) = "n"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/constants.txt b/test/prism/snapshots/constants.txt
new file mode 100644
index 0000000000..59e234148a
--- /dev/null
+++ b/test/prism/snapshots/constants.txt
@@ -0,0 +1,1242 @@
+@ ProgramNode (location: (1,0)-(184,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(184,10))
+ └── body: (length: 90)
+ ├── @ ConstantPathNode (location: (1,0)-(1,4))
+ │ ├── parent:
+ │ │ @ ConstantReadNode (location: (1,0)-(1,1))
+ │ │ └── name: :A
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (1,3)-(1,4))
+ │ │ └── name: :B
+ │ └── delimiter_loc: (1,1)-(1,3) = "::"
+ ├── @ ConstantPathNode (location: (3,0)-(3,7))
+ │ ├── parent:
+ │ │ @ ConstantPathNode (location: (3,0)-(3,4))
+ │ │ ├── parent:
+ │ │ │ @ ConstantReadNode (location: (3,0)-(3,1))
+ │ │ │ └── name: :A
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (3,3)-(3,4))
+ │ │ │ └── name: :B
+ │ │ └── delimiter_loc: (3,1)-(3,3) = "::"
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (3,6)-(3,7))
+ │ │ └── name: :C
+ │ └── delimiter_loc: (3,4)-(3,6) = "::"
+ ├── @ ConstantPathNode (location: (5,0)-(5,4))
+ │ ├── parent:
+ │ │ @ CallNode (location: (5,0)-(5,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (5,0)-(5,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (5,3)-(5,4))
+ │ │ └── name: :B
+ │ └── delimiter_loc: (5,1)-(5,3) = "::"
+ ├── @ ConstantPathWriteNode (location: (7,0)-(7,8))
+ │ ├── target:
+ │ │ @ ConstantPathNode (location: (7,0)-(7,4))
+ │ │ ├── parent:
+ │ │ │ @ ConstantReadNode (location: (7,0)-(7,1))
+ │ │ │ └── name: :A
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (7,3)-(7,4))
+ │ │ │ └── name: :B
+ │ │ └── delimiter_loc: (7,1)-(7,3) = "::"
+ │ ├── operator_loc: (7,5)-(7,6) = "="
+ │ └── value:
+ │ @ IntegerNode (location: (7,7)-(7,8))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ ConstantWriteNode (location: (9,0)-(9,5))
+ │ ├── name: :A
+ │ ├── name_loc: (9,0)-(9,1) = "A"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (9,4)-(9,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (9,2)-(9,3) = "="
+ ├── @ ConstantReadNode (location: (11,0)-(11,3))
+ │ └── name: :ABC
+ ├── @ CallNode (location: (13,0)-(13,5))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :Foo
+ │ ├── message_loc: (13,0)-(13,3) = "Foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (13,4)-(13,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (13,4)-(13,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (15,0)-(15,8))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :Foo
+ │ ├── message_loc: (15,0)-(15,3) = "Foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (15,4)-(15,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ SplatNode (location: (15,4)-(15,8))
+ │ │ ├── operator_loc: (15,4)-(15,5) = "*"
+ │ │ └── expression:
+ │ │ @ CallNode (location: (15,5)-(15,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (15,5)-(15,8) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (17,0)-(17,9))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :Foo
+ │ ├── message_loc: (17,0)-(17,3) = "Foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (17,4)-(17,9))
+ │ │ ├── flags: contains_keyword_splat
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (17,4)-(17,9))
+ │ │ ├── flags: ∅
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocSplatNode (location: (17,4)-(17,9))
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (17,6)-(17,9))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (17,6)-(17,9) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (17,4)-(17,6) = "**"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (19,0)-(19,8))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :Foo
+ │ ├── message_loc: (19,0)-(19,3) = "Foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockArgumentNode (location: (19,4)-(19,8))
+ │ ├── expression:
+ │ │ @ CallNode (location: (19,5)-(19,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (19,5)-(19,8) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (19,4)-(19,5) = "&"
+ ├── @ CallNode (location: (21,0)-(21,13))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (21,0)-(21,3))
+ │ │ └── name: :Foo
+ │ ├── call_operator_loc: (21,3)-(21,5) = "::"
+ │ ├── name: :Bar
+ │ ├── message_loc: (21,5)-(21,8) = "Bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (21,9)-(21,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ SplatNode (location: (21,9)-(21,13))
+ │ │ ├── operator_loc: (21,9)-(21,10) = "*"
+ │ │ └── expression:
+ │ │ @ CallNode (location: (21,10)-(21,13))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (21,10)-(21,13) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (23,0)-(23,14))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (23,0)-(23,3))
+ │ │ └── name: :Foo
+ │ ├── call_operator_loc: (23,3)-(23,5) = "::"
+ │ ├── name: :Bar
+ │ ├── message_loc: (23,5)-(23,8) = "Bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (23,9)-(23,14))
+ │ │ ├── flags: contains_keyword_splat
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (23,9)-(23,14))
+ │ │ ├── flags: ∅
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocSplatNode (location: (23,9)-(23,14))
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (23,11)-(23,14))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (23,11)-(23,14) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (23,9)-(23,11) = "**"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (25,0)-(25,13))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (25,0)-(25,3))
+ │ │ └── name: :Foo
+ │ ├── call_operator_loc: (25,3)-(25,5) = "::"
+ │ ├── name: :Bar
+ │ ├── message_loc: (25,5)-(25,8) = "Bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockArgumentNode (location: (25,9)-(25,13))
+ │ ├── expression:
+ │ │ @ CallNode (location: (25,10)-(25,13))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (25,10)-(25,13) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (25,9)-(25,10) = "&"
+ ├── @ CallNode (location: (27,0)-(27,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantPathNode (location: (27,0)-(27,3))
+ │ │ ├── parent: ∅
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (27,2)-(27,3))
+ │ │ │ └── name: :A
+ │ │ └── delimiter_loc: (27,0)-(27,2) = "::"
+ │ ├── call_operator_loc: (27,3)-(27,5) = "::"
+ │ ├── name: :foo
+ │ ├── message_loc: (27,5)-(27,8) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ ConstantPathWriteNode (location: (29,0)-(29,7))
+ │ ├── target:
+ │ │ @ ConstantPathNode (location: (29,0)-(29,3))
+ │ │ ├── parent: ∅
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (29,2)-(29,3))
+ │ │ │ └── name: :A
+ │ │ └── delimiter_loc: (29,0)-(29,2) = "::"
+ │ ├── operator_loc: (29,4)-(29,5) = "="
+ │ └── value:
+ │ @ IntegerNode (location: (29,6)-(29,7))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ ConstantPathWriteNode (location: (31,0)-(31,10))
+ │ ├── target:
+ │ │ @ ConstantPathNode (location: (31,0)-(31,6))
+ │ │ ├── parent:
+ │ │ │ @ ConstantPathNode (location: (31,0)-(31,3))
+ │ │ │ ├── parent: ∅
+ │ │ │ ├── child:
+ │ │ │ │ @ ConstantReadNode (location: (31,2)-(31,3))
+ │ │ │ │ └── name: :A
+ │ │ │ └── delimiter_loc: (31,0)-(31,2) = "::"
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (31,5)-(31,6))
+ │ │ │ └── name: :B
+ │ │ └── delimiter_loc: (31,3)-(31,5) = "::"
+ │ ├── operator_loc: (31,7)-(31,8) = "="
+ │ └── value:
+ │ @ IntegerNode (location: (31,9)-(31,10))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ ConstantPathNode (location: (33,0)-(33,6))
+ │ ├── parent:
+ │ │ @ ConstantPathNode (location: (33,0)-(33,3))
+ │ │ ├── parent: ∅
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (33,2)-(33,3))
+ │ │ │ └── name: :A
+ │ │ └── delimiter_loc: (33,0)-(33,2) = "::"
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (33,5)-(33,6))
+ │ │ └── name: :B
+ │ └── delimiter_loc: (33,3)-(33,5) = "::"
+ ├── @ ConstantPathNode (location: (35,0)-(35,3))
+ │ ├── parent: ∅
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (35,2)-(35,3))
+ │ │ └── name: :A
+ │ └── delimiter_loc: (35,0)-(35,2) = "::"
+ ├── @ CallNode (location: (37,0)-(37,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (37,0)-(37,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (37,1)-(37,3) = "::"
+ │ ├── name: :false
+ │ ├── message_loc: (37,3)-(37,8) = "false"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (39,0)-(39,10))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantPathNode (location: (39,0)-(39,4))
+ │ │ ├── parent:
+ │ │ │ @ ConstantReadNode (location: (39,0)-(39,1))
+ │ │ │ └── name: :A
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (39,3)-(39,4))
+ │ │ │ └── name: :B
+ │ │ └── delimiter_loc: (39,1)-(39,3) = "::"
+ │ ├── call_operator_loc: (39,4)-(39,6) = "::"
+ │ ├── name: :true
+ │ ├── message_loc: (39,6)-(39,10) = "true"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (41,0)-(41,4))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (41,0)-(41,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (41,1)-(41,3) = "::"
+ │ ├── name: :&
+ │ ├── message_loc: (41,3)-(41,4) = "&"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (43,0)-(43,4))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (43,0)-(43,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (43,1)-(43,3) = "::"
+ │ ├── name: :`
+ │ ├── message_loc: (43,3)-(43,4) = "`"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (45,0)-(45,4))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (45,0)-(45,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (45,1)-(45,3) = "::"
+ │ ├── name: :!
+ │ ├── message_loc: (45,3)-(45,4) = "!"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (47,0)-(47,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (47,0)-(47,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (47,1)-(47,3) = "::"
+ │ ├── name: :!=
+ │ ├── message_loc: (47,3)-(47,5) = "!="
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (49,0)-(49,4))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (49,0)-(49,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (49,1)-(49,3) = "::"
+ │ ├── name: :^
+ │ ├── message_loc: (49,3)-(49,4) = "^"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (51,0)-(51,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (51,0)-(51,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (51,1)-(51,3) = "::"
+ │ ├── name: :==
+ │ ├── message_loc: (51,3)-(51,5) = "=="
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (53,0)-(53,6))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (53,0)-(53,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (53,1)-(53,3) = "::"
+ │ ├── name: :===
+ │ ├── message_loc: (53,3)-(53,6) = "==="
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (55,0)-(55,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (55,0)-(55,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (55,1)-(55,3) = "::"
+ │ ├── name: :=~
+ │ ├── message_loc: (55,3)-(55,5) = "=~"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (57,0)-(57,4))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (57,0)-(57,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (57,1)-(57,3) = "::"
+ │ ├── name: :>
+ │ ├── message_loc: (57,3)-(57,4) = ">"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (59,0)-(59,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (59,0)-(59,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (59,1)-(59,3) = "::"
+ │ ├── name: :>=
+ │ ├── message_loc: (59,3)-(59,5) = ">="
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (61,0)-(61,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (61,0)-(61,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (61,1)-(61,3) = "::"
+ │ ├── name: :>>
+ │ ├── message_loc: (61,3)-(61,5) = ">>"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (63,0)-(63,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (63,0)-(63,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (63,1)-(63,3) = "::"
+ │ ├── name: :<<
+ │ ├── message_loc: (63,3)-(63,5) = "<<"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ ConstantPathNode (location: (65,0)-(67,1))
+ │ ├── parent:
+ │ │ @ ConstantReadNode (location: (65,0)-(65,1))
+ │ │ └── name: :A
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (67,0)-(67,1))
+ │ │ └── name: :C
+ │ └── delimiter_loc: (65,1)-(65,3) = "::"
+ ├── @ CallNode (location: (69,0)-(69,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (69,0)-(69,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (69,1)-(69,3) = "::"
+ │ ├── name: :alias
+ │ ├── message_loc: (69,3)-(69,8) = "alias"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (71,0)-(71,6))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (71,0)-(71,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (71,1)-(71,3) = "::"
+ │ ├── name: :and
+ │ ├── message_loc: (71,3)-(71,6) = "and"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (73,0)-(73,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (73,0)-(73,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (73,1)-(73,3) = "::"
+ │ ├── name: :begin
+ │ ├── message_loc: (73,3)-(73,8) = "begin"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ ConstantPathNode (location: (75,0)-(75,8))
+ │ ├── parent:
+ │ │ @ ConstantReadNode (location: (75,0)-(75,1))
+ │ │ └── name: :A
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (75,3)-(75,8))
+ │ │ └── name: :BEGIN
+ │ └── delimiter_loc: (75,1)-(75,3) = "::"
+ ├── @ CallNode (location: (77,0)-(77,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (77,0)-(77,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (77,1)-(77,3) = "::"
+ │ ├── name: :break
+ │ ├── message_loc: (77,3)-(77,8) = "break"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (79,0)-(79,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (79,0)-(79,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (79,1)-(79,3) = "::"
+ │ ├── name: :class
+ │ ├── message_loc: (79,3)-(79,8) = "class"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (81,0)-(81,6))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (81,0)-(81,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (81,1)-(81,3) = "::"
+ │ ├── name: :def
+ │ ├── message_loc: (81,3)-(81,6) = "def"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (83,0)-(83,10))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (83,0)-(83,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (83,1)-(83,3) = "::"
+ │ ├── name: :defined
+ │ ├── message_loc: (83,3)-(83,10) = "defined"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (85,0)-(85,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (85,0)-(85,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (85,1)-(85,3) = "::"
+ │ ├── name: :do
+ │ ├── message_loc: (85,3)-(85,5) = "do"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (87,0)-(87,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (87,0)-(87,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (87,1)-(87,3) = "::"
+ │ ├── name: :else
+ │ ├── message_loc: (87,3)-(87,7) = "else"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (89,0)-(89,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (89,0)-(89,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (89,1)-(89,3) = "::"
+ │ ├── name: :elsif
+ │ ├── message_loc: (89,3)-(89,8) = "elsif"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (91,0)-(91,6))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (91,0)-(91,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (91,1)-(91,3) = "::"
+ │ ├── name: :end
+ │ ├── message_loc: (91,3)-(91,6) = "end"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ ConstantPathNode (location: (93,0)-(93,6))
+ │ ├── parent:
+ │ │ @ ConstantReadNode (location: (93,0)-(93,1))
+ │ │ └── name: :A
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (93,3)-(93,6))
+ │ │ └── name: :END
+ │ └── delimiter_loc: (93,1)-(93,3) = "::"
+ ├── @ CallNode (location: (95,0)-(95,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (95,0)-(95,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (95,1)-(95,3) = "::"
+ │ ├── name: :ensure
+ │ ├── message_loc: (95,3)-(95,9) = "ensure"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (97,0)-(97,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (97,0)-(97,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (97,1)-(97,3) = "::"
+ │ ├── name: :false
+ │ ├── message_loc: (97,3)-(97,8) = "false"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (99,0)-(99,6))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (99,0)-(99,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (99,1)-(99,3) = "::"
+ │ ├── name: :for
+ │ ├── message_loc: (99,3)-(99,6) = "for"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (101,0)-(101,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (101,0)-(101,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (101,1)-(101,3) = "::"
+ │ ├── name: :if
+ │ ├── message_loc: (101,3)-(101,5) = "if"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (103,0)-(103,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (103,0)-(103,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (103,1)-(103,3) = "::"
+ │ ├── name: :in
+ │ ├── message_loc: (103,3)-(103,5) = "in"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (105,0)-(105,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (105,0)-(105,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (105,1)-(105,3) = "::"
+ │ ├── name: :next
+ │ ├── message_loc: (105,3)-(105,7) = "next"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (107,0)-(107,6))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (107,0)-(107,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (107,1)-(107,3) = "::"
+ │ ├── name: :nil
+ │ ├── message_loc: (107,3)-(107,6) = "nil"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (109,0)-(109,6))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (109,0)-(109,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (109,1)-(109,3) = "::"
+ │ ├── name: :not
+ │ ├── message_loc: (109,3)-(109,6) = "not"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (111,0)-(111,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (111,0)-(111,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (111,1)-(111,3) = "::"
+ │ ├── name: :or
+ │ ├── message_loc: (111,3)-(111,5) = "or"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (113,0)-(113,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (113,0)-(113,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (113,1)-(113,3) = "::"
+ │ ├── name: :redo
+ │ ├── message_loc: (113,3)-(113,7) = "redo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (115,0)-(115,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (115,0)-(115,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (115,1)-(115,3) = "::"
+ │ ├── name: :rescue
+ │ ├── message_loc: (115,3)-(115,9) = "rescue"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (117,0)-(117,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (117,0)-(117,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (117,1)-(117,3) = "::"
+ │ ├── name: :retry
+ │ ├── message_loc: (117,3)-(117,8) = "retry"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (119,0)-(119,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (119,0)-(119,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (119,1)-(119,3) = "::"
+ │ ├── name: :return
+ │ ├── message_loc: (119,3)-(119,9) = "return"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (121,0)-(121,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (121,0)-(121,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (121,1)-(121,3) = "::"
+ │ ├── name: :self
+ │ ├── message_loc: (121,3)-(121,7) = "self"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (123,0)-(123,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (123,0)-(123,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (123,1)-(123,3) = "::"
+ │ ├── name: :super
+ │ ├── message_loc: (123,3)-(123,8) = "super"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (125,0)-(125,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (125,0)-(125,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (125,1)-(125,3) = "::"
+ │ ├── name: :then
+ │ ├── message_loc: (125,3)-(125,7) = "then"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (127,0)-(127,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (127,0)-(127,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (127,1)-(127,3) = "::"
+ │ ├── name: :true
+ │ ├── message_loc: (127,3)-(127,7) = "true"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (129,0)-(129,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (129,0)-(129,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (129,1)-(129,3) = "::"
+ │ ├── name: :undef
+ │ ├── message_loc: (129,3)-(129,8) = "undef"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (131,0)-(131,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (131,0)-(131,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (131,1)-(131,3) = "::"
+ │ ├── name: :unless
+ │ ├── message_loc: (131,3)-(131,9) = "unless"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (133,0)-(133,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (133,0)-(133,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (133,1)-(133,3) = "::"
+ │ ├── name: :until
+ │ ├── message_loc: (133,3)-(133,8) = "until"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (135,0)-(135,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (135,0)-(135,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (135,1)-(135,3) = "::"
+ │ ├── name: :when
+ │ ├── message_loc: (135,3)-(135,7) = "when"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (137,0)-(137,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (137,0)-(137,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (137,1)-(137,3) = "::"
+ │ ├── name: :while
+ │ ├── message_loc: (137,3)-(137,8) = "while"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (139,0)-(139,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (139,0)-(139,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (139,1)-(139,3) = "::"
+ │ ├── name: :yield
+ │ ├── message_loc: (139,3)-(139,8) = "yield"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (141,0)-(141,15))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (141,0)-(141,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (141,1)-(141,3) = "::"
+ │ ├── name: :__ENCODING__
+ │ ├── message_loc: (141,3)-(141,15) = "__ENCODING__"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (143,0)-(143,11))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (143,0)-(143,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (143,1)-(143,3) = "::"
+ │ ├── name: :__FILE__
+ │ ├── message_loc: (143,3)-(143,11) = "__FILE__"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (145,0)-(145,11))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (145,0)-(145,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (145,1)-(145,3) = "::"
+ │ ├── name: :__LINE__
+ │ ├── message_loc: (145,3)-(145,11) = "__LINE__"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (147,0)-(147,4))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (147,0)-(147,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (147,1)-(147,3) = "::"
+ │ ├── name: :<
+ │ ├── message_loc: (147,3)-(147,4) = "<"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (149,0)-(149,6))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (149,0)-(149,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (149,1)-(149,3) = "::"
+ │ ├── name: :<=>
+ │ ├── message_loc: (149,3)-(149,6) = "<=>"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (151,0)-(151,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (151,0)-(151,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (151,1)-(151,3) = "::"
+ │ ├── name: :<<
+ │ ├── message_loc: (151,3)-(151,5) = "<<"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (153,0)-(153,4))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (153,0)-(153,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (153,1)-(153,3) = "::"
+ │ ├── name: :-
+ │ ├── message_loc: (153,3)-(153,4) = "-"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (155,0)-(155,4))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (155,0)-(155,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (155,1)-(155,3) = "::"
+ │ ├── name: :%
+ │ ├── message_loc: (155,3)-(155,4) = "%"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (157,0)-(157,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (157,0)-(157,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (157,1)-(157,3) = "::"
+ │ ├── name: :%
+ │ ├── message_loc: (157,3)-(157,4) = "%"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (157,4)-(157,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (157,4)-(157,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :i
+ │ │ ├── message_loc: (157,4)-(157,5) = "i"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (159,0)-(159,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (159,0)-(159,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (159,1)-(159,3) = "::"
+ │ ├── name: :%
+ │ ├── message_loc: (159,3)-(159,4) = "%"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (159,4)-(159,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (159,4)-(159,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :w
+ │ │ ├── message_loc: (159,4)-(159,5) = "w"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (161,0)-(161,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (161,0)-(161,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (161,1)-(161,3) = "::"
+ │ ├── name: :%
+ │ ├── message_loc: (161,3)-(161,4) = "%"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (161,4)-(161,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (161,4)-(161,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :x
+ │ │ ├── message_loc: (161,4)-(161,5) = "x"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (163,0)-(163,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (163,0)-(163,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (163,1)-(163,3) = "::"
+ │ ├── name: :%
+ │ ├── message_loc: (163,3)-(163,4) = "%"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (163,4)-(163,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ ConstantReadNode (location: (163,4)-(163,5))
+ │ │ └── name: :I
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (165,0)-(165,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (165,0)-(165,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (165,1)-(165,3) = "::"
+ │ ├── name: :%
+ │ ├── message_loc: (165,3)-(165,4) = "%"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (165,4)-(165,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ ConstantReadNode (location: (165,4)-(165,5))
+ │ │ └── name: :W
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (167,0)-(167,4))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (167,0)-(167,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (167,1)-(167,3) = "::"
+ │ ├── name: :|
+ │ ├── message_loc: (167,3)-(167,4) = "|"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (169,0)-(169,4))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (169,0)-(169,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (169,1)-(169,3) = "::"
+ │ ├── name: :+
+ │ ├── message_loc: (169,3)-(169,4) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (171,0)-(171,4))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (171,0)-(171,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (171,1)-(171,3) = "::"
+ │ ├── name: :/
+ │ ├── message_loc: (171,3)-(171,4) = "/"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (173,0)-(173,4))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (173,0)-(173,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (173,1)-(173,3) = "::"
+ │ ├── name: :*
+ │ ├── message_loc: (173,3)-(173,4) = "*"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (175,0)-(175,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (175,0)-(175,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (175,1)-(175,3) = "::"
+ │ ├── name: :**
+ │ ├── message_loc: (175,3)-(175,5) = "**"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (177,0)-(177,4))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (177,0)-(177,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (177,1)-(177,3) = "::"
+ │ ├── name: :~
+ │ ├── message_loc: (177,3)-(177,4) = "~"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ ConstantPathNode (location: (179,0)-(180,1))
+ │ ├── parent:
+ │ │ @ CallNode (location: (179,0)-(179,4))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ ConstantReadNode (location: (179,0)-(179,1))
+ │ │ │ └── name: :A
+ │ │ ├── call_operator_loc: (179,1)-(179,3) = "::"
+ │ │ ├── name: :_
+ │ │ ├── message_loc: (179,3)-(179,4) = "_"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (180,0)-(180,1))
+ │ │ └── name: :C
+ │ └── delimiter_loc: (179,4)-(179,6) = "::"
+ └── @ RangeNode (location: (182,0)-(184,10))
+ ├── flags: ∅
+ ├── left:
+ │ @ CallNode (location: (182,0)-(182,4))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (182,0)-(182,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (182,1)-(182,3) = "::"
+ │ ├── name: :_
+ │ ├── message_loc: (182,3)-(182,4) = "_"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── right:
+ │ @ CallNode (location: (184,0)-(184,10))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (184,0)-(184,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (184,1)-(184,3) = "::"
+ │ ├── name: :__END__
+ │ ├── message_loc: (184,3)-(184,10) = "__END__"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── operator_loc: (182,4)-(182,6) = ".."
diff --git a/test/prism/snapshots/dash_heredocs.txt b/test/prism/snapshots/dash_heredocs.txt
new file mode 100644
index 0000000000..bd2b05ea0d
--- /dev/null
+++ b/test/prism/snapshots/dash_heredocs.txt
@@ -0,0 +1,260 @@
+@ ProgramNode (location: (1,0)-(57,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(57,11))
+ └── body: (length: 13)
+ ├── @ StringNode (location: (1,0)-(1,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,6) = "<<-EOF"
+ │ ├── content_loc: (2,0)-(3,0) = " a\n"
+ │ ├── closing_loc: (3,0)-(4,0) = "EOF\n"
+ │ └── unescaped: " a\n"
+ ├── @ CallNode (location: (5,0)-(5,20))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ StringNode (location: (5,0)-(5,8))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (5,0)-(5,8) = "<<-FIRST"
+ │ │ ├── content_loc: (6,0)-(7,0) = " a\n"
+ │ │ ├── closing_loc: (7,0)-(8,0) = "FIRST\n"
+ │ │ └── unescaped: " a\n"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (5,9)-(5,10) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (5,11)-(5,20))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (5,11)-(5,20))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (5,11)-(5,20) = "<<-SECOND"
+ │ │ ├── content_loc: (8,0)-(9,0) = " b\n"
+ │ │ ├── closing_loc: (9,0)-(10,0) = "SECOND\n"
+ │ │ └── unescaped: " b\n"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ InterpolatedXStringNode (location: (11,0)-(11,8))
+ │ ├── opening_loc: (11,0)-(11,8) = "<<-`EOF`"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (12,0)-(13,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (12,0)-(13,0) = " a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: " a\n"
+ │ │ ├── @ EmbeddedStatementsNode (location: (13,0)-(13,4))
+ │ │ │ ├── opening_loc: (13,0)-(13,2) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (13,2)-(13,3))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (13,2)-(13,3))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── message_loc: (13,2)-(13,3) = "b"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── closing_loc: (13,3)-(13,4) = "}"
+ │ │ └── @ StringNode (location: (13,4)-(14,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (13,4)-(14,0) = "\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\n"
+ │ └── closing_loc: (14,0)-(15,0) = "EOF\n"
+ ├── @ StringNode (location: (16,0)-(16,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (16,0)-(16,6) = "<<-EOF"
+ │ ├── content_loc: (17,0)-(18,0) = " a\n"
+ │ ├── closing_loc: (18,0)-(19,0) = "EOF\n"
+ │ └── unescaped: " a\n"
+ ├── @ StringNode (location: (20,0)-(20,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (20,0)-(20,6) = "<<-EOF"
+ │ ├── content_loc: (21,0)-(23,0) = " a\n b\n"
+ │ ├── closing_loc: (23,0)-(24,0) = " EOF\n"
+ │ └── unescaped: " a\n b\n"
+ ├── @ InterpolatedStringNode (location: (25,0)-(25,8))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (25,0)-(25,8) = "<<-\"EOF\""
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (26,0)-(27,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (26,0)-(27,0) = " a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: " a\n"
+ │ │ ├── @ EmbeddedStatementsNode (location: (27,0)-(27,4))
+ │ │ │ ├── opening_loc: (27,0)-(27,2) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (27,2)-(27,3))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (27,2)-(27,3))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── message_loc: (27,2)-(27,3) = "b"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── closing_loc: (27,3)-(27,4) = "}"
+ │ │ └── @ StringNode (location: (27,4)-(28,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (27,4)-(28,0) = "\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\n"
+ │ └── closing_loc: (28,0)-(29,0) = "EOF\n"
+ ├── @ InterpolatedStringNode (location: (30,0)-(30,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (30,0)-(30,6) = "<<-EOF"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (31,0)-(32,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (31,0)-(32,0) = " a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: " a\n"
+ │ │ ├── @ EmbeddedStatementsNode (location: (32,0)-(32,4))
+ │ │ │ ├── opening_loc: (32,0)-(32,2) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (32,2)-(32,3))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (32,2)-(32,3))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── message_loc: (32,2)-(32,3) = "b"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── closing_loc: (32,3)-(32,4) = "}"
+ │ │ └── @ StringNode (location: (32,4)-(33,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (32,4)-(33,0) = "\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\n"
+ │ └── closing_loc: (33,0)-(34,0) = "EOF\n"
+ ├── @ StringNode (location: (35,0)-(35,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (35,0)-(35,2) = "%#"
+ │ ├── content_loc: (35,2)-(35,5) = "abc"
+ │ ├── closing_loc: (35,5)-(35,6) = "#"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (37,0)-(37,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (37,0)-(37,6) = "<<-EOF"
+ │ ├── content_loc: (38,0)-(40,0) = " a\n b\n"
+ │ ├── closing_loc: (40,0)-(41,0) = "EOF\n"
+ │ └── unescaped: " a\n b\n"
+ ├── @ StringNode (location: (42,0)-(42,5))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (42,0)-(42,5) = "<<-''"
+ │ ├── content_loc: (43,0)-(43,0) = ""
+ │ ├── closing_loc: (43,0)-(44,0) = "\n"
+ │ └── unescaped: ""
+ ├── @ StringNode (location: (45,0)-(45,8))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (45,0)-(45,8) = "<<-'EOF'"
+ │ ├── content_loc: (46,0)-(47,0) = " a \#{1}\n"
+ │ ├── closing_loc: (47,0)-(48,0) = "EOF\n"
+ │ └── unescaped: " a \#{1}\n"
+ ├── @ CallNode (location: (49,0)-(49,11))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ StringNode (location: (49,0)-(49,4))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (49,0)-(49,4) = "<<-A"
+ │ │ ├── content_loc: (50,0)-(51,0) = " a\n"
+ │ │ ├── closing_loc: (51,0)-(52,0) = "A\n"
+ │ │ └── unescaped: " a\n"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (49,5)-(49,6) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (49,7)-(49,11))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (49,7)-(49,11))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (49,7)-(49,11) = "<<-B"
+ │ │ ├── parts: (length: 3)
+ │ │ │ ├── @ StringNode (location: (52,0)-(53,2))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (52,0)-(53,2) = " b\n "
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: " b\n "
+ │ │ │ ├── @ EmbeddedStatementsNode (location: (53,2)-(54,3))
+ │ │ │ │ ├── opening_loc: (53,2)-(53,4) = "\#{"
+ │ │ │ │ ├── statements:
+ │ │ │ │ │ @ StatementsNode (location: (53,4)-(53,5))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ IntegerNode (location: (53,4)-(53,5))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 2
+ │ │ │ │ └── closing_loc: (54,2)-(54,3) = "}"
+ │ │ │ └── @ StringNode (location: (54,3)-(55,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (54,3)-(55,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── closing_loc: (55,0)-(56,0) = "B\n"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (57,0)-(57,11))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ StringNode (location: (57,0)-(57,4))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (57,0)-(57,4) = "<<-A"
+ │ ├── content_loc: (58,0)-(59,0) = " a\n"
+ │ ├── closing_loc: (59,0)-(60,0) = "A\n"
+ │ └── unescaped: " a\n"
+ ├── call_operator_loc: ∅
+ ├── name: :+
+ ├── message_loc: (57,5)-(57,6) = "+"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (57,7)-(57,11))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ InterpolatedStringNode (location: (57,7)-(57,11))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (57,7)-(57,11) = "<<-B"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (60,0)-(61,2))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (60,0)-(61,2) = " b\n "
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: " b\n "
+ │ │ ├── @ EmbeddedStatementsNode (location: (61,2)-(62,4))
+ │ │ │ ├── opening_loc: (61,2)-(61,4) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (62,2)-(62,3))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (62,2)-(62,3))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ └── closing_loc: (62,3)-(62,4) = "}"
+ │ │ └── @ StringNode (location: (62,4)-(63,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (62,4)-(63,0) = "\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\n"
+ │ └── closing_loc: (63,0)-(64,0) = "B\n"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/defined.txt b/test/prism/snapshots/defined.txt
new file mode 100644
index 0000000000..53a5081811
--- /dev/null
+++ b/test/prism/snapshots/defined.txt
@@ -0,0 +1,88 @@
+@ ProgramNode (location: (1,0)-(10,1))
+├── locals: [:x]
+└── statements:
+ @ StatementsNode (location: (1,0)-(10,1))
+ └── body: (length: 5)
+ ├── @ AndNode (location: (1,0)-(1,25))
+ │ ├── left:
+ │ │ @ DefinedNode (location: (1,0)-(1,10))
+ │ │ ├── lparen_loc: ∅
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (1,9)-(1,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── rparen_loc: ∅
+ │ │ └── keyword_loc: (1,0)-(1,8) = "defined?"
+ │ ├── right:
+ │ │ @ DefinedNode (location: (1,15)-(1,25))
+ │ │ ├── lparen_loc: ∅
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (1,24)-(1,25))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── rparen_loc: ∅
+ │ │ └── keyword_loc: (1,15)-(1,23) = "defined?"
+ │ └── operator_loc: (1,11)-(1,14) = "and"
+ ├── @ DefinedNode (location: (3,0)-(3,16))
+ │ ├── lparen_loc: (3,8)-(3,9) = "("
+ │ ├── value:
+ │ │ @ LocalVariableOperatorWriteNode (location: (3,9)-(3,15))
+ │ │ ├── name_loc: (3,9)-(3,10) = "x"
+ │ │ ├── operator_loc: (3,11)-(3,13) = "%="
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (3,14)-(3,15))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── name: :x
+ │ │ ├── operator: :%
+ │ │ └── depth: 0
+ │ ├── rparen_loc: (3,15)-(3,16) = ")"
+ │ └── keyword_loc: (3,0)-(3,8) = "defined?"
+ ├── @ DefinedNode (location: (5,0)-(5,21))
+ │ ├── lparen_loc: (5,8)-(5,9) = "("
+ │ ├── value:
+ │ │ @ AndNode (location: (5,9)-(5,20))
+ │ │ ├── left:
+ │ │ │ @ CallNode (location: (5,9)-(5,12))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (5,9)-(5,12) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── right:
+ │ │ │ @ CallNode (location: (5,17)-(5,20))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (5,17)-(5,20) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (5,13)-(5,16) = "and"
+ │ ├── rparen_loc: (5,20)-(5,21) = ")"
+ │ └── keyword_loc: (5,0)-(5,8) = "defined?"
+ ├── @ DefinedNode (location: (7,0)-(7,10))
+ │ ├── lparen_loc: ∅
+ │ ├── value:
+ │ │ @ IntegerNode (location: (7,9)-(7,10))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── rparen_loc: ∅
+ │ └── keyword_loc: (7,0)-(7,8) = "defined?"
+ └── @ DefinedNode (location: (9,0)-(10,1))
+ ├── lparen_loc: (9,8)-(9,9) = "("
+ ├── value:
+ │ @ StringNode (location: (9,9)-(9,14))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (9,9)-(9,10) = "\""
+ │ ├── content_loc: (9,10)-(9,13) = "foo"
+ │ ├── closing_loc: (9,13)-(9,14) = "\""
+ │ └── unescaped: "foo"
+ ├── rparen_loc: (10,0)-(10,1) = ")"
+ └── keyword_loc: (9,0)-(9,8) = "defined?"
diff --git a/test/prism/snapshots/dos_endings.txt b/test/prism/snapshots/dos_endings.txt
new file mode 100644
index 0000000000..1ae15e1e87
--- /dev/null
+++ b/test/prism/snapshots/dos_endings.txt
@@ -0,0 +1,110 @@
+@ ProgramNode (location: (1,0)-(17,20))
+├── locals: [:x, :a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(17,20))
+ └── body: (length: 5)
+ ├── @ CallNode (location: (1,0)-(2,12))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :puts
+ │ ├── message_loc: (1,0)-(1,4) = "puts"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,5)-(2,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (1,5)-(2,12))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (1,5)-(1,9))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: (1,5)-(1,6) = "\""
+ │ │ │ │ ├── content_loc: (1,6)-(1,8) = "hi"
+ │ │ │ │ ├── closing_loc: (1,8)-(1,9) = "\""
+ │ │ │ │ └── unescaped: "hi"
+ │ │ │ └── @ StringNode (location: (2,5)-(2,12))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: (2,5)-(2,6) = "\""
+ │ │ │ ├── content_loc: (2,6)-(2,11) = "there"
+ │ │ │ ├── closing_loc: (2,11)-(2,12) = "\""
+ │ │ │ └── unescaped: "there"
+ │ │ └── closing_loc: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ ArrayNode (location: (4,0)-(5,2))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ SymbolNode (location: (4,3)-(5,1))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (4,3)-(5,1) = "a\\\r\nb"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a\nb"
+ │ ├── opening_loc: (4,0)-(4,3) = "%I{"
+ │ └── closing_loc: (5,1)-(5,2) = "}"
+ ├── @ StringNode (location: (7,0)-(7,4))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (7,0)-(7,4) = "<<-E"
+ │ ├── content_loc: (8,0)-(11,0) = " 1 \\\r\n 2\r\n 3\r\n"
+ │ ├── closing_loc: (11,0)-(12,0) = "E\r\n"
+ │ └── unescaped: " 1 2\n 3\n"
+ ├── @ LocalVariableWriteNode (location: (13,0)-(15,0))
+ │ ├── name: :x
+ │ ├── depth: 0
+ │ ├── name_loc: (13,0)-(13,1) = "x"
+ │ ├── value:
+ │ │ @ StringNode (location: (13,4)-(15,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (13,4)-(14,0) = "%\r\n"
+ │ │ ├── content_loc: (14,0)-(14,0) = ""
+ │ │ ├── closing_loc: (14,0)-(15,0) = "\r\n"
+ │ │ └── unescaped: ""
+ │ └── operator_loc: (13,2)-(13,3) = "="
+ └── @ LocalVariableWriteNode (location: (17,0)-(17,20))
+ ├── name: :a
+ ├── depth: 0
+ ├── name_loc: (17,0)-(17,1) = "a"
+ ├── value:
+ │ @ CallNode (location: (17,4)-(17,20))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (17,4)-(17,7) = "foo"
+ │ ├── opening_loc: (17,7)-(17,8) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (17,8)-(17,19))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (17,8)-(17,19))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ InterpolatedStringNode (location: (17,8)-(17,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (17,8)-(17,14) = "<<~EOF"
+ │ │ │ ├── parts: (length: 2)
+ │ │ │ │ ├── @ StringNode (location: (18,0)-(19,0))
+ │ │ │ │ │ ├── flags: frozen
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── content_loc: (18,0)-(19,0) = "\r\n"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "\n"
+ │ │ │ │ └── @ StringNode (location: (19,0)-(20,0))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (19,0)-(20,0) = " baz\r\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "baz\n"
+ │ │ │ └── closing_loc: (20,0)-(21,0) = " EOF\r\n"
+ │ │ ├── call_operator_loc: (17,14)-(17,15) = "."
+ │ │ ├── name: :chop
+ │ │ ├── message_loc: (17,15)-(17,19) = "chop"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (17,19)-(17,20) = ")"
+ │ └── block: ∅
+ └── operator_loc: (17,2)-(17,3) = "="
diff --git a/test/prism/snapshots/dstring.txt b/test/prism/snapshots/dstring.txt
new file mode 100644
index 0000000000..3978a2f087
--- /dev/null
+++ b/test/prism/snapshots/dstring.txt
@@ -0,0 +1,85 @@
+@ ProgramNode (location: (1,0)-(29,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(29,1))
+ └── body: (length: 8)
+ ├── @ StringNode (location: (1,0)-(2,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,1) = "\""
+ │ ├── content_loc: (1,1)-(2,5) = "foo\n bar"
+ │ ├── closing_loc: (2,5)-(2,6) = "\""
+ │ └── unescaped: "foo\n bar"
+ ├── @ InterpolatedStringNode (location: (4,0)-(5,9))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (4,0)-(4,1) = "\""
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (4,1)-(5,2))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (4,1)-(5,2) = "foo\n "
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo\n "
+ │ │ └── @ EmbeddedStatementsNode (location: (5,2)-(5,8))
+ │ │ ├── opening_loc: (5,2)-(5,4) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (5,4)-(5,7))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (5,4)-(5,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (5,4)-(5,7) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── closing_loc: (5,7)-(5,8) = "}"
+ │ └── closing_loc: (5,8)-(5,9) = "\""
+ ├── @ InterpolatedStringNode (location: (7,0)-(9,2))
+ │ ├── flags: ∅
+ │ ├── opening_loc: ∅
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (7,0)-(8,2))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: (7,0)-(7,1) = "\""
+ │ │ │ ├── content_loc: (7,1)-(8,1) = "fo\no"
+ │ │ │ ├── closing_loc: (8,1)-(8,2) = "\""
+ │ │ │ └── unescaped: "fo\no"
+ │ │ └── @ StringNode (location: (8,3)-(9,2))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: (8,3)-(8,4) = "\""
+ │ │ ├── content_loc: (8,4)-(9,1) = "ba\nr"
+ │ │ ├── closing_loc: (9,1)-(9,2) = "\""
+ │ │ └── unescaped: "ba\nr"
+ │ └── closing_loc: ∅
+ ├── @ StringNode (location: (11,0)-(13,1))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (11,0)-(11,1) = "\""
+ │ ├── content_loc: (11,1)-(13,0) = "\nfoo\\\n"
+ │ ├── closing_loc: (13,0)-(13,1) = "\""
+ │ └── unescaped: "\nfoo"
+ ├── @ StringNode (location: (15,0)-(17,1))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (15,0)-(15,1) = "\""
+ │ ├── content_loc: (15,1)-(17,0) = "\nfoo\\\\\n"
+ │ ├── closing_loc: (17,0)-(17,1) = "\""
+ │ └── unescaped: "\nfoo\\\n"
+ ├── @ StringNode (location: (19,0)-(21,1))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (19,0)-(19,1) = "\""
+ │ ├── content_loc: (19,1)-(21,0) = "\nfoo\\\\\\\n"
+ │ ├── closing_loc: (21,0)-(21,1) = "\""
+ │ └── unescaped: "\nfoo\\"
+ ├── @ StringNode (location: (23,0)-(25,1))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (23,0)-(23,1) = "\""
+ │ ├── content_loc: (23,1)-(25,0) = "\nfoo\\\\\\\\\n"
+ │ ├── closing_loc: (25,0)-(25,1) = "\""
+ │ └── unescaped: "\nfoo\\\\\n"
+ └── @ StringNode (location: (27,0)-(29,1))
+ ├── flags: ∅
+ ├── opening_loc: (27,0)-(27,1) = "\""
+ ├── content_loc: (27,1)-(29,0) = "\nfoo\\\\\\\\\\\n"
+ ├── closing_loc: (29,0)-(29,1) = "\""
+ └── unescaped: "\nfoo\\\\"
diff --git a/test/prism/snapshots/dsym_str.txt b/test/prism/snapshots/dsym_str.txt
new file mode 100644
index 0000000000..33a5e2da21
--- /dev/null
+++ b/test/prism/snapshots/dsym_str.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(2,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,6))
+ └── body: (length: 1)
+ └── @ SymbolNode (location: (1,0)-(2,6))
+ ├── flags: forced_us_ascii_encoding
+ ├── opening_loc: (1,0)-(1,2) = ":\""
+ ├── value_loc: (1,2)-(2,5) = "foo\n bar"
+ ├── closing_loc: (2,5)-(2,6) = "\""
+ └── unescaped: "foo\n bar"
diff --git a/test/prism/snapshots/embdoc_no_newline_at_end.txt b/test/prism/snapshots/embdoc_no_newline_at_end.txt
new file mode 100644
index 0000000000..3a21ce5559
--- /dev/null
+++ b/test/prism/snapshots/embdoc_no_newline_at_end.txt
@@ -0,0 +1,5 @@
+@ ProgramNode (location: (1,0)-(1,0))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,0))
+ └── body: (length: 0)
diff --git a/test/prism/snapshots/emoji_method_calls.txt b/test/prism/snapshots/emoji_method_calls.txt
new file mode 100644
index 0000000000..8f71181ac1
--- /dev/null
+++ b/test/prism/snapshots/emoji_method_calls.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,12))
+ ├── flags: attribute_write
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,3)-(1,4) = "."
+ ├── name: :🌊=
+ ├── message_loc: (1,4)-(1,8) = "🌊"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,11)-(1,12))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,11)-(1,12))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/endless_methods.txt b/test/prism/snapshots/endless_methods.txt
new file mode 100644
index 0000000000..6e20c0deec
--- /dev/null
+++ b/test/prism/snapshots/endless_methods.txt
@@ -0,0 +1,107 @@
+@ ProgramNode (location: (1,0)-(5,22))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,22))
+ └── body: (length: 3)
+ ├── @ DefNode (location: (1,0)-(1,11))
+ │ ├── name: :foo
+ │ ├── name_loc: (1,4)-(1,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,10)-(1,11))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,10)-(1,11))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: (1,8)-(1,9) = "="
+ │ └── end_keyword_loc: ∅
+ ├── @ DefNode (location: (3,0)-(3,14))
+ │ ├── name: :bar
+ │ ├── name_loc: (3,4)-(3,7) = "bar"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (3,10)-(3,14))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (3,10)-(3,14))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :A
+ │ │ ├── message_loc: (3,10)-(3,11) = "A"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (3,12)-(3,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ StringNode (location: (3,12)-(3,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (3,12)-(3,13) = "\""
+ │ │ │ ├── content_loc: (3,13)-(3,13) = ""
+ │ │ │ ├── closing_loc: (3,13)-(3,14) = "\""
+ │ │ │ └── unescaped: ""
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (3,0)-(3,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: (3,8)-(3,9) = "="
+ │ └── end_keyword_loc: ∅
+ └── @ DefNode (location: (5,0)-(5,22))
+ ├── name: :method
+ ├── name_loc: (5,4)-(5,10) = "method"
+ ├── receiver: ∅
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (5,13)-(5,22))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (5,13)-(5,22))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (5,13)-(5,18))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ IntegerNode (location: (5,13)-(5,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :+
+ │ │ ├── message_loc: (5,15)-(5,16) = "+"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (5,17)-(5,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (5,17)-(5,18))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (5,19)-(5,20) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (5,21)-(5,22))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (5,21)-(5,22))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (5,0)-(5,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: (5,11)-(5,12) = "="
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/endless_range_in_conditional.txt b/test/prism/snapshots/endless_range_in_conditional.txt
new file mode 100644
index 0000000000..1802c4faed
--- /dev/null
+++ b/test/prism/snapshots/endless_range_in_conditional.txt
@@ -0,0 +1,53 @@
+@ ProgramNode (location: (1,0)-(3,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,12))
+ └── body: (length: 3)
+ ├── @ IfNode (location: (1,0)-(1,13))
+ │ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ │ ├── predicate:
+ │ │ @ FlipFlopNode (location: (1,3)-(1,7))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (1,3)-(1,4))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right:
+ │ │ │ @ IntegerNode (location: (1,6)-(1,7))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── operator_loc: (1,4)-(1,6) = ".."
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements: ∅
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (1,10)-(1,13) = "end"
+ ├── @ IfNode (location: (2,0)-(2,12))
+ │ ├── if_keyword_loc: (2,0)-(2,2) = "if"
+ │ ├── predicate:
+ │ │ @ FlipFlopNode (location: (2,3)-(2,6))
+ │ │ ├── flags: ∅
+ │ │ ├── left: ∅
+ │ │ ├── right:
+ │ │ │ @ IntegerNode (location: (2,5)-(2,6))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (2,3)-(2,5) = ".."
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements: ∅
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (2,9)-(2,12) = "end"
+ └── @ IfNode (location: (3,0)-(3,12))
+ ├── if_keyword_loc: (3,0)-(3,2) = "if"
+ ├── predicate:
+ │ @ FlipFlopNode (location: (3,3)-(3,6))
+ │ ├── flags: ∅
+ │ ├── left:
+ │ │ @ IntegerNode (location: (3,3)-(3,4))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── right: ∅
+ │ └── operator_loc: (3,4)-(3,6) = ".."
+ ├── then_keyword_loc: ∅
+ ├── statements: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (3,9)-(3,12) = "end"
diff --git a/test/prism/snapshots/for.txt b/test/prism/snapshots/for.txt
new file mode 100644
index 0000000000..cc4bbc1166
--- /dev/null
+++ b/test/prism/snapshots/for.txt
@@ -0,0 +1,188 @@
+@ ProgramNode (location: (1,0)-(19,22))
+├── locals: [:i, :j, :k]
+└── statements:
+ @ StatementsNode (location: (1,0)-(19,22))
+ └── body: (length: 6)
+ ├── @ ForNode (location: (1,0)-(3,3))
+ │ ├── index:
+ │ │ @ LocalVariableTargetNode (location: (1,4)-(1,5))
+ │ │ ├── name: :i
+ │ │ └── depth: 0
+ │ ├── collection:
+ │ │ @ RangeNode (location: (1,9)-(1,14))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (1,9)-(1,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right:
+ │ │ │ @ IntegerNode (location: (1,12)-(1,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 10
+ │ │ └── operator_loc: (1,10)-(1,12) = ".."
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (2,0)-(2,1))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (2,0)-(2,1))
+ │ │ ├── name: :i
+ │ │ └── depth: 0
+ │ ├── for_keyword_loc: (1,0)-(1,3) = "for"
+ │ ├── in_keyword_loc: (1,6)-(1,8) = "in"
+ │ ├── do_keyword_loc: ∅
+ │ └── end_keyword_loc: (3,0)-(3,3) = "end"
+ ├── @ ForNode (location: (5,0)-(5,22))
+ │ ├── index:
+ │ │ @ LocalVariableTargetNode (location: (5,4)-(5,5))
+ │ │ ├── name: :i
+ │ │ └── depth: 0
+ │ ├── collection:
+ │ │ @ RangeNode (location: (5,9)-(5,14))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (5,9)-(5,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right:
+ │ │ │ @ IntegerNode (location: (5,12)-(5,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 10
+ │ │ └── operator_loc: (5,10)-(5,12) = ".."
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (5,16)-(5,17))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (5,16)-(5,17))
+ │ │ ├── name: :i
+ │ │ └── depth: 0
+ │ ├── for_keyword_loc: (5,0)-(5,3) = "for"
+ │ ├── in_keyword_loc: (5,6)-(5,8) = "in"
+ │ ├── do_keyword_loc: ∅
+ │ └── end_keyword_loc: (5,19)-(5,22) = "end"
+ ├── @ ForNode (location: (7,0)-(9,3))
+ │ ├── index:
+ │ │ @ MultiTargetNode (location: (7,4)-(7,7))
+ │ │ ├── lefts: (length: 2)
+ │ │ │ ├── @ LocalVariableTargetNode (location: (7,4)-(7,5))
+ │ │ │ │ ├── name: :i
+ │ │ │ │ └── depth: 0
+ │ │ │ └── @ LocalVariableTargetNode (location: (7,6)-(7,7))
+ │ │ │ ├── name: :j
+ │ │ │ └── depth: 0
+ │ │ ├── rest: ∅
+ │ │ ├── rights: (length: 0)
+ │ │ ├── lparen_loc: ∅
+ │ │ └── rparen_loc: ∅
+ │ ├── collection:
+ │ │ @ RangeNode (location: (7,11)-(7,16))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (7,11)-(7,12))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right:
+ │ │ │ @ IntegerNode (location: (7,14)-(7,16))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 10
+ │ │ └── operator_loc: (7,12)-(7,14) = ".."
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (8,0)-(8,1))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (8,0)-(8,1))
+ │ │ ├── name: :i
+ │ │ └── depth: 0
+ │ ├── for_keyword_loc: (7,0)-(7,3) = "for"
+ │ ├── in_keyword_loc: (7,8)-(7,10) = "in"
+ │ ├── do_keyword_loc: ∅
+ │ └── end_keyword_loc: (9,0)-(9,3) = "end"
+ ├── @ ForNode (location: (11,0)-(13,3))
+ │ ├── index:
+ │ │ @ MultiTargetNode (location: (11,4)-(11,9))
+ │ │ ├── lefts: (length: 3)
+ │ │ │ ├── @ LocalVariableTargetNode (location: (11,4)-(11,5))
+ │ │ │ │ ├── name: :i
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── @ LocalVariableTargetNode (location: (11,6)-(11,7))
+ │ │ │ │ ├── name: :j
+ │ │ │ │ └── depth: 0
+ │ │ │ └── @ LocalVariableTargetNode (location: (11,8)-(11,9))
+ │ │ │ ├── name: :k
+ │ │ │ └── depth: 0
+ │ │ ├── rest: ∅
+ │ │ ├── rights: (length: 0)
+ │ │ ├── lparen_loc: ∅
+ │ │ └── rparen_loc: ∅
+ │ ├── collection:
+ │ │ @ RangeNode (location: (11,13)-(11,18))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (11,13)-(11,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right:
+ │ │ │ @ IntegerNode (location: (11,16)-(11,18))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 10
+ │ │ └── operator_loc: (11,14)-(11,16) = ".."
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (12,0)-(12,1))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (12,0)-(12,1))
+ │ │ ├── name: :i
+ │ │ └── depth: 0
+ │ ├── for_keyword_loc: (11,0)-(11,3) = "for"
+ │ ├── in_keyword_loc: (11,10)-(11,12) = "in"
+ │ ├── do_keyword_loc: ∅
+ │ └── end_keyword_loc: (13,0)-(13,3) = "end"
+ ├── @ ForNode (location: (15,0)-(17,3))
+ │ ├── index:
+ │ │ @ LocalVariableTargetNode (location: (15,4)-(15,5))
+ │ │ ├── name: :i
+ │ │ └── depth: 0
+ │ ├── collection:
+ │ │ @ RangeNode (location: (15,9)-(15,14))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (15,9)-(15,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right:
+ │ │ │ @ IntegerNode (location: (15,12)-(15,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 10
+ │ │ └── operator_loc: (15,10)-(15,12) = ".."
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (16,0)-(16,1))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (16,0)-(16,1))
+ │ │ ├── name: :i
+ │ │ └── depth: 0
+ │ ├── for_keyword_loc: (15,0)-(15,3) = "for"
+ │ ├── in_keyword_loc: (15,6)-(15,8) = "in"
+ │ ├── do_keyword_loc: (15,15)-(15,17) = "do"
+ │ └── end_keyword_loc: (17,0)-(17,3) = "end"
+ └── @ ForNode (location: (19,0)-(19,22))
+ ├── index:
+ │ @ LocalVariableTargetNode (location: (19,4)-(19,5))
+ │ ├── name: :i
+ │ └── depth: 0
+ ├── collection:
+ │ @ RangeNode (location: (19,9)-(19,14))
+ │ ├── flags: ∅
+ │ ├── left:
+ │ │ @ IntegerNode (location: (19,9)-(19,10))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── right:
+ │ │ @ IntegerNode (location: (19,12)-(19,14))
+ │ │ ├── flags: decimal
+ │ │ └── value: 10
+ │ └── operator_loc: (19,10)-(19,12) = ".."
+ ├── statements:
+ │ @ StatementsNode (location: (19,16)-(19,17))
+ │ └── body: (length: 1)
+ │ └── @ LocalVariableReadNode (location: (19,16)-(19,17))
+ │ ├── name: :i
+ │ └── depth: 0
+ ├── for_keyword_loc: (19,0)-(19,3) = "for"
+ ├── in_keyword_loc: (19,6)-(19,8) = "in"
+ ├── do_keyword_loc: ∅
+ └── end_keyword_loc: (19,19)-(19,22) = "end"
diff --git a/test/prism/snapshots/global_variables.txt b/test/prism/snapshots/global_variables.txt
new file mode 100644
index 0000000000..9f775ed80d
--- /dev/null
+++ b/test/prism/snapshots/global_variables.txt
@@ -0,0 +1,191 @@
+@ ProgramNode (location: (1,0)-(93,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(93,4))
+ └── body: (length: 47)
+ ├── @ GlobalVariableReadNode (location: (1,0)-(1,16))
+ │ └── name: :$global_variable
+ ├── @ GlobalVariableReadNode (location: (3,0)-(3,2))
+ │ └── name: :$_
+ ├── @ GlobalVariableReadNode (location: (5,0)-(5,3))
+ │ └── name: :$-w
+ ├── @ GlobalVariableReadNode (location: (7,0)-(7,10))
+ │ └── name: :$LOAD_PATH
+ ├── @ GlobalVariableReadNode (location: (9,0)-(9,6))
+ │ └── name: :$stdin
+ ├── @ GlobalVariableReadNode (location: (11,0)-(11,7))
+ │ └── name: :$stdout
+ ├── @ GlobalVariableReadNode (location: (13,0)-(13,7))
+ │ └── name: :$stderr
+ ├── @ GlobalVariableReadNode (location: (15,0)-(15,2))
+ │ └── name: :$!
+ ├── @ GlobalVariableReadNode (location: (17,0)-(17,2))
+ │ └── name: :$?
+ ├── @ GlobalVariableReadNode (location: (19,0)-(19,2))
+ │ └── name: :$~
+ ├── @ BackReferenceReadNode (location: (21,0)-(21,2))
+ │ └── name: :$&
+ ├── @ BackReferenceReadNode (location: (23,0)-(23,2))
+ │ └── name: :$`
+ ├── @ BackReferenceReadNode (location: (25,0)-(25,2))
+ │ └── name: :$'
+ ├── @ BackReferenceReadNode (location: (27,0)-(27,2))
+ │ └── name: :$+
+ ├── @ GlobalVariableReadNode (location: (29,0)-(29,2))
+ │ └── name: :$:
+ ├── @ GlobalVariableReadNode (location: (31,0)-(31,2))
+ │ └── name: :$;
+ ├── @ GlobalVariableReadNode (location: (33,0)-(33,2))
+ │ └── name: :$,
+ ├── @ GlobalVariableReadNode (location: (35,0)-(35,6))
+ │ └── name: :$DEBUG
+ ├── @ GlobalVariableReadNode (location: (37,0)-(37,9))
+ │ └── name: :$FILENAME
+ ├── @ GlobalVariableReadNode (location: (39,0)-(39,2))
+ │ └── name: :$0
+ ├── @ GlobalVariableReadNode (location: (41,0)-(41,3))
+ │ └── name: :$-0
+ ├── @ GlobalVariableReadNode (location: (43,0)-(43,16))
+ │ └── name: :$LOADED_FEATURES
+ ├── @ GlobalVariableReadNode (location: (45,0)-(45,8))
+ │ └── name: :$VERBOSE
+ ├── @ GlobalVariableReadNode (location: (47,0)-(47,3))
+ │ └── name: :$-K
+ ├── @ SymbolNode (location: (49,0)-(49,17))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (49,0)-(49,1) = ":"
+ │ ├── value_loc: (49,1)-(49,17) = "$global_variable"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "$global_variable"
+ ├── @ SymbolNode (location: (51,0)-(51,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (51,0)-(51,1) = ":"
+ │ ├── value_loc: (51,1)-(51,3) = "$_"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "$_"
+ ├── @ SymbolNode (location: (53,0)-(53,4))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (53,0)-(53,1) = ":"
+ │ ├── value_loc: (53,1)-(53,4) = "$-w"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "$-w"
+ ├── @ SymbolNode (location: (55,0)-(55,11))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (55,0)-(55,1) = ":"
+ │ ├── value_loc: (55,1)-(55,11) = "$LOAD_PATH"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "$LOAD_PATH"
+ ├── @ SymbolNode (location: (57,0)-(57,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (57,0)-(57,1) = ":"
+ │ ├── value_loc: (57,1)-(57,7) = "$stdin"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "$stdin"
+ ├── @ SymbolNode (location: (59,0)-(59,8))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (59,0)-(59,1) = ":"
+ │ ├── value_loc: (59,1)-(59,8) = "$stdout"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "$stdout"
+ ├── @ SymbolNode (location: (61,0)-(61,8))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (61,0)-(61,1) = ":"
+ │ ├── value_loc: (61,1)-(61,8) = "$stderr"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "$stderr"
+ ├── @ SymbolNode (location: (63,0)-(63,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (63,0)-(63,1) = ":"
+ │ ├── value_loc: (63,1)-(63,3) = "$!"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "$!"
+ ├── @ SymbolNode (location: (65,0)-(65,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (65,0)-(65,1) = ":"
+ │ ├── value_loc: (65,1)-(65,3) = "$?"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "$?"
+ ├── @ SymbolNode (location: (67,0)-(67,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (67,0)-(67,1) = ":"
+ │ ├── value_loc: (67,1)-(67,3) = "$~"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "$~"
+ ├── @ SymbolNode (location: (69,0)-(69,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (69,0)-(69,1) = ":"
+ │ ├── value_loc: (69,1)-(69,3) = "$&"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "$&"
+ ├── @ SymbolNode (location: (71,0)-(71,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (71,0)-(71,1) = ":"
+ │ ├── value_loc: (71,1)-(71,3) = "$`"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "$`"
+ ├── @ SymbolNode (location: (73,0)-(73,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (73,0)-(73,1) = ":"
+ │ ├── value_loc: (73,1)-(73,3) = "$'"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "$'"
+ ├── @ SymbolNode (location: (75,0)-(75,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (75,0)-(75,1) = ":"
+ │ ├── value_loc: (75,1)-(75,3) = "$+"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "$+"
+ ├── @ SymbolNode (location: (77,0)-(77,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (77,0)-(77,1) = ":"
+ │ ├── value_loc: (77,1)-(77,3) = "$:"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "$:"
+ ├── @ SymbolNode (location: (79,0)-(79,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (79,0)-(79,1) = ":"
+ │ ├── value_loc: (79,1)-(79,3) = "$;"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "$;"
+ ├── @ SymbolNode (location: (81,0)-(81,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (81,0)-(81,1) = ":"
+ │ ├── value_loc: (81,1)-(81,7) = "$DEBUG"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "$DEBUG"
+ ├── @ SymbolNode (location: (83,0)-(83,10))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (83,0)-(83,1) = ":"
+ │ ├── value_loc: (83,1)-(83,10) = "$FILENAME"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "$FILENAME"
+ ├── @ SymbolNode (location: (85,0)-(85,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (85,0)-(85,1) = ":"
+ │ ├── value_loc: (85,1)-(85,3) = "$0"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "$0"
+ ├── @ SymbolNode (location: (87,0)-(87,4))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (87,0)-(87,1) = ":"
+ │ ├── value_loc: (87,1)-(87,4) = "$-0"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "$-0"
+ ├── @ SymbolNode (location: (89,0)-(89,17))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (89,0)-(89,1) = ":"
+ │ ├── value_loc: (89,1)-(89,17) = "$LOADED_FEATURES"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "$LOADED_FEATURES"
+ ├── @ SymbolNode (location: (91,0)-(91,9))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (91,0)-(91,1) = ":"
+ │ ├── value_loc: (91,1)-(91,9) = "$VERBOSE"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "$VERBOSE"
+ └── @ SymbolNode (location: (93,0)-(93,4))
+ ├── flags: forced_us_ascii_encoding
+ ├── opening_loc: (93,0)-(93,1) = ":"
+ ├── value_loc: (93,1)-(93,4) = "$-K"
+ ├── closing_loc: ∅
+ └── unescaped: "$-K"
diff --git a/test/prism/snapshots/hashes.txt b/test/prism/snapshots/hashes.txt
new file mode 100644
index 0000000000..7a3ac4b0ea
--- /dev/null
+++ b/test/prism/snapshots/hashes.txt
@@ -0,0 +1,384 @@
+@ ProgramNode (location: (1,0)-(28,9))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(28,9))
+ └── body: (length: 10)
+ ├── @ HashNode (location: (1,0)-(1,2))
+ │ ├── opening_loc: (1,0)-(1,1) = "{"
+ │ ├── elements: (length: 0)
+ │ └── closing_loc: (1,1)-(1,2) = "}"
+ ├── @ HashNode (location: (3,0)-(4,1))
+ │ ├── opening_loc: (3,0)-(3,1) = "{"
+ │ ├── elements: (length: 0)
+ │ └── closing_loc: (4,0)-(4,1) = "}"
+ ├── @ HashNode (location: (6,0)-(6,18))
+ │ ├── opening_loc: (6,0)-(6,1) = "{"
+ │ ├── elements: (length: 2)
+ │ │ ├── @ AssocNode (location: (6,2)-(6,8))
+ │ │ │ ├── key:
+ │ │ │ │ @ CallNode (location: (6,2)-(6,3))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── message_loc: (6,2)-(6,3) = "a"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── value:
+ │ │ │ │ @ CallNode (location: (6,7)-(6,8))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── message_loc: (6,7)-(6,8) = "b"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── operator_loc: (6,4)-(6,6) = "=>"
+ │ │ └── @ AssocNode (location: (6,10)-(6,16))
+ │ │ ├── key:
+ │ │ │ @ CallNode (location: (6,10)-(6,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── message_loc: (6,10)-(6,11) = "c"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (6,15)-(6,16))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :d
+ │ │ │ ├── message_loc: (6,15)-(6,16) = "d"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (6,12)-(6,14) = "=>"
+ │ └── closing_loc: (6,17)-(6,18) = "}"
+ ├── @ HashNode (location: (8,0)-(8,15))
+ │ ├── opening_loc: (8,0)-(8,1) = "{"
+ │ ├── elements: (length: 2)
+ │ │ ├── @ AssocNode (location: (8,2)-(8,8))
+ │ │ │ ├── key:
+ │ │ │ │ @ CallNode (location: (8,2)-(8,3))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── message_loc: (8,2)-(8,3) = "a"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── value:
+ │ │ │ │ @ CallNode (location: (8,7)-(8,8))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── message_loc: (8,7)-(8,8) = "b"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── operator_loc: (8,4)-(8,6) = "=>"
+ │ │ └── @ AssocSplatNode (location: (8,10)-(8,13))
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (8,12)-(8,13))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── message_loc: (8,12)-(8,13) = "c"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (8,10)-(8,12) = "**"
+ │ └── closing_loc: (8,14)-(8,15) = "}"
+ ├── @ HashNode (location: (10,0)-(16,5))
+ │ ├── opening_loc: (10,0)-(10,1) = "{"
+ │ ├── elements: (length: 2)
+ │ │ ├── @ AssocNode (location: (11,6)-(11,10))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (11,6)-(11,8))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (11,6)-(11,7) = "a"
+ │ │ │ │ ├── closing_loc: (11,7)-(11,8) = ":"
+ │ │ │ │ └── unescaped: "a"
+ │ │ │ ├── value:
+ │ │ │ │ @ CallNode (location: (11,9)-(11,10))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── message_loc: (11,9)-(11,10) = "b"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── operator_loc: ∅
+ │ │ └── @ AssocNode (location: (12,6)-(12,10))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (12,6)-(12,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (12,6)-(12,7) = "c"
+ │ │ │ ├── closing_loc: (12,7)-(12,8) = ":"
+ │ │ │ └── unescaped: "c"
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (12,9)-(12,10))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :d
+ │ │ │ ├── message_loc: (12,9)-(12,10) = "d"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: ∅
+ │ └── closing_loc: (16,4)-(16,5) = "}"
+ ├── @ HashNode (location: (18,0)-(18,25))
+ │ ├── opening_loc: (18,0)-(18,1) = "{"
+ │ ├── elements: (length: 4)
+ │ │ ├── @ AssocNode (location: (18,2)-(18,6))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (18,2)-(18,4))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (18,2)-(18,3) = "a"
+ │ │ │ │ ├── closing_loc: (18,3)-(18,4) = ":"
+ │ │ │ │ └── unescaped: "a"
+ │ │ │ ├── value:
+ │ │ │ │ @ CallNode (location: (18,5)-(18,6))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── message_loc: (18,5)-(18,6) = "b"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── operator_loc: ∅
+ │ │ ├── @ AssocNode (location: (18,8)-(18,12))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (18,8)-(18,10))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (18,8)-(18,9) = "c"
+ │ │ │ │ ├── closing_loc: (18,9)-(18,10) = ":"
+ │ │ │ │ └── unescaped: "c"
+ │ │ │ ├── value:
+ │ │ │ │ @ CallNode (location: (18,11)-(18,12))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :d
+ │ │ │ │ ├── message_loc: (18,11)-(18,12) = "d"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── operator_loc: ∅
+ │ │ ├── @ AssocSplatNode (location: (18,14)-(18,17))
+ │ │ │ ├── value:
+ │ │ │ │ @ CallNode (location: (18,16)-(18,17))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :e
+ │ │ │ │ ├── message_loc: (18,16)-(18,17) = "e"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── operator_loc: (18,14)-(18,16) = "**"
+ │ │ └── @ AssocNode (location: (18,19)-(18,23))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (18,19)-(18,21))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (18,19)-(18,20) = "f"
+ │ │ │ ├── closing_loc: (18,20)-(18,21) = ":"
+ │ │ │ └── unescaped: "f"
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (18,22)-(18,23))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :g
+ │ │ │ ├── message_loc: (18,22)-(18,23) = "g"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: ∅
+ │ └── closing_loc: (18,24)-(18,25) = "}"
+ ├── @ HashNode (location: (20,0)-(20,12))
+ │ ├── opening_loc: (20,0)-(20,1) = "{"
+ │ ├── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (20,2)-(20,10))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (20,2)-(20,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (20,2)-(20,3) = "\""
+ │ │ │ ├── value_loc: (20,3)-(20,4) = "a"
+ │ │ │ ├── closing_loc: (20,4)-(20,6) = "\":"
+ │ │ │ └── unescaped: "a"
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (20,7)-(20,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ CallNode (location: (20,8)-(20,10))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :b?
+ │ │ │ │ ├── message_loc: (20,8)-(20,10) = "b?"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :!
+ │ │ │ ├── message_loc: (20,7)-(20,8) = "!"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: ∅
+ │ └── closing_loc: (20,11)-(20,12) = "}"
+ ├── @ LocalVariableWriteNode (location: (22,0)-(22,5))
+ │ ├── name: :a
+ │ ├── depth: 0
+ │ ├── name_loc: (22,0)-(22,1) = "a"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (22,4)-(22,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (22,2)-(22,3) = "="
+ ├── @ CallNode (location: (23,0)-(26,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (23,0)-(23,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (23,4)-(26,3))
+ │ ├── locals: [:b]
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (24,2)-(25,20))
+ │ │ └── body: (length: 2)
+ │ │ ├── @ LocalVariableWriteNode (location: (24,2)-(24,7))
+ │ │ │ ├── name: :b
+ │ │ │ ├── depth: 0
+ │ │ │ ├── name_loc: (24,2)-(24,3) = "b"
+ │ │ │ ├── value:
+ │ │ │ │ @ IntegerNode (location: (24,6)-(24,7))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── operator_loc: (24,4)-(24,5) = "="
+ │ │ └── @ HashNode (location: (25,2)-(25,20))
+ │ │ ├── opening_loc: (25,2)-(25,3) = "{"
+ │ │ ├── elements: (length: 4)
+ │ │ │ ├── @ AssocNode (location: (25,4)-(25,6))
+ │ │ │ │ ├── key:
+ │ │ │ │ │ @ SymbolNode (location: (25,4)-(25,6))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── value_loc: (25,4)-(25,5) = "a"
+ │ │ │ │ │ ├── closing_loc: (25,5)-(25,6) = ":"
+ │ │ │ │ │ └── unescaped: "a"
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ ImplicitNode (location: (25,4)-(25,6))
+ │ │ │ │ │ └── value:
+ │ │ │ │ │ @ LocalVariableReadNode (location: (25,4)-(25,6))
+ │ │ │ │ │ ├── name: :a
+ │ │ │ │ │ └── depth: 1
+ │ │ │ │ └── operator_loc: ∅
+ │ │ │ ├── @ AssocNode (location: (25,8)-(25,10))
+ │ │ │ │ ├── key:
+ │ │ │ │ │ @ SymbolNode (location: (25,8)-(25,10))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── value_loc: (25,8)-(25,9) = "b"
+ │ │ │ │ │ ├── closing_loc: (25,9)-(25,10) = ":"
+ │ │ │ │ │ └── unescaped: "b"
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ ImplicitNode (location: (25,8)-(25,10))
+ │ │ │ │ │ └── value:
+ │ │ │ │ │ @ LocalVariableReadNode (location: (25,8)-(25,10))
+ │ │ │ │ │ ├── name: :b
+ │ │ │ │ │ └── depth: 0
+ │ │ │ │ └── operator_loc: ∅
+ │ │ │ ├── @ AssocNode (location: (25,12)-(25,14))
+ │ │ │ │ ├── key:
+ │ │ │ │ │ @ SymbolNode (location: (25,12)-(25,14))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── value_loc: (25,12)-(25,13) = "c"
+ │ │ │ │ │ ├── closing_loc: (25,13)-(25,14) = ":"
+ │ │ │ │ │ └── unescaped: "c"
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ ImplicitNode (location: (25,12)-(25,14))
+ │ │ │ │ │ └── value:
+ │ │ │ │ │ @ CallNode (location: (25,12)-(25,14))
+ │ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :c
+ │ │ │ │ │ ├── message_loc: (25,12)-(25,13) = "c"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ └── operator_loc: ∅
+ │ │ │ └── @ AssocNode (location: (25,16)-(25,18))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (25,16)-(25,18))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (25,16)-(25,17) = "D"
+ │ │ │ │ ├── closing_loc: (25,17)-(25,18) = ":"
+ │ │ │ │ └── unescaped: "D"
+ │ │ │ ├── value:
+ │ │ │ │ @ ImplicitNode (location: (25,16)-(25,18))
+ │ │ │ │ └── value:
+ │ │ │ │ @ ConstantReadNode (location: (25,16)-(25,18))
+ │ │ │ │ └── name: :D
+ │ │ │ └── operator_loc: ∅
+ │ │ └── closing_loc: (25,19)-(25,20) = "}"
+ │ ├── opening_loc: (23,4)-(23,6) = "do"
+ │ └── closing_loc: (26,0)-(26,3) = "end"
+ └── @ HashNode (location: (28,0)-(28,9))
+ ├── opening_loc: (28,0)-(28,1) = "{"
+ ├── elements: (length: 1)
+ │ └── @ AssocNode (location: (28,2)-(28,7))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (28,2)-(28,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (28,2)-(28,3) = "a"
+ │ │ ├── closing_loc: (28,3)-(28,4) = ":"
+ │ │ └── unescaped: "a"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (28,5)-(28,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: -1
+ │ └── operator_loc: ∅
+ └── closing_loc: (28,8)-(28,9) = "}"
diff --git a/test/prism/snapshots/heredoc.txt b/test/prism/snapshots/heredoc.txt
new file mode 100644
index 0000000000..3ca66dd989
--- /dev/null
+++ b/test/prism/snapshots/heredoc.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,6))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,0)-(1,6))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,6) = "<<TEXT"
+ ├── content_loc: (2,0)-(2,0) = ""
+ ├── closing_loc: (2,0)-(3,0) = "TEXT\n"
+ └── unescaped: ""
diff --git a/test/prism/snapshots/heredoc_with_carriage_returns.txt b/test/prism/snapshots/heredoc_with_carriage_returns.txt
new file mode 100644
index 0000000000..134f863234
--- /dev/null
+++ b/test/prism/snapshots/heredoc_with_carriage_returns.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,6))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,0)-(1,6))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,6) = "<<TEXT"
+ ├── content_loc: (2,0)-(2,0) = ""
+ ├── closing_loc: (2,0)-(3,0) = "TEXT\r\n"
+ └── unescaped: ""
diff --git a/test/prism/snapshots/heredoc_with_comment.txt b/test/prism/snapshots/heredoc_with_comment.txt
new file mode 100644
index 0000000000..f2225ca981
--- /dev/null
+++ b/test/prism/snapshots/heredoc_with_comment.txt
@@ -0,0 +1,21 @@
+@ ProgramNode (location: (1,0)-(1,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,15))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,15))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ StringNode (location: (1,0)-(1,9))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,9) = "<<-TARGET"
+ │ ├── content_loc: (2,0)-(3,0) = " content makes for an obvious error\r\n"
+ │ ├── closing_loc: (3,0)-(3,6) = "TARGET"
+ │ └── unescaped: " content makes for an obvious error\n"
+ ├── call_operator_loc: (1,9)-(1,10) = "."
+ ├── name: :chomp
+ ├── message_loc: (1,10)-(1,15) = "chomp"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/heredoc_with_escaped_newline_at_start.txt b/test/prism/snapshots/heredoc_with_escaped_newline_at_start.txt
new file mode 100644
index 0000000000..acc6b082fc
--- /dev/null
+++ b/test/prism/snapshots/heredoc_with_escaped_newline_at_start.txt
@@ -0,0 +1,67 @@
+@ ProgramNode (location: (1,0)-(5,25))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,25))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,25))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ StringNode (location: (1,0)-(1,9))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,0)-(1,9) = "<<-TARGET"
+ │ │ ├── content_loc: (2,0)-(2,0) = ""
+ │ │ ├── closing_loc: (2,0)-(3,0) = "TARGET\n"
+ │ │ └── unescaped: ""
+ │ ├── call_operator_loc: (1,9)-(1,10) = "."
+ │ ├── name: :gsub
+ │ ├── message_loc: (1,10)-(1,14) = "gsub"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,15)-(1,25))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ RegularExpressionNode (location: (1,15)-(1,21))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (1,15)-(1,16) = "/"
+ │ │ │ ├── content_loc: (1,16)-(1,20) = "^\\s{"
+ │ │ │ ├── closing_loc: (1,20)-(1,21) = "/"
+ │ │ │ └── unescaped: "^\\s{"
+ │ │ └── @ StringNode (location: (1,23)-(1,25))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,23)-(1,24) = "'"
+ │ │ ├── content_loc: (1,24)-(1,24) = ""
+ │ │ ├── closing_loc: (1,24)-(1,25) = "'"
+ │ │ └── unescaped: ""
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (5,0)-(5,25))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ StringNode (location: (5,0)-(5,9))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (5,0)-(5,9) = "<<-TARGET"
+ │ ├── content_loc: (6,0)-(6,0) = ""
+ │ ├── closing_loc: (6,0)-(7,0) = "TARGET\r\n"
+ │ └── unescaped: ""
+ ├── call_operator_loc: (5,9)-(5,10) = "."
+ ├── name: :gsub
+ ├── message_loc: (5,10)-(5,14) = "gsub"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (5,15)-(5,25))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ RegularExpressionNode (location: (5,15)-(5,21))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (5,15)-(5,16) = "/"
+ │ │ ├── content_loc: (5,16)-(5,20) = "^\\s{"
+ │ │ ├── closing_loc: (5,20)-(5,21) = "/"
+ │ │ └── unescaped: "^\\s{"
+ │ └── @ StringNode (location: (5,23)-(5,25))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (5,23)-(5,24) = "'"
+ │ ├── content_loc: (5,24)-(5,24) = ""
+ │ ├── closing_loc: (5,24)-(5,25) = "'"
+ │ └── unescaped: ""
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/heredoc_with_trailing_newline.txt b/test/prism/snapshots/heredoc_with_trailing_newline.txt
new file mode 100644
index 0000000000..b064931aa8
--- /dev/null
+++ b/test/prism/snapshots/heredoc_with_trailing_newline.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,6))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,0)-(1,6))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,6) = "<<-END"
+ ├── content_loc: (2,0)-(2,0) = ""
+ ├── closing_loc: (2,0)-(2,3) = "END"
+ └── unescaped: ""
diff --git a/test/prism/snapshots/heredocs_leading_whitespace.txt b/test/prism/snapshots/heredocs_leading_whitespace.txt
new file mode 100644
index 0000000000..dbcb0d5a19
--- /dev/null
+++ b/test/prism/snapshots/heredocs_leading_whitespace.txt
@@ -0,0 +1,63 @@
+@ ProgramNode (location: (1,0)-(26,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(26,10))
+ └── body: (length: 6)
+ ├── @ StringNode (location: (1,0)-(1,10))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,10) = "<<-' FOO'"
+ │ ├── content_loc: (2,0)-(4,0) = "a\nb\n"
+ │ ├── closing_loc: (4,0)-(5,0) = " FOO\n"
+ │ └── unescaped: "a\nb\n"
+ ├── @ StringNode (location: (6,0)-(6,10))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (6,0)-(6,10) = "<<-\" FOO\""
+ │ ├── content_loc: (7,0)-(9,0) = "a\nb\n"
+ │ ├── closing_loc: (9,0)-(10,0) = " FOO\n"
+ │ └── unescaped: "a\nb\n"
+ ├── @ XStringNode (location: (11,0)-(11,10))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (11,0)-(11,10) = "<<-` FOO`"
+ │ ├── content_loc: (12,0)-(14,0) = "a\nb\n"
+ │ ├── closing_loc: (14,0)-(15,0) = " FOO\n"
+ │ └── unescaped: "a\nb\n"
+ ├── @ StringNode (location: (16,0)-(16,10))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (16,0)-(16,10) = "<<-' FOO'"
+ │ ├── content_loc: (17,0)-(19,0) = "a\nb\n"
+ │ ├── closing_loc: (19,0)-(20,0) = " FOO\n"
+ │ └── unescaped: "a\nb\n"
+ ├── @ InterpolatedStringNode (location: (21,0)-(21,10))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (21,0)-(21,10) = "<<~' FOO'"
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (22,0)-(23,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (22,0)-(23,0) = "a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ └── @ StringNode (location: (23,0)-(24,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (23,0)-(24,0) = "b\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b\n"
+ │ └── closing_loc: (24,0)-(25,0) = " FOO\n"
+ └── @ InterpolatedStringNode (location: (26,0)-(26,10))
+ ├── flags: ∅
+ ├── opening_loc: (26,0)-(26,10) = "<<~' FOO'"
+ ├── parts: (length: 2)
+ │ ├── @ StringNode (location: (27,0)-(28,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (27,0)-(28,0) = "a\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a\n"
+ │ └── @ StringNode (location: (28,0)-(29,0))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (28,0)-(29,0) = "b\n"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "b\n"
+ └── closing_loc: (29,0)-(30,0) = " FOO\n"
diff --git a/test/prism/snapshots/heredocs_nested.txt b/test/prism/snapshots/heredocs_nested.txt
new file mode 100644
index 0000000000..f830b028c7
--- /dev/null
+++ b/test/prism/snapshots/heredocs_nested.txt
@@ -0,0 +1,94 @@
+@ ProgramNode (location: (1,0)-(12,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(12,4))
+ └── body: (length: 2)
+ ├── @ InterpolatedStringNode (location: (1,0)-(1,7))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,7) = "<<~RUBY"
+ │ ├── parts: (length: 4)
+ │ │ ├── @ StringNode (location: (2,0)-(3,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (2,0)-(3,0) = "pre\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "pre\n"
+ │ │ ├── @ EmbeddedStatementsNode (location: (3,0)-(7,1))
+ │ │ │ ├── opening_loc: (3,0)-(3,2) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (4,0)-(4,6))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ StringNode (location: (4,0)-(4,6))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: (4,0)-(4,6) = "<<RUBY"
+ │ │ │ │ ├── content_loc: (5,0)-(6,0) = " hello\n"
+ │ │ │ │ ├── closing_loc: (6,0)-(7,0) = "RUBY\n"
+ │ │ │ │ └── unescaped: " hello\n"
+ │ │ │ └── closing_loc: (7,0)-(7,1) = "}"
+ │ │ ├── @ StringNode (location: (7,1)-(8,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (7,1)-(8,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── @ StringNode (location: (8,0)-(9,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (8,0)-(9,0) = "post\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "post\n"
+ │ └── closing_loc: (9,0)-(10,0) = "RUBY\n"
+ └── @ InterpolatedStringNode (location: (12,0)-(12,4))
+ ├── flags: ∅
+ ├── opening_loc: (12,0)-(12,4) = "<<-A"
+ ├── parts: (length: 2)
+ │ ├── @ EmbeddedStatementsNode (location: (13,0)-(21,1))
+ │ │ ├── opening_loc: (13,0)-(13,2) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (14,0)-(14,4))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ InterpolatedStringNode (location: (14,0)-(14,4))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (14,0)-(14,4) = "<<-B"
+ │ │ │ ├── parts: (length: 2)
+ │ │ │ │ ├── @ EmbeddedStatementsNode (location: (15,0)-(19,1))
+ │ │ │ │ │ ├── opening_loc: (15,0)-(15,2) = "\#{"
+ │ │ │ │ │ ├── statements:
+ │ │ │ │ │ │ @ StatementsNode (location: (16,0)-(16,4))
+ │ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ │ └── @ InterpolatedStringNode (location: (16,0)-(16,4))
+ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ ├── opening_loc: (16,0)-(16,4) = "<<-C"
+ │ │ │ │ │ │ ├── parts: (length: 2)
+ │ │ │ │ │ │ │ ├── @ EmbeddedStatementsNode (location: (17,0)-(17,4))
+ │ │ │ │ │ │ │ │ ├── opening_loc: (17,0)-(17,2) = "\#{"
+ │ │ │ │ │ │ │ │ ├── statements:
+ │ │ │ │ │ │ │ │ │ @ StatementsNode (location: (17,2)-(17,3))
+ │ │ │ │ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ │ │ │ │ └── @ IntegerNode (location: (17,2)-(17,3))
+ │ │ │ │ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ │ │ │ │ └── value: 3
+ │ │ │ │ │ │ │ │ └── closing_loc: (17,3)-(17,4) = "}"
+ │ │ │ │ │ │ │ └── @ StringNode (location: (17,4)-(18,0))
+ │ │ │ │ │ │ │ ├── flags: frozen
+ │ │ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ │ │ ├── content_loc: (17,4)-(18,0) = "\n"
+ │ │ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ │ │ └── unescaped: "\n"
+ │ │ │ │ │ │ └── closing_loc: (18,0)-(19,0) = "C\n"
+ │ │ │ │ │ └── closing_loc: (19,0)-(19,1) = "}"
+ │ │ │ │ └── @ StringNode (location: (19,1)-(20,0))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (19,1)-(20,0) = "\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "\n"
+ │ │ │ └── closing_loc: (20,0)-(21,0) = "B\n"
+ │ │ └── closing_loc: (21,0)-(21,1) = "}"
+ │ └── @ StringNode (location: (21,1)-(22,0))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (21,1)-(22,0) = "\n"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "\n"
+ └── closing_loc: (22,0)-(23,0) = "A\n"
diff --git a/test/prism/snapshots/heredocs_with_ignored_newlines.txt b/test/prism/snapshots/heredocs_with_ignored_newlines.txt
new file mode 100644
index 0000000000..0f964ec294
--- /dev/null
+++ b/test/prism/snapshots/heredocs_with_ignored_newlines.txt
@@ -0,0 +1,70 @@
+@ ProgramNode (location: (1,0)-(4,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,8))
+ └── body: (length: 2)
+ ├── @ StringNode (location: (1,0)-(1,7))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,7) = "<<-HERE"
+ │ ├── content_loc: (2,0)-(2,0) = ""
+ │ ├── closing_loc: (2,0)-(3,0) = "HERE\n"
+ │ └── unescaped: ""
+ └── @ InterpolatedStringNode (location: (4,0)-(4,8))
+ ├── flags: ∅
+ ├── opening_loc: (4,0)-(4,8) = "<<~THERE"
+ ├── parts: (length: 9)
+ │ ├── @ StringNode (location: (5,0)-(6,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (5,0)-(6,0) = " way over\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "way over\n"
+ │ ├── @ StringNode (location: (6,0)-(7,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (6,0)-(7,0) = " <<HERE\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "<<HERE\n"
+ │ ├── @ StringNode (location: (7,0)-(8,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (7,0)-(8,0) = " not here\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: " not here\n"
+ │ ├── @ StringNode (location: (8,0)-(9,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (8,0)-(9,0) = " HERE\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "HERE\n"
+ │ ├── @ StringNode (location: (9,0)-(10,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (9,0)-(10,0) = "\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\n"
+ │ ├── @ StringNode (location: (10,0)-(11,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (10,0)-(11,0) = " <<~BUT\\\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "<<~BUT"
+ │ ├── @ StringNode (location: (11,0)-(12,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (11,0)-(12,0) = " but\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: " but\n"
+ │ ├── @ StringNode (location: (12,0)-(13,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (12,0)-(13,0) = " BUT\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "BUT\n"
+ │ └── @ StringNode (location: (13,0)-(14,0))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (13,0)-(14,0) = " there\n"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: " there\n"
+ └── closing_loc: (14,0)-(15,0) = "THERE\n"
diff --git a/test/prism/snapshots/heredocs_with_ignored_newlines_and_non_empty.txt b/test/prism/snapshots/heredocs_with_ignored_newlines_and_non_empty.txt
new file mode 100644
index 0000000000..df422f0c1c
--- /dev/null
+++ b/test/prism/snapshots/heredocs_with_ignored_newlines_and_non_empty.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,6))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,0)-(1,6))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,6) = "<<-EOE"
+ ├── content_loc: (2,0)-(4,0) = " some\n heredocs\n"
+ ├── closing_loc: (4,0)-(4,3) = "EOE"
+ └── unescaped: " some\n heredocs\n"
diff --git a/test/prism/snapshots/if.txt b/test/prism/snapshots/if.txt
new file mode 100644
index 0000000000..31c33d368f
--- /dev/null
+++ b/test/prism/snapshots/if.txt
@@ -0,0 +1,531 @@
+@ ProgramNode (location: (1,0)-(42,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(42,3))
+ └── body: (length: 13)
+ ├── @ IfNode (location: (1,0)-(1,15))
+ │ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (1,3)-(1,7))
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,9)-(1,10))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,9)-(1,10))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (1,12)-(1,15) = "end"
+ ├── @ IfNode (location: (3,0)-(4,12))
+ │ ├── if_keyword_loc: (3,0)-(3,2) = "if"
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (3,3)-(3,7))
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (4,0)-(4,1))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (4,0)-(4,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (4,2)-(4,12))
+ │ │ ├── else_keyword_loc: (4,2)-(4,6) = "else"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (4,7)-(4,8))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (4,7)-(4,8))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── end_keyword_loc: (4,9)-(4,12) = "end"
+ │ └── end_keyword_loc: (4,9)-(4,12) = "end"
+ ├── @ IfNode (location: (6,0)-(6,73))
+ │ ├── if_keyword_loc: (6,0)-(6,2) = "if"
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (6,3)-(6,7))
+ │ ├── then_keyword_loc: (6,8)-(6,12) = "then"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (6,13)-(6,17))
+ │ │ └── body: (length: 1)
+ │ │ └── @ TrueNode (location: (6,13)-(6,17))
+ │ ├── consequent:
+ │ │ @ IfNode (location: (6,18)-(6,73))
+ │ │ ├── if_keyword_loc: (6,18)-(6,23) = "elsif"
+ │ │ ├── predicate:
+ │ │ │ @ FalseNode (location: (6,24)-(6,29))
+ │ │ ├── then_keyword_loc: (6,30)-(6,34) = "then"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (6,35)-(6,40))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ FalseNode (location: (6,35)-(6,40))
+ │ │ ├── consequent:
+ │ │ │ @ IfNode (location: (6,41)-(6,73))
+ │ │ │ ├── if_keyword_loc: (6,41)-(6,46) = "elsif"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ NilNode (location: (6,47)-(6,50))
+ │ │ │ ├── then_keyword_loc: (6,51)-(6,55) = "then"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (6,56)-(6,59))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ NilNode (location: (6,56)-(6,59))
+ │ │ │ ├── consequent:
+ │ │ │ │ @ ElseNode (location: (6,60)-(6,73))
+ │ │ │ │ ├── else_keyword_loc: (6,60)-(6,64) = "else"
+ │ │ │ │ ├── statements:
+ │ │ │ │ │ @ StatementsNode (location: (6,65)-(6,69))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ SelfNode (location: (6,65)-(6,69))
+ │ │ │ │ └── end_keyword_loc: (6,70)-(6,73) = "end"
+ │ │ │ └── end_keyword_loc: (6,70)-(6,73) = "end"
+ │ │ └── end_keyword_loc: (6,70)-(6,73) = "end"
+ │ └── end_keyword_loc: (6,70)-(6,73) = "end"
+ ├── @ IfNode (location: (8,0)-(8,9))
+ │ ├── if_keyword_loc: (8,2)-(8,4) = "if"
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (8,5)-(8,9))
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (8,0)-(8,1))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (8,0)-(8,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: ∅
+ ├── @ CallNode (location: (10,0)-(10,21))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (10,0)-(10,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (10,4)-(10,21))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (10,6)-(10,19))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IfNode (location: (10,6)-(10,19))
+ │ │ ├── if_keyword_loc: (10,12)-(10,14) = "if"
+ │ │ ├── predicate:
+ │ │ │ @ TrueNode (location: (10,15)-(10,19))
+ │ │ ├── then_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (10,6)-(10,11))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ BreakNode (location: (10,6)-(10,11))
+ │ │ │ ├── arguments: ∅
+ │ │ │ └── keyword_loc: (10,6)-(10,11) = "break"
+ │ │ ├── consequent: ∅
+ │ │ └── end_keyword_loc: ∅
+ │ ├── opening_loc: (10,4)-(10,5) = "{"
+ │ └── closing_loc: (10,20)-(10,21) = "}"
+ ├── @ CallNode (location: (12,0)-(12,20))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (12,0)-(12,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (12,4)-(12,20))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (12,6)-(12,18))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IfNode (location: (12,6)-(12,18))
+ │ │ ├── if_keyword_loc: (12,11)-(12,13) = "if"
+ │ │ ├── predicate:
+ │ │ │ @ TrueNode (location: (12,14)-(12,18))
+ │ │ ├── then_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (12,6)-(12,10))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ NextNode (location: (12,6)-(12,10))
+ │ │ │ ├── arguments: ∅
+ │ │ │ └── keyword_loc: (12,6)-(12,10) = "next"
+ │ │ ├── consequent: ∅
+ │ │ └── end_keyword_loc: ∅
+ │ ├── opening_loc: (12,4)-(12,5) = "{"
+ │ └── closing_loc: (12,19)-(12,20) = "}"
+ ├── @ IfNode (location: (14,0)-(14,14))
+ │ ├── if_keyword_loc: (14,7)-(14,9) = "if"
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (14,10)-(14,14))
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (14,0)-(14,6))
+ │ │ └── body: (length: 1)
+ │ │ └── @ ReturnNode (location: (14,0)-(14,6))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (14,0)-(14,6) = "return"
+ │ │ └── arguments: ∅
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: ∅
+ ├── @ CallNode (location: (16,0)-(16,38))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (16,0)-(16,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (16,4)-(16,38))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (16,6)-(16,36))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IfNode (location: (16,6)-(16,36))
+ │ │ ├── if_keyword_loc: (16,6)-(16,8) = "if"
+ │ │ ├── predicate:
+ │ │ │ @ CallNode (location: (16,9)-(16,18))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :exit_loop
+ │ │ │ ├── message_loc: (16,9)-(16,18) = "exit_loop"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── then_keyword_loc: (16,19)-(16,23) = "then"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (16,24)-(16,32))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ BreakNode (location: (16,24)-(16,32))
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (16,30)-(16,32))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (16,30)-(16,32))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 42
+ │ │ │ └── keyword_loc: (16,24)-(16,29) = "break"
+ │ │ ├── consequent: ∅
+ │ │ └── end_keyword_loc: (16,33)-(16,36) = "end"
+ │ ├── opening_loc: (16,4)-(16,5) = "{"
+ │ └── closing_loc: (16,37)-(16,38) = "}"
+ ├── @ IfNode (location: (18,0)-(20,3))
+ │ ├── if_keyword_loc: (18,0)-(18,2) = "if"
+ │ ├── predicate:
+ │ │ @ CallNode (location: (18,3)-(18,6))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (18,3)-(18,6) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: (19,0)-(19,4) = "then"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (19,5)-(19,8))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (19,5)-(19,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (19,5)-(19,8) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (20,0)-(20,3) = "end"
+ ├── @ IfNode (location: (22,0)-(22,11))
+ │ ├── if_keyword_loc: (22,7)-(22,9) = "if"
+ │ ├── predicate:
+ │ │ @ CallNode (location: (22,10)-(22,11))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :c
+ │ │ ├── message_loc: (22,10)-(22,11) = "c"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (22,0)-(22,6))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IfNode (location: (22,0)-(22,6))
+ │ │ ├── if_keyword_loc: (22,2)-(22,4) = "if"
+ │ │ ├── predicate:
+ │ │ │ @ CallNode (location: (22,5)-(22,6))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (22,5)-(22,6) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── then_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (22,0)-(22,1))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (22,0)-(22,1))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (22,0)-(22,1) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── consequent: ∅
+ │ │ └── end_keyword_loc: ∅
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: ∅
+ ├── @ IfNode (location: (24,0)-(27,3))
+ │ ├── if_keyword_loc: (24,0)-(24,2) = "if"
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (24,3)-(24,7))
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (25,2)-(25,6))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (25,2)-(25,6))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (25,2)-(25,3) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (25,4)-(25,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ KeywordHashNode (location: (25,4)-(25,6))
+ │ │ │ ├── flags: symbol_keys
+ │ │ │ └── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (25,4)-(25,6))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (25,4)-(25,6))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (25,4)-(25,5) = "b"
+ │ │ │ │ ├── closing_loc: (25,5)-(25,6) = ":"
+ │ │ │ │ └── unescaped: "b"
+ │ │ │ ├── value:
+ │ │ │ │ @ ImplicitNode (location: (25,4)-(25,6))
+ │ │ │ │ └── value:
+ │ │ │ │ @ CallNode (location: (25,4)-(25,6))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── message_loc: (25,4)-(25,5) = "b"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── operator_loc: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (26,0)-(27,3))
+ │ │ ├── else_keyword_loc: (26,0)-(26,4) = "else"
+ │ │ ├── statements: ∅
+ │ │ └── end_keyword_loc: (27,0)-(27,3) = "end"
+ │ └── end_keyword_loc: (27,0)-(27,3) = "end"
+ ├── @ IfNode (location: (29,0)-(31,3))
+ │ ├── if_keyword_loc: (29,0)-(29,2) = "if"
+ │ ├── predicate:
+ │ │ @ MatchPredicateNode (location: (29,3)-(29,12))
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (29,3)-(29,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :type
+ │ │ │ ├── message_loc: (29,3)-(29,7) = "type"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── pattern:
+ │ │ │ @ IntegerNode (location: (29,11)-(29,12))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (29,8)-(29,10) = "in"
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements: ∅
+ │ ├── consequent:
+ │ │ @ IfNode (location: (30,0)-(31,3))
+ │ │ ├── if_keyword_loc: (30,0)-(30,5) = "elsif"
+ │ │ ├── predicate:
+ │ │ │ @ MatchPredicateNode (location: (30,6)-(30,15))
+ │ │ │ ├── value:
+ │ │ │ │ @ CallNode (location: (30,6)-(30,10))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :type
+ │ │ │ │ ├── message_loc: (30,6)-(30,10) = "type"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── pattern:
+ │ │ │ │ @ ConstantReadNode (location: (30,14)-(30,15))
+ │ │ │ │ └── name: :B
+ │ │ │ └── operator_loc: (30,11)-(30,13) = "in"
+ │ │ ├── then_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── consequent: ∅
+ │ │ └── end_keyword_loc: (31,0)-(31,3) = "end"
+ │ └── end_keyword_loc: (31,0)-(31,3) = "end"
+ └── @ IfNode (location: (33,0)-(42,3))
+ ├── if_keyword_loc: (33,0)-(33,2) = "if"
+ ├── predicate:
+ │ @ CallNode (location: (33,3)-(33,5))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f1
+ │ ├── message_loc: (33,3)-(33,5) = "f1"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── then_keyword_loc: ∅
+ ├── statements:
+ │ @ StatementsNode (location: (34,2)-(35,5))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (34,2)-(35,5))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :lambda
+ │ ├── message_loc: (34,2)-(34,8) = "lambda"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (34,9)-(35,5))
+ │ ├── locals: [:_]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (34,12)-(34,15))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (34,13)-(34,14))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (34,13)-(34,14))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :_
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (34,12)-(34,13) = "|"
+ │ │ └── closing_loc: (34,14)-(34,15) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (34,9)-(34,11) = "do"
+ │ └── closing_loc: (35,2)-(35,5) = "end"
+ ├── consequent:
+ │ @ IfNode (location: (36,0)-(42,3))
+ │ ├── if_keyword_loc: (36,0)-(36,5) = "elsif"
+ │ ├── predicate:
+ │ │ @ CallNode (location: (36,6)-(36,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :f2
+ │ │ ├── message_loc: (36,6)-(36,8) = "f2"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (37,2)-(38,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (37,2)-(38,5))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :lambda
+ │ │ ├── message_loc: (37,2)-(37,8) = "lambda"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (37,9)-(38,5))
+ │ │ ├── locals: [:_]
+ │ │ ├── parameters:
+ │ │ │ @ BlockParametersNode (location: (37,12)-(37,15))
+ │ │ │ ├── parameters:
+ │ │ │ │ @ ParametersNode (location: (37,13)-(37,14))
+ │ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ │ └── @ RequiredParameterNode (location: (37,13)-(37,14))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :_
+ │ │ │ │ ├── optionals: (length: 0)
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ ├── keywords: (length: 0)
+ │ │ │ │ ├── keyword_rest: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── locals: (length: 0)
+ │ │ │ ├── opening_loc: (37,12)-(37,13) = "|"
+ │ │ │ └── closing_loc: (37,14)-(37,15) = "|"
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (37,9)-(37,11) = "do"
+ │ │ └── closing_loc: (38,2)-(38,5) = "end"
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (39,0)-(42,3))
+ │ │ ├── else_keyword_loc: (39,0)-(39,4) = "else"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (40,2)-(41,5))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (40,2)-(41,5))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :lambda
+ │ │ │ ├── message_loc: (40,2)-(40,8) = "lambda"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (40,9)-(41,5))
+ │ │ │ ├── locals: [:_]
+ │ │ │ ├── parameters:
+ │ │ │ │ @ BlockParametersNode (location: (40,12)-(40,15))
+ │ │ │ │ ├── parameters:
+ │ │ │ │ │ @ ParametersNode (location: (40,13)-(40,14))
+ │ │ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ │ │ └── @ RequiredParameterNode (location: (40,13)-(40,14))
+ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ └── name: :_
+ │ │ │ │ │ ├── optionals: (length: 0)
+ │ │ │ │ │ ├── rest: ∅
+ │ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ │ ├── keywords: (length: 0)
+ │ │ │ │ │ ├── keyword_rest: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── locals: (length: 0)
+ │ │ │ │ ├── opening_loc: (40,12)-(40,13) = "|"
+ │ │ │ │ └── closing_loc: (40,14)-(40,15) = "|"
+ │ │ │ ├── body: ∅
+ │ │ │ ├── opening_loc: (40,9)-(40,11) = "do"
+ │ │ │ └── closing_loc: (41,2)-(41,5) = "end"
+ │ │ └── end_keyword_loc: (42,0)-(42,3) = "end"
+ │ └── end_keyword_loc: (42,0)-(42,3) = "end"
+ └── end_keyword_loc: (42,0)-(42,3) = "end"
diff --git a/test/prism/snapshots/indented_file_end.txt b/test/prism/snapshots/indented_file_end.txt
new file mode 100644
index 0000000000..aa43ec5a1e
--- /dev/null
+++ b/test/prism/snapshots/indented_file_end.txt
@@ -0,0 +1,18 @@
+@ ProgramNode (location: (1,4)-(3,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,4)-(3,7))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,4)-(3,7))
+ ├── name: :hi
+ ├── name_loc: (1,8)-(1,10) = "hi"
+ ├── receiver: ∅
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (1,4)-(1,7) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (3,4)-(3,7) = "end"
diff --git a/test/prism/snapshots/integer_operations.txt b/test/prism/snapshots/integer_operations.txt
new file mode 100644
index 0000000000..4660928ad2
--- /dev/null
+++ b/test/prism/snapshots/integer_operations.txt
@@ -0,0 +1,635 @@
+@ ProgramNode (location: (1,0)-(63,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(63,7))
+ └── body: (length: 32)
+ ├── @ CallNode (location: (1,0)-(1,2))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (1,1)-(1,2))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!
+ │ ├── message_loc: (1,0)-(1,1) = "!"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (3,0)-(3,2))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (3,1)-(3,2))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :~
+ │ ├── message_loc: (3,0)-(3,1) = "~"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (5,0)-(5,6))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (5,0)-(5,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!=
+ │ ├── message_loc: (5,2)-(5,4) = "!="
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (5,5)-(5,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (5,5)-(5,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (7,0)-(7,6))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (7,0)-(7,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!~
+ │ ├── message_loc: (7,2)-(7,4) = "!~"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (7,5)-(7,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (7,5)-(7,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (9,0)-(9,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (9,0)-(9,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :%
+ │ ├── message_loc: (9,2)-(9,3) = "%"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (9,4)-(9,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (9,4)-(9,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (11,0)-(11,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (11,0)-(11,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :&
+ │ ├── message_loc: (11,2)-(11,3) = "&"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (11,4)-(11,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (11,4)-(11,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (13,0)-(13,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (13,0)-(13,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :*
+ │ ├── message_loc: (13,2)-(13,3) = "*"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (13,4)-(13,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (13,4)-(13,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (15,0)-(15,4))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (15,0)-(15,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :**
+ │ ├── message_loc: (15,1)-(15,3) = "**"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (15,3)-(15,4))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (15,3)-(15,4))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (17,0)-(17,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (17,0)-(17,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (17,2)-(17,3) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (17,4)-(17,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (17,4)-(17,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (19,0)-(19,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (19,0)-(19,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :-
+ │ ├── message_loc: (19,2)-(19,3) = "-"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (19,4)-(19,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (19,4)-(19,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (21,0)-(21,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (21,0)-(21,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :/
+ │ ├── message_loc: (21,2)-(21,3) = "/"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (21,4)-(21,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (21,4)-(21,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (23,0)-(23,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (23,0)-(23,3))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ IntegerNode (location: (23,0)-(23,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :/
+ │ │ ├── message_loc: (23,1)-(23,2) = "/"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (23,2)-(23,3))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (23,2)-(23,3))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :/
+ │ ├── message_loc: (23,3)-(23,4) = "/"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (23,4)-(23,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (23,4)-(23,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (25,0)-(25,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (25,0)-(25,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :<
+ │ ├── message_loc: (25,2)-(25,3) = "<"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (25,4)-(25,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (25,4)-(25,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (27,0)-(27,6))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (27,0)-(27,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :<<
+ │ ├── message_loc: (27,2)-(27,4) = "<<"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (27,5)-(27,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (27,5)-(27,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (29,0)-(29,6))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (29,0)-(29,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :<=
+ │ ├── message_loc: (29,2)-(29,4) = "<="
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (29,5)-(29,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (29,5)-(29,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (31,0)-(31,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (31,0)-(31,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :<=>
+ │ ├── message_loc: (31,2)-(31,5) = "<=>"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (31,6)-(31,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (31,6)-(31,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (33,0)-(33,6))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (33,0)-(33,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :==
+ │ ├── message_loc: (33,2)-(33,4) = "=="
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (33,5)-(33,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (33,5)-(33,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (35,0)-(35,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (35,0)-(35,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :===
+ │ ├── message_loc: (35,2)-(35,5) = "==="
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (35,6)-(35,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (35,6)-(35,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (37,0)-(37,6))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (37,0)-(37,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :=~
+ │ ├── message_loc: (37,2)-(37,4) = "=~"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (37,5)-(37,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (37,5)-(37,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (39,0)-(39,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (39,0)-(39,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :>
+ │ ├── message_loc: (39,2)-(39,3) = ">"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (39,4)-(39,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (39,4)-(39,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (41,0)-(41,6))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (41,0)-(41,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :>=
+ │ ├── message_loc: (41,2)-(41,4) = ">="
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (41,5)-(41,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (41,5)-(41,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (43,0)-(43,6))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (43,0)-(43,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :>>
+ │ ├── message_loc: (43,2)-(43,4) = ">>"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (43,5)-(43,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (43,5)-(43,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (45,0)-(45,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (45,0)-(45,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :^
+ │ ├── message_loc: (45,2)-(45,3) = "^"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (45,4)-(45,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (45,4)-(45,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (47,0)-(47,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (47,0)-(47,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :|
+ │ ├── message_loc: (47,2)-(47,3) = "|"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (47,4)-(47,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (47,4)-(47,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ AndNode (location: (49,0)-(49,6))
+ │ ├── left:
+ │ │ @ IntegerNode (location: (49,0)-(49,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── right:
+ │ │ @ IntegerNode (location: (49,5)-(49,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ └── operator_loc: (49,2)-(49,4) = "&&"
+ ├── @ AndNode (location: (51,0)-(51,7))
+ │ ├── left:
+ │ │ @ IntegerNode (location: (51,0)-(51,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── right:
+ │ │ @ IntegerNode (location: (51,6)-(51,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ └── operator_loc: (51,2)-(51,5) = "and"
+ ├── @ CallNode (location: (53,0)-(53,10))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (53,0)-(53,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :*
+ │ ├── message_loc: (53,2)-(53,3) = "*"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (53,4)-(53,10))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (53,4)-(53,10))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ IntegerNode (location: (53,4)-(53,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :**
+ │ │ ├── message_loc: (53,6)-(53,8) = "**"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (53,9)-(53,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (53,9)-(53,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 3
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (55,0)-(55,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (55,0)-(55,5))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ IntegerNode (location: (55,0)-(55,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :*
+ │ │ ├── message_loc: (55,2)-(55,3) = "*"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (55,4)-(55,5))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (55,4)-(55,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (55,6)-(55,7) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (55,8)-(55,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (55,8)-(55,9))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ OrNode (location: (57,0)-(57,6))
+ │ ├── left:
+ │ │ @ IntegerNode (location: (57,0)-(57,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── right:
+ │ │ @ IntegerNode (location: (57,5)-(57,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ └── operator_loc: (57,2)-(57,4) = "or"
+ ├── @ OrNode (location: (59,0)-(59,6))
+ │ ├── left:
+ │ │ @ IntegerNode (location: (59,0)-(59,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── right:
+ │ │ @ IntegerNode (location: (59,5)-(59,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ └── operator_loc: (59,2)-(59,4) = "||"
+ ├── @ CallNode (location: (61,0)-(61,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (61,0)-(61,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (61,2)-(61,3) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (61,4)-(61,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (61,4)-(61,9))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ IntegerNode (location: (61,4)-(61,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :*
+ │ │ ├── message_loc: (61,6)-(61,7) = "*"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (61,8)-(61,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (61,8)-(61,9))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 3
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ ParenthesesNode (location: (63,0)-(63,7))
+ ├── body:
+ │ @ StatementsNode (location: (63,1)-(63,6))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (63,1)-(63,6))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (63,1)-(63,2))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (63,3)-(63,4) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (63,5)-(63,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (63,5)-(63,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── opening_loc: (63,0)-(63,1) = "("
+ └── closing_loc: (63,6)-(63,7) = ")"
diff --git a/test/prism/snapshots/keyword_method_names.txt b/test/prism/snapshots/keyword_method_names.txt
new file mode 100644
index 0000000000..6d59790b07
--- /dev/null
+++ b/test/prism/snapshots/keyword_method_names.txt
@@ -0,0 +1,173 @@
+@ ProgramNode (location: (1,0)-(29,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(29,3))
+ └── body: (length: 10)
+ ├── @ DefNode (location: (1,0)-(2,3))
+ │ ├── name: :def
+ │ ├── name_loc: (1,4)-(1,7) = "def"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (2,0)-(2,3) = "end"
+ ├── @ DefNode (location: (4,0)-(5,3))
+ │ ├── name: :ensure
+ │ ├── name_loc: (4,9)-(4,15) = "ensure"
+ │ ├── receiver:
+ │ │ @ SelfNode (location: (4,4)-(4,8))
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (4,0)-(4,3) = "def"
+ │ ├── operator_loc: (4,8)-(4,9) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (5,0)-(5,3) = "end"
+ ├── @ CallNode (location: (7,0)-(10,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :private
+ │ ├── message_loc: (7,0)-(7,7) = "private"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (7,8)-(10,3))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ DefNode (location: (7,8)-(10,3))
+ │ │ ├── name: :foo
+ │ │ ├── name_loc: (7,12)-(7,15) = "foo"
+ │ │ ├── receiver: ∅
+ │ │ ├── parameters: ∅
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (8,2)-(9,5))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (8,2)-(9,5))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (8,2)-(8,5) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (8,6)-(9,5))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body: ∅
+ │ │ │ ├── opening_loc: (8,6)-(8,8) = "do"
+ │ │ │ └── closing_loc: (9,2)-(9,5) = "end"
+ │ │ ├── locals: []
+ │ │ ├── def_keyword_loc: (7,8)-(7,11) = "def"
+ │ │ ├── operator_loc: ∅
+ │ │ ├── lparen_loc: ∅
+ │ │ ├── rparen_loc: ∅
+ │ │ ├── equal_loc: ∅
+ │ │ └── end_keyword_loc: (10,0)-(10,3) = "end"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ DefNode (location: (12,0)-(13,3))
+ │ ├── name: :m
+ │ ├── name_loc: (12,4)-(12,5) = "m"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (12,6)-(12,14))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (12,6)-(12,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ NoKeywordsParameterNode (location: (12,9)-(12,14))
+ │ │ │ ├── operator_loc: (12,9)-(12,11) = "**"
+ │ │ │ └── keyword_loc: (12,11)-(12,14) = "nil"
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:a]
+ │ ├── def_keyword_loc: (12,0)-(12,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (12,5)-(12,6) = "("
+ │ ├── rparen_loc: (12,14)-(12,15) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (13,0)-(13,3) = "end"
+ ├── @ DefNode (location: (15,0)-(16,3))
+ │ ├── name: :a
+ │ ├── name_loc: (15,17)-(15,18) = "a"
+ │ ├── receiver:
+ │ │ @ SourceEncodingNode (location: (15,4)-(15,16))
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (15,0)-(15,3) = "def"
+ │ ├── operator_loc: (15,16)-(15,17) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (16,0)-(16,3) = "end"
+ ├── @ StringNode (location: (18,0)-(18,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (18,0)-(18,2) = "%{"
+ │ ├── content_loc: (18,2)-(18,5) = "abc"
+ │ ├── closing_loc: (18,5)-(18,6) = "}"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (20,0)-(20,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (20,0)-(20,2) = "%\""
+ │ ├── content_loc: (20,2)-(20,5) = "abc"
+ │ ├── closing_loc: (20,5)-(20,6) = "\""
+ │ └── unescaped: "abc"
+ ├── @ DefNode (location: (22,0)-(23,3))
+ │ ├── name: :a
+ │ ├── name_loc: (22,13)-(22,14) = "a"
+ │ ├── receiver:
+ │ │ @ SourceFileNode (location: (22,4)-(22,12))
+ │ │ ├── flags: ∅
+ │ │ └── filepath: "keyword_method_names.txt"
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (22,0)-(22,3) = "def"
+ │ ├── operator_loc: (22,12)-(22,13) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (23,0)-(23,3) = "end"
+ ├── @ DefNode (location: (25,0)-(26,3))
+ │ ├── name: :a
+ │ ├── name_loc: (25,13)-(25,14) = "a"
+ │ ├── receiver:
+ │ │ @ SourceLineNode (location: (25,4)-(25,12))
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (25,0)-(25,3) = "def"
+ │ ├── operator_loc: (25,12)-(25,13) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (26,0)-(26,3) = "end"
+ └── @ DefNode (location: (28,0)-(29,3))
+ ├── name: :a
+ ├── name_loc: (28,9)-(28,10) = "a"
+ ├── receiver:
+ │ @ NilNode (location: (28,4)-(28,7))
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (28,0)-(28,3) = "def"
+ ├── operator_loc: (28,7)-(28,9) = "::"
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (29,0)-(29,3) = "end"
diff --git a/test/prism/snapshots/keywords.txt b/test/prism/snapshots/keywords.txt
new file mode 100644
index 0000000000..b3d5c5e1c3
--- /dev/null
+++ b/test/prism/snapshots/keywords.txt
@@ -0,0 +1,47 @@
+@ ProgramNode (location: (1,0)-(11,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(11,8))
+ └── body: (length: 6)
+ ├── @ CallNode (location: (1,0)-(1,12))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (1,0)-(1,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,4)-(1,12))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,6)-(1,10))
+ │ │ └── body: (length: 1)
+ │ │ └── @ RedoNode (location: (1,6)-(1,10))
+ │ ├── opening_loc: (1,4)-(1,5) = "{"
+ │ └── closing_loc: (1,11)-(1,12) = "}"
+ ├── @ BeginNode (location: (3,0)-(3,25))
+ │ ├── begin_keyword_loc: (3,0)-(3,5) = "begin"
+ │ ├── statements: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (3,7)-(3,20))
+ │ │ ├── keyword_loc: (3,7)-(3,13) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (3,15)-(3,20))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ RetryNode (location: (3,15)-(3,20))
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (3,22)-(3,25) = "end"
+ ├── @ SelfNode (location: (5,0)-(5,4))
+ ├── @ SourceEncodingNode (location: (7,0)-(7,12))
+ ├── @ SourceFileNode (location: (9,0)-(9,8))
+ │ ├── flags: ∅
+ │ └── filepath: "keywords.txt"
+ └── @ SourceLineNode (location: (11,0)-(11,8))
diff --git a/test/prism/snapshots/lambda.txt b/test/prism/snapshots/lambda.txt
new file mode 100644
index 0000000000..0864b10369
--- /dev/null
+++ b/test/prism/snapshots/lambda.txt
@@ -0,0 +1,202 @@
+@ ProgramNode (location: (1,0)-(11,18))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(11,18))
+ └── body: (length: 5)
+ ├── @ LambdaNode (location: (1,0)-(3,4))
+ │ ├── locals: [:foo]
+ │ ├── operator_loc: (1,0)-(1,2) = "->"
+ │ ├── opening_loc: (3,2)-(3,3) = "{"
+ │ ├── closing_loc: (3,3)-(3,4) = "}"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (1,2)-(3,1))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (2,2)-(2,5))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (2,2)-(2,5))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :foo
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (1,2)-(1,3) = "("
+ │ │ └── closing_loc: (3,0)-(3,1) = ")"
+ │ └── body: ∅
+ ├── @ LambdaNode (location: (5,0)-(5,18))
+ │ ├── locals: [:x]
+ │ ├── operator_loc: (5,0)-(5,2) = "->"
+ │ ├── opening_loc: (5,15)-(5,16) = "{"
+ │ ├── closing_loc: (5,17)-(5,18) = "}"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (5,2)-(5,14))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (5,3)-(5,13))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 1)
+ │ │ │ │ └── @ OptionalKeywordParameterNode (location: (5,3)-(5,13))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :x
+ │ │ │ │ ├── name_loc: (5,3)-(5,5) = "x:"
+ │ │ │ │ └── value:
+ │ │ │ │ @ InterpolatedStringNode (location: (5,6)-(5,13))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: (5,6)-(5,7) = "\""
+ │ │ │ │ ├── parts: (length: 2)
+ │ │ │ │ │ ├── @ StringNode (location: (5,7)-(5,8))
+ │ │ │ │ │ │ ├── flags: frozen
+ │ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ │ ├── content_loc: (5,7)-(5,8) = "b"
+ │ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ │ └── unescaped: "b"
+ │ │ │ │ │ └── @ EmbeddedStatementsNode (location: (5,8)-(5,12))
+ │ │ │ │ │ ├── opening_loc: (5,8)-(5,10) = "\#{"
+ │ │ │ │ │ ├── statements:
+ │ │ │ │ │ │ @ StatementsNode (location: (5,10)-(5,11))
+ │ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ │ └── @ CallNode (location: (5,10)-(5,11))
+ │ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ │ ├── name: :a
+ │ │ │ │ │ │ ├── message_loc: (5,10)-(5,11) = "a"
+ │ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ │ └── block: ∅
+ │ │ │ │ │ └── closing_loc: (5,11)-(5,12) = "}"
+ │ │ │ │ └── closing_loc: (5,12)-(5,13) = "\""
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (5,2)-(5,3) = "("
+ │ │ └── closing_loc: (5,13)-(5,14) = ")"
+ │ └── body: ∅
+ ├── @ LambdaNode (location: (7,0)-(7,15))
+ │ ├── locals: [:a]
+ │ ├── operator_loc: (7,0)-(7,2) = "->"
+ │ ├── opening_loc: (7,13)-(7,14) = "{"
+ │ ├── closing_loc: (7,14)-(7,15) = "}"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (7,2)-(7,12))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (7,3)-(7,11))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 1)
+ │ │ │ │ └── @ OptionalKeywordParameterNode (location: (7,3)-(7,11))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── name_loc: (7,3)-(7,5) = "a:"
+ │ │ │ │ └── value:
+ │ │ │ │ @ CallNode (location: (7,6)-(7,11))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── receiver:
+ │ │ │ │ │ @ CallNode (location: (7,6)-(7,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :b
+ │ │ │ │ │ ├── message_loc: (7,6)-(7,7) = "b"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :*
+ │ │ │ │ ├── message_loc: (7,8)-(7,9) = "*"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (7,10)-(7,11))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ IntegerNode (location: (7,10)-(7,11))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 3
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (7,2)-(7,3) = "("
+ │ │ └── closing_loc: (7,11)-(7,12) = ")"
+ │ └── body: ∅
+ ├── @ LambdaNode (location: (9,0)-(9,19))
+ │ ├── locals: [:foo]
+ │ ├── operator_loc: (9,0)-(9,2) = "->"
+ │ ├── opening_loc: (9,13)-(9,15) = "do"
+ │ ├── closing_loc: (9,16)-(9,19) = "end"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (9,3)-(9,12))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (9,3)-(9,12))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 1)
+ │ │ │ │ └── @ OptionalParameterNode (location: (9,3)-(9,12))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :foo
+ │ │ │ │ ├── name_loc: (9,3)-(9,6) = "foo"
+ │ │ │ │ ├── operator_loc: (9,7)-(9,8) = "="
+ │ │ │ │ └── value:
+ │ │ │ │ @ CallNode (location: (9,9)-(9,12))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (9,9)-(9,12) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── body: ∅
+ └── @ LambdaNode (location: (11,0)-(11,18))
+ ├── locals: [:foo]
+ ├── operator_loc: (11,0)-(11,2) = "->"
+ ├── opening_loc: (11,12)-(11,14) = "do"
+ ├── closing_loc: (11,15)-(11,18) = "end"
+ ├── parameters:
+ │ @ BlockParametersNode (location: (11,3)-(11,11))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (11,3)-(11,11))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ OptionalKeywordParameterNode (location: (11,3)-(11,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── name_loc: (11,3)-(11,7) = "foo:"
+ │ │ │ └── value:
+ │ │ │ @ CallNode (location: (11,8)-(11,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (11,8)-(11,11) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: ∅
+ │ └── closing_loc: ∅
+ └── body: ∅
diff --git a/test/prism/snapshots/method_calls.txt b/test/prism/snapshots/method_calls.txt
new file mode 100644
index 0000000000..de9ba71ae0
--- /dev/null
+++ b/test/prism/snapshots/method_calls.txt
@@ -0,0 +1,2459 @@
+@ ProgramNode (location: (1,0)-(156,19))
+├── locals: [:foo]
+└── statements:
+ @ StatementsNode (location: (1,0)-(156,19))
+ └── body: (length: 67)
+ ├── @ CallNode (location: (1,0)-(1,14))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,0)-(1,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (1,3)-(1,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (1,4)-(1,7) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,8)-(1,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (1,8)-(1,14))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,8)-(1,10) = "%{"
+ │ │ ├── content_loc: (1,10)-(1,13) = "baz"
+ │ │ ├── closing_loc: (1,13)-(1,14) = "}"
+ │ │ └── unescaped: "baz"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (3,0)-(3,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (3,0)-(3,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (3,0)-(3,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (3,1)-(3,2) = "."
+ │ ├── name: :b
+ │ ├── message_loc: (3,2)-(3,3) = "b"
+ │ ├── opening_loc: (3,3)-(3,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,4)-(3,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (3,4)-(3,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── message_loc: (3,4)-(3,5) = "c"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ CallNode (location: (3,7)-(3,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :d
+ │ │ ├── message_loc: (3,7)-(3,8) = "d"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (3,8)-(3,9) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (5,0)-(5,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (5,0)-(5,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (5,0)-(5,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (5,1)-(5,2) = "."
+ │ ├── name: :b
+ │ ├── message_loc: (5,2)-(5,3) = "b"
+ │ ├── opening_loc: (5,3)-(5,4) = "("
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (5,4)-(5,5) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (7,0)-(9,7))
+ │ ├── flags: safe_navigation
+ │ ├── receiver:
+ │ │ @ CallNode (location: (7,0)-(8,6))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (7,0)-(7,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (7,0)-(7,3) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (8,2)-(8,3) = "."
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (8,3)-(8,6) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (9,2)-(9,4) = "&."
+ │ ├── name: :baz
+ │ ├── message_loc: (9,4)-(9,7) = "baz"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (11,0)-(11,2))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a!
+ │ ├── message_loc: (11,0)-(11,2) = "a!"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (13,0)-(13,4))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (13,0)-(13,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (13,0)-(13,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (13,1)-(13,2) = "."
+ │ ├── name: :call
+ │ ├── message_loc: ∅
+ │ ├── opening_loc: (13,2)-(13,3) = "("
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (13,3)-(13,4) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (15,0)-(15,11))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (15,0)-(15,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (15,0)-(15,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (15,1)-(15,2) = "."
+ │ ├── name: :call
+ │ ├── message_loc: ∅
+ │ ├── opening_loc: (15,2)-(15,3) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (15,3)-(15,10))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 3)
+ │ │ ├── @ IntegerNode (location: (15,3)-(15,4))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── @ IntegerNode (location: (15,6)-(15,7))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── @ IntegerNode (location: (15,9)-(15,10))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ ├── closing_loc: (15,10)-(15,11) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (17,0)-(17,4))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (17,0)-(17,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (17,0)-(17,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (17,1)-(17,3) = "::"
+ │ ├── name: :b
+ │ ├── message_loc: (17,3)-(17,4) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (19,0)-(19,6))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (19,0)-(19,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (19,0)-(19,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (19,1)-(19,3) = "::"
+ │ ├── name: :b
+ │ ├── message_loc: (19,3)-(19,4) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (19,5)-(19,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (19,5)-(19,6))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :c
+ │ │ ├── message_loc: (19,5)-(19,6) = "c"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (21,0)-(21,11))
+ │ ├── flags: attribute_write
+ │ ├── receiver:
+ │ │ @ CallNode (location: (21,0)-(21,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (21,0)-(21,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (21,3)-(21,4) = "."
+ │ ├── name: :bar=
+ │ ├── message_loc: (21,4)-(21,7) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (21,10)-(21,11))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (21,10)-(21,11))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (23,0)-(23,2))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a?
+ │ ├── message_loc: (23,0)-(23,2) = "a?"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (25,0)-(25,8))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (25,0)-(25,1) = "a"
+ │ ├── opening_loc: (25,1)-(25,2) = "("
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (25,8)-(25,9) = ")"
+ │ └── block:
+ │ @ BlockArgumentNode (location: (25,2)-(25,8))
+ │ ├── expression:
+ │ │ @ CallNode (location: (25,3)-(25,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :block
+ │ │ ├── message_loc: (25,3)-(25,8) = "block"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (25,2)-(25,3) = "&"
+ ├── @ CallNode (location: (27,0)-(27,11))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (27,0)-(27,1) = "a"
+ │ ├── opening_loc: (27,1)-(27,2) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (27,2)-(27,10))
+ │ │ ├── flags: contains_keyword_splat
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (27,2)-(27,10))
+ │ │ ├── flags: ∅
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocSplatNode (location: (27,2)-(27,10))
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (27,4)-(27,10))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :kwargs
+ │ │ │ ├── message_loc: (27,4)-(27,10) = "kwargs"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (27,2)-(27,4) = "**"
+ │ ├── closing_loc: (27,10)-(27,11) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (29,0)-(29,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (29,0)-(29,3))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (29,0)-(29,1))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (29,0)-(29,1) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (29,1)-(29,2) = "."
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (29,2)-(29,3) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (29,3)-(29,4) = "."
+ │ ├── name: :c
+ │ ├── message_loc: (29,4)-(29,5) = "c"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (31,0)-(31,7))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (31,0)-(31,1) = "a"
+ │ ├── opening_loc: (31,1)-(31,2) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (31,2)-(31,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (31,2)-(31,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (31,2)-(31,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ CallNode (location: (31,5)-(31,6))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :c
+ │ │ ├── message_loc: (31,5)-(31,6) = "c"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (31,6)-(31,7) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (33,0)-(33,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (33,0)-(33,1) = "a"
+ │ ├── opening_loc: (33,1)-(33,2) = "("
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (33,2)-(33,3) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (35,0)-(35,8))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (35,0)-(35,1) = "a"
+ │ ├── opening_loc: (35,1)-(35,2) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (35,2)-(35,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ SplatNode (location: (35,2)-(35,7))
+ │ │ ├── operator_loc: (35,2)-(35,3) = "*"
+ │ │ └── expression:
+ │ │ @ CallNode (location: (35,3)-(35,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :args
+ │ │ ├── message_loc: (35,3)-(35,7) = "args"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (35,7)-(35,8) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (37,0)-(37,6))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (37,0)-(37,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (37,2)-(37,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (37,2)-(37,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (37,2)-(37,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ CallNode (location: (37,5)-(37,6))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :c
+ │ │ ├── message_loc: (37,5)-(37,6) = "c"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (39,0)-(39,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (39,0)-(39,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (39,0)-(39,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (39,1)-(39,2) = "."
+ │ ├── name: :b
+ │ ├── message_loc: (39,2)-(39,3) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (39,4)-(39,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (39,4)-(39,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── message_loc: (39,4)-(39,5) = "c"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ CallNode (location: (39,7)-(39,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :d
+ │ │ ├── message_loc: (39,7)-(39,8) = "d"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ MultiWriteNode (location: (41,0)-(41,23))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ CallTargetNode (location: (41,0)-(41,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ CallNode (location: (41,0)-(41,3))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :foo
+ │ │ │ │ ├── message_loc: (41,0)-(41,3) = "foo"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── call_operator_loc: (41,3)-(41,4) = "."
+ │ │ │ ├── name: :foo=
+ │ │ │ └── message_loc: (41,4)-(41,7) = "foo"
+ │ │ └── @ CallTargetNode (location: (41,9)-(41,16))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (41,9)-(41,12))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (41,9)-(41,12) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (41,12)-(41,13) = "."
+ │ │ ├── name: :bar=
+ │ │ └── message_loc: (41,13)-(41,16) = "bar"
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (41,17)-(41,18) = "="
+ │ └── value:
+ │ @ ArrayNode (location: (41,19)-(41,23))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (41,19)-(41,20))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (41,22)-(41,23))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── opening_loc: ∅
+ │ └── closing_loc: ∅
+ ├── @ CallNode (location: (43,0)-(43,4))
+ │ ├── flags: safe_navigation
+ │ ├── receiver:
+ │ │ @ CallNode (location: (43,0)-(43,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (43,0)-(43,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (43,1)-(43,3) = "&."
+ │ ├── name: :b
+ │ ├── message_loc: (43,3)-(43,4) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (45,0)-(45,5))
+ │ ├── flags: safe_navigation
+ │ ├── receiver:
+ │ │ @ CallNode (location: (45,0)-(45,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (45,0)-(45,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (45,1)-(45,3) = "&."
+ │ ├── name: :call
+ │ ├── message_loc: ∅
+ │ ├── opening_loc: (45,3)-(45,4) = "("
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (45,4)-(45,5) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (47,0)-(47,7))
+ │ ├── flags: safe_navigation
+ │ ├── receiver:
+ │ │ @ CallNode (location: (47,0)-(47,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (47,0)-(47,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (47,1)-(47,3) = "&."
+ │ ├── name: :b
+ │ ├── message_loc: (47,3)-(47,4) = "b"
+ │ ├── opening_loc: (47,4)-(47,5) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (47,5)-(47,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (47,5)-(47,6))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :c
+ │ │ ├── message_loc: (47,5)-(47,6) = "c"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (47,6)-(47,7) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (49,0)-(49,6))
+ │ ├── flags: safe_navigation
+ │ ├── receiver:
+ │ │ @ CallNode (location: (49,0)-(49,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (49,0)-(49,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (49,1)-(49,3) = "&."
+ │ ├── name: :b
+ │ ├── message_loc: (49,3)-(49,4) = "b"
+ │ ├── opening_loc: (49,4)-(49,5) = "("
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (49,5)-(49,6) = ")"
+ │ └── block: ∅
+ ├── @ IfNode (location: (51,0)-(51,33))
+ │ ├── if_keyword_loc: (51,11)-(51,13) = "if"
+ │ ├── predicate:
+ │ │ @ AndNode (location: (51,14)-(51,33))
+ │ │ ├── left:
+ │ │ │ @ OrNode (location: (51,14)-(51,25))
+ │ │ │ ├── left:
+ │ │ │ │ @ CallNode (location: (51,14)-(51,18))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar?
+ │ │ │ │ ├── message_loc: (51,14)-(51,18) = "bar?"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── right:
+ │ │ │ │ @ CallNode (location: (51,22)-(51,25))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ ├── message_loc: (51,22)-(51,25) = "baz"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── operator_loc: (51,19)-(51,21) = "or"
+ │ │ ├── right:
+ │ │ │ @ CallNode (location: (51,30)-(51,33))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :qux
+ │ │ │ ├── message_loc: (51,30)-(51,33) = "qux"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (51,26)-(51,29) = "and"
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (51,0)-(51,10))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (51,0)-(51,10))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (51,0)-(51,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (51,4)-(51,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 2)
+ │ │ │ ├── @ SymbolNode (location: (51,4)-(51,6))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (51,4)-(51,5) = ":"
+ │ │ │ │ ├── value_loc: (51,5)-(51,6) = "a"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "a"
+ │ │ │ └── @ SymbolNode (location: (51,8)-(51,10))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (51,8)-(51,9) = ":"
+ │ │ │ ├── value_loc: (51,9)-(51,10) = "b"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: ∅
+ ├── @ CallNode (location: (53,0)-(56,1))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (53,0)-(53,3) = "foo"
+ │ ├── opening_loc: (53,3)-(53,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (53,4)-(55,4))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ SymbolNode (location: (53,4)-(53,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (53,4)-(53,5) = ":"
+ │ │ │ ├── value_loc: (53,5)-(53,6) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ └── @ SymbolNode (location: (55,2)-(55,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (55,2)-(55,3) = ":"
+ │ │ ├── value_loc: (55,3)-(55,4) = "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b"
+ │ ├── closing_loc: (56,0)-(56,1) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (58,0)-(58,10))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (58,0)-(58,3) = "foo"
+ │ ├── opening_loc: (58,3)-(58,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (58,4)-(58,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ SplatNode (location: (58,4)-(58,9))
+ │ │ ├── operator_loc: (58,4)-(58,5) = "*"
+ │ │ └── expression:
+ │ │ @ CallNode (location: (58,5)-(58,9))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :rest
+ │ │ ├── message_loc: (58,5)-(58,9) = "rest"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (58,9)-(58,10) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (60,0)-(60,39))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (60,0)-(60,3) = "foo"
+ │ ├── opening_loc: (60,3)-(60,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (60,4)-(60,32))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ SymbolNode (location: (60,4)-(60,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (60,4)-(60,5) = ":"
+ │ │ │ ├── value_loc: (60,5)-(60,6) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ └── @ KeywordHashNode (location: (60,8)-(60,32))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 2)
+ │ │ ├── @ AssocNode (location: (60,8)-(60,22))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (60,8)-(60,10))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (60,8)-(60,9) = ":"
+ │ │ │ │ ├── value_loc: (60,9)-(60,10) = "h"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "h"
+ │ │ │ ├── value:
+ │ │ │ │ @ ArrayNode (location: (60,14)-(60,22))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── elements: (length: 2)
+ │ │ │ │ │ ├── @ SymbolNode (location: (60,15)-(60,17))
+ │ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ │ ├── opening_loc: (60,15)-(60,16) = ":"
+ │ │ │ │ │ │ ├── value_loc: (60,16)-(60,17) = "x"
+ │ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ │ └── unescaped: "x"
+ │ │ │ │ │ └── @ SymbolNode (location: (60,19)-(60,21))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: (60,19)-(60,20) = ":"
+ │ │ │ │ │ ├── value_loc: (60,20)-(60,21) = "y"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "y"
+ │ │ │ │ ├── opening_loc: (60,14)-(60,15) = "["
+ │ │ │ │ └── closing_loc: (60,21)-(60,22) = "]"
+ │ │ │ └── operator_loc: (60,11)-(60,13) = "=>"
+ │ │ └── @ AssocNode (location: (60,24)-(60,32))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (60,24)-(60,26))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (60,24)-(60,25) = ":"
+ │ │ │ ├── value_loc: (60,25)-(60,26) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ ├── value:
+ │ │ │ @ SymbolNode (location: (60,30)-(60,32))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (60,30)-(60,31) = ":"
+ │ │ │ ├── value_loc: (60,31)-(60,32) = "b"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "b"
+ │ │ └── operator_loc: (60,27)-(60,29) = "=>"
+ │ ├── closing_loc: (60,39)-(60,40) = ")"
+ │ └── block:
+ │ @ BlockArgumentNode (location: (60,34)-(60,39))
+ │ ├── expression:
+ │ │ @ SymbolNode (location: (60,35)-(60,39))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (60,35)-(60,36) = ":"
+ │ │ ├── value_loc: (60,36)-(60,39) = "bar"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "bar"
+ │ └── operator_loc: (60,34)-(60,35) = "&"
+ ├── @ CallNode (location: (62,0)-(62,49))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :hi
+ │ ├── message_loc: (62,0)-(62,2) = "hi"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (62,3)-(62,49))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ IntegerNode (location: (62,3)-(62,6))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 123
+ │ │ └── @ HashNode (location: (62,8)-(62,49))
+ │ │ ├── opening_loc: (62,8)-(62,9) = "{"
+ │ │ ├── elements: (length: 3)
+ │ │ │ ├── @ AssocNode (location: (62,10)-(62,27))
+ │ │ │ │ ├── key:
+ │ │ │ │ │ @ SymbolNode (location: (62,10)-(62,16))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: (62,10)-(62,11) = ":"
+ │ │ │ │ │ ├── value_loc: (62,11)-(62,16) = "there"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "there"
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ SymbolNode (location: (62,20)-(62,27))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: (62,20)-(62,21) = ":"
+ │ │ │ │ │ ├── value_loc: (62,21)-(62,27) = "friend"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "friend"
+ │ │ │ │ └── operator_loc: (62,17)-(62,19) = "=>"
+ │ │ │ ├── @ AssocSplatNode (location: (62,29)-(62,33))
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ HashNode (location: (62,31)-(62,33))
+ │ │ │ │ │ ├── opening_loc: (62,31)-(62,32) = "{"
+ │ │ │ │ │ ├── elements: (length: 0)
+ │ │ │ │ │ └── closing_loc: (62,32)-(62,33) = "}"
+ │ │ │ │ └── operator_loc: (62,29)-(62,31) = "**"
+ │ │ │ └── @ AssocNode (location: (62,35)-(62,47))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (62,35)-(62,42))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (62,35)-(62,41) = "whatup"
+ │ │ │ │ ├── closing_loc: (62,41)-(62,42) = ":"
+ │ │ │ │ └── unescaped: "whatup"
+ │ │ │ ├── value:
+ │ │ │ │ @ SymbolNode (location: (62,43)-(62,47))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (62,43)-(62,44) = ":"
+ │ │ │ │ ├── value_loc: (62,44)-(62,47) = "dog"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "dog"
+ │ │ │ └── operator_loc: ∅
+ │ │ └── closing_loc: (62,48)-(62,49) = "}"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (64,0)-(64,36))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (64,0)-(64,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (64,4)-(64,15))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ SymbolNode (location: (64,4)-(64,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (64,4)-(64,5) = ":"
+ │ │ │ ├── value_loc: (64,5)-(64,6) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ └── @ KeywordHashNode (location: (64,8)-(64,15))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (64,8)-(64,15))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (64,8)-(64,10))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (64,8)-(64,9) = "b"
+ │ │ │ ├── closing_loc: (64,9)-(64,10) = ":"
+ │ │ │ └── unescaped: "b"
+ │ │ ├── value:
+ │ │ │ @ TrueNode (location: (64,11)-(64,15))
+ │ │ └── operator_loc: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (64,16)-(64,36))
+ │ ├── locals: [:a, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (64,19)-(64,25))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (64,20)-(64,24))
+ │ │ │ ├── requireds: (length: 2)
+ │ │ │ │ ├── @ RequiredParameterNode (location: (64,20)-(64,21))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :a
+ │ │ │ │ └── @ RequiredParameterNode (location: (64,23)-(64,24))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :b
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (64,19)-(64,20) = "|"
+ │ │ └── closing_loc: (64,24)-(64,25) = "|"
+ │ ├── body:
+ │ │ @ StatementsNode (location: (64,26)-(64,32))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (64,26)-(64,32))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :puts
+ │ │ ├── message_loc: (64,26)-(64,30) = "puts"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (64,31)-(64,32))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ LocalVariableReadNode (location: (64,31)-(64,32))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (64,16)-(64,18) = "do"
+ │ └── closing_loc: (64,33)-(64,36) = "end"
+ ├── @ CallNode (location: (66,0)-(66,17))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :hi
+ │ ├── message_loc: (66,0)-(66,2) = "hi"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (66,3)-(66,17))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (66,3)-(66,17))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (66,3)-(66,17))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (66,3)-(66,9))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (66,3)-(66,8) = "there"
+ │ │ │ ├── closing_loc: (66,8)-(66,9) = ":"
+ │ │ │ └── unescaped: "there"
+ │ │ ├── value:
+ │ │ │ @ SymbolNode (location: (66,10)-(66,17))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (66,10)-(66,11) = ":"
+ │ │ │ ├── value_loc: (66,11)-(66,17) = "friend"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "friend"
+ │ │ └── operator_loc: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (68,0)-(68,40))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :hi
+ │ ├── message_loc: (68,0)-(68,2) = "hi"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (68,3)-(68,40))
+ │ │ ├── flags: contains_keyword_splat
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (68,3)-(68,40))
+ │ │ ├── flags: ∅
+ │ │ └── elements: (length: 3)
+ │ │ ├── @ AssocNode (location: (68,3)-(68,20))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (68,3)-(68,9))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (68,3)-(68,4) = ":"
+ │ │ │ │ ├── value_loc: (68,4)-(68,9) = "there"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "there"
+ │ │ │ ├── value:
+ │ │ │ │ @ SymbolNode (location: (68,13)-(68,20))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (68,13)-(68,14) = ":"
+ │ │ │ │ ├── value_loc: (68,14)-(68,20) = "friend"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "friend"
+ │ │ │ └── operator_loc: (68,10)-(68,12) = "=>"
+ │ │ ├── @ AssocSplatNode (location: (68,22)-(68,26))
+ │ │ │ ├── value:
+ │ │ │ │ @ HashNode (location: (68,24)-(68,26))
+ │ │ │ │ ├── opening_loc: (68,24)-(68,25) = "{"
+ │ │ │ │ ├── elements: (length: 0)
+ │ │ │ │ └── closing_loc: (68,25)-(68,26) = "}"
+ │ │ │ └── operator_loc: (68,22)-(68,24) = "**"
+ │ │ └── @ AssocNode (location: (68,28)-(68,40))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (68,28)-(68,35))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (68,28)-(68,34) = "whatup"
+ │ │ │ ├── closing_loc: (68,34)-(68,35) = ":"
+ │ │ │ └── unescaped: "whatup"
+ │ │ ├── value:
+ │ │ │ @ SymbolNode (location: (68,36)-(68,40))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (68,36)-(68,37) = ":"
+ │ │ │ ├── value_loc: (68,37)-(68,40) = "dog"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "dog"
+ │ │ └── operator_loc: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (70,0)-(70,41))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :hi
+ │ ├── message_loc: (70,0)-(70,2) = "hi"
+ │ ├── opening_loc: (70,2)-(70,3) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (70,3)-(70,40))
+ │ │ ├── flags: contains_keyword_splat
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (70,3)-(70,40))
+ │ │ ├── flags: ∅
+ │ │ └── elements: (length: 3)
+ │ │ ├── @ AssocNode (location: (70,3)-(70,20))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (70,3)-(70,9))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (70,3)-(70,4) = ":"
+ │ │ │ │ ├── value_loc: (70,4)-(70,9) = "there"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "there"
+ │ │ │ ├── value:
+ │ │ │ │ @ SymbolNode (location: (70,13)-(70,20))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (70,13)-(70,14) = ":"
+ │ │ │ │ ├── value_loc: (70,14)-(70,20) = "friend"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "friend"
+ │ │ │ └── operator_loc: (70,10)-(70,12) = "=>"
+ │ │ ├── @ AssocSplatNode (location: (70,22)-(70,26))
+ │ │ │ ├── value:
+ │ │ │ │ @ HashNode (location: (70,24)-(70,26))
+ │ │ │ │ ├── opening_loc: (70,24)-(70,25) = "{"
+ │ │ │ │ ├── elements: (length: 0)
+ │ │ │ │ └── closing_loc: (70,25)-(70,26) = "}"
+ │ │ │ └── operator_loc: (70,22)-(70,24) = "**"
+ │ │ └── @ AssocNode (location: (70,28)-(70,40))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (70,28)-(70,35))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (70,28)-(70,34) = "whatup"
+ │ │ │ ├── closing_loc: (70,34)-(70,35) = ":"
+ │ │ │ └── unescaped: "whatup"
+ │ │ ├── value:
+ │ │ │ @ SymbolNode (location: (70,36)-(70,40))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (70,36)-(70,37) = ":"
+ │ │ │ ├── value_loc: (70,37)-(70,40) = "dog"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "dog"
+ │ │ └── operator_loc: ∅
+ │ ├── closing_loc: (70,40)-(70,41) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (72,0)-(72,35))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (72,0)-(72,3) = "foo"
+ │ ├── opening_loc: (72,3)-(72,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (72,4)-(72,26))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ HashNode (location: (72,4)-(72,26))
+ │ │ ├── opening_loc: (72,4)-(72,5) = "{"
+ │ │ ├── elements: (length: 2)
+ │ │ │ ├── @ AssocNode (location: (72,6)-(72,13))
+ │ │ │ │ ├── key:
+ │ │ │ │ │ @ SymbolNode (location: (72,6)-(72,8))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── value_loc: (72,6)-(72,7) = "a"
+ │ │ │ │ │ ├── closing_loc: (72,7)-(72,8) = ":"
+ │ │ │ │ │ └── unescaped: "a"
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ TrueNode (location: (72,9)-(72,13))
+ │ │ │ │ └── operator_loc: ∅
+ │ │ │ └── @ AssocNode (location: (72,15)-(72,23))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (72,15)-(72,17))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (72,15)-(72,16) = "b"
+ │ │ │ │ ├── closing_loc: (72,16)-(72,17) = ":"
+ │ │ │ │ └── unescaped: "b"
+ │ │ │ ├── value:
+ │ │ │ │ @ FalseNode (location: (72,18)-(72,23))
+ │ │ │ └── operator_loc: ∅
+ │ │ └── closing_loc: (72,25)-(72,26) = "}"
+ │ ├── closing_loc: (72,35)-(72,36) = ")"
+ │ └── block:
+ │ @ BlockArgumentNode (location: (72,28)-(72,35))
+ │ ├── expression:
+ │ │ @ SymbolNode (location: (72,29)-(72,35))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (72,29)-(72,30) = ":"
+ │ │ ├── value_loc: (72,30)-(72,35) = "block"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "block"
+ │ └── operator_loc: (72,28)-(72,29) = "&"
+ ├── @ CallNode (location: (74,0)-(74,20))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :hi
+ │ ├── message_loc: (74,0)-(74,2) = "hi"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (74,3)-(74,20))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (74,3)-(74,20))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (74,3)-(74,20))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (74,3)-(74,9))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (74,3)-(74,4) = ":"
+ │ │ │ ├── value_loc: (74,4)-(74,9) = "there"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "there"
+ │ │ ├── value:
+ │ │ │ @ SymbolNode (location: (74,13)-(74,20))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (74,13)-(74,14) = ":"
+ │ │ │ ├── value_loc: (74,14)-(74,20) = "friend"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "friend"
+ │ │ └── operator_loc: (74,10)-(74,12) = "=>"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (76,0)-(78,1))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (76,0)-(76,3) = "foo"
+ │ ├── opening_loc: (76,3)-(76,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (76,4)-(77,2))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ SymbolNode (location: (76,4)-(76,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (76,4)-(76,5) = ":"
+ │ │ │ ├── value_loc: (76,5)-(76,6) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ └── @ SymbolNode (location: (77,0)-(77,2))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (77,0)-(77,1) = ":"
+ │ │ ├── value_loc: (77,1)-(77,2) = "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b"
+ │ ├── closing_loc: (78,0)-(78,1) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (80,0)-(83,1))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (80,0)-(80,3) = "foo"
+ │ ├── opening_loc: (80,3)-(80,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (81,0)-(82,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ SymbolNode (location: (81,0)-(81,2))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (81,0)-(81,1) = ":"
+ │ │ │ ├── value_loc: (81,1)-(81,2) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ └── @ KeywordHashNode (location: (82,0)-(82,5))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (82,0)-(82,5))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (82,0)-(82,2))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (82,0)-(82,1) = "b"
+ │ │ │ ├── closing_loc: (82,1)-(82,2) = ":"
+ │ │ │ └── unescaped: "b"
+ │ │ ├── value:
+ │ │ │ @ SymbolNode (location: (82,3)-(82,5))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (82,3)-(82,4) = ":"
+ │ │ │ ├── value_loc: (82,4)-(82,5) = "c"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "c"
+ │ │ └── operator_loc: ∅
+ │ ├── closing_loc: (83,0)-(83,1) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (85,0)-(85,11))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (85,0)-(85,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockArgumentNode (location: (85,4)-(85,11))
+ │ ├── expression:
+ │ │ @ SymbolNode (location: (85,5)-(85,11))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (85,5)-(85,6) = ":"
+ │ │ ├── value_loc: (85,6)-(85,11) = "block"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "block"
+ │ └── operator_loc: (85,4)-(85,5) = "&"
+ ├── @ CallNode (location: (87,0)-(87,30))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (87,0)-(87,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (87,4)-(87,21))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (87,4)-(87,21))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 2)
+ │ │ ├── @ AssocNode (location: (87,4)-(87,11))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (87,4)-(87,6))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (87,4)-(87,5) = "a"
+ │ │ │ │ ├── closing_loc: (87,5)-(87,6) = ":"
+ │ │ │ │ └── unescaped: "a"
+ │ │ │ ├── value:
+ │ │ │ │ @ TrueNode (location: (87,7)-(87,11))
+ │ │ │ └── operator_loc: ∅
+ │ │ └── @ AssocNode (location: (87,13)-(87,21))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (87,13)-(87,15))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (87,13)-(87,14) = "b"
+ │ │ │ ├── closing_loc: (87,14)-(87,15) = ":"
+ │ │ │ └── unescaped: "b"
+ │ │ ├── value:
+ │ │ │ @ FalseNode (location: (87,16)-(87,21))
+ │ │ └── operator_loc: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockArgumentNode (location: (87,23)-(87,30))
+ │ ├── expression:
+ │ │ @ SymbolNode (location: (87,24)-(87,30))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (87,24)-(87,25) = ":"
+ │ │ ├── value_loc: (87,25)-(87,30) = "block"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "block"
+ │ └── operator_loc: (87,23)-(87,24) = "&"
+ ├── @ CallNode (location: (89,0)-(89,21))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :some_func
+ │ ├── message_loc: (89,0)-(89,9) = "some_func"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (89,10)-(89,21))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ IntegerNode (location: (89,10)-(89,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ KeywordHashNode (location: (89,13)-(89,21))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (89,13)-(89,21))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (89,13)-(89,19))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (89,13)-(89,18) = "kwarg"
+ │ │ │ ├── closing_loc: (89,18)-(89,19) = ":"
+ │ │ │ └── unescaped: "kwarg"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (89,20)-(89,21))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── operator_loc: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (91,0)-(91,18))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (91,0)-(91,6))
+ │ │ └── name: :Kernel
+ │ ├── call_operator_loc: (91,6)-(91,7) = "."
+ │ ├── name: :Integer
+ │ ├── message_loc: (91,7)-(91,14) = "Integer"
+ │ ├── opening_loc: (91,14)-(91,15) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (91,15)-(91,17))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (91,15)-(91,17))
+ │ │ ├── flags: decimal
+ │ │ └── value: 10
+ │ ├── closing_loc: (91,17)-(91,18) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (93,0)-(93,10))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (93,0)-(93,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :x
+ │ │ ├── message_loc: (93,0)-(93,1) = "x"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (93,1)-(93,2) = "."
+ │ ├── name: :each
+ │ ├── message_loc: (93,2)-(93,6) = "each"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (93,7)-(93,10))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (93,7)-(93,8) = "{"
+ │ └── closing_loc: (93,9)-(93,10) = "}"
+ ├── @ CallNode (location: (95,0)-(95,14))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (95,0)-(95,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (95,0)-(95,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (95,3)-(95,4) = "."
+ │ ├── name: :map
+ │ ├── message_loc: (95,4)-(95,7) = "map"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (95,8)-(95,14))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (95,10)-(95,12))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BackReferenceReadNode (location: (95,10)-(95,12))
+ │ │ └── name: :$&
+ │ ├── opening_loc: (95,8)-(95,9) = "{"
+ │ └── closing_loc: (95,13)-(95,14) = "}"
+ ├── @ CallNode (location: (97,0)-(97,12))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantPathNode (location: (97,0)-(97,4))
+ │ │ ├── parent:
+ │ │ │ @ ConstantReadNode (location: (97,0)-(97,1))
+ │ │ │ └── name: :A
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (97,3)-(97,4))
+ │ │ │ └── name: :B
+ │ │ └── delimiter_loc: (97,1)-(97,3) = "::"
+ │ ├── call_operator_loc: (97,4)-(97,6) = "::"
+ │ ├── name: :C
+ │ ├── message_loc: (97,6)-(97,7) = "C"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (97,8)-(97,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ SymbolNode (location: (97,8)-(97,12))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (97,8)-(97,9) = ":"
+ │ │ ├── value_loc: (97,9)-(97,12) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (99,0)-(99,13))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantPathNode (location: (99,0)-(99,4))
+ │ │ ├── parent:
+ │ │ │ @ ConstantReadNode (location: (99,0)-(99,1))
+ │ │ │ └── name: :A
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (99,3)-(99,4))
+ │ │ │ └── name: :B
+ │ │ └── delimiter_loc: (99,1)-(99,3) = "::"
+ │ ├── call_operator_loc: (99,4)-(99,6) = "::"
+ │ ├── name: :C
+ │ ├── message_loc: (99,6)-(99,7) = "C"
+ │ ├── opening_loc: (99,7)-(99,8) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (99,8)-(99,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ SymbolNode (location: (99,8)-(99,12))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (99,8)-(99,9) = ":"
+ │ │ ├── value_loc: (99,9)-(99,12) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ ├── closing_loc: (99,12)-(99,13) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (101,0)-(101,17))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantPathNode (location: (101,0)-(101,4))
+ │ │ ├── parent:
+ │ │ │ @ ConstantReadNode (location: (101,0)-(101,1))
+ │ │ │ └── name: :A
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (101,3)-(101,4))
+ │ │ │ └── name: :B
+ │ │ └── delimiter_loc: (101,1)-(101,3) = "::"
+ │ ├── call_operator_loc: (101,4)-(101,6) = "::"
+ │ ├── name: :C
+ │ ├── message_loc: (101,6)-(101,7) = "C"
+ │ ├── opening_loc: (101,7)-(101,8) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (101,8)-(101,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ SymbolNode (location: (101,8)-(101,12))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (101,8)-(101,9) = ":"
+ │ │ ├── value_loc: (101,9)-(101,12) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ ├── closing_loc: (101,12)-(101,13) = ")"
+ │ └── block:
+ │ @ BlockNode (location: (101,14)-(101,17))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (101,14)-(101,15) = "{"
+ │ └── closing_loc: (101,16)-(101,17) = "}"
+ ├── @ CallNode (location: (103,0)-(103,12))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (103,0)-(103,3) = "foo"
+ │ ├── opening_loc: (103,3)-(103,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (103,4)-(103,11))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (103,4)-(103,11))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (103,4)-(103,11))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (103,4)-(103,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (103,4)-(103,5) = "\""
+ │ │ │ ├── value_loc: (103,5)-(103,6) = "a"
+ │ │ │ ├── closing_loc: (103,6)-(103,8) = "\":"
+ │ │ │ └── unescaped: "a"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (103,9)-(103,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: -1
+ │ │ └── operator_loc: ∅
+ │ ├── closing_loc: (103,11)-(103,12) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (105,0)-(105,28))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (105,0)-(105,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (105,4)-(105,28))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (105,4)-(105,28))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (105,4)-(105,28))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (105,4)-(105,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (105,4)-(105,7) = "bar"
+ │ │ │ ├── closing_loc: (105,7)-(105,8) = ":"
+ │ │ │ └── unescaped: "bar"
+ │ │ ├── value:
+ │ │ │ @ HashNode (location: (105,9)-(105,28))
+ │ │ │ ├── opening_loc: (105,9)-(105,10) = "{"
+ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ └── @ AssocNode (location: (105,11)-(105,26))
+ │ │ │ │ ├── key:
+ │ │ │ │ │ @ SymbolNode (location: (105,11)-(105,15))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── value_loc: (105,11)-(105,14) = "baz"
+ │ │ │ │ │ ├── closing_loc: (105,14)-(105,15) = ":"
+ │ │ │ │ │ └── unescaped: "baz"
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ CallNode (location: (105,16)-(105,26))
+ │ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :qux
+ │ │ │ │ │ ├── message_loc: (105,16)-(105,19) = "qux"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block:
+ │ │ │ │ │ @ BlockNode (location: (105,20)-(105,26))
+ │ │ │ │ │ ├── locals: []
+ │ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ │ ├── body: ∅
+ │ │ │ │ │ ├── opening_loc: (105,20)-(105,22) = "do"
+ │ │ │ │ │ └── closing_loc: (105,23)-(105,26) = "end"
+ │ │ │ │ └── operator_loc: ∅
+ │ │ │ └── closing_loc: (105,27)-(105,28) = "}"
+ │ │ └── operator_loc: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (107,0)-(107,24))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (107,0)-(107,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (107,4)-(107,24))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (107,4)-(107,24))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (107,4)-(107,24))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (107,4)-(107,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (107,4)-(107,7) = "bar"
+ │ │ │ ├── closing_loc: (107,7)-(107,8) = ":"
+ │ │ │ └── unescaped: "bar"
+ │ │ ├── value:
+ │ │ │ @ HashNode (location: (107,9)-(107,24))
+ │ │ │ ├── opening_loc: (107,9)-(107,10) = "{"
+ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ └── @ AssocSplatNode (location: (107,11)-(107,22))
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ CallNode (location: (107,13)-(107,22))
+ │ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :kw
+ │ │ │ │ │ ├── message_loc: (107,13)-(107,15) = "kw"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block:
+ │ │ │ │ │ @ BlockNode (location: (107,16)-(107,22))
+ │ │ │ │ │ ├── locals: []
+ │ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ │ ├── body: ∅
+ │ │ │ │ │ ├── opening_loc: (107,16)-(107,18) = "do"
+ │ │ │ │ │ └── closing_loc: (107,19)-(107,22) = "end"
+ │ │ │ │ └── operator_loc: (107,11)-(107,13) = "**"
+ │ │ │ └── closing_loc: (107,23)-(107,24) = "}"
+ │ │ └── operator_loc: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (109,0)-(109,36))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (109,0)-(109,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (109,4)-(109,29))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (109,4)-(109,29))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (109,4)-(109,5) = "\""
+ │ │ ├── parts: (length: 1)
+ │ │ │ └── @ EmbeddedStatementsNode (location: (109,5)-(109,28))
+ │ │ │ ├── opening_loc: (109,5)-(109,7) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (109,7)-(109,27))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (109,7)-(109,27))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── receiver:
+ │ │ │ │ │ @ CallNode (location: (109,7)-(109,10))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :bar
+ │ │ │ │ │ ├── message_loc: (109,7)-(109,10) = "bar"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── call_operator_loc: (109,10)-(109,11) = "."
+ │ │ │ │ ├── name: :map
+ │ │ │ │ ├── message_loc: (109,11)-(109,14) = "map"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block:
+ │ │ │ │ @ BlockNode (location: (109,15)-(109,27))
+ │ │ │ │ ├── locals: []
+ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ ├── body:
+ │ │ │ │ │ @ StatementsNode (location: (109,18)-(109,23))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ StringNode (location: (109,18)-(109,23))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── opening_loc: (109,18)-(109,19) = "\""
+ │ │ │ │ │ ├── content_loc: (109,19)-(109,22) = "baz"
+ │ │ │ │ │ ├── closing_loc: (109,22)-(109,23) = "\""
+ │ │ │ │ │ └── unescaped: "baz"
+ │ │ │ │ ├── opening_loc: (109,15)-(109,17) = "do"
+ │ │ │ │ └── closing_loc: (109,24)-(109,27) = "end"
+ │ │ │ └── closing_loc: (109,27)-(109,28) = "}"
+ │ │ └── closing_loc: (109,28)-(109,29) = "\""
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (109,30)-(109,36))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (109,30)-(109,32) = "do"
+ │ └── closing_loc: (109,33)-(109,36) = "end"
+ ├── @ CallNode (location: (111,0)-(111,28))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (111,0)-(111,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (111,4)-(111,28))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ ClassNode (location: (111,4)-(111,28))
+ │ │ ├── locals: []
+ │ │ ├── class_keyword_loc: (111,4)-(111,9) = "class"
+ │ │ ├── constant_path:
+ │ │ │ @ ConstantReadNode (location: (111,10)-(111,13))
+ │ │ │ └── name: :Bar
+ │ │ ├── inheritance_operator_loc: ∅
+ │ │ ├── superclass: ∅
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (111,14)-(111,24))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (111,14)-(111,24))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (111,14)-(111,17) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (111,18)-(111,24))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body: ∅
+ │ │ │ ├── opening_loc: (111,18)-(111,20) = "do"
+ │ │ │ └── closing_loc: (111,21)-(111,24) = "end"
+ │ │ ├── end_keyword_loc: (111,25)-(111,28) = "end"
+ │ │ └── name: :Bar
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (113,0)-(113,29))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (113,0)-(113,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (113,4)-(113,29))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ ModuleNode (location: (113,4)-(113,29))
+ │ │ ├── locals: []
+ │ │ ├── module_keyword_loc: (113,4)-(113,10) = "module"
+ │ │ ├── constant_path:
+ │ │ │ @ ConstantReadNode (location: (113,11)-(113,14))
+ │ │ │ └── name: :Bar
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (113,15)-(113,25))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (113,15)-(113,25))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (113,15)-(113,18) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (113,19)-(113,25))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body: ∅
+ │ │ │ ├── opening_loc: (113,19)-(113,21) = "do"
+ │ │ │ └── closing_loc: (113,22)-(113,25) = "end"
+ │ │ ├── end_keyword_loc: (113,26)-(113,29) = "end"
+ │ │ └── name: :Bar
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (115,0)-(115,16))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (115,0)-(115,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (115,4)-(115,16))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ ArrayNode (location: (115,4)-(115,16))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ CallNode (location: (115,5)-(115,15))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (115,5)-(115,8) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (115,9)-(115,15))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body: ∅
+ │ │ │ ├── opening_loc: (115,9)-(115,11) = "do"
+ │ │ │ └── closing_loc: (115,12)-(115,15) = "end"
+ │ │ ├── opening_loc: (115,4)-(115,5) = "["
+ │ │ └── closing_loc: (115,15)-(115,16) = "]"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (117,0)-(117,28))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :p
+ │ ├── message_loc: (117,0)-(117,1) = "p"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (117,2)-(117,28))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ BeginNode (location: (117,2)-(117,28))
+ │ │ ├── begin_keyword_loc: (117,2)-(117,7) = "begin"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (117,8)-(117,24))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (117,8)-(117,24))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ IntegerNode (location: (117,8)-(117,9))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── call_operator_loc: (117,9)-(117,10) = "."
+ │ │ │ ├── name: :times
+ │ │ │ ├── message_loc: (117,10)-(117,15) = "times"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (117,16)-(117,24))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (117,19)-(117,20))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (117,19)-(117,20))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── opening_loc: (117,16)-(117,18) = "do"
+ │ │ │ └── closing_loc: (117,21)-(117,24) = "end"
+ │ │ ├── rescue_clause: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause: ∅
+ │ │ └── end_keyword_loc: (117,25)-(117,28) = "end"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (119,0)-(124,5))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (119,0)-(119,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (119,4)-(124,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ SymbolNode (location: (119,4)-(119,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (119,4)-(119,5) = ":"
+ │ │ │ ├── value_loc: (119,5)-(119,6) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ └── @ IfNode (location: (120,2)-(124,5))
+ │ │ ├── if_keyword_loc: (120,2)-(120,4) = "if"
+ │ │ ├── predicate:
+ │ │ │ @ CallNode (location: (120,5)-(120,6))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :x
+ │ │ │ ├── message_loc: (120,5)-(120,6) = "x"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── then_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (121,4)-(123,7))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (121,4)-(123,7))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (121,4)-(121,7) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (121,8)-(123,7))
+ │ │ │ ├── locals: [:a]
+ │ │ │ ├── parameters:
+ │ │ │ │ @ BlockParametersNode (location: (121,11)-(121,14))
+ │ │ │ │ ├── parameters:
+ │ │ │ │ │ @ ParametersNode (location: (121,12)-(121,13))
+ │ │ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ │ │ └── @ RequiredParameterNode (location: (121,12)-(121,13))
+ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ └── name: :a
+ │ │ │ │ │ ├── optionals: (length: 0)
+ │ │ │ │ │ ├── rest: ∅
+ │ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ │ ├── keywords: (length: 0)
+ │ │ │ │ │ ├── keyword_rest: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── locals: (length: 0)
+ │ │ │ │ ├── opening_loc: (121,11)-(121,12) = "|"
+ │ │ │ │ └── closing_loc: (121,13)-(121,14) = "|"
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (122,6)-(122,7))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ LocalVariableReadNode (location: (122,6)-(122,7))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── opening_loc: (121,8)-(121,10) = "do"
+ │ │ │ └── closing_loc: (123,4)-(123,7) = "end"
+ │ │ ├── consequent: ∅
+ │ │ └── end_keyword_loc: (124,2)-(124,5) = "end"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (126,0)-(135,5))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (126,0)-(126,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (126,4)-(135,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 3)
+ │ │ ├── @ SymbolNode (location: (126,4)-(126,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (126,4)-(126,5) = ":"
+ │ │ │ ├── value_loc: (126,5)-(126,6) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ ├── @ WhileNode (location: (127,2)-(131,5))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── keyword_loc: (127,2)-(127,7) = "while"
+ │ │ │ ├── closing_loc: (131,2)-(131,5) = "end"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ CallNode (location: (127,8)-(127,9))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :x
+ │ │ │ │ ├── message_loc: (127,8)-(127,9) = "x"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── statements:
+ │ │ │ @ StatementsNode (location: (128,4)-(130,7))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (128,4)-(130,7))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (128,4)-(128,7) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (128,8)-(130,7))
+ │ │ │ ├── locals: [:a]
+ │ │ │ ├── parameters:
+ │ │ │ │ @ BlockParametersNode (location: (128,11)-(128,14))
+ │ │ │ │ ├── parameters:
+ │ │ │ │ │ @ ParametersNode (location: (128,12)-(128,13))
+ │ │ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ │ │ └── @ RequiredParameterNode (location: (128,12)-(128,13))
+ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ └── name: :a
+ │ │ │ │ │ ├── optionals: (length: 0)
+ │ │ │ │ │ ├── rest: ∅
+ │ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ │ ├── keywords: (length: 0)
+ │ │ │ │ │ ├── keyword_rest: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── locals: (length: 0)
+ │ │ │ │ ├── opening_loc: (128,11)-(128,12) = "|"
+ │ │ │ │ └── closing_loc: (128,13)-(128,14) = "|"
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (129,6)-(129,7))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ LocalVariableReadNode (location: (129,6)-(129,7))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── opening_loc: (128,8)-(128,10) = "do"
+ │ │ │ └── closing_loc: (130,4)-(130,7) = "end"
+ │ │ └── @ UntilNode (location: (132,2)-(135,5))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (132,2)-(132,7) = "until"
+ │ │ ├── closing_loc: (135,2)-(135,5) = "end"
+ │ │ ├── predicate:
+ │ │ │ @ CallNode (location: (132,8)-(132,9))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :x
+ │ │ │ ├── message_loc: (132,8)-(132,9) = "x"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (133,4)-(134,7))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (133,4)-(134,7))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (133,4)-(133,7) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (133,8)-(134,7))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (133,8)-(133,10) = "do"
+ │ │ └── closing_loc: (134,4)-(134,7) = "end"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (137,0)-(137,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ HashNode (location: (137,0)-(137,2))
+ │ │ ├── opening_loc: (137,0)-(137,1) = "{"
+ │ │ ├── elements: (length: 0)
+ │ │ └── closing_loc: (137,1)-(137,2) = "}"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (137,3)-(137,4) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (137,5)-(137,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (137,5)-(137,9))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :A
+ │ │ ├── message_loc: (137,5)-(137,6) = "A"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (137,7)-(137,9))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (137,7)-(137,8) = "{"
+ │ │ └── closing_loc: (137,8)-(137,9) = "}"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (139,0)-(139,16))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ HashNode (location: (139,0)-(139,2))
+ │ │ ├── opening_loc: (139,0)-(139,1) = "{"
+ │ │ ├── elements: (length: 0)
+ │ │ └── closing_loc: (139,1)-(139,2) = "}"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (139,3)-(139,4) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (139,5)-(139,16))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (139,5)-(139,16))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :A
+ │ │ ├── message_loc: (139,5)-(139,6) = "A"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (139,7)-(139,16))
+ │ │ ├── locals: [:a]
+ │ │ ├── parameters:
+ │ │ │ @ BlockParametersNode (location: (139,9)-(139,12))
+ │ │ │ ├── parameters:
+ │ │ │ │ @ ParametersNode (location: (139,10)-(139,11))
+ │ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ │ └── @ RequiredParameterNode (location: (139,10)-(139,11))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :a
+ │ │ │ │ ├── optionals: (length: 0)
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ ├── keywords: (length: 0)
+ │ │ │ │ ├── keyword_rest: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── locals: (length: 0)
+ │ │ │ ├── opening_loc: (139,9)-(139,10) = "|"
+ │ │ │ └── closing_loc: (139,11)-(139,12) = "|"
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (139,13)-(139,14))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ LocalVariableReadNode (location: (139,13)-(139,14))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ ├── opening_loc: (139,7)-(139,8) = "{"
+ │ │ └── closing_loc: (139,15)-(139,16) = "}"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (141,0)-(141,11))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (141,0)-(141,4))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :A
+ │ │ ├── message_loc: (141,0)-(141,1) = "A"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (141,2)-(141,4))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (141,2)-(141,3) = "{"
+ │ │ └── closing_loc: (141,3)-(141,4) = "}"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (141,5)-(141,6) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (141,7)-(141,11))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (141,7)-(141,11))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :A
+ │ │ ├── message_loc: (141,7)-(141,8) = "A"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (141,9)-(141,11))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (141,9)-(141,10) = "{"
+ │ │ └── closing_loc: (141,10)-(141,11) = "}"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (143,0)-(143,11))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (143,0)-(143,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :lst
+ │ │ ├── message_loc: (143,0)-(143,3) = "lst"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :<<
+ │ ├── message_loc: (143,4)-(143,6) = "<<"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (143,7)-(143,11))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (143,7)-(143,11))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :A
+ │ │ ├── message_loc: (143,7)-(143,8) = "A"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (143,9)-(143,11))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (143,9)-(143,10) = "{"
+ │ │ └── closing_loc: (143,10)-(143,11) = "}"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ InterpolatedStringNode (location: (145,0)-(145,17))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (145,0)-(145,1) = "\""
+ │ ├── parts: (length: 1)
+ │ │ └── @ EmbeddedStatementsNode (location: (145,1)-(145,16))
+ │ │ ├── opening_loc: (145,1)-(145,3) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (145,4)-(145,14))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (145,4)-(145,14))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :join
+ │ │ │ ├── message_loc: (145,4)-(145,8) = "join"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (145,9)-(145,14))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ ParenthesesNode (location: (145,9)-(145,14))
+ │ │ │ │ ├── body:
+ │ │ │ │ │ @ StatementsNode (location: (145,10)-(145,13))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ StringNode (location: (145,10)-(145,13))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── opening_loc: (145,10)-(145,11) = "\""
+ │ │ │ │ │ ├── content_loc: (145,11)-(145,12) = " "
+ │ │ │ │ │ ├── closing_loc: (145,12)-(145,13) = "\""
+ │ │ │ │ │ └── unescaped: " "
+ │ │ │ │ ├── opening_loc: (145,9)-(145,10) = "("
+ │ │ │ │ └── closing_loc: (145,13)-(145,14) = ")"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── closing_loc: (145,15)-(145,16) = "}"
+ │ └── closing_loc: (145,16)-(145,17) = "\""
+ ├── @ InterpolatedStringNode (location: (147,0)-(147,8))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (147,0)-(147,1) = "\""
+ │ ├── parts: (length: 1)
+ │ │ └── @ EmbeddedStatementsNode (location: (147,1)-(147,7))
+ │ │ ├── opening_loc: (147,1)-(147,3) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (147,3)-(147,6))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ ParenthesesNode (location: (147,3)-(147,6))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (147,4)-(147,5))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (147,4)-(147,5))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :v
+ │ │ │ │ ├── message_loc: (147,4)-(147,5) = "v"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── opening_loc: (147,3)-(147,4) = "("
+ │ │ │ └── closing_loc: (147,5)-(147,6) = ")"
+ │ │ └── closing_loc: (147,6)-(147,7) = "}"
+ │ └── closing_loc: (147,7)-(147,8) = "\""
+ ├── @ DefNode (location: (149,0)-(149,18))
+ │ ├── name: :f
+ │ ├── name_loc: (149,4)-(149,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (149,6)-(149,7))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (149,6)-(149,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: ∅
+ │ │ │ ├── name_loc: ∅
+ │ │ │ └── operator_loc: (149,6)-(149,7) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (149,10)-(149,13))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (149,10)-(149,13))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :p
+ │ │ ├── message_loc: (149,10)-(149,11) = "p"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (149,12)-(149,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ SplatNode (location: (149,12)-(149,13))
+ │ │ │ ├── operator_loc: (149,12)-(149,13) = "*"
+ │ │ │ └── expression: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (149,0)-(149,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (149,5)-(149,6) = "("
+ │ ├── rparen_loc: (149,7)-(149,8) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (149,15)-(149,18) = "end"
+ ├── @ CallNode (location: (151,0)-(151,16))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (151,0)-(151,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (151,4)-(151,16))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ IntegerNode (location: (151,4)-(151,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ CallNode (location: (151,7)-(151,16))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :Bar
+ │ │ ├── message_loc: (151,7)-(151,10) = "Bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (151,11)-(151,16))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (151,13)-(151,14))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (151,13)-(151,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── opening_loc: (151,11)-(151,12) = "{"
+ │ │ └── closing_loc: (151,15)-(151,16) = "}"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ LocalVariableWriteNode (location: (153,0)-(153,7))
+ │ ├── name: :foo
+ │ ├── depth: 0
+ │ ├── name_loc: (153,0)-(153,3) = "foo"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (153,6)-(153,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (153,4)-(153,5) = "="
+ ├── @ CallNode (location: (154,0)-(154,6))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (154,0)-(154,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (154,4)-(154,6))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (154,4)-(154,5) = "{"
+ │ └── closing_loc: (154,5)-(154,6) = "}"
+ └── @ CallNode (location: (156,0)-(156,19))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ InstanceVariableReadNode (location: (156,0)-(156,2))
+ │ └── name: :@a
+ ├── call_operator_loc: (156,2)-(156,3) = "."
+ ├── name: :b
+ ├── message_loc: (156,3)-(156,4) = "b"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (156,5)-(156,19))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ KeywordHashNode (location: (156,5)-(156,19))
+ │ ├── flags: ∅
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (156,5)-(156,19))
+ │ ├── key:
+ │ │ @ InterpolatedSymbolNode (location: (156,5)-(156,16))
+ │ │ ├── opening_loc: (156,5)-(156,6) = "\""
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (156,6)-(156,7))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (156,6)-(156,7) = "c"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "c"
+ │ │ │ └── @ EmbeddedStatementsNode (location: (156,7)-(156,14))
+ │ │ │ ├── opening_loc: (156,7)-(156,9) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (156,9)-(156,13))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (156,9)-(156,13))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :name
+ │ │ │ │ ├── message_loc: (156,9)-(156,13) = "name"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── closing_loc: (156,13)-(156,14) = "}"
+ │ │ └── closing_loc: (156,14)-(156,16) = "\":"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (156,17)-(156,19))
+ │ │ ├── flags: decimal
+ │ │ └── value: 42
+ │ └── operator_loc: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/methods.txt b/test/prism/snapshots/methods.txt
new file mode 100644
index 0000000000..76c0361827
--- /dev/null
+++ b/test/prism/snapshots/methods.txt
@@ -0,0 +1,2054 @@
+@ ProgramNode (location: (1,0)-(183,37))
+├── locals: [:a, :c, :foo]
+└── statements:
+ @ StatementsNode (location: (1,0)-(183,37))
+ └── body: (length: 69)
+ ├── @ DefNode (location: (1,0)-(2,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (1,4)-(1,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,8)-(1,18))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ MultiTargetNode (location: (1,8)-(1,18))
+ │ │ │ ├── lefts: (length: 2)
+ │ │ │ │ ├── @ RequiredParameterNode (location: (1,9)-(1,12))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :bar
+ │ │ │ │ └── @ RequiredParameterNode (location: (1,14)-(1,17))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :baz
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: (1,8)-(1,9) = "("
+ │ │ │ └── rparen_loc: (1,17)-(1,18) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:bar, :baz]
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (1,7)-(1,8) = "("
+ │ ├── rparen_loc: (1,18)-(1,19) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (2,0)-(2,3) = "end"
+ ├── @ DefNode (location: (4,0)-(5,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (4,4)-(4,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (4,8)-(4,44))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ MultiTargetNode (location: (4,8)-(4,18))
+ │ │ │ ├── lefts: (length: 2)
+ │ │ │ │ ├── @ RequiredParameterNode (location: (4,9)-(4,12))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :bar
+ │ │ │ │ └── @ RequiredParameterNode (location: (4,14)-(4,17))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :baz
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: (4,8)-(4,9) = "("
+ │ │ │ └── rparen_loc: (4,17)-(4,18) = ")"
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (4,20)-(4,32))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :optional
+ │ │ │ ├── name_loc: (4,20)-(4,28) = "optional"
+ │ │ │ ├── operator_loc: (4,29)-(4,30) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (4,31)-(4,32))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ MultiTargetNode (location: (4,34)-(4,44))
+ │ │ │ ├── lefts: (length: 2)
+ │ │ │ │ ├── @ RequiredParameterNode (location: (4,35)-(4,38))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :bin
+ │ │ │ │ └── @ RequiredParameterNode (location: (4,40)-(4,43))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :bag
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: (4,34)-(4,35) = "("
+ │ │ │ └── rparen_loc: (4,43)-(4,44) = ")"
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:bar, :baz, :optional, :bin, :bag]
+ │ ├── def_keyword_loc: (4,0)-(4,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (4,7)-(4,8) = "("
+ │ ├── rparen_loc: (4,44)-(4,45) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (5,0)-(5,3) = "end"
+ ├── @ DefNode (location: (8,0)-(8,18))
+ │ ├── name: :a
+ │ ├── name_loc: (8,4)-(8,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (8,0)-(8,18))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── rescue_clause: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause:
+ │ │ │ @ EnsureNode (location: (8,7)-(8,18))
+ │ │ │ ├── ensure_keyword_loc: (8,7)-(8,13) = "ensure"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── end_keyword_loc: (8,15)-(8,18) = "end"
+ │ │ └── end_keyword_loc: (8,15)-(8,18) = "end"
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (8,0)-(8,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (8,15)-(8,18) = "end"
+ ├── @ DefNode (location: (10,0)-(11,3))
+ │ ├── name: :a
+ │ ├── name_loc: (10,8)-(10,9) = "a"
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (10,4)-(10,7))
+ │ │ ├── body:
+ │ │ │ @ CallNode (location: (10,5)-(10,6))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (10,5)-(10,6) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (10,4)-(10,5) = "("
+ │ │ └── closing_loc: (10,6)-(10,7) = ")"
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (10,0)-(10,3) = "def"
+ │ ├── operator_loc: (10,7)-(10,8) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (11,0)-(11,3) = "end"
+ ├── @ DefNode (location: (13,0)-(14,3))
+ │ ├── name: :b
+ │ ├── name_loc: (13,9)-(13,10) = "b"
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (13,4)-(13,7))
+ │ │ ├── body:
+ │ │ │ @ CallNode (location: (13,5)-(13,6))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (13,5)-(13,6) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (13,4)-(13,5) = "("
+ │ │ └── closing_loc: (13,6)-(13,7) = ")"
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (13,0)-(13,3) = "def"
+ │ ├── operator_loc: (13,7)-(13,9) = "::"
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (14,0)-(14,3) = "end"
+ ├── @ DefNode (location: (16,0)-(17,3))
+ │ ├── name: :a
+ │ ├── name_loc: (16,10)-(16,11) = "a"
+ │ ├── receiver:
+ │ │ @ FalseNode (location: (16,4)-(16,9))
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (16,0)-(16,3) = "def"
+ │ ├── operator_loc: (16,9)-(16,10) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (17,0)-(17,3) = "end"
+ ├── @ DefNode (location: (19,0)-(20,3))
+ │ ├── name: :a
+ │ ├── name_loc: (19,4)-(19,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (19,6)-(19,9))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ ForwardingParameterNode (location: (19,6)-(19,9))
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (19,0)-(19,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (19,5)-(19,6) = "("
+ │ ├── rparen_loc: (19,9)-(19,10) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (20,0)-(20,3) = "end"
+ ├── @ DefNode (location: (22,0)-(23,3))
+ │ ├── name: :a
+ │ ├── name_loc: (22,9)-(22,10) = "a"
+ │ ├── receiver:
+ │ │ @ GlobalVariableReadNode (location: (22,4)-(22,8))
+ │ │ └── name: :$var
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (22,0)-(22,3) = "def"
+ │ ├── operator_loc: (22,8)-(22,9) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (23,0)-(23,3) = "end"
+ ├── @ DefNode (location: (25,0)-(26,3))
+ │ ├── name: :b
+ │ ├── name_loc: (25,6)-(25,7) = "b"
+ │ ├── receiver:
+ │ │ @ CallNode (location: (25,4)-(25,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (25,4)-(25,5) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (25,0)-(25,3) = "def"
+ │ ├── operator_loc: (25,5)-(25,6) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (26,0)-(26,3) = "end"
+ ├── @ DefNode (location: (28,0)-(29,3))
+ │ ├── name: :a
+ │ ├── name_loc: (28,9)-(28,10) = "a"
+ │ ├── receiver:
+ │ │ @ InstanceVariableReadNode (location: (28,4)-(28,8))
+ │ │ └── name: :@var
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (28,0)-(28,3) = "def"
+ │ ├── operator_loc: (28,8)-(28,9) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (29,0)-(29,3) = "end"
+ ├── @ DefNode (location: (31,0)-(31,13))
+ │ ├── name: :a
+ │ ├── name_loc: (31,4)-(31,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (31,6)-(31,8))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ RequiredKeywordParameterNode (location: (31,6)-(31,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ └── name_loc: (31,6)-(31,8) = "b:"
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:b]
+ │ ├── def_keyword_loc: (31,0)-(31,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (31,10)-(31,13) = "end"
+ ├── @ StringNode (location: (33,0)-(33,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (33,0)-(33,2) = "%,"
+ │ ├── content_loc: (33,2)-(33,5) = "abc"
+ │ ├── closing_loc: (33,5)-(33,6) = ","
+ │ └── unescaped: "abc"
+ ├── @ DefNode (location: (35,0)-(36,3))
+ │ ├── name: :a
+ │ ├── name_loc: (35,4)-(35,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (35,6)-(35,8))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ RequiredKeywordParameterNode (location: (35,6)-(35,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ └── name_loc: (35,6)-(35,8) = "b:"
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:b]
+ │ ├── def_keyword_loc: (35,0)-(35,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (35,5)-(35,6) = "("
+ │ ├── rparen_loc: (35,8)-(35,9) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (36,0)-(36,3) = "end"
+ ├── @ DefNode (location: (38,0)-(39,3))
+ │ ├── name: :a
+ │ ├── name_loc: (38,4)-(38,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (38,6)-(38,9))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ KeywordRestParameterNode (location: (38,6)-(38,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (38,8)-(38,9) = "b"
+ │ │ │ └── operator_loc: (38,6)-(38,8) = "**"
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:b]
+ │ ├── def_keyword_loc: (38,0)-(38,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (38,5)-(38,6) = "("
+ │ ├── rparen_loc: (38,9)-(38,10) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (39,0)-(39,3) = "end"
+ ├── @ DefNode (location: (41,0)-(42,3))
+ │ ├── name: :a
+ │ ├── name_loc: (41,4)-(41,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (41,6)-(41,8))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ KeywordRestParameterNode (location: (41,6)-(41,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: ∅
+ │ │ │ ├── name_loc: ∅
+ │ │ │ └── operator_loc: (41,6)-(41,8) = "**"
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (41,0)-(41,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (41,5)-(41,6) = "("
+ │ ├── rparen_loc: (41,8)-(41,9) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (42,0)-(42,3) = "end"
+ ├── @ LocalVariableWriteNode (location: (44,0)-(44,5))
+ │ ├── name: :a
+ │ ├── depth: 0
+ │ ├── name_loc: (44,0)-(44,1) = "a"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (44,4)-(44,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (44,2)-(44,3) = "="
+ ├── @ DefNode (location: (44,7)-(45,3))
+ │ ├── name: :a
+ │ ├── name_loc: (44,11)-(44,12) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (44,7)-(44,10) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (45,0)-(45,3) = "end"
+ ├── @ DefNode (location: (47,0)-(48,3))
+ │ ├── name: :a
+ │ ├── name_loc: (47,4)-(47,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (47,6)-(47,13))
+ │ │ ├── requireds: (length: 3)
+ │ │ │ ├── @ RequiredParameterNode (location: (47,6)-(47,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :b
+ │ │ │ ├── @ RequiredParameterNode (location: (47,9)-(47,10))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :c
+ │ │ │ └── @ RequiredParameterNode (location: (47,12)-(47,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :d
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:b, :c, :d]
+ │ ├── def_keyword_loc: (47,0)-(47,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (48,0)-(48,3) = "end"
+ ├── @ DefNode (location: (50,0)-(51,3))
+ │ ├── name: :a
+ │ ├── name_loc: (50,8)-(50,9) = "a"
+ │ ├── receiver:
+ │ │ @ NilNode (location: (50,4)-(50,7))
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (50,0)-(50,3) = "def"
+ │ ├── operator_loc: (50,7)-(50,8) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (51,0)-(51,3) = "end"
+ ├── @ DefNode (location: (53,0)-(54,3))
+ │ ├── name: :a
+ │ ├── name_loc: (53,4)-(53,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (53,6)-(53,14))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 2)
+ │ │ │ ├── @ RequiredKeywordParameterNode (location: (53,6)-(53,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ └── name_loc: (53,6)-(53,8) = "b:"
+ │ │ │ └── @ OptionalKeywordParameterNode (location: (53,10)-(53,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── name_loc: (53,10)-(53,12) = "c:"
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (53,13)-(53,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:b, :c]
+ │ ├── def_keyword_loc: (53,0)-(53,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (54,0)-(54,3) = "end"
+ ├── @ DefNode (location: (56,0)-(57,3))
+ │ ├── name: :a
+ │ ├── name_loc: (56,4)-(56,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (56,6)-(56,14))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 2)
+ │ │ │ ├── @ RequiredKeywordParameterNode (location: (56,6)-(56,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ └── name_loc: (56,6)-(56,8) = "b:"
+ │ │ │ └── @ OptionalKeywordParameterNode (location: (56,10)-(56,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── name_loc: (56,10)-(56,12) = "c:"
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (56,13)-(56,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:b, :c]
+ │ ├── def_keyword_loc: (56,0)-(56,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (56,5)-(56,6) = "("
+ │ ├── rparen_loc: (56,14)-(56,15) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (57,0)-(57,3) = "end"
+ ├── @ DefNode (location: (59,0)-(61,3))
+ │ ├── name: :a
+ │ ├── name_loc: (59,4)-(59,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (59,6)-(60,7))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 2)
+ │ │ │ ├── @ OptionalKeywordParameterNode (location: (59,6)-(60,3))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── name_loc: (59,6)-(59,8) = "b:"
+ │ │ │ │ └── value:
+ │ │ │ │ @ IntegerNode (location: (60,2)-(60,3))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── @ RequiredKeywordParameterNode (location: (60,5)-(60,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :c
+ │ │ │ └── name_loc: (60,5)-(60,7) = "c:"
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:b, :c]
+ │ ├── def_keyword_loc: (59,0)-(59,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (59,5)-(59,6) = "("
+ │ ├── rparen_loc: (60,7)-(60,8) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (61,0)-(61,3) = "end"
+ ├── @ StringNode (location: (63,0)-(63,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (63,0)-(63,2) = "%."
+ │ ├── content_loc: (63,2)-(63,5) = "abc"
+ │ ├── closing_loc: (63,5)-(63,6) = "."
+ │ └── unescaped: "abc"
+ ├── @ DefNode (location: (65,0)-(66,3))
+ │ ├── name: :a
+ │ ├── name_loc: (65,4)-(65,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (65,6)-(65,18))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 2)
+ │ │ │ ├── @ OptionalParameterNode (location: (65,6)-(65,11))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── name_loc: (65,6)-(65,7) = "b"
+ │ │ │ │ ├── operator_loc: (65,8)-(65,9) = "="
+ │ │ │ │ └── value:
+ │ │ │ │ @ IntegerNode (location: (65,10)-(65,11))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── @ OptionalParameterNode (location: (65,13)-(65,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── name_loc: (65,13)-(65,14) = "c"
+ │ │ │ ├── operator_loc: (65,15)-(65,16) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (65,17)-(65,18))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:b, :c]
+ │ ├── def_keyword_loc: (65,0)-(65,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (66,0)-(66,3) = "end"
+ ├── @ DefNode (location: (68,0)-(69,3))
+ │ ├── name: :a
+ │ ├── name_loc: (68,4)-(68,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (68,0)-(68,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (68,5)-(68,6) = "("
+ │ ├── rparen_loc: (68,6)-(68,7) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (69,0)-(69,3) = "end"
+ ├── @ DefNode (location: (71,0)-(72,3))
+ │ ├── name: :a
+ │ ├── name_loc: (71,4)-(71,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (71,6)-(71,14))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (71,6)-(71,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :b
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (71,9)-(71,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── name_loc: (71,9)-(71,10) = "c"
+ │ │ │ ├── operator_loc: (71,11)-(71,12) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (71,13)-(71,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:b, :c]
+ │ ├── def_keyword_loc: (71,0)-(71,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (72,0)-(72,3) = "end"
+ ├── @ DefNode (location: (74,0)-(75,3))
+ │ ├── name: :a
+ │ ├── name_loc: (74,4)-(74,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (74,6)-(74,7))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (74,6)-(74,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :b
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:b]
+ │ ├── def_keyword_loc: (74,0)-(74,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (75,0)-(75,3) = "end"
+ ├── @ DefNode (location: (77,0)-(77,32))
+ │ ├── name: :a
+ │ ├── name_loc: (77,4)-(77,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (77,0)-(77,32))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── rescue_clause:
+ │ │ │ @ RescueNode (location: (77,7)-(77,13))
+ │ │ │ ├── keyword_loc: (77,7)-(77,13) = "rescue"
+ │ │ │ ├── exceptions: (length: 0)
+ │ │ │ ├── operator_loc: ∅
+ │ │ │ ├── reference: ∅
+ │ │ │ ├── statements: ∅
+ │ │ │ └── consequent: ∅
+ │ │ ├── else_clause:
+ │ │ │ @ ElseNode (location: (77,15)-(77,27))
+ │ │ │ ├── else_keyword_loc: (77,15)-(77,19) = "else"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── end_keyword_loc: (77,21)-(77,27) = "ensure"
+ │ │ ├── ensure_clause:
+ │ │ │ @ EnsureNode (location: (77,21)-(77,32))
+ │ │ │ ├── ensure_keyword_loc: (77,21)-(77,27) = "ensure"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── end_keyword_loc: (77,29)-(77,32) = "end"
+ │ │ └── end_keyword_loc: (77,29)-(77,32) = "end"
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (77,0)-(77,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (77,29)-(77,32) = "end"
+ ├── @ DefNode (location: (79,0)-(80,3))
+ │ ├── name: :a
+ │ ├── name_loc: (79,4)-(79,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (79,6)-(79,8))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (79,6)-(79,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (79,7)-(79,8) = "b"
+ │ │ │ └── operator_loc: (79,6)-(79,7) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:b]
+ │ ├── def_keyword_loc: (79,0)-(79,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (80,0)-(80,3) = "end"
+ ├── @ DefNode (location: (82,0)-(83,3))
+ │ ├── name: :a
+ │ ├── name_loc: (82,4)-(82,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (82,6)-(82,7))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (82,6)-(82,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: ∅
+ │ │ │ ├── name_loc: ∅
+ │ │ │ └── operator_loc: (82,6)-(82,7) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (82,0)-(82,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (82,5)-(82,6) = "("
+ │ ├── rparen_loc: (82,7)-(82,8) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (83,0)-(83,3) = "end"
+ ├── @ DefNode (location: (85,0)-(87,3))
+ │ ├── name: :a
+ │ ├── name_loc: (85,4)-(85,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (86,0)-(86,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableWriteNode (location: (86,0)-(86,5))
+ │ │ ├── name: :b
+ │ │ ├── depth: 0
+ │ │ ├── name_loc: (86,0)-(86,1) = "b"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (86,4)-(86,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (86,2)-(86,3) = "="
+ │ ├── locals: [:b]
+ │ ├── def_keyword_loc: (85,0)-(85,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (87,0)-(87,3) = "end"
+ ├── @ DefNode (location: (89,0)-(90,3))
+ │ ├── name: :a
+ │ ├── name_loc: (89,9)-(89,10) = "a"
+ │ ├── receiver:
+ │ │ @ SelfNode (location: (89,4)-(89,8))
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (89,0)-(89,3) = "def"
+ │ ├── operator_loc: (89,8)-(89,9) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (90,0)-(90,3) = "end"
+ ├── @ DefNode (location: (92,0)-(93,3))
+ │ ├── name: :a
+ │ ├── name_loc: (92,9)-(92,10) = "a"
+ │ ├── receiver:
+ │ │ @ TrueNode (location: (92,4)-(92,8))
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (92,0)-(92,3) = "def"
+ │ ├── operator_loc: (92,8)-(92,9) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (93,0)-(93,3) = "end"
+ ├── @ DefNode (location: (95,0)-(96,3))
+ │ ├── name: :a
+ │ ├── name_loc: (95,4)-(95,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (95,0)-(95,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (96,0)-(96,3) = "end"
+ ├── @ DefNode (location: (98,0)-(101,3))
+ │ ├── name: :hi
+ │ ├── name_loc: (98,4)-(98,6) = "hi"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (99,0)-(100,4))
+ │ │ └── body: (length: 2)
+ │ │ ├── @ IfNode (location: (99,0)-(99,18))
+ │ │ │ ├── if_keyword_loc: (99,11)-(99,13) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ TrueNode (location: (99,14)-(99,18))
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (99,0)-(99,10))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ ReturnNode (location: (99,0)-(99,10))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── keyword_loc: (99,0)-(99,6) = "return"
+ │ │ │ │ └── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (99,7)-(99,10))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ SymbolNode (location: (99,7)-(99,10))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (99,7)-(99,8) = ":"
+ │ │ │ │ ├── value_loc: (99,8)-(99,10) = "hi"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "hi"
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ └── @ SymbolNode (location: (100,0)-(100,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (100,0)-(100,1) = ":"
+ │ │ ├── value_loc: (100,1)-(100,4) = "bye"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "bye"
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (98,0)-(98,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (101,0)-(101,3) = "end"
+ ├── @ DefNode (location: (103,0)-(103,11))
+ │ ├── name: :foo
+ │ ├── name_loc: (103,4)-(103,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (103,10)-(103,11))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (103,10)-(103,11))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (103,0)-(103,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: (103,8)-(103,9) = "="
+ │ └── end_keyword_loc: ∅
+ ├── @ DefNode (location: (104,0)-(104,11))
+ │ ├── name: :bar
+ │ ├── name_loc: (104,4)-(104,7) = "bar"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (104,10)-(104,11))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (104,10)-(104,11))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (104,0)-(104,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: (104,8)-(104,9) = "="
+ │ └── end_keyword_loc: ∅
+ ├── @ DefNode (location: (106,0)-(106,18))
+ │ ├── name: :foo
+ │ ├── name_loc: (106,4)-(106,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (106,8)-(106,11))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (106,8)-(106,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :bar
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (106,15)-(106,18))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (106,15)-(106,18))
+ │ │ ├── flags: decimal
+ │ │ └── value: 123
+ │ ├── locals: [:bar]
+ │ ├── def_keyword_loc: (106,0)-(106,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (106,7)-(106,8) = "("
+ │ ├── rparen_loc: (106,11)-(106,12) = ")"
+ │ ├── equal_loc: (106,13)-(106,14) = "="
+ │ └── end_keyword_loc: ∅
+ ├── @ DefNode (location: (108,0)-(108,13))
+ │ ├── name: :foo
+ │ ├── name_loc: (108,4)-(108,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (108,10)-(108,13))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (108,10)-(108,13))
+ │ │ ├── flags: decimal
+ │ │ └── value: 123
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (108,0)-(108,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: (108,8)-(108,9) = "="
+ │ └── end_keyword_loc: ∅
+ ├── @ DefNode (location: (110,0)-(110,19))
+ │ ├── name: :a
+ │ ├── name_loc: (110,4)-(110,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (110,6)-(110,7))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (110,6)-(110,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: ∅
+ │ │ │ ├── name_loc: ∅
+ │ │ │ └── operator_loc: (110,6)-(110,7) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (110,10)-(110,14))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (110,10)-(110,14))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (110,10)-(110,11) = "b"
+ │ │ ├── opening_loc: (110,11)-(110,12) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (110,12)-(110,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ SplatNode (location: (110,12)-(110,13))
+ │ │ │ ├── operator_loc: (110,12)-(110,13) = "*"
+ │ │ │ └── expression: ∅
+ │ │ ├── closing_loc: (110,13)-(110,14) = ")"
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (110,0)-(110,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (110,5)-(110,6) = "("
+ │ ├── rparen_loc: (110,7)-(110,8) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (110,16)-(110,19) = "end"
+ ├── @ DefNode (location: (112,0)-(112,23))
+ │ ├── name: :a
+ │ ├── name_loc: (112,4)-(112,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (112,6)-(112,9))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ ForwardingParameterNode (location: (112,6)-(112,9))
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (112,12)-(112,18))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (112,12)-(112,18))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (112,12)-(112,13) = "b"
+ │ │ ├── opening_loc: (112,13)-(112,14) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (112,14)-(112,17))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ ForwardingArgumentsNode (location: (112,14)-(112,17))
+ │ │ ├── closing_loc: (112,17)-(112,18) = ")"
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (112,0)-(112,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (112,5)-(112,6) = "("
+ │ ├── rparen_loc: (112,9)-(112,10) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (112,20)-(112,23) = "end"
+ ├── @ DefNode (location: (114,0)-(114,29))
+ │ ├── name: :a
+ │ ├── name_loc: (114,4)-(114,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (114,6)-(114,9))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ ForwardingParameterNode (location: (114,6)-(114,9))
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (114,12)-(114,24))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (114,12)-(114,24))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (114,12)-(114,13) = "b"
+ │ │ ├── opening_loc: (114,13)-(114,14) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (114,14)-(114,23))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 3)
+ │ │ │ ├── @ IntegerNode (location: (114,14)-(114,15))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── @ IntegerNode (location: (114,17)-(114,18))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ └── @ ForwardingArgumentsNode (location: (114,20)-(114,23))
+ │ │ ├── closing_loc: (114,23)-(114,24) = ")"
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (114,0)-(114,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (114,5)-(114,6) = "("
+ │ ├── rparen_loc: (114,9)-(114,10) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (114,26)-(114,29) = "end"
+ ├── @ DefNode (location: (116,0)-(117,3))
+ │ ├── name: :a
+ │ ├── name_loc: (116,12)-(116,13) = "a"
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (116,4)-(116,11))
+ │ │ ├── body:
+ │ │ │ @ LocalVariableWriteNode (location: (116,5)-(116,10))
+ │ │ │ ├── name: :c
+ │ │ │ ├── depth: 0
+ │ │ │ ├── name_loc: (116,5)-(116,6) = "c"
+ │ │ │ ├── value:
+ │ │ │ │ @ CallNode (location: (116,9)-(116,10))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── message_loc: (116,9)-(116,10) = "b"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── operator_loc: (116,7)-(116,8) = "="
+ │ │ ├── opening_loc: (116,4)-(116,5) = "("
+ │ │ └── closing_loc: (116,10)-(116,11) = ")"
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (116,0)-(116,3) = "def"
+ │ ├── operator_loc: (116,11)-(116,12) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (117,0)-(117,3) = "end"
+ ├── @ DefNode (location: (119,0)-(120,3))
+ │ ├── name: :a
+ │ ├── name_loc: (119,4)-(119,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (119,6)-(119,8))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (119,6)-(119,8))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (119,7)-(119,8) = "b"
+ │ │ └── operator_loc: (119,6)-(119,7) = "&"
+ │ ├── body: ∅
+ │ ├── locals: [:b]
+ │ ├── def_keyword_loc: (119,0)-(119,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (120,0)-(120,3) = "end"
+ ├── @ DefNode (location: (122,0)-(123,3))
+ │ ├── name: :a
+ │ ├── name_loc: (122,4)-(122,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (122,6)-(122,7))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (122,6)-(122,7))
+ │ │ ├── flags: ∅
+ │ │ ├── name: ∅
+ │ │ ├── name_loc: ∅
+ │ │ └── operator_loc: (122,6)-(122,7) = "&"
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (122,0)-(122,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (122,5)-(122,6) = "("
+ │ ├── rparen_loc: (122,7)-(122,8) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (123,0)-(123,3) = "end"
+ ├── @ DefNode (location: (125,0)-(126,3))
+ │ ├── name: :a
+ │ ├── name_loc: (125,10)-(125,11) = "a"
+ │ ├── receiver:
+ │ │ @ ClassVariableReadNode (location: (125,4)-(125,9))
+ │ │ └── name: :@@var
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (125,0)-(125,3) = "def"
+ │ ├── operator_loc: (125,9)-(125,10) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (126,0)-(126,3) = "end"
+ ├── @ DefNode (location: (128,0)-(129,3))
+ │ ├── name: :C
+ │ ├── name_loc: (128,12)-(128,13) = "C"
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (128,4)-(128,11))
+ │ │ ├── body:
+ │ │ │ @ LocalVariableWriteNode (location: (128,5)-(128,10))
+ │ │ │ ├── name: :a
+ │ │ │ ├── depth: 0
+ │ │ │ ├── name_loc: (128,5)-(128,6) = "a"
+ │ │ │ ├── value:
+ │ │ │ │ @ CallNode (location: (128,9)-(128,10))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── message_loc: (128,9)-(128,10) = "b"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── operator_loc: (128,7)-(128,8) = "="
+ │ │ ├── opening_loc: (128,4)-(128,5) = "("
+ │ │ └── closing_loc: (128,10)-(128,11) = ")"
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (128,0)-(128,3) = "def"
+ │ ├── operator_loc: (128,11)-(128,12) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (129,0)-(129,3) = "end"
+ ├── @ DefNode (location: (131,0)-(131,28))
+ │ ├── name: :Array_function
+ │ ├── name_loc: (131,9)-(131,23) = "Array_function"
+ │ ├── receiver:
+ │ │ @ SelfNode (location: (131,4)-(131,8))
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (131,0)-(131,3) = "def"
+ │ ├── operator_loc: (131,8)-(131,9) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (131,25)-(131,28) = "end"
+ ├── @ ConstantWriteNode (location: (133,0)-(133,9))
+ │ ├── name: :Const
+ │ ├── name_loc: (133,0)-(133,5) = "Const"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (133,8)-(133,9))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (133,6)-(133,7) = "="
+ ├── @ DefNode (location: (133,11)-(134,3))
+ │ ├── name: :a
+ │ ├── name_loc: (133,21)-(133,22) = "a"
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (133,15)-(133,20))
+ │ │ └── name: :Const
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (133,11)-(133,14) = "def"
+ │ ├── operator_loc: (133,20)-(133,21) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (134,0)-(134,3) = "end"
+ ├── @ DefNode (location: (136,0)-(136,31))
+ │ ├── name: :a
+ │ ├── name_loc: (136,4)-(136,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (136,6)-(136,9))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ ForwardingParameterNode (location: (136,6)-(136,9))
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (136,12)-(136,26))
+ │ │ └── body: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (136,12)-(136,26))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (136,12)-(136,13) = "\""
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (136,13)-(136,16))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (136,13)-(136,16) = "foo"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ └── @ EmbeddedStatementsNode (location: (136,16)-(136,25))
+ │ │ │ ├── opening_loc: (136,16)-(136,18) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (136,18)-(136,24))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (136,18)-(136,24))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── message_loc: (136,18)-(136,19) = "b"
+ │ │ │ │ ├── opening_loc: (136,19)-(136,20) = "("
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (136,20)-(136,23))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ ForwardingArgumentsNode (location: (136,20)-(136,23))
+ │ │ │ │ ├── closing_loc: (136,23)-(136,24) = ")"
+ │ │ │ │ └── block: ∅
+ │ │ │ └── closing_loc: (136,24)-(136,25) = "}"
+ │ │ └── closing_loc: (136,25)-(136,26) = "\""
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (136,0)-(136,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (136,5)-(136,6) = "("
+ │ ├── rparen_loc: (136,9)-(136,10) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (136,28)-(136,31) = "end"
+ ├── @ DefNode (location: (138,0)-(140,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (138,4)-(138,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (139,2)-(139,30))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (139,2)-(139,30))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ HashNode (location: (139,2)-(139,4))
+ │ │ │ ├── opening_loc: (139,2)-(139,3) = "{"
+ │ │ │ ├── elements: (length: 0)
+ │ │ │ └── closing_loc: (139,3)-(139,4) = "}"
+ │ │ ├── call_operator_loc: (139,4)-(139,5) = "."
+ │ │ ├── name: :merge
+ │ │ ├── message_loc: (139,5)-(139,10) = "merge"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (139,11)-(139,30))
+ │ │ │ ├── flags: contains_keyword_splat
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ KeywordHashNode (location: (139,11)-(139,30))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── elements: (length: 3)
+ │ │ │ ├── @ AssocSplatNode (location: (139,11)-(139,16))
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ CallNode (location: (139,13)-(139,16))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :bar
+ │ │ │ │ │ ├── message_loc: (139,13)-(139,16) = "bar"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ └── operator_loc: (139,11)-(139,13) = "**"
+ │ │ │ ├── @ AssocSplatNode (location: (139,18)-(139,23))
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ CallNode (location: (139,20)-(139,23))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :baz
+ │ │ │ │ │ ├── message_loc: (139,20)-(139,23) = "baz"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ └── operator_loc: (139,18)-(139,20) = "**"
+ │ │ │ └── @ AssocSplatNode (location: (139,25)-(139,30))
+ │ │ │ ├── value:
+ │ │ │ │ @ CallNode (location: (139,27)-(139,30))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :qux
+ │ │ │ │ ├── message_loc: (139,27)-(139,30) = "qux"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── operator_loc: (139,25)-(139,27) = "**"
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (138,0)-(138,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (140,0)-(140,3) = "end"
+ ├── @ DefNode (location: (142,0)-(143,3))
+ │ ├── name: :bar
+ │ ├── name_loc: (142,4)-(142,7) = "bar"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (142,8)-(142,19))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ OptionalKeywordParameterNode (location: (142,8)-(142,19))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── name_loc: (142,8)-(142,10) = "a:"
+ │ │ │ └── value:
+ │ │ │ @ ParenthesesNode (location: (142,11)-(142,19))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (142,12)-(142,18))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ RangeNode (location: (142,12)-(142,18))
+ │ │ │ │ ├── flags: exclude_end
+ │ │ │ │ ├── left:
+ │ │ │ │ │ @ IntegerNode (location: (142,12)-(142,13))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ ├── right:
+ │ │ │ │ │ @ IntegerNode (location: (142,16)-(142,18))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 10
+ │ │ │ │ └── operator_loc: (142,13)-(142,16) = "..."
+ │ │ │ ├── opening_loc: (142,11)-(142,12) = "("
+ │ │ │ └── closing_loc: (142,18)-(142,19) = ")"
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:a]
+ │ ├── def_keyword_loc: (142,0)-(142,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (142,7)-(142,8) = "("
+ │ ├── rparen_loc: (142,19)-(142,20) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (143,0)-(143,3) = "end"
+ ├── @ DefNode (location: (145,0)-(146,3))
+ │ ├── name: :bar
+ │ ├── name_loc: (145,4)-(145,7) = "bar"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (145,8)-(145,18))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ OptionalKeywordParameterNode (location: (145,8)-(145,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── name_loc: (145,8)-(145,10) = "a:"
+ │ │ │ └── value:
+ │ │ │ @ ParenthesesNode (location: (145,11)-(145,18))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (145,12)-(145,17))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ RangeNode (location: (145,12)-(145,17))
+ │ │ │ │ ├── flags: exclude_end
+ │ │ │ │ ├── left: ∅
+ │ │ │ │ ├── right:
+ │ │ │ │ │ @ IntegerNode (location: (145,15)-(145,17))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 10
+ │ │ │ │ └── operator_loc: (145,12)-(145,15) = "..."
+ │ │ │ ├── opening_loc: (145,11)-(145,12) = "("
+ │ │ │ └── closing_loc: (145,17)-(145,18) = ")"
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:a]
+ │ ├── def_keyword_loc: (145,0)-(145,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (145,7)-(145,8) = "("
+ │ ├── rparen_loc: (145,18)-(145,19) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (146,0)-(146,3) = "end"
+ ├── @ DefNode (location: (148,0)-(149,3))
+ │ ├── name: :bar
+ │ ├── name_loc: (148,4)-(148,7) = "bar"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (148,8)-(148,17))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ OptionalKeywordParameterNode (location: (148,8)-(148,17))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── name_loc: (148,8)-(148,10) = "a:"
+ │ │ │ └── value:
+ │ │ │ @ ParenthesesNode (location: (148,11)-(148,17))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (148,12)-(148,16))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ RangeNode (location: (148,12)-(148,16))
+ │ │ │ │ ├── flags: exclude_end
+ │ │ │ │ ├── left:
+ │ │ │ │ │ @ IntegerNode (location: (148,12)-(148,13))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ ├── right: ∅
+ │ │ │ │ └── operator_loc: (148,13)-(148,16) = "..."
+ │ │ │ ├── opening_loc: (148,11)-(148,12) = "("
+ │ │ │ └── closing_loc: (148,16)-(148,17) = ")"
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:a]
+ │ ├── def_keyword_loc: (148,0)-(148,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (148,7)-(148,8) = "("
+ │ ├── rparen_loc: (148,17)-(148,18) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (149,0)-(149,3) = "end"
+ ├── @ DefNode (location: (151,0)-(152,3))
+ │ ├── name: :bar
+ │ ├── name_loc: (151,4)-(151,7) = "bar"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (151,8)-(151,20))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (151,8)-(151,20))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── name_loc: (151,8)-(151,9) = "a"
+ │ │ │ ├── operator_loc: (151,10)-(151,11) = "="
+ │ │ │ └── value:
+ │ │ │ @ ParenthesesNode (location: (151,12)-(151,20))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (151,13)-(151,19))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ RangeNode (location: (151,13)-(151,19))
+ │ │ │ │ ├── flags: exclude_end
+ │ │ │ │ ├── left:
+ │ │ │ │ │ @ IntegerNode (location: (151,13)-(151,14))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ ├── right:
+ │ │ │ │ │ @ IntegerNode (location: (151,17)-(151,19))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 10
+ │ │ │ │ └── operator_loc: (151,14)-(151,17) = "..."
+ │ │ │ ├── opening_loc: (151,12)-(151,13) = "("
+ │ │ │ └── closing_loc: (151,19)-(151,20) = ")"
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:a]
+ │ ├── def_keyword_loc: (151,0)-(151,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (151,7)-(151,8) = "("
+ │ ├── rparen_loc: (151,20)-(151,21) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (152,0)-(152,3) = "end"
+ ├── @ DefNode (location: (154,0)-(155,3))
+ │ ├── name: :bar
+ │ ├── name_loc: (154,4)-(154,7) = "bar"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (154,8)-(154,19))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (154,8)-(154,19))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── name_loc: (154,8)-(154,9) = "a"
+ │ │ │ ├── operator_loc: (154,10)-(154,11) = "="
+ │ │ │ └── value:
+ │ │ │ @ ParenthesesNode (location: (154,12)-(154,19))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (154,13)-(154,18))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ RangeNode (location: (154,13)-(154,18))
+ │ │ │ │ ├── flags: exclude_end
+ │ │ │ │ ├── left: ∅
+ │ │ │ │ ├── right:
+ │ │ │ │ │ @ IntegerNode (location: (154,16)-(154,18))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 10
+ │ │ │ │ └── operator_loc: (154,13)-(154,16) = "..."
+ │ │ │ ├── opening_loc: (154,12)-(154,13) = "("
+ │ │ │ └── closing_loc: (154,18)-(154,19) = ")"
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:a]
+ │ ├── def_keyword_loc: (154,0)-(154,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (154,7)-(154,8) = "("
+ │ ├── rparen_loc: (154,19)-(154,20) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (155,0)-(155,3) = "end"
+ ├── @ DefNode (location: (157,0)-(158,3))
+ │ ├── name: :bar
+ │ ├── name_loc: (157,4)-(157,7) = "bar"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (157,8)-(157,18))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (157,8)-(157,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── name_loc: (157,8)-(157,9) = "a"
+ │ │ │ ├── operator_loc: (157,10)-(157,11) = "="
+ │ │ │ └── value:
+ │ │ │ @ ParenthesesNode (location: (157,12)-(157,18))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (157,13)-(157,17))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ RangeNode (location: (157,13)-(157,17))
+ │ │ │ │ ├── flags: exclude_end
+ │ │ │ │ ├── left:
+ │ │ │ │ │ @ IntegerNode (location: (157,13)-(157,14))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ ├── right: ∅
+ │ │ │ │ └── operator_loc: (157,14)-(157,17) = "..."
+ │ │ │ ├── opening_loc: (157,12)-(157,13) = "("
+ │ │ │ └── closing_loc: (157,17)-(157,18) = ")"
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:a]
+ │ ├── def_keyword_loc: (157,0)-(157,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (157,7)-(157,8) = "("
+ │ ├── rparen_loc: (157,18)-(157,19) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (158,0)-(158,3) = "end"
+ ├── @ DefNode (location: (160,0)-(162,3))
+ │ ├── name: :method
+ │ ├── name_loc: (160,4)-(160,10) = "method"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (160,11)-(160,12))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (160,11)-(160,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (161,2)-(161,14))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (161,2)-(161,14))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (161,2)-(161,6))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :item
+ │ │ │ ├── message_loc: (161,2)-(161,6) = "item"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :>>
+ │ │ ├── message_loc: (161,7)-(161,9) = ">>"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (161,10)-(161,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ CallNode (location: (161,10)-(161,14))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (161,10)-(161,11) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (161,12)-(161,14))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body: ∅
+ │ │ │ ├── opening_loc: (161,12)-(161,13) = "{"
+ │ │ │ └── closing_loc: (161,13)-(161,14) = "}"
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: [:a]
+ │ ├── def_keyword_loc: (160,0)-(160,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (160,10)-(160,11) = "("
+ │ ├── rparen_loc: (160,12)-(160,13) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (162,0)-(162,3) = "end"
+ ├── @ LocalVariableWriteNode (location: (164,0)-(164,7))
+ │ ├── name: :foo
+ │ ├── depth: 0
+ │ ├── name_loc: (164,0)-(164,3) = "foo"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (164,6)-(164,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (164,4)-(164,5) = "="
+ ├── @ DefNode (location: (165,0)-(165,16))
+ │ ├── name: :bar
+ │ ├── name_loc: (165,8)-(165,11) = "bar"
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (165,4)-(165,7))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (165,0)-(165,3) = "def"
+ │ ├── operator_loc: (165,7)-(165,8) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (165,13)-(165,16) = "end"
+ ├── @ DefNode (location: (167,0)-(167,18))
+ │ ├── name: :f
+ │ ├── name_loc: (167,4)-(167,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (167,6)-(167,7))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (167,6)-(167,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: ∅
+ │ │ │ ├── name_loc: ∅
+ │ │ │ └── operator_loc: (167,6)-(167,7) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (167,10)-(167,13))
+ │ │ └── body: (length: 1)
+ │ │ └── @ ArrayNode (location: (167,10)-(167,13))
+ │ │ ├── flags: contains_splat
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ SplatNode (location: (167,11)-(167,12))
+ │ │ │ ├── operator_loc: (167,11)-(167,12) = "*"
+ │ │ │ └── expression: ∅
+ │ │ ├── opening_loc: (167,10)-(167,11) = "["
+ │ │ └── closing_loc: (167,12)-(167,13) = "]"
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (167,0)-(167,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (167,5)-(167,6) = "("
+ │ ├── rparen_loc: (167,7)-(167,8) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (167,15)-(167,18) = "end"
+ ├── @ DefNode (location: (169,0)-(169,15))
+ │ ├── name: :f
+ │ ├── name_loc: (169,4)-(169,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (169,6)-(169,10))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ OptionalKeywordParameterNode (location: (169,6)-(169,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :x
+ │ │ │ ├── name_loc: (169,6)-(169,8) = "x:"
+ │ │ │ └── value:
+ │ │ │ @ CallNode (location: (169,8)-(169,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ CallNode (location: (169,9)-(169,10))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── message_loc: (169,9)-(169,10) = "a"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :-@
+ │ │ │ ├── message_loc: (169,8)-(169,9) = "-"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:x]
+ │ ├── def_keyword_loc: (169,0)-(169,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (169,12)-(169,15) = "end"
+ ├── @ DefNode (location: (171,0)-(171,15))
+ │ ├── name: :f
+ │ ├── name_loc: (171,4)-(171,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (171,6)-(171,10))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ OptionalKeywordParameterNode (location: (171,6)-(171,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :x
+ │ │ │ ├── name_loc: (171,6)-(171,8) = "x:"
+ │ │ │ └── value:
+ │ │ │ @ CallNode (location: (171,8)-(171,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ CallNode (location: (171,9)-(171,10))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── message_loc: (171,9)-(171,10) = "a"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :+@
+ │ │ │ ├── message_loc: (171,8)-(171,9) = "+"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:x]
+ │ ├── def_keyword_loc: (171,0)-(171,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (171,12)-(171,15) = "end"
+ ├── @ DefNode (location: (173,0)-(173,15))
+ │ ├── name: :f
+ │ ├── name_loc: (173,4)-(173,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (173,6)-(173,10))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ OptionalKeywordParameterNode (location: (173,6)-(173,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :x
+ │ │ │ ├── name_loc: (173,6)-(173,8) = "x:"
+ │ │ │ └── value:
+ │ │ │ @ CallNode (location: (173,8)-(173,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ CallNode (location: (173,9)-(173,10))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── message_loc: (173,9)-(173,10) = "a"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :!
+ │ │ │ ├── message_loc: (173,8)-(173,9) = "!"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:x]
+ │ ├── def_keyword_loc: (173,0)-(173,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (173,12)-(173,15) = "end"
+ ├── @ DefNode (location: (175,0)-(175,20))
+ │ ├── name: :foo
+ │ ├── name_loc: (175,4)-(175,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (175,8)-(175,15))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ OptionalKeywordParameterNode (location: (175,8)-(175,15))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :x
+ │ │ │ ├── name_loc: (175,8)-(175,10) = "x:"
+ │ │ │ └── value:
+ │ │ │ @ StringNode (location: (175,10)-(175,15))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (175,10)-(175,12) = "%("
+ │ │ │ ├── content_loc: (175,12)-(175,14) = "xx"
+ │ │ │ ├── closing_loc: (175,14)-(175,15) = ")"
+ │ │ │ └── unescaped: "xx"
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:x]
+ │ ├── def_keyword_loc: (175,0)-(175,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (175,17)-(175,20) = "end"
+ ├── @ DefNode (location: (177,0)-(179,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (177,4)-(177,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (177,8)-(177,11))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ ForwardingParameterNode (location: (177,8)-(177,11))
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (178,2)-(178,10))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (178,2)-(178,10))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (178,2)-(178,5) = "bar"
+ │ │ ├── opening_loc: (178,5)-(178,6) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (178,6)-(178,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ ForwardingArgumentsNode (location: (178,6)-(178,9))
+ │ │ ├── closing_loc: (178,9)-(178,10) = ")"
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (177,0)-(177,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (177,7)-(177,8) = "("
+ │ ├── rparen_loc: (177,11)-(177,12) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (179,0)-(179,3) = "end"
+ ├── @ DefNode (location: (181,0)-(181,42))
+ │ ├── name: :foo
+ │ ├── name_loc: (181,4)-(181,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (181,8)-(181,37))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (181,8)-(181,37))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── name_loc: (181,8)-(181,11) = "bar"
+ │ │ │ ├── operator_loc: (181,12)-(181,13) = "="
+ │ │ │ └── value:
+ │ │ │ @ ParenthesesNode (location: (181,14)-(181,37))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (181,15)-(181,36))
+ │ │ │ │ └── body: (length: 2)
+ │ │ │ │ ├── @ DefNode (location: (181,15)-(181,33))
+ │ │ │ │ │ ├── name: :baz
+ │ │ │ │ │ ├── name_loc: (181,19)-(181,22) = "baz"
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── parameters:
+ │ │ │ │ │ │ @ ParametersNode (location: (181,23)-(181,26))
+ │ │ │ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ │ │ │ └── @ RequiredParameterNode (location: (181,23)-(181,26))
+ │ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ │ └── name: :bar
+ │ │ │ │ │ │ ├── optionals: (length: 0)
+ │ │ │ │ │ │ ├── rest: ∅
+ │ │ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ │ │ ├── keywords: (length: 0)
+ │ │ │ │ │ │ ├── keyword_rest: ∅
+ │ │ │ │ │ │ └── block: ∅
+ │ │ │ │ │ ├── body:
+ │ │ │ │ │ │ @ StatementsNode (location: (181,30)-(181,33))
+ │ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ │ └── @ LocalVariableReadNode (location: (181,30)-(181,33))
+ │ │ │ │ │ │ ├── name: :bar
+ │ │ │ │ │ │ └── depth: 0
+ │ │ │ │ │ ├── locals: [:bar]
+ │ │ │ │ │ ├── def_keyword_loc: (181,15)-(181,18) = "def"
+ │ │ │ │ │ ├── operator_loc: ∅
+ │ │ │ │ │ ├── lparen_loc: (181,22)-(181,23) = "("
+ │ │ │ │ │ ├── rparen_loc: (181,26)-(181,27) = ")"
+ │ │ │ │ │ ├── equal_loc: (181,28)-(181,29) = "="
+ │ │ │ │ │ └── end_keyword_loc: ∅
+ │ │ │ │ └── @ IntegerNode (location: (181,35)-(181,36))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── opening_loc: (181,14)-(181,15) = "("
+ │ │ │ └── closing_loc: (181,36)-(181,37) = ")"
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (181,41)-(181,42))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (181,41)-(181,42))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── locals: [:bar]
+ │ ├── def_keyword_loc: (181,0)-(181,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (181,7)-(181,8) = "("
+ │ ├── rparen_loc: (181,37)-(181,38) = ")"
+ │ ├── equal_loc: (181,39)-(181,40) = "="
+ │ └── end_keyword_loc: ∅
+ └── @ DefNode (location: (183,0)-(183,37))
+ ├── name: :foo
+ ├── name_loc: (183,21)-(183,24) = "foo"
+ ├── receiver:
+ │ @ ParenthesesNode (location: (183,4)-(183,20))
+ │ ├── body:
+ │ │ @ ClassNode (location: (183,5)-(183,19))
+ │ │ ├── locals: []
+ │ │ ├── class_keyword_loc: (183,5)-(183,10) = "class"
+ │ │ ├── constant_path:
+ │ │ │ @ ConstantReadNode (location: (183,11)-(183,14))
+ │ │ │ └── name: :Foo
+ │ │ ├── inheritance_operator_loc: ∅
+ │ │ ├── superclass: ∅
+ │ │ ├── body: ∅
+ │ │ ├── end_keyword_loc: (183,16)-(183,19) = "end"
+ │ │ └── name: :Foo
+ │ ├── opening_loc: (183,4)-(183,5) = "("
+ │ └── closing_loc: (183,19)-(183,20) = ")"
+ ├── parameters:
+ │ @ ParametersNode (location: (183,25)-(183,32))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 1)
+ │ │ └── @ OptionalParameterNode (location: (183,25)-(183,32))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :bar
+ │ │ ├── name_loc: (183,25)-(183,28) = "bar"
+ │ │ ├── operator_loc: (183,29)-(183,30) = "="
+ │ │ └── value:
+ │ │ @ IntegerNode (location: (183,31)-(183,32))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (183,36)-(183,37))
+ │ └── body: (length: 1)
+ │ └── @ IntegerNode (location: (183,36)-(183,37))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── locals: [:bar]
+ ├── def_keyword_loc: (183,0)-(183,3) = "def"
+ ├── operator_loc: (183,20)-(183,21) = "."
+ ├── lparen_loc: (183,24)-(183,25) = "("
+ ├── rparen_loc: (183,32)-(183,33) = ")"
+ ├── equal_loc: (183,34)-(183,35) = "="
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/modules.txt b/test/prism/snapshots/modules.txt
new file mode 100644
index 0000000000..1a0eb6328a
--- /dev/null
+++ b/test/prism/snapshots/modules.txt
@@ -0,0 +1,184 @@
+@ ProgramNode (location: (1,0)-(18,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(18,3))
+ └── body: (length: 7)
+ ├── @ ModuleNode (location: (1,0)-(1,18))
+ │ ├── locals: [:a]
+ │ ├── module_keyword_loc: (1,0)-(1,6) = "module"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (1,7)-(1,8))
+ │ │ └── name: :A
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,9)-(1,14))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableWriteNode (location: (1,9)-(1,14))
+ │ │ ├── name: :a
+ │ │ ├── depth: 0
+ │ │ ├── name_loc: (1,9)-(1,10) = "a"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (1,13)-(1,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (1,11)-(1,12) = "="
+ │ ├── end_keyword_loc: (1,15)-(1,18) = "end"
+ │ └── name: :A
+ ├── @ InterpolatedStringNode (location: (3,0)-(3,18))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (3,0)-(3,3) = "%Q{"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (3,3)-(3,7))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (3,3)-(3,7) = "aaa "
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "aaa "
+ │ │ ├── @ EmbeddedStatementsNode (location: (3,7)-(3,13))
+ │ │ │ ├── opening_loc: (3,7)-(3,9) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (3,9)-(3,12))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (3,9)-(3,12))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bbb
+ │ │ │ │ ├── message_loc: (3,9)-(3,12) = "bbb"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── closing_loc: (3,12)-(3,13) = "}"
+ │ │ └── @ StringNode (location: (3,13)-(3,17))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (3,13)-(3,17) = " ccc"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: " ccc"
+ │ └── closing_loc: (3,17)-(3,18) = "}"
+ ├── @ ModuleNode (location: (5,0)-(6,3))
+ │ ├── locals: []
+ │ ├── module_keyword_loc: (5,0)-(5,6) = "module"
+ │ ├── constant_path:
+ │ │ @ ConstantPathNode (location: (5,7)-(5,11))
+ │ │ ├── parent:
+ │ │ │ @ CallNode (location: (5,7)-(5,8))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :m
+ │ │ │ ├── message_loc: (5,7)-(5,8) = "m"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (5,10)-(5,11))
+ │ │ │ └── name: :M
+ │ │ └── delimiter_loc: (5,8)-(5,10) = "::"
+ │ ├── body: ∅
+ │ ├── end_keyword_loc: (6,0)-(6,3) = "end"
+ │ └── name: :M
+ ├── @ ModuleNode (location: (8,0)-(9,19))
+ │ ├── locals: [:x]
+ │ ├── module_keyword_loc: (8,0)-(8,6) = "module"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (8,7)-(8,8))
+ │ │ └── name: :A
+ │ ├── body:
+ │ │ @ BeginNode (location: (8,0)-(9,19))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (9,1)-(9,6))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ LocalVariableWriteNode (location: (9,1)-(9,6))
+ │ │ │ ├── name: :x
+ │ │ │ ├── depth: 0
+ │ │ │ ├── name_loc: (9,1)-(9,2) = "x"
+ │ │ │ ├── value:
+ │ │ │ │ @ IntegerNode (location: (9,5)-(9,6))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── operator_loc: (9,3)-(9,4) = "="
+ │ │ ├── rescue_clause:
+ │ │ │ @ RescueNode (location: (9,8)-(9,14))
+ │ │ │ ├── keyword_loc: (9,8)-(9,14) = "rescue"
+ │ │ │ ├── exceptions: (length: 0)
+ │ │ │ ├── operator_loc: ∅
+ │ │ │ ├── reference: ∅
+ │ │ │ ├── statements: ∅
+ │ │ │ └── consequent: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause: ∅
+ │ │ └── end_keyword_loc: (9,16)-(9,19) = "end"
+ │ ├── end_keyword_loc: (9,16)-(9,19) = "end"
+ │ └── name: :A
+ ├── @ ModuleNode (location: (11,0)-(12,3))
+ │ ├── locals: []
+ │ ├── module_keyword_loc: (11,0)-(11,6) = "module"
+ │ ├── constant_path:
+ │ │ @ ConstantPathNode (location: (11,7)-(11,10))
+ │ │ ├── parent: ∅
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (11,9)-(11,10))
+ │ │ │ └── name: :A
+ │ │ └── delimiter_loc: (11,7)-(11,9) = "::"
+ │ ├── body: ∅
+ │ ├── end_keyword_loc: (12,0)-(12,3) = "end"
+ │ └── name: :A
+ ├── @ ModuleNode (location: (14,0)-(15,3))
+ │ ├── locals: []
+ │ ├── module_keyword_loc: (14,0)-(14,6) = "module"
+ │ ├── constant_path:
+ │ │ @ ConstantPathNode (location: (14,7)-(14,13))
+ │ │ ├── parent:
+ │ │ │ @ CallNode (location: (14,7)-(14,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ ConstantReadNode (location: (14,7)-(14,8))
+ │ │ │ │ └── name: :A
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :[]
+ │ │ │ ├── message_loc: (14,8)-(14,10) = "[]"
+ │ │ │ ├── opening_loc: (14,8)-(14,9) = "["
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: (14,9)-(14,10) = "]"
+ │ │ │ └── block: ∅
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (14,12)-(14,13))
+ │ │ │ └── name: :B
+ │ │ └── delimiter_loc: (14,10)-(14,12) = "::"
+ │ ├── body: ∅
+ │ ├── end_keyword_loc: (15,0)-(15,3) = "end"
+ │ └── name: :B
+ └── @ ModuleNode (location: (17,0)-(18,3))
+ ├── locals: []
+ ├── module_keyword_loc: (17,0)-(17,6) = "module"
+ ├── constant_path:
+ │ @ ConstantPathNode (location: (17,7)-(17,14))
+ │ ├── parent:
+ │ │ @ CallNode (location: (17,7)-(17,11))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ ConstantReadNode (location: (17,7)-(17,8))
+ │ │ │ └── name: :A
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :[]
+ │ │ ├── message_loc: (17,8)-(17,11) = "[1]"
+ │ │ ├── opening_loc: (17,8)-(17,9) = "["
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (17,9)-(17,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (17,9)-(17,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: (17,10)-(17,11) = "]"
+ │ │ └── block: ∅
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (17,13)-(17,14))
+ │ │ └── name: :B
+ │ └── delimiter_loc: (17,11)-(17,13) = "::"
+ ├── body: ∅
+ ├── end_keyword_loc: (18,0)-(18,3) = "end"
+ └── name: :B
diff --git a/test/prism/snapshots/multi_write.txt b/test/prism/snapshots/multi_write.txt
new file mode 100644
index 0000000000..d313801fdb
--- /dev/null
+++ b/test/prism/snapshots/multi_write.txt
@@ -0,0 +1,93 @@
+@ ProgramNode (location: (1,0)-(4,26))
+├── locals: [:foo, :bar]
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,26))
+ └── body: (length: 4)
+ ├── @ LocalVariableWriteNode (location: (1,0)-(1,18))
+ │ ├── name: :foo
+ │ ├── depth: 0
+ │ ├── name_loc: (1,0)-(1,3) = "foo"
+ │ ├── value:
+ │ │ @ RescueModifierNode (location: (1,6)-(1,18))
+ │ │ ├── expression:
+ │ │ │ @ IntegerNode (location: (1,6)-(1,7))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── keyword_loc: (1,8)-(1,14) = "rescue"
+ │ │ └── rescue_expression:
+ │ │ @ NilNode (location: (1,15)-(1,18))
+ │ └── operator_loc: (1,4)-(1,5) = "="
+ ├── @ MultiWriteNode (location: (2,0)-(2,23))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ LocalVariableTargetNode (location: (2,0)-(2,3))
+ │ │ │ ├── name: :foo
+ │ │ │ └── depth: 0
+ │ │ └── @ LocalVariableTargetNode (location: (2,5)-(2,8))
+ │ │ ├── name: :bar
+ │ │ └── depth: 0
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (2,9)-(2,10) = "="
+ │ └── value:
+ │ @ RescueModifierNode (location: (2,11)-(2,23))
+ │ ├── expression:
+ │ │ @ IntegerNode (location: (2,11)-(2,12))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── keyword_loc: (2,13)-(2,19) = "rescue"
+ │ └── rescue_expression:
+ │ @ NilNode (location: (2,20)-(2,23))
+ ├── @ RescueModifierNode (location: (3,0)-(3,21))
+ │ ├── expression:
+ │ │ @ LocalVariableWriteNode (location: (3,0)-(3,10))
+ │ │ ├── name: :foo
+ │ │ ├── depth: 0
+ │ │ ├── name_loc: (3,0)-(3,3) = "foo"
+ │ │ ├── value:
+ │ │ │ @ ArrayNode (location: (3,6)-(3,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 2)
+ │ │ │ │ ├── @ IntegerNode (location: (3,6)-(3,7))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ └── @ IntegerNode (location: (3,9)-(3,10))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ └── closing_loc: ∅
+ │ │ └── operator_loc: (3,4)-(3,5) = "="
+ │ ├── keyword_loc: (3,11)-(3,17) = "rescue"
+ │ └── rescue_expression:
+ │ @ NilNode (location: (3,18)-(3,21))
+ └── @ MultiWriteNode (location: (4,0)-(4,26))
+ ├── lefts: (length: 2)
+ │ ├── @ LocalVariableTargetNode (location: (4,0)-(4,3))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ └── @ LocalVariableTargetNode (location: (4,5)-(4,8))
+ │ ├── name: :bar
+ │ └── depth: 0
+ ├── rest: ∅
+ ├── rights: (length: 0)
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (4,9)-(4,10) = "="
+ └── value:
+ @ RescueModifierNode (location: (4,11)-(4,26))
+ ├── expression:
+ │ @ ArrayNode (location: (4,11)-(4,15))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (4,11)-(4,12))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (4,14)-(4,15))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── opening_loc: ∅
+ │ └── closing_loc: ∅
+ ├── keyword_loc: (4,16)-(4,22) = "rescue"
+ └── rescue_expression:
+ @ NilNode (location: (4,23)-(4,26))
diff --git a/test/prism/snapshots/newline_terminated.txt b/test/prism/snapshots/newline_terminated.txt
new file mode 100644
index 0000000000..6a3b28dba9
--- /dev/null
+++ b/test/prism/snapshots/newline_terminated.txt
@@ -0,0 +1,107 @@
+@ ProgramNode (location: (3,0)-(41,0))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (3,0)-(41,0))
+ └── body: (length: 17)
+ ├── @ StringNode (location: (3,0)-(3,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (3,0)-(3,2) = "% "
+ │ ├── content_loc: (3,2)-(3,5) = "abc"
+ │ ├── closing_loc: (3,5)-(3,6) = " "
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (4,0)-(4,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (4,0)-(4,2) = "%\t"
+ │ ├── content_loc: (4,2)-(4,5) = "abc"
+ │ ├── closing_loc: (4,5)-(4,6) = "\t"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (5,0)-(5,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (5,0)-(5,2) = "%\v"
+ │ ├── content_loc: (5,2)-(5,5) = "abc"
+ │ ├── closing_loc: (5,5)-(5,6) = "\v"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (6,0)-(6,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (6,0)-(6,2) = "%\r"
+ │ ├── content_loc: (6,2)-(6,5) = "abc"
+ │ ├── closing_loc: (6,5)-(6,6) = "\r"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (7,0)-(9,0))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (7,0)-(8,0) = "%\n"
+ │ ├── content_loc: (8,0)-(8,3) = "abc"
+ │ ├── closing_loc: (8,3)-(9,0) = "\n"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (10,0)-(10,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (10,0)-(10,2) = "%\u0000"
+ │ ├── content_loc: (10,2)-(10,5) = "abc"
+ │ ├── closing_loc: (10,5)-(10,6) = "\u0000"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (11,0)-(13,0))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (11,0)-(12,0) = "%\n"
+ │ ├── content_loc: (12,0)-(12,3) = "abc"
+ │ ├── closing_loc: (12,3)-(13,0) = "\n"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (14,0)-(16,0))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (14,0)-(15,0) = "%\n"
+ │ ├── content_loc: (15,0)-(15,4) = "\rabc"
+ │ ├── closing_loc: (15,4)-(16,0) = "\n"
+ │ └── unescaped: "\rabc"
+ ├── @ StringNode (location: (17,0)-(19,0))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (17,0)-(18,0) = "%\n"
+ │ ├── content_loc: (18,0)-(18,4) = "\rabc"
+ │ ├── closing_loc: (18,4)-(19,0) = "\n"
+ │ └── unescaped: "\rabc"
+ ├── @ StringNode (location: (20,0)-(22,0))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (20,0)-(21,0) = "%\n"
+ │ ├── content_loc: (21,0)-(21,3) = "abc"
+ │ ├── closing_loc: (21,3)-(22,0) = "\n"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (23,0)-(23,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (23,0)-(23,2) = "%\r"
+ │ ├── content_loc: (23,2)-(23,5) = "abc"
+ │ ├── closing_loc: (23,5)-(23,6) = "\r"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (24,0)-(26,0))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (24,0)-(25,0) = "%\n"
+ │ ├── content_loc: (25,0)-(25,3) = "abc"
+ │ ├── closing_loc: (25,3)-(26,0) = "\n"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (27,0)-(29,0))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (27,0)-(28,0) = "%\n"
+ │ ├── content_loc: (28,0)-(28,3) = "abc"
+ │ ├── closing_loc: (28,3)-(29,0) = "\n"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (30,0)-(32,0))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (30,0)-(31,0) = "%\n"
+ │ ├── content_loc: (31,0)-(31,3) = "foo"
+ │ ├── closing_loc: (31,3)-(32,0) = "\n"
+ │ └── unescaped: "foo"
+ ├── @ StringNode (location: (33,0)-(35,0))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (33,0)-(34,0) = "%q\n"
+ │ ├── content_loc: (34,0)-(34,3) = "foo"
+ │ ├── closing_loc: (34,3)-(35,0) = "\n"
+ │ └── unescaped: "foo"
+ ├── @ StringNode (location: (36,0)-(38,0))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (36,0)-(37,0) = "%Q\n"
+ │ ├── content_loc: (37,0)-(37,3) = "foo"
+ │ ├── closing_loc: (37,3)-(38,0) = "\n"
+ │ └── unescaped: "foo"
+ └── @ RegularExpressionNode (location: (39,0)-(41,0))
+ ├── flags: forced_us_ascii_encoding
+ ├── opening_loc: (39,0)-(40,0) = "%r\n"
+ ├── content_loc: (40,0)-(40,3) = "foo"
+ ├── closing_loc: (40,3)-(41,0) = "\n"
+ └── unescaped: "foo"
diff --git a/test/prism/snapshots/next.txt b/test/prism/snapshots/next.txt
new file mode 100644
index 0000000000..ce2e497de9
--- /dev/null
+++ b/test/prism/snapshots/next.txt
@@ -0,0 +1,329 @@
+@ ProgramNode (location: (1,0)-(24,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(24,15))
+ └── body: (length: 10)
+ ├── @ CallNode (location: (1,0)-(1,12))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (1,0)-(1,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,4)-(1,12))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,6)-(1,10))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NextNode (location: (1,6)-(1,10))
+ │ │ ├── arguments: ∅
+ │ │ └── keyword_loc: (1,6)-(1,10) = "next"
+ │ ├── opening_loc: (1,4)-(1,5) = "{"
+ │ └── closing_loc: (1,11)-(1,12) = "}"
+ ├── @ CallNode (location: (3,0)-(3,26))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (3,0)-(3,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (3,4)-(3,26))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (3,6)-(3,24))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NextNode (location: (3,6)-(3,24))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (3,11)-(3,24))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 3)
+ │ │ │ ├── @ ParenthesesNode (location: (3,11)-(3,14))
+ │ │ │ │ ├── body:
+ │ │ │ │ │ @ StatementsNode (location: (3,12)-(3,13))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ IntegerNode (location: (3,12)-(3,13))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ ├── opening_loc: (3,11)-(3,12) = "("
+ │ │ │ │ └── closing_loc: (3,13)-(3,14) = ")"
+ │ │ │ ├── @ ParenthesesNode (location: (3,16)-(3,19))
+ │ │ │ │ ├── body:
+ │ │ │ │ │ @ StatementsNode (location: (3,17)-(3,18))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ IntegerNode (location: (3,17)-(3,18))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 2
+ │ │ │ │ ├── opening_loc: (3,16)-(3,17) = "("
+ │ │ │ │ └── closing_loc: (3,18)-(3,19) = ")"
+ │ │ │ └── @ ParenthesesNode (location: (3,21)-(3,24))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (3,22)-(3,23))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (3,22)-(3,23))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 3
+ │ │ │ ├── opening_loc: (3,21)-(3,22) = "("
+ │ │ │ └── closing_loc: (3,23)-(3,24) = ")"
+ │ │ └── keyword_loc: (3,6)-(3,10) = "next"
+ │ ├── opening_loc: (3,4)-(3,5) = "{"
+ │ └── closing_loc: (3,25)-(3,26) = "}"
+ ├── @ CallNode (location: (5,0)-(5,14))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (5,0)-(5,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (5,4)-(5,14))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (5,6)-(5,12))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NextNode (location: (5,6)-(5,12))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (5,11)-(5,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (5,11)-(5,12))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── keyword_loc: (5,6)-(5,10) = "next"
+ │ ├── opening_loc: (5,4)-(5,5) = "{"
+ │ └── closing_loc: (5,13)-(5,14) = "}"
+ ├── @ CallNode (location: (7,0)-(8,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (7,0)-(7,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (7,4)-(8,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (7,6)-(8,1))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NextNode (location: (7,6)-(8,1))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (7,11)-(8,1))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 3)
+ │ │ │ ├── @ IntegerNode (location: (7,11)-(7,12))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── @ IntegerNode (location: (7,14)-(7,15))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ └── @ IntegerNode (location: (8,0)-(8,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 3
+ │ │ └── keyword_loc: (7,6)-(7,10) = "next"
+ │ ├── opening_loc: (7,4)-(7,5) = "{"
+ │ └── closing_loc: (8,2)-(8,3) = "}"
+ ├── @ CallNode (location: (10,0)-(10,20))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (10,0)-(10,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (10,4)-(10,20))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (10,6)-(10,18))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NextNode (location: (10,6)-(10,18))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (10,11)-(10,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 3)
+ │ │ │ ├── @ IntegerNode (location: (10,11)-(10,12))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── @ IntegerNode (location: (10,14)-(10,15))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ └── @ IntegerNode (location: (10,17)-(10,18))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 3
+ │ │ └── keyword_loc: (10,6)-(10,10) = "next"
+ │ ├── opening_loc: (10,4)-(10,5) = "{"
+ │ └── closing_loc: (10,19)-(10,20) = "}"
+ ├── @ CallNode (location: (12,0)-(12,22))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (12,0)-(12,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (12,4)-(12,22))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (12,6)-(12,20))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NextNode (location: (12,6)-(12,20))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (12,11)-(12,20))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ ArrayNode (location: (12,11)-(12,20))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 3)
+ │ │ │ │ ├── @ IntegerNode (location: (12,12)-(12,13))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ ├── @ IntegerNode (location: (12,15)-(12,16))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 2
+ │ │ │ │ └── @ IntegerNode (location: (12,18)-(12,19))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 3
+ │ │ │ ├── opening_loc: (12,11)-(12,12) = "["
+ │ │ │ └── closing_loc: (12,19)-(12,20) = "]"
+ │ │ └── keyword_loc: (12,6)-(12,10) = "next"
+ │ ├── opening_loc: (12,4)-(12,5) = "{"
+ │ └── closing_loc: (12,21)-(12,22) = "}"
+ ├── @ CallNode (location: (14,0)-(17,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (14,0)-(14,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (14,4)-(17,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (14,6)-(17,1))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NextNode (location: (14,6)-(17,1))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (14,10)-(17,1))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ ParenthesesNode (location: (14,10)-(17,1))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (15,2)-(16,3))
+ │ │ │ │ └── body: (length: 2)
+ │ │ │ │ ├── @ IntegerNode (location: (15,2)-(15,3))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ └── @ IntegerNode (location: (16,2)-(16,3))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ ├── opening_loc: (14,10)-(14,11) = "("
+ │ │ │ └── closing_loc: (17,0)-(17,1) = ")"
+ │ │ └── keyword_loc: (14,6)-(14,10) = "next"
+ │ ├── opening_loc: (14,4)-(14,5) = "{"
+ │ └── closing_loc: (17,2)-(17,3) = "}"
+ ├── @ CallNode (location: (19,0)-(20,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (19,0)-(19,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (19,4)-(20,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (19,6)-(20,1))
+ │ │ └── body: (length: 2)
+ │ │ ├── @ NextNode (location: (19,6)-(19,10))
+ │ │ │ ├── arguments: ∅
+ │ │ │ └── keyword_loc: (19,6)-(19,10) = "next"
+ │ │ └── @ IntegerNode (location: (20,0)-(20,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── opening_loc: (19,4)-(19,5) = "{"
+ │ └── closing_loc: (20,2)-(20,3) = "}"
+ ├── @ CallNode (location: (22,0)-(22,14))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (22,0)-(22,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (22,4)-(22,14))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (22,6)-(22,12))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NextNode (location: (22,6)-(22,12))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (22,10)-(22,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ ParenthesesNode (location: (22,10)-(22,12))
+ │ │ │ ├── body: ∅
+ │ │ │ ├── opening_loc: (22,10)-(22,11) = "("
+ │ │ │ └── closing_loc: (22,11)-(22,12) = ")"
+ │ │ └── keyword_loc: (22,6)-(22,10) = "next"
+ │ ├── opening_loc: (22,4)-(22,5) = "{"
+ │ └── closing_loc: (22,13)-(22,14) = "}"
+ └── @ CallNode (location: (24,0)-(24,15))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :tap
+ ├── message_loc: (24,0)-(24,3) = "tap"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (24,4)-(24,15))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (24,6)-(24,13))
+ │ └── body: (length: 1)
+ │ └── @ NextNode (location: (24,6)-(24,13))
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (24,10)-(24,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ ParenthesesNode (location: (24,10)-(24,13))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (24,11)-(24,12))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (24,11)-(24,12))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── opening_loc: (24,10)-(24,11) = "("
+ │ │ └── closing_loc: (24,12)-(24,13) = ")"
+ │ └── keyword_loc: (24,6)-(24,10) = "next"
+ ├── opening_loc: (24,4)-(24,5) = "{"
+ └── closing_loc: (24,14)-(24,15) = "}"
diff --git a/test/prism/snapshots/nils.txt b/test/prism/snapshots/nils.txt
new file mode 100644
index 0000000000..f72745560f
--- /dev/null
+++ b/test/prism/snapshots/nils.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(12,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(12,11))
+ └── body: (length: 5)
+ ├── @ NilNode (location: (1,0)-(1,3))
+ ├── @ ParenthesesNode (location: (3,0)-(3,2))
+ │ ├── body: ∅
+ │ ├── opening_loc: (3,0)-(3,1) = "("
+ │ └── closing_loc: (3,1)-(3,2) = ")"
+ ├── @ ParenthesesNode (location: (5,0)-(8,1))
+ │ ├── body: ∅
+ │ ├── opening_loc: (5,0)-(5,1) = "("
+ │ └── closing_loc: (8,0)-(8,1) = ")"
+ ├── @ PostExecutionNode (location: (10,0)-(10,9))
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (10,6)-(10,7))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (10,6)-(10,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── keyword_loc: (10,0)-(10,3) = "END"
+ │ ├── opening_loc: (10,4)-(10,5) = "{"
+ │ └── closing_loc: (10,8)-(10,9) = "}"
+ └── @ PreExecutionNode (location: (12,0)-(12,11))
+ ├── statements:
+ │ @ StatementsNode (location: (12,8)-(12,9))
+ │ └── body: (length: 1)
+ │ └── @ IntegerNode (location: (12,8)-(12,9))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── keyword_loc: (12,0)-(12,5) = "BEGIN"
+ ├── opening_loc: (12,6)-(12,7) = "{"
+ └── closing_loc: (12,10)-(12,11) = "}"
diff --git a/test/prism/snapshots/non_alphanumeric_methods.txt b/test/prism/snapshots/non_alphanumeric_methods.txt
new file mode 100644
index 0000000000..2ed66fe0e2
--- /dev/null
+++ b/test/prism/snapshots/non_alphanumeric_methods.txt
@@ -0,0 +1,503 @@
+@ ProgramNode (location: (1,0)-(105,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(105,3))
+ └── body: (length: 36)
+ ├── @ DefNode (location: (1,0)-(2,3))
+ │ ├── name: :!
+ │ ├── name_loc: (1,4)-(1,5) = "!"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (2,0)-(2,3) = "end"
+ ├── @ DefNode (location: (4,0)-(5,3))
+ │ ├── name: :!=
+ │ ├── name_loc: (4,4)-(4,6) = "!="
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (4,0)-(4,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (5,0)-(5,3) = "end"
+ ├── @ DefNode (location: (7,0)-(8,3))
+ │ ├── name: :!~
+ │ ├── name_loc: (7,4)-(7,6) = "!~"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (7,0)-(7,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (8,0)-(8,3) = "end"
+ ├── @ DefNode (location: (10,0)-(11,3))
+ │ ├── name: :%
+ │ ├── name_loc: (10,4)-(10,5) = "%"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (10,0)-(10,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (11,0)-(11,3) = "end"
+ ├── @ DefNode (location: (13,0)-(14,3))
+ │ ├── name: :+
+ │ ├── name_loc: (13,9)-(13,10) = "+"
+ │ ├── receiver:
+ │ │ @ SelfNode (location: (13,4)-(13,8))
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (13,0)-(13,3) = "def"
+ │ ├── operator_loc: (13,8)-(13,9) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (14,0)-(14,3) = "end"
+ ├── @ DefNode (location: (16,0)-(17,3))
+ │ ├── name: :&
+ │ ├── name_loc: (16,4)-(16,5) = "&"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (16,0)-(16,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (17,0)-(17,3) = "end"
+ ├── @ DefNode (location: (19,0)-(20,3))
+ │ ├── name: :*
+ │ ├── name_loc: (19,4)-(19,5) = "*"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (19,0)-(19,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (20,0)-(20,3) = "end"
+ ├── @ DefNode (location: (22,0)-(23,3))
+ │ ├── name: :**
+ │ ├── name_loc: (22,4)-(22,6) = "**"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (22,0)-(22,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (23,0)-(23,3) = "end"
+ ├── @ StringNode (location: (25,0)-(25,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (25,0)-(25,2) = "%|"
+ │ ├── content_loc: (25,2)-(25,5) = "abc"
+ │ ├── closing_loc: (25,5)-(25,6) = "|"
+ │ └── unescaped: "abc"
+ ├── @ DefNode (location: (27,0)-(28,3))
+ │ ├── name: :+
+ │ ├── name_loc: (27,4)-(27,5) = "+"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (27,6)-(27,9))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ KeywordRestParameterNode (location: (27,6)-(27,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (27,8)-(27,9) = "b"
+ │ │ │ └── operator_loc: (27,6)-(27,8) = "**"
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:b]
+ │ ├── def_keyword_loc: (27,0)-(27,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (28,0)-(28,3) = "end"
+ ├── @ DefNode (location: (30,0)-(31,3))
+ │ ├── name: :+
+ │ ├── name_loc: (30,4)-(30,5) = "+"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (30,0)-(30,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (30,5)-(30,6) = "("
+ │ ├── rparen_loc: (30,6)-(30,7) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (31,0)-(31,3) = "end"
+ ├── @ DefNode (location: (33,0)-(34,3))
+ │ ├── name: :+
+ │ ├── name_loc: (33,4)-(33,5) = "+"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (33,6)-(33,7))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (33,6)-(33,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :b
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:b]
+ │ ├── def_keyword_loc: (33,0)-(33,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (34,0)-(34,3) = "end"
+ ├── @ DefNode (location: (36,0)-(37,3))
+ │ ├── name: :+
+ │ ├── name_loc: (36,9)-(36,10) = "+"
+ │ ├── receiver:
+ │ │ @ SelfNode (location: (36,4)-(36,8))
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (36,0)-(36,3) = "def"
+ │ ├── operator_loc: (36,8)-(36,9) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (37,0)-(37,3) = "end"
+ ├── @ DefNode (location: (39,0)-(40,3))
+ │ ├── name: :+
+ │ ├── name_loc: (39,4)-(39,5) = "+"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (39,0)-(39,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (40,0)-(40,3) = "end"
+ ├── @ DefNode (location: (42,0)-(43,3))
+ │ ├── name: :+@
+ │ ├── name_loc: (42,4)-(42,6) = "+@"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (42,0)-(42,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (43,0)-(43,3) = "end"
+ ├── @ DefNode (location: (45,0)-(46,3))
+ │ ├── name: :-
+ │ ├── name_loc: (45,4)-(45,5) = "-"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (45,0)-(45,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (46,0)-(46,3) = "end"
+ ├── @ DefNode (location: (48,0)-(48,11))
+ │ ├── name: :-
+ │ ├── name_loc: (48,6)-(48,7) = "-"
+ │ ├── receiver:
+ │ │ @ CallNode (location: (48,4)-(48,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (48,4)-(48,5) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (48,0)-(48,3) = "def"
+ │ ├── operator_loc: (48,5)-(48,6) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (48,8)-(48,11) = "end"
+ ├── @ DefNode (location: (50,0)-(51,3))
+ │ ├── name: :-@
+ │ ├── name_loc: (50,4)-(50,6) = "-@"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (50,0)-(50,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (51,0)-(51,3) = "end"
+ ├── @ DefNode (location: (53,0)-(54,3))
+ │ ├── name: :/
+ │ ├── name_loc: (53,4)-(53,5) = "/"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (53,0)-(53,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (54,0)-(54,3) = "end"
+ ├── @ DefNode (location: (56,0)-(57,3))
+ │ ├── name: :<
+ │ ├── name_loc: (56,4)-(56,5) = "<"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (56,0)-(56,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (57,0)-(57,3) = "end"
+ ├── @ DefNode (location: (59,0)-(60,3))
+ │ ├── name: :<<
+ │ ├── name_loc: (59,4)-(59,6) = "<<"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (59,0)-(59,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (60,0)-(60,3) = "end"
+ ├── @ DefNode (location: (62,0)-(63,3))
+ │ ├── name: :<=
+ │ ├── name_loc: (62,4)-(62,6) = "<="
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (62,0)-(62,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (63,0)-(63,3) = "end"
+ ├── @ DefNode (location: (65,0)-(66,3))
+ │ ├── name: :<=>
+ │ ├── name_loc: (65,4)-(65,7) = "<=>"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (65,0)-(65,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (66,0)-(66,3) = "end"
+ ├── @ DefNode (location: (68,0)-(69,3))
+ │ ├── name: :==
+ │ ├── name_loc: (68,4)-(68,6) = "=="
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (68,0)-(68,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (69,0)-(69,3) = "end"
+ ├── @ DefNode (location: (71,0)-(72,3))
+ │ ├── name: :===
+ │ ├── name_loc: (71,4)-(71,7) = "==="
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (71,0)-(71,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (72,0)-(72,3) = "end"
+ ├── @ DefNode (location: (74,0)-(75,3))
+ │ ├── name: :=~
+ │ ├── name_loc: (74,4)-(74,6) = "=~"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (74,0)-(74,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (75,0)-(75,3) = "end"
+ ├── @ DefNode (location: (77,0)-(78,3))
+ │ ├── name: :>
+ │ ├── name_loc: (77,4)-(77,5) = ">"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (77,0)-(77,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (78,0)-(78,3) = "end"
+ ├── @ DefNode (location: (80,0)-(81,3))
+ │ ├── name: :>=
+ │ ├── name_loc: (80,4)-(80,6) = ">="
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (80,0)-(80,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (81,0)-(81,3) = "end"
+ ├── @ DefNode (location: (83,0)-(84,3))
+ │ ├── name: :>>
+ │ ├── name_loc: (83,4)-(83,6) = ">>"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (83,0)-(83,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (84,0)-(84,3) = "end"
+ ├── @ DefNode (location: (86,0)-(87,3))
+ │ ├── name: :[]
+ │ ├── name_loc: (86,4)-(86,6) = "[]"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (86,0)-(86,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (87,0)-(87,3) = "end"
+ ├── @ DefNode (location: (89,0)-(90,3))
+ │ ├── name: :[]=
+ │ ├── name_loc: (89,4)-(89,7) = "[]="
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (89,0)-(89,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (90,0)-(90,3) = "end"
+ ├── @ DefNode (location: (92,0)-(93,3))
+ │ ├── name: :^
+ │ ├── name_loc: (92,4)-(92,5) = "^"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (92,0)-(92,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (93,0)-(93,3) = "end"
+ ├── @ DefNode (location: (95,0)-(96,3))
+ │ ├── name: :`
+ │ ├── name_loc: (95,4)-(95,5) = "`"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (95,0)-(95,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (96,0)-(96,3) = "end"
+ ├── @ DefNode (location: (98,0)-(99,3))
+ │ ├── name: :`
+ │ ├── name_loc: (98,9)-(98,10) = "`"
+ │ ├── receiver:
+ │ │ @ SelfNode (location: (98,4)-(98,8))
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (98,0)-(98,3) = "def"
+ │ ├── operator_loc: (98,8)-(98,9) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (99,0)-(99,3) = "end"
+ ├── @ DefNode (location: (101,0)-(102,3))
+ │ ├── name: :|
+ │ ├── name_loc: (101,4)-(101,5) = "|"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (101,0)-(101,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (102,0)-(102,3) = "end"
+ └── @ DefNode (location: (104,0)-(105,3))
+ ├── name: :~
+ ├── name_loc: (104,4)-(104,5) = "~"
+ ├── receiver: ∅
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (104,0)-(104,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (105,0)-(105,3) = "end"
diff --git a/test/prism/snapshots/not.txt b/test/prism/snapshots/not.txt
new file mode 100644
index 0000000000..fda61bb4b5
--- /dev/null
+++ b/test/prism/snapshots/not.txt
@@ -0,0 +1,351 @@
+@ ProgramNode (location: (1,0)-(37,16))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(37,16))
+ └── body: (length: 10)
+ ├── @ AndNode (location: (1,0)-(1,19))
+ │ ├── left:
+ │ │ @ CallNode (location: (1,0)-(1,7))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (1,4)-(1,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (1,4)-(1,7) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :!
+ │ │ ├── message_loc: (1,0)-(1,3) = "not"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right:
+ │ │ @ CallNode (location: (1,12)-(1,19))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (1,16)-(1,19))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (1,16)-(1,19) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :!
+ │ │ ├── message_loc: (1,12)-(1,15) = "not"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (1,8)-(1,11) = "and"
+ ├── @ CallNode (location: (3,0)-(3,16))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ AndNode (location: (3,4)-(3,15))
+ │ │ ├── left:
+ │ │ │ @ CallNode (location: (3,4)-(3,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (3,4)-(3,7) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── right:
+ │ │ │ @ CallNode (location: (3,12)-(3,15))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (3,12)-(3,15) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (3,8)-(3,11) = "and"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!
+ │ ├── message_loc: (3,0)-(3,3) = "not"
+ │ ├── opening_loc: (3,3)-(3,4) = "("
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (3,15)-(3,16) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (5,0)-(5,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (5,4)-(5,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (5,4)-(5,7) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!
+ │ ├── message_loc: (5,0)-(5,3) = "not"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ AndNode (location: (7,0)-(8,5))
+ │ ├── left:
+ │ │ @ CallNode (location: (7,0)-(7,7))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (7,4)-(7,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (7,4)-(7,7) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :!
+ │ │ ├── message_loc: (7,0)-(7,3) = "not"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right:
+ │ │ @ CallNode (location: (7,12)-(8,5))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (8,2)-(8,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (8,2)-(8,5) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :!
+ │ │ ├── message_loc: (7,12)-(7,15) = "not"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (7,8)-(7,11) = "and"
+ ├── @ AndNode (location: (11,0)-(13,5))
+ │ ├── left:
+ │ │ @ CallNode (location: (11,0)-(11,7))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (11,4)-(11,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (11,4)-(11,7) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :!
+ │ │ ├── message_loc: (11,0)-(11,3) = "not"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right:
+ │ │ @ CallNode (location: (12,4)-(13,5))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (13,2)-(13,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (13,2)-(13,5) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :!
+ │ │ ├── message_loc: (12,4)-(12,7) = "not"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (11,8)-(11,11) = "and"
+ ├── @ AndNode (location: (16,0)-(20,5))
+ │ ├── left:
+ │ │ @ CallNode (location: (16,0)-(16,7))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (16,4)-(16,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (16,4)-(16,7) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :!
+ │ │ ├── message_loc: (16,0)-(16,3) = "not"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right:
+ │ │ @ CallNode (location: (17,2)-(20,5))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (20,2)-(20,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (20,2)-(20,5) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :!
+ │ │ ├── message_loc: (17,2)-(17,5) = "not"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (16,8)-(16,11) = "and"
+ ├── @ CallNode (location: (22,0)-(25,1))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (22,4)-(22,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (22,4)-(22,7) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!
+ │ ├── message_loc: (22,0)-(22,3) = "not"
+ │ ├── opening_loc: (22,3)-(22,4) = "("
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (25,0)-(25,1) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (27,0)-(33,3))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (30,0)-(30,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (30,0)-(30,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!
+ │ ├── message_loc: (27,0)-(27,3) = "not"
+ │ ├── opening_loc: (27,3)-(27,4) = "("
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (33,2)-(33,3) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (35,0)-(35,14))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ FlipFlopNode (location: (35,4)-(35,14))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ CallNode (location: (35,4)-(35,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (35,4)-(35,7) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── right:
+ │ │ │ @ CallNode (location: (35,11)-(35,14))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (35,11)-(35,14) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (35,8)-(35,10) = ".."
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!
+ │ ├── message_loc: (35,0)-(35,3) = "not"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (37,0)-(37,16))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ ParenthesesNode (location: (37,4)-(37,16))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (37,5)-(37,15))
+ │ │ └── body: (length: 1)
+ │ │ └── @ FlipFlopNode (location: (37,5)-(37,15))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ CallNode (location: (37,5)-(37,8))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (37,5)-(37,8) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── right:
+ │ │ │ @ CallNode (location: (37,12)-(37,15))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (37,12)-(37,15) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (37,9)-(37,11) = ".."
+ │ ├── opening_loc: (37,4)-(37,5) = "("
+ │ └── closing_loc: (37,15)-(37,16) = ")"
+ ├── call_operator_loc: ∅
+ ├── name: :!
+ ├── message_loc: (37,0)-(37,3) = "not"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/numbers.txt b/test/prism/snapshots/numbers.txt
new file mode 100644
index 0000000000..740f3f5a2a
--- /dev/null
+++ b/test/prism/snapshots/numbers.txt
@@ -0,0 +1,142 @@
+@ ProgramNode (location: (1,0)-(67,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(67,5))
+ └── body: (length: 34)
+ ├── @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 0
+ ├── @ IntegerNode (location: (3,0)-(3,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ FloatNode (location: (5,0)-(5,3))
+ │ └── value: 1.0
+ ├── @ IntegerNode (location: (7,0)-(7,1))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── @ IntegerNode (location: (9,0)-(9,3))
+ │ ├── flags: binary
+ │ └── value: 0
+ ├── @ IntegerNode (location: (11,0)-(11,3))
+ │ ├── flags: binary
+ │ └── value: 1
+ ├── @ IntegerNode (location: (13,0)-(13,4))
+ │ ├── flags: binary
+ │ └── value: 2
+ ├── @ IntegerNode (location: (15,0)-(15,3))
+ │ ├── flags: decimal
+ │ └── value: 0
+ ├── @ IntegerNode (location: (17,0)-(17,3))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ IntegerNode (location: (19,0)-(19,3))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── @ IntegerNode (location: (21,0)-(21,2))
+ │ ├── flags: octal
+ │ └── value: 0
+ ├── @ IntegerNode (location: (23,0)-(23,2))
+ │ ├── flags: octal
+ │ └── value: 1
+ ├── @ IntegerNode (location: (25,0)-(25,2))
+ │ ├── flags: octal
+ │ └── value: 2
+ ├── @ IntegerNode (location: (27,0)-(27,3))
+ │ ├── flags: octal
+ │ └── value: 0
+ ├── @ IntegerNode (location: (29,0)-(29,3))
+ │ ├── flags: octal
+ │ └── value: 1
+ ├── @ IntegerNode (location: (31,0)-(31,3))
+ │ ├── flags: octal
+ │ └── value: 2
+ ├── @ IntegerNode (location: (33,0)-(33,3))
+ │ ├── flags: hexadecimal
+ │ └── value: 0
+ ├── @ IntegerNode (location: (35,0)-(35,3))
+ │ ├── flags: hexadecimal
+ │ └── value: 1
+ ├── @ IntegerNode (location: (37,0)-(37,3))
+ │ ├── flags: hexadecimal
+ │ └── value: 2
+ ├── @ ImaginaryNode (location: (39,0)-(39,2))
+ │ └── numeric:
+ │ @ IntegerNode (location: (39,0)-(39,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ RationalNode (location: (41,0)-(41,2))
+ │ └── numeric:
+ │ @ IntegerNode (location: (41,0)-(41,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ IntegerNode (location: (43,0)-(43,2))
+ │ ├── flags: decimal
+ │ └── value: -1
+ ├── @ ImaginaryNode (location: (45,0)-(45,3))
+ │ └── numeric:
+ │ @ RationalNode (location: (45,0)-(45,2))
+ │ └── numeric:
+ │ @ IntegerNode (location: (45,0)-(45,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ RationalNode (location: (47,0)-(47,4))
+ │ └── numeric:
+ │ @ FloatNode (location: (47,0)-(47,3))
+ │ └── value: 1.2
+ ├── @ ImaginaryNode (location: (49,0)-(49,5))
+ │ └── numeric:
+ │ @ RationalNode (location: (49,0)-(49,4))
+ │ └── numeric:
+ │ @ FloatNode (location: (49,0)-(49,3))
+ │ └── value: 1.2
+ ├── @ ImaginaryNode (location: (51,0)-(51,4))
+ │ └── numeric:
+ │ @ RationalNode (location: (51,0)-(51,3))
+ │ └── numeric:
+ │ @ IntegerNode (location: (51,0)-(51,2))
+ │ ├── flags: decimal
+ │ └── value: -1
+ ├── @ RationalNode (location: (53,0)-(53,5))
+ │ └── numeric:
+ │ @ FloatNode (location: (53,0)-(53,4))
+ │ └── value: -1.2
+ ├── @ ImaginaryNode (location: (55,0)-(55,6))
+ │ └── numeric:
+ │ @ RationalNode (location: (55,0)-(55,5))
+ │ └── numeric:
+ │ @ FloatNode (location: (55,0)-(55,4))
+ │ └── value: -1.2
+ ├── @ RationalNode (location: (57,0)-(57,4))
+ │ └── numeric:
+ │ @ IntegerNode (location: (57,0)-(57,3))
+ │ ├── flags: octal
+ │ └── value: 1
+ ├── @ ImaginaryNode (location: (59,0)-(59,4))
+ │ └── numeric:
+ │ @ IntegerNode (location: (59,0)-(59,3))
+ │ ├── flags: octal
+ │ └── value: 1
+ ├── @ ImaginaryNode (location: (61,0)-(61,5))
+ │ └── numeric:
+ │ @ RationalNode (location: (61,0)-(61,4))
+ │ └── numeric:
+ │ @ IntegerNode (location: (61,0)-(61,3))
+ │ ├── flags: octal
+ │ └── value: 1
+ ├── @ RationalNode (location: (63,0)-(63,4))
+ │ └── numeric:
+ │ @ IntegerNode (location: (63,0)-(63,3))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ ImaginaryNode (location: (65,0)-(65,4))
+ │ └── numeric:
+ │ @ IntegerNode (location: (65,0)-(65,3))
+ │ ├── flags: decimal
+ │ └── value: 1
+ └── @ ImaginaryNode (location: (67,0)-(67,5))
+ └── numeric:
+ @ RationalNode (location: (67,0)-(67,4))
+ └── numeric:
+ @ IntegerNode (location: (67,0)-(67,3))
+ ├── flags: binary
+ └── value: 1
diff --git a/test/prism/snapshots/patterns.txt b/test/prism/snapshots/patterns.txt
new file mode 100644
index 0000000000..5662129dae
--- /dev/null
+++ b/test/prism/snapshots/patterns.txt
@@ -0,0 +1,4921 @@
+@ ProgramNode (location: (1,0)-(217,5))
+├── locals: [:bar, :baz, :qux, :b, :a, :foo, :x]
+└── statements:
+ @ StatementsNode (location: (1,0)-(217,5))
+ └── body: (length: 180)
+ ├── @ MatchRequiredNode (location: (1,0)-(1,10))
+ │ ├── value:
+ │ │ @ CallNode (location: (1,0)-(1,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ LocalVariableTargetNode (location: (1,7)-(1,10))
+ │ │ ├── name: :bar
+ │ │ └── depth: 0
+ │ └── operator_loc: (1,4)-(1,6) = "=>"
+ ├── @ MatchRequiredNode (location: (2,0)-(2,8))
+ │ ├── value:
+ │ │ @ CallNode (location: (2,0)-(2,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (2,0)-(2,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ IntegerNode (location: (2,7)-(2,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (2,4)-(2,6) = "=>"
+ ├── @ MatchRequiredNode (location: (3,0)-(3,10))
+ │ ├── value:
+ │ │ @ CallNode (location: (3,0)-(3,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (3,0)-(3,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ FloatNode (location: (3,7)-(3,10))
+ │ │ └── value: 1.0
+ │ └── operator_loc: (3,4)-(3,6) = "=>"
+ ├── @ MatchRequiredNode (location: (4,0)-(4,9))
+ │ ├── value:
+ │ │ @ CallNode (location: (4,0)-(4,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (4,0)-(4,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ImaginaryNode (location: (4,7)-(4,9))
+ │ │ └── numeric:
+ │ │ @ IntegerNode (location: (4,7)-(4,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (4,4)-(4,6) = "=>"
+ ├── @ MatchRequiredNode (location: (5,0)-(5,9))
+ │ ├── value:
+ │ │ @ CallNode (location: (5,0)-(5,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (5,0)-(5,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RationalNode (location: (5,7)-(5,9))
+ │ │ └── numeric:
+ │ │ @ IntegerNode (location: (5,7)-(5,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (5,4)-(5,6) = "=>"
+ ├── @ MatchRequiredNode (location: (6,0)-(6,11))
+ │ ├── value:
+ │ │ @ CallNode (location: (6,0)-(6,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (6,0)-(6,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ SymbolNode (location: (6,7)-(6,11))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (6,7)-(6,8) = ":"
+ │ │ ├── value_loc: (6,8)-(6,11) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ └── operator_loc: (6,4)-(6,6) = "=>"
+ ├── @ MatchRequiredNode (location: (7,0)-(7,14))
+ │ ├── value:
+ │ │ @ CallNode (location: (7,0)-(7,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (7,0)-(7,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ SymbolNode (location: (7,7)-(7,14))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (7,7)-(7,10) = "%s["
+ │ │ ├── value_loc: (7,10)-(7,13) = "foo"
+ │ │ ├── closing_loc: (7,13)-(7,14) = "]"
+ │ │ └── unescaped: "foo"
+ │ └── operator_loc: (7,4)-(7,6) = "=>"
+ ├── @ MatchRequiredNode (location: (8,0)-(8,13))
+ │ ├── value:
+ │ │ @ CallNode (location: (8,0)-(8,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (8,0)-(8,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ SymbolNode (location: (8,7)-(8,13))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (8,7)-(8,9) = ":\""
+ │ │ ├── value_loc: (8,9)-(8,12) = "foo"
+ │ │ ├── closing_loc: (8,12)-(8,13) = "\""
+ │ │ └── unescaped: "foo"
+ │ └── operator_loc: (8,4)-(8,6) = "=>"
+ ├── @ MatchRequiredNode (location: (9,0)-(9,12))
+ │ ├── value:
+ │ │ @ CallNode (location: (9,0)-(9,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (9,0)-(9,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RegularExpressionNode (location: (9,7)-(9,12))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (9,7)-(9,8) = "/"
+ │ │ ├── content_loc: (9,8)-(9,11) = "foo"
+ │ │ ├── closing_loc: (9,11)-(9,12) = "/"
+ │ │ └── unescaped: "foo"
+ │ └── operator_loc: (9,4)-(9,6) = "=>"
+ ├── @ MatchRequiredNode (location: (10,0)-(10,12))
+ │ ├── value:
+ │ │ @ CallNode (location: (10,0)-(10,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (10,0)-(10,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ XStringNode (location: (10,7)-(10,12))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (10,7)-(10,8) = "`"
+ │ │ ├── content_loc: (10,8)-(10,11) = "foo"
+ │ │ ├── closing_loc: (10,11)-(10,12) = "`"
+ │ │ └── unescaped: "foo"
+ │ └── operator_loc: (10,4)-(10,6) = "=>"
+ ├── @ MatchRequiredNode (location: (11,0)-(11,14))
+ │ ├── value:
+ │ │ @ CallNode (location: (11,0)-(11,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (11,0)-(11,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ XStringNode (location: (11,7)-(11,14))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (11,7)-(11,10) = "%x["
+ │ │ ├── content_loc: (11,10)-(11,13) = "foo"
+ │ │ ├── closing_loc: (11,13)-(11,14) = "]"
+ │ │ └── unescaped: "foo"
+ │ └── operator_loc: (11,4)-(11,6) = "=>"
+ ├── @ MatchRequiredNode (location: (12,0)-(12,14))
+ │ ├── value:
+ │ │ @ CallNode (location: (12,0)-(12,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (12,0)-(12,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayNode (location: (12,7)-(12,14))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (12,10)-(12,13))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (12,10)-(12,13) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── opening_loc: (12,7)-(12,10) = "%i["
+ │ │ └── closing_loc: (12,13)-(12,14) = "]"
+ │ └── operator_loc: (12,4)-(12,6) = "=>"
+ ├── @ MatchRequiredNode (location: (13,0)-(13,14))
+ │ ├── value:
+ │ │ @ CallNode (location: (13,0)-(13,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (13,0)-(13,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayNode (location: (13,7)-(13,14))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (13,10)-(13,13))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (13,10)-(13,13) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── opening_loc: (13,7)-(13,10) = "%I["
+ │ │ └── closing_loc: (13,13)-(13,14) = "]"
+ │ └── operator_loc: (13,4)-(13,6) = "=>"
+ ├── @ MatchRequiredNode (location: (14,0)-(14,14))
+ │ ├── value:
+ │ │ @ CallNode (location: (14,0)-(14,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (14,0)-(14,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayNode (location: (14,7)-(14,14))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ StringNode (location: (14,10)-(14,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (14,10)-(14,13) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── opening_loc: (14,7)-(14,10) = "%w["
+ │ │ └── closing_loc: (14,13)-(14,14) = "]"
+ │ └── operator_loc: (14,4)-(14,6) = "=>"
+ ├── @ MatchRequiredNode (location: (15,0)-(15,14))
+ │ ├── value:
+ │ │ @ CallNode (location: (15,0)-(15,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (15,0)-(15,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayNode (location: (15,7)-(15,14))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ StringNode (location: (15,10)-(15,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (15,10)-(15,13) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── opening_loc: (15,7)-(15,10) = "%W["
+ │ │ └── closing_loc: (15,13)-(15,14) = "]"
+ │ └── operator_loc: (15,4)-(15,6) = "=>"
+ ├── @ MatchRequiredNode (location: (16,0)-(16,14))
+ │ ├── value:
+ │ │ @ CallNode (location: (16,0)-(16,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (16,0)-(16,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ StringNode (location: (16,7)-(16,14))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (16,7)-(16,10) = "%q["
+ │ │ ├── content_loc: (16,10)-(16,13) = "foo"
+ │ │ ├── closing_loc: (16,13)-(16,14) = "]"
+ │ │ └── unescaped: "foo"
+ │ └── operator_loc: (16,4)-(16,6) = "=>"
+ ├── @ MatchRequiredNode (location: (17,0)-(17,14))
+ │ ├── value:
+ │ │ @ CallNode (location: (17,0)-(17,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (17,0)-(17,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ StringNode (location: (17,7)-(17,14))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (17,7)-(17,10) = "%Q["
+ │ │ ├── content_loc: (17,10)-(17,13) = "foo"
+ │ │ ├── closing_loc: (17,13)-(17,14) = "]"
+ │ │ └── unescaped: "foo"
+ │ └── operator_loc: (17,4)-(17,6) = "=>"
+ ├── @ MatchRequiredNode (location: (18,0)-(18,12))
+ │ ├── value:
+ │ │ @ CallNode (location: (18,0)-(18,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (18,0)-(18,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ StringNode (location: (18,7)-(18,12))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (18,7)-(18,8) = "\""
+ │ │ ├── content_loc: (18,8)-(18,11) = "foo"
+ │ │ ├── closing_loc: (18,11)-(18,12) = "\""
+ │ │ └── unescaped: "foo"
+ │ └── operator_loc: (18,4)-(18,6) = "=>"
+ ├── @ MatchRequiredNode (location: (19,0)-(19,10))
+ │ ├── value:
+ │ │ @ CallNode (location: (19,0)-(19,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (19,0)-(19,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ NilNode (location: (19,7)-(19,10))
+ │ └── operator_loc: (19,4)-(19,6) = "=>"
+ ├── @ MatchRequiredNode (location: (20,0)-(20,11))
+ │ ├── value:
+ │ │ @ CallNode (location: (20,0)-(20,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (20,0)-(20,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ SelfNode (location: (20,7)-(20,11))
+ │ └── operator_loc: (20,4)-(20,6) = "=>"
+ ├── @ MatchRequiredNode (location: (21,0)-(21,11))
+ │ ├── value:
+ │ │ @ CallNode (location: (21,0)-(21,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (21,0)-(21,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ TrueNode (location: (21,7)-(21,11))
+ │ └── operator_loc: (21,4)-(21,6) = "=>"
+ ├── @ MatchRequiredNode (location: (22,0)-(22,12))
+ │ ├── value:
+ │ │ @ CallNode (location: (22,0)-(22,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (22,0)-(22,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ FalseNode (location: (22,7)-(22,12))
+ │ └── operator_loc: (22,4)-(22,6) = "=>"
+ ├── @ MatchRequiredNode (location: (23,0)-(23,15))
+ │ ├── value:
+ │ │ @ CallNode (location: (23,0)-(23,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (23,0)-(23,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ SourceFileNode (location: (23,7)-(23,15))
+ │ │ ├── flags: ∅
+ │ │ └── filepath: "patterns.txt"
+ │ └── operator_loc: (23,4)-(23,6) = "=>"
+ ├── @ MatchRequiredNode (location: (24,0)-(24,15))
+ │ ├── value:
+ │ │ @ CallNode (location: (24,0)-(24,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (24,0)-(24,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ SourceLineNode (location: (24,7)-(24,15))
+ │ └── operator_loc: (24,4)-(24,6) = "=>"
+ ├── @ MatchRequiredNode (location: (25,0)-(25,19))
+ │ ├── value:
+ │ │ @ CallNode (location: (25,0)-(25,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (25,0)-(25,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ SourceEncodingNode (location: (25,7)-(25,19))
+ │ └── operator_loc: (25,4)-(25,6) = "=>"
+ ├── @ MatchRequiredNode (location: (26,0)-(26,17))
+ │ ├── value:
+ │ │ @ CallNode (location: (26,0)-(26,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (26,0)-(26,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ LambdaNode (location: (26,7)-(26,17))
+ │ │ ├── locals: []
+ │ │ ├── operator_loc: (26,7)-(26,9) = "->"
+ │ │ ├── opening_loc: (26,10)-(26,11) = "{"
+ │ │ ├── closing_loc: (26,16)-(26,17) = "}"
+ │ │ ├── parameters: ∅
+ │ │ └── body:
+ │ │ @ StatementsNode (location: (26,12)-(26,15))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (26,12)-(26,15))
+ │ │ ├── name: :bar
+ │ │ └── depth: 1
+ │ └── operator_loc: (26,4)-(26,6) = "=>"
+ ├── @ MatchRequiredNode (location: (28,0)-(28,13))
+ │ ├── value:
+ │ │ @ CallNode (location: (28,0)-(28,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (28,0)-(28,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (28,7)-(28,13))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (28,7)-(28,8))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right:
+ │ │ │ @ IntegerNode (location: (28,12)-(28,13))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (28,9)-(28,11) = ".."
+ │ └── operator_loc: (28,4)-(28,6) = "=>"
+ ├── @ MatchRequiredNode (location: (29,0)-(29,17))
+ │ ├── value:
+ │ │ @ CallNode (location: (29,0)-(29,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (29,0)-(29,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (29,7)-(29,17))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ FloatNode (location: (29,7)-(29,10))
+ │ │ │ └── value: 1.0
+ │ │ ├── right:
+ │ │ │ @ FloatNode (location: (29,14)-(29,17))
+ │ │ │ └── value: 1.0
+ │ │ └── operator_loc: (29,11)-(29,13) = ".."
+ │ └── operator_loc: (29,4)-(29,6) = "=>"
+ ├── @ MatchRequiredNode (location: (30,0)-(30,15))
+ │ ├── value:
+ │ │ @ CallNode (location: (30,0)-(30,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (30,0)-(30,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (30,7)-(30,15))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ ImaginaryNode (location: (30,7)-(30,9))
+ │ │ │ └── numeric:
+ │ │ │ @ IntegerNode (location: (30,7)-(30,8))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right:
+ │ │ │ @ ImaginaryNode (location: (30,13)-(30,15))
+ │ │ │ └── numeric:
+ │ │ │ @ IntegerNode (location: (30,13)-(30,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (30,10)-(30,12) = ".."
+ │ └── operator_loc: (30,4)-(30,6) = "=>"
+ ├── @ MatchRequiredNode (location: (31,0)-(31,15))
+ │ ├── value:
+ │ │ @ CallNode (location: (31,0)-(31,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (31,0)-(31,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (31,7)-(31,15))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ RationalNode (location: (31,7)-(31,9))
+ │ │ │ └── numeric:
+ │ │ │ @ IntegerNode (location: (31,7)-(31,8))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right:
+ │ │ │ @ RationalNode (location: (31,13)-(31,15))
+ │ │ │ └── numeric:
+ │ │ │ @ IntegerNode (location: (31,13)-(31,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (31,10)-(31,12) = ".."
+ │ └── operator_loc: (31,4)-(31,6) = "=>"
+ ├── @ MatchRequiredNode (location: (32,0)-(32,19))
+ │ ├── value:
+ │ │ @ CallNode (location: (32,0)-(32,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (32,0)-(32,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (32,7)-(32,19))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ SymbolNode (location: (32,7)-(32,11))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (32,7)-(32,8) = ":"
+ │ │ │ ├── value_loc: (32,8)-(32,11) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── right:
+ │ │ │ @ SymbolNode (location: (32,15)-(32,19))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (32,15)-(32,16) = ":"
+ │ │ │ ├── value_loc: (32,16)-(32,19) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ └── operator_loc: (32,12)-(32,14) = ".."
+ │ └── operator_loc: (32,4)-(32,6) = "=>"
+ ├── @ MatchRequiredNode (location: (33,0)-(33,25))
+ │ ├── value:
+ │ │ @ CallNode (location: (33,0)-(33,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (33,0)-(33,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (33,7)-(33,25))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ SymbolNode (location: (33,7)-(33,14))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (33,7)-(33,10) = "%s["
+ │ │ │ ├── value_loc: (33,10)-(33,13) = "foo"
+ │ │ │ ├── closing_loc: (33,13)-(33,14) = "]"
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── right:
+ │ │ │ @ SymbolNode (location: (33,18)-(33,25))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (33,18)-(33,21) = "%s["
+ │ │ │ ├── value_loc: (33,21)-(33,24) = "foo"
+ │ │ │ ├── closing_loc: (33,24)-(33,25) = "]"
+ │ │ │ └── unescaped: "foo"
+ │ │ └── operator_loc: (33,15)-(33,17) = ".."
+ │ └── operator_loc: (33,4)-(33,6) = "=>"
+ ├── @ MatchRequiredNode (location: (34,0)-(34,23))
+ │ ├── value:
+ │ │ @ CallNode (location: (34,0)-(34,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (34,0)-(34,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (34,7)-(34,23))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ SymbolNode (location: (34,7)-(34,13))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (34,7)-(34,9) = ":\""
+ │ │ │ ├── value_loc: (34,9)-(34,12) = "foo"
+ │ │ │ ├── closing_loc: (34,12)-(34,13) = "\""
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── right:
+ │ │ │ @ SymbolNode (location: (34,17)-(34,23))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (34,17)-(34,19) = ":\""
+ │ │ │ ├── value_loc: (34,19)-(34,22) = "foo"
+ │ │ │ ├── closing_loc: (34,22)-(34,23) = "\""
+ │ │ │ └── unescaped: "foo"
+ │ │ └── operator_loc: (34,14)-(34,16) = ".."
+ │ └── operator_loc: (34,4)-(34,6) = "=>"
+ ├── @ MatchRequiredNode (location: (35,0)-(35,21))
+ │ ├── value:
+ │ │ @ CallNode (location: (35,0)-(35,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (35,0)-(35,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (35,7)-(35,21))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ RegularExpressionNode (location: (35,7)-(35,12))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (35,7)-(35,8) = "/"
+ │ │ │ ├── content_loc: (35,8)-(35,11) = "foo"
+ │ │ │ ├── closing_loc: (35,11)-(35,12) = "/"
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── right:
+ │ │ │ @ RegularExpressionNode (location: (35,16)-(35,21))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (35,16)-(35,17) = "/"
+ │ │ │ ├── content_loc: (35,17)-(35,20) = "foo"
+ │ │ │ ├── closing_loc: (35,20)-(35,21) = "/"
+ │ │ │ └── unescaped: "foo"
+ │ │ └── operator_loc: (35,13)-(35,15) = ".."
+ │ └── operator_loc: (35,4)-(35,6) = "=>"
+ ├── @ MatchRequiredNode (location: (36,0)-(36,21))
+ │ ├── value:
+ │ │ @ CallNode (location: (36,0)-(36,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (36,0)-(36,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (36,7)-(36,21))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ XStringNode (location: (36,7)-(36,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (36,7)-(36,8) = "`"
+ │ │ │ ├── content_loc: (36,8)-(36,11) = "foo"
+ │ │ │ ├── closing_loc: (36,11)-(36,12) = "`"
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── right:
+ │ │ │ @ XStringNode (location: (36,16)-(36,21))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (36,16)-(36,17) = "`"
+ │ │ │ ├── content_loc: (36,17)-(36,20) = "foo"
+ │ │ │ ├── closing_loc: (36,20)-(36,21) = "`"
+ │ │ │ └── unescaped: "foo"
+ │ │ └── operator_loc: (36,13)-(36,15) = ".."
+ │ └── operator_loc: (36,4)-(36,6) = "=>"
+ ├── @ MatchRequiredNode (location: (37,0)-(37,25))
+ │ ├── value:
+ │ │ @ CallNode (location: (37,0)-(37,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (37,0)-(37,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (37,7)-(37,25))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ XStringNode (location: (37,7)-(37,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (37,7)-(37,10) = "%x["
+ │ │ │ ├── content_loc: (37,10)-(37,13) = "foo"
+ │ │ │ ├── closing_loc: (37,13)-(37,14) = "]"
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── right:
+ │ │ │ @ XStringNode (location: (37,18)-(37,25))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (37,18)-(37,21) = "%x["
+ │ │ │ ├── content_loc: (37,21)-(37,24) = "foo"
+ │ │ │ ├── closing_loc: (37,24)-(37,25) = "]"
+ │ │ │ └── unescaped: "foo"
+ │ │ └── operator_loc: (37,15)-(37,17) = ".."
+ │ └── operator_loc: (37,4)-(37,6) = "=>"
+ ├── @ MatchRequiredNode (location: (38,0)-(38,25))
+ │ ├── value:
+ │ │ @ CallNode (location: (38,0)-(38,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (38,0)-(38,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (38,7)-(38,25))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ ArrayNode (location: (38,7)-(38,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ └── @ SymbolNode (location: (38,10)-(38,13))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (38,10)-(38,13) = "foo"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── opening_loc: (38,7)-(38,10) = "%i["
+ │ │ │ └── closing_loc: (38,13)-(38,14) = "]"
+ │ │ ├── right:
+ │ │ │ @ ArrayNode (location: (38,18)-(38,25))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ └── @ SymbolNode (location: (38,21)-(38,24))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (38,21)-(38,24) = "foo"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── opening_loc: (38,18)-(38,21) = "%i["
+ │ │ │ └── closing_loc: (38,24)-(38,25) = "]"
+ │ │ └── operator_loc: (38,15)-(38,17) = ".."
+ │ └── operator_loc: (38,4)-(38,6) = "=>"
+ ├── @ MatchRequiredNode (location: (39,0)-(39,25))
+ │ ├── value:
+ │ │ @ CallNode (location: (39,0)-(39,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (39,0)-(39,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (39,7)-(39,25))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ ArrayNode (location: (39,7)-(39,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ └── @ SymbolNode (location: (39,10)-(39,13))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (39,10)-(39,13) = "foo"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── opening_loc: (39,7)-(39,10) = "%I["
+ │ │ │ └── closing_loc: (39,13)-(39,14) = "]"
+ │ │ ├── right:
+ │ │ │ @ ArrayNode (location: (39,18)-(39,25))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ └── @ SymbolNode (location: (39,21)-(39,24))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (39,21)-(39,24) = "foo"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── opening_loc: (39,18)-(39,21) = "%I["
+ │ │ │ └── closing_loc: (39,24)-(39,25) = "]"
+ │ │ └── operator_loc: (39,15)-(39,17) = ".."
+ │ └── operator_loc: (39,4)-(39,6) = "=>"
+ ├── @ MatchRequiredNode (location: (40,0)-(40,25))
+ │ ├── value:
+ │ │ @ CallNode (location: (40,0)-(40,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (40,0)-(40,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (40,7)-(40,25))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ ArrayNode (location: (40,7)-(40,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ └── @ StringNode (location: (40,10)-(40,13))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (40,10)-(40,13) = "foo"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── opening_loc: (40,7)-(40,10) = "%w["
+ │ │ │ └── closing_loc: (40,13)-(40,14) = "]"
+ │ │ ├── right:
+ │ │ │ @ ArrayNode (location: (40,18)-(40,25))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ └── @ StringNode (location: (40,21)-(40,24))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (40,21)-(40,24) = "foo"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── opening_loc: (40,18)-(40,21) = "%w["
+ │ │ │ └── closing_loc: (40,24)-(40,25) = "]"
+ │ │ └── operator_loc: (40,15)-(40,17) = ".."
+ │ └── operator_loc: (40,4)-(40,6) = "=>"
+ ├── @ MatchRequiredNode (location: (41,0)-(41,25))
+ │ ├── value:
+ │ │ @ CallNode (location: (41,0)-(41,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (41,0)-(41,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (41,7)-(41,25))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ ArrayNode (location: (41,7)-(41,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ └── @ StringNode (location: (41,10)-(41,13))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (41,10)-(41,13) = "foo"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── opening_loc: (41,7)-(41,10) = "%W["
+ │ │ │ └── closing_loc: (41,13)-(41,14) = "]"
+ │ │ ├── right:
+ │ │ │ @ ArrayNode (location: (41,18)-(41,25))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ └── @ StringNode (location: (41,21)-(41,24))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (41,21)-(41,24) = "foo"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── opening_loc: (41,18)-(41,21) = "%W["
+ │ │ │ └── closing_loc: (41,24)-(41,25) = "]"
+ │ │ └── operator_loc: (41,15)-(41,17) = ".."
+ │ └── operator_loc: (41,4)-(41,6) = "=>"
+ ├── @ MatchRequiredNode (location: (42,0)-(42,25))
+ │ ├── value:
+ │ │ @ CallNode (location: (42,0)-(42,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (42,0)-(42,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (42,7)-(42,25))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ StringNode (location: (42,7)-(42,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (42,7)-(42,10) = "%q["
+ │ │ │ ├── content_loc: (42,10)-(42,13) = "foo"
+ │ │ │ ├── closing_loc: (42,13)-(42,14) = "]"
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── right:
+ │ │ │ @ StringNode (location: (42,18)-(42,25))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (42,18)-(42,21) = "%q["
+ │ │ │ ├── content_loc: (42,21)-(42,24) = "foo"
+ │ │ │ ├── closing_loc: (42,24)-(42,25) = "]"
+ │ │ │ └── unescaped: "foo"
+ │ │ └── operator_loc: (42,15)-(42,17) = ".."
+ │ └── operator_loc: (42,4)-(42,6) = "=>"
+ ├── @ MatchRequiredNode (location: (43,0)-(43,25))
+ │ ├── value:
+ │ │ @ CallNode (location: (43,0)-(43,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (43,0)-(43,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (43,7)-(43,25))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ StringNode (location: (43,7)-(43,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (43,7)-(43,10) = "%Q["
+ │ │ │ ├── content_loc: (43,10)-(43,13) = "foo"
+ │ │ │ ├── closing_loc: (43,13)-(43,14) = "]"
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── right:
+ │ │ │ @ StringNode (location: (43,18)-(43,25))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (43,18)-(43,21) = "%Q["
+ │ │ │ ├── content_loc: (43,21)-(43,24) = "foo"
+ │ │ │ ├── closing_loc: (43,24)-(43,25) = "]"
+ │ │ │ └── unescaped: "foo"
+ │ │ └── operator_loc: (43,15)-(43,17) = ".."
+ │ └── operator_loc: (43,4)-(43,6) = "=>"
+ ├── @ MatchRequiredNode (location: (44,0)-(44,21))
+ │ ├── value:
+ │ │ @ CallNode (location: (44,0)-(44,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (44,0)-(44,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (44,7)-(44,21))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ StringNode (location: (44,7)-(44,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (44,7)-(44,8) = "\""
+ │ │ │ ├── content_loc: (44,8)-(44,11) = "foo"
+ │ │ │ ├── closing_loc: (44,11)-(44,12) = "\""
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── right:
+ │ │ │ @ StringNode (location: (44,16)-(44,21))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (44,16)-(44,17) = "\""
+ │ │ │ ├── content_loc: (44,17)-(44,20) = "foo"
+ │ │ │ ├── closing_loc: (44,20)-(44,21) = "\""
+ │ │ │ └── unescaped: "foo"
+ │ │ └── operator_loc: (44,13)-(44,15) = ".."
+ │ └── operator_loc: (44,4)-(44,6) = "=>"
+ ├── @ MatchRequiredNode (location: (45,0)-(45,17))
+ │ ├── value:
+ │ │ @ CallNode (location: (45,0)-(45,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (45,0)-(45,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (45,7)-(45,17))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ NilNode (location: (45,7)-(45,10))
+ │ │ ├── right:
+ │ │ │ @ NilNode (location: (45,14)-(45,17))
+ │ │ └── operator_loc: (45,11)-(45,13) = ".."
+ │ └── operator_loc: (45,4)-(45,6) = "=>"
+ ├── @ MatchRequiredNode (location: (46,0)-(46,19))
+ │ ├── value:
+ │ │ @ CallNode (location: (46,0)-(46,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (46,0)-(46,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (46,7)-(46,19))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ SelfNode (location: (46,7)-(46,11))
+ │ │ ├── right:
+ │ │ │ @ SelfNode (location: (46,15)-(46,19))
+ │ │ └── operator_loc: (46,12)-(46,14) = ".."
+ │ └── operator_loc: (46,4)-(46,6) = "=>"
+ ├── @ MatchRequiredNode (location: (47,0)-(47,19))
+ │ ├── value:
+ │ │ @ CallNode (location: (47,0)-(47,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (47,0)-(47,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (47,7)-(47,19))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ TrueNode (location: (47,7)-(47,11))
+ │ │ ├── right:
+ │ │ │ @ TrueNode (location: (47,15)-(47,19))
+ │ │ └── operator_loc: (47,12)-(47,14) = ".."
+ │ └── operator_loc: (47,4)-(47,6) = "=>"
+ ├── @ MatchRequiredNode (location: (48,0)-(48,21))
+ │ ├── value:
+ │ │ @ CallNode (location: (48,0)-(48,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (48,0)-(48,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (48,7)-(48,21))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ FalseNode (location: (48,7)-(48,12))
+ │ │ ├── right:
+ │ │ │ @ FalseNode (location: (48,16)-(48,21))
+ │ │ └── operator_loc: (48,13)-(48,15) = ".."
+ │ └── operator_loc: (48,4)-(48,6) = "=>"
+ ├── @ MatchRequiredNode (location: (49,0)-(49,27))
+ │ ├── value:
+ │ │ @ CallNode (location: (49,0)-(49,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (49,0)-(49,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (49,7)-(49,27))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ SourceFileNode (location: (49,7)-(49,15))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── filepath: "patterns.txt"
+ │ │ ├── right:
+ │ │ │ @ SourceFileNode (location: (49,19)-(49,27))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── filepath: "patterns.txt"
+ │ │ └── operator_loc: (49,16)-(49,18) = ".."
+ │ └── operator_loc: (49,4)-(49,6) = "=>"
+ ├── @ MatchRequiredNode (location: (50,0)-(50,27))
+ │ ├── value:
+ │ │ @ CallNode (location: (50,0)-(50,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (50,0)-(50,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (50,7)-(50,27))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ SourceLineNode (location: (50,7)-(50,15))
+ │ │ ├── right:
+ │ │ │ @ SourceLineNode (location: (50,19)-(50,27))
+ │ │ └── operator_loc: (50,16)-(50,18) = ".."
+ │ └── operator_loc: (50,4)-(50,6) = "=>"
+ ├── @ MatchRequiredNode (location: (51,0)-(51,35))
+ │ ├── value:
+ │ │ @ CallNode (location: (51,0)-(51,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (51,0)-(51,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (51,7)-(51,35))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ SourceEncodingNode (location: (51,7)-(51,19))
+ │ │ ├── right:
+ │ │ │ @ SourceEncodingNode (location: (51,23)-(51,35))
+ │ │ └── operator_loc: (51,20)-(51,22) = ".."
+ │ └── operator_loc: (51,4)-(51,6) = "=>"
+ ├── @ MatchRequiredNode (location: (52,0)-(52,31))
+ │ ├── value:
+ │ │ @ CallNode (location: (52,0)-(52,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (52,0)-(52,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (52,7)-(52,31))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ LambdaNode (location: (52,7)-(52,17))
+ │ │ │ ├── locals: []
+ │ │ │ ├── operator_loc: (52,7)-(52,9) = "->"
+ │ │ │ ├── opening_loc: (52,10)-(52,11) = "{"
+ │ │ │ ├── closing_loc: (52,16)-(52,17) = "}"
+ │ │ │ ├── parameters: ∅
+ │ │ │ └── body:
+ │ │ │ @ StatementsNode (location: (52,12)-(52,15))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ LocalVariableReadNode (location: (52,12)-(52,15))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 1
+ │ │ ├── right:
+ │ │ │ @ LambdaNode (location: (52,21)-(52,31))
+ │ │ │ ├── locals: []
+ │ │ │ ├── operator_loc: (52,21)-(52,23) = "->"
+ │ │ │ ├── opening_loc: (52,24)-(52,25) = "{"
+ │ │ │ ├── closing_loc: (52,30)-(52,31) = "}"
+ │ │ │ ├── parameters: ∅
+ │ │ │ └── body:
+ │ │ │ @ StatementsNode (location: (52,26)-(52,29))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ LocalVariableReadNode (location: (52,26)-(52,29))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 1
+ │ │ └── operator_loc: (52,18)-(52,20) = ".."
+ │ └── operator_loc: (52,4)-(52,6) = "=>"
+ ├── @ LocalVariableWriteNode (location: (54,0)-(54,7))
+ │ ├── name: :bar
+ │ ├── depth: 0
+ │ ├── name_loc: (54,0)-(54,3) = "bar"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (54,6)-(54,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (54,4)-(54,5) = "="
+ ├── @ MatchRequiredNode (location: (54,9)-(54,20))
+ │ ├── value:
+ │ │ @ CallNode (location: (54,9)-(54,12))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (54,9)-(54,12) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ PinnedVariableNode (location: (54,16)-(54,20))
+ │ │ ├── variable:
+ │ │ │ @ LocalVariableReadNode (location: (54,17)-(54,20))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ └── operator_loc: (54,16)-(54,17) = "^"
+ │ └── operator_loc: (54,13)-(54,15) = "=>"
+ ├── @ MatchRequiredNode (location: (55,0)-(55,12))
+ │ ├── value:
+ │ │ @ CallNode (location: (55,0)-(55,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (55,0)-(55,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ PinnedVariableNode (location: (55,7)-(55,12))
+ │ │ ├── variable:
+ │ │ │ @ InstanceVariableReadNode (location: (55,8)-(55,12))
+ │ │ │ └── name: :@bar
+ │ │ └── operator_loc: (55,7)-(55,8) = "^"
+ │ └── operator_loc: (55,4)-(55,6) = "=>"
+ ├── @ MatchRequiredNode (location: (56,0)-(56,13))
+ │ ├── value:
+ │ │ @ CallNode (location: (56,0)-(56,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (56,0)-(56,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ PinnedVariableNode (location: (56,7)-(56,13))
+ │ │ ├── variable:
+ │ │ │ @ ClassVariableReadNode (location: (56,8)-(56,13))
+ │ │ │ └── name: :@@bar
+ │ │ └── operator_loc: (56,7)-(56,8) = "^"
+ │ └── operator_loc: (56,4)-(56,6) = "=>"
+ ├── @ MatchRequiredNode (location: (57,0)-(57,12))
+ │ ├── value:
+ │ │ @ CallNode (location: (57,0)-(57,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (57,0)-(57,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ PinnedVariableNode (location: (57,7)-(57,12))
+ │ │ ├── variable:
+ │ │ │ @ GlobalVariableReadNode (location: (57,8)-(57,12))
+ │ │ │ └── name: :$bar
+ │ │ └── operator_loc: (57,7)-(57,8) = "^"
+ │ └── operator_loc: (57,4)-(57,6) = "=>"
+ ├── @ MatchRequiredNode (location: (59,0)-(59,11))
+ │ ├── value:
+ │ │ @ CallNode (location: (59,0)-(59,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (59,0)-(59,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ PinnedExpressionNode (location: (59,7)-(59,11))
+ │ │ ├── expression:
+ │ │ │ @ IntegerNode (location: (59,9)-(59,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── operator_loc: (59,7)-(59,8) = "^"
+ │ │ ├── lparen_loc: (59,8)-(59,9) = "("
+ │ │ └── rparen_loc: (59,10)-(59,11) = ")"
+ │ └── operator_loc: (59,4)-(59,6) = "=>"
+ ├── @ MatchRequiredNode (location: (60,0)-(60,13))
+ │ ├── value:
+ │ │ @ CallNode (location: (60,0)-(60,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (60,0)-(60,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ PinnedExpressionNode (location: (60,7)-(60,13))
+ │ │ ├── expression:
+ │ │ │ @ NilNode (location: (60,9)-(60,12))
+ │ │ ├── operator_loc: (60,7)-(60,8) = "^"
+ │ │ ├── lparen_loc: (60,8)-(60,9) = "("
+ │ │ └── rparen_loc: (60,12)-(60,13) = ")"
+ │ └── operator_loc: (60,4)-(60,6) = "=>"
+ ├── @ MatchRequiredNode (location: (61,0)-(61,23))
+ │ ├── value:
+ │ │ @ CallNode (location: (61,0)-(61,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (61,0)-(61,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ PinnedExpressionNode (location: (61,7)-(61,23))
+ │ │ ├── expression:
+ │ │ │ @ CallNode (location: (61,9)-(61,22))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ StringNode (location: (61,9)-(61,14))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: (61,9)-(61,10) = "\""
+ │ │ │ │ ├── content_loc: (61,10)-(61,13) = "bar"
+ │ │ │ │ ├── closing_loc: (61,13)-(61,14) = "\""
+ │ │ │ │ └── unescaped: "bar"
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :+
+ │ │ │ ├── message_loc: (61,15)-(61,16) = "+"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (61,17)-(61,22))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ StringNode (location: (61,17)-(61,22))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: (61,17)-(61,18) = "\""
+ │ │ │ │ ├── content_loc: (61,18)-(61,21) = "baz"
+ │ │ │ │ ├── closing_loc: (61,21)-(61,22) = "\""
+ │ │ │ │ └── unescaped: "baz"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── operator_loc: (61,7)-(61,8) = "^"
+ │ │ ├── lparen_loc: (61,8)-(61,9) = "("
+ │ │ └── rparen_loc: (61,22)-(61,23) = ")"
+ │ └── operator_loc: (61,4)-(61,6) = "=>"
+ ├── @ MatchRequiredNode (location: (63,0)-(63,10))
+ │ ├── value:
+ │ │ @ CallNode (location: (63,0)-(63,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (63,0)-(63,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ConstantReadNode (location: (63,7)-(63,10))
+ │ │ └── name: :Foo
+ │ └── operator_loc: (63,4)-(63,6) = "=>"
+ ├── @ MatchRequiredNode (location: (64,0)-(64,20))
+ │ ├── value:
+ │ │ @ CallNode (location: (64,0)-(64,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (64,0)-(64,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ConstantPathNode (location: (64,7)-(64,20))
+ │ │ ├── parent:
+ │ │ │ @ ConstantPathNode (location: (64,7)-(64,15))
+ │ │ │ ├── parent:
+ │ │ │ │ @ ConstantReadNode (location: (64,7)-(64,10))
+ │ │ │ │ └── name: :Foo
+ │ │ │ ├── child:
+ │ │ │ │ @ ConstantReadNode (location: (64,12)-(64,15))
+ │ │ │ │ └── name: :Bar
+ │ │ │ └── delimiter_loc: (64,10)-(64,12) = "::"
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (64,17)-(64,20))
+ │ │ │ └── name: :Baz
+ │ │ └── delimiter_loc: (64,15)-(64,17) = "::"
+ │ └── operator_loc: (64,4)-(64,6) = "=>"
+ ├── @ MatchRequiredNode (location: (65,0)-(65,12))
+ │ ├── value:
+ │ │ @ CallNode (location: (65,0)-(65,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (65,0)-(65,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ConstantPathNode (location: (65,7)-(65,12))
+ │ │ ├── parent: ∅
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (65,9)-(65,12))
+ │ │ │ └── name: :Foo
+ │ │ └── delimiter_loc: (65,7)-(65,9) = "::"
+ │ └── operator_loc: (65,4)-(65,6) = "=>"
+ ├── @ MatchRequiredNode (location: (66,0)-(66,22))
+ │ ├── value:
+ │ │ @ CallNode (location: (66,0)-(66,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (66,0)-(66,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ConstantPathNode (location: (66,7)-(66,22))
+ │ │ ├── parent:
+ │ │ │ @ ConstantPathNode (location: (66,7)-(66,17))
+ │ │ │ ├── parent:
+ │ │ │ │ @ ConstantPathNode (location: (66,7)-(66,12))
+ │ │ │ │ ├── parent: ∅
+ │ │ │ │ ├── child:
+ │ │ │ │ │ @ ConstantReadNode (location: (66,9)-(66,12))
+ │ │ │ │ │ └── name: :Foo
+ │ │ │ │ └── delimiter_loc: (66,7)-(66,9) = "::"
+ │ │ │ ├── child:
+ │ │ │ │ @ ConstantReadNode (location: (66,14)-(66,17))
+ │ │ │ │ └── name: :Bar
+ │ │ │ └── delimiter_loc: (66,12)-(66,14) = "::"
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (66,19)-(66,22))
+ │ │ │ └── name: :Baz
+ │ │ └── delimiter_loc: (66,17)-(66,19) = "::"
+ │ └── operator_loc: (66,4)-(66,6) = "=>"
+ ├── @ MatchRequiredNode (location: (68,0)-(68,12))
+ │ ├── value:
+ │ │ @ CallNode (location: (68,0)-(68,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (68,0)-(68,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (68,7)-(68,12))
+ │ │ ├── constant:
+ │ │ │ @ ConstantReadNode (location: (68,7)-(68,10))
+ │ │ │ └── name: :Foo
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (68,10)-(68,11) = "("
+ │ │ └── closing_loc: (68,11)-(68,12) = ")"
+ │ └── operator_loc: (68,4)-(68,6) = "=>"
+ ├── @ MatchRequiredNode (location: (69,0)-(69,13))
+ │ ├── value:
+ │ │ @ CallNode (location: (69,0)-(69,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (69,0)-(69,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (69,7)-(69,13))
+ │ │ ├── constant:
+ │ │ │ @ ConstantReadNode (location: (69,7)-(69,10))
+ │ │ │ └── name: :Foo
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (69,11)-(69,12))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (69,10)-(69,11) = "("
+ │ │ └── closing_loc: (69,12)-(69,13) = ")"
+ │ └── operator_loc: (69,4)-(69,6) = "=>"
+ ├── @ MatchRequiredNode (location: (70,0)-(70,19))
+ │ ├── value:
+ │ │ @ CallNode (location: (70,0)-(70,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (70,0)-(70,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (70,7)-(70,19))
+ │ │ ├── constant:
+ │ │ │ @ ConstantReadNode (location: (70,7)-(70,10))
+ │ │ │ └── name: :Foo
+ │ │ ├── requireds: (length: 3)
+ │ │ │ ├── @ IntegerNode (location: (70,11)-(70,12))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── @ IntegerNode (location: (70,14)-(70,15))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ └── @ IntegerNode (location: (70,17)-(70,18))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 3
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (70,10)-(70,11) = "("
+ │ │ └── closing_loc: (70,18)-(70,19) = ")"
+ │ └── operator_loc: (70,4)-(70,6) = "=>"
+ ├── @ MatchRequiredNode (location: (71,0)-(71,15))
+ │ ├── value:
+ │ │ @ CallNode (location: (71,0)-(71,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (71,0)-(71,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (71,7)-(71,15))
+ │ │ ├── constant:
+ │ │ │ @ ConstantReadNode (location: (71,7)-(71,10))
+ │ │ │ └── name: :Foo
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (71,11)-(71,14))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (71,10)-(71,11) = "("
+ │ │ └── closing_loc: (71,14)-(71,15) = ")"
+ │ └── operator_loc: (71,4)-(71,6) = "=>"
+ ├── @ MatchRequiredNode (location: (72,0)-(72,21))
+ │ ├── value:
+ │ │ @ CallNode (location: (72,0)-(72,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (72,0)-(72,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (72,7)-(72,21))
+ │ │ ├── constant:
+ │ │ │ @ ConstantReadNode (location: (72,7)-(72,10))
+ │ │ │ └── name: :Foo
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ SplatNode (location: (72,11)-(72,15))
+ │ │ │ ├── operator_loc: (72,11)-(72,12) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (72,12)-(72,15))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (72,17)-(72,20))
+ │ │ │ ├── name: :baz
+ │ │ │ └── depth: 0
+ │ │ ├── opening_loc: (72,10)-(72,11) = "("
+ │ │ └── closing_loc: (72,20)-(72,21) = ")"
+ │ └── operator_loc: (72,4)-(72,6) = "=>"
+ ├── @ MatchRequiredNode (location: (73,0)-(73,21))
+ │ ├── value:
+ │ │ @ CallNode (location: (73,0)-(73,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (73,0)-(73,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (73,7)-(73,21))
+ │ │ ├── constant:
+ │ │ │ @ ConstantReadNode (location: (73,7)-(73,10))
+ │ │ │ └── name: :Foo
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (73,11)-(73,14))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── rest:
+ │ │ │ @ SplatNode (location: (73,16)-(73,20))
+ │ │ │ ├── operator_loc: (73,16)-(73,17) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (73,17)-(73,20))
+ │ │ │ ├── name: :baz
+ │ │ │ └── depth: 0
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (73,10)-(73,11) = "("
+ │ │ └── closing_loc: (73,20)-(73,21) = ")"
+ │ └── operator_loc: (73,4)-(73,6) = "=>"
+ ├── @ MatchRequiredNode (location: (74,0)-(74,27))
+ │ ├── value:
+ │ │ @ CallNode (location: (74,0)-(74,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (74,0)-(74,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ FindPatternNode (location: (74,7)-(74,27))
+ │ │ ├── constant:
+ │ │ │ @ ConstantReadNode (location: (74,7)-(74,10))
+ │ │ │ └── name: :Foo
+ │ │ ├── left:
+ │ │ │ @ SplatNode (location: (74,11)-(74,15))
+ │ │ │ ├── operator_loc: (74,11)-(74,12) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (74,12)-(74,15))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (74,17)-(74,20))
+ │ │ │ ├── name: :baz
+ │ │ │ └── depth: 0
+ │ │ ├── right:
+ │ │ │ @ SplatNode (location: (74,22)-(74,26))
+ │ │ │ ├── operator_loc: (74,22)-(74,23) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (74,23)-(74,26))
+ │ │ │ ├── name: :qux
+ │ │ │ └── depth: 0
+ │ │ ├── opening_loc: (74,10)-(74,11) = "("
+ │ │ └── closing_loc: (74,26)-(74,27) = ")"
+ │ └── operator_loc: (74,4)-(74,6) = "=>"
+ ├── @ MatchRequiredNode (location: (76,0)-(76,12))
+ │ ├── value:
+ │ │ @ CallNode (location: (76,0)-(76,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (76,0)-(76,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (76,7)-(76,12))
+ │ │ ├── constant:
+ │ │ │ @ ConstantReadNode (location: (76,7)-(76,10))
+ │ │ │ └── name: :Foo
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (76,10)-(76,11) = "["
+ │ │ └── closing_loc: (76,11)-(76,12) = "]"
+ │ └── operator_loc: (76,4)-(76,6) = "=>"
+ ├── @ MatchRequiredNode (location: (77,0)-(77,13))
+ │ ├── value:
+ │ │ @ CallNode (location: (77,0)-(77,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (77,0)-(77,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (77,7)-(77,13))
+ │ │ ├── constant:
+ │ │ │ @ ConstantReadNode (location: (77,7)-(77,10))
+ │ │ │ └── name: :Foo
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (77,11)-(77,12))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (77,10)-(77,11) = "["
+ │ │ └── closing_loc: (77,12)-(77,13) = "]"
+ │ └── operator_loc: (77,4)-(77,6) = "=>"
+ ├── @ MatchRequiredNode (location: (78,0)-(78,19))
+ │ ├── value:
+ │ │ @ CallNode (location: (78,0)-(78,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (78,0)-(78,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (78,7)-(78,19))
+ │ │ ├── constant:
+ │ │ │ @ ConstantReadNode (location: (78,7)-(78,10))
+ │ │ │ └── name: :Foo
+ │ │ ├── requireds: (length: 3)
+ │ │ │ ├── @ IntegerNode (location: (78,11)-(78,12))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── @ IntegerNode (location: (78,14)-(78,15))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ └── @ IntegerNode (location: (78,17)-(78,18))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 3
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (78,10)-(78,11) = "["
+ │ │ └── closing_loc: (78,18)-(78,19) = "]"
+ │ └── operator_loc: (78,4)-(78,6) = "=>"
+ ├── @ MatchRequiredNode (location: (79,0)-(79,17))
+ │ ├── value:
+ │ │ @ CallNode (location: (79,0)-(79,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (79,0)-(79,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (79,7)-(79,17))
+ │ │ ├── constant:
+ │ │ │ @ ConstantReadNode (location: (79,7)-(79,10))
+ │ │ │ └── name: :Foo
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ ArrayPatternNode (location: (79,11)-(79,16))
+ │ │ │ ├── constant:
+ │ │ │ │ @ ConstantReadNode (location: (79,11)-(79,14))
+ │ │ │ │ └── name: :Foo
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── opening_loc: (79,14)-(79,15) = "["
+ │ │ │ └── closing_loc: (79,15)-(79,16) = "]"
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (79,10)-(79,11) = "["
+ │ │ └── closing_loc: (79,16)-(79,17) = "]"
+ │ └── operator_loc: (79,4)-(79,6) = "=>"
+ ├── @ MatchRequiredNode (location: (80,0)-(80,15))
+ │ ├── value:
+ │ │ @ CallNode (location: (80,0)-(80,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (80,0)-(80,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (80,7)-(80,15))
+ │ │ ├── constant:
+ │ │ │ @ ConstantReadNode (location: (80,7)-(80,10))
+ │ │ │ └── name: :Foo
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (80,11)-(80,14))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (80,10)-(80,11) = "["
+ │ │ └── closing_loc: (80,14)-(80,15) = "]"
+ │ └── operator_loc: (80,4)-(80,6) = "=>"
+ ├── @ MatchRequiredNode (location: (81,0)-(81,21))
+ │ ├── value:
+ │ │ @ CallNode (location: (81,0)-(81,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (81,0)-(81,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (81,7)-(81,21))
+ │ │ ├── constant:
+ │ │ │ @ ConstantReadNode (location: (81,7)-(81,10))
+ │ │ │ └── name: :Foo
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ SplatNode (location: (81,11)-(81,15))
+ │ │ │ ├── operator_loc: (81,11)-(81,12) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (81,12)-(81,15))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (81,17)-(81,20))
+ │ │ │ ├── name: :baz
+ │ │ │ └── depth: 0
+ │ │ ├── opening_loc: (81,10)-(81,11) = "["
+ │ │ └── closing_loc: (81,20)-(81,21) = "]"
+ │ └── operator_loc: (81,4)-(81,6) = "=>"
+ ├── @ MatchRequiredNode (location: (82,0)-(82,21))
+ │ ├── value:
+ │ │ @ CallNode (location: (82,0)-(82,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (82,0)-(82,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (82,7)-(82,21))
+ │ │ ├── constant:
+ │ │ │ @ ConstantReadNode (location: (82,7)-(82,10))
+ │ │ │ └── name: :Foo
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (82,11)-(82,14))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── rest:
+ │ │ │ @ SplatNode (location: (82,16)-(82,20))
+ │ │ │ ├── operator_loc: (82,16)-(82,17) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (82,17)-(82,20))
+ │ │ │ ├── name: :baz
+ │ │ │ └── depth: 0
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (82,10)-(82,11) = "["
+ │ │ └── closing_loc: (82,20)-(82,21) = "]"
+ │ └── operator_loc: (82,4)-(82,6) = "=>"
+ ├── @ MatchRequiredNode (location: (83,0)-(83,27))
+ │ ├── value:
+ │ │ @ CallNode (location: (83,0)-(83,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (83,0)-(83,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ FindPatternNode (location: (83,7)-(83,27))
+ │ │ ├── constant:
+ │ │ │ @ ConstantReadNode (location: (83,7)-(83,10))
+ │ │ │ └── name: :Foo
+ │ │ ├── left:
+ │ │ │ @ SplatNode (location: (83,11)-(83,15))
+ │ │ │ ├── operator_loc: (83,11)-(83,12) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (83,12)-(83,15))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (83,17)-(83,20))
+ │ │ │ ├── name: :baz
+ │ │ │ └── depth: 0
+ │ │ ├── right:
+ │ │ │ @ SplatNode (location: (83,22)-(83,26))
+ │ │ │ ├── operator_loc: (83,22)-(83,23) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (83,23)-(83,26))
+ │ │ │ ├── name: :qux
+ │ │ │ └── depth: 0
+ │ │ ├── opening_loc: (83,10)-(83,11) = "["
+ │ │ └── closing_loc: (83,26)-(83,27) = "]"
+ │ └── operator_loc: (83,4)-(83,6) = "=>"
+ ├── @ MatchRequiredNode (location: (85,0)-(85,11))
+ │ ├── value:
+ │ │ @ CallNode (location: (85,0)-(85,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (85,0)-(85,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (85,7)-(85,11))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ SplatNode (location: (85,7)-(85,11))
+ │ │ │ ├── operator_loc: (85,7)-(85,8) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (85,8)-(85,11))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (85,4)-(85,6) = "=>"
+ ├── @ MatchRequiredNode (location: (86,0)-(86,21))
+ │ ├── value:
+ │ │ @ CallNode (location: (86,0)-(86,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (86,0)-(86,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (86,7)-(86,21))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ SplatNode (location: (86,7)-(86,11))
+ │ │ │ ├── operator_loc: (86,7)-(86,8) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (86,8)-(86,11))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── posts: (length: 2)
+ │ │ │ ├── @ LocalVariableTargetNode (location: (86,13)-(86,16))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ └── @ LocalVariableTargetNode (location: (86,18)-(86,21))
+ │ │ │ ├── name: :qux
+ │ │ │ └── depth: 0
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (86,4)-(86,6) = "=>"
+ ├── @ MatchRequiredNode (location: (87,0)-(87,21))
+ │ ├── value:
+ │ │ @ CallNode (location: (87,0)-(87,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (87,0)-(87,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (87,7)-(87,21))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (87,7)-(87,10))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── rest:
+ │ │ │ @ SplatNode (location: (87,12)-(87,16))
+ │ │ │ ├── operator_loc: (87,12)-(87,13) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (87,13)-(87,16))
+ │ │ │ ├── name: :baz
+ │ │ │ └── depth: 0
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (87,18)-(87,21))
+ │ │ │ ├── name: :qux
+ │ │ │ └── depth: 0
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (87,4)-(87,6) = "=>"
+ ├── @ MatchRequiredNode (location: (88,0)-(88,21))
+ │ ├── value:
+ │ │ @ CallNode (location: (88,0)-(88,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (88,0)-(88,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (88,7)-(88,21))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ LocalVariableTargetNode (location: (88,7)-(88,10))
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ └── depth: 0
+ │ │ │ └── @ LocalVariableTargetNode (location: (88,12)-(88,15))
+ │ │ │ ├── name: :baz
+ │ │ │ └── depth: 0
+ │ │ ├── rest:
+ │ │ │ @ SplatNode (location: (88,17)-(88,21))
+ │ │ │ ├── operator_loc: (88,17)-(88,18) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (88,18)-(88,21))
+ │ │ │ ├── name: :qux
+ │ │ │ └── depth: 0
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (88,4)-(88,6) = "=>"
+ ├── @ MatchRequiredNode (location: (89,0)-(89,22))
+ │ ├── value:
+ │ │ @ CallNode (location: (89,0)-(89,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (89,0)-(89,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ FindPatternNode (location: (89,7)-(89,22))
+ │ │ ├── constant: ∅
+ │ │ ├── left:
+ │ │ │ @ SplatNode (location: (89,7)-(89,11))
+ │ │ │ ├── operator_loc: (89,7)-(89,8) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (89,8)-(89,11))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (89,13)-(89,16))
+ │ │ │ ├── name: :baz
+ │ │ │ └── depth: 0
+ │ │ ├── right:
+ │ │ │ @ SplatNode (location: (89,18)-(89,22))
+ │ │ │ ├── operator_loc: (89,18)-(89,19) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (89,19)-(89,22))
+ │ │ │ ├── name: :qux
+ │ │ │ └── depth: 0
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (89,4)-(89,6) = "=>"
+ ├── @ MatchRequiredNode (location: (91,0)-(91,11))
+ │ ├── value:
+ │ │ @ CallNode (location: (91,0)-(91,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (91,0)-(91,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (91,7)-(91,11))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (91,7)-(91,10))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── rest:
+ │ │ │ @ ImplicitRestNode (location: (91,10)-(91,11))
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (91,4)-(91,6) = "=>"
+ ├── @ MatchRequiredNode (location: (95,0)-(95,9))
+ │ ├── value:
+ │ │ @ CallNode (location: (95,0)-(95,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (95,0)-(95,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (95,7)-(95,9))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (95,7)-(95,8) = "["
+ │ │ └── closing_loc: (95,8)-(95,9) = "]"
+ │ └── operator_loc: (95,4)-(95,6) = "=>"
+ ├── @ MatchRequiredNode (location: (96,0)-(96,17))
+ │ ├── value:
+ │ │ @ CallNode (location: (96,0)-(96,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (96,0)-(96,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (96,7)-(96,17))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ ArrayPatternNode (location: (96,8)-(96,16))
+ │ │ │ ├── constant: ∅
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ ArrayPatternNode (location: (96,9)-(96,15))
+ │ │ │ │ ├── constant: ∅
+ │ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ │ └── @ ArrayPatternNode (location: (96,10)-(96,14))
+ │ │ │ │ │ ├── constant: ∅
+ │ │ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ │ │ └── @ ArrayPatternNode (location: (96,11)-(96,13))
+ │ │ │ │ │ │ ├── constant: ∅
+ │ │ │ │ │ │ ├── requireds: (length: 0)
+ │ │ │ │ │ │ ├── rest: ∅
+ │ │ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ │ │ ├── opening_loc: (96,11)-(96,12) = "["
+ │ │ │ │ │ │ └── closing_loc: (96,12)-(96,13) = "]"
+ │ │ │ │ │ ├── rest: ∅
+ │ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ │ ├── opening_loc: (96,10)-(96,11) = "["
+ │ │ │ │ │ └── closing_loc: (96,13)-(96,14) = "]"
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ ├── opening_loc: (96,9)-(96,10) = "["
+ │ │ │ │ └── closing_loc: (96,14)-(96,15) = "]"
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── opening_loc: (96,8)-(96,9) = "["
+ │ │ │ └── closing_loc: (96,15)-(96,16) = "]"
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (96,7)-(96,8) = "["
+ │ │ └── closing_loc: (96,16)-(96,17) = "]"
+ │ └── operator_loc: (96,4)-(96,6) = "=>"
+ ├── @ MatchRequiredNode (location: (98,0)-(98,13))
+ │ ├── value:
+ │ │ @ CallNode (location: (98,0)-(98,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (98,0)-(98,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (98,7)-(98,13))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ SplatNode (location: (98,8)-(98,12))
+ │ │ │ ├── operator_loc: (98,8)-(98,9) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (98,9)-(98,12))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (98,7)-(98,8) = "["
+ │ │ └── closing_loc: (98,12)-(98,13) = "]"
+ │ └── operator_loc: (98,4)-(98,6) = "=>"
+ ├── @ MatchRequiredNode (location: (99,0)-(99,23))
+ │ ├── value:
+ │ │ @ CallNode (location: (99,0)-(99,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (99,0)-(99,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (99,7)-(99,23))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ SplatNode (location: (99,8)-(99,12))
+ │ │ │ ├── operator_loc: (99,8)-(99,9) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (99,9)-(99,12))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── posts: (length: 2)
+ │ │ │ ├── @ LocalVariableTargetNode (location: (99,14)-(99,17))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ └── @ LocalVariableTargetNode (location: (99,19)-(99,22))
+ │ │ │ ├── name: :qux
+ │ │ │ └── depth: 0
+ │ │ ├── opening_loc: (99,7)-(99,8) = "["
+ │ │ └── closing_loc: (99,22)-(99,23) = "]"
+ │ └── operator_loc: (99,4)-(99,6) = "=>"
+ ├── @ MatchRequiredNode (location: (100,0)-(100,23))
+ │ ├── value:
+ │ │ @ CallNode (location: (100,0)-(100,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (100,0)-(100,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (100,7)-(100,23))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (100,8)-(100,11))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── rest:
+ │ │ │ @ SplatNode (location: (100,13)-(100,17))
+ │ │ │ ├── operator_loc: (100,13)-(100,14) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (100,14)-(100,17))
+ │ │ │ ├── name: :baz
+ │ │ │ └── depth: 0
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (100,19)-(100,22))
+ │ │ │ ├── name: :qux
+ │ │ │ └── depth: 0
+ │ │ ├── opening_loc: (100,7)-(100,8) = "["
+ │ │ └── closing_loc: (100,22)-(100,23) = "]"
+ │ └── operator_loc: (100,4)-(100,6) = "=>"
+ ├── @ MatchRequiredNode (location: (101,0)-(101,23))
+ │ ├── value:
+ │ │ @ CallNode (location: (101,0)-(101,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (101,0)-(101,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (101,7)-(101,23))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ LocalVariableTargetNode (location: (101,8)-(101,11))
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ └── depth: 0
+ │ │ │ └── @ LocalVariableTargetNode (location: (101,13)-(101,16))
+ │ │ │ ├── name: :baz
+ │ │ │ └── depth: 0
+ │ │ ├── rest:
+ │ │ │ @ SplatNode (location: (101,18)-(101,22))
+ │ │ │ ├── operator_loc: (101,18)-(101,19) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (101,19)-(101,22))
+ │ │ │ ├── name: :qux
+ │ │ │ └── depth: 0
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (101,7)-(101,8) = "["
+ │ │ └── closing_loc: (101,22)-(101,23) = "]"
+ │ └── operator_loc: (101,4)-(101,6) = "=>"
+ ├── @ MatchRequiredNode (location: (102,0)-(102,24))
+ │ ├── value:
+ │ │ @ CallNode (location: (102,0)-(102,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (102,0)-(102,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ FindPatternNode (location: (102,7)-(102,24))
+ │ │ ├── constant: ∅
+ │ │ ├── left:
+ │ │ │ @ SplatNode (location: (102,8)-(102,12))
+ │ │ │ ├── operator_loc: (102,8)-(102,9) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (102,9)-(102,12))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (102,14)-(102,17))
+ │ │ │ ├── name: :baz
+ │ │ │ └── depth: 0
+ │ │ ├── right:
+ │ │ │ @ SplatNode (location: (102,19)-(102,23))
+ │ │ │ ├── operator_loc: (102,19)-(102,20) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (102,20)-(102,23))
+ │ │ │ ├── name: :qux
+ │ │ │ └── depth: 0
+ │ │ ├── opening_loc: (102,7)-(102,8) = "["
+ │ │ └── closing_loc: (102,23)-(102,24) = "]"
+ │ └── operator_loc: (102,4)-(102,6) = "=>"
+ ├── @ MatchPredicateNode (location: (104,0)-(104,10))
+ │ ├── value:
+ │ │ @ CallNode (location: (104,0)-(104,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (104,0)-(104,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ LocalVariableTargetNode (location: (104,7)-(104,10))
+ │ │ ├── name: :bar
+ │ │ └── depth: 0
+ │ └── operator_loc: (104,4)-(104,6) = "in"
+ ├── @ MatchPredicateNode (location: (105,0)-(105,8))
+ │ ├── value:
+ │ │ @ CallNode (location: (105,0)-(105,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (105,0)-(105,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ IntegerNode (location: (105,7)-(105,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (105,4)-(105,6) = "in"
+ ├── @ MatchPredicateNode (location: (106,0)-(106,10))
+ │ ├── value:
+ │ │ @ CallNode (location: (106,0)-(106,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (106,0)-(106,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ FloatNode (location: (106,7)-(106,10))
+ │ │ └── value: 1.0
+ │ └── operator_loc: (106,4)-(106,6) = "in"
+ ├── @ MatchPredicateNode (location: (107,0)-(107,9))
+ │ ├── value:
+ │ │ @ CallNode (location: (107,0)-(107,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (107,0)-(107,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ImaginaryNode (location: (107,7)-(107,9))
+ │ │ └── numeric:
+ │ │ @ IntegerNode (location: (107,7)-(107,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (107,4)-(107,6) = "in"
+ ├── @ MatchPredicateNode (location: (108,0)-(108,9))
+ │ ├── value:
+ │ │ @ CallNode (location: (108,0)-(108,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (108,0)-(108,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RationalNode (location: (108,7)-(108,9))
+ │ │ └── numeric:
+ │ │ @ IntegerNode (location: (108,7)-(108,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (108,4)-(108,6) = "in"
+ ├── @ MatchPredicateNode (location: (109,0)-(109,11))
+ │ ├── value:
+ │ │ @ CallNode (location: (109,0)-(109,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (109,0)-(109,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ SymbolNode (location: (109,7)-(109,11))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (109,7)-(109,8) = ":"
+ │ │ ├── value_loc: (109,8)-(109,11) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ └── operator_loc: (109,4)-(109,6) = "in"
+ ├── @ MatchPredicateNode (location: (110,0)-(110,14))
+ │ ├── value:
+ │ │ @ CallNode (location: (110,0)-(110,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (110,0)-(110,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ SymbolNode (location: (110,7)-(110,14))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (110,7)-(110,10) = "%s["
+ │ │ ├── value_loc: (110,10)-(110,13) = "foo"
+ │ │ ├── closing_loc: (110,13)-(110,14) = "]"
+ │ │ └── unescaped: "foo"
+ │ └── operator_loc: (110,4)-(110,6) = "in"
+ ├── @ MatchPredicateNode (location: (111,0)-(111,13))
+ │ ├── value:
+ │ │ @ CallNode (location: (111,0)-(111,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (111,0)-(111,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ SymbolNode (location: (111,7)-(111,13))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (111,7)-(111,9) = ":\""
+ │ │ ├── value_loc: (111,9)-(111,12) = "foo"
+ │ │ ├── closing_loc: (111,12)-(111,13) = "\""
+ │ │ └── unescaped: "foo"
+ │ └── operator_loc: (111,4)-(111,6) = "in"
+ ├── @ MatchPredicateNode (location: (112,0)-(112,12))
+ │ ├── value:
+ │ │ @ CallNode (location: (112,0)-(112,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (112,0)-(112,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ RegularExpressionNode (location: (112,7)-(112,12))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (112,7)-(112,8) = "/"
+ │ │ ├── content_loc: (112,8)-(112,11) = "foo"
+ │ │ ├── closing_loc: (112,11)-(112,12) = "/"
+ │ │ └── unescaped: "foo"
+ │ └── operator_loc: (112,4)-(112,6) = "in"
+ ├── @ MatchPredicateNode (location: (113,0)-(113,12))
+ │ ├── value:
+ │ │ @ CallNode (location: (113,0)-(113,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (113,0)-(113,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ XStringNode (location: (113,7)-(113,12))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (113,7)-(113,8) = "`"
+ │ │ ├── content_loc: (113,8)-(113,11) = "foo"
+ │ │ ├── closing_loc: (113,11)-(113,12) = "`"
+ │ │ └── unescaped: "foo"
+ │ └── operator_loc: (113,4)-(113,6) = "in"
+ ├── @ MatchPredicateNode (location: (114,0)-(114,14))
+ │ ├── value:
+ │ │ @ CallNode (location: (114,0)-(114,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (114,0)-(114,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ XStringNode (location: (114,7)-(114,14))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (114,7)-(114,10) = "%x["
+ │ │ ├── content_loc: (114,10)-(114,13) = "foo"
+ │ │ ├── closing_loc: (114,13)-(114,14) = "]"
+ │ │ └── unescaped: "foo"
+ │ └── operator_loc: (114,4)-(114,6) = "in"
+ ├── @ MatchPredicateNode (location: (115,0)-(115,14))
+ │ ├── value:
+ │ │ @ CallNode (location: (115,0)-(115,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (115,0)-(115,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayNode (location: (115,7)-(115,14))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (115,10)-(115,13))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (115,10)-(115,13) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── opening_loc: (115,7)-(115,10) = "%i["
+ │ │ └── closing_loc: (115,13)-(115,14) = "]"
+ │ └── operator_loc: (115,4)-(115,6) = "in"
+ ├── @ MatchPredicateNode (location: (116,0)-(116,14))
+ │ ├── value:
+ │ │ @ CallNode (location: (116,0)-(116,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (116,0)-(116,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayNode (location: (116,7)-(116,14))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (116,10)-(116,13))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (116,10)-(116,13) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── opening_loc: (116,7)-(116,10) = "%I["
+ │ │ └── closing_loc: (116,13)-(116,14) = "]"
+ │ └── operator_loc: (116,4)-(116,6) = "in"
+ ├── @ MatchPredicateNode (location: (117,0)-(117,14))
+ │ ├── value:
+ │ │ @ CallNode (location: (117,0)-(117,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (117,0)-(117,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayNode (location: (117,7)-(117,14))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ StringNode (location: (117,10)-(117,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (117,10)-(117,13) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── opening_loc: (117,7)-(117,10) = "%w["
+ │ │ └── closing_loc: (117,13)-(117,14) = "]"
+ │ └── operator_loc: (117,4)-(117,6) = "in"
+ ├── @ MatchPredicateNode (location: (118,0)-(118,14))
+ │ ├── value:
+ │ │ @ CallNode (location: (118,0)-(118,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (118,0)-(118,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayNode (location: (118,7)-(118,14))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ StringNode (location: (118,10)-(118,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (118,10)-(118,13) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── opening_loc: (118,7)-(118,10) = "%W["
+ │ │ └── closing_loc: (118,13)-(118,14) = "]"
+ │ └── operator_loc: (118,4)-(118,6) = "in"
+ ├── @ MatchPredicateNode (location: (119,0)-(119,14))
+ │ ├── value:
+ │ │ @ CallNode (location: (119,0)-(119,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (119,0)-(119,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ StringNode (location: (119,7)-(119,14))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (119,7)-(119,10) = "%q["
+ │ │ ├── content_loc: (119,10)-(119,13) = "foo"
+ │ │ ├── closing_loc: (119,13)-(119,14) = "]"
+ │ │ └── unescaped: "foo"
+ │ └── operator_loc: (119,4)-(119,6) = "in"
+ ├── @ MatchPredicateNode (location: (120,0)-(120,14))
+ │ ├── value:
+ │ │ @ CallNode (location: (120,0)-(120,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (120,0)-(120,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ StringNode (location: (120,7)-(120,14))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (120,7)-(120,10) = "%Q["
+ │ │ ├── content_loc: (120,10)-(120,13) = "foo"
+ │ │ ├── closing_loc: (120,13)-(120,14) = "]"
+ │ │ └── unescaped: "foo"
+ │ └── operator_loc: (120,4)-(120,6) = "in"
+ ├── @ MatchPredicateNode (location: (121,0)-(121,12))
+ │ ├── value:
+ │ │ @ CallNode (location: (121,0)-(121,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (121,0)-(121,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ StringNode (location: (121,7)-(121,12))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (121,7)-(121,8) = "\""
+ │ │ ├── content_loc: (121,8)-(121,11) = "foo"
+ │ │ ├── closing_loc: (121,11)-(121,12) = "\""
+ │ │ └── unescaped: "foo"
+ │ └── operator_loc: (121,4)-(121,6) = "in"
+ ├── @ MatchPredicateNode (location: (122,0)-(122,10))
+ │ ├── value:
+ │ │ @ CallNode (location: (122,0)-(122,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (122,0)-(122,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ NilNode (location: (122,7)-(122,10))
+ │ └── operator_loc: (122,4)-(122,6) = "in"
+ ├── @ MatchPredicateNode (location: (123,0)-(123,11))
+ │ ├── value:
+ │ │ @ CallNode (location: (123,0)-(123,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (123,0)-(123,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ SelfNode (location: (123,7)-(123,11))
+ │ └── operator_loc: (123,4)-(123,6) = "in"
+ ├── @ MatchPredicateNode (location: (124,0)-(124,11))
+ │ ├── value:
+ │ │ @ CallNode (location: (124,0)-(124,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (124,0)-(124,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ TrueNode (location: (124,7)-(124,11))
+ │ └── operator_loc: (124,4)-(124,6) = "in"
+ ├── @ MatchPredicateNode (location: (125,0)-(125,12))
+ │ ├── value:
+ │ │ @ CallNode (location: (125,0)-(125,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (125,0)-(125,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ FalseNode (location: (125,7)-(125,12))
+ │ └── operator_loc: (125,4)-(125,6) = "in"
+ ├── @ MatchPredicateNode (location: (126,0)-(126,15))
+ │ ├── value:
+ │ │ @ CallNode (location: (126,0)-(126,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (126,0)-(126,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ SourceFileNode (location: (126,7)-(126,15))
+ │ │ ├── flags: ∅
+ │ │ └── filepath: "patterns.txt"
+ │ └── operator_loc: (126,4)-(126,6) = "in"
+ ├── @ MatchPredicateNode (location: (127,0)-(127,15))
+ │ ├── value:
+ │ │ @ CallNode (location: (127,0)-(127,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (127,0)-(127,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ SourceLineNode (location: (127,7)-(127,15))
+ │ └── operator_loc: (127,4)-(127,6) = "in"
+ ├── @ MatchPredicateNode (location: (128,0)-(128,19))
+ │ ├── value:
+ │ │ @ CallNode (location: (128,0)-(128,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (128,0)-(128,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ SourceEncodingNode (location: (128,7)-(128,19))
+ │ └── operator_loc: (128,4)-(128,6) = "in"
+ ├── @ MatchPredicateNode (location: (129,0)-(129,17))
+ │ ├── value:
+ │ │ @ CallNode (location: (129,0)-(129,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (129,0)-(129,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ LambdaNode (location: (129,7)-(129,17))
+ │ │ ├── locals: []
+ │ │ ├── operator_loc: (129,7)-(129,9) = "->"
+ │ │ ├── opening_loc: (129,10)-(129,11) = "{"
+ │ │ ├── closing_loc: (129,16)-(129,17) = "}"
+ │ │ ├── parameters: ∅
+ │ │ └── body:
+ │ │ @ StatementsNode (location: (129,12)-(129,15))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (129,12)-(129,15))
+ │ │ ├── name: :bar
+ │ │ └── depth: 1
+ │ └── operator_loc: (129,4)-(129,6) = "in"
+ ├── @ MatchPredicateNode (location: (131,0)-(131,11))
+ │ ├── value:
+ │ │ @ CallNode (location: (131,0)-(131,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (131,0)-(131,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (131,7)-(131,11))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (131,7)-(131,10))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── rest:
+ │ │ │ @ ImplicitRestNode (location: (131,10)-(131,11))
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (131,4)-(131,6) = "in"
+ ├── @ CaseMatchNode (location: (135,0)-(135,25))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (135,5)-(135,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (135,5)-(135,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (135,10)-(135,21))
+ │ │ ├── pattern:
+ │ │ │ @ LocalVariableTargetNode (location: (135,13)-(135,16))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (135,10)-(135,12) = "in"
+ │ │ └── then_loc: (135,17)-(135,21) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (135,0)-(135,4) = "case"
+ │ └── end_keyword_loc: (135,22)-(135,25) = "end"
+ ├── @ CaseMatchNode (location: (136,0)-(136,23))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (136,5)-(136,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (136,5)-(136,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (136,10)-(136,19))
+ │ │ ├── pattern:
+ │ │ │ @ IntegerNode (location: (136,13)-(136,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (136,10)-(136,12) = "in"
+ │ │ └── then_loc: (136,15)-(136,19) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (136,0)-(136,4) = "case"
+ │ └── end_keyword_loc: (136,20)-(136,23) = "end"
+ ├── @ CaseMatchNode (location: (137,0)-(137,25))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (137,5)-(137,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (137,5)-(137,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (137,10)-(137,21))
+ │ │ ├── pattern:
+ │ │ │ @ FloatNode (location: (137,13)-(137,16))
+ │ │ │ └── value: 1.0
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (137,10)-(137,12) = "in"
+ │ │ └── then_loc: (137,17)-(137,21) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (137,0)-(137,4) = "case"
+ │ └── end_keyword_loc: (137,22)-(137,25) = "end"
+ ├── @ CaseMatchNode (location: (138,0)-(138,24))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (138,5)-(138,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (138,5)-(138,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (138,10)-(138,20))
+ │ │ ├── pattern:
+ │ │ │ @ ImaginaryNode (location: (138,13)-(138,15))
+ │ │ │ └── numeric:
+ │ │ │ @ IntegerNode (location: (138,13)-(138,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (138,10)-(138,12) = "in"
+ │ │ └── then_loc: (138,16)-(138,20) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (138,0)-(138,4) = "case"
+ │ └── end_keyword_loc: (138,21)-(138,24) = "end"
+ ├── @ CaseMatchNode (location: (139,0)-(139,24))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (139,5)-(139,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (139,5)-(139,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (139,10)-(139,20))
+ │ │ ├── pattern:
+ │ │ │ @ RationalNode (location: (139,13)-(139,15))
+ │ │ │ └── numeric:
+ │ │ │ @ IntegerNode (location: (139,13)-(139,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (139,10)-(139,12) = "in"
+ │ │ └── then_loc: (139,16)-(139,20) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (139,0)-(139,4) = "case"
+ │ └── end_keyword_loc: (139,21)-(139,24) = "end"
+ ├── @ CaseMatchNode (location: (140,0)-(140,26))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (140,5)-(140,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (140,5)-(140,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (140,10)-(140,22))
+ │ │ ├── pattern:
+ │ │ │ @ SymbolNode (location: (140,13)-(140,17))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (140,13)-(140,14) = ":"
+ │ │ │ ├── value_loc: (140,14)-(140,17) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (140,10)-(140,12) = "in"
+ │ │ └── then_loc: (140,18)-(140,22) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (140,0)-(140,4) = "case"
+ │ └── end_keyword_loc: (140,23)-(140,26) = "end"
+ ├── @ CaseMatchNode (location: (141,0)-(141,29))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (141,5)-(141,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (141,5)-(141,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (141,10)-(141,25))
+ │ │ ├── pattern:
+ │ │ │ @ SymbolNode (location: (141,13)-(141,20))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (141,13)-(141,16) = "%s["
+ │ │ │ ├── value_loc: (141,16)-(141,19) = "foo"
+ │ │ │ ├── closing_loc: (141,19)-(141,20) = "]"
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (141,10)-(141,12) = "in"
+ │ │ └── then_loc: (141,21)-(141,25) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (141,0)-(141,4) = "case"
+ │ └── end_keyword_loc: (141,26)-(141,29) = "end"
+ ├── @ CaseMatchNode (location: (142,0)-(142,28))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (142,5)-(142,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (142,5)-(142,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (142,10)-(142,24))
+ │ │ ├── pattern:
+ │ │ │ @ SymbolNode (location: (142,13)-(142,19))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (142,13)-(142,15) = ":\""
+ │ │ │ ├── value_loc: (142,15)-(142,18) = "foo"
+ │ │ │ ├── closing_loc: (142,18)-(142,19) = "\""
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (142,10)-(142,12) = "in"
+ │ │ └── then_loc: (142,20)-(142,24) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (142,0)-(142,4) = "case"
+ │ └── end_keyword_loc: (142,25)-(142,28) = "end"
+ ├── @ CaseMatchNode (location: (143,0)-(143,27))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (143,5)-(143,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (143,5)-(143,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (143,10)-(143,23))
+ │ │ ├── pattern:
+ │ │ │ @ RegularExpressionNode (location: (143,13)-(143,18))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (143,13)-(143,14) = "/"
+ │ │ │ ├── content_loc: (143,14)-(143,17) = "foo"
+ │ │ │ ├── closing_loc: (143,17)-(143,18) = "/"
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (143,10)-(143,12) = "in"
+ │ │ └── then_loc: (143,19)-(143,23) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (143,0)-(143,4) = "case"
+ │ └── end_keyword_loc: (143,24)-(143,27) = "end"
+ ├── @ CaseMatchNode (location: (144,0)-(144,27))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (144,5)-(144,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (144,5)-(144,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (144,10)-(144,23))
+ │ │ ├── pattern:
+ │ │ │ @ XStringNode (location: (144,13)-(144,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (144,13)-(144,14) = "`"
+ │ │ │ ├── content_loc: (144,14)-(144,17) = "foo"
+ │ │ │ ├── closing_loc: (144,17)-(144,18) = "`"
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (144,10)-(144,12) = "in"
+ │ │ └── then_loc: (144,19)-(144,23) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (144,0)-(144,4) = "case"
+ │ └── end_keyword_loc: (144,24)-(144,27) = "end"
+ ├── @ CaseMatchNode (location: (145,0)-(145,29))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (145,5)-(145,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (145,5)-(145,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (145,10)-(145,25))
+ │ │ ├── pattern:
+ │ │ │ @ XStringNode (location: (145,13)-(145,20))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (145,13)-(145,16) = "%x["
+ │ │ │ ├── content_loc: (145,16)-(145,19) = "foo"
+ │ │ │ ├── closing_loc: (145,19)-(145,20) = "]"
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (145,10)-(145,12) = "in"
+ │ │ └── then_loc: (145,21)-(145,25) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (145,0)-(145,4) = "case"
+ │ └── end_keyword_loc: (145,26)-(145,29) = "end"
+ ├── @ CaseMatchNode (location: (146,0)-(146,29))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (146,5)-(146,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (146,5)-(146,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (146,10)-(146,25))
+ │ │ ├── pattern:
+ │ │ │ @ ArrayNode (location: (146,13)-(146,20))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ └── @ SymbolNode (location: (146,16)-(146,19))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (146,16)-(146,19) = "foo"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── opening_loc: (146,13)-(146,16) = "%i["
+ │ │ │ └── closing_loc: (146,19)-(146,20) = "]"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (146,10)-(146,12) = "in"
+ │ │ └── then_loc: (146,21)-(146,25) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (146,0)-(146,4) = "case"
+ │ └── end_keyword_loc: (146,26)-(146,29) = "end"
+ ├── @ CaseMatchNode (location: (147,0)-(147,29))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (147,5)-(147,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (147,5)-(147,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (147,10)-(147,25))
+ │ │ ├── pattern:
+ │ │ │ @ ArrayNode (location: (147,13)-(147,20))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ └── @ SymbolNode (location: (147,16)-(147,19))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (147,16)-(147,19) = "foo"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── opening_loc: (147,13)-(147,16) = "%I["
+ │ │ │ └── closing_loc: (147,19)-(147,20) = "]"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (147,10)-(147,12) = "in"
+ │ │ └── then_loc: (147,21)-(147,25) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (147,0)-(147,4) = "case"
+ │ └── end_keyword_loc: (147,26)-(147,29) = "end"
+ ├── @ CaseMatchNode (location: (148,0)-(148,29))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (148,5)-(148,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (148,5)-(148,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (148,10)-(148,25))
+ │ │ ├── pattern:
+ │ │ │ @ ArrayNode (location: (148,13)-(148,20))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ └── @ StringNode (location: (148,16)-(148,19))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (148,16)-(148,19) = "foo"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── opening_loc: (148,13)-(148,16) = "%w["
+ │ │ │ └── closing_loc: (148,19)-(148,20) = "]"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (148,10)-(148,12) = "in"
+ │ │ └── then_loc: (148,21)-(148,25) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (148,0)-(148,4) = "case"
+ │ └── end_keyword_loc: (148,26)-(148,29) = "end"
+ ├── @ CaseMatchNode (location: (149,0)-(149,29))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (149,5)-(149,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (149,5)-(149,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (149,10)-(149,25))
+ │ │ ├── pattern:
+ │ │ │ @ ArrayNode (location: (149,13)-(149,20))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ └── @ StringNode (location: (149,16)-(149,19))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (149,16)-(149,19) = "foo"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── opening_loc: (149,13)-(149,16) = "%W["
+ │ │ │ └── closing_loc: (149,19)-(149,20) = "]"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (149,10)-(149,12) = "in"
+ │ │ └── then_loc: (149,21)-(149,25) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (149,0)-(149,4) = "case"
+ │ └── end_keyword_loc: (149,26)-(149,29) = "end"
+ ├── @ CaseMatchNode (location: (150,0)-(150,29))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (150,5)-(150,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (150,5)-(150,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (150,10)-(150,25))
+ │ │ ├── pattern:
+ │ │ │ @ StringNode (location: (150,13)-(150,20))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (150,13)-(150,16) = "%q["
+ │ │ │ ├── content_loc: (150,16)-(150,19) = "foo"
+ │ │ │ ├── closing_loc: (150,19)-(150,20) = "]"
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (150,10)-(150,12) = "in"
+ │ │ └── then_loc: (150,21)-(150,25) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (150,0)-(150,4) = "case"
+ │ └── end_keyword_loc: (150,26)-(150,29) = "end"
+ ├── @ CaseMatchNode (location: (151,0)-(151,29))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (151,5)-(151,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (151,5)-(151,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (151,10)-(151,25))
+ │ │ ├── pattern:
+ │ │ │ @ StringNode (location: (151,13)-(151,20))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (151,13)-(151,16) = "%Q["
+ │ │ │ ├── content_loc: (151,16)-(151,19) = "foo"
+ │ │ │ ├── closing_loc: (151,19)-(151,20) = "]"
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (151,10)-(151,12) = "in"
+ │ │ └── then_loc: (151,21)-(151,25) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (151,0)-(151,4) = "case"
+ │ └── end_keyword_loc: (151,26)-(151,29) = "end"
+ ├── @ CaseMatchNode (location: (152,0)-(152,27))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (152,5)-(152,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (152,5)-(152,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (152,10)-(152,23))
+ │ │ ├── pattern:
+ │ │ │ @ StringNode (location: (152,13)-(152,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (152,13)-(152,14) = "\""
+ │ │ │ ├── content_loc: (152,14)-(152,17) = "foo"
+ │ │ │ ├── closing_loc: (152,17)-(152,18) = "\""
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (152,10)-(152,12) = "in"
+ │ │ └── then_loc: (152,19)-(152,23) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (152,0)-(152,4) = "case"
+ │ └── end_keyword_loc: (152,24)-(152,27) = "end"
+ ├── @ CaseMatchNode (location: (153,0)-(153,25))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (153,5)-(153,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (153,5)-(153,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (153,10)-(153,21))
+ │ │ ├── pattern:
+ │ │ │ @ NilNode (location: (153,13)-(153,16))
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (153,10)-(153,12) = "in"
+ │ │ └── then_loc: (153,17)-(153,21) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (153,0)-(153,4) = "case"
+ │ └── end_keyword_loc: (153,22)-(153,25) = "end"
+ ├── @ CaseMatchNode (location: (154,0)-(154,26))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (154,5)-(154,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (154,5)-(154,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (154,10)-(154,22))
+ │ │ ├── pattern:
+ │ │ │ @ SelfNode (location: (154,13)-(154,17))
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (154,10)-(154,12) = "in"
+ │ │ └── then_loc: (154,18)-(154,22) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (154,0)-(154,4) = "case"
+ │ └── end_keyword_loc: (154,23)-(154,26) = "end"
+ ├── @ CaseMatchNode (location: (155,0)-(155,26))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (155,5)-(155,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (155,5)-(155,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (155,10)-(155,22))
+ │ │ ├── pattern:
+ │ │ │ @ TrueNode (location: (155,13)-(155,17))
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (155,10)-(155,12) = "in"
+ │ │ └── then_loc: (155,18)-(155,22) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (155,0)-(155,4) = "case"
+ │ └── end_keyword_loc: (155,23)-(155,26) = "end"
+ ├── @ CaseMatchNode (location: (156,0)-(156,27))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (156,5)-(156,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (156,5)-(156,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (156,10)-(156,23))
+ │ │ ├── pattern:
+ │ │ │ @ FalseNode (location: (156,13)-(156,18))
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (156,10)-(156,12) = "in"
+ │ │ └── then_loc: (156,19)-(156,23) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (156,0)-(156,4) = "case"
+ │ └── end_keyword_loc: (156,24)-(156,27) = "end"
+ ├── @ CaseMatchNode (location: (157,0)-(157,30))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (157,5)-(157,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (157,5)-(157,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (157,10)-(157,26))
+ │ │ ├── pattern:
+ │ │ │ @ SourceFileNode (location: (157,13)-(157,21))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── filepath: "patterns.txt"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (157,10)-(157,12) = "in"
+ │ │ └── then_loc: (157,22)-(157,26) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (157,0)-(157,4) = "case"
+ │ └── end_keyword_loc: (157,27)-(157,30) = "end"
+ ├── @ CaseMatchNode (location: (158,0)-(158,30))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (158,5)-(158,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (158,5)-(158,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (158,10)-(158,26))
+ │ │ ├── pattern:
+ │ │ │ @ SourceLineNode (location: (158,13)-(158,21))
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (158,10)-(158,12) = "in"
+ │ │ └── then_loc: (158,22)-(158,26) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (158,0)-(158,4) = "case"
+ │ └── end_keyword_loc: (158,27)-(158,30) = "end"
+ ├── @ CaseMatchNode (location: (159,0)-(159,34))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (159,5)-(159,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (159,5)-(159,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (159,10)-(159,30))
+ │ │ ├── pattern:
+ │ │ │ @ SourceEncodingNode (location: (159,13)-(159,25))
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (159,10)-(159,12) = "in"
+ │ │ └── then_loc: (159,26)-(159,30) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (159,0)-(159,4) = "case"
+ │ └── end_keyword_loc: (159,31)-(159,34) = "end"
+ ├── @ CaseMatchNode (location: (160,0)-(160,32))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (160,5)-(160,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (160,5)-(160,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (160,10)-(160,28))
+ │ │ ├── pattern:
+ │ │ │ @ LambdaNode (location: (160,13)-(160,23))
+ │ │ │ ├── locals: []
+ │ │ │ ├── operator_loc: (160,13)-(160,15) = "->"
+ │ │ │ ├── opening_loc: (160,16)-(160,17) = "{"
+ │ │ │ ├── closing_loc: (160,22)-(160,23) = "}"
+ │ │ │ ├── parameters: ∅
+ │ │ │ └── body:
+ │ │ │ @ StatementsNode (location: (160,18)-(160,21))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ LocalVariableReadNode (location: (160,18)-(160,21))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 1
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (160,10)-(160,12) = "in"
+ │ │ └── then_loc: (160,24)-(160,28) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (160,0)-(160,4) = "case"
+ │ └── end_keyword_loc: (160,29)-(160,32) = "end"
+ ├── @ CaseMatchNode (location: (162,0)-(162,32))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (162,5)-(162,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (162,5)-(162,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (162,10)-(162,28))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (162,13)-(162,23))
+ │ │ │ ├── if_keyword_loc: (162,17)-(162,19) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (162,20)-(162,23))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (162,13)-(162,16))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ LocalVariableTargetNode (location: (162,13)-(162,16))
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (162,10)-(162,12) = "in"
+ │ │ └── then_loc: (162,24)-(162,28) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (162,0)-(162,4) = "case"
+ │ └── end_keyword_loc: (162,29)-(162,32) = "end"
+ ├── @ CaseMatchNode (location: (163,0)-(163,30))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (163,5)-(163,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (163,5)-(163,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (163,10)-(163,26))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (163,13)-(163,21))
+ │ │ │ ├── if_keyword_loc: (163,15)-(163,17) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (163,18)-(163,21))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (163,13)-(163,14))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (163,13)-(163,14))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (163,10)-(163,12) = "in"
+ │ │ └── then_loc: (163,22)-(163,26) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (163,0)-(163,4) = "case"
+ │ └── end_keyword_loc: (163,27)-(163,30) = "end"
+ ├── @ CaseMatchNode (location: (164,0)-(164,32))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (164,5)-(164,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (164,5)-(164,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (164,10)-(164,28))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (164,13)-(164,23))
+ │ │ │ ├── if_keyword_loc: (164,17)-(164,19) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (164,20)-(164,23))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (164,13)-(164,16))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ FloatNode (location: (164,13)-(164,16))
+ │ │ │ │ └── value: 1.0
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (164,10)-(164,12) = "in"
+ │ │ └── then_loc: (164,24)-(164,28) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (164,0)-(164,4) = "case"
+ │ └── end_keyword_loc: (164,29)-(164,32) = "end"
+ ├── @ CaseMatchNode (location: (165,0)-(165,31))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (165,5)-(165,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (165,5)-(165,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (165,10)-(165,27))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (165,13)-(165,22))
+ │ │ │ ├── if_keyword_loc: (165,16)-(165,18) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (165,19)-(165,22))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (165,13)-(165,15))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ ImaginaryNode (location: (165,13)-(165,15))
+ │ │ │ │ └── numeric:
+ │ │ │ │ @ IntegerNode (location: (165,13)-(165,14))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (165,10)-(165,12) = "in"
+ │ │ └── then_loc: (165,23)-(165,27) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (165,0)-(165,4) = "case"
+ │ └── end_keyword_loc: (165,28)-(165,31) = "end"
+ ├── @ CaseMatchNode (location: (166,0)-(166,31))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (166,5)-(166,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (166,5)-(166,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (166,10)-(166,27))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (166,13)-(166,22))
+ │ │ │ ├── if_keyword_loc: (166,16)-(166,18) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (166,19)-(166,22))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (166,13)-(166,15))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ RationalNode (location: (166,13)-(166,15))
+ │ │ │ │ └── numeric:
+ │ │ │ │ @ IntegerNode (location: (166,13)-(166,14))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (166,10)-(166,12) = "in"
+ │ │ └── then_loc: (166,23)-(166,27) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (166,0)-(166,4) = "case"
+ │ └── end_keyword_loc: (166,28)-(166,31) = "end"
+ ├── @ CaseMatchNode (location: (167,0)-(167,33))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (167,5)-(167,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (167,5)-(167,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (167,10)-(167,29))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (167,13)-(167,24))
+ │ │ │ ├── if_keyword_loc: (167,18)-(167,20) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (167,21)-(167,24))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (167,13)-(167,17))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ SymbolNode (location: (167,13)-(167,17))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (167,13)-(167,14) = ":"
+ │ │ │ │ ├── value_loc: (167,14)-(167,17) = "foo"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (167,10)-(167,12) = "in"
+ │ │ └── then_loc: (167,25)-(167,29) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (167,0)-(167,4) = "case"
+ │ └── end_keyword_loc: (167,30)-(167,33) = "end"
+ ├── @ CaseMatchNode (location: (168,0)-(168,36))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (168,5)-(168,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (168,5)-(168,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (168,10)-(168,32))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (168,13)-(168,27))
+ │ │ │ ├── if_keyword_loc: (168,21)-(168,23) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (168,24)-(168,27))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (168,13)-(168,20))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ SymbolNode (location: (168,13)-(168,20))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (168,13)-(168,16) = "%s["
+ │ │ │ │ ├── value_loc: (168,16)-(168,19) = "foo"
+ │ │ │ │ ├── closing_loc: (168,19)-(168,20) = "]"
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (168,10)-(168,12) = "in"
+ │ │ └── then_loc: (168,28)-(168,32) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (168,0)-(168,4) = "case"
+ │ └── end_keyword_loc: (168,33)-(168,36) = "end"
+ ├── @ CaseMatchNode (location: (169,0)-(169,35))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (169,5)-(169,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (169,5)-(169,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (169,10)-(169,31))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (169,13)-(169,26))
+ │ │ │ ├── if_keyword_loc: (169,20)-(169,22) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (169,23)-(169,26))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (169,13)-(169,19))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ SymbolNode (location: (169,13)-(169,19))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (169,13)-(169,15) = ":\""
+ │ │ │ │ ├── value_loc: (169,15)-(169,18) = "foo"
+ │ │ │ │ ├── closing_loc: (169,18)-(169,19) = "\""
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (169,10)-(169,12) = "in"
+ │ │ └── then_loc: (169,27)-(169,31) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (169,0)-(169,4) = "case"
+ │ └── end_keyword_loc: (169,32)-(169,35) = "end"
+ ├── @ CaseMatchNode (location: (170,0)-(170,34))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (170,5)-(170,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (170,5)-(170,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (170,10)-(170,30))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (170,13)-(170,25))
+ │ │ │ ├── if_keyword_loc: (170,19)-(170,21) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (170,22)-(170,25))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (170,13)-(170,18))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ RegularExpressionNode (location: (170,13)-(170,18))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (170,13)-(170,14) = "/"
+ │ │ │ │ ├── content_loc: (170,14)-(170,17) = "foo"
+ │ │ │ │ ├── closing_loc: (170,17)-(170,18) = "/"
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (170,10)-(170,12) = "in"
+ │ │ └── then_loc: (170,26)-(170,30) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (170,0)-(170,4) = "case"
+ │ └── end_keyword_loc: (170,31)-(170,34) = "end"
+ ├── @ CaseMatchNode (location: (171,0)-(171,34))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (171,5)-(171,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (171,5)-(171,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (171,10)-(171,30))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (171,13)-(171,25))
+ │ │ │ ├── if_keyword_loc: (171,19)-(171,21) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (171,22)-(171,25))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (171,13)-(171,18))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ XStringNode (location: (171,13)-(171,18))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: (171,13)-(171,14) = "`"
+ │ │ │ │ ├── content_loc: (171,14)-(171,17) = "foo"
+ │ │ │ │ ├── closing_loc: (171,17)-(171,18) = "`"
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (171,10)-(171,12) = "in"
+ │ │ └── then_loc: (171,26)-(171,30) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (171,0)-(171,4) = "case"
+ │ └── end_keyword_loc: (171,31)-(171,34) = "end"
+ ├── @ CaseMatchNode (location: (172,0)-(172,36))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (172,5)-(172,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (172,5)-(172,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (172,10)-(172,32))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (172,13)-(172,27))
+ │ │ │ ├── if_keyword_loc: (172,21)-(172,23) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (172,24)-(172,27))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (172,13)-(172,20))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ XStringNode (location: (172,13)-(172,20))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: (172,13)-(172,16) = "%x["
+ │ │ │ │ ├── content_loc: (172,16)-(172,19) = "foo"
+ │ │ │ │ ├── closing_loc: (172,19)-(172,20) = "]"
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (172,10)-(172,12) = "in"
+ │ │ └── then_loc: (172,28)-(172,32) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (172,0)-(172,4) = "case"
+ │ └── end_keyword_loc: (172,33)-(172,36) = "end"
+ ├── @ CaseMatchNode (location: (173,0)-(173,36))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (173,5)-(173,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (173,5)-(173,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (173,10)-(173,32))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (173,13)-(173,27))
+ │ │ │ ├── if_keyword_loc: (173,21)-(173,23) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (173,24)-(173,27))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (173,13)-(173,20))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ ArrayNode (location: (173,13)-(173,20))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ │ └── @ SymbolNode (location: (173,16)-(173,19))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── value_loc: (173,16)-(173,19) = "foo"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "foo"
+ │ │ │ │ ├── opening_loc: (173,13)-(173,16) = "%i["
+ │ │ │ │ └── closing_loc: (173,19)-(173,20) = "]"
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (173,10)-(173,12) = "in"
+ │ │ └── then_loc: (173,28)-(173,32) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (173,0)-(173,4) = "case"
+ │ └── end_keyword_loc: (173,33)-(173,36) = "end"
+ ├── @ CaseMatchNode (location: (174,0)-(174,36))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (174,5)-(174,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (174,5)-(174,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (174,10)-(174,32))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (174,13)-(174,27))
+ │ │ │ ├── if_keyword_loc: (174,21)-(174,23) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (174,24)-(174,27))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (174,13)-(174,20))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ ArrayNode (location: (174,13)-(174,20))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ │ └── @ SymbolNode (location: (174,16)-(174,19))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── value_loc: (174,16)-(174,19) = "foo"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "foo"
+ │ │ │ │ ├── opening_loc: (174,13)-(174,16) = "%I["
+ │ │ │ │ └── closing_loc: (174,19)-(174,20) = "]"
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (174,10)-(174,12) = "in"
+ │ │ └── then_loc: (174,28)-(174,32) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (174,0)-(174,4) = "case"
+ │ └── end_keyword_loc: (174,33)-(174,36) = "end"
+ ├── @ CaseMatchNode (location: (175,0)-(175,36))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (175,5)-(175,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (175,5)-(175,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (175,10)-(175,32))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (175,13)-(175,27))
+ │ │ │ ├── if_keyword_loc: (175,21)-(175,23) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (175,24)-(175,27))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (175,13)-(175,20))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ ArrayNode (location: (175,13)-(175,20))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ │ └── @ StringNode (location: (175,16)-(175,19))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── content_loc: (175,16)-(175,19) = "foo"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "foo"
+ │ │ │ │ ├── opening_loc: (175,13)-(175,16) = "%w["
+ │ │ │ │ └── closing_loc: (175,19)-(175,20) = "]"
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (175,10)-(175,12) = "in"
+ │ │ └── then_loc: (175,28)-(175,32) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (175,0)-(175,4) = "case"
+ │ └── end_keyword_loc: (175,33)-(175,36) = "end"
+ ├── @ CaseMatchNode (location: (176,0)-(176,36))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (176,5)-(176,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (176,5)-(176,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (176,10)-(176,32))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (176,13)-(176,27))
+ │ │ │ ├── if_keyword_loc: (176,21)-(176,23) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (176,24)-(176,27))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (176,13)-(176,20))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ ArrayNode (location: (176,13)-(176,20))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ │ └── @ StringNode (location: (176,16)-(176,19))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── content_loc: (176,16)-(176,19) = "foo"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "foo"
+ │ │ │ │ ├── opening_loc: (176,13)-(176,16) = "%W["
+ │ │ │ │ └── closing_loc: (176,19)-(176,20) = "]"
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (176,10)-(176,12) = "in"
+ │ │ └── then_loc: (176,28)-(176,32) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (176,0)-(176,4) = "case"
+ │ └── end_keyword_loc: (176,33)-(176,36) = "end"
+ ├── @ CaseMatchNode (location: (177,0)-(177,36))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (177,5)-(177,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (177,5)-(177,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (177,10)-(177,32))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (177,13)-(177,27))
+ │ │ │ ├── if_keyword_loc: (177,21)-(177,23) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (177,24)-(177,27))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (177,13)-(177,20))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ StringNode (location: (177,13)-(177,20))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: (177,13)-(177,16) = "%q["
+ │ │ │ │ ├── content_loc: (177,16)-(177,19) = "foo"
+ │ │ │ │ ├── closing_loc: (177,19)-(177,20) = "]"
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (177,10)-(177,12) = "in"
+ │ │ └── then_loc: (177,28)-(177,32) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (177,0)-(177,4) = "case"
+ │ └── end_keyword_loc: (177,33)-(177,36) = "end"
+ ├── @ CaseMatchNode (location: (178,0)-(178,36))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (178,5)-(178,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (178,5)-(178,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (178,10)-(178,32))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (178,13)-(178,27))
+ │ │ │ ├── if_keyword_loc: (178,21)-(178,23) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (178,24)-(178,27))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (178,13)-(178,20))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ StringNode (location: (178,13)-(178,20))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: (178,13)-(178,16) = "%Q["
+ │ │ │ │ ├── content_loc: (178,16)-(178,19) = "foo"
+ │ │ │ │ ├── closing_loc: (178,19)-(178,20) = "]"
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (178,10)-(178,12) = "in"
+ │ │ └── then_loc: (178,28)-(178,32) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (178,0)-(178,4) = "case"
+ │ └── end_keyword_loc: (178,33)-(178,36) = "end"
+ ├── @ CaseMatchNode (location: (179,0)-(179,34))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (179,5)-(179,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (179,5)-(179,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (179,10)-(179,30))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (179,13)-(179,25))
+ │ │ │ ├── if_keyword_loc: (179,19)-(179,21) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (179,22)-(179,25))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (179,13)-(179,18))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ StringNode (location: (179,13)-(179,18))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: (179,13)-(179,14) = "\""
+ │ │ │ │ ├── content_loc: (179,14)-(179,17) = "foo"
+ │ │ │ │ ├── closing_loc: (179,17)-(179,18) = "\""
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (179,10)-(179,12) = "in"
+ │ │ └── then_loc: (179,26)-(179,30) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (179,0)-(179,4) = "case"
+ │ └── end_keyword_loc: (179,31)-(179,34) = "end"
+ ├── @ CaseMatchNode (location: (180,0)-(180,32))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (180,5)-(180,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (180,5)-(180,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (180,10)-(180,28))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (180,13)-(180,23))
+ │ │ │ ├── if_keyword_loc: (180,17)-(180,19) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (180,20)-(180,23))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (180,13)-(180,16))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ NilNode (location: (180,13)-(180,16))
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (180,10)-(180,12) = "in"
+ │ │ └── then_loc: (180,24)-(180,28) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (180,0)-(180,4) = "case"
+ │ └── end_keyword_loc: (180,29)-(180,32) = "end"
+ ├── @ CaseMatchNode (location: (181,0)-(181,33))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (181,5)-(181,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (181,5)-(181,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (181,10)-(181,29))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (181,13)-(181,24))
+ │ │ │ ├── if_keyword_loc: (181,18)-(181,20) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (181,21)-(181,24))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (181,13)-(181,17))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ SelfNode (location: (181,13)-(181,17))
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (181,10)-(181,12) = "in"
+ │ │ └── then_loc: (181,25)-(181,29) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (181,0)-(181,4) = "case"
+ │ └── end_keyword_loc: (181,30)-(181,33) = "end"
+ ├── @ CaseMatchNode (location: (182,0)-(182,33))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (182,5)-(182,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (182,5)-(182,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (182,10)-(182,29))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (182,13)-(182,24))
+ │ │ │ ├── if_keyword_loc: (182,18)-(182,20) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (182,21)-(182,24))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (182,13)-(182,17))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ TrueNode (location: (182,13)-(182,17))
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (182,10)-(182,12) = "in"
+ │ │ └── then_loc: (182,25)-(182,29) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (182,0)-(182,4) = "case"
+ │ └── end_keyword_loc: (182,30)-(182,33) = "end"
+ ├── @ CaseMatchNode (location: (183,0)-(183,34))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (183,5)-(183,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (183,5)-(183,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (183,10)-(183,30))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (183,13)-(183,25))
+ │ │ │ ├── if_keyword_loc: (183,19)-(183,21) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (183,22)-(183,25))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (183,13)-(183,18))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ FalseNode (location: (183,13)-(183,18))
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (183,10)-(183,12) = "in"
+ │ │ └── then_loc: (183,26)-(183,30) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (183,0)-(183,4) = "case"
+ │ └── end_keyword_loc: (183,31)-(183,34) = "end"
+ ├── @ CaseMatchNode (location: (184,0)-(184,37))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (184,5)-(184,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (184,5)-(184,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (184,10)-(184,33))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (184,13)-(184,28))
+ │ │ │ ├── if_keyword_loc: (184,22)-(184,24) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (184,25)-(184,28))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (184,13)-(184,21))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ SourceFileNode (location: (184,13)-(184,21))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── filepath: "patterns.txt"
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (184,10)-(184,12) = "in"
+ │ │ └── then_loc: (184,29)-(184,33) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (184,0)-(184,4) = "case"
+ │ └── end_keyword_loc: (184,34)-(184,37) = "end"
+ ├── @ CaseMatchNode (location: (185,0)-(185,37))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (185,5)-(185,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (185,5)-(185,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (185,10)-(185,33))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (185,13)-(185,28))
+ │ │ │ ├── if_keyword_loc: (185,22)-(185,24) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (185,25)-(185,28))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (185,13)-(185,21))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ SourceLineNode (location: (185,13)-(185,21))
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (185,10)-(185,12) = "in"
+ │ │ └── then_loc: (185,29)-(185,33) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (185,0)-(185,4) = "case"
+ │ └── end_keyword_loc: (185,34)-(185,37) = "end"
+ ├── @ CaseMatchNode (location: (186,0)-(186,41))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (186,5)-(186,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (186,5)-(186,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (186,10)-(186,37))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (186,13)-(186,32))
+ │ │ │ ├── if_keyword_loc: (186,26)-(186,28) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (186,29)-(186,32))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (186,13)-(186,25))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ SourceEncodingNode (location: (186,13)-(186,25))
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (186,10)-(186,12) = "in"
+ │ │ └── then_loc: (186,33)-(186,37) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (186,0)-(186,4) = "case"
+ │ └── end_keyword_loc: (186,38)-(186,41) = "end"
+ ├── @ CaseMatchNode (location: (187,0)-(187,39))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (187,5)-(187,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (187,5)-(187,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (187,10)-(187,35))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (187,13)-(187,30))
+ │ │ │ ├── if_keyword_loc: (187,24)-(187,26) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (187,27)-(187,30))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (187,13)-(187,23))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ LambdaNode (location: (187,13)-(187,23))
+ │ │ │ │ ├── locals: []
+ │ │ │ │ ├── operator_loc: (187,13)-(187,15) = "->"
+ │ │ │ │ ├── opening_loc: (187,16)-(187,17) = "{"
+ │ │ │ │ ├── closing_loc: (187,22)-(187,23) = "}"
+ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ └── body:
+ │ │ │ │ @ StatementsNode (location: (187,18)-(187,21))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ LocalVariableReadNode (location: (187,18)-(187,21))
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ └── depth: 1
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (187,10)-(187,12) = "in"
+ │ │ └── then_loc: (187,31)-(187,35) = "then"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (187,0)-(187,4) = "case"
+ │ └── end_keyword_loc: (187,36)-(187,39) = "end"
+ ├── @ IfNode (location: (189,0)-(190,3))
+ │ ├── if_keyword_loc: (189,0)-(189,2) = "if"
+ │ ├── predicate:
+ │ │ @ MatchPredicateNode (location: (189,3)-(189,10))
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (189,3)-(189,4))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (189,3)-(189,4) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── pattern:
+ │ │ │ @ ArrayPatternNode (location: (189,8)-(189,10))
+ │ │ │ ├── constant: ∅
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── opening_loc: (189,8)-(189,9) = "["
+ │ │ │ └── closing_loc: (189,9)-(189,10) = "]"
+ │ │ └── operator_loc: (189,5)-(189,7) = "in"
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements: ∅
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (190,0)-(190,3) = "end"
+ ├── @ MatchRequiredNode (location: (192,0)-(194,1))
+ │ ├── value:
+ │ │ @ CallNode (location: (192,0)-(192,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (192,0)-(192,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (192,5)-(194,1))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (193,2)-(193,3))
+ │ │ │ ├── name: :b
+ │ │ │ └── depth: 0
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (192,5)-(192,6) = "["
+ │ │ └── closing_loc: (194,0)-(194,1) = "]"
+ │ └── operator_loc: (192,2)-(192,4) = "=>"
+ ├── @ MatchPredicateNode (location: (196,0)-(200,1))
+ │ ├── value:
+ │ │ @ CallNode (location: (196,0)-(196,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (196,0)-(196,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ HashPatternNode (location: (196,7)-(200,1))
+ │ │ ├── constant:
+ │ │ │ @ ConstantReadNode (location: (196,7)-(196,8))
+ │ │ │ └── name: :A
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (197,2)-(199,3))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (197,2)-(197,6))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (197,2)-(197,5) = "bar"
+ │ │ │ │ ├── closing_loc: (197,5)-(197,6) = ":"
+ │ │ │ │ └── unescaped: "bar"
+ │ │ │ ├── value:
+ │ │ │ │ @ HashPatternNode (location: (197,7)-(199,3))
+ │ │ │ │ ├── constant:
+ │ │ │ │ │ @ ConstantReadNode (location: (197,7)-(197,8))
+ │ │ │ │ │ └── name: :B
+ │ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ │ └── @ AssocNode (location: (198,4)-(198,12))
+ │ │ │ │ │ ├── key:
+ │ │ │ │ │ │ @ SymbolNode (location: (198,4)-(198,10))
+ │ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ │ ├── value_loc: (198,4)-(198,9) = "value"
+ │ │ │ │ │ │ ├── closing_loc: (198,9)-(198,10) = ":"
+ │ │ │ │ │ │ └── unescaped: "value"
+ │ │ │ │ │ ├── value:
+ │ │ │ │ │ │ @ LocalVariableTargetNode (location: (198,11)-(198,12))
+ │ │ │ │ │ │ ├── name: :a
+ │ │ │ │ │ │ └── depth: 0
+ │ │ │ │ │ └── operator_loc: ∅
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── opening_loc: (197,8)-(197,9) = "["
+ │ │ │ │ └── closing_loc: (199,2)-(199,3) = "]"
+ │ │ │ └── operator_loc: ∅
+ │ │ ├── rest: ∅
+ │ │ ├── opening_loc: (196,8)-(196,9) = "["
+ │ │ └── closing_loc: (200,0)-(200,1) = "]"
+ │ └── operator_loc: (196,4)-(196,6) = "in"
+ ├── @ MatchPredicateNode (location: (202,0)-(202,17))
+ │ ├── value:
+ │ │ @ CallNode (location: (202,0)-(202,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (202,0)-(202,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ CapturePatternNode (location: (202,7)-(202,17))
+ │ │ ├── value:
+ │ │ │ @ LocalVariableTargetNode (location: (202,7)-(202,10))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── target:
+ │ │ │ @ LocalVariableTargetNode (location: (202,14)-(202,17))
+ │ │ │ ├── name: :baz
+ │ │ │ └── depth: 0
+ │ │ └── operator_loc: (202,11)-(202,13) = "=>"
+ │ └── operator_loc: (202,4)-(202,6) = "in"
+ ├── @ MatchRequiredNode (location: (203,0)-(203,17))
+ │ ├── value:
+ │ │ @ CallNode (location: (203,0)-(203,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (203,0)-(203,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ CapturePatternNode (location: (203,7)-(203,17))
+ │ │ ├── value:
+ │ │ │ @ LocalVariableTargetNode (location: (203,7)-(203,10))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── target:
+ │ │ │ @ LocalVariableTargetNode (location: (203,14)-(203,17))
+ │ │ │ ├── name: :baz
+ │ │ │ └── depth: 0
+ │ │ └── operator_loc: (203,11)-(203,13) = "=>"
+ │ └── operator_loc: (203,4)-(203,6) = "=>"
+ ├── @ MultiWriteNode (location: (205,0)-(205,20))
+ │ ├── lefts: (length: 3)
+ │ │ ├── @ LocalVariableTargetNode (location: (205,0)-(205,3))
+ │ │ │ ├── name: :foo
+ │ │ │ └── depth: 0
+ │ │ ├── @ LocalVariableTargetNode (location: (205,5)-(205,8))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ └── @ LocalVariableTargetNode (location: (205,10)-(205,13))
+ │ │ ├── name: :baz
+ │ │ └── depth: 0
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (205,14)-(205,15) = "="
+ │ └── value:
+ │ @ ArrayNode (location: (205,16)-(205,20))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (205,16)-(205,17))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (205,19)-(205,20))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── opening_loc: ∅
+ │ └── closing_loc: ∅
+ ├── @ CallNode (location: (206,0)-(208,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (206,0)-(206,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (206,4)-(208,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (207,2)-(207,29))
+ │ │ └── body: (length: 1)
+ │ │ └── @ MatchRequiredNode (location: (207,2)-(207,29))
+ │ │ ├── value:
+ │ │ │ @ ArrayNode (location: (207,2)-(207,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 2)
+ │ │ │ │ ├── @ IntegerNode (location: (207,3)-(207,4))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ └── @ IntegerNode (location: (207,6)-(207,7))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ ├── opening_loc: (207,2)-(207,3) = "["
+ │ │ │ └── closing_loc: (207,7)-(207,8) = "]"
+ │ │ ├── pattern:
+ │ │ │ @ CapturePatternNode (location: (207,12)-(207,29))
+ │ │ │ ├── value:
+ │ │ │ │ @ ArrayPatternNode (location: (207,12)-(207,22))
+ │ │ │ │ ├── constant: ∅
+ │ │ │ │ ├── requireds: (length: 2)
+ │ │ │ │ │ ├── @ LocalVariableTargetNode (location: (207,13)-(207,16))
+ │ │ │ │ │ │ ├── name: :foo
+ │ │ │ │ │ │ └── depth: 1
+ │ │ │ │ │ └── @ LocalVariableTargetNode (location: (207,18)-(207,21))
+ │ │ │ │ │ ├── name: :bar
+ │ │ │ │ │ └── depth: 1
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ ├── opening_loc: (207,12)-(207,13) = "["
+ │ │ │ │ └── closing_loc: (207,21)-(207,22) = "]"
+ │ │ │ ├── target:
+ │ │ │ │ @ LocalVariableTargetNode (location: (207,26)-(207,29))
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ └── depth: 1
+ │ │ │ └── operator_loc: (207,23)-(207,25) = "=>"
+ │ │ └── operator_loc: (207,9)-(207,11) = "=>"
+ │ ├── opening_loc: (206,4)-(206,6) = "do"
+ │ └── closing_loc: (208,0)-(208,3) = "end"
+ ├── @ MatchRequiredNode (location: (210,0)-(210,19))
+ │ ├── value:
+ │ │ @ LocalVariableReadNode (location: (210,0)-(210,3))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (210,7)-(210,19))
+ │ │ ├── constant:
+ │ │ │ @ ConstantReadNode (location: (210,7)-(210,13))
+ │ │ │ └── name: :Object
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ HashPatternNode (location: (210,14)-(210,18))
+ │ │ │ ├── constant: ∅
+ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ └── @ AssocNode (location: (210,15)-(210,17))
+ │ │ │ │ ├── key:
+ │ │ │ │ │ @ SymbolNode (location: (210,15)-(210,17))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── value_loc: (210,15)-(210,16) = "x"
+ │ │ │ │ │ ├── closing_loc: (210,16)-(210,17) = ":"
+ │ │ │ │ │ └── unescaped: "x"
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ ImplicitNode (location: (210,15)-(210,16))
+ │ │ │ │ │ └── value:
+ │ │ │ │ │ @ LocalVariableTargetNode (location: (210,15)-(210,16))
+ │ │ │ │ │ ├── name: :x
+ │ │ │ │ │ └── depth: 0
+ │ │ │ │ └── operator_loc: ∅
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── opening_loc: (210,14)-(210,15) = "{"
+ │ │ │ └── closing_loc: (210,17)-(210,18) = "}"
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (210,13)-(210,14) = "["
+ │ │ └── closing_loc: (210,18)-(210,19) = "]"
+ │ └── operator_loc: (210,4)-(210,6) = "=>"
+ ├── @ CallNode (location: (212,0)-(212,19))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (212,0)-(212,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: (212,1)-(212,2) = "."
+ │ ├── name: :then
+ │ ├── message_loc: (212,2)-(212,6) = "then"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (212,7)-(212,19))
+ │ ├── locals: [:_1]
+ │ ├── parameters:
+ │ │ @ NumberedParametersNode (location: (212,7)-(212,19))
+ │ │ └── maximum: 1
+ │ ├── body:
+ │ │ @ StatementsNode (location: (212,9)-(212,17))
+ │ │ └── body: (length: 1)
+ │ │ └── @ MatchPredicateNode (location: (212,9)-(212,17))
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (212,9)-(212,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── pattern:
+ │ │ │ @ PinnedVariableNode (location: (212,14)-(212,17))
+ │ │ │ ├── variable:
+ │ │ │ │ @ LocalVariableReadNode (location: (212,15)-(212,17))
+ │ │ │ │ ├── name: :_1
+ │ │ │ │ └── depth: 0
+ │ │ │ └── operator_loc: (212,14)-(212,15) = "^"
+ │ │ └── operator_loc: (212,11)-(212,13) = "in"
+ │ ├── opening_loc: (212,7)-(212,8) = "{"
+ │ └── closing_loc: (212,18)-(212,19) = "}"
+ └── @ MultiWriteNode (location: (214,0)-(217,5))
+ ├── lefts: (length: 2)
+ │ ├── @ LocalVariableTargetNode (location: (215,2)-(215,3))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ └── @ LocalVariableTargetNode (location: (216,2)-(216,3))
+ │ ├── name: :b
+ │ └── depth: 0
+ ├── rest: ∅
+ ├── rights: (length: 0)
+ ├── lparen_loc: (214,0)-(214,1) = "("
+ ├── rparen_loc: (217,0)-(217,1) = ")"
+ ├── operator_loc: (217,2)-(217,3) = "="
+ └── value:
+ @ CallNode (location: (217,4)-(217,5))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :c
+ ├── message_loc: (217,4)-(217,5) = "c"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/procs.txt b/test/prism/snapshots/procs.txt
new file mode 100644
index 0000000000..1329ae6a5f
--- /dev/null
+++ b/test/prism/snapshots/procs.txt
@@ -0,0 +1,403 @@
+@ ProgramNode (location: (1,0)-(27,19))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(27,19))
+ └── body: (length: 10)
+ ├── @ LambdaNode (location: (1,0)-(1,21))
+ │ ├── locals: [:a, :b, :c, :d]
+ │ ├── operator_loc: (1,0)-(1,2) = "->"
+ │ ├── opening_loc: (1,16)-(1,17) = "{"
+ │ ├── closing_loc: (1,20)-(1,21) = "}"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (1,3)-(1,15))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (1,4)-(1,5))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (1,4)-(1,5))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 3)
+ │ │ │ ├── @ BlockLocalVariableNode (location: (1,7)-(1,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :b
+ │ │ │ ├── @ BlockLocalVariableNode (location: (1,10)-(1,11))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :c
+ │ │ │ └── @ BlockLocalVariableNode (location: (1,13)-(1,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :d
+ │ │ ├── opening_loc: (1,3)-(1,4) = "("
+ │ │ └── closing_loc: (1,14)-(1,15) = ")"
+ │ └── body:
+ │ @ StatementsNode (location: (1,18)-(1,19))
+ │ └── body: (length: 1)
+ │ └── @ LocalVariableReadNode (location: (1,18)-(1,19))
+ │ ├── name: :b
+ │ └── depth: 0
+ ├── @ LambdaNode (location: (3,0)-(5,3))
+ │ ├── locals: []
+ │ ├── operator_loc: (3,0)-(3,2) = "->"
+ │ ├── opening_loc: (3,3)-(3,5) = "do"
+ │ ├── closing_loc: (5,0)-(5,3) = "end"
+ │ ├── parameters: ∅
+ │ └── body:
+ │ @ BeginNode (location: (3,3)-(5,3))
+ │ ├── begin_keyword_loc: ∅
+ │ ├── statements: ∅
+ │ ├── rescue_clause: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause:
+ │ │ @ EnsureNode (location: (4,0)-(5,3))
+ │ │ ├── ensure_keyword_loc: (4,0)-(4,6) = "ensure"
+ │ │ ├── statements: ∅
+ │ │ └── end_keyword_loc: (5,0)-(5,3) = "end"
+ │ └── end_keyword_loc: (5,0)-(5,3) = "end"
+ ├── @ LambdaNode (location: (7,0)-(11,3))
+ │ ├── locals: []
+ │ ├── operator_loc: (7,0)-(7,2) = "->"
+ │ ├── opening_loc: (7,3)-(7,5) = "do"
+ │ ├── closing_loc: (11,0)-(11,3) = "end"
+ │ ├── parameters: ∅
+ │ └── body:
+ │ @ BeginNode (location: (7,3)-(11,3))
+ │ ├── begin_keyword_loc: ∅
+ │ ├── statements: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (8,0)-(8,6))
+ │ │ ├── keyword_loc: (8,0)-(8,6) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause:
+ │ │ @ ElseNode (location: (9,0)-(10,6))
+ │ │ ├── else_keyword_loc: (9,0)-(9,4) = "else"
+ │ │ ├── statements: ∅
+ │ │ └── end_keyword_loc: (10,0)-(10,6) = "ensure"
+ │ ├── ensure_clause:
+ │ │ @ EnsureNode (location: (10,0)-(11,3))
+ │ │ ├── ensure_keyword_loc: (10,0)-(10,6) = "ensure"
+ │ │ ├── statements: ∅
+ │ │ └── end_keyword_loc: (11,0)-(11,3) = "end"
+ │ └── end_keyword_loc: (11,0)-(11,3) = "end"
+ ├── @ LambdaNode (location: (13,0)-(13,10))
+ │ ├── locals: []
+ │ ├── operator_loc: (13,0)-(13,2) = "->"
+ │ ├── opening_loc: (13,3)-(13,4) = "{"
+ │ ├── closing_loc: (13,9)-(13,10) = "}"
+ │ ├── parameters: ∅
+ │ └── body:
+ │ @ StatementsNode (location: (13,5)-(13,8))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (13,5)-(13,8))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (13,5)-(13,8) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ LambdaNode (location: (15,0)-(15,15))
+ │ ├── locals: []
+ │ ├── operator_loc: (15,0)-(15,2) = "->"
+ │ ├── opening_loc: (15,3)-(15,5) = "do"
+ │ ├── closing_loc: (15,12)-(15,15) = "end"
+ │ ├── parameters: ∅
+ │ └── body:
+ │ @ StatementsNode (location: (15,7)-(15,10))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (15,7)-(15,10))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (15,7)-(15,10) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ LambdaNode (location: (17,0)-(17,29))
+ │ ├── locals: [:a, :b, :c, :d, :e]
+ │ ├── operator_loc: (17,0)-(17,2) = "->"
+ │ ├── opening_loc: (17,24)-(17,25) = "{"
+ │ ├── closing_loc: (17,28)-(17,29) = "}"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (17,3)-(17,23))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (17,3)-(17,23))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (17,3)-(17,4))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 1)
+ │ │ │ │ └── @ OptionalParameterNode (location: (17,6)-(17,11))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── name_loc: (17,6)-(17,7) = "b"
+ │ │ │ │ ├── operator_loc: (17,8)-(17,9) = "="
+ │ │ │ │ └── value:
+ │ │ │ │ @ IntegerNode (location: (17,10)-(17,11))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 2)
+ │ │ │ │ ├── @ RequiredKeywordParameterNode (location: (17,13)-(17,15))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── name: :c
+ │ │ │ │ │ └── name_loc: (17,13)-(17,15) = "c:"
+ │ │ │ │ └── @ RequiredKeywordParameterNode (location: (17,17)-(17,19))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :d
+ │ │ │ │ └── name_loc: (17,17)-(17,19) = "d:"
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockParameterNode (location: (17,21)-(17,23))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :e
+ │ │ │ ├── name_loc: (17,22)-(17,23) = "e"
+ │ │ │ └── operator_loc: (17,21)-(17,22) = "&"
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── body:
+ │ @ StatementsNode (location: (17,26)-(17,27))
+ │ └── body: (length: 1)
+ │ └── @ LocalVariableReadNode (location: (17,26)-(17,27))
+ │ ├── name: :a
+ │ └── depth: 0
+ ├── @ LambdaNode (location: (19,0)-(19,40))
+ │ ├── locals: [:a, :b, :c, :d, :e, :f, :g]
+ │ ├── operator_loc: (19,0)-(19,2) = "->"
+ │ ├── opening_loc: (19,35)-(19,36) = "{"
+ │ ├── closing_loc: (19,39)-(19,40) = "}"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (19,3)-(19,34))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (19,4)-(19,33))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (19,4)-(19,5))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 1)
+ │ │ │ │ └── @ OptionalParameterNode (location: (19,7)-(19,12))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── name_loc: (19,7)-(19,8) = "b"
+ │ │ │ │ ├── operator_loc: (19,9)-(19,10) = "="
+ │ │ │ │ └── value:
+ │ │ │ │ @ IntegerNode (location: (19,11)-(19,12))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── rest:
+ │ │ │ │ @ RestParameterNode (location: (19,14)-(19,16))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── name_loc: (19,15)-(19,16) = "c"
+ │ │ │ │ └── operator_loc: (19,14)-(19,15) = "*"
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 2)
+ │ │ │ │ ├── @ RequiredKeywordParameterNode (location: (19,18)-(19,20))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ └── name_loc: (19,18)-(19,20) = "d:"
+ │ │ │ │ └── @ RequiredKeywordParameterNode (location: (19,22)-(19,24))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :e
+ │ │ │ │ └── name_loc: (19,22)-(19,24) = "e:"
+ │ │ │ ├── keyword_rest:
+ │ │ │ │ @ KeywordRestParameterNode (location: (19,26)-(19,29))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :f
+ │ │ │ │ ├── name_loc: (19,28)-(19,29) = "f"
+ │ │ │ │ └── operator_loc: (19,26)-(19,28) = "**"
+ │ │ │ └── block:
+ │ │ │ @ BlockParameterNode (location: (19,31)-(19,33))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :g
+ │ │ │ ├── name_loc: (19,32)-(19,33) = "g"
+ │ │ │ └── operator_loc: (19,31)-(19,32) = "&"
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (19,3)-(19,4) = "("
+ │ │ └── closing_loc: (19,33)-(19,34) = ")"
+ │ └── body:
+ │ @ StatementsNode (location: (19,37)-(19,38))
+ │ └── body: (length: 1)
+ │ └── @ LocalVariableReadNode (location: (19,37)-(19,38))
+ │ ├── name: :a
+ │ └── depth: 0
+ ├── @ LambdaNode (location: (21,0)-(23,3))
+ │ ├── locals: [:a, :b, :c, :d, :e, :f, :g]
+ │ ├── operator_loc: (21,0)-(21,2) = "->"
+ │ ├── opening_loc: (21,35)-(21,37) = "do"
+ │ ├── closing_loc: (23,0)-(23,3) = "end"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (21,3)-(21,34))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (21,4)-(21,33))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (21,4)-(21,5))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 1)
+ │ │ │ │ └── @ OptionalParameterNode (location: (21,7)-(21,12))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── name_loc: (21,7)-(21,8) = "b"
+ │ │ │ │ ├── operator_loc: (21,9)-(21,10) = "="
+ │ │ │ │ └── value:
+ │ │ │ │ @ IntegerNode (location: (21,11)-(21,12))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── rest:
+ │ │ │ │ @ RestParameterNode (location: (21,14)-(21,16))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── name_loc: (21,15)-(21,16) = "c"
+ │ │ │ │ └── operator_loc: (21,14)-(21,15) = "*"
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 2)
+ │ │ │ │ ├── @ RequiredKeywordParameterNode (location: (21,18)-(21,20))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ └── name_loc: (21,18)-(21,20) = "d:"
+ │ │ │ │ └── @ RequiredKeywordParameterNode (location: (21,22)-(21,24))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :e
+ │ │ │ │ └── name_loc: (21,22)-(21,24) = "e:"
+ │ │ │ ├── keyword_rest:
+ │ │ │ │ @ KeywordRestParameterNode (location: (21,26)-(21,29))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :f
+ │ │ │ │ ├── name_loc: (21,28)-(21,29) = "f"
+ │ │ │ │ └── operator_loc: (21,26)-(21,28) = "**"
+ │ │ │ └── block:
+ │ │ │ @ BlockParameterNode (location: (21,31)-(21,33))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :g
+ │ │ │ ├── name_loc: (21,32)-(21,33) = "g"
+ │ │ │ └── operator_loc: (21,31)-(21,32) = "&"
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (21,3)-(21,4) = "("
+ │ │ └── closing_loc: (21,33)-(21,34) = ")"
+ │ └── body:
+ │ @ StatementsNode (location: (22,2)-(22,3))
+ │ └── body: (length: 1)
+ │ └── @ LocalVariableReadNode (location: (22,2)-(22,3))
+ │ ├── name: :a
+ │ └── depth: 0
+ ├── @ LambdaNode (location: (25,0)-(25,25))
+ │ ├── locals: [:a]
+ │ ├── operator_loc: (25,0)-(25,2) = "->"
+ │ ├── opening_loc: (25,7)-(25,8) = "{"
+ │ ├── closing_loc: (25,24)-(25,25) = "}"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (25,3)-(25,6))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (25,4)-(25,5))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (25,4)-(25,5))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (25,3)-(25,4) = "("
+ │ │ └── closing_loc: (25,5)-(25,6) = ")"
+ │ └── body:
+ │ @ StatementsNode (location: (25,9)-(25,23))
+ │ └── body: (length: 1)
+ │ └── @ LambdaNode (location: (25,9)-(25,23))
+ │ ├── locals: [:b]
+ │ ├── operator_loc: (25,9)-(25,11) = "->"
+ │ ├── opening_loc: (25,14)-(25,15) = "{"
+ │ ├── closing_loc: (25,22)-(25,23) = "}"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (25,12)-(25,13))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (25,12)-(25,13))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (25,12)-(25,13))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :b
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── body:
+ │ @ StatementsNode (location: (25,16)-(25,21))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (25,16)-(25,21))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (25,16)-(25,17))
+ │ │ ├── name: :a
+ │ │ └── depth: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :*
+ │ ├── message_loc: (25,18)-(25,19) = "*"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (25,20)-(25,21))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (25,20)-(25,21))
+ │ │ ├── name: :b
+ │ │ └── depth: 0
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ LambdaNode (location: (27,0)-(27,19))
+ ├── locals: [:a, :b, :c]
+ ├── operator_loc: (27,0)-(27,2) = "->"
+ ├── opening_loc: (27,16)-(27,17) = "{"
+ ├── closing_loc: (27,18)-(27,19) = "}"
+ ├── parameters:
+ │ @ BlockParametersNode (location: (27,3)-(27,15))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (27,4)-(27,14))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ MultiTargetNode (location: (27,4)-(27,10))
+ │ │ │ ├── lefts: (length: 2)
+ │ │ │ │ ├── @ RequiredParameterNode (location: (27,5)-(27,6))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :a
+ │ │ │ │ └── @ RequiredParameterNode (location: (27,8)-(27,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :b
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: (27,4)-(27,5) = "("
+ │ │ │ └── rparen_loc: (27,9)-(27,10) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (27,12)-(27,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── name_loc: (27,13)-(27,14) = "c"
+ │ │ │ └── operator_loc: (27,12)-(27,13) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (27,3)-(27,4) = "("
+ │ └── closing_loc: (27,14)-(27,15) = ")"
+ └── body: ∅
diff --git a/test/prism/snapshots/range_begin_open_exclusive.txt b/test/prism/snapshots/range_begin_open_exclusive.txt
new file mode 100644
index 0000000000..a630b01ef1
--- /dev/null
+++ b/test/prism/snapshots/range_begin_open_exclusive.txt
@@ -0,0 +1,13 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ RangeNode (location: (1,0)-(1,4))
+ ├── flags: exclude_end
+ ├── left: ∅
+ ├── right:
+ │ @ IntegerNode (location: (1,3)-(1,4))
+ │ ├── flags: decimal
+ │ └── value: 2
+ └── operator_loc: (1,0)-(1,3) = "..."
diff --git a/test/prism/snapshots/range_begin_open_inclusive.txt b/test/prism/snapshots/range_begin_open_inclusive.txt
new file mode 100644
index 0000000000..dc8ef0d2db
--- /dev/null
+++ b/test/prism/snapshots/range_begin_open_inclusive.txt
@@ -0,0 +1,13 @@
+@ ProgramNode (location: (1,0)-(1,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,3))
+ └── body: (length: 1)
+ └── @ RangeNode (location: (1,0)-(1,3))
+ ├── flags: ∅
+ ├── left: ∅
+ ├── right:
+ │ @ IntegerNode (location: (1,2)-(1,3))
+ │ ├── flags: decimal
+ │ └── value: 2
+ └── operator_loc: (1,0)-(1,2) = ".."
diff --git a/test/prism/snapshots/range_end_open_exclusive.txt b/test/prism/snapshots/range_end_open_exclusive.txt
new file mode 100644
index 0000000000..17a75f8945
--- /dev/null
+++ b/test/prism/snapshots/range_end_open_exclusive.txt
@@ -0,0 +1,13 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ RangeNode (location: (1,0)-(1,4))
+ ├── flags: exclude_end
+ ├── left:
+ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── right: ∅
+ └── operator_loc: (1,1)-(1,4) = "..."
diff --git a/test/prism/snapshots/range_end_open_inclusive.txt b/test/prism/snapshots/range_end_open_inclusive.txt
new file mode 100644
index 0000000000..b49272d8cd
--- /dev/null
+++ b/test/prism/snapshots/range_end_open_inclusive.txt
@@ -0,0 +1,13 @@
+@ ProgramNode (location: (1,0)-(1,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,3))
+ └── body: (length: 1)
+ └── @ RangeNode (location: (1,0)-(1,3))
+ ├── flags: ∅
+ ├── left:
+ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── right: ∅
+ └── operator_loc: (1,1)-(1,3) = ".."
diff --git a/test/prism/snapshots/ranges.txt b/test/prism/snapshots/ranges.txt
new file mode 100644
index 0000000000..2fffe80537
--- /dev/null
+++ b/test/prism/snapshots/ranges.txt
@@ -0,0 +1,533 @@
+@ ProgramNode (location: (1,0)-(49,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(49,7))
+ └── body: (length: 25)
+ ├── @ ParenthesesNode (location: (1,0)-(1,6))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,1)-(1,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ RangeNode (location: (1,1)-(1,5))
+ │ │ ├── flags: exclude_end
+ │ │ ├── left: ∅
+ │ │ ├── right:
+ │ │ │ @ IntegerNode (location: (1,4)-(1,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── operator_loc: (1,1)-(1,4) = "..."
+ │ ├── opening_loc: (1,0)-(1,1) = "("
+ │ └── closing_loc: (1,5)-(1,6) = ")"
+ ├── @ ParenthesesNode (location: (3,0)-(3,5))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (3,1)-(3,4))
+ │ │ └── body: (length: 1)
+ │ │ └── @ RangeNode (location: (3,1)-(3,4))
+ │ │ ├── flags: ∅
+ │ │ ├── left: ∅
+ │ │ ├── right:
+ │ │ │ @ IntegerNode (location: (3,3)-(3,4))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── operator_loc: (3,1)-(3,3) = ".."
+ │ ├── opening_loc: (3,0)-(3,1) = "("
+ │ └── closing_loc: (3,4)-(3,5) = ")"
+ ├── @ RangeNode (location: (5,0)-(5,5))
+ │ ├── flags: exclude_end
+ │ ├── left:
+ │ │ @ IntegerNode (location: (5,0)-(5,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── right:
+ │ │ @ IntegerNode (location: (5,4)-(5,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ └── operator_loc: (5,1)-(5,4) = "..."
+ ├── @ CallNode (location: (7,0)-(7,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (7,0)-(7,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (7,0)-(7,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]
+ │ ├── message_loc: (7,3)-(7,9) = "[...2]"
+ │ ├── opening_loc: (7,3)-(7,4) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (7,4)-(7,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ RangeNode (location: (7,4)-(7,8))
+ │ │ ├── flags: exclude_end
+ │ │ ├── left: ∅
+ │ │ ├── right:
+ │ │ │ @ IntegerNode (location: (7,7)-(7,8))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── operator_loc: (7,4)-(7,7) = "..."
+ │ ├── closing_loc: (7,8)-(7,9) = "]"
+ │ └── block: ∅
+ ├── @ HashNode (location: (9,0)-(9,15))
+ │ ├── opening_loc: (9,0)-(9,1) = "{"
+ │ ├── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (9,2)-(9,13))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (9,2)-(9,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (9,2)-(9,5) = "foo"
+ │ │ │ ├── closing_loc: (9,5)-(9,6) = ":"
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── value:
+ │ │ │ @ RangeNode (location: (9,7)-(9,13))
+ │ │ │ ├── flags: exclude_end
+ │ │ │ ├── left: ∅
+ │ │ │ ├── right:
+ │ │ │ │ @ CallNode (location: (9,10)-(9,13))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (9,10)-(9,13) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── operator_loc: (9,7)-(9,10) = "..."
+ │ │ └── operator_loc: ∅
+ │ └── closing_loc: (9,14)-(9,15) = "}"
+ ├── @ ParenthesesNode (location: (11,0)-(11,6))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (11,1)-(11,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ RangeNode (location: (11,1)-(11,5))
+ │ │ ├── flags: exclude_end
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (11,1)-(11,2))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right: ∅
+ │ │ └── operator_loc: (11,2)-(11,5) = "..."
+ │ ├── opening_loc: (11,0)-(11,1) = "("
+ │ └── closing_loc: (11,5)-(11,6) = ")"
+ ├── @ RangeNode (location: (13,0)-(13,4))
+ │ ├── flags: ∅
+ │ ├── left:
+ │ │ @ IntegerNode (location: (13,0)-(13,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── right:
+ │ │ @ IntegerNode (location: (13,3)-(13,4))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ └── operator_loc: (13,1)-(13,3) = ".."
+ ├── @ HashNode (location: (15,0)-(15,14))
+ │ ├── opening_loc: (15,0)-(15,1) = "{"
+ │ ├── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (15,2)-(15,12))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (15,2)-(15,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (15,2)-(15,5) = "foo"
+ │ │ │ ├── closing_loc: (15,5)-(15,6) = ":"
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── value:
+ │ │ │ @ RangeNode (location: (15,7)-(15,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── left: ∅
+ │ │ │ ├── right:
+ │ │ │ │ @ CallNode (location: (15,9)-(15,12))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (15,9)-(15,12) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── operator_loc: (15,7)-(15,9) = ".."
+ │ │ └── operator_loc: ∅
+ │ └── closing_loc: (15,13)-(15,14) = "}"
+ ├── @ ParenthesesNode (location: (17,0)-(17,5))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (17,1)-(17,4))
+ │ │ └── body: (length: 1)
+ │ │ └── @ RangeNode (location: (17,1)-(17,4))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (17,1)-(17,2))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right: ∅
+ │ │ └── operator_loc: (17,2)-(17,4) = ".."
+ │ ├── opening_loc: (17,0)-(17,1) = "("
+ │ └── closing_loc: (17,4)-(17,5) = ")"
+ ├── @ RangeNode (location: (19,0)-(19,8))
+ │ ├── flags: ∅
+ │ ├── left:
+ │ │ @ IntegerNode (location: (19,0)-(19,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── right:
+ │ │ @ RangeNode (location: (19,5)-(19,8))
+ │ │ ├── flags: ∅
+ │ │ ├── left: ∅
+ │ │ ├── right:
+ │ │ │ @ IntegerNode (location: (19,7)-(19,8))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (19,5)-(19,7) = ".."
+ │ └── operator_loc: (19,2)-(19,4) = ".."
+ ├── @ AndNode (location: (21,0)-(21,8))
+ │ ├── left:
+ │ │ @ RangeNode (location: (21,0)-(21,3))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (21,0)-(21,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right: ∅
+ │ │ └── operator_loc: (21,1)-(21,3) = ".."
+ │ ├── right:
+ │ │ @ IntegerNode (location: (21,7)-(21,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ └── operator_loc: (21,4)-(21,6) = "&&"
+ ├── @ CallNode (location: (23,0)-(23,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ RangeNode (location: (23,0)-(23,3))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (23,0)-(23,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right: ∅
+ │ │ └── operator_loc: (23,1)-(23,3) = ".."
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :==
+ │ ├── message_loc: (23,4)-(23,6) = "=="
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (23,7)-(23,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (23,7)-(23,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (25,0)-(25,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ RangeNode (location: (25,0)-(25,3))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (25,0)-(25,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right: ∅
+ │ │ └── operator_loc: (25,1)-(25,3) = ".."
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!=
+ │ ├── message_loc: (25,4)-(25,6) = "!="
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (25,7)-(25,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (25,7)-(25,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (27,0)-(27,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ RangeNode (location: (27,0)-(27,3))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (27,0)-(27,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right: ∅
+ │ │ └── operator_loc: (27,1)-(27,3) = ".."
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :===
+ │ ├── message_loc: (27,4)-(27,7) = "==="
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (27,8)-(27,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (27,8)-(27,9))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (29,0)-(29,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ RangeNode (location: (29,0)-(29,3))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (29,0)-(29,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right: ∅
+ │ │ └── operator_loc: (29,1)-(29,3) = ".."
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :<=>
+ │ ├── message_loc: (29,4)-(29,7) = "<=>"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (29,8)-(29,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (29,8)-(29,9))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (31,0)-(31,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ RangeNode (location: (31,0)-(31,3))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (31,0)-(31,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right: ∅
+ │ │ └── operator_loc: (31,1)-(31,3) = ".."
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :=~
+ │ ├── message_loc: (31,4)-(31,6) = "=~"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (31,7)-(31,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (31,7)-(31,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (33,0)-(33,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ RangeNode (location: (33,0)-(33,3))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (33,0)-(33,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right: ∅
+ │ │ └── operator_loc: (33,1)-(33,3) = ".."
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!~
+ │ ├── message_loc: (33,4)-(33,6) = "!~"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (33,7)-(33,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (33,7)-(33,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (35,0)-(35,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ RangeNode (location: (35,0)-(35,3))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (35,0)-(35,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right: ∅
+ │ │ └── operator_loc: (35,1)-(35,3) = ".."
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :<
+ │ ├── message_loc: (35,4)-(35,5) = "<"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (35,6)-(35,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (35,6)-(35,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (37,0)-(37,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ RangeNode (location: (37,0)-(37,3))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (37,0)-(37,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right: ∅
+ │ │ └── operator_loc: (37,1)-(37,3) = ".."
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :>
+ │ ├── message_loc: (37,4)-(37,5) = ">"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (37,6)-(37,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (37,6)-(37,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (39,0)-(39,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ RangeNode (location: (39,0)-(39,3))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (39,0)-(39,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right: ∅
+ │ │ └── operator_loc: (39,1)-(39,3) = ".."
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :<=
+ │ ├── message_loc: (39,4)-(39,6) = "<="
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (39,7)-(39,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (39,7)-(39,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (41,0)-(41,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ RangeNode (location: (41,0)-(41,3))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (41,0)-(41,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right: ∅
+ │ │ └── operator_loc: (41,1)-(41,3) = ".."
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :>=
+ │ ├── message_loc: (41,4)-(41,6) = ">="
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (41,7)-(41,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (41,7)-(41,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (43,0)-(43,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ RangeNode (location: (43,0)-(43,3))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (43,0)-(43,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right: ∅
+ │ │ └── operator_loc: (43,1)-(43,3) = ".."
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :<<
+ │ ├── message_loc: (43,4)-(43,6) = "<<"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (43,7)-(43,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (43,7)-(43,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (45,0)-(45,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ RangeNode (location: (45,0)-(45,3))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (45,0)-(45,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right: ∅
+ │ │ └── operator_loc: (45,1)-(45,3) = ".."
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :>>
+ │ ├── message_loc: (45,4)-(45,6) = ">>"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (45,7)-(45,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (45,7)-(45,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ RangeNode (location: (47,0)-(47,7))
+ │ ├── flags: ∅
+ │ ├── left:
+ │ │ @ IntegerNode (location: (47,0)-(47,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── right:
+ │ │ @ CallNode (location: (47,4)-(47,7))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ IntegerNode (location: (47,6)-(47,7))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :+@
+ │ │ ├── message_loc: (47,4)-(47,5) = "+"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (47,1)-(47,3) = ".."
+ └── @ RangeNode (location: (49,0)-(49,7))
+ ├── flags: ∅
+ ├── left:
+ │ @ IntegerNode (location: (49,0)-(49,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── right:
+ │ @ CallNode (location: (49,4)-(49,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (49,6)-(49,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :-@
+ │ ├── message_loc: (49,4)-(49,5) = "-"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── operator_loc: (49,1)-(49,3) = ".."
diff --git a/test/prism/snapshots/regex.txt b/test/prism/snapshots/regex.txt
new file mode 100644
index 0000000000..d4d153e8d5
--- /dev/null
+++ b/test/prism/snapshots/regex.txt
@@ -0,0 +1,510 @@
+@ ProgramNode (location: (1,0)-(46,32))
+├── locals: [:foo, :ab, :abc, :a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(46,32))
+ └── body: (length: 25)
+ ├── @ CallNode (location: (1,0)-(1,9))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,4)-(1,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ RegularExpressionNode (location: (1,4)-(1,9))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,4)-(1,5) = "/"
+ │ │ ├── content_loc: (1,5)-(1,8) = "bar"
+ │ │ ├── closing_loc: (1,8)-(1,9) = "/"
+ │ │ └── unescaped: "bar"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ RegularExpressionNode (location: (3,0)-(3,8))
+ │ ├── flags: ignore_case, forced_us_ascii_encoding
+ │ ├── opening_loc: (3,0)-(3,3) = "%r{"
+ │ ├── content_loc: (3,3)-(3,6) = "abc"
+ │ ├── closing_loc: (3,6)-(3,8) = "}i"
+ │ └── unescaped: "abc"
+ ├── @ RegularExpressionNode (location: (5,0)-(5,5))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (5,0)-(5,1) = "/"
+ │ ├── content_loc: (5,1)-(5,4) = "a\\b"
+ │ ├── closing_loc: (5,4)-(5,5) = "/"
+ │ └── unescaped: "a\\b"
+ ├── @ InterpolatedRegularExpressionNode (location: (7,0)-(7,11))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (7,0)-(7,1) = "/"
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (7,1)-(7,5))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (7,1)-(7,5) = "aaa "
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "aaa "
+ │ │ └── @ EmbeddedVariableNode (location: (7,5)-(7,10))
+ │ │ ├── operator_loc: (7,5)-(7,6) = "#"
+ │ │ └── variable:
+ │ │ @ GlobalVariableReadNode (location: (7,6)-(7,10))
+ │ │ └── name: :$bbb
+ │ └── closing_loc: (7,10)-(7,11) = "/"
+ ├── @ InterpolatedRegularExpressionNode (location: (9,0)-(9,16))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (9,0)-(9,1) = "/"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (9,1)-(9,5))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (9,1)-(9,5) = "aaa "
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "aaa "
+ │ │ ├── @ EmbeddedStatementsNode (location: (9,5)-(9,11))
+ │ │ │ ├── opening_loc: (9,5)-(9,7) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (9,7)-(9,10))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (9,7)-(9,10))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bbb
+ │ │ │ │ ├── message_loc: (9,7)-(9,10) = "bbb"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── closing_loc: (9,10)-(9,11) = "}"
+ │ │ └── @ StringNode (location: (9,11)-(9,15))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (9,11)-(9,15) = " ccc"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: " ccc"
+ │ └── closing_loc: (9,15)-(9,16) = "/"
+ ├── @ ArrayNode (location: (11,0)-(11,27))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ MatchWriteNode (location: (11,1)-(11,21))
+ │ │ │ ├── call:
+ │ │ │ │ @ CallNode (location: (11,1)-(11,21))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── receiver:
+ │ │ │ │ │ @ RegularExpressionNode (location: (11,1)-(11,14))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: (11,1)-(11,2) = "/"
+ │ │ │ │ │ ├── content_loc: (11,2)-(11,13) = "(?<foo>bar)"
+ │ │ │ │ │ ├── closing_loc: (11,13)-(11,14) = "/"
+ │ │ │ │ │ └── unescaped: "(?<foo>bar)"
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :=~
+ │ │ │ │ ├── message_loc: (11,15)-(11,17) = "=~"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (11,18)-(11,21))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (11,18)-(11,21))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :baz
+ │ │ │ │ │ ├── message_loc: (11,18)-(11,21) = "baz"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── targets: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (11,5)-(11,8))
+ │ │ │ ├── name: :foo
+ │ │ │ └── depth: 0
+ │ │ └── @ LocalVariableReadNode (location: (11,23)-(11,26))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── opening_loc: (11,0)-(11,1) = "["
+ │ └── closing_loc: (11,26)-(11,27) = "]"
+ ├── @ RegularExpressionNode (location: (13,0)-(13,6))
+ │ ├── flags: ignore_case, forced_us_ascii_encoding
+ │ ├── opening_loc: (13,0)-(13,1) = "/"
+ │ ├── content_loc: (13,1)-(13,4) = "abc"
+ │ ├── closing_loc: (13,4)-(13,6) = "/i"
+ │ └── unescaped: "abc"
+ ├── @ RegularExpressionNode (location: (15,0)-(15,26))
+ │ ├── flags: ignore_case, forced_us_ascii_encoding
+ │ ├── opening_loc: (15,0)-(15,3) = "%r/"
+ │ ├── content_loc: (15,3)-(15,24) = "[a-z$._?][\\w$.?\#@~]*:"
+ │ ├── closing_loc: (15,24)-(15,26) = "/i"
+ │ └── unescaped: "[a-z$._?][\\w$.?\#@~]*:"
+ ├── @ RegularExpressionNode (location: (17,0)-(17,37))
+ │ ├── flags: ignore_case, forced_us_ascii_encoding
+ │ ├── opening_loc: (17,0)-(17,3) = "%r/"
+ │ ├── content_loc: (17,3)-(17,35) = "([a-z$._?][\\w$.?\#@~]*)(\\s+)(equ)"
+ │ ├── closing_loc: (17,35)-(17,37) = "/i"
+ │ └── unescaped: "([a-z$._?][\\w$.?\#@~]*)(\\s+)(equ)"
+ ├── @ RegularExpressionNode (location: (19,0)-(19,25))
+ │ ├── flags: ignore_case, forced_us_ascii_encoding
+ │ ├── opening_loc: (19,0)-(19,3) = "%r/"
+ │ ├── content_loc: (19,3)-(19,23) = "[a-z$._?][\\w$.?\#@~]*"
+ │ ├── closing_loc: (19,23)-(19,25) = "/i"
+ │ └── unescaped: "[a-z$._?][\\w$.?\#@~]*"
+ ├── @ RegularExpressionNode (location: (21,0)-(24,1))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (21,0)-(21,3) = "%r("
+ │ ├── content_loc: (21,3)-(24,0) = "\n(?:[\#$%_']|\\(\\)|\\(,\\)|\\[\\]|[0-9])*\n (?:[\#$%_']+)\n"
+ │ ├── closing_loc: (24,0)-(24,1) = ")"
+ │ └── unescaped: "\n(?:[\#$%_']|\\(\\)|\\(,\\)|\\[\\]|[0-9])*\n (?:[\#$%_']+)\n"
+ ├── @ CallNode (location: (26,0)-(26,16))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ RegularExpressionNode (location: (26,0)-(26,8))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (26,0)-(26,1) = "/"
+ │ │ ├── content_loc: (26,1)-(26,7) = "(?#\\))"
+ │ │ ├── closing_loc: (26,7)-(26,8) = "/"
+ │ │ └── unescaped: "(?#\\))"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :=~
+ │ ├── message_loc: (26,9)-(26,11) = "=~"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (26,12)-(26,16))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (26,12)-(26,16))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (26,12)-(26,13) = "\""
+ │ │ ├── content_loc: (26,13)-(26,15) = "hi"
+ │ │ ├── closing_loc: (26,15)-(26,16) = "\""
+ │ │ └── unescaped: "hi"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ RegularExpressionNode (location: (28,0)-(28,9))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (28,0)-(28,3) = "%r#"
+ │ ├── content_loc: (28,3)-(28,8) = "pound"
+ │ ├── closing_loc: (28,8)-(28,9) = "#"
+ │ └── unescaped: "pound"
+ ├── @ InterpolatedRegularExpressionNode (location: (30,0)-(30,13))
+ │ ├── flags: once
+ │ ├── opening_loc: (30,0)-(30,1) = "/"
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (30,1)-(30,5))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (30,1)-(30,5) = "aaa "
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "aaa "
+ │ │ └── @ EmbeddedStatementsNode (location: (30,5)-(30,11))
+ │ │ ├── opening_loc: (30,5)-(30,7) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (30,7)-(30,10))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (30,7)-(30,10))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bbb
+ │ │ │ ├── message_loc: (30,7)-(30,10) = "bbb"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── closing_loc: (30,10)-(30,11) = "}"
+ │ └── closing_loc: (30,11)-(30,13) = "/o"
+ ├── @ MatchWriteNode (location: (32,0)-(33,10))
+ │ ├── call:
+ │ │ @ CallNode (location: (32,0)-(33,10))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ RegularExpressionNode (location: (32,0)-(33,4))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (32,0)-(32,1) = "/"
+ │ │ │ ├── content_loc: (32,1)-(33,3) = "(?<a\\\nb>)"
+ │ │ │ ├── closing_loc: (33,3)-(33,4) = "/"
+ │ │ │ └── unescaped: "(?<ab>)"
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :=~
+ │ │ ├── message_loc: (33,5)-(33,7) = "=~"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (33,8)-(33,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ StringNode (location: (33,8)-(33,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (33,8)-(33,9) = "\""
+ │ │ │ ├── content_loc: (33,9)-(33,9) = ""
+ │ │ │ ├── closing_loc: (33,9)-(33,10) = "\""
+ │ │ │ └── unescaped: ""
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── targets: (length: 1)
+ │ └── @ LocalVariableTargetNode (location: (32,0)-(33,4))
+ │ ├── name: :ab
+ │ └── depth: 0
+ ├── @ LocalVariableReadNode (location: (33,12)-(33,14))
+ │ ├── name: :ab
+ │ └── depth: 0
+ ├── @ MatchWriteNode (location: (35,0)-(35,24))
+ │ ├── call:
+ │ │ @ CallNode (location: (35,0)-(35,24))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ RegularExpressionNode (location: (35,0)-(35,18))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (35,0)-(35,1) = "/"
+ │ │ │ ├── content_loc: (35,1)-(35,17) = "(?<abc>)(?<abc>)"
+ │ │ │ ├── closing_loc: (35,17)-(35,18) = "/"
+ │ │ │ └── unescaped: "(?<abc>)(?<abc>)"
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :=~
+ │ │ ├── message_loc: (35,19)-(35,21) = "=~"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (35,22)-(35,24))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ StringNode (location: (35,22)-(35,24))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (35,22)-(35,23) = "\""
+ │ │ │ ├── content_loc: (35,23)-(35,23) = ""
+ │ │ │ ├── closing_loc: (35,23)-(35,24) = "\""
+ │ │ │ └── unescaped: ""
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── targets: (length: 1)
+ │ └── @ LocalVariableTargetNode (location: (35,4)-(35,7))
+ │ ├── name: :abc
+ │ └── depth: 0
+ ├── @ LocalVariableReadNode (location: (35,26)-(35,29))
+ │ ├── name: :abc
+ │ └── depth: 0
+ ├── @ CallNode (location: (37,0)-(37,16))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ RegularExpressionNode (location: (37,0)-(37,10))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (37,0)-(37,1) = "/"
+ │ │ ├── content_loc: (37,1)-(37,9) = "(?<a b>)"
+ │ │ ├── closing_loc: (37,9)-(37,10) = "/"
+ │ │ └── unescaped: "(?<a b>)"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :=~
+ │ ├── message_loc: (37,11)-(37,13) = "=~"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (37,14)-(37,16))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (37,14)-(37,16))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (37,14)-(37,15) = "\""
+ │ │ ├── content_loc: (37,15)-(37,15) = ""
+ │ │ ├── closing_loc: (37,15)-(37,16) = "\""
+ │ │ └── unescaped: ""
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ LocalVariableWriteNode (location: (39,0)-(39,5))
+ │ ├── name: :a
+ │ ├── depth: 0
+ │ ├── name_loc: (39,0)-(39,1) = "a"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (39,4)-(39,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (39,2)-(39,3) = "="
+ ├── @ CallNode (location: (40,0)-(40,24))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (40,0)-(40,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (40,4)-(40,24))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (40,6)-(40,22))
+ │ │ └── body: (length: 1)
+ │ │ └── @ MatchWriteNode (location: (40,6)-(40,22))
+ │ │ ├── call:
+ │ │ │ @ CallNode (location: (40,6)-(40,22))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ RegularExpressionNode (location: (40,6)-(40,14))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (40,6)-(40,7) = "/"
+ │ │ │ │ ├── content_loc: (40,7)-(40,13) = "(?<a>)"
+ │ │ │ │ ├── closing_loc: (40,13)-(40,14) = "/"
+ │ │ │ │ └── unescaped: "(?<a>)"
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :=~
+ │ │ │ ├── message_loc: (40,15)-(40,17) = "=~"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (40,18)-(40,22))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (40,18)-(40,22))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :to_s
+ │ │ │ │ ├── message_loc: (40,18)-(40,22) = "to_s"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── targets: (length: 1)
+ │ │ └── @ LocalVariableTargetNode (location: (40,10)-(40,11))
+ │ │ ├── name: :a
+ │ │ └── depth: 1
+ │ ├── opening_loc: (40,4)-(40,5) = "{"
+ │ └── closing_loc: (40,23)-(40,24) = "}"
+ ├── @ MatchWriteNode (location: (42,0)-(42,16))
+ │ ├── call:
+ │ │ @ CallNode (location: (42,0)-(42,16))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ RegularExpressionNode (location: (42,0)-(42,10))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (42,0)-(42,1) = "/"
+ │ │ │ ├── content_loc: (42,1)-(42,9) = "(?<foo>)"
+ │ │ │ ├── closing_loc: (42,9)-(42,10) = "/"
+ │ │ │ └── unescaped: "(?<foo>)"
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :=~
+ │ │ ├── message_loc: (42,11)-(42,13) = "=~"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (42,14)-(42,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ StringNode (location: (42,14)-(42,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (42,14)-(42,15) = "\""
+ │ │ │ ├── content_loc: (42,15)-(42,15) = ""
+ │ │ │ ├── closing_loc: (42,15)-(42,16) = "\""
+ │ │ │ └── unescaped: ""
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── targets: (length: 1)
+ │ └── @ LocalVariableTargetNode (location: (42,4)-(42,7))
+ │ ├── name: :foo
+ │ └── depth: 0
+ ├── @ CallNode (location: (43,0)-(43,16))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ RegularExpressionNode (location: (43,0)-(43,10))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (43,0)-(43,1) = "/"
+ │ │ ├── content_loc: (43,1)-(43,9) = "(?<Foo>)"
+ │ │ ├── closing_loc: (43,9)-(43,10) = "/"
+ │ │ └── unescaped: "(?<Foo>)"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :=~
+ │ ├── message_loc: (43,11)-(43,13) = "=~"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (43,14)-(43,16))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (43,14)-(43,16))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (43,14)-(43,15) = "\""
+ │ │ ├── content_loc: (43,15)-(43,15) = ""
+ │ │ ├── closing_loc: (43,15)-(43,16) = "\""
+ │ │ └── unescaped: ""
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (45,0)-(45,16))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ RegularExpressionNode (location: (45,0)-(45,10))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (45,0)-(45,1) = "/"
+ │ │ ├── content_loc: (45,1)-(45,9) = "(?<nil>)"
+ │ │ ├── closing_loc: (45,9)-(45,10) = "/"
+ │ │ └── unescaped: "(?<nil>)"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :=~
+ │ ├── message_loc: (45,11)-(45,13) = "=~"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (45,14)-(45,16))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (45,14)-(45,16))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (45,14)-(45,15) = "\""
+ │ │ ├── content_loc: (45,15)-(45,15) = ""
+ │ │ ├── closing_loc: (45,15)-(45,16) = "\""
+ │ │ └── unescaped: ""
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ DefNode (location: (46,0)-(46,32))
+ ├── name: :foo
+ ├── name_loc: (46,4)-(46,7) = "foo"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (46,8)-(46,12))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 1)
+ │ │ └── @ RequiredKeywordParameterNode (location: (46,8)-(46,12))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :nil
+ │ │ └── name_loc: (46,8)-(46,12) = "nil:"
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (46,16)-(46,32))
+ │ └── body: (length: 1)
+ │ └── @ MatchWriteNode (location: (46,16)-(46,32))
+ │ ├── call:
+ │ │ @ CallNode (location: (46,16)-(46,32))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ RegularExpressionNode (location: (46,16)-(46,26))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (46,16)-(46,17) = "/"
+ │ │ │ ├── content_loc: (46,17)-(46,25) = "(?<nil>)"
+ │ │ │ ├── closing_loc: (46,25)-(46,26) = "/"
+ │ │ │ └── unescaped: "(?<nil>)"
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :=~
+ │ │ ├── message_loc: (46,27)-(46,29) = "=~"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (46,30)-(46,32))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ StringNode (location: (46,30)-(46,32))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (46,30)-(46,31) = "\""
+ │ │ │ ├── content_loc: (46,31)-(46,31) = ""
+ │ │ │ ├── closing_loc: (46,31)-(46,32) = "\""
+ │ │ │ └── unescaped: ""
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── targets: (length: 1)
+ │ └── @ LocalVariableTargetNode (location: (46,20)-(46,23))
+ │ ├── name: :nil
+ │ └── depth: 0
+ ├── locals: [:nil]
+ ├── def_keyword_loc: (46,0)-(46,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (46,7)-(46,8) = "("
+ ├── rparen_loc: (46,12)-(46,13) = ")"
+ ├── equal_loc: (46,14)-(46,15) = "="
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/regex_char_width.txt b/test/prism/snapshots/regex_char_width.txt
new file mode 100644
index 0000000000..6bf2169b2f
--- /dev/null
+++ b/test/prism/snapshots/regex_char_width.txt
@@ -0,0 +1,50 @@
+@ ProgramNode (location: (2,0)-(3,6))
+├── locals: [:a, :b]
+└── statements:
+ @ StatementsNode (location: (2,0)-(3,6))
+ └── body: (length: 2)
+ ├── @ MatchWriteNode (location: (2,0)-(2,36))
+ │ ├── call:
+ │ │ @ CallNode (location: (2,0)-(2,36))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ RegularExpressionNode (location: (2,0)-(2,22))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (2,0)-(2,1) = "/"
+ │ │ │ ├── content_loc: (2,1)-(2,21) = "\x{E285}\xA7(?<a>.)\x{E285}\xA9(?<b>.)"
+ │ │ │ ├── closing_loc: (2,21)-(2,22) = "/"
+ │ │ │ └── unescaped: "\x{E285}\xA7(?<a>.)\x{E285}\xA9(?<b>.)"
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :=~
+ │ │ ├── message_loc: (2,23)-(2,25) = "=~"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (2,26)-(2,36))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ StringNode (location: (2,26)-(2,36))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (2,26)-(2,27) = "'"
+ │ │ │ ├── content_loc: (2,27)-(2,35) = "\x{E285}\xA7a\x{E285}\xA9b"
+ │ │ │ ├── closing_loc: (2,35)-(2,36) = "'"
+ │ │ │ └── unescaped: "\x{E285}\xA7a\x{E285}\xA9b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── targets: (length: 2)
+ │ ├── @ LocalVariableTargetNode (location: (2,7)-(2,8))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ └── @ LocalVariableTargetNode (location: (2,17)-(2,18))
+ │ ├── name: :b
+ │ └── depth: 0
+ └── @ ArrayNode (location: (3,0)-(3,6))
+ ├── flags: ∅
+ ├── elements: (length: 2)
+ │ ├── @ LocalVariableReadNode (location: (3,1)-(3,2))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ └── @ LocalVariableReadNode (location: (3,4)-(3,5))
+ │ ├── name: :b
+ │ └── depth: 0
+ ├── opening_loc: (3,0)-(3,1) = "["
+ └── closing_loc: (3,5)-(3,6) = "]"
diff --git a/test/prism/snapshots/repeat_parameters.txt b/test/prism/snapshots/repeat_parameters.txt
new file mode 100644
index 0000000000..fd98294ce6
--- /dev/null
+++ b/test/prism/snapshots/repeat_parameters.txt
@@ -0,0 +1,473 @@
+@ ProgramNode (location: (1,0)-(38,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(38,3))
+ └── body: (length: 13)
+ ├── @ DefNode (location: (1,0)-(2,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (1,4)-(1,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,8)-(1,12))
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ RequiredParameterNode (location: (1,8)-(1,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ └── @ RequiredParameterNode (location: (1,11)-(1,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :_
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:a, :_]
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (1,7)-(1,8) = "("
+ │ ├── rparen_loc: (1,12)-(1,13) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (2,0)-(2,3) = "end"
+ ├── @ DefNode (location: (4,0)-(5,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (4,4)-(4,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (4,8)-(4,15))
+ │ │ ├── requireds: (length: 3)
+ │ │ │ ├── @ RequiredParameterNode (location: (4,8)-(4,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── @ RequiredParameterNode (location: (4,11)-(4,12))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :_
+ │ │ │ └── @ RequiredParameterNode (location: (4,14)-(4,15))
+ │ │ │ ├── flags: repeated_parameter
+ │ │ │ └── name: :_
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:a, :_]
+ │ ├── def_keyword_loc: (4,0)-(4,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (4,7)-(4,8) = "("
+ │ ├── rparen_loc: (4,15)-(4,16) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (5,0)-(5,3) = "end"
+ ├── @ DefNode (location: (7,0)-(8,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (7,4)-(7,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (7,8)-(7,19))
+ │ │ ├── requireds: (length: 4)
+ │ │ │ ├── @ RequiredParameterNode (location: (7,8)-(7,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── @ RequiredParameterNode (location: (7,11)-(7,12))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :_
+ │ │ │ ├── @ RequiredParameterNode (location: (7,14)-(7,15))
+ │ │ │ │ ├── flags: repeated_parameter
+ │ │ │ │ └── name: :_
+ │ │ │ └── @ RequiredParameterNode (location: (7,17)-(7,19))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :_b
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:a, :_, :_b]
+ │ ├── def_keyword_loc: (7,0)-(7,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (7,7)-(7,8) = "("
+ │ ├── rparen_loc: (7,19)-(7,20) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (8,0)-(8,3) = "end"
+ ├── @ DefNode (location: (10,0)-(11,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (10,4)-(10,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (10,8)-(10,23))
+ │ │ ├── requireds: (length: 5)
+ │ │ │ ├── @ RequiredParameterNode (location: (10,8)-(10,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── @ RequiredParameterNode (location: (10,11)-(10,12))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :_
+ │ │ │ ├── @ RequiredParameterNode (location: (10,14)-(10,15))
+ │ │ │ │ ├── flags: repeated_parameter
+ │ │ │ │ └── name: :_
+ │ │ │ ├── @ RequiredParameterNode (location: (10,17)-(10,19))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :_b
+ │ │ │ └── @ RequiredParameterNode (location: (10,21)-(10,23))
+ │ │ │ ├── flags: repeated_parameter
+ │ │ │ └── name: :_b
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:a, :_, :_b]
+ │ ├── def_keyword_loc: (10,0)-(10,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (10,7)-(10,8) = "("
+ │ ├── rparen_loc: (10,23)-(10,24) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (11,0)-(11,3) = "end"
+ ├── @ DefNode (location: (13,0)-(14,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (13,4)-(13,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (13,8)-(13,35))
+ │ │ ├── requireds: (length: 3)
+ │ │ │ ├── @ RequiredParameterNode (location: (13,8)-(13,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── @ MultiTargetNode (location: (13,11)-(13,22))
+ │ │ │ │ ├── lefts: (length: 1)
+ │ │ │ │ │ └── @ RequiredParameterNode (location: (13,12)-(13,13))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :b
+ │ │ │ │ ├── rest:
+ │ │ │ │ │ @ SplatNode (location: (13,15)-(13,18))
+ │ │ │ │ │ ├── operator_loc: (13,15)-(13,16) = "*"
+ │ │ │ │ │ └── expression:
+ │ │ │ │ │ @ RequiredParameterNode (location: (13,16)-(13,18))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :_c
+ │ │ │ │ ├── rights: (length: 1)
+ │ │ │ │ │ └── @ RequiredParameterNode (location: (13,20)-(13,21))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :d
+ │ │ │ │ ├── lparen_loc: (13,11)-(13,12) = "("
+ │ │ │ │ └── rparen_loc: (13,21)-(13,22) = ")"
+ │ │ │ └── @ MultiTargetNode (location: (13,24)-(13,35))
+ │ │ │ ├── lefts: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (13,25)-(13,26))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :e
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (13,28)-(13,31))
+ │ │ │ │ ├── operator_loc: (13,28)-(13,29) = "*"
+ │ │ │ │ └── expression:
+ │ │ │ │ @ RequiredParameterNode (location: (13,29)-(13,31))
+ │ │ │ │ ├── flags: repeated_parameter
+ │ │ │ │ └── name: :_c
+ │ │ │ ├── rights: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (13,33)-(13,34))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :f
+ │ │ │ ├── lparen_loc: (13,24)-(13,25) = "("
+ │ │ │ └── rparen_loc: (13,34)-(13,35) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:a, :b, :_c, :d, :e, :f]
+ │ ├── def_keyword_loc: (13,0)-(13,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (13,7)-(13,8) = "("
+ │ ├── rparen_loc: (13,35)-(13,36) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (14,0)-(14,3) = "end"
+ ├── @ DefNode (location: (16,0)-(17,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (16,4)-(16,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (16,8)-(16,20))
+ │ │ ├── requireds: (length: 4)
+ │ │ │ ├── @ RequiredParameterNode (location: (16,8)-(16,10))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :_a
+ │ │ │ ├── @ RequiredParameterNode (location: (16,12)-(16,14))
+ │ │ │ │ ├── flags: repeated_parameter
+ │ │ │ │ └── name: :_a
+ │ │ │ ├── @ RequiredParameterNode (location: (16,16)-(16,17))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :b
+ │ │ │ └── @ RequiredParameterNode (location: (16,19)-(16,20))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :c
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:_a, :b, :c]
+ │ ├── def_keyword_loc: (16,0)-(16,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (16,7)-(16,8) = "("
+ │ ├── rparen_loc: (16,20)-(16,21) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (17,0)-(17,3) = "end"
+ ├── @ DefNode (location: (19,0)-(20,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (19,4)-(19,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (19,8)-(19,32))
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ MultiTargetNode (location: (19,8)-(19,19))
+ │ │ │ │ ├── lefts: (length: 1)
+ │ │ │ │ │ └── @ RequiredParameterNode (location: (19,9)-(19,10))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :a
+ │ │ │ │ ├── rest:
+ │ │ │ │ │ @ SplatNode (location: (19,12)-(19,15))
+ │ │ │ │ │ ├── operator_loc: (19,12)-(19,13) = "*"
+ │ │ │ │ │ └── expression:
+ │ │ │ │ │ @ RequiredParameterNode (location: (19,13)-(19,15))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :_b
+ │ │ │ │ ├── rights: (length: 1)
+ │ │ │ │ │ └── @ RequiredParameterNode (location: (19,17)-(19,18))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :c
+ │ │ │ │ ├── lparen_loc: (19,8)-(19,9) = "("
+ │ │ │ │ └── rparen_loc: (19,18)-(19,19) = ")"
+ │ │ │ └── @ MultiTargetNode (location: (19,21)-(19,32))
+ │ │ │ ├── lefts: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (19,22)-(19,23))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :d
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (19,25)-(19,28))
+ │ │ │ │ ├── operator_loc: (19,25)-(19,26) = "*"
+ │ │ │ │ └── expression:
+ │ │ │ │ @ RequiredParameterNode (location: (19,26)-(19,28))
+ │ │ │ │ ├── flags: repeated_parameter
+ │ │ │ │ └── name: :_b
+ │ │ │ ├── rights: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (19,30)-(19,31))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :e
+ │ │ │ ├── lparen_loc: (19,21)-(19,22) = "("
+ │ │ │ └── rparen_loc: (19,31)-(19,32) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:a, :_b, :c, :d, :e]
+ │ ├── def_keyword_loc: (19,0)-(19,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (19,7)-(19,8) = "("
+ │ ├── rparen_loc: (19,32)-(19,33) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (20,0)-(20,3) = "end"
+ ├── @ DefNode (location: (22,0)-(23,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (22,4)-(22,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (22,8)-(22,22))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 2)
+ │ │ │ ├── @ OptionalParameterNode (location: (22,8)-(22,14))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :_a
+ │ │ │ │ ├── name_loc: (22,8)-(22,10) = "_a"
+ │ │ │ │ ├── operator_loc: (22,11)-(22,12) = "="
+ │ │ │ │ └── value:
+ │ │ │ │ @ IntegerNode (location: (22,13)-(22,14))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── @ OptionalParameterNode (location: (22,16)-(22,22))
+ │ │ │ ├── flags: repeated_parameter
+ │ │ │ ├── name: :_a
+ │ │ │ ├── name_loc: (22,16)-(22,18) = "_a"
+ │ │ │ ├── operator_loc: (22,19)-(22,20) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (22,21)-(22,22))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:_a]
+ │ ├── def_keyword_loc: (22,0)-(22,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (22,7)-(22,8) = "("
+ │ ├── rparen_loc: (22,22)-(22,23) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (23,0)-(23,3) = "end"
+ ├── @ DefNode (location: (25,0)-(26,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (25,4)-(25,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (25,8)-(25,16))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 2)
+ │ │ │ ├── @ RequiredKeywordParameterNode (location: (25,8)-(25,11))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :_a
+ │ │ │ │ └── name_loc: (25,8)-(25,11) = "_a:"
+ │ │ │ └── @ RequiredKeywordParameterNode (location: (25,13)-(25,16))
+ │ │ │ ├── flags: repeated_parameter
+ │ │ │ ├── name: :_a
+ │ │ │ └── name_loc: (25,13)-(25,16) = "_a:"
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:_a]
+ │ ├── def_keyword_loc: (25,0)-(25,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (25,7)-(25,8) = "("
+ │ ├── rparen_loc: (25,16)-(25,17) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (26,0)-(26,3) = "end"
+ ├── @ DefNode (location: (28,0)-(29,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (28,4)-(28,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (28,8)-(28,20))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 2)
+ │ │ │ ├── @ OptionalKeywordParameterNode (location: (28,8)-(28,13))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :_a
+ │ │ │ │ ├── name_loc: (28,8)-(28,11) = "_a:"
+ │ │ │ │ └── value:
+ │ │ │ │ @ IntegerNode (location: (28,12)-(28,13))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── @ OptionalKeywordParameterNode (location: (28,15)-(28,20))
+ │ │ │ ├── flags: repeated_parameter
+ │ │ │ ├── name: :_a
+ │ │ │ ├── name_loc: (28,15)-(28,18) = "_a:"
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (28,19)-(28,20))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:_a]
+ │ ├── def_keyword_loc: (28,0)-(28,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (28,7)-(28,8) = "("
+ │ ├── rparen_loc: (28,20)-(28,21) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (29,0)-(29,3) = "end"
+ ├── @ DefNode (location: (31,0)-(32,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (31,4)-(31,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (31,8)-(31,16))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (31,8)-(31,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :_a
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ KeywordRestParameterNode (location: (31,12)-(31,16))
+ │ │ │ ├── flags: repeated_parameter
+ │ │ │ ├── name: :_a
+ │ │ │ ├── name_loc: (31,14)-(31,16) = "_a"
+ │ │ │ └── operator_loc: (31,12)-(31,14) = "**"
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:_a]
+ │ ├── def_keyword_loc: (31,0)-(31,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (31,7)-(31,8) = "("
+ │ ├── rparen_loc: (31,16)-(31,17) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (32,0)-(32,3) = "end"
+ ├── @ DefNode (location: (34,0)-(35,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (34,4)-(34,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (34,8)-(34,15))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (34,8)-(34,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :_a
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (34,12)-(34,15))
+ │ │ ├── flags: repeated_parameter
+ │ │ ├── name: :_a
+ │ │ ├── name_loc: (34,13)-(34,15) = "_a"
+ │ │ └── operator_loc: (34,12)-(34,13) = "&"
+ │ ├── body: ∅
+ │ ├── locals: [:_a]
+ │ ├── def_keyword_loc: (34,0)-(34,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (34,7)-(34,8) = "("
+ │ ├── rparen_loc: (34,15)-(34,16) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (35,0)-(35,3) = "end"
+ └── @ DefNode (location: (37,0)-(38,3))
+ ├── name: :foo
+ ├── name_loc: (37,4)-(37,7) = "foo"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (37,8)-(37,15))
+ │ ├── requireds: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (37,8)-(37,10))
+ │ │ ├── flags: ∅
+ │ │ └── name: :_a
+ │ ├── optionals: (length: 0)
+ │ ├── rest:
+ │ │ @ RestParameterNode (location: (37,12)-(37,15))
+ │ │ ├── flags: repeated_parameter
+ │ │ ├── name: :_a
+ │ │ ├── name_loc: (37,13)-(37,15) = "_a"
+ │ │ └── operator_loc: (37,12)-(37,13) = "*"
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:_a]
+ ├── def_keyword_loc: (37,0)-(37,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (37,7)-(37,8) = "("
+ ├── rparen_loc: (37,15)-(37,16) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (38,0)-(38,3) = "end"
diff --git a/test/prism/snapshots/rescue.txt b/test/prism/snapshots/rescue.txt
new file mode 100644
index 0000000000..2bdbfdaff3
--- /dev/null
+++ b/test/prism/snapshots/rescue.txt
@@ -0,0 +1,528 @@
+@ ProgramNode (location: (1,0)-(35,18))
+├── locals: [:a, :z]
+└── statements:
+ @ StatementsNode (location: (1,0)-(35,18))
+ └── body: (length: 14)
+ ├── @ RescueModifierNode (location: (1,0)-(1,14))
+ │ ├── expression:
+ │ │ @ CallNode (location: (1,0)-(1,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (1,4)-(1,10) = "rescue"
+ │ └── rescue_expression:
+ │ @ NilNode (location: (1,11)-(1,14))
+ ├── @ RescueModifierNode (location: (3,0)-(4,3))
+ │ ├── expression:
+ │ │ @ CallNode (location: (3,0)-(3,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (3,0)-(3,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (3,4)-(3,10) = "rescue"
+ │ └── rescue_expression:
+ │ @ NilNode (location: (4,0)-(4,3))
+ ├── @ CallNode (location: (6,0)-(6,24))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (6,0)-(6,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (6,4)-(6,24))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (6,6)-(6,22))
+ │ │ └── body: (length: 1)
+ │ │ └── @ RescueModifierNode (location: (6,6)-(6,22))
+ │ │ ├── expression:
+ │ │ │ @ BreakNode (location: (6,6)-(6,11))
+ │ │ │ ├── arguments: ∅
+ │ │ │ └── keyword_loc: (6,6)-(6,11) = "break"
+ │ │ ├── keyword_loc: (6,12)-(6,18) = "rescue"
+ │ │ └── rescue_expression:
+ │ │ @ NilNode (location: (6,19)-(6,22))
+ │ ├── opening_loc: (6,4)-(6,5) = "{"
+ │ └── closing_loc: (6,23)-(6,24) = "}"
+ ├── @ CallNode (location: (8,0)-(8,23))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (8,0)-(8,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (8,4)-(8,23))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (8,6)-(8,21))
+ │ │ └── body: (length: 1)
+ │ │ └── @ RescueModifierNode (location: (8,6)-(8,21))
+ │ │ ├── expression:
+ │ │ │ @ NextNode (location: (8,6)-(8,10))
+ │ │ │ ├── arguments: ∅
+ │ │ │ └── keyword_loc: (8,6)-(8,10) = "next"
+ │ │ ├── keyword_loc: (8,11)-(8,17) = "rescue"
+ │ │ └── rescue_expression:
+ │ │ @ NilNode (location: (8,18)-(8,21))
+ │ ├── opening_loc: (8,4)-(8,5) = "{"
+ │ └── closing_loc: (8,22)-(8,23) = "}"
+ ├── @ RescueModifierNode (location: (10,0)-(10,17))
+ │ ├── expression:
+ │ │ @ ReturnNode (location: (10,0)-(10,6))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (10,0)-(10,6) = "return"
+ │ │ └── arguments: ∅
+ │ ├── keyword_loc: (10,7)-(10,13) = "rescue"
+ │ └── rescue_expression:
+ │ @ NilNode (location: (10,14)-(10,17))
+ ├── @ RescueModifierNode (location: (12,0)-(12,19))
+ │ ├── expression:
+ │ │ @ CallNode (location: (12,0)-(12,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (12,0)-(12,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (12,4)-(12,10) = "rescue"
+ │ └── rescue_expression:
+ │ @ OrNode (location: (12,11)-(12,19))
+ │ ├── left:
+ │ │ @ NilNode (location: (12,11)-(12,14))
+ │ ├── right:
+ │ │ @ IntegerNode (location: (12,18)-(12,19))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (12,15)-(12,17) = "||"
+ ├── @ RescueModifierNode (location: (14,0)-(14,22))
+ │ ├── expression:
+ │ │ @ CallNode (location: (14,0)-(14,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (14,0)-(14,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (14,4)-(14,10) = "rescue"
+ │ └── rescue_expression:
+ │ @ IfNode (location: (14,11)-(14,22))
+ │ ├── if_keyword_loc: ∅
+ │ ├── predicate:
+ │ │ @ NilNode (location: (14,11)-(14,14))
+ │ ├── then_keyword_loc: (14,15)-(14,16) = "?"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (14,17)-(14,18))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (14,17)-(14,18))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (14,19)-(14,22))
+ │ │ ├── else_keyword_loc: (14,19)-(14,20) = ":"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (14,21)-(14,22))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (14,21)-(14,22))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── end_keyword_loc: ∅
+ │ └── end_keyword_loc: ∅
+ ├── @ BeginNode (location: (16,0)-(16,24))
+ │ ├── begin_keyword_loc: (16,0)-(16,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (16,7)-(16,8))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (16,7)-(16,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (16,7)-(16,8) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (16,10)-(16,19))
+ │ │ ├── keyword_loc: (16,10)-(16,16) = "rescue"
+ │ │ ├── exceptions: (length: 1)
+ │ │ │ └── @ SplatNode (location: (16,17)-(16,19))
+ │ │ │ ├── operator_loc: (16,17)-(16,18) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ CallNode (location: (16,18)-(16,19))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (16,18)-(16,19) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (16,21)-(16,24) = "end"
+ ├── @ CallNode (location: (18,0)-(20,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (18,0)-(18,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (18,4)-(20,3))
+ │ ├── locals: [:x]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (18,7)-(18,10))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (18,8)-(18,9))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (18,8)-(18,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :x
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (18,7)-(18,8) = "|"
+ │ │ └── closing_loc: (18,9)-(18,10) = "|"
+ │ ├── body:
+ │ │ @ StatementsNode (location: (19,2)-(19,40))
+ │ │ └── body: (length: 1)
+ │ │ └── @ RescueModifierNode (location: (19,2)-(19,40))
+ │ │ ├── expression:
+ │ │ │ @ CallNode (location: (19,2)-(19,8))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (19,2)-(19,5) = "bar"
+ │ │ │ ├── opening_loc: (19,5)-(19,6) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (19,6)-(19,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (19,6)-(19,7))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :y
+ │ │ │ │ ├── message_loc: (19,6)-(19,7) = "y"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: (19,7)-(19,8) = ")"
+ │ │ │ └── block: ∅
+ │ │ ├── keyword_loc: (19,9)-(19,15) = "rescue"
+ │ │ └── rescue_expression:
+ │ │ @ CallNode (location: (19,16)-(19,40))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :ArgumentError
+ │ │ ├── message_loc: (19,16)-(19,29) = "ArgumentError"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (19,30)-(19,40))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ CallNode (location: (19,30)-(19,40))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :fail
+ │ │ │ ├── message_loc: (19,30)-(19,34) = "fail"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (19,35)-(19,40))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ StringNode (location: (19,35)-(19,40))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: (19,35)-(19,36) = "\""
+ │ │ │ │ ├── content_loc: (19,36)-(19,39) = "baz"
+ │ │ │ │ ├── closing_loc: (19,39)-(19,40) = "\""
+ │ │ │ │ └── unescaped: "baz"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (18,4)-(18,6) = "do"
+ │ └── closing_loc: (20,0)-(20,3) = "end"
+ ├── @ IfNode (location: (22,0)-(24,3))
+ │ ├── if_keyword_loc: (22,0)-(22,2) = "if"
+ │ ├── predicate:
+ │ │ @ LocalVariableWriteNode (location: (22,3)-(22,21))
+ │ │ ├── name: :a
+ │ │ ├── depth: 0
+ │ │ ├── name_loc: (22,3)-(22,4) = "a"
+ │ │ ├── value:
+ │ │ │ @ RescueModifierNode (location: (22,7)-(22,21))
+ │ │ │ ├── expression:
+ │ │ │ │ @ CallNode (location: (22,7)-(22,10))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :foo
+ │ │ │ │ ├── message_loc: (22,7)-(22,10) = "foo"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── keyword_loc: (22,11)-(22,17) = "rescue"
+ │ │ │ └── rescue_expression:
+ │ │ │ @ NilNode (location: (22,18)-(22,21))
+ │ │ └── operator_loc: (22,5)-(22,6) = "="
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (23,2)-(23,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (23,2)-(23,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (23,2)-(23,5) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (24,0)-(24,3) = "end"
+ ├── @ DefNode (location: (26,0)-(26,44))
+ │ ├── name: :some_method
+ │ ├── name_loc: (26,4)-(26,15) = "some_method"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (26,18)-(26,44))
+ │ │ └── body: (length: 1)
+ │ │ └── @ RescueModifierNode (location: (26,18)-(26,44))
+ │ │ ├── expression:
+ │ │ │ @ CallNode (location: (26,18)-(26,33))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :other_method
+ │ │ │ ├── message_loc: (26,18)-(26,30) = "other_method"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (26,31)-(26,33))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (26,31)-(26,33))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 42
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── keyword_loc: (26,34)-(26,40) = "rescue"
+ │ │ └── rescue_expression:
+ │ │ @ NilNode (location: (26,41)-(26,44))
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (26,0)-(26,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: (26,16)-(26,17) = "="
+ │ └── end_keyword_loc: ∅
+ ├── @ DefNode (location: (28,0)-(31,3))
+ │ ├── name: :a
+ │ ├── name_loc: (28,4)-(28,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (28,0)-(31,3))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (29,2)-(29,6))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (29,2)-(29,6))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (29,2)-(29,3) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (29,4)-(29,6))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ KeywordHashNode (location: (29,4)-(29,6))
+ │ │ │ │ ├── flags: symbol_keys
+ │ │ │ │ └── elements: (length: 1)
+ │ │ │ │ └── @ AssocNode (location: (29,4)-(29,6))
+ │ │ │ │ ├── key:
+ │ │ │ │ │ @ SymbolNode (location: (29,4)-(29,6))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── value_loc: (29,4)-(29,5) = "b"
+ │ │ │ │ │ ├── closing_loc: (29,5)-(29,6) = ":"
+ │ │ │ │ │ └── unescaped: "b"
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ ImplicitNode (location: (29,4)-(29,6))
+ │ │ │ │ │ └── value:
+ │ │ │ │ │ @ CallNode (location: (29,4)-(29,6))
+ │ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :b
+ │ │ │ │ │ ├── message_loc: (29,4)-(29,5) = "b"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ └── operator_loc: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── rescue_clause:
+ │ │ │ @ RescueNode (location: (30,0)-(30,6))
+ │ │ │ ├── keyword_loc: (30,0)-(30,6) = "rescue"
+ │ │ │ ├── exceptions: (length: 0)
+ │ │ │ ├── operator_loc: ∅
+ │ │ │ ├── reference: ∅
+ │ │ │ ├── statements: ∅
+ │ │ │ └── consequent: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause: ∅
+ │ │ └── end_keyword_loc: (31,0)-(31,3) = "end"
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (28,0)-(28,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (31,0)-(31,3) = "end"
+ ├── @ RescueModifierNode (location: (33,0)-(33,21))
+ │ ├── expression:
+ │ │ @ IfNode (location: (33,0)-(33,10))
+ │ │ ├── if_keyword_loc: (33,4)-(33,6) = "if"
+ │ │ ├── predicate:
+ │ │ │ @ CallNode (location: (33,7)-(33,10))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (33,7)-(33,10) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── then_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (33,0)-(33,3))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (33,0)-(33,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (33,0)-(33,3) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── consequent: ∅
+ │ │ └── end_keyword_loc: ∅
+ │ ├── keyword_loc: (33,11)-(33,17) = "rescue"
+ │ └── rescue_expression:
+ │ @ CallNode (location: (33,18)-(33,21))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :baz
+ │ ├── message_loc: (33,18)-(33,21) = "baz"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ LocalVariableWriteNode (location: (35,0)-(35,18))
+ ├── name: :z
+ ├── depth: 0
+ ├── name_loc: (35,0)-(35,1) = "z"
+ ├── value:
+ │ @ RescueModifierNode (location: (35,4)-(35,18))
+ │ ├── expression:
+ │ │ @ CallNode (location: (35,4)-(35,7))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :x
+ │ │ ├── message_loc: (35,4)-(35,5) = "x"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (35,6)-(35,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ CallNode (location: (35,6)-(35,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :y
+ │ │ │ ├── message_loc: (35,6)-(35,7) = "y"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (35,8)-(35,14) = "rescue"
+ │ └── rescue_expression:
+ │ @ CallNode (location: (35,15)-(35,18))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :c
+ │ ├── message_loc: (35,15)-(35,16) = "c"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (35,17)-(35,18))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (35,17)-(35,18))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :d
+ │ │ ├── message_loc: (35,17)-(35,18) = "d"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── operator_loc: (35,2)-(35,3) = "="
diff --git a/test/prism/snapshots/return.txt b/test/prism/snapshots/return.txt
new file mode 100644
index 0000000000..0dd26281c1
--- /dev/null
+++ b/test/prism/snapshots/return.txt
@@ -0,0 +1,165 @@
+@ ProgramNode (location: (1,0)-(23,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(23,9))
+ └── body: (length: 10)
+ ├── @ ReturnNode (location: (1,0)-(1,6))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (1,0)-(1,6) = "return"
+ │ └── arguments: ∅
+ ├── @ ReturnNode (location: (3,0)-(3,20))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (3,0)-(3,6) = "return"
+ │ └── arguments:
+ │ @ ArgumentsNode (location: (3,7)-(3,20))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 3)
+ │ ├── @ ParenthesesNode (location: (3,7)-(3,10))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (3,8)-(3,9))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (3,8)-(3,9))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── opening_loc: (3,7)-(3,8) = "("
+ │ │ └── closing_loc: (3,9)-(3,10) = ")"
+ │ ├── @ ParenthesesNode (location: (3,12)-(3,15))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (3,13)-(3,14))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (3,13)-(3,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── opening_loc: (3,12)-(3,13) = "("
+ │ │ └── closing_loc: (3,14)-(3,15) = ")"
+ │ └── @ ParenthesesNode (location: (3,17)-(3,20))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (3,18)-(3,19))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (3,18)-(3,19))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ ├── opening_loc: (3,17)-(3,18) = "("
+ │ └── closing_loc: (3,19)-(3,20) = ")"
+ ├── @ ReturnNode (location: (5,0)-(5,9))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (5,0)-(5,6) = "return"
+ │ └── arguments:
+ │ @ ArgumentsNode (location: (5,7)-(5,9))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ SplatNode (location: (5,7)-(5,9))
+ │ ├── operator_loc: (5,7)-(5,8) = "*"
+ │ └── expression:
+ │ @ IntegerNode (location: (5,8)-(5,9))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ ReturnNode (location: (7,0)-(7,8))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (7,0)-(7,6) = "return"
+ │ └── arguments:
+ │ @ ArgumentsNode (location: (7,7)-(7,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (7,7)-(7,8))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ ReturnNode (location: (9,0)-(10,1))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (9,0)-(9,6) = "return"
+ │ └── arguments:
+ │ @ ArgumentsNode (location: (9,7)-(10,1))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 3)
+ │ ├── @ IntegerNode (location: (9,7)-(9,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── @ IntegerNode (location: (9,10)-(9,11))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ └── @ IntegerNode (location: (10,0)-(10,1))
+ │ ├── flags: decimal
+ │ └── value: 3
+ ├── @ ReturnNode (location: (12,0)-(12,14))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (12,0)-(12,6) = "return"
+ │ └── arguments:
+ │ @ ArgumentsNode (location: (12,7)-(12,14))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 3)
+ │ ├── @ IntegerNode (location: (12,7)-(12,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── @ IntegerNode (location: (12,10)-(12,11))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ └── @ IntegerNode (location: (12,13)-(12,14))
+ │ ├── flags: decimal
+ │ └── value: 3
+ ├── @ ReturnNode (location: (14,0)-(14,16))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (14,0)-(14,6) = "return"
+ │ └── arguments:
+ │ @ ArgumentsNode (location: (14,7)-(14,16))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ ArrayNode (location: (14,7)-(14,16))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 3)
+ │ │ ├── @ IntegerNode (location: (14,8)-(14,9))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── @ IntegerNode (location: (14,11)-(14,12))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── @ IntegerNode (location: (14,14)-(14,15))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ ├── opening_loc: (14,7)-(14,8) = "["
+ │ └── closing_loc: (14,15)-(14,16) = "]"
+ ├── @ ReturnNode (location: (16,0)-(19,1))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (16,0)-(16,6) = "return"
+ │ └── arguments:
+ │ @ ArgumentsNode (location: (16,6)-(19,1))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ ParenthesesNode (location: (16,6)-(19,1))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (17,2)-(18,3))
+ │ │ └── body: (length: 2)
+ │ │ ├── @ IntegerNode (location: (17,2)-(17,3))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (18,2)-(18,3))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── opening_loc: (16,6)-(16,7) = "("
+ │ └── closing_loc: (19,0)-(19,1) = ")"
+ ├── @ ReturnNode (location: (21,0)-(21,8))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (21,0)-(21,6) = "return"
+ │ └── arguments:
+ │ @ ArgumentsNode (location: (21,6)-(21,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ ParenthesesNode (location: (21,6)-(21,8))
+ │ ├── body: ∅
+ │ ├── opening_loc: (21,6)-(21,7) = "("
+ │ └── closing_loc: (21,7)-(21,8) = ")"
+ └── @ ReturnNode (location: (23,0)-(23,9))
+ ├── flags: ∅
+ ├── keyword_loc: (23,0)-(23,6) = "return"
+ └── arguments:
+ @ ArgumentsNode (location: (23,6)-(23,9))
+ ├── flags: ∅
+ └── arguments: (length: 1)
+ └── @ ParenthesesNode (location: (23,6)-(23,9))
+ ├── body:
+ │ @ StatementsNode (location: (23,7)-(23,8))
+ │ └── body: (length: 1)
+ │ └── @ IntegerNode (location: (23,7)-(23,8))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── opening_loc: (23,6)-(23,7) = "("
+ └── closing_loc: (23,8)-(23,9) = ")"
diff --git a/test/prism/snapshots/seattlerb/BEGIN.txt b/test/prism/snapshots/seattlerb/BEGIN.txt
new file mode 100644
index 0000000000..246f39cbba
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/BEGIN.txt
@@ -0,0 +1,15 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ PreExecutionNode (location: (1,0)-(1,12))
+ ├── statements:
+ │ @ StatementsNode (location: (1,8)-(1,10))
+ │ └── body: (length: 1)
+ │ └── @ IntegerNode (location: (1,8)-(1,10))
+ │ ├── flags: decimal
+ │ └── value: 42
+ ├── keyword_loc: (1,0)-(1,5) = "BEGIN"
+ ├── opening_loc: (1,6)-(1,7) = "{"
+ └── closing_loc: (1,11)-(1,12) = "}"
diff --git a/test/prism/snapshots/seattlerb/TestRubyParserShared.txt b/test/prism/snapshots/seattlerb/TestRubyParserShared.txt
new file mode 100644
index 0000000000..fabc92e477
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/TestRubyParserShared.txt
@@ -0,0 +1,361 @@
+@ ProgramNode (location: (1,0)-(92,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(92,1))
+ └── body: (length: 16)
+ ├── @ ArrayNode (location: (1,0)-(4,1))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 0)
+ │ ├── opening_loc: (1,0)-(1,3) = "%I["
+ │ └── closing_loc: (4,0)-(4,1) = "]"
+ ├── @ ArrayNode (location: (6,0)-(9,1))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ SymbolNode (location: (7,0)-(7,5))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (7,0)-(7,5) = "line2"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "line2"
+ │ │ └── @ SymbolNode (location: (8,0)-(8,5))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (8,0)-(8,5) = "line3"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "line3"
+ │ ├── opening_loc: (6,0)-(6,3) = "%I["
+ │ └── closing_loc: (9,0)-(9,1) = "]"
+ ├── @ ArrayNode (location: (11,0)-(14,1))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 0)
+ │ ├── opening_loc: (11,0)-(11,3) = "%W["
+ │ └── closing_loc: (14,0)-(14,1) = "]"
+ ├── @ ArrayNode (location: (16,0)-(19,1))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ StringNode (location: (17,0)-(17,5))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (17,0)-(17,5) = "line2"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "line2"
+ │ │ └── @ StringNode (location: (18,0)-(18,5))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (18,0)-(18,5) = "line3"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "line3"
+ │ ├── opening_loc: (16,0)-(16,3) = "%W["
+ │ └── closing_loc: (19,0)-(19,1) = "]"
+ ├── @ ArrayNode (location: (21,0)-(24,1))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 0)
+ │ ├── opening_loc: (21,0)-(21,3) = "%i["
+ │ └── closing_loc: (24,0)-(24,1) = "]"
+ ├── @ ArrayNode (location: (26,0)-(29,1))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ SymbolNode (location: (27,0)-(27,5))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (27,0)-(27,5) = "line2"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "line2"
+ │ │ └── @ SymbolNode (location: (28,0)-(28,5))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (28,0)-(28,5) = "line3"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "line3"
+ │ ├── opening_loc: (26,0)-(26,3) = "%i["
+ │ └── closing_loc: (29,0)-(29,1) = "]"
+ ├── @ RegularExpressionNode (location: (31,0)-(34,1))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (31,0)-(31,3) = "%r["
+ │ ├── content_loc: (31,3)-(34,0) = "\n\n\n"
+ │ ├── closing_loc: (34,0)-(34,1) = "]"
+ │ └── unescaped: "\n\n\n"
+ ├── @ ArrayNode (location: (36,0)-(39,1))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 0)
+ │ ├── opening_loc: (36,0)-(36,3) = "%w["
+ │ └── closing_loc: (39,0)-(39,1) = "]"
+ ├── @ ArrayNode (location: (41,0)-(44,1))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ StringNode (location: (42,0)-(42,5))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (42,0)-(42,5) = "line2"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "line2"
+ │ │ └── @ StringNode (location: (43,0)-(43,5))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (43,0)-(43,5) = "line3"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "line3"
+ │ ├── opening_loc: (41,0)-(41,3) = "%w["
+ │ └── closing_loc: (44,0)-(44,1) = "]"
+ ├── @ ArrayNode (location: (46,0)-(49,1))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ SymbolNode (location: (47,0)-(47,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (47,0)-(47,1) = ":"
+ │ │ │ ├── value_loc: (47,1)-(47,6) = "line2"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "line2"
+ │ │ └── @ SymbolNode (location: (48,0)-(48,6))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (48,0)-(48,1) = ":"
+ │ │ ├── value_loc: (48,1)-(48,6) = "line3"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "line3"
+ │ ├── opening_loc: (46,0)-(46,1) = "["
+ │ └── closing_loc: (49,0)-(49,1) = "]"
+ ├── @ ClassNode (location: (51,0)-(56,3))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (51,0)-(51,5) = "class"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (51,6)-(51,7))
+ │ │ └── name: :X
+ │ ├── inheritance_operator_loc: ∅
+ │ ├── superclass: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (52,2)-(55,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ DefNode (location: (52,2)-(55,5))
+ │ │ ├── name: :y
+ │ │ ├── name_loc: (52,11)-(52,12) = "y"
+ │ │ ├── receiver:
+ │ │ │ @ SelfNode (location: (52,6)-(52,10))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (52,13)-(53,9))
+ │ │ │ ├── requireds: (length: 2)
+ │ │ │ │ ├── @ RequiredParameterNode (location: (52,13)-(52,14))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :a
+ │ │ │ │ └── @ RequiredParameterNode (location: (53,8)-(53,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :b
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (54,4)-(54,9))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (54,4)-(54,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ LocalVariableReadNode (location: (54,4)-(54,5))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :+
+ │ │ │ ├── message_loc: (54,6)-(54,7) = "+"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (54,8)-(54,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ LocalVariableReadNode (location: (54,8)-(54,9))
+ │ │ │ │ ├── name: :b
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: [:a, :b]
+ │ │ ├── def_keyword_loc: (52,2)-(52,5) = "def"
+ │ │ ├── operator_loc: (52,10)-(52,11) = "."
+ │ │ ├── lparen_loc: (52,12)-(52,13) = "("
+ │ │ ├── rparen_loc: (53,9)-(53,10) = ")"
+ │ │ ├── equal_loc: ∅
+ │ │ └── end_keyword_loc: (55,2)-(55,5) = "end"
+ │ ├── end_keyword_loc: (56,0)-(56,3) = "end"
+ │ └── name: :X
+ ├── @ ClassNode (location: (59,0)-(63,3))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (59,0)-(59,5) = "class"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (59,6)-(59,7))
+ │ │ └── name: :X
+ │ ├── inheritance_operator_loc: ∅
+ │ ├── superclass: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (60,2)-(62,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ ClassNode (location: (60,2)-(62,5))
+ │ │ ├── locals: []
+ │ │ ├── class_keyword_loc: (60,2)-(60,7) = "class"
+ │ │ ├── constant_path:
+ │ │ │ @ ConstantReadNode (location: (60,8)-(60,9))
+ │ │ │ └── name: :Y
+ │ │ ├── inheritance_operator_loc: ∅
+ │ │ ├── superclass: ∅
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (61,4)-(61,10))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ ConstantWriteNode (location: (61,4)-(61,10))
+ │ │ │ ├── name: :Z
+ │ │ │ ├── name_loc: (61,4)-(61,5) = "Z"
+ │ │ │ ├── value:
+ │ │ │ │ @ IntegerNode (location: (61,8)-(61,10))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 42
+ │ │ │ └── operator_loc: (61,6)-(61,7) = "="
+ │ │ ├── end_keyword_loc: (62,2)-(62,5) = "end"
+ │ │ └── name: :Y
+ │ ├── end_keyword_loc: (63,0)-(63,3) = "end"
+ │ └── name: :X
+ ├── @ ClassNode (location: (66,0)-(71,3))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (66,0)-(66,5) = "class"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (66,6)-(66,7))
+ │ │ └── name: :X
+ │ ├── inheritance_operator_loc: ∅
+ │ ├── superclass: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (67,2)-(70,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ DefNode (location: (67,2)-(70,5))
+ │ │ ├── name: :y
+ │ │ ├── name_loc: (67,6)-(67,7) = "y"
+ │ │ ├── receiver: ∅
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (67,8)-(68,9))
+ │ │ │ ├── requireds: (length: 2)
+ │ │ │ │ ├── @ RequiredParameterNode (location: (67,8)-(67,9))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :a
+ │ │ │ │ └── @ RequiredParameterNode (location: (68,8)-(68,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :b
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (69,4)-(69,9))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (69,4)-(69,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ LocalVariableReadNode (location: (69,4)-(69,5))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :+
+ │ │ │ ├── message_loc: (69,6)-(69,7) = "+"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (69,8)-(69,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ LocalVariableReadNode (location: (69,8)-(69,9))
+ │ │ │ │ ├── name: :b
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: [:a, :b]
+ │ │ ├── def_keyword_loc: (67,2)-(67,5) = "def"
+ │ │ ├── operator_loc: ∅
+ │ │ ├── lparen_loc: (67,7)-(67,8) = "("
+ │ │ ├── rparen_loc: (68,9)-(68,10) = ")"
+ │ │ ├── equal_loc: ∅
+ │ │ └── end_keyword_loc: (70,2)-(70,5) = "end"
+ │ ├── end_keyword_loc: (71,0)-(71,3) = "end"
+ │ └── name: :X
+ ├── @ ModuleNode (location: (74,0)-(79,3))
+ │ ├── locals: []
+ │ ├── module_keyword_loc: (74,0)-(74,6) = "module"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (74,7)-(74,8))
+ │ │ └── name: :X
+ │ ├── body:
+ │ │ @ StatementsNode (location: (75,2)-(78,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ ConstantWriteNode (location: (75,2)-(78,3))
+ │ │ ├── name: :X
+ │ │ ├── name_loc: (75,2)-(75,3) = "X"
+ │ │ ├── value:
+ │ │ │ @ ArrayNode (location: (75,6)-(78,3))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 2)
+ │ │ │ │ ├── @ SymbolNode (location: (76,4)-(76,10))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: (76,4)-(76,5) = ":"
+ │ │ │ │ │ ├── value_loc: (76,5)-(76,10) = "line3"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "line3"
+ │ │ │ │ └── @ SymbolNode (location: (77,4)-(77,10))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (77,4)-(77,5) = ":"
+ │ │ │ │ ├── value_loc: (77,5)-(77,10) = "line4"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "line4"
+ │ │ │ ├── opening_loc: (75,6)-(75,7) = "["
+ │ │ │ └── closing_loc: (78,2)-(78,3) = "]"
+ │ │ └── operator_loc: (75,4)-(75,5) = "="
+ │ ├── end_keyword_loc: (79,0)-(79,3) = "end"
+ │ └── name: :X
+ ├── @ ModuleNode (location: (82,0)-(86,3))
+ │ ├── locals: []
+ │ ├── module_keyword_loc: (82,0)-(82,6) = "module"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (82,7)-(82,8))
+ │ │ └── name: :X
+ │ ├── body:
+ │ │ @ StatementsNode (location: (83,2)-(85,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ ModuleNode (location: (83,2)-(85,5))
+ │ │ ├── locals: []
+ │ │ ├── module_keyword_loc: (83,2)-(83,8) = "module"
+ │ │ ├── constant_path:
+ │ │ │ @ ConstantReadNode (location: (83,9)-(83,10))
+ │ │ │ └── name: :Y
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (84,4)-(84,10))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ ConstantWriteNode (location: (84,4)-(84,10))
+ │ │ │ ├── name: :Z
+ │ │ │ ├── name_loc: (84,4)-(84,5) = "Z"
+ │ │ │ ├── value:
+ │ │ │ │ @ IntegerNode (location: (84,8)-(84,10))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 42
+ │ │ │ └── operator_loc: (84,6)-(84,7) = "="
+ │ │ ├── end_keyword_loc: (85,2)-(85,5) = "end"
+ │ │ └── name: :Y
+ │ ├── end_keyword_loc: (86,0)-(86,3) = "end"
+ │ └── name: :X
+ └── @ CallNode (location: (89,0)-(92,1))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :x
+ ├── message_loc: (89,0)-(89,1) = "x"
+ ├── opening_loc: (89,1)-(89,2) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (90,0)-(91,6))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ SymbolNode (location: (90,0)-(90,6))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (90,0)-(90,1) = ":"
+ │ │ ├── value_loc: (90,1)-(90,6) = "line2"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "line2"
+ │ └── @ SymbolNode (location: (91,0)-(91,6))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (91,0)-(91,1) = ":"
+ │ ├── value_loc: (91,1)-(91,6) = "line3"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "line3"
+ ├── closing_loc: (92,0)-(92,1) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/__ENCODING__.txt b/test/prism/snapshots/seattlerb/__ENCODING__.txt
new file mode 100644
index 0000000000..1b223bd8fe
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/__ENCODING__.txt
@@ -0,0 +1,6 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ SourceEncodingNode (location: (1,0)-(1,12))
diff --git a/test/prism/snapshots/seattlerb/alias_gvar_backref.txt b/test/prism/snapshots/seattlerb/alias_gvar_backref.txt
new file mode 100644
index 0000000000..a2586423d7
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/alias_gvar_backref.txt
@@ -0,0 +1,13 @@
+@ ProgramNode (location: (1,0)-(1,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,15))
+ └── body: (length: 1)
+ └── @ AliasGlobalVariableNode (location: (1,0)-(1,15))
+ ├── new_name:
+ │ @ GlobalVariableReadNode (location: (1,6)-(1,12))
+ │ └── name: :$MATCH
+ ├── old_name:
+ │ @ BackReferenceReadNode (location: (1,13)-(1,15))
+ │ └── name: :$&
+ └── keyword_loc: (1,0)-(1,5) = "alias"
diff --git a/test/prism/snapshots/seattlerb/alias_resword.txt b/test/prism/snapshots/seattlerb/alias_resword.txt
new file mode 100644
index 0000000000..99ed696c68
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/alias_resword.txt
@@ -0,0 +1,21 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ AliasMethodNode (location: (1,0)-(1,12))
+ ├── new_name:
+ │ @ SymbolNode (location: (1,6)-(1,8))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: ∅
+ │ ├── value_loc: (1,6)-(1,8) = "in"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "in"
+ ├── old_name:
+ │ @ SymbolNode (location: (1,9)-(1,12))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: ∅
+ │ ├── value_loc: (1,9)-(1,12) = "out"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "out"
+ └── keyword_loc: (1,0)-(1,5) = "alias"
diff --git a/test/prism/snapshots/seattlerb/and_multi.txt b/test/prism/snapshots/seattlerb/and_multi.txt
new file mode 100644
index 0000000000..b60b131fd2
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/and_multi.txt
@@ -0,0 +1,26 @@
+@ ProgramNode (location: (1,0)-(3,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,4))
+ └── body: (length: 1)
+ └── @ AndNode (location: (1,0)-(3,4))
+ ├── left:
+ │ @ AndNode (location: (1,0)-(2,9))
+ │ ├── left:
+ │ │ @ TrueNode (location: (1,0)-(1,4))
+ │ ├── right:
+ │ │ @ CallNode (location: (2,0)-(2,9))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ FalseNode (location: (2,4)-(2,9))
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :!
+ │ │ ├── message_loc: (2,0)-(2,3) = "not"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (1,5)-(1,8) = "and"
+ ├── right:
+ │ @ TrueNode (location: (3,0)-(3,4))
+ └── operator_loc: (2,10)-(2,13) = "and"
diff --git a/test/prism/snapshots/seattlerb/aref_args_assocs.txt b/test/prism/snapshots/seattlerb/aref_args_assocs.txt
new file mode 100644
index 0000000000..b729186dc5
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/aref_args_assocs.txt
@@ -0,0 +1,23 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ ArrayNode (location: (1,0)-(1,8))
+ ├── flags: ∅
+ ├── elements: (length: 1)
+ │ └── @ KeywordHashNode (location: (1,1)-(1,7))
+ │ ├── flags: ∅
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (1,1)-(1,7))
+ │ ├── key:
+ │ │ @ IntegerNode (location: (1,1)-(1,2))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,6)-(1,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ └── operator_loc: (1,3)-(1,5) = "=>"
+ ├── opening_loc: (1,0)-(1,1) = "["
+ └── closing_loc: (1,7)-(1,8) = "]"
diff --git a/test/prism/snapshots/seattlerb/aref_args_lit_assocs.txt b/test/prism/snapshots/seattlerb/aref_args_lit_assocs.txt
new file mode 100644
index 0000000000..0e9454d780
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/aref_args_lit_assocs.txt
@@ -0,0 +1,26 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ ArrayNode (location: (1,0)-(1,11))
+ ├── flags: ∅
+ ├── elements: (length: 2)
+ │ ├── @ IntegerNode (location: (1,1)-(1,2))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── @ KeywordHashNode (location: (1,4)-(1,10))
+ │ ├── flags: ∅
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (1,4)-(1,10))
+ │ ├── key:
+ │ │ @ IntegerNode (location: (1,4)-(1,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,9)-(1,10))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ └── operator_loc: (1,6)-(1,8) = "=>"
+ ├── opening_loc: (1,0)-(1,1) = "["
+ └── closing_loc: (1,10)-(1,11) = "]"
diff --git a/test/prism/snapshots/seattlerb/args_kw_block.txt b/test/prism/snapshots/seattlerb/args_kw_block.txt
new file mode 100644
index 0000000000..1ad933c74e
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/args_kw_block.txt
@@ -0,0 +1,39 @@
+@ ProgramNode (location: (1,0)-(1,20))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,20))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,20))
+ ├── name: :f
+ ├── name_loc: (1,4)-(1,5) = "f"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,14))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 1)
+ │ │ └── @ OptionalKeywordParameterNode (location: (1,6)-(1,10))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :a
+ │ │ ├── name_loc: (1,6)-(1,8) = "a:"
+ │ │ └── value:
+ │ │ @ IntegerNode (location: (1,9)-(1,10))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── keyword_rest: ∅
+ │ └── block:
+ │ @ BlockParameterNode (location: (1,12)-(1,14))
+ │ ├── flags: ∅
+ │ ├── name: :b
+ │ ├── name_loc: (1,13)-(1,14) = "b"
+ │ └── operator_loc: (1,12)-(1,13) = "&"
+ ├── body: ∅
+ ├── locals: [:a, :b]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,5)-(1,6) = "("
+ ├── rparen_loc: (1,14)-(1,15) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,17)-(1,20) = "end"
diff --git a/test/prism/snapshots/seattlerb/array_line_breaks.txt b/test/prism/snapshots/seattlerb/array_line_breaks.txt
new file mode 100644
index 0000000000..880fedf933
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/array_line_breaks.txt
@@ -0,0 +1,25 @@
+@ ProgramNode (location: (1,0)-(4,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,1))
+ └── body: (length: 2)
+ ├── @ ArrayNode (location: (1,0)-(3,4))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ StringNode (location: (2,0)-(2,3))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (2,0)-(2,1) = "'"
+ │ │ │ ├── content_loc: (2,1)-(2,2) = "a"
+ │ │ │ ├── closing_loc: (2,2)-(2,3) = "'"
+ │ │ │ └── unescaped: "a"
+ │ │ └── @ StringNode (location: (3,0)-(3,3))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (3,0)-(3,1) = "'"
+ │ │ ├── content_loc: (3,1)-(3,2) = "b"
+ │ │ ├── closing_loc: (3,2)-(3,3) = "'"
+ │ │ └── unescaped: "b"
+ │ ├── opening_loc: (1,0)-(1,1) = "["
+ │ └── closing_loc: (3,3)-(3,4) = "]"
+ └── @ IntegerNode (location: (4,0)-(4,1))
+ ├── flags: decimal
+ └── value: 1
diff --git a/test/prism/snapshots/seattlerb/array_lits_trailing_calls.txt b/test/prism/snapshots/seattlerb/array_lits_trailing_calls.txt
new file mode 100644
index 0000000000..d46a181fc7
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/array_lits_trailing_calls.txt
@@ -0,0 +1,35 @@
+@ ProgramNode (location: (1,0)-(3,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,4))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,6))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ArrayNode (location: (1,0)-(1,4))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 0)
+ │ │ ├── opening_loc: (1,0)-(1,3) = "%w["
+ │ │ └── closing_loc: (1,3)-(1,4) = "]"
+ │ ├── call_operator_loc: (1,4)-(1,5) = "."
+ │ ├── name: :b
+ │ ├── message_loc: (1,5)-(1,6) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (3,0)-(3,4))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ ArrayNode (location: (3,0)-(3,2))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 0)
+ │ ├── opening_loc: (3,0)-(3,1) = "["
+ │ └── closing_loc: (3,1)-(3,2) = "]"
+ ├── call_operator_loc: (3,2)-(3,3) = "."
+ ├── name: :b
+ ├── message_loc: (3,3)-(3,4) = "b"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/assoc__bare.txt b/test/prism/snapshots/seattlerb/assoc__bare.txt
new file mode 100644
index 0000000000..28e4f713c5
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/assoc__bare.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (1,0)-(1,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,6))
+ └── body: (length: 1)
+ └── @ HashNode (location: (1,0)-(1,6))
+ ├── opening_loc: (1,0)-(1,1) = "{"
+ ├── elements: (length: 1)
+ │ └── @ AssocNode (location: (1,2)-(1,4))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (1,2)-(1,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (1,2)-(1,3) = "y"
+ │ │ ├── closing_loc: (1,3)-(1,4) = ":"
+ │ │ └── unescaped: "y"
+ │ ├── value:
+ │ │ @ ImplicitNode (location: (1,2)-(1,4))
+ │ │ └── value:
+ │ │ @ CallNode (location: (1,2)-(1,4))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :y
+ │ │ ├── message_loc: (1,2)-(1,3) = "y"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: ∅
+ └── closing_loc: (1,5)-(1,6) = "}"
diff --git a/test/prism/snapshots/seattlerb/assoc_label.txt b/test/prism/snapshots/seattlerb/assoc_label.txt
new file mode 100644
index 0000000000..923f5450f4
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/assoc_label.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,6))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,6))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: (1,1)-(1,2) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,5))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ KeywordHashNode (location: (1,2)-(1,5))
+ │ ├── flags: symbol_keys
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (1,2)-(1,5))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (1,2)-(1,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (1,2)-(1,3) = "b"
+ │ │ ├── closing_loc: (1,3)-(1,4) = ":"
+ │ │ └── unescaped: "b"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,4)-(1,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: ∅
+ ├── closing_loc: (1,5)-(1,6) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/attr_asgn_colon_id.txt b/test/prism/snapshots/seattlerb/attr_asgn_colon_id.txt
new file mode 100644
index 0000000000..589433eda8
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/attr_asgn_colon_id.txt
@@ -0,0 +1,23 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,8))
+ ├── flags: attribute_write
+ ├── receiver:
+ │ @ ConstantReadNode (location: (1,0)-(1,1))
+ │ └── name: :A
+ ├── call_operator_loc: (1,1)-(1,3) = "::"
+ ├── name: :b=
+ ├── message_loc: (1,3)-(1,4) = "b"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,7)-(1,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,7)-(1,8))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/attrasgn_array_arg.txt b/test/prism/snapshots/seattlerb/attrasgn_array_arg.txt
new file mode 100644
index 0000000000..9b04ae9656
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/attrasgn_array_arg.txt
@@ -0,0 +1,42 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,13))
+ ├── flags: attribute_write
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :[]=
+ ├── message_loc: (1,1)-(1,9) = "[[1, 2]]"
+ ├── opening_loc: (1,1)-(1,2) = "["
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,13))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ ArrayNode (location: (1,2)-(1,8))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 2)
+ │ │ │ ├── @ IntegerNode (location: (1,3)-(1,4))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── @ IntegerNode (location: (1,6)-(1,7))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── opening_loc: (1,2)-(1,3) = "["
+ │ │ └── closing_loc: (1,7)-(1,8) = "]"
+ │ └── @ IntegerNode (location: (1,12)-(1,13))
+ │ ├── flags: decimal
+ │ └── value: 3
+ ├── closing_loc: (1,8)-(1,9) = "]"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/attrasgn_array_lhs.txt b/test/prism/snapshots/seattlerb/attrasgn_array_lhs.txt
new file mode 100644
index 0000000000..39544e98da
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/attrasgn_array_lhs.txt
@@ -0,0 +1,83 @@
+@ ProgramNode (location: (1,0)-(1,42))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,42))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,42))
+ ├── flags: attribute_write
+ ├── receiver:
+ │ @ ArrayNode (location: (1,0)-(1,12))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 4)
+ │ │ ├── @ IntegerNode (location: (1,1)-(1,2))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── @ IntegerNode (location: (1,4)-(1,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── @ IntegerNode (location: (1,7)-(1,8))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 3
+ │ │ └── @ IntegerNode (location: (1,10)-(1,11))
+ │ │ ├── flags: decimal
+ │ │ └── value: 4
+ │ ├── opening_loc: (1,0)-(1,1) = "["
+ │ └── closing_loc: (1,11)-(1,12) = "]"
+ ├── call_operator_loc: ∅
+ ├── name: :[]=
+ ├── message_loc: (1,12)-(1,24) = "[from .. to]"
+ ├── opening_loc: (1,12)-(1,13) = "["
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,13)-(1,42))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ RangeNode (location: (1,13)-(1,23))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ CallNode (location: (1,13)-(1,17))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :from
+ │ │ │ ├── message_loc: (1,13)-(1,17) = "from"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── right:
+ │ │ │ @ CallNode (location: (1,21)-(1,23))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :to
+ │ │ │ ├── message_loc: (1,21)-(1,23) = "to"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (1,18)-(1,20) = ".."
+ │ └── @ ArrayNode (location: (1,27)-(1,42))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 3)
+ │ │ ├── @ StringNode (location: (1,28)-(1,31))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (1,28)-(1,29) = "\""
+ │ │ │ ├── content_loc: (1,29)-(1,30) = "a"
+ │ │ │ ├── closing_loc: (1,30)-(1,31) = "\""
+ │ │ │ └── unescaped: "a"
+ │ │ ├── @ StringNode (location: (1,33)-(1,36))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (1,33)-(1,34) = "\""
+ │ │ │ ├── content_loc: (1,34)-(1,35) = "b"
+ │ │ │ ├── closing_loc: (1,35)-(1,36) = "\""
+ │ │ │ └── unescaped: "b"
+ │ │ └── @ StringNode (location: (1,38)-(1,41))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,38)-(1,39) = "\""
+ │ │ ├── content_loc: (1,39)-(1,40) = "c"
+ │ │ ├── closing_loc: (1,40)-(1,41) = "\""
+ │ │ └── unescaped: "c"
+ │ ├── opening_loc: (1,27)-(1,28) = "["
+ │ └── closing_loc: (1,41)-(1,42) = "]"
+ ├── closing_loc: (1,23)-(1,24) = "]"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/attrasgn_primary_dot_constant.txt b/test/prism/snapshots/seattlerb/attrasgn_primary_dot_constant.txt
new file mode 100644
index 0000000000..d4c4fe1070
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/attrasgn_primary_dot_constant.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (1,0)-(1,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,7))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,7))
+ ├── flags: attribute_write
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,1)-(1,2) = "."
+ ├── name: :B=
+ ├── message_loc: (1,2)-(1,3) = "B"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,6)-(1,7))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,6)-(1,7))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/backticks_interpolation_line.txt b/test/prism/snapshots/seattlerb/backticks_interpolation_line.txt
new file mode 100644
index 0000000000..67c8be034e
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/backticks_interpolation_line.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,8))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :x
+ ├── message_loc: (1,0)-(1,1) = "x"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ InterpolatedXStringNode (location: (1,2)-(1,8))
+ │ ├── opening_loc: (1,2)-(1,3) = "`"
+ │ ├── parts: (length: 1)
+ │ │ └── @ EmbeddedStatementsNode (location: (1,3)-(1,7))
+ │ │ ├── opening_loc: (1,3)-(1,5) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,5)-(1,6))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (1,5)-(1,6))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :y
+ │ │ │ ├── message_loc: (1,5)-(1,6) = "y"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── closing_loc: (1,6)-(1,7) = "}"
+ │ └── closing_loc: (1,7)-(1,8) = "`"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/bang_eq.txt b/test/prism/snapshots/seattlerb/bang_eq.txt
new file mode 100644
index 0000000000..ec3dd888b2
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bang_eq.txt
@@ -0,0 +1,24 @@
+@ ProgramNode (location: (1,0)-(1,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,6))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,6))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── call_operator_loc: ∅
+ ├── name: :!=
+ ├── message_loc: (1,2)-(1,4) = "!="
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,5)-(1,6))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,5)-(1,6))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/bdot2.txt b/test/prism/snapshots/seattlerb/bdot2.txt
new file mode 100644
index 0000000000..d4fb86fbe6
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bdot2.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 3)
+ ├── @ RangeNode (location: (1,0)-(1,4))
+ │ ├── flags: ∅
+ │ ├── left: ∅
+ │ ├── right:
+ │ │ @ IntegerNode (location: (1,2)-(1,4))
+ │ │ ├── flags: decimal
+ │ │ └── value: 10
+ │ └── operator_loc: (1,0)-(1,2) = ".."
+ ├── @ RangeNode (location: (2,2)-(2,5))
+ │ ├── flags: ∅
+ │ ├── left: ∅
+ │ ├── right:
+ │ │ @ CallNode (location: (2,4)-(2,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (2,4)-(2,5) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (2,2)-(2,4) = ".."
+ └── @ CallNode (location: (3,2)-(3,3))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :c
+ ├── message_loc: (3,2)-(3,3) = "c"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/bdot3.txt b/test/prism/snapshots/seattlerb/bdot3.txt
new file mode 100644
index 0000000000..0c960f0458
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bdot3.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 3)
+ ├── @ RangeNode (location: (1,0)-(1,5))
+ │ ├── flags: exclude_end
+ │ ├── left: ∅
+ │ ├── right:
+ │ │ @ IntegerNode (location: (1,3)-(1,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 10
+ │ └── operator_loc: (1,0)-(1,3) = "..."
+ ├── @ RangeNode (location: (2,2)-(2,6))
+ │ ├── flags: exclude_end
+ │ ├── left: ∅
+ │ ├── right:
+ │ │ @ CallNode (location: (2,5)-(2,6))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (2,5)-(2,6) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (2,2)-(2,5) = "..."
+ └── @ CallNode (location: (3,2)-(3,3))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :c
+ ├── message_loc: (3,2)-(3,3) = "c"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/begin_ensure_no_bodies.txt b/test/prism/snapshots/seattlerb/begin_ensure_no_bodies.txt
new file mode 100644
index 0000000000..e1698d0bae
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/begin_ensure_no_bodies.txt
@@ -0,0 +1,16 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 1)
+ └── @ BeginNode (location: (1,0)-(3,3))
+ ├── begin_keyword_loc: (1,0)-(1,5) = "begin"
+ ├── statements: ∅
+ ├── rescue_clause: ∅
+ ├── else_clause: ∅
+ ├── ensure_clause:
+ │ @ EnsureNode (location: (2,0)-(3,3))
+ │ ├── ensure_keyword_loc: (2,0)-(2,6) = "ensure"
+ │ ├── statements: ∅
+ │ └── end_keyword_loc: (3,0)-(3,3) = "end"
+ └── end_keyword_loc: (3,0)-(3,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/begin_rescue_else_ensure_bodies.txt b/test/prism/snapshots/seattlerb/begin_rescue_else_ensure_bodies.txt
new file mode 100644
index 0000000000..6603e5c894
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/begin_rescue_else_ensure_bodies.txt
@@ -0,0 +1,47 @@
+@ ProgramNode (location: (1,0)-(9,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(9,3))
+ └── body: (length: 1)
+ └── @ BeginNode (location: (1,0)-(9,3))
+ ├── begin_keyword_loc: (1,0)-(1,5) = "begin"
+ ├── statements:
+ │ @ StatementsNode (location: (2,2)-(2,3))
+ │ └── body: (length: 1)
+ │ └── @ IntegerNode (location: (2,2)-(2,3))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── rescue_clause:
+ │ @ RescueNode (location: (3,0)-(4,3))
+ │ ├── keyword_loc: (3,0)-(3,6) = "rescue"
+ │ ├── exceptions: (length: 0)
+ │ ├── operator_loc: ∅
+ │ ├── reference: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (4,2)-(4,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (4,2)-(4,3))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ └── consequent: ∅
+ ├── else_clause:
+ │ @ ElseNode (location: (5,0)-(7,6))
+ │ ├── else_keyword_loc: (5,0)-(5,4) = "else"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (6,2)-(6,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (6,2)-(6,3))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ └── end_keyword_loc: (7,0)-(7,6) = "ensure"
+ ├── ensure_clause:
+ │ @ EnsureNode (location: (7,0)-(9,3))
+ │ ├── ensure_keyword_loc: (7,0)-(7,6) = "ensure"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (8,2)-(8,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (8,2)-(8,3))
+ │ │ ├── flags: decimal
+ │ │ └── value: 4
+ │ └── end_keyword_loc: (9,0)-(9,3) = "end"
+ └── end_keyword_loc: (9,0)-(9,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/begin_rescue_else_ensure_no_bodies.txt b/test/prism/snapshots/seattlerb/begin_rescue_else_ensure_no_bodies.txt
new file mode 100644
index 0000000000..02e1f097ac
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/begin_rescue_else_ensure_no_bodies.txt
@@ -0,0 +1,27 @@
+@ ProgramNode (location: (1,0)-(9,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(9,3))
+ └── body: (length: 1)
+ └── @ BeginNode (location: (1,0)-(9,3))
+ ├── begin_keyword_loc: (1,0)-(1,5) = "begin"
+ ├── statements: ∅
+ ├── rescue_clause:
+ │ @ RescueNode (location: (3,0)-(3,6))
+ │ ├── keyword_loc: (3,0)-(3,6) = "rescue"
+ │ ├── exceptions: (length: 0)
+ │ ├── operator_loc: ∅
+ │ ├── reference: ∅
+ │ ├── statements: ∅
+ │ └── consequent: ∅
+ ├── else_clause:
+ │ @ ElseNode (location: (5,0)-(7,6))
+ │ ├── else_keyword_loc: (5,0)-(5,4) = "else"
+ │ ├── statements: ∅
+ │ └── end_keyword_loc: (7,0)-(7,6) = "ensure"
+ ├── ensure_clause:
+ │ @ EnsureNode (location: (7,0)-(9,3))
+ │ ├── ensure_keyword_loc: (7,0)-(7,6) = "ensure"
+ │ ├── statements: ∅
+ │ └── end_keyword_loc: (9,0)-(9,3) = "end"
+ └── end_keyword_loc: (9,0)-(9,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/begin_rescue_ensure_no_bodies.txt b/test/prism/snapshots/seattlerb/begin_rescue_ensure_no_bodies.txt
new file mode 100644
index 0000000000..b36fe5c1fe
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/begin_rescue_ensure_no_bodies.txt
@@ -0,0 +1,23 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 1)
+ └── @ BeginNode (location: (1,0)-(4,3))
+ ├── begin_keyword_loc: (1,0)-(1,5) = "begin"
+ ├── statements: ∅
+ ├── rescue_clause:
+ │ @ RescueNode (location: (2,0)-(2,6))
+ │ ├── keyword_loc: (2,0)-(2,6) = "rescue"
+ │ ├── exceptions: (length: 0)
+ │ ├── operator_loc: ∅
+ │ ├── reference: ∅
+ │ ├── statements: ∅
+ │ └── consequent: ∅
+ ├── else_clause: ∅
+ ├── ensure_clause:
+ │ @ EnsureNode (location: (3,0)-(4,3))
+ │ ├── ensure_keyword_loc: (3,0)-(3,6) = "ensure"
+ │ ├── statements: ∅
+ │ └── end_keyword_loc: (4,0)-(4,3) = "end"
+ └── end_keyword_loc: (4,0)-(4,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/block_arg__bare.txt b/test/prism/snapshots/seattlerb/block_arg__bare.txt
new file mode 100644
index 0000000000..165c2980be
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_arg__bare.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,13))
+ ├── name: :x
+ ├── name_loc: (1,4)-(1,5) = "x"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,7))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block:
+ │ @ BlockParameterNode (location: (1,6)-(1,7))
+ │ ├── flags: ∅
+ │ ├── name: ∅
+ │ ├── name_loc: ∅
+ │ └── operator_loc: (1,6)-(1,7) = "&"
+ ├── body: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,5)-(1,6) = "("
+ ├── rparen_loc: (1,7)-(1,8) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,10)-(1,13) = "end"
diff --git a/test/prism/snapshots/seattlerb/block_arg_kwsplat.txt b/test/prism/snapshots/seattlerb/block_arg_kwsplat.txt
new file mode 100644
index 0000000000..392de8559b
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_arg_kwsplat.txt
@@ -0,0 +1,39 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,11))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,11))
+ ├── locals: [:b]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,9))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,8))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ KeywordRestParameterNode (location: (1,5)-(1,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (1,7)-(1,8) = "b"
+ │ │ │ └── operator_loc: (1,5)-(1,7) = "**"
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,8)-(1,9) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,10)-(1,11) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_arg_opt_arg_block.txt b/test/prism/snapshots/seattlerb/block_arg_opt_arg_block.txt
new file mode 100644
index 0000000000..ee9644d59d
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_arg_opt_arg_block.txt
@@ -0,0 +1,54 @@
+@ ProgramNode (location: (1,0)-(1,21))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,21))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,21))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,21))
+ ├── locals: [:b, :c, :d, :e]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,19))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,18))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :b
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,8)-(1,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── name_loc: (1,8)-(1,9) = "c"
+ │ │ │ ├── operator_loc: (1,9)-(1,10) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,10)-(1,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,13)-(1,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :d
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (1,16)-(1,18))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :e
+ │ │ ├── name_loc: (1,17)-(1,18) = "e"
+ │ │ └── operator_loc: (1,16)-(1,17) = "&"
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,18)-(1,19) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,20)-(1,21) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_arg_opt_splat.txt b/test/prism/snapshots/seattlerb/block_arg_opt_splat.txt
new file mode 100644
index 0000000000..799bd21057
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_arg_opt_splat.txt
@@ -0,0 +1,51 @@
+@ ProgramNode (location: (1,0)-(1,20))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,20))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,20))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,20))
+ ├── locals: [:b, :c, :d]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,18))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,17))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :b
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,8)-(1,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── name_loc: (1,8)-(1,9) = "c"
+ │ │ │ ├── operator_loc: (1,10)-(1,11) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,12)-(1,13))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (1,15)-(1,17))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :d
+ │ │ │ ├── name_loc: (1,16)-(1,17) = "d"
+ │ │ │ └── operator_loc: (1,15)-(1,16) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,17)-(1,18) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,19)-(1,20) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_arg_opt_splat_arg_block_omfg.txt b/test/prism/snapshots/seattlerb/block_arg_opt_splat_arg_block_omfg.txt
new file mode 100644
index 0000000000..b5df136a62
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_arg_opt_splat_arg_block_omfg.txt
@@ -0,0 +1,59 @@
+@ ProgramNode (location: (1,0)-(1,25))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,25))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,25))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,25))
+ ├── locals: [:b, :c, :d, :e, :f]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,23))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,22))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :b
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,8)-(1,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── name_loc: (1,8)-(1,9) = "c"
+ │ │ │ ├── operator_loc: (1,9)-(1,10) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,10)-(1,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (1,13)-(1,15))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :d
+ │ │ │ ├── name_loc: (1,14)-(1,15) = "d"
+ │ │ │ └── operator_loc: (1,13)-(1,14) = "*"
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,17)-(1,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :e
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (1,20)-(1,22))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :f
+ │ │ ├── name_loc: (1,21)-(1,22) = "f"
+ │ │ └── operator_loc: (1,20)-(1,21) = "&"
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,22)-(1,23) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,24)-(1,25) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_arg_optional.txt b/test/prism/snapshots/seattlerb/block_arg_optional.txt
new file mode 100644
index 0000000000..8a850e9a21
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_arg_optional.txt
@@ -0,0 +1,43 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,13))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,13))
+ ├── locals: [:b]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,11))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,10))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,5)-(1,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (1,5)-(1,6) = "b"
+ │ │ │ ├── operator_loc: (1,7)-(1,8) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,9)-(1,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,10)-(1,11) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,12)-(1,13) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_arg_scope.txt b/test/prism/snapshots/seattlerb/block_arg_scope.txt
new file mode 100644
index 0000000000..b99cc5e45c
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_arg_scope.txt
@@ -0,0 +1,40 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,12))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,12))
+ ├── locals: [:b, :c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,10))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,6))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :b
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 1)
+ │ │ └── @ BlockLocalVariableNode (location: (1,8)-(1,9))
+ │ │ ├── flags: ∅
+ │ │ └── name: :c
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,9)-(1,10) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,11)-(1,12) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_arg_scope2.txt b/test/prism/snapshots/seattlerb/block_arg_scope2.txt
new file mode 100644
index 0000000000..98b3a7da3a
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_arg_scope2.txt
@@ -0,0 +1,43 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,14))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,14))
+ ├── locals: [:b, :c, :d]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,3)-(1,12))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,4)-(1,5))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,4)-(1,5))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :b
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 2)
+ │ │ ├── @ BlockLocalVariableNode (location: (1,7)-(1,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :c
+ │ │ └── @ BlockLocalVariableNode (location: (1,10)-(1,11))
+ │ │ ├── flags: ∅
+ │ │ └── name: :d
+ │ ├── opening_loc: (1,3)-(1,4) = "|"
+ │ └── closing_loc: (1,11)-(1,12) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,13)-(1,14) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_arg_splat_arg.txt b/test/prism/snapshots/seattlerb/block_arg_splat_arg.txt
new file mode 100644
index 0000000000..fd5813c983
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_arg_splat_arg.txt
@@ -0,0 +1,45 @@
+@ ProgramNode (location: (1,0)-(1,16))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,16))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,16))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,16))
+ ├── locals: [:b, :c, :d]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,14))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,13))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :b
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (1,8)-(1,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── name_loc: (1,9)-(1,10) = "c"
+ │ │ │ └── operator_loc: (1,8)-(1,9) = "*"
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,12)-(1,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :d
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,13)-(1,14) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,15)-(1,16) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_args_kwargs.txt b/test/prism/snapshots/seattlerb/block_args_kwargs.txt
new file mode 100644
index 0000000000..0975ce3367
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_args_kwargs.txt
@@ -0,0 +1,44 @@
+@ ProgramNode (location: (1,0)-(1,23))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,23))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,23))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,23))
+ ├── locals: [:kwargs]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,14))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,13))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ KeywordRestParameterNode (location: (1,5)-(1,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :kwargs
+ │ │ │ ├── name_loc: (1,7)-(1,13) = "kwargs"
+ │ │ │ └── operator_loc: (1,5)-(1,7) = "**"
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,13)-(1,14) = "|"
+ ├── body:
+ │ @ StatementsNode (location: (1,15)-(1,21))
+ │ └── body: (length: 1)
+ │ └── @ LocalVariableReadNode (location: (1,15)-(1,21))
+ │ ├── name: :kwargs
+ │ └── depth: 0
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,22)-(1,23) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_args_no_kwargs.txt b/test/prism/snapshots/seattlerb/block_args_no_kwargs.txt
new file mode 100644
index 0000000000..d47349defb
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_args_no_kwargs.txt
@@ -0,0 +1,37 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,13))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,13))
+ ├── locals: []
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,11))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,10))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ NoKeywordsParameterNode (location: (1,5)-(1,10))
+ │ │ │ ├── operator_loc: (1,5)-(1,7) = "**"
+ │ │ │ └── keyword_loc: (1,7)-(1,10) = "nil"
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,10)-(1,11) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,12)-(1,13) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_args_opt1.txt b/test/prism/snapshots/seattlerb/block_args_opt1.txt
new file mode 100644
index 0000000000..1f21c0a477
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_args_opt1.txt
@@ -0,0 +1,59 @@
+@ ProgramNode (location: (1,0)-(1,24))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,24))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,24))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,24))
+ ├── locals: [:a, :b]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,15))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,14))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,8)-(1,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (1,8)-(1,9) = "b"
+ │ │ │ ├── operator_loc: (1,10)-(1,11) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,12)-(1,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,14)-(1,15) = "|"
+ ├── body:
+ │ @ StatementsNode (location: (1,16)-(1,22))
+ │ └── body: (length: 1)
+ │ └── @ ArrayNode (location: (1,16)-(1,22))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ LocalVariableReadNode (location: (1,17)-(1,18))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ └── @ LocalVariableReadNode (location: (1,20)-(1,21))
+ │ │ ├── name: :b
+ │ │ └── depth: 0
+ │ ├── opening_loc: (1,16)-(1,17) = "["
+ │ └── closing_loc: (1,21)-(1,22) = "]"
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,23)-(1,24) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_args_opt2.txt b/test/prism/snapshots/seattlerb/block_args_opt2.txt
new file mode 100644
index 0000000000..a8d736dde7
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_args_opt2.txt
@@ -0,0 +1,52 @@
+@ ProgramNode (location: (1,0)-(1,18))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,18))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,18))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,18))
+ ├── locals: [:b, :c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,16))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,6)-(1,14))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 2)
+ │ │ │ ├── @ OptionalParameterNode (location: (1,6)-(1,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── name_loc: (1,6)-(1,7) = "b"
+ │ │ │ │ ├── operator_loc: (1,7)-(1,8) = "="
+ │ │ │ │ └── value:
+ │ │ │ │ @ IntegerNode (location: (1,8)-(1,9))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── @ OptionalParameterNode (location: (1,11)-(1,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── name_loc: (1,11)-(1,12) = "c"
+ │ │ │ ├── operator_loc: (1,12)-(1,13) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,13)-(1,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,15)-(1,16) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,17)-(1,18) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_args_opt2_2.txt b/test/prism/snapshots/seattlerb/block_args_opt2_2.txt
new file mode 100644
index 0000000000..0403851ed7
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_args_opt2_2.txt
@@ -0,0 +1,71 @@
+@ ProgramNode (location: (1,0)-(1,35))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,35))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,35))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,35))
+ ├── locals: [:a, :b, :c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,23))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,22))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 2)
+ │ │ │ ├── @ OptionalParameterNode (location: (1,8)-(1,14))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── name_loc: (1,8)-(1,9) = "b"
+ │ │ │ │ ├── operator_loc: (1,10)-(1,11) = "="
+ │ │ │ │ └── value:
+ │ │ │ │ @ IntegerNode (location: (1,12)-(1,14))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 42
+ │ │ │ └── @ OptionalParameterNode (location: (1,16)-(1,22))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── name_loc: (1,16)-(1,17) = "c"
+ │ │ │ ├── operator_loc: (1,18)-(1,19) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,20)-(1,22))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 24
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,22)-(1,23) = "|"
+ ├── body:
+ │ @ StatementsNode (location: (1,24)-(1,33))
+ │ └── body: (length: 1)
+ │ └── @ ArrayNode (location: (1,24)-(1,33))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 3)
+ │ │ ├── @ LocalVariableReadNode (location: (1,25)-(1,26))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ ├── @ LocalVariableReadNode (location: (1,28)-(1,29))
+ │ │ │ ├── name: :b
+ │ │ │ └── depth: 0
+ │ │ └── @ LocalVariableReadNode (location: (1,31)-(1,32))
+ │ │ ├── name: :c
+ │ │ └── depth: 0
+ │ ├── opening_loc: (1,24)-(1,25) = "["
+ │ └── closing_loc: (1,32)-(1,33) = "]"
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,34)-(1,35) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_args_opt3.txt b/test/prism/snapshots/seattlerb/block_args_opt3.txt
new file mode 100644
index 0000000000..ff4495019c
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_args_opt3.txt
@@ -0,0 +1,79 @@
+@ ProgramNode (location: (1,0)-(1,42))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,42))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,42))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,42))
+ ├── locals: [:a, :b, :c, :d]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,27))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,26))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 2)
+ │ │ │ ├── @ OptionalParameterNode (location: (1,8)-(1,14))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── name_loc: (1,8)-(1,9) = "b"
+ │ │ │ │ ├── operator_loc: (1,10)-(1,11) = "="
+ │ │ │ │ └── value:
+ │ │ │ │ @ IntegerNode (location: (1,12)-(1,14))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 42
+ │ │ │ └── @ OptionalParameterNode (location: (1,16)-(1,22))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── name_loc: (1,16)-(1,17) = "c"
+ │ │ │ ├── operator_loc: (1,18)-(1,19) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,20)-(1,22))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 24
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (1,24)-(1,26))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :d
+ │ │ ├── name_loc: (1,25)-(1,26) = "d"
+ │ │ └── operator_loc: (1,24)-(1,25) = "&"
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,26)-(1,27) = "|"
+ ├── body:
+ │ @ StatementsNode (location: (1,28)-(1,40))
+ │ └── body: (length: 1)
+ │ └── @ ArrayNode (location: (1,28)-(1,40))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 4)
+ │ │ ├── @ LocalVariableReadNode (location: (1,29)-(1,30))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ ├── @ LocalVariableReadNode (location: (1,32)-(1,33))
+ │ │ │ ├── name: :b
+ │ │ │ └── depth: 0
+ │ │ ├── @ LocalVariableReadNode (location: (1,35)-(1,36))
+ │ │ │ ├── name: :c
+ │ │ │ └── depth: 0
+ │ │ └── @ LocalVariableReadNode (location: (1,38)-(1,39))
+ │ │ ├── name: :d
+ │ │ └── depth: 0
+ │ ├── opening_loc: (1,28)-(1,29) = "["
+ │ └── closing_loc: (1,39)-(1,40) = "]"
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,41)-(1,42) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_call_defn_call_block_call.txt b/test/prism/snapshots/seattlerb/block_call_defn_call_block_call.txt
new file mode 100644
index 0000000000..2e634dc937
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_call_defn_call_block_call.txt
@@ -0,0 +1,80 @@
+@ ProgramNode (location: (1,0)-(4,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,11))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(3,4))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,2)-(3,4))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ DefNode (location: (1,2)-(3,4))
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (1,6)-(1,7) = "b"
+ │ │ ├── receiver: ∅
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (1,8)-(1,9))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (1,8)-(1,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :c
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (2,1)-(2,2))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (2,1)-(2,2))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :d
+ │ │ │ ├── message_loc: (2,1)-(2,2) = "d"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: [:c]
+ │ │ ├── def_keyword_loc: (1,2)-(1,5) = "def"
+ │ │ ├── operator_loc: ∅
+ │ │ ├── lparen_loc: (1,7)-(1,8) = "("
+ │ │ ├── rparen_loc: (1,9)-(1,10) = ")"
+ │ │ ├── equal_loc: ∅
+ │ │ └── end_keyword_loc: (3,1)-(3,4) = "end"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (4,1)-(4,11))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (4,1)-(4,2))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :e
+ │ ├── message_loc: (4,1)-(4,2) = "e"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (4,2)-(4,3) = "."
+ ├── name: :f
+ ├── message_loc: (4,3)-(4,4) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (4,5)-(4,11))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── opening_loc: (4,5)-(4,7) = "do"
+ └── closing_loc: (4,8)-(4,11) = "end"
diff --git a/test/prism/snapshots/seattlerb/block_call_dot_op2_brace_block.txt b/test/prism/snapshots/seattlerb/block_call_dot_op2_brace_block.txt
new file mode 100644
index 0000000000..e46104b868
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_call_dot_op2_brace_block.txt
@@ -0,0 +1,100 @@
+@ ProgramNode (location: (1,0)-(1,31))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,31))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,31))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,16))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,0)-(1,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (1,1)-(1,2) = "."
+ │ ├── name: :b
+ │ ├── message_loc: (1,2)-(1,3) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,4)-(1,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (1,4)-(1,7))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :c
+ │ │ ├── message_loc: (1,4)-(1,5) = "c"
+ │ │ ├── opening_loc: (1,5)-(1,6) = "("
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: (1,6)-(1,7) = ")"
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,8)-(1,16))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,11)-(1,12))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,11)-(1,12))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :d
+ │ │ ├── message_loc: (1,11)-(1,12) = "d"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (1,8)-(1,10) = "do"
+ │ └── closing_loc: (1,13)-(1,16) = "end"
+ ├── call_operator_loc: (1,16)-(1,17) = "."
+ ├── name: :e
+ ├── message_loc: (1,17)-(1,18) = "e"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,19)-(1,31))
+ ├── locals: [:f]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,22)-(1,25))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,23)-(1,24))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,23)-(1,24))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :f
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,22)-(1,23) = "|"
+ │ └── closing_loc: (1,24)-(1,25) = "|"
+ ├── body:
+ │ @ StatementsNode (location: (1,26)-(1,27))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,26)-(1,27))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :g
+ │ ├── message_loc: (1,26)-(1,27) = "g"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── opening_loc: (1,19)-(1,21) = "do"
+ └── closing_loc: (1,28)-(1,31) = "end"
diff --git a/test/prism/snapshots/seattlerb/block_call_dot_op2_cmd_args_do_block.txt b/test/prism/snapshots/seattlerb/block_call_dot_op2_cmd_args_do_block.txt
new file mode 100644
index 0000000000..05d076f8d6
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_call_dot_op2_cmd_args_do_block.txt
@@ -0,0 +1,113 @@
+@ ProgramNode (location: (1,0)-(1,33))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,33))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,33))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,16))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,0)-(1,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (1,1)-(1,2) = "."
+ │ ├── name: :b
+ │ ├── message_loc: (1,2)-(1,3) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,4)-(1,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (1,4)-(1,7))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :c
+ │ │ ├── message_loc: (1,4)-(1,5) = "c"
+ │ │ ├── opening_loc: (1,5)-(1,6) = "("
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: (1,6)-(1,7) = ")"
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,8)-(1,16))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,11)-(1,12))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,11)-(1,12))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :d
+ │ │ ├── message_loc: (1,11)-(1,12) = "d"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (1,8)-(1,10) = "do"
+ │ └── closing_loc: (1,13)-(1,16) = "end"
+ ├── call_operator_loc: (1,16)-(1,17) = "."
+ ├── name: :e
+ ├── message_loc: (1,17)-(1,18) = "e"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,19)-(1,20))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (1,19)-(1,20))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (1,19)-(1,20) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,21)-(1,33))
+ ├── locals: [:g]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,24)-(1,27))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,25)-(1,26))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,25)-(1,26))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :g
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,24)-(1,25) = "|"
+ │ └── closing_loc: (1,26)-(1,27) = "|"
+ ├── body:
+ │ @ StatementsNode (location: (1,28)-(1,29))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,28)-(1,29))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :h
+ │ ├── message_loc: (1,28)-(1,29) = "h"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── opening_loc: (1,21)-(1,23) = "do"
+ └── closing_loc: (1,30)-(1,33) = "end"
diff --git a/test/prism/snapshots/seattlerb/block_call_operation_colon.txt b/test/prism/snapshots/seattlerb/block_call_operation_colon.txt
new file mode 100644
index 0000000000..9fd13b0dfc
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_call_operation_colon.txt
@@ -0,0 +1,54 @@
+@ ProgramNode (location: (1,0)-(1,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,15))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,15))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,12))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,0)-(1,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (1,1)-(1,2) = "."
+ │ ├── name: :b
+ │ ├── message_loc: (1,2)-(1,3) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,4)-(1,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (1,4)-(1,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :c
+ │ │ ├── message_loc: (1,4)-(1,5) = "c"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,6)-(1,12))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,6)-(1,8) = "do"
+ │ └── closing_loc: (1,9)-(1,12) = "end"
+ ├── call_operator_loc: (1,12)-(1,14) = "::"
+ ├── name: :d
+ ├── message_loc: (1,14)-(1,15) = "d"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/block_call_operation_dot.txt b/test/prism/snapshots/seattlerb/block_call_operation_dot.txt
new file mode 100644
index 0000000000..43c19d3318
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_call_operation_dot.txt
@@ -0,0 +1,54 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,14))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,12))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,0)-(1,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (1,1)-(1,2) = "."
+ │ ├── name: :b
+ │ ├── message_loc: (1,2)-(1,3) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,4)-(1,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (1,4)-(1,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :c
+ │ │ ├── message_loc: (1,4)-(1,5) = "c"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,6)-(1,12))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,6)-(1,8) = "do"
+ │ └── closing_loc: (1,9)-(1,12) = "end"
+ ├── call_operator_loc: (1,12)-(1,13) = "."
+ ├── name: :d
+ ├── message_loc: (1,13)-(1,14) = "d"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/block_call_paren_call_block_call.txt b/test/prism/snapshots/seattlerb/block_call_paren_call_block_call.txt
new file mode 100644
index 0000000000..10c1780d37
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_call_paren_call_block_call.txt
@@ -0,0 +1,60 @@
+@ ProgramNode (location: (1,0)-(2,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,10))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,5))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,2)-(1,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ ParenthesesNode (location: (1,2)-(1,5))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (1,3)-(1,4))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (1,3)-(1,4))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (1,3)-(1,4) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (1,2)-(1,3) = "("
+ │ │ └── closing_loc: (1,4)-(1,5) = ")"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (2,0)-(2,10))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (2,0)-(2,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :c
+ │ ├── message_loc: (2,0)-(2,1) = "c"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (2,1)-(2,2) = "."
+ ├── name: :d
+ ├── message_loc: (2,2)-(2,3) = "d"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (2,4)-(2,10))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── opening_loc: (2,4)-(2,6) = "do"
+ └── closing_loc: (2,7)-(2,10) = "end"
diff --git a/test/prism/snapshots/seattlerb/block_command_operation_colon.txt b/test/prism/snapshots/seattlerb/block_command_operation_colon.txt
new file mode 100644
index 0000000000..30fd6dafa0
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_command_operation_colon.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(1,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,17))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,17))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,11))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,2)-(1,4))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ SymbolNode (location: (1,2)-(1,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,2)-(1,3) = ":"
+ │ │ ├── value_loc: (1,3)-(1,4) = "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b"
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,5)-(1,11))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,5)-(1,7) = "do"
+ │ └── closing_loc: (1,8)-(1,11) = "end"
+ ├── call_operator_loc: (1,11)-(1,13) = "::"
+ ├── name: :c
+ ├── message_loc: (1,13)-(1,14) = "c"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,15)-(1,17))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ SymbolNode (location: (1,15)-(1,17))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,15)-(1,16) = ":"
+ │ ├── value_loc: (1,16)-(1,17) = "d"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "d"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/block_command_operation_dot.txt b/test/prism/snapshots/seattlerb/block_command_operation_dot.txt
new file mode 100644
index 0000000000..e4f69d3604
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_command_operation_dot.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(1,16))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,16))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,16))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,11))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,2)-(1,4))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ SymbolNode (location: (1,2)-(1,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,2)-(1,3) = ":"
+ │ │ ├── value_loc: (1,3)-(1,4) = "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b"
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,5)-(1,11))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,5)-(1,7) = "do"
+ │ └── closing_loc: (1,8)-(1,11) = "end"
+ ├── call_operator_loc: (1,11)-(1,12) = "."
+ ├── name: :c
+ ├── message_loc: (1,12)-(1,13) = "c"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,14)-(1,16))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ SymbolNode (location: (1,14)-(1,16))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,14)-(1,15) = ":"
+ │ ├── value_loc: (1,15)-(1,16) = "d"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "d"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/block_decomp_anon_splat_arg.txt b/test/prism/snapshots/seattlerb/block_decomp_anon_splat_arg.txt
new file mode 100644
index 0000000000..e309ec1f98
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_decomp_anon_splat_arg.txt
@@ -0,0 +1,46 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,14))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,14))
+ ├── locals: [:a]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,12))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,11))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ MultiTargetNode (location: (1,5)-(1,11))
+ │ │ │ ├── lefts: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (1,6)-(1,7))
+ │ │ │ │ ├── operator_loc: (1,6)-(1,7) = "*"
+ │ │ │ │ └── expression: ∅
+ │ │ │ ├── rights: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (1,9)-(1,10))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── lparen_loc: (1,5)-(1,6) = "("
+ │ │ │ └── rparen_loc: (1,10)-(1,11) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,11)-(1,12) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,13)-(1,14) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_decomp_arg_splat.txt b/test/prism/snapshots/seattlerb/block_decomp_arg_splat.txt
new file mode 100644
index 0000000000..8d28fa7e02
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_decomp_arg_splat.txt
@@ -0,0 +1,46 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,14))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,14))
+ ├── locals: [:b]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,12))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,11))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ MultiTargetNode (location: (1,5)-(1,11))
+ │ │ │ ├── lefts: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (1,6)-(1,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :b
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (1,9)-(1,10))
+ │ │ │ │ ├── operator_loc: (1,9)-(1,10) = "*"
+ │ │ │ │ └── expression: ∅
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: (1,5)-(1,6) = "("
+ │ │ │ └── rparen_loc: (1,10)-(1,11) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,11)-(1,12) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,13)-(1,14) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_decomp_arg_splat_arg.txt b/test/prism/snapshots/seattlerb/block_decomp_arg_splat_arg.txt
new file mode 100644
index 0000000000..4f4a82acf5
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_decomp_arg_splat_arg.txt
@@ -0,0 +1,52 @@
+@ ProgramNode (location: (1,0)-(1,18))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,18))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,18))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,18))
+ ├── locals: [:a, :b, :c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,16))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,15))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ MultiTargetNode (location: (1,5)-(1,15))
+ │ │ │ ├── lefts: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (1,6)-(1,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (1,9)-(1,11))
+ │ │ │ │ ├── operator_loc: (1,9)-(1,10) = "*"
+ │ │ │ │ └── expression:
+ │ │ │ │ @ RequiredParameterNode (location: (1,10)-(1,11))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :b
+ │ │ │ ├── rights: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (1,13)-(1,14))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :c
+ │ │ │ ├── lparen_loc: (1,5)-(1,6) = "("
+ │ │ │ └── rparen_loc: (1,14)-(1,15) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,15)-(1,16) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,17)-(1,18) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_decomp_splat.txt b/test/prism/snapshots/seattlerb/block_decomp_splat.txt
new file mode 100644
index 0000000000..09d3440126
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_decomp_splat.txt
@@ -0,0 +1,46 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,12))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,12))
+ ├── locals: [:a]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,10))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,9))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ MultiTargetNode (location: (1,5)-(1,9))
+ │ │ │ ├── lefts: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (1,6)-(1,8))
+ │ │ │ │ ├── operator_loc: (1,6)-(1,7) = "*"
+ │ │ │ │ └── expression:
+ │ │ │ │ @ RequiredParameterNode (location: (1,7)-(1,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: (1,5)-(1,6) = "("
+ │ │ │ └── rparen_loc: (1,8)-(1,9) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,9)-(1,10) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,11)-(1,12) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_kw.txt b/test/prism/snapshots/seattlerb/block_kw.txt
new file mode 100644
index 0000000000..f022637dae
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_kw.txt
@@ -0,0 +1,42 @@
+@ ProgramNode (location: (1,0)-(1,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,15))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,15))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :blah
+ ├── message_loc: (1,0)-(1,4) = "blah"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,5)-(1,15))
+ ├── locals: [:k]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,7)-(1,13))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,8)-(1,12))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ OptionalKeywordParameterNode (location: (1,8)-(1,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :k
+ │ │ │ ├── name_loc: (1,8)-(1,10) = "k:"
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,10)-(1,12))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,7)-(1,8) = "|"
+ │ └── closing_loc: (1,12)-(1,13) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,5)-(1,6) = "{"
+ └── closing_loc: (1,14)-(1,15) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_kw__required.txt b/test/prism/snapshots/seattlerb/block_kw__required.txt
new file mode 100644
index 0000000000..8a49c8bec7
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_kw__required.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(1,16))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,16))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,16))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :blah
+ ├── message_loc: (1,0)-(1,4) = "blah"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,5)-(1,16))
+ ├── locals: [:k]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,8)-(1,12))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,9)-(1,11))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ RequiredKeywordParameterNode (location: (1,9)-(1,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :k
+ │ │ │ └── name_loc: (1,9)-(1,11) = "k:"
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,8)-(1,9) = "|"
+ │ └── closing_loc: (1,11)-(1,12) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,5)-(1,7) = "do"
+ └── closing_loc: (1,13)-(1,16) = "end"
diff --git a/test/prism/snapshots/seattlerb/block_kwarg_lvar.txt b/test/prism/snapshots/seattlerb/block_kwarg_lvar.txt
new file mode 100644
index 0000000000..e77bf90a27
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_kwarg_lvar.txt
@@ -0,0 +1,50 @@
+@ ProgramNode (location: (1,0)-(1,20))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,20))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,20))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :bl
+ ├── message_loc: (1,0)-(1,2) = "bl"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,3)-(1,20))
+ ├── locals: [:kw]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,5)-(1,15))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,6)-(1,14))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ OptionalKeywordParameterNode (location: (1,6)-(1,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :kw
+ │ │ │ ├── name_loc: (1,6)-(1,9) = "kw:"
+ │ │ │ └── value:
+ │ │ │ @ SymbolNode (location: (1,10)-(1,14))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (1,10)-(1,11) = ":"
+ │ │ │ ├── value_loc: (1,11)-(1,14) = "val"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "val"
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,5)-(1,6) = "|"
+ │ └── closing_loc: (1,14)-(1,15) = "|"
+ ├── body:
+ │ @ StatementsNode (location: (1,16)-(1,18))
+ │ └── body: (length: 1)
+ │ └── @ LocalVariableReadNode (location: (1,16)-(1,18))
+ │ ├── name: :kw
+ │ └── depth: 0
+ ├── opening_loc: (1,3)-(1,4) = "{"
+ └── closing_loc: (1,19)-(1,20) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_kwarg_lvar_multiple.txt b/test/prism/snapshots/seattlerb/block_kwarg_lvar_multiple.txt
new file mode 100644
index 0000000000..a527c8c993
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_kwarg_lvar_multiple.txt
@@ -0,0 +1,61 @@
+@ ProgramNode (location: (1,0)-(1,33))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,33))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,33))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :bl
+ ├── message_loc: (1,0)-(1,2) = "bl"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,3)-(1,33))
+ ├── locals: [:kw, :kw2]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,5)-(1,28))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,6)-(1,26))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 2)
+ │ │ │ ├── @ OptionalKeywordParameterNode (location: (1,6)-(1,14))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :kw
+ │ │ │ │ ├── name_loc: (1,6)-(1,9) = "kw:"
+ │ │ │ │ └── value:
+ │ │ │ │ @ SymbolNode (location: (1,10)-(1,14))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (1,10)-(1,11) = ":"
+ │ │ │ │ ├── value_loc: (1,11)-(1,14) = "val"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "val"
+ │ │ │ └── @ OptionalKeywordParameterNode (location: (1,16)-(1,26))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :kw2
+ │ │ │ ├── name_loc: (1,16)-(1,20) = "kw2:"
+ │ │ │ └── value:
+ │ │ │ @ SymbolNode (location: (1,21)-(1,26))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (1,21)-(1,22) = ":"
+ │ │ │ ├── value_loc: (1,22)-(1,26) = "val2"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "val2"
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,5)-(1,6) = "|"
+ │ └── closing_loc: (1,27)-(1,28) = "|"
+ ├── body:
+ │ @ StatementsNode (location: (1,29)-(1,31))
+ │ └── body: (length: 1)
+ │ └── @ LocalVariableReadNode (location: (1,29)-(1,31))
+ │ ├── name: :kw
+ │ └── depth: 0
+ ├── opening_loc: (1,3)-(1,4) = "{"
+ └── closing_loc: (1,32)-(1,33) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_opt_arg.txt b/test/prism/snapshots/seattlerb/block_opt_arg.txt
new file mode 100644
index 0000000000..64dc928f14
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_opt_arg.txt
@@ -0,0 +1,46 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,14))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,14))
+ ├── locals: [:b, :c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,12))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,11))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,5)-(1,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (1,5)-(1,6) = "b"
+ │ │ │ ├── operator_loc: (1,6)-(1,7) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,7)-(1,8))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,10)-(1,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :c
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,11)-(1,12) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,13)-(1,14) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_opt_splat.txt b/test/prism/snapshots/seattlerb/block_opt_splat.txt
new file mode 100644
index 0000000000..c18df9c27d
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_opt_splat.txt
@@ -0,0 +1,48 @@
+@ ProgramNode (location: (1,0)-(1,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,17))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,17))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,17))
+ ├── locals: [:b, :c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,15))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,14))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,5)-(1,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (1,5)-(1,6) = "b"
+ │ │ │ ├── operator_loc: (1,7)-(1,8) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,9)-(1,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (1,12)-(1,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── name_loc: (1,13)-(1,14) = "c"
+ │ │ │ └── operator_loc: (1,12)-(1,13) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,14)-(1,15) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,16)-(1,17) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_opt_splat_arg_block_omfg.txt b/test/prism/snapshots/seattlerb/block_opt_splat_arg_block_omfg.txt
new file mode 100644
index 0000000000..3806809d2b
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_opt_splat_arg_block_omfg.txt
@@ -0,0 +1,56 @@
+@ ProgramNode (location: (1,0)-(1,22))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,22))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,22))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,22))
+ ├── locals: [:b, :c, :d, :e]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,20))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,19))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,5)-(1,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (1,5)-(1,6) = "b"
+ │ │ │ ├── operator_loc: (1,6)-(1,7) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,7)-(1,8))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (1,10)-(1,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── name_loc: (1,11)-(1,12) = "c"
+ │ │ │ └── operator_loc: (1,10)-(1,11) = "*"
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,14)-(1,15))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :d
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (1,17)-(1,19))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :e
+ │ │ ├── name_loc: (1,18)-(1,19) = "e"
+ │ │ └── operator_loc: (1,17)-(1,18) = "&"
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,19)-(1,20) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,21)-(1,22) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_optarg.txt b/test/prism/snapshots/seattlerb/block_optarg.txt
new file mode 100644
index 0000000000..5da99aec79
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_optarg.txt
@@ -0,0 +1,46 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,14))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,14))
+ ├── locals: [:b]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,12))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,11))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,5)-(1,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (1,5)-(1,6) = "b"
+ │ │ │ ├── operator_loc: (1,7)-(1,8) = "="
+ │ │ │ └── value:
+ │ │ │ @ SymbolNode (location: (1,9)-(1,11))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (1,9)-(1,10) = ":"
+ │ │ │ ├── value_loc: (1,10)-(1,11) = "c"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "c"
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,11)-(1,12) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,13)-(1,14) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_paren_splat.txt b/test/prism/snapshots/seattlerb/block_paren_splat.txt
new file mode 100644
index 0000000000..ebd937904c
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_paren_splat.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(1,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,15))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,15))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,15))
+ ├── locals: [:b, :c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,13))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,12))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ MultiTargetNode (location: (1,5)-(1,12))
+ │ │ │ ├── lefts: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (1,6)-(1,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :b
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (1,9)-(1,11))
+ │ │ │ │ ├── operator_loc: (1,9)-(1,10) = "*"
+ │ │ │ │ └── expression:
+ │ │ │ │ @ RequiredParameterNode (location: (1,10)-(1,11))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :c
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: (1,5)-(1,6) = "("
+ │ │ │ └── rparen_loc: (1,11)-(1,12) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,12)-(1,13) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,14)-(1,15) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_reg_optarg.txt b/test/prism/snapshots/seattlerb/block_reg_optarg.txt
new file mode 100644
index 0000000000..53c43603a7
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_reg_optarg.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(1,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,17))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,17))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,17))
+ ├── locals: [:b, :c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,15))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,14))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :b
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,8)-(1,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── name_loc: (1,8)-(1,9) = "c"
+ │ │ │ ├── operator_loc: (1,10)-(1,11) = "="
+ │ │ │ └── value:
+ │ │ │ @ SymbolNode (location: (1,12)-(1,14))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (1,12)-(1,13) = ":"
+ │ │ │ ├── value_loc: (1,13)-(1,14) = "d"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "d"
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,14)-(1,15) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,16)-(1,17) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_return.txt b/test/prism/snapshots/seattlerb/block_return.txt
new file mode 100644
index 0000000000..c863b28a22
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_return.txt
@@ -0,0 +1,57 @@
+@ ProgramNode (location: (1,0)-(1,27))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,27))
+ └── body: (length: 1)
+ └── @ ReturnNode (location: (1,0)-(1,27))
+ ├── flags: ∅
+ ├── keyword_loc: (1,0)-(1,6) = "return"
+ └── arguments:
+ @ ArgumentsNode (location: (1,7)-(1,27))
+ ├── flags: ∅
+ └── arguments: (length: 1)
+ └── @ CallNode (location: (1,7)-(1,27))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :foo
+ ├── message_loc: (1,7)-(1,10) = "foo"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,11)-(1,14))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (1,11)-(1,14))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :arg
+ │ ├── message_loc: (1,11)-(1,14) = "arg"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,15)-(1,27))
+ ├── locals: [:bar]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,18)-(1,23))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,19)-(1,22))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,19)-(1,22))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :bar
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,18)-(1,19) = "|"
+ │ └── closing_loc: (1,22)-(1,23) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,15)-(1,17) = "do"
+ └── closing_loc: (1,24)-(1,27) = "end"
diff --git a/test/prism/snapshots/seattlerb/block_scope.txt b/test/prism/snapshots/seattlerb/block_scope.txt
new file mode 100644
index 0000000000..a21a28b993
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_scope.txt
@@ -0,0 +1,29 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,10))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,10))
+ ├── locals: [:b]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,8))
+ │ ├── parameters: ∅
+ │ ├── locals: (length: 1)
+ │ │ └── @ BlockLocalVariableNode (location: (1,6)-(1,7))
+ │ │ ├── flags: ∅
+ │ │ └── name: :b
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,7)-(1,8) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,9)-(1,10) = "}"
diff --git a/test/prism/snapshots/seattlerb/block_splat_reg.txt b/test/prism/snapshots/seattlerb/block_splat_reg.txt
new file mode 100644
index 0000000000..617ff88622
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/block_splat_reg.txt
@@ -0,0 +1,42 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,13))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,13))
+ ├── locals: [:b, :c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,11))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,10))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (1,5)-(1,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (1,6)-(1,7) = "b"
+ │ │ │ └── operator_loc: (1,5)-(1,6) = "*"
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,9)-(1,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :c
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,10)-(1,11) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,12)-(1,13) = "}"
diff --git a/test/prism/snapshots/seattlerb/bug169.txt b/test/prism/snapshots/seattlerb/bug169.txt
new file mode 100644
index 0000000000..e4fb47a6de
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug169.txt
@@ -0,0 +1,28 @@
+@ ProgramNode (location: (1,0)-(1,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,7))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,7))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :m
+ ├── message_loc: (1,0)-(1,1) = "m"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,4))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ ParenthesesNode (location: (1,2)-(1,4))
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,2)-(1,3) = "("
+ │ └── closing_loc: (1,3)-(1,4) = ")"
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,5)-(1,7))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── opening_loc: (1,5)-(1,6) = "{"
+ └── closing_loc: (1,6)-(1,7) = "}"
diff --git a/test/prism/snapshots/seattlerb/bug179.txt b/test/prism/snapshots/seattlerb/bug179.txt
new file mode 100644
index 0000000000..d7695bc7a7
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug179.txt
@@ -0,0 +1,28 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,9))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :p
+ ├── message_loc: (1,0)-(1,1) = "p"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,9))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ RangeNode (location: (1,2)-(1,9))
+ │ ├── flags: ∅
+ │ ├── left:
+ │ │ @ ParenthesesNode (location: (1,2)-(1,4))
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (1,2)-(1,3) = "("
+ │ │ └── closing_loc: (1,3)-(1,4) = ")"
+ │ ├── right:
+ │ │ @ NilNode (location: (1,6)-(1,9))
+ │ └── operator_loc: (1,4)-(1,6) = ".."
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/bug190.txt b/test/prism/snapshots/seattlerb/bug190.txt
new file mode 100644
index 0000000000..b261a166cf
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug190.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,6))
+ └── body: (length: 1)
+ └── @ RegularExpressionNode (location: (1,0)-(1,6))
+ ├── flags: forced_us_ascii_encoding
+ ├── opening_loc: (1,0)-(1,3) = "%r'"
+ ├── content_loc: (1,3)-(1,5) = "\\'"
+ ├── closing_loc: (1,5)-(1,6) = "'"
+ └── unescaped: "'"
diff --git a/test/prism/snapshots/seattlerb/bug191.txt b/test/prism/snapshots/seattlerb/bug191.txt
new file mode 100644
index 0000000000..69835ab1d0
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug191.txt
@@ -0,0 +1,87 @@
+@ ProgramNode (location: (1,0)-(3,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,9))
+ └── body: (length: 2)
+ ├── @ IfNode (location: (1,0)-(1,9))
+ │ ├── if_keyword_loc: ∅
+ │ ├── predicate:
+ │ │ @ CallNode (location: (1,0)-(1,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: (1,2)-(1,3) = "?"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,4)-(1,6))
+ │ │ └── body: (length: 1)
+ │ │ └── @ StringNode (location: (1,4)-(1,6))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,4)-(1,5) = "\""
+ │ │ ├── content_loc: (1,5)-(1,5) = ""
+ │ │ ├── closing_loc: (1,5)-(1,6) = "\""
+ │ │ └── unescaped: ""
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (1,6)-(1,9))
+ │ │ ├── else_keyword_loc: (1,6)-(1,7) = ":"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,8)-(1,9))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (1,8)-(1,9))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (1,8)-(1,9) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── end_keyword_loc: ∅
+ │ └── end_keyword_loc: ∅
+ └── @ IfNode (location: (3,0)-(3,9))
+ ├── if_keyword_loc: ∅
+ ├── predicate:
+ │ @ CallNode (location: (3,0)-(3,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (3,0)-(3,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── then_keyword_loc: (3,2)-(3,3) = "?"
+ ├── statements:
+ │ @ StatementsNode (location: (3,4)-(3,6))
+ │ └── body: (length: 1)
+ │ └── @ StringNode (location: (3,4)-(3,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (3,4)-(3,5) = "'"
+ │ ├── content_loc: (3,5)-(3,5) = ""
+ │ ├── closing_loc: (3,5)-(3,6) = "'"
+ │ └── unescaped: ""
+ ├── consequent:
+ │ @ ElseNode (location: (3,6)-(3,9))
+ │ ├── else_keyword_loc: (3,6)-(3,7) = ":"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,8)-(3,9))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (3,8)-(3,9))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (3,8)-(3,9) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── end_keyword_loc: ∅
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/bug202.txt b/test/prism/snapshots/seattlerb/bug202.txt
new file mode 100644
index 0000000000..377b53727e
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug202.txt
@@ -0,0 +1,22 @@
+@ ProgramNode (location: (1,0)-(2,10))
+├── locals: [:测试]
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,10))
+ └── body: (length: 2)
+ ├── @ GlobalVariableWriteNode (location: (1,0)-(1,11))
+ │ ├── name: :$测试
+ │ ├── name_loc: (1,0)-(1,7) = "$测试"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,10)-(1,11))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (1,8)-(1,9) = "="
+ └── @ LocalVariableWriteNode (location: (2,0)-(2,10))
+ ├── name: :测试
+ ├── depth: 0
+ ├── name_loc: (2,0)-(2,6) = "测试"
+ ├── value:
+ │ @ IntegerNode (location: (2,9)-(2,10))
+ │ ├── flags: decimal
+ │ └── value: 1
+ └── operator_loc: (2,7)-(2,8) = "="
diff --git a/test/prism/snapshots/seattlerb/bug236.txt b/test/prism/snapshots/seattlerb/bug236.txt
new file mode 100644
index 0000000000..203a39a793
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug236.txt
@@ -0,0 +1,70 @@
+@ ProgramNode (location: (1,0)-(3,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,6))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,7))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :x
+ │ ├── message_loc: (1,0)-(1,1) = "x"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,1)-(1,7))
+ │ ├── locals: [:a]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (1,2)-(1,6))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (1,3)-(1,5))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (1,3)-(1,4))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ ImplicitRestNode (location: (1,4)-(1,5))
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (1,2)-(1,3) = "|"
+ │ │ └── closing_loc: (1,5)-(1,6) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,1)-(1,2) = "{"
+ │ └── closing_loc: (1,6)-(1,7) = "}"
+ └── @ CallNode (location: (3,0)-(3,6))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :x
+ ├── message_loc: (3,0)-(3,1) = "x"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (3,1)-(3,6))
+ ├── locals: [:a]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (3,2)-(3,5))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (3,3)-(3,4))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (3,3)-(3,4))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (3,2)-(3,3) = "|"
+ │ └── closing_loc: (3,4)-(3,5) = "|"
+ ├── body: ∅
+ ├── opening_loc: (3,1)-(3,2) = "{"
+ └── closing_loc: (3,5)-(3,6) = "}"
diff --git a/test/prism/snapshots/seattlerb/bug290.txt b/test/prism/snapshots/seattlerb/bug290.txt
new file mode 100644
index 0000000000..4f1e673c4b
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug290.txt
@@ -0,0 +1,24 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 1)
+ └── @ BeginNode (location: (1,0)-(3,3))
+ ├── begin_keyword_loc: (1,0)-(1,5) = "begin"
+ ├── statements:
+ │ @ StatementsNode (location: (2,2)-(2,5))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (2,2)-(2,5))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (2,2)-(2,5) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── rescue_clause: ∅
+ ├── else_clause: ∅
+ ├── ensure_clause: ∅
+ └── end_keyword_loc: (3,0)-(3,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/bug_187.txt b/test/prism/snapshots/seattlerb/bug_187.txt
new file mode 100644
index 0000000000..ae72675e5c
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug_187.txt
@@ -0,0 +1,59 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(3,3))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :private
+ ├── message_loc: (1,0)-(1,7) = "private"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,8)-(3,3))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ DefNode (location: (1,8)-(3,3))
+ │ ├── name: :f
+ │ ├── name_loc: (1,12)-(1,13) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (2,0)-(2,10))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (2,0)-(2,10))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (2,0)-(2,1))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (2,0)-(2,1) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (2,1)-(2,2) = "."
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (2,2)-(2,3) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (2,4)-(2,10))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (2,4)-(2,6) = "do"
+ │ │ └── closing_loc: (2,7)-(2,10) = "end"
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (1,8)-(1,11) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (3,0)-(3,3) = "end"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/bug_215.txt b/test/prism/snapshots/seattlerb/bug_215.txt
new file mode 100644
index 0000000000..de7716335e
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug_215.txt
@@ -0,0 +1,14 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ UndefNode (location: (1,0)-(1,13))
+ ├── names: (length: 1)
+ │ └── @ SymbolNode (location: (1,6)-(1,13))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,6)-(1,9) = "%s("
+ │ ├── value_loc: (1,9)-(1,12) = "foo"
+ │ ├── closing_loc: (1,12)-(1,13) = ")"
+ │ └── unescaped: "foo"
+ └── keyword_loc: (1,0)-(1,5) = "undef"
diff --git a/test/prism/snapshots/seattlerb/bug_249.txt b/test/prism/snapshots/seattlerb/bug_249.txt
new file mode 100644
index 0000000000..569bea14c5
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug_249.txt
@@ -0,0 +1,86 @@
+@ ProgramNode (location: (1,0)-(4,28))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,28))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(4,28))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :mount
+ ├── message_loc: (1,0)-(1,5) = "mount"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,6)-(4,28))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ CallNode (location: (1,6)-(4,9))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ ParenthesesNode (location: (1,6)-(4,5))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (1,7)-(4,4))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (1,7)-(4,4))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── receiver:
+ │ │ │ │ │ @ ConstantReadNode (location: (1,7)-(1,12))
+ │ │ │ │ │ └── name: :Class
+ │ │ │ │ ├── call_operator_loc: (1,12)-(1,13) = "."
+ │ │ │ │ ├── name: :new
+ │ │ │ │ ├── message_loc: (1,13)-(1,16) = "new"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block:
+ │ │ │ │ @ BlockNode (location: (1,17)-(4,4))
+ │ │ │ │ ├── locals: []
+ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ ├── body:
+ │ │ │ │ │ @ StatementsNode (location: (2,0)-(3,3))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ DefNode (location: (2,0)-(3,3))
+ │ │ │ │ │ ├── name: :initialize
+ │ │ │ │ │ ├── name_loc: (2,4)-(2,14) = "initialize"
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ │ ├── body: ∅
+ │ │ │ │ │ ├── locals: []
+ │ │ │ │ │ ├── def_keyword_loc: (2,0)-(2,3) = "def"
+ │ │ │ │ │ ├── operator_loc: ∅
+ │ │ │ │ │ ├── lparen_loc: ∅
+ │ │ │ │ │ ├── rparen_loc: ∅
+ │ │ │ │ │ ├── equal_loc: ∅
+ │ │ │ │ │ └── end_keyword_loc: (3,0)-(3,3) = "end"
+ │ │ │ │ ├── opening_loc: (1,17)-(1,19) = "do"
+ │ │ │ │ └── closing_loc: (4,1)-(4,4) = "end"
+ │ │ │ ├── opening_loc: (1,6)-(1,7) = "("
+ │ │ │ └── closing_loc: (4,4)-(4,5) = ")"
+ │ │ ├── call_operator_loc: (4,5)-(4,6) = "."
+ │ │ ├── name: :new
+ │ │ ├── message_loc: (4,6)-(4,9) = "new"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── @ KeywordHashNode (location: (4,11)-(4,28))
+ │ ├── flags: symbol_keys
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (4,11)-(4,28))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (4,11)-(4,14))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (4,11)-(4,12) = ":"
+ │ │ ├── value_loc: (4,12)-(4,14) = "at"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "at"
+ │ ├── value:
+ │ │ @ StringNode (location: (4,18)-(4,28))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (4,18)-(4,19) = "'"
+ │ │ ├── content_loc: (4,19)-(4,27) = "endpoint"
+ │ │ ├── closing_loc: (4,27)-(4,28) = "'"
+ │ │ └── unescaped: "endpoint"
+ │ └── operator_loc: (4,15)-(4,17) = "=>"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/bug_and.txt b/test/prism/snapshots/seattlerb/bug_and.txt
new file mode 100644
index 0000000000..3daf505e5f
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug_and.txt
@@ -0,0 +1,21 @@
+@ ProgramNode (location: (1,0)-(4,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,11))
+ └── body: (length: 2)
+ ├── @ AndNode (location: (1,0)-(2,4))
+ │ ├── left:
+ │ │ @ TrueNode (location: (1,0)-(1,4))
+ │ ├── right:
+ │ │ @ TrueNode (location: (2,0)-(2,4))
+ │ └── operator_loc: (1,5)-(1,8) = "and"
+ └── @ AndNode (location: (4,0)-(4,11))
+ ├── left:
+ │ @ TrueNode (location: (4,0)-(4,4))
+ ├── right:
+ │ @ ArrayNode (location: (4,9)-(4,11))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 0)
+ │ ├── opening_loc: (4,9)-(4,10) = "["
+ │ └── closing_loc: (4,10)-(4,11) = "]"
+ └── operator_loc: (4,5)-(4,8) = "and"
diff --git a/test/prism/snapshots/seattlerb/bug_args__19.txt b/test/prism/snapshots/seattlerb/bug_args__19.txt
new file mode 100644
index 0000000000..f451bd0172
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug_args__19.txt
@@ -0,0 +1,58 @@
+@ ProgramNode (location: (1,0)-(1,16))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,16))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,16))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,16))
+ ├── locals: [:a, :b]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,12))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,11))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ MultiTargetNode (location: (1,5)-(1,11))
+ │ │ │ ├── lefts: (length: 2)
+ │ │ │ │ ├── @ RequiredParameterNode (location: (1,6)-(1,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :a
+ │ │ │ │ └── @ RequiredParameterNode (location: (1,9)-(1,10))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :b
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: (1,5)-(1,6) = "("
+ │ │ │ └── rparen_loc: (1,10)-(1,11) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,11)-(1,12) = "|"
+ ├── body:
+ │ @ StatementsNode (location: (1,13)-(1,14))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,13)-(1,14))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :d
+ │ ├── message_loc: (1,13)-(1,14) = "d"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,15)-(1,16) = "}"
diff --git a/test/prism/snapshots/seattlerb/bug_args_masgn.txt b/test/prism/snapshots/seattlerb/bug_args_masgn.txt
new file mode 100644
index 0000000000..297979c182
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug_args_masgn.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(1,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,17))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,17))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,17))
+ ├── locals: [:a, :b, :c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,15))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,14))
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ MultiTargetNode (location: (1,5)-(1,11))
+ │ │ │ │ ├── lefts: (length: 2)
+ │ │ │ │ │ ├── @ RequiredParameterNode (location: (1,6)-(1,7))
+ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ └── name: :a
+ │ │ │ │ │ └── @ RequiredParameterNode (location: (1,9)-(1,10))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :b
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── rights: (length: 0)
+ │ │ │ │ ├── lparen_loc: (1,5)-(1,6) = "("
+ │ │ │ │ └── rparen_loc: (1,10)-(1,11) = ")"
+ │ │ │ └── @ RequiredParameterNode (location: (1,13)-(1,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :c
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,14)-(1,15) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,16)-(1,17) = "}"
diff --git a/test/prism/snapshots/seattlerb/bug_args_masgn2.txt b/test/prism/snapshots/seattlerb/bug_args_masgn2.txt
new file mode 100644
index 0000000000..6bec9187b3
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug_args_masgn2.txt
@@ -0,0 +1,58 @@
+@ ProgramNode (location: (1,0)-(1,22))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,22))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,22))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,22))
+ ├── locals: [:a, :b, :c, :d]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,20))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,19))
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ MultiTargetNode (location: (1,5)-(1,16))
+ │ │ │ │ ├── lefts: (length: 2)
+ │ │ │ │ │ ├── @ MultiTargetNode (location: (1,6)-(1,12))
+ │ │ │ │ │ │ ├── lefts: (length: 2)
+ │ │ │ │ │ │ │ ├── @ RequiredParameterNode (location: (1,7)-(1,8))
+ │ │ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ │ │ └── name: :a
+ │ │ │ │ │ │ │ └── @ RequiredParameterNode (location: (1,10)-(1,11))
+ │ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ │ └── name: :b
+ │ │ │ │ │ │ ├── rest: ∅
+ │ │ │ │ │ │ ├── rights: (length: 0)
+ │ │ │ │ │ │ ├── lparen_loc: (1,6)-(1,7) = "("
+ │ │ │ │ │ │ └── rparen_loc: (1,11)-(1,12) = ")"
+ │ │ │ │ │ └── @ RequiredParameterNode (location: (1,14)-(1,15))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :c
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── rights: (length: 0)
+ │ │ │ │ ├── lparen_loc: (1,5)-(1,6) = "("
+ │ │ │ │ └── rparen_loc: (1,15)-(1,16) = ")"
+ │ │ │ └── @ RequiredParameterNode (location: (1,18)-(1,19))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :d
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,19)-(1,20) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,21)-(1,22) = "}"
diff --git a/test/prism/snapshots/seattlerb/bug_args_masgn_outer_parens__19.txt b/test/prism/snapshots/seattlerb/bug_args_masgn_outer_parens__19.txt
new file mode 100644
index 0000000000..42a060d02a
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug_args_masgn_outer_parens__19.txt
@@ -0,0 +1,55 @@
+@ ProgramNode (location: (1,0)-(1,19))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,19))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,19))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,19))
+ ├── locals: [:k, :v, :i]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,17))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,16))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ MultiTargetNode (location: (1,5)-(1,16))
+ │ │ │ ├── lefts: (length: 2)
+ │ │ │ │ ├── @ MultiTargetNode (location: (1,6)-(1,12))
+ │ │ │ │ │ ├── lefts: (length: 2)
+ │ │ │ │ │ │ ├── @ RequiredParameterNode (location: (1,7)-(1,8))
+ │ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ │ └── name: :k
+ │ │ │ │ │ │ └── @ RequiredParameterNode (location: (1,10)-(1,11))
+ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ └── name: :v
+ │ │ │ │ │ ├── rest: ∅
+ │ │ │ │ │ ├── rights: (length: 0)
+ │ │ │ │ │ ├── lparen_loc: (1,6)-(1,7) = "("
+ │ │ │ │ │ └── rparen_loc: (1,11)-(1,12) = ")"
+ │ │ │ │ └── @ RequiredParameterNode (location: (1,14)-(1,15))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :i
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: (1,5)-(1,6) = "("
+ │ │ │ └── rparen_loc: (1,15)-(1,16) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,16)-(1,17) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,18)-(1,19) = "}"
diff --git a/test/prism/snapshots/seattlerb/bug_call_arglist_parens.txt b/test/prism/snapshots/seattlerb/bug_call_arglist_parens.txt
new file mode 100644
index 0000000000..53d6f9220b
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug_call_arglist_parens.txt
@@ -0,0 +1,110 @@
+@ ProgramNode (location: (1,6)-(11,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,6)-(11,9))
+ └── body: (length: 3)
+ ├── @ DefNode (location: (1,6)-(3,9))
+ │ ├── name: :f
+ │ ├── name_loc: (1,10)-(1,11) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (2,8)-(2,17))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (2,8)-(2,17))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :g
+ │ │ ├── message_loc: (2,8)-(2,9) = "g"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (2,10)-(2,17))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 2)
+ │ │ │ ├── @ ParenthesesNode (location: (2,10)-(2,14))
+ │ │ │ │ ├── body:
+ │ │ │ │ │ @ StatementsNode (location: (2,12)-(2,13))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ IntegerNode (location: (2,12)-(2,13))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ ├── opening_loc: (2,10)-(2,11) = "("
+ │ │ │ │ └── closing_loc: (2,13)-(2,14) = ")"
+ │ │ │ └── @ IntegerNode (location: (2,16)-(2,17))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (1,6)-(1,9) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (3,6)-(3,9) = "end"
+ ├── @ DefNode (location: (6,6)-(8,9))
+ │ ├── name: :f
+ │ ├── name_loc: (6,10)-(6,11) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (7,8)-(7,16))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (7,8)-(7,16))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :g
+ │ │ ├── message_loc: (7,8)-(7,9) = "g"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (7,10)-(7,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 2)
+ │ │ │ ├── @ ParenthesesNode (location: (7,10)-(7,13))
+ │ │ │ │ ├── body:
+ │ │ │ │ │ @ StatementsNode (location: (7,11)-(7,12))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ IntegerNode (location: (7,11)-(7,12))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ ├── opening_loc: (7,10)-(7,11) = "("
+ │ │ │ │ └── closing_loc: (7,12)-(7,13) = ")"
+ │ │ │ └── @ IntegerNode (location: (7,15)-(7,16))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (6,6)-(6,9) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (6,11)-(6,12) = "("
+ │ ├── rparen_loc: (6,12)-(6,13) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (8,6)-(8,9) = "end"
+ └── @ CallNode (location: (11,0)-(11,9))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :g
+ ├── message_loc: (11,0)-(11,1) = "g"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (11,2)-(11,9))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ ParenthesesNode (location: (11,2)-(11,6))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (11,4)-(11,5))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (11,4)-(11,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── opening_loc: (11,2)-(11,3) = "("
+ │ │ └── closing_loc: (11,5)-(11,6) = ")"
+ │ └── @ IntegerNode (location: (11,8)-(11,9))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/bug_case_when_regexp.txt b/test/prism/snapshots/seattlerb/bug_case_when_regexp.txt
new file mode 100644
index 0000000000..0cc1ca05e1
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug_case_when_regexp.txt
@@ -0,0 +1,28 @@
+@ ProgramNode (location: (1,0)-(1,26))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,26))
+ └── body: (length: 1)
+ └── @ CaseNode (location: (1,0)-(1,26))
+ ├── predicate:
+ │ @ SymbolNode (location: (1,5)-(1,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ ├── value_loc: (1,6)-(1,7) = "x"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "x"
+ ├── conditions: (length: 1)
+ │ └── @ WhenNode (location: (1,9)-(1,22))
+ │ ├── keyword_loc: (1,9)-(1,13) = "when"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ RegularExpressionNode (location: (1,14)-(1,17))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,14)-(1,15) = "/"
+ │ │ ├── content_loc: (1,15)-(1,16) = "x"
+ │ │ ├── closing_loc: (1,16)-(1,17) = "/"
+ │ │ └── unescaped: "x"
+ │ ├── then_keyword_loc: (1,18)-(1,22) = "then"
+ │ └── statements: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (1,23)-(1,26) = "end"
diff --git a/test/prism/snapshots/seattlerb/bug_comma.txt b/test/prism/snapshots/seattlerb/bug_comma.txt
new file mode 100644
index 0000000000..af886999b5
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug_comma.txt
@@ -0,0 +1,41 @@
+@ ProgramNode (location: (1,0)-(1,24))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,24))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(1,24))
+ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ ├── predicate:
+ │ @ CallNode (location: (1,3)-(1,15))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :test
+ │ ├── message_loc: (1,3)-(1,7) = "test"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,8)-(1,15))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ StringNode (location: (1,8)-(1,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (1,8)-(1,9) = "?"
+ │ │ │ ├── content_loc: (1,9)-(1,10) = "d"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "d"
+ │ │ └── @ CallNode (location: (1,12)-(1,15))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :dir
+ │ │ ├── message_loc: (1,12)-(1,15) = "dir"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── then_keyword_loc: (1,16)-(1,20) = "then"
+ ├── statements: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (1,21)-(1,24) = "end"
diff --git a/test/prism/snapshots/seattlerb/bug_cond_pct.txt b/test/prism/snapshots/seattlerb/bug_cond_pct.txt
new file mode 100644
index 0000000000..cbf3bc3ef0
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug_cond_pct.txt
@@ -0,0 +1,22 @@
+@ ProgramNode (location: (1,0)-(1,28))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,28))
+ └── body: (length: 1)
+ └── @ CaseNode (location: (1,0)-(1,28))
+ ├── predicate: ∅
+ ├── conditions: (length: 1)
+ │ └── @ WhenNode (location: (1,6)-(1,23))
+ │ ├── keyword_loc: (1,6)-(1,10) = "when"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ RegularExpressionNode (location: (1,11)-(1,23))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,11)-(1,14) = "%r%"
+ │ │ ├── content_loc: (1,14)-(1,22) = "blahblah"
+ │ │ ├── closing_loc: (1,22)-(1,23) = "%"
+ │ │ └── unescaped: "blahblah"
+ │ ├── then_keyword_loc: ∅
+ │ └── statements: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (1,25)-(1,28) = "end"
diff --git a/test/prism/snapshots/seattlerb/bug_hash_args.txt b/test/prism/snapshots/seattlerb/bug_hash_args.txt
new file mode 100644
index 0000000000..6f17e88714
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug_hash_args.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(1,19))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,19))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,19))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :foo
+ ├── message_loc: (1,0)-(1,3) = "foo"
+ ├── opening_loc: (1,3)-(1,4) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,18))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ SymbolNode (location: (1,4)-(1,8))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,4)-(1,5) = ":"
+ │ │ ├── value_loc: (1,5)-(1,8) = "bar"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "bar"
+ │ └── @ KeywordHashNode (location: (1,10)-(1,18))
+ │ ├── flags: symbol_keys
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (1,10)-(1,18))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (1,10)-(1,14))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (1,10)-(1,13) = "baz"
+ │ │ ├── closing_loc: (1,13)-(1,14) = ":"
+ │ │ └── unescaped: "baz"
+ │ ├── value:
+ │ │ @ NilNode (location: (1,15)-(1,18))
+ │ └── operator_loc: ∅
+ ├── closing_loc: (1,18)-(1,19) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/bug_hash_args_trailing_comma.txt b/test/prism/snapshots/seattlerb/bug_hash_args_trailing_comma.txt
new file mode 100644
index 0000000000..e7256b337b
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug_hash_args_trailing_comma.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(1,20))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,20))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,20))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :foo
+ ├── message_loc: (1,0)-(1,3) = "foo"
+ ├── opening_loc: (1,3)-(1,4) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,18))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ SymbolNode (location: (1,4)-(1,8))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,4)-(1,5) = ":"
+ │ │ ├── value_loc: (1,5)-(1,8) = "bar"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "bar"
+ │ └── @ KeywordHashNode (location: (1,10)-(1,18))
+ │ ├── flags: symbol_keys
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (1,10)-(1,18))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (1,10)-(1,14))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (1,10)-(1,13) = "baz"
+ │ │ ├── closing_loc: (1,13)-(1,14) = ":"
+ │ │ └── unescaped: "baz"
+ │ ├── value:
+ │ │ @ NilNode (location: (1,15)-(1,18))
+ │ └── operator_loc: ∅
+ ├── closing_loc: (1,19)-(1,20) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/bug_hash_interp_array.txt b/test/prism/snapshots/seattlerb/bug_hash_interp_array.txt
new file mode 100644
index 0000000000..433fb02411
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug_hash_interp_array.txt
@@ -0,0 +1,26 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ HashNode (location: (1,0)-(1,13))
+ ├── opening_loc: (1,0)-(1,1) = "{"
+ ├── elements: (length: 1)
+ │ └── @ AssocNode (location: (1,2)-(1,11))
+ │ ├── key:
+ │ │ @ InterpolatedSymbolNode (location: (1,2)-(1,8))
+ │ │ ├── opening_loc: (1,2)-(1,3) = "\""
+ │ │ ├── parts: (length: 1)
+ │ │ │ └── @ EmbeddedStatementsNode (location: (1,3)-(1,6))
+ │ │ │ ├── opening_loc: (1,3)-(1,5) = "\#{"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── closing_loc: (1,5)-(1,6) = "}"
+ │ │ └── closing_loc: (1,6)-(1,8) = "\":"
+ │ ├── value:
+ │ │ @ ArrayNode (location: (1,9)-(1,11))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 0)
+ │ │ ├── opening_loc: (1,9)-(1,10) = "["
+ │ │ └── closing_loc: (1,10)-(1,11) = "]"
+ │ └── operator_loc: ∅
+ └── closing_loc: (1,12)-(1,13) = "}"
diff --git a/test/prism/snapshots/seattlerb/bug_masgn_right.txt b/test/prism/snapshots/seattlerb/bug_masgn_right.txt
new file mode 100644
index 0000000000..b4c75c4607
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug_masgn_right.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(1,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,17))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,17))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,17))
+ ├── locals: [:a, :b, :c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,15))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,14))
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ └── @ MultiTargetNode (location: (1,8)-(1,14))
+ │ │ │ ├── lefts: (length: 2)
+ │ │ │ │ ├── @ RequiredParameterNode (location: (1,9)-(1,10))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :b
+ │ │ │ │ └── @ RequiredParameterNode (location: (1,12)-(1,13))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :c
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: (1,8)-(1,9) = "("
+ │ │ │ └── rparen_loc: (1,13)-(1,14) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,14)-(1,15) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,16)-(1,17) = "}"
diff --git a/test/prism/snapshots/seattlerb/bug_not_parens.txt b/test/prism/snapshots/seattlerb/bug_not_parens.txt
new file mode 100644
index 0000000000..9e4a416d4a
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug_not_parens.txt
@@ -0,0 +1,25 @@
+@ ProgramNode (location: (1,0)-(1,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,6))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,6))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,4)-(1,5))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,4)-(1,5) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :!
+ ├── message_loc: (1,0)-(1,3) = "not"
+ ├── opening_loc: (1,3)-(1,4) = "("
+ ├── arguments: ∅
+ ├── closing_loc: (1,5)-(1,6) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/bug_op_asgn_rescue.txt b/test/prism/snapshots/seattlerb/bug_op_asgn_rescue.txt
new file mode 100644
index 0000000000..33016f32f8
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/bug_op_asgn_rescue.txt
@@ -0,0 +1,26 @@
+@ ProgramNode (location: (1,0)-(1,18))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,18))
+ └── body: (length: 1)
+ └── @ LocalVariableOrWriteNode (location: (1,0)-(1,18))
+ ├── name_loc: (1,0)-(1,1) = "a"
+ ├── operator_loc: (1,2)-(1,5) = "||="
+ ├── value:
+ │ @ RescueModifierNode (location: (1,6)-(1,18))
+ │ ├── expression:
+ │ │ @ CallNode (location: (1,6)-(1,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (1,6)-(1,7) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (1,8)-(1,14) = "rescue"
+ │ └── rescue_expression:
+ │ @ NilNode (location: (1,15)-(1,18))
+ ├── name: :a
+ └── depth: 0
diff --git a/test/prism/snapshots/seattlerb/call_and.txt b/test/prism/snapshots/seattlerb/call_and.txt
new file mode 100644
index 0000000000..d3e88b3f9e
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_and.txt
@@ -0,0 +1,24 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,5))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── call_operator_loc: ∅
+ ├── name: :&
+ ├── message_loc: (1,2)-(1,3) = "&"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,5))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,4)-(1,5))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_arg_assoc.txt b/test/prism/snapshots/seattlerb/call_arg_assoc.txt
new file mode 100644
index 0000000000..27c19fd339
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_arg_assoc.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,10))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: (1,1)-(1,2) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,9))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ IntegerNode (location: (1,2)-(1,3))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── @ KeywordHashNode (location: (1,5)-(1,9))
+ │ ├── flags: ∅
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (1,5)-(1,9))
+ │ ├── key:
+ │ │ @ IntegerNode (location: (1,5)-(1,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,8)-(1,9))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ └── operator_loc: (1,6)-(1,8) = "=>"
+ ├── closing_loc: (1,9)-(1,10) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt b/test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt
new file mode 100644
index 0000000000..0193eb1dfc
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt
@@ -0,0 +1,43 @@
+@ ProgramNode (location: (1,0)-(1,16))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,16))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,16))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: (1,1)-(1,2) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,15))
+ │ ├── flags: contains_keyword_splat
+ │ └── arguments: (length: 2)
+ │ ├── @ IntegerNode (location: (1,2)-(1,3))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── @ KeywordHashNode (location: (1,5)-(1,15))
+ │ ├── flags: ∅
+ │ └── elements: (length: 2)
+ │ ├── @ AssocNode (location: (1,5)-(1,10))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (1,5)-(1,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (1,5)-(1,7) = "kw"
+ │ │ │ ├── closing_loc: (1,7)-(1,8) = ":"
+ │ │ │ └── unescaped: "kw"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (1,9)-(1,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── operator_loc: ∅
+ │ └── @ AssocSplatNode (location: (1,12)-(1,15))
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,14)-(1,15))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ └── operator_loc: (1,12)-(1,14) = "**"
+ ├── closing_loc: (1,15)-(1,16) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_arg_kwsplat.txt b/test/prism/snapshots/seattlerb/call_arg_kwsplat.txt
new file mode 100644
index 0000000000..91c7725525
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_arg_kwsplat.txt
@@ -0,0 +1,37 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,9))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: (1,1)-(1,2) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,8))
+ │ ├── flags: contains_keyword_splat
+ │ └── arguments: (length: 2)
+ │ ├── @ CallNode (location: (1,2)-(1,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (1,2)-(1,3) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── @ KeywordHashNode (location: (1,5)-(1,8))
+ │ ├── flags: ∅
+ │ └── elements: (length: 1)
+ │ └── @ AssocSplatNode (location: (1,5)-(1,8))
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,7)-(1,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (1,5)-(1,7) = "**"
+ ├── closing_loc: (1,8)-(1,9) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt b/test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt
new file mode 100644
index 0000000000..2d6f81c818
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt
@@ -0,0 +1,106 @@
+@ ProgramNode (location: (1,0)-(5,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,8))
+ └── body: (length: 3)
+ ├── @ CallNode (location: (1,0)-(1,11))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :x
+ │ ├── message_loc: (1,0)-(1,1) = "x"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,2)-(1,11))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (1,2)-(1,11))
+ │ │ ├── flags: ∅
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (1,2)-(1,11))
+ │ │ ├── key:
+ │ │ │ @ InterpolatedSymbolNode (location: (1,2)-(1,9))
+ │ │ │ ├── opening_loc: (1,2)-(1,3) = "\""
+ │ │ │ ├── parts: (length: 1)
+ │ │ │ │ └── @ EmbeddedStatementsNode (location: (1,3)-(1,7))
+ │ │ │ │ ├── opening_loc: (1,3)-(1,5) = "\#{"
+ │ │ │ │ ├── statements:
+ │ │ │ │ │ @ StatementsNode (location: (1,5)-(1,6))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (1,5)-(1,6))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :k
+ │ │ │ │ │ ├── message_loc: (1,5)-(1,6) = "k"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ └── closing_loc: (1,6)-(1,7) = "}"
+ │ │ │ └── closing_loc: (1,7)-(1,9) = "\":"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (1,9)-(1,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ └── operator_loc: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (3,0)-(3,8))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :x
+ │ ├── message_loc: (3,0)-(3,1) = "x"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,2)-(3,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (3,2)-(3,8))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (3,2)-(3,8))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (3,2)-(3,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (3,2)-(3,3) = "\""
+ │ │ │ ├── value_loc: (3,3)-(3,4) = "k"
+ │ │ │ ├── closing_loc: (3,4)-(3,6) = "\":"
+ │ │ │ └── unescaped: "k"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (3,6)-(3,8))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ └── operator_loc: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (5,0)-(5,8))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :x
+ ├── message_loc: (5,0)-(5,1) = "x"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (5,2)-(5,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ KeywordHashNode (location: (5,2)-(5,8))
+ │ ├── flags: symbol_keys
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (5,2)-(5,8))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (5,2)-(5,6))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (5,2)-(5,3) = "'"
+ │ │ ├── value_loc: (5,3)-(5,4) = "k"
+ │ │ ├── closing_loc: (5,4)-(5,6) = "':"
+ │ │ └── unescaped: "k"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (5,6)-(5,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 42
+ │ └── operator_loc: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_args_assoc_trailing_comma.txt b/test/prism/snapshots/seattlerb/call_args_assoc_trailing_comma.txt
new file mode 100644
index 0000000000..312a1981a0
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_args_assoc_trailing_comma.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,11))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: (1,1)-(1,2) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,9))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ IntegerNode (location: (1,2)-(1,3))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── @ KeywordHashNode (location: (1,5)-(1,9))
+ │ ├── flags: ∅
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (1,5)-(1,9))
+ │ ├── key:
+ │ │ @ IntegerNode (location: (1,5)-(1,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,8)-(1,9))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ └── operator_loc: (1,6)-(1,8) = "=>"
+ ├── closing_loc: (1,10)-(1,11) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_args_command.txt b/test/prism/snapshots/seattlerb/call_args_command.txt
new file mode 100644
index 0000000000..f4a55ff58c
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_args_command.txt
@@ -0,0 +1,54 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,9))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,1)-(1,2) = "."
+ ├── name: :b
+ ├── message_loc: (1,2)-(1,3) = "b"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,9))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (1,4)-(1,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,4)-(1,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :c
+ │ │ ├── message_loc: (1,4)-(1,5) = "c"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (1,5)-(1,6) = "."
+ │ ├── name: :d
+ │ ├── message_loc: (1,6)-(1,7) = "d"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,8)-(1,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,8)-(1,9))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_array_arg.txt b/test/prism/snapshots/seattlerb/call_array_arg.txt
new file mode 100644
index 0000000000..95d2f4859d
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_array_arg.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,13))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── call_operator_loc: ∅
+ ├── name: :==
+ ├── message_loc: (1,2)-(1,4) = "=="
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,5)-(1,13))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ ArrayNode (location: (1,5)-(1,13))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ SymbolNode (location: (1,6)-(1,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (1,6)-(1,7) = ":"
+ │ │ │ ├── value_loc: (1,7)-(1,8) = "b"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "b"
+ │ │ └── @ SymbolNode (location: (1,10)-(1,12))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,10)-(1,11) = ":"
+ │ │ ├── value_loc: (1,11)-(1,12) = "c"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "c"
+ │ ├── opening_loc: (1,5)-(1,6) = "["
+ │ └── closing_loc: (1,12)-(1,13) = "]"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_array_block_call.txt b/test/prism/snapshots/seattlerb/call_array_block_call.txt
new file mode 100644
index 0000000000..e02740e7f5
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_array_block_call.txt
@@ -0,0 +1,40 @@
+@ ProgramNode (location: (1,0)-(1,19))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,19))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,19))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,19))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ ArrayNode (location: (1,2)-(1,19))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ NilNode (location: (1,4)-(1,7))
+ │ │ └── @ CallNode (location: (1,9)-(1,17))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (1,9)-(1,10) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (1,11)-(1,17))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (1,11)-(1,13) = "do"
+ │ │ └── closing_loc: (1,14)-(1,17) = "end"
+ │ ├── opening_loc: (1,2)-(1,3) = "["
+ │ └── closing_loc: (1,18)-(1,19) = "]"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_array_lambda_block_call.txt b/test/prism/snapshots/seattlerb/call_array_lambda_block_call.txt
new file mode 100644
index 0000000000..c6aa722812
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_array_lambda_block_call.txt
@@ -0,0 +1,41 @@
+@ ProgramNode (location: (1,0)-(2,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,3))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(2,3))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,11))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ ArrayNode (location: (1,2)-(1,11))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ LambdaNode (location: (1,3)-(1,10))
+ │ │ ├── locals: []
+ │ │ ├── operator_loc: (1,3)-(1,5) = "->"
+ │ │ ├── opening_loc: (1,8)-(1,9) = "{"
+ │ │ ├── closing_loc: (1,9)-(1,10) = "}"
+ │ │ ├── parameters:
+ │ │ │ @ BlockParametersNode (location: (1,5)-(1,7))
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── locals: (length: 0)
+ │ │ │ ├── opening_loc: (1,5)-(1,6) = "("
+ │ │ │ └── closing_loc: (1,6)-(1,7) = ")"
+ │ │ └── body: ∅
+ │ ├── opening_loc: (1,2)-(1,3) = "["
+ │ └── closing_loc: (1,10)-(1,11) = "]"
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,12)-(2,3))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── opening_loc: (1,12)-(1,14) = "do"
+ └── closing_loc: (2,0)-(2,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/call_array_lit_inline_hash.txt b/test/prism/snapshots/seattlerb/call_array_lit_inline_hash.txt
new file mode 100644
index 0000000000..091e21c00a
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_array_lit_inline_hash.txt
@@ -0,0 +1,45 @@
+@ ProgramNode (location: (1,0)-(1,16))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,16))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,16))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: (1,1)-(1,2) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,15))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ ArrayNode (location: (1,2)-(1,15))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ SymbolNode (location: (1,3)-(1,5))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (1,3)-(1,4) = ":"
+ │ │ │ ├── value_loc: (1,4)-(1,5) = "b"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "b"
+ │ │ └── @ KeywordHashNode (location: (1,7)-(1,14))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (1,7)-(1,14))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (1,7)-(1,9))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (1,7)-(1,8) = ":"
+ │ │ │ ├── value_loc: (1,8)-(1,9) = "c"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "c"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (1,13)-(1,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (1,10)-(1,12) = "=>"
+ │ ├── opening_loc: (1,2)-(1,3) = "["
+ │ └── closing_loc: (1,14)-(1,15) = "]"
+ ├── closing_loc: (1,15)-(1,16) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_assoc.txt b/test/prism/snapshots/seattlerb/call_assoc.txt
new file mode 100644
index 0000000000..438c256553
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_assoc.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (1,0)-(1,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,7))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,7))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: (1,1)-(1,2) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,6))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ KeywordHashNode (location: (1,2)-(1,6))
+ │ ├── flags: ∅
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (1,2)-(1,6))
+ │ ├── key:
+ │ │ @ IntegerNode (location: (1,2)-(1,3))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,5)-(1,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ └── operator_loc: (1,3)-(1,5) = "=>"
+ ├── closing_loc: (1,6)-(1,7) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_assoc_new.txt b/test/prism/snapshots/seattlerb/call_assoc_new.txt
new file mode 100644
index 0000000000..b4d7e0bf83
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_assoc_new.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,6))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,6))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: (1,1)-(1,2) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,5))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ KeywordHashNode (location: (1,2)-(1,5))
+ │ ├── flags: symbol_keys
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (1,2)-(1,5))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (1,2)-(1,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (1,2)-(1,3) = "a"
+ │ │ ├── closing_loc: (1,3)-(1,4) = ":"
+ │ │ └── unescaped: "a"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,4)-(1,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ └── operator_loc: ∅
+ ├── closing_loc: (1,5)-(1,6) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_assoc_new_if_multiline.txt b/test/prism/snapshots/seattlerb/call_assoc_new_if_multiline.txt
new file mode 100644
index 0000000000..9587e2e074
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_assoc_new_if_multiline.txt
@@ -0,0 +1,58 @@
+@ ProgramNode (location: (1,0)-(5,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,4))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(5,4))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: (1,1)-(1,2) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(5,3))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ KeywordHashNode (location: (1,2)-(5,3))
+ │ ├── flags: symbol_keys
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (1,2)-(5,3))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (1,2)-(1,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (1,2)-(1,3) = "b"
+ │ │ ├── closing_loc: (1,3)-(1,4) = ":"
+ │ │ └── unescaped: "b"
+ │ ├── value:
+ │ │ @ IfNode (location: (1,5)-(5,3))
+ │ │ ├── if_keyword_loc: (1,5)-(1,7) = "if"
+ │ │ ├── predicate:
+ │ │ │ @ SymbolNode (location: (1,8)-(1,10))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (1,8)-(1,9) = ":"
+ │ │ │ ├── value_loc: (1,9)-(1,10) = "c"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "c"
+ │ │ ├── then_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (2,0)-(2,1))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (2,0)-(2,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── consequent:
+ │ │ │ @ ElseNode (location: (3,0)-(5,3))
+ │ │ │ ├── else_keyword_loc: (3,0)-(3,4) = "else"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (4,0)-(4,1))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (4,0)-(4,1))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ └── end_keyword_loc: (5,0)-(5,3) = "end"
+ │ │ └── end_keyword_loc: (5,0)-(5,3) = "end"
+ │ └── operator_loc: ∅
+ ├── closing_loc: (5,3)-(5,4) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt b/test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt
new file mode 100644
index 0000000000..8d0b285172
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,8))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: (1,1)-(1,2) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,6))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ KeywordHashNode (location: (1,2)-(1,6))
+ │ ├── flags: ∅
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (1,2)-(1,6))
+ │ ├── key:
+ │ │ @ IntegerNode (location: (1,2)-(1,3))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,5)-(1,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ └── operator_loc: (1,3)-(1,5) = "=>"
+ ├── closing_loc: (1,7)-(1,8) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_bang_command_call.txt b/test/prism/snapshots/seattlerb/call_bang_command_call.txt
new file mode 100644
index 0000000000..5e4e10d953
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_bang_command_call.txt
@@ -0,0 +1,41 @@
+@ ProgramNode (location: (1,0)-(1,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,7))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,7))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,2)-(1,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,2)-(1,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (1,2)-(1,3) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (1,3)-(1,4) = "."
+ │ ├── name: :b
+ │ ├── message_loc: (1,4)-(1,5) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,6)-(1,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,6)-(1,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :!
+ ├── message_loc: (1,0)-(1,1) = "!"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_bang_squiggle.txt b/test/prism/snapshots/seattlerb/call_bang_squiggle.txt
new file mode 100644
index 0000000000..bf11bc0136
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_bang_squiggle.txt
@@ -0,0 +1,24 @@
+@ ProgramNode (location: (1,0)-(1,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,6))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,6))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── call_operator_loc: ∅
+ ├── name: :!~
+ ├── message_loc: (1,2)-(1,4) = "!~"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,5)-(1,6))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,5)-(1,6))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_begin_call_block_call.txt b/test/prism/snapshots/seattlerb/call_begin_call_block_call.txt
new file mode 100644
index 0000000000..1aa994c8e6
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_begin_call_block_call.txt
@@ -0,0 +1,53 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(3,3))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(3,3))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ BeginNode (location: (1,2)-(3,3))
+ │ ├── begin_keyword_loc: (1,2)-(1,7) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (2,0)-(2,10))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (2,0)-(2,10))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (2,0)-(2,1))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (2,0)-(2,1) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (2,1)-(2,2) = "."
+ │ │ ├── name: :c
+ │ │ ├── message_loc: (2,2)-(2,3) = "c"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (2,4)-(2,10))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (2,4)-(2,6) = "do"
+ │ │ └── closing_loc: (2,7)-(2,10) = "end"
+ │ ├── rescue_clause: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (3,0)-(3,3) = "end"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_block_arg_named.txt b/test/prism/snapshots/seattlerb/call_block_arg_named.txt
new file mode 100644
index 0000000000..f87c29cf2e
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_block_arg_named.txt
@@ -0,0 +1,28 @@
+@ ProgramNode (location: (1,0)-(1,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,6))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,6))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :x
+ ├── message_loc: (1,0)-(1,1) = "x"
+ ├── opening_loc: (1,1)-(1,2) = "("
+ ├── arguments: ∅
+ ├── closing_loc: (1,6)-(1,7) = ")"
+ └── block:
+ @ BlockArgumentNode (location: (1,2)-(1,6))
+ ├── expression:
+ │ @ CallNode (location: (1,3)-(1,6))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :blk
+ │ ├── message_loc: (1,3)-(1,6) = "blk"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── operator_loc: (1,2)-(1,3) = "&"
diff --git a/test/prism/snapshots/seattlerb/call_carat.txt b/test/prism/snapshots/seattlerb/call_carat.txt
new file mode 100644
index 0000000000..856e9a7847
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_carat.txt
@@ -0,0 +1,24 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,5))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── call_operator_loc: ∅
+ ├── name: :^
+ ├── message_loc: (1,2)-(1,3) = "^"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,5))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,4)-(1,5))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_colon2.txt b/test/prism/snapshots/seattlerb/call_colon2.txt
new file mode 100644
index 0000000000..98bfc63126
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_colon2.txt
@@ -0,0 +1,17 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,4))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ ConstantReadNode (location: (1,0)-(1,1))
+ │ └── name: :A
+ ├── call_operator_loc: (1,1)-(1,3) = "::"
+ ├── name: :b
+ ├── message_loc: (1,3)-(1,4) = "b"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_colon_parens.txt b/test/prism/snapshots/seattlerb/call_colon_parens.txt
new file mode 100644
index 0000000000..6d10171a2b
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_colon_parens.txt
@@ -0,0 +1,18 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,5))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── call_operator_loc: (1,1)-(1,3) = "::"
+ ├── name: :call
+ ├── message_loc: ∅
+ ├── opening_loc: (1,3)-(1,4) = "("
+ ├── arguments: ∅
+ ├── closing_loc: (1,4)-(1,5) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_div.txt b/test/prism/snapshots/seattlerb/call_div.txt
new file mode 100644
index 0000000000..ba62fb87bd
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_div.txt
@@ -0,0 +1,24 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,5))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── call_operator_loc: ∅
+ ├── name: :/
+ ├── message_loc: (1,2)-(1,3) = "/"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,5))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,4)-(1,5))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_dot_parens.txt b/test/prism/snapshots/seattlerb/call_dot_parens.txt
new file mode 100644
index 0000000000..c9b7084699
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_dot_parens.txt
@@ -0,0 +1,18 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,4))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── call_operator_loc: (1,1)-(1,2) = "."
+ ├── name: :call
+ ├── message_loc: ∅
+ ├── opening_loc: (1,2)-(1,3) = "("
+ ├── arguments: ∅
+ ├── closing_loc: (1,3)-(1,4) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_env.txt b/test/prism/snapshots/seattlerb/call_env.txt
new file mode 100644
index 0000000000..fd1f95388e
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_env.txt
@@ -0,0 +1,25 @@
+@ ProgramNode (location: (1,0)-(1,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,7))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,7))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,1)-(1,2) = "."
+ ├── name: :happy
+ ├── message_loc: (1,2)-(1,7) = "happy"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_eq3.txt b/test/prism/snapshots/seattlerb/call_eq3.txt
new file mode 100644
index 0000000000..e636e15725
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_eq3.txt
@@ -0,0 +1,24 @@
+@ ProgramNode (location: (1,0)-(1,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,7))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,7))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── call_operator_loc: ∅
+ ├── name: :===
+ ├── message_loc: (1,2)-(1,5) = "==="
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,6)-(1,7))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,6)-(1,7))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_gt.txt b/test/prism/snapshots/seattlerb/call_gt.txt
new file mode 100644
index 0000000000..a6f19e5adf
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_gt.txt
@@ -0,0 +1,24 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,5))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── call_operator_loc: ∅
+ ├── name: :>
+ ├── message_loc: (1,2)-(1,3) = ">"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,5))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,4)-(1,5))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_kwsplat.txt b/test/prism/snapshots/seattlerb/call_kwsplat.txt
new file mode 100644
index 0000000000..4199e97a44
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_kwsplat.txt
@@ -0,0 +1,27 @@
+@ ProgramNode (location: (1,0)-(1,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,6))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,6))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: (1,1)-(1,2) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,5))
+ │ ├── flags: contains_keyword_splat
+ │ └── arguments: (length: 1)
+ │ └── @ KeywordHashNode (location: (1,2)-(1,5))
+ │ ├── flags: ∅
+ │ └── elements: (length: 1)
+ │ └── @ AssocSplatNode (location: (1,2)-(1,5))
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,4)-(1,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (1,2)-(1,4) = "**"
+ ├── closing_loc: (1,5)-(1,6) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_leading_dots.txt b/test/prism/snapshots/seattlerb/call_leading_dots.txt
new file mode 100644
index 0000000000..e8435d7e7a
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_leading_dots.txt
@@ -0,0 +1,35 @@
+@ ProgramNode (location: (1,0)-(3,2))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,2))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(3,2))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(2,2))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,0)-(1,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (2,0)-(2,1) = "."
+ │ ├── name: :b
+ │ ├── message_loc: (2,1)-(2,2) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (3,0)-(3,1) = "."
+ ├── name: :c
+ ├── message_loc: (3,1)-(3,2) = "c"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_leading_dots_comment.txt b/test/prism/snapshots/seattlerb/call_leading_dots_comment.txt
new file mode 100644
index 0000000000..e5dfb72372
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_leading_dots_comment.txt
@@ -0,0 +1,35 @@
+@ ProgramNode (location: (1,0)-(4,2))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,2))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(4,2))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(2,2))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,0)-(1,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (2,0)-(2,1) = "."
+ │ ├── name: :b
+ │ ├── message_loc: (2,1)-(2,2) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (4,0)-(4,1) = "."
+ ├── name: :d
+ ├── message_loc: (4,1)-(4,2) = "d"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_lt.txt b/test/prism/snapshots/seattlerb/call_lt.txt
new file mode 100644
index 0000000000..14f50585d9
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_lt.txt
@@ -0,0 +1,24 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,5))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── call_operator_loc: ∅
+ ├── name: :<
+ ├── message_loc: (1,2)-(1,3) = "<"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,5))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,4)-(1,5))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_lte.txt b/test/prism/snapshots/seattlerb/call_lte.txt
new file mode 100644
index 0000000000..665a99d60a
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_lte.txt
@@ -0,0 +1,24 @@
+@ ProgramNode (location: (1,0)-(1,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,6))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,6))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── call_operator_loc: ∅
+ ├── name: :<=
+ ├── message_loc: (1,2)-(1,4) = "<="
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,5)-(1,6))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,5)-(1,6))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_not.txt b/test/prism/snapshots/seattlerb/call_not.txt
new file mode 100644
index 0000000000..86c6892303
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_not.txt
@@ -0,0 +1,18 @@
+@ ProgramNode (location: (1,0)-(1,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,6))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,6))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ IntegerNode (location: (1,4)-(1,6))
+ │ ├── flags: decimal
+ │ └── value: 42
+ ├── call_operator_loc: ∅
+ ├── name: :!
+ ├── message_loc: (1,0)-(1,3) = "not"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_pipe.txt b/test/prism/snapshots/seattlerb/call_pipe.txt
new file mode 100644
index 0000000000..855e986ef6
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_pipe.txt
@@ -0,0 +1,24 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,5))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── call_operator_loc: ∅
+ ├── name: :|
+ ├── message_loc: (1,2)-(1,3) = "|"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,5))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,4)-(1,5))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_rshift.txt b/test/prism/snapshots/seattlerb/call_rshift.txt
new file mode 100644
index 0000000000..26e593db18
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_rshift.txt
@@ -0,0 +1,24 @@
+@ ProgramNode (location: (1,0)-(1,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,6))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,6))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── call_operator_loc: ∅
+ ├── name: :>>
+ ├── message_loc: (1,2)-(1,4) = ">>"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,5)-(1,6))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,5)-(1,6))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_self_brackets.txt b/test/prism/snapshots/seattlerb/call_self_brackets.txt
new file mode 100644
index 0000000000..16ca69b5c2
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_self_brackets.txt
@@ -0,0 +1,22 @@
+@ ProgramNode (location: (1,0)-(1,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,7))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,7))
+ ├── flags: ignore_visibility
+ ├── receiver:
+ │ @ SelfNode (location: (1,0)-(1,4))
+ ├── call_operator_loc: ∅
+ ├── name: :[]
+ ├── message_loc: (1,4)-(1,7) = "[1]"
+ ├── opening_loc: (1,4)-(1,5) = "["
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,5)-(1,6))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,5)-(1,6))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: (1,6)-(1,7) = "]"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_spaceship.txt b/test/prism/snapshots/seattlerb/call_spaceship.txt
new file mode 100644
index 0000000000..8d43c3f971
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_spaceship.txt
@@ -0,0 +1,24 @@
+@ ProgramNode (location: (1,0)-(1,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,7))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,7))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── call_operator_loc: ∅
+ ├── name: :<=>
+ ├── message_loc: (1,2)-(1,5) = "<=>"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,6)-(1,7))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,6)-(1,7))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_stabby_do_end_with_block.txt b/test/prism/snapshots/seattlerb/call_stabby_do_end_with_block.txt
new file mode 100644
index 0000000000..242db9e9cb
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_stabby_do_end_with_block.txt
@@ -0,0 +1,41 @@
+@ ProgramNode (location: (1,0)-(1,22))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,22))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,22))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,13))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ LambdaNode (location: (1,2)-(1,13))
+ │ ├── locals: []
+ │ ├── operator_loc: (1,2)-(1,4) = "->"
+ │ ├── opening_loc: (1,5)-(1,7) = "do"
+ │ ├── closing_loc: (1,10)-(1,13) = "end"
+ │ ├── parameters: ∅
+ │ └── body:
+ │ @ StatementsNode (location: (1,8)-(1,9))
+ │ └── body: (length: 1)
+ │ └── @ IntegerNode (location: (1,8)-(1,9))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,14)-(1,22))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,17)-(1,18))
+ │ └── body: (length: 1)
+ │ └── @ IntegerNode (location: (1,17)-(1,18))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── opening_loc: (1,14)-(1,16) = "do"
+ └── closing_loc: (1,19)-(1,22) = "end"
diff --git a/test/prism/snapshots/seattlerb/call_stabby_with_braces_block.txt b/test/prism/snapshots/seattlerb/call_stabby_with_braces_block.txt
new file mode 100644
index 0000000000..7c3ab8dad8
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_stabby_with_braces_block.txt
@@ -0,0 +1,41 @@
+@ ProgramNode (location: (1,0)-(1,19))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,19))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,19))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,10))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ LambdaNode (location: (1,2)-(1,10))
+ │ ├── locals: []
+ │ ├── operator_loc: (1,2)-(1,4) = "->"
+ │ ├── opening_loc: (1,5)-(1,6) = "{"
+ │ ├── closing_loc: (1,9)-(1,10) = "}"
+ │ ├── parameters: ∅
+ │ └── body:
+ │ @ StatementsNode (location: (1,7)-(1,8))
+ │ └── body: (length: 1)
+ │ └── @ IntegerNode (location: (1,7)-(1,8))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,11)-(1,19))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,14)-(1,15))
+ │ └── body: (length: 1)
+ │ └── @ IntegerNode (location: (1,14)-(1,15))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── opening_loc: (1,11)-(1,13) = "do"
+ └── closing_loc: (1,16)-(1,19) = "end"
diff --git a/test/prism/snapshots/seattlerb/call_star.txt b/test/prism/snapshots/seattlerb/call_star.txt
new file mode 100644
index 0000000000..49aee1672c
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_star.txt
@@ -0,0 +1,24 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,5))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── call_operator_loc: ∅
+ ├── name: :*
+ ├── message_loc: (1,2)-(1,3) = "*"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,5))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,4)-(1,5))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_star2.txt b/test/prism/snapshots/seattlerb/call_star2.txt
new file mode 100644
index 0000000000..cc2532cc7c
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_star2.txt
@@ -0,0 +1,24 @@
+@ ProgramNode (location: (1,0)-(1,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,6))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,6))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── call_operator_loc: ∅
+ ├── name: :**
+ ├── message_loc: (1,2)-(1,4) = "**"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,5)-(1,6))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,5)-(1,6))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_trailing_comma.txt b/test/prism/snapshots/seattlerb/call_trailing_comma.txt
new file mode 100644
index 0000000000..fe28a3ad3e
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_trailing_comma.txt
@@ -0,0 +1,21 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,5))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: (1,1)-(1,2) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,3))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,2)-(1,3))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: (1,4)-(1,5) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_trailing_dots.txt b/test/prism/snapshots/seattlerb/call_trailing_dots.txt
new file mode 100644
index 0000000000..b0e23eb27b
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_trailing_dots.txt
@@ -0,0 +1,35 @@
+@ ProgramNode (location: (1,0)-(3,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,1))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(3,1))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(2,1))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,0)-(1,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (1,1)-(1,2) = "."
+ │ ├── name: :b
+ │ ├── message_loc: (2,0)-(2,1) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (2,1)-(2,2) = "."
+ ├── name: :c
+ ├── message_loc: (3,0)-(3,1) = "c"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/call_unary_bang.txt b/test/prism/snapshots/seattlerb/call_unary_bang.txt
new file mode 100644
index 0000000000..782cc83b10
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/call_unary_bang.txt
@@ -0,0 +1,18 @@
+@ ProgramNode (location: (1,0)-(1,2))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,2))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,2))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ IntegerNode (location: (1,1)-(1,2))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── call_operator_loc: ∅
+ ├── name: :!
+ ├── message_loc: (1,0)-(1,1) = "!"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/case_in.txt b/test/prism/snapshots/seattlerb/case_in.txt
new file mode 100644
index 0000000000..950d66647e
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in.txt
@@ -0,0 +1,976 @@
+@ ProgramNode (location: (1,0)-(111,3))
+├── locals: [:b, :_, :lhs, :x, :rhs, :c, :e]
+└── statements:
+ @ StatementsNode (location: (1,0)-(111,3))
+ └── body: (length: 28)
+ ├── @ CaseMatchNode (location: (1,0)-(3,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (1,5)-(1,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (2,0)-(2,8))
+ │ │ ├── pattern:
+ │ │ │ @ HashPatternNode (location: (2,4)-(2,8))
+ │ │ │ ├── constant: ∅
+ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ └── @ AssocNode (location: (2,4)-(2,8))
+ │ │ │ │ ├── key:
+ │ │ │ │ │ @ SymbolNode (location: (2,4)-(2,8))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: (2,4)-(2,5) = "\""
+ │ │ │ │ │ ├── value_loc: (2,5)-(2,6) = "b"
+ │ │ │ │ │ ├── closing_loc: (2,6)-(2,8) = "\":"
+ │ │ │ │ │ └── unescaped: "b"
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ ImplicitNode (location: (2,5)-(2,6))
+ │ │ │ │ │ └── value:
+ │ │ │ │ │ @ LocalVariableTargetNode (location: (2,5)-(2,6))
+ │ │ │ │ │ ├── name: :b
+ │ │ │ │ │ └── depth: 0
+ │ │ │ │ └── operator_loc: ∅
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ └── closing_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ │ └── end_keyword_loc: (3,0)-(3,3) = "end"
+ ├── @ CaseMatchNode (location: (5,0)-(7,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (5,5)-(5,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (5,5)-(5,6) = ":"
+ │ │ ├── value_loc: (5,6)-(5,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (6,0)-(6,10))
+ │ │ ├── pattern:
+ │ │ │ @ ArrayNode (location: (6,3)-(6,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 2)
+ │ │ │ │ ├── @ SymbolNode (location: (6,6)-(6,7))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── value_loc: (6,6)-(6,7) = "a"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "a"
+ │ │ │ │ └── @ SymbolNode (location: (6,8)-(6,9))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (6,8)-(6,9) = "b"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "b"
+ │ │ │ ├── opening_loc: (6,3)-(6,6) = "%I["
+ │ │ │ └── closing_loc: (6,9)-(6,10) = "]"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (6,0)-(6,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (5,0)-(5,4) = "case"
+ │ └── end_keyword_loc: (7,0)-(7,3) = "end"
+ ├── @ CaseMatchNode (location: (9,0)-(11,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (9,5)-(9,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (9,5)-(9,6) = ":"
+ │ │ ├── value_loc: (9,6)-(9,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (10,0)-(10,10))
+ │ │ ├── pattern:
+ │ │ │ @ ArrayNode (location: (10,3)-(10,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 2)
+ │ │ │ │ ├── @ StringNode (location: (10,6)-(10,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── content_loc: (10,6)-(10,7) = "a"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "a"
+ │ │ │ │ └── @ StringNode (location: (10,8)-(10,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (10,8)-(10,9) = "b"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "b"
+ │ │ │ ├── opening_loc: (10,3)-(10,6) = "%W["
+ │ │ │ └── closing_loc: (10,9)-(10,10) = "]"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (10,0)-(10,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (9,0)-(9,4) = "case"
+ │ └── end_keyword_loc: (11,0)-(11,3) = "end"
+ ├── @ CaseMatchNode (location: (13,0)-(15,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (13,5)-(13,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (13,5)-(13,6) = ":"
+ │ │ ├── value_loc: (13,6)-(13,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (14,0)-(14,10))
+ │ │ ├── pattern:
+ │ │ │ @ ArrayNode (location: (14,3)-(14,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 2)
+ │ │ │ │ ├── @ SymbolNode (location: (14,6)-(14,7))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── value_loc: (14,6)-(14,7) = "a"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "a"
+ │ │ │ │ └── @ SymbolNode (location: (14,8)-(14,9))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (14,8)-(14,9) = "b"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "b"
+ │ │ │ ├── opening_loc: (14,3)-(14,6) = "%i["
+ │ │ │ └── closing_loc: (14,9)-(14,10) = "]"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (14,0)-(14,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (13,0)-(13,4) = "case"
+ │ └── end_keyword_loc: (15,0)-(15,3) = "end"
+ ├── @ CaseMatchNode (location: (17,0)-(19,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (17,5)-(17,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (17,5)-(17,6) = ":"
+ │ │ ├── value_loc: (17,6)-(17,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (18,0)-(18,10))
+ │ │ ├── pattern:
+ │ │ │ @ ArrayNode (location: (18,3)-(18,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 2)
+ │ │ │ │ ├── @ StringNode (location: (18,6)-(18,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── content_loc: (18,6)-(18,7) = "a"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "a"
+ │ │ │ │ └── @ StringNode (location: (18,8)-(18,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (18,8)-(18,9) = "b"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "b"
+ │ │ │ ├── opening_loc: (18,3)-(18,6) = "%w["
+ │ │ │ └── closing_loc: (18,9)-(18,10) = "]"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (18,0)-(18,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (17,0)-(17,4) = "case"
+ │ └── end_keyword_loc: (19,0)-(19,3) = "end"
+ ├── @ CaseMatchNode (location: (21,0)-(23,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (21,5)-(21,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (21,5)-(21,6) = ":"
+ │ │ ├── value_loc: (21,6)-(21,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (22,0)-(22,10))
+ │ │ ├── pattern:
+ │ │ │ @ ParenthesesNode (location: (22,3)-(22,10))
+ │ │ │ ├── body:
+ │ │ │ │ @ RangeNode (location: (22,4)-(22,9))
+ │ │ │ │ ├── flags: exclude_end
+ │ │ │ │ ├── left: ∅
+ │ │ │ │ ├── right:
+ │ │ │ │ │ @ IntegerNode (location: (22,7)-(22,9))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 10
+ │ │ │ │ └── operator_loc: (22,4)-(22,7) = "..."
+ │ │ │ ├── opening_loc: (22,3)-(22,4) = "("
+ │ │ │ └── closing_loc: (22,9)-(22,10) = ")"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (22,0)-(22,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (21,0)-(21,4) = "case"
+ │ └── end_keyword_loc: (23,0)-(23,3) = "end"
+ ├── @ CaseMatchNode (location: (25,0)-(27,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (25,5)-(25,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (25,5)-(25,6) = ":"
+ │ │ ├── value_loc: (25,6)-(25,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (26,0)-(26,9))
+ │ │ ├── pattern:
+ │ │ │ @ ParenthesesNode (location: (26,3)-(26,9))
+ │ │ │ ├── body:
+ │ │ │ │ @ RangeNode (location: (26,4)-(26,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── left: ∅
+ │ │ │ │ ├── right:
+ │ │ │ │ │ @ IntegerNode (location: (26,6)-(26,8))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 10
+ │ │ │ │ └── operator_loc: (26,4)-(26,6) = ".."
+ │ │ │ ├── opening_loc: (26,3)-(26,4) = "("
+ │ │ │ └── closing_loc: (26,8)-(26,9) = ")"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (26,0)-(26,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (25,0)-(25,4) = "case"
+ │ └── end_keyword_loc: (27,0)-(27,3) = "end"
+ ├── @ CaseMatchNode (location: (29,0)-(31,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (29,5)-(29,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (29,5)-(29,6) = ":"
+ │ │ ├── value_loc: (29,6)-(29,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (30,0)-(30,9))
+ │ │ ├── pattern:
+ │ │ │ @ ParenthesesNode (location: (30,3)-(30,9))
+ │ │ │ ├── body:
+ │ │ │ │ @ RangeNode (location: (30,4)-(30,8))
+ │ │ │ │ ├── flags: exclude_end
+ │ │ │ │ ├── left:
+ │ │ │ │ │ @ IntegerNode (location: (30,4)-(30,5))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ ├── right: ∅
+ │ │ │ │ └── operator_loc: (30,5)-(30,8) = "..."
+ │ │ │ ├── opening_loc: (30,3)-(30,4) = "("
+ │ │ │ └── closing_loc: (30,8)-(30,9) = ")"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (30,0)-(30,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (29,0)-(29,4) = "case"
+ │ └── end_keyword_loc: (31,0)-(31,3) = "end"
+ ├── @ CaseMatchNode (location: (33,0)-(35,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (33,5)-(33,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (33,5)-(33,6) = ":"
+ │ │ ├── value_loc: (33,6)-(33,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (34,0)-(34,10))
+ │ │ ├── pattern:
+ │ │ │ @ ParenthesesNode (location: (34,3)-(34,10))
+ │ │ │ ├── body:
+ │ │ │ │ @ RangeNode (location: (34,4)-(34,9))
+ │ │ │ │ ├── flags: exclude_end
+ │ │ │ │ ├── left:
+ │ │ │ │ │ @ IntegerNode (location: (34,4)-(34,5))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ ├── right:
+ │ │ │ │ │ @ IntegerNode (location: (34,8)-(34,9))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 3
+ │ │ │ │ └── operator_loc: (34,5)-(34,8) = "..."
+ │ │ │ ├── opening_loc: (34,3)-(34,4) = "("
+ │ │ │ └── closing_loc: (34,9)-(34,10) = ")"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (34,0)-(34,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (33,0)-(33,4) = "case"
+ │ └── end_keyword_loc: (35,0)-(35,3) = "end"
+ ├── @ CaseMatchNode (location: (37,0)-(39,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (37,5)-(37,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (37,5)-(37,6) = ":"
+ │ │ ├── value_loc: (37,6)-(37,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (38,0)-(38,7))
+ │ │ ├── pattern:
+ │ │ │ @ ParenthesesNode (location: (38,3)-(38,7))
+ │ │ │ ├── body:
+ │ │ │ │ @ IntegerNode (location: (38,4)-(38,6))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 42
+ │ │ │ ├── opening_loc: (38,3)-(38,4) = "("
+ │ │ │ └── closing_loc: (38,6)-(38,7) = ")"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (38,0)-(38,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (37,0)-(37,4) = "case"
+ │ └── end_keyword_loc: (39,0)-(39,3) = "end"
+ ├── @ CaseMatchNode (location: (41,0)-(43,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (41,5)-(41,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (41,5)-(41,6) = ":"
+ │ │ ├── value_loc: (41,6)-(41,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (42,0)-(42,8))
+ │ │ ├── pattern:
+ │ │ │ @ HashPatternNode (location: (42,3)-(42,8))
+ │ │ │ ├── constant: ∅
+ │ │ │ ├── elements: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ NoKeywordsParameterNode (location: (42,3)-(42,8))
+ │ │ │ │ ├── operator_loc: (42,3)-(42,5) = "**"
+ │ │ │ │ └── keyword_loc: (42,5)-(42,8) = "nil"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ └── closing_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (42,0)-(42,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (41,0)-(41,4) = "case"
+ │ └── end_keyword_loc: (43,0)-(43,3) = "end"
+ ├── @ CaseMatchNode (location: (45,0)-(47,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (45,5)-(45,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (45,5)-(45,6) = ":"
+ │ │ ├── value_loc: (45,6)-(45,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (46,0)-(46,11))
+ │ │ ├── pattern:
+ │ │ │ @ RegularExpressionNode (location: (46,3)-(46,11))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (46,3)-(46,4) = "/"
+ │ │ │ ├── content_loc: (46,4)-(46,10) = "regexp"
+ │ │ │ ├── closing_loc: (46,10)-(46,11) = "/"
+ │ │ │ └── unescaped: "regexp"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (46,0)-(46,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (45,0)-(45,4) = "case"
+ │ └── end_keyword_loc: (47,0)-(47,3) = "end"
+ ├── @ CaseMatchNode (location: (49,0)-(51,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (49,5)-(49,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (49,5)-(49,6) = ":"
+ │ │ ├── value_loc: (49,6)-(49,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (50,0)-(50,13))
+ │ │ ├── pattern:
+ │ │ │ @ ArrayPatternNode (location: (50,3)-(50,13))
+ │ │ │ ├── constant: ∅
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ SymbolNode (location: (50,3)-(50,5))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (50,3)-(50,4) = ":"
+ │ │ │ │ ├── value_loc: (50,4)-(50,5) = "b"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "b"
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (50,7)-(50,9))
+ │ │ │ │ ├── operator_loc: (50,7)-(50,8) = "*"
+ │ │ │ │ └── expression:
+ │ │ │ │ @ LocalVariableTargetNode (location: (50,8)-(50,9))
+ │ │ │ │ ├── name: :_
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── posts: (length: 1)
+ │ │ │ │ └── @ SymbolNode (location: (50,11)-(50,13))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (50,11)-(50,12) = ":"
+ │ │ │ │ ├── value_loc: (50,12)-(50,13) = "c"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "c"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ └── closing_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (50,0)-(50,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (49,0)-(49,4) = "case"
+ │ └── end_keyword_loc: (51,0)-(51,3) = "end"
+ ├── @ CaseMatchNode (location: (53,0)-(55,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (53,5)-(53,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (53,5)-(53,6) = ":"
+ │ │ ├── value_loc: (53,6)-(53,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (54,0)-(54,11))
+ │ │ ├── pattern:
+ │ │ │ @ ArrayPatternNode (location: (54,3)-(54,11))
+ │ │ │ ├── constant: ∅
+ │ │ │ ├── requireds: (length: 2)
+ │ │ │ │ ├── @ SymbolNode (location: (54,3)-(54,5))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: (54,3)-(54,4) = ":"
+ │ │ │ │ │ ├── value_loc: (54,4)-(54,5) = "b"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "b"
+ │ │ │ │ └── @ ArrayPatternNode (location: (54,7)-(54,11))
+ │ │ │ │ ├── constant: ∅
+ │ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ │ └── @ SymbolNode (location: (54,8)-(54,10))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: (54,8)-(54,9) = ":"
+ │ │ │ │ │ ├── value_loc: (54,9)-(54,10) = "c"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "c"
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ ├── opening_loc: (54,7)-(54,8) = "["
+ │ │ │ │ └── closing_loc: (54,10)-(54,11) = "]"
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ └── closing_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (54,0)-(54,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (53,0)-(53,4) = "case"
+ │ └── end_keyword_loc: (55,0)-(55,3) = "end"
+ ├── @ CaseMatchNode (location: (57,0)-(59,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (57,5)-(57,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (57,5)-(57,6) = ":"
+ │ │ ├── value_loc: (57,6)-(57,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (58,0)-(58,11))
+ │ │ ├── pattern:
+ │ │ │ @ ArrayPatternNode (location: (58,3)-(58,11))
+ │ │ │ ├── constant:
+ │ │ │ │ @ ConstantReadNode (location: (58,3)-(58,9))
+ │ │ │ │ └── name: :Symbol
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── opening_loc: (58,9)-(58,10) = "("
+ │ │ │ └── closing_loc: (58,10)-(58,11) = ")"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (58,0)-(58,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (57,0)-(57,4) = "case"
+ │ └── end_keyword_loc: (59,0)-(59,3) = "end"
+ ├── @ CaseMatchNode (location: (61,0)-(63,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (61,5)-(61,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (61,5)-(61,6) = ":"
+ │ │ ├── value_loc: (61,6)-(61,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (62,0)-(62,24))
+ │ │ ├── pattern:
+ │ │ │ @ FindPatternNode (location: (62,3)-(62,24))
+ │ │ │ ├── constant:
+ │ │ │ │ @ ConstantReadNode (location: (62,3)-(62,9))
+ │ │ │ │ └── name: :Symbol
+ │ │ │ ├── left:
+ │ │ │ │ @ SplatNode (location: (62,10)-(62,14))
+ │ │ │ │ ├── operator_loc: (62,10)-(62,11) = "*"
+ │ │ │ │ └── expression:
+ │ │ │ │ @ LocalVariableTargetNode (location: (62,11)-(62,14))
+ │ │ │ │ ├── name: :lhs
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ LocalVariableTargetNode (location: (62,16)-(62,17))
+ │ │ │ │ ├── name: :x
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── right:
+ │ │ │ │ @ SplatNode (location: (62,19)-(62,23))
+ │ │ │ │ ├── operator_loc: (62,19)-(62,20) = "*"
+ │ │ │ │ └── expression:
+ │ │ │ │ @ LocalVariableTargetNode (location: (62,20)-(62,23))
+ │ │ │ │ ├── name: :rhs
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── opening_loc: (62,9)-(62,10) = "("
+ │ │ │ └── closing_loc: (62,23)-(62,24) = ")"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (62,0)-(62,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (61,0)-(61,4) = "case"
+ │ └── end_keyword_loc: (63,0)-(63,3) = "end"
+ ├── @ CaseMatchNode (location: (65,0)-(67,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (65,5)-(65,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (65,5)-(65,6) = ":"
+ │ │ ├── value_loc: (65,6)-(65,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (66,0)-(66,24))
+ │ │ ├── pattern:
+ │ │ │ @ FindPatternNode (location: (66,3)-(66,24))
+ │ │ │ ├── constant:
+ │ │ │ │ @ ConstantReadNode (location: (66,3)-(66,9))
+ │ │ │ │ └── name: :Symbol
+ │ │ │ ├── left:
+ │ │ │ │ @ SplatNode (location: (66,10)-(66,14))
+ │ │ │ │ ├── operator_loc: (66,10)-(66,11) = "*"
+ │ │ │ │ └── expression:
+ │ │ │ │ @ LocalVariableTargetNode (location: (66,11)-(66,14))
+ │ │ │ │ ├── name: :lhs
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ LocalVariableTargetNode (location: (66,16)-(66,17))
+ │ │ │ │ ├── name: :x
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── right:
+ │ │ │ │ @ SplatNode (location: (66,19)-(66,23))
+ │ │ │ │ ├── operator_loc: (66,19)-(66,20) = "*"
+ │ │ │ │ └── expression:
+ │ │ │ │ @ LocalVariableTargetNode (location: (66,20)-(66,23))
+ │ │ │ │ ├── name: :rhs
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── opening_loc: (66,9)-(66,10) = "["
+ │ │ │ └── closing_loc: (66,23)-(66,24) = "]"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (66,0)-(66,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (65,0)-(65,4) = "case"
+ │ └── end_keyword_loc: (67,0)-(67,3) = "end"
+ ├── @ CaseMatchNode (location: (69,0)-(71,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (69,5)-(69,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (69,5)-(69,6) = ":"
+ │ │ ├── value_loc: (69,6)-(69,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (70,0)-(70,22))
+ │ │ ├── pattern:
+ │ │ │ @ ArrayPatternNode (location: (70,3)-(70,22))
+ │ │ │ ├── constant: ∅
+ │ │ │ ├── requireds: (length: 2)
+ │ │ │ │ ├── @ LambdaNode (location: (70,4)-(70,18))
+ │ │ │ │ │ ├── locals: [:b]
+ │ │ │ │ │ ├── operator_loc: (70,4)-(70,6) = "->"
+ │ │ │ │ │ ├── opening_loc: (70,10)-(70,11) = "{"
+ │ │ │ │ │ ├── closing_loc: (70,17)-(70,18) = "}"
+ │ │ │ │ │ ├── parameters:
+ │ │ │ │ │ │ @ BlockParametersNode (location: (70,6)-(70,9))
+ │ │ │ │ │ │ ├── parameters:
+ │ │ │ │ │ │ │ @ ParametersNode (location: (70,7)-(70,8))
+ │ │ │ │ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ │ │ │ │ └── @ RequiredParameterNode (location: (70,7)-(70,8))
+ │ │ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ │ │ └── name: :b
+ │ │ │ │ │ │ │ ├── optionals: (length: 0)
+ │ │ │ │ │ │ │ ├── rest: ∅
+ │ │ │ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ │ │ │ ├── keywords: (length: 0)
+ │ │ │ │ │ │ │ ├── keyword_rest: ∅
+ │ │ │ │ │ │ │ └── block: ∅
+ │ │ │ │ │ │ ├── locals: (length: 0)
+ │ │ │ │ │ │ ├── opening_loc: (70,6)-(70,7) = "("
+ │ │ │ │ │ │ └── closing_loc: (70,8)-(70,9) = ")"
+ │ │ │ │ │ └── body:
+ │ │ │ │ │ @ StatementsNode (location: (70,12)-(70,16))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ TrueNode (location: (70,12)-(70,16))
+ │ │ │ │ └── @ LocalVariableTargetNode (location: (70,20)-(70,21))
+ │ │ │ │ ├── name: :c
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── opening_loc: (70,3)-(70,4) = "["
+ │ │ │ └── closing_loc: (70,21)-(70,22) = "]"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (70,0)-(70,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (69,0)-(69,4) = "case"
+ │ └── end_keyword_loc: (71,0)-(71,3) = "end"
+ ├── @ CaseMatchNode (location: (73,0)-(75,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (73,5)-(73,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (73,5)-(73,6) = ":"
+ │ │ ├── value_loc: (73,6)-(73,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (74,0)-(74,28))
+ │ │ ├── pattern:
+ │ │ │ @ ArrayPatternNode (location: (74,3)-(74,28))
+ │ │ │ ├── constant: ∅
+ │ │ │ ├── requireds: (length: 4)
+ │ │ │ │ ├── @ SymbolNode (location: (74,4)-(74,6))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: (74,4)-(74,5) = ":"
+ │ │ │ │ │ ├── value_loc: (74,5)-(74,6) = "a"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "a"
+ │ │ │ │ ├── @ LocalVariableTargetNode (location: (74,8)-(74,9))
+ │ │ │ │ │ ├── name: :b
+ │ │ │ │ │ └── depth: 0
+ │ │ │ │ ├── @ LocalVariableTargetNode (location: (74,11)-(74,12))
+ │ │ │ │ │ ├── name: :c
+ │ │ │ │ │ └── depth: 0
+ │ │ │ │ └── @ ArrayPatternNode (location: (74,14)-(74,27))
+ │ │ │ │ ├── constant: ∅
+ │ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ │ └── @ SymbolNode (location: (74,15)-(74,17))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: (74,15)-(74,16) = ":"
+ │ │ │ │ │ ├── value_loc: (74,16)-(74,17) = "d"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "d"
+ │ │ │ │ ├── rest:
+ │ │ │ │ │ @ SplatNode (location: (74,19)-(74,21))
+ │ │ │ │ │ ├── operator_loc: (74,19)-(74,20) = "*"
+ │ │ │ │ │ └── expression:
+ │ │ │ │ │ @ LocalVariableTargetNode (location: (74,20)-(74,21))
+ │ │ │ │ │ ├── name: :e
+ │ │ │ │ │ └── depth: 0
+ │ │ │ │ ├── posts: (length: 1)
+ │ │ │ │ │ └── @ NilNode (location: (74,23)-(74,26))
+ │ │ │ │ ├── opening_loc: (74,14)-(74,15) = "["
+ │ │ │ │ └── closing_loc: (74,26)-(74,27) = "]"
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── opening_loc: (74,3)-(74,4) = "["
+ │ │ │ └── closing_loc: (74,27)-(74,28) = "]"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (74,0)-(74,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (73,0)-(73,4) = "case"
+ │ └── end_keyword_loc: (75,0)-(75,3) = "end"
+ ├── @ CaseMatchNode (location: (77,0)-(79,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (77,5)-(77,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (77,5)-(77,6) = ":"
+ │ │ ├── value_loc: (77,6)-(77,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (78,0)-(78,12))
+ │ │ ├── pattern:
+ │ │ │ @ ArrayPatternNode (location: (78,3)-(78,12))
+ │ │ │ ├── constant: ∅
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ ConstantReadNode (location: (78,4)-(78,5))
+ │ │ │ │ └── name: :A
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (78,7)-(78,8))
+ │ │ │ │ ├── operator_loc: (78,7)-(78,8) = "*"
+ │ │ │ │ └── expression: ∅
+ │ │ │ ├── posts: (length: 1)
+ │ │ │ │ └── @ ConstantReadNode (location: (78,10)-(78,11))
+ │ │ │ │ └── name: :B
+ │ │ │ ├── opening_loc: (78,3)-(78,4) = "["
+ │ │ │ └── closing_loc: (78,11)-(78,12) = "]"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (78,0)-(78,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (77,0)-(77,4) = "case"
+ │ └── end_keyword_loc: (79,0)-(79,3) = "end"
+ ├── @ CaseMatchNode (location: (81,0)-(83,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (81,5)-(81,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (81,5)-(81,6) = ":"
+ │ │ ├── value_loc: (81,6)-(81,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (82,0)-(82,22))
+ │ │ ├── pattern:
+ │ │ │ @ ArrayPatternNode (location: (82,3)-(82,22))
+ │ │ │ ├── constant: ∅
+ │ │ │ ├── requireds: (length: 2)
+ │ │ │ │ ├── @ ArrayPatternNode (location: (82,4)-(82,11))
+ │ │ │ │ │ ├── constant: ∅
+ │ │ │ │ │ ├── requireds: (length: 2)
+ │ │ │ │ │ │ ├── @ SymbolNode (location: (82,5)-(82,7))
+ │ │ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ │ │ ├── opening_loc: (82,5)-(82,6) = ":"
+ │ │ │ │ │ │ │ ├── value_loc: (82,6)-(82,7) = "b"
+ │ │ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ │ │ └── unescaped: "b"
+ │ │ │ │ │ │ └── @ LocalVariableTargetNode (location: (82,9)-(82,10))
+ │ │ │ │ │ │ ├── name: :c
+ │ │ │ │ │ │ └── depth: 0
+ │ │ │ │ │ ├── rest: ∅
+ │ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ │ ├── opening_loc: (82,4)-(82,5) = "["
+ │ │ │ │ │ └── closing_loc: (82,10)-(82,11) = "]"
+ │ │ │ │ └── @ ArrayPatternNode (location: (82,13)-(82,21))
+ │ │ │ │ ├── constant: ∅
+ │ │ │ │ ├── requireds: (length: 2)
+ │ │ │ │ │ ├── @ SymbolNode (location: (82,14)-(82,16))
+ │ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ │ ├── opening_loc: (82,14)-(82,15) = ":"
+ │ │ │ │ │ │ ├── value_loc: (82,15)-(82,16) = "d"
+ │ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ │ └── unescaped: "d"
+ │ │ │ │ │ └── @ PinnedVariableNode (location: (82,18)-(82,20))
+ │ │ │ │ │ ├── variable:
+ │ │ │ │ │ │ @ LocalVariableReadNode (location: (82,19)-(82,20))
+ │ │ │ │ │ │ ├── name: :e
+ │ │ │ │ │ │ └── depth: 0
+ │ │ │ │ │ └── operator_loc: (82,18)-(82,19) = "^"
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ ├── opening_loc: (82,13)-(82,14) = "["
+ │ │ │ │ └── closing_loc: (82,20)-(82,21) = "]"
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── opening_loc: (82,3)-(82,4) = "["
+ │ │ │ └── closing_loc: (82,21)-(82,22) = "]"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (82,0)-(82,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (81,0)-(81,4) = "case"
+ │ └── end_keyword_loc: (83,0)-(83,3) = "end"
+ ├── @ CaseMatchNode (location: (85,0)-(87,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (85,5)-(85,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (85,5)-(85,6) = ":"
+ │ │ ├── value_loc: (85,6)-(85,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (86,0)-(86,5))
+ │ │ ├── pattern:
+ │ │ │ @ ArrayPatternNode (location: (86,3)-(86,5))
+ │ │ │ ├── constant: ∅
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── opening_loc: (86,3)-(86,4) = "["
+ │ │ │ └── closing_loc: (86,4)-(86,5) = "]"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (86,0)-(86,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (85,0)-(85,4) = "case"
+ │ └── end_keyword_loc: (87,0)-(87,3) = "end"
+ ├── @ CaseMatchNode (location: (89,0)-(91,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (89,5)-(89,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (89,5)-(89,6) = ":"
+ │ │ ├── value_loc: (89,6)-(89,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (90,0)-(90,9))
+ │ │ ├── pattern:
+ │ │ │ @ ArrayPatternNode (location: (90,3)-(90,9))
+ │ │ │ ├── constant: ∅
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ PinnedExpressionNode (location: (90,4)-(90,8))
+ │ │ │ │ ├── expression:
+ │ │ │ │ │ @ CallNode (location: (90,6)-(90,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :a
+ │ │ │ │ │ ├── message_loc: (90,6)-(90,7) = "a"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── operator_loc: (90,4)-(90,5) = "^"
+ │ │ │ │ ├── lparen_loc: (90,5)-(90,6) = "("
+ │ │ │ │ └── rparen_loc: (90,7)-(90,8) = ")"
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── opening_loc: (90,3)-(90,4) = "["
+ │ │ │ └── closing_loc: (90,8)-(90,9) = "]"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (90,0)-(90,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (89,0)-(89,4) = "case"
+ │ └── end_keyword_loc: (91,0)-(91,3) = "end"
+ ├── @ CaseMatchNode (location: (93,0)-(95,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (93,5)-(93,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (93,5)-(93,6) = ":"
+ │ │ ├── value_loc: (93,6)-(93,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (94,0)-(94,19))
+ │ │ ├── pattern:
+ │ │ │ @ ArrayPatternNode (location: (94,3)-(94,19))
+ │ │ │ ├── constant: ∅
+ │ │ │ ├── requireds: (length: 3)
+ │ │ │ │ ├── @ PinnedVariableNode (location: (94,4)-(94,7))
+ │ │ │ │ │ ├── variable:
+ │ │ │ │ │ │ @ InstanceVariableReadNode (location: (94,5)-(94,7))
+ │ │ │ │ │ │ └── name: :@a
+ │ │ │ │ │ └── operator_loc: (94,4)-(94,5) = "^"
+ │ │ │ │ ├── @ PinnedVariableNode (location: (94,9)-(94,12))
+ │ │ │ │ │ ├── variable:
+ │ │ │ │ │ │ @ GlobalVariableReadNode (location: (94,10)-(94,12))
+ │ │ │ │ │ │ └── name: :$b
+ │ │ │ │ │ └── operator_loc: (94,9)-(94,10) = "^"
+ │ │ │ │ └── @ PinnedVariableNode (location: (94,14)-(94,18))
+ │ │ │ │ ├── variable:
+ │ │ │ │ │ @ ClassVariableReadNode (location: (94,15)-(94,18))
+ │ │ │ │ │ └── name: :@@c
+ │ │ │ │ └── operator_loc: (94,14)-(94,15) = "^"
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── opening_loc: (94,3)-(94,4) = "["
+ │ │ │ └── closing_loc: (94,18)-(94,19) = "]"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (94,0)-(94,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (93,0)-(93,4) = "case"
+ │ └── end_keyword_loc: (95,0)-(95,3) = "end"
+ ├── @ CaseMatchNode (location: (97,0)-(99,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (97,5)-(97,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (97,5)-(97,6) = ":"
+ │ │ ├── value_loc: (97,6)-(97,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (98,0)-(98,12))
+ │ │ ├── pattern:
+ │ │ │ @ XStringNode (location: (98,3)-(98,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (98,3)-(98,4) = "`"
+ │ │ │ ├── content_loc: (98,4)-(98,11) = "echo hi"
+ │ │ │ ├── closing_loc: (98,11)-(98,12) = "`"
+ │ │ │ └── unescaped: "echo hi"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (98,0)-(98,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (97,0)-(97,4) = "case"
+ │ └── end_keyword_loc: (99,0)-(99,3) = "end"
+ ├── @ CaseMatchNode (location: (101,0)-(103,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (101,5)-(101,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (101,5)-(101,6) = ":"
+ │ │ ├── value_loc: (101,6)-(101,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (102,0)-(102,16))
+ │ │ ├── pattern:
+ │ │ │ @ ArrayPatternNode (location: (102,3)-(102,16))
+ │ │ │ ├── constant: ∅
+ │ │ │ ├── requireds: (length: 3)
+ │ │ │ │ ├── @ NilNode (location: (102,3)-(102,6))
+ │ │ │ │ ├── @ NilNode (location: (102,8)-(102,11))
+ │ │ │ │ └── @ NilNode (location: (102,13)-(102,16))
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ └── closing_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (102,0)-(102,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (101,0)-(101,4) = "case"
+ │ └── end_keyword_loc: (103,0)-(103,3) = "end"
+ ├── @ CaseMatchNode (location: (105,0)-(107,3))
+ │ ├── predicate:
+ │ │ @ SymbolNode (location: (105,5)-(105,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (105,5)-(105,6) = ":"
+ │ │ ├── value_loc: (105,6)-(105,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (106,0)-(106,11))
+ │ │ ├── pattern:
+ │ │ │ @ HashPatternNode (location: (106,3)-(106,11))
+ │ │ │ ├── constant: ∅
+ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ └── @ AssocNode (location: (106,5)-(106,9))
+ │ │ │ │ ├── key:
+ │ │ │ │ │ @ SymbolNode (location: (106,5)-(106,9))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: (106,5)-(106,6) = "\""
+ │ │ │ │ │ ├── value_loc: (106,6)-(106,7) = "b"
+ │ │ │ │ │ ├── closing_loc: (106,7)-(106,9) = "\":"
+ │ │ │ │ │ └── unescaped: "b"
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ ImplicitNode (location: (106,6)-(106,7))
+ │ │ │ │ │ └── value:
+ │ │ │ │ │ @ LocalVariableTargetNode (location: (106,6)-(106,7))
+ │ │ │ │ │ ├── name: :b
+ │ │ │ │ │ └── depth: 0
+ │ │ │ │ └── operator_loc: ∅
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── opening_loc: (106,3)-(106,4) = "{"
+ │ │ │ └── closing_loc: (106,10)-(106,11) = "}"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (106,0)-(106,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (105,0)-(105,4) = "case"
+ │ └── end_keyword_loc: (107,0)-(107,3) = "end"
+ └── @ CaseMatchNode (location: (109,0)-(111,3))
+ ├── predicate:
+ │ @ SymbolNode (location: (109,5)-(109,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (109,5)-(109,6) = ":"
+ │ ├── value_loc: (109,6)-(109,7) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (110,0)-(110,5))
+ │ ├── pattern:
+ │ │ @ HashPatternNode (location: (110,3)-(110,5))
+ │ │ ├── constant: ∅
+ │ │ ├── elements: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── opening_loc: (110,3)-(110,4) = "{"
+ │ │ └── closing_loc: (110,4)-(110,5) = "}"
+ │ ├── statements: ∅
+ │ ├── in_loc: (110,0)-(110,2) = "in"
+ │ └── then_loc: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (109,0)-(109,4) = "case"
+ └── end_keyword_loc: (111,0)-(111,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/case_in_31.txt b/test/prism/snapshots/seattlerb/case_in_31.txt
new file mode 100644
index 0000000000..fdf5ce2a29
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in_31.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: [:c]
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(4,3))
+ ├── predicate:
+ │ @ SymbolNode (location: (1,5)-(1,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(3,4))
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (2,3)-(2,11))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (2,4)-(2,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (2,4)-(2,5) = ":"
+ │ │ │ ├── value_loc: (2,5)-(2,6) = "b"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "b"
+ │ │ ├── rest:
+ │ │ │ @ SplatNode (location: (2,8)-(2,10))
+ │ │ │ ├── operator_loc: (2,8)-(2,9) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (2,9)-(2,10))
+ │ │ │ ├── name: :c
+ │ │ │ └── depth: 0
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (2,3)-(2,4) = "["
+ │ │ └── closing_loc: (2,10)-(2,11) = "]"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,2)-(3,4))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SymbolNode (location: (3,2)-(3,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (3,2)-(3,3) = ":"
+ │ │ ├── value_loc: (3,3)-(3,4) = "d"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "d"
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (4,0)-(4,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/case_in_37.txt b/test/prism/snapshots/seattlerb/case_in_37.txt
new file mode 100644
index 0000000000..1a1d887b4f
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in_37.txt
@@ -0,0 +1,58 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(4,3))
+ ├── predicate:
+ │ @ SymbolNode (location: (1,5)-(1,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(3,4))
+ │ ├── pattern:
+ │ │ @ HashPatternNode (location: (2,3)-(2,19))
+ │ │ ├── constant: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (2,5)-(2,17))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (2,5)-(2,7))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (2,5)-(2,6) = "b"
+ │ │ │ │ ├── closing_loc: (2,6)-(2,7) = ":"
+ │ │ │ │ └── unescaped: "b"
+ │ │ │ ├── value:
+ │ │ │ │ @ ArrayPatternNode (location: (2,8)-(2,17))
+ │ │ │ │ ├── constant: ∅
+ │ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ │ └── @ ConstantReadNode (location: (2,9)-(2,13))
+ │ │ │ │ │ └── name: :Hash
+ │ │ │ │ ├── rest:
+ │ │ │ │ │ @ SplatNode (location: (2,15)-(2,16))
+ │ │ │ │ │ ├── operator_loc: (2,15)-(2,16) = "*"
+ │ │ │ │ │ └── expression: ∅
+ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ ├── opening_loc: (2,8)-(2,9) = "["
+ │ │ │ │ └── closing_loc: (2,16)-(2,17) = "]"
+ │ │ │ └── operator_loc: ∅
+ │ │ ├── rest: ∅
+ │ │ ├── opening_loc: (2,3)-(2,4) = "{"
+ │ │ └── closing_loc: (2,18)-(2,19) = "}"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,2)-(3,4))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SymbolNode (location: (3,2)-(3,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (3,2)-(3,3) = ":"
+ │ │ ├── value_loc: (3,3)-(3,4) = "c"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "c"
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (4,0)-(4,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/case_in_42.txt b/test/prism/snapshots/seattlerb/case_in_42.txt
new file mode 100644
index 0000000000..f985d6bc8d
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in_42.txt
@@ -0,0 +1,44 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: [:_]
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(3,3))
+ ├── predicate:
+ │ @ SymbolNode (location: (1,5)-(1,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(2,18))
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (2,3)-(2,9))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (2,3)-(2,5))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (2,3)-(2,4) = ":"
+ │ │ │ ├── value_loc: (2,4)-(2,5) = "b"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "b"
+ │ │ ├── rest:
+ │ │ │ @ SplatNode (location: (2,7)-(2,9))
+ │ │ │ ├── operator_loc: (2,7)-(2,8) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (2,8)-(2,9))
+ │ │ │ ├── name: :_
+ │ │ │ └── depth: 0
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (2,15)-(2,18))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NilNode (location: (2,15)-(2,18))
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: (2,10)-(2,14) = "then"
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (3,0)-(3,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/case_in_42_2.txt b/test/prism/snapshots/seattlerb/case_in_42_2.txt
new file mode 100644
index 0000000000..c399ba1bfa
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in_42_2.txt
@@ -0,0 +1,40 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: [:list]
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(3,3))
+ ├── predicate:
+ │ @ SymbolNode (location: (1,5)-(1,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(2,20))
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (2,3)-(2,11))
+ │ │ ├── constant:
+ │ │ │ @ ConstantReadNode (location: (2,3)-(2,4))
+ │ │ │ └── name: :A
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ SplatNode (location: (2,5)-(2,10))
+ │ │ │ ├── operator_loc: (2,5)-(2,6) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (2,6)-(2,10))
+ │ │ │ ├── name: :list
+ │ │ │ └── depth: 0
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (2,4)-(2,5) = "("
+ │ │ └── closing_loc: (2,10)-(2,11) = ")"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (2,17)-(2,20))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NilNode (location: (2,17)-(2,20))
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: (2,12)-(2,16) = "then"
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (3,0)-(3,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/case_in_47.txt b/test/prism/snapshots/seattlerb/case_in_47.txt
new file mode 100644
index 0000000000..99baebce05
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in_47.txt
@@ -0,0 +1,52 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(4,3))
+ ├── predicate:
+ │ @ SymbolNode (location: (1,5)-(1,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(3,4))
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (2,3)-(2,14))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ SplatNode (location: (2,4)-(2,5))
+ │ │ │ ├── operator_loc: (2,4)-(2,5) = "*"
+ │ │ │ └── expression: ∅
+ │ │ ├── posts: (length: 2)
+ │ │ │ ├── @ SymbolNode (location: (2,7)-(2,9))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (2,7)-(2,8) = ":"
+ │ │ │ │ ├── value_loc: (2,8)-(2,9) = "b"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "b"
+ │ │ │ └── @ SymbolNode (location: (2,11)-(2,13))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (2,11)-(2,12) = ":"
+ │ │ │ ├── value_loc: (2,12)-(2,13) = "c"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "c"
+ │ │ ├── opening_loc: (2,3)-(2,4) = "["
+ │ │ └── closing_loc: (2,13)-(2,14) = "]"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,2)-(3,4))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SymbolNode (location: (3,2)-(3,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (3,2)-(3,3) = ":"
+ │ │ ├── value_loc: (3,3)-(3,4) = "d"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "d"
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (4,0)-(4,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/case_in_67.txt b/test/prism/snapshots/seattlerb/case_in_67.txt
new file mode 100644
index 0000000000..4bab417d57
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in_67.txt
@@ -0,0 +1,33 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(3,3))
+ ├── predicate:
+ │ @ SymbolNode (location: (1,5)-(1,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(2,15))
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (2,3)-(2,6))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (2,3)-(2,4))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right: ∅
+ │ │ └── operator_loc: (2,4)-(2,6) = ".."
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (2,12)-(2,15))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NilNode (location: (2,12)-(2,15))
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: (2,7)-(2,11) = "then"
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (3,0)-(3,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/case_in_86.txt b/test/prism/snapshots/seattlerb/case_in_86.txt
new file mode 100644
index 0000000000..5889137844
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in_86.txt
@@ -0,0 +1,52 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(3,3))
+ ├── predicate:
+ │ @ ArrayNode (location: (1,5)-(1,13))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ SymbolNode (location: (1,6)-(1,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (1,6)-(1,7) = ":"
+ │ │ │ ├── value_loc: (1,7)-(1,8) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ └── @ SymbolNode (location: (1,10)-(1,12))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,10)-(1,11) = ":"
+ │ │ ├── value_loc: (1,11)-(1,12) = "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b"
+ │ ├── opening_loc: (1,5)-(1,6) = "["
+ │ └── closing_loc: (1,12)-(1,13) = "]"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(2,25))
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (2,3)-(2,16))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ ConstantPathNode (location: (2,3)-(2,13))
+ │ │ │ ├── parent: ∅
+ │ │ │ ├── child:
+ │ │ │ │ @ ConstantReadNode (location: (2,5)-(2,13))
+ │ │ │ │ └── name: :NilClass
+ │ │ │ └── delimiter_loc: (2,3)-(2,5) = "::"
+ │ │ ├── rest:
+ │ │ │ @ SplatNode (location: (2,15)-(2,16))
+ │ │ │ ├── operator_loc: (2,15)-(2,16) = "*"
+ │ │ │ └── expression: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (2,22)-(2,25))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NilNode (location: (2,22)-(2,25))
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: (2,17)-(2,21) = "then"
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (3,0)-(3,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/case_in_86_2.txt b/test/prism/snapshots/seattlerb/case_in_86_2.txt
new file mode 100644
index 0000000000..18ce70ae93
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in_86_2.txt
@@ -0,0 +1,52 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(3,3))
+ ├── predicate:
+ │ @ ArrayNode (location: (1,5)-(1,13))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ SymbolNode (location: (1,6)-(1,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (1,6)-(1,7) = ":"
+ │ │ │ ├── value_loc: (1,7)-(1,8) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ └── @ SymbolNode (location: (1,10)-(1,12))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,10)-(1,11) = ":"
+ │ │ ├── value_loc: (1,11)-(1,12) = "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b"
+ │ ├── opening_loc: (1,5)-(1,6) = "["
+ │ └── closing_loc: (1,12)-(1,13) = "]"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(2,25))
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (2,3)-(2,16))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ SplatNode (location: (2,3)-(2,4))
+ │ │ │ ├── operator_loc: (2,3)-(2,4) = "*"
+ │ │ │ └── expression: ∅
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ ConstantPathNode (location: (2,6)-(2,16))
+ │ │ │ ├── parent: ∅
+ │ │ │ ├── child:
+ │ │ │ │ @ ConstantReadNode (location: (2,8)-(2,16))
+ │ │ │ │ └── name: :NilClass
+ │ │ │ └── delimiter_loc: (2,6)-(2,8) = "::"
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (2,22)-(2,25))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NilNode (location: (2,22)-(2,25))
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: (2,17)-(2,21) = "then"
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (3,0)-(3,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/case_in_array_pat_const.txt b/test/prism/snapshots/seattlerb/case_in_array_pat_const.txt
new file mode 100644
index 0000000000..f361e8d458
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in_array_pat_const.txt
@@ -0,0 +1,42 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: [:c]
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(4,3))
+ ├── predicate:
+ │ @ SymbolNode (location: (1,5)-(1,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(3,4))
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (2,3)-(2,7))
+ │ │ ├── constant:
+ │ │ │ @ ConstantReadNode (location: (2,3)-(2,4))
+ │ │ │ └── name: :B
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (2,5)-(2,6))
+ │ │ │ ├── name: :c
+ │ │ │ └── depth: 0
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (2,4)-(2,5) = "["
+ │ │ └── closing_loc: (2,6)-(2,7) = "]"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,2)-(3,4))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SymbolNode (location: (3,2)-(3,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (3,2)-(3,3) = ":"
+ │ │ ├── value_loc: (3,3)-(3,4) = "d"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "d"
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (4,0)-(4,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/case_in_array_pat_const2.txt b/test/prism/snapshots/seattlerb/case_in_array_pat_const2.txt
new file mode 100644
index 0000000000..c783af9ce5
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in_array_pat_const2.txt
@@ -0,0 +1,48 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: [:d]
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(4,3))
+ ├── predicate:
+ │ @ SymbolNode (location: (1,5)-(1,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(3,4))
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (2,3)-(2,10))
+ │ │ ├── constant:
+ │ │ │ @ ConstantPathNode (location: (2,3)-(2,7))
+ │ │ │ ├── parent:
+ │ │ │ │ @ ConstantReadNode (location: (2,3)-(2,4))
+ │ │ │ │ └── name: :B
+ │ │ │ ├── child:
+ │ │ │ │ @ ConstantReadNode (location: (2,6)-(2,7))
+ │ │ │ │ └── name: :C
+ │ │ │ └── delimiter_loc: (2,4)-(2,6) = "::"
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (2,8)-(2,9))
+ │ │ │ ├── name: :d
+ │ │ │ └── depth: 0
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (2,7)-(2,8) = "["
+ │ │ └── closing_loc: (2,9)-(2,10) = "]"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,2)-(3,4))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SymbolNode (location: (3,2)-(3,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (3,2)-(3,3) = ":"
+ │ │ ├── value_loc: (3,3)-(3,4) = "e"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "e"
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (4,0)-(4,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/case_in_array_pat_paren_assign.txt b/test/prism/snapshots/seattlerb/case_in_array_pat_paren_assign.txt
new file mode 100644
index 0000000000..8d185b250a
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in_array_pat_paren_assign.txt
@@ -0,0 +1,48 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: [:d]
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(4,3))
+ ├── predicate:
+ │ @ SymbolNode (location: (1,5)-(1,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(3,4))
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (2,3)-(2,12))
+ │ │ ├── constant:
+ │ │ │ @ ConstantReadNode (location: (2,3)-(2,4))
+ │ │ │ └── name: :B
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ CapturePatternNode (location: (2,5)-(2,11))
+ │ │ │ ├── value:
+ │ │ │ │ @ ConstantReadNode (location: (2,5)-(2,6))
+ │ │ │ │ └── name: :C
+ │ │ │ ├── target:
+ │ │ │ │ @ LocalVariableTargetNode (location: (2,10)-(2,11))
+ │ │ │ │ ├── name: :d
+ │ │ │ │ └── depth: 0
+ │ │ │ └── operator_loc: (2,7)-(2,9) = "=>"
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (2,4)-(2,5) = "("
+ │ │ └── closing_loc: (2,11)-(2,12) = ")"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,2)-(3,4))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SymbolNode (location: (3,2)-(3,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (3,2)-(3,3) = ":"
+ │ │ ├── value_loc: (3,3)-(3,4) = "d"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "d"
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (4,0)-(4,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/case_in_const.txt b/test/prism/snapshots/seattlerb/case_in_const.txt
new file mode 100644
index 0000000000..c4b838aa1d
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in_const.txt
@@ -0,0 +1,28 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(4,3))
+ ├── predicate:
+ │ @ ConstantReadNode (location: (1,5)-(1,10))
+ │ └── name: :Array
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(3,4))
+ │ ├── pattern:
+ │ │ @ ConstantReadNode (location: (2,3)-(2,8))
+ │ │ └── name: :Class
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,2)-(3,4))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SymbolNode (location: (3,2)-(3,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (3,2)-(3,3) = ":"
+ │ │ ├── value_loc: (3,3)-(3,4) = "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b"
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (4,0)-(4,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/case_in_else.txt b/test/prism/snapshots/seattlerb/case_in_else.txt
new file mode 100644
index 0000000000..5eae7fc1ea
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in_else.txt
@@ -0,0 +1,40 @@
+@ ProgramNode (location: (1,0)-(6,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(6,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(6,3))
+ ├── predicate:
+ │ @ ConstantReadNode (location: (1,5)-(1,10))
+ │ └── name: :Array
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(3,4))
+ │ ├── pattern:
+ │ │ @ ConstantReadNode (location: (2,3)-(2,8))
+ │ │ └── name: :Class
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,2)-(3,4))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SymbolNode (location: (3,2)-(3,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (3,2)-(3,3) = ":"
+ │ │ ├── value_loc: (3,3)-(3,4) = "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b"
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: ∅
+ ├── consequent:
+ │ @ ElseNode (location: (4,0)-(6,3))
+ │ ├── else_keyword_loc: (4,0)-(4,4) = "else"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (5,2)-(5,4))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SymbolNode (location: (5,2)-(5,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (5,2)-(5,3) = ":"
+ │ │ ├── value_loc: (5,3)-(5,4) = "c"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "c"
+ │ └── end_keyword_loc: (6,0)-(6,3) = "end"
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (6,0)-(6,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/case_in_find.txt b/test/prism/snapshots/seattlerb/case_in_find.txt
new file mode 100644
index 0000000000..f84c4c30d0
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in_find.txt
@@ -0,0 +1,47 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: [:a, :b]
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(3,3))
+ ├── predicate:
+ │ @ SymbolNode (location: (1,5)-(1,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,2)-(2,15))
+ │ ├── pattern:
+ │ │ @ FindPatternNode (location: (2,5)-(2,15))
+ │ │ ├── constant: ∅
+ │ │ ├── left:
+ │ │ │ @ SplatNode (location: (2,5)-(2,7))
+ │ │ │ ├── operator_loc: (2,5)-(2,6) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (2,6)-(2,7))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (2,9)-(2,11))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (2,9)-(2,10) = ":"
+ │ │ │ ├── value_loc: (2,10)-(2,11) = "+"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "+"
+ │ │ ├── right:
+ │ │ │ @ SplatNode (location: (2,13)-(2,15))
+ │ │ │ ├── operator_loc: (2,13)-(2,14) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (2,14)-(2,15))
+ │ │ │ ├── name: :b
+ │ │ │ └── depth: 0
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ ├── statements: ∅
+ │ ├── in_loc: (2,2)-(2,4) = "in"
+ │ └── then_loc: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (3,0)-(3,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/case_in_find_array.txt b/test/prism/snapshots/seattlerb/case_in_find_array.txt
new file mode 100644
index 0000000000..a757f80346
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in_find_array.txt
@@ -0,0 +1,44 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: [:c]
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(3,3))
+ ├── predicate:
+ │ @ SymbolNode (location: (1,5)-(1,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(2,16))
+ │ ├── pattern:
+ │ │ @ FindPatternNode (location: (2,3)-(2,16))
+ │ │ ├── constant: ∅
+ │ │ ├── left:
+ │ │ │ @ SplatNode (location: (2,4)-(2,5))
+ │ │ │ ├── operator_loc: (2,4)-(2,5) = "*"
+ │ │ │ └── expression: ∅
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ SymbolNode (location: (2,7)-(2,9))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (2,7)-(2,8) = ":"
+ │ │ │ │ ├── value_loc: (2,8)-(2,9) = "b"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "b"
+ │ │ │ └── @ LocalVariableTargetNode (location: (2,11)-(2,12))
+ │ │ │ ├── name: :c
+ │ │ │ └── depth: 0
+ │ │ ├── right:
+ │ │ │ @ SplatNode (location: (2,14)-(2,15))
+ │ │ │ ├── operator_loc: (2,14)-(2,15) = "*"
+ │ │ │ └── expression: ∅
+ │ │ ├── opening_loc: (2,3)-(2,4) = "["
+ │ │ └── closing_loc: (2,15)-(2,16) = "]"
+ │ ├── statements: ∅
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (3,0)-(3,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/case_in_hash_pat.txt b/test/prism/snapshots/seattlerb/case_in_hash_pat.txt
new file mode 100644
index 0000000000..e813efa9ee
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in_hash_pat.txt
@@ -0,0 +1,68 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(4,3))
+ ├── predicate:
+ │ @ SymbolNode (location: (1,5)-(1,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(3,4))
+ │ ├── pattern:
+ │ │ @ HashPatternNode (location: (2,3)-(2,21))
+ │ │ ├── constant: ∅
+ │ │ ├── elements: (length: 2)
+ │ │ │ ├── @ AssocNode (location: (2,5)-(2,11))
+ │ │ │ │ ├── key:
+ │ │ │ │ │ @ SymbolNode (location: (2,5)-(2,7))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── value_loc: (2,5)-(2,6) = "b"
+ │ │ │ │ │ ├── closing_loc: (2,6)-(2,7) = ":"
+ │ │ │ │ │ └── unescaped: "b"
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ StringNode (location: (2,8)-(2,11))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── opening_loc: (2,8)-(2,9) = "'"
+ │ │ │ │ │ ├── content_loc: (2,9)-(2,10) = "c"
+ │ │ │ │ │ ├── closing_loc: (2,10)-(2,11) = "'"
+ │ │ │ │ │ └── unescaped: "c"
+ │ │ │ │ └── operator_loc: ∅
+ │ │ │ └── @ AssocNode (location: (2,13)-(2,19))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (2,13)-(2,15))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (2,13)-(2,14) = "d"
+ │ │ │ │ ├── closing_loc: (2,14)-(2,15) = ":"
+ │ │ │ │ └── unescaped: "d"
+ │ │ │ ├── value:
+ │ │ │ │ @ StringNode (location: (2,16)-(2,19))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: (2,16)-(2,17) = "\""
+ │ │ │ │ ├── content_loc: (2,17)-(2,18) = "e"
+ │ │ │ │ ├── closing_loc: (2,18)-(2,19) = "\""
+ │ │ │ │ └── unescaped: "e"
+ │ │ │ └── operator_loc: ∅
+ │ │ ├── rest: ∅
+ │ │ ├── opening_loc: (2,3)-(2,4) = "{"
+ │ │ └── closing_loc: (2,20)-(2,21) = "}"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,2)-(3,4))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SymbolNode (location: (3,2)-(3,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (3,2)-(3,3) = ":"
+ │ │ ├── value_loc: (3,3)-(3,4) = "f"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "f"
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: (2,22)-(2,26) = "then"
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (4,0)-(4,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/case_in_hash_pat_assign.txt b/test/prism/snapshots/seattlerb/case_in_hash_pat_assign.txt
new file mode 100644
index 0000000000..790d9d63ff
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in_hash_pat_assign.txt
@@ -0,0 +1,86 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: [:x, :f]
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(4,3))
+ ├── predicate:
+ │ @ SymbolNode (location: (1,5)-(1,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(3,4))
+ │ ├── pattern:
+ │ │ @ HashPatternNode (location: (2,3)-(2,34))
+ │ │ ├── constant: ∅
+ │ │ ├── elements: (length: 3)
+ │ │ │ ├── @ AssocNode (location: (2,5)-(2,20))
+ │ │ │ │ ├── key:
+ │ │ │ │ │ @ SymbolNode (location: (2,5)-(2,7))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── value_loc: (2,5)-(2,6) = "b"
+ │ │ │ │ │ ├── closing_loc: (2,6)-(2,7) = ":"
+ │ │ │ │ │ └── unescaped: "b"
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ CapturePatternNode (location: (2,8)-(2,20))
+ │ │ │ │ │ ├── value:
+ │ │ │ │ │ │ @ ConstantReadNode (location: (2,8)-(2,15))
+ │ │ │ │ │ │ └── name: :Integer
+ │ │ │ │ │ ├── target:
+ │ │ │ │ │ │ @ LocalVariableTargetNode (location: (2,19)-(2,20))
+ │ │ │ │ │ │ ├── name: :x
+ │ │ │ │ │ │ └── depth: 0
+ │ │ │ │ │ └── operator_loc: (2,16)-(2,18) = "=>"
+ │ │ │ │ └── operator_loc: ∅
+ │ │ │ ├── @ AssocNode (location: (2,22)-(2,28))
+ │ │ │ │ ├── key:
+ │ │ │ │ │ @ SymbolNode (location: (2,22)-(2,24))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── value_loc: (2,22)-(2,23) = "d"
+ │ │ │ │ │ ├── closing_loc: (2,23)-(2,24) = ":"
+ │ │ │ │ │ └── unescaped: "d"
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ StringNode (location: (2,25)-(2,28))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── opening_loc: (2,25)-(2,26) = "\""
+ │ │ │ │ │ ├── content_loc: (2,26)-(2,27) = "e"
+ │ │ │ │ │ ├── closing_loc: (2,27)-(2,28) = "\""
+ │ │ │ │ │ └── unescaped: "e"
+ │ │ │ │ └── operator_loc: ∅
+ │ │ │ └── @ AssocNode (location: (2,30)-(2,32))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (2,30)-(2,32))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (2,30)-(2,31) = "f"
+ │ │ │ │ ├── closing_loc: (2,31)-(2,32) = ":"
+ │ │ │ │ └── unescaped: "f"
+ │ │ │ ├── value:
+ │ │ │ │ @ ImplicitNode (location: (2,30)-(2,31))
+ │ │ │ │ └── value:
+ │ │ │ │ @ LocalVariableTargetNode (location: (2,30)-(2,31))
+ │ │ │ │ ├── name: :f
+ │ │ │ │ └── depth: 0
+ │ │ │ └── operator_loc: ∅
+ │ │ ├── rest: ∅
+ │ │ ├── opening_loc: (2,3)-(2,4) = "{"
+ │ │ └── closing_loc: (2,33)-(2,34) = "}"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,2)-(3,4))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SymbolNode (location: (3,2)-(3,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (3,2)-(3,3) = ":"
+ │ │ ├── value_loc: (3,3)-(3,4) = "g"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "g"
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: (2,35)-(2,39) = "then"
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (4,0)-(4,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/case_in_hash_pat_paren_assign.txt b/test/prism/snapshots/seattlerb/case_in_hash_pat_paren_assign.txt
new file mode 100644
index 0000000000..4c8cfd0e54
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in_hash_pat_paren_assign.txt
@@ -0,0 +1,51 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(4,3))
+ ├── predicate:
+ │ @ SymbolNode (location: (1,5)-(1,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(3,4))
+ │ ├── pattern:
+ │ │ @ HashPatternNode (location: (2,3)-(2,11))
+ │ │ ├── constant:
+ │ │ │ @ ConstantReadNode (location: (2,3)-(2,4))
+ │ │ │ └── name: :B
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (2,5)-(2,10))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (2,5)-(2,7))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (2,5)-(2,6) = "a"
+ │ │ │ │ ├── closing_loc: (2,6)-(2,7) = ":"
+ │ │ │ │ └── unescaped: "a"
+ │ │ │ ├── value:
+ │ │ │ │ @ IntegerNode (location: (2,8)-(2,10))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 42
+ │ │ │ └── operator_loc: ∅
+ │ │ ├── rest: ∅
+ │ │ ├── opening_loc: (2,4)-(2,5) = "("
+ │ │ └── closing_loc: (2,10)-(2,11) = ")"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,2)-(3,4))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SymbolNode (location: (3,2)-(3,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (3,2)-(3,3) = ":"
+ │ │ ├── value_loc: (3,3)-(3,4) = "d"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "d"
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (4,0)-(4,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/case_in_hash_pat_paren_true.txt b/test/prism/snapshots/seattlerb/case_in_hash_pat_paren_true.txt
new file mode 100644
index 0000000000..8ff95a161f
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in_hash_pat_paren_true.txt
@@ -0,0 +1,47 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(4,3))
+ ├── predicate:
+ │ @ SymbolNode (location: (1,5)-(1,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(3,4))
+ │ ├── pattern:
+ │ │ @ HashPatternNode (location: (2,3)-(2,10))
+ │ │ ├── constant: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (2,3)-(2,10))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (2,3)-(2,5))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (2,3)-(2,4) = "b"
+ │ │ │ │ ├── closing_loc: (2,4)-(2,5) = ":"
+ │ │ │ │ └── unescaped: "b"
+ │ │ │ ├── value:
+ │ │ │ │ @ TrueNode (location: (2,6)-(2,10))
+ │ │ │ └── operator_loc: ∅
+ │ │ ├── rest: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,2)-(3,4))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SymbolNode (location: (3,2)-(3,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (3,2)-(3,3) = ":"
+ │ │ ├── value_loc: (3,3)-(3,4) = "c"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "c"
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: (2,11)-(2,15) = "then"
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (4,0)-(4,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/case_in_hash_pat_rest.txt b/test/prism/snapshots/seattlerb/case_in_hash_pat_rest.txt
new file mode 100644
index 0000000000..b93b889ec5
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in_hash_pat_rest.txt
@@ -0,0 +1,55 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: [:c, :rest]
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(3,3))
+ ├── predicate:
+ │ @ SymbolNode (location: (1,5)-(1,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(2,23))
+ │ ├── pattern:
+ │ │ @ HashPatternNode (location: (2,3)-(2,15))
+ │ │ ├── constant: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (2,3)-(2,7))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (2,3)-(2,5))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (2,3)-(2,4) = "b"
+ │ │ │ │ ├── closing_loc: (2,4)-(2,5) = ":"
+ │ │ │ │ └── unescaped: "b"
+ │ │ │ ├── value:
+ │ │ │ │ @ LocalVariableTargetNode (location: (2,6)-(2,7))
+ │ │ │ │ ├── name: :c
+ │ │ │ │ └── depth: 0
+ │ │ │ └── operator_loc: ∅
+ │ │ ├── rest:
+ │ │ │ @ AssocSplatNode (location: (2,9)-(2,15))
+ │ │ │ ├── value:
+ │ │ │ │ @ LocalVariableTargetNode (location: (2,11)-(2,15))
+ │ │ │ │ ├── name: :rest
+ │ │ │ │ └── depth: 0
+ │ │ │ └── operator_loc: (2,9)-(2,11) = "**"
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (2,21)-(2,23))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SymbolNode (location: (2,21)-(2,23))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (2,21)-(2,22) = ":"
+ │ │ ├── value_loc: (2,22)-(2,23) = "d"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "d"
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: (2,16)-(2,20) = "then"
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (3,0)-(3,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/case_in_hash_pat_rest_solo.txt b/test/prism/snapshots/seattlerb/case_in_hash_pat_rest_solo.txt
new file mode 100644
index 0000000000..956e93faa0
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in_hash_pat_rest_solo.txt
@@ -0,0 +1,42 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: [:rest]
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(3,3))
+ ├── predicate:
+ │ @ SymbolNode (location: (1,5)-(1,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(2,17))
+ │ ├── pattern:
+ │ │ @ HashPatternNode (location: (2,3)-(2,9))
+ │ │ ├── constant: ∅
+ │ │ ├── elements: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ AssocSplatNode (location: (2,3)-(2,9))
+ │ │ │ ├── value:
+ │ │ │ │ @ LocalVariableTargetNode (location: (2,5)-(2,9))
+ │ │ │ │ ├── name: :rest
+ │ │ │ │ └── depth: 0
+ │ │ │ └── operator_loc: (2,3)-(2,5) = "**"
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (2,15)-(2,17))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SymbolNode (location: (2,15)-(2,17))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (2,15)-(2,16) = ":"
+ │ │ ├── value_loc: (2,16)-(2,17) = "d"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "d"
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: (2,10)-(2,14) = "then"
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (3,0)-(3,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/case_in_if_unless_post_mod.txt b/test/prism/snapshots/seattlerb/case_in_if_unless_post_mod.txt
new file mode 100644
index 0000000000..a21d3e15dd
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in_if_unless_post_mod.txt
@@ -0,0 +1,67 @@
+@ ProgramNode (location: (1,0)-(6,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(6,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(6,3))
+ ├── predicate:
+ │ @ SymbolNode (location: (1,5)-(1,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── conditions: (length: 2)
+ │ ├── @ InNode (location: (2,0)-(3,4))
+ │ │ ├── pattern:
+ │ │ │ @ IfNode (location: (2,3)-(2,12))
+ │ │ │ ├── if_keyword_loc: (2,5)-(2,7) = "if"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ TrueNode (location: (2,8)-(2,12))
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (2,3)-(2,4))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ ConstantReadNode (location: (2,3)-(2,4))
+ │ │ │ │ └── name: :A
+ │ │ │ ├── consequent: ∅
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (3,2)-(3,4))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (3,2)-(3,4))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (3,2)-(3,3) = ":"
+ │ │ │ ├── value_loc: (3,3)-(3,4) = "C"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "C"
+ │ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ │ └── then_loc: ∅
+ │ └── @ InNode (location: (4,0)-(5,4))
+ │ ├── pattern:
+ │ │ @ UnlessNode (location: (4,3)-(4,17))
+ │ │ ├── keyword_loc: (4,5)-(4,11) = "unless"
+ │ │ ├── predicate:
+ │ │ │ @ FalseNode (location: (4,12)-(4,17))
+ │ │ ├── then_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (4,3)-(4,4))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ ConstantReadNode (location: (4,3)-(4,4))
+ │ │ │ └── name: :D
+ │ │ ├── consequent: ∅
+ │ │ └── end_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (5,2)-(5,4))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SymbolNode (location: (5,2)-(5,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (5,2)-(5,3) = ":"
+ │ │ ├── value_loc: (5,3)-(5,4) = "E"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "E"
+ │ ├── in_loc: (4,0)-(4,2) = "in"
+ │ └── then_loc: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (6,0)-(6,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/case_in_multiple.txt b/test/prism/snapshots/seattlerb/case_in_multiple.txt
new file mode 100644
index 0000000000..d8597c4bfa
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in_multiple.txt
@@ -0,0 +1,59 @@
+@ ProgramNode (location: (1,0)-(6,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(6,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(6,3))
+ ├── predicate:
+ │ @ SymbolNode (location: (1,5)-(1,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── conditions: (length: 2)
+ │ ├── @ InNode (location: (2,0)-(3,4))
+ │ │ ├── pattern:
+ │ │ │ @ ConstantPathNode (location: (2,3)-(2,7))
+ │ │ │ ├── parent:
+ │ │ │ │ @ ConstantReadNode (location: (2,3)-(2,4))
+ │ │ │ │ └── name: :A
+ │ │ │ ├── child:
+ │ │ │ │ @ ConstantReadNode (location: (2,6)-(2,7))
+ │ │ │ │ └── name: :B
+ │ │ │ └── delimiter_loc: (2,4)-(2,6) = "::"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (3,2)-(3,4))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (3,2)-(3,4))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (3,2)-(3,3) = ":"
+ │ │ │ ├── value_loc: (3,3)-(3,4) = "C"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "C"
+ │ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ │ └── then_loc: ∅
+ │ └── @ InNode (location: (4,0)-(5,4))
+ │ ├── pattern:
+ │ │ @ ConstantPathNode (location: (4,3)-(4,7))
+ │ │ ├── parent:
+ │ │ │ @ ConstantReadNode (location: (4,3)-(4,4))
+ │ │ │ └── name: :D
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (4,6)-(4,7))
+ │ │ │ └── name: :E
+ │ │ └── delimiter_loc: (4,4)-(4,6) = "::"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (5,2)-(5,4))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SymbolNode (location: (5,2)-(5,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (5,2)-(5,3) = ":"
+ │ │ ├── value_loc: (5,3)-(5,4) = "F"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "F"
+ │ ├── in_loc: (4,0)-(4,2) = "in"
+ │ └── then_loc: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (6,0)-(6,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/case_in_or.txt b/test/prism/snapshots/seattlerb/case_in_or.txt
new file mode 100644
index 0000000000..7ac6617608
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/case_in_or.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(4,3))
+ ├── predicate:
+ │ @ SymbolNode (location: (1,5)-(1,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(3,4))
+ │ ├── pattern:
+ │ │ @ AlternationPatternNode (location: (2,3)-(2,8))
+ │ │ ├── left:
+ │ │ │ @ ConstantReadNode (location: (2,3)-(2,4))
+ │ │ │ └── name: :B
+ │ │ ├── right:
+ │ │ │ @ ConstantReadNode (location: (2,7)-(2,8))
+ │ │ │ └── name: :C
+ │ │ └── operator_loc: (2,5)-(2,6) = "|"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,2)-(3,4))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SymbolNode (location: (3,2)-(3,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (3,2)-(3,3) = ":"
+ │ │ ├── value_loc: (3,3)-(3,4) = "d"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "d"
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (4,0)-(4,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/class_comments.txt b/test/prism/snapshots/seattlerb/class_comments.txt
new file mode 100644
index 0000000000..5ac05b6be6
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/class_comments.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (4,0)-(9,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (4,0)-(9,3))
+ └── body: (length: 1)
+ └── @ ClassNode (location: (4,0)-(9,3))
+ ├── locals: []
+ ├── class_keyword_loc: (4,0)-(4,5) = "class"
+ ├── constant_path:
+ │ @ ConstantReadNode (location: (4,6)-(4,7))
+ │ └── name: :X
+ ├── inheritance_operator_loc: ∅
+ ├── superclass: ∅
+ ├── body:
+ │ @ StatementsNode (location: (6,2)-(8,5))
+ │ └── body: (length: 1)
+ │ └── @ DefNode (location: (6,2)-(8,5))
+ │ ├── name: :blah
+ │ ├── name_loc: (6,6)-(6,10) = "blah"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (6,2)-(6,5) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (8,2)-(8,5) = "end"
+ ├── end_keyword_loc: (9,0)-(9,3) = "end"
+ └── name: :X
diff --git a/test/prism/snapshots/seattlerb/cond_unary_minus.txt b/test/prism/snapshots/seattlerb/cond_unary_minus.txt
new file mode 100644
index 0000000000..b6e12dfb15
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/cond_unary_minus.txt
@@ -0,0 +1,15 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(1,10))
+ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ ├── predicate:
+ │ @ IntegerNode (location: (1,3)-(1,5))
+ │ ├── flags: decimal
+ │ └── value: -1
+ ├── then_keyword_loc: ∅
+ ├── statements: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (1,7)-(1,10) = "end"
diff --git a/test/prism/snapshots/seattlerb/const_2_op_asgn_or2.txt b/test/prism/snapshots/seattlerb/const_2_op_asgn_or2.txt
new file mode 100644
index 0000000000..b018ac48d4
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/const_2_op_asgn_or2.txt
@@ -0,0 +1,24 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ ConstantPathOrWriteNode (location: (1,0)-(1,12))
+ ├── target:
+ │ @ ConstantPathNode (location: (1,0)-(1,6))
+ │ ├── parent:
+ │ │ @ ConstantPathNode (location: (1,0)-(1,3))
+ │ │ ├── parent: ∅
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (1,2)-(1,3))
+ │ │ │ └── name: :X
+ │ │ └── delimiter_loc: (1,0)-(1,2) = "::"
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (1,5)-(1,6))
+ │ │ └── name: :Y
+ │ └── delimiter_loc: (1,3)-(1,5) = "::"
+ ├── operator_loc: (1,7)-(1,10) = "||="
+ └── value:
+ @ IntegerNode (location: (1,11)-(1,12))
+ ├── flags: decimal
+ └── value: 1
diff --git a/test/prism/snapshots/seattlerb/const_3_op_asgn_or.txt b/test/prism/snapshots/seattlerb/const_3_op_asgn_or.txt
new file mode 100644
index 0000000000..8d9d94931b
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/const_3_op_asgn_or.txt
@@ -0,0 +1,18 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ ConstantPathOrWriteNode (location: (1,0)-(1,9))
+ ├── target:
+ │ @ ConstantPathNode (location: (1,0)-(1,3))
+ │ ├── parent: ∅
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (1,2)-(1,3))
+ │ │ └── name: :X
+ │ └── delimiter_loc: (1,0)-(1,2) = "::"
+ ├── operator_loc: (1,4)-(1,7) = "||="
+ └── value:
+ @ IntegerNode (location: (1,8)-(1,9))
+ ├── flags: decimal
+ └── value: 1
diff --git a/test/prism/snapshots/seattlerb/const_op_asgn_and1.txt b/test/prism/snapshots/seattlerb/const_op_asgn_and1.txt
new file mode 100644
index 0000000000..b1d61b3752
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/const_op_asgn_and1.txt
@@ -0,0 +1,19 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ ConstantPathOperatorWriteNode (location: (1,0)-(1,8))
+ ├── target:
+ │ @ ConstantPathNode (location: (1,0)-(1,3))
+ │ ├── parent: ∅
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (1,2)-(1,3))
+ │ │ └── name: :X
+ │ └── delimiter_loc: (1,0)-(1,2) = "::"
+ ├── operator_loc: (1,4)-(1,6) = "&="
+ ├── value:
+ │ @ IntegerNode (location: (1,7)-(1,8))
+ │ ├── flags: decimal
+ │ └── value: 1
+ └── operator: :&
diff --git a/test/prism/snapshots/seattlerb/const_op_asgn_and2.txt b/test/prism/snapshots/seattlerb/const_op_asgn_and2.txt
new file mode 100644
index 0000000000..22f6682534
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/const_op_asgn_and2.txt
@@ -0,0 +1,18 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ ConstantPathAndWriteNode (location: (1,0)-(1,9))
+ ├── target:
+ │ @ ConstantPathNode (location: (1,0)-(1,3))
+ │ ├── parent: ∅
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (1,2)-(1,3))
+ │ │ └── name: :X
+ │ └── delimiter_loc: (1,0)-(1,2) = "::"
+ ├── operator_loc: (1,4)-(1,7) = "&&="
+ └── value:
+ @ IntegerNode (location: (1,8)-(1,9))
+ ├── flags: decimal
+ └── value: 1
diff --git a/test/prism/snapshots/seattlerb/const_op_asgn_or.txt b/test/prism/snapshots/seattlerb/const_op_asgn_or.txt
new file mode 100644
index 0000000000..067e0fbb93
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/const_op_asgn_or.txt
@@ -0,0 +1,20 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ ConstantPathOrWriteNode (location: (1,0)-(1,10))
+ ├── target:
+ │ @ ConstantPathNode (location: (1,0)-(1,4))
+ │ ├── parent:
+ │ │ @ ConstantReadNode (location: (1,0)-(1,1))
+ │ │ └── name: :X
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (1,3)-(1,4))
+ │ │ └── name: :Y
+ │ └── delimiter_loc: (1,1)-(1,3) = "::"
+ ├── operator_loc: (1,5)-(1,8) = "||="
+ └── value:
+ @ IntegerNode (location: (1,9)-(1,10))
+ ├── flags: decimal
+ └── value: 1
diff --git a/test/prism/snapshots/seattlerb/dasgn_icky2.txt b/test/prism/snapshots/seattlerb/dasgn_icky2.txt
new file mode 100644
index 0000000000..e118362f87
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/dasgn_icky2.txt
@@ -0,0 +1,61 @@
+@ ProgramNode (location: (1,0)-(8,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(8,3))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(8,3))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(8,3))
+ ├── locals: [:v]
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (2,2)-(7,5))
+ │ └── body: (length: 2)
+ │ ├── @ LocalVariableWriteNode (location: (2,2)-(2,9))
+ │ │ ├── name: :v
+ │ │ ├── depth: 0
+ │ │ ├── name_loc: (2,2)-(2,3) = "v"
+ │ │ ├── value:
+ │ │ │ @ NilNode (location: (2,6)-(2,9))
+ │ │ └── operator_loc: (2,4)-(2,5) = "="
+ │ └── @ BeginNode (location: (3,2)-(7,5))
+ │ ├── begin_keyword_loc: (3,2)-(3,7) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (4,4)-(4,9))
+ │ │ └── body: (length: 1)
+ │ │ └── @ YieldNode (location: (4,4)-(4,9))
+ │ │ ├── keyword_loc: (4,4)-(4,9) = "yield"
+ │ │ ├── lparen_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ └── rparen_loc: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (5,2)-(6,9))
+ │ │ ├── keyword_loc: (5,2)-(5,8) = "rescue"
+ │ │ ├── exceptions: (length: 1)
+ │ │ │ └── @ ConstantReadNode (location: (5,9)-(5,18))
+ │ │ │ └── name: :Exception
+ │ │ ├── operator_loc: (5,19)-(5,21) = "=>"
+ │ │ ├── reference:
+ │ │ │ @ LocalVariableTargetNode (location: (5,22)-(5,23))
+ │ │ │ ├── name: :v
+ │ │ │ └── depth: 0
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (6,4)-(6,9))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ BreakNode (location: (6,4)-(6,9))
+ │ │ │ ├── arguments: ∅
+ │ │ │ └── keyword_loc: (6,4)-(6,9) = "break"
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (7,2)-(7,5) = "end"
+ ├── opening_loc: (1,2)-(1,4) = "do"
+ └── closing_loc: (8,0)-(8,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/defined_eh_parens.txt b/test/prism/snapshots/seattlerb/defined_eh_parens.txt
new file mode 100644
index 0000000000..49b577fcd1
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defined_eh_parens.txt
@@ -0,0 +1,13 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ DefinedNode (location: (1,0)-(1,12))
+ ├── lparen_loc: (1,8)-(1,9) = "("
+ ├── value:
+ │ @ IntegerNode (location: (1,9)-(1,11))
+ │ ├── flags: decimal
+ │ └── value: 42
+ ├── rparen_loc: (1,11)-(1,12) = ")"
+ └── keyword_loc: (1,0)-(1,8) = "defined?"
diff --git a/test/prism/snapshots/seattlerb/defn_arg_asplat_arg.txt b/test/prism/snapshots/seattlerb/defn_arg_asplat_arg.txt
new file mode 100644
index 0000000000..3f2bdf44a4
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_arg_asplat_arg.txt
@@ -0,0 +1,37 @@
+@ ProgramNode (location: (1,0)-(1,29))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,29))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,29))
+ ├── name: :call
+ ├── name_loc: (1,4)-(1,8) = "call"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,9)-(1,24))
+ │ ├── requireds: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (1,9)-(1,15))
+ │ │ ├── flags: ∅
+ │ │ └── name: :interp
+ │ ├── optionals: (length: 0)
+ │ ├── rest:
+ │ │ @ RestParameterNode (location: (1,17)-(1,18))
+ │ │ ├── flags: ∅
+ │ │ ├── name: ∅
+ │ │ ├── name_loc: ∅
+ │ │ └── operator_loc: (1,17)-(1,18) = "*"
+ │ ├── posts: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (1,20)-(1,24))
+ │ │ ├── flags: ∅
+ │ │ └── name: :args
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:interp, :args]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,8)-(1,9) = "("
+ ├── rparen_loc: (1,24)-(1,25) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,26)-(1,29) = "end"
diff --git a/test/prism/snapshots/seattlerb/defn_arg_forward_args.txt b/test/prism/snapshots/seattlerb/defn_arg_forward_args.txt
new file mode 100644
index 0000000000..4121770c5c
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_arg_forward_args.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(1,29))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,29))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,29))
+ ├── name: :a
+ ├── name_loc: (1,4)-(1,5) = "a"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,12))
+ │ ├── requireds: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (1,6)-(1,7))
+ │ │ ├── flags: ∅
+ │ │ └── name: :x
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest:
+ │ │ @ ForwardingParameterNode (location: (1,9)-(1,12))
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,15)-(1,24))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,15)-(1,24))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :b
+ │ ├── message_loc: (1,15)-(1,16) = "b"
+ │ ├── opening_loc: (1,16)-(1,17) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,17)-(1,23))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ LocalVariableReadNode (location: (1,17)-(1,18))
+ │ │ │ ├── name: :x
+ │ │ │ └── depth: 0
+ │ │ └── @ ForwardingArgumentsNode (location: (1,20)-(1,23))
+ │ ├── closing_loc: (1,23)-(1,24) = ")"
+ │ └── block: ∅
+ ├── locals: [:x]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,5)-(1,6) = "("
+ ├── rparen_loc: (1,12)-(1,13) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,26)-(1,29) = "end"
diff --git a/test/prism/snapshots/seattlerb/defn_args_forward_args.txt b/test/prism/snapshots/seattlerb/defn_args_forward_args.txt
new file mode 100644
index 0000000000..178b6ccde7
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_args_forward_args.txt
@@ -0,0 +1,61 @@
+@ ProgramNode (location: (1,0)-(1,41))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,41))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,41))
+ ├── name: :a
+ ├── name_loc: (1,4)-(1,5) = "a"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,18))
+ │ ├── requireds: (length: 3)
+ │ │ ├── @ RequiredParameterNode (location: (1,6)-(1,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :x
+ │ │ ├── @ RequiredParameterNode (location: (1,9)-(1,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :y
+ │ │ └── @ RequiredParameterNode (location: (1,12)-(1,13))
+ │ │ ├── flags: ∅
+ │ │ └── name: :z
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest:
+ │ │ @ ForwardingParameterNode (location: (1,15)-(1,18))
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,21)-(1,36))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,21)-(1,36))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :b
+ │ ├── message_loc: (1,21)-(1,22) = "b"
+ │ ├── opening_loc: (1,22)-(1,23) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,23)-(1,35))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 3)
+ │ │ ├── @ SymbolNode (location: (1,23)-(1,27))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (1,23)-(1,24) = ":"
+ │ │ │ ├── value_loc: (1,24)-(1,27) = "get"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "get"
+ │ │ ├── @ LocalVariableReadNode (location: (1,29)-(1,30))
+ │ │ │ ├── name: :z
+ │ │ │ └── depth: 0
+ │ │ └── @ ForwardingArgumentsNode (location: (1,32)-(1,35))
+ │ ├── closing_loc: (1,35)-(1,36) = ")"
+ │ └── block: ∅
+ ├── locals: [:x, :y, :z]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,5)-(1,6) = "("
+ ├── rparen_loc: (1,18)-(1,19) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,38)-(1,41) = "end"
diff --git a/test/prism/snapshots/seattlerb/defn_comments.txt b/test/prism/snapshots/seattlerb/defn_comments.txt
new file mode 100644
index 0000000000..585aa65c9a
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_comments.txt
@@ -0,0 +1,18 @@
+@ ProgramNode (location: (4,0)-(5,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (4,0)-(5,3))
+ └── body: (length: 1)
+ └── @ DefNode (location: (4,0)-(5,3))
+ ├── name: :blah
+ ├── name_loc: (4,4)-(4,8) = "blah"
+ ├── receiver: ∅
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (4,0)-(4,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (5,0)-(5,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/defn_endless_command.txt b/test/prism/snapshots/seattlerb/defn_endless_command.txt
new file mode 100644
index 0000000000..c3ea59282a
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_endless_command.txt
@@ -0,0 +1,36 @@
+@ ProgramNode (location: (1,0)-(1,33))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,33))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,33))
+ ├── name: :some_method
+ ├── name_loc: (1,4)-(1,15) = "some_method"
+ ├── receiver: ∅
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,18)-(1,33))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,18)-(1,33))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :other_method
+ │ ├── message_loc: (1,18)-(1,30) = "other_method"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,31)-(1,33))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,31)-(1,33))
+ │ │ ├── flags: decimal
+ │ │ └── value: 42
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: (1,16)-(1,17) = "="
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/defn_endless_command_rescue.txt b/test/prism/snapshots/seattlerb/defn_endless_command_rescue.txt
new file mode 100644
index 0000000000..dfd1d01ba8
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_endless_command_rescue.txt
@@ -0,0 +1,43 @@
+@ ProgramNode (location: (1,0)-(1,43))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,43))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,43))
+ ├── name: :some_method
+ ├── name_loc: (1,4)-(1,15) = "some_method"
+ ├── receiver: ∅
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,18)-(1,43))
+ │ └── body: (length: 1)
+ │ └── @ RescueModifierNode (location: (1,18)-(1,43))
+ │ ├── expression:
+ │ │ @ CallNode (location: (1,18)-(1,33))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :other_method
+ │ │ ├── message_loc: (1,18)-(1,30) = "other_method"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (1,31)-(1,33))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (1,31)-(1,33))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (1,34)-(1,40) = "rescue"
+ │ └── rescue_expression:
+ │ @ IntegerNode (location: (1,41)-(1,43))
+ │ ├── flags: decimal
+ │ └── value: 24
+ ├── locals: []
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: (1,16)-(1,17) = "="
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/defn_forward_args.txt b/test/prism/snapshots/seattlerb/defn_forward_args.txt
new file mode 100644
index 0000000000..71a984c811
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_forward_args.txt
@@ -0,0 +1,43 @@
+@ ProgramNode (location: (1,0)-(1,23))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,23))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,23))
+ ├── name: :a
+ ├── name_loc: (1,4)-(1,5) = "a"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,9))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest:
+ │ │ @ ForwardingParameterNode (location: (1,6)-(1,9))
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,12)-(1,18))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,12)-(1,18))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :b
+ │ ├── message_loc: (1,12)-(1,13) = "b"
+ │ ├── opening_loc: (1,13)-(1,14) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,14)-(1,17))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ ForwardingArgumentsNode (location: (1,14)-(1,17))
+ │ ├── closing_loc: (1,17)-(1,18) = ")"
+ │ └── block: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,5)-(1,6) = "("
+ ├── rparen_loc: (1,9)-(1,10) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,20)-(1,23) = "end"
diff --git a/test/prism/snapshots/seattlerb/defn_forward_args__no_parens.txt b/test/prism/snapshots/seattlerb/defn_forward_args__no_parens.txt
new file mode 100644
index 0000000000..4a4d69e4d0
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_forward_args__no_parens.txt
@@ -0,0 +1,43 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(3,3))
+ ├── name: :f
+ ├── name_loc: (1,4)-(1,5) = "f"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,9))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest:
+ │ │ @ ForwardingParameterNode (location: (1,6)-(1,9))
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (2,2)-(2,8))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (2,2)-(2,8))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (2,2)-(2,3) = "m"
+ │ ├── opening_loc: (2,3)-(2,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (2,4)-(2,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ ForwardingArgumentsNode (location: (2,4)-(2,7))
+ │ ├── closing_loc: (2,7)-(2,8) = ")"
+ │ └── block: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (3,0)-(3,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/defn_kwarg_env.txt b/test/prism/snapshots/seattlerb/defn_kwarg_env.txt
new file mode 100644
index 0000000000..f420420fc3
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_kwarg_env.txt
@@ -0,0 +1,55 @@
+@ ProgramNode (location: (1,0)-(1,45))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,45))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,45))
+ ├── name: :test
+ ├── name_loc: (1,4)-(1,8) = "test"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,9)-(1,18))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest:
+ │ │ @ KeywordRestParameterNode (location: (1,9)-(1,18))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :testing
+ │ │ ├── name_loc: (1,11)-(1,18) = "testing"
+ │ │ └── operator_loc: (1,9)-(1,11) = "**"
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,20)-(1,41))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,20)-(1,41))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :test_splat
+ │ ├── message_loc: (1,20)-(1,30) = "test_splat"
+ │ ├── opening_loc: (1,30)-(1,31) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,31)-(1,40))
+ │ │ ├── flags: contains_keyword_splat
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (1,31)-(1,40))
+ │ │ ├── flags: ∅
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocSplatNode (location: (1,31)-(1,40))
+ │ │ ├── value:
+ │ │ │ @ LocalVariableReadNode (location: (1,33)-(1,40))
+ │ │ │ ├── name: :testing
+ │ │ │ └── depth: 0
+ │ │ └── operator_loc: (1,31)-(1,33) = "**"
+ │ ├── closing_loc: (1,40)-(1,41) = ")"
+ │ └── block: ∅
+ ├── locals: [:testing]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,8)-(1,9) = "("
+ ├── rparen_loc: (1,18)-(1,19) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,42)-(1,45) = "end"
diff --git a/test/prism/snapshots/seattlerb/defn_kwarg_kwarg.txt b/test/prism/snapshots/seattlerb/defn_kwarg_kwarg.txt
new file mode 100644
index 0000000000..8a5022ff37
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_kwarg_kwarg.txt
@@ -0,0 +1,45 @@
+@ ProgramNode (location: (1,0)-(1,24))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,24))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,24))
+ ├── name: :f
+ ├── name_loc: (1,4)-(1,5) = "f"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,19))
+ │ ├── requireds: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (1,6)-(1,7))
+ │ │ ├── flags: ∅
+ │ │ └── name: :a
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 2)
+ │ │ ├── @ OptionalKeywordParameterNode (location: (1,9)-(1,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (1,9)-(1,11) = "b:"
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,12)-(1,13))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ OptionalKeywordParameterNode (location: (1,15)-(1,19))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :c
+ │ │ ├── name_loc: (1,15)-(1,17) = "c:"
+ │ │ └── value:
+ │ │ @ IntegerNode (location: (1,18)-(1,19))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:a, :b, :c]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,5)-(1,6) = "("
+ ├── rparen_loc: (1,19)-(1,20) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,21)-(1,24) = "end"
diff --git a/test/prism/snapshots/seattlerb/defn_kwarg_kwsplat.txt b/test/prism/snapshots/seattlerb/defn_kwarg_kwsplat.txt
new file mode 100644
index 0000000000..4c980bc27f
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_kwarg_kwsplat.txt
@@ -0,0 +1,39 @@
+@ ProgramNode (location: (1,0)-(1,20))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,20))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,20))
+ ├── name: :a
+ ├── name_loc: (1,4)-(1,5) = "a"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,15))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 1)
+ │ │ └── @ OptionalKeywordParameterNode (location: (1,6)-(1,10))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (1,6)-(1,8) = "b:"
+ │ │ └── value:
+ │ │ @ IntegerNode (location: (1,9)-(1,10))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── keyword_rest:
+ │ │ @ KeywordRestParameterNode (location: (1,12)-(1,15))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :c
+ │ │ ├── name_loc: (1,14)-(1,15) = "c"
+ │ │ └── operator_loc: (1,12)-(1,14) = "**"
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:b, :c]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,5)-(1,6) = "("
+ ├── rparen_loc: (1,15)-(1,16) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,17)-(1,20) = "end"
diff --git a/test/prism/snapshots/seattlerb/defn_kwarg_kwsplat_anon.txt b/test/prism/snapshots/seattlerb/defn_kwarg_kwsplat_anon.txt
new file mode 100644
index 0000000000..40afacc2a7
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_kwarg_kwsplat_anon.txt
@@ -0,0 +1,39 @@
+@ ProgramNode (location: (1,0)-(1,19))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,19))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,19))
+ ├── name: :a
+ ├── name_loc: (1,4)-(1,5) = "a"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,14))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 1)
+ │ │ └── @ OptionalKeywordParameterNode (location: (1,6)-(1,10))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (1,6)-(1,8) = "b:"
+ │ │ └── value:
+ │ │ @ IntegerNode (location: (1,9)-(1,10))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── keyword_rest:
+ │ │ @ KeywordRestParameterNode (location: (1,12)-(1,14))
+ │ │ ├── flags: ∅
+ │ │ ├── name: ∅
+ │ │ ├── name_loc: ∅
+ │ │ └── operator_loc: (1,12)-(1,14) = "**"
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:b]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,5)-(1,6) = "("
+ ├── rparen_loc: (1,14)-(1,15) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,16)-(1,19) = "end"
diff --git a/test/prism/snapshots/seattlerb/defn_kwarg_lvar.txt b/test/prism/snapshots/seattlerb/defn_kwarg_lvar.txt
new file mode 100644
index 0000000000..0eae56924c
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_kwarg_lvar.txt
@@ -0,0 +1,42 @@
+@ ProgramNode (location: (1,0)-(1,26))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,26))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,26))
+ ├── name: :fun
+ ├── name_loc: (1,4)-(1,7) = "fun"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,8)-(1,16))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 1)
+ │ │ └── @ OptionalKeywordParameterNode (location: (1,8)-(1,16))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :kw
+ │ │ ├── name_loc: (1,8)-(1,11) = "kw:"
+ │ │ └── value:
+ │ │ @ SymbolNode (location: (1,12)-(1,16))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,12)-(1,13) = ":"
+ │ │ ├── value_loc: (1,13)-(1,16) = "val"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "val"
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,19)-(1,21))
+ │ └── body: (length: 1)
+ │ └── @ LocalVariableReadNode (location: (1,19)-(1,21))
+ │ ├── name: :kw
+ │ └── depth: 0
+ ├── locals: [:kw]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,7)-(1,8) = "("
+ ├── rparen_loc: (1,16)-(1,17) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,23)-(1,26) = "end"
diff --git a/test/prism/snapshots/seattlerb/defn_kwarg_no_parens.txt b/test/prism/snapshots/seattlerb/defn_kwarg_no_parens.txt
new file mode 100644
index 0000000000..bc5747ad02
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_kwarg_no_parens.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(2,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,3))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(2,3))
+ ├── name: :f
+ ├── name_loc: (1,4)-(1,5) = "f"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,10))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 1)
+ │ │ └── @ OptionalKeywordParameterNode (location: (1,6)-(1,10))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :a
+ │ │ ├── name_loc: (1,6)-(1,8) = "a:"
+ │ │ └── value:
+ │ │ @ IntegerNode (location: (1,9)-(1,10))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:a]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (2,0)-(2,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/defn_kwarg_val.txt b/test/prism/snapshots/seattlerb/defn_kwarg_val.txt
new file mode 100644
index 0000000000..82527f7875
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_kwarg_val.txt
@@ -0,0 +1,37 @@
+@ ProgramNode (location: (1,0)-(1,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,17))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,17))
+ ├── name: :f
+ ├── name_loc: (1,4)-(1,5) = "f"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,12))
+ │ ├── requireds: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (1,6)-(1,7))
+ │ │ ├── flags: ∅
+ │ │ └── name: :a
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 1)
+ │ │ └── @ OptionalKeywordParameterNode (location: (1,9)-(1,12))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (1,9)-(1,11) = "b:"
+ │ │ └── value:
+ │ │ @ IntegerNode (location: (1,11)-(1,12))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:a, :b]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,5)-(1,6) = "("
+ ├── rparen_loc: (1,12)-(1,13) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,14)-(1,17) = "end"
diff --git a/test/prism/snapshots/seattlerb/defn_no_kwargs.txt b/test/prism/snapshots/seattlerb/defn_no_kwargs.txt
new file mode 100644
index 0000000000..0ef0634a53
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_no_kwargs.txt
@@ -0,0 +1,29 @@
+@ ProgramNode (location: (1,0)-(1,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,17))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,17))
+ ├── name: :x
+ ├── name_loc: (1,4)-(1,5) = "x"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,11))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest:
+ │ │ @ NoKeywordsParameterNode (location: (1,6)-(1,11))
+ │ │ ├── operator_loc: (1,6)-(1,8) = "**"
+ │ │ └── keyword_loc: (1,8)-(1,11) = "nil"
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,5)-(1,6) = "("
+ ├── rparen_loc: (1,11)-(1,12) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,14)-(1,17) = "end"
diff --git a/test/prism/snapshots/seattlerb/defn_oneliner.txt b/test/prism/snapshots/seattlerb/defn_oneliner.txt
new file mode 100644
index 0000000000..e700499809
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_oneliner.txt
@@ -0,0 +1,47 @@
+@ ProgramNode (location: (1,0)-(1,27))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,27))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,27))
+ ├── name: :exec
+ ├── name_loc: (1,4)-(1,8) = "exec"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,9)-(1,12))
+ │ ├── requireds: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (1,9)-(1,12))
+ │ │ ├── flags: ∅
+ │ │ └── name: :cmd
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,16)-(1,27))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,16)-(1,27))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :system
+ │ ├── message_loc: (1,16)-(1,22) = "system"
+ │ ├── opening_loc: (1,22)-(1,23) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,23)-(1,26))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (1,23)-(1,26))
+ │ │ ├── name: :cmd
+ │ │ └── depth: 0
+ │ ├── closing_loc: (1,26)-(1,27) = ")"
+ │ └── block: ∅
+ ├── locals: [:cmd]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,8)-(1,9) = "("
+ ├── rparen_loc: (1,12)-(1,13) = ")"
+ ├── equal_loc: (1,14)-(1,15) = "="
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/defn_oneliner_eq2.txt b/test/prism/snapshots/seattlerb/defn_oneliner_eq2.txt
new file mode 100644
index 0000000000..2708351ede
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_oneliner_eq2.txt
@@ -0,0 +1,47 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 1)
+ └── @ ClassNode (location: (1,0)-(3,3))
+ ├── locals: []
+ ├── class_keyword_loc: (1,0)-(1,5) = "class"
+ ├── constant_path:
+ │ @ ConstantReadNode (location: (1,6)-(1,7))
+ │ └── name: :X
+ ├── inheritance_operator_loc: ∅
+ ├── superclass: ∅
+ ├── body:
+ │ @ StatementsNode (location: (2,2)-(2,16))
+ │ └── body: (length: 1)
+ │ └── @ DefNode (location: (2,2)-(2,16))
+ │ ├── name: :==
+ │ ├── name_loc: (2,6)-(2,8) = "=="
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (2,9)-(2,10))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (2,9)-(2,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :o
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (2,14)-(2,16))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (2,14)-(2,16))
+ │ │ ├── flags: decimal
+ │ │ └── value: 42
+ │ ├── locals: [:o]
+ │ ├── def_keyword_loc: (2,2)-(2,5) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (2,8)-(2,9) = "("
+ │ ├── rparen_loc: (2,10)-(2,11) = ")"
+ │ ├── equal_loc: (2,12)-(2,13) = "="
+ │ └── end_keyword_loc: ∅
+ ├── end_keyword_loc: (3,0)-(3,3) = "end"
+ └── name: :X
diff --git a/test/prism/snapshots/seattlerb/defn_oneliner_noargs.txt b/test/prism/snapshots/seattlerb/defn_oneliner_noargs.txt
new file mode 100644
index 0000000000..54555b1a23
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_oneliner_noargs.txt
@@ -0,0 +1,30 @@
+@ ProgramNode (location: (1,0)-(1,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,17))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,17))
+ ├── name: :exec
+ ├── name_loc: (1,4)-(1,8) = "exec"
+ ├── receiver: ∅
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,11)-(1,17))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,11)-(1,17))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :system
+ │ ├── message_loc: (1,11)-(1,17) = "system"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: (1,9)-(1,10) = "="
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/defn_oneliner_noargs_parentheses.txt b/test/prism/snapshots/seattlerb/defn_oneliner_noargs_parentheses.txt
new file mode 100644
index 0000000000..e0fc4636f1
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_oneliner_noargs_parentheses.txt
@@ -0,0 +1,30 @@
+@ ProgramNode (location: (1,0)-(1,19))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,19))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,19))
+ ├── name: :exec
+ ├── name_loc: (1,4)-(1,8) = "exec"
+ ├── receiver: ∅
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,13)-(1,19))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,13)-(1,19))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :system
+ │ ├── message_loc: (1,13)-(1,19) = "system"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,8)-(1,9) = "("
+ ├── rparen_loc: (1,9)-(1,10) = ")"
+ ├── equal_loc: (1,11)-(1,12) = "="
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/defn_oneliner_rescue.txt b/test/prism/snapshots/seattlerb/defn_oneliner_rescue.txt
new file mode 100644
index 0000000000..b5b5dbe6ac
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_oneliner_rescue.txt
@@ -0,0 +1,158 @@
+@ ProgramNode (location: (1,0)-(13,38))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(13,38))
+ └── body: (length: 3)
+ ├── @ DefNode (location: (1,0)-(5,3))
+ │ ├── name: :exec
+ │ ├── name_loc: (1,4)-(1,8) = "exec"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,9)-(1,12))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,9)-(1,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :cmd
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (1,0)-(5,3))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (2,2)-(2,13))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (2,2)-(2,13))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :system
+ │ │ │ ├── message_loc: (2,2)-(2,8) = "system"
+ │ │ │ ├── opening_loc: (2,8)-(2,9) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (2,9)-(2,12))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ LocalVariableReadNode (location: (2,9)-(2,12))
+ │ │ │ │ ├── name: :cmd
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── closing_loc: (2,12)-(2,13) = ")"
+ │ │ │ └── block: ∅
+ │ │ ├── rescue_clause:
+ │ │ │ @ RescueNode (location: (3,0)-(4,5))
+ │ │ │ ├── keyword_loc: (3,0)-(3,6) = "rescue"
+ │ │ │ ├── exceptions: (length: 0)
+ │ │ │ ├── operator_loc: ∅
+ │ │ │ ├── reference: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (4,2)-(4,5))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ NilNode (location: (4,2)-(4,5))
+ │ │ │ └── consequent: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause: ∅
+ │ │ └── end_keyword_loc: (5,0)-(5,3) = "end"
+ │ ├── locals: [:cmd]
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (1,8)-(1,9) = "("
+ │ ├── rparen_loc: (1,12)-(1,13) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (5,0)-(5,3) = "end"
+ ├── @ DefNode (location: (8,0)-(10,3))
+ │ ├── name: :exec
+ │ ├── name_loc: (8,4)-(8,8) = "exec"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (8,9)-(8,12))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (8,9)-(8,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :cmd
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (9,2)-(9,24))
+ │ │ └── body: (length: 1)
+ │ │ └── @ RescueModifierNode (location: (9,2)-(9,24))
+ │ │ ├── expression:
+ │ │ │ @ CallNode (location: (9,2)-(9,13))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :system
+ │ │ │ ├── message_loc: (9,2)-(9,8) = "system"
+ │ │ │ ├── opening_loc: (9,8)-(9,9) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (9,9)-(9,12))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ LocalVariableReadNode (location: (9,9)-(9,12))
+ │ │ │ │ ├── name: :cmd
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── closing_loc: (9,12)-(9,13) = ")"
+ │ │ │ └── block: ∅
+ │ │ ├── keyword_loc: (9,14)-(9,20) = "rescue"
+ │ │ └── rescue_expression:
+ │ │ @ NilNode (location: (9,21)-(9,24))
+ │ ├── locals: [:cmd]
+ │ ├── def_keyword_loc: (8,0)-(8,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (8,8)-(8,9) = "("
+ │ ├── rparen_loc: (8,12)-(8,13) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (10,0)-(10,3) = "end"
+ └── @ DefNode (location: (13,0)-(13,38))
+ ├── name: :exec
+ ├── name_loc: (13,4)-(13,8) = "exec"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (13,9)-(13,12))
+ │ ├── requireds: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (13,9)-(13,12))
+ │ │ ├── flags: ∅
+ │ │ └── name: :cmd
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (13,16)-(13,38))
+ │ └── body: (length: 1)
+ │ └── @ RescueModifierNode (location: (13,16)-(13,38))
+ │ ├── expression:
+ │ │ @ CallNode (location: (13,16)-(13,27))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :system
+ │ │ ├── message_loc: (13,16)-(13,22) = "system"
+ │ │ ├── opening_loc: (13,22)-(13,23) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (13,23)-(13,26))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ LocalVariableReadNode (location: (13,23)-(13,26))
+ │ │ │ ├── name: :cmd
+ │ │ │ └── depth: 0
+ │ │ ├── closing_loc: (13,26)-(13,27) = ")"
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (13,28)-(13,34) = "rescue"
+ │ └── rescue_expression:
+ │ @ NilNode (location: (13,35)-(13,38))
+ ├── locals: [:cmd]
+ ├── def_keyword_loc: (13,0)-(13,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (13,8)-(13,9) = "("
+ ├── rparen_loc: (13,12)-(13,13) = ")"
+ ├── equal_loc: (13,14)-(13,15) = "="
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/defn_opt_last_arg.txt b/test/prism/snapshots/seattlerb/defn_opt_last_arg.txt
new file mode 100644
index 0000000000..569bc23078
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_opt_last_arg.txt
@@ -0,0 +1,33 @@
+@ ProgramNode (location: (1,0)-(2,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,3))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(2,3))
+ ├── name: :m
+ ├── name_loc: (1,4)-(1,5) = "m"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,17))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 1)
+ │ │ └── @ OptionalParameterNode (location: (1,6)-(1,17))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :arg
+ │ │ ├── name_loc: (1,6)-(1,9) = "arg"
+ │ │ ├── operator_loc: (1,10)-(1,11) = "="
+ │ │ └── value:
+ │ │ @ FalseNode (location: (1,12)-(1,17))
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:arg]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (2,0)-(2,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/defn_opt_reg.txt b/test/prism/snapshots/seattlerb/defn_opt_reg.txt
new file mode 100644
index 0000000000..f86168513a
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_opt_reg.txt
@@ -0,0 +1,36 @@
+@ ProgramNode (location: (1,0)-(1,19))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,19))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,19))
+ ├── name: :f
+ ├── name_loc: (1,4)-(1,5) = "f"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,14))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 1)
+ │ │ └── @ OptionalParameterNode (location: (1,6)-(1,11))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :a
+ │ │ ├── name_loc: (1,6)-(1,7) = "a"
+ │ │ ├── operator_loc: (1,7)-(1,8) = "="
+ │ │ └── value:
+ │ │ @ NilNode (location: (1,8)-(1,11))
+ │ ├── rest: ∅
+ │ ├── posts: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (1,13)-(1,14))
+ │ │ ├── flags: ∅
+ │ │ └── name: :b
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:a, :b]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,5)-(1,6) = "("
+ ├── rparen_loc: (1,14)-(1,15) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,16)-(1,19) = "end"
diff --git a/test/prism/snapshots/seattlerb/defn_opt_splat_arg.txt b/test/prism/snapshots/seattlerb/defn_opt_splat_arg.txt
new file mode 100644
index 0000000000..3019e9b73e
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_opt_splat_arg.txt
@@ -0,0 +1,43 @@
+@ ProgramNode (location: (1,0)-(1,24))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,24))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,24))
+ ├── name: :f
+ ├── name_loc: (1,4)-(1,5) = "f"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,7)-(1,19))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 1)
+ │ │ └── @ OptionalParameterNode (location: (1,7)-(1,12))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :a
+ │ │ ├── name_loc: (1,7)-(1,8) = "a"
+ │ │ ├── operator_loc: (1,9)-(1,10) = "="
+ │ │ └── value:
+ │ │ @ IntegerNode (location: (1,11)-(1,12))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── rest:
+ │ │ @ RestParameterNode (location: (1,14)-(1,16))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (1,15)-(1,16) = "b"
+ │ │ └── operator_loc: (1,14)-(1,15) = "*"
+ │ ├── posts: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (1,18)-(1,19))
+ │ │ ├── flags: ∅
+ │ │ └── name: :c
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:a, :b, :c]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,6)-(1,7) = "("
+ ├── rparen_loc: (1,19)-(1,20) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,21)-(1,24) = "end"
diff --git a/test/prism/snapshots/seattlerb/defn_powarg.txt b/test/prism/snapshots/seattlerb/defn_powarg.txt
new file mode 100644
index 0000000000..bce131ad18
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_powarg.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (1,0)-(1,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,17))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,17))
+ ├── name: :f
+ ├── name_loc: (1,4)-(1,5) = "f"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,12))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest:
+ │ │ @ KeywordRestParameterNode (location: (1,6)-(1,12))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :opts
+ │ │ ├── name_loc: (1,8)-(1,12) = "opts"
+ │ │ └── operator_loc: (1,6)-(1,8) = "**"
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:opts]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,5)-(1,6) = "("
+ ├── rparen_loc: (1,12)-(1,13) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,14)-(1,17) = "end"
diff --git a/test/prism/snapshots/seattlerb/defn_reg_opt_reg.txt b/test/prism/snapshots/seattlerb/defn_reg_opt_reg.txt
new file mode 100644
index 0000000000..d079e1b5f3
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_reg_opt_reg.txt
@@ -0,0 +1,44 @@
+@ ProgramNode (location: (1,0)-(1,23))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,23))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,23))
+ ├── name: :f
+ ├── name_loc: (1,4)-(1,5) = "f"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,18))
+ │ ├── requireds: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (1,6)-(1,7))
+ │ │ ├── flags: ∅
+ │ │ └── name: :a
+ │ ├── optionals: (length: 1)
+ │ │ └── @ OptionalParameterNode (location: (1,9)-(1,15))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (1,9)-(1,10) = "b"
+ │ │ ├── operator_loc: (1,11)-(1,12) = "="
+ │ │ └── value:
+ │ │ @ SymbolNode (location: (1,13)-(1,15))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,13)-(1,14) = ":"
+ │ │ ├── value_loc: (1,14)-(1,15) = "c"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "c"
+ │ ├── rest: ∅
+ │ ├── posts: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (1,17)-(1,18))
+ │ │ ├── flags: ∅
+ │ │ └── name: :d
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:a, :b, :d]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,5)-(1,6) = "("
+ ├── rparen_loc: (1,18)-(1,19) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,20)-(1,23) = "end"
diff --git a/test/prism/snapshots/seattlerb/defn_splat_arg.txt b/test/prism/snapshots/seattlerb/defn_splat_arg.txt
new file mode 100644
index 0000000000..109fac495a
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_splat_arg.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,15))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,15))
+ ├── name: :f
+ ├── name_loc: (1,4)-(1,5) = "f"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,10))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest:
+ │ │ @ RestParameterNode (location: (1,6)-(1,7))
+ │ │ ├── flags: ∅
+ │ │ ├── name: ∅
+ │ │ ├── name_loc: ∅
+ │ │ └── operator_loc: (1,6)-(1,7) = "*"
+ │ ├── posts: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (1,9)-(1,10))
+ │ │ ├── flags: ∅
+ │ │ └── name: :a
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:a]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,5)-(1,6) = "("
+ ├── rparen_loc: (1,10)-(1,11) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,12)-(1,15) = "end"
diff --git a/test/prism/snapshots/seattlerb/defn_unary_not.txt b/test/prism/snapshots/seattlerb/defn_unary_not.txt
new file mode 100644
index 0000000000..231a3c0da9
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defn_unary_not.txt
@@ -0,0 +1,21 @@
+@ ProgramNode (location: (1,0)-(1,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,17))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,17))
+ ├── name: :!
+ ├── name_loc: (1,4)-(1,6) = "!@"
+ ├── receiver: ∅
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,8)-(1,12))
+ │ └── body: (length: 1)
+ │ └── @ TrueNode (location: (1,8)-(1,12))
+ ├── locals: []
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,14)-(1,17) = "end"
diff --git a/test/prism/snapshots/seattlerb/defns_reserved.txt b/test/prism/snapshots/seattlerb/defns_reserved.txt
new file mode 100644
index 0000000000..96860b49ce
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defns_reserved.txt
@@ -0,0 +1,19 @@
+@ ProgramNode (location: (1,0)-(1,20))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,20))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,20))
+ ├── name: :return
+ ├── name_loc: (1,9)-(1,15) = "return"
+ ├── receiver:
+ │ @ SelfNode (location: (1,4)-(1,8))
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: (1,8)-(1,9) = "."
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,17)-(1,20) = "end"
diff --git a/test/prism/snapshots/seattlerb/defs_as_arg_with_do_block_inside.txt b/test/prism/snapshots/seattlerb/defs_as_arg_with_do_block_inside.txt
new file mode 100644
index 0000000000..24bb14f8e1
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defs_as_arg_with_do_block_inside.txt
@@ -0,0 +1,60 @@
+@ ProgramNode (location: (1,0)-(1,30))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,30))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,30))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :p
+ ├── message_loc: (1,0)-(1,1) = "p"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,30))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ DefNode (location: (1,2)-(1,30))
+ │ ├── name: :b
+ │ ├── name_loc: (1,11)-(1,12) = "b"
+ │ ├── receiver:
+ │ │ @ SelfNode (location: (1,6)-(1,10))
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,14)-(1,25))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,14)-(1,25))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (1,14)-(1,15))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :x
+ │ │ │ ├── message_loc: (1,14)-(1,15) = "x"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (1,15)-(1,16) = "."
+ │ │ ├── name: :y
+ │ │ ├── message_loc: (1,16)-(1,17) = "y"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (1,18)-(1,25))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (1,18)-(1,20) = "do"
+ │ │ └── closing_loc: (1,22)-(1,25) = "end"
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (1,2)-(1,5) = "def"
+ │ ├── operator_loc: (1,10)-(1,11) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (1,27)-(1,30) = "end"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/defs_comments.txt b/test/prism/snapshots/seattlerb/defs_comments.txt
new file mode 100644
index 0000000000..a2976e7ee2
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defs_comments.txt
@@ -0,0 +1,19 @@
+@ ProgramNode (location: (4,0)-(5,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (4,0)-(5,3))
+ └── body: (length: 1)
+ └── @ DefNode (location: (4,0)-(5,3))
+ ├── name: :blah
+ ├── name_loc: (4,9)-(4,13) = "blah"
+ ├── receiver:
+ │ @ SelfNode (location: (4,4)-(4,8))
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (4,0)-(4,3) = "def"
+ ├── operator_loc: (4,8)-(4,9) = "."
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (5,0)-(5,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/defs_endless_command.txt b/test/prism/snapshots/seattlerb/defs_endless_command.txt
new file mode 100644
index 0000000000..f3c4e79417
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defs_endless_command.txt
@@ -0,0 +1,46 @@
+@ ProgramNode (location: (1,0)-(1,35))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,35))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,35))
+ ├── name: :some_method
+ ├── name_loc: (1,6)-(1,17) = "some_method"
+ ├── receiver:
+ │ @ CallNode (location: (1,4)-(1,5))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :x
+ │ ├── message_loc: (1,4)-(1,5) = "x"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,20)-(1,35))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,20)-(1,35))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :other_method
+ │ ├── message_loc: (1,20)-(1,32) = "other_method"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,33)-(1,35))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,33)-(1,35))
+ │ │ ├── flags: decimal
+ │ │ └── value: 42
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: (1,5)-(1,6) = "."
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: (1,18)-(1,19) = "="
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/defs_endless_command_rescue.txt b/test/prism/snapshots/seattlerb/defs_endless_command_rescue.txt
new file mode 100644
index 0000000000..b0cd34a9c8
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defs_endless_command_rescue.txt
@@ -0,0 +1,53 @@
+@ ProgramNode (location: (1,0)-(1,45))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,45))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,45))
+ ├── name: :some_method
+ ├── name_loc: (1,6)-(1,17) = "some_method"
+ ├── receiver:
+ │ @ CallNode (location: (1,4)-(1,5))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :x
+ │ ├── message_loc: (1,4)-(1,5) = "x"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,20)-(1,45))
+ │ └── body: (length: 1)
+ │ └── @ RescueModifierNode (location: (1,20)-(1,45))
+ │ ├── expression:
+ │ │ @ CallNode (location: (1,20)-(1,35))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :other_method
+ │ │ ├── message_loc: (1,20)-(1,32) = "other_method"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (1,33)-(1,35))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (1,33)-(1,35))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (1,36)-(1,42) = "rescue"
+ │ └── rescue_expression:
+ │ @ IntegerNode (location: (1,43)-(1,45))
+ │ ├── flags: decimal
+ │ └── value: 24
+ ├── locals: []
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: (1,5)-(1,6) = "."
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: (1,18)-(1,19) = "="
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/defs_kwarg.txt b/test/prism/snapshots/seattlerb/defs_kwarg.txt
new file mode 100644
index 0000000000..53235c9bb8
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defs_kwarg.txt
@@ -0,0 +1,35 @@
+@ ProgramNode (location: (1,0)-(2,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,3))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(2,3))
+ ├── name: :a
+ ├── name_loc: (1,9)-(1,10) = "a"
+ ├── receiver:
+ │ @ SelfNode (location: (1,4)-(1,8))
+ ├── parameters:
+ │ @ ParametersNode (location: (1,11)-(1,15))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 1)
+ │ │ └── @ OptionalKeywordParameterNode (location: (1,11)-(1,15))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (1,11)-(1,13) = "b:"
+ │ │ └── value:
+ │ │ @ IntegerNode (location: (1,14)-(1,15))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:b]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: (1,8)-(1,9) = "."
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (2,0)-(2,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/defs_oneliner.txt b/test/prism/snapshots/seattlerb/defs_oneliner.txt
new file mode 100644
index 0000000000..d32975354d
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defs_oneliner.txt
@@ -0,0 +1,48 @@
+@ ProgramNode (location: (1,0)-(1,32))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,32))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,32))
+ ├── name: :exec
+ ├── name_loc: (1,9)-(1,13) = "exec"
+ ├── receiver:
+ │ @ SelfNode (location: (1,4)-(1,8))
+ ├── parameters:
+ │ @ ParametersNode (location: (1,14)-(1,17))
+ │ ├── requireds: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (1,14)-(1,17))
+ │ │ ├── flags: ∅
+ │ │ └── name: :cmd
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,21)-(1,32))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,21)-(1,32))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :system
+ │ ├── message_loc: (1,21)-(1,27) = "system"
+ │ ├── opening_loc: (1,27)-(1,28) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,28)-(1,31))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (1,28)-(1,31))
+ │ │ ├── name: :cmd
+ │ │ └── depth: 0
+ │ ├── closing_loc: (1,31)-(1,32) = ")"
+ │ └── block: ∅
+ ├── locals: [:cmd]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: (1,8)-(1,9) = "."
+ ├── lparen_loc: (1,13)-(1,14) = "("
+ ├── rparen_loc: (1,17)-(1,18) = ")"
+ ├── equal_loc: (1,19)-(1,20) = "="
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/defs_oneliner_eq2.txt b/test/prism/snapshots/seattlerb/defs_oneliner_eq2.txt
new file mode 100644
index 0000000000..fcc5c63cf0
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defs_oneliner_eq2.txt
@@ -0,0 +1,48 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 1)
+ └── @ ClassNode (location: (1,0)-(3,3))
+ ├── locals: []
+ ├── class_keyword_loc: (1,0)-(1,5) = "class"
+ ├── constant_path:
+ │ @ ConstantReadNode (location: (1,6)-(1,7))
+ │ └── name: :X
+ ├── inheritance_operator_loc: ∅
+ ├── superclass: ∅
+ ├── body:
+ │ @ StatementsNode (location: (2,2)-(2,21))
+ │ └── body: (length: 1)
+ │ └── @ DefNode (location: (2,2)-(2,21))
+ │ ├── name: :==
+ │ ├── name_loc: (2,11)-(2,13) = "=="
+ │ ├── receiver:
+ │ │ @ SelfNode (location: (2,6)-(2,10))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (2,14)-(2,15))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (2,14)-(2,15))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :o
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (2,19)-(2,21))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (2,19)-(2,21))
+ │ │ ├── flags: decimal
+ │ │ └── value: 42
+ │ ├── locals: [:o]
+ │ ├── def_keyword_loc: (2,2)-(2,5) = "def"
+ │ ├── operator_loc: (2,10)-(2,11) = "."
+ │ ├── lparen_loc: (2,13)-(2,14) = "("
+ │ ├── rparen_loc: (2,15)-(2,16) = ")"
+ │ ├── equal_loc: (2,17)-(2,18) = "="
+ │ └── end_keyword_loc: ∅
+ ├── end_keyword_loc: (3,0)-(3,3) = "end"
+ └── name: :X
diff --git a/test/prism/snapshots/seattlerb/defs_oneliner_rescue.txt b/test/prism/snapshots/seattlerb/defs_oneliner_rescue.txt
new file mode 100644
index 0000000000..f776210768
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/defs_oneliner_rescue.txt
@@ -0,0 +1,161 @@
+@ ProgramNode (location: (1,0)-(13,43))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(13,43))
+ └── body: (length: 3)
+ ├── @ DefNode (location: (1,0)-(5,3))
+ │ ├── name: :exec
+ │ ├── name_loc: (1,9)-(1,13) = "exec"
+ │ ├── receiver:
+ │ │ @ SelfNode (location: (1,4)-(1,8))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,14)-(1,17))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,14)-(1,17))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :cmd
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (1,0)-(5,3))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (2,2)-(2,13))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (2,2)-(2,13))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :system
+ │ │ │ ├── message_loc: (2,2)-(2,8) = "system"
+ │ │ │ ├── opening_loc: (2,8)-(2,9) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (2,9)-(2,12))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ LocalVariableReadNode (location: (2,9)-(2,12))
+ │ │ │ │ ├── name: :cmd
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── closing_loc: (2,12)-(2,13) = ")"
+ │ │ │ └── block: ∅
+ │ │ ├── rescue_clause:
+ │ │ │ @ RescueNode (location: (3,0)-(4,5))
+ │ │ │ ├── keyword_loc: (3,0)-(3,6) = "rescue"
+ │ │ │ ├── exceptions: (length: 0)
+ │ │ │ ├── operator_loc: ∅
+ │ │ │ ├── reference: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (4,2)-(4,5))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ NilNode (location: (4,2)-(4,5))
+ │ │ │ └── consequent: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause: ∅
+ │ │ └── end_keyword_loc: (5,0)-(5,3) = "end"
+ │ ├── locals: [:cmd]
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: (1,8)-(1,9) = "."
+ │ ├── lparen_loc: (1,13)-(1,14) = "("
+ │ ├── rparen_loc: (1,17)-(1,18) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (5,0)-(5,3) = "end"
+ ├── @ DefNode (location: (8,0)-(10,3))
+ │ ├── name: :exec
+ │ ├── name_loc: (8,9)-(8,13) = "exec"
+ │ ├── receiver:
+ │ │ @ SelfNode (location: (8,4)-(8,8))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (8,14)-(8,17))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (8,14)-(8,17))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :cmd
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (9,2)-(9,24))
+ │ │ └── body: (length: 1)
+ │ │ └── @ RescueModifierNode (location: (9,2)-(9,24))
+ │ │ ├── expression:
+ │ │ │ @ CallNode (location: (9,2)-(9,13))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :system
+ │ │ │ ├── message_loc: (9,2)-(9,8) = "system"
+ │ │ │ ├── opening_loc: (9,8)-(9,9) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (9,9)-(9,12))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ LocalVariableReadNode (location: (9,9)-(9,12))
+ │ │ │ │ ├── name: :cmd
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── closing_loc: (9,12)-(9,13) = ")"
+ │ │ │ └── block: ∅
+ │ │ ├── keyword_loc: (9,14)-(9,20) = "rescue"
+ │ │ └── rescue_expression:
+ │ │ @ NilNode (location: (9,21)-(9,24))
+ │ ├── locals: [:cmd]
+ │ ├── def_keyword_loc: (8,0)-(8,3) = "def"
+ │ ├── operator_loc: (8,8)-(8,9) = "."
+ │ ├── lparen_loc: (8,13)-(8,14) = "("
+ │ ├── rparen_loc: (8,17)-(8,18) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (10,0)-(10,3) = "end"
+ └── @ DefNode (location: (13,0)-(13,43))
+ ├── name: :exec
+ ├── name_loc: (13,9)-(13,13) = "exec"
+ ├── receiver:
+ │ @ SelfNode (location: (13,4)-(13,8))
+ ├── parameters:
+ │ @ ParametersNode (location: (13,14)-(13,17))
+ │ ├── requireds: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (13,14)-(13,17))
+ │ │ ├── flags: ∅
+ │ │ └── name: :cmd
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (13,21)-(13,43))
+ │ └── body: (length: 1)
+ │ └── @ RescueModifierNode (location: (13,21)-(13,43))
+ │ ├── expression:
+ │ │ @ CallNode (location: (13,21)-(13,32))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :system
+ │ │ ├── message_loc: (13,21)-(13,27) = "system"
+ │ │ ├── opening_loc: (13,27)-(13,28) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (13,28)-(13,31))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ LocalVariableReadNode (location: (13,28)-(13,31))
+ │ │ │ ├── name: :cmd
+ │ │ │ └── depth: 0
+ │ │ ├── closing_loc: (13,31)-(13,32) = ")"
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (13,33)-(13,39) = "rescue"
+ │ └── rescue_expression:
+ │ @ NilNode (location: (13,40)-(13,43))
+ ├── locals: [:cmd]
+ ├── def_keyword_loc: (13,0)-(13,3) = "def"
+ ├── operator_loc: (13,8)-(13,9) = "."
+ ├── lparen_loc: (13,13)-(13,14) = "("
+ ├── rparen_loc: (13,17)-(13,18) = ")"
+ ├── equal_loc: (13,19)-(13,20) = "="
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/difficult0_.txt b/test/prism/snapshots/seattlerb/difficult0_.txt
new file mode 100644
index 0000000000..8ba30ccf85
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/difficult0_.txt
@@ -0,0 +1,73 @@
+@ ProgramNode (location: (1,0)-(4,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,8))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(4,8))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :p
+ ├── message_loc: (1,0)-(1,1) = "p"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(4,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (1,2)-(4,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,2)-(4,4))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ StringNode (location: (1,2)-(1,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (1,2)-(1,8) = "<<-END"
+ │ │ │ ├── content_loc: (2,0)-(3,0) = " a\n"
+ │ │ │ ├── closing_loc: (3,0)-(4,0) = " END\n"
+ │ │ │ └── unescaped: " a\n"
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :+
+ │ │ ├── message_loc: (1,8)-(1,9) = "+"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (1,9)-(4,4))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ InterpolatedStringNode (location: (1,9)-(4,4))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (1,9)-(1,10) = "'"
+ │ │ │ ├── parts: (length: 2)
+ │ │ │ │ ├── @ StringNode (location: (1,10)-(2,0))
+ │ │ │ │ │ ├── flags: frozen
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── content_loc: (1,10)-(2,0) = "b\n"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "b\n"
+ │ │ │ │ └── @ StringNode (location: (4,0)-(4,3))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (4,0)-(4,3) = " c"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: " c"
+ │ │ │ └── closing_loc: (4,3)-(4,4) = "'"
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (4,4)-(4,5) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (4,5)-(4,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (4,5)-(4,8))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (4,5)-(4,6) = "'"
+ │ │ ├── content_loc: (4,6)-(4,7) = "d"
+ │ │ ├── closing_loc: (4,7)-(4,8) = "'"
+ │ │ └── unescaped: "d"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/difficult1_line_numbers.txt b/test/prism/snapshots/seattlerb/difficult1_line_numbers.txt
new file mode 100644
index 0000000000..da2306312c
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/difficult1_line_numbers.txt
@@ -0,0 +1,267 @@
+@ ProgramNode (location: (1,0)-(12,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(12,3))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(12,3))
+ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ ├── predicate:
+ │ @ TrueNode (location: (1,3)-(1,7))
+ ├── then_keyword_loc: ∅
+ ├── statements:
+ │ @ StatementsNode (location: (2,2)-(11,11))
+ │ └── body: (length: 10)
+ │ ├── @ CallNode (location: (2,2)-(2,5))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :p
+ │ │ ├── message_loc: (2,2)-(2,3) = "p"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (2,4)-(2,5))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (2,4)-(2,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── @ CallNode (location: (3,2)-(3,7))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (3,2)-(3,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (3,2)-(3,3) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (3,3)-(3,4) = "."
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (3,4)-(3,5) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (3,6)-(3,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (3,6)-(3,7))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── @ CallNode (location: (4,2)-(4,10))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (4,2)-(4,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── message_loc: (4,2)-(4,3) = "c"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (4,3)-(4,4) = "."
+ │ │ ├── name: :d
+ │ │ ├── message_loc: (4,4)-(4,5) = "d"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (4,6)-(4,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 2)
+ │ │ │ ├── @ IntegerNode (location: (4,6)-(4,7))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 3
+ │ │ │ └── @ IntegerNode (location: (4,9)-(4,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 4
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── @ CallNode (location: (5,2)-(5,7))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (5,2)-(5,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :e
+ │ │ │ ├── message_loc: (5,2)-(5,3) = "e"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (5,3)-(5,4) = "."
+ │ │ ├── name: :f
+ │ │ ├── message_loc: (5,4)-(5,5) = "f"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (5,6)-(5,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (5,6)-(5,7))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 5
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── @ CallNode (location: (6,2)-(6,10))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (6,2)-(6,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :g
+ │ │ │ ├── message_loc: (6,2)-(6,3) = "g"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (6,3)-(6,4) = "."
+ │ │ ├── name: :h
+ │ │ ├── message_loc: (6,4)-(6,5) = "h"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (6,6)-(6,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 2)
+ │ │ │ ├── @ IntegerNode (location: (6,6)-(6,7))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 6
+ │ │ │ └── @ IntegerNode (location: (6,9)-(6,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 7
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── @ CallNode (location: (7,2)-(7,6))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :p
+ │ │ ├── message_loc: (7,2)-(7,3) = "p"
+ │ │ ├── opening_loc: (7,3)-(7,4) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (7,4)-(7,5))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (7,4)-(7,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: (7,5)-(7,6) = ")"
+ │ │ └── block: ∅
+ │ ├── @ CallNode (location: (8,2)-(8,8))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (8,2)-(8,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (8,2)-(8,3) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (8,3)-(8,4) = "."
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (8,4)-(8,5) = "b"
+ │ │ ├── opening_loc: (8,5)-(8,6) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (8,6)-(8,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (8,6)-(8,7))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── closing_loc: (8,7)-(8,8) = ")"
+ │ │ └── block: ∅
+ │ ├── @ CallNode (location: (9,2)-(9,11))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (9,2)-(9,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── message_loc: (9,2)-(9,3) = "c"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (9,3)-(9,4) = "."
+ │ │ ├── name: :d
+ │ │ ├── message_loc: (9,4)-(9,5) = "d"
+ │ │ ├── opening_loc: (9,5)-(9,6) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (9,6)-(9,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 2)
+ │ │ │ ├── @ IntegerNode (location: (9,6)-(9,7))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 3
+ │ │ │ └── @ IntegerNode (location: (9,9)-(9,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 4
+ │ │ ├── closing_loc: (9,10)-(9,11) = ")"
+ │ │ └── block: ∅
+ │ ├── @ CallNode (location: (10,2)-(10,8))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (10,2)-(10,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :e
+ │ │ │ ├── message_loc: (10,2)-(10,3) = "e"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (10,3)-(10,4) = "."
+ │ │ ├── name: :f
+ │ │ ├── message_loc: (10,4)-(10,5) = "f"
+ │ │ ├── opening_loc: (10,5)-(10,6) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (10,6)-(10,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (10,6)-(10,7))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 5
+ │ │ ├── closing_loc: (10,7)-(10,8) = ")"
+ │ │ └── block: ∅
+ │ └── @ CallNode (location: (11,2)-(11,11))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (11,2)-(11,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :g
+ │ │ ├── message_loc: (11,2)-(11,3) = "g"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (11,3)-(11,4) = "."
+ │ ├── name: :h
+ │ ├── message_loc: (11,4)-(11,5) = "h"
+ │ ├── opening_loc: (11,5)-(11,6) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (11,6)-(11,10))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ IntegerNode (location: (11,6)-(11,7))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 6
+ │ │ └── @ IntegerNode (location: (11,9)-(11,10))
+ │ │ ├── flags: decimal
+ │ │ └── value: 7
+ │ ├── closing_loc: (11,10)-(11,11) = ")"
+ │ └── block: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (12,0)-(12,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/difficult1_line_numbers2.txt b/test/prism/snapshots/seattlerb/difficult1_line_numbers2.txt
new file mode 100644
index 0000000000..f586634c59
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/difficult1_line_numbers2.txt
@@ -0,0 +1,78 @@
+@ ProgramNode (location: (1,0)-(7,1))
+├── locals: [:b, :c]
+└── statements:
+ @ StatementsNode (location: (1,0)-(7,1))
+ └── body: (length: 2)
+ ├── @ IfNode (location: (1,0)-(6,3))
+ │ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (1,3)-(1,7))
+ │ ├── then_keyword_loc: (1,8)-(1,12) = "then"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (2,2)-(5,6))
+ │ │ └── body: (length: 4)
+ │ │ ├── @ CallNode (location: (2,2)-(2,8))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :p
+ │ │ │ ├── message_loc: (2,2)-(2,3) = "p"
+ │ │ │ ├── opening_loc: (2,3)-(2,4) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (2,4)-(2,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ StringNode (location: (2,4)-(2,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: (2,4)-(2,5) = "\""
+ │ │ │ │ ├── content_loc: (2,5)-(2,6) = "a"
+ │ │ │ │ ├── closing_loc: (2,6)-(2,7) = "\""
+ │ │ │ │ └── unescaped: "a"
+ │ │ │ ├── closing_loc: (2,7)-(2,8) = ")"
+ │ │ │ └── block: ∅
+ │ │ ├── @ LocalVariableWriteNode (location: (3,2)-(3,7))
+ │ │ │ ├── name: :b
+ │ │ │ ├── depth: 0
+ │ │ │ ├── name_loc: (3,2)-(3,3) = "b"
+ │ │ │ ├── value:
+ │ │ │ │ @ IntegerNode (location: (3,6)-(3,7))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── operator_loc: (3,4)-(3,5) = "="
+ │ │ ├── @ CallNode (location: (4,2)-(4,5))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :p
+ │ │ │ ├── message_loc: (4,2)-(4,3) = "p"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (4,4)-(4,5))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ LocalVariableReadNode (location: (4,4)-(4,5))
+ │ │ │ │ ├── name: :b
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ LocalVariableWriteNode (location: (5,2)-(5,6))
+ │ │ ├── name: :c
+ │ │ ├── depth: 0
+ │ │ ├── name_loc: (5,2)-(5,3) = "c"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (5,5)-(5,6))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (5,4)-(5,5) = "="
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (6,0)-(6,3) = "end"
+ └── @ CallNode (location: (7,0)-(7,1))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (7,0)-(7,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/difficult2_.txt b/test/prism/snapshots/seattlerb/difficult2_.txt
new file mode 100644
index 0000000000..a9b3736fe3
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/difficult2_.txt
@@ -0,0 +1,74 @@
+@ ProgramNode (location: (1,0)-(2,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,6))
+ └── body: (length: 2)
+ ├── @ IfNode (location: (1,0)-(1,13))
+ │ ├── if_keyword_loc: ∅
+ │ ├── predicate:
+ │ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── then_keyword_loc: (1,2)-(1,3) = "?"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,4)-(1,9))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,4)-(1,9))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (1,4)-(1,5) = "b"
+ │ │ ├── opening_loc: (1,5)-(1,6) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (1,6)-(1,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ StringNode (location: (1,6)-(1,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (1,6)-(1,7) = "'"
+ │ │ │ ├── content_loc: (1,7)-(1,7) = ""
+ │ │ │ ├── closing_loc: (1,7)-(1,8) = "'"
+ │ │ │ └── unescaped: ""
+ │ │ ├── closing_loc: (1,8)-(1,9) = ")"
+ │ │ └── block: ∅
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (1,10)-(1,13))
+ │ │ ├── else_keyword_loc: (1,10)-(1,11) = ":"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,12)-(1,13))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (1,12)-(1,13))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── end_keyword_loc: ∅
+ │ └── end_keyword_loc: ∅
+ └── @ CallNode (location: (2,0)-(2,6))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (2,0)-(2,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (2,2)-(2,6))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ KeywordHashNode (location: (2,2)-(2,6))
+ │ ├── flags: symbol_keys
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (2,2)-(2,6))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (2,2)-(2,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (2,2)-(2,3) = "d"
+ │ │ ├── closing_loc: (2,3)-(2,4) = ":"
+ │ │ └── unescaped: "d"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (2,5)-(2,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ └── operator_loc: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/difficult3_.txt b/test/prism/snapshots/seattlerb/difficult3_.txt
new file mode 100644
index 0000000000..f074c49a9f
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/difficult3_.txt
@@ -0,0 +1,52 @@
+@ ProgramNode (location: (1,0)-(1,18))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,18))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,18))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,18))
+ ├── locals: [:a, :b, :c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,16))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,15))
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ └── @ MultiTargetNode (location: (1,8)-(1,15))
+ │ │ │ ├── lefts: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (1,9)-(1,10))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :b
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (1,12)-(1,14))
+ │ │ │ │ ├── operator_loc: (1,12)-(1,13) = "*"
+ │ │ │ │ └── expression:
+ │ │ │ │ @ RequiredParameterNode (location: (1,13)-(1,14))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :c
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: (1,8)-(1,9) = "("
+ │ │ │ └── rparen_loc: (1,14)-(1,15) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,15)-(1,16) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,17)-(1,18) = "}"
diff --git a/test/prism/snapshots/seattlerb/difficult3_2.txt b/test/prism/snapshots/seattlerb/difficult3_2.txt
new file mode 100644
index 0000000000..af1a649171
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/difficult3_2.txt
@@ -0,0 +1,42 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,13))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,13))
+ ├── locals: [:a, :b]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,11))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,10))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (1,5)-(1,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── name_loc: (1,6)-(1,7) = "a"
+ │ │ │ └── operator_loc: (1,5)-(1,6) = "*"
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,9)-(1,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :b
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,10)-(1,11) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,12)-(1,13) = "}"
diff --git a/test/prism/snapshots/seattlerb/difficult3_3.txt b/test/prism/snapshots/seattlerb/difficult3_3.txt
new file mode 100644
index 0000000000..e49bbcd55a
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/difficult3_3.txt
@@ -0,0 +1,47 @@
+@ ProgramNode (location: (1,0)-(1,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,17))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,17))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,17))
+ ├── locals: [:a, :b, :c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,15))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,14))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (1,5)-(1,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── name_loc: (1,6)-(1,7) = "a"
+ │ │ │ └── operator_loc: (1,5)-(1,6) = "*"
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,9)-(1,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :b
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (1,12)-(1,14))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :c
+ │ │ ├── name_loc: (1,13)-(1,14) = "c"
+ │ │ └── operator_loc: (1,12)-(1,13) = "&"
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,14)-(1,15) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,16)-(1,17) = "}"
diff --git a/test/prism/snapshots/seattlerb/difficult3_4.txt b/test/prism/snapshots/seattlerb/difficult3_4.txt
new file mode 100644
index 0000000000..73afffb4cb
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/difficult3_4.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(1,17))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,17))
+ └── body: (length: 1)
+ └── @ LocalVariableWriteNode (location: (1,0)-(1,17))
+ ├── name: :a
+ ├── depth: 0
+ ├── name_loc: (1,0)-(1,1) = "a"
+ ├── value:
+ │ @ IfNode (location: (1,2)-(1,17))
+ │ ├── if_keyword_loc: ∅
+ │ ├── predicate:
+ │ │ @ CallNode (location: (1,2)-(1,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (1,2)-(1,3) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: (1,4)-(1,5) = "?"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,6)-(1,10))
+ │ │ └── body: (length: 1)
+ │ │ └── @ TrueNode (location: (1,6)-(1,10))
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (1,10)-(1,17))
+ │ │ ├── else_keyword_loc: (1,10)-(1,11) = ":"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,12)-(1,17))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ FalseNode (location: (1,12)-(1,17))
+ │ │ └── end_keyword_loc: ∅
+ │ └── end_keyword_loc: ∅
+ └── operator_loc: (1,1)-(1,2) = "="
diff --git a/test/prism/snapshots/seattlerb/difficult3_5.txt b/test/prism/snapshots/seattlerb/difficult3_5.txt
new file mode 100644
index 0000000000..793c3f1e11
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/difficult3_5.txt
@@ -0,0 +1,48 @@
+@ ProgramNode (location: (1,0)-(1,19))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,19))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,19))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,19))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ LambdaNode (location: (1,2)-(1,19))
+ │ ├── locals: []
+ │ ├── operator_loc: (1,2)-(1,4) = "->"
+ │ ├── opening_loc: (1,7)-(1,8) = "{"
+ │ ├── closing_loc: (1,18)-(1,19) = "}"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (1,4)-(1,6))
+ │ │ ├── parameters: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (1,4)-(1,5) = "("
+ │ │ └── closing_loc: (1,5)-(1,6) = ")"
+ │ └── body:
+ │ @ StatementsNode (location: (1,9)-(1,17))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,9)-(1,17))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :g
+ │ ├── message_loc: (1,9)-(1,10) = "g"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,11)-(1,17))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,11)-(1,13) = "do"
+ │ └── closing_loc: (1,14)-(1,17) = "end"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/difficult3__10.txt b/test/prism/snapshots/seattlerb/difficult3__10.txt
new file mode 100644
index 0000000000..0131e44d44
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/difficult3__10.txt
@@ -0,0 +1,52 @@
+@ ProgramNode (location: (1,0)-(1,18))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,18))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,18))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,18))
+ ├── locals: [:a, :b, :c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,16))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,15))
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ └── @ MultiTargetNode (location: (1,8)-(1,15))
+ │ │ │ ├── lefts: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (1,9)-(1,11))
+ │ │ │ │ ├── operator_loc: (1,9)-(1,10) = "*"
+ │ │ │ │ └── expression:
+ │ │ │ │ @ RequiredParameterNode (location: (1,10)-(1,11))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :b
+ │ │ │ ├── rights: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (1,13)-(1,14))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :c
+ │ │ │ ├── lparen_loc: (1,8)-(1,9) = "("
+ │ │ │ └── rparen_loc: (1,14)-(1,15) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,15)-(1,16) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,17)-(1,18) = "}"
diff --git a/test/prism/snapshots/seattlerb/difficult3__11.txt b/test/prism/snapshots/seattlerb/difficult3__11.txt
new file mode 100644
index 0000000000..a658b091c2
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/difficult3__11.txt
@@ -0,0 +1,46 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,14))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,14))
+ ├── locals: [:a]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,12))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,11))
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ └── @ MultiTargetNode (location: (1,8)-(1,11))
+ │ │ │ ├── lefts: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (1,9)-(1,10))
+ │ │ │ │ ├── operator_loc: (1,9)-(1,10) = "*"
+ │ │ │ │ └── expression: ∅
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: (1,8)-(1,9) = "("
+ │ │ │ └── rparen_loc: (1,10)-(1,11) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,11)-(1,12) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,13)-(1,14) = "}"
diff --git a/test/prism/snapshots/seattlerb/difficult3__12.txt b/test/prism/snapshots/seattlerb/difficult3__12.txt
new file mode 100644
index 0000000000..5aa252fe6a
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/difficult3__12.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(1,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,17))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,17))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,17))
+ ├── locals: [:a, :b]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,15))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,14))
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ └── @ MultiTargetNode (location: (1,8)-(1,14))
+ │ │ │ ├── lefts: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (1,9)-(1,10))
+ │ │ │ │ ├── operator_loc: (1,9)-(1,10) = "*"
+ │ │ │ │ └── expression: ∅
+ │ │ │ ├── rights: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (1,12)-(1,13))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :b
+ │ │ │ ├── lparen_loc: (1,8)-(1,9) = "("
+ │ │ │ └── rparen_loc: (1,13)-(1,14) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,14)-(1,15) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,16)-(1,17) = "}"
diff --git a/test/prism/snapshots/seattlerb/difficult3__6.txt b/test/prism/snapshots/seattlerb/difficult3__6.txt
new file mode 100644
index 0000000000..a42a625be7
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/difficult3__6.txt
@@ -0,0 +1,55 @@
+@ ProgramNode (location: (1,0)-(1,21))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,21))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,21))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,21))
+ ├── locals: [:a, :b, :c, :d]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,19))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,18))
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ └── @ MultiTargetNode (location: (1,8)-(1,18))
+ │ │ │ ├── lefts: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (1,9)-(1,10))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :b
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (1,12)-(1,14))
+ │ │ │ │ ├── operator_loc: (1,12)-(1,13) = "*"
+ │ │ │ │ └── expression:
+ │ │ │ │ @ RequiredParameterNode (location: (1,13)-(1,14))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :c
+ │ │ │ ├── rights: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (1,16)-(1,17))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :d
+ │ │ │ ├── lparen_loc: (1,8)-(1,9) = "("
+ │ │ │ └── rparen_loc: (1,17)-(1,18) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,18)-(1,19) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,20)-(1,21) = "}"
diff --git a/test/prism/snapshots/seattlerb/difficult3__7.txt b/test/prism/snapshots/seattlerb/difficult3__7.txt
new file mode 100644
index 0000000000..b08025804c
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/difficult3__7.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(1,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,17))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,17))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,17))
+ ├── locals: [:a, :b]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,15))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,14))
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ └── @ MultiTargetNode (location: (1,8)-(1,14))
+ │ │ │ ├── lefts: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (1,9)-(1,10))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :b
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (1,12)-(1,13))
+ │ │ │ │ ├── operator_loc: (1,12)-(1,13) = "*"
+ │ │ │ │ └── expression: ∅
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: (1,8)-(1,9) = "("
+ │ │ │ └── rparen_loc: (1,13)-(1,14) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,14)-(1,15) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,16)-(1,17) = "}"
diff --git a/test/prism/snapshots/seattlerb/difficult3__8.txt b/test/prism/snapshots/seattlerb/difficult3__8.txt
new file mode 100644
index 0000000000..b2b118faef
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/difficult3__8.txt
@@ -0,0 +1,52 @@
+@ ProgramNode (location: (1,0)-(1,20))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,20))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,20))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,20))
+ ├── locals: [:a, :b, :c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,18))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,17))
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ └── @ MultiTargetNode (location: (1,8)-(1,17))
+ │ │ │ ├── lefts: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (1,9)-(1,10))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :b
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (1,12)-(1,13))
+ │ │ │ │ ├── operator_loc: (1,12)-(1,13) = "*"
+ │ │ │ │ └── expression: ∅
+ │ │ │ ├── rights: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (1,15)-(1,16))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :c
+ │ │ │ ├── lparen_loc: (1,8)-(1,9) = "("
+ │ │ │ └── rparen_loc: (1,16)-(1,17) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,17)-(1,18) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,19)-(1,20) = "}"
diff --git a/test/prism/snapshots/seattlerb/difficult3__9.txt b/test/prism/snapshots/seattlerb/difficult3__9.txt
new file mode 100644
index 0000000000..85c10a4432
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/difficult3__9.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(1,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,15))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,15))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,15))
+ ├── locals: [:a, :b]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,13))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,12))
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ └── @ MultiTargetNode (location: (1,8)-(1,12))
+ │ │ │ ├── lefts: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (1,9)-(1,11))
+ │ │ │ │ ├── operator_loc: (1,9)-(1,10) = "*"
+ │ │ │ │ └── expression:
+ │ │ │ │ @ RequiredParameterNode (location: (1,10)-(1,11))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :b
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: (1,8)-(1,9) = "("
+ │ │ │ └── rparen_loc: (1,11)-(1,12) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,12)-(1,13) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,14)-(1,15) = "}"
diff --git a/test/prism/snapshots/seattlerb/difficult4__leading_dots.txt b/test/prism/snapshots/seattlerb/difficult4__leading_dots.txt
new file mode 100644
index 0000000000..8307c806e6
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/difficult4__leading_dots.txt
@@ -0,0 +1,25 @@
+@ ProgramNode (location: (1,0)-(2,2))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,2))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(2,2))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (2,0)-(2,1) = "."
+ ├── name: :b
+ ├── message_loc: (2,1)-(2,2) = "b"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/difficult4__leading_dots2.txt b/test/prism/snapshots/seattlerb/difficult4__leading_dots2.txt
new file mode 100644
index 0000000000..ee4370c0f0
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/difficult4__leading_dots2.txt
@@ -0,0 +1,16 @@
+@ ProgramNode (location: (1,0)-(2,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,3))
+ └── body: (length: 2)
+ ├── @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ └── @ RangeNode (location: (2,0)-(2,3))
+ ├── flags: ∅
+ ├── left: ∅
+ ├── right:
+ │ @ IntegerNode (location: (2,2)-(2,3))
+ │ ├── flags: decimal
+ │ └── value: 3
+ └── operator_loc: (2,0)-(2,2) = ".."
diff --git a/test/prism/snapshots/seattlerb/difficult6_.txt b/test/prism/snapshots/seattlerb/difficult6_.txt
new file mode 100644
index 0000000000..bf80034fe9
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/difficult6_.txt
@@ -0,0 +1,61 @@
+@ ProgramNode (location: (1,0)-(1,25))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,25))
+ └── body: (length: 1)
+ └── @ LambdaNode (location: (1,0)-(1,25))
+ ├── locals: [:a, :b]
+ ├── operator_loc: (1,0)-(1,2) = "->"
+ ├── opening_loc: (1,13)-(1,14) = "{"
+ ├── closing_loc: (1,24)-(1,25) = "}"
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,2)-(1,12))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,3)-(1,11))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,3)-(1,4))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,6)-(1,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (1,6)-(1,7) = "b"
+ │ │ │ ├── operator_loc: (1,7)-(1,8) = "="
+ │ │ │ └── value:
+ │ │ │ @ NilNode (location: (1,8)-(1,11))
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,2)-(1,3) = "("
+ │ └── closing_loc: (1,11)-(1,12) = ")"
+ └── body:
+ @ StatementsNode (location: (1,15)-(1,23))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,15)-(1,23))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :p
+ ├── message_loc: (1,15)-(1,16) = "p"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,17)-(1,23))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ ArrayNode (location: (1,17)-(1,23))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ LocalVariableReadNode (location: (1,18)-(1,19))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ └── @ LocalVariableReadNode (location: (1,21)-(1,22))
+ │ │ ├── name: :b
+ │ │ └── depth: 0
+ │ ├── opening_loc: (1,17)-(1,18) = "["
+ │ └── closing_loc: (1,22)-(1,23) = "]"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/difficult6__7.txt b/test/prism/snapshots/seattlerb/difficult6__7.txt
new file mode 100644
index 0000000000..7fe70c7033
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/difficult6__7.txt
@@ -0,0 +1,55 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,11))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,1)-(1,2) = "."
+ ├── name: :b
+ ├── message_loc: (1,2)-(1,3) = "b"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,7))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ ParenthesesNode (location: (1,4)-(1,7))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,5)-(1,6))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,5)-(1,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── opening_loc: (1,4)-(1,5) = "("
+ │ └── closing_loc: (1,6)-(1,7) = ")"
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,8)-(1,11))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,9)-(1,10))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,9)-(1,10))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :c
+ │ ├── message_loc: (1,9)-(1,10) = "c"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── opening_loc: (1,8)-(1,9) = "{"
+ └── closing_loc: (1,10)-(1,11) = "}"
diff --git a/test/prism/snapshots/seattlerb/difficult6__8.txt b/test/prism/snapshots/seattlerb/difficult6__8.txt
new file mode 100644
index 0000000000..7f915e283c
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/difficult6__8.txt
@@ -0,0 +1,55 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,12))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,1)-(1,3) = "::"
+ ├── name: :b
+ ├── message_loc: (1,3)-(1,4) = "b"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,5)-(1,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ ParenthesesNode (location: (1,5)-(1,8))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,6)-(1,7))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,6)-(1,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── opening_loc: (1,5)-(1,6) = "("
+ │ └── closing_loc: (1,7)-(1,8) = ")"
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,9)-(1,12))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,10)-(1,11))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,10)-(1,11))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :c
+ │ ├── message_loc: (1,10)-(1,11) = "c"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── opening_loc: (1,9)-(1,10) = "{"
+ └── closing_loc: (1,11)-(1,12) = "}"
diff --git a/test/prism/snapshots/seattlerb/difficult7_.txt b/test/prism/snapshots/seattlerb/difficult7_.txt
new file mode 100644
index 0000000000..40c778cf6c
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/difficult7_.txt
@@ -0,0 +1,93 @@
+@ ProgramNode (location: (1,6)-(4,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,6)-(4,7))
+ └── body: (length: 1)
+ └── @ HashNode (location: (1,6)-(4,7))
+ ├── opening_loc: (1,6)-(1,7) = "{"
+ ├── elements: (length: 2)
+ │ ├── @ AssocNode (location: (2,8)-(2,33))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (2,8)-(2,10))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (2,8)-(2,9) = "a"
+ │ │ │ ├── closing_loc: (2,9)-(2,10) = ":"
+ │ │ │ └── unescaped: "a"
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (2,11)-(2,33))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :lambda
+ │ │ │ ├── message_loc: (2,11)-(2,17) = "lambda"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (2,18)-(2,33))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (2,20)-(2,31))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IfNode (location: (2,20)-(2,31))
+ │ │ │ │ ├── if_keyword_loc: ∅
+ │ │ │ │ ├── predicate:
+ │ │ │ │ │ @ CallNode (location: (2,20)-(2,21))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :b
+ │ │ │ │ │ ├── message_loc: (2,20)-(2,21) = "b"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── then_keyword_loc: (2,22)-(2,23) = "?"
+ │ │ │ │ ├── statements:
+ │ │ │ │ │ @ StatementsNode (location: (2,24)-(2,27))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (2,24)-(2,27))
+ │ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :c
+ │ │ │ │ │ ├── message_loc: (2,24)-(2,25) = "c"
+ │ │ │ │ │ ├── opening_loc: (2,25)-(2,26) = "("
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: (2,26)-(2,27) = ")"
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── consequent:
+ │ │ │ │ │ @ ElseNode (location: (2,28)-(2,31))
+ │ │ │ │ │ ├── else_keyword_loc: (2,28)-(2,29) = ":"
+ │ │ │ │ │ ├── statements:
+ │ │ │ │ │ │ @ StatementsNode (location: (2,30)-(2,31))
+ │ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ │ └── @ CallNode (location: (2,30)-(2,31))
+ │ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ │ ├── message_loc: (2,30)-(2,31) = "d"
+ │ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ │ └── block: ∅
+ │ │ │ │ │ └── end_keyword_loc: ∅
+ │ │ │ │ └── end_keyword_loc: ∅
+ │ │ │ ├── opening_loc: (2,18)-(2,19) = "{"
+ │ │ │ └── closing_loc: (2,32)-(2,33) = "}"
+ │ │ └── operator_loc: ∅
+ │ └── @ AssocNode (location: (3,8)-(3,14))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (3,8)-(3,10))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (3,8)-(3,9) = "e"
+ │ │ ├── closing_loc: (3,9)-(3,10) = ":"
+ │ │ └── unescaped: "e"
+ │ ├── value:
+ │ │ @ NilNode (location: (3,11)-(3,14))
+ │ └── operator_loc: ∅
+ └── closing_loc: (4,6)-(4,7) = "}"
diff --git a/test/prism/snapshots/seattlerb/do_bug.txt b/test/prism/snapshots/seattlerb/do_bug.txt
new file mode 100644
index 0000000000..5877b18d68
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/do_bug.txt
@@ -0,0 +1,63 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,2)-(1,3))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,2)-(1,3))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (2,0)-(4,3))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (2,0)-(2,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (2,0)-(2,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (2,1)-(2,2) = "."
+ ├── name: :b
+ ├── message_loc: (2,2)-(2,3) = "b"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (2,4)-(4,3))
+ ├── locals: [:c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (2,7)-(2,10))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (2,8)-(2,9))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (2,8)-(2,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :c
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (2,7)-(2,8) = "|"
+ │ └── closing_loc: (2,9)-(2,10) = "|"
+ ├── body: ∅
+ ├── opening_loc: (2,4)-(2,6) = "do"
+ └── closing_loc: (4,0)-(4,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/do_lambda.txt b/test/prism/snapshots/seattlerb/do_lambda.txt
new file mode 100644
index 0000000000..4713fb3e4b
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/do_lambda.txt
@@ -0,0 +1,17 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ LambdaNode (location: (1,0)-(1,11))
+ ├── locals: []
+ ├── operator_loc: (1,0)-(1,2) = "->"
+ ├── opening_loc: (1,5)-(1,7) = "do"
+ ├── closing_loc: (1,8)-(1,11) = "end"
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,2)-(1,4))
+ │ ├── parameters: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,2)-(1,3) = "("
+ │ └── closing_loc: (1,3)-(1,4) = ")"
+ └── body: ∅
diff --git a/test/prism/snapshots/seattlerb/dot2_nil__26.txt b/test/prism/snapshots/seattlerb/dot2_nil__26.txt
new file mode 100644
index 0000000000..104515ac3a
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/dot2_nil__26.txt
@@ -0,0 +1,20 @@
+@ ProgramNode (location: (1,0)-(1,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,3))
+ └── body: (length: 1)
+ └── @ RangeNode (location: (1,0)-(1,3))
+ ├── flags: ∅
+ ├── left:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── right: ∅
+ └── operator_loc: (1,1)-(1,3) = ".."
diff --git a/test/prism/snapshots/seattlerb/dot3_nil__26.txt b/test/prism/snapshots/seattlerb/dot3_nil__26.txt
new file mode 100644
index 0000000000..ec7f57cd96
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/dot3_nil__26.txt
@@ -0,0 +1,20 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ RangeNode (location: (1,0)-(1,4))
+ ├── flags: exclude_end
+ ├── left:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── right: ∅
+ └── operator_loc: (1,1)-(1,4) = "..."
diff --git a/test/prism/snapshots/seattlerb/dstr_evstr.txt b/test/prism/snapshots/seattlerb/dstr_evstr.txt
new file mode 100644
index 0000000000..8d771e88c2
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/dstr_evstr.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(1,12))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,1) = "\""
+ ├── parts: (length: 2)
+ │ ├── @ EmbeddedStatementsNode (location: (1,1)-(1,7))
+ │ │ ├── opening_loc: (1,1)-(1,3) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,3)-(1,6))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ StringNode (location: (1,3)-(1,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (1,3)-(1,4) = "'"
+ │ │ │ ├── content_loc: (1,4)-(1,5) = "a"
+ │ │ │ ├── closing_loc: (1,5)-(1,6) = "'"
+ │ │ │ └── unescaped: "a"
+ │ │ └── closing_loc: (1,6)-(1,7) = "}"
+ │ └── @ EmbeddedStatementsNode (location: (1,7)-(1,11))
+ │ ├── opening_loc: (1,7)-(1,9) = "\#{"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,9)-(1,10))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,9)-(1,10))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (1,9)-(1,10) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── closing_loc: (1,10)-(1,11) = "}"
+ └── closing_loc: (1,11)-(1,12) = "\""
diff --git a/test/prism/snapshots/seattlerb/dstr_evstr_empty_end.txt b/test/prism/snapshots/seattlerb/dstr_evstr_empty_end.txt
new file mode 100644
index 0000000000..53f97e4b36
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/dstr_evstr_empty_end.txt
@@ -0,0 +1,25 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ InterpolatedSymbolNode (location: (1,0)-(1,11))
+ ├── opening_loc: (1,0)-(1,2) = ":\""
+ ├── parts: (length: 1)
+ │ └── @ EmbeddedStatementsNode (location: (1,2)-(1,10))
+ │ ├── opening_loc: (1,2)-(1,4) = "\#{"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,4)-(1,9))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,4)-(1,9))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :field
+ │ │ ├── message_loc: (1,4)-(1,9) = "field"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── closing_loc: (1,9)-(1,10) = "}"
+ └── closing_loc: (1,10)-(1,11) = "\""
diff --git a/test/prism/snapshots/seattlerb/dstr_lex_state.txt b/test/prism/snapshots/seattlerb/dstr_lex_state.txt
new file mode 100644
index 0000000000..c4c0ef0437
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/dstr_lex_state.txt
@@ -0,0 +1,35 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(1,8))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,1) = "\""
+ ├── parts: (length: 1)
+ │ └── @ EmbeddedStatementsNode (location: (1,1)-(1,7))
+ │ ├── opening_loc: (1,1)-(1,3) = "\#{"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,3)-(1,6))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,3)-(1,6))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :p
+ │ │ ├── message_loc: (1,3)-(1,4) = "p"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (1,4)-(1,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (1,4)-(1,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (1,4)-(1,5) = ":"
+ │ │ │ ├── value_loc: (1,5)-(1,6) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── closing_loc: (1,6)-(1,7) = "}"
+ └── closing_loc: (1,7)-(1,8) = "\""
diff --git a/test/prism/snapshots/seattlerb/dstr_str.txt b/test/prism/snapshots/seattlerb/dstr_str.txt
new file mode 100644
index 0000000000..70b5752ce3
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/dstr_str.txt
@@ -0,0 +1,28 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(1,10))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,1) = "\""
+ ├── parts: (length: 2)
+ │ ├── @ EmbeddedStatementsNode (location: (1,1)-(1,7))
+ │ │ ├── opening_loc: (1,1)-(1,3) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,3)-(1,6))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ StringNode (location: (1,3)-(1,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (1,3)-(1,4) = "'"
+ │ │ │ ├── content_loc: (1,4)-(1,5) = "a"
+ │ │ │ ├── closing_loc: (1,5)-(1,6) = "'"
+ │ │ │ └── unescaped: "a"
+ │ │ └── closing_loc: (1,6)-(1,7) = "}"
+ │ └── @ StringNode (location: (1,7)-(1,9))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (1,7)-(1,9) = " b"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: " b"
+ └── closing_loc: (1,9)-(1,10) = "\""
diff --git a/test/prism/snapshots/seattlerb/dsym_esc_to_sym.txt b/test/prism/snapshots/seattlerb/dsym_esc_to_sym.txt
new file mode 100644
index 0000000000..7b1b68131e
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/dsym_esc_to_sym.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,17))
+ └── body: (length: 1)
+ └── @ SymbolNode (location: (1,0)-(1,17))
+ ├── flags: forced_utf8_encoding
+ ├── opening_loc: (1,0)-(1,2) = ":\""
+ ├── value_loc: (1,2)-(1,16) = "Variet\\303\\240"
+ ├── closing_loc: (1,16)-(1,17) = "\""
+ └── unescaped: "Varietà"
diff --git a/test/prism/snapshots/seattlerb/dsym_to_sym.txt b/test/prism/snapshots/seattlerb/dsym_to_sym.txt
new file mode 100644
index 0000000000..eb7e435c63
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/dsym_to_sym.txt
@@ -0,0 +1,37 @@
+@ ProgramNode (location: (1,0)-(3,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,13))
+ └── body: (length: 2)
+ ├── @ AliasMethodNode (location: (1,0)-(1,17))
+ │ ├── new_name:
+ │ │ @ SymbolNode (location: (1,6)-(1,11))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,6)-(1,8) = ":\""
+ │ │ ├── value_loc: (1,8)-(1,10) = "<<"
+ │ │ ├── closing_loc: (1,10)-(1,11) = "\""
+ │ │ └── unescaped: "<<"
+ │ ├── old_name:
+ │ │ @ SymbolNode (location: (1,12)-(1,17))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,12)-(1,14) = ":\""
+ │ │ ├── value_loc: (1,14)-(1,16) = ">>"
+ │ │ ├── closing_loc: (1,16)-(1,17) = "\""
+ │ │ └── unescaped: ">>"
+ │ └── keyword_loc: (1,0)-(1,5) = "alias"
+ └── @ AliasMethodNode (location: (3,0)-(3,13))
+ ├── new_name:
+ │ @ SymbolNode (location: (3,6)-(3,9))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (3,6)-(3,7) = ":"
+ │ ├── value_loc: (3,7)-(3,9) = "<<"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "<<"
+ ├── old_name:
+ │ @ SymbolNode (location: (3,10)-(3,13))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (3,10)-(3,11) = ":"
+ │ ├── value_loc: (3,11)-(3,13) = ">>"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: ">>"
+ └── keyword_loc: (3,0)-(3,5) = "alias"
diff --git a/test/prism/snapshots/seattlerb/eq_begin_line_numbers.txt b/test/prism/snapshots/seattlerb/eq_begin_line_numbers.txt
new file mode 100644
index 0000000000..a5fc3951d6
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/eq_begin_line_numbers.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(6,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(6,1))
+ └── body: (length: 2)
+ ├── @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ └── @ IntegerNode (location: (6,0)-(6,1))
+ ├── flags: decimal
+ └── value: 2
diff --git a/test/prism/snapshots/seattlerb/eq_begin_why_wont_people_use_their_spacebar.txt b/test/prism/snapshots/seattlerb/eq_begin_why_wont_people_use_their_spacebar.txt
new file mode 100644
index 0000000000..2103bde8cb
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/eq_begin_why_wont_people_use_their_spacebar.txt
@@ -0,0 +1,50 @@
+@ ProgramNode (location: (1,0)-(3,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,8))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(3,8))
+ ├── flags: attribute_write
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :h
+ │ ├── message_loc: (1,0)-(1,1) = "h"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :[]=
+ ├── message_loc: (1,1)-(1,4) = "[k]"
+ ├── opening_loc: (1,1)-(1,2) = "["
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(3,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ CallNode (location: (1,2)-(1,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :k
+ │ │ ├── message_loc: (1,2)-(1,3) = "k"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── @ BeginNode (location: (1,5)-(3,8))
+ │ ├── begin_keyword_loc: (1,5)-(1,10) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (2,7)-(2,9))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (2,7)-(2,9))
+ │ │ ├── flags: decimal
+ │ │ └── value: 42
+ │ ├── rescue_clause: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (3,5)-(3,8) = "end"
+ ├── closing_loc: (1,3)-(1,4) = "]"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/evstr_evstr.txt b/test/prism/snapshots/seattlerb/evstr_evstr.txt
new file mode 100644
index 0000000000..9c801299f8
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/evstr_evstr.txt
@@ -0,0 +1,42 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(1,10))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,1) = "\""
+ ├── parts: (length: 2)
+ │ ├── @ EmbeddedStatementsNode (location: (1,1)-(1,5))
+ │ │ ├── opening_loc: (1,1)-(1,3) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,3)-(1,4))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (1,3)-(1,4))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (1,3)-(1,4) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── closing_loc: (1,4)-(1,5) = "}"
+ │ └── @ EmbeddedStatementsNode (location: (1,5)-(1,9))
+ │ ├── opening_loc: (1,5)-(1,7) = "\#{"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,7)-(1,8))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,7)-(1,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (1,7)-(1,8) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── closing_loc: (1,8)-(1,9) = "}"
+ └── closing_loc: (1,9)-(1,10) = "\""
diff --git a/test/prism/snapshots/seattlerb/evstr_str.txt b/test/prism/snapshots/seattlerb/evstr_str.txt
new file mode 100644
index 0000000000..54319e613c
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/evstr_str.txt
@@ -0,0 +1,32 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(1,8))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,1) = "\""
+ ├── parts: (length: 2)
+ │ ├── @ EmbeddedStatementsNode (location: (1,1)-(1,5))
+ │ │ ├── opening_loc: (1,1)-(1,3) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,3)-(1,4))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (1,3)-(1,4))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (1,3)-(1,4) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── closing_loc: (1,4)-(1,5) = "}"
+ │ └── @ StringNode (location: (1,5)-(1,7))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (1,5)-(1,7) = " b"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: " b"
+ └── closing_loc: (1,7)-(1,8) = "\""
diff --git a/test/prism/snapshots/seattlerb/expr_not_bang.txt b/test/prism/snapshots/seattlerb/expr_not_bang.txt
new file mode 100644
index 0000000000..0a289ab7be
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/expr_not_bang.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,5))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,2)-(1,5))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,2)-(1,3) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,4)-(1,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (1,4)-(1,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (1,4)-(1,5) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :!
+ ├── message_loc: (1,0)-(1,1) = "!"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/f_kw.txt b/test/prism/snapshots/seattlerb/f_kw.txt
new file mode 100644
index 0000000000..4226137925
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/f_kw.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,15))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,15))
+ ├── name: :x
+ ├── name_loc: (1,4)-(1,5) = "x"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,10))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 1)
+ │ │ └── @ OptionalKeywordParameterNode (location: (1,6)-(1,10))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :k
+ │ │ ├── name_loc: (1,6)-(1,8) = "k:"
+ │ │ └── value:
+ │ │ @ IntegerNode (location: (1,8)-(1,10))
+ │ │ ├── flags: decimal
+ │ │ └── value: 42
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:k]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,12)-(1,15) = "end"
diff --git a/test/prism/snapshots/seattlerb/f_kw__required.txt b/test/prism/snapshots/seattlerb/f_kw__required.txt
new file mode 100644
index 0000000000..f72f43e034
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/f_kw__required.txt
@@ -0,0 +1,30 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,13))
+ ├── name: :x
+ ├── name_loc: (1,4)-(1,5) = "x"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,8))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 1)
+ │ │ └── @ RequiredKeywordParameterNode (location: (1,6)-(1,8))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :k
+ │ │ └── name_loc: (1,6)-(1,8) = "k:"
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:k]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,10)-(1,13) = "end"
diff --git a/test/prism/snapshots/seattlerb/flip2_env_lvar.txt b/test/prism/snapshots/seattlerb/flip2_env_lvar.txt
new file mode 100644
index 0000000000..5a71ab6cda
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/flip2_env_lvar.txt
@@ -0,0 +1,37 @@
+@ ProgramNode (location: (1,0)-(1,16))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,16))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(1,16))
+ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ ├── predicate:
+ │ @ FlipFlopNode (location: (1,3)-(1,7))
+ │ ├── flags: ∅
+ │ ├── left:
+ │ │ @ CallNode (location: (1,3)-(1,4))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (1,3)-(1,4) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right:
+ │ │ @ CallNode (location: (1,6)-(1,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (1,6)-(1,7) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (1,4)-(1,6) = ".."
+ ├── then_keyword_loc: (1,8)-(1,12) = "then"
+ ├── statements: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (1,13)-(1,16) = "end"
diff --git a/test/prism/snapshots/seattlerb/float_with_if_modifier.txt b/test/prism/snapshots/seattlerb/float_with_if_modifier.txt
new file mode 100644
index 0000000000..9c1da70f24
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/float_with_if_modifier.txt
@@ -0,0 +1,17 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(1,10))
+ ├── if_keyword_loc: (1,3)-(1,5) = "if"
+ ├── predicate:
+ │ @ TrueNode (location: (1,6)-(1,10))
+ ├── then_keyword_loc: ∅
+ ├── statements:
+ │ @ StatementsNode (location: (1,0)-(1,3))
+ │ └── body: (length: 1)
+ │ └── @ FloatNode (location: (1,0)-(1,3))
+ │ └── value: 1.0
+ ├── consequent: ∅
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/heredoc__backslash_dos_format.txt b/test/prism/snapshots/seattlerb/heredoc__backslash_dos_format.txt
new file mode 100644
index 0000000000..353e4c6964
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc__backslash_dos_format.txt
@@ -0,0 +1,17 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: [:str]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ LocalVariableWriteNode (location: (1,0)-(1,12))
+ ├── name: :str
+ ├── depth: 0
+ ├── name_loc: (1,0)-(1,3) = "str"
+ ├── value:
+ │ @ StringNode (location: (1,6)-(1,12))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,6)-(1,12) = "<<-XXX"
+ │ ├── content_loc: (2,0)-(4,0) = "before\\\r\nafter\r\n"
+ │ ├── closing_loc: (4,0)-(5,0) = "XXX\r\n"
+ │ └── unescaped: "beforeafter\n"
+ └── operator_loc: (1,4)-(1,5) = "="
diff --git a/test/prism/snapshots/seattlerb/heredoc_backslash_nl.txt b/test/prism/snapshots/seattlerb/heredoc_backslash_nl.txt
new file mode 100644
index 0000000000..fc4c1784fe
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_backslash_nl.txt
@@ -0,0 +1,17 @@
+@ ProgramNode (location: (1,0)-(5,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,7))
+ └── body: (length: 2)
+ ├── @ StringNode (location: (1,0)-(3,1))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,1) = "\""
+ │ ├── content_loc: (1,1)-(3,0) = " why would someone do this? \\\n blah\n"
+ │ ├── closing_loc: (3,0)-(3,1) = "\""
+ │ └── unescaped: " why would someone do this? blah\n"
+ └── @ StringNode (location: (5,0)-(5,7))
+ ├── flags: ∅
+ ├── opening_loc: (5,0)-(5,7) = "<<-DESC"
+ ├── content_loc: (6,0)-(8,0) = " why would someone do this? \\\n blah\n"
+ ├── closing_loc: (8,0)-(9,0) = "DESC\n"
+ └── unescaped: " why would someone do this? blah\n"
diff --git a/test/prism/snapshots/seattlerb/heredoc_bad_hex_escape.txt b/test/prism/snapshots/seattlerb/heredoc_bad_hex_escape.txt
new file mode 100644
index 0000000000..2b1d776404
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_bad_hex_escape.txt
@@ -0,0 +1,17 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: [:s]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ LocalVariableWriteNode (location: (1,0)-(1,9))
+ ├── name: :s
+ ├── depth: 0
+ ├── name_loc: (1,0)-(1,1) = "s"
+ ├── value:
+ │ @ StringNode (location: (1,4)-(1,9))
+ │ ├── flags: forced_utf8_encoding
+ │ ├── opening_loc: (1,4)-(1,9) = "<<eos"
+ │ ├── content_loc: (2,0)-(3,0) = "a\\xE9b\n"
+ │ ├── closing_loc: (3,0)-(4,0) = "eos\n"
+ │ └── unescaped: "a\xE9b\n"
+ └── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/seattlerb/heredoc_bad_oct_escape.txt b/test/prism/snapshots/seattlerb/heredoc_bad_oct_escape.txt
new file mode 100644
index 0000000000..7a01f8d6d1
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_bad_oct_escape.txt
@@ -0,0 +1,17 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: [:s]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ LocalVariableWriteNode (location: (1,0)-(1,10))
+ ├── name: :s
+ ├── depth: 0
+ ├── name_loc: (1,0)-(1,1) = "s"
+ ├── value:
+ │ @ StringNode (location: (1,4)-(1,10))
+ │ ├── flags: forced_utf8_encoding
+ │ ├── opening_loc: (1,4)-(1,10) = "<<-EOS"
+ │ ├── content_loc: (2,0)-(4,0) = "a\\247b\ncöd\n"
+ │ ├── closing_loc: (4,0)-(5,0) = "EOS\n"
+ │ └── unescaped: "a\xA7b\ncöd\n"
+ └── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/seattlerb/heredoc_comma_arg.txt b/test/prism/snapshots/seattlerb/heredoc_comma_arg.txt
new file mode 100644
index 0000000000..888ebc809a
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_comma_arg.txt
@@ -0,0 +1,27 @@
+@ ProgramNode (location: (1,0)-(7,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(7,1))
+ └── body: (length: 2)
+ ├── @ ArrayNode (location: (1,0)-(2,3))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ StringNode (location: (1,1)-(2,1))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,1)-(1,2) = "\""
+ │ │ ├── content_loc: (1,2)-(2,0) = " some text\n"
+ │ │ ├── closing_loc: (2,0)-(2,1) = "\""
+ │ │ └── unescaped: " some text\n"
+ │ ├── opening_loc: (1,0)-(1,1) = "["
+ │ └── closing_loc: (2,2)-(2,3) = "]"
+ └── @ ArrayNode (location: (4,0)-(7,1))
+ ├── flags: ∅
+ ├── elements: (length: 1)
+ │ └── @ StringNode (location: (4,1)-(4,8))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (4,1)-(4,8) = "<<-FILE"
+ │ ├── content_loc: (5,0)-(6,0) = " some text\n"
+ │ ├── closing_loc: (6,0)-(7,0) = "FILE\n"
+ │ └── unescaped: " some text\n"
+ ├── opening_loc: (4,0)-(4,1) = "["
+ └── closing_loc: (7,0)-(7,1) = "]"
diff --git a/test/prism/snapshots/seattlerb/heredoc_lineno.txt b/test/prism/snapshots/seattlerb/heredoc_lineno.txt
new file mode 100644
index 0000000000..a51ce71afe
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_lineno.txt
@@ -0,0 +1,26 @@
+@ ProgramNode (location: (1,0)-(7,6))
+├── locals: [:c, :d]
+└── statements:
+ @ StatementsNode (location: (1,0)-(7,6))
+ └── body: (length: 2)
+ ├── @ LocalVariableWriteNode (location: (1,0)-(1,11))
+ │ ├── name: :c
+ │ ├── depth: 0
+ │ ├── name_loc: (1,0)-(1,1) = "c"
+ │ ├── value:
+ │ │ @ StringNode (location: (1,4)-(1,11))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,4)-(1,11) = "<<'CCC'"
+ │ │ ├── content_loc: (2,0)-(5,0) = "line2\nline3\nline4\n"
+ │ │ ├── closing_loc: (5,0)-(6,0) = "CCC\n"
+ │ │ └── unescaped: "line2\nline3\nline4\n"
+ │ └── operator_loc: (1,2)-(1,3) = "="
+ └── @ LocalVariableWriteNode (location: (7,0)-(7,6))
+ ├── name: :d
+ ├── depth: 0
+ ├── name_loc: (7,0)-(7,1) = "d"
+ ├── value:
+ │ @ IntegerNode (location: (7,4)-(7,6))
+ │ ├── flags: decimal
+ │ └── value: 42
+ └── operator_loc: (7,2)-(7,3) = "="
diff --git a/test/prism/snapshots/seattlerb/heredoc_nested.txt b/test/prism/snapshots/seattlerb/heredoc_nested.txt
new file mode 100644
index 0000000000..26d533a33d
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_nested.txt
@@ -0,0 +1,42 @@
+@ ProgramNode (location: (1,0)-(7,2))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(7,2))
+ └── body: (length: 1)
+ └── @ ArrayNode (location: (1,0)-(7,2))
+ ├── flags: ∅
+ ├── elements: (length: 2)
+ │ ├── @ InterpolatedStringNode (location: (1,1)-(1,4))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,1)-(1,4) = "<<A"
+ │ │ ├── parts: (length: 3)
+ │ │ │ ├── @ EmbeddedStatementsNode (location: (2,0)-(2,6))
+ │ │ │ │ ├── opening_loc: (2,0)-(2,2) = "\#{"
+ │ │ │ │ ├── statements:
+ │ │ │ │ │ @ StatementsNode (location: (2,2)-(2,5))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ StringNode (location: (2,2)-(2,5))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── opening_loc: (2,2)-(2,5) = "<<B"
+ │ │ │ │ │ ├── content_loc: (3,0)-(4,0) = "b\n"
+ │ │ │ │ │ ├── closing_loc: (4,0)-(5,0) = "B\n"
+ │ │ │ │ │ └── unescaped: "b\n"
+ │ │ │ │ └── closing_loc: (2,5)-(2,6) = "}"
+ │ │ │ ├── @ StringNode (location: (2,6)-(3,0))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (2,6)-(3,0) = "\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "\n"
+ │ │ │ └── @ StringNode (location: (5,0)-(6,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (5,0)-(6,0) = "a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ └── closing_loc: (6,0)-(7,0) = "A\n"
+ │ └── @ IntegerNode (location: (7,0)-(7,1))
+ │ ├── flags: decimal
+ │ └── value: 0
+ ├── opening_loc: (1,0)-(1,1) = "["
+ └── closing_loc: (7,1)-(7,2) = "]"
diff --git a/test/prism/snapshots/seattlerb/heredoc_squiggly.txt b/test/prism/snapshots/seattlerb/heredoc_squiggly.txt
new file mode 100644
index 0000000000..9bab044ace
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_squiggly.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ LocalVariableWriteNode (location: (1,0)-(1,12))
+ ├── name: :a
+ ├── depth: 0
+ ├── name_loc: (1,0)-(1,1) = "a"
+ ├── value:
+ │ @ InterpolatedStringNode (location: (1,4)-(1,12))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,4)-(1,12) = "<<~\"EOF\""
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (2,0)-(3,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (2,0)-(3,0) = " x\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "x\n"
+ │ │ ├── @ StringNode (location: (3,0)-(4,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (3,0)-(4,0) = " y\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "y\n"
+ │ │ └── @ StringNode (location: (4,0)-(5,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (4,0)-(5,0) = " z\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "z\n"
+ │ └── closing_loc: (5,0)-(6,0) = " EOF\n"
+ └── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/seattlerb/heredoc_squiggly_blank_line_plus_interpolation.txt b/test/prism/snapshots/seattlerb/heredoc_squiggly_blank_line_plus_interpolation.txt
new file mode 100644
index 0000000000..e12014afa5
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_squiggly_blank_line_plus_interpolation.txt
@@ -0,0 +1,67 @@
+@ ProgramNode (location: (1,0)-(1,20))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,20))
+ └── body: (length: 1)
+ └── @ LocalVariableWriteNode (location: (1,0)-(1,20))
+ ├── name: :a
+ ├── depth: 0
+ ├── name_loc: (1,0)-(1,1) = "a"
+ ├── value:
+ │ @ CallNode (location: (1,4)-(1,20))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,4)-(1,7) = "foo"
+ │ ├── opening_loc: (1,7)-(1,8) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,8)-(1,19))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (1,8)-(1,19))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ InterpolatedStringNode (location: (1,8)-(1,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (1,8)-(1,14) = "<<~EOF"
+ │ │ │ ├── parts: (length: 3)
+ │ │ │ │ ├── @ StringNode (location: (2,0)-(3,0))
+ │ │ │ │ │ ├── flags: frozen
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── content_loc: (2,0)-(3,0) = "\n"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "\n"
+ │ │ │ │ ├── @ EmbeddedStatementsNode (location: (3,4)-(3,10))
+ │ │ │ │ │ ├── opening_loc: (3,4)-(3,6) = "\#{"
+ │ │ │ │ │ ├── statements:
+ │ │ │ │ │ │ @ StatementsNode (location: (3,6)-(3,9))
+ │ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ │ └── @ CallNode (location: (3,6)-(3,9))
+ │ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ │ ├── name: :bar
+ │ │ │ │ │ │ ├── message_loc: (3,6)-(3,9) = "bar"
+ │ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ │ └── block: ∅
+ │ │ │ │ │ └── closing_loc: (3,9)-(3,10) = "}"
+ │ │ │ │ └── @ StringNode (location: (3,10)-(4,0))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (3,10)-(4,0) = "baz\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "baz\n"
+ │ │ │ └── closing_loc: (4,0)-(5,0) = " EOF\n"
+ │ │ ├── call_operator_loc: (1,14)-(1,15) = "."
+ │ │ ├── name: :chop
+ │ │ ├── message_loc: (1,15)-(1,19) = "chop"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (1,19)-(1,20) = ")"
+ │ └── block: ∅
+ └── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/seattlerb/heredoc_squiggly_blank_lines.txt b/test/prism/snapshots/seattlerb/heredoc_squiggly_blank_lines.txt
new file mode 100644
index 0000000000..8596647e72
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_squiggly_blank_lines.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ LocalVariableWriteNode (location: (1,0)-(1,10))
+ ├── name: :a
+ ├── depth: 0
+ ├── name_loc: (1,0)-(1,1) = "a"
+ ├── value:
+ │ @ InterpolatedStringNode (location: (1,4)-(1,10))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,4)-(1,10) = "<<~EOF"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (2,0)-(3,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (2,0)-(3,0) = " x\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "x\n"
+ │ │ ├── @ StringNode (location: (3,0)-(4,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (3,0)-(4,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── @ StringNode (location: (4,0)-(5,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (4,0)-(5,0) = " z\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "z\n"
+ │ └── closing_loc: (5,0)-(6,0) = "EOF\n"
+ └── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/seattlerb/heredoc_squiggly_empty.txt b/test/prism/snapshots/seattlerb/heredoc_squiggly_empty.txt
new file mode 100644
index 0000000000..c998f19984
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_squiggly_empty.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,0)-(1,4))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,4) = "<<~A"
+ ├── content_loc: (2,0)-(2,0) = ""
+ ├── closing_loc: (2,0)-(3,0) = "A\n"
+ └── unescaped: ""
diff --git a/test/prism/snapshots/seattlerb/heredoc_squiggly_interp.txt b/test/prism/snapshots/seattlerb/heredoc_squiggly_interp.txt
new file mode 100644
index 0000000000..d34f89ff9c
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_squiggly_interp.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ LocalVariableWriteNode (location: (1,0)-(1,10))
+ ├── name: :a
+ ├── depth: 0
+ ├── name_loc: (1,0)-(1,1) = "a"
+ ├── value:
+ │ @ InterpolatedStringNode (location: (1,4)-(1,10))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,4)-(1,10) = "<<~EOF"
+ │ ├── parts: (length: 5)
+ │ │ ├── @ StringNode (location: (2,0)-(3,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (2,0)-(3,0) = " w\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: " w\n"
+ │ │ ├── @ StringNode (location: (3,0)-(3,3))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (3,0)-(3,3) = " x"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "x"
+ │ │ ├── @ EmbeddedStatementsNode (location: (3,3)-(3,8))
+ │ │ │ ├── opening_loc: (3,3)-(3,5) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (3,5)-(3,7))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (3,5)-(3,7))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 42
+ │ │ │ └── closing_loc: (3,7)-(3,8) = "}"
+ │ │ ├── @ StringNode (location: (3,8)-(4,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (3,8)-(4,0) = " y\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: " y\n"
+ │ │ └── @ StringNode (location: (4,0)-(5,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (4,0)-(5,0) = " z\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: " z\n"
+ │ └── closing_loc: (5,0)-(6,0) = " EOF\n"
+ └── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/seattlerb/heredoc_squiggly_no_indent.txt b/test/prism/snapshots/seattlerb/heredoc_squiggly_no_indent.txt
new file mode 100644
index 0000000000..7c17bc4820
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_squiggly_no_indent.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,0)-(1,4))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,4) = "<<~A"
+ ├── content_loc: (2,0)-(3,0) = "a\n"
+ ├── closing_loc: (3,0)-(4,0) = "A\n"
+ └── unescaped: "a\n"
diff --git a/test/prism/snapshots/seattlerb/heredoc_squiggly_tabs.txt b/test/prism/snapshots/seattlerb/heredoc_squiggly_tabs.txt
new file mode 100644
index 0000000000..311d7bc1b7
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_squiggly_tabs.txt
@@ -0,0 +1,28 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ LocalVariableWriteNode (location: (1,0)-(1,12))
+ ├── name: :a
+ ├── depth: 0
+ ├── name_loc: (1,0)-(1,1) = "a"
+ ├── value:
+ │ @ InterpolatedStringNode (location: (1,4)-(1,12))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,4)-(1,12) = "<<~\"EOF\""
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (2,0)-(3,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (2,0)-(3,0) = " blah blah\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "blah blah\n"
+ │ │ └── @ StringNode (location: (3,0)-(4,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (3,0)-(4,0) = "\t blah blah\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: " blah blah\n"
+ │ └── closing_loc: (4,0)-(5,0) = " EOF\n"
+ └── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/seattlerb/heredoc_squiggly_tabs_extra.txt b/test/prism/snapshots/seattlerb/heredoc_squiggly_tabs_extra.txt
new file mode 100644
index 0000000000..3150f807a2
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_squiggly_tabs_extra.txt
@@ -0,0 +1,28 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ LocalVariableWriteNode (location: (1,0)-(1,12))
+ ├── name: :a
+ ├── depth: 0
+ ├── name_loc: (1,0)-(1,1) = "a"
+ ├── value:
+ │ @ InterpolatedStringNode (location: (1,4)-(1,12))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,4)-(1,12) = "<<~\"EOF\""
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (2,0)-(3,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (2,0)-(3,0) = " blah blah\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "blah blah\n"
+ │ │ └── @ StringNode (location: (3,0)-(4,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (3,0)-(4,0) = " \tblah blah\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\tblah blah\n"
+ │ └── closing_loc: (4,0)-(5,0) = " EOF\n"
+ └── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/seattlerb/heredoc_squiggly_visually_blank_lines.txt b/test/prism/snapshots/seattlerb/heredoc_squiggly_visually_blank_lines.txt
new file mode 100644
index 0000000000..85c3d2df6d
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_squiggly_visually_blank_lines.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ LocalVariableWriteNode (location: (1,0)-(1,10))
+ ├── name: :a
+ ├── depth: 0
+ ├── name_loc: (1,0)-(1,1) = "a"
+ ├── value:
+ │ @ InterpolatedStringNode (location: (1,4)-(1,10))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,4)-(1,10) = "<<~EOF"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (2,0)-(3,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (2,0)-(3,0) = " x\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "x\n"
+ │ │ ├── @ StringNode (location: (3,0)-(4,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (3,0)-(4,0) = " \n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── @ StringNode (location: (4,0)-(5,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (4,0)-(5,0) = " z\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "z\n"
+ │ └── closing_loc: (5,0)-(6,0) = "EOF\n"
+ └── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/seattlerb/heredoc_trailing_slash_continued_call.txt b/test/prism/snapshots/seattlerb/heredoc_trailing_slash_continued_call.txt
new file mode 100644
index 0000000000..bc05047b40
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_trailing_slash_continued_call.txt
@@ -0,0 +1,21 @@
+@ ProgramNode (location: (1,0)-(4,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,6))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(4,6))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ StringNode (location: (1,0)-(1,5))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,5) = "<<END"
+ │ ├── content_loc: (2,0)-(3,0) = "blah\n"
+ │ ├── closing_loc: (3,0)-(4,0) = "END\n"
+ │ └── unescaped: "blah\n"
+ ├── call_operator_loc: (4,0)-(4,1) = "."
+ ├── name: :strip
+ ├── message_loc: (4,1)-(4,6) = "strip"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/heredoc_unicode.txt b/test/prism/snapshots/seattlerb/heredoc_unicode.txt
new file mode 100644
index 0000000000..2c72854324
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_unicode.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,0)-(1,9))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,9) = "<<OOTPÜT"
+ ├── content_loc: (2,0)-(3,0) = ".\n"
+ ├── closing_loc: (3,0)-(4,0) = "OOTPÜT\n"
+ └── unescaped: ".\n"
diff --git a/test/prism/snapshots/seattlerb/heredoc_with_carriage_return_escapes.txt b/test/prism/snapshots/seattlerb/heredoc_with_carriage_return_escapes.txt
new file mode 100644
index 0000000000..43bb8d5ea8
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_with_carriage_return_escapes.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,0)-(1,5))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,5) = "<<EOS"
+ ├── content_loc: (2,0)-(4,0) = "foo\\rbar\nbaz\\r\n"
+ ├── closing_loc: (4,0)-(5,0) = "EOS\n"
+ └── unescaped: "foo\rbar\nbaz\r\n"
diff --git a/test/prism/snapshots/seattlerb/heredoc_with_carriage_return_escapes_windows.txt b/test/prism/snapshots/seattlerb/heredoc_with_carriage_return_escapes_windows.txt
new file mode 100644
index 0000000000..2ef6763389
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_with_carriage_return_escapes_windows.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,0)-(1,5))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,5) = "<<EOS"
+ ├── content_loc: (2,0)-(4,0) = "foo\\rbar\r\nbaz\\r\r\n"
+ ├── closing_loc: (4,0)-(5,0) = "EOS\r\n"
+ └── unescaped: "foo\rbar\nbaz\r\n"
diff --git a/test/prism/snapshots/seattlerb/heredoc_with_extra_carriage_horrible_mix.txt b/test/prism/snapshots/seattlerb/heredoc_with_extra_carriage_horrible_mix.txt
new file mode 100644
index 0000000000..fbee030100
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_with_extra_carriage_horrible_mix.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,7))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,0)-(1,7))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,7) = "<<'eot'"
+ ├── content_loc: (2,0)-(3,0) = "body\r\n"
+ ├── closing_loc: (3,0)-(4,0) = "eot\n"
+ └── unescaped: "body\n"
diff --git a/test/prism/snapshots/seattlerb/heredoc_with_extra_carriage_returns.txt b/test/prism/snapshots/seattlerb/heredoc_with_extra_carriage_returns.txt
new file mode 100644
index 0000000000..b59203bc4e
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_with_extra_carriage_returns.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,0)-(1,5))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,5) = "<<EOS"
+ ├── content_loc: (2,0)-(4,0) = "foo\rbar\r\nbaz\n"
+ ├── closing_loc: (4,0)-(5,0) = "EOS\n"
+ └── unescaped: "foo\rbar\nbaz\n"
diff --git a/test/prism/snapshots/seattlerb/heredoc_with_extra_carriage_returns_windows.txt b/test/prism/snapshots/seattlerb/heredoc_with_extra_carriage_returns_windows.txt
new file mode 100644
index 0000000000..36bc4c6560
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_with_extra_carriage_returns_windows.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,0)-(1,5))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,5) = "<<EOS"
+ ├── content_loc: (2,0)-(4,0) = "foo\rbar\r\r\nbaz\r\n"
+ ├── closing_loc: (4,0)-(5,0) = "EOS\r\n"
+ └── unescaped: "foo\rbar\r\nbaz\n"
diff --git a/test/prism/snapshots/seattlerb/heredoc_with_interpolation_and_carriage_return_escapes.txt b/test/prism/snapshots/seattlerb/heredoc_with_interpolation_and_carriage_return_escapes.txt
new file mode 100644
index 0000000000..c4be1244dc
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_with_interpolation_and_carriage_return_escapes.txt
@@ -0,0 +1,27 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(1,5))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,5) = "<<EOS"
+ ├── parts: (length: 3)
+ │ ├── @ StringNode (location: (2,0)-(2,5))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (2,0)-(2,5) = "foo\\r"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo\r"
+ │ ├── @ EmbeddedVariableNode (location: (2,5)-(2,10))
+ │ │ ├── operator_loc: (2,5)-(2,6) = "#"
+ │ │ └── variable:
+ │ │ @ InstanceVariableReadNode (location: (2,6)-(2,10))
+ │ │ └── name: :@bar
+ │ └── @ StringNode (location: (2,10)-(3,0))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (2,10)-(3,0) = "\n"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "\n"
+ └── closing_loc: (3,0)-(4,0) = "EOS\n"
diff --git a/test/prism/snapshots/seattlerb/heredoc_with_interpolation_and_carriage_return_escapes_windows.txt b/test/prism/snapshots/seattlerb/heredoc_with_interpolation_and_carriage_return_escapes_windows.txt
new file mode 100644
index 0000000000..c2290cec13
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_with_interpolation_and_carriage_return_escapes_windows.txt
@@ -0,0 +1,27 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(1,5))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,5) = "<<EOS"
+ ├── parts: (length: 3)
+ │ ├── @ StringNode (location: (2,0)-(2,5))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (2,0)-(2,5) = "foo\\r"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo\r"
+ │ ├── @ EmbeddedVariableNode (location: (2,5)-(2,10))
+ │ │ ├── operator_loc: (2,5)-(2,6) = "#"
+ │ │ └── variable:
+ │ │ @ InstanceVariableReadNode (location: (2,6)-(2,10))
+ │ │ └── name: :@bar
+ │ └── @ StringNode (location: (2,10)-(3,0))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (2,10)-(3,0) = "\r\n"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "\n"
+ └── closing_loc: (3,0)-(4,0) = "EOS\r\n"
diff --git a/test/prism/snapshots/seattlerb/heredoc_with_not_global_interpolation.txt b/test/prism/snapshots/seattlerb/heredoc_with_not_global_interpolation.txt
new file mode 100644
index 0000000000..6e1648f659
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_with_not_global_interpolation.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,0)-(1,10))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,10) = "<<-HEREDOC"
+ ├── content_loc: (2,0)-(3,0) = "\#${\n"
+ ├── closing_loc: (3,0)-(4,0) = "HEREDOC\n"
+ └── unescaped: "\#${\n"
diff --git a/test/prism/snapshots/seattlerb/heredoc_with_only_carriage_returns.txt b/test/prism/snapshots/seattlerb/heredoc_with_only_carriage_returns.txt
new file mode 100644
index 0000000000..6a535c6472
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_with_only_carriage_returns.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,0)-(1,5))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,5) = "<<EOS"
+ ├── content_loc: (2,0)-(5,0) = "\r\n\r\r\n\\r\n"
+ ├── closing_loc: (5,0)-(6,0) = "EOS\n"
+ └── unescaped: "\n\r\n\r\n"
diff --git a/test/prism/snapshots/seattlerb/heredoc_with_only_carriage_returns_windows.txt b/test/prism/snapshots/seattlerb/heredoc_with_only_carriage_returns_windows.txt
new file mode 100644
index 0000000000..6539846ff1
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/heredoc_with_only_carriage_returns_windows.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,0)-(1,5))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,5) = "<<EOS"
+ ├── content_loc: (2,0)-(5,0) = "\r\r\n\r\r\r\n\\r\r\n"
+ ├── closing_loc: (5,0)-(6,0) = "EOS\r\n"
+ └── unescaped: "\r\n\r\r\n\r\n"
diff --git a/test/prism/snapshots/seattlerb/if_elsif.txt b/test/prism/snapshots/seattlerb/if_elsif.txt
new file mode 100644
index 0000000000..034850158c
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/if_elsif.txt
@@ -0,0 +1,25 @@
+@ ProgramNode (location: (1,0)-(1,18))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,18))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(1,18))
+ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ ├── predicate:
+ │ @ IntegerNode (location: (1,3)-(1,4))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── then_keyword_loc: ∅
+ ├── statements: ∅
+ ├── consequent:
+ │ @ IfNode (location: (1,6)-(1,18))
+ │ ├── if_keyword_loc: (1,6)-(1,11) = "elsif"
+ │ ├── predicate:
+ │ │ @ IntegerNode (location: (1,12)-(1,13))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements: ∅
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (1,15)-(1,18) = "end"
+ └── end_keyword_loc: (1,15)-(1,18) = "end"
diff --git a/test/prism/snapshots/seattlerb/if_symbol.txt b/test/prism/snapshots/seattlerb/if_symbol.txt
new file mode 100644
index 0000000000..85ec52b4c8
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/if_symbol.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(1,12))
+ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ ├── predicate:
+ │ @ CallNode (location: (1,3)-(1,7))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (1,3)-(1,4) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,5)-(1,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ SymbolNode (location: (1,5)-(1,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ │ ├── value_loc: (1,6)-(1,7) = "x"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "x"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── then_keyword_loc: ∅
+ ├── statements: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (1,9)-(1,12) = "end"
diff --git a/test/prism/snapshots/seattlerb/in_expr_no_case.txt b/test/prism/snapshots/seattlerb/in_expr_no_case.txt
new file mode 100644
index 0000000000..0a026c08db
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/in_expr_no_case.txt
@@ -0,0 +1,17 @@
+@ ProgramNode (location: (1,0)-(1,16))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,16))
+ └── body: (length: 1)
+ └── @ MatchPredicateNode (location: (1,0)-(1,16))
+ ├── value:
+ │ @ StringNode (location: (1,0)-(1,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,1) = "'"
+ │ ├── content_loc: (1,1)-(1,5) = "woot"
+ │ ├── closing_loc: (1,5)-(1,6) = "'"
+ │ └── unescaped: "woot"
+ ├── pattern:
+ │ @ ConstantReadNode (location: (1,10)-(1,16))
+ │ └── name: :String
+ └── operator_loc: (1,7)-(1,9) = "in"
diff --git a/test/prism/snapshots/seattlerb/index_0.txt b/test/prism/snapshots/seattlerb/index_0.txt
new file mode 100644
index 0000000000..9771e9c2bd
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/index_0.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(1,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,7))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,7))
+ ├── flags: attribute_write
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :[]=
+ ├── message_loc: (1,1)-(1,3) = "[]"
+ ├── opening_loc: (1,1)-(1,2) = "["
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,6)-(1,7))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (1,6)-(1,7))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :b
+ │ ├── message_loc: (1,6)-(1,7) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: (1,2)-(1,3) = "]"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/index_0_opasgn.txt b/test/prism/snapshots/seattlerb/index_0_opasgn.txt
new file mode 100644
index 0000000000..239a549253
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/index_0_opasgn.txt
@@ -0,0 +1,36 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ IndexOperatorWriteNode (location: (1,0)-(1,8))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── opening_loc: (1,1)-(1,2) = "["
+ ├── arguments: ∅
+ ├── closing_loc: (1,2)-(1,3) = "]"
+ ├── block: ∅
+ ├── operator: :+
+ ├── operator_loc: (1,4)-(1,6) = "+="
+ └── value:
+ @ CallNode (location: (1,7)-(1,8))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :b
+ ├── message_loc: (1,7)-(1,8) = "b"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/integer_with_if_modifier.txt b/test/prism/snapshots/seattlerb/integer_with_if_modifier.txt
new file mode 100644
index 0000000000..2108c1f0a0
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/integer_with_if_modifier.txt
@@ -0,0 +1,18 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(1,12))
+ ├── if_keyword_loc: (1,5)-(1,7) = "if"
+ ├── predicate:
+ │ @ TrueNode (location: (1,8)-(1,12))
+ ├── then_keyword_loc: ∅
+ ├── statements:
+ │ @ StatementsNode (location: (1,0)-(1,5))
+ │ └── body: (length: 1)
+ │ └── @ IntegerNode (location: (1,0)-(1,5))
+ │ ├── flags: decimal
+ │ └── value: 1234
+ ├── consequent: ∅
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/interpolated_symbol_array_line_breaks.txt b/test/prism/snapshots/seattlerb/interpolated_symbol_array_line_breaks.txt
new file mode 100644
index 0000000000..ab231cf539
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/interpolated_symbol_array_line_breaks.txt
@@ -0,0 +1,25 @@
+@ ProgramNode (location: (1,0)-(5,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,1))
+ └── body: (length: 2)
+ ├── @ ArrayNode (location: (1,0)-(4,1))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ SymbolNode (location: (2,0)-(2,1))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (2,0)-(2,1) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ └── @ SymbolNode (location: (3,0)-(3,1))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (3,0)-(3,1) = "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b"
+ │ ├── opening_loc: (1,0)-(1,3) = "%I("
+ │ └── closing_loc: (4,0)-(4,1) = ")"
+ └── @ IntegerNode (location: (5,0)-(5,1))
+ ├── flags: decimal
+ └── value: 1
diff --git a/test/prism/snapshots/seattlerb/interpolated_word_array_line_breaks.txt b/test/prism/snapshots/seattlerb/interpolated_word_array_line_breaks.txt
new file mode 100644
index 0000000000..933e5de627
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/interpolated_word_array_line_breaks.txt
@@ -0,0 +1,25 @@
+@ ProgramNode (location: (1,0)-(5,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,1))
+ └── body: (length: 2)
+ ├── @ ArrayNode (location: (1,0)-(4,1))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ StringNode (location: (2,0)-(2,1))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (2,0)-(2,1) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ └── @ StringNode (location: (3,0)-(3,1))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (3,0)-(3,1) = "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b"
+ │ ├── opening_loc: (1,0)-(1,3) = "%W("
+ │ └── closing_loc: (4,0)-(4,1) = ")"
+ └── @ IntegerNode (location: (5,0)-(5,1))
+ ├── flags: decimal
+ └── value: 1
diff --git a/test/prism/snapshots/seattlerb/iter_args_1.txt b/test/prism/snapshots/seattlerb/iter_args_1.txt
new file mode 100644
index 0000000000..f76bbfc559
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/iter_args_1.txt
@@ -0,0 +1,40 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,11))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,11))
+ ├── locals: [:a, :b]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,9))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,8))
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ └── @ RequiredParameterNode (location: (1,7)-(1,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :b
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,8)-(1,9) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,10)-(1,11) = "}"
diff --git a/test/prism/snapshots/seattlerb/iter_args_10_1.txt b/test/prism/snapshots/seattlerb/iter_args_10_1.txt
new file mode 100644
index 0000000000..b8aaae800a
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/iter_args_10_1.txt
@@ -0,0 +1,51 @@
+@ ProgramNode (location: (1,0)-(1,21))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,21))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,21))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,21))
+ ├── locals: [:a, :b, :c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,19))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,18))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,8)-(1,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (1,8)-(1,9) = "b"
+ │ │ │ ├── operator_loc: (1,10)-(1,11) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,12)-(1,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (1,16)-(1,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── name_loc: (1,17)-(1,18) = "c"
+ │ │ │ └── operator_loc: (1,16)-(1,17) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,18)-(1,19) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,20)-(1,21) = "}"
diff --git a/test/prism/snapshots/seattlerb/iter_args_10_2.txt b/test/prism/snapshots/seattlerb/iter_args_10_2.txt
new file mode 100644
index 0000000000..833f6429dc
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/iter_args_10_2.txt
@@ -0,0 +1,56 @@
+@ ProgramNode (location: (1,0)-(1,25))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,25))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,25))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,25))
+ ├── locals: [:a, :b, :c, :d]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,23))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,22))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,8)-(1,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (1,8)-(1,9) = "b"
+ │ │ │ ├── operator_loc: (1,10)-(1,11) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,12)-(1,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (1,16)-(1,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── name_loc: (1,17)-(1,18) = "c"
+ │ │ │ └── operator_loc: (1,16)-(1,17) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (1,20)-(1,22))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :d
+ │ │ ├── name_loc: (1,21)-(1,22) = "d"
+ │ │ └── operator_loc: (1,20)-(1,21) = "&"
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,22)-(1,23) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,24)-(1,25) = "}"
diff --git a/test/prism/snapshots/seattlerb/iter_args_11_1.txt b/test/prism/snapshots/seattlerb/iter_args_11_1.txt
new file mode 100644
index 0000000000..e4d2f79474
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/iter_args_11_1.txt
@@ -0,0 +1,54 @@
+@ ProgramNode (location: (1,0)-(1,24))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,24))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,24))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,24))
+ ├── locals: [:a, :b, :c, :d]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,22))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,21))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,8)-(1,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (1,8)-(1,9) = "b"
+ │ │ │ ├── operator_loc: (1,10)-(1,11) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,12)-(1,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (1,16)-(1,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── name_loc: (1,17)-(1,18) = "c"
+ │ │ │ └── operator_loc: (1,16)-(1,17) = "*"
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,20)-(1,21))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :d
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,21)-(1,22) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,23)-(1,24) = "}"
diff --git a/test/prism/snapshots/seattlerb/iter_args_11_2.txt b/test/prism/snapshots/seattlerb/iter_args_11_2.txt
new file mode 100644
index 0000000000..07a0e5ed9d
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/iter_args_11_2.txt
@@ -0,0 +1,59 @@
+@ ProgramNode (location: (1,0)-(1,28))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,28))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,28))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,28))
+ ├── locals: [:a, :b, :c, :d, :e]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,26))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,25))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,8)-(1,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (1,8)-(1,9) = "b"
+ │ │ │ ├── operator_loc: (1,10)-(1,11) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,12)-(1,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (1,16)-(1,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── name_loc: (1,17)-(1,18) = "c"
+ │ │ │ └── operator_loc: (1,16)-(1,17) = "*"
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,20)-(1,21))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :d
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (1,23)-(1,25))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :e
+ │ │ ├── name_loc: (1,24)-(1,25) = "e"
+ │ │ └── operator_loc: (1,23)-(1,24) = "&"
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,25)-(1,26) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,27)-(1,28) = "}"
diff --git a/test/prism/snapshots/seattlerb/iter_args_2__19.txt b/test/prism/snapshots/seattlerb/iter_args_2__19.txt
new file mode 100644
index 0000000000..a905286f47
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/iter_args_2__19.txt
@@ -0,0 +1,46 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,14))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,14))
+ ├── locals: [:a, :b]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,12))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,11))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ MultiTargetNode (location: (1,5)-(1,11))
+ │ │ │ ├── lefts: (length: 2)
+ │ │ │ │ ├── @ RequiredParameterNode (location: (1,6)-(1,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :a
+ │ │ │ │ └── @ RequiredParameterNode (location: (1,9)-(1,10))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :b
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: (1,5)-(1,6) = "("
+ │ │ │ └── rparen_loc: (1,10)-(1,11) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,11)-(1,12) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,13)-(1,14) = "}"
diff --git a/test/prism/snapshots/seattlerb/iter_args_3.txt b/test/prism/snapshots/seattlerb/iter_args_3.txt
new file mode 100644
index 0000000000..66782f0793
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/iter_args_3.txt
@@ -0,0 +1,52 @@
+@ ProgramNode (location: (1,0)-(1,20))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,20))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,20))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,20))
+ ├── locals: [:a, :b, :c, :d]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,18))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,17))
+ │ │ ├── requireds: (length: 3)
+ │ │ │ ├── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── @ MultiTargetNode (location: (1,8)-(1,14))
+ │ │ │ │ ├── lefts: (length: 2)
+ │ │ │ │ │ ├── @ RequiredParameterNode (location: (1,9)-(1,10))
+ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ └── name: :b
+ │ │ │ │ │ └── @ RequiredParameterNode (location: (1,12)-(1,13))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :c
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── rights: (length: 0)
+ │ │ │ │ ├── lparen_loc: (1,8)-(1,9) = "("
+ │ │ │ │ └── rparen_loc: (1,13)-(1,14) = ")"
+ │ │ │ └── @ RequiredParameterNode (location: (1,16)-(1,17))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :d
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,17)-(1,18) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,19)-(1,20) = "}"
diff --git a/test/prism/snapshots/seattlerb/iter_args_4.txt b/test/prism/snapshots/seattlerb/iter_args_4.txt
new file mode 100644
index 0000000000..c4f2b47a03
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/iter_args_4.txt
@@ -0,0 +1,45 @@
+@ ProgramNode (location: (1,0)-(1,16))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,16))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,16))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,16))
+ ├── locals: [:a, :b, :c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,14))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,13))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (1,8)-(1,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (1,9)-(1,10) = "b"
+ │ │ │ └── operator_loc: (1,8)-(1,9) = "*"
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,12)-(1,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :c
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,13)-(1,14) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,15)-(1,16) = "}"
diff --git a/test/prism/snapshots/seattlerb/iter_args_5.txt b/test/prism/snapshots/seattlerb/iter_args_5.txt
new file mode 100644
index 0000000000..44940629b4
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/iter_args_5.txt
@@ -0,0 +1,42 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,13))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,13))
+ ├── locals: [:a, :b]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,11))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,10))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (1,8)-(1,10))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (1,9)-(1,10) = "b"
+ │ │ └── operator_loc: (1,8)-(1,9) = "&"
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,10)-(1,11) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,12)-(1,13) = "}"
diff --git a/test/prism/snapshots/seattlerb/iter_args_6.txt b/test/prism/snapshots/seattlerb/iter_args_6.txt
new file mode 100644
index 0000000000..df3a405dff
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/iter_args_6.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(1,18))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,18))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,18))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,18))
+ ├── locals: [:a, :b, :c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,16))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,15))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,8)-(1,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (1,8)-(1,9) = "b"
+ │ │ │ ├── operator_loc: (1,9)-(1,10) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,10)-(1,12))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,14)-(1,15))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :c
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,15)-(1,16) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,17)-(1,18) = "}"
diff --git a/test/prism/snapshots/seattlerb/iter_args_7_1.txt b/test/prism/snapshots/seattlerb/iter_args_7_1.txt
new file mode 100644
index 0000000000..3f65fb8dc0
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/iter_args_7_1.txt
@@ -0,0 +1,48 @@
+@ ProgramNode (location: (1,0)-(1,18))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,18))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,18))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,18))
+ ├── locals: [:a, :b]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,16))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,15))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,5)-(1,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── name_loc: (1,5)-(1,6) = "a"
+ │ │ │ ├── operator_loc: (1,7)-(1,8) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,9)-(1,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (1,13)-(1,15))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (1,14)-(1,15) = "b"
+ │ │ │ └── operator_loc: (1,13)-(1,14) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,15)-(1,16) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,17)-(1,18) = "}"
diff --git a/test/prism/snapshots/seattlerb/iter_args_7_2.txt b/test/prism/snapshots/seattlerb/iter_args_7_2.txt
new file mode 100644
index 0000000000..112a5a1b6b
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/iter_args_7_2.txt
@@ -0,0 +1,53 @@
+@ ProgramNode (location: (1,0)-(1,22))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,22))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,22))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,22))
+ ├── locals: [:a, :b, :c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,20))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,19))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,5)-(1,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── name_loc: (1,5)-(1,6) = "a"
+ │ │ │ ├── operator_loc: (1,7)-(1,8) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,9)-(1,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (1,13)-(1,15))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (1,14)-(1,15) = "b"
+ │ │ │ └── operator_loc: (1,13)-(1,14) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (1,17)-(1,19))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :c
+ │ │ ├── name_loc: (1,18)-(1,19) = "c"
+ │ │ └── operator_loc: (1,17)-(1,18) = "&"
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,19)-(1,20) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,21)-(1,22) = "}"
diff --git a/test/prism/snapshots/seattlerb/iter_args_8_1.txt b/test/prism/snapshots/seattlerb/iter_args_8_1.txt
new file mode 100644
index 0000000000..5591fcfc69
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/iter_args_8_1.txt
@@ -0,0 +1,51 @@
+@ ProgramNode (location: (1,0)-(1,21))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,21))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,21))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,21))
+ ├── locals: [:a, :b, :c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,19))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,18))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,5)-(1,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── name_loc: (1,5)-(1,6) = "a"
+ │ │ │ ├── operator_loc: (1,7)-(1,8) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,9)-(1,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (1,13)-(1,15))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (1,14)-(1,15) = "b"
+ │ │ │ └── operator_loc: (1,13)-(1,14) = "*"
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,17)-(1,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :c
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,18)-(1,19) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,20)-(1,21) = "}"
diff --git a/test/prism/snapshots/seattlerb/iter_args_8_2.txt b/test/prism/snapshots/seattlerb/iter_args_8_2.txt
new file mode 100644
index 0000000000..8cb061d5a7
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/iter_args_8_2.txt
@@ -0,0 +1,56 @@
+@ ProgramNode (location: (1,0)-(1,25))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,25))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,25))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,25))
+ ├── locals: [:a, :b, :c, :d]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,23))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,22))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,5)-(1,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── name_loc: (1,5)-(1,6) = "a"
+ │ │ │ ├── operator_loc: (1,7)-(1,8) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,9)-(1,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (1,13)-(1,15))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (1,14)-(1,15) = "b"
+ │ │ │ └── operator_loc: (1,13)-(1,14) = "*"
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,17)-(1,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :c
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (1,20)-(1,22))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :d
+ │ │ ├── name_loc: (1,21)-(1,22) = "d"
+ │ │ └── operator_loc: (1,20)-(1,21) = "&"
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,22)-(1,23) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,24)-(1,25) = "}"
diff --git a/test/prism/snapshots/seattlerb/iter_args_9_1.txt b/test/prism/snapshots/seattlerb/iter_args_9_1.txt
new file mode 100644
index 0000000000..70b2a6cb19
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/iter_args_9_1.txt
@@ -0,0 +1,46 @@
+@ ProgramNode (location: (1,0)-(1,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,17))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,17))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,17))
+ ├── locals: [:a, :b]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,15))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,14))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,5)-(1,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── name_loc: (1,5)-(1,6) = "a"
+ │ │ │ ├── operator_loc: (1,7)-(1,8) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,9)-(1,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,13)-(1,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :b
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,14)-(1,15) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,16)-(1,17) = "}"
diff --git a/test/prism/snapshots/seattlerb/iter_args_9_2.txt b/test/prism/snapshots/seattlerb/iter_args_9_2.txt
new file mode 100644
index 0000000000..cad8b47e7f
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/iter_args_9_2.txt
@@ -0,0 +1,51 @@
+@ ProgramNode (location: (1,0)-(1,21))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,21))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,21))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,21))
+ ├── locals: [:a, :b, :c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,19))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,18))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,5)-(1,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── name_loc: (1,5)-(1,6) = "a"
+ │ │ │ ├── operator_loc: (1,7)-(1,8) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,9)-(1,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,13)-(1,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :b
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (1,16)-(1,18))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :c
+ │ │ ├── name_loc: (1,17)-(1,18) = "c"
+ │ │ └── operator_loc: (1,16)-(1,17) = "&"
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,18)-(1,19) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,20)-(1,21) = "}"
diff --git a/test/prism/snapshots/seattlerb/iter_kwarg.txt b/test/prism/snapshots/seattlerb/iter_kwarg.txt
new file mode 100644
index 0000000000..13b9027661
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/iter_kwarg.txt
@@ -0,0 +1,42 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,12))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,12))
+ ├── locals: [:b]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,10))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,9))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ OptionalKeywordParameterNode (location: (1,5)-(1,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (1,5)-(1,7) = "b:"
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,8)-(1,9))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,9)-(1,10) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,11)-(1,12) = "}"
diff --git a/test/prism/snapshots/seattlerb/iter_kwarg_kwsplat.txt b/test/prism/snapshots/seattlerb/iter_kwarg_kwsplat.txt
new file mode 100644
index 0000000000..321e15cb2b
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/iter_kwarg_kwsplat.txt
@@ -0,0 +1,47 @@
+@ ProgramNode (location: (1,0)-(1,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,17))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,17))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,17))
+ ├── locals: [:b, :c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,4)-(1,15))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,5)-(1,14))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ OptionalKeywordParameterNode (location: (1,5)-(1,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (1,5)-(1,7) = "b:"
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,8)-(1,9))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── keyword_rest:
+ │ │ │ @ KeywordRestParameterNode (location: (1,11)-(1,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── name_loc: (1,13)-(1,14) = "c"
+ │ │ │ └── operator_loc: (1,11)-(1,13) = "**"
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ └── closing_loc: (1,14)-(1,15) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,16)-(1,17) = "}"
diff --git a/test/prism/snapshots/seattlerb/label_vs_string.txt b/test/prism/snapshots/seattlerb/label_vs_string.txt
new file mode 100644
index 0000000000..c8d7af8f48
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/label_vs_string.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(2,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,1))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(2,1))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,4))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :_buf
+ │ ├── message_loc: (1,0)-(1,4) = "_buf"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :<<
+ ├── message_loc: (1,5)-(1,7) = "<<"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,8)-(2,1))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ StringNode (location: (1,8)-(2,1))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,8)-(1,9) = "'"
+ │ ├── content_loc: (1,9)-(2,0) = ":\n"
+ │ ├── closing_loc: (2,0)-(2,1) = "'"
+ │ └── unescaped: ":\n"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/lambda_do_vs_brace.txt b/test/prism/snapshots/seattlerb/lambda_do_vs_brace.txt
new file mode 100644
index 0000000000..afc0d3d56f
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/lambda_do_vs_brace.txt
@@ -0,0 +1,95 @@
+@ ProgramNode (location: (1,0)-(7,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(7,9))
+ └── body: (length: 4)
+ ├── @ CallNode (location: (1,0)-(1,11))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (1,0)-(1,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,2)-(1,11))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ LambdaNode (location: (1,2)-(1,11))
+ │ │ ├── locals: []
+ │ │ ├── operator_loc: (1,2)-(1,4) = "->"
+ │ │ ├── opening_loc: (1,5)-(1,7) = "do"
+ │ │ ├── closing_loc: (1,8)-(1,11) = "end"
+ │ │ ├── parameters: ∅
+ │ │ └── body: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (3,0)-(3,7))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (3,0)-(3,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,2)-(3,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ LambdaNode (location: (3,2)-(3,7))
+ │ │ ├── locals: []
+ │ │ ├── operator_loc: (3,2)-(3,4) = "->"
+ │ │ ├── opening_loc: (3,5)-(3,6) = "{"
+ │ │ ├── closing_loc: (3,6)-(3,7) = "}"
+ │ │ ├── parameters: ∅
+ │ │ └── body: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (5,0)-(5,13))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (5,0)-(5,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (5,2)-(5,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ LambdaNode (location: (5,2)-(5,13))
+ │ │ ├── locals: []
+ │ │ ├── operator_loc: (5,2)-(5,4) = "->"
+ │ │ ├── opening_loc: (5,7)-(5,9) = "do"
+ │ │ ├── closing_loc: (5,10)-(5,13) = "end"
+ │ │ ├── parameters:
+ │ │ │ @ BlockParametersNode (location: (5,4)-(5,6))
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── locals: (length: 0)
+ │ │ │ ├── opening_loc: (5,4)-(5,5) = "("
+ │ │ │ └── closing_loc: (5,5)-(5,6) = ")"
+ │ │ └── body: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (7,0)-(7,9))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (7,0)-(7,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (7,2)-(7,9))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ LambdaNode (location: (7,2)-(7,9))
+ │ ├── locals: []
+ │ ├── operator_loc: (7,2)-(7,4) = "->"
+ │ ├── opening_loc: (7,7)-(7,8) = "{"
+ │ ├── closing_loc: (7,8)-(7,9) = "}"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (7,4)-(7,6))
+ │ │ ├── parameters: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (7,4)-(7,5) = "("
+ │ │ └── closing_loc: (7,5)-(7,6) = ")"
+ │ └── body: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/lasgn_arg_rescue_arg.txt b/test/prism/snapshots/seattlerb/lasgn_arg_rescue_arg.txt
new file mode 100644
index 0000000000..7bbef7055a
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/lasgn_arg_rescue_arg.txt
@@ -0,0 +1,21 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ LocalVariableWriteNode (location: (1,0)-(1,14))
+ ├── name: :a
+ ├── depth: 0
+ ├── name_loc: (1,0)-(1,1) = "a"
+ ├── value:
+ │ @ RescueModifierNode (location: (1,4)-(1,14))
+ │ ├── expression:
+ │ │ @ IntegerNode (location: (1,4)-(1,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── keyword_loc: (1,6)-(1,12) = "rescue"
+ │ └── rescue_expression:
+ │ @ IntegerNode (location: (1,13)-(1,14))
+ │ ├── flags: decimal
+ │ └── value: 2
+ └── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/seattlerb/lasgn_call_bracket_rescue_arg.txt b/test/prism/snapshots/seattlerb/lasgn_call_bracket_rescue_arg.txt
new file mode 100644
index 0000000000..521fceaf1b
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/lasgn_call_bracket_rescue_arg.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,17))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,17))
+ └── body: (length: 1)
+ └── @ LocalVariableWriteNode (location: (1,0)-(1,17))
+ ├── name: :a
+ ├── depth: 0
+ ├── name_loc: (1,0)-(1,1) = "a"
+ ├── value:
+ │ @ RescueModifierNode (location: (1,4)-(1,17))
+ │ ├── expression:
+ │ │ @ CallNode (location: (1,4)-(1,8))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (1,4)-(1,5) = "b"
+ │ │ ├── opening_loc: (1,5)-(1,6) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (1,6)-(1,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (1,6)-(1,7))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: (1,7)-(1,8) = ")"
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (1,9)-(1,15) = "rescue"
+ │ └── rescue_expression:
+ │ @ IntegerNode (location: (1,16)-(1,17))
+ │ ├── flags: decimal
+ │ └── value: 2
+ └── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/seattlerb/lasgn_call_nobracket_rescue_arg.txt b/test/prism/snapshots/seattlerb/lasgn_call_nobracket_rescue_arg.txt
new file mode 100644
index 0000000000..d730fb51cb
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/lasgn_call_nobracket_rescue_arg.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,16))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,16))
+ └── body: (length: 1)
+ └── @ LocalVariableWriteNode (location: (1,0)-(1,16))
+ ├── name: :a
+ ├── depth: 0
+ ├── name_loc: (1,0)-(1,1) = "a"
+ ├── value:
+ │ @ RescueModifierNode (location: (1,4)-(1,16))
+ │ ├── expression:
+ │ │ @ CallNode (location: (1,4)-(1,7))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (1,4)-(1,5) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (1,6)-(1,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (1,6)-(1,7))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (1,8)-(1,14) = "rescue"
+ │ └── rescue_expression:
+ │ @ IntegerNode (location: (1,15)-(1,16))
+ │ ├── flags: decimal
+ │ └── value: 2
+ └── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/seattlerb/lasgn_command.txt b/test/prism/snapshots/seattlerb/lasgn_command.txt
new file mode 100644
index 0000000000..d6ed787b26
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/lasgn_command.txt
@@ -0,0 +1,37 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ LocalVariableWriteNode (location: (1,0)-(1,9))
+ ├── name: :a
+ ├── depth: 0
+ ├── name_loc: (1,0)-(1,1) = "a"
+ ├── value:
+ │ @ CallNode (location: (1,4)-(1,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,4)-(1,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (1,4)-(1,5) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (1,5)-(1,6) = "."
+ │ ├── name: :c
+ │ ├── message_loc: (1,6)-(1,7) = "c"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,8)-(1,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,8)-(1,9))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/seattlerb/lasgn_env.txt b/test/prism/snapshots/seattlerb/lasgn_env.txt
new file mode 100644
index 0000000000..a0ada99a9b
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/lasgn_env.txt
@@ -0,0 +1,14 @@
+@ ProgramNode (location: (1,0)-(1,6))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,6))
+ └── body: (length: 1)
+ └── @ LocalVariableWriteNode (location: (1,0)-(1,6))
+ ├── name: :a
+ ├── depth: 0
+ ├── name_loc: (1,0)-(1,1) = "a"
+ ├── value:
+ │ @ IntegerNode (location: (1,4)-(1,6))
+ │ ├── flags: decimal
+ │ └── value: 42
+ └── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/seattlerb/lasgn_ivar_env.txt b/test/prism/snapshots/seattlerb/lasgn_ivar_env.txt
new file mode 100644
index 0000000000..5675730477
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/lasgn_ivar_env.txt
@@ -0,0 +1,13 @@
+@ ProgramNode (location: (1,0)-(1,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,7))
+ └── body: (length: 1)
+ └── @ InstanceVariableWriteNode (location: (1,0)-(1,7))
+ ├── name: :@a
+ ├── name_loc: (1,0)-(1,2) = "@a"
+ ├── value:
+ │ @ IntegerNode (location: (1,5)-(1,7))
+ │ ├── flags: decimal
+ │ └── value: 42
+ └── operator_loc: (1,3)-(1,4) = "="
diff --git a/test/prism/snapshots/seattlerb/lasgn_lasgn_command_call.txt b/test/prism/snapshots/seattlerb/lasgn_lasgn_command_call.txt
new file mode 100644
index 0000000000..bb4b64e88c
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/lasgn_lasgn_command_call.txt
@@ -0,0 +1,33 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: [:a, :b]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ LocalVariableWriteNode (location: (1,0)-(1,11))
+ ├── name: :a
+ ├── depth: 0
+ ├── name_loc: (1,0)-(1,1) = "a"
+ ├── value:
+ │ @ LocalVariableWriteNode (location: (1,4)-(1,11))
+ │ ├── name: :b
+ │ ├── depth: 0
+ │ ├── name_loc: (1,4)-(1,5) = "b"
+ │ ├── value:
+ │ │ @ CallNode (location: (1,8)-(1,11))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :c
+ │ │ ├── message_loc: (1,8)-(1,9) = "c"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (1,10)-(1,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (1,10)-(1,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (1,6)-(1,7) = "="
+ └── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/seattlerb/lasgn_middle_splat.txt b/test/prism/snapshots/seattlerb/lasgn_middle_splat.txt
new file mode 100644
index 0000000000..c113fef13f
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/lasgn_middle_splat.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ LocalVariableWriteNode (location: (1,0)-(1,12))
+ ├── name: :a
+ ├── depth: 0
+ ├── name_loc: (1,0)-(1,1) = "a"
+ ├── value:
+ │ @ ArrayNode (location: (1,4)-(1,12))
+ │ ├── flags: contains_splat
+ │ ├── elements: (length: 3)
+ │ │ ├── @ CallNode (location: (1,4)-(1,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (1,4)-(1,5) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── @ SplatNode (location: (1,7)-(1,9))
+ │ │ │ ├── operator_loc: (1,7)-(1,8) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ CallNode (location: (1,8)-(1,9))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── message_loc: (1,8)-(1,9) = "c"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ CallNode (location: (1,11)-(1,12))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :d
+ │ │ ├── message_loc: (1,11)-(1,12) = "d"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: ∅
+ │ └── closing_loc: ∅
+ └── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/seattlerb/magic_encoding_comment.txt b/test/prism/snapshots/seattlerb/magic_encoding_comment.txt
new file mode 100644
index 0000000000..9c4ca884d2
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/magic_encoding_comment.txt
@@ -0,0 +1,46 @@
+@ ProgramNode (location: (2,0)-(3,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (2,0)-(3,3))
+ └── body: (length: 1)
+ └── @ ClassNode (location: (2,0)-(3,3))
+ ├── locals: []
+ ├── class_keyword_loc: (2,0)-(2,5) = "class"
+ ├── constant_path:
+ │ @ ConstantReadNode (location: (2,6)-(2,34))
+ │ └── name: :ExampleUTF8ClassNameVarietà
+ ├── inheritance_operator_loc: ∅
+ ├── superclass: ∅
+ ├── body:
+ │ @ StatementsNode (location: (2,36)-(2,68))
+ │ └── body: (length: 1)
+ │ └── @ DefNode (location: (2,36)-(2,68))
+ │ ├── name: :è
+ │ ├── name_loc: (2,45)-(2,47) = "è"
+ │ ├── receiver:
+ │ │ @ SelfNode (location: (2,40)-(2,44))
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (2,49)-(2,63))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableWriteNode (location: (2,49)-(2,63))
+ │ │ ├── name: :così
+ │ │ ├── depth: 0
+ │ │ ├── name_loc: (2,49)-(2,54) = "così"
+ │ │ ├── value:
+ │ │ │ @ SymbolNode (location: (2,57)-(2,63))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (2,57)-(2,58) = ":"
+ │ │ │ ├── value_loc: (2,58)-(2,63) = "però"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "però"
+ │ │ └── operator_loc: (2,55)-(2,56) = "="
+ │ ├── locals: [:così]
+ │ ├── def_keyword_loc: (2,36)-(2,39) = "def"
+ │ ├── operator_loc: (2,44)-(2,45) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (2,65)-(2,68) = "end"
+ ├── end_keyword_loc: (3,0)-(3,3) = "end"
+ └── name: :ExampleUTF8ClassNameVarietà
diff --git a/test/prism/snapshots/seattlerb/masgn_anon_splat_arg.txt b/test/prism/snapshots/seattlerb/masgn_anon_splat_arg.txt
new file mode 100644
index 0000000000..9ebcab3095
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/masgn_anon_splat_arg.txt
@@ -0,0 +1,29 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ MultiWriteNode (location: (1,0)-(1,8))
+ ├── lefts: (length: 0)
+ ├── rest:
+ │ @ SplatNode (location: (1,0)-(1,1))
+ │ ├── operator_loc: (1,0)-(1,1) = "*"
+ │ └── expression: ∅
+ ├── rights: (length: 1)
+ │ └── @ LocalVariableTargetNode (location: (1,3)-(1,4))
+ │ ├── name: :a
+ │ └── depth: 0
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (1,5)-(1,6) = "="
+ └── value:
+ @ CallNode (location: (1,7)-(1,8))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :b
+ ├── message_loc: (1,7)-(1,8) = "b"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/masgn_arg_colon_arg.txt b/test/prism/snapshots/seattlerb/masgn_arg_colon_arg.txt
new file mode 100644
index 0000000000..83613c42d1
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/masgn_arg_colon_arg.txt
@@ -0,0 +1,42 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ MultiWriteNode (location: (1,0)-(1,11))
+ ├── lefts: (length: 2)
+ │ ├── @ LocalVariableTargetNode (location: (1,0)-(1,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ └── @ CallTargetNode (location: (1,3)-(1,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,3)-(1,4))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (1,3)-(1,4) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (1,4)-(1,6) = "::"
+ │ ├── name: :c=
+ │ └── message_loc: (1,6)-(1,7) = "c"
+ ├── rest: ∅
+ ├── rights: (length: 0)
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (1,8)-(1,9) = "="
+ └── value:
+ @ CallNode (location: (1,10)-(1,11))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :d
+ ├── message_loc: (1,10)-(1,11) = "d"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/masgn_arg_ident.txt b/test/prism/snapshots/seattlerb/masgn_arg_ident.txt
new file mode 100644
index 0000000000..f4c99648f0
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/masgn_arg_ident.txt
@@ -0,0 +1,42 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ MultiWriteNode (location: (1,0)-(1,10))
+ ├── lefts: (length: 2)
+ │ ├── @ LocalVariableTargetNode (location: (1,0)-(1,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ └── @ CallTargetNode (location: (1,3)-(1,6))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,3)-(1,4))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (1,3)-(1,4) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (1,4)-(1,5) = "."
+ │ ├── name: :C=
+ │ └── message_loc: (1,5)-(1,6) = "C"
+ ├── rest: ∅
+ ├── rights: (length: 0)
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (1,7)-(1,8) = "="
+ └── value:
+ @ CallNode (location: (1,9)-(1,10))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :d
+ ├── message_loc: (1,9)-(1,10) = "d"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/masgn_arg_splat_arg.txt b/test/prism/snapshots/seattlerb/masgn_arg_splat_arg.txt
new file mode 100644
index 0000000000..48e58de076
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/masgn_arg_splat_arg.txt
@@ -0,0 +1,35 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: [:a, :b, :c]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ MultiWriteNode (location: (1,0)-(1,12))
+ ├── lefts: (length: 1)
+ │ └── @ LocalVariableTargetNode (location: (1,0)-(1,1))
+ │ ├── name: :a
+ │ └── depth: 0
+ ├── rest:
+ │ @ SplatNode (location: (1,3)-(1,5))
+ │ ├── operator_loc: (1,3)-(1,4) = "*"
+ │ └── expression:
+ │ @ LocalVariableTargetNode (location: (1,4)-(1,5))
+ │ ├── name: :b
+ │ └── depth: 0
+ ├── rights: (length: 1)
+ │ └── @ LocalVariableTargetNode (location: (1,7)-(1,8))
+ │ ├── name: :c
+ │ └── depth: 0
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (1,9)-(1,10) = "="
+ └── value:
+ @ CallNode (location: (1,11)-(1,12))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :d
+ ├── message_loc: (1,11)-(1,12) = "d"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/masgn_colon2.txt b/test/prism/snapshots/seattlerb/masgn_colon2.txt
new file mode 100644
index 0000000000..73ce8a71da
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/masgn_colon2.txt
@@ -0,0 +1,43 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ MultiWriteNode (location: (1,0)-(1,14))
+ ├── lefts: (length: 2)
+ │ ├── @ LocalVariableTargetNode (location: (1,0)-(1,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ └── @ ConstantPathTargetNode (location: (1,3)-(1,7))
+ │ ├── parent:
+ │ │ @ CallNode (location: (1,3)-(1,4))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (1,3)-(1,4) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (1,6)-(1,7))
+ │ │ └── name: :C
+ │ └── delimiter_loc: (1,4)-(1,6) = "::"
+ ├── rest: ∅
+ ├── rights: (length: 0)
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (1,8)-(1,9) = "="
+ └── value:
+ @ ArrayNode (location: (1,10)-(1,14))
+ ├── flags: ∅
+ ├── elements: (length: 2)
+ │ ├── @ IntegerNode (location: (1,10)-(1,11))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── @ IntegerNode (location: (1,13)-(1,14))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── opening_loc: ∅
+ └── closing_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/masgn_colon3.txt b/test/prism/snapshots/seattlerb/masgn_colon3.txt
new file mode 100644
index 0000000000..0cf4f8626d
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/masgn_colon3.txt
@@ -0,0 +1,36 @@
+@ ProgramNode (location: (1,0)-(1,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,15))
+ └── body: (length: 1)
+ └── @ MultiWriteNode (location: (1,0)-(1,15))
+ ├── lefts: (length: 2)
+ │ ├── @ ConstantPathTargetNode (location: (1,0)-(1,3))
+ │ │ ├── parent: ∅
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (1,2)-(1,3))
+ │ │ │ └── name: :A
+ │ │ └── delimiter_loc: (1,0)-(1,2) = "::"
+ │ └── @ ConstantPathTargetNode (location: (1,5)-(1,8))
+ │ ├── parent: ∅
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (1,7)-(1,8))
+ │ │ └── name: :B
+ │ └── delimiter_loc: (1,5)-(1,7) = "::"
+ ├── rest: ∅
+ ├── rights: (length: 0)
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (1,9)-(1,10) = "="
+ └── value:
+ @ ArrayNode (location: (1,11)-(1,15))
+ ├── flags: ∅
+ ├── elements: (length: 2)
+ │ ├── @ IntegerNode (location: (1,11)-(1,12))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── @ IntegerNode (location: (1,14)-(1,15))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── opening_loc: ∅
+ └── closing_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/masgn_command_call.txt b/test/prism/snapshots/seattlerb/masgn_command_call.txt
new file mode 100644
index 0000000000..687ea38243
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/masgn_command_call.txt
@@ -0,0 +1,43 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ MultiWriteNode (location: (1,0)-(1,10))
+ ├── lefts: (length: 1)
+ │ └── @ LocalVariableTargetNode (location: (1,0)-(1,1))
+ │ ├── name: :a
+ │ └── depth: 0
+ ├── rest:
+ │ @ ImplicitRestNode (location: (1,1)-(1,2))
+ ├── rights: (length: 0)
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (1,3)-(1,4) = "="
+ └── value:
+ @ CallNode (location: (1,5)-(1,10))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,5)-(1,6))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :b
+ │ ├── message_loc: (1,5)-(1,6) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,6)-(1,7) = "."
+ ├── name: :c
+ ├── message_loc: (1,7)-(1,8) = "c"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,9)-(1,10))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,9)-(1,10))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/masgn_double_paren.txt b/test/prism/snapshots/seattlerb/masgn_double_paren.txt
new file mode 100644
index 0000000000..590df8fa07
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/masgn_double_paren.txt
@@ -0,0 +1,35 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: [:a, :b]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ MultiWriteNode (location: (1,0)-(1,9))
+ ├── lefts: (length: 1)
+ │ └── @ MultiTargetNode (location: (1,1)-(1,6))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ LocalVariableTargetNode (location: (1,2)-(1,3))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ └── @ LocalVariableTargetNode (location: (1,4)-(1,5))
+ │ │ ├── name: :b
+ │ │ └── depth: 0
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: (1,1)-(1,2) = "("
+ │ └── rparen_loc: (1,5)-(1,6) = ")"
+ ├── rest: ∅
+ ├── rights: (length: 0)
+ ├── lparen_loc: (1,0)-(1,1) = "("
+ ├── rparen_loc: (1,6)-(1,7) = ")"
+ ├── operator_loc: (1,7)-(1,8) = "="
+ └── value:
+ @ CallNode (location: (1,8)-(1,9))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :c
+ ├── message_loc: (1,8)-(1,9) = "c"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/masgn_lhs_splat.txt b/test/prism/snapshots/seattlerb/masgn_lhs_splat.txt
new file mode 100644
index 0000000000..771dd7c040
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/masgn_lhs_splat.txt
@@ -0,0 +1,33 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ MultiWriteNode (location: (1,0)-(1,12))
+ ├── lefts: (length: 0)
+ ├── rest:
+ │ @ SplatNode (location: (1,0)-(1,2))
+ │ ├── operator_loc: (1,0)-(1,1) = "*"
+ │ └── expression:
+ │ @ LocalVariableTargetNode (location: (1,1)-(1,2))
+ │ ├── name: :a
+ │ └── depth: 0
+ ├── rights: (length: 0)
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (1,3)-(1,4) = "="
+ └── value:
+ @ ArrayNode (location: (1,5)-(1,12))
+ ├── flags: ∅
+ ├── elements: (length: 3)
+ │ ├── @ IntegerNode (location: (1,5)-(1,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── @ IntegerNode (location: (1,8)-(1,9))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ └── @ IntegerNode (location: (1,11)-(1,12))
+ │ ├── flags: decimal
+ │ └── value: 3
+ ├── opening_loc: ∅
+ └── closing_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/masgn_paren.txt b/test/prism/snapshots/seattlerb/masgn_paren.txt
new file mode 100644
index 0000000000..5d79774d5e
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/masgn_paren.txt
@@ -0,0 +1,39 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: [:a, :b]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ MultiWriteNode (location: (1,0)-(1,12))
+ ├── lefts: (length: 2)
+ │ ├── @ LocalVariableTargetNode (location: (1,1)-(1,2))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ └── @ LocalVariableTargetNode (location: (1,4)-(1,5))
+ │ ├── name: :b
+ │ └── depth: 0
+ ├── rest: ∅
+ ├── rights: (length: 0)
+ ├── lparen_loc: (1,0)-(1,1) = "("
+ ├── rparen_loc: (1,5)-(1,6) = ")"
+ ├── operator_loc: (1,7)-(1,8) = "="
+ └── value:
+ @ CallNode (location: (1,9)-(1,12))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,9)-(1,10))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :c
+ │ ├── message_loc: (1,9)-(1,10) = "c"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,10)-(1,11) = "."
+ ├── name: :d
+ ├── message_loc: (1,11)-(1,12) = "d"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/masgn_splat_arg.txt b/test/prism/snapshots/seattlerb/masgn_splat_arg.txt
new file mode 100644
index 0000000000..b8113b126f
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/masgn_splat_arg.txt
@@ -0,0 +1,32 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: [:a, :b]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ MultiWriteNode (location: (1,0)-(1,9))
+ ├── lefts: (length: 0)
+ ├── rest:
+ │ @ SplatNode (location: (1,0)-(1,2))
+ │ ├── operator_loc: (1,0)-(1,1) = "*"
+ │ └── expression:
+ │ @ LocalVariableTargetNode (location: (1,1)-(1,2))
+ │ ├── name: :a
+ │ └── depth: 0
+ ├── rights: (length: 1)
+ │ └── @ LocalVariableTargetNode (location: (1,4)-(1,5))
+ │ ├── name: :b
+ │ └── depth: 0
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (1,6)-(1,7) = "="
+ └── value:
+ @ CallNode (location: (1,8)-(1,9))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :c
+ ├── message_loc: (1,8)-(1,9) = "c"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/masgn_splat_arg_arg.txt b/test/prism/snapshots/seattlerb/masgn_splat_arg_arg.txt
new file mode 100644
index 0000000000..a832aef1e0
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/masgn_splat_arg_arg.txt
@@ -0,0 +1,35 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: [:a, :b, :c]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ MultiWriteNode (location: (1,0)-(1,12))
+ ├── lefts: (length: 0)
+ ├── rest:
+ │ @ SplatNode (location: (1,0)-(1,2))
+ │ ├── operator_loc: (1,0)-(1,1) = "*"
+ │ └── expression:
+ │ @ LocalVariableTargetNode (location: (1,1)-(1,2))
+ │ ├── name: :a
+ │ └── depth: 0
+ ├── rights: (length: 2)
+ │ ├── @ LocalVariableTargetNode (location: (1,4)-(1,5))
+ │ │ ├── name: :b
+ │ │ └── depth: 0
+ │ └── @ LocalVariableTargetNode (location: (1,7)-(1,8))
+ │ ├── name: :c
+ │ └── depth: 0
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (1,9)-(1,10) = "="
+ └── value:
+ @ CallNode (location: (1,11)-(1,12))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :d
+ ├── message_loc: (1,11)-(1,12) = "d"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/masgn_star.txt b/test/prism/snapshots/seattlerb/masgn_star.txt
new file mode 100644
index 0000000000..3e01eef8a7
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/masgn_star.txt
@@ -0,0 +1,19 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ MultiWriteNode (location: (1,0)-(1,5))
+ ├── lefts: (length: 0)
+ ├── rest:
+ │ @ SplatNode (location: (1,0)-(1,1))
+ │ ├── operator_loc: (1,0)-(1,1) = "*"
+ │ └── expression: ∅
+ ├── rights: (length: 0)
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (1,2)-(1,3) = "="
+ └── value:
+ @ IntegerNode (location: (1,4)-(1,5))
+ ├── flags: decimal
+ └── value: 1
diff --git a/test/prism/snapshots/seattlerb/masgn_var_star_var.txt b/test/prism/snapshots/seattlerb/masgn_var_star_var.txt
new file mode 100644
index 0000000000..37851efd6f
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/masgn_var_star_var.txt
@@ -0,0 +1,32 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: [:a, :b]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ MultiWriteNode (location: (1,0)-(1,11))
+ ├── lefts: (length: 1)
+ │ └── @ LocalVariableTargetNode (location: (1,0)-(1,1))
+ │ ├── name: :a
+ │ └── depth: 0
+ ├── rest:
+ │ @ SplatNode (location: (1,3)-(1,4))
+ │ ├── operator_loc: (1,3)-(1,4) = "*"
+ │ └── expression: ∅
+ ├── rights: (length: 1)
+ │ └── @ LocalVariableTargetNode (location: (1,6)-(1,7))
+ │ ├── name: :b
+ │ └── depth: 0
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (1,8)-(1,9) = "="
+ └── value:
+ @ CallNode (location: (1,10)-(1,11))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :c
+ ├── message_loc: (1,10)-(1,11) = "c"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/messy_op_asgn_lineno.txt b/test/prism/snapshots/seattlerb/messy_op_asgn_lineno.txt
new file mode 100644
index 0000000000..7a3e9affb5
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/messy_op_asgn_lineno.txt
@@ -0,0 +1,60 @@
+@ ProgramNode (location: (1,0)-(1,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,15))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,15))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,15))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ ParenthesesNode (location: (1,2)-(1,15))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,3)-(1,14))
+ │ │ └── body: (length: 1)
+ │ │ └── @ ConstantPathOperatorWriteNode (location: (1,3)-(1,14))
+ │ │ ├── target:
+ │ │ │ @ ConstantPathNode (location: (1,3)-(1,7))
+ │ │ │ ├── parent:
+ │ │ │ │ @ ConstantReadNode (location: (1,3)-(1,4))
+ │ │ │ │ └── name: :B
+ │ │ │ ├── child:
+ │ │ │ │ @ ConstantReadNode (location: (1,6)-(1,7))
+ │ │ │ │ └── name: :C
+ │ │ │ └── delimiter_loc: (1,4)-(1,6) = "::"
+ │ │ ├── operator_loc: (1,8)-(1,10) = "*="
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (1,11)-(1,14))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :d
+ │ │ │ ├── message_loc: (1,11)-(1,12) = "d"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (1,13)-(1,14))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (1,13)-(1,14))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :e
+ │ │ │ │ ├── message_loc: (1,13)-(1,14) = "e"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator: :*
+ │ ├── opening_loc: (1,2)-(1,3) = "("
+ │ └── closing_loc: (1,14)-(1,15) = ")"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/method_call_assoc_trailing_comma.txt b/test/prism/snapshots/seattlerb/method_call_assoc_trailing_comma.txt
new file mode 100644
index 0000000000..da10a474c3
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/method_call_assoc_trailing_comma.txt
@@ -0,0 +1,41 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,10))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,1)-(1,2) = "."
+ ├── name: :f
+ ├── message_loc: (1,2)-(1,3) = "f"
+ ├── opening_loc: (1,3)-(1,4) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ KeywordHashNode (location: (1,4)-(1,8))
+ │ ├── flags: ∅
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (1,4)-(1,8))
+ │ ├── key:
+ │ │ @ IntegerNode (location: (1,4)-(1,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,7)-(1,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ └── operator_loc: (1,5)-(1,7) = "=>"
+ ├── closing_loc: (1,9)-(1,10) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/method_call_trailing_comma.txt b/test/prism/snapshots/seattlerb/method_call_trailing_comma.txt
new file mode 100644
index 0000000000..edf79062cf
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/method_call_trailing_comma.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (1,0)-(1,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,7))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,7))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,1)-(1,2) = "."
+ ├── name: :f
+ ├── message_loc: (1,2)-(1,3) = "f"
+ ├── opening_loc: (1,3)-(1,4) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,5))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,4)-(1,5))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: (1,6)-(1,7) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/mlhs_back_anonsplat.txt b/test/prism/snapshots/seattlerb/mlhs_back_anonsplat.txt
new file mode 100644
index 0000000000..600f7f717b
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/mlhs_back_anonsplat.txt
@@ -0,0 +1,35 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: [:a, :b, :c]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ MultiWriteNode (location: (1,0)-(1,14))
+ ├── lefts: (length: 3)
+ │ ├── @ LocalVariableTargetNode (location: (1,0)-(1,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── @ LocalVariableTargetNode (location: (1,3)-(1,4))
+ │ │ ├── name: :b
+ │ │ └── depth: 0
+ │ └── @ LocalVariableTargetNode (location: (1,6)-(1,7))
+ │ ├── name: :c
+ │ └── depth: 0
+ ├── rest:
+ │ @ SplatNode (location: (1,9)-(1,10))
+ │ ├── operator_loc: (1,9)-(1,10) = "*"
+ │ └── expression: ∅
+ ├── rights: (length: 0)
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (1,11)-(1,12) = "="
+ └── value:
+ @ CallNode (location: (1,13)-(1,14))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,13)-(1,14) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/mlhs_back_splat.txt b/test/prism/snapshots/seattlerb/mlhs_back_splat.txt
new file mode 100644
index 0000000000..f5d3fe5ae9
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/mlhs_back_splat.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(1,15))
+├── locals: [:a, :b, :c, :s]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,15))
+ └── body: (length: 1)
+ └── @ MultiWriteNode (location: (1,0)-(1,15))
+ ├── lefts: (length: 3)
+ │ ├── @ LocalVariableTargetNode (location: (1,0)-(1,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── @ LocalVariableTargetNode (location: (1,3)-(1,4))
+ │ │ ├── name: :b
+ │ │ └── depth: 0
+ │ └── @ LocalVariableTargetNode (location: (1,6)-(1,7))
+ │ ├── name: :c
+ │ └── depth: 0
+ ├── rest:
+ │ @ SplatNode (location: (1,9)-(1,11))
+ │ ├── operator_loc: (1,9)-(1,10) = "*"
+ │ └── expression:
+ │ @ LocalVariableTargetNode (location: (1,10)-(1,11))
+ │ ├── name: :s
+ │ └── depth: 0
+ ├── rights: (length: 0)
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (1,12)-(1,13) = "="
+ └── value:
+ @ CallNode (location: (1,14)-(1,15))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,14)-(1,15) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/mlhs_front_anonsplat.txt b/test/prism/snapshots/seattlerb/mlhs_front_anonsplat.txt
new file mode 100644
index 0000000000..d4797031a5
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/mlhs_front_anonsplat.txt
@@ -0,0 +1,35 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: [:x, :y, :z]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ MultiWriteNode (location: (1,0)-(1,14))
+ ├── lefts: (length: 0)
+ ├── rest:
+ │ @ SplatNode (location: (1,0)-(1,1))
+ │ ├── operator_loc: (1,0)-(1,1) = "*"
+ │ └── expression: ∅
+ ├── rights: (length: 3)
+ │ ├── @ LocalVariableTargetNode (location: (1,3)-(1,4))
+ │ │ ├── name: :x
+ │ │ └── depth: 0
+ │ ├── @ LocalVariableTargetNode (location: (1,6)-(1,7))
+ │ │ ├── name: :y
+ │ │ └── depth: 0
+ │ └── @ LocalVariableTargetNode (location: (1,9)-(1,10))
+ │ ├── name: :z
+ │ └── depth: 0
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (1,11)-(1,12) = "="
+ └── value:
+ @ CallNode (location: (1,13)-(1,14))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,13)-(1,14) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/mlhs_front_splat.txt b/test/prism/snapshots/seattlerb/mlhs_front_splat.txt
new file mode 100644
index 0000000000..47a7b8da7c
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/mlhs_front_splat.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(1,15))
+├── locals: [:s, :x, :y, :z]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,15))
+ └── body: (length: 1)
+ └── @ MultiWriteNode (location: (1,0)-(1,15))
+ ├── lefts: (length: 0)
+ ├── rest:
+ │ @ SplatNode (location: (1,0)-(1,2))
+ │ ├── operator_loc: (1,0)-(1,1) = "*"
+ │ └── expression:
+ │ @ LocalVariableTargetNode (location: (1,1)-(1,2))
+ │ ├── name: :s
+ │ └── depth: 0
+ ├── rights: (length: 3)
+ │ ├── @ LocalVariableTargetNode (location: (1,4)-(1,5))
+ │ │ ├── name: :x
+ │ │ └── depth: 0
+ │ ├── @ LocalVariableTargetNode (location: (1,7)-(1,8))
+ │ │ ├── name: :y
+ │ │ └── depth: 0
+ │ └── @ LocalVariableTargetNode (location: (1,10)-(1,11))
+ │ ├── name: :z
+ │ └── depth: 0
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (1,12)-(1,13) = "="
+ └── value:
+ @ CallNode (location: (1,14)-(1,15))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,14)-(1,15) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/mlhs_keyword.txt b/test/prism/snapshots/seattlerb/mlhs_keyword.txt
new file mode 100644
index 0000000000..6142640b2c
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/mlhs_keyword.txt
@@ -0,0 +1,30 @@
+@ ProgramNode (location: (1,0)-(1,16))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,16))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,16))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,1)-(1,2) = "."
+ ├── name: :!=
+ ├── message_loc: (1,2)-(1,4) = "!="
+ ├── opening_loc: (1,4)-(1,5) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,5)-(1,15))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ TrueNode (location: (1,5)-(1,9))
+ │ └── @ TrueNode (location: (1,11)-(1,15))
+ ├── closing_loc: (1,15)-(1,16) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/mlhs_mid_anonsplat.txt b/test/prism/snapshots/seattlerb/mlhs_mid_anonsplat.txt
new file mode 100644
index 0000000000..b6306e1674
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/mlhs_mid_anonsplat.txt
@@ -0,0 +1,44 @@
+@ ProgramNode (location: (1,0)-(1,23))
+├── locals: [:a, :b, :c, :x, :y, :z]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,23))
+ └── body: (length: 1)
+ └── @ MultiWriteNode (location: (1,0)-(1,23))
+ ├── lefts: (length: 3)
+ │ ├── @ LocalVariableTargetNode (location: (1,0)-(1,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── @ LocalVariableTargetNode (location: (1,3)-(1,4))
+ │ │ ├── name: :b
+ │ │ └── depth: 0
+ │ └── @ LocalVariableTargetNode (location: (1,6)-(1,7))
+ │ ├── name: :c
+ │ └── depth: 0
+ ├── rest:
+ │ @ SplatNode (location: (1,9)-(1,10))
+ │ ├── operator_loc: (1,9)-(1,10) = "*"
+ │ └── expression: ∅
+ ├── rights: (length: 3)
+ │ ├── @ LocalVariableTargetNode (location: (1,12)-(1,13))
+ │ │ ├── name: :x
+ │ │ └── depth: 0
+ │ ├── @ LocalVariableTargetNode (location: (1,15)-(1,16))
+ │ │ ├── name: :y
+ │ │ └── depth: 0
+ │ └── @ LocalVariableTargetNode (location: (1,18)-(1,19))
+ │ ├── name: :z
+ │ └── depth: 0
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (1,20)-(1,21) = "="
+ └── value:
+ @ CallNode (location: (1,22)-(1,23))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,22)-(1,23) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/mlhs_mid_splat.txt b/test/prism/snapshots/seattlerb/mlhs_mid_splat.txt
new file mode 100644
index 0000000000..1dae24d911
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/mlhs_mid_splat.txt
@@ -0,0 +1,47 @@
+@ ProgramNode (location: (1,0)-(1,24))
+├── locals: [:a, :b, :c, :s, :x, :y, :z]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,24))
+ └── body: (length: 1)
+ └── @ MultiWriteNode (location: (1,0)-(1,24))
+ ├── lefts: (length: 3)
+ │ ├── @ LocalVariableTargetNode (location: (1,0)-(1,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── @ LocalVariableTargetNode (location: (1,3)-(1,4))
+ │ │ ├── name: :b
+ │ │ └── depth: 0
+ │ └── @ LocalVariableTargetNode (location: (1,6)-(1,7))
+ │ ├── name: :c
+ │ └── depth: 0
+ ├── rest:
+ │ @ SplatNode (location: (1,9)-(1,11))
+ │ ├── operator_loc: (1,9)-(1,10) = "*"
+ │ └── expression:
+ │ @ LocalVariableTargetNode (location: (1,10)-(1,11))
+ │ ├── name: :s
+ │ └── depth: 0
+ ├── rights: (length: 3)
+ │ ├── @ LocalVariableTargetNode (location: (1,13)-(1,14))
+ │ │ ├── name: :x
+ │ │ └── depth: 0
+ │ ├── @ LocalVariableTargetNode (location: (1,16)-(1,17))
+ │ │ ├── name: :y
+ │ │ └── depth: 0
+ │ └── @ LocalVariableTargetNode (location: (1,19)-(1,20))
+ │ ├── name: :z
+ │ └── depth: 0
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (1,21)-(1,22) = "="
+ └── value:
+ @ CallNode (location: (1,23)-(1,24))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,23)-(1,24) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/mlhs_rescue.txt b/test/prism/snapshots/seattlerb/mlhs_rescue.txt
new file mode 100644
index 0000000000..bd983cd3f9
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/mlhs_rescue.txt
@@ -0,0 +1,36 @@
+@ ProgramNode (location: (1,0)-(1,18))
+├── locals: [:a, :b]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,18))
+ └── body: (length: 1)
+ └── @ MultiWriteNode (location: (1,0)-(1,18))
+ ├── lefts: (length: 2)
+ │ ├── @ LocalVariableTargetNode (location: (1,0)-(1,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ └── @ LocalVariableTargetNode (location: (1,3)-(1,4))
+ │ ├── name: :b
+ │ └── depth: 0
+ ├── rest: ∅
+ ├── rights: (length: 0)
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (1,5)-(1,6) = "="
+ └── value:
+ @ RescueModifierNode (location: (1,7)-(1,18))
+ ├── expression:
+ │ @ CallNode (location: (1,7)-(1,8))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (1,7)-(1,8) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── keyword_loc: (1,9)-(1,15) = "rescue"
+ └── rescue_expression:
+ @ IntegerNode (location: (1,16)-(1,18))
+ ├── flags: decimal
+ └── value: 42
diff --git a/test/prism/snapshots/seattlerb/module_comments.txt b/test/prism/snapshots/seattlerb/module_comments.txt
new file mode 100644
index 0000000000..2785187a29
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/module_comments.txt
@@ -0,0 +1,29 @@
+@ ProgramNode (location: (5,0)-(10,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (5,0)-(10,3))
+ └── body: (length: 1)
+ └── @ ModuleNode (location: (5,0)-(10,3))
+ ├── locals: []
+ ├── module_keyword_loc: (5,0)-(5,6) = "module"
+ ├── constant_path:
+ │ @ ConstantReadNode (location: (5,7)-(5,8))
+ │ └── name: :X
+ ├── body:
+ │ @ StatementsNode (location: (7,2)-(9,5))
+ │ └── body: (length: 1)
+ │ └── @ DefNode (location: (7,2)-(9,5))
+ │ ├── name: :blah
+ │ ├── name_loc: (7,6)-(7,10) = "blah"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (7,2)-(7,5) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (9,2)-(9,5) = "end"
+ ├── end_keyword_loc: (10,0)-(10,3) = "end"
+ └── name: :X
diff --git a/test/prism/snapshots/seattlerb/multiline_hash_declaration.txt b/test/prism/snapshots/seattlerb/multiline_hash_declaration.txt
new file mode 100644
index 0000000000..79b0ef5d23
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/multiline_hash_declaration.txt
@@ -0,0 +1,95 @@
+@ ProgramNode (location: (1,0)-(8,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(8,12))
+ └── body: (length: 3)
+ ├── @ CallNode (location: (1,0)-(3,2))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (1,0)-(1,1) = "f"
+ │ ├── opening_loc: (1,1)-(1,2) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,2)-(3,1))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (1,2)-(3,1))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (1,2)-(3,1))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (1,2)-(1,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (1,2)-(1,7) = "state"
+ │ │ │ ├── closing_loc: (1,7)-(1,8) = ":"
+ │ │ │ └── unescaped: "state"
+ │ │ ├── value:
+ │ │ │ @ HashNode (location: (2,1)-(3,1))
+ │ │ │ ├── opening_loc: (2,1)-(2,2) = "{"
+ │ │ │ ├── elements: (length: 0)
+ │ │ │ └── closing_loc: (3,0)-(3,1) = "}"
+ │ │ └── operator_loc: ∅
+ │ ├── closing_loc: (3,1)-(3,2) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (5,0)-(6,2))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (5,0)-(5,1) = "f"
+ │ ├── opening_loc: (5,1)-(5,2) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (5,2)-(6,1))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (5,2)-(6,1))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (5,2)-(6,1))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (5,2)-(5,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (5,2)-(5,7) = "state"
+ │ │ │ ├── closing_loc: (5,7)-(5,8) = ":"
+ │ │ │ └── unescaped: "state"
+ │ │ ├── value:
+ │ │ │ @ HashNode (location: (5,9)-(6,1))
+ │ │ │ ├── opening_loc: (5,9)-(5,10) = "{"
+ │ │ │ ├── elements: (length: 0)
+ │ │ │ └── closing_loc: (6,0)-(6,1) = "}"
+ │ │ └── operator_loc: ∅
+ │ ├── closing_loc: (6,1)-(6,2) = ")"
+ │ └── block: ∅
+ └── @ CallNode (location: (8,0)-(8,12))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (8,0)-(8,1) = "f"
+ ├── opening_loc: (8,1)-(8,2) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (8,2)-(8,11))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ KeywordHashNode (location: (8,2)-(8,11))
+ │ ├── flags: symbol_keys
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (8,2)-(8,11))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (8,2)-(8,8))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (8,2)-(8,7) = "state"
+ │ │ ├── closing_loc: (8,7)-(8,8) = ":"
+ │ │ └── unescaped: "state"
+ │ ├── value:
+ │ │ @ HashNode (location: (8,9)-(8,11))
+ │ │ ├── opening_loc: (8,9)-(8,10) = "{"
+ │ │ ├── elements: (length: 0)
+ │ │ └── closing_loc: (8,10)-(8,11) = "}"
+ │ └── operator_loc: ∅
+ ├── closing_loc: (8,11)-(8,12) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/non_interpolated_symbol_array_line_breaks.txt b/test/prism/snapshots/seattlerb/non_interpolated_symbol_array_line_breaks.txt
new file mode 100644
index 0000000000..cbf4ec4a08
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/non_interpolated_symbol_array_line_breaks.txt
@@ -0,0 +1,25 @@
+@ ProgramNode (location: (1,0)-(5,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,1))
+ └── body: (length: 2)
+ ├── @ ArrayNode (location: (1,0)-(4,1))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ SymbolNode (location: (2,0)-(2,1))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (2,0)-(2,1) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ └── @ SymbolNode (location: (3,0)-(3,1))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (3,0)-(3,1) = "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b"
+ │ ├── opening_loc: (1,0)-(1,3) = "%i("
+ │ └── closing_loc: (4,0)-(4,1) = ")"
+ └── @ IntegerNode (location: (5,0)-(5,1))
+ ├── flags: decimal
+ └── value: 1
diff --git a/test/prism/snapshots/seattlerb/non_interpolated_word_array_line_breaks.txt b/test/prism/snapshots/seattlerb/non_interpolated_word_array_line_breaks.txt
new file mode 100644
index 0000000000..e82f250098
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/non_interpolated_word_array_line_breaks.txt
@@ -0,0 +1,25 @@
+@ ProgramNode (location: (1,0)-(5,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,1))
+ └── body: (length: 2)
+ ├── @ ArrayNode (location: (1,0)-(4,1))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ StringNode (location: (2,0)-(2,1))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (2,0)-(2,1) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ └── @ StringNode (location: (3,0)-(3,1))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (3,0)-(3,1) = "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b"
+ │ ├── opening_loc: (1,0)-(1,3) = "%w("
+ │ └── closing_loc: (4,0)-(4,1) = ")"
+ └── @ IntegerNode (location: (5,0)-(5,1))
+ ├── flags: decimal
+ └── value: 1
diff --git a/test/prism/snapshots/seattlerb/op_asgn_command_call.txt b/test/prism/snapshots/seattlerb/op_asgn_command_call.txt
new file mode 100644
index 0000000000..54aa06214f
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/op_asgn_command_call.txt
@@ -0,0 +1,37 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ LocalVariableOrWriteNode (location: (1,0)-(1,11))
+ ├── name_loc: (1,0)-(1,1) = "a"
+ ├── operator_loc: (1,2)-(1,5) = "||="
+ ├── value:
+ │ @ CallNode (location: (1,6)-(1,11))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,6)-(1,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (1,6)-(1,7) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (1,7)-(1,8) = "."
+ │ ├── name: :c
+ │ ├── message_loc: (1,8)-(1,9) = "c"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,10)-(1,11))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,10)-(1,11))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── name: :a
+ └── depth: 0
diff --git a/test/prism/snapshots/seattlerb/op_asgn_dot_ident_command_call.txt b/test/prism/snapshots/seattlerb/op_asgn_dot_ident_command_call.txt
new file mode 100644
index 0000000000..324c042b00
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/op_asgn_dot_ident_command_call.txt
@@ -0,0 +1,32 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ CallOrWriteNode (location: (1,0)-(1,11))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ ConstantReadNode (location: (1,0)-(1,1))
+ │ └── name: :A
+ ├── call_operator_loc: (1,1)-(1,2) = "."
+ ├── message_loc: (1,2)-(1,3) = "B"
+ ├── read_name: :B
+ ├── write_name: :B=
+ ├── operator_loc: (1,4)-(1,7) = "||="
+ └── value:
+ @ CallNode (location: (1,8)-(1,11))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :c
+ ├── message_loc: (1,8)-(1,9) = "c"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,10)-(1,11))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,10)-(1,11))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/op_asgn_index_command_call.txt b/test/prism/snapshots/seattlerb/op_asgn_index_command_call.txt
new file mode 100644
index 0000000000..ddee4cde49
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/op_asgn_index_command_call.txt
@@ -0,0 +1,53 @@
+@ ProgramNode (location: (1,0)-(1,16))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,16))
+ └── body: (length: 1)
+ └── @ IndexOrWriteNode (location: (1,0)-(1,16))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── opening_loc: (1,1)-(1,2) = "["
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,4))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ SymbolNode (location: (1,2)-(1,4))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,2)-(1,3) = ":"
+ │ ├── value_loc: (1,3)-(1,4) = "b"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "b"
+ ├── closing_loc: (1,4)-(1,5) = "]"
+ ├── block: ∅
+ ├── operator_loc: (1,6)-(1,9) = "||="
+ └── value:
+ @ CallNode (location: (1,10)-(1,16))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :c
+ ├── message_loc: (1,10)-(1,11) = "c"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,12)-(1,16))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ IntegerNode (location: (1,12)-(1,13))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── @ IntegerNode (location: (1,15)-(1,16))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/op_asgn_primary_colon_const_command_call.txt b/test/prism/snapshots/seattlerb/op_asgn_primary_colon_const_command_call.txt
new file mode 100644
index 0000000000..8e6df3e812
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/op_asgn_primary_colon_const_command_call.txt
@@ -0,0 +1,41 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ ConstantPathOperatorWriteNode (location: (1,0)-(1,11))
+ ├── target:
+ │ @ ConstantPathNode (location: (1,0)-(1,4))
+ │ ├── parent:
+ │ │ @ ConstantReadNode (location: (1,0)-(1,1))
+ │ │ └── name: :A
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (1,3)-(1,4))
+ │ │ └── name: :B
+ │ └── delimiter_loc: (1,1)-(1,3) = "::"
+ ├── operator_loc: (1,5)-(1,7) = "*="
+ ├── value:
+ │ @ CallNode (location: (1,8)-(1,11))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :c
+ │ ├── message_loc: (1,8)-(1,9) = "c"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,10)-(1,11))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (1,10)-(1,11))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :d
+ │ │ ├── message_loc: (1,10)-(1,11) = "d"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── operator: :*
diff --git a/test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier1.txt b/test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier1.txt
new file mode 100644
index 0000000000..0daadcf6ff
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier1.txt
@@ -0,0 +1,20 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ CallOperatorWriteNode (location: (1,0)-(1,9))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ ConstantReadNode (location: (1,0)-(1,1))
+ │ └── name: :A
+ ├── call_operator_loc: (1,1)-(1,3) = "::"
+ ├── message_loc: (1,3)-(1,4) = "b"
+ ├── read_name: :b
+ ├── write_name: :b=
+ ├── operator: :+
+ ├── operator_loc: (1,5)-(1,7) = "+="
+ └── value:
+ @ IntegerNode (location: (1,8)-(1,9))
+ ├── flags: decimal
+ └── value: 1
diff --git a/test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier_command_call.txt b/test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier_command_call.txt
new file mode 100644
index 0000000000..ea8603165b
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier_command_call.txt
@@ -0,0 +1,40 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ CallOperatorWriteNode (location: (1,0)-(1,11))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ ConstantReadNode (location: (1,0)-(1,1))
+ │ └── name: :A
+ ├── call_operator_loc: (1,1)-(1,3) = "::"
+ ├── message_loc: (1,3)-(1,4) = "b"
+ ├── read_name: :b
+ ├── write_name: :b=
+ ├── operator: :*
+ ├── operator_loc: (1,5)-(1,7) = "*="
+ └── value:
+ @ CallNode (location: (1,8)-(1,11))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :c
+ ├── message_loc: (1,8)-(1,9) = "c"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,10)-(1,11))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (1,10)-(1,11))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :d
+ │ ├── message_loc: (1,10)-(1,11) = "d"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/op_asgn_val_dot_ident_command_call.txt b/test/prism/snapshots/seattlerb/op_asgn_val_dot_ident_command_call.txt
new file mode 100644
index 0000000000..b3b5709193
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/op_asgn_val_dot_ident_command_call.txt
@@ -0,0 +1,40 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ CallOrWriteNode (location: (1,0)-(1,11))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,1)-(1,2) = "."
+ ├── message_loc: (1,2)-(1,3) = "b"
+ ├── read_name: :b
+ ├── write_name: :b=
+ ├── operator_loc: (1,4)-(1,7) = "||="
+ └── value:
+ @ CallNode (location: (1,8)-(1,11))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :c
+ ├── message_loc: (1,8)-(1,9) = "c"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,10)-(1,11))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,10)-(1,11))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/parse_def_special_name.txt b/test/prism/snapshots/seattlerb/parse_def_special_name.txt
new file mode 100644
index 0000000000..dfbfe8a391
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_def_special_name.txt
@@ -0,0 +1,18 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,13))
+ ├── name: :next
+ ├── name_loc: (1,4)-(1,8) = "next"
+ ├── receiver: ∅
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,10)-(1,13) = "end"
diff --git a/test/prism/snapshots/seattlerb/parse_if_not_canonical.txt b/test/prism/snapshots/seattlerb/parse_if_not_canonical.txt
new file mode 100644
index 0000000000..763bd24efd
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_if_not_canonical.txt
@@ -0,0 +1,62 @@
+@ ProgramNode (location: (1,0)-(2,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,3))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(2,3))
+ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ ├── predicate:
+ │ @ CallNode (location: (1,3)-(1,15))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,7)-(1,15))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (1,7)-(1,10))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :var
+ │ │ │ ├── message_loc: (1,7)-(1,10) = "var"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (1,10)-(1,11) = "."
+ │ │ ├── name: :nil?
+ │ │ ├── message_loc: (1,11)-(1,15) = "nil?"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!
+ │ ├── message_loc: (1,3)-(1,6) = "not"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── then_keyword_loc: (1,16)-(1,20) = "then"
+ ├── statements:
+ │ @ StatementsNode (location: (1,21)-(1,26))
+ │ └── body: (length: 1)
+ │ └── @ StringNode (location: (1,21)-(1,26))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,21)-(1,22) = "'"
+ │ ├── content_loc: (1,22)-(1,25) = "foo"
+ │ ├── closing_loc: (1,25)-(1,26) = "'"
+ │ └── unescaped: "foo"
+ ├── consequent:
+ │ @ ElseNode (location: (1,27)-(2,3))
+ │ ├── else_keyword_loc: (1,27)-(1,31) = "else"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,32)-(1,37))
+ │ │ └── body: (length: 1)
+ │ │ └── @ StringNode (location: (1,32)-(1,37))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,32)-(1,33) = "'"
+ │ │ ├── content_loc: (1,33)-(1,36) = "bar"
+ │ │ ├── closing_loc: (1,36)-(1,37) = "'"
+ │ │ └── unescaped: "bar"
+ │ └── end_keyword_loc: (2,0)-(2,3) = "end"
+ └── end_keyword_loc: (2,0)-(2,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/parse_if_not_noncanonical.txt b/test/prism/snapshots/seattlerb/parse_if_not_noncanonical.txt
new file mode 100644
index 0000000000..763bd24efd
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_if_not_noncanonical.txt
@@ -0,0 +1,62 @@
+@ ProgramNode (location: (1,0)-(2,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,3))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(2,3))
+ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ ├── predicate:
+ │ @ CallNode (location: (1,3)-(1,15))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,7)-(1,15))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (1,7)-(1,10))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :var
+ │ │ │ ├── message_loc: (1,7)-(1,10) = "var"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (1,10)-(1,11) = "."
+ │ │ ├── name: :nil?
+ │ │ ├── message_loc: (1,11)-(1,15) = "nil?"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!
+ │ ├── message_loc: (1,3)-(1,6) = "not"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── then_keyword_loc: (1,16)-(1,20) = "then"
+ ├── statements:
+ │ @ StatementsNode (location: (1,21)-(1,26))
+ │ └── body: (length: 1)
+ │ └── @ StringNode (location: (1,21)-(1,26))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,21)-(1,22) = "'"
+ │ ├── content_loc: (1,22)-(1,25) = "foo"
+ │ ├── closing_loc: (1,25)-(1,26) = "'"
+ │ └── unescaped: "foo"
+ ├── consequent:
+ │ @ ElseNode (location: (1,27)-(2,3))
+ │ ├── else_keyword_loc: (1,27)-(1,31) = "else"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,32)-(1,37))
+ │ │ └── body: (length: 1)
+ │ │ └── @ StringNode (location: (1,32)-(1,37))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,32)-(1,33) = "'"
+ │ │ ├── content_loc: (1,33)-(1,36) = "bar"
+ │ │ ├── closing_loc: (1,36)-(1,37) = "'"
+ │ │ └── unescaped: "bar"
+ │ └── end_keyword_loc: (2,0)-(2,3) = "end"
+ └── end_keyword_loc: (2,0)-(2,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/parse_line_block.txt b/test/prism/snapshots/seattlerb/parse_line_block.txt
new file mode 100644
index 0000000000..623c08d50e
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_block.txt
@@ -0,0 +1,30 @@
+@ ProgramNode (location: (1,0)-(2,3))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,3))
+ └── body: (length: 2)
+ ├── @ LocalVariableWriteNode (location: (1,0)-(1,6))
+ │ ├── name: :a
+ │ ├── depth: 0
+ │ ├── name_loc: (1,0)-(1,1) = "a"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,4)-(1,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 42
+ │ └── operator_loc: (1,2)-(1,3) = "="
+ └── @ CallNode (location: (2,0)-(2,3))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :p
+ ├── message_loc: (2,0)-(2,1) = "p"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (2,2)-(2,3))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ LocalVariableReadNode (location: (2,2)-(2,3))
+ │ ├── name: :a
+ │ └── depth: 0
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/parse_line_block_inline_comment.txt b/test/prism/snapshots/seattlerb/parse_line_block_inline_comment.txt
new file mode 100644
index 0000000000..8495527cf4
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_block_inline_comment.txt
@@ -0,0 +1,35 @@
+@ ProgramNode (location: (1,0)-(3,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,1))
+ └── body: (length: 3)
+ ├── @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (2,0)-(2,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :b
+ │ ├── message_loc: (2,0)-(2,1) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (3,0)-(3,1))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :c
+ ├── message_loc: (3,0)-(3,1) = "c"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/parse_line_block_inline_comment_leading_newlines.txt b/test/prism/snapshots/seattlerb/parse_line_block_inline_comment_leading_newlines.txt
new file mode 100644
index 0000000000..f531a73a58
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_block_inline_comment_leading_newlines.txt
@@ -0,0 +1,35 @@
+@ ProgramNode (location: (4,0)-(7,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (4,0)-(7,1))
+ └── body: (length: 3)
+ ├── @ CallNode (location: (4,0)-(4,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (4,0)-(4,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (5,0)-(5,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :b
+ │ ├── message_loc: (5,0)-(5,1) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (7,0)-(7,1))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :c
+ ├── message_loc: (7,0)-(7,1) = "c"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/parse_line_block_inline_multiline_comment.txt b/test/prism/snapshots/seattlerb/parse_line_block_inline_multiline_comment.txt
new file mode 100644
index 0000000000..d4e962b355
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_block_inline_multiline_comment.txt
@@ -0,0 +1,35 @@
+@ ProgramNode (location: (1,0)-(4,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,1))
+ └── body: (length: 3)
+ ├── @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (2,0)-(2,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :b
+ │ ├── message_loc: (2,0)-(2,1) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (4,0)-(4,1))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :c
+ ├── message_loc: (4,0)-(4,1) = "c"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/parse_line_call_ivar_arg_no_parens_line_break.txt b/test/prism/snapshots/seattlerb/parse_line_call_ivar_arg_no_parens_line_break.txt
new file mode 100644
index 0000000000..a08f5419f1
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_call_ivar_arg_no_parens_line_break.txt
@@ -0,0 +1,20 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,4))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,4))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ InstanceVariableReadNode (location: (1,2)-(1,4))
+ │ └── name: :@b
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/parse_line_call_ivar_line_break_paren.txt b/test/prism/snapshots/seattlerb/parse_line_call_ivar_line_break_paren.txt
new file mode 100644
index 0000000000..dd58d92d3a
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_call_ivar_line_break_paren.txt
@@ -0,0 +1,20 @@
+@ ProgramNode (location: (1,0)-(2,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,1))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(2,1))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: (1,1)-(1,2) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,4))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ InstanceVariableReadNode (location: (1,2)-(1,4))
+ │ └── name: :@b
+ ├── closing_loc: (2,0)-(2,1) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/parse_line_call_no_args.txt b/test/prism/snapshots/seattlerb/parse_line_call_no_args.txt
new file mode 100644
index 0000000000..8a0fcd63af
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_call_no_args.txt
@@ -0,0 +1,61 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(3,3))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(3,3))
+ ├── locals: [:x, :y]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,5)-(1,11))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,6)-(1,10))
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ RequiredParameterNode (location: (1,6)-(1,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :x
+ │ │ │ └── @ RequiredParameterNode (location: (1,9)-(1,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :y
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,5)-(1,6) = "|"
+ │ └── closing_loc: (1,10)-(1,11) = "|"
+ ├── body:
+ │ @ StatementsNode (location: (2,2)-(2,7))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (2,2)-(2,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (2,2)-(2,3))
+ │ │ ├── name: :x
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (2,4)-(2,5) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (2,6)-(2,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (2,6)-(2,7))
+ │ │ ├── name: :y
+ │ │ └── depth: 0
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── opening_loc: (1,2)-(1,4) = "do"
+ └── closing_loc: (3,0)-(3,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/parse_line_defn_complex.txt b/test/prism/snapshots/seattlerb/parse_line_defn_complex.txt
new file mode 100644
index 0000000000..8199eb2449
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_defn_complex.txt
@@ -0,0 +1,67 @@
+@ ProgramNode (location: (1,0)-(5,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,3))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(5,3))
+ ├── name: :x
+ ├── name_loc: (1,4)-(1,5) = "x"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,7))
+ │ ├── requireds: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (1,6)-(1,7))
+ │ │ ├── flags: ∅
+ │ │ └── name: :y
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (2,2)-(4,10))
+ │ └── body: (length: 3)
+ │ ├── @ CallNode (location: (2,2)-(2,6))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :p
+ │ │ ├── message_loc: (2,2)-(2,3) = "p"
+ │ │ ├── opening_loc: (2,3)-(2,4) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (2,4)-(2,5))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ LocalVariableReadNode (location: (2,4)-(2,5))
+ │ │ │ ├── name: :y
+ │ │ │ └── depth: 0
+ │ │ ├── closing_loc: (2,5)-(2,6) = ")"
+ │ │ └── block: ∅
+ │ ├── @ LocalVariableOperatorWriteNode (location: (3,2)-(3,8))
+ │ │ ├── name_loc: (3,2)-(3,3) = "y"
+ │ │ ├── operator_loc: (3,4)-(3,6) = "*="
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (3,7)-(3,8))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── name: :y
+ │ │ ├── operator: :*
+ │ │ └── depth: 0
+ │ └── @ ReturnNode (location: (4,2)-(4,10))
+ │ ├── flags: redundant
+ │ ├── keyword_loc: (4,2)-(4,8) = "return"
+ │ └── arguments:
+ │ @ ArgumentsNode (location: (4,9)-(4,10))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ LocalVariableReadNode (location: (4,9)-(4,10))
+ │ ├── name: :y
+ │ └── depth: 0
+ ├── locals: [:y]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,5)-(1,6) = "("
+ ├── rparen_loc: (1,7)-(1,8) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (5,0)-(5,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/parse_line_defn_no_parens.txt b/test/prism/snapshots/seattlerb/parse_line_defn_no_parens.txt
new file mode 100644
index 0000000000..74240322ac
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_defn_no_parens.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (1,0)-(6,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(6,3))
+ └── body: (length: 2)
+ ├── @ DefNode (location: (1,0)-(3,3))
+ │ ├── name: :f
+ │ ├── name_loc: (1,4)-(1,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (3,0)-(3,3) = "end"
+ └── @ DefNode (location: (5,0)-(6,3))
+ ├── name: :f
+ ├── name_loc: (5,4)-(5,5) = "f"
+ ├── receiver: ∅
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (5,0)-(5,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (6,0)-(6,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/parse_line_defn_no_parens_args.txt b/test/prism/snapshots/seattlerb/parse_line_defn_no_parens_args.txt
new file mode 100644
index 0000000000..8445743293
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_defn_no_parens_args.txt
@@ -0,0 +1,29 @@
+@ ProgramNode (location: (1,0)-(2,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,3))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(2,3))
+ ├── name: :f
+ ├── name_loc: (1,4)-(1,5) = "f"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,7))
+ │ ├── requireds: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (1,6)-(1,7))
+ │ │ ├── flags: ∅
+ │ │ └── name: :a
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:a]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (2,0)-(2,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/parse_line_dot2.txt b/test/prism/snapshots/seattlerb/parse_line_dot2.txt
new file mode 100644
index 0000000000..9ccf5bdc99
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_dot2.txt
@@ -0,0 +1,51 @@
+@ ProgramNode (location: (1,0)-(5,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,1))
+ └── body: (length: 3)
+ ├── @ RangeNode (location: (1,0)-(2,1))
+ │ ├── flags: ∅
+ │ ├── left:
+ │ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 0
+ │ ├── right:
+ │ │ @ IntegerNode (location: (2,0)-(2,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 4
+ │ └── operator_loc: (1,1)-(1,3) = ".."
+ ├── @ RangeNode (location: (3,0)-(4,1))
+ │ ├── flags: ∅
+ │ ├── left:
+ │ │ @ CallNode (location: (3,0)-(3,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (3,0)-(3,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right:
+ │ │ @ CallNode (location: (4,0)-(4,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (4,0)-(4,1) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (3,1)-(3,3) = ".."
+ └── @ CallNode (location: (5,0)-(5,1))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :c
+ ├── message_loc: (5,0)-(5,1) = "c"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/parse_line_dot2_open.txt b/test/prism/snapshots/seattlerb/parse_line_dot2_open.txt
new file mode 100644
index 0000000000..f85fdd6d4b
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_dot2_open.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 3)
+ ├── @ RangeNode (location: (1,0)-(1,3))
+ │ ├── flags: ∅
+ │ ├── left:
+ │ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 0
+ │ ├── right: ∅
+ │ └── operator_loc: (1,1)-(1,3) = ".."
+ ├── @ RangeNode (location: (2,2)-(2,5))
+ │ ├── flags: ∅
+ │ ├── left:
+ │ │ @ CallNode (location: (2,2)-(2,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (2,2)-(2,3) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right: ∅
+ │ └── operator_loc: (2,3)-(2,5) = ".."
+ └── @ CallNode (location: (3,2)-(3,3))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :c
+ ├── message_loc: (3,2)-(3,3) = "c"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/parse_line_dot3.txt b/test/prism/snapshots/seattlerb/parse_line_dot3.txt
new file mode 100644
index 0000000000..6364c1f136
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_dot3.txt
@@ -0,0 +1,51 @@
+@ ProgramNode (location: (1,0)-(5,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,1))
+ └── body: (length: 3)
+ ├── @ RangeNode (location: (1,0)-(2,1))
+ │ ├── flags: exclude_end
+ │ ├── left:
+ │ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 0
+ │ ├── right:
+ │ │ @ IntegerNode (location: (2,0)-(2,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 4
+ │ └── operator_loc: (1,1)-(1,4) = "..."
+ ├── @ RangeNode (location: (3,0)-(4,1))
+ │ ├── flags: exclude_end
+ │ ├── left:
+ │ │ @ CallNode (location: (3,0)-(3,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (3,0)-(3,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right:
+ │ │ @ CallNode (location: (4,0)-(4,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (4,0)-(4,1) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (3,1)-(3,4) = "..."
+ └── @ CallNode (location: (5,0)-(5,1))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :c
+ ├── message_loc: (5,0)-(5,1) = "c"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/parse_line_dot3_open.txt b/test/prism/snapshots/seattlerb/parse_line_dot3_open.txt
new file mode 100644
index 0000000000..35759d12e3
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_dot3_open.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 3)
+ ├── @ RangeNode (location: (1,0)-(1,4))
+ │ ├── flags: exclude_end
+ │ ├── left:
+ │ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 0
+ │ ├── right: ∅
+ │ └── operator_loc: (1,1)-(1,4) = "..."
+ ├── @ RangeNode (location: (2,2)-(2,6))
+ │ ├── flags: exclude_end
+ │ ├── left:
+ │ │ @ CallNode (location: (2,2)-(2,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (2,2)-(2,3) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right: ∅
+ │ └── operator_loc: (2,3)-(2,6) = "..."
+ └── @ CallNode (location: (3,2)-(3,3))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :c
+ ├── message_loc: (3,2)-(3,3) = "c"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/parse_line_dstr_escaped_newline.txt b/test/prism/snapshots/seattlerb/parse_line_dstr_escaped_newline.txt
new file mode 100644
index 0000000000..aada5a9477
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_dstr_escaped_newline.txt
@@ -0,0 +1,21 @@
+@ ProgramNode (location: (1,0)-(3,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,4))
+ └── body: (length: 2)
+ ├── @ InterpolatedStringNode (location: (1,0)-(2,2))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,1) = "\""
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (1,1)-(1,4))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (1,1)-(1,4) = "a\\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ └── @ EmbeddedStatementsNode (location: (1,4)-(2,1))
+ │ │ ├── opening_loc: (1,4)-(1,6) = "\#{"
+ │ │ ├── statements: ∅
+ │ │ └── closing_loc: (2,0)-(2,1) = "}"
+ │ └── closing_loc: (2,1)-(2,2) = "\""
+ └── @ TrueNode (location: (3,0)-(3,4))
diff --git a/test/prism/snapshots/seattlerb/parse_line_dstr_soft_newline.txt b/test/prism/snapshots/seattlerb/parse_line_dstr_soft_newline.txt
new file mode 100644
index 0000000000..7ef56acb76
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_dstr_soft_newline.txt
@@ -0,0 +1,21 @@
+@ ProgramNode (location: (1,0)-(4,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,4))
+ └── body: (length: 2)
+ ├── @ InterpolatedStringNode (location: (1,0)-(3,2))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,1) = "\""
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (1,1)-(2,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (1,1)-(2,0) = "a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ └── @ EmbeddedStatementsNode (location: (2,0)-(3,1))
+ │ │ ├── opening_loc: (2,0)-(2,2) = "\#{"
+ │ │ ├── statements: ∅
+ │ │ └── closing_loc: (3,0)-(3,1) = "}"
+ │ └── closing_loc: (3,1)-(3,2) = "\""
+ └── @ TrueNode (location: (4,0)-(4,4))
diff --git a/test/prism/snapshots/seattlerb/parse_line_evstr_after_break.txt b/test/prism/snapshots/seattlerb/parse_line_evstr_after_break.txt
new file mode 100644
index 0000000000..82f461e340
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_evstr_after_break.txt
@@ -0,0 +1,37 @@
+@ ProgramNode (location: (1,0)-(2,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,6))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(2,6))
+ ├── flags: ∅
+ ├── opening_loc: ∅
+ ├── parts: (length: 2)
+ │ ├── @ StringNode (location: (1,0)-(1,3))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: (1,0)-(1,1) = "\""
+ │ │ ├── content_loc: (1,1)-(1,2) = "a"
+ │ │ ├── closing_loc: (1,2)-(1,3) = "\""
+ │ │ └── unescaped: "a"
+ │ └── @ InterpolatedStringNode (location: (2,0)-(2,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (2,0)-(2,1) = "\""
+ │ ├── parts: (length: 1)
+ │ │ └── @ EmbeddedStatementsNode (location: (2,1)-(2,5))
+ │ │ ├── opening_loc: (2,1)-(2,3) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (2,3)-(2,4))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (2,3)-(2,4))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (2,3)-(2,4) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── closing_loc: (2,4)-(2,5) = "}"
+ │ └── closing_loc: (2,5)-(2,6) = "\""
+ └── closing_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/parse_line_hash_lit.txt b/test/prism/snapshots/seattlerb/parse_line_hash_lit.txt
new file mode 100644
index 0000000000..0f95a607aa
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_hash_lit.txt
@@ -0,0 +1,22 @@
+@ ProgramNode (location: (1,0)-(3,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,1))
+ └── body: (length: 1)
+ └── @ HashNode (location: (1,0)-(3,1))
+ ├── opening_loc: (1,0)-(1,1) = "{"
+ ├── elements: (length: 1)
+ │ └── @ AssocNode (location: (2,0)-(2,8))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (2,0)-(2,3))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (2,0)-(2,1) = ":"
+ │ │ ├── value_loc: (2,1)-(2,3) = "s1"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "s1"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (2,7)-(2,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (2,4)-(2,6) = "=>"
+ └── closing_loc: (3,0)-(3,1) = "}"
diff --git a/test/prism/snapshots/seattlerb/parse_line_heredoc.txt b/test/prism/snapshots/seattlerb/parse_line_heredoc.txt
new file mode 100644
index 0000000000..ba00f01504
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_heredoc.txt
@@ -0,0 +1,43 @@
+@ ProgramNode (location: (1,6)-(4,17))
+├── locals: [:string]
+└── statements:
+ @ StatementsNode (location: (1,6)-(4,17))
+ └── body: (length: 2)
+ ├── @ LocalVariableWriteNode (location: (1,6)-(1,31))
+ │ ├── name: :string
+ │ ├── depth: 0
+ │ ├── name_loc: (1,6)-(1,12) = "string"
+ │ ├── value:
+ │ │ @ CallNode (location: (1,15)-(1,31))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ StringNode (location: (1,15)-(1,25))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (1,15)-(1,25) = "<<-HEREDOC"
+ │ │ │ ├── content_loc: (2,0)-(3,0) = " very long string\n"
+ │ │ │ ├── closing_loc: (3,0)-(4,0) = " HEREDOC\n"
+ │ │ │ └── unescaped: " very long string\n"
+ │ │ ├── call_operator_loc: (1,25)-(1,26) = "."
+ │ │ ├── name: :strip
+ │ │ ├── message_loc: (1,26)-(1,31) = "strip"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (1,13)-(1,14) = "="
+ └── @ CallNode (location: (4,6)-(4,17))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :puts
+ ├── message_loc: (4,6)-(4,10) = "puts"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (4,11)-(4,17))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ LocalVariableReadNode (location: (4,11)-(4,17))
+ │ ├── name: :string
+ │ └── depth: 0
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/parse_line_heredoc_evstr.txt b/test/prism/snapshots/seattlerb/parse_line_heredoc_evstr.txt
new file mode 100644
index 0000000000..b251b2b344
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_heredoc_evstr.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(1,4))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,4) = "<<-A"
+ ├── parts: (length: 3)
+ │ ├── @ StringNode (location: (2,0)-(3,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (2,0)-(3,0) = "a\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a\n"
+ │ ├── @ EmbeddedStatementsNode (location: (3,0)-(3,4))
+ │ │ ├── opening_loc: (3,0)-(3,2) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (3,2)-(3,3))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (3,2)-(3,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (3,2)-(3,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── closing_loc: (3,3)-(3,4) = "}"
+ │ └── @ StringNode (location: (3,4)-(4,0))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (3,4)-(4,0) = "\n"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "\n"
+ └── closing_loc: (4,0)-(5,0) = "A\n"
diff --git a/test/prism/snapshots/seattlerb/parse_line_heredoc_hardnewline.txt b/test/prism/snapshots/seattlerb/parse_line_heredoc_hardnewline.txt
new file mode 100644
index 0000000000..ad0f0dfd99
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_heredoc_hardnewline.txt
@@ -0,0 +1,22 @@
+@ ProgramNode (location: (1,0)-(6,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(6,3))
+ └── body: (length: 2)
+ ├── @ StringNode (location: (1,0)-(1,8))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,8) = "<<-EOFOO"
+ │ ├── content_loc: (2,0)-(3,0) = "\\n\\n\\n\\n\\n\\n\\n\\n\\n\n"
+ │ ├── closing_loc: (3,0)-(4,0) = "EOFOO\n"
+ │ └── unescaped: "\n\n\n\n\n\n\n\n\n\n"
+ └── @ ClassNode (location: (5,0)-(6,3))
+ ├── locals: []
+ ├── class_keyword_loc: (5,0)-(5,5) = "class"
+ ├── constant_path:
+ │ @ ConstantReadNode (location: (5,6)-(5,9))
+ │ └── name: :Foo
+ ├── inheritance_operator_loc: ∅
+ ├── superclass: ∅
+ ├── body: ∅
+ ├── end_keyword_loc: (6,0)-(6,3) = "end"
+ └── name: :Foo
diff --git a/test/prism/snapshots/seattlerb/parse_line_heredoc_regexp_chars.txt b/test/prism/snapshots/seattlerb/parse_line_heredoc_regexp_chars.txt
new file mode 100644
index 0000000000..fdac30fab7
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_heredoc_regexp_chars.txt
@@ -0,0 +1,33 @@
+@ ProgramNode (location: (1,6)-(4,17))
+├── locals: [:string]
+└── statements:
+ @ StatementsNode (location: (1,6)-(4,17))
+ └── body: (length: 2)
+ ├── @ LocalVariableWriteNode (location: (1,6)-(1,22))
+ │ ├── name: :string
+ │ ├── depth: 0
+ │ ├── name_loc: (1,6)-(1,12) = "string"
+ │ ├── value:
+ │ │ @ StringNode (location: (1,15)-(1,22))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,15)-(1,22) = "<<-\"^D\""
+ │ │ ├── content_loc: (2,0)-(3,0) = " very long string\n"
+ │ │ ├── closing_loc: (3,0)-(4,0) = " ^D\n"
+ │ │ └── unescaped: " very long string\n"
+ │ └── operator_loc: (1,13)-(1,14) = "="
+ └── @ CallNode (location: (4,6)-(4,17))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :puts
+ ├── message_loc: (4,6)-(4,10) = "puts"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (4,11)-(4,17))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ LocalVariableReadNode (location: (4,11)-(4,17))
+ │ ├── name: :string
+ │ └── depth: 0
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/parse_line_iter_call_no_parens.txt b/test/prism/snapshots/seattlerb/parse_line_iter_call_no_parens.txt
new file mode 100644
index 0000000000..8d9dbf24ab
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_iter_call_no_parens.txt
@@ -0,0 +1,74 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(3,3))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,3))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (1,2)-(1,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,2)-(1,3) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,4)-(3,3))
+ ├── locals: [:x, :y]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,7)-(1,13))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,8)-(1,12))
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ RequiredParameterNode (location: (1,8)-(1,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :x
+ │ │ │ └── @ RequiredParameterNode (location: (1,11)-(1,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :y
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,7)-(1,8) = "|"
+ │ └── closing_loc: (1,12)-(1,13) = "|"
+ ├── body:
+ │ @ StatementsNode (location: (2,2)-(2,7))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (2,2)-(2,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (2,2)-(2,3))
+ │ │ ├── name: :x
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (2,4)-(2,5) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (2,6)-(2,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (2,6)-(2,7))
+ │ │ ├── name: :y
+ │ │ └── depth: 0
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── opening_loc: (1,4)-(1,6) = "do"
+ └── closing_loc: (3,0)-(3,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/parse_line_iter_call_parens.txt b/test/prism/snapshots/seattlerb/parse_line_iter_call_parens.txt
new file mode 100644
index 0000000000..663d870137
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_iter_call_parens.txt
@@ -0,0 +1,74 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(3,3))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: (1,1)-(1,2) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,3))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (1,2)-(1,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,2)-(1,3) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: (1,3)-(1,4) = ")"
+ └── block:
+ @ BlockNode (location: (1,5)-(3,3))
+ ├── locals: [:x, :y]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,8)-(1,14))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,9)-(1,13))
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ RequiredParameterNode (location: (1,9)-(1,10))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :x
+ │ │ │ └── @ RequiredParameterNode (location: (1,12)-(1,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :y
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,8)-(1,9) = "|"
+ │ └── closing_loc: (1,13)-(1,14) = "|"
+ ├── body:
+ │ @ StatementsNode (location: (2,2)-(2,7))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (2,2)-(2,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (2,2)-(2,3))
+ │ │ ├── name: :x
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (2,4)-(2,5) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (2,6)-(2,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (2,6)-(2,7))
+ │ │ ├── name: :y
+ │ │ └── depth: 0
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── opening_loc: (1,5)-(1,7) = "do"
+ └── closing_loc: (3,0)-(3,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/parse_line_multiline_str.txt b/test/prism/snapshots/seattlerb/parse_line_multiline_str.txt
new file mode 100644
index 0000000000..8d4578eaec
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_multiline_str.txt
@@ -0,0 +1,14 @@
+@ ProgramNode (location: (1,0)-(3,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,1))
+ └── body: (length: 2)
+ ├── @ StringNode (location: (1,0)-(2,2))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,1) = "\""
+ │ ├── content_loc: (1,1)-(2,1) = "a\nb"
+ │ ├── closing_loc: (2,1)-(2,2) = "\""
+ │ └── unescaped: "a\nb"
+ └── @ IntegerNode (location: (3,0)-(3,1))
+ ├── flags: decimal
+ └── value: 1
diff --git a/test/prism/snapshots/seattlerb/parse_line_multiline_str_literal_n.txt b/test/prism/snapshots/seattlerb/parse_line_multiline_str_literal_n.txt
new file mode 100644
index 0000000000..49d31f5b1b
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_multiline_str_literal_n.txt
@@ -0,0 +1,14 @@
+@ ProgramNode (location: (1,0)-(2,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,1))
+ └── body: (length: 2)
+ ├── @ StringNode (location: (1,0)-(1,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,1) = "\""
+ │ ├── content_loc: (1,1)-(1,5) = "a\\nb"
+ │ ├── closing_loc: (1,5)-(1,6) = "\""
+ │ └── unescaped: "a\nb"
+ └── @ IntegerNode (location: (2,0)-(2,1))
+ ├── flags: decimal
+ └── value: 1
diff --git a/test/prism/snapshots/seattlerb/parse_line_newlines.txt b/test/prism/snapshots/seattlerb/parse_line_newlines.txt
new file mode 100644
index 0000000000..3e1ceef586
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_newlines.txt
@@ -0,0 +1,6 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ TrueNode (location: (1,0)-(1,4))
diff --git a/test/prism/snapshots/seattlerb/parse_line_op_asgn.txt b/test/prism/snapshots/seattlerb/parse_line_op_asgn.txt
new file mode 100644
index 0000000000..5c2eb2da3c
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_op_asgn.txt
@@ -0,0 +1,32 @@
+@ ProgramNode (location: (1,6)-(3,9))
+├── locals: [:foo]
+└── statements:
+ @ StatementsNode (location: (1,6)-(3,9))
+ └── body: (length: 2)
+ ├── @ LocalVariableOperatorWriteNode (location: (1,6)-(2,11))
+ │ ├── name_loc: (1,6)-(1,9) = "foo"
+ │ ├── operator_loc: (1,10)-(1,12) = "+="
+ │ ├── value:
+ │ │ @ CallNode (location: (2,8)-(2,11))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (2,8)-(2,11) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── name: :foo
+ │ ├── operator: :+
+ │ └── depth: 0
+ └── @ CallNode (location: (3,6)-(3,9))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :baz
+ ├── message_loc: (3,6)-(3,9) = "baz"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/parse_line_postexe.txt b/test/prism/snapshots/seattlerb/parse_line_postexe.txt
new file mode 100644
index 0000000000..68b5f02fe0
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_postexe.txt
@@ -0,0 +1,22 @@
+@ ProgramNode (location: (1,0)-(3,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,1))
+ └── body: (length: 1)
+ └── @ PostExecutionNode (location: (1,0)-(3,1))
+ ├── statements:
+ │ @ StatementsNode (location: (2,0)-(2,3))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (2,0)-(2,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (2,0)-(2,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── keyword_loc: (1,0)-(1,3) = "END"
+ ├── opening_loc: (1,4)-(1,5) = "{"
+ └── closing_loc: (3,0)-(3,1) = "}"
diff --git a/test/prism/snapshots/seattlerb/parse_line_preexe.txt b/test/prism/snapshots/seattlerb/parse_line_preexe.txt
new file mode 100644
index 0000000000..65ea22cf7d
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_preexe.txt
@@ -0,0 +1,22 @@
+@ ProgramNode (location: (1,0)-(3,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,1))
+ └── body: (length: 1)
+ └── @ PreExecutionNode (location: (1,0)-(3,1))
+ ├── statements:
+ │ @ StatementsNode (location: (2,0)-(2,3))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (2,0)-(2,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (2,0)-(2,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── keyword_loc: (1,0)-(1,5) = "BEGIN"
+ ├── opening_loc: (1,6)-(1,7) = "{"
+ └── closing_loc: (3,0)-(3,1) = "}"
diff --git a/test/prism/snapshots/seattlerb/parse_line_rescue.txt b/test/prism/snapshots/seattlerb/parse_line_rescue.txt
new file mode 100644
index 0000000000..cb20d5403b
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_rescue.txt
@@ -0,0 +1,62 @@
+@ ProgramNode (location: (1,0)-(7,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(7,3))
+ └── body: (length: 1)
+ └── @ BeginNode (location: (1,0)-(7,3))
+ ├── begin_keyword_loc: (1,0)-(1,5) = "begin"
+ ├── statements:
+ │ @ StatementsNode (location: (2,2)-(2,3))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (2,2)-(2,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (2,2)-(2,3) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── rescue_clause:
+ │ @ RescueNode (location: (3,0)-(6,3))
+ │ ├── keyword_loc: (3,0)-(3,6) = "rescue"
+ │ ├── exceptions: (length: 0)
+ │ ├── operator_loc: ∅
+ │ ├── reference: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (4,2)-(4,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (4,2)-(4,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (4,2)-(4,3) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── consequent:
+ │ @ RescueNode (location: (5,0)-(6,3))
+ │ ├── keyword_loc: (5,0)-(5,6) = "rescue"
+ │ ├── exceptions: (length: 0)
+ │ ├── operator_loc: ∅
+ │ ├── reference: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (6,2)-(6,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (6,2)-(6,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :c
+ │ │ ├── message_loc: (6,2)-(6,3) = "c"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── consequent: ∅
+ ├── else_clause: ∅
+ ├── ensure_clause: ∅
+ └── end_keyword_loc: (7,0)-(7,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/parse_line_return.txt b/test/prism/snapshots/seattlerb/parse_line_return.txt
new file mode 100644
index 0000000000..719a4da5da
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_return.txt
@@ -0,0 +1,40 @@
+@ ProgramNode (location: (1,6)-(5,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,6)-(5,9))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,6)-(5,9))
+ ├── name: :blah
+ ├── name_loc: (1,10)-(1,14) = "blah"
+ ├── receiver: ∅
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (2,8)-(4,11))
+ │ └── body: (length: 1)
+ │ └── @ IfNode (location: (2,8)-(4,11))
+ │ ├── if_keyword_loc: (2,8)-(2,10) = "if"
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (2,11)-(2,15))
+ │ ├── then_keyword_loc: (2,16)-(2,20) = "then"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,10)-(3,19))
+ │ │ └── body: (length: 1)
+ │ │ └── @ ReturnNode (location: (3,10)-(3,19))
+ │ │ ├── flags: redundant
+ │ │ ├── keyword_loc: (3,10)-(3,16) = "return"
+ │ │ └── arguments:
+ │ │ @ ArgumentsNode (location: (3,17)-(3,19))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (3,17)-(3,19))
+ │ │ ├── flags: decimal
+ │ │ └── value: 42
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (4,8)-(4,11) = "end"
+ ├── locals: []
+ ├── def_keyword_loc: (1,6)-(1,9) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (5,6)-(5,9) = "end"
diff --git a/test/prism/snapshots/seattlerb/parse_line_str_with_newline_escape.txt b/test/prism/snapshots/seattlerb/parse_line_str_with_newline_escape.txt
new file mode 100644
index 0000000000..4a675d67c4
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_str_with_newline_escape.txt
@@ -0,0 +1,25 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,13))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: (1,1)-(1,2) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,12))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ StringNode (location: (1,2)-(1,6))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,2)-(1,3) = "\""
+ │ │ ├── content_loc: (1,3)-(1,5) = "\\n"
+ │ │ ├── closing_loc: (1,5)-(1,6) = "\""
+ │ │ └── unescaped: "\n"
+ │ └── @ TrueNode (location: (1,8)-(1,12))
+ ├── closing_loc: (1,12)-(1,13) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/parse_line_to_ary.txt b/test/prism/snapshots/seattlerb/parse_line_to_ary.txt
new file mode 100644
index 0000000000..0485b0d2e3
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_to_ary.txt
@@ -0,0 +1,39 @@
+@ ProgramNode (location: (1,0)-(3,1))
+├── locals: [:a, :b]
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,1))
+ └── body: (length: 2)
+ ├── @ MultiWriteNode (location: (1,0)-(2,5))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ LocalVariableTargetNode (location: (1,0)-(1,1))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ └── @ LocalVariableTargetNode (location: (2,0)-(2,1))
+ │ │ ├── name: :b
+ │ │ └── depth: 0
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (2,2)-(2,3) = "="
+ │ └── value:
+ │ @ CallNode (location: (2,4)-(2,5))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :c
+ │ ├── message_loc: (2,4)-(2,5) = "c"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (3,0)-(3,1))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :d
+ ├── message_loc: (3,0)-(3,1) = "d"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/parse_line_trailing_newlines.txt b/test/prism/snapshots/seattlerb/parse_line_trailing_newlines.txt
new file mode 100644
index 0000000000..5cd7702847
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_line_trailing_newlines.txt
@@ -0,0 +1,25 @@
+@ ProgramNode (location: (1,0)-(2,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,1))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (2,0)-(2,1))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :b
+ ├── message_loc: (2,0)-(2,1) = "b"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/parse_opt_call_args_assocs_comma.txt b/test/prism/snapshots/seattlerb/parse_opt_call_args_assocs_comma.txt
new file mode 100644
index 0000000000..b767a5c17e
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_opt_call_args_assocs_comma.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,8))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── call_operator_loc: ∅
+ ├── name: :[]
+ ├── message_loc: (1,1)-(1,8) = "[2=>3,]"
+ ├── opening_loc: (1,1)-(1,2) = "["
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,6))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ KeywordHashNode (location: (1,2)-(1,6))
+ │ ├── flags: ∅
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (1,2)-(1,6))
+ │ ├── key:
+ │ │ @ IntegerNode (location: (1,2)-(1,3))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,5)-(1,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ └── operator_loc: (1,3)-(1,5) = "=>"
+ ├── closing_loc: (1,7)-(1,8) = "]"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/parse_opt_call_args_lit_comma.txt b/test/prism/snapshots/seattlerb/parse_opt_call_args_lit_comma.txt
new file mode 100644
index 0000000000..d1d3d9335f
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_opt_call_args_lit_comma.txt
@@ -0,0 +1,24 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,5))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── call_operator_loc: ∅
+ ├── name: :[]
+ ├── message_loc: (1,1)-(1,5) = "[2,]"
+ ├── opening_loc: (1,1)-(1,2) = "["
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,3))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,2)-(1,3))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── closing_loc: (1,4)-(1,5) = "]"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/parse_pattern_019.txt b/test/prism/snapshots/seattlerb/parse_pattern_019.txt
new file mode 100644
index 0000000000..9e2500fbde
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_pattern_019.txt
@@ -0,0 +1,33 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(4,3))
+ ├── predicate:
+ │ @ IntegerNode (location: (1,5)-(1,6))
+ │ ├── flags: decimal
+ │ └── value: 0
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(3,6))
+ │ ├── pattern:
+ │ │ @ RangeNode (location: (2,3)-(2,8))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (2,3)-(2,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: -1
+ │ │ ├── right:
+ │ │ │ @ IntegerNode (location: (2,7)-(2,8))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (2,5)-(2,7) = ".."
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,2)-(3,6))
+ │ │ └── body: (length: 1)
+ │ │ └── @ TrueNode (location: (3,2)-(3,6))
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (4,0)-(4,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/parse_pattern_044.txt b/test/prism/snapshots/seattlerb/parse_pattern_044.txt
new file mode 100644
index 0000000000..951a5100b6
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_pattern_044.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(4,3))
+ ├── predicate:
+ │ @ CallNode (location: (1,5)-(1,8))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :obj
+ │ ├── message_loc: (1,5)-(1,8) = "obj"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(3,6))
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (2,3)-(2,11))
+ │ │ ├── constant:
+ │ │ │ @ ConstantReadNode (location: (2,3)-(2,9))
+ │ │ │ └── name: :Object
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (2,9)-(2,10) = "["
+ │ │ └── closing_loc: (2,10)-(2,11) = "]"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,2)-(3,6))
+ │ │ └── body: (length: 1)
+ │ │ └── @ TrueNode (location: (3,2)-(3,6))
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (4,0)-(4,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/parse_pattern_051.txt b/test/prism/snapshots/seattlerb/parse_pattern_051.txt
new file mode 100644
index 0000000000..6c366e559f
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_pattern_051.txt
@@ -0,0 +1,47 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(4,3))
+ ├── predicate:
+ │ @ ArrayNode (location: (1,5)-(1,14))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 3)
+ │ │ ├── @ IntegerNode (location: (1,6)-(1,7))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 0
+ │ │ ├── @ IntegerNode (location: (1,9)-(1,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (1,12)-(1,13))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── opening_loc: (1,5)-(1,6) = "["
+ │ └── closing_loc: (1,13)-(1,14) = "]"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(3,6))
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (2,3)-(2,10))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ IntegerNode (location: (2,4)-(2,5))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 0
+ │ │ │ └── @ IntegerNode (location: (2,7)-(2,8))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── rest:
+ │ │ │ @ ImplicitRestNode (location: (2,8)-(2,9))
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (2,3)-(2,4) = "["
+ │ │ └── closing_loc: (2,9)-(2,10) = "]"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,2)-(3,6))
+ │ │ └── body: (length: 1)
+ │ │ └── @ TrueNode (location: (3,2)-(3,6))
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (4,0)-(4,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/parse_pattern_058.txt b/test/prism/snapshots/seattlerb/parse_pattern_058.txt
new file mode 100644
index 0000000000..8a4f8f8a68
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_pattern_058.txt
@@ -0,0 +1,73 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: [:a, :rest]
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(4,3))
+ ├── predicate:
+ │ @ HashNode (location: (1,5)-(1,11))
+ │ ├── opening_loc: (1,5)-(1,6) = "{"
+ │ ├── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (1,6)-(1,10))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (1,6)-(1,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ │ │ ├── closing_loc: (1,7)-(1,8) = ":"
+ │ │ │ └── unescaped: "a"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (1,9)-(1,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 0
+ │ │ └── operator_loc: ∅
+ │ └── closing_loc: (1,10)-(1,11) = "}"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(3,11))
+ │ ├── pattern:
+ │ │ @ HashPatternNode (location: (2,3)-(2,15))
+ │ │ ├── constant: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (2,4)-(2,6))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (2,4)-(2,6))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (2,4)-(2,5) = "a"
+ │ │ │ │ ├── closing_loc: (2,5)-(2,6) = ":"
+ │ │ │ │ └── unescaped: "a"
+ │ │ │ ├── value:
+ │ │ │ │ @ ImplicitNode (location: (2,4)-(2,5))
+ │ │ │ │ └── value:
+ │ │ │ │ @ LocalVariableTargetNode (location: (2,4)-(2,5))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ └── depth: 0
+ │ │ │ └── operator_loc: ∅
+ │ │ ├── rest:
+ │ │ │ @ AssocSplatNode (location: (2,8)-(2,14))
+ │ │ │ ├── value:
+ │ │ │ │ @ LocalVariableTargetNode (location: (2,10)-(2,14))
+ │ │ │ │ ├── name: :rest
+ │ │ │ │ └── depth: 0
+ │ │ │ └── operator_loc: (2,8)-(2,10) = "**"
+ │ │ ├── opening_loc: (2,3)-(2,4) = "{"
+ │ │ └── closing_loc: (2,14)-(2,15) = "}"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,2)-(3,11))
+ │ │ └── body: (length: 1)
+ │ │ └── @ ArrayNode (location: (3,2)-(3,11))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 2)
+ │ │ │ ├── @ LocalVariableReadNode (location: (3,3)-(3,4))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ └── depth: 0
+ │ │ │ └── @ LocalVariableReadNode (location: (3,6)-(3,10))
+ │ │ │ ├── name: :rest
+ │ │ │ └── depth: 0
+ │ │ ├── opening_loc: (3,2)-(3,3) = "["
+ │ │ └── closing_loc: (3,10)-(3,11) = "]"
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (4,0)-(4,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/parse_pattern_058_2.txt b/test/prism/snapshots/seattlerb/parse_pattern_058_2.txt
new file mode 100644
index 0000000000..3507d0f2cf
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_pattern_058_2.txt
@@ -0,0 +1,67 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(4,3))
+ ├── predicate:
+ │ @ HashNode (location: (1,5)-(1,11))
+ │ ├── opening_loc: (1,5)-(1,6) = "{"
+ │ ├── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (1,6)-(1,10))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (1,6)-(1,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ │ │ ├── closing_loc: (1,7)-(1,8) = ":"
+ │ │ │ └── unescaped: "a"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (1,9)-(1,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 0
+ │ │ └── operator_loc: ∅
+ │ └── closing_loc: (1,10)-(1,11) = "}"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(3,5))
+ │ ├── pattern:
+ │ │ @ HashPatternNode (location: (2,3)-(2,11))
+ │ │ ├── constant: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (2,4)-(2,6))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (2,4)-(2,6))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (2,4)-(2,5) = "a"
+ │ │ │ │ ├── closing_loc: (2,5)-(2,6) = ":"
+ │ │ │ │ └── unescaped: "a"
+ │ │ │ ├── value:
+ │ │ │ │ @ ImplicitNode (location: (2,4)-(2,5))
+ │ │ │ │ └── value:
+ │ │ │ │ @ LocalVariableTargetNode (location: (2,4)-(2,5))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ └── depth: 0
+ │ │ │ └── operator_loc: ∅
+ │ │ ├── rest:
+ │ │ │ @ AssocSplatNode (location: (2,8)-(2,10))
+ │ │ │ ├── value: ∅
+ │ │ │ └── operator_loc: (2,8)-(2,10) = "**"
+ │ │ ├── opening_loc: (2,3)-(2,4) = "{"
+ │ │ └── closing_loc: (2,10)-(2,11) = "}"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,2)-(3,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ ArrayNode (location: (3,2)-(3,5))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ LocalVariableReadNode (location: (3,3)-(3,4))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ ├── opening_loc: (3,2)-(3,3) = "["
+ │ │ └── closing_loc: (3,4)-(3,5) = "]"
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (4,0)-(4,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/parse_pattern_069.txt b/test/prism/snapshots/seattlerb/parse_pattern_069.txt
new file mode 100644
index 0000000000..09ac7653c6
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_pattern_069.txt
@@ -0,0 +1,48 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(4,3))
+ ├── predicate:
+ │ @ SymbolNode (location: (1,5)-(1,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(3,3))
+ │ ├── pattern:
+ │ │ @ HashPatternNode (location: (2,3)-(2,15))
+ │ │ ├── constant:
+ │ │ │ @ ConstantReadNode (location: (2,3)-(2,9))
+ │ │ │ └── name: :Object
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (2,10)-(2,14))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (2,10)-(2,12))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (2,10)-(2,11) = "b"
+ │ │ │ │ ├── closing_loc: (2,11)-(2,12) = ":"
+ │ │ │ │ └── unescaped: "b"
+ │ │ │ ├── value:
+ │ │ │ │ @ IntegerNode (location: (2,13)-(2,14))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── operator_loc: ∅
+ │ │ ├── rest: ∅
+ │ │ ├── opening_loc: (2,9)-(2,10) = "["
+ │ │ └── closing_loc: (2,14)-(2,15) = "]"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,2)-(3,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (3,2)-(3,3))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (4,0)-(4,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/parse_pattern_076.txt b/test/prism/snapshots/seattlerb/parse_pattern_076.txt
new file mode 100644
index 0000000000..60e71cd6fe
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_pattern_076.txt
@@ -0,0 +1,58 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(4,3))
+ ├── predicate:
+ │ @ HashNode (location: (1,5)-(1,11))
+ │ ├── opening_loc: (1,5)-(1,6) = "{"
+ │ ├── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (1,6)-(1,10))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (1,6)-(1,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ │ │ ├── closing_loc: (1,7)-(1,8) = ":"
+ │ │ │ └── unescaped: "a"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (1,9)-(1,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: ∅
+ │ └── closing_loc: (1,10)-(1,11) = "}"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,0)-(3,6))
+ │ ├── pattern:
+ │ │ @ HashPatternNode (location: (2,3)-(2,16))
+ │ │ ├── constant: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (2,4)-(2,8))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (2,4)-(2,6))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (2,4)-(2,5) = "a"
+ │ │ │ │ ├── closing_loc: (2,5)-(2,6) = ":"
+ │ │ │ │ └── unescaped: "a"
+ │ │ │ ├── value:
+ │ │ │ │ @ IntegerNode (location: (2,7)-(2,8))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── operator_loc: ∅
+ │ │ ├── rest:
+ │ │ │ @ NoKeywordsParameterNode (location: (2,10)-(2,15))
+ │ │ │ ├── operator_loc: (2,10)-(2,12) = "**"
+ │ │ │ └── keyword_loc: (2,12)-(2,15) = "nil"
+ │ │ ├── opening_loc: (2,3)-(2,4) = "{"
+ │ │ └── closing_loc: (2,15)-(2,16) = "}"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,2)-(3,6))
+ │ │ └── body: (length: 1)
+ │ │ └── @ TrueNode (location: (3,2)-(3,6))
+ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ └── then_loc: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (4,0)-(4,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/parse_until_not_canonical.txt b/test/prism/snapshots/seattlerb/parse_until_not_canonical.txt
new file mode 100644
index 0000000000..7d5ef19a05
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_until_not_canonical.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 1)
+ └── @ UntilNode (location: (1,0)-(3,3))
+ ├── flags: ∅
+ ├── keyword_loc: (1,0)-(1,5) = "until"
+ ├── closing_loc: (3,0)-(3,3) = "end"
+ ├── predicate:
+ │ @ CallNode (location: (1,6)-(1,18))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,10)-(1,18))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (1,10)-(1,13))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :var
+ │ │ │ ├── message_loc: (1,10)-(1,13) = "var"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (1,13)-(1,14) = "."
+ │ │ ├── name: :nil?
+ │ │ ├── message_loc: (1,14)-(1,18) = "nil?"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!
+ │ ├── message_loc: (1,6)-(1,9) = "not"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── statements:
+ @ StatementsNode (location: (2,2)-(2,7))
+ └── body: (length: 1)
+ └── @ StringNode (location: (2,2)-(2,7))
+ ├── flags: ∅
+ ├── opening_loc: (2,2)-(2,3) = "'"
+ ├── content_loc: (2,3)-(2,6) = "foo"
+ ├── closing_loc: (2,6)-(2,7) = "'"
+ └── unescaped: "foo"
diff --git a/test/prism/snapshots/seattlerb/parse_until_not_noncanonical.txt b/test/prism/snapshots/seattlerb/parse_until_not_noncanonical.txt
new file mode 100644
index 0000000000..7d5ef19a05
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_until_not_noncanonical.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 1)
+ └── @ UntilNode (location: (1,0)-(3,3))
+ ├── flags: ∅
+ ├── keyword_loc: (1,0)-(1,5) = "until"
+ ├── closing_loc: (3,0)-(3,3) = "end"
+ ├── predicate:
+ │ @ CallNode (location: (1,6)-(1,18))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,10)-(1,18))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (1,10)-(1,13))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :var
+ │ │ │ ├── message_loc: (1,10)-(1,13) = "var"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (1,13)-(1,14) = "."
+ │ │ ├── name: :nil?
+ │ │ ├── message_loc: (1,14)-(1,18) = "nil?"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!
+ │ ├── message_loc: (1,6)-(1,9) = "not"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── statements:
+ @ StatementsNode (location: (2,2)-(2,7))
+ └── body: (length: 1)
+ └── @ StringNode (location: (2,2)-(2,7))
+ ├── flags: ∅
+ ├── opening_loc: (2,2)-(2,3) = "'"
+ ├── content_loc: (2,3)-(2,6) = "foo"
+ ├── closing_loc: (2,6)-(2,7) = "'"
+ └── unescaped: "foo"
diff --git a/test/prism/snapshots/seattlerb/parse_while_not_canonical.txt b/test/prism/snapshots/seattlerb/parse_while_not_canonical.txt
new file mode 100644
index 0000000000..91eb88a70f
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_while_not_canonical.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 1)
+ └── @ WhileNode (location: (1,0)-(3,3))
+ ├── flags: ∅
+ ├── keyword_loc: (1,0)-(1,5) = "while"
+ ├── closing_loc: (3,0)-(3,3) = "end"
+ ├── predicate:
+ │ @ CallNode (location: (1,6)-(1,18))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,10)-(1,18))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (1,10)-(1,13))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :var
+ │ │ │ ├── message_loc: (1,10)-(1,13) = "var"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (1,13)-(1,14) = "."
+ │ │ ├── name: :nil?
+ │ │ ├── message_loc: (1,14)-(1,18) = "nil?"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!
+ │ ├── message_loc: (1,6)-(1,9) = "not"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── statements:
+ @ StatementsNode (location: (2,2)-(2,7))
+ └── body: (length: 1)
+ └── @ StringNode (location: (2,2)-(2,7))
+ ├── flags: ∅
+ ├── opening_loc: (2,2)-(2,3) = "'"
+ ├── content_loc: (2,3)-(2,6) = "foo"
+ ├── closing_loc: (2,6)-(2,7) = "'"
+ └── unescaped: "foo"
diff --git a/test/prism/snapshots/seattlerb/parse_while_not_noncanonical.txt b/test/prism/snapshots/seattlerb/parse_while_not_noncanonical.txt
new file mode 100644
index 0000000000..91eb88a70f
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/parse_while_not_noncanonical.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 1)
+ └── @ WhileNode (location: (1,0)-(3,3))
+ ├── flags: ∅
+ ├── keyword_loc: (1,0)-(1,5) = "while"
+ ├── closing_loc: (3,0)-(3,3) = "end"
+ ├── predicate:
+ │ @ CallNode (location: (1,6)-(1,18))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,10)-(1,18))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (1,10)-(1,13))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :var
+ │ │ │ ├── message_loc: (1,10)-(1,13) = "var"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (1,13)-(1,14) = "."
+ │ │ ├── name: :nil?
+ │ │ ├── message_loc: (1,14)-(1,18) = "nil?"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!
+ │ ├── message_loc: (1,6)-(1,9) = "not"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── statements:
+ @ StatementsNode (location: (2,2)-(2,7))
+ └── body: (length: 1)
+ └── @ StringNode (location: (2,2)-(2,7))
+ ├── flags: ∅
+ ├── opening_loc: (2,2)-(2,3) = "'"
+ ├── content_loc: (2,3)-(2,6) = "foo"
+ ├── closing_loc: (2,6)-(2,7) = "'"
+ └── unescaped: "foo"
diff --git a/test/prism/snapshots/seattlerb/pctW_lineno.txt b/test/prism/snapshots/seattlerb/pctW_lineno.txt
new file mode 100644
index 0000000000..58efa9c59a
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/pctW_lineno.txt
@@ -0,0 +1,52 @@
+@ ProgramNode (location: (1,0)-(5,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,11))
+ └── body: (length: 1)
+ └── @ ArrayNode (location: (1,0)-(5,11))
+ ├── flags: ∅
+ ├── elements: (length: 7)
+ │ ├── @ StringNode (location: (1,3)-(1,7))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (1,3)-(1,7) = "a\\nb"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a\nb"
+ │ ├── @ StringNode (location: (2,0)-(2,1))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (2,0)-(2,1) = "c"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "c"
+ │ ├── @ StringNode (location: (2,2)-(2,3))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (2,2)-(2,3) = "d"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "d"
+ │ ├── @ StringNode (location: (3,0)-(4,1))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (3,0)-(4,1) = "e\\\nf"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "e\nf"
+ │ ├── @ StringNode (location: (5,0)-(5,2))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (5,0)-(5,2) = "gy"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "gy"
+ │ ├── @ StringNode (location: (5,3)-(5,6))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (5,3)-(5,6) = "h\\y"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "hy"
+ │ └── @ StringNode (location: (5,7)-(5,10))
+ │ ├── flags: ∅
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (5,7)-(5,10) = "i\\y"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "iy"
+ ├── opening_loc: (1,0)-(1,3) = "%W("
+ └── closing_loc: (5,10)-(5,11) = ")"
diff --git a/test/prism/snapshots/seattlerb/pct_Q_backslash_nl.txt b/test/prism/snapshots/seattlerb/pct_Q_backslash_nl.txt
new file mode 100644
index 0000000000..c8a990c672
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/pct_Q_backslash_nl.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(2,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,1))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,0)-(2,1))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,3) = "%Q{"
+ ├── content_loc: (1,3)-(2,0) = " \\\n"
+ ├── closing_loc: (2,0)-(2,1) = "}"
+ └── unescaped: " "
diff --git a/test/prism/snapshots/seattlerb/pct_nl.txt b/test/prism/snapshots/seattlerb/pct_nl.txt
new file mode 100644
index 0000000000..1009fcb51d
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/pct_nl.txt
@@ -0,0 +1,17 @@
+@ ProgramNode (location: (1,0)-(3,0))
+├── locals: [:x]
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,0))
+ └── body: (length: 1)
+ └── @ LocalVariableWriteNode (location: (1,0)-(3,0))
+ ├── name: :x
+ ├── depth: 0
+ ├── name_loc: (1,0)-(1,1) = "x"
+ ├── value:
+ │ @ StringNode (location: (1,4)-(3,0))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,4)-(2,0) = "%\n"
+ │ ├── content_loc: (2,0)-(2,0) = ""
+ │ ├── closing_loc: (2,0)-(3,0) = "\n"
+ │ └── unescaped: ""
+ └── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/seattlerb/pct_w_heredoc_interp_nested.txt b/test/prism/snapshots/seattlerb/pct_w_heredoc_interp_nested.txt
new file mode 100644
index 0000000000..c4bc53b723
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/pct_w_heredoc_interp_nested.txt
@@ -0,0 +1,51 @@
+@ ProgramNode (location: (1,0)-(4,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,11))
+ └── body: (length: 1)
+ └── @ ArrayNode (location: (1,0)-(4,11))
+ ├── flags: ∅
+ ├── elements: (length: 5)
+ │ ├── @ StringNode (location: (1,4)-(1,5))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (1,4)-(1,5) = "1"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "1"
+ │ ├── @ InterpolatedStringNode (location: (1,6)-(1,12))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── parts: (length: 1)
+ │ │ │ └── @ EmbeddedStatementsNode (location: (1,6)-(1,12))
+ │ │ │ ├── opening_loc: (1,6)-(1,8) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (1,8)-(1,11))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ StringNode (location: (1,8)-(1,11))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: (1,8)-(1,11) = "<<A"
+ │ │ │ │ ├── content_loc: (2,0)-(3,0) = "2\n"
+ │ │ │ │ ├── closing_loc: (3,0)-(4,0) = "A\n"
+ │ │ │ │ └── unescaped: "2\n"
+ │ │ │ └── closing_loc: (1,11)-(1,12) = "}"
+ │ │ └── closing_loc: ∅
+ │ ├── @ StringNode (location: (1,13)-(1,14))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (1,13)-(1,14) = "3"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "3"
+ │ ├── @ StringNode (location: (4,6)-(4,7))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (4,6)-(4,7) = "4"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "4"
+ │ └── @ StringNode (location: (4,8)-(4,9))
+ │ ├── flags: ∅
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (4,8)-(4,9) = "5"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "5"
+ ├── opening_loc: (1,0)-(1,3) = "%W("
+ └── closing_loc: (4,10)-(4,11) = ")"
diff --git a/test/prism/snapshots/seattlerb/pipe_semicolon.txt b/test/prism/snapshots/seattlerb/pipe_semicolon.txt
new file mode 100644
index 0000000000..71fb4fbed5
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/pipe_semicolon.txt
@@ -0,0 +1,39 @@
+@ ProgramNode (location: (1,0)-(1,18))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,18))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,18))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,1)-(1,2) = "."
+ ├── name: :b
+ ├── message_loc: (1,2)-(1,3) = "b"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,4)-(1,18))
+ ├── locals: [:c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,7)-(1,14))
+ │ ├── parameters: ∅
+ │ ├── locals: (length: 1)
+ │ │ └── @ BlockLocalVariableNode (location: (1,11)-(1,12))
+ │ │ ├── flags: ∅
+ │ │ └── name: :c
+ │ ├── opening_loc: (1,7)-(1,8) = "|"
+ │ └── closing_loc: (1,13)-(1,14) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,4)-(1,6) = "do"
+ └── closing_loc: (1,15)-(1,18) = "end"
diff --git a/test/prism/snapshots/seattlerb/pipe_space.txt b/test/prism/snapshots/seattlerb/pipe_space.txt
new file mode 100644
index 0000000000..302d225337
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/pipe_space.txt
@@ -0,0 +1,36 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,14))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,1)-(1,2) = "."
+ ├── name: :b
+ ├── message_loc: (1,2)-(1,3) = "b"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,4)-(1,14))
+ ├── locals: []
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,7)-(1,10))
+ │ ├── parameters: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,7)-(1,8) = "|"
+ │ └── closing_loc: (1,9)-(1,10) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,4)-(1,6) = "do"
+ └── closing_loc: (1,11)-(1,14) = "end"
diff --git a/test/prism/snapshots/seattlerb/qWords_space.txt b/test/prism/snapshots/seattlerb/qWords_space.txt
new file mode 100644
index 0000000000..95ae6d4075
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/qWords_space.txt
@@ -0,0 +1,10 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ ArrayNode (location: (1,0)-(1,5))
+ ├── flags: ∅
+ ├── elements: (length: 0)
+ ├── opening_loc: (1,0)-(1,3) = "%W("
+ └── closing_loc: (1,4)-(1,5) = ")"
diff --git a/test/prism/snapshots/seattlerb/qsymbols.txt b/test/prism/snapshots/seattlerb/qsymbols.txt
new file mode 100644
index 0000000000..8ba68638c5
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/qsymbols.txt
@@ -0,0 +1,28 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ ArrayNode (location: (1,0)-(1,9))
+ ├── flags: ∅
+ ├── elements: (length: 3)
+ │ ├── @ SymbolNode (location: (1,3)-(1,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (1,3)-(1,4) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── @ SymbolNode (location: (1,5)-(1,6))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (1,5)-(1,6) = "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b"
+ │ └── @ SymbolNode (location: (1,7)-(1,8))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: ∅
+ │ ├── value_loc: (1,7)-(1,8) = "c"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "c"
+ ├── opening_loc: (1,0)-(1,3) = "%I("
+ └── closing_loc: (1,8)-(1,9) = ")"
diff --git a/test/prism/snapshots/seattlerb/qsymbols_empty.txt b/test/prism/snapshots/seattlerb/qsymbols_empty.txt
new file mode 100644
index 0000000000..54aa3f77d7
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/qsymbols_empty.txt
@@ -0,0 +1,10 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ ArrayNode (location: (1,0)-(1,4))
+ ├── flags: ∅
+ ├── elements: (length: 0)
+ ├── opening_loc: (1,0)-(1,3) = "%I("
+ └── closing_loc: (1,3)-(1,4) = ")"
diff --git a/test/prism/snapshots/seattlerb/qsymbols_empty_space.txt b/test/prism/snapshots/seattlerb/qsymbols_empty_space.txt
new file mode 100644
index 0000000000..624b922ce6
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/qsymbols_empty_space.txt
@@ -0,0 +1,10 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ ArrayNode (location: (1,0)-(1,5))
+ ├── flags: ∅
+ ├── elements: (length: 0)
+ ├── opening_loc: (1,0)-(1,3) = "%I("
+ └── closing_loc: (1,4)-(1,5) = ")"
diff --git a/test/prism/snapshots/seattlerb/qsymbols_interp.txt b/test/prism/snapshots/seattlerb/qsymbols_interp.txt
new file mode 100644
index 0000000000..97bc6754ff
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/qsymbols_interp.txt
@@ -0,0 +1,57 @@
+@ ProgramNode (location: (1,0)-(1,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,15))
+ └── body: (length: 1)
+ └── @ ArrayNode (location: (1,0)-(1,15))
+ ├── flags: ∅
+ ├── elements: (length: 3)
+ │ ├── @ SymbolNode (location: (1,3)-(1,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (1,3)-(1,4) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── @ InterpolatedSymbolNode (location: (1,5)-(1,12))
+ │ │ ├── opening_loc: ∅
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (1,5)-(1,6))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (1,5)-(1,6) = "b"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "b"
+ │ │ │ └── @ EmbeddedStatementsNode (location: (1,6)-(1,12))
+ │ │ │ ├── opening_loc: (1,6)-(1,8) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (1,8)-(1,11))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (1,8)-(1,11))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── receiver:
+ │ │ │ │ │ @ IntegerNode (location: (1,8)-(1,9))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :+
+ │ │ │ │ ├── message_loc: (1,9)-(1,10) = "+"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (1,10)-(1,11))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ IntegerNode (location: (1,10)-(1,11))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── closing_loc: (1,11)-(1,12) = "}"
+ │ │ └── closing_loc: ∅
+ │ └── @ SymbolNode (location: (1,13)-(1,14))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: ∅
+ │ ├── value_loc: (1,13)-(1,14) = "c"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "c"
+ ├── opening_loc: (1,0)-(1,3) = "%I("
+ └── closing_loc: (1,14)-(1,15) = ")"
diff --git a/test/prism/snapshots/seattlerb/quoted_symbol_hash_arg.txt b/test/prism/snapshots/seattlerb/quoted_symbol_hash_arg.txt
new file mode 100644
index 0000000000..64caf51bcb
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/quoted_symbol_hash_arg.txt
@@ -0,0 +1,35 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,12))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :puts
+ ├── message_loc: (1,0)-(1,4) = "puts"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,5)-(1,12))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ KeywordHashNode (location: (1,5)-(1,12))
+ │ ├── flags: symbol_keys
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (1,5)-(1,12))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (1,5)-(1,9))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,5)-(1,6) = "'"
+ │ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ │ ├── closing_loc: (1,7)-(1,9) = "':"
+ │ │ └── unescaped: "a"
+ │ ├── value:
+ │ │ @ HashNode (location: (1,10)-(1,12))
+ │ │ ├── opening_loc: (1,10)-(1,11) = "{"
+ │ │ ├── elements: (length: 0)
+ │ │ └── closing_loc: (1,11)-(1,12) = "}"
+ │ └── operator_loc: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/quoted_symbol_keys.txt b/test/prism/snapshots/seattlerb/quoted_symbol_keys.txt
new file mode 100644
index 0000000000..96e6af51a4
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/quoted_symbol_keys.txt
@@ -0,0 +1,25 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ HashNode (location: (1,0)-(1,11))
+ ├── opening_loc: (1,0)-(1,1) = "{"
+ ├── elements: (length: 1)
+ │ └── @ AssocNode (location: (1,2)-(1,9))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (1,2)-(1,6))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,2)-(1,3) = "'"
+ │ │ ├── value_loc: (1,3)-(1,4) = "a"
+ │ │ ├── closing_loc: (1,4)-(1,6) = "':"
+ │ │ └── unescaped: "a"
+ │ ├── value:
+ │ │ @ SymbolNode (location: (1,7)-(1,9))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,7)-(1,8) = ":"
+ │ │ ├── value_loc: (1,8)-(1,9) = "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b"
+ │ └── operator_loc: ∅
+ └── closing_loc: (1,10)-(1,11) = "}"
diff --git a/test/prism/snapshots/seattlerb/qw_escape.txt b/test/prism/snapshots/seattlerb/qw_escape.txt
new file mode 100644
index 0000000000..d92c1da7a6
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/qw_escape.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,7))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,0)-(1,7))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,3) = "%q("
+ ├── content_loc: (1,3)-(1,6) = "\u0001\\'"
+ ├── closing_loc: (1,6)-(1,7) = ")"
+ └── unescaped: "\u0001\\'"
diff --git a/test/prism/snapshots/seattlerb/qw_escape_term.txt b/test/prism/snapshots/seattlerb/qw_escape_term.txt
new file mode 100644
index 0000000000..e935b7eb68
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/qw_escape_term.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,26))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,26))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,0)-(1,26))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,3) = "%q|"
+ ├── content_loc: (1,3)-(1,25) = "blah blah \\| blah blah"
+ ├── closing_loc: (1,25)-(1,26) = "|"
+ └── unescaped: "blah blah | blah blah"
diff --git a/test/prism/snapshots/seattlerb/qwords_empty.txt b/test/prism/snapshots/seattlerb/qwords_empty.txt
new file mode 100644
index 0000000000..f9915c97c9
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/qwords_empty.txt
@@ -0,0 +1,10 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ ArrayNode (location: (1,0)-(1,4))
+ ├── flags: ∅
+ ├── elements: (length: 0)
+ ├── opening_loc: (1,0)-(1,3) = "%w("
+ └── closing_loc: (1,3)-(1,4) = ")"
diff --git a/test/prism/snapshots/seattlerb/read_escape_unicode_curlies.txt b/test/prism/snapshots/seattlerb/read_escape_unicode_curlies.txt
new file mode 100644
index 0000000000..3ea5604b69
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/read_escape_unicode_curlies.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,0)-(1,9))
+ ├── flags: forced_utf8_encoding
+ ├── opening_loc: (1,0)-(1,1) = "?"
+ ├── content_loc: (1,1)-(1,9) = "\\u{00a0}"
+ ├── closing_loc: ∅
+ └── unescaped: " "
diff --git a/test/prism/snapshots/seattlerb/read_escape_unicode_h4.txt b/test/prism/snapshots/seattlerb/read_escape_unicode_h4.txt
new file mode 100644
index 0000000000..1eba1396fd
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/read_escape_unicode_h4.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,7))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,0)-(1,7))
+ ├── flags: forced_utf8_encoding
+ ├── opening_loc: (1,0)-(1,1) = "?"
+ ├── content_loc: (1,1)-(1,7) = "\\u00a0"
+ ├── closing_loc: ∅
+ └── unescaped: " "
diff --git a/test/prism/snapshots/seattlerb/regexp.txt b/test/prism/snapshots/seattlerb/regexp.txt
new file mode 100644
index 0000000000..06cf99264e
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/regexp.txt
@@ -0,0 +1,35 @@
+@ ProgramNode (location: (1,0)-(9,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(9,13))
+ └── body: (length: 5)
+ ├── @ RegularExpressionNode (location: (1,0)-(1,5))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,0)-(1,1) = "/"
+ │ ├── content_loc: (1,1)-(1,4) = "wtf"
+ │ ├── closing_loc: (1,4)-(1,5) = "/"
+ │ └── unescaped: "wtf"
+ ├── @ RegularExpressionNode (location: (3,0)-(3,6))
+ │ ├── flags: multi_line, forced_us_ascii_encoding
+ │ ├── opening_loc: (3,0)-(3,1) = "/"
+ │ ├── content_loc: (3,1)-(3,4) = "wtf"
+ │ ├── closing_loc: (3,4)-(3,6) = "/m"
+ │ └── unescaped: "wtf"
+ ├── @ RegularExpressionNode (location: (5,0)-(5,6))
+ │ ├── flags: ascii_8bit, forced_us_ascii_encoding
+ │ ├── opening_loc: (5,0)-(5,1) = "/"
+ │ ├── content_loc: (5,1)-(5,4) = "wtf"
+ │ ├── closing_loc: (5,4)-(5,6) = "/n"
+ │ └── unescaped: "wtf"
+ ├── @ RegularExpressionNode (location: (7,0)-(7,7))
+ │ ├── flags: multi_line, ascii_8bit, forced_us_ascii_encoding
+ │ ├── opening_loc: (7,0)-(7,1) = "/"
+ │ ├── content_loc: (7,1)-(7,4) = "wtf"
+ │ ├── closing_loc: (7,4)-(7,7) = "/nm"
+ │ └── unescaped: "wtf"
+ └── @ RegularExpressionNode (location: (9,0)-(9,13))
+ ├── flags: multi_line, ascii_8bit, forced_us_ascii_encoding
+ ├── opening_loc: (9,0)-(9,1) = "/"
+ ├── content_loc: (9,1)-(9,4) = "wtf"
+ ├── closing_loc: (9,4)-(9,13) = "/nmnmnmnm"
+ └── unescaped: "wtf"
diff --git a/test/prism/snapshots/seattlerb/regexp_esc_C_slash.txt b/test/prism/snapshots/seattlerb/regexp_esc_C_slash.txt
new file mode 100644
index 0000000000..4dbedc44ca
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/regexp_esc_C_slash.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,7))
+ └── body: (length: 1)
+ └── @ RegularExpressionNode (location: (1,0)-(1,7))
+ ├── flags: forced_us_ascii_encoding
+ ├── opening_loc: (1,0)-(1,1) = "/"
+ ├── content_loc: (1,1)-(1,6) = "\\cC\\d"
+ ├── closing_loc: (1,6)-(1,7) = "/"
+ └── unescaped: "\\x03\\d"
diff --git a/test/prism/snapshots/seattlerb/regexp_esc_u.txt b/test/prism/snapshots/seattlerb/regexp_esc_u.txt
new file mode 100644
index 0000000000..bca451eb3b
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/regexp_esc_u.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,17))
+ └── body: (length: 1)
+ └── @ RegularExpressionNode (location: (1,0)-(1,17))
+ ├── flags: forced_us_ascii_encoding
+ ├── opening_loc: (1,0)-(1,1) = "/"
+ ├── content_loc: (1,1)-(1,16) = "[\\u0021-\\u0027]"
+ ├── closing_loc: (1,16)-(1,17) = "/"
+ └── unescaped: "[\\u0021-\\u0027]"
diff --git a/test/prism/snapshots/seattlerb/regexp_escape_extended.txt b/test/prism/snapshots/seattlerb/regexp_escape_extended.txt
new file mode 100644
index 0000000000..6568d2bd92
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/regexp_escape_extended.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,6))
+ └── body: (length: 1)
+ └── @ RegularExpressionNode (location: (1,0)-(1,6))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,1) = "/"
+ ├── content_loc: (1,1)-(1,5) = "\\“"
+ ├── closing_loc: (1,5)-(1,6) = "/"
+ └── unescaped: "“"
diff --git a/test/prism/snapshots/seattlerb/regexp_unicode_curlies.txt b/test/prism/snapshots/seattlerb/regexp_unicode_curlies.txt
new file mode 100644
index 0000000000..487161b4d0
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/regexp_unicode_curlies.txt
@@ -0,0 +1,17 @@
+@ ProgramNode (location: (1,0)-(3,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,8))
+ └── body: (length: 2)
+ ├── @ RegularExpressionNode (location: (1,0)-(1,15))
+ │ ├── flags: forced_utf8_encoding
+ │ ├── opening_loc: (1,0)-(1,1) = "/"
+ │ ├── content_loc: (1,1)-(1,14) = "\\u{c0de babe}"
+ │ ├── closing_loc: (1,14)-(1,15) = "/"
+ │ └── unescaped: "\\u{c0de babe}"
+ └── @ RegularExpressionNode (location: (3,0)-(3,8))
+ ├── flags: forced_utf8_encoding
+ ├── opening_loc: (3,0)-(3,1) = "/"
+ ├── content_loc: (3,1)-(3,7) = "\\u{df}"
+ ├── closing_loc: (3,7)-(3,8) = "/"
+ └── unescaped: "\\u{df}"
diff --git a/test/prism/snapshots/seattlerb/required_kwarg_no_value.txt b/test/prism/snapshots/seattlerb/required_kwarg_no_value.txt
new file mode 100644
index 0000000000..54595ac5cb
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/required_kwarg_no_value.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(2,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,3))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(2,3))
+ ├── name: :x
+ ├── name_loc: (1,4)-(1,5) = "x"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,12))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 2)
+ │ │ ├── @ RequiredKeywordParameterNode (location: (1,6)-(1,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :a
+ │ │ │ └── name_loc: (1,6)-(1,8) = "a:"
+ │ │ └── @ RequiredKeywordParameterNode (location: (1,10)-(1,12))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ └── name_loc: (1,10)-(1,12) = "b:"
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:a, :b]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (2,0)-(2,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/rescue_do_end_ensure_result.txt b/test/prism/snapshots/seattlerb/rescue_do_end_ensure_result.txt
new file mode 100644
index 0000000000..21f8bb08a5
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/rescue_do_end_ensure_result.txt
@@ -0,0 +1,58 @@
+@ ProgramNode (location: (1,0)-(5,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,8))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(5,8))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(5,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :proc
+ │ ├── message_loc: (1,0)-(1,4) = "proc"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,5)-(5,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (1,5)-(5,3))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (2,2)-(2,8))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (2,2)-(2,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (2,2)-(2,3) = ":"
+ │ │ │ ├── value_loc: (2,3)-(2,8) = "begin"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "begin"
+ │ │ ├── rescue_clause: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause:
+ │ │ │ @ EnsureNode (location: (3,0)-(5,3))
+ │ │ │ ├── ensure_keyword_loc: (3,0)-(3,6) = "ensure"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (4,2)-(4,9))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ SymbolNode (location: (4,2)-(4,9))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (4,2)-(4,3) = ":"
+ │ │ │ │ ├── value_loc: (4,3)-(4,9) = "ensure"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "ensure"
+ │ │ │ └── end_keyword_loc: (5,0)-(5,3) = "end"
+ │ │ └── end_keyword_loc: (5,0)-(5,3) = "end"
+ │ ├── opening_loc: (1,5)-(1,7) = "do"
+ │ └── closing_loc: (5,0)-(5,3) = "end"
+ ├── call_operator_loc: (5,3)-(5,4) = "."
+ ├── name: :call
+ ├── message_loc: (5,4)-(5,8) = "call"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/rescue_do_end_no_raise.txt b/test/prism/snapshots/seattlerb/rescue_do_end_no_raise.txt
new file mode 100644
index 0000000000..aa4e85c171
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/rescue_do_end_no_raise.txt
@@ -0,0 +1,75 @@
+@ ProgramNode (location: (1,0)-(9,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(9,3))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(9,3))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :tap
+ ├── message_loc: (1,0)-(1,3) = "tap"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,4)-(9,3))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body:
+ │ @ BeginNode (location: (1,4)-(9,3))
+ │ ├── begin_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (2,2)-(2,8))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SymbolNode (location: (2,2)-(2,8))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (2,2)-(2,3) = ":"
+ │ │ ├── value_loc: (2,3)-(2,8) = "begin"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "begin"
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (3,0)-(4,9))
+ │ │ ├── keyword_loc: (3,0)-(3,6) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (4,2)-(4,9))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (4,2)-(4,9))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (4,2)-(4,3) = ":"
+ │ │ │ ├── value_loc: (4,3)-(4,9) = "rescue"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "rescue"
+ │ │ └── consequent: ∅
+ │ ├── else_clause:
+ │ │ @ ElseNode (location: (5,0)-(7,6))
+ │ │ ├── else_keyword_loc: (5,0)-(5,4) = "else"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (6,2)-(6,7))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (6,2)-(6,7))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (6,2)-(6,3) = ":"
+ │ │ │ ├── value_loc: (6,3)-(6,7) = "else"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "else"
+ │ │ └── end_keyword_loc: (7,0)-(7,6) = "ensure"
+ │ ├── ensure_clause:
+ │ │ @ EnsureNode (location: (7,0)-(9,3))
+ │ │ ├── ensure_keyword_loc: (7,0)-(7,6) = "ensure"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (8,2)-(8,9))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (8,2)-(8,9))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (8,2)-(8,3) = ":"
+ │ │ │ ├── value_loc: (8,3)-(8,9) = "ensure"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "ensure"
+ │ │ └── end_keyword_loc: (9,0)-(9,3) = "end"
+ │ └── end_keyword_loc: (9,0)-(9,3) = "end"
+ ├── opening_loc: (1,4)-(1,6) = "do"
+ └── closing_loc: (9,0)-(9,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/rescue_do_end_raised.txt b/test/prism/snapshots/seattlerb/rescue_do_end_raised.txt
new file mode 100644
index 0000000000..06f67fae69
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/rescue_do_end_raised.txt
@@ -0,0 +1,52 @@
+@ ProgramNode (location: (1,0)-(5,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,3))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(5,3))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :tap
+ ├── message_loc: (1,0)-(1,3) = "tap"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,4)-(5,3))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body:
+ │ @ BeginNode (location: (1,4)-(5,3))
+ │ ├── begin_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (2,2)-(2,7))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (2,2)-(2,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :raise
+ │ │ ├── message_loc: (2,2)-(2,7) = "raise"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause:
+ │ │ @ EnsureNode (location: (3,0)-(5,3))
+ │ │ ├── ensure_keyword_loc: (3,0)-(3,6) = "ensure"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (4,2)-(4,9))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (4,2)-(4,9))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (4,2)-(4,3) = ":"
+ │ │ │ ├── value_loc: (4,3)-(4,9) = "ensure"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "ensure"
+ │ │ └── end_keyword_loc: (5,0)-(5,3) = "end"
+ │ └── end_keyword_loc: (5,0)-(5,3) = "end"
+ ├── opening_loc: (1,4)-(1,6) = "do"
+ └── closing_loc: (5,0)-(5,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/rescue_do_end_rescued.txt b/test/prism/snapshots/seattlerb/rescue_do_end_rescued.txt
new file mode 100644
index 0000000000..b4576c3bb2
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/rescue_do_end_rescued.txt
@@ -0,0 +1,79 @@
+@ ProgramNode (location: (1,0)-(9,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(9,3))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(9,3))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :tap
+ ├── message_loc: (1,0)-(1,3) = "tap"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,4)-(9,3))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body:
+ │ @ BeginNode (location: (1,4)-(9,3))
+ │ ├── begin_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (2,2)-(2,7))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (2,2)-(2,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :raise
+ │ │ ├── message_loc: (2,2)-(2,7) = "raise"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (3,0)-(4,9))
+ │ │ ├── keyword_loc: (3,0)-(3,6) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (4,2)-(4,9))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (4,2)-(4,9))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (4,2)-(4,3) = ":"
+ │ │ │ ├── value_loc: (4,3)-(4,9) = "rescue"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "rescue"
+ │ │ └── consequent: ∅
+ │ ├── else_clause:
+ │ │ @ ElseNode (location: (5,0)-(7,6))
+ │ │ ├── else_keyword_loc: (5,0)-(5,4) = "else"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (6,2)-(6,7))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (6,2)-(6,7))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (6,2)-(6,3) = ":"
+ │ │ │ ├── value_loc: (6,3)-(6,7) = "else"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "else"
+ │ │ └── end_keyword_loc: (7,0)-(7,6) = "ensure"
+ │ ├── ensure_clause:
+ │ │ @ EnsureNode (location: (7,0)-(9,3))
+ │ │ ├── ensure_keyword_loc: (7,0)-(7,6) = "ensure"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (8,2)-(8,9))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (8,2)-(8,9))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (8,2)-(8,3) = ":"
+ │ │ │ ├── value_loc: (8,3)-(8,9) = "ensure"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "ensure"
+ │ │ └── end_keyword_loc: (9,0)-(9,3) = "end"
+ │ └── end_keyword_loc: (9,0)-(9,3) = "end"
+ ├── opening_loc: (1,4)-(1,6) = "do"
+ └── closing_loc: (9,0)-(9,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/rescue_in_block.txt b/test/prism/snapshots/seattlerb/rescue_in_block.txt
new file mode 100644
index 0000000000..daac2b6776
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/rescue_in_block.txt
@@ -0,0 +1,47 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(4,3))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :blah
+ ├── message_loc: (1,0)-(1,4) = "blah"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,5)-(4,3))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body:
+ │ @ BeginNode (location: (1,5)-(4,3))
+ │ ├── begin_keyword_loc: ∅
+ │ ├── statements: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (2,0)-(3,7))
+ │ │ ├── keyword_loc: (2,0)-(2,6) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (3,2)-(3,7))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (3,2)-(3,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :stuff
+ │ │ │ ├── message_loc: (3,2)-(3,7) = "stuff"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (4,0)-(4,3) = "end"
+ ├── opening_loc: (1,5)-(1,7) = "do"
+ └── closing_loc: (4,0)-(4,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/rescue_parens.txt b/test/prism/snapshots/seattlerb/rescue_parens.txt
new file mode 100644
index 0000000000..d086095e7a
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/rescue_parens.txt
@@ -0,0 +1,48 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,14))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,14))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ ParenthesesNode (location: (1,2)-(1,14))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,3)-(1,13))
+ │ │ └── body: (length: 1)
+ │ │ └── @ RescueModifierNode (location: (1,3)-(1,13))
+ │ │ ├── expression:
+ │ │ │ @ CallNode (location: (1,3)-(1,4))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (1,3)-(1,4) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── keyword_loc: (1,5)-(1,11) = "rescue"
+ │ │ └── rescue_expression:
+ │ │ @ CallNode (location: (1,12)-(1,13))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :c
+ │ │ ├── message_loc: (1,12)-(1,13) = "c"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (1,2)-(1,3) = "("
+ │ └── closing_loc: (1,13)-(1,14) = ")"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/return_call_assocs.txt b/test/prism/snapshots/seattlerb/return_call_assocs.txt
new file mode 100644
index 0000000000..66b3cb2f82
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/return_call_assocs.txt
@@ -0,0 +1,218 @@
+@ ProgramNode (location: (1,0)-(11,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(11,14))
+ └── body: (length: 6)
+ ├── @ ReturnNode (location: (1,0)-(1,17))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (1,0)-(1,6) = "return"
+ │ └── arguments:
+ │ @ ArgumentsNode (location: (1,7)-(1,17))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ IntegerNode (location: (1,7)-(1,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── @ KeywordHashNode (location: (1,10)-(1,17))
+ │ ├── flags: symbol_keys
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (1,10)-(1,17))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (1,10)-(1,12))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,10)-(1,11) = ":"
+ │ │ ├── value_loc: (1,11)-(1,12) = "z"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "z"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,16)-(1,17))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (1,13)-(1,15) = "=>"
+ ├── @ ReturnNode (location: (3,0)-(3,26))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (3,0)-(3,6) = "return"
+ │ └── arguments:
+ │ @ ArgumentsNode (location: (3,7)-(3,26))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ IntegerNode (location: (3,7)-(3,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── @ KeywordHashNode (location: (3,10)-(3,26))
+ │ ├── flags: symbol_keys
+ │ └── elements: (length: 2)
+ │ ├── @ AssocNode (location: (3,10)-(3,17))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (3,10)-(3,12))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (3,10)-(3,11) = ":"
+ │ │ │ ├── value_loc: (3,11)-(3,12) = "z"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "z"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (3,16)-(3,17))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (3,13)-(3,15) = "=>"
+ │ └── @ AssocNode (location: (3,19)-(3,26))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (3,19)-(3,21))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (3,19)-(3,20) = ":"
+ │ │ ├── value_loc: (3,20)-(3,21) = "w"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "w"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (3,25)-(3,26))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ └── operator_loc: (3,22)-(3,24) = "=>"
+ ├── @ ReturnNode (location: (5,0)-(5,14))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (5,0)-(5,6) = "return"
+ │ └── arguments:
+ │ @ ArgumentsNode (location: (5,7)-(5,14))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (5,7)-(5,14))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :y
+ │ ├── message_loc: (5,7)-(5,8) = "y"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (5,9)-(5,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (5,9)-(5,14))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (5,9)-(5,14))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (5,9)-(5,11))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (5,9)-(5,10) = ":"
+ │ │ │ ├── value_loc: (5,10)-(5,11) = "z"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "z"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (5,13)-(5,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (5,11)-(5,13) = "=>"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ ReturnNode (location: (7,0)-(7,12))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (7,0)-(7,6) = "return"
+ │ └── arguments:
+ │ @ ArgumentsNode (location: (7,7)-(7,12))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (7,7)-(7,12))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :y
+ │ ├── message_loc: (7,7)-(7,8) = "y"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (7,9)-(7,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (7,9)-(7,12))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (7,9)-(7,12))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (7,9)-(7,11))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (7,9)-(7,10) = "z"
+ │ │ │ ├── closing_loc: (7,10)-(7,11) = ":"
+ │ │ │ └── unescaped: "z"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (7,11)-(7,12))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ ReturnNode (location: (9,0)-(9,13))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (9,0)-(9,6) = "return"
+ │ └── arguments:
+ │ @ ArgumentsNode (location: (9,7)-(9,13))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (9,7)-(9,13))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :y
+ │ ├── message_loc: (9,7)-(9,8) = "y"
+ │ ├── opening_loc: (9,8)-(9,9) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (9,9)-(9,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (9,9)-(9,12))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (9,9)-(9,12))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (9,9)-(9,11))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (9,9)-(9,10) = "z"
+ │ │ │ ├── closing_loc: (9,10)-(9,11) = ":"
+ │ │ │ └── unescaped: "z"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (9,11)-(9,12))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: ∅
+ │ ├── closing_loc: (9,12)-(9,13) = ")"
+ │ └── block: ∅
+ └── @ ReturnNode (location: (11,0)-(11,14))
+ ├── flags: ∅
+ ├── keyword_loc: (11,0)-(11,6) = "return"
+ └── arguments:
+ @ ArgumentsNode (location: (11,7)-(11,14))
+ ├── flags: ∅
+ └── arguments: (length: 1)
+ └── @ CallNode (location: (11,7)-(11,14))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :y
+ ├── message_loc: (11,7)-(11,8) = "y"
+ ├── opening_loc: (11,8)-(11,9) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (11,9)-(11,13))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ KeywordHashNode (location: (11,9)-(11,13))
+ │ ├── flags: ∅
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (11,9)-(11,13))
+ │ ├── key:
+ │ │ @ CallNode (location: (11,9)-(11,10))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :z
+ │ │ ├── message_loc: (11,9)-(11,10) = "z"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── value:
+ │ │ @ IntegerNode (location: (11,12)-(11,13))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (11,10)-(11,12) = "=>"
+ ├── closing_loc: (11,13)-(11,14) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/rhs_asgn.txt b/test/prism/snapshots/seattlerb/rhs_asgn.txt
new file mode 100644
index 0000000000..9ee187218b
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/rhs_asgn.txt
@@ -0,0 +1,15 @@
+@ ProgramNode (location: (1,0)-(1,7))
+├── locals: [:n]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,7))
+ └── body: (length: 1)
+ └── @ MatchRequiredNode (location: (1,0)-(1,7))
+ ├── value:
+ │ @ IntegerNode (location: (1,0)-(1,2))
+ │ ├── flags: decimal
+ │ └── value: 42
+ ├── pattern:
+ │ @ LocalVariableTargetNode (location: (1,6)-(1,7))
+ │ ├── name: :n
+ │ └── depth: 0
+ └── operator_loc: (1,3)-(1,5) = "=>"
diff --git a/test/prism/snapshots/seattlerb/ruby21_numbers.txt b/test/prism/snapshots/seattlerb/ruby21_numbers.txt
new file mode 100644
index 0000000000..34a3452d1b
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/ruby21_numbers.txt
@@ -0,0 +1,27 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ ArrayNode (location: (1,0)-(1,13))
+ ├── flags: ∅
+ ├── elements: (length: 3)
+ │ ├── @ ImaginaryNode (location: (1,1)-(1,3))
+ │ │ └── numeric:
+ │ │ @ IntegerNode (location: (1,1)-(1,2))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── @ RationalNode (location: (1,5)-(1,7))
+ │ │ └── numeric:
+ │ │ @ IntegerNode (location: (1,5)-(1,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ └── @ ImaginaryNode (location: (1,9)-(1,12))
+ │ └── numeric:
+ │ @ RationalNode (location: (1,9)-(1,11))
+ │ └── numeric:
+ │ @ IntegerNode (location: (1,9)-(1,10))
+ │ ├── flags: decimal
+ │ └── value: 3
+ ├── opening_loc: (1,0)-(1,1) = "["
+ └── closing_loc: (1,12)-(1,13) = "]"
diff --git a/test/prism/snapshots/seattlerb/safe_attrasgn.txt b/test/prism/snapshots/seattlerb/safe_attrasgn.txt
new file mode 100644
index 0000000000..3cec95ae7c
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/safe_attrasgn.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,8))
+ ├── flags: safe_navigation, attribute_write
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,1)-(1,3) = "&."
+ ├── name: :b=
+ ├── message_loc: (1,3)-(1,4) = "b"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,7)-(1,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,7)-(1,8))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/safe_attrasgn_constant.txt b/test/prism/snapshots/seattlerb/safe_attrasgn_constant.txt
new file mode 100644
index 0000000000..baea063186
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/safe_attrasgn_constant.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,8))
+ ├── flags: safe_navigation, attribute_write
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,1)-(1,3) = "&."
+ ├── name: :B=
+ ├── message_loc: (1,3)-(1,4) = "B"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,7)-(1,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,7)-(1,8))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/safe_call.txt b/test/prism/snapshots/seattlerb/safe_call.txt
new file mode 100644
index 0000000000..7b402d9ef2
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/safe_call.txt
@@ -0,0 +1,25 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,4))
+ ├── flags: safe_navigation
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,1)-(1,3) = "&."
+ ├── name: :b
+ ├── message_loc: (1,3)-(1,4) = "b"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/safe_call_after_newline.txt b/test/prism/snapshots/seattlerb/safe_call_after_newline.txt
new file mode 100644
index 0000000000..0a69cbc9e5
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/safe_call_after_newline.txt
@@ -0,0 +1,25 @@
+@ ProgramNode (location: (1,0)-(2,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,3))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(2,3))
+ ├── flags: safe_navigation
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (2,0)-(2,2) = "&."
+ ├── name: :b
+ ├── message_loc: (2,2)-(2,3) = "b"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/safe_call_dot_parens.txt b/test/prism/snapshots/seattlerb/safe_call_dot_parens.txt
new file mode 100644
index 0000000000..1d6ba9e49e
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/safe_call_dot_parens.txt
@@ -0,0 +1,25 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,5))
+ ├── flags: safe_navigation
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,1)-(1,3) = "&."
+ ├── name: :call
+ ├── message_loc: ∅
+ ├── opening_loc: (1,3)-(1,4) = "("
+ ├── arguments: ∅
+ ├── closing_loc: (1,4)-(1,5) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/safe_call_newline.txt b/test/prism/snapshots/seattlerb/safe_call_newline.txt
new file mode 100644
index 0000000000..7b402d9ef2
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/safe_call_newline.txt
@@ -0,0 +1,25 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,4))
+ ├── flags: safe_navigation
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,1)-(1,3) = "&."
+ ├── name: :b
+ ├── message_loc: (1,3)-(1,4) = "b"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/safe_call_operator.txt b/test/prism/snapshots/seattlerb/safe_call_operator.txt
new file mode 100644
index 0000000000..d1f9b1ea9e
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/safe_call_operator.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (1,0)-(1,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,6))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,6))
+ ├── flags: safe_navigation
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,1)-(1,3) = "&."
+ ├── name: :>
+ ├── message_loc: (1,3)-(1,4) = ">"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,5)-(1,6))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,5)-(1,6))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/safe_call_rhs_newline.txt b/test/prism/snapshots/seattlerb/safe_call_rhs_newline.txt
new file mode 100644
index 0000000000..34790ebb33
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/safe_call_rhs_newline.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: [:c]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ LocalVariableWriteNode (location: (1,0)-(1,8))
+ ├── name: :c
+ ├── depth: 0
+ ├── name_loc: (1,0)-(1,1) = "c"
+ ├── value:
+ │ @ CallNode (location: (1,4)-(1,8))
+ │ ├── flags: safe_navigation
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,4)-(1,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (1,4)-(1,5) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (1,5)-(1,7) = "&."
+ │ ├── name: :b
+ │ ├── message_loc: (1,7)-(1,8) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/seattlerb/safe_calls.txt b/test/prism/snapshots/seattlerb/safe_calls.txt
new file mode 100644
index 0000000000..54e591d9c0
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/safe_calls.txt
@@ -0,0 +1,41 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,10))
+ ├── flags: safe_navigation
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,4))
+ │ ├── flags: safe_navigation
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,0)-(1,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (1,1)-(1,3) = "&."
+ │ ├── name: :b
+ │ ├── message_loc: (1,3)-(1,4) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,4)-(1,6) = "&."
+ ├── name: :c
+ ├── message_loc: (1,6)-(1,7) = "c"
+ ├── opening_loc: (1,7)-(1,8) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,8)-(1,9))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,8)-(1,9))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: (1,9)-(1,10) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/safe_op_asgn.txt b/test/prism/snapshots/seattlerb/safe_op_asgn.txt
new file mode 100644
index 0000000000..7a9fd2b7f7
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/safe_op_asgn.txt
@@ -0,0 +1,41 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ CallOperatorWriteNode (location: (1,0)-(1,11))
+ ├── flags: safe_navigation
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,1)-(1,3) = "&."
+ ├── message_loc: (1,3)-(1,4) = "b"
+ ├── read_name: :b
+ ├── write_name: :b=
+ ├── operator: :+
+ ├── operator_loc: (1,5)-(1,7) = "+="
+ └── value:
+ @ CallNode (location: (1,8)-(1,11))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :x
+ ├── message_loc: (1,8)-(1,9) = "x"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,10)-(1,11))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,10)-(1,11))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/safe_op_asgn2.txt b/test/prism/snapshots/seattlerb/safe_op_asgn2.txt
new file mode 100644
index 0000000000..bdb0e06156
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/safe_op_asgn2.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(2,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,1))
+ └── body: (length: 1)
+ └── @ CallOrWriteNode (location: (1,0)-(2,1))
+ ├── flags: safe_navigation
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,1)-(1,3) = "&."
+ ├── message_loc: (1,3)-(1,4) = "b"
+ ├── read_name: :b
+ ├── write_name: :b=
+ ├── operator_loc: (1,5)-(1,8) = "||="
+ └── value:
+ @ CallNode (location: (2,0)-(2,1))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :x
+ ├── message_loc: (2,0)-(2,1) = "x"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/slashy_newlines_within_string.txt b/test/prism/snapshots/seattlerb/slashy_newlines_within_string.txt
new file mode 100644
index 0000000000..f9be33ffdd
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/slashy_newlines_within_string.txt
@@ -0,0 +1,57 @@
+@ ProgramNode (location: (1,0)-(6,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(6,5))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(4,8))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :puts
+ │ ├── message_loc: (1,0)-(1,4) = "puts"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,5)-(4,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (1,5)-(4,8))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,5)-(1,6) = "\""
+ │ │ ├── content_loc: (1,6)-(4,7) = "hello\\\n my\\\n dear\\\n friend"
+ │ │ ├── closing_loc: (4,7)-(4,8) = "\""
+ │ │ └── unescaped: "hello my dear friend"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (6,0)-(6,5))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (6,0)-(6,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (6,0)-(6,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :+
+ ├── message_loc: (6,2)-(6,3) = "+"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (6,4)-(6,5))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (6,4)-(6,5))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :b
+ │ ├── message_loc: (6,4)-(6,5) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/stabby_arg_no_paren.txt b/test/prism/snapshots/seattlerb/stabby_arg_no_paren.txt
new file mode 100644
index 0000000000..e665565d38
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/stabby_arg_no_paren.txt
@@ -0,0 +1,28 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ LambdaNode (location: (1,0)-(1,5))
+ ├── locals: [:a]
+ ├── operator_loc: (1,0)-(1,2) = "->"
+ ├── opening_loc: (1,3)-(1,4) = "{"
+ ├── closing_loc: (1,4)-(1,5) = "}"
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,2)-(1,3))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,2)-(1,3))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,2)-(1,3))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: ∅
+ │ └── closing_loc: ∅
+ └── body: ∅
diff --git a/test/prism/snapshots/seattlerb/stabby_arg_opt_splat_arg_block_omfg.txt b/test/prism/snapshots/seattlerb/stabby_arg_opt_splat_arg_block_omfg.txt
new file mode 100644
index 0000000000..0b0000ef33
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/stabby_arg_opt_splat_arg_block_omfg.txt
@@ -0,0 +1,50 @@
+@ ProgramNode (location: (1,0)-(1,23))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,23))
+ └── body: (length: 1)
+ └── @ LambdaNode (location: (1,0)-(1,23))
+ ├── locals: [:b, :c, :d, :e, :f]
+ ├── operator_loc: (1,0)-(1,2) = "->"
+ ├── opening_loc: (1,21)-(1,22) = "{"
+ ├── closing_loc: (1,22)-(1,23) = "}"
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,2)-(1,21))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,3)-(1,20))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,3)-(1,4))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :b
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,6)-(1,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── name_loc: (1,6)-(1,7) = "c"
+ │ │ │ ├── operator_loc: (1,7)-(1,8) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,8)-(1,9))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (1,11)-(1,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :d
+ │ │ │ ├── name_loc: (1,12)-(1,13) = "d"
+ │ │ │ └── operator_loc: (1,11)-(1,12) = "*"
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,15)-(1,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :e
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (1,18)-(1,20))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :f
+ │ │ ├── name_loc: (1,19)-(1,20) = "f"
+ │ │ └── operator_loc: (1,18)-(1,19) = "&"
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,2)-(1,3) = "("
+ │ └── closing_loc: (1,20)-(1,21) = ")"
+ └── body: ∅
diff --git a/test/prism/snapshots/seattlerb/stabby_block_iter_call.txt b/test/prism/snapshots/seattlerb/stabby_block_iter_call.txt
new file mode 100644
index 0000000000..e51c7d97ed
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/stabby_block_iter_call.txt
@@ -0,0 +1,58 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(4,3))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :x
+ ├── message_loc: (1,0)-(1,1) = "x"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(4,3))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ LambdaNode (location: (1,2)-(4,3))
+ │ ├── locals: []
+ │ ├── operator_loc: (1,2)-(1,4) = "->"
+ │ ├── opening_loc: (1,8)-(1,10) = "do"
+ │ ├── closing_loc: (4,0)-(4,3) = "end"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (1,5)-(1,7))
+ │ │ ├── parameters: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (1,5)-(1,6) = "("
+ │ │ └── closing_loc: (1,6)-(1,7) = ")"
+ │ └── body:
+ │ @ StatementsNode (location: (2,0)-(3,3))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (2,0)-(3,3))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (2,0)-(2,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (2,0)-(2,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (2,1)-(2,2) = "."
+ │ ├── name: :b
+ │ ├── message_loc: (2,2)-(2,3) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (2,4)-(3,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (2,4)-(2,6) = "do"
+ │ └── closing_loc: (3,0)-(3,3) = "end"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/stabby_block_iter_call_no_target_with_arg.txt b/test/prism/snapshots/seattlerb/stabby_block_iter_call_no_target_with_arg.txt
new file mode 100644
index 0000000000..d7a268a5d5
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/stabby_block_iter_call_no_target_with_arg.txt
@@ -0,0 +1,54 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(4,3))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :x
+ ├── message_loc: (1,0)-(1,1) = "x"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(4,3))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ LambdaNode (location: (1,2)-(4,3))
+ │ ├── locals: []
+ │ ├── operator_loc: (1,2)-(1,4) = "->"
+ │ ├── opening_loc: (1,8)-(1,10) = "do"
+ │ ├── closing_loc: (4,0)-(4,3) = "end"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (1,5)-(1,7))
+ │ │ ├── parameters: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (1,5)-(1,6) = "("
+ │ │ └── closing_loc: (1,6)-(1,7) = ")"
+ │ └── body:
+ │ @ StatementsNode (location: (2,0)-(3,3))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (2,0)-(3,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (2,0)-(2,1) = "a"
+ │ ├── opening_loc: (2,1)-(2,2) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (2,2)-(2,3))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (2,2)-(2,3))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: (2,3)-(2,4) = ")"
+ │ └── block:
+ │ @ BlockNode (location: (2,5)-(3,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (2,5)-(2,7) = "do"
+ │ └── closing_loc: (3,0)-(3,3) = "end"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/stabby_block_kw.txt b/test/prism/snapshots/seattlerb/stabby_block_kw.txt
new file mode 100644
index 0000000000..7addbb8b28
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/stabby_block_kw.txt
@@ -0,0 +1,33 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ LambdaNode (location: (1,0)-(1,13))
+ ├── locals: [:k]
+ ├── operator_loc: (1,0)-(1,2) = "->"
+ ├── opening_loc: (1,10)-(1,11) = "{"
+ ├── closing_loc: (1,12)-(1,13) = "}"
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,3)-(1,9))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,4)-(1,8))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ OptionalKeywordParameterNode (location: (1,4)-(1,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :k
+ │ │ │ ├── name_loc: (1,4)-(1,6) = "k:"
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,6)-(1,8))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,3)-(1,4) = "("
+ │ └── closing_loc: (1,8)-(1,9) = ")"
+ └── body: ∅
diff --git a/test/prism/snapshots/seattlerb/stabby_block_kw__required.txt b/test/prism/snapshots/seattlerb/stabby_block_kw__required.txt
new file mode 100644
index 0000000000..b5040d91db
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/stabby_block_kw__required.txt
@@ -0,0 +1,29 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ LambdaNode (location: (1,0)-(1,11))
+ ├── locals: [:k]
+ ├── operator_loc: (1,0)-(1,2) = "->"
+ ├── opening_loc: (1,8)-(1,9) = "{"
+ ├── closing_loc: (1,10)-(1,11) = "}"
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,3)-(1,7))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,4)-(1,6))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ RequiredKeywordParameterNode (location: (1,4)-(1,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :k
+ │ │ │ └── name_loc: (1,4)-(1,6) = "k:"
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,3)-(1,4) = "("
+ │ └── closing_loc: (1,6)-(1,7) = ")"
+ └── body: ∅
diff --git a/test/prism/snapshots/seattlerb/stabby_proc_scope.txt b/test/prism/snapshots/seattlerb/stabby_proc_scope.txt
new file mode 100644
index 0000000000..898f823f24
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/stabby_proc_scope.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ LambdaNode (location: (1,0)-(1,11))
+ ├── locals: [:a, :b]
+ ├── operator_loc: (1,0)-(1,2) = "->"
+ ├── opening_loc: (1,9)-(1,10) = "{"
+ ├── closing_loc: (1,10)-(1,11) = "}"
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,2)-(1,8))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,3)-(1,4))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,3)-(1,4))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 1)
+ │ │ └── @ BlockLocalVariableNode (location: (1,6)-(1,7))
+ │ │ ├── flags: ∅
+ │ │ └── name: :b
+ │ ├── opening_loc: (1,2)-(1,3) = "("
+ │ └── closing_loc: (1,7)-(1,8) = ")"
+ └── body: ∅
diff --git a/test/prism/snapshots/seattlerb/str_backslashes.txt b/test/prism/snapshots/seattlerb/str_backslashes.txt
new file mode 100644
index 0000000000..ec41a89c38
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/str_backslashes.txt
@@ -0,0 +1,24 @@
+@ ProgramNode (location: (1,0)-(1,204))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,204))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,204))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :x
+ ├── message_loc: (1,0)-(1,1) = "x"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,204))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ StringNode (location: (1,2)-(1,204))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,2)-(1,3) = "'"
+ │ ├── content_loc: (1,3)-(1,203) = "\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n"
+ │ ├── closing_loc: (1,203)-(1,204) = "'"
+ │ └── unescaped: "\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/str_double_double_escaped_newline.txt b/test/prism/snapshots/seattlerb/str_double_double_escaped_newline.txt
new file mode 100644
index 0000000000..620b43f631
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/str_double_double_escaped_newline.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,7))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,2)-(1,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (1,2)-(1,7))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,2)-(1,3) = "\""
+ │ │ ├── content_loc: (1,3)-(1,6) = "\\\\n"
+ │ │ ├── closing_loc: (1,6)-(1,7) = "\""
+ │ │ └── unescaped: "\\n"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (1,8)-(1,9))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :b
+ ├── message_loc: (1,8)-(1,9) = "b"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/str_double_escaped_newline.txt b/test/prism/snapshots/seattlerb/str_double_escaped_newline.txt
new file mode 100644
index 0000000000..2aee91b75c
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/str_double_escaped_newline.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,6))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,2)-(1,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (1,2)-(1,6))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,2)-(1,3) = "\""
+ │ │ ├── content_loc: (1,3)-(1,5) = "\\n"
+ │ │ ├── closing_loc: (1,5)-(1,6) = "\""
+ │ │ └── unescaped: "\n"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (1,7)-(1,8))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :b
+ ├── message_loc: (1,7)-(1,8) = "b"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/str_double_newline.txt b/test/prism/snapshots/seattlerb/str_double_newline.txt
new file mode 100644
index 0000000000..eb249cde8a
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/str_double_newline.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(2,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,3))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(2,1))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,2)-(2,1))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (1,2)-(2,1))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,2)-(1,3) = "\""
+ │ │ ├── content_loc: (1,3)-(2,0) = "\n"
+ │ │ ├── closing_loc: (2,0)-(2,1) = "\""
+ │ │ └── unescaped: "\n"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (2,2)-(2,3))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :b
+ ├── message_loc: (2,2)-(2,3) = "b"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/str_evstr.txt b/test/prism/snapshots/seattlerb/str_evstr.txt
new file mode 100644
index 0000000000..7010ad0a68
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/str_evstr.txt
@@ -0,0 +1,32 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(1,8))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,1) = "\""
+ ├── parts: (length: 2)
+ │ ├── @ StringNode (location: (1,1)-(1,3))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (1,1)-(1,3) = "a "
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a "
+ │ └── @ EmbeddedStatementsNode (location: (1,3)-(1,7))
+ │ ├── opening_loc: (1,3)-(1,5) = "\#{"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,5)-(1,6))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,5)-(1,6))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (1,5)-(1,6) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── closing_loc: (1,6)-(1,7) = "}"
+ └── closing_loc: (1,7)-(1,8) = "\""
diff --git a/test/prism/snapshots/seattlerb/str_evstr_escape.txt b/test/prism/snapshots/seattlerb/str_evstr_escape.txt
new file mode 100644
index 0000000000..3867574c7c
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/str_evstr_escape.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(1,16))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,16))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(1,16))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,1) = "\""
+ ├── parts: (length: 3)
+ │ ├── @ StringNode (location: (1,1)-(1,3))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (1,1)-(1,3) = "a "
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a "
+ │ ├── @ EmbeddedStatementsNode (location: (1,3)-(1,7))
+ │ │ ├── opening_loc: (1,3)-(1,5) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,5)-(1,6))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (1,5)-(1,6))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (1,5)-(1,6) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── closing_loc: (1,6)-(1,7) = "}"
+ │ └── @ StringNode (location: (1,7)-(1,15))
+ │ ├── flags: forced_utf8_encoding, frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (1,7)-(1,15) = "\\302\\275"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "½"
+ └── closing_loc: (1,15)-(1,16) = "\""
diff --git a/test/prism/snapshots/seattlerb/str_heredoc_interp.txt b/test/prism/snapshots/seattlerb/str_heredoc_interp.txt
new file mode 100644
index 0000000000..bb7bbba259
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/str_heredoc_interp.txt
@@ -0,0 +1,32 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(1,4))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,4) = "<<\"\""
+ ├── parts: (length: 2)
+ │ ├── @ EmbeddedStatementsNode (location: (2,0)-(2,4))
+ │ │ ├── opening_loc: (2,0)-(2,2) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (2,2)-(2,3))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (2,2)-(2,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :x
+ │ │ │ ├── message_loc: (2,2)-(2,3) = "x"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── closing_loc: (2,3)-(2,4) = "}"
+ │ └── @ StringNode (location: (2,4)-(4,0))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (2,4)-(4,0) = "\nblah2\n"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "\nblah2\n"
+ └── closing_loc: (4,0)-(5,0) = "\n"
diff --git a/test/prism/snapshots/seattlerb/str_interp_ternary_or_label.txt b/test/prism/snapshots/seattlerb/str_interp_ternary_or_label.txt
new file mode 100644
index 0000000000..5a2f435e0e
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/str_interp_ternary_or_label.txt
@@ -0,0 +1,105 @@
+@ ProgramNode (location: (1,0)-(1,23))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,23))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(1,23))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,1) = "\""
+ ├── parts: (length: 1)
+ │ └── @ EmbeddedStatementsNode (location: (1,1)-(1,22))
+ │ ├── opening_loc: (1,1)-(1,3) = "\#{"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,3)-(1,21))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IfNode (location: (1,3)-(1,21))
+ │ │ ├── if_keyword_loc: ∅
+ │ │ ├── predicate:
+ │ │ │ @ CallNode (location: (1,3)-(1,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ CallNode (location: (1,3)-(1,4))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── message_loc: (1,3)-(1,4) = "a"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── call_operator_loc: (1,4)-(1,5) = "."
+ │ │ │ ├── name: :b?
+ │ │ │ ├── message_loc: (1,5)-(1,7) = "b?"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── then_keyword_loc: (1,8)-(1,9) = "?"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,10)-(1,17))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (1,10)-(1,17))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ CallNode (location: (1,10)-(1,14))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── receiver:
+ │ │ │ │ │ @ StringNode (location: (1,10)-(1,12))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── opening_loc: (1,10)-(1,11) = "\""
+ │ │ │ │ │ ├── content_loc: (1,11)-(1,11) = ""
+ │ │ │ │ │ ├── closing_loc: (1,11)-(1,12) = "\""
+ │ │ │ │ │ └── unescaped: ""
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :+
+ │ │ │ │ ├── message_loc: (1,12)-(1,13) = "+"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (1,13)-(1,14))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (1,13)-(1,14))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :a
+ │ │ │ │ │ ├── message_loc: (1,13)-(1,14) = "a"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :+
+ │ │ │ ├── message_loc: (1,14)-(1,15) = "+"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (1,15)-(1,17))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ StringNode (location: (1,15)-(1,17))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: (1,15)-(1,16) = "\""
+ │ │ │ │ ├── content_loc: (1,16)-(1,16) = ""
+ │ │ │ │ ├── closing_loc: (1,16)-(1,17) = "\""
+ │ │ │ │ └── unescaped: ""
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── consequent:
+ │ │ │ @ ElseNode (location: (1,17)-(1,21))
+ │ │ │ ├── else_keyword_loc: (1,17)-(1,18) = ":"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (1,19)-(1,21))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ StringNode (location: (1,19)-(1,21))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: (1,19)-(1,20) = "\""
+ │ │ │ │ ├── content_loc: (1,20)-(1,20) = ""
+ │ │ │ │ ├── closing_loc: (1,20)-(1,21) = "\""
+ │ │ │ │ └── unescaped: ""
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ └── end_keyword_loc: ∅
+ │ └── closing_loc: (1,21)-(1,22) = "}"
+ └── closing_loc: (1,22)-(1,23) = "\""
diff --git a/test/prism/snapshots/seattlerb/str_lit_concat_bad_encodings.txt b/test/prism/snapshots/seattlerb/str_lit_concat_bad_encodings.txt
new file mode 100644
index 0000000000..0066f66e84
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/str_lit_concat_bad_encodings.txt
@@ -0,0 +1,22 @@
+@ ProgramNode (location: (1,0)-(2,66))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,66))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(2,66))
+ ├── flags: ∅
+ ├── opening_loc: ∅
+ ├── parts: (length: 2)
+ │ ├── @ StringNode (location: (1,0)-(1,62))
+ │ │ ├── flags: forced_utf8_encoding, frozen
+ │ │ ├── opening_loc: (1,0)-(1,1) = "\""
+ │ │ ├── content_loc: (1,1)-(1,61) = "\\xE3\\xD3\\x8B\\xE3\\x83\\xBC\\x83\\xE3\\x83\\xE3\\x82\\xB3\\xA3\\x82\\x99"
+ │ │ ├── closing_loc: (1,61)-(1,62) = "\""
+ │ │ └── unescaped: "\xE3Ӌー\x83\xE3\x83コ\xA3\x82\x99"
+ │ └── @ StringNode (location: (2,8)-(2,66))
+ │ ├── flags: forced_utf8_encoding, frozen
+ │ ├── opening_loc: (2,8)-(2,9) = "\""
+ │ ├── content_loc: (2,9)-(2,65) = "\\xE3\\x83\\xB3\\xE3\\x83\\x8F\\xE3\\x82\\x9A\\xC3\\xBD;foo@bar.com"
+ │ ├── closing_loc: (2,65)-(2,66) = "\""
+ │ └── unescaped: "ンãƒã‚šÃ½;foo@bar.com"
+ └── closing_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/str_newline_hash_line_number.txt b/test/prism/snapshots/seattlerb/str_newline_hash_line_number.txt
new file mode 100644
index 0000000000..d55d965068
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/str_newline_hash_line_number.txt
@@ -0,0 +1,14 @@
+@ ProgramNode (location: (1,0)-(2,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,1))
+ └── body: (length: 2)
+ ├── @ StringNode (location: (1,0)-(1,11))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,1) = "\""
+ │ ├── content_loc: (1,1)-(1,10) = "\\n\\n\\n\\n#"
+ │ ├── closing_loc: (1,10)-(1,11) = "\""
+ │ └── unescaped: "\n\n\n\n#"
+ └── @ IntegerNode (location: (2,0)-(2,1))
+ ├── flags: decimal
+ └── value: 1
diff --git a/test/prism/snapshots/seattlerb/str_pct_Q_nested.txt b/test/prism/snapshots/seattlerb/str_pct_Q_nested.txt
new file mode 100644
index 0000000000..1db0e76270
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/str_pct_Q_nested.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(1,26))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,26))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(1,26))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,3) = "%Q["
+ ├── parts: (length: 3)
+ │ ├── @ StringNode (location: (1,3)-(1,11))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (1,3)-(1,11) = "before ["
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "before ["
+ │ ├── @ EmbeddedStatementsNode (location: (1,11)-(1,18))
+ │ │ ├── opening_loc: (1,11)-(1,13) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,13)-(1,17))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (1,13)-(1,17))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :nest
+ │ │ │ ├── message_loc: (1,13)-(1,17) = "nest"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── closing_loc: (1,17)-(1,18) = "}"
+ │ └── @ StringNode (location: (1,18)-(1,25))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (1,18)-(1,25) = "] after"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "] after"
+ └── closing_loc: (1,25)-(1,26) = "]"
diff --git a/test/prism/snapshots/seattlerb/str_pct_nested_nested.txt b/test/prism/snapshots/seattlerb/str_pct_nested_nested.txt
new file mode 100644
index 0000000000..22c3031832
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/str_pct_nested_nested.txt
@@ -0,0 +1,42 @@
+@ ProgramNode (location: (1,0)-(1,20))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,20))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(1,20))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,2) = "%{"
+ ├── parts: (length: 3)
+ │ ├── @ StringNode (location: (1,2)-(1,5))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (1,2)-(1,5) = " { "
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: " { "
+ │ ├── @ EmbeddedStatementsNode (location: (1,5)-(1,16))
+ │ │ ├── opening_loc: (1,5)-(1,7) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,8)-(1,14))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ InterpolatedStringNode (location: (1,8)-(1,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (1,8)-(1,9) = "\""
+ │ │ │ ├── parts: (length: 1)
+ │ │ │ │ └── @ EmbeddedStatementsNode (location: (1,9)-(1,13))
+ │ │ │ │ ├── opening_loc: (1,9)-(1,11) = "\#{"
+ │ │ │ │ ├── statements:
+ │ │ │ │ │ @ StatementsNode (location: (1,11)-(1,12))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ IntegerNode (location: (1,11)-(1,12))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ └── closing_loc: (1,12)-(1,13) = "}"
+ │ │ │ └── closing_loc: (1,13)-(1,14) = "\""
+ │ │ └── closing_loc: (1,15)-(1,16) = "}"
+ │ └── @ StringNode (location: (1,16)-(1,19))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (1,16)-(1,19) = " } "
+ │ ├── closing_loc: ∅
+ │ └── unescaped: " } "
+ └── closing_loc: (1,19)-(1,20) = "}"
diff --git a/test/prism/snapshots/seattlerb/str_pct_q.txt b/test/prism/snapshots/seattlerb/str_pct_q.txt
new file mode 100644
index 0000000000..c4dd5bacae
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/str_pct_q.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,0)-(1,9))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,3) = "%q{"
+ ├── content_loc: (1,3)-(1,8) = "a b c"
+ ├── closing_loc: (1,8)-(1,9) = "}"
+ └── unescaped: "a b c"
diff --git a/test/prism/snapshots/seattlerb/str_single_double_escaped_newline.txt b/test/prism/snapshots/seattlerb/str_single_double_escaped_newline.txt
new file mode 100644
index 0000000000..8fa8886029
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/str_single_double_escaped_newline.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,7))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,2)-(1,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (1,2)-(1,7))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,2)-(1,3) = "'"
+ │ │ ├── content_loc: (1,3)-(1,6) = "\\\\n"
+ │ │ ├── closing_loc: (1,6)-(1,7) = "'"
+ │ │ └── unescaped: "\\n"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (1,8)-(1,9))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :b
+ ├── message_loc: (1,8)-(1,9) = "b"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/str_single_escaped_newline.txt b/test/prism/snapshots/seattlerb/str_single_escaped_newline.txt
new file mode 100644
index 0000000000..c840c7688b
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/str_single_escaped_newline.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,6))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,2)-(1,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (1,2)-(1,6))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,2)-(1,3) = "'"
+ │ │ ├── content_loc: (1,3)-(1,5) = "\\n"
+ │ │ ├── closing_loc: (1,5)-(1,6) = "'"
+ │ │ └── unescaped: "\\n"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (1,7)-(1,8))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :b
+ ├── message_loc: (1,7)-(1,8) = "b"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/str_single_newline.txt b/test/prism/snapshots/seattlerb/str_single_newline.txt
new file mode 100644
index 0000000000..15b0f2ff72
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/str_single_newline.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(2,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,3))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(2,1))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,2)-(2,1))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (1,2)-(2,1))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,2)-(1,3) = "'"
+ │ │ ├── content_loc: (1,3)-(2,0) = "\n"
+ │ │ ├── closing_loc: (2,0)-(2,1) = "'"
+ │ │ └── unescaped: "\n"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (2,2)-(2,3))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :b
+ ├── message_loc: (2,2)-(2,3) = "b"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/str_str.txt b/test/prism/snapshots/seattlerb/str_str.txt
new file mode 100644
index 0000000000..f3f1213a0c
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/str_str.txt
@@ -0,0 +1,28 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(1,10))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,1) = "\""
+ ├── parts: (length: 2)
+ │ ├── @ StringNode (location: (1,1)-(1,3))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (1,1)-(1,3) = "a "
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a "
+ │ └── @ EmbeddedStatementsNode (location: (1,3)-(1,9))
+ │ ├── opening_loc: (1,3)-(1,5) = "\#{"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,5)-(1,8))
+ │ │ └── body: (length: 1)
+ │ │ └── @ StringNode (location: (1,5)-(1,8))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,5)-(1,6) = "'"
+ │ │ ├── content_loc: (1,6)-(1,7) = "b"
+ │ │ ├── closing_loc: (1,7)-(1,8) = "'"
+ │ │ └── unescaped: "b"
+ │ └── closing_loc: (1,8)-(1,9) = "}"
+ └── closing_loc: (1,9)-(1,10) = "\""
diff --git a/test/prism/snapshots/seattlerb/str_str_str.txt b/test/prism/snapshots/seattlerb/str_str_str.txt
new file mode 100644
index 0000000000..b01f2b5794
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/str_str_str.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(1,12))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,1) = "\""
+ ├── parts: (length: 3)
+ │ ├── @ StringNode (location: (1,1)-(1,3))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (1,1)-(1,3) = "a "
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a "
+ │ ├── @ EmbeddedStatementsNode (location: (1,3)-(1,9))
+ │ │ ├── opening_loc: (1,3)-(1,5) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,5)-(1,8))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ StringNode (location: (1,5)-(1,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (1,5)-(1,6) = "'"
+ │ │ │ ├── content_loc: (1,6)-(1,7) = "b"
+ │ │ │ ├── closing_loc: (1,7)-(1,8) = "'"
+ │ │ │ └── unescaped: "b"
+ │ │ └── closing_loc: (1,8)-(1,9) = "}"
+ │ └── @ StringNode (location: (1,9)-(1,11))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (1,9)-(1,11) = " c"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: " c"
+ └── closing_loc: (1,11)-(1,12) = "\""
diff --git a/test/prism/snapshots/seattlerb/super_arg.txt b/test/prism/snapshots/seattlerb/super_arg.txt
new file mode 100644
index 0000000000..61b5f0b631
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/super_arg.txt
@@ -0,0 +1,17 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ SuperNode (location: (1,0)-(1,8))
+ ├── keyword_loc: (1,0)-(1,5) = "super"
+ ├── lparen_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,6)-(1,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,6)-(1,8))
+ │ ├── flags: decimal
+ │ └── value: 42
+ ├── rparen_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/symbol_empty.txt b/test/prism/snapshots/seattlerb/symbol_empty.txt
new file mode 100644
index 0000000000..e95543e925
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/symbol_empty.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,3))
+ └── body: (length: 1)
+ └── @ SymbolNode (location: (1,0)-(1,3))
+ ├── flags: forced_us_ascii_encoding
+ ├── opening_loc: (1,0)-(1,2) = ":'"
+ ├── value_loc: (1,2)-(1,2) = ""
+ ├── closing_loc: (1,2)-(1,3) = "'"
+ └── unescaped: ""
diff --git a/test/prism/snapshots/seattlerb/symbol_list.txt b/test/prism/snapshots/seattlerb/symbol_list.txt
new file mode 100644
index 0000000000..6750160d50
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/symbol_list.txt
@@ -0,0 +1,50 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ ArrayNode (location: (1,0)-(1,13))
+ ├── flags: ∅
+ ├── elements: (length: 2)
+ │ ├── @ InterpolatedSymbolNode (location: (1,3)-(1,7))
+ │ │ ├── opening_loc: ∅
+ │ │ ├── parts: (length: 1)
+ │ │ │ └── @ EmbeddedStatementsNode (location: (1,3)-(1,7))
+ │ │ │ ├── opening_loc: (1,3)-(1,5) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (1,5)-(1,6))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (1,5)-(1,6))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── message_loc: (1,5)-(1,6) = "a"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── closing_loc: (1,6)-(1,7) = "}"
+ │ │ └── closing_loc: ∅
+ │ └── @ InterpolatedSymbolNode (location: (1,8)-(1,12))
+ │ ├── opening_loc: ∅
+ │ ├── parts: (length: 1)
+ │ │ └── @ EmbeddedStatementsNode (location: (1,8)-(1,12))
+ │ │ ├── opening_loc: (1,8)-(1,10) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,10)-(1,11))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (1,10)-(1,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (1,10)-(1,11) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── closing_loc: (1,11)-(1,12) = "}"
+ │ └── closing_loc: ∅
+ ├── opening_loc: (1,0)-(1,3) = "%I["
+ └── closing_loc: (1,12)-(1,13) = "]"
diff --git a/test/prism/snapshots/seattlerb/symbols.txt b/test/prism/snapshots/seattlerb/symbols.txt
new file mode 100644
index 0000000000..30cf57c528
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/symbols.txt
@@ -0,0 +1,28 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ ArrayNode (location: (1,0)-(1,9))
+ ├── flags: ∅
+ ├── elements: (length: 3)
+ │ ├── @ SymbolNode (location: (1,3)-(1,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (1,3)-(1,4) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── @ SymbolNode (location: (1,5)-(1,6))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (1,5)-(1,6) = "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b"
+ │ └── @ SymbolNode (location: (1,7)-(1,8))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: ∅
+ │ ├── value_loc: (1,7)-(1,8) = "c"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "c"
+ ├── opening_loc: (1,0)-(1,3) = "%i("
+ └── closing_loc: (1,8)-(1,9) = ")"
diff --git a/test/prism/snapshots/seattlerb/symbols_empty.txt b/test/prism/snapshots/seattlerb/symbols_empty.txt
new file mode 100644
index 0000000000..dc743e2be5
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/symbols_empty.txt
@@ -0,0 +1,10 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ ArrayNode (location: (1,0)-(1,4))
+ ├── flags: ∅
+ ├── elements: (length: 0)
+ ├── opening_loc: (1,0)-(1,3) = "%i("
+ └── closing_loc: (1,3)-(1,4) = ")"
diff --git a/test/prism/snapshots/seattlerb/symbols_empty_space.txt b/test/prism/snapshots/seattlerb/symbols_empty_space.txt
new file mode 100644
index 0000000000..ea7ada9446
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/symbols_empty_space.txt
@@ -0,0 +1,10 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ ArrayNode (location: (1,0)-(1,5))
+ ├── flags: ∅
+ ├── elements: (length: 0)
+ ├── opening_loc: (1,0)-(1,3) = "%i("
+ └── closing_loc: (1,4)-(1,5) = ")"
diff --git a/test/prism/snapshots/seattlerb/symbols_interp.txt b/test/prism/snapshots/seattlerb/symbols_interp.txt
new file mode 100644
index 0000000000..2ad3cc502d
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/symbols_interp.txt
@@ -0,0 +1,28 @@
+@ ProgramNode (location: (1,0)-(1,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,15))
+ └── body: (length: 1)
+ └── @ ArrayNode (location: (1,0)-(1,15))
+ ├── flags: ∅
+ ├── elements: (length: 3)
+ │ ├── @ SymbolNode (location: (1,3)-(1,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (1,3)-(1,4) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── @ SymbolNode (location: (1,5)-(1,12))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (1,5)-(1,12) = "b\#{1+1}"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b\#{1+1}"
+ │ └── @ SymbolNode (location: (1,13)-(1,14))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: ∅
+ │ ├── value_loc: (1,13)-(1,14) = "c"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "c"
+ ├── opening_loc: (1,0)-(1,3) = "%i("
+ └── closing_loc: (1,14)-(1,15) = ")"
diff --git a/test/prism/snapshots/seattlerb/thingy.txt b/test/prism/snapshots/seattlerb/thingy.txt
new file mode 100644
index 0000000000..4dd2ac44a6
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/thingy.txt
@@ -0,0 +1,57 @@
+@ ProgramNode (location: (1,0)-(3,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,7))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,6))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,0)-(1,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :f
+ │ │ ├── message_loc: (1,0)-(1,1) = "f"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (1,1)-(1,2) = "."
+ │ ├── name: :call
+ │ ├── message_loc: ∅
+ │ ├── opening_loc: (1,2)-(1,3) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,3)-(1,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,3)-(1,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 42
+ │ ├── closing_loc: (1,5)-(1,6) = ")"
+ │ └── block: ∅
+ └── @ CallNode (location: (3,0)-(3,7))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (3,0)-(3,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (3,0)-(3,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (3,1)-(3,3) = "::"
+ ├── name: :call
+ ├── message_loc: ∅
+ ├── opening_loc: (3,3)-(3,4) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (3,4)-(3,6))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (3,4)-(3,6))
+ │ ├── flags: decimal
+ │ └── value: 42
+ ├── closing_loc: (3,6)-(3,7) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/uminus_float.txt b/test/prism/snapshots/seattlerb/uminus_float.txt
new file mode 100644
index 0000000000..0578dbbd68
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/uminus_float.txt
@@ -0,0 +1,7 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ FloatNode (location: (1,0)-(1,4))
+ └── value: -0.0
diff --git a/test/prism/snapshots/seattlerb/unary_minus.txt b/test/prism/snapshots/seattlerb/unary_minus.txt
new file mode 100644
index 0000000000..79889bffb0
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/unary_minus.txt
@@ -0,0 +1,25 @@
+@ ProgramNode (location: (1,0)-(1,2))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,2))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,2))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,1)-(1,2))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,1)-(1,2) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :-@
+ ├── message_loc: (1,0)-(1,1) = "-"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/unary_plus.txt b/test/prism/snapshots/seattlerb/unary_plus.txt
new file mode 100644
index 0000000000..b570cbf73b
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/unary_plus.txt
@@ -0,0 +1,25 @@
+@ ProgramNode (location: (1,0)-(1,2))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,2))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,2))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,1)-(1,2))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,1)-(1,2) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :+@
+ ├── message_loc: (1,0)-(1,1) = "+"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/unary_plus_on_literal.txt b/test/prism/snapshots/seattlerb/unary_plus_on_literal.txt
new file mode 100644
index 0000000000..4deb857536
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/unary_plus_on_literal.txt
@@ -0,0 +1,21 @@
+@ ProgramNode (location: (1,0)-(1,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,3))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,3))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ SymbolNode (location: (1,1)-(1,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,1)-(1,2) = ":"
+ │ ├── value_loc: (1,2)-(1,3) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── call_operator_loc: ∅
+ ├── name: :+@
+ ├── message_loc: (1,0)-(1,1) = "+"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/unary_tilde.txt b/test/prism/snapshots/seattlerb/unary_tilde.txt
new file mode 100644
index 0000000000..5fd1a5d00e
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/unary_tilde.txt
@@ -0,0 +1,25 @@
+@ ProgramNode (location: (1,0)-(1,2))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,2))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,2))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,1)-(1,2))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,1)-(1,2) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :~
+ ├── message_loc: (1,0)-(1,1) = "~"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/utf8_bom.txt b/test/prism/snapshots/seattlerb/utf8_bom.txt
new file mode 100644
index 0000000000..9f0eb83b05
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/utf8_bom.txt
@@ -0,0 +1,21 @@
+@ ProgramNode (location: (2,0)-(2,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (2,0)-(2,3))
+ └── body: (length: 1)
+ └── @ CallNode (location: (2,0)-(2,3))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :p
+ ├── message_loc: (2,0)-(2,1) = "p"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (2,2)-(2,3))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (2,2)-(2,3))
+ │ ├── flags: decimal
+ │ └── value: 0
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/seattlerb/when_splat.txt b/test/prism/snapshots/seattlerb/when_splat.txt
new file mode 100644
index 0000000000..19e70019c0
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/when_splat.txt
@@ -0,0 +1,39 @@
+@ ProgramNode (location: (1,0)-(1,25))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,25))
+ └── body: (length: 1)
+ └── @ CaseNode (location: (1,0)-(1,25))
+ ├── predicate:
+ │ @ CallNode (location: (1,5)-(1,6))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,5)-(1,6) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── conditions: (length: 1)
+ │ └── @ WhenNode (location: (1,8)-(1,20))
+ │ ├── keyword_loc: (1,8)-(1,12) = "when"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ SplatNode (location: (1,13)-(1,15))
+ │ │ ├── operator_loc: (1,13)-(1,14) = "*"
+ │ │ └── expression:
+ │ │ @ CallNode (location: (1,14)-(1,15))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (1,14)-(1,15) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: (1,16)-(1,20) = "then"
+ │ └── statements: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (1,22)-(1,25) = "end"
diff --git a/test/prism/snapshots/seattlerb/words_interp.txt b/test/prism/snapshots/seattlerb/words_interp.txt
new file mode 100644
index 0000000000..1175a6f476
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/words_interp.txt
@@ -0,0 +1,30 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ ArrayNode (location: (1,0)-(1,9))
+ ├── flags: ∅
+ ├── elements: (length: 1)
+ │ └── @ InterpolatedStringNode (location: (1,3)-(1,8))
+ │ ├── flags: ∅
+ │ ├── opening_loc: ∅
+ │ ├── parts: (length: 2)
+ │ │ ├── @ EmbeddedStatementsNode (location: (1,3)-(1,7))
+ │ │ │ ├── opening_loc: (1,3)-(1,5) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (1,5)-(1,6))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (1,5)-(1,6))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── closing_loc: (1,6)-(1,7) = "}"
+ │ │ └── @ StringNode (location: (1,7)-(1,8))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (1,7)-(1,8) = "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b"
+ │ └── closing_loc: ∅
+ ├── opening_loc: (1,0)-(1,3) = "%W("
+ └── closing_loc: (1,8)-(1,9) = ")"
diff --git a/test/prism/snapshots/seattlerb/yield_arg.txt b/test/prism/snapshots/seattlerb/yield_arg.txt
new file mode 100644
index 0000000000..22e0c14f83
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/yield_arg.txt
@@ -0,0 +1,16 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ YieldNode (location: (1,0)-(1,8))
+ ├── keyword_loc: (1,0)-(1,5) = "yield"
+ ├── lparen_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,6)-(1,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,6)-(1,8))
+ │ ├── flags: decimal
+ │ └── value: 42
+ └── rparen_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/yield_call_assocs.txt b/test/prism/snapshots/seattlerb/yield_call_assocs.txt
new file mode 100644
index 0000000000..c04273f5aa
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/yield_call_assocs.txt
@@ -0,0 +1,224 @@
+@ ProgramNode (location: (1,0)-(11,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(11,13))
+ └── body: (length: 6)
+ ├── @ YieldNode (location: (1,0)-(1,16))
+ │ ├── keyword_loc: (1,0)-(1,5) = "yield"
+ │ ├── lparen_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,6)-(1,16))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ IntegerNode (location: (1,6)-(1,7))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ KeywordHashNode (location: (1,9)-(1,16))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (1,9)-(1,16))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (1,9)-(1,11))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (1,9)-(1,10) = ":"
+ │ │ │ ├── value_loc: (1,10)-(1,11) = "z"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "z"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (1,15)-(1,16))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (1,12)-(1,14) = "=>"
+ │ └── rparen_loc: ∅
+ ├── @ YieldNode (location: (3,0)-(3,25))
+ │ ├── keyword_loc: (3,0)-(3,5) = "yield"
+ │ ├── lparen_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,6)-(3,25))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ IntegerNode (location: (3,6)-(3,7))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ KeywordHashNode (location: (3,9)-(3,25))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 2)
+ │ │ ├── @ AssocNode (location: (3,9)-(3,16))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (3,9)-(3,11))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (3,9)-(3,10) = ":"
+ │ │ │ │ ├── value_loc: (3,10)-(3,11) = "z"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "z"
+ │ │ │ ├── value:
+ │ │ │ │ @ IntegerNode (location: (3,15)-(3,16))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── operator_loc: (3,12)-(3,14) = "=>"
+ │ │ └── @ AssocNode (location: (3,18)-(3,25))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (3,18)-(3,20))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (3,18)-(3,19) = ":"
+ │ │ │ ├── value_loc: (3,19)-(3,20) = "w"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "w"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (3,24)-(3,25))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── operator_loc: (3,21)-(3,23) = "=>"
+ │ └── rparen_loc: ∅
+ ├── @ YieldNode (location: (5,0)-(5,13))
+ │ ├── keyword_loc: (5,0)-(5,5) = "yield"
+ │ ├── lparen_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (5,6)-(5,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (5,6)-(5,13))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :y
+ │ │ ├── message_loc: (5,6)-(5,7) = "y"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (5,8)-(5,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ KeywordHashNode (location: (5,8)-(5,13))
+ │ │ │ ├── flags: symbol_keys
+ │ │ │ └── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (5,8)-(5,13))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (5,8)-(5,10))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (5,8)-(5,9) = ":"
+ │ │ │ │ ├── value_loc: (5,9)-(5,10) = "z"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "z"
+ │ │ │ ├── value:
+ │ │ │ │ @ IntegerNode (location: (5,12)-(5,13))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── operator_loc: (5,10)-(5,12) = "=>"
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── rparen_loc: ∅
+ ├── @ YieldNode (location: (7,0)-(7,11))
+ │ ├── keyword_loc: (7,0)-(7,5) = "yield"
+ │ ├── lparen_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (7,6)-(7,11))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (7,6)-(7,11))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :y
+ │ │ ├── message_loc: (7,6)-(7,7) = "y"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (7,8)-(7,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ KeywordHashNode (location: (7,8)-(7,11))
+ │ │ │ ├── flags: symbol_keys
+ │ │ │ └── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (7,8)-(7,11))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (7,8)-(7,10))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (7,8)-(7,9) = "z"
+ │ │ │ │ ├── closing_loc: (7,9)-(7,10) = ":"
+ │ │ │ │ └── unescaped: "z"
+ │ │ │ ├── value:
+ │ │ │ │ @ IntegerNode (location: (7,10)-(7,11))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── operator_loc: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── rparen_loc: ∅
+ ├── @ YieldNode (location: (9,0)-(9,12))
+ │ ├── keyword_loc: (9,0)-(9,5) = "yield"
+ │ ├── lparen_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (9,6)-(9,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (9,6)-(9,12))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :y
+ │ │ ├── message_loc: (9,6)-(9,7) = "y"
+ │ │ ├── opening_loc: (9,7)-(9,8) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (9,8)-(9,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ KeywordHashNode (location: (9,8)-(9,11))
+ │ │ │ ├── flags: symbol_keys
+ │ │ │ └── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (9,8)-(9,11))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (9,8)-(9,10))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (9,8)-(9,9) = "z"
+ │ │ │ │ ├── closing_loc: (9,9)-(9,10) = ":"
+ │ │ │ │ └── unescaped: "z"
+ │ │ │ ├── value:
+ │ │ │ │ @ IntegerNode (location: (9,10)-(9,11))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── operator_loc: ∅
+ │ │ ├── closing_loc: (9,11)-(9,12) = ")"
+ │ │ └── block: ∅
+ │ └── rparen_loc: ∅
+ └── @ YieldNode (location: (11,0)-(11,13))
+ ├── keyword_loc: (11,0)-(11,5) = "yield"
+ ├── lparen_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (11,6)-(11,13))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (11,6)-(11,13))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :y
+ │ ├── message_loc: (11,6)-(11,7) = "y"
+ │ ├── opening_loc: (11,7)-(11,8) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (11,8)-(11,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (11,8)-(11,12))
+ │ │ ├── flags: ∅
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (11,8)-(11,12))
+ │ │ ├── key:
+ │ │ │ @ CallNode (location: (11,8)-(11,9))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :z
+ │ │ │ ├── message_loc: (11,8)-(11,9) = "z"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (11,11)-(11,12))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (11,9)-(11,11) = "=>"
+ │ ├── closing_loc: (11,12)-(11,13) = ")"
+ │ └── block: ∅
+ └── rparen_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/yield_empty_parens.txt b/test/prism/snapshots/seattlerb/yield_empty_parens.txt
new file mode 100644
index 0000000000..5ecd89823f
--- /dev/null
+++ b/test/prism/snapshots/seattlerb/yield_empty_parens.txt
@@ -0,0 +1,10 @@
+@ ProgramNode (location: (1,0)-(1,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,7))
+ └── body: (length: 1)
+ └── @ YieldNode (location: (1,0)-(1,7))
+ ├── keyword_loc: (1,0)-(1,5) = "yield"
+ ├── lparen_loc: (1,5)-(1,6) = "("
+ ├── arguments: ∅
+ └── rparen_loc: (1,6)-(1,7) = ")"
diff --git a/test/prism/snapshots/single_method_call_with_bang.txt b/test/prism/snapshots/single_method_call_with_bang.txt
new file mode 100644
index 0000000000..4c68e0adac
--- /dev/null
+++ b/test/prism/snapshots/single_method_call_with_bang.txt
@@ -0,0 +1,15 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,4))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :foo!
+ ├── message_loc: (1,0)-(1,4) = "foo!"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/single_quote_heredocs.txt b/test/prism/snapshots/single_quote_heredocs.txt
new file mode 100644
index 0000000000..429c9daf11
--- /dev/null
+++ b/test/prism/snapshots/single_quote_heredocs.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,0)-(1,8))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,8) = "<<-'EOS'"
+ ├── content_loc: (2,0)-(3,0) = " cd L:\\Work\\MG3710IQPro\\Develop\n"
+ ├── closing_loc: (3,0)-(4,0) = "EOS\n"
+ └── unescaped: " cd L:\\Work\\MG3710IQPro\\Develop\n"
diff --git a/test/prism/snapshots/spanning_heredoc.txt b/test/prism/snapshots/spanning_heredoc.txt
new file mode 100644
index 0000000000..c89daaed09
--- /dev/null
+++ b/test/prism/snapshots/spanning_heredoc.txt
@@ -0,0 +1,413 @@
+@ ProgramNode (location: (4,0)-(63,2))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (4,0)-(63,2))
+ └── body: (length: 14)
+ ├── @ CallNode (location: (4,0)-(7,7))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :pp
+ │ ├── message_loc: (4,0)-(4,2) = "pp"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (4,3)-(7,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (4,3)-(7,7))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ StringNode (location: (4,3)-(4,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (4,3)-(4,7) = "<<-A"
+ │ │ │ ├── content_loc: (5,0)-(6,0) = "a\n"
+ │ │ │ ├── closing_loc: (6,0)-(7,0) = "A\n"
+ │ │ │ └── unescaped: "a\n"
+ │ │ ├── call_operator_loc: (4,7)-(4,8) = "."
+ │ │ ├── name: :gsub
+ │ │ ├── message_loc: (4,8)-(4,12) = "gsub"
+ │ │ ├── opening_loc: (4,12)-(4,13) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (4,13)-(7,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 2)
+ │ │ │ ├── @ InterpolatedRegularExpressionNode (location: (4,13)-(7,2))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: (4,13)-(4,14) = "/"
+ │ │ │ │ ├── parts: (length: 2)
+ │ │ │ │ │ ├── @ StringNode (location: (4,14)-(4,16))
+ │ │ │ │ │ │ ├── flags: frozen
+ │ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ │ ├── content_loc: (4,14)-(4,16) = "b\\"
+ │ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ │ └── unescaped: "b"
+ │ │ │ │ │ └── @ StringNode (location: (7,0)-(7,1))
+ │ │ │ │ │ ├── flags: frozen
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── content_loc: (7,0)-(7,1) = "b"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "b"
+ │ │ │ │ └── closing_loc: (7,1)-(7,2) = "/"
+ │ │ │ └── @ StringNode (location: (7,4)-(7,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (7,4)-(7,5) = "\""
+ │ │ │ ├── content_loc: (7,5)-(7,5) = ""
+ │ │ │ ├── closing_loc: (7,5)-(7,6) = "\""
+ │ │ │ └── unescaped: ""
+ │ │ ├── closing_loc: (7,6)-(7,7) = ")"
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (10,0)-(13,2))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :pp
+ │ ├── message_loc: (10,0)-(10,2) = "pp"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (10,3)-(13,2))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ StringNode (location: (10,3)-(10,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (10,3)-(10,7) = "<<-A"
+ │ │ │ ├── content_loc: (11,0)-(12,0) = "c\n"
+ │ │ │ ├── closing_loc: (12,0)-(13,0) = "A\n"
+ │ │ │ └── unescaped: "c\n"
+ │ │ └── @ InterpolatedStringNode (location: (10,9)-(13,2))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (10,9)-(10,10) = "\""
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (10,10)-(10,12))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (10,10)-(10,12) = "d\\"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "d"
+ │ │ │ └── @ StringNode (location: (13,0)-(13,1))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (13,0)-(13,1) = "d"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "d"
+ │ │ └── closing_loc: (13,1)-(13,2) = "\""
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (16,0)-(19,2))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :pp
+ │ ├── message_loc: (16,0)-(16,2) = "pp"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (16,3)-(19,2))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ StringNode (location: (16,3)-(16,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (16,3)-(16,7) = "<<-A"
+ │ │ │ ├── content_loc: (17,0)-(18,0) = "e\n"
+ │ │ │ ├── closing_loc: (18,0)-(19,0) = "A\n"
+ │ │ │ └── unescaped: "e\n"
+ │ │ └── @ InterpolatedStringNode (location: (16,9)-(19,2))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (16,9)-(16,12) = "%q["
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (16,12)-(16,14))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (16,12)-(16,14) = "f\\"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "f\\\n"
+ │ │ │ └── @ StringNode (location: (19,0)-(19,1))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (19,0)-(19,1) = "f"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "f"
+ │ │ └── closing_loc: (19,1)-(19,2) = "]"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (22,0)-(25,2))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :pp
+ │ ├── message_loc: (22,0)-(22,2) = "pp"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (22,3)-(25,2))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ StringNode (location: (22,3)-(22,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (22,3)-(22,7) = "<<-A"
+ │ │ │ ├── content_loc: (23,0)-(24,0) = "g\n"
+ │ │ │ ├── closing_loc: (24,0)-(25,0) = "A\n"
+ │ │ │ └── unescaped: "g\n"
+ │ │ └── @ InterpolatedStringNode (location: (22,9)-(25,2))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (22,9)-(22,12) = "%Q["
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (22,12)-(22,14))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (22,12)-(22,14) = "h\\"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "h"
+ │ │ │ └── @ StringNode (location: (25,0)-(25,1))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (25,0)-(25,1) = "h"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "h"
+ │ │ └── closing_loc: (25,1)-(25,2) = "]"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (28,0)-(31,2))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :pp
+ │ ├── message_loc: (28,0)-(28,2) = "pp"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (28,3)-(31,2))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ StringNode (location: (28,3)-(28,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (28,3)-(28,7) = "<<-A"
+ │ │ │ ├── content_loc: (29,0)-(30,0) = "i\n"
+ │ │ │ ├── closing_loc: (30,0)-(31,0) = "A\n"
+ │ │ │ └── unescaped: "i\n"
+ │ │ └── @ ArrayNode (location: (28,9)-(31,2))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 2)
+ │ │ │ ├── @ StringNode (location: (28,12)-(28,14))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (28,12)-(28,14) = "j\\"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "j\n"
+ │ │ │ └── @ StringNode (location: (31,0)-(31,1))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (31,0)-(31,1) = "j"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "j"
+ │ │ ├── opening_loc: (28,9)-(28,12) = "%w["
+ │ │ └── closing_loc: (31,1)-(31,2) = "]"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (35,0)-(38,2))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :pp
+ │ ├── message_loc: (35,0)-(35,2) = "pp"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (35,3)-(38,2))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ StringNode (location: (35,3)-(35,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (35,3)-(35,7) = "<<-A"
+ │ │ │ ├── content_loc: (36,0)-(37,0) = "k\n"
+ │ │ │ ├── closing_loc: (37,0)-(38,0) = "A\n"
+ │ │ │ └── unescaped: "k\n"
+ │ │ └── @ ArrayNode (location: (35,9)-(38,2))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ InterpolatedStringNode (location: (35,12)-(38,1))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── parts: (length: 2)
+ │ │ │ │ ├── @ StringNode (location: (35,12)-(35,14))
+ │ │ │ │ │ ├── flags: frozen
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── content_loc: (35,12)-(35,14) = "l\\"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "l\n"
+ │ │ │ │ └── @ StringNode (location: (38,0)-(38,1))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (38,0)-(38,1) = "l"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "l"
+ │ │ │ └── closing_loc: ∅
+ │ │ ├── opening_loc: (35,9)-(35,12) = "%W["
+ │ │ └── closing_loc: (38,1)-(38,2) = "]"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (41,0)-(44,2))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :pp
+ │ ├── message_loc: (41,0)-(41,2) = "pp"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (41,3)-(44,2))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ StringNode (location: (41,3)-(41,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (41,3)-(41,7) = "<<-A"
+ │ │ │ ├── content_loc: (42,0)-(43,0) = "m\n"
+ │ │ │ ├── closing_loc: (43,0)-(44,0) = "A\n"
+ │ │ │ └── unescaped: "m\n"
+ │ │ └── @ ArrayNode (location: (41,9)-(44,2))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 2)
+ │ │ │ ├── @ SymbolNode (location: (41,12)-(41,14))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (41,12)-(41,14) = "n\\"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "n\n"
+ │ │ │ └── @ SymbolNode (location: (44,0)-(44,1))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (44,0)-(44,1) = "n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "n"
+ │ │ ├── opening_loc: (41,9)-(41,12) = "%i["
+ │ │ └── closing_loc: (44,1)-(44,2) = "]"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (48,0)-(51,2))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :pp
+ │ ├── message_loc: (48,0)-(48,2) = "pp"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (48,3)-(51,2))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ StringNode (location: (48,3)-(48,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (48,3)-(48,7) = "<<-A"
+ │ │ │ ├── content_loc: (49,0)-(50,0) = "o\n"
+ │ │ │ ├── closing_loc: (50,0)-(51,0) = "A\n"
+ │ │ │ └── unescaped: "o\n"
+ │ │ └── @ ArrayNode (location: (48,9)-(51,2))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ InterpolatedSymbolNode (location: (48,12)-(48,14))
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── parts: (length: 2)
+ │ │ │ │ ├── @ StringNode (location: (48,12)-(48,14))
+ │ │ │ │ │ ├── flags: frozen
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── content_loc: (48,12)-(48,14) = "p\\"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "p\n"
+ │ │ │ │ └── @ StringNode (location: (48,12)-(48,14))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (48,12)-(48,14) = "p\\"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "p"
+ │ │ │ └── closing_loc: ∅
+ │ │ ├── opening_loc: (48,9)-(48,12) = "%I["
+ │ │ └── closing_loc: (51,1)-(51,2) = "]"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ StringNode (location: (53,0)-(53,3))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (53,0)-(53,3) = "<<A"
+ │ ├── content_loc: (54,0)-(54,0) = ""
+ │ ├── closing_loc: (54,0)-(55,0) = "A\n"
+ │ └── unescaped: ""
+ ├── @ MatchWriteNode (location: (53,5)-(55,13))
+ │ ├── call:
+ │ │ @ CallNode (location: (53,5)-(55,13))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ InterpolatedRegularExpressionNode (location: (53,5)-(55,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (53,5)-(53,6) = "/"
+ │ │ │ ├── parts: (length: 2)
+ │ │ │ │ ├── @ StringNode (location: (53,6)-(53,7))
+ │ │ │ │ │ ├── flags: frozen
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── content_loc: (53,6)-(53,7) = "\\"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: ""
+ │ │ │ │ └── @ StringNode (location: (55,0)-(55,6))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (55,0)-(55,6) = "(?<a>)"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "(?<a>)"
+ │ │ │ └── closing_loc: (55,6)-(55,7) = "/"
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :=~
+ │ │ ├── message_loc: (55,8)-(55,10) = "=~"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (55,11)-(55,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ StringNode (location: (55,11)-(55,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (55,11)-(55,12) = "'"
+ │ │ │ ├── content_loc: (55,12)-(55,12) = ""
+ │ │ │ ├── closing_loc: (55,12)-(55,13) = "'"
+ │ │ │ └── unescaped: ""
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── targets: (length: 1)
+ │ └── @ LocalVariableTargetNode (location: (53,5)-(55,7))
+ │ ├── name: :a
+ │ └── depth: 0
+ ├── @ StringNode (location: (57,0)-(57,3))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (57,0)-(57,3) = "<<A"
+ │ ├── content_loc: (58,0)-(58,0) = ""
+ │ ├── closing_loc: (58,0)-(59,0) = "A\n"
+ │ └── unescaped: ""
+ ├── @ InterpolatedSymbolNode (location: (57,5)-(59,2))
+ │ ├── opening_loc: (57,5)-(57,7) = ":'"
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (57,7)-(58,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (57,7)-(58,0) = "a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ └── @ StringNode (location: (59,0)-(59,1))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (59,0)-(59,1) = "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b"
+ │ └── closing_loc: (59,1)-(59,2) = "'"
+ ├── @ StringNode (location: (61,0)-(61,3))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (61,0)-(61,3) = "<<A"
+ │ ├── content_loc: (62,0)-(62,0) = ""
+ │ ├── closing_loc: (62,0)-(63,0) = "A\n"
+ │ └── unescaped: ""
+ └── @ InterpolatedSymbolNode (location: (61,5)-(63,2))
+ ├── opening_loc: (61,5)-(61,7) = ":\""
+ ├── parts: (length: 2)
+ │ ├── @ StringNode (location: (61,7)-(62,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (61,7)-(62,0) = "a\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a\n"
+ │ └── @ StringNode (location: (63,0)-(63,1))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (63,0)-(63,1) = "b"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "b"
+ └── closing_loc: (63,1)-(63,2) = "\""
diff --git a/test/prism/snapshots/spanning_heredoc_newlines.txt b/test/prism/snapshots/spanning_heredoc_newlines.txt
new file mode 100644
index 0000000000..e3609ddbba
--- /dev/null
+++ b/test/prism/snapshots/spanning_heredoc_newlines.txt
@@ -0,0 +1,155 @@
+@ ProgramNode (location: (1,0)-(24,0))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(24,0))
+ └── body: (length: 6)
+ ├── @ CallNode (location: (1,0)-(4,0))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ StringNode (location: (1,0)-(1,3))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,0)-(1,3) = "<<A"
+ │ │ ├── content_loc: (2,0)-(2,0) = ""
+ │ │ ├── closing_loc: (2,0)-(3,0) = "A\n"
+ │ │ └── unescaped: ""
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (1,3)-(1,4) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,4)-(4,0))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (1,4)-(4,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,4)-(2,0) = "%\n"
+ │ │ ├── content_loc: (3,0)-(3,0) = ""
+ │ │ ├── closing_loc: (3,0)-(4,0) = "\n"
+ │ │ └── unescaped: ""
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (5,0)-(8,0))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ StringNode (location: (5,0)-(5,3))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (5,0)-(5,3) = "<<A"
+ │ │ ├── content_loc: (6,0)-(6,0) = ""
+ │ │ ├── closing_loc: (6,0)-(7,0) = "A\n"
+ │ │ └── unescaped: ""
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (5,3)-(5,4) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (5,4)-(8,0))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ RegularExpressionNode (location: (5,4)-(8,0))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (5,4)-(6,0) = "%r\n"
+ │ │ ├── content_loc: (6,0)-(6,0) = ""
+ │ │ ├── closing_loc: (7,0)-(8,0) = "\n"
+ │ │ └── unescaped: ""
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (9,0)-(12,0))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ StringNode (location: (9,0)-(9,3))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (9,0)-(9,3) = "<<A"
+ │ │ ├── content_loc: (10,0)-(10,0) = ""
+ │ │ ├── closing_loc: (10,0)-(11,0) = "A\n"
+ │ │ └── unescaped: ""
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (9,3)-(9,4) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (9,4)-(12,0))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (9,4)-(12,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (9,4)-(10,0) = "%q\n"
+ │ │ ├── content_loc: (11,0)-(11,0) = ""
+ │ │ ├── closing_loc: (11,0)-(12,0) = "\n"
+ │ │ └── unescaped: ""
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (13,0)-(16,0))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ StringNode (location: (13,0)-(13,3))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (13,0)-(13,3) = "<<A"
+ │ │ ├── content_loc: (14,0)-(14,0) = ""
+ │ │ ├── closing_loc: (14,0)-(15,0) = "A\n"
+ │ │ └── unescaped: ""
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (13,3)-(13,4) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (13,4)-(16,0))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (13,4)-(16,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (13,4)-(14,0) = "%Q\n"
+ │ │ ├── content_loc: (15,0)-(15,0) = ""
+ │ │ ├── closing_loc: (15,0)-(16,0) = "\n"
+ │ │ └── unescaped: ""
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (17,0)-(20,0))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ StringNode (location: (17,0)-(17,3))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (17,0)-(17,3) = "<<A"
+ │ │ ├── content_loc: (18,0)-(18,0) = ""
+ │ │ ├── closing_loc: (18,0)-(19,0) = "A\n"
+ │ │ └── unescaped: ""
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (17,3)-(17,4) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (17,4)-(20,0))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ SymbolNode (location: (17,4)-(20,0))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (17,4)-(18,0) = "%s\n"
+ │ │ ├── value_loc: (18,0)-(18,0) = ""
+ │ │ ├── closing_loc: (19,0)-(20,0) = "\n"
+ │ │ └── unescaped: ""
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (21,0)-(24,0))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ StringNode (location: (21,0)-(21,3))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (21,0)-(21,3) = "<<A"
+ │ ├── content_loc: (22,0)-(22,0) = ""
+ │ ├── closing_loc: (22,0)-(23,0) = "A\n"
+ │ └── unescaped: ""
+ ├── call_operator_loc: ∅
+ ├── name: :+
+ ├── message_loc: (21,3)-(21,4) = "+"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (21,4)-(24,0))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ XStringNode (location: (21,4)-(24,0))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (21,4)-(22,0) = "%x\n"
+ │ ├── content_loc: (22,0)-(22,0) = ""
+ │ ├── closing_loc: (23,0)-(24,0) = "\n"
+ │ └── unescaped: ""
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/strings.txt b/test/prism/snapshots/strings.txt
new file mode 100644
index 0000000000..632d2ac3b5
--- /dev/null
+++ b/test/prism/snapshots/strings.txt
@@ -0,0 +1,534 @@
+@ ProgramNode (location: (1,0)-(105,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(105,4))
+ └── body: (length: 50)
+ ├── @ StringNode (location: (1,0)-(1,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,2) = "%%"
+ │ ├── content_loc: (1,2)-(1,5) = "abc"
+ │ ├── closing_loc: (1,5)-(1,6) = "%"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (3,0)-(3,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (3,0)-(3,2) = "%^"
+ │ ├── content_loc: (3,2)-(3,5) = "abc"
+ │ ├── closing_loc: (3,5)-(3,6) = "^"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (5,0)-(5,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (5,0)-(5,2) = "%&"
+ │ ├── content_loc: (5,2)-(5,5) = "abc"
+ │ ├── closing_loc: (5,5)-(5,6) = "&"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (7,0)-(7,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (7,0)-(7,2) = "%*"
+ │ ├── content_loc: (7,2)-(7,5) = "abc"
+ │ ├── closing_loc: (7,5)-(7,6) = "*"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (9,0)-(9,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (9,0)-(9,2) = "%_"
+ │ ├── content_loc: (9,2)-(9,5) = "abc"
+ │ ├── closing_loc: (9,5)-(9,6) = "_"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (11,0)-(11,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (11,0)-(11,2) = "%+"
+ │ ├── content_loc: (11,2)-(11,5) = "abc"
+ │ ├── closing_loc: (11,5)-(11,6) = "+"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (13,0)-(13,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (13,0)-(13,2) = "%-"
+ │ ├── content_loc: (13,2)-(13,5) = "abc"
+ │ ├── closing_loc: (13,5)-(13,6) = "-"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (15,0)-(15,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (15,0)-(15,2) = "%:"
+ │ ├── content_loc: (15,2)-(15,5) = "abc"
+ │ ├── closing_loc: (15,5)-(15,6) = ":"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (17,0)-(17,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (17,0)-(17,2) = "%;"
+ │ ├── content_loc: (17,2)-(17,5) = "abc"
+ │ ├── closing_loc: (17,5)-(17,6) = ";"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (19,0)-(19,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (19,0)-(19,2) = "%'"
+ │ ├── content_loc: (19,2)-(19,5) = "abc"
+ │ ├── closing_loc: (19,5)-(19,6) = "'"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (21,0)-(21,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (21,0)-(21,2) = "%~"
+ │ ├── content_loc: (21,2)-(21,5) = "abc"
+ │ ├── closing_loc: (21,5)-(21,6) = "~"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (23,0)-(23,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (23,0)-(23,2) = "%?"
+ │ ├── content_loc: (23,2)-(23,5) = "abc"
+ │ ├── closing_loc: (23,5)-(23,6) = "?"
+ │ └── unescaped: "abc"
+ ├── @ ArrayNode (location: (25,0)-(25,8))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 0)
+ │ ├── opening_loc: (25,0)-(25,3) = "%w{"
+ │ └── closing_loc: (25,7)-(25,8) = "}"
+ ├── @ StringNode (location: (27,0)-(27,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (27,0)-(27,2) = "%/"
+ │ ├── content_loc: (27,2)-(27,5) = "abc"
+ │ ├── closing_loc: (27,5)-(27,6) = "/"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (29,0)-(29,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (29,0)-(29,2) = "%`"
+ │ ├── content_loc: (29,2)-(29,5) = "abc"
+ │ ├── closing_loc: (29,5)-(29,6) = "`"
+ │ └── unescaped: "abc"
+ ├── @ InterpolatedStringNode (location: (31,0)-(31,8))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (31,0)-(31,1) = "\""
+ │ ├── parts: (length: 1)
+ │ │ └── @ EmbeddedVariableNode (location: (31,1)-(31,7))
+ │ │ ├── operator_loc: (31,1)-(31,2) = "#"
+ │ │ └── variable:
+ │ │ @ ClassVariableReadNode (location: (31,2)-(31,7))
+ │ │ └── name: :@@foo
+ │ └── closing_loc: (31,7)-(31,8) = "\""
+ ├── @ StringNode (location: (33,0)-(33,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (33,0)-(33,2) = "%\\"
+ │ ├── content_loc: (33,2)-(33,5) = "abc"
+ │ ├── closing_loc: (33,5)-(33,6) = "\\"
+ │ └── unescaped: "abc"
+ ├── @ InterpolatedStringNode (location: (35,0)-(35,17))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (35,0)-(35,2) = "%{"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (35,2)-(35,6))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (35,2)-(35,6) = "aaa "
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "aaa "
+ │ │ ├── @ EmbeddedStatementsNode (location: (35,6)-(35,12))
+ │ │ │ ├── opening_loc: (35,6)-(35,8) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (35,8)-(35,11))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (35,8)-(35,11))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bbb
+ │ │ │ │ ├── message_loc: (35,8)-(35,11) = "bbb"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── closing_loc: (35,11)-(35,12) = "}"
+ │ │ └── @ StringNode (location: (35,12)-(35,16))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (35,12)-(35,16) = " ccc"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: " ccc"
+ │ └── closing_loc: (35,16)-(35,17) = "}"
+ ├── @ StringNode (location: (37,0)-(37,8))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (37,0)-(37,2) = "%["
+ │ ├── content_loc: (37,2)-(37,7) = "foo[]"
+ │ ├── closing_loc: (37,7)-(37,8) = "]"
+ │ └── unescaped: "foo[]"
+ ├── @ CallNode (location: (39,0)-(41,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ StringNode (location: (39,0)-(39,5))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (39,0)-(39,1) = "\""
+ │ │ ├── content_loc: (39,1)-(39,4) = "foo"
+ │ │ ├── closing_loc: (39,4)-(39,5) = "\""
+ │ │ └── unescaped: "foo"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (39,6)-(39,7) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (41,0)-(41,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (41,0)-(41,5))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (41,0)-(41,1) = "\""
+ │ │ ├── content_loc: (41,1)-(41,4) = "bar"
+ │ │ ├── closing_loc: (41,4)-(41,5) = "\""
+ │ │ └── unescaped: "bar"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ StringNode (location: (43,0)-(43,7))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (43,0)-(43,3) = "%q{"
+ │ ├── content_loc: (43,3)-(43,6) = "abc"
+ │ ├── closing_loc: (43,6)-(43,7) = "}"
+ │ └── unescaped: "abc"
+ ├── @ SymbolNode (location: (45,0)-(45,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (45,0)-(45,3) = "%s["
+ │ ├── value_loc: (45,3)-(45,6) = "abc"
+ │ ├── closing_loc: (45,6)-(45,7) = "]"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (47,0)-(47,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (47,0)-(47,2) = "%{"
+ │ ├── content_loc: (47,2)-(47,5) = "abc"
+ │ ├── closing_loc: (47,5)-(47,6) = "}"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (49,0)-(49,2))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (49,0)-(49,1) = "'"
+ │ ├── content_loc: (49,1)-(49,1) = ""
+ │ ├── closing_loc: (49,1)-(49,2) = "'"
+ │ └── unescaped: ""
+ ├── @ StringNode (location: (51,0)-(51,5))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (51,0)-(51,1) = "\""
+ │ ├── content_loc: (51,1)-(51,4) = "abc"
+ │ ├── closing_loc: (51,4)-(51,5) = "\""
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (53,0)-(53,7))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (53,0)-(53,1) = "\""
+ │ ├── content_loc: (53,1)-(53,6) = "\#@---"
+ │ ├── closing_loc: (53,6)-(53,7) = "\""
+ │ └── unescaped: "\#@---"
+ ├── @ InterpolatedStringNode (location: (55,0)-(55,16))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (55,0)-(55,1) = "\""
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (55,1)-(55,5))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (55,1)-(55,5) = "aaa "
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "aaa "
+ │ │ ├── @ EmbeddedStatementsNode (location: (55,5)-(55,11))
+ │ │ │ ├── opening_loc: (55,5)-(55,7) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (55,7)-(55,10))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (55,7)-(55,10))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bbb
+ │ │ │ │ ├── message_loc: (55,7)-(55,10) = "bbb"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── closing_loc: (55,10)-(55,11) = "}"
+ │ │ └── @ StringNode (location: (55,11)-(55,15))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (55,11)-(55,15) = " ccc"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: " ccc"
+ │ └── closing_loc: (55,15)-(55,16) = "\""
+ ├── @ StringNode (location: (57,0)-(57,5))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (57,0)-(57,1) = "'"
+ │ ├── content_loc: (57,1)-(57,4) = "abc"
+ │ ├── closing_loc: (57,4)-(57,5) = "'"
+ │ └── unescaped: "abc"
+ ├── @ ArrayNode (location: (59,0)-(59,9))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 3)
+ │ │ ├── @ StringNode (location: (59,3)-(59,4))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (59,3)-(59,4) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ ├── @ StringNode (location: (59,5)-(59,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (59,5)-(59,6) = "b"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "b"
+ │ │ └── @ StringNode (location: (59,7)-(59,8))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (59,7)-(59,8) = "c"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "c"
+ │ ├── opening_loc: (59,0)-(59,3) = "%w["
+ │ └── closing_loc: (59,8)-(59,9) = "]"
+ ├── @ ArrayNode (location: (61,0)-(61,17))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 3)
+ │ │ ├── @ StringNode (location: (61,3)-(61,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (61,3)-(61,6) = "a[]"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a[]"
+ │ │ ├── @ StringNode (location: (61,7)-(61,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (61,7)-(61,12) = "b[[]]"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "b[[]]"
+ │ │ └── @ StringNode (location: (61,13)-(61,16))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (61,13)-(61,16) = "c[]"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "c[]"
+ │ ├── opening_loc: (61,0)-(61,3) = "%w["
+ │ └── closing_loc: (61,16)-(61,17) = "]"
+ ├── @ ArrayNode (location: (63,0)-(63,18))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ StringNode (location: (63,3)-(63,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (63,3)-(63,11) = "foo\\ bar"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo bar"
+ │ │ └── @ StringNode (location: (63,12)-(63,17))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (63,12)-(63,17) = "\\\#{1}"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\\\#{1}"
+ │ ├── opening_loc: (63,0)-(63,3) = "%w["
+ │ └── closing_loc: (63,17)-(63,18) = "]"
+ ├── @ ArrayNode (location: (65,0)-(65,16))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ StringNode (location: (65,3)-(65,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (65,3)-(65,11) = "foo\\ bar"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo bar"
+ │ │ └── @ StringNode (location: (65,12)-(65,15))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (65,12)-(65,15) = "baz"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "baz"
+ │ ├── opening_loc: (65,0)-(65,3) = "%w["
+ │ └── closing_loc: (65,15)-(65,16) = "]"
+ ├── @ ArrayNode (location: (67,0)-(67,14))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 3)
+ │ │ ├── @ StringNode (location: (67,3)-(67,4))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (67,3)-(67,4) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ ├── @ InterpolatedStringNode (location: (67,5)-(67,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── parts: (length: 3)
+ │ │ │ │ ├── @ StringNode (location: (67,5)-(67,6))
+ │ │ │ │ │ ├── flags: frozen
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── content_loc: (67,5)-(67,6) = "b"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "b"
+ │ │ │ │ ├── @ EmbeddedStatementsNode (location: (67,6)-(67,10))
+ │ │ │ │ │ ├── opening_loc: (67,6)-(67,8) = "\#{"
+ │ │ │ │ │ ├── statements:
+ │ │ │ │ │ │ @ StatementsNode (location: (67,8)-(67,9))
+ │ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ │ └── @ CallNode (location: (67,8)-(67,9))
+ │ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ │ ├── name: :c
+ │ │ │ │ │ │ ├── message_loc: (67,8)-(67,9) = "c"
+ │ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ │ └── block: ∅
+ │ │ │ │ │ └── closing_loc: (67,9)-(67,10) = "}"
+ │ │ │ │ └── @ StringNode (location: (67,10)-(67,11))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (67,10)-(67,11) = "d"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "d"
+ │ │ │ └── closing_loc: ∅
+ │ │ └── @ StringNode (location: (67,12)-(67,13))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (67,12)-(67,13) = "e"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "e"
+ │ ├── opening_loc: (67,0)-(67,3) = "%W["
+ │ └── closing_loc: (67,13)-(67,14) = "]"
+ ├── @ ArrayNode (location: (69,0)-(69,9))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 3)
+ │ │ ├── @ StringNode (location: (69,3)-(69,4))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (69,3)-(69,4) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ ├── @ StringNode (location: (69,5)-(69,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (69,5)-(69,6) = "b"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "b"
+ │ │ └── @ StringNode (location: (69,7)-(69,8))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (69,7)-(69,8) = "c"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "c"
+ │ ├── opening_loc: (69,0)-(69,3) = "%W["
+ │ └── closing_loc: (69,8)-(69,9) = "]"
+ ├── @ ArrayNode (location: (71,0)-(75,1))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 3)
+ │ │ ├── @ StringNode (location: (72,2)-(72,3))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (72,2)-(72,3) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ ├── @ StringNode (location: (73,2)-(73,3))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (73,2)-(73,3) = "b"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "b"
+ │ │ └── @ StringNode (location: (74,2)-(74,3))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (74,2)-(74,3) = "c"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "c"
+ │ ├── opening_loc: (71,0)-(71,3) = "%w["
+ │ └── closing_loc: (75,0)-(75,1) = "]"
+ ├── @ StringNode (location: (77,0)-(77,15))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (77,0)-(77,1) = "'"
+ │ ├── content_loc: (77,1)-(77,14) = "\\' foo \\' bar"
+ │ ├── closing_loc: (77,14)-(77,15) = "'"
+ │ └── unescaped: "' foo ' bar"
+ ├── @ StringNode (location: (79,0)-(79,15))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (79,0)-(79,1) = "'"
+ │ ├── content_loc: (79,1)-(79,14) = "\\\\ foo \\\\ bar"
+ │ ├── closing_loc: (79,14)-(79,15) = "'"
+ │ └── unescaped: "\\ foo \\ bar"
+ ├── @ InterpolatedStringNode (location: (81,0)-(81,7))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (81,0)-(81,1) = "\""
+ │ ├── parts: (length: 1)
+ │ │ └── @ EmbeddedVariableNode (location: (81,1)-(81,6))
+ │ │ ├── operator_loc: (81,1)-(81,2) = "#"
+ │ │ └── variable:
+ │ │ @ GlobalVariableReadNode (location: (81,2)-(81,6))
+ │ │ └── name: :$foo
+ │ └── closing_loc: (81,6)-(81,7) = "\""
+ ├── @ InterpolatedStringNode (location: (83,0)-(83,7))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (83,0)-(83,1) = "\""
+ │ ├── parts: (length: 1)
+ │ │ └── @ EmbeddedVariableNode (location: (83,1)-(83,6))
+ │ │ ├── operator_loc: (83,1)-(83,2) = "#"
+ │ │ └── variable:
+ │ │ @ InstanceVariableReadNode (location: (83,2)-(83,6))
+ │ │ └── name: :@foo
+ │ └── closing_loc: (83,6)-(83,7) = "\""
+ ├── @ StringNode (location: (85,0)-(85,15))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (85,0)-(85,1) = "\""
+ │ ├── content_loc: (85,1)-(85,14) = "\\x7 \\x23 \\x61"
+ │ ├── closing_loc: (85,14)-(85,15) = "\""
+ │ └── unescaped: "\a # a"
+ ├── @ StringNode (location: (87,0)-(87,13))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (87,0)-(87,1) = "\""
+ │ ├── content_loc: (87,1)-(87,12) = "\\7 \\43 \\141"
+ │ ├── closing_loc: (87,12)-(87,13) = "\""
+ │ └── unescaped: "\a # a"
+ ├── @ StringNode (location: (89,0)-(89,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (89,0)-(89,2) = "%["
+ │ ├── content_loc: (89,2)-(89,5) = "abc"
+ │ ├── closing_loc: (89,5)-(89,6) = "]"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (91,0)-(91,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (91,0)-(91,2) = "%("
+ │ ├── content_loc: (91,2)-(91,5) = "abc"
+ │ ├── closing_loc: (91,5)-(91,6) = ")"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (93,0)-(93,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (93,0)-(93,2) = "%@"
+ │ ├── content_loc: (93,2)-(93,5) = "abc"
+ │ ├── closing_loc: (93,5)-(93,6) = "@"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (95,0)-(95,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (95,0)-(95,2) = "%$"
+ │ ├── content_loc: (95,2)-(95,5) = "abc"
+ │ ├── closing_loc: (95,5)-(95,6) = "$"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (97,0)-(97,2))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (97,0)-(97,1) = "?"
+ │ ├── content_loc: (97,1)-(97,2) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── @ InterpolatedStringNode (location: (99,0)-(99,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: ∅
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (99,0)-(99,2))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: (99,0)-(99,1) = "?"
+ │ │ │ ├── content_loc: (99,1)-(99,2) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ └── @ StringNode (location: (99,3)-(99,6))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: (99,3)-(99,4) = "\""
+ │ │ ├── content_loc: (99,4)-(99,5) = "a"
+ │ │ ├── closing_loc: (99,5)-(99,6) = "\""
+ │ │ └── unescaped: "a"
+ │ └── closing_loc: ∅
+ ├── @ StringNode (location: (101,0)-(101,7))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (101,0)-(101,3) = "%Q{"
+ │ ├── content_loc: (101,3)-(101,6) = "abc"
+ │ ├── closing_loc: (101,6)-(101,7) = "}"
+ │ └── unescaped: "abc"
+ ├── @ StringNode (location: (103,0)-(103,5))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (103,0)-(103,2) = "%^"
+ │ ├── content_loc: (103,2)-(103,4) = "\#$"
+ │ ├── closing_loc: (103,4)-(103,5) = "^"
+ │ └── unescaped: "\#$"
+ └── @ StringNode (location: (105,0)-(105,4))
+ ├── flags: ∅
+ ├── opening_loc: (105,0)-(105,2) = "%@"
+ ├── content_loc: (105,2)-(105,3) = "#"
+ ├── closing_loc: (105,3)-(105,4) = "@"
+ └── unescaped: "#"
diff --git a/test/prism/snapshots/super.txt b/test/prism/snapshots/super.txt
new file mode 100644
index 0000000000..79f9a5d5a5
--- /dev/null
+++ b/test/prism/snapshots/super.txt
@@ -0,0 +1,132 @@
+@ ProgramNode (location: (1,0)-(17,21))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(17,21))
+ └── body: (length: 9)
+ ├── @ ForwardingSuperNode (location: (1,0)-(1,5))
+ │ └── block: ∅
+ ├── @ SuperNode (location: (3,0)-(3,7))
+ │ ├── keyword_loc: (3,0)-(3,5) = "super"
+ │ ├── lparen_loc: (3,5)-(3,6) = "("
+ │ ├── arguments: ∅
+ │ ├── rparen_loc: (3,6)-(3,7) = ")"
+ │ └── block: ∅
+ ├── @ SuperNode (location: (5,0)-(5,8))
+ │ ├── keyword_loc: (5,0)-(5,5) = "super"
+ │ ├── lparen_loc: (5,5)-(5,6) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (5,6)-(5,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (5,6)-(5,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── rparen_loc: (5,7)-(5,8) = ")"
+ │ └── block: ∅
+ ├── @ SuperNode (location: (7,0)-(7,14))
+ │ ├── keyword_loc: (7,0)-(7,5) = "super"
+ │ ├── lparen_loc: (7,5)-(7,6) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (7,6)-(7,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 3)
+ │ │ ├── @ IntegerNode (location: (7,6)-(7,7))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── @ IntegerNode (location: (7,9)-(7,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── @ IntegerNode (location: (7,12)-(7,13))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ ├── rparen_loc: (7,13)-(7,14) = ")"
+ │ └── block: ∅
+ ├── @ SuperNode (location: (9,0)-(9,11))
+ │ ├── keyword_loc: (9,0)-(9,5) = "super"
+ │ ├── lparen_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── rparen_loc: ∅
+ │ └── block:
+ │ @ BlockArgumentNode (location: (9,6)-(9,11))
+ │ ├── expression:
+ │ │ @ SymbolNode (location: (9,7)-(9,11))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (9,7)-(9,8) = ":"
+ │ │ ├── value_loc: (9,8)-(9,11) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ └── operator_loc: (9,6)-(9,7) = "&"
+ ├── @ SuperNode (location: (11,0)-(11,12))
+ │ ├── keyword_loc: (11,0)-(11,5) = "super"
+ │ ├── lparen_loc: (11,5)-(11,6) = "("
+ │ ├── arguments: ∅
+ │ ├── rparen_loc: (11,11)-(11,12) = ")"
+ │ └── block:
+ │ @ BlockArgumentNode (location: (11,6)-(11,11))
+ │ ├── expression:
+ │ │ @ SymbolNode (location: (11,7)-(11,11))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (11,7)-(11,8) = ":"
+ │ │ ├── value_loc: (11,8)-(11,11) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ └── operator_loc: (11,6)-(11,7) = "&"
+ ├── @ ForwardingSuperNode (location: (13,0)-(13,8))
+ │ └── block:
+ │ @ BlockNode (location: (13,6)-(13,8))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (13,6)-(13,7) = "{"
+ │ └── closing_loc: (13,7)-(13,8) = "}"
+ ├── @ SuperNode (location: (15,0)-(15,17))
+ │ ├── keyword_loc: (15,0)-(15,5) = "super"
+ │ ├── lparen_loc: (15,5)-(15,6) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (15,6)-(15,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 3)
+ │ │ ├── @ IntegerNode (location: (15,6)-(15,7))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── @ IntegerNode (location: (15,9)-(15,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── @ IntegerNode (location: (15,12)-(15,13))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ ├── rparen_loc: (15,13)-(15,14) = ")"
+ │ └── block:
+ │ @ BlockNode (location: (15,15)-(15,17))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (15,15)-(15,16) = "{"
+ │ └── closing_loc: (15,16)-(15,17) = "}"
+ └── @ SuperNode (location: (17,0)-(17,21))
+ ├── keyword_loc: (17,0)-(17,5) = "super"
+ ├── lparen_loc: (17,5)-(17,6) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (17,6)-(17,13))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 3)
+ │ ├── @ IntegerNode (location: (17,6)-(17,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── @ IntegerNode (location: (17,9)-(17,10))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ └── @ IntegerNode (location: (17,12)-(17,13))
+ │ ├── flags: decimal
+ │ └── value: 3
+ ├── rparen_loc: (17,20)-(17,21) = ")"
+ └── block:
+ @ BlockArgumentNode (location: (17,15)-(17,20))
+ ├── expression:
+ │ @ SymbolNode (location: (17,16)-(17,20))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (17,16)-(17,17) = ":"
+ │ ├── value_loc: (17,17)-(17,20) = "foo"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "foo"
+ └── operator_loc: (17,15)-(17,16) = "&"
diff --git a/test/prism/snapshots/symbols.txt b/test/prism/snapshots/symbols.txt
new file mode 100644
index 0000000000..dbd3a4d030
--- /dev/null
+++ b/test/prism/snapshots/symbols.txt
@@ -0,0 +1,464 @@
+@ ProgramNode (location: (1,0)-(93,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(93,13))
+ └── body: (length: 47)
+ ├── @ SymbolNode (location: (1,0)-(1,6))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,0)-(1,2) = ":'"
+ │ ├── value_loc: (1,2)-(1,5) = "abc"
+ │ ├── closing_loc: (1,5)-(1,6) = "'"
+ │ └── unescaped: "abc"
+ ├── @ InterpolatedSymbolNode (location: (3,0)-(3,9))
+ │ ├── opening_loc: (3,0)-(3,2) = ":\""
+ │ ├── parts: (length: 1)
+ │ │ └── @ EmbeddedStatementsNode (location: (3,2)-(3,8))
+ │ │ ├── opening_loc: (3,2)-(3,4) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (3,4)-(3,7))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (3,4)-(3,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :var
+ │ │ │ ├── message_loc: (3,4)-(3,7) = "var"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── closing_loc: (3,7)-(3,8) = "}"
+ │ └── closing_loc: (3,8)-(3,9) = "\""
+ ├── @ InterpolatedSymbolNode (location: (5,0)-(5,10))
+ │ ├── opening_loc: (5,0)-(5,2) = ":\""
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (5,2)-(5,5))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (5,2)-(5,5) = "abc"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "abc"
+ │ │ └── @ EmbeddedStatementsNode (location: (5,5)-(5,9))
+ │ │ ├── opening_loc: (5,5)-(5,7) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (5,7)-(5,8))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (5,7)-(5,8))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── closing_loc: (5,8)-(5,9) = "}"
+ │ └── closing_loc: (5,9)-(5,10) = "\""
+ ├── @ ArrayNode (location: (7,0)-(7,20))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 4)
+ │ │ ├── @ SymbolNode (location: (7,1)-(7,4))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (7,1)-(7,2) = ":"
+ │ │ │ ├── value_loc: (7,2)-(7,4) = "Υ"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "Υ"
+ │ │ ├── @ SymbolNode (location: (7,6)-(7,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (7,6)-(7,7) = ":"
+ │ │ │ ├── value_loc: (7,7)-(7,9) = "ά"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "ά"
+ │ │ ├── @ SymbolNode (location: (7,11)-(7,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (7,11)-(7,12) = ":"
+ │ │ │ ├── value_loc: (7,12)-(7,14) = "ŗ"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "ŗ"
+ │ │ └── @ SymbolNode (location: (7,16)-(7,19))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (7,16)-(7,17) = ":"
+ │ │ ├── value_loc: (7,17)-(7,19) = "Ï"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "Ï"
+ │ ├── opening_loc: (7,0)-(7,1) = "["
+ │ └── closing_loc: (7,19)-(7,20) = "]"
+ ├── @ SymbolNode (location: (9,0)-(9,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (9,0)-(9,1) = ":"
+ │ ├── value_loc: (9,1)-(9,3) = "-@"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "-@"
+ ├── @ SymbolNode (location: (11,0)-(11,2))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (11,0)-(11,1) = ":"
+ │ ├── value_loc: (11,1)-(11,2) = "-"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "-"
+ ├── @ SymbolNode (location: (13,0)-(13,2))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (13,0)-(13,1) = ":"
+ │ ├── value_loc: (13,1)-(13,2) = "%"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "%"
+ ├── @ SymbolNode (location: (15,0)-(15,2))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (15,0)-(15,1) = ":"
+ │ ├── value_loc: (15,1)-(15,2) = "|"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "|"
+ ├── @ SymbolNode (location: (17,0)-(17,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (17,0)-(17,1) = ":"
+ │ ├── value_loc: (17,1)-(17,3) = "+@"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "+@"
+ ├── @ SymbolNode (location: (19,0)-(19,2))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (19,0)-(19,1) = ":"
+ │ ├── value_loc: (19,1)-(19,2) = "+"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "+"
+ ├── @ SymbolNode (location: (21,0)-(21,2))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (21,0)-(21,1) = ":"
+ │ ├── value_loc: (21,1)-(21,2) = "/"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "/"
+ ├── @ SymbolNode (location: (23,0)-(23,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (23,0)-(23,1) = ":"
+ │ ├── value_loc: (23,1)-(23,3) = "**"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "**"
+ ├── @ SymbolNode (location: (25,0)-(25,2))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (25,0)-(25,1) = ":"
+ │ ├── value_loc: (25,1)-(25,2) = "*"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "*"
+ ├── @ SymbolNode (location: (27,0)-(27,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (27,0)-(27,1) = ":"
+ │ ├── value_loc: (27,1)-(27,3) = "~@"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "~"
+ ├── @ ArrayNode (location: (29,0)-(29,16))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 4)
+ │ │ ├── @ IntegerNode (location: (29,1)-(29,2))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── @ FloatNode (location: (29,4)-(29,7))
+ │ │ │ └── value: 1.0
+ │ │ ├── @ RationalNode (location: (29,9)-(29,11))
+ │ │ │ └── numeric:
+ │ │ │ @ IntegerNode (location: (29,9)-(29,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ ImaginaryNode (location: (29,13)-(29,15))
+ │ │ └── numeric:
+ │ │ @ IntegerNode (location: (29,13)-(29,14))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── opening_loc: (29,0)-(29,1) = "["
+ │ └── closing_loc: (29,15)-(29,16) = "]"
+ ├── @ SymbolNode (location: (31,0)-(31,2))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (31,0)-(31,1) = ":"
+ │ ├── value_loc: (31,1)-(31,2) = "~"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "~"
+ ├── @ SymbolNode (location: (33,0)-(33,2))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (33,0)-(33,1) = ":"
+ │ ├── value_loc: (33,1)-(33,2) = "a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "a"
+ ├── @ ArrayNode (location: (35,0)-(35,9))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 3)
+ │ │ ├── @ SymbolNode (location: (35,3)-(35,4))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (35,3)-(35,4) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ ├── @ SymbolNode (location: (35,5)-(35,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (35,5)-(35,6) = "b"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "b"
+ │ │ └── @ SymbolNode (location: (35,7)-(35,8))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (35,7)-(35,8) = "c"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "c"
+ │ ├── opening_loc: (35,0)-(35,3) = "%i["
+ │ └── closing_loc: (35,8)-(35,9) = "]"
+ ├── @ ArrayNode (location: (37,0)-(37,24))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 4)
+ │ │ ├── @ SymbolNode (location: (37,3)-(37,4))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (37,3)-(37,4) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ ├── @ SymbolNode (location: (37,5)-(37,10))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (37,5)-(37,10) = "b\#{1}"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "b\#{1}"
+ │ │ ├── @ SymbolNode (location: (37,11)-(37,16))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (37,11)-(37,16) = "\#{2}c"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\#{2}c"
+ │ │ └── @ SymbolNode (location: (37,17)-(37,23))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (37,17)-(37,23) = "d\#{3}f"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "d\#{3}f"
+ │ ├── opening_loc: (37,0)-(37,3) = "%i["
+ │ └── closing_loc: (37,23)-(37,24) = "]"
+ ├── @ ArrayNode (location: (39,0)-(39,24))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 4)
+ │ │ ├── @ SymbolNode (location: (39,3)-(39,4))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (39,3)-(39,4) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ ├── @ InterpolatedSymbolNode (location: (39,5)-(39,10))
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── parts: (length: 2)
+ │ │ │ │ ├── @ StringNode (location: (39,5)-(39,6))
+ │ │ │ │ │ ├── flags: frozen
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── content_loc: (39,5)-(39,6) = "b"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "b"
+ │ │ │ │ └── @ EmbeddedStatementsNode (location: (39,6)-(39,10))
+ │ │ │ │ ├── opening_loc: (39,6)-(39,8) = "\#{"
+ │ │ │ │ ├── statements:
+ │ │ │ │ │ @ StatementsNode (location: (39,8)-(39,9))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ IntegerNode (location: (39,8)-(39,9))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ └── closing_loc: (39,9)-(39,10) = "}"
+ │ │ │ └── closing_loc: ∅
+ │ │ ├── @ InterpolatedSymbolNode (location: (39,11)-(39,16))
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── parts: (length: 2)
+ │ │ │ │ ├── @ EmbeddedStatementsNode (location: (39,11)-(39,15))
+ │ │ │ │ │ ├── opening_loc: (39,11)-(39,13) = "\#{"
+ │ │ │ │ │ ├── statements:
+ │ │ │ │ │ │ @ StatementsNode (location: (39,13)-(39,14))
+ │ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ │ └── @ IntegerNode (location: (39,13)-(39,14))
+ │ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ │ └── value: 2
+ │ │ │ │ │ └── closing_loc: (39,14)-(39,15) = "}"
+ │ │ │ │ └── @ StringNode (location: (39,15)-(39,16))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (39,15)-(39,16) = "c"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "c"
+ │ │ │ └── closing_loc: ∅
+ │ │ └── @ InterpolatedSymbolNode (location: (39,17)-(39,23))
+ │ │ ├── opening_loc: ∅
+ │ │ ├── parts: (length: 3)
+ │ │ │ ├── @ StringNode (location: (39,17)-(39,18))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (39,17)-(39,18) = "d"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "d"
+ │ │ │ ├── @ EmbeddedStatementsNode (location: (39,18)-(39,22))
+ │ │ │ │ ├── opening_loc: (39,18)-(39,20) = "\#{"
+ │ │ │ │ ├── statements:
+ │ │ │ │ │ @ StatementsNode (location: (39,20)-(39,21))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ IntegerNode (location: (39,20)-(39,21))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 3
+ │ │ │ │ └── closing_loc: (39,21)-(39,22) = "}"
+ │ │ │ └── @ StringNode (location: (39,22)-(39,23))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (39,22)-(39,23) = "f"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "f"
+ │ │ └── closing_loc: ∅
+ │ ├── opening_loc: (39,0)-(39,3) = "%I["
+ │ └── closing_loc: (39,23)-(39,24) = "]"
+ ├── @ SymbolNode (location: (41,0)-(41,4))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (41,0)-(41,1) = ":"
+ │ ├── value_loc: (41,1)-(41,4) = "@@a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "@@a"
+ ├── @ SymbolNode (location: (43,0)-(43,5))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (43,0)-(43,1) = ":"
+ │ ├── value_loc: (43,1)-(43,5) = "ðŸ‘"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "ðŸ‘"
+ ├── @ ArrayNode (location: (45,0)-(45,7))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ SymbolNode (location: (45,3)-(45,6))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (45,3)-(45,6) = "a\\b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a\\b"
+ │ ├── opening_loc: (45,0)-(45,3) = "%i["
+ │ └── closing_loc: (45,6)-(45,7) = "]"
+ ├── @ SymbolNode (location: (47,0)-(47,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (47,0)-(47,1) = ":"
+ │ ├── value_loc: (47,1)-(47,3) = "$a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "$a"
+ ├── @ SymbolNode (location: (49,0)-(49,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (49,0)-(49,1) = ":"
+ │ ├── value_loc: (49,1)-(49,3) = "@a"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "@a"
+ ├── @ SymbolNode (location: (51,0)-(51,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (51,0)-(51,1) = ":"
+ │ ├── value_loc: (51,1)-(51,3) = "do"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "do"
+ ├── @ SymbolNode (location: (53,0)-(53,2))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (53,0)-(53,1) = ":"
+ │ ├── value_loc: (53,1)-(53,2) = "&"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "&"
+ ├── @ SymbolNode (location: (55,0)-(55,2))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (55,0)-(55,1) = ":"
+ │ ├── value_loc: (55,1)-(55,2) = "`"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "`"
+ ├── @ SymbolNode (location: (57,0)-(57,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (57,0)-(57,1) = ":"
+ │ ├── value_loc: (57,1)-(57,3) = "!@"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "!"
+ ├── @ SymbolNode (location: (59,0)-(59,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (59,0)-(59,1) = ":"
+ │ ├── value_loc: (59,1)-(59,3) = "!~"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "!~"
+ ├── @ SymbolNode (location: (61,0)-(61,2))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (61,0)-(61,1) = ":"
+ │ ├── value_loc: (61,1)-(61,2) = "!"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "!"
+ ├── @ SymbolNode (location: (63,0)-(63,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (63,0)-(63,1) = ":"
+ │ ├── value_loc: (63,1)-(63,3) = "[]"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "[]"
+ ├── @ SymbolNode (location: (65,0)-(65,4))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (65,0)-(65,1) = ":"
+ │ ├── value_loc: (65,1)-(65,4) = "[]="
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "[]="
+ ├── @ SymbolNode (location: (67,0)-(67,2))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (67,0)-(67,1) = ":"
+ │ ├── value_loc: (67,1)-(67,2) = "^"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "^"
+ ├── @ SymbolNode (location: (69,0)-(69,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (69,0)-(69,1) = ":"
+ │ ├── value_loc: (69,1)-(69,3) = "=="
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "=="
+ ├── @ SymbolNode (location: (71,0)-(71,4))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (71,0)-(71,1) = ":"
+ │ ├── value_loc: (71,1)-(71,4) = "==="
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "==="
+ ├── @ SymbolNode (location: (73,0)-(73,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (73,0)-(73,1) = ":"
+ │ ├── value_loc: (73,1)-(73,3) = "=~"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "=~"
+ ├── @ SymbolNode (location: (75,0)-(75,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (75,0)-(75,1) = ":"
+ │ ├── value_loc: (75,1)-(75,3) = ">="
+ │ ├── closing_loc: ∅
+ │ └── unescaped: ">="
+ ├── @ SymbolNode (location: (77,0)-(77,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (77,0)-(77,1) = ":"
+ │ ├── value_loc: (77,1)-(77,3) = ">>"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: ">>"
+ ├── @ SymbolNode (location: (79,0)-(79,2))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (79,0)-(79,1) = ":"
+ │ ├── value_loc: (79,1)-(79,2) = ">"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: ">"
+ ├── @ SymbolNode (location: (81,0)-(81,4))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (81,0)-(81,1) = ":"
+ │ ├── value_loc: (81,1)-(81,4) = "<=>"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "<=>"
+ ├── @ SymbolNode (location: (83,0)-(83,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (83,0)-(83,1) = ":"
+ │ ├── value_loc: (83,1)-(83,3) = "<="
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "<="
+ ├── @ SymbolNode (location: (85,0)-(85,3))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (85,0)-(85,1) = ":"
+ │ ├── value_loc: (85,1)-(85,3) = "<<"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "<<"
+ ├── @ SymbolNode (location: (87,0)-(87,2))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (87,0)-(87,1) = ":"
+ │ ├── value_loc: (87,1)-(87,2) = "<"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "<"
+ ├── @ SymbolNode (location: (89,0)-(89,9))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (89,0)-(89,1) = ":"
+ │ ├── value_loc: (89,1)-(89,9) = "__LINE__"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "__LINE__"
+ ├── @ SymbolNode (location: (91,0)-(91,9))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (91,0)-(91,1) = ":"
+ │ ├── value_loc: (91,1)-(91,9) = "__FILE__"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "__FILE__"
+ └── @ SymbolNode (location: (93,0)-(93,13))
+ ├── flags: forced_us_ascii_encoding
+ ├── opening_loc: (93,0)-(93,1) = ":"
+ ├── value_loc: (93,1)-(93,13) = "__ENCODING__"
+ ├── closing_loc: ∅
+ └── unescaped: "__ENCODING__"
diff --git a/test/prism/snapshots/ternary_operator.txt b/test/prism/snapshots/ternary_operator.txt
new file mode 100644
index 0000000000..0277ac88f0
--- /dev/null
+++ b/test/prism/snapshots/ternary_operator.txt
@@ -0,0 +1,295 @@
+@ ProgramNode (location: (1,0)-(15,12))
+├── locals: [:_a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(15,12))
+ └── body: (length: 8)
+ ├── @ IfNode (location: (1,0)-(1,9))
+ │ ├── if_keyword_loc: ∅
+ │ ├── predicate:
+ │ │ @ CallNode (location: (1,0)-(1,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: (1,2)-(1,3) = "?"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,4)-(1,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,4)-(1,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (1,4)-(1,5) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (1,6)-(1,9))
+ │ │ ├── else_keyword_loc: (1,6)-(1,7) = ":"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,8)-(1,9))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (1,8)-(1,9))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── message_loc: (1,8)-(1,9) = "c"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── end_keyword_loc: ∅
+ │ └── end_keyword_loc: ∅
+ ├── @ IfNode (location: (3,0)-(3,27))
+ │ ├── if_keyword_loc: ∅
+ │ ├── predicate:
+ │ │ @ CallNode (location: (3,0)-(3,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (3,0)-(3,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: (3,2)-(3,3) = "?"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,4)-(3,14))
+ │ │ └── body: (length: 1)
+ │ │ └── @ DefinedNode (location: (3,4)-(3,14))
+ │ │ ├── lparen_loc: ∅
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (3,13)-(3,14))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (3,13)-(3,14) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── rparen_loc: ∅
+ │ │ └── keyword_loc: (3,4)-(3,12) = "defined?"
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (3,15)-(3,27))
+ │ │ ├── else_keyword_loc: (3,15)-(3,16) = ":"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (3,17)-(3,27))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ DefinedNode (location: (3,17)-(3,27))
+ │ │ │ ├── lparen_loc: ∅
+ │ │ │ ├── value:
+ │ │ │ │ @ CallNode (location: (3,26)-(3,27))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (3,26)-(3,27) = "c"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── rparen_loc: ∅
+ │ │ │ └── keyword_loc: (3,17)-(3,25) = "defined?"
+ │ │ └── end_keyword_loc: ∅
+ │ └── end_keyword_loc: ∅
+ ├── @ IfNode (location: (5,0)-(5,15))
+ │ ├── if_keyword_loc: ∅
+ │ ├── predicate:
+ │ │ @ CallNode (location: (5,0)-(5,6))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :empty?
+ │ │ ├── message_loc: (5,0)-(5,6) = "empty?"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: (5,6)-(5,7) = "?"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (5,7)-(5,11))
+ │ │ └── body: (length: 1)
+ │ │ └── @ TrueNode (location: (5,7)-(5,11))
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (5,11)-(5,15))
+ │ │ ├── else_keyword_loc: (5,11)-(5,12) = ":"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (5,12)-(5,15))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ NilNode (location: (5,12)-(5,15))
+ │ │ └── end_keyword_loc: ∅
+ │ └── end_keyword_loc: ∅
+ ├── @ IfNode (location: (7,0)-(7,16))
+ │ ├── if_keyword_loc: ∅
+ │ ├── predicate:
+ │ │ @ CallNode (location: (7,0)-(7,6))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :empty?
+ │ │ ├── message_loc: (7,0)-(7,6) = "empty?"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: (7,6)-(7,7) = "?"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (7,7)-(7,12))
+ │ │ └── body: (length: 1)
+ │ │ └── @ FalseNode (location: (7,7)-(7,12))
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (7,12)-(7,16))
+ │ │ ├── else_keyword_loc: (7,12)-(7,13) = ":"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (7,13)-(7,16))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ NilNode (location: (7,13)-(7,16))
+ │ │ └── end_keyword_loc: ∅
+ │ └── end_keyword_loc: ∅
+ ├── @ IfNode (location: (9,0)-(9,14))
+ │ ├── if_keyword_loc: ∅
+ │ ├── predicate:
+ │ │ @ CallNode (location: (9,0)-(9,6))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :empty?
+ │ │ ├── message_loc: (9,0)-(9,6) = "empty?"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: (9,6)-(9,7) = "?"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (9,7)-(9,10))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NilNode (location: (9,7)-(9,10))
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (9,10)-(9,14))
+ │ │ ├── else_keyword_loc: (9,10)-(9,11) = ":"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (9,11)-(9,14))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ NilNode (location: (9,11)-(9,14))
+ │ │ └── end_keyword_loc: ∅
+ │ └── end_keyword_loc: ∅
+ ├── @ IfNode (location: (11,0)-(11,10))
+ │ ├── if_keyword_loc: ∅
+ │ ├── predicate:
+ │ │ @ CallNode (location: (11,0)-(11,2))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a?
+ │ │ ├── message_loc: (11,0)-(11,2) = "a?"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: (11,2)-(11,3) = "?"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (11,3)-(11,6))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NilNode (location: (11,3)-(11,6))
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (11,6)-(11,10))
+ │ │ ├── else_keyword_loc: (11,6)-(11,7) = ":"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (11,7)-(11,10))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ NilNode (location: (11,7)-(11,10))
+ │ │ └── end_keyword_loc: ∅
+ │ └── end_keyword_loc: ∅
+ ├── @ IfNode (location: (13,0)-(13,14))
+ │ ├── if_keyword_loc: ∅
+ │ ├── predicate:
+ │ │ @ CallNode (location: (13,0)-(13,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (13,0)-(13,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: (13,2)-(13,3) = "?"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (13,3)-(13,7))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (13,3)-(13,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :var1
+ │ │ ├── message_loc: (13,3)-(13,7) = "var1"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (13,8)-(13,14))
+ │ │ ├── else_keyword_loc: (13,8)-(13,9) = ":"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (13,10)-(13,14))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (13,10)-(13,14))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :var2
+ │ │ │ ├── message_loc: (13,10)-(13,14) = "var2"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── end_keyword_loc: ∅
+ │ └── end_keyword_loc: ∅
+ └── @ IfNode (location: (15,0)-(15,12))
+ ├── if_keyword_loc: ∅
+ ├── predicate:
+ │ @ CallNode (location: (15,0)-(15,4))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :nil?
+ │ ├── message_loc: (15,0)-(15,4) = "nil?"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── then_keyword_loc: (15,4)-(15,5) = "?"
+ ├── statements:
+ │ @ StatementsNode (location: (15,5)-(15,10))
+ │ └── body: (length: 1)
+ │ └── @ LocalVariableWriteNode (location: (15,5)-(15,10))
+ │ ├── name: :_a
+ │ ├── depth: 0
+ │ ├── name_loc: (15,5)-(15,7) = "_a"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (15,9)-(15,10))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ └── operator_loc: (15,8)-(15,9) = "="
+ ├── consequent:
+ │ @ ElseNode (location: (15,10)-(15,12))
+ │ ├── else_keyword_loc: (15,10)-(15,11) = ":"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (15,11)-(15,12))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (15,11)-(15,12))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── end_keyword_loc: ∅
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/tilde_heredocs.txt b/test/prism/snapshots/tilde_heredocs.txt
new file mode 100644
index 0000000000..f50f915a64
--- /dev/null
+++ b/test/prism/snapshots/tilde_heredocs.txt
@@ -0,0 +1,405 @@
+@ ProgramNode (location: (1,0)-(94,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(94,6))
+ └── body: (length: 19)
+ ├── @ InterpolatedStringNode (location: (1,0)-(1,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,6) = "<<~EOF"
+ │ ├── parts: (length: 4)
+ │ │ ├── @ StringNode (location: (2,0)-(3,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (2,0)-(3,0) = " a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: " a\n"
+ │ │ ├── @ EmbeddedStatementsNode (location: (3,0)-(3,4))
+ │ │ │ ├── opening_loc: (3,0)-(3,2) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (3,2)-(3,3))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (3,2)-(3,3))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── closing_loc: (3,3)-(3,4) = "}"
+ │ │ ├── @ StringNode (location: (3,4)-(4,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (3,4)-(4,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── @ StringNode (location: (4,0)-(5,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (4,0)-(5,0) = " a\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: " a\n"
+ │ └── closing_loc: (5,0)-(6,0) = "EOF\n"
+ ├── @ StringNode (location: (7,0)-(7,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (7,0)-(7,6) = "<<~EOF"
+ │ ├── content_loc: (8,0)-(9,0) = " a\n"
+ │ ├── closing_loc: (9,0)-(10,0) = "EOF\n"
+ │ └── unescaped: "a\n"
+ ├── @ InterpolatedStringNode (location: (11,0)-(11,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (11,0)-(11,6) = "<<~EOF"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (12,0)-(13,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (12,0)-(13,0) = "\ta\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\ta\n"
+ │ │ ├── @ StringNode (location: (13,0)-(14,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (13,0)-(14,0) = " b\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "b\n"
+ │ │ └── @ StringNode (location: (14,0)-(15,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (14,0)-(15,0) = "\t\tc\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\t\tc\n"
+ │ └── closing_loc: (15,0)-(16,0) = "EOF\n"
+ ├── @ InterpolatedStringNode (location: (17,0)-(17,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (17,0)-(17,6) = "<<~EOF"
+ │ ├── parts: (length: 2)
+ │ │ ├── @ EmbeddedStatementsNode (location: (18,2)-(18,6))
+ │ │ │ ├── opening_loc: (18,2)-(18,4) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (18,4)-(18,5))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (18,4)-(18,5))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── closing_loc: (18,5)-(18,6) = "}"
+ │ │ └── @ StringNode (location: (18,6)-(19,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (18,6)-(19,0) = " a\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: " a\n"
+ │ └── closing_loc: (19,0)-(20,0) = "EOF\n"
+ ├── @ InterpolatedStringNode (location: (21,0)-(21,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (21,0)-(21,6) = "<<~EOF"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (22,0)-(22,4))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (22,0)-(22,4) = " a "
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a "
+ │ │ ├── @ EmbeddedStatementsNode (location: (22,4)-(22,8))
+ │ │ │ ├── opening_loc: (22,4)-(22,6) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (22,6)-(22,7))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (22,6)-(22,7))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── closing_loc: (22,7)-(22,8) = "}"
+ │ │ └── @ StringNode (location: (22,8)-(23,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (22,8)-(23,0) = "\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\n"
+ │ └── closing_loc: (23,0)-(24,0) = "EOF\n"
+ ├── @ InterpolatedStringNode (location: (25,0)-(25,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (25,0)-(25,6) = "<<~EOF"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (26,0)-(27,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (26,0)-(27,0) = " a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: " a\n"
+ │ │ ├── @ EmbeddedStatementsNode (location: (27,1)-(27,5))
+ │ │ │ ├── opening_loc: (27,1)-(27,3) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (27,3)-(27,4))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (27,3)-(27,4))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── closing_loc: (27,4)-(27,5) = "}"
+ │ │ └── @ StringNode (location: (27,5)-(28,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (27,5)-(28,0) = "\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\n"
+ │ └── closing_loc: (28,0)-(29,0) = "EOF\n"
+ ├── @ InterpolatedStringNode (location: (30,0)-(30,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (30,0)-(30,6) = "<<~EOF"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (31,0)-(32,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (31,0)-(32,0) = " a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ ├── @ EmbeddedStatementsNode (location: (32,2)-(32,6))
+ │ │ │ ├── opening_loc: (32,2)-(32,4) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (32,4)-(32,5))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (32,4)-(32,5))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── closing_loc: (32,5)-(32,6) = "}"
+ │ │ └── @ StringNode (location: (32,6)-(33,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (32,6)-(33,0) = "\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\n"
+ │ └── closing_loc: (33,0)-(34,0) = "EOF\n"
+ ├── @ InterpolatedStringNode (location: (35,0)-(35,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (35,0)-(35,6) = "<<~EOF"
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (36,0)-(37,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (36,0)-(37,0) = " a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ └── @ StringNode (location: (37,0)-(38,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (37,0)-(38,0) = " b\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b\n"
+ │ └── closing_loc: (38,0)-(39,0) = "EOF\n"
+ ├── @ InterpolatedStringNode (location: (40,0)-(40,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (40,0)-(40,6) = "<<~EOF"
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (41,0)-(42,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (41,0)-(42,0) = " a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ └── @ StringNode (location: (42,0)-(43,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (42,0)-(43,0) = " b\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: " b\n"
+ │ └── closing_loc: (43,0)-(44,0) = "EOF\n"
+ ├── @ InterpolatedStringNode (location: (45,0)-(45,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (45,0)-(45,6) = "<<~EOF"
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (46,0)-(47,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (46,0)-(47,0) = "\t\t\ta\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\ta\n"
+ │ │ └── @ StringNode (location: (47,0)-(48,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (47,0)-(48,0) = "\t\tb\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b\n"
+ │ └── closing_loc: (48,0)-(49,0) = "EOF\n"
+ ├── @ StringNode (location: (50,0)-(50,8))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (50,0)-(50,8) = "<<~'EOF'"
+ │ ├── content_loc: (51,0)-(52,0) = " a \#{1}\n"
+ │ ├── closing_loc: (52,0)-(53,0) = "EOF\n"
+ │ └── unescaped: "a \#{1}\n"
+ ├── @ InterpolatedStringNode (location: (54,0)-(54,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (54,0)-(54,6) = "<<~EOF"
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (55,0)-(56,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (55,0)-(56,0) = "\ta\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ └── @ StringNode (location: (56,0)-(57,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (56,0)-(57,0) = "\t b\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: " b\n"
+ │ └── closing_loc: (57,0)-(58,0) = "EOF\n"
+ ├── @ InterpolatedStringNode (location: (59,0)-(59,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (59,0)-(59,6) = "<<~EOF"
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (60,0)-(61,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (60,0)-(61,0) = "\t a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: " a\n"
+ │ │ └── @ StringNode (location: (61,0)-(62,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (61,0)-(62,0) = "\tb\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b\n"
+ │ └── closing_loc: (62,0)-(63,0) = "EOF\n"
+ ├── @ InterpolatedStringNode (location: (64,0)-(64,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (64,0)-(64,6) = "<<~EOF"
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (65,0)-(66,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (65,0)-(66,0) = " \ta\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ └── @ StringNode (location: (66,0)-(67,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (66,0)-(67,0) = " b\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b\n"
+ │ └── closing_loc: (67,0)-(68,0) = "EOF\n"
+ ├── @ InterpolatedStringNode (location: (69,0)-(69,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (69,0)-(69,6) = "<<~EOF"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (70,0)-(71,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (70,0)-(71,0) = " a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ ├── @ StringNode (location: (71,0)-(72,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (71,0)-(72,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── @ StringNode (location: (72,0)-(73,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (72,0)-(73,0) = " b\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b\n"
+ │ └── closing_loc: (73,0)-(74,0) = "EOF\n"
+ ├── @ InterpolatedStringNode (location: (75,0)-(75,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (75,0)-(75,6) = "<<~EOF"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (76,0)-(77,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (76,0)-(77,0) = " a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ ├── @ StringNode (location: (77,0)-(78,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (77,0)-(78,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── @ StringNode (location: (78,0)-(79,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (78,0)-(79,0) = " b\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b\n"
+ │ └── closing_loc: (79,0)-(80,0) = "EOF\n"
+ ├── @ InterpolatedStringNode (location: (81,0)-(81,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (81,0)-(81,6) = "<<~EOF"
+ │ ├── parts: (length: 5)
+ │ │ ├── @ StringNode (location: (82,0)-(83,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (82,0)-(83,0) = " a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ ├── @ StringNode (location: (83,0)-(84,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (83,0)-(84,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ ├── @ StringNode (location: (84,0)-(85,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (84,0)-(85,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ ├── @ StringNode (location: (85,0)-(86,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (85,0)-(86,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── @ StringNode (location: (86,0)-(87,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (86,0)-(87,0) = " b\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b\n"
+ │ └── closing_loc: (87,0)-(88,0) = "EOF\n"
+ ├── @ InterpolatedStringNode (location: (89,0)-(89,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (89,0)-(89,6) = "<<~EOF"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (90,0)-(91,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (90,0)-(91,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ ├── @ EmbeddedStatementsNode (location: (91,2)-(91,6))
+ │ │ │ ├── opening_loc: (91,2)-(91,4) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (91,4)-(91,5))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (91,4)-(91,5))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── closing_loc: (91,5)-(91,6) = "}"
+ │ │ └── @ StringNode (location: (91,6)-(92,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (91,6)-(92,0) = "a\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a\n"
+ │ └── closing_loc: (92,0)-(93,0) = " EOF\n"
+ └── @ InterpolatedStringNode (location: (94,0)-(94,6))
+ ├── flags: ∅
+ ├── opening_loc: (94,0)-(94,6) = "<<~EOT"
+ ├── parts: (length: 3)
+ │ ├── @ EmbeddedStatementsNode (location: (95,2)-(95,6))
+ │ │ ├── opening_loc: (95,2)-(95,4) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (95,4)-(95,5))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (95,4)-(95,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── closing_loc: (95,5)-(95,6) = "}"
+ │ ├── @ StringNode (location: (95,6)-(96,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (95,6)-(96,0) = "\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\n"
+ │ └── @ StringNode (location: (96,0)-(97,0))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (96,0)-(97,0) = "\tb\n"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "\tb\n"
+ └── closing_loc: (97,0)-(98,0) = "EOT\n"
diff --git a/test/prism/snapshots/undef.txt b/test/prism/snapshots/undef.txt
new file mode 100644
index 0000000000..e59ace92f5
--- /dev/null
+++ b/test/prism/snapshots/undef.txt
@@ -0,0 +1,117 @@
+@ ProgramNode (location: (1,0)-(17,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(17,14))
+ └── body: (length: 9)
+ ├── @ UndefNode (location: (1,0)-(1,7))
+ │ ├── names: (length: 1)
+ │ │ └── @ SymbolNode (location: (1,6)-(1,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ └── keyword_loc: (1,0)-(1,5) = "undef"
+ ├── @ UndefNode (location: (3,0)-(3,10))
+ │ ├── names: (length: 2)
+ │ │ ├── @ SymbolNode (location: (3,6)-(3,7))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (3,6)-(3,7) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ └── @ SymbolNode (location: (3,9)-(3,10))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (3,9)-(3,10) = "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b"
+ │ └── keyword_loc: (3,0)-(3,5) = "undef"
+ ├── @ UndefNode (location: (5,0)-(5,8))
+ │ ├── names: (length: 1)
+ │ │ └── @ SymbolNode (location: (5,6)-(5,8))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (5,6)-(5,8) = "if"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "if"
+ │ └── keyword_loc: (5,0)-(5,5) = "undef"
+ ├── @ UndefNode (location: (7,0)-(7,9))
+ │ ├── names: (length: 1)
+ │ │ └── @ SymbolNode (location: (7,6)-(7,9))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (7,6)-(7,9) = "<=>"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "<=>"
+ │ └── keyword_loc: (7,0)-(7,5) = "undef"
+ ├── @ UndefNode (location: (9,0)-(9,8))
+ │ ├── names: (length: 1)
+ │ │ └── @ SymbolNode (location: (9,6)-(9,8))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (9,6)-(9,7) = ":"
+ │ │ ├── value_loc: (9,7)-(9,8) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ └── keyword_loc: (9,0)-(9,5) = "undef"
+ ├── @ UndefNode (location: (11,0)-(11,16))
+ │ ├── names: (length: 3)
+ │ │ ├── @ SymbolNode (location: (11,6)-(11,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (11,6)-(11,7) = ":"
+ │ │ │ ├── value_loc: (11,7)-(11,8) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ ├── @ SymbolNode (location: (11,10)-(11,12))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (11,10)-(11,11) = ":"
+ │ │ │ ├── value_loc: (11,11)-(11,12) = "b"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "b"
+ │ │ └── @ SymbolNode (location: (11,14)-(11,16))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (11,14)-(11,15) = ":"
+ │ │ ├── value_loc: (11,15)-(11,16) = "c"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "c"
+ │ └── keyword_loc: (11,0)-(11,5) = "undef"
+ ├── @ UndefNode (location: (13,0)-(13,12))
+ │ ├── names: (length: 1)
+ │ │ └── @ SymbolNode (location: (13,6)-(13,12))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (13,6)-(13,8) = ":'"
+ │ │ ├── value_loc: (13,8)-(13,11) = "abc"
+ │ │ ├── closing_loc: (13,11)-(13,12) = "'"
+ │ │ └── unescaped: "abc"
+ │ └── keyword_loc: (13,0)-(13,5) = "undef"
+ ├── @ UndefNode (location: (15,0)-(15,16))
+ │ ├── names: (length: 1)
+ │ │ └── @ InterpolatedSymbolNode (location: (15,6)-(15,16))
+ │ │ ├── opening_loc: (15,6)-(15,8) = ":\""
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (15,8)-(15,11))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (15,8)-(15,11) = "abc"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "abc"
+ │ │ │ └── @ EmbeddedStatementsNode (location: (15,11)-(15,15))
+ │ │ │ ├── opening_loc: (15,11)-(15,13) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (15,13)-(15,14))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (15,13)-(15,14))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── closing_loc: (15,14)-(15,15) = "}"
+ │ │ └── closing_loc: (15,15)-(15,16) = "\""
+ │ └── keyword_loc: (15,0)-(15,5) = "undef"
+ └── @ UndefNode (location: (17,0)-(17,14))
+ ├── names: (length: 1)
+ │ └── @ SymbolNode (location: (17,6)-(17,14))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: ∅
+ │ ├── value_loc: (17,6)-(17,14) = "Constant"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "Constant"
+ └── keyword_loc: (17,0)-(17,5) = "undef"
diff --git a/test/prism/snapshots/unescaping.txt b/test/prism/snapshots/unescaping.txt
new file mode 100644
index 0000000000..456ef226d0
--- /dev/null
+++ b/test/prism/snapshots/unescaping.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(7,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(7,7))
+ └── body: (length: 4)
+ ├── @ ArrayNode (location: (1,0)-(1,10))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ StringNode (location: (1,1)-(1,9))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,1)-(1,2) = "\""
+ │ │ ├── content_loc: (1,2)-(1,8) = "\\c\#{1}"
+ │ │ ├── closing_loc: (1,8)-(1,9) = "\""
+ │ │ └── unescaped: "\u0003{1}"
+ │ ├── opening_loc: (1,0)-(1,1) = "["
+ │ └── closing_loc: (1,9)-(1,10) = "]"
+ ├── @ RegularExpressionNode (location: (3,0)-(3,8))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (3,0)-(3,1) = "/"
+ │ ├── content_loc: (3,1)-(3,7) = "\\c\#{1}"
+ │ ├── closing_loc: (3,7)-(3,8) = "/"
+ │ └── unescaped: "\\x03{1}"
+ ├── @ StringNode (location: (5,0)-(5,8))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (5,0)-(5,1) = "\""
+ │ ├── content_loc: (5,1)-(5,7) = "\\c\#{1}"
+ │ ├── closing_loc: (5,7)-(5,8) = "\""
+ │ └── unescaped: "\u0003{1}"
+ └── @ StringNode (location: (7,0)-(7,7))
+ ├── flags: ∅
+ ├── opening_loc: (7,0)-(7,7) = "<<~HERE"
+ ├── content_loc: (8,0)-(9,0) = " \\c\#{1}\n"
+ ├── closing_loc: (9,0)-(10,0) = "HERE\n"
+ └── unescaped: "\u0003{1}\n"
diff --git a/test/prism/snapshots/unless.txt b/test/prism/snapshots/unless.txt
new file mode 100644
index 0000000000..6c4aaf66a5
--- /dev/null
+++ b/test/prism/snapshots/unless.txt
@@ -0,0 +1,173 @@
+@ ProgramNode (location: (1,0)-(14,22))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(14,22))
+ └── body: (length: 7)
+ ├── @ UnlessNode (location: (1,0)-(1,19))
+ │ ├── keyword_loc: (1,0)-(1,6) = "unless"
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (1,7)-(1,11))
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,13)-(1,14))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,13)-(1,14))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (1,16)-(1,19) = "end"
+ ├── @ UnlessNode (location: (3,0)-(4,12))
+ │ ├── keyword_loc: (3,0)-(3,6) = "unless"
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (3,7)-(3,11))
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (4,0)-(4,1))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (4,0)-(4,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (4,2)-(4,12))
+ │ │ ├── else_keyword_loc: (4,2)-(4,6) = "else"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (4,7)-(4,8))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (4,7)-(4,8))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── end_keyword_loc: (4,9)-(4,12) = "end"
+ │ └── end_keyword_loc: (4,9)-(4,12) = "end"
+ ├── @ UnlessNode (location: (6,0)-(6,13))
+ │ ├── keyword_loc: (6,2)-(6,8) = "unless"
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (6,9)-(6,13))
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (6,0)-(6,1))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (6,0)-(6,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: ∅
+ ├── @ CallNode (location: (8,0)-(8,25))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (8,0)-(8,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (8,4)-(8,25))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (8,6)-(8,23))
+ │ │ └── body: (length: 1)
+ │ │ └── @ UnlessNode (location: (8,6)-(8,23))
+ │ │ ├── keyword_loc: (8,12)-(8,18) = "unless"
+ │ │ ├── predicate:
+ │ │ │ @ TrueNode (location: (8,19)-(8,23))
+ │ │ ├── then_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (8,6)-(8,11))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ BreakNode (location: (8,6)-(8,11))
+ │ │ │ ├── arguments: ∅
+ │ │ │ └── keyword_loc: (8,6)-(8,11) = "break"
+ │ │ ├── consequent: ∅
+ │ │ └── end_keyword_loc: ∅
+ │ ├── opening_loc: (8,4)-(8,5) = "{"
+ │ └── closing_loc: (8,24)-(8,25) = "}"
+ ├── @ CallNode (location: (10,0)-(10,24))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (10,0)-(10,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (10,4)-(10,24))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (10,6)-(10,22))
+ │ │ └── body: (length: 1)
+ │ │ └── @ UnlessNode (location: (10,6)-(10,22))
+ │ │ ├── keyword_loc: (10,11)-(10,17) = "unless"
+ │ │ ├── predicate:
+ │ │ │ @ TrueNode (location: (10,18)-(10,22))
+ │ │ ├── then_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (10,6)-(10,10))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ NextNode (location: (10,6)-(10,10))
+ │ │ │ ├── arguments: ∅
+ │ │ │ └── keyword_loc: (10,6)-(10,10) = "next"
+ │ │ ├── consequent: ∅
+ │ │ └── end_keyword_loc: ∅
+ │ ├── opening_loc: (10,4)-(10,5) = "{"
+ │ └── closing_loc: (10,23)-(10,24) = "}"
+ ├── @ UnlessNode (location: (12,0)-(12,18))
+ │ ├── keyword_loc: (12,7)-(12,13) = "unless"
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (12,14)-(12,18))
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (12,0)-(12,6))
+ │ │ └── body: (length: 1)
+ │ │ └── @ ReturnNode (location: (12,0)-(12,6))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (12,0)-(12,6) = "return"
+ │ │ └── arguments: ∅
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: ∅
+ └── @ UnlessNode (location: (14,0)-(14,22))
+ ├── keyword_loc: (14,11)-(14,17) = "unless"
+ ├── predicate:
+ │ @ CallNode (location: (14,18)-(14,22))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar?
+ │ ├── message_loc: (14,18)-(14,22) = "bar?"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── then_keyword_loc: ∅
+ ├── statements:
+ │ @ StatementsNode (location: (14,0)-(14,10))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (14,0)-(14,10))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (14,0)-(14,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (14,4)-(14,10))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ SymbolNode (location: (14,4)-(14,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (14,4)-(14,5) = ":"
+ │ │ │ ├── value_loc: (14,5)-(14,6) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ └── @ SymbolNode (location: (14,8)-(14,10))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (14,8)-(14,9) = ":"
+ │ │ ├── value_loc: (14,9)-(14,10) = "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/unparser/corpus/literal/alias.txt b/test/prism/snapshots/unparser/corpus/literal/alias.txt
new file mode 100644
index 0000000000..18ddc86d4f
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/alias.txt
@@ -0,0 +1,29 @@
+@ ProgramNode (location: (1,0)-(2,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,15))
+ └── body: (length: 2)
+ ├── @ AliasGlobalVariableNode (location: (1,0)-(1,15))
+ │ ├── new_name:
+ │ │ @ GlobalVariableReadNode (location: (1,6)-(1,10))
+ │ │ └── name: :$foo
+ │ ├── old_name:
+ │ │ @ GlobalVariableReadNode (location: (1,11)-(1,15))
+ │ │ └── name: :$bar
+ │ └── keyword_loc: (1,0)-(1,5) = "alias"
+ └── @ AliasMethodNode (location: (2,0)-(2,15))
+ ├── new_name:
+ │ @ SymbolNode (location: (2,6)-(2,10))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (2,6)-(2,7) = ":"
+ │ ├── value_loc: (2,7)-(2,10) = "foo"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "foo"
+ ├── old_name:
+ │ @ SymbolNode (location: (2,11)-(2,15))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (2,11)-(2,12) = ":"
+ │ ├── value_loc: (2,12)-(2,15) = "bar"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "bar"
+ └── keyword_loc: (2,0)-(2,5) = "alias"
diff --git a/test/prism/snapshots/unparser/corpus/literal/assignment.txt b/test/prism/snapshots/unparser/corpus/literal/assignment.txt
new file mode 100644
index 0000000000..99c8daf34c
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/assignment.txt
@@ -0,0 +1,1080 @@
+@ ProgramNode (location: (1,0)-(51,17))
+├── locals: [:a, :b, :foo, :c, :x]
+└── statements:
+ @ StatementsNode (location: (1,0)-(51,17))
+ └── body: (length: 43)
+ ├── @ GlobalVariableWriteNode (location: (1,0)-(1,6))
+ │ ├── name: :$a
+ │ ├── name_loc: (1,0)-(1,2) = "$a"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,5)-(1,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (1,3)-(1,4) = "="
+ ├── @ MultiWriteNode (location: (2,0)-(2,17))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ GlobalVariableTargetNode (location: (2,1)-(2,3))
+ │ │ │ └── name: :$a
+ │ │ └── @ GlobalVariableTargetNode (location: (2,5)-(2,7))
+ │ │ └── name: :$b
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: (2,0)-(2,1) = "("
+ │ ├── rparen_loc: (2,7)-(2,8) = ")"
+ │ ├── operator_loc: (2,9)-(2,10) = "="
+ │ └── value:
+ │ @ ArrayNode (location: (2,11)-(2,17))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (2,12)-(2,13))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (2,15)-(2,16))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── opening_loc: (2,11)-(2,12) = "["
+ │ └── closing_loc: (2,16)-(2,17) = "]"
+ ├── @ MultiWriteNode (location: (3,0)-(3,13))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ MultiTargetNode (location: (3,1)-(3,5))
+ │ │ │ ├── lefts: (length: 1)
+ │ │ │ │ └── @ LocalVariableTargetNode (location: (3,2)-(3,3))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── rest:
+ │ │ │ │ @ ImplicitRestNode (location: (3,3)-(3,4))
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: (3,1)-(3,2) = "("
+ │ │ │ └── rparen_loc: (3,4)-(3,5) = ")"
+ │ │ └── @ LocalVariableTargetNode (location: (3,7)-(3,8))
+ │ │ ├── name: :b
+ │ │ └── depth: 0
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: (3,0)-(3,1) = "("
+ │ ├── rparen_loc: (3,8)-(3,9) = ")"
+ │ ├── operator_loc: (3,10)-(3,11) = "="
+ │ └── value:
+ │ @ IntegerNode (location: (3,12)-(3,13))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ MultiWriteNode (location: (4,0)-(4,9))
+ │ ├── lefts: (length: 0)
+ │ ├── rest:
+ │ │ @ SplatNode (location: (4,1)-(4,3))
+ │ │ ├── operator_loc: (4,1)-(4,2) = "*"
+ │ │ └── expression:
+ │ │ @ LocalVariableTargetNode (location: (4,2)-(4,3))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: (4,0)-(4,1) = "("
+ │ ├── rparen_loc: (4,3)-(4,4) = ")"
+ │ ├── operator_loc: (4,5)-(4,6) = "="
+ │ └── value:
+ │ @ ArrayNode (location: (4,7)-(4,9))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 0)
+ │ ├── opening_loc: (4,7)-(4,8) = "["
+ │ └── closing_loc: (4,8)-(4,9) = "]"
+ ├── @ MultiWriteNode (location: (5,0)-(5,15))
+ │ ├── lefts: (length: 0)
+ │ ├── rest:
+ │ │ @ SplatNode (location: (5,1)-(5,5))
+ │ │ ├── operator_loc: (5,1)-(5,2) = "*"
+ │ │ └── expression:
+ │ │ @ LocalVariableTargetNode (location: (5,2)-(5,5))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: (5,0)-(5,1) = "("
+ │ ├── rparen_loc: (5,5)-(5,6) = ")"
+ │ ├── operator_loc: (5,7)-(5,8) = "="
+ │ └── value:
+ │ @ ArrayNode (location: (5,9)-(5,15))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (5,10)-(5,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (5,13)-(5,14))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── opening_loc: (5,9)-(5,10) = "["
+ │ └── closing_loc: (5,14)-(5,15) = "]"
+ ├── @ MultiWriteNode (location: (6,0)-(6,19))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ ClassVariableTargetNode (location: (6,1)-(6,4))
+ │ │ │ └── name: :@@a
+ │ │ └── @ ClassVariableTargetNode (location: (6,6)-(6,9))
+ │ │ └── name: :@@b
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: (6,0)-(6,1) = "("
+ │ ├── rparen_loc: (6,9)-(6,10) = ")"
+ │ ├── operator_loc: (6,11)-(6,12) = "="
+ │ └── value:
+ │ @ ArrayNode (location: (6,13)-(6,19))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (6,14)-(6,15))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (6,17)-(6,18))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── opening_loc: (6,13)-(6,14) = "["
+ │ └── closing_loc: (6,18)-(6,19) = "]"
+ ├── @ MultiWriteNode (location: (7,0)-(7,17))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ InstanceVariableTargetNode (location: (7,1)-(7,3))
+ │ │ │ └── name: :@a
+ │ │ └── @ InstanceVariableTargetNode (location: (7,5)-(7,7))
+ │ │ └── name: :@b
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: (7,0)-(7,1) = "("
+ │ ├── rparen_loc: (7,7)-(7,8) = ")"
+ │ ├── operator_loc: (7,9)-(7,10) = "="
+ │ └── value:
+ │ @ ArrayNode (location: (7,11)-(7,17))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (7,12)-(7,13))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (7,15)-(7,16))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── opening_loc: (7,11)-(7,12) = "["
+ │ └── closing_loc: (7,16)-(7,17) = "]"
+ ├── @ MultiWriteNode (location: (8,0)-(8,25))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ LocalVariableTargetNode (location: (8,1)-(8,2))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ └── @ MultiTargetNode (location: (8,4)-(8,10))
+ │ │ ├── lefts: (length: 2)
+ │ │ │ ├── @ LocalVariableTargetNode (location: (8,5)-(8,6))
+ │ │ │ │ ├── name: :b
+ │ │ │ │ └── depth: 0
+ │ │ │ └── @ LocalVariableTargetNode (location: (8,8)-(8,9))
+ │ │ │ ├── name: :c
+ │ │ │ └── depth: 0
+ │ │ ├── rest: ∅
+ │ │ ├── rights: (length: 0)
+ │ │ ├── lparen_loc: (8,4)-(8,5) = "("
+ │ │ └── rparen_loc: (8,9)-(8,10) = ")"
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: (8,0)-(8,1) = "("
+ │ ├── rparen_loc: (8,10)-(8,11) = ")"
+ │ ├── operator_loc: (8,12)-(8,13) = "="
+ │ └── value:
+ │ @ ArrayNode (location: (8,14)-(8,25))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (8,15)-(8,16))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ ArrayNode (location: (8,18)-(8,24))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 2)
+ │ │ │ ├── @ IntegerNode (location: (8,19)-(8,20))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ └── @ IntegerNode (location: (8,22)-(8,23))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 3
+ │ │ ├── opening_loc: (8,18)-(8,19) = "["
+ │ │ └── closing_loc: (8,23)-(8,24) = "]"
+ │ ├── opening_loc: (8,14)-(8,15) = "["
+ │ └── closing_loc: (8,24)-(8,25) = "]"
+ ├── @ MultiWriteNode (location: (9,0)-(9,15))
+ │ ├── lefts: (length: 1)
+ │ │ └── @ LocalVariableTargetNode (location: (9,1)-(9,2))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── rest:
+ │ │ @ SplatNode (location: (9,4)-(9,5))
+ │ │ ├── operator_loc: (9,4)-(9,5) = "*"
+ │ │ └── expression: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: (9,0)-(9,1) = "("
+ │ ├── rparen_loc: (9,5)-(9,6) = ")"
+ │ ├── operator_loc: (9,7)-(9,8) = "="
+ │ └── value:
+ │ @ ArrayNode (location: (9,9)-(9,15))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (9,10)-(9,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (9,13)-(9,14))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── opening_loc: (9,9)-(9,10) = "["
+ │ └── closing_loc: (9,14)-(9,15) = "]"
+ ├── @ MultiWriteNode (location: (10,0)-(10,18))
+ │ ├── lefts: (length: 1)
+ │ │ └── @ LocalVariableTargetNode (location: (10,1)-(10,2))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── rest:
+ │ │ @ SplatNode (location: (10,4)-(10,8))
+ │ │ ├── operator_loc: (10,4)-(10,5) = "*"
+ │ │ └── expression:
+ │ │ @ LocalVariableTargetNode (location: (10,5)-(10,8))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: (10,0)-(10,1) = "("
+ │ ├── rparen_loc: (10,8)-(10,9) = ")"
+ │ ├── operator_loc: (10,10)-(10,11) = "="
+ │ └── value:
+ │ @ ArrayNode (location: (10,12)-(10,18))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (10,13)-(10,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (10,16)-(10,17))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── opening_loc: (10,12)-(10,13) = "["
+ │ └── closing_loc: (10,17)-(10,18) = "]"
+ ├── @ MultiWriteNode (location: (11,0)-(11,15))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ LocalVariableTargetNode (location: (11,1)-(11,2))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ └── @ LocalVariableTargetNode (location: (11,4)-(11,5))
+ │ │ ├── name: :b
+ │ │ └── depth: 0
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: (11,0)-(11,1) = "("
+ │ ├── rparen_loc: (11,5)-(11,6) = ")"
+ │ ├── operator_loc: (11,7)-(11,8) = "="
+ │ └── value:
+ │ @ ArrayNode (location: (11,9)-(11,15))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (11,10)-(11,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (11,13)-(11,14))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── opening_loc: (11,9)-(11,10) = "["
+ │ └── closing_loc: (11,14)-(11,15) = "]"
+ ├── @ MultiWriteNode (location: (12,0)-(12,12))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ LocalVariableTargetNode (location: (12,1)-(12,2))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ └── @ LocalVariableTargetNode (location: (12,4)-(12,5))
+ │ │ ├── name: :b
+ │ │ └── depth: 0
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: (12,0)-(12,1) = "("
+ │ ├── rparen_loc: (12,5)-(12,6) = ")"
+ │ ├── operator_loc: (12,7)-(12,8) = "="
+ │ └── value:
+ │ @ LocalVariableReadNode (location: (12,9)-(12,12))
+ │ ├── name: :foo
+ │ └── depth: 0
+ ├── @ MultiWriteNode (location: (13,0)-(13,10))
+ │ ├── lefts: (length: 1)
+ │ │ └── @ LocalVariableTargetNode (location: (13,1)-(13,2))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── rest:
+ │ │ @ ImplicitRestNode (location: (13,2)-(13,3))
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: (13,0)-(13,1) = "("
+ │ ├── rparen_loc: (13,3)-(13,4) = ")"
+ │ ├── operator_loc: (13,5)-(13,6) = "="
+ │ └── value:
+ │ @ LocalVariableReadNode (location: (13,7)-(13,10))
+ │ ├── name: :foo
+ │ └── depth: 0
+ ├── @ MultiWriteNode (location: (14,0)-(14,23))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ CallTargetNode (location: (14,1)-(14,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ LocalVariableReadNode (location: (14,1)-(14,2))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── call_operator_loc: (14,2)-(14,3) = "."
+ │ │ │ ├── name: :foo=
+ │ │ │ └── message_loc: (14,3)-(14,6) = "foo"
+ │ │ └── @ CallTargetNode (location: (14,8)-(14,13))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ LocalVariableReadNode (location: (14,8)-(14,9))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ ├── call_operator_loc: (14,9)-(14,10) = "."
+ │ │ ├── name: :bar=
+ │ │ └── message_loc: (14,10)-(14,13) = "bar"
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: (14,0)-(14,1) = "("
+ │ ├── rparen_loc: (14,13)-(14,14) = ")"
+ │ ├── operator_loc: (14,15)-(14,16) = "="
+ │ └── value:
+ │ @ ArrayNode (location: (14,17)-(14,23))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (14,18)-(14,19))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (14,21)-(14,22))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── opening_loc: (14,17)-(14,18) = "["
+ │ └── closing_loc: (14,22)-(14,23) = "]"
+ ├── @ MultiWriteNode (location: (15,0)-(15,24))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ IndexTargetNode (location: (15,1)-(15,8))
+ │ │ │ ├── flags: attribute_write
+ │ │ │ ├── receiver:
+ │ │ │ │ @ LocalVariableReadNode (location: (15,1)-(15,2))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── opening_loc: (15,2)-(15,3) = "["
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (15,3)-(15,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ SplatNode (location: (15,3)-(15,7))
+ │ │ │ │ ├── operator_loc: (15,3)-(15,4) = "*"
+ │ │ │ │ └── expression:
+ │ │ │ │ @ LocalVariableReadNode (location: (15,4)-(15,7))
+ │ │ │ │ ├── name: :foo
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── closing_loc: (15,7)-(15,8) = "]"
+ │ │ │ └── block: ∅
+ │ │ └── @ IndexTargetNode (location: (15,10)-(15,14))
+ │ │ ├── flags: attribute_write
+ │ │ ├── receiver:
+ │ │ │ @ LocalVariableReadNode (location: (15,10)-(15,11))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ ├── opening_loc: (15,11)-(15,12) = "["
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (15,12)-(15,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (15,12)-(15,13))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: (15,13)-(15,14) = "]"
+ │ │ └── block: ∅
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: (15,0)-(15,1) = "("
+ │ ├── rparen_loc: (15,14)-(15,15) = ")"
+ │ ├── operator_loc: (15,16)-(15,17) = "="
+ │ └── value:
+ │ @ ArrayNode (location: (15,18)-(15,24))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (15,19)-(15,20))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (15,22)-(15,23))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── opening_loc: (15,18)-(15,19) = "["
+ │ └── closing_loc: (15,23)-(15,24) = "]"
+ ├── @ MultiWriteNode (location: (16,0)-(16,21))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ IndexTargetNode (location: (16,1)-(16,5))
+ │ │ │ ├── flags: attribute_write
+ │ │ │ ├── receiver:
+ │ │ │ │ @ LocalVariableReadNode (location: (16,1)-(16,2))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── opening_loc: (16,2)-(16,3) = "["
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (16,3)-(16,4))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (16,3)-(16,4))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 0
+ │ │ │ ├── closing_loc: (16,4)-(16,5) = "]"
+ │ │ │ └── block: ∅
+ │ │ └── @ IndexTargetNode (location: (16,7)-(16,11))
+ │ │ ├── flags: attribute_write
+ │ │ ├── receiver:
+ │ │ │ @ LocalVariableReadNode (location: (16,7)-(16,8))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ ├── opening_loc: (16,8)-(16,9) = "["
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (16,9)-(16,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (16,9)-(16,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: (16,10)-(16,11) = "]"
+ │ │ └── block: ∅
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: (16,0)-(16,1) = "("
+ │ ├── rparen_loc: (16,11)-(16,12) = ")"
+ │ ├── operator_loc: (16,13)-(16,14) = "="
+ │ └── value:
+ │ @ ArrayNode (location: (16,15)-(16,21))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (16,16)-(16,17))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (16,19)-(16,20))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── opening_loc: (16,15)-(16,16) = "["
+ │ └── closing_loc: (16,20)-(16,21) = "]"
+ ├── @ MultiWriteNode (location: (17,0)-(17,12))
+ │ ├── lefts: (length: 0)
+ │ ├── rest:
+ │ │ @ SplatNode (location: (17,1)-(17,7))
+ │ │ ├── operator_loc: (17,1)-(17,2) = "*"
+ │ │ └── expression:
+ │ │ @ CallTargetNode (location: (17,2)-(17,7))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ LocalVariableReadNode (location: (17,2)-(17,3))
+ │ │ │ ├── name: :c
+ │ │ │ └── depth: 0
+ │ │ ├── call_operator_loc: (17,3)-(17,4) = "."
+ │ │ ├── name: :foo=
+ │ │ └── message_loc: (17,4)-(17,7) = "foo"
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: (17,0)-(17,1) = "("
+ │ ├── rparen_loc: (17,7)-(17,8) = ")"
+ │ ├── operator_loc: (17,9)-(17,10) = "="
+ │ └── value:
+ │ @ IntegerNode (location: (17,11)-(17,12))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ ConstantPathWriteNode (location: (18,0)-(18,13))
+ │ ├── target:
+ │ │ @ ConstantPathNode (location: (18,0)-(18,5))
+ │ │ ├── parent: ∅
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (18,2)-(18,5))
+ │ │ │ └── name: :Foo
+ │ │ └── delimiter_loc: (18,0)-(18,2) = "::"
+ │ ├── operator_loc: (18,6)-(18,7) = "="
+ │ └── value:
+ │ @ ConstantPathNode (location: (18,8)-(18,13))
+ │ ├── parent: ∅
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (18,10)-(18,13))
+ │ │ └── name: :Bar
+ │ └── delimiter_loc: (18,8)-(18,10) = "::"
+ ├── @ ClassVariableWriteNode (location: (19,0)-(19,7))
+ │ ├── name: :@@a
+ │ ├── name_loc: (19,0)-(19,3) = "@@a"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (19,6)-(19,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (19,4)-(19,5) = "="
+ ├── @ InstanceVariableWriteNode (location: (20,0)-(20,6))
+ │ ├── name: :@a
+ │ ├── name_loc: (20,0)-(20,2) = "@a"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (20,5)-(20,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (20,3)-(20,4) = "="
+ ├── @ ConstantWriteNode (location: (21,0)-(21,9))
+ │ ├── name: :CONST
+ │ ├── name_loc: (21,0)-(21,5) = "CONST"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (21,8)-(21,9))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (21,6)-(21,7) = "="
+ ├── @ ConstantPathWriteNode (location: (22,0)-(22,23))
+ │ ├── target:
+ │ │ @ ConstantPathNode (location: (22,0)-(22,19))
+ │ │ ├── parent:
+ │ │ │ @ ConstantPathNode (location: (22,0)-(22,12))
+ │ │ │ ├── parent:
+ │ │ │ │ @ ConstantReadNode (location: (22,0)-(22,4))
+ │ │ │ │ └── name: :Name
+ │ │ │ ├── child:
+ │ │ │ │ @ ConstantReadNode (location: (22,6)-(22,12))
+ │ │ │ │ └── name: :Spaced
+ │ │ │ └── delimiter_loc: (22,4)-(22,6) = "::"
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (22,14)-(22,19))
+ │ │ │ └── name: :CONST
+ │ │ └── delimiter_loc: (22,12)-(22,14) = "::"
+ │ ├── operator_loc: (22,20)-(22,21) = "="
+ │ └── value:
+ │ @ IntegerNode (location: (22,22)-(22,23))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ LocalVariableWriteNode (location: (23,0)-(23,16))
+ │ ├── name: :a
+ │ ├── depth: 0
+ │ ├── name_loc: (23,0)-(23,1) = "a"
+ │ ├── value:
+ │ │ @ ParenthesesNode (location: (23,4)-(23,16))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (23,5)-(23,15))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ MultiWriteNode (location: (23,5)-(23,15))
+ │ │ │ ├── lefts: (length: 2)
+ │ │ │ │ ├── @ LocalVariableTargetNode (location: (23,6)-(23,7))
+ │ │ │ │ │ ├── name: :b
+ │ │ │ │ │ └── depth: 0
+ │ │ │ │ └── @ LocalVariableTargetNode (location: (23,9)-(23,10))
+ │ │ │ │ ├── name: :c
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: (23,5)-(23,6) = "("
+ │ │ │ ├── rparen_loc: (23,10)-(23,11) = ")"
+ │ │ │ ├── operator_loc: (23,12)-(23,13) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (23,14)-(23,15))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── opening_loc: (23,4)-(23,5) = "("
+ │ │ └── closing_loc: (23,15)-(23,16) = ")"
+ │ └── operator_loc: (23,2)-(23,3) = "="
+ ├── @ LocalVariableWriteNode (location: (24,0)-(24,5))
+ │ ├── name: :a
+ │ ├── depth: 0
+ │ ├── name_loc: (24,0)-(24,1) = "a"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (24,4)-(24,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (24,2)-(24,3) = "="
+ ├── @ LocalVariableWriteNode (location: (25,0)-(25,11))
+ │ ├── name: :foo
+ │ ├── depth: 0
+ │ ├── name_loc: (25,0)-(25,3) = "foo"
+ │ ├── value:
+ │ │ @ CallNode (location: (25,6)-(25,11))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (25,6)-(25,9) = "foo"
+ │ │ ├── opening_loc: (25,9)-(25,10) = "("
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: (25,10)-(25,11) = ")"
+ │ │ └── block: ∅
+ │ └── operator_loc: (25,4)-(25,5) = "="
+ ├── @ CallNode (location: (26,0)-(26,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (26,0)-(26,3))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: (26,3)-(26,4) = "."
+ │ ├── name: :[]=
+ │ ├── message_loc: (26,4)-(26,7) = "[]="
+ │ ├── opening_loc: (26,7)-(26,8) = "("
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (26,8)-(26,9) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (27,0)-(27,13))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (27,0)-(27,3))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: (27,3)-(27,4) = "."
+ │ ├── name: :[]=
+ │ ├── message_loc: (27,4)-(27,7) = "[]="
+ │ ├── opening_loc: (27,7)-(27,8) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (27,8)-(27,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ IntegerNode (location: (27,8)-(27,9))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (27,11)-(27,12))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: (27,12)-(27,13) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (28,0)-(28,11))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (28,0)-(28,3))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: (28,3)-(28,4) = "."
+ │ ├── name: :[]=
+ │ ├── message_loc: (28,4)-(28,7) = "[]="
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (28,7)-(28,11))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ TrueNode (location: (28,7)-(28,11))
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (29,0)-(29,19))
+ │ ├── flags: attribute_write
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (29,0)-(29,3))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]=
+ │ ├── message_loc: (29,3)-(29,11) = "[*index]"
+ │ ├── opening_loc: (29,3)-(29,4) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (29,4)-(29,19))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ SplatNode (location: (29,4)-(29,10))
+ │ │ │ ├── operator_loc: (29,4)-(29,5) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ CallNode (location: (29,5)-(29,10))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :index
+ │ │ │ ├── message_loc: (29,5)-(29,10) = "index"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ CallNode (location: (29,14)-(29,19))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :value
+ │ │ ├── message_loc: (29,14)-(29,19) = "value"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (29,10)-(29,11) = "]"
+ │ └── block: ∅
+ ├── @ CallNode (location: (30,0)-(30,17))
+ │ ├── flags: attribute_write
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (30,0)-(30,3))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]=
+ │ ├── message_loc: (30,3)-(30,9) = "[1..2]"
+ │ ├── opening_loc: (30,3)-(30,4) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (30,4)-(30,17))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ RangeNode (location: (30,4)-(30,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── left:
+ │ │ │ │ @ IntegerNode (location: (30,4)-(30,5))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── right:
+ │ │ │ │ @ IntegerNode (location: (30,7)-(30,8))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ └── operator_loc: (30,5)-(30,7) = ".."
+ │ │ └── @ CallNode (location: (30,12)-(30,17))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :value
+ │ │ ├── message_loc: (30,12)-(30,17) = "value"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (30,8)-(30,9) = "]"
+ │ └── block: ∅
+ ├── @ CallNode (location: (31,0)-(31,9))
+ │ ├── flags: attribute_write
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (31,0)-(31,3))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]=
+ │ ├── message_loc: (31,3)-(31,5) = "[]"
+ │ ├── opening_loc: (31,3)-(31,4) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (31,8)-(31,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (31,8)-(31,9))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: (31,4)-(31,5) = "]"
+ │ └── block: ∅
+ ├── @ CallNode (location: (32,0)-(32,17))
+ │ ├── flags: attribute_write
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (32,0)-(32,3))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]=
+ │ ├── message_loc: (32,3)-(32,9) = "[a, b]"
+ │ ├── opening_loc: (32,3)-(32,4) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (32,4)-(32,17))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 3)
+ │ │ ├── @ LocalVariableReadNode (location: (32,4)-(32,5))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ ├── @ LocalVariableReadNode (location: (32,7)-(32,8))
+ │ │ │ ├── name: :b
+ │ │ │ └── depth: 0
+ │ │ └── @ CallNode (location: (32,12)-(32,17))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :value
+ │ │ ├── message_loc: (32,12)-(32,17) = "value"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (32,8)-(32,9) = "]"
+ │ └── block: ∅
+ ├── @ CallNode (location: (33,0)-(33,18))
+ │ ├── flags: attribute_write
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (33,0)-(33,3))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]=
+ │ ├── message_loc: (33,3)-(33,10) = "[index]"
+ │ ├── opening_loc: (33,3)-(33,4) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (33,4)-(33,18))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (33,4)-(33,9))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :index
+ │ │ │ ├── message_loc: (33,4)-(33,9) = "index"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ CallNode (location: (33,13)-(33,18))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :value
+ │ │ ├── message_loc: (33,13)-(33,18) = "value"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (33,9)-(33,10) = "]"
+ │ └── block: ∅
+ ├── @ LocalVariableWriteNode (location: (34,0)-(34,7))
+ │ ├── name: :x
+ │ ├── depth: 0
+ │ ├── name_loc: (34,0)-(34,1) = "x"
+ │ ├── value:
+ │ │ @ StringNode (location: (34,4)-(34,7))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (34,4)-(34,6) = "%("
+ │ │ ├── content_loc: (34,6)-(34,6) = ""
+ │ │ ├── closing_loc: (34,6)-(34,7) = ")"
+ │ │ └── unescaped: ""
+ │ └── operator_loc: (34,2)-(34,3) = "="
+ ├── @ CallNode (location: (35,0)-(35,7))
+ │ ├── flags: attribute_write
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (35,0)-(35,1))
+ │ │ ├── name: :x
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: (35,1)-(35,2) = "."
+ │ ├── name: :x=
+ │ ├── message_loc: (35,2)-(35,3) = "x"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (35,4)-(35,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (35,4)-(35,7))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (35,4)-(35,6) = "%("
+ │ │ ├── content_loc: (35,6)-(35,6) = ""
+ │ │ ├── closing_loc: (35,6)-(35,7) = ")"
+ │ │ └── unescaped: ""
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (36,0)-(36,12))
+ │ ├── flags: attribute_write
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (36,0)-(36,1))
+ │ │ ├── name: :x
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]=
+ │ ├── message_loc: (36,1)-(36,6) = "[%()]"
+ │ ├── opening_loc: (36,1)-(36,2) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (36,2)-(36,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ StringNode (location: (36,2)-(36,5))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (36,2)-(36,4) = "%("
+ │ │ │ ├── content_loc: (36,4)-(36,4) = ""
+ │ │ │ ├── closing_loc: (36,4)-(36,5) = ")"
+ │ │ │ └── unescaped: ""
+ │ │ └── @ CallNode (location: (36,9)-(36,12))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (36,9)-(36,12) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (36,5)-(36,6) = "]"
+ │ └── block: ∅
+ ├── @ IndexOrWriteNode (location: (37,0)-(37,14))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (37,0)-(37,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── opening_loc: (37,1)-(37,2) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (37,2)-(37,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (37,2)-(37,5))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (37,2)-(37,4) = "%("
+ │ │ ├── content_loc: (37,4)-(37,4) = ""
+ │ │ ├── closing_loc: (37,4)-(37,5) = ")"
+ │ │ └── unescaped: ""
+ │ ├── closing_loc: (37,5)-(37,6) = "]"
+ │ ├── block: ∅
+ │ ├── operator_loc: (37,7)-(37,10) = "||="
+ │ └── value:
+ │ @ CallNode (location: (37,11)-(37,14))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (37,11)-(37,14) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ InstanceVariableOrWriteNode (location: (38,0)-(38,10))
+ │ ├── name: :@a
+ │ ├── name_loc: (38,0)-(38,2) = "@a"
+ │ ├── operator_loc: (38,3)-(38,6) = "||="
+ │ └── value:
+ │ @ StringNode (location: (38,7)-(38,10))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (38,7)-(38,9) = "%("
+ │ ├── content_loc: (38,9)-(38,9) = ""
+ │ ├── closing_loc: (38,9)-(38,10) = ")"
+ │ └── unescaped: ""
+ ├── @ LocalVariableWriteNode (location: (39,0)-(39,14))
+ │ ├── name: :x
+ │ ├── depth: 0
+ │ ├── name_loc: (39,0)-(39,1) = "x"
+ │ ├── value:
+ │ │ @ InterpolatedStringNode (location: (39,4)-(39,14))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (39,4)-(39,14) = "<<-HEREDOC"
+ │ │ ├── parts: (length: 3)
+ │ │ │ ├── @ StringNode (location: (40,0)-(40,2))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (40,0)-(40,2) = " "
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: " "
+ │ │ │ ├── @ EmbeddedStatementsNode (location: (40,2)-(40,5))
+ │ │ │ │ ├── opening_loc: (40,2)-(40,4) = "\#{"
+ │ │ │ │ ├── statements: ∅
+ │ │ │ │ └── closing_loc: (40,4)-(40,5) = "}"
+ │ │ │ └── @ StringNode (location: (40,5)-(41,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (40,5)-(41,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── closing_loc: (41,0)-(42,0) = "HEREDOC\n"
+ │ └── operator_loc: (39,2)-(39,3) = "="
+ ├── @ CallNode (location: (42,0)-(42,14))
+ │ ├── flags: attribute_write
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (42,0)-(42,1))
+ │ │ ├── name: :x
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: (42,1)-(42,2) = "."
+ │ ├── name: :x=
+ │ ├── message_loc: (42,2)-(42,3) = "x"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (42,4)-(42,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (42,4)-(42,14))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (42,4)-(42,14) = "<<-HEREDOC"
+ │ │ ├── parts: (length: 3)
+ │ │ │ ├── @ StringNode (location: (43,0)-(43,2))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (43,0)-(43,2) = " "
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: " "
+ │ │ │ ├── @ EmbeddedStatementsNode (location: (43,2)-(43,5))
+ │ │ │ │ ├── opening_loc: (43,2)-(43,4) = "\#{"
+ │ │ │ │ ├── statements: ∅
+ │ │ │ │ └── closing_loc: (43,4)-(43,5) = "}"
+ │ │ │ └── @ StringNode (location: (43,5)-(44,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (43,5)-(44,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── closing_loc: (44,0)-(45,0) = "HEREDOC\n"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (45,0)-(45,16))
+ │ ├── flags: attribute_write
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (45,0)-(45,1))
+ │ │ ├── name: :x
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]=
+ │ ├── message_loc: (45,1)-(45,3) = "[]"
+ │ ├── opening_loc: (45,1)-(45,2) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (45,6)-(45,16))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (45,6)-(45,16))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (45,6)-(45,16) = "<<-HEREDOC"
+ │ │ ├── parts: (length: 3)
+ │ │ │ ├── @ StringNode (location: (46,0)-(46,2))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (46,0)-(46,2) = " "
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: " "
+ │ │ │ ├── @ EmbeddedStatementsNode (location: (46,2)-(46,5))
+ │ │ │ │ ├── opening_loc: (46,2)-(46,4) = "\#{"
+ │ │ │ │ ├── statements: ∅
+ │ │ │ │ └── closing_loc: (46,4)-(46,5) = "}"
+ │ │ │ └── @ StringNode (location: (46,5)-(47,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (46,5)-(47,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── closing_loc: (47,0)-(48,0) = "HEREDOC\n"
+ │ ├── closing_loc: (45,2)-(45,3) = "]"
+ │ └── block: ∅
+ ├── @ IndexOrWriteNode (location: (48,0)-(48,21))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (48,0)-(48,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── opening_loc: (48,1)-(48,2) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (48,2)-(48,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (48,2)-(48,12))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (48,2)-(48,12) = "<<-HEREDOC"
+ │ │ ├── parts: (length: 3)
+ │ │ │ ├── @ StringNode (location: (49,0)-(49,2))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (49,0)-(49,2) = " "
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: " "
+ │ │ │ ├── @ EmbeddedStatementsNode (location: (49,2)-(49,5))
+ │ │ │ │ ├── opening_loc: (49,2)-(49,4) = "\#{"
+ │ │ │ │ ├── statements: ∅
+ │ │ │ │ └── closing_loc: (49,4)-(49,5) = "}"
+ │ │ │ └── @ StringNode (location: (49,5)-(50,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (49,5)-(50,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── closing_loc: (50,0)-(51,0) = "HEREDOC\n"
+ │ ├── closing_loc: (48,12)-(48,13) = "]"
+ │ ├── block: ∅
+ │ ├── operator_loc: (48,14)-(48,17) = "||="
+ │ └── value:
+ │ @ CallNode (location: (48,18)-(48,21))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (48,18)-(48,21) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ InstanceVariableOrWriteNode (location: (51,0)-(51,17))
+ ├── name: :@a
+ ├── name_loc: (51,0)-(51,2) = "@a"
+ ├── operator_loc: (51,3)-(51,6) = "||="
+ └── value:
+ @ InterpolatedStringNode (location: (51,7)-(51,17))
+ ├── flags: ∅
+ ├── opening_loc: (51,7)-(51,17) = "<<-HEREDOC"
+ ├── parts: (length: 3)
+ │ ├── @ StringNode (location: (52,0)-(52,2))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (52,0)-(52,2) = " "
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: " "
+ │ ├── @ EmbeddedStatementsNode (location: (52,2)-(52,5))
+ │ │ ├── opening_loc: (52,2)-(52,4) = "\#{"
+ │ │ ├── statements: ∅
+ │ │ └── closing_loc: (52,4)-(52,5) = "}"
+ │ └── @ StringNode (location: (52,5)-(53,0))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (52,5)-(53,0) = "\n"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "\n"
+ └── closing_loc: (53,0)-(54,0) = "HEREDOC\n"
diff --git a/test/prism/snapshots/unparser/corpus/literal/block.txt b/test/prism/snapshots/unparser/corpus/literal/block.txt
new file mode 100644
index 0000000000..b4c86d0b04
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/block.txt
@@ -0,0 +1,1402 @@
+@ ProgramNode (location: (1,0)-(96,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(96,1))
+ └── body: (length: 30)
+ ├── @ CallNode (location: (1,0)-(2,1))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,4)-(2,1))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,4)-(1,5) = "{"
+ │ └── closing_loc: (2,0)-(2,1) = "}"
+ ├── @ CallNode (location: (3,0)-(4,1))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (3,0)-(3,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (3,4)-(4,1))
+ │ ├── locals: [:a]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (3,6)-(3,9))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (3,7)-(3,8))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (3,7)-(3,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (3,6)-(3,7) = "|"
+ │ │ └── closing_loc: (3,8)-(3,9) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (3,4)-(3,5) = "{"
+ │ └── closing_loc: (4,0)-(4,1) = "}"
+ ├── @ CallNode (location: (5,0)-(6,1))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (5,0)-(5,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (5,4)-(6,1))
+ │ ├── locals: [:a]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (5,6)-(5,10))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (5,7)-(5,9))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (5,7)-(5,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ ImplicitRestNode (location: (5,8)-(5,9))
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (5,6)-(5,7) = "|"
+ │ │ └── closing_loc: (5,9)-(5,10) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (5,4)-(5,5) = "{"
+ │ └── closing_loc: (6,0)-(6,1) = "}"
+ ├── @ CallNode (location: (7,0)-(8,1))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (7,0)-(7,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (7,4)-(8,1))
+ │ ├── locals: [:a, :x]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (7,6)-(7,13))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (7,7)-(7,9))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (7,7)-(7,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ ImplicitRestNode (location: (7,8)-(7,9))
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 1)
+ │ │ │ └── @ BlockLocalVariableNode (location: (7,11)-(7,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :x
+ │ │ ├── opening_loc: (7,6)-(7,7) = "|"
+ │ │ └── closing_loc: (7,12)-(7,13) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (7,4)-(7,5) = "{"
+ │ └── closing_loc: (8,0)-(8,1) = "}"
+ ├── @ CallNode (location: (9,0)-(10,1))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (9,0)-(9,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (9,4)-(10,1))
+ │ ├── locals: [:a, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (9,6)-(9,12))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (9,7)-(9,11))
+ │ │ │ ├── requireds: (length: 2)
+ │ │ │ │ ├── @ RequiredParameterNode (location: (9,7)-(9,8))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :a
+ │ │ │ │ └── @ RequiredParameterNode (location: (9,10)-(9,11))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :b
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (9,6)-(9,7) = "|"
+ │ │ └── closing_loc: (9,11)-(9,12) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (9,4)-(9,5) = "{"
+ │ └── closing_loc: (10,0)-(10,1) = "}"
+ ├── @ CallNode (location: (11,0)-(13,1))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (11,0)-(11,3) = "foo"
+ │ ├── opening_loc: (11,3)-(11,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (11,4)-(11,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (11,4)-(11,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: (11,5)-(11,6) = ")"
+ │ └── block:
+ │ @ BlockNode (location: (11,7)-(13,1))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (12,2)-(12,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NilNode (location: (12,2)-(12,5))
+ │ ├── opening_loc: (11,7)-(11,8) = "{"
+ │ └── closing_loc: (13,0)-(13,1) = "}"
+ ├── @ CallNode (location: (14,0)-(16,1))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (14,0)-(14,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (14,4)-(16,1))
+ │ ├── locals: [:a, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (14,6)-(14,13))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (14,7)-(14,12))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (14,7)-(14,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ RestParameterNode (location: (14,10)-(14,12))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── name_loc: (14,11)-(14,12) = "b"
+ │ │ │ │ └── operator_loc: (14,10)-(14,11) = "*"
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (14,6)-(14,7) = "|"
+ │ │ └── closing_loc: (14,12)-(14,13) = "|"
+ │ ├── body:
+ │ │ @ StatementsNode (location: (15,2)-(15,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NilNode (location: (15,2)-(15,5))
+ │ ├── opening_loc: (14,4)-(14,5) = "{"
+ │ └── closing_loc: (16,0)-(16,1) = "}"
+ ├── @ CallNode (location: (17,0)-(19,1))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (17,0)-(17,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (17,4)-(19,1))
+ │ ├── locals: [:a]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (17,6)-(17,12))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (17,7)-(17,11))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (17,7)-(17,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ RestParameterNode (location: (17,10)-(17,11))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: ∅
+ │ │ │ │ ├── name_loc: ∅
+ │ │ │ │ └── operator_loc: (17,10)-(17,11) = "*"
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (17,6)-(17,7) = "|"
+ │ │ └── closing_loc: (17,11)-(17,12) = "|"
+ │ ├── body:
+ │ │ @ StatementsNode (location: (18,2)-(18,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NilNode (location: (18,2)-(18,5))
+ │ ├── opening_loc: (17,4)-(17,5) = "{"
+ │ └── closing_loc: (19,0)-(19,1) = "}"
+ ├── @ CallNode (location: (20,0)-(22,1))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (20,0)-(20,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (20,4)-(22,1))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (21,2)-(21,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (21,2)-(21,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (21,2)-(21,5) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (20,4)-(20,5) = "{"
+ │ └── closing_loc: (22,0)-(22,1) = "}"
+ ├── @ CallNode (location: (23,0)-(25,1))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (23,0)-(23,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (23,0)-(23,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (23,3)-(23,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (23,4)-(23,7) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (23,8)-(25,1))
+ │ ├── locals: [:a, :b, :c]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (23,10)-(23,21))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (23,11)-(23,20))
+ │ │ │ ├── requireds: (length: 2)
+ │ │ │ │ ├── @ MultiTargetNode (location: (23,11)-(23,17))
+ │ │ │ │ │ ├── lefts: (length: 2)
+ │ │ │ │ │ │ ├── @ RequiredParameterNode (location: (23,12)-(23,13))
+ │ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ │ └── name: :a
+ │ │ │ │ │ │ └── @ RequiredParameterNode (location: (23,15)-(23,16))
+ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ └── name: :b
+ │ │ │ │ │ ├── rest: ∅
+ │ │ │ │ │ ├── rights: (length: 0)
+ │ │ │ │ │ ├── lparen_loc: (23,11)-(23,12) = "("
+ │ │ │ │ │ └── rparen_loc: (23,16)-(23,17) = ")"
+ │ │ │ │ └── @ RequiredParameterNode (location: (23,19)-(23,20))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :c
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (23,10)-(23,11) = "|"
+ │ │ └── closing_loc: (23,20)-(23,21) = "|"
+ │ ├── body:
+ │ │ @ StatementsNode (location: (24,2)-(24,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (24,2)-(24,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :d
+ │ │ ├── message_loc: (24,2)-(24,3) = "d"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (23,8)-(23,9) = "{"
+ │ └── closing_loc: (25,0)-(25,1) = "}"
+ ├── @ CallNode (location: (26,0)-(27,1))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (26,0)-(26,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (26,0)-(26,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (26,3)-(26,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (26,4)-(26,7) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (26,8)-(27,1))
+ │ ├── locals: [:a, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (26,10)-(26,17))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (26,11)-(26,13))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ RestParameterNode (location: (26,11)-(26,13))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── name_loc: (26,12)-(26,13) = "a"
+ │ │ │ │ └── operator_loc: (26,11)-(26,12) = "*"
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 1)
+ │ │ │ └── @ BlockLocalVariableNode (location: (26,15)-(26,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :b
+ │ │ ├── opening_loc: (26,10)-(26,11) = "|"
+ │ │ └── closing_loc: (26,16)-(26,17) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (26,8)-(26,9) = "{"
+ │ └── closing_loc: (27,0)-(27,1) = "}"
+ ├── @ CallNode (location: (28,0)-(29,1))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (28,0)-(28,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (28,0)-(28,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (28,3)-(28,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (28,4)-(28,7) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (28,8)-(29,1))
+ │ ├── locals: [:a, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (28,10)-(28,16))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (28,11)-(28,12))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (28,11)-(28,12))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 1)
+ │ │ │ └── @ BlockLocalVariableNode (location: (28,14)-(28,15))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :b
+ │ │ ├── opening_loc: (28,10)-(28,11) = "|"
+ │ │ └── closing_loc: (28,15)-(28,16) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (28,8)-(28,9) = "{"
+ │ └── closing_loc: (29,0)-(29,1) = "}"
+ ├── @ CallNode (location: (30,0)-(31,1))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (30,0)-(30,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (30,0)-(30,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (30,3)-(30,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (30,4)-(30,7) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (30,8)-(31,1))
+ │ ├── locals: [:a, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (30,10)-(30,18))
+ │ │ ├── parameters: ∅
+ │ │ ├── locals: (length: 2)
+ │ │ │ ├── @ BlockLocalVariableNode (location: (30,13)-(30,14))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ └── @ BlockLocalVariableNode (location: (30,16)-(30,17))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :b
+ │ │ ├── opening_loc: (30,10)-(30,11) = "|"
+ │ │ └── closing_loc: (30,17)-(30,18) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (30,8)-(30,9) = "{"
+ │ └── closing_loc: (31,0)-(31,1) = "}"
+ ├── @ CallNode (location: (32,0)-(34,1))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (32,0)-(32,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (32,0)-(32,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (32,3)-(32,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (32,4)-(32,7) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (32,8)-(34,1))
+ │ ├── locals: []
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (32,10)-(32,13))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (32,11)-(32,12))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ RestParameterNode (location: (32,11)-(32,12))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: ∅
+ │ │ │ │ ├── name_loc: ∅
+ │ │ │ │ └── operator_loc: (32,11)-(32,12) = "*"
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (32,10)-(32,11) = "|"
+ │ │ └── closing_loc: (32,12)-(32,13) = "|"
+ │ ├── body:
+ │ │ @ StatementsNode (location: (33,2)-(33,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (33,2)-(33,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :d
+ │ │ ├── message_loc: (33,2)-(33,3) = "d"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (32,8)-(32,9) = "{"
+ │ └── closing_loc: (34,0)-(34,1) = "}"
+ ├── @ CallNode (location: (35,0)-(37,1))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (35,0)-(35,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (35,0)-(35,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (35,3)-(35,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (35,4)-(35,7) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (35,8)-(37,1))
+ │ ├── locals: []
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (35,10)-(35,15))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (35,11)-(35,14))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ MultiTargetNode (location: (35,11)-(35,14))
+ │ │ │ │ ├── lefts: (length: 0)
+ │ │ │ │ ├── rest:
+ │ │ │ │ │ @ SplatNode (location: (35,12)-(35,13))
+ │ │ │ │ │ ├── operator_loc: (35,12)-(35,13) = "*"
+ │ │ │ │ │ └── expression: ∅
+ │ │ │ │ ├── rights: (length: 0)
+ │ │ │ │ ├── lparen_loc: (35,11)-(35,12) = "("
+ │ │ │ │ └── rparen_loc: (35,13)-(35,14) = ")"
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (35,10)-(35,11) = "|"
+ │ │ └── closing_loc: (35,14)-(35,15) = "|"
+ │ ├── body:
+ │ │ @ StatementsNode (location: (36,2)-(36,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (36,2)-(36,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :d
+ │ │ ├── message_loc: (36,2)-(36,3) = "d"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (35,8)-(35,9) = "{"
+ │ └── closing_loc: (37,0)-(37,1) = "}"
+ ├── @ CallNode (location: (38,0)-(40,1))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (38,0)-(38,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (38,0)-(38,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (38,3)-(38,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (38,4)-(38,7) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (38,8)-(40,1))
+ │ ├── locals: []
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (38,10)-(38,17))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (38,11)-(38,16))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ MultiTargetNode (location: (38,11)-(38,16))
+ │ │ │ │ ├── lefts: (length: 1)
+ │ │ │ │ │ └── @ MultiTargetNode (location: (38,12)-(38,15))
+ │ │ │ │ │ ├── lefts: (length: 0)
+ │ │ │ │ │ ├── rest:
+ │ │ │ │ │ │ @ SplatNode (location: (38,13)-(38,14))
+ │ │ │ │ │ │ ├── operator_loc: (38,13)-(38,14) = "*"
+ │ │ │ │ │ │ └── expression: ∅
+ │ │ │ │ │ ├── rights: (length: 0)
+ │ │ │ │ │ ├── lparen_loc: (38,12)-(38,13) = "("
+ │ │ │ │ │ └── rparen_loc: (38,14)-(38,15) = ")"
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── rights: (length: 0)
+ │ │ │ │ ├── lparen_loc: (38,11)-(38,12) = "("
+ │ │ │ │ └── rparen_loc: (38,15)-(38,16) = ")"
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (38,10)-(38,11) = "|"
+ │ │ └── closing_loc: (38,16)-(38,17) = "|"
+ │ ├── body:
+ │ │ @ StatementsNode (location: (39,2)-(39,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (39,2)-(39,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :d
+ │ │ ├── message_loc: (39,2)-(39,3) = "d"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (38,8)-(38,9) = "{"
+ │ └── closing_loc: (40,0)-(40,1) = "}"
+ ├── @ CallNode (location: (41,0)-(43,1))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (41,0)-(41,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (41,0)-(41,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (41,3)-(41,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (41,4)-(41,7) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (41,8)-(43,1))
+ │ ├── locals: [:a]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (41,10)-(41,20))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (41,11)-(41,19))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ MultiTargetNode (location: (41,11)-(41,19))
+ │ │ │ │ ├── lefts: (length: 2)
+ │ │ │ │ │ ├── @ RequiredParameterNode (location: (41,12)-(41,13))
+ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ └── name: :a
+ │ │ │ │ │ └── @ MultiTargetNode (location: (41,15)-(41,18))
+ │ │ │ │ │ ├── lefts: (length: 0)
+ │ │ │ │ │ ├── rest:
+ │ │ │ │ │ │ @ SplatNode (location: (41,16)-(41,17))
+ │ │ │ │ │ │ ├── operator_loc: (41,16)-(41,17) = "*"
+ │ │ │ │ │ │ └── expression: ∅
+ │ │ │ │ │ ├── rights: (length: 0)
+ │ │ │ │ │ ├── lparen_loc: (41,15)-(41,16) = "("
+ │ │ │ │ │ └── rparen_loc: (41,17)-(41,18) = ")"
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── rights: (length: 0)
+ │ │ │ │ ├── lparen_loc: (41,11)-(41,12) = "("
+ │ │ │ │ └── rparen_loc: (41,18)-(41,19) = ")"
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (41,10)-(41,11) = "|"
+ │ │ └── closing_loc: (41,19)-(41,20) = "|"
+ │ ├── body:
+ │ │ @ StatementsNode (location: (42,2)-(42,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (42,2)-(42,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :d
+ │ │ ├── message_loc: (42,2)-(42,3) = "d"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (41,8)-(41,9) = "{"
+ │ └── closing_loc: (43,0)-(43,1) = "}"
+ ├── @ CallNode (location: (44,0)-(46,1))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (44,0)-(44,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (44,0)-(44,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (44,3)-(44,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (44,4)-(44,7) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (44,8)-(46,1))
+ │ ├── locals: [:a, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (44,10)-(44,18))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (44,11)-(44,17))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ MultiTargetNode (location: (44,11)-(44,17))
+ │ │ │ │ ├── lefts: (length: 2)
+ │ │ │ │ │ ├── @ RequiredParameterNode (location: (44,12)-(44,13))
+ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ └── name: :a
+ │ │ │ │ │ └── @ RequiredParameterNode (location: (44,15)-(44,16))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :b
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── rights: (length: 0)
+ │ │ │ │ ├── lparen_loc: (44,11)-(44,12) = "("
+ │ │ │ │ └── rparen_loc: (44,16)-(44,17) = ")"
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (44,10)-(44,11) = "|"
+ │ │ └── closing_loc: (44,17)-(44,18) = "|"
+ │ ├── body:
+ │ │ @ StatementsNode (location: (45,2)-(45,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (45,2)-(45,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :d
+ │ │ ├── message_loc: (45,2)-(45,3) = "d"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (44,8)-(44,9) = "{"
+ │ └── closing_loc: (46,0)-(46,1) = "}"
+ ├── @ CallNode (location: (47,0)-(48,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (47,0)-(48,1))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (47,0)-(47,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (47,0)-(47,3) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (47,3)-(47,4) = "."
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (47,4)-(47,7) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (47,8)-(48,1))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (47,8)-(47,9) = "{"
+ │ │ └── closing_loc: (48,0)-(48,1) = "}"
+ │ ├── call_operator_loc: (48,1)-(48,2) = "."
+ │ ├── name: :baz
+ │ ├── message_loc: (48,2)-(48,5) = "baz"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (49,0)-(51,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (49,0)-(49,1) = "m"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (49,2)-(51,3))
+ │ ├── locals: [:e]
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (49,2)-(51,3))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── rescue_clause:
+ │ │ │ @ RescueNode (location: (50,0)-(50,21))
+ │ │ │ ├── keyword_loc: (50,0)-(50,6) = "rescue"
+ │ │ │ ├── exceptions: (length: 1)
+ │ │ │ │ └── @ ConstantReadNode (location: (50,7)-(50,16))
+ │ │ │ │ └── name: :Exception
+ │ │ │ ├── operator_loc: (50,17)-(50,19) = "=>"
+ │ │ │ ├── reference:
+ │ │ │ │ @ LocalVariableTargetNode (location: (50,20)-(50,21))
+ │ │ │ │ ├── name: :e
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── statements: ∅
+ │ │ │ └── consequent: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause: ∅
+ │ │ └── end_keyword_loc: (51,0)-(51,3) = "end"
+ │ ├── opening_loc: (49,2)-(49,4) = "do"
+ │ └── closing_loc: (51,0)-(51,3) = "end"
+ ├── @ CallNode (location: (52,0)-(56,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (52,0)-(52,1) = "m"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (52,2)-(56,3))
+ │ ├── locals: [:bar]
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (52,2)-(56,3))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (53,2)-(53,5))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (53,2)-(53,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (53,2)-(53,5) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── rescue_clause:
+ │ │ │ @ RescueNode (location: (54,0)-(55,5))
+ │ │ │ ├── keyword_loc: (54,0)-(54,6) = "rescue"
+ │ │ │ ├── exceptions: (length: 1)
+ │ │ │ │ └── @ ConstantReadNode (location: (54,7)-(54,16))
+ │ │ │ │ └── name: :Exception
+ │ │ │ ├── operator_loc: (54,17)-(54,19) = "=>"
+ │ │ │ ├── reference:
+ │ │ │ │ @ LocalVariableTargetNode (location: (54,20)-(54,23))
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (55,2)-(55,5))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ LocalVariableReadNode (location: (55,2)-(55,5))
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ └── depth: 0
+ │ │ │ └── consequent: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause: ∅
+ │ │ └── end_keyword_loc: (56,0)-(56,3) = "end"
+ │ ├── opening_loc: (52,2)-(52,4) = "do"
+ │ └── closing_loc: (56,0)-(56,3) = "end"
+ ├── @ CallNode (location: (57,0)-(61,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (57,0)-(57,1) = "m"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (57,2)-(61,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (57,2)-(61,3))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (58,2)-(58,5))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (58,2)-(58,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (58,2)-(58,5) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── rescue_clause:
+ │ │ │ @ RescueNode (location: (59,0)-(60,5))
+ │ │ │ ├── keyword_loc: (59,0)-(59,6) = "rescue"
+ │ │ │ ├── exceptions: (length: 2)
+ │ │ │ │ ├── @ ConstantReadNode (location: (59,7)-(59,16))
+ │ │ │ │ │ └── name: :SomeError
+ │ │ │ │ └── @ SplatNode (location: (59,18)-(59,22))
+ │ │ │ │ ├── operator_loc: (59,18)-(59,19) = "*"
+ │ │ │ │ └── expression:
+ │ │ │ │ @ CallNode (location: (59,19)-(59,22))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (59,19)-(59,22) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── operator_loc: ∅
+ │ │ │ ├── reference: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (60,2)-(60,5))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (60,2)-(60,5))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ ├── message_loc: (60,2)-(60,5) = "baz"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── consequent: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause: ∅
+ │ │ └── end_keyword_loc: (61,0)-(61,3) = "end"
+ │ ├── opening_loc: (57,2)-(57,4) = "do"
+ │ └── closing_loc: (61,0)-(61,3) = "end"
+ ├── @ CallNode (location: (62,0)-(66,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (62,0)-(62,1) = "m"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (62,2)-(66,3))
+ │ ├── locals: [:exception]
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (62,2)-(66,3))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (63,2)-(63,5))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (63,2)-(63,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (63,2)-(63,5) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── rescue_clause:
+ │ │ │ @ RescueNode (location: (64,0)-(65,5))
+ │ │ │ ├── keyword_loc: (64,0)-(64,6) = "rescue"
+ │ │ │ ├── exceptions: (length: 2)
+ │ │ │ │ ├── @ ConstantReadNode (location: (64,7)-(64,16))
+ │ │ │ │ │ └── name: :SomeError
+ │ │ │ │ └── @ SplatNode (location: (64,18)-(64,22))
+ │ │ │ │ ├── operator_loc: (64,18)-(64,19) = "*"
+ │ │ │ │ └── expression:
+ │ │ │ │ @ CallNode (location: (64,19)-(64,22))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (64,19)-(64,22) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── operator_loc: (64,23)-(64,25) = "=>"
+ │ │ │ ├── reference:
+ │ │ │ │ @ LocalVariableTargetNode (location: (64,26)-(64,35))
+ │ │ │ │ ├── name: :exception
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (65,2)-(65,5))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (65,2)-(65,5))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ ├── message_loc: (65,2)-(65,5) = "baz"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── consequent: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause: ∅
+ │ │ └── end_keyword_loc: (66,0)-(66,3) = "end"
+ │ ├── opening_loc: (62,2)-(62,4) = "do"
+ │ └── closing_loc: (66,0)-(66,3) = "end"
+ ├── @ CallNode (location: (67,0)-(71,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (67,0)-(67,1) = "m"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (67,2)-(71,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (67,2)-(71,3))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (68,2)-(68,5))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (68,2)-(68,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (68,2)-(68,5) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── rescue_clause:
+ │ │ │ @ RescueNode (location: (69,0)-(70,5))
+ │ │ │ ├── keyword_loc: (69,0)-(69,6) = "rescue"
+ │ │ │ ├── exceptions: (length: 1)
+ │ │ │ │ └── @ SplatNode (location: (69,7)-(69,11))
+ │ │ │ │ ├── operator_loc: (69,7)-(69,8) = "*"
+ │ │ │ │ └── expression:
+ │ │ │ │ @ CallNode (location: (69,8)-(69,11))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (69,8)-(69,11) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── operator_loc: ∅
+ │ │ │ ├── reference: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (70,2)-(70,5))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (70,2)-(70,5))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ ├── message_loc: (70,2)-(70,5) = "baz"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── consequent: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause: ∅
+ │ │ └── end_keyword_loc: (71,0)-(71,3) = "end"
+ │ ├── opening_loc: (67,2)-(67,4) = "do"
+ │ └── closing_loc: (71,0)-(71,3) = "end"
+ ├── @ CallNode (location: (72,0)-(75,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (72,0)-(72,1) = "m"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (72,2)-(75,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (72,2)-(75,3))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (73,2)-(73,5))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (73,2)-(73,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (73,2)-(73,5) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── rescue_clause:
+ │ │ │ @ RescueNode (location: (74,0)-(74,16))
+ │ │ │ ├── keyword_loc: (74,0)-(74,6) = "rescue"
+ │ │ │ ├── exceptions: (length: 1)
+ │ │ │ │ └── @ ConstantReadNode (location: (74,7)-(74,16))
+ │ │ │ │ └── name: :LoadError
+ │ │ │ ├── operator_loc: ∅
+ │ │ │ ├── reference: ∅
+ │ │ │ ├── statements: ∅
+ │ │ │ └── consequent: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause: ∅
+ │ │ └── end_keyword_loc: (75,0)-(75,3) = "end"
+ │ ├── opening_loc: (72,2)-(72,4) = "do"
+ │ └── closing_loc: (75,0)-(75,3) = "end"
+ ├── @ CallNode (location: (76,0)-(81,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (76,0)-(76,1) = "m"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (76,2)-(81,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (76,2)-(81,3))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (77,2)-(77,5))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (77,2)-(77,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (77,2)-(77,5) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── rescue_clause:
+ │ │ │ @ RescueNode (location: (78,0)-(78,6))
+ │ │ │ ├── keyword_loc: (78,0)-(78,6) = "rescue"
+ │ │ │ ├── exceptions: (length: 0)
+ │ │ │ ├── operator_loc: ∅
+ │ │ │ ├── reference: ∅
+ │ │ │ ├── statements: ∅
+ │ │ │ └── consequent: ∅
+ │ │ ├── else_clause:
+ │ │ │ @ ElseNode (location: (79,0)-(81,3))
+ │ │ │ ├── else_keyword_loc: (79,0)-(79,4) = "else"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (80,2)-(80,5))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (80,2)-(80,5))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ ├── message_loc: (80,2)-(80,5) = "baz"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── end_keyword_loc: (81,0)-(81,3) = "end"
+ │ │ ├── ensure_clause: ∅
+ │ │ └── end_keyword_loc: (81,0)-(81,3) = "end"
+ │ ├── opening_loc: (76,2)-(76,4) = "do"
+ │ └── closing_loc: (81,0)-(81,3) = "end"
+ ├── @ CallNode (location: (82,0)-(86,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (82,0)-(82,1) = "m"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (82,2)-(86,3))
+ │ ├── locals: [:exception]
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (82,2)-(86,3))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (83,2)-(83,5))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (83,2)-(83,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (83,2)-(83,5) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── rescue_clause:
+ │ │ │ @ RescueNode (location: (84,0)-(85,5))
+ │ │ │ ├── keyword_loc: (84,0)-(84,6) = "rescue"
+ │ │ │ ├── exceptions: (length: 1)
+ │ │ │ │ └── @ SplatNode (location: (84,7)-(84,11))
+ │ │ │ │ ├── operator_loc: (84,7)-(84,8) = "*"
+ │ │ │ │ └── expression:
+ │ │ │ │ @ CallNode (location: (84,8)-(84,11))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (84,8)-(84,11) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── operator_loc: (84,12)-(84,14) = "=>"
+ │ │ │ ├── reference:
+ │ │ │ │ @ LocalVariableTargetNode (location: (84,15)-(84,24))
+ │ │ │ │ ├── name: :exception
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (85,2)-(85,5))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (85,2)-(85,5))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ ├── message_loc: (85,2)-(85,5) = "baz"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── consequent: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause: ∅
+ │ │ └── end_keyword_loc: (86,0)-(86,3) = "end"
+ │ ├── opening_loc: (82,2)-(82,4) = "do"
+ │ └── closing_loc: (86,0)-(86,3) = "end"
+ ├── @ CallNode (location: (87,0)-(89,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (87,0)-(87,1) = "m"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (87,2)-(89,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (87,2)-(89,3))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── rescue_clause: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause:
+ │ │ │ @ EnsureNode (location: (88,0)-(89,3))
+ │ │ │ ├── ensure_keyword_loc: (88,0)-(88,6) = "ensure"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── end_keyword_loc: (89,0)-(89,3) = "end"
+ │ │ └── end_keyword_loc: (89,0)-(89,3) = "end"
+ │ ├── opening_loc: (87,2)-(87,4) = "do"
+ │ └── closing_loc: (89,0)-(89,3) = "end"
+ ├── @ CallNode (location: (90,0)-(93,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (90,0)-(90,1) = "m"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (90,2)-(93,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (90,2)-(93,3))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── rescue_clause:
+ │ │ │ @ RescueNode (location: (91,0)-(91,6))
+ │ │ │ ├── keyword_loc: (91,0)-(91,6) = "rescue"
+ │ │ │ ├── exceptions: (length: 0)
+ │ │ │ ├── operator_loc: ∅
+ │ │ │ ├── reference: ∅
+ │ │ │ ├── statements: ∅
+ │ │ │ └── consequent: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause:
+ │ │ │ @ EnsureNode (location: (92,0)-(93,3))
+ │ │ │ ├── ensure_keyword_loc: (92,0)-(92,6) = "ensure"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── end_keyword_loc: (93,0)-(93,3) = "end"
+ │ │ └── end_keyword_loc: (93,0)-(93,3) = "end"
+ │ ├── opening_loc: (90,2)-(90,4) = "do"
+ │ └── closing_loc: (93,0)-(93,3) = "end"
+ └── @ CallNode (location: (94,0)-(96,1))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :bar
+ ├── message_loc: (94,0)-(94,3) = "bar"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (94,4)-(96,1))
+ ├── locals: [:_1, :_2]
+ ├── parameters:
+ │ @ NumberedParametersNode (location: (94,4)-(96,1))
+ │ └── maximum: 2
+ ├── body:
+ │ @ StatementsNode (location: (95,2)-(95,9))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (95,2)-(95,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (95,2)-(95,4))
+ │ │ ├── name: :_1
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (95,5)-(95,6) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (95,7)-(95,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (95,7)-(95,9))
+ │ │ ├── name: :_2
+ │ │ └── depth: 0
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── opening_loc: (94,4)-(94,5) = "{"
+ └── closing_loc: (96,0)-(96,1) = "}"
diff --git a/test/prism/snapshots/unparser/corpus/literal/case.txt b/test/prism/snapshots/unparser/corpus/literal/case.txt
new file mode 100644
index 0000000000..509caa55c8
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/case.txt
@@ -0,0 +1,446 @@
+@ ProgramNode (location: (1,0)-(37,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(37,3))
+ └── body: (length: 8)
+ ├── @ CaseNode (location: (1,0)-(6,3))
+ │ ├── predicate: ∅
+ │ ├── conditions: (length: 2)
+ │ │ ├── @ WhenNode (location: (2,0)-(3,5))
+ │ │ │ ├── keyword_loc: (2,0)-(2,4) = "when"
+ │ │ │ ├── conditions: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (2,5)-(2,8))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (2,5)-(2,8) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ └── statements:
+ │ │ │ @ StatementsNode (location: (3,2)-(3,5))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (3,2)-(3,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (3,2)-(3,5) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ WhenNode (location: (4,0)-(5,5))
+ │ │ ├── keyword_loc: (4,0)-(4,4) = "when"
+ │ │ ├── conditions: (length: 1)
+ │ │ │ └── @ CallNode (location: (4,5)-(4,8))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (4,5)-(4,8) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── then_keyword_loc: ∅
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (5,2)-(5,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (5,2)-(5,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (5,2)-(5,5) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ │ └── end_keyword_loc: (6,0)-(6,3) = "end"
+ ├── @ CaseNode (location: (7,0)-(11,3))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (7,5)-(7,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (7,5)-(7,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 2)
+ │ │ ├── @ WhenNode (location: (8,0)-(8,8))
+ │ │ │ ├── keyword_loc: (8,0)-(8,4) = "when"
+ │ │ │ ├── conditions: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (8,5)-(8,8))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (8,5)-(8,8) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ └── statements: ∅
+ │ │ └── @ WhenNode (location: (9,0)-(10,5))
+ │ │ ├── keyword_loc: (9,0)-(9,4) = "when"
+ │ │ ├── conditions: (length: 1)
+ │ │ │ └── @ CallNode (location: (9,5)-(9,8))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (9,5)-(9,8) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── then_keyword_loc: ∅
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (10,2)-(10,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (10,2)-(10,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (10,2)-(10,5) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (7,0)-(7,4) = "case"
+ │ └── end_keyword_loc: (11,0)-(11,3) = "end"
+ ├── @ CaseNode (location: (12,0)-(17,3))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (12,5)-(12,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (12,5)-(12,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 2)
+ │ │ ├── @ WhenNode (location: (13,0)-(14,5))
+ │ │ │ ├── keyword_loc: (13,0)-(13,4) = "when"
+ │ │ │ ├── conditions: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (13,5)-(13,8))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (13,5)-(13,8) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ └── statements:
+ │ │ │ @ StatementsNode (location: (14,2)-(14,5))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (14,2)-(14,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (14,2)-(14,5) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ WhenNode (location: (15,0)-(16,5))
+ │ │ ├── keyword_loc: (15,0)-(15,4) = "when"
+ │ │ ├── conditions: (length: 1)
+ │ │ │ └── @ CallNode (location: (15,5)-(15,8))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (15,5)-(15,8) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── then_keyword_loc: ∅
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (16,2)-(16,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (16,2)-(16,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (16,2)-(16,5) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (12,0)-(12,4) = "case"
+ │ └── end_keyword_loc: (17,0)-(17,3) = "end"
+ ├── @ CaseNode (location: (18,0)-(21,3))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (18,5)-(18,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (18,5)-(18,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ WhenNode (location: (19,0)-(20,8))
+ │ │ ├── keyword_loc: (19,0)-(19,4) = "when"
+ │ │ ├── conditions: (length: 2)
+ │ │ │ ├── @ CallNode (location: (19,5)-(19,8))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (19,5)-(19,8) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── @ CallNode (location: (19,10)-(19,13))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (19,10)-(19,13) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── then_keyword_loc: ∅
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (20,2)-(20,8))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SymbolNode (location: (20,2)-(20,8))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (20,2)-(20,3) = ":"
+ │ │ ├── value_loc: (20,3)-(20,8) = "other"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "other"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (18,0)-(18,4) = "case"
+ │ └── end_keyword_loc: (21,0)-(21,3) = "end"
+ ├── @ CaseNode (location: (22,0)-(25,3))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (22,5)-(22,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (22,5)-(22,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ WhenNode (location: (23,0)-(24,8))
+ │ │ ├── keyword_loc: (23,0)-(23,4) = "when"
+ │ │ ├── conditions: (length: 1)
+ │ │ │ └── @ SplatNode (location: (23,5)-(23,9))
+ │ │ │ ├── operator_loc: (23,5)-(23,6) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ CallNode (location: (23,6)-(23,9))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (23,6)-(23,9) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── then_keyword_loc: ∅
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (24,2)-(24,8))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SymbolNode (location: (24,2)-(24,8))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (24,2)-(24,3) = ":"
+ │ │ ├── value_loc: (24,3)-(24,8) = "value"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "value"
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (22,0)-(22,4) = "case"
+ │ └── end_keyword_loc: (25,0)-(25,3) = "end"
+ ├── @ CaseNode (location: (26,0)-(31,3))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (26,5)-(26,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (26,5)-(26,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ WhenNode (location: (27,0)-(28,5))
+ │ │ ├── keyword_loc: (27,0)-(27,4) = "when"
+ │ │ ├── conditions: (length: 1)
+ │ │ │ └── @ CallNode (location: (27,5)-(27,8))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (27,5)-(27,8) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── then_keyword_loc: ∅
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (28,2)-(28,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (28,2)-(28,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (28,2)-(28,5) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (29,0)-(31,3))
+ │ │ ├── else_keyword_loc: (29,0)-(29,4) = "else"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (30,2)-(30,6))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (30,2)-(30,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (30,2)-(30,3) = ":"
+ │ │ │ ├── value_loc: (30,3)-(30,6) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ └── end_keyword_loc: (31,0)-(31,3) = "end"
+ │ ├── case_keyword_loc: (26,0)-(26,4) = "case"
+ │ └── end_keyword_loc: (31,0)-(31,3) = "end"
+ ├── @ CaseNode (location: (32,0)-(34,3))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (32,5)-(32,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (32,5)-(32,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ WhenNode (location: (33,0)-(33,15))
+ │ │ ├── keyword_loc: (33,0)-(33,4) = "when"
+ │ │ ├── conditions: (length: 1)
+ │ │ │ └── @ SplatNode (location: (33,5)-(33,15))
+ │ │ │ ├── operator_loc: (33,5)-(33,6) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ CallNode (location: (33,6)-(33,15))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ CallNode (location: (33,6)-(33,9))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (33,6)-(33,9) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :|
+ │ │ │ ├── message_loc: (33,10)-(33,11) = "|"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (33,12)-(33,15))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (33,12)-(33,15))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ ├── message_loc: (33,12)-(33,15) = "baz"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── then_keyword_loc: ∅
+ │ │ └── statements: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (32,0)-(32,4) = "case"
+ │ └── end_keyword_loc: (34,0)-(34,3) = "end"
+ └── @ CaseNode (location: (35,0)-(37,3))
+ ├── predicate:
+ │ @ CallNode (location: (35,5)-(35,8))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (35,5)-(35,8) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── conditions: (length: 1)
+ │ └── @ WhenNode (location: (36,0)-(36,15))
+ │ ├── keyword_loc: (36,0)-(36,4) = "when"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ SplatNode (location: (36,5)-(36,15))
+ │ │ ├── operator_loc: (36,5)-(36,6) = "*"
+ │ │ └── expression:
+ │ │ @ CallNode (location: (36,6)-(36,15))
+ │ │ ├── flags: attribute_write
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (36,6)-(36,9))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (36,6)-(36,9) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (36,9)-(36,10) = "."
+ │ │ ├── name: :baz=
+ │ │ ├── message_loc: (36,10)-(36,13) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (36,14)-(36,15))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (36,14)-(36,15))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: ∅
+ │ └── statements: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (35,0)-(35,4) = "case"
+ └── end_keyword_loc: (37,0)-(37,3) = "end"
diff --git a/test/prism/snapshots/unparser/corpus/literal/class.txt b/test/prism/snapshots/unparser/corpus/literal/class.txt
new file mode 100644
index 0000000000..34eb03edb3
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/class.txt
@@ -0,0 +1,233 @@
+@ ProgramNode (location: (1,0)-(35,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(35,3))
+ └── body: (length: 10)
+ ├── @ ClassNode (location: (1,0)-(2,3))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (1,0)-(1,5) = "class"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (1,6)-(1,7))
+ │ │ └── name: :A
+ │ ├── inheritance_operator_loc: ∅
+ │ ├── superclass: ∅
+ │ ├── body: ∅
+ │ ├── end_keyword_loc: (2,0)-(2,3) = "end"
+ │ └── name: :A
+ ├── @ SingletonClassNode (location: (4,0)-(5,3))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (4,0)-(4,5) = "class"
+ │ ├── operator_loc: (4,6)-(4,8) = "<<"
+ │ ├── expression:
+ │ │ @ CallNode (location: (4,9)-(4,10))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (4,9)-(4,10) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ └── end_keyword_loc: (5,0)-(5,3) = "end"
+ ├── @ SingletonClassNode (location: (7,0)-(9,3))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (7,0)-(7,5) = "class"
+ │ ├── operator_loc: (7,6)-(7,8) = "<<"
+ │ ├── expression:
+ │ │ @ CallNode (location: (7,9)-(7,10))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (7,9)-(7,10) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (8,2)-(8,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (8,2)-(8,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (8,2)-(8,3) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── end_keyword_loc: (9,0)-(9,3) = "end"
+ ├── @ ClassNode (location: (11,0)-(12,3))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (11,0)-(11,5) = "class"
+ │ ├── constant_path:
+ │ │ @ ConstantPathNode (location: (11,6)-(11,10))
+ │ │ ├── parent:
+ │ │ │ @ ConstantReadNode (location: (11,6)-(11,7))
+ │ │ │ └── name: :A
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (11,9)-(11,10))
+ │ │ │ └── name: :B
+ │ │ └── delimiter_loc: (11,7)-(11,9) = "::"
+ │ ├── inheritance_operator_loc: ∅
+ │ ├── superclass: ∅
+ │ ├── body: ∅
+ │ ├── end_keyword_loc: (12,0)-(12,3) = "end"
+ │ └── name: :B
+ ├── @ ClassNode (location: (14,0)-(15,3))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (14,0)-(14,5) = "class"
+ │ ├── constant_path:
+ │ │ @ ConstantPathNode (location: (14,6)-(14,13))
+ │ │ ├── parent:
+ │ │ │ @ ConstantPathNode (location: (14,6)-(14,10))
+ │ │ │ ├── parent:
+ │ │ │ │ @ ConstantReadNode (location: (14,6)-(14,7))
+ │ │ │ │ └── name: :A
+ │ │ │ ├── child:
+ │ │ │ │ @ ConstantReadNode (location: (14,9)-(14,10))
+ │ │ │ │ └── name: :B
+ │ │ │ └── delimiter_loc: (14,7)-(14,9) = "::"
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (14,12)-(14,13))
+ │ │ │ └── name: :C
+ │ │ └── delimiter_loc: (14,10)-(14,12) = "::"
+ │ ├── inheritance_operator_loc: ∅
+ │ ├── superclass: ∅
+ │ ├── body: ∅
+ │ ├── end_keyword_loc: (15,0)-(15,3) = "end"
+ │ └── name: :C
+ ├── @ ClassNode (location: (17,0)-(18,3))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (17,0)-(17,5) = "class"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (17,6)-(17,7))
+ │ │ └── name: :A
+ │ ├── inheritance_operator_loc: (17,8)-(17,9) = "<"
+ │ ├── superclass:
+ │ │ @ ConstantReadNode (location: (17,10)-(17,11))
+ │ │ └── name: :B
+ │ ├── body: ∅
+ │ ├── end_keyword_loc: (18,0)-(18,3) = "end"
+ │ └── name: :A
+ ├── @ ClassNode (location: (20,0)-(21,3))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (20,0)-(20,5) = "class"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (20,6)-(20,7))
+ │ │ └── name: :A
+ │ ├── inheritance_operator_loc: (20,8)-(20,9) = "<"
+ │ ├── superclass:
+ │ │ @ ConstantPathNode (location: (20,10)-(20,14))
+ │ │ ├── parent:
+ │ │ │ @ ConstantReadNode (location: (20,10)-(20,11))
+ │ │ │ └── name: :B
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (20,13)-(20,14))
+ │ │ │ └── name: :C
+ │ │ └── delimiter_loc: (20,11)-(20,13) = "::"
+ │ ├── body: ∅
+ │ ├── end_keyword_loc: (21,0)-(21,3) = "end"
+ │ └── name: :A
+ ├── @ ClassNode (location: (23,0)-(24,3))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (23,0)-(23,5) = "class"
+ │ ├── constant_path:
+ │ │ @ ConstantPathNode (location: (23,6)-(23,10))
+ │ │ ├── parent:
+ │ │ │ @ ConstantReadNode (location: (23,6)-(23,7))
+ │ │ │ └── name: :A
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (23,9)-(23,10))
+ │ │ │ └── name: :B
+ │ │ └── delimiter_loc: (23,7)-(23,9) = "::"
+ │ ├── inheritance_operator_loc: (23,11)-(23,12) = "<"
+ │ ├── superclass:
+ │ │ @ ConstantPathNode (location: (23,13)-(23,17))
+ │ │ ├── parent:
+ │ │ │ @ ConstantReadNode (location: (23,13)-(23,14))
+ │ │ │ └── name: :C
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (23,16)-(23,17))
+ │ │ │ └── name: :D
+ │ │ └── delimiter_loc: (23,14)-(23,16) = "::"
+ │ ├── body: ∅
+ │ ├── end_keyword_loc: (24,0)-(24,3) = "end"
+ │ └── name: :B
+ ├── @ ClassNode (location: (26,0)-(32,3))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (26,0)-(26,5) = "class"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (26,6)-(26,7))
+ │ │ └── name: :A
+ │ ├── inheritance_operator_loc: ∅
+ │ ├── superclass: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (27,2)-(31,5))
+ │ │ └── body: (length: 2)
+ │ │ ├── @ CallNode (location: (27,2)-(27,16))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :include
+ │ │ │ ├── message_loc: (27,2)-(27,9) = "include"
+ │ │ │ ├── opening_loc: (27,9)-(27,10) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (27,10)-(27,15))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (27,10)-(27,15))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── receiver:
+ │ │ │ │ │ @ ConstantReadNode (location: (27,10)-(27,11))
+ │ │ │ │ │ └── name: :B
+ │ │ │ │ ├── call_operator_loc: (27,11)-(27,12) = "."
+ │ │ │ │ ├── name: :new
+ │ │ │ │ ├── message_loc: (27,12)-(27,15) = "new"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: (27,15)-(27,16) = ")"
+ │ │ │ └── block: ∅
+ │ │ └── @ DefNode (location: (29,2)-(31,5))
+ │ │ ├── name: :foo
+ │ │ ├── name_loc: (29,6)-(29,9) = "foo"
+ │ │ ├── receiver: ∅
+ │ │ ├── parameters: ∅
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (30,4)-(30,8))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (30,4)-(30,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (30,4)-(30,5) = ":"
+ │ │ │ ├── value_loc: (30,5)-(30,8) = "bar"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "bar"
+ │ │ ├── locals: []
+ │ │ ├── def_keyword_loc: (29,2)-(29,5) = "def"
+ │ │ ├── operator_loc: ∅
+ │ │ ├── lparen_loc: ∅
+ │ │ ├── rparen_loc: ∅
+ │ │ ├── equal_loc: ∅
+ │ │ └── end_keyword_loc: (31,2)-(31,5) = "end"
+ │ ├── end_keyword_loc: (32,0)-(32,3) = "end"
+ │ └── name: :A
+ └── @ ClassNode (location: (34,0)-(35,3))
+ ├── locals: []
+ ├── class_keyword_loc: (34,0)-(34,5) = "class"
+ ├── constant_path:
+ │ @ ConstantPathNode (location: (34,6)-(34,9))
+ │ ├── parent: ∅
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (34,8)-(34,9))
+ │ │ └── name: :A
+ │ └── delimiter_loc: (34,6)-(34,8) = "::"
+ ├── inheritance_operator_loc: ∅
+ ├── superclass: ∅
+ ├── body: ∅
+ ├── end_keyword_loc: (35,0)-(35,3) = "end"
+ └── name: :A
diff --git a/test/prism/snapshots/unparser/corpus/literal/def.txt b/test/prism/snapshots/unparser/corpus/literal/def.txt
new file mode 100644
index 0000000000..f3ef6c388e
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/def.txt
@@ -0,0 +1,1204 @@
+@ ProgramNode (location: (1,0)-(134,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(134,3))
+ └── body: (length: 30)
+ ├── @ DefNode (location: (1,0)-(9,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (1,4)-(1,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (1,0)-(9,3))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (2,2)-(2,3))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (2,2)-(2,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (2,2)-(2,3) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── rescue_clause:
+ │ │ │ @ RescueNode (location: (3,0)-(4,3))
+ │ │ │ ├── keyword_loc: (3,0)-(3,6) = "rescue"
+ │ │ │ ├── exceptions: (length: 0)
+ │ │ │ ├── operator_loc: ∅
+ │ │ │ ├── reference: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (4,2)-(4,3))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (4,2)-(4,3))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── message_loc: (4,2)-(4,3) = "b"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── consequent: ∅
+ │ │ ├── else_clause:
+ │ │ │ @ ElseNode (location: (5,0)-(7,6))
+ │ │ │ ├── else_keyword_loc: (5,0)-(5,4) = "else"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (6,2)-(6,3))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (6,2)-(6,3))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (6,2)-(6,3) = "c"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── end_keyword_loc: (7,0)-(7,6) = "ensure"
+ │ │ ├── ensure_clause:
+ │ │ │ @ EnsureNode (location: (7,0)-(9,3))
+ │ │ │ ├── ensure_keyword_loc: (7,0)-(7,6) = "ensure"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (8,2)-(8,3))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (8,2)-(8,3))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :d
+ │ │ │ │ ├── message_loc: (8,2)-(8,3) = "d"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── end_keyword_loc: (9,0)-(9,3) = "end"
+ │ │ └── end_keyword_loc: (9,0)-(9,3) = "end"
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (9,0)-(9,3) = "end"
+ ├── @ DefNode (location: (11,0)-(19,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (11,4)-(11,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (11,0)-(19,3))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (12,2)-(12,12))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ RescueModifierNode (location: (12,2)-(12,12))
+ │ │ │ ├── expression:
+ │ │ │ │ @ CallNode (location: (12,2)-(12,3))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── message_loc: (12,2)-(12,3) = "a"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── keyword_loc: (12,4)-(12,10) = "rescue"
+ │ │ │ └── rescue_expression:
+ │ │ │ @ CallNode (location: (12,11)-(12,12))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (12,11)-(12,12) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── rescue_clause:
+ │ │ │ @ RescueNode (location: (13,0)-(14,3))
+ │ │ │ ├── keyword_loc: (13,0)-(13,6) = "rescue"
+ │ │ │ ├── exceptions: (length: 0)
+ │ │ │ ├── operator_loc: ∅
+ │ │ │ ├── reference: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (14,2)-(14,3))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (14,2)-(14,3))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── message_loc: (14,2)-(14,3) = "b"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── consequent: ∅
+ │ │ ├── else_clause:
+ │ │ │ @ ElseNode (location: (15,0)-(17,6))
+ │ │ │ ├── else_keyword_loc: (15,0)-(15,4) = "else"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (16,2)-(16,3))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (16,2)-(16,3))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (16,2)-(16,3) = "c"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── end_keyword_loc: (17,0)-(17,6) = "ensure"
+ │ │ ├── ensure_clause:
+ │ │ │ @ EnsureNode (location: (17,0)-(19,3))
+ │ │ │ ├── ensure_keyword_loc: (17,0)-(17,6) = "ensure"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (18,2)-(18,3))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (18,2)-(18,3))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :d
+ │ │ │ │ ├── message_loc: (18,2)-(18,3) = "d"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── end_keyword_loc: (19,0)-(19,3) = "end"
+ │ │ └── end_keyword_loc: (19,0)-(19,3) = "end"
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (11,0)-(11,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (19,0)-(19,3) = "end"
+ ├── @ DefNode (location: (21,0)-(22,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (21,4)-(21,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (21,8)-(21,18))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 2)
+ │ │ │ ├── @ RequiredKeywordParameterNode (location: (21,8)-(21,12))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ └── name_loc: (21,8)-(21,12) = "bar:"
+ │ │ │ └── @ RequiredKeywordParameterNode (location: (21,14)-(21,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ └── name_loc: (21,14)-(21,18) = "baz:"
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:bar, :baz]
+ │ ├── def_keyword_loc: (21,0)-(21,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (21,7)-(21,8) = "("
+ │ ├── rparen_loc: (21,18)-(21,19) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (22,0)-(22,3) = "end"
+ ├── @ DefNode (location: (24,0)-(25,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (24,4)-(24,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (24,0)-(24,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (25,0)-(25,3) = "end"
+ ├── @ DefNode (location: (27,0)-(29,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (27,4)-(27,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (28,2)-(28,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (28,2)-(28,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (28,2)-(28,5) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (27,0)-(27,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (29,0)-(29,3) = "end"
+ ├── @ DefNode (location: (31,0)-(37,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (31,4)-(31,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (31,0)-(37,3))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (32,2)-(32,5))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (32,2)-(32,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (32,2)-(32,5) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── rescue_clause:
+ │ │ │ @ RescueNode (location: (33,0)-(34,5))
+ │ │ │ ├── keyword_loc: (33,0)-(33,6) = "rescue"
+ │ │ │ ├── exceptions: (length: 0)
+ │ │ │ ├── operator_loc: ∅
+ │ │ │ ├── reference: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (34,2)-(34,5))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (34,2)-(34,5))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (34,2)-(34,5) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── consequent: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause:
+ │ │ │ @ EnsureNode (location: (35,0)-(37,3))
+ │ │ │ ├── ensure_keyword_loc: (35,0)-(35,6) = "ensure"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (36,2)-(36,5))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (36,2)-(36,5))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ ├── message_loc: (36,2)-(36,5) = "baz"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── end_keyword_loc: (37,0)-(37,3) = "end"
+ │ │ └── end_keyword_loc: (37,0)-(37,3) = "end"
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (31,0)-(31,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (37,0)-(37,3) = "end"
+ ├── @ DefNode (location: (39,0)-(43,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (39,4)-(39,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (39,0)-(43,3))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (40,2)-(40,5))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (40,2)-(40,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (40,2)-(40,5) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── rescue_clause: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause:
+ │ │ │ @ EnsureNode (location: (41,0)-(43,3))
+ │ │ │ ├── ensure_keyword_loc: (41,0)-(41,6) = "ensure"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (42,2)-(42,5))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (42,2)-(42,5))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ ├── message_loc: (42,2)-(42,5) = "baz"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── end_keyword_loc: (43,0)-(43,3) = "end"
+ │ │ └── end_keyword_loc: (43,0)-(43,3) = "end"
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (39,0)-(39,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (43,0)-(43,3) = "end"
+ ├── @ DefNode (location: (45,0)-(49,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (45,4)-(45,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (45,0)-(49,3))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (46,2)-(46,5))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (46,2)-(46,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (46,2)-(46,5) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── rescue_clause:
+ │ │ │ @ RescueNode (location: (47,0)-(48,5))
+ │ │ │ ├── keyword_loc: (47,0)-(47,6) = "rescue"
+ │ │ │ ├── exceptions: (length: 0)
+ │ │ │ ├── operator_loc: ∅
+ │ │ │ ├── reference: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (48,2)-(48,5))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (48,2)-(48,5))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ ├── message_loc: (48,2)-(48,5) = "baz"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── consequent: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause: ∅
+ │ │ └── end_keyword_loc: (49,0)-(49,3) = "end"
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (45,0)-(45,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (49,0)-(49,3) = "end"
+ ├── @ DefNode (location: (51,0)-(53,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (51,4)-(51,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (51,8)-(51,11))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (51,8)-(51,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :bar
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (52,2)-(52,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (52,2)-(52,5))
+ │ │ ├── name: :bar
+ │ │ └── depth: 0
+ │ ├── locals: [:bar]
+ │ ├── def_keyword_loc: (51,0)-(51,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (51,7)-(51,8) = "("
+ │ ├── rparen_loc: (51,11)-(51,12) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (53,0)-(53,3) = "end"
+ ├── @ DefNode (location: (55,0)-(57,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (55,4)-(55,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (55,8)-(55,16))
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ RequiredParameterNode (location: (55,8)-(55,11))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :bar
+ │ │ │ └── @ RequiredParameterNode (location: (55,13)-(55,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :baz
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (56,2)-(56,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (56,2)-(56,5))
+ │ │ ├── name: :bar
+ │ │ └── depth: 0
+ │ ├── locals: [:bar, :baz]
+ │ ├── def_keyword_loc: (55,0)-(55,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (55,7)-(55,8) = "("
+ │ ├── rparen_loc: (55,16)-(55,17) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (57,0)-(57,3) = "end"
+ ├── @ DefNode (location: (59,0)-(61,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (59,4)-(59,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (59,8)-(59,16))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (59,8)-(59,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── name_loc: (59,8)-(59,11) = "bar"
+ │ │ │ ├── operator_loc: (59,12)-(59,13) = "="
+ │ │ │ └── value:
+ │ │ │ @ ParenthesesNode (location: (59,14)-(59,16))
+ │ │ │ ├── body: ∅
+ │ │ │ ├── opening_loc: (59,14)-(59,15) = "("
+ │ │ │ └── closing_loc: (59,15)-(59,16) = ")"
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (60,2)-(60,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (60,2)-(60,5))
+ │ │ ├── name: :bar
+ │ │ └── depth: 0
+ │ ├── locals: [:bar]
+ │ ├── def_keyword_loc: (59,0)-(59,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (59,7)-(59,8) = "("
+ │ ├── rparen_loc: (59,16)-(59,17) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (61,0)-(61,3) = "end"
+ ├── @ DefNode (location: (63,0)-(64,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (63,4)-(63,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (63,8)-(63,24))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (63,8)-(63,24))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── name_loc: (63,8)-(63,11) = "bar"
+ │ │ │ ├── operator_loc: (63,12)-(63,13) = "="
+ │ │ │ └── value:
+ │ │ │ @ ParenthesesNode (location: (63,14)-(63,24))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (63,15)-(63,23))
+ │ │ │ │ └── body: (length: 2)
+ │ │ │ │ ├── @ CallNode (location: (63,15)-(63,18))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :baz
+ │ │ │ │ │ ├── message_loc: (63,15)-(63,18) = "baz"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ └── @ NilNode (location: (63,20)-(63,23))
+ │ │ │ ├── opening_loc: (63,14)-(63,15) = "("
+ │ │ │ └── closing_loc: (63,23)-(63,24) = ")"
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:bar]
+ │ ├── def_keyword_loc: (63,0)-(63,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (63,7)-(63,8) = "("
+ │ ├── rparen_loc: (63,24)-(63,25) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (64,0)-(64,3) = "end"
+ ├── @ DefNode (location: (66,0)-(68,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (66,4)-(66,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (66,8)-(66,18))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (66,8)-(66,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── name_loc: (66,8)-(66,11) = "bar"
+ │ │ │ ├── operator_loc: (66,12)-(66,13) = "="
+ │ │ │ └── value:
+ │ │ │ @ TrueNode (location: (66,14)-(66,18))
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (67,2)-(67,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (67,2)-(67,5))
+ │ │ ├── name: :bar
+ │ │ └── depth: 0
+ │ ├── locals: [:bar]
+ │ ├── def_keyword_loc: (66,0)-(66,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (66,7)-(66,8) = "("
+ │ ├── rparen_loc: (66,18)-(66,19) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (68,0)-(68,3) = "end"
+ ├── @ DefNode (location: (70,0)-(72,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (70,4)-(70,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (70,8)-(70,23))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (70,8)-(70,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :bar
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (70,13)-(70,23))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── name_loc: (70,13)-(70,16) = "baz"
+ │ │ │ ├── operator_loc: (70,17)-(70,18) = "="
+ │ │ │ └── value:
+ │ │ │ @ TrueNode (location: (70,19)-(70,23))
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (71,2)-(71,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (71,2)-(71,5))
+ │ │ ├── name: :bar
+ │ │ └── depth: 0
+ │ ├── locals: [:bar, :baz]
+ │ ├── def_keyword_loc: (70,0)-(70,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (70,7)-(70,8) = "("
+ │ ├── rparen_loc: (70,23)-(70,24) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (72,0)-(72,3) = "end"
+ ├── @ DefNode (location: (74,0)-(75,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (74,4)-(74,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (74,8)-(74,14))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ OptionalKeywordParameterNode (location: (74,8)-(74,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── name_loc: (74,8)-(74,12) = "bar:"
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (74,13)-(74,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:bar]
+ │ ├── def_keyword_loc: (74,0)-(74,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (74,7)-(74,8) = "("
+ │ ├── rparen_loc: (74,14)-(74,15) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (75,0)-(75,3) = "end"
+ ├── @ DefNode (location: (77,0)-(78,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (77,4)-(77,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (77,8)-(77,16))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ OptionalKeywordParameterNode (location: (77,8)-(77,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── name_loc: (77,8)-(77,12) = "bar:"
+ │ │ │ └── value:
+ │ │ │ @ CallNode (location: (77,13)-(77,16))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (77,13)-(77,16) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:bar]
+ │ ├── def_keyword_loc: (77,0)-(77,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (77,7)-(77,8) = "("
+ │ ├── rparen_loc: (77,16)-(77,17) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (78,0)-(78,3) = "end"
+ ├── @ DefNode (location: (80,0)-(81,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (80,4)-(80,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (80,8)-(80,18))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ OptionalKeywordParameterNode (location: (80,8)-(80,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── name_loc: (80,8)-(80,12) = "bar:"
+ │ │ │ └── value:
+ │ │ │ @ CallNode (location: (80,13)-(80,18))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (80,13)-(80,16) = "bar"
+ │ │ │ ├── opening_loc: (80,16)-(80,17) = "("
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: (80,17)-(80,18) = ")"
+ │ │ │ └── block: ∅
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:bar]
+ │ ├── def_keyword_loc: (80,0)-(80,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (80,7)-(80,8) = "("
+ │ ├── rparen_loc: (80,18)-(80,19) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (81,0)-(81,3) = "end"
+ ├── @ DefNode (location: (83,0)-(85,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (83,4)-(83,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (83,8)-(83,9))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (83,8)-(83,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: ∅
+ │ │ │ ├── name_loc: ∅
+ │ │ │ └── operator_loc: (83,8)-(83,9) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (84,2)-(84,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (84,2)-(84,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (84,2)-(84,5) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (83,0)-(83,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (83,7)-(83,8) = "("
+ │ ├── rparen_loc: (83,9)-(83,10) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (85,0)-(85,3) = "end"
+ ├── @ DefNode (location: (87,0)-(89,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (87,4)-(87,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (87,8)-(87,12))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (87,8)-(87,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── name_loc: (87,9)-(87,12) = "bar"
+ │ │ │ └── operator_loc: (87,8)-(87,9) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (88,2)-(88,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (88,2)-(88,5))
+ │ │ ├── name: :bar
+ │ │ └── depth: 0
+ │ ├── locals: [:bar]
+ │ ├── def_keyword_loc: (87,0)-(87,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (87,7)-(87,8) = "("
+ │ ├── rparen_loc: (87,12)-(87,13) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (89,0)-(89,3) = "end"
+ ├── @ DefNode (location: (91,0)-(93,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (91,4)-(91,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (91,8)-(91,17))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (91,8)-(91,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :bar
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (91,13)-(91,17))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── name_loc: (91,14)-(91,17) = "baz"
+ │ │ │ └── operator_loc: (91,13)-(91,14) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (92,2)-(92,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (92,2)-(92,5))
+ │ │ ├── name: :bar
+ │ │ └── depth: 0
+ │ ├── locals: [:bar, :baz]
+ │ ├── def_keyword_loc: (91,0)-(91,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (91,7)-(91,8) = "("
+ │ ├── rparen_loc: (91,17)-(91,18) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (93,0)-(93,3) = "end"
+ ├── @ DefNode (location: (95,0)-(97,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (95,4)-(95,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (95,8)-(95,24))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (95,8)-(95,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── name_loc: (95,8)-(95,11) = "baz"
+ │ │ │ ├── operator_loc: (95,12)-(95,13) = "="
+ │ │ │ └── value:
+ │ │ │ @ TrueNode (location: (95,14)-(95,18))
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (95,20)-(95,24))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :bor
+ │ │ │ ├── name_loc: (95,21)-(95,24) = "bor"
+ │ │ │ └── operator_loc: (95,20)-(95,21) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (96,2)-(96,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (96,2)-(96,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (96,2)-(96,5) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: [:baz, :bor]
+ │ ├── def_keyword_loc: (95,0)-(95,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (95,7)-(95,8) = "("
+ │ ├── rparen_loc: (95,24)-(95,25) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (97,0)-(97,3) = "end"
+ ├── @ DefNode (location: (99,0)-(101,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (99,4)-(99,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (99,8)-(99,32))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (99,8)-(99,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── name_loc: (99,8)-(99,11) = "baz"
+ │ │ │ ├── operator_loc: (99,12)-(99,13) = "="
+ │ │ │ └── value:
+ │ │ │ @ TrueNode (location: (99,14)-(99,18))
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (99,20)-(99,24))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :bor
+ │ │ │ ├── name_loc: (99,21)-(99,24) = "bor"
+ │ │ │ └── operator_loc: (99,20)-(99,21) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (99,26)-(99,32))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :block
+ │ │ ├── name_loc: (99,27)-(99,32) = "block"
+ │ │ └── operator_loc: (99,26)-(99,27) = "&"
+ │ ├── body:
+ │ │ @ StatementsNode (location: (100,2)-(100,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (100,2)-(100,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (100,2)-(100,5) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: [:baz, :bor, :block]
+ │ ├── def_keyword_loc: (99,0)-(99,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (99,7)-(99,8) = "("
+ │ ├── rparen_loc: (99,32)-(99,33) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (101,0)-(101,3) = "end"
+ ├── @ DefNode (location: (103,0)-(105,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (103,4)-(103,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (103,8)-(103,29))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (103,8)-(103,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :bar
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (103,13)-(103,23))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── name_loc: (103,13)-(103,16) = "baz"
+ │ │ │ ├── operator_loc: (103,17)-(103,18) = "="
+ │ │ │ └── value:
+ │ │ │ @ TrueNode (location: (103,19)-(103,23))
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (103,25)-(103,29))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :bor
+ │ │ │ ├── name_loc: (103,26)-(103,29) = "bor"
+ │ │ │ └── operator_loc: (103,25)-(103,26) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (104,2)-(104,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (104,2)-(104,5))
+ │ │ ├── name: :bar
+ │ │ └── depth: 0
+ │ ├── locals: [:bar, :baz, :bor]
+ │ ├── def_keyword_loc: (103,0)-(103,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (103,7)-(103,8) = "("
+ │ ├── rparen_loc: (103,29)-(103,30) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (105,0)-(105,3) = "end"
+ ├── @ DefNode (location: (107,0)-(109,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (107,4)-(107,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (107,8)-(107,14))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (107,8)-(107,14))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :block
+ │ │ ├── name_loc: (107,9)-(107,14) = "block"
+ │ │ └── operator_loc: (107,8)-(107,9) = "&"
+ │ ├── body:
+ │ │ @ StatementsNode (location: (108,2)-(108,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (108,2)-(108,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (108,2)-(108,5) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: [:block]
+ │ ├── def_keyword_loc: (107,0)-(107,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (107,7)-(107,8) = "("
+ │ ├── rparen_loc: (107,14)-(107,15) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (109,0)-(109,3) = "end"
+ ├── @ DefNode (location: (111,0)-(113,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (111,4)-(111,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (111,8)-(111,19))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (111,8)-(111,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :bar
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (111,13)-(111,19))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :block
+ │ │ ├── name_loc: (111,14)-(111,19) = "block"
+ │ │ └── operator_loc: (111,13)-(111,14) = "&"
+ │ ├── body:
+ │ │ @ StatementsNode (location: (112,2)-(112,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (112,2)-(112,5))
+ │ │ ├── name: :bar
+ │ │ └── depth: 0
+ │ ├── locals: [:bar, :block]
+ │ ├── def_keyword_loc: (111,0)-(111,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (111,7)-(111,8) = "("
+ │ ├── rparen_loc: (111,19)-(111,20) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (113,0)-(113,3) = "end"
+ ├── @ DefNode (location: (115,0)-(118,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (115,4)-(115,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (116,2)-(117,5))
+ │ │ └── body: (length: 2)
+ │ │ ├── @ CallNode (location: (116,2)-(116,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (116,2)-(116,5) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ CallNode (location: (117,2)-(117,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (117,2)-(117,5) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (115,0)-(115,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (118,0)-(118,3) = "end"
+ ├── @ DefNode (location: (120,0)-(121,3))
+ │ ├── name: :f
+ │ ├── name_loc: (120,4)-(120,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (120,6)-(120,11))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ MultiTargetNode (location: (120,6)-(120,11))
+ │ │ │ ├── lefts: (length: 1)
+ │ │ │ │ └── @ MultiTargetNode (location: (120,7)-(120,10))
+ │ │ │ │ ├── lefts: (length: 1)
+ │ │ │ │ │ └── @ RequiredParameterNode (location: (120,8)-(120,9))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :a
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── rights: (length: 0)
+ │ │ │ │ ├── lparen_loc: (120,7)-(120,8) = "("
+ │ │ │ │ └── rparen_loc: (120,9)-(120,10) = ")"
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: (120,6)-(120,7) = "("
+ │ │ │ └── rparen_loc: (120,10)-(120,11) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:a]
+ │ ├── def_keyword_loc: (120,0)-(120,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (120,5)-(120,6) = "("
+ │ ├── rparen_loc: (120,11)-(120,12) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (121,0)-(121,3) = "end"
+ ├── @ DefNode (location: (123,0)-(124,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (123,4)-(123,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (123,8)-(123,26))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 2)
+ │ │ │ ├── @ RequiredKeywordParameterNode (location: (123,8)-(123,12))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ └── name_loc: (123,8)-(123,12) = "bar:"
+ │ │ │ └── @ OptionalKeywordParameterNode (location: (123,14)-(123,26))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── name_loc: (123,14)-(123,18) = "baz:"
+ │ │ │ └── value:
+ │ │ │ @ StringNode (location: (123,19)-(123,26))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (123,19)-(123,20) = "\""
+ │ │ │ ├── content_loc: (123,20)-(123,25) = "value"
+ │ │ │ ├── closing_loc: (123,25)-(123,26) = "\""
+ │ │ │ └── unescaped: "value"
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:bar, :baz]
+ │ ├── def_keyword_loc: (123,0)-(123,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (123,7)-(123,8) = "("
+ │ ├── rparen_loc: (123,26)-(123,27) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (124,0)-(124,3) = "end"
+ ├── @ DefNode (location: (126,0)-(130,3))
+ │ ├── name: :f
+ │ ├── name_loc: (126,4)-(126,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (127,2)-(127,12))
+ │ │ └── body: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (127,2)-(127,12))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (127,2)-(127,12) = "<<-HEREDOC"
+ │ │ ├── parts: (length: 3)
+ │ │ │ ├── @ StringNode (location: (128,0)-(128,4))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (128,0)-(128,4) = " "
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: " "
+ │ │ │ ├── @ EmbeddedStatementsNode (location: (128,4)-(128,7))
+ │ │ │ │ ├── opening_loc: (128,4)-(128,6) = "\#{"
+ │ │ │ │ ├── statements: ∅
+ │ │ │ │ └── closing_loc: (128,6)-(128,7) = "}"
+ │ │ │ └── @ StringNode (location: (128,7)-(129,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (128,7)-(129,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── closing_loc: (129,0)-(130,0) = " HEREDOC\n"
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (126,0)-(126,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (130,0)-(130,3) = "end"
+ └── @ DefNode (location: (132,0)-(134,3))
+ ├── name: :f
+ ├── name_loc: (132,4)-(132,5) = "f"
+ ├── receiver: ∅
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (133,2)-(133,5))
+ │ └── body: (length: 1)
+ │ └── @ StringNode (location: (133,2)-(133,5))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (133,2)-(133,4) = "%("
+ │ ├── content_loc: (133,4)-(133,4) = ""
+ │ ├── closing_loc: (133,4)-(133,5) = ")"
+ │ └── unescaped: ""
+ ├── locals: []
+ ├── def_keyword_loc: (132,0)-(132,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (134,0)-(134,3) = "end"
diff --git a/test/prism/snapshots/unparser/corpus/literal/defined.txt b/test/prism/snapshots/unparser/corpus/literal/defined.txt
new file mode 100644
index 0000000000..89145ddcda
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/defined.txt
@@ -0,0 +1,55 @@
+@ ProgramNode (location: (1,0)-(3,27))
+├── locals: [:a, :b]
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,27))
+ └── body: (length: 3)
+ ├── @ DefinedNode (location: (1,0)-(1,14))
+ │ ├── lparen_loc: (1,8)-(1,9) = "("
+ │ ├── value:
+ │ │ @ InstanceVariableReadNode (location: (1,9)-(1,13))
+ │ │ └── name: :@foo
+ │ ├── rparen_loc: (1,13)-(1,14) = ")"
+ │ └── keyword_loc: (1,0)-(1,8) = "defined?"
+ ├── @ DefinedNode (location: (2,0)-(2,13))
+ │ ├── lparen_loc: (2,8)-(2,9) = "("
+ │ ├── value:
+ │ │ @ ConstantReadNode (location: (2,9)-(2,12))
+ │ │ └── name: :Foo
+ │ ├── rparen_loc: (2,12)-(2,13) = ")"
+ │ └── keyword_loc: (2,0)-(2,8) = "defined?"
+ └── @ DefinedNode (location: (3,0)-(3,27))
+ ├── lparen_loc: (3,8)-(3,9) = "("
+ ├── value:
+ │ @ ParenthesesNode (location: (3,9)-(3,26))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (3,10)-(3,25))
+ │ │ └── body: (length: 1)
+ │ │ └── @ MultiWriteNode (location: (3,10)-(3,25))
+ │ │ ├── lefts: (length: 2)
+ │ │ │ ├── @ LocalVariableTargetNode (location: (3,11)-(3,12))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ └── depth: 0
+ │ │ │ └── @ LocalVariableTargetNode (location: (3,14)-(3,15))
+ │ │ │ ├── name: :b
+ │ │ │ └── depth: 0
+ │ │ ├── rest: ∅
+ │ │ ├── rights: (length: 0)
+ │ │ ├── lparen_loc: (3,10)-(3,11) = "("
+ │ │ ├── rparen_loc: (3,15)-(3,16) = ")"
+ │ │ ├── operator_loc: (3,17)-(3,18) = "="
+ │ │ └── value:
+ │ │ @ ArrayNode (location: (3,19)-(3,25))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 2)
+ │ │ │ ├── @ IntegerNode (location: (3,20)-(3,21))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── @ IntegerNode (location: (3,23)-(3,24))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── opening_loc: (3,19)-(3,20) = "["
+ │ │ └── closing_loc: (3,24)-(3,25) = "]"
+ │ ├── opening_loc: (3,9)-(3,10) = "("
+ │ └── closing_loc: (3,25)-(3,26) = ")"
+ ├── rparen_loc: (3,26)-(3,27) = ")"
+ └── keyword_loc: (3,0)-(3,8) = "defined?"
diff --git a/test/prism/snapshots/unparser/corpus/literal/defs.txt b/test/prism/snapshots/unparser/corpus/literal/defs.txt
new file mode 100644
index 0000000000..431843cc19
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/defs.txt
@@ -0,0 +1,360 @@
+@ ProgramNode (location: (1,0)-(40,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(40,3))
+ └── body: (length: 10)
+ ├── @ DefNode (location: (1,0)-(2,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (1,9)-(1,12) = "foo"
+ │ ├── receiver:
+ │ │ @ SelfNode (location: (1,4)-(1,8))
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: (1,8)-(1,9) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (2,0)-(2,3) = "end"
+ ├── @ DefNode (location: (4,0)-(6,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (4,9)-(4,12) = "foo"
+ │ ├── receiver:
+ │ │ @ SelfNode (location: (4,4)-(4,8))
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (5,2)-(5,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (5,2)-(5,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (5,2)-(5,5) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (4,0)-(4,3) = "def"
+ │ ├── operator_loc: (4,8)-(4,9) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (6,0)-(6,3) = "end"
+ ├── @ DefNode (location: (8,0)-(11,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (8,9)-(8,12) = "foo"
+ │ ├── receiver:
+ │ │ @ SelfNode (location: (8,4)-(8,8))
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (9,2)-(10,5))
+ │ │ └── body: (length: 2)
+ │ │ ├── @ CallNode (location: (9,2)-(9,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (9,2)-(9,5) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ CallNode (location: (10,2)-(10,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (10,2)-(10,5) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (8,0)-(8,3) = "def"
+ │ ├── operator_loc: (8,8)-(8,9) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (11,0)-(11,3) = "end"
+ ├── @ DefNode (location: (13,0)-(15,3))
+ │ ├── name: :bar
+ │ ├── name_loc: (13,8)-(13,11) = "bar"
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (13,4)-(13,7))
+ │ │ └── name: :Foo
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (14,2)-(14,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (14,2)-(14,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (14,2)-(14,5) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (13,0)-(13,3) = "def"
+ │ ├── operator_loc: (13,7)-(13,8) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (15,0)-(15,3) = "end"
+ ├── @ DefNode (location: (17,0)-(20,3))
+ │ ├── name: :bar
+ │ ├── name_loc: (18,3)-(18,6) = "bar"
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (17,4)-(18,2))
+ │ │ ├── body:
+ │ │ │ @ CallNode (location: (17,5)-(18,1))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (17,5)-(17,8) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (17,9)-(18,1))
+ │ │ │ ├── locals: [:bar]
+ │ │ │ ├── parameters:
+ │ │ │ │ @ BlockParametersNode (location: (17,11)-(17,16))
+ │ │ │ │ ├── parameters:
+ │ │ │ │ │ @ ParametersNode (location: (17,12)-(17,15))
+ │ │ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ │ │ └── @ RequiredParameterNode (location: (17,12)-(17,15))
+ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ └── name: :bar
+ │ │ │ │ │ ├── optionals: (length: 0)
+ │ │ │ │ │ ├── rest: ∅
+ │ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ │ ├── keywords: (length: 0)
+ │ │ │ │ │ ├── keyword_rest: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── locals: (length: 0)
+ │ │ │ │ ├── opening_loc: (17,11)-(17,12) = "|"
+ │ │ │ │ └── closing_loc: (17,15)-(17,16) = "|"
+ │ │ │ ├── body: ∅
+ │ │ │ ├── opening_loc: (17,9)-(17,10) = "{"
+ │ │ │ └── closing_loc: (18,0)-(18,1) = "}"
+ │ │ ├── opening_loc: (17,4)-(17,5) = "("
+ │ │ └── closing_loc: (18,1)-(18,2) = ")"
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (19,2)-(19,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (19,2)-(19,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (19,2)-(19,5) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (17,0)-(17,3) = "def"
+ │ ├── operator_loc: (18,2)-(18,3) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (20,0)-(20,3) = "end"
+ ├── @ DefNode (location: (22,0)-(24,3))
+ │ ├── name: :bar
+ │ ├── name_loc: (22,13)-(22,16) = "bar"
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (22,4)-(22,12))
+ │ │ ├── body:
+ │ │ │ @ CallNode (location: (22,5)-(22,11))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (22,5)-(22,8) = "foo"
+ │ │ │ ├── opening_loc: (22,8)-(22,9) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (22,9)-(22,10))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (22,9)-(22,10))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── closing_loc: (22,10)-(22,11) = ")"
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (22,4)-(22,5) = "("
+ │ │ └── closing_loc: (22,11)-(22,12) = ")"
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (23,2)-(23,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (23,2)-(23,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (23,2)-(23,5) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (22,0)-(22,3) = "def"
+ │ ├── operator_loc: (22,12)-(22,13) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (24,0)-(24,3) = "end"
+ ├── @ DefNode (location: (26,0)-(28,3))
+ │ ├── name: :bar
+ │ ├── name_loc: (26,19)-(26,22) = "bar"
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (26,4)-(26,18))
+ │ │ ├── body:
+ │ │ │ @ CallNode (location: (26,5)-(26,17))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ ConstantPathNode (location: (26,5)-(26,13))
+ │ │ │ │ ├── parent:
+ │ │ │ │ │ @ ConstantReadNode (location: (26,5)-(26,8))
+ │ │ │ │ │ └── name: :Foo
+ │ │ │ │ ├── child:
+ │ │ │ │ │ @ ConstantReadNode (location: (26,10)-(26,13))
+ │ │ │ │ │ └── name: :Bar
+ │ │ │ │ └── delimiter_loc: (26,8)-(26,10) = "::"
+ │ │ │ ├── call_operator_loc: (26,13)-(26,14) = "."
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (26,14)-(26,17) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (26,4)-(26,5) = "("
+ │ │ └── closing_loc: (26,17)-(26,18) = ")"
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (27,2)-(27,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (27,2)-(27,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (27,2)-(27,5) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (26,0)-(26,3) = "def"
+ │ ├── operator_loc: (26,18)-(26,19) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (28,0)-(28,3) = "end"
+ ├── @ DefNode (location: (30,0)-(32,3))
+ │ ├── name: :bar
+ │ ├── name_loc: (30,15)-(30,18) = "bar"
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (30,4)-(30,14))
+ │ │ ├── body:
+ │ │ │ @ ConstantPathNode (location: (30,5)-(30,13))
+ │ │ │ ├── parent:
+ │ │ │ │ @ ConstantReadNode (location: (30,5)-(30,8))
+ │ │ │ │ └── name: :Foo
+ │ │ │ ├── child:
+ │ │ │ │ @ ConstantReadNode (location: (30,10)-(30,13))
+ │ │ │ │ └── name: :Bar
+ │ │ │ └── delimiter_loc: (30,8)-(30,10) = "::"
+ │ │ ├── opening_loc: (30,4)-(30,5) = "("
+ │ │ └── closing_loc: (30,13)-(30,14) = ")"
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (31,2)-(31,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (31,2)-(31,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (31,2)-(31,5) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (30,0)-(30,3) = "def"
+ │ ├── operator_loc: (30,14)-(30,15) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (32,0)-(32,3) = "end"
+ ├── @ DefNode (location: (34,0)-(36,3))
+ │ ├── name: :bar
+ │ ├── name_loc: (34,8)-(34,11) = "bar"
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (34,4)-(34,7))
+ │ │ └── name: :Foo
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (35,2)-(35,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (35,2)-(35,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (35,2)-(35,5) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (34,0)-(34,3) = "def"
+ │ ├── operator_loc: (34,7)-(34,8) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (36,0)-(36,3) = "end"
+ └── @ DefNode (location: (38,0)-(40,3))
+ ├── name: :bar
+ ├── name_loc: (38,8)-(38,11) = "bar"
+ ├── receiver:
+ │ @ CallNode (location: (38,4)-(38,7))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (38,4)-(38,7) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (39,2)-(39,5))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (39,2)-(39,5))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :baz
+ │ ├── message_loc: (39,2)-(39,5) = "baz"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (38,0)-(38,3) = "def"
+ ├── operator_loc: (38,7)-(38,8) = "."
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (40,0)-(40,3) = "end"
diff --git a/test/prism/snapshots/unparser/corpus/literal/dstr.txt b/test/prism/snapshots/unparser/corpus/literal/dstr.txt
new file mode 100644
index 0000000000..8893e8b75d
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/dstr.txt
@@ -0,0 +1,353 @@
+@ ProgramNode (location: (1,0)-(37,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(37,1))
+ └── body: (length: 11)
+ ├── @ IfNode (location: (1,0)-(3,3))
+ │ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (1,3)-(1,7))
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (2,2)-(2,8))
+ │ │ └── body: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (2,2)-(2,8))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (2,2)-(2,3) = "\""
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ EmbeddedStatementsNode (location: (2,3)-(2,6))
+ │ │ │ │ ├── opening_loc: (2,3)-(2,5) = "\#{"
+ │ │ │ │ ├── statements: ∅
+ │ │ │ │ └── closing_loc: (2,5)-(2,6) = "}"
+ │ │ │ └── @ StringNode (location: (2,6)-(2,7))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (2,6)-(2,7) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ └── closing_loc: (2,7)-(2,8) = "\""
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (3,0)-(3,3) = "end"
+ ├── @ IfNode (location: (4,0)-(11,3))
+ │ ├── if_keyword_loc: (4,0)-(4,2) = "if"
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (4,3)-(4,7))
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (5,2)-(10,3))
+ │ │ └── body: (length: 2)
+ │ │ ├── @ InterpolatedStringNode (location: (5,2)-(5,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (5,2)-(5,12) = "<<-HEREDOC"
+ │ │ │ ├── parts: (length: 3)
+ │ │ │ │ ├── @ StringNode (location: (6,0)-(7,0))
+ │ │ │ │ │ ├── flags: frozen
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── content_loc: (6,0)-(7,0) = "a\n"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "a\n"
+ │ │ │ │ ├── @ EmbeddedStatementsNode (location: (7,0)-(7,3))
+ │ │ │ │ │ ├── opening_loc: (7,0)-(7,2) = "\#{"
+ │ │ │ │ │ ├── statements: ∅
+ │ │ │ │ │ └── closing_loc: (7,2)-(7,3) = "}"
+ │ │ │ │ └── @ StringNode (location: (7,3)-(9,0))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (7,3)-(9,0) = "a\nb\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "a\nb\n"
+ │ │ │ └── closing_loc: (9,0)-(10,0) = " HEREDOC\n"
+ │ │ └── @ CallNode (location: (10,2)-(10,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :x
+ │ │ ├── message_loc: (10,2)-(10,3) = "x"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (11,0)-(11,3) = "end"
+ ├── @ InterpolatedStringNode (location: (12,0)-(12,10))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (12,0)-(12,10) = "<<-HEREDOC"
+ │ ├── parts: (length: 7)
+ │ │ ├── @ StringNode (location: (13,0)-(14,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (13,0)-(14,0) = "\\\#{}\\\#{}\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\#{}\#{}\n"
+ │ │ ├── @ EmbeddedStatementsNode (location: (14,0)-(14,3))
+ │ │ │ ├── opening_loc: (14,0)-(14,2) = "\#{"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── closing_loc: (14,2)-(14,3) = "}"
+ │ │ ├── @ StringNode (location: (14,3)-(15,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (14,3)-(15,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ ├── @ EmbeddedStatementsNode (location: (15,0)-(15,3))
+ │ │ │ ├── opening_loc: (15,0)-(15,2) = "\#{"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── closing_loc: (15,2)-(15,3) = "}"
+ │ │ ├── @ StringNode (location: (15,3)-(16,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (15,3)-(16,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ ├── @ EmbeddedStatementsNode (location: (16,0)-(16,3))
+ │ │ │ ├── opening_loc: (16,0)-(16,2) = "\#{"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── closing_loc: (16,2)-(16,3) = "}"
+ │ │ └── @ StringNode (location: (16,3)-(17,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (16,3)-(17,0) = "\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\n"
+ │ └── closing_loc: (17,0)-(18,0) = "HEREDOC\n"
+ ├── @ RescueModifierNode (location: (18,0)-(18,21))
+ │ ├── expression:
+ │ │ @ InterpolatedStringNode (location: (18,0)-(18,10))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (18,0)-(18,10) = "<<-HEREDOC"
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ EmbeddedStatementsNode (location: (19,0)-(19,3))
+ │ │ │ │ ├── opening_loc: (19,0)-(19,2) = "\#{"
+ │ │ │ │ ├── statements: ∅
+ │ │ │ │ └── closing_loc: (19,2)-(19,3) = "}"
+ │ │ │ └── @ StringNode (location: (19,3)-(21,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (19,3)-(21,0) = "\na\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\na\n"
+ │ │ └── closing_loc: (21,0)-(22,0) = "HEREDOC\n"
+ │ ├── keyword_loc: (18,11)-(18,17) = "rescue"
+ │ └── rescue_expression:
+ │ @ NilNode (location: (18,18)-(18,21))
+ ├── @ InterpolatedStringNode (location: (22,0)-(22,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (22,0)-(22,1) = "\""
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (22,1)-(22,2))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (22,1)-(22,2) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ └── @ EmbeddedVariableNode (location: (22,2)-(22,5))
+ │ │ ├── operator_loc: (22,2)-(22,3) = "#"
+ │ │ └── variable:
+ │ │ @ NumberedReferenceReadNode (location: (22,3)-(22,5))
+ │ │ └── number: 1
+ │ └── closing_loc: (22,5)-(22,6) = "\""
+ ├── @ InterpolatedStringNode (location: (23,0)-(23,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (23,0)-(23,1) = "\""
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (23,1)-(23,2))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (23,1)-(23,2) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ └── @ EmbeddedVariableNode (location: (23,2)-(23,5))
+ │ │ ├── operator_loc: (23,2)-(23,3) = "#"
+ │ │ └── variable:
+ │ │ @ GlobalVariableReadNode (location: (23,3)-(23,5))
+ │ │ └── name: :$a
+ │ └── closing_loc: (23,5)-(23,6) = "\""
+ ├── @ InterpolatedStringNode (location: (24,0)-(24,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (24,0)-(24,1) = "\""
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (24,1)-(24,2))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (24,1)-(24,2) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ └── @ EmbeddedVariableNode (location: (24,2)-(24,5))
+ │ │ ├── operator_loc: (24,2)-(24,3) = "#"
+ │ │ └── variable:
+ │ │ @ InstanceVariableReadNode (location: (24,3)-(24,5))
+ │ │ └── name: :@a
+ │ └── closing_loc: (24,5)-(24,6) = "\""
+ ├── @ InterpolatedStringNode (location: (25,0)-(25,7))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (25,0)-(25,1) = "\""
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (25,1)-(25,2))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (25,1)-(25,2) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ └── @ EmbeddedVariableNode (location: (25,2)-(25,6))
+ │ │ ├── operator_loc: (25,2)-(25,3) = "#"
+ │ │ └── variable:
+ │ │ @ ClassVariableReadNode (location: (25,3)-(25,6))
+ │ │ └── name: :@@a
+ │ └── closing_loc: (25,6)-(25,7) = "\""
+ ├── @ IfNode (location: (26,0)-(30,3))
+ │ ├── if_keyword_loc: (26,0)-(26,2) = "if"
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (26,3)-(26,7))
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (27,2)-(27,19))
+ │ │ └── body: (length: 1)
+ │ │ └── @ ReturnNode (location: (27,2)-(27,19))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (27,2)-(27,8) = "return"
+ │ │ └── arguments:
+ │ │ @ ArgumentsNode (location: (27,9)-(27,19))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (27,9)-(27,19))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (27,9)-(27,19) = "<<-HEREDOC"
+ │ │ ├── parts: (length: 3)
+ │ │ │ ├── @ StringNode (location: (28,0)-(28,4))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (28,0)-(28,4) = " "
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: " "
+ │ │ │ ├── @ EmbeddedStatementsNode (location: (28,4)-(28,9))
+ │ │ │ │ ├── opening_loc: (28,4)-(28,6) = "\#{"
+ │ │ │ │ ├── statements:
+ │ │ │ │ │ @ StatementsNode (location: (28,6)-(28,8))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ IntegerNode (location: (28,6)-(28,8))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 42
+ │ │ │ │ └── closing_loc: (28,8)-(28,9) = "}"
+ │ │ │ └── @ StringNode (location: (28,9)-(29,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (28,9)-(29,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── closing_loc: (29,0)-(30,0) = " HEREDOC\n"
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (30,0)-(30,3) = "end"
+ ├── @ CallNode (location: (31,0)-(31,15))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (31,0)-(31,3) = "foo"
+ │ ├── opening_loc: (31,3)-(31,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (31,4)-(31,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (31,4)-(31,14))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (31,4)-(31,14) = "<<-HEREDOC"
+ │ │ ├── parts: (length: 3)
+ │ │ │ ├── @ StringNode (location: (32,0)-(32,2))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (32,0)-(32,2) = " "
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: " "
+ │ │ │ ├── @ EmbeddedStatementsNode (location: (32,2)-(32,8))
+ │ │ │ │ ├── opening_loc: (32,2)-(32,4) = "\#{"
+ │ │ │ │ ├── statements:
+ │ │ │ │ │ @ StatementsNode (location: (32,4)-(32,7))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (32,4)-(32,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :bar
+ │ │ │ │ │ ├── message_loc: (32,4)-(32,7) = "bar"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ └── closing_loc: (32,7)-(32,8) = "}"
+ │ │ │ └── @ StringNode (location: (32,8)-(33,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (32,8)-(33,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── closing_loc: (33,0)-(34,0) = "HEREDOC\n"
+ │ ├── closing_loc: (31,14)-(31,15) = ")"
+ │ └── block: ∅
+ └── @ CallNode (location: (34,0)-(37,1))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :foo
+ ├── message_loc: (34,0)-(34,3) = "foo"
+ ├── opening_loc: (34,3)-(34,4) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (34,4)-(34,14))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ InterpolatedStringNode (location: (34,4)-(34,14))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (34,4)-(34,14) = "<<-HEREDOC"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (35,0)-(35,2))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (35,0)-(35,2) = " "
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: " "
+ │ │ ├── @ EmbeddedStatementsNode (location: (35,2)-(35,8))
+ │ │ │ ├── opening_loc: (35,2)-(35,4) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (35,4)-(35,7))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (35,4)-(35,7))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (35,4)-(35,7) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── closing_loc: (35,7)-(35,8) = "}"
+ │ │ └── @ StringNode (location: (35,8)-(36,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (35,8)-(36,0) = "\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\n"
+ │ └── closing_loc: (36,0)-(37,0) = "HEREDOC\n"
+ ├── closing_loc: (34,14)-(34,15) = ")"
+ └── block:
+ @ BlockNode (location: (34,16)-(37,1))
+ ├── locals: [:x]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (34,18)-(34,21))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (34,19)-(34,20))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (34,19)-(34,20))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :x
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (34,18)-(34,19) = "|"
+ │ └── closing_loc: (34,20)-(34,21) = "|"
+ ├── body: ∅
+ ├── opening_loc: (34,16)-(34,17) = "{"
+ └── closing_loc: (37,0)-(37,1) = "}"
diff --git a/test/prism/snapshots/unparser/corpus/literal/empty.txt b/test/prism/snapshots/unparser/corpus/literal/empty.txt
new file mode 100644
index 0000000000..3a21ce5559
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/empty.txt
@@ -0,0 +1,5 @@
+@ ProgramNode (location: (1,0)-(1,0))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,0))
+ └── body: (length: 0)
diff --git a/test/prism/snapshots/unparser/corpus/literal/empty_begin.txt b/test/prism/snapshots/unparser/corpus/literal/empty_begin.txt
new file mode 100644
index 0000000000..838b4bf6f0
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/empty_begin.txt
@@ -0,0 +1,9 @@
+@ ProgramNode (location: (1,0)-(1,2))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,2))
+ └── body: (length: 1)
+ └── @ ParenthesesNode (location: (1,0)-(1,2))
+ ├── body: ∅
+ ├── opening_loc: (1,0)-(1,1) = "("
+ └── closing_loc: (1,1)-(1,2) = ")"
diff --git a/test/prism/snapshots/unparser/corpus/literal/flipflop.txt b/test/prism/snapshots/unparser/corpus/literal/flipflop.txt
new file mode 100644
index 0000000000..2d9f669e6f
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/flipflop.txt
@@ -0,0 +1,237 @@
+@ ProgramNode (location: (1,0)-(10,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(10,3))
+ └── body: (length: 4)
+ ├── @ IfNode (location: (1,0)-(3,3))
+ │ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ │ ├── predicate:
+ │ │ @ ParenthesesNode (location: (1,3)-(1,23))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (1,4)-(1,22))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ FlipFlopNode (location: (1,4)-(1,22))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── left:
+ │ │ │ │ @ ParenthesesNode (location: (1,4)-(1,12))
+ │ │ │ │ ├── body:
+ │ │ │ │ │ @ StatementsNode (location: (1,5)-(1,11))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (1,5)-(1,11))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── receiver:
+ │ │ │ │ │ │ @ CallNode (location: (1,5)-(1,6))
+ │ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ │ ├── name: :i
+ │ │ │ │ │ │ ├── message_loc: (1,5)-(1,6) = "i"
+ │ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ │ └── block: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :==
+ │ │ │ │ │ ├── message_loc: (1,7)-(1,9) = "=="
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments:
+ │ │ │ │ │ │ @ ArgumentsNode (location: (1,10)-(1,11))
+ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ │ └── @ IntegerNode (location: (1,10)-(1,11))
+ │ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ │ └── value: 4
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── opening_loc: (1,4)-(1,5) = "("
+ │ │ │ │ └── closing_loc: (1,11)-(1,12) = ")"
+ │ │ │ ├── right:
+ │ │ │ │ @ ParenthesesNode (location: (1,14)-(1,22))
+ │ │ │ │ ├── body:
+ │ │ │ │ │ @ StatementsNode (location: (1,15)-(1,21))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (1,15)-(1,21))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── receiver:
+ │ │ │ │ │ │ @ CallNode (location: (1,15)-(1,16))
+ │ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ │ ├── name: :i
+ │ │ │ │ │ │ ├── message_loc: (1,15)-(1,16) = "i"
+ │ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ │ └── block: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :==
+ │ │ │ │ │ ├── message_loc: (1,17)-(1,19) = "=="
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments:
+ │ │ │ │ │ │ @ ArgumentsNode (location: (1,20)-(1,21))
+ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ │ └── @ IntegerNode (location: (1,20)-(1,21))
+ │ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ │ └── value: 4
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── opening_loc: (1,14)-(1,15) = "("
+ │ │ │ │ └── closing_loc: (1,21)-(1,22) = ")"
+ │ │ │ └── operator_loc: (1,12)-(1,14) = ".."
+ │ │ ├── opening_loc: (1,3)-(1,4) = "("
+ │ │ └── closing_loc: (1,22)-(1,23) = ")"
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (2,2)-(2,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (2,2)-(2,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (2,2)-(2,5) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (3,0)-(3,3) = "end"
+ ├── @ IfNode (location: (4,0)-(6,3))
+ │ ├── if_keyword_loc: (4,0)-(4,2) = "if"
+ │ ├── predicate:
+ │ │ @ ParenthesesNode (location: (4,3)-(4,24))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (4,4)-(4,23))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ FlipFlopNode (location: (4,4)-(4,23))
+ │ │ │ ├── flags: exclude_end
+ │ │ │ ├── left:
+ │ │ │ │ @ ParenthesesNode (location: (4,4)-(4,12))
+ │ │ │ │ ├── body:
+ │ │ │ │ │ @ StatementsNode (location: (4,5)-(4,11))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (4,5)-(4,11))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── receiver:
+ │ │ │ │ │ │ @ CallNode (location: (4,5)-(4,6))
+ │ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ │ ├── name: :i
+ │ │ │ │ │ │ ├── message_loc: (4,5)-(4,6) = "i"
+ │ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ │ └── block: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :==
+ │ │ │ │ │ ├── message_loc: (4,7)-(4,9) = "=="
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments:
+ │ │ │ │ │ │ @ ArgumentsNode (location: (4,10)-(4,11))
+ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ │ └── @ IntegerNode (location: (4,10)-(4,11))
+ │ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ │ └── value: 4
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── opening_loc: (4,4)-(4,5) = "("
+ │ │ │ │ └── closing_loc: (4,11)-(4,12) = ")"
+ │ │ │ ├── right:
+ │ │ │ │ @ ParenthesesNode (location: (4,15)-(4,23))
+ │ │ │ │ ├── body:
+ │ │ │ │ │ @ StatementsNode (location: (4,16)-(4,22))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (4,16)-(4,22))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── receiver:
+ │ │ │ │ │ │ @ CallNode (location: (4,16)-(4,17))
+ │ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ │ ├── name: :i
+ │ │ │ │ │ │ ├── message_loc: (4,16)-(4,17) = "i"
+ │ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ │ └── block: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :==
+ │ │ │ │ │ ├── message_loc: (4,18)-(4,20) = "=="
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments:
+ │ │ │ │ │ │ @ ArgumentsNode (location: (4,21)-(4,22))
+ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ │ └── @ IntegerNode (location: (4,21)-(4,22))
+ │ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ │ └── value: 4
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── opening_loc: (4,15)-(4,16) = "("
+ │ │ │ │ └── closing_loc: (4,22)-(4,23) = ")"
+ │ │ │ └── operator_loc: (4,12)-(4,15) = "..."
+ │ │ ├── opening_loc: (4,3)-(4,4) = "("
+ │ │ └── closing_loc: (4,23)-(4,24) = ")"
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (5,2)-(5,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (5,2)-(5,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (5,2)-(5,5) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (6,0)-(6,3) = "end"
+ ├── @ IfNode (location: (7,0)-(8,3))
+ │ ├── if_keyword_loc: (7,0)-(7,2) = "if"
+ │ ├── predicate:
+ │ │ @ FlipFlopNode (location: (7,3)-(7,8))
+ │ │ ├── flags: ∅
+ │ │ ├── left: ∅
+ │ │ ├── right:
+ │ │ │ @ CallNode (location: (7,5)-(7,8))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (7,5)-(7,8) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (7,3)-(7,5) = ".."
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements: ∅
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (8,0)-(8,3) = "end"
+ └── @ IfNode (location: (9,0)-(10,3))
+ ├── if_keyword_loc: (9,0)-(9,2) = "if"
+ ├── predicate:
+ │ @ FlipFlopNode (location: (9,3)-(9,8))
+ │ ├── flags: ∅
+ │ ├── left:
+ │ │ @ CallNode (location: (9,3)-(9,6))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (9,3)-(9,6) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right: ∅
+ │ └── operator_loc: (9,6)-(9,8) = ".."
+ ├── then_keyword_loc: ∅
+ ├── statements: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (10,0)-(10,3) = "end"
diff --git a/test/prism/snapshots/unparser/corpus/literal/for.txt b/test/prism/snapshots/unparser/corpus/literal/for.txt
new file mode 100644
index 0000000000..660c6b73f3
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/for.txt
@@ -0,0 +1,171 @@
+@ ProgramNode (location: (1,0)-(12,3))
+├── locals: [:a, :b]
+└── statements:
+ @ StatementsNode (location: (1,0)-(12,3))
+ └── body: (length: 4)
+ ├── @ CallNode (location: (1,0)-(3,4))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (1,0)-(1,3) = "bar"
+ │ ├── opening_loc: (1,3)-(1,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,4)-(3,3))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ ForNode (location: (1,4)-(3,3))
+ │ │ ├── index:
+ │ │ │ @ LocalVariableTargetNode (location: (1,8)-(1,9))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ ├── collection:
+ │ │ │ @ CallNode (location: (1,13)-(1,16))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (1,13)-(1,16) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (2,2)-(2,5))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (2,2)-(2,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (2,2)-(2,5) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── for_keyword_loc: (1,4)-(1,7) = "for"
+ │ │ ├── in_keyword_loc: (1,10)-(1,12) = "in"
+ │ │ ├── do_keyword_loc: (1,17)-(1,19) = "do"
+ │ │ └── end_keyword_loc: (3,0)-(3,3) = "end"
+ │ ├── closing_loc: (3,3)-(3,4) = ")"
+ │ └── block: ∅
+ ├── @ ForNode (location: (4,0)-(6,3))
+ │ ├── index:
+ │ │ @ LocalVariableTargetNode (location: (4,4)-(4,5))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── collection:
+ │ │ @ CallNode (location: (4,9)-(4,12))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (4,9)-(4,12) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (5,2)-(5,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (5,2)-(5,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (5,2)-(5,5) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── for_keyword_loc: (4,0)-(4,3) = "for"
+ │ ├── in_keyword_loc: (4,6)-(4,8) = "in"
+ │ ├── do_keyword_loc: (4,13)-(4,15) = "do"
+ │ └── end_keyword_loc: (6,0)-(6,3) = "end"
+ ├── @ ForNode (location: (7,0)-(9,3))
+ │ ├── index:
+ │ │ @ MultiTargetNode (location: (7,4)-(7,11))
+ │ │ ├── lefts: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (7,5)-(7,6))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ ├── rest:
+ │ │ │ @ SplatNode (location: (7,8)-(7,10))
+ │ │ │ ├── operator_loc: (7,8)-(7,9) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableTargetNode (location: (7,9)-(7,10))
+ │ │ │ ├── name: :b
+ │ │ │ └── depth: 0
+ │ │ ├── rights: (length: 0)
+ │ │ ├── lparen_loc: (7,4)-(7,5) = "("
+ │ │ └── rparen_loc: (7,10)-(7,11) = ")"
+ │ ├── collection:
+ │ │ @ CallNode (location: (7,15)-(7,18))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (7,15)-(7,18) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (8,2)-(8,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (8,2)-(8,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (8,2)-(8,5) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── for_keyword_loc: (7,0)-(7,3) = "for"
+ │ ├── in_keyword_loc: (7,12)-(7,14) = "in"
+ │ ├── do_keyword_loc: (7,19)-(7,21) = "do"
+ │ └── end_keyword_loc: (9,0)-(9,3) = "end"
+ └── @ ForNode (location: (10,0)-(12,3))
+ ├── index:
+ │ @ MultiTargetNode (location: (10,4)-(10,10))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ LocalVariableTargetNode (location: (10,5)-(10,6))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ └── @ LocalVariableTargetNode (location: (10,8)-(10,9))
+ │ │ ├── name: :b
+ │ │ └── depth: 0
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: (10,4)-(10,5) = "("
+ │ └── rparen_loc: (10,9)-(10,10) = ")"
+ ├── collection:
+ │ @ CallNode (location: (10,14)-(10,17))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (10,14)-(10,17) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── statements:
+ │ @ StatementsNode (location: (11,2)-(11,5))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (11,2)-(11,5))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :baz
+ │ ├── message_loc: (11,2)-(11,5) = "baz"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── for_keyword_loc: (10,0)-(10,3) = "for"
+ ├── in_keyword_loc: (10,11)-(10,13) = "in"
+ ├── do_keyword_loc: (10,18)-(10,20) = "do"
+ └── end_keyword_loc: (12,0)-(12,3) = "end"
diff --git a/test/prism/snapshots/unparser/corpus/literal/hookexe.txt b/test/prism/snapshots/unparser/corpus/literal/hookexe.txt
new file mode 100644
index 0000000000..dabedbc588
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/hookexe.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(7,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(7,1))
+ └── body: (length: 3)
+ ├── @ PreExecutionNode (location: (1,0)-(3,1))
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (2,2)-(2,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (2,2)-(2,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (2,2)-(2,5) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (1,0)-(1,5) = "BEGIN"
+ │ ├── opening_loc: (1,6)-(1,7) = "{"
+ │ └── closing_loc: (3,0)-(3,1) = "}"
+ ├── @ CallNode (location: (4,0)-(4,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (4,0)-(4,3) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ PostExecutionNode (location: (5,0)-(7,1))
+ ├── statements:
+ │ @ StatementsNode (location: (6,2)-(6,5))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (6,2)-(6,5))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :baz
+ │ ├── message_loc: (6,2)-(6,5) = "baz"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── keyword_loc: (5,0)-(5,3) = "END"
+ ├── opening_loc: (5,4)-(5,5) = "{"
+ └── closing_loc: (7,0)-(7,1) = "}"
diff --git a/test/prism/snapshots/unparser/corpus/literal/if.txt b/test/prism/snapshots/unparser/corpus/literal/if.txt
new file mode 100644
index 0000000000..00eeba179c
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/if.txt
@@ -0,0 +1,288 @@
+@ ProgramNode (location: (1,0)-(36,3))
+├── locals: [:foo, :pair]
+└── statements:
+ @ StatementsNode (location: (1,0)-(36,3))
+ └── body: (length: 10)
+ ├── @ IfNode (location: (1,0)-(3,3))
+ │ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ │ ├── predicate:
+ │ │ @ MatchLastLineNode (location: (1,3)-(1,8))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,3)-(1,4) = "/"
+ │ │ ├── content_loc: (1,4)-(1,7) = "foo"
+ │ │ ├── closing_loc: (1,7)-(1,8) = "/"
+ │ │ └── unescaped: "foo"
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (2,2)-(2,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (2,2)-(2,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (2,2)-(2,5) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (3,0)-(3,3) = "end"
+ ├── @ IfNode (location: (4,0)-(6,3))
+ │ ├── if_keyword_loc: (4,0)-(4,2) = "if"
+ │ ├── predicate:
+ │ │ @ IntegerNode (location: (4,3)-(4,4))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (5,2)-(5,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (5,2)-(5,3))
+ │ │ ├── flags: decimal
+ │ │ └── value: 9
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (6,0)-(6,3) = "end"
+ ├── @ IfNode (location: (7,0)-(11,3))
+ │ ├── if_keyword_loc: (7,0)-(7,2) = "if"
+ │ ├── predicate:
+ │ │ @ IntegerNode (location: (7,3)-(7,4))
+ │ │ ├── flags: decimal
+ │ │ └── value: 4
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (8,2)-(8,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (8,2)-(8,3))
+ │ │ ├── flags: decimal
+ │ │ └── value: 5
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (9,0)-(11,3))
+ │ │ ├── else_keyword_loc: (9,0)-(9,4) = "else"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (10,2)-(10,3))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (10,2)-(10,3))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 6
+ │ │ └── end_keyword_loc: (11,0)-(11,3) = "end"
+ │ └── end_keyword_loc: (11,0)-(11,3) = "end"
+ ├── @ UnlessNode (location: (12,0)-(14,3))
+ │ ├── keyword_loc: (12,0)-(12,6) = "unless"
+ │ ├── predicate:
+ │ │ @ IntegerNode (location: (12,7)-(12,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (13,2)-(13,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NilNode (location: (13,2)-(13,5))
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (14,0)-(14,3) = "end"
+ ├── @ UnlessNode (location: (15,0)-(17,3))
+ │ ├── keyword_loc: (15,0)-(15,6) = "unless"
+ │ ├── predicate:
+ │ │ @ IntegerNode (location: (15,7)-(15,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (16,2)-(16,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (16,2)-(16,3))
+ │ │ ├── flags: decimal
+ │ │ └── value: 9
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (17,0)-(17,3) = "end"
+ ├── @ IfNode (location: (18,0)-(19,3))
+ │ ├── if_keyword_loc: (18,0)-(18,2) = "if"
+ │ ├── predicate:
+ │ │ @ CallNode (location: (18,3)-(18,6))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (18,3)-(18,6) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements: ∅
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (19,0)-(19,3) = "end"
+ ├── @ ModuleNode (location: (21,0)-(23,3))
+ │ ├── locals: [:foo]
+ │ ├── module_keyword_loc: (21,0)-(21,6) = "module"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (21,7)-(21,8))
+ │ │ └── name: :A
+ │ ├── body:
+ │ │ @ StatementsNode (location: (22,2)-(22,18))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IfNode (location: (22,2)-(22,18))
+ │ │ ├── if_keyword_loc: (22,12)-(22,14) = "if"
+ │ │ ├── predicate:
+ │ │ │ @ LocalVariableReadNode (location: (22,15)-(22,18))
+ │ │ │ ├── name: :foo
+ │ │ │ └── depth: 0
+ │ │ ├── then_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (22,2)-(22,11))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ LocalVariableWriteNode (location: (22,2)-(22,11))
+ │ │ │ ├── name: :foo
+ │ │ │ ├── depth: 0
+ │ │ │ ├── name_loc: (22,2)-(22,5) = "foo"
+ │ │ │ ├── value:
+ │ │ │ │ @ CallNode (location: (22,8)-(22,11))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (22,8)-(22,11) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── operator_loc: (22,6)-(22,7) = "="
+ │ │ ├── consequent: ∅
+ │ │ └── end_keyword_loc: ∅
+ │ ├── end_keyword_loc: (23,0)-(23,3) = "end"
+ │ └── name: :A
+ ├── @ ModuleNode (location: (25,0)-(27,3))
+ │ ├── locals: [:foo]
+ │ ├── module_keyword_loc: (25,0)-(25,6) = "module"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (25,7)-(25,8))
+ │ │ └── name: :B
+ │ ├── body:
+ │ │ @ StatementsNode (location: (26,2)-(26,22))
+ │ │ └── body: (length: 1)
+ │ │ └── @ UnlessNode (location: (26,2)-(26,22))
+ │ │ ├── keyword_loc: (26,12)-(26,18) = "unless"
+ │ │ ├── predicate:
+ │ │ │ @ LocalVariableReadNode (location: (26,19)-(26,22))
+ │ │ │ ├── name: :foo
+ │ │ │ └── depth: 0
+ │ │ ├── then_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (26,2)-(26,11))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ LocalVariableWriteNode (location: (26,2)-(26,11))
+ │ │ │ ├── name: :foo
+ │ │ │ ├── depth: 0
+ │ │ │ ├── name_loc: (26,2)-(26,5) = "foo"
+ │ │ │ ├── value:
+ │ │ │ │ @ CallNode (location: (26,8)-(26,11))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (26,8)-(26,11) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── operator_loc: (26,6)-(26,7) = "="
+ │ │ ├── consequent: ∅
+ │ │ └── end_keyword_loc: ∅
+ │ ├── end_keyword_loc: (27,0)-(27,3) = "end"
+ │ └── name: :B
+ ├── @ UnlessNode (location: (28,0)-(30,3))
+ │ ├── keyword_loc: (28,0)-(28,6) = "unless"
+ │ ├── predicate:
+ │ │ @ CallNode (location: (28,7)-(28,10))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (28,7)-(28,10) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (29,2)-(29,11))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableWriteNode (location: (29,2)-(29,11))
+ │ │ ├── name: :foo
+ │ │ ├── depth: 0
+ │ │ ├── name_loc: (29,2)-(29,5) = "foo"
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (29,8)-(29,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (29,8)-(29,11) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (29,6)-(29,7) = "="
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (30,0)-(30,3) = "end"
+ └── @ IfNode (location: (31,0)-(36,3))
+ ├── if_keyword_loc: (31,0)-(31,2) = "if"
+ ├── predicate:
+ │ @ CallNode (location: (31,3)-(33,1))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (31,3)-(31,6) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (31,7)-(33,1))
+ │ ├── locals: [:pair]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (31,9)-(31,15))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (31,10)-(31,14))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (31,10)-(31,14))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :pair
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (31,9)-(31,10) = "|"
+ │ │ └── closing_loc: (31,14)-(31,15) = "|"
+ │ ├── body:
+ │ │ @ StatementsNode (location: (32,2)-(32,6))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (32,2)-(32,6))
+ │ │ ├── name: :pair
+ │ │ └── depth: 0
+ │ ├── opening_loc: (31,7)-(31,8) = "{"
+ │ └── closing_loc: (33,0)-(33,1) = "}"
+ ├── then_keyword_loc: ∅
+ ├── statements:
+ │ @ StatementsNode (location: (34,2)-(35,5))
+ │ └── body: (length: 2)
+ │ ├── @ LocalVariableWriteNode (location: (34,2)-(34,13))
+ │ │ ├── name: :pair
+ │ │ ├── depth: 0
+ │ │ ├── name_loc: (34,2)-(34,6) = "pair"
+ │ │ ├── value:
+ │ │ │ @ SymbolNode (location: (34,9)-(34,13))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (34,9)-(34,10) = ":"
+ │ │ │ ├── value_loc: (34,10)-(34,13) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ └── operator_loc: (34,7)-(34,8) = "="
+ │ └── @ LocalVariableReadNode (location: (35,2)-(35,5))
+ │ ├── name: :foo
+ │ └── depth: 0
+ ├── consequent: ∅
+ └── end_keyword_loc: (36,0)-(36,3) = "end"
diff --git a/test/prism/snapshots/unparser/corpus/literal/kwbegin.txt b/test/prism/snapshots/unparser/corpus/literal/kwbegin.txt
new file mode 100644
index 0000000000..48e53af00e
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/kwbegin.txt
@@ -0,0 +1,491 @@
+@ ProgramNode (location: (1,0)-(80,3))
+├── locals: [:foo, :bar, :exception]
+└── statements:
+ @ StatementsNode (location: (1,0)-(80,3))
+ └── body: (length: 14)
+ ├── @ BeginNode (location: (1,0)-(3,3))
+ │ ├── begin_keyword_loc: (1,0)-(1,5) = "begin"
+ │ ├── statements: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (2,0)-(2,6))
+ │ │ ├── keyword_loc: (2,0)-(2,6) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (3,0)-(3,3) = "end"
+ ├── @ BeginNode (location: (5,0)-(7,3))
+ │ ├── begin_keyword_loc: (5,0)-(5,5) = "begin"
+ │ ├── statements: ∅
+ │ ├── rescue_clause: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause:
+ │ │ @ EnsureNode (location: (6,0)-(7,3))
+ │ │ ├── ensure_keyword_loc: (6,0)-(6,6) = "ensure"
+ │ │ ├── statements: ∅
+ │ │ └── end_keyword_loc: (7,0)-(7,3) = "end"
+ │ └── end_keyword_loc: (7,0)-(7,3) = "end"
+ ├── @ BeginNode (location: (9,0)-(11,3))
+ │ ├── begin_keyword_loc: (9,0)-(9,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (10,2)-(10,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (10,2)-(10,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (10,2)-(10,3) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (11,0)-(11,3) = "end"
+ ├── @ BeginNode (location: (13,0)-(17,3))
+ │ ├── begin_keyword_loc: (13,0)-(13,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (14,2)-(14,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (14,2)-(14,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (14,2)-(14,3) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (15,0)-(16,3))
+ │ │ ├── keyword_loc: (15,0)-(15,6) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (16,2)-(16,3))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (16,2)-(16,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (16,2)-(16,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (17,0)-(17,3) = "end"
+ ├── @ BeginNode (location: (19,0)-(24,3))
+ │ ├── begin_keyword_loc: (19,0)-(19,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (20,2)-(21,3))
+ │ │ └── body: (length: 2)
+ │ │ ├── @ CallNode (location: (20,2)-(20,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (20,2)-(20,3) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ CallNode (location: (21,2)-(21,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (21,2)-(21,3) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (22,0)-(23,3))
+ │ │ ├── keyword_loc: (22,0)-(22,6) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (23,2)-(23,3))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (23,2)-(23,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (23,2)-(23,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (24,0)-(24,3) = "end"
+ ├── @ BeginNode (location: (26,0)-(28,3))
+ │ ├── begin_keyword_loc: (26,0)-(26,5) = "begin"
+ │ ├── statements: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (27,0)-(27,8))
+ │ │ ├── keyword_loc: (27,0)-(27,6) = "rescue"
+ │ │ ├── exceptions: (length: 1)
+ │ │ │ └── @ ConstantReadNode (location: (27,7)-(27,8))
+ │ │ │ └── name: :A
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (28,0)-(28,3) = "end"
+ ├── @ BeginNode (location: (30,0)-(32,3))
+ │ ├── begin_keyword_loc: (30,0)-(30,5) = "begin"
+ │ ├── statements: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (31,0)-(31,15))
+ │ │ ├── keyword_loc: (31,0)-(31,6) = "rescue"
+ │ │ ├── exceptions: (length: 1)
+ │ │ │ └── @ ConstantReadNode (location: (31,7)-(31,8))
+ │ │ │ └── name: :A
+ │ │ ├── operator_loc: (31,9)-(31,11) = "=>"
+ │ │ ├── reference:
+ │ │ │ @ LocalVariableTargetNode (location: (31,12)-(31,15))
+ │ │ │ ├── name: :foo
+ │ │ │ └── depth: 0
+ │ │ ├── statements: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (32,0)-(32,3) = "end"
+ ├── @ BeginNode (location: (34,0)-(42,3))
+ │ ├── begin_keyword_loc: (34,0)-(34,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (35,2)-(35,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (35,2)-(35,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (35,2)-(35,3) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (36,0)-(39,3))
+ │ │ ├── keyword_loc: (36,0)-(36,6) = "rescue"
+ │ │ ├── exceptions: (length: 1)
+ │ │ │ └── @ ConstantReadNode (location: (36,7)-(36,8))
+ │ │ │ └── name: :A
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (37,2)-(37,3))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (37,2)-(37,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (37,2)-(37,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent:
+ │ │ @ RescueNode (location: (38,0)-(39,3))
+ │ │ ├── keyword_loc: (38,0)-(38,6) = "rescue"
+ │ │ ├── exceptions: (length: 1)
+ │ │ │ └── @ ConstantReadNode (location: (38,7)-(38,8))
+ │ │ │ └── name: :B
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (39,2)-(39,3))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (39,2)-(39,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── message_loc: (39,2)-(39,3) = "c"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause:
+ │ │ @ EnsureNode (location: (40,0)-(42,3))
+ │ │ ├── ensure_keyword_loc: (40,0)-(40,6) = "ensure"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (41,2)-(41,3))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (41,2)-(41,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :d
+ │ │ │ ├── message_loc: (41,2)-(41,3) = "d"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── end_keyword_loc: (42,0)-(42,3) = "end"
+ │ └── end_keyword_loc: (42,0)-(42,3) = "end"
+ ├── @ BeginNode (location: (44,0)-(53,3))
+ │ ├── begin_keyword_loc: (44,0)-(44,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (45,2)-(49,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BeginNode (location: (45,2)-(49,5))
+ │ │ ├── begin_keyword_loc: (45,2)-(45,7) = "begin"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (46,4)-(47,7))
+ │ │ │ └── body: (length: 2)
+ │ │ │ ├── @ LocalVariableReadNode (location: (46,4)-(46,7))
+ │ │ │ │ ├── name: :foo
+ │ │ │ │ └── depth: 0
+ │ │ │ └── @ CallNode (location: (47,4)-(47,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (47,4)-(47,7) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── rescue_clause:
+ │ │ │ @ RescueNode (location: (48,2)-(48,8))
+ │ │ │ ├── keyword_loc: (48,2)-(48,8) = "rescue"
+ │ │ │ ├── exceptions: (length: 0)
+ │ │ │ ├── operator_loc: ∅
+ │ │ │ ├── reference: ∅
+ │ │ │ ├── statements: ∅
+ │ │ │ └── consequent: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause: ∅
+ │ │ └── end_keyword_loc: (49,2)-(49,5) = "end"
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (50,0)-(52,5))
+ │ │ ├── keyword_loc: (50,0)-(50,6) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (51,2)-(52,5))
+ │ │ │ └── body: (length: 2)
+ │ │ │ ├── @ CallNode (location: (51,2)-(51,5))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ ├── message_loc: (51,2)-(51,5) = "baz"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── @ CallNode (location: (52,2)-(52,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (52,2)-(52,5) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (53,0)-(53,3) = "end"
+ ├── @ BeginNode (location: (55,0)-(58,3))
+ │ ├── begin_keyword_loc: (55,0)-(55,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (56,2)-(56,35))
+ │ │ └── body: (length: 1)
+ │ │ └── @ RescueModifierNode (location: (56,2)-(56,35))
+ │ │ ├── expression:
+ │ │ │ @ CallNode (location: (56,2)-(56,18))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :raise
+ │ │ │ ├── message_loc: (56,2)-(56,7) = "raise"
+ │ │ │ ├── opening_loc: (56,7)-(56,8) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (56,8)-(56,17))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ ConstantReadNode (location: (56,8)-(56,17))
+ │ │ │ │ └── name: :Exception
+ │ │ │ ├── closing_loc: (56,17)-(56,18) = ")"
+ │ │ │ └── block: ∅
+ │ │ ├── keyword_loc: (56,19)-(56,25) = "rescue"
+ │ │ └── rescue_expression:
+ │ │ @ LocalVariableWriteNode (location: (56,26)-(56,35))
+ │ │ ├── name: :foo
+ │ │ ├── depth: 0
+ │ │ ├── name_loc: (56,26)-(56,29) = "foo"
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (56,32)-(56,35))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (56,32)-(56,35) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (56,30)-(56,31) = "="
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (57,0)-(57,16))
+ │ │ ├── keyword_loc: (57,0)-(57,6) = "rescue"
+ │ │ ├── exceptions: (length: 1)
+ │ │ │ └── @ ConstantReadNode (location: (57,7)-(57,16))
+ │ │ │ └── name: :Exception
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (58,0)-(58,3) = "end"
+ ├── @ BeginNode (location: (60,0)-(64,3))
+ │ ├── begin_keyword_loc: (60,0)-(60,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (61,2)-(61,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (61,2)-(61,5))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (62,0)-(63,5))
+ │ │ ├── keyword_loc: (62,0)-(62,6) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: (62,7)-(62,9) = "=>"
+ │ │ ├── reference:
+ │ │ │ @ LocalVariableTargetNode (location: (62,10)-(62,13))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (63,2)-(63,5))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ LocalVariableReadNode (location: (63,2)-(63,5))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (64,0)-(64,3) = "end"
+ ├── @ BeginNode (location: (66,0)-(70,3))
+ │ ├── begin_keyword_loc: (66,0)-(66,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (67,2)-(67,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (67,2)-(67,5))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (68,0)-(69,5))
+ │ │ ├── keyword_loc: (68,0)-(68,6) = "rescue"
+ │ │ ├── exceptions: (length: 2)
+ │ │ │ ├── @ ConstantReadNode (location: (68,7)-(68,16))
+ │ │ │ │ └── name: :Exception
+ │ │ │ └── @ ConstantReadNode (location: (68,18)-(68,23))
+ │ │ │ └── name: :Other
+ │ │ ├── operator_loc: (68,24)-(68,26) = "=>"
+ │ │ ├── reference:
+ │ │ │ @ LocalVariableTargetNode (location: (68,27)-(68,30))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (69,2)-(69,5))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ LocalVariableReadNode (location: (69,2)-(69,5))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (70,0)-(70,3) = "end"
+ ├── @ BeginNode (location: (72,0)-(76,3))
+ │ ├── begin_keyword_loc: (72,0)-(72,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (73,2)-(73,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (73,2)-(73,5))
+ │ │ ├── name: :bar
+ │ │ └── depth: 0
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (74,0)-(75,5))
+ │ │ ├── keyword_loc: (74,0)-(74,6) = "rescue"
+ │ │ ├── exceptions: (length: 2)
+ │ │ │ ├── @ ConstantReadNode (location: (74,7)-(74,16))
+ │ │ │ │ └── name: :SomeError
+ │ │ │ └── @ SplatNode (location: (74,18)-(74,22))
+ │ │ │ ├── operator_loc: (74,18)-(74,19) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableReadNode (location: (74,19)-(74,22))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── operator_loc: (74,23)-(74,25) = "=>"
+ │ │ ├── reference:
+ │ │ │ @ LocalVariableTargetNode (location: (74,26)-(74,35))
+ │ │ │ ├── name: :exception
+ │ │ │ └── depth: 0
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (75,2)-(75,5))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (75,2)-(75,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (75,2)-(75,5) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (76,0)-(76,3) = "end"
+ └── @ SingletonClassNode (location: (78,0)-(80,3))
+ ├── locals: []
+ ├── class_keyword_loc: (78,0)-(78,5) = "class"
+ ├── operator_loc: (78,6)-(78,8) = "<<"
+ ├── expression:
+ │ @ SelfNode (location: (78,9)-(78,13))
+ ├── body:
+ │ @ StatementsNode (location: (79,2)-(79,23))
+ │ └── body: (length: 1)
+ │ └── @ RescueModifierNode (location: (79,2)-(79,23))
+ │ ├── expression:
+ │ │ @ UndefNode (location: (79,2)-(79,12))
+ │ │ ├── names: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (79,8)-(79,12))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (79,8)-(79,9) = ":"
+ │ │ │ ├── value_loc: (79,9)-(79,12) = "bar"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "bar"
+ │ │ └── keyword_loc: (79,2)-(79,7) = "undef"
+ │ ├── keyword_loc: (79,13)-(79,19) = "rescue"
+ │ └── rescue_expression:
+ │ @ NilNode (location: (79,20)-(79,23))
+ └── end_keyword_loc: (80,0)-(80,3) = "end"
diff --git a/test/prism/snapshots/unparser/corpus/literal/lambda.txt b/test/prism/snapshots/unparser/corpus/literal/lambda.txt
new file mode 100644
index 0000000000..3594787bca
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/lambda.txt
@@ -0,0 +1,151 @@
+@ ProgramNode (location: (1,0)-(13,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(13,1))
+ └── body: (length: 6)
+ ├── @ CallNode (location: (1,0)-(2,1))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :lambda
+ │ ├── message_loc: (1,0)-(1,6) = "lambda"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,7)-(2,1))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,7)-(1,8) = "{"
+ │ └── closing_loc: (2,0)-(2,1) = "}"
+ ├── @ CallNode (location: (3,0)-(5,1))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :lambda
+ │ ├── message_loc: (3,0)-(3,6) = "lambda"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (3,7)-(5,1))
+ │ ├── locals: [:a, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (3,9)-(3,15))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (3,10)-(3,14))
+ │ │ │ ├── requireds: (length: 2)
+ │ │ │ │ ├── @ RequiredParameterNode (location: (3,10)-(3,11))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :a
+ │ │ │ │ └── @ RequiredParameterNode (location: (3,13)-(3,14))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :b
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (3,9)-(3,10) = "|"
+ │ │ └── closing_loc: (3,14)-(3,15) = "|"
+ │ ├── body:
+ │ │ @ StatementsNode (location: (4,2)-(4,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (4,2)-(4,3))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── opening_loc: (3,7)-(3,8) = "{"
+ │ └── closing_loc: (5,0)-(5,1) = "}"
+ ├── @ LambdaNode (location: (6,0)-(7,1))
+ │ ├── locals: []
+ │ ├── operator_loc: (6,0)-(6,2) = "->"
+ │ ├── opening_loc: (6,5)-(6,6) = "{"
+ │ ├── closing_loc: (7,0)-(7,1) = "}"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (6,2)-(6,4))
+ │ │ ├── parameters: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (6,2)-(6,3) = "("
+ │ │ └── closing_loc: (6,3)-(6,4) = ")"
+ │ └── body: ∅
+ ├── @ LambdaNode (location: (8,0)-(9,1))
+ │ ├── locals: [:a]
+ │ ├── operator_loc: (8,0)-(8,2) = "->"
+ │ ├── opening_loc: (8,6)-(8,7) = "{"
+ │ ├── closing_loc: (9,0)-(9,1) = "}"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (8,2)-(8,5))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (8,3)-(8,4))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (8,3)-(8,4))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (8,2)-(8,3) = "("
+ │ │ └── closing_loc: (8,4)-(8,5) = ")"
+ │ └── body: ∅
+ ├── @ LambdaNode (location: (10,0)-(11,1))
+ │ ├── locals: [:a, :b]
+ │ ├── operator_loc: (10,0)-(10,2) = "->"
+ │ ├── opening_loc: (10,9)-(10,10) = "{"
+ │ ├── closing_loc: (11,0)-(11,1) = "}"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (10,2)-(10,8))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (10,3)-(10,7))
+ │ │ │ ├── requireds: (length: 2)
+ │ │ │ │ ├── @ RequiredParameterNode (location: (10,3)-(10,4))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :a
+ │ │ │ │ └── @ RequiredParameterNode (location: (10,6)-(10,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :b
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (10,2)-(10,3) = "("
+ │ │ └── closing_loc: (10,7)-(10,8) = ")"
+ │ └── body: ∅
+ └── @ LambdaNode (location: (12,0)-(13,1))
+ ├── locals: [:a, :b, :c]
+ ├── operator_loc: (12,0)-(12,2) = "->"
+ ├── opening_loc: (12,12)-(12,13) = "{"
+ ├── closing_loc: (13,0)-(13,1) = "}"
+ ├── parameters:
+ │ @ BlockParametersNode (location: (12,2)-(12,11))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (12,3)-(12,7))
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ RequiredParameterNode (location: (12,3)-(12,4))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ └── @ RequiredParameterNode (location: (12,6)-(12,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :b
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 1)
+ │ │ └── @ BlockLocalVariableNode (location: (12,9)-(12,10))
+ │ │ ├── flags: ∅
+ │ │ └── name: :c
+ │ ├── opening_loc: (12,2)-(12,3) = "("
+ │ └── closing_loc: (12,10)-(12,11) = ")"
+ └── body: ∅
diff --git a/test/prism/snapshots/unparser/corpus/literal/literal.txt b/test/prism/snapshots/unparser/corpus/literal/literal.txt
new file mode 100644
index 0000000000..dcf00bf4e0
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/literal.txt
@@ -0,0 +1,1198 @@
+@ ProgramNode (location: (1,0)-(91,2))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(91,2))
+ └── body: (length: 78)
+ ├── @ HashNode (location: (1,0)-(1,38))
+ │ ├── opening_loc: (1,0)-(1,1) = "{"
+ │ ├── elements: (length: 2)
+ │ │ ├── @ AssocNode (location: (1,2)-(1,21))
+ │ │ │ ├── key:
+ │ │ │ │ @ StringNode (location: (1,2)-(1,7))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: (1,2)-(1,3) = "\""
+ │ │ │ │ ├── content_loc: (1,3)-(1,6) = "foo"
+ │ │ │ │ ├── closing_loc: (1,6)-(1,7) = "\""
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── value:
+ │ │ │ │ @ InterpolatedStringNode (location: (1,11)-(1,21))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: (1,11)-(1,21) = "<<-HEREDOC"
+ │ │ │ │ ├── parts: (length: 3)
+ │ │ │ │ │ ├── @ StringNode (location: (2,0)-(2,2))
+ │ │ │ │ │ │ ├── flags: frozen
+ │ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ │ ├── content_loc: (2,0)-(2,2) = " "
+ │ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ │ └── unescaped: " "
+ │ │ │ │ │ ├── @ EmbeddedStatementsNode (location: (2,2)-(2,5))
+ │ │ │ │ │ │ ├── opening_loc: (2,2)-(2,4) = "\#{"
+ │ │ │ │ │ │ ├── statements: ∅
+ │ │ │ │ │ │ └── closing_loc: (2,4)-(2,5) = "}"
+ │ │ │ │ │ └── @ StringNode (location: (2,5)-(3,0))
+ │ │ │ │ │ ├── flags: frozen
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── content_loc: (2,5)-(3,0) = "\n"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "\n"
+ │ │ │ │ └── closing_loc: (3,0)-(4,0) = "HEREDOC\n"
+ │ │ │ └── operator_loc: (1,8)-(1,10) = "=>"
+ │ │ └── @ AssocNode (location: (1,23)-(1,36))
+ │ │ ├── key:
+ │ │ │ @ StringNode (location: (1,23)-(1,28))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: (1,23)-(1,24) = "\""
+ │ │ │ ├── content_loc: (1,24)-(1,27) = "bar"
+ │ │ │ ├── closing_loc: (1,27)-(1,28) = "\""
+ │ │ │ └── unescaped: "bar"
+ │ │ ├── value:
+ │ │ │ @ SymbolNode (location: (1,32)-(1,36))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (1,32)-(1,33) = ":"
+ │ │ │ ├── value_loc: (1,33)-(1,36) = "baz"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "baz"
+ │ │ └── operator_loc: (1,29)-(1,31) = "=>"
+ │ └── closing_loc: (1,37)-(1,38) = "}"
+ ├── @ HashNode (location: (4,0)-(4,31))
+ │ ├── opening_loc: (4,0)-(4,1) = "{"
+ │ ├── elements: (length: 2)
+ │ │ ├── @ AssocNode (location: (4,2)-(4,14))
+ │ │ │ ├── key:
+ │ │ │ │ @ StringNode (location: (4,2)-(4,7))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: (4,2)-(4,3) = "\""
+ │ │ │ │ ├── content_loc: (4,3)-(4,6) = "foo"
+ │ │ │ │ ├── closing_loc: (4,6)-(4,7) = "\""
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── value:
+ │ │ │ │ @ StringNode (location: (4,11)-(4,14))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: (4,11)-(4,13) = "%("
+ │ │ │ │ ├── content_loc: (4,13)-(4,13) = ""
+ │ │ │ │ ├── closing_loc: (4,13)-(4,14) = ")"
+ │ │ │ │ └── unescaped: ""
+ │ │ │ └── operator_loc: (4,8)-(4,10) = "=>"
+ │ │ └── @ AssocNode (location: (4,16)-(4,29))
+ │ │ ├── key:
+ │ │ │ @ StringNode (location: (4,16)-(4,21))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: (4,16)-(4,17) = "\""
+ │ │ │ ├── content_loc: (4,17)-(4,20) = "bar"
+ │ │ │ ├── closing_loc: (4,20)-(4,21) = "\""
+ │ │ │ └── unescaped: "bar"
+ │ │ ├── value:
+ │ │ │ @ SymbolNode (location: (4,25)-(4,29))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (4,25)-(4,26) = ":"
+ │ │ │ ├── value_loc: (4,26)-(4,29) = "baz"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "baz"
+ │ │ └── operator_loc: (4,22)-(4,24) = "=>"
+ │ └── closing_loc: (4,30)-(4,31) = "}"
+ ├── @ ArrayNode (location: (5,0)-(5,12))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ StringNode (location: (5,1)-(5,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (5,1)-(5,2) = "\""
+ │ │ │ ├── content_loc: (5,2)-(5,5) = "foo"
+ │ │ │ ├── closing_loc: (5,5)-(5,6) = "\""
+ │ │ │ └── unescaped: "foo"
+ │ │ └── @ StringNode (location: (5,8)-(5,11))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (5,8)-(5,10) = "%("
+ │ │ ├── content_loc: (5,10)-(5,10) = ""
+ │ │ ├── closing_loc: (5,10)-(5,11) = ")"
+ │ │ └── unescaped: ""
+ │ ├── opening_loc: (5,0)-(5,1) = "["
+ │ └── closing_loc: (5,11)-(5,12) = "]"
+ ├── @ CallNode (location: (6,0)-(6,15))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (6,0)-(6,13))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (6,0)-(6,1) = "a"
+ │ │ ├── opening_loc: (6,1)-(6,2) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (6,2)-(6,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ InterpolatedStringNode (location: (6,2)-(6,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (6,2)-(6,12) = "<<-HEREDOC"
+ │ │ │ ├── parts: (length: 3)
+ │ │ │ │ ├── @ StringNode (location: (7,0)-(7,2))
+ │ │ │ │ │ ├── flags: frozen
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── content_loc: (7,0)-(7,2) = " "
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: " "
+ │ │ │ │ ├── @ EmbeddedStatementsNode (location: (7,2)-(7,5))
+ │ │ │ │ │ ├── opening_loc: (7,2)-(7,4) = "\#{"
+ │ │ │ │ │ ├── statements: ∅
+ │ │ │ │ │ └── closing_loc: (7,4)-(7,5) = "}"
+ │ │ │ │ └── @ StringNode (location: (7,5)-(8,0))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (7,5)-(8,0) = "\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "\n"
+ │ │ │ └── closing_loc: (8,0)-(9,0) = "HEREDOC\n"
+ │ │ ├── closing_loc: (6,12)-(6,13) = ")"
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (6,13)-(6,14) = "."
+ │ ├── name: :a
+ │ ├── message_loc: (6,14)-(6,15) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (9,0)-(9,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (9,0)-(9,6))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (9,0)-(9,1) = "a"
+ │ │ ├── opening_loc: (9,1)-(9,2) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (9,2)-(9,5))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ StringNode (location: (9,2)-(9,5))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (9,2)-(9,4) = "%("
+ │ │ │ ├── content_loc: (9,4)-(9,4) = ""
+ │ │ │ ├── closing_loc: (9,4)-(9,5) = ")"
+ │ │ │ └── unescaped: ""
+ │ │ ├── closing_loc: (9,5)-(9,6) = ")"
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (9,6)-(9,7) = "."
+ │ ├── name: :a
+ │ ├── message_loc: (9,7)-(9,8) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ HashNode (location: (10,0)-(10,30))
+ │ ├── opening_loc: (10,0)-(10,1) = "{"
+ │ ├── elements: (length: 2)
+ │ │ ├── @ AssocNode (location: (10,2)-(10,21))
+ │ │ │ ├── key:
+ │ │ │ │ @ StringNode (location: (10,2)-(10,7))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: (10,2)-(10,3) = "\""
+ │ │ │ │ ├── content_loc: (10,3)-(10,6) = "foo"
+ │ │ │ │ ├── closing_loc: (10,6)-(10,7) = "\""
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── value:
+ │ │ │ │ @ InterpolatedStringNode (location: (10,11)-(10,21))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: (10,11)-(10,21) = "<<-HEREDOC"
+ │ │ │ │ ├── parts: (length: 3)
+ │ │ │ │ │ ├── @ StringNode (location: (11,0)-(11,2))
+ │ │ │ │ │ │ ├── flags: frozen
+ │ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ │ ├── content_loc: (11,0)-(11,2) = " "
+ │ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ │ └── unescaped: " "
+ │ │ │ │ │ ├── @ EmbeddedStatementsNode (location: (11,2)-(11,5))
+ │ │ │ │ │ │ ├── opening_loc: (11,2)-(11,4) = "\#{"
+ │ │ │ │ │ │ ├── statements: ∅
+ │ │ │ │ │ │ └── closing_loc: (11,4)-(11,5) = "}"
+ │ │ │ │ │ └── @ StringNode (location: (11,5)-(12,0))
+ │ │ │ │ │ ├── flags: frozen
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── content_loc: (11,5)-(12,0) = "\n"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "\n"
+ │ │ │ │ └── closing_loc: (12,0)-(13,0) = "HEREDOC\n"
+ │ │ │ └── operator_loc: (10,8)-(10,10) = "=>"
+ │ │ └── @ AssocSplatNode (location: (10,23)-(10,28))
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (10,25)-(10,28))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (10,25)-(10,28) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (10,23)-(10,25) = "**"
+ │ └── closing_loc: (10,29)-(10,30) = "}"
+ ├── @ HashNode (location: (13,0)-(13,23))
+ │ ├── opening_loc: (13,0)-(13,1) = "{"
+ │ ├── elements: (length: 2)
+ │ │ ├── @ AssocNode (location: (13,2)-(13,14))
+ │ │ │ ├── key:
+ │ │ │ │ @ StringNode (location: (13,2)-(13,7))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: (13,2)-(13,3) = "\""
+ │ │ │ │ ├── content_loc: (13,3)-(13,6) = "foo"
+ │ │ │ │ ├── closing_loc: (13,6)-(13,7) = "\""
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── value:
+ │ │ │ │ @ StringNode (location: (13,11)-(13,14))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: (13,11)-(13,13) = "%("
+ │ │ │ │ ├── content_loc: (13,13)-(13,13) = ""
+ │ │ │ │ ├── closing_loc: (13,13)-(13,14) = ")"
+ │ │ │ │ └── unescaped: ""
+ │ │ │ └── operator_loc: (13,8)-(13,10) = "=>"
+ │ │ └── @ AssocSplatNode (location: (13,16)-(13,21))
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (13,18)-(13,21))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (13,18)-(13,21) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (13,16)-(13,18) = "**"
+ │ └── closing_loc: (13,22)-(13,23) = "}"
+ ├── @ InterpolatedStringNode (location: (14,0)-(14,14))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (14,0)-(14,1) = "\""
+ │ ├── parts: (length: 5)
+ │ │ ├── @ EmbeddedVariableNode (location: (14,1)-(14,4))
+ │ │ │ ├── operator_loc: (14,1)-(14,2) = "#"
+ │ │ │ └── variable:
+ │ │ │ @ InstanceVariableReadNode (location: (14,2)-(14,4))
+ │ │ │ └── name: :@a
+ │ │ ├── @ StringNode (location: (14,4)-(14,5))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (14,4)-(14,5) = " "
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: " "
+ │ │ ├── @ EmbeddedVariableNode (location: (14,5)-(14,9))
+ │ │ │ ├── operator_loc: (14,5)-(14,6) = "#"
+ │ │ │ └── variable:
+ │ │ │ @ ClassVariableReadNode (location: (14,6)-(14,9))
+ │ │ │ └── name: :@@a
+ │ │ ├── @ StringNode (location: (14,9)-(14,10))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (14,9)-(14,10) = " "
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: " "
+ │ │ └── @ EmbeddedVariableNode (location: (14,10)-(14,13))
+ │ │ ├── operator_loc: (14,10)-(14,11) = "#"
+ │ │ └── variable:
+ │ │ @ GlobalVariableReadNode (location: (14,11)-(14,13))
+ │ │ └── name: :$a
+ │ └── closing_loc: (14,13)-(14,14) = "\""
+ ├── @ IntegerNode (location: (15,0)-(15,1))
+ │ ├── flags: decimal
+ │ └── value: 0
+ ├── @ CallNode (location: (16,0)-(16,3))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (16,1)-(16,3))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+@
+ │ ├── message_loc: (16,0)-(16,1) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ IntegerNode (location: (17,0)-(17,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ IntegerNode (location: (18,0)-(18,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ RationalNode (location: (19,0)-(19,2))
+ │ └── numeric:
+ │ @ IntegerNode (location: (19,0)-(19,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ RationalNode (location: (20,0)-(20,4))
+ │ └── numeric:
+ │ @ FloatNode (location: (20,0)-(20,3))
+ │ └── value: 1.5
+ ├── @ RationalNode (location: (21,0)-(21,4))
+ │ └── numeric:
+ │ @ FloatNode (location: (21,0)-(21,3))
+ │ └── value: 1.3
+ ├── @ ImaginaryNode (location: (22,0)-(22,2))
+ │ └── numeric:
+ │ @ IntegerNode (location: (22,0)-(22,1))
+ │ ├── flags: decimal
+ │ └── value: 5
+ ├── @ ImaginaryNode (location: (23,0)-(23,3))
+ │ └── numeric:
+ │ @ IntegerNode (location: (23,0)-(23,2))
+ │ ├── flags: decimal
+ │ └── value: -5
+ ├── @ ImaginaryNode (location: (24,0)-(24,4))
+ │ └── numeric:
+ │ @ FloatNode (location: (24,0)-(24,3))
+ │ └── value: 0.6
+ ├── @ ImaginaryNode (location: (25,0)-(25,5))
+ │ └── numeric:
+ │ @ FloatNode (location: (25,0)-(25,4))
+ │ └── value: -0.6
+ ├── @ ImaginaryNode (location: (26,0)-(26,32))
+ │ └── numeric:
+ │ @ IntegerNode (location: (26,0)-(26,31))
+ │ ├── flags: decimal
+ │ └── value: 1000000000000000000000000000000
+ ├── @ ImaginaryNode (location: (27,0)-(27,3))
+ │ └── numeric:
+ │ @ RationalNode (location: (27,0)-(27,2))
+ │ └── numeric:
+ │ @ IntegerNode (location: (27,0)-(27,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ InterpolatedStringNode (location: (28,0)-(28,11))
+ │ ├── flags: ∅
+ │ ├── opening_loc: ∅
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (28,0)-(28,5))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: (28,0)-(28,1) = "\""
+ │ │ │ ├── content_loc: (28,1)-(28,4) = "foo"
+ │ │ │ ├── closing_loc: (28,4)-(28,5) = "\""
+ │ │ │ └── unescaped: "foo"
+ │ │ └── @ StringNode (location: (28,6)-(28,11))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: (28,6)-(28,7) = "\""
+ │ │ ├── content_loc: (28,7)-(28,10) = "bar"
+ │ │ ├── closing_loc: (28,10)-(28,11) = "\""
+ │ │ └── unescaped: "bar"
+ │ └── closing_loc: ∅
+ ├── @ InterpolatedStringNode (location: (29,0)-(29,15))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (29,0)-(29,1) = "\""
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (29,1)-(29,8))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (29,1)-(29,8) = "foobar "
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foobar "
+ │ │ └── @ EmbeddedStatementsNode (location: (29,8)-(29,14))
+ │ │ ├── opening_loc: (29,8)-(29,10) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (29,10)-(29,13))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (29,10)-(29,13))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (29,10)-(29,13) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── closing_loc: (29,13)-(29,14) = "}"
+ │ └── closing_loc: (29,14)-(29,15) = "\""
+ ├── @ InterpolatedStringNode (location: (30,0)-(30,12))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (30,0)-(30,1) = "\""
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (30,1)-(30,4))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (30,1)-(30,4) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── @ EmbeddedStatementsNode (location: (30,4)-(30,8))
+ │ │ │ ├── opening_loc: (30,4)-(30,6) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (30,6)-(30,7))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (30,6)-(30,7))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── closing_loc: (30,7)-(30,8) = "}"
+ │ │ └── @ StringNode (location: (30,8)-(30,11))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (30,8)-(30,11) = "bar"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "bar"
+ │ └── closing_loc: (30,11)-(30,12) = "\""
+ ├── @ InterpolatedStringNode (location: (31,0)-(31,9))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (31,0)-(31,1) = "\""
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (31,1)-(31,5))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (31,1)-(31,5) = "\\\\\\\\"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\\\\"
+ │ │ └── @ EmbeddedStatementsNode (location: (31,5)-(31,8))
+ │ │ ├── opening_loc: (31,5)-(31,7) = "\#{"
+ │ │ ├── statements: ∅
+ │ │ └── closing_loc: (31,7)-(31,8) = "}"
+ │ └── closing_loc: (31,8)-(31,9) = "\""
+ ├── @ InterpolatedStringNode (location: (32,0)-(32,9))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (32,0)-(32,1) = "\""
+ │ ├── parts: (length: 2)
+ │ │ ├── @ EmbeddedStatementsNode (location: (32,1)-(32,4))
+ │ │ │ ├── opening_loc: (32,1)-(32,3) = "\#{"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── closing_loc: (32,3)-(32,4) = "}"
+ │ │ └── @ StringNode (location: (32,4)-(32,8))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (32,4)-(32,8) = "\\\#{}"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\#{}"
+ │ └── closing_loc: (32,8)-(32,9) = "\""
+ ├── @ InterpolatedStringNode (location: (33,0)-(33,9))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (33,0)-(33,1) = "\""
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (33,1)-(33,5))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (33,1)-(33,5) = "\\\#{}"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\#{}"
+ │ │ └── @ EmbeddedStatementsNode (location: (33,5)-(33,8))
+ │ │ ├── opening_loc: (33,5)-(33,7) = "\#{"
+ │ │ ├── statements: ∅
+ │ │ └── closing_loc: (33,7)-(33,8) = "}"
+ │ └── closing_loc: (33,8)-(33,9) = "\""
+ ├── @ StringNode (location: (34,0)-(34,15))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (34,0)-(34,1) = "\""
+ │ ├── content_loc: (34,1)-(34,14) = "foo\\\\\\\#{@bar}"
+ │ ├── closing_loc: (34,14)-(34,15) = "\""
+ │ └── unescaped: "foo\\\#{@bar}"
+ ├── @ StringNode (location: (35,0)-(35,4))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (35,0)-(35,1) = "\""
+ │ ├── content_loc: (35,1)-(35,3) = "\\\""
+ │ ├── closing_loc: (35,3)-(35,4) = "\""
+ │ └── unescaped: "\""
+ ├── @ StringNode (location: (36,0)-(36,9))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (36,0)-(36,1) = "\""
+ │ ├── content_loc: (36,1)-(36,8) = "foo bar"
+ │ ├── closing_loc: (36,8)-(36,9) = "\""
+ │ └── unescaped: "foo bar"
+ ├── @ StringNode (location: (37,0)-(37,10))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (37,0)-(37,1) = "\""
+ │ ├── content_loc: (37,1)-(37,9) = "foo\\nbar"
+ │ ├── closing_loc: (37,9)-(37,10) = "\""
+ │ └── unescaped: "foo\nbar"
+ ├── @ XStringNode (location: (38,0)-(38,5))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (38,0)-(38,1) = "`"
+ │ ├── content_loc: (38,1)-(38,4) = "foo"
+ │ ├── closing_loc: (38,4)-(38,5) = "`"
+ │ └── unescaped: "foo"
+ ├── @ InterpolatedXStringNode (location: (39,0)-(39,12))
+ │ ├── opening_loc: (39,0)-(39,1) = "`"
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (39,1)-(39,4))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (39,1)-(39,4) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ └── @ EmbeddedStatementsNode (location: (39,4)-(39,11))
+ │ │ ├── opening_loc: (39,4)-(39,6) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (39,6)-(39,10))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ InstanceVariableReadNode (location: (39,6)-(39,10))
+ │ │ │ └── name: :@bar
+ │ │ └── closing_loc: (39,10)-(39,11) = "}"
+ │ └── closing_loc: (39,11)-(39,12) = "`"
+ ├── @ XStringNode (location: (40,0)-(40,3))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (40,0)-(40,1) = "`"
+ │ ├── content_loc: (40,1)-(40,2) = ")"
+ │ ├── closing_loc: (40,2)-(40,3) = "`"
+ │ └── unescaped: ")"
+ ├── @ XStringNode (location: (41,0)-(41,4))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (41,0)-(41,1) = "`"
+ │ ├── content_loc: (41,1)-(41,3) = "\\`"
+ │ ├── closing_loc: (41,3)-(41,4) = "`"
+ │ └── unescaped: "`"
+ ├── @ XStringNode (location: (42,0)-(42,3))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (42,0)-(42,1) = "`"
+ │ ├── content_loc: (42,1)-(42,2) = "\""
+ │ ├── closing_loc: (42,2)-(42,3) = "`"
+ │ └── unescaped: "\""
+ ├── @ SymbolNode (location: (43,0)-(43,4))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (43,0)-(43,1) = ":"
+ │ ├── value_loc: (43,1)-(43,4) = "foo"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "foo"
+ ├── @ SymbolNode (location: (44,0)-(44,6))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (44,0)-(44,2) = ":\""
+ │ ├── value_loc: (44,2)-(44,5) = "A B"
+ │ ├── closing_loc: (44,5)-(44,6) = "\""
+ │ └── unescaped: "A B"
+ ├── @ SymbolNode (location: (45,0)-(45,4))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (45,0)-(45,1) = ":"
+ │ ├── value_loc: (45,1)-(45,4) = "foo"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "foo"
+ ├── @ SymbolNode (location: (46,0)-(46,6))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (46,0)-(46,2) = ":\""
+ │ ├── value_loc: (46,2)-(46,5) = "A B"
+ │ ├── closing_loc: (46,5)-(46,6) = "\""
+ │ └── unescaped: "A B"
+ ├── @ SymbolNode (location: (47,0)-(47,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (47,0)-(47,2) = ":\""
+ │ ├── value_loc: (47,2)-(47,6) = "A\\\"B"
+ │ ├── closing_loc: (47,6)-(47,7) = "\""
+ │ └── unescaped: "A\"B"
+ ├── @ SymbolNode (location: (48,0)-(48,3))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (48,0)-(48,2) = ":\""
+ │ ├── value_loc: (1,0)-(1,0) = ""
+ │ ├── closing_loc: (48,2)-(48,3) = "\""
+ │ └── unescaped: ""
+ ├── @ RegularExpressionNode (location: (49,0)-(49,5))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (49,0)-(49,1) = "/"
+ │ ├── content_loc: (49,1)-(49,4) = "foo"
+ │ ├── closing_loc: (49,4)-(49,5) = "/"
+ │ └── unescaped: "foo"
+ ├── @ RegularExpressionNode (location: (50,0)-(50,28))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (50,0)-(50,1) = "/"
+ │ ├── content_loc: (50,1)-(50,27) = "[^-+',.\\/:@[:alnum:]\\[\\]]+"
+ │ ├── closing_loc: (50,27)-(50,28) = "/"
+ │ └── unescaped: "[^-+',./:@[:alnum:]\\[\\]]+"
+ ├── @ InterpolatedRegularExpressionNode (location: (51,0)-(51,12))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (51,0)-(51,1) = "/"
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (51,1)-(51,4))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (51,1)-(51,4) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ └── @ EmbeddedStatementsNode (location: (51,4)-(51,11))
+ │ │ ├── opening_loc: (51,4)-(51,6) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (51,6)-(51,10))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ InstanceVariableReadNode (location: (51,6)-(51,10))
+ │ │ │ └── name: :@bar
+ │ │ └── closing_loc: (51,10)-(51,11) = "}"
+ │ └── closing_loc: (51,11)-(51,12) = "/"
+ ├── @ InterpolatedRegularExpressionNode (location: (52,0)-(52,15))
+ │ ├── flags: ignore_case, extended, multi_line
+ │ ├── opening_loc: (52,0)-(52,1) = "/"
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (52,1)-(52,4))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (52,1)-(52,4) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ └── @ EmbeddedStatementsNode (location: (52,4)-(52,11))
+ │ │ ├── opening_loc: (52,4)-(52,6) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (52,6)-(52,10))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ InstanceVariableReadNode (location: (52,6)-(52,10))
+ │ │ │ └── name: :@bar
+ │ │ └── closing_loc: (52,10)-(52,11) = "}"
+ │ └── closing_loc: (52,11)-(52,15) = "/imx"
+ ├── @ InterpolatedRegularExpressionNode (location: (53,0)-(53,13))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (53,0)-(53,1) = "/"
+ │ ├── parts: (length: 1)
+ │ │ └── @ EmbeddedStatementsNode (location: (53,1)-(53,12))
+ │ │ ├── opening_loc: (53,1)-(53,3) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (53,3)-(53,11))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ StringNode (location: (53,3)-(53,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (53,3)-(53,4) = "\""
+ │ │ │ ├── content_loc: (53,4)-(53,10) = "\\u0000"
+ │ │ │ ├── closing_loc: (53,10)-(53,11) = "\""
+ │ │ │ └── unescaped: "\u0000"
+ │ │ └── closing_loc: (53,11)-(53,12) = "}"
+ │ └── closing_loc: (53,12)-(53,13) = "/"
+ ├── @ RegularExpressionNode (location: (54,0)-(54,4))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (54,0)-(54,1) = "/"
+ │ ├── content_loc: (54,1)-(54,3) = "\\n"
+ │ ├── closing_loc: (54,3)-(54,4) = "/"
+ │ └── unescaped: "\\n"
+ ├── @ RegularExpressionNode (location: (55,0)-(55,4))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (55,0)-(55,1) = "/"
+ │ ├── content_loc: (55,1)-(55,3) = "\\n"
+ │ ├── closing_loc: (55,3)-(55,4) = "/"
+ │ └── unescaped: "\\n"
+ ├── @ RegularExpressionNode (location: (56,0)-(56,5))
+ │ ├── flags: extended, forced_us_ascii_encoding
+ │ ├── opening_loc: (56,0)-(56,1) = "/"
+ │ ├── content_loc: (56,1)-(56,3) = "\\n"
+ │ ├── closing_loc: (56,3)-(56,5) = "/x"
+ │ └── unescaped: "\\n"
+ ├── @ RegularExpressionNode (location: (57,0)-(57,7))
+ │ ├── flags: extended, forced_us_ascii_encoding
+ │ ├── opening_loc: (57,0)-(57,1) = "/"
+ │ ├── content_loc: (57,1)-(57,5) = "\\/\\/"
+ │ ├── closing_loc: (57,5)-(57,7) = "/x"
+ │ └── unescaped: "//"
+ ├── @ InterpolatedSymbolNode (location: (58,0)-(58,15))
+ │ ├── opening_loc: (58,0)-(58,2) = ":\""
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (58,2)-(58,5))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (58,2)-(58,5) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── @ EmbeddedStatementsNode (location: (58,5)-(58,11))
+ │ │ │ ├── opening_loc: (58,5)-(58,7) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (58,7)-(58,10))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (58,7)-(58,10))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (58,7)-(58,10) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── closing_loc: (58,10)-(58,11) = "}"
+ │ │ └── @ StringNode (location: (58,11)-(58,14))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (58,11)-(58,14) = "baz"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "baz"
+ │ └── closing_loc: (58,14)-(58,15) = "\""
+ ├── @ InterpolatedSymbolNode (location: (59,0)-(59,11))
+ │ ├── opening_loc: (59,0)-(59,2) = ":\""
+ │ ├── parts: (length: 1)
+ │ │ └── @ EmbeddedStatementsNode (location: (59,2)-(59,10))
+ │ │ ├── opening_loc: (59,2)-(59,4) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (59,4)-(59,9))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ StringNode (location: (59,4)-(59,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (59,4)-(59,5) = "\""
+ │ │ │ ├── content_loc: (59,5)-(59,8) = "foo"
+ │ │ │ ├── closing_loc: (59,8)-(59,9) = "\""
+ │ │ │ └── unescaped: "foo"
+ │ │ └── closing_loc: (59,9)-(59,10) = "}"
+ │ └── closing_loc: (59,10)-(59,11) = "\""
+ ├── @ RangeNode (location: (60,0)-(60,14))
+ │ ├── flags: ∅
+ │ ├── left:
+ │ │ @ ParenthesesNode (location: (60,0)-(60,11))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (60,1)-(60,10))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (60,1)-(60,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ FloatNode (location: (60,1)-(60,4))
+ │ │ │ │ └── value: 0.0
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :/
+ │ │ │ ├── message_loc: (60,5)-(60,6) = "/"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (60,7)-(60,10))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ FloatNode (location: (60,7)-(60,10))
+ │ │ │ │ └── value: 0.0
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (60,0)-(60,1) = "("
+ │ │ └── closing_loc: (60,10)-(60,11) = ")"
+ │ ├── right:
+ │ │ @ IntegerNode (location: (60,13)-(60,14))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (60,11)-(60,13) = ".."
+ ├── @ RangeNode (location: (61,0)-(61,14))
+ │ ├── flags: ∅
+ │ ├── left:
+ │ │ @ IntegerNode (location: (61,0)-(61,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── right:
+ │ │ @ ParenthesesNode (location: (61,3)-(61,14))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (61,4)-(61,13))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (61,4)-(61,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ FloatNode (location: (61,4)-(61,7))
+ │ │ │ │ └── value: 0.0
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :/
+ │ │ │ ├── message_loc: (61,8)-(61,9) = "/"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (61,10)-(61,13))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ FloatNode (location: (61,10)-(61,13))
+ │ │ │ │ └── value: 0.0
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (61,3)-(61,4) = "("
+ │ │ └── closing_loc: (61,13)-(61,14) = ")"
+ │ └── operator_loc: (61,1)-(61,3) = ".."
+ ├── @ RangeNode (location: (62,0)-(62,16))
+ │ ├── flags: ∅
+ │ ├── left:
+ │ │ @ ParenthesesNode (location: (62,0)-(62,11))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (62,1)-(62,10))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (62,1)-(62,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ FloatNode (location: (62,1)-(62,4))
+ │ │ │ │ └── value: 0.0
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :/
+ │ │ │ ├── message_loc: (62,5)-(62,6) = "/"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (62,7)-(62,10))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ FloatNode (location: (62,7)-(62,10))
+ │ │ │ │ └── value: 0.0
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (62,0)-(62,1) = "("
+ │ │ └── closing_loc: (62,10)-(62,11) = ")"
+ │ ├── right:
+ │ │ @ IntegerNode (location: (62,13)-(62,16))
+ │ │ ├── flags: decimal
+ │ │ └── value: 100
+ │ └── operator_loc: (62,11)-(62,13) = ".."
+ ├── @ FloatNode (location: (63,0)-(63,4))
+ │ └── value: -0.1
+ ├── @ FloatNode (location: (64,0)-(64,3))
+ │ └── value: 0.1
+ ├── @ ArrayNode (location: (65,0)-(65,6))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (65,1)-(65,2))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (65,4)-(65,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── opening_loc: (65,0)-(65,1) = "["
+ │ └── closing_loc: (65,5)-(65,6) = "]"
+ ├── @ ArrayNode (location: (66,0)-(66,11))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 3)
+ │ │ ├── @ IntegerNode (location: (66,1)-(66,2))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── @ ParenthesesNode (location: (66,4)-(66,6))
+ │ │ │ ├── body: ∅
+ │ │ │ ├── opening_loc: (66,4)-(66,5) = "("
+ │ │ │ └── closing_loc: (66,5)-(66,6) = ")"
+ │ │ └── @ CallNode (location: (66,8)-(66,10))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :n2
+ │ │ ├── message_loc: (66,8)-(66,10) = "n2"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (66,0)-(66,1) = "["
+ │ └── closing_loc: (66,10)-(66,11) = "]"
+ ├── @ ArrayNode (location: (67,0)-(67,3))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ IntegerNode (location: (67,1)-(67,2))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── opening_loc: (67,0)-(67,1) = "["
+ │ └── closing_loc: (67,2)-(67,3) = "]"
+ ├── @ ArrayNode (location: (68,0)-(68,2))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 0)
+ │ ├── opening_loc: (68,0)-(68,1) = "["
+ │ └── closing_loc: (68,1)-(68,2) = "]"
+ ├── @ ArrayNode (location: (69,0)-(69,10))
+ │ ├── flags: contains_splat
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (69,1)-(69,2))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ SplatNode (location: (69,4)-(69,9))
+ │ │ ├── operator_loc: (69,4)-(69,5) = "*"
+ │ │ └── expression:
+ │ │ @ InstanceVariableReadNode (location: (69,5)-(69,9))
+ │ │ └── name: :@foo
+ │ ├── opening_loc: (69,0)-(69,1) = "["
+ │ └── closing_loc: (69,9)-(69,10) = "]"
+ ├── @ ArrayNode (location: (70,0)-(70,10))
+ │ ├── flags: contains_splat
+ │ ├── elements: (length: 2)
+ │ │ ├── @ SplatNode (location: (70,1)-(70,6))
+ │ │ │ ├── operator_loc: (70,1)-(70,2) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ InstanceVariableReadNode (location: (70,2)-(70,6))
+ │ │ │ └── name: :@foo
+ │ │ └── @ IntegerNode (location: (70,8)-(70,9))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── opening_loc: (70,0)-(70,1) = "["
+ │ └── closing_loc: (70,9)-(70,10) = "]"
+ ├── @ ArrayNode (location: (71,0)-(71,14))
+ │ ├── flags: contains_splat
+ │ ├── elements: (length: 2)
+ │ │ ├── @ SplatNode (location: (71,1)-(71,6))
+ │ │ │ ├── operator_loc: (71,1)-(71,2) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ InstanceVariableReadNode (location: (71,2)-(71,6))
+ │ │ │ └── name: :@foo
+ │ │ └── @ SplatNode (location: (71,8)-(71,13))
+ │ │ ├── operator_loc: (71,8)-(71,9) = "*"
+ │ │ └── expression:
+ │ │ @ InstanceVariableReadNode (location: (71,9)-(71,13))
+ │ │ └── name: :@baz
+ │ ├── opening_loc: (71,0)-(71,1) = "["
+ │ └── closing_loc: (71,13)-(71,14) = "]"
+ ├── @ HashNode (location: (72,0)-(72,2))
+ │ ├── opening_loc: (72,0)-(72,1) = "{"
+ │ ├── elements: (length: 0)
+ │ └── closing_loc: (72,1)-(72,2) = "}"
+ ├── @ HashNode (location: (73,0)-(73,12))
+ │ ├── opening_loc: (73,0)-(73,1) = "{"
+ │ ├── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (73,2)-(73,10))
+ │ │ ├── key:
+ │ │ │ @ ParenthesesNode (location: (73,2)-(73,4))
+ │ │ │ ├── body: ∅
+ │ │ │ ├── opening_loc: (73,2)-(73,3) = "("
+ │ │ │ └── closing_loc: (73,3)-(73,4) = ")"
+ │ │ ├── value:
+ │ │ │ @ ParenthesesNode (location: (73,8)-(73,10))
+ │ │ │ ├── body: ∅
+ │ │ │ ├── opening_loc: (73,8)-(73,9) = "("
+ │ │ │ └── closing_loc: (73,9)-(73,10) = ")"
+ │ │ └── operator_loc: (73,5)-(73,7) = "=>"
+ │ └── closing_loc: (73,11)-(73,12) = "}"
+ ├── @ HashNode (location: (74,0)-(74,10))
+ │ ├── opening_loc: (74,0)-(74,1) = "{"
+ │ ├── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (74,2)-(74,8))
+ │ │ ├── key:
+ │ │ │ @ IntegerNode (location: (74,2)-(74,3))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (74,7)-(74,8))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── operator_loc: (74,4)-(74,6) = "=>"
+ │ └── closing_loc: (74,9)-(74,10) = "}"
+ ├── @ HashNode (location: (75,0)-(75,18))
+ │ ├── opening_loc: (75,0)-(75,1) = "{"
+ │ ├── elements: (length: 2)
+ │ │ ├── @ AssocNode (location: (75,2)-(75,8))
+ │ │ │ ├── key:
+ │ │ │ │ @ IntegerNode (location: (75,2)-(75,3))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── value:
+ │ │ │ │ @ IntegerNode (location: (75,7)-(75,8))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ └── operator_loc: (75,4)-(75,6) = "=>"
+ │ │ └── @ AssocNode (location: (75,10)-(75,16))
+ │ │ ├── key:
+ │ │ │ @ IntegerNode (location: (75,10)-(75,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 3
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (75,15)-(75,16))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 4
+ │ │ └── operator_loc: (75,12)-(75,14) = "=>"
+ │ └── closing_loc: (75,17)-(75,18) = "}"
+ ├── @ HashNode (location: (76,0)-(76,27))
+ │ ├── opening_loc: (76,0)-(76,1) = "{"
+ │ ├── elements: (length: 2)
+ │ │ ├── @ AssocNode (location: (76,2)-(76,19))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (76,2)-(76,4))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (76,2)-(76,3) = "a"
+ │ │ │ │ ├── closing_loc: (76,3)-(76,4) = ":"
+ │ │ │ │ └── unescaped: "a"
+ │ │ │ ├── value:
+ │ │ │ │ @ ParenthesesNode (location: (76,5)-(76,19))
+ │ │ │ │ ├── body:
+ │ │ │ │ │ @ StatementsNode (location: (76,6)-(76,18))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ RescueModifierNode (location: (76,6)-(76,18))
+ │ │ │ │ │ ├── expression:
+ │ │ │ │ │ │ @ IntegerNode (location: (76,6)-(76,7))
+ │ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ │ └── value: 1
+ │ │ │ │ │ ├── keyword_loc: (76,8)-(76,14) = "rescue"
+ │ │ │ │ │ └── rescue_expression:
+ │ │ │ │ │ @ CallNode (location: (76,15)-(76,18))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :foo
+ │ │ │ │ │ ├── message_loc: (76,15)-(76,18) = "foo"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── opening_loc: (76,5)-(76,6) = "("
+ │ │ │ │ └── closing_loc: (76,18)-(76,19) = ")"
+ │ │ │ └── operator_loc: ∅
+ │ │ └── @ AssocNode (location: (76,21)-(76,25))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (76,21)-(76,23))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (76,21)-(76,22) = "b"
+ │ │ │ ├── closing_loc: (76,22)-(76,23) = ":"
+ │ │ │ └── unescaped: "b"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (76,24)-(76,25))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── operator_loc: ∅
+ │ └── closing_loc: (76,26)-(76,27) = "}"
+ ├── @ HashNode (location: (77,0)-(77,14))
+ │ ├── opening_loc: (77,0)-(77,1) = "{"
+ │ ├── elements: (length: 2)
+ │ │ ├── @ AssocNode (location: (77,2)-(77,6))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (77,2)-(77,4))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (77,2)-(77,3) = "a"
+ │ │ │ │ ├── closing_loc: (77,3)-(77,4) = ":"
+ │ │ │ │ └── unescaped: "a"
+ │ │ │ ├── value:
+ │ │ │ │ @ IntegerNode (location: (77,5)-(77,6))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── operator_loc: ∅
+ │ │ └── @ AssocNode (location: (77,8)-(77,12))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (77,8)-(77,10))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (77,8)-(77,9) = "b"
+ │ │ │ ├── closing_loc: (77,9)-(77,10) = ":"
+ │ │ │ └── unescaped: "b"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (77,11)-(77,12))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── operator_loc: ∅
+ │ └── closing_loc: (77,13)-(77,14) = "}"
+ ├── @ HashNode (location: (78,0)-(78,9))
+ │ ├── opening_loc: (78,0)-(78,1) = "{"
+ │ ├── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (78,2)-(78,7))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (78,2)-(78,4))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (78,2)-(78,3) = "a"
+ │ │ │ ├── closing_loc: (78,3)-(78,4) = ":"
+ │ │ │ └── unescaped: "a"
+ │ │ ├── value:
+ │ │ │ @ SymbolNode (location: (78,5)-(78,7))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (78,5)-(78,6) = ":"
+ │ │ │ ├── value_loc: (78,6)-(78,7) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ └── operator_loc: ∅
+ │ └── closing_loc: (78,8)-(78,9) = "}"
+ ├── @ HashNode (location: (79,0)-(79,15))
+ │ ├── opening_loc: (79,0)-(79,1) = "{"
+ │ ├── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (79,2)-(79,13))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (79,2)-(79,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (79,2)-(79,4) = ":\""
+ │ │ │ ├── value_loc: (79,4)-(79,7) = "a b"
+ │ │ │ ├── closing_loc: (79,7)-(79,8) = "\""
+ │ │ │ └── unescaped: "a b"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (79,12)-(79,13))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (79,9)-(79,11) = "=>"
+ │ └── closing_loc: (79,14)-(79,15) = "}"
+ ├── @ HashNode (location: (80,0)-(80,12))
+ │ ├── opening_loc: (80,0)-(80,1) = "{"
+ │ ├── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (80,2)-(80,10))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (80,2)-(80,5))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (80,2)-(80,3) = ":"
+ │ │ │ ├── value_loc: (80,3)-(80,5) = "-@"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "-@"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (80,9)-(80,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (80,6)-(80,8) = "=>"
+ │ └── closing_loc: (80,11)-(80,12) = "}"
+ ├── @ InterpolatedStringNode (location: (81,0)-(82,7))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (81,0)-(81,1) = "\""
+ │ ├── parts: (length: 4)
+ │ │ ├── @ EmbeddedStatementsNode (location: (81,1)-(81,4))
+ │ │ │ ├── opening_loc: (81,1)-(81,3) = "\#{"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── closing_loc: (81,3)-(81,4) = "}"
+ │ │ ├── @ StringNode (location: (81,4)-(82,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (81,4)-(82,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ ├── @ EmbeddedStatementsNode (location: (82,0)-(82,3))
+ │ │ │ ├── opening_loc: (82,0)-(82,2) = "\#{"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── closing_loc: (82,2)-(82,3) = "}"
+ │ │ └── @ StringNode (location: (82,3)-(82,6))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (82,3)-(82,6) = "\\na"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\na"
+ │ └── closing_loc: (82,6)-(82,7) = "\""
+ ├── @ CallNode (location: (83,0)-(86,1))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (83,0)-(83,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (83,4)-(86,1))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (84,2)-(85,7))
+ │ │ └── body: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (84,2)-(85,7))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (84,2)-(84,3) = "\""
+ │ │ ├── parts: (length: 4)
+ │ │ │ ├── @ EmbeddedStatementsNode (location: (84,3)-(84,6))
+ │ │ │ │ ├── opening_loc: (84,3)-(84,5) = "\#{"
+ │ │ │ │ ├── statements: ∅
+ │ │ │ │ └── closing_loc: (84,5)-(84,6) = "}"
+ │ │ │ ├── @ StringNode (location: (84,6)-(85,0))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (84,6)-(85,0) = "\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "\n"
+ │ │ │ ├── @ EmbeddedStatementsNode (location: (85,0)-(85,3))
+ │ │ │ │ ├── opening_loc: (85,0)-(85,2) = "\#{"
+ │ │ │ │ ├── statements: ∅
+ │ │ │ │ └── closing_loc: (85,2)-(85,3) = "}"
+ │ │ │ └── @ StringNode (location: (85,3)-(85,6))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (85,3)-(85,6) = "\\na"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\na"
+ │ │ └── closing_loc: (85,6)-(85,7) = "\""
+ │ ├── opening_loc: (83,4)-(83,5) = "{"
+ │ └── closing_loc: (86,0)-(86,1) = "}"
+ ├── @ SymbolNode (location: (87,0)-(88,2))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (87,0)-(87,2) = ":\""
+ │ ├── value_loc: (87,2)-(88,1) = "a\\\\\nb"
+ │ ├── closing_loc: (88,1)-(88,2) = "\""
+ │ └── unescaped: "a\\\nb"
+ └── @ InterpolatedXStringNode (location: (89,0)-(91,2))
+ ├── opening_loc: (89,0)-(89,1) = "`"
+ ├── parts: (length: 3)
+ │ ├── @ StringNode (location: (89,1)-(90,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (89,1)-(90,0) = " x\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: " x\n"
+ │ ├── @ EmbeddedStatementsNode (location: (90,0)-(90,6))
+ │ │ ├── opening_loc: (90,0)-(90,2) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (90,2)-(90,5))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (90,2)-(90,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (90,2)-(90,5) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── closing_loc: (90,5)-(90,6) = "}"
+ │ └── @ StringNode (location: (90,6)-(91,1))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (90,6)-(91,1) = "\n#"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "\n#"
+ └── closing_loc: (91,1)-(91,2) = "`"
diff --git a/test/prism/snapshots/unparser/corpus/literal/module.txt b/test/prism/snapshots/unparser/corpus/literal/module.txt
new file mode 100644
index 0000000000..5dd8c03b51
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/module.txt
@@ -0,0 +1,107 @@
+@ ProgramNode (location: (1,0)-(16,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(16,3))
+ └── body: (length: 4)
+ ├── @ ModuleNode (location: (1,0)-(2,3))
+ │ ├── locals: []
+ │ ├── module_keyword_loc: (1,0)-(1,6) = "module"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (1,7)-(1,8))
+ │ │ └── name: :A
+ │ ├── body: ∅
+ │ ├── end_keyword_loc: (2,0)-(2,3) = "end"
+ │ └── name: :A
+ ├── @ ModuleNode (location: (4,0)-(5,3))
+ │ ├── locals: []
+ │ ├── module_keyword_loc: (4,0)-(4,6) = "module"
+ │ ├── constant_path:
+ │ │ @ ConstantPathNode (location: (4,7)-(4,11))
+ │ │ ├── parent:
+ │ │ │ @ ConstantReadNode (location: (4,7)-(4,8))
+ │ │ │ └── name: :A
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (4,10)-(4,11))
+ │ │ │ └── name: :B
+ │ │ └── delimiter_loc: (4,8)-(4,10) = "::"
+ │ ├── body: ∅
+ │ ├── end_keyword_loc: (5,0)-(5,3) = "end"
+ │ └── name: :B
+ ├── @ ModuleNode (location: (7,0)-(8,3))
+ │ ├── locals: []
+ │ ├── module_keyword_loc: (7,0)-(7,6) = "module"
+ │ ├── constant_path:
+ │ │ @ ConstantPathNode (location: (7,7)-(7,14))
+ │ │ ├── parent:
+ │ │ │ @ ConstantPathNode (location: (7,7)-(7,11))
+ │ │ │ ├── parent:
+ │ │ │ │ @ ConstantReadNode (location: (7,7)-(7,8))
+ │ │ │ │ └── name: :A
+ │ │ │ ├── child:
+ │ │ │ │ @ ConstantReadNode (location: (7,10)-(7,11))
+ │ │ │ │ └── name: :B
+ │ │ │ └── delimiter_loc: (7,8)-(7,10) = "::"
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (7,13)-(7,14))
+ │ │ │ └── name: :C
+ │ │ └── delimiter_loc: (7,11)-(7,13) = "::"
+ │ ├── body: ∅
+ │ ├── end_keyword_loc: (8,0)-(8,3) = "end"
+ │ └── name: :C
+ └── @ ModuleNode (location: (10,0)-(16,3))
+ ├── locals: []
+ ├── module_keyword_loc: (10,0)-(10,6) = "module"
+ ├── constant_path:
+ │ @ ConstantReadNode (location: (10,7)-(10,8))
+ │ └── name: :A
+ ├── body:
+ │ @ StatementsNode (location: (11,2)-(15,5))
+ │ └── body: (length: 2)
+ │ ├── @ CallNode (location: (11,2)-(11,16))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :include
+ │ │ ├── message_loc: (11,2)-(11,9) = "include"
+ │ │ ├── opening_loc: (11,9)-(11,10) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (11,10)-(11,15))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ CallNode (location: (11,10)-(11,15))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ ConstantReadNode (location: (11,10)-(11,11))
+ │ │ │ │ └── name: :B
+ │ │ │ ├── call_operator_loc: (11,11)-(11,12) = "."
+ │ │ │ ├── name: :new
+ │ │ │ ├── message_loc: (11,12)-(11,15) = "new"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── closing_loc: (11,15)-(11,16) = ")"
+ │ │ └── block: ∅
+ │ └── @ DefNode (location: (13,2)-(15,5))
+ │ ├── name: :foo
+ │ ├── name_loc: (13,6)-(13,9) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (14,4)-(14,8))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SymbolNode (location: (14,4)-(14,8))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (14,4)-(14,5) = ":"
+ │ │ ├── value_loc: (14,5)-(14,8) = "bar"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "bar"
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (13,2)-(13,5) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (15,2)-(15,5) = "end"
+ ├── end_keyword_loc: (16,0)-(16,3) = "end"
+ └── name: :A
diff --git a/test/prism/snapshots/unparser/corpus/literal/opasgn.txt b/test/prism/snapshots/unparser/corpus/literal/opasgn.txt
new file mode 100644
index 0000000000..8dc0849638
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/opasgn.txt
@@ -0,0 +1,509 @@
+@ ProgramNode (location: (1,0)-(24,10))
+├── locals: [:a, :h]
+└── statements:
+ @ StatementsNode (location: (1,0)-(24,10))
+ └── body: (length: 24)
+ ├── @ LocalVariableOperatorWriteNode (location: (1,0)-(1,6))
+ │ ├── name_loc: (1,0)-(1,1) = "a"
+ │ ├── operator_loc: (1,2)-(1,4) = "+="
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,5)-(1,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── name: :a
+ │ ├── operator: :+
+ │ └── depth: 0
+ ├── @ LocalVariableOperatorWriteNode (location: (2,0)-(2,6))
+ │ ├── name_loc: (2,0)-(2,1) = "a"
+ │ ├── operator_loc: (2,2)-(2,4) = "-="
+ │ ├── value:
+ │ │ @ IntegerNode (location: (2,5)-(2,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── name: :a
+ │ ├── operator: :-
+ │ └── depth: 0
+ ├── @ LocalVariableOperatorWriteNode (location: (3,0)-(3,7))
+ │ ├── name_loc: (3,0)-(3,1) = "a"
+ │ ├── operator_loc: (3,2)-(3,5) = "**="
+ │ ├── value:
+ │ │ @ IntegerNode (location: (3,6)-(3,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── name: :a
+ │ ├── operator: :**
+ │ └── depth: 0
+ ├── @ LocalVariableOperatorWriteNode (location: (4,0)-(4,6))
+ │ ├── name_loc: (4,0)-(4,1) = "a"
+ │ ├── operator_loc: (4,2)-(4,4) = "*="
+ │ ├── value:
+ │ │ @ IntegerNode (location: (4,5)-(4,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── name: :a
+ │ ├── operator: :*
+ │ └── depth: 0
+ ├── @ LocalVariableOperatorWriteNode (location: (5,0)-(5,6))
+ │ ├── name_loc: (5,0)-(5,1) = "a"
+ │ ├── operator_loc: (5,2)-(5,4) = "/="
+ │ ├── value:
+ │ │ @ IntegerNode (location: (5,5)-(5,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── name: :a
+ │ ├── operator: :/
+ │ └── depth: 0
+ ├── @ LocalVariableAndWriteNode (location: (6,0)-(6,7))
+ │ ├── name_loc: (6,0)-(6,1) = "a"
+ │ ├── operator_loc: (6,2)-(6,5) = "&&="
+ │ ├── value:
+ │ │ @ CallNode (location: (6,6)-(6,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (6,6)-(6,7) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── name: :a
+ │ └── depth: 0
+ ├── @ LocalVariableOrWriteNode (location: (7,0)-(7,7))
+ │ ├── name_loc: (7,0)-(7,1) = "a"
+ │ ├── operator_loc: (7,2)-(7,5) = "||="
+ │ ├── value:
+ │ │ @ IntegerNode (location: (7,6)-(7,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── name: :a
+ │ └── depth: 0
+ ├── @ CallNode (location: (8,0)-(8,13))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (8,0)-(8,9))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (8,1)-(8,8))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ LocalVariableOrWriteNode (location: (8,1)-(8,8))
+ │ │ │ ├── name_loc: (8,1)-(8,2) = "a"
+ │ │ │ ├── operator_loc: (8,3)-(8,6) = "||="
+ │ │ │ ├── value:
+ │ │ │ │ @ IntegerNode (location: (8,7)-(8,8))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ ├── opening_loc: (8,0)-(8,1) = "("
+ │ │ └── closing_loc: (8,8)-(8,9) = ")"
+ │ ├── call_operator_loc: (8,9)-(8,10) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (8,10)-(8,13) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (9,0)-(9,17))
+ │ ├── flags: attribute_write
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (9,0)-(9,10))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (9,1)-(9,9))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ LocalVariableOrWriteNode (location: (9,1)-(9,9))
+ │ │ │ ├── name_loc: (9,1)-(9,2) = "h"
+ │ │ │ ├── operator_loc: (9,3)-(9,6) = "||="
+ │ │ │ ├── value:
+ │ │ │ │ @ HashNode (location: (9,7)-(9,9))
+ │ │ │ │ ├── opening_loc: (9,7)-(9,8) = "{"
+ │ │ │ │ ├── elements: (length: 0)
+ │ │ │ │ └── closing_loc: (9,8)-(9,9) = "}"
+ │ │ │ ├── name: :h
+ │ │ │ └── depth: 0
+ │ │ ├── opening_loc: (9,0)-(9,1) = "("
+ │ │ └── closing_loc: (9,9)-(9,10) = ")"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]=
+ │ ├── message_loc: (9,10)-(9,13) = "[k]"
+ │ ├── opening_loc: (9,10)-(9,11) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (9,11)-(9,17))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (9,11)-(9,12))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :k
+ │ │ │ ├── message_loc: (9,11)-(9,12) = "k"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ CallNode (location: (9,16)-(9,17))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :v
+ │ │ ├── message_loc: (9,16)-(9,17) = "v"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (9,12)-(9,13) = "]"
+ │ └── block: ∅
+ ├── @ CallOperatorWriteNode (location: (10,0)-(10,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (10,0)-(10,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: (10,1)-(10,2) = "."
+ │ ├── message_loc: (10,2)-(10,3) = "b"
+ │ ├── read_name: :b
+ │ ├── write_name: :b=
+ │ ├── operator: :+
+ │ ├── operator_loc: (10,4)-(10,6) = "+="
+ │ └── value:
+ │ @ IntegerNode (location: (10,7)-(10,8))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── @ CallOperatorWriteNode (location: (11,0)-(11,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (11,0)-(11,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: (11,1)-(11,2) = "."
+ │ ├── message_loc: (11,2)-(11,3) = "b"
+ │ ├── read_name: :b
+ │ ├── write_name: :b=
+ │ ├── operator: :-
+ │ ├── operator_loc: (11,4)-(11,6) = "-="
+ │ └── value:
+ │ @ IntegerNode (location: (11,7)-(11,8))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── @ CallOperatorWriteNode (location: (12,0)-(12,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (12,0)-(12,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: (12,1)-(12,2) = "."
+ │ ├── message_loc: (12,2)-(12,3) = "b"
+ │ ├── read_name: :b
+ │ ├── write_name: :b=
+ │ ├── operator: :**
+ │ ├── operator_loc: (12,4)-(12,7) = "**="
+ │ └── value:
+ │ @ IntegerNode (location: (12,8)-(12,9))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── @ CallOperatorWriteNode (location: (13,0)-(13,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (13,0)-(13,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: (13,1)-(13,2) = "."
+ │ ├── message_loc: (13,2)-(13,3) = "b"
+ │ ├── read_name: :b
+ │ ├── write_name: :b=
+ │ ├── operator: :*
+ │ ├── operator_loc: (13,4)-(13,6) = "*="
+ │ └── value:
+ │ @ IntegerNode (location: (13,7)-(13,8))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── @ CallOperatorWriteNode (location: (14,0)-(14,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (14,0)-(14,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: (14,1)-(14,2) = "."
+ │ ├── message_loc: (14,2)-(14,3) = "b"
+ │ ├── read_name: :b
+ │ ├── write_name: :b=
+ │ ├── operator: :/
+ │ ├── operator_loc: (14,4)-(14,6) = "/="
+ │ └── value:
+ │ @ IntegerNode (location: (14,7)-(14,8))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── @ CallAndWriteNode (location: (15,0)-(15,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (15,0)-(15,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: (15,1)-(15,2) = "."
+ │ ├── message_loc: (15,2)-(15,3) = "b"
+ │ ├── read_name: :b
+ │ ├── write_name: :b=
+ │ ├── operator_loc: (15,4)-(15,7) = "&&="
+ │ └── value:
+ │ @ CallNode (location: (15,8)-(15,9))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :b
+ │ ├── message_loc: (15,8)-(15,9) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallOrWriteNode (location: (16,0)-(16,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (16,0)-(16,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: (16,1)-(16,2) = "."
+ │ ├── message_loc: (16,2)-(16,3) = "b"
+ │ ├── read_name: :b
+ │ ├── write_name: :b=
+ │ ├── operator_loc: (16,4)-(16,7) = "||="
+ │ └── value:
+ │ @ IntegerNode (location: (16,8)-(16,9))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── @ IndexOperatorWriteNode (location: (17,0)-(17,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (17,0)-(17,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── opening_loc: (17,1)-(17,2) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (17,2)-(17,3))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (17,2)-(17,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (17,2)-(17,3) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (17,3)-(17,4) = "]"
+ │ ├── block: ∅
+ │ ├── operator: :+
+ │ ├── operator_loc: (17,5)-(17,7) = "+="
+ │ └── value:
+ │ @ IntegerNode (location: (17,8)-(17,9))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── @ IndexOperatorWriteNode (location: (18,0)-(18,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (18,0)-(18,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── opening_loc: (18,1)-(18,2) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (18,2)-(18,3))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (18,2)-(18,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (18,2)-(18,3) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (18,3)-(18,4) = "]"
+ │ ├── block: ∅
+ │ ├── operator: :-
+ │ ├── operator_loc: (18,5)-(18,7) = "-="
+ │ └── value:
+ │ @ IntegerNode (location: (18,8)-(18,9))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── @ IndexOperatorWriteNode (location: (19,0)-(19,10))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (19,0)-(19,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── opening_loc: (19,1)-(19,2) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (19,2)-(19,3))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (19,2)-(19,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (19,2)-(19,3) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (19,3)-(19,4) = "]"
+ │ ├── block: ∅
+ │ ├── operator: :**
+ │ ├── operator_loc: (19,5)-(19,8) = "**="
+ │ └── value:
+ │ @ IntegerNode (location: (19,9)-(19,10))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── @ IndexOperatorWriteNode (location: (20,0)-(20,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (20,0)-(20,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── opening_loc: (20,1)-(20,2) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (20,2)-(20,3))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (20,2)-(20,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (20,2)-(20,3) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (20,3)-(20,4) = "]"
+ │ ├── block: ∅
+ │ ├── operator: :*
+ │ ├── operator_loc: (20,5)-(20,7) = "*="
+ │ └── value:
+ │ @ IntegerNode (location: (20,8)-(20,9))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── @ IndexOperatorWriteNode (location: (21,0)-(21,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (21,0)-(21,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── opening_loc: (21,1)-(21,2) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (21,2)-(21,3))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (21,2)-(21,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (21,2)-(21,3) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (21,3)-(21,4) = "]"
+ │ ├── block: ∅
+ │ ├── operator: :/
+ │ ├── operator_loc: (21,5)-(21,7) = "/="
+ │ └── value:
+ │ @ IntegerNode (location: (21,8)-(21,9))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── @ IndexAndWriteNode (location: (22,0)-(22,10))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (22,0)-(22,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── opening_loc: (22,1)-(22,2) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (22,2)-(22,3))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (22,2)-(22,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (22,2)-(22,3) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (22,3)-(22,4) = "]"
+ │ ├── block: ∅
+ │ ├── operator_loc: (22,5)-(22,8) = "&&="
+ │ └── value:
+ │ @ CallNode (location: (22,9)-(22,10))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :b
+ │ ├── message_loc: (22,9)-(22,10) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ IndexOrWriteNode (location: (23,0)-(23,10))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (23,0)-(23,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── opening_loc: (23,1)-(23,2) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (23,2)-(23,3))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (23,2)-(23,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (23,2)-(23,3) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (23,3)-(23,4) = "]"
+ │ ├── block: ∅
+ │ ├── operator_loc: (23,5)-(23,8) = "||="
+ │ └── value:
+ │ @ IntegerNode (location: (23,9)-(23,10))
+ │ ├── flags: decimal
+ │ └── value: 2
+ └── @ CallOperatorWriteNode (location: (24,0)-(24,10))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (24,0)-(24,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (24,0)-(24,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (24,3)-(24,4) = "."
+ ├── message_loc: (24,4)-(24,5) = "A"
+ ├── read_name: :A
+ ├── write_name: :A=
+ ├── operator: :+
+ ├── operator_loc: (24,6)-(24,8) = "+="
+ └── value:
+ @ IntegerNode (location: (24,9)-(24,10))
+ ├── flags: decimal
+ └── value: 1
diff --git a/test/prism/snapshots/unparser/corpus/literal/pattern.txt b/test/prism/snapshots/unparser/corpus/literal/pattern.txt
new file mode 100644
index 0000000000..5a0b4bb733
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/pattern.txt
@@ -0,0 +1,446 @@
+@ ProgramNode (location: (1,0)-(41,8))
+├── locals: [:a, :x, :y]
+└── statements:
+ @ StatementsNode (location: (1,0)-(41,8))
+ └── body: (length: 4)
+ ├── @ CaseMatchNode (location: (1,0)-(33,3))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (1,5)-(1,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,5)-(1,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 15)
+ │ │ ├── @ InNode (location: (2,0)-(3,6))
+ │ │ │ ├── pattern:
+ │ │ │ │ @ ArrayPatternNode (location: (2,3)-(2,17))
+ │ │ │ │ ├── constant:
+ │ │ │ │ │ @ ConstantReadNode (location: (2,3)-(2,4))
+ │ │ │ │ │ └── name: :A
+ │ │ │ │ ├── requireds: (length: 2)
+ │ │ │ │ │ ├── @ IntegerNode (location: (2,5)-(2,6))
+ │ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ │ └── value: 1
+ │ │ │ │ │ └── @ IntegerNode (location: (2,8)-(2,9))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 2
+ │ │ │ │ ├── rest:
+ │ │ │ │ │ @ SplatNode (location: (2,11)-(2,13))
+ │ │ │ │ │ ├── operator_loc: (2,11)-(2,12) = "*"
+ │ │ │ │ │ └── expression:
+ │ │ │ │ │ @ LocalVariableTargetNode (location: (2,12)-(2,13))
+ │ │ │ │ │ ├── name: :a
+ │ │ │ │ │ └── depth: 0
+ │ │ │ │ ├── posts: (length: 1)
+ │ │ │ │ │ └── @ IntegerNode (location: (2,15)-(2,16))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 3
+ │ │ │ │ ├── opening_loc: (2,4)-(2,5) = "["
+ │ │ │ │ └── closing_loc: (2,16)-(2,17) = "]"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (3,2)-(3,6))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ TrueNode (location: (3,2)-(3,6))
+ │ │ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ │ │ └── then_loc: (2,18)-(2,22) = "then"
+ │ │ ├── @ InNode (location: (4,0)-(5,3))
+ │ │ │ ├── pattern:
+ │ │ │ │ @ ArrayPatternNode (location: (4,3)-(4,11))
+ │ │ │ │ ├── constant: ∅
+ │ │ │ │ ├── requireds: (length: 2)
+ │ │ │ │ │ ├── @ IntegerNode (location: (4,4)-(4,5))
+ │ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ │ └── value: 1
+ │ │ │ │ │ └── @ IntegerNode (location: (4,7)-(4,8))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 2
+ │ │ │ │ ├── rest:
+ │ │ │ │ │ @ ImplicitRestNode (location: (4,8)-(4,9))
+ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ ├── opening_loc: (4,3)-(4,4) = "["
+ │ │ │ │ └── closing_loc: (4,10)-(4,11) = "]"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (5,2)-(5,3))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (5,2)-(5,3))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :y
+ │ │ │ │ ├── message_loc: (5,2)-(5,3) = "y"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── in_loc: (4,0)-(4,2) = "in"
+ │ │ │ └── then_loc: (4,12)-(4,16) = "then"
+ │ │ ├── @ InNode (location: (6,0)-(7,6))
+ │ │ │ ├── pattern:
+ │ │ │ │ @ HashPatternNode (location: (6,3)-(6,8))
+ │ │ │ │ ├── constant:
+ │ │ │ │ │ @ ConstantReadNode (location: (6,3)-(6,4))
+ │ │ │ │ │ └── name: :A
+ │ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ │ └── @ AssocNode (location: (6,5)-(6,7))
+ │ │ │ │ │ ├── key:
+ │ │ │ │ │ │ @ SymbolNode (location: (6,5)-(6,7))
+ │ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ │ ├── value_loc: (6,5)-(6,6) = "x"
+ │ │ │ │ │ │ ├── closing_loc: (6,6)-(6,7) = ":"
+ │ │ │ │ │ │ └── unescaped: "x"
+ │ │ │ │ │ ├── value:
+ │ │ │ │ │ │ @ ImplicitNode (location: (6,5)-(6,6))
+ │ │ │ │ │ │ └── value:
+ │ │ │ │ │ │ @ LocalVariableTargetNode (location: (6,5)-(6,6))
+ │ │ │ │ │ │ ├── name: :x
+ │ │ │ │ │ │ └── depth: 0
+ │ │ │ │ │ └── operator_loc: ∅
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── opening_loc: (6,4)-(6,5) = "("
+ │ │ │ │ └── closing_loc: (6,7)-(6,8) = ")"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (7,2)-(7,6))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ TrueNode (location: (7,2)-(7,6))
+ │ │ │ ├── in_loc: (6,0)-(6,2) = "in"
+ │ │ │ └── then_loc: (6,9)-(6,13) = "then"
+ │ │ ├── @ InNode (location: (8,0)-(9,6))
+ │ │ │ ├── pattern:
+ │ │ │ │ @ HashPatternNode (location: (8,3)-(8,8))
+ │ │ │ │ ├── constant: ∅
+ │ │ │ │ ├── elements: (length: 0)
+ │ │ │ │ ├── rest:
+ │ │ │ │ │ @ AssocSplatNode (location: (8,4)-(8,7))
+ │ │ │ │ │ ├── value:
+ │ │ │ │ │ │ @ LocalVariableTargetNode (location: (8,6)-(8,7))
+ │ │ │ │ │ │ ├── name: :a
+ │ │ │ │ │ │ └── depth: 0
+ │ │ │ │ │ └── operator_loc: (8,4)-(8,6) = "**"
+ │ │ │ │ ├── opening_loc: (8,3)-(8,4) = "{"
+ │ │ │ │ └── closing_loc: (8,7)-(8,8) = "}"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (9,2)-(9,6))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ TrueNode (location: (9,2)-(9,6))
+ │ │ │ ├── in_loc: (8,0)-(8,2) = "in"
+ │ │ │ └── then_loc: (8,9)-(8,13) = "then"
+ │ │ ├── @ InNode (location: (10,0)-(11,6))
+ │ │ │ ├── pattern:
+ │ │ │ │ @ IfNode (location: (10,3)-(10,13))
+ │ │ │ │ ├── if_keyword_loc: (10,6)-(10,8) = "if"
+ │ │ │ │ ├── predicate:
+ │ │ │ │ │ @ TrueNode (location: (10,9)-(10,13))
+ │ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ │ ├── statements:
+ │ │ │ │ │ @ StatementsNode (location: (10,3)-(10,5))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ HashPatternNode (location: (10,3)-(10,5))
+ │ │ │ │ │ ├── constant: ∅
+ │ │ │ │ │ ├── elements: (length: 0)
+ │ │ │ │ │ ├── rest: ∅
+ │ │ │ │ │ ├── opening_loc: (10,3)-(10,4) = "{"
+ │ │ │ │ │ └── closing_loc: (10,4)-(10,5) = "}"
+ │ │ │ │ ├── consequent: ∅
+ │ │ │ │ └── end_keyword_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (11,2)-(11,6))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ TrueNode (location: (11,2)-(11,6))
+ │ │ │ ├── in_loc: (10,0)-(10,2) = "in"
+ │ │ │ └── then_loc: (10,14)-(10,18) = "then"
+ │ │ ├── @ InNode (location: (12,0)-(13,6))
+ │ │ │ ├── pattern:
+ │ │ │ │ @ ArrayPatternNode (location: (12,3)-(12,12))
+ │ │ │ │ ├── constant: ∅
+ │ │ │ │ ├── requireds: (length: 2)
+ │ │ │ │ │ ├── @ LocalVariableTargetNode (location: (12,4)-(12,5))
+ │ │ │ │ │ │ ├── name: :x
+ │ │ │ │ │ │ └── depth: 0
+ │ │ │ │ │ └── @ LocalVariableTargetNode (location: (12,7)-(12,8))
+ │ │ │ │ │ ├── name: :y
+ │ │ │ │ │ └── depth: 0
+ │ │ │ │ ├── rest:
+ │ │ │ │ │ @ SplatNode (location: (12,10)-(12,11))
+ │ │ │ │ │ ├── operator_loc: (12,10)-(12,11) = "*"
+ │ │ │ │ │ └── expression: ∅
+ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ ├── opening_loc: (12,3)-(12,4) = "["
+ │ │ │ │ └── closing_loc: (12,11)-(12,12) = "]"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (13,2)-(13,6))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ TrueNode (location: (13,2)-(13,6))
+ │ │ │ ├── in_loc: (12,0)-(12,2) = "in"
+ │ │ │ └── then_loc: (12,13)-(12,17) = "then"
+ │ │ ├── @ InNode (location: (14,0)-(15,6))
+ │ │ │ ├── pattern:
+ │ │ │ │ @ HashPatternNode (location: (14,3)-(14,16))
+ │ │ │ │ ├── constant: ∅
+ │ │ │ │ ├── elements: (length: 2)
+ │ │ │ │ │ ├── @ AssocNode (location: (14,4)-(14,8))
+ │ │ │ │ │ │ ├── key:
+ │ │ │ │ │ │ │ @ SymbolNode (location: (14,4)-(14,6))
+ │ │ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ │ │ ├── value_loc: (14,4)-(14,5) = "a"
+ │ │ │ │ │ │ │ ├── closing_loc: (14,5)-(14,6) = ":"
+ │ │ │ │ │ │ │ └── unescaped: "a"
+ │ │ │ │ │ │ ├── value:
+ │ │ │ │ │ │ │ @ IntegerNode (location: (14,7)-(14,8))
+ │ │ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ │ │ └── value: 1
+ │ │ │ │ │ │ └── operator_loc: ∅
+ │ │ │ │ │ └── @ AssocNode (location: (14,10)-(14,15))
+ │ │ │ │ │ ├── key:
+ │ │ │ │ │ │ @ SymbolNode (location: (14,10)-(14,13))
+ │ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ │ ├── value_loc: (14,10)-(14,12) = "aa"
+ │ │ │ │ │ │ ├── closing_loc: (14,12)-(14,13) = ":"
+ │ │ │ │ │ │ └── unescaped: "aa"
+ │ │ │ │ │ ├── value:
+ │ │ │ │ │ │ @ IntegerNode (location: (14,14)-(14,15))
+ │ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ │ └── value: 2
+ │ │ │ │ │ └── operator_loc: ∅
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── opening_loc: (14,3)-(14,4) = "{"
+ │ │ │ │ └── closing_loc: (14,15)-(14,16) = "}"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (15,2)-(15,6))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ TrueNode (location: (15,2)-(15,6))
+ │ │ │ ├── in_loc: (14,0)-(14,2) = "in"
+ │ │ │ └── then_loc: (14,17)-(14,21) = "then"
+ │ │ ├── @ InNode (location: (16,0)-(17,6))
+ │ │ │ ├── pattern:
+ │ │ │ │ @ HashPatternNode (location: (16,3)-(16,5))
+ │ │ │ │ ├── constant: ∅
+ │ │ │ │ ├── elements: (length: 0)
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── opening_loc: (16,3)-(16,4) = "{"
+ │ │ │ │ └── closing_loc: (16,4)-(16,5) = "}"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (17,2)-(17,6))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ TrueNode (location: (17,2)-(17,6))
+ │ │ │ ├── in_loc: (16,0)-(16,2) = "in"
+ │ │ │ └── then_loc: (16,6)-(16,10) = "then"
+ │ │ ├── @ InNode (location: (18,0)-(19,6))
+ │ │ │ ├── pattern:
+ │ │ │ │ @ HashPatternNode (location: (18,3)-(18,10))
+ │ │ │ │ ├── constant: ∅
+ │ │ │ │ ├── elements: (length: 0)
+ │ │ │ │ ├── rest:
+ │ │ │ │ │ @ NoKeywordsParameterNode (location: (18,4)-(18,9))
+ │ │ │ │ │ ├── operator_loc: (18,4)-(18,6) = "**"
+ │ │ │ │ │ └── keyword_loc: (18,6)-(18,9) = "nil"
+ │ │ │ │ ├── opening_loc: (18,3)-(18,4) = "{"
+ │ │ │ │ └── closing_loc: (18,9)-(18,10) = "}"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (19,2)-(19,6))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ TrueNode (location: (19,2)-(19,6))
+ │ │ │ ├── in_loc: (18,0)-(18,2) = "in"
+ │ │ │ └── then_loc: (18,11)-(18,15) = "then"
+ │ │ ├── @ InNode (location: (20,0)-(21,6))
+ │ │ │ ├── pattern:
+ │ │ │ │ @ HashPatternNode (location: (20,3)-(20,11))
+ │ │ │ │ ├── constant: ∅
+ │ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ │ └── @ AssocNode (location: (20,4)-(20,10))
+ │ │ │ │ │ ├── key:
+ │ │ │ │ │ │ @ SymbolNode (location: (20,4)-(20,8))
+ │ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ │ ├── opening_loc: (20,4)-(20,5) = "\""
+ │ │ │ │ │ │ ├── value_loc: (20,5)-(20,6) = "a"
+ │ │ │ │ │ │ ├── closing_loc: (20,6)-(20,8) = "\":"
+ │ │ │ │ │ │ └── unescaped: "a"
+ │ │ │ │ │ ├── value:
+ │ │ │ │ │ │ @ IntegerNode (location: (20,9)-(20,10))
+ │ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ │ └── value: 1
+ │ │ │ │ │ └── operator_loc: ∅
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── opening_loc: (20,3)-(20,4) = "{"
+ │ │ │ │ └── closing_loc: (20,10)-(20,11) = "}"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (21,2)-(21,6))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ TrueNode (location: (21,2)-(21,6))
+ │ │ │ ├── in_loc: (20,0)-(20,2) = "in"
+ │ │ │ └── then_loc: (20,12)-(20,16) = "then"
+ │ │ ├── @ InNode (location: (22,0)-(23,6))
+ │ │ │ ├── pattern:
+ │ │ │ │ @ AlternationPatternNode (location: (22,3)-(22,8))
+ │ │ │ │ ├── left:
+ │ │ │ │ │ @ IntegerNode (location: (22,3)-(22,4))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ ├── right:
+ │ │ │ │ │ @ IntegerNode (location: (22,7)-(22,8))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 2
+ │ │ │ │ └── operator_loc: (22,5)-(22,6) = "|"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (23,2)-(23,6))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ TrueNode (location: (23,2)-(23,6))
+ │ │ │ ├── in_loc: (22,0)-(22,2) = "in"
+ │ │ │ └── then_loc: (22,9)-(22,13) = "then"
+ │ │ ├── @ InNode (location: (24,0)-(25,6))
+ │ │ │ ├── pattern:
+ │ │ │ │ @ CapturePatternNode (location: (24,3)-(24,9))
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ IntegerNode (location: (24,3)-(24,4))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ ├── target:
+ │ │ │ │ │ @ LocalVariableTargetNode (location: (24,8)-(24,9))
+ │ │ │ │ │ ├── name: :a
+ │ │ │ │ │ └── depth: 0
+ │ │ │ │ └── operator_loc: (24,5)-(24,7) = "=>"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (25,2)-(25,6))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ TrueNode (location: (25,2)-(25,6))
+ │ │ │ ├── in_loc: (24,0)-(24,2) = "in"
+ │ │ │ └── then_loc: (24,10)-(24,14) = "then"
+ │ │ ├── @ InNode (location: (26,0)-(27,6))
+ │ │ │ ├── pattern:
+ │ │ │ │ @ PinnedVariableNode (location: (26,3)-(26,5))
+ │ │ │ │ ├── variable:
+ │ │ │ │ │ @ LocalVariableReadNode (location: (26,4)-(26,5))
+ │ │ │ │ │ ├── name: :x
+ │ │ │ │ │ └── depth: 0
+ │ │ │ │ └── operator_loc: (26,3)-(26,4) = "^"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (27,2)-(27,6))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ TrueNode (location: (27,2)-(27,6))
+ │ │ │ ├── in_loc: (26,0)-(26,2) = "in"
+ │ │ │ └── then_loc: (26,6)-(26,10) = "then"
+ │ │ ├── @ InNode (location: (28,0)-(28,4))
+ │ │ │ ├── pattern:
+ │ │ │ │ @ IntegerNode (location: (28,3)-(28,4))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── statements: ∅
+ │ │ │ ├── in_loc: (28,0)-(28,2) = "in"
+ │ │ │ └── then_loc: ∅
+ │ │ └── @ InNode (location: (29,0)-(30,6))
+ │ │ ├── pattern:
+ │ │ │ @ IntegerNode (location: (29,3)-(29,4))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (30,2)-(30,6))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ TrueNode (location: (30,2)-(30,6))
+ │ │ ├── in_loc: (29,0)-(29,2) = "in"
+ │ │ └── then_loc: (29,5)-(29,9) = "then"
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (31,0)-(33,3))
+ │ │ ├── else_keyword_loc: (31,0)-(31,4) = "else"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (32,2)-(32,6))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ TrueNode (location: (32,2)-(32,6))
+ │ │ └── end_keyword_loc: (33,0)-(33,3) = "end"
+ │ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ │ └── end_keyword_loc: (33,0)-(33,3) = "end"
+ ├── @ CaseMatchNode (location: (34,0)-(36,3))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (34,5)-(34,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (34,5)-(34,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (35,0)-(35,17))
+ │ │ ├── pattern:
+ │ │ │ @ ArrayPatternNode (location: (35,3)-(35,17))
+ │ │ │ ├── constant:
+ │ │ │ │ @ ConstantReadNode (location: (35,3)-(35,4))
+ │ │ │ │ └── name: :A
+ │ │ │ ├── requireds: (length: 2)
+ │ │ │ │ ├── @ IntegerNode (location: (35,5)-(35,6))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ └── @ IntegerNode (location: (35,8)-(35,9))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (35,11)-(35,13))
+ │ │ │ │ ├── operator_loc: (35,11)-(35,12) = "*"
+ │ │ │ │ └── expression:
+ │ │ │ │ @ LocalVariableTargetNode (location: (35,12)-(35,13))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── posts: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (35,15)-(35,16))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 3
+ │ │ │ ├── opening_loc: (35,4)-(35,5) = "["
+ │ │ │ └── closing_loc: (35,16)-(35,17) = "]"
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (35,0)-(35,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (34,0)-(34,4) = "case"
+ │ └── end_keyword_loc: (36,0)-(36,3) = "end"
+ ├── @ CaseMatchNode (location: (37,0)-(40,3))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (37,5)-(37,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (37,5)-(37,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 1)
+ │ │ └── @ InNode (location: (38,0)-(38,4))
+ │ │ ├── pattern:
+ │ │ │ @ ConstantReadNode (location: (38,3)-(38,4))
+ │ │ │ └── name: :A
+ │ │ ├── statements: ∅
+ │ │ ├── in_loc: (38,0)-(38,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (39,0)-(40,3))
+ │ │ ├── else_keyword_loc: (39,0)-(39,4) = "else"
+ │ │ ├── statements: ∅
+ │ │ └── end_keyword_loc: (40,0)-(40,3) = "end"
+ │ ├── case_keyword_loc: (37,0)-(37,4) = "case"
+ │ └── end_keyword_loc: (40,0)-(40,3) = "end"
+ └── @ MatchPredicateNode (location: (41,0)-(41,8))
+ ├── value:
+ │ @ IntegerNode (location: (41,0)-(41,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── pattern:
+ │ @ ArrayPatternNode (location: (41,5)-(41,8))
+ │ ├── constant: ∅
+ │ ├── requireds: (length: 1)
+ │ │ └── @ LocalVariableTargetNode (location: (41,6)-(41,7))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── opening_loc: (41,5)-(41,6) = "["
+ │ └── closing_loc: (41,7)-(41,8) = "]"
+ └── operator_loc: (41,2)-(41,4) = "in"
diff --git a/test/prism/snapshots/unparser/corpus/literal/pragma.txt b/test/prism/snapshots/unparser/corpus/literal/pragma.txt
new file mode 100644
index 0000000000..08e386b872
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/pragma.txt
@@ -0,0 +1,20 @@
+@ ProgramNode (location: (1,0)-(4,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,7))
+ └── body: (length: 4)
+ ├── @ SourceEncodingNode (location: (1,0)-(1,12))
+ ├── @ SourceFileNode (location: (2,0)-(2,8))
+ │ ├── flags: ∅
+ │ └── filepath: "unparser/corpus/literal/pragma.txt"
+ ├── @ SourceLineNode (location: (3,0)-(3,8))
+ └── @ CallNode (location: (4,0)-(4,7))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :__dir__
+ ├── message_loc: (4,0)-(4,7) = "__dir__"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/unparser/corpus/literal/range.txt b/test/prism/snapshots/unparser/corpus/literal/range.txt
new file mode 100644
index 0000000000..ab015d04fc
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/range.txt
@@ -0,0 +1,55 @@
+@ ProgramNode (location: (1,0)-(4,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,5))
+ └── body: (length: 4)
+ ├── @ ParenthesesNode (location: (1,0)-(1,5))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,1)-(1,4))
+ │ │ └── body: (length: 1)
+ │ │ └── @ RangeNode (location: (1,1)-(1,4))
+ │ │ ├── flags: ∅
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (1,1)-(1,2))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right: ∅
+ │ │ └── operator_loc: (1,2)-(1,4) = ".."
+ │ ├── opening_loc: (1,0)-(1,1) = "("
+ │ └── closing_loc: (1,4)-(1,5) = ")"
+ ├── @ RangeNode (location: (2,0)-(2,4))
+ │ ├── flags: ∅
+ │ ├── left:
+ │ │ @ IntegerNode (location: (2,0)-(2,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── right:
+ │ │ @ IntegerNode (location: (2,3)-(2,4))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ └── operator_loc: (2,1)-(2,3) = ".."
+ ├── @ ParenthesesNode (location: (3,0)-(3,6))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (3,1)-(3,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ RangeNode (location: (3,1)-(3,5))
+ │ │ ├── flags: exclude_end
+ │ │ ├── left:
+ │ │ │ @ IntegerNode (location: (3,1)-(3,2))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── right: ∅
+ │ │ └── operator_loc: (3,2)-(3,5) = "..."
+ │ ├── opening_loc: (3,0)-(3,1) = "("
+ │ └── closing_loc: (3,5)-(3,6) = ")"
+ └── @ RangeNode (location: (4,0)-(4,5))
+ ├── flags: exclude_end
+ ├── left:
+ │ @ IntegerNode (location: (4,0)-(4,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── right:
+ │ @ IntegerNode (location: (4,4)-(4,5))
+ │ ├── flags: decimal
+ │ └── value: 2
+ └── operator_loc: (4,1)-(4,4) = "..."
diff --git a/test/prism/snapshots/unparser/corpus/literal/rescue.txt b/test/prism/snapshots/unparser/corpus/literal/rescue.txt
new file mode 100644
index 0000000000..d3c9d62166
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/rescue.txt
@@ -0,0 +1,103 @@
+@ ProgramNode (location: (1,0)-(3,27))
+├── locals: [:x]
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,27))
+ └── body: (length: 3)
+ ├── @ RescueModifierNode (location: (1,0)-(1,14))
+ │ ├── expression:
+ │ │ @ CallNode (location: (1,0)-(1,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (1,4)-(1,10) = "rescue"
+ │ └── rescue_expression:
+ │ @ CallNode (location: (1,11)-(1,14))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (1,11)-(1,14) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ RescueModifierNode (location: (2,0)-(2,21))
+ │ ├── expression:
+ │ │ @ CallNode (location: (2,0)-(2,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (2,0)-(2,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (2,4)-(2,10) = "rescue"
+ │ └── rescue_expression:
+ │ @ ReturnNode (location: (2,11)-(2,21))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (2,11)-(2,17) = "return"
+ │ └── arguments:
+ │ @ ArgumentsNode (location: (2,18)-(2,21))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (2,18)-(2,21))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (2,18)-(2,21) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ LocalVariableWriteNode (location: (3,0)-(3,27))
+ ├── name: :x
+ ├── depth: 0
+ ├── name_loc: (3,0)-(3,1) = "x"
+ ├── value:
+ │ @ ParenthesesNode (location: (3,4)-(3,27))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (3,5)-(3,26))
+ │ │ └── body: (length: 1)
+ │ │ └── @ RescueModifierNode (location: (3,5)-(3,26))
+ │ │ ├── expression:
+ │ │ │ @ CallNode (location: (3,5)-(3,8))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (3,5)-(3,8) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── keyword_loc: (3,9)-(3,15) = "rescue"
+ │ │ └── rescue_expression:
+ │ │ @ ReturnNode (location: (3,16)-(3,26))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (3,16)-(3,22) = "return"
+ │ │ └── arguments:
+ │ │ @ ArgumentsNode (location: (3,23)-(3,26))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (3,23)-(3,26))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (3,23)-(3,26) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (3,4)-(3,5) = "("
+ │ └── closing_loc: (3,26)-(3,27) = ")"
+ └── operator_loc: (3,2)-(3,3) = "="
diff --git a/test/prism/snapshots/unparser/corpus/literal/send.txt b/test/prism/snapshots/unparser/corpus/literal/send.txt
new file mode 100644
index 0000000000..b7eb064717
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/send.txt
@@ -0,0 +1,2190 @@
+@ ProgramNode (location: (1,0)-(84,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(84,7))
+ └── body: (length: 62)
+ ├── @ ModuleNode (location: (1,0)-(3,3))
+ │ ├── locals: [:foo, :a, :_]
+ │ ├── module_keyword_loc: (1,0)-(1,6) = "module"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (1,7)-(1,8))
+ │ │ └── name: :A
+ │ ├── body:
+ │ │ @ StatementsNode (location: (2,2)-(2,22))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableOrWriteNode (location: (2,2)-(2,22))
+ │ │ ├── name_loc: (2,2)-(2,5) = "foo"
+ │ │ ├── operator_loc: (2,6)-(2,9) = "||="
+ │ │ ├── value:
+ │ │ │ @ ParenthesesNode (location: (2,10)-(2,22))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (2,11)-(2,21))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ MultiWriteNode (location: (2,11)-(2,21))
+ │ │ │ │ ├── lefts: (length: 2)
+ │ │ │ │ │ ├── @ LocalVariableTargetNode (location: (2,12)-(2,13))
+ │ │ │ │ │ │ ├── name: :a
+ │ │ │ │ │ │ └── depth: 0
+ │ │ │ │ │ └── @ LocalVariableTargetNode (location: (2,15)-(2,16))
+ │ │ │ │ │ ├── name: :_
+ │ │ │ │ │ └── depth: 0
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── rights: (length: 0)
+ │ │ │ │ ├── lparen_loc: (2,11)-(2,12) = "("
+ │ │ │ │ ├── rparen_loc: (2,16)-(2,17) = ")"
+ │ │ │ │ ├── operator_loc: (2,18)-(2,19) = "="
+ │ │ │ │ └── value:
+ │ │ │ │ @ CallNode (location: (2,20)-(2,21))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── message_loc: (2,20)-(2,21) = "b"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── opening_loc: (2,10)-(2,11) = "("
+ │ │ │ └── closing_loc: (2,21)-(2,22) = ")"
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── end_keyword_loc: (3,0)-(3,3) = "end"
+ │ └── name: :A
+ ├── @ ModuleNode (location: (5,0)-(8,3))
+ │ ├── locals: [:local]
+ │ ├── module_keyword_loc: (5,0)-(5,6) = "module"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (5,7)-(5,8))
+ │ │ └── name: :A
+ │ ├── body:
+ │ │ @ StatementsNode (location: (6,2)-(7,11))
+ │ │ └── body: (length: 2)
+ │ │ ├── @ LocalVariableWriteNode (location: (6,2)-(6,11))
+ │ │ │ ├── name: :local
+ │ │ │ ├── depth: 0
+ │ │ │ ├── name_loc: (6,2)-(6,7) = "local"
+ │ │ │ ├── value:
+ │ │ │ │ @ IntegerNode (location: (6,10)-(6,11))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── operator_loc: (6,8)-(6,9) = "="
+ │ │ └── @ CallNode (location: (7,2)-(7,11))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ LocalVariableReadNode (location: (7,2)-(7,7))
+ │ │ │ ├── name: :local
+ │ │ │ └── depth: 0
+ │ │ ├── call_operator_loc: (7,7)-(7,8) = "."
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (7,8)-(7,11) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── end_keyword_loc: (8,0)-(8,3) = "end"
+ │ └── name: :A
+ ├── @ CallNode (location: (9,0)-(10,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ClassNode (location: (9,0)-(10,3))
+ │ │ ├── locals: []
+ │ │ ├── class_keyword_loc: (9,0)-(9,5) = "class"
+ │ │ ├── constant_path:
+ │ │ │ @ ConstantReadNode (location: (9,6)-(9,7))
+ │ │ │ └── name: :A
+ │ │ ├── inheritance_operator_loc: ∅
+ │ │ ├── superclass: ∅
+ │ │ ├── body: ∅
+ │ │ ├── end_keyword_loc: (10,0)-(10,3) = "end"
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (10,3)-(10,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (10,4)-(10,7) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (11,0)-(12,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ModuleNode (location: (11,0)-(12,3))
+ │ │ ├── locals: []
+ │ │ ├── module_keyword_loc: (11,0)-(11,6) = "module"
+ │ │ ├── constant_path:
+ │ │ │ @ ConstantReadNode (location: (11,7)-(11,8))
+ │ │ │ └── name: :A
+ │ │ ├── body: ∅
+ │ │ ├── end_keyword_loc: (12,0)-(12,3) = "end"
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (12,3)-(12,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (12,4)-(12,7) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (13,0)-(15,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ BeginNode (location: (13,0)-(15,3))
+ │ │ ├── begin_keyword_loc: (13,0)-(13,5) = "begin"
+ │ │ ├── statements: ∅
+ │ │ ├── rescue_clause:
+ │ │ │ @ RescueNode (location: (14,0)-(14,6))
+ │ │ │ ├── keyword_loc: (14,0)-(14,6) = "rescue"
+ │ │ │ ├── exceptions: (length: 0)
+ │ │ │ ├── operator_loc: ∅
+ │ │ │ ├── reference: ∅
+ │ │ │ ├── statements: ∅
+ │ │ │ └── consequent: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause: ∅
+ │ │ └── end_keyword_loc: (15,0)-(15,3) = "end"
+ │ ├── call_operator_loc: (15,3)-(15,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (15,4)-(15,7) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (16,0)-(19,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CaseNode (location: (16,0)-(19,3))
+ │ │ ├── predicate:
+ │ │ │ @ ParenthesesNode (location: (16,5)-(17,10))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (16,6)-(17,9))
+ │ │ │ │ └── body: (length: 2)
+ │ │ │ │ ├── @ DefNode (location: (16,6)-(17,3))
+ │ │ │ │ │ ├── name: :foo
+ │ │ │ │ │ ├── name_loc: (16,10)-(16,13) = "foo"
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ │ ├── body: ∅
+ │ │ │ │ │ ├── locals: []
+ │ │ │ │ │ ├── def_keyword_loc: (16,6)-(16,9) = "def"
+ │ │ │ │ │ ├── operator_loc: ∅
+ │ │ │ │ │ ├── lparen_loc: ∅
+ │ │ │ │ │ ├── rparen_loc: ∅
+ │ │ │ │ │ ├── equal_loc: ∅
+ │ │ │ │ │ └── end_keyword_loc: (17,0)-(17,3) = "end"
+ │ │ │ │ └── @ SymbolNode (location: (17,5)-(17,9))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (17,5)-(17,6) = ":"
+ │ │ │ │ ├── value_loc: (17,6)-(17,9) = "bar"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "bar"
+ │ │ │ ├── opening_loc: (16,5)-(16,6) = "("
+ │ │ │ └── closing_loc: (17,9)-(17,10) = ")"
+ │ │ ├── conditions: (length: 1)
+ │ │ │ └── @ WhenNode (location: (18,0)-(18,8))
+ │ │ │ ├── keyword_loc: (18,0)-(18,4) = "when"
+ │ │ │ ├── conditions: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (18,5)-(18,8))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (18,5)-(18,8) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ └── statements: ∅
+ │ │ ├── consequent: ∅
+ │ │ ├── case_keyword_loc: (16,0)-(16,4) = "case"
+ │ │ └── end_keyword_loc: (19,0)-(19,3) = "end"
+ │ ├── call_operator_loc: (19,3)-(19,4) = "."
+ │ ├── name: :baz
+ │ ├── message_loc: (19,4)-(19,7) = "baz"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (20,0)-(22,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CaseNode (location: (20,0)-(22,3))
+ │ │ ├── predicate:
+ │ │ │ @ CallNode (location: (20,5)-(20,8))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (20,5)-(20,8) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── conditions: (length: 1)
+ │ │ │ └── @ WhenNode (location: (21,0)-(21,8))
+ │ │ │ ├── keyword_loc: (21,0)-(21,4) = "when"
+ │ │ │ ├── conditions: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (21,5)-(21,8))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (21,5)-(21,8) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── then_keyword_loc: ∅
+ │ │ │ └── statements: ∅
+ │ │ ├── consequent: ∅
+ │ │ ├── case_keyword_loc: (20,0)-(20,4) = "case"
+ │ │ └── end_keyword_loc: (22,0)-(22,3) = "end"
+ │ ├── call_operator_loc: (22,3)-(22,4) = "."
+ │ ├── name: :baz
+ │ ├── message_loc: (22,4)-(22,7) = "baz"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (23,0)-(24,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ SingletonClassNode (location: (23,0)-(24,3))
+ │ │ ├── locals: []
+ │ │ ├── class_keyword_loc: (23,0)-(23,5) = "class"
+ │ │ ├── operator_loc: (23,6)-(23,8) = "<<"
+ │ │ ├── expression:
+ │ │ │ @ SelfNode (location: (23,9)-(23,13))
+ │ │ ├── body: ∅
+ │ │ └── end_keyword_loc: (24,0)-(24,3) = "end"
+ │ ├── call_operator_loc: (24,3)-(24,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (24,4)-(24,7) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (25,0)-(26,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ DefNode (location: (25,0)-(26,3))
+ │ │ ├── name: :foo
+ │ │ ├── name_loc: (25,9)-(25,12) = "foo"
+ │ │ ├── receiver:
+ │ │ │ @ SelfNode (location: (25,4)-(25,8))
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── locals: []
+ │ │ ├── def_keyword_loc: (25,0)-(25,3) = "def"
+ │ │ ├── operator_loc: (25,8)-(25,9) = "."
+ │ │ ├── lparen_loc: ∅
+ │ │ ├── rparen_loc: ∅
+ │ │ ├── equal_loc: ∅
+ │ │ └── end_keyword_loc: (26,0)-(26,3) = "end"
+ │ ├── call_operator_loc: (26,3)-(26,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (26,4)-(26,7) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (27,0)-(28,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ DefNode (location: (27,0)-(28,3))
+ │ │ ├── name: :foo
+ │ │ ├── name_loc: (27,4)-(27,7) = "foo"
+ │ │ ├── receiver: ∅
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── locals: []
+ │ │ ├── def_keyword_loc: (27,0)-(27,3) = "def"
+ │ │ ├── operator_loc: ∅
+ │ │ ├── lparen_loc: ∅
+ │ │ ├── rparen_loc: ∅
+ │ │ ├── equal_loc: ∅
+ │ │ └── end_keyword_loc: (28,0)-(28,3) = "end"
+ │ ├── call_operator_loc: (28,3)-(28,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (28,4)-(28,7) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (29,0)-(30,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ UntilNode (location: (29,0)-(30,3))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (29,0)-(29,5) = "until"
+ │ │ ├── closing_loc: (30,0)-(30,3) = "end"
+ │ │ ├── predicate:
+ │ │ │ @ CallNode (location: (29,6)-(29,9))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (29,6)-(29,9) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── statements: ∅
+ │ ├── call_operator_loc: (30,3)-(30,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (30,4)-(30,7) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (31,0)-(32,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ WhileNode (location: (31,0)-(32,3))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (31,0)-(31,5) = "while"
+ │ │ ├── closing_loc: (32,0)-(32,3) = "end"
+ │ │ ├── predicate:
+ │ │ │ @ CallNode (location: (31,6)-(31,9))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (31,6)-(31,9) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── statements: ∅
+ │ ├── call_operator_loc: (32,3)-(32,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (32,4)-(32,7) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (33,0)-(34,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (33,0)-(34,1))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :loop
+ │ │ ├── message_loc: (33,0)-(33,4) = "loop"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (33,5)-(34,1))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (33,5)-(33,6) = "{"
+ │ │ └── closing_loc: (34,0)-(34,1) = "}"
+ │ ├── call_operator_loc: (34,1)-(34,2) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (34,2)-(34,5) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (35,0)-(36,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IfNode (location: (35,0)-(36,3))
+ │ │ ├── if_keyword_loc: (35,0)-(35,2) = "if"
+ │ │ ├── predicate:
+ │ │ │ @ CallNode (location: (35,3)-(35,6))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (35,3)-(35,6) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── then_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── consequent: ∅
+ │ │ └── end_keyword_loc: (36,0)-(36,3) = "end"
+ │ ├── call_operator_loc: (36,3)-(36,4) = "."
+ │ ├── name: :baz
+ │ ├── message_loc: (36,4)-(36,7) = "baz"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (37,0)-(37,19))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (37,0)-(37,15))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (37,1)-(37,14))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (37,1)-(37,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ RegularExpressionNode (location: (37,1)-(37,6))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (37,1)-(37,2) = "/"
+ │ │ │ │ ├── content_loc: (37,2)-(37,5) = "bar"
+ │ │ │ │ ├── closing_loc: (37,5)-(37,6) = "/"
+ │ │ │ │ └── unescaped: "bar"
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :=~
+ │ │ │ ├── message_loc: (37,7)-(37,9) = "=~"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (37,10)-(37,14))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ SymbolNode (location: (37,10)-(37,14))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (37,10)-(37,11) = ":"
+ │ │ │ │ ├── value_loc: (37,11)-(37,14) = "foo"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (37,0)-(37,1) = "("
+ │ │ └── closing_loc: (37,14)-(37,15) = ")"
+ │ ├── call_operator_loc: (37,15)-(37,16) = "."
+ │ ├── name: :foo
+ │ ├── message_loc: (37,16)-(37,19) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (38,0)-(38,10))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (38,0)-(38,6))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (38,1)-(38,5))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ RangeNode (location: (38,1)-(38,5))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── left:
+ │ │ │ │ @ IntegerNode (location: (38,1)-(38,2))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── right:
+ │ │ │ │ @ IntegerNode (location: (38,4)-(38,5))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ └── operator_loc: (38,2)-(38,4) = ".."
+ │ │ ├── opening_loc: (38,0)-(38,1) = "("
+ │ │ └── closing_loc: (38,5)-(38,6) = ")"
+ │ ├── call_operator_loc: (38,6)-(38,7) = "."
+ │ ├── name: :max
+ │ ├── message_loc: (38,7)-(38,10) = "max"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (39,0)-(39,18))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (39,0)-(39,14))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (39,1)-(39,13))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (39,1)-(39,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ CallNode (location: (39,1)-(39,4))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :foo
+ │ │ │ │ ├── message_loc: (39,1)-(39,4) = "foo"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :=~
+ │ │ │ ├── message_loc: (39,5)-(39,7) = "=~"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (39,8)-(39,13))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ RegularExpressionNode (location: (39,8)-(39,13))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (39,8)-(39,9) = "/"
+ │ │ │ │ ├── content_loc: (39,9)-(39,12) = "bar"
+ │ │ │ │ ├── closing_loc: (39,12)-(39,13) = "/"
+ │ │ │ │ └── unescaped: "bar"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (39,0)-(39,1) = "("
+ │ │ └── closing_loc: (39,13)-(39,14) = ")"
+ │ ├── call_operator_loc: (39,14)-(39,15) = "."
+ │ ├── name: :foo
+ │ ├── message_loc: (39,15)-(39,18) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (40,0)-(40,13))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ RegularExpressionNode (location: (40,0)-(40,5))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (40,0)-(40,1) = "/"
+ │ │ ├── content_loc: (40,1)-(40,4) = "bar"
+ │ │ ├── closing_loc: (40,4)-(40,5) = "/"
+ │ │ └── unescaped: "bar"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :=~
+ │ ├── message_loc: (40,6)-(40,8) = "=~"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (40,9)-(40,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ SymbolNode (location: (40,9)-(40,13))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (40,9)-(40,10) = ":"
+ │ │ ├── value_loc: (40,10)-(40,13) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (41,0)-(41,12))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ RegularExpressionNode (location: (41,0)-(41,5))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (41,0)-(41,1) = "/"
+ │ │ ├── content_loc: (41,1)-(41,4) = "bar"
+ │ │ ├── closing_loc: (41,4)-(41,5) = "/"
+ │ │ └── unescaped: "bar"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :=~
+ │ ├── message_loc: (41,6)-(41,8) = "=~"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (41,9)-(41,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (41,9)-(41,12))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (41,9)-(41,12) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ RangeNode (location: (42,0)-(42,8))
+ │ ├── flags: ∅
+ │ ├── left:
+ │ │ @ IntegerNode (location: (42,0)-(42,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── right:
+ │ │ @ CallNode (location: (42,3)-(42,8))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ IntegerNode (location: (42,3)-(42,4))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── call_operator_loc: (42,4)-(42,5) = "."
+ │ │ ├── name: :max
+ │ │ ├── message_loc: (42,5)-(42,8) = "max"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (42,1)-(42,3) = ".."
+ ├── @ CallNode (location: (43,0)-(43,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (43,0)-(43,1))
+ │ │ └── name: :A
+ │ ├── call_operator_loc: (43,1)-(43,2) = "."
+ │ ├── name: :foo
+ │ ├── message_loc: (43,2)-(43,5) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (44,0)-(44,5))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :FOO
+ │ ├── message_loc: (44,0)-(44,3) = "FOO"
+ │ ├── opening_loc: (44,3)-(44,4) = "("
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (44,4)-(44,5) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (45,0)-(45,4))
+ │ ├── flags: safe_navigation
+ │ ├── receiver:
+ │ │ @ CallNode (location: (45,0)-(45,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (45,0)-(45,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (45,1)-(45,3) = "&."
+ │ ├── name: :b
+ │ ├── message_loc: (45,3)-(45,4) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (46,0)-(46,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (46,0)-(46,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (46,0)-(46,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (46,1)-(46,2) = "."
+ │ ├── name: :foo
+ │ ├── message_loc: (46,2)-(46,5) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (47,0)-(47,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (47,0)-(47,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (48,0)-(48,18))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (48,0)-(48,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (48,0)-(48,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :<<
+ │ ├── message_loc: (48,4)-(48,6) = "<<"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (48,7)-(48,18))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ ParenthesesNode (location: (48,7)-(48,18))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (48,8)-(48,17))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (48,8)-(48,17))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ CallNode (location: (48,8)-(48,11))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (48,8)-(48,11) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :*
+ │ │ │ ├── message_loc: (48,12)-(48,13) = "*"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (48,14)-(48,17))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (48,14)-(48,17))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ ├── message_loc: (48,14)-(48,17) = "baz"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (48,7)-(48,8) = "("
+ │ │ └── closing_loc: (48,17)-(48,18) = ")"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (49,0)-(49,12))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (49,0)-(49,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (49,0)-(49,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :=~
+ │ ├── message_loc: (49,4)-(49,6) = "=~"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (49,7)-(49,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ RegularExpressionNode (location: (49,7)-(49,12))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (49,7)-(49,8) = "/"
+ │ │ ├── content_loc: (49,8)-(49,11) = "bar"
+ │ │ ├── closing_loc: (49,11)-(49,12) = "/"
+ │ │ └── unescaped: "bar"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (50,0)-(50,17))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (50,0)-(50,3) = "foo"
+ │ ├── opening_loc: (50,3)-(50,4) = "("
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (50,17)-(50,18) = ")"
+ │ └── block:
+ │ @ BlockArgumentNode (location: (50,4)-(50,17))
+ │ ├── expression:
+ │ │ @ ParenthesesNode (location: (50,5)-(50,17))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (50,6)-(50,16))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ OrNode (location: (50,6)-(50,16))
+ │ │ │ ├── left:
+ │ │ │ │ @ CallNode (location: (50,6)-(50,9))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :foo
+ │ │ │ │ ├── message_loc: (50,6)-(50,9) = "foo"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── right:
+ │ │ │ │ @ CallNode (location: (50,13)-(50,16))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (50,13)-(50,16) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── operator_loc: (50,10)-(50,12) = "||"
+ │ │ ├── opening_loc: (50,5)-(50,6) = "("
+ │ │ └── closing_loc: (50,16)-(50,17) = ")"
+ │ └── operator_loc: (50,4)-(50,5) = "&"
+ ├── @ CallNode (location: (51,0)-(51,10))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (51,0)-(51,3) = "foo"
+ │ ├── opening_loc: (51,3)-(51,4) = "("
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (51,10)-(51,11) = ")"
+ │ └── block:
+ │ @ BlockArgumentNode (location: (51,4)-(51,10))
+ │ ├── expression:
+ │ │ @ CallNode (location: (51,5)-(51,10))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :block
+ │ │ ├── message_loc: (51,5)-(51,10) = "block"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (51,4)-(51,5) = "&"
+ ├── @ CallNode (location: (52,0)-(52,17))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (52,0)-(52,3) = "foo"
+ │ ├── opening_loc: (52,3)-(52,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (52,4)-(52,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ SplatNode (location: (52,4)-(52,9))
+ │ │ ├── operator_loc: (52,4)-(52,5) = "*"
+ │ │ └── expression:
+ │ │ @ CallNode (location: (52,5)-(52,9))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :args
+ │ │ ├── message_loc: (52,5)-(52,9) = "args"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (52,17)-(52,18) = ")"
+ │ └── block:
+ │ @ BlockArgumentNode (location: (52,11)-(52,17))
+ │ ├── expression:
+ │ │ @ CallNode (location: (52,12)-(52,17))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :block
+ │ │ ├── message_loc: (52,12)-(52,17) = "block"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (52,11)-(52,12) = "&"
+ ├── @ CallNode (location: (53,0)-(53,15))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (53,0)-(53,3) = "foo"
+ │ ├── opening_loc: (53,3)-(53,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (53,4)-(53,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ SplatNode (location: (53,4)-(53,14))
+ │ │ ├── operator_loc: (53,4)-(53,5) = "*"
+ │ │ └── expression:
+ │ │ @ CallNode (location: (53,5)-(53,14))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :arguments
+ │ │ ├── message_loc: (53,5)-(53,14) = "arguments"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (53,14)-(53,15) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (54,0)-(54,9))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (54,0)-(54,3) = "foo"
+ │ ├── opening_loc: (54,3)-(54,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (54,4)-(54,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ IntegerNode (location: (54,4)-(54,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (54,7)-(54,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: (54,8)-(54,9) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (55,0)-(55,8))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (55,0)-(55,3) = "foo"
+ │ ├── opening_loc: (55,3)-(55,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (55,4)-(55,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (55,4)-(55,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (55,4)-(55,7) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (55,7)-(55,8) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (56,0)-(56,15))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (56,0)-(56,3) = "foo"
+ │ ├── opening_loc: (56,3)-(56,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (56,4)-(56,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (56,4)-(56,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (56,4)-(56,7) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ SplatNode (location: (56,9)-(56,14))
+ │ │ ├── operator_loc: (56,9)-(56,10) = "*"
+ │ │ └── expression:
+ │ │ @ CallNode (location: (56,10)-(56,14))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :args
+ │ │ ├── message_loc: (56,10)-(56,14) = "args"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (56,14)-(56,15) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (57,0)-(57,17))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (57,0)-(57,3) = "foo"
+ │ ├── opening_loc: (57,3)-(57,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (57,4)-(57,16))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (57,4)-(57,16))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (57,4)-(57,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (57,4)-(57,7) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :=~
+ │ │ ├── message_loc: (57,8)-(57,10) = "=~"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (57,11)-(57,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ RegularExpressionNode (location: (57,11)-(57,16))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (57,11)-(57,12) = "/"
+ │ │ │ ├── content_loc: (57,12)-(57,15) = "bar"
+ │ │ │ ├── closing_loc: (57,15)-(57,16) = "/"
+ │ │ │ └── unescaped: "bar"
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (57,16)-(57,17) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (58,0)-(58,13))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (58,0)-(58,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (58,0)-(58,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (58,3)-(58,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (58,4)-(58,7) = "bar"
+ │ ├── opening_loc: (58,7)-(58,8) = "("
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (58,12)-(58,13) = ")"
+ │ └── block:
+ │ @ BlockArgumentNode (location: (58,8)-(58,12))
+ │ ├── expression:
+ │ │ @ CallNode (location: (58,9)-(58,12))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (58,9)-(58,12) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (58,8)-(58,9) = "&"
+ ├── @ CallNode (location: (59,0)-(59,26))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (59,0)-(59,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (59,0)-(59,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (59,3)-(59,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (59,4)-(59,7) = "bar"
+ │ ├── opening_loc: (59,7)-(59,8) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (59,8)-(59,25))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 3)
+ │ │ ├── @ SplatNode (location: (59,8)-(59,13))
+ │ │ │ ├── operator_loc: (59,8)-(59,9) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ CallNode (location: (59,9)-(59,13))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :arga
+ │ │ │ ├── message_loc: (59,9)-(59,13) = "arga"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── @ CallNode (location: (59,15)-(59,18))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (59,15)-(59,18) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ SplatNode (location: (59,20)-(59,25))
+ │ │ ├── operator_loc: (59,20)-(59,21) = "*"
+ │ │ └── expression:
+ │ │ @ CallNode (location: (59,21)-(59,25))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :argb
+ │ │ ├── message_loc: (59,21)-(59,25) = "argb"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (59,25)-(59,26) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (60,0)-(60,14))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (60,0)-(60,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (60,0)-(60,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (60,3)-(60,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (60,4)-(60,7) = "bar"
+ │ ├── opening_loc: (60,7)-(60,8) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (60,8)-(60,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ SplatNode (location: (60,8)-(60,13))
+ │ │ ├── operator_loc: (60,8)-(60,9) = "*"
+ │ │ └── expression:
+ │ │ @ CallNode (location: (60,9)-(60,13))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :args
+ │ │ ├── message_loc: (60,9)-(60,13) = "args"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (60,13)-(60,14) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (61,0)-(61,19))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (61,0)-(61,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (61,0)-(61,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (61,3)-(61,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (61,4)-(61,7) = "bar"
+ │ ├── opening_loc: (61,7)-(61,8) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (61,8)-(61,18))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ SplatNode (location: (61,8)-(61,13))
+ │ │ │ ├── operator_loc: (61,8)-(61,9) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ CallNode (location: (61,9)-(61,13))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :args
+ │ │ │ ├── message_loc: (61,9)-(61,13) = "args"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ CallNode (location: (61,15)-(61,18))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (61,15)-(61,18) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (61,18)-(61,19) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (62,0)-(62,19))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (62,0)-(62,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (62,0)-(62,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (62,3)-(62,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (62,4)-(62,7) = "bar"
+ │ ├── opening_loc: (62,7)-(62,8) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (62,8)-(62,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ SymbolNode (location: (62,8)-(62,12))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (62,8)-(62,9) = ":"
+ │ │ ├── value_loc: (62,9)-(62,12) = "baz"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "baz"
+ │ ├── closing_loc: (62,18)-(62,19) = ")"
+ │ └── block:
+ │ @ BlockArgumentNode (location: (62,14)-(62,18))
+ │ ├── expression:
+ │ │ @ CallNode (location: (62,15)-(62,18))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (62,15)-(62,18) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (62,14)-(62,15) = "&"
+ ├── @ CallNode (location: (63,0)-(63,17))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (63,0)-(63,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (63,0)-(63,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (63,3)-(63,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (63,4)-(63,7) = "bar"
+ │ ├── opening_loc: (63,7)-(63,8) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (63,8)-(63,16))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (63,8)-(63,16))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (63,8)-(63,16))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (63,8)-(63,12))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (63,8)-(63,11) = "baz"
+ │ │ │ ├── closing_loc: (63,11)-(63,12) = ":"
+ │ │ │ └── unescaped: "baz"
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (63,13)-(63,16))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :boz
+ │ │ │ ├── message_loc: (63,13)-(63,16) = "boz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: ∅
+ │ ├── closing_loc: (63,16)-(63,17) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (64,0)-(64,26))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (64,0)-(64,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (64,0)-(64,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (64,3)-(64,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (64,4)-(64,7) = "bar"
+ │ ├── opening_loc: (64,7)-(64,8) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (64,8)-(64,25))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (64,8)-(64,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (64,8)-(64,11) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ KeywordHashNode (location: (64,13)-(64,25))
+ │ │ ├── flags: ∅
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (64,13)-(64,25))
+ │ │ ├── key:
+ │ │ │ @ StringNode (location: (64,13)-(64,18))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: (64,13)-(64,14) = "\""
+ │ │ │ ├── content_loc: (64,14)-(64,17) = "baz"
+ │ │ │ ├── closing_loc: (64,17)-(64,18) = "\""
+ │ │ │ └── unescaped: "baz"
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (64,22)-(64,25))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :boz
+ │ │ │ ├── message_loc: (64,22)-(64,25) = "boz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (64,19)-(64,21) = "=>"
+ │ ├── closing_loc: (64,25)-(64,26) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (65,0)-(65,19))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (65,0)-(65,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (65,0)-(65,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (65,3)-(65,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (65,4)-(65,7) = "bar"
+ │ ├── opening_loc: (65,7)-(65,8) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (65,8)-(65,18))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (65,8)-(65,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (65,8)-(65,11) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ SplatNode (location: (65,13)-(65,18))
+ │ │ ├── operator_loc: (65,13)-(65,14) = "*"
+ │ │ └── expression:
+ │ │ @ CallNode (location: (65,14)-(65,18))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :args
+ │ │ ├── message_loc: (65,14)-(65,18) = "args"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (65,18)-(65,19) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (66,0)-(66,27))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (66,0)-(66,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (66,0)-(66,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (66,3)-(66,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (66,4)-(66,7) = "bar"
+ │ ├── opening_loc: (66,7)-(66,8) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (66,8)-(66,18))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (66,8)-(66,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (66,8)-(66,11) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ SplatNode (location: (66,13)-(66,18))
+ │ │ ├── operator_loc: (66,13)-(66,14) = "*"
+ │ │ └── expression:
+ │ │ @ CallNode (location: (66,14)-(66,18))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :args
+ │ │ ├── message_loc: (66,14)-(66,18) = "args"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (66,26)-(66,27) = ")"
+ │ └── block:
+ │ @ BlockArgumentNode (location: (66,20)-(66,26))
+ │ ├── expression:
+ │ │ @ CallNode (location: (66,21)-(66,26))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :block
+ │ │ ├── message_loc: (66,21)-(66,26) = "block"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (66,20)-(66,21) = "&"
+ ├── @ CallNode (location: (67,0)-(67,16))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (67,0)-(67,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (67,0)-(67,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (67,3)-(67,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (67,4)-(67,7) = "bar"
+ │ ├── opening_loc: (67,7)-(67,8) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (67,8)-(67,15))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (67,8)-(67,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (67,8)-(67,11) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ HashNode (location: (67,13)-(67,15))
+ │ │ ├── opening_loc: (67,13)-(67,14) = "{"
+ │ │ ├── elements: (length: 0)
+ │ │ └── closing_loc: (67,14)-(67,15) = "}"
+ │ ├── closing_loc: (67,15)-(67,16) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (68,0)-(68,26))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (68,0)-(68,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (68,0)-(68,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (68,3)-(68,4) = "."
+ │ ├── name: :bar
+ │ ├── message_loc: (68,4)-(68,7) = "bar"
+ │ ├── opening_loc: (68,7)-(68,8) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (68,8)-(68,25))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ HashNode (location: (68,8)-(68,20))
+ │ │ │ ├── opening_loc: (68,8)-(68,9) = "{"
+ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ └── @ AssocNode (location: (68,10)-(68,18))
+ │ │ │ │ ├── key:
+ │ │ │ │ │ @ SymbolNode (location: (68,10)-(68,14))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── value_loc: (68,10)-(68,13) = "foo"
+ │ │ │ │ │ ├── closing_loc: (68,13)-(68,14) = ":"
+ │ │ │ │ │ └── unescaped: "foo"
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ CallNode (location: (68,15)-(68,18))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :boz
+ │ │ │ │ │ ├── message_loc: (68,15)-(68,18) = "boz"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ └── operator_loc: ∅
+ │ │ │ └── closing_loc: (68,19)-(68,20) = "}"
+ │ │ └── @ CallNode (location: (68,22)-(68,25))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :boz
+ │ │ ├── message_loc: (68,22)-(68,25) = "boz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (68,25)-(68,26) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (69,0)-(69,12))
+ │ ├── flags: attribute_write
+ │ ├── receiver:
+ │ │ @ CallNode (location: (69,0)-(69,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (69,0)-(69,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (69,3)-(69,4) = "."
+ │ ├── name: :bar=
+ │ ├── message_loc: (69,4)-(69,7) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (69,8)-(69,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ SymbolNode (location: (69,8)-(69,12))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (69,8)-(69,9) = ":"
+ │ │ ├── value_loc: (69,9)-(69,12) = "baz"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "baz"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (70,0)-(70,9))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (70,0)-(70,3) = "foo"
+ │ ├── opening_loc: (70,3)-(70,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (70,4)-(70,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (70,4)-(70,8))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (70,4)-(70,8))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (70,4)-(70,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (70,4)-(70,5) = "a"
+ │ │ │ ├── closing_loc: (70,5)-(70,6) = ":"
+ │ │ │ └── unescaped: "a"
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (70,7)-(70,8))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (70,7)-(70,8) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: ∅
+ │ ├── closing_loc: (70,8)-(70,9) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (71,0)-(71,11))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (71,0)-(71,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (71,0)-(71,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (71,3)-(71,4) = "."
+ │ ├── name: :&
+ │ ├── message_loc: (71,4)-(71,5) = "&"
+ │ ├── opening_loc: (71,5)-(71,6) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (71,6)-(71,10))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (71,6)-(71,10))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (71,6)-(71,10))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (71,6)-(71,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (71,6)-(71,7) = "a"
+ │ │ │ ├── closing_loc: (71,7)-(71,8) = ":"
+ │ │ │ └── unescaped: "a"
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (71,9)-(71,10))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (71,9)-(71,10) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: ∅
+ │ ├── closing_loc: (71,10)-(71,11) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (72,0)-(72,10))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (72,0)-(72,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (72,0)-(72,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (72,3)-(72,4) = "."
+ │ ├── name: :&
+ │ ├── message_loc: (72,4)-(72,5) = "&"
+ │ ├── opening_loc: (72,5)-(72,6) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (72,6)-(72,9))
+ │ │ ├── flags: contains_keyword_splat
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (72,6)-(72,9))
+ │ │ ├── flags: ∅
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocSplatNode (location: (72,6)-(72,9))
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (72,8)-(72,9))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (72,8)-(72,9) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (72,6)-(72,8) = "**"
+ │ ├── closing_loc: (72,9)-(72,10) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (73,0)-(73,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (73,0)-(73,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (73,0)-(73,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]
+ │ ├── message_loc: (73,3)-(73,9) = "[*baz]"
+ │ ├── opening_loc: (73,3)-(73,4) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (73,4)-(73,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ SplatNode (location: (73,4)-(73,8))
+ │ │ ├── operator_loc: (73,4)-(73,5) = "*"
+ │ │ └── expression:
+ │ │ @ CallNode (location: (73,5)-(73,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (73,5)-(73,8) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (73,8)-(73,9) = "]"
+ │ └── block: ∅
+ ├── @ CallNode (location: (74,0)-(74,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (74,0)-(74,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (74,0)-(74,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]
+ │ ├── message_loc: (74,3)-(74,9) = "[1, 2]"
+ │ ├── opening_loc: (74,3)-(74,4) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (74,4)-(74,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ IntegerNode (location: (74,4)-(74,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (74,7)-(74,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── closing_loc: (74,8)-(74,9) = "]"
+ │ └── block: ∅
+ ├── @ CallNode (location: (75,0)-(75,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (75,0)-(75,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (75,0)-(75,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]
+ │ ├── message_loc: (75,3)-(75,5) = "[]"
+ │ ├── opening_loc: (75,3)-(75,4) = "["
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (75,4)-(75,5) = "]"
+ │ └── block: ∅
+ ├── @ CallNode (location: (76,0)-(76,8))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver:
+ │ │ @ SelfNode (location: (76,0)-(76,4))
+ │ ├── call_operator_loc: (76,4)-(76,5) = "."
+ │ ├── name: :foo
+ │ ├── message_loc: (76,5)-(76,8) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (77,0)-(77,13))
+ │ ├── flags: attribute_write, ignore_visibility
+ │ ├── receiver:
+ │ │ @ SelfNode (location: (77,0)-(77,4))
+ │ ├── call_operator_loc: (77,4)-(77,5) = "."
+ │ ├── name: :foo=
+ │ ├── message_loc: (77,5)-(77,8) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (77,9)-(77,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ SymbolNode (location: (77,9)-(77,13))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (77,9)-(77,10) = ":"
+ │ │ ├── value_loc: (77,10)-(77,13) = "bar"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "bar"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (78,0)-(78,17))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (78,0)-(78,7))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (78,1)-(78,6))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (78,1)-(78,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ CallNode (location: (78,1)-(78,2))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── message_loc: (78,1)-(78,2) = "a"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :+
+ │ │ │ ├── message_loc: (78,3)-(78,4) = "+"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (78,5)-(78,6))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (78,5)-(78,6))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── message_loc: (78,5)-(78,6) = "b"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (78,0)-(78,1) = "("
+ │ │ └── closing_loc: (78,6)-(78,7) = ")"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :/
+ │ ├── message_loc: (78,8)-(78,9) = "/"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (78,10)-(78,17))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ ParenthesesNode (location: (78,10)-(78,17))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (78,11)-(78,16))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (78,11)-(78,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ CallNode (location: (78,11)-(78,12))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (78,11)-(78,12) = "c"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :-
+ │ │ │ ├── message_loc: (78,13)-(78,14) = "-"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (78,15)-(78,16))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (78,15)-(78,16))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :d
+ │ │ │ │ ├── message_loc: (78,15)-(78,16) = "d"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (78,10)-(78,11) = "("
+ │ │ └── closing_loc: (78,16)-(78,17) = ")"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (79,0)-(79,19))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (79,0)-(79,7))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (79,1)-(79,6))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (79,1)-(79,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ CallNode (location: (79,1)-(79,2))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── message_loc: (79,1)-(79,2) = "a"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :+
+ │ │ │ ├── message_loc: (79,3)-(79,4) = "+"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (79,5)-(79,6))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (79,5)-(79,6))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── message_loc: (79,5)-(79,6) = "b"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (79,0)-(79,1) = "("
+ │ │ └── closing_loc: (79,6)-(79,7) = ")"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :/
+ │ ├── message_loc: (79,8)-(79,9) = "/"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (79,10)-(79,19))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (79,10)-(79,19))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (79,10)-(79,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── message_loc: (79,10)-(79,11) = "c"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (79,11)-(79,12) = "."
+ │ │ ├── name: :-
+ │ │ ├── message_loc: (79,12)-(79,13) = "-"
+ │ │ ├── opening_loc: (79,13)-(79,14) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (79,14)-(79,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 2)
+ │ │ │ ├── @ CallNode (location: (79,14)-(79,15))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :e
+ │ │ │ │ ├── message_loc: (79,14)-(79,15) = "e"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── @ CallNode (location: (79,17)-(79,18))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :f
+ │ │ │ ├── message_loc: (79,17)-(79,18) = "f"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── closing_loc: (79,18)-(79,19) = ")"
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (80,0)-(80,17))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (80,0)-(80,7))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (80,1)-(80,6))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (80,1)-(80,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ CallNode (location: (80,1)-(80,2))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── message_loc: (80,1)-(80,2) = "a"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :+
+ │ │ │ ├── message_loc: (80,3)-(80,4) = "+"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (80,5)-(80,6))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (80,5)-(80,6))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── message_loc: (80,5)-(80,6) = "b"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (80,0)-(80,1) = "("
+ │ │ └── closing_loc: (80,6)-(80,7) = ")"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :/
+ │ ├── message_loc: (80,8)-(80,9) = "/"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (80,10)-(80,17))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (80,10)-(80,17))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (80,10)-(80,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── message_loc: (80,10)-(80,11) = "c"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (80,11)-(80,12) = "."
+ │ │ ├── name: :-
+ │ │ ├── message_loc: (80,12)-(80,13) = "-"
+ │ │ ├── opening_loc: (80,13)-(80,14) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (80,14)-(80,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ SplatNode (location: (80,14)-(80,16))
+ │ │ │ ├── operator_loc: (80,14)-(80,15) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ CallNode (location: (80,15)-(80,16))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :f
+ │ │ │ ├── message_loc: (80,15)-(80,16) = "f"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── closing_loc: (80,16)-(80,17) = ")"
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (81,0)-(81,8))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :x
+ │ ├── message_loc: (81,0)-(81,1) = "x"
+ │ ├── opening_loc: (81,1)-(81,2) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (81,2)-(81,7))
+ │ │ ├── flags: contains_keyword_splat
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (81,2)-(81,7))
+ │ │ ├── flags: ∅
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocSplatNode (location: (81,2)-(81,7))
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (81,4)-(81,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (81,4)-(81,7) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (81,2)-(81,4) = "**"
+ │ ├── closing_loc: (81,7)-(81,8) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (82,0)-(82,6))
+ │ ├── flags: safe_navigation
+ │ ├── receiver:
+ │ │ @ CallNode (location: (82,0)-(82,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (82,0)-(82,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (82,3)-(82,5) = "&."
+ │ ├── name: :!
+ │ ├── message_loc: (82,5)-(82,6) = "!"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (83,0)-(83,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (83,0)-(83,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (83,0)-(83,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (83,3)-(83,4) = "."
+ │ ├── name: :~
+ │ ├── message_loc: (83,4)-(83,5) = "~"
+ │ ├── opening_loc: (83,5)-(83,6) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (83,6)-(83,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (83,6)-(83,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (83,6)-(83,7) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (83,7)-(83,8) = ")"
+ │ └── block: ∅
+ └── @ CallNode (location: (84,0)-(84,7))
+ ├── flags: safe_navigation
+ ├── receiver:
+ │ @ CallNode (location: (84,0)-(84,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (84,0)-(84,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (84,1)-(84,3) = "&."
+ ├── name: :+
+ ├── message_loc: (84,3)-(84,4) = "+"
+ ├── opening_loc: (84,4)-(84,5) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (84,5)-(84,6))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (84,5)-(84,6))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :b
+ │ ├── message_loc: (84,5)-(84,6) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: (84,6)-(84,7) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/unparser/corpus/literal/since/27.txt b/test/prism/snapshots/unparser/corpus/literal/since/27.txt
new file mode 100644
index 0000000000..60edc18604
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/since/27.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(4,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,5))
+ └── body: (length: 2)
+ ├── @ LambdaNode (location: (1,0)-(3,1))
+ │ ├── locals: [:_1, :_2]
+ │ ├── operator_loc: (1,0)-(1,2) = "->"
+ │ ├── opening_loc: (1,3)-(1,4) = "{"
+ │ ├── closing_loc: (3,0)-(3,1) = "}"
+ │ ├── parameters:
+ │ │ @ NumberedParametersNode (location: (1,0)-(3,1))
+ │ │ └── maximum: 2
+ │ └── body:
+ │ @ StatementsNode (location: (2,2)-(2,9))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (2,2)-(2,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (2,2)-(2,4))
+ │ │ ├── name: :_1
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (2,5)-(2,6) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (2,7)-(2,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (2,7)-(2,9))
+ │ │ ├── name: :_2
+ │ │ └── depth: 0
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ ParenthesesNode (location: (4,0)-(4,5))
+ ├── body:
+ │ @ StatementsNode (location: (4,1)-(4,4))
+ │ └── body: (length: 1)
+ │ └── @ RangeNode (location: (4,1)-(4,4))
+ │ ├── flags: ∅
+ │ ├── left: ∅
+ │ ├── right:
+ │ │ @ IntegerNode (location: (4,3)-(4,4))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (4,1)-(4,3) = ".."
+ ├── opening_loc: (4,0)-(4,1) = "("
+ └── closing_loc: (4,4)-(4,5) = ")"
diff --git a/test/prism/snapshots/unparser/corpus/literal/since/30.txt b/test/prism/snapshots/unparser/corpus/literal/since/30.txt
new file mode 100644
index 0000000000..300dd869f5
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/since/30.txt
@@ -0,0 +1,88 @@
+@ ProgramNode (location: (1,0)-(4,17))
+├── locals: [:a, :foo]
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,17))
+ └── body: (length: 4)
+ ├── @ MatchRequiredNode (location: (1,0)-(1,8))
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (1,5)-(1,8))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (1,6)-(1,7))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (1,5)-(1,6) = "["
+ │ │ └── closing_loc: (1,7)-(1,8) = "]"
+ │ └── operator_loc: (1,2)-(1,4) = "=>"
+ ├── @ MatchRequiredNode (location: (2,0)-(2,8))
+ │ ├── value:
+ │ │ @ IntegerNode (location: (2,0)-(2,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (2,5)-(2,8))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ SplatNode (location: (2,6)-(2,7))
+ │ │ │ ├── operator_loc: (2,6)-(2,7) = "*"
+ │ │ │ └── expression: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (2,5)-(2,6) = "["
+ │ │ └── closing_loc: (2,7)-(2,8) = "]"
+ │ └── operator_loc: (2,2)-(2,4) = "=>"
+ ├── @ MatchPredicateNode (location: (3,0)-(3,15))
+ │ ├── value:
+ │ │ @ IntegerNode (location: (3,0)-(3,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── pattern:
+ │ │ @ FindPatternNode (location: (3,5)-(3,15))
+ │ │ ├── constant: ∅
+ │ │ ├── left:
+ │ │ │ @ SplatNode (location: (3,6)-(3,7))
+ │ │ │ ├── operator_loc: (3,6)-(3,7) = "*"
+ │ │ │ └── expression: ∅
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (3,9)-(3,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ ├── right:
+ │ │ │ @ SplatNode (location: (3,13)-(3,14))
+ │ │ │ ├── operator_loc: (3,13)-(3,14) = "*"
+ │ │ │ └── expression: ∅
+ │ │ ├── opening_loc: (3,5)-(3,6) = "["
+ │ │ └── closing_loc: (3,14)-(3,15) = "]"
+ │ └── operator_loc: (3,2)-(3,4) = "in"
+ └── @ MatchPredicateNode (location: (4,0)-(4,17))
+ ├── value:
+ │ @ IntegerNode (location: (4,0)-(4,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── pattern:
+ │ @ FindPatternNode (location: (4,5)-(4,17))
+ │ ├── constant: ∅
+ │ ├── left:
+ │ │ @ SplatNode (location: (4,6)-(4,7))
+ │ │ ├── operator_loc: (4,6)-(4,7) = "*"
+ │ │ └── expression: ∅
+ │ ├── requireds: (length: 1)
+ │ │ └── @ LocalVariableTargetNode (location: (4,9)-(4,10))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── right:
+ │ │ @ SplatNode (location: (4,12)-(4,16))
+ │ │ ├── operator_loc: (4,12)-(4,13) = "*"
+ │ │ └── expression:
+ │ │ @ LocalVariableTargetNode (location: (4,13)-(4,16))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── opening_loc: (4,5)-(4,6) = "["
+ │ └── closing_loc: (4,16)-(4,17) = "]"
+ └── operator_loc: (4,2)-(4,4) = "in"
diff --git a/test/prism/snapshots/unparser/corpus/literal/since/31.txt b/test/prism/snapshots/unparser/corpus/literal/since/31.txt
new file mode 100644
index 0000000000..142a56ae83
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/since/31.txt
@@ -0,0 +1,90 @@
+@ ProgramNode (location: (1,0)-(7,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(7,3))
+ └── body: (length: 2)
+ ├── @ DefNode (location: (1,0)-(3,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (1,4)-(1,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,8)-(1,9))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (1,8)-(1,9))
+ │ │ ├── flags: ∅
+ │ │ ├── name: ∅
+ │ │ ├── name_loc: ∅
+ │ │ └── operator_loc: (1,8)-(1,9) = "&"
+ │ ├── body:
+ │ │ @ StatementsNode (location: (2,2)-(2,7))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (2,2)-(2,7))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (2,2)-(2,5) = "bar"
+ │ │ ├── opening_loc: (2,5)-(2,6) = "("
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: (2,7)-(2,8) = ")"
+ │ │ └── block:
+ │ │ @ BlockArgumentNode (location: (2,6)-(2,7))
+ │ │ ├── expression: ∅
+ │ │ └── operator_loc: (2,6)-(2,7) = "&"
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (1,7)-(1,8) = "("
+ │ ├── rparen_loc: (1,9)-(1,10) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (3,0)-(3,3) = "end"
+ └── @ DefNode (location: (5,0)-(7,3))
+ ├── name: :foo
+ ├── name_loc: (5,4)-(5,7) = "foo"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (5,8)-(5,12))
+ │ ├── requireds: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (5,8)-(5,9))
+ │ │ ├── flags: ∅
+ │ │ └── name: :a
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block:
+ │ @ BlockParameterNode (location: (5,11)-(5,12))
+ │ ├── flags: ∅
+ │ ├── name: ∅
+ │ ├── name_loc: ∅
+ │ └── operator_loc: (5,11)-(5,12) = "&"
+ ├── body:
+ │ @ StatementsNode (location: (6,2)-(6,7))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (6,2)-(6,7))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (6,2)-(6,5) = "bar"
+ │ ├── opening_loc: (6,5)-(6,6) = "("
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (6,7)-(6,8) = ")"
+ │ └── block:
+ │ @ BlockArgumentNode (location: (6,6)-(6,7))
+ │ ├── expression: ∅
+ │ └── operator_loc: (6,6)-(6,7) = "&"
+ ├── locals: [:a]
+ ├── def_keyword_loc: (5,0)-(5,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (5,7)-(5,8) = "("
+ ├── rparen_loc: (5,12)-(5,13) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (7,0)-(7,3) = "end"
diff --git a/test/prism/snapshots/unparser/corpus/literal/since/32.txt b/test/prism/snapshots/unparser/corpus/literal/since/32.txt
new file mode 100644
index 0000000000..e72be6d8b7
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/since/32.txt
@@ -0,0 +1,156 @@
+@ ProgramNode (location: (1,0)-(11,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(11,3))
+ └── body: (length: 3)
+ ├── @ DefNode (location: (1,0)-(3,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (1,4)-(1,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,8)-(1,20))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,8)-(1,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :argument
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ KeywordRestParameterNode (location: (1,18)-(1,20))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: ∅
+ │ │ │ ├── name_loc: ∅
+ │ │ │ └── operator_loc: (1,18)-(1,20) = "**"
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (2,2)-(2,19))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (2,2)-(2,19))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (2,2)-(2,5) = "bar"
+ │ │ ├── opening_loc: (2,5)-(2,6) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (2,6)-(2,18))
+ │ │ │ ├── flags: contains_keyword_splat
+ │ │ │ └── arguments: (length: 2)
+ │ │ │ ├── @ LocalVariableReadNode (location: (2,6)-(2,14))
+ │ │ │ │ ├── name: :argument
+ │ │ │ │ └── depth: 0
+ │ │ │ └── @ KeywordHashNode (location: (2,16)-(2,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── elements: (length: 1)
+ │ │ │ └── @ AssocSplatNode (location: (2,16)-(2,18))
+ │ │ │ ├── value: ∅
+ │ │ │ └── operator_loc: (2,16)-(2,18) = "**"
+ │ │ ├── closing_loc: (2,18)-(2,19) = ")"
+ │ │ └── block: ∅
+ │ ├── locals: [:argument]
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (1,7)-(1,8) = "("
+ │ ├── rparen_loc: (1,20)-(1,21) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (3,0)-(3,3) = "end"
+ ├── @ DefNode (location: (5,0)-(7,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (5,4)-(5,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (5,8)-(5,19))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (5,8)-(5,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :argument
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (5,18)-(5,19))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: ∅
+ │ │ │ ├── name_loc: ∅
+ │ │ │ └── operator_loc: (5,18)-(5,19) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (6,2)-(6,18))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (6,2)-(6,18))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (6,2)-(6,5) = "bar"
+ │ │ ├── opening_loc: (6,5)-(6,6) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (6,6)-(6,17))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 2)
+ │ │ │ ├── @ LocalVariableReadNode (location: (6,6)-(6,14))
+ │ │ │ │ ├── name: :argument
+ │ │ │ │ └── depth: 0
+ │ │ │ └── @ SplatNode (location: (6,16)-(6,17))
+ │ │ │ ├── operator_loc: (6,16)-(6,17) = "*"
+ │ │ │ └── expression: ∅
+ │ │ ├── closing_loc: (6,17)-(6,18) = ")"
+ │ │ └── block: ∅
+ │ ├── locals: [:argument]
+ │ ├── def_keyword_loc: (5,0)-(5,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (5,7)-(5,8) = "("
+ │ ├── rparen_loc: (5,19)-(5,20) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (7,0)-(7,3) = "end"
+ └── @ DefNode (location: (9,0)-(11,3))
+ ├── name: :foo
+ ├── name_loc: (9,4)-(9,7) = "foo"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (9,8)-(9,10))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest:
+ │ │ @ KeywordRestParameterNode (location: (9,8)-(9,10))
+ │ │ ├── flags: ∅
+ │ │ ├── name: ∅
+ │ │ ├── name_loc: ∅
+ │ │ └── operator_loc: (9,8)-(9,10) = "**"
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (10,2)-(10,20))
+ │ └── body: (length: 1)
+ │ └── @ HashNode (location: (10,2)-(10,20))
+ │ ├── opening_loc: (10,2)-(10,3) = "{"
+ │ ├── elements: (length: 2)
+ │ │ ├── @ AssocNode (location: (10,4)-(10,14))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (10,4)-(10,12))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (10,4)-(10,11) = "default"
+ │ │ │ │ ├── closing_loc: (10,11)-(10,12) = ":"
+ │ │ │ │ └── unescaped: "default"
+ │ │ │ ├── value:
+ │ │ │ │ @ IntegerNode (location: (10,13)-(10,14))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── operator_loc: ∅
+ │ │ └── @ AssocSplatNode (location: (10,16)-(10,18))
+ │ │ ├── value: ∅
+ │ │ └── operator_loc: (10,16)-(10,18) = "**"
+ │ └── closing_loc: (10,19)-(10,20) = "}"
+ ├── locals: []
+ ├── def_keyword_loc: (9,0)-(9,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (9,7)-(9,8) = "("
+ ├── rparen_loc: (9,10)-(9,11) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (11,0)-(11,3) = "end"
diff --git a/test/prism/snapshots/unparser/corpus/literal/singletons.txt b/test/prism/snapshots/unparser/corpus/literal/singletons.txt
new file mode 100644
index 0000000000..45c06f7b07
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/singletons.txt
@@ -0,0 +1,9 @@
+@ ProgramNode (location: (1,0)-(4,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,4))
+ └── body: (length: 4)
+ ├── @ FalseNode (location: (1,0)-(1,5))
+ ├── @ NilNode (location: (2,0)-(2,3))
+ ├── @ SelfNode (location: (3,0)-(3,4))
+ └── @ TrueNode (location: (4,0)-(4,4))
diff --git a/test/prism/snapshots/unparser/corpus/literal/super.txt b/test/prism/snapshots/unparser/corpus/literal/super.txt
new file mode 100644
index 0000000000..d5a7889919
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/super.txt
@@ -0,0 +1,277 @@
+@ ProgramNode (location: (1,0)-(21,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(21,1))
+ └── body: (length: 11)
+ ├── @ ForwardingSuperNode (location: (1,0)-(1,5))
+ │ └── block: ∅
+ ├── @ SuperNode (location: (2,0)-(2,7))
+ │ ├── keyword_loc: (2,0)-(2,5) = "super"
+ │ ├── lparen_loc: (2,5)-(2,6) = "("
+ │ ├── arguments: ∅
+ │ ├── rparen_loc: (2,6)-(2,7) = ")"
+ │ └── block: ∅
+ ├── @ SuperNode (location: (3,0)-(3,8))
+ │ ├── keyword_loc: (3,0)-(3,5) = "super"
+ │ ├── lparen_loc: (3,5)-(3,6) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,6)-(3,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (3,6)-(3,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (3,6)-(3,7) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rparen_loc: (3,7)-(3,8) = ")"
+ │ └── block: ∅
+ ├── @ SuperNode (location: (4,0)-(4,11))
+ │ ├── keyword_loc: (4,0)-(4,5) = "super"
+ │ ├── lparen_loc: (4,5)-(4,6) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (4,6)-(4,10))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (4,6)-(4,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (4,6)-(4,7) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ CallNode (location: (4,9)-(4,10))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (4,9)-(4,10) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rparen_loc: (4,10)-(4,11) = ")"
+ │ └── block: ∅
+ ├── @ SuperNode (location: (5,0)-(5,13))
+ │ ├── keyword_loc: (5,0)-(5,5) = "super"
+ │ ├── lparen_loc: (5,5)-(5,6) = "("
+ │ ├── arguments: ∅
+ │ ├── rparen_loc: (5,12)-(5,13) = ")"
+ │ └── block:
+ │ @ BlockArgumentNode (location: (5,6)-(5,12))
+ │ ├── expression:
+ │ │ @ CallNode (location: (5,7)-(5,12))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :block
+ │ │ ├── message_loc: (5,7)-(5,12) = "block"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (5,6)-(5,7) = "&"
+ ├── @ SuperNode (location: (6,0)-(6,16))
+ │ ├── keyword_loc: (6,0)-(6,5) = "super"
+ │ ├── lparen_loc: (6,5)-(6,6) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (6,6)-(6,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (6,6)-(6,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (6,6)-(6,7) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rparen_loc: (6,15)-(6,16) = ")"
+ │ └── block:
+ │ @ BlockArgumentNode (location: (6,9)-(6,15))
+ │ ├── expression:
+ │ │ @ CallNode (location: (6,10)-(6,15))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :block
+ │ │ ├── message_loc: (6,10)-(6,15) = "block"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (6,9)-(6,10) = "&"
+ ├── @ SuperNode (location: (7,0)-(9,2))
+ │ ├── keyword_loc: (7,0)-(7,5) = "super"
+ │ ├── lparen_loc: (7,5)-(7,6) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (7,6)-(9,1))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (7,6)-(9,1))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (7,6)-(7,7) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (7,8)-(9,1))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (8,2)-(8,5))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (8,2)-(8,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (8,2)-(8,5) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (7,8)-(7,9) = "{"
+ │ │ └── closing_loc: (9,0)-(9,1) = "}"
+ │ ├── rparen_loc: (9,1)-(9,2) = ")"
+ │ └── block: ∅
+ ├── @ ForwardingSuperNode (location: (10,0)-(12,1))
+ │ └── block:
+ │ @ BlockNode (location: (10,6)-(12,1))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (11,2)-(11,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (11,2)-(11,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (11,2)-(11,5) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (10,6)-(10,7) = "{"
+ │ └── closing_loc: (12,0)-(12,1) = "}"
+ ├── @ SuperNode (location: (13,0)-(15,1))
+ │ ├── keyword_loc: (13,0)-(13,5) = "super"
+ │ ├── lparen_loc: (13,5)-(13,6) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (13,6)-(13,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (13,6)-(13,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (13,6)-(13,7) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rparen_loc: (13,7)-(13,8) = ")"
+ │ └── block:
+ │ @ BlockNode (location: (13,9)-(15,1))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (14,2)-(14,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (14,2)-(14,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (14,2)-(14,5) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (13,9)-(13,10) = "{"
+ │ └── closing_loc: (15,0)-(15,1) = "}"
+ ├── @ SuperNode (location: (16,0)-(18,1))
+ │ ├── keyword_loc: (16,0)-(16,5) = "super"
+ │ ├── lparen_loc: (16,5)-(16,6) = "("
+ │ ├── arguments: ∅
+ │ ├── rparen_loc: (16,6)-(16,7) = ")"
+ │ └── block:
+ │ @ BlockNode (location: (16,8)-(18,1))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (17,2)-(17,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (17,2)-(17,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (17,2)-(17,5) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (16,8)-(16,9) = "{"
+ │ └── closing_loc: (18,0)-(18,1) = "}"
+ └── @ SuperNode (location: (19,0)-(21,1))
+ ├── keyword_loc: (19,0)-(19,5) = "super"
+ ├── lparen_loc: (19,5)-(19,6) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (19,6)-(19,10))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ CallNode (location: (19,6)-(19,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (19,6)-(19,7) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── @ CallNode (location: (19,9)-(19,10))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :b
+ │ ├── message_loc: (19,9)-(19,10) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── rparen_loc: (19,10)-(19,11) = ")"
+ └── block:
+ @ BlockNode (location: (19,12)-(21,1))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (20,2)-(20,5))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (20,2)-(20,5))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (20,2)-(20,5) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── opening_loc: (19,12)-(19,13) = "{"
+ └── closing_loc: (21,0)-(21,1) = "}"
diff --git a/test/prism/snapshots/unparser/corpus/literal/unary.txt b/test/prism/snapshots/unparser/corpus/literal/unary.txt
new file mode 100644
index 0000000000..5e9563d811
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/unary.txt
@@ -0,0 +1,248 @@
+@ ProgramNode (location: (1,0)-(8,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(8,9))
+ └── body: (length: 8)
+ ├── @ CallNode (location: (1,0)-(1,2))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (1,1)-(1,2))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!
+ │ ├── message_loc: (1,0)-(1,1) = "!"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (2,0)-(2,5))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (2,1)-(2,5))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (2,2)-(2,4))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (2,2)-(2,4))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ IntegerNode (location: (2,3)-(2,4))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :!
+ │ │ │ ├── message_loc: (2,2)-(2,3) = "!"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (2,1)-(2,2) = "("
+ │ │ └── closing_loc: (2,4)-(2,5) = ")"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!
+ │ ├── message_loc: (2,0)-(2,1) = "!"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (3,0)-(3,16))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (3,1)-(3,16))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (3,2)-(3,15))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (3,2)-(3,15))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ ParenthesesNode (location: (3,3)-(3,15))
+ │ │ │ │ ├── body:
+ │ │ │ │ │ @ StatementsNode (location: (3,4)-(3,14))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ OrNode (location: (3,4)-(3,14))
+ │ │ │ │ │ ├── left:
+ │ │ │ │ │ │ @ CallNode (location: (3,4)-(3,7))
+ │ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ │ ├── name: :foo
+ │ │ │ │ │ │ ├── message_loc: (3,4)-(3,7) = "foo"
+ │ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ │ └── block: ∅
+ │ │ │ │ │ ├── right:
+ │ │ │ │ │ │ @ CallNode (location: (3,11)-(3,14))
+ │ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ │ ├── name: :bar
+ │ │ │ │ │ │ ├── message_loc: (3,11)-(3,14) = "bar"
+ │ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ │ └── block: ∅
+ │ │ │ │ │ └── operator_loc: (3,8)-(3,10) = "||"
+ │ │ │ │ ├── opening_loc: (3,3)-(3,4) = "("
+ │ │ │ │ └── closing_loc: (3,14)-(3,15) = ")"
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :!
+ │ │ │ ├── message_loc: (3,2)-(3,3) = "!"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (3,1)-(3,2) = "("
+ │ │ └── closing_loc: (3,15)-(3,16) = ")"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!
+ │ ├── message_loc: (3,0)-(3,1) = "!"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (4,0)-(4,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (4,1)-(4,9))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ ParenthesesNode (location: (4,1)-(4,5))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (4,2)-(4,4))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (4,2)-(4,4))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── receiver:
+ │ │ │ │ │ @ IntegerNode (location: (4,3)-(4,4))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :!
+ │ │ │ │ ├── message_loc: (4,2)-(4,3) = "!"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── opening_loc: (4,1)-(4,2) = "("
+ │ │ │ └── closing_loc: (4,4)-(4,5) = ")"
+ │ │ ├── call_operator_loc: (4,5)-(4,6) = "."
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (4,6)-(4,9) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!
+ │ ├── message_loc: (4,0)-(4,1) = "!"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (5,0)-(5,2))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (5,1)-(5,2))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (5,1)-(5,2) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :~
+ │ ├── message_loc: (5,0)-(5,1) = "~"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (6,0)-(6,2))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (6,1)-(6,2))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (6,1)-(6,2) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :-@
+ │ ├── message_loc: (6,0)-(6,1) = "-"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (7,0)-(7,2))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (7,1)-(7,2))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (7,1)-(7,2) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+@
+ │ ├── message_loc: (7,0)-(7,1) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (8,0)-(8,9))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (8,1)-(8,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (8,1)-(8,5))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (8,2)-(8,4))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (8,2)-(8,4))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ CallNode (location: (8,3)-(8,4))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── message_loc: (8,3)-(8,4) = "a"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :-@
+ │ │ │ ├── message_loc: (8,2)-(8,3) = "-"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (8,1)-(8,2) = "("
+ │ │ └── closing_loc: (8,4)-(8,5) = ")"
+ │ ├── call_operator_loc: (8,5)-(8,6) = "."
+ │ ├── name: :foo
+ │ ├── message_loc: (8,6)-(8,9) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :-@
+ ├── message_loc: (8,0)-(8,1) = "-"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/unparser/corpus/literal/undef.txt b/test/prism/snapshots/unparser/corpus/literal/undef.txt
new file mode 100644
index 0000000000..282cbe8f87
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/undef.txt
@@ -0,0 +1,29 @@
+@ ProgramNode (location: (1,0)-(2,16))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,16))
+ └── body: (length: 2)
+ ├── @ UndefNode (location: (1,0)-(1,10))
+ │ ├── names: (length: 1)
+ │ │ └── @ SymbolNode (location: (1,6)-(1,10))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,6)-(1,7) = ":"
+ │ │ ├── value_loc: (1,7)-(1,10) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ └── keyword_loc: (1,0)-(1,5) = "undef"
+ └── @ UndefNode (location: (2,0)-(2,16))
+ ├── names: (length: 2)
+ │ ├── @ SymbolNode (location: (2,6)-(2,10))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (2,6)-(2,7) = ":"
+ │ │ ├── value_loc: (2,7)-(2,10) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ └── @ SymbolNode (location: (2,12)-(2,16))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (2,12)-(2,13) = ":"
+ │ ├── value_loc: (2,13)-(2,16) = "bar"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "bar"
+ └── keyword_loc: (2,0)-(2,5) = "undef"
diff --git a/test/prism/snapshots/unparser/corpus/literal/variables.txt b/test/prism/snapshots/unparser/corpus/literal/variables.txt
new file mode 100644
index 0000000000..bf79de385c
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/variables.txt
@@ -0,0 +1,53 @@
+@ ProgramNode (location: (1,0)-(10,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(10,17))
+ └── body: (length: 10)
+ ├── @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ InstanceVariableReadNode (location: (2,0)-(2,2))
+ │ └── name: :@a
+ ├── @ ClassVariableReadNode (location: (3,0)-(3,3))
+ │ └── name: :@@a
+ ├── @ GlobalVariableReadNode (location: (4,0)-(4,2))
+ │ └── name: :$a
+ ├── @ NumberedReferenceReadNode (location: (5,0)-(5,2))
+ │ └── number: 1
+ ├── @ BackReferenceReadNode (location: (6,0)-(6,2))
+ │ └── name: :$`
+ ├── @ ConstantReadNode (location: (7,0)-(7,5))
+ │ └── name: :CONST
+ ├── @ ConstantPathNode (location: (8,0)-(8,13))
+ │ ├── parent:
+ │ │ @ ConstantReadNode (location: (8,0)-(8,6))
+ │ │ └── name: :SCOPED
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (8,8)-(8,13))
+ │ │ └── name: :CONST
+ │ └── delimiter_loc: (8,6)-(8,8) = "::"
+ ├── @ ConstantPathNode (location: (9,0)-(9,10))
+ │ ├── parent: ∅
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (9,2)-(9,10))
+ │ │ └── name: :TOPLEVEL
+ │ └── delimiter_loc: (9,0)-(9,2) = "::"
+ └── @ ConstantPathNode (location: (10,0)-(10,17))
+ ├── parent:
+ │ @ ConstantPathNode (location: (10,0)-(10,10))
+ │ ├── parent: ∅
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (10,2)-(10,10))
+ │ │ └── name: :TOPLEVEL
+ │ └── delimiter_loc: (10,0)-(10,2) = "::"
+ ├── child:
+ │ @ ConstantReadNode (location: (10,12)-(10,17))
+ │ └── name: :CONST
+ └── delimiter_loc: (10,10)-(10,12) = "::"
diff --git a/test/prism/snapshots/unparser/corpus/literal/while.txt b/test/prism/snapshots/unparser/corpus/literal/while.txt
new file mode 100644
index 0000000000..0f752f3392
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/while.txt
@@ -0,0 +1,704 @@
+@ ProgramNode (location: (1,0)-(73,3))
+├── locals: [:x]
+└── statements:
+ @ StatementsNode (location: (1,0)-(73,3))
+ └── body: (length: 17)
+ ├── @ ModuleNode (location: (1,0)-(7,3))
+ │ ├── locals: []
+ │ ├── module_keyword_loc: (1,0)-(1,6) = "module"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (1,7)-(1,8))
+ │ │ └── name: :A
+ │ ├── body:
+ │ │ @ StatementsNode (location: (2,2)-(6,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (2,2)-(6,3))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (2,2)-(2,5) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (2,6)-(6,3))
+ │ │ ├── locals: [:bar, :foo]
+ │ │ ├── parameters:
+ │ │ │ @ BlockParametersNode (location: (2,8)-(2,13))
+ │ │ │ ├── parameters:
+ │ │ │ │ @ ParametersNode (location: (2,9)-(2,12))
+ │ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ │ └── @ RequiredParameterNode (location: (2,9)-(2,12))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :bar
+ │ │ │ │ ├── optionals: (length: 0)
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ ├── keywords: (length: 0)
+ │ │ │ │ ├── keyword_rest: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── locals: (length: 0)
+ │ │ │ ├── opening_loc: (2,8)-(2,9) = "|"
+ │ │ │ └── closing_loc: (2,12)-(2,13) = "|"
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (3,4)-(5,7))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ WhileNode (location: (3,4)-(5,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── keyword_loc: (3,4)-(3,9) = "while"
+ │ │ │ ├── closing_loc: (5,4)-(5,7) = "end"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ CallNode (location: (3,10)-(3,13))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :foo
+ │ │ │ │ ├── message_loc: (3,10)-(3,13) = "foo"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── statements:
+ │ │ │ @ StatementsNode (location: (4,6)-(4,15))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ LocalVariableWriteNode (location: (4,6)-(4,15))
+ │ │ │ ├── name: :foo
+ │ │ │ ├── depth: 0
+ │ │ │ ├── name_loc: (4,6)-(4,9) = "foo"
+ │ │ │ ├── value:
+ │ │ │ │ @ LocalVariableReadNode (location: (4,12)-(4,15))
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ └── depth: 0
+ │ │ │ └── operator_loc: (4,10)-(4,11) = "="
+ │ │ ├── opening_loc: (2,6)-(2,7) = "{"
+ │ │ └── closing_loc: (6,2)-(6,3) = "}"
+ │ ├── end_keyword_loc: (7,0)-(7,3) = "end"
+ │ └── name: :A
+ ├── @ DefNode (location: (9,0)-(11,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (9,4)-(9,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (10,2)-(10,28))
+ │ │ └── body: (length: 1)
+ │ │ └── @ WhileNode (location: (10,2)-(10,28))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (10,12)-(10,17) = "while"
+ │ │ ├── closing_loc: ∅
+ │ │ ├── predicate:
+ │ │ │ @ CallNode (location: (10,18)-(10,28))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ LocalVariableReadNode (location: (10,18)-(10,21))
+ │ │ │ │ ├── name: :foo
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :!=
+ │ │ │ ├── message_loc: (10,22)-(10,24) = "!="
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (10,25)-(10,28))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (10,25)-(10,28))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ ├── message_loc: (10,25)-(10,28) = "baz"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (10,2)-(10,11))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableWriteNode (location: (10,2)-(10,11))
+ │ │ ├── name: :foo
+ │ │ ├── depth: 0
+ │ │ ├── name_loc: (10,2)-(10,5) = "foo"
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (10,8)-(10,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (10,8)-(10,11) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (10,6)-(10,7) = "="
+ │ ├── locals: [:foo]
+ │ ├── def_keyword_loc: (9,0)-(9,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (11,0)-(11,3) = "end"
+ ├── @ ModuleNode (location: (13,0)-(15,3))
+ │ ├── locals: [:foo]
+ │ ├── module_keyword_loc: (13,0)-(13,6) = "module"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (13,7)-(13,8))
+ │ │ └── name: :A
+ │ ├── body:
+ │ │ @ StatementsNode (location: (14,2)-(14,21))
+ │ │ └── body: (length: 1)
+ │ │ └── @ WhileNode (location: (14,2)-(14,21))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (14,12)-(14,17) = "while"
+ │ │ ├── closing_loc: ∅
+ │ │ ├── predicate:
+ │ │ │ @ LocalVariableReadNode (location: (14,18)-(14,21))
+ │ │ │ ├── name: :foo
+ │ │ │ └── depth: 0
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (14,2)-(14,11))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableWriteNode (location: (14,2)-(14,11))
+ │ │ ├── name: :foo
+ │ │ ├── depth: 0
+ │ │ ├── name_loc: (14,2)-(14,5) = "foo"
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (14,8)-(14,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (14,8)-(14,11) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (14,6)-(14,7) = "="
+ │ ├── end_keyword_loc: (15,0)-(15,3) = "end"
+ │ └── name: :A
+ ├── @ ModuleNode (location: (17,0)-(19,3))
+ │ ├── locals: [:foo]
+ │ ├── module_keyword_loc: (17,0)-(17,6) = "module"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (17,7)-(17,8))
+ │ │ └── name: :A
+ │ ├── body:
+ │ │ @ StatementsNode (location: (18,2)-(18,21))
+ │ │ └── body: (length: 1)
+ │ │ └── @ UntilNode (location: (18,2)-(18,21))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (18,12)-(18,17) = "until"
+ │ │ ├── closing_loc: ∅
+ │ │ ├── predicate:
+ │ │ │ @ LocalVariableReadNode (location: (18,18)-(18,21))
+ │ │ │ ├── name: :foo
+ │ │ │ └── depth: 0
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (18,2)-(18,11))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableWriteNode (location: (18,2)-(18,11))
+ │ │ ├── name: :foo
+ │ │ ├── depth: 0
+ │ │ ├── name_loc: (18,2)-(18,5) = "foo"
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (18,8)-(18,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (18,8)-(18,11) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (18,6)-(18,7) = "="
+ │ ├── end_keyword_loc: (19,0)-(19,3) = "end"
+ │ └── name: :A
+ ├── @ ModuleNode (location: (21,0)-(25,3))
+ │ ├── locals: [:foo]
+ │ ├── module_keyword_loc: (21,0)-(21,6) = "module"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (21,7)-(21,8))
+ │ │ └── name: :A
+ │ ├── body:
+ │ │ @ StatementsNode (location: (22,2)-(24,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ WhileNode (location: (22,2)-(24,5))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (22,2)-(22,7) = "while"
+ │ │ ├── closing_loc: (24,2)-(24,5) = "end"
+ │ │ ├── predicate:
+ │ │ │ @ CallNode (location: (22,8)-(22,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (22,8)-(22,11) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (23,4)-(23,13))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableWriteNode (location: (23,4)-(23,13))
+ │ │ ├── name: :foo
+ │ │ ├── depth: 0
+ │ │ ├── name_loc: (23,4)-(23,7) = "foo"
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (23,10)-(23,13))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (23,10)-(23,13) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (23,8)-(23,9) = "="
+ │ ├── end_keyword_loc: (25,0)-(25,3) = "end"
+ │ └── name: :A
+ ├── @ ModuleNode (location: (27,0)-(33,3))
+ │ ├── locals: []
+ │ ├── module_keyword_loc: (27,0)-(27,6) = "module"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (27,7)-(27,8))
+ │ │ └── name: :A
+ │ ├── body:
+ │ │ @ StatementsNode (location: (28,2)-(32,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (28,2)-(32,3))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :each
+ │ │ ├── message_loc: (28,2)-(28,6) = "each"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (28,7)-(32,3))
+ │ │ ├── locals: [:baz, :foo]
+ │ │ ├── parameters:
+ │ │ │ @ BlockParametersNode (location: (28,9)-(28,14))
+ │ │ │ ├── parameters:
+ │ │ │ │ @ ParametersNode (location: (28,10)-(28,13))
+ │ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ │ └── @ RequiredParameterNode (location: (28,10)-(28,13))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :baz
+ │ │ │ │ ├── optionals: (length: 0)
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ ├── keywords: (length: 0)
+ │ │ │ │ ├── keyword_rest: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── locals: (length: 0)
+ │ │ │ ├── opening_loc: (28,9)-(28,10) = "|"
+ │ │ │ └── closing_loc: (28,13)-(28,14) = "|"
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (29,4)-(31,7))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ WhileNode (location: (29,4)-(31,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── keyword_loc: (29,4)-(29,9) = "while"
+ │ │ │ ├── closing_loc: (31,4)-(31,7) = "end"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ CallNode (location: (29,10)-(29,13))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :foo
+ │ │ │ │ ├── message_loc: (29,10)-(29,13) = "foo"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── statements:
+ │ │ │ @ StatementsNode (location: (30,6)-(30,15))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ LocalVariableWriteNode (location: (30,6)-(30,15))
+ │ │ │ ├── name: :foo
+ │ │ │ ├── depth: 0
+ │ │ │ ├── name_loc: (30,6)-(30,9) = "foo"
+ │ │ │ ├── value:
+ │ │ │ │ @ CallNode (location: (30,12)-(30,15))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (30,12)-(30,15) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── operator_loc: (30,10)-(30,11) = "="
+ │ │ ├── opening_loc: (28,7)-(28,8) = "{"
+ │ │ └── closing_loc: (32,2)-(32,3) = "}"
+ │ ├── end_keyword_loc: (33,0)-(33,3) = "end"
+ │ └── name: :A
+ ├── @ ModuleNode (location: (35,0)-(41,3))
+ │ ├── locals: []
+ │ ├── module_keyword_loc: (35,0)-(35,6) = "module"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (35,7)-(35,8))
+ │ │ └── name: :A
+ │ ├── body:
+ │ │ @ StatementsNode (location: (36,2)-(40,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (36,2)-(40,3))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :each
+ │ │ ├── message_loc: (36,2)-(36,6) = "each"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (36,7)-(40,3))
+ │ │ ├── locals: [:foo]
+ │ │ ├── parameters:
+ │ │ │ @ BlockParametersNode (location: (36,9)-(36,14))
+ │ │ │ ├── parameters:
+ │ │ │ │ @ ParametersNode (location: (36,10)-(36,13))
+ │ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ │ └── @ RequiredParameterNode (location: (36,10)-(36,13))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :foo
+ │ │ │ │ ├── optionals: (length: 0)
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ ├── keywords: (length: 0)
+ │ │ │ │ ├── keyword_rest: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── locals: (length: 0)
+ │ │ │ ├── opening_loc: (36,9)-(36,10) = "|"
+ │ │ │ └── closing_loc: (36,13)-(36,14) = "|"
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (37,4)-(39,7))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ WhileNode (location: (37,4)-(39,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── keyword_loc: (37,4)-(37,9) = "while"
+ │ │ │ ├── closing_loc: (39,4)-(39,7) = "end"
+ │ │ │ ├── predicate:
+ │ │ │ │ @ LocalVariableReadNode (location: (37,10)-(37,13))
+ │ │ │ │ ├── name: :foo
+ │ │ │ │ └── depth: 0
+ │ │ │ └── statements:
+ │ │ │ @ StatementsNode (location: (38,6)-(38,15))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ LocalVariableWriteNode (location: (38,6)-(38,15))
+ │ │ │ ├── name: :foo
+ │ │ │ ├── depth: 0
+ │ │ │ ├── name_loc: (38,6)-(38,9) = "foo"
+ │ │ │ ├── value:
+ │ │ │ │ @ CallNode (location: (38,12)-(38,15))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (38,12)-(38,15) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── operator_loc: (38,10)-(38,11) = "="
+ │ │ ├── opening_loc: (36,7)-(36,8) = "{"
+ │ │ └── closing_loc: (40,2)-(40,3) = "}"
+ │ ├── end_keyword_loc: (41,0)-(41,3) = "end"
+ │ └── name: :A
+ ├── @ LocalVariableWriteNode (location: (42,0)-(44,14))
+ │ ├── name: :x
+ │ ├── depth: 0
+ │ ├── name_loc: (42,0)-(42,1) = "x"
+ │ ├── value:
+ │ │ @ ParenthesesNode (location: (42,4)-(44,14))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (42,5)-(44,13))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ WhileNode (location: (42,5)-(44,13))
+ │ │ │ ├── flags: begin_modifier
+ │ │ │ ├── keyword_loc: (44,4)-(44,9) = "while"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ ├── predicate:
+ │ │ │ │ @ CallNode (location: (44,10)-(44,13))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ ├── message_loc: (44,10)-(44,13) = "baz"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── statements:
+ │ │ │ @ StatementsNode (location: (42,5)-(44,3))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ BeginNode (location: (42,5)-(44,3))
+ │ │ │ ├── begin_keyword_loc: (42,5)-(42,10) = "begin"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (43,2)-(43,5))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (43,2)-(43,5))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :foo
+ │ │ │ │ ├── message_loc: (43,2)-(43,5) = "foo"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── rescue_clause: ∅
+ │ │ │ ├── else_clause: ∅
+ │ │ │ ├── ensure_clause: ∅
+ │ │ │ └── end_keyword_loc: (44,0)-(44,3) = "end"
+ │ │ ├── opening_loc: (42,4)-(42,5) = "("
+ │ │ └── closing_loc: (44,13)-(44,14) = ")"
+ │ └── operator_loc: (42,2)-(42,3) = "="
+ ├── @ WhileNode (location: (45,0)-(47,13))
+ │ ├── flags: begin_modifier
+ │ ├── keyword_loc: (47,4)-(47,9) = "while"
+ │ ├── closing_loc: ∅
+ │ ├── predicate:
+ │ │ @ CallNode (location: (47,10)-(47,13))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (47,10)-(47,13) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── statements:
+ │ @ StatementsNode (location: (45,0)-(47,3))
+ │ └── body: (length: 1)
+ │ └── @ BeginNode (location: (45,0)-(47,3))
+ │ ├── begin_keyword_loc: (45,0)-(45,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (46,2)-(46,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (46,2)-(46,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (46,2)-(46,5) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (47,0)-(47,3) = "end"
+ ├── @ UntilNode (location: (48,0)-(51,13))
+ │ ├── flags: begin_modifier
+ │ ├── keyword_loc: (51,4)-(51,9) = "until"
+ │ ├── closing_loc: ∅
+ │ ├── predicate:
+ │ │ @ CallNode (location: (51,10)-(51,13))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (51,10)-(51,13) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── statements:
+ │ @ StatementsNode (location: (48,0)-(51,3))
+ │ └── body: (length: 1)
+ │ └── @ BeginNode (location: (48,0)-(51,3))
+ │ ├── begin_keyword_loc: (48,0)-(48,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (49,2)-(50,5))
+ │ │ └── body: (length: 2)
+ │ │ ├── @ CallNode (location: (49,2)-(49,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (49,2)-(49,5) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ CallNode (location: (50,2)-(50,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (50,2)-(50,5) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (51,0)-(51,3) = "end"
+ ├── @ WhileNode (location: (52,0)-(55,13))
+ │ ├── flags: begin_modifier
+ │ ├── keyword_loc: (55,4)-(55,9) = "while"
+ │ ├── closing_loc: ∅
+ │ ├── predicate:
+ │ │ @ CallNode (location: (55,10)-(55,13))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (55,10)-(55,13) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── statements:
+ │ @ StatementsNode (location: (52,0)-(55,3))
+ │ └── body: (length: 1)
+ │ └── @ BeginNode (location: (52,0)-(55,3))
+ │ ├── begin_keyword_loc: (52,0)-(52,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (53,2)-(54,5))
+ │ │ └── body: (length: 2)
+ │ │ ├── @ CallNode (location: (53,2)-(53,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (53,2)-(53,5) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ CallNode (location: (54,2)-(54,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (54,2)-(54,5) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (55,0)-(55,3) = "end"
+ ├── @ WhileNode (location: (56,0)-(57,3))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (56,0)-(56,5) = "while"
+ │ ├── closing_loc: (57,0)-(57,3) = "end"
+ │ ├── predicate:
+ │ │ @ FalseNode (location: (56,6)-(56,11))
+ │ └── statements: ∅
+ ├── @ WhileNode (location: (58,0)-(60,3))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (58,0)-(58,5) = "while"
+ │ ├── closing_loc: (60,0)-(60,3) = "end"
+ │ ├── predicate:
+ │ │ @ FalseNode (location: (58,6)-(58,11))
+ │ └── statements:
+ │ @ StatementsNode (location: (59,2)-(59,3))
+ │ └── body: (length: 1)
+ │ └── @ IntegerNode (location: (59,2)-(59,3))
+ │ ├── flags: decimal
+ │ └── value: 3
+ ├── @ WhileNode (location: (61,0)-(64,3))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (61,0)-(61,5) = "while"
+ │ ├── closing_loc: (64,0)-(64,3) = "end"
+ │ ├── predicate:
+ │ │ @ ParenthesesNode (location: (61,6)-(62,2))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (61,7)-(62,1))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (61,7)-(62,1))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (61,7)-(61,10) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (61,11)-(62,1))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body: ∅
+ │ │ │ ├── opening_loc: (61,11)-(61,12) = "{"
+ │ │ │ └── closing_loc: (62,0)-(62,1) = "}"
+ │ │ ├── opening_loc: (61,6)-(61,7) = "("
+ │ │ └── closing_loc: (62,1)-(62,2) = ")"
+ │ └── statements:
+ │ @ StatementsNode (location: (63,2)-(63,7))
+ │ └── body: (length: 1)
+ │ └── @ SymbolNode (location: (63,2)-(63,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (63,2)-(63,3) = ":"
+ │ ├── value_loc: (63,3)-(63,7) = "body"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "body"
+ ├── @ UntilNode (location: (65,0)-(66,3))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (65,0)-(65,5) = "until"
+ │ ├── closing_loc: (66,0)-(66,3) = "end"
+ │ ├── predicate:
+ │ │ @ FalseNode (location: (65,6)-(65,11))
+ │ └── statements: ∅
+ ├── @ UntilNode (location: (67,0)-(69,3))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (67,0)-(67,5) = "until"
+ │ ├── closing_loc: (69,0)-(69,3) = "end"
+ │ ├── predicate:
+ │ │ @ FalseNode (location: (67,6)-(67,11))
+ │ └── statements:
+ │ @ StatementsNode (location: (68,2)-(68,3))
+ │ └── body: (length: 1)
+ │ └── @ IntegerNode (location: (68,2)-(68,3))
+ │ ├── flags: decimal
+ │ └── value: 3
+ └── @ UntilNode (location: (70,0)-(73,3))
+ ├── flags: ∅
+ ├── keyword_loc: (70,0)-(70,5) = "until"
+ ├── closing_loc: (73,0)-(73,3) = "end"
+ ├── predicate:
+ │ @ ParenthesesNode (location: (70,6)-(71,2))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (70,7)-(71,1))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (70,7)-(71,1))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (70,7)-(70,10) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (70,11)-(71,1))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (70,11)-(70,12) = "{"
+ │ │ └── closing_loc: (71,0)-(71,1) = "}"
+ │ ├── opening_loc: (70,6)-(70,7) = "("
+ │ └── closing_loc: (71,1)-(71,2) = ")"
+ └── statements:
+ @ StatementsNode (location: (72,2)-(72,7))
+ └── body: (length: 1)
+ └── @ SymbolNode (location: (72,2)-(72,7))
+ ├── flags: forced_us_ascii_encoding
+ ├── opening_loc: (72,2)-(72,3) = ":"
+ ├── value_loc: (72,3)-(72,7) = "body"
+ ├── closing_loc: ∅
+ └── unescaped: "body"
diff --git a/test/prism/snapshots/unparser/corpus/literal/yield.txt b/test/prism/snapshots/unparser/corpus/literal/yield.txt
new file mode 100644
index 0000000000..4d2b272e35
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/literal/yield.txt
@@ -0,0 +1,56 @@
+@ ProgramNode (location: (1,0)-(3,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,11))
+ └── body: (length: 3)
+ ├── @ YieldNode (location: (1,0)-(1,5))
+ │ ├── keyword_loc: (1,0)-(1,5) = "yield"
+ │ ├── lparen_loc: ∅
+ │ ├── arguments: ∅
+ │ └── rparen_loc: ∅
+ ├── @ YieldNode (location: (2,0)-(2,8))
+ │ ├── keyword_loc: (2,0)-(2,5) = "yield"
+ │ ├── lparen_loc: (2,5)-(2,6) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (2,6)-(2,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (2,6)-(2,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (2,6)-(2,7) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── rparen_loc: (2,7)-(2,8) = ")"
+ └── @ YieldNode (location: (3,0)-(3,11))
+ ├── keyword_loc: (3,0)-(3,5) = "yield"
+ ├── lparen_loc: (3,5)-(3,6) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (3,6)-(3,10))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ CallNode (location: (3,6)-(3,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (3,6)-(3,7) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── @ CallNode (location: (3,9)-(3,10))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :b
+ │ ├── message_loc: (3,9)-(3,10) = "b"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── rparen_loc: (3,10)-(3,11) = ")"
diff --git a/test/prism/snapshots/unparser/corpus/semantic/and.txt b/test/prism/snapshots/unparser/corpus/semantic/and.txt
new file mode 100644
index 0000000000..bc9d674e44
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/semantic/and.txt
@@ -0,0 +1,235 @@
+@ ProgramNode (location: (1,0)-(8,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(8,3))
+ └── body: (length: 4)
+ ├── @ OrNode (location: (1,0)-(1,14))
+ │ ├── left:
+ │ │ @ RangeNode (location: (1,0)-(1,5))
+ │ │ ├── flags: exclude_end
+ │ │ ├── left:
+ │ │ │ @ CallNode (location: (1,0)-(1,1))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── right:
+ │ │ │ @ CallNode (location: (1,4)-(1,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (1,4)-(1,5) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (1,1)-(1,4) = "..."
+ │ ├── right:
+ │ │ @ RangeNode (location: (1,9)-(1,14))
+ │ │ ├── flags: exclude_end
+ │ │ ├── left:
+ │ │ │ @ CallNode (location: (1,9)-(1,10))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── message_loc: (1,9)-(1,10) = "c"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── right:
+ │ │ │ @ CallNode (location: (1,13)-(1,14))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :d
+ │ │ │ ├── message_loc: (1,13)-(1,14) = "d"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (1,10)-(1,13) = "..."
+ │ └── operator_loc: (1,6)-(1,8) = "or"
+ ├── @ AndNode (location: (2,0)-(2,15))
+ │ ├── left:
+ │ │ @ RangeNode (location: (2,0)-(2,5))
+ │ │ ├── flags: exclude_end
+ │ │ ├── left:
+ │ │ │ @ CallNode (location: (2,0)-(2,1))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (2,0)-(2,1) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── right:
+ │ │ │ @ CallNode (location: (2,4)-(2,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (2,4)-(2,5) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (2,1)-(2,4) = "..."
+ │ ├── right:
+ │ │ @ RangeNode (location: (2,10)-(2,15))
+ │ │ ├── flags: exclude_end
+ │ │ ├── left:
+ │ │ │ @ CallNode (location: (2,10)-(2,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── message_loc: (2,10)-(2,11) = "c"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── right:
+ │ │ │ @ CallNode (location: (2,14)-(2,15))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :d
+ │ │ │ ├── message_loc: (2,14)-(2,15) = "d"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (2,11)-(2,14) = "..."
+ │ └── operator_loc: (2,6)-(2,9) = "and"
+ ├── @ IfNode (location: (4,0)-(5,3))
+ │ ├── if_keyword_loc: (4,0)-(4,2) = "if"
+ │ ├── predicate:
+ │ │ @ OrNode (location: (4,3)-(4,17))
+ │ │ ├── left:
+ │ │ │ @ FlipFlopNode (location: (4,3)-(4,8))
+ │ │ │ ├── flags: exclude_end
+ │ │ │ ├── left:
+ │ │ │ │ @ CallNode (location: (4,3)-(4,4))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── message_loc: (4,3)-(4,4) = "a"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── right:
+ │ │ │ │ @ CallNode (location: (4,7)-(4,8))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── message_loc: (4,7)-(4,8) = "b"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── operator_loc: (4,4)-(4,7) = "..."
+ │ │ ├── right:
+ │ │ │ @ FlipFlopNode (location: (4,12)-(4,17))
+ │ │ │ ├── flags: exclude_end
+ │ │ │ ├── left:
+ │ │ │ │ @ CallNode (location: (4,12)-(4,13))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (4,12)-(4,13) = "c"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── right:
+ │ │ │ │ @ CallNode (location: (4,16)-(4,17))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :d
+ │ │ │ │ ├── message_loc: (4,16)-(4,17) = "d"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── operator_loc: (4,13)-(4,16) = "..."
+ │ │ └── operator_loc: (4,9)-(4,11) = "or"
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements: ∅
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (5,0)-(5,3) = "end"
+ └── @ IfNode (location: (7,0)-(8,3))
+ ├── if_keyword_loc: (7,0)-(7,2) = "if"
+ ├── predicate:
+ │ @ AndNode (location: (7,3)-(7,18))
+ │ ├── left:
+ │ │ @ FlipFlopNode (location: (7,3)-(7,8))
+ │ │ ├── flags: exclude_end
+ │ │ ├── left:
+ │ │ │ @ CallNode (location: (7,3)-(7,4))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (7,3)-(7,4) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── right:
+ │ │ │ @ CallNode (location: (7,7)-(7,8))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (7,7)-(7,8) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (7,4)-(7,7) = "..."
+ │ ├── right:
+ │ │ @ FlipFlopNode (location: (7,13)-(7,18))
+ │ │ ├── flags: exclude_end
+ │ │ ├── left:
+ │ │ │ @ CallNode (location: (7,13)-(7,14))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── message_loc: (7,13)-(7,14) = "c"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── right:
+ │ │ │ @ CallNode (location: (7,17)-(7,18))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :d
+ │ │ │ ├── message_loc: (7,17)-(7,18) = "d"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (7,14)-(7,17) = "..."
+ │ └── operator_loc: (7,9)-(7,12) = "and"
+ ├── then_keyword_loc: ∅
+ ├── statements: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (8,0)-(8,3) = "end"
diff --git a/test/prism/snapshots/unparser/corpus/semantic/block.txt b/test/prism/snapshots/unparser/corpus/semantic/block.txt
new file mode 100644
index 0000000000..b9aa6b2184
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/semantic/block.txt
@@ -0,0 +1,191 @@
+@ ProgramNode (location: (1,0)-(26,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(26,3))
+ └── body: (length: 6)
+ ├── @ CallNode (location: (1,0)-(2,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,4)-(2,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,4)-(1,6) = "do"
+ │ └── closing_loc: (2,0)-(2,3) = "end"
+ ├── @ CallNode (location: (4,0)-(6,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (4,0)-(4,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (4,4)-(6,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ BeginNode (location: (4,4)-(6,3))
+ │ │ ├── begin_keyword_loc: ∅
+ │ │ ├── statements: ∅
+ │ │ ├── rescue_clause:
+ │ │ │ @ RescueNode (location: (5,0)-(5,6))
+ │ │ │ ├── keyword_loc: (5,0)-(5,6) = "rescue"
+ │ │ │ ├── exceptions: (length: 0)
+ │ │ │ ├── operator_loc: ∅
+ │ │ │ ├── reference: ∅
+ │ │ │ ├── statements: ∅
+ │ │ │ └── consequent: ∅
+ │ │ ├── else_clause: ∅
+ │ │ ├── ensure_clause: ∅
+ │ │ └── end_keyword_loc: (6,0)-(6,3) = "end"
+ │ ├── opening_loc: (4,4)-(4,6) = "do"
+ │ └── closing_loc: (6,0)-(6,3) = "end"
+ ├── @ CallNode (location: (8,0)-(11,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (8,0)-(8,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (8,4)-(11,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (9,2)-(10,5))
+ │ │ └── body: (length: 2)
+ │ │ ├── @ RescueModifierNode (location: (9,2)-(9,16))
+ │ │ │ ├── expression:
+ │ │ │ │ @ NilNode (location: (9,2)-(9,5))
+ │ │ │ ├── keyword_loc: (9,6)-(9,12) = "rescue"
+ │ │ │ └── rescue_expression:
+ │ │ │ @ NilNode (location: (9,13)-(9,16))
+ │ │ └── @ NilNode (location: (10,2)-(10,5))
+ │ ├── opening_loc: (8,4)-(8,6) = "do"
+ │ └── closing_loc: (11,0)-(11,3) = "end"
+ ├── @ CallNode (location: (13,0)-(14,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (13,0)-(13,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (13,4)-(14,3))
+ │ ├── locals: [:a]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (13,7)-(13,10))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (13,8)-(13,9))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (13,8)-(13,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (13,7)-(13,8) = "|"
+ │ │ └── closing_loc: (13,9)-(13,10) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (13,4)-(13,6) = "do"
+ │ └── closing_loc: (14,0)-(14,3) = "end"
+ ├── @ CallNode (location: (16,0)-(20,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (16,0)-(16,3) = "foo"
+ │ ├── opening_loc: (16,3)-(16,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (16,4)-(16,10))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (16,4)-(16,10))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (16,4)-(16,10) = "<<-DOC"
+ │ │ ├── content_loc: (17,0)-(18,0) = " b\n"
+ │ │ ├── closing_loc: (18,0)-(19,0) = "DOC\n"
+ │ │ └── unescaped: " b\n"
+ │ ├── closing_loc: (16,10)-(16,11) = ")"
+ │ └── block:
+ │ @ BlockNode (location: (16,12)-(20,3))
+ │ ├── locals: [:a]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (16,15)-(16,18))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (16,16)-(16,17))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (16,16)-(16,17))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (16,15)-(16,16) = "|"
+ │ │ └── closing_loc: (16,17)-(16,18) = "|"
+ │ ├── body:
+ │ │ @ StatementsNode (location: (19,2)-(19,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (19,2)-(19,3))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── opening_loc: (16,12)-(16,14) = "do"
+ │ └── closing_loc: (20,0)-(20,3) = "end"
+ └── @ CallNode (location: (22,0)-(26,3))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :foo
+ ├── message_loc: (22,0)-(22,3) = "foo"
+ ├── opening_loc: (22,3)-(22,4) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (22,4)-(22,10))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ StringNode (location: (22,4)-(22,10))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (22,4)-(22,10) = "<<-DOC"
+ │ ├── content_loc: (23,0)-(24,0) = " b\n"
+ │ ├── closing_loc: (24,0)-(25,0) = "DOC\n"
+ │ └── unescaped: " b\n"
+ ├── closing_loc: (22,10)-(22,11) = ")"
+ └── block:
+ @ BlockNode (location: (22,12)-(26,3))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (25,2)-(25,3))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (25,2)-(25,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (25,2)-(25,3) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── opening_loc: (22,12)-(22,14) = "do"
+ └── closing_loc: (26,0)-(26,3) = "end"
diff --git a/test/prism/snapshots/unparser/corpus/semantic/def.txt b/test/prism/snapshots/unparser/corpus/semantic/def.txt
new file mode 100644
index 0000000000..b44983c2f3
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/semantic/def.txt
@@ -0,0 +1,90 @@
+@ ProgramNode (location: (1,0)-(7,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(7,3))
+ └── body: (length: 2)
+ ├── @ DefNode (location: (1,0)-(3,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (1,4)-(1,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (2,2)-(2,9))
+ │ │ └── body: (length: 1)
+ │ │ └── @ ParenthesesNode (location: (2,2)-(2,9))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (2,3)-(2,8))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (2,3)-(2,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ CallNode (location: (2,3)-(2,4))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── message_loc: (2,3)-(2,4) = "a"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :-
+ │ │ │ ├── message_loc: (2,5)-(2,6) = "-"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (2,7)-(2,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (2,7)-(2,8))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── message_loc: (2,7)-(2,8) = "b"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (2,2)-(2,3) = "("
+ │ │ └── closing_loc: (2,8)-(2,9) = ")"
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (3,0)-(3,3) = "end"
+ └── @ DefNode (location: (5,0)-(7,3))
+ ├── name: :foo
+ ├── name_loc: (5,4)-(5,7) = "foo"
+ ├── receiver: ∅
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (6,2)-(6,20))
+ │ └── body: (length: 1)
+ │ └── @ RescueModifierNode (location: (6,2)-(6,20))
+ │ ├── expression:
+ │ │ @ CallNode (location: (6,2)-(6,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (6,2)-(6,3) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (6,4)-(6,10) = "rescue"
+ │ └── rescue_expression:
+ │ @ ConstantReadNode (location: (6,11)-(6,20))
+ │ └── name: :Exception
+ ├── locals: []
+ ├── def_keyword_loc: (5,0)-(5,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (7,0)-(7,3) = "end"
diff --git a/test/prism/snapshots/unparser/corpus/semantic/dstr.txt b/test/prism/snapshots/unparser/corpus/semantic/dstr.txt
new file mode 100644
index 0000000000..499bf59d1a
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/semantic/dstr.txt
@@ -0,0 +1,598 @@
+@ ProgramNode (location: (1,0)-(127,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(127,11))
+ └── body: (length: 33)
+ ├── @ StringNode (location: (1,0)-(1,5))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,5) = "<<DOC"
+ │ ├── content_loc: (2,0)-(2,0) = ""
+ │ ├── closing_loc: (2,0)-(3,0) = "DOC\n"
+ │ └── unescaped: ""
+ ├── @ StringNode (location: (4,0)-(4,7))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (4,0)-(4,7) = "<<'DOC'"
+ │ ├── content_loc: (5,0)-(5,0) = ""
+ │ ├── closing_loc: (5,0)-(6,0) = "DOC\n"
+ │ └── unescaped: ""
+ ├── @ StringNode (location: (7,0)-(7,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (7,0)-(7,6) = "<<~DOC"
+ │ ├── content_loc: (8,0)-(8,0) = ""
+ │ ├── closing_loc: (8,0)-(9,0) = "DOC\n"
+ │ └── unescaped: ""
+ ├── @ StringNode (location: (10,0)-(10,8))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (10,0)-(10,8) = "<<~'DOC'"
+ │ ├── content_loc: (11,0)-(11,0) = ""
+ │ ├── closing_loc: (11,0)-(12,0) = "DOC\n"
+ │ └── unescaped: ""
+ ├── @ StringNode (location: (13,0)-(13,5))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (13,0)-(13,5) = "<<DOC"
+ │ ├── content_loc: (14,0)-(15,0) = " a\n"
+ │ ├── closing_loc: (15,0)-(16,0) = "DOC\n"
+ │ └── unescaped: " a\n"
+ ├── @ StringNode (location: (17,0)-(17,7))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (17,0)-(17,7) = "<<'DOC'"
+ │ ├── content_loc: (18,0)-(19,0) = " a\n"
+ │ ├── closing_loc: (19,0)-(20,0) = "DOC\n"
+ │ └── unescaped: " a\n"
+ ├── @ InterpolatedStringNode (location: (21,0)-(21,5))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (21,0)-(21,5) = "<<DOC"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (22,0)-(23,2))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (22,0)-(23,2) = " a\n "
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: " a\n "
+ │ │ ├── @ EmbeddedStatementsNode (location: (23,2)-(23,5))
+ │ │ │ ├── opening_loc: (23,2)-(23,4) = "\#{"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── closing_loc: (23,4)-(23,5) = "}"
+ │ │ └── @ StringNode (location: (23,5)-(24,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (23,5)-(24,0) = "\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\n"
+ │ └── closing_loc: (24,0)-(25,0) = "DOC\n"
+ ├── @ InterpolatedStringNode (location: (26,0)-(26,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (26,0)-(26,6) = "<<~DOC"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (27,0)-(28,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (27,0)-(28,0) = " a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ ├── @ EmbeddedStatementsNode (location: (28,2)-(28,5))
+ │ │ │ ├── opening_loc: (28,2)-(28,4) = "\#{"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── closing_loc: (28,4)-(28,5) = "}"
+ │ │ └── @ StringNode (location: (28,5)-(29,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (28,5)-(29,0) = "\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\n"
+ │ └── closing_loc: (29,0)-(30,0) = "DOC\n"
+ ├── @ InterpolatedStringNode (location: (31,0)-(31,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (31,0)-(31,6) = "<<~DOC"
+ │ ├── parts: (length: 4)
+ │ │ ├── @ StringNode (location: (32,0)-(33,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (32,0)-(33,0) = " a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ ├── @ EmbeddedStatementsNode (location: (33,2)-(33,5))
+ │ │ │ ├── opening_loc: (33,2)-(33,4) = "\#{"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── closing_loc: (33,4)-(33,5) = "}"
+ │ │ ├── @ StringNode (location: (33,5)-(34,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (33,5)-(34,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── @ StringNode (location: (34,0)-(35,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (34,0)-(35,0) = " b\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b\n"
+ │ └── closing_loc: (35,0)-(36,0) = "DOC\n"
+ ├── @ InterpolatedStringNode (location: (37,0)-(37,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (37,0)-(37,6) = "<<~DOC"
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (38,0)-(39,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (38,0)-(39,0) = " a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ └── @ StringNode (location: (39,0)-(40,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (39,0)-(40,0) = " b\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: " b\n"
+ │ └── closing_loc: (40,0)-(41,0) = "DOC\n"
+ ├── @ StringNode (location: (42,0)-(42,7))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (42,0)-(42,7) = "<<'DOC'"
+ │ ├── content_loc: (43,0)-(46,0) = "a\n\nb\n"
+ │ ├── closing_loc: (46,0)-(47,0) = "DOC\n"
+ │ └── unescaped: "a\n\nb\n"
+ ├── @ StringNode (location: (48,0)-(48,7))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (48,0)-(48,7) = "<<'DOC'"
+ │ ├── content_loc: (49,0)-(52,0) = " a\n\n b\n"
+ │ ├── closing_loc: (52,0)-(53,0) = "DOC\n"
+ │ └── unescaped: " a\n\n b\n"
+ ├── @ StringNode (location: (54,0)-(54,7))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (54,0)-(54,7) = "<<'DOC'"
+ │ ├── content_loc: (55,0)-(56,0) = " a\\nb\n"
+ │ ├── closing_loc: (56,0)-(57,0) = "DOC\n"
+ │ └── unescaped: " a\\nb\n"
+ ├── @ InterpolatedStringNode (location: (58,0)-(58,5))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (58,0)-(58,5) = "<<DOC"
+ │ ├── parts: (length: 4)
+ │ │ ├── @ EmbeddedStatementsNode (location: (59,0)-(59,3))
+ │ │ │ ├── opening_loc: (59,0)-(59,2) = "\#{"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── closing_loc: (59,2)-(59,3) = "}"
+ │ │ ├── @ StringNode (location: (59,3)-(60,1))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (59,3)-(60,1) = "a\n "
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n "
+ │ │ ├── @ EmbeddedStatementsNode (location: (60,1)-(60,4))
+ │ │ │ ├── opening_loc: (60,1)-(60,3) = "\#{"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── closing_loc: (60,3)-(60,4) = "}"
+ │ │ └── @ StringNode (location: (60,4)-(61,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (60,4)-(61,0) = "a\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a\n"
+ │ └── closing_loc: (61,0)-(62,0) = "DOC\n"
+ ├── @ InterpolatedStringNode (location: (63,0)-(63,5))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (63,0)-(63,5) = "<<DOC"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (64,0)-(64,2))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (64,0)-(64,2) = " "
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: " "
+ │ │ ├── @ EmbeddedStatementsNode (location: (64,2)-(64,5))
+ │ │ │ ├── opening_loc: (64,2)-(64,4) = "\#{"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── closing_loc: (64,4)-(64,5) = "}"
+ │ │ └── @ StringNode (location: (64,5)-(66,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (64,5)-(66,0) = "\n \\\#{}\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\n \#{}\n"
+ │ └── closing_loc: (66,0)-(67,0) = "DOC\n"
+ ├── @ InterpolatedStringNode (location: (68,0)-(68,5))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (68,0)-(68,5) = "<<DOC"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (69,0)-(69,2))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (69,0)-(69,2) = " a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: " a"
+ │ │ ├── @ EmbeddedStatementsNode (location: (69,2)-(69,5))
+ │ │ │ ├── opening_loc: (69,2)-(69,4) = "\#{"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── closing_loc: (69,4)-(69,5) = "}"
+ │ │ └── @ StringNode (location: (69,5)-(71,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (69,5)-(71,0) = "b\n c\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b\n c\n"
+ │ └── closing_loc: (71,0)-(72,0) = "DOC\n"
+ ├── @ InterpolatedStringNode (location: (73,0)-(73,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (73,0)-(73,6) = "<<~DOC"
+ │ ├── parts: (length: 2)
+ │ │ ├── @ EmbeddedStatementsNode (location: (74,2)-(74,5))
+ │ │ │ ├── opening_loc: (74,2)-(74,4) = "\#{"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── closing_loc: (74,4)-(74,5) = "}"
+ │ │ └── @ StringNode (location: (74,5)-(75,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (74,5)-(75,0) = "\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\n"
+ │ └── closing_loc: (75,0)-(76,0) = "DOC\n"
+ ├── @ IfNode (location: (77,0)-(81,3))
+ │ ├── if_keyword_loc: (77,0)-(77,2) = "if"
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (77,3)-(77,7))
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (78,2)-(78,8))
+ │ │ └── body: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (78,2)-(78,8))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (78,2)-(78,8) = "<<~DOC"
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ EmbeddedStatementsNode (location: (79,4)-(79,7))
+ │ │ │ │ ├── opening_loc: (79,4)-(79,6) = "\#{"
+ │ │ │ │ ├── statements: ∅
+ │ │ │ │ └── closing_loc: (79,6)-(79,7) = "}"
+ │ │ │ └── @ StringNode (location: (79,7)-(80,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (79,7)-(80,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── closing_loc: (80,0)-(81,0) = " DOC\n"
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (81,0)-(81,3) = "end"
+ ├── @ IfNode (location: (83,0)-(87,3))
+ │ ├── if_keyword_loc: (83,0)-(83,2) = "if"
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (83,3)-(83,7))
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (84,2)-(84,8))
+ │ │ └── body: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (84,2)-(84,8))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (84,2)-(84,8) = "<<~DOC"
+ │ │ ├── parts: (length: 3)
+ │ │ │ ├── @ StringNode (location: (85,0)-(85,5))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (85,0)-(85,5) = " b"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "b"
+ │ │ │ ├── @ EmbeddedStatementsNode (location: (85,5)-(85,8))
+ │ │ │ │ ├── opening_loc: (85,5)-(85,7) = "\#{"
+ │ │ │ │ ├── statements: ∅
+ │ │ │ │ └── closing_loc: (85,7)-(85,8) = "}"
+ │ │ │ └── @ StringNode (location: (85,8)-(86,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (85,8)-(86,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── closing_loc: (86,0)-(87,0) = " DOC\n"
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (87,0)-(87,3) = "end"
+ ├── @ IfNode (location: (89,0)-(93,3))
+ │ ├── if_keyword_loc: (89,0)-(89,2) = "if"
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (89,3)-(89,7))
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (90,2)-(90,8))
+ │ │ └── body: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (90,2)-(90,8))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (90,2)-(90,8) = "<<~DOC"
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ EmbeddedStatementsNode (location: (91,4)-(91,7))
+ │ │ │ │ ├── opening_loc: (91,4)-(91,6) = "\#{"
+ │ │ │ │ ├── statements: ∅
+ │ │ │ │ └── closing_loc: (91,6)-(91,7) = "}"
+ │ │ │ └── @ StringNode (location: (91,7)-(92,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (91,7)-(92,0) = "a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ └── closing_loc: (92,0)-(93,0) = " DOC\n"
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (93,0)-(93,3) = "end"
+ ├── @ IfNode (location: (95,0)-(101,3))
+ │ ├── if_keyword_loc: (95,0)-(95,2) = "if"
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (95,3)-(95,7))
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (96,2)-(96,10))
+ │ │ └── body: (length: 1)
+ │ │ └── @ StringNode (location: (96,2)-(96,10))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (96,2)-(96,10) = "<<-'DOC'"
+ │ │ ├── content_loc: (97,0)-(100,0) = " a\n\n b\n"
+ │ │ ├── closing_loc: (100,0)-(101,0) = " DOC\n"
+ │ │ └── unescaped: " a\n\n b\n"
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (101,0)-(101,3) = "end"
+ ├── @ InterpolatedStringNode (location: (103,0)-(103,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (103,0)-(103,1) = "\""
+ │ ├── parts: (length: 2)
+ │ │ ├── @ EmbeddedStatementsNode (location: (103,1)-(103,4))
+ │ │ │ ├── opening_loc: (103,1)-(103,3) = "\#{"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── closing_loc: (103,3)-(103,4) = "}"
+ │ │ └── @ StringNode (location: (103,4)-(103,5))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (103,4)-(103,5) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ └── closing_loc: (103,5)-(103,6) = "\""
+ ├── @ InterpolatedStringNode (location: (105,0)-(105,12))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (105,0)-(105,2) = "%("
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (105,2)-(105,5))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (105,2)-(105,5) = "\\n\""
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n\""
+ │ │ ├── @ EmbeddedStatementsNode (location: (105,5)-(105,8))
+ │ │ │ ├── opening_loc: (105,5)-(105,7) = "\#{"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── closing_loc: (105,7)-(105,8) = "}"
+ │ │ └── @ StringNode (location: (105,8)-(105,11))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (105,8)-(105,11) = "\"\\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\"\n"
+ │ └── closing_loc: (105,11)-(105,12) = ")"
+ ├── @ InterpolatedStringNode (location: (107,0)-(107,14))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (107,0)-(107,3) = "%Q("
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (107,3)-(107,7))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (107,3)-(107,7) = "-\\n\""
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "-\n\""
+ │ │ ├── @ EmbeddedStatementsNode (location: (107,7)-(107,10))
+ │ │ │ ├── opening_loc: (107,7)-(107,9) = "\#{"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── closing_loc: (107,9)-(107,10) = "}"
+ │ │ └── @ StringNode (location: (107,10)-(107,13))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (107,10)-(107,13) = "\"\\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\"\n"
+ │ └── closing_loc: (107,13)-(107,14) = ")"
+ ├── @ InterpolatedStringNode (location: (109,0)-(111,2))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (109,0)-(109,1) = "\""
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (109,1)-(110,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (109,1)-(110,0) = "a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ ├── @ EmbeddedStatementsNode (location: (110,0)-(110,3))
+ │ │ │ ├── opening_loc: (110,0)-(110,2) = "\#{"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── closing_loc: (110,2)-(110,3) = "}"
+ │ │ └── @ StringNode (location: (110,3)-(111,1))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (110,3)-(111,1) = "\nb"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\nb"
+ │ └── closing_loc: (111,1)-(111,2) = "\""
+ ├── @ InterpolatedStringNode (location: (113,0)-(114,2))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (113,0)-(113,1) = "\""
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (113,1)-(113,4))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (113,1)-(113,4) = "a\\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ ├── @ EmbeddedStatementsNode (location: (113,4)-(113,7))
+ │ │ │ ├── opening_loc: (113,4)-(113,6) = "\#{"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── closing_loc: (113,6)-(113,7) = "}"
+ │ │ └── @ StringNode (location: (113,7)-(114,1))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (113,7)-(114,1) = "\nb"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\nb"
+ │ └── closing_loc: (114,1)-(114,2) = "\""
+ ├── @ InterpolatedStringNode (location: (116,0)-(117,7))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (116,0)-(116,1) = "\""
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (116,1)-(117,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (116,1)-(117,0) = "a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ ├── @ EmbeddedStatementsNode (location: (117,0)-(117,3))
+ │ │ │ ├── opening_loc: (117,0)-(117,2) = "\#{"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── closing_loc: (117,2)-(117,3) = "}"
+ │ │ └── @ StringNode (location: (117,3)-(117,6))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (117,3)-(117,6) = "\\nb"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\nb"
+ │ └── closing_loc: (117,6)-(117,7) = "\""
+ ├── @ InterpolatedStringNode (location: (119,0)-(120,5))
+ │ ├── flags: ∅
+ │ ├── opening_loc: ∅
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (119,0)-(119,3))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: (119,0)-(119,1) = "'"
+ │ │ │ ├── content_loc: (119,1)-(119,2) = "a"
+ │ │ │ ├── closing_loc: (119,2)-(119,3) = "'"
+ │ │ │ └── unescaped: "a"
+ │ │ └── @ InterpolatedStringNode (location: (120,0)-(120,5))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (120,0)-(120,1) = "\""
+ │ │ ├── parts: (length: 1)
+ │ │ │ └── @ EmbeddedStatementsNode (location: (120,1)-(120,4))
+ │ │ │ ├── opening_loc: (120,1)-(120,3) = "\#{"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── closing_loc: (120,3)-(120,4) = "}"
+ │ │ └── closing_loc: (120,4)-(120,5) = "\""
+ │ └── closing_loc: ∅
+ ├── @ InterpolatedStringNode (location: (122,0)-(122,8))
+ │ ├── flags: ∅
+ │ ├── opening_loc: ∅
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (122,0)-(122,2))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: (122,0)-(122,1) = "\""
+ │ │ │ ├── content_loc: (122,1)-(122,1) = ""
+ │ │ │ ├── closing_loc: (122,1)-(122,2) = "\""
+ │ │ │ └── unescaped: ""
+ │ │ ├── @ StringNode (location: (122,3)-(122,5))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: (122,3)-(122,4) = "\""
+ │ │ │ ├── content_loc: (122,4)-(122,4) = ""
+ │ │ │ ├── closing_loc: (122,4)-(122,5) = "\""
+ │ │ │ └── unescaped: ""
+ │ │ └── @ StringNode (location: (122,6)-(122,8))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: (122,6)-(122,7) = "\""
+ │ │ ├── content_loc: (122,7)-(122,7) = ""
+ │ │ ├── closing_loc: (122,7)-(122,8) = "\""
+ │ │ └── unescaped: ""
+ │ └── closing_loc: ∅
+ ├── @ InterpolatedStringNode (location: (124,0)-(124,12))
+ │ ├── flags: ∅
+ │ ├── opening_loc: ∅
+ │ ├── parts: (length: 2)
+ │ │ ├── @ InterpolatedStringNode (location: (124,0)-(124,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (124,0)-(124,1) = "\""
+ │ │ │ ├── parts: (length: 2)
+ │ │ │ │ ├── @ StringNode (location: (124,1)-(124,2))
+ │ │ │ │ │ ├── flags: frozen
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── content_loc: (124,1)-(124,2) = "a"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "a"
+ │ │ │ │ └── @ EmbeddedStatementsNode (location: (124,2)-(124,7))
+ │ │ │ │ ├── opening_loc: (124,2)-(124,4) = "\#{"
+ │ │ │ │ ├── statements:
+ │ │ │ │ │ @ StatementsNode (location: (124,4)-(124,6))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ InstanceVariableReadNode (location: (124,4)-(124,6))
+ │ │ │ │ │ └── name: :@a
+ │ │ │ │ └── closing_loc: (124,6)-(124,7) = "}"
+ │ │ │ └── closing_loc: (124,7)-(124,8) = "\""
+ │ │ └── @ StringNode (location: (124,9)-(124,12))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: (124,9)-(124,10) = "\""
+ │ │ ├── content_loc: (124,10)-(124,11) = "b"
+ │ │ ├── closing_loc: (124,11)-(124,12) = "\""
+ │ │ └── unescaped: "b"
+ │ └── closing_loc: ∅
+ ├── @ InterpolatedStringNode (location: (125,0)-(125,10))
+ │ ├── flags: ∅
+ │ ├── opening_loc: ∅
+ │ ├── parts: (length: 2)
+ │ │ ├── @ InterpolatedStringNode (location: (125,0)-(125,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (125,0)-(125,1) = "\""
+ │ │ │ ├── parts: (length: 2)
+ │ │ │ │ ├── @ StringNode (location: (125,1)-(125,2))
+ │ │ │ │ │ ├── flags: frozen
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── content_loc: (125,1)-(125,2) = "a"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "a"
+ │ │ │ │ └── @ EmbeddedVariableNode (location: (125,2)-(125,5))
+ │ │ │ │ ├── operator_loc: (125,2)-(125,3) = "#"
+ │ │ │ │ └── variable:
+ │ │ │ │ @ InstanceVariableReadNode (location: (125,3)-(125,5))
+ │ │ │ │ └── name: :@a
+ │ │ │ └── closing_loc: (125,5)-(125,6) = "\""
+ │ │ └── @ StringNode (location: (125,7)-(125,10))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: (125,7)-(125,8) = "\""
+ │ │ ├── content_loc: (125,8)-(125,9) = "b"
+ │ │ ├── closing_loc: (125,9)-(125,10) = "\""
+ │ │ └── unescaped: "b"
+ │ └── closing_loc: ∅
+ ├── @ InterpolatedStringNode (location: (126,0)-(126,10))
+ │ ├── flags: ∅
+ │ ├── opening_loc: ∅
+ │ ├── parts: (length: 2)
+ │ │ ├── @ InterpolatedStringNode (location: (126,0)-(126,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (126,0)-(126,1) = "\""
+ │ │ │ ├── parts: (length: 2)
+ │ │ │ │ ├── @ StringNode (location: (126,1)-(126,2))
+ │ │ │ │ │ ├── flags: frozen
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── content_loc: (126,1)-(126,2) = "a"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "a"
+ │ │ │ │ └── @ EmbeddedVariableNode (location: (126,2)-(126,5))
+ │ │ │ │ ├── operator_loc: (126,2)-(126,3) = "#"
+ │ │ │ │ └── variable:
+ │ │ │ │ @ GlobalVariableReadNode (location: (126,3)-(126,5))
+ │ │ │ │ └── name: :$a
+ │ │ │ └── closing_loc: (126,5)-(126,6) = "\""
+ │ │ └── @ StringNode (location: (126,7)-(126,10))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: (126,7)-(126,8) = "\""
+ │ │ ├── content_loc: (126,8)-(126,9) = "b"
+ │ │ ├── closing_loc: (126,9)-(126,10) = "\""
+ │ │ └── unescaped: "b"
+ │ └── closing_loc: ∅
+ └── @ InterpolatedStringNode (location: (127,0)-(127,11))
+ ├── flags: ∅
+ ├── opening_loc: ∅
+ ├── parts: (length: 2)
+ │ ├── @ InterpolatedStringNode (location: (127,0)-(127,7))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (127,0)-(127,1) = "\""
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (127,1)-(127,2))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (127,1)-(127,2) = "a"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "a"
+ │ │ │ └── @ EmbeddedVariableNode (location: (127,2)-(127,6))
+ │ │ │ ├── operator_loc: (127,2)-(127,3) = "#"
+ │ │ │ └── variable:
+ │ │ │ @ ClassVariableReadNode (location: (127,3)-(127,6))
+ │ │ │ └── name: :@@a
+ │ │ └── closing_loc: (127,6)-(127,7) = "\""
+ │ └── @ StringNode (location: (127,8)-(127,11))
+ │ ├── flags: frozen
+ │ ├── opening_loc: (127,8)-(127,9) = "\""
+ │ ├── content_loc: (127,9)-(127,10) = "b"
+ │ ├── closing_loc: (127,10)-(127,11) = "\""
+ │ └── unescaped: "b"
+ └── closing_loc: ∅
diff --git a/test/prism/snapshots/unparser/corpus/semantic/kwbegin.txt b/test/prism/snapshots/unparser/corpus/semantic/kwbegin.txt
new file mode 100644
index 0000000000..38486ddc12
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/semantic/kwbegin.txt
@@ -0,0 +1,259 @@
+@ ProgramNode (location: (1,0)-(42,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(42,3))
+ └── body: (length: 8)
+ ├── @ BeginNode (location: (1,0)-(3,3))
+ │ ├── begin_keyword_loc: (1,0)-(1,5) = "begin"
+ │ ├── statements: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (2,0)-(2,6))
+ │ │ ├── keyword_loc: (2,0)-(2,6) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (3,0)-(3,3) = "end"
+ ├── @ BeginNode (location: (5,0)-(8,3))
+ │ ├── begin_keyword_loc: (5,0)-(5,5) = "begin"
+ │ ├── statements: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (6,0)-(6,6))
+ │ │ ├── keyword_loc: (6,0)-(6,6) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause:
+ │ │ @ ElseNode (location: (7,0)-(8,3))
+ │ │ ├── else_keyword_loc: (7,0)-(7,4) = "else"
+ │ │ ├── statements: ∅
+ │ │ └── end_keyword_loc: (8,0)-(8,3) = "end"
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (8,0)-(8,3) = "end"
+ ├── @ BeginNode (location: (10,0)-(12,3))
+ │ ├── begin_keyword_loc: (10,0)-(10,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (11,2)-(11,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (11,2)-(11,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (11,2)-(11,3) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (12,0)-(12,3) = "end"
+ ├── @ BeginNode (location: (14,0)-(18,3))
+ │ ├── begin_keyword_loc: (14,0)-(14,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (15,2)-(15,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (15,2)-(15,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (15,2)-(15,3) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (16,0)-(17,3))
+ │ │ ├── keyword_loc: (16,0)-(16,6) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (17,2)-(17,3))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (17,2)-(17,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (17,2)-(17,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (18,0)-(18,3) = "end"
+ ├── @ BeginNode (location: (20,0)-(25,3))
+ │ ├── begin_keyword_loc: (20,0)-(20,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (21,2)-(22,3))
+ │ │ └── body: (length: 2)
+ │ │ ├── @ CallNode (location: (21,2)-(21,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (21,2)-(21,3) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ CallNode (location: (22,2)-(22,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (22,2)-(22,3) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (23,0)-(24,3))
+ │ │ ├── keyword_loc: (23,0)-(23,6) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (24,2)-(24,3))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (24,2)-(24,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (24,2)-(24,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (25,0)-(25,3) = "end"
+ ├── @ BeginNode (location: (27,0)-(30,3))
+ │ ├── begin_keyword_loc: (27,0)-(27,5) = "begin"
+ │ ├── statements: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (28,0)-(28,8))
+ │ │ ├── keyword_loc: (28,0)-(28,6) = "rescue"
+ │ │ ├── exceptions: (length: 1)
+ │ │ │ └── @ ConstantReadNode (location: (28,7)-(28,8))
+ │ │ │ └── name: :A
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause:
+ │ │ @ ElseNode (location: (29,0)-(30,3))
+ │ │ ├── else_keyword_loc: (29,0)-(29,4) = "else"
+ │ │ ├── statements: ∅
+ │ │ └── end_keyword_loc: (30,0)-(30,3) = "end"
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (30,0)-(30,3) = "end"
+ ├── @ BeginNode (location: (32,0)-(32,26))
+ │ ├── begin_keyword_loc: (32,0)-(32,5) = "begin"
+ │ ├── statements: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (32,7)-(32,15))
+ │ │ ├── keyword_loc: (32,7)-(32,13) = "rescue"
+ │ │ ├── exceptions: (length: 1)
+ │ │ │ └── @ ConstantReadNode (location: (32,14)-(32,15))
+ │ │ │ └── name: :A
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause:
+ │ │ @ ElseNode (location: (32,17)-(32,26))
+ │ │ ├── else_keyword_loc: (32,17)-(32,21) = "else"
+ │ │ ├── statements: ∅
+ │ │ └── end_keyword_loc: (32,23)-(32,26) = "end"
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (32,23)-(32,26) = "end"
+ └── @ BeginNode (location: (34,0)-(42,3))
+ ├── begin_keyword_loc: (34,0)-(34,5) = "begin"
+ ├── statements:
+ │ @ StatementsNode (location: (35,2)-(35,3))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (35,2)-(35,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (35,2)-(35,3) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── rescue_clause:
+ │ @ RescueNode (location: (36,0)-(39,3))
+ │ ├── keyword_loc: (36,0)-(36,6) = "rescue"
+ │ ├── exceptions: (length: 1)
+ │ │ └── @ ConstantReadNode (location: (36,7)-(36,8))
+ │ │ └── name: :A
+ │ ├── operator_loc: ∅
+ │ ├── reference: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (37,2)-(37,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (37,2)-(37,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (37,2)-(37,3) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── consequent:
+ │ @ RescueNode (location: (38,0)-(39,3))
+ │ ├── keyword_loc: (38,0)-(38,6) = "rescue"
+ │ ├── exceptions: (length: 1)
+ │ │ └── @ ConstantReadNode (location: (38,7)-(38,8))
+ │ │ └── name: :B
+ │ ├── operator_loc: ∅
+ │ ├── reference: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (39,2)-(39,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (39,2)-(39,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :c
+ │ │ ├── message_loc: (39,2)-(39,3) = "c"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── consequent: ∅
+ ├── else_clause: ∅
+ ├── ensure_clause:
+ │ @ EnsureNode (location: (40,0)-(42,3))
+ │ ├── ensure_keyword_loc: (40,0)-(40,6) = "ensure"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (41,2)-(41,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (41,2)-(41,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :d
+ │ │ ├── message_loc: (41,2)-(41,3) = "d"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── end_keyword_loc: (42,0)-(42,3) = "end"
+ └── end_keyword_loc: (42,0)-(42,3) = "end"
diff --git a/test/prism/snapshots/unparser/corpus/semantic/literal.txt b/test/prism/snapshots/unparser/corpus/semantic/literal.txt
new file mode 100644
index 0000000000..ef666890be
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/semantic/literal.txt
@@ -0,0 +1,103 @@
+@ ProgramNode (location: (1,0)-(14,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(14,10))
+ └── body: (length: 14)
+ ├── @ RationalNode (location: (1,0)-(1,4))
+ │ └── numeric:
+ │ @ FloatNode (location: (1,0)-(1,3))
+ │ └── value: 1.0
+ ├── @ RationalNode (location: (2,0)-(2,3))
+ │ └── numeric:
+ │ @ IntegerNode (location: (2,0)-(2,2))
+ │ ├── flags: decimal
+ │ └── value: 0
+ ├── @ IntegerNode (location: (3,0)-(3,3))
+ │ ├── flags: hexadecimal
+ │ └── value: 1
+ ├── @ IntegerNode (location: (4,0)-(4,5))
+ │ ├── flags: decimal
+ │ └── value: 1000
+ ├── @ FloatNode (location: (5,0)-(5,4))
+ │ └── value: 10000000000.0
+ ├── @ FloatNode (location: (6,0)-(6,14))
+ │ └── value: Infinity
+ ├── @ FloatNode (location: (7,0)-(7,15))
+ │ └── value: -Infinity
+ ├── @ StringNode (location: (8,0)-(8,2))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (8,0)-(8,1) = "?"
+ │ ├── content_loc: (8,1)-(8,2) = "c"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "c"
+ ├── @ RegularExpressionNode (location: (9,0)-(9,5))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (9,0)-(9,3) = "%r("
+ │ ├── content_loc: (9,3)-(9,4) = "/"
+ │ ├── closing_loc: (9,4)-(9,5) = ")"
+ │ └── unescaped: "/"
+ ├── @ RegularExpressionNode (location: (10,0)-(10,6))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (10,0)-(10,3) = "%r("
+ │ ├── content_loc: (10,3)-(10,5) = "\\)"
+ │ ├── closing_loc: (10,5)-(10,6) = ")"
+ │ └── unescaped: "\\)"
+ ├── @ InterpolatedRegularExpressionNode (location: (11,0)-(11,14))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (11,0)-(11,3) = "%r("
+ │ ├── parts: (length: 2)
+ │ │ ├── @ EmbeddedStatementsNode (location: (11,3)-(11,10))
+ │ │ │ ├── opening_loc: (11,3)-(11,5) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (11,5)-(11,9))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ InstanceVariableReadNode (location: (11,5)-(11,9))
+ │ │ │ │ └── name: :@bar
+ │ │ │ └── closing_loc: (11,9)-(11,10) = "}"
+ │ │ └── @ StringNode (location: (11,10)-(11,13))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (11,10)-(11,13) = "baz"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "baz"
+ │ └── closing_loc: (11,13)-(11,14) = ")"
+ ├── @ FloatNode (location: (12,0)-(12,16))
+ │ └── value: Infinity
+ ├── @ FloatNode (location: (13,0)-(13,17))
+ │ └── value: -Infinity
+ └── @ CallNode (location: (14,0)-(14,10))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :w
+ ├── message_loc: (14,0)-(14,1) = "w"
+ ├── opening_loc: (14,1)-(14,2) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (14,2)-(14,9))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (14,2)-(14,9))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (14,2)-(14,5) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (14,6)-(14,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (14,6)-(14,9))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (14,6)-(14,9) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: (14,9)-(14,10) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/unparser/corpus/semantic/opasgn.txt b/test/prism/snapshots/unparser/corpus/semantic/opasgn.txt
new file mode 100644
index 0000000000..e100dd8ecb
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/semantic/opasgn.txt
@@ -0,0 +1,69 @@
+@ ProgramNode (location: (1,0)-(1,25))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,25))
+ └── body: (length: 1)
+ └── @ IndexOperatorWriteNode (location: (1,0)-(1,25))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :y
+ │ ├── message_loc: (1,0)-(1,1) = "y"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── opening_loc: (1,1)-(1,2) = "["
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,11))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ InterpolatedStringNode (location: (1,2)-(1,11))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,2)-(1,3) = "\""
+ │ ├── parts: (length: 2)
+ │ │ ├── @ EmbeddedStatementsNode (location: (1,3)-(1,8))
+ │ │ │ ├── opening_loc: (1,3)-(1,5) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (1,5)-(1,7))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (1,5)-(1,7))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 42
+ │ │ │ └── closing_loc: (1,7)-(1,8) = "}"
+ │ │ └── @ StringNode (location: (1,8)-(1,10))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (1,8)-(1,10) = "\\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\n"
+ │ └── closing_loc: (1,10)-(1,11) = "\""
+ ├── closing_loc: (1,11)-(1,12) = "]"
+ ├── block: ∅
+ ├── operator: :+
+ ├── operator_loc: (1,13)-(1,15) = "+="
+ └── value:
+ @ InterpolatedStringNode (location: (1,16)-(1,25))
+ ├── flags: ∅
+ ├── opening_loc: (1,16)-(1,17) = "\""
+ ├── parts: (length: 2)
+ │ ├── @ EmbeddedStatementsNode (location: (1,17)-(1,22))
+ │ │ ├── opening_loc: (1,17)-(1,19) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,19)-(1,21))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (1,19)-(1,21))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ └── closing_loc: (1,21)-(1,22) = "}"
+ │ └── @ StringNode (location: (1,22)-(1,24))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (1,22)-(1,24) = "\\n"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "\n"
+ └── closing_loc: (1,24)-(1,25) = "\""
diff --git a/test/prism/snapshots/unparser/corpus/semantic/send.txt b/test/prism/snapshots/unparser/corpus/semantic/send.txt
new file mode 100644
index 0000000000..7c152c3bfb
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/semantic/send.txt
@@ -0,0 +1,163 @@
+@ ProgramNode (location: (1,0)-(6,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(6,15))
+ └── body: (length: 4)
+ ├── @ CallNode (location: (1,0)-(1,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (2,0)-(2,6))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (2,0)-(2,3) = "foo"
+ │ ├── opening_loc: (2,3)-(2,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (2,4)-(2,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (2,4)-(2,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: (2,5)-(2,6) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (4,0)-(4,15))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (4,0)-(4,10))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (4,0)-(4,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ CallNode (location: (4,0)-(4,1))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── message_loc: (4,0)-(4,1) = "a"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── call_operator_loc: (4,1)-(4,2) = "."
+ │ │ │ ├── name: :===
+ │ │ │ ├── message_loc: (4,2)-(4,5) = "==="
+ │ │ │ ├── opening_loc: (4,5)-(4,6) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (4,6)-(4,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (4,6)-(4,7))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :b
+ │ │ │ │ ├── message_loc: (4,6)-(4,7) = "b"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: (4,7)-(4,8) = ")"
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (4,8)-(4,9) = "."
+ │ │ ├── name: :c
+ │ │ ├── message_loc: (4,9)-(4,10) = "c"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :==
+ │ ├── message_loc: (4,11)-(4,13) = "=="
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (4,14)-(4,15))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (4,14)-(4,15))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :d
+ │ │ ├── message_loc: (4,14)-(4,15) = "d"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (6,0)-(6,15))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (6,0)-(6,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (6,0)-(6,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :==
+ ├── message_loc: (6,2)-(6,4) = "=="
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (6,5)-(6,15))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (6,5)-(6,15))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (6,5)-(6,8))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ CallNode (location: (6,5)-(6,6))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :d
+ │ │ │ ├── message_loc: (6,5)-(6,6) = "d"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── call_operator_loc: (6,6)-(6,7) = "."
+ │ │ ├── name: :c
+ │ │ ├── message_loc: (6,7)-(6,8) = "c"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (6,8)-(6,9) = "."
+ │ ├── name: :===
+ │ ├── message_loc: (6,9)-(6,12) = "==="
+ │ ├── opening_loc: (6,12)-(6,13) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (6,13)-(6,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (6,13)-(6,14))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :c
+ │ │ ├── message_loc: (6,13)-(6,14) = "c"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (6,14)-(6,15) = ")"
+ │ └── block: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/unparser/corpus/semantic/undef.txt b/test/prism/snapshots/unparser/corpus/semantic/undef.txt
new file mode 100644
index 0000000000..ecb073148d
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/semantic/undef.txt
@@ -0,0 +1,29 @@
+@ ProgramNode (location: (1,0)-(2,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,14))
+ └── body: (length: 2)
+ ├── @ UndefNode (location: (1,0)-(1,9))
+ │ ├── names: (length: 1)
+ │ │ └── @ SymbolNode (location: (1,6)-(1,9))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (1,6)-(1,9) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ └── keyword_loc: (1,0)-(1,5) = "undef"
+ └── @ UndefNode (location: (2,0)-(2,14))
+ ├── names: (length: 2)
+ │ ├── @ SymbolNode (location: (2,6)-(2,9))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (2,6)-(2,9) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ └── @ SymbolNode (location: (2,11)-(2,14))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: ∅
+ │ ├── value_loc: (2,11)-(2,14) = "bar"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "bar"
+ └── keyword_loc: (2,0)-(2,5) = "undef"
diff --git a/test/prism/snapshots/unparser/corpus/semantic/while.txt b/test/prism/snapshots/unparser/corpus/semantic/while.txt
new file mode 100644
index 0000000000..36bfba5be6
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/semantic/while.txt
@@ -0,0 +1,277 @@
+@ ProgramNode (location: (1,0)-(25,3))
+├── locals: [:foo, :a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(25,3))
+ └── body: (length: 7)
+ ├── @ UntilNode (location: (1,0)-(1,13))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (1,2)-(1,7) = "until"
+ │ ├── closing_loc: ∅
+ │ ├── predicate:
+ │ │ @ CallNode (location: (1,8)-(1,13))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b?
+ │ │ ├── message_loc: (1,8)-(1,10) = "b?"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (1,11)-(1,13))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (1,11)-(1,12) = "{"
+ │ │ └── closing_loc: (1,12)-(1,13) = "}"
+ │ └── statements:
+ │ @ StatementsNode (location: (1,0)-(1,1))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ UntilNode (location: (3,0)-(5,3))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (3,0)-(3,5) = "until"
+ │ ├── closing_loc: (5,0)-(5,3) = "end"
+ │ ├── predicate:
+ │ │ @ CallNode (location: (3,6)-(3,11))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b?
+ │ │ ├── message_loc: (3,6)-(3,8) = "b?"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (3,9)-(3,11))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (3,9)-(3,10) = "{"
+ │ │ └── closing_loc: (3,10)-(3,11) = "}"
+ │ └── statements:
+ │ @ StatementsNode (location: (4,2)-(4,3))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (4,2)-(4,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (4,2)-(4,3) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ WhileNode (location: (7,0)-(7,19))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (7,10)-(7,15) = "while"
+ │ ├── closing_loc: ∅
+ │ ├── predicate:
+ │ │ @ LocalVariableReadNode (location: (7,16)-(7,19))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ └── statements:
+ │ @ StatementsNode (location: (7,0)-(7,9))
+ │ └── body: (length: 1)
+ │ └── @ LocalVariableWriteNode (location: (7,0)-(7,9))
+ │ ├── name: :foo
+ │ ├── depth: 0
+ │ ├── name_loc: (7,0)-(7,3) = "foo"
+ │ ├── value:
+ │ │ @ CallNode (location: (7,6)-(7,9))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (7,6)-(7,9) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (7,4)-(7,5) = "="
+ ├── @ UntilNode (location: (9,0)-(9,18))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (9,2)-(9,7) = "until"
+ │ ├── closing_loc: ∅
+ │ ├── predicate:
+ │ │ @ AndNode (location: (9,8)-(9,18))
+ │ │ ├── left:
+ │ │ │ @ CallNode (location: (9,8)-(9,9))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (9,8)-(9,9) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── right:
+ │ │ │ @ CallNode (location: (9,13)-(9,18))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (9,13)-(9,14) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (9,15)-(9,18))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body: ∅
+ │ │ │ ├── opening_loc: (9,15)-(9,16) = "{"
+ │ │ │ └── closing_loc: (9,17)-(9,18) = "}"
+ │ │ └── operator_loc: (9,10)-(9,12) = "&&"
+ │ └── statements:
+ │ @ StatementsNode (location: (9,0)-(9,1))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (9,0)-(9,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (9,0)-(9,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ WhileNode (location: (11,0)-(13,3))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (11,0)-(11,5) = "while"
+ │ ├── closing_loc: (13,0)-(13,3) = "end"
+ │ ├── predicate:
+ │ │ @ LocalVariableWriteNode (location: (11,6)-(11,11))
+ │ │ ├── name: :a
+ │ │ ├── depth: 0
+ │ │ ├── name_loc: (11,6)-(11,7) = "a"
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (11,10)-(11,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (11,10)-(11,11) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (11,8)-(11,9) = "="
+ │ └── statements:
+ │ @ StatementsNode (location: (12,2)-(12,3))
+ │ └── body: (length: 1)
+ │ └── @ LocalVariableReadNode (location: (12,2)-(12,3))
+ │ ├── name: :a
+ │ └── depth: 0
+ ├── @ UntilNode (location: (15,0)-(18,3))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (15,2)-(15,7) = "until"
+ │ ├── closing_loc: ∅
+ │ ├── predicate:
+ │ │ @ CallNode (location: (15,8)-(18,3))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (15,8)-(15,9) = "b"
+ │ │ ├── opening_loc: (15,9)-(15,10) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (15,10)-(15,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ StringNode (location: (15,10)-(15,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (15,10)-(15,16) = "<<-FOO"
+ │ │ │ ├── content_loc: (16,0)-(16,0) = ""
+ │ │ │ ├── closing_loc: (16,0)-(17,0) = "FOO\n"
+ │ │ │ └── unescaped: ""
+ │ │ ├── closing_loc: (15,16)-(15,17) = ")"
+ │ │ └── block:
+ │ │ @ BlockNode (location: (15,18)-(18,3))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (17,2)-(17,3))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (17,2)-(17,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── message_loc: (17,2)-(17,3) = "c"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (15,18)-(15,20) = "do"
+ │ │ └── closing_loc: (18,0)-(18,3) = "end"
+ │ └── statements:
+ │ @ StatementsNode (location: (15,0)-(15,1))
+ │ └── body: (length: 1)
+ │ └── @ LocalVariableReadNode (location: (15,0)-(15,1))
+ │ ├── name: :a
+ │ └── depth: 0
+ └── @ ModuleNode (location: (20,0)-(25,3))
+ ├── locals: [:foo]
+ ├── module_keyword_loc: (20,0)-(20,6) = "module"
+ ├── constant_path:
+ │ @ ConstantReadNode (location: (20,7)-(20,8))
+ │ └── name: :A
+ ├── body:
+ │ @ StatementsNode (location: (21,2)-(24,5))
+ │ └── body: (length: 2)
+ │ ├── @ LocalVariableWriteNode (location: (21,2)-(21,11))
+ │ │ ├── name: :foo
+ │ │ ├── depth: 0
+ │ │ ├── name_loc: (21,2)-(21,5) = "foo"
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (21,8)-(21,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :exp
+ │ │ │ ├── message_loc: (21,8)-(21,11) = "exp"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (21,6)-(21,7) = "="
+ │ └── @ WhileNode (location: (22,2)-(24,5))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (22,2)-(22,7) = "while"
+ │ ├── closing_loc: (24,2)-(24,5) = "end"
+ │ ├── predicate:
+ │ │ @ LocalVariableReadNode (location: (22,8)-(22,11))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ └── statements:
+ │ @ StatementsNode (location: (23,4)-(23,13))
+ │ └── body: (length: 1)
+ │ └── @ LocalVariableWriteNode (location: (23,4)-(23,13))
+ │ ├── name: :foo
+ │ ├── depth: 0
+ │ ├── name_loc: (23,4)-(23,7) = "foo"
+ │ ├── value:
+ │ │ @ CallNode (location: (23,10)-(23,13))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (23,10)-(23,13) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (23,8)-(23,9) = "="
+ ├── end_keyword_loc: (25,0)-(25,3) = "end"
+ └── name: :A
diff --git a/test/prism/snapshots/until.txt b/test/prism/snapshots/until.txt
new file mode 100644
index 0000000000..e855dc89f7
--- /dev/null
+++ b/test/prism/snapshots/until.txt
@@ -0,0 +1,180 @@
+@ ProgramNode (location: (1,0)-(13,20))
+├── locals: [:baz]
+└── statements:
+ @ StatementsNode (location: (1,0)-(13,20))
+ └── body: (length: 7)
+ ├── @ UntilNode (location: (1,0)-(1,18))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (1,0)-(1,5) = "until"
+ │ ├── closing_loc: (1,15)-(1,18) = "end"
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (1,6)-(1,10))
+ │ └── statements:
+ │ @ StatementsNode (location: (1,12)-(1,13))
+ │ └── body: (length: 1)
+ │ └── @ IntegerNode (location: (1,12)-(1,13))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ UntilNode (location: (3,0)-(3,12))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (3,2)-(3,7) = "until"
+ │ ├── closing_loc: ∅
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (3,8)-(3,12))
+ │ └── statements:
+ │ @ StatementsNode (location: (3,0)-(3,1))
+ │ └── body: (length: 1)
+ │ └── @ IntegerNode (location: (3,0)-(3,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ CallNode (location: (5,0)-(5,24))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (5,0)-(5,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (5,4)-(5,24))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (5,6)-(5,22))
+ │ │ └── body: (length: 1)
+ │ │ └── @ UntilNode (location: (5,6)-(5,22))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (5,12)-(5,17) = "until"
+ │ │ ├── closing_loc: ∅
+ │ │ ├── predicate:
+ │ │ │ @ TrueNode (location: (5,18)-(5,22))
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (5,6)-(5,11))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (5,6)-(5,11))
+ │ │ ├── arguments: ∅
+ │ │ └── keyword_loc: (5,6)-(5,11) = "break"
+ │ ├── opening_loc: (5,4)-(5,5) = "{"
+ │ └── closing_loc: (5,23)-(5,24) = "}"
+ ├── @ CallNode (location: (7,0)-(7,23))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (7,0)-(7,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (7,4)-(7,23))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (7,6)-(7,21))
+ │ │ └── body: (length: 1)
+ │ │ └── @ UntilNode (location: (7,6)-(7,21))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (7,11)-(7,16) = "until"
+ │ │ ├── closing_loc: ∅
+ │ │ ├── predicate:
+ │ │ │ @ TrueNode (location: (7,17)-(7,21))
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (7,6)-(7,10))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NextNode (location: (7,6)-(7,10))
+ │ │ ├── arguments: ∅
+ │ │ └── keyword_loc: (7,6)-(7,10) = "next"
+ │ ├── opening_loc: (7,4)-(7,5) = "{"
+ │ └── closing_loc: (7,22)-(7,23) = "}"
+ ├── @ UntilNode (location: (9,0)-(9,17))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (9,7)-(9,12) = "until"
+ │ ├── closing_loc: ∅
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (9,13)-(9,17))
+ │ └── statements:
+ │ @ StatementsNode (location: (9,0)-(9,6))
+ │ └── body: (length: 1)
+ │ └── @ ReturnNode (location: (9,0)-(9,6))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (9,0)-(9,6) = "return"
+ │ └── arguments: ∅
+ ├── @ UntilNode (location: (11,0)-(11,21))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (11,11)-(11,16) = "until"
+ │ ├── closing_loc: ∅
+ │ ├── predicate:
+ │ │ @ CallNode (location: (11,17)-(11,21))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar?
+ │ │ ├── message_loc: (11,17)-(11,21) = "bar?"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── statements:
+ │ @ StatementsNode (location: (11,0)-(11,10))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (11,0)-(11,10))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (11,0)-(11,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (11,4)-(11,10))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ SymbolNode (location: (11,4)-(11,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (11,4)-(11,5) = ":"
+ │ │ │ ├── value_loc: (11,5)-(11,6) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ └── @ SymbolNode (location: (11,8)-(11,10))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (11,8)-(11,9) = ":"
+ │ │ ├── value_loc: (11,9)-(11,10) = "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ WhileNode (location: (13,0)-(13,20))
+ ├── flags: ∅
+ ├── keyword_loc: (13,4)-(13,9) = "while"
+ ├── closing_loc: ∅
+ ├── predicate:
+ │ @ MatchPredicateNode (location: (13,10)-(13,20))
+ │ ├── value:
+ │ │ @ CallNode (location: (13,10)-(13,13))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (13,10)-(13,13) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ LocalVariableTargetNode (location: (13,17)-(13,20))
+ │ │ ├── name: :baz
+ │ │ └── depth: 0
+ │ └── operator_loc: (13,14)-(13,16) = "in"
+ └── statements:
+ @ StatementsNode (location: (13,0)-(13,3))
+ └── body: (length: 1)
+ └── @ CallNode (location: (13,0)-(13,3))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :foo
+ ├── message_loc: (13,0)-(13,3) = "foo"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/variables.txt b/test/prism/snapshots/variables.txt
new file mode 100644
index 0000000000..b79612f924
--- /dev/null
+++ b/test/prism/snapshots/variables.txt
@@ -0,0 +1,408 @@
+@ ProgramNode (location: (1,0)-(47,17))
+├── locals: [:abc, :foo, :bar, :baz, :a, :b, :c, :d]
+└── statements:
+ @ StatementsNode (location: (1,0)-(47,17))
+ └── body: (length: 25)
+ ├── @ ClassVariableReadNode (location: (1,0)-(1,5))
+ │ └── name: :@@abc
+ ├── @ ClassVariableWriteNode (location: (3,0)-(3,9))
+ │ ├── name: :@@abc
+ │ ├── name_loc: (3,0)-(3,5) = "@@abc"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (3,8)-(3,9))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (3,6)-(3,7) = "="
+ ├── @ MultiWriteNode (location: (5,0)-(5,16))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ ClassVariableTargetNode (location: (5,0)-(5,5))
+ │ │ │ └── name: :@@foo
+ │ │ └── @ ClassVariableTargetNode (location: (5,7)-(5,12))
+ │ │ └── name: :@@bar
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (5,13)-(5,14) = "="
+ │ └── value:
+ │ @ IntegerNode (location: (5,15)-(5,16))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ ClassVariableWriteNode (location: (7,0)-(7,12))
+ │ ├── name: :@@foo
+ │ ├── name_loc: (7,0)-(7,5) = "@@foo"
+ │ ├── value:
+ │ │ @ ArrayNode (location: (7,8)-(7,12))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 2)
+ │ │ │ ├── @ IntegerNode (location: (7,8)-(7,9))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── @ IntegerNode (location: (7,11)-(7,12))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (7,6)-(7,7) = "="
+ ├── @ GlobalVariableWriteNode (location: (9,0)-(9,8))
+ │ ├── name: :$abc
+ │ ├── name_loc: (9,0)-(9,4) = "$abc"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (9,7)-(9,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (9,5)-(9,6) = "="
+ ├── @ GlobalVariableReadNode (location: (11,0)-(11,4))
+ │ └── name: :$abc
+ ├── @ InstanceVariableReadNode (location: (13,0)-(13,4))
+ │ └── name: :@abc
+ ├── @ InstanceVariableWriteNode (location: (15,0)-(15,8))
+ │ ├── name: :@abc
+ │ ├── name_loc: (15,0)-(15,4) = "@abc"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (15,7)-(15,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (15,5)-(15,6) = "="
+ ├── @ CallNode (location: (17,0)-(17,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (17,0)-(17,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ LocalVariableWriteNode (location: (19,0)-(19,7))
+ │ ├── name: :abc
+ │ ├── depth: 0
+ │ ├── name_loc: (19,0)-(19,3) = "abc"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (19,6)-(19,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (19,4)-(19,5) = "="
+ ├── @ MultiWriteNode (location: (21,0)-(21,14))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ GlobalVariableTargetNode (location: (21,0)-(21,4))
+ │ │ │ └── name: :$foo
+ │ │ └── @ GlobalVariableTargetNode (location: (21,6)-(21,10))
+ │ │ └── name: :$bar
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (21,11)-(21,12) = "="
+ │ └── value:
+ │ @ IntegerNode (location: (21,13)-(21,14))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ GlobalVariableWriteNode (location: (23,0)-(23,11))
+ │ ├── name: :$foo
+ │ ├── name_loc: (23,0)-(23,4) = "$foo"
+ │ ├── value:
+ │ │ @ ArrayNode (location: (23,7)-(23,11))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 2)
+ │ │ │ ├── @ IntegerNode (location: (23,7)-(23,8))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── @ IntegerNode (location: (23,10)-(23,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (23,5)-(23,6) = "="
+ ├── @ MultiWriteNode (location: (25,0)-(25,14))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ InstanceVariableTargetNode (location: (25,0)-(25,4))
+ │ │ │ └── name: :@foo
+ │ │ └── @ InstanceVariableTargetNode (location: (25,6)-(25,10))
+ │ │ └── name: :@bar
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (25,11)-(25,12) = "="
+ │ └── value:
+ │ @ IntegerNode (location: (25,13)-(25,14))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ InstanceVariableWriteNode (location: (27,0)-(27,11))
+ │ ├── name: :@foo
+ │ ├── name_loc: (27,0)-(27,4) = "@foo"
+ │ ├── value:
+ │ │ @ ArrayNode (location: (27,7)-(27,11))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 2)
+ │ │ │ ├── @ IntegerNode (location: (27,7)-(27,8))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── @ IntegerNode (location: (27,10)-(27,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (27,5)-(27,6) = "="
+ ├── @ LocalVariableWriteNode (location: (29,0)-(29,7))
+ │ ├── name: :foo
+ │ ├── depth: 0
+ │ ├── name_loc: (29,0)-(29,3) = "foo"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (29,6)-(29,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (29,4)-(29,5) = "="
+ ├── @ LocalVariableWriteNode (location: (29,9)-(29,19))
+ │ ├── name: :foo
+ │ ├── depth: 0
+ │ ├── name_loc: (29,9)-(29,12) = "foo"
+ │ ├── value:
+ │ │ @ ArrayNode (location: (29,15)-(29,19))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 2)
+ │ │ │ ├── @ IntegerNode (location: (29,15)-(29,16))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── @ IntegerNode (location: (29,18)-(29,19))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (29,13)-(29,14) = "="
+ ├── @ LocalVariableWriteNode (location: (31,0)-(31,10))
+ │ ├── name: :foo
+ │ ├── depth: 0
+ │ ├── name_loc: (31,0)-(31,3) = "foo"
+ │ ├── value:
+ │ │ @ ArrayNode (location: (31,6)-(31,10))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 2)
+ │ │ │ ├── @ IntegerNode (location: (31,6)-(31,7))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── @ IntegerNode (location: (31,9)-(31,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (31,4)-(31,5) = "="
+ ├── @ MultiWriteNode (location: (33,0)-(33,13))
+ │ ├── lefts: (length: 1)
+ │ │ └── @ LocalVariableTargetNode (location: (33,0)-(33,3))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── rest:
+ │ │ @ SplatNode (location: (33,5)-(33,6))
+ │ │ ├── operator_loc: (33,5)-(33,6) = "*"
+ │ │ └── expression: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (33,7)-(33,8) = "="
+ │ └── value:
+ │ @ ArrayNode (location: (33,9)-(33,13))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (33,9)-(33,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (33,12)-(33,13))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── opening_loc: ∅
+ │ └── closing_loc: ∅
+ ├── @ MultiWriteNode (location: (35,0)-(35,11))
+ │ ├── lefts: (length: 1)
+ │ │ └── @ LocalVariableTargetNode (location: (35,0)-(35,3))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── rest:
+ │ │ @ ImplicitRestNode (location: (35,3)-(35,4))
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (35,5)-(35,6) = "="
+ │ └── value:
+ │ @ ArrayNode (location: (35,7)-(35,11))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (35,7)-(35,8))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (35,10)-(35,11))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── opening_loc: ∅
+ │ └── closing_loc: ∅
+ ├── @ MultiWriteNode (location: (37,0)-(37,16))
+ │ ├── lefts: (length: 1)
+ │ │ └── @ LocalVariableTargetNode (location: (37,0)-(37,3))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── rest:
+ │ │ @ SplatNode (location: (37,5)-(37,9))
+ │ │ ├── operator_loc: (37,5)-(37,6) = "*"
+ │ │ └── expression:
+ │ │ @ LocalVariableTargetNode (location: (37,6)-(37,9))
+ │ │ ├── name: :bar
+ │ │ └── depth: 0
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (37,10)-(37,11) = "="
+ │ └── value:
+ │ @ ArrayNode (location: (37,12)-(37,16))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (37,12)-(37,13))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (37,15)-(37,16))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── opening_loc: ∅
+ │ └── closing_loc: ∅
+ ├── @ MultiWriteNode (location: (39,0)-(39,27))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ LocalVariableTargetNode (location: (39,0)-(39,3))
+ │ │ │ ├── name: :foo
+ │ │ │ └── depth: 0
+ │ │ └── @ MultiTargetNode (location: (39,5)-(39,15))
+ │ │ ├── lefts: (length: 2)
+ │ │ │ ├── @ LocalVariableTargetNode (location: (39,6)-(39,9))
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ └── depth: 0
+ │ │ │ └── @ LocalVariableTargetNode (location: (39,11)-(39,14))
+ │ │ │ ├── name: :baz
+ │ │ │ └── depth: 0
+ │ │ ├── rest: ∅
+ │ │ ├── rights: (length: 0)
+ │ │ ├── lparen_loc: (39,5)-(39,6) = "("
+ │ │ └── rparen_loc: (39,14)-(39,15) = ")"
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (39,16)-(39,17) = "="
+ │ └── value:
+ │ @ ArrayNode (location: (39,18)-(39,27))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (39,18)-(39,19))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ ArrayNode (location: (39,21)-(39,27))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 2)
+ │ │ │ ├── @ IntegerNode (location: (39,22)-(39,23))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ └── @ IntegerNode (location: (39,25)-(39,26))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 3
+ │ │ ├── opening_loc: (39,21)-(39,22) = "["
+ │ │ └── closing_loc: (39,26)-(39,27) = "]"
+ │ ├── opening_loc: ∅
+ │ └── closing_loc: ∅
+ ├── @ LocalVariableWriteNode (location: (41,0)-(41,10))
+ │ ├── name: :foo
+ │ ├── depth: 0
+ │ ├── name_loc: (41,0)-(41,3) = "foo"
+ │ ├── value:
+ │ │ @ ArrayNode (location: (41,6)-(41,10))
+ │ │ ├── flags: contains_splat
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ SplatNode (location: (41,6)-(41,10))
+ │ │ │ ├── operator_loc: (41,6)-(41,7) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ LocalVariableReadNode (location: (41,7)-(41,10))
+ │ │ │ ├── name: :bar
+ │ │ │ └── depth: 0
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (41,4)-(41,5) = "="
+ ├── @ ConstantWriteNode (location: (43,0)-(43,10))
+ │ ├── name: :Foo
+ │ ├── name_loc: (43,0)-(43,3) = "Foo"
+ │ ├── value:
+ │ │ @ ArrayNode (location: (43,6)-(43,10))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 2)
+ │ │ │ ├── @ IntegerNode (location: (43,6)-(43,7))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── @ IntegerNode (location: (43,9)-(43,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (43,4)-(43,5) = "="
+ ├── @ ParenthesesNode (location: (45,0)-(45,9))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (45,1)-(45,8))
+ │ │ └── body: (length: 3)
+ │ │ ├── @ CallNode (location: (45,1)-(45,2))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (45,1)-(45,2) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── @ CallNode (location: (45,4)-(45,5))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (45,4)-(45,5) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ CallNode (location: (45,7)-(45,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :c
+ │ │ ├── message_loc: (45,7)-(45,8) = "c"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (45,0)-(45,1) = "("
+ │ └── closing_loc: (45,8)-(45,9) = ")"
+ └── @ MultiWriteNode (location: (47,0)-(47,17))
+ ├── lefts: (length: 3)
+ │ ├── @ LocalVariableTargetNode (location: (47,0)-(47,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── @ MultiTargetNode (location: (47,3)-(47,9))
+ │ │ ├── lefts: (length: 2)
+ │ │ │ ├── @ LocalVariableTargetNode (location: (47,4)-(47,5))
+ │ │ │ │ ├── name: :b
+ │ │ │ │ └── depth: 0
+ │ │ │ └── @ LocalVariableTargetNode (location: (47,7)-(47,8))
+ │ │ │ ├── name: :c
+ │ │ │ └── depth: 0
+ │ │ ├── rest: ∅
+ │ │ ├── rights: (length: 0)
+ │ │ ├── lparen_loc: (47,3)-(47,4) = "("
+ │ │ └── rparen_loc: (47,8)-(47,9) = ")"
+ │ └── @ LocalVariableTargetNode (location: (47,11)-(47,12))
+ │ ├── name: :d
+ │ └── depth: 0
+ ├── rest: ∅
+ ├── rights: (length: 0)
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (47,13)-(47,14) = "="
+ └── value:
+ @ ArrayNode (location: (47,15)-(47,17))
+ ├── flags: ∅
+ ├── elements: (length: 0)
+ ├── opening_loc: (47,15)-(47,16) = "["
+ └── closing_loc: (47,16)-(47,17) = "]"
diff --git a/test/prism/snapshots/while.txt b/test/prism/snapshots/while.txt
new file mode 100644
index 0000000000..a2972face0
--- /dev/null
+++ b/test/prism/snapshots/while.txt
@@ -0,0 +1,470 @@
+@ ProgramNode (location: (1,0)-(23,20))
+├── locals: [:baz]
+└── statements:
+ @ StatementsNode (location: (1,0)-(23,20))
+ └── body: (length: 12)
+ ├── @ WhileNode (location: (1,0)-(1,18))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (1,0)-(1,5) = "while"
+ │ ├── closing_loc: (1,15)-(1,18) = "end"
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (1,6)-(1,10))
+ │ └── statements:
+ │ @ StatementsNode (location: (1,12)-(1,13))
+ │ └── body: (length: 1)
+ │ └── @ IntegerNode (location: (1,12)-(1,13))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ WhileNode (location: (3,0)-(3,12))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (3,2)-(3,7) = "while"
+ │ ├── closing_loc: ∅
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (3,8)-(3,12))
+ │ └── statements:
+ │ @ StatementsNode (location: (3,0)-(3,1))
+ │ └── body: (length: 1)
+ │ └── @ IntegerNode (location: (3,0)-(3,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ CallNode (location: (5,0)-(5,24))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (5,0)-(5,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (5,4)-(5,24))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (5,6)-(5,22))
+ │ │ └── body: (length: 1)
+ │ │ └── @ WhileNode (location: (5,6)-(5,22))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (5,12)-(5,17) = "while"
+ │ │ ├── closing_loc: ∅
+ │ │ ├── predicate:
+ │ │ │ @ TrueNode (location: (5,18)-(5,22))
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (5,6)-(5,11))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (5,6)-(5,11))
+ │ │ ├── arguments: ∅
+ │ │ └── keyword_loc: (5,6)-(5,11) = "break"
+ │ ├── opening_loc: (5,4)-(5,5) = "{"
+ │ └── closing_loc: (5,23)-(5,24) = "}"
+ ├── @ CallNode (location: (7,0)-(7,23))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (7,0)-(7,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (7,4)-(7,23))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (7,6)-(7,21))
+ │ │ └── body: (length: 1)
+ │ │ └── @ WhileNode (location: (7,6)-(7,21))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (7,11)-(7,16) = "while"
+ │ │ ├── closing_loc: ∅
+ │ │ ├── predicate:
+ │ │ │ @ TrueNode (location: (7,17)-(7,21))
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (7,6)-(7,10))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NextNode (location: (7,6)-(7,10))
+ │ │ ├── arguments: ∅
+ │ │ └── keyword_loc: (7,6)-(7,10) = "next"
+ │ ├── opening_loc: (7,4)-(7,5) = "{"
+ │ └── closing_loc: (7,22)-(7,23) = "}"
+ ├── @ WhileNode (location: (9,0)-(9,17))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (9,7)-(9,12) = "while"
+ │ ├── closing_loc: ∅
+ │ ├── predicate:
+ │ │ @ TrueNode (location: (9,13)-(9,17))
+ │ └── statements:
+ │ @ StatementsNode (location: (9,0)-(9,6))
+ │ └── body: (length: 1)
+ │ └── @ ReturnNode (location: (9,0)-(9,6))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (9,0)-(9,6) = "return"
+ │ └── arguments: ∅
+ ├── @ WhileNode (location: (11,0)-(11,21))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (11,11)-(11,16) = "while"
+ │ ├── closing_loc: ∅
+ │ ├── predicate:
+ │ │ @ CallNode (location: (11,17)-(11,21))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar?
+ │ │ ├── message_loc: (11,17)-(11,21) = "bar?"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── statements:
+ │ @ StatementsNode (location: (11,0)-(11,10))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (11,0)-(11,10))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (11,0)-(11,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (11,4)-(11,10))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ SymbolNode (location: (11,4)-(11,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (11,4)-(11,5) = ":"
+ │ │ │ ├── value_loc: (11,5)-(11,6) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ └── @ SymbolNode (location: (11,8)-(11,10))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (11,8)-(11,9) = ":"
+ │ │ ├── value_loc: (11,9)-(11,10) = "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (13,0)-(13,58))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (13,0)-(13,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (13,4)-(13,58))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (13,6)-(13,56))
+ │ │ └── body: (length: 1)
+ │ │ └── @ WhileNode (location: (13,6)-(13,56))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (13,6)-(13,11) = "while"
+ │ │ ├── closing_loc: (13,53)-(13,56) = "end"
+ │ │ ├── predicate:
+ │ │ │ @ DefNode (location: (13,12)-(13,44))
+ │ │ │ ├── name: :foo
+ │ │ │ ├── name_loc: (13,21)-(13,24) = "foo"
+ │ │ │ ├── receiver:
+ │ │ │ │ @ SelfNode (location: (13,16)-(13,20))
+ │ │ │ ├── parameters:
+ │ │ │ │ @ ParametersNode (location: (13,25)-(13,39))
+ │ │ │ │ ├── requireds: (length: 0)
+ │ │ │ │ ├── optionals: (length: 1)
+ │ │ │ │ │ └── @ OptionalParameterNode (location: (13,25)-(13,39))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── name: :a
+ │ │ │ │ │ ├── name_loc: (13,25)-(13,26) = "a"
+ │ │ │ │ │ ├── operator_loc: (13,27)-(13,28) = "="
+ │ │ │ │ │ └── value:
+ │ │ │ │ │ @ CallNode (location: (13,29)-(13,39))
+ │ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :tap
+ │ │ │ │ │ ├── message_loc: (13,29)-(13,32) = "tap"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block:
+ │ │ │ │ │ @ BlockNode (location: (13,33)-(13,39))
+ │ │ │ │ │ ├── locals: []
+ │ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ │ ├── body: ∅
+ │ │ │ │ │ ├── opening_loc: (13,33)-(13,35) = "do"
+ │ │ │ │ │ └── closing_loc: (13,36)-(13,39) = "end"
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ ├── keywords: (length: 0)
+ │ │ │ │ ├── keyword_rest: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── body: ∅
+ │ │ │ ├── locals: [:a]
+ │ │ │ ├── def_keyword_loc: (13,12)-(13,15) = "def"
+ │ │ │ ├── operator_loc: (13,20)-(13,21) = "."
+ │ │ │ ├── lparen_loc: ∅
+ │ │ │ ├── rparen_loc: ∅
+ │ │ │ ├── equal_loc: ∅
+ │ │ │ └── end_keyword_loc: (13,41)-(13,44) = "end"
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (13,46)-(13,51))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (13,46)-(13,51))
+ │ │ ├── arguments: ∅
+ │ │ └── keyword_loc: (13,46)-(13,51) = "break"
+ │ ├── opening_loc: (13,4)-(13,5) = "{"
+ │ └── closing_loc: (13,57)-(13,58) = "}"
+ ├── @ CallNode (location: (15,0)-(15,55))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (15,0)-(15,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (15,4)-(15,55))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (15,6)-(15,53))
+ │ │ └── body: (length: 1)
+ │ │ └── @ WhileNode (location: (15,6)-(15,53))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (15,6)-(15,11) = "while"
+ │ │ ├── closing_loc: (15,50)-(15,53) = "end"
+ │ │ ├── predicate:
+ │ │ │ @ ClassNode (location: (15,12)-(15,41))
+ │ │ │ ├── locals: [:a]
+ │ │ │ ├── class_keyword_loc: (15,12)-(15,17) = "class"
+ │ │ │ ├── constant_path:
+ │ │ │ │ @ ConstantReadNode (location: (15,18)-(15,21))
+ │ │ │ │ └── name: :Foo
+ │ │ │ ├── inheritance_operator_loc: ∅
+ │ │ │ ├── superclass: ∅
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (15,22)-(15,36))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ LocalVariableWriteNode (location: (15,22)-(15,36))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── depth: 0
+ │ │ │ │ ├── name_loc: (15,22)-(15,23) = "a"
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ CallNode (location: (15,26)-(15,36))
+ │ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :tap
+ │ │ │ │ │ ├── message_loc: (15,26)-(15,29) = "tap"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block:
+ │ │ │ │ │ @ BlockNode (location: (15,30)-(15,36))
+ │ │ │ │ │ ├── locals: []
+ │ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ │ ├── body: ∅
+ │ │ │ │ │ ├── opening_loc: (15,30)-(15,32) = "do"
+ │ │ │ │ │ └── closing_loc: (15,33)-(15,36) = "end"
+ │ │ │ │ └── operator_loc: (15,24)-(15,25) = "="
+ │ │ │ ├── end_keyword_loc: (15,38)-(15,41) = "end"
+ │ │ │ └── name: :Foo
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (15,43)-(15,48))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (15,43)-(15,48))
+ │ │ ├── arguments: ∅
+ │ │ └── keyword_loc: (15,43)-(15,48) = "break"
+ │ ├── opening_loc: (15,4)-(15,5) = "{"
+ │ └── closing_loc: (15,54)-(15,55) = "}"
+ ├── @ CallNode (location: (17,0)-(17,56))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (17,0)-(17,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (17,4)-(17,56))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (17,6)-(17,54))
+ │ │ └── body: (length: 1)
+ │ │ └── @ WhileNode (location: (17,6)-(17,54))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (17,6)-(17,11) = "while"
+ │ │ ├── closing_loc: (17,51)-(17,54) = "end"
+ │ │ ├── predicate:
+ │ │ │ @ SingletonClassNode (location: (17,12)-(17,42))
+ │ │ │ ├── locals: []
+ │ │ │ ├── class_keyword_loc: (17,12)-(17,17) = "class"
+ │ │ │ ├── operator_loc: (17,18)-(17,20) = "<<"
+ │ │ │ ├── expression:
+ │ │ │ │ @ SelfNode (location: (17,21)-(17,25))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (17,27)-(17,37))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (17,27)-(17,37))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :tap
+ │ │ │ │ ├── message_loc: (17,27)-(17,30) = "tap"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block:
+ │ │ │ │ @ BlockNode (location: (17,31)-(17,37))
+ │ │ │ │ ├── locals: []
+ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ ├── body: ∅
+ │ │ │ │ ├── opening_loc: (17,31)-(17,33) = "do"
+ │ │ │ │ └── closing_loc: (17,34)-(17,37) = "end"
+ │ │ │ └── end_keyword_loc: (17,39)-(17,42) = "end"
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (17,44)-(17,49))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (17,44)-(17,49))
+ │ │ ├── arguments: ∅
+ │ │ └── keyword_loc: (17,44)-(17,49) = "break"
+ │ ├── opening_loc: (17,4)-(17,5) = "{"
+ │ └── closing_loc: (17,55)-(17,56) = "}"
+ ├── @ CallNode (location: (19,0)-(19,60))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (19,0)-(19,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (19,4)-(19,60))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (19,6)-(19,58))
+ │ │ └── body: (length: 1)
+ │ │ └── @ WhileNode (location: (19,6)-(19,58))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (19,6)-(19,11) = "while"
+ │ │ ├── closing_loc: (19,55)-(19,58) = "end"
+ │ │ ├── predicate:
+ │ │ │ @ SingletonClassNode (location: (19,12)-(19,46))
+ │ │ │ ├── locals: [:a]
+ │ │ │ ├── class_keyword_loc: (19,12)-(19,17) = "class"
+ │ │ │ ├── operator_loc: (19,18)-(19,20) = "<<"
+ │ │ │ ├── expression:
+ │ │ │ │ @ SelfNode (location: (19,21)-(19,25))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (19,27)-(19,41))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ LocalVariableWriteNode (location: (19,27)-(19,41))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── depth: 0
+ │ │ │ │ ├── name_loc: (19,27)-(19,28) = "a"
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ CallNode (location: (19,31)-(19,41))
+ │ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :tap
+ │ │ │ │ │ ├── message_loc: (19,31)-(19,34) = "tap"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block:
+ │ │ │ │ │ @ BlockNode (location: (19,35)-(19,41))
+ │ │ │ │ │ ├── locals: []
+ │ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ │ ├── body: ∅
+ │ │ │ │ │ ├── opening_loc: (19,35)-(19,37) = "do"
+ │ │ │ │ │ └── closing_loc: (19,38)-(19,41) = "end"
+ │ │ │ │ └── operator_loc: (19,29)-(19,30) = "="
+ │ │ │ └── end_keyword_loc: (19,43)-(19,46) = "end"
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (19,48)-(19,53))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (19,48)-(19,53))
+ │ │ ├── arguments: ∅
+ │ │ └── keyword_loc: (19,48)-(19,53) = "break"
+ │ ├── opening_loc: (19,4)-(19,5) = "{"
+ │ └── closing_loc: (19,59)-(19,60) = "}"
+ ├── @ WhileNode (location: (21,0)-(21,31))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (21,0)-(21,5) = "while"
+ │ ├── closing_loc: (21,28)-(21,31) = "end"
+ │ ├── predicate:
+ │ │ @ DefNode (location: (21,6)-(21,26))
+ │ │ ├── name: :foo
+ │ │ ├── name_loc: (21,10)-(21,13) = "foo"
+ │ │ ├── receiver: ∅
+ │ │ ├── parameters: ∅
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (21,16)-(21,26))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (21,16)-(21,26))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (21,16)-(21,19) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (21,20)-(21,26))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body: ∅
+ │ │ │ ├── opening_loc: (21,20)-(21,22) = "do"
+ │ │ │ └── closing_loc: (21,23)-(21,26) = "end"
+ │ │ ├── locals: []
+ │ │ ├── def_keyword_loc: (21,6)-(21,9) = "def"
+ │ │ ├── operator_loc: ∅
+ │ │ ├── lparen_loc: ∅
+ │ │ ├── rparen_loc: ∅
+ │ │ ├── equal_loc: (21,14)-(21,15) = "="
+ │ │ └── end_keyword_loc: ∅
+ │ └── statements: ∅
+ └── @ WhileNode (location: (23,0)-(23,20))
+ ├── flags: ∅
+ ├── keyword_loc: (23,4)-(23,9) = "while"
+ ├── closing_loc: ∅
+ ├── predicate:
+ │ @ MatchPredicateNode (location: (23,10)-(23,20))
+ │ ├── value:
+ │ │ @ CallNode (location: (23,10)-(23,13))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (23,10)-(23,13) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── pattern:
+ │ │ @ LocalVariableTargetNode (location: (23,17)-(23,20))
+ │ │ ├── name: :baz
+ │ │ └── depth: 0
+ │ └── operator_loc: (23,14)-(23,16) = "in"
+ └── statements:
+ @ StatementsNode (location: (23,0)-(23,3))
+ └── body: (length: 1)
+ └── @ CallNode (location: (23,0)-(23,3))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :foo
+ ├── message_loc: (23,0)-(23,3) = "foo"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/__ENCODING__.txt b/test/prism/snapshots/whitequark/__ENCODING__.txt
new file mode 100644
index 0000000000..1b223bd8fe
--- /dev/null
+++ b/test/prism/snapshots/whitequark/__ENCODING__.txt
@@ -0,0 +1,6 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ SourceEncodingNode (location: (1,0)-(1,12))
diff --git a/test/prism/snapshots/whitequark/__ENCODING___legacy_.txt b/test/prism/snapshots/whitequark/__ENCODING___legacy_.txt
new file mode 100644
index 0000000000..1b223bd8fe
--- /dev/null
+++ b/test/prism/snapshots/whitequark/__ENCODING___legacy_.txt
@@ -0,0 +1,6 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ SourceEncodingNode (location: (1,0)-(1,12))
diff --git a/test/prism/snapshots/whitequark/alias.txt b/test/prism/snapshots/whitequark/alias.txt
new file mode 100644
index 0000000000..509ea2b633
--- /dev/null
+++ b/test/prism/snapshots/whitequark/alias.txt
@@ -0,0 +1,21 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ AliasMethodNode (location: (1,0)-(1,14))
+ ├── new_name:
+ │ @ SymbolNode (location: (1,6)-(1,10))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,6)-(1,7) = ":"
+ │ ├── value_loc: (1,7)-(1,10) = "foo"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "foo"
+ ├── old_name:
+ │ @ SymbolNode (location: (1,11)-(1,14))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: ∅
+ │ ├── value_loc: (1,11)-(1,14) = "bar"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "bar"
+ └── keyword_loc: (1,0)-(1,5) = "alias"
diff --git a/test/prism/snapshots/whitequark/alias_gvar.txt b/test/prism/snapshots/whitequark/alias_gvar.txt
new file mode 100644
index 0000000000..d13f816555
--- /dev/null
+++ b/test/prism/snapshots/whitequark/alias_gvar.txt
@@ -0,0 +1,21 @@
+@ ProgramNode (location: (1,0)-(3,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,11))
+ └── body: (length: 2)
+ ├── @ AliasGlobalVariableNode (location: (1,0)-(1,11))
+ │ ├── new_name:
+ │ │ @ GlobalVariableReadNode (location: (1,6)-(1,8))
+ │ │ └── name: :$a
+ │ ├── old_name:
+ │ │ @ BackReferenceReadNode (location: (1,9)-(1,11))
+ │ │ └── name: :$+
+ │ └── keyword_loc: (1,0)-(1,5) = "alias"
+ └── @ AliasGlobalVariableNode (location: (3,0)-(3,11))
+ ├── new_name:
+ │ @ GlobalVariableReadNode (location: (3,6)-(3,8))
+ │ └── name: :$a
+ ├── old_name:
+ │ @ GlobalVariableReadNode (location: (3,9)-(3,11))
+ │ └── name: :$b
+ └── keyword_loc: (3,0)-(3,5) = "alias"
diff --git a/test/prism/snapshots/whitequark/ambiuous_quoted_label_in_ternary_operator.txt b/test/prism/snapshots/whitequark/ambiuous_quoted_label_in_ternary_operator.txt
new file mode 100644
index 0000000000..c6a5c14934
--- /dev/null
+++ b/test/prism/snapshots/whitequark/ambiuous_quoted_label_in_ternary_operator.txt
@@ -0,0 +1,60 @@
+@ ProgramNode (location: (1,0)-(1,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,15))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(1,15))
+ ├── if_keyword_loc: ∅
+ ├── predicate:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── then_keyword_loc: (1,2)-(1,3) = "?"
+ ├── statements:
+ │ @ StatementsNode (location: (1,4)-(1,10))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,4)-(1,10))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,4)-(1,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (1,4)-(1,5) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :&
+ │ ├── message_loc: (1,6)-(1,7) = "&"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,8)-(1,10))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (1,8)-(1,10))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,8)-(1,9) = "'"
+ │ │ ├── content_loc: (1,9)-(1,9) = ""
+ │ │ ├── closing_loc: (1,9)-(1,10) = "'"
+ │ │ └── unescaped: ""
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── consequent:
+ │ @ ElseNode (location: (1,10)-(1,15))
+ │ ├── else_keyword_loc: (1,10)-(1,11) = ":"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,12)-(1,15))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NilNode (location: (1,12)-(1,15))
+ │ └── end_keyword_loc: ∅
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/whitequark/and.txt b/test/prism/snapshots/whitequark/and.txt
new file mode 100644
index 0000000000..d2e1b33c50
--- /dev/null
+++ b/test/prism/snapshots/whitequark/and.txt
@@ -0,0 +1,53 @@
+@ ProgramNode (location: (1,0)-(3,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,11))
+ └── body: (length: 2)
+ ├── @ AndNode (location: (1,0)-(1,10))
+ │ ├── left:
+ │ │ @ CallNode (location: (1,0)-(1,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right:
+ │ │ @ CallNode (location: (1,7)-(1,10))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,7)-(1,10) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (1,4)-(1,6) = "&&"
+ └── @ AndNode (location: (3,0)-(3,11))
+ ├── left:
+ │ @ CallNode (location: (3,0)-(3,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (3,0)-(3,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── right:
+ │ @ CallNode (location: (3,8)-(3,11))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (3,8)-(3,11) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── operator_loc: (3,4)-(3,7) = "and"
diff --git a/test/prism/snapshots/whitequark/and_asgn.txt b/test/prism/snapshots/whitequark/and_asgn.txt
new file mode 100644
index 0000000000..5c7a9a2319
--- /dev/null
+++ b/test/prism/snapshots/whitequark/and_asgn.txt
@@ -0,0 +1,59 @@
+@ ProgramNode (location: (1,0)-(3,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,15))
+ └── body: (length: 2)
+ ├── @ CallAndWriteNode (location: (1,0)-(1,11))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,0)-(1,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (1,3)-(1,4) = "."
+ │ ├── message_loc: (1,4)-(1,5) = "a"
+ │ ├── read_name: :a
+ │ ├── write_name: :a=
+ │ ├── operator_loc: (1,6)-(1,9) = "&&="
+ │ └── value:
+ │ @ IntegerNode (location: (1,10)-(1,11))
+ │ ├── flags: decimal
+ │ └── value: 1
+ └── @ IndexAndWriteNode (location: (3,0)-(3,15))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (3,0)-(3,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (3,0)-(3,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── opening_loc: (3,3)-(3,4) = "["
+ ├── arguments:
+ │ @ ArgumentsNode (location: (3,4)-(3,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ IntegerNode (location: (3,4)-(3,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 0
+ │ └── @ IntegerNode (location: (3,7)-(3,8))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: (3,8)-(3,9) = "]"
+ ├── block: ∅
+ ├── operator_loc: (3,10)-(3,13) = "&&="
+ └── value:
+ @ IntegerNode (location: (3,14)-(3,15))
+ ├── flags: decimal
+ └── value: 2
diff --git a/test/prism/snapshots/whitequark/and_or_masgn.txt b/test/prism/snapshots/whitequark/and_or_masgn.txt
new file mode 100644
index 0000000000..05aff040cd
--- /dev/null
+++ b/test/prism/snapshots/whitequark/and_or_masgn.txt
@@ -0,0 +1,93 @@
+@ ProgramNode (location: (1,0)-(3,19))
+├── locals: [:a, :b]
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,19))
+ └── body: (length: 2)
+ ├── @ AndNode (location: (1,0)-(1,19))
+ │ ├── left:
+ │ │ @ CallNode (location: (1,0)-(1,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right:
+ │ │ @ ParenthesesNode (location: (1,7)-(1,19))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (1,8)-(1,18))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ MultiWriteNode (location: (1,8)-(1,18))
+ │ │ │ ├── lefts: (length: 2)
+ │ │ │ │ ├── @ LocalVariableTargetNode (location: (1,8)-(1,9))
+ │ │ │ │ │ ├── name: :a
+ │ │ │ │ │ └── depth: 0
+ │ │ │ │ └── @ LocalVariableTargetNode (location: (1,11)-(1,12))
+ │ │ │ │ ├── name: :b
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: ∅
+ │ │ │ ├── rparen_loc: ∅
+ │ │ │ ├── operator_loc: (1,13)-(1,14) = "="
+ │ │ │ └── value:
+ │ │ │ @ CallNode (location: (1,15)-(1,18))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (1,15)-(1,18) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (1,7)-(1,8) = "("
+ │ │ └── closing_loc: (1,18)-(1,19) = ")"
+ │ └── operator_loc: (1,4)-(1,6) = "&&"
+ └── @ OrNode (location: (3,0)-(3,19))
+ ├── left:
+ │ @ CallNode (location: (3,0)-(3,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (3,0)-(3,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── right:
+ │ @ ParenthesesNode (location: (3,7)-(3,19))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (3,8)-(3,18))
+ │ │ └── body: (length: 1)
+ │ │ └── @ MultiWriteNode (location: (3,8)-(3,18))
+ │ │ ├── lefts: (length: 2)
+ │ │ │ ├── @ LocalVariableTargetNode (location: (3,8)-(3,9))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ └── depth: 0
+ │ │ │ └── @ LocalVariableTargetNode (location: (3,11)-(3,12))
+ │ │ │ ├── name: :b
+ │ │ │ └── depth: 0
+ │ │ ├── rest: ∅
+ │ │ ├── rights: (length: 0)
+ │ │ ├── lparen_loc: ∅
+ │ │ ├── rparen_loc: ∅
+ │ │ ├── operator_loc: (3,13)-(3,14) = "="
+ │ │ └── value:
+ │ │ @ CallNode (location: (3,15)-(3,18))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (3,15)-(3,18) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (3,7)-(3,8) = "("
+ │ └── closing_loc: (3,18)-(3,19) = ")"
+ └── operator_loc: (3,4)-(3,6) = "||"
diff --git a/test/prism/snapshots/whitequark/anonymous_blockarg.txt b/test/prism/snapshots/whitequark/anonymous_blockarg.txt
new file mode 100644
index 0000000000..92cf1504a9
--- /dev/null
+++ b/test/prism/snapshots/whitequark/anonymous_blockarg.txt
@@ -0,0 +1,46 @@
+@ ProgramNode (location: (1,0)-(1,23))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,23))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,23))
+ ├── name: :foo
+ ├── name_loc: (1,4)-(1,7) = "foo"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,8)-(1,9))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block:
+ │ @ BlockParameterNode (location: (1,8)-(1,9))
+ │ ├── flags: ∅
+ │ ├── name: ∅
+ │ ├── name_loc: ∅
+ │ └── operator_loc: (1,8)-(1,9) = "&"
+ ├── body:
+ │ @ StatementsNode (location: (1,12)-(1,17))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,12)-(1,17))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (1,12)-(1,15) = "bar"
+ │ ├── opening_loc: (1,15)-(1,16) = "("
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (1,17)-(1,18) = ")"
+ │ └── block:
+ │ @ BlockArgumentNode (location: (1,16)-(1,17))
+ │ ├── expression: ∅
+ │ └── operator_loc: (1,16)-(1,17) = "&"
+ ├── locals: []
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,7)-(1,8) = "("
+ ├── rparen_loc: (1,9)-(1,10) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,20)-(1,23) = "end"
diff --git a/test/prism/snapshots/whitequark/arg.txt b/test/prism/snapshots/whitequark/arg.txt
new file mode 100644
index 0000000000..38fdb190f3
--- /dev/null
+++ b/test/prism/snapshots/whitequark/arg.txt
@@ -0,0 +1,56 @@
+@ ProgramNode (location: (1,0)-(3,20))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,20))
+ └── body: (length: 2)
+ ├── @ DefNode (location: (1,0)-(1,15))
+ │ ├── name: :f
+ │ ├── name_loc: (1,4)-(1,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,6)-(1,9))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,6)-(1,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :foo
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:foo]
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (1,5)-(1,6) = "("
+ │ ├── rparen_loc: (1,9)-(1,10) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (1,12)-(1,15) = "end"
+ └── @ DefNode (location: (3,0)-(3,20))
+ ├── name: :f
+ ├── name_loc: (3,4)-(3,5) = "f"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (3,6)-(3,14))
+ │ ├── requireds: (length: 2)
+ │ │ ├── @ RequiredParameterNode (location: (3,6)-(3,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :foo
+ │ │ └── @ RequiredParameterNode (location: (3,11)-(3,14))
+ │ │ ├── flags: ∅
+ │ │ └── name: :bar
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:foo, :bar]
+ ├── def_keyword_loc: (3,0)-(3,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (3,5)-(3,6) = "("
+ ├── rparen_loc: (3,14)-(3,15) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (3,17)-(3,20) = "end"
diff --git a/test/prism/snapshots/whitequark/arg_duplicate_ignored.txt b/test/prism/snapshots/whitequark/arg_duplicate_ignored.txt
new file mode 100644
index 0000000000..21dc1b7417
--- /dev/null
+++ b/test/prism/snapshots/whitequark/arg_duplicate_ignored.txt
@@ -0,0 +1,59 @@
+@ ProgramNode (location: (1,0)-(3,20))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,20))
+ └── body: (length: 2)
+ ├── @ DefNode (location: (1,0)-(1,18))
+ │ ├── name: :foo
+ │ ├── name_loc: (1,4)-(1,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,8)-(1,12))
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ RequiredParameterNode (location: (1,8)-(1,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :_
+ │ │ │ └── @ RequiredParameterNode (location: (1,11)-(1,12))
+ │ │ │ ├── flags: repeated_parameter
+ │ │ │ └── name: :_
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:_]
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (1,7)-(1,8) = "("
+ │ ├── rparen_loc: (1,12)-(1,13) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (1,15)-(1,18) = "end"
+ └── @ DefNode (location: (3,0)-(3,20))
+ ├── name: :foo
+ ├── name_loc: (3,4)-(3,7) = "foo"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (3,8)-(3,14))
+ │ ├── requireds: (length: 2)
+ │ │ ├── @ RequiredParameterNode (location: (3,8)-(3,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :_a
+ │ │ └── @ RequiredParameterNode (location: (3,12)-(3,14))
+ │ │ ├── flags: repeated_parameter
+ │ │ └── name: :_a
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:_a]
+ ├── def_keyword_loc: (3,0)-(3,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (3,7)-(3,8) = "("
+ ├── rparen_loc: (3,14)-(3,15) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (3,17)-(3,20) = "end"
diff --git a/test/prism/snapshots/whitequark/arg_label.txt b/test/prism/snapshots/whitequark/arg_label.txt
new file mode 100644
index 0000000000..b72ea59a5d
--- /dev/null
+++ b/test/prism/snapshots/whitequark/arg_label.txt
@@ -0,0 +1,115 @@
+@ ProgramNode (location: (1,0)-(6,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(6,12))
+ └── body: (length: 3)
+ ├── @ DefNode (location: (1,0)-(2,8))
+ │ ├── name: :foo
+ │ ├── name_loc: (1,4)-(1,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (2,1)-(2,4))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (2,1)-(2,4))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (2,1)-(2,2) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (2,2)-(2,4))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (2,2)-(2,4))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (2,2)-(2,3) = ":"
+ │ │ │ ├── value_loc: (2,3)-(2,4) = "b"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (2,5)-(2,8) = "end"
+ ├── @ DefNode (location: (4,0)-(4,17))
+ │ ├── name: :foo
+ │ ├── name_loc: (4,4)-(4,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (4,10)-(4,13))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (4,10)-(4,13))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (4,10)-(4,11) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (4,11)-(4,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (4,11)-(4,13))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (4,11)-(4,12) = ":"
+ │ │ │ ├── value_loc: (4,12)-(4,13) = "b"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (4,0)-(4,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (4,7)-(4,8) = "("
+ │ ├── rparen_loc: (4,8)-(4,9) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (4,14)-(4,17) = "end"
+ └── @ CallNode (location: (6,0)-(6,12))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (6,0)-(6,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (6,2)-(6,12))
+ ├── locals: []
+ ├── parameters:
+ │ @ BlockParametersNode (location: (6,4)-(6,6))
+ │ ├── parameters: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (6,4)-(6,5) = "|"
+ │ └── closing_loc: (6,5)-(6,6) = "|"
+ ├── body:
+ │ @ StatementsNode (location: (6,7)-(6,10))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (6,7)-(6,10))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (6,7)-(6,8) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (6,8)-(6,10))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ SymbolNode (location: (6,8)-(6,10))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (6,8)-(6,9) = ":"
+ │ │ ├── value_loc: (6,9)-(6,10) = "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── opening_loc: (6,2)-(6,3) = "{"
+ └── closing_loc: (6,11)-(6,12) = "}"
diff --git a/test/prism/snapshots/whitequark/arg_scope.txt b/test/prism/snapshots/whitequark/arg_scope.txt
new file mode 100644
index 0000000000..c04356b8ee
--- /dev/null
+++ b/test/prism/snapshots/whitequark/arg_scope.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,13))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :lambda
+ ├── message_loc: (1,0)-(1,6) = "lambda"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,6)-(1,13))
+ ├── locals: [:a]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,7)-(1,11))
+ │ ├── parameters: ∅
+ │ ├── locals: (length: 1)
+ │ │ └── @ BlockLocalVariableNode (location: (1,9)-(1,10))
+ │ │ ├── flags: ∅
+ │ │ └── name: :a
+ │ ├── opening_loc: (1,7)-(1,8) = "|"
+ │ └── closing_loc: (1,10)-(1,11) = "|"
+ ├── body:
+ │ @ StatementsNode (location: (1,11)-(1,12))
+ │ └── body: (length: 1)
+ │ └── @ LocalVariableReadNode (location: (1,11)-(1,12))
+ │ ├── name: :a
+ │ └── depth: 0
+ ├── opening_loc: (1,6)-(1,7) = "{"
+ └── closing_loc: (1,12)-(1,13) = "}"
diff --git a/test/prism/snapshots/whitequark/args.txt b/test/prism/snapshots/whitequark/args.txt
new file mode 100644
index 0000000000..bc7ea8ad7d
--- /dev/null
+++ b/test/prism/snapshots/whitequark/args.txt
@@ -0,0 +1,1075 @@
+@ ProgramNode (location: (1,0)-(63,21))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(63,21))
+ └── body: (length: 31)
+ ├── @ DefNode (location: (1,0)-(1,13))
+ │ ├── name: :f
+ │ ├── name_loc: (1,4)-(1,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,6)-(1,8))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (1,6)-(1,8))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (1,7)-(1,8) = "b"
+ │ │ └── operator_loc: (1,6)-(1,7) = "&"
+ │ ├── body: ∅
+ │ ├── locals: [:b]
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (1,10)-(1,13) = "end"
+ ├── @ DefNode (location: (3,0)-(3,18))
+ │ ├── name: :f
+ │ ├── name_loc: (3,4)-(3,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (3,7)-(3,12))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ MultiTargetNode (location: (3,7)-(3,12))
+ │ │ │ ├── lefts: (length: 1)
+ │ │ │ │ └── @ MultiTargetNode (location: (3,8)-(3,11))
+ │ │ │ │ ├── lefts: (length: 1)
+ │ │ │ │ │ └── @ RequiredParameterNode (location: (3,9)-(3,10))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :a
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── rights: (length: 0)
+ │ │ │ │ ├── lparen_loc: (3,8)-(3,9) = "("
+ │ │ │ │ └── rparen_loc: (3,10)-(3,11) = ")"
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: (3,7)-(3,8) = "("
+ │ │ │ └── rparen_loc: (3,11)-(3,12) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:a]
+ │ ├── def_keyword_loc: (3,0)-(3,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (3,6)-(3,7) = "("
+ │ ├── rparen_loc: (3,12)-(3,13) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (3,15)-(3,18) = "end"
+ ├── @ DefNode (location: (5,0)-(5,16))
+ │ ├── name: :f
+ │ ├── name_loc: (5,4)-(5,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (5,7)-(5,10))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ MultiTargetNode (location: (5,7)-(5,10))
+ │ │ │ ├── lefts: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (5,8)-(5,9))
+ │ │ │ │ ├── operator_loc: (5,8)-(5,9) = "*"
+ │ │ │ │ └── expression: ∅
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: (5,7)-(5,8) = "("
+ │ │ │ └── rparen_loc: (5,9)-(5,10) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (5,0)-(5,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (5,6)-(5,7) = "("
+ │ ├── rparen_loc: (5,10)-(5,11) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (5,13)-(5,16) = "end"
+ ├── @ DefNode (location: (7,0)-(7,19))
+ │ ├── name: :f
+ │ ├── name_loc: (7,4)-(7,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (7,7)-(7,13))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ MultiTargetNode (location: (7,7)-(7,13))
+ │ │ │ ├── lefts: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (7,8)-(7,9))
+ │ │ │ │ ├── operator_loc: (7,8)-(7,9) = "*"
+ │ │ │ │ └── expression: ∅
+ │ │ │ ├── rights: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (7,11)-(7,12))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :p
+ │ │ │ ├── lparen_loc: (7,7)-(7,8) = "("
+ │ │ │ └── rparen_loc: (7,12)-(7,13) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:p]
+ │ ├── def_keyword_loc: (7,0)-(7,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (7,6)-(7,7) = "("
+ │ ├── rparen_loc: (7,13)-(7,14) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (7,16)-(7,19) = "end"
+ ├── @ DefNode (location: (9,0)-(9,17))
+ │ ├── name: :f
+ │ ├── name_loc: (9,4)-(9,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (9,7)-(9,11))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ MultiTargetNode (location: (9,7)-(9,11))
+ │ │ │ ├── lefts: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (9,8)-(9,10))
+ │ │ │ │ ├── operator_loc: (9,8)-(9,9) = "*"
+ │ │ │ │ └── expression:
+ │ │ │ │ @ RequiredParameterNode (location: (9,9)-(9,10))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :r
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: (9,7)-(9,8) = "("
+ │ │ │ └── rparen_loc: (9,10)-(9,11) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:r]
+ │ ├── def_keyword_loc: (9,0)-(9,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (9,6)-(9,7) = "("
+ │ ├── rparen_loc: (9,11)-(9,12) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (9,14)-(9,17) = "end"
+ ├── @ DefNode (location: (11,0)-(11,20))
+ │ ├── name: :f
+ │ ├── name_loc: (11,4)-(11,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (11,7)-(11,14))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ MultiTargetNode (location: (11,7)-(11,14))
+ │ │ │ ├── lefts: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (11,8)-(11,10))
+ │ │ │ │ ├── operator_loc: (11,8)-(11,9) = "*"
+ │ │ │ │ └── expression:
+ │ │ │ │ @ RequiredParameterNode (location: (11,9)-(11,10))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :r
+ │ │ │ ├── rights: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (11,12)-(11,13))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :p
+ │ │ │ ├── lparen_loc: (11,7)-(11,8) = "("
+ │ │ │ └── rparen_loc: (11,13)-(11,14) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:r, :p]
+ │ ├── def_keyword_loc: (11,0)-(11,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (11,6)-(11,7) = "("
+ │ ├── rparen_loc: (11,14)-(11,15) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (11,17)-(11,20) = "end"
+ ├── @ DefNode (location: (13,0)-(13,19))
+ │ ├── name: :f
+ │ ├── name_loc: (13,4)-(13,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (13,7)-(13,13))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ MultiTargetNode (location: (13,7)-(13,13))
+ │ │ │ ├── lefts: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (13,8)-(13,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (13,11)-(13,12))
+ │ │ │ │ ├── operator_loc: (13,11)-(13,12) = "*"
+ │ │ │ │ └── expression: ∅
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: (13,7)-(13,8) = "("
+ │ │ │ └── rparen_loc: (13,12)-(13,13) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:a]
+ │ ├── def_keyword_loc: (13,0)-(13,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (13,6)-(13,7) = "("
+ │ ├── rparen_loc: (13,13)-(13,14) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (13,16)-(13,19) = "end"
+ ├── @ DefNode (location: (15,0)-(15,22))
+ │ ├── name: :f
+ │ ├── name_loc: (15,4)-(15,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (15,7)-(15,16))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ MultiTargetNode (location: (15,7)-(15,16))
+ │ │ │ ├── lefts: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (15,8)-(15,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (15,11)-(15,12))
+ │ │ │ │ ├── operator_loc: (15,11)-(15,12) = "*"
+ │ │ │ │ └── expression: ∅
+ │ │ │ ├── rights: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (15,14)-(15,15))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :p
+ │ │ │ ├── lparen_loc: (15,7)-(15,8) = "("
+ │ │ │ └── rparen_loc: (15,15)-(15,16) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:a, :p]
+ │ ├── def_keyword_loc: (15,0)-(15,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (15,6)-(15,7) = "("
+ │ ├── rparen_loc: (15,16)-(15,17) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (15,19)-(15,22) = "end"
+ ├── @ DefNode (location: (17,0)-(17,20))
+ │ ├── name: :f
+ │ ├── name_loc: (17,4)-(17,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (17,7)-(17,14))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ MultiTargetNode (location: (17,7)-(17,14))
+ │ │ │ ├── lefts: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (17,8)-(17,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (17,11)-(17,13))
+ │ │ │ │ ├── operator_loc: (17,11)-(17,12) = "*"
+ │ │ │ │ └── expression:
+ │ │ │ │ @ RequiredParameterNode (location: (17,12)-(17,13))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :r
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: (17,7)-(17,8) = "("
+ │ │ │ └── rparen_loc: (17,13)-(17,14) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:a, :r]
+ │ ├── def_keyword_loc: (17,0)-(17,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (17,6)-(17,7) = "("
+ │ ├── rparen_loc: (17,14)-(17,15) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (17,17)-(17,20) = "end"
+ ├── @ DefNode (location: (19,0)-(19,23))
+ │ ├── name: :f
+ │ ├── name_loc: (19,4)-(19,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (19,7)-(19,17))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ MultiTargetNode (location: (19,7)-(19,17))
+ │ │ │ ├── lefts: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (19,8)-(19,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── rest:
+ │ │ │ │ @ SplatNode (location: (19,11)-(19,13))
+ │ │ │ │ ├── operator_loc: (19,11)-(19,12) = "*"
+ │ │ │ │ └── expression:
+ │ │ │ │ @ RequiredParameterNode (location: (19,12)-(19,13))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :r
+ │ │ │ ├── rights: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (19,15)-(19,16))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :p
+ │ │ │ ├── lparen_loc: (19,7)-(19,8) = "("
+ │ │ │ └── rparen_loc: (19,16)-(19,17) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:a, :r, :p]
+ │ ├── def_keyword_loc: (19,0)-(19,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (19,6)-(19,7) = "("
+ │ ├── rparen_loc: (19,17)-(19,18) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (19,20)-(19,23) = "end"
+ ├── @ DefNode (location: (21,0)-(21,20))
+ │ ├── name: :f
+ │ ├── name_loc: (21,4)-(21,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (21,7)-(21,14))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ MultiTargetNode (location: (21,7)-(21,14))
+ │ │ │ ├── lefts: (length: 2)
+ │ │ │ │ ├── @ RequiredParameterNode (location: (21,8)-(21,9))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :a
+ │ │ │ │ └── @ RequiredParameterNode (location: (21,11)-(21,13))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a1
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── rights: (length: 0)
+ │ │ │ ├── lparen_loc: (21,7)-(21,8) = "("
+ │ │ │ └── rparen_loc: (21,13)-(21,14) = ")"
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:a, :a1]
+ │ ├── def_keyword_loc: (21,0)-(21,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (21,6)-(21,7) = "("
+ │ ├── rparen_loc: (21,14)-(21,15) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (21,17)-(21,20) = "end"
+ ├── @ DefNode (location: (23,0)-(23,23))
+ │ ├── name: :f
+ │ ├── name_loc: (23,4)-(23,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (23,7)-(23,17))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ OptionalKeywordParameterNode (location: (23,7)-(23,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── name_loc: (23,7)-(23,11) = "foo:"
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (23,12)-(23,13))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (23,15)-(23,17))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (23,16)-(23,17) = "b"
+ │ │ └── operator_loc: (23,15)-(23,16) = "&"
+ │ ├── body: ∅
+ │ ├── locals: [:foo, :b]
+ │ ├── def_keyword_loc: (23,0)-(23,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (23,6)-(23,7) = "("
+ │ ├── rparen_loc: (23,17)-(23,18) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (23,20)-(23,23) = "end"
+ ├── @ DefNode (location: (25,0)-(25,38))
+ │ ├── name: :f
+ │ ├── name_loc: (25,4)-(25,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (25,7)-(25,32))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 2)
+ │ │ │ ├── @ OptionalKeywordParameterNode (location: (25,7)-(25,13))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :foo
+ │ │ │ │ ├── name_loc: (25,7)-(25,11) = "foo:"
+ │ │ │ │ └── value:
+ │ │ │ │ @ IntegerNode (location: (25,12)-(25,13))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── @ OptionalKeywordParameterNode (location: (25,15)-(25,21))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── name_loc: (25,15)-(25,19) = "bar:"
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (25,20)-(25,21))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── keyword_rest:
+ │ │ │ @ KeywordRestParameterNode (location: (25,23)-(25,28))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── name_loc: (25,25)-(25,28) = "baz"
+ │ │ │ └── operator_loc: (25,23)-(25,25) = "**"
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (25,30)-(25,32))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (25,31)-(25,32) = "b"
+ │ │ └── operator_loc: (25,30)-(25,31) = "&"
+ │ ├── body: ∅
+ │ ├── locals: [:foo, :bar, :baz, :b]
+ │ ├── def_keyword_loc: (25,0)-(25,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (25,6)-(25,7) = "("
+ │ ├── rparen_loc: (25,32)-(25,33) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (25,35)-(25,38) = "end"
+ ├── @ DefNode (location: (27,0)-(27,20))
+ │ ├── name: :f
+ │ ├── name_loc: (27,4)-(27,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (27,6)-(27,15))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ KeywordRestParameterNode (location: (27,6)-(27,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── name_loc: (27,8)-(27,11) = "baz"
+ │ │ │ └── operator_loc: (27,6)-(27,8) = "**"
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (27,13)-(27,15))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (27,14)-(27,15) = "b"
+ │ │ └── operator_loc: (27,13)-(27,14) = "&"
+ │ ├── body: ∅
+ │ ├── locals: [:baz, :b]
+ │ ├── def_keyword_loc: (27,0)-(27,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (27,17)-(27,20) = "end"
+ ├── @ DefNode (location: (29,0)-(29,16))
+ │ ├── name: :f
+ │ ├── name_loc: (29,4)-(29,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (29,6)-(29,11))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (29,6)-(29,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: ∅
+ │ │ │ ├── name_loc: ∅
+ │ │ │ └── operator_loc: (29,6)-(29,7) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ KeywordRestParameterNode (location: (29,9)-(29,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: ∅
+ │ │ │ ├── name_loc: ∅
+ │ │ │ └── operator_loc: (29,9)-(29,11) = "**"
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (29,0)-(29,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (29,13)-(29,16) = "end"
+ ├── @ DefNode (location: (31,0)-(31,17))
+ │ ├── name: :f
+ │ ├── name_loc: (31,4)-(31,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (31,6)-(31,12))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (31,6)-(31,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :r
+ │ │ │ ├── name_loc: (31,7)-(31,8) = "r"
+ │ │ │ └── operator_loc: (31,6)-(31,7) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (31,10)-(31,12))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (31,11)-(31,12) = "b"
+ │ │ └── operator_loc: (31,10)-(31,11) = "&"
+ │ ├── body: ∅
+ │ ├── locals: [:r, :b]
+ │ ├── def_keyword_loc: (31,0)-(31,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (31,14)-(31,17) = "end"
+ ├── @ DefNode (location: (33,0)-(33,20))
+ │ ├── name: :f
+ │ ├── name_loc: (33,4)-(33,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (33,6)-(33,15))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (33,6)-(33,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :r
+ │ │ │ ├── name_loc: (33,7)-(33,8) = "r"
+ │ │ │ └── operator_loc: (33,6)-(33,7) = "*"
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (33,10)-(33,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :p
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (33,13)-(33,15))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (33,14)-(33,15) = "b"
+ │ │ └── operator_loc: (33,13)-(33,14) = "&"
+ │ ├── body: ∅
+ │ ├── locals: [:r, :p, :b]
+ │ ├── def_keyword_loc: (33,0)-(33,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (33,17)-(33,20) = "end"
+ ├── @ DefNode (location: (35,0)-(35,11))
+ │ ├── name: :f
+ │ ├── name_loc: (35,4)-(35,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (35,0)-(35,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (35,8)-(35,11) = "end"
+ ├── @ DefNode (location: (37,0)-(37,16))
+ │ ├── name: :f
+ │ ├── name_loc: (37,4)-(37,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (37,6)-(37,11))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (37,6)-(37,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (37,9)-(37,11))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (37,10)-(37,11) = "b"
+ │ │ └── operator_loc: (37,9)-(37,10) = "&"
+ │ ├── body: ∅
+ │ ├── locals: [:a, :b]
+ │ ├── def_keyword_loc: (37,0)-(37,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (37,13)-(37,16) = "end"
+ ├── @ DefNode (location: (39,0)-(39,20))
+ │ ├── name: :f
+ │ ├── name_loc: (39,4)-(39,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (39,6)-(39,15))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (39,6)-(39,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (39,9)-(39,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :r
+ │ │ │ ├── name_loc: (39,10)-(39,11) = "r"
+ │ │ │ └── operator_loc: (39,9)-(39,10) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (39,13)-(39,15))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (39,14)-(39,15) = "b"
+ │ │ └── operator_loc: (39,13)-(39,14) = "&"
+ │ ├── body: ∅
+ │ ├── locals: [:a, :r, :b]
+ │ ├── def_keyword_loc: (39,0)-(39,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (39,17)-(39,20) = "end"
+ ├── @ DefNode (location: (41,0)-(41,23))
+ │ ├── name: :f
+ │ ├── name_loc: (41,4)-(41,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (41,6)-(41,18))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (41,6)-(41,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (41,9)-(41,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :r
+ │ │ │ ├── name_loc: (41,10)-(41,11) = "r"
+ │ │ │ └── operator_loc: (41,9)-(41,10) = "*"
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (41,13)-(41,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :p
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (41,16)-(41,18))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (41,17)-(41,18) = "b"
+ │ │ └── operator_loc: (41,16)-(41,17) = "&"
+ │ ├── body: ∅
+ │ ├── locals: [:a, :r, :p, :b]
+ │ ├── def_keyword_loc: (41,0)-(41,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (41,20)-(41,23) = "end"
+ ├── @ DefNode (location: (43,0)-(43,21))
+ │ ├── name: :f
+ │ ├── name_loc: (43,4)-(43,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (43,6)-(43,16))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (43,6)-(43,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (43,9)-(43,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :o
+ │ │ │ ├── name_loc: (43,9)-(43,10) = "o"
+ │ │ │ ├── operator_loc: (43,10)-(43,11) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (43,11)-(43,12))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (43,14)-(43,16))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (43,15)-(43,16) = "b"
+ │ │ └── operator_loc: (43,14)-(43,15) = "&"
+ │ ├── body: ∅
+ │ ├── locals: [:a, :o, :b]
+ │ ├── def_keyword_loc: (43,0)-(43,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (43,18)-(43,21) = "end"
+ ├── @ DefNode (location: (45,0)-(45,25))
+ │ ├── name: :f
+ │ ├── name_loc: (45,4)-(45,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (45,6)-(45,20))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (45,6)-(45,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (45,9)-(45,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :o
+ │ │ │ ├── name_loc: (45,9)-(45,10) = "o"
+ │ │ │ ├── operator_loc: (45,10)-(45,11) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (45,11)-(45,12))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (45,14)-(45,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :r
+ │ │ │ ├── name_loc: (45,15)-(45,16) = "r"
+ │ │ │ └── operator_loc: (45,14)-(45,15) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (45,18)-(45,20))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (45,19)-(45,20) = "b"
+ │ │ └── operator_loc: (45,18)-(45,19) = "&"
+ │ ├── body: ∅
+ │ ├── locals: [:a, :o, :r, :b]
+ │ ├── def_keyword_loc: (45,0)-(45,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (45,22)-(45,25) = "end"
+ ├── @ DefNode (location: (47,0)-(47,28))
+ │ ├── name: :f
+ │ ├── name_loc: (47,4)-(47,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (47,6)-(47,23))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (47,6)-(47,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (47,9)-(47,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :o
+ │ │ │ ├── name_loc: (47,9)-(47,10) = "o"
+ │ │ │ ├── operator_loc: (47,10)-(47,11) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (47,11)-(47,12))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (47,14)-(47,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :r
+ │ │ │ ├── name_loc: (47,15)-(47,16) = "r"
+ │ │ │ └── operator_loc: (47,14)-(47,15) = "*"
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (47,18)-(47,19))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :p
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (47,21)-(47,23))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (47,22)-(47,23) = "b"
+ │ │ └── operator_loc: (47,21)-(47,22) = "&"
+ │ ├── body: ∅
+ │ ├── locals: [:a, :o, :r, :p, :b]
+ │ ├── def_keyword_loc: (47,0)-(47,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (47,25)-(47,28) = "end"
+ ├── @ DefNode (location: (49,0)-(49,24))
+ │ ├── name: :f
+ │ ├── name_loc: (49,4)-(49,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (49,6)-(49,19))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (49,6)-(49,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (49,9)-(49,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :o
+ │ │ │ ├── name_loc: (49,9)-(49,10) = "o"
+ │ │ │ ├── operator_loc: (49,10)-(49,11) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (49,11)-(49,12))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (49,14)-(49,15))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :p
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (49,17)-(49,19))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (49,18)-(49,19) = "b"
+ │ │ └── operator_loc: (49,17)-(49,18) = "&"
+ │ ├── body: ∅
+ │ ├── locals: [:a, :o, :p, :b]
+ │ ├── def_keyword_loc: (49,0)-(49,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (49,21)-(49,24) = "end"
+ ├── @ DefNode (location: (51,0)-(52,5))
+ │ ├── name: :f
+ │ ├── name_loc: (51,4)-(51,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (51,6)-(51,10))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ RequiredKeywordParameterNode (location: (51,6)-(51,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ └── name_loc: (51,6)-(51,10) = "foo:"
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:foo]
+ │ ├── def_keyword_loc: (51,0)-(51,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (52,2)-(52,5) = "end"
+ ├── @ DefNode (location: (54,0)-(55,5))
+ │ ├── name: :f
+ │ ├── name_loc: (54,4)-(54,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (54,6)-(54,13))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ OptionalKeywordParameterNode (location: (54,6)-(54,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── name_loc: (54,6)-(54,10) = "foo:"
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (54,11)-(54,13))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: -1
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:foo]
+ │ ├── def_keyword_loc: (54,0)-(54,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (55,2)-(55,5) = "end"
+ ├── @ DefNode (location: (57,0)-(57,18))
+ │ ├── name: :f
+ │ ├── name_loc: (57,4)-(57,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (57,6)-(57,13))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (57,6)-(57,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :o
+ │ │ │ ├── name_loc: (57,6)-(57,7) = "o"
+ │ │ │ ├── operator_loc: (57,7)-(57,8) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (57,8)-(57,9))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (57,11)-(57,13))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (57,12)-(57,13) = "b"
+ │ │ └── operator_loc: (57,11)-(57,12) = "&"
+ │ ├── body: ∅
+ │ ├── locals: [:o, :b]
+ │ ├── def_keyword_loc: (57,0)-(57,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (57,15)-(57,18) = "end"
+ ├── @ DefNode (location: (59,0)-(59,22))
+ │ ├── name: :f
+ │ ├── name_loc: (59,4)-(59,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (59,6)-(59,17))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (59,6)-(59,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :o
+ │ │ │ ├── name_loc: (59,6)-(59,7) = "o"
+ │ │ │ ├── operator_loc: (59,7)-(59,8) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (59,8)-(59,9))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (59,11)-(59,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :r
+ │ │ │ ├── name_loc: (59,12)-(59,13) = "r"
+ │ │ │ └── operator_loc: (59,11)-(59,12) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (59,15)-(59,17))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (59,16)-(59,17) = "b"
+ │ │ └── operator_loc: (59,15)-(59,16) = "&"
+ │ ├── body: ∅
+ │ ├── locals: [:o, :r, :b]
+ │ ├── def_keyword_loc: (59,0)-(59,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (59,19)-(59,22) = "end"
+ ├── @ DefNode (location: (61,0)-(61,25))
+ │ ├── name: :f
+ │ ├── name_loc: (61,4)-(61,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (61,6)-(61,20))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (61,6)-(61,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :o
+ │ │ │ ├── name_loc: (61,6)-(61,7) = "o"
+ │ │ │ ├── operator_loc: (61,7)-(61,8) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (61,8)-(61,9))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (61,11)-(61,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :r
+ │ │ │ ├── name_loc: (61,12)-(61,13) = "r"
+ │ │ │ └── operator_loc: (61,11)-(61,12) = "*"
+ │ │ ├── posts: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (61,15)-(61,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :p
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block:
+ │ │ @ BlockParameterNode (location: (61,18)-(61,20))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :b
+ │ │ ├── name_loc: (61,19)-(61,20) = "b"
+ │ │ └── operator_loc: (61,18)-(61,19) = "&"
+ │ ├── body: ∅
+ │ ├── locals: [:o, :r, :p, :b]
+ │ ├── def_keyword_loc: (61,0)-(61,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (61,22)-(61,25) = "end"
+ └── @ DefNode (location: (63,0)-(63,21))
+ ├── name: :f
+ ├── name_loc: (63,4)-(63,5) = "f"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (63,6)-(63,16))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 1)
+ │ │ └── @ OptionalParameterNode (location: (63,6)-(63,9))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :o
+ │ │ ├── name_loc: (63,6)-(63,7) = "o"
+ │ │ ├── operator_loc: (63,7)-(63,8) = "="
+ │ │ └── value:
+ │ │ @ IntegerNode (location: (63,8)-(63,9))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── rest: ∅
+ │ ├── posts: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (63,11)-(63,12))
+ │ │ ├── flags: ∅
+ │ │ └── name: :p
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block:
+ │ @ BlockParameterNode (location: (63,14)-(63,16))
+ │ ├── flags: ∅
+ │ ├── name: :b
+ │ ├── name_loc: (63,15)-(63,16) = "b"
+ │ └── operator_loc: (63,14)-(63,15) = "&"
+ ├── body: ∅
+ ├── locals: [:o, :p, :b]
+ ├── def_keyword_loc: (63,0)-(63,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (63,18)-(63,21) = "end"
diff --git a/test/prism/snapshots/whitequark/args_args_assocs.txt b/test/prism/snapshots/whitequark/args_args_assocs.txt
new file mode 100644
index 0000000000..6297f212d8
--- /dev/null
+++ b/test/prism/snapshots/whitequark/args_args_assocs.txt
@@ -0,0 +1,96 @@
+@ ProgramNode (location: (1,0)-(3,24))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,24))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,19))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :fun
+ │ ├── message_loc: (1,0)-(1,3) = "fun"
+ │ ├── opening_loc: (1,3)-(1,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,4)-(1,18))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (1,4)-(1,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (1,4)-(1,7) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ KeywordHashNode (location: (1,9)-(1,18))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (1,9)-(1,18))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (1,9)-(1,13))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (1,9)-(1,10) = ":"
+ │ │ │ ├── value_loc: (1,10)-(1,13) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (1,17)-(1,18))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (1,14)-(1,16) = "=>"
+ │ ├── closing_loc: (1,18)-(1,19) = ")"
+ │ └── block: ∅
+ └── @ CallNode (location: (3,0)-(3,24))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :fun
+ ├── message_loc: (3,0)-(3,3) = "fun"
+ ├── opening_loc: (3,3)-(3,4) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (3,4)-(3,18))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ CallNode (location: (3,4)-(3,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (3,4)-(3,7) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── @ KeywordHashNode (location: (3,9)-(3,18))
+ │ ├── flags: symbol_keys
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (3,9)-(3,18))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (3,9)-(3,13))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (3,9)-(3,10) = ":"
+ │ │ ├── value_loc: (3,10)-(3,13) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (3,17)-(3,18))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (3,14)-(3,16) = "=>"
+ ├── closing_loc: (3,24)-(3,25) = ")"
+ └── block:
+ @ BlockArgumentNode (location: (3,20)-(3,24))
+ ├── expression:
+ │ @ CallNode (location: (3,21)-(3,24))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :baz
+ │ ├── message_loc: (3,21)-(3,24) = "baz"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── operator_loc: (3,20)-(3,21) = "&"
diff --git a/test/prism/snapshots/whitequark/args_args_assocs_comma.txt b/test/prism/snapshots/whitequark/args_args_assocs_comma.txt
new file mode 100644
index 0000000000..969514a511
--- /dev/null
+++ b/test/prism/snapshots/whitequark/args_args_assocs_comma.txt
@@ -0,0 +1,54 @@
+@ ProgramNode (location: (1,0)-(1,20))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,20))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,20))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :[]
+ ├── message_loc: (1,3)-(1,20) = "[bar, :baz => 1,]"
+ ├── opening_loc: (1,3)-(1,4) = "["
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,18))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ CallNode (location: (1,4)-(1,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,4)-(1,7) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── @ KeywordHashNode (location: (1,9)-(1,18))
+ │ ├── flags: symbol_keys
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (1,9)-(1,18))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (1,9)-(1,13))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,9)-(1,10) = ":"
+ │ │ ├── value_loc: (1,10)-(1,13) = "baz"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "baz"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,17)-(1,18))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (1,14)-(1,16) = "=>"
+ ├── closing_loc: (1,19)-(1,20) = "]"
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/args_args_comma.txt b/test/prism/snapshots/whitequark/args_args_comma.txt
new file mode 100644
index 0000000000..09113a72b6
--- /dev/null
+++ b/test/prism/snapshots/whitequark/args_args_comma.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,9))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :[]
+ ├── message_loc: (1,3)-(1,9) = "[bar,]"
+ ├── opening_loc: (1,3)-(1,4) = "["
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,7))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (1,4)-(1,7))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (1,4)-(1,7) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: (1,8)-(1,9) = "]"
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/args_args_star.txt b/test/prism/snapshots/whitequark/args_args_star.txt
new file mode 100644
index 0000000000..26c9e72da3
--- /dev/null
+++ b/test/prism/snapshots/whitequark/args_args_star.txt
@@ -0,0 +1,90 @@
+@ ProgramNode (location: (1,0)-(3,19))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,19))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,14))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :fun
+ │ ├── message_loc: (1,0)-(1,3) = "fun"
+ │ ├── opening_loc: (1,3)-(1,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,4)-(1,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (1,4)-(1,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (1,4)-(1,7) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ SplatNode (location: (1,9)-(1,13))
+ │ │ ├── operator_loc: (1,9)-(1,10) = "*"
+ │ │ └── expression:
+ │ │ @ CallNode (location: (1,10)-(1,13))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,10)-(1,13) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (1,13)-(1,14) = ")"
+ │ └── block: ∅
+ └── @ CallNode (location: (3,0)-(3,19))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :fun
+ ├── message_loc: (3,0)-(3,3) = "fun"
+ ├── opening_loc: (3,3)-(3,4) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (3,4)-(3,13))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ CallNode (location: (3,4)-(3,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (3,4)-(3,7) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── @ SplatNode (location: (3,9)-(3,13))
+ │ ├── operator_loc: (3,9)-(3,10) = "*"
+ │ └── expression:
+ │ @ CallNode (location: (3,10)-(3,13))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (3,10)-(3,13) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: (3,19)-(3,20) = ")"
+ └── block:
+ @ BlockArgumentNode (location: (3,15)-(3,19))
+ ├── expression:
+ │ @ CallNode (location: (3,16)-(3,19))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :baz
+ │ ├── message_loc: (3,16)-(3,19) = "baz"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── operator_loc: (3,15)-(3,16) = "&"
diff --git a/test/prism/snapshots/whitequark/args_assocs.txt b/test/prism/snapshots/whitequark/args_assocs.txt
new file mode 100644
index 0000000000..47cb68d899
--- /dev/null
+++ b/test/prism/snapshots/whitequark/args_assocs.txt
@@ -0,0 +1,195 @@
+@ ProgramNode (location: (1,0)-(11,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(11,17))
+ └── body: (length: 6)
+ ├── @ CallNode (location: (1,0)-(1,14))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :fun
+ │ ├── message_loc: (1,0)-(1,3) = "fun"
+ │ ├── opening_loc: (1,3)-(1,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,4)-(1,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (1,4)-(1,13))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (1,4)-(1,13))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (1,4)-(1,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (1,4)-(1,5) = ":"
+ │ │ │ ├── value_loc: (1,5)-(1,8) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (1,12)-(1,13))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (1,9)-(1,11) = "=>"
+ │ ├── closing_loc: (1,13)-(1,14) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (3,0)-(3,19))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :fun
+ │ ├── message_loc: (3,0)-(3,3) = "fun"
+ │ ├── opening_loc: (3,3)-(3,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,4)-(3,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (3,4)-(3,13))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (3,4)-(3,13))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (3,4)-(3,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (3,4)-(3,5) = ":"
+ │ │ │ ├── value_loc: (3,5)-(3,8) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (3,12)-(3,13))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (3,9)-(3,11) = "=>"
+ │ ├── closing_loc: (3,19)-(3,20) = ")"
+ │ └── block:
+ │ @ BlockArgumentNode (location: (3,15)-(3,19))
+ │ ├── expression:
+ │ │ @ CallNode (location: (3,16)-(3,19))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (3,16)-(3,19) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (3,15)-(3,16) = "&"
+ ├── @ CallNode (location: (5,0)-(5,21))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver:
+ │ │ @ SelfNode (location: (5,0)-(5,4))
+ │ ├── call_operator_loc: (5,4)-(5,5) = "."
+ │ ├── name: :[]=
+ │ ├── message_loc: (5,5)-(5,8) = "[]="
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (5,9)-(5,21))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (5,9)-(5,12))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (5,9)-(5,12) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ KeywordHashNode (location: (5,14)-(5,21))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (5,14)-(5,21))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (5,14)-(5,16))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (5,14)-(5,15) = ":"
+ │ │ │ ├── value_loc: (5,15)-(5,16) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (5,20)-(5,21))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (5,17)-(5,19) = "=>"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (7,0)-(7,15))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver:
+ │ │ @ SelfNode (location: (7,0)-(7,4))
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]
+ │ ├── message_loc: (7,4)-(7,15) = "[:bar => 1]"
+ │ ├── opening_loc: (7,4)-(7,5) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (7,5)-(7,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (7,5)-(7,14))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (7,5)-(7,14))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (7,5)-(7,9))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (7,5)-(7,6) = ":"
+ │ │ │ ├── value_loc: (7,6)-(7,9) = "bar"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "bar"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (7,13)-(7,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (7,10)-(7,12) = "=>"
+ │ ├── closing_loc: (7,14)-(7,15) = "]"
+ │ └── block: ∅
+ ├── @ SuperNode (location: (9,0)-(9,17))
+ │ ├── keyword_loc: (9,0)-(9,5) = "super"
+ │ ├── lparen_loc: (9,5)-(9,6) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (9,6)-(9,16))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (9,6)-(9,16))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (9,6)-(9,16))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (9,6)-(9,10))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (9,6)-(9,7) = ":"
+ │ │ │ ├── value_loc: (9,7)-(9,10) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (9,14)-(9,16))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ └── operator_loc: (9,11)-(9,13) = "=>"
+ │ ├── rparen_loc: (9,16)-(9,17) = ")"
+ │ └── block: ∅
+ └── @ YieldNode (location: (11,0)-(11,17))
+ ├── keyword_loc: (11,0)-(11,5) = "yield"
+ ├── lparen_loc: (11,5)-(11,6) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (11,6)-(11,16))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ KeywordHashNode (location: (11,6)-(11,16))
+ │ ├── flags: symbol_keys
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (11,6)-(11,16))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (11,6)-(11,10))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (11,6)-(11,7) = ":"
+ │ │ ├── value_loc: (11,7)-(11,10) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (11,14)-(11,16))
+ │ │ ├── flags: decimal
+ │ │ └── value: 42
+ │ └── operator_loc: (11,11)-(11,13) = "=>"
+ └── rparen_loc: (11,16)-(11,17) = ")"
diff --git a/test/prism/snapshots/whitequark/args_assocs_comma.txt b/test/prism/snapshots/whitequark/args_assocs_comma.txt
new file mode 100644
index 0000000000..b1b9fbeefe
--- /dev/null
+++ b/test/prism/snapshots/whitequark/args_assocs_comma.txt
@@ -0,0 +1,44 @@
+@ ProgramNode (location: (1,0)-(1,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,15))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,15))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :[]
+ ├── message_loc: (1,3)-(1,15) = "[:baz => 1,]"
+ ├── opening_loc: (1,3)-(1,4) = "["
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,13))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ KeywordHashNode (location: (1,4)-(1,13))
+ │ ├── flags: symbol_keys
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (1,4)-(1,13))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (1,4)-(1,8))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,4)-(1,5) = ":"
+ │ │ ├── value_loc: (1,5)-(1,8) = "baz"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "baz"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,12)-(1,13))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (1,9)-(1,11) = "=>"
+ ├── closing_loc: (1,14)-(1,15) = "]"
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/args_assocs_legacy.txt b/test/prism/snapshots/whitequark/args_assocs_legacy.txt
new file mode 100644
index 0000000000..47cb68d899
--- /dev/null
+++ b/test/prism/snapshots/whitequark/args_assocs_legacy.txt
@@ -0,0 +1,195 @@
+@ ProgramNode (location: (1,0)-(11,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(11,17))
+ └── body: (length: 6)
+ ├── @ CallNode (location: (1,0)-(1,14))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :fun
+ │ ├── message_loc: (1,0)-(1,3) = "fun"
+ │ ├── opening_loc: (1,3)-(1,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,4)-(1,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (1,4)-(1,13))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (1,4)-(1,13))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (1,4)-(1,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (1,4)-(1,5) = ":"
+ │ │ │ ├── value_loc: (1,5)-(1,8) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (1,12)-(1,13))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (1,9)-(1,11) = "=>"
+ │ ├── closing_loc: (1,13)-(1,14) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (3,0)-(3,19))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :fun
+ │ ├── message_loc: (3,0)-(3,3) = "fun"
+ │ ├── opening_loc: (3,3)-(3,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,4)-(3,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (3,4)-(3,13))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (3,4)-(3,13))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (3,4)-(3,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (3,4)-(3,5) = ":"
+ │ │ │ ├── value_loc: (3,5)-(3,8) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (3,12)-(3,13))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (3,9)-(3,11) = "=>"
+ │ ├── closing_loc: (3,19)-(3,20) = ")"
+ │ └── block:
+ │ @ BlockArgumentNode (location: (3,15)-(3,19))
+ │ ├── expression:
+ │ │ @ CallNode (location: (3,16)-(3,19))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (3,16)-(3,19) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (3,15)-(3,16) = "&"
+ ├── @ CallNode (location: (5,0)-(5,21))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver:
+ │ │ @ SelfNode (location: (5,0)-(5,4))
+ │ ├── call_operator_loc: (5,4)-(5,5) = "."
+ │ ├── name: :[]=
+ │ ├── message_loc: (5,5)-(5,8) = "[]="
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (5,9)-(5,21))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (5,9)-(5,12))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (5,9)-(5,12) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ KeywordHashNode (location: (5,14)-(5,21))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (5,14)-(5,21))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (5,14)-(5,16))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (5,14)-(5,15) = ":"
+ │ │ │ ├── value_loc: (5,15)-(5,16) = "a"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (5,20)-(5,21))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (5,17)-(5,19) = "=>"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (7,0)-(7,15))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver:
+ │ │ @ SelfNode (location: (7,0)-(7,4))
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :[]
+ │ ├── message_loc: (7,4)-(7,15) = "[:bar => 1]"
+ │ ├── opening_loc: (7,4)-(7,5) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (7,5)-(7,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (7,5)-(7,14))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (7,5)-(7,14))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (7,5)-(7,9))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (7,5)-(7,6) = ":"
+ │ │ │ ├── value_loc: (7,6)-(7,9) = "bar"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "bar"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (7,13)-(7,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: (7,10)-(7,12) = "=>"
+ │ ├── closing_loc: (7,14)-(7,15) = "]"
+ │ └── block: ∅
+ ├── @ SuperNode (location: (9,0)-(9,17))
+ │ ├── keyword_loc: (9,0)-(9,5) = "super"
+ │ ├── lparen_loc: (9,5)-(9,6) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (9,6)-(9,16))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (9,6)-(9,16))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (9,6)-(9,16))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (9,6)-(9,10))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (9,6)-(9,7) = ":"
+ │ │ │ ├── value_loc: (9,7)-(9,10) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (9,14)-(9,16))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ └── operator_loc: (9,11)-(9,13) = "=>"
+ │ ├── rparen_loc: (9,16)-(9,17) = ")"
+ │ └── block: ∅
+ └── @ YieldNode (location: (11,0)-(11,17))
+ ├── keyword_loc: (11,0)-(11,5) = "yield"
+ ├── lparen_loc: (11,5)-(11,6) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (11,6)-(11,16))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ KeywordHashNode (location: (11,6)-(11,16))
+ │ ├── flags: symbol_keys
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (11,6)-(11,16))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (11,6)-(11,10))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (11,6)-(11,7) = ":"
+ │ │ ├── value_loc: (11,7)-(11,10) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (11,14)-(11,16))
+ │ │ ├── flags: decimal
+ │ │ └── value: 42
+ │ └── operator_loc: (11,11)-(11,13) = "=>"
+ └── rparen_loc: (11,16)-(11,17) = ")"
diff --git a/test/prism/snapshots/whitequark/args_block_pass.txt b/test/prism/snapshots/whitequark/args_block_pass.txt
new file mode 100644
index 0000000000..99ffa81934
--- /dev/null
+++ b/test/prism/snapshots/whitequark/args_block_pass.txt
@@ -0,0 +1,28 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,8))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :fun
+ ├── message_loc: (1,0)-(1,3) = "fun"
+ ├── opening_loc: (1,3)-(1,4) = "("
+ ├── arguments: ∅
+ ├── closing_loc: (1,8)-(1,9) = ")"
+ └── block:
+ @ BlockArgumentNode (location: (1,4)-(1,8))
+ ├── expression:
+ │ @ CallNode (location: (1,5)-(1,8))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (1,5)-(1,8) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── operator_loc: (1,4)-(1,5) = "&"
diff --git a/test/prism/snapshots/whitequark/args_cmd.txt b/test/prism/snapshots/whitequark/args_cmd.txt
new file mode 100644
index 0000000000..0089e56157
--- /dev/null
+++ b/test/prism/snapshots/whitequark/args_cmd.txt
@@ -0,0 +1,41 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,10))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :fun
+ ├── message_loc: (1,0)-(1,3) = "fun"
+ ├── opening_loc: (1,3)-(1,4) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,9))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (1,4)-(1,9))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (1,4)-(1,5) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,6)-(1,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (1,6)-(1,9))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,6)-(1,9) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: (1,9)-(1,10) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/args_star.txt b/test/prism/snapshots/whitequark/args_star.txt
new file mode 100644
index 0000000000..c19577f106
--- /dev/null
+++ b/test/prism/snapshots/whitequark/args_star.txt
@@ -0,0 +1,70 @@
+@ ProgramNode (location: (1,0)-(3,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,14))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,9))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :fun
+ │ ├── message_loc: (1,0)-(1,3) = "fun"
+ │ ├── opening_loc: (1,3)-(1,4) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,4)-(1,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ SplatNode (location: (1,4)-(1,8))
+ │ │ ├── operator_loc: (1,4)-(1,5) = "*"
+ │ │ └── expression:
+ │ │ @ CallNode (location: (1,5)-(1,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,5)-(1,8) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (1,8)-(1,9) = ")"
+ │ └── block: ∅
+ └── @ CallNode (location: (3,0)-(3,14))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :fun
+ ├── message_loc: (3,0)-(3,3) = "fun"
+ ├── opening_loc: (3,3)-(3,4) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (3,4)-(3,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ SplatNode (location: (3,4)-(3,8))
+ │ ├── operator_loc: (3,4)-(3,5) = "*"
+ │ └── expression:
+ │ @ CallNode (location: (3,5)-(3,8))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (3,5)-(3,8) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: (3,14)-(3,15) = ")"
+ └── block:
+ @ BlockArgumentNode (location: (3,10)-(3,14))
+ ├── expression:
+ │ @ CallNode (location: (3,11)-(3,14))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :baz
+ │ ├── message_loc: (3,11)-(3,14) = "baz"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── operator_loc: (3,10)-(3,11) = "&"
diff --git a/test/prism/snapshots/whitequark/array_assocs.txt b/test/prism/snapshots/whitequark/array_assocs.txt
new file mode 100644
index 0000000000..3747e3f7d8
--- /dev/null
+++ b/test/prism/snapshots/whitequark/array_assocs.txt
@@ -0,0 +1,44 @@
+@ ProgramNode (location: (1,0)-(3,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,13))
+ └── body: (length: 2)
+ ├── @ ArrayNode (location: (1,0)-(1,10))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (1,2)-(1,8))
+ │ │ ├── flags: ∅
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (1,2)-(1,8))
+ │ │ ├── key:
+ │ │ │ @ IntegerNode (location: (1,2)-(1,3))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (1,7)-(1,8))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── operator_loc: (1,4)-(1,6) = "=>"
+ │ ├── opening_loc: (1,0)-(1,1) = "["
+ │ └── closing_loc: (1,9)-(1,10) = "]"
+ └── @ ArrayNode (location: (3,0)-(3,13))
+ ├── flags: ∅
+ ├── elements: (length: 2)
+ │ ├── @ IntegerNode (location: (3,2)-(3,3))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── @ KeywordHashNode (location: (3,5)-(3,11))
+ │ ├── flags: ∅
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (3,5)-(3,11))
+ │ ├── key:
+ │ │ @ IntegerNode (location: (3,5)-(3,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── value:
+ │ │ @ IntegerNode (location: (3,10)-(3,11))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ └── operator_loc: (3,7)-(3,9) = "=>"
+ ├── opening_loc: (3,0)-(3,1) = "["
+ └── closing_loc: (3,12)-(3,13) = "]"
diff --git a/test/prism/snapshots/whitequark/array_plain.txt b/test/prism/snapshots/whitequark/array_plain.txt
new file mode 100644
index 0000000000..b183a7ac6b
--- /dev/null
+++ b/test/prism/snapshots/whitequark/array_plain.txt
@@ -0,0 +1,16 @@
+@ ProgramNode (location: (1,0)-(1,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,6))
+ └── body: (length: 1)
+ └── @ ArrayNode (location: (1,0)-(1,6))
+ ├── flags: ∅
+ ├── elements: (length: 2)
+ │ ├── @ IntegerNode (location: (1,1)-(1,2))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── @ IntegerNode (location: (1,4)-(1,5))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── opening_loc: (1,0)-(1,1) = "["
+ └── closing_loc: (1,5)-(1,6) = "]"
diff --git a/test/prism/snapshots/whitequark/array_splat.txt b/test/prism/snapshots/whitequark/array_splat.txt
new file mode 100644
index 0000000000..f76563e9f4
--- /dev/null
+++ b/test/prism/snapshots/whitequark/array_splat.txt
@@ -0,0 +1,68 @@
+@ ProgramNode (location: (1,0)-(5,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,9))
+ └── body: (length: 3)
+ ├── @ ArrayNode (location: (1,0)-(1,6))
+ │ ├── flags: contains_splat
+ │ ├── elements: (length: 1)
+ │ │ └── @ SplatNode (location: (1,1)-(1,5))
+ │ │ ├── operator_loc: (1,1)-(1,2) = "*"
+ │ │ └── expression:
+ │ │ @ CallNode (location: (1,2)-(1,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,2)-(1,5) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (1,0)-(1,1) = "["
+ │ └── closing_loc: (1,5)-(1,6) = "]"
+ ├── @ ArrayNode (location: (3,0)-(3,12))
+ │ ├── flags: contains_splat
+ │ ├── elements: (length: 3)
+ │ │ ├── @ IntegerNode (location: (3,1)-(3,2))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── @ SplatNode (location: (3,4)-(3,8))
+ │ │ │ ├── operator_loc: (3,4)-(3,5) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ CallNode (location: (3,5)-(3,8))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (3,5)-(3,8) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ IntegerNode (location: (3,10)-(3,11))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── opening_loc: (3,0)-(3,1) = "["
+ │ └── closing_loc: (3,11)-(3,12) = "]"
+ └── @ ArrayNode (location: (5,0)-(5,9))
+ ├── flags: contains_splat
+ ├── elements: (length: 2)
+ │ ├── @ IntegerNode (location: (5,1)-(5,2))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── @ SplatNode (location: (5,4)-(5,8))
+ │ ├── operator_loc: (5,4)-(5,5) = "*"
+ │ └── expression:
+ │ @ CallNode (location: (5,5)-(5,8))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (5,5)-(5,8) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── opening_loc: (5,0)-(5,1) = "["
+ └── closing_loc: (5,8)-(5,9) = "]"
diff --git a/test/prism/snapshots/whitequark/array_symbols.txt b/test/prism/snapshots/whitequark/array_symbols.txt
new file mode 100644
index 0000000000..0662f01af1
--- /dev/null
+++ b/test/prism/snapshots/whitequark/array_symbols.txt
@@ -0,0 +1,22 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ ArrayNode (location: (1,0)-(1,11))
+ ├── flags: ∅
+ ├── elements: (length: 2)
+ │ ├── @ SymbolNode (location: (1,3)-(1,6))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (1,3)-(1,6) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ └── @ SymbolNode (location: (1,7)-(1,10))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: ∅
+ │ ├── value_loc: (1,7)-(1,10) = "bar"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "bar"
+ ├── opening_loc: (1,0)-(1,3) = "%i["
+ └── closing_loc: (1,10)-(1,11) = "]"
diff --git a/test/prism/snapshots/whitequark/array_symbols_empty.txt b/test/prism/snapshots/whitequark/array_symbols_empty.txt
new file mode 100644
index 0000000000..1068ba6d0e
--- /dev/null
+++ b/test/prism/snapshots/whitequark/array_symbols_empty.txt
@@ -0,0 +1,15 @@
+@ ProgramNode (location: (1,0)-(3,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,4))
+ └── body: (length: 2)
+ ├── @ ArrayNode (location: (1,0)-(1,4))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 0)
+ │ ├── opening_loc: (1,0)-(1,3) = "%I("
+ │ └── closing_loc: (1,3)-(1,4) = ")"
+ └── @ ArrayNode (location: (3,0)-(3,4))
+ ├── flags: ∅
+ ├── elements: (length: 0)
+ ├── opening_loc: (3,0)-(3,3) = "%i["
+ └── closing_loc: (3,3)-(3,4) = "]"
diff --git a/test/prism/snapshots/whitequark/array_symbols_interp.txt b/test/prism/snapshots/whitequark/array_symbols_interp.txt
new file mode 100644
index 0000000000..2437486b43
--- /dev/null
+++ b/test/prism/snapshots/whitequark/array_symbols_interp.txt
@@ -0,0 +1,67 @@
+@ ProgramNode (location: (1,0)-(3,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,13))
+ └── body: (length: 2)
+ ├── @ ArrayNode (location: (1,0)-(1,14))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ SymbolNode (location: (1,3)-(1,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (1,3)-(1,6) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ └── @ InterpolatedSymbolNode (location: (1,7)-(1,13))
+ │ │ ├── opening_loc: ∅
+ │ │ ├── parts: (length: 1)
+ │ │ │ └── @ EmbeddedStatementsNode (location: (1,7)-(1,13))
+ │ │ │ ├── opening_loc: (1,7)-(1,9) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (1,9)-(1,12))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (1,9)-(1,12))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (1,9)-(1,12) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── closing_loc: (1,12)-(1,13) = "}"
+ │ │ └── closing_loc: ∅
+ │ ├── opening_loc: (1,0)-(1,3) = "%I["
+ │ └── closing_loc: (1,13)-(1,14) = "]"
+ └── @ ArrayNode (location: (3,0)-(3,13))
+ ├── flags: ∅
+ ├── elements: (length: 1)
+ │ └── @ InterpolatedSymbolNode (location: (3,3)-(3,12))
+ │ ├── opening_loc: ∅
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (3,3)-(3,6))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (3,3)-(3,6) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ └── @ EmbeddedStatementsNode (location: (3,6)-(3,12))
+ │ │ ├── opening_loc: (3,6)-(3,8) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (3,8)-(3,11))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (3,8)-(3,11))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (3,8)-(3,11) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── closing_loc: (3,11)-(3,12) = "}"
+ │ └── closing_loc: ∅
+ ├── opening_loc: (3,0)-(3,3) = "%I["
+ └── closing_loc: (3,12)-(3,13) = "]"
diff --git a/test/prism/snapshots/whitequark/array_words.txt b/test/prism/snapshots/whitequark/array_words.txt
new file mode 100644
index 0000000000..84121de355
--- /dev/null
+++ b/test/prism/snapshots/whitequark/array_words.txt
@@ -0,0 +1,22 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ ArrayNode (location: (1,0)-(1,11))
+ ├── flags: ∅
+ ├── elements: (length: 2)
+ │ ├── @ StringNode (location: (1,3)-(1,6))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (1,3)-(1,6) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ └── @ StringNode (location: (1,7)-(1,10))
+ │ ├── flags: ∅
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (1,7)-(1,10) = "bar"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "bar"
+ ├── opening_loc: (1,0)-(1,3) = "%w["
+ └── closing_loc: (1,10)-(1,11) = "]"
diff --git a/test/prism/snapshots/whitequark/array_words_empty.txt b/test/prism/snapshots/whitequark/array_words_empty.txt
new file mode 100644
index 0000000000..e4dba94cdf
--- /dev/null
+++ b/test/prism/snapshots/whitequark/array_words_empty.txt
@@ -0,0 +1,15 @@
+@ ProgramNode (location: (1,0)-(3,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,4))
+ └── body: (length: 2)
+ ├── @ ArrayNode (location: (1,0)-(1,4))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 0)
+ │ ├── opening_loc: (1,0)-(1,3) = "%W("
+ │ └── closing_loc: (1,3)-(1,4) = ")"
+ └── @ ArrayNode (location: (3,0)-(3,4))
+ ├── flags: ∅
+ ├── elements: (length: 0)
+ ├── opening_loc: (3,0)-(3,3) = "%w["
+ └── closing_loc: (3,3)-(3,4) = "]"
diff --git a/test/prism/snapshots/whitequark/array_words_interp.txt b/test/prism/snapshots/whitequark/array_words_interp.txt
new file mode 100644
index 0000000000..c3ae9a0cf8
--- /dev/null
+++ b/test/prism/snapshots/whitequark/array_words_interp.txt
@@ -0,0 +1,80 @@
+@ ProgramNode (location: (1,0)-(3,22))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,22))
+ └── body: (length: 2)
+ ├── @ ArrayNode (location: (1,0)-(1,14))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ StringNode (location: (1,3)-(1,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (1,3)-(1,6) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ └── @ InterpolatedStringNode (location: (1,7)-(1,13))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── parts: (length: 1)
+ │ │ │ └── @ EmbeddedStatementsNode (location: (1,7)-(1,13))
+ │ │ │ ├── opening_loc: (1,7)-(1,9) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (1,9)-(1,12))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (1,9)-(1,12))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (1,9)-(1,12) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── closing_loc: (1,12)-(1,13) = "}"
+ │ │ └── closing_loc: ∅
+ │ ├── opening_loc: (1,0)-(1,3) = "%W["
+ │ └── closing_loc: (1,13)-(1,14) = "]"
+ └── @ ArrayNode (location: (3,0)-(3,22))
+ ├── flags: ∅
+ ├── elements: (length: 2)
+ │ ├── @ StringNode (location: (3,3)-(3,6))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (3,3)-(3,6) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ └── @ InterpolatedStringNode (location: (3,7)-(3,21))
+ │ ├── flags: ∅
+ │ ├── opening_loc: ∅
+ │ ├── parts: (length: 3)
+ │ │ ├── @ EmbeddedStatementsNode (location: (3,7)-(3,13))
+ │ │ │ ├── opening_loc: (3,7)-(3,9) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (3,9)-(3,12))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (3,9)-(3,12))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (3,9)-(3,12) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── closing_loc: (3,12)-(3,13) = "}"
+ │ │ ├── @ StringNode (location: (3,13)-(3,16))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (3,13)-(3,16) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ └── @ EmbeddedVariableNode (location: (3,16)-(3,21))
+ │ │ ├── operator_loc: (3,16)-(3,17) = "#"
+ │ │ └── variable:
+ │ │ @ InstanceVariableReadNode (location: (3,17)-(3,21))
+ │ │ └── name: :@baz
+ │ └── closing_loc: ∅
+ ├── opening_loc: (3,0)-(3,3) = "%W["
+ └── closing_loc: (3,21)-(3,22) = "]"
diff --git a/test/prism/snapshots/whitequark/asgn_cmd.txt b/test/prism/snapshots/whitequark/asgn_cmd.txt
new file mode 100644
index 0000000000..4a3c36680b
--- /dev/null
+++ b/test/prism/snapshots/whitequark/asgn_cmd.txt
@@ -0,0 +1,55 @@
+@ ProgramNode (location: (1,0)-(3,11))
+├── locals: [:foo, :bar]
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,11))
+ └── body: (length: 2)
+ ├── @ LocalVariableWriteNode (location: (1,0)-(1,17))
+ │ ├── name: :foo
+ │ ├── depth: 0
+ │ ├── name_loc: (1,0)-(1,3) = "foo"
+ │ ├── value:
+ │ │ @ LocalVariableWriteNode (location: (1,6)-(1,17))
+ │ │ ├── name: :bar
+ │ │ ├── depth: 0
+ │ │ ├── name_loc: (1,6)-(1,9) = "bar"
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (1,12)-(1,17))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :m
+ │ │ │ ├── message_loc: (1,12)-(1,13) = "m"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (1,14)-(1,17))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ LocalVariableReadNode (location: (1,14)-(1,17))
+ │ │ │ │ ├── name: :foo
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (1,10)-(1,11) = "="
+ │ └── operator_loc: (1,4)-(1,5) = "="
+ └── @ LocalVariableWriteNode (location: (3,0)-(3,11))
+ ├── name: :foo
+ ├── depth: 0
+ ├── name_loc: (3,0)-(3,3) = "foo"
+ ├── value:
+ │ @ CallNode (location: (3,6)-(3,11))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (3,6)-(3,7) = "m"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,8)-(3,11))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (3,8)-(3,11))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── operator_loc: (3,4)-(3,5) = "="
diff --git a/test/prism/snapshots/whitequark/asgn_mrhs.txt b/test/prism/snapshots/whitequark/asgn_mrhs.txt
new file mode 100644
index 0000000000..41d5bdc5bf
--- /dev/null
+++ b/test/prism/snapshots/whitequark/asgn_mrhs.txt
@@ -0,0 +1,87 @@
+@ ProgramNode (location: (1,0)-(5,15))
+├── locals: [:foo]
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,15))
+ └── body: (length: 3)
+ ├── @ LocalVariableWriteNode (location: (1,0)-(1,10))
+ │ ├── name: :foo
+ │ ├── depth: 0
+ │ ├── name_loc: (1,0)-(1,3) = "foo"
+ │ ├── value:
+ │ │ @ ArrayNode (location: (1,6)-(1,10))
+ │ │ ├── flags: contains_splat
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ SplatNode (location: (1,6)-(1,10))
+ │ │ │ ├── operator_loc: (1,6)-(1,7) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ CallNode (location: (1,7)-(1,10))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (1,7)-(1,10) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (1,4)-(1,5) = "="
+ ├── @ LocalVariableWriteNode (location: (3,0)-(3,12))
+ │ ├── name: :foo
+ │ ├── depth: 0
+ │ ├── name_loc: (3,0)-(3,3) = "foo"
+ │ ├── value:
+ │ │ @ ArrayNode (location: (3,6)-(3,12))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 2)
+ │ │ │ ├── @ CallNode (location: (3,6)-(3,9))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (3,6)-(3,9) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── @ IntegerNode (location: (3,11)-(3,12))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (3,4)-(3,5) = "="
+ └── @ LocalVariableWriteNode (location: (5,0)-(5,15))
+ ├── name: :foo
+ ├── depth: 0
+ ├── name_loc: (5,0)-(5,3) = "foo"
+ ├── value:
+ │ @ ArrayNode (location: (5,6)-(5,15))
+ │ ├── flags: contains_splat
+ │ ├── elements: (length: 2)
+ │ │ ├── @ CallNode (location: (5,6)-(5,9))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (5,6)-(5,9) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ SplatNode (location: (5,11)-(5,15))
+ │ │ ├── operator_loc: (5,11)-(5,12) = "*"
+ │ │ └── expression:
+ │ │ @ CallNode (location: (5,12)-(5,15))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (5,12)-(5,15) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: ∅
+ │ └── closing_loc: ∅
+ └── operator_loc: (5,4)-(5,5) = "="
diff --git a/test/prism/snapshots/whitequark/back_ref.txt b/test/prism/snapshots/whitequark/back_ref.txt
new file mode 100644
index 0000000000..ea6b76faf7
--- /dev/null
+++ b/test/prism/snapshots/whitequark/back_ref.txt
@@ -0,0 +1,7 @@
+@ ProgramNode (location: (1,0)-(1,2))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,2))
+ └── body: (length: 1)
+ └── @ BackReferenceReadNode (location: (1,0)-(1,2))
+ └── name: :$+
diff --git a/test/prism/snapshots/whitequark/bang.txt b/test/prism/snapshots/whitequark/bang.txt
new file mode 100644
index 0000000000..997a8718c5
--- /dev/null
+++ b/test/prism/snapshots/whitequark/bang.txt
@@ -0,0 +1,25 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,4))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,1)-(1,4))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,1)-(1,4) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :!
+ ├── message_loc: (1,0)-(1,1) = "!"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/bang_cmd.txt b/test/prism/snapshots/whitequark/bang_cmd.txt
new file mode 100644
index 0000000000..e487da5e57
--- /dev/null
+++ b/test/prism/snapshots/whitequark/bang_cmd.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(1,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,6))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,6))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,1)-(1,6))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (1,1)-(1,2) = "m"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,3)-(1,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (1,3)-(1,6))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,3)-(1,6) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :!
+ ├── message_loc: (1,0)-(1,1) = "!"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/begin_cmdarg.txt b/test/prism/snapshots/whitequark/begin_cmdarg.txt
new file mode 100644
index 0000000000..b0c03c1754
--- /dev/null
+++ b/test/prism/snapshots/whitequark/begin_cmdarg.txt
@@ -0,0 +1,51 @@
+@ ProgramNode (location: (1,0)-(1,28))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,28))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,28))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :p
+ ├── message_loc: (1,0)-(1,1) = "p"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,28))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ BeginNode (location: (1,2)-(1,28))
+ │ ├── begin_keyword_loc: (1,2)-(1,7) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,8)-(1,24))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,8)-(1,24))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ IntegerNode (location: (1,8)-(1,9))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── call_operator_loc: (1,9)-(1,10) = "."
+ │ │ ├── name: :times
+ │ │ ├── message_loc: (1,10)-(1,15) = "times"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (1,16)-(1,24))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (1,19)-(1,20))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (1,19)-(1,20))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── opening_loc: (1,16)-(1,18) = "do"
+ │ │ └── closing_loc: (1,21)-(1,24) = "end"
+ │ ├── rescue_clause: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (1,25)-(1,28) = "end"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/beginless_erange_after_newline.txt b/test/prism/snapshots/whitequark/beginless_erange_after_newline.txt
new file mode 100644
index 0000000000..69db24a127
--- /dev/null
+++ b/test/prism/snapshots/whitequark/beginless_erange_after_newline.txt
@@ -0,0 +1,23 @@
+@ ProgramNode (location: (1,0)-(2,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,6))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ RangeNode (location: (2,0)-(2,6))
+ ├── flags: exclude_end
+ ├── left: ∅
+ ├── right:
+ │ @ IntegerNode (location: (2,3)-(2,6))
+ │ ├── flags: decimal
+ │ └── value: 100
+ └── operator_loc: (2,0)-(2,3) = "..."
diff --git a/test/prism/snapshots/whitequark/beginless_irange_after_newline.txt b/test/prism/snapshots/whitequark/beginless_irange_after_newline.txt
new file mode 100644
index 0000000000..cbc11c83c1
--- /dev/null
+++ b/test/prism/snapshots/whitequark/beginless_irange_after_newline.txt
@@ -0,0 +1,23 @@
+@ ProgramNode (location: (1,0)-(2,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,5))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ RangeNode (location: (2,0)-(2,5))
+ ├── flags: ∅
+ ├── left: ∅
+ ├── right:
+ │ @ IntegerNode (location: (2,2)-(2,5))
+ │ ├── flags: decimal
+ │ └── value: 100
+ └── operator_loc: (2,0)-(2,2) = ".."
diff --git a/test/prism/snapshots/whitequark/beginless_range.txt b/test/prism/snapshots/whitequark/beginless_range.txt
new file mode 100644
index 0000000000..59e6f135ad
--- /dev/null
+++ b/test/prism/snapshots/whitequark/beginless_range.txt
@@ -0,0 +1,21 @@
+@ ProgramNode (location: (1,0)-(3,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,5))
+ └── body: (length: 2)
+ ├── @ RangeNode (location: (1,0)-(1,6))
+ │ ├── flags: exclude_end
+ │ ├── left: ∅
+ │ ├── right:
+ │ │ @ IntegerNode (location: (1,3)-(1,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 100
+ │ └── operator_loc: (1,0)-(1,3) = "..."
+ └── @ RangeNode (location: (3,0)-(3,5))
+ ├── flags: ∅
+ ├── left: ∅
+ ├── right:
+ │ @ IntegerNode (location: (3,2)-(3,5))
+ │ ├── flags: decimal
+ │ └── value: 100
+ └── operator_loc: (3,0)-(3,2) = ".."
diff --git a/test/prism/snapshots/whitequark/blockarg.txt b/test/prism/snapshots/whitequark/blockarg.txt
new file mode 100644
index 0000000000..70b054e073
--- /dev/null
+++ b/test/prism/snapshots/whitequark/blockarg.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (1,0)-(1,18))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,18))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,18))
+ ├── name: :f
+ ├── name_loc: (1,4)-(1,5) = "f"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,12))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block:
+ │ @ BlockParameterNode (location: (1,6)-(1,12))
+ │ ├── flags: ∅
+ │ ├── name: :block
+ │ ├── name_loc: (1,7)-(1,12) = "block"
+ │ └── operator_loc: (1,6)-(1,7) = "&"
+ ├── body: ∅
+ ├── locals: [:block]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,5)-(1,6) = "("
+ ├── rparen_loc: (1,12)-(1,13) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,15)-(1,18) = "end"
diff --git a/test/prism/snapshots/whitequark/blockargs.txt b/test/prism/snapshots/whitequark/blockargs.txt
new file mode 100644
index 0000000000..149c3765c9
--- /dev/null
+++ b/test/prism/snapshots/whitequark/blockargs.txt
@@ -0,0 +1,1339 @@
+@ ProgramNode (location: (1,0)-(71,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(71,7))
+ └── body: (length: 35)
+ ├── @ CallNode (location: (1,0)-(1,5))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (1,0)-(1,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,1)-(1,5))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,1)-(1,2) = "{"
+ │ └── closing_loc: (1,4)-(1,5) = "}"
+ ├── @ CallNode (location: (3,0)-(3,8))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (3,0)-(3,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (3,1)-(3,8))
+ │ ├── locals: []
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (3,3)-(3,6))
+ │ │ ├── parameters: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (3,3)-(3,4) = "|"
+ │ │ └── closing_loc: (3,5)-(3,6) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (3,1)-(3,2) = "{"
+ │ └── closing_loc: (3,7)-(3,8) = "}"
+ ├── @ CallNode (location: (5,0)-(5,9))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (5,0)-(5,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (5,1)-(5,9))
+ │ ├── locals: [:b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (5,3)-(5,7))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (5,4)-(5,6))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockParameterNode (location: (5,4)-(5,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (5,5)-(5,6) = "b"
+ │ │ │ └── operator_loc: (5,4)-(5,5) = "&"
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (5,3)-(5,4) = "|"
+ │ │ └── closing_loc: (5,6)-(5,7) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (5,1)-(5,2) = "{"
+ │ └── closing_loc: (5,8)-(5,9) = "}"
+ ├── @ CallNode (location: (7,0)-(7,16))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (7,0)-(7,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (7,1)-(7,16))
+ │ ├── locals: [:baz, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (7,3)-(7,14))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (7,4)-(7,13))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest:
+ │ │ │ │ @ KeywordRestParameterNode (location: (7,4)-(7,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ ├── name_loc: (7,6)-(7,9) = "baz"
+ │ │ │ │ └── operator_loc: (7,4)-(7,6) = "**"
+ │ │ │ └── block:
+ │ │ │ @ BlockParameterNode (location: (7,11)-(7,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (7,12)-(7,13) = "b"
+ │ │ │ └── operator_loc: (7,11)-(7,12) = "&"
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (7,3)-(7,4) = "|"
+ │ │ └── closing_loc: (7,13)-(7,14) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (7,1)-(7,2) = "{"
+ │ └── closing_loc: (7,15)-(7,16) = "}"
+ ├── @ CallNode (location: (9,0)-(9,12))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (9,0)-(9,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (9,1)-(9,12))
+ │ ├── locals: [:b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (9,3)-(9,10))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (9,4)-(9,9))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ RestParameterNode (location: (9,4)-(9,5))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: ∅
+ │ │ │ │ ├── name_loc: ∅
+ │ │ │ │ └── operator_loc: (9,4)-(9,5) = "*"
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockParameterNode (location: (9,7)-(9,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (9,8)-(9,9) = "b"
+ │ │ │ └── operator_loc: (9,7)-(9,8) = "&"
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (9,3)-(9,4) = "|"
+ │ │ └── closing_loc: (9,9)-(9,10) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (9,1)-(9,2) = "{"
+ │ └── closing_loc: (9,11)-(9,12) = "}"
+ ├── @ CallNode (location: (11,0)-(11,16))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (11,0)-(11,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (11,1)-(11,16))
+ │ ├── locals: [:r, :p, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (11,3)-(11,14))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (11,4)-(11,13))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ RestParameterNode (location: (11,4)-(11,6))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :r
+ │ │ │ │ ├── name_loc: (11,5)-(11,6) = "r"
+ │ │ │ │ └── operator_loc: (11,4)-(11,5) = "*"
+ │ │ │ ├── posts: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (11,8)-(11,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :p
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockParameterNode (location: (11,11)-(11,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (11,12)-(11,13) = "b"
+ │ │ │ └── operator_loc: (11,11)-(11,12) = "&"
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (11,3)-(11,4) = "|"
+ │ │ └── closing_loc: (11,13)-(11,14) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (11,1)-(11,2) = "{"
+ │ └── closing_loc: (11,15)-(11,16) = "}"
+ ├── @ CallNode (location: (13,0)-(13,13))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (13,0)-(13,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (13,1)-(13,13))
+ │ ├── locals: [:s, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (13,3)-(13,11))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (13,4)-(13,10))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ RestParameterNode (location: (13,4)-(13,6))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :s
+ │ │ │ │ ├── name_loc: (13,5)-(13,6) = "s"
+ │ │ │ │ └── operator_loc: (13,4)-(13,5) = "*"
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockParameterNode (location: (13,8)-(13,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (13,9)-(13,10) = "b"
+ │ │ │ └── operator_loc: (13,8)-(13,9) = "&"
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (13,3)-(13,4) = "|"
+ │ │ └── closing_loc: (13,10)-(13,11) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (13,1)-(13,2) = "{"
+ │ └── closing_loc: (13,12)-(13,13) = "}"
+ ├── @ CallNode (location: (15,0)-(15,9))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (15,0)-(15,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (15,1)-(15,9))
+ │ ├── locals: [:s]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (15,3)-(15,7))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (15,4)-(15,6))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ RestParameterNode (location: (15,4)-(15,6))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :s
+ │ │ │ │ ├── name_loc: (15,5)-(15,6) = "s"
+ │ │ │ │ └── operator_loc: (15,4)-(15,5) = "*"
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (15,3)-(15,4) = "|"
+ │ │ └── closing_loc: (15,6)-(15,7) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (15,1)-(15,2) = "{"
+ │ └── closing_loc: (15,8)-(15,9) = "}"
+ ├── @ CallNode (location: (17,0)-(17,8))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (17,0)-(17,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (17,1)-(17,8))
+ │ ├── locals: []
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (17,3)-(17,6))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (17,4)-(17,5))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ RestParameterNode (location: (17,4)-(17,5))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: ∅
+ │ │ │ │ ├── name_loc: ∅
+ │ │ │ │ └── operator_loc: (17,4)-(17,5) = "*"
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (17,3)-(17,4) = "|"
+ │ │ └── closing_loc: (17,5)-(17,6) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (17,1)-(17,2) = "{"
+ │ └── closing_loc: (17,7)-(17,8) = "}"
+ ├── @ CallNode (location: (19,0)-(21,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (19,0)-(19,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (19,1)-(21,3))
+ │ ├── locals: [:a]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (19,3)-(21,1))
+ │ │ ├── parameters: ∅
+ │ │ ├── locals: (length: 1)
+ │ │ │ └── @ BlockLocalVariableNode (location: (20,0)-(20,1))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── opening_loc: (19,3)-(19,4) = "|"
+ │ │ └── closing_loc: (21,0)-(21,1) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (19,1)-(19,2) = "{"
+ │ └── closing_loc: (21,2)-(21,3) = "}"
+ ├── @ CallNode (location: (23,0)-(23,9))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (23,0)-(23,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (23,1)-(23,9))
+ │ ├── locals: [:a]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (23,3)-(23,7))
+ │ │ ├── parameters: ∅
+ │ │ ├── locals: (length: 1)
+ │ │ │ └── @ BlockLocalVariableNode (location: (23,5)-(23,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── opening_loc: (23,3)-(23,4) = "|"
+ │ │ └── closing_loc: (23,6)-(23,7) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (23,1)-(23,2) = "{"
+ │ └── closing_loc: (23,8)-(23,9) = "}"
+ ├── @ CallNode (location: (25,0)-(25,12))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (25,0)-(25,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (25,1)-(25,12))
+ │ ├── locals: [:a, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (25,3)-(25,10))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (25,4)-(25,9))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (25,4)-(25,5))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockParameterNode (location: (25,7)-(25,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (25,8)-(25,9) = "b"
+ │ │ │ └── operator_loc: (25,7)-(25,8) = "&"
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (25,3)-(25,4) = "|"
+ │ │ └── closing_loc: (25,9)-(25,10) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (25,1)-(25,2) = "{"
+ │ └── closing_loc: (25,11)-(25,12) = "}"
+ ├── @ CallNode (location: (27,0)-(27,15))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (27,0)-(27,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (27,1)-(27,15))
+ │ ├── locals: [:a, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (27,3)-(27,13))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (27,4)-(27,12))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (27,4)-(27,5))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ RestParameterNode (location: (27,7)-(27,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: ∅
+ │ │ │ │ ├── name_loc: ∅
+ │ │ │ │ └── operator_loc: (27,7)-(27,8) = "*"
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockParameterNode (location: (27,10)-(27,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (27,11)-(27,12) = "b"
+ │ │ │ └── operator_loc: (27,10)-(27,11) = "&"
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (27,3)-(27,4) = "|"
+ │ │ └── closing_loc: (27,12)-(27,13) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (27,1)-(27,2) = "{"
+ │ └── closing_loc: (27,14)-(27,15) = "}"
+ ├── @ CallNode (location: (29,0)-(29,19))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (29,0)-(29,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (29,1)-(29,19))
+ │ ├── locals: [:a, :r, :p, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (29,3)-(29,17))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (29,4)-(29,16))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (29,4)-(29,5))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ RestParameterNode (location: (29,7)-(29,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :r
+ │ │ │ │ ├── name_loc: (29,8)-(29,9) = "r"
+ │ │ │ │ └── operator_loc: (29,7)-(29,8) = "*"
+ │ │ │ ├── posts: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (29,11)-(29,12))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :p
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockParameterNode (location: (29,14)-(29,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (29,15)-(29,16) = "b"
+ │ │ │ └── operator_loc: (29,14)-(29,15) = "&"
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (29,3)-(29,4) = "|"
+ │ │ └── closing_loc: (29,16)-(29,17) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (29,1)-(29,2) = "{"
+ │ └── closing_loc: (29,18)-(29,19) = "}"
+ ├── @ CallNode (location: (31,0)-(31,16))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (31,0)-(31,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (31,1)-(31,16))
+ │ ├── locals: [:a, :s, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (31,3)-(31,14))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (31,4)-(31,13))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (31,4)-(31,5))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ RestParameterNode (location: (31,7)-(31,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :s
+ │ │ │ │ ├── name_loc: (31,8)-(31,9) = "s"
+ │ │ │ │ └── operator_loc: (31,7)-(31,8) = "*"
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockParameterNode (location: (31,11)-(31,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (31,12)-(31,13) = "b"
+ │ │ │ └── operator_loc: (31,11)-(31,12) = "&"
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (31,3)-(31,4) = "|"
+ │ │ └── closing_loc: (31,13)-(31,14) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (31,1)-(31,2) = "{"
+ │ └── closing_loc: (31,15)-(31,16) = "}"
+ ├── @ CallNode (location: (33,0)-(33,12))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (33,0)-(33,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (33,1)-(33,12))
+ │ ├── locals: [:a, :s]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (33,3)-(33,10))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (33,4)-(33,9))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (33,4)-(33,5))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ RestParameterNode (location: (33,7)-(33,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :s
+ │ │ │ │ ├── name_loc: (33,8)-(33,9) = "s"
+ │ │ │ │ └── operator_loc: (33,7)-(33,8) = "*"
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (33,3)-(33,4) = "|"
+ │ │ └── closing_loc: (33,9)-(33,10) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (33,1)-(33,2) = "{"
+ │ └── closing_loc: (33,11)-(33,12) = "}"
+ ├── @ CallNode (location: (35,0)-(35,11))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (35,0)-(35,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (35,1)-(35,11))
+ │ ├── locals: [:a]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (35,3)-(35,9))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (35,4)-(35,8))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (35,4)-(35,5))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ RestParameterNode (location: (35,7)-(35,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: ∅
+ │ │ │ │ ├── name_loc: ∅
+ │ │ │ │ └── operator_loc: (35,7)-(35,8) = "*"
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (35,3)-(35,4) = "|"
+ │ │ └── closing_loc: (35,8)-(35,9) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (35,1)-(35,2) = "{"
+ │ └── closing_loc: (35,10)-(35,11) = "}"
+ ├── @ CallNode (location: (37,0)-(37,12))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (37,0)-(37,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (37,1)-(37,12))
+ │ ├── locals: [:a, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (37,3)-(37,10))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (37,4)-(37,9))
+ │ │ │ ├── requireds: (length: 2)
+ │ │ │ │ ├── @ RequiredParameterNode (location: (37,4)-(37,5))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :a
+ │ │ │ │ └── @ RequiredParameterNode (location: (37,7)-(37,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :b
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ ImplicitRestNode (location: (37,8)-(37,9))
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (37,3)-(37,4) = "|"
+ │ │ └── closing_loc: (37,9)-(37,10) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (37,1)-(37,2) = "{"
+ │ └── closing_loc: (37,11)-(37,12) = "}"
+ ├── @ CallNode (location: (39,0)-(39,11))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (39,0)-(39,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (39,1)-(39,11))
+ │ ├── locals: [:a, :c]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (39,3)-(39,9))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (39,4)-(39,8))
+ │ │ │ ├── requireds: (length: 2)
+ │ │ │ │ ├── @ RequiredParameterNode (location: (39,4)-(39,5))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :a
+ │ │ │ │ └── @ RequiredParameterNode (location: (39,7)-(39,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :c
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (39,3)-(39,4) = "|"
+ │ │ └── closing_loc: (39,8)-(39,9) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (39,1)-(39,2) = "{"
+ │ └── closing_loc: (39,10)-(39,11) = "}"
+ ├── @ CallNode (location: (41,0)-(41,17))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (41,0)-(41,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (41,1)-(41,17))
+ │ ├── locals: [:a, :o, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (41,3)-(41,15))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (41,4)-(41,14))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (41,4)-(41,5))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 1)
+ │ │ │ │ └── @ OptionalParameterNode (location: (41,7)-(41,10))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :o
+ │ │ │ │ ├── name_loc: (41,7)-(41,8) = "o"
+ │ │ │ │ ├── operator_loc: (41,8)-(41,9) = "="
+ │ │ │ │ └── value:
+ │ │ │ │ @ IntegerNode (location: (41,9)-(41,10))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockParameterNode (location: (41,12)-(41,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (41,13)-(41,14) = "b"
+ │ │ │ └── operator_loc: (41,12)-(41,13) = "&"
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (41,3)-(41,4) = "|"
+ │ │ └── closing_loc: (41,14)-(41,15) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (41,1)-(41,2) = "{"
+ │ └── closing_loc: (41,16)-(41,17) = "}"
+ ├── @ CallNode (location: (43,0)-(43,24))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (43,0)-(43,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (43,1)-(43,24))
+ │ ├── locals: [:a, :o, :r, :p, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (43,3)-(43,22))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (43,4)-(43,21))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (43,4)-(43,5))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 1)
+ │ │ │ │ └── @ OptionalParameterNode (location: (43,7)-(43,10))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :o
+ │ │ │ │ ├── name_loc: (43,7)-(43,8) = "o"
+ │ │ │ │ ├── operator_loc: (43,8)-(43,9) = "="
+ │ │ │ │ └── value:
+ │ │ │ │ @ IntegerNode (location: (43,9)-(43,10))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── rest:
+ │ │ │ │ @ RestParameterNode (location: (43,12)-(43,14))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :r
+ │ │ │ │ ├── name_loc: (43,13)-(43,14) = "r"
+ │ │ │ │ └── operator_loc: (43,12)-(43,13) = "*"
+ │ │ │ ├── posts: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (43,16)-(43,17))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :p
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockParameterNode (location: (43,19)-(43,21))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (43,20)-(43,21) = "b"
+ │ │ │ └── operator_loc: (43,19)-(43,20) = "&"
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (43,3)-(43,4) = "|"
+ │ │ └── closing_loc: (43,21)-(43,22) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (43,1)-(43,2) = "{"
+ │ └── closing_loc: (43,23)-(43,24) = "}"
+ ├── @ CallNode (location: (45,0)-(45,27))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (45,0)-(45,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (45,1)-(45,27))
+ │ ├── locals: [:a, :o, :o1, :r, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (45,3)-(45,25))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (45,4)-(45,24))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (45,4)-(45,5))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 2)
+ │ │ │ │ ├── @ OptionalParameterNode (location: (45,7)-(45,10))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── name: :o
+ │ │ │ │ │ ├── name_loc: (45,7)-(45,8) = "o"
+ │ │ │ │ │ ├── operator_loc: (45,8)-(45,9) = "="
+ │ │ │ │ │ └── value:
+ │ │ │ │ │ @ IntegerNode (location: (45,9)-(45,10))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ └── @ OptionalParameterNode (location: (45,12)-(45,16))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :o1
+ │ │ │ │ ├── name_loc: (45,12)-(45,14) = "o1"
+ │ │ │ │ ├── operator_loc: (45,14)-(45,15) = "="
+ │ │ │ │ └── value:
+ │ │ │ │ @ IntegerNode (location: (45,15)-(45,16))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ ├── rest:
+ │ │ │ │ @ RestParameterNode (location: (45,18)-(45,20))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :r
+ │ │ │ │ ├── name_loc: (45,19)-(45,20) = "r"
+ │ │ │ │ └── operator_loc: (45,18)-(45,19) = "*"
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockParameterNode (location: (45,22)-(45,24))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (45,23)-(45,24) = "b"
+ │ │ │ └── operator_loc: (45,22)-(45,23) = "&"
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (45,3)-(45,4) = "|"
+ │ │ └── closing_loc: (45,24)-(45,25) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (45,1)-(45,2) = "{"
+ │ └── closing_loc: (45,26)-(45,27) = "}"
+ ├── @ CallNode (location: (47,0)-(47,20))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (47,0)-(47,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (47,1)-(47,20))
+ │ ├── locals: [:a, :o, :p, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (47,3)-(47,18))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (47,4)-(47,17))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (47,4)-(47,5))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 1)
+ │ │ │ │ └── @ OptionalParameterNode (location: (47,7)-(47,10))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :o
+ │ │ │ │ ├── name_loc: (47,7)-(47,8) = "o"
+ │ │ │ │ ├── operator_loc: (47,8)-(47,9) = "="
+ │ │ │ │ └── value:
+ │ │ │ │ @ IntegerNode (location: (47,9)-(47,10))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (47,12)-(47,13))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :p
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockParameterNode (location: (47,15)-(47,17))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (47,16)-(47,17) = "b"
+ │ │ │ └── operator_loc: (47,15)-(47,16) = "&"
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (47,3)-(47,4) = "|"
+ │ │ └── closing_loc: (47,17)-(47,18) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (47,1)-(47,2) = "{"
+ │ └── closing_loc: (47,19)-(47,20) = "}"
+ ├── @ CallNode (location: (49,0)-(49,9))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (49,0)-(49,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (49,1)-(49,9))
+ │ ├── locals: [:a]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (49,3)-(49,7))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (49,4)-(49,6))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (49,4)-(49,5))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ ImplicitRestNode (location: (49,5)-(49,6))
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (49,3)-(49,4) = "|"
+ │ │ └── closing_loc: (49,6)-(49,7) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (49,1)-(49,2) = "{"
+ │ └── closing_loc: (49,8)-(49,9) = "}"
+ ├── @ CallNode (location: (51,0)-(51,8))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (51,0)-(51,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (51,1)-(51,8))
+ │ ├── locals: [:a]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (51,3)-(51,6))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (51,4)-(51,5))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (51,4)-(51,5))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (51,3)-(51,4) = "|"
+ │ │ └── closing_loc: (51,5)-(51,6) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (51,1)-(51,2) = "{"
+ │ └── closing_loc: (51,7)-(51,8) = "}"
+ ├── @ CallNode (location: (53,0)-(53,8))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (53,0)-(53,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (53,1)-(53,8))
+ │ ├── locals: [:a]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (53,3)-(53,6))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (53,4)-(53,5))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (53,4)-(53,5))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (53,3)-(53,4) = "|"
+ │ │ └── closing_loc: (53,5)-(53,6) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (53,1)-(53,2) = "{"
+ │ └── closing_loc: (53,7)-(53,8) = "}"
+ ├── @ CallNode (location: (55,0)-(55,8))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (55,0)-(55,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (55,1)-(55,8))
+ │ ├── locals: [:a]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (55,3)-(55,6))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (55,4)-(55,5))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (55,4)-(55,5))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (55,3)-(55,4) = "|"
+ │ │ └── closing_loc: (55,5)-(55,6) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (55,1)-(55,2) = "{"
+ │ └── closing_loc: (55,7)-(55,8) = "}"
+ ├── @ CallNode (location: (57,0)-(57,17))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (57,0)-(57,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (57,1)-(57,17))
+ │ ├── locals: [:foo, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (57,3)-(57,15))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (57,4)-(57,14))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 1)
+ │ │ │ │ └── @ OptionalKeywordParameterNode (location: (57,4)-(57,10))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :foo
+ │ │ │ │ ├── name_loc: (57,4)-(57,8) = "foo:"
+ │ │ │ │ └── value:
+ │ │ │ │ @ IntegerNode (location: (57,9)-(57,10))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockParameterNode (location: (57,12)-(57,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (57,13)-(57,14) = "b"
+ │ │ │ └── operator_loc: (57,12)-(57,13) = "&"
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (57,3)-(57,4) = "|"
+ │ │ └── closing_loc: (57,14)-(57,15) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (57,1)-(57,2) = "{"
+ │ └── closing_loc: (57,16)-(57,17) = "}"
+ ├── @ CallNode (location: (59,0)-(59,32))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (59,0)-(59,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (59,1)-(59,32))
+ │ ├── locals: [:foo, :bar, :baz, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (59,3)-(59,30))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (59,4)-(59,29))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 2)
+ │ │ │ │ ├── @ OptionalKeywordParameterNode (location: (59,4)-(59,10))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── name: :foo
+ │ │ │ │ │ ├── name_loc: (59,4)-(59,8) = "foo:"
+ │ │ │ │ │ └── value:
+ │ │ │ │ │ @ IntegerNode (location: (59,9)-(59,10))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ └── @ OptionalKeywordParameterNode (location: (59,12)-(59,18))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── name_loc: (59,12)-(59,16) = "bar:"
+ │ │ │ │ └── value:
+ │ │ │ │ @ IntegerNode (location: (59,17)-(59,18))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ ├── keyword_rest:
+ │ │ │ │ @ KeywordRestParameterNode (location: (59,20)-(59,25))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :baz
+ │ │ │ │ ├── name_loc: (59,22)-(59,25) = "baz"
+ │ │ │ │ └── operator_loc: (59,20)-(59,22) = "**"
+ │ │ │ └── block:
+ │ │ │ @ BlockParameterNode (location: (59,27)-(59,29))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (59,28)-(59,29) = "b"
+ │ │ │ └── operator_loc: (59,27)-(59,28) = "&"
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (59,3)-(59,4) = "|"
+ │ │ └── closing_loc: (59,29)-(59,30) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (59,1)-(59,2) = "{"
+ │ └── closing_loc: (59,31)-(59,32) = "}"
+ ├── @ CallNode (location: (61,0)-(61,11))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (61,0)-(61,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (61,1)-(61,11))
+ │ ├── locals: [:foo]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (61,3)-(61,9))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (61,4)-(61,8))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 1)
+ │ │ │ │ └── @ RequiredKeywordParameterNode (location: (61,4)-(61,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :foo
+ │ │ │ │ └── name_loc: (61,4)-(61,8) = "foo:"
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (61,3)-(61,4) = "|"
+ │ │ └── closing_loc: (61,8)-(61,9) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (61,1)-(61,2) = "{"
+ │ └── closing_loc: (61,10)-(61,11) = "}"
+ ├── @ CallNode (location: (63,0)-(63,14))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (63,0)-(63,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (63,1)-(63,14))
+ │ ├── locals: [:o, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (63,3)-(63,12))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (63,4)-(63,11))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 1)
+ │ │ │ │ └── @ OptionalParameterNode (location: (63,4)-(63,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :o
+ │ │ │ │ ├── name_loc: (63,4)-(63,5) = "o"
+ │ │ │ │ ├── operator_loc: (63,5)-(63,6) = "="
+ │ │ │ │ └── value:
+ │ │ │ │ @ IntegerNode (location: (63,6)-(63,7))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockParameterNode (location: (63,9)-(63,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (63,10)-(63,11) = "b"
+ │ │ │ └── operator_loc: (63,9)-(63,10) = "&"
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (63,3)-(63,4) = "|"
+ │ │ └── closing_loc: (63,11)-(63,12) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (63,1)-(63,2) = "{"
+ │ └── closing_loc: (63,13)-(63,14) = "}"
+ ├── @ CallNode (location: (65,0)-(65,18))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (65,0)-(65,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (65,1)-(65,18))
+ │ ├── locals: [:o, :r, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (65,3)-(65,16))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (65,4)-(65,15))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 1)
+ │ │ │ │ └── @ OptionalParameterNode (location: (65,4)-(65,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :o
+ │ │ │ │ ├── name_loc: (65,4)-(65,5) = "o"
+ │ │ │ │ ├── operator_loc: (65,5)-(65,6) = "="
+ │ │ │ │ └── value:
+ │ │ │ │ @ IntegerNode (location: (65,6)-(65,7))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── rest:
+ │ │ │ │ @ RestParameterNode (location: (65,9)-(65,11))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :r
+ │ │ │ │ ├── name_loc: (65,10)-(65,11) = "r"
+ │ │ │ │ └── operator_loc: (65,9)-(65,10) = "*"
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockParameterNode (location: (65,13)-(65,15))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (65,14)-(65,15) = "b"
+ │ │ │ └── operator_loc: (65,13)-(65,14) = "&"
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (65,3)-(65,4) = "|"
+ │ │ └── closing_loc: (65,15)-(65,16) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (65,1)-(65,2) = "{"
+ │ └── closing_loc: (65,17)-(65,18) = "}"
+ ├── @ CallNode (location: (67,0)-(67,21))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (67,0)-(67,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (67,1)-(67,21))
+ │ ├── locals: [:o, :r, :p, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (67,3)-(67,19))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (67,4)-(67,18))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 1)
+ │ │ │ │ └── @ OptionalParameterNode (location: (67,4)-(67,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :o
+ │ │ │ │ ├── name_loc: (67,4)-(67,5) = "o"
+ │ │ │ │ ├── operator_loc: (67,5)-(67,6) = "="
+ │ │ │ │ └── value:
+ │ │ │ │ @ IntegerNode (location: (67,6)-(67,7))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── rest:
+ │ │ │ │ @ RestParameterNode (location: (67,9)-(67,11))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :r
+ │ │ │ │ ├── name_loc: (67,10)-(67,11) = "r"
+ │ │ │ │ └── operator_loc: (67,9)-(67,10) = "*"
+ │ │ │ ├── posts: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (67,13)-(67,14))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :p
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockParameterNode (location: (67,16)-(67,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (67,17)-(67,18) = "b"
+ │ │ │ └── operator_loc: (67,16)-(67,17) = "&"
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (67,3)-(67,4) = "|"
+ │ │ └── closing_loc: (67,18)-(67,19) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (67,1)-(67,2) = "{"
+ │ └── closing_loc: (67,20)-(67,21) = "}"
+ ├── @ CallNode (location: (69,0)-(69,17))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (69,0)-(69,1) = "f"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (69,1)-(69,17))
+ │ ├── locals: [:o, :p, :b]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (69,3)-(69,15))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (69,4)-(69,14))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 1)
+ │ │ │ │ └── @ OptionalParameterNode (location: (69,4)-(69,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :o
+ │ │ │ │ ├── name_loc: (69,4)-(69,5) = "o"
+ │ │ │ │ ├── operator_loc: (69,5)-(69,6) = "="
+ │ │ │ │ └── value:
+ │ │ │ │ @ IntegerNode (location: (69,6)-(69,7))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (69,9)-(69,10))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :p
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockParameterNode (location: (69,12)-(69,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (69,13)-(69,14) = "b"
+ │ │ │ └── operator_loc: (69,12)-(69,13) = "&"
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (69,3)-(69,4) = "|"
+ │ │ └── closing_loc: (69,14)-(69,15) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (69,1)-(69,2) = "{"
+ │ └── closing_loc: (69,16)-(69,17) = "}"
+ └── @ CallNode (location: (71,0)-(71,7))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (71,0)-(71,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (71,1)-(71,7))
+ ├── locals: []
+ ├── parameters:
+ │ @ BlockParametersNode (location: (71,3)-(71,5))
+ │ ├── parameters: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (71,3)-(71,4) = "|"
+ │ └── closing_loc: (71,4)-(71,5) = "|"
+ ├── body: ∅
+ ├── opening_loc: (71,1)-(71,2) = "{"
+ └── closing_loc: (71,6)-(71,7) = "}"
diff --git a/test/prism/snapshots/whitequark/bug_435.txt b/test/prism/snapshots/whitequark/bug_435.txt
new file mode 100644
index 0000000000..42f9a49c5c
--- /dev/null
+++ b/test/prism/snapshots/whitequark/bug_435.txt
@@ -0,0 +1,39 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(1,14))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,1) = "\""
+ ├── parts: (length: 1)
+ │ └── @ EmbeddedStatementsNode (location: (1,1)-(1,13))
+ │ ├── opening_loc: (1,1)-(1,3) = "\#{"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,3)-(1,12))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LambdaNode (location: (1,3)-(1,12))
+ │ │ ├── locals: [:foo]
+ │ │ ├── operator_loc: (1,3)-(1,5) = "->"
+ │ │ ├── opening_loc: (1,10)-(1,11) = "{"
+ │ │ ├── closing_loc: (1,11)-(1,12) = "}"
+ │ │ ├── parameters:
+ │ │ │ @ BlockParametersNode (location: (1,6)-(1,9))
+ │ │ │ ├── parameters:
+ │ │ │ │ @ ParametersNode (location: (1,6)-(1,9))
+ │ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ │ └── @ RequiredParameterNode (location: (1,6)-(1,9))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :foo
+ │ │ │ │ ├── optionals: (length: 0)
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ ├── keywords: (length: 0)
+ │ │ │ │ ├── keyword_rest: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── locals: (length: 0)
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ └── closing_loc: ∅
+ │ │ └── body: ∅
+ │ └── closing_loc: (1,12)-(1,13) = "}"
+ └── closing_loc: (1,13)-(1,14) = "\""
diff --git a/test/prism/snapshots/whitequark/bug_447.txt b/test/prism/snapshots/whitequark/bug_447.txt
new file mode 100644
index 0000000000..7291d01175
--- /dev/null
+++ b/test/prism/snapshots/whitequark/bug_447.txt
@@ -0,0 +1,56 @@
+@ ProgramNode (location: (1,0)-(3,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,14))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,11))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (1,0)-(1,1) = "m"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,2)-(1,4))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ ArrayNode (location: (1,2)-(1,4))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 0)
+ │ │ ├── opening_loc: (1,2)-(1,3) = "["
+ │ │ └── closing_loc: (1,3)-(1,4) = "]"
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,5)-(1,11))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,5)-(1,7) = "do"
+ │ └── closing_loc: (1,8)-(1,11) = "end"
+ └── @ CallNode (location: (3,0)-(3,14))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :m
+ ├── message_loc: (3,0)-(3,1) = "m"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (3,2)-(3,7))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ ArrayNode (location: (3,2)-(3,4))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 0)
+ │ │ ├── opening_loc: (3,2)-(3,3) = "["
+ │ │ └── closing_loc: (3,3)-(3,4) = "]"
+ │ └── @ IntegerNode (location: (3,6)-(3,7))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (3,8)-(3,14))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── opening_loc: (3,8)-(3,10) = "do"
+ └── closing_loc: (3,11)-(3,14) = "end"
diff --git a/test/prism/snapshots/whitequark/bug_452.txt b/test/prism/snapshots/whitequark/bug_452.txt
new file mode 100644
index 0000000000..e1ea52fbd1
--- /dev/null
+++ b/test/prism/snapshots/whitequark/bug_452.txt
@@ -0,0 +1,63 @@
+@ ProgramNode (location: (1,0)-(1,37))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,37))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,21))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :td
+ │ ├── message_loc: (1,0)-(1,2) = "td"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,3)-(1,21))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (1,3)-(1,21))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ ParenthesesNode (location: (1,3)-(1,10))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (1,4)-(1,9))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (1,4)-(1,9))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1500
+ │ │ │ ├── opening_loc: (1,3)-(1,4) = "("
+ │ │ │ └── closing_loc: (1,9)-(1,10) = ")"
+ │ │ ├── call_operator_loc: (1,10)-(1,11) = "."
+ │ │ ├── name: :toString
+ │ │ ├── message_loc: (1,11)-(1,19) = "toString"
+ │ │ ├── opening_loc: (1,19)-(1,20) = "("
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: (1,20)-(1,21) = ")"
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (1,23)-(1,37))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,23)-(1,25))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :td
+ │ ├── message_loc: (1,23)-(1,25) = "td"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,25)-(1,26) = "."
+ ├── name: :num
+ ├── message_loc: (1,26)-(1,29) = "num"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,30)-(1,37))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── opening_loc: (1,30)-(1,32) = "do"
+ └── closing_loc: (1,34)-(1,37) = "end"
diff --git a/test/prism/snapshots/whitequark/bug_466.txt b/test/prism/snapshots/whitequark/bug_466.txt
new file mode 100644
index 0000000000..4167c223f2
--- /dev/null
+++ b/test/prism/snapshots/whitequark/bug_466.txt
@@ -0,0 +1,70 @@
+@ ProgramNode (location: (1,0)-(1,27))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,27))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,27))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :foo
+ ├── message_loc: (1,0)-(1,3) = "foo"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,19))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ InterpolatedStringNode (location: (1,4)-(1,19))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,4)-(1,5) = "\""
+ │ ├── parts: (length: 1)
+ │ │ └── @ EmbeddedStatementsNode (location: (1,5)-(1,18))
+ │ │ ├── opening_loc: (1,5)-(1,7) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,7)-(1,17))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (1,7)-(1,17))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ ParenthesesNode (location: (1,7)-(1,12))
+ │ │ │ │ ├── body:
+ │ │ │ │ │ @ StatementsNode (location: (1,8)-(1,11))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (1,8)-(1,11))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── receiver:
+ │ │ │ │ │ │ @ IntegerNode (location: (1,8)-(1,9))
+ │ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ │ └── value: 1
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :+
+ │ │ │ │ │ ├── message_loc: (1,9)-(1,10) = "+"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments:
+ │ │ │ │ │ │ @ ArgumentsNode (location: (1,10)-(1,11))
+ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ │ └── @ IntegerNode (location: (1,10)-(1,11))
+ │ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ │ └── value: 1
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── opening_loc: (1,7)-(1,8) = "("
+ │ │ │ │ └── closing_loc: (1,11)-(1,12) = ")"
+ │ │ │ ├── call_operator_loc: (1,12)-(1,13) = "."
+ │ │ │ ├── name: :to_i
+ │ │ │ ├── message_loc: (1,13)-(1,17) = "to_i"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── closing_loc: (1,17)-(1,18) = "}"
+ │ └── closing_loc: (1,18)-(1,19) = "\""
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,20)-(1,27))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── opening_loc: (1,20)-(1,22) = "do"
+ └── closing_loc: (1,24)-(1,27) = "end"
diff --git a/test/prism/snapshots/whitequark/bug_473.txt b/test/prism/snapshots/whitequark/bug_473.txt
new file mode 100644
index 0000000000..028b6a517c
--- /dev/null
+++ b/test/prism/snapshots/whitequark/bug_473.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,9))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :m
+ ├── message_loc: (1,0)-(1,1) = "m"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,9))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ InterpolatedStringNode (location: (1,2)-(1,9))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,2)-(1,3) = "\""
+ │ ├── parts: (length: 1)
+ │ │ └── @ EmbeddedStatementsNode (location: (1,3)-(1,8))
+ │ │ ├── opening_loc: (1,3)-(1,5) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,5)-(1,7))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ ArrayNode (location: (1,5)-(1,7))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 0)
+ │ │ │ ├── opening_loc: (1,5)-(1,6) = "["
+ │ │ │ └── closing_loc: (1,6)-(1,7) = "]"
+ │ │ └── closing_loc: (1,7)-(1,8) = "}"
+ │ └── closing_loc: (1,8)-(1,9) = "\""
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/bug_480.txt b/test/prism/snapshots/whitequark/bug_480.txt
new file mode 100644
index 0000000000..ed6ba40795
--- /dev/null
+++ b/test/prism/snapshots/whitequark/bug_480.txt
@@ -0,0 +1,37 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,12))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :m
+ ├── message_loc: (1,0)-(1,1) = "m"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,12))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ InterpolatedStringNode (location: (1,2)-(1,12))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,2)-(1,3) = "\""
+ │ ├── parts: (length: 2)
+ │ │ ├── @ EmbeddedStatementsNode (location: (1,3)-(1,6))
+ │ │ │ ├── opening_loc: (1,3)-(1,5) = "\#{"
+ │ │ │ ├── statements: ∅
+ │ │ │ └── closing_loc: (1,5)-(1,6) = "}"
+ │ │ └── @ EmbeddedStatementsNode (location: (1,6)-(1,11))
+ │ │ ├── opening_loc: (1,6)-(1,8) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,8)-(1,10))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ ParenthesesNode (location: (1,8)-(1,10))
+ │ │ │ ├── body: ∅
+ │ │ │ ├── opening_loc: (1,8)-(1,9) = "("
+ │ │ │ └── closing_loc: (1,9)-(1,10) = ")"
+ │ │ └── closing_loc: (1,10)-(1,11) = "}"
+ │ └── closing_loc: (1,11)-(1,12) = "\""
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/bug_481.txt b/test/prism/snapshots/whitequark/bug_481.txt
new file mode 100644
index 0000000000..0b093d483c
--- /dev/null
+++ b/test/prism/snapshots/whitequark/bug_481.txt
@@ -0,0 +1,50 @@
+@ ProgramNode (location: (1,0)-(1,28))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,28))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,14))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (1,0)-(1,1) = "m"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,2)-(1,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ DefNode (location: (1,2)-(1,14))
+ │ │ ├── name: :x
+ │ │ ├── name_loc: (1,6)-(1,7) = "x"
+ │ │ ├── receiver: ∅
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── locals: []
+ │ │ ├── def_keyword_loc: (1,2)-(1,5) = "def"
+ │ │ ├── operator_loc: ∅
+ │ │ ├── lparen_loc: (1,7)-(1,8) = "("
+ │ │ ├── rparen_loc: (1,8)-(1,9) = ")"
+ │ │ ├── equal_loc: ∅
+ │ │ └── end_keyword_loc: (1,11)-(1,14) = "end"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (1,16)-(1,28))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ IntegerNode (location: (1,16)-(1,17))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── call_operator_loc: (1,17)-(1,18) = "."
+ ├── name: :tap
+ ├── message_loc: (1,18)-(1,21) = "tap"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,22)-(1,28))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── opening_loc: (1,22)-(1,24) = "do"
+ └── closing_loc: (1,25)-(1,28) = "end"
diff --git a/test/prism/snapshots/whitequark/bug_ascii_8bit_in_literal.txt b/test/prism/snapshots/whitequark/bug_ascii_8bit_in_literal.txt
new file mode 100644
index 0000000000..7aa8694f66
--- /dev/null
+++ b/test/prism/snapshots/whitequark/bug_ascii_8bit_in_literal.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (2,9)-(2,75))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (2,9)-(2,75))
+ └── body: (length: 1)
+ └── @ StringNode (location: (2,9)-(2,75))
+ ├── flags: forced_utf8_encoding
+ ├── opening_loc: (2,9)-(2,10) = "\""
+ ├── content_loc: (2,10)-(2,74) = "\\xD0\\xBF\\xD1\\x80\\xD0\\xBE\\xD0\\xB2\\xD0\\xB5\\xD1\\x80\\xD0\\xBA\\xD0\\xB0"
+ ├── closing_loc: (2,74)-(2,75) = "\""
+ └── unescaped: "проверка"
diff --git a/test/prism/snapshots/whitequark/bug_cmd_string_lookahead.txt b/test/prism/snapshots/whitequark/bug_cmd_string_lookahead.txt
new file mode 100644
index 0000000000..50c988fa1f
--- /dev/null
+++ b/test/prism/snapshots/whitequark/bug_cmd_string_lookahead.txt
@@ -0,0 +1,30 @@
+@ ProgramNode (location: (1,0)-(1,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,17))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,17))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :desc
+ ├── message_loc: (1,0)-(1,4) = "desc"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,5)-(1,10))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ StringNode (location: (1,5)-(1,10))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,5)-(1,6) = "\""
+ │ ├── content_loc: (1,6)-(1,9) = "foo"
+ │ ├── closing_loc: (1,9)-(1,10) = "\""
+ │ └── unescaped: "foo"
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,11)-(1,17))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── opening_loc: (1,11)-(1,13) = "do"
+ └── closing_loc: (1,14)-(1,17) = "end"
diff --git a/test/prism/snapshots/whitequark/bug_cmdarg.txt b/test/prism/snapshots/whitequark/bug_cmdarg.txt
new file mode 100644
index 0000000000..509dd7e818
--- /dev/null
+++ b/test/prism/snapshots/whitequark/bug_cmdarg.txt
@@ -0,0 +1,106 @@
+@ ProgramNode (location: (1,0)-(5,26))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,26))
+ └── body: (length: 3)
+ ├── @ CallNode (location: (1,0)-(1,15))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :assert
+ │ ├── message_loc: (1,0)-(1,6) = "assert"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,7)-(1,15))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (1,7)-(1,15))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (1,7)-(1,15))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (1,7)-(1,10))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (1,7)-(1,9) = "do"
+ │ │ │ ├── closing_loc: (1,9)-(1,10) = ":"
+ │ │ │ └── unescaped: "do"
+ │ │ ├── value:
+ │ │ │ @ TrueNode (location: (1,11)-(1,15))
+ │ │ └── operator_loc: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (3,0)-(3,11))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :assert
+ │ ├── message_loc: (3,0)-(3,6) = "assert"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,7)-(3,11))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (3,7)-(3,11))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :dogs
+ │ │ ├── message_loc: (3,7)-(3,11) = "dogs"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (5,0)-(5,26))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (5,0)-(5,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (5,2)-(5,26))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ KeywordHashNode (location: (5,2)-(5,26))
+ │ ├── flags: symbol_keys
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (5,2)-(5,26))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (5,2)-(5,4))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (5,2)-(5,3) = "x"
+ │ │ ├── closing_loc: (5,3)-(5,4) = ":"
+ │ │ └── unescaped: "x"
+ │ ├── value:
+ │ │ @ LambdaNode (location: (5,5)-(5,26))
+ │ │ ├── locals: []
+ │ │ ├── operator_loc: (5,5)-(5,7) = "->"
+ │ │ ├── opening_loc: (5,8)-(5,10) = "do"
+ │ │ ├── closing_loc: (5,23)-(5,26) = "end"
+ │ │ ├── parameters: ∅
+ │ │ └── body:
+ │ │ @ StatementsNode (location: (5,11)-(5,22))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (5,11)-(5,22))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :meth
+ │ │ ├── message_loc: (5,11)-(5,15) = "meth"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (5,16)-(5,22))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (5,16)-(5,18) = "do"
+ │ │ └── closing_loc: (5,19)-(5,22) = "end"
+ │ └── operator_loc: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/bug_def_no_paren_eql_begin.txt b/test/prism/snapshots/whitequark/bug_def_no_paren_eql_begin.txt
new file mode 100644
index 0000000000..1b45d0132b
--- /dev/null
+++ b/test/prism/snapshots/whitequark/bug_def_no_paren_eql_begin.txt
@@ -0,0 +1,18 @@
+@ ProgramNode (location: (1,0)-(4,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(4,3))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(4,3))
+ ├── name: :foo
+ ├── name_loc: (1,4)-(1,7) = "foo"
+ ├── receiver: ∅
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (4,0)-(4,3) = "end"
diff --git a/test/prism/snapshots/whitequark/bug_do_block_in_call_args.txt b/test/prism/snapshots/whitequark/bug_do_block_in_call_args.txt
new file mode 100644
index 0000000000..9b95adda35
--- /dev/null
+++ b/test/prism/snapshots/whitequark/bug_do_block_in_call_args.txt
@@ -0,0 +1,50 @@
+@ ProgramNode (location: (1,0)-(1,33))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,33))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,33))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :bar
+ ├── message_loc: (1,0)-(1,3) = "bar"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,33))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ DefNode (location: (1,4)-(1,33))
+ │ ├── name: :foo
+ │ ├── name_loc: (1,8)-(1,11) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,13)-(1,29))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,13)-(1,29))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver:
+ │ │ │ @ SelfNode (location: (1,13)-(1,17))
+ │ │ ├── call_operator_loc: (1,17)-(1,18) = "."
+ │ │ ├── name: :each
+ │ │ ├── message_loc: (1,18)-(1,22) = "each"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (1,23)-(1,29))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (1,23)-(1,25) = "do"
+ │ │ └── closing_loc: (1,26)-(1,29) = "end"
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (1,4)-(1,7) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (1,30)-(1,33) = "end"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/bug_do_block_in_cmdarg.txt b/test/prism/snapshots/whitequark/bug_do_block_in_cmdarg.txt
new file mode 100644
index 0000000000..3946bc1a4a
--- /dev/null
+++ b/test/prism/snapshots/whitequark/bug_do_block_in_cmdarg.txt
@@ -0,0 +1,40 @@
+@ ProgramNode (location: (1,0)-(1,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,17))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,17))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :tap
+ ├── message_loc: (1,0)-(1,3) = "tap"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,17))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ ParenthesesNode (location: (1,4)-(1,17))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,5)-(1,16))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,5)-(1,16))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :proc
+ │ │ ├── message_loc: (1,5)-(1,9) = "proc"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (1,10)-(1,16))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (1,10)-(1,12) = "do"
+ │ │ └── closing_loc: (1,13)-(1,16) = "end"
+ │ ├── opening_loc: (1,4)-(1,5) = "("
+ │ └── closing_loc: (1,16)-(1,17) = ")"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/bug_do_block_in_hash_brace.txt b/test/prism/snapshots/whitequark/bug_do_block_in_hash_brace.txt
new file mode 100644
index 0000000000..183d465439
--- /dev/null
+++ b/test/prism/snapshots/whitequark/bug_do_block_in_hash_brace.txt
@@ -0,0 +1,383 @@
+@ ProgramNode (location: (1,0)-(9,52))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(9,52))
+ └── body: (length: 5)
+ ├── @ CallNode (location: (1,0)-(1,42))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :p
+ │ ├── message_loc: (1,0)-(1,1) = "p"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,2)-(1,42))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ SymbolNode (location: (1,2)-(1,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (1,2)-(1,3) = ":"
+ │ │ │ ├── value_loc: (1,3)-(1,6) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ └── @ HashNode (location: (1,8)-(1,42))
+ │ │ ├── opening_loc: (1,8)-(1,9) = "{"
+ │ │ ├── elements: (length: 2)
+ │ │ │ ├── @ AssocNode (location: (1,9)-(1,25))
+ │ │ │ │ ├── key:
+ │ │ │ │ │ @ SymbolNode (location: (1,9)-(1,13))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: (1,9)-(1,10) = "\""
+ │ │ │ │ │ ├── value_loc: (1,10)-(1,11) = "a"
+ │ │ │ │ │ ├── closing_loc: (1,11)-(1,13) = "\":"
+ │ │ │ │ │ └── unescaped: "a"
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ CallNode (location: (1,14)-(1,25))
+ │ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :proc
+ │ │ │ │ │ ├── message_loc: (1,14)-(1,18) = "proc"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block:
+ │ │ │ │ │ @ BlockNode (location: (1,19)-(1,25))
+ │ │ │ │ │ ├── locals: []
+ │ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ │ ├── body: ∅
+ │ │ │ │ │ ├── opening_loc: (1,19)-(1,21) = "do"
+ │ │ │ │ │ └── closing_loc: (1,22)-(1,25) = "end"
+ │ │ │ │ └── operator_loc: ∅
+ │ │ │ └── @ AssocNode (location: (1,27)-(1,41))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (1,27)-(1,29))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (1,27)-(1,28) = "b"
+ │ │ │ │ ├── closing_loc: (1,28)-(1,29) = ":"
+ │ │ │ │ └── unescaped: "b"
+ │ │ │ ├── value:
+ │ │ │ │ @ CallNode (location: (1,30)-(1,41))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :proc
+ │ │ │ │ ├── message_loc: (1,30)-(1,34) = "proc"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block:
+ │ │ │ │ @ BlockNode (location: (1,35)-(1,41))
+ │ │ │ │ ├── locals: []
+ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ ├── body: ∅
+ │ │ │ │ ├── opening_loc: (1,35)-(1,37) = "do"
+ │ │ │ │ └── closing_loc: (1,38)-(1,41) = "end"
+ │ │ │ └── operator_loc: ∅
+ │ │ └── closing_loc: (1,41)-(1,42) = "}"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (3,0)-(3,40))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :p
+ │ ├── message_loc: (3,0)-(3,1) = "p"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,2)-(3,40))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ SymbolNode (location: (3,2)-(3,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (3,2)-(3,3) = ":"
+ │ │ │ ├── value_loc: (3,3)-(3,6) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ └── @ HashNode (location: (3,8)-(3,40))
+ │ │ ├── opening_loc: (3,8)-(3,9) = "{"
+ │ │ ├── elements: (length: 2)
+ │ │ │ ├── @ AssocSplatNode (location: (3,9)-(3,23))
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ CallNode (location: (3,12)-(3,23))
+ │ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :proc
+ │ │ │ │ │ ├── message_loc: (3,12)-(3,16) = "proc"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block:
+ │ │ │ │ │ @ BlockNode (location: (3,17)-(3,23))
+ │ │ │ │ │ ├── locals: []
+ │ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ │ ├── body: ∅
+ │ │ │ │ │ ├── opening_loc: (3,17)-(3,19) = "do"
+ │ │ │ │ │ └── closing_loc: (3,20)-(3,23) = "end"
+ │ │ │ │ └── operator_loc: (3,9)-(3,11) = "**"
+ │ │ │ └── @ AssocNode (location: (3,25)-(3,39))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (3,25)-(3,27))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (3,25)-(3,26) = "b"
+ │ │ │ │ ├── closing_loc: (3,26)-(3,27) = ":"
+ │ │ │ │ └── unescaped: "b"
+ │ │ │ ├── value:
+ │ │ │ │ @ CallNode (location: (3,28)-(3,39))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :proc
+ │ │ │ │ ├── message_loc: (3,28)-(3,32) = "proc"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block:
+ │ │ │ │ @ BlockNode (location: (3,33)-(3,39))
+ │ │ │ │ ├── locals: []
+ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ ├── body: ∅
+ │ │ │ │ ├── opening_loc: (3,33)-(3,35) = "do"
+ │ │ │ │ └── closing_loc: (3,36)-(3,39) = "end"
+ │ │ │ └── operator_loc: ∅
+ │ │ └── closing_loc: (3,39)-(3,40) = "}"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (5,0)-(5,43))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :p
+ │ ├── message_loc: (5,0)-(5,1) = "p"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (5,2)-(5,43))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ SymbolNode (location: (5,2)-(5,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (5,2)-(5,3) = ":"
+ │ │ │ ├── value_loc: (5,3)-(5,6) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ └── @ HashNode (location: (5,8)-(5,43))
+ │ │ ├── opening_loc: (5,8)-(5,9) = "{"
+ │ │ ├── elements: (length: 2)
+ │ │ │ ├── @ AssocNode (location: (5,9)-(5,26))
+ │ │ │ │ ├── key:
+ │ │ │ │ │ @ SymbolNode (location: (5,9)-(5,11))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: (5,9)-(5,10) = ":"
+ │ │ │ │ │ ├── value_loc: (5,10)-(5,11) = "a"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "a"
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ CallNode (location: (5,15)-(5,26))
+ │ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :proc
+ │ │ │ │ │ ├── message_loc: (5,15)-(5,19) = "proc"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block:
+ │ │ │ │ │ @ BlockNode (location: (5,20)-(5,26))
+ │ │ │ │ │ ├── locals: []
+ │ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ │ ├── body: ∅
+ │ │ │ │ │ ├── opening_loc: (5,20)-(5,22) = "do"
+ │ │ │ │ │ └── closing_loc: (5,23)-(5,26) = "end"
+ │ │ │ │ └── operator_loc: (5,12)-(5,14) = "=>"
+ │ │ │ └── @ AssocNode (location: (5,28)-(5,42))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (5,28)-(5,30))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (5,28)-(5,29) = "b"
+ │ │ │ │ ├── closing_loc: (5,29)-(5,30) = ":"
+ │ │ │ │ └── unescaped: "b"
+ │ │ │ ├── value:
+ │ │ │ │ @ CallNode (location: (5,31)-(5,42))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :proc
+ │ │ │ │ ├── message_loc: (5,31)-(5,35) = "proc"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block:
+ │ │ │ │ @ BlockNode (location: (5,36)-(5,42))
+ │ │ │ │ ├── locals: []
+ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ ├── body: ∅
+ │ │ │ │ ├── opening_loc: (5,36)-(5,38) = "do"
+ │ │ │ │ └── closing_loc: (5,39)-(5,42) = "end"
+ │ │ │ └── operator_loc: ∅
+ │ │ └── closing_loc: (5,42)-(5,43) = "}"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (7,0)-(7,40))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :p
+ │ ├── message_loc: (7,0)-(7,1) = "p"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (7,2)-(7,40))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ SymbolNode (location: (7,2)-(7,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (7,2)-(7,3) = ":"
+ │ │ │ ├── value_loc: (7,3)-(7,6) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ └── @ HashNode (location: (7,8)-(7,40))
+ │ │ ├── opening_loc: (7,8)-(7,9) = "{"
+ │ │ ├── elements: (length: 2)
+ │ │ │ ├── @ AssocNode (location: (7,9)-(7,23))
+ │ │ │ │ ├── key:
+ │ │ │ │ │ @ SymbolNode (location: (7,9)-(7,11))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── value_loc: (7,9)-(7,10) = "a"
+ │ │ │ │ │ ├── closing_loc: (7,10)-(7,11) = ":"
+ │ │ │ │ │ └── unescaped: "a"
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ CallNode (location: (7,12)-(7,23))
+ │ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :proc
+ │ │ │ │ │ ├── message_loc: (7,12)-(7,16) = "proc"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block:
+ │ │ │ │ │ @ BlockNode (location: (7,17)-(7,23))
+ │ │ │ │ │ ├── locals: []
+ │ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ │ ├── body: ∅
+ │ │ │ │ │ ├── opening_loc: (7,17)-(7,19) = "do"
+ │ │ │ │ │ └── closing_loc: (7,20)-(7,23) = "end"
+ │ │ │ │ └── operator_loc: ∅
+ │ │ │ └── @ AssocNode (location: (7,25)-(7,39))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (7,25)-(7,27))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (7,25)-(7,26) = "b"
+ │ │ │ │ ├── closing_loc: (7,26)-(7,27) = ":"
+ │ │ │ │ └── unescaped: "b"
+ │ │ │ ├── value:
+ │ │ │ │ @ CallNode (location: (7,28)-(7,39))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :proc
+ │ │ │ │ ├── message_loc: (7,28)-(7,32) = "proc"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block:
+ │ │ │ │ @ BlockNode (location: (7,33)-(7,39))
+ │ │ │ │ ├── locals: []
+ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ ├── body: ∅
+ │ │ │ │ ├── opening_loc: (7,33)-(7,35) = "do"
+ │ │ │ │ └── closing_loc: (7,36)-(7,39) = "end"
+ │ │ │ └── operator_loc: ∅
+ │ │ └── closing_loc: (7,39)-(7,40) = "}"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (9,0)-(9,52))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :p
+ ├── message_loc: (9,0)-(9,1) = "p"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (9,2)-(9,52))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ SymbolNode (location: (9,2)-(9,6))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (9,2)-(9,3) = ":"
+ │ │ ├── value_loc: (9,3)-(9,6) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ └── @ HashNode (location: (9,8)-(9,52))
+ │ ├── opening_loc: (9,8)-(9,9) = "{"
+ │ ├── elements: (length: 2)
+ │ │ ├── @ AssocNode (location: (9,9)-(9,35))
+ │ │ │ ├── key:
+ │ │ │ │ @ CallNode (location: (9,9)-(9,20))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :proc
+ │ │ │ │ ├── message_loc: (9,9)-(9,13) = "proc"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block:
+ │ │ │ │ @ BlockNode (location: (9,14)-(9,20))
+ │ │ │ │ ├── locals: []
+ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ ├── body: ∅
+ │ │ │ │ ├── opening_loc: (9,14)-(9,16) = "do"
+ │ │ │ │ └── closing_loc: (9,17)-(9,20) = "end"
+ │ │ │ ├── value:
+ │ │ │ │ @ CallNode (location: (9,24)-(9,35))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :proc
+ │ │ │ │ ├── message_loc: (9,24)-(9,28) = "proc"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block:
+ │ │ │ │ @ BlockNode (location: (9,29)-(9,35))
+ │ │ │ │ ├── locals: []
+ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ ├── body: ∅
+ │ │ │ │ ├── opening_loc: (9,29)-(9,31) = "do"
+ │ │ │ │ └── closing_loc: (9,32)-(9,35) = "end"
+ │ │ │ └── operator_loc: (9,21)-(9,23) = "=>"
+ │ │ └── @ AssocNode (location: (9,37)-(9,51))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (9,37)-(9,39))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (9,37)-(9,38) = "b"
+ │ │ │ ├── closing_loc: (9,38)-(9,39) = ":"
+ │ │ │ └── unescaped: "b"
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (9,40)-(9,51))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :proc
+ │ │ │ ├── message_loc: (9,40)-(9,44) = "proc"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (9,45)-(9,51))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body: ∅
+ │ │ │ ├── opening_loc: (9,45)-(9,47) = "do"
+ │ │ │ └── closing_loc: (9,48)-(9,51) = "end"
+ │ │ └── operator_loc: ∅
+ │ └── closing_loc: (9,51)-(9,52) = "}"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/bug_heredoc_do.txt b/test/prism/snapshots/whitequark/bug_heredoc_do.txt
new file mode 100644
index 0000000000..bf4dd645fb
--- /dev/null
+++ b/test/prism/snapshots/whitequark/bug_heredoc_do.txt
@@ -0,0 +1,30 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(3,3))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,10))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ StringNode (location: (1,2)-(1,10))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,2)-(1,10) = "<<-TABLE"
+ │ ├── content_loc: (2,0)-(2,0) = ""
+ │ ├── closing_loc: (2,0)-(3,0) = "TABLE\n"
+ │ └── unescaped: ""
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,11)-(3,3))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── opening_loc: (1,11)-(1,13) = "do"
+ └── closing_loc: (3,0)-(3,3) = "end"
diff --git a/test/prism/snapshots/whitequark/bug_interp_single.txt b/test/prism/snapshots/whitequark/bug_interp_single.txt
new file mode 100644
index 0000000000..74af8607e0
--- /dev/null
+++ b/test/prism/snapshots/whitequark/bug_interp_single.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(3,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,8))
+ └── body: (length: 2)
+ ├── @ InterpolatedStringNode (location: (1,0)-(1,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,1) = "\""
+ │ ├── parts: (length: 1)
+ │ │ └── @ EmbeddedStatementsNode (location: (1,1)-(1,5))
+ │ │ ├── opening_loc: (1,1)-(1,3) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,3)-(1,4))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (1,3)-(1,4))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── closing_loc: (1,4)-(1,5) = "}"
+ │ └── closing_loc: (1,5)-(1,6) = "\""
+ └── @ ArrayNode (location: (3,0)-(3,8))
+ ├── flags: ∅
+ ├── elements: (length: 1)
+ │ └── @ InterpolatedStringNode (location: (3,3)-(3,7))
+ │ ├── flags: ∅
+ │ ├── opening_loc: ∅
+ │ ├── parts: (length: 1)
+ │ │ └── @ EmbeddedStatementsNode (location: (3,3)-(3,7))
+ │ │ ├── opening_loc: (3,3)-(3,5) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (3,5)-(3,6))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (3,5)-(3,6))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── closing_loc: (3,6)-(3,7) = "}"
+ │ └── closing_loc: ∅
+ ├── opening_loc: (3,0)-(3,3) = "%W\""
+ └── closing_loc: (3,7)-(3,8) = "\""
diff --git a/test/prism/snapshots/whitequark/bug_lambda_leakage.txt b/test/prism/snapshots/whitequark/bug_lambda_leakage.txt
new file mode 100644
index 0000000000..7178345a22
--- /dev/null
+++ b/test/prism/snapshots/whitequark/bug_lambda_leakage.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(1,19))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,19))
+ └── body: (length: 2)
+ ├── @ LambdaNode (location: (1,0)-(1,12))
+ │ ├── locals: [:scope]
+ │ ├── operator_loc: (1,0)-(1,2) = "->"
+ │ ├── opening_loc: (1,10)-(1,11) = "{"
+ │ ├── closing_loc: (1,11)-(1,12) = "}"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (1,2)-(1,9))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (1,3)-(1,8))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (1,3)-(1,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :scope
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (1,2)-(1,3) = "("
+ │ │ └── closing_loc: (1,8)-(1,9) = ")"
+ │ └── body: ∅
+ └── @ CallNode (location: (1,14)-(1,19))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :scope
+ ├── message_loc: (1,14)-(1,19) = "scope"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/bug_regex_verification.txt b/test/prism/snapshots/whitequark/bug_regex_verification.txt
new file mode 100644
index 0000000000..4464b66e38
--- /dev/null
+++ b/test/prism/snapshots/whitequark/bug_regex_verification.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ RegularExpressionNode (location: (1,0)-(1,5))
+ ├── flags: extended, forced_us_ascii_encoding
+ ├── opening_loc: (1,0)-(1,1) = "/"
+ ├── content_loc: (1,1)-(1,3) = "#)"
+ ├── closing_loc: (1,3)-(1,5) = "/x"
+ └── unescaped: "#)"
diff --git a/test/prism/snapshots/whitequark/bug_rescue_empty_else.txt b/test/prism/snapshots/whitequark/bug_rescue_empty_else.txt
new file mode 100644
index 0000000000..52734cc1e9
--- /dev/null
+++ b/test/prism/snapshots/whitequark/bug_rescue_empty_else.txt
@@ -0,0 +1,25 @@
+@ ProgramNode (location: (1,0)-(1,34))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,34))
+ └── body: (length: 1)
+ └── @ BeginNode (location: (1,0)-(1,34))
+ ├── begin_keyword_loc: (1,0)-(1,5) = "begin"
+ ├── statements: ∅
+ ├── rescue_clause:
+ │ @ RescueNode (location: (1,7)-(1,23))
+ │ ├── keyword_loc: (1,7)-(1,13) = "rescue"
+ │ ├── exceptions: (length: 1)
+ │ │ └── @ ConstantReadNode (location: (1,14)-(1,23))
+ │ │ └── name: :LoadError
+ │ ├── operator_loc: ∅
+ │ ├── reference: ∅
+ │ ├── statements: ∅
+ │ └── consequent: ∅
+ ├── else_clause:
+ │ @ ElseNode (location: (1,25)-(1,34))
+ │ ├── else_keyword_loc: (1,25)-(1,29) = "else"
+ │ ├── statements: ∅
+ │ └── end_keyword_loc: (1,31)-(1,34) = "end"
+ ├── ensure_clause: ∅
+ └── end_keyword_loc: (1,31)-(1,34) = "end"
diff --git a/test/prism/snapshots/whitequark/bug_while_not_parens_do.txt b/test/prism/snapshots/whitequark/bug_while_not_parens_do.txt
new file mode 100644
index 0000000000..aa8aa11ccc
--- /dev/null
+++ b/test/prism/snapshots/whitequark/bug_while_not_parens_do.txt
@@ -0,0 +1,28 @@
+@ ProgramNode (location: (1,0)-(1,23))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,23))
+ └── body: (length: 1)
+ └── @ WhileNode (location: (1,0)-(1,23))
+ ├── flags: ∅
+ ├── keyword_loc: (1,0)-(1,5) = "while"
+ ├── closing_loc: (1,20)-(1,23) = "end"
+ ├── predicate:
+ │ @ CallNode (location: (1,6)-(1,16))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (1,10)-(1,16))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (1,11)-(1,15))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ TrueNode (location: (1,11)-(1,15))
+ │ │ ├── opening_loc: (1,10)-(1,11) = "("
+ │ │ └── closing_loc: (1,15)-(1,16) = ")"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!
+ │ ├── message_loc: (1,6)-(1,9) = "not"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── statements: ∅
diff --git a/test/prism/snapshots/whitequark/case_cond.txt b/test/prism/snapshots/whitequark/case_cond.txt
new file mode 100644
index 0000000000..fbe17eaf8f
--- /dev/null
+++ b/test/prism/snapshots/whitequark/case_cond.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,26))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,26))
+ └── body: (length: 1)
+ └── @ CaseNode (location: (1,0)-(1,26))
+ ├── predicate: ∅
+ ├── conditions: (length: 1)
+ │ └── @ WhenNode (location: (1,6)-(1,21))
+ │ ├── keyword_loc: (1,6)-(1,10) = "when"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ CallNode (location: (1,11)-(1,14))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,11)-(1,14) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: ∅
+ │ └── statements:
+ │ @ StatementsNode (location: (1,16)-(1,21))
+ │ └── body: (length: 1)
+ │ └── @ StringNode (location: (1,16)-(1,21))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,16)-(1,17) = "'"
+ │ ├── content_loc: (1,17)-(1,20) = "foo"
+ │ ├── closing_loc: (1,20)-(1,21) = "'"
+ │ └── unescaped: "foo"
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (1,23)-(1,26) = "end"
diff --git a/test/prism/snapshots/whitequark/case_cond_else.txt b/test/prism/snapshots/whitequark/case_cond_else.txt
new file mode 100644
index 0000000000..b1aff2450e
--- /dev/null
+++ b/test/prism/snapshots/whitequark/case_cond_else.txt
@@ -0,0 +1,46 @@
+@ ProgramNode (location: (1,0)-(1,38))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,38))
+ └── body: (length: 1)
+ └── @ CaseNode (location: (1,0)-(1,38))
+ ├── predicate: ∅
+ ├── conditions: (length: 1)
+ │ └── @ WhenNode (location: (1,6)-(1,21))
+ │ ├── keyword_loc: (1,6)-(1,10) = "when"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ CallNode (location: (1,11)-(1,14))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,11)-(1,14) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: ∅
+ │ └── statements:
+ │ @ StatementsNode (location: (1,16)-(1,21))
+ │ └── body: (length: 1)
+ │ └── @ StringNode (location: (1,16)-(1,21))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,16)-(1,17) = "'"
+ │ ├── content_loc: (1,17)-(1,20) = "foo"
+ │ ├── closing_loc: (1,20)-(1,21) = "'"
+ │ └── unescaped: "foo"
+ ├── consequent:
+ │ @ ElseNode (location: (1,23)-(1,38))
+ │ ├── else_keyword_loc: (1,23)-(1,27) = "else"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,28)-(1,33))
+ │ │ └── body: (length: 1)
+ │ │ └── @ StringNode (location: (1,28)-(1,33))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,28)-(1,29) = "'"
+ │ │ ├── content_loc: (1,29)-(1,32) = "bar"
+ │ │ ├── closing_loc: (1,32)-(1,33) = "'"
+ │ │ └── unescaped: "bar"
+ │ └── end_keyword_loc: (1,35)-(1,38) = "end"
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (1,35)-(1,38) = "end"
diff --git a/test/prism/snapshots/whitequark/case_expr.txt b/test/prism/snapshots/whitequark/case_expr.txt
new file mode 100644
index 0000000000..23054ed132
--- /dev/null
+++ b/test/prism/snapshots/whitequark/case_expr.txt
@@ -0,0 +1,44 @@
+@ ProgramNode (location: (1,0)-(1,30))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,30))
+ └── body: (length: 1)
+ └── @ CaseNode (location: (1,0)-(1,30))
+ ├── predicate:
+ │ @ CallNode (location: (1,5)-(1,8))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,5)-(1,8) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── conditions: (length: 1)
+ │ └── @ WhenNode (location: (1,10)-(1,25))
+ │ ├── keyword_loc: (1,10)-(1,14) = "when"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ StringNode (location: (1,15)-(1,20))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: (1,15)-(1,16) = "'"
+ │ │ ├── content_loc: (1,16)-(1,19) = "bar"
+ │ │ ├── closing_loc: (1,19)-(1,20) = "'"
+ │ │ └── unescaped: "bar"
+ │ ├── then_keyword_loc: ∅
+ │ └── statements:
+ │ @ StatementsNode (location: (1,22)-(1,25))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,22)-(1,25))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (1,22)-(1,25) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (1,27)-(1,30) = "end"
diff --git a/test/prism/snapshots/whitequark/case_expr_else.txt b/test/prism/snapshots/whitequark/case_expr_else.txt
new file mode 100644
index 0000000000..0624d97c84
--- /dev/null
+++ b/test/prism/snapshots/whitequark/case_expr_else.txt
@@ -0,0 +1,60 @@
+@ ProgramNode (location: (1,0)-(1,40))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,40))
+ └── body: (length: 1)
+ └── @ CaseNode (location: (1,0)-(1,40))
+ ├── predicate:
+ │ @ CallNode (location: (1,5)-(1,8))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,5)-(1,8) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── conditions: (length: 1)
+ │ └── @ WhenNode (location: (1,10)-(1,25))
+ │ ├── keyword_loc: (1,10)-(1,14) = "when"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ StringNode (location: (1,15)-(1,20))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: (1,15)-(1,16) = "'"
+ │ │ ├── content_loc: (1,16)-(1,19) = "bar"
+ │ │ ├── closing_loc: (1,19)-(1,20) = "'"
+ │ │ └── unescaped: "bar"
+ │ ├── then_keyword_loc: ∅
+ │ └── statements:
+ │ @ StatementsNode (location: (1,22)-(1,25))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,22)-(1,25))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (1,22)-(1,25) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── consequent:
+ │ @ ElseNode (location: (1,27)-(1,40))
+ │ ├── else_keyword_loc: (1,27)-(1,31) = "else"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,32)-(1,35))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,32)-(1,35))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (1,32)-(1,35) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── end_keyword_loc: (1,37)-(1,40) = "end"
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (1,37)-(1,40) = "end"
diff --git a/test/prism/snapshots/whitequark/casgn_scoped.txt b/test/prism/snapshots/whitequark/casgn_scoped.txt
new file mode 100644
index 0000000000..4e3fd6fe44
--- /dev/null
+++ b/test/prism/snapshots/whitequark/casgn_scoped.txt
@@ -0,0 +1,20 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ ConstantPathWriteNode (location: (1,0)-(1,13))
+ ├── target:
+ │ @ ConstantPathNode (location: (1,0)-(1,8))
+ │ ├── parent:
+ │ │ @ ConstantReadNode (location: (1,0)-(1,3))
+ │ │ └── name: :Bar
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (1,5)-(1,8))
+ │ │ └── name: :Foo
+ │ └── delimiter_loc: (1,3)-(1,5) = "::"
+ ├── operator_loc: (1,9)-(1,10) = "="
+ └── value:
+ @ IntegerNode (location: (1,11)-(1,13))
+ ├── flags: decimal
+ └── value: 10
diff --git a/test/prism/snapshots/whitequark/casgn_toplevel.txt b/test/prism/snapshots/whitequark/casgn_toplevel.txt
new file mode 100644
index 0000000000..11facfefb3
--- /dev/null
+++ b/test/prism/snapshots/whitequark/casgn_toplevel.txt
@@ -0,0 +1,18 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ ConstantPathWriteNode (location: (1,0)-(1,10))
+ ├── target:
+ │ @ ConstantPathNode (location: (1,0)-(1,5))
+ │ ├── parent: ∅
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (1,2)-(1,5))
+ │ │ └── name: :Foo
+ │ └── delimiter_loc: (1,0)-(1,2) = "::"
+ ├── operator_loc: (1,6)-(1,7) = "="
+ └── value:
+ @ IntegerNode (location: (1,8)-(1,10))
+ ├── flags: decimal
+ └── value: 10
diff --git a/test/prism/snapshots/whitequark/casgn_unscoped.txt b/test/prism/snapshots/whitequark/casgn_unscoped.txt
new file mode 100644
index 0000000000..f535cafdc6
--- /dev/null
+++ b/test/prism/snapshots/whitequark/casgn_unscoped.txt
@@ -0,0 +1,13 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ ConstantWriteNode (location: (1,0)-(1,8))
+ ├── name: :Foo
+ ├── name_loc: (1,0)-(1,3) = "Foo"
+ ├── value:
+ │ @ IntegerNode (location: (1,6)-(1,8))
+ │ ├── flags: decimal
+ │ └── value: 10
+ └── operator_loc: (1,4)-(1,5) = "="
diff --git a/test/prism/snapshots/whitequark/character.txt b/test/prism/snapshots/whitequark/character.txt
new file mode 100644
index 0000000000..b70f05b544
--- /dev/null
+++ b/test/prism/snapshots/whitequark/character.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,2))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,2))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,0)-(1,2))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,1) = "?"
+ ├── content_loc: (1,1)-(1,2) = "a"
+ ├── closing_loc: ∅
+ └── unescaped: "a"
diff --git a/test/prism/snapshots/whitequark/class.txt b/test/prism/snapshots/whitequark/class.txt
new file mode 100644
index 0000000000..e38bec6c47
--- /dev/null
+++ b/test/prism/snapshots/whitequark/class.txt
@@ -0,0 +1,27 @@
+@ ProgramNode (location: (1,0)-(3,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,14))
+ └── body: (length: 2)
+ ├── @ ClassNode (location: (1,0)-(1,13))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (1,0)-(1,5) = "class"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (1,6)-(1,9))
+ │ │ └── name: :Foo
+ │ ├── inheritance_operator_loc: ∅
+ │ ├── superclass: ∅
+ │ ├── body: ∅
+ │ ├── end_keyword_loc: (1,10)-(1,13) = "end"
+ │ └── name: :Foo
+ └── @ ClassNode (location: (3,0)-(3,14))
+ ├── locals: []
+ ├── class_keyword_loc: (3,0)-(3,5) = "class"
+ ├── constant_path:
+ │ @ ConstantReadNode (location: (3,6)-(3,9))
+ │ └── name: :Foo
+ ├── inheritance_operator_loc: ∅
+ ├── superclass: ∅
+ ├── body: ∅
+ ├── end_keyword_loc: (3,11)-(3,14) = "end"
+ └── name: :Foo
diff --git a/test/prism/snapshots/whitequark/class_super.txt b/test/prism/snapshots/whitequark/class_super.txt
new file mode 100644
index 0000000000..ea8bbd70d9
--- /dev/null
+++ b/test/prism/snapshots/whitequark/class_super.txt
@@ -0,0 +1,18 @@
+@ ProgramNode (location: (1,0)-(1,20))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,20))
+ └── body: (length: 1)
+ └── @ ClassNode (location: (1,0)-(1,20))
+ ├── locals: []
+ ├── class_keyword_loc: (1,0)-(1,5) = "class"
+ ├── constant_path:
+ │ @ ConstantReadNode (location: (1,6)-(1,9))
+ │ └── name: :Foo
+ ├── inheritance_operator_loc: (1,10)-(1,11) = "<"
+ ├── superclass:
+ │ @ ConstantReadNode (location: (1,12)-(1,15))
+ │ └── name: :Bar
+ ├── body: ∅
+ ├── end_keyword_loc: (1,17)-(1,20) = "end"
+ └── name: :Foo
diff --git a/test/prism/snapshots/whitequark/class_super_label.txt b/test/prism/snapshots/whitequark/class_super_label.txt
new file mode 100644
index 0000000000..c873ea0c12
--- /dev/null
+++ b/test/prism/snapshots/whitequark/class_super_label.txt
@@ -0,0 +1,35 @@
+@ ProgramNode (location: (1,0)-(1,20))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,20))
+ └── body: (length: 1)
+ └── @ ClassNode (location: (1,0)-(1,20))
+ ├── locals: []
+ ├── class_keyword_loc: (1,0)-(1,5) = "class"
+ ├── constant_path:
+ │ @ ConstantReadNode (location: (1,6)-(1,9))
+ │ └── name: :Foo
+ ├── inheritance_operator_loc: (1,10)-(1,11) = "<"
+ ├── superclass:
+ │ @ CallNode (location: (1,12)-(1,15))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,12)-(1,13) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,13)-(1,15))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ SymbolNode (location: (1,13)-(1,15))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,13)-(1,14) = ":"
+ │ │ ├── value_loc: (1,14)-(1,15) = "b"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── body: ∅
+ ├── end_keyword_loc: (1,17)-(1,20) = "end"
+ └── name: :Foo
diff --git a/test/prism/snapshots/whitequark/comments_before_leading_dot__27.txt b/test/prism/snapshots/whitequark/comments_before_leading_dot__27.txt
new file mode 100644
index 0000000000..e33f798ef5
--- /dev/null
+++ b/test/prism/snapshots/whitequark/comments_before_leading_dot__27.txt
@@ -0,0 +1,85 @@
+@ ProgramNode (location: (1,0)-(18,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(18,4))
+ └── body: (length: 4)
+ ├── @ CallNode (location: (1,0)-(3,5))
+ │ ├── flags: safe_navigation
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,0)-(1,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (3,0)-(3,2) = "&."
+ │ ├── name: :foo
+ │ ├── message_loc: (3,2)-(3,5) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (6,0)-(8,4))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (6,0)-(6,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (6,0)-(6,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (8,0)-(8,1) = "."
+ │ ├── name: :foo
+ │ ├── message_loc: (8,1)-(8,4) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (11,0)-(13,5))
+ │ ├── flags: safe_navigation
+ │ ├── receiver:
+ │ │ @ CallNode (location: (11,0)-(11,1))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (11,0)-(11,1) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (13,0)-(13,2) = "&."
+ │ ├── name: :foo
+ │ ├── message_loc: (13,2)-(13,5) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (16,0)-(18,4))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (16,0)-(16,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (16,0)-(16,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (18,0)-(18,1) = "."
+ ├── name: :foo
+ ├── message_loc: (18,1)-(18,4) = "foo"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/complex.txt b/test/prism/snapshots/whitequark/complex.txt
new file mode 100644
index 0000000000..e688585a5f
--- /dev/null
+++ b/test/prism/snapshots/whitequark/complex.txt
@@ -0,0 +1,27 @@
+@ ProgramNode (location: (1,0)-(7,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(7,4))
+ └── body: (length: 4)
+ ├── @ ImaginaryNode (location: (1,0)-(1,5))
+ │ └── numeric:
+ │ @ FloatNode (location: (1,0)-(1,4))
+ │ └── value: 42.1
+ ├── @ ImaginaryNode (location: (3,0)-(3,6))
+ │ └── numeric:
+ │ @ RationalNode (location: (3,0)-(3,5))
+ │ └── numeric:
+ │ @ FloatNode (location: (3,0)-(3,4))
+ │ └── value: 42.1
+ ├── @ ImaginaryNode (location: (5,0)-(5,3))
+ │ └── numeric:
+ │ @ IntegerNode (location: (5,0)-(5,2))
+ │ ├── flags: decimal
+ │ └── value: 42
+ └── @ ImaginaryNode (location: (7,0)-(7,4))
+ └── numeric:
+ @ RationalNode (location: (7,0)-(7,3))
+ └── numeric:
+ @ IntegerNode (location: (7,0)-(7,2))
+ ├── flags: decimal
+ └── value: 42
diff --git a/test/prism/snapshots/whitequark/cond_begin.txt b/test/prism/snapshots/whitequark/cond_begin.txt
new file mode 100644
index 0000000000..e349c198a5
--- /dev/null
+++ b/test/prism/snapshots/whitequark/cond_begin.txt
@@ -0,0 +1,40 @@
+@ ProgramNode (location: (1,0)-(1,18))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,18))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(1,18))
+ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ ├── predicate:
+ │ @ ParenthesesNode (location: (1,3)-(1,8))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,4)-(1,7))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,4)-(1,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,4)-(1,7) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (1,3)-(1,4) = "("
+ │ └── closing_loc: (1,7)-(1,8) = ")"
+ ├── then_keyword_loc: ∅
+ ├── statements:
+ │ @ StatementsNode (location: (1,10)-(1,13))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,10)-(1,13))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,10)-(1,13) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (1,15)-(1,18) = "end"
diff --git a/test/prism/snapshots/whitequark/cond_begin_masgn.txt b/test/prism/snapshots/whitequark/cond_begin_masgn.txt
new file mode 100644
index 0000000000..b4e6d8682c
--- /dev/null
+++ b/test/prism/snapshots/whitequark/cond_begin_masgn.txt
@@ -0,0 +1,52 @@
+@ ProgramNode (location: (1,0)-(1,25))
+├── locals: [:a, :b]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,25))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(1,25))
+ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ ├── predicate:
+ │ @ ParenthesesNode (location: (1,3)-(1,20))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,4)-(1,19))
+ │ │ └── body: (length: 2)
+ │ │ ├── @ CallNode (location: (1,4)-(1,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (1,4)-(1,7) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ MultiWriteNode (location: (1,9)-(1,19))
+ │ │ ├── lefts: (length: 2)
+ │ │ │ ├── @ LocalVariableTargetNode (location: (1,9)-(1,10))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ └── depth: 0
+ │ │ │ └── @ LocalVariableTargetNode (location: (1,12)-(1,13))
+ │ │ │ ├── name: :b
+ │ │ │ └── depth: 0
+ │ │ ├── rest: ∅
+ │ │ ├── rights: (length: 0)
+ │ │ ├── lparen_loc: ∅
+ │ │ ├── rparen_loc: ∅
+ │ │ ├── operator_loc: (1,14)-(1,15) = "="
+ │ │ └── value:
+ │ │ @ CallNode (location: (1,16)-(1,19))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,16)-(1,19) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (1,3)-(1,4) = "("
+ │ └── closing_loc: (1,19)-(1,20) = ")"
+ ├── then_keyword_loc: ∅
+ ├── statements: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (1,22)-(1,25) = "end"
diff --git a/test/prism/snapshots/whitequark/cond_eflipflop.txt b/test/prism/snapshots/whitequark/cond_eflipflop.txt
new file mode 100644
index 0000000000..18fee58492
--- /dev/null
+++ b/test/prism/snapshots/whitequark/cond_eflipflop.txt
@@ -0,0 +1,78 @@
+@ ProgramNode (location: (1,0)-(3,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,17))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,12))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (1,1)-(1,12))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (1,2)-(1,11))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ FlipFlopNode (location: (1,2)-(1,11))
+ │ │ │ ├── flags: exclude_end
+ │ │ │ ├── left:
+ │ │ │ │ @ CallNode (location: (1,2)-(1,5))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :foo
+ │ │ │ │ ├── message_loc: (1,2)-(1,5) = "foo"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── right:
+ │ │ │ │ @ CallNode (location: (1,8)-(1,11))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (1,8)-(1,11) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── operator_loc: (1,5)-(1,8) = "..."
+ │ │ ├── opening_loc: (1,1)-(1,2) = "("
+ │ │ └── closing_loc: (1,11)-(1,12) = ")"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!
+ │ ├── message_loc: (1,0)-(1,1) = "!"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ IfNode (location: (3,0)-(3,17))
+ ├── if_keyword_loc: (3,0)-(3,2) = "if"
+ ├── predicate:
+ │ @ FlipFlopNode (location: (3,3)-(3,12))
+ │ ├── flags: exclude_end
+ │ ├── left:
+ │ │ @ CallNode (location: (3,3)-(3,6))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (3,3)-(3,6) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right:
+ │ │ @ CallNode (location: (3,9)-(3,12))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (3,9)-(3,12) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (3,6)-(3,9) = "..."
+ ├── then_keyword_loc: ∅
+ ├── statements: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (3,14)-(3,17) = "end"
diff --git a/test/prism/snapshots/whitequark/cond_eflipflop_with_beginless_range.txt b/test/prism/snapshots/whitequark/cond_eflipflop_with_beginless_range.txt
new file mode 100644
index 0000000000..05972521e8
--- /dev/null
+++ b/test/prism/snapshots/whitequark/cond_eflipflop_with_beginless_range.txt
@@ -0,0 +1,27 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(1,14))
+ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ ├── predicate:
+ │ @ FlipFlopNode (location: (1,3)-(1,9))
+ │ ├── flags: exclude_end
+ │ ├── left: ∅
+ │ ├── right:
+ │ │ @ CallNode (location: (1,6)-(1,9))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,6)-(1,9) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (1,3)-(1,6) = "..."
+ ├── then_keyword_loc: ∅
+ ├── statements: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (1,11)-(1,14) = "end"
diff --git a/test/prism/snapshots/whitequark/cond_eflipflop_with_endless_range.txt b/test/prism/snapshots/whitequark/cond_eflipflop_with_endless_range.txt
new file mode 100644
index 0000000000..c85ff292a5
--- /dev/null
+++ b/test/prism/snapshots/whitequark/cond_eflipflop_with_endless_range.txt
@@ -0,0 +1,27 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(1,14))
+ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ ├── predicate:
+ │ @ FlipFlopNode (location: (1,3)-(1,9))
+ │ ├── flags: exclude_end
+ │ ├── left:
+ │ │ @ CallNode (location: (1,3)-(1,6))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,3)-(1,6) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right: ∅
+ │ └── operator_loc: (1,6)-(1,9) = "..."
+ ├── then_keyword_loc: ∅
+ ├── statements: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (1,11)-(1,14) = "end"
diff --git a/test/prism/snapshots/whitequark/cond_iflipflop.txt b/test/prism/snapshots/whitequark/cond_iflipflop.txt
new file mode 100644
index 0000000000..f76a6636f6
--- /dev/null
+++ b/test/prism/snapshots/whitequark/cond_iflipflop.txt
@@ -0,0 +1,78 @@
+@ ProgramNode (location: (1,0)-(3,16))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,16))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,11))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (1,1)-(1,11))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (1,2)-(1,10))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ FlipFlopNode (location: (1,2)-(1,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── left:
+ │ │ │ │ @ CallNode (location: (1,2)-(1,5))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :foo
+ │ │ │ │ ├── message_loc: (1,2)-(1,5) = "foo"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── right:
+ │ │ │ │ @ CallNode (location: (1,7)-(1,10))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (1,7)-(1,10) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── operator_loc: (1,5)-(1,7) = ".."
+ │ │ ├── opening_loc: (1,1)-(1,2) = "("
+ │ │ └── closing_loc: (1,10)-(1,11) = ")"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!
+ │ ├── message_loc: (1,0)-(1,1) = "!"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ IfNode (location: (3,0)-(3,16))
+ ├── if_keyword_loc: (3,0)-(3,2) = "if"
+ ├── predicate:
+ │ @ FlipFlopNode (location: (3,3)-(3,11))
+ │ ├── flags: ∅
+ │ ├── left:
+ │ │ @ CallNode (location: (3,3)-(3,6))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (3,3)-(3,6) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right:
+ │ │ @ CallNode (location: (3,8)-(3,11))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (3,8)-(3,11) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (3,6)-(3,8) = ".."
+ ├── then_keyword_loc: ∅
+ ├── statements: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (3,13)-(3,16) = "end"
diff --git a/test/prism/snapshots/whitequark/cond_iflipflop_with_beginless_range.txt b/test/prism/snapshots/whitequark/cond_iflipflop_with_beginless_range.txt
new file mode 100644
index 0000000000..63b87ffd49
--- /dev/null
+++ b/test/prism/snapshots/whitequark/cond_iflipflop_with_beginless_range.txt
@@ -0,0 +1,27 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(1,13))
+ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ ├── predicate:
+ │ @ FlipFlopNode (location: (1,3)-(1,8))
+ │ ├── flags: ∅
+ │ ├── left: ∅
+ │ ├── right:
+ │ │ @ CallNode (location: (1,5)-(1,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,5)-(1,8) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (1,3)-(1,5) = ".."
+ ├── then_keyword_loc: ∅
+ ├── statements: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (1,10)-(1,13) = "end"
diff --git a/test/prism/snapshots/whitequark/cond_iflipflop_with_endless_range.txt b/test/prism/snapshots/whitequark/cond_iflipflop_with_endless_range.txt
new file mode 100644
index 0000000000..328a2da153
--- /dev/null
+++ b/test/prism/snapshots/whitequark/cond_iflipflop_with_endless_range.txt
@@ -0,0 +1,27 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(1,13))
+ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ ├── predicate:
+ │ @ FlipFlopNode (location: (1,3)-(1,8))
+ │ ├── flags: ∅
+ │ ├── left:
+ │ │ @ CallNode (location: (1,3)-(1,6))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,3)-(1,6) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right: ∅
+ │ └── operator_loc: (1,6)-(1,8) = ".."
+ ├── then_keyword_loc: ∅
+ ├── statements: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (1,10)-(1,13) = "end"
diff --git a/test/prism/snapshots/whitequark/cond_match_current_line.txt b/test/prism/snapshots/whitequark/cond_match_current_line.txt
new file mode 100644
index 0000000000..700d0966f7
--- /dev/null
+++ b/test/prism/snapshots/whitequark/cond_match_current_line.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(3,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,13))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,6))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ MatchLastLineNode (location: (1,1)-(1,6))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,1)-(1,2) = "/"
+ │ │ ├── content_loc: (1,2)-(1,5) = "wat"
+ │ │ ├── closing_loc: (1,5)-(1,6) = "/"
+ │ │ └── unescaped: "wat"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!
+ │ ├── message_loc: (1,0)-(1,1) = "!"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ IfNode (location: (3,0)-(3,13))
+ ├── if_keyword_loc: (3,0)-(3,2) = "if"
+ ├── predicate:
+ │ @ MatchLastLineNode (location: (3,3)-(3,8))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (3,3)-(3,4) = "/"
+ │ ├── content_loc: (3,4)-(3,7) = "wat"
+ │ ├── closing_loc: (3,7)-(3,8) = "/"
+ │ └── unescaped: "wat"
+ ├── then_keyword_loc: ∅
+ ├── statements: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (3,10)-(3,13) = "end"
diff --git a/test/prism/snapshots/whitequark/const_op_asgn.txt b/test/prism/snapshots/whitequark/const_op_asgn.txt
new file mode 100644
index 0000000000..4985f3e54b
--- /dev/null
+++ b/test/prism/snapshots/whitequark/const_op_asgn.txt
@@ -0,0 +1,101 @@
+@ ProgramNode (location: (1,0)-(9,25))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(9,25))
+ └── body: (length: 5)
+ ├── @ ConstantPathOperatorWriteNode (location: (1,0)-(1,8))
+ │ ├── target:
+ │ │ @ ConstantPathNode (location: (1,0)-(1,3))
+ │ │ ├── parent: ∅
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (1,2)-(1,3))
+ │ │ │ └── name: :A
+ │ │ └── delimiter_loc: (1,0)-(1,2) = "::"
+ │ ├── operator_loc: (1,4)-(1,6) = "+="
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,7)-(1,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator: :+
+ ├── @ ConstantOperatorWriteNode (location: (3,0)-(3,6))
+ │ ├── name: :A
+ │ ├── name_loc: (3,0)-(3,1) = "A"
+ │ ├── operator_loc: (3,2)-(3,4) = "+="
+ │ ├── value:
+ │ │ @ IntegerNode (location: (3,5)-(3,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator: :+
+ ├── @ ConstantPathOperatorWriteNode (location: (5,0)-(5,9))
+ │ ├── target:
+ │ │ @ ConstantPathNode (location: (5,0)-(5,4))
+ │ │ ├── parent:
+ │ │ │ @ ConstantReadNode (location: (5,0)-(5,1))
+ │ │ │ └── name: :B
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (5,3)-(5,4))
+ │ │ │ └── name: :A
+ │ │ └── delimiter_loc: (5,1)-(5,3) = "::"
+ │ ├── operator_loc: (5,5)-(5,7) = "+="
+ │ ├── value:
+ │ │ @ IntegerNode (location: (5,8)-(5,9))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator: :+
+ ├── @ DefNode (location: (7,0)-(7,21))
+ │ ├── name: :x
+ │ ├── name_loc: (7,4)-(7,5) = "x"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (7,7)-(7,16))
+ │ │ └── body: (length: 1)
+ │ │ └── @ ConstantPathOrWriteNode (location: (7,7)-(7,16))
+ │ │ ├── target:
+ │ │ │ @ ConstantPathNode (location: (7,7)-(7,10))
+ │ │ │ ├── parent: ∅
+ │ │ │ ├── child:
+ │ │ │ │ @ ConstantReadNode (location: (7,9)-(7,10))
+ │ │ │ │ └── name: :A
+ │ │ │ └── delimiter_loc: (7,7)-(7,9) = "::"
+ │ │ ├── operator_loc: (7,11)-(7,14) = "||="
+ │ │ └── value:
+ │ │ @ IntegerNode (location: (7,15)-(7,16))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (7,0)-(7,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (7,18)-(7,21) = "end"
+ └── @ DefNode (location: (9,0)-(9,25))
+ ├── name: :x
+ ├── name_loc: (9,4)-(9,5) = "x"
+ ├── receiver: ∅
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (9,7)-(9,20))
+ │ └── body: (length: 1)
+ │ └── @ ConstantPathOrWriteNode (location: (9,7)-(9,20))
+ │ ├── target:
+ │ │ @ ConstantPathNode (location: (9,7)-(9,14))
+ │ │ ├── parent:
+ │ │ │ @ SelfNode (location: (9,7)-(9,11))
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (9,13)-(9,14))
+ │ │ │ └── name: :A
+ │ │ └── delimiter_loc: (9,11)-(9,13) = "::"
+ │ ├── operator_loc: (9,15)-(9,18) = "||="
+ │ └── value:
+ │ @ IntegerNode (location: (9,19)-(9,20))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── locals: []
+ ├── def_keyword_loc: (9,0)-(9,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (9,22)-(9,25) = "end"
diff --git a/test/prism/snapshots/whitequark/const_scoped.txt b/test/prism/snapshots/whitequark/const_scoped.txt
new file mode 100644
index 0000000000..1e2bccef96
--- /dev/null
+++ b/test/prism/snapshots/whitequark/const_scoped.txt
@@ -0,0 +1,13 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ ConstantPathNode (location: (1,0)-(1,8))
+ ├── parent:
+ │ @ ConstantReadNode (location: (1,0)-(1,3))
+ │ └── name: :Bar
+ ├── child:
+ │ @ ConstantReadNode (location: (1,5)-(1,8))
+ │ └── name: :Foo
+ └── delimiter_loc: (1,3)-(1,5) = "::"
diff --git a/test/prism/snapshots/whitequark/const_toplevel.txt b/test/prism/snapshots/whitequark/const_toplevel.txt
new file mode 100644
index 0000000000..b54b069d06
--- /dev/null
+++ b/test/prism/snapshots/whitequark/const_toplevel.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ ConstantPathNode (location: (1,0)-(1,5))
+ ├── parent: ∅
+ ├── child:
+ │ @ ConstantReadNode (location: (1,2)-(1,5))
+ │ └── name: :Foo
+ └── delimiter_loc: (1,0)-(1,2) = "::"
diff --git a/test/prism/snapshots/whitequark/const_unscoped.txt b/test/prism/snapshots/whitequark/const_unscoped.txt
new file mode 100644
index 0000000000..5e272e1775
--- /dev/null
+++ b/test/prism/snapshots/whitequark/const_unscoped.txt
@@ -0,0 +1,7 @@
+@ ProgramNode (location: (1,0)-(1,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,3))
+ └── body: (length: 1)
+ └── @ ConstantReadNode (location: (1,0)-(1,3))
+ └── name: :Foo
diff --git a/test/prism/snapshots/whitequark/cpath.txt b/test/prism/snapshots/whitequark/cpath.txt
new file mode 100644
index 0000000000..b892525646
--- /dev/null
+++ b/test/prism/snapshots/whitequark/cpath.txt
@@ -0,0 +1,33 @@
+@ ProgramNode (location: (1,0)-(3,20))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,20))
+ └── body: (length: 2)
+ ├── @ ModuleNode (location: (1,0)-(1,17))
+ │ ├── locals: []
+ │ ├── module_keyword_loc: (1,0)-(1,6) = "module"
+ │ ├── constant_path:
+ │ │ @ ConstantPathNode (location: (1,7)-(1,12))
+ │ │ ├── parent: ∅
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (1,9)-(1,12))
+ │ │ │ └── name: :Foo
+ │ │ └── delimiter_loc: (1,7)-(1,9) = "::"
+ │ ├── body: ∅
+ │ ├── end_keyword_loc: (1,14)-(1,17) = "end"
+ │ └── name: :Foo
+ └── @ ModuleNode (location: (3,0)-(3,20))
+ ├── locals: []
+ ├── module_keyword_loc: (3,0)-(3,6) = "module"
+ ├── constant_path:
+ │ @ ConstantPathNode (location: (3,7)-(3,15))
+ │ ├── parent:
+ │ │ @ ConstantReadNode (location: (3,7)-(3,10))
+ │ │ └── name: :Bar
+ │ ├── child:
+ │ │ @ ConstantReadNode (location: (3,12)-(3,15))
+ │ │ └── name: :Foo
+ │ └── delimiter_loc: (3,10)-(3,12) = "::"
+ ├── body: ∅
+ ├── end_keyword_loc: (3,17)-(3,20) = "end"
+ └── name: :Foo
diff --git a/test/prism/snapshots/whitequark/cvar.txt b/test/prism/snapshots/whitequark/cvar.txt
new file mode 100644
index 0000000000..7847ce4495
--- /dev/null
+++ b/test/prism/snapshots/whitequark/cvar.txt
@@ -0,0 +1,7 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ ClassVariableReadNode (location: (1,0)-(1,5))
+ └── name: :@@foo
diff --git a/test/prism/snapshots/whitequark/cvasgn.txt b/test/prism/snapshots/whitequark/cvasgn.txt
new file mode 100644
index 0000000000..f3eceed4a4
--- /dev/null
+++ b/test/prism/snapshots/whitequark/cvasgn.txt
@@ -0,0 +1,13 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ ClassVariableWriteNode (location: (1,0)-(1,10))
+ ├── name: :@@var
+ ├── name_loc: (1,0)-(1,5) = "@@var"
+ ├── value:
+ │ @ IntegerNode (location: (1,8)-(1,10))
+ │ ├── flags: decimal
+ │ └── value: 10
+ └── operator_loc: (1,6)-(1,7) = "="
diff --git a/test/prism/snapshots/whitequark/dedenting_heredoc.txt b/test/prism/snapshots/whitequark/dedenting_heredoc.txt
new file mode 100644
index 0000000000..acb79e83d2
--- /dev/null
+++ b/test/prism/snapshots/whitequark/dedenting_heredoc.txt
@@ -0,0 +1,496 @@
+@ ProgramNode (location: (1,0)-(72,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(72,8))
+ └── body: (length: 16)
+ ├── @ CallNode (location: (1,0)-(1,8))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :p
+ │ ├── message_loc: (1,0)-(1,1) = "p"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,2)-(1,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (1,2)-(1,8))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,2)-(1,8) = "<<~\"E\""
+ │ │ ├── parts: (length: 3)
+ │ │ │ ├── @ StringNode (location: (2,0)-(3,0))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (2,0)-(3,0) = " x\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: " x\n"
+ │ │ │ ├── @ EmbeddedStatementsNode (location: (3,2)-(3,10))
+ │ │ │ │ ├── opening_loc: (3,2)-(3,4) = "\#{"
+ │ │ │ │ ├── statements:
+ │ │ │ │ │ @ StatementsNode (location: (3,4)-(3,9))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ StringNode (location: (3,4)-(3,9))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── opening_loc: (3,4)-(3,5) = "\""
+ │ │ │ │ │ ├── content_loc: (3,5)-(3,8) = " y"
+ │ │ │ │ │ ├── closing_loc: (3,8)-(3,9) = "\""
+ │ │ │ │ │ └── unescaped: " y"
+ │ │ │ │ └── closing_loc: (3,9)-(3,10) = "}"
+ │ │ │ └── @ StringNode (location: (3,10)-(4,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (3,10)-(4,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── closing_loc: (4,0)-(5,0) = "E\n"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (6,0)-(6,8))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :p
+ │ ├── message_loc: (6,0)-(6,1) = "p"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (6,2)-(6,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (6,2)-(6,8))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (6,2)-(6,8) = "<<~\"E\""
+ │ │ ├── parts: (length: 3)
+ │ │ │ ├── @ StringNode (location: (7,0)-(8,0))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (7,0)-(8,0) = " x\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: " x\n"
+ │ │ │ ├── @ EmbeddedStatementsNode (location: (8,2)-(8,8))
+ │ │ │ │ ├── opening_loc: (8,2)-(8,4) = "\#{"
+ │ │ │ │ ├── statements:
+ │ │ │ │ │ @ StatementsNode (location: (8,4)-(8,7))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (8,4)-(8,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :foo
+ │ │ │ │ │ ├── message_loc: (8,4)-(8,7) = "foo"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ └── closing_loc: (8,7)-(8,8) = "}"
+ │ │ │ └── @ StringNode (location: (8,8)-(9,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (8,8)-(9,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── closing_loc: (9,0)-(10,0) = "E\n"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (11,0)-(11,6))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :p
+ │ ├── message_loc: (11,0)-(11,1) = "p"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (11,2)-(11,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (11,2)-(11,6))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (11,2)-(11,6) = "<<~E"
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (12,0)-(13,0))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (12,0)-(13,0) = "\tx\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "x\n"
+ │ │ │ └── @ StringNode (location: (13,0)-(14,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (13,0)-(14,0) = " y\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "y\n"
+ │ │ └── closing_loc: (14,0)-(15,0) = "E\n"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (16,0)-(16,6))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :p
+ │ ├── message_loc: (16,0)-(16,1) = "p"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (16,2)-(16,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (16,2)-(16,6))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (16,2)-(16,6) = "<<~E"
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (17,0)-(18,0))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (17,0)-(18,0) = "\tx\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "\tx\n"
+ │ │ │ └── @ StringNode (location: (18,0)-(19,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (18,0)-(19,0) = " y\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "y\n"
+ │ │ └── closing_loc: (19,0)-(20,0) = "E\n"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (21,0)-(21,6))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :p
+ │ ├── message_loc: (21,0)-(21,1) = "p"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (21,2)-(21,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (21,2)-(21,6))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (21,2)-(21,6) = "<<~E"
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (22,0)-(23,0))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (22,0)-(23,0) = " \tx\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "x\n"
+ │ │ │ └── @ StringNode (location: (23,0)-(24,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (23,0)-(24,0) = " y\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "y\n"
+ │ │ └── closing_loc: (24,0)-(25,0) = "E\n"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (26,0)-(26,6))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :p
+ │ ├── message_loc: (26,0)-(26,1) = "p"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (26,2)-(26,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (26,2)-(26,6))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (26,2)-(26,6) = "<<~E"
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (27,0)-(28,0))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (27,0)-(28,0) = " \tx\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "\tx\n"
+ │ │ │ └── @ StringNode (location: (28,0)-(29,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (28,0)-(29,0) = "\ty\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "y\n"
+ │ │ └── closing_loc: (29,0)-(30,0) = "E\n"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (31,0)-(31,6))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :p
+ │ ├── message_loc: (31,0)-(31,1) = "p"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (31,2)-(31,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (31,2)-(31,6))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (31,2)-(31,6) = "<<~E"
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (32,0)-(33,0))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (32,0)-(33,0) = " x\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: " x\n"
+ │ │ │ └── @ StringNode (location: (33,0)-(34,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (33,0)-(34,0) = " \\\ty\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\ty\n"
+ │ │ └── closing_loc: (34,0)-(35,0) = "E\n"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (36,0)-(36,6))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :p
+ │ ├── message_loc: (36,0)-(36,1) = "p"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (36,2)-(36,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (36,2)-(36,6))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (36,2)-(36,6) = "<<~E"
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (37,0)-(38,0))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (37,0)-(38,0) = " x\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: " x\n"
+ │ │ │ └── @ StringNode (location: (38,0)-(39,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (38,0)-(39,0) = " \\ y\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: " y\n"
+ │ │ └── closing_loc: (39,0)-(40,0) = "E\n"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (41,0)-(41,6))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :p
+ │ ├── message_loc: (41,0)-(41,1) = "p"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (41,2)-(41,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (41,2)-(41,6))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (41,2)-(41,6) = "<<~E"
+ │ │ ├── content_loc: (42,0)-(42,0) = ""
+ │ │ ├── closing_loc: (42,0)-(43,0) = " E\n"
+ │ │ └── unescaped: ""
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (44,0)-(44,6))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :p
+ │ ├── message_loc: (44,0)-(44,1) = "p"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (44,2)-(44,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (44,2)-(44,6))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (44,2)-(44,6) = "<<~E"
+ │ │ ├── parts: (length: 3)
+ │ │ │ ├── @ StringNode (location: (45,0)-(46,0))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (45,0)-(46,0) = " x\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: " x\n"
+ │ │ │ ├── @ StringNode (location: (46,0)-(47,0))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (46,0)-(47,0) = "\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "\n"
+ │ │ │ └── @ StringNode (location: (47,0)-(48,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (47,0)-(48,0) = "y\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "y\n"
+ │ │ └── closing_loc: (48,0)-(49,0) = "E\n"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (50,0)-(50,6))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :p
+ │ ├── message_loc: (50,0)-(50,1) = "p"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (50,2)-(50,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (50,2)-(50,6))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (50,2)-(50,6) = "<<~E"
+ │ │ ├── parts: (length: 3)
+ │ │ │ ├── @ StringNode (location: (51,0)-(52,0))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (51,0)-(52,0) = " x\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "x\n"
+ │ │ │ ├── @ StringNode (location: (52,0)-(53,0))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (52,0)-(53,0) = " \n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: " \n"
+ │ │ │ └── @ StringNode (location: (53,0)-(54,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (53,0)-(54,0) = " y\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "y\n"
+ │ │ └── closing_loc: (54,0)-(55,0) = "E\n"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (56,0)-(56,6))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :p
+ │ ├── message_loc: (56,0)-(56,1) = "p"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (56,2)-(56,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ InterpolatedStringNode (location: (56,2)-(56,6))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (56,2)-(56,6) = "<<~E"
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (57,0)-(58,0))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (57,0)-(58,0) = " x\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "x\n"
+ │ │ │ └── @ StringNode (location: (58,0)-(59,0))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (58,0)-(59,0) = " y\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: " y\n"
+ │ │ └── closing_loc: (59,0)-(60,0) = "E\n"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (61,0)-(61,6))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :p
+ │ ├── message_loc: (61,0)-(61,1) = "p"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (61,2)-(61,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (61,2)-(61,6))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (61,2)-(61,6) = "<<~E"
+ │ │ ├── content_loc: (62,0)-(63,0) = " x\n"
+ │ │ ├── closing_loc: (63,0)-(64,0) = "E\n"
+ │ │ └── unescaped: "x\n"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (65,0)-(65,6))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :p
+ │ ├── message_loc: (65,0)-(65,1) = "p"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (65,2)-(65,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (65,2)-(65,6))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (65,2)-(65,6) = "<<~E"
+ │ │ ├── content_loc: (66,0)-(67,0) = " ð\n"
+ │ │ ├── closing_loc: (67,0)-(68,0) = "E\n"
+ │ │ └── unescaped: "ð\n"
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (69,0)-(69,6))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :p
+ │ ├── message_loc: (69,0)-(69,1) = "p"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (69,2)-(69,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ StringNode (location: (69,2)-(69,6))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (69,2)-(69,6) = "<<~E"
+ │ │ ├── content_loc: (70,0)-(70,0) = ""
+ │ │ ├── closing_loc: (70,0)-(71,0) = "E\n"
+ │ │ └── unescaped: ""
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (72,0)-(72,8))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :p
+ ├── message_loc: (72,0)-(72,1) = "p"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (72,2)-(72,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ InterpolatedXStringNode (location: (72,2)-(72,8))
+ │ ├── opening_loc: (72,2)-(72,8) = "<<~`E`"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (73,0)-(74,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (73,0)-(74,0) = " x\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: " x\n"
+ │ │ ├── @ EmbeddedStatementsNode (location: (74,2)-(74,8))
+ │ │ │ ├── opening_loc: (74,2)-(74,4) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (74,4)-(74,7))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (74,4)-(74,7))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :foo
+ │ │ │ │ ├── message_loc: (74,4)-(74,7) = "foo"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── closing_loc: (74,7)-(74,8) = "}"
+ │ │ └── @ StringNode (location: (74,8)-(75,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (74,8)-(75,0) = "\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\n"
+ │ └── closing_loc: (75,0)-(76,0) = "E\n"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/dedenting_interpolating_heredoc_fake_line_continuation.txt b/test/prism/snapshots/whitequark/dedenting_interpolating_heredoc_fake_line_continuation.txt
new file mode 100644
index 0000000000..8d093fdab6
--- /dev/null
+++ b/test/prism/snapshots/whitequark/dedenting_interpolating_heredoc_fake_line_continuation.txt
@@ -0,0 +1,22 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(1,8))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,8) = "<<~'FOO'"
+ ├── parts: (length: 2)
+ │ ├── @ StringNode (location: (2,0)-(3,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (2,0)-(3,0) = " baz\\\\\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "baz\\\\\n"
+ │ └── @ StringNode (location: (3,0)-(4,0))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (3,0)-(4,0) = " qux\n"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "qux\n"
+ └── closing_loc: (4,0)-(5,0) = "FOO\n"
diff --git a/test/prism/snapshots/whitequark/dedenting_non_interpolating_heredoc_line_continuation.txt b/test/prism/snapshots/whitequark/dedenting_non_interpolating_heredoc_line_continuation.txt
new file mode 100644
index 0000000000..d43d313e6b
--- /dev/null
+++ b/test/prism/snapshots/whitequark/dedenting_non_interpolating_heredoc_line_continuation.txt
@@ -0,0 +1,22 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(1,8))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,8) = "<<~'FOO'"
+ ├── parts: (length: 2)
+ │ ├── @ StringNode (location: (2,0)-(3,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (2,0)-(3,0) = " baz\\\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "baz\\\n"
+ │ └── @ StringNode (location: (3,0)-(4,0))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (3,0)-(4,0) = " qux\n"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "qux\n"
+ └── closing_loc: (4,0)-(5,0) = "FOO\n"
diff --git a/test/prism/snapshots/whitequark/def.txt b/test/prism/snapshots/whitequark/def.txt
new file mode 100644
index 0000000000..d5e1139c4d
--- /dev/null
+++ b/test/prism/snapshots/whitequark/def.txt
@@ -0,0 +1,83 @@
+@ ProgramNode (location: (1,0)-(11,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(11,14))
+ └── body: (length: 6)
+ ├── @ DefNode (location: (1,0)-(1,14))
+ │ ├── name: :BEGIN
+ │ ├── name_loc: (1,4)-(1,9) = "BEGIN"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (1,11)-(1,14) = "end"
+ ├── @ DefNode (location: (3,0)-(3,12))
+ │ ├── name: :END
+ │ ├── name_loc: (3,4)-(3,7) = "END"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (3,0)-(3,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (3,9)-(3,12) = "end"
+ ├── @ DefNode (location: (5,0)-(5,15))
+ │ ├── name: :String
+ │ ├── name_loc: (5,4)-(5,10) = "String"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (5,0)-(5,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (5,12)-(5,15) = "end"
+ ├── @ DefNode (location: (7,0)-(7,16))
+ │ ├── name: :String=
+ │ ├── name_loc: (7,4)-(7,11) = "String="
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (7,0)-(7,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (7,13)-(7,16) = "end"
+ ├── @ DefNode (location: (9,0)-(9,12))
+ │ ├── name: :foo
+ │ ├── name_loc: (9,4)-(9,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (9,0)-(9,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (9,9)-(9,12) = "end"
+ └── @ DefNode (location: (11,0)-(11,14))
+ ├── name: :until
+ ├── name_loc: (11,4)-(11,9) = "until"
+ ├── receiver: ∅
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (11,0)-(11,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (11,11)-(11,14) = "end"
diff --git a/test/prism/snapshots/whitequark/defined.txt b/test/prism/snapshots/whitequark/defined.txt
new file mode 100644
index 0000000000..5e05870b21
--- /dev/null
+++ b/test/prism/snapshots/whitequark/defined.txt
@@ -0,0 +1,42 @@
+@ ProgramNode (location: (1,0)-(5,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,13))
+ └── body: (length: 3)
+ ├── @ DefinedNode (location: (1,0)-(1,13))
+ │ ├── lparen_loc: ∅
+ │ ├── value:
+ │ │ @ InstanceVariableReadNode (location: (1,9)-(1,13))
+ │ │ └── name: :@foo
+ │ ├── rparen_loc: ∅
+ │ └── keyword_loc: (1,0)-(1,8) = "defined?"
+ ├── @ DefinedNode (location: (3,0)-(3,12))
+ │ ├── lparen_loc: ∅
+ │ ├── value:
+ │ │ @ CallNode (location: (3,9)-(3,12))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (3,9)-(3,12) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rparen_loc: ∅
+ │ └── keyword_loc: (3,0)-(3,8) = "defined?"
+ └── @ DefinedNode (location: (5,0)-(5,13))
+ ├── lparen_loc: (5,8)-(5,9) = "("
+ ├── value:
+ │ @ CallNode (location: (5,9)-(5,12))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (5,9)-(5,12) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── rparen_loc: (5,12)-(5,13) = ")"
+ └── keyword_loc: (5,0)-(5,8) = "defined?"
diff --git a/test/prism/snapshots/whitequark/defs.txt b/test/prism/snapshots/whitequark/defs.txt
new file mode 100644
index 0000000000..616cca6155
--- /dev/null
+++ b/test/prism/snapshots/whitequark/defs.txt
@@ -0,0 +1,90 @@
+@ ProgramNode (location: (1,0)-(9,18))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(9,18))
+ └── body: (length: 5)
+ ├── @ DefNode (location: (1,0)-(1,18))
+ │ ├── name: :foo
+ │ ├── name_loc: (1,10)-(1,13) = "foo"
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (1,4)-(1,9))
+ │ │ ├── body:
+ │ │ │ @ CallNode (location: (1,5)-(1,8))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (1,5)-(1,8) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (1,4)-(1,5) = "("
+ │ │ └── closing_loc: (1,8)-(1,9) = ")"
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: (1,9)-(1,10) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (1,15)-(1,18) = "end"
+ ├── @ DefNode (location: (3,0)-(3,19))
+ │ ├── name: :foo
+ │ ├── name_loc: (3,11)-(3,14) = "foo"
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (3,4)-(3,10))
+ │ │ └── name: :String
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (3,0)-(3,3) = "def"
+ │ ├── operator_loc: (3,10)-(3,11) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (3,16)-(3,19) = "end"
+ ├── @ DefNode (location: (5,0)-(5,20))
+ │ ├── name: :foo
+ │ ├── name_loc: (5,12)-(5,15) = "foo"
+ │ ├── receiver:
+ │ │ @ ConstantReadNode (location: (5,4)-(5,10))
+ │ │ └── name: :String
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (5,0)-(5,3) = "def"
+ │ ├── operator_loc: (5,10)-(5,12) = "::"
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (5,17)-(5,20) = "end"
+ ├── @ DefNode (location: (7,0)-(7,17))
+ │ ├── name: :foo
+ │ ├── name_loc: (7,9)-(7,12) = "foo"
+ │ ├── receiver:
+ │ │ @ SelfNode (location: (7,4)-(7,8))
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (7,0)-(7,3) = "def"
+ │ ├── operator_loc: (7,8)-(7,9) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (7,14)-(7,17) = "end"
+ └── @ DefNode (location: (9,0)-(9,18))
+ ├── name: :foo
+ ├── name_loc: (9,10)-(9,13) = "foo"
+ ├── receiver:
+ │ @ SelfNode (location: (9,4)-(9,8))
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (9,0)-(9,3) = "def"
+ ├── operator_loc: (9,8)-(9,10) = "::"
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (9,15)-(9,18) = "end"
diff --git a/test/prism/snapshots/whitequark/empty_stmt.txt b/test/prism/snapshots/whitequark/empty_stmt.txt
new file mode 100644
index 0000000000..3a21ce5559
--- /dev/null
+++ b/test/prism/snapshots/whitequark/empty_stmt.txt
@@ -0,0 +1,5 @@
+@ ProgramNode (location: (1,0)-(1,0))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,0))
+ └── body: (length: 0)
diff --git a/test/prism/snapshots/whitequark/endless_comparison_method.txt b/test/prism/snapshots/whitequark/endless_comparison_method.txt
new file mode 100644
index 0000000000..28ec3843bb
--- /dev/null
+++ b/test/prism/snapshots/whitequark/endless_comparison_method.txt
@@ -0,0 +1,221 @@
+@ ProgramNode (location: (1,0)-(11,28))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(11,28))
+ └── body: (length: 6)
+ ├── @ DefNode (location: (1,0)-(1,28))
+ │ ├── name: :!=
+ │ ├── name_loc: (1,4)-(1,6) = "!="
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,7)-(1,12))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,7)-(1,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :other
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,16)-(1,28))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,16)-(1,28))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :do_something
+ │ │ ├── message_loc: (1,16)-(1,28) = "do_something"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: [:other]
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (1,6)-(1,7) = "("
+ │ ├── rparen_loc: (1,12)-(1,13) = ")"
+ │ ├── equal_loc: (1,14)-(1,15) = "="
+ │ └── end_keyword_loc: ∅
+ ├── @ DefNode (location: (3,0)-(3,28))
+ │ ├── name: :!=
+ │ ├── name_loc: (3,4)-(3,6) = "!="
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (3,7)-(3,12))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (3,7)-(3,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :other
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (3,16)-(3,28))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (3,16)-(3,28))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :do_something
+ │ │ ├── message_loc: (3,16)-(3,28) = "do_something"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: [:other]
+ │ ├── def_keyword_loc: (3,0)-(3,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (3,6)-(3,7) = "("
+ │ ├── rparen_loc: (3,12)-(3,13) = ")"
+ │ ├── equal_loc: (3,14)-(3,15) = "="
+ │ └── end_keyword_loc: ∅
+ ├── @ DefNode (location: (5,0)-(5,28))
+ │ ├── name: :<=
+ │ ├── name_loc: (5,4)-(5,6) = "<="
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (5,7)-(5,12))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (5,7)-(5,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :other
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (5,16)-(5,28))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (5,16)-(5,28))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :do_something
+ │ │ ├── message_loc: (5,16)-(5,28) = "do_something"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: [:other]
+ │ ├── def_keyword_loc: (5,0)-(5,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (5,6)-(5,7) = "("
+ │ ├── rparen_loc: (5,12)-(5,13) = ")"
+ │ ├── equal_loc: (5,14)-(5,15) = "="
+ │ └── end_keyword_loc: ∅
+ ├── @ DefNode (location: (7,0)-(7,28))
+ │ ├── name: :==
+ │ ├── name_loc: (7,4)-(7,6) = "=="
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (7,7)-(7,12))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (7,7)-(7,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :other
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (7,16)-(7,28))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (7,16)-(7,28))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :do_something
+ │ │ ├── message_loc: (7,16)-(7,28) = "do_something"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: [:other]
+ │ ├── def_keyword_loc: (7,0)-(7,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (7,6)-(7,7) = "("
+ │ ├── rparen_loc: (7,12)-(7,13) = ")"
+ │ ├── equal_loc: (7,14)-(7,15) = "="
+ │ └── end_keyword_loc: ∅
+ ├── @ DefNode (location: (9,0)-(9,29))
+ │ ├── name: :===
+ │ ├── name_loc: (9,4)-(9,7) = "==="
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (9,8)-(9,13))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (9,8)-(9,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :other
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (9,17)-(9,29))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (9,17)-(9,29))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :do_something
+ │ │ ├── message_loc: (9,17)-(9,29) = "do_something"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: [:other]
+ │ ├── def_keyword_loc: (9,0)-(9,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (9,7)-(9,8) = "("
+ │ ├── rparen_loc: (9,13)-(9,14) = ")"
+ │ ├── equal_loc: (9,15)-(9,16) = "="
+ │ └── end_keyword_loc: ∅
+ └── @ DefNode (location: (11,0)-(11,28))
+ ├── name: :>=
+ ├── name_loc: (11,4)-(11,6) = ">="
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (11,7)-(11,12))
+ │ ├── requireds: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (11,7)-(11,12))
+ │ │ ├── flags: ∅
+ │ │ └── name: :other
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (11,16)-(11,28))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (11,16)-(11,28))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :do_something
+ │ ├── message_loc: (11,16)-(11,28) = "do_something"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── locals: [:other]
+ ├── def_keyword_loc: (11,0)-(11,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (11,6)-(11,7) = "("
+ ├── rparen_loc: (11,12)-(11,13) = ")"
+ ├── equal_loc: (11,14)-(11,15) = "="
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/whitequark/endless_method.txt b/test/prism/snapshots/whitequark/endless_method.txt
new file mode 100644
index 0000000000..17d3873b66
--- /dev/null
+++ b/test/prism/snapshots/whitequark/endless_method.txt
@@ -0,0 +1,151 @@
+@ ProgramNode (location: (1,0)-(7,22))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(7,22))
+ └── body: (length: 4)
+ ├── @ DefNode (location: (1,0)-(1,14))
+ │ ├── name: :foo
+ │ ├── name_loc: (1,4)-(1,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,12)-(1,14))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,12)-(1,14))
+ │ │ ├── flags: decimal
+ │ │ └── value: 42
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (1,7)-(1,8) = "("
+ │ ├── rparen_loc: (1,8)-(1,9) = ")"
+ │ ├── equal_loc: (1,10)-(1,11) = "="
+ │ └── end_keyword_loc: ∅
+ ├── @ DefNode (location: (3,0)-(3,18))
+ │ ├── name: :inc
+ │ ├── name_loc: (3,4)-(3,7) = "inc"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (3,8)-(3,9))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (3,8)-(3,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :x
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (3,13)-(3,18))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (3,13)-(3,18))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ LocalVariableReadNode (location: (3,13)-(3,14))
+ │ │ │ ├── name: :x
+ │ │ │ └── depth: 0
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :+
+ │ │ ├── message_loc: (3,15)-(3,16) = "+"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (3,17)-(3,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (3,17)-(3,18))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: [:x]
+ │ ├── def_keyword_loc: (3,0)-(3,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (3,7)-(3,8) = "("
+ │ ├── rparen_loc: (3,9)-(3,10) = ")"
+ │ ├── equal_loc: (3,11)-(3,12) = "="
+ │ └── end_keyword_loc: ∅
+ ├── @ DefNode (location: (5,0)-(5,18))
+ │ ├── name: :foo
+ │ ├── name_loc: (5,8)-(5,11) = "foo"
+ │ ├── receiver:
+ │ │ @ CallNode (location: (5,4)-(5,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :obj
+ │ │ ├── message_loc: (5,4)-(5,7) = "obj"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (5,16)-(5,18))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (5,16)-(5,18))
+ │ │ ├── flags: decimal
+ │ │ └── value: 42
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (5,0)-(5,3) = "def"
+ │ ├── operator_loc: (5,7)-(5,8) = "."
+ │ ├── lparen_loc: (5,11)-(5,12) = "("
+ │ ├── rparen_loc: (5,12)-(5,13) = ")"
+ │ ├── equal_loc: (5,14)-(5,15) = "="
+ │ └── end_keyword_loc: ∅
+ └── @ DefNode (location: (7,0)-(7,22))
+ ├── name: :inc
+ ├── name_loc: (7,8)-(7,11) = "inc"
+ ├── receiver:
+ │ @ CallNode (location: (7,4)-(7,7))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :obj
+ │ ├── message_loc: (7,4)-(7,7) = "obj"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (7,12)-(7,13))
+ │ ├── requireds: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (7,12)-(7,13))
+ │ │ ├── flags: ∅
+ │ │ └── name: :x
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (7,17)-(7,22))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (7,17)-(7,22))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (7,17)-(7,18))
+ │ │ ├── name: :x
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (7,19)-(7,20) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (7,21)-(7,22))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (7,21)-(7,22))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── locals: [:x]
+ ├── def_keyword_loc: (7,0)-(7,3) = "def"
+ ├── operator_loc: (7,7)-(7,8) = "."
+ ├── lparen_loc: (7,11)-(7,12) = "("
+ ├── rparen_loc: (7,13)-(7,14) = ")"
+ ├── equal_loc: (7,15)-(7,16) = "="
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/whitequark/endless_method_command_syntax.txt b/test/prism/snapshots/whitequark/endless_method_command_syntax.txt
new file mode 100644
index 0000000000..4ec57ccd35
--- /dev/null
+++ b/test/prism/snapshots/whitequark/endless_method_command_syntax.txt
@@ -0,0 +1,394 @@
+@ ProgramNode (location: (1,0)-(15,62))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(15,62))
+ └── body: (length: 8)
+ ├── @ DefNode (location: (1,0)-(1,22))
+ │ ├── name: :foo
+ │ ├── name_loc: (1,4)-(1,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,10)-(1,22))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,10)-(1,22))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :puts
+ │ │ ├── message_loc: (1,10)-(1,14) = "puts"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (1,15)-(1,22))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ StringNode (location: (1,15)-(1,22))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (1,15)-(1,16) = "\""
+ │ │ │ ├── content_loc: (1,16)-(1,21) = "Hello"
+ │ │ │ ├── closing_loc: (1,21)-(1,22) = "\""
+ │ │ │ └── unescaped: "Hello"
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: (1,8)-(1,9) = "="
+ │ └── end_keyword_loc: ∅
+ ├── @ DefNode (location: (3,0)-(3,24))
+ │ ├── name: :foo
+ │ ├── name_loc: (3,4)-(3,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (3,12)-(3,24))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (3,12)-(3,24))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :puts
+ │ │ ├── message_loc: (3,12)-(3,16) = "puts"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (3,17)-(3,24))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ StringNode (location: (3,17)-(3,24))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (3,17)-(3,18) = "\""
+ │ │ │ ├── content_loc: (3,18)-(3,23) = "Hello"
+ │ │ │ ├── closing_loc: (3,23)-(3,24) = "\""
+ │ │ │ └── unescaped: "Hello"
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (3,0)-(3,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (3,7)-(3,8) = "("
+ │ ├── rparen_loc: (3,8)-(3,9) = ")"
+ │ ├── equal_loc: (3,10)-(3,11) = "="
+ │ └── end_keyword_loc: ∅
+ ├── @ DefNode (location: (5,0)-(5,19))
+ │ ├── name: :foo
+ │ ├── name_loc: (5,4)-(5,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (5,8)-(5,9))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (5,8)-(5,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :x
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (5,13)-(5,19))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (5,13)-(5,19))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :puts
+ │ │ ├── message_loc: (5,13)-(5,17) = "puts"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (5,18)-(5,19))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ LocalVariableReadNode (location: (5,18)-(5,19))
+ │ │ │ ├── name: :x
+ │ │ │ └── depth: 0
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: [:x]
+ │ ├── def_keyword_loc: (5,0)-(5,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (5,7)-(5,8) = "("
+ │ ├── rparen_loc: (5,9)-(5,10) = ")"
+ │ ├── equal_loc: (5,11)-(5,12) = "="
+ │ └── end_keyword_loc: ∅
+ ├── @ DefNode (location: (7,0)-(7,26))
+ │ ├── name: :foo
+ │ ├── name_loc: (7,8)-(7,11) = "foo"
+ │ ├── receiver:
+ │ │ @ CallNode (location: (7,4)-(7,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :obj
+ │ │ ├── message_loc: (7,4)-(7,7) = "obj"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (7,14)-(7,26))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (7,14)-(7,26))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :puts
+ │ │ ├── message_loc: (7,14)-(7,18) = "puts"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (7,19)-(7,26))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ StringNode (location: (7,19)-(7,26))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (7,19)-(7,20) = "\""
+ │ │ │ ├── content_loc: (7,20)-(7,25) = "Hello"
+ │ │ │ ├── closing_loc: (7,25)-(7,26) = "\""
+ │ │ │ └── unescaped: "Hello"
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (7,0)-(7,3) = "def"
+ │ ├── operator_loc: (7,7)-(7,8) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: (7,12)-(7,13) = "="
+ │ └── end_keyword_loc: ∅
+ ├── @ DefNode (location: (9,0)-(9,28))
+ │ ├── name: :foo
+ │ ├── name_loc: (9,8)-(9,11) = "foo"
+ │ ├── receiver:
+ │ │ @ CallNode (location: (9,4)-(9,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :obj
+ │ │ ├── message_loc: (9,4)-(9,7) = "obj"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (9,16)-(9,28))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (9,16)-(9,28))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :puts
+ │ │ ├── message_loc: (9,16)-(9,20) = "puts"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (9,21)-(9,28))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ StringNode (location: (9,21)-(9,28))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (9,21)-(9,22) = "\""
+ │ │ │ ├── content_loc: (9,22)-(9,27) = "Hello"
+ │ │ │ ├── closing_loc: (9,27)-(9,28) = "\""
+ │ │ │ └── unescaped: "Hello"
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (9,0)-(9,3) = "def"
+ │ ├── operator_loc: (9,7)-(9,8) = "."
+ │ ├── lparen_loc: (9,11)-(9,12) = "("
+ │ ├── rparen_loc: (9,12)-(9,13) = ")"
+ │ ├── equal_loc: (9,14)-(9,15) = "="
+ │ └── end_keyword_loc: ∅
+ ├── @ DefNode (location: (11,0)-(11,23))
+ │ ├── name: :foo
+ │ ├── name_loc: (11,8)-(11,11) = "foo"
+ │ ├── receiver:
+ │ │ @ CallNode (location: (11,4)-(11,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :obj
+ │ │ ├── message_loc: (11,4)-(11,7) = "obj"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (11,12)-(11,13))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (11,12)-(11,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :x
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (11,17)-(11,23))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (11,17)-(11,23))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :puts
+ │ │ ├── message_loc: (11,17)-(11,21) = "puts"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (11,22)-(11,23))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ LocalVariableReadNode (location: (11,22)-(11,23))
+ │ │ │ ├── name: :x
+ │ │ │ └── depth: 0
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: [:x]
+ │ ├── def_keyword_loc: (11,0)-(11,3) = "def"
+ │ ├── operator_loc: (11,7)-(11,8) = "."
+ │ ├── lparen_loc: (11,11)-(11,12) = "("
+ │ ├── rparen_loc: (11,13)-(11,14) = ")"
+ │ ├── equal_loc: (11,15)-(11,16) = "="
+ │ └── end_keyword_loc: ∅
+ ├── @ DefNode (location: (13,0)-(13,60))
+ │ ├── name: :rescued
+ │ ├── name_loc: (13,4)-(13,11) = "rescued"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (13,12)-(13,13))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (13,12)-(13,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :x
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (13,17)-(13,60))
+ │ │ └── body: (length: 1)
+ │ │ └── @ RescueModifierNode (location: (13,17)-(13,60))
+ │ │ ├── expression:
+ │ │ │ @ CallNode (location: (13,17)-(13,37))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :raise
+ │ │ │ ├── message_loc: (13,17)-(13,22) = "raise"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (13,23)-(13,37))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ StringNode (location: (13,23)-(13,37))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: (13,23)-(13,24) = "\""
+ │ │ │ │ ├── content_loc: (13,24)-(13,36) = "to be caught"
+ │ │ │ │ ├── closing_loc: (13,36)-(13,37) = "\""
+ │ │ │ │ └── unescaped: "to be caught"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── keyword_loc: (13,38)-(13,44) = "rescue"
+ │ │ └── rescue_expression:
+ │ │ @ InterpolatedStringNode (location: (13,45)-(13,60))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (13,45)-(13,46) = "\""
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (13,46)-(13,55))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (13,46)-(13,55) = "instance "
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "instance "
+ │ │ │ └── @ EmbeddedStatementsNode (location: (13,55)-(13,59))
+ │ │ │ ├── opening_loc: (13,55)-(13,57) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (13,57)-(13,58))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ LocalVariableReadNode (location: (13,57)-(13,58))
+ │ │ │ │ ├── name: :x
+ │ │ │ │ └── depth: 0
+ │ │ │ └── closing_loc: (13,58)-(13,59) = "}"
+ │ │ └── closing_loc: (13,59)-(13,60) = "\""
+ │ ├── locals: [:x]
+ │ ├── def_keyword_loc: (13,0)-(13,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (13,11)-(13,12) = "("
+ │ ├── rparen_loc: (13,13)-(13,14) = ")"
+ │ ├── equal_loc: (13,15)-(13,16) = "="
+ │ └── end_keyword_loc: ∅
+ └── @ DefNode (location: (15,0)-(15,62))
+ ├── name: :rescued
+ ├── name_loc: (15,9)-(15,16) = "rescued"
+ ├── receiver:
+ │ @ SelfNode (location: (15,4)-(15,8))
+ ├── parameters:
+ │ @ ParametersNode (location: (15,17)-(15,18))
+ │ ├── requireds: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (15,17)-(15,18))
+ │ │ ├── flags: ∅
+ │ │ └── name: :x
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (15,22)-(15,62))
+ │ └── body: (length: 1)
+ │ └── @ RescueModifierNode (location: (15,22)-(15,62))
+ │ ├── expression:
+ │ │ @ CallNode (location: (15,22)-(15,42))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :raise
+ │ │ ├── message_loc: (15,22)-(15,27) = "raise"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (15,28)-(15,42))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ StringNode (location: (15,28)-(15,42))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (15,28)-(15,29) = "\""
+ │ │ │ ├── content_loc: (15,29)-(15,41) = "to be caught"
+ │ │ │ ├── closing_loc: (15,41)-(15,42) = "\""
+ │ │ │ └── unescaped: "to be caught"
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (15,43)-(15,49) = "rescue"
+ │ └── rescue_expression:
+ │ @ InterpolatedStringNode (location: (15,50)-(15,62))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (15,50)-(15,51) = "\""
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (15,51)-(15,57))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (15,51)-(15,57) = "class "
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "class "
+ │ │ └── @ EmbeddedStatementsNode (location: (15,57)-(15,61))
+ │ │ ├── opening_loc: (15,57)-(15,59) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (15,59)-(15,60))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ LocalVariableReadNode (location: (15,59)-(15,60))
+ │ │ │ ├── name: :x
+ │ │ │ └── depth: 0
+ │ │ └── closing_loc: (15,60)-(15,61) = "}"
+ │ └── closing_loc: (15,61)-(15,62) = "\""
+ ├── locals: [:x]
+ ├── def_keyword_loc: (15,0)-(15,3) = "def"
+ ├── operator_loc: (15,8)-(15,9) = "."
+ ├── lparen_loc: (15,16)-(15,17) = "("
+ ├── rparen_loc: (15,18)-(15,19) = ")"
+ ├── equal_loc: (15,20)-(15,21) = "="
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/whitequark/endless_method_forwarded_args_legacy.txt b/test/prism/snapshots/whitequark/endless_method_forwarded_args_legacy.txt
new file mode 100644
index 0000000000..64a3ffa252
--- /dev/null
+++ b/test/prism/snapshots/whitequark/endless_method_forwarded_args_legacy.txt
@@ -0,0 +1,43 @@
+@ ProgramNode (location: (1,0)-(1,23))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,23))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,23))
+ ├── name: :foo
+ ├── name_loc: (1,4)-(1,7) = "foo"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,8)-(1,11))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest:
+ │ │ @ ForwardingParameterNode (location: (1,8)-(1,11))
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,15)-(1,23))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,15)-(1,23))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (1,15)-(1,18) = "bar"
+ │ ├── opening_loc: (1,18)-(1,19) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,19)-(1,22))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ ForwardingArgumentsNode (location: (1,19)-(1,22))
+ │ ├── closing_loc: (1,22)-(1,23) = ")"
+ │ └── block: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,7)-(1,8) = "("
+ ├── rparen_loc: (1,11)-(1,12) = ")"
+ ├── equal_loc: (1,13)-(1,14) = "="
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/whitequark/endless_method_with_rescue_mod.txt b/test/prism/snapshots/whitequark/endless_method_with_rescue_mod.txt
new file mode 100644
index 0000000000..2284b24354
--- /dev/null
+++ b/test/prism/snapshots/whitequark/endless_method_with_rescue_mod.txt
@@ -0,0 +1,56 @@
+@ ProgramNode (location: (1,0)-(3,25))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,25))
+ └── body: (length: 2)
+ ├── @ DefNode (location: (1,0)-(1,20))
+ │ ├── name: :m
+ │ ├── name_loc: (1,4)-(1,5) = "m"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,10)-(1,20))
+ │ │ └── body: (length: 1)
+ │ │ └── @ RescueModifierNode (location: (1,10)-(1,20))
+ │ │ ├── expression:
+ │ │ │ @ IntegerNode (location: (1,10)-(1,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── keyword_loc: (1,12)-(1,18) = "rescue"
+ │ │ └── rescue_expression:
+ │ │ @ IntegerNode (location: (1,19)-(1,20))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (1,5)-(1,6) = "("
+ │ ├── rparen_loc: (1,6)-(1,7) = ")"
+ │ ├── equal_loc: (1,8)-(1,9) = "="
+ │ └── end_keyword_loc: ∅
+ └── @ DefNode (location: (3,0)-(3,25))
+ ├── name: :m
+ ├── name_loc: (3,9)-(3,10) = "m"
+ ├── receiver:
+ │ @ SelfNode (location: (3,4)-(3,8))
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (3,15)-(3,25))
+ │ └── body: (length: 1)
+ │ └── @ RescueModifierNode (location: (3,15)-(3,25))
+ │ ├── expression:
+ │ │ @ IntegerNode (location: (3,15)-(3,16))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── keyword_loc: (3,17)-(3,23) = "rescue"
+ │ └── rescue_expression:
+ │ @ IntegerNode (location: (3,24)-(3,25))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── locals: []
+ ├── def_keyword_loc: (3,0)-(3,3) = "def"
+ ├── operator_loc: (3,8)-(3,9) = "."
+ ├── lparen_loc: (3,10)-(3,11) = "("
+ ├── rparen_loc: (3,11)-(3,12) = ")"
+ ├── equal_loc: (3,13)-(3,14) = "="
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/whitequark/endless_method_without_args.txt b/test/prism/snapshots/whitequark/endless_method_without_args.txt
new file mode 100644
index 0000000000..a7a9c93ef3
--- /dev/null
+++ b/test/prism/snapshots/whitequark/endless_method_without_args.txt
@@ -0,0 +1,89 @@
+@ ProgramNode (location: (1,0)-(7,28))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(7,28))
+ └── body: (length: 4)
+ ├── @ DefNode (location: (1,0)-(1,12))
+ │ ├── name: :foo
+ │ ├── name_loc: (1,4)-(1,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,10)-(1,12))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,10)-(1,12))
+ │ │ ├── flags: decimal
+ │ │ └── value: 42
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: (1,8)-(1,9) = "="
+ │ └── end_keyword_loc: ∅
+ ├── @ DefNode (location: (3,0)-(3,23))
+ │ ├── name: :foo
+ │ ├── name_loc: (3,4)-(3,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (3,10)-(3,23))
+ │ │ └── body: (length: 1)
+ │ │ └── @ RescueModifierNode (location: (3,10)-(3,23))
+ │ │ ├── expression:
+ │ │ │ @ IntegerNode (location: (3,10)-(3,12))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ ├── keyword_loc: (3,13)-(3,19) = "rescue"
+ │ │ └── rescue_expression:
+ │ │ @ NilNode (location: (3,20)-(3,23))
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (3,0)-(3,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: (3,8)-(3,9) = "="
+ │ └── end_keyword_loc: ∅
+ ├── @ DefNode (location: (5,0)-(5,17))
+ │ ├── name: :foo
+ │ ├── name_loc: (5,9)-(5,12) = "foo"
+ │ ├── receiver:
+ │ │ @ SelfNode (location: (5,4)-(5,8))
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (5,15)-(5,17))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (5,15)-(5,17))
+ │ │ ├── flags: decimal
+ │ │ └── value: 42
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (5,0)-(5,3) = "def"
+ │ ├── operator_loc: (5,8)-(5,9) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: (5,13)-(5,14) = "="
+ │ └── end_keyword_loc: ∅
+ └── @ DefNode (location: (7,0)-(7,28))
+ ├── name: :foo
+ ├── name_loc: (7,9)-(7,12) = "foo"
+ ├── receiver:
+ │ @ SelfNode (location: (7,4)-(7,8))
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (7,15)-(7,28))
+ │ └── body: (length: 1)
+ │ └── @ RescueModifierNode (location: (7,15)-(7,28))
+ │ ├── expression:
+ │ │ @ IntegerNode (location: (7,15)-(7,17))
+ │ │ ├── flags: decimal
+ │ │ └── value: 42
+ │ ├── keyword_loc: (7,18)-(7,24) = "rescue"
+ │ └── rescue_expression:
+ │ @ NilNode (location: (7,25)-(7,28))
+ ├── locals: []
+ ├── def_keyword_loc: (7,0)-(7,3) = "def"
+ ├── operator_loc: (7,8)-(7,9) = "."
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: (7,13)-(7,14) = "="
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/whitequark/ensure.txt b/test/prism/snapshots/whitequark/ensure.txt
new file mode 100644
index 0000000000..a48d2370e8
--- /dev/null
+++ b/test/prism/snapshots/whitequark/ensure.txt
@@ -0,0 +1,40 @@
+@ ProgramNode (location: (1,0)-(1,29))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,29))
+ └── body: (length: 1)
+ └── @ BeginNode (location: (1,0)-(1,29))
+ ├── begin_keyword_loc: (1,0)-(1,5) = "begin"
+ ├── statements:
+ │ @ StatementsNode (location: (1,7)-(1,11))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,7)-(1,11))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :meth
+ │ ├── message_loc: (1,7)-(1,11) = "meth"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── rescue_clause: ∅
+ ├── else_clause: ∅
+ ├── ensure_clause:
+ │ @ EnsureNode (location: (1,13)-(1,29))
+ │ ├── ensure_keyword_loc: (1,13)-(1,19) = "ensure"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,21)-(1,24))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,21)-(1,24))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,21)-(1,24) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── end_keyword_loc: (1,26)-(1,29) = "end"
+ └── end_keyword_loc: (1,26)-(1,29) = "end"
diff --git a/test/prism/snapshots/whitequark/ensure_empty.txt b/test/prism/snapshots/whitequark/ensure_empty.txt
new file mode 100644
index 0000000000..0bab5d80c3
--- /dev/null
+++ b/test/prism/snapshots/whitequark/ensure_empty.txt
@@ -0,0 +1,16 @@
+@ ProgramNode (location: (1,0)-(1,16))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,16))
+ └── body: (length: 1)
+ └── @ BeginNode (location: (1,0)-(1,16))
+ ├── begin_keyword_loc: (1,0)-(1,5) = "begin"
+ ├── statements: ∅
+ ├── rescue_clause: ∅
+ ├── else_clause: ∅
+ ├── ensure_clause:
+ │ @ EnsureNode (location: (1,6)-(1,16))
+ │ ├── ensure_keyword_loc: (1,6)-(1,12) = "ensure"
+ │ ├── statements: ∅
+ │ └── end_keyword_loc: (1,13)-(1,16) = "end"
+ └── end_keyword_loc: (1,13)-(1,16) = "end"
diff --git a/test/prism/snapshots/whitequark/false.txt b/test/prism/snapshots/whitequark/false.txt
new file mode 100644
index 0000000000..00562f703a
--- /dev/null
+++ b/test/prism/snapshots/whitequark/false.txt
@@ -0,0 +1,6 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ FalseNode (location: (1,0)-(1,5))
diff --git a/test/prism/snapshots/whitequark/float.txt b/test/prism/snapshots/whitequark/float.txt
new file mode 100644
index 0000000000..5e6a597db7
--- /dev/null
+++ b/test/prism/snapshots/whitequark/float.txt
@@ -0,0 +1,9 @@
+@ ProgramNode (location: (1,0)-(3,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,4))
+ └── body: (length: 2)
+ ├── @ FloatNode (location: (1,0)-(1,5))
+ │ └── value: -1.33
+ └── @ FloatNode (location: (3,0)-(3,4))
+ └── value: 1.33
diff --git a/test/prism/snapshots/whitequark/for.txt b/test/prism/snapshots/whitequark/for.txt
new file mode 100644
index 0000000000..fec8bdfd64
--- /dev/null
+++ b/test/prism/snapshots/whitequark/for.txt
@@ -0,0 +1,83 @@
+@ ProgramNode (location: (1,0)-(3,22))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,22))
+ └── body: (length: 2)
+ ├── @ ForNode (location: (1,0)-(1,24))
+ │ ├── index:
+ │ │ @ LocalVariableTargetNode (location: (1,4)-(1,5))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── collection:
+ │ │ @ CallNode (location: (1,9)-(1,12))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,9)-(1,12) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,16)-(1,19))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,16)-(1,19))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :p
+ │ │ ├── message_loc: (1,16)-(1,17) = "p"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (1,18)-(1,19))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ LocalVariableReadNode (location: (1,18)-(1,19))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── for_keyword_loc: (1,0)-(1,3) = "for"
+ │ ├── in_keyword_loc: (1,6)-(1,8) = "in"
+ │ ├── do_keyword_loc: (1,13)-(1,15) = "do"
+ │ └── end_keyword_loc: (1,21)-(1,24) = "end"
+ └── @ ForNode (location: (3,0)-(3,22))
+ ├── index:
+ │ @ LocalVariableTargetNode (location: (3,4)-(3,5))
+ │ ├── name: :a
+ │ └── depth: 0
+ ├── collection:
+ │ @ CallNode (location: (3,9)-(3,12))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (3,9)-(3,12) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── statements:
+ │ @ StatementsNode (location: (3,14)-(3,17))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (3,14)-(3,17))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :p
+ │ ├── message_loc: (3,14)-(3,15) = "p"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,16)-(3,17))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (3,16)-(3,17))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── for_keyword_loc: (3,0)-(3,3) = "for"
+ ├── in_keyword_loc: (3,6)-(3,8) = "in"
+ ├── do_keyword_loc: ∅
+ └── end_keyword_loc: (3,19)-(3,22) = "end"
diff --git a/test/prism/snapshots/whitequark/for_mlhs.txt b/test/prism/snapshots/whitequark/for_mlhs.txt
new file mode 100644
index 0000000000..42d8fa2258
--- /dev/null
+++ b/test/prism/snapshots/whitequark/for_mlhs.txt
@@ -0,0 +1,56 @@
+@ ProgramNode (location: (1,0)-(1,28))
+├── locals: [:a, :b]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,28))
+ └── body: (length: 1)
+ └── @ ForNode (location: (1,0)-(1,28))
+ ├── index:
+ │ @ MultiTargetNode (location: (1,4)-(1,8))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ LocalVariableTargetNode (location: (1,4)-(1,5))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ └── @ LocalVariableTargetNode (location: (1,7)-(1,8))
+ │ │ ├── name: :b
+ │ │ └── depth: 0
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: ∅
+ │ └── rparen_loc: ∅
+ ├── collection:
+ │ @ CallNode (location: (1,12)-(1,15))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,12)-(1,15) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── statements:
+ │ @ StatementsNode (location: (1,17)-(1,23))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,17)-(1,23))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :p
+ │ ├── message_loc: (1,17)-(1,18) = "p"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,19)-(1,23))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ LocalVariableReadNode (location: (1,19)-(1,20))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ └── @ LocalVariableReadNode (location: (1,22)-(1,23))
+ │ │ ├── name: :b
+ │ │ └── depth: 0
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── for_keyword_loc: (1,0)-(1,3) = "for"
+ ├── in_keyword_loc: (1,9)-(1,11) = "in"
+ ├── do_keyword_loc: ∅
+ └── end_keyword_loc: (1,25)-(1,28) = "end"
diff --git a/test/prism/snapshots/whitequark/forward_arg.txt b/test/prism/snapshots/whitequark/forward_arg.txt
new file mode 100644
index 0000000000..17504c64e0
--- /dev/null
+++ b/test/prism/snapshots/whitequark/forward_arg.txt
@@ -0,0 +1,43 @@
+@ ProgramNode (location: (1,0)-(1,27))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,27))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,27))
+ ├── name: :foo
+ ├── name_loc: (1,4)-(1,7) = "foo"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,8)-(1,11))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest:
+ │ │ @ ForwardingParameterNode (location: (1,8)-(1,11))
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,14)-(1,22))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,14)-(1,22))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (1,14)-(1,17) = "bar"
+ │ ├── opening_loc: (1,17)-(1,18) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,18)-(1,21))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ ForwardingArgumentsNode (location: (1,18)-(1,21))
+ │ ├── closing_loc: (1,21)-(1,22) = ")"
+ │ └── block: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,7)-(1,8) = "("
+ ├── rparen_loc: (1,11)-(1,12) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,24)-(1,27) = "end"
diff --git a/test/prism/snapshots/whitequark/forward_arg_with_open_args.txt b/test/prism/snapshots/whitequark/forward_arg_with_open_args.txt
new file mode 100644
index 0000000000..7e58260b58
--- /dev/null
+++ b/test/prism/snapshots/whitequark/forward_arg_with_open_args.txt
@@ -0,0 +1,404 @@
+@ ProgramNode (location: (1,0)-(27,28))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(27,28))
+ └── body: (length: 10)
+ ├── @ ParenthesesNode (location: (1,0)-(3,4))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,1)-(3,3))
+ │ │ └── body: (length: 1)
+ │ │ └── @ DefNode (location: (1,1)-(3,3))
+ │ │ ├── name: :foo
+ │ │ ├── name_loc: (1,5)-(1,8) = "foo"
+ │ │ ├── receiver: ∅
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (1,9)-(1,12))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest:
+ │ │ │ │ @ ForwardingParameterNode (location: (1,9)-(1,12))
+ │ │ │ └── block: ∅
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (2,2)-(2,10))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (2,2)-(2,10))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (2,2)-(2,5) = "bar"
+ │ │ │ ├── opening_loc: (2,5)-(2,6) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (2,6)-(2,9))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ ForwardingArgumentsNode (location: (2,6)-(2,9))
+ │ │ │ ├── closing_loc: (2,9)-(2,10) = ")"
+ │ │ │ └── block: ∅
+ │ │ ├── locals: []
+ │ │ ├── def_keyword_loc: (1,1)-(1,4) = "def"
+ │ │ ├── operator_loc: ∅
+ │ │ ├── lparen_loc: ∅
+ │ │ ├── rparen_loc: ∅
+ │ │ ├── equal_loc: ∅
+ │ │ └── end_keyword_loc: (3,0)-(3,3) = "end"
+ │ ├── opening_loc: (1,0)-(1,1) = "("
+ │ └── closing_loc: (3,3)-(3,4) = ")"
+ ├── @ ParenthesesNode (location: (5,0)-(5,28))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (5,1)-(5,27))
+ │ │ └── body: (length: 1)
+ │ │ └── @ DefNode (location: (5,1)-(5,27))
+ │ │ ├── name: :foo
+ │ │ ├── name_loc: (5,5)-(5,8) = "foo"
+ │ │ ├── receiver: ∅
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (5,9)-(5,12))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest:
+ │ │ │ │ @ ForwardingParameterNode (location: (5,9)-(5,12))
+ │ │ │ └── block: ∅
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (5,14)-(5,22))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (5,14)-(5,22))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (5,14)-(5,17) = "bar"
+ │ │ │ ├── opening_loc: (5,17)-(5,18) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (5,18)-(5,21))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ ForwardingArgumentsNode (location: (5,18)-(5,21))
+ │ │ │ ├── closing_loc: (5,21)-(5,22) = ")"
+ │ │ │ └── block: ∅
+ │ │ ├── locals: []
+ │ │ ├── def_keyword_loc: (5,1)-(5,4) = "def"
+ │ │ ├── operator_loc: ∅
+ │ │ ├── lparen_loc: ∅
+ │ │ ├── rparen_loc: ∅
+ │ │ ├── equal_loc: ∅
+ │ │ └── end_keyword_loc: (5,24)-(5,27) = "end"
+ │ ├── opening_loc: (5,0)-(5,1) = "("
+ │ └── closing_loc: (5,27)-(5,28) = ")"
+ ├── @ DefNode (location: (7,0)-(8,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (7,4)-(7,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (7,8)-(7,11))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ ForwardingParameterNode (location: (7,8)-(7,11))
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (7,0)-(7,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (8,0)-(8,3) = "end"
+ ├── @ DefNode (location: (10,0)-(10,26))
+ │ ├── name: :foo
+ │ ├── name_loc: (10,4)-(10,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (10,8)-(10,11))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ ForwardingParameterNode (location: (10,8)-(10,11))
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (10,13)-(10,21))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (10,13)-(10,21))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (10,13)-(10,16) = "bar"
+ │ │ ├── opening_loc: (10,16)-(10,17) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (10,17)-(10,20))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ ForwardingArgumentsNode (location: (10,17)-(10,20))
+ │ │ ├── closing_loc: (10,20)-(10,21) = ")"
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (10,0)-(10,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (10,23)-(10,26) = "end"
+ ├── @ DefNode (location: (12,0)-(14,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (12,4)-(12,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (12,8)-(12,14))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (12,8)-(12,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ ForwardingParameterNode (location: (12,11)-(12,14))
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (13,2)-(13,10))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (13,2)-(13,10))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (13,2)-(13,5) = "bar"
+ │ │ ├── opening_loc: (13,5)-(13,6) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (13,6)-(13,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ ForwardingArgumentsNode (location: (13,6)-(13,9))
+ │ │ ├── closing_loc: (13,9)-(13,10) = ")"
+ │ │ └── block: ∅
+ │ ├── locals: [:a]
+ │ ├── def_keyword_loc: (12,0)-(12,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (14,0)-(14,3) = "end"
+ ├── @ DefNode (location: (16,0)-(16,29))
+ │ ├── name: :foo
+ │ ├── name_loc: (16,4)-(16,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (16,8)-(16,14))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (16,8)-(16,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ ForwardingParameterNode (location: (16,11)-(16,14))
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (16,16)-(16,24))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (16,16)-(16,24))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (16,16)-(16,19) = "bar"
+ │ │ ├── opening_loc: (16,19)-(16,20) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (16,20)-(16,23))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ ForwardingArgumentsNode (location: (16,20)-(16,23))
+ │ │ ├── closing_loc: (16,23)-(16,24) = ")"
+ │ │ └── block: ∅
+ │ ├── locals: [:a]
+ │ ├── def_keyword_loc: (16,0)-(16,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (16,26)-(16,29) = "end"
+ ├── @ DefNode (location: (18,0)-(19,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (18,4)-(18,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (18,8)-(18,21))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (18,8)-(18,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (18,11)-(18,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (18,11)-(18,12) = "b"
+ │ │ │ ├── operator_loc: (18,13)-(18,14) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (18,15)-(18,16))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ ForwardingParameterNode (location: (18,18)-(18,21))
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:a, :b]
+ │ ├── def_keyword_loc: (18,0)-(18,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (19,0)-(19,3) = "end"
+ ├── @ DefNode (location: (21,0)-(23,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (21,4)-(21,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (21,8)-(21,18))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (21,8)-(21,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (21,8)-(21,9) = "b"
+ │ │ │ ├── operator_loc: (21,10)-(21,11) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (21,12)-(21,13))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ ForwardingParameterNode (location: (21,15)-(21,18))
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (22,2)-(22,10))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (22,2)-(22,10))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (22,2)-(22,5) = "bar"
+ │ │ ├── opening_loc: (22,5)-(22,6) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (22,6)-(22,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ ForwardingArgumentsNode (location: (22,6)-(22,9))
+ │ │ ├── closing_loc: (22,9)-(22,10) = ")"
+ │ │ └── block: ∅
+ │ ├── locals: [:b]
+ │ ├── def_keyword_loc: (21,0)-(21,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (23,0)-(23,3) = "end"
+ ├── @ DefNode (location: (25,0)-(25,33))
+ │ ├── name: :foo
+ │ ├── name_loc: (25,4)-(25,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (25,8)-(25,18))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (25,8)-(25,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── name_loc: (25,8)-(25,9) = "b"
+ │ │ │ ├── operator_loc: (25,10)-(25,11) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (25,12)-(25,13))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ ForwardingParameterNode (location: (25,15)-(25,18))
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (25,20)-(25,28))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (25,20)-(25,28))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (25,20)-(25,23) = "bar"
+ │ │ ├── opening_loc: (25,23)-(25,24) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (25,24)-(25,27))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ ForwardingArgumentsNode (location: (25,24)-(25,27))
+ │ │ ├── closing_loc: (25,27)-(25,28) = ")"
+ │ │ └── block: ∅
+ │ ├── locals: [:b]
+ │ ├── def_keyword_loc: (25,0)-(25,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (25,30)-(25,33) = "end"
+ └── @ DefNode (location: (27,0)-(27,28))
+ ├── name: :foo
+ ├── name_loc: (27,4)-(27,7) = "foo"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (27,8)-(27,14))
+ │ ├── requireds: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (27,8)-(27,9))
+ │ │ ├── flags: ∅
+ │ │ └── name: :a
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest:
+ │ │ @ ForwardingParameterNode (location: (27,11)-(27,14))
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (27,16)-(27,24))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (27,16)-(27,24))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (27,16)-(27,19) = "bar"
+ │ ├── opening_loc: (27,19)-(27,20) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (27,20)-(27,23))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ ForwardingArgumentsNode (location: (27,20)-(27,23))
+ │ ├── closing_loc: (27,23)-(27,24) = ")"
+ │ └── block: ∅
+ ├── locals: [:a]
+ ├── def_keyword_loc: (27,0)-(27,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (27,7)-(27,8) = "("
+ ├── rparen_loc: (27,14)-(27,15) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (27,25)-(27,28) = "end"
diff --git a/test/prism/snapshots/whitequark/forward_args_legacy.txt b/test/prism/snapshots/whitequark/forward_args_legacy.txt
new file mode 100644
index 0000000000..f46967ba50
--- /dev/null
+++ b/test/prism/snapshots/whitequark/forward_args_legacy.txt
@@ -0,0 +1,99 @@
+@ ProgramNode (location: (1,0)-(5,29))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,29))
+ └── body: (length: 3)
+ ├── @ DefNode (location: (1,0)-(1,27))
+ │ ├── name: :foo
+ │ ├── name_loc: (1,4)-(1,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,8)-(1,11))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ ForwardingParameterNode (location: (1,8)-(1,11))
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,14)-(1,22))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,14)-(1,22))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,14)-(1,17) = "bar"
+ │ │ ├── opening_loc: (1,17)-(1,18) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (1,18)-(1,21))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ ForwardingArgumentsNode (location: (1,18)-(1,21))
+ │ │ ├── closing_loc: (1,21)-(1,22) = ")"
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (1,7)-(1,8) = "("
+ │ ├── rparen_loc: (1,11)-(1,12) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (1,24)-(1,27) = "end"
+ ├── @ DefNode (location: (3,0)-(3,17))
+ │ ├── name: :foo
+ │ ├── name_loc: (3,4)-(3,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (3,8)-(3,11))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ ForwardingParameterNode (location: (3,8)-(3,11))
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (3,0)-(3,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (3,7)-(3,8) = "("
+ │ ├── rparen_loc: (3,11)-(3,12) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (3,14)-(3,17) = "end"
+ └── @ DefNode (location: (5,0)-(5,29))
+ ├── name: :foo
+ ├── name_loc: (5,4)-(5,7) = "foo"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (5,8)-(5,11))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest:
+ │ │ @ ForwardingParameterNode (location: (5,8)-(5,11))
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (5,14)-(5,24))
+ │ └── body: (length: 1)
+ │ └── @ SuperNode (location: (5,14)-(5,24))
+ │ ├── keyword_loc: (5,14)-(5,19) = "super"
+ │ ├── lparen_loc: (5,19)-(5,20) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (5,20)-(5,23))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ ForwardingArgumentsNode (location: (5,20)-(5,23))
+ │ ├── rparen_loc: (5,23)-(5,24) = ")"
+ │ └── block: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (5,0)-(5,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (5,7)-(5,8) = "("
+ ├── rparen_loc: (5,11)-(5,12) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (5,26)-(5,29) = "end"
diff --git a/test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt b/test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt
new file mode 100644
index 0000000000..c06818a98b
--- /dev/null
+++ b/test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt
@@ -0,0 +1,58 @@
+@ ProgramNode (location: (1,0)-(1,45))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,45))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,45))
+ ├── name: :foo
+ ├── name_loc: (1,4)-(1,7) = "foo"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,8)-(1,20))
+ │ ├── requireds: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (1,8)-(1,16))
+ │ │ ├── flags: ∅
+ │ │ └── name: :argument
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest:
+ │ │ @ KeywordRestParameterNode (location: (1,18)-(1,20))
+ │ │ ├── flags: ∅
+ │ │ ├── name: ∅
+ │ │ ├── name_loc: ∅
+ │ │ └── operator_loc: (1,18)-(1,20) = "**"
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,23)-(1,40))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,23)-(1,40))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (1,23)-(1,26) = "bar"
+ │ ├── opening_loc: (1,26)-(1,27) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,27)-(1,39))
+ │ │ ├── flags: contains_keyword_splat
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ LocalVariableReadNode (location: (1,27)-(1,35))
+ │ │ │ ├── name: :argument
+ │ │ │ └── depth: 0
+ │ │ └── @ KeywordHashNode (location: (1,37)-(1,39))
+ │ │ ├── flags: ∅
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocSplatNode (location: (1,37)-(1,39))
+ │ │ ├── value: ∅
+ │ │ └── operator_loc: (1,37)-(1,39) = "**"
+ │ ├── closing_loc: (1,39)-(1,40) = ")"
+ │ └── block: ∅
+ ├── locals: [:argument]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,7)-(1,8) = "("
+ ├── rparen_loc: (1,20)-(1,21) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,42)-(1,45) = "end"
diff --git a/test/prism/snapshots/whitequark/forwarded_argument_with_restarg.txt b/test/prism/snapshots/whitequark/forwarded_argument_with_restarg.txt
new file mode 100644
index 0000000000..367fad7fec
--- /dev/null
+++ b/test/prism/snapshots/whitequark/forwarded_argument_with_restarg.txt
@@ -0,0 +1,55 @@
+@ ProgramNode (location: (1,0)-(1,43))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,43))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,43))
+ ├── name: :foo
+ ├── name_loc: (1,4)-(1,7) = "foo"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,8)-(1,19))
+ │ ├── requireds: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (1,8)-(1,16))
+ │ │ ├── flags: ∅
+ │ │ └── name: :argument
+ │ ├── optionals: (length: 0)
+ │ ├── rest:
+ │ │ @ RestParameterNode (location: (1,18)-(1,19))
+ │ │ ├── flags: ∅
+ │ │ ├── name: ∅
+ │ │ ├── name_loc: ∅
+ │ │ └── operator_loc: (1,18)-(1,19) = "*"
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,22)-(1,38))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,22)-(1,38))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (1,22)-(1,25) = "bar"
+ │ ├── opening_loc: (1,25)-(1,26) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,26)-(1,37))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ LocalVariableReadNode (location: (1,26)-(1,34))
+ │ │ │ ├── name: :argument
+ │ │ │ └── depth: 0
+ │ │ └── @ SplatNode (location: (1,36)-(1,37))
+ │ │ ├── operator_loc: (1,36)-(1,37) = "*"
+ │ │ └── expression: ∅
+ │ ├── closing_loc: (1,37)-(1,38) = ")"
+ │ └── block: ∅
+ ├── locals: [:argument]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,7)-(1,8) = "("
+ ├── rparen_loc: (1,19)-(1,20) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,40)-(1,43) = "end"
diff --git a/test/prism/snapshots/whitequark/forwarded_kwrestarg.txt b/test/prism/snapshots/whitequark/forwarded_kwrestarg.txt
new file mode 100644
index 0000000000..adaf111fd7
--- /dev/null
+++ b/test/prism/snapshots/whitequark/forwarded_kwrestarg.txt
@@ -0,0 +1,52 @@
+@ ProgramNode (location: (1,0)-(1,25))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,25))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,25))
+ ├── name: :foo
+ ├── name_loc: (1,4)-(1,7) = "foo"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,8)-(1,10))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest:
+ │ │ @ KeywordRestParameterNode (location: (1,8)-(1,10))
+ │ │ ├── flags: ∅
+ │ │ ├── name: ∅
+ │ │ ├── name_loc: ∅
+ │ │ └── operator_loc: (1,8)-(1,10) = "**"
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,13)-(1,20))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,13)-(1,20))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (1,13)-(1,16) = "bar"
+ │ ├── opening_loc: (1,16)-(1,17) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,17)-(1,19))
+ │ │ ├── flags: contains_keyword_splat
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (1,17)-(1,19))
+ │ │ ├── flags: ∅
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocSplatNode (location: (1,17)-(1,19))
+ │ │ ├── value: ∅
+ │ │ └── operator_loc: (1,17)-(1,19) = "**"
+ │ ├── closing_loc: (1,19)-(1,20) = ")"
+ │ └── block: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,7)-(1,8) = "("
+ ├── rparen_loc: (1,10)-(1,11) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,22)-(1,25) = "end"
diff --git a/test/prism/snapshots/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt b/test/prism/snapshots/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt
new file mode 100644
index 0000000000..a03421455c
--- /dev/null
+++ b/test/prism/snapshots/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt
@@ -0,0 +1,63 @@
+@ ProgramNode (location: (1,0)-(1,41))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,41))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,41))
+ ├── name: :foo
+ ├── name_loc: (1,4)-(1,7) = "foo"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,8)-(1,10))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest:
+ │ │ @ KeywordRestParameterNode (location: (1,8)-(1,10))
+ │ │ ├── flags: ∅
+ │ │ ├── name: ∅
+ │ │ ├── name_loc: ∅
+ │ │ └── operator_loc: (1,8)-(1,10) = "**"
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,13)-(1,36))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,13)-(1,36))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (1,13)-(1,16) = "bar"
+ │ ├── opening_loc: (1,16)-(1,17) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,17)-(1,35))
+ │ │ ├── flags: contains_keyword_splat
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (1,17)-(1,35))
+ │ │ ├── flags: ∅
+ │ │ └── elements: (length: 2)
+ │ │ ├── @ AssocSplatNode (location: (1,17)-(1,19))
+ │ │ │ ├── value: ∅
+ │ │ │ └── operator_loc: (1,17)-(1,19) = "**"
+ │ │ └── @ AssocNode (location: (1,21)-(1,35))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (1,21)-(1,30))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (1,21)-(1,29) = "from_foo"
+ │ │ │ ├── closing_loc: (1,29)-(1,30) = ":"
+ │ │ │ └── unescaped: "from_foo"
+ │ │ ├── value:
+ │ │ │ @ TrueNode (location: (1,31)-(1,35))
+ │ │ └── operator_loc: ∅
+ │ ├── closing_loc: (1,35)-(1,36) = ")"
+ │ └── block: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,7)-(1,8) = "("
+ ├── rparen_loc: (1,10)-(1,11) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,38)-(1,41) = "end"
diff --git a/test/prism/snapshots/whitequark/forwarded_restarg.txt b/test/prism/snapshots/whitequark/forwarded_restarg.txt
new file mode 100644
index 0000000000..17ff8894af
--- /dev/null
+++ b/test/prism/snapshots/whitequark/forwarded_restarg.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(1,23))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,23))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,23))
+ ├── name: :foo
+ ├── name_loc: (1,4)-(1,7) = "foo"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,8)-(1,9))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest:
+ │ │ @ RestParameterNode (location: (1,8)-(1,9))
+ │ │ ├── flags: ∅
+ │ │ ├── name: ∅
+ │ │ ├── name_loc: ∅
+ │ │ └── operator_loc: (1,8)-(1,9) = "*"
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,12)-(1,18))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,12)-(1,18))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (1,12)-(1,15) = "bar"
+ │ ├── opening_loc: (1,15)-(1,16) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,16)-(1,17))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ SplatNode (location: (1,16)-(1,17))
+ │ │ ├── operator_loc: (1,16)-(1,17) = "*"
+ │ │ └── expression: ∅
+ │ ├── closing_loc: (1,17)-(1,18) = ")"
+ │ └── block: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,7)-(1,8) = "("
+ ├── rparen_loc: (1,9)-(1,10) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,20)-(1,23) = "end"
diff --git a/test/prism/snapshots/whitequark/gvar.txt b/test/prism/snapshots/whitequark/gvar.txt
new file mode 100644
index 0000000000..f4401c4389
--- /dev/null
+++ b/test/prism/snapshots/whitequark/gvar.txt
@@ -0,0 +1,7 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ GlobalVariableReadNode (location: (1,0)-(1,4))
+ └── name: :$foo
diff --git a/test/prism/snapshots/whitequark/gvasgn.txt b/test/prism/snapshots/whitequark/gvasgn.txt
new file mode 100644
index 0000000000..fc044054d8
--- /dev/null
+++ b/test/prism/snapshots/whitequark/gvasgn.txt
@@ -0,0 +1,13 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ GlobalVariableWriteNode (location: (1,0)-(1,9))
+ ├── name: :$var
+ ├── name_loc: (1,0)-(1,4) = "$var"
+ ├── value:
+ │ @ IntegerNode (location: (1,7)-(1,9))
+ │ ├── flags: decimal
+ │ └── value: 10
+ └── operator_loc: (1,5)-(1,6) = "="
diff --git a/test/prism/snapshots/whitequark/hash_empty.txt b/test/prism/snapshots/whitequark/hash_empty.txt
new file mode 100644
index 0000000000..38a2c15a9a
--- /dev/null
+++ b/test/prism/snapshots/whitequark/hash_empty.txt
@@ -0,0 +1,9 @@
+@ ProgramNode (location: (1,0)-(1,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,3))
+ └── body: (length: 1)
+ └── @ HashNode (location: (1,0)-(1,3))
+ ├── opening_loc: (1,0)-(1,1) = "{"
+ ├── elements: (length: 0)
+ └── closing_loc: (1,2)-(1,3) = "}"
diff --git a/test/prism/snapshots/whitequark/hash_hashrocket.txt b/test/prism/snapshots/whitequark/hash_hashrocket.txt
new file mode 100644
index 0000000000..e661a7b048
--- /dev/null
+++ b/test/prism/snapshots/whitequark/hash_hashrocket.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(3,25))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,25))
+ └── body: (length: 2)
+ ├── @ HashNode (location: (1,0)-(1,10))
+ │ ├── opening_loc: (1,0)-(1,1) = "{"
+ │ ├── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (1,2)-(1,8))
+ │ │ ├── key:
+ │ │ │ @ IntegerNode (location: (1,2)-(1,3))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (1,7)-(1,8))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── operator_loc: (1,4)-(1,6) = "=>"
+ │ └── closing_loc: (1,9)-(1,10) = "}"
+ └── @ HashNode (location: (3,0)-(3,25))
+ ├── opening_loc: (3,0)-(3,1) = "{"
+ ├── elements: (length: 2)
+ │ ├── @ AssocNode (location: (3,2)-(3,8))
+ │ │ ├── key:
+ │ │ │ @ IntegerNode (location: (3,2)-(3,3))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (3,7)-(3,8))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── operator_loc: (3,4)-(3,6) = "=>"
+ │ └── @ AssocNode (location: (3,10)-(3,23))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (3,10)-(3,14))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (3,10)-(3,11) = ":"
+ │ │ ├── value_loc: (3,11)-(3,14) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ ├── value:
+ │ │ @ StringNode (location: (3,18)-(3,23))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (3,18)-(3,19) = "\""
+ │ │ ├── content_loc: (3,19)-(3,22) = "bar"
+ │ │ ├── closing_loc: (3,22)-(3,23) = "\""
+ │ │ └── unescaped: "bar"
+ │ └── operator_loc: (3,15)-(3,17) = "=>"
+ └── closing_loc: (3,24)-(3,25) = "}"
diff --git a/test/prism/snapshots/whitequark/hash_kwsplat.txt b/test/prism/snapshots/whitequark/hash_kwsplat.txt
new file mode 100644
index 0000000000..a5d12174cc
--- /dev/null
+++ b/test/prism/snapshots/whitequark/hash_kwsplat.txt
@@ -0,0 +1,35 @@
+@ ProgramNode (location: (1,0)-(1,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,17))
+ └── body: (length: 1)
+ └── @ HashNode (location: (1,0)-(1,17))
+ ├── opening_loc: (1,0)-(1,1) = "{"
+ ├── elements: (length: 2)
+ │ ├── @ AssocNode (location: (1,2)-(1,8))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (1,2)-(1,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (1,2)-(1,5) = "foo"
+ │ │ │ ├── closing_loc: (1,5)-(1,6) = ":"
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (1,7)-(1,8))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── operator_loc: ∅
+ │ └── @ AssocSplatNode (location: (1,10)-(1,15))
+ │ ├── value:
+ │ │ @ CallNode (location: (1,12)-(1,15))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,12)-(1,15) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (1,10)-(1,12) = "**"
+ └── closing_loc: (1,16)-(1,17) = "}"
diff --git a/test/prism/snapshots/whitequark/hash_label.txt b/test/prism/snapshots/whitequark/hash_label.txt
new file mode 100644
index 0000000000..fdf7a21ed0
--- /dev/null
+++ b/test/prism/snapshots/whitequark/hash_label.txt
@@ -0,0 +1,22 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ HashNode (location: (1,0)-(1,10))
+ ├── opening_loc: (1,0)-(1,1) = "{"
+ ├── elements: (length: 1)
+ │ └── @ AssocNode (location: (1,2)-(1,8))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (1,2)-(1,6))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (1,2)-(1,5) = "foo"
+ │ │ ├── closing_loc: (1,5)-(1,6) = ":"
+ │ │ └── unescaped: "foo"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,7)-(1,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ └── operator_loc: ∅
+ └── closing_loc: (1,9)-(1,10) = "}"
diff --git a/test/prism/snapshots/whitequark/hash_label_end.txt b/test/prism/snapshots/whitequark/hash_label_end.txt
new file mode 100644
index 0000000000..88b70d38e2
--- /dev/null
+++ b/test/prism/snapshots/whitequark/hash_label_end.txt
@@ -0,0 +1,100 @@
+@ ProgramNode (location: (1,0)-(5,22))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,22))
+ └── body: (length: 3)
+ ├── @ CallNode (location: (1,0)-(1,12))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :f
+ │ ├── message_loc: (1,0)-(1,1) = "f"
+ │ ├── opening_loc: (1,1)-(1,2) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,2)-(1,11))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IfNode (location: (1,2)-(1,11))
+ │ │ ├── if_keyword_loc: ∅
+ │ │ ├── predicate:
+ │ │ │ @ CallNode (location: (1,2)-(1,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (1,2)-(1,3) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── then_keyword_loc: (1,4)-(1,5) = "?"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,6)-(1,9))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ StringNode (location: (1,6)-(1,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (1,6)-(1,7) = "\""
+ │ │ │ ├── content_loc: (1,7)-(1,8) = "a"
+ │ │ │ ├── closing_loc: (1,8)-(1,9) = "\""
+ │ │ │ └── unescaped: "a"
+ │ │ ├── consequent:
+ │ │ │ @ ElseNode (location: (1,9)-(1,11))
+ │ │ │ ├── else_keyword_loc: (1,9)-(1,10) = ":"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (1,10)-(1,11))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (1,10)-(1,11))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── end_keyword_loc: ∅
+ │ │ └── end_keyword_loc: ∅
+ │ ├── closing_loc: (1,11)-(1,12) = ")"
+ │ └── block: ∅
+ ├── @ HashNode (location: (3,0)-(3,12))
+ │ ├── opening_loc: (3,0)-(3,1) = "{"
+ │ ├── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (3,2)-(3,10))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (3,2)-(3,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (3,2)-(3,3) = "'"
+ │ │ │ ├── value_loc: (3,3)-(3,6) = "foo"
+ │ │ │ ├── closing_loc: (3,6)-(3,8) = "':"
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (3,9)-(3,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── operator_loc: ∅
+ │ └── closing_loc: (3,11)-(3,12) = "}"
+ └── @ HashNode (location: (5,0)-(5,22))
+ ├── opening_loc: (5,0)-(5,1) = "{"
+ ├── elements: (length: 2)
+ │ ├── @ AssocNode (location: (5,2)-(5,10))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (5,2)-(5,8))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (5,2)-(5,3) = "'"
+ │ │ │ ├── value_loc: (5,3)-(5,6) = "foo"
+ │ │ │ ├── closing_loc: (5,6)-(5,8) = "':"
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (5,9)-(5,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── operator_loc: ∅
+ │ └── @ AssocNode (location: (5,12)-(5,21))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (5,12)-(5,18))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (5,12)-(5,13) = "'"
+ │ │ ├── value_loc: (5,13)-(5,16) = "bar"
+ │ │ ├── closing_loc: (5,16)-(5,18) = "':"
+ │ │ └── unescaped: "bar"
+ │ ├── value:
+ │ │ @ HashNode (location: (5,19)-(5,21))
+ │ │ ├── opening_loc: (5,19)-(5,20) = "{"
+ │ │ ├── elements: (length: 0)
+ │ │ └── closing_loc: (5,20)-(5,21) = "}"
+ │ └── operator_loc: ∅
+ └── closing_loc: (5,21)-(5,22) = "}"
diff --git a/test/prism/snapshots/whitequark/hash_pair_value_omission.txt b/test/prism/snapshots/whitequark/hash_pair_value_omission.txt
new file mode 100644
index 0000000000..455ba48407
--- /dev/null
+++ b/test/prism/snapshots/whitequark/hash_pair_value_omission.txt
@@ -0,0 +1,97 @@
+@ ProgramNode (location: (1,0)-(5,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,7))
+ └── body: (length: 3)
+ ├── @ HashNode (location: (1,0)-(1,6))
+ │ ├── opening_loc: (1,0)-(1,1) = "{"
+ │ ├── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (1,1)-(1,5))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (1,1)-(1,5))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (1,1)-(1,4) = "BAR"
+ │ │ │ ├── closing_loc: (1,4)-(1,5) = ":"
+ │ │ │ └── unescaped: "BAR"
+ │ │ ├── value:
+ │ │ │ @ ImplicitNode (location: (1,1)-(1,5))
+ │ │ │ └── value:
+ │ │ │ @ ConstantReadNode (location: (1,1)-(1,5))
+ │ │ │ └── name: :BAR
+ │ │ └── operator_loc: ∅
+ │ └── closing_loc: (1,5)-(1,6) = "}"
+ ├── @ HashNode (location: (3,0)-(3,8))
+ │ ├── opening_loc: (3,0)-(3,1) = "{"
+ │ ├── elements: (length: 2)
+ │ │ ├── @ AssocNode (location: (3,1)-(3,3))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (3,1)-(3,3))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (3,1)-(3,2) = "a"
+ │ │ │ │ ├── closing_loc: (3,2)-(3,3) = ":"
+ │ │ │ │ └── unescaped: "a"
+ │ │ │ ├── value:
+ │ │ │ │ @ ImplicitNode (location: (3,1)-(3,3))
+ │ │ │ │ └── value:
+ │ │ │ │ @ CallNode (location: (3,1)-(3,3))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── message_loc: (3,1)-(3,2) = "a"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── operator_loc: ∅
+ │ │ └── @ AssocNode (location: (3,5)-(3,7))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (3,5)-(3,7))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (3,5)-(3,6) = "b"
+ │ │ │ ├── closing_loc: (3,6)-(3,7) = ":"
+ │ │ │ └── unescaped: "b"
+ │ │ ├── value:
+ │ │ │ @ ImplicitNode (location: (3,5)-(3,7))
+ │ │ │ └── value:
+ │ │ │ @ CallNode (location: (3,5)-(3,7))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (3,5)-(3,6) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: ∅
+ │ └── closing_loc: (3,7)-(3,8) = "}"
+ └── @ HashNode (location: (5,0)-(5,7))
+ ├── opening_loc: (5,0)-(5,1) = "{"
+ ├── elements: (length: 1)
+ │ └── @ AssocNode (location: (5,1)-(5,6))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (5,1)-(5,6))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (5,1)-(5,5) = "puts"
+ │ │ ├── closing_loc: (5,5)-(5,6) = ":"
+ │ │ └── unescaped: "puts"
+ │ ├── value:
+ │ │ @ ImplicitNode (location: (5,1)-(5,6))
+ │ │ └── value:
+ │ │ @ CallNode (location: (5,1)-(5,6))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :puts
+ │ │ ├── message_loc: (5,1)-(5,5) = "puts"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: ∅
+ └── closing_loc: (5,6)-(5,7) = "}"
diff --git a/test/prism/snapshots/whitequark/heredoc.txt b/test/prism/snapshots/whitequark/heredoc.txt
new file mode 100644
index 0000000000..86543097ee
--- /dev/null
+++ b/test/prism/snapshots/whitequark/heredoc.txt
@@ -0,0 +1,23 @@
+@ ProgramNode (location: (1,0)-(11,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(11,8))
+ └── body: (length: 3)
+ ├── @ StringNode (location: (1,0)-(1,8))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,8) = "<<'HERE'"
+ │ ├── content_loc: (2,0)-(4,0) = "foo\nbar\n"
+ │ ├── closing_loc: (4,0)-(5,0) = "HERE\n"
+ │ └── unescaped: "foo\nbar\n"
+ ├── @ StringNode (location: (6,0)-(6,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (6,0)-(6,6) = "<<HERE"
+ │ ├── content_loc: (7,0)-(9,0) = "foo\nbar\n"
+ │ ├── closing_loc: (9,0)-(10,0) = "HERE\n"
+ │ └── unescaped: "foo\nbar\n"
+ └── @ XStringNode (location: (11,0)-(11,8))
+ ├── flags: ∅
+ ├── opening_loc: (11,0)-(11,8) = "<<`HERE`"
+ ├── content_loc: (12,0)-(14,0) = "foo\nbar\n"
+ ├── closing_loc: (14,0)-(15,0) = "HERE\n"
+ └── unescaped: "foo\nbar\n"
diff --git a/test/prism/snapshots/whitequark/if.txt b/test/prism/snapshots/whitequark/if.txt
new file mode 100644
index 0000000000..809a100f8c
--- /dev/null
+++ b/test/prism/snapshots/whitequark/if.txt
@@ -0,0 +1,63 @@
+@ ProgramNode (location: (1,0)-(3,16))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,16))
+ └── body: (length: 2)
+ ├── @ IfNode (location: (1,0)-(1,20))
+ │ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ │ ├── predicate:
+ │ │ @ CallNode (location: (1,3)-(1,6))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,3)-(1,6) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: (1,7)-(1,11) = "then"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,12)-(1,15))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,12)-(1,15))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,12)-(1,15) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (1,17)-(1,20) = "end"
+ └── @ IfNode (location: (3,0)-(3,16))
+ ├── if_keyword_loc: (3,0)-(3,2) = "if"
+ ├── predicate:
+ │ @ CallNode (location: (3,3)-(3,6))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (3,3)-(3,6) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── then_keyword_loc: ∅
+ ├── statements:
+ │ @ StatementsNode (location: (3,8)-(3,11))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (3,8)-(3,11))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (3,8)-(3,11) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (3,13)-(3,16) = "end"
diff --git a/test/prism/snapshots/whitequark/if_else.txt b/test/prism/snapshots/whitequark/if_else.txt
new file mode 100644
index 0000000000..c7dab47f13
--- /dev/null
+++ b/test/prism/snapshots/whitequark/if_else.txt
@@ -0,0 +1,95 @@
+@ ProgramNode (location: (1,0)-(3,26))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,26))
+ └── body: (length: 2)
+ ├── @ IfNode (location: (1,0)-(1,30))
+ │ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ │ ├── predicate:
+ │ │ @ CallNode (location: (1,3)-(1,6))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,3)-(1,6) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: (1,7)-(1,11) = "then"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,12)-(1,15))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,12)-(1,15))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,12)-(1,15) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (1,17)-(1,30))
+ │ │ ├── else_keyword_loc: (1,17)-(1,21) = "else"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,22)-(1,25))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (1,22)-(1,25))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (1,22)-(1,25) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── end_keyword_loc: (1,27)-(1,30) = "end"
+ │ └── end_keyword_loc: (1,27)-(1,30) = "end"
+ └── @ IfNode (location: (3,0)-(3,26))
+ ├── if_keyword_loc: (3,0)-(3,2) = "if"
+ ├── predicate:
+ │ @ CallNode (location: (3,3)-(3,6))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (3,3)-(3,6) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── then_keyword_loc: ∅
+ ├── statements:
+ │ @ StatementsNode (location: (3,8)-(3,11))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (3,8)-(3,11))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (3,8)-(3,11) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── consequent:
+ │ @ ElseNode (location: (3,13)-(3,26))
+ │ ├── else_keyword_loc: (3,13)-(3,17) = "else"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,18)-(3,21))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (3,18)-(3,21))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (3,18)-(3,21) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── end_keyword_loc: (3,23)-(3,26) = "end"
+ └── end_keyword_loc: (3,23)-(3,26) = "end"
diff --git a/test/prism/snapshots/whitequark/if_elsif.txt b/test/prism/snapshots/whitequark/if_elsif.txt
new file mode 100644
index 0000000000..1bd1ab7f42
--- /dev/null
+++ b/test/prism/snapshots/whitequark/if_elsif.txt
@@ -0,0 +1,65 @@
+@ ProgramNode (location: (1,0)-(1,38))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,38))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(1,38))
+ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ ├── predicate:
+ │ @ CallNode (location: (1,3)-(1,6))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,3)-(1,6) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── then_keyword_loc: ∅
+ ├── statements:
+ │ @ StatementsNode (location: (1,8)-(1,11))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,8)-(1,11))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (1,8)-(1,11) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── consequent:
+ │ @ IfNode (location: (1,13)-(1,38))
+ │ ├── if_keyword_loc: (1,13)-(1,18) = "elsif"
+ │ ├── predicate:
+ │ │ @ CallNode (location: (1,19)-(1,22))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (1,19)-(1,22) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,24)-(1,25))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,24)-(1,25))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (1,27)-(1,38))
+ │ │ ├── else_keyword_loc: (1,27)-(1,31) = "else"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,32)-(1,33))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (1,32)-(1,33))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── end_keyword_loc: (1,35)-(1,38) = "end"
+ │ └── end_keyword_loc: (1,35)-(1,38) = "end"
+ └── end_keyword_loc: (1,35)-(1,38) = "end"
diff --git a/test/prism/snapshots/whitequark/if_masgn__24.txt b/test/prism/snapshots/whitequark/if_masgn__24.txt
new file mode 100644
index 0000000000..c76a93574d
--- /dev/null
+++ b/test/prism/snapshots/whitequark/if_masgn__24.txt
@@ -0,0 +1,42 @@
+@ ProgramNode (location: (1,0)-(1,20))
+├── locals: [:a, :b]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,20))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(1,20))
+ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ ├── predicate:
+ │ @ ParenthesesNode (location: (1,3)-(1,15))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,4)-(1,14))
+ │ │ └── body: (length: 1)
+ │ │ └── @ MultiWriteNode (location: (1,4)-(1,14))
+ │ │ ├── lefts: (length: 2)
+ │ │ │ ├── @ LocalVariableTargetNode (location: (1,4)-(1,5))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ └── depth: 0
+ │ │ │ └── @ LocalVariableTargetNode (location: (1,7)-(1,8))
+ │ │ │ ├── name: :b
+ │ │ │ └── depth: 0
+ │ │ ├── rest: ∅
+ │ │ ├── rights: (length: 0)
+ │ │ ├── lparen_loc: ∅
+ │ │ ├── rparen_loc: ∅
+ │ │ ├── operator_loc: (1,9)-(1,10) = "="
+ │ │ └── value:
+ │ │ @ CallNode (location: (1,11)-(1,14))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,11)-(1,14) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (1,3)-(1,4) = "("
+ │ └── closing_loc: (1,14)-(1,15) = ")"
+ ├── then_keyword_loc: ∅
+ ├── statements: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (1,17)-(1,20) = "end"
diff --git a/test/prism/snapshots/whitequark/if_mod.txt b/test/prism/snapshots/whitequark/if_mod.txt
new file mode 100644
index 0000000000..241918f020
--- /dev/null
+++ b/test/prism/snapshots/whitequark/if_mod.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(1,10))
+ ├── if_keyword_loc: (1,4)-(1,6) = "if"
+ ├── predicate:
+ │ @ CallNode (location: (1,7)-(1,10))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,7)-(1,10) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── then_keyword_loc: ∅
+ ├── statements:
+ │ @ StatementsNode (location: (1,0)-(1,3))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,0)-(1,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (1,0)-(1,3) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/whitequark/if_nl_then.txt b/test/prism/snapshots/whitequark/if_nl_then.txt
new file mode 100644
index 0000000000..fbbafe7390
--- /dev/null
+++ b/test/prism/snapshots/whitequark/if_nl_then.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(2,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,12))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(2,12))
+ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ ├── predicate:
+ │ @ CallNode (location: (1,3)-(1,6))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,3)-(1,6) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── then_keyword_loc: (2,0)-(2,4) = "then"
+ ├── statements:
+ │ @ StatementsNode (location: (2,5)-(2,8))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (2,5)-(2,8))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (2,5)-(2,8) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (2,9)-(2,12) = "end"
diff --git a/test/prism/snapshots/whitequark/int.txt b/test/prism/snapshots/whitequark/int.txt
new file mode 100644
index 0000000000..6fee32f117
--- /dev/null
+++ b/test/prism/snapshots/whitequark/int.txt
@@ -0,0 +1,14 @@
+@ ProgramNode (location: (1,0)-(5,2))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,2))
+ └── body: (length: 3)
+ ├── @ IntegerNode (location: (1,0)-(1,3))
+ │ ├── flags: decimal
+ │ └── value: 42
+ ├── @ IntegerNode (location: (3,0)-(3,3))
+ │ ├── flags: decimal
+ │ └── value: -42
+ └── @ IntegerNode (location: (5,0)-(5,2))
+ ├── flags: decimal
+ └── value: 42
diff --git a/test/prism/snapshots/whitequark/int___LINE__.txt b/test/prism/snapshots/whitequark/int___LINE__.txt
new file mode 100644
index 0000000000..bf2ea47102
--- /dev/null
+++ b/test/prism/snapshots/whitequark/int___LINE__.txt
@@ -0,0 +1,6 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ SourceLineNode (location: (1,0)-(1,8))
diff --git a/test/prism/snapshots/whitequark/interp_digit_var.txt b/test/prism/snapshots/whitequark/interp_digit_var.txt
new file mode 100644
index 0000000000..09d9098105
--- /dev/null
+++ b/test/prism/snapshots/whitequark/interp_digit_var.txt
@@ -0,0 +1,273 @@
+@ ProgramNode (location: (1,1)-(85,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,1)-(85,9))
+ └── body: (length: 38)
+ ├── @ StringNode (location: (1,1)-(1,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,1)-(1,2) = "\""
+ │ ├── content_loc: (1,2)-(1,5) = "\#@1"
+ │ ├── closing_loc: (1,5)-(1,6) = "\""
+ │ └── unescaped: "\#@1"
+ ├── @ StringNode (location: (3,1)-(3,7))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (3,1)-(3,2) = "\""
+ │ ├── content_loc: (3,2)-(3,6) = "\#@@1"
+ │ ├── closing_loc: (3,6)-(3,7) = "\""
+ │ └── unescaped: "\#@@1"
+ ├── @ ArrayNode (location: (5,1)-(5,8))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ SymbolNode (location: (5,4)-(5,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (5,4)-(5,7) = "\#@1"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\#@1"
+ │ ├── opening_loc: (5,1)-(5,4) = "%I["
+ │ └── closing_loc: (5,7)-(5,8) = "]"
+ ├── @ ArrayNode (location: (7,1)-(7,9))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ SymbolNode (location: (7,4)-(7,8))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (7,4)-(7,8) = "\#@@1"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\#@@1"
+ │ ├── opening_loc: (7,1)-(7,4) = "%I["
+ │ └── closing_loc: (7,8)-(7,9) = "]"
+ ├── @ StringNode (location: (9,1)-(9,8))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (9,1)-(9,4) = "%Q{"
+ │ ├── content_loc: (9,4)-(9,7) = "\#@1"
+ │ ├── closing_loc: (9,7)-(9,8) = "}"
+ │ └── unescaped: "\#@1"
+ ├── @ StringNode (location: (11,1)-(11,9))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (11,1)-(11,4) = "%Q{"
+ │ ├── content_loc: (11,4)-(11,8) = "\#@@1"
+ │ ├── closing_loc: (11,8)-(11,9) = "}"
+ │ └── unescaped: "\#@@1"
+ ├── @ ArrayNode (location: (13,1)-(13,8))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ StringNode (location: (13,4)-(13,7))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (13,4)-(13,7) = "\#@1"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\#@1"
+ │ ├── opening_loc: (13,1)-(13,4) = "%W["
+ │ └── closing_loc: (13,7)-(13,8) = "]"
+ ├── @ ArrayNode (location: (15,1)-(15,9))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ StringNode (location: (15,4)-(15,8))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (15,4)-(15,8) = "\#@@1"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\#@@1"
+ │ ├── opening_loc: (15,1)-(15,4) = "%W["
+ │ └── closing_loc: (15,8)-(15,9) = "]"
+ ├── @ ArrayNode (location: (17,1)-(17,10))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ SymbolNode (location: (17,5)-(17,8))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (17,5)-(17,8) = "\#@1"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\#@1"
+ │ ├── opening_loc: (17,1)-(17,4) = "%i["
+ │ └── closing_loc: (17,9)-(17,10) = "]"
+ ├── @ ArrayNode (location: (19,1)-(19,11))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ SymbolNode (location: (19,5)-(19,9))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (19,5)-(19,9) = "\#@@1"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\#@@1"
+ │ ├── opening_loc: (19,1)-(19,4) = "%i["
+ │ └── closing_loc: (19,10)-(19,11) = "]"
+ ├── @ StringNode (location: (21,1)-(21,8))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (21,1)-(21,4) = "%q{"
+ │ ├── content_loc: (21,4)-(21,7) = "\#@1"
+ │ ├── closing_loc: (21,7)-(21,8) = "}"
+ │ └── unescaped: "\#@1"
+ ├── @ StringNode (location: (23,1)-(23,9))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (23,1)-(23,4) = "%q{"
+ │ ├── content_loc: (23,4)-(23,8) = "\#@@1"
+ │ ├── closing_loc: (23,8)-(23,9) = "}"
+ │ └── unescaped: "\#@@1"
+ ├── @ RegularExpressionNode (location: (25,1)-(25,8))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (25,1)-(25,4) = "%r{"
+ │ ├── content_loc: (25,4)-(25,7) = "\#@1"
+ │ ├── closing_loc: (25,7)-(25,8) = "}"
+ │ └── unescaped: "\#@1"
+ ├── @ RegularExpressionNode (location: (27,1)-(27,9))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (27,1)-(27,4) = "%r{"
+ │ ├── content_loc: (27,4)-(27,8) = "\#@@1"
+ │ ├── closing_loc: (27,8)-(27,9) = "}"
+ │ └── unescaped: "\#@@1"
+ ├── @ SymbolNode (location: (29,1)-(29,8))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (29,1)-(29,4) = "%s{"
+ │ ├── value_loc: (29,4)-(29,7) = "\#@1"
+ │ ├── closing_loc: (29,7)-(29,8) = "}"
+ │ └── unescaped: "\#@1"
+ ├── @ SymbolNode (location: (31,1)-(31,9))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (31,1)-(31,4) = "%s{"
+ │ ├── value_loc: (31,4)-(31,8) = "\#@@1"
+ │ ├── closing_loc: (31,8)-(31,9) = "}"
+ │ └── unescaped: "\#@@1"
+ ├── @ ArrayNode (location: (33,1)-(33,10))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ StringNode (location: (33,5)-(33,8))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (33,5)-(33,8) = "\#@1"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\#@1"
+ │ ├── opening_loc: (33,1)-(33,4) = "%w["
+ │ └── closing_loc: (33,9)-(33,10) = "]"
+ ├── @ ArrayNode (location: (35,1)-(35,11))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ StringNode (location: (35,5)-(35,9))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (35,5)-(35,9) = "\#@@1"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\#@@1"
+ │ ├── opening_loc: (35,1)-(35,4) = "%w["
+ │ └── closing_loc: (35,10)-(35,11) = "]"
+ ├── @ XStringNode (location: (37,1)-(37,8))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (37,1)-(37,4) = "%x{"
+ │ ├── content_loc: (37,4)-(37,7) = "\#@1"
+ │ ├── closing_loc: (37,7)-(37,8) = "}"
+ │ └── unescaped: "\#@1"
+ ├── @ XStringNode (location: (39,1)-(39,9))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (39,1)-(39,4) = "%x{"
+ │ ├── content_loc: (39,4)-(39,8) = "\#@@1"
+ │ ├── closing_loc: (39,8)-(39,9) = "}"
+ │ └── unescaped: "\#@@1"
+ ├── @ StringNode (location: (41,1)-(41,7))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (41,1)-(41,3) = "%{"
+ │ ├── content_loc: (41,3)-(41,6) = "\#@1"
+ │ ├── closing_loc: (41,6)-(41,7) = "}"
+ │ └── unescaped: "\#@1"
+ ├── @ StringNode (location: (43,1)-(43,8))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (43,1)-(43,3) = "%{"
+ │ ├── content_loc: (43,3)-(43,7) = "\#@@1"
+ │ ├── closing_loc: (43,7)-(43,8) = "}"
+ │ └── unescaped: "\#@@1"
+ ├── @ StringNode (location: (45,1)-(45,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (45,1)-(45,2) = "'"
+ │ ├── content_loc: (45,2)-(45,5) = "\#@1"
+ │ ├── closing_loc: (45,5)-(45,6) = "'"
+ │ └── unescaped: "\#@1"
+ ├── @ StringNode (location: (47,1)-(47,7))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (47,1)-(47,2) = "'"
+ │ ├── content_loc: (47,2)-(47,6) = "\#@@1"
+ │ ├── closing_loc: (47,6)-(47,7) = "'"
+ │ └── unescaped: "\#@@1"
+ ├── @ RegularExpressionNode (location: (49,1)-(49,6))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (49,1)-(49,2) = "/"
+ │ ├── content_loc: (49,2)-(49,5) = "\#@1"
+ │ ├── closing_loc: (49,5)-(49,6) = "/"
+ │ └── unescaped: "\#@1"
+ ├── @ RegularExpressionNode (location: (51,1)-(51,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (51,1)-(51,2) = "/"
+ │ ├── content_loc: (51,2)-(51,6) = "\#@@1"
+ │ ├── closing_loc: (51,6)-(51,7) = "/"
+ │ └── unescaped: "\#@@1"
+ ├── @ SymbolNode (location: (53,1)-(53,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (53,1)-(53,3) = ":\""
+ │ ├── value_loc: (53,3)-(53,6) = "\#@1"
+ │ ├── closing_loc: (53,6)-(53,7) = "\""
+ │ └── unescaped: "\#@1"
+ ├── @ SymbolNode (location: (55,1)-(55,8))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (55,1)-(55,3) = ":\""
+ │ ├── value_loc: (55,3)-(55,7) = "\#@@1"
+ │ ├── closing_loc: (55,7)-(55,8) = "\""
+ │ └── unescaped: "\#@@1"
+ ├── @ SymbolNode (location: (57,1)-(57,7))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (57,1)-(57,3) = ":'"
+ │ ├── value_loc: (57,3)-(57,6) = "\#@1"
+ │ ├── closing_loc: (57,6)-(57,7) = "'"
+ │ └── unescaped: "\#@1"
+ ├── @ SymbolNode (location: (59,1)-(59,8))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (59,1)-(59,3) = ":'"
+ │ ├── value_loc: (59,3)-(59,7) = "\#@@1"
+ │ ├── closing_loc: (59,7)-(59,8) = "'"
+ │ └── unescaped: "\#@@1"
+ ├── @ XStringNode (location: (61,1)-(61,6))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (61,1)-(61,2) = "`"
+ │ ├── content_loc: (61,2)-(61,5) = "\#@1"
+ │ ├── closing_loc: (61,5)-(61,6) = "`"
+ │ └── unescaped: "\#@1"
+ ├── @ XStringNode (location: (63,1)-(63,7))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (63,1)-(63,2) = "`"
+ │ ├── content_loc: (63,2)-(63,6) = "\#@@1"
+ │ ├── closing_loc: (63,6)-(63,7) = "`"
+ │ └── unescaped: "\#@@1"
+ ├── @ StringNode (location: (65,0)-(65,9))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (65,0)-(65,9) = "<<-\"HERE\""
+ │ ├── content_loc: (66,0)-(67,0) = "\#@1\n"
+ │ ├── closing_loc: (67,0)-(68,0) = "HERE\n"
+ │ └── unescaped: "\#@1\n"
+ ├── @ StringNode (location: (69,0)-(69,9))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (69,0)-(69,9) = "<<-\"HERE\""
+ │ ├── content_loc: (70,0)-(71,0) = "\#@@1\n"
+ │ ├── closing_loc: (71,0)-(72,0) = "HERE\n"
+ │ └── unescaped: "\#@@1\n"
+ ├── @ StringNode (location: (73,0)-(73,9))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (73,0)-(73,9) = "<<-'HERE'"
+ │ ├── content_loc: (74,0)-(75,0) = "\#@1\n"
+ │ ├── closing_loc: (75,0)-(76,0) = "HERE\n"
+ │ └── unescaped: "\#@1\n"
+ ├── @ StringNode (location: (77,0)-(77,9))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (77,0)-(77,9) = "<<-'HERE'"
+ │ ├── content_loc: (78,0)-(79,0) = "\#@@1\n"
+ │ ├── closing_loc: (79,0)-(80,0) = "HERE\n"
+ │ └── unescaped: "\#@@1\n"
+ ├── @ XStringNode (location: (81,0)-(81,9))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (81,0)-(81,9) = "<<-`HERE`"
+ │ ├── content_loc: (82,0)-(83,0) = "\#@1\n"
+ │ ├── closing_loc: (83,0)-(84,0) = "HERE\n"
+ │ └── unescaped: "\#@1\n"
+ └── @ XStringNode (location: (85,0)-(85,9))
+ ├── flags: ∅
+ ├── opening_loc: (85,0)-(85,9) = "<<-`HERE`"
+ ├── content_loc: (86,0)-(87,0) = "\#@@1\n"
+ ├── closing_loc: (87,0)-(88,0) = "HERE\n"
+ └── unescaped: "\#@@1\n"
diff --git a/test/prism/snapshots/whitequark/ivar.txt b/test/prism/snapshots/whitequark/ivar.txt
new file mode 100644
index 0000000000..9c70e6e959
--- /dev/null
+++ b/test/prism/snapshots/whitequark/ivar.txt
@@ -0,0 +1,7 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ InstanceVariableReadNode (location: (1,0)-(1,4))
+ └── name: :@foo
diff --git a/test/prism/snapshots/whitequark/ivasgn.txt b/test/prism/snapshots/whitequark/ivasgn.txt
new file mode 100644
index 0000000000..2b57f39554
--- /dev/null
+++ b/test/prism/snapshots/whitequark/ivasgn.txt
@@ -0,0 +1,13 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ InstanceVariableWriteNode (location: (1,0)-(1,9))
+ ├── name: :@var
+ ├── name_loc: (1,0)-(1,4) = "@var"
+ ├── value:
+ │ @ IntegerNode (location: (1,7)-(1,9))
+ │ ├── flags: decimal
+ │ └── value: 10
+ └── operator_loc: (1,5)-(1,6) = "="
diff --git a/test/prism/snapshots/whitequark/keyword_argument_omission.txt b/test/prism/snapshots/whitequark/keyword_argument_omission.txt
new file mode 100644
index 0000000000..62e8fecf4e
--- /dev/null
+++ b/test/prism/snapshots/whitequark/keyword_argument_omission.txt
@@ -0,0 +1,65 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,11))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :foo
+ ├── message_loc: (1,0)-(1,3) = "foo"
+ ├── opening_loc: (1,3)-(1,4) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,10))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ KeywordHashNode (location: (1,4)-(1,10))
+ │ ├── flags: symbol_keys
+ │ └── elements: (length: 2)
+ │ ├── @ AssocNode (location: (1,4)-(1,6))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (1,4)-(1,6))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (1,4)-(1,5) = "a"
+ │ │ │ ├── closing_loc: (1,5)-(1,6) = ":"
+ │ │ │ └── unescaped: "a"
+ │ │ ├── value:
+ │ │ │ @ ImplicitNode (location: (1,4)-(1,6))
+ │ │ │ └── value:
+ │ │ │ @ CallNode (location: (1,4)-(1,6))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :a
+ │ │ │ ├── message_loc: (1,4)-(1,5) = "a"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: ∅
+ │ └── @ AssocNode (location: (1,8)-(1,10))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (1,8)-(1,10))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (1,8)-(1,9) = "b"
+ │ │ ├── closing_loc: (1,9)-(1,10) = ":"
+ │ │ └── unescaped: "b"
+ │ ├── value:
+ │ │ @ ImplicitNode (location: (1,8)-(1,10))
+ │ │ └── value:
+ │ │ @ CallNode (location: (1,8)-(1,10))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (1,8)-(1,9) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: ∅
+ ├── closing_loc: (1,10)-(1,11) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/kwarg.txt b/test/prism/snapshots/whitequark/kwarg.txt
new file mode 100644
index 0000000000..5969402356
--- /dev/null
+++ b/test/prism/snapshots/whitequark/kwarg.txt
@@ -0,0 +1,30 @@
+@ ProgramNode (location: (1,0)-(1,16))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,16))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,16))
+ ├── name: :f
+ ├── name_loc: (1,4)-(1,5) = "f"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,10))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 1)
+ │ │ └── @ RequiredKeywordParameterNode (location: (1,6)-(1,10))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :foo
+ │ │ └── name_loc: (1,6)-(1,10) = "foo:"
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:foo]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,5)-(1,6) = "("
+ ├── rparen_loc: (1,10)-(1,11) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,13)-(1,16) = "end"
diff --git a/test/prism/snapshots/whitequark/kwbegin_compstmt.txt b/test/prism/snapshots/whitequark/kwbegin_compstmt.txt
new file mode 100644
index 0000000000..376e628f8e
--- /dev/null
+++ b/test/prism/snapshots/whitequark/kwbegin_compstmt.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,20))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,20))
+ └── body: (length: 1)
+ └── @ BeginNode (location: (1,0)-(1,20))
+ ├── begin_keyword_loc: (1,0)-(1,5) = "begin"
+ ├── statements:
+ │ @ StatementsNode (location: (1,6)-(1,16))
+ │ └── body: (length: 2)
+ │ ├── @ CallNode (location: (1,6)-(1,10))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo!
+ │ │ ├── message_loc: (1,6)-(1,10) = "foo!"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── @ CallNode (location: (1,12)-(1,16))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar!
+ │ ├── message_loc: (1,12)-(1,16) = "bar!"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── rescue_clause: ∅
+ ├── else_clause: ∅
+ ├── ensure_clause: ∅
+ └── end_keyword_loc: (1,17)-(1,20) = "end"
diff --git a/test/prism/snapshots/whitequark/kwnilarg.txt b/test/prism/snapshots/whitequark/kwnilarg.txt
new file mode 100644
index 0000000000..492f817696
--- /dev/null
+++ b/test/prism/snapshots/whitequark/kwnilarg.txt
@@ -0,0 +1,84 @@
+@ ProgramNode (location: (1,0)-(5,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,13))
+ └── body: (length: 3)
+ ├── @ LambdaNode (location: (1,0)-(1,12))
+ │ ├── locals: []
+ │ ├── operator_loc: (1,0)-(1,2) = "->"
+ │ ├── opening_loc: (1,10)-(1,11) = "{"
+ │ ├── closing_loc: (1,11)-(1,12) = "}"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (1,2)-(1,9))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (1,3)-(1,8))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest:
+ │ │ │ │ @ NoKeywordsParameterNode (location: (1,3)-(1,8))
+ │ │ │ │ ├── operator_loc: (1,3)-(1,5) = "**"
+ │ │ │ │ └── keyword_loc: (1,5)-(1,8) = "nil"
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (1,2)-(1,3) = "("
+ │ │ └── closing_loc: (1,8)-(1,9) = ")"
+ │ └── body: ∅
+ ├── @ DefNode (location: (3,0)-(3,17))
+ │ ├── name: :f
+ │ ├── name_loc: (3,4)-(3,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (3,6)-(3,11))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ NoKeywordsParameterNode (location: (3,6)-(3,11))
+ │ │ │ ├── operator_loc: (3,6)-(3,8) = "**"
+ │ │ │ └── keyword_loc: (3,8)-(3,11) = "nil"
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (3,0)-(3,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (3,5)-(3,6) = "("
+ │ ├── rparen_loc: (3,11)-(3,12) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (3,14)-(3,17) = "end"
+ └── @ CallNode (location: (5,0)-(5,13))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :m
+ ├── message_loc: (5,0)-(5,1) = "m"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (5,2)-(5,13))
+ ├── locals: []
+ ├── parameters:
+ │ @ BlockParametersNode (location: (5,4)-(5,11))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (5,5)-(5,10))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest:
+ │ │ │ @ NoKeywordsParameterNode (location: (5,5)-(5,10))
+ │ │ │ ├── operator_loc: (5,5)-(5,7) = "**"
+ │ │ │ └── keyword_loc: (5,7)-(5,10) = "nil"
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (5,4)-(5,5) = "|"
+ │ └── closing_loc: (5,10)-(5,11) = "|"
+ ├── body: ∅
+ ├── opening_loc: (5,2)-(5,3) = "{"
+ └── closing_loc: (5,12)-(5,13) = "}"
diff --git a/test/prism/snapshots/whitequark/kwoptarg.txt b/test/prism/snapshots/whitequark/kwoptarg.txt
new file mode 100644
index 0000000000..9c2c4f9c4c
--- /dev/null
+++ b/test/prism/snapshots/whitequark/kwoptarg.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,18))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,18))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,18))
+ ├── name: :f
+ ├── name_loc: (1,4)-(1,5) = "f"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,12))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 1)
+ │ │ └── @ OptionalKeywordParameterNode (location: (1,6)-(1,12))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :foo
+ │ │ ├── name_loc: (1,6)-(1,10) = "foo:"
+ │ │ └── value:
+ │ │ @ IntegerNode (location: (1,11)-(1,12))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:foo]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,5)-(1,6) = "("
+ ├── rparen_loc: (1,12)-(1,13) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,15)-(1,18) = "end"
diff --git a/test/prism/snapshots/whitequark/kwoptarg_with_kwrestarg_and_forwarded_args.txt b/test/prism/snapshots/whitequark/kwoptarg_with_kwrestarg_and_forwarded_args.txt
new file mode 100644
index 0000000000..6d0bdfb817
--- /dev/null
+++ b/test/prism/snapshots/whitequark/kwoptarg_with_kwrestarg_and_forwarded_args.txt
@@ -0,0 +1,58 @@
+@ ProgramNode (location: (1,0)-(1,28))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,28))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,28))
+ ├── name: :f
+ ├── name_loc: (1,4)-(1,5) = "f"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,16))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 1)
+ │ │ └── @ OptionalKeywordParameterNode (location: (1,6)-(1,12))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :a
+ │ │ ├── name_loc: (1,6)-(1,8) = "a:"
+ │ │ └── value:
+ │ │ @ NilNode (location: (1,9)-(1,12))
+ │ ├── keyword_rest:
+ │ │ @ KeywordRestParameterNode (location: (1,14)-(1,16))
+ │ │ ├── flags: ∅
+ │ │ ├── name: ∅
+ │ │ ├── name_loc: ∅
+ │ │ └── operator_loc: (1,14)-(1,16) = "**"
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,19)-(1,24))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,19)-(1,24))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :b
+ │ ├── message_loc: (1,19)-(1,20) = "b"
+ │ ├── opening_loc: (1,20)-(1,21) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,21)-(1,23))
+ │ │ ├── flags: contains_keyword_splat
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (1,21)-(1,23))
+ │ │ ├── flags: ∅
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocSplatNode (location: (1,21)-(1,23))
+ │ │ ├── value: ∅
+ │ │ └── operator_loc: (1,21)-(1,23) = "**"
+ │ ├── closing_loc: (1,23)-(1,24) = ")"
+ │ └── block: ∅
+ ├── locals: [:a]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,5)-(1,6) = "("
+ ├── rparen_loc: (1,16)-(1,17) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,25)-(1,28) = "end"
diff --git a/test/prism/snapshots/whitequark/kwrestarg_named.txt b/test/prism/snapshots/whitequark/kwrestarg_named.txt
new file mode 100644
index 0000000000..d24426aed5
--- /dev/null
+++ b/test/prism/snapshots/whitequark/kwrestarg_named.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (1,0)-(1,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,17))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,17))
+ ├── name: :f
+ ├── name_loc: (1,4)-(1,5) = "f"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,11))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest:
+ │ │ @ KeywordRestParameterNode (location: (1,6)-(1,11))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :foo
+ │ │ ├── name_loc: (1,8)-(1,11) = "foo"
+ │ │ └── operator_loc: (1,6)-(1,8) = "**"
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:foo]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,5)-(1,6) = "("
+ ├── rparen_loc: (1,11)-(1,12) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,14)-(1,17) = "end"
diff --git a/test/prism/snapshots/whitequark/kwrestarg_unnamed.txt b/test/prism/snapshots/whitequark/kwrestarg_unnamed.txt
new file mode 100644
index 0000000000..d471e5ab09
--- /dev/null
+++ b/test/prism/snapshots/whitequark/kwrestarg_unnamed.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,14))
+ ├── name: :f
+ ├── name_loc: (1,4)-(1,5) = "f"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,8))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest:
+ │ │ @ KeywordRestParameterNode (location: (1,6)-(1,8))
+ │ │ ├── flags: ∅
+ │ │ ├── name: ∅
+ │ │ ├── name_loc: ∅
+ │ │ └── operator_loc: (1,6)-(1,8) = "**"
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,5)-(1,6) = "("
+ ├── rparen_loc: (1,8)-(1,9) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,11)-(1,14) = "end"
diff --git a/test/prism/snapshots/whitequark/lbrace_arg_after_command_args.txt b/test/prism/snapshots/whitequark/lbrace_arg_after_command_args.txt
new file mode 100644
index 0000000000..f877fcd270
--- /dev/null
+++ b/test/prism/snapshots/whitequark/lbrace_arg_after_command_args.txt
@@ -0,0 +1,54 @@
+@ ProgramNode (location: (1,0)-(1,22))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,22))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,22))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :let
+ ├── message_loc: (1,0)-(1,3) = "let"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ ParenthesesNode (location: (1,4)-(1,8))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,5)-(1,7))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SymbolNode (location: (1,5)-(1,7))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,5)-(1,6) = ":"
+ │ │ ├── value_loc: (1,6)-(1,7) = "a"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a"
+ │ ├── opening_loc: (1,4)-(1,5) = "("
+ │ └── closing_loc: (1,7)-(1,8) = ")"
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,9)-(1,22))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,11)-(1,20))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,11)-(1,20))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (1,11)-(1,12) = "m"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,13)-(1,20))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,13)-(1,15) = "do"
+ │ └── closing_loc: (1,17)-(1,20) = "end"
+ ├── opening_loc: (1,9)-(1,10) = "{"
+ └── closing_loc: (1,21)-(1,22) = "}"
diff --git a/test/prism/snapshots/whitequark/lparenarg_after_lvar__since_25.txt b/test/prism/snapshots/whitequark/lparenarg_after_lvar__since_25.txt
new file mode 100644
index 0000000000..afddc9cd3c
--- /dev/null
+++ b/test/prism/snapshots/whitequark/lparenarg_after_lvar__since_25.txt
@@ -0,0 +1,67 @@
+@ ProgramNode (location: (1,0)-(3,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,15))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,14))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,4)-(1,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (1,4)-(1,14))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ ParenthesesNode (location: (1,4)-(1,10))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (1,5)-(1,9))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ FloatNode (location: (1,5)-(1,9))
+ │ │ │ │ └── value: -1.3
+ │ │ │ ├── opening_loc: (1,4)-(1,5) = "("
+ │ │ │ └── closing_loc: (1,9)-(1,10) = ")"
+ │ │ ├── call_operator_loc: (1,10)-(1,11) = "."
+ │ │ ├── name: :abs
+ │ │ ├── message_loc: (1,11)-(1,14) = "abs"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (3,0)-(3,15))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :meth
+ ├── message_loc: (3,0)-(3,4) = "meth"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (3,5)-(3,15))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (3,5)-(3,15))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (3,5)-(3,11))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (3,6)-(3,10))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ FloatNode (location: (3,6)-(3,10))
+ │ │ │ └── value: -1.3
+ │ │ ├── opening_loc: (3,5)-(3,6) = "("
+ │ │ └── closing_loc: (3,10)-(3,11) = ")"
+ │ ├── call_operator_loc: (3,11)-(3,12) = "."
+ │ ├── name: :abs
+ │ ├── message_loc: (3,12)-(3,15) = "abs"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/lvar.txt b/test/prism/snapshots/whitequark/lvar.txt
new file mode 100644
index 0000000000..afceb6a1a4
--- /dev/null
+++ b/test/prism/snapshots/whitequark/lvar.txt
@@ -0,0 +1,15 @@
+@ ProgramNode (location: (1,0)-(1,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,3))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,3))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :foo
+ ├── message_loc: (1,0)-(1,3) = "foo"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/lvar_injecting_match.txt b/test/prism/snapshots/whitequark/lvar_injecting_match.txt
new file mode 100644
index 0000000000..0d1df23d0d
--- /dev/null
+++ b/test/prism/snapshots/whitequark/lvar_injecting_match.txt
@@ -0,0 +1,39 @@
+@ ProgramNode (location: (1,0)-(1,31))
+├── locals: [:match]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,31))
+ └── body: (length: 2)
+ ├── @ MatchWriteNode (location: (1,0)-(1,24))
+ │ ├── call:
+ │ │ @ CallNode (location: (1,0)-(1,24))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ RegularExpressionNode (location: (1,0)-(1,15))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (1,0)-(1,1) = "/"
+ │ │ │ ├── content_loc: (1,1)-(1,14) = "(?<match>bar)"
+ │ │ │ ├── closing_loc: (1,14)-(1,15) = "/"
+ │ │ │ └── unescaped: "(?<match>bar)"
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :=~
+ │ │ ├── message_loc: (1,16)-(1,18) = "=~"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (1,19)-(1,24))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ StringNode (location: (1,19)-(1,24))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: (1,19)-(1,20) = "'"
+ │ │ │ ├── content_loc: (1,20)-(1,23) = "bar"
+ │ │ │ ├── closing_loc: (1,23)-(1,24) = "'"
+ │ │ │ └── unescaped: "bar"
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── targets: (length: 1)
+ │ └── @ LocalVariableTargetNode (location: (1,4)-(1,9))
+ │ ├── name: :match
+ │ └── depth: 0
+ └── @ LocalVariableReadNode (location: (1,26)-(1,31))
+ ├── name: :match
+ └── depth: 0
diff --git a/test/prism/snapshots/whitequark/lvasgn.txt b/test/prism/snapshots/whitequark/lvasgn.txt
new file mode 100644
index 0000000000..be35c00587
--- /dev/null
+++ b/test/prism/snapshots/whitequark/lvasgn.txt
@@ -0,0 +1,17 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: [:var]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 2)
+ ├── @ LocalVariableWriteNode (location: (1,0)-(1,8))
+ │ ├── name: :var
+ │ ├── depth: 0
+ │ ├── name_loc: (1,0)-(1,3) = "var"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,6)-(1,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 10
+ │ └── operator_loc: (1,4)-(1,5) = "="
+ └── @ LocalVariableReadNode (location: (1,10)-(1,13))
+ ├── name: :var
+ └── depth: 0
diff --git a/test/prism/snapshots/whitequark/masgn.txt b/test/prism/snapshots/whitequark/masgn.txt
new file mode 100644
index 0000000000..32e46bfdf7
--- /dev/null
+++ b/test/prism/snapshots/whitequark/masgn.txt
@@ -0,0 +1,83 @@
+@ ProgramNode (location: (1,0)-(5,20))
+├── locals: [:foo, :bar, :baz]
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,20))
+ └── body: (length: 3)
+ ├── @ MultiWriteNode (location: (1,0)-(1,17))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ LocalVariableTargetNode (location: (1,1)-(1,4))
+ │ │ │ ├── name: :foo
+ │ │ │ └── depth: 0
+ │ │ └── @ LocalVariableTargetNode (location: (1,6)-(1,9))
+ │ │ ├── name: :bar
+ │ │ └── depth: 0
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: (1,0)-(1,1) = "("
+ │ ├── rparen_loc: (1,9)-(1,10) = ")"
+ │ ├── operator_loc: (1,11)-(1,12) = "="
+ │ └── value:
+ │ @ ArrayNode (location: (1,13)-(1,17))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (1,13)-(1,14))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (1,16)-(1,17))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── opening_loc: ∅
+ │ └── closing_loc: ∅
+ ├── @ MultiWriteNode (location: (3,0)-(3,15))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ LocalVariableTargetNode (location: (3,0)-(3,3))
+ │ │ │ ├── name: :foo
+ │ │ │ └── depth: 0
+ │ │ └── @ LocalVariableTargetNode (location: (3,5)-(3,8))
+ │ │ ├── name: :bar
+ │ │ └── depth: 0
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (3,9)-(3,10) = "="
+ │ └── value:
+ │ @ ArrayNode (location: (3,11)-(3,15))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 2)
+ │ │ ├── @ IntegerNode (location: (3,11)-(3,12))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ IntegerNode (location: (3,14)-(3,15))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── opening_loc: ∅
+ │ └── closing_loc: ∅
+ └── @ MultiWriteNode (location: (5,0)-(5,20))
+ ├── lefts: (length: 3)
+ │ ├── @ LocalVariableTargetNode (location: (5,0)-(5,3))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── @ LocalVariableTargetNode (location: (5,5)-(5,8))
+ │ │ ├── name: :bar
+ │ │ └── depth: 0
+ │ └── @ LocalVariableTargetNode (location: (5,10)-(5,13))
+ │ ├── name: :baz
+ │ └── depth: 0
+ ├── rest: ∅
+ ├── rights: (length: 0)
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (5,14)-(5,15) = "="
+ └── value:
+ @ ArrayNode (location: (5,16)-(5,20))
+ ├── flags: ∅
+ ├── elements: (length: 2)
+ │ ├── @ IntegerNode (location: (5,16)-(5,17))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── @ IntegerNode (location: (5,19)-(5,20))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── opening_loc: ∅
+ └── closing_loc: ∅
diff --git a/test/prism/snapshots/whitequark/masgn_attr.txt b/test/prism/snapshots/whitequark/masgn_attr.txt
new file mode 100644
index 0000000000..f4f4276597
--- /dev/null
+++ b/test/prism/snapshots/whitequark/masgn_attr.txt
@@ -0,0 +1,82 @@
+@ ProgramNode (location: (1,0)-(5,18))
+├── locals: [:foo]
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,18))
+ └── body: (length: 3)
+ ├── @ MultiWriteNode (location: (1,0)-(1,17))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ CallTargetNode (location: (1,0)-(1,6))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver:
+ │ │ │ │ @ SelfNode (location: (1,0)-(1,4))
+ │ │ │ ├── call_operator_loc: (1,4)-(1,5) = "."
+ │ │ │ ├── name: :A=
+ │ │ │ └── message_loc: (1,5)-(1,6) = "A"
+ │ │ └── @ LocalVariableTargetNode (location: (1,8)-(1,11))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (1,12)-(1,13) = "="
+ │ └── value:
+ │ @ LocalVariableReadNode (location: (1,14)-(1,17))
+ │ ├── name: :foo
+ │ └── depth: 0
+ ├── @ MultiWriteNode (location: (3,0)-(3,24))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ CallTargetNode (location: (3,0)-(3,6))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver:
+ │ │ │ │ @ SelfNode (location: (3,0)-(3,4))
+ │ │ │ ├── call_operator_loc: (3,4)-(3,5) = "."
+ │ │ │ ├── name: :a=
+ │ │ │ └── message_loc: (3,5)-(3,6) = "a"
+ │ │ └── @ IndexTargetNode (location: (3,8)-(3,18))
+ │ │ ├── flags: attribute_write, ignore_visibility
+ │ │ ├── receiver:
+ │ │ │ @ SelfNode (location: (3,8)-(3,12))
+ │ │ ├── opening_loc: (3,12)-(3,13) = "["
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (3,13)-(3,17))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 2)
+ │ │ │ ├── @ IntegerNode (location: (3,13)-(3,14))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── @ IntegerNode (location: (3,16)-(3,17))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── closing_loc: (3,17)-(3,18) = "]"
+ │ │ └── block: ∅
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (3,19)-(3,20) = "="
+ │ └── value:
+ │ @ LocalVariableReadNode (location: (3,21)-(3,24))
+ │ ├── name: :foo
+ │ └── depth: 0
+ └── @ MultiWriteNode (location: (5,0)-(5,18))
+ ├── lefts: (length: 2)
+ │ ├── @ CallTargetNode (location: (5,0)-(5,7))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver:
+ │ │ │ @ SelfNode (location: (5,0)-(5,4))
+ │ │ ├── call_operator_loc: (5,4)-(5,6) = "::"
+ │ │ ├── name: :a=
+ │ │ └── message_loc: (5,6)-(5,7) = "a"
+ │ └── @ LocalVariableTargetNode (location: (5,9)-(5,12))
+ │ ├── name: :foo
+ │ └── depth: 0
+ ├── rest: ∅
+ ├── rights: (length: 0)
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (5,13)-(5,14) = "="
+ └── value:
+ @ LocalVariableReadNode (location: (5,15)-(5,18))
+ ├── name: :foo
+ └── depth: 0
diff --git a/test/prism/snapshots/whitequark/masgn_cmd.txt b/test/prism/snapshots/whitequark/masgn_cmd.txt
new file mode 100644
index 0000000000..1c62658ab2
--- /dev/null
+++ b/test/prism/snapshots/whitequark/masgn_cmd.txt
@@ -0,0 +1,35 @@
+@ ProgramNode (location: (1,0)-(1,16))
+├── locals: [:foo, :bar]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,16))
+ └── body: (length: 1)
+ └── @ MultiWriteNode (location: (1,0)-(1,16))
+ ├── lefts: (length: 2)
+ │ ├── @ LocalVariableTargetNode (location: (1,0)-(1,3))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ └── @ LocalVariableTargetNode (location: (1,5)-(1,8))
+ │ ├── name: :bar
+ │ └── depth: 0
+ ├── rest: ∅
+ ├── rights: (length: 0)
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (1,9)-(1,10) = "="
+ └── value:
+ @ CallNode (location: (1,11)-(1,16))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :m
+ ├── message_loc: (1,11)-(1,12) = "m"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,13)-(1,16))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ LocalVariableReadNode (location: (1,13)-(1,16))
+ │ ├── name: :foo
+ │ └── depth: 0
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/masgn_const.txt b/test/prism/snapshots/whitequark/masgn_const.txt
new file mode 100644
index 0000000000..56169deb17
--- /dev/null
+++ b/test/prism/snapshots/whitequark/masgn_const.txt
@@ -0,0 +1,46 @@
+@ ProgramNode (location: (1,0)-(3,18))
+├── locals: [:foo]
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,18))
+ └── body: (length: 2)
+ ├── @ MultiWriteNode (location: (1,0)-(1,14))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ ConstantPathTargetNode (location: (1,0)-(1,3))
+ │ │ │ ├── parent: ∅
+ │ │ │ ├── child:
+ │ │ │ │ @ ConstantReadNode (location: (1,2)-(1,3))
+ │ │ │ │ └── name: :A
+ │ │ │ └── delimiter_loc: (1,0)-(1,2) = "::"
+ │ │ └── @ LocalVariableTargetNode (location: (1,5)-(1,8))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (1,9)-(1,10) = "="
+ │ └── value:
+ │ @ LocalVariableReadNode (location: (1,11)-(1,14))
+ │ ├── name: :foo
+ │ └── depth: 0
+ └── @ MultiWriteNode (location: (3,0)-(3,18))
+ ├── lefts: (length: 2)
+ │ ├── @ ConstantPathTargetNode (location: (3,0)-(3,7))
+ │ │ ├── parent:
+ │ │ │ @ SelfNode (location: (3,0)-(3,4))
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (3,6)-(3,7))
+ │ │ │ └── name: :A
+ │ │ └── delimiter_loc: (3,4)-(3,6) = "::"
+ │ └── @ LocalVariableTargetNode (location: (3,9)-(3,12))
+ │ ├── name: :foo
+ │ └── depth: 0
+ ├── rest: ∅
+ ├── rights: (length: 0)
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (3,13)-(3,14) = "="
+ └── value:
+ @ LocalVariableReadNode (location: (3,15)-(3,18))
+ ├── name: :foo
+ └── depth: 0
diff --git a/test/prism/snapshots/whitequark/masgn_nested.txt b/test/prism/snapshots/whitequark/masgn_nested.txt
new file mode 100644
index 0000000000..3e4602472f
--- /dev/null
+++ b/test/prism/snapshots/whitequark/masgn_nested.txt
@@ -0,0 +1,66 @@
+@ ProgramNode (location: (1,0)-(3,15))
+├── locals: [:b, :a, :c]
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,15))
+ └── body: (length: 2)
+ ├── @ MultiWriteNode (location: (1,0)-(1,13))
+ │ ├── lefts: (length: 1)
+ │ │ └── @ MultiTargetNode (location: (1,1)-(1,6))
+ │ │ ├── lefts: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (1,2)-(1,3))
+ │ │ │ ├── name: :b
+ │ │ │ └── depth: 0
+ │ │ ├── rest:
+ │ │ │ @ ImplicitRestNode (location: (1,3)-(1,4))
+ │ │ ├── rights: (length: 0)
+ │ │ ├── lparen_loc: (1,1)-(1,2) = "("
+ │ │ └── rparen_loc: (1,5)-(1,6) = ")"
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: (1,0)-(1,1) = "("
+ │ ├── rparen_loc: (1,6)-(1,7) = ")"
+ │ ├── operator_loc: (1,8)-(1,9) = "="
+ │ └── value:
+ │ @ CallNode (location: (1,10)-(1,13))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,10)-(1,13) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ MultiWriteNode (location: (3,0)-(3,15))
+ ├── lefts: (length: 2)
+ │ ├── @ LocalVariableTargetNode (location: (3,0)-(3,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ └── @ MultiTargetNode (location: (3,3)-(3,9))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ LocalVariableTargetNode (location: (3,4)-(3,5))
+ │ │ │ ├── name: :b
+ │ │ │ └── depth: 0
+ │ │ └── @ LocalVariableTargetNode (location: (3,7)-(3,8))
+ │ │ ├── name: :c
+ │ │ └── depth: 0
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: (3,3)-(3,4) = "("
+ │ └── rparen_loc: (3,8)-(3,9) = ")"
+ ├── rest: ∅
+ ├── rights: (length: 0)
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (3,10)-(3,11) = "="
+ └── value:
+ @ CallNode (location: (3,12)-(3,15))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :foo
+ ├── message_loc: (3,12)-(3,15) = "foo"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/masgn_splat.txt b/test/prism/snapshots/whitequark/masgn_splat.txt
new file mode 100644
index 0000000000..0db040a3ab
--- /dev/null
+++ b/test/prism/snapshots/whitequark/masgn_splat.txt
@@ -0,0 +1,284 @@
+@ ProgramNode (location: (1,0)-(19,16))
+├── locals: [:c, :d, :b, :a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(19,16))
+ └── body: (length: 10)
+ ├── @ MultiWriteNode (location: (1,0)-(1,7))
+ │ ├── lefts: (length: 0)
+ │ ├── rest:
+ │ │ @ SplatNode (location: (1,0)-(1,1))
+ │ │ ├── operator_loc: (1,0)-(1,1) = "*"
+ │ │ └── expression: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (1,2)-(1,3) = "="
+ │ └── value:
+ │ @ CallNode (location: (1,4)-(1,7))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (1,4)-(1,7) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ MultiWriteNode (location: (3,0)-(3,13))
+ │ ├── lefts: (length: 0)
+ │ ├── rest:
+ │ │ @ SplatNode (location: (3,0)-(3,1))
+ │ │ ├── operator_loc: (3,0)-(3,1) = "*"
+ │ │ └── expression: ∅
+ │ ├── rights: (length: 2)
+ │ │ ├── @ LocalVariableTargetNode (location: (3,3)-(3,4))
+ │ │ │ ├── name: :c
+ │ │ │ └── depth: 0
+ │ │ └── @ LocalVariableTargetNode (location: (3,6)-(3,7))
+ │ │ ├── name: :d
+ │ │ └── depth: 0
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (3,8)-(3,9) = "="
+ │ └── value:
+ │ @ CallNode (location: (3,10)-(3,13))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (3,10)-(3,13) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ MultiWriteNode (location: (5,0)-(5,8))
+ │ ├── lefts: (length: 0)
+ │ ├── rest:
+ │ │ @ SplatNode (location: (5,0)-(5,2))
+ │ │ ├── operator_loc: (5,0)-(5,1) = "*"
+ │ │ └── expression:
+ │ │ @ LocalVariableTargetNode (location: (5,1)-(5,2))
+ │ │ ├── name: :b
+ │ │ └── depth: 0
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (5,3)-(5,4) = "="
+ │ └── value:
+ │ @ CallNode (location: (5,5)-(5,8))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (5,5)-(5,8) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ MultiWriteNode (location: (7,0)-(7,11))
+ │ ├── lefts: (length: 0)
+ │ ├── rest:
+ │ │ @ SplatNode (location: (7,0)-(7,2))
+ │ │ ├── operator_loc: (7,0)-(7,1) = "*"
+ │ │ └── expression:
+ │ │ @ LocalVariableTargetNode (location: (7,1)-(7,2))
+ │ │ ├── name: :b
+ │ │ └── depth: 0
+ │ ├── rights: (length: 1)
+ │ │ └── @ LocalVariableTargetNode (location: (7,4)-(7,5))
+ │ │ ├── name: :c
+ │ │ └── depth: 0
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (7,6)-(7,7) = "="
+ │ └── value:
+ │ @ CallNode (location: (7,8)-(7,11))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (7,8)-(7,11) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ MultiWriteNode (location: (9,0)-(9,18))
+ │ ├── lefts: (length: 2)
+ │ │ ├── @ InstanceVariableTargetNode (location: (9,0)-(9,4))
+ │ │ │ └── name: :@foo
+ │ │ └── @ ClassVariableTargetNode (location: (9,6)-(9,11))
+ │ │ └── name: :@@bar
+ │ ├── rest: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (9,12)-(9,13) = "="
+ │ └── value:
+ │ @ ArrayNode (location: (9,14)-(9,18))
+ │ ├── flags: contains_splat
+ │ ├── elements: (length: 1)
+ │ │ └── @ SplatNode (location: (9,14)-(9,18))
+ │ │ ├── operator_loc: (9,14)-(9,15) = "*"
+ │ │ └── expression:
+ │ │ @ CallNode (location: (9,15)-(9,18))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (9,15)-(9,18) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: ∅
+ │ └── closing_loc: ∅
+ ├── @ MultiWriteNode (location: (11,0)-(11,10))
+ │ ├── lefts: (length: 1)
+ │ │ └── @ LocalVariableTargetNode (location: (11,0)-(11,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── rest:
+ │ │ @ SplatNode (location: (11,3)-(11,4))
+ │ │ ├── operator_loc: (11,3)-(11,4) = "*"
+ │ │ └── expression: ∅
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (11,5)-(11,6) = "="
+ │ └── value:
+ │ @ CallNode (location: (11,7)-(11,10))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (11,7)-(11,10) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ MultiWriteNode (location: (13,0)-(13,13))
+ │ ├── lefts: (length: 1)
+ │ │ └── @ LocalVariableTargetNode (location: (13,0)-(13,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── rest:
+ │ │ @ SplatNode (location: (13,3)-(13,4))
+ │ │ ├── operator_loc: (13,3)-(13,4) = "*"
+ │ │ └── expression: ∅
+ │ ├── rights: (length: 1)
+ │ │ └── @ LocalVariableTargetNode (location: (13,6)-(13,7))
+ │ │ ├── name: :c
+ │ │ └── depth: 0
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (13,8)-(13,9) = "="
+ │ └── value:
+ │ @ CallNode (location: (13,10)-(13,13))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (13,10)-(13,13) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ MultiWriteNode (location: (15,0)-(15,11))
+ │ ├── lefts: (length: 1)
+ │ │ └── @ LocalVariableTargetNode (location: (15,0)-(15,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── rest:
+ │ │ @ SplatNode (location: (15,3)-(15,5))
+ │ │ ├── operator_loc: (15,3)-(15,4) = "*"
+ │ │ └── expression:
+ │ │ @ LocalVariableTargetNode (location: (15,4)-(15,5))
+ │ │ ├── name: :b
+ │ │ └── depth: 0
+ │ ├── rights: (length: 0)
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (15,6)-(15,7) = "="
+ │ └── value:
+ │ @ CallNode (location: (15,8)-(15,11))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (15,8)-(15,11) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ MultiWriteNode (location: (17,0)-(17,14))
+ │ ├── lefts: (length: 1)
+ │ │ └── @ LocalVariableTargetNode (location: (17,0)-(17,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── rest:
+ │ │ @ SplatNode (location: (17,3)-(17,5))
+ │ │ ├── operator_loc: (17,3)-(17,4) = "*"
+ │ │ └── expression:
+ │ │ @ LocalVariableTargetNode (location: (17,4)-(17,5))
+ │ │ ├── name: :b
+ │ │ └── depth: 0
+ │ ├── rights: (length: 1)
+ │ │ └── @ LocalVariableTargetNode (location: (17,7)-(17,8))
+ │ │ ├── name: :c
+ │ │ └── depth: 0
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── operator_loc: (17,9)-(17,10) = "="
+ │ └── value:
+ │ @ CallNode (location: (17,11)-(17,14))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (17,11)-(17,14) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ MultiWriteNode (location: (19,0)-(19,16))
+ ├── lefts: (length: 2)
+ │ ├── @ LocalVariableTargetNode (location: (19,0)-(19,1))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ └── @ LocalVariableTargetNode (location: (19,3)-(19,4))
+ │ ├── name: :b
+ │ └── depth: 0
+ ├── rest: ∅
+ ├── rights: (length: 0)
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (19,5)-(19,6) = "="
+ └── value:
+ @ ArrayNode (location: (19,7)-(19,16))
+ ├── flags: contains_splat
+ ├── elements: (length: 2)
+ │ ├── @ SplatNode (location: (19,7)-(19,11))
+ │ │ ├── operator_loc: (19,7)-(19,8) = "*"
+ │ │ └── expression:
+ │ │ @ CallNode (location: (19,8)-(19,11))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (19,8)-(19,11) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── @ CallNode (location: (19,13)-(19,16))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (19,13)-(19,16) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── opening_loc: ∅
+ └── closing_loc: ∅
diff --git a/test/prism/snapshots/whitequark/module.txt b/test/prism/snapshots/whitequark/module.txt
new file mode 100644
index 0000000000..87348c4ec9
--- /dev/null
+++ b/test/prism/snapshots/whitequark/module.txt
@@ -0,0 +1,14 @@
+@ ProgramNode (location: (1,0)-(1,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,15))
+ └── body: (length: 1)
+ └── @ ModuleNode (location: (1,0)-(1,15))
+ ├── locals: []
+ ├── module_keyword_loc: (1,0)-(1,6) = "module"
+ ├── constant_path:
+ │ @ ConstantReadNode (location: (1,7)-(1,10))
+ │ └── name: :Foo
+ ├── body: ∅
+ ├── end_keyword_loc: (1,12)-(1,15) = "end"
+ └── name: :Foo
diff --git a/test/prism/snapshots/whitequark/multiple_pattern_matches.txt b/test/prism/snapshots/whitequark/multiple_pattern_matches.txt
new file mode 100644
index 0000000000..afa3517e11
--- /dev/null
+++ b/test/prism/snapshots/whitequark/multiple_pattern_matches.txt
@@ -0,0 +1,173 @@
+@ ProgramNode (location: (1,0)-(5,12))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,12))
+ └── body: (length: 4)
+ ├── @ MatchRequiredNode (location: (1,0)-(1,12))
+ │ ├── value:
+ │ │ @ HashNode (location: (1,0)-(1,6))
+ │ │ ├── opening_loc: (1,0)-(1,1) = "{"
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (1,1)-(1,5))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (1,1)-(1,3))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (1,1)-(1,2) = "a"
+ │ │ │ │ ├── closing_loc: (1,2)-(1,3) = ":"
+ │ │ │ │ └── unescaped: "a"
+ │ │ │ ├── value:
+ │ │ │ │ @ IntegerNode (location: (1,4)-(1,5))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 0
+ │ │ │ └── operator_loc: ∅
+ │ │ └── closing_loc: (1,5)-(1,6) = "}"
+ │ ├── pattern:
+ │ │ @ HashPatternNode (location: (1,10)-(1,12))
+ │ │ ├── constant: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (1,10)-(1,12))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (1,10)-(1,12))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (1,10)-(1,11) = "a"
+ │ │ │ │ ├── closing_loc: (1,11)-(1,12) = ":"
+ │ │ │ │ └── unescaped: "a"
+ │ │ │ ├── value:
+ │ │ │ │ @ ImplicitNode (location: (1,10)-(1,11))
+ │ │ │ │ └── value:
+ │ │ │ │ @ LocalVariableTargetNode (location: (1,10)-(1,11))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ └── depth: 0
+ │ │ │ └── operator_loc: ∅
+ │ │ ├── rest: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (1,7)-(1,9) = "=>"
+ ├── @ MatchRequiredNode (location: (2,0)-(2,12))
+ │ ├── value:
+ │ │ @ HashNode (location: (2,0)-(2,6))
+ │ │ ├── opening_loc: (2,0)-(2,1) = "{"
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (2,1)-(2,5))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (2,1)-(2,3))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (2,1)-(2,2) = "a"
+ │ │ │ │ ├── closing_loc: (2,2)-(2,3) = ":"
+ │ │ │ │ └── unescaped: "a"
+ │ │ │ ├── value:
+ │ │ │ │ @ IntegerNode (location: (2,4)-(2,5))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 0
+ │ │ │ └── operator_loc: ∅
+ │ │ └── closing_loc: (2,5)-(2,6) = "}"
+ │ ├── pattern:
+ │ │ @ HashPatternNode (location: (2,10)-(2,12))
+ │ │ ├── constant: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (2,10)-(2,12))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (2,10)-(2,12))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (2,10)-(2,11) = "a"
+ │ │ │ │ ├── closing_loc: (2,11)-(2,12) = ":"
+ │ │ │ │ └── unescaped: "a"
+ │ │ │ ├── value:
+ │ │ │ │ @ ImplicitNode (location: (2,10)-(2,11))
+ │ │ │ │ └── value:
+ │ │ │ │ @ LocalVariableTargetNode (location: (2,10)-(2,11))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ └── depth: 0
+ │ │ │ └── operator_loc: ∅
+ │ │ ├── rest: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (2,7)-(2,9) = "=>"
+ ├── @ MatchPredicateNode (location: (4,0)-(4,12))
+ │ ├── value:
+ │ │ @ HashNode (location: (4,0)-(4,6))
+ │ │ ├── opening_loc: (4,0)-(4,1) = "{"
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (4,1)-(4,5))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (4,1)-(4,3))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (4,1)-(4,2) = "a"
+ │ │ │ │ ├── closing_loc: (4,2)-(4,3) = ":"
+ │ │ │ │ └── unescaped: "a"
+ │ │ │ ├── value:
+ │ │ │ │ @ IntegerNode (location: (4,4)-(4,5))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 0
+ │ │ │ └── operator_loc: ∅
+ │ │ └── closing_loc: (4,5)-(4,6) = "}"
+ │ ├── pattern:
+ │ │ @ HashPatternNode (location: (4,10)-(4,12))
+ │ │ ├── constant: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (4,10)-(4,12))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (4,10)-(4,12))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (4,10)-(4,11) = "a"
+ │ │ │ │ ├── closing_loc: (4,11)-(4,12) = ":"
+ │ │ │ │ └── unescaped: "a"
+ │ │ │ ├── value:
+ │ │ │ │ @ ImplicitNode (location: (4,10)-(4,11))
+ │ │ │ │ └── value:
+ │ │ │ │ @ LocalVariableTargetNode (location: (4,10)-(4,11))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ └── depth: 0
+ │ │ │ └── operator_loc: ∅
+ │ │ ├── rest: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (4,7)-(4,9) = "in"
+ └── @ MatchPredicateNode (location: (5,0)-(5,12))
+ ├── value:
+ │ @ HashNode (location: (5,0)-(5,6))
+ │ ├── opening_loc: (5,0)-(5,1) = "{"
+ │ ├── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (5,1)-(5,5))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (5,1)-(5,3))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (5,1)-(5,2) = "a"
+ │ │ │ ├── closing_loc: (5,2)-(5,3) = ":"
+ │ │ │ └── unescaped: "a"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (5,4)-(5,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 0
+ │ │ └── operator_loc: ∅
+ │ └── closing_loc: (5,5)-(5,6) = "}"
+ ├── pattern:
+ │ @ HashPatternNode (location: (5,10)-(5,12))
+ │ ├── constant: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (5,10)-(5,12))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (5,10)-(5,12))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (5,10)-(5,11) = "a"
+ │ │ │ ├── closing_loc: (5,11)-(5,12) = ":"
+ │ │ │ └── unescaped: "a"
+ │ │ ├── value:
+ │ │ │ @ ImplicitNode (location: (5,10)-(5,11))
+ │ │ │ └── value:
+ │ │ │ @ LocalVariableTargetNode (location: (5,10)-(5,11))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ └── operator_loc: ∅
+ │ ├── rest: ∅
+ │ ├── opening_loc: ∅
+ │ └── closing_loc: ∅
+ └── operator_loc: (5,7)-(5,9) = "in"
diff --git a/test/prism/snapshots/whitequark/newline_in_hash_argument.txt b/test/prism/snapshots/whitequark/newline_in_hash_argument.txt
new file mode 100644
index 0000000000..d5e89d87f9
--- /dev/null
+++ b/test/prism/snapshots/whitequark/newline_in_hash_argument.txt
@@ -0,0 +1,163 @@
+@ ProgramNode (location: (1,0)-(14,1))
+├── locals: [:a, :b]
+└── statements:
+ @ StatementsNode (location: (1,0)-(14,1))
+ └── body: (length: 3)
+ ├── @ CaseMatchNode (location: (1,0)-(8,3))
+ │ ├── predicate:
+ │ │ @ CallNode (location: (1,5)-(1,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,5)-(1,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── conditions: (length: 2)
+ │ │ ├── @ InNode (location: (2,0)-(4,4))
+ │ │ │ ├── pattern:
+ │ │ │ │ @ HashPatternNode (location: (2,3)-(2,5))
+ │ │ │ │ ├── constant: ∅
+ │ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ │ └── @ AssocNode (location: (2,3)-(2,5))
+ │ │ │ │ │ ├── key:
+ │ │ │ │ │ │ @ SymbolNode (location: (2,3)-(2,5))
+ │ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ │ ├── value_loc: (2,3)-(2,4) = "a"
+ │ │ │ │ │ │ ├── closing_loc: (2,4)-(2,5) = ":"
+ │ │ │ │ │ │ └── unescaped: "a"
+ │ │ │ │ │ ├── value:
+ │ │ │ │ │ │ @ ImplicitNode (location: (2,3)-(2,4))
+ │ │ │ │ │ │ └── value:
+ │ │ │ │ │ │ @ LocalVariableTargetNode (location: (2,3)-(2,4))
+ │ │ │ │ │ │ ├── name: :a
+ │ │ │ │ │ │ └── depth: 0
+ │ │ │ │ │ └── operator_loc: ∅
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ └── closing_loc: ∅
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (3,0)-(4,4))
+ │ │ │ │ └── body: (length: 2)
+ │ │ │ │ ├── @ IntegerNode (location: (3,0)-(3,1))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 0
+ │ │ │ │ └── @ TrueNode (location: (4,0)-(4,4))
+ │ │ │ ├── in_loc: (2,0)-(2,2) = "in"
+ │ │ │ └── then_loc: ∅
+ │ │ └── @ InNode (location: (5,0)-(7,4))
+ │ │ ├── pattern:
+ │ │ │ @ HashPatternNode (location: (5,3)-(5,7))
+ │ │ │ ├── constant: ∅
+ │ │ │ ├── elements: (length: 1)
+ │ │ │ │ └── @ AssocNode (location: (5,3)-(5,7))
+ │ │ │ │ ├── key:
+ │ │ │ │ │ @ SymbolNode (location: (5,3)-(5,7))
+ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ │ ├── opening_loc: (5,3)-(5,4) = "\""
+ │ │ │ │ │ ├── value_loc: (5,4)-(5,5) = "b"
+ │ │ │ │ │ ├── closing_loc: (5,5)-(5,7) = "\":"
+ │ │ │ │ │ └── unescaped: "b"
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ ImplicitNode (location: (5,4)-(5,5))
+ │ │ │ │ │ └── value:
+ │ │ │ │ │ @ LocalVariableTargetNode (location: (5,4)-(5,5))
+ │ │ │ │ │ ├── name: :b
+ │ │ │ │ │ └── depth: 0
+ │ │ │ │ └── operator_loc: ∅
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ └── closing_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (6,0)-(7,4))
+ │ │ │ └── body: (length: 2)
+ │ │ │ ├── @ IntegerNode (location: (6,0)-(6,1))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 0
+ │ │ │ └── @ TrueNode (location: (7,0)-(7,4))
+ │ │ ├── in_loc: (5,0)-(5,2) = "in"
+ │ │ └── then_loc: ∅
+ │ ├── consequent: ∅
+ │ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ │ └── end_keyword_loc: (8,0)-(8,3) = "end"
+ ├── @ CallNode (location: (10,0)-(11,1))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (10,0)-(10,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :obj
+ │ │ ├── message_loc: (10,0)-(10,3) = "obj"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (10,3)-(10,4) = "."
+ │ ├── name: :set
+ │ ├── message_loc: (10,4)-(10,7) = "set"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (10,8)-(11,1))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (10,8)-(11,1))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (10,8)-(11,1))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (10,8)-(10,14))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (10,8)-(10,9) = "\""
+ │ │ │ ├── value_loc: (10,9)-(10,12) = "foo"
+ │ │ │ ├── closing_loc: (10,12)-(10,14) = "\":"
+ │ │ │ └── unescaped: "foo"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (11,0)-(11,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (13,0)-(14,1))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (13,0)-(13,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :obj
+ │ ├── message_loc: (13,0)-(13,3) = "obj"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (13,3)-(13,4) = "."
+ ├── name: :set
+ ├── message_loc: (13,4)-(13,7) = "set"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (13,8)-(14,1))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ KeywordHashNode (location: (13,8)-(14,1))
+ │ ├── flags: symbol_keys
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (13,8)-(14,1))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (13,8)-(13,12))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (13,8)-(13,11) = "foo"
+ │ │ ├── closing_loc: (13,11)-(13,12) = ":"
+ │ │ └── unescaped: "foo"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (14,0)-(14,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/nil.txt b/test/prism/snapshots/whitequark/nil.txt
new file mode 100644
index 0000000000..15774c02fd
--- /dev/null
+++ b/test/prism/snapshots/whitequark/nil.txt
@@ -0,0 +1,6 @@
+@ ProgramNode (location: (1,0)-(1,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,3))
+ └── body: (length: 1)
+ └── @ NilNode (location: (1,0)-(1,3))
diff --git a/test/prism/snapshots/whitequark/nil_expression.txt b/test/prism/snapshots/whitequark/nil_expression.txt
new file mode 100644
index 0000000000..7421115611
--- /dev/null
+++ b/test/prism/snapshots/whitequark/nil_expression.txt
@@ -0,0 +1,16 @@
+@ ProgramNode (location: (1,0)-(3,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,9))
+ └── body: (length: 2)
+ ├── @ ParenthesesNode (location: (1,0)-(1,2))
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,0)-(1,1) = "("
+ │ └── closing_loc: (1,1)-(1,2) = ")"
+ └── @ BeginNode (location: (3,0)-(3,9))
+ ├── begin_keyword_loc: (3,0)-(3,5) = "begin"
+ ├── statements: ∅
+ ├── rescue_clause: ∅
+ ├── else_clause: ∅
+ ├── ensure_clause: ∅
+ └── end_keyword_loc: (3,6)-(3,9) = "end"
diff --git a/test/prism/snapshots/whitequark/non_lvar_injecting_match.txt b/test/prism/snapshots/whitequark/non_lvar_injecting_match.txt
new file mode 100644
index 0000000000..584e997df2
--- /dev/null
+++ b/test/prism/snapshots/whitequark/non_lvar_injecting_match.txt
@@ -0,0 +1,44 @@
+@ ProgramNode (location: (1,0)-(1,28))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,28))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,28))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ InterpolatedRegularExpressionNode (location: (1,0)-(1,19))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,1) = "/"
+ │ ├── parts: (length: 2)
+ │ │ ├── @ EmbeddedStatementsNode (location: (1,1)-(1,5))
+ │ │ │ ├── opening_loc: (1,1)-(1,3) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (1,3)-(1,4))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (1,3)-(1,4))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── closing_loc: (1,4)-(1,5) = "}"
+ │ │ └── @ StringNode (location: (1,5)-(1,18))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (1,5)-(1,18) = "(?<match>bar)"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "(?<match>bar)"
+ │ └── closing_loc: (1,18)-(1,19) = "/"
+ ├── call_operator_loc: ∅
+ ├── name: :=~
+ ├── message_loc: (1,20)-(1,22) = "=~"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,23)-(1,28))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ StringNode (location: (1,23)-(1,28))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,23)-(1,24) = "'"
+ │ ├── content_loc: (1,24)-(1,27) = "bar"
+ │ ├── closing_loc: (1,27)-(1,28) = "'"
+ │ └── unescaped: "bar"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/not.txt b/test/prism/snapshots/whitequark/not.txt
new file mode 100644
index 0000000000..0a6d60e502
--- /dev/null
+++ b/test/prism/snapshots/whitequark/not.txt
@@ -0,0 +1,55 @@
+@ ProgramNode (location: (1,0)-(5,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,8))
+ └── body: (length: 3)
+ ├── @ CallNode (location: (1,0)-(1,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,4)-(1,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,4)-(1,7) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!
+ │ ├── message_loc: (1,0)-(1,3) = "not"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (3,0)-(3,5))
+ │ ├── flags: ∅
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!
+ │ ├── message_loc: (3,0)-(3,3) = "not"
+ │ ├── opening_loc: (3,3)-(3,4) = "("
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (3,4)-(3,5) = ")"
+ │ └── block: ∅
+ └── @ CallNode (location: (5,0)-(5,8))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (5,4)-(5,7))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (5,4)-(5,7) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :!
+ ├── message_loc: (5,0)-(5,3) = "not"
+ ├── opening_loc: (5,3)-(5,4) = "("
+ ├── arguments: ∅
+ ├── closing_loc: (5,7)-(5,8) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/not_cmd.txt b/test/prism/snapshots/whitequark/not_cmd.txt
new file mode 100644
index 0000000000..3b111272c6
--- /dev/null
+++ b/test/prism/snapshots/whitequark/not_cmd.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,9))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,4)-(1,9))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (1,4)-(1,5) = "m"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,6)-(1,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (1,6)-(1,9))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,6)-(1,9) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :!
+ ├── message_loc: (1,0)-(1,3) = "not"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/not_masgn__24.txt b/test/prism/snapshots/whitequark/not_masgn__24.txt
new file mode 100644
index 0000000000..90124c3866
--- /dev/null
+++ b/test/prism/snapshots/whitequark/not_masgn__24.txt
@@ -0,0 +1,45 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: [:a, :b]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,13))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ ParenthesesNode (location: (1,1)-(1,13))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,2)-(1,12))
+ │ │ └── body: (length: 1)
+ │ │ └── @ MultiWriteNode (location: (1,2)-(1,12))
+ │ │ ├── lefts: (length: 2)
+ │ │ │ ├── @ LocalVariableTargetNode (location: (1,2)-(1,3))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ └── depth: 0
+ │ │ │ └── @ LocalVariableTargetNode (location: (1,5)-(1,6))
+ │ │ │ ├── name: :b
+ │ │ │ └── depth: 0
+ │ │ ├── rest: ∅
+ │ │ ├── rights: (length: 0)
+ │ │ ├── lparen_loc: ∅
+ │ │ ├── rparen_loc: ∅
+ │ │ ├── operator_loc: (1,7)-(1,8) = "="
+ │ │ └── value:
+ │ │ @ CallNode (location: (1,9)-(1,12))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,9)-(1,12) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (1,1)-(1,2) = "("
+ │ └── closing_loc: (1,12)-(1,13) = ")"
+ ├── call_operator_loc: ∅
+ ├── name: :!
+ ├── message_loc: (1,0)-(1,1) = "!"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/nth_ref.txt b/test/prism/snapshots/whitequark/nth_ref.txt
new file mode 100644
index 0000000000..1d386d518b
--- /dev/null
+++ b/test/prism/snapshots/whitequark/nth_ref.txt
@@ -0,0 +1,7 @@
+@ ProgramNode (location: (1,0)-(1,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,3))
+ └── body: (length: 1)
+ └── @ NumberedReferenceReadNode (location: (1,0)-(1,3))
+ └── number: 10
diff --git a/test/prism/snapshots/whitequark/numbered_args_after_27.txt b/test/prism/snapshots/whitequark/numbered_args_after_27.txt
new file mode 100644
index 0000000000..56419adccd
--- /dev/null
+++ b/test/prism/snapshots/whitequark/numbered_args_after_27.txt
@@ -0,0 +1,143 @@
+@ ProgramNode (location: (1,0)-(7,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(7,13))
+ └── body: (length: 4)
+ ├── @ LambdaNode (location: (1,0)-(1,17))
+ │ ├── locals: [:_1, :_2, :_3, :_4, :_5, :_6, :_7, :_8, :_9]
+ │ ├── operator_loc: (1,0)-(1,2) = "->"
+ │ ├── opening_loc: (1,3)-(1,5) = "do"
+ │ ├── closing_loc: (1,14)-(1,17) = "end"
+ │ ├── parameters:
+ │ │ @ NumberedParametersNode (location: (1,0)-(1,17))
+ │ │ └── maximum: 9
+ │ └── body:
+ │ @ StatementsNode (location: (1,6)-(1,13))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,6)-(1,13))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (1,6)-(1,8))
+ │ │ ├── name: :_1
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (1,9)-(1,10) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,11)-(1,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (1,11)-(1,13))
+ │ │ ├── name: :_9
+ │ │ └── depth: 0
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ LambdaNode (location: (3,0)-(3,13))
+ │ ├── locals: [:_1, :_2, :_3, :_4, :_5, :_6, :_7, :_8, :_9]
+ │ ├── operator_loc: (3,0)-(3,2) = "->"
+ │ ├── opening_loc: (3,3)-(3,4) = "{"
+ │ ├── closing_loc: (3,12)-(3,13) = "}"
+ │ ├── parameters:
+ │ │ @ NumberedParametersNode (location: (3,0)-(3,13))
+ │ │ └── maximum: 9
+ │ └── body:
+ │ @ StatementsNode (location: (3,5)-(3,12))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (3,5)-(3,12))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (3,5)-(3,7))
+ │ │ ├── name: :_1
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (3,8)-(3,9) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,10)-(3,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (3,10)-(3,12))
+ │ │ ├── name: :_9
+ │ │ └── depth: 0
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (5,0)-(5,16))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (5,0)-(5,1) = "m"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (5,2)-(5,16))
+ │ ├── locals: [:_1, :_2, :_3, :_4, :_5, :_6, :_7, :_8, :_9]
+ │ ├── parameters:
+ │ │ @ NumberedParametersNode (location: (5,2)-(5,16))
+ │ │ └── maximum: 9
+ │ ├── body:
+ │ │ @ StatementsNode (location: (5,5)-(5,12))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (5,5)-(5,12))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ LocalVariableReadNode (location: (5,5)-(5,7))
+ │ │ │ ├── name: :_1
+ │ │ │ └── depth: 0
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :+
+ │ │ ├── message_loc: (5,8)-(5,9) = "+"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (5,10)-(5,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ LocalVariableReadNode (location: (5,10)-(5,12))
+ │ │ │ ├── name: :_9
+ │ │ │ └── depth: 0
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (5,2)-(5,4) = "do"
+ │ └── closing_loc: (5,13)-(5,16) = "end"
+ └── @ CallNode (location: (7,0)-(7,13))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :m
+ ├── message_loc: (7,0)-(7,1) = "m"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (7,2)-(7,13))
+ ├── locals: [:_1, :_2, :_3, :_4, :_5, :_6, :_7, :_8, :_9]
+ ├── parameters:
+ │ @ NumberedParametersNode (location: (7,2)-(7,13))
+ │ └── maximum: 9
+ ├── body:
+ │ @ StatementsNode (location: (7,4)-(7,11))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (7,4)-(7,11))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (7,4)-(7,6))
+ │ │ ├── name: :_1
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (7,7)-(7,8) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (7,9)-(7,11))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (7,9)-(7,11))
+ │ │ ├── name: :_9
+ │ │ └── depth: 0
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── opening_loc: (7,2)-(7,3) = "{"
+ └── closing_loc: (7,12)-(7,13) = "}"
diff --git a/test/prism/snapshots/whitequark/numparam_outside_block.txt b/test/prism/snapshots/whitequark/numparam_outside_block.txt
new file mode 100644
index 0000000000..d79aedf7f9
--- /dev/null
+++ b/test/prism/snapshots/whitequark/numparam_outside_block.txt
@@ -0,0 +1,114 @@
+@ ProgramNode (location: (1,0)-(9,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(9,17))
+ └── body: (length: 5)
+ ├── @ CallNode (location: (1,0)-(1,2))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :_1
+ │ ├── message_loc: (1,0)-(1,2) = "_1"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ SingletonClassNode (location: (3,0)-(3,21))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (3,0)-(3,5) = "class"
+ │ ├── operator_loc: (3,6)-(3,8) = "<<"
+ │ ├── expression:
+ │ │ @ CallNode (location: (3,9)-(3,12))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (3,9)-(3,12) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (3,14)-(3,16))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (3,14)-(3,16))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :_1
+ │ │ ├── message_loc: (3,14)-(3,16) = "_1"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── end_keyword_loc: (3,18)-(3,21) = "end"
+ ├── @ ClassNode (location: (5,0)-(5,16))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (5,0)-(5,5) = "class"
+ │ ├── constant_path:
+ │ │ @ ConstantReadNode (location: (5,6)-(5,7))
+ │ │ └── name: :A
+ │ ├── inheritance_operator_loc: ∅
+ │ ├── superclass: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (5,9)-(5,11))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (5,9)-(5,11))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :_1
+ │ │ ├── message_loc: (5,9)-(5,11) = "_1"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── end_keyword_loc: (5,13)-(5,16) = "end"
+ │ └── name: :A
+ ├── @ DefNode (location: (7,0)-(7,19))
+ │ ├── name: :m
+ │ ├── name_loc: (7,9)-(7,10) = "m"
+ │ ├── receiver:
+ │ │ @ SelfNode (location: (7,4)-(7,8))
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (7,12)-(7,14))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (7,12)-(7,14))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :_1
+ │ │ ├── message_loc: (7,12)-(7,14) = "_1"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (7,0)-(7,3) = "def"
+ │ ├── operator_loc: (7,8)-(7,9) = "."
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (7,16)-(7,19) = "end"
+ └── @ ModuleNode (location: (9,0)-(9,17))
+ ├── locals: []
+ ├── module_keyword_loc: (9,0)-(9,6) = "module"
+ ├── constant_path:
+ │ @ ConstantReadNode (location: (9,7)-(9,8))
+ │ └── name: :A
+ ├── body:
+ │ @ StatementsNode (location: (9,10)-(9,12))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (9,10)-(9,12))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :_1
+ │ ├── message_loc: (9,10)-(9,12) = "_1"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── end_keyword_loc: (9,14)-(9,17) = "end"
+ └── name: :A
diff --git a/test/prism/snapshots/whitequark/numparam_ruby_bug_19025.txt b/test/prism/snapshots/whitequark/numparam_ruby_bug_19025.txt
new file mode 100644
index 0000000000..396238cbbf
--- /dev/null
+++ b/test/prism/snapshots/whitequark/numparam_ruby_bug_19025.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,14))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :p
+ ├── message_loc: (1,0)-(1,1) = "p"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,14))
+ ├── locals: [:_1]
+ ├── parameters:
+ │ @ NumberedParametersNode (location: (1,2)-(1,14))
+ │ └── maximum: 1
+ ├── body:
+ │ @ StatementsNode (location: (1,4)-(1,12))
+ │ └── body: (length: 1)
+ │ └── @ ArrayNode (location: (1,4)-(1,12))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ CallNode (location: (1,5)-(1,11))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ LocalVariableReadNode (location: (1,5)-(1,7))
+ │ │ │ ├── name: :_1
+ │ │ │ └── depth: 0
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :**
+ │ │ ├── message_loc: (1,8)-(1,10) = "**"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (1,10)-(1,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (1,10)-(1,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (1,4)-(1,5) = "["
+ │ └── closing_loc: (1,11)-(1,12) = "]"
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,13)-(1,14) = "}"
diff --git a/test/prism/snapshots/whitequark/op_asgn.txt b/test/prism/snapshots/whitequark/op_asgn.txt
new file mode 100644
index 0000000000..8f24a35ad0
--- /dev/null
+++ b/test/prism/snapshots/whitequark/op_asgn.txt
@@ -0,0 +1,74 @@
+@ ProgramNode (location: (1,0)-(5,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,11))
+ └── body: (length: 3)
+ ├── @ CallOperatorWriteNode (location: (1,0)-(1,10))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,0)-(1,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (1,3)-(1,4) = "."
+ │ ├── message_loc: (1,4)-(1,5) = "A"
+ │ ├── read_name: :A
+ │ ├── write_name: :A=
+ │ ├── operator: :+
+ │ ├── operator_loc: (1,6)-(1,8) = "+="
+ │ └── value:
+ │ @ IntegerNode (location: (1,9)-(1,10))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── @ CallOperatorWriteNode (location: (3,0)-(3,10))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (3,0)-(3,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (3,0)-(3,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (3,3)-(3,4) = "."
+ │ ├── message_loc: (3,4)-(3,5) = "a"
+ │ ├── read_name: :a
+ │ ├── write_name: :a=
+ │ ├── operator: :+
+ │ ├── operator_loc: (3,6)-(3,8) = "+="
+ │ └── value:
+ │ @ IntegerNode (location: (3,9)-(3,10))
+ │ ├── flags: decimal
+ │ └── value: 1
+ └── @ CallOperatorWriteNode (location: (5,0)-(5,11))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (5,0)-(5,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (5,0)-(5,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (5,3)-(5,5) = "::"
+ ├── message_loc: (5,5)-(5,6) = "a"
+ ├── read_name: :a
+ ├── write_name: :a=
+ ├── operator: :+
+ ├── operator_loc: (5,7)-(5,9) = "+="
+ └── value:
+ @ IntegerNode (location: (5,10)-(5,11))
+ ├── flags: decimal
+ └── value: 1
diff --git a/test/prism/snapshots/whitequark/op_asgn_cmd.txt b/test/prism/snapshots/whitequark/op_asgn_cmd.txt
new file mode 100644
index 0000000000..109c2fd2ed
--- /dev/null
+++ b/test/prism/snapshots/whitequark/op_asgn_cmd.txt
@@ -0,0 +1,178 @@
+@ ProgramNode (location: (1,0)-(7,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(7,15))
+ └── body: (length: 4)
+ ├── @ CallOperatorWriteNode (location: (1,0)-(1,14))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,0)-(1,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (1,3)-(1,4) = "."
+ │ ├── message_loc: (1,4)-(1,5) = "A"
+ │ ├── read_name: :A
+ │ ├── write_name: :A=
+ │ ├── operator: :+
+ │ ├── operator_loc: (1,6)-(1,8) = "+="
+ │ └── value:
+ │ @ CallNode (location: (1,9)-(1,14))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (1,9)-(1,10) = "m"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,11)-(1,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (1,11)-(1,14))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,11)-(1,14) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallOperatorWriteNode (location: (3,0)-(3,14))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (3,0)-(3,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (3,0)-(3,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (3,3)-(3,4) = "."
+ │ ├── message_loc: (3,4)-(3,5) = "a"
+ │ ├── read_name: :a
+ │ ├── write_name: :a=
+ │ ├── operator: :+
+ │ ├── operator_loc: (3,6)-(3,8) = "+="
+ │ └── value:
+ │ @ CallNode (location: (3,9)-(3,14))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (3,9)-(3,10) = "m"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,11)-(3,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (3,11)-(3,14))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (3,11)-(3,14) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ ConstantPathOperatorWriteNode (location: (5,0)-(5,15))
+ │ ├── target:
+ │ │ @ ConstantPathNode (location: (5,0)-(5,6))
+ │ │ ├── parent:
+ │ │ │ @ CallNode (location: (5,0)-(5,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (5,0)-(5,3) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (5,5)-(5,6))
+ │ │ │ └── name: :A
+ │ │ └── delimiter_loc: (5,3)-(5,5) = "::"
+ │ ├── operator_loc: (5,7)-(5,9) = "+="
+ │ ├── value:
+ │ │ @ CallNode (location: (5,10)-(5,15))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :m
+ │ │ ├── message_loc: (5,10)-(5,11) = "m"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (5,12)-(5,15))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ CallNode (location: (5,12)-(5,15))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (5,12)-(5,15) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator: :+
+ └── @ CallOperatorWriteNode (location: (7,0)-(7,15))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (7,0)-(7,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (7,0)-(7,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (7,3)-(7,5) = "::"
+ ├── message_loc: (7,5)-(7,6) = "a"
+ ├── read_name: :a
+ ├── write_name: :a=
+ ├── operator: :+
+ ├── operator_loc: (7,7)-(7,9) = "+="
+ └── value:
+ @ CallNode (location: (7,10)-(7,15))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :m
+ ├── message_loc: (7,10)-(7,11) = "m"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (7,12)-(7,15))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (7,12)-(7,15))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (7,12)-(7,15) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/op_asgn_index.txt b/test/prism/snapshots/whitequark/op_asgn_index.txt
new file mode 100644
index 0000000000..fe077fae13
--- /dev/null
+++ b/test/prism/snapshots/whitequark/op_asgn_index.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ IndexOperatorWriteNode (location: (1,0)-(1,14))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── opening_loc: (1,3)-(1,4) = "["
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ IntegerNode (location: (1,4)-(1,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 0
+ │ └── @ IntegerNode (location: (1,7)-(1,8))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: (1,8)-(1,9) = "]"
+ ├── block: ∅
+ ├── operator: :+
+ ├── operator_loc: (1,10)-(1,12) = "+="
+ └── value:
+ @ IntegerNode (location: (1,13)-(1,14))
+ ├── flags: decimal
+ └── value: 2
diff --git a/test/prism/snapshots/whitequark/op_asgn_index_cmd.txt b/test/prism/snapshots/whitequark/op_asgn_index_cmd.txt
new file mode 100644
index 0000000000..87082aad94
--- /dev/null
+++ b/test/prism/snapshots/whitequark/op_asgn_index_cmd.txt
@@ -0,0 +1,58 @@
+@ ProgramNode (location: (1,0)-(1,18))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,18))
+ └── body: (length: 1)
+ └── @ IndexOperatorWriteNode (location: (1,0)-(1,18))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── opening_loc: (1,3)-(1,4) = "["
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ IntegerNode (location: (1,4)-(1,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 0
+ │ └── @ IntegerNode (location: (1,7)-(1,8))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: (1,8)-(1,9) = "]"
+ ├── block: ∅
+ ├── operator: :+
+ ├── operator_loc: (1,10)-(1,12) = "+="
+ └── value:
+ @ CallNode (location: (1,13)-(1,18))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :m
+ ├── message_loc: (1,13)-(1,14) = "m"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,15)-(1,18))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (1,15)-(1,18))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,15)-(1,18) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/optarg.txt b/test/prism/snapshots/whitequark/optarg.txt
new file mode 100644
index 0000000000..695ed827ad
--- /dev/null
+++ b/test/prism/snapshots/whitequark/optarg.txt
@@ -0,0 +1,74 @@
+@ ProgramNode (location: (1,0)-(3,24))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,24))
+ └── body: (length: 2)
+ ├── @ DefNode (location: (1,0)-(1,18))
+ │ ├── name: :f
+ │ ├── name_loc: (1,4)-(1,5) = "f"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,6)-(1,13))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,6)-(1,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── name_loc: (1,6)-(1,9) = "foo"
+ │ │ │ ├── operator_loc: (1,10)-(1,11) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (1,12)-(1,13))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body: ∅
+ │ ├── locals: [:foo]
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (1,15)-(1,18) = "end"
+ └── @ DefNode (location: (3,0)-(3,24))
+ ├── name: :f
+ ├── name_loc: (3,4)-(3,5) = "f"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (3,6)-(3,18))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 2)
+ │ │ ├── @ OptionalParameterNode (location: (3,6)-(3,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── name_loc: (3,6)-(3,9) = "foo"
+ │ │ │ ├── operator_loc: (3,9)-(3,10) = "="
+ │ │ │ └── value:
+ │ │ │ @ IntegerNode (location: (3,10)-(3,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── @ OptionalParameterNode (location: (3,13)-(3,18))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :bar
+ │ │ ├── name_loc: (3,13)-(3,16) = "bar"
+ │ │ ├── operator_loc: (3,16)-(3,17) = "="
+ │ │ └── value:
+ │ │ @ IntegerNode (location: (3,17)-(3,18))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:foo, :bar]
+ ├── def_keyword_loc: (3,0)-(3,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (3,5)-(3,6) = "("
+ ├── rparen_loc: (3,18)-(3,19) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (3,21)-(3,24) = "end"
diff --git a/test/prism/snapshots/whitequark/or.txt b/test/prism/snapshots/whitequark/or.txt
new file mode 100644
index 0000000000..439146b8db
--- /dev/null
+++ b/test/prism/snapshots/whitequark/or.txt
@@ -0,0 +1,53 @@
+@ ProgramNode (location: (1,0)-(3,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,10))
+ └── body: (length: 2)
+ ├── @ OrNode (location: (1,0)-(1,10))
+ │ ├── left:
+ │ │ @ CallNode (location: (1,0)-(1,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right:
+ │ │ @ CallNode (location: (1,7)-(1,10))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,7)-(1,10) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (1,4)-(1,6) = "or"
+ └── @ OrNode (location: (3,0)-(3,10))
+ ├── left:
+ │ @ CallNode (location: (3,0)-(3,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (3,0)-(3,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── right:
+ │ @ CallNode (location: (3,7)-(3,10))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (3,7)-(3,10) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── operator_loc: (3,4)-(3,6) = "||"
diff --git a/test/prism/snapshots/whitequark/or_asgn.txt b/test/prism/snapshots/whitequark/or_asgn.txt
new file mode 100644
index 0000000000..c0ded24b93
--- /dev/null
+++ b/test/prism/snapshots/whitequark/or_asgn.txt
@@ -0,0 +1,59 @@
+@ ProgramNode (location: (1,0)-(3,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,15))
+ └── body: (length: 2)
+ ├── @ CallOrWriteNode (location: (1,0)-(1,11))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,0)-(1,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (1,3)-(1,4) = "."
+ │ ├── message_loc: (1,4)-(1,5) = "a"
+ │ ├── read_name: :a
+ │ ├── write_name: :a=
+ │ ├── operator_loc: (1,6)-(1,9) = "||="
+ │ └── value:
+ │ @ IntegerNode (location: (1,10)-(1,11))
+ │ ├── flags: decimal
+ │ └── value: 1
+ └── @ IndexOrWriteNode (location: (3,0)-(3,15))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (3,0)-(3,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (3,0)-(3,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── opening_loc: (3,3)-(3,4) = "["
+ ├── arguments:
+ │ @ ArgumentsNode (location: (3,4)-(3,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ IntegerNode (location: (3,4)-(3,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 0
+ │ └── @ IntegerNode (location: (3,7)-(3,8))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: (3,8)-(3,9) = "]"
+ ├── block: ∅
+ ├── operator_loc: (3,10)-(3,13) = "||="
+ └── value:
+ @ IntegerNode (location: (3,14)-(3,15))
+ ├── flags: decimal
+ └── value: 2
diff --git a/test/prism/snapshots/whitequark/parser_bug_272.txt b/test/prism/snapshots/whitequark/parser_bug_272.txt
new file mode 100644
index 0000000000..f158f255b9
--- /dev/null
+++ b/test/prism/snapshots/whitequark/parser_bug_272.txt
@@ -0,0 +1,42 @@
+@ ProgramNode (location: (1,0)-(1,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,15))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,15))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (1,0)-(1,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,4))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ InstanceVariableReadNode (location: (1,2)-(1,4))
+ │ └── name: :@b
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,5)-(1,15))
+ ├── locals: [:c]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,8)-(1,11))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,9)-(1,10))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,9)-(1,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :c
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,8)-(1,9) = "|"
+ │ └── closing_loc: (1,10)-(1,11) = "|"
+ ├── body: ∅
+ ├── opening_loc: (1,5)-(1,7) = "do"
+ └── closing_loc: (1,12)-(1,15) = "end"
diff --git a/test/prism/snapshots/whitequark/parser_bug_490.txt b/test/prism/snapshots/whitequark/parser_bug_490.txt
new file mode 100644
index 0000000000..9e4cd2bd15
--- /dev/null
+++ b/test/prism/snapshots/whitequark/parser_bug_490.txt
@@ -0,0 +1,106 @@
+@ ProgramNode (location: (1,0)-(5,45))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,45))
+ └── body: (length: 3)
+ ├── @ DefNode (location: (1,0)-(1,39))
+ │ ├── name: :m
+ │ ├── name_loc: (1,4)-(1,5) = "m"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,7)-(1,34))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SingletonClassNode (location: (1,7)-(1,34))
+ │ │ ├── locals: []
+ │ │ ├── class_keyword_loc: (1,7)-(1,12) = "class"
+ │ │ ├── operator_loc: (1,13)-(1,15) = "<<"
+ │ │ ├── expression:
+ │ │ │ @ SelfNode (location: (1,16)-(1,20))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (1,22)-(1,29))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ ConstantWriteNode (location: (1,22)-(1,29))
+ │ │ │ ├── name: :A
+ │ │ │ ├── name_loc: (1,22)-(1,23) = "A"
+ │ │ │ ├── value:
+ │ │ │ │ @ NilNode (location: (1,26)-(1,29))
+ │ │ │ └── operator_loc: (1,24)-(1,25) = "="
+ │ │ └── end_keyword_loc: (1,31)-(1,34) = "end"
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (1,36)-(1,39) = "end"
+ ├── @ DefNode (location: (3,0)-(3,44))
+ │ ├── name: :m
+ │ ├── name_loc: (3,4)-(3,5) = "m"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (3,7)-(3,39))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SingletonClassNode (location: (3,7)-(3,39))
+ │ │ ├── locals: []
+ │ │ ├── class_keyword_loc: (3,7)-(3,12) = "class"
+ │ │ ├── operator_loc: (3,13)-(3,15) = "<<"
+ │ │ ├── expression:
+ │ │ │ @ SelfNode (location: (3,16)-(3,20))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (3,22)-(3,34))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ ClassNode (location: (3,22)-(3,34))
+ │ │ │ ├── locals: []
+ │ │ │ ├── class_keyword_loc: (3,22)-(3,27) = "class"
+ │ │ │ ├── constant_path:
+ │ │ │ │ @ ConstantReadNode (location: (3,28)-(3,29))
+ │ │ │ │ └── name: :C
+ │ │ │ ├── inheritance_operator_loc: ∅
+ │ │ │ ├── superclass: ∅
+ │ │ │ ├── body: ∅
+ │ │ │ ├── end_keyword_loc: (3,31)-(3,34) = "end"
+ │ │ │ └── name: :C
+ │ │ └── end_keyword_loc: (3,36)-(3,39) = "end"
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (3,0)-(3,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (3,41)-(3,44) = "end"
+ └── @ DefNode (location: (5,0)-(5,45))
+ ├── name: :m
+ ├── name_loc: (5,4)-(5,5) = "m"
+ ├── receiver: ∅
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (5,7)-(5,40))
+ │ └── body: (length: 1)
+ │ └── @ SingletonClassNode (location: (5,7)-(5,40))
+ │ ├── locals: []
+ │ ├── class_keyword_loc: (5,7)-(5,12) = "class"
+ │ ├── operator_loc: (5,13)-(5,15) = "<<"
+ │ ├── expression:
+ │ │ @ SelfNode (location: (5,16)-(5,20))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (5,22)-(5,35))
+ │ │ └── body: (length: 1)
+ │ │ └── @ ModuleNode (location: (5,22)-(5,35))
+ │ │ ├── locals: []
+ │ │ ├── module_keyword_loc: (5,22)-(5,28) = "module"
+ │ │ ├── constant_path:
+ │ │ │ @ ConstantReadNode (location: (5,29)-(5,30))
+ │ │ │ └── name: :M
+ │ │ ├── body: ∅
+ │ │ ├── end_keyword_loc: (5,32)-(5,35) = "end"
+ │ │ └── name: :M
+ │ └── end_keyword_loc: (5,37)-(5,40) = "end"
+ ├── locals: []
+ ├── def_keyword_loc: (5,0)-(5,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (5,42)-(5,45) = "end"
diff --git a/test/prism/snapshots/whitequark/parser_bug_507.txt b/test/prism/snapshots/whitequark/parser_bug_507.txt
new file mode 100644
index 0000000000..7e5fc9ee35
--- /dev/null
+++ b/test/prism/snapshots/whitequark/parser_bug_507.txt
@@ -0,0 +1,36 @@
+@ ProgramNode (location: (1,0)-(1,19))
+├── locals: [:m]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,19))
+ └── body: (length: 1)
+ └── @ LocalVariableWriteNode (location: (1,0)-(1,19))
+ ├── name: :m
+ ├── depth: 0
+ ├── name_loc: (1,0)-(1,1) = "m"
+ ├── value:
+ │ @ LambdaNode (location: (1,4)-(1,19))
+ │ ├── locals: [:args]
+ │ ├── operator_loc: (1,4)-(1,6) = "->"
+ │ ├── opening_loc: (1,13)-(1,15) = "do"
+ │ ├── closing_loc: (1,16)-(1,19) = "end"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (1,7)-(1,12))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (1,7)-(1,12))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ RestParameterNode (location: (1,7)-(1,12))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :args
+ │ │ │ │ ├── name_loc: (1,8)-(1,12) = "args"
+ │ │ │ │ └── operator_loc: (1,7)-(1,8) = "*"
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── body: ∅
+ └── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/whitequark/parser_bug_518.txt b/test/prism/snapshots/whitequark/parser_bug_518.txt
new file mode 100644
index 0000000000..b63fbb8284
--- /dev/null
+++ b/test/prism/snapshots/whitequark/parser_bug_518.txt
@@ -0,0 +1,18 @@
+@ ProgramNode (location: (1,0)-(2,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,3))
+ └── body: (length: 1)
+ └── @ ClassNode (location: (1,0)-(2,3))
+ ├── locals: []
+ ├── class_keyword_loc: (1,0)-(1,5) = "class"
+ ├── constant_path:
+ │ @ ConstantReadNode (location: (1,6)-(1,7))
+ │ └── name: :A
+ ├── inheritance_operator_loc: (1,8)-(1,9) = "<"
+ ├── superclass:
+ │ @ ConstantReadNode (location: (1,10)-(1,11))
+ │ └── name: :B
+ ├── body: ∅
+ ├── end_keyword_loc: (2,0)-(2,3) = "end"
+ └── name: :A
diff --git a/test/prism/snapshots/whitequark/parser_bug_525.txt b/test/prism/snapshots/whitequark/parser_bug_525.txt
new file mode 100644
index 0000000000..a69b8a207f
--- /dev/null
+++ b/test/prism/snapshots/whitequark/parser_bug_525.txt
@@ -0,0 +1,65 @@
+@ ProgramNode (location: (1,0)-(1,32))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,32))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,32))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :m1
+ ├── message_loc: (1,0)-(1,2) = "m1"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,3)-(1,11))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ KeywordHashNode (location: (1,3)-(1,11))
+ │ ├── flags: symbol_keys
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (1,3)-(1,11))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (1,3)-(1,5))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,3)-(1,4) = ":"
+ │ │ ├── value_loc: (1,4)-(1,5) = "k"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "k"
+ │ ├── value:
+ │ │ @ CallNode (location: (1,9)-(1,11))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :m2
+ │ │ ├── message_loc: (1,9)-(1,11) = "m2"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (1,6)-(1,8) = "=>"
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,12)-(1,32))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,16)-(1,27))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,16)-(1,27))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m3
+ │ ├── message_loc: (1,16)-(1,18) = "m3"
+ │ ├── opening_loc: (1,18)-(1,19) = "("
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (1,19)-(1,20) = ")"
+ │ └── block:
+ │ @ BlockNode (location: (1,21)-(1,27))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,21)-(1,23) = "do"
+ │ └── closing_loc: (1,24)-(1,27) = "end"
+ ├── opening_loc: (1,12)-(1,14) = "do"
+ └── closing_loc: (1,29)-(1,32) = "end"
diff --git a/test/prism/snapshots/whitequark/parser_bug_604.txt b/test/prism/snapshots/whitequark/parser_bug_604.txt
new file mode 100644
index 0000000000..2577e3bc55
--- /dev/null
+++ b/test/prism/snapshots/whitequark/parser_bug_604.txt
@@ -0,0 +1,57 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,14))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :m
+ ├── message_loc: (1,0)-(1,1) = "m"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,7))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (1,2)-(1,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,2)-(1,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (1,2)-(1,3) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (1,4)-(1,5) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,6)-(1,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (1,6)-(1,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (1,6)-(1,7) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,8)-(1,14))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── opening_loc: (1,8)-(1,10) = "do"
+ └── closing_loc: (1,11)-(1,14) = "end"
diff --git a/test/prism/snapshots/whitequark/parser_bug_640.txt b/test/prism/snapshots/whitequark/parser_bug_640.txt
new file mode 100644
index 0000000000..157576838b
--- /dev/null
+++ b/test/prism/snapshots/whitequark/parser_bug_640.txt
@@ -0,0 +1,22 @@
+@ ProgramNode (location: (1,0)-(1,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,6))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(1,6))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,6) = "<<~FOO"
+ ├── parts: (length: 2)
+ │ ├── @ StringNode (location: (2,0)-(3,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (2,0)-(3,0) = " baz\\\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "baz"
+ │ └── @ StringNode (location: (3,0)-(4,0))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (3,0)-(4,0) = " qux\n"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "qux\n"
+ └── closing_loc: (4,0)-(5,0) = "FOO\n"
diff --git a/test/prism/snapshots/whitequark/parser_bug_645.txt b/test/prism/snapshots/whitequark/parser_bug_645.txt
new file mode 100644
index 0000000000..5700f3c3db
--- /dev/null
+++ b/test/prism/snapshots/whitequark/parser_bug_645.txt
@@ -0,0 +1,35 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ LambdaNode (location: (1,0)-(1,14))
+ ├── locals: [:arg]
+ ├── operator_loc: (1,0)-(1,2) = "->"
+ ├── opening_loc: (1,12)-(1,13) = "{"
+ ├── closing_loc: (1,13)-(1,14) = "}"
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,3)-(1,11))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,4)-(1,10))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 1)
+ │ │ │ └── @ OptionalParameterNode (location: (1,4)-(1,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :arg
+ │ │ │ ├── name_loc: (1,4)-(1,7) = "arg"
+ │ │ │ ├── operator_loc: (1,7)-(1,8) = "="
+ │ │ │ └── value:
+ │ │ │ @ HashNode (location: (1,8)-(1,10))
+ │ │ │ ├── opening_loc: (1,8)-(1,9) = "{"
+ │ │ │ ├── elements: (length: 0)
+ │ │ │ └── closing_loc: (1,9)-(1,10) = "}"
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (1,3)-(1,4) = "("
+ │ └── closing_loc: (1,10)-(1,11) = ")"
+ └── body: ∅
diff --git a/test/prism/snapshots/whitequark/parser_bug_830.txt b/test/prism/snapshots/whitequark/parser_bug_830.txt
new file mode 100644
index 0000000000..e52b291d6a
--- /dev/null
+++ b/test/prism/snapshots/whitequark/parser_bug_830.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ RegularExpressionNode (location: (1,0)-(1,4))
+ ├── flags: forced_us_ascii_encoding
+ ├── opening_loc: (1,0)-(1,1) = "/"
+ ├── content_loc: (1,1)-(1,3) = "\\("
+ ├── closing_loc: (1,3)-(1,4) = "/"
+ └── unescaped: "\\("
diff --git a/test/prism/snapshots/whitequark/parser_bug_989.txt b/test/prism/snapshots/whitequark/parser_bug_989.txt
new file mode 100644
index 0000000000..c241d6127f
--- /dev/null
+++ b/test/prism/snapshots/whitequark/parser_bug_989.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,1)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,1)-(1,8))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,1)-(1,8))
+ ├── flags: ∅
+ ├── opening_loc: (1,1)-(1,8) = "<<-HERE"
+ ├── content_loc: (2,0)-(3,0) = "\t\tcontent\n"
+ ├── closing_loc: (3,0)-(4,0) = "\tHERE\n"
+ └── unescaped: "\t\tcontent\n"
diff --git a/test/prism/snapshots/whitequark/parser_drops_truncated_parts_of_squiggly_heredoc.txt b/test/prism/snapshots/whitequark/parser_drops_truncated_parts_of_squiggly_heredoc.txt
new file mode 100644
index 0000000000..018d7916a6
--- /dev/null
+++ b/test/prism/snapshots/whitequark/parser_drops_truncated_parts_of_squiggly_heredoc.txt
@@ -0,0 +1,20 @@
+@ ProgramNode (location: (1,0)-(1,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,7))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(1,7))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,7) = "<<~HERE"
+ ├── parts: (length: 2)
+ │ ├── @ EmbeddedStatementsNode (location: (2,2)-(2,5))
+ │ │ ├── opening_loc: (2,2)-(2,4) = "\#{"
+ │ │ ├── statements: ∅
+ │ │ └── closing_loc: (2,4)-(2,5) = "}"
+ │ └── @ StringNode (location: (2,5)-(3,0))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (2,5)-(3,0) = "\n"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "\n"
+ └── closing_loc: (3,0)-(4,0) = "HERE\n"
diff --git a/test/prism/snapshots/whitequark/parser_slash_slash_n_escaping_in_literals.txt b/test/prism/snapshots/whitequark/parser_slash_slash_n_escaping_in_literals.txt
new file mode 100644
index 0000000000..080d4d0e7d
--- /dev/null
+++ b/test/prism/snapshots/whitequark/parser_slash_slash_n_escaping_in_literals.txt
@@ -0,0 +1,139 @@
+@ ProgramNode (location: (1,0)-(62,2))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(62,2))
+ └── body: (length: 19)
+ ├── @ StringNode (location: (1,0)-(2,2))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,1) = "\""
+ │ ├── content_loc: (1,1)-(2,1) = "a\\\nb"
+ │ ├── closing_loc: (2,1)-(2,2) = "\""
+ │ └── unescaped: "ab"
+ ├── @ ArrayNode (location: (4,0)-(5,2))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ SymbolNode (location: (4,3)-(5,1))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (4,3)-(5,1) = "a\\\nb"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a\nb"
+ │ ├── opening_loc: (4,0)-(4,3) = "%I{"
+ │ └── closing_loc: (5,1)-(5,2) = "}"
+ ├── @ StringNode (location: (7,0)-(8,2))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (7,0)-(7,3) = "%Q{"
+ │ ├── content_loc: (7,3)-(8,1) = "a\\\nb"
+ │ ├── closing_loc: (8,1)-(8,2) = "}"
+ │ └── unescaped: "ab"
+ ├── @ ArrayNode (location: (10,0)-(11,2))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ StringNode (location: (10,3)-(11,1))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (10,3)-(11,1) = "a\\\nb"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a\nb"
+ │ ├── opening_loc: (10,0)-(10,3) = "%W{"
+ │ └── closing_loc: (11,1)-(11,2) = "}"
+ ├── @ ArrayNode (location: (13,0)-(14,2))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ SymbolNode (location: (13,3)-(14,1))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (13,3)-(14,1) = "a\\\nb"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a\nb"
+ │ ├── opening_loc: (13,0)-(13,3) = "%i{"
+ │ └── closing_loc: (14,1)-(14,2) = "}"
+ ├── @ StringNode (location: (16,0)-(17,2))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (16,0)-(16,3) = "%q{"
+ │ ├── content_loc: (16,3)-(17,1) = "a\\\nb"
+ │ ├── closing_loc: (17,1)-(17,2) = "}"
+ │ └── unescaped: "a\\\nb"
+ ├── @ RegularExpressionNode (location: (19,0)-(20,2))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (19,0)-(19,3) = "%r{"
+ │ ├── content_loc: (19,3)-(20,1) = "a\\\nb"
+ │ ├── closing_loc: (20,1)-(20,2) = "}"
+ │ └── unescaped: "ab"
+ ├── @ SymbolNode (location: (22,0)-(23,2))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (22,0)-(22,3) = "%s{"
+ │ ├── value_loc: (22,3)-(23,1) = "a\\\nb"
+ │ ├── closing_loc: (23,1)-(23,2) = "}"
+ │ └── unescaped: "a\\\nb"
+ ├── @ ArrayNode (location: (25,0)-(26,2))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ StringNode (location: (25,3)-(26,1))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (25,3)-(26,1) = "a\\\nb"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "a\nb"
+ │ ├── opening_loc: (25,0)-(25,3) = "%w{"
+ │ └── closing_loc: (26,1)-(26,2) = "}"
+ ├── @ XStringNode (location: (28,0)-(29,2))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (28,0)-(28,3) = "%x{"
+ │ ├── content_loc: (28,3)-(29,1) = "a\\\nb"
+ │ ├── closing_loc: (29,1)-(29,2) = "}"
+ │ └── unescaped: "ab"
+ ├── @ StringNode (location: (31,0)-(32,2))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (31,0)-(31,2) = "%{"
+ │ ├── content_loc: (31,2)-(32,1) = "a\\\nb"
+ │ ├── closing_loc: (32,1)-(32,2) = "}"
+ │ └── unescaped: "ab"
+ ├── @ StringNode (location: (34,0)-(35,2))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (34,0)-(34,1) = "'"
+ │ ├── content_loc: (34,1)-(35,1) = "a\\\nb"
+ │ ├── closing_loc: (35,1)-(35,2) = "'"
+ │ └── unescaped: "a\\\nb"
+ ├── @ RegularExpressionNode (location: (37,0)-(38,2))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (37,0)-(37,1) = "/"
+ │ ├── content_loc: (37,1)-(38,1) = "a\\\nb"
+ │ ├── closing_loc: (38,1)-(38,2) = "/"
+ │ └── unescaped: "ab"
+ ├── @ SymbolNode (location: (40,0)-(41,2))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (40,0)-(40,2) = ":\""
+ │ ├── value_loc: (40,2)-(41,1) = "a\\\nb"
+ │ ├── closing_loc: (41,1)-(41,2) = "\""
+ │ └── unescaped: "ab"
+ ├── @ SymbolNode (location: (43,0)-(44,2))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (43,0)-(43,2) = ":'"
+ │ ├── value_loc: (43,2)-(44,1) = "a\\\nb"
+ │ ├── closing_loc: (44,1)-(44,2) = "'"
+ │ └── unescaped: "a\\\nb"
+ ├── @ StringNode (location: (46,0)-(46,9))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (46,0)-(46,9) = "<<-\"HERE\""
+ │ ├── content_loc: (47,0)-(49,0) = "a\\\nb\n"
+ │ ├── closing_loc: (49,0)-(50,0) = "HERE\n"
+ │ └── unescaped: "ab\n"
+ ├── @ StringNode (location: (51,0)-(51,9))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (51,0)-(51,9) = "<<-'HERE'"
+ │ ├── content_loc: (52,0)-(54,0) = "a\\\nb\n"
+ │ ├── closing_loc: (54,0)-(55,0) = "HERE\n"
+ │ └── unescaped: "a\\\nb\n"
+ ├── @ XStringNode (location: (56,0)-(56,9))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (56,0)-(56,9) = "<<-`HERE`"
+ │ ├── content_loc: (57,0)-(59,0) = "a\\\nb\n"
+ │ ├── closing_loc: (59,0)-(60,0) = "HERE\n"
+ │ └── unescaped: "ab\n"
+ └── @ XStringNode (location: (61,0)-(62,2))
+ ├── flags: ∅
+ ├── opening_loc: (61,0)-(61,1) = "`"
+ ├── content_loc: (61,1)-(62,1) = "a\\\nb"
+ ├── closing_loc: (62,1)-(62,2) = "`"
+ └── unescaped: "ab"
diff --git a/test/prism/snapshots/whitequark/pattern_matching__FILE__LINE_literals.txt b/test/prism/snapshots/whitequark/pattern_matching__FILE__LINE_literals.txt
new file mode 100644
index 0000000000..6f315780ad
--- /dev/null
+++ b/test/prism/snapshots/whitequark/pattern_matching__FILE__LINE_literals.txt
@@ -0,0 +1,54 @@
+@ ProgramNode (location: (1,8)-(3,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,8)-(3,11))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,8)-(3,11))
+ ├── predicate:
+ │ @ ArrayNode (location: (1,13)-(1,51))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 3)
+ │ │ ├── @ SourceFileNode (location: (1,14)-(1,22))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── filepath: "whitequark/pattern_matching__FILE__LINE_literals.txt"
+ │ │ ├── @ CallNode (location: (1,24)-(1,36))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── receiver:
+ │ │ │ │ @ SourceLineNode (location: (1,24)-(1,32))
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :+
+ │ │ │ ├── message_loc: (1,33)-(1,34) = "+"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (1,35)-(1,36))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (1,35)-(1,36))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── @ SourceEncodingNode (location: (1,38)-(1,50))
+ │ ├── opening_loc: (1,13)-(1,14) = "["
+ │ └── closing_loc: (1,50)-(1,51) = "]"
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (2,10)-(2,47))
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (2,13)-(2,47))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 3)
+ │ │ │ ├── @ SourceFileNode (location: (2,14)-(2,22))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── filepath: "whitequark/pattern_matching__FILE__LINE_literals.txt"
+ │ │ │ ├── @ SourceLineNode (location: (2,24)-(2,32))
+ │ │ │ └── @ SourceEncodingNode (location: (2,34)-(2,46))
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (2,13)-(2,14) = "["
+ │ │ └── closing_loc: (2,46)-(2,47) = "]"
+ │ ├── statements: ∅
+ │ ├── in_loc: (2,10)-(2,12) = "in"
+ │ └── then_loc: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,8)-(1,12) = "case"
+ └── end_keyword_loc: (3,8)-(3,11) = "end"
diff --git a/test/prism/snapshots/whitequark/pattern_matching_blank_else.txt b/test/prism/snapshots/whitequark/pattern_matching_blank_else.txt
new file mode 100644
index 0000000000..6015c000a9
--- /dev/null
+++ b/test/prism/snapshots/whitequark/pattern_matching_blank_else.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (1,0)-(1,26))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,26))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(1,26))
+ ├── predicate:
+ │ @ IntegerNode (location: (1,5)-(1,6))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (1,8)-(1,15))
+ │ ├── pattern:
+ │ │ @ IntegerNode (location: (1,11)-(1,12))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,14)-(1,15))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,14)-(1,15))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ ├── in_loc: (1,8)-(1,10) = "in"
+ │ └── then_loc: ∅
+ ├── consequent:
+ │ @ ElseNode (location: (1,17)-(1,26))
+ │ ├── else_keyword_loc: (1,17)-(1,21) = "else"
+ │ ├── statements: ∅
+ │ └── end_keyword_loc: (1,23)-(1,26) = "end"
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (1,23)-(1,26) = "end"
diff --git a/test/prism/snapshots/whitequark/pattern_matching_else.txt b/test/prism/snapshots/whitequark/pattern_matching_else.txt
new file mode 100644
index 0000000000..7f83aafe99
--- /dev/null
+++ b/test/prism/snapshots/whitequark/pattern_matching_else.txt
@@ -0,0 +1,36 @@
+@ ProgramNode (location: (1,0)-(1,29))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,29))
+ └── body: (length: 1)
+ └── @ CaseMatchNode (location: (1,0)-(1,29))
+ ├── predicate:
+ │ @ IntegerNode (location: (1,5)-(1,6))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── conditions: (length: 1)
+ │ └── @ InNode (location: (1,8)-(1,15))
+ │ ├── pattern:
+ │ │ @ IntegerNode (location: (1,11)-(1,12))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,14)-(1,15))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,14)-(1,15))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ ├── in_loc: (1,8)-(1,10) = "in"
+ │ └── then_loc: ∅
+ ├── consequent:
+ │ @ ElseNode (location: (1,17)-(1,29))
+ │ ├── else_keyword_loc: (1,17)-(1,21) = "else"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,23)-(1,24))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,23)-(1,24))
+ │ │ ├── flags: decimal
+ │ │ └── value: 4
+ │ └── end_keyword_loc: (1,26)-(1,29) = "end"
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (1,26)-(1,29) = "end"
diff --git a/test/prism/snapshots/whitequark/pattern_matching_single_line.txt b/test/prism/snapshots/whitequark/pattern_matching_single_line.txt
new file mode 100644
index 0000000000..2396172dce
--- /dev/null
+++ b/test/prism/snapshots/whitequark/pattern_matching_single_line.txt
@@ -0,0 +1,45 @@
+@ ProgramNode (location: (1,0)-(3,11))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,11))
+ └── body: (length: 4)
+ ├── @ MatchRequiredNode (location: (1,0)-(1,8))
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (1,5)-(1,8))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (1,6)-(1,7))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (1,5)-(1,6) = "["
+ │ │ └── closing_loc: (1,7)-(1,8) = "]"
+ │ └── operator_loc: (1,2)-(1,4) = "=>"
+ ├── @ LocalVariableReadNode (location: (1,10)-(1,11))
+ │ ├── name: :a
+ │ └── depth: 0
+ ├── @ MatchPredicateNode (location: (3,0)-(3,8))
+ │ ├── value:
+ │ │ @ IntegerNode (location: (3,0)-(3,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (3,5)-(3,8))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ LocalVariableTargetNode (location: (3,6)-(3,7))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: (3,5)-(3,6) = "["
+ │ │ └── closing_loc: (3,7)-(3,8) = "]"
+ │ └── operator_loc: (3,2)-(3,4) = "in"
+ └── @ LocalVariableReadNode (location: (3,10)-(3,11))
+ ├── name: :a
+ └── depth: 0
diff --git a/test/prism/snapshots/whitequark/pattern_matching_single_line_allowed_omission_of_parentheses.txt b/test/prism/snapshots/whitequark/pattern_matching_single_line_allowed_omission_of_parentheses.txt
new file mode 100644
index 0000000000..757a7780c2
--- /dev/null
+++ b/test/prism/snapshots/whitequark/pattern_matching_single_line_allowed_omission_of_parentheses.txt
@@ -0,0 +1,249 @@
+@ ProgramNode (location: (1,0)-(11,34))
+├── locals: [:a, :b, :value]
+└── statements:
+ @ StatementsNode (location: (1,0)-(11,34))
+ └── body: (length: 12)
+ ├── @ MatchRequiredNode (location: (1,0)-(1,14))
+ │ ├── value:
+ │ │ @ ArrayNode (location: (1,0)-(1,6))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 2)
+ │ │ │ ├── @ IntegerNode (location: (1,1)-(1,2))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── @ IntegerNode (location: (1,4)-(1,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── opening_loc: (1,0)-(1,1) = "["
+ │ │ └── closing_loc: (1,5)-(1,6) = "]"
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (1,10)-(1,14))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ LocalVariableTargetNode (location: (1,10)-(1,11))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ └── depth: 0
+ │ │ │ └── @ LocalVariableTargetNode (location: (1,13)-(1,14))
+ │ │ │ ├── name: :b
+ │ │ │ └── depth: 0
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (1,7)-(1,9) = "=>"
+ ├── @ LocalVariableReadNode (location: (1,16)-(1,17))
+ │ ├── name: :a
+ │ └── depth: 0
+ ├── @ MatchPredicateNode (location: (3,0)-(3,14))
+ │ ├── value:
+ │ │ @ ArrayNode (location: (3,0)-(3,6))
+ │ │ ├── flags: ∅
+ │ │ ├── elements: (length: 2)
+ │ │ │ ├── @ IntegerNode (location: (3,1)-(3,2))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── @ IntegerNode (location: (3,4)-(3,5))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── opening_loc: (3,0)-(3,1) = "["
+ │ │ └── closing_loc: (3,5)-(3,6) = "]"
+ │ ├── pattern:
+ │ │ @ ArrayPatternNode (location: (3,10)-(3,14))
+ │ │ ├── constant: ∅
+ │ │ ├── requireds: (length: 2)
+ │ │ │ ├── @ LocalVariableTargetNode (location: (3,10)-(3,11))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ └── depth: 0
+ │ │ │ └── @ LocalVariableTargetNode (location: (3,13)-(3,14))
+ │ │ │ ├── name: :b
+ │ │ │ └── depth: 0
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (3,7)-(3,9) = "in"
+ ├── @ LocalVariableReadNode (location: (3,16)-(3,17))
+ │ ├── name: :a
+ │ └── depth: 0
+ ├── @ MatchRequiredNode (location: (5,0)-(5,12))
+ │ ├── value:
+ │ │ @ HashNode (location: (5,0)-(5,6))
+ │ │ ├── opening_loc: (5,0)-(5,1) = "{"
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (5,1)-(5,5))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (5,1)-(5,3))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (5,1)-(5,2) = "a"
+ │ │ │ │ ├── closing_loc: (5,2)-(5,3) = ":"
+ │ │ │ │ └── unescaped: "a"
+ │ │ │ ├── value:
+ │ │ │ │ @ IntegerNode (location: (5,4)-(5,5))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── operator_loc: ∅
+ │ │ └── closing_loc: (5,5)-(5,6) = "}"
+ │ ├── pattern:
+ │ │ @ HashPatternNode (location: (5,10)-(5,12))
+ │ │ ├── constant: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (5,10)-(5,12))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (5,10)-(5,12))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (5,10)-(5,11) = "a"
+ │ │ │ │ ├── closing_loc: (5,11)-(5,12) = ":"
+ │ │ │ │ └── unescaped: "a"
+ │ │ │ ├── value:
+ │ │ │ │ @ ImplicitNode (location: (5,10)-(5,11))
+ │ │ │ │ └── value:
+ │ │ │ │ @ LocalVariableTargetNode (location: (5,10)-(5,11))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ └── depth: 0
+ │ │ │ └── operator_loc: ∅
+ │ │ ├── rest: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (5,7)-(5,9) = "=>"
+ ├── @ LocalVariableReadNode (location: (5,14)-(5,15))
+ │ ├── name: :a
+ │ └── depth: 0
+ ├── @ MatchPredicateNode (location: (7,0)-(7,12))
+ │ ├── value:
+ │ │ @ HashNode (location: (7,0)-(7,6))
+ │ │ ├── opening_loc: (7,0)-(7,1) = "{"
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (7,1)-(7,5))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (7,1)-(7,3))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (7,1)-(7,2) = "a"
+ │ │ │ │ ├── closing_loc: (7,2)-(7,3) = ":"
+ │ │ │ │ └── unescaped: "a"
+ │ │ │ ├── value:
+ │ │ │ │ @ IntegerNode (location: (7,4)-(7,5))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── operator_loc: ∅
+ │ │ └── closing_loc: (7,5)-(7,6) = "}"
+ │ ├── pattern:
+ │ │ @ HashPatternNode (location: (7,10)-(7,12))
+ │ │ ├── constant: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (7,10)-(7,12))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (7,10)-(7,12))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (7,10)-(7,11) = "a"
+ │ │ │ │ ├── closing_loc: (7,11)-(7,12) = ":"
+ │ │ │ │ └── unescaped: "a"
+ │ │ │ ├── value:
+ │ │ │ │ @ ImplicitNode (location: (7,10)-(7,11))
+ │ │ │ │ └── value:
+ │ │ │ │ @ LocalVariableTargetNode (location: (7,10)-(7,11))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ └── depth: 0
+ │ │ │ └── operator_loc: ∅
+ │ │ ├── rest: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (7,7)-(7,9) = "in"
+ ├── @ LocalVariableReadNode (location: (7,14)-(7,15))
+ │ ├── name: :a
+ │ └── depth: 0
+ ├── @ MatchRequiredNode (location: (9,0)-(9,27))
+ │ ├── value:
+ │ │ @ HashNode (location: (9,0)-(9,13))
+ │ │ ├── opening_loc: (9,0)-(9,1) = "{"
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (9,1)-(9,12))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (9,1)-(9,5))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (9,1)-(9,4) = "key"
+ │ │ │ │ ├── closing_loc: (9,4)-(9,5) = ":"
+ │ │ │ │ └── unescaped: "key"
+ │ │ │ ├── value:
+ │ │ │ │ @ SymbolNode (location: (9,6)-(9,12))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (9,6)-(9,7) = ":"
+ │ │ │ │ ├── value_loc: (9,7)-(9,12) = "value"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "value"
+ │ │ │ └── operator_loc: ∅
+ │ │ └── closing_loc: (9,12)-(9,13) = "}"
+ │ ├── pattern:
+ │ │ @ HashPatternNode (location: (9,17)-(9,27))
+ │ │ ├── constant: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (9,17)-(9,27))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (9,17)-(9,21))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (9,17)-(9,20) = "key"
+ │ │ │ │ ├── closing_loc: (9,20)-(9,21) = ":"
+ │ │ │ │ └── unescaped: "key"
+ │ │ │ ├── value:
+ │ │ │ │ @ LocalVariableTargetNode (location: (9,22)-(9,27))
+ │ │ │ │ ├── name: :value
+ │ │ │ │ └── depth: 0
+ │ │ │ └── operator_loc: ∅
+ │ │ ├── rest: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (9,14)-(9,16) = "=>"
+ ├── @ LocalVariableReadNode (location: (9,29)-(9,34))
+ │ ├── name: :value
+ │ └── depth: 0
+ ├── @ MatchPredicateNode (location: (11,0)-(11,27))
+ │ ├── value:
+ │ │ @ HashNode (location: (11,0)-(11,13))
+ │ │ ├── opening_loc: (11,0)-(11,1) = "{"
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (11,1)-(11,12))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (11,1)-(11,5))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (11,1)-(11,4) = "key"
+ │ │ │ │ ├── closing_loc: (11,4)-(11,5) = ":"
+ │ │ │ │ └── unescaped: "key"
+ │ │ │ ├── value:
+ │ │ │ │ @ SymbolNode (location: (11,6)-(11,12))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (11,6)-(11,7) = ":"
+ │ │ │ │ ├── value_loc: (11,7)-(11,12) = "value"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "value"
+ │ │ │ └── operator_loc: ∅
+ │ │ └── closing_loc: (11,12)-(11,13) = "}"
+ │ ├── pattern:
+ │ │ @ HashPatternNode (location: (11,17)-(11,27))
+ │ │ ├── constant: ∅
+ │ │ ├── elements: (length: 1)
+ │ │ │ └── @ AssocNode (location: (11,17)-(11,27))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (11,17)-(11,21))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (11,17)-(11,20) = "key"
+ │ │ │ │ ├── closing_loc: (11,20)-(11,21) = ":"
+ │ │ │ │ └── unescaped: "key"
+ │ │ │ ├── value:
+ │ │ │ │ @ LocalVariableTargetNode (location: (11,22)-(11,27))
+ │ │ │ │ ├── name: :value
+ │ │ │ │ └── depth: 0
+ │ │ │ └── operator_loc: ∅
+ │ │ ├── rest: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── operator_loc: (11,14)-(11,16) = "in"
+ └── @ LocalVariableReadNode (location: (11,29)-(11,34))
+ ├── name: :value
+ └── depth: 0
diff --git a/test/prism/snapshots/whitequark/postexe.txt b/test/prism/snapshots/whitequark/postexe.txt
new file mode 100644
index 0000000000..6f27327063
--- /dev/null
+++ b/test/prism/snapshots/whitequark/postexe.txt
@@ -0,0 +1,15 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ PostExecutionNode (location: (1,0)-(1,9))
+ ├── statements:
+ │ @ StatementsNode (location: (1,6)-(1,7))
+ │ └── body: (length: 1)
+ │ └── @ IntegerNode (location: (1,6)-(1,7))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── keyword_loc: (1,0)-(1,3) = "END"
+ ├── opening_loc: (1,4)-(1,5) = "{"
+ └── closing_loc: (1,8)-(1,9) = "}"
diff --git a/test/prism/snapshots/whitequark/preexe.txt b/test/prism/snapshots/whitequark/preexe.txt
new file mode 100644
index 0000000000..5e4b88d096
--- /dev/null
+++ b/test/prism/snapshots/whitequark/preexe.txt
@@ -0,0 +1,15 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ PreExecutionNode (location: (1,0)-(1,11))
+ ├── statements:
+ │ @ StatementsNode (location: (1,8)-(1,9))
+ │ └── body: (length: 1)
+ │ └── @ IntegerNode (location: (1,8)-(1,9))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── keyword_loc: (1,0)-(1,5) = "BEGIN"
+ ├── opening_loc: (1,6)-(1,7) = "{"
+ └── closing_loc: (1,10)-(1,11) = "}"
diff --git a/test/prism/snapshots/whitequark/procarg0.txt b/test/prism/snapshots/whitequark/procarg0.txt
new file mode 100644
index 0000000000..378c7e5b36
--- /dev/null
+++ b/test/prism/snapshots/whitequark/procarg0.txt
@@ -0,0 +1,78 @@
+@ ProgramNode (location: (1,0)-(3,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,11))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,18))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (1,0)-(1,1) = "m"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,2)-(1,18))
+ │ ├── locals: [:foo, :bar]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (1,4)-(1,16))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (1,5)-(1,15))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ MultiTargetNode (location: (1,5)-(1,15))
+ │ │ │ │ ├── lefts: (length: 2)
+ │ │ │ │ │ ├── @ RequiredParameterNode (location: (1,6)-(1,9))
+ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ └── name: :foo
+ │ │ │ │ │ └── @ RequiredParameterNode (location: (1,11)-(1,14))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :bar
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── rights: (length: 0)
+ │ │ │ │ ├── lparen_loc: (1,5)-(1,6) = "("
+ │ │ │ │ └── rparen_loc: (1,14)-(1,15) = ")"
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (1,4)-(1,5) = "|"
+ │ │ └── closing_loc: (1,15)-(1,16) = "|"
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,2)-(1,3) = "{"
+ │ └── closing_loc: (1,17)-(1,18) = "}"
+ └── @ CallNode (location: (3,0)-(3,11))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :m
+ ├── message_loc: (3,0)-(3,1) = "m"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (3,2)-(3,11))
+ ├── locals: [:foo]
+ ├── parameters:
+ │ @ BlockParametersNode (location: (3,4)-(3,9))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (3,5)-(3,8))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (3,5)-(3,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :foo
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (3,4)-(3,5) = "|"
+ │ └── closing_loc: (3,8)-(3,9) = "|"
+ ├── body: ∅
+ ├── opening_loc: (3,2)-(3,3) = "{"
+ └── closing_loc: (3,10)-(3,11) = "}"
diff --git a/test/prism/snapshots/whitequark/range_exclusive.txt b/test/prism/snapshots/whitequark/range_exclusive.txt
new file mode 100644
index 0000000000..f74077ce67
--- /dev/null
+++ b/test/prism/snapshots/whitequark/range_exclusive.txt
@@ -0,0 +1,16 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ RangeNode (location: (1,0)-(1,5))
+ ├── flags: exclude_end
+ ├── left:
+ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── right:
+ │ @ IntegerNode (location: (1,4)-(1,5))
+ │ ├── flags: decimal
+ │ └── value: 2
+ └── operator_loc: (1,1)-(1,4) = "..."
diff --git a/test/prism/snapshots/whitequark/range_inclusive.txt b/test/prism/snapshots/whitequark/range_inclusive.txt
new file mode 100644
index 0000000000..1836312033
--- /dev/null
+++ b/test/prism/snapshots/whitequark/range_inclusive.txt
@@ -0,0 +1,16 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ RangeNode (location: (1,0)-(1,4))
+ ├── flags: ∅
+ ├── left:
+ │ @ IntegerNode (location: (1,0)-(1,1))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── right:
+ │ @ IntegerNode (location: (1,3)-(1,4))
+ │ ├── flags: decimal
+ │ └── value: 2
+ └── operator_loc: (1,1)-(1,3) = ".."
diff --git a/test/prism/snapshots/whitequark/rational.txt b/test/prism/snapshots/whitequark/rational.txt
new file mode 100644
index 0000000000..90bbd17929
--- /dev/null
+++ b/test/prism/snapshots/whitequark/rational.txt
@@ -0,0 +1,14 @@
+@ ProgramNode (location: (1,0)-(3,3))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,3))
+ └── body: (length: 2)
+ ├── @ RationalNode (location: (1,0)-(1,5))
+ │ └── numeric:
+ │ @ FloatNode (location: (1,0)-(1,4))
+ │ └── value: 42.1
+ └── @ RationalNode (location: (3,0)-(3,3))
+ └── numeric:
+ @ IntegerNode (location: (3,0)-(3,2))
+ ├── flags: decimal
+ └── value: 42
diff --git a/test/prism/snapshots/whitequark/regex_interp.txt b/test/prism/snapshots/whitequark/regex_interp.txt
new file mode 100644
index 0000000000..0a6db4cfdf
--- /dev/null
+++ b/test/prism/snapshots/whitequark/regex_interp.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ InterpolatedRegularExpressionNode (location: (1,0)-(1,14))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,1) = "/"
+ ├── parts: (length: 3)
+ │ ├── @ StringNode (location: (1,1)-(1,4))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (1,1)-(1,4) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ ├── @ EmbeddedStatementsNode (location: (1,4)-(1,10))
+ │ │ ├── opening_loc: (1,4)-(1,6) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,6)-(1,9))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (1,6)-(1,9))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (1,6)-(1,9) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── closing_loc: (1,9)-(1,10) = "}"
+ │ └── @ StringNode (location: (1,10)-(1,13))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (1,10)-(1,13) = "baz"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "baz"
+ └── closing_loc: (1,13)-(1,14) = "/"
diff --git a/test/prism/snapshots/whitequark/regex_plain.txt b/test/prism/snapshots/whitequark/regex_plain.txt
new file mode 100644
index 0000000000..df771f7a21
--- /dev/null
+++ b/test/prism/snapshots/whitequark/regex_plain.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ RegularExpressionNode (location: (1,0)-(1,10))
+ ├── flags: ignore_case, multi_line, forced_us_ascii_encoding
+ ├── opening_loc: (1,0)-(1,1) = "/"
+ ├── content_loc: (1,1)-(1,7) = "source"
+ ├── closing_loc: (1,7)-(1,10) = "/im"
+ └── unescaped: "source"
diff --git a/test/prism/snapshots/whitequark/resbody_list.txt b/test/prism/snapshots/whitequark/resbody_list.txt
new file mode 100644
index 0000000000..52fcfd02e0
--- /dev/null
+++ b/test/prism/snapshots/whitequark/resbody_list.txt
@@ -0,0 +1,45 @@
+@ ProgramNode (location: (1,0)-(1,39))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,39))
+ └── body: (length: 1)
+ └── @ BeginNode (location: (1,0)-(1,39))
+ ├── begin_keyword_loc: (1,0)-(1,5) = "begin"
+ ├── statements:
+ │ @ StatementsNode (location: (1,7)-(1,11))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,7)-(1,11))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :meth
+ │ ├── message_loc: (1,7)-(1,11) = "meth"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── rescue_clause:
+ │ @ RescueNode (location: (1,13)-(1,34))
+ │ ├── keyword_loc: (1,13)-(1,19) = "rescue"
+ │ ├── exceptions: (length: 1)
+ │ │ └── @ ConstantReadNode (location: (1,20)-(1,29))
+ │ │ └── name: :Exception
+ │ ├── operator_loc: ∅
+ │ ├── reference: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,31)-(1,34))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,31)-(1,34))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,31)-(1,34) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── consequent: ∅
+ ├── else_clause: ∅
+ ├── ensure_clause: ∅
+ └── end_keyword_loc: (1,36)-(1,39) = "end"
diff --git a/test/prism/snapshots/whitequark/resbody_list_mrhs.txt b/test/prism/snapshots/whitequark/resbody_list_mrhs.txt
new file mode 100644
index 0000000000..d48ddb120d
--- /dev/null
+++ b/test/prism/snapshots/whitequark/resbody_list_mrhs.txt
@@ -0,0 +1,55 @@
+@ ProgramNode (location: (1,0)-(1,44))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,44))
+ └── body: (length: 1)
+ └── @ BeginNode (location: (1,0)-(1,44))
+ ├── begin_keyword_loc: (1,0)-(1,5) = "begin"
+ ├── statements:
+ │ @ StatementsNode (location: (1,7)-(1,11))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,7)-(1,11))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :meth
+ │ ├── message_loc: (1,7)-(1,11) = "meth"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── rescue_clause:
+ │ @ RescueNode (location: (1,13)-(1,39))
+ │ ├── keyword_loc: (1,13)-(1,19) = "rescue"
+ │ ├── exceptions: (length: 2)
+ │ │ ├── @ ConstantReadNode (location: (1,20)-(1,29))
+ │ │ │ └── name: :Exception
+ │ │ └── @ CallNode (location: (1,31)-(1,34))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,31)-(1,34) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── operator_loc: ∅
+ │ ├── reference: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,36)-(1,39))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,36)-(1,39))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,36)-(1,39) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── consequent: ∅
+ ├── else_clause: ∅
+ ├── ensure_clause: ∅
+ └── end_keyword_loc: (1,41)-(1,44) = "end"
diff --git a/test/prism/snapshots/whitequark/resbody_list_var.txt b/test/prism/snapshots/whitequark/resbody_list_var.txt
new file mode 100644
index 0000000000..85efb1a3de
--- /dev/null
+++ b/test/prism/snapshots/whitequark/resbody_list_var.txt
@@ -0,0 +1,56 @@
+@ ProgramNode (location: (1,0)-(1,39))
+├── locals: [:ex]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,39))
+ └── body: (length: 1)
+ └── @ BeginNode (location: (1,0)-(1,39))
+ ├── begin_keyword_loc: (1,0)-(1,5) = "begin"
+ ├── statements:
+ │ @ StatementsNode (location: (1,7)-(1,11))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,7)-(1,11))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :meth
+ │ ├── message_loc: (1,7)-(1,11) = "meth"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── rescue_clause:
+ │ @ RescueNode (location: (1,13)-(1,34))
+ │ ├── keyword_loc: (1,13)-(1,19) = "rescue"
+ │ ├── exceptions: (length: 1)
+ │ │ └── @ CallNode (location: (1,20)-(1,23))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,20)-(1,23) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── operator_loc: (1,24)-(1,26) = "=>"
+ │ ├── reference:
+ │ │ @ LocalVariableTargetNode (location: (1,27)-(1,29))
+ │ │ ├── name: :ex
+ │ │ └── depth: 0
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,31)-(1,34))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,31)-(1,34))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,31)-(1,34) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── consequent: ∅
+ ├── else_clause: ∅
+ ├── ensure_clause: ∅
+ └── end_keyword_loc: (1,36)-(1,39) = "end"
diff --git a/test/prism/snapshots/whitequark/resbody_var.txt b/test/prism/snapshots/whitequark/resbody_var.txt
new file mode 100644
index 0000000000..e12d921c75
--- /dev/null
+++ b/test/prism/snapshots/whitequark/resbody_var.txt
@@ -0,0 +1,86 @@
+@ ProgramNode (location: (1,0)-(3,35))
+├── locals: [:ex]
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,35))
+ └── body: (length: 2)
+ ├── @ BeginNode (location: (1,0)-(1,36))
+ │ ├── begin_keyword_loc: (1,0)-(1,5) = "begin"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,7)-(1,11))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,7)-(1,11))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :meth
+ │ │ ├── message_loc: (1,7)-(1,11) = "meth"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (1,13)-(1,31))
+ │ │ ├── keyword_loc: (1,13)-(1,19) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: (1,20)-(1,22) = "=>"
+ │ │ ├── reference:
+ │ │ │ @ InstanceVariableTargetNode (location: (1,23)-(1,26))
+ │ │ │ └── name: :@ex
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,28)-(1,31))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (1,28)-(1,31))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (1,28)-(1,31) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (1,33)-(1,36) = "end"
+ └── @ BeginNode (location: (3,0)-(3,35))
+ ├── begin_keyword_loc: (3,0)-(3,5) = "begin"
+ ├── statements:
+ │ @ StatementsNode (location: (3,7)-(3,11))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (3,7)-(3,11))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :meth
+ │ ├── message_loc: (3,7)-(3,11) = "meth"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── rescue_clause:
+ │ @ RescueNode (location: (3,13)-(3,30))
+ │ ├── keyword_loc: (3,13)-(3,19) = "rescue"
+ │ ├── exceptions: (length: 0)
+ │ ├── operator_loc: (3,20)-(3,22) = "=>"
+ │ ├── reference:
+ │ │ @ LocalVariableTargetNode (location: (3,23)-(3,25))
+ │ │ ├── name: :ex
+ │ │ └── depth: 0
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,27)-(3,30))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (3,27)-(3,30))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (3,27)-(3,30) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── consequent: ∅
+ ├── else_clause: ∅
+ ├── ensure_clause: ∅
+ └── end_keyword_loc: (3,32)-(3,35) = "end"
diff --git a/test/prism/snapshots/whitequark/rescue.txt b/test/prism/snapshots/whitequark/rescue.txt
new file mode 100644
index 0000000000..28c4f11e67
--- /dev/null
+++ b/test/prism/snapshots/whitequark/rescue.txt
@@ -0,0 +1,43 @@
+@ ProgramNode (location: (1,0)-(1,29))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,29))
+ └── body: (length: 1)
+ └── @ BeginNode (location: (1,0)-(1,29))
+ ├── begin_keyword_loc: (1,0)-(1,5) = "begin"
+ ├── statements:
+ │ @ StatementsNode (location: (1,7)-(1,11))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,7)-(1,11))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :meth
+ │ ├── message_loc: (1,7)-(1,11) = "meth"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── rescue_clause:
+ │ @ RescueNode (location: (1,13)-(1,24))
+ │ ├── keyword_loc: (1,13)-(1,19) = "rescue"
+ │ ├── exceptions: (length: 0)
+ │ ├── operator_loc: ∅
+ │ ├── reference: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,21)-(1,24))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,21)-(1,24))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,21)-(1,24) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── consequent: ∅
+ ├── else_clause: ∅
+ ├── ensure_clause: ∅
+ └── end_keyword_loc: (1,26)-(1,29) = "end"
diff --git a/test/prism/snapshots/whitequark/rescue_else.txt b/test/prism/snapshots/whitequark/rescue_else.txt
new file mode 100644
index 0000000000..36b01a7ef6
--- /dev/null
+++ b/test/prism/snapshots/whitequark/rescue_else.txt
@@ -0,0 +1,59 @@
+@ ProgramNode (location: (1,0)-(1,40))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,40))
+ └── body: (length: 1)
+ └── @ BeginNode (location: (1,0)-(1,40))
+ ├── begin_keyword_loc: (1,0)-(1,5) = "begin"
+ ├── statements:
+ │ @ StatementsNode (location: (1,7)-(1,11))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,7)-(1,11))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :meth
+ │ ├── message_loc: (1,7)-(1,11) = "meth"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── rescue_clause:
+ │ @ RescueNode (location: (1,13)-(1,24))
+ │ ├── keyword_loc: (1,13)-(1,19) = "rescue"
+ │ ├── exceptions: (length: 0)
+ │ ├── operator_loc: ∅
+ │ ├── reference: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,21)-(1,24))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,21)-(1,24))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,21)-(1,24) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── consequent: ∅
+ ├── else_clause:
+ │ @ ElseNode (location: (1,26)-(1,40))
+ │ ├── else_keyword_loc: (1,26)-(1,30) = "else"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,32)-(1,35))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,32)-(1,35))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,32)-(1,35) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── end_keyword_loc: (1,37)-(1,40) = "end"
+ ├── ensure_clause: ∅
+ └── end_keyword_loc: (1,37)-(1,40) = "end"
diff --git a/test/prism/snapshots/whitequark/rescue_else_ensure.txt b/test/prism/snapshots/whitequark/rescue_else_ensure.txt
new file mode 100644
index 0000000000..d97821931b
--- /dev/null
+++ b/test/prism/snapshots/whitequark/rescue_else_ensure.txt
@@ -0,0 +1,75 @@
+@ ProgramNode (location: (1,0)-(1,51))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,51))
+ └── body: (length: 1)
+ └── @ BeginNode (location: (1,0)-(1,51))
+ ├── begin_keyword_loc: (1,0)-(1,5) = "begin"
+ ├── statements:
+ │ @ StatementsNode (location: (1,7)-(1,11))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,7)-(1,11))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :meth
+ │ ├── message_loc: (1,7)-(1,11) = "meth"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── rescue_clause:
+ │ @ RescueNode (location: (1,13)-(1,24))
+ │ ├── keyword_loc: (1,13)-(1,19) = "rescue"
+ │ ├── exceptions: (length: 0)
+ │ ├── operator_loc: ∅
+ │ ├── reference: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,21)-(1,24))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,21)-(1,24))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (1,21)-(1,24) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── consequent: ∅
+ ├── else_clause:
+ │ @ ElseNode (location: (1,26)-(1,42))
+ │ ├── else_keyword_loc: (1,26)-(1,30) = "else"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,31)-(1,34))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,31)-(1,34))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,31)-(1,34) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── end_keyword_loc: (1,36)-(1,42) = "ensure"
+ ├── ensure_clause:
+ │ @ EnsureNode (location: (1,36)-(1,51))
+ │ ├── ensure_keyword_loc: (1,36)-(1,42) = "ensure"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,44)-(1,47))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,44)-(1,47))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,44)-(1,47) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── end_keyword_loc: (1,48)-(1,51) = "end"
+ └── end_keyword_loc: (1,48)-(1,51) = "end"
diff --git a/test/prism/snapshots/whitequark/rescue_ensure.txt b/test/prism/snapshots/whitequark/rescue_ensure.txt
new file mode 100644
index 0000000000..f6cddbcc5e
--- /dev/null
+++ b/test/prism/snapshots/whitequark/rescue_ensure.txt
@@ -0,0 +1,59 @@
+@ ProgramNode (location: (1,0)-(1,42))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,42))
+ └── body: (length: 1)
+ └── @ BeginNode (location: (1,0)-(1,42))
+ ├── begin_keyword_loc: (1,0)-(1,5) = "begin"
+ ├── statements:
+ │ @ StatementsNode (location: (1,7)-(1,11))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,7)-(1,11))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :meth
+ │ ├── message_loc: (1,7)-(1,11) = "meth"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── rescue_clause:
+ │ @ RescueNode (location: (1,13)-(1,24))
+ │ ├── keyword_loc: (1,13)-(1,19) = "rescue"
+ │ ├── exceptions: (length: 0)
+ │ ├── operator_loc: ∅
+ │ ├── reference: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,21)-(1,24))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,21)-(1,24))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (1,21)-(1,24) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── consequent: ∅
+ ├── else_clause: ∅
+ ├── ensure_clause:
+ │ @ EnsureNode (location: (1,26)-(1,42))
+ │ ├── ensure_keyword_loc: (1,26)-(1,32) = "ensure"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,34)-(1,37))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,34)-(1,37))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,34)-(1,37) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── end_keyword_loc: (1,39)-(1,42) = "end"
+ └── end_keyword_loc: (1,39)-(1,42) = "end"
diff --git a/test/prism/snapshots/whitequark/rescue_in_lambda_block.txt b/test/prism/snapshots/whitequark/rescue_in_lambda_block.txt
new file mode 100644
index 0000000000..2ab854cdd7
--- /dev/null
+++ b/test/prism/snapshots/whitequark/rescue_in_lambda_block.txt
@@ -0,0 +1,26 @@
+@ ProgramNode (location: (1,0)-(1,17))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,17))
+ └── body: (length: 1)
+ └── @ LambdaNode (location: (1,0)-(1,17))
+ ├── locals: []
+ ├── operator_loc: (1,0)-(1,2) = "->"
+ ├── opening_loc: (1,3)-(1,5) = "do"
+ ├── closing_loc: (1,14)-(1,17) = "end"
+ ├── parameters: ∅
+ └── body:
+ @ BeginNode (location: (1,3)-(1,17))
+ ├── begin_keyword_loc: ∅
+ ├── statements: ∅
+ ├── rescue_clause:
+ │ @ RescueNode (location: (1,6)-(1,12))
+ │ ├── keyword_loc: (1,6)-(1,12) = "rescue"
+ │ ├── exceptions: (length: 0)
+ │ ├── operator_loc: ∅
+ │ ├── reference: ∅
+ │ ├── statements: ∅
+ │ └── consequent: ∅
+ ├── else_clause: ∅
+ ├── ensure_clause: ∅
+ └── end_keyword_loc: (1,14)-(1,17) = "end"
diff --git a/test/prism/snapshots/whitequark/rescue_mod.txt b/test/prism/snapshots/whitequark/rescue_mod.txt
new file mode 100644
index 0000000000..cd4f0fff45
--- /dev/null
+++ b/test/prism/snapshots/whitequark/rescue_mod.txt
@@ -0,0 +1,29 @@
+@ ProgramNode (location: (1,0)-(1,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,15))
+ └── body: (length: 1)
+ └── @ RescueModifierNode (location: (1,0)-(1,15))
+ ├── expression:
+ │ @ CallNode (location: (1,0)-(1,4))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :meth
+ │ ├── message_loc: (1,0)-(1,4) = "meth"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── keyword_loc: (1,5)-(1,11) = "rescue"
+ └── rescue_expression:
+ @ CallNode (location: (1,12)-(1,15))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :bar
+ ├── message_loc: (1,12)-(1,15) = "bar"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/rescue_mod_asgn.txt b/test/prism/snapshots/whitequark/rescue_mod_asgn.txt
new file mode 100644
index 0000000000..ee094138e5
--- /dev/null
+++ b/test/prism/snapshots/whitequark/rescue_mod_asgn.txt
@@ -0,0 +1,35 @@
+@ ProgramNode (location: (1,0)-(1,21))
+├── locals: [:foo]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,21))
+ └── body: (length: 1)
+ └── @ LocalVariableWriteNode (location: (1,0)-(1,21))
+ ├── name: :foo
+ ├── depth: 0
+ ├── name_loc: (1,0)-(1,3) = "foo"
+ ├── value:
+ │ @ RescueModifierNode (location: (1,6)-(1,21))
+ │ ├── expression:
+ │ │ @ CallNode (location: (1,6)-(1,10))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :meth
+ │ │ ├── message_loc: (1,6)-(1,10) = "meth"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (1,11)-(1,17) = "rescue"
+ │ └── rescue_expression:
+ │ @ CallNode (location: (1,18)-(1,21))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (1,18)-(1,21) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── operator_loc: (1,4)-(1,5) = "="
diff --git a/test/prism/snapshots/whitequark/rescue_mod_masgn.txt b/test/prism/snapshots/whitequark/rescue_mod_masgn.txt
new file mode 100644
index 0000000000..dbe289702f
--- /dev/null
+++ b/test/prism/snapshots/whitequark/rescue_mod_masgn.txt
@@ -0,0 +1,44 @@
+@ ProgramNode (location: (1,0)-(1,29))
+├── locals: [:foo, :bar]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,29))
+ └── body: (length: 1)
+ └── @ MultiWriteNode (location: (1,0)-(1,29))
+ ├── lefts: (length: 2)
+ │ ├── @ LocalVariableTargetNode (location: (1,0)-(1,3))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ └── @ LocalVariableTargetNode (location: (1,5)-(1,8))
+ │ ├── name: :bar
+ │ └── depth: 0
+ ├── rest: ∅
+ ├── rights: (length: 0)
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── operator_loc: (1,9)-(1,10) = "="
+ └── value:
+ @ RescueModifierNode (location: (1,11)-(1,29))
+ ├── expression:
+ │ @ CallNode (location: (1,11)-(1,15))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :meth
+ │ ├── message_loc: (1,11)-(1,15) = "meth"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── keyword_loc: (1,16)-(1,22) = "rescue"
+ └── rescue_expression:
+ @ ArrayNode (location: (1,23)-(1,29))
+ ├── flags: ∅
+ ├── elements: (length: 2)
+ │ ├── @ IntegerNode (location: (1,24)-(1,25))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── @ IntegerNode (location: (1,27)-(1,28))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── opening_loc: (1,23)-(1,24) = "["
+ └── closing_loc: (1,28)-(1,29) = "]"
diff --git a/test/prism/snapshots/whitequark/rescue_mod_op_assign.txt b/test/prism/snapshots/whitequark/rescue_mod_op_assign.txt
new file mode 100644
index 0000000000..b269104f30
--- /dev/null
+++ b/test/prism/snapshots/whitequark/rescue_mod_op_assign.txt
@@ -0,0 +1,36 @@
+@ ProgramNode (location: (1,0)-(1,22))
+├── locals: [:foo]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,22))
+ └── body: (length: 1)
+ └── @ LocalVariableOperatorWriteNode (location: (1,0)-(1,22))
+ ├── name_loc: (1,0)-(1,3) = "foo"
+ ├── operator_loc: (1,4)-(1,6) = "+="
+ ├── value:
+ │ @ RescueModifierNode (location: (1,7)-(1,22))
+ │ ├── expression:
+ │ │ @ CallNode (location: (1,7)-(1,11))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :meth
+ │ │ ├── message_loc: (1,7)-(1,11) = "meth"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (1,12)-(1,18) = "rescue"
+ │ └── rescue_expression:
+ │ @ CallNode (location: (1,19)-(1,22))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (1,19)-(1,22) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── name: :foo
+ ├── operator: :+
+ └── depth: 0
diff --git a/test/prism/snapshots/whitequark/rescue_without_begin_end.txt b/test/prism/snapshots/whitequark/rescue_without_begin_end.txt
new file mode 100644
index 0000000000..4281442ab2
--- /dev/null
+++ b/test/prism/snapshots/whitequark/rescue_without_begin_end.txt
@@ -0,0 +1,59 @@
+@ ProgramNode (location: (1,0)-(1,30))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,30))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,30))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :meth
+ ├── message_loc: (1,0)-(1,4) = "meth"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,5)-(1,30))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body:
+ │ @ BeginNode (location: (1,5)-(1,30))
+ │ ├── begin_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,9)-(1,12))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,9)-(1,12))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,9)-(1,12) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (1,14)-(1,25))
+ │ │ ├── keyword_loc: (1,14)-(1,20) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,22)-(1,25))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (1,22)-(1,25))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (1,22)-(1,25) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (1,27)-(1,30) = "end"
+ ├── opening_loc: (1,5)-(1,7) = "do"
+ └── closing_loc: (1,27)-(1,30) = "end"
diff --git a/test/prism/snapshots/whitequark/restarg_named.txt b/test/prism/snapshots/whitequark/restarg_named.txt
new file mode 100644
index 0000000000..fab9dd9a79
--- /dev/null
+++ b/test/prism/snapshots/whitequark/restarg_named.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (1,0)-(1,16))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,16))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,16))
+ ├── name: :f
+ ├── name_loc: (1,4)-(1,5) = "f"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,10))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest:
+ │ │ @ RestParameterNode (location: (1,6)-(1,10))
+ │ │ ├── flags: ∅
+ │ │ ├── name: :foo
+ │ │ ├── name_loc: (1,7)-(1,10) = "foo"
+ │ │ └── operator_loc: (1,6)-(1,7) = "*"
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: [:foo]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,5)-(1,6) = "("
+ ├── rparen_loc: (1,10)-(1,11) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,13)-(1,16) = "end"
diff --git a/test/prism/snapshots/whitequark/restarg_unnamed.txt b/test/prism/snapshots/whitequark/restarg_unnamed.txt
new file mode 100644
index 0000000000..077230f7ba
--- /dev/null
+++ b/test/prism/snapshots/whitequark/restarg_unnamed.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,13))
+ ├── name: :f
+ ├── name_loc: (1,4)-(1,5) = "f"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,6)-(1,7))
+ │ ├── requireds: (length: 0)
+ │ ├── optionals: (length: 0)
+ │ ├── rest:
+ │ │ @ RestParameterNode (location: (1,6)-(1,7))
+ │ │ ├── flags: ∅
+ │ │ ├── name: ∅
+ │ │ ├── name_loc: ∅
+ │ │ └── operator_loc: (1,6)-(1,7) = "*"
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body: ∅
+ ├── locals: []
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,5)-(1,6) = "("
+ ├── rparen_loc: (1,7)-(1,8) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,10)-(1,13) = "end"
diff --git a/test/prism/snapshots/whitequark/return.txt b/test/prism/snapshots/whitequark/return.txt
new file mode 100644
index 0000000000..5c98259103
--- /dev/null
+++ b/test/prism/snapshots/whitequark/return.txt
@@ -0,0 +1,60 @@
+@ ProgramNode (location: (1,0)-(7,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(7,11))
+ └── body: (length: 4)
+ ├── @ ReturnNode (location: (1,0)-(1,6))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (1,0)-(1,6) = "return"
+ │ └── arguments: ∅
+ ├── @ ReturnNode (location: (3,0)-(3,10))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (3,0)-(3,6) = "return"
+ │ └── arguments:
+ │ @ ArgumentsNode (location: (3,7)-(3,10))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (3,7)-(3,10))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (3,7)-(3,10) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ ReturnNode (location: (5,0)-(5,8))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (5,0)-(5,6) = "return"
+ │ └── arguments:
+ │ @ ArgumentsNode (location: (5,6)-(5,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ ParenthesesNode (location: (5,6)-(5,8))
+ │ ├── body: ∅
+ │ ├── opening_loc: (5,6)-(5,7) = "("
+ │ └── closing_loc: (5,7)-(5,8) = ")"
+ └── @ ReturnNode (location: (7,0)-(7,11))
+ ├── flags: ∅
+ ├── keyword_loc: (7,0)-(7,6) = "return"
+ └── arguments:
+ @ ArgumentsNode (location: (7,6)-(7,11))
+ ├── flags: ∅
+ └── arguments: (length: 1)
+ └── @ ParenthesesNode (location: (7,6)-(7,11))
+ ├── body:
+ │ @ StatementsNode (location: (7,7)-(7,10))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (7,7)-(7,10))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (7,7)-(7,10) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── opening_loc: (7,6)-(7,7) = "("
+ └── closing_loc: (7,10)-(7,11) = ")"
diff --git a/test/prism/snapshots/whitequark/return_block.txt b/test/prism/snapshots/whitequark/return_block.txt
new file mode 100644
index 0000000000..36746a361b
--- /dev/null
+++ b/test/prism/snapshots/whitequark/return_block.txt
@@ -0,0 +1,41 @@
+@ ProgramNode (location: (1,0)-(1,21))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,21))
+ └── body: (length: 1)
+ └── @ ReturnNode (location: (1,0)-(1,21))
+ ├── flags: ∅
+ ├── keyword_loc: (1,0)-(1,6) = "return"
+ └── arguments:
+ @ ArgumentsNode (location: (1,7)-(1,21))
+ ├── flags: ∅
+ └── arguments: (length: 1)
+ └── @ CallNode (location: (1,7)-(1,21))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :fun
+ ├── message_loc: (1,7)-(1,10) = "fun"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,11)-(1,14))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (1,11)-(1,14))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,11)-(1,14) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,15)-(1,21))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── opening_loc: (1,15)-(1,17) = "do"
+ └── closing_loc: (1,18)-(1,21) = "end"
diff --git a/test/prism/snapshots/whitequark/ruby_bug_10279.txt b/test/prism/snapshots/whitequark/ruby_bug_10279.txt
new file mode 100644
index 0000000000..66684350a4
--- /dev/null
+++ b/test/prism/snapshots/whitequark/ruby_bug_10279.txt
@@ -0,0 +1,32 @@
+@ ProgramNode (location: (1,0)-(1,24))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,24))
+ └── body: (length: 1)
+ └── @ HashNode (location: (1,0)-(1,24))
+ ├── opening_loc: (1,0)-(1,1) = "{"
+ ├── elements: (length: 1)
+ │ └── @ AssocNode (location: (1,1)-(1,23))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (1,1)-(1,3))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (1,1)-(1,2) = "a"
+ │ │ ├── closing_loc: (1,2)-(1,3) = ":"
+ │ │ └── unescaped: "a"
+ │ ├── value:
+ │ │ @ IfNode (location: (1,4)-(1,23))
+ │ │ ├── if_keyword_loc: (1,4)-(1,6) = "if"
+ │ │ ├── predicate:
+ │ │ │ @ TrueNode (location: (1,7)-(1,11))
+ │ │ ├── then_keyword_loc: (1,12)-(1,16) = "then"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,17)-(1,19))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (1,17)-(1,19))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ ├── consequent: ∅
+ │ │ └── end_keyword_loc: (1,20)-(1,23) = "end"
+ │ └── operator_loc: ∅
+ └── closing_loc: (1,23)-(1,24) = "}"
diff --git a/test/prism/snapshots/whitequark/ruby_bug_10653.txt b/test/prism/snapshots/whitequark/ruby_bug_10653.txt
new file mode 100644
index 0000000000..400a8c2861
--- /dev/null
+++ b/test/prism/snapshots/whitequark/ruby_bug_10653.txt
@@ -0,0 +1,173 @@
+@ ProgramNode (location: (1,0)-(5,31))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,31))
+ └── body: (length: 3)
+ ├── @ IfNode (location: (1,0)-(1,33))
+ │ ├── if_keyword_loc: ∅
+ │ ├── predicate:
+ │ │ @ FalseNode (location: (1,0)-(1,5))
+ │ ├── then_keyword_loc: (1,6)-(1,7) = "?"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,8)-(1,20))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,8)-(1,20))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :raise
+ │ │ ├── message_loc: (1,8)-(1,13) = "raise"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (1,14)-(1,20))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (1,14)-(1,16) = "do"
+ │ │ └── closing_loc: (1,17)-(1,20) = "end"
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (1,21)-(1,33))
+ │ │ ├── else_keyword_loc: (1,21)-(1,22) = ":"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,23)-(1,33))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (1,23)-(1,33))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :tap
+ │ │ │ ├── message_loc: (1,23)-(1,26) = "tap"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (1,27)-(1,33))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body: ∅
+ │ │ │ ├── opening_loc: (1,27)-(1,29) = "do"
+ │ │ │ └── closing_loc: (1,30)-(1,33) = "end"
+ │ │ └── end_keyword_loc: ∅
+ │ └── end_keyword_loc: ∅
+ ├── @ IfNode (location: (3,0)-(3,25))
+ │ ├── if_keyword_loc: ∅
+ │ ├── predicate:
+ │ │ @ FalseNode (location: (3,0)-(3,5))
+ │ ├── then_keyword_loc: (3,6)-(3,7) = "?"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,8)-(3,16))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (3,8)-(3,16))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :raise
+ │ │ ├── message_loc: (3,8)-(3,13) = "raise"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (3,14)-(3,16))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (3,14)-(3,15) = "{"
+ │ │ └── closing_loc: (3,15)-(3,16) = "}"
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (3,17)-(3,25))
+ │ │ ├── else_keyword_loc: (3,17)-(3,18) = ":"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (3,19)-(3,25))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (3,19)-(3,25))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :tap
+ │ │ │ ├── message_loc: (3,19)-(3,22) = "tap"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (3,23)-(3,25))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body: ∅
+ │ │ │ ├── opening_loc: (3,23)-(3,24) = "{"
+ │ │ │ └── closing_loc: (3,24)-(3,25) = "}"
+ │ │ └── end_keyword_loc: ∅
+ │ └── end_keyword_loc: ∅
+ └── @ IfNode (location: (5,0)-(5,31))
+ ├── if_keyword_loc: ∅
+ ├── predicate:
+ │ @ TrueNode (location: (5,0)-(5,4))
+ ├── then_keyword_loc: (5,5)-(5,6) = "?"
+ ├── statements:
+ │ @ StatementsNode (location: (5,7)-(5,27))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (5,7)-(5,27))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ IntegerNode (location: (5,7)-(5,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── call_operator_loc: (5,8)-(5,9) = "."
+ │ ├── name: :tap
+ │ ├── message_loc: (5,9)-(5,12) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (5,13)-(5,27))
+ │ ├── locals: [:n]
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (5,16)-(5,19))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (5,17)-(5,18))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (5,17)-(5,18))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :n
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (5,16)-(5,17) = "|"
+ │ │ └── closing_loc: (5,18)-(5,19) = "|"
+ │ ├── body:
+ │ │ @ StatementsNode (location: (5,20)-(5,23))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (5,20)-(5,23))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :p
+ │ │ ├── message_loc: (5,20)-(5,21) = "p"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (5,22)-(5,23))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ LocalVariableReadNode (location: (5,22)-(5,23))
+ │ │ │ ├── name: :n
+ │ │ │ └── depth: 0
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (5,13)-(5,15) = "do"
+ │ └── closing_loc: (5,24)-(5,27) = "end"
+ ├── consequent:
+ │ @ ElseNode (location: (5,28)-(5,31))
+ │ ├── else_keyword_loc: (5,28)-(5,29) = ":"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (5,30)-(5,31))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (5,30)-(5,31))
+ │ │ ├── flags: decimal
+ │ │ └── value: 0
+ │ └── end_keyword_loc: ∅
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/whitequark/ruby_bug_11107.txt b/test/prism/snapshots/whitequark/ruby_bug_11107.txt
new file mode 100644
index 0000000000..36ece50d88
--- /dev/null
+++ b/test/prism/snapshots/whitequark/ruby_bug_11107.txt
@@ -0,0 +1,48 @@
+@ ProgramNode (location: (1,0)-(1,24))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,24))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,24))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :p
+ ├── message_loc: (1,0)-(1,1) = "p"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,24))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ LambdaNode (location: (1,2)-(1,24))
+ │ ├── locals: []
+ │ ├── operator_loc: (1,2)-(1,4) = "->"
+ │ ├── opening_loc: (1,7)-(1,9) = "do"
+ │ ├── closing_loc: (1,21)-(1,24) = "end"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (1,4)-(1,6))
+ │ │ ├── parameters: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (1,4)-(1,5) = "("
+ │ │ └── closing_loc: (1,5)-(1,6) = ")"
+ │ └── body:
+ │ @ StatementsNode (location: (1,10)-(1,20))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,10)-(1,20))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,10)-(1,11) = "a"
+ │ ├── opening_loc: (1,11)-(1,12) = "("
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (1,12)-(1,13) = ")"
+ │ └── block:
+ │ @ BlockNode (location: (1,14)-(1,20))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,14)-(1,16) = "do"
+ │ └── closing_loc: (1,17)-(1,20) = "end"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/ruby_bug_11380.txt b/test/prism/snapshots/whitequark/ruby_bug_11380.txt
new file mode 100644
index 0000000000..b7a7ef9a98
--- /dev/null
+++ b/test/prism/snapshots/whitequark/ruby_bug_11380.txt
@@ -0,0 +1,55 @@
+@ ProgramNode (location: (1,0)-(1,28))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,28))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,28))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :p
+ ├── message_loc: (1,0)-(1,1) = "p"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,21))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ LambdaNode (location: (1,2)-(1,15))
+ │ │ ├── locals: []
+ │ │ ├── operator_loc: (1,2)-(1,4) = "->"
+ │ │ ├── opening_loc: (1,5)-(1,6) = "{"
+ │ │ ├── closing_loc: (1,14)-(1,15) = "}"
+ │ │ ├── parameters: ∅
+ │ │ └── body:
+ │ │ @ StatementsNode (location: (1,7)-(1,13))
+ │ │ └── body: (length: 1)
+ │ │ └── @ SymbolNode (location: (1,7)-(1,13))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,7)-(1,8) = ":"
+ │ │ ├── value_loc: (1,8)-(1,13) = "hello"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "hello"
+ │ └── @ KeywordHashNode (location: (1,17)-(1,21))
+ │ ├── flags: symbol_keys
+ │ └── elements: (length: 1)
+ │ └── @ AssocNode (location: (1,17)-(1,21))
+ │ ├── key:
+ │ │ @ SymbolNode (location: (1,17)-(1,19))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (1,17)-(1,18) = "a"
+ │ │ ├── closing_loc: (1,18)-(1,19) = ":"
+ │ │ └── unescaped: "a"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,20)-(1,21))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,22)-(1,28))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── opening_loc: (1,22)-(1,24) = "do"
+ └── closing_loc: (1,25)-(1,28) = "end"
diff --git a/test/prism/snapshots/whitequark/ruby_bug_11873.txt b/test/prism/snapshots/whitequark/ruby_bug_11873.txt
new file mode 100644
index 0000000000..2999662cc4
--- /dev/null
+++ b/test/prism/snapshots/whitequark/ruby_bug_11873.txt
@@ -0,0 +1,767 @@
+@ ProgramNode (location: (1,0)-(23,22))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(23,22))
+ └── body: (length: 12)
+ ├── @ CallNode (location: (1,0)-(1,20))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,2)-(1,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (1,2)-(1,8))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (1,2)-(1,3) = "b"
+ │ │ │ ├── opening_loc: (1,3)-(1,4) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (1,4)-(1,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (1,4)-(1,7))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (1,4)-(1,5) = "c"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (1,6)-(1,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (1,6)-(1,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (1,6)-(1,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: (1,7)-(1,8) = ")"
+ │ │ │ └── block: ∅
+ │ │ └── @ StringNode (location: (1,10)-(1,13))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,10)-(1,11) = "\""
+ │ │ ├── content_loc: (1,11)-(1,12) = "x"
+ │ │ ├── closing_loc: (1,12)-(1,13) = "\""
+ │ │ └── unescaped: "x"
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,14)-(1,20))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,14)-(1,16) = "do"
+ │ └── closing_loc: (1,17)-(1,20) = "end"
+ ├── @ CallNode (location: (3,0)-(3,20))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (3,0)-(3,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,2)-(3,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (3,2)-(3,8))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (3,2)-(3,3) = "b"
+ │ │ │ ├── opening_loc: (3,3)-(3,4) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (3,4)-(3,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (3,4)-(3,7))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (3,4)-(3,5) = "c"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (3,6)-(3,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (3,6)-(3,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (3,6)-(3,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: (3,7)-(3,8) = ")"
+ │ │ │ └── block: ∅
+ │ │ └── @ RegularExpressionNode (location: (3,10)-(3,13))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (3,10)-(3,11) = "/"
+ │ │ ├── content_loc: (3,11)-(3,12) = "x"
+ │ │ ├── closing_loc: (3,12)-(3,13) = "/"
+ │ │ └── unescaped: "x"
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (3,14)-(3,20))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (3,14)-(3,16) = "do"
+ │ └── closing_loc: (3,17)-(3,20) = "end"
+ ├── @ CallNode (location: (5,0)-(5,21))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (5,0)-(5,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (5,2)-(5,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (5,2)-(5,8))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (5,2)-(5,3) = "b"
+ │ │ │ ├── opening_loc: (5,3)-(5,4) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (5,4)-(5,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (5,4)-(5,7))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (5,4)-(5,5) = "c"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (5,6)-(5,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (5,6)-(5,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (5,6)-(5,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: (5,7)-(5,8) = ")"
+ │ │ │ └── block: ∅
+ │ │ └── @ RegularExpressionNode (location: (5,10)-(5,14))
+ │ │ ├── flags: multi_line, forced_us_ascii_encoding
+ │ │ ├── opening_loc: (5,10)-(5,11) = "/"
+ │ │ ├── content_loc: (5,11)-(5,12) = "x"
+ │ │ ├── closing_loc: (5,12)-(5,14) = "/m"
+ │ │ └── unescaped: "x"
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (5,15)-(5,21))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (5,15)-(5,17) = "do"
+ │ └── closing_loc: (5,18)-(5,21) = "end"
+ ├── @ CallNode (location: (7,0)-(7,21))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (7,0)-(7,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (7,2)-(7,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (7,2)-(7,9))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (7,2)-(7,3) = "b"
+ │ │ │ ├── opening_loc: (7,3)-(7,4) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (7,4)-(7,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (7,4)-(7,8))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (7,4)-(7,5) = "c"
+ │ │ │ │ ├── opening_loc: (7,5)-(7,6) = "("
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (7,6)-(7,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (7,6)-(7,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (7,6)-(7,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: (7,7)-(7,8) = ")"
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: (7,8)-(7,9) = ")"
+ │ │ │ └── block: ∅
+ │ │ └── @ StringNode (location: (7,11)-(7,14))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (7,11)-(7,12) = "\""
+ │ │ ├── content_loc: (7,12)-(7,13) = "x"
+ │ │ ├── closing_loc: (7,13)-(7,14) = "\""
+ │ │ └── unescaped: "x"
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (7,15)-(7,21))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (7,15)-(7,17) = "do"
+ │ └── closing_loc: (7,18)-(7,21) = "end"
+ ├── @ CallNode (location: (9,0)-(9,21))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (9,0)-(9,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (9,2)-(9,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (9,2)-(9,9))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (9,2)-(9,3) = "b"
+ │ │ │ ├── opening_loc: (9,3)-(9,4) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (9,4)-(9,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (9,4)-(9,8))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (9,4)-(9,5) = "c"
+ │ │ │ │ ├── opening_loc: (9,5)-(9,6) = "("
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (9,6)-(9,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (9,6)-(9,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (9,6)-(9,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: (9,7)-(9,8) = ")"
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: (9,8)-(9,9) = ")"
+ │ │ │ └── block: ∅
+ │ │ └── @ RegularExpressionNode (location: (9,11)-(9,14))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (9,11)-(9,12) = "/"
+ │ │ ├── content_loc: (9,12)-(9,13) = "x"
+ │ │ ├── closing_loc: (9,13)-(9,14) = "/"
+ │ │ └── unescaped: "x"
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (9,15)-(9,21))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (9,15)-(9,17) = "do"
+ │ └── closing_loc: (9,18)-(9,21) = "end"
+ ├── @ CallNode (location: (11,0)-(11,22))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (11,0)-(11,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (11,2)-(11,15))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (11,2)-(11,9))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (11,2)-(11,3) = "b"
+ │ │ │ ├── opening_loc: (11,3)-(11,4) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (11,4)-(11,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (11,4)-(11,8))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (11,4)-(11,5) = "c"
+ │ │ │ │ ├── opening_loc: (11,5)-(11,6) = "("
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (11,6)-(11,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (11,6)-(11,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (11,6)-(11,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: (11,7)-(11,8) = ")"
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: (11,8)-(11,9) = ")"
+ │ │ │ └── block: ∅
+ │ │ └── @ RegularExpressionNode (location: (11,11)-(11,15))
+ │ │ ├── flags: multi_line, forced_us_ascii_encoding
+ │ │ ├── opening_loc: (11,11)-(11,12) = "/"
+ │ │ ├── content_loc: (11,12)-(11,13) = "x"
+ │ │ ├── closing_loc: (11,13)-(11,15) = "/m"
+ │ │ └── unescaped: "x"
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (11,16)-(11,22))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (11,16)-(11,18) = "do"
+ │ └── closing_loc: (11,19)-(11,22) = "end"
+ ├── @ CallNode (location: (13,0)-(13,20))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (13,0)-(13,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (13,2)-(13,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (13,2)-(13,8))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (13,2)-(13,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (13,3)-(13,8))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (13,4)-(13,7))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (13,4)-(13,7))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (13,4)-(13,5) = "c"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (13,6)-(13,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (13,6)-(13,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (13,6)-(13,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── opening_loc: (13,3)-(13,4) = "{"
+ │ │ │ └── closing_loc: (13,7)-(13,8) = "}"
+ │ │ └── @ StringNode (location: (13,10)-(13,13))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (13,10)-(13,11) = "\""
+ │ │ ├── content_loc: (13,11)-(13,12) = "x"
+ │ │ ├── closing_loc: (13,12)-(13,13) = "\""
+ │ │ └── unescaped: "x"
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (13,14)-(13,20))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (13,14)-(13,16) = "do"
+ │ └── closing_loc: (13,17)-(13,20) = "end"
+ ├── @ CallNode (location: (15,0)-(15,20))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (15,0)-(15,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (15,2)-(15,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (15,2)-(15,8))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (15,2)-(15,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (15,3)-(15,8))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (15,4)-(15,7))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (15,4)-(15,7))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (15,4)-(15,5) = "c"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (15,6)-(15,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (15,6)-(15,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (15,6)-(15,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── opening_loc: (15,3)-(15,4) = "{"
+ │ │ │ └── closing_loc: (15,7)-(15,8) = "}"
+ │ │ └── @ RegularExpressionNode (location: (15,10)-(15,13))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (15,10)-(15,11) = "/"
+ │ │ ├── content_loc: (15,11)-(15,12) = "x"
+ │ │ ├── closing_loc: (15,12)-(15,13) = "/"
+ │ │ └── unescaped: "x"
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (15,14)-(15,20))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (15,14)-(15,16) = "do"
+ │ └── closing_loc: (15,17)-(15,20) = "end"
+ ├── @ CallNode (location: (17,0)-(17,21))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (17,0)-(17,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (17,2)-(17,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (17,2)-(17,8))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (17,2)-(17,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (17,3)-(17,8))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (17,4)-(17,7))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (17,4)-(17,7))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (17,4)-(17,5) = "c"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (17,6)-(17,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (17,6)-(17,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (17,6)-(17,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── opening_loc: (17,3)-(17,4) = "{"
+ │ │ │ └── closing_loc: (17,7)-(17,8) = "}"
+ │ │ └── @ RegularExpressionNode (location: (17,10)-(17,14))
+ │ │ ├── flags: multi_line, forced_us_ascii_encoding
+ │ │ ├── opening_loc: (17,10)-(17,11) = "/"
+ │ │ ├── content_loc: (17,11)-(17,12) = "x"
+ │ │ ├── closing_loc: (17,12)-(17,14) = "/m"
+ │ │ └── unescaped: "x"
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (17,15)-(17,21))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (17,15)-(17,17) = "do"
+ │ └── closing_loc: (17,18)-(17,21) = "end"
+ ├── @ CallNode (location: (19,0)-(19,21))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (19,0)-(19,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (19,2)-(19,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (19,2)-(19,9))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (19,2)-(19,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (19,3)-(19,9))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (19,4)-(19,8))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (19,4)-(19,8))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (19,4)-(19,5) = "c"
+ │ │ │ │ ├── opening_loc: (19,5)-(19,6) = "("
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (19,6)-(19,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (19,6)-(19,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (19,6)-(19,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: (19,7)-(19,8) = ")"
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── opening_loc: (19,3)-(19,4) = "{"
+ │ │ │ └── closing_loc: (19,8)-(19,9) = "}"
+ │ │ └── @ StringNode (location: (19,11)-(19,14))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (19,11)-(19,12) = "\""
+ │ │ ├── content_loc: (19,12)-(19,13) = "x"
+ │ │ ├── closing_loc: (19,13)-(19,14) = "\""
+ │ │ └── unescaped: "x"
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (19,15)-(19,21))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (19,15)-(19,17) = "do"
+ │ └── closing_loc: (19,18)-(19,21) = "end"
+ ├── @ CallNode (location: (21,0)-(21,21))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (21,0)-(21,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (21,2)-(21,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (21,2)-(21,9))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (21,2)-(21,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (21,3)-(21,9))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (21,4)-(21,8))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (21,4)-(21,8))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (21,4)-(21,5) = "c"
+ │ │ │ │ ├── opening_loc: (21,5)-(21,6) = "("
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (21,6)-(21,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (21,6)-(21,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (21,6)-(21,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: (21,7)-(21,8) = ")"
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── opening_loc: (21,3)-(21,4) = "{"
+ │ │ │ └── closing_loc: (21,8)-(21,9) = "}"
+ │ │ └── @ RegularExpressionNode (location: (21,11)-(21,14))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (21,11)-(21,12) = "/"
+ │ │ ├── content_loc: (21,12)-(21,13) = "x"
+ │ │ ├── closing_loc: (21,13)-(21,14) = "/"
+ │ │ └── unescaped: "x"
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (21,15)-(21,21))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (21,15)-(21,17) = "do"
+ │ └── closing_loc: (21,18)-(21,21) = "end"
+ └── @ CallNode (location: (23,0)-(23,22))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (23,0)-(23,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (23,2)-(23,15))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ CallNode (location: (23,2)-(23,9))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (23,2)-(23,3) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (23,3)-(23,9))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (23,4)-(23,8))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (23,4)-(23,8))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── message_loc: (23,4)-(23,5) = "c"
+ │ │ │ ├── opening_loc: (23,5)-(23,6) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (23,6)-(23,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (23,6)-(23,7))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :d
+ │ │ │ │ ├── message_loc: (23,6)-(23,7) = "d"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: (23,7)-(23,8) = ")"
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (23,3)-(23,4) = "{"
+ │ │ └── closing_loc: (23,8)-(23,9) = "}"
+ │ └── @ RegularExpressionNode (location: (23,11)-(23,15))
+ │ ├── flags: multi_line, forced_us_ascii_encoding
+ │ ├── opening_loc: (23,11)-(23,12) = "/"
+ │ ├── content_loc: (23,12)-(23,13) = "x"
+ │ ├── closing_loc: (23,13)-(23,15) = "/m"
+ │ └── unescaped: "x"
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (23,16)-(23,22))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── opening_loc: (23,16)-(23,18) = "do"
+ └── closing_loc: (23,19)-(23,22) = "end"
diff --git a/test/prism/snapshots/whitequark/ruby_bug_11873_a.txt b/test/prism/snapshots/whitequark/ruby_bug_11873_a.txt
new file mode 100644
index 0000000000..93418e6448
--- /dev/null
+++ b/test/prism/snapshots/whitequark/ruby_bug_11873_a.txt
@@ -0,0 +1,1231 @@
+@ ProgramNode (location: (1,0)-(39,20))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(39,20))
+ └── body: (length: 20)
+ ├── @ CallNode (location: (1,0)-(1,18))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,2)-(1,11))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (1,2)-(1,8))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (1,2)-(1,3) = "b"
+ │ │ │ ├── opening_loc: (1,3)-(1,4) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (1,4)-(1,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (1,4)-(1,7))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (1,4)-(1,5) = "c"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (1,6)-(1,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (1,6)-(1,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (1,6)-(1,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: (1,7)-(1,8) = ")"
+ │ │ │ └── block: ∅
+ │ │ └── @ IntegerNode (location: (1,10)-(1,11))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,12)-(1,18))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,12)-(1,14) = "do"
+ │ └── closing_loc: (1,15)-(1,18) = "end"
+ ├── @ CallNode (location: (3,0)-(3,20))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (3,0)-(3,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,2)-(3,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (3,2)-(3,8))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (3,2)-(3,3) = "b"
+ │ │ │ ├── opening_loc: (3,3)-(3,4) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (3,4)-(3,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (3,4)-(3,7))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (3,4)-(3,5) = "c"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (3,6)-(3,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (3,6)-(3,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (3,6)-(3,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: (3,7)-(3,8) = ")"
+ │ │ │ └── block: ∅
+ │ │ └── @ FloatNode (location: (3,10)-(3,13))
+ │ │ └── value: 1.0
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (3,14)-(3,20))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (3,14)-(3,16) = "do"
+ │ └── closing_loc: (3,17)-(3,20) = "end"
+ ├── @ CallNode (location: (5,0)-(5,21))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (5,0)-(5,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (5,2)-(5,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (5,2)-(5,8))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (5,2)-(5,3) = "b"
+ │ │ │ ├── opening_loc: (5,3)-(5,4) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (5,4)-(5,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (5,4)-(5,7))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (5,4)-(5,5) = "c"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (5,6)-(5,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (5,6)-(5,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (5,6)-(5,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: (5,7)-(5,8) = ")"
+ │ │ │ └── block: ∅
+ │ │ └── @ ImaginaryNode (location: (5,10)-(5,14))
+ │ │ └── numeric:
+ │ │ @ FloatNode (location: (5,10)-(5,13))
+ │ │ └── value: 1.0
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (5,15)-(5,21))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (5,15)-(5,17) = "do"
+ │ └── closing_loc: (5,18)-(5,21) = "end"
+ ├── @ CallNode (location: (7,0)-(7,21))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (7,0)-(7,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (7,2)-(7,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (7,2)-(7,8))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (7,2)-(7,3) = "b"
+ │ │ │ ├── opening_loc: (7,3)-(7,4) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (7,4)-(7,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (7,4)-(7,7))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (7,4)-(7,5) = "c"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (7,6)-(7,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (7,6)-(7,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (7,6)-(7,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: (7,7)-(7,8) = ")"
+ │ │ │ └── block: ∅
+ │ │ └── @ RationalNode (location: (7,10)-(7,14))
+ │ │ └── numeric:
+ │ │ @ FloatNode (location: (7,10)-(7,13))
+ │ │ └── value: 1.0
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (7,15)-(7,21))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (7,15)-(7,17) = "do"
+ │ └── closing_loc: (7,18)-(7,21) = "end"
+ ├── @ CallNode (location: (9,0)-(9,19))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (9,0)-(9,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (9,2)-(9,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (9,2)-(9,8))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (9,2)-(9,3) = "b"
+ │ │ │ ├── opening_loc: (9,3)-(9,4) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (9,4)-(9,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (9,4)-(9,7))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (9,4)-(9,5) = "c"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (9,6)-(9,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (9,6)-(9,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (9,6)-(9,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: (9,7)-(9,8) = ")"
+ │ │ │ └── block: ∅
+ │ │ └── @ SymbolNode (location: (9,10)-(9,12))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (9,10)-(9,11) = ":"
+ │ │ ├── value_loc: (9,11)-(9,12) = "e"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "e"
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (9,13)-(9,19))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (9,13)-(9,15) = "do"
+ │ └── closing_loc: (9,16)-(9,19) = "end"
+ ├── @ CallNode (location: (11,0)-(11,19))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (11,0)-(11,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (11,2)-(11,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (11,2)-(11,9))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (11,2)-(11,3) = "b"
+ │ │ │ ├── opening_loc: (11,3)-(11,4) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (11,4)-(11,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (11,4)-(11,8))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (11,4)-(11,5) = "c"
+ │ │ │ │ ├── opening_loc: (11,5)-(11,6) = "("
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (11,6)-(11,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (11,6)-(11,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (11,6)-(11,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: (11,7)-(11,8) = ")"
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: (11,8)-(11,9) = ")"
+ │ │ │ └── block: ∅
+ │ │ └── @ IntegerNode (location: (11,11)-(11,12))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (11,13)-(11,19))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (11,13)-(11,15) = "do"
+ │ └── closing_loc: (11,16)-(11,19) = "end"
+ ├── @ CallNode (location: (13,0)-(13,21))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (13,0)-(13,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (13,2)-(13,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (13,2)-(13,9))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (13,2)-(13,3) = "b"
+ │ │ │ ├── opening_loc: (13,3)-(13,4) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (13,4)-(13,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (13,4)-(13,8))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (13,4)-(13,5) = "c"
+ │ │ │ │ ├── opening_loc: (13,5)-(13,6) = "("
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (13,6)-(13,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (13,6)-(13,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (13,6)-(13,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: (13,7)-(13,8) = ")"
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: (13,8)-(13,9) = ")"
+ │ │ │ └── block: ∅
+ │ │ └── @ FloatNode (location: (13,11)-(13,14))
+ │ │ └── value: 1.0
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (13,15)-(13,21))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (13,15)-(13,17) = "do"
+ │ └── closing_loc: (13,18)-(13,21) = "end"
+ ├── @ CallNode (location: (15,0)-(15,22))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (15,0)-(15,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (15,2)-(15,15))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (15,2)-(15,9))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (15,2)-(15,3) = "b"
+ │ │ │ ├── opening_loc: (15,3)-(15,4) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (15,4)-(15,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (15,4)-(15,8))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (15,4)-(15,5) = "c"
+ │ │ │ │ ├── opening_loc: (15,5)-(15,6) = "("
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (15,6)-(15,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (15,6)-(15,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (15,6)-(15,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: (15,7)-(15,8) = ")"
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: (15,8)-(15,9) = ")"
+ │ │ │ └── block: ∅
+ │ │ └── @ ImaginaryNode (location: (15,11)-(15,15))
+ │ │ └── numeric:
+ │ │ @ FloatNode (location: (15,11)-(15,14))
+ │ │ └── value: 1.0
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (15,16)-(15,22))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (15,16)-(15,18) = "do"
+ │ └── closing_loc: (15,19)-(15,22) = "end"
+ ├── @ CallNode (location: (17,0)-(17,22))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (17,0)-(17,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (17,2)-(17,15))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (17,2)-(17,9))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (17,2)-(17,3) = "b"
+ │ │ │ ├── opening_loc: (17,3)-(17,4) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (17,4)-(17,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (17,4)-(17,8))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (17,4)-(17,5) = "c"
+ │ │ │ │ ├── opening_loc: (17,5)-(17,6) = "("
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (17,6)-(17,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (17,6)-(17,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (17,6)-(17,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: (17,7)-(17,8) = ")"
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: (17,8)-(17,9) = ")"
+ │ │ │ └── block: ∅
+ │ │ └── @ RationalNode (location: (17,11)-(17,15))
+ │ │ └── numeric:
+ │ │ @ FloatNode (location: (17,11)-(17,14))
+ │ │ └── value: 1.0
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (17,16)-(17,22))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (17,16)-(17,18) = "do"
+ │ └── closing_loc: (17,19)-(17,22) = "end"
+ ├── @ CallNode (location: (19,0)-(19,20))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (19,0)-(19,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (19,2)-(19,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (19,2)-(19,9))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (19,2)-(19,3) = "b"
+ │ │ │ ├── opening_loc: (19,3)-(19,4) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (19,4)-(19,8))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (19,4)-(19,8))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (19,4)-(19,5) = "c"
+ │ │ │ │ ├── opening_loc: (19,5)-(19,6) = "("
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (19,6)-(19,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (19,6)-(19,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (19,6)-(19,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: (19,7)-(19,8) = ")"
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: (19,8)-(19,9) = ")"
+ │ │ │ └── block: ∅
+ │ │ └── @ SymbolNode (location: (19,11)-(19,13))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (19,11)-(19,12) = ":"
+ │ │ ├── value_loc: (19,12)-(19,13) = "e"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "e"
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (19,14)-(19,20))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (19,14)-(19,16) = "do"
+ │ └── closing_loc: (19,17)-(19,20) = "end"
+ ├── @ CallNode (location: (21,0)-(21,18))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (21,0)-(21,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (21,2)-(21,11))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (21,2)-(21,8))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (21,2)-(21,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (21,3)-(21,8))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (21,4)-(21,7))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (21,4)-(21,7))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (21,4)-(21,5) = "c"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (21,6)-(21,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (21,6)-(21,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (21,6)-(21,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── opening_loc: (21,3)-(21,4) = "{"
+ │ │ │ └── closing_loc: (21,7)-(21,8) = "}"
+ │ │ └── @ IntegerNode (location: (21,10)-(21,11))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (21,12)-(21,18))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (21,12)-(21,14) = "do"
+ │ └── closing_loc: (21,15)-(21,18) = "end"
+ ├── @ CallNode (location: (23,0)-(23,20))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (23,0)-(23,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (23,2)-(23,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (23,2)-(23,8))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (23,2)-(23,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (23,3)-(23,8))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (23,4)-(23,7))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (23,4)-(23,7))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (23,4)-(23,5) = "c"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (23,6)-(23,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (23,6)-(23,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (23,6)-(23,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── opening_loc: (23,3)-(23,4) = "{"
+ │ │ │ └── closing_loc: (23,7)-(23,8) = "}"
+ │ │ └── @ FloatNode (location: (23,10)-(23,13))
+ │ │ └── value: 1.0
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (23,14)-(23,20))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (23,14)-(23,16) = "do"
+ │ └── closing_loc: (23,17)-(23,20) = "end"
+ ├── @ CallNode (location: (25,0)-(25,21))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (25,0)-(25,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (25,2)-(25,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (25,2)-(25,8))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (25,2)-(25,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (25,3)-(25,8))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (25,4)-(25,7))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (25,4)-(25,7))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (25,4)-(25,5) = "c"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (25,6)-(25,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (25,6)-(25,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (25,6)-(25,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── opening_loc: (25,3)-(25,4) = "{"
+ │ │ │ └── closing_loc: (25,7)-(25,8) = "}"
+ │ │ └── @ ImaginaryNode (location: (25,10)-(25,14))
+ │ │ └── numeric:
+ │ │ @ FloatNode (location: (25,10)-(25,13))
+ │ │ └── value: 1.0
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (25,15)-(25,21))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (25,15)-(25,17) = "do"
+ │ └── closing_loc: (25,18)-(25,21) = "end"
+ ├── @ CallNode (location: (27,0)-(27,21))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (27,0)-(27,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (27,2)-(27,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (27,2)-(27,8))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (27,2)-(27,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (27,3)-(27,8))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (27,4)-(27,7))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (27,4)-(27,7))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (27,4)-(27,5) = "c"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (27,6)-(27,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (27,6)-(27,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (27,6)-(27,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── opening_loc: (27,3)-(27,4) = "{"
+ │ │ │ └── closing_loc: (27,7)-(27,8) = "}"
+ │ │ └── @ RationalNode (location: (27,10)-(27,14))
+ │ │ └── numeric:
+ │ │ @ FloatNode (location: (27,10)-(27,13))
+ │ │ └── value: 1.0
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (27,15)-(27,21))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (27,15)-(27,17) = "do"
+ │ └── closing_loc: (27,18)-(27,21) = "end"
+ ├── @ CallNode (location: (29,0)-(29,19))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (29,0)-(29,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (29,2)-(29,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (29,2)-(29,8))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (29,2)-(29,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (29,3)-(29,8))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (29,4)-(29,7))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (29,4)-(29,7))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (29,4)-(29,5) = "c"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (29,6)-(29,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (29,6)-(29,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (29,6)-(29,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── opening_loc: (29,3)-(29,4) = "{"
+ │ │ │ └── closing_loc: (29,7)-(29,8) = "}"
+ │ │ └── @ SymbolNode (location: (29,10)-(29,12))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (29,10)-(29,11) = ":"
+ │ │ ├── value_loc: (29,11)-(29,12) = "e"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "e"
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (29,13)-(29,19))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (29,13)-(29,15) = "do"
+ │ └── closing_loc: (29,16)-(29,19) = "end"
+ ├── @ CallNode (location: (31,0)-(31,19))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (31,0)-(31,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (31,2)-(31,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (31,2)-(31,9))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (31,2)-(31,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (31,3)-(31,9))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (31,4)-(31,8))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (31,4)-(31,8))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (31,4)-(31,5) = "c"
+ │ │ │ │ ├── opening_loc: (31,5)-(31,6) = "("
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (31,6)-(31,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (31,6)-(31,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (31,6)-(31,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: (31,7)-(31,8) = ")"
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── opening_loc: (31,3)-(31,4) = "{"
+ │ │ │ └── closing_loc: (31,8)-(31,9) = "}"
+ │ │ └── @ IntegerNode (location: (31,11)-(31,12))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (31,13)-(31,19))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (31,13)-(31,15) = "do"
+ │ └── closing_loc: (31,16)-(31,19) = "end"
+ ├── @ CallNode (location: (33,0)-(33,21))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (33,0)-(33,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (33,2)-(33,14))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (33,2)-(33,9))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (33,2)-(33,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (33,3)-(33,9))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (33,4)-(33,8))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (33,4)-(33,8))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (33,4)-(33,5) = "c"
+ │ │ │ │ ├── opening_loc: (33,5)-(33,6) = "("
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (33,6)-(33,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (33,6)-(33,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (33,6)-(33,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: (33,7)-(33,8) = ")"
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── opening_loc: (33,3)-(33,4) = "{"
+ │ │ │ └── closing_loc: (33,8)-(33,9) = "}"
+ │ │ └── @ FloatNode (location: (33,11)-(33,14))
+ │ │ └── value: 1.0
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (33,15)-(33,21))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (33,15)-(33,17) = "do"
+ │ └── closing_loc: (33,18)-(33,21) = "end"
+ ├── @ CallNode (location: (35,0)-(35,22))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (35,0)-(35,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (35,2)-(35,15))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (35,2)-(35,9))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (35,2)-(35,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (35,3)-(35,9))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (35,4)-(35,8))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (35,4)-(35,8))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (35,4)-(35,5) = "c"
+ │ │ │ │ ├── opening_loc: (35,5)-(35,6) = "("
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (35,6)-(35,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (35,6)-(35,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (35,6)-(35,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: (35,7)-(35,8) = ")"
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── opening_loc: (35,3)-(35,4) = "{"
+ │ │ │ └── closing_loc: (35,8)-(35,9) = "}"
+ │ │ └── @ ImaginaryNode (location: (35,11)-(35,15))
+ │ │ └── numeric:
+ │ │ @ FloatNode (location: (35,11)-(35,14))
+ │ │ └── value: 1.0
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (35,16)-(35,22))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (35,16)-(35,18) = "do"
+ │ └── closing_loc: (35,19)-(35,22) = "end"
+ ├── @ CallNode (location: (37,0)-(37,22))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (37,0)-(37,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (37,2)-(37,15))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ CallNode (location: (37,2)-(37,9))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :b
+ │ │ │ ├── message_loc: (37,2)-(37,3) = "b"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block:
+ │ │ │ @ BlockNode (location: (37,3)-(37,9))
+ │ │ │ ├── locals: []
+ │ │ │ ├── parameters: ∅
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (37,4)-(37,8))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (37,4)-(37,8))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :c
+ │ │ │ │ ├── message_loc: (37,4)-(37,5) = "c"
+ │ │ │ │ ├── opening_loc: (37,5)-(37,6) = "("
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (37,6)-(37,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (37,6)-(37,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :d
+ │ │ │ │ │ ├── message_loc: (37,6)-(37,7) = "d"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: (37,7)-(37,8) = ")"
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── opening_loc: (37,3)-(37,4) = "{"
+ │ │ │ └── closing_loc: (37,8)-(37,9) = "}"
+ │ │ └── @ RationalNode (location: (37,11)-(37,15))
+ │ │ └── numeric:
+ │ │ @ FloatNode (location: (37,11)-(37,14))
+ │ │ └── value: 1.0
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (37,16)-(37,22))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (37,16)-(37,18) = "do"
+ │ └── closing_loc: (37,19)-(37,22) = "end"
+ └── @ CallNode (location: (39,0)-(39,20))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :a
+ ├── message_loc: (39,0)-(39,1) = "a"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (39,2)-(39,13))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ CallNode (location: (39,2)-(39,9))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :b
+ │ │ ├── message_loc: (39,2)-(39,3) = "b"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (39,3)-(39,9))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (39,4)-(39,8))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (39,4)-(39,8))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :c
+ │ │ │ ├── message_loc: (39,4)-(39,5) = "c"
+ │ │ │ ├── opening_loc: (39,5)-(39,6) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (39,6)-(39,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (39,6)-(39,7))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :d
+ │ │ │ │ ├── message_loc: (39,6)-(39,7) = "d"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: (39,7)-(39,8) = ")"
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (39,3)-(39,4) = "{"
+ │ │ └── closing_loc: (39,8)-(39,9) = "}"
+ │ └── @ SymbolNode (location: (39,11)-(39,13))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (39,11)-(39,12) = ":"
+ │ ├── value_loc: (39,12)-(39,13) = "e"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "e"
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (39,14)-(39,20))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── opening_loc: (39,14)-(39,16) = "do"
+ └── closing_loc: (39,17)-(39,20) = "end"
diff --git a/test/prism/snapshots/whitequark/ruby_bug_11873_b.txt b/test/prism/snapshots/whitequark/ruby_bug_11873_b.txt
new file mode 100644
index 0000000000..6aa8e55e54
--- /dev/null
+++ b/test/prism/snapshots/whitequark/ruby_bug_11873_b.txt
@@ -0,0 +1,98 @@
+@ ProgramNode (location: (1,0)-(1,25))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,25))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,25))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :p
+ ├── message_loc: (1,0)-(1,1) = "p"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,18))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ CallNode (location: (1,2)-(1,13))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :p
+ │ │ ├── message_loc: (1,2)-(1,3) = "p"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (1,3)-(1,13))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (1,4)-(1,12))
+ │ │ │ └── body: (length: 2)
+ │ │ │ ├── @ CallNode (location: (1,4)-(1,8))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :p
+ │ │ │ │ ├── message_loc: (1,4)-(1,5) = "p"
+ │ │ │ │ ├── opening_loc: (1,5)-(1,6) = "("
+ │ │ │ │ ├── arguments:
+ │ │ │ │ │ @ ArgumentsNode (location: (1,6)-(1,7))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (1,6)-(1,7))
+ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :p
+ │ │ │ │ │ ├── message_loc: (1,6)-(1,7) = "p"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── closing_loc: (1,7)-(1,8) = ")"
+ │ │ │ │ └── block: ∅
+ │ │ │ └── @ CallNode (location: (1,9)-(1,12))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :p
+ │ │ │ ├── message_loc: (1,9)-(1,10) = "p"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (1,11)-(1,12))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (1,11)-(1,12))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :p
+ │ │ │ │ ├── message_loc: (1,11)-(1,12) = "p"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── opening_loc: (1,3)-(1,4) = "{"
+ │ │ └── closing_loc: (1,12)-(1,13) = "}"
+ │ └── @ CallNode (location: (1,15)-(1,18))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (1,15)-(1,18) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,19)-(1,25))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── opening_loc: (1,19)-(1,21) = "do"
+ └── closing_loc: (1,22)-(1,25) = "end"
diff --git a/test/prism/snapshots/whitequark/ruby_bug_11989.txt b/test/prism/snapshots/whitequark/ruby_bug_11989.txt
new file mode 100644
index 0000000000..fe17087e53
--- /dev/null
+++ b/test/prism/snapshots/whitequark/ruby_bug_11989.txt
@@ -0,0 +1,24 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,8))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :p
+ ├── message_loc: (1,0)-(1,1) = "p"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ StringNode (location: (1,2)-(1,8))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,2)-(1,8) = "<<~\"E\""
+ │ ├── content_loc: (2,0)-(3,0) = " x\\n y\n"
+ │ ├── closing_loc: (3,0)-(4,0) = "E\n"
+ │ └── unescaped: "x\n y\n"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/ruby_bug_11990.txt b/test/prism/snapshots/whitequark/ruby_bug_11990.txt
new file mode 100644
index 0000000000..0a5fba1482
--- /dev/null
+++ b/test/prism/snapshots/whitequark/ruby_bug_11990.txt
@@ -0,0 +1,35 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,12))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :p
+ ├── message_loc: (1,0)-(1,1) = "p"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,12))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ InterpolatedStringNode (location: (1,2)-(1,12))
+ │ ├── flags: ∅
+ │ ├── opening_loc: ∅
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (1,2)-(1,6))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: (1,2)-(1,6) = "<<~E"
+ │ │ │ ├── content_loc: (2,0)-(3,0) = " x\n"
+ │ │ │ ├── closing_loc: (3,0)-(4,0) = "E\n"
+ │ │ │ └── unescaped: "x\n"
+ │ │ └── @ StringNode (location: (1,7)-(1,12))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: (1,7)-(1,8) = "\""
+ │ │ ├── content_loc: (1,8)-(1,11) = " y"
+ │ │ ├── closing_loc: (1,11)-(1,12) = "\""
+ │ │ └── unescaped: " y"
+ │ └── closing_loc: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/ruby_bug_12073.txt b/test/prism/snapshots/whitequark/ruby_bug_12073.txt
new file mode 100644
index 0000000000..9db30f6fec
--- /dev/null
+++ b/test/prism/snapshots/whitequark/ruby_bug_12073.txt
@@ -0,0 +1,96 @@
+@ ProgramNode (location: (1,0)-(3,34))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,34))
+ └── body: (length: 3)
+ ├── @ LocalVariableWriteNode (location: (1,0)-(1,5))
+ │ ├── name: :a
+ │ ├── depth: 0
+ │ ├── name_loc: (1,0)-(1,1) = "a"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,4)-(1,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (1,2)-(1,3) = "="
+ ├── @ CallNode (location: (1,7)-(1,13))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,7)-(1,8) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,9)-(1,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ KeywordHashNode (location: (1,9)-(1,13))
+ │ │ ├── flags: symbol_keys
+ │ │ └── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (1,9)-(1,13))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (1,9)-(1,11))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (1,9)-(1,10) = "b"
+ │ │ │ ├── closing_loc: (1,10)-(1,11) = ":"
+ │ │ │ └── unescaped: "b"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (1,12)-(1,13))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ DefNode (location: (3,0)-(3,34))
+ ├── name: :foo
+ ├── name_loc: (3,4)-(3,7) = "foo"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (3,8)-(3,13))
+ │ ├── requireds: (length: 1)
+ │ │ └── @ RequiredParameterNode (location: (3,8)-(3,13))
+ │ │ ├── flags: ∅
+ │ │ └── name: :raise
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest: ∅
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (3,15)-(3,29))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (3,15)-(3,29))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :raise
+ │ ├── message_loc: (3,15)-(3,20) = "raise"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,21)-(3,29))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 2)
+ │ │ ├── @ ConstantPathNode (location: (3,21)-(3,25))
+ │ │ │ ├── parent:
+ │ │ │ │ @ ConstantReadNode (location: (3,21)-(3,22))
+ │ │ │ │ └── name: :A
+ │ │ │ ├── child:
+ │ │ │ │ @ ConstantReadNode (location: (3,24)-(3,25))
+ │ │ │ │ └── name: :B
+ │ │ │ └── delimiter_loc: (3,22)-(3,24) = "::"
+ │ │ └── @ StringNode (location: (3,27)-(3,29))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (3,27)-(3,28) = "'"
+ │ │ ├── content_loc: (3,28)-(3,28) = ""
+ │ │ ├── closing_loc: (3,28)-(3,29) = "'"
+ │ │ └── unescaped: ""
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── locals: [:raise]
+ ├── def_keyword_loc: (3,0)-(3,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (3,31)-(3,34) = "end"
diff --git a/test/prism/snapshots/whitequark/ruby_bug_12402.txt b/test/prism/snapshots/whitequark/ruby_bug_12402.txt
new file mode 100644
index 0000000000..df5aea00c3
--- /dev/null
+++ b/test/prism/snapshots/whitequark/ruby_bug_12402.txt
@@ -0,0 +1,567 @@
+@ ProgramNode (location: (1,0)-(27,31))
+├── locals: [:foo]
+└── statements:
+ @ StatementsNode (location: (1,0)-(27,31))
+ └── body: (length: 14)
+ ├── @ LocalVariableOperatorWriteNode (location: (1,0)-(1,27))
+ │ ├── name_loc: (1,0)-(1,3) = "foo"
+ │ ├── operator_loc: (1,4)-(1,6) = "+="
+ │ ├── value:
+ │ │ @ RescueModifierNode (location: (1,7)-(1,27))
+ │ │ ├── expression:
+ │ │ │ @ CallNode (location: (1,7)-(1,16))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :raise
+ │ │ │ ├── message_loc: (1,7)-(1,12) = "raise"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (1,13)-(1,16))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (1,13)-(1,16))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (1,13)-(1,16) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── keyword_loc: (1,17)-(1,23) = "rescue"
+ │ │ └── rescue_expression:
+ │ │ @ NilNode (location: (1,24)-(1,27))
+ │ ├── name: :foo
+ │ ├── operator: :+
+ │ └── depth: 0
+ ├── @ LocalVariableOperatorWriteNode (location: (3,0)-(3,28))
+ │ ├── name_loc: (3,0)-(3,3) = "foo"
+ │ ├── operator_loc: (3,4)-(3,6) = "+="
+ │ ├── value:
+ │ │ @ RescueModifierNode (location: (3,7)-(3,28))
+ │ │ ├── expression:
+ │ │ │ @ CallNode (location: (3,7)-(3,17))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :raise
+ │ │ │ ├── message_loc: (3,7)-(3,12) = "raise"
+ │ │ │ ├── opening_loc: (3,12)-(3,13) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (3,13)-(3,16))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (3,13)-(3,16))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (3,13)-(3,16) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: (3,16)-(3,17) = ")"
+ │ │ │ └── block: ∅
+ │ │ ├── keyword_loc: (3,18)-(3,24) = "rescue"
+ │ │ └── rescue_expression:
+ │ │ @ NilNode (location: (3,25)-(3,28))
+ │ ├── name: :foo
+ │ ├── operator: :+
+ │ └── depth: 0
+ ├── @ LocalVariableWriteNode (location: (5,0)-(5,26))
+ │ ├── name: :foo
+ │ ├── depth: 0
+ │ ├── name_loc: (5,0)-(5,3) = "foo"
+ │ ├── value:
+ │ │ @ RescueModifierNode (location: (5,6)-(5,26))
+ │ │ ├── expression:
+ │ │ │ @ CallNode (location: (5,6)-(5,15))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :raise
+ │ │ │ ├── message_loc: (5,6)-(5,11) = "raise"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (5,12)-(5,15))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (5,12)-(5,15))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (5,12)-(5,15) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── keyword_loc: (5,16)-(5,22) = "rescue"
+ │ │ └── rescue_expression:
+ │ │ @ NilNode (location: (5,23)-(5,26))
+ │ └── operator_loc: (5,4)-(5,5) = "="
+ ├── @ LocalVariableWriteNode (location: (7,0)-(7,27))
+ │ ├── name: :foo
+ │ ├── depth: 0
+ │ ├── name_loc: (7,0)-(7,3) = "foo"
+ │ ├── value:
+ │ │ @ RescueModifierNode (location: (7,6)-(7,27))
+ │ │ ├── expression:
+ │ │ │ @ CallNode (location: (7,6)-(7,16))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :raise
+ │ │ │ ├── message_loc: (7,6)-(7,11) = "raise"
+ │ │ │ ├── opening_loc: (7,11)-(7,12) = "("
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (7,12)-(7,15))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (7,12)-(7,15))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (7,12)-(7,15) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── closing_loc: (7,15)-(7,16) = ")"
+ │ │ │ └── block: ∅
+ │ │ ├── keyword_loc: (7,17)-(7,23) = "rescue"
+ │ │ └── rescue_expression:
+ │ │ @ NilNode (location: (7,24)-(7,27))
+ │ └── operator_loc: (7,4)-(7,5) = "="
+ ├── @ CallOperatorWriteNode (location: (9,0)-(9,29))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (9,0)-(9,3))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: (9,3)-(9,4) = "."
+ │ ├── message_loc: (9,4)-(9,5) = "C"
+ │ ├── read_name: :C
+ │ ├── write_name: :C=
+ │ ├── operator: :+
+ │ ├── operator_loc: (9,6)-(9,8) = "+="
+ │ └── value:
+ │ @ RescueModifierNode (location: (9,9)-(9,29))
+ │ ├── expression:
+ │ │ @ CallNode (location: (9,9)-(9,18))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :raise
+ │ │ ├── message_loc: (9,9)-(9,14) = "raise"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (9,15)-(9,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ CallNode (location: (9,15)-(9,18))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (9,15)-(9,18) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (9,19)-(9,25) = "rescue"
+ │ └── rescue_expression:
+ │ @ NilNode (location: (9,26)-(9,29))
+ ├── @ CallOperatorWriteNode (location: (11,0)-(11,30))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (11,0)-(11,3))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: (11,3)-(11,4) = "."
+ │ ├── message_loc: (11,4)-(11,5) = "C"
+ │ ├── read_name: :C
+ │ ├── write_name: :C=
+ │ ├── operator: :+
+ │ ├── operator_loc: (11,6)-(11,8) = "+="
+ │ └── value:
+ │ @ RescueModifierNode (location: (11,9)-(11,30))
+ │ ├── expression:
+ │ │ @ CallNode (location: (11,9)-(11,19))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :raise
+ │ │ ├── message_loc: (11,9)-(11,14) = "raise"
+ │ │ ├── opening_loc: (11,14)-(11,15) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (11,15)-(11,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ CallNode (location: (11,15)-(11,18))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (11,15)-(11,18) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── closing_loc: (11,18)-(11,19) = ")"
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (11,20)-(11,26) = "rescue"
+ │ └── rescue_expression:
+ │ @ NilNode (location: (11,27)-(11,30))
+ ├── @ CallOperatorWriteNode (location: (13,0)-(13,29))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (13,0)-(13,3))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: (13,3)-(13,4) = "."
+ │ ├── message_loc: (13,4)-(13,5) = "m"
+ │ ├── read_name: :m
+ │ ├── write_name: :m=
+ │ ├── operator: :+
+ │ ├── operator_loc: (13,6)-(13,8) = "+="
+ │ └── value:
+ │ @ RescueModifierNode (location: (13,9)-(13,29))
+ │ ├── expression:
+ │ │ @ CallNode (location: (13,9)-(13,18))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :raise
+ │ │ ├── message_loc: (13,9)-(13,14) = "raise"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (13,15)-(13,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ CallNode (location: (13,15)-(13,18))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (13,15)-(13,18) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (13,19)-(13,25) = "rescue"
+ │ └── rescue_expression:
+ │ @ NilNode (location: (13,26)-(13,29))
+ ├── @ CallOperatorWriteNode (location: (15,0)-(15,30))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (15,0)-(15,3))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: (15,3)-(15,4) = "."
+ │ ├── message_loc: (15,4)-(15,5) = "m"
+ │ ├── read_name: :m
+ │ ├── write_name: :m=
+ │ ├── operator: :+
+ │ ├── operator_loc: (15,6)-(15,8) = "+="
+ │ └── value:
+ │ @ RescueModifierNode (location: (15,9)-(15,30))
+ │ ├── expression:
+ │ │ @ CallNode (location: (15,9)-(15,19))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :raise
+ │ │ ├── message_loc: (15,9)-(15,14) = "raise"
+ │ │ ├── opening_loc: (15,14)-(15,15) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (15,15)-(15,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ CallNode (location: (15,15)-(15,18))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (15,15)-(15,18) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── closing_loc: (15,18)-(15,19) = ")"
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (15,20)-(15,26) = "rescue"
+ │ └── rescue_expression:
+ │ @ NilNode (location: (15,27)-(15,30))
+ ├── @ ConstantPathOrWriteNode (location: (17,0)-(17,31))
+ │ ├── target:
+ │ │ @ ConstantPathNode (location: (17,0)-(17,6))
+ │ │ ├── parent:
+ │ │ │ @ LocalVariableReadNode (location: (17,0)-(17,3))
+ │ │ │ ├── name: :foo
+ │ │ │ └── depth: 0
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (17,5)-(17,6))
+ │ │ │ └── name: :C
+ │ │ └── delimiter_loc: (17,3)-(17,5) = "::"
+ │ ├── operator_loc: (17,7)-(17,10) = "||="
+ │ └── value:
+ │ @ RescueModifierNode (location: (17,11)-(17,31))
+ │ ├── expression:
+ │ │ @ CallNode (location: (17,11)-(17,20))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :raise
+ │ │ ├── message_loc: (17,11)-(17,16) = "raise"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (17,17)-(17,20))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ CallNode (location: (17,17)-(17,20))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (17,17)-(17,20) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (17,21)-(17,27) = "rescue"
+ │ └── rescue_expression:
+ │ @ NilNode (location: (17,28)-(17,31))
+ ├── @ ConstantPathOrWriteNode (location: (19,0)-(19,32))
+ │ ├── target:
+ │ │ @ ConstantPathNode (location: (19,0)-(19,6))
+ │ │ ├── parent:
+ │ │ │ @ LocalVariableReadNode (location: (19,0)-(19,3))
+ │ │ │ ├── name: :foo
+ │ │ │ └── depth: 0
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (19,5)-(19,6))
+ │ │ │ └── name: :C
+ │ │ └── delimiter_loc: (19,3)-(19,5) = "::"
+ │ ├── operator_loc: (19,7)-(19,10) = "||="
+ │ └── value:
+ │ @ RescueModifierNode (location: (19,11)-(19,32))
+ │ ├── expression:
+ │ │ @ CallNode (location: (19,11)-(19,21))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :raise
+ │ │ ├── message_loc: (19,11)-(19,16) = "raise"
+ │ │ ├── opening_loc: (19,16)-(19,17) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (19,17)-(19,20))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ CallNode (location: (19,17)-(19,20))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (19,17)-(19,20) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── closing_loc: (19,20)-(19,21) = ")"
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (19,22)-(19,28) = "rescue"
+ │ └── rescue_expression:
+ │ @ NilNode (location: (19,29)-(19,32))
+ ├── @ CallOperatorWriteNode (location: (21,0)-(21,30))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (21,0)-(21,3))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: (21,3)-(21,5) = "::"
+ │ ├── message_loc: (21,5)-(21,6) = "m"
+ │ ├── read_name: :m
+ │ ├── write_name: :m=
+ │ ├── operator: :+
+ │ ├── operator_loc: (21,7)-(21,9) = "+="
+ │ └── value:
+ │ @ RescueModifierNode (location: (21,10)-(21,30))
+ │ ├── expression:
+ │ │ @ CallNode (location: (21,10)-(21,19))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :raise
+ │ │ ├── message_loc: (21,10)-(21,15) = "raise"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (21,16)-(21,19))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ CallNode (location: (21,16)-(21,19))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (21,16)-(21,19) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (21,20)-(21,26) = "rescue"
+ │ └── rescue_expression:
+ │ @ NilNode (location: (21,27)-(21,30))
+ ├── @ CallOperatorWriteNode (location: (23,0)-(23,31))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (23,0)-(23,3))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: (23,3)-(23,5) = "::"
+ │ ├── message_loc: (23,5)-(23,6) = "m"
+ │ ├── read_name: :m
+ │ ├── write_name: :m=
+ │ ├── operator: :+
+ │ ├── operator_loc: (23,7)-(23,9) = "+="
+ │ └── value:
+ │ @ RescueModifierNode (location: (23,10)-(23,31))
+ │ ├── expression:
+ │ │ @ CallNode (location: (23,10)-(23,20))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :raise
+ │ │ ├── message_loc: (23,10)-(23,15) = "raise"
+ │ │ ├── opening_loc: (23,15)-(23,16) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (23,16)-(23,19))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ CallNode (location: (23,16)-(23,19))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (23,16)-(23,19) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── closing_loc: (23,19)-(23,20) = ")"
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (23,21)-(23,27) = "rescue"
+ │ └── rescue_expression:
+ │ @ NilNode (location: (23,28)-(23,31))
+ ├── @ IndexOperatorWriteNode (location: (25,0)-(25,30))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ LocalVariableReadNode (location: (25,0)-(25,3))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── call_operator_loc: ∅
+ │ ├── opening_loc: (25,3)-(25,4) = "["
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (25,4)-(25,5))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (25,4)-(25,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 0
+ │ ├── closing_loc: (25,5)-(25,6) = "]"
+ │ ├── block: ∅
+ │ ├── operator: :+
+ │ ├── operator_loc: (25,7)-(25,9) = "+="
+ │ └── value:
+ │ @ RescueModifierNode (location: (25,10)-(25,30))
+ │ ├── expression:
+ │ │ @ CallNode (location: (25,10)-(25,19))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :raise
+ │ │ ├── message_loc: (25,10)-(25,15) = "raise"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (25,16)-(25,19))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ CallNode (location: (25,16)-(25,19))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (25,16)-(25,19) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── keyword_loc: (25,20)-(25,26) = "rescue"
+ │ └── rescue_expression:
+ │ @ NilNode (location: (25,27)-(25,30))
+ └── @ IndexOperatorWriteNode (location: (27,0)-(27,31))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ LocalVariableReadNode (location: (27,0)-(27,3))
+ │ ├── name: :foo
+ │ └── depth: 0
+ ├── call_operator_loc: ∅
+ ├── opening_loc: (27,3)-(27,4) = "["
+ ├── arguments:
+ │ @ ArgumentsNode (location: (27,4)-(27,5))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (27,4)-(27,5))
+ │ ├── flags: decimal
+ │ └── value: 0
+ ├── closing_loc: (27,5)-(27,6) = "]"
+ ├── block: ∅
+ ├── operator: :+
+ ├── operator_loc: (27,7)-(27,9) = "+="
+ └── value:
+ @ RescueModifierNode (location: (27,10)-(27,31))
+ ├── expression:
+ │ @ CallNode (location: (27,10)-(27,20))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :raise
+ │ ├── message_loc: (27,10)-(27,15) = "raise"
+ │ ├── opening_loc: (27,15)-(27,16) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (27,16)-(27,19))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (27,16)-(27,19))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (27,16)-(27,19) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (27,19)-(27,20) = ")"
+ │ └── block: ∅
+ ├── keyword_loc: (27,21)-(27,27) = "rescue"
+ └── rescue_expression:
+ @ NilNode (location: (27,28)-(27,31))
diff --git a/test/prism/snapshots/whitequark/ruby_bug_12669.txt b/test/prism/snapshots/whitequark/ruby_bug_12669.txt
new file mode 100644
index 0000000000..86b021351b
--- /dev/null
+++ b/test/prism/snapshots/whitequark/ruby_bug_12669.txt
@@ -0,0 +1,133 @@
+@ ProgramNode (location: (1,0)-(7,16))
+├── locals: [:a, :b]
+└── statements:
+ @ StatementsNode (location: (1,0)-(7,16))
+ └── body: (length: 4)
+ ├── @ LocalVariableOperatorWriteNode (location: (1,0)-(1,18))
+ │ ├── name_loc: (1,0)-(1,1) = "a"
+ │ ├── operator_loc: (1,2)-(1,4) = "+="
+ │ ├── value:
+ │ │ @ LocalVariableOperatorWriteNode (location: (1,5)-(1,18))
+ │ │ ├── name_loc: (1,5)-(1,6) = "b"
+ │ │ ├── operator_loc: (1,7)-(1,9) = "+="
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (1,10)-(1,18))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :raise
+ │ │ │ ├── message_loc: (1,10)-(1,15) = "raise"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (1,16)-(1,18))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ SymbolNode (location: (1,16)-(1,18))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (1,16)-(1,17) = ":"
+ │ │ │ │ ├── value_loc: (1,17)-(1,18) = "x"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "x"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── name: :b
+ │ │ ├── operator: :+
+ │ │ └── depth: 0
+ │ ├── name: :a
+ │ ├── operator: :+
+ │ └── depth: 0
+ ├── @ LocalVariableOperatorWriteNode (location: (3,0)-(3,17))
+ │ ├── name_loc: (3,0)-(3,1) = "a"
+ │ ├── operator_loc: (3,2)-(3,4) = "+="
+ │ ├── value:
+ │ │ @ LocalVariableWriteNode (location: (3,5)-(3,17))
+ │ │ ├── name: :b
+ │ │ ├── depth: 0
+ │ │ ├── name_loc: (3,5)-(3,6) = "b"
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (3,9)-(3,17))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :raise
+ │ │ │ ├── message_loc: (3,9)-(3,14) = "raise"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (3,15)-(3,17))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ SymbolNode (location: (3,15)-(3,17))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (3,15)-(3,16) = ":"
+ │ │ │ │ ├── value_loc: (3,16)-(3,17) = "x"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "x"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (3,7)-(3,8) = "="
+ │ ├── name: :a
+ │ ├── operator: :+
+ │ └── depth: 0
+ ├── @ LocalVariableWriteNode (location: (5,0)-(5,17))
+ │ ├── name: :a
+ │ ├── depth: 0
+ │ ├── name_loc: (5,0)-(5,1) = "a"
+ │ ├── value:
+ │ │ @ LocalVariableOperatorWriteNode (location: (5,4)-(5,17))
+ │ │ ├── name_loc: (5,4)-(5,5) = "b"
+ │ │ ├── operator_loc: (5,6)-(5,8) = "+="
+ │ │ ├── value:
+ │ │ │ @ CallNode (location: (5,9)-(5,17))
+ │ │ │ ├── flags: ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :raise
+ │ │ │ ├── message_loc: (5,9)-(5,14) = "raise"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (5,15)-(5,17))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ SymbolNode (location: (5,15)-(5,17))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: (5,15)-(5,16) = ":"
+ │ │ │ │ ├── value_loc: (5,16)-(5,17) = "x"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "x"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── name: :b
+ │ │ ├── operator: :+
+ │ │ └── depth: 0
+ │ └── operator_loc: (5,2)-(5,3) = "="
+ └── @ LocalVariableWriteNode (location: (7,0)-(7,16))
+ ├── name: :a
+ ├── depth: 0
+ ├── name_loc: (7,0)-(7,1) = "a"
+ ├── value:
+ │ @ LocalVariableWriteNode (location: (7,4)-(7,16))
+ │ ├── name: :b
+ │ ├── depth: 0
+ │ ├── name_loc: (7,4)-(7,5) = "b"
+ │ ├── value:
+ │ │ @ CallNode (location: (7,8)-(7,16))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :raise
+ │ │ ├── message_loc: (7,8)-(7,13) = "raise"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (7,14)-(7,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ SymbolNode (location: (7,14)-(7,16))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: (7,14)-(7,15) = ":"
+ │ │ │ ├── value_loc: (7,15)-(7,16) = "x"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "x"
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (7,6)-(7,7) = "="
+ └── operator_loc: (7,2)-(7,3) = "="
diff --git a/test/prism/snapshots/whitequark/ruby_bug_12686.txt b/test/prism/snapshots/whitequark/ruby_bug_12686.txt
new file mode 100644
index 0000000000..427c96bbe0
--- /dev/null
+++ b/test/prism/snapshots/whitequark/ruby_bug_12686.txt
@@ -0,0 +1,39 @@
+@ ProgramNode (location: (1,0)-(1,16))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,16))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,16))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :f
+ ├── message_loc: (1,0)-(1,1) = "f"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,16))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ ParenthesesNode (location: (1,2)-(1,16))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,3)-(1,15))
+ │ │ └── body: (length: 1)
+ │ │ └── @ RescueModifierNode (location: (1,3)-(1,15))
+ │ │ ├── expression:
+ │ │ │ @ CallNode (location: (1,3)-(1,4))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :g
+ │ │ │ ├── message_loc: (1,3)-(1,4) = "g"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── keyword_loc: (1,5)-(1,11) = "rescue"
+ │ │ └── rescue_expression:
+ │ │ @ NilNode (location: (1,12)-(1,15))
+ │ ├── opening_loc: (1,2)-(1,3) = "("
+ │ └── closing_loc: (1,15)-(1,16) = ")"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/ruby_bug_13547.txt b/test/prism/snapshots/whitequark/ruby_bug_13547.txt
new file mode 100644
index 0000000000..eae9587ea0
--- /dev/null
+++ b/test/prism/snapshots/whitequark/ruby_bug_13547.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,9))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,4))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :meth
+ │ ├── message_loc: (1,0)-(1,4) = "meth"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :[]
+ ├── message_loc: (1,4)-(1,6) = "[]"
+ ├── opening_loc: (1,4)-(1,5) = "["
+ ├── arguments: ∅
+ ├── closing_loc: (1,5)-(1,6) = "]"
+ └── block:
+ @ BlockNode (location: (1,7)-(1,9))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── opening_loc: (1,7)-(1,8) = "{"
+ └── closing_loc: (1,8)-(1,9) = "}"
diff --git a/test/prism/snapshots/whitequark/ruby_bug_14690.txt b/test/prism/snapshots/whitequark/ruby_bug_14690.txt
new file mode 100644
index 0000000000..36629797db
--- /dev/null
+++ b/test/prism/snapshots/whitequark/ruby_bug_14690.txt
@@ -0,0 +1,59 @@
+@ ProgramNode (location: (1,0)-(1,23))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,23))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,23))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :let
+ ├── message_loc: (1,0)-(1,3) = "let"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,6))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ ParenthesesNode (location: (1,4)-(1,6))
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,4)-(1,5) = "("
+ │ └── closing_loc: (1,5)-(1,6) = ")"
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,7)-(1,23))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,9)-(1,21))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,9)-(1,21))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (1,9)-(1,10) = "m"
+ │ ├── opening_loc: (1,10)-(1,11) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,11)-(1,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (1,11)-(1,12))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :a
+ │ │ ├── message_loc: (1,11)-(1,12) = "a"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (1,12)-(1,13) = ")"
+ │ └── block:
+ │ @ BlockNode (location: (1,14)-(1,21))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,14)-(1,16) = "do"
+ │ └── closing_loc: (1,18)-(1,21) = "end"
+ ├── opening_loc: (1,7)-(1,8) = "{"
+ └── closing_loc: (1,22)-(1,23) = "}"
diff --git a/test/prism/snapshots/whitequark/ruby_bug_15789.txt b/test/prism/snapshots/whitequark/ruby_bug_15789.txt
new file mode 100644
index 0000000000..db8bc1401f
--- /dev/null
+++ b/test/prism/snapshots/whitequark/ruby_bug_15789.txt
@@ -0,0 +1,120 @@
+@ ProgramNode (location: (1,0)-(3,19))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,19))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,20))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (1,0)-(1,1) = "m"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,2)-(1,20))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ LambdaNode (location: (1,2)-(1,20))
+ │ │ ├── locals: [:a]
+ │ │ ├── operator_loc: (1,2)-(1,4) = "->"
+ │ │ ├── opening_loc: (1,17)-(1,18) = "{"
+ │ │ ├── closing_loc: (1,19)-(1,20) = "}"
+ │ │ ├── parameters:
+ │ │ │ @ BlockParametersNode (location: (1,4)-(1,16))
+ │ │ │ ├── parameters:
+ │ │ │ │ @ ParametersNode (location: (1,5)-(1,15))
+ │ │ │ │ ├── requireds: (length: 0)
+ │ │ │ │ ├── optionals: (length: 1)
+ │ │ │ │ │ └── @ OptionalParameterNode (location: (1,5)-(1,15))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── name: :a
+ │ │ │ │ │ ├── name_loc: (1,5)-(1,6) = "a"
+ │ │ │ │ │ ├── operator_loc: (1,7)-(1,8) = "="
+ │ │ │ │ │ └── value:
+ │ │ │ │ │ @ LambdaNode (location: (1,9)-(1,15))
+ │ │ │ │ │ ├── locals: [:_1]
+ │ │ │ │ │ ├── operator_loc: (1,9)-(1,11) = "->"
+ │ │ │ │ │ ├── opening_loc: (1,11)-(1,12) = "{"
+ │ │ │ │ │ ├── closing_loc: (1,14)-(1,15) = "}"
+ │ │ │ │ │ ├── parameters:
+ │ │ │ │ │ │ @ NumberedParametersNode (location: (1,9)-(1,15))
+ │ │ │ │ │ │ └── maximum: 1
+ │ │ │ │ │ └── body:
+ │ │ │ │ │ @ StatementsNode (location: (1,12)-(1,14))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ LocalVariableReadNode (location: (1,12)-(1,14))
+ │ │ │ │ │ ├── name: :_1
+ │ │ │ │ │ └── depth: 0
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ ├── keywords: (length: 0)
+ │ │ │ │ ├── keyword_rest: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── locals: (length: 0)
+ │ │ │ ├── opening_loc: (1,4)-(1,5) = "("
+ │ │ │ └── closing_loc: (1,15)-(1,16) = ")"
+ │ │ └── body:
+ │ │ @ StatementsNode (location: (1,18)-(1,19))
+ │ │ └── body: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (1,18)-(1,19))
+ │ │ ├── name: :a
+ │ │ └── depth: 0
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (3,0)-(3,19))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :m
+ ├── message_loc: (3,0)-(3,1) = "m"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (3,2)-(3,19))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ LambdaNode (location: (3,2)-(3,19))
+ │ ├── locals: [:a]
+ │ ├── operator_loc: (3,2)-(3,4) = "->"
+ │ ├── opening_loc: (3,16)-(3,17) = "{"
+ │ ├── closing_loc: (3,18)-(3,19) = "}"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (3,4)-(3,15))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (3,5)-(3,14))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 1)
+ │ │ │ │ └── @ OptionalKeywordParameterNode (location: (3,5)-(3,14))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── name_loc: (3,5)-(3,7) = "a:"
+ │ │ │ │ └── value:
+ │ │ │ │ @ LambdaNode (location: (3,8)-(3,14))
+ │ │ │ │ ├── locals: [:_1]
+ │ │ │ │ ├── operator_loc: (3,8)-(3,10) = "->"
+ │ │ │ │ ├── opening_loc: (3,10)-(3,11) = "{"
+ │ │ │ │ ├── closing_loc: (3,13)-(3,14) = "}"
+ │ │ │ │ ├── parameters:
+ │ │ │ │ │ @ NumberedParametersNode (location: (3,8)-(3,14))
+ │ │ │ │ │ └── maximum: 1
+ │ │ │ │ └── body:
+ │ │ │ │ @ StatementsNode (location: (3,11)-(3,13))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ LocalVariableReadNode (location: (3,11)-(3,13))
+ │ │ │ │ ├── name: :_1
+ │ │ │ │ └── depth: 0
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (3,4)-(3,5) = "("
+ │ │ └── closing_loc: (3,14)-(3,15) = ")"
+ │ └── body:
+ │ @ StatementsNode (location: (3,17)-(3,18))
+ │ └── body: (length: 1)
+ │ └── @ LocalVariableReadNode (location: (3,17)-(3,18))
+ │ ├── name: :a
+ │ └── depth: 0
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/ruby_bug_9669.txt b/test/prism/snapshots/whitequark/ruby_bug_9669.txt
new file mode 100644
index 0000000000..910b08c3ef
--- /dev/null
+++ b/test/prism/snapshots/whitequark/ruby_bug_9669.txt
@@ -0,0 +1,59 @@
+@ ProgramNode (location: (1,0)-(8,1))
+├── locals: [:o]
+└── statements:
+ @ StatementsNode (location: (1,0)-(8,1))
+ └── body: (length: 2)
+ ├── @ DefNode (location: (1,0)-(3,3))
+ │ ├── name: :a
+ │ ├── name_loc: (1,4)-(1,5) = "a"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,6)-(1,8))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ RequiredKeywordParameterNode (location: (1,6)-(1,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :b
+ │ │ │ └── name_loc: (1,6)-(1,8) = "b:"
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (2,0)-(2,6))
+ │ │ └── body: (length: 1)
+ │ │ └── @ ReturnNode (location: (2,0)-(2,6))
+ │ │ ├── flags: redundant
+ │ │ ├── keyword_loc: (2,0)-(2,6) = "return"
+ │ │ └── arguments: ∅
+ │ ├── locals: [:b]
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (3,0)-(3,3) = "end"
+ └── @ LocalVariableWriteNode (location: (5,0)-(8,1))
+ ├── name: :o
+ ├── depth: 0
+ ├── name_loc: (5,0)-(5,1) = "o"
+ ├── value:
+ │ @ HashNode (location: (5,4)-(8,1))
+ │ ├── opening_loc: (5,4)-(5,5) = "{"
+ │ ├── elements: (length: 1)
+ │ │ └── @ AssocNode (location: (6,0)-(7,1))
+ │ │ ├── key:
+ │ │ │ @ SymbolNode (location: (6,0)-(6,2))
+ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── value_loc: (6,0)-(6,1) = "a"
+ │ │ │ ├── closing_loc: (6,1)-(6,2) = ":"
+ │ │ │ └── unescaped: "a"
+ │ │ ├── value:
+ │ │ │ @ IntegerNode (location: (7,0)-(7,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── operator_loc: ∅
+ │ └── closing_loc: (8,0)-(8,1) = "}"
+ └── operator_loc: (5,2)-(5,3) = "="
diff --git a/test/prism/snapshots/whitequark/sclass.txt b/test/prism/snapshots/whitequark/sclass.txt
new file mode 100644
index 0000000000..53188a7b8a
--- /dev/null
+++ b/test/prism/snapshots/whitequark/sclass.txt
@@ -0,0 +1,25 @@
+@ ProgramNode (location: (1,0)-(1,22))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,22))
+ └── body: (length: 1)
+ └── @ SingletonClassNode (location: (1,0)-(1,22))
+ ├── locals: []
+ ├── class_keyword_loc: (1,0)-(1,5) = "class"
+ ├── operator_loc: (1,6)-(1,8) = "<<"
+ ├── expression:
+ │ @ CallNode (location: (1,9)-(1,12))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,9)-(1,12) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,14)-(1,17))
+ │ └── body: (length: 1)
+ │ └── @ NilNode (location: (1,14)-(1,17))
+ └── end_keyword_loc: (1,19)-(1,22) = "end"
diff --git a/test/prism/snapshots/whitequark/self.txt b/test/prism/snapshots/whitequark/self.txt
new file mode 100644
index 0000000000..c69f8fa8c5
--- /dev/null
+++ b/test/prism/snapshots/whitequark/self.txt
@@ -0,0 +1,6 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ SelfNode (location: (1,0)-(1,4))
diff --git a/test/prism/snapshots/whitequark/send_attr_asgn.txt b/test/prism/snapshots/whitequark/send_attr_asgn.txt
new file mode 100644
index 0000000000..392ae5587e
--- /dev/null
+++ b/test/prism/snapshots/whitequark/send_attr_asgn.txt
@@ -0,0 +1,106 @@
+@ ProgramNode (location: (1,0)-(7,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(7,10))
+ └── body: (length: 4)
+ ├── @ CallNode (location: (1,0)-(1,9))
+ │ ├── flags: attribute_write
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,0)-(1,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (1,3)-(1,4) = "."
+ │ ├── name: :A=
+ │ ├── message_loc: (1,4)-(1,5) = "A"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,8)-(1,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,8)-(1,9))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (3,0)-(3,9))
+ │ ├── flags: attribute_write
+ │ ├── receiver:
+ │ │ @ CallNode (location: (3,0)-(3,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (3,0)-(3,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (3,3)-(3,4) = "."
+ │ ├── name: :a=
+ │ ├── message_loc: (3,4)-(3,5) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,8)-(3,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (3,8)-(3,9))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ ConstantPathWriteNode (location: (5,0)-(5,10))
+ │ ├── target:
+ │ │ @ ConstantPathNode (location: (5,0)-(5,6))
+ │ │ ├── parent:
+ │ │ │ @ CallNode (location: (5,0)-(5,3))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (5,0)-(5,3) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── child:
+ │ │ │ @ ConstantReadNode (location: (5,5)-(5,6))
+ │ │ │ └── name: :A
+ │ │ └── delimiter_loc: (5,3)-(5,5) = "::"
+ │ ├── operator_loc: (5,7)-(5,8) = "="
+ │ └── value:
+ │ @ IntegerNode (location: (5,9)-(5,10))
+ │ ├── flags: decimal
+ │ └── value: 1
+ └── @ CallNode (location: (7,0)-(7,10))
+ ├── flags: attribute_write
+ ├── receiver:
+ │ @ CallNode (location: (7,0)-(7,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (7,0)-(7,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (7,3)-(7,5) = "::"
+ ├── name: :a=
+ ├── message_loc: (7,5)-(7,6) = "a"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (7,9)-(7,10))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (7,9)-(7,10))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/send_attr_asgn_conditional.txt b/test/prism/snapshots/whitequark/send_attr_asgn_conditional.txt
new file mode 100644
index 0000000000..3cec95ae7c
--- /dev/null
+++ b/test/prism/snapshots/whitequark/send_attr_asgn_conditional.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,8))
+ ├── flags: safe_navigation, attribute_write
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,1)-(1,3) = "&."
+ ├── name: :b=
+ ├── message_loc: (1,3)-(1,4) = "b"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,7)-(1,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (1,7)-(1,8))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/send_binary_op.txt b/test/prism/snapshots/whitequark/send_binary_op.txt
new file mode 100644
index 0000000000..540a7681dc
--- /dev/null
+++ b/test/prism/snapshots/whitequark/send_binary_op.txt
@@ -0,0 +1,551 @@
+@ ProgramNode (location: (1,0)-(41,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(41,7))
+ └── body: (length: 21)
+ ├── @ CallNode (location: (1,0)-(1,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,0)-(1,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!=
+ │ ├── message_loc: (1,4)-(1,6) = "!="
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,7)-(1,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,7)-(1,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (3,0)-(3,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (3,0)-(3,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (3,0)-(3,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :!~
+ │ ├── message_loc: (3,4)-(3,6) = "!~"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,7)-(3,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (3,7)-(3,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (5,0)-(5,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (5,0)-(5,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (5,0)-(5,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :%
+ │ ├── message_loc: (5,4)-(5,5) = "%"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (5,6)-(5,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (5,6)-(5,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (7,0)-(7,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (7,0)-(7,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (7,0)-(7,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :&
+ │ ├── message_loc: (7,4)-(7,5) = "&"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (7,6)-(7,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (7,6)-(7,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (9,0)-(9,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (9,0)-(9,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (9,0)-(9,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :*
+ │ ├── message_loc: (9,4)-(9,5) = "*"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (9,6)-(9,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (9,6)-(9,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (11,0)-(11,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (11,0)-(11,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (11,0)-(11,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :**
+ │ ├── message_loc: (11,4)-(11,6) = "**"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (11,7)-(11,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (11,7)-(11,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (13,0)-(13,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (13,0)-(13,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (13,0)-(13,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+
+ │ ├── message_loc: (13,4)-(13,5) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (13,6)-(13,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (13,6)-(13,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (15,0)-(15,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (15,0)-(15,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (15,0)-(15,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :-
+ │ ├── message_loc: (15,4)-(15,5) = "-"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (15,6)-(15,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (15,6)-(15,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (17,0)-(17,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (17,0)-(17,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (17,0)-(17,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :/
+ │ ├── message_loc: (17,4)-(17,5) = "/"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (17,6)-(17,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (17,6)-(17,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (19,0)-(19,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (19,0)-(19,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (19,0)-(19,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :<
+ │ ├── message_loc: (19,4)-(19,5) = "<"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (19,6)-(19,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (19,6)-(19,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (21,0)-(21,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (21,0)-(21,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (21,0)-(21,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :<<
+ │ ├── message_loc: (21,4)-(21,6) = "<<"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (21,7)-(21,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (21,7)-(21,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (23,0)-(23,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (23,0)-(23,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (23,0)-(23,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :<=
+ │ ├── message_loc: (23,4)-(23,6) = "<="
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (23,7)-(23,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (23,7)-(23,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (25,0)-(25,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (25,0)-(25,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (25,0)-(25,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :<=>
+ │ ├── message_loc: (25,4)-(25,7) = "<=>"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (25,8)-(25,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (25,8)-(25,9))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (27,0)-(27,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (27,0)-(27,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (27,0)-(27,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :==
+ │ ├── message_loc: (27,4)-(27,6) = "=="
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (27,7)-(27,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (27,7)-(27,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (29,0)-(29,9))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (29,0)-(29,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (29,0)-(29,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :===
+ │ ├── message_loc: (29,4)-(29,7) = "==="
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (29,8)-(29,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (29,8)-(29,9))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (31,0)-(31,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (31,0)-(31,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (31,0)-(31,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :=~
+ │ ├── message_loc: (31,4)-(31,6) = "=~"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (31,7)-(31,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (31,7)-(31,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (33,0)-(33,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (33,0)-(33,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (33,0)-(33,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :>
+ │ ├── message_loc: (33,4)-(33,5) = ">"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (33,6)-(33,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (33,6)-(33,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (35,0)-(35,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (35,0)-(35,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (35,0)-(35,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :>=
+ │ ├── message_loc: (35,4)-(35,6) = ">="
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (35,7)-(35,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (35,7)-(35,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (37,0)-(37,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (37,0)-(37,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (37,0)-(37,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :>>
+ │ ├── message_loc: (37,4)-(37,6) = ">>"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (37,7)-(37,8))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (37,7)-(37,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (39,0)-(39,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (39,0)-(39,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (39,0)-(39,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :^
+ │ ├── message_loc: (39,4)-(39,5) = "^"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (39,6)-(39,7))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (39,6)-(39,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (41,0)-(41,7))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (41,0)-(41,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (41,0)-(41,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :|
+ ├── message_loc: (41,4)-(41,5) = "|"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (41,6)-(41,7))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (41,6)-(41,7))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/send_block_chain_cmd.txt b/test/prism/snapshots/whitequark/send_block_chain_cmd.txt
new file mode 100644
index 0000000000..4013888882
--- /dev/null
+++ b/test/prism/snapshots/whitequark/send_block_chain_cmd.txt
@@ -0,0 +1,325 @@
+@ ProgramNode (location: (1,0)-(13,23))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(13,23))
+ └── body: (length: 7)
+ ├── @ CallNode (location: (1,0)-(1,21))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,0)-(1,13))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :meth
+ │ │ ├── message_loc: (1,0)-(1,4) = "meth"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (1,5)-(1,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (1,5)-(1,6))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (1,7)-(1,13))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (1,7)-(1,9) = "do"
+ │ │ └── closing_loc: (1,10)-(1,13) = "end"
+ │ ├── call_operator_loc: (1,13)-(1,14) = "."
+ │ ├── name: :fun
+ │ ├── message_loc: (1,14)-(1,17) = "fun"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,18)-(1,21))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (1,18)-(1,21))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,18)-(1,21) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (3,0)-(3,28))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (3,0)-(3,13))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :meth
+ │ │ ├── message_loc: (3,0)-(3,4) = "meth"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (3,5)-(3,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (3,5)-(3,6))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (3,7)-(3,13))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (3,7)-(3,9) = "do"
+ │ │ └── closing_loc: (3,10)-(3,13) = "end"
+ │ ├── call_operator_loc: (3,13)-(3,14) = "."
+ │ ├── name: :fun
+ │ ├── message_loc: (3,14)-(3,17) = "fun"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,18)-(3,21))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (3,18)-(3,21))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (3,18)-(3,21) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (3,22)-(3,28))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (3,22)-(3,24) = "do"
+ │ └── closing_loc: (3,25)-(3,28) = "end"
+ ├── @ CallNode (location: (5,0)-(5,20))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (5,0)-(5,13))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :meth
+ │ │ ├── message_loc: (5,0)-(5,4) = "meth"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (5,5)-(5,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (5,5)-(5,6))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (5,7)-(5,13))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (5,7)-(5,9) = "do"
+ │ │ └── closing_loc: (5,10)-(5,13) = "end"
+ │ ├── call_operator_loc: (5,13)-(5,14) = "."
+ │ ├── name: :fun
+ │ ├── message_loc: (5,14)-(5,17) = "fun"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (5,18)-(5,20))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (5,18)-(5,19) = "{"
+ │ └── closing_loc: (5,19)-(5,20) = "}"
+ ├── @ CallNode (location: (7,0)-(7,22))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (7,0)-(7,13))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :meth
+ │ │ ├── message_loc: (7,0)-(7,4) = "meth"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (7,5)-(7,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (7,5)-(7,6))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (7,7)-(7,13))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (7,7)-(7,9) = "do"
+ │ │ └── closing_loc: (7,10)-(7,13) = "end"
+ │ ├── call_operator_loc: (7,13)-(7,14) = "."
+ │ ├── name: :fun
+ │ ├── message_loc: (7,14)-(7,17) = "fun"
+ │ ├── opening_loc: (7,17)-(7,18) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (7,18)-(7,21))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (7,18)-(7,21))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (7,18)-(7,21) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (7,21)-(7,22) = ")"
+ │ └── block: ∅
+ ├── @ CallNode (location: (9,0)-(9,25))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (9,0)-(9,13))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :meth
+ │ │ ├── message_loc: (9,0)-(9,4) = "meth"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (9,5)-(9,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (9,5)-(9,6))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (9,7)-(9,13))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (9,7)-(9,9) = "do"
+ │ │ └── closing_loc: (9,10)-(9,13) = "end"
+ │ ├── call_operator_loc: (9,13)-(9,14) = "."
+ │ ├── name: :fun
+ │ ├── message_loc: (9,14)-(9,17) = "fun"
+ │ ├── opening_loc: (9,17)-(9,18) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (9,18)-(9,21))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (9,18)-(9,21))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (9,18)-(9,21) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: (9,21)-(9,22) = ")"
+ │ └── block:
+ │ @ BlockNode (location: (9,23)-(9,25))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (9,23)-(9,24) = "{"
+ │ └── closing_loc: (9,24)-(9,25) = "}"
+ ├── @ CallNode (location: (11,0)-(11,22))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (11,0)-(11,13))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :meth
+ │ │ ├── message_loc: (11,0)-(11,4) = "meth"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (11,5)-(11,6))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (11,5)-(11,6))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (11,7)-(11,13))
+ │ │ ├── locals: []
+ │ │ ├── parameters: ∅
+ │ │ ├── body: ∅
+ │ │ ├── opening_loc: (11,7)-(11,9) = "do"
+ │ │ └── closing_loc: (11,10)-(11,13) = "end"
+ │ ├── call_operator_loc: (11,13)-(11,15) = "::"
+ │ ├── name: :fun
+ │ ├── message_loc: (11,15)-(11,18) = "fun"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (11,19)-(11,22))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (11,19)-(11,22))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (11,19)-(11,22) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (13,0)-(13,23))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (13,0)-(13,13))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :meth
+ │ ├── message_loc: (13,0)-(13,4) = "meth"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (13,5)-(13,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (13,5)-(13,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (13,7)-(13,13))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (13,7)-(13,9) = "do"
+ │ └── closing_loc: (13,10)-(13,13) = "end"
+ ├── call_operator_loc: (13,13)-(13,15) = "::"
+ ├── name: :fun
+ ├── message_loc: (13,15)-(13,18) = "fun"
+ ├── opening_loc: (13,18)-(13,19) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (13,19)-(13,22))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (13,19)-(13,22))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (13,19)-(13,22) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: (13,22)-(13,23) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/send_block_conditional.txt b/test/prism/snapshots/whitequark/send_block_conditional.txt
new file mode 100644
index 0000000000..826d3c8464
--- /dev/null
+++ b/test/prism/snapshots/whitequark/send_block_conditional.txt
@@ -0,0 +1,31 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,11))
+ ├── flags: safe_navigation
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,3)-(1,5) = "&."
+ ├── name: :bar
+ ├── message_loc: (1,5)-(1,8) = "bar"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,9)-(1,11))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── opening_loc: (1,9)-(1,10) = "{"
+ └── closing_loc: (1,10)-(1,11) = "}"
diff --git a/test/prism/snapshots/whitequark/send_call.txt b/test/prism/snapshots/whitequark/send_call.txt
new file mode 100644
index 0000000000..48063e0121
--- /dev/null
+++ b/test/prism/snapshots/whitequark/send_call.txt
@@ -0,0 +1,57 @@
+@ ProgramNode (location: (1,0)-(3,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,8))
+ └── body: (length: 2)
+ ├── @ CallNode (location: (1,0)-(1,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,0)-(1,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (1,3)-(1,4) = "."
+ │ ├── name: :call
+ │ ├── message_loc: ∅
+ │ ├── opening_loc: (1,4)-(1,5) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,5)-(1,6))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,5)-(1,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── closing_loc: (1,6)-(1,7) = ")"
+ │ └── block: ∅
+ └── @ CallNode (location: (3,0)-(3,8))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (3,0)-(3,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (3,0)-(3,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (3,3)-(3,5) = "::"
+ ├── name: :call
+ ├── message_loc: ∅
+ ├── opening_loc: (3,5)-(3,6) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (3,6)-(3,7))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (3,6)-(3,7))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: (3,7)-(3,8) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/send_conditional.txt b/test/prism/snapshots/whitequark/send_conditional.txt
new file mode 100644
index 0000000000..7b402d9ef2
--- /dev/null
+++ b/test/prism/snapshots/whitequark/send_conditional.txt
@@ -0,0 +1,25 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,4))
+ ├── flags: safe_navigation
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,1)-(1,3) = "&."
+ ├── name: :b
+ ├── message_loc: (1,3)-(1,4) = "b"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/send_index.txt b/test/prism/snapshots/whitequark/send_index.txt
new file mode 100644
index 0000000000..6c9e08f2ea
--- /dev/null
+++ b/test/prism/snapshots/whitequark/send_index.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,9))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :[]
+ ├── message_loc: (1,3)-(1,9) = "[1, 2]"
+ ├── opening_loc: (1,3)-(1,4) = "["
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ IntegerNode (location: (1,4)-(1,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── @ IntegerNode (location: (1,7)-(1,8))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── closing_loc: (1,8)-(1,9) = "]"
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/send_index_asgn.txt b/test/prism/snapshots/whitequark/send_index_asgn.txt
new file mode 100644
index 0000000000..9d2e6efcb6
--- /dev/null
+++ b/test/prism/snapshots/whitequark/send_index_asgn.txt
@@ -0,0 +1,37 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,13))
+ ├── flags: attribute_write
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :[]=
+ ├── message_loc: (1,3)-(1,9) = "[1, 2]"
+ ├── opening_loc: (1,3)-(1,4) = "["
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,13))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 3)
+ │ ├── @ IntegerNode (location: (1,4)-(1,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── @ IntegerNode (location: (1,7)-(1,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ └── @ IntegerNode (location: (1,12)-(1,13))
+ │ ├── flags: decimal
+ │ └── value: 3
+ ├── closing_loc: (1,8)-(1,9) = "]"
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/send_index_asgn_legacy.txt b/test/prism/snapshots/whitequark/send_index_asgn_legacy.txt
new file mode 100644
index 0000000000..9d2e6efcb6
--- /dev/null
+++ b/test/prism/snapshots/whitequark/send_index_asgn_legacy.txt
@@ -0,0 +1,37 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,13))
+ ├── flags: attribute_write
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :[]=
+ ├── message_loc: (1,3)-(1,9) = "[1, 2]"
+ ├── opening_loc: (1,3)-(1,4) = "["
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,13))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 3)
+ │ ├── @ IntegerNode (location: (1,4)-(1,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── @ IntegerNode (location: (1,7)-(1,8))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ └── @ IntegerNode (location: (1,12)-(1,13))
+ │ ├── flags: decimal
+ │ └── value: 3
+ ├── closing_loc: (1,8)-(1,9) = "]"
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/send_index_cmd.txt b/test/prism/snapshots/whitequark/send_index_cmd.txt
new file mode 100644
index 0000000000..0a41fd051d
--- /dev/null
+++ b/test/prism/snapshots/whitequark/send_index_cmd.txt
@@ -0,0 +1,51 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,10))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :[]
+ ├── message_loc: (1,3)-(1,10) = "[m bar]"
+ ├── opening_loc: (1,3)-(1,4) = "["
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,9))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (1,4)-(1,9))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (1,4)-(1,5) = "m"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,6)-(1,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (1,6)-(1,9))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,6)-(1,9) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: (1,9)-(1,10) = "]"
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/send_index_legacy.txt b/test/prism/snapshots/whitequark/send_index_legacy.txt
new file mode 100644
index 0000000000..6c9e08f2ea
--- /dev/null
+++ b/test/prism/snapshots/whitequark/send_index_legacy.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,9))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :[]
+ ├── message_loc: (1,3)-(1,9) = "[1, 2]"
+ ├── opening_loc: (1,3)-(1,4) = "["
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,8))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ IntegerNode (location: (1,4)-(1,5))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── @ IntegerNode (location: (1,7)-(1,8))
+ │ ├── flags: decimal
+ │ └── value: 2
+ ├── closing_loc: (1,8)-(1,9) = "]"
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/send_lambda.txt b/test/prism/snapshots/whitequark/send_lambda.txt
new file mode 100644
index 0000000000..bf21700539
--- /dev/null
+++ b/test/prism/snapshots/whitequark/send_lambda.txt
@@ -0,0 +1,44 @@
+@ ProgramNode (location: (1,0)-(5,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,5))
+ └── body: (length: 3)
+ ├── @ LambdaNode (location: (1,0)-(1,8))
+ │ ├── locals: []
+ │ ├── operator_loc: (1,0)-(1,2) = "->"
+ │ ├── opening_loc: (1,5)-(1,6) = "{"
+ │ ├── closing_loc: (1,7)-(1,8) = "}"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (1,3)-(1,4))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (1,3)-(1,4))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest:
+ │ │ │ │ @ RestParameterNode (location: (1,3)-(1,4))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: ∅
+ │ │ │ │ ├── name_loc: ∅
+ │ │ │ │ └── operator_loc: (1,3)-(1,4) = "*"
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── body: ∅
+ ├── @ LambdaNode (location: (3,0)-(3,9))
+ │ ├── locals: []
+ │ ├── operator_loc: (3,0)-(3,2) = "->"
+ │ ├── opening_loc: (3,3)-(3,5) = "do"
+ │ ├── closing_loc: (3,6)-(3,9) = "end"
+ │ ├── parameters: ∅
+ │ └── body: ∅
+ └── @ LambdaNode (location: (5,0)-(5,5))
+ ├── locals: []
+ ├── operator_loc: (5,0)-(5,2) = "->"
+ ├── opening_loc: (5,2)-(5,3) = "{"
+ ├── closing_loc: (5,4)-(5,5) = "}"
+ ├── parameters: ∅
+ └── body: ∅
diff --git a/test/prism/snapshots/whitequark/send_lambda_args.txt b/test/prism/snapshots/whitequark/send_lambda_args.txt
new file mode 100644
index 0000000000..f61f622bdd
--- /dev/null
+++ b/test/prism/snapshots/whitequark/send_lambda_args.txt
@@ -0,0 +1,51 @@
+@ ProgramNode (location: (1,0)-(3,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,9))
+ └── body: (length: 2)
+ ├── @ LambdaNode (location: (1,0)-(1,10))
+ │ ├── locals: [:a]
+ │ ├── operator_loc: (1,0)-(1,2) = "->"
+ │ ├── opening_loc: (1,7)-(1,8) = "{"
+ │ ├── closing_loc: (1,9)-(1,10) = "}"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (1,3)-(1,6))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (1,4)-(1,5))
+ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ └── @ RequiredParameterNode (location: (1,4)-(1,5))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── name: :a
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 0)
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: (1,3)-(1,4) = "("
+ │ │ └── closing_loc: (1,5)-(1,6) = ")"
+ │ └── body: ∅
+ └── @ LambdaNode (location: (3,0)-(3,9))
+ ├── locals: [:a]
+ ├── operator_loc: (3,0)-(3,2) = "->"
+ ├── opening_loc: (3,6)-(3,7) = "{"
+ ├── closing_loc: (3,8)-(3,9) = "}"
+ ├── parameters:
+ │ @ BlockParametersNode (location: (3,2)-(3,5))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (3,3)-(3,4))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (3,3)-(3,4))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: (3,2)-(3,3) = "("
+ │ └── closing_loc: (3,4)-(3,5) = ")"
+ └── body: ∅
diff --git a/test/prism/snapshots/whitequark/send_lambda_args_noparen.txt b/test/prism/snapshots/whitequark/send_lambda_args_noparen.txt
new file mode 100644
index 0000000000..747656af6b
--- /dev/null
+++ b/test/prism/snapshots/whitequark/send_lambda_args_noparen.txt
@@ -0,0 +1,57 @@
+@ ProgramNode (location: (1,0)-(3,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,9))
+ └── body: (length: 2)
+ ├── @ LambdaNode (location: (1,0)-(1,11))
+ │ ├── locals: [:a]
+ │ ├── operator_loc: (1,0)-(1,2) = "->"
+ │ ├── opening_loc: (1,8)-(1,9) = "{"
+ │ ├── closing_loc: (1,10)-(1,11) = "}"
+ │ ├── parameters:
+ │ │ @ BlockParametersNode (location: (1,3)-(1,7))
+ │ │ ├── parameters:
+ │ │ │ @ ParametersNode (location: (1,3)-(1,7))
+ │ │ │ ├── requireds: (length: 0)
+ │ │ │ ├── optionals: (length: 0)
+ │ │ │ ├── rest: ∅
+ │ │ │ ├── posts: (length: 0)
+ │ │ │ ├── keywords: (length: 1)
+ │ │ │ │ └── @ OptionalKeywordParameterNode (location: (1,3)-(1,7))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── name_loc: (1,3)-(1,5) = "a:"
+ │ │ │ │ └── value:
+ │ │ │ │ @ IntegerNode (location: (1,6)-(1,7))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── keyword_rest: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── locals: (length: 0)
+ │ │ ├── opening_loc: ∅
+ │ │ └── closing_loc: ∅
+ │ └── body: ∅
+ └── @ LambdaNode (location: (3,0)-(3,9))
+ ├── locals: [:a]
+ ├── operator_loc: (3,0)-(3,2) = "->"
+ ├── opening_loc: (3,6)-(3,7) = "{"
+ ├── closing_loc: (3,8)-(3,9) = "}"
+ ├── parameters:
+ │ @ BlockParametersNode (location: (3,3)-(3,5))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (3,3)-(3,5))
+ │ │ ├── requireds: (length: 0)
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 1)
+ │ │ │ └── @ RequiredKeywordParameterNode (location: (3,3)-(3,5))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: :a
+ │ │ │ └── name_loc: (3,3)-(3,5) = "a:"
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 0)
+ │ ├── opening_loc: ∅
+ │ └── closing_loc: ∅
+ └── body: ∅
diff --git a/test/prism/snapshots/whitequark/send_lambda_args_shadow.txt b/test/prism/snapshots/whitequark/send_lambda_args_shadow.txt
new file mode 100644
index 0000000000..34a63ec503
--- /dev/null
+++ b/test/prism/snapshots/whitequark/send_lambda_args_shadow.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,19))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,19))
+ └── body: (length: 1)
+ └── @ LambdaNode (location: (1,0)-(1,19))
+ ├── locals: [:a, :foo, :bar]
+ ├── operator_loc: (1,0)-(1,2) = "->"
+ ├── opening_loc: (1,16)-(1,17) = "{"
+ ├── closing_loc: (1,18)-(1,19) = "}"
+ ├── parameters:
+ │ @ BlockParametersNode (location: (1,2)-(1,15))
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (1,3)-(1,4))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (1,3)-(1,4))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest: ∅
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── locals: (length: 2)
+ │ │ ├── @ BlockLocalVariableNode (location: (1,6)-(1,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :foo
+ │ │ └── @ BlockLocalVariableNode (location: (1,11)-(1,14))
+ │ │ ├── flags: ∅
+ │ │ └── name: :bar
+ │ ├── opening_loc: (1,2)-(1,3) = "("
+ │ └── closing_loc: (1,14)-(1,15) = ")"
+ └── body: ∅
diff --git a/test/prism/snapshots/whitequark/send_lambda_legacy.txt b/test/prism/snapshots/whitequark/send_lambda_legacy.txt
new file mode 100644
index 0000000000..3a64e941b6
--- /dev/null
+++ b/test/prism/snapshots/whitequark/send_lambda_legacy.txt
@@ -0,0 +1,12 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ LambdaNode (location: (1,0)-(1,5))
+ ├── locals: []
+ ├── operator_loc: (1,0)-(1,2) = "->"
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ ├── closing_loc: (1,4)-(1,5) = "}"
+ ├── parameters: ∅
+ └── body: ∅
diff --git a/test/prism/snapshots/whitequark/send_op_asgn_conditional.txt b/test/prism/snapshots/whitequark/send_op_asgn_conditional.txt
new file mode 100644
index 0000000000..05966fc5a3
--- /dev/null
+++ b/test/prism/snapshots/whitequark/send_op_asgn_conditional.txt
@@ -0,0 +1,27 @@
+@ ProgramNode (location: (1,0)-(1,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,10))
+ └── body: (length: 1)
+ └── @ CallAndWriteNode (location: (1,0)-(1,10))
+ ├── flags: safe_navigation
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :a
+ │ ├── message_loc: (1,0)-(1,1) = "a"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (1,1)-(1,3) = "&."
+ ├── message_loc: (1,3)-(1,4) = "b"
+ ├── read_name: :b
+ ├── write_name: :b=
+ ├── operator_loc: (1,5)-(1,8) = "&&="
+ └── value:
+ @ IntegerNode (location: (1,9)-(1,10))
+ ├── flags: decimal
+ └── value: 1
diff --git a/test/prism/snapshots/whitequark/send_plain.txt b/test/prism/snapshots/whitequark/send_plain.txt
new file mode 100644
index 0000000000..be57bee5a0
--- /dev/null
+++ b/test/prism/snapshots/whitequark/send_plain.txt
@@ -0,0 +1,65 @@
+@ ProgramNode (location: (1,0)-(5,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,8))
+ └── body: (length: 3)
+ ├── @ CallNode (location: (1,0)-(1,7))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,0)-(1,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (1,3)-(1,4) = "."
+ │ ├── name: :fun
+ │ ├── message_loc: (1,4)-(1,7) = "fun"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (3,0)-(3,10))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (3,0)-(3,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (3,0)-(3,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (3,3)-(3,5) = "::"
+ │ ├── name: :Fun
+ │ ├── message_loc: (3,5)-(3,8) = "Fun"
+ │ ├── opening_loc: (3,8)-(3,9) = "("
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (3,9)-(3,10) = ")"
+ │ └── block: ∅
+ └── @ CallNode (location: (5,0)-(5,8))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (5,0)-(5,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (5,0)-(5,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (5,3)-(5,5) = "::"
+ ├── name: :fun
+ ├── message_loc: (5,5)-(5,8) = "fun"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/send_plain_cmd.txt b/test/prism/snapshots/whitequark/send_plain_cmd.txt
new file mode 100644
index 0000000000..59236e114d
--- /dev/null
+++ b/test/prism/snapshots/whitequark/send_plain_cmd.txt
@@ -0,0 +1,104 @@
+@ ProgramNode (location: (1,0)-(5,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,12))
+ └── body: (length: 3)
+ ├── @ CallNode (location: (1,0)-(1,11))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,0)-(1,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (1,3)-(1,4) = "."
+ │ ├── name: :fun
+ │ ├── message_loc: (1,4)-(1,7) = "fun"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,8)-(1,11))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (1,8)-(1,11))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,8)-(1,11) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (3,0)-(3,12))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (3,0)-(3,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (3,0)-(3,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (3,3)-(3,5) = "::"
+ │ ├── name: :Fun
+ │ ├── message_loc: (3,5)-(3,8) = "Fun"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,9)-(3,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (3,9)-(3,12))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (3,9)-(3,12) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (5,0)-(5,12))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (5,0)-(5,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (5,0)-(5,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: (5,3)-(5,5) = "::"
+ ├── name: :fun
+ ├── message_loc: (5,5)-(5,8) = "fun"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (5,9)-(5,12))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (5,9)-(5,12))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (5,9)-(5,12) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/send_self.txt b/test/prism/snapshots/whitequark/send_self.txt
new file mode 100644
index 0000000000..41fd822110
--- /dev/null
+++ b/test/prism/snapshots/whitequark/send_self.txt
@@ -0,0 +1,41 @@
+@ ProgramNode (location: (1,0)-(5,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,6))
+ └── body: (length: 3)
+ ├── @ CallNode (location: (1,0)-(1,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :fun
+ │ ├── message_loc: (1,0)-(1,3) = "fun"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (3,0)-(3,4))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :fun!
+ │ ├── message_loc: (3,0)-(3,4) = "fun!"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (5,0)-(5,6))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :fun
+ ├── message_loc: (5,0)-(5,3) = "fun"
+ ├── opening_loc: (5,3)-(5,4) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (5,4)-(5,5))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (5,4)-(5,5))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: (5,5)-(5,6) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/send_self_block.txt b/test/prism/snapshots/whitequark/send_self_block.txt
new file mode 100644
index 0000000000..c92935603b
--- /dev/null
+++ b/test/prism/snapshots/whitequark/send_self_block.txt
@@ -0,0 +1,75 @@
+@ ProgramNode (location: (1,0)-(7,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(7,10))
+ └── body: (length: 4)
+ ├── @ CallNode (location: (1,0)-(1,10))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :fun
+ │ ├── message_loc: (1,0)-(1,3) = "fun"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,4)-(1,10))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,4)-(1,6) = "do"
+ │ └── closing_loc: (1,7)-(1,10) = "end"
+ ├── @ CallNode (location: (3,0)-(3,7))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :fun
+ │ ├── message_loc: (3,0)-(3,3) = "fun"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (3,4)-(3,7))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (3,4)-(3,5) = "{"
+ │ └── closing_loc: (3,6)-(3,7) = "}"
+ ├── @ CallNode (location: (5,0)-(5,9))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :fun
+ │ ├── message_loc: (5,0)-(5,3) = "fun"
+ │ ├── opening_loc: (5,3)-(5,4) = "("
+ │ ├── arguments: ∅
+ │ ├── closing_loc: (5,4)-(5,5) = ")"
+ │ └── block:
+ │ @ BlockNode (location: (5,6)-(5,9))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (5,6)-(5,7) = "{"
+ │ └── closing_loc: (5,8)-(5,9) = "}"
+ └── @ CallNode (location: (7,0)-(7,10))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :fun
+ ├── message_loc: (7,0)-(7,3) = "fun"
+ ├── opening_loc: (7,3)-(7,4) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (7,4)-(7,5))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ IntegerNode (location: (7,4)-(7,5))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── closing_loc: (7,5)-(7,6) = ")"
+ └── block:
+ @ BlockNode (location: (7,7)-(7,10))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── opening_loc: (7,7)-(7,8) = "{"
+ └── closing_loc: (7,9)-(7,10) = "}"
diff --git a/test/prism/snapshots/whitequark/send_unary_op.txt b/test/prism/snapshots/whitequark/send_unary_op.txt
new file mode 100644
index 0000000000..8ca1de7968
--- /dev/null
+++ b/test/prism/snapshots/whitequark/send_unary_op.txt
@@ -0,0 +1,65 @@
+@ ProgramNode (location: (1,0)-(5,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,4))
+ └── body: (length: 3)
+ ├── @ CallNode (location: (1,0)-(1,4))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,1)-(1,4))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,1)-(1,4) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :+@
+ │ ├── message_loc: (1,0)-(1,1) = "+"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (3,0)-(3,4))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (3,1)-(3,4))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (3,1)-(3,4) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :-@
+ │ ├── message_loc: (3,0)-(3,1) = "-"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (5,0)-(5,4))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (5,1)-(5,4))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (5,1)-(5,4) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :~
+ ├── message_loc: (5,0)-(5,1) = "~"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/slash_newline_in_heredocs.txt b/test/prism/snapshots/whitequark/slash_newline_in_heredocs.txt
new file mode 100644
index 0000000000..ba1fce0c68
--- /dev/null
+++ b/test/prism/snapshots/whitequark/slash_newline_in_heredocs.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(8,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(8,4))
+ └── body: (length: 2)
+ ├── @ StringNode (location: (1,0)-(1,4))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,4) = "<<-E"
+ │ ├── content_loc: (2,0)-(5,0) = " 1 \\\n 2\n 3\n"
+ │ ├── closing_loc: (5,0)-(6,0) = "E\n"
+ │ └── unescaped: " 1 2\n 3\n"
+ └── @ InterpolatedStringNode (location: (8,0)-(8,4))
+ ├── flags: ∅
+ ├── opening_loc: (8,0)-(8,4) = "<<~E"
+ ├── parts: (length: 3)
+ │ ├── @ StringNode (location: (9,0)-(10,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (9,0)-(10,0) = " 1 \\\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "1 "
+ │ ├── @ StringNode (location: (10,0)-(11,0))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (10,0)-(11,0) = " 2\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "2\n"
+ │ └── @ StringNode (location: (11,0)-(12,0))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (11,0)-(12,0) = " 3\n"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "3\n"
+ └── closing_loc: (12,0)-(13,0) = "E\n"
diff --git a/test/prism/snapshots/whitequark/space_args_arg.txt b/test/prism/snapshots/whitequark/space_args_arg.txt
new file mode 100644
index 0000000000..55750d2b61
--- /dev/null
+++ b/test/prism/snapshots/whitequark/space_args_arg.txt
@@ -0,0 +1,27 @@
+@ ProgramNode (location: (1,0)-(1,7))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,7))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,7))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :fun
+ ├── message_loc: (1,0)-(1,3) = "fun"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,7))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ ParenthesesNode (location: (1,4)-(1,7))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,5)-(1,6))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,5)-(1,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── opening_loc: (1,4)-(1,5) = "("
+ │ └── closing_loc: (1,6)-(1,7) = ")"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/space_args_arg_block.txt b/test/prism/snapshots/whitequark/space_args_arg_block.txt
new file mode 100644
index 0000000000..a6224bcca1
--- /dev/null
+++ b/test/prism/snapshots/whitequark/space_args_arg_block.txt
@@ -0,0 +1,109 @@
+@ ProgramNode (location: (1,0)-(5,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,10))
+ └── body: (length: 3)
+ ├── @ CallNode (location: (1,0)-(1,14))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (1,0)-(1,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (1,3)-(1,4) = "."
+ │ ├── name: :fun
+ │ ├── message_loc: (1,4)-(1,7) = "fun"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,8)-(1,11))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ ParenthesesNode (location: (1,8)-(1,11))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (1,9)-(1,10))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (1,9)-(1,10))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── opening_loc: (1,8)-(1,9) = "("
+ │ │ └── closing_loc: (1,10)-(1,11) = ")"
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,12)-(1,14))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,12)-(1,13) = "{"
+ │ └── closing_loc: (1,13)-(1,14) = "}"
+ ├── @ CallNode (location: (3,0)-(3,15))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (3,0)-(3,3))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (3,0)-(3,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: (3,3)-(3,5) = "::"
+ │ ├── name: :fun
+ │ ├── message_loc: (3,5)-(3,8) = "fun"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,9)-(3,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ ParenthesesNode (location: (3,9)-(3,12))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (3,10)-(3,11))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (3,10)-(3,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── opening_loc: (3,9)-(3,10) = "("
+ │ │ └── closing_loc: (3,11)-(3,12) = ")"
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (3,13)-(3,15))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (3,13)-(3,14) = "{"
+ │ └── closing_loc: (3,14)-(3,15) = "}"
+ └── @ CallNode (location: (5,0)-(5,10))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :fun
+ ├── message_loc: (5,0)-(5,3) = "fun"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (5,4)-(5,7))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ ParenthesesNode (location: (5,4)-(5,7))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (5,5)-(5,6))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (5,5)-(5,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── opening_loc: (5,4)-(5,5) = "("
+ │ └── closing_loc: (5,6)-(5,7) = ")"
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (5,8)-(5,10))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── opening_loc: (5,8)-(5,9) = "{"
+ └── closing_loc: (5,9)-(5,10) = "}"
diff --git a/test/prism/snapshots/whitequark/space_args_arg_call.txt b/test/prism/snapshots/whitequark/space_args_arg_call.txt
new file mode 100644
index 0000000000..767b099a8b
--- /dev/null
+++ b/test/prism/snapshots/whitequark/space_args_arg_call.txt
@@ -0,0 +1,37 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,12))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :fun
+ ├── message_loc: (1,0)-(1,3) = "fun"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,12))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (1,4)-(1,12))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ ParenthesesNode (location: (1,4)-(1,7))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (1,5)-(1,6))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (1,5)-(1,6))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── opening_loc: (1,4)-(1,5) = "("
+ │ │ └── closing_loc: (1,6)-(1,7) = ")"
+ │ ├── call_operator_loc: (1,7)-(1,8) = "."
+ │ ├── name: :to_i
+ │ ├── message_loc: (1,8)-(1,12) = "to_i"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/space_args_arg_newline.txt b/test/prism/snapshots/whitequark/space_args_arg_newline.txt
new file mode 100644
index 0000000000..7727a5ddd9
--- /dev/null
+++ b/test/prism/snapshots/whitequark/space_args_arg_newline.txt
@@ -0,0 +1,27 @@
+@ ProgramNode (location: (1,0)-(2,1))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(2,1))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(2,1))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :fun
+ ├── message_loc: (1,0)-(1,3) = "fun"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(2,1))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ ParenthesesNode (location: (1,4)-(2,1))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,5)-(1,6))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,5)-(1,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── opening_loc: (1,4)-(1,5) = "("
+ │ └── closing_loc: (2,0)-(2,1) = ")"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/space_args_block.txt b/test/prism/snapshots/whitequark/space_args_block.txt
new file mode 100644
index 0000000000..62b549a674
--- /dev/null
+++ b/test/prism/snapshots/whitequark/space_args_block.txt
@@ -0,0 +1,28 @@
+@ ProgramNode (location: (1,0)-(1,9))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,9))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,9))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :fun
+ ├── message_loc: (1,0)-(1,3) = "fun"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,6))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ ParenthesesNode (location: (1,4)-(1,6))
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,4)-(1,5) = "("
+ │ └── closing_loc: (1,5)-(1,6) = ")"
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,7)-(1,9))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── opening_loc: (1,7)-(1,8) = "{"
+ └── closing_loc: (1,8)-(1,9) = "}"
diff --git a/test/prism/snapshots/whitequark/space_args_cmd.txt b/test/prism/snapshots/whitequark/space_args_cmd.txt
new file mode 100644
index 0000000000..8a75bef6a8
--- /dev/null
+++ b/test/prism/snapshots/whitequark/space_args_cmd.txt
@@ -0,0 +1,47 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,11))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :fun
+ ├── message_loc: (1,0)-(1,3) = "fun"
+ ├── opening_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,4)-(1,11))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ ParenthesesNode (location: (1,4)-(1,11))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,5)-(1,10))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,5)-(1,10))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :f
+ │ │ ├── message_loc: (1,5)-(1,6) = "f"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (1,7)-(1,10))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ CallNode (location: (1,7)-(1,10))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (1,7)-(1,10) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (1,4)-(1,5) = "("
+ │ └── closing_loc: (1,10)-(1,11) = ")"
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/string___FILE__.txt b/test/prism/snapshots/whitequark/string___FILE__.txt
new file mode 100644
index 0000000000..a12499a631
--- /dev/null
+++ b/test/prism/snapshots/whitequark/string___FILE__.txt
@@ -0,0 +1,8 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ SourceFileNode (location: (1,0)-(1,8))
+ ├── flags: ∅
+ └── filepath: "whitequark/string___FILE__.txt"
diff --git a/test/prism/snapshots/whitequark/string_concat.txt b/test/prism/snapshots/whitequark/string_concat.txt
new file mode 100644
index 0000000000..f7f7bf9723
--- /dev/null
+++ b/test/prism/snapshots/whitequark/string_concat.txt
@@ -0,0 +1,32 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(1,14))
+ ├── flags: ∅
+ ├── opening_loc: ∅
+ ├── parts: (length: 2)
+ │ ├── @ InterpolatedStringNode (location: (1,0)-(1,8))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: (1,0)-(1,1) = "\""
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (1,1)-(1,4))
+ │ │ │ │ ├── flags: frozen
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (1,1)-(1,4) = "foo"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "foo"
+ │ │ │ └── @ EmbeddedVariableNode (location: (1,4)-(1,7))
+ │ │ │ ├── operator_loc: (1,4)-(1,5) = "#"
+ │ │ │ └── variable:
+ │ │ │ @ InstanceVariableReadNode (location: (1,5)-(1,7))
+ │ │ │ └── name: :@a
+ │ │ └── closing_loc: (1,7)-(1,8) = "\""
+ │ └── @ StringNode (location: (1,9)-(1,14))
+ │ ├── flags: frozen
+ │ ├── opening_loc: (1,9)-(1,10) = "\""
+ │ ├── content_loc: (1,10)-(1,13) = "bar"
+ │ ├── closing_loc: (1,13)-(1,14) = "\""
+ │ └── unescaped: "bar"
+ └── closing_loc: ∅
diff --git a/test/prism/snapshots/whitequark/string_dvar.txt b/test/prism/snapshots/whitequark/string_dvar.txt
new file mode 100644
index 0000000000..9d04232580
--- /dev/null
+++ b/test/prism/snapshots/whitequark/string_dvar.txt
@@ -0,0 +1,37 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(1,14))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,1) = "\""
+ ├── parts: (length: 5)
+ │ ├── @ EmbeddedVariableNode (location: (1,1)-(1,4))
+ │ │ ├── operator_loc: (1,1)-(1,2) = "#"
+ │ │ └── variable:
+ │ │ @ InstanceVariableReadNode (location: (1,2)-(1,4))
+ │ │ └── name: :@a
+ │ ├── @ StringNode (location: (1,4)-(1,5))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (1,4)-(1,5) = " "
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: " "
+ │ ├── @ EmbeddedVariableNode (location: (1,5)-(1,9))
+ │ │ ├── operator_loc: (1,5)-(1,6) = "#"
+ │ │ └── variable:
+ │ │ @ ClassVariableReadNode (location: (1,6)-(1,9))
+ │ │ └── name: :@@a
+ │ ├── @ StringNode (location: (1,9)-(1,10))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (1,9)-(1,10) = " "
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: " "
+ │ └── @ EmbeddedVariableNode (location: (1,10)-(1,13))
+ │ ├── operator_loc: (1,10)-(1,11) = "#"
+ │ └── variable:
+ │ @ GlobalVariableReadNode (location: (1,11)-(1,13))
+ │ └── name: :$a
+ └── closing_loc: (1,13)-(1,14) = "\""
diff --git a/test/prism/snapshots/whitequark/string_interp.txt b/test/prism/snapshots/whitequark/string_interp.txt
new file mode 100644
index 0000000000..597e8c5d5b
--- /dev/null
+++ b/test/prism/snapshots/whitequark/string_interp.txt
@@ -0,0 +1,38 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ InterpolatedStringNode (location: (1,0)-(1,14))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,1) = "\""
+ ├── parts: (length: 3)
+ │ ├── @ StringNode (location: (1,1)-(1,4))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (1,1)-(1,4) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ ├── @ EmbeddedStatementsNode (location: (1,4)-(1,10))
+ │ │ ├── opening_loc: (1,4)-(1,6) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,6)-(1,9))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (1,6)-(1,9))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (1,6)-(1,9) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── closing_loc: (1,9)-(1,10) = "}"
+ │ └── @ StringNode (location: (1,10)-(1,13))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (1,10)-(1,13) = "baz"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "baz"
+ └── closing_loc: (1,13)-(1,14) = "\""
diff --git a/test/prism/snapshots/whitequark/string_plain.txt b/test/prism/snapshots/whitequark/string_plain.txt
new file mode 100644
index 0000000000..7534ac1844
--- /dev/null
+++ b/test/prism/snapshots/whitequark/string_plain.txt
@@ -0,0 +1,17 @@
+@ ProgramNode (location: (1,0)-(3,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,8))
+ └── body: (length: 2)
+ ├── @ StringNode (location: (1,0)-(1,10))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,3) = "%q("
+ │ ├── content_loc: (1,3)-(1,9) = "foobar"
+ │ ├── closing_loc: (1,9)-(1,10) = ")"
+ │ └── unescaped: "foobar"
+ └── @ StringNode (location: (3,0)-(3,8))
+ ├── flags: ∅
+ ├── opening_loc: (3,0)-(3,1) = "'"
+ ├── content_loc: (3,1)-(3,7) = "foobar"
+ ├── closing_loc: (3,7)-(3,8) = "'"
+ └── unescaped: "foobar"
diff --git a/test/prism/snapshots/whitequark/super.txt b/test/prism/snapshots/whitequark/super.txt
new file mode 100644
index 0000000000..132b4912e4
--- /dev/null
+++ b/test/prism/snapshots/whitequark/super.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(5,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,10))
+ └── body: (length: 3)
+ ├── @ SuperNode (location: (1,0)-(1,9))
+ │ ├── keyword_loc: (1,0)-(1,5) = "super"
+ │ ├── lparen_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,6)-(1,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (1,6)-(1,9))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,6)-(1,9) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── rparen_loc: ∅
+ │ └── block: ∅
+ ├── @ SuperNode (location: (3,0)-(3,7))
+ │ ├── keyword_loc: (3,0)-(3,5) = "super"
+ │ ├── lparen_loc: (3,5)-(3,6) = "("
+ │ ├── arguments: ∅
+ │ ├── rparen_loc: (3,6)-(3,7) = ")"
+ │ └── block: ∅
+ └── @ SuperNode (location: (5,0)-(5,10))
+ ├── keyword_loc: (5,0)-(5,5) = "super"
+ ├── lparen_loc: (5,5)-(5,6) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (5,6)-(5,9))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (5,6)-(5,9))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (5,6)-(5,9) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── rparen_loc: (5,9)-(5,10) = ")"
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/super_block.txt b/test/prism/snapshots/whitequark/super_block.txt
new file mode 100644
index 0000000000..d9ce7b86be
--- /dev/null
+++ b/test/prism/snapshots/whitequark/super_block.txt
@@ -0,0 +1,48 @@
+@ ProgramNode (location: (1,0)-(3,21))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,21))
+ └── body: (length: 2)
+ ├── @ ForwardingSuperNode (location: (1,0)-(1,12))
+ │ └── block:
+ │ @ BlockNode (location: (1,6)-(1,12))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body: ∅
+ │ ├── opening_loc: (1,6)-(1,8) = "do"
+ │ └── closing_loc: (1,9)-(1,12) = "end"
+ └── @ SuperNode (location: (3,0)-(3,21))
+ ├── keyword_loc: (3,0)-(3,5) = "super"
+ ├── lparen_loc: ∅
+ ├── arguments:
+ │ @ ArgumentsNode (location: (3,6)-(3,14))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 2)
+ │ ├── @ CallNode (location: (3,6)-(3,9))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (3,6)-(3,9) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── @ CallNode (location: (3,11)-(3,14))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (3,11)-(3,14) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── rparen_loc: ∅
+ └── block:
+ @ BlockNode (location: (3,15)-(3,21))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body: ∅
+ ├── opening_loc: (3,15)-(3,17) = "do"
+ └── closing_loc: (3,18)-(3,21) = "end"
diff --git a/test/prism/snapshots/whitequark/symbol_interp.txt b/test/prism/snapshots/whitequark/symbol_interp.txt
new file mode 100644
index 0000000000..a9b8dfcb63
--- /dev/null
+++ b/test/prism/snapshots/whitequark/symbol_interp.txt
@@ -0,0 +1,37 @@
+@ ProgramNode (location: (1,0)-(1,15))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,15))
+ └── body: (length: 1)
+ └── @ InterpolatedSymbolNode (location: (1,0)-(1,15))
+ ├── opening_loc: (1,0)-(1,2) = ":\""
+ ├── parts: (length: 3)
+ │ ├── @ StringNode (location: (1,2)-(1,5))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (1,2)-(1,5) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ ├── @ EmbeddedStatementsNode (location: (1,5)-(1,11))
+ │ │ ├── opening_loc: (1,5)-(1,7) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,7)-(1,10))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (1,7)-(1,10))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (1,7)-(1,10) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── closing_loc: (1,10)-(1,11) = "}"
+ │ └── @ StringNode (location: (1,11)-(1,14))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (1,11)-(1,14) = "baz"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "baz"
+ └── closing_loc: (1,14)-(1,15) = "\""
diff --git a/test/prism/snapshots/whitequark/symbol_plain.txt b/test/prism/snapshots/whitequark/symbol_plain.txt
new file mode 100644
index 0000000000..a2466600f5
--- /dev/null
+++ b/test/prism/snapshots/whitequark/symbol_plain.txt
@@ -0,0 +1,17 @@
+@ ProgramNode (location: (1,0)-(3,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,4))
+ └── body: (length: 2)
+ ├── @ SymbolNode (location: (1,0)-(1,6))
+ │ ├── flags: forced_us_ascii_encoding
+ │ ├── opening_loc: (1,0)-(1,2) = ":'"
+ │ ├── value_loc: (1,2)-(1,5) = "foo"
+ │ ├── closing_loc: (1,5)-(1,6) = "'"
+ │ └── unescaped: "foo"
+ └── @ SymbolNode (location: (3,0)-(3,4))
+ ├── flags: forced_us_ascii_encoding
+ ├── opening_loc: (3,0)-(3,1) = ":"
+ ├── value_loc: (3,1)-(3,4) = "foo"
+ ├── closing_loc: ∅
+ └── unescaped: "foo"
diff --git a/test/prism/snapshots/whitequark/ternary.txt b/test/prism/snapshots/whitequark/ternary.txt
new file mode 100644
index 0000000000..fa637ffb4e
--- /dev/null
+++ b/test/prism/snapshots/whitequark/ternary.txt
@@ -0,0 +1,36 @@
+@ ProgramNode (location: (1,0)-(1,11))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,11))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(1,11))
+ ├── if_keyword_loc: ∅
+ ├── predicate:
+ │ @ CallNode (location: (1,0)-(1,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,0)-(1,3) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── then_keyword_loc: (1,4)-(1,5) = "?"
+ ├── statements:
+ │ @ StatementsNode (location: (1,6)-(1,7))
+ │ └── body: (length: 1)
+ │ └── @ IntegerNode (location: (1,6)-(1,7))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── consequent:
+ │ @ ElseNode (location: (1,8)-(1,11))
+ │ ├── else_keyword_loc: (1,8)-(1,9) = ":"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,10)-(1,11))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,10)-(1,11))
+ │ │ ├── flags: decimal
+ │ │ └── value: 2
+ │ └── end_keyword_loc: ∅
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/whitequark/ternary_ambiguous_symbol.txt b/test/prism/snapshots/whitequark/ternary_ambiguous_symbol.txt
new file mode 100644
index 0000000000..833afcff42
--- /dev/null
+++ b/test/prism/snapshots/whitequark/ternary_ambiguous_symbol.txt
@@ -0,0 +1,50 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: [:t]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 2)
+ ├── @ LocalVariableWriteNode (location: (1,0)-(1,3))
+ │ ├── name: :t
+ │ ├── depth: 0
+ │ ├── name_loc: (1,0)-(1,1) = "t"
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,2)-(1,3))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator_loc: (1,1)-(1,2) = "="
+ └── @ IfNode (location: (1,4)-(1,13))
+ ├── if_keyword_loc: ∅
+ ├── predicate:
+ │ @ ParenthesesNode (location: (1,4)-(1,9))
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,5)-(1,8))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,5)-(1,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,5)-(1,8) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (1,4)-(1,5) = "("
+ │ └── closing_loc: (1,8)-(1,9) = ")"
+ ├── then_keyword_loc: (1,9)-(1,10) = "?"
+ ├── statements:
+ │ @ StatementsNode (location: (1,10)-(1,11))
+ │ └── body: (length: 1)
+ │ └── @ LocalVariableReadNode (location: (1,10)-(1,11))
+ │ ├── name: :t
+ │ └── depth: 0
+ ├── consequent:
+ │ @ ElseNode (location: (1,11)-(1,13))
+ │ ├── else_keyword_loc: (1,11)-(1,12) = ":"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,12)-(1,13))
+ │ │ └── body: (length: 1)
+ │ │ └── @ ConstantReadNode (location: (1,12)-(1,13))
+ │ │ └── name: :T
+ │ └── end_keyword_loc: ∅
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/whitequark/trailing_forward_arg.txt b/test/prism/snapshots/whitequark/trailing_forward_arg.txt
new file mode 100644
index 0000000000..e12dad132b
--- /dev/null
+++ b/test/prism/snapshots/whitequark/trailing_forward_arg.txt
@@ -0,0 +1,55 @@
+@ ProgramNode (location: (1,0)-(1,40))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,40))
+ └── body: (length: 1)
+ └── @ DefNode (location: (1,0)-(1,40))
+ ├── name: :foo
+ ├── name_loc: (1,4)-(1,7) = "foo"
+ ├── receiver: ∅
+ ├── parameters:
+ │ @ ParametersNode (location: (1,8)-(1,17))
+ │ ├── requireds: (length: 2)
+ │ │ ├── @ RequiredParameterNode (location: (1,8)-(1,9))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :a
+ │ │ └── @ RequiredParameterNode (location: (1,11)-(1,12))
+ │ │ ├── flags: ∅
+ │ │ └── name: :b
+ │ ├── optionals: (length: 0)
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest:
+ │ │ @ ForwardingParameterNode (location: (1,14)-(1,17))
+ │ └── block: ∅
+ ├── body:
+ │ @ StatementsNode (location: (1,20)-(1,35))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,20)-(1,35))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (1,20)-(1,23) = "bar"
+ │ ├── opening_loc: (1,23)-(1,24) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,24)-(1,34))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 3)
+ │ │ ├── @ LocalVariableReadNode (location: (1,24)-(1,25))
+ │ │ │ ├── name: :a
+ │ │ │ └── depth: 0
+ │ │ ├── @ IntegerNode (location: (1,27)-(1,29))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ └── @ ForwardingArgumentsNode (location: (1,31)-(1,34))
+ │ ├── closing_loc: (1,34)-(1,35) = ")"
+ │ └── block: ∅
+ ├── locals: [:a, :b]
+ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: (1,7)-(1,8) = "("
+ ├── rparen_loc: (1,17)-(1,18) = ")"
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (1,37)-(1,40) = "end"
diff --git a/test/prism/snapshots/whitequark/true.txt b/test/prism/snapshots/whitequark/true.txt
new file mode 100644
index 0000000000..3e1ceef586
--- /dev/null
+++ b/test/prism/snapshots/whitequark/true.txt
@@ -0,0 +1,6 @@
+@ ProgramNode (location: (1,0)-(1,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ TrueNode (location: (1,0)-(1,4))
diff --git a/test/prism/snapshots/whitequark/unary_num_pow_precedence.txt b/test/prism/snapshots/whitequark/unary_num_pow_precedence.txt
new file mode 100644
index 0000000000..e14b0567e7
--- /dev/null
+++ b/test/prism/snapshots/whitequark/unary_num_pow_precedence.txt
@@ -0,0 +1,80 @@
+@ ProgramNode (location: (1,0)-(5,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(5,10))
+ └── body: (length: 3)
+ ├── @ CallNode (location: (1,0)-(1,10))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ FloatNode (location: (1,0)-(1,4))
+ │ │ └── value: 2.0
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :**
+ │ ├── message_loc: (1,5)-(1,7) = "**"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,8)-(1,10))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (1,8)-(1,10))
+ │ │ ├── flags: decimal
+ │ │ └── value: 10
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ CallNode (location: (3,0)-(3,8))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (3,1)-(3,8))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ IntegerNode (location: (3,1)-(3,2))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :**
+ │ │ ├── message_loc: (3,3)-(3,5) = "**"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (3,6)-(3,8))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (3,6)-(3,8))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 10
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :-@
+ │ ├── message_loc: (3,0)-(3,1) = "-"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ CallNode (location: (5,0)-(5,10))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (5,1)-(5,10))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ FloatNode (location: (5,1)-(5,4))
+ │ │ └── value: 2.0
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :**
+ │ ├── message_loc: (5,5)-(5,7) = "**"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (5,8)-(5,10))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (5,8)-(5,10))
+ │ │ ├── flags: decimal
+ │ │ └── value: 10
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :-@
+ ├── message_loc: (5,0)-(5,1) = "-"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/undef.txt b/test/prism/snapshots/whitequark/undef.txt
new file mode 100644
index 0000000000..163cc2e867
--- /dev/null
+++ b/test/prism/snapshots/whitequark/undef.txt
@@ -0,0 +1,39 @@
+@ ProgramNode (location: (1,0)-(1,27))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,27))
+ └── body: (length: 1)
+ └── @ UndefNode (location: (1,0)-(1,27))
+ ├── names: (length: 3)
+ │ ├── @ SymbolNode (location: (1,6)-(1,9))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: ∅
+ │ │ ├── value_loc: (1,6)-(1,9) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ ├── @ SymbolNode (location: (1,11)-(1,15))
+ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ ├── opening_loc: (1,11)-(1,12) = ":"
+ │ │ ├── value_loc: (1,12)-(1,15) = "bar"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "bar"
+ │ └── @ InterpolatedSymbolNode (location: (1,17)-(1,27))
+ │ ├── opening_loc: (1,17)-(1,19) = ":\""
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (1,19)-(1,22))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (1,19)-(1,22) = "foo"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo"
+ │ │ └── @ EmbeddedStatementsNode (location: (1,22)-(1,26))
+ │ │ ├── opening_loc: (1,22)-(1,24) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,24)-(1,25))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (1,24)-(1,25))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── closing_loc: (1,25)-(1,26) = "}"
+ │ └── closing_loc: (1,26)-(1,27) = "\""
+ └── keyword_loc: (1,0)-(1,5) = "undef"
diff --git a/test/prism/snapshots/whitequark/unless.txt b/test/prism/snapshots/whitequark/unless.txt
new file mode 100644
index 0000000000..a3bbbe69c8
--- /dev/null
+++ b/test/prism/snapshots/whitequark/unless.txt
@@ -0,0 +1,63 @@
+@ ProgramNode (location: (1,0)-(3,20))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,20))
+ └── body: (length: 2)
+ ├── @ UnlessNode (location: (1,0)-(1,24))
+ │ ├── keyword_loc: (1,0)-(1,6) = "unless"
+ │ ├── predicate:
+ │ │ @ CallNode (location: (1,7)-(1,10))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,7)-(1,10) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: (1,11)-(1,15) = "then"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,16)-(1,19))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,16)-(1,19))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,16)-(1,19) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (1,21)-(1,24) = "end"
+ └── @ UnlessNode (location: (3,0)-(3,20))
+ ├── keyword_loc: (3,0)-(3,6) = "unless"
+ ├── predicate:
+ │ @ CallNode (location: (3,7)-(3,10))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (3,7)-(3,10) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── then_keyword_loc: ∅
+ ├── statements:
+ │ @ StatementsNode (location: (3,12)-(3,15))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (3,12)-(3,15))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (3,12)-(3,15) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (3,17)-(3,20) = "end"
diff --git a/test/prism/snapshots/whitequark/unless_else.txt b/test/prism/snapshots/whitequark/unless_else.txt
new file mode 100644
index 0000000000..f4f95379e5
--- /dev/null
+++ b/test/prism/snapshots/whitequark/unless_else.txt
@@ -0,0 +1,95 @@
+@ ProgramNode (location: (1,0)-(3,30))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,30))
+ └── body: (length: 2)
+ ├── @ UnlessNode (location: (1,0)-(1,34))
+ │ ├── keyword_loc: (1,0)-(1,6) = "unless"
+ │ ├── predicate:
+ │ │ @ CallNode (location: (1,7)-(1,10))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,7)-(1,10) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: (1,11)-(1,15) = "then"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (1,16)-(1,19))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,16)-(1,19))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,16)-(1,19) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── consequent:
+ │ │ @ ElseNode (location: (1,21)-(1,34))
+ │ │ ├── else_keyword_loc: (1,21)-(1,25) = "else"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,26)-(1,29))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (1,26)-(1,29))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (1,26)-(1,29) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── end_keyword_loc: (1,31)-(1,34) = "end"
+ │ └── end_keyword_loc: (1,31)-(1,34) = "end"
+ └── @ UnlessNode (location: (3,0)-(3,30))
+ ├── keyword_loc: (3,0)-(3,6) = "unless"
+ ├── predicate:
+ │ @ CallNode (location: (3,7)-(3,10))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (3,7)-(3,10) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── then_keyword_loc: ∅
+ ├── statements:
+ │ @ StatementsNode (location: (3,12)-(3,15))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (3,12)-(3,15))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (3,12)-(3,15) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── consequent:
+ │ @ ElseNode (location: (3,17)-(3,30))
+ │ ├── else_keyword_loc: (3,17)-(3,21) = "else"
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (3,22)-(3,25))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (3,22)-(3,25))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :baz
+ │ │ ├── message_loc: (3,22)-(3,25) = "baz"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── end_keyword_loc: (3,27)-(3,30) = "end"
+ └── end_keyword_loc: (3,27)-(3,30) = "end"
diff --git a/test/prism/snapshots/whitequark/unless_mod.txt b/test/prism/snapshots/whitequark/unless_mod.txt
new file mode 100644
index 0000000000..d4dfda6b2c
--- /dev/null
+++ b/test/prism/snapshots/whitequark/unless_mod.txt
@@ -0,0 +1,34 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ UnlessNode (location: (1,0)-(1,14))
+ ├── keyword_loc: (1,4)-(1,10) = "unless"
+ ├── predicate:
+ │ @ CallNode (location: (1,11)-(1,14))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,11)-(1,14) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── then_keyword_loc: ∅
+ ├── statements:
+ │ @ StatementsNode (location: (1,0)-(1,3))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,0)-(1,3))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (1,0)-(1,3) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: ∅
diff --git a/test/prism/snapshots/whitequark/until.txt b/test/prism/snapshots/whitequark/until.txt
new file mode 100644
index 0000000000..e5f60a2cf7
--- /dev/null
+++ b/test/prism/snapshots/whitequark/until.txt
@@ -0,0 +1,61 @@
+@ ProgramNode (location: (1,0)-(3,19))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,19))
+ └── body: (length: 2)
+ ├── @ UntilNode (location: (1,0)-(1,21))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (1,0)-(1,5) = "until"
+ │ ├── closing_loc: (1,18)-(1,21) = "end"
+ │ ├── predicate:
+ │ │ @ CallNode (location: (1,6)-(1,9))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,6)-(1,9) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── statements:
+ │ @ StatementsNode (location: (1,13)-(1,17))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,13)-(1,17))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :meth
+ │ ├── message_loc: (1,13)-(1,17) = "meth"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ UntilNode (location: (3,0)-(3,19))
+ ├── flags: ∅
+ ├── keyword_loc: (3,0)-(3,5) = "until"
+ ├── closing_loc: (3,16)-(3,19) = "end"
+ ├── predicate:
+ │ @ CallNode (location: (3,6)-(3,9))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (3,6)-(3,9) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── statements:
+ @ StatementsNode (location: (3,11)-(3,15))
+ └── body: (length: 1)
+ └── @ CallNode (location: (3,11)-(3,15))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :meth
+ ├── message_loc: (3,11)-(3,15) = "meth"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/until_mod.txt b/test/prism/snapshots/whitequark/until_mod.txt
new file mode 100644
index 0000000000..0b7e2360b5
--- /dev/null
+++ b/test/prism/snapshots/whitequark/until_mod.txt
@@ -0,0 +1,33 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ UntilNode (location: (1,0)-(1,14))
+ ├── flags: ∅
+ ├── keyword_loc: (1,5)-(1,10) = "until"
+ ├── closing_loc: ∅
+ ├── predicate:
+ │ @ CallNode (location: (1,11)-(1,14))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,11)-(1,14) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,4))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :meth
+ ├── message_loc: (1,0)-(1,4) = "meth"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/until_post.txt b/test/prism/snapshots/whitequark/until_post.txt
new file mode 100644
index 0000000000..5b282c363b
--- /dev/null
+++ b/test/prism/snapshots/whitequark/until_post.txt
@@ -0,0 +1,42 @@
+@ ProgramNode (location: (1,0)-(1,24))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,24))
+ └── body: (length: 1)
+ └── @ UntilNode (location: (1,0)-(1,24))
+ ├── flags: begin_modifier
+ ├── keyword_loc: (1,15)-(1,20) = "until"
+ ├── closing_loc: ∅
+ ├── predicate:
+ │ @ CallNode (location: (1,21)-(1,24))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,21)-(1,24) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ BeginNode (location: (1,0)-(1,14))
+ ├── begin_keyword_loc: (1,0)-(1,5) = "begin"
+ ├── statements:
+ │ @ StatementsNode (location: (1,6)-(1,10))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,6)-(1,10))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :meth
+ │ ├── message_loc: (1,6)-(1,10) = "meth"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── rescue_clause: ∅
+ ├── else_clause: ∅
+ ├── ensure_clause: ∅
+ └── end_keyword_loc: (1,11)-(1,14) = "end"
diff --git a/test/prism/snapshots/whitequark/var_and_asgn.txt b/test/prism/snapshots/whitequark/var_and_asgn.txt
new file mode 100644
index 0000000000..a3c90f804c
--- /dev/null
+++ b/test/prism/snapshots/whitequark/var_and_asgn.txt
@@ -0,0 +1,14 @@
+@ ProgramNode (location: (1,0)-(1,7))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,7))
+ └── body: (length: 1)
+ └── @ LocalVariableAndWriteNode (location: (1,0)-(1,7))
+ ├── name_loc: (1,0)-(1,1) = "a"
+ ├── operator_loc: (1,2)-(1,5) = "&&="
+ ├── value:
+ │ @ IntegerNode (location: (1,6)-(1,7))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── name: :a
+ └── depth: 0
diff --git a/test/prism/snapshots/whitequark/var_op_asgn.txt b/test/prism/snapshots/whitequark/var_op_asgn.txt
new file mode 100644
index 0000000000..f423a62dee
--- /dev/null
+++ b/test/prism/snapshots/whitequark/var_op_asgn.txt
@@ -0,0 +1,57 @@
+@ ProgramNode (location: (1,0)-(7,23))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(7,23))
+ └── body: (length: 4)
+ ├── @ ClassVariableOperatorWriteNode (location: (1,0)-(1,11))
+ │ ├── name: :@@var
+ │ ├── name_loc: (1,0)-(1,5) = "@@var"
+ │ ├── operator_loc: (1,6)-(1,8) = "|="
+ │ ├── value:
+ │ │ @ IntegerNode (location: (1,9)-(1,11))
+ │ │ ├── flags: decimal
+ │ │ └── value: 10
+ │ └── operator: :|
+ ├── @ InstanceVariableOperatorWriteNode (location: (3,0)-(3,7))
+ │ ├── name: :@a
+ │ ├── name_loc: (3,0)-(3,2) = "@a"
+ │ ├── operator_loc: (3,3)-(3,5) = "|="
+ │ ├── value:
+ │ │ @ IntegerNode (location: (3,6)-(3,7))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ └── operator: :|
+ ├── @ LocalVariableOperatorWriteNode (location: (5,0)-(5,6))
+ │ ├── name_loc: (5,0)-(5,1) = "a"
+ │ ├── operator_loc: (5,2)-(5,4) = "+="
+ │ ├── value:
+ │ │ @ IntegerNode (location: (5,5)-(5,6))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── name: :a
+ │ ├── operator: :+
+ │ └── depth: 0
+ └── @ DefNode (location: (7,0)-(7,23))
+ ├── name: :a
+ ├── name_loc: (7,4)-(7,5) = "a"
+ ├── receiver: ∅
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (7,7)-(7,18))
+ │ └── body: (length: 1)
+ │ └── @ ClassVariableOperatorWriteNode (location: (7,7)-(7,18))
+ │ ├── name: :@@var
+ │ ├── name_loc: (7,7)-(7,12) = "@@var"
+ │ ├── operator_loc: (7,13)-(7,15) = "|="
+ │ ├── value:
+ │ │ @ IntegerNode (location: (7,16)-(7,18))
+ │ │ ├── flags: decimal
+ │ │ └── value: 10
+ │ └── operator: :|
+ ├── locals: []
+ ├── def_keyword_loc: (7,0)-(7,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (7,20)-(7,23) = "end"
diff --git a/test/prism/snapshots/whitequark/var_op_asgn_cmd.txt b/test/prism/snapshots/whitequark/var_op_asgn_cmd.txt
new file mode 100644
index 0000000000..d56c099c7e
--- /dev/null
+++ b/test/prism/snapshots/whitequark/var_op_asgn_cmd.txt
@@ -0,0 +1,28 @@
+@ ProgramNode (location: (1,0)-(1,12))
+├── locals: [:foo]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,12))
+ └── body: (length: 1)
+ └── @ LocalVariableOperatorWriteNode (location: (1,0)-(1,12))
+ ├── name_loc: (1,0)-(1,3) = "foo"
+ ├── operator_loc: (1,4)-(1,6) = "+="
+ ├── value:
+ │ @ CallNode (location: (1,7)-(1,12))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :m
+ │ ├── message_loc: (1,7)-(1,8) = "m"
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (1,9)-(1,12))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ LocalVariableReadNode (location: (1,9)-(1,12))
+ │ │ ├── name: :foo
+ │ │ └── depth: 0
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── name: :foo
+ ├── operator: :+
+ └── depth: 0
diff --git a/test/prism/snapshots/whitequark/var_or_asgn.txt b/test/prism/snapshots/whitequark/var_or_asgn.txt
new file mode 100644
index 0000000000..a0531d2c30
--- /dev/null
+++ b/test/prism/snapshots/whitequark/var_or_asgn.txt
@@ -0,0 +1,14 @@
+@ ProgramNode (location: (1,0)-(1,7))
+├── locals: [:a]
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,7))
+ └── body: (length: 1)
+ └── @ LocalVariableOrWriteNode (location: (1,0)-(1,7))
+ ├── name_loc: (1,0)-(1,1) = "a"
+ ├── operator_loc: (1,2)-(1,5) = "||="
+ ├── value:
+ │ @ IntegerNode (location: (1,6)-(1,7))
+ │ ├── flags: decimal
+ │ └── value: 1
+ ├── name: :a
+ └── depth: 0
diff --git a/test/prism/snapshots/whitequark/when_multi.txt b/test/prism/snapshots/whitequark/when_multi.txt
new file mode 100644
index 0000000000..1c399b642d
--- /dev/null
+++ b/test/prism/snapshots/whitequark/when_multi.txt
@@ -0,0 +1,50 @@
+@ ProgramNode (location: (1,0)-(1,37))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,37))
+ └── body: (length: 1)
+ └── @ CaseNode (location: (1,0)-(1,37))
+ ├── predicate:
+ │ @ CallNode (location: (1,5)-(1,8))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,5)-(1,8) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── conditions: (length: 1)
+ │ └── @ WhenNode (location: (1,10)-(1,32))
+ │ ├── keyword_loc: (1,10)-(1,14) = "when"
+ │ ├── conditions: (length: 2)
+ │ │ ├── @ StringNode (location: (1,15)-(1,20))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: (1,15)-(1,16) = "'"
+ │ │ │ ├── content_loc: (1,16)-(1,19) = "bar"
+ │ │ │ ├── closing_loc: (1,19)-(1,20) = "'"
+ │ │ │ └── unescaped: "bar"
+ │ │ └── @ StringNode (location: (1,22)-(1,27))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: (1,22)-(1,23) = "'"
+ │ │ ├── content_loc: (1,23)-(1,26) = "baz"
+ │ │ ├── closing_loc: (1,26)-(1,27) = "'"
+ │ │ └── unescaped: "baz"
+ │ ├── then_keyword_loc: ∅
+ │ └── statements:
+ │ @ StatementsNode (location: (1,29)-(1,32))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,29)-(1,32))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (1,29)-(1,32) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (1,34)-(1,37) = "end"
diff --git a/test/prism/snapshots/whitequark/when_splat.txt b/test/prism/snapshots/whitequark/when_splat.txt
new file mode 100644
index 0000000000..351631714e
--- /dev/null
+++ b/test/prism/snapshots/whitequark/when_splat.txt
@@ -0,0 +1,72 @@
+@ ProgramNode (location: (1,0)-(1,43))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,43))
+ └── body: (length: 1)
+ └── @ CaseNode (location: (1,0)-(1,43))
+ ├── predicate:
+ │ @ CallNode (location: (1,5)-(1,8))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,5)-(1,8) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── conditions: (length: 2)
+ │ ├── @ WhenNode (location: (1,10)-(1,27))
+ │ │ ├── keyword_loc: (1,10)-(1,14) = "when"
+ │ │ ├── conditions: (length: 2)
+ │ │ │ ├── @ IntegerNode (location: (1,15)-(1,16))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── @ SplatNode (location: (1,18)-(1,22))
+ │ │ │ ├── operator_loc: (1,18)-(1,19) = "*"
+ │ │ │ └── expression:
+ │ │ │ @ CallNode (location: (1,19)-(1,22))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :baz
+ │ │ │ ├── message_loc: (1,19)-(1,22) = "baz"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── then_keyword_loc: ∅
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (1,24)-(1,27))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (1,24)-(1,27))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,24)-(1,27) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── @ WhenNode (location: (1,29)-(1,38))
+ │ ├── keyword_loc: (1,29)-(1,33) = "when"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ SplatNode (location: (1,34)-(1,38))
+ │ │ ├── operator_loc: (1,34)-(1,35) = "*"
+ │ │ └── expression:
+ │ │ @ CallNode (location: (1,35)-(1,38))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,35)-(1,38) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── then_keyword_loc: ∅
+ │ └── statements: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (1,40)-(1,43) = "end"
diff --git a/test/prism/snapshots/whitequark/when_then.txt b/test/prism/snapshots/whitequark/when_then.txt
new file mode 100644
index 0000000000..eb6f261ba4
--- /dev/null
+++ b/test/prism/snapshots/whitequark/when_then.txt
@@ -0,0 +1,44 @@
+@ ProgramNode (location: (1,0)-(1,34))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,34))
+ └── body: (length: 1)
+ └── @ CaseNode (location: (1,0)-(1,34))
+ ├── predicate:
+ │ @ CallNode (location: (1,5)-(1,8))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,5)-(1,8) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── conditions: (length: 1)
+ │ └── @ WhenNode (location: (1,10)-(1,29))
+ │ ├── keyword_loc: (1,10)-(1,14) = "when"
+ │ ├── conditions: (length: 1)
+ │ │ └── @ StringNode (location: (1,15)-(1,20))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: (1,15)-(1,16) = "'"
+ │ │ ├── content_loc: (1,16)-(1,19) = "bar"
+ │ │ ├── closing_loc: (1,19)-(1,20) = "'"
+ │ │ └── unescaped: "bar"
+ │ ├── then_keyword_loc: (1,21)-(1,25) = "then"
+ │ └── statements:
+ │ @ StatementsNode (location: (1,26)-(1,29))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,26)-(1,29))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :bar
+ │ ├── message_loc: (1,26)-(1,29) = "bar"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── consequent: ∅
+ ├── case_keyword_loc: (1,0)-(1,4) = "case"
+ └── end_keyword_loc: (1,31)-(1,34) = "end"
diff --git a/test/prism/snapshots/whitequark/while.txt b/test/prism/snapshots/whitequark/while.txt
new file mode 100644
index 0000000000..72f9971fe5
--- /dev/null
+++ b/test/prism/snapshots/whitequark/while.txt
@@ -0,0 +1,61 @@
+@ ProgramNode (location: (1,0)-(3,19))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(3,19))
+ └── body: (length: 2)
+ ├── @ WhileNode (location: (1,0)-(1,21))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (1,0)-(1,5) = "while"
+ │ ├── closing_loc: (1,18)-(1,21) = "end"
+ │ ├── predicate:
+ │ │ @ CallNode (location: (1,6)-(1,9))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,6)-(1,9) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── statements:
+ │ @ StatementsNode (location: (1,13)-(1,17))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,13)-(1,17))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :meth
+ │ ├── message_loc: (1,13)-(1,17) = "meth"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── @ WhileNode (location: (3,0)-(3,19))
+ ├── flags: ∅
+ ├── keyword_loc: (3,0)-(3,5) = "while"
+ ├── closing_loc: (3,16)-(3,19) = "end"
+ ├── predicate:
+ │ @ CallNode (location: (3,6)-(3,9))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (3,6)-(3,9) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── statements:
+ @ StatementsNode (location: (3,11)-(3,15))
+ └── body: (length: 1)
+ └── @ CallNode (location: (3,11)-(3,15))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :meth
+ ├── message_loc: (3,11)-(3,15) = "meth"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/while_mod.txt b/test/prism/snapshots/whitequark/while_mod.txt
new file mode 100644
index 0000000000..50a8f84d81
--- /dev/null
+++ b/test/prism/snapshots/whitequark/while_mod.txt
@@ -0,0 +1,33 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ WhileNode (location: (1,0)-(1,14))
+ ├── flags: ∅
+ ├── keyword_loc: (1,5)-(1,10) = "while"
+ ├── closing_loc: ∅
+ ├── predicate:
+ │ @ CallNode (location: (1,11)-(1,14))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,11)-(1,14) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── statements:
+ @ StatementsNode (location: (1,0)-(1,4))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,4))
+ ├── flags: variable_call, ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :meth
+ ├── message_loc: (1,0)-(1,4) = "meth"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block: ∅
diff --git a/test/prism/snapshots/whitequark/while_post.txt b/test/prism/snapshots/whitequark/while_post.txt
new file mode 100644
index 0000000000..63c6c84a24
--- /dev/null
+++ b/test/prism/snapshots/whitequark/while_post.txt
@@ -0,0 +1,42 @@
+@ ProgramNode (location: (1,0)-(1,24))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,24))
+ └── body: (length: 1)
+ └── @ WhileNode (location: (1,0)-(1,24))
+ ├── flags: begin_modifier
+ ├── keyword_loc: (1,15)-(1,20) = "while"
+ ├── closing_loc: ∅
+ ├── predicate:
+ │ @ CallNode (location: (1,21)-(1,24))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (1,21)-(1,24) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ BeginNode (location: (1,0)-(1,14))
+ ├── begin_keyword_loc: (1,0)-(1,5) = "begin"
+ ├── statements:
+ │ @ StatementsNode (location: (1,6)-(1,10))
+ │ └── body: (length: 1)
+ │ └── @ CallNode (location: (1,6)-(1,10))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :meth
+ │ ├── message_loc: (1,6)-(1,10) = "meth"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── rescue_clause: ∅
+ ├── else_clause: ∅
+ ├── ensure_clause: ∅
+ └── end_keyword_loc: (1,11)-(1,14) = "end"
diff --git a/test/prism/snapshots/whitequark/xstring_interp.txt b/test/prism/snapshots/whitequark/xstring_interp.txt
new file mode 100644
index 0000000000..0676ea9683
--- /dev/null
+++ b/test/prism/snapshots/whitequark/xstring_interp.txt
@@ -0,0 +1,37 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ InterpolatedXStringNode (location: (1,0)-(1,14))
+ ├── opening_loc: (1,0)-(1,1) = "`"
+ ├── parts: (length: 3)
+ │ ├── @ StringNode (location: (1,1)-(1,4))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (1,1)-(1,4) = "foo"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "foo"
+ │ ├── @ EmbeddedStatementsNode (location: (1,4)-(1,10))
+ │ │ ├── opening_loc: (1,4)-(1,6) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,6)-(1,9))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ CallNode (location: (1,6)-(1,9))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :bar
+ │ │ │ ├── message_loc: (1,6)-(1,9) = "bar"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── closing_loc: (1,9)-(1,10) = "}"
+ │ └── @ StringNode (location: (1,10)-(1,13))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (1,10)-(1,13) = "baz"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "baz"
+ └── closing_loc: (1,13)-(1,14) = "`"
diff --git a/test/prism/snapshots/whitequark/xstring_plain.txt b/test/prism/snapshots/whitequark/xstring_plain.txt
new file mode 100644
index 0000000000..97084286d9
--- /dev/null
+++ b/test/prism/snapshots/whitequark/xstring_plain.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,8))
+ └── body: (length: 1)
+ └── @ XStringNode (location: (1,0)-(1,8))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,1) = "`"
+ ├── content_loc: (1,1)-(1,7) = "foobar"
+ ├── closing_loc: (1,7)-(1,8) = "`"
+ └── unescaped: "foobar"
diff --git a/test/prism/snapshots/whitequark/yield.txt b/test/prism/snapshots/whitequark/yield.txt
new file mode 100644
index 0000000000..2b37dd479f
--- /dev/null
+++ b/test/prism/snapshots/whitequark/yield.txt
@@ -0,0 +1,51 @@
+@ ProgramNode (location: (1,0)-(7,10))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(7,10))
+ └── body: (length: 4)
+ ├── @ YieldNode (location: (1,0)-(1,5))
+ │ ├── keyword_loc: (1,0)-(1,5) = "yield"
+ │ ├── lparen_loc: ∅
+ │ ├── arguments: ∅
+ │ └── rparen_loc: ∅
+ ├── @ YieldNode (location: (3,0)-(3,9))
+ │ ├── keyword_loc: (3,0)-(3,5) = "yield"
+ │ ├── lparen_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (3,6)-(3,9))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ CallNode (location: (3,6)-(3,9))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (3,6)-(3,9) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── rparen_loc: ∅
+ ├── @ YieldNode (location: (5,0)-(5,7))
+ │ ├── keyword_loc: (5,0)-(5,5) = "yield"
+ │ ├── lparen_loc: (5,5)-(5,6) = "("
+ │ ├── arguments: ∅
+ │ └── rparen_loc: (5,6)-(5,7) = ")"
+ └── @ YieldNode (location: (7,0)-(7,10))
+ ├── keyword_loc: (7,0)-(7,5) = "yield"
+ ├── lparen_loc: (7,5)-(7,6) = "("
+ ├── arguments:
+ │ @ ArgumentsNode (location: (7,6)-(7,9))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ CallNode (location: (7,6)-(7,9))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :foo
+ │ ├── message_loc: (7,6)-(7,9) = "foo"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ └── rparen_loc: (7,9)-(7,10) = ")"
diff --git a/test/prism/snapshots/whitequark/zsuper.txt b/test/prism/snapshots/whitequark/zsuper.txt
new file mode 100644
index 0000000000..9c28128d06
--- /dev/null
+++ b/test/prism/snapshots/whitequark/zsuper.txt
@@ -0,0 +1,7 @@
+@ ProgramNode (location: (1,0)-(1,5))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,5))
+ └── body: (length: 1)
+ └── @ ForwardingSuperNode (location: (1,0)-(1,5))
+ └── block: ∅
diff --git a/test/prism/snapshots/xstring.txt b/test/prism/snapshots/xstring.txt
new file mode 100644
index 0000000000..1a177026db
--- /dev/null
+++ b/test/prism/snapshots/xstring.txt
@@ -0,0 +1,67 @@
+@ ProgramNode (location: (1,0)-(13,4))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(13,4))
+ └── body: (length: 6)
+ ├── @ XStringNode (location: (1,0)-(1,7))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,0)-(1,3) = "%x["
+ │ ├── content_loc: (1,3)-(1,6) = "foo"
+ │ ├── closing_loc: (1,6)-(1,7) = "]"
+ │ └── unescaped: "foo"
+ ├── @ InterpolatedXStringNode (location: (3,0)-(3,16))
+ │ ├── opening_loc: (3,0)-(3,1) = "`"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (3,1)-(3,5))
+ │ │ │ ├── flags: frozen
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (3,1)-(3,5) = "foo "
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "foo "
+ │ │ ├── @ EmbeddedStatementsNode (location: (3,5)-(3,11))
+ │ │ │ ├── opening_loc: (3,5)-(3,7) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (3,7)-(3,10))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (3,7)-(3,10))
+ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :bar
+ │ │ │ │ ├── message_loc: (3,7)-(3,10) = "bar"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ └── closing_loc: (3,10)-(3,11) = "}"
+ │ │ └── @ StringNode (location: (3,11)-(3,15))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (3,11)-(3,15) = " baz"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: " baz"
+ │ └── closing_loc: (3,15)-(3,16) = "`"
+ ├── @ XStringNode (location: (5,0)-(5,5))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (5,0)-(5,1) = "`"
+ │ ├── content_loc: (5,1)-(5,4) = "foo"
+ │ ├── closing_loc: (5,4)-(5,5) = "`"
+ │ └── unescaped: "foo"
+ ├── @ XStringNode (location: (7,0)-(9,1))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (7,0)-(7,3) = "%x{"
+ │ ├── content_loc: (7,3)-(9,0) = "\n foo\n"
+ │ ├── closing_loc: (9,0)-(9,1) = "}"
+ │ └── unescaped: "\n foo\n"
+ ├── @ XStringNode (location: (11,0)-(11,2))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (11,0)-(11,1) = "`"
+ │ ├── content_loc: (11,1)-(11,1) = ""
+ │ ├── closing_loc: (11,1)-(11,2) = "`"
+ │ └── unescaped: ""
+ └── @ XStringNode (location: (13,0)-(13,4))
+ ├── flags: ∅
+ ├── opening_loc: (13,0)-(13,3) = "%x{"
+ ├── content_loc: (13,3)-(13,3) = ""
+ ├── closing_loc: (13,3)-(13,4) = "}"
+ └── unescaped: ""
diff --git a/test/prism/snapshots/xstring_with_backslash.txt b/test/prism/snapshots/xstring_with_backslash.txt
new file mode 100644
index 0000000000..7e0fa1ab5f
--- /dev/null
+++ b/test/prism/snapshots/xstring_with_backslash.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,0)-(1,6))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,6))
+ └── body: (length: 1)
+ └── @ XStringNode (location: (1,0)-(1,6))
+ ├── flags: ∅
+ ├── opening_loc: (1,0)-(1,1) = "`"
+ ├── content_loc: (1,1)-(1,5) = "f\\oo"
+ ├── closing_loc: (1,5)-(1,6) = "`"
+ └── unescaped: "foo"
diff --git a/test/prism/snapshots/yield.txt b/test/prism/snapshots/yield.txt
new file mode 100644
index 0000000000..e9680c3b2d
--- /dev/null
+++ b/test/prism/snapshots/yield.txt
@@ -0,0 +1,103 @@
+@ ProgramNode (location: (1,0)-(7,28))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(7,28))
+ └── body: (length: 4)
+ ├── @ DefNode (location: (1,0)-(1,19))
+ │ ├── name: :foo
+ │ ├── name_loc: (1,4)-(1,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,9)-(1,14))
+ │ │ └── body: (length: 1)
+ │ │ └── @ YieldNode (location: (1,9)-(1,14))
+ │ │ ├── keyword_loc: (1,9)-(1,14) = "yield"
+ │ │ ├── lparen_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ └── rparen_loc: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (1,16)-(1,19) = "end"
+ ├── @ DefNode (location: (3,0)-(3,21))
+ │ ├── name: :foo
+ │ ├── name_loc: (3,4)-(3,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (3,9)-(3,16))
+ │ │ └── body: (length: 1)
+ │ │ └── @ YieldNode (location: (3,9)-(3,16))
+ │ │ ├── keyword_loc: (3,9)-(3,14) = "yield"
+ │ │ ├── lparen_loc: (3,14)-(3,15) = "("
+ │ │ ├── arguments: ∅
+ │ │ └── rparen_loc: (3,15)-(3,16) = ")"
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (3,0)-(3,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (3,18)-(3,21) = "end"
+ ├── @ DefNode (location: (5,0)-(5,22))
+ │ ├── name: :foo
+ │ ├── name_loc: (5,4)-(5,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (5,9)-(5,17))
+ │ │ └── body: (length: 1)
+ │ │ └── @ YieldNode (location: (5,9)-(5,17))
+ │ │ ├── keyword_loc: (5,9)-(5,14) = "yield"
+ │ │ ├── lparen_loc: (5,14)-(5,15) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (5,15)-(5,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (5,15)-(5,16))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── rparen_loc: (5,16)-(5,17) = ")"
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (5,0)-(5,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (5,19)-(5,22) = "end"
+ └── @ DefNode (location: (7,0)-(7,28))
+ ├── name: :foo
+ ├── name_loc: (7,4)-(7,7) = "foo"
+ ├── receiver: ∅
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (7,9)-(7,23))
+ │ └── body: (length: 1)
+ │ └── @ YieldNode (location: (7,9)-(7,23))
+ │ ├── keyword_loc: (7,9)-(7,14) = "yield"
+ │ ├── lparen_loc: (7,14)-(7,15) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (7,15)-(7,22))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 3)
+ │ │ ├── @ IntegerNode (location: (7,15)-(7,16))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── @ IntegerNode (location: (7,18)-(7,19))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── @ IntegerNode (location: (7,21)-(7,22))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ └── rparen_loc: (7,22)-(7,23) = ")"
+ ├── locals: []
+ ├── def_keyword_loc: (7,0)-(7,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (7,25)-(7,28) = "end"
diff --git a/test/prism/static_inspect_test.rb b/test/prism/static_inspect_test.rb
new file mode 100644
index 0000000000..8df2fd241e
--- /dev/null
+++ b/test/prism/static_inspect_test.rb
@@ -0,0 +1,90 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+return if Prism::BACKEND == :FFI
+
+module Prism
+ class StaticInspectTest < TestCase
+ def test_false
+ assert_equal "false", static_inspect("false")
+ end
+
+ def test_float
+ assert_equal "0.25", static_inspect("0.25")
+ assert_equal "5.125", static_inspect("5.125")
+
+ assert_equal "0.0", static_inspect("0.0")
+ assert_equal "-0.0", static_inspect("-0.0")
+
+ assert_equal "1.0e+100", static_inspect("1e100")
+ assert_equal "-1.0e+100", static_inspect("-1e100")
+
+ assert_equal "Infinity", static_inspect("1e1000")
+ assert_equal "-Infinity", static_inspect("-1e1000")
+ end
+
+ def test_imaginary
+ assert_equal "(0+1i)", static_inspect("1i")
+ assert_equal "(0-1i)", static_inspect("-1i")
+ end
+
+ def test_integer
+ assert_equal "1000", static_inspect("1_0_0_0")
+ assert_equal "10000000000000000000000000000", static_inspect("1_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0")
+ end
+
+ def test_nil
+ assert_equal "nil", static_inspect("nil")
+ end
+
+ def test_rational
+ assert_equal "(0/1)", static_inspect("0r")
+ assert_equal "(1/1)", static_inspect("1r")
+ assert_equal "(1/1)", static_inspect("1.0r")
+ assert_equal "(77777/1000)", static_inspect("77.777r")
+ end
+
+ def test_regular_expression
+ assert_equal "/.*/", static_inspect("/.*/")
+ assert_equal "/.*/i", static_inspect("/.*/i")
+ assert_equal "/.*/", static_inspect("/.*/u")
+ assert_equal "/.*/n", static_inspect("/.*/un")
+ end
+
+ def test_source_encoding
+ assert_equal "#<Encoding:UTF-8>", static_inspect("__ENCODING__")
+ assert_equal "#<Encoding:Windows-31J>", static_inspect("__ENCODING__", encoding: "Windows-31J")
+ end
+
+ def test_source_file
+ assert_equal __FILE__.inspect, static_inspect("__FILE__", filepath: __FILE__, frozen_string_literal: true)
+ end
+
+ def test_source_line
+ assert_equal "1", static_inspect("__LINE__")
+ assert_equal "5", static_inspect("__LINE__", line: 5)
+ end
+
+ def test_string
+ assert_equal "\"\"", static_inspect('""', frozen_string_literal: true)
+ assert_equal "\"Hello, World!\"", static_inspect('"Hello, World!"', frozen_string_literal: true)
+ assert_equal "\"\\a\"", static_inspect("\"\\a\"", frozen_string_literal: true)
+ end
+
+ def test_symbol
+ assert_equal ":foo", static_inspect(":foo")
+ assert_equal ":foo", static_inspect("%s[foo]")
+ end
+
+ def test_true
+ assert_equal "true", static_inspect("true")
+ end
+
+ private
+
+ def static_inspect(source, **options)
+ Debug.static_inspect(source, **options)
+ end
+ end
+end
diff --git a/test/prism/static_literals_test.rb b/test/prism/static_literals_test.rb
new file mode 100644
index 0000000000..31c802bf90
--- /dev/null
+++ b/test/prism/static_literals_test.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+module Prism
+ class StaticLiteralsTest < TestCase
+ def test_static_literals
+ assert_warning("1")
+ assert_warning("0xA", "10", "10")
+ assert_warning("0o10", "8", "8")
+ assert_warning("0b10", "2", "2")
+ assert_warning("1_000", "1000", "1000")
+ assert_warning((2**32).to_s(10), "0x#{(2**32).to_s(16)}", (2**32).to_s(10))
+ assert_warning((2**64).to_s(10), "0x#{(2**64).to_s(16)}", (2**64).to_s(10))
+
+ refute_warning("1", "-1")
+ refute_warning((2**32).to_s(10), "-0x#{(2**32).to_s(16)}")
+ refute_warning((2**64).to_s(10), "-0x#{(2**64).to_s(16)}")
+
+ assert_warning("__LINE__", "2", "2")
+ assert_warning("3", "__LINE__", "3")
+
+ assert_warning("1.0")
+ assert_warning("1e2", "100.0", "100.0")
+
+ assert_warning("1r", "1r", "(1/1)")
+ assert_warning("1.0r", "1.0r", "(1/1)")
+
+ assert_warning("1i", "1i", "(0+1i)")
+ assert_warning("1.0i", "1.0i", "(0+1.0i)")
+
+ assert_warning("1ri", "1ri", "(0+(1/1)*i)")
+ assert_warning("1.0ri", "1.0ri", "(0+(1/1)*i)")
+
+ assert_warning("__FILE__", "\"#{__FILE__}\"", __FILE__)
+ assert_warning("\"#{__FILE__}\"")
+ assert_warning("\"foo\"")
+
+ assert_warning("/foo/")
+
+ refute_warning("/foo/", "/foo/i")
+
+ assert_warning(":foo")
+ assert_warning("%s[foo]", ":foo", ":foo")
+
+ assert_warning("true")
+ assert_warning("false")
+ assert_warning("nil")
+ assert_warning("__ENCODING__", "__ENCODING__", "#<Encoding:UTF-8>")
+ end
+
+ private
+
+ class NullWarning
+ def message
+ ""
+ end
+ end
+
+ def parse_warnings(left, right)
+ warnings = []
+
+ warnings << (Prism.parse(<<~RUBY, filepath: __FILE__).warnings.first || NullWarning.new)
+ {
+ #{left} => 1,
+ #{right} => 2
+ }
+ RUBY
+
+ warnings << (Prism.parse(<<~RUBY, filepath: __FILE__).warnings.first || NullWarning.new)
+ case foo
+ when #{left}
+ when #{right}
+ end
+ RUBY
+
+ warnings
+ end
+
+ def assert_warning(left, right = left, message = left)
+ hash_keys, when_clauses = parse_warnings(left, right)
+
+ assert_include hash_keys.message, message
+ assert_include hash_keys.message, "line 3"
+ assert_include when_clauses.message, "line 3"
+ end
+
+ def refute_warning(left, right)
+ assert_empty parse_warnings(left, right).grep_v(NullWarning)
+ end
+ end
+end
diff --git a/test/prism/test_helper.rb b/test/prism/test_helper.rb
new file mode 100644
index 0000000000..77af7e7b45
--- /dev/null
+++ b/test/prism/test_helper.rb
@@ -0,0 +1,126 @@
+# frozen_string_literal: true
+
+require "prism"
+require "ripper"
+require "pp"
+require "test/unit"
+require "tempfile"
+
+puts "Using prism backend: #{Prism::BACKEND}" if ENV["PRISM_FFI_BACKEND"]
+
+# It is useful to have a diff even if the strings to compare are big
+# However, ruby/ruby does not have a version of Test::Unit with access to
+# max_diff_target_string_size
+if defined?(Test::Unit::Assertions::AssertionMessage)
+ Test::Unit::Assertions::AssertionMessage.max_diff_target_string_size = 5000
+end
+
+module Prism
+ class TestCase < ::Test::Unit::TestCase
+ private
+
+ if RUBY_ENGINE == "ruby"
+ # Check that the given source is valid syntax by compiling it with RubyVM.
+ def check_syntax(source)
+ $VERBOSE, previous = nil, $VERBOSE
+
+ begin
+ RubyVM::InstructionSequence.compile(source)
+ ensure
+ $VERBOSE = previous
+ end
+ end
+
+ # Assert that the given source is valid Ruby syntax by attempting to
+ # compile it, and then implicitly checking that it does not raise an
+ # syntax errors.
+ def assert_valid_syntax(source)
+ check_syntax(source)
+ end
+
+ # Refute that the given source is invalid Ruby syntax by attempting to
+ # compile it and asserting that it raises a SyntaxError.
+ def refute_valid_syntax(source)
+ assert_raise(SyntaxError) { check_syntax(source) }
+ end
+ else
+ def assert_valid_syntax(source)
+ end
+
+ def refute_valid_syntax(source)
+ end
+ end
+
+ def assert_raises(*args, &block)
+ raise "Use assert_raise instead"
+ end
+
+ def assert_equal_nodes(expected, actual, compare_location: true, parent: nil)
+ assert_equal expected.class, actual.class
+
+ case expected
+ when Array
+ assert_equal(
+ expected.size,
+ actual.size,
+ -> { "Arrays were different sizes. Parent: #{parent.pretty_inspect}" }
+ )
+
+ expected.zip(actual).each do |(expected_element, actual_element)|
+ assert_equal_nodes(
+ expected_element,
+ actual_element,
+ compare_location: compare_location,
+ parent: actual
+ )
+ end
+ when SourceFileNode
+ expected_deconstruct = expected.deconstruct_keys(nil)
+ actual_deconstruct = actual.deconstruct_keys(nil)
+ assert_equal expected_deconstruct.keys, actual_deconstruct.keys
+
+ # Filepaths can be different if test suites were run on different
+ # machines. We accommodate for this by comparing the basenames, and not
+ # the absolute filepaths.
+ expected_filepath = expected_deconstruct.delete(:filepath)
+ actual_filepath = actual_deconstruct.delete(:filepath)
+
+ assert_equal expected_deconstruct, actual_deconstruct
+ assert_equal File.basename(expected_filepath), File.basename(actual_filepath)
+ when Node
+ deconstructed_expected = expected.deconstruct_keys(nil)
+ deconstructed_actual = actual.deconstruct_keys(nil)
+ assert_equal deconstructed_expected.keys, deconstructed_actual.keys
+
+ deconstructed_expected.each_key do |key|
+ assert_equal_nodes(
+ deconstructed_expected[key],
+ deconstructed_actual[key],
+ compare_location: compare_location,
+ parent: actual
+ )
+ end
+ when Location
+ assert_operator actual.start_offset, :<=, actual.end_offset, -> {
+ "start_offset > end_offset for #{actual.inspect}, parent is #{parent.pretty_inspect}"
+ }
+
+ if compare_location
+ assert_equal(
+ expected.start_offset,
+ actual.start_offset,
+ -> { "Start locations were different. Parent: #{parent.pretty_inspect}" }
+ )
+
+ assert_equal(
+ expected.end_offset,
+ actual.end_offset,
+ -> { "End locations were different. Parent: #{parent.pretty_inspect}" }
+ )
+ end
+ else
+ assert_equal expected, actual
+ end
+ end
+ end
+end
diff --git a/test/prism/unescape_test.rb b/test/prism/unescape_test.rb
new file mode 100644
index 0000000000..2a352c5234
--- /dev/null
+++ b/test/prism/unescape_test.rb
@@ -0,0 +1,235 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+return if RUBY_VERSION < "3.1.0" || Prism::BACKEND == :FFI
+
+module Prism
+ class UnescapeTest < TestCase
+ module Context
+ class Base
+ attr_reader :left, :right
+
+ def initialize(left, right)
+ @left = left
+ @right = right
+ end
+
+ def name
+ "#{left}#{right}".delete("\n")
+ end
+
+ private
+
+ def code(escape)
+ "#{left}\\#{escape}#{right}".b
+ end
+
+ def ruby(escape)
+ previous, $VERBOSE = $VERBOSE, nil
+
+ begin
+ yield eval(code(escape))
+ rescue SyntaxError
+ :error
+ ensure
+ $VERBOSE = previous
+ end
+ end
+
+ def prism(escape)
+ result = Prism.parse(code(escape))
+
+ if result.success?
+ yield result.value.statements.body.first
+ else
+ :error
+ end
+ end
+
+ def `(command)
+ command
+ end
+ end
+
+ class List < Base
+ def ruby_result(escape)
+ ruby(escape) { |value| value.first.to_s }
+ end
+
+ def prism_result(escape)
+ prism(escape) { |node| node.elements.first.unescaped }
+ end
+ end
+
+ class Symbol < Base
+ def ruby_result(escape)
+ ruby(escape, &:to_s)
+ end
+
+ def prism_result(escape)
+ prism(escape, &:unescaped)
+ end
+ end
+
+ class String < Base
+ def ruby_result(escape)
+ ruby(escape, &:itself)
+ end
+
+ def prism_result(escape)
+ prism(escape, &:unescaped)
+ end
+ end
+
+ class Heredoc < Base
+ def ruby_result(escape)
+ ruby(escape, &:itself)
+ end
+
+ def prism_result(escape)
+ prism(escape) do |node|
+ case node.type
+ when :interpolated_string_node, :interpolated_x_string_node
+ node.parts.flat_map(&:unescaped).join
+ else
+ node.unescaped
+ end
+ end
+ end
+ end
+
+ class RegExp < Base
+ def ruby_result(escape)
+ ruby(escape, &:source)
+ end
+
+ def prism_result(escape)
+ prism(escape, &:unescaped)
+ end
+ end
+ end
+
+ def test_char; assert_context(Context::String.new("?", "")); end
+ def test_sqte; assert_context(Context::String.new("'", "'")); end
+ def test_dqte; assert_context(Context::String.new("\"", "\"")); end
+ def test_lwrq; assert_context(Context::String.new("%q[", "]")); end
+ def test_uprq; assert_context(Context::String.new("%Q[", "]")); end
+ def test_dstr; assert_context(Context::String.new("%[", "]")); end
+ def test_xstr; assert_context(Context::String.new("`", "`")); end
+ def test_lwrx; assert_context(Context::String.new("%x[", "]")); end
+ def test_h0_1; assert_context(Context::String.new("<<H\n", "\nH")); end
+ def test_h0_2; assert_context(Context::String.new("<<'H'\n", "\nH")); end
+ def test_h0_3; assert_context(Context::String.new("<<\"H\"\n", "\nH")); end
+ def test_h0_4; assert_context(Context::String.new("<<`H`\n", "\nH")); end
+ def test_hd_1; assert_context(Context::String.new("<<-H\n", "\nH")); end
+ def test_hd_2; assert_context(Context::String.new("<<-'H'\n", "\nH")); end
+ def test_hd_3; assert_context(Context::String.new("<<-\"H\"\n", "\nH")); end
+ def test_hd_4; assert_context(Context::String.new("<<-`H`\n", "\nH")); end
+ def test_ht_1; assert_context(Context::Heredoc.new("<<~H\n", "\nH")); end
+ def test_ht_2; assert_context(Context::Heredoc.new("<<~'H'\n", "\nH")); end
+ def test_ht_3; assert_context(Context::Heredoc.new("<<~\"H\"\n", "\nH")); end
+ def test_ht_4; assert_context(Context::Heredoc.new("<<~`H`\n", "\nH")); end
+ def test_pw_1; assert_context(Context::List.new("%w[", "]")); end
+ def test_pw_2; assert_context(Context::List.new("%w<", ">")); end
+ def test_uprw; assert_context(Context::List.new("%W[", "]")); end
+ def test_lwri; assert_context(Context::List.new("%i[", "]")); end
+ def test_upri; assert_context(Context::List.new("%I[", "]")); end
+ def test_lwrs; assert_context(Context::Symbol.new("%s[", "]")); end
+ def test_sym1; assert_context(Context::Symbol.new(":'", "'")); end
+ def test_sym2; assert_context(Context::Symbol.new(":\"", "\"")); end
+ def test_reg1; assert_context(Context::RegExp.new("/", "/")); end
+ def test_reg2; assert_context(Context::RegExp.new("%r[", "]")); end
+ def test_reg3; assert_context(Context::RegExp.new("%r<", ">")); end
+ def test_reg4; assert_context(Context::RegExp.new("%r{", "}")); end
+ def test_reg5; assert_context(Context::RegExp.new("%r(", ")")); end
+ def test_reg6; assert_context(Context::RegExp.new("%r|", "|")); end
+
+ private
+
+ def assert_context(context)
+ octal = [*("0".."7")]
+ hex = [*("a".."f"), *("A".."F"), *("0".."9")]
+
+ (0...256).each do |ord|
+ # I think this might be a bug in Ruby.
+ next if context.name == "?" && ord == 0xFF
+
+ # We don't currently support scanning for the number of capture groups
+ # to validate backreferences so these are all going to fail.
+ next if (context.name == "//" || context.name.start_with?("%r")) && ord.chr.start_with?(/\d/)
+
+ # \a \b \c ...
+ assert_unescape(context, ord.chr)
+ end
+
+ # \\r\n
+ assert_unescape(context, "\r\n")
+
+ # We don't currently support scanning for the number of capture groups to
+ # validate backreferences so these are all going to fail.
+ if context.name != "//" && !context.name.start_with?("%r")
+ # \00 \01 \02 ...
+ octal.product(octal).each { |digits| assert_unescape(context, digits.join) }
+
+ # \000 \001 \002 ...
+ octal.product(octal).product(octal).each { |digits| assert_unescape(context, digits.join) }
+ end
+
+ # \x0 \x1 \x2 ...
+ hex.each { |digit| assert_unescape(context, "x#{digit}") }
+
+ # \x00 \x01 \x02 ...
+ hex.product(hex).each { |digits| assert_unescape(context, "x#{digits.join}") }
+
+ # \u0000 \u0001 \u0002 ...
+ assert_unescape(context, "u#{["5"].concat(hex.sample(3)).join}")
+
+ # The behavior of whitespace in the middle of these escape sequences
+ # changed in Ruby 3.3.0, so we only want to test against latest.
+ if RUBY_VERSION >= "3.3.0"
+ # \u{00 00} ...
+ assert_unescape(context, "u{00#{["5"].concat(hex.sample(3)).join} \t\v 00#{["5"].concat(hex.sample(3)).join}}")
+ end
+
+ (0...128).each do |ord|
+ chr = ord.chr
+ next if chr == "\\" || !chr.match?(/[[:print:]]/)
+
+ # \C-a \C-b \C-c ...
+ assert_unescape(context, "C-#{chr}")
+
+ # \ca \cb \cc ...
+ assert_unescape(context, "c#{chr}")
+
+ # \M-a \M-b \M-c ...
+ assert_unescape(context, "M-#{chr}")
+
+ # \M-\C-a \M-\C-b \M-\C-c ...
+ assert_unescape(context, "M-\\C-#{chr}")
+
+ # \M-\ca \M-\cb \M-\cc ...
+ assert_unescape(context, "M-\\c#{chr}")
+
+ # \c\M-a \c\M-b \c\M-c ...
+ assert_unescape(context, "c\\M-#{chr}")
+ end
+ end
+
+ def assert_unescape(context, escape)
+ expected = context.ruby_result(escape)
+ actual = context.prism_result(escape)
+
+ message = -> do
+ "Expected #{context.name} to unescape #{escape.inspect} to " \
+ "#{expected.inspect}, but got #{actual.inspect}"
+ end
+
+ if expected == :error || actual == :error
+ assert_equal expected, actual, message
+ else
+ assert_equal expected.bytes, actual.bytes, message
+ end
+ end
+ end
+end
diff --git a/test/prism/version_test.rb b/test/prism/version_test.rb
new file mode 100644
index 0000000000..29ee6b224c
--- /dev/null
+++ b/test/prism/version_test.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+module Prism
+ class VersionTest < TestCase
+ def test_version_is_set
+ refute_nil VERSION
+ end
+ end
+end
diff --git a/test/prism/warnings_test.rb b/test/prism/warnings_test.rb
new file mode 100644
index 0000000000..d01db01a0e
--- /dev/null
+++ b/test/prism/warnings_test.rb
@@ -0,0 +1,237 @@
+# frozen_string_literal: true
+
+return if RUBY_VERSION < "3.1"
+
+require_relative "test_helper"
+require "stringio"
+
+module Prism
+ class WarningsTest < TestCase
+ def test_ambiguous_uminus
+ assert_warning("a -b", "ambiguous first argument")
+ end
+
+ def test_ambiguous_uplus
+ assert_warning("a +b", "ambiguous first argument")
+ end
+
+ def test_ambiguous_ustar
+ assert_warning("a *b", "argument prefix")
+ end
+
+ def test_ambiguous_regexp
+ assert_warning("a /b/", "wrap regexp in parentheses")
+ end
+
+ def test_equal_in_conditional
+ assert_warning("if a = 1; end; a = a", "should be ==")
+ end
+
+ def test_dot_dot_dot_eol
+ assert_warning("_ = foo...", "... at EOL")
+ assert_warning("def foo(...) = bar ...", "... at EOL")
+
+ assert_warning("_ = foo... #", "... at EOL")
+ assert_warning("_ = foo... \t\v\f\n", "... at EOL")
+
+ refute_warning("p foo...bar")
+ refute_warning("p foo... bar")
+ end
+
+ def test_END_in_method
+ assert_warning("def foo; END {}; end", "END in method")
+ end
+
+ def test_duplicated_hash_key
+ assert_warning("{ a: 1, a: 2 }", "duplicated and overwritten")
+ assert_warning("{ a: 1, **{ a: 2 } }", "duplicated and overwritten")
+ end
+
+ def test_duplicated_when_clause
+ assert_warning("case 1; when 1, 1; end", "clause with line")
+ end
+
+ def test_float_out_of_range
+ assert_warning("_ = 1.0e100000", "out of range")
+ end
+
+ def test_integer_in_flip_flop
+ assert_warning("1 if 2..foo", "integer")
+ end
+
+ def test_keyword_eol
+ assert_warning("if\ntrue; end", "end of line")
+ assert_warning("if true\nelsif\nfalse; end", "end of line")
+ end
+
+ def test_string_in_predicate
+ assert_warning("if 'foo'; end", "string")
+ assert_warning("if \"\#{foo}\"; end", "string")
+ assert_warning("if __FILE__; end", "string")
+ end
+
+ def test_symbol_in_predicate
+ assert_warning("if :foo; end", "symbol")
+ assert_warning("if :\"\#{foo}\"; end", "symbol")
+ end
+
+ def test_literal_in_predicate
+ assert_warning("if __LINE__; end", "literal")
+ assert_warning("if __ENCODING__; end", "literal")
+ assert_warning("if 1; end", "literal")
+ assert_warning("if 1.0; end", "literal")
+ assert_warning("if 1r; end", "literal")
+ assert_warning("if 1i; end", "literal")
+ end
+
+ def test_regexp_in_predicate
+ assert_warning("if /foo/; end", "regex")
+ assert_warning("if /foo\#{bar}/; end", "regex")
+ end
+
+ def test_unused_local_variables
+ assert_warning("foo = 1", "unused")
+
+ refute_warning("foo = 1", compare: false, command_line: "e")
+ refute_warning("foo = 1", compare: false, scopes: [[]])
+
+ assert_warning("def foo; bar = 1; end", "unused")
+ assert_warning("def foo; bar, = 1; end", "unused")
+
+ refute_warning("def foo; bar &&= 1; end")
+ refute_warning("def foo; bar ||= 1; end")
+ refute_warning("def foo; bar += 1; end")
+
+ refute_warning("def foo; bar = bar; end")
+ refute_warning("def foo; bar = bar = 1; end")
+ refute_warning("def foo; bar = (bar = 1); end")
+ refute_warning("def foo; bar = begin; bar = 1; end; end")
+ refute_warning("def foo; bar = (qux; bar = 1); end")
+ refute_warning("def foo; bar, = bar = 1; end")
+ refute_warning("def foo; bar, = 1, bar = 1; end")
+
+ refute_warning("def foo(bar); end")
+ refute_warning("def foo(bar = 1); end")
+ refute_warning("def foo((bar)); end")
+ refute_warning("def foo(*bar); end")
+ refute_warning("def foo(*, bar); end")
+ refute_warning("def foo(*, (bar)); end")
+ refute_warning("def foo(bar:); end")
+ refute_warning("def foo(**bar); end")
+ refute_warning("def foo(&bar); end")
+ refute_warning("->(bar) {}")
+ refute_warning("->(; bar) {}", compare: false)
+
+ refute_warning("def foo; bar = 1; tap { bar }; end")
+ refute_warning("def foo; bar = 1; tap { baz = bar; baz }; end")
+ end
+
+ def test_void_statements
+ assert_warning("foo = 1; foo", "a variable in void")
+ assert_warning("@foo", "a variable in void")
+ assert_warning("@@foo", "a variable in void")
+ assert_warning("$foo", "a variable in void")
+ assert_warning("$+", "a variable in void")
+ assert_warning("$1", "a variable in void")
+
+ assert_warning("self", "self in void")
+ assert_warning("nil", "nil in void")
+ assert_warning("true", "true in void")
+ assert_warning("false", "false in void")
+
+ assert_warning("1", "literal in void")
+ assert_warning("1.0", "literal in void")
+ assert_warning("1r", "literal in void")
+ assert_warning("1i", "literal in void")
+ assert_warning(":foo", "literal in void")
+ assert_warning("\"foo\"", "literal in void")
+ assert_warning("\"foo\#{1}\"", "literal in void")
+ assert_warning("/foo/", "literal in void")
+ assert_warning("/foo\#{1}/", "literal in void")
+
+ assert_warning("Foo", "constant in void")
+ assert_warning("::Foo", ":: in void")
+ assert_warning("Foo::Bar", ":: in void")
+
+ assert_warning("1..2", ".. in void")
+ assert_warning("1..", ".. in void")
+ assert_warning("..2", ".. in void")
+ assert_warning("1...2", "... in void")
+ assert_warning("1...;", "... in void")
+ assert_warning("...2", "... in void")
+
+ assert_warning("defined?(foo)", "defined? in void")
+
+ assert_warning("1 + 1", "+ in void")
+ assert_warning("1 - 1", "- in void")
+ assert_warning("1 * 1", "* in void")
+ assert_warning("1 / 1", "/ in void")
+ assert_warning("1 % 1", "% in void")
+ assert_warning("1 | 1", "| in void")
+ assert_warning("1 ^ 1", "^ in void")
+ assert_warning("1 & 1", "& in void")
+ assert_warning("1 > 1", "> in void")
+ assert_warning("1 < 1", "< in void")
+
+ assert_warning("1 ** 1", "** in void")
+ assert_warning("1 <= 1", "<= in void")
+ assert_warning("1 >= 1", ">= in void")
+ assert_warning("1 != 1", "!= in void")
+ assert_warning("1 == 1", "== in void")
+ assert_warning("1 <=> 1", "<=> in void")
+
+ assert_warning("+foo", "+@ in void")
+ assert_warning("-foo", "-@ in void")
+
+ assert_warning("def foo; @bar; @baz; end", "variable in void")
+ refute_warning("def foo; @bar; end")
+ refute_warning("@foo", compare: false, scopes: [[]])
+ end
+
+ def test_unreachable_statement
+ assert_warning("begin; rescue; retry; foo; end", "statement not reached")
+
+ assert_warning("return; foo", "statement not reached")
+
+ assert_warning("tap { break; foo }", "statement not reached")
+ assert_warning("tap { break 1; foo }", "statement not reached")
+
+ assert_warning("tap { next; foo }", "statement not reached")
+ assert_warning("tap { next 1; foo }", "statement not reached")
+
+ assert_warning("tap { redo; foo }", "statement not reached")
+ end
+
+ private
+
+ def assert_warning(source, message)
+ warnings = Prism.parse(source).warnings
+
+ assert_equal 1, warnings.length
+ assert_include warnings.first.message, message
+
+ if defined?(RubyVM::AbstractSyntaxTree)
+ assert_include capture_warning { RubyVM::AbstractSyntaxTree.parse(source) }, message
+ end
+ end
+
+ def refute_warning(source, compare: true, **options)
+ assert_empty Prism.parse(source, **options).warnings
+
+ if compare && defined?(RubyVM::AbstractSyntaxTree)
+ assert_empty capture_warning { RubyVM::AbstractSyntaxTree.parse(source) }
+ end
+ end
+
+ def capture_warning
+ stderr, $stderr, verbose, $VERBOSE = $stderr, StringIO.new, $VERBOSE, true
+
+ begin
+ yield
+ $stderr.string
+ ensure
+ $stderr, $VERBOSE = stderr, verbose
+ end
+ end
+ end
+end
diff --git a/test/psych/test_numeric.rb b/test/psych/test_numeric.rb
index 9c75c016cd..4c012e4562 100644
--- a/test/psych/test_numeric.rb
+++ b/test/psych/test_numeric.rb
@@ -1,6 +1,9 @@
# frozen_string_literal: true
require_relative 'helper'
-require 'bigdecimal'
+begin
+ require 'bigdecimal'
+rescue LoadError
+end
module Psych
###
@@ -29,13 +32,13 @@ module Psych
def test_big_decimal_tag
decimal = BigDecimal("12.34")
assert_match "!ruby/object:BigDecimal", Psych.dump(decimal)
- end
+ end if defined?(BigDecimal)
def test_big_decimal_round_trip
decimal = BigDecimal("12.34")
$DEBUG = false
assert_cycle decimal
- end
+ end if defined?(BigDecimal)
def test_does_not_attempt_numeric
str = Psych.load('--- 4 roses')
diff --git a/test/psych/test_object_references.rb b/test/psych/test_object_references.rb
index 269d72242e..86bb9034b9 100644
--- a/test/psych/test_object_references.rb
+++ b/test/psych/test_object_references.rb
@@ -39,7 +39,7 @@ module Psych
rescue Psych::DisallowedClass
data = Psych.unsafe_load yml
end
- assert_equal data.first.object_id, data.last.object_id
+ assert_same data.first, data.last
end
def test_float_references
@@ -49,7 +49,7 @@ module Psych
- *name
eoyml
assert_equal data.first, data.last
- assert_equal data.first.object_id, data.last.object_id
+ assert_same data.first, data.last
end
def test_binary_references
@@ -60,7 +60,7 @@ module Psych
- *name
eoyml
assert_equal data.first, data.last
- assert_equal data.first.object_id, data.last.object_id
+ assert_same data.first, data.last
end
def test_regexp_references
@@ -70,7 +70,7 @@ module Psych
- *name
eoyml
assert_equal data.first, data.last
- assert_equal data.first.object_id, data.last.object_id
+ assert_same data.first, data.last
end
end
end
diff --git a/test/psych/test_parser.rb b/test/psych/test_parser.rb
index 3b67a8eab1..c1e0abb89d 100644
--- a/test/psych/test_parser.rb
+++ b/test/psych/test_parser.rb
@@ -384,6 +384,25 @@ module Psych
[:end_stream, [2, 0, 2, 0]]], events
end
+ if Psych::Parser.method_defined?(:code_point_limit)
+ def test_code_point_limit
+ yaml = "foo: bar\n" * 500_000
+ assert_raise(org.snakeyaml.engine.v2.exceptions.YamlEngineException) do
+ Psych.load(yaml)
+ end
+
+ assert_nothing_raised do
+ begin
+ old_code_point_limit, Psych::Parser.code_point_limit = Psych::Parser::code_point_limit, 5_000_000
+
+ Psych.load(yaml)
+ ensure
+ Psych::Parser.code_point_limit = old_code_point_limit
+ end
+ end
+ end
+ end
+
def assert_called call, with = nil, parser = @parser
if with
call = parser.handler.calls.find { |x|
diff --git a/test/psych/test_psych.rb b/test/psych/test_psych.rb
index c977e799e3..42586a8779 100644
--- a/test/psych/test_psych.rb
+++ b/test/psych/test_psych.rb
@@ -430,6 +430,32 @@ eoyml
assert_match(/\A--- :foo\n(?:\.\.\.\n)?\z/, Psych.safe_dump(:foo, permitted_symbols: [:foo]))
end
+ def test_safe_dump_stringify_names
+ yaml = <<-eoyml
+---
+foo:
+ bar: bar
+ 'no': special escapes
+ 123: number
+eoyml
+
+ payload = Psych.safe_dump({
+ foo: {
+ bar: "bar",
+ no: "special escapes",
+ 123 => "number"
+ }
+ }, stringify_names: true)
+ assert_equal yaml, payload
+
+ assert_equal("---\nfoo: :bar\n", Psych.safe_dump({foo: :bar}, stringify_names: true, permitted_symbols: [:bar]))
+
+ error = assert_raise Psych::DisallowedClass do
+ Psych.safe_dump({foo: :bar}, stringify_names: true)
+ end
+ assert_equal "Tried to dump unspecified class: Symbol(:bar)", error.message
+ end
+
def test_safe_dump_aliases
x = []
x << x
diff --git a/test/psych/test_scalar_scanner.rb b/test/psych/test_scalar_scanner.rb
index 145db58fd9..02b923afe2 100644
--- a/test/psych/test_scalar_scanner.rb
+++ b/test/psych/test_scalar_scanner.rb
@@ -150,7 +150,7 @@ module Psych
end
def test_scan_strict_int_commas_and_underscores
- # this test is to ensure adherance to YML spec using the 'strict_integer' option
+ # this test is to ensure adherence to YML spec using the 'strict_integer' option
scanner = Psych::ScalarScanner.new ClassLoader.new, strict_integer: true
assert_equal 123_456_789, scanner.tokenize('123_456_789')
assert_equal '123,456,789', scanner.tokenize('123,456,789')
diff --git a/test/psych/test_set.rb b/test/psych/test_set.rb
index 87944d839e..b4968d3425 100644
--- a/test/psych/test_set.rb
+++ b/test/psych/test_set.rb
@@ -46,5 +46,12 @@ bar: baz
@set['self'] = @set
assert_cycle(@set)
end
+
+ def test_stringify_names
+ @set[:symbol] = :value
+
+ assert_match(/^:symbol: :value/, Psych.dump(@set))
+ assert_match(/^symbol: :value/, Psych.dump(@set, stringify_names: true))
+ end
end
end
diff --git a/test/psych/test_string.rb b/test/psych/test_string.rb
index 0dc34b3083..84ae5cbb45 100644
--- a/test/psych/test_string.rb
+++ b/test/psych/test_string.rb
@@ -17,17 +17,17 @@ module Psych
end
end
- # 'y' and 'n' are kind of ambiguous. Syck treated y and n literals in
+ # 'y', 'Y', 'n', 'N' are kind of ambiguous. Syck treated those literals in
# YAML documents as strings. But this is not what the YAML 1.1 spec says.
# YAML 1.1 says they should be treated as booleans. When we're dumping
# documents, we know it's a string, so adding quotes will eliminate the
# "ambiguity" in the emitted document
- def test_y_is_quoted
- assert_match(/"y"/, Psych.dump("y"))
- end
- def test_n_is_quoted
- assert_match(/"n"/, Psych.dump("n"))
+ def test_all_yaml_1_1_booleans_are_quoted
+ yaml_1_1_booleans = %w[y Y yes Yes YES n N no No NO true True TRUE false False FALSE on On ON off Off OFF] # from https://yaml.org/type/bool.html
+ yaml_1_1_booleans.each do |boolean|
+ assert_match(/"#{boolean}"|'#{boolean}'/, Psych.dump(boolean))
+ end
end
def test_string_with_newline
diff --git a/test/psych/visitors/test_emitter.rb b/test/psych/visitors/test_emitter.rb
index 70adbb9ca0..8bcf5491ca 100644
--- a/test/psych/visitors/test_emitter.rb
+++ b/test/psych/visitors/test_emitter.rb
@@ -61,9 +61,9 @@ module Psych
@visitor.accept s
- assert_match(/key: value/, @io.string)
+ assert_include(@io.string, "key: value")
assert_equal @io.string, s.yaml
- assert(/\.\.\./ !~ s.yaml)
+ assert_not_include(s.yaml, "...")
end
def test_scalar
@@ -76,7 +76,7 @@ module Psych
@visitor.accept s
- assert_match(/hello/, @io.string)
+ assert_include(@io.string, "hello")
assert_equal @io.string, s.yaml
end
@@ -90,8 +90,8 @@ module Psych
@visitor.accept s
- assert_match(/str/, @io.string)
- assert_match(/hello/, @io.string)
+ assert_include(@io.string, "str")
+ assert_include(@io.string, "hello")
assert_equal @io.string, s.yaml
end
@@ -107,7 +107,7 @@ module Psych
@visitor.accept s
- assert_match(/- hello/, @io.string)
+ assert_include(@io.string, "- hello")
assert_equal @io.string, s.yaml
end
@@ -122,7 +122,7 @@ module Psych
@visitor.accept s
- assert_match(/key: value/, @io.string)
+ assert_include(@io.string, "key: value")
assert_equal @io.string, s.yaml
end
@@ -137,7 +137,7 @@ module Psych
@visitor.accept s
- assert_match(/&A key: \*A/, @io.string)
+ assert_include(@io.string, "&A key: \*A")
assert_equal @io.string, s.yaml
end
end
diff --git a/test/psych/visitors/test_to_ruby.rb b/test/psych/visitors/test_to_ruby.rb
index 3d4608b903..89c3676651 100644
--- a/test/psych/visitors/test_to_ruby.rb
+++ b/test/psych/visitors/test_to_ruby.rb
@@ -319,7 +319,7 @@ description:
list = seq.to_ruby
assert_equal %w{ foo foo }, list
- assert_equal list[0].object_id, list[1].object_id
+ assert_same list[0], list[1]
end
def test_mapping_with_str_tag
diff --git a/test/psych/visitors/test_yaml_tree.rb b/test/psych/visitors/test_yaml_tree.rb
index 4c48670f2f..01e685134a 100644
--- a/test/psych/visitors/test_yaml_tree.rb
+++ b/test/psych/visitors/test_yaml_tree.rb
@@ -34,7 +34,7 @@ module Psych
v << "hello world"
v.finish
- assert_match "hello world", io.string
+ assert_include io.string, "hello world"
end
def test_binary_formatting
@@ -167,9 +167,9 @@ module Psych
end
def test_string
- assert_match(/'017'/, Psych.dump({'a' => '017'}))
- assert_match(/'019'/, Psych.dump({'a' => '019'}))
- assert_match(/'01818'/, Psych.dump({'a' => '01818'}))
+ assert_include(Psych.dump({'a' => '017'}), "'017'")
+ assert_include(Psych.dump({'a' => '019'}), "'019'")
+ assert_include(Psych.dump({'a' => '01818'}), "'01818'")
end
# http://yaml.org/type/null.html
diff --git a/test/racc/assets/cadenza.y b/test/racc/assets/cadenza.y
deleted file mode 100644
index 1940ead225..0000000000
--- a/test/racc/assets/cadenza.y
+++ /dev/null
@@ -1,170 +0,0 @@
-# This grammar is released under an MIT license
-# Author: William Howard (http://github.com/whoward)
-# Source: https://github.com/whoward/cadenza/blob/master/src/cadenza.y
-
-class Cadenza::RaccParser
-
-/* expect this many shift/reduce conflicts */
-expect 37
-
-rule
- target
- : document
- | /* none */ { result = nil }
- ;
-
- parameter_list
- : logical_expression { result = [val[0]] }
- | parameter_list ',' logical_expression { result = val[0].push(val[2]) }
- ;
-
- /* this has a shift/reduce conflict but since Racc will shift in this case it is the correct behavior */
- primary_expression
- : IDENTIFIER { result = VariableNode.new(val[0].value) }
- | IDENTIFIER parameter_list { result = VariableNode.new(val[0].value, val[1]) }
- | INTEGER { result = ConstantNode.new(val[0].value) }
- | REAL { result = ConstantNode.new(val[0].value) }
- | STRING { result = ConstantNode.new(val[0].value) }
- | '(' filtered_expression ')' { result = val[1] }
- ;
-
- multiplicative_expression
- : primary_expression
- | multiplicative_expression '*' primary_expression { result = OperationNode.new(val[0], "*", val[2]) }
- | multiplicative_expression '/' primary_expression { result = OperationNode.new(val[0], "/", val[2]) }
- ;
-
- additive_expression
- : multiplicative_expression
- | additive_expression '+' multiplicative_expression { result = OperationNode.new(val[0], "+", val[2]) }
- | additive_expression '-' multiplicative_expression { result = OperationNode.new(val[0], "-", val[2]) }
- ;
-
- boolean_expression
- : additive_expression
- | boolean_expression OP_EQ additive_expression { result = OperationNode.new(val[0], "==", val[2]) }
- | boolean_expression OP_NEQ additive_expression { result = OperationNode.new(val[0], "!=", val[2]) }
- | boolean_expression OP_LEQ additive_expression { result = OperationNode.new(val[0], "<=", val[2]) }
- | boolean_expression OP_GEQ additive_expression { result = OperationNode.new(val[0], ">=", val[2]) }
- | boolean_expression '>' additive_expression { result = OperationNode.new(val[0], ">", val[2]) }
- | boolean_expression '<' additive_expression { result = OperationNode.new(val[0], "<", val[2]) }
- ;
-
- inverse_expression
- : boolean_expression
- | NOT boolean_expression { result = BooleanInverseNode.new(val[1]) }
- ;
-
- logical_expression
- : inverse_expression
- | logical_expression AND inverse_expression { result = OperationNode.new(val[0], "and", val[2]) }
- | logical_expression OR inverse_expression { result = OperationNode.new(val[0], "or", val[2]) }
- ;
-
- filter
- : IDENTIFIER { result = FilterNode.new(val[0].value) }
- | IDENTIFIER ':' parameter_list { result = FilterNode.new(val[0].value, val[2]) }
- ;
-
- filter_list
- : filter { result = [val[0]] }
- | filter_list '|' filter { result = val[0].push(val[2]) }
- ;
-
- filtered_expression
- : logical_expression
- | logical_expression '|' filter_list { result = FilteredValueNode.new(val[0], val[2]) }
- ;
-
- inject_statement
- : VAR_OPEN filtered_expression VAR_CLOSE { result = val[1] }
- ;
-
- if_tag
- : STMT_OPEN IF logical_expression STMT_CLOSE { open_scope!; result = val[2] }
- | STMT_OPEN UNLESS logical_expression STMT_CLOSE { open_scope!; result = BooleanInverseNode.new(val[2]) }
- ;
-
- else_tag
- : STMT_OPEN ELSE STMT_CLOSE { result = close_scope!; open_scope! }
- ;
-
- end_if_tag
- : STMT_OPEN ENDIF STMT_CLOSE { result = close_scope! }
- | STMT_OPEN ENDUNLESS STMT_CLOSE { result = close_scope! }
- ;
-
- if_block
- : if_tag end_if_tag { result = IfNode.new(val[0], val[1]) }
- | if_tag document end_if_tag { result = IfNode.new(val[0], val[2]) }
- | if_tag else_tag document end_if_tag { result = IfNode.new(val[0], val[1], val[3]) }
- | if_tag document else_tag end_if_tag { result = IfNode.new(val[0], val[2], val[3]) }
- | if_tag document else_tag document end_if_tag { result = IfNode.new(val[0], val[2], val[4]) }
- ;
-
- for_tag
- : STMT_OPEN FOR IDENTIFIER IN filtered_expression STMT_CLOSE { open_scope!; result = [val[2].value, val[4]] }
- ;
-
- end_for_tag
- : STMT_OPEN ENDFOR STMT_CLOSE { result = close_scope! }
- ;
-
- /* this has a shift/reduce conflict but since Racc will shift in this case it is the correct behavior */
- for_block
- : for_tag end_for_tag { result = ForNode.new(VariableNode.new(val[0].first), val[0].last, val[1]) }
- | for_tag document end_for_tag { result = ForNode.new(VariableNode.new(val[0].first), val[0].last, val[2]) }
- ;
-
- block_tag
- : STMT_OPEN BLOCK IDENTIFIER STMT_CLOSE { result = open_block_scope!(val[2].value) }
- ;
-
- end_block_tag
- : STMT_OPEN ENDBLOCK STMT_CLOSE { result = close_block_scope! }
- ;
-
- /* this has a shift/reduce conflict but since Racc will shift in this case it is the correct behavior */
- block_block
- : block_tag end_block_tag { result = BlockNode.new(val[0], val[1]) }
- | block_tag document end_block_tag { result = BlockNode.new(val[0], val[2]) }
- ;
-
- generic_block_tag
- : STMT_OPEN IDENTIFIER STMT_CLOSE { open_scope!; result = [val[1].value, []] }
- | STMT_OPEN IDENTIFIER parameter_list STMT_CLOSE { open_scope!; result = [val[1].value, val[2]] }
- ;
-
- end_generic_block_tag
- : STMT_OPEN END STMT_CLOSE { result = close_scope! }
- ;
-
- generic_block
- : generic_block_tag document end_generic_block_tag { result = GenericBlockNode.new(val[0].first, val[2], val[0].last) }
- ;
-
- extends_statement
- : STMT_OPEN EXTENDS STRING STMT_CLOSE { result = val[2].value }
- | STMT_OPEN EXTENDS IDENTIFIER STMT_CLOSE { result = VariableNode.new(val[2].value) }
- ;
-
- document_component
- : TEXT_BLOCK { result = TextNode.new(val[0].value) }
- | inject_statement
- | if_block
- | for_block
- | generic_block
- | block_block
- ;
-
- document
- : document_component { push val[0] }
- | document document_component { push val[1] }
- | extends_statement { document.extends = val[0] }
- | document extends_statement { document.extends = val[1] }
- ;
-
----- header ----
-# racc_parser.rb : generated by racc
-
----- inner ----
diff --git a/test/racc/assets/cast.y b/test/racc/assets/cast.y
deleted file mode 100644
index 5fdbca7a40..0000000000
--- a/test/racc/assets/cast.y
+++ /dev/null
@@ -1,926 +0,0 @@
-# The MIT License
-#
-# Copyright (c) George Ogata
-#
-# 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.
-
-class C::Parser
-# shift/reduce conflict on "if (c) if (c) ; else ; else ;"
-expect 1
-rule
-
-# A.2.4 External definitions
-
-# Returns TranslationUnit
-translation_unit
- : external_declaration {result = TranslationUnit.new_at(val[0].pos, NodeChain[val[0]])}
- | translation_unit external_declaration {result = val[0]; result.entities << val[1]}
-
-# Returns Declaration|FunctionDef
-external_declaration
- : function_definition {result = val[0]}
- | declaration {result = val[0]}
-
-# Returns FunctionDef
-function_definition
- : declaration_specifiers declarator declaration_list compound_statement {result = make_function_def(val[0][0], val[0][1], val[1], val[2], val[3])}
- | declaration_specifiers declarator compound_statement {result = make_function_def(val[0][0], val[0][1], val[1], nil , val[2])}
-
-# Returns [Declaration]
-declaration_list
- : declaration {result = [val[0]]}
- | declaration_list declaration {result = val[0] << val[1]}
-
-# A.2.3 Statements
-
-# Returns Statement
-statement
- : labeled_statement {result = val[0]}
- | compound_statement {result = val[0]}
- | expression_statement {result = val[0]}
- | selection_statement {result = val[0]}
- | iteration_statement {result = val[0]}
- | jump_statement {result = val[0]}
-
-# Returns Statement
-labeled_statement
- : identifier COLON statement {val[2].labels.unshift(PlainLabel.new_at(val[0].pos, val[0].val)); result = val[2]}
- | CASE constant_expression COLON statement {val[3].labels.unshift(Case .new_at(val[0].pos, val[1] )); result = val[3]}
- | DEFAULT COLON statement {val[2].labels.unshift(Default .new_at(val[0].pos )); result = val[2]}
- # type names can also be used as labels
- | typedef_name COLON statement {val[2].labels.unshift(PlainLabel.new_at(val[0].pos, val[0].name)); result = val[2]}
-
-# Returns Block
-compound_statement
- : LBRACE block_item_list RBRACE {result = Block.new_at(val[0].pos, val[1])}
- | LBRACE RBRACE {result = Block.new_at(val[0].pos )}
-
-# Returns NodeChain[Declaration|Statement]
-block_item_list
- : block_item {result = NodeChain[val[0]]}
- | block_item_list block_item {result = val[0] << val[1]}
-
-# Returns Declaration|Statement
-block_item
- : declaration {result = val[0]}
- | statement {result = val[0]}
-
-# Returns ExpressionStatement
-expression_statement
- : expression SEMICOLON {result = ExpressionStatement.new_at(val[0].pos, val[0])}
- | SEMICOLON {result = ExpressionStatement.new_at(val[0].pos )}
-
-# Returns Statement
-selection_statement
- : IF LPAREN expression RPAREN statement {result = If .new_at(val[0].pos, val[2], val[4] )}
- | IF LPAREN expression RPAREN statement ELSE statement {result = If .new_at(val[0].pos, val[2], val[4], val[6])}
- | SWITCH LPAREN expression RPAREN statement {result = Switch.new_at(val[0].pos, val[2], val[4] )}
-
-# Returns Statement
-iteration_statement
- : WHILE LPAREN expression RPAREN statement {result = While.new_at(val[0].pos, val[2], val[4] )}
- | DO statement WHILE LPAREN expression RPAREN SEMICOLON {result = While.new_at(val[0].pos, val[4], val[1], :do => true )}
- | FOR LPAREN expression SEMICOLON expression SEMICOLON expression RPAREN statement {result = For.new_at(val[0].pos, val[2], val[4], val[6], val[8])}
- | FOR LPAREN expression SEMICOLON expression SEMICOLON RPAREN statement {result = For.new_at(val[0].pos, val[2], val[4], nil , val[7])}
- | FOR LPAREN expression SEMICOLON SEMICOLON expression RPAREN statement {result = For.new_at(val[0].pos, val[2], nil , val[5], val[7])}
- | FOR LPAREN expression SEMICOLON SEMICOLON RPAREN statement {result = For.new_at(val[0].pos, val[2], nil , nil , val[6])}
- | FOR LPAREN SEMICOLON expression SEMICOLON expression RPAREN statement {result = For.new_at(val[0].pos, nil , val[3], val[5], val[7])}
- | FOR LPAREN SEMICOLON expression SEMICOLON RPAREN statement {result = For.new_at(val[0].pos, nil , val[3], nil , val[6])}
- | FOR LPAREN SEMICOLON SEMICOLON expression RPAREN statement {result = For.new_at(val[0].pos, nil , nil , val[4], val[6])}
- | FOR LPAREN SEMICOLON SEMICOLON RPAREN statement {result = For.new_at(val[0].pos, nil , nil , nil , val[5])}
- | FOR LPAREN declaration expression SEMICOLON expression RPAREN statement {result = For.new_at(val[0].pos, val[2], val[3], val[5], val[7])}
- | FOR LPAREN declaration expression SEMICOLON RPAREN statement {result = For.new_at(val[0].pos, val[2], val[3], nil , val[6])}
- | FOR LPAREN declaration SEMICOLON expression RPAREN statement {result = For.new_at(val[0].pos, val[2], nil , val[4], val[6])}
- | FOR LPAREN declaration SEMICOLON RPAREN statement {result = For.new_at(val[0].pos, val[2], nil , nil , val[5])}
-
-# Returns Statement
-jump_statement
- : GOTO identifier SEMICOLON {result = Goto .new_at(val[0].pos, val[1].val)}
- | CONTINUE SEMICOLON {result = Continue.new_at(val[0].pos )}
- | BREAK SEMICOLON {result = Break .new_at(val[0].pos )}
- | RETURN expression SEMICOLON {result = Return .new_at(val[0].pos, val[1] )}
- | RETURN SEMICOLON {result = Return .new_at(val[0].pos )}
- # type names can also be used as labels
- | GOTO typedef_name SEMICOLON {result = Goto .new_at(val[0].pos, val[1].name)}
-
-# A.2.2 Declarations
-
-# Returns Declaration
-declaration
- : declaration_specifiers init_declarator_list SEMICOLON {result = make_declaration(val[0][0], val[0][1], val[1])}
- | declaration_specifiers SEMICOLON {result = make_declaration(val[0][0], val[0][1], NodeArray[])}
-
-# Returns {Pos, [Symbol]}
-declaration_specifiers
- : storage_class_specifier declaration_specifiers {val[1][1] << val[0][1]; result = val[1]}
- | storage_class_specifier {result = [val[0][0], [val[0][1]]]}
- | type_specifier declaration_specifiers {val[1][1] << val[0][1]; result = val[1]}
- | type_specifier {result = [val[0][0], [val[0][1]]]}
- | type_qualifier declaration_specifiers {val[1][1] << val[0][1]; result = val[1]}
- | type_qualifier {result = [val[0][0], [val[0][1]]]}
- | function_specifier declaration_specifiers {val[1][1] << val[0][1]; result = val[1]}
- | function_specifier {result = [val[0][0], [val[0][1]]]}
-
-# Returns NodeArray[Declarator]
-init_declarator_list
- : init_declarator {result = NodeArray[val[0]]}
- | init_declarator_list COMMA init_declarator {result = val[0] << val[2]}
-
-# Returns Declarator
-init_declarator
- : declarator {result = val[0]}
- | declarator EQ initializer {val[0].init = val[2]; result = val[0]}
-
-# Returns [Pos, Symbol]
-storage_class_specifier
- : TYPEDEF {result = [val[0].pos, :typedef ]}
- | EXTERN {result = [val[0].pos, :extern ]}
- | STATIC {result = [val[0].pos, :static ]}
- | AUTO {result = [val[0].pos, :auto ]}
- | REGISTER {result = [val[0].pos, :register]}
-
-# Returns [Pos, Type|Symbol]
-type_specifier
- : VOID {result = [val[0].pos, :void ]}
- | CHAR {result = [val[0].pos, :char ]}
- | SHORT {result = [val[0].pos, :short ]}
- | INT {result = [val[0].pos, :int ]}
- | LONG {result = [val[0].pos, :long ]}
- | FLOAT {result = [val[0].pos, :float ]}
- | DOUBLE {result = [val[0].pos, :double ]}
- | SIGNED {result = [val[0].pos, :signed ]}
- | UNSIGNED {result = [val[0].pos, :unsigned ]}
- | BOOL {result = [val[0].pos, :_Bool ]}
- | COMPLEX {result = [val[0].pos, :_Complex ]}
- | IMAGINARY {result = [val[0].pos, :_Imaginary]}
- | struct_or_union_specifier {result = [val[0].pos, val[0] ]}
- | enum_specifier {result = [val[0].pos, val[0] ]}
- | typedef_name {result = [val[0].pos, val[0] ]}
-
-# Returns Struct|Union
-struct_or_union_specifier
- : struct_or_union identifier LBRACE struct_declaration_list RBRACE {result = val[0][1].new_at(val[0][0], val[1].val, val[3])}
- | struct_or_union LBRACE struct_declaration_list RBRACE {result = val[0][1].new_at(val[0][0], nil , val[2])}
- | struct_or_union identifier {result = val[0][1].new_at(val[0][0], val[1].val, nil )}
- # type names can also be used as struct identifiers
- | struct_or_union typedef_name LBRACE struct_declaration_list RBRACE {result = val[0][1].new_at(val[0][0], val[1].name, val[3])}
- | struct_or_union typedef_name {result = val[0][1].new_at(val[0][0], val[1].name, nil )}
-
-# Returns [Pos, Class]
-struct_or_union
- : STRUCT {result = [val[0].pos, Struct]}
- | UNION {result = [val[0].pos, Union ]}
-
-# Returns NodeArray[Declaration]
-struct_declaration_list
- : struct_declaration {result = NodeArray[val[0]]}
- | struct_declaration_list struct_declaration {val[0] << val[1]; result = val[0]}
-
-# Returns Declaration
-struct_declaration
- : specifier_qualifier_list struct_declarator_list SEMICOLON {result = make_declaration(val[0][0], val[0][1], val[1])}
-
-# Returns {Pos, [Symbol]}
-specifier_qualifier_list
- : type_specifier specifier_qualifier_list {val[1][1] << val[0][1]; result = val[1]}
- | type_specifier {result = [val[0][0], [val[0][1]]]}
- | type_qualifier specifier_qualifier_list {val[1][1] << val[0][1]; result = val[1]}
- | type_qualifier {result = [val[0][0], [val[0][1]]]}
-
-# Returns NodeArray[Declarator]
-struct_declarator_list
- : struct_declarator {result = NodeArray[val[0]]}
- | struct_declarator_list COMMA struct_declarator {result = val[0] << val[2]}
-
-# Returns Declarator
-struct_declarator
- : declarator {result = val[0]}
- | declarator COLON constant_expression {result = val[0]; val[0].num_bits = val[2]}
- | COLON constant_expression {result = Declarator.new_at(val[0].pos, :num_bits => val[1])}
-
-# Returns Enum
-enum_specifier
- : ENUM identifier LBRACE enumerator_list RBRACE {result = Enum.new_at(val[0].pos, val[1].val, val[3])}
- | ENUM LBRACE enumerator_list RBRACE {result = Enum.new_at(val[0].pos, nil , val[2])}
- | ENUM identifier LBRACE enumerator_list COMMA RBRACE {result = Enum.new_at(val[0].pos, val[1].val, val[3])}
- | ENUM LBRACE enumerator_list COMMA RBRACE {result = Enum.new_at(val[0].pos, nil , val[2])}
- | ENUM identifier {result = Enum.new_at(val[0].pos, val[1].val, nil )}
- # type names can also be used as enum names
- | ENUM typedef_name LBRACE enumerator_list RBRACE {result = Enum.new_at(val[0].pos, val[1].name, val[3])}
- | ENUM typedef_name LBRACE enumerator_list COMMA RBRACE {result = Enum.new_at(val[0].pos, val[1].name, val[3])}
- | ENUM typedef_name {result = Enum.new_at(val[0].pos, val[1].name, nil )}
-
-# Returns NodeArray[Enumerator]
-enumerator_list
- : enumerator {result = NodeArray[val[0]]}
- | enumerator_list COMMA enumerator {result = val[0] << val[2]}
-
-# Returns Enumerator
-enumerator
- : enumeration_constant {result = Enumerator.new_at(val[0].pos, val[0].val, nil )}
- | enumeration_constant EQ constant_expression {result = Enumerator.new_at(val[0].pos, val[0].val, val[2])}
-
-# Returns [Pos, Symbol]
-type_qualifier
- : CONST {result = [val[0].pos, :const ]}
- | RESTRICT {result = [val[0].pos, :restrict]}
- | VOLATILE {result = [val[0].pos, :volatile]}
-
-# Returns [Pos, Symbol]
-function_specifier
- : INLINE {result = [val[0].pos, :inline]}
-
-# Returns Declarator
-declarator
- : pointer direct_declarator {result = add_decl_type(val[1], val[0])}
- | direct_declarator {result = val[0]}
-
-# Returns Declarator
-direct_declarator
- : identifier {result = Declarator.new_at(val[0].pos, nil, val[0].val)}
- | LPAREN declarator RPAREN {result = val[1]}
- | direct_declarator LBRACKET type_qualifier_list assignment_expression RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO
- | direct_declarator LBRACKET type_qualifier_list RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO
- | direct_declarator LBRACKET assignment_expression RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos, nil, val[2]))}
- | direct_declarator LBRACKET RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))}
- | direct_declarator LBRACKET STATIC type_qualifier_list assignment_expression RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO
- | direct_declarator LBRACKET STATIC assignment_expression RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO
- | direct_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO
- | direct_declarator LBRACKET type_qualifier_list MUL RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO
- | direct_declarator LBRACKET MUL RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO
- | direct_declarator LPAREN parameter_type_list RPAREN {result = add_decl_type(val[0], Function.new_at(val[0].pos, nil, param_list(*val[2]), :var_args => val[2][1]))}
- | direct_declarator LPAREN identifier_list RPAREN {result = add_decl_type(val[0], Function.new_at(val[0].pos, nil, val[2]))}
- | direct_declarator LPAREN RPAREN {result = add_decl_type(val[0], Function.new_at(val[0].pos ))}
-
-# Returns Pointer
-pointer
- : MUL type_qualifier_list {result = add_type_quals(Pointer.new_at(val[0].pos), val[1][1]) }
- | MUL {result = Pointer.new_at(val[0].pos) }
- | MUL type_qualifier_list pointer {p = add_type_quals(Pointer.new_at(val[0].pos), val[1][1]); val[2].direct_type = p; result = val[2]}
- | MUL pointer {p = Pointer.new_at(val[0].pos) ; val[1].direct_type = p; result = val[1]}
-
-# Returns {Pos, [Symbol]}
-type_qualifier_list
- : type_qualifier {result = [val[0][0], [val[0][1]]]}
- | type_qualifier_list type_qualifier {val[0][1] << val[1][1]; result = val[0]}
-
-# Returns [NodeArray[Parameter], var_args?]
-parameter_type_list
- : parameter_list {result = [val[0], false]}
- | parameter_list COMMA ELLIPSIS {result = [val[0], true ]}
-
-# Returns NodeArray[Parameter]
-parameter_list
- : parameter_declaration {result = NodeArray[val[0]]}
- | parameter_list COMMA parameter_declaration {result = val[0] << val[2]}
-
-# Returns Parameter
-parameter_declaration
- : declaration_specifiers declarator {ind_type = val[1].indirect_type and ind_type.detach
- result = make_parameter(val[0][0], val[0][1], ind_type, val[1].name)}
- | declaration_specifiers abstract_declarator {result = make_parameter(val[0][0], val[0][1], val[1] , nil )}
- | declaration_specifiers {result = make_parameter(val[0][0], val[0][1], nil , nil )}
-
-# Returns NodeArray[Parameter]
-identifier_list
- : identifier {result = NodeArray[Parameter.new_at(val[0].pos, nil, val[0].val)]}
- | identifier_list COMMA identifier {result = val[0] << Parameter.new_at(val[2].pos, nil, val[2].val)}
-
-# Returns Type
-type_name
- : specifier_qualifier_list abstract_declarator {val[1].direct_type = make_direct_type(val[0][0], val[0][1]); result = val[1]}
- | specifier_qualifier_list {result = make_direct_type(val[0][0], val[0][1]) }
-
-# Returns Type
-abstract_declarator
- : pointer {result = val[0]}
- | pointer direct_abstract_declarator {val[1].direct_type = val[0]; result = val[1]}
- | direct_abstract_declarator {result = val[0]}
-
-# Returns Type
-direct_abstract_declarator
- : LPAREN abstract_declarator RPAREN {result = val[1]}
- | direct_abstract_declarator LBRACKET assignment_expression RBRACKET {val[0].direct_type = Array.new_at(val[0].pos, nil, val[2]); result = val[0]}
- | direct_abstract_declarator LBRACKET RBRACKET {val[0].direct_type = Array.new_at(val[0].pos, nil, nil ); result = val[0]}
- | LBRACKET assignment_expression RBRACKET {result = Array.new_at(val[0].pos, nil, val[1])}
- | LBRACKET RBRACKET {result = Array.new_at(val[0].pos )}
- | direct_abstract_declarator LBRACKET MUL RBRACKET {val[0].direct_type = Array.new_at(val[0].pos); result = val[0]} # TODO
- | LBRACKET MUL RBRACKET {result = Array.new_at(val[0].pos)} # TODO
- | direct_abstract_declarator LPAREN parameter_type_list RPAREN {val[0].direct_type = Function.new_at(val[0].pos, nil, param_list(*val[2]), val[2][1]); result = val[0]}
- | direct_abstract_declarator LPAREN RPAREN {val[0].direct_type = Function.new_at(val[0].pos ); result = val[0]}
- | LPAREN parameter_type_list RPAREN {result = Function.new_at(val[0].pos, nil, param_list(*val[1]), val[1][1])}
- | LPAREN RPAREN {result = Function.new_at(val[0].pos )}
-
-# Returns CustomType
-typedef_name
- #: identifier -- insufficient since we must distinguish between type
- # names and var names (otherwise we have a conflict)
- : TYPENAME {result = CustomType.new_at(val[0].pos, val[0].val)}
-
-# Returns Expression
-initializer
- : assignment_expression {result = val[0]}
- | LBRACE initializer_list RBRACE {result = CompoundLiteral.new_at(val[0].pos, nil, val[1])}
- | LBRACE initializer_list COMMA RBRACE {result = CompoundLiteral.new_at(val[0].pos, nil, val[1])}
-
-# Returns NodeArray[MemberInit]
-initializer_list
- : designation initializer {result = NodeArray[MemberInit.new_at(val[0][0] , val[0][1], val[1])]}
- | initializer {result = NodeArray[MemberInit.new_at(val[0].pos, nil , val[0])]}
- | initializer_list COMMA designation initializer {result = val[0] << MemberInit.new_at(val[2][0] , val[2][1], val[3])}
- | initializer_list COMMA initializer {result = val[0] << MemberInit.new_at(val[2].pos, nil , val[2])}
-
-# Returns {Pos, NodeArray[Expression|Token]}
-designation
- : designator_list EQ {result = val[0]}
-
-# Returns {Pos, NodeArray[Expression|Token]}
-designator_list
- : designator {result = val[0]; val[0][1] = NodeArray[val[0][1]]}
- | designator_list designator {result = val[0]; val[0][1] << val[1][1]}
-
-# Returns {Pos, Expression|Member}
-designator
- : LBRACKET constant_expression RBRACKET {result = [val[1].pos, val[1] ]}
- | DOT identifier {result = [val[1].pos, Member.new_at(val[1].pos, val[1].val)]}
-
-# A.2.1 Expressions
-
-# Returns Expression
-primary_expression
- : identifier {result = Variable.new_at(val[0].pos, val[0].val)}
- | constant {result = val[0]}
- | string_literal {result = val[0]}
- # GCC EXTENSION: allow a compound statement in parentheses as an expression
- | LPAREN expression RPAREN {result = val[1]}
- | LPAREN compound_statement RPAREN {block_expressions_enabled? or parse_error val[0].pos, "compound statement found where expression expected"
- result = BlockExpression.new(val[1]); result.pos = val[0].pos}
-
-# Returns Expression
-postfix_expression
- : primary_expression {result = val[0]}
- | postfix_expression LBRACKET expression RBRACKET {result = Index .new_at(val[0].pos, val[0], val[2])}
- | postfix_expression LPAREN argument_expression_list RPAREN {result = Call .new_at(val[0].pos, val[0], val[2] )}
- | postfix_expression LPAREN RPAREN {result = Call .new_at(val[0].pos, val[0], NodeArray[])}
- | postfix_expression DOT identifier {result = Dot .new_at(val[0].pos, val[0], Member.new(val[2].val))}
- | postfix_expression ARROW identifier {result = Arrow .new_at(val[0].pos, val[0], Member.new(val[2].val))}
- | postfix_expression INC {result = PostInc .new_at(val[0].pos, val[0] )}
- | postfix_expression DEC {result = PostDec .new_at(val[0].pos, val[0] )}
- | LPAREN type_name RPAREN LBRACE initializer_list RBRACE {result = CompoundLiteral.new_at(val[0].pos, val[1], val[4])}
- | LPAREN type_name RPAREN LBRACE initializer_list COMMA RBRACE {result = CompoundLiteral.new_at(val[0].pos, val[1], val[4])}
-
-# Returns [Expression|Type]
-argument_expression_list
- : argument_expression {result = NodeArray[val[0]]}
- | argument_expression_list COMMA argument_expression {result = val[0] << val[2]}
-
-# Returns Expression|Type -- EXTENSION: allow type names here too, to support some standard library macros (e.g., va_arg [7.15.1.1])
-argument_expression
- : assignment_expression {result = val[0]}
- | type_name {result = val[0]}
-
-# Returns Expression
-unary_expression
- : postfix_expression {result = val[0]}
- | INC unary_expression {result = PreInc.new_at(val[0].pos, val[1])}
- | DEC unary_expression {result = PreDec.new_at(val[0].pos, val[1])}
- | unary_operator cast_expression {result = val[0][0].new_at(val[0][1], val[1])}
- | SIZEOF unary_expression {result = Sizeof.new_at(val[0].pos, val[1])}
- | SIZEOF LPAREN type_name RPAREN {result = Sizeof.new_at(val[0].pos, val[2])}
-
-# Returns [Class, Pos]
-unary_operator
- : AND {result = [Address , val[0].pos]}
- | MUL {result = [Dereference, val[0].pos]}
- | ADD {result = [Positive , val[0].pos]}
- | SUB {result = [Negative , val[0].pos]}
- | NOT {result = [BitNot , val[0].pos]}
- | BANG {result = [Not , val[0].pos]}
-
-# Returns Expression
-cast_expression
- : unary_expression {result = val[0]}
- | LPAREN type_name RPAREN cast_expression {result = Cast.new_at(val[0].pos, val[1], val[3])}
-
-# Returns Expression
-multiplicative_expression
- : cast_expression {result = val[0]}
- | multiplicative_expression MUL cast_expression {result = Multiply.new_at(val[0].pos, val[0], val[2])}
- | multiplicative_expression DIV cast_expression {result = Divide .new_at(val[0].pos, val[0], val[2])}
- | multiplicative_expression MOD cast_expression {result = Mod .new_at(val[0].pos, val[0], val[2])}
-
-# Returns Expression
-additive_expression
- : multiplicative_expression {result = val[0]}
- | additive_expression ADD multiplicative_expression {result = Add .new_at(val[0].pos, val[0], val[2])}
- | additive_expression SUB multiplicative_expression {result = Subtract.new_at(val[0].pos, val[0], val[2])}
-
-# Returns Expression
-shift_expression
- : additive_expression {result = val[0]}
- | shift_expression LSHIFT additive_expression {result = ShiftLeft .new_at(val[0].pos, val[0], val[2])}
- | shift_expression RSHIFT additive_expression {result = ShiftRight.new_at(val[0].pos, val[0], val[2])}
-
-# Returns Expression
-relational_expression
- : shift_expression {result = val[0]}
- | relational_expression LT shift_expression {result = Less.new_at(val[0].pos, val[0], val[2])}
- | relational_expression GT shift_expression {result = More.new_at(val[0].pos, val[0], val[2])}
- | relational_expression LEQ shift_expression {result = LessOrEqual.new_at(val[0].pos, val[0], val[2])}
- | relational_expression GEQ shift_expression {result = MoreOrEqual.new_at(val[0].pos, val[0], val[2])}
-
-# Returns Expression
-equality_expression
- : relational_expression {result = val[0]}
- | equality_expression EQEQ relational_expression {result = Equal .new_at(val[0].pos, val[0], val[2])}
- | equality_expression NEQ relational_expression {result = NotEqual.new_at(val[0].pos, val[0], val[2])}
-
-# Returns Expression
-and_expression
- : equality_expression {result = val[0]}
- | and_expression AND equality_expression {result = BitAnd.new_at(val[0].pos, val[0], val[2])}
-
-# Returns Expression
-exclusive_or_expression
- : and_expression {result = val[0]}
- | exclusive_or_expression XOR and_expression {result = BitXor.new_at(val[0].pos, val[0], val[2])}
-
-# Returns Expression
-inclusive_or_expression
- : exclusive_or_expression {result = val[0]}
- | inclusive_or_expression OR exclusive_or_expression {result = BitOr.new_at(val[0].pos, val[0], val[2])}
-
-# Returns Expression
-logical_and_expression
- : inclusive_or_expression {result = val[0]}
- | logical_and_expression ANDAND inclusive_or_expression {result = And.new_at(val[0].pos, val[0], val[2])}
-
-# Returns Expression
-logical_or_expression
- : logical_and_expression {result = val[0]}
- | logical_or_expression OROR logical_and_expression {result = Or.new_at(val[0].pos, val[0], val[2])}
-
-# Returns Expression
-conditional_expression
- : logical_or_expression {result = val[0]}
- | logical_or_expression QUESTION expression COLON conditional_expression {result = Conditional.new_at(val[0].pos, val[0], val[2], val[4])}
-
-# Returns Expression
-assignment_expression
- : conditional_expression {result = val[0]}
- | unary_expression assignment_operator assignment_expression {result = val[1].new_at(val[0].pos, val[0], val[2])}
-
-# Returns Class
-assignment_operator
- : EQ {result = Assign}
- | MULEQ {result = MultiplyAssign}
- | DIVEQ {result = DivideAssign}
- | MODEQ {result = ModAssign}
- | ADDEQ {result = AddAssign}
- | SUBEQ {result = SubtractAssign}
- | LSHIFTEQ {result = ShiftLeftAssign}
- | RSHIFTEQ {result = ShiftRightAssign}
- | ANDEQ {result = BitAndAssign}
- | XOREQ {result = BitXorAssign}
- | OREQ {result = BitOrAssign}
-
-# Returns Expression
-expression
- : assignment_expression {result = val[0]}
- | expression COMMA assignment_expression {
- if val[0].is_a? Comma
- if val[2].is_a? Comma
- val[0].exprs.push(*val[2].exprs)
- else
- val[0].exprs << val[2]
- end
- result = val[0]
- else
- if val[2].is_a? Comma
- val[2].exprs.unshift(val[0])
- val[2].pos = val[0].pos
- result = val[2]
- else
- result = Comma.new_at(val[0].pos, NodeArray[val[0], val[2]])
- end
- end
- }
-
-# Returns Expression
-constant_expression
- : conditional_expression {result = val[0]}
-
-# A.1.1 -- Lexical elements
-#
-# token
-# : keyword (raw string)
-# | identifier expanded below
-# | constant expanded below
-# | string_literal expanded below
-# | punctuator (raw string)
-#
-# preprocessing-token (skip)
-
-# Returns Token
-identifier
- : ID {result = val[0]}
-
-# Returns Literal
-constant
- : ICON {result = val[0].val; result.pos = val[0].pos}
- | FCON {result = val[0].val; result.pos = val[0].pos}
- #| enumeration_constant -- these are parsed as identifiers at all
- # places the `constant' nonterminal appears
- | CCON {result = val[0].val; result.pos = val[0].pos}
-
-# Returns Token
-enumeration_constant
- : ID {result = val[0]}
-
-# Returns StringLiteral
-# Also handles string literal concatenation (6.4.5.4)
-string_literal
- : string_literal SCON {val[0].val << val[1].val.val; result = val[0]}
- | SCON { result = val[0].val; result.pos = val[0].pos }
-
----- inner
- # A.1.9 -- Preprocessing numbers -- skip
- # A.1.8 -- Header names -- skip
-
- # A.1.7 -- Puncuators -- we don't bother with {##,#,%:,%:%:} since
- # we don't do preprocessing
- @@punctuators = %r'\+\+|-[->]|&&|\|\||\.\.\.|(?:<<|>>|[<>=!*/%+\-&^|])=?|[\[\](){}.~?:;,]'
- @@digraphs = %r'<[:%]|[:%]>'
-
- # A.1.6 -- String Literals -- simple for us because we don't decode
- # the string (and indeed accept some illegal strings)
- @@string_literal = %r'L?"(?:[^\\]|\\.)*?"'m
-
- # A.1.5 -- Constants
- @@decimal_floating_constant = %r'(?:(?:\d*\.\d+|\d+\.)(?:e[-+]?\d+)?|\d+e[-+]?\d+)[fl]?'i
- @@hexadecimal_floating_constant = %r'0x(?:(?:[0-9a-f]*\.[0-9a-f]+|[0-9a-f]+\.)|[0-9a-f]+)p[-+]?\d+[fl]?'i
-
- @@integer_constant = %r'(?:[1-9][0-9]*|0x[0-9a-f]+|0[0-7]*)(?:ul?l?|ll?u?)?'i
- @@floating_constant = %r'#{@@decimal_floating_constant}|#{@@hexadecimal_floating_constant}'
- @@enumeration_constant = %r'[a-zA-Z_\\][a-zA-Z_\\0-9]*'
- @@character_constant = %r"L?'(?:[^\\]|\\.)+?'"
- # (note that as with string-literals, we accept some illegal
- # character-constants)
-
- # A.1.4 -- Universal character names -- skip
-
- # A.1.3 -- Identifiers -- skip, since an identifier is lexically
- # identical to an enumeration constant
-
- # A.1.2 Keywords
- keywords = %w'auto break case char const continue default do
-double else enum extern float for goto if inline int long register
-restrict return short signed sizeof static struct switch typedef union
- unsigned void volatile while _Bool _Complex _Imaginary'
- @@keywords = %r"#{keywords.join('|')}"
-
- def initialize
- @type_names = ::Set.new
-
- @warning_proc = lambda{}
- @pos = C::Node::Pos.new(nil, 1, 0)
- end
- def initialize_copy(x)
- @pos = x.pos.dup
- @type_names = x.type_names.dup
- end
- attr_accessor :pos, :type_names
-
- def parse(str)
- if str.respond_to? :read
- str = str.read
- end
- @str = str
- begin
- prepare_lexer(str)
- return do_parse
- rescue ParseError => e
- e.set_backtrace(caller)
- raise
- end
- end
-
- #
- # Error handler, as used by racc.
- #
- def on_error(error_token_id, error_value, value_stack)
- if error_value == '$'
- parse_error @pos, "unexpected EOF"
- else
- parse_error(error_value.pos,
- "parse error on #{token_to_str(error_token_id)} (#{error_value.val})")
- end
- end
-
- def self.feature(name)
- attr_writer "#{name}_enabled"
- class_eval <<~RUBY, __FILE__, __LINE__ + 1
- def enable_#{name}
- @#{name}_enabled = true
- end
- def #{name}_enabled?
- @#{name}_enabled
- end
- RUBY
- end
- private_class_method :feature
-
- #
- # Allow blocks in parentheses as expressions, as per the gcc
- # extension. [http://rubyurl.com/iB7]
- #
- feature :block_expressions
-
- private # ---------------------------------------------------------
-
- class Token
- attr_accessor :pos, :val
- def initialize(pos, val)
- @pos = pos
- @val = val
- end
- end
- def eat(str)
- lines = str.split(/\r\n|[\r\n]/, -1)
- if lines.length == 1
- @pos.col_num += lines[0].length
- else
- @pos.line_num += lines.length - 1
- @pos.col_num = lines[-1].length
- end
- end
-
- #
- # Make a Declaration from the given specs and declarators.
- #
- def make_declaration(pos, specs, declarators)
- specs.all?{|x| x.is_a?(Symbol) || x.is_a?(Type)} or raise specs.map{|x| x.class}.inspect
- decl = Declaration.new_at(pos, nil, declarators)
-
- # set storage class
- storage_classes = specs.find_all do |x|
- [:typedef, :extern, :static, :auto, :register].include? x
- end
- # 6.7.1p2: at most, one storage-class specifier may be given in
- # the declaration specifiers in a declaration
- storage_classes.length <= 1 or
- begin
- if declarators.length == 0
- for_name = ''
- else
- for_name = "for `#{declarators[0].name}'"
- end
- parse_error pos, "multiple or duplicate storage classes given #{for_name}'"
- end
- decl.storage = storage_classes[0]
-
- # set type (specifiers, qualifiers)
- decl.type = make_direct_type(pos, specs)
-
- # set function specifiers
- decl.inline = specs.include?(:inline)
-
- # look for new type names
- if decl.typedef?
- decl.declarators.each do |d|
- if d.name
- @type_names << d.name
- end
- end
- end
-
- return decl
- end
-
- def make_function_def(pos, specs, func_declarator, decl_list, defn)
- add_decl_type(func_declarator, make_direct_type(pos, specs))
-
- # get types from decl_list if necessary
- function = func_declarator.indirect_type
- function.is_a? Function or
- parse_error pos, "non function type for function `#{func_declarator.name}'"
- params = function.params
- if decl_list
- params.all?{|p| p.type.nil?} or
- parse_error pos, "both prototype and declaration list given for `#{func_declarator.name}'"
- decl_list.each do |declaration|
- declaration.declarators.each do |declarator|
- param = params.find{|p| p.name == declarator.name} or
- parse_error pos, "no parameter named #{declarator.name}"
- if declarator.indirect_type
- param.type = declarator.indirect_type
- param.type.direct_type = declaration.type.dup
- else
- param.type = declaration.type.dup
- end
- end
- end
- params.all?{|p| p.type} or
- begin
- s = params.find_all{|p| p.type.nil?}.map{|p| "`#{p.name}'"}.join(' and ')
- parse_error pos, "types missing for parameters #{s}"
- end
- end
-
- fd = FunctionDef.new_at(pos,
- function.detach,
- func_declarator.name,
- defn,
- :no_prototype => !decl_list.nil?)
-
- # set storage class
- # 6.9.1p4: only extern or static allowed
- specs.each do |s|
- [:typedef, :auto, :register].include?(s) and
- "`#{s}' illegal for function"
- end
- storage_classes = specs.find_all do |s|
- s == :extern || s == :static
- end
- # 6.7.1p2: at most, one storage-class specifier may be given in
- # the declaration specifiers in a declaration
- storage_classes.length <= 1 or
- "multiple or duplicate storage classes given for `#{func_declarator.name}'"
- fd.storage = storage_classes[0] if storage_classes[0]
-
- # set function specifiers
- # 6.7.4p5 'inline' can be repeated
- fd.inline = specs.include?(:inline)
-
- return fd
- end
-
- #
- # Make a direct type from the list of type specifiers and type
- # qualifiers.
- #
- def make_direct_type(pos, specs)
- specs_order = [:signed, :unsigned, :short, :long, :double, :void,
- :char, :int, :float, :_Bool, :_Complex, :_Imaginary]
-
- type_specs = specs.find_all do |x|
- specs_order.include?(x) || !x.is_a?(Symbol)
- end
- type_specs.sort! do |a, b|
- (specs_order.index(a)||100) <=> (specs_order.index(b)||100)
- end
-
- # set type specifiers
- # 6.7.2p2: the specifier list should be one of these
- type =
- case type_specs
- when [:void]
- Void.new
- when [:char]
- Char.new
- when [:signed, :char]
- Char.new :signed => true
- when [:unsigned, :char]
- Char.new :signed => false
- when [:short], [:signed, :short], [:short, :int],
- [:signed, :short, :int]
- Int.new :longness => -1
- when [:unsigned, :short], [:unsigned, :short, :int]
- Int.new :unsigned => true, :longness => -1
- when [:int], [:signed], [:signed, :int]
- Int.new
- when [:unsigned], [:unsigned, :int]
- Int.new :unsigned => true
- when [:long], [:signed, :long], [:long, :int],
- [:signed, :long, :int]
- Int.new :longness => 1
- when [:unsigned, :long], [:unsigned, :long, :int]
- Int.new :longness => 1, :unsigned => true
- when [:long, :long], [:signed, :long, :long],
- [:long, :long, :int], [:signed, :long, :long, :int]
- Int.new :longness => 2
- when [:unsigned, :long, :long], [:unsigned, :long, :long, :int]
- Int.new :longness => 2, :unsigned => true
- when [:float]
- Float.new
- when [:double]
- Float.new :longness => 1
- when [:long, :double]
- Float.new :longness => 2
- when [:_Bool]
- Bool.new
- when [:float, :_Complex]
- Complex.new
- when [:double, :_Complex]
- Complex.new :longness => 1
- when [:long, :double, :_Complex]
- Complex.new :longness => 2
- when [:float, :_Imaginary]
- Imaginary.new
- when [:double, :_Imaginary]
- Imaginary.new :longness => 1
- when [:long, :double, :_Imaginary]
- Imaginary.new :longness => 2
- else
- if type_specs.length == 1 &&
- [CustomType, Struct, Union, Enum].any?{|c| type_specs[0].is_a? c}
- type_specs[0]
- else
- if type_specs == []
- parse_error pos, "no type specifiers given"
- else
- parse_error pos, "invalid type specifier combination: #{type_specs.join(' ')}"
- end
- end
- end
- type.pos ||= pos
-
- # set type qualifiers
- # 6.7.3p4: type qualifiers can be repeated
- type.const = specs.any?{|x| x.equal? :const }
- type.restrict = specs.any?{|x| x.equal? :restrict}
- type.volatile = specs.any?{|x| x.equal? :volatile}
-
- return type
- end
-
- def make_parameter(pos, specs, indirect_type, name)
- type = indirect_type
- if type
- type.direct_type = make_direct_type(pos, specs)
- else
- type = make_direct_type(pos, specs)
- end
- [:typedef, :extern, :static, :auto, :inline].each do |sym|
- specs.include? sym and
- parse_error pos, "parameter `#{declarator.name}' declared `#{sym}'"
- end
- return Parameter.new_at(pos, type, name,
- :register => specs.include?(:register))
- end
-
- def add_type_quals(type, quals)
- type.const = quals.include?(:const )
- type.restrict = quals.include?(:restrict)
- type.volatile = quals.include?(:volatile)
- return type
- end
-
- #
- # Add te given type as the "most direct" type to the given
- # declarator. Return the declarator.
- #
- def add_decl_type(declarator, type)
- if declarator.indirect_type
- declarator.indirect_type.direct_type = type
- else
- declarator.indirect_type = type
- end
- return declarator
- end
-
- def param_list(params, var_args)
- if params.length == 1 &&
- params[0].type.is_a?(Void) &&
- params[0].name.nil?
- return NodeArray[]
- elsif params.empty?
- return nil
- else
- return params
- end
- end
-
- def parse_error(pos, str)
- raise ParseError, "#{pos}: #{str}"
- end
-
----- header
-
-require 'set'
-
-# Error classes
-module C
- class ParseError < StandardError; end
-end
-
-# Local variables:
-# mode: ruby
-# end:
diff --git a/test/racc/assets/chk.y b/test/racc/assets/chk.y
deleted file mode 100644
index 7e0ee20f1e..0000000000
--- a/test/racc/assets/chk.y
+++ /dev/null
@@ -1,126 +0,0 @@
-#
-# racc tester
-#
-
-class Calcp
-
- prechigh
- left '*' '/'
- left '+' '-'
- preclow
-
- convert
- NUMBER 'Number'
- end
-
-rule
-
- target : exp | /* none */ { result = 0 } ;
-
- exp : exp '+' exp { result += val[2]; @plus = 'plus' }
- | exp '-' exp { result -= val[2]; @str = "string test" }
- | exp '*' exp { result *= val[2] }
- | exp '/' exp { result /= val[2] }
- | '(' { $emb = true } exp ')'
- {
- raise 'must not happen' unless $emb
- result = val[2]
- }
- | '-' NUMBER { result = -val[1] }
- | NUMBER
- ;
-
-end
-
-----header
-
-class Number; end
-
-----inner
-
- def parse( src )
- $emb = false
- @plus = nil
- @str = nil
- @src = src
- result = do_parse
- if @plus
- raise 'string parse failed' unless @plus == 'plus'
- end
- if @str
- raise 'string parse failed' unless @str == 'string test'
- end
- result
- end
-
- def next_token
- @src.shift
- end
-
- def initialize
- @yydebug = true
- end
-
-----footer
-
-$parser = Calcp.new
-$test_number = 1
-
-def chk( src, ans )
- result = $parser.parse(src)
- raise "test #{$test_number} fail" unless result == ans
- $test_number += 1
-end
-
-chk(
- [ [Number, 9],
- [false, false],
- [false, false] ], 9
-)
-
-chk(
- [ [Number, 5],
- ['*', nil],
- [Number, 1],
- ['-', nil],
- [Number, 1],
- ['*', nil],
- [Number, 8],
- [false, false],
- [false, false] ], -3
-)
-
-chk(
- [ [Number, 5],
- ['+', nil],
- [Number, 2],
- ['-', nil],
- [Number, 5],
- ['+', nil],
- [Number, 2],
- ['-', nil],
- [Number, 5],
- [false, false],
- [false, false] ], -1
-)
-
-chk(
- [ ['-', nil],
- [Number, 4],
- [false, false],
- [false, false] ], -4
-)
-
-chk(
- [ [Number, 7],
- ['*', nil],
- ['(', nil],
- [Number, 4],
- ['+', nil],
- [Number, 3],
- [')', nil],
- ['-', nil],
- [Number, 9],
- [false, false],
- [false, false] ], 40
-)
diff --git a/test/racc/assets/conf.y b/test/racc/assets/conf.y
deleted file mode 100644
index de9de71d28..0000000000
--- a/test/racc/assets/conf.y
+++ /dev/null
@@ -1,16 +0,0 @@
-
-class A
-rule
-
-a: A c C expr;
-
-b: A B; # useless
-
-c: A;
-c: A;
-
-expr: expr '+' expr
-expr: expr '-' expr
-expr: NUMBER
-
-end
diff --git a/test/racc/assets/csspool.y b/test/racc/assets/csspool.y
deleted file mode 100644
index 3d6af25d85..0000000000
--- a/test/racc/assets/csspool.y
+++ /dev/null
@@ -1,729 +0,0 @@
-class CSSPool::CSS::Parser
-
-token CHARSET_SYM IMPORT_SYM STRING SEMI IDENT S COMMA LBRACE RBRACE STAR HASH
-token LSQUARE RSQUARE EQUAL INCLUDES DASHMATCH LPAREN RPAREN FUNCTION GREATER PLUS
-token SLASH NUMBER MINUS LENGTH PERCENTAGE ANGLE TIME FREQ URI
-token IMPORTANT_SYM MEDIA_SYM NOT ONLY AND NTH_PSEUDO_CLASS
-token DOCUMENT_QUERY_SYM FUNCTION_NO_QUOTE
-token TILDE
-token PREFIXMATCH SUFFIXMATCH SUBSTRINGMATCH
-token NOT_PSEUDO_CLASS
-token KEYFRAMES_SYM
-token MATCHES_PSEUDO_CLASS
-token NAMESPACE_SYM
-token MOZ_PSEUDO_ELEMENT
-token RESOLUTION
-token COLON
-token SUPPORTS_SYM
-token OR
-token VARIABLE_NAME
-token CALC_SYM
-token FONTFACE_SYM
-token UNICODE_RANGE
-token RATIO
-
-rule
- document
- : { @handler.start_document }
- stylesheet
- { @handler.end_document }
- ;
- stylesheet
- : charset stylesheet
- | import stylesheet
- | namespace stylesheet
- | charset
- | import
- | namespace
- | body
- |
- ;
- charset
- : CHARSET_SYM STRING SEMI { @handler.charset interpret_string(val[1]), {} }
- ;
- import
- : IMPORT_SYM import_location medium SEMI {
- @handler.import_style val[2], val[1]
- }
- | IMPORT_SYM import_location SEMI {
- @handler.import_style [], val[1]
- }
- ;
- import_location
- : import_location S
- | STRING { result = Terms::String.new interpret_string val.first }
- | URI { result = Terms::URI.new interpret_uri val.first }
- ;
- namespace
- : NAMESPACE_SYM ident import_location SEMI {
- @handler.namespace val[1], val[2]
- }
- | NAMESPACE_SYM import_location SEMI {
- @handler.namespace nil, val[1]
- }
- ;
- medium
- : medium COMMA IDENT {
- result = val[0] << MediaType.new(val[2])
- }
- | IDENT {
- result = [MediaType.new(val[0])]
- }
- ;
- media_query_list
- : media_query { result = MediaQueryList.new([ val[0] ]) }
- | media_query_list COMMA media_query { result = val[0] << val[2] }
- | { result = MediaQueryList.new }
- ;
- media_query
- : optional_only_or_not media_type optional_and_exprs { result = MediaQuery.new(val[0], val[1], val[2]) }
- | media_expr optional_and_exprs { result = MediaQuery.new(nil, val[0], val[1]) }
- ;
- optional_only_or_not
- : ONLY { result = :only }
- | NOT { result = :not }
- | { result = nil }
- ;
- media_type
- : IDENT { result = MediaType.new(val[0]) }
- ;
- media_expr
- : LPAREN optional_space IDENT optional_space RPAREN { result = MediaType.new(val[2]) }
- | LPAREN optional_space IDENT optional_space COLON optional_space expr RPAREN { result = MediaFeature.new(val[2], val[6][0]) }
- ;
- optional_space
- : S { result = val[0] }
- | { result = nil }
- ;
- optional_and_exprs
- : optional_and_exprs AND media_expr { result = val[0] << val[2] }
- | { result = [] }
- ;
- resolution
- : RESOLUTION {
- unit = val.first.gsub(/[\s\d.]/, '')
- number = numeric(val.first)
- result = Terms::Resolution.new(number, unit)
- }
- ;
- body
- : ruleset body
- | conditional_rule body
- | keyframes_rule body
- | fontface_rule body
- | ruleset
- | conditional_rule
- | keyframes_rule
- | fontface_rule
- ;
- conditional_rule
- : media
- | document_query
- | supports
- ;
- body_in_media
- : body
- | empty_ruleset
- ;
- media
- : start_media body_in_media RBRACE { @handler.end_media val.first }
- ;
- start_media
- : MEDIA_SYM media_query_list LBRACE {
- result = val[1]
- @handler.start_media result
- }
- ;
- document_query
- : start_document_query body RBRACE { @handler.end_document_query(before_pos(val), after_pos(val)) }
- | start_document_query RBRACE { @handler.end_document_query(before_pos(val), after_pos(val)) }
- ;
- start_document_query
- : start_document_query_pos url_match_fns LBRACE {
- @handler.start_document_query(val[1], after_pos(val))
- }
- ;
- start_document_query_pos
- : DOCUMENT_QUERY_SYM {
- @handler.node_start_pos = before_pos(val)
- }
- ;
- url_match_fns
- : url_match_fn COMMA url_match_fns {
- result = [val[0], val[2]].flatten
- }
- | url_match_fn {
- result = val
- }
- ;
- url_match_fn
- : function_no_quote
- | function
- | uri
- ;
- supports
- : start_supports body RBRACE { @handler.end_supports }
- | start_supports RBRACE { @handler.end_supports }
- ;
- start_supports
- : SUPPORTS_SYM supports_condition_root LBRACE {
- @handler.start_supports val[1]
- }
- ;
- supports_condition_root
- : supports_negation { result = val.join('') }
- | supports_conjunction_or_disjunction { result = val.join('') }
- | supports_condition_in_parens { result = val.join('') }
- ;
- supports_condition
- : supports_negation { result = val.join('') }
- | supports_conjunction_or_disjunction { result = val.join('') }
- | supports_condition_in_parens { result = val.join('') }
- ;
- supports_condition_in_parens
- : LPAREN supports_condition RPAREN { result = val.join('') }
- | supports_declaration_condition { result = val.join('') }
- ;
- supports_negation
- : NOT supports_condition_in_parens { result = val.join('') }
- ;
- supports_conjunction_or_disjunction
- : supports_conjunction
- | supports_disjunction
- ;
- supports_conjunction
- : supports_condition_in_parens AND supports_condition_in_parens { result = val.join('') }
- | supports_conjunction_or_disjunction AND supports_condition_in_parens { result = val.join('') }
- ;
- supports_disjunction
- : supports_condition_in_parens OR supports_condition_in_parens { result = val.join('') }
- | supports_conjunction_or_disjunction OR supports_condition_in_parens { result = val.join('') }
- ;
- supports_declaration_condition
- : LPAREN declaration_internal RPAREN { result = val.join('') }
- | LPAREN S declaration_internal RPAREN { result = val.join('') }
- ;
- keyframes_rule
- : start_keyframes_rule keyframes_blocks RBRACE
- | start_keyframes_rule RBRACE
- ;
- start_keyframes_rule
- : KEYFRAMES_SYM IDENT LBRACE {
- @handler.start_keyframes_rule val[1]
- }
- ;
- keyframes_blocks
- : keyframes_block keyframes_blocks
- | keyframes_block
- ;
- keyframes_block
- : start_keyframes_block declarations RBRACE { @handler.end_keyframes_block }
- | start_keyframes_block RBRACE { @handler.end_keyframes_block }
- ;
- start_keyframes_block
- : keyframes_selectors LBRACE {
- @handler.start_keyframes_block val[0]
- }
- ;
- keyframes_selectors
- | keyframes_selector COMMA keyframes_selectors {
- result = val[0] + ', ' + val[2]
- }
- | keyframes_selector
- ;
- keyframes_selector
- : IDENT
- | PERCENTAGE { result = val[0].strip }
- ;
- fontface_rule
- : start_fontface_rule declarations RBRACE { @handler.end_fontface_rule }
- | start_fontface_rule RBRACE { @handler.end_fontface_rule }
- ;
- start_fontface_rule
- : FONTFACE_SYM LBRACE {
- @handler.start_fontface_rule
- }
- ;
- ruleset
- : start_selector declarations RBRACE {
- @handler.end_selector val.first
- }
- | start_selector RBRACE {
- @handler.end_selector val.first
- }
- ;
- empty_ruleset
- : optional_space {
- start = @handler.start_selector([])
- @handler.end_selector(start)
- }
- ;
- start_selector
- : S start_selector { result = val.last }
- | selectors LBRACE {
- @handler.start_selector val.first
- }
- ;
- selectors
- : selector COMMA selectors
- {
- sel = Selector.new(val.first, {})
- result = [sel].concat(val[2])
- }
- | selector
- {
- result = [Selector.new(val.first, {})]
- }
- ;
- selector
- : simple_selector combinator selector
- {
- val.flatten!
- val[2].combinator = val.delete_at 1
- result = val
- }
- | simple_selector
- ;
- combinator
- : S { result = :s }
- | GREATER { result = :> }
- | PLUS { result = :+ }
- | TILDE { result = :~ }
- ;
- simple_selector
- : element_name hcap {
- selector = val.first
- selector.additional_selectors = val.last
- result = [selector]
- }
- | element_name { result = val }
- | hcap
- {
- ss = Selectors::Simple.new nil, nil
- ss.additional_selectors = val.flatten
- result = [ss]
- }
- ;
- simple_selectors
- : simple_selector COMMA simple_selectors { result = [val[0], val[2]].flatten }
- | simple_selector
- ;
- ident_with_namespace
- : IDENT { result = [interpret_identifier(val[0]), nil] }
- | IDENT '|' IDENT { result = [interpret_identifier(val[2]), interpret_identifier(val[0])] }
- | '|' IDENT { result = [interpret_identifier(val[1]), nil] }
- | STAR '|' IDENT { result = [interpret_identifier(val[2]), '*'] }
- ;
- element_name
- : ident_with_namespace { result = Selectors::Type.new val.first[0], nil, val.first[1] }
- | STAR { result = Selectors::Universal.new val.first }
- | '|' STAR { result = Selectors::Universal.new val[1] }
- | STAR '|' STAR { result = Selectors::Universal.new val[2], nil, val[0] }
- | IDENT '|' STAR { result = Selectors::Universal.new val[2], nil, interpret_identifier(val[0]) }
- ;
- hcap
- : hash { result = val }
- | class { result = val }
- | attrib { result = val }
- | pseudo { result = val }
- | hash hcap { result = val.flatten }
- | class hcap { result = val.flatten }
- | attrib hcap { result = val.flatten }
- | pseudo hcap { result = val.flatten }
- ;
- hash
- : HASH {
- result = Selectors::Id.new interpret_identifier val.first.sub(/^#/, '')
- }
- class
- : '.' IDENT {
- result = Selectors::Class.new interpret_identifier val.last
- }
- ;
- attrib
- : LSQUARE ident_with_namespace EQUAL IDENT RSQUARE {
- result = Selectors::Attribute.new(
- val[1][0],
- interpret_identifier(val[3]),
- Selectors::Attribute::EQUALS,
- val[1][1]
- )
- }
- | LSQUARE ident_with_namespace EQUAL STRING RSQUARE {
- result = Selectors::Attribute.new(
- val[1][0],
- interpret_string(val[3]),
- Selectors::Attribute::EQUALS,
- val[1][1]
- )
- }
- | LSQUARE ident_with_namespace INCLUDES STRING RSQUARE {
- result = Selectors::Attribute.new(
- val[1][0],
- interpret_string(val[3]),
- Selectors::Attribute::INCLUDES,
- val[1][1]
- )
- }
- | LSQUARE ident_with_namespace INCLUDES IDENT RSQUARE {
- result = Selectors::Attribute.new(
- val[1][0],
- interpret_identifier(val[3]),
- Selectors::Attribute::INCLUDES,
- val[1][1]
- )
- }
- | LSQUARE ident_with_namespace DASHMATCH IDENT RSQUARE {
- result = Selectors::Attribute.new(
- val[1][0],
- interpret_identifier(val[3]),
- Selectors::Attribute::DASHMATCH,
- val[1][1]
- )
- }
- | LSQUARE ident_with_namespace DASHMATCH STRING RSQUARE {
- result = Selectors::Attribute.new(
- val[1][0],
- interpret_string(val[3]),
- Selectors::Attribute::DASHMATCH,
- val[1][1]
- )
- }
- | LSQUARE ident_with_namespace PREFIXMATCH IDENT RSQUARE {
- result = Selectors::Attribute.new(
- val[1][0],
- interpret_identifier(val[3]),
- Selectors::Attribute::PREFIXMATCH,
- val[1][1]
- )
- }
- | LSQUARE ident_with_namespace PREFIXMATCH STRING RSQUARE {
- result = Selectors::Attribute.new(
- val[1][0],
- interpret_string(val[3]),
- Selectors::Attribute::PREFIXMATCH,
- val[1][1]
- )
- }
- | LSQUARE ident_with_namespace SUFFIXMATCH IDENT RSQUARE {
- result = Selectors::Attribute.new(
- val[1][0],
- interpret_identifier(val[3]),
- Selectors::Attribute::SUFFIXMATCH,
- val[1][1]
- )
- }
- | LSQUARE ident_with_namespace SUFFIXMATCH STRING RSQUARE {
- result = Selectors::Attribute.new(
- val[1][0],
- interpret_string(val[3]),
- Selectors::Attribute::SUFFIXMATCH,
- val[1][1]
- )
- }
- | LSQUARE ident_with_namespace SUBSTRINGMATCH IDENT RSQUARE {
- result = Selectors::Attribute.new(
- val[1][0],
- interpret_identifier(val[3]),
- Selectors::Attribute::SUBSTRINGMATCH,
- val[1][1]
- )
- }
- | LSQUARE ident_with_namespace SUBSTRINGMATCH STRING RSQUARE {
- result = Selectors::Attribute.new(
- val[1][0],
- interpret_string(val[3]),
- Selectors::Attribute::SUBSTRINGMATCH,
- val[1][1]
- )
- }
- | LSQUARE ident_with_namespace RSQUARE {
- result = Selectors::Attribute.new(
- val[1][0],
- nil,
- Selectors::Attribute::SET,
- val[1][1]
- )
- }
- ;
- pseudo
- : COLON IDENT {
- result = Selectors::pseudo interpret_identifier(val[1])
- }
- | COLON COLON IDENT {
- result = Selectors::PseudoElement.new(
- interpret_identifier(val[2])
- )
- }
- | COLON FUNCTION RPAREN {
- result = Selectors::PseudoClass.new(
- interpret_identifier(val[1].sub(/\($/, '')),
- ''
- )
- }
- | COLON FUNCTION IDENT RPAREN {
- result = Selectors::PseudoClass.new(
- interpret_identifier(val[1].sub(/\($/, '')),
- interpret_identifier(val[2])
- )
- }
- | COLON NOT_PSEUDO_CLASS simple_selector RPAREN {
- result = Selectors::PseudoClass.new(
- 'not',
- val[2].first.to_s
- )
- }
- | COLON NTH_PSEUDO_CLASS {
- result = Selectors::PseudoClass.new(
- interpret_identifier(val[1].sub(/\(.*/, '')),
- interpret_identifier(val[1].sub(/.*\(/, '').sub(/\).*/, ''))
- )
- }
- | COLON MATCHES_PSEUDO_CLASS simple_selectors RPAREN {
- result = Selectors::PseudoClass.new(
- val[1].split('(').first.strip,
- val[2].join(', ')
- )
- }
- | COLON MOZ_PSEUDO_ELEMENT optional_space any_number_of_idents optional_space RPAREN {
- result = Selectors::PseudoElement.new(
- interpret_identifier(val[1].sub(/\($/, ''))
- )
- }
- | COLON COLON MOZ_PSEUDO_ELEMENT optional_space any_number_of_idents optional_space RPAREN {
- result = Selectors::PseudoElement.new(
- interpret_identifier(val[2].sub(/\($/, ''))
- )
- }
- ;
- any_number_of_idents
- :
- | multiple_idents
- ;
- multiple_idents
- : IDENT
- | IDENT COMMA multiple_idents
- ;
- # declarations can be separated by one *or more* semicolons. semi-colons at the start or end of a ruleset are also allowed
- one_or_more_semis
- : SEMI
- | SEMI one_or_more_semis
- ;
- declarations
- : declaration one_or_more_semis declarations
- | one_or_more_semis declarations
- | declaration one_or_more_semis
- | declaration
- | one_or_more_semis
- ;
- declaration
- : declaration_internal { @handler.property val.first }
- ;
- declaration_internal
- : property COLON expr prio
- { result = Declaration.new(val.first, val[2], val[3]) }
- | property COLON S expr prio
- { result = Declaration.new(val.first, val[3], val[4]) }
- | property S COLON expr prio
- { result = Declaration.new(val.first, val[3], val[4]) }
- | property S COLON S expr prio
- { result = Declaration.new(val.first, val[4], val[5]) }
- ;
- prio
- : IMPORTANT_SYM { result = true }
- | { result = false }
- ;
- property
- : IDENT { result = interpret_identifier val[0] }
- | STAR IDENT { result = interpret_identifier val.join }
- | VARIABLE_NAME { result = interpret_identifier val[0] }
- ;
- operator
- : COMMA
- | SLASH
- | EQUAL
- ;
- expr
- : term operator expr {
- result = [val.first, val.last].flatten
- val.last.first.operator = val[1]
- }
- | term expr { result = val.flatten }
- | term { result = val }
- ;
- term
- : ident
- | ratio
- | numeric
- | string
- | uri
- | hexcolor
- | calc
- | function
- | resolution
- | VARIABLE_NAME
- | uranges
- ;
- function
- : function S { result = val.first }
- | FUNCTION expr RPAREN {
- name = interpret_identifier val.first.sub(/\($/, '')
- if name == 'rgb'
- result = Terms::Rgb.new(*val[1])
- else
- result = Terms::Function.new name, val[1]
- end
- }
- | FUNCTION RPAREN {
- name = interpret_identifier val.first.sub(/\($/, '')
- result = Terms::Function.new name
- }
- ;
- function_no_quote
- : function_no_quote S { result = val.first }
- | FUNCTION_NO_QUOTE {
- parts = val.first.split('(')
- name = interpret_identifier parts.first
- result = Terms::Function.new(name, [Terms::String.new(interpret_string_no_quote(parts.last))])
- }
- ;
- uranges
- : UNICODE_RANGE COMMA uranges
- | UNICODE_RANGE
- ;
- calc
- : CALC_SYM calc_sum RPAREN optional_space {
- result = Terms::Math.new(val.first.split('(').first, val[1])
- }
- ;
- # plus and minus are supposed to have whitespace around them, per http://dev.w3.org/csswg/css-values/#calc-syntax, but the numbers are eating trailing whitespace, so we inject it back in
- calc_sum
- : calc_product
- | calc_product PLUS calc_sum { val.insert(1, ' '); result = val.join('') }
- | calc_product MINUS calc_sum { val.insert(1, ' '); result = val.join('') }
- ;
- calc_product
- : calc_value
- | calc_value optional_space STAR calc_value { result = val.join('') }
- | calc_value optional_space SLASH calc_value { result = val.join('') }
- ;
- calc_value
- : numeric { result = val.join('') }
- | function { result = val.join('') } # for var() variable references
- | LPAREN calc_sum RPAREN { result = val.join('') }
- ;
- hexcolor
- : hexcolor S { result = val.first }
- | HASH { result = Terms::Hash.new val.first.sub(/^#/, '') }
- ;
- uri
- : uri S { result = val.first }
- | URI { result = Terms::URI.new interpret_uri val.first }
- ;
- string
- : string S { result = val.first }
- | STRING { result = Terms::String.new interpret_string val.first }
- ;
- numeric
- : unary_operator numeric {
- result = val[1]
- val[1].unary_operator = val.first
- }
- | NUMBER {
- result = Terms::Number.new numeric val.first
- }
- | PERCENTAGE {
- result = Terms::Number.new numeric(val.first), nil, '%'
- }
- | LENGTH {
- unit = val.first.gsub(/[\s\d.]/, '')
- result = Terms::Number.new numeric(val.first), nil, unit
- }
- | ANGLE {
- unit = val.first.gsub(/[\s\d.]/, '')
- result = Terms::Number.new numeric(val.first), nil, unit
- }
- | TIME {
- unit = val.first.gsub(/[\s\d.]/, '')
- result = Terms::Number.new numeric(val.first), nil, unit
- }
- | FREQ {
- unit = val.first.gsub(/[\s\d.]/, '')
- result = Terms::Number.new numeric(val.first), nil, unit
- }
- ;
- ratio
- : RATIO {
- result = Terms::Ratio.new(val[0], val[1])
- }
- ;
- unary_operator
- : MINUS { result = :minus }
- | PLUS { result = :plus }
- ;
- ident
- : ident S { result = val.first }
- | IDENT { result = Terms::Ident.new interpret_identifier val.first }
- ;
-
----- inner
-
-def numeric thing
- thing = thing.gsub(/[^\d.]/, '')
- Integer(thing) rescue Float(thing)
-end
-
-def interpret_identifier s
- interpret_escapes s
-end
-
-def interpret_uri s
- interpret_escapes s.match(/^url\((.*)\)$/mui)[1].strip.match(/^(['"]?)((?:\\.|.)*)\1$/mu)[2]
-end
-
-def interpret_string_no_quote s
- interpret_escapes s.match(/^(.*)\)$/mu)[1].strip.match(/^(['"]?)((?:\\.|.)*)\1$/mu)[2]
-end
-
-def interpret_string s
- interpret_escapes s.match(/^(['"])((?:\\.|.)*)\1$/mu)[2]
-end
-
-def interpret_escapes s
- token_exp = /\\(?:([0-9a-fA-F]{1,6}(?:\r\n|\s)?)|(.))/mu
- return s.gsub(token_exp) do |escape_sequence|
- if !$1.nil?
- code = $1.chomp.to_i 16
- code = 0xFFFD if code > 0x10FFFF
- next [code].pack('U')
- end
- next '' if $2 == "\n"
- next $2
- end
-end
-
-# override racc's on_error so we can have context in our error messages
-def on_error(t, val, vstack)
- errcontext = (@ss.pre_match[-10..-1] || @ss.pre_match) +
- @ss.matched + @ss.post_match[0..9]
- line_number = @ss.pre_match.lines.count
- raise ParseError, sprintf("parse error on value %s (%s) " +
- "on line %s around \"%s\"",
- val.inspect, token_to_str(t) || '?',
- line_number, errcontext)
-end
-
-def before_pos(val)
- # don't include leading whitespace
- return current_pos - val.last.length + val.last[/\A\s*/].size
-end
-
-def after_pos(val)
- # don't include trailing whitespace
- return current_pos - val.last[/\s*\z/].size
-end
-
-# charpos will work with multibyte strings but is not available until ruby 2
-def current_pos
- @ss.respond_to?('charpos') ? @ss.charpos : @ss.pos
-end
diff --git a/test/racc/assets/digraph.y b/test/racc/assets/digraph.y
deleted file mode 100644
index 17a034ee54..0000000000
--- a/test/racc/assets/digraph.y
+++ /dev/null
@@ -1,29 +0,0 @@
-# ? detect digraph bug
-
-class P
- token A B C D
-rule
- target : a b c d
- a : A
- |
- b : B
- |
- c : C
- |
- d : D
- |
-end
-
----- inner
-
- def parse
- do_parse
- end
-
- def next_token
- [false, '$']
- end
-
----- footer
-
-P.new.parse
diff --git a/test/racc/assets/echk.y b/test/racc/assets/echk.y
deleted file mode 100644
index 0fda2685aa..0000000000
--- a/test/racc/assets/echk.y
+++ /dev/null
@@ -1,118 +0,0 @@
-#
-# racc tester
-#
-
-class Calcp
-
- prechigh
- left '*' '/'
- left '+' '-'
- preclow
-
- convert
- NUMBER 'Number'
- end
-
-rule
-
- target : exp | /* none */ { result = 0 } ;
-
- exp : exp '+' exp { result += val[2]; a = 'plus' }
- | exp '-' exp { result -= val[2]; "string test" }
- | exp '*' exp { result *= val[2] }
- | exp '/' exp { result /= val[2] }
- | '(' { $emb = true } exp ')'
- {
- raise 'must not happen' unless $emb
- result = val[2]
- }
- | '-' NUMBER { result = -val[1] }
- | NUMBER
- ;
-
-end
-
-----header
-
-class Number ; end
-
-----inner
-
- def parse( src )
- @src = src
- do_parse
- end
-
- def next_token
- @src.shift
- end
-
- def initialize
- @yydebug = true
- end
-
-----footer
-
-$parser = Calcp.new
-$tidx = 1
-
-def chk( src, ans )
- ret = $parser.parse( src )
- unless ret == ans then
- bug! "test #{$tidx} fail"
- end
- $tidx += 1
-end
-
-chk(
- [ [Number, 9],
- [false, false],
- [false, false] ], 9
-)
-
-chk(
- [ [Number, 5],
- ['*', nil],
- [Number, 1],
- ['-', nil],
- [Number, 1],
- ['*', nil],
- [Number, 8],
- [false, false],
- [false, false] ], -3
-)
-
-chk(
- [ [Number, 5],
- ['+', nil],
- [Number, 2],
- ['-', nil],
- [Number, 5],
- ['+', nil],
- [Number, 2],
- ['-', nil],
- [Number, 5],
- [false, false],
- [false, false] ], -1
-)
-
-chk(
- [ ['-', nil],
- [Number, 4],
- [false, false],
- [false, false] ], -4
-)
-
-chk(
- [ [Number, 7],
- ['*', nil],
- ['(', nil],
- [Number, 4],
- ['+', nil],
- [Number, 3],
- [')', nil],
- ['-', nil],
- [Number, 9],
- [false, false],
- [false, false] ], 40
-)
diff --git a/test/racc/assets/edtf.y b/test/racc/assets/edtf.y
deleted file mode 100644
index 4f5f6bb4fd..0000000000
--- a/test/racc/assets/edtf.y
+++ /dev/null
@@ -1,583 +0,0 @@
-# -*- racc -*-
-
-# Copyright 2011 Sylvester Keil. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR
-# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-# EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
-# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
-# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
-# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-# The views and conclusions contained in the software and documentation are
-# those of the authors and should not be interpreted as representing official
-# policies, either expressed or implied, of the copyright holder.
-
-class EDTF::Parser
-
-token T Z E X U UNKNOWN OPEN LONGYEAR UNMATCHED DOTS UA PUA
-
-expect 0
-
-rule
-
- edtf : level_0_expression
- | level_1_expression
- | level_2_expression
- ;
-
- # ---- Level 0 / ISO 8601 Rules ----
-
- # NB: level 0 intervals are covered by the level 1 interval rules
- level_0_expression : date
- | date_time
- ;
-
- date : positive_date
- | negative_date
- ;
-
- positive_date :
- year { result = Date.new(val[0]).year_precision! }
- | year_month { result = Date.new(*val.flatten).month_precision! }
- | year_month_day { result = Date.new(*val.flatten).day_precision! }
- ;
-
- negative_date : '-' positive_date { result = -val[1] }
-
-
- date_time : date T time {
- result = DateTime.new(val[0].year, val[0].month, val[0].day, *val[2])
- result.skip_timezone = (val[2].length == 3)
- }
-
- time : base_time
- | base_time zone_offset { result = val.flatten }
-
- base_time : hour ':' minute ':' second { result = val.values_at(0, 2, 4) }
- | midnight
-
- midnight : '2' '4' ':' '0' '0' ':' '0' '0' { result = [24, 0, 0] }
-
- zone_offset : Z { result = 0 }
- | '-' zone_offset_hour { result = -1 * val[1] }
- | '+' positive_zone_offset { result = val[1] }
- ;
-
- positive_zone_offset : zone_offset_hour
- | '0' '0' ':' '0' '0' { result = 0 }
- ;
-
-
- zone_offset_hour : d01_13 ':' minute { result = Rational(val[0] * 60 + val[2], 1440) }
- | '1' '4' ':' '0' '0' { result = Rational(840, 1440) }
- | '0' '0' ':' d01_59 { result = Rational(val[3], 1440) }
- ;
-
- year : digit digit digit digit {
- result = val.zip([1000,100,10,1]).reduce(0) { |s,(a,b)| s += a * b }
- }
-
- month : d01_12
- day : d01_31
-
- year_month : year '-' month { result = [val[0], val[2]] }
-
- # We raise an exception if there are two many days for the month, but
- # do not consider leap years, as the EDTF BNF did not either.
- # NB: an exception will be raised regardless, because the Ruby Date
- # implementation calculates leap years.
- year_month_day : year_month '-' day {
- result = val[0] << val[2]
- if result[2] > 31 || (result[2] > 30 && [2,4,6,9,11].include?(result[1])) || (result[2] > 29 && result[1] == 2)
- raise ArgumentError, "invalid date (invalid days #{result[2]} for month #{result[1]})"
- end
- }
-
- hour : d00_23
- minute : d00_59
- second : d00_59
-
- # Completely covered by level_1_interval
- # level_0_interval : date '/' date { result = Interval.new(val[0], val[1]) }
-
-
- # ---- Level 1 Extension Rules ----
-
- # NB: Uncertain/approximate Dates are covered by the Level 2 rules
- level_1_expression : unspecified | level_1_interval | long_year_simple | season
-
- # uncertain_or_approximate_date : date UA { result = uoa(val[0], val[1]) }
-
- unspecified : unspecified_year
- {
- result = Date.new(val[0][0]).year_precision!
- result.unspecified.year[2,2] = val[0][1]
- }
- | unspecified_month
- | unspecified_day
- | unspecified_day_and_month
- ;
-
- unspecified_year :
- digit digit digit U
- {
- result = [val[0,3].zip([1000,100,10]).reduce(0) { |s,(a,b)| s += a * b }, [false,true]]
- }
- | digit digit U U
- {
- result = [val[0,2].zip([1000,100]).reduce(0) { |s,(a,b)| s += a * b }, [true, true]]
- }
-
- unspecified_month : year '-' U U {
- result = Date.new(val[0]).unspecified!(:month)
- result.precision = :month
- }
-
- unspecified_day : year_month '-' U U {
- result = Date.new(*val[0]).unspecified!(:day)
- }
-
- unspecified_day_and_month : year '-' U U '-' U U {
- result = Date.new(val[0]).unspecified!([:day,:month])
- }
-
-
- level_1_interval : level_1_start '/' level_1_end {
- result = Interval.new(val[0], val[2])
- }
-
- level_1_start : date | partial_uncertain_or_approximate | unspecified | partial_unspecified | UNKNOWN
-
- level_1_end : level_1_start | OPEN
-
-
- long_year_simple :
- LONGYEAR long_year
- {
- result = Date.new(val[1])
- result.precision = :year
- }
- | LONGYEAR '-' long_year
- {
- result = Date.new(-1 * val[2])
- result.precision = :year
- }
- ;
-
- long_year :
- positive_digit digit digit digit digit {
- result = val.zip([10000,1000,100,10,1]).reduce(0) { |s,(a,b)| s += a * b }
- }
- | long_year digit { result = 10 * val[0] + val[1] }
- ;
-
-
- season : year '-' season_number ua {
- result = Season.new(val[0], val[2])
- val[3].each { |ua| result.send(ua) }
- }
-
- season_number : '2' '1' { result = 21 }
- | '2' '2' { result = 22 }
- | '2' '3' { result = 23 }
- | '2' '4' { result = 24 }
- ;
-
-
- # ---- Level 2 Extension Rules ----
-
- # NB: Level 2 Intervals are covered by the Level 1 Interval rules.
- level_2_expression : season_qualified
- | partial_uncertain_or_approximate
- | partial_unspecified
- | choice_list
- | inclusive_list
- | masked_precision
- | date_and_calendar
- | long_year_scientific
- ;
-
-
- season_qualified : season '^' { result = val[0]; result.qualifier = val[1] }
-
-
- long_year_scientific :
- long_year_simple E integer
- {
- result = Date.new(val[0].year * 10 ** val[2]).year_precision!
- }
- | LONGYEAR int1_4 E integer
- {
- result = Date.new(val[1] * 10 ** val[3]).year_precision!
- }
- | LONGYEAR '-' int1_4 E integer
- {
- result = Date.new(-1 * val[2] * 10 ** val[4]).year_precision!
- }
- ;
-
-
- date_and_calendar : date '^' { result = val[0]; result.calendar = val[1] }
-
-
- masked_precision :
- digit digit digit X
- {
- d = val[0,3].zip([1000,100,10]).reduce(0) { |s,(a,b)| s += a * b }
- result = EDTF::Decade.new(d)
- }
- | digit digit X X
- {
- d = val[0,2].zip([1000,100]).reduce(0) { |s,(a,b)| s += a * b }
- result = EDTF::Century.new(d)
- }
- ;
-
-
- choice_list : '[' list ']' { result = val[1].choice! }
-
- inclusive_list : '{' list '}' { result = val[1] }
-
- list : earlier { result = EDTF::Set.new(val[0]).earlier! }
- | earlier ',' list_elements ',' later { result = EDTF::Set.new([val[0]] + val[2] + [val[4]]).earlier!.later! }
- | earlier ',' list_elements { result = EDTF::Set.new([val[0]] + val[2]).earlier! }
- | earlier ',' later { result = EDTF::Set.new([val[0]] + [val[2]]).earlier!.later! }
- | list_elements ',' later { result = EDTF::Set.new(val[0] + [val[2]]).later! }
- | list_elements { result = EDTF::Set.new(*val[0]) }
- | later { result = EDTF::Set.new(val[0]).later! }
- ;
-
- list_elements : list_element { result = [val[0]].flatten }
- | list_elements ',' list_element { result = val[0] + [val[2]].flatten }
- ;
-
- list_element : atomic
- | consecutives
- ;
-
- atomic : date
- | partial_uncertain_or_approximate
- | unspecified
- ;
-
- earlier : DOTS date { result = val[1] }
-
- later : year_month_day DOTS { result = Date.new(*val[0]).year_precision! }
- | year_month DOTS { result = Date.new(*val[0]).month_precision! }
- | year DOTS { result = Date.new(val[0]).year_precision! }
- ;
-
- consecutives : year_month_day DOTS year_month_day { result = (Date.new(val[0]).day_precision! .. Date.new(val[2]).day_precision!) }
- | year_month DOTS year_month { result = (Date.new(val[0]).month_precision! .. Date.new(val[2]).month_precision!) }
- | year DOTS year { result = (Date.new(val[0]).year_precision! .. Date.new(val[2]).year_precision!) }
- ;
-
- partial_unspecified :
- unspecified_year '-' month '-' day
- {
- result = Date.new(val[0][0], val[2], val[4])
- result.unspecified.year[2,2] = val[0][1]
- }
- | unspecified_year '-' U U '-' day
- {
- result = Date.new(val[0][0], 1, val[5])
- result.unspecified.year[2,2] = val[0][1]
- result.unspecified!(:month)
- }
- | unspecified_year '-' U U '-' U U
- {
- result = Date.new(val[0][0], 1, 1)
- result.unspecified.year[2,2] = val[0][1]
- result.unspecified!([:month, :day])
- }
- | unspecified_year '-' month '-' U U
- {
- result = Date.new(val[0][0], val[2], 1)
- result.unspecified.year[2,2] = val[0][1]
- result.unspecified!(:day)
- }
- | year '-' U U '-' day
- {
- result = Date.new(val[0], 1, val[5])
- result.unspecified!(:month)
- }
- ;
-
-
- partial_uncertain_or_approximate : pua_base
- | '(' pua_base ')' UA { result = uoa(val[1], val[3]) }
-
- pua_base :
- pua_year { result = val[0].year_precision! }
- | pua_year_month { result = val[0][0].month_precision! }
- | pua_year_month_day { result = val[0].day_precision! }
-
- pua_year : year UA { result = uoa(Date.new(val[0]), val[1], :year) }
-
- pua_year_month :
- pua_year '-' month ua {
- result = [uoa(val[0].change(:month => val[2]), val[3], [:month, :year])]
- }
- | year '-' month UA {
- result = [uoa(Date.new(val[0], val[2]), val[3], [:year, :month])]
- }
- | year '-(' month ')' UA {
- result = [uoa(Date.new(val[0], val[2]), val[4], [:month]), true]
- }
- | pua_year '-(' month ')' UA {
- result = [uoa(val[0].change(:month => val[2]), val[4], [:month]), true]
- }
- ;
-
- pua_year_month_day :
- pua_year_month '-' day ua {
- result = uoa(val[0][0].change(:day => val[2]), val[3], val[0][1] ? [:day] : nil)
- }
- | pua_year_month '-(' day ')' UA {
- result = uoa(val[0][0].change(:day => val[2]), val[4], [:day])
- }
- | year '-(' month ')' UA day ua {
- result = uoa(uoa(Date.new(val[0], val[2], val[5]), val[4], :month), val[6], :day)
- }
- | year_month '-' day UA {
- result = uoa(Date.new(val[0][0], val[0][1], val[2]), val[3])
- }
- | year_month '-(' day ')' UA {
- result = uoa(Date.new(val[0][0], val[0][1], val[2]), val[4], [:day])
- }
- | year '-(' month '-' day ')' UA {
- result = uoa(Date.new(val[0], val[2], val[4]), val[6], [:month, :day])
- }
- | year '-(' month '-(' day ')' UA ')' UA {
- result = Date.new(val[0], val[2], val[4])
- result = uoa(result, val[6], [:day])
- result = uoa(result, val[8], [:month, :day])
- }
- | pua_year '-(' month '-' day ')' UA {
- result = val[0].change(:month => val[2], :day => val[4])
- result = uoa(result, val[6], [:month, :day])
- }
- | pua_year '-(' month '-(' day ')' UA ')' UA {
- result = val[0].change(:month => val[2], :day => val[4])
- result = uoa(result, val[6], [:day])
- result = uoa(result, val[8], [:month, :day])
- }
- # | '(' pua_year '-(' month ')' UA ')' UA '-' day ua {
- # result = val[1].change(:month => val[3], :day => val[9])
- # result = uoa(result, val[5], [:month])
- # result = [uoa(result, val[7], [:year]), true]
- # }
- ;
-
- ua : { result = [] } | UA
-
- # ---- Auxiliary Rules ----
-
- digit : '0' { result = 0 }
- | positive_digit
- ;
-
- positive_digit : '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
-
- d01_12 : '0' positive_digit { result = val[1] }
- | '1' '0' { result = 10 }
- | '1' '1' { result = 11 }
- | '1' '2' { result = 12 }
- ;
-
- d01_13 : d01_12
- | '1' '3' { result = 13 }
- ;
-
- d01_23 : '0' positive_digit { result = val[1] }
- | '1' digit { result = 10 + val[1] }
- | '2' '0' { result = 20 }
- | '2' '1' { result = 21 }
- | '2' '2' { result = 22 }
- | '2' '3' { result = 23 }
- ;
-
- d00_23 : '0' '0'
- | d01_23
- ;
-
- d01_29 : d01_23
- | '2' '4' { result = 24 }
- | '2' '5' { result = 25 }
- | '2' '6' { result = 26 }
- | '2' '7' { result = 27 }
- | '2' '8' { result = 28 }
- | '2' '9' { result = 29 }
- ;
-
- d01_30 : d01_29
- | '3' '0' { result = 30 }
- ;
-
- d01_31 : d01_30
- | '3' '1' { result = 31 }
- ;
-
- d01_59 : d01_29
- | '3' digit { result = 30 + val[1] }
- | '4' digit { result = 40 + val[1] }
- | '5' digit { result = 50 + val[1] }
- ;
-
- d00_59 : '0' '0'
- | d01_59
- ;
-
- int1_4 : positive_digit { result = val[0] }
- | positive_digit digit { result = 10 * val[0] + val[1] }
- | positive_digit digit digit
- {
- result = val.zip([100,10,1]).reduce(0) { |s,(a,b)| s += a * b }
- }
- | positive_digit digit digit digit
- {
- result = val.zip([1000,100,10,1]).reduce(0) { |s,(a,b)| s += a * b }
- }
- ;
-
- integer : positive_digit { result = val[0] }
- | integer digit { result = 10 * val[0] + val[1] }
- ;
-
-
-
----- header
-require 'strscan'
-
----- inner
-
- @defaults = {
- :level => 2,
- :debug => false
- }.freeze
-
- class << self; attr_reader :defaults; end
-
- attr_reader :options
-
- def initialize(options = {})
- @options = Parser.defaults.merge(options)
- end
-
- def debug?
- !!(options[:debug] || ENV['DEBUG'])
- end
-
- def parse(input)
- parse!(input)
- rescue => e
- warn e.message if debug?
- nil
- end
-
- def parse!(input)
- @yydebug = debug?
- @src = StringScanner.new(input)
- do_parse
- end
-
- def on_error(tid, value, stack)
- raise ArgumentError,
- "failed to parse date: unexpected '#{value}' at #{stack.inspect}"
- end
-
- def apply_uncertainty(date, uncertainty, scope = nil)
- uncertainty.each do |u|
- scope.nil? ? date.send(u) : date.send(u, scope)
- end
- date
- end
-
- alias uoa apply_uncertainty
-
- def next_token
- case
- when @src.eos?
- nil
- # when @src.scan(/\s+/)
- # ignore whitespace
- when @src.scan(/\(/)
- ['(', @src.matched]
- # when @src.scan(/\)\?~-/)
- # [:PUA, [:uncertain!, :approximate!]]
- # when @src.scan(/\)\?-/)
- # [:PUA, [:uncertain!]]
- # when @src.scan(/\)~-/)
- # [:PUA, [:approximate!]]
- when @src.scan(/\)/)
- [')', @src.matched]
- when @src.scan(/\[/)
- ['[', @src.matched]
- when @src.scan(/\]/)
- [']', @src.matched]
- when @src.scan(/\{/)
- ['{', @src.matched]
- when @src.scan(/\}/)
- ['}', @src.matched]
- when @src.scan(/T/)
- [:T, @src.matched]
- when @src.scan(/Z/)
- [:Z, @src.matched]
- when @src.scan(/\?~/)
- [:UA, [:uncertain!, :approximate!]]
- when @src.scan(/\?/)
- [:UA, [:uncertain!]]
- when @src.scan(/~/)
- [:UA, [:approximate!]]
- when @src.scan(/open/i)
- [:OPEN, :open]
- when @src.scan(/unkn?own/i) # matches 'unkown' typo too
- [:UNKNOWN, :unknown]
- when @src.scan(/u/)
- [:U, @src.matched]
- when @src.scan(/x/i)
- [:X, @src.matched]
- when @src.scan(/y/)
- [:LONGYEAR, @src.matched]
- when @src.scan(/e/)
- [:E, @src.matched]
- when @src.scan(/\+/)
- ['+', @src.matched]
- when @src.scan(/-\(/)
- ['-(', @src.matched]
- when @src.scan(/-/)
- ['-', @src.matched]
- when @src.scan(/:/)
- [':', @src.matched]
- when @src.scan(/\//)
- ['/', @src.matched]
- when @src.scan(/\s*\.\.\s*/)
- [:DOTS, '..']
- when @src.scan(/\s*,\s*/)
- [',', ',']
- when @src.scan(/\^\w+/)
- ['^', @src.matched[1..-1]]
- when @src.scan(/\d/)
- [@src.matched, @src.matched.to_i]
- else @src.scan(/./)
- [:UNMATCHED, @src.rest]
- end
- end
-
-
-# -*- racc -*-
diff --git a/test/racc/assets/err.y b/test/racc/assets/err.y
deleted file mode 100644
index ae280957cc..0000000000
--- a/test/racc/assets/err.y
+++ /dev/null
@@ -1,60 +0,0 @@
-
-class ErrTestp
-
-rule
-
-target: lines
- ;
-
-lines: line
- | lines line
- ;
-
-line: A B C D E
- | error E
- ;
-
-end
-
----- inner
-
-def initialize
- @yydebug = false
- @q = [
- [:A, 'a'],
- # [:B, 'b'],
- [:C, 'c'],
- [:D, 'd'],
- [:E, 'e'],
-
- [:A, 'a'],
- [:B, 'b'],
- [:C, 'c'],
- [:D, 'd'],
- [:E, 'e'],
-
- [:A, 'a'],
- [:B, 'b'],
- # [:C, 'c'],
- [:D, 'd'],
- [:E, 'e'],
- [false, nil]
- ]
-end
-
-def next_token
- @q.shift
-end
-
-def on_error( t, val, values )
- $stderr.puts "error on token '#{val}'(#{t})"
-end
-
-def parse
- do_parse
-end
-
----- footer
-
-p = ErrTestp.new
-p.parse
diff --git a/test/racc/assets/error_recovery.y b/test/racc/assets/error_recovery.y
deleted file mode 100644
index 7128c364ca..0000000000
--- a/test/racc/assets/error_recovery.y
+++ /dev/null
@@ -1,35 +0,0 @@
-# Regression test case for the bug discussed here:
-# https://github.com/whitequark/parser/issues/93
-# In short, a Racc-generated parser could go into an infinite loop when
-# attempting error recovery at EOF
-
-class InfiniteLoop
-
-rule
-
- stmts: stmt
- | error stmt
-
- stmt: '%' stmt
-
-end
-
----- inner
-
- def parse
- @errors = []
- do_parse
- end
-
- def next_token
- nil
- end
-
- def on_error(error_token, error_value, value_stack)
- # oh my, an error
- @errors << [error_token, error_value]
- end
-
----- footer
-
-InfiniteLoop.new.parse
diff --git a/test/racc/assets/expect.y b/test/racc/assets/expect.y
deleted file mode 100644
index 24c27443e2..0000000000
--- a/test/racc/assets/expect.y
+++ /dev/null
@@ -1,7 +0,0 @@
-class E
- expect 1
-rule
- list: inlist inlist
- inlist:
- | A
-end
diff --git a/test/racc/assets/firstline.y b/test/racc/assets/firstline.y
deleted file mode 100644
index ab0692e543..0000000000
--- a/test/racc/assets/firstline.y
+++ /dev/null
@@ -1,4 +0,0 @@
-class T
-rule
- a: A B C
-end
diff --git a/test/racc/assets/huia.y b/test/racc/assets/huia.y
deleted file mode 100644
index de9d45150c..0000000000
--- a/test/racc/assets/huia.y
+++ /dev/null
@@ -1,318 +0,0 @@
-# Copyright (c) 2014 James Harton
-#
-# MIT License
-#
-# 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.
-
-class Huia::Parser
-
- token
- IDENTIFIER EQUAL PLUS MINUS ASTERISK FWD_SLASH COLON FLOAT INTEGER STRING
- EXPO INDENT OUTDENT OPAREN CPAREN DOT SIGNATURE NL EOF PIPE COMMA NIL TRUE
- FALSE EQUALITY CALL SELF CONSTANT CHAR DOUBLE_TICK_STRING
- DOUBLE_TICK_STRING_END INTERPOLATE_START INTERPOLATE_END BOX LSQUARE
- RSQUARE FACES LFACE RFACE BANG TILDE RETURN NOT_EQUALITY OR AND GT LT
- GTE LTE AT
-
- prechigh
- left EXPO
- left BANG TILDE
- left ASTERISK FWD_SLASH PERCENT
- left PLUS MINUS
-
- right EQUAL
- preclow
-
- rule
- statements: statement
- | statements statement { return scope }
-
- statement: expr eol { return scope.append val[0] }
- | expr { return scope.append val[0] }
- | eol { return scope }
-
- eol: NL | EOF
- nlq: NL |
-
- expr: literal
- | grouped_expr
- | binary_op
- | unary_op
- | method_call
- | constant
- | variable
- | array
- | hash
- | return
-
- return: return_expr
- | return_nil
- return_expr: RETURN expr { return n(:Return, val[1]) }
- return_nil: RETURN { return n(:Return, n(:Nil)) }
-
- array: empty_array
- | array_list
-
- empty_array: BOX { return n :Array }
-
- array_list: LSQUARE array_items RSQUARE { return val[1] }
- array_items: expr { return n :Array, [val[0]] }
- | array_items COMMA expr { val[0].append(val[2]); return val[0] }
-
- hash: empty_hash
- | hash_list
- empty_hash: FACES { return n :Hash }
- hash_list: LFACE hash_items RFACE { return val[1] }
- hash_items: hash_item { return n :Hash, val[0] }
- | hash_items COMMA hash_item { val[0].append(val[2]); return val[0] }
- hash_item: expr COLON expr { return n :HashItem, val[0], val[2] }
-
- constant: CONSTANT { return constant val[0] }
-
- indented: indented_w_stmts
- | indented_w_expr
- | indented_wo_stmts
- indented_w_stmts: indent statements outdent { return val[0] }
- indented_w_expr: indent expr outdent { return val[0].append(val[1]) }
- indented_wo_stmts: indent outdent { return val[0] }
- outdent: OUTDENT { return pop_scope }
-
-
- indent_w_args: indent_pipe indent_args PIPE nlq INDENT { return val[0] }
- indent_pipe: PIPE { return push_scope }
- indent_wo_args: INDENT { return push_scope }
- indent: indent_w_args
- | indent_wo_args
-
- indent_args: indent_arg
- | indent_args COMMA indent_arg
- indent_arg: arg_var { return scope.add_argument val[0] }
- | arg_var EQUAL expr { return n :Assignment, val[0], val[2] }
- arg_var: IDENTIFIER { return n :Variable, val[0] }
-
- method_call: method_call_on_object
- | method_call_on_self
- | method_call_on_closure
- method_call_on_object: expr DOT call_signature { return n :MethodCall, val[0], val[2] }
- | expr DOT IDENTIFIER { return n :MethodCall, val[0], n(:CallSignature, val[2]) }
- method_call_on_self: call_signature { return n :MethodCall, scope_instance, val[0] }
-
- method_call_on_closure: AT call_signature { return n :MethodCall, this_closure, val[1] }
- | AT IDENTIFIER { return n :MethodCall, this_closure, n(:CallSignature, val[1]) }
-
- call_signature: call_arguments
- | call_simple_name
- call_simple_name: CALL { return n :CallSignature, val[0] }
- call_argument: SIGNATURE call_passed_arg { return n :CallSignature, val[0], [val[1]] }
- call_passed_arg: call_passed_simple
- | call_passed_indented
- call_passed_simple: expr
- | expr NL
- call_passed_indented: indented
- | indented NL
- call_arguments: call_argument { return val[0] }
- | call_arguments call_argument { return val[0].concat_signature val[1] }
-
- grouped_expr: OPAREN expr CPAREN { return n :Expression, val[1] }
-
- variable: IDENTIFIER { return allocate_local val[0] }
-
- binary_op: assignment
- | addition
- | subtraction
- | multiplication
- | division
- | exponentiation
- | modulo
- | equality
- | not_equality
- | logical_or
- | logical_and
- | greater_than
- | less_than
- | greater_or_eq
- | less_or_eq
-
- assignment: IDENTIFIER EQUAL expr { return allocate_local_assignment val[0], val[2] }
- addition: expr PLUS expr { return binary val[0], val[2], 'plus:' }
- subtraction: expr MINUS expr { return binary val[0], val[2], 'minus:' }
- multiplication: expr ASTERISK expr { return binary val[0], val[2], 'multiplyBy:' }
- division: expr FWD_SLASH expr { return binary val[0], val[2], 'divideBy:' }
- exponentiation: expr EXPO expr { return binary val[0], val[2], 'toThePowerOf:' }
- modulo: expr PERCENT expr { return binary val[0], val[2], 'moduloOf:' }
- equality: expr EQUALITY expr { return binary val[0], val[2], 'isEqualTo:' }
- not_equality: expr NOT_EQUALITY expr { return binary val[0], val[2], 'isNotEqualTo:' }
- logical_or: expr OR expr { return binary val[0], val[2], 'logicalOr:' }
- logical_and: expr AND expr { return binary val[0], val[2], 'logicalAnd:' }
- greater_than: expr GT expr { return binary val[0], val[2], 'isGreaterThan:' }
- less_than: expr LT expr { return binary val[0], val[2], 'isLessThan:' }
- greater_or_eq: expr GTE expr { return binary val[0], val[2], 'isGreaterOrEqualTo:' }
- less_or_eq: expr LTE expr { return binary val[0], val[2], 'isLessOrEqualTo:' }
-
- unary_op: unary_not
- | unary_plus
- | unary_minus
- | unary_complement
-
- unary_not: BANG expr { return unary val[1], 'unaryNot' }
- unary_plus: PLUS expr { return unary val[1], 'unaryPlus' }
- unary_minus: MINUS expr { return unary val[1], 'unaryMinus' }
- unary_complement: TILDE expr { return unary val[1], 'unaryComplement' }
-
- literal: integer
- | float
- | string
- | nil
- | true
- | false
- | self
-
- float: FLOAT { return n :Float, val[0] }
- integer: INTEGER { return n :Integer, val[0] }
- nil: NIL { return n :Nil }
- true: TRUE { return n :True }
- false: FALSE { return n :False }
- self: SELF { return n :Self }
-
- string: STRING { return n :String, val[0] }
- | interpolated_string
- | empty_string
-
- interpolated_string: DOUBLE_TICK_STRING interpolated_string_contents DOUBLE_TICK_STRING_END { return val[1] }
- interpolation: INTERPOLATE_START expr INTERPOLATE_END { return val[1] }
- interpolated_string_contents: interpolated_string_chunk { return n :InterpolatedString, val[0] }
- | interpolated_string_contents interpolated_string_chunk { val[0].append(val[1]); return val[0] }
- interpolated_string_chunk: chars { return val[0] }
- | interpolation { return to_string(val[0]) }
- empty_string: DOUBLE_TICK_STRING DOUBLE_TICK_STRING_END { return n :String, '' }
-
- chars: CHAR { return n :String, val[0] }
- | chars CHAR { val[0].append(val[1]); return val[0] }
-end
-
----- inner
-
-attr_accessor :lexer, :scopes, :state
-
-def initialize lexer
- @lexer = lexer
- @state = []
- @scopes = []
- push_scope
-end
-
-def ast
- @ast ||= do_parse
- @scopes.first
-end
-
-def on_error t, val, vstack
- line = lexer.line
- col = lexer.column
- message = "Unexpected #{token_to_str t} at #{lexer.filename} line #{line}:#{col}:\n\n"
-
- start = line - 5 > 0 ? line - 5 : 0
- i_size = line.to_s.size
- (start..(start + 5)).each do |i|
- message << sprintf("\t%#{i_size}d: %s\n", i, lexer.get_line(i))
- message << "\t#{' ' * i_size} #{'-' * (col - 1)}^\n" if i == line
- end
-
- raise SyntaxError, message
-end
-
-def next_token
- nt = lexer.next_computed_token
- # just use a state stack for now, we'll have to do something
- # more sophisticated soon.
- if nt && nt.first == :state
- if nt.last
- state.push << nt.last
- else
- state.pop
- end
- next_token
- else
- nt
- end
-end
-
-def push_scope
- new_scope = Huia::AST::Scope.new scope
- new_scope.file = lexer.filename
- new_scope.line = lexer.line
- new_scope.column = lexer.column
- scopes.push new_scope
- new_scope
-end
-
-def pop_scope
- scopes.pop
-end
-
-def scope
- scopes.last
-end
-
-def binary left, right, method
- node(:MethodCall, left, node(:CallSignature, method, [right]))
-end
-
-def unary left, method
- node(:MethodCall, left, node(:CallSignature, method))
-end
-
-def node type, *args
- Huia::AST.const_get(type).new(*args).tap do |n|
- n.file = lexer.filename
- n.line = lexer.line
- n.column = lexer.column
- end
-end
-alias n node
-
-def allocate_local name
- node(:Variable, name).tap do |n|
- scope.allocate_local n
- end
-end
-
-def allocate_local_assignment name, value
- node(:Assignment, name, value).tap do |n|
- scope.allocate_local n
- end
-end
-
-def this_closure
- allocate_local('@')
-end
-
-def scope_instance
- node(:ScopeInstance, scope)
-end
-
-def constant name
- return scope_instance if name == 'self'
- node(:Constant, name)
-end
-
-def to_string expr
- node(:MethodCall, expr, node(:CallSignature, 'toString'))
-end
diff --git a/test/racc/assets/ichk.y b/test/racc/assets/ichk.y
deleted file mode 100644
index 1d359df83e..0000000000
--- a/test/racc/assets/ichk.y
+++ /dev/null
@@ -1,102 +0,0 @@
-class Calculator
-
- prechigh
- left '*' '/'
- left '+' '-'
- preclow
-
- convert
- NUMBER 'Number'
- end
-
-rule
-
- target : exp
- | /* none */ { result = 0 }
-
- exp : exp '+' exp { result += val[2]; a = 'plus' }
- | exp '-' exp { result -= val[2]; a = "string test" }
- | exp '*' exp { result *= val[2] }
- | exp '/' exp { result /= val[2] }
- | '(' { $emb = true } exp ')'
- {
- raise 'must not happen' unless $emb
- result = val[2]
- }
- | '-' NUMBER { result = -val[1] }
- | NUMBER
-
-----header
-
-class Number
-end
-
-----inner
-
- def initialize
- @racc_debug_out = $stdout
- @yydebug = false
- end
-
- def validate(expected, src)
- result = parse(src)
- unless result == expected
- raise "test #{@test_number} fail"
- end
- @test_number += 1
- end
-
- def parse(src)
- @src = src
- @test_number = 1
- yyparse self, :scan
- end
-
- def scan(&block)
- @src.each(&block)
- end
-
-----footer
-
-calc = Calculator.new
-
-calc.validate(9, [[Number, 9], nil])
-
-calc.validate(-3,
- [[Number, 5],
- ['*', '*'],
- [Number, 1],
- ['-', '*'],
- [Number, 1],
- ['*', '*'],
- [Number, 8],
- nil])
-
-calc.validate(-1,
- [[Number, 5],
- ['+', '+'],
- [Number, 2],
- ['-', '-'],
- [Number, 5],
- ['+', '+'],
- [Number, 2],
- ['-', '-'],
- [Number, 5],
- nil])
-
-calc.validate(-4,
- [['-', 'UMINUS'],
- [Number, 4],
- nil])
-
-calc.validate(40,
- [[Number, 7],
- ['*', '*'],
- ['(', '('],
- [Number, 4],
- ['+', '+'],
- [Number, 3],
- [')', ')'],
- ['-', '-'],
- [Number, 9],
- nil])
diff --git a/test/racc/assets/ifelse.y b/test/racc/assets/ifelse.y
deleted file mode 100644
index 18dbe4b1a7..0000000000
--- a/test/racc/assets/ifelse.y
+++ /dev/null
@@ -1,14 +0,0 @@
-class C::Parser
-token tSOMETHING
-rule
- statement
- : tSOMETHING
- | 'if' statement 'then' statement
- | 'if' statement 'then' statement 'else' statement
- ;
-
- dummy
- : tSOMETHING '+' tSOMETHING
- | tSOMETHING '-' tSOMETHING
- ;
-
diff --git a/test/racc/assets/intp.y b/test/racc/assets/intp.y
deleted file mode 100644
index 39e42afd74..0000000000
--- a/test/racc/assets/intp.y
+++ /dev/null
@@ -1,546 +0,0 @@
-#
-# intp
-#
-
-class Intp::Parser
-
-prechigh
- nonassoc UMINUS
- left '*' '/'
- left '+' '-'
- nonassoc EQ
-preclow
-
-rule
-
- program : stmt_list
- {
- result = RootNode.new( val[0] )
- }
-
- stmt_list :
- {
- result = []
- }
- | stmt_list stmt EOL
- {
- result.push val[1]
- }
- | stmt_list EOL
-
- stmt : expr
- | assign
- | IDENT realprim
- {
- result = FuncallNode.new( @fname, val[0][0],
- val[0][1], [val[1]] )
- }
- | if_stmt
- | while_stmt
- | defun
-
- if_stmt : IF stmt THEN EOL stmt_list else_stmt END
- {
- result = IfNode.new( @fname, val[0][0],
- val[1], val[4], val[5] )
- }
-
- else_stmt : ELSE EOL stmt_list
- {
- result = val[2]
- }
- |
- {
- result = nil
- }
-
- while_stmt: WHILE stmt DO EOL stmt_list END
- {
- result = WhileNode.new(@fname, val[0][0],
- val[1], val[4])
- }
-
- defun : DEF IDENT param EOL stmt_list END
- {
- result = DefNode.new(@fname, val[0][0], val[1][1],
- Function.new(@fname, val[0][0], val[2], val[4]))
- }
-
- param : '(' name_list ')'
- {
- result = val[1]
- }
- | '(' ')'
- {
- result = []
- }
- |
- {
- result = []
- }
-
- name_list : IDENT
- {
- result = [ val[0][1] ]
- }
- | name_list ',' IDENT
- {
- result.push val[2][1]
- }
-
- assign : IDENT '=' expr
- {
- result = AssignNode.new(@fname, val[0][0], val[0][1], val[2])
- }
-
- expr : expr '+' expr
- {
- result = FuncallNode.new(@fname, val[0].lineno, '+', [val[0], val[2]])
- }
- | expr '-' expr
- {
- result = FuncallNode.new(@fname, val[0].lineno, '-', [val[0], val[2]])
- }
- | expr '*' expr
- {
- result = FuncallNode.new(@fname, val[0].lineno, '*', [val[0], val[2]])
- }
- | expr '/' expr
- {
- result = FuncallNode.new(@fname, val[0].lineno,
- '/', [val[0], val[2]])
- }
- | expr EQ expr
- {
- result = FuncallNode.new(@fname, val[0].lineno, '==', [val[0], val[2]])
- }
- | primary
-
- primary : realprim
- | '(' expr ')'
- {
- result = val[1]
- }
- | '-' expr =UMINUS
- {
- result = FuncallNode.new(@fname, val[0][0], '-@', [val[1]])
- }
-
- realprim : IDENT
- {
- result = VarRefNode.new(@fname, val[0][0],
- val[0][1])
- }
- | NUMBER
- {
- result = LiteralNode.new(@fname, *val[0])
- }
- | STRING
- {
- result = StringNode.new(@fname, *val[0])
- }
- | TRUE
- {
- result = LiteralNode.new(@fname, *val[0])
- }
- | FALSE
- {
- result = LiteralNode.new(@fname, *val[0])
- }
- | NIL
- {
- result = LiteralNode.new(@fname, *val[0])
- }
- | funcall
-
- funcall : IDENT '(' args ')'
- {
- result = FuncallNode.new(@fname, val[0][0], val[0][1], val[2])
- }
- | IDENT '(' ')'
- {
- result = FuncallNode.new(@fname, val[0][0], val[0][1], [])
- }
-
- args : expr
- {
- result = val
- }
- | args ',' expr
- {
- result.push val[2]
- }
-
-end
-
----- header
-#
-# intp/parser.rb
-#
-
----- inner
-
- def initialize
- @scope = {}
- end
-
- RESERVED = {
- 'if' => :IF,
- 'else' => :ELSE,
- 'while' => :WHILE,
- 'then' => :THEN,
- 'do' => :DO,
- 'def' => :DEF,
- 'true' => :TRUE,
- 'false' => :FALSE,
- 'nil' => :NIL,
- 'end' => :END
- }
-
- RESERVED_V = {
- 'true' => true,
- 'false' => false,
- 'nil' => nil
- }
-
- def parse(f, fname)
- @q = []
- @fname = fname
- lineno = 1
- f.each do |line|
- line.strip!
- until line.empty?
- case line
- when /\A\s+/, /\A\#.*/
- ;
- when /\A[a-zA-Z_]\w*/
- word = $&
- @q.push [(RESERVED[word] || :IDENT),
- [lineno, RESERVED_V.key?(word) ? RESERVED_V[word] : word.intern]]
- when /\A\d+/
- @q.push [:NUMBER, [lineno, $&.to_i]]
- when /\A"(?:[^"\\]+|\\.)*"/, /\A'(?:[^'\\]+|\\.)*'/
- @q.push [:STRING, [lineno, eval($&)]]
- when /\A==/
- @q.push [:EQ, [lineno, '==']]
- when /\A./
- @q.push [$&, [lineno, $&]]
- else
- raise RuntimeError, 'must not happen'
- end
- line = $'
- end
- @q.push [:EOL, [lineno, nil]]
- lineno += 1
- end
- @q.push [false, '$']
- do_parse
- end
-
- def next_token
- @q.shift
- end
-
- def on_error(t, v, values)
- if v
- line = v[0]
- v = v[1]
- else
- line = 'last'
- end
- raise Racc::ParseError, "#{@fname}:#{line}: syntax error on #{v.inspect}"
- end
-
----- footer
-# intp/node.rb
-
-module Intp
-
- class IntpError < StandardError; end
- class IntpArgumentError < IntpError; end
-
- class Core
-
- def initialize
- @ftab = {}
- @obj = Object.new
- @stack = []
- @stack.push Frame.new '(toplevel)'
- end
-
- def frame
- @stack[-1]
- end
-
- def define_function(fname, node)
- raise IntpError, "function #{fname} defined twice" if @ftab.key?(fname)
- @ftab[fname] = node
- end
-
- def call_function_or(fname, args)
- call_intp_function_or(fname, args) {
- call_ruby_toplevel_or(fname, args) {
- yield
- }
- }
- end
-
- def call_intp_function_or(fname, args)
- if func = @ftab[fname]
- frame = Frame.new(fname)
- @stack.push frame
- func.call self, frame, args
- @stack.pop
- else
- yield
- end
- end
-
- def call_ruby_toplevel_or(fname, args)
- if @obj.respond_to? fname, true
- @obj.send fname, *args
- else
- yield
- end
- end
-
- end
-
- class Frame
-
- def initialize(fname)
- @fname = fname
- @lvars = {}
- end
-
- attr :fname
-
- def lvar?(name)
- @lvars.key? name
- end
-
- def [](key)
- @lvars[key]
- end
-
- def []=(key, val)
- @lvars[key] = val
- end
-
- end
-
-
- class Node
-
- def initialize(fname, lineno)
- @filename = fname
- @lineno = lineno
- end
-
- attr_reader :filename
- attr_reader :lineno
-
- def exec_list(intp, nodes)
- v = nil
- nodes.each {|i| v = i.evaluate(intp) }
- v
- end
-
- def intp_error!(msg)
- raise IntpError, "in #{filename}:#{lineno}: #{msg}"
- end
-
- def inspect
- "#{self.class.name}/#{lineno}"
- end
-
- end
-
-
- class RootNode < Node
-
- def initialize(tree)
- super nil, nil
- @tree = tree
- end
-
- def evaluate
- exec_list Core.new, @tree
- end
-
- end
-
-
- class DefNode < Node
-
- def initialize(file, lineno, fname, func)
- super file, lineno
- @funcname = fname
- @funcobj = func
- end
-
- def evaluate(intp)
- intp.define_function @funcname, @funcobj
- end
-
- end
-
- class FuncallNode < Node
-
- def initialize(file, lineno, func, args)
- super file, lineno
- @funcname = func
- @args = args
- end
-
- def evaluate(intp)
- args = @args.map {|i| i.evaluate intp }
- begin
- intp.call_intp_function_or(@funcname, args) {
- if args.empty? or not args[0].respond_to?(@funcname)
- intp.call_ruby_toplevel_or(@funcname, args) {
- intp_error! "undefined function #{@funcname.id2name}"
- }
- else
- recv = args.shift
- recv.send @funcname, *args
- end
- }
- rescue IntpArgumentError, ArgumentError
- intp_error! $!.message
- end
- end
-
- end
-
- class Function < Node
-
- def initialize(file, lineno, params, body)
- super file, lineno
- @params = params
- @body = body
- end
-
- def call(intp, frame, args)
- unless args.size == @params.size
- raise IntpArgumentError,
- "wrong # of arg for #{frame.fname}() (#{args.size} for #{@params.size})"
- end
- args.each_with_index do |v,i|
- frame[@params[i]] = v
- end
- exec_list intp, @body
- end
-
- end
-
-
- class IfNode < Node
-
- def initialize(fname, lineno, cond, tstmt, fstmt)
- super fname, lineno
- @condition = cond
- @tstmt = tstmt
- @fstmt = fstmt
- end
-
- def evaluate(intp)
- if @condition.evaluate(intp)
- exec_list intp, @tstmt
- else
- exec_list intp, @fstmt if @fstmt
- end
- end
-
- end
-
- class WhileNode < Node
-
- def initialize(fname, lineno, cond, body)
- super fname, lineno
- @condition = cond
- @body = body
- end
-
- def evaluate(intp)
- while @condition.evaluate(intp)
- exec_list intp, @body
- end
- end
-
- end
-
-
- class AssignNode < Node
-
- def initialize(fname, lineno, vname, val)
- super fname, lineno
- @vname = vname
- @val = val
- end
-
- def evaluate(intp)
- intp.frame[@vname] = @val.evaluate(intp)
- end
-
- end
-
- class VarRefNode < Node
-
- def initialize(fname, lineno, vname)
- super fname, lineno
- @vname = vname
- end
-
- def evaluate(intp)
- if intp.frame.lvar?(@vname)
- intp.frame[@vname]
- else
- intp.call_function_or(@vname, []) {
- intp_error! "unknown method or local variable #{@vname.id2name}"
- }
- end
- end
-
- end
-
- class StringNode < Node
-
- def initialize(fname, lineno, str)
- super fname, lineno
- @val = str
- end
-
- def evaluate(intp)
- @val.dup
- end
-
- end
-
- class LiteralNode < Node
-
- def initialize(fname, lineno, val)
- super fname, lineno
- @val = val
- end
-
- def evaluate(intp)
- @val
- end
-
- end
-
-end # module Intp
-
-begin
- tree = nil
- fname = 'src.intp'
- File.open(fname) {|f|
- tree = Intp::Parser.new.parse(f, fname)
- }
- tree.evaluate
-rescue Racc::ParseError, Intp::IntpError, Errno::ENOENT
- raise ####
- $stderr.puts "#{File.basename $0}: #{$!}"
- exit 1
-end
diff --git a/test/racc/assets/journey.y b/test/racc/assets/journey.y
deleted file mode 100644
index c2640f3339..0000000000
--- a/test/racc/assets/journey.y
+++ /dev/null
@@ -1,47 +0,0 @@
-class Journey::Parser
-
-token SLASH LITERAL SYMBOL LPAREN RPAREN DOT STAR OR
-
-rule
- expressions
- : expressions expression { result = Cat.new(val.first, val.last) }
- | expression { result = val.first }
- | or
- ;
- expression
- : terminal
- | group
- | star
- ;
- group
- : LPAREN expressions RPAREN { result = Group.new(val[1]) }
- ;
- or
- : expressions OR expression { result = Or.new([val.first, val.last]) }
- ;
- star
- : STAR { result = Star.new(Symbol.new(val.last)) }
- ;
- terminal
- : symbol
- | literal
- | slash
- | dot
- ;
- slash
- : SLASH { result = Slash.new('/') }
- ;
- symbol
- : SYMBOL { result = Symbol.new(val.first) }
- ;
- literal
- : LITERAL { result = Literal.new(val.first) }
- dot
- : DOT { result = Dot.new(val.first) }
- ;
-
-end
-
----- header
-
-require 'journey/parser_extras'
diff --git a/test/racc/assets/liquor.y b/test/racc/assets/liquor.y
deleted file mode 100644
index edf441118c..0000000000
--- a/test/racc/assets/liquor.y
+++ /dev/null
@@ -1,313 +0,0 @@
-# Copyright (c) 2012-2013 Peter Zotov <whitequark@whitequark.org>
-# 2012 Yaroslav Markin <yaroslav@markin.net>
-# 2012 Nate Gadgibalaev <nat@xnsv.ru>
-#
-# MIT License
-#
-# 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.
-
-class Liquor::Parser
- token comma dot endtag ident integer keyword lblock lblock2 lbracket
- linterp lparen op_div op_eq op_gt op_geq op_lt op_leq op_minus
- op_mod op_mul op_neq op_not op_plus pipe plaintext rblock
- rbracket rinterp rparen string tag_ident
-
- prechigh
- left dot
- nonassoc op_uminus op_not
- left op_mul op_div op_mod
- left op_plus op_minus
- left op_eq op_neq op_lt op_leq op_gt op_geq
- left op_and
- left op_or
- preclow
-
- expect 15
-
- start block
-
-rule
- block: /* empty */
- { result = [] }
- | plaintext block
- { result = [ val[0], *val[1] ] }
- | interp block
- { result = [ val[0], *val[1] ] }
- | tag block
- { result = [ val[0], *val[1] ] }
-
- interp:
- linterp expr rinterp
- { result = [ :interp, retag(val), val[1] ] }
- | linterp filter_chain rinterp
- { result = [ :interp, retag(val), val[1] ] }
-
- primary_expr:
- ident
- | lparen expr rparen
- { result = [ val[1][0], retag(val), *val[1][2..-1] ] }
-
- expr:
- integer
- | string
- | tuple
- | ident function_args
- { result = [ :call, retag(val), val[0], val[1] ] }
- | expr lbracket expr rbracket
- { result = [ :index, retag(val), val[0], val[2] ] }
- | expr dot ident function_args
- { result = [ :external, retag(val), val[0], val[2], val[3] ] }
- | expr dot ident
- { result = [ :external, retag(val), val[0], val[2], nil ] }
- | op_minus expr =op_uminus
- { result = [ :uminus, retag(val), val[1] ] }
- | op_not expr
- { result = [ :not, retag(val), val[1] ] }
- | expr op_mul expr
- { result = [ :mul, retag(val), val[0], val[2] ] }
- | expr op_div expr
- { result = [ :div, retag(val), val[0], val[2] ] }
- | expr op_mod expr
- { result = [ :mod, retag(val), val[0], val[2] ] }
- | expr op_plus expr
- { result = [ :plus, retag(val), val[0], val[2] ] }
- | expr op_minus expr
- { result = [ :minus, retag(val), val[0], val[2] ] }
- | expr op_eq expr
- { result = [ :eq, retag(val), val[0], val[2] ] }
- | expr op_neq expr
- { result = [ :neq, retag(val), val[0], val[2] ] }
- | expr op_lt expr
- { result = [ :lt, retag(val), val[0], val[2] ] }
- | expr op_leq expr
- { result = [ :leq, retag(val), val[0], val[2] ] }
- | expr op_gt expr
- { result = [ :gt, retag(val), val[0], val[2] ] }
- | expr op_geq expr
- { result = [ :geq, retag(val), val[0], val[2] ] }
- | expr op_and expr
- { result = [ :and, retag(val), val[0], val[2] ] }
- | expr op_or expr
- { result = [ :or, retag(val), val[0], val[2] ] }
- | primary_expr
-
- tuple:
- lbracket tuple_content rbracket
- { result = [ :tuple, retag(val), val[1].compact ] }
-
- tuple_content:
- expr comma tuple_content
- { result = [ val[0], *val[2] ] }
- | expr
- { result = [ val[0] ] }
- | /* empty */
- { result = [ ] }
-
- function_args:
- lparen function_args_inside rparen
- { result = [ :args, retag(val), *val[1] ] }
-
- function_args_inside:
- expr function_keywords
- { result = [ val[0], val[1][2] ] }
- | function_keywords
- { result = [ nil, val[0][2] ] }
-
- function_keywords:
- keyword expr function_keywords
- { name = val[0][2].to_sym
- tail = val[2][2]
- loc = retag([ val[0], val[1] ])
-
- if tail.include? name
- @errors << SyntaxError.new("duplicate keyword argument `#{val[0][2]}'",
- tail[name][1])
- end
-
- hash = {
- name => [ val[1][0], loc, *val[1][2..-1] ]
- }.merge(tail)
-
- result = [ :keywords, retag([ loc, val[2] ]), hash ]
- }
- | /* empty */
- { result = [ :keywords, nil, {} ] }
-
- filter_chain:
- expr pipe filter_chain_cont
- { result = [ val[0], *val[2] ].
- reduce { |tree, node| node[3][2] = tree; node }
- }
-
- filter_chain_cont:
- filter_call pipe filter_chain_cont
- { result = [ val[0], *val[2] ] }
- | filter_call
- { result = [ val[0] ] }
-
- filter_call:
- ident function_keywords
- { ident_loc = val[0][1]
- empty_args_loc = { line: ident_loc[:line],
- start: ident_loc[:end] + 1,
- end: ident_loc[:end] + 1, }
- result = [ :call, val[0][1], val[0],
- [ :args, val[1][1] || empty_args_loc, nil, val[1][2] ] ]
- }
-
- tag:
- lblock ident expr tag_first_cont
- { result = [ :tag, retag(val), val[1], val[2], *reduce_tag_args(val[3][2]) ] }
- | lblock ident tag_first_cont
- { result = [ :tag, retag(val), val[1], nil, *reduce_tag_args(val[2][2]) ] }
-
- # Racc cannot do lookahead across rules. I had to add states
- # explicitly to avoid S/R conflicts. You are not expected to
- # understand this.
-
- tag_first_cont:
- rblock
- { result = [ :cont, retag(val), [] ] }
- | keyword tag_first_cont2
- { result = [ :cont, retag(val), [ val[0], *val[1][2] ] ] }
-
- tag_first_cont2:
- rblock block lblock2 tag_next_cont
- { result = [ :cont2, val[0][1], [ [:block, val[0][1], val[1] ], *val[3] ] ] }
- | expr tag_first_cont
- { result = [ :cont2, retag(val), [ val[0], *val[1][2] ] ] }
-
- tag_next_cont:
- endtag rblock
- { result = [] }
- | keyword tag_next_cont2
- { result = [ val[0], *val[1] ] }
-
- tag_next_cont2:
- rblock block lblock2 tag_next_cont
- { result = [ [:block, val[0][1], val[1] ], *val[3] ] }
- | expr keyword tag_next_cont3
- { result = [ val[0], val[1], *val[2] ] }
-
- tag_next_cont3:
- rblock block lblock2 tag_next_cont
- { result = [ [:block, val[0][1], val[1] ], *val[3] ] }
- | expr tag_next_cont
- { result = [ val[0], *val[1] ] }
-
----- inner
- attr_reader :errors, :ast
-
- def initialize(tags={})
- super()
-
- @errors = []
- @ast = nil
- @tags = tags
- end
-
- def success?
- @errors.empty?
- end
-
- def parse(string, name='(code)')
- @errors.clear
- @name = name
- @ast = nil
-
- begin
- @stream = Lexer.lex(string, @name, @tags)
- @ast = do_parse
- rescue Liquor::SyntaxError => e
- @errors << e
- end
-
- success?
- end
-
- def next_token
- tok = @stream.shift
- [ tok[0], tok ] if tok
- end
-
- TOKEN_NAME_MAP = {
- :comma => ',',
- :dot => '.',
- :lblock => '{%',
- :rblock => '%}',
- :linterp => '{{',
- :rinterp => '}}',
- :lbracket => '[',
- :rbracket => ']',
- :lparen => '(',
- :rparen => ')',
- :pipe => '|',
- :op_not => '!',
- :op_mul => '*',
- :op_div => '/',
- :op_mod => '%',
- :op_plus => '+',
- :op_minus => '-',
- :op_eq => '==',
- :op_neq => '!=',
- :op_lt => '<',
- :op_leq => '<=',
- :op_gt => '>',
- :op_geq => '>=',
- :keyword => 'keyword argument name',
- :kwarg => 'keyword argument',
- :ident => 'identifier',
- }
-
- def on_error(error_token_id, error_token, value_stack)
- if token_to_str(error_token_id) == "$end"
- raise Liquor::SyntaxError.new("unexpected end of program", {
- file: @name
- })
- else
- type, (loc, value) = error_token
- type = TOKEN_NAME_MAP[type] || type
-
- raise Liquor::SyntaxError.new("unexpected token `#{type}'", loc)
- end
- end
-
- def retag(nodes)
- loc = nodes.map { |node| node[1] }.compact
- first, *, last = loc
- return first if last.nil?
-
- {
- file: first[:file],
- line: first[:line],
- start: first[:start],
- end: last[:end],
- }
- end
-
- def reduce_tag_args(list)
- list.each_slice(2).reduce([]) { |args, (k, v)|
- if v[0] == :block
- args << [ :blockarg, retag([ k, v ]), k, v[2] || [] ]
- else
- args << [ :kwarg, retag([ k, v ]), k, v ]
- end
- }
- end
diff --git a/test/racc/assets/machete.y b/test/racc/assets/machete.y
deleted file mode 100644
index ea92d47a69..0000000000
--- a/test/racc/assets/machete.y
+++ /dev/null
@@ -1,423 +0,0 @@
-# Copyright (c) 2011 SUSE
-#
-# 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.
-
-class Machete::Parser
-
-token NIL
-token TRUE
-token FALSE
-token INTEGER
-token SYMBOL
-token STRING
-token REGEXP
-token ANY
-token EVEN
-token ODD
-token METHOD_NAME
-token CLASS_NAME
-
-start expression
-
-rule
-
-expression : primary
- | expression "|" primary {
- result = if val[0].is_a?(ChoiceMatcher)
- ChoiceMatcher.new(val[0].alternatives << val[2])
- else
- ChoiceMatcher.new([val[0], val[2]])
- end
- }
-
-primary : node
- | array
- | literal
- | any
-
-node : CLASS_NAME {
- result = NodeMatcher.new(val[0].to_sym)
- }
- | CLASS_NAME "<" attrs ">" {
- result = NodeMatcher.new(val[0].to_sym, val[2])
- }
-
-attrs : attr
- | attrs "," attr { result = val[0].merge(val[2]) }
-
-attr : method_name "=" expression { result = { val[0].to_sym => val[2] } }
- | method_name "^=" SYMBOL {
- result = {
- val[0].to_sym => SymbolRegexpMatcher.new(
- Regexp.new("^" + Regexp.escape(symbol_value(val[2]).to_s))
- )
- }
- }
- | method_name "$=" SYMBOL {
- result = {
- val[0].to_sym => SymbolRegexpMatcher.new(
- Regexp.new(Regexp.escape(symbol_value(val[2]).to_s) + "$")
- )
- }
- }
- | method_name "*=" SYMBOL {
- result = {
- val[0].to_sym => SymbolRegexpMatcher.new(
- Regexp.new(Regexp.escape(symbol_value(val[2]).to_s))
- )
- }
- }
- | method_name "^=" STRING {
- result = {
- val[0].to_sym => StringRegexpMatcher.new(
- Regexp.new("^" + Regexp.escape(string_value(val[2])))
- )
- }
- }
- | method_name "$=" STRING {
- result = {
- val[0].to_sym => StringRegexpMatcher.new(
- Regexp.new(Regexp.escape(string_value(val[2])) + "$")
- )
- }
- }
- | method_name "*=" STRING {
- result = {
- val[0].to_sym => StringRegexpMatcher.new(
- Regexp.new(Regexp.escape(string_value(val[2])))
- )
- }
- }
- | method_name "*=" REGEXP {
- result = {
- val[0].to_sym => IndifferentRegexpMatcher.new(
- Regexp.new(regexp_value(val[2]))
- )
- }
- }
-
-# Hack to overcome the fact that some tokens will lex as simple tokens, not
-# METHOD_NAME tokens, and that "reserved words" will lex as separate kinds of
-# tokens.
-method_name : METHOD_NAME
- | NIL
- | TRUE
- | FALSE
- | ANY
- | EVEN
- | ODD
- | "*"
- | "+"
- | "<"
- | ">"
- | "^"
- | "|"
-
-array : "[" items_opt "]" { result = ArrayMatcher.new(val[1]) }
-
-items_opt : /* empty */ { result = [] }
- | items
-
-items : item { result = [val[0]] }
- | items "," item { result = val[0] << val[2] }
-
-item : expression
- | expression quantifier { result = Quantifier.new(val[0], *val[1]) }
-
-quantifier : "*" { result = [0, nil, 1] }
- | "+" { result = [1, nil, 1] }
- | "?" { result = [0, 1, 1] }
- | "{" INTEGER "}" {
- result = [integer_value(val[1]), integer_value(val[1]), 1]
- }
- | "{" INTEGER "," "}" {
- result = [integer_value(val[1]), nil, 1]
- }
- | "{" "," INTEGER "}" {
- result = [0, integer_value(val[2]), 1]
- }
- | "{" INTEGER "," INTEGER "}" {
- result = [integer_value(val[1]), integer_value(val[3]), 1]
- }
- | "{" EVEN "}" { result = [0, nil, 2] }
- | "{" ODD "}" { result = [1, nil, 2] }
-
-literal : NIL { result = LiteralMatcher.new(nil) }
- | TRUE { result = LiteralMatcher.new(true) }
- | FALSE { result = LiteralMatcher.new(false) }
- | INTEGER { result = LiteralMatcher.new(integer_value(val[0])) }
- | SYMBOL { result = LiteralMatcher.new(symbol_value(val[0])) }
- | STRING { result = LiteralMatcher.new(string_value(val[0])) }
- | REGEXP { result = LiteralMatcher.new(regexp_value(val[0])) }
-
-any : ANY { result = AnyMatcher.new }
-
----- inner
-
-include Matchers
-
-class SyntaxError < StandardError; end
-
-def parse(input)
- @input = input
- @pos = 0
-
- do_parse
-end
-
-private
-
-def integer_value(value)
- if value =~ /^0[bB]/
- value[2..-1].to_i(2)
- elsif value =~ /^0[oO]/
- value[2..-1].to_i(8)
- elsif value =~ /^0[dD]/
- value[2..-1].to_i(10)
- elsif value =~ /^0[xX]/
- value[2..-1].to_i(16)
- elsif value =~ /^0/
- value.to_i(8)
- else
- value.to_i
- end
-end
-
-def symbol_value(value)
- value[1..-1].to_sym
-end
-
-def string_value(value)
- quote = value[0..0]
- if quote == "'"
- value[1..-2].gsub("\\\\", "\\").gsub("\\'", "'")
- elsif quote == '"'
- value[1..-2].
- gsub("\\\\", "\\").
- gsub('\\"', '"').
- gsub("\\n", "\n").
- gsub("\\t", "\t").
- gsub("\\r", "\r").
- gsub("\\f", "\f").
- gsub("\\v", "\v").
- gsub("\\a", "\a").
- gsub("\\e", "\e").
- gsub("\\b", "\b").
- gsub("\\s", "\s").
- gsub(/\\([0-7]{1,3})/) { $1.to_i(8).chr }.
- gsub(/\\x([0-9a-fA-F]{1,2})/) { $1.to_i(16).chr }
- else
- raise "Unknown quote: #{quote.inspect}."
- end
-end
-
-REGEXP_OPTIONS = {
- 'i' => Regexp::IGNORECASE,
- 'm' => Regexp::MULTILINE,
- 'x' => Regexp::EXTENDED
-}
-
-def regexp_value(value)
- /\A\/(.*)\/([imx]*)\z/ =~ value
- pattern, options = $1, $2
-
- Regexp.new(pattern, options.chars.map { |ch| REGEXP_OPTIONS[ch] }.inject(:|))
-end
-
-# "^" needs to be here because if it were among operators recognized by
-# METHOD_NAME, "^=" would be recognized as two tokens.
-SIMPLE_TOKENS = [
- "|",
- "<",
- ">",
- ",",
- "=",
- "^=",
- "^",
- "$=",
- "[",
- "]",
- "*=",
- "*",
- "+",
- "?",
- "{",
- "}"
-]
-
-COMPLEX_TOKENS = [
- [:NIL, /^nil/],
- [:TRUE, /^true/],
- [:FALSE, /^false/],
- # INTEGER needs to be before METHOD_NAME, otherwise e.g. "+1" would be
- # recognized as two tokens.
- [
- :INTEGER,
- /^
- [+-]? # sign
- (
- 0[bB][01]+(_[01]+)* # binary (prefixed)
- |
- 0[oO][0-7]+(_[0-7]+)* # octal (prefixed)
- |
- 0[dD]\d+(_\d+)* # decimal (prefixed)
- |
- 0[xX][0-9a-fA-F]+(_[0-9a-fA-F]+)* # hexadecimal (prefixed)
- |
- 0[0-7]*(_[0-7]+)* # octal (unprefixed)
- |
- [1-9]\d*(_\d+)* # decimal (unprefixed)
- )
- /x
- ],
- [
- :SYMBOL,
- /^
- :
- (
- # class name
- [A-Z][a-zA-Z0-9_]*
- |
- # regular method name
- [a-z_][a-zA-Z0-9_]*[?!=]?
- |
- # instance variable name
- @[a-zA-Z_][a-zA-Z0-9_]*
- |
- # class variable name
- @@[a-zA-Z_][a-zA-Z0-9_]*
- |
- # operator (sorted by length, then alphabetically)
- (<=>|===|\[\]=|\*\*|\+@|-@|<<|<=|==|=~|>=|>>|\[\]|[%&*+\-\/<>^`|~])
- )
- /x
- ],
- [
- :STRING,
- /^
- (
- ' # sinqle-quoted string
- (
- \\[\\'] # escape
- |
- [^'] # regular character
- )*
- '
- |
- " # double-quoted string
- (
- \\ # escape
- (
- [\\"ntrfvaebs] # one-character escape
- |
- [0-7]{1,3} # octal number escape
- |
- x[0-9a-fA-F]{1,2} # hexadecimal number escape
- )
- |
- [^"] # regular character
- )*
- "
- )
- /x
- ],
- [
- :REGEXP,
- /^
- \/
- (
- \\ # escape
- (
- [\\\/ntrfvaebs\(\)\[\]\{\}\-\.\?\*\+\|\^\$] # one-character escape
- |
- [0-7]{2,3} # octal number escape
- |
- x[0-9a-fA-F]{1,2} # hexadecimal number escape
- )
- |
- [^\/] # regular character
- )*
- \/
- [imx]*
- /x
- ],
- # ANY, EVEN and ODD need to be before METHOD_NAME, otherwise they would be
- # recognized as method names.
- [:ANY, /^any/],
- [:EVEN, /^even/],
- [:ODD, /^odd/],
- # We exclude "*", "+", "<", ">", "^" and "|" from method names since they are
- # lexed as simple tokens. This is because they have also other meanings in
- # Machette patterns beside Ruby method names.
- [
- :METHOD_NAME,
- /^
- (
- # regular name
- [a-z_][a-zA-Z0-9_]*[?!=]?
- |
- # operator (sorted by length, then alphabetically)
- (<=>|===|\[\]=|\*\*|\+@|-@|<<|<=|==|=~|>=|>>|\[\]|[%&\-\/`~])
- )
- /x
- ],
- [:CLASS_NAME, /^[A-Z][a-zA-Z0-9_]*/]
-]
-
-def next_token
- skip_whitespace
-
- return false if remaining_input.empty?
-
- # Complex tokens need to be before simple tokens, otherwise e.g. "<<" would be
- # recognized as two tokens.
-
- COMPLEX_TOKENS.each do |type, regexp|
- if remaining_input =~ regexp
- @pos += $&.length
- return [type, $&]
- end
- end
-
- SIMPLE_TOKENS.each do |token|
- if remaining_input[0...token.length] == token
- @pos += token.length
- return [token, token]
- end
- end
-
- raise SyntaxError, "Unexpected character: #{remaining_input[0..0].inspect}."
-end
-
-def skip_whitespace
- if remaining_input =~ /\A^[ \t\r\n]+/
- @pos += $&.length
- end
-end
-
-def remaining_input
- @input[@pos..-1]
-end
-
-def on_error(error_token_id, error_value, value_stack)
- raise SyntaxError, "Unexpected token: #{error_value.inspect}."
-end
diff --git a/test/racc/assets/macruby.y b/test/racc/assets/macruby.y
deleted file mode 100644
index 5ede008308..0000000000
--- a/test/racc/assets/macruby.y
+++ /dev/null
@@ -1,2197 +0,0 @@
-# Copyright (c) 2013 Peter Zotov <whitequark@whitequark.org>
-#
-# Parts of the source are derived from ruby_parser:
-# Copyright (c) Ryan Davis, seattle.rb
-#
-# MIT License
-#
-# 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.
-
-class Parser::MacRuby
-
-token kCLASS kMODULE kDEF kUNDEF kBEGIN kRESCUE kENSURE kEND kIF kUNLESS
- kTHEN kELSIF kELSE kCASE kWHEN kWHILE kUNTIL kFOR kBREAK kNEXT
- kREDO kRETRY kIN kDO kDO_COND kDO_BLOCK kDO_LAMBDA kRETURN kYIELD kSUPER
- kSELF kNIL kTRUE kFALSE kAND kOR kNOT kIF_MOD kUNLESS_MOD kWHILE_MOD
- kUNTIL_MOD kRESCUE_MOD kALIAS kDEFINED klBEGIN klEND k__LINE__
- k__FILE__ k__ENCODING__ tIDENTIFIER tFID tGVAR tIVAR tCONSTANT
- tLABEL tCVAR tNTH_REF tBACK_REF tSTRING_CONTENT tINTEGER tFLOAT
- tREGEXP_END tUPLUS tUMINUS tUMINUS_NUM tPOW tCMP tEQ tEQQ tNEQ
- tGEQ tLEQ tANDOP tOROP tMATCH tNMATCH tDOT tDOT2 tDOT3 tAREF
- tASET tLSHFT tRSHFT tCOLON2 tCOLON3 tOP_ASGN tASSOC tLPAREN
- tLPAREN2 tRPAREN tLPAREN_ARG tLBRACK tLBRACK2 tRBRACK tLBRACE
- tLBRACE_ARG tSTAR tSTAR2 tAMPER tAMPER2 tTILDE tPERCENT tDIVIDE
- tPLUS tMINUS tLT tGT tPIPE tBANG tCARET tLCURLY tRCURLY
- tBACK_REF2 tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG tREGEXP_OPT
- tWORDS_BEG tQWORDS_BEG tSTRING_DBEG tSTRING_DVAR tSTRING_END
- tSTRING tSYMBOL tNL tEH tCOLON tCOMMA tSPACE tSEMI tLAMBDA tLAMBEG
- tCHARACTER
-
-prechigh
- right tBANG tTILDE tUPLUS
- right tPOW
- right tUMINUS_NUM tUMINUS
- left tSTAR2 tDIVIDE tPERCENT
- left tPLUS tMINUS
- left tLSHFT tRSHFT
- left tAMPER2
- left tPIPE tCARET
- left tGT tGEQ tLT tLEQ
- nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH
- left tANDOP
- left tOROP
- nonassoc tDOT2 tDOT3
- right tEH tCOLON
- left kRESCUE_MOD
- right tEQL tOP_ASGN
- nonassoc kDEFINED
- right kNOT
- left kOR kAND
- nonassoc kIF_MOD kUNLESS_MOD kWHILE_MOD kUNTIL_MOD
- nonassoc tLBRACE_ARG
- nonassoc tLOWEST
-preclow
-
-rule
-
- program: top_compstmt
-
- top_compstmt: top_stmts opt_terms
- {
- result = @builder.compstmt(val[0])
- }
-
- top_stmts: # nothing
- {
- result = []
- }
- | top_stmt
- {
- result = [ val[0] ]
- }
- | top_stmts terms top_stmt
- {
- result = val[0] << val[2]
- }
- | error top_stmt
- {
- result = [ val[1] ]
- }
-
- top_stmt: stmt
- | klBEGIN tLCURLY top_compstmt tRCURLY
- {
- result = @builder.preexe(val[0], val[1], val[2], val[3])
- }
-
- bodystmt: compstmt opt_rescue opt_else opt_ensure
- {
- rescue_bodies = val[1]
- else_t, else_ = val[2]
- ensure_t, ensure_ = val[3]
-
- if rescue_bodies.empty? && !else_.nil?
- diagnostic :warning, :useless_else, nil, else_t
- end
-
- result = @builder.begin_body(val[0],
- rescue_bodies,
- else_t, else_,
- ensure_t, ensure_)
- }
-
- compstmt: stmts opt_terms
- {
- result = @builder.compstmt(val[0])
- }
-
- stmts: # nothing
- {
- result = []
- }
- | stmt
- {
- result = [ val[0] ]
- }
- | stmts terms stmt
- {
- result = val[0] << val[2]
- }
- | error stmt
- {
- result = [ val[1] ]
- }
-
- stmt: kALIAS fitem
- {
- @lexer.state = :expr_fname
- }
- fitem
- {
- result = @builder.alias(val[0], val[1], val[3])
- }
- | kALIAS tGVAR tGVAR
- {
- result = @builder.alias(val[0],
- @builder.gvar(val[1]),
- @builder.gvar(val[2]))
- }
- | kALIAS tGVAR tBACK_REF
- {
- result = @builder.alias(val[0],
- @builder.gvar(val[1]),
- @builder.back_ref(val[2]))
- }
- | kALIAS tGVAR tNTH_REF
- {
- diagnostic :error, :nth_ref_alias, nil, val[2]
- }
- | kUNDEF undef_list
- {
- result = @builder.undef_method(val[0], val[1])
- }
- | stmt kIF_MOD expr_value
- {
- result = @builder.condition_mod(val[0], nil,
- val[1], val[2])
- }
- | stmt kUNLESS_MOD expr_value
- {
- result = @builder.condition_mod(nil, val[0],
- val[1], val[2])
- }
- | stmt kWHILE_MOD expr_value
- {
- result = @builder.loop_mod(:while, val[0], val[1], val[2])
- }
- | stmt kUNTIL_MOD expr_value
- {
- result = @builder.loop_mod(:until, val[0], val[1], val[2])
- }
- | stmt kRESCUE_MOD stmt
- {
- rescue_body = @builder.rescue_body(val[1],
- nil, nil, nil,
- nil, val[2])
-
- result = @builder.begin_body(val[0], [ rescue_body ])
- }
- | klEND tLCURLY compstmt tRCURLY
- {
- result = @builder.postexe(val[0], val[1], val[2], val[3])
- }
- | lhs tEQL command_call
- {
- result = @builder.assign(val[0], val[1], val[2])
- }
- | mlhs tEQL command_call
- {
- result = @builder.multi_assign(val[0], val[1], val[2])
- }
- | var_lhs tOP_ASGN command_call
- {
- result = @builder.op_assign(val[0], val[1], val[2])
- }
- | primary_value tLBRACK2 opt_call_args rbracket tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.index(
- val[0], val[1], val[2], val[3]),
- val[4], val[5])
- }
- | primary_value tDOT tIDENTIFIER tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tDOT tCONSTANT tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | backref tOP_ASGN command_call
- {
- @builder.op_assign(val[0], val[1], val[2])
- }
- | lhs tEQL mrhs
- {
- result = @builder.assign(val[0], val[1],
- @builder.array(nil, val[2], nil))
- }
- | mlhs tEQL arg_value
- {
- result = @builder.multi_assign(val[0], val[1], val[2])
- }
- | mlhs tEQL mrhs
- {
- result = @builder.multi_assign(val[0], val[1],
- @builder.array(nil, val[2], nil))
- }
- | expr
-
- expr: command_call
- | expr kAND expr
- {
- result = @builder.logical_op(:and, val[0], val[1], val[2])
- }
- | expr kOR expr
- {
- result = @builder.logical_op(:or, val[0], val[1], val[2])
- }
- | kNOT opt_nl expr
- {
- result = @builder.not_op(val[0], nil, val[2], nil)
- }
- | tBANG command_call
- {
- result = @builder.not_op(val[0], nil, val[1], nil)
- }
- | arg
-
- expr_value: expr
-
- command_call: command
- | block_command
-
- block_command: block_call
- | block_call tDOT operation2 command_args
- {
- result = @builder.call_method(val[0], val[1], val[2],
- *val[3])
- }
- | block_call tCOLON2 operation2 command_args
- {
- result = @builder.call_method(val[0], val[1], val[2],
- *val[3])
- }
-
- cmd_brace_block: tLBRACE_ARG
- {
- @static_env.extend_dynamic
- }
- opt_block_param compstmt tRCURLY
- {
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
- }
-
- command: operation command_args =tLOWEST
- {
- result = @builder.call_method(nil, nil, val[0],
- *val[1])
- }
- | operation command_args cmd_brace_block
- {
- method_call = @builder.call_method(nil, nil, val[0],
- *val[1])
-
- begin_t, args, body, end_t = val[2]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
- | primary_value tDOT operation2 command_args =tLOWEST
- {
- result = @builder.call_method(val[0], val[1], val[2],
- *val[3])
- }
- | primary_value tDOT operation2 command_args cmd_brace_block
- {
- method_call = @builder.call_method(val[0], val[1], val[2],
- *val[3])
-
- begin_t, args, body, end_t = val[4]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
- | primary_value tCOLON2 operation2 command_args =tLOWEST
- {
- result = @builder.call_method(val[0], val[1], val[2],
- *val[3])
- }
- | primary_value tCOLON2 operation2 command_args cmd_brace_block
- {
- method_call = @builder.call_method(val[0], val[1], val[2],
- *val[3])
-
- begin_t, args, body, end_t = val[4]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
- | kSUPER command_args
- {
- result = @builder.keyword_cmd(:super, val[0],
- *val[1])
- }
- | kYIELD command_args
- {
- result = @builder.keyword_cmd(:yield, val[0],
- *val[1])
- }
- | kRETURN call_args
- {
- result = @builder.keyword_cmd(:return, val[0],
- nil, val[1], nil)
- }
- | kBREAK call_args
- {
- result = @builder.keyword_cmd(:break, val[0],
- nil, val[1], nil)
- }
- | kNEXT call_args
- {
- result = @builder.keyword_cmd(:next, val[0],
- nil, val[1], nil)
- }
-
- mlhs: mlhs_basic
- {
- result = @builder.multi_lhs(nil, val[0], nil)
- }
- | tLPAREN mlhs_inner rparen
- {
- result = @builder.begin(val[0], val[1], val[2])
- }
-
- mlhs_inner: mlhs_basic
- {
- result = @builder.multi_lhs(nil, val[0], nil)
- }
- | tLPAREN mlhs_inner rparen
- {
- result = @builder.multi_lhs(val[0], val[1], val[2])
- }
-
- mlhs_basic: mlhs_head
- | mlhs_head mlhs_item
- {
- result = val[0].
- push(val[1])
- }
- | mlhs_head tSTAR mlhs_node
- {
- result = val[0].
- push(@builder.splat(val[1], val[2]))
- }
- | mlhs_head tSTAR mlhs_node tCOMMA mlhs_post
- {
- result = val[0].
- push(@builder.splat(val[1], val[2])).
- concat(val[4])
- }
- | mlhs_head tSTAR
- {
- result = val[0].
- push(@builder.splat(val[1]))
- }
- | mlhs_head tSTAR tCOMMA mlhs_post
- {
- result = val[0].
- push(@builder.splat(val[1])).
- concat(val[3])
- }
- | tSTAR mlhs_node
- {
- result = [ @builder.splat(val[0], val[1]) ]
- }
- | tSTAR mlhs_node tCOMMA mlhs_post
- {
- result = [ @builder.splat(val[0], val[1]),
- *val[3] ]
- }
- | tSTAR
- {
- result = [ @builder.splat(val[0]) ]
- }
- | tSTAR tCOMMA mlhs_post
- {
- result = [ @builder.splat(val[0]),
- *val[2] ]
- }
-
- mlhs_item: mlhs_node
- | tLPAREN mlhs_inner rparen
- {
- result = @builder.begin(val[0], val[1], val[2])
- }
-
- mlhs_head: mlhs_item tCOMMA
- {
- result = [ val[0] ]
- }
- | mlhs_head mlhs_item tCOMMA
- {
- result = val[0] << val[1]
- }
-
- mlhs_post: mlhs_item
- {
- result = [ val[0] ]
- }
- | mlhs_post tCOMMA mlhs_item
- {
- result = val[0] << val[2]
- }
-
- mlhs_node: variable
- {
- result = @builder.assignable(val[0])
- }
- | primary_value tLBRACK2 opt_call_args rbracket
- {
- result = @builder.index_asgn(val[0], val[1], val[2], val[3])
- }
- | primary_value tDOT tIDENTIFIER
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tIDENTIFIER
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tDOT tCONSTANT
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tCONSTANT
- {
- result = @builder.assignable(
- @builder.const_fetch(val[0], val[1], val[2]))
- }
- | tCOLON3 tCONSTANT
- {
- result = @builder.assignable(
- @builder.const_global(val[0], val[1]))
- }
- | backref
- {
- result = @builder.assignable(val[0])
- }
-
- lhs: variable
- {
- result = @builder.assignable(val[0])
- }
- | primary_value tLBRACK2 opt_call_args rbracket
- {
- result = @builder.index_asgn(val[0], val[1], val[2], val[3])
- }
- | primary_value tDOT tIDENTIFIER
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tIDENTIFIER
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tDOT tCONSTANT
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tCONSTANT
- {
- result = @builder.assignable(
- @builder.const_fetch(val[0], val[1], val[2]))
- }
- | tCOLON3 tCONSTANT
- {
- result = @builder.assignable(
- @builder.const_global(val[0], val[1]))
- }
- | backref
- {
- result = @builder.assignable(val[0])
- }
-
- cname: tIDENTIFIER
- {
- diagnostic :error, :module_name_const, nil, val[0]
- }
- | tCONSTANT
-
- cpath: tCOLON3 cname
- {
- result = @builder.const_global(val[0], val[1])
- }
- | cname
- {
- result = @builder.const(val[0])
- }
- | primary_value tCOLON2 cname
- {
- result = @builder.const_fetch(val[0], val[1], val[2])
- }
-
- fname: tIDENTIFIER | tCONSTANT | tFID
- | op
- | reswords
-
- fsym: fname
- {
- result = @builder.symbol(val[0])
- }
- | symbol
-
- fitem: fsym
- | dsym
-
- undef_list: fitem
- {
- result = [ val[0] ]
- }
- | undef_list tCOMMA
- {
- @lexer.state = :expr_fname
- }
- fitem
- {
- result = val[0] << val[3]
- }
-
- op: tPIPE | tCARET | tAMPER2 | tCMP | tEQ | tEQQ
- | tMATCH | tNMATCH | tGT | tGEQ | tLT | tLEQ
- | tNEQ | tLSHFT | tRSHFT | tPLUS | tMINUS | tSTAR2
- | tSTAR | tDIVIDE | tPERCENT | tPOW | tBANG | tTILDE
- | tUPLUS | tUMINUS | tAREF | tASET | tBACK_REF2
-
- reswords: k__LINE__ | k__FILE__ | k__ENCODING__ | klBEGIN | klEND
- | kALIAS | kAND | kBEGIN | kBREAK | kCASE
- | kCLASS | kDEF | kDEFINED | kDO | kELSE
- | kELSIF | kEND | kENSURE | kFALSE | kFOR
- | kIN | kMODULE | kNEXT | kNIL | kNOT
- | kOR | kREDO | kRESCUE | kRETRY | kRETURN
- | kSELF | kSUPER | kTHEN | kTRUE | kUNDEF
- | kWHEN | kYIELD | kIF | kUNLESS | kWHILE
- | kUNTIL
-
- arg: lhs tEQL arg
- {
- result = @builder.assign(val[0], val[1], val[2])
- }
- | lhs tEQL arg kRESCUE_MOD arg
- {
- rescue_body = @builder.rescue_body(val[3],
- nil, nil, nil,
- nil, val[4])
-
- rescue_ = @builder.begin_body(val[2], [ rescue_body ])
-
- result = @builder.assign(val[0], val[1], rescue_)
- }
- | var_lhs tOP_ASGN arg
- {
- result = @builder.op_assign(val[0], val[1], val[2])
- }
- | var_lhs tOP_ASGN arg kRESCUE_MOD arg
- {
- rescue_body = @builder.rescue_body(val[3],
- nil, nil, nil,
- nil, val[4])
-
- rescue_ = @builder.begin_body(val[2], [ rescue_body ])
-
- result = @builder.op_assign(val[0], val[1], rescue_)
- }
- | primary_value tLBRACK2 opt_call_args rbracket tOP_ASGN arg
- {
- result = @builder.op_assign(
- @builder.index(
- val[0], val[1], val[2], val[3]),
- val[4], val[5])
- }
- | primary_value tDOT tIDENTIFIER tOP_ASGN arg
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tDOT tCONSTANT tOP_ASGN arg
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tCOLON2 tCONSTANT tOP_ASGN arg
- {
- diagnostic :error, :dynamic_const, nil, val[2], [ val[3] ]
- }
- | tCOLON3 tCONSTANT tOP_ASGN arg
- {
- diagnostic :error, :dynamic_const, nil, val[1], [ val[2] ]
- }
- | backref tOP_ASGN arg
- {
- result = @builder.op_assign(val[0], val[1], val[2])
- }
- | arg tDOT2 arg
- {
- result = @builder.range_inclusive(val[0], val[1], val[2])
- }
- | arg tDOT3 arg
- {
- result = @builder.range_exclusive(val[0], val[1], val[2])
- }
- | arg tPLUS arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tMINUS arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tSTAR2 arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tDIVIDE arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tPERCENT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tPOW arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | tUMINUS_NUM tINTEGER tPOW arg
- {
- result = @builder.unary_op(val[0],
- @builder.binary_op(
- @builder.integer(val[1]),
- val[2], val[3]))
- }
- | tUMINUS_NUM tFLOAT tPOW arg
- {
- result = @builder.unary_op(val[0],
- @builder.binary_op(
- @builder.float(val[1]),
- val[2], val[3]))
- }
- | tUPLUS arg
- {
- result = @builder.unary_op(val[0], val[1])
- }
- | tUMINUS arg
- {
- result = @builder.unary_op(val[0], val[1])
- }
- | arg tPIPE arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tCARET arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tAMPER2 arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tCMP arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tGT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tGEQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tLT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tLEQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tEQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tEQQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tNEQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tMATCH arg
- {
- result = @builder.match_op(val[0], val[1], val[2])
- }
- | arg tNMATCH arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | tBANG arg
- {
- result = @builder.not_op(val[0], nil, val[1], nil)
- }
- | tTILDE arg
- {
- result = @builder.unary_op(val[0], val[1])
- }
- | arg tLSHFT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tRSHFT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tANDOP arg
- {
- result = @builder.logical_op(:and, val[0], val[1], val[2])
- }
- | arg tOROP arg
- {
- result = @builder.logical_op(:or, val[0], val[1], val[2])
- }
- | kDEFINED opt_nl arg
- {
- result = @builder.keyword_cmd(:defined?, val[0], nil, [ val[2] ], nil)
- }
-
- | arg tEH arg opt_nl tCOLON arg
- {
- result = @builder.ternary(val[0], val[1],
- val[2], val[4], val[5])
- }
- | primary
-
- arg_value: arg
-
- aref_args: none
- | args trailer
- | args tCOMMA assocs trailer
- {
- result = val[0] << @builder.associate(nil, val[2], nil)
- }
- | assocs trailer
- {
- result = [ @builder.associate(nil, val[0], nil) ]
- }
-
- paren_args: tLPAREN2 opt_call_args rparen
- {
- result = val
- }
-
- opt_paren_args: # nothing
- {
- result = [ nil, [], nil ]
- }
- | paren_args
-
- opt_call_args: # nothing
- {
- result = []
- }
- | call_args
-
- call_args: command
- {
- result = [ val[0] ]
- }
- | args opt_block_arg
- {
- result = val[0].concat(val[1])
- }
- | assocs opt_block_arg
- {
- result = [ @builder.associate(nil, val[0], nil) ]
- result.concat(val[1])
- }
- | args tCOMMA assocs opt_block_arg
- {
- assocs = @builder.associate(nil, val[2], nil)
- result = val[0] << assocs
- result.concat(val[3])
- }
- | args tCOMMA assocs tCOMMA args opt_block_arg
- {
- val[2][-1] = @builder.objc_varargs(val[2][-1], val[4])
- assocs = @builder.associate(nil, val[2], nil)
- result = val[0] << assocs
- result.concat(val[5])
- }
- | block_arg
- {
- result = [ val[0] ]
- }
-
- call_args2: arg_value tCOMMA args opt_block_arg
- {
- result = [ val[0], *val[2].concat(val[3]) ]
- }
- | arg_value tCOMMA block_arg
- {
- result = [ val[0], val[2] ]
- }
- | assocs opt_block_arg
- {
- result = [ @builder.associate(nil, val[0], nil),
- *val[1] ]
- }
- | arg_value tCOMMA assocs opt_block_arg
- {
- result = [ val[0],
- @builder.associate(nil, val[2], nil),
- *val[3] ]
- }
- | arg_value tCOMMA args tCOMMA assocs opt_block_arg
- {
- result = [ val[0],
- *val[2].
- push(@builder.associate(nil, val[4], nil)).
- concat(val[5]) ]
- }
- | block_arg
- {
- result = [ val[0] ]
- }
-
- command_args: {
- result = @lexer.cmdarg.dup
- @lexer.cmdarg.push(true)
- }
- open_args
- {
- @lexer.cmdarg = val[0]
-
- result = val[1]
- }
-
- open_args: call_args
- {
- result = [ nil, val[0], nil ]
- }
- | tLPAREN_ARG
- {
- @lexer.state = :expr_endarg
- }
- rparen
- {
- result = [ val[0], [], val[2] ]
- }
- | tLPAREN_ARG call_args2
- {
- @lexer.state = :expr_endarg
- }
- rparen
- {
- result = [ val[0], val[1], val[3] ]
- }
-
- block_arg: tAMPER arg_value
- {
- result = @builder.block_pass(val[0], val[1])
- }
-
- opt_block_arg: tCOMMA block_arg
- {
- result = [ val[1] ]
- }
- | tCOMMA
- {
- result = []
- }
- | # nothing
- {
- result = []
- }
-
- args: arg_value
- {
- result = [ val[0] ]
- }
- | tSTAR arg_value
- {
- result = [ @builder.splat(val[0], val[1]) ]
- }
- | args tCOMMA arg_value
- {
- result = val[0] << val[2]
- }
- | args tCOMMA tSTAR arg_value
- {
- result = val[0] << @builder.splat(val[2], val[3])
- }
-
- mrhs: args tCOMMA arg_value
- {
- result = val[0] << val[2]
- }
- | args tCOMMA tSTAR arg_value
- {
- result = val[0] << @builder.splat(val[2], val[3])
- }
- | tSTAR arg_value
- {
- result = [ @builder.splat(val[0], val[1]) ]
- }
-
- primary: literal
- | strings
- | xstring
- | regexp
- | words
- | qwords
- | var_ref
- | backref
- | tFID
- {
- result = @builder.call_method(nil, nil, val[0])
- }
- | kBEGIN bodystmt kEND
- {
- result = @builder.begin_keyword(val[0], val[1], val[2])
- }
- | tLPAREN_ARG expr
- {
- @lexer.state = :expr_endarg
- }
- rparen
- {
- result = @builder.begin(val[0], val[1], val[3])
- }
- | tLPAREN compstmt tRPAREN
- {
- result = @builder.begin(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tCONSTANT
- {
- result = @builder.const_fetch(val[0], val[1], val[2])
- }
- | tCOLON3 tCONSTANT
- {
- result = @builder.const_global(val[0], val[1])
- }
- | tLBRACK aref_args tRBRACK
- {
- result = @builder.array(val[0], val[1], val[2])
- }
- | tLBRACE assoc_list tRCURLY
- {
- result = @builder.associate(val[0], val[1], val[2])
- }
- | kRETURN
- {
- result = @builder.keyword_cmd(:return, val[0])
- }
- | kYIELD tLPAREN2 call_args rparen
- {
- result = @builder.keyword_cmd(:yield, val[0], val[1], val[2], val[3])
- }
- | kYIELD tLPAREN2 rparen
- {
- result = @builder.keyword_cmd(:yield, val[0], val[1], [], val[2])
- }
- | kYIELD
- {
- result = @builder.keyword_cmd(:yield, val[0])
- }
- | kDEFINED opt_nl tLPAREN2 expr rparen
- {
- result = @builder.keyword_cmd(:defined?, val[0],
- val[2], [ val[3] ], val[4])
- }
- | kNOT tLPAREN2 expr rparen
- {
- result = @builder.not_op(val[0], val[1], val[2], val[3])
- }
- | kNOT tLPAREN2 rparen
- {
- result = @builder.not_op(val[0], val[1], nil, val[2])
- }
- | operation brace_block
- {
- method_call = @builder.call_method(nil, nil, val[0])
-
- begin_t, args, body, end_t = val[1]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
- | method_call
- | method_call brace_block
- {
- begin_t, args, body, end_t = val[1]
- result = @builder.block(val[0],
- begin_t, args, body, end_t)
- }
- | tLAMBDA lambda
- {
- lambda_call = @builder.call_lambda(val[0])
-
- args, (begin_t, body, end_t) = val[1]
- result = @builder.block(lambda_call,
- begin_t, args, body, end_t)
- }
- | kIF expr_value then compstmt if_tail kEND
- {
- else_t, else_ = val[4]
- result = @builder.condition(val[0], val[1], val[2],
- val[3], else_t,
- else_, val[5])
- }
- | kUNLESS expr_value then compstmt opt_else kEND
- {
- else_t, else_ = val[4]
- result = @builder.condition(val[0], val[1], val[2],
- else_, else_t,
- val[3], val[5])
- }
- | kWHILE
- {
- @lexer.cond.push(true)
- }
- expr_value do
- {
- @lexer.cond.pop
- }
- compstmt kEND
- {
- result = @builder.loop(:while, val[0], val[2], val[3],
- val[5], val[6])
- }
- | kUNTIL
- {
- @lexer.cond.push(true)
- }
- expr_value do
- {
- @lexer.cond.pop
- }
- compstmt kEND
- {
- result = @builder.loop(:until, val[0], val[2], val[3],
- val[5], val[6])
- }
- | kCASE expr_value opt_terms case_body kEND
- {
- *when_bodies, (else_t, else_body) = *val[3]
-
- result = @builder.case(val[0], val[1],
- when_bodies, else_t, else_body,
- val[4])
- }
- | kCASE opt_terms case_body kEND
- {
- *when_bodies, (else_t, else_body) = *val[2]
-
- result = @builder.case(val[0], nil,
- when_bodies, else_t, else_body,
- val[3])
- }
- | kFOR for_var kIN
- {
- @lexer.cond.push(true)
- }
- expr_value do
- {
- @lexer.cond.pop
- }
- compstmt kEND
- {
- result = @builder.for(val[0], val[1],
- val[2], val[4],
- val[5], val[7], val[8])
- }
- | kCLASS cpath superclass
- {
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- bodystmt kEND
- {
- if in_def?
- diagnostic :error, :class_in_def, nil, val[0]
- end
-
- lt_t, superclass = val[2]
- result = @builder.def_class(val[0], val[1],
- lt_t, superclass,
- val[4], val[5])
-
- @lexer.pop_cmdarg
- @static_env.unextend
- }
- | kCLASS tLSHFT expr term
- {
- result = @def_level
- @def_level = 0
-
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- bodystmt kEND
- {
- result = @builder.def_sclass(val[0], val[1], val[2],
- val[5], val[6])
-
- @lexer.pop_cmdarg
- @static_env.unextend
-
- @def_level = val[4]
- }
- | kMODULE cpath
- {
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- bodystmt kEND
- {
- if in_def?
- diagnostic :error, :module_in_def, nil, val[0]
- end
-
- result = @builder.def_module(val[0], val[1],
- val[3], val[4])
-
- @lexer.pop_cmdarg
- @static_env.unextend
- }
- | kDEF fname
- {
- @def_level += 1
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- f_arglist bodystmt kEND
- {
- result = @builder.def_method(val[0], val[1],
- val[3], val[4], val[5])
-
- @lexer.pop_cmdarg
- @static_env.unextend
- @def_level -= 1
- }
- | kDEF singleton dot_or_colon
- {
- @lexer.state = :expr_fname
- }
- fname
- {
- @def_level += 1
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- f_arglist bodystmt kEND
- {
- result = @builder.def_singleton(val[0], val[1], val[2],
- val[4], val[6], val[7], val[8])
-
- @lexer.pop_cmdarg
- @static_env.unextend
- @def_level -= 1
- }
- | kBREAK
- {
- result = @builder.keyword_cmd(:break, val[0])
- }
- | kNEXT
- {
- result = @builder.keyword_cmd(:next, val[0])
- }
- | kREDO
- {
- result = @builder.keyword_cmd(:redo, val[0])
- }
- | kRETRY
- {
- result = @builder.keyword_cmd(:retry, val[0])
- }
-
- primary_value: primary
-
- then: term
- | kTHEN
- | term kTHEN
- {
- result = val[1]
- }
-
- do: term
- | kDO_COND
-
- if_tail: opt_else
- | kELSIF expr_value then compstmt if_tail
- {
- else_t, else_ = val[4]
- result = [ val[0],
- @builder.condition(val[0], val[1], val[2],
- val[3], else_t,
- else_, nil),
- ]
- }
-
- opt_else: none
- | kELSE compstmt
- {
- result = val
- }
-
- for_var: lhs
- | mlhs
-
- f_marg: f_norm_arg
- | tLPAREN f_margs rparen
- {
- result = @builder.multi_lhs(val[0], val[1], val[2])
- }
-
- f_marg_list: f_marg
- {
- result = [ val[0] ]
- }
- | f_marg_list tCOMMA f_marg
- {
- result = val[0] << val[2]
- }
-
- f_margs: f_marg_list
- | f_marg_list tCOMMA tSTAR f_norm_arg
- {
- result = val[0].
- push(@builder.objc_restarg(val[2], val[3]))
- }
- | f_marg_list tCOMMA tSTAR f_norm_arg tCOMMA f_marg_list
- {
- result = val[0].
- push(@builder.objc_restarg(val[2], val[3])).
- concat(val[5])
- }
- | f_marg_list tCOMMA tSTAR
- {
- result = val[0].
- push(@builder.objc_restarg(val[2]))
- }
- | f_marg_list tCOMMA tSTAR tCOMMA f_marg_list
- {
- result = val[0].
- push(@builder.objc_restarg(val[2])).
- concat(val[4])
- }
- | tSTAR f_norm_arg
- {
- result = [ @builder.objc_restarg(val[0], val[1]) ]
- }
- | tSTAR f_norm_arg tCOMMA f_marg_list
- {
- result = [ @builder.objc_restarg(val[0], val[1]),
- *val[3] ]
- }
- | tSTAR
- {
- result = [ @builder.objc_restarg(val[0]) ]
- }
- | tSTAR tCOMMA f_marg_list
- {
- result = [ @builder.objc_restarg(val[0]),
- *val[2] ]
- }
-
- block_param: f_arg tCOMMA f_block_optarg tCOMMA f_rest_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg tCOMMA f_block_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[6]).
- concat(val[7])
- }
- | f_arg tCOMMA f_block_optarg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_arg tCOMMA f_block_optarg tCOMMA f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg tCOMMA f_rest_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_arg tCOMMA
- | f_arg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg opt_f_block_arg
- {
- result = val[0].concat(val[1])
- }
- | f_block_optarg tCOMMA f_rest_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_block_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_block_optarg opt_f_block_arg
- {
- result = val[0].
- concat(val[1])
- }
- | f_block_optarg tCOMMA f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_rest_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[1])
- }
- | f_rest_arg tCOMMA f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_block_arg
- {
- result = [ val[0] ]
- }
-
- opt_block_param: # nothing
- {
- result = @builder.args(nil, [], nil)
- }
- | block_param_def
- {
- @lexer.state = :expr_value
- }
-
- block_param_def: tPIPE opt_bv_decl tPIPE
- {
- result = @builder.args(val[0], val[1], val[2])
- }
- | tOROP
- {
- result = @builder.args(val[0], [], val[0])
- }
- | tPIPE block_param opt_bv_decl tPIPE
- {
- result = @builder.args(val[0], val[1].concat(val[2]), val[3])
- }
-
- opt_bv_decl: # nothing
- {
- result = []
- }
- | tSEMI bv_decls
- {
- result = val[1]
- }
-
- bv_decls: bvar
- {
- result = [ val[0] ]
- }
- | bv_decls tCOMMA bvar
- {
- result = val[0] << val[2]
- }
-
- bvar: tIDENTIFIER
- {
- result = @builder.shadowarg(val[0])
- }
- | f_bad_arg
-
- lambda: {
- @static_env.extend_dynamic
- }
- f_larglist lambda_body
- {
- result = [ val[1], val[2] ]
-
- @static_env.unextend
- }
-
- f_larglist: tLPAREN2 f_args opt_bv_decl rparen
- {
- result = @builder.args(val[0], val[1].concat(val[2]), val[3])
- }
- | f_args
- {
- result = @builder.args(nil, val[0], nil)
- }
-
- lambda_body: tLAMBEG compstmt tRCURLY
- {
- result = [ val[0], val[1], val[2] ]
- }
- | kDO_LAMBDA compstmt kEND
- {
- result = [ val[0], val[1], val[2] ]
- }
-
- do_block: kDO_BLOCK
- {
- @static_env.extend_dynamic
- }
- opt_block_param compstmt kEND
- {
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
- }
-
- block_call: command do_block
- {
- begin_t, block_args, body, end_t = val[1]
- result = @builder.block(val[0],
- begin_t, block_args, body, end_t)
- }
- | block_call tDOT operation2 opt_paren_args
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
- }
- | block_call tCOLON2 operation2 opt_paren_args
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
- }
-
- method_call: operation paren_args
- {
- lparen_t, args, rparen_t = val[1]
- result = @builder.call_method(nil, nil, val[0],
- lparen_t, args, rparen_t)
- }
- | primary_value tDOT operation2 opt_paren_args
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
- }
- | primary_value tCOLON2 operation2 paren_args
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
- }
- | primary_value tCOLON2 operation3
- {
- result = @builder.call_method(val[0], val[1], val[2])
- }
- | primary_value tDOT paren_args
- {
- lparen_t, args, rparen_t = val[2]
- result = @builder.call_method(val[0], val[1], nil,
- lparen_t, args, rparen_t)
- }
- | primary_value tCOLON2 paren_args
- {
- lparen_t, args, rparen_t = val[2]
- result = @builder.call_method(val[0], val[1], nil,
- lparen_t, args, rparen_t)
- }
- | kSUPER paren_args
- {
- lparen_t, args, rparen_t = val[1]
- result = @builder.keyword_cmd(:super, val[0],
- lparen_t, args, rparen_t)
- }
- | kSUPER
- {
- result = @builder.keyword_cmd(:zsuper, val[0])
- }
- | primary_value tLBRACK2 opt_call_args rbracket
- {
- result = @builder.index(val[0], val[1], val[2], val[3])
- }
-
- brace_block: tLCURLY
- {
- @static_env.extend_dynamic
- }
- opt_block_param compstmt tRCURLY
- {
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
- }
- | kDO
- {
- @static_env.extend_dynamic
- }
- opt_block_param compstmt kEND
- {
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
- }
-
- case_body: kWHEN args then compstmt cases
- {
- result = [ @builder.when(val[0], val[1], val[2], val[3]),
- *val[4] ]
- }
-
- cases: opt_else
- {
- result = [ val[0] ]
- }
- | case_body
-
- opt_rescue: kRESCUE exc_list exc_var then compstmt opt_rescue
- {
- assoc_t, exc_var = val[2]
-
- if val[1]
- exc_list = @builder.array(nil, val[1], nil)
- end
-
- result = [ @builder.rescue_body(val[0],
- exc_list, assoc_t, exc_var,
- val[3], val[4]),
- *val[5] ]
- }
- |
- {
- result = []
- }
-
- exc_list: arg_value
- {
- result = [ val[0] ]
- }
- | mrhs
- | none
-
- exc_var: tASSOC lhs
- {
- result = [ val[0], val[1] ]
- }
- | none
-
- opt_ensure: kENSURE compstmt
- {
- result = [ val[0], val[1] ]
- }
- | none
-
- literal: numeric
- | symbol
- | dsym
-
- strings: string
- {
- result = @builder.string_compose(nil, val[0], nil)
- }
-
- string: string1
- {
- result = [ val[0] ]
- }
- | string string1
- {
- result = val[0] << val[1]
- }
-
- string1: tSTRING_BEG string_contents tSTRING_END
- {
- result = @builder.string_compose(val[0], val[1], val[2])
- }
- | tSTRING
- {
- result = @builder.string(val[0])
- }
- | tCHARACTER
- {
- result = @builder.character(val[0])
- }
-
- xstring: tXSTRING_BEG xstring_contents tSTRING_END
- {
- result = @builder.xstring_compose(val[0], val[1], val[2])
- }
-
- regexp: tREGEXP_BEG regexp_contents tSTRING_END tREGEXP_OPT
- {
- opts = @builder.regexp_options(val[3])
- result = @builder.regexp_compose(val[0], val[1], val[2], opts)
- }
-
- words: tWORDS_BEG word_list tSTRING_END
- {
- result = @builder.words_compose(val[0], val[1], val[2])
- }
-
- word_list: # nothing
- {
- result = []
- }
- | word_list word tSPACE
- {
- result = val[0] << @builder.word(val[1])
- }
-
- word: string_content
- {
- result = [ val[0] ]
- }
- | word string_content
- {
- result = val[0] << val[1]
- }
-
- qwords: tQWORDS_BEG qword_list tSTRING_END
- {
- result = @builder.words_compose(val[0], val[1], val[2])
- }
-
- qword_list: # nothing
- {
- result = []
- }
- | qword_list tSTRING_CONTENT tSPACE
- {
- result = val[0] << @builder.string_internal(val[1])
- }
-
- string_contents: # nothing
- {
- result = []
- }
- | string_contents string_content
- {
- result = val[0] << val[1]
- }
-
-xstring_contents: # nothing
- {
- result = []
- }
- | xstring_contents string_content
- {
- result = val[0] << val[1]
- }
-
-regexp_contents: # nothing
- {
- result = []
- }
- | regexp_contents string_content
- {
- result = val[0] << val[1]
- }
-
- string_content: tSTRING_CONTENT
- {
- result = @builder.string_internal(val[0])
- }
- | tSTRING_DVAR string_dvar
- {
- result = val[1]
- }
- | tSTRING_DBEG
- {
- @lexer.cond.push(false)
- @lexer.cmdarg.push(false)
- }
- compstmt tRCURLY
- {
- @lexer.cond.lexpop
- @lexer.cmdarg.lexpop
-
- result = @builder.begin(val[0], val[2], val[3])
- }
-
- string_dvar: tGVAR
- {
- result = @builder.gvar(val[0])
- }
- | tIVAR
- {
- result = @builder.ivar(val[0])
- }
- | tCVAR
- {
- result = @builder.cvar(val[0])
- }
- | backref
-
-
- symbol: tSYMBOL
- {
- result = @builder.symbol(val[0])
- }
-
- dsym: tSYMBEG xstring_contents tSTRING_END
- {
- result = @builder.symbol_compose(val[0], val[1], val[2])
- }
-
- numeric: tINTEGER
- {
- result = @builder.integer(val[0])
- }
- | tFLOAT
- {
- result = @builder.float(val[0])
- }
- | tUMINUS_NUM tINTEGER =tLOWEST
- {
- result = @builder.negate(val[0],
- @builder.integer(val[1]))
- }
- | tUMINUS_NUM tFLOAT =tLOWEST
- {
- result = @builder.negate(val[0],
- @builder.float(val[1]))
- }
-
- variable: tIDENTIFIER
- {
- result = @builder.ident(val[0])
- }
- | tIVAR
- {
- result = @builder.ivar(val[0])
- }
- | tGVAR
- {
- result = @builder.gvar(val[0])
- }
- | tCONSTANT
- {
- result = @builder.const(val[0])
- }
- | tCVAR
- {
- result = @builder.cvar(val[0])
- }
- | kNIL
- {
- result = @builder.nil(val[0])
- }
- | kSELF
- {
- result = @builder.self(val[0])
- }
- | kTRUE
- {
- result = @builder.true(val[0])
- }
- | kFALSE
- {
- result = @builder.false(val[0])
- }
- | k__FILE__
- {
- result = @builder.__FILE__(val[0])
- }
- | k__LINE__
- {
- result = @builder.__LINE__(val[0])
- }
- | k__ENCODING__
- {
- result = @builder.__ENCODING__(val[0])
- }
-
- var_ref: variable
- {
- result = @builder.accessible(val[0])
- }
-
- var_lhs: variable
- {
- result = @builder.assignable(val[0])
- }
-
- backref: tNTH_REF
- {
- result = @builder.nth_ref(val[0])
- }
- | tBACK_REF
- {
- result = @builder.back_ref(val[0])
- }
-
- superclass: term
- {
- result = nil
- }
- | tLT expr_value term
- {
- result = [ val[0], val[1] ]
- }
- | error term
- {
- yyerrok
- result = nil
- }
-
- f_arglist: tLPAREN2 f_args rparen
- {
- result = @builder.args(val[0], val[1], val[2])
-
- @lexer.state = :expr_value
- }
- | f_args term
- {
- result = @builder.args(nil, val[0], nil)
- }
-
- f_args: f_arg tCOMMA f_optarg tCOMMA f_rest_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg tCOMMA f_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[6]).
- concat(val[7])
- }
- | f_arg tCOMMA f_optarg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_arg tCOMMA f_optarg tCOMMA f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg tCOMMA f_rest_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_arg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[1])
- }
- | f_optarg tCOMMA f_rest_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_optarg opt_f_block_arg
- {
- result = val[0].
- concat(val[1])
- }
- | f_optarg tCOMMA f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_rest_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[1])
- }
- | f_rest_arg tCOMMA f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_block_arg
- {
- result = [ val[0] ]
- }
- | # nothing
- {
- result = []
- }
-
- f_bad_arg: tCONSTANT
- {
- diagnostic :error, :argument_const, nil, val[0]
- }
- | tIVAR
- {
- diagnostic :error, :argument_ivar, nil, val[0]
- }
- | tGVAR
- {
- diagnostic :error, :argument_gvar, nil, val[0]
- }
- | tCVAR
- {
- diagnostic :error, :argument_cvar, nil, val[0]
- }
-
- f_norm_arg: f_bad_arg
- | tIDENTIFIER
- {
- @static_env.declare val[0][0]
-
- result = @builder.arg(val[0])
- }
- | tIDENTIFIER tASSOC tIDENTIFIER
- {
- @static_env.declare val[2][0]
-
- result = @builder.objc_kwarg(val[0], val[1], val[2])
- }
- | tLABEL tIDENTIFIER
- {
- @static_env.declare val[1][0]
-
- result = @builder.objc_kwarg(val[0], nil, val[1])
- }
-
- f_arg_item: f_norm_arg
- | tLPAREN f_margs rparen
- {
- result = @builder.multi_lhs(val[0], val[1], val[2])
- }
-
- f_arg: f_arg_item
- {
- result = [ val[0] ]
- }
- | f_arg tCOMMA f_arg_item
- {
- result = val[0] << val[2]
- }
-
- f_opt: tIDENTIFIER tEQL arg_value
- {
- @static_env.declare val[0][0]
-
- result = @builder.optarg(val[0], val[1], val[2])
- }
-
- f_block_opt: tIDENTIFIER tEQL primary_value
- {
- @static_env.declare val[0][0]
-
- result = @builder.optarg(val[0], val[1], val[2])
- }
-
- f_block_optarg: f_block_opt
- {
- result = [ val[0] ]
- }
- | f_block_optarg tCOMMA f_block_opt
- {
- result = val[0] << val[2]
- }
-
- f_optarg: f_opt
- {
- result = [ val[0] ]
- }
- | f_optarg tCOMMA f_opt
- {
- result = val[0] << val[2]
- }
-
- restarg_mark: tSTAR2 | tSTAR
-
- f_rest_arg: restarg_mark tIDENTIFIER
- {
- @static_env.declare val[1][0]
-
- result = [ @builder.restarg(val[0], val[1]) ]
- }
- | restarg_mark
- {
- result = [ @builder.restarg(val[0]) ]
- }
-
- blkarg_mark: tAMPER2 | tAMPER
-
- f_block_arg: blkarg_mark tIDENTIFIER
- {
- @static_env.declare val[1][0]
-
- result = @builder.blockarg(val[0], val[1])
- }
-
- opt_f_block_arg: tCOMMA f_block_arg
- {
- result = [ val[1] ]
- }
- | # nothing
- {
- result = []
- }
-
- singleton: var_ref
- | tLPAREN2 expr rparen
- {
- result = val[1]
- }
-
- assoc_list: # nothing
- {
- result = []
- }
- | assocs trailer
-
- assocs: assoc
- {
- result = [ val[0] ]
- }
- | assocs tCOMMA assoc
- {
- result = val[0] << val[2]
- }
-
- assoc: arg_value tASSOC arg_value
- {
- result = @builder.pair(val[0], val[1], val[2])
- }
- | tLABEL arg_value
- {
- result = @builder.pair_keyword(val[0], val[1])
- }
-
- operation: tIDENTIFIER | tCONSTANT | tFID
- operation2: tIDENTIFIER | tCONSTANT | tFID | op
- operation3: tIDENTIFIER | tFID | op
- dot_or_colon: tDOT | tCOLON2
- opt_terms: | terms
- opt_nl: | tNL
- rparen: opt_nl tRPAREN
- {
- result = val[1]
- }
- rbracket: opt_nl tRBRACK
- {
- result = val[1]
- }
- trailer: | tNL | tCOMMA
-
- term: tSEMI
- {
- yyerrok
- }
- | tNL
-
- terms: term
- | terms tSEMI
-
- none: # nothing
- {
- result = nil
- }
-end
-
----- header
-
-require 'parser'
-
-Parser.check_for_encoding_support
-
----- inner
-
- def version
- 19 # closest released match: v1_9_0_2
- end
-
- def default_encoding
- Encoding::BINARY
- end
diff --git a/test/racc/assets/mailp.y b/test/racc/assets/mailp.y
deleted file mode 100644
index eb7d4d529d..0000000000
--- a/test/racc/assets/mailp.y
+++ /dev/null
@@ -1,437 +0,0 @@
-#
-# mailp for test
-#
-
-class Testp
-
-rule
-
- content : DateH datetime { @field.date = val[1] }
- | RecvH received
- | RetpathH returnpath
- | MaddrH addrs { @field.addrs.replace val[1] }
- | SaddrH addr { @field.addr = val[1] }
- | MmboxH mboxes { @field.addrs.replace val[1] }
- | SmboxH mbox { @field.addr = val[1] }
- | MsgidH msgid { @field.msgid = val[1] }
- | KeyH keys { @field.keys.replace val[1] }
- | EncH enc
- | VersionH version
- | CTypeH ctype
- | CEncodingH cencode
- | CDispositionH cdisp
- | Mbox mbox
- {
- mb = val[1]
- @field.phrase = mb.phrase
- @field.setroute mb.route
- @field.local = mb.local
- @field.domain = mb.domain
- }
- | Spec spec
- {
- mb = val[1]
- @field.local = mb.local
- @field.domain = mb.domain
- }
- ;
-
- datetime : day DIGIT ATOM DIGIT hour zone
- # 0 1 2 3 4 5
- # day month year
- {
- t = Time.gm( val[3].to_i, val[2], val[1].to_i, 0, 0, 0 )
- result = (t + val[4] - val[5]).localtime
- }
- ;
-
- day : /* none */
- | ATOM ','
- ;
-
- hour : DIGIT ':' DIGIT
- {
- result = (result.to_i * 60 * 60) + (val[2].to_i * 60)
- }
- | DIGIT ':' DIGIT ':' DIGIT
- {
- result = (result.to_i * 60 * 60) +
- (val[2].to_i * 60)
- + val[4].to_i
- }
- ;
-
- zone : ATOM
- {
- result = ::TMail.zonestr2i( val[0] ) * 60
- }
- ;
-
- received : from by via with id for recvdatetime
- ;
-
- from : /* none */
- | FROM domain
- {
- @field.from = Address.join( val[1] )
- }
- | FROM domain '@' domain
- {
- @field.from = Address.join( val[3] )
- }
- | FROM domain DOMLIT
- {
- @field.from = Address.join( val[1] )
- }
- ;
-
- by : /* none */
- | BY domain
- {
- @field.by = Address.join( val[1] )
- }
- ;
-
- via : /* none */
- | VIA ATOM
- {
- @field.via = val[1]
- }
- ;
-
- with : /* none */
- | WITH ATOM
- {
- @field.with.push val[1]
- }
- ;
-
- id : /* none */
- | ID msgid
- {
- @field.msgid = val[1]
- }
- | ID ATOM
- {
- @field.msgid = val[1]
- }
- ;
-
- for : /* none */
- | FOR addr
- {
- @field.for_ = val[1].address
- }
- ;
-
- recvdatetime
- : /* none */
- | ';' datetime
- {
- @field.date = val[1]
- }
- ;
-
- returnpath: '<' '>'
- | routeaddr
- {
- @field.route.replace result.route
- @field.addr = result.addr
- }
- ;
-
- addrs : addr { result = val }
- | addrs ',' addr { result.push val[2] }
- ;
-
- addr : mbox
- | group
- ;
-
- mboxes : mbox
- {
- result = val
- }
- | mboxes ',' mbox
- {
- result.push val[2]
- }
- ;
-
- mbox : spec
- | routeaddr
- | phrase routeaddr
- {
- val[1].phrase = HFdecoder.decode( result )
- result = val[1]
- }
- ;
-
- group : phrase ':' mboxes ';'
- {
- result = AddressGroup.new( result, val[2] )
- }
- # | phrase ':' ';' { result = AddressGroup.new( result ) }
- ;
-
- routeaddr : '<' route spec '>'
- {
- result = val[2]
- result.route = val[1]
- }
- | '<' spec '>'
- {
- result = val[1]
- }
- ;
-
- route : at_domains ':'
- ;
-
- at_domains: '@' domain { result = [ val[1] ] }
- | at_domains ',' '@' domain { result.push val[3] }
- ;
-
- spec : local '@' domain { result = Address.new( val[0], val[2] ) }
- | local { result = Address.new( result, nil ) }
- ;
-
- local : word { result = val }
- | local '.' word { result.push val[2] }
- ;
-
- domain : domword { result = val }
- | domain '.' domword { result.push val[2] }
- ;
-
- domword : atom
- | DOMLIT
- | DIGIT
- ;
-
- msgid : '<' spec '>'
- {
- val[1] = val[1].addr
- result = val.join('')
- }
- ;
-
- phrase : word
- | phrase word { result << ' ' << val[1] }
- ;
-
- word : atom
- | QUOTED
- | DIGIT
- ;
-
- keys : phrase
- | keys ',' phrase
- ;
-
- enc : word
- {
- @field.encrypter = val[0]
- }
- | word word
- {
- @field.encrypter = val[0]
- @field.keyword = val[1]
- }
- ;
-
- version : DIGIT '.' DIGIT
- {
- @field.major = val[0].to_i
- @field.minor = val[2].to_i
- }
- ;
-
- ctype : TOKEN '/' TOKEN params
- {
- @field.main = val[0]
- @field.sub = val[2]
- }
- | TOKEN params
- {
- @field.main = val[0]
- @field.sub = ''
- }
- ;
-
- params : /* none */
- | params ';' TOKEN '=' value
- {
- @field.params[ val[2].downcase ] = val[4]
- }
- ;
-
- value : TOKEN
- | QUOTED
- ;
-
- cencode : TOKEN
- {
- @field.encoding = val[0]
- }
- ;
-
- cdisp : TOKEN disp_params
- {
- @field.disposition = val[0]
- }
- ;
-
- disp_params
- : /* none */
- | disp_params ';' disp_param
- ;
-
- disp_param: /* none */
- | TOKEN '=' value
- {
- @field.params[ val[0].downcase ] = val[2]
- }
- ;
-
- atom : ATOM
- | FROM
- | BY
- | VIA
- | WITH
- | ID
- | FOR
- ;
-
-end
-
-
----- header
-#
-# mailp for test
-#
-
-require 'tmail/mails'
-
-
-module TMail
-
----- inner
-
- MAILP_DEBUG = false
-
- def initialize
- self.debug = MAILP_DEBUG
- end
-
- def debug=( flag )
- @yydebug = flag && Racc_debug_parser
- @scanner_debug = flag
- end
-
- def debug
- @yydebug
- end
-
-
- def Mailp.parse( str, obj, ident )
- new.parse( str, obj, ident )
- end
-
-
- NATIVE_ROUTINE = {
- 'TMail::MsgidH' => :msgid_parse,
- 'TMail::RefH' => :refs_parse
- }
-
- def parse( str, obj, ident )
- return if /\A\s*\z/ === str
-
- @field = obj
-
- if mid = NATIVE_ROUTINE[ obj.type.name ] then
- send mid, str
- else
- unless ident then
- ident = obj.type.name.split('::')[-1].to_s
- cmt = []
- obj.comments.replace cmt
- else
- cmt = nil
- end
-
- @scanner = MailScanner.new( str, ident, cmt )
- @scanner.debug = @scanner_debug
- @first = [ ident.intern, ident ]
- @pass_array = [nil, nil]
-
- do_parse
- end
- end
-
-
- private
-
-
- def next_token
- if @first then
- ret = @first
- @first = nil
- ret
- else
- @scanner.scan @pass_array
- end
- end
-
- def on_error( tok, val, vstack )
- raise ParseError,
- "\nparse error in '#{@field.name}' header, on token #{val.inspect}"
- end
-
-
-
- def refs_parse( str )
- arr = []
-
- while mdata = ::TMail::MSGID.match( str ) do
- str = mdata.post_match
-
- pre = mdata.pre_match
- pre.strip!
- proc_phrase pre, arr unless pre.empty?
- arr.push mdata.to_s
- end
- str.strip!
- proc_phrase str, arr if not pre or pre.empty?
-
- @field.refs.replace arr
- end
-
- def proc_phrase( str, arr )
- while mdata = /"([^\\]*(?:\\.[^"\\]*)*)"/.match( str ) do
- str = mdata.post_match
-
- pre = mdata.pre_match
- pre.strip!
- arr.push pre unless pre.empty?
- arr.push mdata[1]
- end
- str.strip!
- arr.push unless str.empty?
- end
-
-
- def msgid_parse( str )
- if mdata = ::TMail::MSGID.match( str ) then
- @field.msgid = mdata.to_s
- else
- raise ParseError, "wrong Message-ID format: #{str}"
- end
- end
-
----- footer
-
-end # module TMail
-
-mp = TMail::Testp.new
-mp.parse
diff --git a/test/racc/assets/mediacloth.y b/test/racc/assets/mediacloth.y
deleted file mode 100644
index 94cc411ea7..0000000000
--- a/test/racc/assets/mediacloth.y
+++ /dev/null
@@ -1,599 +0,0 @@
-# Copyright (c) 2006 Pluron Inc.
-#
-# Permission is hereby granted, free of charge, to any person obtaining
-# a copy of this software and associated documentation files (the
-# "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to
-# permit persons to whom the Software is furnished to do so, subject to
-# the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-# The parser for the MediaWiki language.
-#
-# Usage together with a lexer:
-# inputFile = File.new("data/input1", "r")
-# input = inputFile.read
-# parser = MediaWikiParser.new
-# parser.lexer = MediaWikiLexer.new
-# parser.parse(input)
-
-class MediaWikiParser
-
-token TEXT BOLD_START BOLD_END ITALIC_START ITALIC_END LINK_START LINK_END LINKSEP
- INTLINK_START INTLINK_END INTLINKSEP RESOURCESEP CHAR_ENT
- PRE_START PRE_END PREINDENT_START PREINDENT_END
- SECTION_START SECTION_END HLINE SIGNATURE_NAME SIGNATURE_DATE SIGNATURE_FULL
- PARA_START PARA_END UL_START UL_END OL_START OL_END LI_START LI_END
- DL_START DL_END DT_START DT_END DD_START DD_END TAG_START TAG_END ATTR_NAME ATTR_VALUE
- TABLE_START TABLE_END ROW_START ROW_END HEAD_START HEAD_END CELL_START CELL_END
- KEYWORD TEMPLATE_START TEMPLATE_END CATEGORY PASTE_START PASTE_END
-
-
-rule
-
-wiki:
- repeated_contents
- {
- @nodes.push WikiAST.new(0, @wiki_ast_length)
- #@nodes.last.children.insert(0, val[0])
- #puts val[0]
- @nodes.last.children += val[0]
- }
- ;
-
-contents:
- text
- {
- result = val[0]
- }
- | bulleted_list
- {
- result = val[0]
- }
- | numbered_list
- {
- result = val[0]
- }
- | dictionary_list
- {
- list = ListAST.new(@ast_index, @ast_length)
- list.list_type = :Dictionary
- list.children = val[0]
- result = list
- }
- | preformatted
- {
- result = val[0]
- }
- | section
- {
- result = val[0]
- }
- | tag
- {
- result = val[0]
- }
- | template
- {
- result = val[0]
- }
- | KEYWORD
- {
- k = KeywordAST.new(@ast_index, @ast_length)
- k.text = val[0]
- result = k
- }
- | PARA_START para_contents PARA_END
- {
- p = ParagraphAST.new(@ast_index, @ast_length)
- p.children = val[1]
- result = p
- }
- | LINK_START link_contents LINK_END
- {
- l = LinkAST.new(@ast_index, @ast_length)
- l.link_type = val[0]
- l.url = val[1][0]
- l.children += val[1][1..-1] if val[1].length > 1
- result = l
- }
- | PASTE_START para_contents PASTE_END
- {
- p = PasteAST.new(@ast_index, @ast_length)
- p.children = val[1]
- result = p
- }
- | INTLINK_START TEXT RESOURCESEP TEXT reslink_repeated_contents INTLINK_END
- {
- l = ResourceLinkAST.new(@ast_index, @ast_length)
- l.prefix = val[1]
- l.locator = val[3]
- l.children = val[4] unless val[4].nil? or val[4].empty?
- result = l
- }
- | INTLINK_START TEXT intlink_repeated_contents INTLINK_END
- {
- l = InternalLinkAST.new(@ast_index, @ast_length)
- l.locator = val[1]
- l.children = val[2] unless val[2].nil? or val[2].empty?
- result = l
- }
- | INTLINK_START CATEGORY TEXT cat_sort_contents INTLINK_END
- {
- l = CategoryAST.new(@ast_index, @ast_length)
- l.locator = val[2]
- l.sort_as = val[3]
- result = l
- }
- | INTLINK_START RESOURCESEP CATEGORY TEXT intlink_repeated_contents INTLINK_END
- {
- l = CategoryLinkAST.new(@ast_index, @ast_length)
- l.locator = val[3]
- l.children = val[4] unless val[4].nil? or val[4].empty?
- result = l
- }
- | table
- ;
-
-para_contents:
- {
- result = nil
- }
- | repeated_contents
- {
- result = val[0]
- }
- ;
-
-tag:
- TAG_START tag_attributes TAG_END
- {
- if val[0] != val[2]
- raise Racc::ParseError.new("XHTML end tag #{val[2]} does not match start tag #{val[0]}")
- end
- elem = ElementAST.new(@ast_index, @ast_length)
- elem.name = val[0]
- elem.attributes = val[1]
- result = elem
- }
- | TAG_START tag_attributes repeated_contents TAG_END
- {
- if val[0] != val[3]
- raise Racc::ParseError.new("XHTML end tag #{val[3]} does not match start tag #{val[0]}")
- end
- elem = ElementAST.new(@ast_index, @ast_length)
- elem.name = val[0]
- elem.attributes = val[1]
- elem.children += val[2]
- result = elem
- }
- ;
-
-tag_attributes:
- {
- result = nil
- }
- | ATTR_NAME tag_attributes
- {
- attr_map = val[2] ? val[2] : {}
- attr_map[val[0]] = true
- result = attr_map
- }
- | ATTR_NAME ATTR_VALUE tag_attributes
- {
- attr_map = val[2] ? val[2] : {}
- attr_map[val[0]] = val[1]
- result = attr_map
- }
- ;
-
-
-link_contents:
- TEXT
- {
- result = val
- }
- | TEXT LINKSEP link_repeated_contents
- {
- result = [val[0]]
- result += val[2]
- }
- ;
-
-
-link_repeated_contents:
- repeated_contents
- {
- result = val[0]
- }
- | repeated_contents LINKSEP link_repeated_contents
- {
- result = val[0]
- result += val[2] if val[2]
- }
- ;
-
-
-intlink_repeated_contents:
- {
- result = nil
- }
- | INTLINKSEP repeated_contents
- {
- result = val[1]
- }
- ;
-
-cat_sort_contents:
- {
- result = nil
- }
- | INTLINKSEP TEXT
- {
- result = val[1]
- }
- ;
-
-reslink_repeated_contents:
- {
- result = nil
- }
- | INTLINKSEP reslink_repeated_contents
- {
- result = val[1]
- }
- | INTLINKSEP repeated_contents reslink_repeated_contents
- {
- i = InternalLinkItemAST.new(@ast_index, @ast_length)
- i.children = val[1]
- result = [i]
- result += val[2] if val[2]
- }
- ;
-
-repeated_contents: contents
- {
- result = []
- result << val[0]
- }
- | repeated_contents contents
- {
- result = []
- result += val[0]
- result << val[1]
- }
- ;
-
-text: element
- {
- p = TextAST.new(@ast_index, @ast_length)
- p.formatting = val[0][0]
- p.contents = val[0][1]
- result = p
- }
- | formatted_element
- {
- result = val[0]
- }
- ;
-
-table:
- TABLE_START table_contents TABLE_END
- {
- table = TableAST.new(@ast_index, @ast_length)
- table.children = val[1] unless val[1].nil? or val[1].empty?
- result = table
- }
- | TABLE_START TEXT table_contents TABLE_END
- {
- table = TableAST.new(@ast_index, @ast_length)
- table.options = val[1]
- table.children = val[2] unless val[2].nil? or val[2].empty?
- result = table
- }
-
-table_contents:
- {
- result = nil
- }
- | ROW_START row_contents ROW_END table_contents
- {
- row = TableRowAST.new(@ast_index, @ast_length)
- row.children = val[1] unless val[1].nil? or val[1].empty?
- result = [row]
- result += val[3] unless val[3].nil? or val[3].empty?
- }
- | ROW_START TEXT row_contents ROW_END table_contents
- {
- row = TableRowAST.new(@ast_index, @ast_length)
- row.children = val[2] unless val[2].nil? or val[2].empty?
- row.options = val[1]
- result = [row]
- result += val[4] unless val[4].nil? or val[4].empty?
- }
-
-row_contents:
- {
- result = nil
- }
- | HEAD_START HEAD_END row_contents
- {
- cell = TableCellAST.new(@ast_index, @ast_length)
- cell.type = :head
- result = [cell]
- result += val[2] unless val[2].nil? or val[2].empty?
- }
- | HEAD_START repeated_contents HEAD_END row_contents
- {
- cell = TableCellAST.new(@ast_index, @ast_length)
- cell.children = val[1] unless val[1].nil? or val[1].empty?
- cell.type = :head
- result = [cell]
- result += val[3] unless val[3].nil? or val[3].empty?
- }
- | CELL_START CELL_END row_contents
- {
- cell = TableCellAST.new(@ast_index, @ast_length)
- cell.type = :body
- result = [cell]
- result += val[2] unless val[2].nil? or val[2].empty?
- }
- | CELL_START repeated_contents CELL_END row_contents
- {
- if val[2] == 'attributes'
- result = []
- else
- cell = TableCellAST.new(@ast_index, @ast_length)
- cell.children = val[1] unless val[1].nil? or val[1].empty?
- cell.type = :body
- result = [cell]
- end
- result += val[3] unless val[3].nil? or val[3].empty?
- if val[2] == 'attributes' and val[3] and val[3].first.class == TableCellAST
- val[3].first.attributes = val[1]
- end
- result
- }
-
-
-element:
- TEXT
- { return [:None, val[0]] }
- | HLINE
- { return [:HLine, val[0]] }
- | CHAR_ENT
- { return [:CharacterEntity, val[0]] }
- | SIGNATURE_DATE
- { return [:SignatureDate, val[0]] }
- | SIGNATURE_NAME
- { return [:SignatureName, val[0]] }
- | SIGNATURE_FULL
- { return [:SignatureFull, val[0]] }
- ;
-
-formatted_element:
- BOLD_START BOLD_END
- {
- result = FormattedAST.new(@ast_index, @ast_length)
- result.formatting = :Bold
- result
- }
- | ITALIC_START ITALIC_END
- {
- result = FormattedAST.new(@ast_index, @ast_length)
- result.formatting = :Italic
- result
- }
- | BOLD_START repeated_contents BOLD_END
- {
- p = FormattedAST.new(@ast_index, @ast_length)
- p.formatting = :Bold
- p.children += val[1]
- result = p
- }
- | ITALIC_START repeated_contents ITALIC_END
- {
- p = FormattedAST.new(@ast_index, @ast_length)
- p.formatting = :Italic
- p.children += val[1]
- result = p
- }
- ;
-
-bulleted_list: UL_START list_item list_contents UL_END
- {
- list = ListAST.new(@ast_index, @ast_length)
- list.list_type = :Bulleted
- list.children << val[1]
- list.children += val[2]
- result = list
- }
- ;
-
-numbered_list: OL_START list_item list_contents OL_END
- {
- list = ListAST.new(@ast_index, @ast_length)
- list.list_type = :Numbered
- list.children << val[1]
- list.children += val[2]
- result = list
- }
- ;
-
-list_contents:
- { result = [] }
- list_item list_contents
- {
- result << val[1]
- result += val[2]
- }
- |
- { result = [] }
- ;
-
-list_item:
- LI_START LI_END
- {
- result = ListItemAST.new(@ast_index, @ast_length)
- }
- | LI_START repeated_contents LI_END
- {
- li = ListItemAST.new(@ast_index, @ast_length)
- li.children += val[1]
- result = li
- }
- ;
-
-dictionary_list:
- DL_START dictionary_term dictionary_contents DL_END
- {
- result = [val[1]]
- result += val[2]
- }
- | DL_START dictionary_contents DL_END
- {
- result = val[1]
- }
- ;
-
-dictionary_term:
- DT_START DT_END
- {
- result = ListTermAST.new(@ast_index, @ast_length)
- }
- | DT_START repeated_contents DT_END
- {
- term = ListTermAST.new(@ast_index, @ast_length)
- term.children += val[1]
- result = term
- }
-
-dictionary_contents:
- dictionary_definition dictionary_contents
- {
- result = [val[0]]
- result += val[1] if val[1]
- }
- |
- {
- result = []
- }
-
-dictionary_definition:
- DD_START DD_END
- {
- result = ListDefinitionAST.new(@ast_index, @ast_length)
- }
- | DD_START repeated_contents DD_END
- {
- term = ListDefinitionAST.new(@ast_index, @ast_length)
- term.children += val[1]
- result = term
- }
-
-preformatted: PRE_START repeated_contents PRE_END
- {
- p = PreformattedAST.new(@ast_index, @ast_length)
- p.children += val[1]
- result = p
- }
- | PREINDENT_START repeated_contents PREINDENT_END
- {
- p = PreformattedAST.new(@ast_index, @ast_length)
- p.indented = true
- p.children += val[1]
- result = p
- }
- ;
-
-section: SECTION_START repeated_contents SECTION_END
- { result = [val[1], val[0].length]
- s = SectionAST.new(@ast_index, @ast_length)
- s.children = val[1]
- s.level = val[0].length
- result = s
- }
- ;
-
-template: TEMPLATE_START TEXT template_parameters TEMPLATE_END
- {
- t = TemplateAST.new(@ast_index, @ast_length)
- t.template_name = val[1]
- t.children = val[2] unless val[2].nil? or val[2].empty?
- result = t
- }
- ;
-
-template_parameters:
- {
- result = nil
- }
- | INTLINKSEP TEXT template_parameters
- {
- p = TemplateParameterAST.new(@ast_index, @ast_length)
- p.parameter_value = val[1]
- result = [p]
- result += val[2] if val[2]
- }
- | INTLINKSEP template template_parameters
- {
- p = TemplateParameterAST.new(@ast_index, @ast_length)
- p.children << val[1]
- result = [p]
- result += val[2] if val[2]
- }
- ;
-
-end
-
----- header ----
-require 'mediacloth/mediawikiast'
-
----- inner ----
-
-attr_accessor :lexer
-
-def initialize
- @nodes = []
- @context = []
- @wiki_ast_length = 0
- super
-end
-
-#Tokenizes input string and parses it.
-def parse(input)
- @yydebug=true
- lexer.tokenize(input)
- do_parse
- return @nodes.last
-end
-
-#Asks the lexer to return the next token.
-def next_token
- token = @lexer.lex
- if token[0].to_s.upcase.include? "_START"
- @context << token[2..3]
- elsif token[0].to_s.upcase.include? "_END"
- @ast_index = @context.last[0]
- @ast_length = token[2] + token[3] - @context.last[0]
- @context.pop
- else
- @ast_index = token[2]
- @ast_length = token[3]
- end
-
- @wiki_ast_length += token[3]
-
- return token[0..1]
-end
diff --git a/test/racc/assets/mof.y b/test/racc/assets/mof.y
deleted file mode 100644
index 2e83c79b6f..0000000000
--- a/test/racc/assets/mof.y
+++ /dev/null
@@ -1,649 +0,0 @@
-# Distributed under the Ruby license
-# See http://www.ruby-lang.org/en/LICENSE.txt for the full license text
-# Copyright (c) 2010 Klaus Kämpf <kkaempf@suse.de>
-
-/*
- * According to appendix A of
- * http://www.dmtf.org/standards/cim/cim_spec_v22
- */
-
-class MOF::Parser
- prechigh
-/* nonassoc UMINUS */
- left '*' '/'
- left '+' '-'
- preclow
-
- token PRAGMA INCLUDE IDENTIFIER CLASS ASSOCIATION INDICATION
- AMENDED ENABLEOVERRIDE DISABLEOVERRIDE RESTRICTED TOSUBCLASS TOINSTANCE
- TRANSLATABLE QUALIFIER SCOPE SCHEMA PROPERTY REFERENCE
- METHOD PARAMETER FLAVOR INSTANCE
- AS REF ANY OF
- DT_VOID
- DT_UINT8 DT_SINT8 DT_UINT16 DT_SINT16 DT_UINT32 DT_SINT32
- DT_UINT64 DT_SINT64 DT_REAL32 DT_REAL64 DT_CHAR16 DT_STR
- DT_BOOLEAN DT_DATETIME
- positiveDecimalValue
- stringValue
- realValue
- charValue
- booleanValue
- nullValue
- binaryValue
- octalValue
- decimalValue
- hexValue
-
-rule
-
- /* Returns a Hash of filename and MofResult */
- mofSpecification
- : /* empty */
- { result = Hash.new }
- | mofProduction
- { result = { @name => @result } }
- | mofSpecification mofProduction
- { result = val[0]
- result[@name] = @result
- }
- ;
-
- mofProduction
- : compilerDirective
- | classDeclaration
- { #puts "Class '#{val[0].name}'"
- @result.classes << val[0]
- }
- | qualifierDeclaration
- { @result.qualifiers << val[0]
- @qualifiers[val[0].name.downcase] = val[0]
- }
- | instanceDeclaration
- { @result.instances << val[0] }
- ;
-
-/***
- * compilerDirective
- *
- */
-
- compilerDirective
- : "#" PRAGMA INCLUDE pragmaParameters_opt
- { raise MOF::Helper::Error.new(@name,@lineno,@line,"Missing filename after '#pragma include'") unless val[3]
- open val[3], :pragma
- }
- | "#" PRAGMA pragmaName pragmaParameters_opt
- | "#" INCLUDE pragmaParameters_opt
- { raise StyleError.new(@name,@lineno,@line,"Use '#pragma include' instead of '#include'") unless @style == :wmi
- raise MOF::Helper::Error.new(@name,@lineno,@line,"Missing filename after '#include'") unless val[2]
- open val[2], :pragma
- }
- ;
-
- pragmaName
- : IDENTIFIER
- ;
-
- pragmaParameters_opt
- : /* empty */
- { raise StyleError.new(@name,@lineno,@line,"#pragma parameter missing") unless @style == :wmi }
- | "(" pragmaParameterValues ")"
- { result = val[1] }
- ;
-
- pragmaParameterValues
- : pragmaParameterValue
- | pragmaParameterValues "," pragmaParameterValue
- ;
-
- pragmaParameterValue
- : string
- | integerValue
- { raise StyleError.new(@name,@lineno,@line,"#pragma parameter missing") unless @style == :wmi }
- | IDENTIFIER
- ;
-
-/***
- * classDeclaration
- *
- */
-
- classDeclaration
- : qualifierList_opt CLASS className alias_opt superClass_opt "{" classFeatures "}" ";"
- { qualifiers = val[0]
- features = val[6]
- # FIXME: features must not include references
- result = CIM::Class.new(val[2],qualifiers,val[3],val[4],features)
- }
- ;
-
- classFeatures
- : /* empty */
- { result = [] }
- | classFeatures classFeature
- { result = val[0] << val[1] }
- ;
-
- classFeature
- : propertyDeclaration
- | methodDeclaration
- | referenceDeclaration /* must have association qualifier */
- ;
-
-
- qualifierList_opt
- : /* empty */
- | qualifierList
- { result = CIM::QualifierSet.new val[0] }
- ;
-
- qualifierList
- : "[" qualifier qualifiers "]"
- { result = val[2]
- result.unshift val[1] if val[1] }
- ;
-
- qualifiers
- : /* empty */
- { result = [] }
- | qualifiers "," qualifier
- { result = val[0]
- result << val[2] if val[2]
- }
- ;
-
- qualifier
- : qualifierName qualifierParameter_opt flavor_opt
- { # Get qualifier decl
- qualifier = case val[0]
- when CIM::Qualifier then val[0].definition
- when CIM::QualifierDeclaration then val[0]
- when String then @qualifiers[val[0].downcase]
- else
- nil
- end
- raise MOF::Helper::Error.new(@name,@lineno,@line,"'#{val[0]}' is not a valid qualifier") unless qualifier
- value = val[1]
- raise MOF::Helper::Error.new(@name,@lineno,@line,"#{value.inspect} does not match qualifier type '#{qualifier.type}'") unless qualifier.type.matches?(value)||@style == :wmi
- # Don't propagate a boolean 'false'
- if qualifier.type == :boolean && value == false
- result = nil
- else
- result = CIM::Qualifier.new(qualifier,value,val[2])
- end
- }
- ;
-
- flavor_opt
- : /* empty */
- | ":" flavor
- { result = CIM::QualifierFlavors.new val[1] }
- ;
-
- qualifierParameter_opt
- : /* empty */
- | qualifierParameter
- ;
-
- qualifierParameter
- : "(" constantValue ")"
- { result = val[1] }
- | arrayInitializer
- ;
-
- /* CIM::Flavors */
- flavor
- : AMENDED | ENABLEOVERRIDE | DISABLEOVERRIDE | RESTRICTED | TOSUBCLASS | TRANSLATABLE | TOINSTANCE
- { case val[0].to_sym
- when :amended, :toinstance
- raise StyleError.new(@name,@lineno,@line,"'#{val[0]}' is not a valid flavor") unless @style == :wmi
- end
- }
- ;
-
- alias_opt
- : /* empty */
- | alias
- ;
-
- superClass_opt
- : /* empty */
- | superClass
- ;
-
- className
- : IDENTIFIER /* must be <schema>_<classname> in CIM v2.x */
- { raise ParseError.new("Class name must be prefixed by '<schema>_'") unless val[0].include?("_") || @style == :wmi }
- ;
-
- alias
- : AS aliasIdentifier
- { result = val[1] }
- ;
-
- aliasIdentifier
- : "$" IDENTIFIER /* NO whitespace ! */
- { result = val[1] }
- ;
-
- superClass
- : ":" className
- { result = val[1] }
- ;
-
-
- propertyDeclaration
- : qualifierList_opt dataType propertyName array_opt defaultValue_opt ";"
- { if val[3]
- type = CIM::Array.new val[3],val[1]
- else
- type = val[1]
- end
- result = CIM::Property.new(type,val[2],val[0],val[4])
- }
- ;
-
- referenceDeclaration
- : qualifierList_opt objectRef referenceName array_opt defaultValue_opt ";"
- { if val[4]
- raise StyleError.new(@name,@lineno,@line,"Array not allowed in reference declaration") unless @style == :wmi
- end
- result = CIM::Reference.new(val[1],val[2],val[0],val[4]) }
- ;
-
- methodDeclaration
- : qualifierList_opt dataType methodName "(" parameterList_opt ")" ";"
- { result = CIM::Method.new(val[1],val[2],val[0],val[4]) }
- ;
-
- propertyName
- : IDENTIFIER
- | PROPERTY
- { # tmplprov.mof has 'string Property;'
- raise StyleError.new(@name,@lineno,@line,"Invalid keyword '#{val[0]}' used for property name") unless @style == :wmi
- }
- ;
-
- referenceName
- : IDENTIFIER
- | INDICATION
- { result = "Indication" }
- ;
-
- methodName
- : IDENTIFIER
- ;
-
- dataType
- : DT_UINT8
- | DT_SINT8
- | DT_UINT16
- | DT_SINT16
- | DT_UINT32
- | DT_SINT32
- | DT_UINT64
- | DT_SINT64
- | DT_REAL32
- | DT_REAL64
- | DT_CHAR16
- | DT_STR
- | DT_BOOLEAN
- | DT_DATETIME
- | DT_VOID
- { raise StyleError.new(@name,@lineno,@line,"'void' is not a valid datatype") unless @style == :wmi }
- ;
-
- objectRef
- : className
- { # WMI uses class names as data types (without REF ?!)
- raise StyleError.new(@name,@lineno,@line,"Expected 'ref' keyword after classname '#{val[0]}'") unless @style == :wmi
- result = CIM::ReferenceType.new val[0]
- }
-
- | className REF
- { result = CIM::ReferenceType.new val[0] }
- ;
-
- parameterList_opt
- : /* empty */
- | parameterList
- ;
-
- parameterList
- : parameter parameters
- { result = val[1].unshift val[0] }
- ;
-
- parameters
- : /* empty */
- { result = [] }
- | parameters "," parameter
- { result = val[0] << val[2] }
- ;
-
- parameter
- : qualifierList_opt typespec parameterName array_opt parameterValue_opt
- { if val[3]
- type = CIM::Array.new val[3], val[1]
- else
- type = val[1]
- end
- result = CIM::Property.new(type,val[2],val[0])
- }
- ;
-
- typespec
- : dataType
- | objectRef
- ;
-
- parameterName
- : IDENTIFIER
- ;
-
- array_opt
- : /* empty */
- | array
- ;
-
- parameterValue_opt
- : /* empty */
- | defaultValue
- { raise "Default parameter value not allowed in syntax style '{@style}'" unless @style == :wmi }
- ;
-
- array
- : "[" positiveDecimalValue_opt "]"
- { result = val[1] }
- ;
-
- positiveDecimalValue_opt
- : /* empty */
- { result = -1 }
- | positiveDecimalValue
- ;
-
- defaultValue_opt
- : /* empty */
- | defaultValue
- ;
-
- defaultValue
- : "=" initializer
- { result = val[1] }
- ;
-
- initializer
- : constantValue
- | arrayInitializer
- | referenceInitializer
- ;
-
- arrayInitializer
- : "{" constantValues "}"
- { result = val[1] }
- ;
-
- constantValues
- : /* empty */
- | constantValue
- { result = [ val[0] ] }
- | constantValues "," constantValue
- { result = val[0] << val[2] }
- ;
-
- constantValue
- : integerValue
- | realValue
- | charValue
- | string
- | booleanValue
- | nullValue
- | instance
- { raise "Instance as property value not allowed in syntax style '{@style}'" unless @style == :wmi }
- ;
-
- integerValue
- : binaryValue
- | octalValue
- | decimalValue
- | positiveDecimalValue
- | hexValue
- ;
-
- string
- : stringValue
- | string stringValue
- { result = val[0] + val[1] }
- ;
-
- referenceInitializer
- : objectHandle
- | aliasIdentifier
- ;
-
- objectHandle
- : namespace_opt modelPath
- ;
-
- namespace_opt
- : /* empty */
- | namespaceHandle ":"
- ;
-
- namespaceHandle
- : IDENTIFIER
- ;
-
- /*
- * Note
- : structure depends on type of namespace
- */
-
- modelPath
- : className "." keyValuePairList
- ;
-
- keyValuePairList
- : keyValuePair keyValuePairs
- ;
-
- keyValuePairs
- : /* empty */
- | keyValuePairs "," keyValuePair
- ;
-
- keyValuePair
- : keyname "=" initializer
- ;
-
- keyname
- : propertyName | referenceName
- ;
-
-/***
- * qualifierDeclaration
- *
- */
-
- qualifierDeclaration
- /* 0 1 2 3 4 */
- : QUALIFIER qualifierName qualifierType scope defaultFlavor_opt ";"
- { result = CIM::QualifierDeclaration.new( val[1], val[2][0], val[2][1], val[3], val[4]) }
- ;
-
- defaultFlavor_opt
- : /* empty */
- | defaultFlavor
- ;
-
- qualifierName
- : IDENTIFIER
- | ASSOCIATION /* meta qualifier */
- | INDICATION /* meta qualifier */
- | REFERENCE /* Added in DSP0004 2.7.0 */
- | SCHEMA
- ;
-
- /* [type, value] */
- qualifierType
- : ":" dataType array_opt defaultValue_opt
- { type = val[2].nil? ? val[1] : CIM::Array.new(val[2],val[1])
- result = [ type, val[3] ]
- }
- ;
-
- scope
- : "," SCOPE "(" metaElements ")"
- { result = CIM::QualifierScopes.new(val[3]) }
- ;
-
- metaElements
- : metaElement
- { result = [ val[0] ] }
- | metaElements "," metaElement
- { result = val[0] << val[2] }
- ;
-
- metaElement
- : SCHEMA
- | CLASS
- | ASSOCIATION
- | INDICATION
- | QUALIFIER
- | PROPERTY
- | REFERENCE
- | METHOD
- | PARAMETER
- | ANY
- ;
-
- defaultFlavor
- : "," FLAVOR "(" flavors ")"
- { result = CIM::QualifierFlavors.new val[3] }
- ;
-
- flavors
- : flavor
- { result = [ val[0] ] }
- | flavors "," flavor
- { result = val[0] << val[2] }
- ;
-
-/***
- * instanceDeclaration
- *
- */
-
- instanceDeclaration
- : instance ";"
- ;
-
- instance
- : qualifierList_opt INSTANCE OF className alias_opt "{" valueInitializers "}"
- ;
-
- valueInitializers
- : valueInitializer
- | valueInitializers valueInitializer
- ;
-
- valueInitializer
- : qualifierList_opt keyname "=" initializer ";"
- | qualifierList_opt keyname ";"
- { raise "Instance property '#{val[1]} must have a value" unless @style == :wmi }
- ;
-
-end # class Parser
-
----- header ----
-
-# parser.rb - generated by racc
-
-require 'strscan'
-require 'rubygems'
-require 'cim'
-require File.join(__dir__, 'result')
-require File.join(__dir__, 'scanner')
-require File.join(__dir__, 'case')
-
----- inner ----
-
-#
-# Initialize MOF::Parser
-# MOF::Parser.new options = {}
-#
-# options -> Hash of options
-# :debug -> boolean
-# :includes -> array of include dirs
-# :style -> :cim or :wmi
-#
-def initialize options = {}
- @yydebug = options[:debug]
- @includes = options[:includes] || []
- @quiet = options[:quiet]
- @style = options[:style] || :cim # default to style CIM v2.2 syntax
-
- @lineno = 1
- @file = nil
- @iconv = nil
- @eol = "\n"
- @fname = nil
- @fstack = []
- @in_comment = false
- @seen_files = []
- @qualifiers = {}
-end
-
-#
-# Make options hash from argv
-#
-# returns [ files, options ]
-#
-
- def self.argv_handler name, argv
- files = []
- options = { :namespace => "" }
- while argv.size > 0
- case opt = argv.shift
- when "-h"
- $stderr.puts "Ruby MOF compiler"
- $stderr.puts "#{name} [-h] [-d] [-I <dir>] [<moffiles>]"
- $stderr.puts "Compiles <moffile>"
- $stderr.puts "\t-d debug"
- $stderr.puts "\t-h this help"
- $stderr.puts "\t-I <dir> include dir"
- $stderr.puts "\t-f force"
- $stderr.puts "\t-n <namespace>"
- $stderr.puts "\t-o <output>"
- $stderr.puts "\t-s <style> syntax style (wmi,cim)"
- $stderr.puts "\t-q quiet"
- $stderr.puts "\t<moffiles> file(s) to read (else use $stdin)"
- exit 0
- when "-f" then options[:force] = true
- when "-s" then options[:style] = argv.shift.to_sym
- when "-d" then options[:debug] = true
- when "-q" then options[:quiet] = true
- when "-I"
- options[:includes] ||= []
- dirname = argv.shift
- unless File.directory?(dirname)
- files << dirname
- dirname = File.dirname(dirname)
- end
- options[:includes] << Pathname.new(dirname)
- when "-n" then options[:namespace] = argv.shift
- when "-o" then options[:output] = argv.shift
- when /^-.+/
- $stderr.puts "Undefined option #{opt}"
- else
- files << opt
- end
- end
- [ files, options ]
- end
-
-include Helper
-include Scanner
-
----- footer ----
diff --git a/test/racc/assets/namae.y b/test/racc/assets/namae.y
deleted file mode 100644
index 0378345fef..0000000000
--- a/test/racc/assets/namae.y
+++ /dev/null
@@ -1,302 +0,0 @@
-# -*- ruby -*-
-# vi: set ft=ruby :
-
-# Copyright (C) 2012 President and Fellows of Harvard College
-# Copyright (C) 2013-2014 Sylvester Keil
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR
-# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-# EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
-# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
-# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
-# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-# The views and conclusions contained in the software and documentation are
-# those of the authors and should not be interpreted as representing official
-# policies, either expressed or implied, of the copyright holder.
-
-class Namae::Parser
-
-token COMMA UWORD LWORD PWORD NICK AND APPELLATION TITLE SUFFIX
-
-expect 0
-
-rule
-
- names : { result = [] }
- | name { result = [val[0]] }
- | names AND name { result = val[0] << val[2] }
-
- name : word { result = Name.new(:given => val[0]) }
- | display_order
- | honorific word { result = val[0].merge(:family => val[1]) }
- | honorific display_order { result = val[1].merge(val[0]) }
- | sort_order
-
- honorific : APPELLATION { result = Name.new(:appellation => val[0]) }
- | TITLE { result = Name.new(:title => val[0]) }
-
- display_order : u_words word opt_suffices opt_titles
- {
- result = Name.new(:given => val[0], :family => val[1],
- :suffix => val[2], :title => val[3])
- }
- | u_words NICK last opt_suffices opt_titles
- {
- result = Name.new(:given => val[0], :nick => val[1],
- :family => val[2], :suffix => val[3], :title => val[4])
- }
- | u_words NICK von last opt_suffices opt_titles
- {
- result = Name.new(:given => val[0], :nick => val[1],
- :particle => val[2], :family => val[3],
- :suffix => val[4], :title => val[5])
- }
- | u_words von last
- {
- result = Name.new(:given => val[0], :particle => val[1],
- :family => val[2])
- }
- | von last
- {
- result = Name.new(:particle => val[0], :family => val[1])
- }
-
- sort_order : last COMMA first
- {
- result = Name.new({ :family => val[0], :suffix => val[2][0],
- :given => val[2][1] }, !!val[2][0])
- }
- | von last COMMA first
- {
- result = Name.new({ :particle => val[0], :family => val[1],
- :suffix => val[3][0], :given => val[3][1] }, !!val[3][0])
- }
- | u_words von last COMMA first
- {
- result = Name.new({ :particle => val[0,2].join(' '), :family => val[2],
- :suffix => val[4][0], :given => val[4][1] }, !!val[4][0])
- }
- ;
-
- von : LWORD
- | von LWORD { result = val.join(' ') }
- | von u_words LWORD { result = val.join(' ') }
-
- last : LWORD | u_words
-
- first : opt_words { result = [nil,val[0]] }
- | words opt_comma suffices { result = [val[2],val[0]] }
- | suffices { result = [val[0],nil] }
- | suffices COMMA words { result = [val[0],val[2]] }
-
- u_words : u_word
- | u_words u_word { result = val.join(' ') }
-
- u_word : UWORD | PWORD
-
- words : word
- | words word { result = val.join(' ') }
-
- opt_comma : /* empty */ | COMMA
- opt_words : /* empty */ | words
-
- word : LWORD | UWORD | PWORD
-
- opt_suffices : /* empty */ | suffices
-
- suffices : SUFFIX
- | suffices SUFFIX { result = val.join(' ') }
-
- opt_titles : /* empty */ | titles
-
- titles : TITLE
- | titles TITLE { result = val.join(' ') }
-
----- header
-require 'singleton'
-require 'strscan'
-
----- inner
-
- include Singleton
-
- attr_reader :options, :input
-
- def initialize
- @input, @options = StringScanner.new(''), {
- :debug => false,
- :prefer_comma_as_separator => false,
- :comma => ',',
- :stops => ',;',
- :separator => /\s*(\band\b|\&|;)\s*/i,
- :title => /\s*\b(sir|lord|count(ess)?|(gen|adm|col|maj|capt|cmdr|lt|sgt|cpl|pvt|prof|dr|md|ph\.?d)\.?)(\s+|$)/i,
- :suffix => /\s*\b(JR|Jr|jr|SR|Sr|sr|[IVX]{2,})(\.|\b)/,
- :appellation => /\s*\b((mrs?|ms|fr|hr)\.?|miss|herr|frau)(\s+|$)/i
- }
- end
-
- def debug?
- options[:debug] || ENV['DEBUG']
- end
-
- def separator
- options[:separator]
- end
-
- def comma
- options[:comma]
- end
-
- def stops
- options[:stops]
- end
-
- def title
- options[:title]
- end
-
- def suffix
- options[:suffix]
- end
-
- def appellation
- options[:appellation]
- end
-
- def prefer_comma_as_separator?
- options[:prefer_comma_as_separator]
- end
-
- def parse(input)
- parse!(input)
- rescue => e
- warn e.message if debug?
- []
- end
-
- def parse!(string)
- input.string = normalize(string)
- reset
- do_parse
- end
-
- def normalize(string)
- string = string.strip
- string
- end
-
- def reset
- @commas, @words, @initials, @suffices, @yydebug = 0, 0, 0, 0, debug?
- self
- end
-
- private
-
- def stack
- @vstack || @racc_vstack || []
- end
-
- def last_token
- stack[-1]
- end
-
- def consume_separator
- return next_token if seen_separator?
- @commas, @words, @initials, @suffices = 0, 0, 0, 0
- [:AND, :AND]
- end
-
- def consume_comma
- @commas += 1
- [:COMMA, :COMMA]
- end
-
- def consume_word(type, word)
- @words += 1
-
- case type
- when :UWORD
- @initials += 1 if word =~ /^[[:upper:]]+\b/
- when :SUFFIX
- @suffices += 1
- end
-
- [type, word]
- end
-
- def seen_separator?
- !stack.empty? && last_token == :AND
- end
-
- def suffix?
- !@suffices.zero? || will_see_suffix?
- end
-
- def will_see_suffix?
- input.peek(8).to_s.strip.split(/\s+/)[0] =~ suffix
- end
-
- def will_see_initial?
- input.peek(6).to_s.strip.split(/\s+/)[0] =~ /^[[:upper:]]+\b/
- end
-
- def seen_full_name?
- prefer_comma_as_separator? && @words > 1 &&
- (@initials > 0 || !will_see_initial?) && !will_see_suffix?
- end
-
- def next_token
- case
- when input.nil?, input.eos?
- nil
- when input.scan(separator)
- consume_separator
- when input.scan(/\s*#{comma}\s*/)
- if @commas.zero? && !seen_full_name? || @commas == 1 && suffix?
- consume_comma
- else
- consume_separator
- end
- when input.scan(/\s+/)
- next_token
- when input.scan(title)
- consume_word(:TITLE, input.matched.strip)
- when input.scan(suffix)
- consume_word(:SUFFIX, input.matched.strip)
- when input.scan(appellation)
- [:APPELLATION, input.matched.strip]
- when input.scan(/((\\\w+)?\{[^\}]*\})*[[:upper:]][^\s#{stops}]*/)
- consume_word(:UWORD, input.matched)
- when input.scan(/((\\\w+)?\{[^\}]*\})*[[:lower:]][^\s#{stops}]*/)
- consume_word(:LWORD, input.matched)
- when input.scan(/(\\\w+)?\{[^\}]*\}[^\s#{stops}]*/)
- consume_word(:PWORD, input.matched)
- when input.scan(/('[^'\n]+')|("[^"\n]+")/)
- consume_word(:NICK, input.matched[1...-1])
- else
- raise ArgumentError,
- "Failed to parse name #{input.string.inspect}: unmatched data at offset #{input.pos}"
- end
- end
-
- def on_error(tid, value, stack)
- raise ArgumentError,
- "Failed to parse name: unexpected '#{value}' at #{stack.inspect}"
- end
-
-# -*- racc -*-
diff --git a/test/racc/assets/nasl.y b/test/racc/assets/nasl.y
deleted file mode 100644
index c7b8e46551..0000000000
--- a/test/racc/assets/nasl.y
+++ /dev/null
@@ -1,626 +0,0 @@
-################################################################################
-# Copyright (c) 2011-2014, Tenable Network Security
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions of source code must retain the above copyright notice, this
-# list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-################################################################################
-
-class Nasl::Grammar
-
-preclow
- right ASS_EQ ADD_EQ SUB_EQ MUL_EQ DIV_EQ MOD_EQ SLL_EQ SRA_EQ SRL_EQ
- left OR
- left AND
- left CMP_LT CMP_GT CMP_EQ CMP_NE CMP_GE CMP_LE SUBSTR_EQ SUBSTR_NE REGEX_EQ REGEX_NE
- left BIT_OR
- left BIT_XOR
- left AMPERSAND
- left BIT_SRA BIT_SRL BIT_SLL
- left ADD SUB
- left MUL DIV MOD
- right NOT
- right UMINUS BIT_NOT
- right EXP
- right INCR DECR
-prechigh
-
-# Tell the parser generator that we don't wish to use the result variable in the
-# action section of rules. Instead, the result of the rule will be the value of
-# evaluating the action block.
-options no_result_var
-
-# Tell the parser generator that we expect one shift/reduce conflict due to the
-# well-known dangling else problem. We could make the grammar solve this
-# problem, but this is how the NASL YACC file solves it, so we'll follow suit.
-expect 1
-
-rule
- ##############################################################################
- # Aggregate Statements
- ##############################################################################
-
- start : roots
- { val[0] }
- | /* Blank */
- { [] }
- ;
-
- roots : root roots
- { [val[0]] + val[1] }
- | root
- { [val[0]] }
- ;
-
- root : COMMENT export
- { c(*val) }
- | export
- { val[0] }
- | COMMENT function
- { c(*val) }
- | function
- { val[0] }
- | statement
- { val[0] }
- ;
-
- statement : simple
- { val[0] }
- | compound
- { val[0] }
- ;
-
- ##############################################################################
- # Root Statements
- ##############################################################################
-
- export : EXPORT function
- { n(:Export, *val) }
- ;
-
- function : FUNCTION ident LPAREN params RPAREN block
- { n(:Function, *val) }
- | FUNCTION ident LPAREN RPAREN block
- { n(:Function, *val) }
- ;
-
- simple : assign
- { val[0] }
- | break
- { val[0] }
- | call
- { val[0] }
- | continue
- { val[0] }
- | decr
- { val[0] }
- | empty
- { val[0] }
- | COMMENT global
- { c(*val) }
- | global
- { val[0] }
- | import
- { val[0] }
- | include
- { val[0] }
- | incr
- { val[0] }
- | local
- { val[0] }
- | rep
- { val[0] }
- | return
- { val[0] }
- ;
-
- compound : block
- { val[0] }
- | for
- { val[0] }
- | foreach
- { val[0] }
- | if
- { val[0] }
- | repeat
- { val[0] }
- | while
- { val[0] }
- ;
-
- ##############################################################################
- # Simple Statements
- ##############################################################################
-
- assign : assign_exp SEMICOLON
- { val[0] }
- ;
-
- break : BREAK SEMICOLON
- { n(:Break, *val) }
- ;
-
- call : call_exp SEMICOLON
- { val[0] }
- ;
-
- continue : CONTINUE SEMICOLON
- { n(:Continue, *val) }
- ;
-
- decr : decr_exp SEMICOLON
- { val[0] }
- ;
-
- empty : SEMICOLON
- { n(:Empty, *val) }
- ;
-
- global : GLOBAL var_decls SEMICOLON
- { n(:Global, *val) }
- ;
-
- incr : incr_exp SEMICOLON
- { val[0] }
- ;
-
- import : IMPORT LPAREN string RPAREN SEMICOLON
- { n(:Import, *val) }
- ;
-
- include : INCLUDE LPAREN string RPAREN SEMICOLON
- { n(:Include, *val) }
- ;
-
- local : LOCAL var_decls SEMICOLON
- { n(:Local, *val) }
- ;
-
- rep : call_exp REP expr SEMICOLON
- { n(:Repetition, *val[0..-1]) }
- ;
-
- return : RETURN expr SEMICOLON
- { n(:Return, *val) }
- | RETURN ref SEMICOLON
- { n(:Return, *val) }
- | RETURN SEMICOLON
- { n(:Return, *val) }
- ;
-
- ##############################################################################
- # Compound Statements
- ##############################################################################
-
- block : LBRACE statements RBRACE
- { n(:Block, *val) }
- | LBRACE RBRACE
- { n(:Block, *val) }
- ;
-
- for : FOR LPAREN field SEMICOLON expr SEMICOLON field RPAREN statement
- { n(:For, *val) }
- ;
-
- foreach : FOREACH ident LPAREN expr RPAREN statement
- { n(:Foreach, val[0], val[1], val[3], val[5]) }
- | FOREACH LPAREN ident IN expr RPAREN statement
- { n(:Foreach, val[0], val[2], val[4], val[6]) }
- ;
-
- if : IF LPAREN expr RPAREN statement
- { n(:If, *val) }
- | IF LPAREN expr RPAREN statement ELSE statement
- { n(:If, *val) }
- ;
-
- repeat : REPEAT statement UNTIL expr SEMICOLON
- { n(:Repeat, *val) }
- ;
-
- while : WHILE LPAREN expr RPAREN statement
- { n(:While, *val) }
- ;
-
- ##############################################################################
- # Expressions
- ##############################################################################
-
- assign_exp : lval ASS_EQ expr
- { n(:Assignment, *val) }
- | lval ASS_EQ ref
- { n(:Assignment, *val) }
- | lval ADD_EQ expr
- { n(:Assignment, *val) }
- | lval SUB_EQ expr
- { n(:Assignment, *val) }
- | lval MUL_EQ expr
- { n(:Assignment, *val) }
- | lval DIV_EQ expr
- { n(:Assignment, *val) }
- | lval MOD_EQ expr
- { n(:Assignment, *val) }
- | lval SRL_EQ expr
- { n(:Assignment, *val) }
- | lval SRA_EQ expr
- { n(:Assignment, *val) }
- | lval SLL_EQ expr
- { n(:Assignment, *val) }
- ;
-
- call_exp : lval LPAREN args RPAREN
- { n(:Call, *val) }
- | lval LPAREN RPAREN
- { n(:Call, *val) }
- ;
-
- decr_exp : DECR lval
- { n(:Decrement, val[0]) }
- | lval DECR
- { n(:Decrement, val[0]) }
- ;
-
- incr_exp : INCR lval
- { n(:Increment, val[0]) }
- | lval INCR
- { n(:Increment, val[0]) }
- ;
-
- expr : LPAREN expr RPAREN
- { n(:Expression, *val) }
- | expr AND expr
- { n(:Expression, *val) }
- | NOT expr
- { n(:Expression, *val) }
- | expr OR expr
- { n(:Expression, *val) }
- | expr ADD expr
- { n(:Expression, *val) }
- | expr SUB expr
- { n(:Expression, *val) }
- | SUB expr =UMINUS
- { n(:Expression, *val) }
- | BIT_NOT expr
- { n(:Expression, *val) }
- | expr MUL expr
- { n(:Expression, *val) }
- | expr EXP expr
- { n(:Expression, *val) }
- | expr DIV expr
- { n(:Expression, *val) }
- | expr MOD expr
- { n(:Expression, *val) }
- | expr AMPERSAND expr
- { n(:Expression, *val) }
- | expr BIT_XOR expr
- { n(:Expression, *val) }
- | expr BIT_OR expr
- { n(:Expression, *val) }
- | expr BIT_SRA expr
- { n(:Expression, *val) }
- | expr BIT_SRL expr
- { n(:Expression, *val) }
- | expr BIT_SLL expr
- { n(:Expression, *val) }
- | incr_exp
- { val[0] }
- | decr_exp
- { val[0] }
- | expr SUBSTR_EQ expr
- { n(:Expression, *val) }
- | expr SUBSTR_NE expr
- { n(:Expression, *val) }
- | expr REGEX_EQ expr
- { n(:Expression, *val) }
- | expr REGEX_NE expr
- { n(:Expression, *val) }
- | expr CMP_LT expr
- { n(:Expression, *val) }
- | expr CMP_GT expr
- { n(:Expression, *val) }
- | expr CMP_EQ expr
- { n(:Expression, *val) }
- | expr CMP_NE expr
- { n(:Expression, *val) }
- | expr CMP_GE expr
- { n(:Expression, *val) }
- | expr CMP_LE expr
- { n(:Expression, *val) }
- | assign_exp
- { val[0] }
- | string
- { val[0] }
- | call_exp
- { val[0] }
- | lval
- { val[0] }
- | ip
- { val[0] }
- | int
- { val[0] }
- | undef
- { val[0] }
- | list_expr
- { val[0] }
- | array_expr
- { val[0] }
- ;
-
- ##############################################################################
- # Named Components
- ##############################################################################
-
- arg : ident COLON expr
- { n(:Argument, *val) }
- | ident COLON ref
- { n(:Argument, *val) }
- | expr
- { n(:Argument, *val) }
- | ref
- { n(:Argument, *val) }
- ;
-
- kv_pair : string COLON expr
- { n(:KeyValuePair, *val) }
- | int COLON expr
- { n(:KeyValuePair, *val) }
- | ident COLON expr
- { n(:KeyValuePair, *val) }
- | string COLON ref
- { n(:KeyValuePair, *val) }
- | int COLON ref
- { n(:KeyValuePair, *val) }
- | ident COLON ref
- { n(:KeyValuePair, *val) }
- ;
-
- kv_pairs : kv_pair COMMA kv_pairs
- { [val[0]] + val[2] }
- | kv_pair COMMA
- { [val[0]] }
- | kv_pair
- { [val[0]] }
- ;
-
- lval : ident indexes
- { n(:Lvalue, *val) }
- | ident
- { n(:Lvalue, *val) }
- ;
-
- ref : AT_SIGN ident
- { n(:Reference, val[1]) }
- ;
-
- ##############################################################################
- # Anonymous Components
- ##############################################################################
-
- args : arg COMMA args
- { [val[0]] + val[2] }
- | arg
- { [val[0]] }
- ;
-
- array_expr : LBRACE kv_pairs RBRACE
- { n(:Array, *val) }
- | LBRACE RBRACE
- { n(:Array, *val) }
- ;
-
- field : assign_exp
- { val[0] }
- | call_exp
- { val[0] }
- | decr_exp
- { val[0] }
- | incr_exp
- { val[0] }
- | /* Blank */
- { nil }
- ;
-
- index : LBRACK expr RBRACK
- { val[1] }
- | PERIOD ident
- { val[1] }
- ;
-
- indexes : index indexes
- { [val[0]] + val[1] }
- | index
- { [val[0]] }
- ;
-
- list_elem : expr
- { val[0] }
- | ref
- { val[0] }
- ;
-
- list_elems : list_elem COMMA list_elems
- { [val[0]] + val[2] }
- | list_elem
- { [val[0]] }
- ;
-
- list_expr : LBRACK list_elems RBRACK
- { n(:List, *val) }
- | LBRACK RBRACK
- { n(:List, *val) }
- ;
-
- param : AMPERSAND ident
- { n(:Parameter, val[1], 'reference') }
- | ident
- { n(:Parameter, val[0], 'value') }
- ;
-
- params : param COMMA params
- { [val[0]] + val[2] }
- | param
- { [val[0]] }
- ;
-
- statements : statement statements
- { [val[0]] + val[1] }
- | statement
- { [val[0]] }
- ;
-
- var_decl : ident ASS_EQ expr
- { n(:Assignment, *val) }
- | ident ASS_EQ ref
- { n(:Assignment, *val) }
- | ident
- { val[0] }
- ;
-
- var_decls : var_decl COMMA var_decls
- { [val[0]] + val[2] }
- | var_decl
- { [val[0]] }
- ;
-
- ##############################################################################
- # Literals
- ##############################################################################
-
- ident : IDENT
- { n(:Identifier, *val) }
- | REP
- { n(:Identifier, *val) }
- | IN
- { n(:Identifier, *val) }
- ;
-
- int : INT_DEC
- { n(:Integer, *val) }
- | INT_HEX
- { n(:Integer, *val) }
- | INT_OCT
- { n(:Integer, *val) }
- | FALSE
- { n(:Integer, *val) }
- | TRUE
- { n(:Integer, *val) }
- ;
-
- ip : int PERIOD int PERIOD int PERIOD int
- { n(:Ip, *val) }
-
- string : DATA
- { n(:String, *val) }
- | STRING
- { n(:String, *val) }
- ;
-
- undef : UNDEF
- { n(:Undefined, *val) }
- ;
-end
-
----- header ----
-
-require 'nasl/parser/tree'
-
-require 'nasl/parser/argument'
-require 'nasl/parser/array'
-require 'nasl/parser/assigment'
-require 'nasl/parser/block'
-require 'nasl/parser/break'
-require 'nasl/parser/call'
-require 'nasl/parser/comment'
-require 'nasl/parser/continue'
-require 'nasl/parser/decrement'
-require 'nasl/parser/empty'
-require 'nasl/parser/export'
-require 'nasl/parser/expression'
-require 'nasl/parser/for'
-require 'nasl/parser/foreach'
-require 'nasl/parser/function'
-require 'nasl/parser/global'
-require 'nasl/parser/identifier'
-require 'nasl/parser/if'
-require 'nasl/parser/import'
-require 'nasl/parser/include'
-require 'nasl/parser/increment'
-require 'nasl/parser/integer'
-require 'nasl/parser/ip'
-require 'nasl/parser/key_value_pair'
-require 'nasl/parser/list'
-require 'nasl/parser/local'
-require 'nasl/parser/lvalue'
-require 'nasl/parser/parameter'
-require 'nasl/parser/reference'
-require 'nasl/parser/repeat'
-require 'nasl/parser/repetition'
-require 'nasl/parser/return'
-require 'nasl/parser/string'
-require 'nasl/parser/undefined'
-require 'nasl/parser/while'
-
----- inner ----
-
-def n(cls, *args)
- begin
- Nasl.const_get(cls).new(@tree, *args)
- rescue
- puts "An exception occurred during the creation of a #{cls} instance."
- puts
- puts "The arguments passed to the constructor were:"
- puts args
- puts
- puts @tok.last.context
- puts
- raise
- end
-end
-
-def c(*args)
- n(:Comment, *args)
- args[1]
-end
-
-def on_error(type, value, stack)
- raise ParseException, "The language's grammar does not permit #{value.name} to appear here", value.context
-end
-
-def next_token
- @tok = @tkz.get_token
-
- if @first && @tok.first == :COMMENT
- n(:Comment, @tok.last)
- @tok = @tkz.get_token
- end
- @first = false
-
- return @tok
-end
-
-def parse(env, code, path)
- @first = true
- @tree = Tree.new(env)
- @tkz = Tokenizer.new(code, path)
- @tree.concat(do_parse)
-end
-
----- footer ----
diff --git a/test/racc/assets/newsyn.y b/test/racc/assets/newsyn.y
deleted file mode 100644
index 5b670c966a..0000000000
--- a/test/racc/assets/newsyn.y
+++ /dev/null
@@ -1,25 +0,0 @@
-
-class A
-
- preclow
- left preclow prechigh right left nonassoc token
- right preclow prechigh right left nonassoc token
- nonassoc preclow prechigh right left nonassoc token
- prechigh
-
- convert
- left 'a'
- right 'b'
- preclow 'c'
- nonassoc 'd'
- preclow 'e'
- prechigh 'f'
- end
-
-rule
-
- left: right nonassoc preclow prechigh
-
- right: A B C
-
-end
diff --git a/test/racc/assets/noend.y b/test/racc/assets/noend.y
deleted file mode 100644
index 5aa0670be0..0000000000
--- a/test/racc/assets/noend.y
+++ /dev/null
@@ -1,4 +0,0 @@
-class MyParser
-rule
-input: A B C
-end
diff --git a/test/racc/assets/nokogiri-css.y b/test/racc/assets/nokogiri-css.y
deleted file mode 100644
index 24dfbf3b1b..0000000000
--- a/test/racc/assets/nokogiri-css.y
+++ /dev/null
@@ -1,255 +0,0 @@
-class Nokogiri::CSS::Parser
-
-token FUNCTION INCLUDES DASHMATCH LBRACE HASH PLUS GREATER S STRING IDENT
-token COMMA NUMBER PREFIXMATCH SUFFIXMATCH SUBSTRINGMATCH TILDE NOT_EQUAL
-token SLASH DOUBLESLASH NOT EQUAL RPAREN LSQUARE RSQUARE HAS
-
-rule
- selector
- : selector COMMA simple_selector_1toN {
- result = [val.first, val.last].flatten
- }
- | prefixless_combinator_selector { result = val.flatten }
- | optional_S simple_selector_1toN { result = [val.last].flatten }
- ;
- combinator
- : PLUS { result = :DIRECT_ADJACENT_SELECTOR }
- | GREATER { result = :CHILD_SELECTOR }
- | TILDE { result = :FOLLOWING_SELECTOR }
- | DOUBLESLASH { result = :DESCENDANT_SELECTOR }
- | SLASH { result = :CHILD_SELECTOR }
- ;
- simple_selector
- : element_name hcap_0toN {
- result = if val[1].nil?
- val.first
- else
- Node.new(:CONDITIONAL_SELECTOR, [val.first, val[1]])
- end
- }
- | function
- | function pseudo {
- result = Node.new(:CONDITIONAL_SELECTOR, val)
- }
- | function attrib {
- result = Node.new(:CONDITIONAL_SELECTOR, val)
- }
- | hcap_1toN {
- result = Node.new(:CONDITIONAL_SELECTOR,
- [Node.new(:ELEMENT_NAME, ['*']), val.first]
- )
- }
- ;
- prefixless_combinator_selector
- : combinator simple_selector_1toN {
- result = Node.new(val.first, [nil, val.last])
- }
- ;
- simple_selector_1toN
- : simple_selector combinator simple_selector_1toN {
- result = Node.new(val[1], [val.first, val.last])
- }
- | simple_selector S simple_selector_1toN {
- result = Node.new(:DESCENDANT_SELECTOR, [val.first, val.last])
- }
- | simple_selector
- ;
- class
- : '.' IDENT { result = Node.new(:CLASS_CONDITION, [val[1]]) }
- ;
- element_name
- : namespaced_ident
- | '*' { result = Node.new(:ELEMENT_NAME, val) }
- ;
- namespaced_ident
- : namespace '|' IDENT {
- result = Node.new(:ELEMENT_NAME,
- [[val.first, val.last].compact.join(':')]
- )
- }
- | IDENT {
- name = @namespaces.key?('xmlns') ? "xmlns:#{val.first}" : val.first
- result = Node.new(:ELEMENT_NAME, [name])
- }
- ;
- namespace
- : IDENT { result = val[0] }
- |
- ;
- attrib
- : LSQUARE attrib_name attrib_val_0or1 RSQUARE {
- result = Node.new(:ATTRIBUTE_CONDITION,
- [val[1]] + (val[2] || [])
- )
- }
- | LSQUARE function attrib_val_0or1 RSQUARE {
- result = Node.new(:ATTRIBUTE_CONDITION,
- [val[1]] + (val[2] || [])
- )
- }
- | LSQUARE NUMBER RSQUARE {
- # Non standard, but hpricot supports it.
- result = Node.new(:PSEUDO_CLASS,
- [Node.new(:FUNCTION, ['nth-child(', val[1]])]
- )
- }
- ;
- attrib_name
- : namespace '|' IDENT {
- result = Node.new(:ELEMENT_NAME,
- [[val.first, val.last].compact.join(':')]
- )
- }
- | IDENT {
- # Default namespace is not applied to attributes.
- # So we don't add prefix "xmlns:" as in namespaced_ident.
- result = Node.new(:ELEMENT_NAME, [val.first])
- }
- ;
- function
- : FUNCTION RPAREN {
- result = Node.new(:FUNCTION, [val.first.strip])
- }
- | FUNCTION expr RPAREN {
- result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
- }
- | FUNCTION nth RPAREN {
- result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
- }
- | NOT expr RPAREN {
- result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
- }
- | HAS selector RPAREN {
- result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
- }
- ;
- expr
- : NUMBER COMMA expr { result = [val.first, val.last] }
- | STRING COMMA expr { result = [val.first, val.last] }
- | IDENT COMMA expr { result = [val.first, val.last] }
- | NUMBER
- | STRING
- | IDENT # even, odd
- {
- case val[0]
- when 'even'
- result = Node.new(:NTH, ['2','n','+','0'])
- when 'odd'
- result = Node.new(:NTH, ['2','n','+','1'])
- when 'n'
- result = Node.new(:NTH, ['1','n','+','0'])
- else
- # This is not CSS standard. It allows us to support this:
- # assert_xpath("//a[foo(., @href)]", @parser.parse('a:foo(@href)'))
- # assert_xpath("//a[foo(., @a, b)]", @parser.parse('a:foo(@a, b)'))
- # assert_xpath("//a[foo(., a, 10)]", @parser.parse('a:foo(a, 10)'))
- result = val
- end
- }
- ;
- nth
- : NUMBER IDENT PLUS NUMBER # 5n+3 -5n+3
- {
- if val[1] == 'n'
- result = Node.new(:NTH, val)
- else
- raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
- end
- }
- | IDENT PLUS NUMBER { # n+3, -n+3
- if val[0] == 'n'
- val.unshift("1")
- result = Node.new(:NTH, val)
- elsif val[0] == '-n'
- val[0] = 'n'
- val.unshift("-1")
- result = Node.new(:NTH, val)
- else
- raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
- end
- }
- | NUMBER IDENT { # 5n, -5n, 10n-1
- n = val[1]
- if n[0, 2] == 'n-'
- val[1] = 'n'
- val << "-"
- # b is contained in n as n is the string "n-b"
- val << n[2, n.size]
- result = Node.new(:NTH, val)
- elsif n == 'n'
- val << "+"
- val << "0"
- result = Node.new(:NTH, val)
- else
- raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
- end
- }
- ;
- pseudo
- : ':' function {
- result = Node.new(:PSEUDO_CLASS, [val[1]])
- }
- | ':' IDENT { result = Node.new(:PSEUDO_CLASS, [val[1]]) }
- ;
- hcap_0toN
- : hcap_1toN
- |
- ;
- hcap_1toN
- : attribute_id hcap_1toN {
- result = Node.new(:COMBINATOR, val)
- }
- | class hcap_1toN {
- result = Node.new(:COMBINATOR, val)
- }
- | attrib hcap_1toN {
- result = Node.new(:COMBINATOR, val)
- }
- | pseudo hcap_1toN {
- result = Node.new(:COMBINATOR, val)
- }
- | negation hcap_1toN {
- result = Node.new(:COMBINATOR, val)
- }
- | attribute_id
- | class
- | attrib
- | pseudo
- | negation
- ;
- attribute_id
- : HASH { result = Node.new(:ID, val) }
- ;
- attrib_val_0or1
- : eql_incl_dash IDENT { result = [val.first, val[1]] }
- | eql_incl_dash STRING { result = [val.first, val[1]] }
- |
- ;
- eql_incl_dash
- : EQUAL { result = :equal }
- | PREFIXMATCH { result = :prefix_match }
- | SUFFIXMATCH { result = :suffix_match }
- | SUBSTRINGMATCH { result = :substring_match }
- | NOT_EQUAL { result = :not_equal }
- | INCLUDES { result = :includes }
- | DASHMATCH { result = :dash_match }
- ;
- negation
- : NOT negation_arg RPAREN {
- result = Node.new(:NOT, [val[1]])
- }
- ;
- negation_arg
- : element_name
- | element_name hcap_1toN
- | hcap_1toN
- ;
- optional_S
- : S
- |
- ;
-end
-
----- header
-
-require 'nokogiri/css/parser_extras'
diff --git a/test/racc/assets/nonass.y b/test/racc/assets/nonass.y
deleted file mode 100644
index b9a35a2626..0000000000
--- a/test/racc/assets/nonass.y
+++ /dev/null
@@ -1,41 +0,0 @@
-#
-# nonassoc test
-#
-
-class P
-
-preclow
- nonassoc N
- left P
-prechigh
-
-rule
-
-target : exp
-exp : exp N exp
- | exp P exp
- | T
-
-end
-
----- inner
-
- def parse
- @src = [[:T,'T'], [:N,'N'], [:T,'T'], [:N,'N'], [:T,'T']]
- do_parse
- end
-
- def next_token
- @src.shift
- end
-
----- footer
-
-begin
- P.new.parse
-rescue ParseError
- exit 0
-else
- $stderr.puts 'parse error not raised: nonassoc not work'
- exit 1
-end
diff --git a/test/racc/assets/normal.y b/test/racc/assets/normal.y
deleted file mode 100644
index 96ae352c82..0000000000
--- a/test/racc/assets/normal.y
+++ /dev/null
@@ -1,27 +0,0 @@
-
-class Testp
-
- convert
- A '2'
- B '3'
- end
-
- prechigh
- left B
- preclow
-
-rule
-
-/* comment */
- target: A B C nonterminal { action "string" == /regexp/o
- 1 /= 3 }
- ; # comment
-
- nonterminal: A '+' B = A;
-
-/* end */
-end
-
----- driver
-
- # driver is old name
diff --git a/test/racc/assets/norule.y b/test/racc/assets/norule.y
deleted file mode 100644
index e50a4b3472..0000000000
--- a/test/racc/assets/norule.y
+++ /dev/null
@@ -1,4 +0,0 @@
-
-class A
-rule
-end
diff --git a/test/racc/assets/nullbug1.y b/test/racc/assets/nullbug1.y
deleted file mode 100644
index 4b267ba0ea..0000000000
--- a/test/racc/assets/nullbug1.y
+++ /dev/null
@@ -1,25 +0,0 @@
-#
-# number of conflicts must be ZERO.
-#
-
-class T
-
-rule
-
-targ : dummy
- | a b c
-
-dummy : V v
-
-V : E e
- | F f
- |
- ;
-
-E :
- ;
-
-F :
- ;
-
-end
diff --git a/test/racc/assets/nullbug2.y b/test/racc/assets/nullbug2.y
deleted file mode 100644
index 0c1d43bf3e..0000000000
--- a/test/racc/assets/nullbug2.y
+++ /dev/null
@@ -1,15 +0,0 @@
-#
-# number of conflicts must be ZERO.
-#
-
-class A
-rule
- targ: operation voidhead
- | variable
-
- voidhead : void B
- void:
-
- operation: A
- variable : A
-end
diff --git a/test/racc/assets/opal.y b/test/racc/assets/opal.y
deleted file mode 100644
index ae6a5a6bdd..0000000000
--- a/test/racc/assets/opal.y
+++ /dev/null
@@ -1,1807 +0,0 @@
-# Copyright (C) 2013 by Adam Beynon
-#
-# 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.
-
-class Opal::Parser
-
-token kCLASS kMODULE kDEF kUNDEF kBEGIN kRESCUE kENSURE kEND kIF kUNLESS
- kTHEN kELSIF kELSE kCASE kWHEN kWHILE kUNTIL kFOR kBREAK kNEXT
- kREDO kRETRY kIN kDO kDO_COND kDO_BLOCK kDO_LAMBDA kRETURN kYIELD kSUPER
- kSELF kNIL kTRUE kFALSE kAND kOR kNOT kIF_MOD kUNLESS_MOD kWHILE_MOD
- kUNTIL_MOD kRESCUE_MOD kALIAS kDEFINED klBEGIN klEND k__LINE__
- k__FILE__ k__ENCODING__ tIDENTIFIER tFID tGVAR tIVAR tCONSTANT
- tLABEL tCVAR tNTH_REF tBACK_REF tSTRING_CONTENT tINTEGER tFLOAT
- tREGEXP_END tUPLUS tUMINUS tUMINUS_NUM tPOW tCMP tEQ tEQQ tNEQ tGEQ tLEQ tANDOP
- tOROP tMATCH tNMATCH tJSDOT tDOT tDOT2 tDOT3 tAREF tASET tLSHFT tRSHFT
- tCOLON2 tCOLON3 tOP_ASGN tASSOC tLPAREN tLPAREN2 tRPAREN tLPAREN_ARG
- ARRAY_BEG tRBRACK tLBRACE tLBRACE_ARG tSTAR tSTAR2 tAMPER tAMPER2
- tTILDE tPERCENT tDIVIDE tPLUS tMINUS tLT tGT tPIPE tBANG tCARET
- tLCURLY tRCURLY tBACK_REF2 tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG
- tWORDS_BEG tAWORDS_BEG tSTRING_DBEG tSTRING_DVAR tSTRING_END tSTRING
- tSYMBOL tNL tEH tCOLON tCOMMA tSPACE tSEMI tLAMBDA tLAMBEG
- tLBRACK2 tLBRACK tJSLBRACK tDSTAR
-
-prechigh
- right tBANG tTILDE tUPLUS
- right tPOW
- right tUMINUS_NUM tUMINUS
- left tSTAR2 tDIVIDE tPERCENT
- left tPLUS tMINUS
- left tLSHFT tRSHFT
- left tAMPER2
- left tPIPE tCARET
- left tGT tGEQ tLT tLEQ
- nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH
- left tANDOP
- left tOROP
- nonassoc tDOT2 tDOT3
- right tEH tCOLON
- left kRESCUE_MOD
- right tEQL tOP_ASGN
- nonassoc kDEFINED
- right kNOT
- left kOR kAND
- nonassoc kIF_MOD kUNLESS_MOD kWHILE_MOD kUNTIL_MOD
- nonassoc tLBRACE_ARG
- nonassoc tLOWEST
-preclow
-
-rule
-
- program: top_compstmt
-
- top_compstmt: top_stmts opt_terms
- {
- result = new_compstmt val[0]
- }
-
- top_stmts: # none
- {
- result = new_block
- }
- | top_stmt
- {
- result = new_block val[0]
- }
- | top_stmts terms top_stmt
- {
- val[0] << val[2]
- result = val[0]
- }
-
- top_stmt: stmt
- | klBEGIN tLCURLY top_compstmt tRCURLY
- {
- result = val[2]
- }
-
- bodystmt: compstmt opt_rescue opt_else opt_ensure
- {
- result = new_body(val[0], val[1], val[2], val[3])
- }
-
- compstmt: stmts opt_terms
- {
- result = new_compstmt val[0]
- }
-
- stmts: # none
- {
- result = new_block
- }
- | stmt
- {
- result = new_block val[0]
- }
- | stmts terms stmt
- {
- val[0] << val[2]
- result = val[0]
- }
-
- stmt: kALIAS fitem
- {
- lexer.lex_state = :expr_fname
- }
- fitem
- {
- result = new_alias(val[0], val[1], val[3])
- }
- | kALIAS tGVAR tGVAR
- {
- result = s(:valias, value(val[1]).to_sym, value(val[2]).to_sym)
- }
- | kALIAS tGVAR tBACK_REF
- | kALIAS tGVAR tNTH_REF
- {
- result = s(:valias, value(val[1]).to_sym, value(val[2]).to_sym)
- }
- | kUNDEF undef_list
- {
- result = val[1]
- }
- | stmt kIF_MOD expr_value
- {
- result = new_if(val[1], val[2], val[0], nil)
- }
- | stmt kUNLESS_MOD expr_value
- {
- result = new_if(val[1], val[2], nil, val[0])
- }
- | stmt kWHILE_MOD expr_value
- {
- result = new_while(val[1], val[2], val[0])
- }
- | stmt kUNTIL_MOD expr_value
- {
- result = new_until(val[1], val[2], val[0])
- }
- | stmt kRESCUE_MOD stmt
- {
- result = new_rescue_mod(val[1], val[0], val[2])
- }
- | klEND tLCURLY compstmt tRCURLY
- | lhs tEQL command_call
- {
- result = new_assign(val[0], val[1], val[2])
- }
- | mlhs tEQL command_call
- {
- result = s(:masgn, val[0], s(:to_ary, val[2]))
- }
- | var_lhs tOP_ASGN command_call
- {
- result = new_op_asgn val[1], val[0], val[2]
- }
- | primary_value tLBRACK2 aref_args tRBRACK tOP_ASGN command_call
- | primary_value tJSLBRACK aref_args tRBRACK tOP_ASGN command_call
- | primary_value tDOT tIDENTIFIER tOP_ASGN command_call
- {
- result = s(:op_asgn2, val[0], op_to_setter(val[2]), value(val[3]).to_sym, val[4])
- }
- | primary_value tDOT tCONSTANT tOP_ASGN command_call
- | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call
- | backref tOP_ASGN command_call
- | lhs tEQL mrhs
- {
- result = new_assign val[0], val[1], s(:svalue, val[2])
- }
- | mlhs tEQL arg_value
- {
- result = s(:masgn, val[0], s(:to_ary, val[2]))
- }
- | mlhs tEQL mrhs
- {
- result = s(:masgn, val[0], val[2])
- }
- | expr
-
- expr: command_call
- | expr kAND expr
- {
- result = s(:and, val[0], val[2])
- }
- | expr kOR expr
- {
- result = s(:or, val[0], val[2])
- }
- | kNOT expr
- {
- result = new_unary_call(['!', []], val[1])
- }
- | tBANG command_call
- {
- result = new_unary_call(val[0], val[1])
- }
- | arg
-
- expr_value: expr
-
- command_call: command
- | block_command
- | kRETURN call_args
- {
- result = new_return(val[0], val[1])
- }
- | kBREAK call_args
- {
- result = new_break(val[0], val[1])
- }
- | kNEXT call_args
- {
- result = new_next(val[0], val[1])
- }
-
- block_command: block_call
- | block_call tJSDOT operation2 command_args
- | block_call tDOT operation2 command_args
- | block_call tCOLON2 operation2 command_args
-
- cmd_brace_block: tLBRACE_ARG opt_block_var compstmt tRCURLY
-
- command: operation command_args =tLOWEST
- {
- result = new_call(nil, val[0], val[1])
- }
- | operation command_args cmd_brace_block
- | primary_value tJSDOT operation2 command_args =tLOWEST
- {
- result = new_js_call(val[0], val[2], val[3])
- }
- | primary_value tJSDOT operation2 command_args cmd_brace_block
- | primary_value tDOT operation2 command_args =tLOWEST
- {
- result = new_call(val[0], val[2], val[3])
- }
- | primary_value tDOT operation2 command_args cmd_brace_block
- | primary_value tCOLON2 operation2 command_args =tLOWEST
- {
- result = new_call(val[0], val[2], val[3])
- }
- | primary_value tCOLON2 operation2 command_args cmd_brace_block
- | kSUPER command_args
- {
- result = new_super(val[0], val[1])
- }
- | kYIELD command_args
- {
- result = new_yield val[1]
- }
-
- mlhs: mlhs_basic
- {
- result = val[0]
- }
- | tLPAREN mlhs_entry tRPAREN
- {
- result = val[1]
- }
-
- mlhs_entry: mlhs_basic
- {
- result = val[0]
- }
- | tLPAREN mlhs_entry tRPAREN
- {
- result = val[1]
- }
-
- mlhs_basic: mlhs_head
- {
- result = val[0]
- }
- | mlhs_head mlhs_item
- {
- result = val[0] << val[1]
- }
- | mlhs_head tSTAR mlhs_node
- {
- result = val[0] << s(:splat, val[2])
- }
- | mlhs_head tSTAR mlhs_node tCOMMA mlhs_post
- | mlhs_head tSTAR
- {
- result = val[0] << s(:splat)
- }
- | mlhs_head tSTAR tCOMMA mlhs_post
- | tSTAR mlhs_node
- {
- result = s(:array, s(:splat, val[1]))
- }
- | tSTAR
- {
- result = s(:array, s(:splat))
- }
- | tSTAR tCOMMA mlhs_post
-
- mlhs_item: mlhs_node
- {
- result = val[0]
- }
- | tLPAREN mlhs_entry tRPAREN
- {
- result = val[1]
- }
-
- mlhs_head: mlhs_item tCOMMA
- {
- result = s(:array, val[0])
- }
- | mlhs_head mlhs_item tCOMMA
- {
- result = val[0] << val[1]
- }
-
- mlhs_post: mlhs_item
- | mlhs_post tCOMMA mlhs_item
-
- mlhs_node: variable
- {
- result = new_assignable val[0]
- }
- | primary_value tLBRACK2 aref_args tRBRACK
- {
- args = val[2] ? val[2] : []
- result = s(:attrasgn, val[0], :[]=, s(:arglist, *args))
- }
- | primary_value tDOT tIDENTIFIER
- {
- result = new_call val[0], val[2], []
- }
- | primary_value tCOLON2 tIDENTIFIER
- | primary_value tDOT tCONSTANT
- | primary_value tCOLON2 tCONSTANT
- | tCOLON3 tCONSTANT
- | backref
-
- lhs: variable
- {
- result = new_assignable val[0]
- }
- | primary_value tJSLBRACK aref_args tRBRACK
- {
- result = new_js_attrasgn(val[0], val[2])
- }
- | primary_value tLBRACK2 aref_args tRBRACK
- {
- result = new_attrasgn(val[0], :[]=, val[2])
- }
- | primary_value tDOT tIDENTIFIER
- {
- result = new_attrasgn(val[0], op_to_setter(val[2]))
- }
- | primary_value tCOLON2 tIDENTIFIER
- {
- result = new_attrasgn(val[0], op_to_setter(val[2]))
- }
- | primary_value tDOT tCONSTANT
- {
- result = new_attrasgn(val[0], op_to_setter(val[2]))
- }
- | primary_value tCOLON2 tCONSTANT
- {
- result = new_colon2(val[0], val[1], val[2])
- }
- | tCOLON3 tCONSTANT
- {
- result = new_colon3(val[0], val[1])
- }
- | backref
-
- cname: tCONSTANT
-
- cpath: tCOLON3 cname
- {
- result = new_colon3(val[0], val[1])
- }
- | cname
- {
- result = new_const(val[0])
- }
- | primary_value tCOLON2 cname
- {
- result = new_colon2(val[0], val[1], val[2])
- }
-
- fname: tIDENTIFIER
- | tCONSTANT
- | tFID
- | op
- {
- lexer.lex_state = :expr_end
- result = val[0]
- }
- | reswords
- {
- lexer.lex_state = :expr_end
- result = val[0]
- }
-
- fitem: fname
- {
- result = new_sym(val[0])
- }
- | symbol
-
- undef_list: fitem
- {
- result = s(:undef, val[0])
- }
- | undef_list tCOMMA fitem
- {
- result = val[0] << val[2]
- }
-
- op: tPIPE | tCARET | tAMPER2 | tCMP | tEQ | tEQQ
- | tMATCH | tNMATCH | tGT | tGEQ | tLT | tLEQ
- | tNEQ | tLSHFT | tRSHFT | tPLUS | tMINUS | tSTAR2
- | tSTAR | tDIVIDE | tPERCENT | tPOW | tBANG | tTILDE
- | tUPLUS | tUMINUS | tAREF | tASET | tBACK_REF2
-
- reswords: k__LINE__ | k__FILE__ | klBEGIN | klEND | kALIAS | kAND
- | kBEGIN | kBREAK | kCASE | kCLASS | kDEF | kDEFINED
- | kDO | kELSE | kELSIF | kEND | kENSURE | kFALSE
- | kFOR | kIN | kMODULE | kNEXT | kNIL | kNOT
- | kOR | kREDO | kRESCUE | kRETRY | kRETURN | kSELF
- | kSUPER | kTHEN | kTRUE | kUNDEF | kWHEN | kYIELD
- | kIF_MOD | kUNLESS_MOD | kWHILE_MOD | kUNTIL_MOD | kRESCUE_MOD
- | kIF | kWHILE | kUNTIL | kUNLESS
-
- arg: lhs tEQL arg
- {
- result = new_assign(val[0], val[1], val[2])
- }
- | lhs tEQL arg kRESCUE_MOD arg
- {
- result = new_assign val[0], val[1], s(:rescue_mod, val[2], val[4])
- }
- | var_lhs tOP_ASGN arg
- {
- result = new_op_asgn val[1], val[0], val[2]
- }
- | primary_value tLBRACK2 aref_args tRBRACK tOP_ASGN arg
- {
- result = new_op_asgn1(val[0], val[2], val[4], val[5])
- }
- | primary_value tJSLBRACK aref_args tRBRACK tOP_ASGN arg
- {
- raise ".JS[...] #{val[4]} is not supported"
- }
- | primary_value tDOT tIDENTIFIER tOP_ASGN arg
- {
- result = s(:op_asgn2, val[0], op_to_setter(val[2]), value(val[3]).to_sym, val[4])
- }
- | primary_value tDOT tCONSTANT tOP_ASGN arg
- | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg
- | primary_value tCOLON2 tCONSTANT tOP_ASGN arg
- | tCOLON3 tCONSTANT tOP_ASGN arg
- | backref tOP_ASGN arg
- | arg tDOT2 arg
- {
- result = new_irange(val[0], val[1], val[2])
- }
- | arg tDOT3 arg
- {
- result = new_erange(val[0], val[1], val[2])
- }
- | arg tPLUS arg
- {
- result = new_binary_call(val[0], val[1], val[2])
- }
- | arg tMINUS arg
- {
- result = new_binary_call(val[0], val[1], val[2])
- }
- | arg tSTAR2 arg
- {
- result = new_binary_call(val[0], val[1], val[2])
- }
- | arg tDIVIDE arg
- {
- result = new_binary_call(val[0], val[1], val[2])
- }
- | arg tPERCENT arg
- {
- result = new_binary_call(val[0], val[1], val[2])
- }
- | arg tPOW arg
- {
- result = new_binary_call(val[0], val[1], val[2])
- }
- | '-@NUM' tINTEGER tPOW arg
- {
- result = new_call new_binary_call(new_int(val[1]), val[2], val[3]), [:"-@", []], []
- }
- | '-@NUM' tFLOAT tPOW arg
- {
- result = new_call new_binary_call(new_float(val[1]), val[2], val[3]), [:"-@", []], []
- }
- | tUPLUS arg
- {
- result = new_call val[1], [:"+@", []], []
- if [:int, :float].include? val[1].type
- result = val[1]
- end
- }
- | tUMINUS arg
- {
- result = new_call val[1], [:"-@", []], []
- if val[1].type == :int
- val[1][1] = -val[1][1]
- result = val[1]
- elsif val[1].type == :float
- val[1][1] = -val[1][1].to_f
- result = val[1]
- end
- }
- | arg tPIPE arg
- {
- result = new_binary_call(val[0], val[1], val[2])
- }
- | arg tCARET arg
- {
- result = new_binary_call(val[0], val[1], val[2])
- }
- | arg tAMPER2 arg
- {
- result = new_binary_call(val[0], val[1], val[2])
- }
- | arg tCMP arg
- {
- result = new_binary_call(val[0], val[1], val[2])
- }
- | arg tGT arg
- {
- result = new_binary_call(val[0], val[1], val[2])
- }
- | arg tGEQ arg
- {
- result = new_binary_call(val[0], val[1], val[2])
- }
- | arg tLT arg
- {
- result = new_binary_call(val[0], val[1], val[2])
- }
- | arg tLEQ arg
- {
- result = new_binary_call(val[0], val[1], val[2])
- }
- | arg tEQ arg
- {
- result = new_binary_call(val[0], val[1], val[2])
- }
- | arg tEQQ arg
- {
- result = new_binary_call(val[0], val[1], val[2])
- }
- | arg tNEQ arg
- {
- result = new_binary_call(val[0], val[1], val[2])
- }
- | arg tMATCH arg
- {
- result = new_binary_call(val[0], val[1], val[2])
- }
- | arg tNMATCH arg
- {
- result = new_binary_call(val[0], val[1], val[2])
- }
- | tBANG arg
- {
- result = new_unary_call(val[0], val[1])
- }
- | tTILDE arg
- {
- result = new_unary_call(val[0], val[1])
- }
- | arg tLSHFT arg
- {
- result = new_binary_call(val[0], val[1], val[2])
- }
- | arg tRSHFT arg
- {
- result = new_binary_call(val[0], val[1], val[2])
- }
- | arg tANDOP arg
- {
- result = new_and(val[0], val[1], val[2])
- }
- | arg tOROP arg
- {
- result = new_or(val[0], val[1], val[2])
- }
- | kDEFINED opt_nl arg
- {
- result = s(:defined, val[2])
- }
- | arg tEH arg tCOLON arg
- {
- result = new_if(val[1], val[0], val[2], val[4])
- }
- | primary
-
- arg_value: arg
-
- aref_args: none
- {
- result = nil
- }
- | command opt_nl
- {
- result = [val[0]]
- }
- | args trailer
- {
- result = val[0]
- }
- | args tCOMMA assocs trailer
- {
- val[0] << s(:hash, *val[2])
- result = val[0]
- }
- | assocs trailer
- {
- result = [s(:hash, *val[0])]
- }
-
- paren_args: tLPAREN2 opt_call_args rparen
- {
- result = val[1]
- }
-
- rparen: opt_nl tRPAREN
-
- opt_paren_args: none
- {
- result = []
- }
- | paren_args
-
- opt_call_args: none
- {
- result = []
- }
- | call_args
- | args tCOMMA
- {
- result = val[0]
- }
- | args tCOMMA assocs tCOMMA
- {
- result = val[0]
- result << new_hash(nil, val[2], nil)
- }
- | assocs tCOMMA
- {
- result = [new_hash(nil, val[0], nil)]
- }
-
- call_args: command
- {
- result = [val[0]]
- }
- | args opt_block_arg
- {
- result = val[0]
- add_block_pass val[0], val[1]
- }
- | assocs opt_block_arg
- {
- result = [new_hash(nil, val[0], nil)]
- add_block_pass result, val[1]
- }
- | args tCOMMA assocs opt_block_arg
- {
- result = val[0]
- result << new_hash(nil, val[2], nil)
- result << val[3] if val[3]
- }
- | block_arg
- {
- result = []
- add_block_pass result, val[0]
- }
-
- call_args2: arg_value tCOMMA args opt_block_arg
- | block_arg
-
- command_args: {
- lexer.cmdarg_push 1
- }
- open_args
- {
- lexer.cmdarg_pop
- result = val[1]
- }
-
- open_args: call_args
- | tLPAREN_ARG tRPAREN
- {
- result = nil
- }
- | tLPAREN_ARG call_args2 tRPAREN
- {
- result = val[1]
- }
-
- block_arg: tAMPER arg_value
- {
- result = new_block_pass(val[0], val[1])
- }
-
- opt_block_arg: tCOMMA block_arg
- {
- result = val[1]
- }
- | # none
- {
- result = nil
- }
-
- args: arg_value
- {
- result = [val[0]]
- }
- | tSTAR arg_value
- {
- result = [new_splat(val[0], val[1])]
- }
- | args tCOMMA arg_value
- {
- result = val[0] << val[2]
- }
- | args tCOMMA tSTAR arg_value
- {
- result = val[0] << new_splat(val[2], val[3])
- }
-
- mrhs: args tCOMMA arg_value
- {
- val[0] << val[2]
- result = s(:array, *val[0])
- }
- | args tCOMMA tSTAR arg_value
- {
- val[0] << s(:splat, val[3])
- result = s(:array, *val[0])
- }
- | tSTAR arg_value
- {
- result = s(:splat, val[1])
- }
-
- primary: literal
- | strings
- | xstring
- | regexp
- | words
- | awords
- | var_ref
- | backref
- | tFID
- | kBEGIN
- {
- result = lexer.line
- }
- bodystmt kEND
- {
- result = s(:begin, val[2])
- }
- | tLPAREN_ARG expr opt_nl tRPAREN
- {
- result = val[1]
- }
- | tLPAREN compstmt tRPAREN
- {
- result = new_paren(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tCONSTANT
- {
- result = new_colon2(val[0], val[1], val[2])
- }
- | tCOLON3 tCONSTANT
- {
- result = new_colon3(val[0], val[1])
- }
- | primary_value tLBRACK2 aref_args tRBRACK
- {
- result = new_call val[0], [:[], []], val[2]
- }
- | primary_value tJSLBRACK aref_args tRBRACK
- {
- result = new_js_call val[0], [:[], []], val[2]
- }
- | tLBRACK aref_args tRBRACK
- {
- result = new_array(val[0], val[1], val[2])
- }
- | tLBRACE assoc_list tRCURLY
- {
- result = new_hash(val[0], val[1], val[2])
- }
- | kRETURN
- {
- result = new_return(val[0])
- }
- | kYIELD tLPAREN2 call_args tRPAREN
- {
- result = new_yield val[2]
- }
- | kYIELD tLPAREN2 tRPAREN
- {
- result = s(:yield)
- }
- | kYIELD
- {
- result = s(:yield)
- }
- | kDEFINED opt_nl tLPAREN2 expr tRPAREN
- {
- result = s(:defined, val[3])
- }
- | kNOT tLPAREN2 expr tRPAREN
- {
- result = new_unary_call(['!', []], val[2])
- }
- | kNOT tLPAREN2 tRPAREN
- {
- result = new_unary_call(['!', []], new_nil(val[0]))
- }
- | operation brace_block
- {
- result = new_call(nil, val[0], [])
- result << val[1]
- }
- | method_call
- | method_call brace_block
- {
- val[0] << val[1]
- result = val[0]
- }
- | tLAMBDA lambda
- {
- result = val[1]
- }
- | kIF expr_value then compstmt if_tail kEND
- {
- result = new_if(val[0], val[1], val[3], val[4])
- }
- | kUNLESS expr_value then compstmt opt_else kEND
- {
- result = new_if(val[0], val[1], val[4], val[3])
- }
- | kWHILE
- {
- lexer.cond_push 1
- result = lexer.line
- }
- expr_value do
- {
- lexer.cond_pop
- }
- compstmt kEND
- {
- result = s(:while, val[2], val[5])
- }
- | kUNTIL
- {
- lexer.cond_push 1
- result = lexer.line
- }
- expr_value do
- {
- lexer.cond_pop
- }
- compstmt kEND
- {
- result = s(:until, val[2], val[5])
- }
- | kCASE expr_value opt_terms case_body kEND
- {
- result = s(:case, val[1], *val[3])
- }
- | kCASE opt_terms case_body kEND
- {
- result = s(:case, nil, *val[2])
- }
- | kCASE opt_terms kELSE compstmt kEND
- {
- result = s(:case, nil, val[3])
- }
- | kFOR for_var kIN
- {
- lexer.cond_push 1
- result = lexer.line
- }
- expr_value do
- {
- lexer.cond_pop
- }
- compstmt kEND
- {
- result = s(:for, val[4], val[1], val[7])
- }
- | kCLASS cpath superclass
- {
- # ...
- }
- bodystmt kEND
- {
- result = new_class val[0], val[1], val[2], val[4], val[5]
- }
- | kCLASS tLSHFT
- {
- result = lexer.line
- }
- expr term
- {
- # ...
- }
- bodystmt kEND
- {
- result = new_sclass(val[0], val[3], val[6], val[7])
- }
- | kMODULE
- {
- result = lexer.line
- }
- cpath
- {
- # ...
- }
- bodystmt kEND
- {
- result = new_module(val[0], val[2], val[4], val[5])
- }
- | kDEF fname
- {
- push_scope
- lexer.lex_state = :expr_endfn
- }
- f_arglist bodystmt kEND
- {
- result = new_def(val[0], nil, val[1], val[3], val[4], val[5])
- pop_scope
- }
- | kDEF singleton dot_or_colon
- {
- lexer.lex_state = :expr_fname
- }
- fname
- {
- push_scope
- lexer.lex_state = :expr_endfn
- }
- f_arglist bodystmt kEND
- {
- result = new_def(val[0], val[1], val[4], val[6], val[7], val[8])
- pop_scope
- }
- | kBREAK
- {
- result = new_break(val[0])
- }
- | kNEXT
- {
- result = s(:next)
- }
- | kREDO
- {
- result = s(:redo)
- }
- | kRETRY
-
- primary_value: primary
-
- then: term
- | tCOLON
- | kTHEN
- | term kTHEN
-
- do: term
- | tCOLON
- | kDO_COND
-
- lambda: f_larglist lambda_body
- {
- result = new_call nil, [:lambda, []], []
- result << new_iter(val[0], val[1])
- }
-
- f_larglist: tLPAREN2 block_param tRPAREN
- {
- result = val[1]
- }
- | tLPAREN2 tRPAREN
- {
- result = nil
- }
- | block_param
- | none
-
- lambda_body: tLAMBEG compstmt tRCURLY
- {
- result = val[1]
- }
- | kDO_LAMBDA compstmt kEND
- {
- result = val[1]
- }
-
- if_tail: opt_else
- {
- result = val[0]
- }
- | kELSIF expr_value then compstmt if_tail
- {
- result = new_if(val[0], val[1], val[3], val[4])
- }
-
- opt_else: none
- | kELSE compstmt
- {
- result = val[1]
- }
-
- f_block_optarg: f_block_opt
- {
- result = s(:block, val[0])
- }
- | f_block_optarg tCOMMA f_block_opt
- {
- val[0] << val[2]
- result = val[0]
- }
-
- f_block_opt: tIDENTIFIER tEQL primary_value
- {
- result = new_assign(new_assignable(new_ident(
- val[0])), val[1], val[2])
- }
-
- opt_block_var: none
- | tPIPE tPIPE
- {
- result = nil
- }
- | tOROP
- {
- result = nil
- }
- | tPIPE block_param tPIPE
- {
- result = val[1]
- }
-
- block_args_tail: f_block_arg
- {
- result = val[0]
- }
-
-opt_block_args_tail: tCOMMA block_args_tail
- {
- result = val[1]
- }
- | none
- {
- nil
- }
-
- block_param: f_arg tCOMMA f_block_optarg tCOMMA f_rest_arg opt_block_args_tail
- {
- result = new_block_args(val[0], val[2], val[4], val[5])
- }
- | f_arg tCOMMA f_block_optarg opt_block_args_tail
- {
- result = new_block_args(val[0], val[2], nil, val[3])
- }
- | f_arg tCOMMA f_rest_arg opt_block_args_tail
- {
- result = new_block_args(val[0], nil, val[2], val[3])
- }
- | f_arg tCOMMA
- {
- result = new_block_args(val[0], nil, nil, nil)
- }
- | f_arg opt_block_args_tail
- {
- result = new_block_args(val[0], nil, nil, val[1])
- }
- | f_block_optarg tCOMMA f_rest_arg opt_block_args_tail
- {
- result = new_block_args(nil, val[0], val[2], val[3])
- }
- | f_block_optarg opt_block_args_tail
- {
- result = new_block_args(nil, val[0], nil, val[1])
- }
- | f_rest_arg opt_block_args_tail
- {
- result = new_block_args(nil, nil, val[0], val[1])
- }
- | block_args_tail
- {
- result = new_block_args(nil, nil, nil, val[0])
- }
-
- do_block: kDO_BLOCK
- {
- push_scope :block
- result = lexer.line
- }
- opt_block_var compstmt kEND
- {
- result = new_iter val[2], val[3]
- pop_scope
- }
-
- block_call: command do_block
- {
- val[0] << val[1]
- result = val[0]
- }
- | block_call tJSDOT operation2 opt_paren_args
- | block_call tDOT operation2 opt_paren_args
- | block_call tCOLON2 operation2 opt_paren_args
-
- method_call: operation paren_args
- {
- result = new_call(nil, val[0], val[1])
- }
- | primary_value tDOT operation2 opt_paren_args
- {
- result = new_call(val[0], val[2], val[3])
- }
- | primary_value tJSDOT operation2 opt_paren_args
- {
- result = new_js_call(val[0], val[2], val[3])
- }
- | primary_value tDOT paren_args
- {
- result = new_call(val[0], [:call, []], val[2])
- }
- | primary_value tCOLON2 operation2 paren_args
- {
- result = new_call(val[0], val[2], val[3])
- }
- | primary_value tCOLON2 operation3
- {
- result = new_call(val[0], val[2])
- }
- | kSUPER paren_args
- {
- result = new_super(val[0], val[1])
- }
- | kSUPER
- {
- result = new_super(val[0], nil)
- }
-
- brace_block: tLCURLY
- {
- push_scope :block
- result = lexer.line
- }
- opt_block_var compstmt tRCURLY
- {
- result = new_iter val[2], val[3]
- pop_scope
- }
- | kDO
- {
- push_scope :block
- result = lexer.line
- }
- opt_block_var compstmt kEND
- {
- result = new_iter val[2], val[3]
- pop_scope
- }
-
- case_body: kWHEN
- {
- result = lexer.line
- }
- args then compstmt cases
- {
- part = s(:when, s(:array, *val[2]), val[4])
- result = [part]
- result.push(*val[5]) if val[5]
- }
-
- cases: opt_else
- {
- result = [val[0]]
- }
- | case_body
-
- opt_rescue: kRESCUE exc_list exc_var then compstmt opt_rescue
- {
- exc = val[1] || s(:array)
- exc << new_assign(val[2], val[2], s(:gvar, '$!'.intern)) if val[2]
- result = [s(:resbody, exc, val[4])]
- result.push val[5].first if val[5]
- }
- | # none
- {
- result = nil
- }
-
- exc_list: arg_value
- {
- result = s(:array, val[0])
- }
- | mrhs
- | none
-
- exc_var: tASSOC lhs
- {
- result = val[1]
- }
- | none
- {
- result = nil
- }
-
- opt_ensure: kENSURE compstmt
- {
- result = val[1].nil? ? s(:nil) : val[1]
- }
- | none
-
- literal: numeric
- | symbol
- | dsym
-
- strings: string
- {
- result = new_str val[0]
- }
-
- string: string1
- | string string1
- {
- result = str_append val[0], val[1]
- }
-
- string1: tSTRING_BEG string_contents tSTRING_END
- {
- result = val[1]
- }
- | tSTRING
- {
- result = s(:str, value(val[0]))
- }
-
- xstring: tXSTRING_BEG xstring_contents tSTRING_END
- {
- result = new_xstr(val[0], val[1], val[2])
- }
-
- regexp: tREGEXP_BEG xstring_contents tREGEXP_END
- {
- result = new_regexp val[1], val[2]
- }
-
- words: tWORDS_BEG tSPACE tSTRING_END
- {
- result = s(:array)
- }
- | tWORDS_BEG word_list tSTRING_END
- {
- result = val[1]
- }
-
- word_list: none
- {
- result = s(:array)
- }
- | word_list word tSPACE
- {
- part = val[1]
- part = s(:dstr, "", val[1]) if part.type == :evstr
- result = val[0] << part
- }
-
- word: string_content
- {
- result = val[0]
- }
- | word string_content
- {
- result = val[0].concat([val[1]])
- }
-
- awords: tAWORDS_BEG tSPACE tSTRING_END
- {
- result = s(:array)
- }
- | tAWORDS_BEG qword_list tSTRING_END
- {
- result = val[1]
- }
-
- qword_list: none
- {
- result = s(:array)
- }
- | qword_list tSTRING_CONTENT tSPACE
- {
- result = val[0] << s(:str, value(val[1]))
- }
-
- string_contents: none
- {
- result = nil
- }
- | string_contents string_content
- {
- result = str_append val[0], val[1]
- }
-
-xstring_contents: none
- {
- result = nil
- }
- | xstring_contents string_content
- {
- result = str_append val[0], val[1]
- }
-
- string_content: tSTRING_CONTENT
- {
- result = new_str_content(val[0])
- }
- | tSTRING_DVAR
- {
- result = lexer.strterm
- lexer.strterm = nil
- }
- string_dvar
- {
- lexer.strterm = val[1]
- result = new_evstr(val[2])
- }
- | tSTRING_DBEG
- {
- lexer.cond_push 0
- lexer.cmdarg_push 0
- result = lexer.strterm
- lexer.strterm = nil
- lexer.lex_state = :expr_beg
- }
- compstmt tRCURLY
- {
- lexer.strterm = val[1]
- lexer.cond_lexpop
- lexer.cmdarg_lexpop
- result = new_evstr(val[2])
- }
-
- string_dvar: tGVAR
- {
- result = new_gvar(val[0])
- }
- | tIVAR
- {
- result = new_ivar(val[0])
- }
- | tCVAR
- {
- result = new_cvar(val[0])
- }
- | backref
-
-
- symbol: tSYMBEG sym
- {
- result = new_sym(val[1])
- lexer.lex_state = :expr_end
- }
- | tSYMBOL
- {
- result = new_sym(val[0])
- }
-
- sym: fname
- | tIVAR
- | tGVAR
- | tCVAR
-
- dsym: tSYMBEG xstring_contents tSTRING_END
- {
- result = new_dsym val[1]
- }
-
- numeric: tINTEGER
- {
- result = new_int(val[0])
- }
- | tFLOAT
- {
- result = new_float(val[0])
- }
- | '-@NUM' tINTEGER =tLOWEST
- {
- result = negate_num(new_int(val[1]))
- }
- | '-@NUM' tFLOAT =tLOWEST
- {
- result = negate_num(new_float(val[1]))
- }
- | '+@NUM' tINTEGER =tLOWEST
- {
- result = new_int(val[1])
- }
- | '+@NUM' tFLOAT =tLOWEST
- {
- result = new_float(val[1])
- }
-
- variable: tIDENTIFIER
- {
- result = new_ident(val[0])
- }
- | tIVAR
- {
- result = new_ivar(val[0])
- }
- | tGVAR
- {
- result = new_gvar(val[0])
- }
- | tCONSTANT
- {
- result = new_const(val[0])
- }
- | tCVAR
- {
- result = new_cvar(val[0])
- }
- | kNIL
- {
- result = new_nil(val[0])
- }
- | kSELF
- {
- result = new_self(val[0])
- }
- | kTRUE
- {
- result = new_true(val[0])
- }
- | kFALSE
- {
- result = new_false(val[0])
- }
- | k__FILE__
- {
- result = new___FILE__(val[0])
- }
- | k__LINE__
- {
- result = new___LINE__(val[0])
- }
-
- var_ref: variable
- {
- result = new_var_ref(val[0])
- }
-
- var_lhs: variable
- {
- result = new_assignable val[0]
- }
-
- backref: tNTH_REF
- {
- result = s(:nth_ref, value(val[0]))
- }
- | tBACK_REF
-
- superclass: term
- {
- result = nil
- }
- | tLT expr_value term
- {
- result = val[1]
- }
- | error term
- {
- result = nil
- }
-
- f_arglist: tLPAREN2 f_args opt_nl tRPAREN
- {
- result = val[1]
- lexer.lex_state = :expr_beg
- }
- | f_args term
- {
- result = val[0]
- lexer.lex_state = :expr_beg
- }
-
- kwrest_mark: tPOW
- | tDSTAR
-
- f_kwrest: kwrest_mark tIDENTIFIER
- {
- result = new_kwrestarg(val[1])
- }
- | kwrest_mark
- {
- result = new_kwrestarg()
- }
-
- f_label: tLABEL
- {
- result = new_sym(val[0])
- }
-
- f_kw: f_label arg_value
- {
- result = new_kwoptarg(val[0], val[1])
- }
- | f_label
- {
- result = new_kwarg(val[0])
- }
-
- f_kwarg: f_kw
- {
- result = [val[0]]
- }
- | f_kwarg tCOMMA f_kw
- {
- result = val[0]
- result << val[2]
- }
-
- args_tail: f_kwarg tCOMMA f_kwrest opt_f_block_arg
- {
- result = new_args_tail(val[0], val[2], val[3])
- }
- | f_kwarg opt_f_block_arg
- {
- result = new_args_tail(val[0], nil, val[1])
- }
- | f_kwrest opt_f_block_arg
- {
- result = new_args_tail(nil, val[0], val[1])
- }
- | f_block_arg
- {
- result = new_args_tail(nil, nil, val[0])
- }
-
- opt_args_tail: tCOMMA args_tail
- {
- result = val[1]
- }
- | # none
- {
- result = new_args_tail(nil, nil, nil)
- }
-
- f_args: f_arg tCOMMA f_optarg tCOMMA f_rest_arg opt_args_tail
- {
- result = new_args(val[0], val[2], val[4], val[5])
- }
- | f_arg tCOMMA f_optarg opt_args_tail
- {
- result = new_args(val[0], val[2], nil, val[3])
- }
- | f_arg tCOMMA f_rest_arg opt_args_tail
- {
- result = new_args(val[0], nil, val[2], val[3])
- }
- | f_arg opt_args_tail
- {
- result = new_args(val[0], nil, nil, val[1])
- }
- | f_optarg tCOMMA f_rest_arg opt_args_tail
- {
- result = new_args(nil, val[0], val[2], val[3])
- }
- | f_optarg opt_args_tail
- {
- result = new_args(nil, val[0], nil, val[1])
- }
- | f_rest_arg opt_args_tail
- {
- result = new_args(nil, nil, val[0], val[1])
- }
- | args_tail
- {
- result = new_args(nil, nil, nil, val[0])
- }
- | # none
- {
- result = new_args(nil, nil, nil, nil)
- }
-
- f_norm_arg: f_bad_arg
- | tIDENTIFIER
- {
- result = value(val[0]).to_sym
- scope.add_local result
- }
-
- f_bad_arg: tCONSTANT
- {
- raise 'formal argument cannot be a constant'
- }
- | tIVAR
- {
- raise 'formal argument cannot be an instance variable'
- }
- | tCVAR
- {
- raise 'formal argument cannot be a class variable'
- }
- | tGVAR
- {
- raise 'formal argument cannot be a global variable'
- }
-
- f_arg_item: f_norm_arg
- {
- result = val[0]
- }
- | tLPAREN f_margs tRPAREN
- {
- result = val[1]
- }
-
- for_var: lhs
- | mlhs
-
- f_marg: f_norm_arg
- {
- result = s(:lasgn, val[0])
- }
- | tLPAREN f_margs tRPAREN
-
- f_marg_list: f_marg
- {
- result = s(:array, val[0])
- }
- | f_marg_list tCOMMA f_marg
- {
- val[0] << val[2]
- result = val[0]
- }
-
- f_margs: f_marg_list
- | f_marg_list tCOMMA tSTAR f_norm_arg
- | f_marg_list tCOMMA tSTAR
- | tSTAR f_norm_arg
- | tSTAR
-
- f_arg: f_arg_item
- {
- result = [val[0]]
- }
- | f_arg tCOMMA f_arg_item
- {
- val[0] << val[2]
- result = val[0]
- }
-
- f_opt: tIDENTIFIER tEQL arg_value
- {
- result = new_assign(new_assignable(new_ident(val[0])), val[1], val[2])
- }
-
- f_optarg: f_opt
- {
- result = s(:block, val[0])
- }
- | f_optarg tCOMMA f_opt
- {
- result = val[0]
- val[0] << val[2]
- }
-
- restarg_mark: tSTAR2
- | tSTAR
-
- f_rest_arg: restarg_mark tIDENTIFIER
- {
- result = "*#{value(val[1])}".to_sym
- }
- | restarg_mark
- {
- result = :"*"
- }
-
- blkarg_mark: tAMPER2
- | tAMPER
-
- f_block_arg: blkarg_mark tIDENTIFIER
- {
- result = "&#{value(val[1])}".to_sym
- }
-
- opt_f_block_arg: tCOMMA f_block_arg
- {
- result = val[1]
- }
- | # none
- {
- result = nil
- }
-
- singleton: var_ref
- {
- result = val[0]
- }
- | tLPAREN2 expr opt_nl tRPAREN
- {
- result = val[1]
- }
-
- assoc_list: # none
- {
- result = []
- }
- | assocs trailer
- {
- result = val[0]
- }
-
- assocs: assoc
- {
- result = val[0]
- }
- | assocs tCOMMA assoc
- {
- result = val[0].push(*val[2])
- }
-
- assoc: arg_value tASSOC arg_value
- {
- result = [val[0], val[2]]
- }
- | tLABEL arg_value
- {
- result = [new_sym(val[0]), val[1]]
- }
-
- operation: tIDENTIFIER
- | tCONSTANT
- | tFID
-
- operation2: tIDENTIFIER
- | tCONSTANT
- | tFID
- | op
-
- operation3: tIDENTIFIER
- | tFID
- | op
-
- dot_or_colon: tDOT
- | tCOLON2
-
- opt_terms: # none
- | terms
-
- opt_nl: # none
- | tNL
-
- trailer: # none
- | tNL
- | tCOMMA
-
- term: tSEMI
- | tNL
-
- terms: term
- | terms tSEMI
-
- none: # none
- {
- result = nil
- }
-end
-
----- inner
diff --git a/test/racc/assets/opt.y b/test/racc/assets/opt.y
deleted file mode 100644
index a011953a51..0000000000
--- a/test/racc/assets/opt.y
+++ /dev/null
@@ -1,123 +0,0 @@
-#
-# check options working
-#
-
-class Calcp
-
- prechigh
- left '*' '/'
- left '+' '-'
- preclow
-
- convert
- NUMBER 'Number'
- end
-
- options no_omit_action_call no_result_var
-
-rule
-
- target : exp | /* none */ { 0 } ;
-
- exp : exp '+' exp { chk(val[0] + val[2]) }
- | exp '-' exp { chk(val[0] - val[2]) }
- | exp '*' exp { chk(val[0] * val[2]) }
- | exp '/' exp { chk(val[0] / val[2]) }
- | '(' { $emb = true } exp ')'
- {
- raise 'must not happen' unless $emb
- val[2]
- }
- | '-' NUMBER { -val[1] }
- | NUMBER
- ;
-
-end
-
-----header
-
-class Number; end
-
-----inner
-
- def parse( src )
- @src = src
- do_parse
- end
-
- def next_token
- @src.shift
- end
-
- def initialize
- @yydebug = true
- end
-
- def chk( i )
- # p i
- i
- end
-
-----footer
-
-$parser = Calcp.new
-$test_number = 1
-
-def chk( src, ans )
- result = $parser.parse(src)
- raise "test #{$test_number} failed" unless result == ans
- $test_number += 1
-end
-
-chk(
- [ [Number, 9],
- [false, false],
- [false, false] ], 9
-)
-
-chk(
- [ [Number, 5],
- ['*', nil],
- [Number, 1],
- ['-', nil],
- [Number, 1],
- ['*', nil],
- [Number, 8],
- [false, false],
- [false, false] ], -3
-)
-
-chk(
- [ [Number, 5],
- ['+', nil],
- [Number, 2],
- ['-', nil],
- [Number, 5],
- ['+', nil],
- [Number, 2],
- ['-', nil],
- [Number, 5],
- [false, false],
- [false, false] ], -1
-)
-
-chk(
- [ ['-', nil],
- [Number, 4],
- [false, false],
- [false, false] ], -4
-)
-
-chk(
- [ [Number, 7],
- ['*', nil],
- ['(', nil],
- [Number, 4],
- ['+', nil],
- [Number, 3],
- [')', nil],
- ['-', nil],
- [Number, 9],
- [false, false],
- [false, false] ], 40
-)
diff --git a/test/racc/assets/percent.y b/test/racc/assets/percent.y
deleted file mode 100644
index 68d63583ca..0000000000
--- a/test/racc/assets/percent.y
+++ /dev/null
@@ -1,35 +0,0 @@
-class ScannerChecker
-rule
- target: A
- {
- i = 7
- i %= 4
- raise 'assert failed' unless i == 3
- tmp = %-This is percent string.-
- raise 'assert failed' unless tmp == 'This is percent string.'
- a = 5; b = 3
- assert_equal(2,(a%b)) #A
- # assert_equal(2,(a %b)) # is %-string
- assert_equal(2,(a% b)) #B
- assert_equal(2,(a % b)) #C
- }
-end
-
----- inner ----
-
- def parse
- @q = [[:A, 'A'], [false, '$']]
- do_parse
- end
-
- def next_token
- @q.shift
- end
-
- def assert_equal( expect, real )
- raise "expect #{expect.inspect} but #{real.inspect}" unless expect == real
- end
-
----- footer ----
-
-parser = ScannerChecker.new.parse
diff --git a/test/racc/assets/php_serialization.y b/test/racc/assets/php_serialization.y
deleted file mode 100644
index 99f78f2081..0000000000
--- a/test/racc/assets/php_serialization.y
+++ /dev/null
@@ -1,98 +0,0 @@
-# MIT License
-# See https://github.com/divoxx/ruby-php-serialization/blob/master/LICENSE.txt
-
-class PhpSerialization::Unserializer
-rule
-
- data : null ';' { @object = val[0] }
- | bool ';' { @object = val[0] }
- | integer ';' { @object = val[0] }
- | double ';' { @object = val[0] }
- | string ';' { @object = val[0] }
- | assoc_array { @object = val[0] }
- | object { @object = val[0] }
- ;
-
- null : 'N' { result = nil }
- ;
-
- bool : 'b' ':' NUMBER { result = Integer(val[2]) > 0 }
- ;
-
- integer : 'i' ':' NUMBER { result = Integer(val[2]) }
- ;
-
- double : 'd' ':' NUMBER { result = Float(val[2]) }
- ;
-
- string : 's' ':' NUMBER ':' STRING { result = val[4] }
- ;
-
- object : 'O' ':' NUMBER ':' STRING ':' NUMBER ':' '{' attribute_list '}'
- {
- if eval("defined?(#{val[4]})")
- result = Object.const_get(val[4]).new
-
- val[9].each do |(attr_name, value)|
- # Protected and private attributes will have a \0..\0 prefix
- attr_name = attr_name.gsub(/\A\\0[^\\]+\\0/, '')
- result.instance_variable_set("@#{attr_name}", value)
- end
- else
- klass_name = val[4].gsub(/^Struct::/, '')
- attr_names, values = [], []
-
- val[9].each do |(attr_name, value)|
- # Protected and private attributes will have a \0..\0 prefix
- attr_names << attr_name.gsub(/\A\\0[^\\]+\\0/, '')
- values << value
- end
-
- result = Struct.new(klass_name, *attr_names).new(*values)
- result.instance_variable_set("@_php_class", klass_name)
- end
- }
- ;
-
- attribute_list : attribute_list attribute { result = val[0] << val[1] }
- | { result = [] }
- ;
-
- attribute : data data { result = val }
- ;
-
- assoc_array : 'a' ':' NUMBER ':' '{' attribute_list '}'
- {
- # Checks if the keys are a sequence of integers
- idx = -1
- arr = val[5].all? { |(k,v)| k == (idx += 1) }
-
- if arr
- result = val[5].map { |(k,v)| v }
- else
- result = Hash[val[5]]
- end
- }
- ;
-
-end
-
----- header ----
-require 'php_serialization/tokenizer'
-
----- inner ----
- def initialize(tokenizer_klass = Tokenizer)
- @tokenizer_klass = tokenizer_klass
- end
-
- def run(string)
- @tokenizer = @tokenizer_klass.new(string)
- yyparse(@tokenizer, :each)
- return @object
- ensure
- @tokenizer = nil
- end
-
- def next_token
- @tokenizer.next_token
- end
diff --git a/test/racc/assets/recv.y b/test/racc/assets/recv.y
deleted file mode 100644
index b6e849dda9..0000000000
--- a/test/racc/assets/recv.y
+++ /dev/null
@@ -1,97 +0,0 @@
-# s/r 5, r/r 10
-class A
-rule
-
- content: RecvH received
- ;
-
- datetime: day
- ;
-
- msgid: '<' spec '>';
-
- day:
- | ATOM ','
- ;
-
- received: recvitem_list recvdatetime
- ;
-
- recvitem_list:
- | recvitem_list recvitem
- ;
-
- recvitem: by | via | with | for ;
-
- by:
- | BY domain
- ;
-
- via:
- | VIA ATOM
- ;
-
- with: WITH ATOM
- ;
-
- for:
- | FOR addr
- ;
-
- recvdatetime:
- | ';' datetime
- ;
-
- addr: mbox | group ;
-
- mboxes: mbox
- | mboxes ',' mbox
- ;
-
- mbox: spec
- | routeaddr
- | phrase routeaddr
- ;
-
- group: phrase ':' mboxes ';'
- ;
-
- routeaddr: '<' route spec '>'
- | '<' spec '>'
- ;
-
- route: at_domains ':' ;
-
- at_domains: '@' domain
- | at_domains ',' '@' domain
- ;
-
- spec: local '@' domain
- | local
- ;
-
- local: word
- | local '.' word
- ;
-
- domain: domword
- | domain '.' domword
- ;
-
- domword: atom
- | DOMLIT
- | DIGIT
- ;
-
- phrase: word
- | phrase word
- ;
-
- word: atom
- | QUOTED
- | DIGIT
- ;
-
- atom: ATOM | FROM | BY | VIA | WITH | ID | FOR ;
-
-end
diff --git a/test/racc/assets/riml.y b/test/racc/assets/riml.y
deleted file mode 100644
index 1d99b0fdb8..0000000000
--- a/test/racc/assets/riml.y
+++ /dev/null
@@ -1,665 +0,0 @@
-# Copyright (c) 2012-2014 by Luke Gruber
-#
-# 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.
-
-class Riml::Parser
-
-token IF ELSE ELSEIF THEN UNLESS END
-token WHILE UNTIL BREAK CONTINUE
-token TRY CATCH FINALLY
-token FOR IN
-token DEF DEF_BANG SPLAT_PARAM SPLAT_ARG CALL BUILTIN_COMMAND # such as echo "hi"
-token CLASS NEW DEFM DEFM_BANG SUPER
-token RIML_FILE_COMMAND RIML_CLASS_COMMAND
-token RETURN
-token NEWLINE
-token NUMBER
-token STRING_D STRING_S # single- and double-quoted
-token EX_LITERAL
-token REGEXP
-token TRUE FALSE
-token LET UNLET UNLET_BANG IDENTIFIER
-token DICT_VAL # like dict.key, 'key' is a DICT_VAL
-token SCOPE_MODIFIER SCOPE_MODIFIER_LITERAL SPECIAL_VAR_PREFIX
-token FINISH
-
-prechigh
- right '!'
- left '*' '/' '%'
- left '+' '-' '.'
- left '>' '>#' '>?' '<' '<#' '<?' '>=' '>=#' '>=?' '<=' '<=#' '<=?'
- left '==' '==?' '==#' '=~' '=~?' '=~#' '!~' '!~?' '!~#' '!=' '!=?' '!=#'
- left IS ISNOT
- left '&&'
- left '||'
- right '?'
- right '=' '+=' '-=' '.='
- left ','
- left IF UNLESS
-preclow
-
-# All rules
-rule
-
- Root:
- /* nothing */ { result = make_node(val) { |_| Riml::Nodes.new([]) } }
- | Terminator { result = make_node(val) { |_| Riml::Nodes.new([]) } }
- | Statements { result = val[0] }
- ;
-
- # any list of expressions
- Statements:
- Statement { result = make_node(val) { |v| Riml::Nodes.new([ v[0] ]) } }
- | Statements Terminator Statement { result = val[0] << val[2] }
- | Statements Terminator { result = val[0] }
- | Terminator Statements { result = make_node(val) { |v| Riml::Nodes.new(v[1]) } }
- ;
-
- # All types of expressions in Riml
- Statement:
- ExplicitCall { result = val[0] }
- | Def { result = val[0] }
- | Return { result = val[0] }
- | UnletVariable { result = val[0] }
- | ExLiteral { result = val[0] }
- | For { result = val[0] }
- | While { result = val[0] }
- | Until { result = val[0] }
- | Try { result = val[0] }
- | ClassDefinition { result = val[0] }
- | LoopKeyword { result = val[0] }
- | EndScript { result = val[0] }
- | RimlFileCommand { result = val[0] }
- | RimlClassCommand { result = val[0] }
- | MultiAssign { result = val[0] }
- | If { result = val[0] }
- | Unless { result = val[0] }
- | Expression { result = val[0] }
- ;
-
- Expression:
- ExpressionWithoutDictLiteral { result = val[0] }
- | Dictionary { result = val[0] }
- | Dictionary DictGetWithDotLiteral { result = make_node(val) { |v| Riml::DictGetDotNode.new(v[0], v[1]) } }
- | BinaryOperator { result = val[0] }
- | Ternary { result = val[0] }
- | Assign { result = val[0] }
- | Super { result = val[0] }
- | '(' Expression ')' { result = make_node(val) { |v| Riml::WrapInParensNode.new(v[1]) } }
- ;
-
- ExpressionWithoutDictLiteral:
- UnaryOperator { result = val[0] }
- | DictGet { result = val[0] }
- | ListOrDictGet { result = val[0] }
- | AllVariableRetrieval { result = val[0] }
- | LiteralWithoutDictLiteral { result = val[0] }
- | Call { result = val[0] }
- | ObjectInstantiation { result = val[0] }
- | '(' ExpressionWithoutDictLiteral ')' { result = make_node(val) { |v| Riml::WrapInParensNode.new(v[1]) } }
- ;
-
- # for inside curly-brace variable names
- PossibleStringValue:
- String { result = val[0] }
- | DictGet { result = val[0] }
- | ListOrDictGet { result = val[0] }
- | AllVariableRetrieval { result = val[0] }
- | BinaryOperator { result = val[0] }
- | Ternary { result = val[0] }
- | Call { result = val[0] }
- ;
-
- Terminator:
- NEWLINE { result = nil }
- | ';' { result = nil }
- ;
-
- LiteralWithoutDictLiteral:
- Number { result = val[0] }
- | String { result = val[0] }
- | Regexp { result = val[0] }
- | List { result = val[0] }
- | ScopeModifierLiteral { result = val[0] }
- | TRUE { result = make_node(val) { |_| Riml::TrueNode.new } }
- | FALSE { result = make_node(val) { |_| Riml::FalseNode.new } }
- ;
-
- Number:
- NUMBER { result = make_node(val) { |v| Riml::NumberNode.new(v[0]) } }
- ;
-
- String:
- STRING_S { result = make_node(val) { |v| Riml::StringNode.new(v[0], :s) } }
- | STRING_D { result = make_node(val) { |v| Riml::StringNode.new(v[0], :d) } }
- | String STRING_S { result = make_node(val) { |v| Riml::StringLiteralConcatNode.new(v[0], Riml::StringNode.new(v[1], :s)) } }
- | String STRING_D { result = make_node(val) { |v| Riml::StringLiteralConcatNode.new(v[0], Riml::StringNode.new(v[1], :d)) } }
- ;
-
- Regexp:
- REGEXP { result = make_node(val) { |v| Riml::RegexpNode.new(v[0]) } }
- ;
-
- ScopeModifierLiteral:
- SCOPE_MODIFIER_LITERAL { result = make_node(val) { |v| Riml::ScopeModifierLiteralNode.new(v[0]) } }
- ;
-
- List:
- ListLiteral { result = make_node(val) { |v| Riml::ListNode.new(v[0]) } }
- ;
-
- ListUnpack:
- '[' ListItems ';' Expression ']' { result = make_node(val) { |v| Riml::ListUnpackNode.new(v[1] << v[3]) } }
- ;
-
- ListLiteral:
- '[' ListItems ']' { result = val[1] }
- | '[' ListItems ',' ']' { result = val[1] }
- ;
-
- ListItems:
- /* nothing */ { result = [] }
- | Expression { result = [val[0]] }
- | ListItems ',' Expression { result = val[0] << val[2] }
- ;
-
- Dictionary:
- DictionaryLiteral { result = make_node(val) { |v| Riml::DictionaryNode.new(v[0]) } }
- ;
-
- # {'key': 'value', 'key2': 'value2'}
- # Save as [['key', 'value'], ['key2', 'value2']] because ruby-1.8.7 offers
- # no guarantee for key-value pair ordering.
- DictionaryLiteral:
- '{' DictItems '}' { result = val[1] }
- | '{' DictItems ',' '}' { result = val[1] }
- ;
-
- # [[key, value], [key, value]]
- DictItems:
- /* nothing */ { result = [] }
- | DictItem { result = val }
- | DictItems ',' DictItem { result = val[0] << val[2] }
- ;
-
- # [key, value]
- DictItem:
- Expression ':' Expression { result = [val[0], val[2]] }
- ;
-
- DictGet:
- AllVariableRetrieval DictGetWithDot { result = make_node(val) { |v| Riml::DictGetDotNode.new(v[0], v[1]) } }
- | ListOrDictGet DictGetWithDot { result = make_node(val) { |v| Riml::DictGetDotNode.new(v[0], v[1]) } }
- | Call DictGetWithDot { result = make_node(val) { |v| Riml::DictGetDotNode.new(v[0], v[1]) } }
- | '(' Expression ')' DictGetWithDot { result = make_node(val) { |v| Riml::DictGetDotNode.new(Riml::WrapInParensNode.new(v[1]), v[3]) } }
- ;
-
- ListOrDictGet:
- ExpressionWithoutDictLiteral ListOrDictGetWithBrackets { result = make_node(val) { |v| Riml::ListOrDictGetNode.new(v[0], v[1]) } }
- | '(' Expression ')' ListOrDictGetWithBrackets { result = make_node(val) { |v| Riml::ListOrDictGetNode.new(Riml::WrapInParensNode.new(v[1]), v[3]) } }
- ;
-
- ListOrDictGetAssign:
- ExpressionWithoutDictLiteral ListOrDictGetWithBrackets { result = make_node(val) { |v| Riml::ListOrDictGetNode.new(v[0], v[1]) } }
- ;
-
- ListOrDictGetWithBrackets:
- '[' Expression ']' { result = [val[1]] }
- | '[' SubList ']' { result = [val[1]] }
- | ListOrDictGetWithBrackets '[' Expression ']' { result = val[0] << val[2] }
- | ListOrDictGetWithBrackets '[' SubList ']' { result = val[0] << val[2] }
- ;
-
- SubList:
- Expression ':' Expression { result = make_node(val) { |v| Riml::SublistNode.new([v[0], Riml::LiteralNode.new(' : '), v[2]]) } }
- | Expression ':' { result = make_node(val) { |v| Riml::SublistNode.new([v[0], Riml::LiteralNode.new(' :')]) } }
- | ':' Expression { result = make_node(val) { |v| Riml::SublistNode.new([Riml::LiteralNode.new(': '), v[1]]) } }
- | ':' { result = make_node(val) { |_| Riml::SublistNode.new([Riml::LiteralNode.new(':')]) } }
- ;
-
- DictGetWithDot:
- DICT_VAL { result = [val[0]] }
- | DictGetWithDot DICT_VAL { result = val[0] << val[1] }
- ;
-
- DictGetWithDotLiteral:
- '.' IDENTIFIER { result = [val[1]] }
- | DictGetWithDotLiteral DICT_VAL { result = val[0] << val[1] }
- ;
-
- Call:
- Scope DefCallIdentifier '(' ArgList ')' { result = make_node(val) { |v| Riml::CallNode.new(v[0], v[1], v[3]) } }
- | DictGet '(' ArgList ')' { result = make_node(val) { |v| Riml::CallNode.new(nil, v[0], v[2]) } }
- | BUILTIN_COMMAND '(' ArgList ')' { result = make_node(val) { |v| Riml::CallNode.new(nil, v[0], v[2]) } }
- | BUILTIN_COMMAND ArgListWithoutNothing { result = make_node(val) { |v| Riml::CallNode.new(nil, v[0], v[1]) } }
- | BUILTIN_COMMAND NEWLINE { result = make_node(val) { |v| Riml::CallNode.new(nil, v[0], []) } }
- | CALL '(' ArgList ')' { result = make_node(val) { |v| Riml::ExplicitCallNode.new(nil, nil, v[2]) } }
- ;
-
- ObjectInstantiationCall:
- Scope DefCallIdentifier '(' ArgList ')' { result = make_node(val) { |v| Riml::CallNode.new(v[0], v[1], v[3]) } }
- | Scope DefCallIdentifier { result = make_node(val) { |v| Riml::CallNode.new(v[0], v[1], []) } }
- ;
-
- RimlFileCommand:
- RIML_FILE_COMMAND '(' ArgList ')' { result = make_node(val) { |v| Riml::RimlFileCommandNode.new(nil, v[0], v[2]) } }
- | RIML_FILE_COMMAND ArgList { result = make_node(val) { |v| Riml::RimlFileCommandNode.new(nil, v[0], v[1]) } }
- ;
-
- RimlClassCommand:
- RIML_CLASS_COMMAND '(' ClassArgList ')' { result = make_node(val) { |v| Riml::RimlClassCommandNode.new(nil, v[0], v[2]) } }
- | RIML_CLASS_COMMAND ClassArgList { result = make_node(val) { |v| Riml::RimlClassCommandNode.new(nil, v[0], v[1]) } }
- ;
-
- ClassArgList:
- Scope IDENTIFIER { result = ["#{val[0]}#{val[1]}"] }
- | String { result = val }
- | ClassArgList ',' Scope IDENTIFIER { result = val[0].concat ["#{val[2]}#{val[3]}"] }
- ;
-
- ExplicitCall:
- CALL Scope DefCallIdentifier '(' ArgList ')' { result = make_node(val) { |v| Riml::ExplicitCallNode.new(v[1], v[2], v[4]) } }
- | CALL DictGet '(' ArgList ')' { result = make_node(val) { |v| Riml::ExplicitCallNode.new(nil, v[1], v[3]) } }
- ;
-
- Scope:
- SCOPE_MODIFIER { result = val[0] }
- | /* nothing */ { result = nil }
- ;
-
- # [SID, scope_modifier]
- SIDAndScope:
- Scope { result = [ nil, val[0] ] }
- | '<' IDENTIFIER '>' Scope { result = [ make_node(val) { |v| Riml::SIDNode.new(v[1]) }, val[3] ] }
- ;
-
- ArgList:
- /* nothing */ { result = [] }
- | ArgListWithoutNothingWithSplat { result = val[0] }
- ;
-
- ArgListWithSplat:
- /* nothing */ { result = [] }
- | ArgListWithoutNothingWithSplat { result = val[0] }
- ;
-
- ArgListWithoutNothingWithSplat:
- Expression { result = val }
- | SPLAT_ARG Expression { result = [ make_node(val) { |v| Riml::SplatNode.new(v[1]) } ] }
- | ArgListWithoutNothingWithSplat "," Expression { result = val[0] << val[2] }
- | ArgListWithoutNothingWithSplat "," SPLAT_ARG Expression { result = val[0] << make_node(val) { |v| Riml::SplatNode.new(v[3]) } }
- ;
-
- ArgListWithoutNothing:
- Expression { result = val }
- | ArgListWithoutNothing "," Expression { result = val[0] << val[2] }
- ;
-
- BinaryOperator:
- Expression '||' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
- | Expression '&&' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
-
- | Expression '==' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
- | Expression '==#' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
- | Expression '==?' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
-
- # added by riml
- | Expression '===' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
-
- | Expression '!=' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
- | Expression '!=#' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
- | Expression '!=?' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
-
- | Expression '=~' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
- | Expression '=~#' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
- | Expression '=~?' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
-
- | Expression '!~' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
- | Expression '!~#' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
- | Expression '!~?' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
-
- | Expression '>' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
- | Expression '>#' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
- | Expression '>?' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
-
- | Expression '>=' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
- | Expression '>=#' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
- | Expression '>=?' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
-
- | Expression '<' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
- | Expression '<#' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
- | Expression '<?' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
-
- | Expression '<=' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
- | Expression '<=#' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
- | Expression '<=?' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
-
- | Expression '+' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
- | Expression '-' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
- | Expression '*' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
- | Expression '/' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
- | Expression '.' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
- | Expression '%' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
-
- | Expression IS Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
- | Expression ISNOT Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
- ;
-
- UnaryOperator:
- '!' Expression { result = make_node(val) { |v| Riml::UnaryOperatorNode.new(val[0], [val[1]]) } }
- | '+' Expression { result = make_node(val) { |v| Riml::UnaryOperatorNode.new(val[0], [val[1]]) } }
- | '-' Expression { result = make_node(val) { |v| Riml::UnaryOperatorNode.new(val[0], [val[1]]) } }
- ;
-
- # ['=', LHS, RHS]
- Assign:
- LET AssignExpression { result = make_node(val) { |v| Riml::AssignNode.new(v[1][0], v[1][1], v[1][2]) } }
- | AssignExpression { result = make_node(val) { |v| Riml::AssignNode.new(v[0][0], v[0][1], v[0][2]) } }
- ;
-
- MultiAssign:
- Assign ',' Assign { result = make_node(val) { |v| Riml::MultiAssignNode.new([v[0], v[2]]) } }
- | MultiAssign ',' Assign { val[0].assigns << val[2]; result = val[0] }
- ;
-
- # ['=', AssignLHS, Expression]
- AssignExpression:
- AssignLHS '=' Expression { result = [val[1], val[0], val[2]] }
- | AssignLHS '+=' Expression { result = [val[1], val[0], val[2]] }
- | AssignLHS '-=' Expression { result = [val[1], val[0], val[2]] }
- | AssignLHS '.=' Expression { result = [val[1], val[0], val[2]] }
- ;
-
- AssignLHS:
- AllVariableRetrieval { result = val[0] }
- | List { result = val[0] }
- | ListUnpack { result = val[0] }
- | DictGet { result = val[0] }
- | ListOrDictGetAssign { result = val[0] }
- ;
-
- # retrieving the value of a variable
- VariableRetrieval:
- SimpleVariableRetrieval { result = val[0] }
- | SPECIAL_VAR_PREFIX IDENTIFIER { result = make_node(val) { |v| Riml::GetSpecialVariableNode.new(v[0], v[1]) } }
- | ScopeModifierLiteral ListOrDictGetWithBrackets { result = make_node(val) { |v| Riml::GetVariableByScopeAndDictNameNode.new(v[0], v[1]) } }
- ;
-
- SimpleVariableRetrieval:
- Scope IDENTIFIER { result = make_node(val) { |v| Riml::GetVariableNode.new(v[0], v[1]) } }
- ;
-
- AllVariableRetrieval:
- VariableRetrieval { result = val[0] }
- | Scope CurlyBraceName { result = make_node(val) { |v| Riml::GetCurlyBraceNameNode.new(v[0], v[1]) } }
- ;
-
- UnletVariable:
- UNLET VariableRetrieval { result = make_node(val) { |v| Riml::UnletVariableNode.new('!', [ v[1] ]) } }
- | UNLET_BANG VariableRetrieval { result = make_node(val) { |v| Riml::UnletVariableNode.new('!', [ v[1] ]) } }
- | UnletVariable VariableRetrieval { result = val[0] << val[1] }
- ;
-
- CurlyBraceName:
- CurlyBraceVarPart { result = make_node(val) { |v| Riml::CurlyBraceVariable.new([ v[0] ]) } }
- | IDENTIFIER CurlyBraceName { result = make_node(val) { |v| Riml::CurlyBraceVariable.new([ Riml::CurlyBracePart.new(v[0]), v[1] ]) } }
- | CurlyBraceName IDENTIFIER { result = val[0] << make_node(val) { |v| Riml::CurlyBracePart.new(v[1]) } }
- | CurlyBraceName CurlyBraceVarPart { result = val[0] << val[1] }
- ;
-
- CurlyBraceVarPart:
- '{' PossibleStringValue '}' { result = make_node(val) { |v| Riml::CurlyBracePart.new(v[1]) } }
- | '{' PossibleStringValue CurlyBraceVarPart '}' { result = make_node(val) { |v| Riml::CurlyBracePart.new([v[1], v[2]]) } }
- | '{' CurlyBraceVarPart PossibleStringValue '}' { result = make_node(val) { |v| Riml::CurlyBracePart.new([v[1], v[2]]) } }
- ;
-
- # Method definition
- # [SID, scope_modifier, name, parameters, keyword, expressions]
- Def:
- FunctionType SIDAndScope DefCallIdentifier DefKeywords Block END { result = make_node(val) { |v| Riml.const_get(val[0]).new('!', v[1][0], v[1][1], v[2], [], v[3], v[4]) } }
- | FunctionType SIDAndScope DefCallIdentifier '(' ParamList ')' DefKeywords Block END { result = make_node(val) { |v| Riml.const_get(val[0]).new('!', v[1][0], v[1][1], v[2], v[4], v[6], v[7]) } }
- | FunctionType SIDAndScope DefCallIdentifier '(' SPLAT_PARAM ')' DefKeywords Block END { result = make_node(val) { |v| Riml.const_get(val[0]).new('!', v[1][0], v[1][1], v[2], [v[4]], v[6], v[7]) } }
- | FunctionType SIDAndScope DefCallIdentifier '(' ParamList ',' SPLAT_PARAM ')' DefKeywords Block END { result = make_node(val) { |v| Riml.const_get(val[0]).new('!', v[1][0], v[1][1], v[2], v[4] << v[6], v[8], v[9]) } }
- ;
-
- FunctionType:
- DEF { result = "DefNode" }
- | DEF_BANG { result = "DefNode" }
- | DEFM { result = "DefMethodNode" }
- ;
-
- DefCallIdentifier:
- # use '' for first argument instead of nil in order to avoid a double scope-modifier
- CurlyBraceName { result = make_node(val) { |v| Riml::GetCurlyBraceNameNode.new('', v[0]) } }
- | IDENTIFIER { result = val[0] }
- ;
-
- # Example: 'range', 'dict' or 'abort' after function definition
- DefKeywords:
- IDENTIFIER { result = [val[0]] }
- | DefKeywords IDENTIFIER { result = val[0] << val[1] }
- | /* nothing */ { result = nil }
- ;
-
- ParamList:
- /* nothing */ { result = [] }
- | IDENTIFIER { result = val }
- | DefaultParam { result = val }
- | ParamList ',' IDENTIFIER { result = val[0] << val[2] }
- | ParamList ',' DefaultParam { result = val[0] << val[2] }
- ;
-
- DefaultParam:
- IDENTIFIER '=' Expression { result = make_node(val) { |v| Riml::DefaultParamNode.new(v[0], v[2]) } }
- ;
-
- Return:
- RETURN Returnable { result = make_node(val) { |v| Riml::ReturnNode.new(v[1]) } }
- | RETURN Returnable IF Expression { result = make_node(val) { |v| Riml::IfNode.new(v[3], Nodes.new([ReturnNode.new(v[1])])) } }
- | RETURN Returnable UNLESS Expression { result = make_node(val) { |v| Riml::UnlessNode.new(v[3], Nodes.new([ReturnNode.new(v[1])])) } }
- ;
-
- Returnable:
- /* nothing */ { result = nil }
- | Expression { result = val[0] }
- ;
-
- EndScript:
- FINISH { result = make_node(val) { |_| Riml::FinishNode.new } }
- ;
-
- # [expression, expressions]
- If:
- IF Expression IfBlock END { result = make_node(val) { |v| Riml::IfNode.new(v[1], v[2]) } }
- | IF Expression THEN Expression END { result = make_node(val) { |v| Riml::IfNode.new(v[1], Riml::Nodes.new([v[3]])) } }
- | Expression IF Expression { result = make_node(val) { |v| Riml::IfNode.new(v[2], Riml::Nodes.new([v[0]])) } }
- ;
-
- Unless:
- UNLESS Expression IfBlock END { result = make_node(val) { |v| Riml::UnlessNode.new(v[1], v[2]) } }
- | UNLESS Expression THEN Expression END { result = make_node(val) { |v| Riml::UnlessNode.new(v[1], Riml::Nodes.new([v[3]])) } }
- | Expression UNLESS Expression { result = make_node(val) { |v| Riml::UnlessNode.new(v[2], Riml::Nodes.new([v[0]])) } }
- ;
-
- Ternary:
- Expression '?' Expression ':' Expression { result = make_node(val) { |v| Riml::TernaryOperatorNode.new([v[0], v[2], v[4]]) } }
- ;
-
- While:
- WHILE Expression Block END { result = make_node(val) { |v| Riml::WhileNode.new(v[1], v[2]) } }
- ;
-
- LoopKeyword:
- BREAK { result = make_node(val) { |_| Riml::BreakNode.new } }
- | CONTINUE { result = make_node(val) { |_| Riml::ContinueNode.new } }
- ;
-
- Until:
- UNTIL Expression Block END { result = make_node(val) { |v| Riml::UntilNode.new(v[1], v[2]) } }
- ;
-
- For:
- FOR SimpleVariableRetrieval IN Expression Block END { result = make_node(val) { |v| Riml::ForNode.new(v[1], v[3], v[4]) } }
- | FOR List IN Expression Block END { result = make_node(val) { |v| Riml::ForNode.new(v[1], v[3], v[4]) } }
- | FOR ListUnpack IN Expression Block END { result = make_node(val) { |v| Riml::ForNode.new(v[1], v[3], v[4]) } }
- ;
-
- Try:
- TRY Block END { result = make_node(val) { |v| Riml::TryNode.new(v[1], nil, nil) } }
- | TRY Block Catch END { result = make_node(val) { |v| Riml::TryNode.new(v[1], v[2], nil) } }
- | TRY Block Catch FINALLY Block END { result = make_node(val) { |v| Riml::TryNode.new(v[1], v[2], v[4]) } }
- ;
-
- Catch:
- /* nothing */ { result = nil }
- | CATCH Block { result = [ make_node(val) { |v| Riml::CatchNode.new(nil, v[1]) } ] }
- | CATCH Catchable Block { result = [ make_node(val) { |v| Riml::CatchNode.new(v[1], v[2]) } ] }
- | Catch CATCH Block { result = val[0] << make_node(val) { |v| Riml::CatchNode.new(nil, v[2]) } }
- | Catch CATCH Catchable Block { result = val[0] << make_node(val) { |v| Riml::CatchNode.new(v[2], v[3]) } }
- ;
-
- Catchable:
- Regexp { result = val[0] }
- | String { result = val[0] }
- ;
-
- # [expressions]
- # expressions list could contain an ElseNode, which contains expressions
- # itself
- Block:
- NEWLINE Statements { result = val[1] }
- | NEWLINE { result = make_node(val) { |_| Riml::Nodes.new([]) } }
- ;
-
- IfBlock:
- Block { result = val[0] }
- | NEWLINE Statements ElseBlock { result = val[1] << val[2] }
- | NEWLINE Statements ElseifBlock { result = val[1] << val[2] }
- | NEWLINE Statements ElseifBlock ElseBlock { result = val[1] << val[2] << val[3] }
- ;
-
- ElseBlock:
- ELSE NEWLINE Statements { result = make_node(val) { |v| Riml::ElseNode.new(v[2]) } }
- ;
-
- ElseifBlock:
- ELSEIF Expression NEWLINE Statements { result = make_node(val) { |v| Riml::Nodes.new([Riml::ElseifNode.new(v[1], v[3])]) } }
- | ElseifBlock ELSEIF Expression NEWLINE Statements { result = val[0] << make_node(val) { |v| Riml::ElseifNode.new(v[2], v[4]) } }
- ;
-
- ClassDefinition:
- CLASS Scope IDENTIFIER Block END { result = make_node(val) { |v| Riml::ClassDefinitionNode.new(v[1], v[2], nil, v[3]) } }
- | CLASS Scope IDENTIFIER '<' Scope IDENTIFIER Block END { result = make_node(val) { |v| Riml::ClassDefinitionNode.new(v[1], v[2], (v[4] || ClassDefinitionNode::DEFAULT_SCOPE_MODIFIER) + v[5], v[6]) } }
- ;
-
- ObjectInstantiation:
- NEW ObjectInstantiationCall { result = make_node(val) { |v| Riml::ObjectInstantiationNode.new(v[1]) } }
- ;
-
- Super:
- SUPER '(' ArgListWithSplat ')' { result = make_node(val) { |v| Riml::SuperNode.new(v[2], true) } }
- | SUPER { result = make_node(val) { |_| Riml::SuperNode.new([], false) } }
- ;
-
- ExLiteral:
- EX_LITERAL { result = make_node(val) { |v| Riml::ExLiteralNode.new(v[0]) } }
- ;
-end
-
----- header
- require File.expand_path("../lexer", __FILE__)
- require File.expand_path("../nodes", __FILE__)
- require File.expand_path("../errors", __FILE__)
- require File.expand_path("../ast_rewriter", __FILE__)
----- inner
- # This code will be put as-is in the parser class
-
- attr_accessor :ast_rewriter
- attr_writer :options
-
- # The Parser and AST_Rewriter share this same hash of options
- def options
- @options ||= {}
- end
-
- def self.ast_cache
- @ast_cache
- end
- @ast_cache = {}
-
- # parses tokens or code into output nodes
- def parse(object, ast_rewriter = Riml::AST_Rewriter.new, filename = nil, included = false)
- if (ast = self.class.ast_cache[filename])
- else
- if tokens?(object)
- @tokens = object
- elsif code?(object)
- @lexer = Riml::Lexer.new(object, filename, true)
- end
-
- begin
- ast = do_parse
- rescue Racc::ParseError => e
- raise unless @lexer
- if (invalid_token = @lexer.prev_token_is_keyword?)
- warning = "#{invalid_token.inspect} is a keyword, and cannot " \
- "be used as a variable name"
- end
- error_msg = e.message
- error_msg << "\nWARNING: #{warning}" if warning
- error = Riml::ParseError.new(error_msg, @lexer.filename, @lexer.lineno)
- raise error
- end
- self.class.ast_cache[filename] = ast if filename
- end
- @ast_rewriter ||= ast_rewriter
- return ast unless @ast_rewriter
- @ast_rewriter.ast = ast.dup
- @ast_rewriter.options ||= options
- @ast_rewriter.rewrite(filename, included)
- @ast_rewriter.ast
- end
-
- # get the next token from either the list of tokens provided, or
- # the lexer getting the next token
- def next_token
- return @tokens.shift unless @lexer
- token = @lexer.next_token
- if token && @lexer.parser_info
- @current_parser_info = token.pop
- end
- token
- end
-
- private
-
- def tokens?(object)
- Array === object
- end
-
- def code?(object)
- String === object
- end
-
- def make_node(racc_val)
- node = yield racc_val
- node.parser_info = @current_parser_info
- node
- end
diff --git a/test/racc/assets/rrconf.y b/test/racc/assets/rrconf.y
deleted file mode 100644
index baf9249a77..0000000000
--- a/test/racc/assets/rrconf.y
+++ /dev/null
@@ -1,14 +0,0 @@
-# 1 s/r conflict and 1 r/r conflict
-
-class A
-rule
-
-target: a
-
-a :
- | a list
-
-list :
- | list ITEM
-
-end
diff --git a/test/racc/assets/ruby18.y b/test/racc/assets/ruby18.y
deleted file mode 100644
index eceb253298..0000000000
--- a/test/racc/assets/ruby18.y
+++ /dev/null
@@ -1,1943 +0,0 @@
-# Copyright (c) 2013 Peter Zotov <whitequark@whitequark.org>
-#
-# Parts of the source are derived from ruby_parser:
-# Copyright (c) Ryan Davis, seattle.rb
-#
-# MIT License
-#
-# 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.
-
-class Parser::Ruby18
-
-token kCLASS kMODULE kDEF kUNDEF kBEGIN kRESCUE kENSURE kEND kIF kUNLESS
- kTHEN kELSIF kELSE kCASE kWHEN kWHILE kUNTIL kFOR kBREAK kNEXT
- kREDO kRETRY kIN kDO kDO_COND kDO_BLOCK kRETURN kYIELD kSUPER
- kSELF kNIL kTRUE kFALSE kAND kOR kNOT kIF_MOD kUNLESS_MOD kWHILE_MOD
- kUNTIL_MOD kRESCUE_MOD kALIAS kDEFINED klBEGIN klEND k__LINE__
- k__FILE__ tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR tNTH_REF
- tBACK_REF tSTRING_CONTENT tINTEGER tFLOAT tREGEXP_END tUPLUS
- tUMINUS tUMINUS_NUM tPOW tCMP tEQ tEQQ tNEQ tGEQ tLEQ tANDOP
- tOROP tMATCH tNMATCH tDOT tDOT2 tDOT3 tAREF tASET tLSHFT tRSHFT
- tCOLON2 tCOLON3 tOP_ASGN tASSOC tLPAREN tLPAREN2 tRPAREN tLPAREN_ARG
- tLBRACK tLBRACK2 tRBRACK tLBRACE tLBRACE_ARG tSTAR tSTAR2 tAMPER tAMPER2
- tTILDE tPERCENT tDIVIDE tPLUS tMINUS tLT tGT tPIPE tBANG tCARET
- tLCURLY tRCURLY tBACK_REF2 tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG
- tWORDS_BEG tQWORDS_BEG tSTRING_DBEG tSTRING_DVAR tSTRING_END tSTRING
- tSYMBOL tREGEXP_OPT tNL tEH tCOLON tCOMMA tSPACE tSEMI
-
-prechigh
- right tBANG tTILDE tUPLUS
- right tPOW
- right tUMINUS_NUM tUMINUS
- left tSTAR2 tDIVIDE tPERCENT
- left tPLUS tMINUS
- left tLSHFT tRSHFT
- left tAMPER2
- left tPIPE tCARET
- left tGT tGEQ tLT tLEQ
- nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH
- left tANDOP
- left tOROP
- nonassoc tDOT2 tDOT3
- right tEH tCOLON
- left kRESCUE_MOD
- right tEQL tOP_ASGN
- nonassoc kDEFINED
- right kNOT
- left kOR kAND
- nonassoc kIF_MOD kUNLESS_MOD kWHILE_MOD kUNTIL_MOD
- nonassoc tLBRACE_ARG
- nonassoc tLOWEST
-preclow
-
-rule
-
- program: compstmt
- {
- result = val[0]
- }
-
- bodystmt: compstmt opt_rescue opt_else opt_ensure
- {
- rescue_bodies = val[1]
- else_t, else_ = val[2]
- ensure_t, ensure_ = val[3]
-
- if rescue_bodies.empty? && !else_.nil?
- diagnostic :warning, :useless_else, nil, else_t
- end
-
- result = @builder.begin_body(val[0],
- rescue_bodies,
- else_t, else_,
- ensure_t, ensure_)
- }
-
- compstmt: stmts opt_terms
- {
- result = @builder.compstmt(val[0])
- }
-
- stmts: # nothing
- {
- result = []
- }
- | stmt
- {
- result = [ val[0] ]
- }
- | error stmt
- {
- result = [ val[1] ]
- }
- | stmts terms stmt
- {
- result = val[0] << val[2]
- }
-
- stmt: kALIAS fitem
- {
- @lexer.state = :expr_fname
- }
- fitem
- {
- result = @builder.alias(val[0], val[1], val[3])
- }
- | kALIAS tGVAR tGVAR
- {
- result = @builder.alias(val[0],
- @builder.gvar(val[1]),
- @builder.gvar(val[2]))
- }
- | kALIAS tGVAR tBACK_REF
- {
- result = @builder.alias(val[0],
- @builder.gvar(val[1]),
- @builder.back_ref(val[2]))
- }
- | kALIAS tGVAR tNTH_REF
- {
- diagnostic :error, :nth_ref_alias, nil, val[2]
- }
- | kUNDEF undef_list
- {
- result = @builder.undef_method(val[0], val[1])
- }
- | stmt kIF_MOD expr_value
- {
- result = @builder.condition_mod(val[0], nil,
- val[1], val[2])
- }
- | stmt kUNLESS_MOD expr_value
- {
- result = @builder.condition_mod(nil, val[0],
- val[1], val[2])
- }
- | stmt kWHILE_MOD expr_value
- {
- result = @builder.loop_mod(:while, val[0], val[1], val[2])
- }
- | stmt kUNTIL_MOD expr_value
- {
- result = @builder.loop_mod(:until, val[0], val[1], val[2])
- }
- | stmt kRESCUE_MOD stmt
- {
- rescue_body = @builder.rescue_body(val[1],
- nil, nil, nil,
- nil, val[2])
-
- result = @builder.begin_body(val[0], [ rescue_body ])
- }
- | klBEGIN tLCURLY compstmt tRCURLY
- {
- if in_def?
- diagnostic :error, :begin_in_method, nil, val[0]
- end
-
- result = @builder.preexe(val[0], val[1], val[2], val[3])
- }
- | klEND tLCURLY compstmt tRCURLY
- {
- result = @builder.postexe(val[0], val[1], val[2], val[3])
- }
- | lhs tEQL command_call
- {
- result = @builder.assign(val[0], val[1], val[2])
- }
- | mlhs tEQL command_call
- {
- result = @builder.multi_assign(val[0], val[1], val[2])
- }
- | var_lhs tOP_ASGN command_call
- {
- result = @builder.op_assign(val[0], val[1], val[2])
- }
- | primary_value tLBRACK2 aref_args tRBRACK tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.index(
- val[0], val[1], val[2], val[3]),
- val[4], val[5])
- }
- | primary_value tDOT tIDENTIFIER tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tDOT tCONSTANT tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | backref tOP_ASGN command_call
- {
- @builder.op_assign(val[0], val[1], val[2])
- }
- | lhs tEQL mrhs
- {
- result = @builder.assign(val[0], val[1],
- @builder.array(nil, val[2], nil))
- }
- | mlhs tEQL arg_value
- {
- result = @builder.multi_assign(val[0], val[1], val[2])
- }
- | mlhs tEQL mrhs
- {
- result = @builder.multi_assign(val[0], val[1],
- @builder.array(nil, val[2], nil))
- }
- | expr
-
- expr: command_call
- | expr kAND expr
- {
- result = @builder.logical_op(:and, val[0], val[1], val[2])
- }
- | expr kOR expr
- {
- result = @builder.logical_op(:or, val[0], val[1], val[2])
- }
- | kNOT expr
- {
- result = @builder.not_op(val[0], nil, val[1], nil)
- }
- | tBANG command_call
- {
- result = @builder.not_op(val[0], nil, val[1], nil)
- }
- | arg
-
- expr_value: expr
-
- command_call: command
- | block_command
- | kRETURN call_args
- {
- result = @builder.keyword_cmd(:return, val[0],
- nil, val[1], nil)
- }
- | kBREAK call_args
- {
- result = @builder.keyword_cmd(:break, val[0],
- nil, val[1], nil)
- }
- | kNEXT call_args
- {
- result = @builder.keyword_cmd(:next, val[0],
- nil, val[1], nil)
- }
-
- block_command: block_call
- | block_call tDOT operation2 command_args
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
- }
- | block_call tCOLON2 operation2 command_args
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
- }
-
- cmd_brace_block: tLBRACE_ARG
- {
- @static_env.extend_dynamic
- }
- opt_block_var compstmt tRCURLY
- {
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
- }
-
- command: operation command_args =tLOWEST
- {
- lparen_t, args, rparen_t = val[1]
- result = @builder.call_method(nil, nil, val[0],
- lparen_t, args, rparen_t)
- }
- | operation command_args cmd_brace_block
- {
- lparen_t, args, rparen_t = val[1]
- method_call = @builder.call_method(nil, nil, val[0],
- lparen_t, args, rparen_t)
-
- begin_t, block_args, body, end_t = val[2]
- result = @builder.block(method_call,
- begin_t, block_args, body, end_t)
- }
- | primary_value tDOT operation2 command_args =tLOWEST
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
-
- }
- | primary_value tDOT operation2 command_args cmd_brace_block
- {
- lparen_t, args, rparen_t = val[3]
- method_call = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
-
- begin_t, block_args, body, end_t = val[4]
- result = @builder.block(method_call,
- begin_t, block_args, body, end_t)
- }
- | primary_value tCOLON2 operation2 command_args =tLOWEST
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
- }
- | primary_value tCOLON2 operation2 command_args cmd_brace_block
- {
- lparen_t, args, rparen_t = val[3]
- method_call = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
-
- begin_t, block_args, body, end_t = val[4]
- result = @builder.block(method_call,
- begin_t, block_args, body, end_t)
- }
- | kSUPER command_args
- {
- lparen_t, args, rparen_t = val[1]
- result = @builder.keyword_cmd(:super, val[0],
- lparen_t, args, rparen_t)
- }
- | kYIELD command_args
- {
- lparen_t, args, rparen_t = val[1]
- result = @builder.keyword_cmd(:yield, val[0],
- lparen_t, args, rparen_t)
- }
-
- mlhs: mlhs_basic
- {
- result = @builder.multi_lhs(nil, val[0], nil)
- }
- | tLPAREN mlhs_entry tRPAREN
- {
- result = @builder.begin(val[0], val[1], val[2])
- }
-
- mlhs_entry: mlhs_basic
- {
- result = @builder.multi_lhs(nil, val[0], nil)
- }
- | tLPAREN mlhs_entry tRPAREN
- {
- result = @builder.multi_lhs(val[0], val[1], val[2])
- }
-
- mlhs_basic: mlhs_head
- {
- result = val[0]
- }
- | mlhs_head mlhs_item
- {
- result = val[0] << val[1]
- }
- | mlhs_head tSTAR mlhs_node
- {
- result = val[0] << @builder.splat(val[1], val[2])
- }
- | mlhs_head tSTAR
- {
- result = val[0] << @builder.splat(val[1])
- }
- | tSTAR mlhs_node
- {
- result = [ @builder.splat(val[0], val[1]) ]
- }
- | tSTAR
- {
- result = [ @builder.splat(val[0]) ]
- }
-
- mlhs_item: mlhs_node
- | tLPAREN mlhs_entry tRPAREN
- {
- result = @builder.begin(val[0], val[1], val[2])
- }
-
- mlhs_head: mlhs_item tCOMMA
- {
- result = [ val[0] ]
- }
- | mlhs_head mlhs_item tCOMMA
- {
- result = val[0] << val[1]
- }
-
- mlhs_node: variable
- {
- result = @builder.assignable(val[0])
- }
- | primary_value tLBRACK2 aref_args tRBRACK
- {
- result = @builder.index_asgn(val[0], val[1], val[2], val[3])
- }
- | primary_value tDOT tIDENTIFIER
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tIDENTIFIER
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tDOT tCONSTANT
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tCONSTANT
- {
- result = @builder.assignable(
- @builder.const_fetch(val[0], val[1], val[2]))
- }
- | tCOLON3 tCONSTANT
- {
- result = @builder.assignable(
- @builder.const_global(val[0], val[1]))
- }
- | backref
- {
- result = @builder.assignable(val[0])
- }
-
- lhs: variable
- {
- result = @builder.assignable(val[0])
- }
- | primary_value tLBRACK2 aref_args tRBRACK
- {
- result = @builder.index_asgn(val[0], val[1], val[2], val[3])
- }
- | primary_value tDOT tIDENTIFIER
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tIDENTIFIER
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tDOT tCONSTANT
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tCONSTANT
- {
- result = @builder.assignable(
- @builder.const_fetch(val[0], val[1], val[2]))
- }
- | tCOLON3 tCONSTANT
- {
- result = @builder.assignable(
- @builder.const_global(val[0], val[1]))
- }
- | backref
- {
- result = @builder.assignable(val[0])
- }
-
- cname: tIDENTIFIER
- {
- diagnostic :error, :module_name_const, nil, val[0]
- }
- | tCONSTANT
-
- cpath: tCOLON3 cname
- {
- result = @builder.const_global(val[0], val[1])
- }
- | cname
- {
- result = @builder.const(val[0])
- }
- | primary_value tCOLON2 cname
- {
- result = @builder.const_fetch(val[0], val[1], val[2])
- }
-
- fname: tIDENTIFIER | tCONSTANT | tFID
- | op
- | reswords
-
- fsym: fname
- {
- result = @builder.symbol(val[0])
- }
- | symbol
-
- fitem: fsym
- | dsym
-
- undef_list: fitem
- {
- result = [ val[0] ]
- }
- | undef_list tCOMMA
- {
- @lexer.state = :expr_fname
- }
- fitem
- {
- result = val[0] << val[3]
- }
-
- op: tPIPE | tCARET | tAMPER2 | tCMP | tEQ | tEQQ
- | tMATCH | tGT | tGEQ | tLT | tLEQ | tLSHFT
- | tRSHFT | tPLUS | tMINUS | tSTAR2 | tSTAR | tDIVIDE
- | tPERCENT | tPOW | tTILDE | tUPLUS | tUMINUS | tAREF
- | tASET | tBACK_REF2
-
- reswords: k__LINE__ | k__FILE__ | klBEGIN | klEND | kALIAS | kAND
- | kBEGIN | kBREAK | kCASE | kCLASS | kDEF | kDEFINED
- | kDO | kELSE | kELSIF | kEND | kENSURE | kFALSE
- | kFOR | kIN | kMODULE | kNEXT | kNIL | kNOT
- | kOR | kREDO | kRESCUE | kRETRY | kRETURN | kSELF
- | kSUPER | kTHEN | kTRUE | kUNDEF | kWHEN | kYIELD
- | kIF | kUNLESS | kWHILE | kUNTIL
-
- arg: lhs tEQL arg
- {
- result = @builder.assign(val[0], val[1], val[2])
- }
- | lhs tEQL arg kRESCUE_MOD arg
- {
- rescue_body = @builder.rescue_body(val[3],
- nil, nil, nil,
- nil, val[4])
-
- rescue_ = @builder.begin_body(val[2], [ rescue_body ])
-
- result = @builder.assign(val[0], val[1], rescue_)
- }
- | var_lhs tOP_ASGN arg
- {
- result = @builder.op_assign(val[0], val[1], val[2])
- }
- | primary_value tLBRACK2 aref_args tRBRACK tOP_ASGN arg
- {
- result = @builder.op_assign(
- @builder.index(
- val[0], val[1], val[2], val[3]),
- val[4], val[5])
- }
- | primary_value tDOT tIDENTIFIER tOP_ASGN arg
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tDOT tCONSTANT tOP_ASGN arg
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tCOLON2 tCONSTANT tOP_ASGN arg
- {
- diagnostic :error, :dynamic_const, nil, val[2], [ val[3] ]
- }
- | tCOLON3 tCONSTANT tOP_ASGN arg
- {
- diagnostic :error, :dynamic_const, nil, val[1], [ val[2] ]
- }
- | backref tOP_ASGN arg
- {
- result = @builder.op_assign(val[0], val[1], val[2])
- }
- | arg tDOT2 arg
- {
- result = @builder.range_inclusive(val[0], val[1], val[2])
- }
- | arg tDOT3 arg
- {
- result = @builder.range_exclusive(val[0], val[1], val[2])
- }
- | arg tPLUS arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tMINUS arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tSTAR2 arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tDIVIDE arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tPERCENT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tPOW arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | tUMINUS_NUM tINTEGER tPOW arg
- {
- result = @builder.unary_op(val[0],
- @builder.binary_op(
- @builder.integer(val[1]),
- val[2], val[3]))
- }
- | tUMINUS_NUM tFLOAT tPOW arg
- {
- result = @builder.unary_op(val[0],
- @builder.binary_op(
- @builder.float(val[1]),
- val[2], val[3]))
- }
- | tUPLUS arg
- {
- result = @builder.unary_op(val[0], val[1])
- }
- | tUMINUS arg
- {
- result = @builder.unary_op(val[0], val[1])
- }
- | arg tPIPE arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tCARET arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tAMPER2 arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tCMP arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tGT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tGEQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tLT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tLEQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tEQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tEQQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tNEQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tMATCH arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tNMATCH arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | tBANG arg
- {
- result = @builder.not_op(val[0], nil, val[1], nil)
- }
- | tTILDE arg
- {
- result = @builder.unary_op(val[0], val[1])
- }
- | arg tLSHFT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tRSHFT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tANDOP arg
- {
- result = @builder.logical_op(:and, val[0], val[1], val[2])
- }
- | arg tOROP arg
- {
- result = @builder.logical_op(:or, val[0], val[1], val[2])
- }
- | kDEFINED opt_nl arg
- {
- result = @builder.keyword_cmd(:defined?, val[0], nil, [ val[2] ], nil)
- }
- | arg tEH arg tCOLON arg
- {
- result = @builder.ternary(val[0], val[1],
- val[2], val[3], val[4])
- }
- | primary
-
- arg_value: arg
-
- aref_args: none
- | command opt_nl
- {
- result = [ val[0] ]
- }
- | args trailer
- {
- result = val[0]
- }
- | args tCOMMA tSTAR arg opt_nl
- {
- result = val[0] << @builder.splat(val[2], val[3])
- }
- | assocs trailer
- {
- result = [ @builder.associate(nil, val[0], nil) ]
- }
- | tSTAR arg opt_nl
- {
- result = [ @builder.splat(val[0], val[1]) ]
- }
-
- paren_args: tLPAREN2 none tRPAREN
- {
- result = [ val[0], [], val[2] ]
- }
- | tLPAREN2 call_args opt_nl tRPAREN
- {
- result = [ val[0], val[1], val[3] ]
- }
- | tLPAREN2 block_call opt_nl tRPAREN
- {
- result = [ val[0], [ val[1] ], val[3] ]
- }
- | tLPAREN2 args tCOMMA block_call opt_nl tRPAREN
- {
- result = [ val[0], val[1] << val[3], val[5] ]
- }
-
- opt_paren_args: # nothing
- {
- result = [ nil, [], nil ]
- }
- | paren_args
-
- call_args: command
- {
- result = [ val[0] ]
- }
- | args opt_block_arg
- {
- result = val[0].concat(val[1])
- }
- | args tCOMMA tSTAR arg_value opt_block_arg
- {
- result = val[0].concat(
- [ @builder.splat(val[2], val[3]),
- *val[4] ])
- }
- | assocs opt_block_arg
- {
- result = [ @builder.associate(nil, val[0], nil),
- *val[1] ]
- }
- | assocs tCOMMA tSTAR arg_value opt_block_arg
- {
- result = [ @builder.associate(nil, val[0], nil),
- @builder.splat(val[2], val[3]),
- *val[4] ]
- }
- | args tCOMMA assocs opt_block_arg
- {
- result = val[0].concat(
- [ @builder.associate(nil, val[2], nil),
- *val[3] ])
- }
- | args tCOMMA assocs tCOMMA tSTAR arg opt_block_arg
- {
- result = val[0].concat(
- [ @builder.associate(nil, val[2], nil),
- @builder.splat(val[4], val[5]),
- *val[6] ])
- }
- | tSTAR arg_value opt_block_arg
- {
- result = [ @builder.splat(val[0], val[1]),
- *val[2] ]
- }
- | block_arg
- {
- result = [ val[0] ]
- }
-
- call_args2: arg_value tCOMMA args opt_block_arg
- {
- result = [ val[0], *val[2].concat(val[3]) ]
- }
- | arg_value tCOMMA block_arg
- {
- result = [ val[0], val[2] ]
- }
- | arg_value tCOMMA tSTAR arg_value opt_block_arg
- {
- result = [ val[0],
- @builder.splat(val[2], val[3]),
- *val[4] ]
- }
- | arg_value tCOMMA args tCOMMA tSTAR arg_value opt_block_arg
- {
- result = [ val[0],
- *val[2].
- push(@builder.splat(val[4], val[5])).
- concat(val[6]) ]
- }
- | assocs opt_block_arg
- {
- result = [ @builder.associate(nil, val[0], nil),
- *val[1] ]
- }
- | assocs tCOMMA tSTAR arg_value opt_block_arg
- {
- result = [ @builder.associate(nil, val[0], nil),
- @builder.splat(val[2], val[3]),
- *val[4] ]
- }
- | arg_value tCOMMA assocs opt_block_arg
- {
- result = [ val[0],
- @builder.associate(nil, val[2], nil),
- *val[3] ]
- }
- | arg_value tCOMMA args tCOMMA assocs opt_block_arg
- {
- result = [ val[0],
- *val[2].
- push(@builder.associate(nil, val[4], nil)).
- concat(val[5]) ]
- }
- | arg_value tCOMMA assocs tCOMMA tSTAR arg_value opt_block_arg
- {
- result = [ val[0],
- @builder.associate(nil, val[2], nil),
- @builder.splat(val[4], val[5]),
- *val[6] ]
- }
- | arg_value tCOMMA args tCOMMA assocs tCOMMA tSTAR arg_value opt_block_arg
- {
- result = [ val[0],
- *val[2].
- push(@builder.associate(nil, val[4], nil)).
- push(@builder.splat(val[6], val[7])).
- concat(val[8]) ]
- }
- | tSTAR arg_value opt_block_arg
- {
- result = [ @builder.splat(val[0], val[1]),
- *val[2] ]
- }
- | block_arg
- {
- result = [ val[0] ]
- }
-
- command_args: {
- result = @lexer.cmdarg.dup
- @lexer.cmdarg.push(true)
- }
- open_args
- {
- @lexer.cmdarg = val[0]
-
- result = val[1]
- }
-
- open_args: call_args
- {
- result = [ nil, val[0], nil ]
- }
- | tLPAREN_ARG
- {
- @lexer.state = :expr_endarg
- }
- tRPAREN
- {
- result = [ val[0], [], val[2] ]
- }
- | tLPAREN_ARG call_args2
- {
- @lexer.state = :expr_endarg
- }
- tRPAREN
- {
- result = [ val[0], val[1], val[3] ]
- }
-
- block_arg: tAMPER arg_value
- {
- result = @builder.block_pass(val[0], val[1])
- }
-
- opt_block_arg: tCOMMA block_arg
- {
- result = [ val[1] ]
- }
- | # nothing
- {
- result = []
- }
-
- args: arg_value
- {
- result = [ val[0] ]
- }
- | args tCOMMA arg_value
- {
- result = val[0] << val[2]
- }
-
- mrhs: args tCOMMA arg_value
- {
- result = val[0] << val[2]
- }
- | args tCOMMA tSTAR arg_value
- {
- result = val[0] << @builder.splat(val[2], val[3])
- }
- | tSTAR arg_value
- {
- result = [ @builder.splat(val[0], val[1]) ]
- }
-
- primary: literal
- | strings
- | xstring
- | regexp
- | words
- | qwords
- | var_ref
- | backref
- | tFID
- {
- result = @builder.call_method(nil, nil, val[0])
- }
- | kBEGIN bodystmt kEND
- {
- result = @builder.begin_keyword(val[0], val[1], val[2])
- }
- | tLPAREN_ARG expr
- {
- @lexer.state = :expr_endarg
- }
- opt_nl tRPAREN
- {
- result = @builder.begin(val[0], val[1], val[4])
- }
- | tLPAREN compstmt tRPAREN
- {
- result = @builder.begin(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tCONSTANT
- {
- result = @builder.const_fetch(val[0], val[1], val[2])
- }
- | tCOLON3 tCONSTANT
- {
- result = @builder.const_global(val[0], val[1])
- }
- | primary_value tLBRACK2 aref_args tRBRACK
- {
- result = @builder.index(val[0], val[1], val[2], val[3])
- }
- | tLBRACK aref_args tRBRACK
- {
- result = @builder.array(val[0], val[1], val[2])
- }
- | tLBRACE assoc_list tRCURLY
- {
- result = @builder.associate(val[0], val[1], val[2])
- }
- | kRETURN
- {
- result = @builder.keyword_cmd(:return, val[0])
- }
- | kYIELD tLPAREN2 call_args tRPAREN
- {
- result = @builder.keyword_cmd(:yield, val[0], val[1], val[2], val[3])
- }
- | kYIELD tLPAREN2 tRPAREN
- {
- result = @builder.keyword_cmd(:yield, val[0], val[1], [], val[2])
- }
- | kYIELD
- {
- result = @builder.keyword_cmd(:yield, val[0])
- }
- | kDEFINED opt_nl tLPAREN2 expr tRPAREN
- {
- result = @builder.keyword_cmd(:defined?, val[0],
- val[2], [ val[3] ], val[4])
- }
- | operation brace_block
- {
- method_call = @builder.call_method(nil, nil, val[0])
-
- begin_t, args, body, end_t = val[1]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
- | method_call
- | method_call brace_block
- {
- begin_t, args, body, end_t = val[1]
- result = @builder.block(val[0],
- begin_t, args, body, end_t)
- }
- | kIF expr_value then compstmt if_tail kEND
- {
- else_t, else_ = val[4]
- result = @builder.condition(val[0], val[1], val[2],
- val[3], else_t,
- else_, val[5])
- }
- | kUNLESS expr_value then compstmt opt_else kEND
- {
- else_t, else_ = val[4]
- result = @builder.condition(val[0], val[1], val[2],
- else_, else_t,
- val[3], val[5])
- }
- | kWHILE
- {
- @lexer.cond.push(true)
- }
- expr_value do
- {
- @lexer.cond.pop
- }
- compstmt kEND
- {
- result = @builder.loop(:while, val[0], val[2], val[3],
- val[5], val[6])
- }
- | kUNTIL
- {
- @lexer.cond.push(true)
- }
- expr_value do
- {
- @lexer.cond.pop
- }
- compstmt kEND
- {
- result = @builder.loop(:until, val[0], val[2], val[3],
- val[5], val[6])
- }
- | kCASE expr_value opt_terms case_body kEND
- {
- when_bodies = val[3][0..-2]
- else_t, else_body = val[3][-1]
-
- result = @builder.case(val[0], val[1],
- when_bodies, else_t, else_body,
- val[4])
- }
- | kCASE opt_terms case_body kEND
- {
- when_bodies = val[2][0..-2]
- else_t, else_body = val[2][-1]
-
- result = @builder.case(val[0], nil,
- when_bodies, else_t, else_body,
- val[3])
- }
- | kCASE opt_terms kELSE compstmt kEND
- {
- result = @builder.case(val[0], nil,
- [], val[2], val[3],
- val[4])
- }
- | kFOR for_var kIN
- {
- @lexer.cond.push(true)
- }
- expr_value do
- {
- @lexer.cond.pop
- }
- compstmt kEND
- {
- result = @builder.for(val[0], val[1],
- val[2], val[4],
- val[5], val[7], val[8])
- }
- | kCLASS cpath superclass
- {
- @static_env.extend_static
- }
- bodystmt kEND
- {
- if in_def?
- diagnostic :error, :class_in_def, nil, val[0]
- end
-
- lt_t, superclass = val[2]
- result = @builder.def_class(val[0], val[1],
- lt_t, superclass,
- val[4], val[5])
-
- @static_env.unextend
- }
- | kCLASS tLSHFT expr term
- {
- result = @def_level
- @def_level = 0
-
- @static_env.extend_static
- }
- bodystmt kEND
- {
- result = @builder.def_sclass(val[0], val[1], val[2],
- val[5], val[6])
-
- @static_env.unextend
-
- @def_level = val[4]
- }
- | kMODULE cpath
- {
- @static_env.extend_static
- }
- bodystmt kEND
- {
- if in_def?
- diagnostic :error, :module_in_def, nil, val[0]
- end
-
- result = @builder.def_module(val[0], val[1],
- val[3], val[4])
-
- @static_env.unextend
- }
- | kDEF fname
- {
- @def_level += 1
- @static_env.extend_static
- }
- f_arglist bodystmt kEND
- {
- result = @builder.def_method(val[0], val[1],
- val[3], val[4], val[5])
-
- @static_env.unextend
- @def_level -= 1
- }
- | kDEF singleton dot_or_colon
- {
- @lexer.state = :expr_fname
- }
- fname
- {
- @def_level += 1
- @static_env.extend_static
- }
- f_arglist bodystmt kEND
- {
- result = @builder.def_singleton(val[0], val[1], val[2],
- val[4], val[6], val[7], val[8])
-
- @static_env.unextend
- @def_level -= 1
- }
- | kBREAK
- {
- result = @builder.keyword_cmd(:break, val[0])
- }
- | kNEXT
- {
- result = @builder.keyword_cmd(:next, val[0])
- }
- | kREDO
- {
- result = @builder.keyword_cmd(:redo, val[0])
- }
- | kRETRY
- {
- result = @builder.keyword_cmd(:retry, val[0])
- }
-
- primary_value: primary
-
- then: term
- | tCOLON
- | kTHEN
- | term kTHEN
- {
- result = val[1]
- }
-
- do: term
- | tCOLON
- | kDO_COND
-
- if_tail: opt_else
- | kELSIF expr_value then compstmt if_tail
- {
- else_t, else_ = val[4]
- result = [ val[0],
- @builder.condition(val[0], val[1], val[2],
- val[3], else_t,
- else_, nil),
- ]
- }
-
- opt_else: none
- | kELSE compstmt
- {
- result = val
- }
-
- for_var: lhs
- | mlhs
-
- block_par: mlhs_item
- {
- result = [ @builder.arg_expr(val[0]) ]
- }
- | block_par tCOMMA mlhs_item
- {
- result = val[0] << @builder.arg_expr(val[2])
- }
-
- block_var: block_par
- | block_par tCOMMA
- | block_par tCOMMA tAMPER lhs
- {
- result = val[0].
- push(@builder.blockarg_expr(val[2], val[3]))
- }
- | block_par tCOMMA tSTAR lhs tCOMMA tAMPER lhs
- {
- result = val[0].
- push(@builder.restarg_expr(val[2], val[3])).
- push(@builder.blockarg_expr(val[5], val[6]))
- }
- | block_par tCOMMA tSTAR tCOMMA tAMPER lhs
- {
- result = val[0].
- push(@builder.restarg_expr(val[2])).
- push(@builder.blockarg_expr(val[4], val[5]))
- }
- | block_par tCOMMA tSTAR lhs
- {
- result = val[0].
- push(@builder.restarg_expr(val[2], val[3]))
- }
- | block_par tCOMMA tSTAR
- {
- result = val[0].
- push(@builder.restarg_expr(val[2]))
- }
- | tSTAR lhs tCOMMA tAMPER lhs
- {
- result = [ @builder.restarg_expr(val[0], val[1]),
- @builder.blockarg_expr(val[3], val[4]) ]
- }
- | tSTAR tCOMMA tAMPER lhs
- {
- result = [ @builder.restarg_expr(val[0]),
- @builder.blockarg_expr(val[2], val[3]) ]
- }
- | tSTAR lhs
- {
- result = [ @builder.restarg_expr(val[0], val[1]) ]
- }
- | tSTAR
- {
- result = [ @builder.restarg_expr(val[0]) ]
- }
- | tAMPER lhs
- {
- result = [ @builder.blockarg_expr(val[0], val[1]) ]
- }
- ;
-
- opt_block_var: # nothing
- {
- result = @builder.args(nil, [], nil)
- }
- | tPIPE tPIPE
- {
- result = @builder.args(val[0], [], val[1])
- }
- | tOROP
- {
- result = @builder.args(val[0], [], val[0])
- }
- | tPIPE block_var tPIPE
- {
- result = @builder.args(val[0], val[1], val[2], false)
- }
-
- do_block: kDO_BLOCK
- {
- @static_env.extend_dynamic
- }
- opt_block_var compstmt kEND
- {
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
- }
-
- block_call: command do_block
- {
- begin_t, block_args, body, end_t = val[1]
- result = @builder.block(val[0],
- begin_t, block_args, body, end_t)
- }
- | block_call tDOT operation2 opt_paren_args
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
- }
- | block_call tCOLON2 operation2 opt_paren_args
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
- }
-
- method_call: operation paren_args
- {
- lparen_t, args, rparen_t = val[1]
- result = @builder.call_method(nil, nil, val[0],
- lparen_t, args, rparen_t)
- }
- | primary_value tDOT operation2 opt_paren_args
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
- }
- | primary_value tCOLON2 operation2 paren_args
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
- }
- | primary_value tCOLON2 operation3
- {
- result = @builder.call_method(val[0], val[1], val[2])
- }
- | kSUPER paren_args
- {
- lparen_t, args, rparen_t = val[1]
- result = @builder.keyword_cmd(:super, val[0],
- lparen_t, args, rparen_t)
- }
- | kSUPER
- {
- result = @builder.keyword_cmd(:zsuper, val[0])
- }
-
- brace_block: tLCURLY
- {
- @static_env.extend_dynamic
- }
- opt_block_var compstmt tRCURLY
- {
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
- }
- | kDO
- {
- @static_env.extend_dynamic
- }
- opt_block_var compstmt kEND
- {
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
- }
-
- case_body: kWHEN when_args then compstmt cases
- {
- result = [ @builder.when(val[0], val[1], val[2], val[3]),
- *val[4] ]
- }
-
- when_args: args
- | args tCOMMA tSTAR arg_value
- {
- result = val[0] << @builder.splat(val[2], val[3])
- }
- | tSTAR arg_value
- {
- result = [ @builder.splat(val[0], val[1]) ]
- }
-
- cases: opt_else
- {
- result = [ val[0] ]
- }
- | case_body
-
- opt_rescue: kRESCUE exc_list exc_var then compstmt opt_rescue
- {
- assoc_t, exc_var = val[2]
-
- if val[1]
- exc_list = @builder.array(nil, val[1], nil)
- end
-
- result = [ @builder.rescue_body(val[0],
- exc_list, assoc_t, exc_var,
- val[3], val[4]),
- *val[5] ]
- }
- | # nothing
- {
- result = []
- }
-
- exc_list: arg_value
- {
- result = [ val[0] ]
- }
- | mrhs
- | none
-
- exc_var: tASSOC lhs
- {
- result = [ val[0], val[1] ]
- }
- | none
-
- opt_ensure: kENSURE compstmt
- {
- result = [ val[0], val[1] ]
- }
- | none
-
- literal: numeric
- | symbol
- | dsym
-
- strings: string
- {
- result = @builder.string_compose(nil, val[0], nil)
- }
-
- string: string1
- {
- result = [ val[0] ]
- }
- | string string1
- {
- result = val[0] << val[1]
- }
-
- string1: tSTRING_BEG string_contents tSTRING_END
- {
- result = @builder.string_compose(val[0], val[1], val[2])
- }
- | tSTRING
- {
- result = @builder.string(val[0])
- }
-
- xstring: tXSTRING_BEG xstring_contents tSTRING_END
- {
- result = @builder.xstring_compose(val[0], val[1], val[2])
- }
-
- regexp: tREGEXP_BEG xstring_contents tSTRING_END tREGEXP_OPT
- {
- opts = @builder.regexp_options(val[3])
- result = @builder.regexp_compose(val[0], val[1], val[2], opts)
- }
-
- words: tWORDS_BEG word_list tSTRING_END
- {
- result = @builder.words_compose(val[0], val[1], val[2])
- }
-
- word_list: # nothing
- {
- result = []
- }
- | word_list word tSPACE
- {
- result = val[0] << @builder.word(val[1])
- }
-
- word: string_content
- {
- result = [ val[0] ]
- }
- | word string_content
- {
- result = val[0] << val[1]
- }
-
- qwords: tQWORDS_BEG qword_list tSTRING_END
- {
- result = @builder.words_compose(val[0], val[1], val[2])
- }
-
- qword_list: # nothing
- {
- result = []
- }
- | qword_list tSTRING_CONTENT tSPACE
- {
- result = val[0] << @builder.string_internal(val[1])
- }
-
- string_contents: # nothing
- {
- result = []
- }
- | string_contents string_content
- {
- result = val[0] << val[1]
- }
-
-xstring_contents: # nothing
- {
- result = []
- }
- | xstring_contents string_content
- {
- result = val[0] << val[1]
- }
-
- string_content: tSTRING_CONTENT
- {
- result = @builder.string_internal(val[0])
- }
- | tSTRING_DVAR string_dvar
- {
- result = val[1]
- }
- | tSTRING_DBEG
- {
- @lexer.cond.push(false)
- @lexer.cmdarg.push(false)
- }
- compstmt tRCURLY
- {
- @lexer.cond.lexpop
- @lexer.cmdarg.lexpop
-
- result = @builder.begin(val[0], val[2], val[3])
- }
-
- string_dvar: tGVAR
- {
- result = @builder.gvar(val[0])
- }
- | tIVAR
- {
- result = @builder.ivar(val[0])
- }
- | tCVAR
- {
- result = @builder.cvar(val[0])
- }
- | backref
-
-
- symbol: tSYMBOL
- {
- result = @builder.symbol(val[0])
- }
-
- dsym: tSYMBEG xstring_contents tSTRING_END
- {
- result = @builder.symbol_compose(val[0], val[1], val[2])
- }
-
- numeric: tINTEGER
- {
- result = @builder.integer(val[0])
- }
- | tFLOAT
- {
- result = @builder.float(val[0])
- }
- | tUMINUS_NUM tINTEGER =tLOWEST
- {
- result = @builder.negate(val[0],
- @builder.integer(val[1]))
- }
- | tUMINUS_NUM tFLOAT =tLOWEST
- {
- result = @builder.negate(val[0],
- @builder.float(val[1]))
- }
-
- variable: tIDENTIFIER
- {
- result = @builder.ident(val[0])
- }
- | tIVAR
- {
- result = @builder.ivar(val[0])
- }
- | tGVAR
- {
- result = @builder.gvar(val[0])
- }
- | tCVAR
- {
- result = @builder.cvar(val[0])
- }
- | tCONSTANT
- {
- result = @builder.const(val[0])
- }
- | kNIL
- {
- result = @builder.nil(val[0])
- }
- | kSELF
- {
- result = @builder.self(val[0])
- }
- | kTRUE
- {
- result = @builder.true(val[0])
- }
- | kFALSE
- {
- result = @builder.false(val[0])
- }
- | k__FILE__
- {
- result = @builder.__FILE__(val[0])
- }
- | k__LINE__
- {
- result = @builder.__LINE__(val[0])
- }
-
- var_ref: variable
- {
- result = @builder.accessible(val[0])
- }
-
- var_lhs: variable
- {
- result = @builder.assignable(val[0])
- }
-
- backref: tNTH_REF
- {
- result = @builder.nth_ref(val[0])
- }
- | tBACK_REF
- {
- result = @builder.back_ref(val[0])
- }
-
- superclass: term
- {
- result = nil
- }
- | tLT expr_value term
- {
- result = [ val[0], val[1] ]
- }
- | error term
- {
- yyerrok
- result = nil
- }
-
- f_arglist: tLPAREN2 f_args opt_nl tRPAREN
- {
- result = @builder.args(val[0], val[1], val[3])
-
- @lexer.state = :expr_beg
- }
- | f_args term
- {
- result = @builder.args(nil, val[0], nil)
- }
-
- f_args: f_arg tCOMMA f_optarg tCOMMA f_rest_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg tCOMMA f_optarg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_arg tCOMMA f_rest_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[1])
- }
- | f_optarg tCOMMA f_rest_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_optarg opt_f_block_arg
- {
- result = val[0].
- concat(val[1])
- }
- | f_rest_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[1])
- }
- | f_block_arg
- {
- result = [ val[0] ]
- }
- | # nothing
- {
- result = []
- }
-
- f_norm_arg: tCONSTANT
- {
- diagnostic :error, :argument_const, nil, val[0]
- }
- | tIVAR
- {
- diagnostic :error, :argument_ivar, nil, val[0]
- }
- | tGVAR
- {
- diagnostic :error, :argument_gvar, nil, val[0]
- }
- | tCVAR
- {
- diagnostic :error, :argument_cvar, nil, val[0]
- }
- | tIDENTIFIER
- {
- @static_env.declare val[0][0]
-
- result = @builder.arg(val[0])
- }
-
- f_arg: f_norm_arg
- {
- result = [ val[0] ]
- }
- | f_arg tCOMMA f_norm_arg
- {
- result = val[0] << val[2]
- }
-
- f_opt: tIDENTIFIER tEQL arg_value
- {
- @static_env.declare val[0][0]
-
- result = @builder.optarg(val[0], val[1], val[2])
- }
-
- f_optarg: f_opt
- {
- result = [ val[0] ]
- }
- | f_optarg tCOMMA f_opt
- {
- result = val[0] << val[2]
- }
-
- restarg_mark: tSTAR2 | tSTAR
-
- f_rest_arg: restarg_mark tIDENTIFIER
- {
- @static_env.declare val[1][0]
-
- result = [ @builder.restarg(val[0], val[1]) ]
- }
- | restarg_mark
- {
- result = [ @builder.restarg(val[0]) ]
- }
-
- blkarg_mark: tAMPER2 | tAMPER
-
- f_block_arg: blkarg_mark tIDENTIFIER
- {
- @static_env.declare val[1][0]
-
- result = @builder.blockarg(val[0], val[1])
- }
-
- opt_f_block_arg: tCOMMA f_block_arg
- {
- result = [ val[1] ]
- }
- | # nothing
- {
- result = []
- }
-
- singleton: var_ref
- | tLPAREN2 expr opt_nl tRPAREN
- {
- result = val[1]
- }
-
- assoc_list: # nothing
- {
- result = []
- }
- | assocs trailer
- {
- result = val[0]
- }
- | args trailer
- {
- result = @builder.pair_list_18(val[0])
- }
-
- assocs: assoc
- {
- result = [ val[0] ]
- }
- | assocs tCOMMA assoc
- {
- result = val[0] << val[2]
- }
-
- assoc: arg_value tASSOC arg_value
- {
- result = @builder.pair(val[0], val[1], val[2])
- }
-
- operation: tIDENTIFIER | tCONSTANT | tFID
- operation2: tIDENTIFIER | tCONSTANT | tFID | op
- operation3: tIDENTIFIER | tFID | op
- dot_or_colon: tDOT | tCOLON2
- opt_terms: | terms
- opt_nl: | tNL
- trailer: | tNL | tCOMMA
-
- term: tSEMI
- {
- yyerrok
- }
- | tNL
-
- terms: term
- | terms tSEMI
-
- none: # nothing
- {
- result = nil
- }
-
-end
-
----- header
-
-require 'parser'
-
----- inner
-
- def version
- 18
- end
-
- def default_encoding
- Encoding::BINARY if defined? Encoding
- end
diff --git a/test/racc/assets/ruby19.y b/test/racc/assets/ruby19.y
deleted file mode 100644
index b405c952e7..0000000000
--- a/test/racc/assets/ruby19.y
+++ /dev/null
@@ -1,2174 +0,0 @@
-# Copyright (c) 2013 Peter Zotov <whitequark@whitequark.org>
-#
-# Parts of the source are derived from ruby_parser:
-# Copyright (c) Ryan Davis, seattle.rb
-#
-# MIT License
-#
-# 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.
-
-class Parser::Ruby19
-
-token kCLASS kMODULE kDEF kUNDEF kBEGIN kRESCUE kENSURE kEND kIF kUNLESS
- kTHEN kELSIF kELSE kCASE kWHEN kWHILE kUNTIL kFOR kBREAK kNEXT
- kREDO kRETRY kIN kDO kDO_COND kDO_BLOCK kDO_LAMBDA kRETURN kYIELD kSUPER
- kSELF kNIL kTRUE kFALSE kAND kOR kNOT kIF_MOD kUNLESS_MOD kWHILE_MOD
- kUNTIL_MOD kRESCUE_MOD kALIAS kDEFINED klBEGIN klEND k__LINE__
- k__FILE__ k__ENCODING__ tIDENTIFIER tFID tGVAR tIVAR tCONSTANT
- tLABEL tCVAR tNTH_REF tBACK_REF tSTRING_CONTENT tINTEGER tFLOAT
- tREGEXP_END tUPLUS tUMINUS tUMINUS_NUM tPOW tCMP tEQ tEQQ tNEQ
- tGEQ tLEQ tANDOP tOROP tMATCH tNMATCH tDOT tDOT2 tDOT3 tAREF
- tASET tLSHFT tRSHFT tCOLON2 tCOLON3 tOP_ASGN tASSOC tLPAREN
- tLPAREN2 tRPAREN tLPAREN_ARG tLBRACK tLBRACK2 tRBRACK tLBRACE
- tLBRACE_ARG tSTAR tSTAR2 tAMPER tAMPER2 tTILDE tPERCENT tDIVIDE
- tPLUS tMINUS tLT tGT tPIPE tBANG tCARET tLCURLY tRCURLY
- tBACK_REF2 tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG tREGEXP_OPT
- tWORDS_BEG tQWORDS_BEG tSTRING_DBEG tSTRING_DVAR tSTRING_END
- tSTRING tSYMBOL tNL tEH tCOLON tCOMMA tSPACE tSEMI tLAMBDA tLAMBEG
- tCHARACTER
-
-prechigh
- right tBANG tTILDE tUPLUS
- right tPOW
- right tUMINUS_NUM tUMINUS
- left tSTAR2 tDIVIDE tPERCENT
- left tPLUS tMINUS
- left tLSHFT tRSHFT
- left tAMPER2
- left tPIPE tCARET
- left tGT tGEQ tLT tLEQ
- nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH
- left tANDOP
- left tOROP
- nonassoc tDOT2 tDOT3
- right tEH tCOLON
- left kRESCUE_MOD
- right tEQL tOP_ASGN
- nonassoc kDEFINED
- right kNOT
- left kOR kAND
- nonassoc kIF_MOD kUNLESS_MOD kWHILE_MOD kUNTIL_MOD
- nonassoc tLBRACE_ARG
- nonassoc tLOWEST
-preclow
-
-rule
-
- program: top_compstmt
-
- top_compstmt: top_stmts opt_terms
- {
- result = @builder.compstmt(val[0])
- }
-
- top_stmts: # nothing
- {
- result = []
- }
- | top_stmt
- {
- result = [ val[0] ]
- }
- | top_stmts terms top_stmt
- {
- result = val[0] << val[2]
- }
- | error top_stmt
- {
- result = [ val[1] ]
- }
-
- top_stmt: stmt
- | klBEGIN tLCURLY top_compstmt tRCURLY
- {
- result = @builder.preexe(val[0], val[1], val[2], val[3])
- }
-
- bodystmt: compstmt opt_rescue opt_else opt_ensure
- {
- rescue_bodies = val[1]
- else_t, else_ = val[2]
- ensure_t, ensure_ = val[3]
-
- if rescue_bodies.empty? && !else_.nil?
- diagnostic :warning, :useless_else, nil, else_t
- end
-
- result = @builder.begin_body(val[0],
- rescue_bodies,
- else_t, else_,
- ensure_t, ensure_)
- }
-
- compstmt: stmts opt_terms
- {
- result = @builder.compstmt(val[0])
- }
-
- stmts: # nothing
- {
- result = []
- }
- | stmt
- {
- result = [ val[0] ]
- }
- | stmts terms stmt
- {
- result = val[0] << val[2]
- }
- | error stmt
- {
- result = [ val[1] ]
- }
-
- stmt: kALIAS fitem
- {
- @lexer.state = :expr_fname
- }
- fitem
- {
- result = @builder.alias(val[0], val[1], val[3])
- }
- | kALIAS tGVAR tGVAR
- {
- result = @builder.alias(val[0],
- @builder.gvar(val[1]),
- @builder.gvar(val[2]))
- }
- | kALIAS tGVAR tBACK_REF
- {
- result = @builder.alias(val[0],
- @builder.gvar(val[1]),
- @builder.back_ref(val[2]))
- }
- | kALIAS tGVAR tNTH_REF
- {
- diagnostic :error, :nth_ref_alias, nil, val[2]
- }
- | kUNDEF undef_list
- {
- result = @builder.undef_method(val[0], val[1])
- }
- | stmt kIF_MOD expr_value
- {
- result = @builder.condition_mod(val[0], nil,
- val[1], val[2])
- }
- | stmt kUNLESS_MOD expr_value
- {
- result = @builder.condition_mod(nil, val[0],
- val[1], val[2])
- }
- | stmt kWHILE_MOD expr_value
- {
- result = @builder.loop_mod(:while, val[0], val[1], val[2])
- }
- | stmt kUNTIL_MOD expr_value
- {
- result = @builder.loop_mod(:until, val[0], val[1], val[2])
- }
- | stmt kRESCUE_MOD stmt
- {
- rescue_body = @builder.rescue_body(val[1],
- nil, nil, nil,
- nil, val[2])
-
- result = @builder.begin_body(val[0], [ rescue_body ])
- }
- | klEND tLCURLY compstmt tRCURLY
- {
- result = @builder.postexe(val[0], val[1], val[2], val[3])
- }
- | command_asgn
- | mlhs tEQL command_call
- {
- result = @builder.multi_assign(val[0], val[1], val[2])
- }
- | var_lhs tOP_ASGN command_call
- {
- result = @builder.op_assign(val[0], val[1], val[2])
- }
- | primary_value tLBRACK2 opt_call_args rbracket tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.index(
- val[0], val[1], val[2], val[3]),
- val[4], val[5])
- }
- | primary_value tDOT tIDENTIFIER tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tDOT tCONSTANT tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | backref tOP_ASGN command_call
- {
- @builder.op_assign(val[0], val[1], val[2])
- }
- | lhs tEQL mrhs
- {
- result = @builder.assign(val[0], val[1],
- @builder.array(nil, val[2], nil))
- }
- | mlhs tEQL arg_value
- {
- result = @builder.multi_assign(val[0], val[1], val[2])
- }
- | mlhs tEQL mrhs
- {
- result = @builder.multi_assign(val[0], val[1],
- @builder.array(nil, val[2], nil))
- }
- | expr
-
- command_asgn: lhs tEQL command_call
- {
- result = @builder.assign(val[0], val[1], val[2])
- }
- | lhs tEQL command_asgn
- {
- result = @builder.assign(val[0], val[1], val[2])
- }
-
- expr: command_call
- | expr kAND expr
- {
- result = @builder.logical_op(:and, val[0], val[1], val[2])
- }
- | expr kOR expr
- {
- result = @builder.logical_op(:or, val[0], val[1], val[2])
- }
- | kNOT opt_nl expr
- {
- result = @builder.not_op(val[0], nil, val[2], nil)
- }
- | tBANG command_call
- {
- result = @builder.not_op(val[0], nil, val[1], nil)
- }
- | arg
-
- expr_value: expr
-
- command_call: command
- | block_command
-
- block_command: block_call
- | block_call tDOT operation2 command_args
- {
- result = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
- }
- | block_call tCOLON2 operation2 command_args
- {
- result = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
- }
-
- cmd_brace_block: tLBRACE_ARG
- {
- @static_env.extend_dynamic
- }
- opt_block_param compstmt tRCURLY
- {
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
- }
-
- command: operation command_args =tLOWEST
- {
- result = @builder.call_method(nil, nil, val[0],
- nil, val[1], nil)
- }
- | operation command_args cmd_brace_block
- {
- method_call = @builder.call_method(nil, nil, val[0],
- nil, val[1], nil)
-
- begin_t, args, body, end_t = val[2]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
- | primary_value tDOT operation2 command_args =tLOWEST
- {
- result = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
- }
- | primary_value tDOT operation2 command_args cmd_brace_block
- {
- method_call = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
-
- begin_t, args, body, end_t = val[4]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
- | primary_value tCOLON2 operation2 command_args =tLOWEST
- {
- result = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
- }
- | primary_value tCOLON2 operation2 command_args cmd_brace_block
- {
- method_call = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
-
- begin_t, args, body, end_t = val[4]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
- | kSUPER command_args
- {
- result = @builder.keyword_cmd(:super, val[0],
- nil, val[1], nil)
- }
- | kYIELD command_args
- {
- result = @builder.keyword_cmd(:yield, val[0],
- nil, val[1], nil)
- }
- | kRETURN call_args
- {
- result = @builder.keyword_cmd(:return, val[0],
- nil, val[1], nil)
- }
- | kBREAK call_args
- {
- result = @builder.keyword_cmd(:break, val[0],
- nil, val[1], nil)
- }
- | kNEXT call_args
- {
- result = @builder.keyword_cmd(:next, val[0],
- nil, val[1], nil)
- }
-
- mlhs: mlhs_basic
- {
- result = @builder.multi_lhs(nil, val[0], nil)
- }
- | tLPAREN mlhs_inner rparen
- {
- result = @builder.begin(val[0], val[1], val[2])
- }
-
- mlhs_inner: mlhs_basic
- {
- result = @builder.multi_lhs(nil, val[0], nil)
- }
- | tLPAREN mlhs_inner rparen
- {
- result = @builder.multi_lhs(val[0], val[1], val[2])
- }
-
- mlhs_basic: mlhs_head
- | mlhs_head mlhs_item
- {
- result = val[0].
- push(val[1])
- }
- | mlhs_head tSTAR mlhs_node
- {
- result = val[0].
- push(@builder.splat(val[1], val[2]))
- }
- | mlhs_head tSTAR mlhs_node tCOMMA mlhs_post
- {
- result = val[0].
- push(@builder.splat(val[1], val[2])).
- concat(val[4])
- }
- | mlhs_head tSTAR
- {
- result = val[0].
- push(@builder.splat(val[1]))
- }
- | mlhs_head tSTAR tCOMMA mlhs_post
- {
- result = val[0].
- push(@builder.splat(val[1])).
- concat(val[3])
- }
- | tSTAR mlhs_node
- {
- result = [ @builder.splat(val[0], val[1]) ]
- }
- | tSTAR mlhs_node tCOMMA mlhs_post
- {
- result = [ @builder.splat(val[0], val[1]),
- *val[3] ]
- }
- | tSTAR
- {
- result = [ @builder.splat(val[0]) ]
- }
- | tSTAR tCOMMA mlhs_post
- {
- result = [ @builder.splat(val[0]),
- *val[2] ]
- }
-
- mlhs_item: mlhs_node
- | tLPAREN mlhs_inner rparen
- {
- result = @builder.begin(val[0], val[1], val[2])
- }
-
- mlhs_head: mlhs_item tCOMMA
- {
- result = [ val[0] ]
- }
- | mlhs_head mlhs_item tCOMMA
- {
- result = val[0] << val[1]
- }
-
- mlhs_post: mlhs_item
- {
- result = [ val[0] ]
- }
- | mlhs_post tCOMMA mlhs_item
- {
- result = val[0] << val[2]
- }
-
- mlhs_node: user_variable
- {
- result = @builder.assignable(val[0])
- }
- | keyword_variable
- {
- result = @builder.assignable(val[0])
- }
- | primary_value tLBRACK2 opt_call_args rbracket
- {
- result = @builder.index_asgn(val[0], val[1], val[2], val[3])
- }
- | primary_value tDOT tIDENTIFIER
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tIDENTIFIER
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tDOT tCONSTANT
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tCONSTANT
- {
- result = @builder.assignable(
- @builder.const_fetch(val[0], val[1], val[2]))
- }
- | tCOLON3 tCONSTANT
- {
- result = @builder.assignable(
- @builder.const_global(val[0], val[1]))
- }
- | backref
- {
- result = @builder.assignable(val[0])
- }
-
- lhs: user_variable
- {
- result = @builder.assignable(val[0])
- }
- | keyword_variable
- {
- result = @builder.assignable(val[0])
- }
- | primary_value tLBRACK2 opt_call_args rbracket
- {
- result = @builder.index_asgn(val[0], val[1], val[2], val[3])
- }
- | primary_value tDOT tIDENTIFIER
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tIDENTIFIER
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tDOT tCONSTANT
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tCONSTANT
- {
- result = @builder.assignable(
- @builder.const_fetch(val[0], val[1], val[2]))
- }
- | tCOLON3 tCONSTANT
- {
- result = @builder.assignable(
- @builder.const_global(val[0], val[1]))
- }
- | backref
- {
- result = @builder.assignable(val[0])
- }
-
- cname: tIDENTIFIER
- {
- diagnostic :error, :module_name_const, nil, val[0]
- }
- | tCONSTANT
-
- cpath: tCOLON3 cname
- {
- result = @builder.const_global(val[0], val[1])
- }
- | cname
- {
- result = @builder.const(val[0])
- }
- | primary_value tCOLON2 cname
- {
- result = @builder.const_fetch(val[0], val[1], val[2])
- }
-
- fname: tIDENTIFIER | tCONSTANT | tFID
- | op
- | reswords
-
- fsym: fname
- {
- result = @builder.symbol(val[0])
- }
- | symbol
-
- fitem: fsym
- | dsym
-
- undef_list: fitem
- {
- result = [ val[0] ]
- }
- | undef_list tCOMMA
- {
- @lexer.state = :expr_fname
- }
- fitem
- {
- result = val[0] << val[3]
- }
-
- op: tPIPE | tCARET | tAMPER2 | tCMP | tEQ | tEQQ
- | tMATCH | tNMATCH | tGT | tGEQ | tLT | tLEQ
- | tNEQ | tLSHFT | tRSHFT | tPLUS | tMINUS | tSTAR2
- | tSTAR | tDIVIDE | tPERCENT | tPOW | tBANG | tTILDE
- | tUPLUS | tUMINUS | tAREF | tASET | tBACK_REF2
-
- reswords: k__LINE__ | k__FILE__ | k__ENCODING__ | klBEGIN | klEND
- | kALIAS | kAND | kBEGIN | kBREAK | kCASE
- | kCLASS | kDEF | kDEFINED | kDO | kELSE
- | kELSIF | kEND | kENSURE | kFALSE | kFOR
- | kIN | kMODULE | kNEXT | kNIL | kNOT
- | kOR | kREDO | kRESCUE | kRETRY | kRETURN
- | kSELF | kSUPER | kTHEN | kTRUE | kUNDEF
- | kWHEN | kYIELD | kIF | kUNLESS | kWHILE
- | kUNTIL
-
- arg: lhs tEQL arg
- {
- result = @builder.assign(val[0], val[1], val[2])
- }
- | lhs tEQL arg kRESCUE_MOD arg
- {
- rescue_body = @builder.rescue_body(val[3],
- nil, nil, nil,
- nil, val[4])
-
- rescue_ = @builder.begin_body(val[2], [ rescue_body ])
-
- result = @builder.assign(val[0], val[1], rescue_)
- }
- | var_lhs tOP_ASGN arg
- {
- result = @builder.op_assign(val[0], val[1], val[2])
- }
- | var_lhs tOP_ASGN arg kRESCUE_MOD arg
- {
- rescue_body = @builder.rescue_body(val[3],
- nil, nil, nil,
- nil, val[4])
-
- rescue_ = @builder.begin_body(val[2], [ rescue_body ])
-
- result = @builder.op_assign(val[0], val[1], rescue_)
- }
- | primary_value tLBRACK2 opt_call_args rbracket tOP_ASGN arg
- {
- result = @builder.op_assign(
- @builder.index(
- val[0], val[1], val[2], val[3]),
- val[4], val[5])
- }
- | primary_value tDOT tIDENTIFIER tOP_ASGN arg
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tDOT tCONSTANT tOP_ASGN arg
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tCOLON2 tCONSTANT tOP_ASGN arg
- {
- diagnostic :error, :dynamic_const, nil, val[2], [ val[3] ]
- }
- | tCOLON3 tCONSTANT tOP_ASGN arg
- {
- diagnostic :error, :dynamic_const, nil, val[1], [ val[2] ]
- }
- | backref tOP_ASGN arg
- {
- result = @builder.op_assign(val[0], val[1], val[2])
- }
- | arg tDOT2 arg
- {
- result = @builder.range_inclusive(val[0], val[1], val[2])
- }
- | arg tDOT3 arg
- {
- result = @builder.range_exclusive(val[0], val[1], val[2])
- }
- | arg tPLUS arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tMINUS arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tSTAR2 arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tDIVIDE arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tPERCENT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tPOW arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | tUMINUS_NUM tINTEGER tPOW arg
- {
- result = @builder.unary_op(val[0],
- @builder.binary_op(
- @builder.integer(val[1]),
- val[2], val[3]))
- }
- | tUMINUS_NUM tFLOAT tPOW arg
- {
- result = @builder.unary_op(val[0],
- @builder.binary_op(
- @builder.float(val[1]),
- val[2], val[3]))
- }
- | tUPLUS arg
- {
- result = @builder.unary_op(val[0], val[1])
- }
- | tUMINUS arg
- {
- result = @builder.unary_op(val[0], val[1])
- }
- | arg tPIPE arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tCARET arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tAMPER2 arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tCMP arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tGT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tGEQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tLT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tLEQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tEQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tEQQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tNEQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tMATCH arg
- {
- result = @builder.match_op(val[0], val[1], val[2])
- }
- | arg tNMATCH arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | tBANG arg
- {
- result = @builder.not_op(val[0], nil, val[1], nil)
- }
- | tTILDE arg
- {
- result = @builder.unary_op(val[0], val[1])
- }
- | arg tLSHFT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tRSHFT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tANDOP arg
- {
- result = @builder.logical_op(:and, val[0], val[1], val[2])
- }
- | arg tOROP arg
- {
- result = @builder.logical_op(:or, val[0], val[1], val[2])
- }
- | kDEFINED opt_nl arg
- {
- result = @builder.keyword_cmd(:defined?, val[0], nil, [ val[2] ], nil)
- }
-
- | arg tEH arg opt_nl tCOLON arg
- {
- result = @builder.ternary(val[0], val[1],
- val[2], val[4], val[5])
- }
- | primary
-
- arg_value: arg
-
- aref_args: none
- | args trailer
- | args tCOMMA assocs trailer
- {
- result = val[0] << @builder.associate(nil, val[2], nil)
- }
- | assocs trailer
- {
- result = [ @builder.associate(nil, val[0], nil) ]
- }
-
- paren_args: tLPAREN2 opt_call_args rparen
- {
- result = val
- }
-
- opt_paren_args: # nothing
- {
- result = [ nil, [], nil ]
- }
- | paren_args
-
- opt_call_args: # nothing
- {
- result = []
- }
- | call_args
- | args tCOMMA
- | args tCOMMA assocs tCOMMA
- {
- result = val[0] << @builder.associate(nil, val[2], nil)
- }
- | assocs tCOMMA
- {
- result = [ @builder.associate(nil, val[0], nil) ]
- }
-
- call_args: command
- {
- result = [ val[0] ]
- }
- | args opt_block_arg
- {
- result = val[0].concat(val[1])
- }
- | assocs opt_block_arg
- {
- result = [ @builder.associate(nil, val[0], nil) ]
- result.concat(val[1])
- }
- | args tCOMMA assocs opt_block_arg
- {
- assocs = @builder.associate(nil, val[2], nil)
- result = val[0] << assocs
- result.concat(val[3])
- }
- | block_arg
- {
- result = [ val[0] ]
- }
-
- command_args: {
- result = @lexer.cmdarg.dup
- @lexer.cmdarg.push(true)
- }
- call_args
- {
- @lexer.cmdarg = val[0]
-
- result = val[1]
- }
-
- block_arg: tAMPER arg_value
- {
- result = @builder.block_pass(val[0], val[1])
- }
-
- opt_block_arg: tCOMMA block_arg
- {
- result = [ val[1] ]
- }
- | # nothing
- {
- result = []
- }
-
- args: arg_value
- {
- result = [ val[0] ]
- }
- | tSTAR arg_value
- {
- result = [ @builder.splat(val[0], val[1]) ]
- }
- | args tCOMMA arg_value
- {
- result = val[0] << val[2]
- }
- | args tCOMMA tSTAR arg_value
- {
- result = val[0] << @builder.splat(val[2], val[3])
- }
-
- mrhs: args tCOMMA arg_value
- {
- result = val[0] << val[2]
- }
- | args tCOMMA tSTAR arg_value
- {
- result = val[0] << @builder.splat(val[2], val[3])
- }
- | tSTAR arg_value
- {
- result = [ @builder.splat(val[0], val[1]) ]
- }
-
- primary: literal
- | strings
- | xstring
- | regexp
- | words
- | qwords
- | var_ref
- | backref
- | tFID
- {
- result = @builder.call_method(nil, nil, val[0])
- }
- | kBEGIN bodystmt kEND
- {
- result = @builder.begin_keyword(val[0], val[1], val[2])
- }
- | tLPAREN_ARG
- {
- result = @lexer.cmdarg.dup
- @lexer.cmdarg.clear
- }
- expr
- {
- @lexer.state = :expr_endarg
- }
- opt_nl tRPAREN
- {
- @lexer.cmdarg = val[1]
-
- result = @builder.begin(val[0], val[2], val[5])
- }
- | tLPAREN compstmt tRPAREN
- {
- result = @builder.begin(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tCONSTANT
- {
- result = @builder.const_fetch(val[0], val[1], val[2])
- }
- | tCOLON3 tCONSTANT
- {
- result = @builder.const_global(val[0], val[1])
- }
- | tLBRACK aref_args tRBRACK
- {
- result = @builder.array(val[0], val[1], val[2])
- }
- | tLBRACE assoc_list tRCURLY
- {
- result = @builder.associate(val[0], val[1], val[2])
- }
- | kRETURN
- {
- result = @builder.keyword_cmd(:return, val[0])
- }
- | kYIELD tLPAREN2 call_args rparen
- {
- result = @builder.keyword_cmd(:yield, val[0], val[1], val[2], val[3])
- }
- | kYIELD tLPAREN2 rparen
- {
- result = @builder.keyword_cmd(:yield, val[0], val[1], [], val[2])
- }
- | kYIELD
- {
- result = @builder.keyword_cmd(:yield, val[0])
- }
- | kDEFINED opt_nl tLPAREN2 expr rparen
- {
- result = @builder.keyword_cmd(:defined?, val[0],
- val[2], [ val[3] ], val[4])
- }
- | kNOT tLPAREN2 expr rparen
- {
- result = @builder.not_op(val[0], val[1], val[2], val[3])
- }
- | kNOT tLPAREN2 rparen
- {
- result = @builder.not_op(val[0], val[1], nil, val[2])
- }
- | operation brace_block
- {
- method_call = @builder.call_method(nil, nil, val[0])
-
- begin_t, args, body, end_t = val[1]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
- | method_call
- | method_call brace_block
- {
- begin_t, args, body, end_t = val[1]
- result = @builder.block(val[0],
- begin_t, args, body, end_t)
- }
- | tLAMBDA lambda
- {
- lambda_call = @builder.call_lambda(val[0])
-
- args, (begin_t, body, end_t) = val[1]
- result = @builder.block(lambda_call,
- begin_t, args, body, end_t)
- }
- | kIF expr_value then compstmt if_tail kEND
- {
- else_t, else_ = val[4]
- result = @builder.condition(val[0], val[1], val[2],
- val[3], else_t,
- else_, val[5])
- }
- | kUNLESS expr_value then compstmt opt_else kEND
- {
- else_t, else_ = val[4]
- result = @builder.condition(val[0], val[1], val[2],
- else_, else_t,
- val[3], val[5])
- }
- | kWHILE
- {
- @lexer.cond.push(true)
- }
- expr_value do
- {
- @lexer.cond.pop
- }
- compstmt kEND
- {
- result = @builder.loop(:while, val[0], val[2], val[3],
- val[5], val[6])
- }
- | kUNTIL
- {
- @lexer.cond.push(true)
- }
- expr_value do
- {
- @lexer.cond.pop
- }
- compstmt kEND
- {
- result = @builder.loop(:until, val[0], val[2], val[3],
- val[5], val[6])
- }
- | kCASE expr_value opt_terms case_body kEND
- {
- *when_bodies, (else_t, else_body) = *val[3]
-
- result = @builder.case(val[0], val[1],
- when_bodies, else_t, else_body,
- val[4])
- }
- | kCASE opt_terms case_body kEND
- {
- *when_bodies, (else_t, else_body) = *val[2]
-
- result = @builder.case(val[0], nil,
- when_bodies, else_t, else_body,
- val[3])
- }
- | kFOR for_var kIN
- {
- @lexer.cond.push(true)
- }
- expr_value do
- {
- @lexer.cond.pop
- }
- compstmt kEND
- {
- result = @builder.for(val[0], val[1],
- val[2], val[4],
- val[5], val[7], val[8])
- }
- | kCLASS cpath superclass
- {
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- bodystmt kEND
- {
- if in_def?
- diagnostic :error, :class_in_def, nil, val[0]
- end
-
- lt_t, superclass = val[2]
- result = @builder.def_class(val[0], val[1],
- lt_t, superclass,
- val[4], val[5])
-
- @lexer.pop_cmdarg
- @static_env.unextend
- }
- | kCLASS tLSHFT expr term
- {
- result = @def_level
- @def_level = 0
-
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- bodystmt kEND
- {
- result = @builder.def_sclass(val[0], val[1], val[2],
- val[5], val[6])
-
- @lexer.pop_cmdarg
- @static_env.unextend
-
- @def_level = val[4]
- }
- | kMODULE cpath
- {
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- bodystmt kEND
- {
- if in_def?
- diagnostic :error, :module_in_def, nil, val[0]
- end
-
- result = @builder.def_module(val[0], val[1],
- val[3], val[4])
-
- @lexer.pop_cmdarg
- @static_env.unextend
- }
- | kDEF fname
- {
- @def_level += 1
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- f_arglist bodystmt kEND
- {
- result = @builder.def_method(val[0], val[1],
- val[3], val[4], val[5])
-
- @lexer.pop_cmdarg
- @static_env.unextend
- @def_level -= 1
- }
- | kDEF singleton dot_or_colon
- {
- @lexer.state = :expr_fname
- }
- fname
- {
- @def_level += 1
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- f_arglist bodystmt kEND
- {
- result = @builder.def_singleton(val[0], val[1], val[2],
- val[4], val[6], val[7], val[8])
-
- @lexer.pop_cmdarg
- @static_env.unextend
- @def_level -= 1
- }
- | kBREAK
- {
- result = @builder.keyword_cmd(:break, val[0])
- }
- | kNEXT
- {
- result = @builder.keyword_cmd(:next, val[0])
- }
- | kREDO
- {
- result = @builder.keyword_cmd(:redo, val[0])
- }
- | kRETRY
- {
- result = @builder.keyword_cmd(:retry, val[0])
- }
-
- primary_value: primary
-
- then: term
- | kTHEN
- | term kTHEN
- {
- result = val[1]
- }
-
- do: term
- | kDO_COND
-
- if_tail: opt_else
- | kELSIF expr_value then compstmt if_tail
- {
- else_t, else_ = val[4]
- result = [ val[0],
- @builder.condition(val[0], val[1], val[2],
- val[3], else_t,
- else_, nil),
- ]
- }
-
- opt_else: none
- | kELSE compstmt
- {
- result = val
- }
-
- for_var: lhs
- | mlhs
-
- f_marg: f_norm_arg
- {
- @static_env.declare val[0][0]
-
- result = @builder.arg(val[0])
- }
- | tLPAREN f_margs rparen
- {
- result = @builder.multi_lhs(val[0], val[1], val[2])
- }
-
- f_marg_list: f_marg
- {
- result = [ val[0] ]
- }
- | f_marg_list tCOMMA f_marg
- {
- result = val[0] << val[2]
- }
-
- f_margs: f_marg_list
- | f_marg_list tCOMMA tSTAR f_norm_arg
- {
- @static_env.declare val[3][0]
-
- result = val[0].
- push(@builder.restarg(val[2], val[3]))
- }
- | f_marg_list tCOMMA tSTAR f_norm_arg tCOMMA f_marg_list
- {
- @static_env.declare val[3][0]
-
- result = val[0].
- push(@builder.restarg(val[2], val[3])).
- concat(val[5])
- }
- | f_marg_list tCOMMA tSTAR
- {
- result = val[0].
- push(@builder.restarg(val[2]))
- }
- | f_marg_list tCOMMA tSTAR tCOMMA f_marg_list
- {
- result = val[0].
- push(@builder.restarg(val[2])).
- concat(val[4])
- }
- | tSTAR f_norm_arg
- {
- @static_env.declare val[1][0]
-
- result = [ @builder.restarg(val[0], val[1]) ]
- }
- | tSTAR f_norm_arg tCOMMA f_marg_list
- {
- @static_env.declare val[1][0]
-
- result = [ @builder.restarg(val[0], val[1]),
- *val[3] ]
- }
- | tSTAR
- {
- result = [ @builder.restarg(val[0]) ]
- }
- | tSTAR tCOMMA f_marg_list
- {
- result = [ @builder.restarg(val[0]),
- *val[2] ]
- }
-
- block_param: f_arg tCOMMA f_block_optarg tCOMMA f_rest_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg tCOMMA f_block_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[6]).
- concat(val[7])
- }
- | f_arg tCOMMA f_block_optarg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_arg tCOMMA f_block_optarg tCOMMA f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg tCOMMA f_rest_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_arg tCOMMA
- | f_arg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg opt_f_block_arg
- {
- result = val[0].concat(val[1])
- }
- | f_block_optarg tCOMMA f_rest_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_block_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_block_optarg opt_f_block_arg
- {
- result = val[0].
- concat(val[1])
- }
- | f_block_optarg tCOMMA f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_rest_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[1])
- }
- | f_rest_arg tCOMMA f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_block_arg
- {
- result = [ val[0] ]
- }
-
- opt_block_param: # nothing
- {
- result = @builder.args(nil, [], nil)
- }
- | block_param_def
- {
- @lexer.state = :expr_value
- }
-
- block_param_def: tPIPE opt_bv_decl tPIPE
- {
- result = @builder.args(val[0], val[1], val[2])
- }
- | tOROP
- {
- result = @builder.args(val[0], [], val[0])
- }
- | tPIPE block_param opt_bv_decl tPIPE
- {
- result = @builder.args(val[0], val[1].concat(val[2]), val[3])
- }
-
- opt_bv_decl: # nothing
- {
- result = []
- }
- | tSEMI bv_decls
- {
- result = val[1]
- }
-
- bv_decls: bvar
- {
- result = [ val[0] ]
- }
- | bv_decls tCOMMA bvar
- {
- result = val[0] << val[2]
- }
-
- bvar: tIDENTIFIER
- {
- result = @builder.shadowarg(val[0])
- }
- | f_bad_arg
-
- lambda: {
- @static_env.extend_dynamic
- }
- f_larglist lambda_body
- {
- result = [ val[1], val[2] ]
-
- @static_env.unextend
- }
-
- f_larglist: tLPAREN2 f_args opt_bv_decl rparen
- {
- result = @builder.args(val[0], val[1].concat(val[2]), val[3])
- }
- | f_args
- {
- result = @builder.args(nil, val[0], nil)
- }
-
- lambda_body: tLAMBEG compstmt tRCURLY
- {
- result = [ val[0], val[1], val[2] ]
- }
- | kDO_LAMBDA compstmt kEND
- {
- result = [ val[0], val[1], val[2] ]
- }
-
- do_block: kDO_BLOCK
- {
- @static_env.extend_dynamic
- }
- opt_block_param compstmt kEND
- {
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
- }
-
- block_call: command do_block
- {
- begin_t, block_args, body, end_t = val[1]
- result = @builder.block(val[0],
- begin_t, block_args, body, end_t)
- }
- | block_call tDOT operation2 opt_paren_args
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
- }
- | block_call tCOLON2 operation2 opt_paren_args
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
- }
-
- method_call: operation paren_args
- {
- lparen_t, args, rparen_t = val[1]
- result = @builder.call_method(nil, nil, val[0],
- lparen_t, args, rparen_t)
- }
- | primary_value tDOT operation2 opt_paren_args
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
- }
- | primary_value tCOLON2 operation2 paren_args
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
- }
- | primary_value tCOLON2 operation3
- {
- result = @builder.call_method(val[0], val[1], val[2])
- }
- | primary_value tDOT paren_args
- {
- lparen_t, args, rparen_t = val[2]
- result = @builder.call_method(val[0], val[1], nil,
- lparen_t, args, rparen_t)
- }
- | primary_value tCOLON2 paren_args
- {
- lparen_t, args, rparen_t = val[2]
- result = @builder.call_method(val[0], val[1], nil,
- lparen_t, args, rparen_t)
- }
- | kSUPER paren_args
- {
- lparen_t, args, rparen_t = val[1]
- result = @builder.keyword_cmd(:super, val[0],
- lparen_t, args, rparen_t)
- }
- | kSUPER
- {
- result = @builder.keyword_cmd(:zsuper, val[0])
- }
- | primary_value tLBRACK2 opt_call_args rbracket
- {
- result = @builder.index(val[0], val[1], val[2], val[3])
- }
-
- brace_block: tLCURLY
- {
- @static_env.extend_dynamic
- }
- opt_block_param compstmt tRCURLY
- {
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
- }
- | kDO
- {
- @static_env.extend_dynamic
- }
- opt_block_param compstmt kEND
- {
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
- }
-
- case_body: kWHEN args then compstmt cases
- {
- result = [ @builder.when(val[0], val[1], val[2], val[3]),
- *val[4] ]
- }
-
- cases: opt_else
- {
- result = [ val[0] ]
- }
- | case_body
-
- opt_rescue: kRESCUE exc_list exc_var then compstmt opt_rescue
- {
- assoc_t, exc_var = val[2]
-
- if val[1]
- exc_list = @builder.array(nil, val[1], nil)
- end
-
- result = [ @builder.rescue_body(val[0],
- exc_list, assoc_t, exc_var,
- val[3], val[4]),
- *val[5] ]
- }
- |
- {
- result = []
- }
-
- exc_list: arg_value
- {
- result = [ val[0] ]
- }
- | mrhs
- | none
-
- exc_var: tASSOC lhs
- {
- result = [ val[0], val[1] ]
- }
- | none
-
- opt_ensure: kENSURE compstmt
- {
- result = [ val[0], val[1] ]
- }
- | none
-
- literal: numeric
- | symbol
- | dsym
-
- strings: string
- {
- result = @builder.string_compose(nil, val[0], nil)
- }
-
- string: string1
- {
- result = [ val[0] ]
- }
- | string string1
- {
- result = val[0] << val[1]
- }
-
- string1: tSTRING_BEG string_contents tSTRING_END
- {
- result = @builder.string_compose(val[0], val[1], val[2])
- }
- | tSTRING
- {
- result = @builder.string(val[0])
- }
- | tCHARACTER
- {
- result = @builder.character(val[0])
- }
-
- xstring: tXSTRING_BEG xstring_contents tSTRING_END
- {
- result = @builder.xstring_compose(val[0], val[1], val[2])
- }
-
- regexp: tREGEXP_BEG regexp_contents tSTRING_END tREGEXP_OPT
- {
- opts = @builder.regexp_options(val[3])
- result = @builder.regexp_compose(val[0], val[1], val[2], opts)
- }
-
- words: tWORDS_BEG word_list tSTRING_END
- {
- result = @builder.words_compose(val[0], val[1], val[2])
- }
-
- word_list: # nothing
- {
- result = []
- }
- | word_list word tSPACE
- {
- result = val[0] << @builder.word(val[1])
- }
-
- word: string_content
- {
- result = [ val[0] ]
- }
- | word string_content
- {
- result = val[0] << val[1]
- }
-
- qwords: tQWORDS_BEG qword_list tSTRING_END
- {
- result = @builder.words_compose(val[0], val[1], val[2])
- }
-
- qword_list: # nothing
- {
- result = []
- }
- | qword_list tSTRING_CONTENT tSPACE
- {
- result = val[0] << @builder.string_internal(val[1])
- }
-
- string_contents: # nothing
- {
- result = []
- }
- | string_contents string_content
- {
- result = val[0] << val[1]
- }
-
-xstring_contents: # nothing
- {
- result = []
- }
- | xstring_contents string_content
- {
- result = val[0] << val[1]
- }
-
-regexp_contents: # nothing
- {
- result = []
- }
- | regexp_contents string_content
- {
- result = val[0] << val[1]
- }
-
- string_content: tSTRING_CONTENT
- {
- result = @builder.string_internal(val[0])
- }
- | tSTRING_DVAR string_dvar
- {
- result = val[1]
- }
- | tSTRING_DBEG
- {
- @lexer.cond.push(false)
- @lexer.cmdarg.push(false)
- }
- compstmt tRCURLY
- {
- @lexer.cond.lexpop
- @lexer.cmdarg.lexpop
-
- result = @builder.begin(val[0], val[2], val[3])
- }
-
- string_dvar: tGVAR
- {
- result = @builder.gvar(val[0])
- }
- | tIVAR
- {
- result = @builder.ivar(val[0])
- }
- | tCVAR
- {
- result = @builder.cvar(val[0])
- }
- | backref
-
-
- symbol: tSYMBOL
- {
- result = @builder.symbol(val[0])
- }
-
- dsym: tSYMBEG xstring_contents tSTRING_END
- {
- result = @builder.symbol_compose(val[0], val[1], val[2])
- }
-
- numeric: tINTEGER
- {
- result = @builder.integer(val[0])
- }
- | tFLOAT
- {
- result = @builder.float(val[0])
- }
- | tUMINUS_NUM tINTEGER =tLOWEST
- {
- result = @builder.negate(val[0],
- @builder.integer(val[1]))
- }
- | tUMINUS_NUM tFLOAT =tLOWEST
- {
- result = @builder.negate(val[0],
- @builder.float(val[1]))
- }
-
- user_variable: tIDENTIFIER
- {
- result = @builder.ident(val[0])
- }
- | tIVAR
- {
- result = @builder.ivar(val[0])
- }
- | tGVAR
- {
- result = @builder.gvar(val[0])
- }
- | tCONSTANT
- {
- result = @builder.const(val[0])
- }
- | tCVAR
- {
- result = @builder.cvar(val[0])
- }
-
-keyword_variable: kNIL
- {
- result = @builder.nil(val[0])
- }
- | kSELF
- {
- result = @builder.self(val[0])
- }
- | kTRUE
- {
- result = @builder.true(val[0])
- }
- | kFALSE
- {
- result = @builder.false(val[0])
- }
- | k__FILE__
- {
- result = @builder.__FILE__(val[0])
- }
- | k__LINE__
- {
- result = @builder.__LINE__(val[0])
- }
- | k__ENCODING__
- {
- result = @builder.__ENCODING__(val[0])
- }
-
- var_ref: user_variable
- {
- result = @builder.accessible(val[0])
- }
- | keyword_variable
- {
- result = @builder.accessible(val[0])
- }
-
- var_lhs: user_variable
- {
- result = @builder.assignable(val[0])
- }
- | keyword_variable
- {
- result = @builder.assignable(val[0])
- }
-
- backref: tNTH_REF
- {
- result = @builder.nth_ref(val[0])
- }
- | tBACK_REF
- {
- result = @builder.back_ref(val[0])
- }
-
- superclass: term
- {
- result = nil
- }
- | tLT expr_value term
- {
- result = [ val[0], val[1] ]
- }
- | error term
- {
- yyerrok
- result = nil
- }
-
- f_arglist: tLPAREN2 f_args rparen
- {
- result = @builder.args(val[0], val[1], val[2])
-
- @lexer.state = :expr_value
- }
- | f_args term
- {
- result = @builder.args(nil, val[0], nil)
- }
-
- f_args: f_arg tCOMMA f_optarg tCOMMA f_rest_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg tCOMMA f_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[6]).
- concat(val[7])
- }
- | f_arg tCOMMA f_optarg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_arg tCOMMA f_optarg tCOMMA f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg tCOMMA f_rest_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_arg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[1])
- }
- | f_optarg tCOMMA f_rest_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_optarg opt_f_block_arg
- {
- result = val[0].
- concat(val[1])
- }
- | f_optarg tCOMMA f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_rest_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[1])
- }
- | f_rest_arg tCOMMA f_arg opt_f_block_arg
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_block_arg
- {
- result = [ val[0] ]
- }
- | # nothing
- {
- result = []
- }
-
- f_bad_arg: tCONSTANT
- {
- diagnostic :error, :argument_const, nil, val[0]
- }
- | tIVAR
- {
- diagnostic :error, :argument_ivar, nil, val[0]
- }
- | tGVAR
- {
- diagnostic :error, :argument_gvar, nil, val[0]
- }
- | tCVAR
- {
- diagnostic :error, :argument_cvar, nil, val[0]
- }
-
- f_norm_arg: f_bad_arg
- | tIDENTIFIER
-
- f_arg_item: f_norm_arg
- {
- @static_env.declare val[0][0]
-
- result = @builder.arg(val[0])
- }
- | tLPAREN f_margs rparen
- {
- result = @builder.multi_lhs(val[0], val[1], val[2])
- }
-
- f_arg: f_arg_item
- {
- result = [ val[0] ]
- }
- | f_arg tCOMMA f_arg_item
- {
- result = val[0] << val[2]
- }
-
- f_opt: tIDENTIFIER tEQL arg_value
- {
- @static_env.declare val[0][0]
-
- result = @builder.optarg(val[0], val[1], val[2])
- }
-
- f_block_opt: tIDENTIFIER tEQL primary_value
- {
- @static_env.declare val[0][0]
-
- result = @builder.optarg(val[0], val[1], val[2])
- }
-
- f_block_optarg: f_block_opt
- {
- result = [ val[0] ]
- }
- | f_block_optarg tCOMMA f_block_opt
- {
- result = val[0] << val[2]
- }
-
- f_optarg: f_opt
- {
- result = [ val[0] ]
- }
- | f_optarg tCOMMA f_opt
- {
- result = val[0] << val[2]
- }
-
- restarg_mark: tSTAR2 | tSTAR
-
- f_rest_arg: restarg_mark tIDENTIFIER
- {
- @static_env.declare val[1][0]
-
- result = [ @builder.restarg(val[0], val[1]) ]
- }
- | restarg_mark
- {
- result = [ @builder.restarg(val[0]) ]
- }
-
- blkarg_mark: tAMPER2 | tAMPER
-
- f_block_arg: blkarg_mark tIDENTIFIER
- {
- @static_env.declare val[1][0]
-
- result = @builder.blockarg(val[0], val[1])
- }
-
- opt_f_block_arg: tCOMMA f_block_arg
- {
- result = [ val[1] ]
- }
- | # nothing
- {
- result = []
- }
-
- singleton: var_ref
- | tLPAREN2 expr rparen
- {
- result = val[1]
- }
-
- assoc_list: # nothing
- {
- result = []
- }
- | assocs trailer
-
- assocs: assoc
- {
- result = [ val[0] ]
- }
- | assocs tCOMMA assoc
- {
- result = val[0] << val[2]
- }
-
- assoc: arg_value tASSOC arg_value
- {
- result = @builder.pair(val[0], val[1], val[2])
- }
- | tLABEL arg_value
- {
- result = @builder.pair_keyword(val[0], val[1])
- }
-
- operation: tIDENTIFIER | tCONSTANT | tFID
- operation2: tIDENTIFIER | tCONSTANT | tFID | op
- operation3: tIDENTIFIER | tFID | op
- dot_or_colon: tDOT | tCOLON2
- opt_terms: | terms
- opt_nl: | tNL
- rparen: opt_nl tRPAREN
- {
- result = val[1]
- }
- rbracket: opt_nl tRBRACK
- {
- result = val[1]
- }
- trailer: | tNL | tCOMMA
-
- term: tSEMI
- {
- yyerrok
- }
- | tNL
-
- terms: term
- | terms tSEMI
-
- none: # nothing
- {
- result = nil
- }
-end
-
----- header
-
-require 'parser'
-
-Parser.check_for_encoding_support
-
----- inner
-
- def version
- 19
- end
-
- def default_encoding
- Encoding::BINARY
- end
diff --git a/test/racc/assets/ruby20.y b/test/racc/assets/ruby20.y
deleted file mode 100644
index 6e07734778..0000000000
--- a/test/racc/assets/ruby20.y
+++ /dev/null
@@ -1,2350 +0,0 @@
-# Copyright (c) 2013 Peter Zotov <whitequark@whitequark.org>
-#
-# Parts of the source are derived from ruby_parser:
-# Copyright (c) Ryan Davis, seattle.rb
-#
-# MIT License
-#
-# 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.
-
-class Parser::Ruby20
-
-token kCLASS kMODULE kDEF kUNDEF kBEGIN kRESCUE kENSURE kEND kIF kUNLESS
- kTHEN kELSIF kELSE kCASE kWHEN kWHILE kUNTIL kFOR kBREAK kNEXT
- kREDO kRETRY kIN kDO kDO_COND kDO_BLOCK kDO_LAMBDA kRETURN kYIELD kSUPER
- kSELF kNIL kTRUE kFALSE kAND kOR kNOT kIF_MOD kUNLESS_MOD kWHILE_MOD
- kUNTIL_MOD kRESCUE_MOD kALIAS kDEFINED klBEGIN klEND k__LINE__
- k__FILE__ k__ENCODING__ tIDENTIFIER tFID tGVAR tIVAR tCONSTANT
- tLABEL tCVAR tNTH_REF tBACK_REF tSTRING_CONTENT tINTEGER tFLOAT
- tREGEXP_END tUPLUS tUMINUS tUMINUS_NUM tPOW tCMP tEQ tEQQ tNEQ
- tGEQ tLEQ tANDOP tOROP tMATCH tNMATCH tDOT tDOT2 tDOT3 tAREF
- tASET tLSHFT tRSHFT tCOLON2 tCOLON3 tOP_ASGN tASSOC tLPAREN
- tLPAREN2 tRPAREN tLPAREN_ARG tLBRACK tLBRACK2 tRBRACK tLBRACE
- tLBRACE_ARG tSTAR tSTAR2 tAMPER tAMPER2 tTILDE tPERCENT tDIVIDE
- tDSTAR tPLUS tMINUS tLT tGT tPIPE tBANG tCARET tLCURLY tRCURLY
- tBACK_REF2 tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG tREGEXP_OPT
- tWORDS_BEG tQWORDS_BEG tSYMBOLS_BEG tQSYMBOLS_BEG tSTRING_DBEG
- tSTRING_DVAR tSTRING_END tSTRING_DEND tSTRING tSYMBOL
- tNL tEH tCOLON tCOMMA tSPACE tSEMI tLAMBDA tLAMBEG tCHARACTER
-
-prechigh
- right tBANG tTILDE tUPLUS
- right tPOW
- right tUMINUS_NUM tUMINUS
- left tSTAR2 tDIVIDE tPERCENT
- left tPLUS tMINUS
- left tLSHFT tRSHFT
- left tAMPER2
- left tPIPE tCARET
- left tGT tGEQ tLT tLEQ
- nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH
- left tANDOP
- left tOROP
- nonassoc tDOT2 tDOT3
- right tEH tCOLON
- left kRESCUE_MOD
- right tEQL tOP_ASGN
- nonassoc kDEFINED
- right kNOT
- left kOR kAND
- nonassoc kIF_MOD kUNLESS_MOD kWHILE_MOD kUNTIL_MOD
- nonassoc tLBRACE_ARG
- nonassoc tLOWEST
-preclow
-
-rule
-
- program: top_compstmt
-
- top_compstmt: top_stmts opt_terms
- {
- result = @builder.compstmt(val[0])
- }
-
- top_stmts: # nothing
- {
- result = []
- }
- | top_stmt
- {
- result = [ val[0] ]
- }
- | top_stmts terms top_stmt
- {
- result = val[0] << val[2]
- }
- | error top_stmt
- {
- result = [ val[1] ]
- }
-
- top_stmt: stmt
- | klBEGIN tLCURLY top_compstmt tRCURLY
- {
- result = @builder.preexe(val[0], val[1], val[2], val[3])
- }
-
- bodystmt: compstmt opt_rescue opt_else opt_ensure
- {
- rescue_bodies = val[1]
- else_t, else_ = val[2]
- ensure_t, ensure_ = val[3]
-
- if rescue_bodies.empty? && !else_.nil?
- diagnostic :warning, :useless_else, nil, else_t
- end
-
- result = @builder.begin_body(val[0],
- rescue_bodies,
- else_t, else_,
- ensure_t, ensure_)
- }
-
- compstmt: stmts opt_terms
- {
- result = @builder.compstmt(val[0])
- }
-
- stmts: # nothing
- {
- result = []
- }
- | stmt_or_begin
- {
- result = [ val[0] ]
- }
- | stmts terms stmt_or_begin
- {
- result = val[0] << val[2]
- }
- | error stmt
- {
- result = [ val[1] ]
- }
-
- stmt_or_begin: stmt
- | klBEGIN tLCURLY top_compstmt tRCURLY
- {
- if in_def?
- diagnostic :error, :begin_in_method, nil, val[0]
- end
-
- result = @builder.preexe(val[0], val[1], val[2], val[3])
- }
-
- stmt: kALIAS fitem
- {
- @lexer.state = :expr_fname
- }
- fitem
- {
- result = @builder.alias(val[0], val[1], val[3])
- }
- | kALIAS tGVAR tGVAR
- {
- result = @builder.alias(val[0],
- @builder.gvar(val[1]),
- @builder.gvar(val[2]))
- }
- | kALIAS tGVAR tBACK_REF
- {
- result = @builder.alias(val[0],
- @builder.gvar(val[1]),
- @builder.back_ref(val[2]))
- }
- | kALIAS tGVAR tNTH_REF
- {
- diagnostic :error, :nth_ref_alias, nil, val[2]
- }
- | kUNDEF undef_list
- {
- result = @builder.undef_method(val[0], val[1])
- }
- | stmt kIF_MOD expr_value
- {
- result = @builder.condition_mod(val[0], nil,
- val[1], val[2])
- }
- | stmt kUNLESS_MOD expr_value
- {
- result = @builder.condition_mod(nil, val[0],
- val[1], val[2])
- }
- | stmt kWHILE_MOD expr_value
- {
- result = @builder.loop_mod(:while, val[0], val[1], val[2])
- }
- | stmt kUNTIL_MOD expr_value
- {
- result = @builder.loop_mod(:until, val[0], val[1], val[2])
- }
- | stmt kRESCUE_MOD stmt
- {
- rescue_body = @builder.rescue_body(val[1],
- nil, nil, nil,
- nil, val[2])
-
- result = @builder.begin_body(val[0], [ rescue_body ])
- }
- | klEND tLCURLY compstmt tRCURLY
- {
- result = @builder.postexe(val[0], val[1], val[2], val[3])
- }
- | command_asgn
- | mlhs tEQL command_call
- {
- result = @builder.multi_assign(val[0], val[1], val[2])
- }
- | var_lhs tOP_ASGN command_call
- {
- result = @builder.op_assign(val[0], val[1], val[2])
- }
- | primary_value tLBRACK2 opt_call_args rbracket tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.index(
- val[0], val[1], val[2], val[3]),
- val[4], val[5])
- }
- | primary_value tDOT tIDENTIFIER tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tDOT tCONSTANT tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | backref tOP_ASGN command_call
- {
- @builder.op_assign(val[0], val[1], val[2])
- }
- | lhs tEQL mrhs
- {
- result = @builder.assign(val[0], val[1],
- @builder.array(nil, val[2], nil))
- }
- | mlhs tEQL arg_value
- {
- result = @builder.multi_assign(val[0], val[1], val[2])
- }
- | mlhs tEQL mrhs
- {
- result = @builder.multi_assign(val[0], val[1],
- @builder.array(nil, val[2], nil))
- }
- | expr
-
- command_asgn: lhs tEQL command_call
- {
- result = @builder.assign(val[0], val[1], val[2])
- }
- | lhs tEQL command_asgn
- {
- result = @builder.assign(val[0], val[1], val[2])
- }
-
- expr: command_call
- | expr kAND expr
- {
- result = @builder.logical_op(:and, val[0], val[1], val[2])
- }
- | expr kOR expr
- {
- result = @builder.logical_op(:or, val[0], val[1], val[2])
- }
- | kNOT opt_nl expr
- {
- result = @builder.not_op(val[0], nil, val[2], nil)
- }
- | tBANG command_call
- {
- result = @builder.not_op(val[0], nil, val[1], nil)
- }
- | arg
-
- expr_value: expr
-
- command_call: command
- | block_command
-
- block_command: block_call
- | block_call dot_or_colon operation2 command_args
- {
- result = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
- }
-
- cmd_brace_block: tLBRACE_ARG
- {
- @static_env.extend_dynamic
- }
- opt_block_param compstmt tRCURLY
- {
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
- }
-
- fcall: operation
-
- command: fcall command_args =tLOWEST
- {
- result = @builder.call_method(nil, nil, val[0],
- nil, val[1], nil)
- }
- | fcall command_args cmd_brace_block
- {
- method_call = @builder.call_method(nil, nil, val[0],
- nil, val[1], nil)
-
- begin_t, args, body, end_t = val[2]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
- | primary_value tDOT operation2 command_args =tLOWEST
- {
- result = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
- }
- | primary_value tDOT operation2 command_args cmd_brace_block
- {
- method_call = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
-
- begin_t, args, body, end_t = val[4]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
- | primary_value tCOLON2 operation2 command_args =tLOWEST
- {
- result = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
- }
- | primary_value tCOLON2 operation2 command_args cmd_brace_block
- {
- method_call = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
-
- begin_t, args, body, end_t = val[4]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
- | kSUPER command_args
- {
- result = @builder.keyword_cmd(:super, val[0],
- nil, val[1], nil)
- }
- | kYIELD command_args
- {
- result = @builder.keyword_cmd(:yield, val[0],
- nil, val[1], nil)
- }
- | kRETURN call_args
- {
- result = @builder.keyword_cmd(:return, val[0],
- nil, val[1], nil)
- }
- | kBREAK call_args
- {
- result = @builder.keyword_cmd(:break, val[0],
- nil, val[1], nil)
- }
- | kNEXT call_args
- {
- result = @builder.keyword_cmd(:next, val[0],
- nil, val[1], nil)
- }
-
- mlhs: mlhs_basic
- {
- result = @builder.multi_lhs(nil, val[0], nil)
- }
- | tLPAREN mlhs_inner rparen
- {
- result = @builder.begin(val[0], val[1], val[2])
- }
-
- mlhs_inner: mlhs_basic
- {
- result = @builder.multi_lhs(nil, val[0], nil)
- }
- | tLPAREN mlhs_inner rparen
- {
- result = @builder.multi_lhs(val[0], val[1], val[2])
- }
-
- mlhs_basic: mlhs_head
- | mlhs_head mlhs_item
- {
- result = val[0].
- push(val[1])
- }
- | mlhs_head tSTAR mlhs_node
- {
- result = val[0].
- push(@builder.splat(val[1], val[2]))
- }
- | mlhs_head tSTAR mlhs_node tCOMMA mlhs_post
- {
- result = val[0].
- push(@builder.splat(val[1], val[2])).
- concat(val[4])
- }
- | mlhs_head tSTAR
- {
- result = val[0].
- push(@builder.splat(val[1]))
- }
- | mlhs_head tSTAR tCOMMA mlhs_post
- {
- result = val[0].
- push(@builder.splat(val[1])).
- concat(val[3])
- }
- | tSTAR mlhs_node
- {
- result = [ @builder.splat(val[0], val[1]) ]
- }
- | tSTAR mlhs_node tCOMMA mlhs_post
- {
- result = [ @builder.splat(val[0], val[1]),
- *val[3] ]
- }
- | tSTAR
- {
- result = [ @builder.splat(val[0]) ]
- }
- | tSTAR tCOMMA mlhs_post
- {
- result = [ @builder.splat(val[0]),
- *val[2] ]
- }
-
- mlhs_item: mlhs_node
- | tLPAREN mlhs_inner rparen
- {
- result = @builder.begin(val[0], val[1], val[2])
- }
-
- mlhs_head: mlhs_item tCOMMA
- {
- result = [ val[0] ]
- }
- | mlhs_head mlhs_item tCOMMA
- {
- result = val[0] << val[1]
- }
-
- mlhs_post: mlhs_item
- {
- result = [ val[0] ]
- }
- | mlhs_post tCOMMA mlhs_item
- {
- result = val[0] << val[2]
- }
-
- mlhs_node: user_variable
- {
- result = @builder.assignable(val[0])
- }
- | keyword_variable
- {
- result = @builder.assignable(val[0])
- }
- | primary_value tLBRACK2 opt_call_args rbracket
- {
- result = @builder.index_asgn(val[0], val[1], val[2], val[3])
- }
- | primary_value tDOT tIDENTIFIER
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tIDENTIFIER
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tDOT tCONSTANT
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tCONSTANT
- {
- result = @builder.assignable(
- @builder.const_fetch(val[0], val[1], val[2]))
- }
- | tCOLON3 tCONSTANT
- {
- result = @builder.assignable(
- @builder.const_global(val[0], val[1]))
- }
- | backref
- {
- result = @builder.assignable(val[0])
- }
-
- lhs: user_variable
- {
- result = @builder.assignable(val[0])
- }
- | keyword_variable
- {
- result = @builder.assignable(val[0])
- }
- | primary_value tLBRACK2 opt_call_args rbracket
- {
- result = @builder.index_asgn(val[0], val[1], val[2], val[3])
- }
- | primary_value tDOT tIDENTIFIER
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tIDENTIFIER
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tDOT tCONSTANT
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tCONSTANT
- {
- result = @builder.assignable(
- @builder.const_fetch(val[0], val[1], val[2]))
- }
- | tCOLON3 tCONSTANT
- {
- result = @builder.assignable(
- @builder.const_global(val[0], val[1]))
- }
- | backref
- {
- result = @builder.assignable(val[0])
- }
-
- cname: tIDENTIFIER
- {
- diagnostic :error, :module_name_const, nil, val[0]
- }
- | tCONSTANT
-
- cpath: tCOLON3 cname
- {
- result = @builder.const_global(val[0], val[1])
- }
- | cname
- {
- result = @builder.const(val[0])
- }
- | primary_value tCOLON2 cname
- {
- result = @builder.const_fetch(val[0], val[1], val[2])
- }
-
- fname: tIDENTIFIER | tCONSTANT | tFID
- | op
- | reswords
-
- fsym: fname
- {
- result = @builder.symbol(val[0])
- }
- | symbol
-
- fitem: fsym
- | dsym
-
- undef_list: fitem
- {
- result = [ val[0] ]
- }
- | undef_list tCOMMA
- {
- @lexer.state = :expr_fname
- }
- fitem
- {
- result = val[0] << val[3]
- }
-
- op: tPIPE | tCARET | tAMPER2 | tCMP | tEQ | tEQQ
- | tMATCH | tNMATCH | tGT | tGEQ | tLT | tLEQ
- | tNEQ | tLSHFT | tRSHFT | tPLUS | tMINUS | tSTAR2
- | tSTAR | tDIVIDE | tPERCENT | tPOW | tBANG | tTILDE
- | tUPLUS | tUMINUS | tAREF | tASET | tDSTAR | tBACK_REF2
-
- reswords: k__LINE__ | k__FILE__ | k__ENCODING__ | klBEGIN | klEND
- | kALIAS | kAND | kBEGIN | kBREAK | kCASE
- | kCLASS | kDEF | kDEFINED | kDO | kELSE
- | kELSIF | kEND | kENSURE | kFALSE | kFOR
- | kIN | kMODULE | kNEXT | kNIL | kNOT
- | kOR | kREDO | kRESCUE | kRETRY | kRETURN
- | kSELF | kSUPER | kTHEN | kTRUE | kUNDEF
- | kWHEN | kYIELD | kIF | kUNLESS | kWHILE
- | kUNTIL
-
- arg: lhs tEQL arg
- {
- result = @builder.assign(val[0], val[1], val[2])
- }
- | lhs tEQL arg kRESCUE_MOD arg
- {
- rescue_body = @builder.rescue_body(val[3],
- nil, nil, nil,
- nil, val[4])
-
- rescue_ = @builder.begin_body(val[2], [ rescue_body ])
-
- result = @builder.assign(val[0], val[1], rescue_)
- }
- | var_lhs tOP_ASGN arg
- {
- result = @builder.op_assign(val[0], val[1], val[2])
- }
- | var_lhs tOP_ASGN arg kRESCUE_MOD arg
- {
- rescue_body = @builder.rescue_body(val[3],
- nil, nil, nil,
- nil, val[4])
-
- rescue_ = @builder.begin_body(val[2], [ rescue_body ])
-
- result = @builder.op_assign(val[0], val[1], rescue_)
- }
- | primary_value tLBRACK2 opt_call_args rbracket tOP_ASGN arg
- {
- result = @builder.op_assign(
- @builder.index(
- val[0], val[1], val[2], val[3]),
- val[4], val[5])
- }
- | primary_value tDOT tIDENTIFIER tOP_ASGN arg
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tDOT tCONSTANT tOP_ASGN arg
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tCOLON2 tCONSTANT tOP_ASGN arg
- {
- const = @builder.const_op_assignable(
- @builder.const_fetch(val[0], val[1], val[2]))
- result = @builder.op_assign(const, val[3], val[4])
- }
- | tCOLON3 tCONSTANT tOP_ASGN arg
- {
- const = @builder.const_op_assignable(
- @builder.const_global(val[0], val[1]))
- result = @builder.op_assign(const, val[2], val[3])
- }
- | backref tOP_ASGN arg
- {
- result = @builder.op_assign(val[0], val[1], val[2])
- }
- | arg tDOT2 arg
- {
- result = @builder.range_inclusive(val[0], val[1], val[2])
- }
- | arg tDOT3 arg
- {
- result = @builder.range_exclusive(val[0], val[1], val[2])
- }
- | arg tPLUS arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tMINUS arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tSTAR2 arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tDIVIDE arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tPERCENT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tPOW arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | tUMINUS_NUM tINTEGER tPOW arg
- {
- result = @builder.unary_op(val[0],
- @builder.binary_op(
- @builder.integer(val[1]),
- val[2], val[3]))
- }
- | tUMINUS_NUM tFLOAT tPOW arg
- {
- result = @builder.unary_op(val[0],
- @builder.binary_op(
- @builder.float(val[1]),
- val[2], val[3]))
- }
- | tUPLUS arg
- {
- result = @builder.unary_op(val[0], val[1])
- }
- | tUMINUS arg
- {
- result = @builder.unary_op(val[0], val[1])
- }
- | arg tPIPE arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tCARET arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tAMPER2 arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tCMP arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tGT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tGEQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tLT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tLEQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tEQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tEQQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tNEQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tMATCH arg
- {
- result = @builder.match_op(val[0], val[1], val[2])
- }
- | arg tNMATCH arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | tBANG arg
- {
- result = @builder.not_op(val[0], nil, val[1], nil)
- }
- | tTILDE arg
- {
- result = @builder.unary_op(val[0], val[1])
- }
- | arg tLSHFT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tRSHFT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tANDOP arg
- {
- result = @builder.logical_op(:and, val[0], val[1], val[2])
- }
- | arg tOROP arg
- {
- result = @builder.logical_op(:or, val[0], val[1], val[2])
- }
- | kDEFINED opt_nl arg
- {
- result = @builder.keyword_cmd(:defined?, val[0], nil, [ val[2] ], nil)
- }
-
- | arg tEH arg opt_nl tCOLON arg
- {
- result = @builder.ternary(val[0], val[1],
- val[2], val[4], val[5])
- }
- | primary
-
- arg_value: arg
-
- aref_args: none
- | args trailer
- | args tCOMMA assocs trailer
- {
- result = val[0] << @builder.associate(nil, val[2], nil)
- }
- | assocs trailer
- {
- result = [ @builder.associate(nil, val[0], nil) ]
- }
-
- paren_args: tLPAREN2 opt_call_args rparen
- {
- result = val
- }
-
- opt_paren_args: # nothing
- {
- result = [ nil, [], nil ]
- }
- | paren_args
-
- opt_call_args: # nothing
- {
- result = []
- }
- | call_args
- | args tCOMMA
- | args tCOMMA assocs tCOMMA
- {
- result = val[0] << @builder.associate(nil, val[2], nil)
- }
- | assocs tCOMMA
- {
- result = [ @builder.associate(nil, val[0], nil) ]
- }
-
- call_args: command
- {
- result = [ val[0] ]
- }
- | args opt_block_arg
- {
- result = val[0].concat(val[1])
- }
- | assocs opt_block_arg
- {
- result = [ @builder.associate(nil, val[0], nil) ]
- result.concat(val[1])
- }
- | args tCOMMA assocs opt_block_arg
- {
- assocs = @builder.associate(nil, val[2], nil)
- result = val[0] << assocs
- result.concat(val[3])
- }
- | block_arg
- {
- result = [ val[0] ]
- }
-
- command_args: {
- result = @lexer.cmdarg.dup
- @lexer.cmdarg.push(true)
- }
- call_args
- {
- @lexer.cmdarg = val[0]
-
- result = val[1]
- }
-
- block_arg: tAMPER arg_value
- {
- result = @builder.block_pass(val[0], val[1])
- }
-
- opt_block_arg: tCOMMA block_arg
- {
- result = [ val[1] ]
- }
- | # nothing
- {
- result = []
- }
-
- args: arg_value
- {
- result = [ val[0] ]
- }
- | tSTAR arg_value
- {
- result = [ @builder.splat(val[0], val[1]) ]
- }
- | args tCOMMA arg_value
- {
- result = val[0] << val[2]
- }
- | args tCOMMA tSTAR arg_value
- {
- result = val[0] << @builder.splat(val[2], val[3])
- }
-
- mrhs: args tCOMMA arg_value
- {
- result = val[0] << val[2]
- }
- | args tCOMMA tSTAR arg_value
- {
- result = val[0] << @builder.splat(val[2], val[3])
- }
- | tSTAR arg_value
- {
- result = [ @builder.splat(val[0], val[1]) ]
- }
-
- primary: literal
- | strings
- | xstring
- | regexp
- | words
- | qwords
- | symbols
- | qsymbols
- | var_ref
- | backref
- | tFID
- {
- result = @builder.call_method(nil, nil, val[0])
- }
- | kBEGIN
- {
- result = @lexer.cmdarg.dup
- @lexer.cmdarg.clear
- }
- bodystmt kEND
- {
- @lexer.cmdarg = val[1]
-
- result = @builder.begin_keyword(val[0], val[2], val[3])
- }
- | tLPAREN_ARG
- {
- result = @lexer.cmdarg.dup
- @lexer.cmdarg.clear
- }
- expr
- {
- @lexer.state = :expr_endarg
- }
- opt_nl tRPAREN
- {
- @lexer.cmdarg = val[1]
-
- result = @builder.begin(val[0], val[2], val[5])
- }
- | tLPAREN_ARG
- {
- @lexer.state = :expr_endarg
- }
- opt_nl tRPAREN
- {
- result = @builder.begin(val[0], nil, val[3])
- }
- | tLPAREN compstmt tRPAREN
- {
- result = @builder.begin(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tCONSTANT
- {
- result = @builder.const_fetch(val[0], val[1], val[2])
- }
- | tCOLON3 tCONSTANT
- {
- result = @builder.const_global(val[0], val[1])
- }
- | tLBRACK aref_args tRBRACK
- {
- result = @builder.array(val[0], val[1], val[2])
- }
- | tLBRACE assoc_list tRCURLY
- {
- result = @builder.associate(val[0], val[1], val[2])
- }
- | kRETURN
- {
- result = @builder.keyword_cmd(:return, val[0])
- }
- | kYIELD tLPAREN2 call_args rparen
- {
- result = @builder.keyword_cmd(:yield, val[0], val[1], val[2], val[3])
- }
- | kYIELD tLPAREN2 rparen
- {
- result = @builder.keyword_cmd(:yield, val[0], val[1], [], val[2])
- }
- | kYIELD
- {
- result = @builder.keyword_cmd(:yield, val[0])
- }
- | kDEFINED opt_nl tLPAREN2 expr rparen
- {
- result = @builder.keyword_cmd(:defined?, val[0],
- val[2], [ val[3] ], val[4])
- }
- | kNOT tLPAREN2 expr rparen
- {
- result = @builder.not_op(val[0], val[1], val[2], val[3])
- }
- | kNOT tLPAREN2 rparen
- {
- result = @builder.not_op(val[0], val[1], nil, val[2])
- }
- | fcall brace_block
- {
- method_call = @builder.call_method(nil, nil, val[0])
-
- begin_t, args, body, end_t = val[1]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
- | method_call
- | method_call brace_block
- {
- begin_t, args, body, end_t = val[1]
- result = @builder.block(val[0],
- begin_t, args, body, end_t)
- }
- | tLAMBDA lambda
- {
- lambda_call = @builder.call_lambda(val[0])
-
- args, (begin_t, body, end_t) = val[1]
- result = @builder.block(lambda_call,
- begin_t, args, body, end_t)
- }
- | kIF expr_value then compstmt if_tail kEND
- {
- else_t, else_ = val[4]
- result = @builder.condition(val[0], val[1], val[2],
- val[3], else_t,
- else_, val[5])
- }
- | kUNLESS expr_value then compstmt opt_else kEND
- {
- else_t, else_ = val[4]
- result = @builder.condition(val[0], val[1], val[2],
- else_, else_t,
- val[3], val[5])
- }
- | kWHILE
- {
- @lexer.cond.push(true)
- }
- expr_value do
- {
- @lexer.cond.pop
- }
- compstmt kEND
- {
- result = @builder.loop(:while, val[0], val[2], val[3],
- val[5], val[6])
- }
- | kUNTIL
- {
- @lexer.cond.push(true)
- }
- expr_value do
- {
- @lexer.cond.pop
- }
- compstmt kEND
- {
- result = @builder.loop(:until, val[0], val[2], val[3],
- val[5], val[6])
- }
- | kCASE expr_value opt_terms case_body kEND
- {
- *when_bodies, (else_t, else_body) = *val[3]
-
- result = @builder.case(val[0], val[1],
- when_bodies, else_t, else_body,
- val[4])
- }
- | kCASE opt_terms case_body kEND
- {
- *when_bodies, (else_t, else_body) = *val[2]
-
- result = @builder.case(val[0], nil,
- when_bodies, else_t, else_body,
- val[3])
- }
- | kFOR for_var kIN
- {
- @lexer.cond.push(true)
- }
- expr_value do
- {
- @lexer.cond.pop
- }
- compstmt kEND
- {
- result = @builder.for(val[0], val[1],
- val[2], val[4],
- val[5], val[7], val[8])
- }
- | kCLASS cpath superclass
- {
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- bodystmt kEND
- {
- if in_def?
- diagnostic :error, :class_in_def, nil, val[0]
- end
-
- lt_t, superclass = val[2]
- result = @builder.def_class(val[0], val[1],
- lt_t, superclass,
- val[4], val[5])
-
- @lexer.pop_cmdarg
- @static_env.unextend
- }
- | kCLASS tLSHFT expr term
- {
- result = @def_level
- @def_level = 0
-
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- bodystmt kEND
- {
- result = @builder.def_sclass(val[0], val[1], val[2],
- val[5], val[6])
-
- @lexer.pop_cmdarg
- @static_env.unextend
-
- @def_level = val[4]
- }
- | kMODULE cpath
- {
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- bodystmt kEND
- {
- if in_def?
- diagnostic :error, :module_in_def, nil, val[0]
- end
-
- result = @builder.def_module(val[0], val[1],
- val[3], val[4])
-
- @lexer.pop_cmdarg
- @static_env.unextend
- }
- | kDEF fname
- {
- @def_level += 1
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- f_arglist bodystmt kEND
- {
- result = @builder.def_method(val[0], val[1],
- val[3], val[4], val[5])
-
- @lexer.pop_cmdarg
- @static_env.unextend
- @def_level -= 1
- }
- | kDEF singleton dot_or_colon
- {
- @lexer.state = :expr_fname
- }
- fname
- {
- @def_level += 1
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- f_arglist bodystmt kEND
- {
- result = @builder.def_singleton(val[0], val[1], val[2],
- val[4], val[6], val[7], val[8])
-
- @lexer.pop_cmdarg
- @static_env.unextend
- @def_level -= 1
- }
- | kBREAK
- {
- result = @builder.keyword_cmd(:break, val[0])
- }
- | kNEXT
- {
- result = @builder.keyword_cmd(:next, val[0])
- }
- | kREDO
- {
- result = @builder.keyword_cmd(:redo, val[0])
- }
- | kRETRY
- {
- result = @builder.keyword_cmd(:retry, val[0])
- }
-
- primary_value: primary
-
- then: term
- | kTHEN
- | term kTHEN
- {
- result = val[1]
- }
-
- do: term
- | kDO_COND
-
- if_tail: opt_else
- | kELSIF expr_value then compstmt if_tail
- {
- else_t, else_ = val[4]
- result = [ val[0],
- @builder.condition(val[0], val[1], val[2],
- val[3], else_t,
- else_, nil),
- ]
- }
-
- opt_else: none
- | kELSE compstmt
- {
- result = val
- }
-
- for_var: lhs
- | mlhs
-
- f_marg: f_norm_arg
- {
- @static_env.declare val[0][0]
-
- result = @builder.arg(val[0])
- }
- | tLPAREN f_margs rparen
- {
- result = @builder.multi_lhs(val[0], val[1], val[2])
- }
-
- f_marg_list: f_marg
- {
- result = [ val[0] ]
- }
- | f_marg_list tCOMMA f_marg
- {
- result = val[0] << val[2]
- }
-
- f_margs: f_marg_list
- | f_marg_list tCOMMA tSTAR f_norm_arg
- {
- @static_env.declare val[3][0]
-
- result = val[0].
- push(@builder.restarg(val[2], val[3]))
- }
- | f_marg_list tCOMMA tSTAR f_norm_arg tCOMMA f_marg_list
- {
- @static_env.declare val[3][0]
-
- result = val[0].
- push(@builder.restarg(val[2], val[3])).
- concat(val[5])
- }
- | f_marg_list tCOMMA tSTAR
- {
- result = val[0].
- push(@builder.restarg(val[2]))
- }
- | f_marg_list tCOMMA tSTAR tCOMMA f_marg_list
- {
- result = val[0].
- push(@builder.restarg(val[2])).
- concat(val[4])
- }
- | tSTAR f_norm_arg
- {
- @static_env.declare val[1][0]
-
- result = [ @builder.restarg(val[0], val[1]) ]
- }
- | tSTAR f_norm_arg tCOMMA f_marg_list
- {
- @static_env.declare val[1][0]
-
- result = [ @builder.restarg(val[0], val[1]),
- *val[3] ]
- }
- | tSTAR
- {
- result = [ @builder.restarg(val[0]) ]
- }
- | tSTAR tCOMMA f_marg_list
- {
- result = [ @builder.restarg(val[0]),
- *val[2] ]
- }
-
- block_args_tail: f_block_kwarg tCOMMA f_kwrest opt_f_block_arg
- {
- result = val[0].concat(val[2]).concat(val[3])
- }
- | f_block_kwarg opt_f_block_arg
- {
- result = val[0].concat(val[1])
- }
- | f_kwrest opt_f_block_arg
- {
- result = val[0].concat(val[1])
- }
- | f_block_arg
- {
- result = [ val[0] ]
- }
-
-opt_block_args_tail:
- tCOMMA block_args_tail
- {
- result = val[1]
- }
- | # nothing
- {
- result = []
- }
-
- block_param: f_arg tCOMMA f_block_optarg tCOMMA f_rest_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg tCOMMA f_block_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[6]).
- concat(val[7])
- }
- | f_arg tCOMMA f_block_optarg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_arg tCOMMA f_block_optarg tCOMMA f_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg tCOMMA f_rest_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_arg tCOMMA
- | f_arg tCOMMA f_rest_arg tCOMMA f_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg opt_block_args_tail
- {
- result = val[0].concat(val[1])
- }
- | f_block_optarg tCOMMA f_rest_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_block_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_block_optarg opt_block_args_tail
- {
- result = val[0].
- concat(val[1])
- }
- | f_block_optarg tCOMMA f_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_rest_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[1])
- }
- | f_rest_arg tCOMMA f_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | block_args_tail
-
- opt_block_param: # nothing
- {
- result = @builder.args(nil, [], nil)
- }
- | block_param_def
- {
- @lexer.state = :expr_value
- }
-
- block_param_def: tPIPE opt_bv_decl tPIPE
- {
- result = @builder.args(val[0], val[1], val[2])
- }
- | tOROP
- {
- result = @builder.args(val[0], [], val[0])
- }
- | tPIPE block_param opt_bv_decl tPIPE
- {
- result = @builder.args(val[0], val[1].concat(val[2]), val[3])
- }
-
- opt_bv_decl: opt_nl
- {
- result = []
- }
- | opt_nl tSEMI bv_decls opt_nl
- {
- result = val[2]
- }
-
- bv_decls: bvar
- {
- result = [ val[0] ]
- }
- | bv_decls tCOMMA bvar
- {
- result = val[0] << val[2]
- }
-
- bvar: tIDENTIFIER
- {
- result = @builder.shadowarg(val[0])
- }
- | f_bad_arg
-
- lambda: {
- @static_env.extend_dynamic
- }
- f_larglist lambda_body
- {
- result = [ val[1], val[2] ]
-
- @static_env.unextend
- }
-
- f_larglist: tLPAREN2 f_args opt_bv_decl tRPAREN
- {
- result = @builder.args(val[0], val[1].concat(val[2]), val[3])
- }
- | f_args
- {
- result = @builder.args(nil, val[0], nil)
- }
-
- lambda_body: tLAMBEG compstmt tRCURLY
- {
- result = [ val[0], val[1], val[2] ]
- }
- | kDO_LAMBDA compstmt kEND
- {
- result = [ val[0], val[1], val[2] ]
- }
-
- do_block: kDO_BLOCK
- {
- @static_env.extend_dynamic
- }
- opt_block_param compstmt kEND
- {
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
- }
-
- block_call: command do_block
- {
- begin_t, block_args, body, end_t = val[1]
- result = @builder.block(val[0],
- begin_t, block_args, body, end_t)
- }
- | block_call dot_or_colon operation2 opt_paren_args
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
- }
- | block_call dot_or_colon operation2 opt_paren_args brace_block
- {
- lparen_t, args, rparen_t = val[3]
- method_call = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
-
- begin_t, args, body, end_t = val[4]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
- | block_call dot_or_colon operation2 command_args do_block
- {
- method_call = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
-
- begin_t, args, body, end_t = val[4]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
-
- method_call: fcall paren_args
- {
- lparen_t, args, rparen_t = val[1]
- result = @builder.call_method(nil, nil, val[0],
- lparen_t, args, rparen_t)
- }
- | primary_value tDOT operation2 opt_paren_args
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
- }
- | primary_value tCOLON2 operation2 paren_args
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
- }
- | primary_value tCOLON2 operation3
- {
- result = @builder.call_method(val[0], val[1], val[2])
- }
- | primary_value tDOT paren_args
- {
- lparen_t, args, rparen_t = val[2]
- result = @builder.call_method(val[0], val[1], nil,
- lparen_t, args, rparen_t)
- }
- | primary_value tCOLON2 paren_args
- {
- lparen_t, args, rparen_t = val[2]
- result = @builder.call_method(val[0], val[1], nil,
- lparen_t, args, rparen_t)
- }
- | kSUPER paren_args
- {
- lparen_t, args, rparen_t = val[1]
- result = @builder.keyword_cmd(:super, val[0],
- lparen_t, args, rparen_t)
- }
- | kSUPER
- {
- result = @builder.keyword_cmd(:zsuper, val[0])
- }
- | primary_value tLBRACK2 opt_call_args rbracket
- {
- result = @builder.index(val[0], val[1], val[2], val[3])
- }
-
- brace_block: tLCURLY
- {
- @static_env.extend_dynamic
- }
- opt_block_param compstmt tRCURLY
- {
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
- }
- | kDO
- {
- @static_env.extend_dynamic
- }
- opt_block_param compstmt kEND
- {
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
- }
-
- case_body: kWHEN args then compstmt cases
- {
- result = [ @builder.when(val[0], val[1], val[2], val[3]),
- *val[4] ]
- }
-
- cases: opt_else
- {
- result = [ val[0] ]
- }
- | case_body
-
- opt_rescue: kRESCUE exc_list exc_var then compstmt opt_rescue
- {
- assoc_t, exc_var = val[2]
-
- if val[1]
- exc_list = @builder.array(nil, val[1], nil)
- end
-
- result = [ @builder.rescue_body(val[0],
- exc_list, assoc_t, exc_var,
- val[3], val[4]),
- *val[5] ]
- }
- |
- {
- result = []
- }
-
- exc_list: arg_value
- {
- result = [ val[0] ]
- }
- | mrhs
- | none
-
- exc_var: tASSOC lhs
- {
- result = [ val[0], val[1] ]
- }
- | none
-
- opt_ensure: kENSURE compstmt
- {
- result = [ val[0], val[1] ]
- }
- | none
-
- literal: numeric
- | symbol
- | dsym
-
- strings: string
- {
- result = @builder.string_compose(nil, val[0], nil)
- }
-
- string: string1
- {
- result = [ val[0] ]
- }
- | string string1
- {
- result = val[0] << val[1]
- }
-
- string1: tSTRING_BEG string_contents tSTRING_END
- {
- result = @builder.string_compose(val[0], val[1], val[2])
- }
- | tSTRING
- {
- result = @builder.string(val[0])
- }
- | tCHARACTER
- {
- result = @builder.character(val[0])
- }
-
- xstring: tXSTRING_BEG xstring_contents tSTRING_END
- {
- result = @builder.xstring_compose(val[0], val[1], val[2])
- }
-
- regexp: tREGEXP_BEG regexp_contents tSTRING_END tREGEXP_OPT
- {
- opts = @builder.regexp_options(val[3])
- result = @builder.regexp_compose(val[0], val[1], val[2], opts)
- }
-
- words: tWORDS_BEG word_list tSTRING_END
- {
- result = @builder.words_compose(val[0], val[1], val[2])
- }
-
- word_list: # nothing
- {
- result = []
- }
- | word_list word tSPACE
- {
- result = val[0] << @builder.word(val[1])
- }
-
- word: string_content
- {
- result = [ val[0] ]
- }
- | word string_content
- {
- result = val[0] << val[1]
- }
-
- symbols: tSYMBOLS_BEG symbol_list tSTRING_END
- {
- result = @builder.symbols_compose(val[0], val[1], val[2])
- }
-
- symbol_list: # nothing
- {
- result = []
- }
- | symbol_list word tSPACE
- {
- result = val[0] << @builder.word(val[1])
- }
-
- qwords: tQWORDS_BEG qword_list tSTRING_END
- {
- result = @builder.words_compose(val[0], val[1], val[2])
- }
-
- qsymbols: tQSYMBOLS_BEG qsym_list tSTRING_END
- {
- result = @builder.symbols_compose(val[0], val[1], val[2])
- }
-
- qword_list: # nothing
- {
- result = []
- }
- | qword_list tSTRING_CONTENT tSPACE
- {
- result = val[0] << @builder.string_internal(val[1])
- }
-
- qsym_list: # nothing
- {
- result = []
- }
- | qsym_list tSTRING_CONTENT tSPACE
- {
- result = val[0] << @builder.symbol_internal(val[1])
- }
-
- string_contents: # nothing
- {
- result = []
- }
- | string_contents string_content
- {
- result = val[0] << val[1]
- }
-
-xstring_contents: # nothing
- {
- result = []
- }
- | xstring_contents string_content
- {
- result = val[0] << val[1]
- }
-
-regexp_contents: # nothing
- {
- result = []
- }
- | regexp_contents string_content
- {
- result = val[0] << val[1]
- }
-
- string_content: tSTRING_CONTENT
- {
- result = @builder.string_internal(val[0])
- }
- | tSTRING_DVAR string_dvar
- {
- result = val[1]
- }
- | tSTRING_DBEG
- {
- @lexer.cond.push(false)
- @lexer.cmdarg.push(false)
- }
- compstmt tSTRING_DEND
- {
- @lexer.cond.lexpop
- @lexer.cmdarg.lexpop
-
- result = @builder.begin(val[0], val[2], val[3])
- }
-
- string_dvar: tGVAR
- {
- result = @builder.gvar(val[0])
- }
- | tIVAR
- {
- result = @builder.ivar(val[0])
- }
- | tCVAR
- {
- result = @builder.cvar(val[0])
- }
- | backref
-
-
- symbol: tSYMBOL
- {
- result = @builder.symbol(val[0])
- }
-
- dsym: tSYMBEG xstring_contents tSTRING_END
- {
- result = @builder.symbol_compose(val[0], val[1], val[2])
- }
-
- numeric: tINTEGER
- {
- result = @builder.integer(val[0])
- }
- | tFLOAT
- {
- result = @builder.float(val[0])
- }
- | tUMINUS_NUM tINTEGER =tLOWEST
- {
- result = @builder.negate(val[0],
- @builder.integer(val[1]))
- }
- | tUMINUS_NUM tFLOAT =tLOWEST
- {
- result = @builder.negate(val[0],
- @builder.float(val[1]))
- }
-
- user_variable: tIDENTIFIER
- {
- result = @builder.ident(val[0])
- }
- | tIVAR
- {
- result = @builder.ivar(val[0])
- }
- | tGVAR
- {
- result = @builder.gvar(val[0])
- }
- | tCONSTANT
- {
- result = @builder.const(val[0])
- }
- | tCVAR
- {
- result = @builder.cvar(val[0])
- }
-
-keyword_variable: kNIL
- {
- result = @builder.nil(val[0])
- }
- | kSELF
- {
- result = @builder.self(val[0])
- }
- | kTRUE
- {
- result = @builder.true(val[0])
- }
- | kFALSE
- {
- result = @builder.false(val[0])
- }
- | k__FILE__
- {
- result = @builder.__FILE__(val[0])
- }
- | k__LINE__
- {
- result = @builder.__LINE__(val[0])
- }
- | k__ENCODING__
- {
- result = @builder.__ENCODING__(val[0])
- }
-
- var_ref: user_variable
- {
- result = @builder.accessible(val[0])
- }
- | keyword_variable
- {
- result = @builder.accessible(val[0])
- }
-
- var_lhs: user_variable
- {
- result = @builder.assignable(val[0])
- }
- | keyword_variable
- {
- result = @builder.assignable(val[0])
- }
-
- backref: tNTH_REF
- {
- result = @builder.nth_ref(val[0])
- }
- | tBACK_REF
- {
- result = @builder.back_ref(val[0])
- }
-
- superclass: term
- {
- result = nil
- }
- | tLT
- {
- @lexer.state = :expr_value
- }
- expr_value term
- {
- result = [ val[0], val[2] ]
- }
- | error term
- {
- yyerrok
- result = nil
- }
-
- f_arglist: tLPAREN2 f_args rparen
- {
- result = @builder.args(val[0], val[1], val[2])
-
- @lexer.state = :expr_value
- }
- | f_args term
- {
- result = @builder.args(nil, val[0], nil)
- }
-
- args_tail: f_kwarg tCOMMA f_kwrest opt_f_block_arg
- {
- result = val[0].concat(val[2]).concat(val[3])
- }
- | f_kwarg opt_f_block_arg
- {
- result = val[0].concat(val[1])
- }
- | f_kwrest opt_f_block_arg
- {
- result = val[0].concat(val[1])
- }
- | f_block_arg
- {
- result = [ val[0] ]
- }
-
- opt_args_tail: tCOMMA args_tail
- {
- result = val[1]
- }
- | # nothing
- {
- result = []
- }
-
- f_args: f_arg tCOMMA f_optarg tCOMMA f_rest_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg tCOMMA f_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[6]).
- concat(val[7])
- }
- | f_arg tCOMMA f_optarg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_arg tCOMMA f_optarg tCOMMA f_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg tCOMMA f_rest_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_arg tCOMMA f_rest_arg tCOMMA f_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg opt_args_tail
- {
- result = val[0].
- concat(val[1])
- }
- | f_optarg tCOMMA f_rest_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_optarg opt_args_tail
- {
- result = val[0].
- concat(val[1])
- }
- | f_optarg tCOMMA f_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_rest_arg opt_args_tail
- {
- result = val[0].
- concat(val[1])
- }
- | f_rest_arg tCOMMA f_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | args_tail
- {
- result = val[0]
- }
- | # nothing
- {
- result = []
- }
-
- f_bad_arg: tCONSTANT
- {
- diagnostic :error, :argument_const, nil, val[0]
- }
- | tIVAR
- {
- diagnostic :error, :argument_ivar, nil, val[0]
- }
- | tGVAR
- {
- diagnostic :error, :argument_gvar, nil, val[0]
- }
- | tCVAR
- {
- diagnostic :error, :argument_cvar, nil, val[0]
- }
-
- f_norm_arg: f_bad_arg
- | tIDENTIFIER
-
- f_arg_item: f_norm_arg
- {
- @static_env.declare val[0][0]
-
- result = @builder.arg(val[0])
- }
- | tLPAREN f_margs rparen
- {
- result = @builder.multi_lhs(val[0], val[1], val[2])
- }
-
- f_arg: f_arg_item
- {
- result = [ val[0] ]
- }
- | f_arg tCOMMA f_arg_item
- {
- result = val[0] << val[2]
- }
-
- f_kw: tLABEL arg_value
- {
- check_kwarg_name(val[0])
-
- @static_env.declare val[0][0]
-
- result = @builder.kwoptarg(val[0], val[1])
- }
-
- f_block_kw: tLABEL primary_value
- {
- check_kwarg_name(val[0])
-
- @static_env.declare val[0][0]
-
- result = @builder.kwoptarg(val[0], val[1])
- }
-
- f_block_kwarg: f_block_kw
- {
- result = [ val[0] ]
- }
- | f_block_kwarg tCOMMA f_block_kw
- {
- result = val[0] << val[2]
- }
-
- f_kwarg: f_kw
- {
- result = [ val[0] ]
- }
- | f_kwarg tCOMMA f_kw
- {
- result = val[0] << val[2]
- }
-
- kwrest_mark: tPOW | tDSTAR
-
- f_kwrest: kwrest_mark tIDENTIFIER
- {
- @static_env.declare val[1][0]
-
- result = [ @builder.kwrestarg(val[0], val[1]) ]
- }
- | kwrest_mark
- {
- result = [ @builder.kwrestarg(val[0]) ]
- }
-
- f_opt: tIDENTIFIER tEQL arg_value
- {
- @static_env.declare val[0][0]
-
- result = @builder.optarg(val[0], val[1], val[2])
- }
-
- f_block_opt: tIDENTIFIER tEQL primary_value
- {
- @static_env.declare val[0][0]
-
- result = @builder.optarg(val[0], val[1], val[2])
- }
-
- f_block_optarg: f_block_opt
- {
- result = [ val[0] ]
- }
- | f_block_optarg tCOMMA f_block_opt
- {
- result = val[0] << val[2]
- }
-
- f_optarg: f_opt
- {
- result = [ val[0] ]
- }
- | f_optarg tCOMMA f_opt
- {
- result = val[0] << val[2]
- }
-
- restarg_mark: tSTAR2 | tSTAR
-
- f_rest_arg: restarg_mark tIDENTIFIER
- {
- @static_env.declare val[1][0]
-
- result = [ @builder.restarg(val[0], val[1]) ]
- }
- | restarg_mark
- {
- result = [ @builder.restarg(val[0]) ]
- }
-
- blkarg_mark: tAMPER2 | tAMPER
-
- f_block_arg: blkarg_mark tIDENTIFIER
- {
- @static_env.declare val[1][0]
-
- result = @builder.blockarg(val[0], val[1])
- }
-
- opt_f_block_arg: tCOMMA f_block_arg
- {
- result = [ val[1] ]
- }
- |
- {
- result = []
- }
-
- singleton: var_ref
- | tLPAREN2 expr rparen
- {
- result = val[1]
- }
-
- assoc_list: # nothing
- {
- result = []
- }
- | assocs trailer
-
- assocs: assoc
- {
- result = [ val[0] ]
- }
- | assocs tCOMMA assoc
- {
- result = val[0] << val[2]
- }
-
- assoc: arg_value tASSOC arg_value
- {
- result = @builder.pair(val[0], val[1], val[2])
- }
- | tLABEL arg_value
- {
- result = @builder.pair_keyword(val[0], val[1])
- }
- | tDSTAR arg_value
- {
- result = @builder.kwsplat(val[0], val[1])
- }
-
- operation: tIDENTIFIER | tCONSTANT | tFID
- operation2: tIDENTIFIER | tCONSTANT | tFID | op
- operation3: tIDENTIFIER | tFID | op
- dot_or_colon: tDOT | tCOLON2
- opt_terms: | terms
- opt_nl: | tNL
- rparen: opt_nl tRPAREN
- {
- result = val[1]
- }
- rbracket: opt_nl tRBRACK
- {
- result = val[1]
- }
- trailer: | tNL | tCOMMA
-
- term: tSEMI
- {
- yyerrok
- }
- | tNL
-
- terms: term
- | terms tSEMI
-
- none: # nothing
- {
- result = nil
- }
-end
-
----- header
-
-require 'parser'
-
-Parser.check_for_encoding_support
-
----- inner
-
- def version
- 20
- end
-
- def default_encoding
- Encoding::UTF_8
- end
diff --git a/test/racc/assets/ruby21.y b/test/racc/assets/ruby21.y
deleted file mode 100644
index 2ac94afb0c..0000000000
--- a/test/racc/assets/ruby21.y
+++ /dev/null
@@ -1,2359 +0,0 @@
-# Copyright (c) 2013 Peter Zotov <whitequark@whitequark.org>
-#
-# Parts of the source are derived from ruby_parser:
-# Copyright (c) Ryan Davis, seattle.rb
-#
-# MIT License
-#
-# 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.
-
-class Parser::Ruby21
-
-token kCLASS kMODULE kDEF kUNDEF kBEGIN kRESCUE kENSURE kEND kIF kUNLESS
- kTHEN kELSIF kELSE kCASE kWHEN kWHILE kUNTIL kFOR kBREAK kNEXT
- kREDO kRETRY kIN kDO kDO_COND kDO_BLOCK kDO_LAMBDA kRETURN kYIELD kSUPER
- kSELF kNIL kTRUE kFALSE kAND kOR kNOT kIF_MOD kUNLESS_MOD kWHILE_MOD
- kUNTIL_MOD kRESCUE_MOD kALIAS kDEFINED klBEGIN klEND k__LINE__
- k__FILE__ k__ENCODING__ tIDENTIFIER tFID tGVAR tIVAR tCONSTANT
- tLABEL tCVAR tNTH_REF tBACK_REF tSTRING_CONTENT tINTEGER tFLOAT
- tREGEXP_END tUPLUS tUMINUS tUMINUS_NUM tPOW tCMP tEQ tEQQ tNEQ
- tGEQ tLEQ tANDOP tOROP tMATCH tNMATCH tDOT tDOT2 tDOT3 tAREF
- tASET tLSHFT tRSHFT tCOLON2 tCOLON3 tOP_ASGN tASSOC tLPAREN
- tLPAREN2 tRPAREN tLPAREN_ARG tLBRACK tLBRACK2 tRBRACK tLBRACE
- tLBRACE_ARG tSTAR tSTAR2 tAMPER tAMPER2 tTILDE tPERCENT tDIVIDE
- tDSTAR tPLUS tMINUS tLT tGT tPIPE tBANG tCARET tLCURLY tRCURLY
- tBACK_REF2 tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG tREGEXP_OPT
- tWORDS_BEG tQWORDS_BEG tSYMBOLS_BEG tQSYMBOLS_BEG tSTRING_DBEG
- tSTRING_DVAR tSTRING_END tSTRING_DEND tSTRING tSYMBOL
- tNL tEH tCOLON tCOMMA tSPACE tSEMI tLAMBDA tLAMBEG tCHARACTER
- tRATIONAL tIMAGINARY
-
-prechigh
- right tBANG tTILDE tUPLUS
- right tPOW
- right tUMINUS_NUM tUMINUS
- left tSTAR2 tDIVIDE tPERCENT
- left tPLUS tMINUS
- left tLSHFT tRSHFT
- left tAMPER2
- left tPIPE tCARET
- left tGT tGEQ tLT tLEQ
- nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH
- left tANDOP
- left tOROP
- nonassoc tDOT2 tDOT3
- right tEH tCOLON
- left kRESCUE_MOD
- right tEQL tOP_ASGN
- nonassoc kDEFINED
- right kNOT
- left kOR kAND
- nonassoc kIF_MOD kUNLESS_MOD kWHILE_MOD kUNTIL_MOD
- nonassoc tLBRACE_ARG
- nonassoc tLOWEST
-preclow
-
-rule
-
- program: top_compstmt
-
- top_compstmt: top_stmts opt_terms
- {
- result = @builder.compstmt(val[0])
- }
-
- top_stmts: # nothing
- {
- result = []
- }
- | top_stmt
- {
- result = [ val[0] ]
- }
- | top_stmts terms top_stmt
- {
- result = val[0] << val[2]
- }
- | error top_stmt
- {
- result = [ val[1] ]
- }
-
- top_stmt: stmt
- | klBEGIN tLCURLY top_compstmt tRCURLY
- {
- result = @builder.preexe(val[0], val[1], val[2], val[3])
- }
-
- bodystmt: compstmt opt_rescue opt_else opt_ensure
- {
- rescue_bodies = val[1]
- else_t, else_ = val[2]
- ensure_t, ensure_ = val[3]
-
- if rescue_bodies.empty? && !else_.nil?
- diagnostic :warning, :useless_else, nil, else_t
- end
-
- result = @builder.begin_body(val[0],
- rescue_bodies,
- else_t, else_,
- ensure_t, ensure_)
- }
-
- compstmt: stmts opt_terms
- {
- result = @builder.compstmt(val[0])
- }
-
- stmts: # nothing
- {
- result = []
- }
- | stmt_or_begin
- {
- result = [ val[0] ]
- }
- | stmts terms stmt_or_begin
- {
- result = val[0] << val[2]
- }
- | error stmt
- {
- result = [ val[1] ]
- }
-
- stmt_or_begin: stmt
- | klBEGIN tLCURLY top_compstmt tRCURLY
- {
- diagnostic :error, :begin_in_method, nil, val[0]
- }
-
- stmt: kALIAS fitem
- {
- @lexer.state = :expr_fname
- }
- fitem
- {
- result = @builder.alias(val[0], val[1], val[3])
- }
- | kALIAS tGVAR tGVAR
- {
- result = @builder.alias(val[0],
- @builder.gvar(val[1]),
- @builder.gvar(val[2]))
- }
- | kALIAS tGVAR tBACK_REF
- {
- result = @builder.alias(val[0],
- @builder.gvar(val[1]),
- @builder.back_ref(val[2]))
- }
- | kALIAS tGVAR tNTH_REF
- {
- diagnostic :error, :nth_ref_alias, nil, val[2]
- }
- | kUNDEF undef_list
- {
- result = @builder.undef_method(val[0], val[1])
- }
- | stmt kIF_MOD expr_value
- {
- result = @builder.condition_mod(val[0], nil,
- val[1], val[2])
- }
- | stmt kUNLESS_MOD expr_value
- {
- result = @builder.condition_mod(nil, val[0],
- val[1], val[2])
- }
- | stmt kWHILE_MOD expr_value
- {
- result = @builder.loop_mod(:while, val[0], val[1], val[2])
- }
- | stmt kUNTIL_MOD expr_value
- {
- result = @builder.loop_mod(:until, val[0], val[1], val[2])
- }
- | stmt kRESCUE_MOD stmt
- {
- rescue_body = @builder.rescue_body(val[1],
- nil, nil, nil,
- nil, val[2])
-
- result = @builder.begin_body(val[0], [ rescue_body ])
- }
- | klEND tLCURLY compstmt tRCURLY
- {
- result = @builder.postexe(val[0], val[1], val[2], val[3])
- }
- | command_asgn
- | mlhs tEQL command_call
- {
- result = @builder.multi_assign(val[0], val[1], val[2])
- }
- | var_lhs tOP_ASGN command_call
- {
- result = @builder.op_assign(val[0], val[1], val[2])
- }
- | primary_value tLBRACK2 opt_call_args rbracket tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.index(
- val[0], val[1], val[2], val[3]),
- val[4], val[5])
- }
- | primary_value tDOT tIDENTIFIER tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tDOT tCONSTANT tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | backref tOP_ASGN command_call
- {
- @builder.op_assign(val[0], val[1], val[2])
- }
- | lhs tEQL mrhs
- {
- result = @builder.assign(val[0], val[1],
- @builder.array(nil, val[2], nil))
- }
- | mlhs tEQL mrhs_arg
- {
- result = @builder.multi_assign(val[0], val[1], val[2])
- }
- | expr
-
- command_asgn: lhs tEQL command_call
- {
- result = @builder.assign(val[0], val[1], val[2])
- }
- | lhs tEQL command_asgn
- {
- result = @builder.assign(val[0], val[1], val[2])
- }
-
- expr: command_call
- | expr kAND expr
- {
- result = @builder.logical_op(:and, val[0], val[1], val[2])
- }
- | expr kOR expr
- {
- result = @builder.logical_op(:or, val[0], val[1], val[2])
- }
- | kNOT opt_nl expr
- {
- result = @builder.not_op(val[0], nil, val[2], nil)
- }
- | tBANG command_call
- {
- result = @builder.not_op(val[0], nil, val[1], nil)
- }
- | arg
-
- expr_value: expr
-
- command_call: command
- | block_command
-
- block_command: block_call
- | block_call dot_or_colon operation2 command_args
- {
- result = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
- }
-
- cmd_brace_block: tLBRACE_ARG
- {
- @static_env.extend_dynamic
- }
- opt_block_param compstmt tRCURLY
- {
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
- }
-
- fcall: operation
-
- command: fcall command_args =tLOWEST
- {
- result = @builder.call_method(nil, nil, val[0],
- nil, val[1], nil)
- }
- | fcall command_args cmd_brace_block
- {
- method_call = @builder.call_method(nil, nil, val[0],
- nil, val[1], nil)
-
- begin_t, args, body, end_t = val[2]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
- | primary_value tDOT operation2 command_args =tLOWEST
- {
- result = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
- }
- | primary_value tDOT operation2 command_args cmd_brace_block
- {
- method_call = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
-
- begin_t, args, body, end_t = val[4]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
- | primary_value tCOLON2 operation2 command_args =tLOWEST
- {
- result = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
- }
- | primary_value tCOLON2 operation2 command_args cmd_brace_block
- {
- method_call = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
-
- begin_t, args, body, end_t = val[4]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
- | kSUPER command_args
- {
- result = @builder.keyword_cmd(:super, val[0],
- nil, val[1], nil)
- }
- | kYIELD command_args
- {
- result = @builder.keyword_cmd(:yield, val[0],
- nil, val[1], nil)
- }
- | kRETURN call_args
- {
- result = @builder.keyword_cmd(:return, val[0],
- nil, val[1], nil)
- }
- | kBREAK call_args
- {
- result = @builder.keyword_cmd(:break, val[0],
- nil, val[1], nil)
- }
- | kNEXT call_args
- {
- result = @builder.keyword_cmd(:next, val[0],
- nil, val[1], nil)
- }
-
- mlhs: mlhs_basic
- {
- result = @builder.multi_lhs(nil, val[0], nil)
- }
- | tLPAREN mlhs_inner rparen
- {
- result = @builder.begin(val[0], val[1], val[2])
- }
-
- mlhs_inner: mlhs_basic
- {
- result = @builder.multi_lhs(nil, val[0], nil)
- }
- | tLPAREN mlhs_inner rparen
- {
- result = @builder.multi_lhs(val[0], val[1], val[2])
- }
-
- mlhs_basic: mlhs_head
- | mlhs_head mlhs_item
- {
- result = val[0].
- push(val[1])
- }
- | mlhs_head tSTAR mlhs_node
- {
- result = val[0].
- push(@builder.splat(val[1], val[2]))
- }
- | mlhs_head tSTAR mlhs_node tCOMMA mlhs_post
- {
- result = val[0].
- push(@builder.splat(val[1], val[2])).
- concat(val[4])
- }
- | mlhs_head tSTAR
- {
- result = val[0].
- push(@builder.splat(val[1]))
- }
- | mlhs_head tSTAR tCOMMA mlhs_post
- {
- result = val[0].
- push(@builder.splat(val[1])).
- concat(val[3])
- }
- | tSTAR mlhs_node
- {
- result = [ @builder.splat(val[0], val[1]) ]
- }
- | tSTAR mlhs_node tCOMMA mlhs_post
- {
- result = [ @builder.splat(val[0], val[1]),
- *val[3] ]
- }
- | tSTAR
- {
- result = [ @builder.splat(val[0]) ]
- }
- | tSTAR tCOMMA mlhs_post
- {
- result = [ @builder.splat(val[0]),
- *val[2] ]
- }
-
- mlhs_item: mlhs_node
- | tLPAREN mlhs_inner rparen
- {
- result = @builder.begin(val[0], val[1], val[2])
- }
-
- mlhs_head: mlhs_item tCOMMA
- {
- result = [ val[0] ]
- }
- | mlhs_head mlhs_item tCOMMA
- {
- result = val[0] << val[1]
- }
-
- mlhs_post: mlhs_item
- {
- result = [ val[0] ]
- }
- | mlhs_post tCOMMA mlhs_item
- {
- result = val[0] << val[2]
- }
-
- mlhs_node: user_variable
- {
- result = @builder.assignable(val[0])
- }
- | keyword_variable
- {
- result = @builder.assignable(val[0])
- }
- | primary_value tLBRACK2 opt_call_args rbracket
- {
- result = @builder.index_asgn(val[0], val[1], val[2], val[3])
- }
- | primary_value tDOT tIDENTIFIER
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tIDENTIFIER
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tDOT tCONSTANT
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tCONSTANT
- {
- result = @builder.assignable(
- @builder.const_fetch(val[0], val[1], val[2]))
- }
- | tCOLON3 tCONSTANT
- {
- result = @builder.assignable(
- @builder.const_global(val[0], val[1]))
- }
- | backref
- {
- result = @builder.assignable(val[0])
- }
-
- lhs: user_variable
- {
- result = @builder.assignable(val[0])
- }
- | keyword_variable
- {
- result = @builder.assignable(val[0])
- }
- | primary_value tLBRACK2 opt_call_args rbracket
- {
- result = @builder.index_asgn(val[0], val[1], val[2], val[3])
- }
- | primary_value tDOT tIDENTIFIER
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tIDENTIFIER
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tDOT tCONSTANT
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tCONSTANT
- {
- result = @builder.assignable(
- @builder.const_fetch(val[0], val[1], val[2]))
- }
- | tCOLON3 tCONSTANT
- {
- result = @builder.assignable(
- @builder.const_global(val[0], val[1]))
- }
- | backref
- {
- result = @builder.assignable(val[0])
- }
-
- cname: tIDENTIFIER
- {
- diagnostic :error, :module_name_const, nil, val[0]
- }
- | tCONSTANT
-
- cpath: tCOLON3 cname
- {
- result = @builder.const_global(val[0], val[1])
- }
- | cname
- {
- result = @builder.const(val[0])
- }
- | primary_value tCOLON2 cname
- {
- result = @builder.const_fetch(val[0], val[1], val[2])
- }
-
- fname: tIDENTIFIER | tCONSTANT | tFID
- | op
- | reswords
-
- fsym: fname
- {
- result = @builder.symbol(val[0])
- }
- | symbol
-
- fitem: fsym
- | dsym
-
- undef_list: fitem
- {
- result = [ val[0] ]
- }
- | undef_list tCOMMA
- {
- @lexer.state = :expr_fname
- }
- fitem
- {
- result = val[0] << val[3]
- }
-
- op: tPIPE | tCARET | tAMPER2 | tCMP | tEQ | tEQQ
- | tMATCH | tNMATCH | tGT | tGEQ | tLT | tLEQ
- | tNEQ | tLSHFT | tRSHFT | tPLUS | tMINUS | tSTAR2
- | tSTAR | tDIVIDE | tPERCENT | tPOW | tBANG | tTILDE
- | tUPLUS | tUMINUS | tAREF | tASET | tDSTAR | tBACK_REF2
-
- reswords: k__LINE__ | k__FILE__ | k__ENCODING__ | klBEGIN | klEND
- | kALIAS | kAND | kBEGIN | kBREAK | kCASE
- | kCLASS | kDEF | kDEFINED | kDO | kELSE
- | kELSIF | kEND | kENSURE | kFALSE | kFOR
- | kIN | kMODULE | kNEXT | kNIL | kNOT
- | kOR | kREDO | kRESCUE | kRETRY | kRETURN
- | kSELF | kSUPER | kTHEN | kTRUE | kUNDEF
- | kWHEN | kYIELD | kIF | kUNLESS | kWHILE
- | kUNTIL
-
- arg: lhs tEQL arg
- {
- result = @builder.assign(val[0], val[1], val[2])
- }
- | lhs tEQL arg kRESCUE_MOD arg
- {
- rescue_body = @builder.rescue_body(val[3],
- nil, nil, nil,
- nil, val[4])
-
- rescue_ = @builder.begin_body(val[2], [ rescue_body ])
-
- result = @builder.assign(val[0], val[1], rescue_)
- }
- | var_lhs tOP_ASGN arg
- {
- result = @builder.op_assign(val[0], val[1], val[2])
- }
- | var_lhs tOP_ASGN arg kRESCUE_MOD arg
- {
- rescue_body = @builder.rescue_body(val[3],
- nil, nil, nil,
- nil, val[4])
-
- rescue_ = @builder.begin_body(val[2], [ rescue_body ])
-
- result = @builder.op_assign(val[0], val[1], rescue_)
- }
- | primary_value tLBRACK2 opt_call_args rbracket tOP_ASGN arg
- {
- result = @builder.op_assign(
- @builder.index(
- val[0], val[1], val[2], val[3]),
- val[4], val[5])
- }
- | primary_value tDOT tIDENTIFIER tOP_ASGN arg
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tDOT tCONSTANT tOP_ASGN arg
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tCOLON2 tCONSTANT tOP_ASGN arg
- {
- const = @builder.const_op_assignable(
- @builder.const_fetch(val[0], val[1], val[2]))
- result = @builder.op_assign(const, val[3], val[4])
- }
- | tCOLON3 tCONSTANT tOP_ASGN arg
- {
- const = @builder.const_op_assignable(
- @builder.const_global(val[0], val[1]))
- result = @builder.op_assign(const, val[2], val[3])
- }
- | backref tOP_ASGN arg
- {
- result = @builder.op_assign(val[0], val[1], val[2])
- }
- | arg tDOT2 arg
- {
- result = @builder.range_inclusive(val[0], val[1], val[2])
- }
- | arg tDOT3 arg
- {
- result = @builder.range_exclusive(val[0], val[1], val[2])
- }
- | arg tPLUS arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tMINUS arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tSTAR2 arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tDIVIDE arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tPERCENT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tPOW arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | tUMINUS_NUM simple_numeric tPOW arg
- {
- result = @builder.unary_op(val[0],
- @builder.binary_op(
- val[1], val[2], val[3]))
- }
- | tUPLUS arg
- {
- result = @builder.unary_op(val[0], val[1])
- }
- | tUMINUS arg
- {
- result = @builder.unary_op(val[0], val[1])
- }
- | arg tPIPE arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tCARET arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tAMPER2 arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tCMP arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tGT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tGEQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tLT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tLEQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tEQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tEQQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tNEQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tMATCH arg
- {
- result = @builder.match_op(val[0], val[1], val[2])
- }
- | arg tNMATCH arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | tBANG arg
- {
- result = @builder.not_op(val[0], nil, val[1], nil)
- }
- | tTILDE arg
- {
- result = @builder.unary_op(val[0], val[1])
- }
- | arg tLSHFT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tRSHFT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tANDOP arg
- {
- result = @builder.logical_op(:and, val[0], val[1], val[2])
- }
- | arg tOROP arg
- {
- result = @builder.logical_op(:or, val[0], val[1], val[2])
- }
- | kDEFINED opt_nl arg
- {
- result = @builder.keyword_cmd(:defined?, val[0], nil, [ val[2] ], nil)
- }
-
- | arg tEH arg opt_nl tCOLON arg
- {
- result = @builder.ternary(val[0], val[1],
- val[2], val[4], val[5])
- }
- | primary
-
- arg_value: arg
-
- aref_args: none
- | args trailer
- | args tCOMMA assocs trailer
- {
- result = val[0] << @builder.associate(nil, val[2], nil)
- }
- | assocs trailer
- {
- result = [ @builder.associate(nil, val[0], nil) ]
- }
-
- paren_args: tLPAREN2 opt_call_args rparen
- {
- result = val
- }
-
- opt_paren_args: # nothing
- {
- result = [ nil, [], nil ]
- }
- | paren_args
-
- opt_call_args: # nothing
- {
- result = []
- }
- | call_args
- | args tCOMMA
- | args tCOMMA assocs tCOMMA
- {
- result = val[0] << @builder.associate(nil, val[2], nil)
- }
- | assocs tCOMMA
- {
- result = [ @builder.associate(nil, val[0], nil) ]
- }
-
- call_args: command
- {
- result = [ val[0] ]
- }
- | args opt_block_arg
- {
- result = val[0].concat(val[1])
- }
- | assocs opt_block_arg
- {
- result = [ @builder.associate(nil, val[0], nil) ]
- result.concat(val[1])
- }
- | args tCOMMA assocs opt_block_arg
- {
- assocs = @builder.associate(nil, val[2], nil)
- result = val[0] << assocs
- result.concat(val[3])
- }
- | block_arg
- {
- result = [ val[0] ]
- }
-
- command_args: {
- result = @lexer.cmdarg.dup
- @lexer.cmdarg.push(true)
- }
- call_args
- {
- @lexer.cmdarg = val[0]
-
- result = val[1]
- }
-
- block_arg: tAMPER arg_value
- {
- result = @builder.block_pass(val[0], val[1])
- }
-
- opt_block_arg: tCOMMA block_arg
- {
- result = [ val[1] ]
- }
- | # nothing
- {
- result = []
- }
-
- args: arg_value
- {
- result = [ val[0] ]
- }
- | tSTAR arg_value
- {
- result = [ @builder.splat(val[0], val[1]) ]
- }
- | args tCOMMA arg_value
- {
- result = val[0] << val[2]
- }
- | args tCOMMA tSTAR arg_value
- {
- result = val[0] << @builder.splat(val[2], val[3])
- }
-
- mrhs_arg: mrhs
- {
- result = @builder.array(nil, val[0], nil)
- }
- | arg_value
-
- mrhs: args tCOMMA arg_value
- {
- result = val[0] << val[2]
- }
- | args tCOMMA tSTAR arg_value
- {
- result = val[0] << @builder.splat(val[2], val[3])
- }
- | tSTAR arg_value
- {
- result = [ @builder.splat(val[0], val[1]) ]
- }
-
- primary: literal
- | strings
- | xstring
- | regexp
- | words
- | qwords
- | symbols
- | qsymbols
- | var_ref
- | backref
- | tFID
- {
- result = @builder.call_method(nil, nil, val[0])
- }
- | kBEGIN
- {
- result = @lexer.cmdarg.dup
- @lexer.cmdarg.clear
- }
- bodystmt kEND
- {
- @lexer.cmdarg = val[1]
-
- result = @builder.begin_keyword(val[0], val[2], val[3])
- }
- | tLPAREN_ARG
- {
- result = @lexer.cmdarg.dup
- @lexer.cmdarg.clear
- }
- expr
- {
- @lexer.state = :expr_endarg
- }
- opt_nl tRPAREN
- {
- @lexer.cmdarg = val[1]
-
- result = @builder.begin(val[0], val[2], val[5])
- }
- | tLPAREN_ARG
- {
- @lexer.state = :expr_endarg
- }
- opt_nl tRPAREN
- {
- result = @builder.begin(val[0], nil, val[3])
- }
- | tLPAREN compstmt tRPAREN
- {
- result = @builder.begin(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tCONSTANT
- {
- result = @builder.const_fetch(val[0], val[1], val[2])
- }
- | tCOLON3 tCONSTANT
- {
- result = @builder.const_global(val[0], val[1])
- }
- | tLBRACK aref_args tRBRACK
- {
- result = @builder.array(val[0], val[1], val[2])
- }
- | tLBRACE assoc_list tRCURLY
- {
- result = @builder.associate(val[0], val[1], val[2])
- }
- | kRETURN
- {
- result = @builder.keyword_cmd(:return, val[0])
- }
- | kYIELD tLPAREN2 call_args rparen
- {
- result = @builder.keyword_cmd(:yield, val[0], val[1], val[2], val[3])
- }
- | kYIELD tLPAREN2 rparen
- {
- result = @builder.keyword_cmd(:yield, val[0], val[1], [], val[2])
- }
- | kYIELD
- {
- result = @builder.keyword_cmd(:yield, val[0])
- }
- | kDEFINED opt_nl tLPAREN2 expr rparen
- {
- result = @builder.keyword_cmd(:defined?, val[0],
- val[2], [ val[3] ], val[4])
- }
- | kNOT tLPAREN2 expr rparen
- {
- result = @builder.not_op(val[0], val[1], val[2], val[3])
- }
- | kNOT tLPAREN2 rparen
- {
- result = @builder.not_op(val[0], val[1], nil, val[2])
- }
- | fcall brace_block
- {
- method_call = @builder.call_method(nil, nil, val[0])
-
- begin_t, args, body, end_t = val[1]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
- | method_call
- | method_call brace_block
- {
- begin_t, args, body, end_t = val[1]
- result = @builder.block(val[0],
- begin_t, args, body, end_t)
- }
- | tLAMBDA lambda
- {
- lambda_call = @builder.call_lambda(val[0])
-
- args, (begin_t, body, end_t) = val[1]
- result = @builder.block(lambda_call,
- begin_t, args, body, end_t)
- }
- | kIF expr_value then compstmt if_tail kEND
- {
- else_t, else_ = val[4]
- result = @builder.condition(val[0], val[1], val[2],
- val[3], else_t,
- else_, val[5])
- }
- | kUNLESS expr_value then compstmt opt_else kEND
- {
- else_t, else_ = val[4]
- result = @builder.condition(val[0], val[1], val[2],
- else_, else_t,
- val[3], val[5])
- }
- | kWHILE
- {
- @lexer.cond.push(true)
- }
- expr_value do
- {
- @lexer.cond.pop
- }
- compstmt kEND
- {
- result = @builder.loop(:while, val[0], val[2], val[3],
- val[5], val[6])
- }
- | kUNTIL
- {
- @lexer.cond.push(true)
- }
- expr_value do
- {
- @lexer.cond.pop
- }
- compstmt kEND
- {
- result = @builder.loop(:until, val[0], val[2], val[3],
- val[5], val[6])
- }
- | kCASE expr_value opt_terms case_body kEND
- {
- *when_bodies, (else_t, else_body) = *val[3]
-
- result = @builder.case(val[0], val[1],
- when_bodies, else_t, else_body,
- val[4])
- }
- | kCASE opt_terms case_body kEND
- {
- *when_bodies, (else_t, else_body) = *val[2]
-
- result = @builder.case(val[0], nil,
- when_bodies, else_t, else_body,
- val[3])
- }
- | kFOR for_var kIN
- {
- @lexer.cond.push(true)
- }
- expr_value do
- {
- @lexer.cond.pop
- }
- compstmt kEND
- {
- result = @builder.for(val[0], val[1],
- val[2], val[4],
- val[5], val[7], val[8])
- }
- | kCLASS cpath superclass
- {
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- bodystmt kEND
- {
- if in_def?
- diagnostic :error, :class_in_def, nil, val[0]
- end
-
- lt_t, superclass = val[2]
- result = @builder.def_class(val[0], val[1],
- lt_t, superclass,
- val[4], val[5])
-
- @lexer.pop_cmdarg
- @static_env.unextend
- }
- | kCLASS tLSHFT expr term
- {
- result = @def_level
- @def_level = 0
-
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- bodystmt kEND
- {
- result = @builder.def_sclass(val[0], val[1], val[2],
- val[5], val[6])
-
- @lexer.pop_cmdarg
- @static_env.unextend
-
- @def_level = val[4]
- }
- | kMODULE cpath
- {
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- bodystmt kEND
- {
- if in_def?
- diagnostic :error, :module_in_def, nil, val[0]
- end
-
- result = @builder.def_module(val[0], val[1],
- val[3], val[4])
-
- @lexer.pop_cmdarg
- @static_env.unextend
- }
- | kDEF fname
- {
- @def_level += 1
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- f_arglist bodystmt kEND
- {
- result = @builder.def_method(val[0], val[1],
- val[3], val[4], val[5])
-
- @lexer.pop_cmdarg
- @static_env.unextend
- @def_level -= 1
- }
- | kDEF singleton dot_or_colon
- {
- @lexer.state = :expr_fname
- }
- fname
- {
- @def_level += 1
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- f_arglist bodystmt kEND
- {
- result = @builder.def_singleton(val[0], val[1], val[2],
- val[4], val[6], val[7], val[8])
-
- @lexer.pop_cmdarg
- @static_env.unextend
- @def_level -= 1
- }
- | kBREAK
- {
- result = @builder.keyword_cmd(:break, val[0])
- }
- | kNEXT
- {
- result = @builder.keyword_cmd(:next, val[0])
- }
- | kREDO
- {
- result = @builder.keyword_cmd(:redo, val[0])
- }
- | kRETRY
- {
- result = @builder.keyword_cmd(:retry, val[0])
- }
-
- primary_value: primary
-
- then: term
- | kTHEN
- | term kTHEN
- {
- result = val[1]
- }
-
- do: term
- | kDO_COND
-
- if_tail: opt_else
- | kELSIF expr_value then compstmt if_tail
- {
- else_t, else_ = val[4]
- result = [ val[0],
- @builder.condition(val[0], val[1], val[2],
- val[3], else_t,
- else_, nil),
- ]
- }
-
- opt_else: none
- | kELSE compstmt
- {
- result = val
- }
-
- for_var: lhs
- | mlhs
-
- f_marg: f_norm_arg
- {
- result = @builder.arg(val[0])
- }
- | tLPAREN f_margs rparen
- {
- result = @builder.multi_lhs(val[0], val[1], val[2])
- }
-
- f_marg_list: f_marg
- {
- result = [ val[0] ]
- }
- | f_marg_list tCOMMA f_marg
- {
- result = val[0] << val[2]
- }
-
- f_margs: f_marg_list
- | f_marg_list tCOMMA tSTAR f_norm_arg
- {
- result = val[0].
- push(@builder.restarg(val[2], val[3]))
- }
- | f_marg_list tCOMMA tSTAR f_norm_arg tCOMMA f_marg_list
- {
- result = val[0].
- push(@builder.restarg(val[2], val[3])).
- concat(val[5])
- }
- | f_marg_list tCOMMA tSTAR
- {
- result = val[0].
- push(@builder.restarg(val[2]))
- }
- | f_marg_list tCOMMA tSTAR tCOMMA f_marg_list
- {
- result = val[0].
- push(@builder.restarg(val[2])).
- concat(val[4])
- }
- | tSTAR f_norm_arg
- {
- result = [ @builder.restarg(val[0], val[1]) ]
- }
- | tSTAR f_norm_arg tCOMMA f_marg_list
- {
- result = [ @builder.restarg(val[0], val[1]),
- *val[3] ]
- }
- | tSTAR
- {
- result = [ @builder.restarg(val[0]) ]
- }
- | tSTAR tCOMMA f_marg_list
- {
- result = [ @builder.restarg(val[0]),
- *val[2] ]
- }
-
- block_args_tail: f_block_kwarg tCOMMA f_kwrest opt_f_block_arg
- {
- result = val[0].concat(val[2]).concat(val[3])
- }
- | f_block_kwarg opt_f_block_arg
- {
- result = val[0].concat(val[1])
- }
- | f_kwrest opt_f_block_arg
- {
- result = val[0].concat(val[1])
- }
- | f_block_arg
- {
- result = [ val[0] ]
- }
-
-opt_block_args_tail:
- tCOMMA block_args_tail
- {
- result = val[1]
- }
- | # nothing
- {
- result = []
- }
-
- block_param: f_arg tCOMMA f_block_optarg tCOMMA f_rest_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg tCOMMA f_block_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[6]).
- concat(val[7])
- }
- | f_arg tCOMMA f_block_optarg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_arg tCOMMA f_block_optarg tCOMMA f_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg tCOMMA f_rest_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_arg tCOMMA
- | f_arg tCOMMA f_rest_arg tCOMMA f_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg opt_block_args_tail
- {
- result = val[0].concat(val[1])
- }
- | f_block_optarg tCOMMA f_rest_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_block_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_block_optarg opt_block_args_tail
- {
- result = val[0].
- concat(val[1])
- }
- | f_block_optarg tCOMMA f_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_rest_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[1])
- }
- | f_rest_arg tCOMMA f_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | block_args_tail
-
- opt_block_param: # nothing
- {
- result = @builder.args(nil, [], nil)
- }
- | block_param_def
- {
- @lexer.state = :expr_value
- }
-
- block_param_def: tPIPE opt_bv_decl tPIPE
- {
- result = @builder.args(val[0], val[1], val[2])
- }
- | tOROP
- {
- result = @builder.args(val[0], [], val[0])
- }
- | tPIPE block_param opt_bv_decl tPIPE
- {
- result = @builder.args(val[0], val[1].concat(val[2]), val[3])
- }
-
- opt_bv_decl: opt_nl
- {
- result = []
- }
- | opt_nl tSEMI bv_decls opt_nl
- {
- result = val[2]
- }
-
- bv_decls: bvar
- {
- result = [ val[0] ]
- }
- | bv_decls tCOMMA bvar
- {
- result = val[0] << val[2]
- }
-
- bvar: tIDENTIFIER
- {
- result = @builder.shadowarg(val[0])
- }
- | f_bad_arg
-
- lambda: {
- @static_env.extend_dynamic
- }
- f_larglist
- {
- result = @lexer.cmdarg.dup
- @lexer.cmdarg.clear
- }
- lambda_body
- {
- @lexer.cmdarg = val[2]
- @lexer.cmdarg.lexpop
-
- result = [ val[1], val[3] ]
-
- @static_env.unextend
- }
-
- f_larglist: tLPAREN2 f_args opt_bv_decl tRPAREN
- {
- result = @builder.args(val[0], val[1].concat(val[2]), val[3])
- }
- | f_args
- {
- result = @builder.args(nil, val[0], nil)
- }
-
- lambda_body: tLAMBEG compstmt tRCURLY
- {
- result = [ val[0], val[1], val[2] ]
- }
- | kDO_LAMBDA compstmt kEND
- {
- result = [ val[0], val[1], val[2] ]
- }
-
- do_block: kDO_BLOCK
- {
- @static_env.extend_dynamic
- }
- opt_block_param compstmt kEND
- {
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
- }
-
- block_call: command do_block
- {
- begin_t, block_args, body, end_t = val[1]
- result = @builder.block(val[0],
- begin_t, block_args, body, end_t)
- }
- | block_call dot_or_colon operation2 opt_paren_args
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
- }
- | block_call dot_or_colon operation2 opt_paren_args brace_block
- {
- lparen_t, args, rparen_t = val[3]
- method_call = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
-
- begin_t, args, body, end_t = val[4]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
- | block_call dot_or_colon operation2 command_args do_block
- {
- method_call = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
-
- begin_t, args, body, end_t = val[4]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
-
- method_call: fcall paren_args
- {
- lparen_t, args, rparen_t = val[1]
- result = @builder.call_method(nil, nil, val[0],
- lparen_t, args, rparen_t)
- }
- | primary_value tDOT operation2 opt_paren_args
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
- }
- | primary_value tCOLON2 operation2 paren_args
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
- }
- | primary_value tCOLON2 operation3
- {
- result = @builder.call_method(val[0], val[1], val[2])
- }
- | primary_value tDOT paren_args
- {
- lparen_t, args, rparen_t = val[2]
- result = @builder.call_method(val[0], val[1], nil,
- lparen_t, args, rparen_t)
- }
- | primary_value tCOLON2 paren_args
- {
- lparen_t, args, rparen_t = val[2]
- result = @builder.call_method(val[0], val[1], nil,
- lparen_t, args, rparen_t)
- }
- | kSUPER paren_args
- {
- lparen_t, args, rparen_t = val[1]
- result = @builder.keyword_cmd(:super, val[0],
- lparen_t, args, rparen_t)
- }
- | kSUPER
- {
- result = @builder.keyword_cmd(:zsuper, val[0])
- }
- | primary_value tLBRACK2 opt_call_args rbracket
- {
- result = @builder.index(val[0], val[1], val[2], val[3])
- }
-
- brace_block: tLCURLY
- {
- @static_env.extend_dynamic
- }
- opt_block_param compstmt tRCURLY
- {
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
- }
- | kDO
- {
- @static_env.extend_dynamic
- }
- opt_block_param compstmt kEND
- {
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
- }
-
- case_body: kWHEN args then compstmt cases
- {
- result = [ @builder.when(val[0], val[1], val[2], val[3]),
- *val[4] ]
- }
-
- cases: opt_else
- {
- result = [ val[0] ]
- }
- | case_body
-
- opt_rescue: kRESCUE exc_list exc_var then compstmt opt_rescue
- {
- assoc_t, exc_var = val[2]
-
- if val[1]
- exc_list = @builder.array(nil, val[1], nil)
- end
-
- result = [ @builder.rescue_body(val[0],
- exc_list, assoc_t, exc_var,
- val[3], val[4]),
- *val[5] ]
- }
- |
- {
- result = []
- }
-
- exc_list: arg_value
- {
- result = [ val[0] ]
- }
- | mrhs
- | none
-
- exc_var: tASSOC lhs
- {
- result = [ val[0], val[1] ]
- }
- | none
-
- opt_ensure: kENSURE compstmt
- {
- result = [ val[0], val[1] ]
- }
- | none
-
- literal: numeric
- | symbol
- | dsym
-
- strings: string
- {
- result = @builder.string_compose(nil, val[0], nil)
- }
-
- string: string1
- {
- result = [ val[0] ]
- }
- | string string1
- {
- result = val[0] << val[1]
- }
-
- string1: tSTRING_BEG string_contents tSTRING_END
- {
- result = @builder.string_compose(val[0], val[1], val[2])
- }
- | tSTRING
- {
- result = @builder.string(val[0])
- }
- | tCHARACTER
- {
- result = @builder.character(val[0])
- }
-
- xstring: tXSTRING_BEG xstring_contents tSTRING_END
- {
- result = @builder.xstring_compose(val[0], val[1], val[2])
- }
-
- regexp: tREGEXP_BEG regexp_contents tSTRING_END tREGEXP_OPT
- {
- opts = @builder.regexp_options(val[3])
- result = @builder.regexp_compose(val[0], val[1], val[2], opts)
- }
-
- words: tWORDS_BEG word_list tSTRING_END
- {
- result = @builder.words_compose(val[0], val[1], val[2])
- }
-
- word_list: # nothing
- {
- result = []
- }
- | word_list word tSPACE
- {
- result = val[0] << @builder.word(val[1])
- }
-
- word: string_content
- {
- result = [ val[0] ]
- }
- | word string_content
- {
- result = val[0] << val[1]
- }
-
- symbols: tSYMBOLS_BEG symbol_list tSTRING_END
- {
- result = @builder.symbols_compose(val[0], val[1], val[2])
- }
-
- symbol_list: # nothing
- {
- result = []
- }
- | symbol_list word tSPACE
- {
- result = val[0] << @builder.word(val[1])
- }
-
- qwords: tQWORDS_BEG qword_list tSTRING_END
- {
- result = @builder.words_compose(val[0], val[1], val[2])
- }
-
- qsymbols: tQSYMBOLS_BEG qsym_list tSTRING_END
- {
- result = @builder.symbols_compose(val[0], val[1], val[2])
- }
-
- qword_list: # nothing
- {
- result = []
- }
- | qword_list tSTRING_CONTENT tSPACE
- {
- result = val[0] << @builder.string_internal(val[1])
- }
-
- qsym_list: # nothing
- {
- result = []
- }
- | qsym_list tSTRING_CONTENT tSPACE
- {
- result = val[0] << @builder.symbol_internal(val[1])
- }
-
- string_contents: # nothing
- {
- result = []
- }
- | string_contents string_content
- {
- result = val[0] << val[1]
- }
-
-xstring_contents: # nothing
- {
- result = []
- }
- | xstring_contents string_content
- {
- result = val[0] << val[1]
- }
-
-regexp_contents: # nothing
- {
- result = []
- }
- | regexp_contents string_content
- {
- result = val[0] << val[1]
- }
-
- string_content: tSTRING_CONTENT
- {
- result = @builder.string_internal(val[0])
- }
- | tSTRING_DVAR string_dvar
- {
- result = val[1]
- }
- | tSTRING_DBEG
- {
- @lexer.cond.push(false)
- @lexer.cmdarg.push(false)
- }
- compstmt tSTRING_DEND
- {
- @lexer.cond.lexpop
- @lexer.cmdarg.lexpop
-
- result = @builder.begin(val[0], val[2], val[3])
- }
-
- string_dvar: tGVAR
- {
- result = @builder.gvar(val[0])
- }
- | tIVAR
- {
- result = @builder.ivar(val[0])
- }
- | tCVAR
- {
- result = @builder.cvar(val[0])
- }
- | backref
-
-
- symbol: tSYMBOL
- {
- result = @builder.symbol(val[0])
- }
-
- dsym: tSYMBEG xstring_contents tSTRING_END
- {
- result = @builder.symbol_compose(val[0], val[1], val[2])
- }
-
- numeric: simple_numeric
- {
- result = val[0]
- }
- | tUMINUS_NUM simple_numeric =tLOWEST
- {
- result = @builder.negate(val[0], val[1])
- }
-
- simple_numeric: tINTEGER
- {
- result = @builder.integer(val[0])
- }
- | tFLOAT
- {
- result = @builder.float(val[0])
- }
- | tRATIONAL
- {
- result = @builder.rational(val[0])
- }
- | tIMAGINARY
- {
- result = @builder.complex(val[0])
- }
-
- user_variable: tIDENTIFIER
- {
- result = @builder.ident(val[0])
- }
- | tIVAR
- {
- result = @builder.ivar(val[0])
- }
- | tGVAR
- {
- result = @builder.gvar(val[0])
- }
- | tCONSTANT
- {
- result = @builder.const(val[0])
- }
- | tCVAR
- {
- result = @builder.cvar(val[0])
- }
-
-keyword_variable: kNIL
- {
- result = @builder.nil(val[0])
- }
- | kSELF
- {
- result = @builder.self(val[0])
- }
- | kTRUE
- {
- result = @builder.true(val[0])
- }
- | kFALSE
- {
- result = @builder.false(val[0])
- }
- | k__FILE__
- {
- result = @builder.__FILE__(val[0])
- }
- | k__LINE__
- {
- result = @builder.__LINE__(val[0])
- }
- | k__ENCODING__
- {
- result = @builder.__ENCODING__(val[0])
- }
-
- var_ref: user_variable
- {
- result = @builder.accessible(val[0])
- }
- | keyword_variable
- {
- result = @builder.accessible(val[0])
- }
-
- var_lhs: user_variable
- {
- result = @builder.assignable(val[0])
- }
- | keyword_variable
- {
- result = @builder.assignable(val[0])
- }
-
- backref: tNTH_REF
- {
- result = @builder.nth_ref(val[0])
- }
- | tBACK_REF
- {
- result = @builder.back_ref(val[0])
- }
-
- superclass: term
- {
- result = nil
- }
- | tLT
- {
- @lexer.state = :expr_value
- }
- expr_value term
- {
- result = [ val[0], val[2] ]
- }
- | error term
- {
- yyerrok
- result = nil
- }
-
- f_arglist: tLPAREN2 f_args rparen
- {
- result = @builder.args(val[0], val[1], val[2])
-
- @lexer.state = :expr_value
- }
- | {
- result = @lexer.in_kwarg
- @lexer.in_kwarg = true
- }
- f_args term
- {
- @lexer.in_kwarg = val[0]
- result = @builder.args(nil, val[1], nil)
- }
-
-
- args_tail: f_kwarg tCOMMA f_kwrest opt_f_block_arg
- {
- result = val[0].concat(val[2]).concat(val[3])
- }
- | f_kwarg opt_f_block_arg
- {
- result = val[0].concat(val[1])
- }
- | f_kwrest opt_f_block_arg
- {
- result = val[0].concat(val[1])
- }
- | f_block_arg
- {
- result = [ val[0] ]
- }
-
- opt_args_tail: tCOMMA args_tail
- {
- result = val[1]
- }
- | # nothing
- {
- result = []
- }
-
- f_args: f_arg tCOMMA f_optarg tCOMMA f_rest_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg tCOMMA f_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[6]).
- concat(val[7])
- }
- | f_arg tCOMMA f_optarg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_arg tCOMMA f_optarg tCOMMA f_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg tCOMMA f_rest_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_arg tCOMMA f_rest_arg tCOMMA f_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg opt_args_tail
- {
- result = val[0].
- concat(val[1])
- }
- | f_optarg tCOMMA f_rest_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_optarg opt_args_tail
- {
- result = val[0].
- concat(val[1])
- }
- | f_optarg tCOMMA f_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_rest_arg opt_args_tail
- {
- result = val[0].
- concat(val[1])
- }
- | f_rest_arg tCOMMA f_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | args_tail
- {
- result = val[0]
- }
- | # nothing
- {
- result = []
- }
-
- f_bad_arg: tCONSTANT
- {
- diagnostic :error, :argument_const, nil, val[0]
- }
- | tIVAR
- {
- diagnostic :error, :argument_ivar, nil, val[0]
- }
- | tGVAR
- {
- diagnostic :error, :argument_gvar, nil, val[0]
- }
- | tCVAR
- {
- diagnostic :error, :argument_cvar, nil, val[0]
- }
-
- f_norm_arg: f_bad_arg
- | tIDENTIFIER
- {
- @static_env.declare val[0][0]
-
- result = val[0]
- }
-
- f_arg_item: f_norm_arg
- {
- result = @builder.arg(val[0])
- }
- | tLPAREN f_margs rparen
- {
- result = @builder.multi_lhs(val[0], val[1], val[2])
- }
-
- f_arg: f_arg_item
- {
- result = [ val[0] ]
- }
- | f_arg tCOMMA f_arg_item
- {
- result = val[0] << val[2]
- }
-
- f_label: tLABEL
- {
- check_kwarg_name(val[0])
-
- @static_env.declare val[0][0]
-
- result = val[0]
- }
-
- f_kw: f_label arg_value
- {
- result = @builder.kwoptarg(val[0], val[1])
- }
- | f_label
- {
- result = @builder.kwarg(val[0])
- }
-
- f_block_kw: f_label primary_value
- {
- result = @builder.kwoptarg(val[0], val[1])
- }
- | f_label
- {
- result = @builder.kwarg(val[0])
- }
-
- f_block_kwarg: f_block_kw
- {
- result = [ val[0] ]
- }
- | f_block_kwarg tCOMMA f_block_kw
- {
- result = val[0] << val[2]
- }
-
- f_kwarg: f_kw
- {
- result = [ val[0] ]
- }
- | f_kwarg tCOMMA f_kw
- {
- result = val[0] << val[2]
- }
-
- kwrest_mark: tPOW | tDSTAR
-
- f_kwrest: kwrest_mark tIDENTIFIER
- {
- @static_env.declare val[1][0]
-
- result = [ @builder.kwrestarg(val[0], val[1]) ]
- }
- | kwrest_mark
- {
- result = [ @builder.kwrestarg(val[0]) ]
- }
-
- f_opt: f_norm_arg tEQL arg_value
- {
- result = @builder.optarg(val[0], val[1], val[2])
- }
-
- f_block_opt: f_norm_arg tEQL primary_value
- {
- result = @builder.optarg(val[0], val[1], val[2])
- }
-
- f_block_optarg: f_block_opt
- {
- result = [ val[0] ]
- }
- | f_block_optarg tCOMMA f_block_opt
- {
- result = val[0] << val[2]
- }
-
- f_optarg: f_opt
- {
- result = [ val[0] ]
- }
- | f_optarg tCOMMA f_opt
- {
- result = val[0] << val[2]
- }
-
- restarg_mark: tSTAR2 | tSTAR
-
- f_rest_arg: restarg_mark tIDENTIFIER
- {
- @static_env.declare val[1][0]
-
- result = [ @builder.restarg(val[0], val[1]) ]
- }
- | restarg_mark
- {
- result = [ @builder.restarg(val[0]) ]
- }
-
- blkarg_mark: tAMPER2 | tAMPER
-
- f_block_arg: blkarg_mark tIDENTIFIER
- {
- @static_env.declare val[1][0]
-
- result = @builder.blockarg(val[0], val[1])
- }
-
- opt_f_block_arg: tCOMMA f_block_arg
- {
- result = [ val[1] ]
- }
- |
- {
- result = []
- }
-
- singleton: var_ref
- | tLPAREN2 expr rparen
- {
- result = val[1]
- }
-
- assoc_list: # nothing
- {
- result = []
- }
- | assocs trailer
-
- assocs: assoc
- {
- result = [ val[0] ]
- }
- | assocs tCOMMA assoc
- {
- result = val[0] << val[2]
- }
-
- assoc: arg_value tASSOC arg_value
- {
- result = @builder.pair(val[0], val[1], val[2])
- }
- | tLABEL arg_value
- {
- result = @builder.pair_keyword(val[0], val[1])
- }
- | tDSTAR arg_value
- {
- result = @builder.kwsplat(val[0], val[1])
- }
-
- operation: tIDENTIFIER | tCONSTANT | tFID
- operation2: tIDENTIFIER | tCONSTANT | tFID | op
- operation3: tIDENTIFIER | tFID | op
- dot_or_colon: tDOT | tCOLON2
- opt_terms: | terms
- opt_nl: | tNL
- rparen: opt_nl tRPAREN
- {
- result = val[1]
- }
- rbracket: opt_nl tRBRACK
- {
- result = val[1]
- }
- trailer: | tNL | tCOMMA
-
- term: tSEMI
- {
- yyerrok
- }
- | tNL
-
- terms: term
- | terms tSEMI
-
- none: # nothing
- {
- result = nil
- }
-end
-
----- header
-
-require 'parser'
-
-Parser.check_for_encoding_support
-
----- inner
-
- def version
- 21
- end
-
- def default_encoding
- Encoding::UTF_8
- end
diff --git a/test/racc/assets/ruby22.y b/test/racc/assets/ruby22.y
deleted file mode 100644
index 751c0e866b..0000000000
--- a/test/racc/assets/ruby22.y
+++ /dev/null
@@ -1,2381 +0,0 @@
-# Copyright (c) 2013 Peter Zotov <whitequark@whitequark.org>
-#
-# Parts of the source are derived from ruby_parser:
-# Copyright (c) Ryan Davis, seattle.rb
-#
-# MIT License
-#
-# 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.
-
-class Parser::Ruby22
-
-token kCLASS kMODULE kDEF kUNDEF kBEGIN kRESCUE kENSURE kEND kIF kUNLESS
- kTHEN kELSIF kELSE kCASE kWHEN kWHILE kUNTIL kFOR kBREAK kNEXT
- kREDO kRETRY kIN kDO kDO_COND kDO_BLOCK kDO_LAMBDA kRETURN kYIELD kSUPER
- kSELF kNIL kTRUE kFALSE kAND kOR kNOT kIF_MOD kUNLESS_MOD kWHILE_MOD
- kUNTIL_MOD kRESCUE_MOD kALIAS kDEFINED klBEGIN klEND k__LINE__
- k__FILE__ k__ENCODING__ tIDENTIFIER tFID tGVAR tIVAR tCONSTANT
- tLABEL tCVAR tNTH_REF tBACK_REF tSTRING_CONTENT tINTEGER tFLOAT
- tREGEXP_END tUPLUS tUMINUS tUMINUS_NUM tPOW tCMP tEQ tEQQ tNEQ
- tGEQ tLEQ tANDOP tOROP tMATCH tNMATCH tDOT tDOT2 tDOT3 tAREF
- tASET tLSHFT tRSHFT tCOLON2 tCOLON3 tOP_ASGN tASSOC tLPAREN
- tLPAREN2 tRPAREN tLPAREN_ARG tLBRACK tLBRACK2 tRBRACK tLBRACE
- tLBRACE_ARG tSTAR tSTAR2 tAMPER tAMPER2 tTILDE tPERCENT tDIVIDE
- tDSTAR tPLUS tMINUS tLT tGT tPIPE tBANG tCARET tLCURLY tRCURLY
- tBACK_REF2 tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG tREGEXP_OPT
- tWORDS_BEG tQWORDS_BEG tSYMBOLS_BEG tQSYMBOLS_BEG tSTRING_DBEG
- tSTRING_DVAR tSTRING_END tSTRING_DEND tSTRING tSYMBOL
- tNL tEH tCOLON tCOMMA tSPACE tSEMI tLAMBDA tLAMBEG tCHARACTER
- tRATIONAL tIMAGINARY tLABEL_END
-
-prechigh
- right tBANG tTILDE tUPLUS
- right tPOW
- right tUMINUS_NUM tUMINUS
- left tSTAR2 tDIVIDE tPERCENT
- left tPLUS tMINUS
- left tLSHFT tRSHFT
- left tAMPER2
- left tPIPE tCARET
- left tGT tGEQ tLT tLEQ
- nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH
- left tANDOP
- left tOROP
- nonassoc tDOT2 tDOT3
- right tEH tCOLON
- left kRESCUE_MOD
- right tEQL tOP_ASGN
- nonassoc kDEFINED
- right kNOT
- left kOR kAND
- nonassoc kIF_MOD kUNLESS_MOD kWHILE_MOD kUNTIL_MOD
- nonassoc tLBRACE_ARG
- nonassoc tLOWEST
-preclow
-
-rule
-
- program: top_compstmt
-
- top_compstmt: top_stmts opt_terms
- {
- result = @builder.compstmt(val[0])
- }
-
- top_stmts: # nothing
- {
- result = []
- }
- | top_stmt
- {
- result = [ val[0] ]
- }
- | top_stmts terms top_stmt
- {
- result = val[0] << val[2]
- }
- | error top_stmt
- {
- result = [ val[1] ]
- }
-
- top_stmt: stmt
- | klBEGIN tLCURLY top_compstmt tRCURLY
- {
- result = @builder.preexe(val[0], val[1], val[2], val[3])
- }
-
- bodystmt: compstmt opt_rescue opt_else opt_ensure
- {
- rescue_bodies = val[1]
- else_t, else_ = val[2]
- ensure_t, ensure_ = val[3]
-
- if rescue_bodies.empty? && !else_.nil?
- diagnostic :warning, :useless_else, nil, else_t
- end
-
- result = @builder.begin_body(val[0],
- rescue_bodies,
- else_t, else_,
- ensure_t, ensure_)
- }
-
- compstmt: stmts opt_terms
- {
- result = @builder.compstmt(val[0])
- }
-
- stmts: # nothing
- {
- result = []
- }
- | stmt_or_begin
- {
- result = [ val[0] ]
- }
- | stmts terms stmt_or_begin
- {
- result = val[0] << val[2]
- }
- | error stmt
- {
- result = [ val[1] ]
- }
-
- stmt_or_begin: stmt
- | klBEGIN tLCURLY top_compstmt tRCURLY
- {
- diagnostic :error, :begin_in_method, nil, val[0]
- }
-
- stmt: kALIAS fitem
- {
- @lexer.state = :expr_fname
- }
- fitem
- {
- result = @builder.alias(val[0], val[1], val[3])
- }
- | kALIAS tGVAR tGVAR
- {
- result = @builder.alias(val[0],
- @builder.gvar(val[1]),
- @builder.gvar(val[2]))
- }
- | kALIAS tGVAR tBACK_REF
- {
- result = @builder.alias(val[0],
- @builder.gvar(val[1]),
- @builder.back_ref(val[2]))
- }
- | kALIAS tGVAR tNTH_REF
- {
- diagnostic :error, :nth_ref_alias, nil, val[2]
- }
- | kUNDEF undef_list
- {
- result = @builder.undef_method(val[0], val[1])
- }
- | stmt kIF_MOD expr_value
- {
- result = @builder.condition_mod(val[0], nil,
- val[1], val[2])
- }
- | stmt kUNLESS_MOD expr_value
- {
- result = @builder.condition_mod(nil, val[0],
- val[1], val[2])
- }
- | stmt kWHILE_MOD expr_value
- {
- result = @builder.loop_mod(:while, val[0], val[1], val[2])
- }
- | stmt kUNTIL_MOD expr_value
- {
- result = @builder.loop_mod(:until, val[0], val[1], val[2])
- }
- | stmt kRESCUE_MOD stmt
- {
- rescue_body = @builder.rescue_body(val[1],
- nil, nil, nil,
- nil, val[2])
-
- result = @builder.begin_body(val[0], [ rescue_body ])
- }
- | klEND tLCURLY compstmt tRCURLY
- {
- result = @builder.postexe(val[0], val[1], val[2], val[3])
- }
- | command_asgn
- | mlhs tEQL command_call
- {
- result = @builder.multi_assign(val[0], val[1], val[2])
- }
- | var_lhs tOP_ASGN command_call
- {
- result = @builder.op_assign(val[0], val[1], val[2])
- }
- | primary_value tLBRACK2 opt_call_args rbracket tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.index(
- val[0], val[1], val[2], val[3]),
- val[4], val[5])
- }
- | primary_value tDOT tIDENTIFIER tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tDOT tCONSTANT tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | backref tOP_ASGN command_call
- {
- @builder.op_assign(val[0], val[1], val[2])
- }
- | lhs tEQL mrhs
- {
- result = @builder.assign(val[0], val[1],
- @builder.array(nil, val[2], nil))
- }
- | mlhs tEQL mrhs_arg
- {
- result = @builder.multi_assign(val[0], val[1], val[2])
- }
- | expr
-
- command_asgn: lhs tEQL command_call
- {
- result = @builder.assign(val[0], val[1], val[2])
- }
- | lhs tEQL command_asgn
- {
- result = @builder.assign(val[0], val[1], val[2])
- }
-
- expr: command_call
- | expr kAND expr
- {
- result = @builder.logical_op(:and, val[0], val[1], val[2])
- }
- | expr kOR expr
- {
- result = @builder.logical_op(:or, val[0], val[1], val[2])
- }
- | kNOT opt_nl expr
- {
- result = @builder.not_op(val[0], nil, val[2], nil)
- }
- | tBANG command_call
- {
- result = @builder.not_op(val[0], nil, val[1], nil)
- }
- | arg
-
- expr_value: expr
-
- command_call: command
- | block_command
-
- block_command: block_call
- | block_call dot_or_colon operation2 command_args
- {
- result = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
- }
-
- cmd_brace_block: tLBRACE_ARG
- {
- @static_env.extend_dynamic
- }
- opt_block_param compstmt tRCURLY
- {
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
- }
-
- fcall: operation
-
- command: fcall command_args =tLOWEST
- {
- result = @builder.call_method(nil, nil, val[0],
- nil, val[1], nil)
- }
- | fcall command_args cmd_brace_block
- {
- method_call = @builder.call_method(nil, nil, val[0],
- nil, val[1], nil)
-
- begin_t, args, body, end_t = val[2]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
- | primary_value tDOT operation2 command_args =tLOWEST
- {
- result = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
- }
- | primary_value tDOT operation2 command_args cmd_brace_block
- {
- method_call = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
-
- begin_t, args, body, end_t = val[4]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
- | primary_value tCOLON2 operation2 command_args =tLOWEST
- {
- result = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
- }
- | primary_value tCOLON2 operation2 command_args cmd_brace_block
- {
- method_call = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
-
- begin_t, args, body, end_t = val[4]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
- | kSUPER command_args
- {
- result = @builder.keyword_cmd(:super, val[0],
- nil, val[1], nil)
- }
- | kYIELD command_args
- {
- result = @builder.keyword_cmd(:yield, val[0],
- nil, val[1], nil)
- }
- | kRETURN call_args
- {
- result = @builder.keyword_cmd(:return, val[0],
- nil, val[1], nil)
- }
- | kBREAK call_args
- {
- result = @builder.keyword_cmd(:break, val[0],
- nil, val[1], nil)
- }
- | kNEXT call_args
- {
- result = @builder.keyword_cmd(:next, val[0],
- nil, val[1], nil)
- }
-
- mlhs: mlhs_basic
- {
- result = @builder.multi_lhs(nil, val[0], nil)
- }
- | tLPAREN mlhs_inner rparen
- {
- result = @builder.begin(val[0], val[1], val[2])
- }
-
- mlhs_inner: mlhs_basic
- {
- result = @builder.multi_lhs(nil, val[0], nil)
- }
- | tLPAREN mlhs_inner rparen
- {
- result = @builder.multi_lhs(val[0], val[1], val[2])
- }
-
- mlhs_basic: mlhs_head
- | mlhs_head mlhs_item
- {
- result = val[0].
- push(val[1])
- }
- | mlhs_head tSTAR mlhs_node
- {
- result = val[0].
- push(@builder.splat(val[1], val[2]))
- }
- | mlhs_head tSTAR mlhs_node tCOMMA mlhs_post
- {
- result = val[0].
- push(@builder.splat(val[1], val[2])).
- concat(val[4])
- }
- | mlhs_head tSTAR
- {
- result = val[0].
- push(@builder.splat(val[1]))
- }
- | mlhs_head tSTAR tCOMMA mlhs_post
- {
- result = val[0].
- push(@builder.splat(val[1])).
- concat(val[3])
- }
- | tSTAR mlhs_node
- {
- result = [ @builder.splat(val[0], val[1]) ]
- }
- | tSTAR mlhs_node tCOMMA mlhs_post
- {
- result = [ @builder.splat(val[0], val[1]),
- *val[3] ]
- }
- | tSTAR
- {
- result = [ @builder.splat(val[0]) ]
- }
- | tSTAR tCOMMA mlhs_post
- {
- result = [ @builder.splat(val[0]),
- *val[2] ]
- }
-
- mlhs_item: mlhs_node
- | tLPAREN mlhs_inner rparen
- {
- result = @builder.begin(val[0], val[1], val[2])
- }
-
- mlhs_head: mlhs_item tCOMMA
- {
- result = [ val[0] ]
- }
- | mlhs_head mlhs_item tCOMMA
- {
- result = val[0] << val[1]
- }
-
- mlhs_post: mlhs_item
- {
- result = [ val[0] ]
- }
- | mlhs_post tCOMMA mlhs_item
- {
- result = val[0] << val[2]
- }
-
- mlhs_node: user_variable
- {
- result = @builder.assignable(val[0])
- }
- | keyword_variable
- {
- result = @builder.assignable(val[0])
- }
- | primary_value tLBRACK2 opt_call_args rbracket
- {
- result = @builder.index_asgn(val[0], val[1], val[2], val[3])
- }
- | primary_value tDOT tIDENTIFIER
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tIDENTIFIER
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tDOT tCONSTANT
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tCONSTANT
- {
- result = @builder.assignable(
- @builder.const_fetch(val[0], val[1], val[2]))
- }
- | tCOLON3 tCONSTANT
- {
- result = @builder.assignable(
- @builder.const_global(val[0], val[1]))
- }
- | backref
- {
- result = @builder.assignable(val[0])
- }
-
- lhs: user_variable
- {
- result = @builder.assignable(val[0])
- }
- | keyword_variable
- {
- result = @builder.assignable(val[0])
- }
- | primary_value tLBRACK2 opt_call_args rbracket
- {
- result = @builder.index_asgn(val[0], val[1], val[2], val[3])
- }
- | primary_value tDOT tIDENTIFIER
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tIDENTIFIER
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tDOT tCONSTANT
- {
- result = @builder.attr_asgn(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tCONSTANT
- {
- result = @builder.assignable(
- @builder.const_fetch(val[0], val[1], val[2]))
- }
- | tCOLON3 tCONSTANT
- {
- result = @builder.assignable(
- @builder.const_global(val[0], val[1]))
- }
- | backref
- {
- result = @builder.assignable(val[0])
- }
-
- cname: tIDENTIFIER
- {
- diagnostic :error, :module_name_const, nil, val[0]
- }
- | tCONSTANT
-
- cpath: tCOLON3 cname
- {
- result = @builder.const_global(val[0], val[1])
- }
- | cname
- {
- result = @builder.const(val[0])
- }
- | primary_value tCOLON2 cname
- {
- result = @builder.const_fetch(val[0], val[1], val[2])
- }
-
- fname: tIDENTIFIER | tCONSTANT | tFID
- | op
- | reswords
-
- fsym: fname
- {
- result = @builder.symbol(val[0])
- }
- | symbol
-
- fitem: fsym
- | dsym
-
- undef_list: fitem
- {
- result = [ val[0] ]
- }
- | undef_list tCOMMA
- {
- @lexer.state = :expr_fname
- }
- fitem
- {
- result = val[0] << val[3]
- }
-
- op: tPIPE | tCARET | tAMPER2 | tCMP | tEQ | tEQQ
- | tMATCH | tNMATCH | tGT | tGEQ | tLT | tLEQ
- | tNEQ | tLSHFT | tRSHFT | tPLUS | tMINUS | tSTAR2
- | tSTAR | tDIVIDE | tPERCENT | tPOW | tBANG | tTILDE
- | tUPLUS | tUMINUS | tAREF | tASET | tDSTAR | tBACK_REF2
-
- reswords: k__LINE__ | k__FILE__ | k__ENCODING__ | klBEGIN | klEND
- | kALIAS | kAND | kBEGIN | kBREAK | kCASE
- | kCLASS | kDEF | kDEFINED | kDO | kELSE
- | kELSIF | kEND | kENSURE | kFALSE | kFOR
- | kIN | kMODULE | kNEXT | kNIL | kNOT
- | kOR | kREDO | kRESCUE | kRETRY | kRETURN
- | kSELF | kSUPER | kTHEN | kTRUE | kUNDEF
- | kWHEN | kYIELD | kIF | kUNLESS | kWHILE
- | kUNTIL
-
- arg: lhs tEQL arg
- {
- result = @builder.assign(val[0], val[1], val[2])
- }
- | lhs tEQL arg kRESCUE_MOD arg
- {
- rescue_body = @builder.rescue_body(val[3],
- nil, nil, nil,
- nil, val[4])
-
- rescue_ = @builder.begin_body(val[2], [ rescue_body ])
-
- result = @builder.assign(val[0], val[1], rescue_)
- }
- | var_lhs tOP_ASGN arg
- {
- result = @builder.op_assign(val[0], val[1], val[2])
- }
- | var_lhs tOP_ASGN arg kRESCUE_MOD arg
- {
- rescue_body = @builder.rescue_body(val[3],
- nil, nil, nil,
- nil, val[4])
-
- rescue_ = @builder.begin_body(val[2], [ rescue_body ])
-
- result = @builder.op_assign(val[0], val[1], rescue_)
- }
- | primary_value tLBRACK2 opt_call_args rbracket tOP_ASGN arg
- {
- result = @builder.op_assign(
- @builder.index(
- val[0], val[1], val[2], val[3]),
- val[4], val[5])
- }
- | primary_value tDOT tIDENTIFIER tOP_ASGN arg
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tDOT tCONSTANT tOP_ASGN arg
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg
- {
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
- }
- | primary_value tCOLON2 tCONSTANT tOP_ASGN arg
- {
- const = @builder.const_op_assignable(
- @builder.const_fetch(val[0], val[1], val[2]))
- result = @builder.op_assign(const, val[3], val[4])
- }
- | tCOLON3 tCONSTANT tOP_ASGN arg
- {
- const = @builder.const_op_assignable(
- @builder.const_global(val[0], val[1]))
- result = @builder.op_assign(const, val[2], val[3])
- }
- | backref tOP_ASGN arg
- {
- result = @builder.op_assign(val[0], val[1], val[2])
- }
- | arg tDOT2 arg
- {
- result = @builder.range_inclusive(val[0], val[1], val[2])
- }
- | arg tDOT3 arg
- {
- result = @builder.range_exclusive(val[0], val[1], val[2])
- }
- | arg tPLUS arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tMINUS arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tSTAR2 arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tDIVIDE arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tPERCENT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tPOW arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | tUMINUS_NUM simple_numeric tPOW arg
- {
- result = @builder.unary_op(val[0],
- @builder.binary_op(
- val[1], val[2], val[3]))
- }
- | tUPLUS arg
- {
- result = @builder.unary_op(val[0], val[1])
- }
- | tUMINUS arg
- {
- result = @builder.unary_op(val[0], val[1])
- }
- | arg tPIPE arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tCARET arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tAMPER2 arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tCMP arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tGT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tGEQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tLT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tLEQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tEQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tEQQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tNEQ arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tMATCH arg
- {
- result = @builder.match_op(val[0], val[1], val[2])
- }
- | arg tNMATCH arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | tBANG arg
- {
- result = @builder.not_op(val[0], nil, val[1], nil)
- }
- | tTILDE arg
- {
- result = @builder.unary_op(val[0], val[1])
- }
- | arg tLSHFT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tRSHFT arg
- {
- result = @builder.binary_op(val[0], val[1], val[2])
- }
- | arg tANDOP arg
- {
- result = @builder.logical_op(:and, val[0], val[1], val[2])
- }
- | arg tOROP arg
- {
- result = @builder.logical_op(:or, val[0], val[1], val[2])
- }
- | kDEFINED opt_nl arg
- {
- result = @builder.keyword_cmd(:defined?, val[0], nil, [ val[2] ], nil)
- }
-
- # Note: MRI eventually came to rely on disambiguation based on
- # the lexer state, but it is too contrived with the Ragel lexer,
- # so we kept this approach. See ruby/ruby@b0c03f63e5 for
- # the initial commit, and ruby/ruby@23352f62a for MRI revert,
- # which we decided not to track.
- | arg tEH
- {
- @lexer.push_cond
- @lexer.cond.push(true)
- }
- arg opt_nl tCOLON
- {
- @lexer.pop_cond
- }
- arg
- {
- result = @builder.ternary(val[0], val[1],
- val[3], val[5], val[7])
- }
- | primary
-
- arg_value: arg
-
- aref_args: none
- | args trailer
- | args tCOMMA assocs trailer
- {
- result = val[0] << @builder.associate(nil, val[2], nil)
- }
- | assocs trailer
- {
- result = [ @builder.associate(nil, val[0], nil) ]
- }
-
- paren_args: tLPAREN2 opt_call_args rparen
- {
- result = val
- }
-
- opt_paren_args: # nothing
- {
- result = [ nil, [], nil ]
- }
- | paren_args
-
- opt_call_args: # nothing
- {
- result = []
- }
- | call_args
- | args tCOMMA
- | args tCOMMA assocs tCOMMA
- {
- result = val[0] << @builder.associate(nil, val[2], nil)
- }
- | assocs tCOMMA
- {
- result = [ @builder.associate(nil, val[0], nil) ]
- }
-
- call_args: command
- {
- result = [ val[0] ]
- }
- | args opt_block_arg
- {
- result = val[0].concat(val[1])
- }
- | assocs opt_block_arg
- {
- result = [ @builder.associate(nil, val[0], nil) ]
- result.concat(val[1])
- }
- | args tCOMMA assocs opt_block_arg
- {
- assocs = @builder.associate(nil, val[2], nil)
- result = val[0] << assocs
- result.concat(val[3])
- }
- | block_arg
- {
- result = [ val[0] ]
- }
-
- command_args: {
- result = @lexer.cmdarg.dup
- @lexer.cmdarg.push(true)
- }
- call_args
- {
- @lexer.cmdarg = val[0]
-
- result = val[1]
- }
-
- block_arg: tAMPER arg_value
- {
- result = @builder.block_pass(val[0], val[1])
- }
-
- opt_block_arg: tCOMMA block_arg
- {
- result = [ val[1] ]
- }
- | # nothing
- {
- result = []
- }
-
- args: arg_value
- {
- result = [ val[0] ]
- }
- | tSTAR arg_value
- {
- result = [ @builder.splat(val[0], val[1]) ]
- }
- | args tCOMMA arg_value
- {
- result = val[0] << val[2]
- }
- | args tCOMMA tSTAR arg_value
- {
- result = val[0] << @builder.splat(val[2], val[3])
- }
-
- mrhs_arg: mrhs
- {
- result = @builder.array(nil, val[0], nil)
- }
- | arg_value
-
- mrhs: args tCOMMA arg_value
- {
- result = val[0] << val[2]
- }
- | args tCOMMA tSTAR arg_value
- {
- result = val[0] << @builder.splat(val[2], val[3])
- }
- | tSTAR arg_value
- {
- result = [ @builder.splat(val[0], val[1]) ]
- }
-
- primary: literal
- | strings
- | xstring
- | regexp
- | words
- | qwords
- | symbols
- | qsymbols
- | var_ref
- | backref
- | tFID
- {
- result = @builder.call_method(nil, nil, val[0])
- }
- | kBEGIN
- {
- result = @lexer.cmdarg.dup
- @lexer.cmdarg.clear
- }
- bodystmt kEND
- {
- @lexer.cmdarg = val[1]
-
- result = @builder.begin_keyword(val[0], val[2], val[3])
- }
- | tLPAREN_ARG
- {
- result = @lexer.cmdarg.dup
- @lexer.cmdarg.clear
- }
- expr
- {
- @lexer.state = :expr_endarg
- }
- opt_nl tRPAREN
- {
- @lexer.cmdarg = val[1]
-
- result = @builder.begin(val[0], val[2], val[5])
- }
- | tLPAREN_ARG
- {
- @lexer.state = :expr_endarg
- }
- opt_nl tRPAREN
- {
- result = @builder.begin(val[0], nil, val[3])
- }
- | tLPAREN compstmt tRPAREN
- {
- result = @builder.begin(val[0], val[1], val[2])
- }
- | primary_value tCOLON2 tCONSTANT
- {
- result = @builder.const_fetch(val[0], val[1], val[2])
- }
- | tCOLON3 tCONSTANT
- {
- result = @builder.const_global(val[0], val[1])
- }
- | tLBRACK aref_args tRBRACK
- {
- result = @builder.array(val[0], val[1], val[2])
- }
- | tLBRACE assoc_list tRCURLY
- {
- result = @builder.associate(val[0], val[1], val[2])
- }
- | kRETURN
- {
- result = @builder.keyword_cmd(:return, val[0])
- }
- | kYIELD tLPAREN2 call_args rparen
- {
- result = @builder.keyword_cmd(:yield, val[0], val[1], val[2], val[3])
- }
- | kYIELD tLPAREN2 rparen
- {
- result = @builder.keyword_cmd(:yield, val[0], val[1], [], val[2])
- }
- | kYIELD
- {
- result = @builder.keyword_cmd(:yield, val[0])
- }
- | kDEFINED opt_nl tLPAREN2 expr rparen
- {
- result = @builder.keyword_cmd(:defined?, val[0],
- val[2], [ val[3] ], val[4])
- }
- | kNOT tLPAREN2 expr rparen
- {
- result = @builder.not_op(val[0], val[1], val[2], val[3])
- }
- | kNOT tLPAREN2 rparen
- {
- result = @builder.not_op(val[0], val[1], nil, val[2])
- }
- | fcall brace_block
- {
- method_call = @builder.call_method(nil, nil, val[0])
-
- begin_t, args, body, end_t = val[1]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
- | method_call
- | method_call brace_block
- {
- begin_t, args, body, end_t = val[1]
- result = @builder.block(val[0],
- begin_t, args, body, end_t)
- }
- | tLAMBDA lambda
- {
- lambda_call = @builder.call_lambda(val[0])
-
- args, (begin_t, body, end_t) = val[1]
- result = @builder.block(lambda_call,
- begin_t, args, body, end_t)
- }
- | kIF expr_value then compstmt if_tail kEND
- {
- else_t, else_ = val[4]
- result = @builder.condition(val[0], val[1], val[2],
- val[3], else_t,
- else_, val[5])
- }
- | kUNLESS expr_value then compstmt opt_else kEND
- {
- else_t, else_ = val[4]
- result = @builder.condition(val[0], val[1], val[2],
- else_, else_t,
- val[3], val[5])
- }
- | kWHILE
- {
- @lexer.cond.push(true)
- }
- expr_value do
- {
- @lexer.cond.pop
- }
- compstmt kEND
- {
- result = @builder.loop(:while, val[0], val[2], val[3],
- val[5], val[6])
- }
- | kUNTIL
- {
- @lexer.cond.push(true)
- }
- expr_value do
- {
- @lexer.cond.pop
- }
- compstmt kEND
- {
- result = @builder.loop(:until, val[0], val[2], val[3],
- val[5], val[6])
- }
- | kCASE expr_value opt_terms case_body kEND
- {
- *when_bodies, (else_t, else_body) = *val[3]
-
- result = @builder.case(val[0], val[1],
- when_bodies, else_t, else_body,
- val[4])
- }
- | kCASE opt_terms case_body kEND
- {
- *when_bodies, (else_t, else_body) = *val[2]
-
- result = @builder.case(val[0], nil,
- when_bodies, else_t, else_body,
- val[3])
- }
- | kFOR for_var kIN
- {
- @lexer.cond.push(true)
- }
- expr_value do
- {
- @lexer.cond.pop
- }
- compstmt kEND
- {
- result = @builder.for(val[0], val[1],
- val[2], val[4],
- val[5], val[7], val[8])
- }
- | kCLASS cpath superclass
- {
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- bodystmt kEND
- {
- if in_def?
- diagnostic :error, :class_in_def, nil, val[0]
- end
-
- lt_t, superclass = val[2]
- result = @builder.def_class(val[0], val[1],
- lt_t, superclass,
- val[4], val[5])
-
- @lexer.pop_cmdarg
- @static_env.unextend
- }
- | kCLASS tLSHFT expr term
- {
- result = @def_level
- @def_level = 0
-
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- bodystmt kEND
- {
- result = @builder.def_sclass(val[0], val[1], val[2],
- val[5], val[6])
-
- @lexer.pop_cmdarg
- @static_env.unextend
-
- @def_level = val[4]
- }
- | kMODULE cpath
- {
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- bodystmt kEND
- {
- if in_def?
- diagnostic :error, :module_in_def, nil, val[0]
- end
-
- result = @builder.def_module(val[0], val[1],
- val[3], val[4])
-
- @lexer.pop_cmdarg
- @static_env.unextend
- }
- | kDEF fname
- {
- @def_level += 1
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- f_arglist bodystmt kEND
- {
- result = @builder.def_method(val[0], val[1],
- val[3], val[4], val[5])
-
- @lexer.pop_cmdarg
- @static_env.unextend
- @def_level -= 1
- }
- | kDEF singleton dot_or_colon
- {
- @lexer.state = :expr_fname
- }
- fname
- {
- @def_level += 1
- @static_env.extend_static
- @lexer.push_cmdarg
- }
- f_arglist bodystmt kEND
- {
- result = @builder.def_singleton(val[0], val[1], val[2],
- val[4], val[6], val[7], val[8])
-
- @lexer.pop_cmdarg
- @static_env.unextend
- @def_level -= 1
- }
- | kBREAK
- {
- result = @builder.keyword_cmd(:break, val[0])
- }
- | kNEXT
- {
- result = @builder.keyword_cmd(:next, val[0])
- }
- | kREDO
- {
- result = @builder.keyword_cmd(:redo, val[0])
- }
- | kRETRY
- {
- result = @builder.keyword_cmd(:retry, val[0])
- }
-
- primary_value: primary
-
- then: term
- | kTHEN
- | term kTHEN
- {
- result = val[1]
- }
-
- do: term
- | kDO_COND
-
- if_tail: opt_else
- | kELSIF expr_value then compstmt if_tail
- {
- else_t, else_ = val[4]
- result = [ val[0],
- @builder.condition(val[0], val[1], val[2],
- val[3], else_t,
- else_, nil),
- ]
- }
-
- opt_else: none
- | kELSE compstmt
- {
- result = val
- }
-
- for_var: lhs
- | mlhs
-
- f_marg: f_norm_arg
- {
- result = @builder.arg(val[0])
- }
- | tLPAREN f_margs rparen
- {
- result = @builder.multi_lhs(val[0], val[1], val[2])
- }
-
- f_marg_list: f_marg
- {
- result = [ val[0] ]
- }
- | f_marg_list tCOMMA f_marg
- {
- result = val[0] << val[2]
- }
-
- f_margs: f_marg_list
- | f_marg_list tCOMMA tSTAR f_norm_arg
- {
- result = val[0].
- push(@builder.restarg(val[2], val[3]))
- }
- | f_marg_list tCOMMA tSTAR f_norm_arg tCOMMA f_marg_list
- {
- result = val[0].
- push(@builder.restarg(val[2], val[3])).
- concat(val[5])
- }
- | f_marg_list tCOMMA tSTAR
- {
- result = val[0].
- push(@builder.restarg(val[2]))
- }
- | f_marg_list tCOMMA tSTAR tCOMMA f_marg_list
- {
- result = val[0].
- push(@builder.restarg(val[2])).
- concat(val[4])
- }
- | tSTAR f_norm_arg
- {
- result = [ @builder.restarg(val[0], val[1]) ]
- }
- | tSTAR f_norm_arg tCOMMA f_marg_list
- {
- result = [ @builder.restarg(val[0], val[1]),
- *val[3] ]
- }
- | tSTAR
- {
- result = [ @builder.restarg(val[0]) ]
- }
- | tSTAR tCOMMA f_marg_list
- {
- result = [ @builder.restarg(val[0]),
- *val[2] ]
- }
-
- block_args_tail: f_block_kwarg tCOMMA f_kwrest opt_f_block_arg
- {
- result = val[0].concat(val[2]).concat(val[3])
- }
- | f_block_kwarg opt_f_block_arg
- {
- result = val[0].concat(val[1])
- }
- | f_kwrest opt_f_block_arg
- {
- result = val[0].concat(val[1])
- }
- | f_block_arg
- {
- result = [ val[0] ]
- }
-
-opt_block_args_tail:
- tCOMMA block_args_tail
- {
- result = val[1]
- }
- | # nothing
- {
- result = []
- }
-
- block_param: f_arg tCOMMA f_block_optarg tCOMMA f_rest_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg tCOMMA f_block_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[6]).
- concat(val[7])
- }
- | f_arg tCOMMA f_block_optarg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_arg tCOMMA f_block_optarg tCOMMA f_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg tCOMMA f_rest_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_arg tCOMMA
- | f_arg tCOMMA f_rest_arg tCOMMA f_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg opt_block_args_tail
- {
- result = val[0].concat(val[1])
- }
- | f_block_optarg tCOMMA f_rest_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_block_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_block_optarg opt_block_args_tail
- {
- result = val[0].
- concat(val[1])
- }
- | f_block_optarg tCOMMA f_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_rest_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[1])
- }
- | f_rest_arg tCOMMA f_arg opt_block_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | block_args_tail
-
- opt_block_param: # nothing
- {
- result = @builder.args(nil, [], nil)
- }
- | block_param_def
- {
- @lexer.state = :expr_value
- }
-
- block_param_def: tPIPE opt_bv_decl tPIPE
- {
- result = @builder.args(val[0], val[1], val[2])
- }
- | tOROP
- {
- result = @builder.args(val[0], [], val[0])
- }
- | tPIPE block_param opt_bv_decl tPIPE
- {
- result = @builder.args(val[0], val[1].concat(val[2]), val[3])
- }
-
- opt_bv_decl: opt_nl
- {
- result = []
- }
- | opt_nl tSEMI bv_decls opt_nl
- {
- result = val[2]
- }
-
- bv_decls: bvar
- {
- result = [ val[0] ]
- }
- | bv_decls tCOMMA bvar
- {
- result = val[0] << val[2]
- }
-
- bvar: tIDENTIFIER
- {
- result = @builder.shadowarg(val[0])
- }
- | f_bad_arg
-
- lambda: {
- @static_env.extend_dynamic
- }
- f_larglist
- {
- result = @lexer.cmdarg.dup
- @lexer.cmdarg.clear
- }
- lambda_body
- {
- @lexer.cmdarg = val[2]
- @lexer.cmdarg.lexpop
-
- result = [ val[1], val[3] ]
-
- @static_env.unextend
- }
-
- f_larglist: tLPAREN2 f_args opt_bv_decl tRPAREN
- {
- result = @builder.args(val[0], val[1].concat(val[2]), val[3])
- }
- | f_args
- {
- result = @builder.args(nil, val[0], nil)
- }
-
- lambda_body: tLAMBEG compstmt tRCURLY
- {
- result = [ val[0], val[1], val[2] ]
- }
- | kDO_LAMBDA compstmt kEND
- {
- result = [ val[0], val[1], val[2] ]
- }
-
- do_block: kDO_BLOCK
- {
- @static_env.extend_dynamic
- }
- opt_block_param compstmt kEND
- {
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
- }
-
- block_call: command do_block
- {
- begin_t, block_args, body, end_t = val[1]
- result = @builder.block(val[0],
- begin_t, block_args, body, end_t)
- }
- | block_call dot_or_colon operation2 opt_paren_args
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
- }
- | block_call dot_or_colon operation2 opt_paren_args brace_block
- {
- lparen_t, args, rparen_t = val[3]
- method_call = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
-
- begin_t, args, body, end_t = val[4]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
- | block_call dot_or_colon operation2 command_args do_block
- {
- method_call = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
-
- begin_t, args, body, end_t = val[4]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
- }
-
- method_call: fcall paren_args
- {
- lparen_t, args, rparen_t = val[1]
- result = @builder.call_method(nil, nil, val[0],
- lparen_t, args, rparen_t)
- }
- | primary_value tDOT operation2 opt_paren_args
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
- }
- | primary_value tCOLON2 operation2 paren_args
- {
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
- }
- | primary_value tCOLON2 operation3
- {
- result = @builder.call_method(val[0], val[1], val[2])
- }
- | primary_value tDOT paren_args
- {
- lparen_t, args, rparen_t = val[2]
- result = @builder.call_method(val[0], val[1], nil,
- lparen_t, args, rparen_t)
- }
- | primary_value tCOLON2 paren_args
- {
- lparen_t, args, rparen_t = val[2]
- result = @builder.call_method(val[0], val[1], nil,
- lparen_t, args, rparen_t)
- }
- | kSUPER paren_args
- {
- lparen_t, args, rparen_t = val[1]
- result = @builder.keyword_cmd(:super, val[0],
- lparen_t, args, rparen_t)
- }
- | kSUPER
- {
- result = @builder.keyword_cmd(:zsuper, val[0])
- }
- | primary_value tLBRACK2 opt_call_args rbracket
- {
- result = @builder.index(val[0], val[1], val[2], val[3])
- }
-
- brace_block: tLCURLY
- {
- @static_env.extend_dynamic
- }
- opt_block_param compstmt tRCURLY
- {
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
- }
- | kDO
- {
- @static_env.extend_dynamic
- }
- opt_block_param compstmt kEND
- {
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
- }
-
- case_body: kWHEN args then compstmt cases
- {
- result = [ @builder.when(val[0], val[1], val[2], val[3]),
- *val[4] ]
- }
-
- cases: opt_else
- {
- result = [ val[0] ]
- }
- | case_body
-
- opt_rescue: kRESCUE exc_list exc_var then compstmt opt_rescue
- {
- assoc_t, exc_var = val[2]
-
- if val[1]
- exc_list = @builder.array(nil, val[1], nil)
- end
-
- result = [ @builder.rescue_body(val[0],
- exc_list, assoc_t, exc_var,
- val[3], val[4]),
- *val[5] ]
- }
- |
- {
- result = []
- }
-
- exc_list: arg_value
- {
- result = [ val[0] ]
- }
- | mrhs
- | none
-
- exc_var: tASSOC lhs
- {
- result = [ val[0], val[1] ]
- }
- | none
-
- opt_ensure: kENSURE compstmt
- {
- result = [ val[0], val[1] ]
- }
- | none
-
- literal: numeric
- | symbol
- | dsym
-
- strings: string
- {
- result = @builder.string_compose(nil, val[0], nil)
- }
-
- string: string1
- {
- result = [ val[0] ]
- }
- | string string1
- {
- result = val[0] << val[1]
- }
-
- string1: tSTRING_BEG string_contents tSTRING_END
- {
- result = @builder.string_compose(val[0], val[1], val[2])
- }
- | tSTRING
- {
- result = @builder.string(val[0])
- }
- | tCHARACTER
- {
- result = @builder.character(val[0])
- }
-
- xstring: tXSTRING_BEG xstring_contents tSTRING_END
- {
- result = @builder.xstring_compose(val[0], val[1], val[2])
- }
-
- regexp: tREGEXP_BEG regexp_contents tSTRING_END tREGEXP_OPT
- {
- opts = @builder.regexp_options(val[3])
- result = @builder.regexp_compose(val[0], val[1], val[2], opts)
- }
-
- words: tWORDS_BEG word_list tSTRING_END
- {
- result = @builder.words_compose(val[0], val[1], val[2])
- }
-
- word_list: # nothing
- {
- result = []
- }
- | word_list word tSPACE
- {
- result = val[0] << @builder.word(val[1])
- }
-
- word: string_content
- {
- result = [ val[0] ]
- }
- | word string_content
- {
- result = val[0] << val[1]
- }
-
- symbols: tSYMBOLS_BEG symbol_list tSTRING_END
- {
- result = @builder.symbols_compose(val[0], val[1], val[2])
- }
-
- symbol_list: # nothing
- {
- result = []
- }
- | symbol_list word tSPACE
- {
- result = val[0] << @builder.word(val[1])
- }
-
- qwords: tQWORDS_BEG qword_list tSTRING_END
- {
- result = @builder.words_compose(val[0], val[1], val[2])
- }
-
- qsymbols: tQSYMBOLS_BEG qsym_list tSTRING_END
- {
- result = @builder.symbols_compose(val[0], val[1], val[2])
- }
-
- qword_list: # nothing
- {
- result = []
- }
- | qword_list tSTRING_CONTENT tSPACE
- {
- result = val[0] << @builder.string_internal(val[1])
- }
-
- qsym_list: # nothing
- {
- result = []
- }
- | qsym_list tSTRING_CONTENT tSPACE
- {
- result = val[0] << @builder.symbol_internal(val[1])
- }
-
- string_contents: # nothing
- {
- result = []
- }
- | string_contents string_content
- {
- result = val[0] << val[1]
- }
-
-xstring_contents: # nothing
- {
- result = []
- }
- | xstring_contents string_content
- {
- result = val[0] << val[1]
- }
-
-regexp_contents: # nothing
- {
- result = []
- }
- | regexp_contents string_content
- {
- result = val[0] << val[1]
- }
-
- string_content: tSTRING_CONTENT
- {
- result = @builder.string_internal(val[0])
- }
- | tSTRING_DVAR string_dvar
- {
- result = val[1]
- }
- | tSTRING_DBEG
- {
- @lexer.cond.push(false)
- @lexer.cmdarg.push(false)
- }
- compstmt tSTRING_DEND
- {
- @lexer.cond.lexpop
- @lexer.cmdarg.lexpop
-
- result = @builder.begin(val[0], val[2], val[3])
- }
-
- string_dvar: tGVAR
- {
- result = @builder.gvar(val[0])
- }
- | tIVAR
- {
- result = @builder.ivar(val[0])
- }
- | tCVAR
- {
- result = @builder.cvar(val[0])
- }
- | backref
-
-
- symbol: tSYMBOL
- {
- result = @builder.symbol(val[0])
- }
-
- dsym: tSYMBEG xstring_contents tSTRING_END
- {
- result = @builder.symbol_compose(val[0], val[1], val[2])
- }
-
- numeric: simple_numeric
- {
- result = val[0]
- }
- | tUMINUS_NUM simple_numeric =tLOWEST
- {
- result = @builder.negate(val[0], val[1])
- }
-
- simple_numeric: tINTEGER
- {
- result = @builder.integer(val[0])
- }
- | tFLOAT
- {
- result = @builder.float(val[0])
- }
- | tRATIONAL
- {
- result = @builder.rational(val[0])
- }
- | tIMAGINARY
- {
- result = @builder.complex(val[0])
- }
-
- user_variable: tIDENTIFIER
- {
- result = @builder.ident(val[0])
- }
- | tIVAR
- {
- result = @builder.ivar(val[0])
- }
- | tGVAR
- {
- result = @builder.gvar(val[0])
- }
- | tCONSTANT
- {
- result = @builder.const(val[0])
- }
- | tCVAR
- {
- result = @builder.cvar(val[0])
- }
-
-keyword_variable: kNIL
- {
- result = @builder.nil(val[0])
- }
- | kSELF
- {
- result = @builder.self(val[0])
- }
- | kTRUE
- {
- result = @builder.true(val[0])
- }
- | kFALSE
- {
- result = @builder.false(val[0])
- }
- | k__FILE__
- {
- result = @builder.__FILE__(val[0])
- }
- | k__LINE__
- {
- result = @builder.__LINE__(val[0])
- }
- | k__ENCODING__
- {
- result = @builder.__ENCODING__(val[0])
- }
-
- var_ref: user_variable
- {
- result = @builder.accessible(val[0])
- }
- | keyword_variable
- {
- result = @builder.accessible(val[0])
- }
-
- var_lhs: user_variable
- {
- result = @builder.assignable(val[0])
- }
- | keyword_variable
- {
- result = @builder.assignable(val[0])
- }
-
- backref: tNTH_REF
- {
- result = @builder.nth_ref(val[0])
- }
- | tBACK_REF
- {
- result = @builder.back_ref(val[0])
- }
-
- superclass: term
- {
- result = nil
- }
- | tLT
- {
- @lexer.state = :expr_value
- }
- expr_value term
- {
- result = [ val[0], val[2] ]
- }
- | error term
- {
- yyerrok
- result = nil
- }
-
- f_arglist: tLPAREN2 f_args rparen
- {
- result = @builder.args(val[0], val[1], val[2])
-
- @lexer.state = :expr_value
- }
- | {
- result = @lexer.in_kwarg
- @lexer.in_kwarg = true
- }
- f_args term
- {
- @lexer.in_kwarg = val[0]
- result = @builder.args(nil, val[1], nil)
- }
-
- args_tail: f_kwarg tCOMMA f_kwrest opt_f_block_arg
- {
- result = val[0].concat(val[2]).concat(val[3])
- }
- | f_kwarg opt_f_block_arg
- {
- result = val[0].concat(val[1])
- }
- | f_kwrest opt_f_block_arg
- {
- result = val[0].concat(val[1])
- }
- | f_block_arg
- {
- result = [ val[0] ]
- }
-
- opt_args_tail: tCOMMA args_tail
- {
- result = val[1]
- }
- | # nothing
- {
- result = []
- }
-
- f_args: f_arg tCOMMA f_optarg tCOMMA f_rest_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg tCOMMA f_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[6]).
- concat(val[7])
- }
- | f_arg tCOMMA f_optarg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_arg tCOMMA f_optarg tCOMMA f_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg tCOMMA f_rest_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_arg tCOMMA f_rest_arg tCOMMA f_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_arg opt_args_tail
- {
- result = val[0].
- concat(val[1])
- }
- | f_optarg tCOMMA f_rest_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
- }
- | f_optarg opt_args_tail
- {
- result = val[0].
- concat(val[1])
- }
- | f_optarg tCOMMA f_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | f_rest_arg opt_args_tail
- {
- result = val[0].
- concat(val[1])
- }
- | f_rest_arg tCOMMA f_arg opt_args_tail
- {
- result = val[0].
- concat(val[2]).
- concat(val[3])
- }
- | args_tail
- {
- result = val[0]
- }
- | # nothing
- {
- result = []
- }
-
- f_bad_arg: tCONSTANT
- {
- diagnostic :error, :argument_const, nil, val[0]
- }
- | tIVAR
- {
- diagnostic :error, :argument_ivar, nil, val[0]
- }
- | tGVAR
- {
- diagnostic :error, :argument_gvar, nil, val[0]
- }
- | tCVAR
- {
- diagnostic :error, :argument_cvar, nil, val[0]
- }
-
- f_norm_arg: f_bad_arg
- | tIDENTIFIER
- {
- @static_env.declare val[0][0]
-
- result = val[0]
- }
-
- f_arg_asgn: f_norm_arg
- {
- result = val[0]
- }
-
- f_arg_item: f_arg_asgn
- {
- result = @builder.arg(val[0])
- }
- | tLPAREN f_margs rparen
- {
- result = @builder.multi_lhs(val[0], val[1], val[2])
- }
-
- f_arg: f_arg_item
- {
- result = [ val[0] ]
- }
- | f_arg tCOMMA f_arg_item
- {
- result = val[0] << val[2]
- }
-
- f_label: tLABEL
- {
- check_kwarg_name(val[0])
-
- @static_env.declare val[0][0]
-
- result = val[0]
- }
-
- f_kw: f_label arg_value
- {
- result = @builder.kwoptarg(val[0], val[1])
- }
- | f_label
- {
- result = @builder.kwarg(val[0])
- }
-
- f_block_kw: f_label primary_value
- {
- result = @builder.kwoptarg(val[0], val[1])
- }
- | f_label
- {
- result = @builder.kwarg(val[0])
- }
-
- f_block_kwarg: f_block_kw
- {
- result = [ val[0] ]
- }
- | f_block_kwarg tCOMMA f_block_kw
- {
- result = val[0] << val[2]
- }
-
- f_kwarg: f_kw
- {
- result = [ val[0] ]
- }
- | f_kwarg tCOMMA f_kw
- {
- result = val[0] << val[2]
- }
-
- kwrest_mark: tPOW | tDSTAR
-
- f_kwrest: kwrest_mark tIDENTIFIER
- {
- @static_env.declare val[1][0]
-
- result = [ @builder.kwrestarg(val[0], val[1]) ]
- }
- | kwrest_mark
- {
- result = [ @builder.kwrestarg(val[0]) ]
- }
-
- f_opt: f_arg_asgn tEQL arg_value
- {
- result = @builder.optarg(val[0], val[1], val[2])
- }
-
- f_block_opt: f_arg_asgn tEQL primary_value
- {
- result = @builder.optarg(val[0], val[1], val[2])
- }
-
- f_block_optarg: f_block_opt
- {
- result = [ val[0] ]
- }
- | f_block_optarg tCOMMA f_block_opt
- {
- result = val[0] << val[2]
- }
-
- f_optarg: f_opt
- {
- result = [ val[0] ]
- }
- | f_optarg tCOMMA f_opt
- {
- result = val[0] << val[2]
- }
-
- restarg_mark: tSTAR2 | tSTAR
-
- f_rest_arg: restarg_mark tIDENTIFIER
- {
- @static_env.declare val[1][0]
-
- result = [ @builder.restarg(val[0], val[1]) ]
- }
- | restarg_mark
- {
- result = [ @builder.restarg(val[0]) ]
- }
-
- blkarg_mark: tAMPER2 | tAMPER
-
- f_block_arg: blkarg_mark tIDENTIFIER
- {
- @static_env.declare val[1][0]
-
- result = @builder.blockarg(val[0], val[1])
- }
-
- opt_f_block_arg: tCOMMA f_block_arg
- {
- result = [ val[1] ]
- }
- |
- {
- result = []
- }
-
- singleton: var_ref
- | tLPAREN2 expr rparen
- {
- result = val[1]
- }
-
- assoc_list: # nothing
- {
- result = []
- }
- | assocs trailer
-
- assocs: assoc
- {
- result = [ val[0] ]
- }
- | assocs tCOMMA assoc
- {
- result = val[0] << val[2]
- }
-
- assoc: arg_value tASSOC arg_value
- {
- result = @builder.pair(val[0], val[1], val[2])
- }
- | tLABEL arg_value
- {
- result = @builder.pair_keyword(val[0], val[1])
- }
- | tSTRING_BEG string_contents tLABEL_END arg_value
- {
- result = @builder.pair_quoted(val[0], val[1], val[2], val[3])
- }
- | tDSTAR arg_value
- {
- result = @builder.kwsplat(val[0], val[1])
- }
-
- operation: tIDENTIFIER | tCONSTANT | tFID
- operation2: tIDENTIFIER | tCONSTANT | tFID | op
- operation3: tIDENTIFIER | tFID | op
- dot_or_colon: tDOT | tCOLON2
- opt_terms: | terms
- opt_nl: | tNL
- rparen: opt_nl tRPAREN
- {
- result = val[1]
- }
- rbracket: opt_nl tRBRACK
- {
- result = val[1]
- }
- trailer: | tNL | tCOMMA
-
- term: tSEMI
- {
- yyerrok
- }
- | tNL
-
- terms: term
- | terms tSEMI
-
- none: # nothing
- {
- result = nil
- }
-end
-
----- header
-
-require 'parser'
-
-Parser.check_for_encoding_support
-
----- inner
-
- def version
- 22
- end
-
- def default_encoding
- Encoding::UTF_8
- end
diff --git a/test/racc/assets/scan.y b/test/racc/assets/scan.y
deleted file mode 100644
index 709254ed66..0000000000
--- a/test/racc/assets/scan.y
+++ /dev/null
@@ -1,72 +0,0 @@
-class P
-
-rule
-
- a: A
- {
- # comment test
-
- # comment test
-
- # string
- @sstring = 'squote string'
- @dstring = 'dquote string'
-
- # regexp
- @regexp = /some regexp with spaces/
-
- # gvar
- /regexp/ === 'some regexp matches to this string'
- @pre_match = $`
- @matched = $&
- @post_match = $'
- @m = $~
-
- # braces
- @array = []
- [1,2,3].each {|i|
- @array.push i
- }
- 3.times { @array.push 10 }
- }
-
-end
-
----- inner
-
- def parse
- @sstring = @dstring = nil
- @regexp = nil
- @pre_match = @matched = @post_match = @m = nil
-
- @src = [[:A, 'A'], [false, '$']]
- do_parse
-
- assert_equal 'squote string', @sstring
- assert_equal 'dquote string', @dstring
- assert_equal(/some regexp with spaces/, @regexp)
- assert_equal 'some ', @pre_match
- assert_equal 'regexp', @matched
- assert_equal ' matches to this string', @post_match
- assert_instance_of MatchData, @m
- end
-
- def assert_equal(ok, data)
- unless ok == data
- raise "expected <#{ok.inspect}> but is <#{data.inspect}>"
- end
- end
-
- def assert_instance_of(klass, obj)
- unless obj.instance_of?(klass)
- raise "expected #{klass} but is #{obj.class}"
- end
- end
-
- def next_token
- @src.shift
- end
-
----- footer
-
-P.new.parse
diff --git a/test/racc/assets/syntax.y b/test/racc/assets/syntax.y
deleted file mode 100644
index 727f74a29d..0000000000
--- a/test/racc/assets/syntax.y
+++ /dev/null
@@ -1,50 +0,0 @@
-#
-# racc syntax checker
-#
-
-class M1::M2::ParserClass < S1::S2::SuperClass
-
- token A
- | B C
-
- convert
- A '5'
- end
-
- prechigh
- left B
- preclow
-
- start target
-
- expect 0
-
-rule
-
- target: A B C
- {
- print 'abc'
- }
- | B C A
- | C B A
- {
- print 'cba'
- }
- | cont
-
- cont : A c2 B c2 C
-
- c2 : C C C C C
-
-end
-
----- inner
-
- junk code !!!!
-
-kjaljlajrlaolanbla /// %%% (*((( token rule
-akiurtlajluealjflaj @@@@ end end end end __END__
- laieu2o879urkq96ga(Q#*&%Q#
- #&lkji END
-
- q395q?/// liutjqlkr7
diff --git a/test/racc/assets/tp_plus.y b/test/racc/assets/tp_plus.y
deleted file mode 100644
index 388ed1302d..0000000000
--- a/test/racc/assets/tp_plus.y
+++ /dev/null
@@ -1,622 +0,0 @@
-# Released under an MIT License (http://www.opensource.org/licenses/MIT)
-# By Jay Strybis (https://github.com/unreal)
-
-class TPPlus::Parser
-token ASSIGN AT_SYM COMMENT JUMP IO_METHOD INPUT OUTPUT
-token NUMREG POSREG VREG SREG TIME_SEGMENT ARG UALM
-token MOVE DOT TO AT TERM OFFSET SKIP GROUP
-token SEMICOLON NEWLINE STRING
-token REAL DIGIT WORD EQUAL
-token EEQUAL NOTEQUAL GTE LTE LT GT BANG
-token PLUS MINUS STAR SLASH DIV AND OR MOD
-token IF ELSE END UNLESS FOR IN WHILE
-token WAIT_FOR WAIT_UNTIL TIMEOUT AFTER
-token FANUC_USE SET_SKIP_CONDITION NAMESPACE
-token CASE WHEN INDIRECT POSITION
-token EVAL TIMER TIMER_METHOD RAISE ABORT
-token POSITION_DATA TRUE_FALSE RUN TP_HEADER PAUSE
-token LPAREN RPAREN COLON COMMA LBRACK RBRACK LBRACE RBRACE
-token LABEL ADDRESS
-token false
-
-prechigh
- right BANG
- left STAR SLASH DIV MOD
- left PLUS MINUS
- left GT GTE LT LTE
- left EEQUAL NOTEQUAL
- left AND
- left OR
- right EQUAL
-preclow
-
-rule
- program
- #: statements { @interpreter.nodes = val[0].flatten }
- : statements { @interpreter.nodes = val[0] }
- |
- ;
-
-
- statements
- : statement terminator {
- result = [val[0]]
- result << val[1] unless val[1].nil?
- }
- | statements statement terminator {
- result = val[0] << val[1]
- result << val[2] unless val[2].nil?
- }
- ;
-
- block
- : NEWLINE statements { result = val[1] }
- ;
-
- optional_newline
- : NEWLINE
- |
- ;
-
- statement
- : comment
- | definition
- | namespace
- #| assignment
- | motion_statement
- #| jump
- #| io_method
- | label_definition
- | address
- | conditional
- | inline_conditional
- | forloop
- | while_loop
- #| program_call
- | use_statement
- | set_skip_statement
- | wait_statement
- | case_statement
- | fanuc_eval
- | timer_method
- | position_data
- | raise
- | tp_header_definition
- | empty_stmt
- | PAUSE { result = PauseNode.new }
- | ABORT { result = AbortNode.new }
- ;
-
- empty_stmt
- : NEWLINE { result = EmptyStmtNode.new() }
- ;
-
- tp_header_definition
- : TP_HEADER EQUAL tp_header_value { result = HeaderNode.new(val[0],val[2]) }
- ;
-
- tp_header_value
- : STRING
- | TRUE_FALSE
- ;
-
- raise
- : RAISE var_or_indirect { result = RaiseNode.new(val[1]) }
- ;
-
- timer_method
- : TIMER_METHOD var_or_indirect { result = TimerMethodNode.new(val[0],val[1]) }
- ;
-
- fanuc_eval
- : EVAL STRING { result = EvalNode.new(val[1]) }
- ;
-
- wait_statement
- : WAIT_FOR LPAREN indirectable COMMA STRING RPAREN
- { result = WaitForNode.new(val[2], val[4]) }
- | WAIT_UNTIL LPAREN expression RPAREN
- { result = WaitUntilNode.new(val[2], nil) }
- | WAIT_UNTIL LPAREN expression RPAREN DOT wait_modifier
- { result = WaitUntilNode.new(val[2],val[5]) }
- | WAIT_UNTIL LPAREN expression RPAREN DOT wait_modifier DOT wait_modifier
- { result = WaitUntilNode.new(val[2],val[5].merge(val[7])) }
- ;
-
- wait_modifier
- : timeout_modifier
- | after_modifier
- ;
-
- timeout_modifier
- : swallow_newlines TIMEOUT LPAREN label RPAREN
- { result = { label: val[3] } }
- ;
-
- after_modifier
- : swallow_newlines AFTER LPAREN indirectable COMMA STRING RPAREN
- { result = { timeout: [val[3],val[5]] } }
- ;
-
- label
- : LABEL { result = val[0] }
- ;
-
- use_statement
- : FANUC_USE indirectable { result = UseNode.new(val[0],val[1]) }
- ;
-
- # set_skip_condition x
- set_skip_statement
- : SET_SKIP_CONDITION expression { result = SetSkipNode.new(val[1]) }
- ;
-
- program_call
- : WORD LPAREN args RPAREN { result = CallNode.new(val[0],val[2]) }
- | RUN WORD LPAREN args RPAREN { result = CallNode.new(val[1],val[3],async: true) }
- ;
-
- args
- : arg { result = [val[0]] }
- | args COMMA arg { result = val[0] << val[2] }
- | { result = [] }
- ;
-
- arg
- : number
- | var
- | string
- | address
- ;
-
- string
- : STRING { result = StringNode.new(val[0]) }
- ;
-
- io_method
- : IO_METHOD var_or_indirect { result = IOMethodNode.new(val[0],val[1]) }
- | IO_METHOD LPAREN var_or_indirect RPAREN
- { result = IOMethodNode.new(val[0],val[2]) }
- | IO_METHOD LPAREN var_or_indirect COMMA number COMMA STRING RPAREN
- { result = IOMethodNode.new(val[0],val[2],{ pulse_time: val[4], pulse_units: val[6] }) }
- ;
-
- var_or_indirect
- : var
- | indirect_thing
- ;
-
-
- jump
- : JUMP label { result = JumpNode.new(val[1]) }
- ;
-
- conditional
- : IF expression block else_block END
- { result = ConditionalNode.new("if",val[1],val[2],val[3]) }
- | UNLESS expression block else_block END
- { result = ConditionalNode.new("unless",val[1],val[2],val[3]) }
- ;
-
- forloop
- : FOR var IN LPAREN minmax_val TO minmax_val RPAREN block END
- { result = ForNode.new(val[1],val[4],val[6],val[8]) }
- ;
-
- while_loop
- : WHILE expression block END { result = WhileNode.new(val[1],val[2]) }
- ;
-
- minmax_val
- : integer
- | var
- ;
-
- namespace
- : NAMESPACE WORD block END { result = NamespaceNode.new(val[1],val[2]) }
- ;
-
- case_statement
- : CASE var swallow_newlines
- case_conditions
- case_else
- END { result = CaseNode.new(val[1],val[3],val[4]) }
- ;
-
- case_conditions
- : case_condition { result = val }
- | case_conditions case_condition
- { result = val[0] << val[1] << val[2] }
- ;
-
- case_condition
- : WHEN case_allowed_condition swallow_newlines case_allowed_statement
- terminator { result = CaseConditionNode.new(val[1],val[3]) }
- ;
-
- case_allowed_condition
- : number
- | var
- ;
-
- case_else
- : ELSE swallow_newlines case_allowed_statement terminator
- { result = CaseConditionNode.new(nil,val[2]) }
- |
- ;
-
- case_allowed_statement
- : program_call
- | jump
- ;
-
- inline_conditional
- : inlineable
- | inlineable IF expression { result = InlineConditionalNode.new(val[1], val[2], val[0]) }
- | inlineable UNLESS expression { result = InlineConditionalNode.new(val[1], val[2], val[0]) }
- ;
-
- inlineable
- : jump
- | assignment
- | io_method
- | program_call
- ;
-
- else_block
- : ELSE block { result = val[1] }
- | { result = [] }
- ;
-
- motion_statement
- : MOVE DOT swallow_newlines TO LPAREN var RPAREN motion_modifiers
- { result = MotionNode.new(val[0],val[5],val[7]) }
- ;
-
- motion_modifiers
- : motion_modifier { result = val }
- | motion_modifiers motion_modifier
- { result = val[0] << val[1] }
- ;
-
- motion_modifier
- : DOT swallow_newlines AT LPAREN speed RPAREN
- { result = SpeedNode.new(val[4]) }
- | DOT swallow_newlines TERM LPAREN valid_terminations RPAREN
- { result = TerminationNode.new(val[4]) }
- | DOT swallow_newlines OFFSET LPAREN var RPAREN
- { result = OffsetNode.new(val[2],val[4]) }
- | DOT swallow_newlines TIME_SEGMENT LPAREN time COMMA time_seg_actions RPAREN
- { result = TimeNode.new(val[2],val[4],val[6]) }
- | DOT swallow_newlines SKIP LPAREN label optional_lpos_arg RPAREN
- { result = SkipNode.new(val[4],val[5]) }
- ;
-
- valid_terminations
- : integer
- | var
- | MINUS DIGIT {
- raise Racc::ParseError, sprintf("\ninvalid termination type: (%s)", val[1]) if val[1] != 1
-
- result = DigitNode.new(val[1].to_i * -1)
- }
- ;
-
- optional_lpos_arg
- : COMMA var { result = val[1] }
- |
- ;
-
- indirectable
- : number
- | var
- ;
-
- time_seg_actions
- : program_call
- | io_method
- ;
-
- time
- : var
- | number
- ;
-
- speed
- : indirectable COMMA STRING { result = { speed: val[0], units: val[2] } }
- | STRING { result = { speed: val[0], units: nil } }
- ;
-
- label_definition
- : label { result = LabelDefinitionNode.new(val[0]) }#@interpreter.add_label(val[1]) }
- ;
-
- definition
- : WORD ASSIGN definable { result = DefinitionNode.new(val[0],val[2]) }
- ;
-
- assignment
- : var_or_indirect EQUAL expression { result = AssignmentNode.new(val[0],val[2]) }
- | var_or_indirect PLUS EQUAL expression { result = AssignmentNode.new(
- val[0],
- ExpressionNode.new(val[0],"+",val[3])
- )
- }
- | var_or_indirect MINUS EQUAL expression { result = AssignmentNode.new(
- val[0],
- ExpressionNode.new(val[0],"-",val[3])
- )
- }
- ;
-
- var
- : var_without_namespaces
- | var_with_namespaces
- ;
-
- var_without_namespaces
- : WORD { result = VarNode.new(val[0]) }
- | WORD var_method_modifiers { result = VarMethodNode.new(val[0],val[1]) }
- ;
-
- var_with_namespaces
- : namespaces var_without_namespaces
- { result = NamespacedVarNode.new(val[0],val[1]) }
- ;
-
- var_method_modifiers
- : var_method_modifier { result = val[0] }
- | var_method_modifiers var_method_modifier
- { result = val[0].merge(val[1]) }
- ;
-
- var_method_modifier
- : DOT swallow_newlines WORD { result = { method: val[2] } }
- | DOT swallow_newlines GROUP LPAREN integer RPAREN
- { result = { group: val[4] } }
- ;
-
- namespaces
- : ns { result = [val[0]] }
- | namespaces ns { result = val[0] << val[1] }
- ;
-
- ns
- : WORD COLON COLON { result = val[0] }
- ;
-
-
- expression
- : unary_expression
- | binary_expression
- ;
-
- unary_expression
- : factor { result = val[0] }
- | address
- | BANG factor { result = ExpressionNode.new(val[1], "!", nil) }
- ;
-
- binary_expression
- : expression operator expression
- { result = ExpressionNode.new(val[0], val[1], val[2]) }
- ;
-
- operator
- : EEQUAL { result = "==" }
- | NOTEQUAL { result = "<>" }
- | LT { result = "<" }
- | GT { result = ">" }
- | GTE { result = ">=" }
- | LTE { result = "<=" }
- | PLUS { result = "+" }
- | MINUS { result = "-" }
- | OR { result = "||" }
- | STAR { result = "*" }
- | SLASH { result = "/" }
- | DIV { result = "DIV" }
- | MOD { result = "%" }
- | AND { result = "&&" }
- ;
-
- factor
- : number
- | signed_number
- | var
- | indirect_thing
- | paren_expr
- ;
-
- paren_expr
- : LPAREN expression RPAREN { result = ParenExpressionNode.new(val[1]) }
- ;
-
- indirect_thing
- : INDIRECT LPAREN STRING COMMA indirectable RPAREN
- { result = IndirectNode.new(val[2].to_sym, val[4]) }
- ;
-
- signed_number
- : sign DIGIT {
- val[1] = val[1].to_i * -1 if val[0] == "-"
- result = DigitNode.new(val[1])
- }
- | sign REAL { val[1] = val[1].to_f * -1 if val[0] == "-"; result = RealNode.new(val[1]) }
- ;
-
- sign
- : MINUS { result = "-" }
- ;
-
- number
- : integer
- | REAL { result = RealNode.new(val[0]) }
- ;
-
- integer
- : DIGIT { result = DigitNode.new(val[0]) }
- ;
-
- definable
- : numreg
- | output
- | input
- | posreg
- | position
- | vreg
- | number
- | signed_number
- | argument
- | timer
- | ualm
- | sreg
- ;
-
-
- sreg
- : SREG LBRACK DIGIT RBRACK { result = StringRegisterNode.new(val[2].to_i) }
- ;
-
- ualm
- : UALM LBRACK DIGIT RBRACK { result = UserAlarmNode.new(val[2].to_i) }
- ;
-
- timer
- : TIMER LBRACK DIGIT RBRACK { result = TimerNode.new(val[2].to_i) }
- ;
-
- argument
- : ARG LBRACK DIGIT RBRACK { result = ArgumentNode.new(val[2].to_i) }
- ;
-
- vreg
- : VREG LBRACK DIGIT RBRACK { result = VisionRegisterNode.new(val[2].to_i) }
- ;
-
- position
- : POSITION LBRACK DIGIT RBRACK { result = PositionNode.new(val[2].to_i) }
- ;
-
- numreg
- : NUMREG LBRACK DIGIT RBRACK { result = NumregNode.new(val[2].to_i) }
- ;
-
- posreg
- : POSREG LBRACK DIGIT RBRACK { result = PosregNode.new(val[2].to_i) }
- ;
-
- output
- : OUTPUT LBRACK DIGIT RBRACK { result = IONode.new(val[0], val[2].to_i) }
- ;
-
- input
- : INPUT LBRACK DIGIT RBRACK { result = IONode.new(val[0], val[2].to_i) }
- ;
-
- address
- : ADDRESS { result = AddressNode.new(val[0]) }
- ;
-
- comment
- : COMMENT { result = CommentNode.new(val[0]) }
- ;
-
- terminator
- : NEWLINE { result = TerminatorNode.new }
- | comment optional_newline { result = val[0] }
- # ^-- consume newlines or else we will get an extra space from EmptyStmt in the output
- | false
- |
- ;
-
- swallow_newlines
- : NEWLINE { result = TerminatorNode.new }
- |
- ;
-
- position_data
- : POSITION_DATA sn hash sn END
- { result = PositionDataNode.new(val[2]) }
- ;
-
- sn
- : swallow_newlines
- ;
-
- hash
- : LBRACE sn hash_attributes sn RBRACE { result = val[2] }
- | LBRACE sn RBRACE { result = {} }
- ;
-
- hash_attributes
- : hash_attribute { result = val[0] }
- | hash_attributes COMMA sn hash_attribute
- { result = val[0].merge(val[3]) }
- ;
-
- hash_attribute
- : STRING COLON hash_value { result = { val[0].to_sym => val[2] } }
- ;
-
- hash_value
- : STRING
- | hash
- | array
- | optional_sign DIGIT { val[1] = val[1].to_i * -1 if val[0] == "-"; result = val[1] }
- | optional_sign REAL { val[1] = val[1].to_f * -1 if val[0] == "-"; result = val[1] }
- | TRUE_FALSE { result = val[0] == "true" }
- ;
-
- optional_sign
- : sign
- |
- ;
-
- array
- : LBRACK sn array_values sn RBRACK { result = val[2] }
- ;
-
- array_values
- : array_value { result = val }
- | array_values COMMA sn array_value { result = val[0] << val[3] }
- ;
-
- array_value
- : hash_value
- ;
-
-
-end
-
----- inner
-
- include TPPlus::Nodes
-
- attr_reader :interpreter
- def initialize(scanner, interpreter = TPPlus::Interpreter.new)
- @scanner = scanner
- @interpreter = interpreter
- super()
- end
-
- def next_token
- t = @scanner.next_token
- @interpreter.line_count += 1 if t && t[0] == :NEWLINE
-
- #puts t.inspect
- t
- end
-
- def parse
- #@yydebug =true
-
- do_parse
- @interpreter
- end
-
- def on_error(t, val, vstack)
- raise ParseError, sprintf("Parse error on line #{@scanner.tok_line} column #{@scanner.tok_col}: %s (%s)",
- val.inspect, token_to_str(t) || '?')
- end
-
- class ParseError < StandardError ; end
diff --git a/test/racc/assets/twowaysql.y b/test/racc/assets/twowaysql.y
deleted file mode 100644
index d3bc748d3a..0000000000
--- a/test/racc/assets/twowaysql.y
+++ /dev/null
@@ -1,278 +0,0 @@
-# Copyright 2008-2015 Takuto Wada
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-class TwoWaySQL::Parser
-
-rule
-
-sql : stmt_list
- {
- result = RootNode.new( val[0] )
- }
-
-stmt_list :
- {
- result = []
- }
- | stmt_list stmt
- {
- result.push val[1]
- }
-
-stmt : primary
- | if_stmt
- | begin_stmt
-
-begin_stmt : BEGIN stmt_list END
- {
- result = BeginNode.new( val[1] )
- }
-
-if_stmt : IF sub_stmt else_stmt END
- {
- result = IfNode.new( val[0][1], val[1], val[2] )
- }
-
-else_stmt : ELSE sub_stmt
- {
- result = val[1]
- }
- |
- {
- result = nil
- }
-
-sub_stmt : and_stmt
- | or_stmt
- | stmt_list
-
-and_stmt : AND stmt_list
- {
- result = SubStatementNode.new( val[0][1], val[1] )
- }
-
-or_stmt : OR stmt_list
- {
- result = SubStatementNode.new( val[0][1], val[1] )
- }
-
-primary : IDENT
- {
- result = LiteralNode.new( val[0][1] )
- }
- | STRING_LITERAL
- {
- result = LiteralNode.new( val[0][1] )
- }
- | AND
- {
- result = LiteralNode.new( val[0][1] )
- }
- | OR
- {
- result = LiteralNode.new( val[0][1] )
- }
- | SPACES
- {
- result = WhiteSpaceNode.new( val[0][1], @preserve_space )
- }
- | COMMA
- {
- result = LiteralNode.new( val[0][1] )
- }
- | LPAREN
- {
- result = LiteralNode.new( val[0][1] )
- }
- | RPAREN
- {
- result = LiteralNode.new( val[0][1] )
- }
- | QUESTION
- {
- @num_questions += 1
- result = QuestionNode.new( @num_questions )
- }
- | ACTUAL_COMMENT
- {
- result = ActualCommentNode.new( val[0][1] , val[0][2] )
- }
- | bind_var
- | embed_var
-
-bind_var : BIND_VARIABLE STRING_LITERAL
- {
- result = BindVariableNode.new( val[0][1] )
- }
- | BIND_VARIABLE SPACES STRING_LITERAL
- {
- result = BindVariableNode.new( val[0][1] )
- }
- | BIND_VARIABLE IDENT
- {
- result = BindVariableNode.new( val[0][1] )
- }
- | BIND_VARIABLE SPACES IDENT
- {
- result = BindVariableNode.new( val[0][1] )
- }
- | PAREN_BIND_VARIABLE
- {
- result = ParenBindVariableNode.new( val[0][1] )
- }
-
-embed_var : EMBED_VARIABLE IDENT
- {
- result = EmbedVariableNode.new( val[0][1] )
- }
- | EMBED_VARIABLE SPACES IDENT
- {
- result = EmbedVariableNode.new( val[0][1] )
- }
-
-end
-
-
----- inner
-
-require 'strscan'
-
-def initialize(opts={})
- opts = {
- :debug => false,
- :preserve_space => true,
- :preserve_comment => false
- }.merge(opts)
- @yydebug = opts[:debug]
- @preserve_space = opts[:preserve_space]
- @preserve_comment = opts[:preserve_comment]
- @num_questions = 0
-end
-
-
-PAREN_EXAMPLE = '\([^\)]+\)'
-BEGIN_BIND_VARIABLE = '(\/|\#)\*([^\*]+)\*\1'
-BIND_VARIABLE_PATTERN = /\A#{BEGIN_BIND_VARIABLE}\s*/
-PAREN_BIND_VARIABLE_PATTERN = /\A#{BEGIN_BIND_VARIABLE}\s*#{PAREN_EXAMPLE}/
-EMBED_VARIABLE_PATTERN = /\A(\/|\#)\*\$([^\*]+)\*\1\s*/
-
-CONDITIONAL_PATTERN = /\A(\/|\#)\*(IF)\s+([^\*]+)\s*\*\1/
-BEGIN_END_PATTERN = /\A(\/|\#)\*(BEGIN|END)\s*\*\1/
-STRING_LITERAL_PATTERN = /\A(\'(?:[^\']+|\'\')*\')/ ## quoted string
-SPLIT_TOKEN_PATTERN = /\A(\S+?)(?=\s*(?:(?:\/|\#)\*|-{2,}|\(|\)|\,))/ ## stop on delimiters --,/*,#*,',',(,)
-LITERAL_PATTERN = /\A([^;\s]+)/
-SPACES_PATTERN = /\A(\s+)/
-QUESTION_PATTERN = /\A\?/
-COMMA_PATTERN = /\A\,/
-LPAREN_PATTERN = /\A\(/
-RPAREN_PATTERN = /\A\)/
-ACTUAL_COMMENT_PATTERN = /\A(\/|\#)\*(\s{1,}(?:.*?))\*\1/m ## start with spaces
-SEMICOLON_AT_INPUT_END_PATTERN = /\A\;\s*\Z/
-UNMATCHED_COMMENT_START_PATTERN = /\A(?:(?:\/|\#)\*)/
-
-#TODO: remove trailing spaces for S2Dao compatibility, but this spec sometimes causes SQL bugs...
-ELSE_PATTERN = /\A\-{2,}\s*ELSE\s*/
-AND_PATTERN = /\A(\ *AND)\b/i
-OR_PATTERN = /\A(\ *OR)\b/i
-
-
-def parse( io )
- @q = []
- io.each_line(nil) do |whole|
- @s = StringScanner.new(whole)
- end
- scan_str
-
- # @q.push [ false, nil ]
- @q.push [ false, [@s.pos, nil] ]
-
- ## call racc's private parse method
- do_parse
-end
-
-
-## called by racc
-def next_token
- @q.shift
-end
-
-
-def scan_str
- until @s.eos? do
- case
- when @s.scan(AND_PATTERN)
- @q.push [ :AND, [@s.pos, @s[1]] ]
- when @s.scan(OR_PATTERN)
- @q.push [ :OR, [@s.pos, @s[1]] ]
- when @s.scan(SPACES_PATTERN)
- @q.push [ :SPACES, [@s.pos, @s[1]] ]
- when @s.scan(QUESTION_PATTERN)
- @q.push [ :QUESTION, [@s.pos, nil] ]
- when @s.scan(COMMA_PATTERN)
- @q.push [ :COMMA, [@s.pos, ','] ]
- when @s.scan(LPAREN_PATTERN)
- @q.push [ :LPAREN, [@s.pos, '('] ]
- when @s.scan(RPAREN_PATTERN)
- @q.push [ :RPAREN, [@s.pos, ')'] ]
- when @s.scan(ELSE_PATTERN)
- @q.push [ :ELSE, [@s.pos, nil] ]
- when @s.scan(ACTUAL_COMMENT_PATTERN)
- @q.push [ :ACTUAL_COMMENT, [@s.pos, @s[1], @s[2]] ] if @preserve_comment
- when @s.scan(BEGIN_END_PATTERN)
- @q.push [ @s[2].intern, [@s.pos, nil] ]
- when @s.scan(CONDITIONAL_PATTERN)
- @q.push [ @s[2].intern, [@s.pos, @s[3]] ]
- when @s.scan(EMBED_VARIABLE_PATTERN)
- @q.push [ :EMBED_VARIABLE, [@s.pos, @s[2]] ]
- when @s.scan(PAREN_BIND_VARIABLE_PATTERN)
- @q.push [ :PAREN_BIND_VARIABLE, [@s.pos, @s[2]] ]
- when @s.scan(BIND_VARIABLE_PATTERN)
- @q.push [ :BIND_VARIABLE, [@s.pos, @s[2]] ]
- when @s.scan(STRING_LITERAL_PATTERN)
- @q.push [ :STRING_LITERAL, [@s.pos, @s[1]] ]
- when @s.scan(SPLIT_TOKEN_PATTERN)
- @q.push [ :IDENT, [@s.pos, @s[1]] ]
- when @s.scan(UNMATCHED_COMMENT_START_PATTERN) ## unmatched comment start, '/*','#*'
- raise Racc::ParseError, "unmatched comment. line:[#{line_no(@s.pos)}], str:[#{@s.rest}]"
- when @s.scan(LITERAL_PATTERN) ## other string token
- @q.push [ :IDENT, [@s.pos, @s[1]] ]
- when @s.scan(SEMICOLON_AT_INPUT_END_PATTERN)
- #drop semicolon at input end
- else
- raise Racc::ParseError, "syntax error at or near line:[#{line_no(@s.pos)}], str:[#{@s.rest}]"
- end
- end
-end
-
-
-## override racc's default on_error method
-def on_error(t, v, vstack)
- ## cursor in value-stack is an array of two items,
- ## that have position value as 0th item. like [731, "ctx[:limit] "]
- cursor = vstack.find do |tokens|
- tokens.size == 2 and tokens[0].kind_of?(Fixnum)
- end
- pos = cursor[0]
- line = line_no(pos)
- rest = @s.string[pos .. -1]
- raise Racc::ParseError, "syntax error at or near line:[#{line}], str:[#{rest}]"
-end
-
-
-def line_no(pos)
- lines = 0
- scanned = @s.string[0..(pos)]
- scanned.each_line { lines += 1 }
- lines
-end
diff --git a/test/racc/assets/unterm.y b/test/racc/assets/unterm.y
deleted file mode 100644
index 518acc7f31..0000000000
--- a/test/racc/assets/unterm.y
+++ /dev/null
@@ -1,5 +0,0 @@
-# unterminated action
-
-class A
-rule
- a: A {
diff --git a/test/racc/assets/useless.y b/test/racc/assets/useless.y
deleted file mode 100644
index 3f172e341c..0000000000
--- a/test/racc/assets/useless.y
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-class A
-token A B C X
-rule
-
-targ : A list B
- | A B C
-
-list: list X
-
-end
diff --git a/test/racc/assets/yyerr.y b/test/racc/assets/yyerr.y
deleted file mode 100644
index 9faae89a79..0000000000
--- a/test/racc/assets/yyerr.y
+++ /dev/null
@@ -1,46 +0,0 @@
-#
-# yyerror/yyerrok/yyaccept test
-#
-
-class A
-rule
-
-target: a b c
-
-a:
- {
- yyerror
- raise ArgumentError, "yyerror failed"
- }
- | error
-
-b:
- {
- yyerrok
- }
-
-c:
- {
- yyaccept
- raise ArgumentError, "yyaccept failed"
- }
-
-end
-
----- inner
-
- def parse
- do_parse
- end
-
- def next_token
- [false, '$end']
- end
-
- def on_error( *args )
- $stderr.puts "on_error called: args=#{args.inspect}"
- end
-
----- footer
-
-A.new.parse
diff --git a/test/racc/bench.y b/test/racc/bench.y
deleted file mode 100644
index c6ba136201..0000000000
--- a/test/racc/bench.y
+++ /dev/null
@@ -1,36 +0,0 @@
-class BenchmarkParser
-
-rule
-
- target: a a a a a a a a a a;
- a: b b b b b b b b b b;
- b: c c c c c c c c c c;
- c: d d d d d d d d d d;
- d: e e e e e e e e e e;
-
-end
-
----- inner
-
-def initialize
- @old = [ :e, 'e' ]
- @i = 0
-end
-
-def next_token
- return [false, '$'] if @i >= 10_0000
- @i += 1
- @old
-end
-
-def parse
- do_parse
-end
-
----- footer
-
-require 'benchmark'
-
-Benchmark.bm do |x|
- x.report { BenchmarkParser.new.parse }
-end
diff --git a/test/racc/case.rb b/test/racc/case.rb
deleted file mode 100644
index ebc30b8288..0000000000
--- a/test/racc/case.rb
+++ /dev/null
@@ -1,111 +0,0 @@
-verbose = $VERBOSE
-$VERBOSE = true
-begin
-
-require 'test/unit'
-require 'racc/static'
-require 'fileutils'
-require 'tempfile'
-require 'timeout'
-
-module Racc
- class TestCase < Test::Unit::TestCase
- PROJECT_DIR = File.expand_path(File.join(__dir__, '..'))
-
- test_dir = File.join(PROJECT_DIR, 'test')
- test_dir = File.join(PROJECT_DIR, 'racc') unless File.exist?(test_dir)
- TEST_DIR = test_dir
- racc = File.join(PROJECT_DIR, 'bin', 'racc')
- racc = File.join(PROJECT_DIR, '..', 'libexec', 'racc') unless File.exist?(racc)
- racc = 'racc' unless File.exist?(racc)
-
- RACC = racc
- ASSET_DIR = File.join(TEST_DIR, 'assets') # test grammars
- REGRESS_DIR = File.join(TEST_DIR, 'regress') # known-good generated outputs
-
- INC = [
- File.join(PROJECT_DIR, 'lib'),
- File.join(PROJECT_DIR, 'ext'),
- ].join(':')
-
- def setup
- @TEMP_DIR = Dir.mktmpdir("racc")
- @OUT_DIR = File.join(@TEMP_DIR, 'out')
- @TAB_DIR = File.join(@TEMP_DIR, 'tab') # generated parsers go here
- @LOG_DIR = File.join(@TEMP_DIR, 'log')
- @ERR_DIR = File.join(@TEMP_DIR, 'err')
- FileUtils.mkdir_p([@OUT_DIR, @TAB_DIR, @LOG_DIR, @ERR_DIR])
- FileUtils.cp File.join(TEST_DIR, "src.intp"), @TEMP_DIR
- end
-
- def teardown
- FileUtils.rm_f(File.join(@TEMP_DIR, "src.intp"))
- FileUtils.rm_rf([@OUT_DIR, @TAB_DIR, @LOG_DIR, @ERR_DIR, @TEMP_DIR])
- end
-
- def assert_compile(asset, args = [], **opt)
- file = File.basename(asset, '.y')
- args = ([args].flatten) + [
- "#{ASSET_DIR}/#{file}.y",
- '-Do',
- "-O#{@OUT_DIR}/#{file}",
- "-o#{@TAB_DIR}/#{file}",
- ]
- racc(*args, **opt)
- end
-
- def assert_debugfile(asset, ok)
- file = File.basename(asset, '.y')
- Dir.chdir(@LOG_DIR) do
- File.foreach("#{file}.y") do |line|
- line.strip!
- case line
- when /sr/ then assert_equal "sr#{ok[0]}", line
- when /rr/ then assert_equal "rr#{ok[1]}", line
- when /un/ then assert_equal "un#{ok[2]}", line
- when /ur/ then assert_equal "ur#{ok[3]}", line
- when /ex/ then assert_equal "ex#{ok[4]}", line
- else
- raise TestFailed, 'racc outputs unknown debug report???'
- end
- end
- end
- end
-
- def assert_exec(asset)
- lib_path = File.expand_path("../../lib", __FILE__)
- file = File.basename(asset, '.y')
- ruby "-I#{lib_path}", "#{@TAB_DIR}/#{file}"
- end
-
- def strip_version(source)
- source.sub(/This file is automatically generated by Racc \d+\.\d+\.\d+/, '')
- end
-
- def assert_output_unchanged(asset)
- file = File.basename(asset, '.y')
-
- # Code to re-generate the expectation files
- # File.write("#{REGRESS_DIR}/#{file}", File.read("#{@TAB_DIR}/#{file}"))
-
- expected = File.read("#{REGRESS_DIR}/#{file}")
- actual = File.read("#{@TAB_DIR}/#{file}")
- result = (strip_version(expected) == strip_version(actual))
-
- assert(result, proc {`diff -u #{REGRESS_DIR}/#{file} #{@TAB_DIR}/#{file}`})
- end
-
- def racc(*arg, **opt)
- lib_path = File.expand_path("../../lib", __FILE__)
- ruby "-I#{lib_path}", "-S", RACC, *arg, **opt
- end
-
- def ruby(*arg, **opt)
- assert_ruby_status(["-C", @TEMP_DIR, *arg], **opt)
- end
- end
-end
-
-ensure
-$VERBOSE = verbose
-end
diff --git a/test/racc/infini.y b/test/racc/infini.y
deleted file mode 100644
index 88b1e1e93b..0000000000
--- a/test/racc/infini.y
+++ /dev/null
@@ -1,8 +0,0 @@
-
-class I
-
-rule
-
-list: list X
-
-end
diff --git a/test/racc/regress/README.txt b/test/racc/regress/README.txt
deleted file mode 100644
index dcab73260d..0000000000
--- a/test/racc/regress/README.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-These files are "known-good" compiler output, generated from a stable version of
-Racc. Whenever Racc is refactored, or changes are made which should not affect the
-compiler output, running "rake test" checks that the compiler output is exactly
-the same as these files.
-
-If a change is made which *should* change the compiler output, these files will
-have to be regenerated from the source in test/assets, and the results committed.
diff --git a/test/racc/regress/cadenza b/test/racc/regress/cadenza
deleted file mode 100644
index ab8b5c7f59..0000000000
--- a/test/racc/regress/cadenza
+++ /dev/null
@@ -1,798 +0,0 @@
-#
-# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.5.2
-# from Racc grammar file "".
-#
-
-require 'racc/parser.rb'
-
-# racc_parser.rb : generated by racc
-
-module Cadenza
- class RaccParser < Racc::Parser
-
-module_eval(<<'...end cadenza.y/module_eval...', 'cadenza.y', 171)
-
-...end cadenza.y/module_eval...
-##### State transition tables begin ###
-
-racc_action_table = [
- 37, 89, 65, 66, 20, 21, 22, 23, 24, 17,
- 106, 37, 3, 76, 4, 3, 75, 4, 50, 37,
- 29, 68, 65, 66, 33, 67, 34, 110, 9, 74,
- 35, 9, 37, 36, 71, 33, 38, 34, 77, 78,
- 79, 35, 72, 33, 36, 34, 37, 38, 3, 35,
- 39, 83, 36, 54, 55, 38, 33, 89, 34, 37,
- 90, 3, 35, 43, 9, 36, 85, 103, 38, 108,
- 33, 109, 34, 56, 57, 111, 35, 9, 3, 36,
- 46, 122, 38, 33, 112, 34, 113, 78, 79, 35,
- 114, 3, 36, 4, 9, 38, 20, 21, 22, 23,
- 24, 20, 21, 22, 23, 24, 115, 9, 65, 66,
- 65, 66, 29, 54, 55, 120, 107, 29, 20, 21,
- 22, 23, 24, 20, 21, 22, 23, 24, 20, 21,
- 22, 23, 24, 3, 29, 39, 3, 121, 4, 29,
- 3, 3, 43, 46, 29, 3, 124, 87, 125, 9,
- 54, 55, 9, 56, 57, 128, 9, 9, 3, 103,
- 116, 9, 20, 21, 22, 23, 24, 20, 21, 22,
- 23, 24, 131, 3, 9, 116, 56, 57, 29, 3,
- 89, 116, nil, 29, 20, 21, 22, 23, 24, 9,
- 20, 21, 22, 23, 24, 9, 56, 57, 56, 57,
- 29, 20, 21, 22, 23, 24, 29, 58, 59, 60,
- 61, 62, 63, 56, 57, 56, 57, 29, 58, 59,
- 60, 61, 62, 63, 20, 21, 22, 23, 24, 20,
- 21, 22, 23, 24, 20, 21, 22, 23, 24, 20,
- 21, 22, 23, 24, 20, 21, 22, 23, 24, 20,
- 21, 22, 23, 24, 20, 21, 22, 23, 24, 20,
- 21, 22, 23, 24, 20, 21, 22, 23, 24, 20,
- 21, 22, 23, 24, 20, 21, 22, 23, 24, 65,
- 66 ]
-
-racc_action_check = [
- 4, 73, 69, 69, 37, 37, 37, 37, 37, 1,
- 69, 39, 0, 38, 0, 2, 38, 2, 17, 43,
- 37, 32, 31, 31, 4, 31, 4, 73, 0, 37,
- 4, 2, 46, 4, 35, 39, 4, 39, 39, 39,
- 39, 39, 36, 43, 39, 43, 87, 39, 5, 43,
- 5, 43, 43, 26, 26, 43, 46, 52, 46, 116,
- 53, 6, 46, 6, 5, 46, 46, 67, 46, 71,
- 87, 72, 87, 27, 27, 75, 87, 6, 7, 87,
- 7, 87, 87, 116, 76, 116, 77, 116, 116, 116,
- 78, 8, 116, 8, 7, 116, 3, 3, 3, 3,
- 3, 20, 20, 20, 20, 20, 79, 8, 70, 70,
- 51, 51, 3, 93, 93, 83, 70, 20, 24, 24,
- 24, 24, 24, 33, 33, 33, 33, 33, 34, 34,
- 34, 34, 34, 41, 24, 41, 42, 85, 42, 33,
- 45, 48, 45, 48, 34, 49, 103, 49, 105, 41,
- 94, 94, 42, 95, 95, 122, 45, 48, 81, 125,
- 81, 49, 65, 65, 65, 65, 65, 66, 66, 66,
- 66, 66, 126, 82, 81, 82, 96, 96, 65, 118,
- 129, 118, nil, 66, 89, 89, 89, 89, 89, 82,
- 108, 108, 108, 108, 108, 118, 97, 97, 98, 98,
- 89, 124, 124, 124, 124, 124, 108, 28, 28, 28,
- 28, 28, 28, 99, 99, 100, 100, 124, 64, 64,
- 64, 64, 64, 64, 29, 29, 29, 29, 29, 54,
- 54, 54, 54, 54, 55, 55, 55, 55, 55, 56,
- 56, 56, 56, 56, 57, 57, 57, 57, 57, 58,
- 58, 58, 58, 58, 59, 59, 59, 59, 59, 60,
- 60, 60, 60, 60, 61, 61, 61, 61, 61, 62,
- 62, 62, 62, 62, 63, 63, 63, 63, 63, 123,
- 123 ]
-
-racc_action_pointer = [
- -12, 9, -9, 93, -3, 24, 37, 54, 67, nil,
- nil, nil, nil, nil, nil, nil, nil, 18, nil, nil,
- 98, nil, nil, nil, 115, nil, 44, 62, 194, 221,
- nil, 2, -4, 120, 125, 31, 39, 1, 10, 8,
- nil, 109, 112, 16, nil, 116, 29, nil, 117, 121,
- nil, 90, 55, 52, 226, 231, 236, 241, 246, 251,
- 256, 261, 266, 271, 205, 159, 164, 64, nil, -18,
- 88, 35, 43, -1, nil, 47, 56, 58, 62, 78,
- nil, 134, 149, 87, nil, 109, nil, 43, nil, 181,
- nil, nil, nil, 104, 141, 142, 165, 185, 187, 202,
- 204, nil, nil, 124, nil, 125, nil, nil, 187, nil,
- nil, nil, nil, nil, nil, nil, 56, nil, 155, nil,
- nil, nil, 127, 259, 198, 156, 144, nil, nil, 178,
- nil, nil ]
-
-racc_action_default = [
- -2, -70, -1, -70, -70, -70, -70, -70, -70, -60,
- -61, -62, -63, -64, -65, -66, -68, -70, -67, -69,
- -5, -7, -8, -9, -70, -11, -14, -17, -24, -70,
- -26, -33, -70, -70, -70, -70, -70, -70, -70, -70,
- -41, -70, -70, -70, -48, -70, -70, -52, -70, -70,
- 132, -3, -6, -70, -70, -70, -70, -70, -70, -70,
- -70, -70, -70, -70, -25, -70, -70, -70, -35, -70,
- -70, -70, -70, -70, -54, -70, -70, -70, -70, -70,
- -42, -70, -70, -70, -49, -70, -53, -70, -57, -70,
- -10, -12, -13, -15, -16, -18, -19, -20, -21, -22,
- -23, -27, -28, -29, -31, -34, -36, -37, -70, -50,
- -55, -58, -59, -38, -39, -40, -70, -44, -70, -43,
- -47, -51, -70, -4, -70, -70, -70, -45, -56, -30,
- -32, -46 ]
-
-racc_goto_table = [
- 19, 40, 18, 32, 104, 52, 51, 1, 2, 64,
- 47, 91, 92, 41, 45, 48, 49, 44, 42, 69,
- 70, 105, 73, 51, 53, 95, 96, 97, 98, 99,
- 100, 93, 94, 101, 102, 88, nil, 80, nil, 19,
- nil, 18, nil, 19, nil, 18, 19, 19, 18, 18,
- 82, 86, nil, nil, 81, nil, 84, nil, nil, nil,
- nil, nil, 130, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 123, nil, 117, 119, nil,
- 19, nil, 18, nil, nil, nil, nil, nil, nil, 118,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 126, 129,
- 51, nil, nil, nil, 127, nil, 19, nil, 18 ]
-
-racc_goto_check = [
- 27, 16, 28, 6, 11, 3, 4, 1, 2, 9,
- 22, 5, 5, 2, 2, 2, 2, 19, 15, 4,
- 4, 12, 3, 4, 6, 8, 8, 8, 8, 8,
- 8, 7, 7, 10, 10, 25, nil, 16, nil, 27,
- nil, 28, nil, 27, nil, 28, 27, 27, 28, 28,
- 2, 22, nil, nil, 15, nil, 19, nil, nil, nil,
- nil, nil, 11, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 4, nil, 16, 16, nil,
- 27, nil, 28, nil, nil, nil, nil, nil, nil, 2,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 6, 3,
- 4, nil, nil, nil, 16, nil, 27, nil, 28 ]
-
-racc_goto_pointer = [
- nil, 7, 8, -15, -14, -43, 0, -25, -33, -20,
- -32, -63, -46, nil, nil, 13, -4, nil, nil, 11,
- nil, nil, 3, nil, nil, -14, nil, -2, 0 ]
-
-racc_goto_default = [
- nil, nil, nil, nil, 31, 25, nil, 26, 27, 28,
- 30, nil, nil, 10, 5, nil, nil, 11, 6, nil,
- 12, 7, nil, 14, 8, nil, 13, 16, 15 ]
-
-racc_reduce_table = [
- 0, 0, :racc_error,
- 1, 42, :_reduce_none,
- 0, 42, :_reduce_2,
- 1, 44, :_reduce_3,
- 3, 44, :_reduce_4,
- 1, 46, :_reduce_5,
- 2, 46, :_reduce_6,
- 1, 46, :_reduce_7,
- 1, 46, :_reduce_8,
- 1, 46, :_reduce_9,
- 3, 46, :_reduce_10,
- 1, 48, :_reduce_none,
- 3, 48, :_reduce_12,
- 3, 48, :_reduce_13,
- 1, 49, :_reduce_none,
- 3, 49, :_reduce_15,
- 3, 49, :_reduce_16,
- 1, 50, :_reduce_none,
- 3, 50, :_reduce_18,
- 3, 50, :_reduce_19,
- 3, 50, :_reduce_20,
- 3, 50, :_reduce_21,
- 3, 50, :_reduce_22,
- 3, 50, :_reduce_23,
- 1, 51, :_reduce_none,
- 2, 51, :_reduce_25,
- 1, 45, :_reduce_none,
- 3, 45, :_reduce_27,
- 3, 45, :_reduce_28,
- 1, 52, :_reduce_29,
- 3, 52, :_reduce_30,
- 1, 53, :_reduce_31,
- 3, 53, :_reduce_32,
- 1, 47, :_reduce_none,
- 3, 47, :_reduce_34,
- 3, 54, :_reduce_35,
- 4, 55, :_reduce_36,
- 4, 55, :_reduce_37,
- 3, 56, :_reduce_38,
- 3, 57, :_reduce_39,
- 3, 57, :_reduce_40,
- 2, 58, :_reduce_41,
- 3, 58, :_reduce_42,
- 4, 58, :_reduce_43,
- 4, 58, :_reduce_44,
- 5, 58, :_reduce_45,
- 6, 59, :_reduce_46,
- 3, 60, :_reduce_47,
- 2, 61, :_reduce_48,
- 3, 61, :_reduce_49,
- 4, 62, :_reduce_50,
- 3, 63, :_reduce_51,
- 2, 64, :_reduce_52,
- 3, 64, :_reduce_53,
- 3, 65, :_reduce_54,
- 4, 65, :_reduce_55,
- 3, 66, :_reduce_56,
- 3, 67, :_reduce_57,
- 4, 68, :_reduce_58,
- 4, 68, :_reduce_59,
- 1, 69, :_reduce_60,
- 1, 69, :_reduce_none,
- 1, 69, :_reduce_none,
- 1, 69, :_reduce_none,
- 1, 69, :_reduce_none,
- 1, 69, :_reduce_none,
- 1, 43, :_reduce_66,
- 2, 43, :_reduce_67,
- 1, 43, :_reduce_68,
- 2, 43, :_reduce_69 ]
-
-racc_reduce_n = 70
-
-racc_shift_n = 132
-
-racc_token_table = {
- false => 0,
- :error => 1,
- "," => 2,
- :IDENTIFIER => 3,
- :INTEGER => 4,
- :REAL => 5,
- :STRING => 6,
- "(" => 7,
- ")" => 8,
- "*" => 9,
- "/" => 10,
- "+" => 11,
- "-" => 12,
- :OP_EQ => 13,
- :OP_NEQ => 14,
- :OP_LEQ => 15,
- :OP_GEQ => 16,
- ">" => 17,
- "<" => 18,
- :NOT => 19,
- :AND => 20,
- :OR => 21,
- ":" => 22,
- "|" => 23,
- :VAR_OPEN => 24,
- :VAR_CLOSE => 25,
- :STMT_OPEN => 26,
- :IF => 27,
- :STMT_CLOSE => 28,
- :UNLESS => 29,
- :ELSE => 30,
- :ENDIF => 31,
- :ENDUNLESS => 32,
- :FOR => 33,
- :IN => 34,
- :ENDFOR => 35,
- :BLOCK => 36,
- :ENDBLOCK => 37,
- :END => 38,
- :EXTENDS => 39,
- :TEXT_BLOCK => 40 }
-
-racc_nt_base = 41
-
-racc_use_result_var = true
-
-Racc_arg = [
- racc_action_table,
- racc_action_check,
- racc_action_default,
- racc_action_pointer,
- racc_goto_table,
- racc_goto_check,
- racc_goto_default,
- racc_goto_pointer,
- racc_nt_base,
- racc_reduce_table,
- racc_token_table,
- racc_shift_n,
- racc_reduce_n,
- racc_use_result_var ]
-Ractor.make_shareable(Racc_arg) if defined?(Ractor)
-
-Racc_token_to_s_table = [
- "$end",
- "error",
- "\",\"",
- "IDENTIFIER",
- "INTEGER",
- "REAL",
- "STRING",
- "\"(\"",
- "\")\"",
- "\"*\"",
- "\"/\"",
- "\"+\"",
- "\"-\"",
- "OP_EQ",
- "OP_NEQ",
- "OP_LEQ",
- "OP_GEQ",
- "\">\"",
- "\"<\"",
- "NOT",
- "AND",
- "OR",
- "\":\"",
- "\"|\"",
- "VAR_OPEN",
- "VAR_CLOSE",
- "STMT_OPEN",
- "IF",
- "STMT_CLOSE",
- "UNLESS",
- "ELSE",
- "ENDIF",
- "ENDUNLESS",
- "FOR",
- "IN",
- "ENDFOR",
- "BLOCK",
- "ENDBLOCK",
- "END",
- "EXTENDS",
- "TEXT_BLOCK",
- "$start",
- "target",
- "document",
- "parameter_list",
- "logical_expression",
- "primary_expression",
- "filtered_expression",
- "multiplicative_expression",
- "additive_expression",
- "boolean_expression",
- "inverse_expression",
- "filter",
- "filter_list",
- "inject_statement",
- "if_tag",
- "else_tag",
- "end_if_tag",
- "if_block",
- "for_tag",
- "end_for_tag",
- "for_block",
- "block_tag",
- "end_block_tag",
- "block_block",
- "generic_block_tag",
- "end_generic_block_tag",
- "generic_block",
- "extends_statement",
- "document_component" ]
-Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)
-
-Racc_debug_parser = false
-
-##### State transition tables end #####
-
-# reduce 0 omitted
-
-# reduce 1 omitted
-
-module_eval(<<'.,.,', 'cadenza.y', 12)
- def _reduce_2(val, _values, result)
- result = nil
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 16)
- def _reduce_3(val, _values, result)
- result = [val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 17)
- def _reduce_4(val, _values, result)
- result = val[0].push(val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 22)
- def _reduce_5(val, _values, result)
- result = VariableNode.new(val[0].value)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 23)
- def _reduce_6(val, _values, result)
- result = VariableNode.new(val[0].value, val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 24)
- def _reduce_7(val, _values, result)
- result = ConstantNode.new(val[0].value)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 25)
- def _reduce_8(val, _values, result)
- result = ConstantNode.new(val[0].value)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 26)
- def _reduce_9(val, _values, result)
- result = ConstantNode.new(val[0].value)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 27)
- def _reduce_10(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-# reduce 11 omitted
-
-module_eval(<<'.,.,', 'cadenza.y', 32)
- def _reduce_12(val, _values, result)
- result = OperationNode.new(val[0], "*", val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 33)
- def _reduce_13(val, _values, result)
- result = OperationNode.new(val[0], "/", val[2])
- result
- end
-.,.,
-
-# reduce 14 omitted
-
-module_eval(<<'.,.,', 'cadenza.y', 38)
- def _reduce_15(val, _values, result)
- result = OperationNode.new(val[0], "+", val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 39)
- def _reduce_16(val, _values, result)
- result = OperationNode.new(val[0], "-", val[2])
- result
- end
-.,.,
-
-# reduce 17 omitted
-
-module_eval(<<'.,.,', 'cadenza.y', 44)
- def _reduce_18(val, _values, result)
- result = OperationNode.new(val[0], "==", val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 45)
- def _reduce_19(val, _values, result)
- result = OperationNode.new(val[0], "!=", val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 46)
- def _reduce_20(val, _values, result)
- result = OperationNode.new(val[0], "<=", val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 47)
- def _reduce_21(val, _values, result)
- result = OperationNode.new(val[0], ">=", val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 48)
- def _reduce_22(val, _values, result)
- result = OperationNode.new(val[0], ">", val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 49)
- def _reduce_23(val, _values, result)
- result = OperationNode.new(val[0], "<", val[2])
- result
- end
-.,.,
-
-# reduce 24 omitted
-
-module_eval(<<'.,.,', 'cadenza.y', 54)
- def _reduce_25(val, _values, result)
- result = BooleanInverseNode.new(val[1])
- result
- end
-.,.,
-
-# reduce 26 omitted
-
-module_eval(<<'.,.,', 'cadenza.y', 59)
- def _reduce_27(val, _values, result)
- result = OperationNode.new(val[0], "and", val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 60)
- def _reduce_28(val, _values, result)
- result = OperationNode.new(val[0], "or", val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 64)
- def _reduce_29(val, _values, result)
- result = FilterNode.new(val[0].value)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 65)
- def _reduce_30(val, _values, result)
- result = FilterNode.new(val[0].value, val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 69)
- def _reduce_31(val, _values, result)
- result = [val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 70)
- def _reduce_32(val, _values, result)
- result = val[0].push(val[2])
- result
- end
-.,.,
-
-# reduce 33 omitted
-
-module_eval(<<'.,.,', 'cadenza.y', 75)
- def _reduce_34(val, _values, result)
- result = FilteredValueNode.new(val[0], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 79)
- def _reduce_35(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 83)
- def _reduce_36(val, _values, result)
- open_scope!; result = val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 84)
- def _reduce_37(val, _values, result)
- open_scope!; result = BooleanInverseNode.new(val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 88)
- def _reduce_38(val, _values, result)
- result = close_scope!; open_scope!
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 92)
- def _reduce_39(val, _values, result)
- result = close_scope!
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 93)
- def _reduce_40(val, _values, result)
- result = close_scope!
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 97)
- def _reduce_41(val, _values, result)
- result = IfNode.new(val[0], val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 98)
- def _reduce_42(val, _values, result)
- result = IfNode.new(val[0], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 99)
- def _reduce_43(val, _values, result)
- result = IfNode.new(val[0], val[1], val[3])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 100)
- def _reduce_44(val, _values, result)
- result = IfNode.new(val[0], val[2], val[3])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 101)
- def _reduce_45(val, _values, result)
- result = IfNode.new(val[0], val[2], val[4])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 105)
- def _reduce_46(val, _values, result)
- open_scope!; result = [val[2].value, val[4]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 109)
- def _reduce_47(val, _values, result)
- result = close_scope!
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 114)
- def _reduce_48(val, _values, result)
- result = ForNode.new(VariableNode.new(val[0].first), val[0].last, val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 115)
- def _reduce_49(val, _values, result)
- result = ForNode.new(VariableNode.new(val[0].first), val[0].last, val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 119)
- def _reduce_50(val, _values, result)
- result = open_block_scope!(val[2].value)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 123)
- def _reduce_51(val, _values, result)
- result = close_block_scope!
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 128)
- def _reduce_52(val, _values, result)
- result = BlockNode.new(val[0], val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 129)
- def _reduce_53(val, _values, result)
- result = BlockNode.new(val[0], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 133)
- def _reduce_54(val, _values, result)
- open_scope!; result = [val[1].value, []]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 134)
- def _reduce_55(val, _values, result)
- open_scope!; result = [val[1].value, val[2]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 138)
- def _reduce_56(val, _values, result)
- result = close_scope!
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 142)
- def _reduce_57(val, _values, result)
- result = GenericBlockNode.new(val[0].first, val[2], val[0].last)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 146)
- def _reduce_58(val, _values, result)
- result = val[2].value
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 147)
- def _reduce_59(val, _values, result)
- result = VariableNode.new(val[2].value)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 151)
- def _reduce_60(val, _values, result)
- result = TextNode.new(val[0].value)
- result
- end
-.,.,
-
-# reduce 61 omitted
-
-# reduce 62 omitted
-
-# reduce 63 omitted
-
-# reduce 64 omitted
-
-# reduce 65 omitted
-
-module_eval(<<'.,.,', 'cadenza.y', 160)
- def _reduce_66(val, _values, result)
- push val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 161)
- def _reduce_67(val, _values, result)
- push val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 162)
- def _reduce_68(val, _values, result)
- document.extends = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cadenza.y', 163)
- def _reduce_69(val, _values, result)
- document.extends = val[1]
- result
- end
-.,.,
-
-def _reduce_none(val, _values, result)
- val[0]
-end
-
- end # class RaccParser
-end # module Cadenza
diff --git a/test/racc/regress/cast b/test/racc/regress/cast
deleted file mode 100644
index 58f1818cf7..0000000000
--- a/test/racc/regress/cast
+++ /dev/null
@@ -1,3947 +0,0 @@
-#
-# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.5.2
-# from Racc grammar file "".
-#
-
-require 'racc/parser.rb'
-
-
-require 'set'
-
-# Error classes
-module C
- class ParseError < StandardError; end
-end
-
-# Local variables:
-# mode: ruby
-# end:
-module C
- class Parser < Racc::Parser
-
-module_eval(<<'...end cast.y/module_eval...', 'cast.y', 564)
- # A.1.9 -- Preprocessing numbers -- skip
- # A.1.8 -- Header names -- skip
-
- # A.1.7 -- Puncuators -- we don't bother with {##,#,%:,%:%:} since
- # we don't do preprocessing
- @@punctuators = %r'\+\+|-[->]|&&|\|\||\.\.\.|(?:<<|>>|[<>=!*/%+\-&^|])=?|[\[\](){}.~?:;,]'
- @@digraphs = %r'<[:%]|[:%]>'
-
- # A.1.6 -- String Literals -- simple for us because we don't decode
- # the string (and indeed accept some illegal strings)
- @@string_literal = %r'L?"(?:[^\\]|\\.)*?"'m
-
- # A.1.5 -- Constants
- @@decimal_floating_constant = %r'(?:(?:\d*\.\d+|\d+\.)(?:e[-+]?\d+)?|\d+e[-+]?\d+)[fl]?'i
- @@hexadecimal_floating_constant = %r'0x(?:(?:[0-9a-f]*\.[0-9a-f]+|[0-9a-f]+\.)|[0-9a-f]+)p[-+]?\d+[fl]?'i
-
- @@integer_constant = %r'(?:[1-9][0-9]*|0x[0-9a-f]+|0[0-7]*)(?:ul?l?|ll?u?)?'i
- @@floating_constant = %r'#{@@decimal_floating_constant}|#{@@hexadecimal_floating_constant}'
- @@enumeration_constant = %r'[a-zA-Z_\\][a-zA-Z_\\0-9]*'
- @@character_constant = %r"L?'(?:[^\\]|\\.)+?'"
- # (note that as with string-literals, we accept some illegal
- # character-constants)
-
- # A.1.4 -- Universal character names -- skip
-
- # A.1.3 -- Identifiers -- skip, since an identifier is lexically
- # identical to an enumeration constant
-
- # A.1.2 Keywords
- keywords = %w'auto break case char const continue default do
-double else enum extern float for goto if inline int long register
-restrict return short signed sizeof static struct switch typedef union
- unsigned void volatile while _Bool _Complex _Imaginary'
- @@keywords = %r"#{keywords.join('|')}"
-
- def initialize
- @type_names = ::Set.new
-
- @warning_proc = lambda{}
- @pos = C::Node::Pos.new(nil, 1, 0)
- end
- def initialize_copy(x)
- @pos = x.pos.dup
- @type_names = x.type_names.dup
- end
- attr_accessor :pos, :type_names
-
- def parse(str)
- if str.respond_to? :read
- str = str.read
- end
- @str = str
- begin
- prepare_lexer(str)
- return do_parse
- rescue ParseError => e
- e.set_backtrace(caller)
- raise
- end
- end
-
- #
- # Error handler, as used by racc.
- #
- def on_error(error_token_id, error_value, value_stack)
- if error_value == '$'
- parse_error @pos, "unexpected EOF"
- else
- parse_error(error_value.pos,
- "parse error on #{token_to_str(error_token_id)} (#{error_value.val})")
- end
- end
-
- def self.feature(name)
- attr_writer "#{name}_enabled"
- class_eval <<~RUBY, __FILE__, __LINE__ + 1
- def enable_#{name}
- @#{name}_enabled = true
- end
- def #{name}_enabled?
- @#{name}_enabled
- end
- RUBY
- end
- private_class_method :feature
-
- #
- # Allow blocks in parentheses as expressions, as per the gcc
- # extension. [http://rubyurl.com/iB7]
- #
- feature :block_expressions
-
- private # ---------------------------------------------------------
-
- class Token
- attr_accessor :pos, :val
- def initialize(pos, val)
- @pos = pos
- @val = val
- end
- end
- def eat(str)
- lines = str.split(/\r\n|[\r\n]/, -1)
- if lines.length == 1
- @pos.col_num += lines[0].length
- else
- @pos.line_num += lines.length - 1
- @pos.col_num = lines[-1].length
- end
- end
-
- #
- # Make a Declaration from the given specs and declarators.
- #
- def make_declaration(pos, specs, declarators)
- specs.all?{|x| x.is_a?(Symbol) || x.is_a?(Type)} or raise specs.map{|x| x.class}.inspect
- decl = Declaration.new_at(pos, nil, declarators)
-
- # set storage class
- storage_classes = specs.find_all do |x|
- [:typedef, :extern, :static, :auto, :register].include? x
- end
- # 6.7.1p2: at most, one storage-class specifier may be given in
- # the declaration specifiers in a declaration
- storage_classes.length <= 1 or
- begin
- if declarators.length == 0
- for_name = ''
- else
- for_name = "for `#{declarators[0].name}'"
- end
- parse_error pos, "multiple or duplicate storage classes given #{for_name}'"
- end
- decl.storage = storage_classes[0]
-
- # set type (specifiers, qualifiers)
- decl.type = make_direct_type(pos, specs)
-
- # set function specifiers
- decl.inline = specs.include?(:inline)
-
- # look for new type names
- if decl.typedef?
- decl.declarators.each do |d|
- if d.name
- @type_names << d.name
- end
- end
- end
-
- return decl
- end
-
- def make_function_def(pos, specs, func_declarator, decl_list, defn)
- add_decl_type(func_declarator, make_direct_type(pos, specs))
-
- # get types from decl_list if necessary
- function = func_declarator.indirect_type
- function.is_a? Function or
- parse_error pos, "non function type for function `#{func_declarator.name}'"
- params = function.params
- if decl_list
- params.all?{|p| p.type.nil?} or
- parse_error pos, "both prototype and declaration list given for `#{func_declarator.name}'"
- decl_list.each do |declaration|
- declaration.declarators.each do |declarator|
- param = params.find{|p| p.name == declarator.name} or
- parse_error pos, "no parameter named #{declarator.name}"
- if declarator.indirect_type
- param.type = declarator.indirect_type
- param.type.direct_type = declaration.type.dup
- else
- param.type = declaration.type.dup
- end
- end
- end
- params.all?{|p| p.type} or
- begin
- s = params.find_all{|p| p.type.nil?}.map{|p| "`#{p.name}'"}.join(' and ')
- parse_error pos, "types missing for parameters #{s}"
- end
- end
-
- fd = FunctionDef.new_at(pos,
- function.detach,
- func_declarator.name,
- defn,
- :no_prototype => !decl_list.nil?)
-
- # set storage class
- # 6.9.1p4: only extern or static allowed
- specs.each do |s|
- [:typedef, :auto, :register].include?(s) and
- "`#{s}' illegal for function"
- end
- storage_classes = specs.find_all do |s|
- s == :extern || s == :static
- end
- # 6.7.1p2: at most, one storage-class specifier may be given in
- # the declaration specifiers in a declaration
- storage_classes.length <= 1 or
- "multiple or duplicate storage classes given for `#{func_declarator.name}'"
- fd.storage = storage_classes[0] if storage_classes[0]
-
- # set function specifiers
- # 6.7.4p5 'inline' can be repeated
- fd.inline = specs.include?(:inline)
-
- return fd
- end
-
- #
- # Make a direct type from the list of type specifiers and type
- # qualifiers.
- #
- def make_direct_type(pos, specs)
- specs_order = [:signed, :unsigned, :short, :long, :double, :void,
- :char, :int, :float, :_Bool, :_Complex, :_Imaginary]
-
- type_specs = specs.find_all do |x|
- specs_order.include?(x) || !x.is_a?(Symbol)
- end
- type_specs.sort! do |a, b|
- (specs_order.index(a)||100) <=> (specs_order.index(b)||100)
- end
-
- # set type specifiers
- # 6.7.2p2: the specifier list should be one of these
- type =
- case type_specs
- when [:void]
- Void.new
- when [:char]
- Char.new
- when [:signed, :char]
- Char.new :signed => true
- when [:unsigned, :char]
- Char.new :signed => false
- when [:short], [:signed, :short], [:short, :int],
- [:signed, :short, :int]
- Int.new :longness => -1
- when [:unsigned, :short], [:unsigned, :short, :int]
- Int.new :unsigned => true, :longness => -1
- when [:int], [:signed], [:signed, :int]
- Int.new
- when [:unsigned], [:unsigned, :int]
- Int.new :unsigned => true
- when [:long], [:signed, :long], [:long, :int],
- [:signed, :long, :int]
- Int.new :longness => 1
- when [:unsigned, :long], [:unsigned, :long, :int]
- Int.new :longness => 1, :unsigned => true
- when [:long, :long], [:signed, :long, :long],
- [:long, :long, :int], [:signed, :long, :long, :int]
- Int.new :longness => 2
- when [:unsigned, :long, :long], [:unsigned, :long, :long, :int]
- Int.new :longness => 2, :unsigned => true
- when [:float]
- Float.new
- when [:double]
- Float.new :longness => 1
- when [:long, :double]
- Float.new :longness => 2
- when [:_Bool]
- Bool.new
- when [:float, :_Complex]
- Complex.new
- when [:double, :_Complex]
- Complex.new :longness => 1
- when [:long, :double, :_Complex]
- Complex.new :longness => 2
- when [:float, :_Imaginary]
- Imaginary.new
- when [:double, :_Imaginary]
- Imaginary.new :longness => 1
- when [:long, :double, :_Imaginary]
- Imaginary.new :longness => 2
- else
- if type_specs.length == 1 &&
- [CustomType, Struct, Union, Enum].any?{|c| type_specs[0].is_a? c}
- type_specs[0]
- else
- if type_specs == []
- parse_error pos, "no type specifiers given"
- else
- parse_error pos, "invalid type specifier combination: #{type_specs.join(' ')}"
- end
- end
- end
- type.pos ||= pos
-
- # set type qualifiers
- # 6.7.3p4: type qualifiers can be repeated
- type.const = specs.any?{|x| x.equal? :const }
- type.restrict = specs.any?{|x| x.equal? :restrict}
- type.volatile = specs.any?{|x| x.equal? :volatile}
-
- return type
- end
-
- def make_parameter(pos, specs, indirect_type, name)
- type = indirect_type
- if type
- type.direct_type = make_direct_type(pos, specs)
- else
- type = make_direct_type(pos, specs)
- end
- [:typedef, :extern, :static, :auto, :inline].each do |sym|
- specs.include? sym and
- parse_error pos, "parameter `#{declarator.name}' declared `#{sym}'"
- end
- return Parameter.new_at(pos, type, name,
- :register => specs.include?(:register))
- end
-
- def add_type_quals(type, quals)
- type.const = quals.include?(:const )
- type.restrict = quals.include?(:restrict)
- type.volatile = quals.include?(:volatile)
- return type
- end
-
- #
- # Add te given type as the "most direct" type to the given
- # declarator. Return the declarator.
- #
- def add_decl_type(declarator, type)
- if declarator.indirect_type
- declarator.indirect_type.direct_type = type
- else
- declarator.indirect_type = type
- end
- return declarator
- end
-
- def param_list(params, var_args)
- if params.length == 1 &&
- params[0].type.is_a?(Void) &&
- params[0].name.nil?
- return NodeArray[]
- elsif params.empty?
- return nil
- else
- return params
- end
- end
-
- def parse_error(pos, str)
- raise ParseError, "#{pos}: #{str}"
- end
-
-...end cast.y/module_eval...
-##### State transition tables begin ###
-
-racc_action_table = [
- 99, 100, 65, 103, 108, 109, 120, 312, 61, 110,
- 111, 112, 113, 114, 115, 116, 117, 77, 48, 10,
- 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
- 21, 22, 23, 24, 25, 26, 31, 32, 33, 34,
- 35, 36, 37, 72, 281, 128, 49, 38, 196, 391,
- 123, 124, 126, 127, 129, 130, 131, 132, 183, 277,
- 83, 273, 84, 238, 288, 293, 88, 196, 72, 290,
- 38, 274, 184, 372, 373, 386, 239, 240, 289, 294,
- 71, 241, 242, 50, 147, 148, 149, 150, 99, 100,
- 65, 193, 108, 109, 120, 50, 281, 110, 111, 112,
- 113, 114, 115, 116, 117, 71, 50, 10, 11, 12,
- 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
- 23, 24, 25, 26, 31, 32, 33, 34, 35, 36,
- 37, 56, 366, 128, 89, 38, 50, 169, 123, 124,
- 126, 127, 129, 130, 131, 132, 99, 100, 65, 88,
- 108, 109, 120, 88, 88, 110, 111, 112, 113, 114,
- 115, 116, 117, 346, 349, 238, 59, 68, 48, 365,
- 195, 50, 147, 148, 149, 150, 38, 347, 239, 240,
- 69, 178, 283, 196, 312, 366, 243, 244, 48, 262,
- 88, 128, 185, 38, 263, 284, 123, 124, 126, 127,
- 129, 130, 131, 132, 99, 100, 65, 49, 108, 109,
- 120, 38, 50, 110, 111, 112, 113, 114, 115, 116,
- 117, 281, 365, 309, 440, 249, 250, 49, 238, 50,
- 147, 148, 149, 150, 313, 379, 196, 227, 439, 178,
- 413, 239, 240, 88, 196, 50, 48, 50, 196, 128,
- 187, 38, 191, 196, 123, 124, 126, 127, 129, 130,
- 131, 132, 99, 100, 65, 50, 108, 109, 120, 416,
- 428, 110, 111, 112, 113, 114, 115, 116, 117, 255,
- 256, 192, 196, 196, 197, 49, 198, 50, 147, 148,
- 149, 150, 228, 229, 230, 231, 232, 233, 234, 235,
- 236, 237, 43, 199, 48, 241, 242, 128, 202, 38,
- 241, 242, 123, 124, 126, 127, 129, 130, 131, 132,
- 99, 100, 65, 50, 108, 109, 120, 375, 376, 110,
- 111, 112, 113, 114, 115, 116, 117, 196, 196, 205,
- 377, 387, 427, 49, 433, 50, 147, 148, 149, 150,
- 196, 388, 196, 437, 196, 206, 445, 209, 447, 450,
- 43, 251, 48, 196, 252, 128, 196, 38, 196, 196,
- 123, 124, 126, 127, 129, 130, 131, 132, 99, 100,
- 65, 50, 108, 109, 120, 454, 253, 110, 111, 112,
- 113, 114, 115, 116, 117, 196, 34, 35, 36, 243,
- 244, 49, 49, 50, 147, 148, 149, 150, 34, 35,
- 36, 243, 244, 254, 49, 245, 246, 247, 248, 67,
- 48, 243, 244, 128, 268, 38, 243, 244, 123, 124,
- 126, 127, 129, 130, 131, 132, 99, 100, 65, 50,
- 108, 109, 120, 249, 250, 110, 111, 112, 113, 114,
- 115, 116, 117, 245, 246, 247, 248, 271, 272, 49,
- 275, 50, 147, 148, 149, 150, 245, 246, 247, 248,
- 285, 296, 192, 303, 307, 308, 314, 315, 277, 50,
- 50, 128, 50, 38, 353, 355, 123, 124, 126, 127,
- 129, 130, 131, 132, 99, 100, 65, 50, 108, 109,
- 120, 357, 50, 110, 111, 112, 113, 114, 115, 116,
- 117, 378, 389, 390, 251, 281, 252, 49, 253, 50,
- 147, 148, 149, 150, 254, 395, 396, 397, 398, 399,
- 405, 406, 384, 384, 423, 424, 425, 426, 442, 128,
- nil, 38, nil, nil, 123, 124, 126, 127, 129, 130,
- 131, 132, 99, 100, 65, 50, 108, 109, 120, nil,
- nil, 110, 111, 112, 113, 114, 115, 116, 117, nil,
- nil, nil, nil, nil, nil, nil, nil, 50, 147, 148,
- 149, 150, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 128, nil, 38,
- nil, nil, 123, 124, 126, 127, 129, 130, 131, 132,
- 99, 100, 65, nil, 108, 109, 120, nil, nil, 110,
- 111, 112, 113, 114, 115, 116, 117, nil, nil, nil,
- nil, nil, nil, nil, nil, 50, 147, 148, 149, 150,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 128, nil, 38, nil, nil,
- 123, 124, 126, 127, 129, 130, 131, 132, 99, 100,
- 65, nil, 108, 109, 120, nil, nil, 110, 111, 112,
- 113, 114, 115, 116, 117, nil, nil, nil, nil, nil,
- nil, nil, nil, 50, 147, 148, 149, 150, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 128, nil, 38, nil, nil, 123, 124,
- 126, 127, 129, 130, 131, 132, 99, 100, 65, nil,
- 108, 109, 120, nil, nil, 110, 111, 112, 113, 114,
- 115, 116, 117, nil, nil, nil, nil, nil, nil, nil,
- nil, 50, 147, 148, 149, 150, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 128, nil, 38, nil, nil, 123, 124, 126, 127,
- 129, 130, 131, 132, 99, 100, 65, nil, 108, 109,
- 120, nil, nil, 110, 111, 112, 113, 114, 115, 116,
- 117, nil, nil, nil, nil, nil, nil, nil, nil, 50,
- 147, 148, 149, 150, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 128,
- nil, 38, nil, nil, 123, 124, 126, 127, 129, 130,
- 131, 132, 99, 100, 65, nil, 108, 109, 120, nil,
- nil, 110, 111, 112, 113, 114, 115, 116, 117, nil,
- nil, nil, nil, nil, nil, nil, nil, 50, 147, 148,
- 149, 150, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 128, nil, 38,
- nil, nil, 123, 124, 126, 127, 129, 130, 131, 132,
- 99, 100, 65, nil, 108, 109, 120, nil, nil, 110,
- 111, 112, 113, 114, 115, 116, 117, nil, nil, nil,
- nil, nil, nil, nil, nil, 50, 147, 148, 149, 150,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 128, nil, 38, nil, nil,
- 123, 124, 126, 127, 129, 130, 131, 132, 99, 100,
- 65, nil, 108, 109, 120, nil, nil, 110, 111, 112,
- 113, 114, 115, 116, 117, nil, nil, nil, nil, nil,
- nil, nil, nil, 50, 147, 148, 149, 150, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 128, nil, 38, nil, nil, 123, 124,
- 126, 127, 129, 130, 131, 132, 99, 100, 65, nil,
- 108, 109, 120, nil, nil, 110, 111, 112, 113, 114,
- 115, 116, 117, nil, nil, nil, nil, nil, nil, nil,
- nil, 50, 147, 148, 149, 150, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 128, nil, 38, nil, nil, 123, 124, 126, 127,
- 129, 130, 131, 132, 99, 100, 65, nil, 108, 109,
- 120, nil, nil, 110, 111, 112, 113, 114, 115, 116,
- 117, nil, nil, nil, nil, nil, nil, nil, nil, 50,
- 147, 148, 149, 150, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 128,
- nil, 38, nil, nil, 123, 124, 126, 127, 129, 130,
- 131, 132, 99, 100, 65, nil, 108, 109, 120, nil,
- nil, 110, 111, 112, 113, 114, 115, 116, 117, nil,
- nil, nil, nil, nil, nil, nil, nil, 50, 147, 148,
- 149, 150, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 128, nil, 38,
- nil, nil, 123, 124, 126, 127, 129, 130, 131, 132,
- 99, 100, 65, nil, 108, 109, 120, nil, nil, 110,
- 111, 112, 113, 114, 115, 116, 117, nil, nil, nil,
- nil, nil, nil, nil, nil, 50, 147, 148, 149, 150,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 128, nil, 38, nil, nil,
- 123, 124, 126, 127, 129, 130, 131, 132, 99, 100,
- 65, nil, 108, 109, 120, nil, nil, 110, 111, 112,
- 113, 114, 115, 116, 117, nil, nil, nil, nil, nil,
- nil, nil, nil, 50, 147, 148, 149, 150, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 128, nil, 38, nil, nil, 123, 124,
- 126, 127, 129, 130, 131, 132, 99, 100, 65, nil,
- 108, 109, 120, nil, nil, 110, 111, 112, 113, 114,
- 115, 116, 117, 154, nil, nil, nil, 120, nil, nil,
- nil, 50, 147, 148, 149, 150, nil, nil, nil, nil,
- nil, nil, 215, nil, nil, nil, nil, nil, nil, nil,
- nil, 128, nil, 38, nil, nil, 123, 124, 126, 127,
- 129, 130, 131, 132, nil, nil, 128, nil, nil, nil,
- nil, 123, 124, 126, 127, 129, 130, 131, 132, 214,
- nil, nil, nil, nil, 216, 217, 218, 219, nil, 50,
- 147, 148, 149, 150, 65, nil, nil, nil, 120, nil,
- nil, nil, nil, nil, 50, 147, 148, 149, 150, 154,
- nil, nil, nil, 120, nil, nil, 15, 16, 17, 18,
- 19, 20, 21, 22, 23, 24, 25, 26, 31, 32,
- 33, 34, 35, 36, nil, nil, nil, 128, nil, 38,
- nil, nil, 123, 124, 126, 127, 129, 130, 131, 132,
- 262, nil, 128, nil, nil, 263, nil, 123, 124, 126,
- 127, 129, 130, 131, 132, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 50, 147, 148, 149, 150,
- 65, nil, nil, nil, 120, nil, nil, nil, nil, nil,
- 50, 147, 148, 149, 150, nil, nil, nil, nil, nil,
- nil, nil, 15, 16, 17, 18, 19, 20, 21, 22,
- 23, 24, 25, 26, 31, 32, 33, 34, 35, 36,
- nil, nil, nil, 128, nil, 38, nil, nil, 123, 124,
- 126, 127, 129, 130, 131, 132, nil, nil, nil, nil,
- nil, nil, nil, nil, 65, nil, nil, nil, 120, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 50, 147, 148, 149, 150, 15, 16, 17, 18,
- 19, 20, 21, 22, 23, 24, 25, 26, 31, 32,
- 33, 34, 35, 36, nil, nil, nil, 128, nil, 38,
- nil, nil, 123, 124, 126, 127, 129, 130, 131, 132,
- 154, nil, nil, nil, 120, nil, nil, nil, nil, nil,
- nil, nil, nil, 384, nil, nil, nil, 120, nil, nil,
- nil, nil, nil, nil, nil, 50, 147, 148, 149, 150,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 128, nil, nil, nil, nil, 123, 124,
- 126, 127, 129, 130, 131, 132, 128, nil, nil, nil,
- nil, 123, 124, 126, 127, 129, 130, 131, 132, 154,
- 392, nil, nil, 120, nil, nil, nil, nil, nil, nil,
- nil, 50, 147, 148, 149, 150, 154, nil, nil, nil,
- 120, nil, nil, nil, 50, 147, 148, 149, 150, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 262, nil, 128, nil, nil, 263, nil, 123, 124, 126,
- 127, 129, 130, 131, 132, nil, nil, 262, nil, 128,
- nil, nil, 263, nil, 123, 124, 126, 127, 129, 130,
- 131, 132, 154, nil, nil, nil, 120, nil, nil, nil,
- 50, 147, 148, 149, 150, nil, nil, 154, 453, nil,
- nil, 120, nil, nil, nil, nil, nil, 50, 147, 148,
- 149, 150, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 128, nil, nil, nil, nil,
- 123, 124, 126, 127, 129, 130, 131, 132, 262, nil,
- 128, nil, 208, 263, 120, 123, 124, 126, 127, 129,
- 130, 131, 132, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 50, 147, 148, 149, 150, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 50, 147,
- 148, 149, 150, 128, nil, nil, nil, nil, 123, 124,
- 126, 127, 129, 130, 131, 132, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 305, nil, 120, nil, nil, nil, nil, nil, nil,
- nil, 50, 147, 148, 149, 150, 10, 11, 12, 13,
- 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
- 24, 25, 26, 31, 32, 33, 34, 35, 36, 37,
- nil, nil, 128, nil, 38, nil, nil, 123, 124, 126,
- 127, 129, 130, 131, 132, 381, nil, 120, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 383, nil,
- 120, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 50, 147, 148, 149, 150, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 128, nil, nil, nil,
- nil, 123, 124, 126, 127, 129, 130, 131, 132, 128,
- nil, 412, nil, 120, 123, 124, 126, 127, 129, 130,
- 131, 132, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 50, 147, 148, 149, 150, nil,
- nil, nil, nil, nil, nil, nil, nil, 50, 147, 148,
- 149, 150, 128, nil, nil, nil, nil, 123, 124, 126,
- 127, 129, 130, 131, 132, 120, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 120, nil,
- 160, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 50, 147, 148, 149, 150, nil, nil, nil, 34, 35,
- 36, nil, nil, 159, 161, nil, nil, nil, nil, 123,
- 124, 126, 127, 129, 130, 131, 132, 128, nil, nil,
- nil, 220, 123, 124, 126, 127, 129, 130, 131, 132,
- nil, nil, nil, nil, 220, nil, nil, nil, nil, nil,
- nil, nil, 50, 147, 148, 149, 150, nil, nil, nil,
- nil, nil, nil, nil, nil, 50, 147, 148, 149, 150,
- 128, nil, nil, nil, nil, 123, 124, 126, 127, 129,
- 130, 131, 132, 128, nil, nil, nil, 120, 123, 124,
- 126, 127, 129, 130, 131, 132, nil, nil, nil, nil,
- 224, nil, nil, nil, nil, nil, nil, nil, 50, 147,
- 148, 149, 150, nil, nil, nil, nil, nil, nil, nil,
- nil, 50, 147, 148, 149, 150, 128, nil, nil, nil,
- nil, 123, 124, 126, 127, 129, 130, 131, 132, 128,
- nil, nil, nil, nil, 123, 124, 126, 127, 129, 130,
- 131, 132, nil, nil, 120, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 50, 147, 148, 149, 150, 266,
- nil, nil, nil, 120, nil, nil, nil, 50, 147, 148,
- 149, 150, nil, nil, nil, nil, nil, 34, 35, 36,
- nil, nil, 265, 267, nil, nil, nil, 120, 123, 124,
- 126, 127, 129, 130, 131, 132, 34, 35, 36, nil,
- nil, nil, 128, nil, nil, nil, 120, 123, 124, 126,
- 127, 129, 130, 131, 132, nil, nil, nil, nil, nil,
- nil, 50, 147, 148, 149, 150, 128, nil, nil, nil,
- 120, 123, 124, 126, 127, 129, 130, 131, 132, nil,
- 50, 147, 148, 149, 150, 128, nil, nil, nil, 120,
- 123, 124, 126, 127, 129, 130, 131, 132, nil, nil,
- nil, nil, nil, nil, 50, 147, 148, 149, 150, 128,
- nil, nil, nil, 120, 123, 124, 126, 127, 129, 130,
- 131, 132, nil, 50, 147, 148, 149, 150, 128, nil,
- nil, nil, 120, 123, 124, 126, 127, 129, 130, 131,
- 132, nil, nil, nil, nil, nil, nil, 50, 147, 148,
- 149, 150, 128, nil, nil, nil, 120, 123, 124, 126,
- 127, 129, 130, 131, 132, nil, 50, 147, 148, 149,
- 150, 128, nil, nil, nil, nil, 123, 124, 126, 127,
- 129, 130, 131, 132, nil, nil, nil, nil, nil, nil,
- 50, 147, 148, 149, 150, 128, nil, nil, nil, nil,
- 123, 124, 126, 127, 129, 130, 131, 132, nil, 50,
- 147, 148, 149, 150, nil, nil, nil, nil, nil, nil,
- 120, 318, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 50, 147, 148, 149, 150, 15, 16,
- 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
- 31, 32, 33, 34, 35, 36, nil, nil, nil, 128,
- nil, 38, nil, 120, 123, 124, 126, 127, 129, 130,
- 131, 132, nil, nil, nil, nil, 120, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 50, 147, 148,
- 149, 150, 128, nil, nil, nil, nil, 123, 124, 126,
- 127, 129, 130, 131, 132, 128, nil, nil, nil, 120,
- 123, 124, 126, 127, 129, 130, 131, 132, nil, nil,
- nil, nil, 120, nil, nil, nil, nil, nil, nil, nil,
- 50, 147, 148, 149, 150, nil, nil, nil, nil, nil,
- nil, nil, nil, 50, 147, 148, 149, 150, 128, nil,
- nil, nil, nil, 123, 124, 126, 127, 129, 130, 131,
- 132, 128, nil, nil, nil, 120, 123, 124, 126, 127,
- 129, 130, 131, 132, nil, nil, nil, nil, 120, nil,
- nil, nil, nil, nil, nil, nil, 50, 147, 148, 149,
- 150, nil, nil, nil, nil, nil, nil, nil, nil, 50,
- 147, 148, 149, 150, 128, nil, nil, nil, nil, 123,
- 124, 126, 127, 129, 130, 131, 132, 128, nil, nil,
- nil, 120, 123, 124, 126, 127, 129, 130, 131, 132,
- nil, nil, nil, nil, 120, nil, nil, nil, nil, nil,
- nil, nil, 50, 147, 148, 149, 150, nil, nil, nil,
- nil, nil, nil, nil, nil, 50, 147, 148, 149, 150,
- 128, nil, nil, nil, nil, 123, 124, 126, 127, 129,
- 130, 131, 132, 128, nil, nil, nil, 120, 123, 124,
- 126, 127, 129, 130, 131, 132, nil, nil, nil, nil,
- 120, nil, nil, nil, nil, nil, nil, nil, 50, 147,
- 148, 149, 150, nil, nil, nil, nil, nil, nil, nil,
- nil, 50, 147, 148, 149, 150, 128, nil, nil, nil,
- nil, 123, 124, 126, 127, 129, 130, 131, 132, 128,
- nil, nil, nil, 120, 123, 124, 126, 127, 129, 130,
- 131, 132, nil, nil, nil, nil, 120, nil, nil, nil,
- nil, nil, nil, nil, 50, 147, 148, 149, 150, nil,
- nil, nil, nil, nil, nil, nil, nil, 50, 147, 148,
- 149, 150, 128, nil, nil, nil, nil, 123, 124, 126,
- 127, 129, 130, 131, 132, 128, nil, nil, nil, 120,
- 123, 124, 126, 127, 129, 130, 131, 132, nil, nil,
- nil, nil, 120, nil, nil, nil, nil, nil, nil, nil,
- 50, 147, 148, 149, 150, nil, nil, nil, nil, nil,
- nil, nil, nil, 50, 147, 148, 149, 150, 128, nil,
- nil, nil, nil, 123, 124, 126, 127, 129, 130, 131,
- 132, 128, nil, nil, nil, 120, 123, 124, 126, 127,
- 129, 130, 131, 132, nil, nil, nil, nil, 120, nil,
- nil, nil, nil, nil, nil, nil, 50, 147, 148, 149,
- 150, nil, nil, nil, nil, nil, nil, nil, nil, 50,
- 147, 148, 149, 150, 128, nil, nil, nil, nil, 123,
- 124, 126, 127, 129, 130, 131, 132, 128, nil, nil,
- nil, 120, 123, 124, 126, 127, 129, 130, 131, 132,
- nil, nil, nil, nil, 120, nil, nil, nil, nil, nil,
- nil, nil, 50, 147, 148, 149, 150, nil, nil, nil,
- nil, nil, nil, nil, nil, 50, 147, 148, 149, 150,
- 128, nil, nil, nil, nil, 123, 124, 126, 127, 129,
- 130, 131, 132, 128, nil, nil, nil, 120, 123, 124,
- 126, 127, 129, 130, 131, 132, nil, nil, nil, nil,
- 120, nil, nil, nil, nil, nil, nil, nil, 50, 147,
- 148, 149, 150, nil, nil, nil, nil, nil, nil, nil,
- nil, 50, 147, 148, 149, 150, 128, nil, nil, nil,
- nil, 123, 124, 126, 127, 129, 130, 131, 132, 128,
- nil, nil, nil, 120, 123, 124, 126, 127, 129, 130,
- 131, 132, nil, nil, nil, nil, 120, nil, nil, nil,
- nil, nil, nil, nil, 50, 147, 148, 149, 150, nil,
- nil, nil, nil, nil, nil, nil, nil, 50, 147, 148,
- 149, 150, 128, nil, nil, nil, nil, 123, 124, 126,
- 127, 129, 130, 131, 132, 128, nil, nil, nil, nil,
- 123, 124, 126, 127, 129, 130, 131, 132, 120, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 50, 147, 148, 149, 150, 120, nil, nil, nil, nil,
- nil, nil, nil, 50, 147, 148, 149, 150, nil, nil,
- nil, 34, 35, 36, nil, nil, nil, 128, nil, nil,
- nil, nil, 123, 124, 126, 127, 129, 130, 131, 132,
- nil, nil, nil, 368, 369, nil, nil, nil, 120, 123,
- 124, 126, 127, 129, 130, 131, 132, nil, nil, nil,
- nil, nil, 120, nil, nil, 50, 147, 148, 149, 150,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 50, 147, 148, 149, 150, 128, nil, nil,
- nil, nil, 123, 124, 126, 127, 129, 130, 131, 132,
- 401, 402, nil, nil, nil, 120, 123, 124, 126, 127,
- 129, 130, 131, 132, nil, nil, nil, nil, 120, 415,
- nil, nil, nil, nil, nil, 50, 147, 148, 149, 150,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 50,
- 147, 148, 149, 150, 128, nil, nil, nil, nil, 123,
- 124, 126, 127, 129, 130, 131, 132, 128, nil, nil,
- nil, nil, 123, 124, 126, 127, 129, 130, 131, 132,
- 120, 418, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 50, 147, 148, 149, 150, nil, nil, nil,
- nil, nil, nil, nil, nil, 50, 147, 148, 149, 150,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 128,
- nil, nil, nil, nil, 123, 124, 126, 127, 129, 130,
- 131, 132, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 120, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 50, 147, 148,
- 149, 150, 15, 16, 17, 18, 19, 20, 21, 22,
- 23, 24, 25, 26, 31, 32, 33, 34, 35, 36,
- nil, nil, nil, 128, nil, 38, nil, 120, 123, 124,
- 126, 127, 129, 130, 131, 132, nil, nil, nil, nil,
- 120, 430, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 50, 147, 148, 149, 150, 128, nil, nil, nil,
- nil, 123, 124, 126, 127, 129, 130, 131, 132, 128,
- nil, nil, nil, nil, 123, 124, 126, 127, 129, 130,
- 131, 132, 120, 432, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 50, 147, 148, 149, 150, 120,
- 436, nil, nil, nil, nil, nil, nil, 50, 147, 148,
- 149, 150, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 128, nil, nil, nil, nil, 123, 124, 126, 127,
- 129, 130, 131, 132, nil, nil, nil, nil, 128, nil,
- nil, nil, nil, 123, 124, 126, 127, 129, 130, 131,
- 132, 120, 444, nil, nil, nil, nil, nil, nil, 50,
- 147, 148, 149, 150, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 50, 147, 148, 149,
- 150, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 128, nil, nil, nil, nil, 123, 124, 126, 127, 129,
- 130, 131, 132, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 277, 364, nil, nil, nil, nil, nil, nil, 50, 147,
- 148, 149, 150, 10, 11, 12, 13, 14, 15, 16,
- 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
- 31, 32, 33, 34, 35, 36, 37, 281, 164, 49,
- nil, 38, nil, nil, nil, nil, nil, nil, nil, nil,
- 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
- 20, 21, 22, 23, 24, 25, 26, 31, 32, 33,
- 34, 35, 36, 37, nil, nil, nil, 50, 38, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 39, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 50, 10, 11, 12, 13, 14,
- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
- 25, 26, 31, 32, 33, 34, 35, 36, 37, 65,
- nil, nil, nil, 38, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 67, 10, 11, 12, 13,
- 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
- 24, 25, 26, 31, 32, 33, 34, 35, 36, 37,
- 65, nil, nil, nil, 38, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 10, 11, 12,
- 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
- 23, 24, 25, 26, 31, 32, 33, 34, 35, 36,
- 37, 173, nil, nil, nil, 38, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 15, 16, 17, 18, 19, 20, 21, 22,
- 23, 24, 25, 26, 31, 32, 33, 34, 35, 36,
- 282, nil, nil, nil, nil, 38, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 15, 16, 17, 18, 19, 20, 21, 22, 23,
- 24, 25, 26, 31, 32, 33, 34, 35, 36, 287,
- nil, nil, nil, nil, 38, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
- 25, 26, 31, 32, 33, 34, 35, 36, 312, 364,
- nil, nil, nil, 38, nil, nil, nil, nil, nil, nil,
- nil, 10, 11, 12, 13, 14, 15, 16, 17, 18,
- 19, 20, 21, 22, 23, 24, 25, 26, 31, 32,
- 33, 34, 35, 36, 37, 281, 404, 49, nil, 38,
- nil, nil, nil, nil, nil, nil, nil, nil, 10, 11,
- 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
- 22, 23, 24, 25, 26, 31, 32, 33, 34, 35,
- 36, 37, nil, nil, nil, nil, 38, 10, 11, 12,
- 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
- 23, 24, 25, 26, 31, 32, 33, 34, 35, 36,
- 37, nil, nil, nil, nil, 38, 10, 11, 12, 13,
- 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
- 24, 25, 26, 31, 32, 33, 34, 35, 36, 37,
- nil, nil, nil, nil, 38, 10, 11, 12, 13, 14,
- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
- 25, 26, 31, 32, 33, 34, 35, 36, 37, nil,
- nil, nil, nil, 38, 10, 11, 12, 13, 14, 15,
- 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
- 26, 31, 32, 33, 34, 35, 36, 37, nil, nil,
- nil, nil, 38, 10, 11, 12, 13, 14, 15, 16,
- 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
- 31, 32, 33, 34, 35, 36, 37, nil, nil, nil,
- nil, 38, 10, 11, 12, 13, 14, 15, 16, 17,
- 18, 19, 20, 21, 22, 23, 24, 25, 26, 31,
- 32, 33, 34, 35, 36, 37, nil, nil, nil, 359,
- 38, 15, 16, 17, 18, 19, 20, 21, 22, 23,
- 24, 25, 26, 31, 32, 33, 34, 35, 36, nil,
- nil, nil, nil, nil, 38, 15, 16, 17, 18, 19,
- 20, 21, 22, 23, 24, 25, 26, 31, 32, 33,
- 34, 35, 36, nil, nil, nil, nil, nil, 38, 15,
- 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
- 26, 31, 32, 33, 34, 35, 36, nil, nil, nil,
- nil, nil, 38, 15, 16, 17, 18, 19, 20, 21,
- 22, 23, 24, 25, 26, 31, 32, 33, 34, 35,
- 36, nil, nil, nil, nil, nil, 38, 15, 16, 17,
- 18, 19, 20, 21, 22, 23, 24, 25, 26, 31,
- 32, 33, 34, 35, 36, nil, nil, nil, nil, nil,
- 38 ]
-
-racc_action_check = [
- 65, 65, 65, 65, 65, 65, 65, 210, 39, 65,
- 65, 65, 65, 65, 65, 65, 65, 55, 45, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 46, 210, 65, 210, 65, 316, 345,
- 65, 65, 65, 65, 65, 65, 65, 65, 85, 276,
- 57, 163, 58, 135, 182, 186, 59, 345, 70, 184,
- 114, 163, 85, 289, 294, 316, 135, 135, 182, 186,
- 46, 136, 136, 65, 65, 65, 65, 65, 102, 102,
- 102, 102, 102, 102, 102, 45, 276, 102, 102, 102,
- 102, 102, 102, 102, 102, 70, 114, 102, 102, 102,
- 102, 102, 102, 102, 102, 102, 102, 102, 102, 102,
- 102, 102, 102, 102, 102, 102, 102, 102, 102, 102,
- 102, 30, 280, 102, 60, 102, 276, 73, 102, 102,
- 102, 102, 102, 102, 102, 102, 112, 112, 112, 184,
- 112, 112, 112, 289, 294, 112, 112, 112, 112, 112,
- 112, 112, 112, 257, 260, 330, 33, 42, 48, 280,
- 107, 102, 102, 102, 102, 102, 30, 257, 330, 330,
- 42, 80, 175, 107, 311, 361, 137, 137, 80, 260,
- 84, 112, 87, 112, 260, 175, 112, 112, 112, 112,
- 112, 112, 112, 112, 187, 187, 187, 48, 187, 187,
- 187, 33, 30, 187, 187, 187, 187, 187, 187, 187,
- 187, 311, 361, 207, 419, 139, 139, 80, 331, 112,
- 112, 112, 112, 112, 211, 304, 207, 133, 419, 284,
- 380, 331, 331, 89, 211, 48, 284, 33, 304, 187,
- 98, 187, 100, 380, 187, 187, 187, 187, 187, 187,
- 187, 187, 191, 191, 191, 80, 191, 191, 191, 382,
- 411, 191, 191, 191, 191, 191, 191, 191, 191, 144,
- 144, 101, 382, 411, 109, 284, 110, 187, 187, 187,
- 187, 187, 133, 133, 133, 133, 133, 133, 133, 133,
- 133, 133, 5, 111, 5, 332, 332, 191, 113, 191,
- 333, 333, 191, 191, 191, 191, 191, 191, 191, 191,
- 192, 192, 192, 284, 192, 192, 192, 300, 301, 192,
- 192, 192, 192, 192, 192, 192, 192, 300, 301, 115,
- 302, 317, 410, 5, 414, 191, 191, 191, 191, 191,
- 302, 317, 410, 417, 414, 116, 429, 119, 431, 435,
- 66, 140, 66, 417, 141, 192, 429, 192, 431, 435,
- 192, 192, 192, 192, 192, 192, 192, 192, 296, 296,
- 296, 5, 296, 296, 296, 443, 142, 296, 296, 296,
- 296, 296, 296, 296, 296, 443, 49, 49, 49, 334,
- 334, 66, 49, 192, 192, 192, 192, 192, 74, 74,
- 74, 335, 335, 143, 74, 138, 138, 138, 138, 151,
- 69, 336, 336, 296, 158, 296, 337, 337, 296, 296,
- 296, 296, 296, 296, 296, 296, 375, 375, 375, 66,
- 375, 375, 375, 340, 340, 375, 375, 375, 375, 375,
- 375, 375, 375, 338, 338, 338, 338, 161, 162, 69,
- 165, 296, 296, 296, 296, 296, 339, 339, 339, 339,
- 177, 188, 200, 201, 203, 204, 212, 213, 167, 216,
- 217, 375, 263, 375, 264, 267, 375, 375, 375, 375,
- 375, 375, 375, 375, 376, 376, 376, 69, 376, 376,
- 376, 270, 274, 376, 376, 376, 376, 376, 376, 376,
- 376, 303, 324, 325, 341, 167, 342, 167, 343, 375,
- 375, 375, 375, 375, 344, 351, 354, 356, 362, 363,
- 367, 369, 389, 390, 400, 402, 403, 407, 427, 376,
- nil, 376, nil, nil, 376, 376, 376, 376, 376, 376,
- 376, 376, 377, 377, 377, 167, 377, 377, 377, nil,
- nil, 377, 377, 377, 377, 377, 377, 377, 377, nil,
- nil, nil, nil, nil, nil, nil, nil, 376, 376, 376,
- 376, 376, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 377, nil, 377,
- nil, nil, 377, 377, 377, 377, 377, 377, 377, 377,
- 415, 415, 415, nil, 415, 415, 415, nil, nil, 415,
- 415, 415, 415, 415, 415, 415, 415, nil, nil, nil,
- nil, nil, nil, nil, nil, 377, 377, 377, 377, 377,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 415, nil, 415, nil, nil,
- 415, 415, 415, 415, 415, 415, 415, 415, 418, 418,
- 418, nil, 418, 418, 418, nil, nil, 418, 418, 418,
- 418, 418, 418, 418, 418, nil, nil, nil, nil, nil,
- nil, nil, nil, 415, 415, 415, 415, 415, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 418, nil, 418, nil, nil, 418, 418,
- 418, 418, 418, 418, 418, 418, 426, 426, 426, nil,
- 426, 426, 426, nil, nil, 426, 426, 426, 426, 426,
- 426, 426, 426, nil, nil, nil, nil, nil, nil, nil,
- nil, 418, 418, 418, 418, 418, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 426, nil, 426, nil, nil, 426, 426, 426, 426,
- 426, 426, 426, 426, 430, 430, 430, nil, 430, 430,
- 430, nil, nil, 430, 430, 430, 430, 430, 430, 430,
- 430, nil, nil, nil, nil, nil, nil, nil, nil, 426,
- 426, 426, 426, 426, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 430,
- nil, 430, nil, nil, 430, 430, 430, 430, 430, 430,
- 430, 430, 432, 432, 432, nil, 432, 432, 432, nil,
- nil, 432, 432, 432, 432, 432, 432, 432, 432, nil,
- nil, nil, nil, nil, nil, nil, nil, 430, 430, 430,
- 430, 430, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 432, nil, 432,
- nil, nil, 432, 432, 432, 432, 432, 432, 432, 432,
- 433, 433, 433, nil, 433, 433, 433, nil, nil, 433,
- 433, 433, 433, 433, 433, 433, 433, nil, nil, nil,
- nil, nil, nil, nil, nil, 432, 432, 432, 432, 432,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 433, nil, 433, nil, nil,
- 433, 433, 433, 433, 433, 433, 433, 433, 436, 436,
- 436, nil, 436, 436, 436, nil, nil, 436, 436, 436,
- 436, 436, 436, 436, 436, nil, nil, nil, nil, nil,
- nil, nil, nil, 433, 433, 433, 433, 433, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 436, nil, 436, nil, nil, 436, 436,
- 436, 436, 436, 436, 436, 436, 437, 437, 437, nil,
- 437, 437, 437, nil, nil, 437, 437, 437, 437, 437,
- 437, 437, 437, nil, nil, nil, nil, nil, nil, nil,
- nil, 436, 436, 436, 436, 436, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 437, nil, 437, nil, nil, 437, 437, 437, 437,
- 437, 437, 437, 437, 444, 444, 444, nil, 444, 444,
- 444, nil, nil, 444, 444, 444, 444, 444, 444, 444,
- 444, nil, nil, nil, nil, nil, nil, nil, nil, 437,
- 437, 437, 437, 437, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 444,
- nil, 444, nil, nil, 444, 444, 444, 444, 444, 444,
- 444, 444, 445, 445, 445, nil, 445, 445, 445, nil,
- nil, 445, 445, 445, 445, 445, 445, 445, 445, nil,
- nil, nil, nil, nil, nil, nil, nil, 444, 444, 444,
- 444, 444, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 445, nil, 445,
- nil, nil, 445, 445, 445, 445, 445, 445, 445, 445,
- 447, 447, 447, nil, 447, 447, 447, nil, nil, 447,
- 447, 447, 447, 447, 447, 447, 447, nil, nil, nil,
- nil, nil, nil, nil, nil, 445, 445, 445, 445, 445,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 447, nil, 447, nil, nil,
- 447, 447, 447, 447, 447, 447, 447, 447, 450, 450,
- 450, nil, 450, 450, 450, nil, nil, 450, 450, 450,
- 450, 450, 450, 450, 450, nil, nil, nil, nil, nil,
- nil, nil, nil, 447, 447, 447, 447, 447, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 450, nil, 450, nil, nil, 450, 450,
- 450, 450, 450, 450, 450, 450, 454, 454, 454, nil,
- 454, 454, 454, nil, nil, 454, 454, 454, 454, 454,
- 454, 454, 454, 67, nil, nil, nil, 67, nil, nil,
- nil, 450, 450, 450, 450, 450, nil, nil, nil, nil,
- nil, nil, 122, nil, nil, nil, nil, nil, nil, nil,
- nil, 454, nil, 454, nil, nil, 454, 454, 454, 454,
- 454, 454, 454, 454, nil, nil, 67, nil, nil, nil,
- nil, 67, 67, 67, 67, 67, 67, 67, 67, 122,
- nil, nil, nil, nil, 122, 122, 122, 122, nil, 454,
- 454, 454, 454, 454, 120, nil, nil, nil, 120, nil,
- nil, nil, nil, nil, 67, 67, 67, 67, 67, 154,
- nil, nil, nil, 154, nil, nil, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, nil, nil, nil, 120, nil, 120,
- nil, nil, 120, 120, 120, 120, 120, 120, 120, 120,
- 154, nil, 154, nil, nil, 154, nil, 154, 154, 154,
- 154, 154, 154, 154, 154, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 120, 120, 120, 120, 120,
- 220, nil, nil, nil, 220, nil, nil, nil, nil, nil,
- 154, 154, 154, 154, 154, nil, nil, nil, nil, nil,
- nil, nil, 220, 220, 220, 220, 220, 220, 220, 220,
- 220, 220, 220, 220, 220, 220, 220, 220, 220, 220,
- nil, nil, nil, 220, nil, 220, nil, nil, 220, 220,
- 220, 220, 220, 220, 220, 220, nil, nil, nil, nil,
- nil, nil, nil, nil, 224, nil, nil, nil, 224, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 220, 220, 220, 220, 220, 224, 224, 224, 224,
- 224, 224, 224, 224, 224, 224, 224, 224, 224, 224,
- 224, 224, 224, 224, nil, nil, nil, 224, nil, 224,
- nil, nil, 224, 224, 224, 224, 224, 224, 224, 224,
- 258, nil, nil, nil, 258, nil, nil, nil, nil, nil,
- nil, nil, nil, 315, nil, nil, nil, 315, nil, nil,
- nil, nil, nil, nil, nil, 224, 224, 224, 224, 224,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 258, nil, nil, nil, nil, 258, 258,
- 258, 258, 258, 258, 258, 258, 315, nil, nil, nil,
- nil, 315, 315, 315, 315, 315, 315, 315, 315, 347,
- 347, nil, nil, 347, nil, nil, nil, nil, nil, nil,
- nil, 258, 258, 258, 258, 258, 384, nil, nil, nil,
- 384, nil, nil, nil, 315, 315, 315, 315, 315, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 347, nil, 347, nil, nil, 347, nil, 347, 347, 347,
- 347, 347, 347, 347, 347, nil, nil, 384, nil, 384,
- nil, nil, 384, nil, 384, 384, 384, 384, 384, 384,
- 384, 384, 393, nil, nil, nil, 393, nil, nil, nil,
- 347, 347, 347, 347, 347, nil, nil, 439, 439, nil,
- nil, 439, nil, nil, nil, nil, nil, 384, 384, 384,
- 384, 384, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 393, nil, nil, nil, nil,
- 393, 393, 393, 393, 393, 393, 393, 393, 439, nil,
- 439, nil, 117, 439, 117, 439, 439, 439, 439, 439,
- 439, 439, 439, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 393, 393, 393, 393, 393, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 439, 439,
- 439, 439, 439, 117, nil, nil, nil, nil, 117, 117,
- 117, 117, 117, 117, 117, 117, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 202, nil, 202, nil, nil, nil, nil, nil, nil,
- nil, 117, 117, 117, 117, 117, 202, 202, 202, 202,
- 202, 202, 202, 202, 202, 202, 202, 202, 202, 202,
- 202, 202, 202, 202, 202, 202, 202, 202, 202, 202,
- nil, nil, 202, nil, 202, nil, nil, 202, 202, 202,
- 202, 202, 202, 202, 202, 305, nil, 305, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 306, nil,
- 306, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 202, 202, 202, 202, 202, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 305, nil, nil, nil,
- nil, 305, 305, 305, 305, 305, 305, 305, 305, 306,
- nil, 379, nil, 379, 306, 306, 306, 306, 306, 306,
- 306, 306, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 305, 305, 305, 305, 305, nil,
- nil, nil, nil, nil, nil, nil, nil, 306, 306, 306,
- 306, 306, 379, nil, nil, nil, nil, 379, 379, 379,
- 379, 379, 379, 379, 379, 71, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 99, nil,
- 71, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 379, 379, 379, 379, 379, nil, nil, nil, 71, 71,
- 71, nil, nil, 71, 71, nil, nil, nil, nil, 71,
- 71, 71, 71, 71, 71, 71, 71, 99, nil, nil,
- nil, 123, 99, 99, 99, 99, 99, 99, 99, 99,
- nil, nil, nil, nil, 124, nil, nil, nil, nil, nil,
- nil, nil, 71, 71, 71, 71, 71, nil, nil, nil,
- nil, nil, nil, nil, nil, 99, 99, 99, 99, 99,
- 123, nil, nil, nil, nil, 123, 123, 123, 123, 123,
- 123, 123, 123, 124, nil, nil, nil, 125, 124, 124,
- 124, 124, 124, 124, 124, 124, nil, nil, nil, nil,
- 126, nil, nil, nil, nil, nil, nil, nil, 123, 123,
- 123, 123, 123, nil, nil, nil, nil, nil, nil, nil,
- nil, 124, 124, 124, 124, 124, 125, nil, nil, nil,
- nil, 125, 125, 125, 125, 125, 125, 125, 125, 126,
- nil, nil, nil, nil, 126, 126, 126, 126, 126, 126,
- 126, 126, nil, nil, 157, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 125, 125, 125, 125, 125, 157,
- nil, nil, nil, 160, nil, nil, nil, 126, 126, 126,
- 126, 126, nil, nil, nil, nil, nil, 157, 157, 157,
- nil, nil, 157, 157, nil, nil, nil, 178, 157, 157,
- 157, 157, 157, 157, 157, 157, 160, 160, 160, nil,
- nil, nil, 160, nil, nil, nil, 185, 160, 160, 160,
- 160, 160, 160, 160, 160, nil, nil, nil, nil, nil,
- nil, 157, 157, 157, 157, 157, 178, nil, nil, nil,
- 196, 178, 178, 178, 178, 178, 178, 178, 178, nil,
- 160, 160, 160, 160, 160, 185, nil, nil, nil, 197,
- 185, 185, 185, 185, 185, 185, 185, 185, nil, nil,
- nil, nil, nil, nil, 178, 178, 178, 178, 178, 196,
- nil, nil, nil, 198, 196, 196, 196, 196, 196, 196,
- 196, 196, nil, 185, 185, 185, 185, 185, 197, nil,
- nil, nil, 199, 197, 197, 197, 197, 197, 197, 197,
- 197, nil, nil, nil, nil, nil, nil, 196, 196, 196,
- 196, 196, 198, nil, nil, nil, 214, 198, 198, 198,
- 198, 198, 198, 198, 198, nil, 197, 197, 197, 197,
- 197, 199, nil, nil, nil, nil, 199, 199, 199, 199,
- 199, 199, 199, 199, nil, nil, nil, nil, nil, nil,
- 198, 198, 198, 198, 198, 214, nil, nil, nil, nil,
- 214, 214, 214, 214, 214, 214, 214, 214, nil, 199,
- 199, 199, 199, 199, nil, nil, nil, nil, nil, nil,
- 215, 215, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 214, 214, 214, 214, 214, 215, 215,
- 215, 215, 215, 215, 215, 215, 215, 215, 215, 215,
- 215, 215, 215, 215, 215, 215, nil, nil, nil, 215,
- nil, 215, nil, 226, 215, 215, 215, 215, 215, 215,
- 215, 215, nil, nil, nil, nil, 238, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 215, 215, 215,
- 215, 215, 226, nil, nil, nil, nil, 226, 226, 226,
- 226, 226, 226, 226, 226, 238, nil, nil, nil, 239,
- 238, 238, 238, 238, 238, 238, 238, 238, nil, nil,
- nil, nil, 240, nil, nil, nil, nil, nil, nil, nil,
- 226, 226, 226, 226, 226, nil, nil, nil, nil, nil,
- nil, nil, nil, 238, 238, 238, 238, 238, 239, nil,
- nil, nil, nil, 239, 239, 239, 239, 239, 239, 239,
- 239, 240, nil, nil, nil, 241, 240, 240, 240, 240,
- 240, 240, 240, 240, nil, nil, nil, nil, 242, nil,
- nil, nil, nil, nil, nil, nil, 239, 239, 239, 239,
- 239, nil, nil, nil, nil, nil, nil, nil, nil, 240,
- 240, 240, 240, 240, 241, nil, nil, nil, nil, 241,
- 241, 241, 241, 241, 241, 241, 241, 242, nil, nil,
- nil, 243, 242, 242, 242, 242, 242, 242, 242, 242,
- nil, nil, nil, nil, 244, nil, nil, nil, nil, nil,
- nil, nil, 241, 241, 241, 241, 241, nil, nil, nil,
- nil, nil, nil, nil, nil, 242, 242, 242, 242, 242,
- 243, nil, nil, nil, nil, 243, 243, 243, 243, 243,
- 243, 243, 243, 244, nil, nil, nil, 245, 244, 244,
- 244, 244, 244, 244, 244, 244, nil, nil, nil, nil,
- 246, nil, nil, nil, nil, nil, nil, nil, 243, 243,
- 243, 243, 243, nil, nil, nil, nil, nil, nil, nil,
- nil, 244, 244, 244, 244, 244, 245, nil, nil, nil,
- nil, 245, 245, 245, 245, 245, 245, 245, 245, 246,
- nil, nil, nil, 247, 246, 246, 246, 246, 246, 246,
- 246, 246, nil, nil, nil, nil, 248, nil, nil, nil,
- nil, nil, nil, nil, 245, 245, 245, 245, 245, nil,
- nil, nil, nil, nil, nil, nil, nil, 246, 246, 246,
- 246, 246, 247, nil, nil, nil, nil, 247, 247, 247,
- 247, 247, 247, 247, 247, 248, nil, nil, nil, 249,
- 248, 248, 248, 248, 248, 248, 248, 248, nil, nil,
- nil, nil, 250, nil, nil, nil, nil, nil, nil, nil,
- 247, 247, 247, 247, 247, nil, nil, nil, nil, nil,
- nil, nil, nil, 248, 248, 248, 248, 248, 249, nil,
- nil, nil, nil, 249, 249, 249, 249, 249, 249, 249,
- 249, 250, nil, nil, nil, 251, 250, 250, 250, 250,
- 250, 250, 250, 250, nil, nil, nil, nil, 252, nil,
- nil, nil, nil, nil, nil, nil, 249, 249, 249, 249,
- 249, nil, nil, nil, nil, nil, nil, nil, nil, 250,
- 250, 250, 250, 250, 251, nil, nil, nil, nil, 251,
- 251, 251, 251, 251, 251, 251, 251, 252, nil, nil,
- nil, 253, 252, 252, 252, 252, 252, 252, 252, 252,
- nil, nil, nil, nil, 254, nil, nil, nil, nil, nil,
- nil, nil, 251, 251, 251, 251, 251, nil, nil, nil,
- nil, nil, nil, nil, nil, 252, 252, 252, 252, 252,
- 253, nil, nil, nil, nil, 253, 253, 253, 253, 253,
- 253, 253, 253, 254, nil, nil, nil, 255, 254, 254,
- 254, 254, 254, 254, 254, 254, nil, nil, nil, nil,
- 256, nil, nil, nil, nil, nil, nil, nil, 253, 253,
- 253, 253, 253, nil, nil, nil, nil, nil, nil, nil,
- nil, 254, 254, 254, 254, 254, 255, nil, nil, nil,
- nil, 255, 255, 255, 255, 255, 255, 255, 255, 256,
- nil, nil, nil, 262, 256, 256, 256, 256, 256, 256,
- 256, 256, nil, nil, nil, nil, 266, nil, nil, nil,
- nil, nil, nil, nil, 255, 255, 255, 255, 255, nil,
- nil, nil, nil, nil, nil, nil, nil, 256, 256, 256,
- 256, 256, 262, nil, nil, nil, nil, 262, 262, 262,
- 262, 262, 262, 262, 262, 266, nil, nil, nil, nil,
- 266, 266, 266, 266, 266, 266, 266, 266, 269, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 262, 262, 262, 262, 262, 281, nil, nil, nil, nil,
- nil, nil, nil, 266, 266, 266, 266, 266, nil, nil,
- nil, 269, 269, 269, nil, nil, nil, 269, nil, nil,
- nil, nil, 269, 269, 269, 269, 269, 269, 269, 269,
- nil, nil, nil, 281, 281, nil, nil, nil, 285, 281,
- 281, 281, 281, 281, 281, 281, 281, nil, nil, nil,
- nil, nil, 365, nil, nil, 269, 269, 269, 269, 269,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 281, 281, 281, 281, 281, 285, nil, nil,
- nil, nil, 285, 285, 285, 285, 285, 285, 285, 285,
- 365, 365, nil, nil, nil, 378, 365, 365, 365, 365,
- 365, 365, 365, 365, nil, nil, nil, nil, 381, 381,
- nil, nil, nil, nil, nil, 285, 285, 285, 285, 285,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 365,
- 365, 365, 365, 365, 378, nil, nil, nil, nil, 378,
- 378, 378, 378, 378, 378, 378, 378, 381, nil, nil,
- nil, nil, 381, 381, 381, 381, 381, 381, 381, 381,
- 383, 383, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 378, 378, 378, 378, 378, nil, nil, nil,
- nil, nil, nil, nil, nil, 381, 381, 381, 381, 381,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 383,
- nil, nil, nil, nil, 383, 383, 383, 383, 383, 383,
- 383, 383, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 388, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 383, 383, 383,
- 383, 383, 388, 388, 388, 388, 388, 388, 388, 388,
- 388, 388, 388, 388, 388, 388, 388, 388, 388, 388,
- nil, nil, nil, 388, nil, 388, nil, 391, 388, 388,
- 388, 388, 388, 388, 388, 388, nil, nil, nil, nil,
- 412, 412, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 388, 388, 388, 388, 388, 391, nil, nil, nil,
- nil, 391, 391, 391, 391, 391, 391, 391, 391, 412,
- nil, nil, nil, nil, 412, 412, 412, 412, 412, 412,
- 412, 412, 413, 413, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 391, 391, 391, 391, 391, 416,
- 416, nil, nil, nil, nil, nil, nil, 412, 412, 412,
- 412, 412, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 413, nil, nil, nil, nil, 413, 413, 413, 413,
- 413, 413, 413, 413, nil, nil, nil, nil, 416, nil,
- nil, nil, nil, 416, 416, 416, 416, 416, 416, 416,
- 416, 428, 428, nil, nil, nil, nil, nil, nil, 413,
- 413, 413, 413, 413, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 416, 416, 416, 416,
- 416, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 428, nil, nil, nil, nil, 428, 428, 428, 428, 428,
- 428, 428, 428, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 277, 277, nil, nil, nil, nil, nil, nil, 428, 428,
- 428, 428, 428, 277, 277, 277, 277, 277, 277, 277,
- 277, 277, 277, 277, 277, 277, 277, 277, 277, 277,
- 277, 277, 277, 277, 277, 277, 277, 277, 72, 277,
- nil, 277, nil, nil, nil, nil, nil, nil, nil, nil,
- 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
- 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
- 72, 72, 72, 72, nil, nil, nil, 277, 72, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 1, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 72, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 41,
- nil, nil, nil, 1, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 41, 41, 41, 41, 41,
- 41, 41, 41, 41, 41, 41, 41, 41, 41, 41,
- 41, 41, 41, 41, 41, 41, 41, 41, 41, 41,
- 62, nil, nil, nil, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 62, 62, 62,
- 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
- 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
- 62, 78, nil, nil, nil, 62, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 78, 78, 78, 78, 78, 78, 78, 78,
- 78, 78, 78, 78, 78, 78, 78, 78, 78, 78,
- 172, nil, nil, nil, nil, 78, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 172, 172, 172, 172, 172, 172, 172, 172, 172,
- 172, 172, 172, 172, 172, 172, 172, 172, 172, 181,
- nil, nil, nil, nil, 172, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 181, 181, 181, 181, 181, 181, 181, 181, 181, 181,
- 181, 181, 181, 181, 181, 181, 181, 181, 312, 312,
- nil, nil, nil, 181, nil, nil, nil, nil, nil, nil,
- nil, 312, 312, 312, 312, 312, 312, 312, 312, 312,
- 312, 312, 312, 312, 312, 312, 312, 312, 312, 312,
- 312, 312, 312, 312, 312, 312, 366, 312, nil, 312,
- nil, nil, nil, nil, nil, nil, nil, nil, 366, 366,
- 366, 366, 366, 366, 366, 366, 366, 366, 366, 366,
- 366, 366, 366, 366, 366, 366, 366, 366, 366, 366,
- 366, 366, nil, nil, nil, nil, 366, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, nil, nil, nil, nil, 0, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- nil, nil, nil, nil, 6, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, nil,
- nil, nil, nil, 7, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, nil, nil,
- nil, nil, 8, 9, 9, 9, 9, 9, 9, 9,
- 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
- 9, 9, 9, 9, 9, 9, 9, nil, nil, nil,
- nil, 9, 275, 275, 275, 275, 275, 275, 275, 275,
- 275, 275, 275, 275, 275, 275, 275, 275, 275, 275,
- 275, 275, 275, 275, 275, 275, nil, nil, nil, 275,
- 275, 56, 56, 56, 56, 56, 56, 56, 56, 56,
- 56, 56, 56, 56, 56, 56, 56, 56, 56, nil,
- nil, nil, nil, nil, 56, 77, 77, 77, 77, 77,
- 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
- 77, 77, 77, nil, nil, nil, nil, nil, 77, 81,
- 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
- 81, 81, 81, 81, 81, 81, 81, nil, nil, nil,
- nil, nil, 81, 82, 82, 82, 82, 82, 82, 82,
- 82, 82, 82, 82, 82, 82, 82, 82, 82, 82,
- 82, nil, nil, nil, nil, nil, 82, 83, 83, 83,
- 83, 83, 83, 83, 83, 83, 83, 83, 83, 83,
- 83, 83, 83, 83, 83, nil, nil, nil, nil, nil,
- 83 ]
-
-racc_action_pointer = [
- 4005, 3703, nil, nil, nil, 295, 4034, 4063, 4092, 4121,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 126, nil, nil, 161, nil, nil, nil, nil, nil, 8,
- nil, 3744, 160, nil, nil, 9, 34, nil, 159, 354,
- nil, nil, nil, nil, nil, 12, 4174, 55, 57, -20,
- 129, nil, 3785, nil, nil, -3, 353, 1318, nil, 411,
- 59, 2026, 3638, 127, 366, nil, nil, 4198, 3825, nil,
- 179, 4222, 4246, 4270, 104, 52, nil, 171, nil, 157,
- nil, nil, nil, nil, nil, nil, nil, nil, 248, 2039,
- 250, 279, 85, nil, nil, nil, nil, 163, nil, 275,
- 277, 294, 143, 299, 20, 332, 348, 1795, nil, 267,
- 1389, nil, 1333, 2082, 2095, 2138, 2151, nil, nil, nil,
- nil, nil, nil, 216, nil, 15, 24, 123, 350, 156,
- 305, 293, 314, 340, 205, nil, nil, nil, nil, nil,
- nil, 398, nil, nil, 1404, nil, nil, 2205, 377, nil,
- 2224, 410, 448, 51, nil, 440, nil, 469, nil, nil,
- nil, nil, 3864, nil, nil, 175, nil, 468, 2248, nil,
- nil, 3903, 58, nil, 63, 2267, 59, 201, 469, nil,
- nil, 259, 317, nil, nil, nil, 2291, 2310, 2334, 2353,
- 470, 460, 1864, 467, 468, nil, nil, 216, nil, nil,
- -2, 224, 466, 467, 2377, 2441, 393, 394, nil, nil,
- 1475, nil, nil, nil, 1539, nil, 2484, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 2497, 2540,
- 2553, 2596, 2609, 2652, 2665, 2708, 2721, 2764, 2777, 2820,
- 2833, 2876, 2889, 2932, 2945, 2988, 3001, 157, 1595, nil,
- 143, nil, 3044, 396, 437, nil, 3057, 438, nil, 3109,
- 454, nil, nil, nil, 416, 4150, 50, 3601, nil, nil,
- 123, 3126, nil, nil, 237, 3169, nil, nil, nil, 67,
- nil, nil, nil, nil, 68, nil, 375, nil, nil, nil,
- 317, 318, 330, 502, 228, 1918, 1931, nil, nil, nil,
- nil, 175, 3939, nil, nil, 1608, 28, 331, nil, nil,
- nil, nil, nil, nil, 502, 503, nil, nil, nil, nil,
- 117, 180, 248, 253, 336, 348, 358, 363, 388, 401,
- 374, 458, 445, 446, 451, 47, nil, 1664, nil, nil,
- nil, 478, nil, nil, 479, nil, 480, nil, nil, nil,
- nil, 176, 518, 519, nil, 3183, 3976, 483, nil, 484,
- nil, nil, nil, nil, nil, 433, 491, 549, 3226, 1974,
- 233, 3239, 262, 3291, 1681, nil, nil, nil, 3355, 527,
- 528, 3398, nil, 1737, nil, nil, nil, nil, nil, nil,
- 487, nil, 488, 526, nil, nil, nil, 526, nil, nil,
- 332, 263, 3411, 3463, 334, 607, 3480, 343, 665, 218,
- nil, nil, nil, nil, nil, nil, 723, 531, 3532, 346,
- 781, 348, 839, 897, nil, 349, 955, 1013, nil, 1752,
- nil, nil, nil, 375, 1071, 1129, nil, 1187, nil, nil,
- 1245, nil, nil, nil, 1303, nil, nil, nil, nil, nil ]
-
-racc_action_default = [
- -265, -265, -1, -3, -4, -265, -53, -55, -57, -59,
- -64, -65, -66, -67, -68, -69, -70, -71, -72, -73,
- -74, -75, -76, -77, -78, -79, -80, -81, -82, -83,
- -265, -89, -90, -265, -115, -116, -117, -118, -166, -265,
- -2, -62, -265, -51, -60, -265, -120, -121, -265, -136,
- -258, -52, -54, -56, -58, -86, -265, -88, -107, -265,
- -110, 460, -265, -6, -7, -265, -265, -265, -50, -265,
- -119, -265, -265, -265, -135, -138, -139, -265, -265, -91,
- -265, -95, -97, -265, -265, -265, -111, -113, -262, -265,
- -5, -8, -9, -10, -11, -12, -13, -14, -179, -265,
- -265, -83, -265, -20, -21, -23, -24, -265, -26, -265,
- -265, -265, -265, -265, -265, -265, -265, -265, -180, -181,
- -265, -184, -198, -265, -265, -265, -265, -204, -205, -206,
- -207, -208, -209, -210, -212, -216, -219, -222, -227, -230,
- -232, -234, -236, -238, -240, -242, -255, -259, -260, -261,
- -264, -62, -63, -167, -265, -179, -61, -265, -265, -126,
- -265, -205, -265, -265, -134, -141, -143, -147, -148, -122,
- -137, -140, -265, -85, -92, -265, -98, -100, -265, -94,
- -96, -265, -265, -104, -265, -265, -265, -265, -265, -210,
- -257, -265, -265, -19, -22, -25, -265, -265, -265, -265,
- -265, -265, -265, -265, -265, -45, -46, -265, -48, -263,
- -151, -265, -265, -265, -265, -265, -265, -265, -190, -191,
- -265, -199, -200, -201, -265, -202, -265, -244, -245, -246,
- -247, -248, -249, -250, -251, -252, -253, -254, -265, -265,
- -265, -265, -265, -265, -265, -265, -265, -265, -265, -265,
- -265, -265, -265, -265, -265, -265, -265, -265, -265, -171,
- -265, -175, -265, -265, -265, -124, -265, -205, -125, -265,
- -265, -131, -132, -133, -265, -265, -152, -265, -145, -146,
- -154, -265, -84, -93, -265, -265, -102, -87, -103, -265,
- -106, -112, -114, -108, -265, -15, -265, -17, -18, -256,
- -265, -265, -265, -265, -265, -265, -265, -44, -49, -47,
- -150, -152, -265, -182, -183, -265, -265, -265, -187, -194,
- -196, -197, -188, -189, -265, -265, -243, -213, -214, -215,
- -217, -218, -220, -221, -223, -224, -225, -226, -228, -229,
- -231, -233, -235, -237, -239, -265, -168, -265, -170, -174,
- -176, -265, -178, -123, -265, -130, -265, -128, -149, -142,
- -144, -153, -265, -265, -165, -265, -265, -265, -159, -205,
- -99, -101, -105, -109, -16, -265, -265, -265, -265, -265,
- -265, -265, -265, -265, -265, -211, -185, -186, -265, -265,
- -203, -265, -169, -265, -173, -177, -129, -127, -155, -164,
- -265, -157, -205, -265, -163, -158, -161, -27, -29, -30,
- -265, -265, -265, -265, -265, -265, -265, -265, -265, -265,
- -195, -241, -172, -156, -160, -162, -265, -265, -265, -265,
- -265, -265, -265, -265, -39, -265, -265, -265, -43, -265,
- -192, -28, -31, -265, -265, -265, -35, -265, -37, -38,
- -265, -41, -42, -193, -265, -33, -34, -36, -40, -32 ]
-
-racc_goto_table = [
- 47, 152, 57, 162, 75, 60, 70, 319, 153, 5,
- 5, 257, 158, 176, 174, 51, 52, 53, 54, 76,
- 393, 291, 279, 361, 74, 55, 82, 1, 58, 170,
- 62, 104, 179, 180, 190, 223, 63, 101, 102, 41,
- 47, 76, 188, 47, 171, 64, 157, 82, 82, 78,
- 156, 82, 82, 82, 213, 85, 175, 90, 361, 163,
- 98, 47, 2, 40, 47, 310, 91, 168, 194, 105,
- 172, 210, 330, 331, 101, 47, 181, 332, 333, 360,
- 182, 167, 338, 339, 200, 186, 204, 350, 317, 340,
- 82, 334, 335, 336, 337, 153, 341, 98, 264, 342,
- 151, 270, 343, 151, 344, 226, 105, 98, 174, 203,
- nil, nil, 393, 190, 177, 212, nil, 174, nil, 81,
- 190, 286, 276, nil, nil, nil, 291, 171, 292, nil,
- 76, 291, nil, nil, nil, 269, nil, 299, nil, nil,
- 81, 81, 82, nil, 81, 81, 81, nil, 327, 328,
- 329, 82, nil, nil, 324, nil, 320, nil, 325, 200,
- nil, nil, 47, 200, 200, 311, 210, 326, nil, nil,
- 201, 210, nil, nil, nil, 210, nil, nil, nil, nil,
- 420, nil, 98, 81, nil, 82, 98, 98, nil, nil,
- 82, nil, 348, nil, 82, nil, nil, 190, nil, 153,
- nil, 278, nil, nil, nil, 351, 306, 354, nil, nil,
- 356, 322, 323, nil, nil, 212, nil, 370, nil, 212,
- 190, nil, 367, nil, nil, 385, nil, nil, 371, nil,
- nil, nil, 276, nil, nil, 81, nil, 70, nil, 171,
- nil, 419, nil, nil, 81, 295, nil, nil, nil, 297,
- 298, nil, nil, nil, nil, nil, nil, nil, 352, nil,
- nil, nil, nil, nil, nil, nil, nil, 311, 200, 358,
- nil, 47, 47, nil, nil, nil, nil, nil, 81, 47,
- nil, 394, nil, 81, 167, nil, 167, 81, 153, nil,
- nil, 98, nil, nil, nil, nil, nil, 403, nil, nil,
- nil, nil, nil, nil, 207, nil, 400, 211, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 177, nil,
- nil, 167, nil, nil, nil, 153, 421, 422, nil, 320,
- nil, nil, nil, nil, 153, nil, nil, nil, nil, 210,
- nil, nil, nil, nil, nil, nil, nil, 200, 200, 200,
- nil, nil, nil, nil, 374, nil, nil, nil, 82, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 98, 98, 98, 394, nil, 167, nil, nil, nil, nil,
- 153, nil, nil, nil, 300, 301, 302, 200, nil, 304,
- 200, nil, nil, nil, nil, nil, nil, nil, 200, nil,
- nil, 316, 200, nil, 200, 200, nil, 211, 200, 200,
- 98, 211, nil, 98, nil, nil, 200, 200, nil, 200,
- nil, 98, 200, nil, nil, 98, 200, 98, 98, nil,
- nil, 98, 98, 407, 408, 409, nil, nil, 189, 98,
- 98, nil, 98, 345, nil, 98, nil, nil, nil, 98,
- nil, 81, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 221, 222, 189, 225, nil, nil, nil, nil,
- nil, nil, nil, 434, nil, nil, 438, nil, nil, nil,
- nil, nil, nil, nil, 441, nil, nil, nil, 446, nil,
- 448, 449, 380, 382, 451, 452, nil, nil, nil, nil,
- nil, nil, 455, 456, nil, 457, nil, nil, 458, nil,
- nil, nil, 459, nil, nil, nil, nil, 189, nil, nil,
- nil, nil, nil, nil, 189, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 410, 411, nil, 414, nil,
- 417, nil, nil, nil, nil, nil, nil, 189, 189, 189,
- 189, 189, 189, 189, 189, 189, 189, 189, 189, 189,
- 189, 189, 189, 189, 189, nil, nil, nil, nil, 429,
- 431, 189, nil, 435, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 443, nil, nil, nil, nil,
- nil, nil, nil, nil, 189, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 189, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 189 ]
-
-racc_goto_check = [
- 15, 27, 17, 43, 39, 17, 40, 59, 42, 5,
- 5, 50, 42, 35, 32, 5, 5, 5, 5, 24,
- 51, 37, 47, 49, 41, 15, 24, 1, 15, 39,
- 7, 19, 33, 33, 73, 62, 8, 17, 18, 6,
- 15, 24, 16, 15, 24, 4, 41, 24, 24, 31,
- 26, 24, 24, 24, 48, 36, 34, 8, 49, 44,
- 15, 15, 2, 2, 15, 47, 4, 15, 19, 4,
- 31, 33, 63, 63, 17, 15, 31, 64, 64, 46,
- 36, 5, 66, 66, 17, 36, 17, 53, 58, 67,
- 24, 65, 65, 65, 65, 42, 68, 15, 42, 69,
- 6, 42, 70, 6, 71, 74, 4, 15, 32, 15,
- nil, nil, 51, 73, 6, 8, nil, 32, nil, 23,
- 73, 16, 39, nil, nil, nil, 37, 24, 16, nil,
- 24, 37, nil, nil, nil, 41, nil, 42, nil, nil,
- 23, 23, 24, nil, 23, 23, 23, nil, 62, 62,
- 62, 24, nil, nil, 48, nil, 42, nil, 48, 17,
- nil, nil, 15, 17, 17, 39, 33, 42, nil, nil,
- 9, 33, nil, nil, nil, 33, nil, nil, nil, nil,
- 59, nil, 15, 23, nil, 24, 15, 15, nil, nil,
- 24, nil, 27, nil, 24, nil, nil, 73, nil, 42,
- nil, 6, nil, nil, nil, 16, 4, 42, nil, nil,
- 42, 15, 15, nil, nil, 8, nil, 35, nil, 8,
- 73, nil, 42, nil, nil, 62, nil, nil, 16, nil,
- nil, nil, 39, nil, nil, 23, nil, 40, nil, 24,
- nil, 50, nil, nil, 23, 9, nil, nil, nil, 9,
- 9, nil, nil, nil, nil, nil, nil, nil, 15, nil,
- nil, nil, nil, nil, nil, nil, nil, 39, 17, 15,
- nil, 15, 15, nil, nil, nil, nil, nil, 23, 15,
- nil, 27, nil, 23, 5, nil, 5, 23, 42, nil,
- nil, 15, nil, nil, nil, nil, nil, 43, nil, nil,
- nil, nil, nil, nil, 20, nil, 42, 20, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 6, nil,
- nil, 5, nil, nil, nil, 42, 73, 27, nil, 42,
- nil, nil, nil, nil, 42, nil, nil, nil, nil, 33,
- nil, nil, nil, nil, nil, nil, nil, 17, 17, 17,
- nil, nil, nil, nil, 9, nil, nil, nil, 24, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 15, 15, 15, 27, nil, 5, nil, nil, nil, nil,
- 42, nil, nil, nil, 20, 20, 20, 17, nil, 20,
- 17, nil, nil, nil, nil, nil, nil, nil, 17, nil,
- nil, 20, 17, nil, 17, 17, nil, 20, 17, 17,
- 15, 20, nil, 15, nil, nil, 17, 17, nil, 17,
- nil, 15, 17, nil, nil, 15, 17, 15, 15, nil,
- nil, 15, 15, 9, 9, 9, nil, nil, 60, 15,
- 15, nil, 15, 20, nil, 15, nil, nil, nil, 15,
- nil, 23, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 60, 60, 60, 60, nil, nil, nil, nil,
- nil, nil, nil, 9, nil, nil, 9, nil, nil, nil,
- nil, nil, nil, nil, 9, nil, nil, nil, 9, nil,
- 9, 9, 20, 20, 9, 9, nil, nil, nil, nil,
- nil, nil, 9, 9, nil, 9, nil, nil, 9, nil,
- nil, nil, 9, nil, nil, nil, nil, 60, nil, nil,
- nil, nil, nil, nil, 60, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 20, 20, nil, 20, nil,
- 20, nil, nil, nil, nil, nil, nil, 60, 60, 60,
- 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,
- 60, 60, 60, 60, 60, nil, nil, nil, nil, 20,
- 20, 60, nil, 20, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 20, nil, nil, nil, nil,
- nil, nil, nil, nil, 60, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 60, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 60 ]
-
-racc_goto_pointer = [
- nil, 27, 62, nil, 4, 9, 34, -11, -5, 58,
- nil, nil, nil, nil, nil, -5, -57, -28, -27, -34,
- 187, nil, nil, 63, -30, nil, -19, -66, nil, nil,
- nil, -7, -64, -49, -24, -67, -4, -163, nil, -45,
- -39, -25, -59, -69, -13, nil, -196, -145, -66, -253,
- -143, -327, nil, -173, nil, nil, nil, nil, -127, -208,
- 339, nil, -90, -169, -166, -154, -167, -162, -156, -154,
- -152, -151, nil, -65, -28 ]
-
-racc_goto_default = [
- nil, nil, nil, 3, 4, 66, 73, nil, 93, 106,
- 92, 94, 95, 96, 97, 155, nil, 29, nil, nil,
- 107, 42, 6, 7, 8, 9, 44, 259, 27, 28,
- 30, nil, 79, 80, nil, nil, nil, 86, 87, 45,
- 46, nil, 146, 363, nil, 165, 166, 362, 321, 280,
- nil, 258, 260, 261, 121, 118, 119, 122, nil, nil,
- 133, 125, 134, 135, 136, 137, 138, 139, 140, 141,
- 142, 143, 144, 145, nil ]
-
-racc_reduce_table = [
- 0, 0, :racc_error,
- 1, 92, :_reduce_1,
- 2, 92, :_reduce_2,
- 1, 93, :_reduce_3,
- 1, 93, :_reduce_4,
- 4, 94, :_reduce_5,
- 3, 94, :_reduce_6,
- 1, 98, :_reduce_7,
- 2, 98, :_reduce_8,
- 1, 100, :_reduce_9,
- 1, 100, :_reduce_10,
- 1, 100, :_reduce_11,
- 1, 100, :_reduce_12,
- 1, 100, :_reduce_13,
- 1, 100, :_reduce_14,
- 3, 101, :_reduce_15,
- 4, 101, :_reduce_16,
- 3, 101, :_reduce_17,
- 3, 101, :_reduce_18,
- 3, 99, :_reduce_19,
- 2, 99, :_reduce_20,
- 1, 109, :_reduce_21,
- 2, 109, :_reduce_22,
- 1, 110, :_reduce_23,
- 1, 110, :_reduce_24,
- 2, 102, :_reduce_25,
- 1, 102, :_reduce_26,
- 5, 103, :_reduce_27,
- 7, 103, :_reduce_28,
- 5, 103, :_reduce_29,
- 5, 104, :_reduce_30,
- 7, 104, :_reduce_31,
- 9, 104, :_reduce_32,
- 8, 104, :_reduce_33,
- 8, 104, :_reduce_34,
- 7, 104, :_reduce_35,
- 8, 104, :_reduce_36,
- 7, 104, :_reduce_37,
- 7, 104, :_reduce_38,
- 6, 104, :_reduce_39,
- 8, 104, :_reduce_40,
- 7, 104, :_reduce_41,
- 7, 104, :_reduce_42,
- 6, 104, :_reduce_43,
- 3, 105, :_reduce_44,
- 2, 105, :_reduce_45,
- 2, 105, :_reduce_46,
- 3, 105, :_reduce_47,
- 2, 105, :_reduce_48,
- 3, 105, :_reduce_49,
- 3, 95, :_reduce_50,
- 2, 95, :_reduce_51,
- 2, 96, :_reduce_52,
- 1, 96, :_reduce_53,
- 2, 96, :_reduce_54,
- 1, 96, :_reduce_55,
- 2, 96, :_reduce_56,
- 1, 96, :_reduce_57,
- 2, 96, :_reduce_58,
- 1, 96, :_reduce_59,
- 1, 112, :_reduce_60,
- 3, 112, :_reduce_61,
- 1, 117, :_reduce_62,
- 3, 117, :_reduce_63,
- 1, 113, :_reduce_64,
- 1, 113, :_reduce_65,
- 1, 113, :_reduce_66,
- 1, 113, :_reduce_67,
- 1, 113, :_reduce_68,
- 1, 114, :_reduce_69,
- 1, 114, :_reduce_70,
- 1, 114, :_reduce_71,
- 1, 114, :_reduce_72,
- 1, 114, :_reduce_73,
- 1, 114, :_reduce_74,
- 1, 114, :_reduce_75,
- 1, 114, :_reduce_76,
- 1, 114, :_reduce_77,
- 1, 114, :_reduce_78,
- 1, 114, :_reduce_79,
- 1, 114, :_reduce_80,
- 1, 114, :_reduce_81,
- 1, 114, :_reduce_82,
- 1, 114, :_reduce_83,
- 5, 119, :_reduce_84,
- 4, 119, :_reduce_85,
- 2, 119, :_reduce_86,
- 5, 119, :_reduce_87,
- 2, 119, :_reduce_88,
- 1, 121, :_reduce_89,
- 1, 121, :_reduce_90,
- 1, 122, :_reduce_91,
- 2, 122, :_reduce_92,
- 3, 123, :_reduce_93,
- 2, 124, :_reduce_94,
- 1, 124, :_reduce_95,
- 2, 124, :_reduce_96,
- 1, 124, :_reduce_97,
- 1, 125, :_reduce_98,
- 3, 125, :_reduce_99,
- 1, 126, :_reduce_100,
- 3, 126, :_reduce_101,
- 2, 126, :_reduce_102,
- 5, 120, :_reduce_103,
- 4, 120, :_reduce_104,
- 6, 120, :_reduce_105,
- 5, 120, :_reduce_106,
- 2, 120, :_reduce_107,
- 5, 120, :_reduce_108,
- 6, 120, :_reduce_109,
- 2, 120, :_reduce_110,
- 1, 127, :_reduce_111,
- 3, 127, :_reduce_112,
- 1, 128, :_reduce_113,
- 3, 128, :_reduce_114,
- 1, 115, :_reduce_115,
- 1, 115, :_reduce_116,
- 1, 115, :_reduce_117,
- 1, 116, :_reduce_118,
- 2, 97, :_reduce_119,
- 1, 97, :_reduce_120,
- 1, 131, :_reduce_121,
- 3, 131, :_reduce_122,
- 5, 131, :_reduce_123,
- 4, 131, :_reduce_124,
- 4, 131, :_reduce_125,
- 3, 131, :_reduce_126,
- 6, 131, :_reduce_127,
- 5, 131, :_reduce_128,
- 6, 131, :_reduce_129,
- 5, 131, :_reduce_130,
- 4, 131, :_reduce_131,
- 4, 131, :_reduce_132,
- 4, 131, :_reduce_133,
- 3, 131, :_reduce_134,
- 2, 130, :_reduce_135,
- 1, 130, :_reduce_136,
- 3, 130, :_reduce_137,
- 2, 130, :_reduce_138,
- 1, 132, :_reduce_139,
- 2, 132, :_reduce_140,
- 1, 134, :_reduce_141,
- 3, 134, :_reduce_142,
- 1, 136, :_reduce_143,
- 3, 136, :_reduce_144,
- 2, 137, :_reduce_145,
- 2, 137, :_reduce_146,
- 1, 137, :_reduce_147,
- 1, 135, :_reduce_148,
- 3, 135, :_reduce_149,
- 2, 139, :_reduce_150,
- 1, 139, :_reduce_151,
- 1, 138, :_reduce_152,
- 2, 138, :_reduce_153,
- 1, 138, :_reduce_154,
- 3, 140, :_reduce_155,
- 4, 140, :_reduce_156,
- 3, 140, :_reduce_157,
- 3, 140, :_reduce_158,
- 2, 140, :_reduce_159,
- 4, 140, :_reduce_160,
- 3, 140, :_reduce_161,
- 4, 140, :_reduce_162,
- 3, 140, :_reduce_163,
- 3, 140, :_reduce_164,
- 2, 140, :_reduce_165,
- 1, 108, :_reduce_166,
- 1, 118, :_reduce_167,
- 3, 118, :_reduce_168,
- 4, 118, :_reduce_169,
- 2, 141, :_reduce_170,
- 1, 141, :_reduce_171,
- 4, 141, :_reduce_172,
- 3, 141, :_reduce_173,
- 2, 142, :_reduce_174,
- 1, 143, :_reduce_175,
- 2, 143, :_reduce_176,
- 3, 144, :_reduce_177,
- 2, 144, :_reduce_178,
- 1, 145, :_reduce_179,
- 1, 145, :_reduce_180,
- 1, 145, :_reduce_181,
- 3, 145, :_reduce_182,
- 3, 145, :_reduce_183,
- 1, 148, :_reduce_184,
- 4, 148, :_reduce_185,
- 4, 148, :_reduce_186,
- 3, 148, :_reduce_187,
- 3, 148, :_reduce_188,
- 3, 148, :_reduce_189,
- 2, 148, :_reduce_190,
- 2, 148, :_reduce_191,
- 6, 148, :_reduce_192,
- 7, 148, :_reduce_193,
- 1, 149, :_reduce_194,
- 3, 149, :_reduce_195,
- 1, 150, :_reduce_196,
- 1, 150, :_reduce_197,
- 1, 151, :_reduce_198,
- 2, 151, :_reduce_199,
- 2, 151, :_reduce_200,
- 2, 151, :_reduce_201,
- 2, 151, :_reduce_202,
- 4, 151, :_reduce_203,
- 1, 152, :_reduce_204,
- 1, 152, :_reduce_205,
- 1, 152, :_reduce_206,
- 1, 152, :_reduce_207,
- 1, 152, :_reduce_208,
- 1, 152, :_reduce_209,
- 1, 153, :_reduce_210,
- 4, 153, :_reduce_211,
- 1, 154, :_reduce_212,
- 3, 154, :_reduce_213,
- 3, 154, :_reduce_214,
- 3, 154, :_reduce_215,
- 1, 155, :_reduce_216,
- 3, 155, :_reduce_217,
- 3, 155, :_reduce_218,
- 1, 156, :_reduce_219,
- 3, 156, :_reduce_220,
- 3, 156, :_reduce_221,
- 1, 157, :_reduce_222,
- 3, 157, :_reduce_223,
- 3, 157, :_reduce_224,
- 3, 157, :_reduce_225,
- 3, 157, :_reduce_226,
- 1, 158, :_reduce_227,
- 3, 158, :_reduce_228,
- 3, 158, :_reduce_229,
- 1, 159, :_reduce_230,
- 3, 159, :_reduce_231,
- 1, 160, :_reduce_232,
- 3, 160, :_reduce_233,
- 1, 161, :_reduce_234,
- 3, 161, :_reduce_235,
- 1, 162, :_reduce_236,
- 3, 162, :_reduce_237,
- 1, 163, :_reduce_238,
- 3, 163, :_reduce_239,
- 1, 164, :_reduce_240,
- 5, 164, :_reduce_241,
- 1, 133, :_reduce_242,
- 3, 133, :_reduce_243,
- 1, 165, :_reduce_244,
- 1, 165, :_reduce_245,
- 1, 165, :_reduce_246,
- 1, 165, :_reduce_247,
- 1, 165, :_reduce_248,
- 1, 165, :_reduce_249,
- 1, 165, :_reduce_250,
- 1, 165, :_reduce_251,
- 1, 165, :_reduce_252,
- 1, 165, :_reduce_253,
- 1, 165, :_reduce_254,
- 1, 111, :_reduce_255,
- 3, 111, :_reduce_256,
- 1, 107, :_reduce_257,
- 1, 106, :_reduce_258,
- 1, 146, :_reduce_259,
- 1, 146, :_reduce_260,
- 1, 146, :_reduce_261,
- 1, 129, :_reduce_262,
- 2, 147, :_reduce_263,
- 1, 147, :_reduce_264 ]
-
-racc_reduce_n = 265
-
-racc_shift_n = 460
-
-racc_token_table = {
- false => 0,
- :error => 1,
- :COLON => 2,
- :CASE => 3,
- :DEFAULT => 4,
- :LBRACE => 5,
- :RBRACE => 6,
- :SEMICOLON => 7,
- :IF => 8,
- :LPAREN => 9,
- :RPAREN => 10,
- :ELSE => 11,
- :SWITCH => 12,
- :WHILE => 13,
- :DO => 14,
- :FOR => 15,
- :GOTO => 16,
- :CONTINUE => 17,
- :BREAK => 18,
- :RETURN => 19,
- :COMMA => 20,
- :EQ => 21,
- :TYPEDEF => 22,
- :EXTERN => 23,
- :STATIC => 24,
- :AUTO => 25,
- :REGISTER => 26,
- :VOID => 27,
- :CHAR => 28,
- :SHORT => 29,
- :INT => 30,
- :LONG => 31,
- :FLOAT => 32,
- :DOUBLE => 33,
- :SIGNED => 34,
- :UNSIGNED => 35,
- :BOOL => 36,
- :COMPLEX => 37,
- :IMAGINARY => 38,
- :STRUCT => 39,
- :UNION => 40,
- :ENUM => 41,
- :CONST => 42,
- :RESTRICT => 43,
- :VOLATILE => 44,
- :INLINE => 45,
- :LBRACKET => 46,
- :RBRACKET => 47,
- :MUL => 48,
- :ELLIPSIS => 49,
- :TYPENAME => 50,
- :DOT => 51,
- :ARROW => 52,
- :INC => 53,
- :DEC => 54,
- :SIZEOF => 55,
- :AND => 56,
- :ADD => 57,
- :SUB => 58,
- :NOT => 59,
- :BANG => 60,
- :DIV => 61,
- :MOD => 62,
- :LSHIFT => 63,
- :RSHIFT => 64,
- :LT => 65,
- :GT => 66,
- :LEQ => 67,
- :GEQ => 68,
- :EQEQ => 69,
- :NEQ => 70,
- :XOR => 71,
- :OR => 72,
- :ANDAND => 73,
- :OROR => 74,
- :QUESTION => 75,
- :MULEQ => 76,
- :DIVEQ => 77,
- :MODEQ => 78,
- :ADDEQ => 79,
- :SUBEQ => 80,
- :LSHIFTEQ => 81,
- :RSHIFTEQ => 82,
- :ANDEQ => 83,
- :XOREQ => 84,
- :OREQ => 85,
- :ID => 86,
- :ICON => 87,
- :FCON => 88,
- :CCON => 89,
- :SCON => 90 }
-
-racc_nt_base = 91
-
-racc_use_result_var = true
-
-Racc_arg = [
- racc_action_table,
- racc_action_check,
- racc_action_default,
- racc_action_pointer,
- racc_goto_table,
- racc_goto_check,
- racc_goto_default,
- racc_goto_pointer,
- racc_nt_base,
- racc_reduce_table,
- racc_token_table,
- racc_shift_n,
- racc_reduce_n,
- racc_use_result_var ]
-Ractor.make_shareable(Racc_arg) if defined?(Ractor)
-
-Racc_token_to_s_table = [
- "$end",
- "error",
- "COLON",
- "CASE",
- "DEFAULT",
- "LBRACE",
- "RBRACE",
- "SEMICOLON",
- "IF",
- "LPAREN",
- "RPAREN",
- "ELSE",
- "SWITCH",
- "WHILE",
- "DO",
- "FOR",
- "GOTO",
- "CONTINUE",
- "BREAK",
- "RETURN",
- "COMMA",
- "EQ",
- "TYPEDEF",
- "EXTERN",
- "STATIC",
- "AUTO",
- "REGISTER",
- "VOID",
- "CHAR",
- "SHORT",
- "INT",
- "LONG",
- "FLOAT",
- "DOUBLE",
- "SIGNED",
- "UNSIGNED",
- "BOOL",
- "COMPLEX",
- "IMAGINARY",
- "STRUCT",
- "UNION",
- "ENUM",
- "CONST",
- "RESTRICT",
- "VOLATILE",
- "INLINE",
- "LBRACKET",
- "RBRACKET",
- "MUL",
- "ELLIPSIS",
- "TYPENAME",
- "DOT",
- "ARROW",
- "INC",
- "DEC",
- "SIZEOF",
- "AND",
- "ADD",
- "SUB",
- "NOT",
- "BANG",
- "DIV",
- "MOD",
- "LSHIFT",
- "RSHIFT",
- "LT",
- "GT",
- "LEQ",
- "GEQ",
- "EQEQ",
- "NEQ",
- "XOR",
- "OR",
- "ANDAND",
- "OROR",
- "QUESTION",
- "MULEQ",
- "DIVEQ",
- "MODEQ",
- "ADDEQ",
- "SUBEQ",
- "LSHIFTEQ",
- "RSHIFTEQ",
- "ANDEQ",
- "XOREQ",
- "OREQ",
- "ID",
- "ICON",
- "FCON",
- "CCON",
- "SCON",
- "$start",
- "translation_unit",
- "external_declaration",
- "function_definition",
- "declaration",
- "declaration_specifiers",
- "declarator",
- "declaration_list",
- "compound_statement",
- "statement",
- "labeled_statement",
- "expression_statement",
- "selection_statement",
- "iteration_statement",
- "jump_statement",
- "identifier",
- "constant_expression",
- "typedef_name",
- "block_item_list",
- "block_item",
- "expression",
- "init_declarator_list",
- "storage_class_specifier",
- "type_specifier",
- "type_qualifier",
- "function_specifier",
- "init_declarator",
- "initializer",
- "struct_or_union_specifier",
- "enum_specifier",
- "struct_or_union",
- "struct_declaration_list",
- "struct_declaration",
- "specifier_qualifier_list",
- "struct_declarator_list",
- "struct_declarator",
- "enumerator_list",
- "enumerator",
- "enumeration_constant",
- "pointer",
- "direct_declarator",
- "type_qualifier_list",
- "assignment_expression",
- "parameter_type_list",
- "identifier_list",
- "parameter_list",
- "parameter_declaration",
- "abstract_declarator",
- "type_name",
- "direct_abstract_declarator",
- "initializer_list",
- "designation",
- "designator_list",
- "designator",
- "primary_expression",
- "constant",
- "string_literal",
- "postfix_expression",
- "argument_expression_list",
- "argument_expression",
- "unary_expression",
- "unary_operator",
- "cast_expression",
- "multiplicative_expression",
- "additive_expression",
- "shift_expression",
- "relational_expression",
- "equality_expression",
- "and_expression",
- "exclusive_or_expression",
- "inclusive_or_expression",
- "logical_and_expression",
- "logical_or_expression",
- "conditional_expression",
- "assignment_operator" ]
-Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)
-
-Racc_debug_parser = false
-
-##### State transition tables end #####
-
-# reduce 0 omitted
-
-module_eval(<<'.,.,', 'cast.y', 32)
- def _reduce_1(val, _values, result)
- result = TranslationUnit.new_at(val[0].pos, NodeChain[val[0]])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 33)
- def _reduce_2(val, _values, result)
- result = val[0]; result.entities << val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 37)
- def _reduce_3(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 38)
- def _reduce_4(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 42)
- def _reduce_5(val, _values, result)
- result = make_function_def(val[0][0], val[0][1], val[1], val[2], val[3])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 43)
- def _reduce_6(val, _values, result)
- result = make_function_def(val[0][0], val[0][1], val[1], nil , val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 47)
- def _reduce_7(val, _values, result)
- result = [val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 48)
- def _reduce_8(val, _values, result)
- result = val[0] << val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 54)
- def _reduce_9(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 55)
- def _reduce_10(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 56)
- def _reduce_11(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 57)
- def _reduce_12(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 58)
- def _reduce_13(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 59)
- def _reduce_14(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 63)
- def _reduce_15(val, _values, result)
- val[2].labels.unshift(PlainLabel.new_at(val[0].pos, val[0].val)); result = val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 64)
- def _reduce_16(val, _values, result)
- val[3].labels.unshift(Case .new_at(val[0].pos, val[1] )); result = val[3]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 65)
- def _reduce_17(val, _values, result)
- val[2].labels.unshift(Default .new_at(val[0].pos )); result = val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 67)
- def _reduce_18(val, _values, result)
- val[2].labels.unshift(PlainLabel.new_at(val[0].pos, val[0].name)); result = val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 71)
- def _reduce_19(val, _values, result)
- result = Block.new_at(val[0].pos, val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 72)
- def _reduce_20(val, _values, result)
- result = Block.new_at(val[0].pos )
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 76)
- def _reduce_21(val, _values, result)
- result = NodeChain[val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 77)
- def _reduce_22(val, _values, result)
- result = val[0] << val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 81)
- def _reduce_23(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 82)
- def _reduce_24(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 86)
- def _reduce_25(val, _values, result)
- result = ExpressionStatement.new_at(val[0].pos, val[0])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 87)
- def _reduce_26(val, _values, result)
- result = ExpressionStatement.new_at(val[0].pos )
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 91)
- def _reduce_27(val, _values, result)
- result = If .new_at(val[0].pos, val[2], val[4] )
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 92)
- def _reduce_28(val, _values, result)
- result = If .new_at(val[0].pos, val[2], val[4], val[6])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 93)
- def _reduce_29(val, _values, result)
- result = Switch.new_at(val[0].pos, val[2], val[4] )
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 97)
- def _reduce_30(val, _values, result)
- result = While.new_at(val[0].pos, val[2], val[4] )
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 98)
- def _reduce_31(val, _values, result)
- result = While.new_at(val[0].pos, val[4], val[1], :do => true )
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 99)
- def _reduce_32(val, _values, result)
- result = For.new_at(val[0].pos, val[2], val[4], val[6], val[8])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 100)
- def _reduce_33(val, _values, result)
- result = For.new_at(val[0].pos, val[2], val[4], nil , val[7])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 101)
- def _reduce_34(val, _values, result)
- result = For.new_at(val[0].pos, val[2], nil , val[5], val[7])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 102)
- def _reduce_35(val, _values, result)
- result = For.new_at(val[0].pos, val[2], nil , nil , val[6])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 103)
- def _reduce_36(val, _values, result)
- result = For.new_at(val[0].pos, nil , val[3], val[5], val[7])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 104)
- def _reduce_37(val, _values, result)
- result = For.new_at(val[0].pos, nil , val[3], nil , val[6])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 105)
- def _reduce_38(val, _values, result)
- result = For.new_at(val[0].pos, nil , nil , val[4], val[6])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 106)
- def _reduce_39(val, _values, result)
- result = For.new_at(val[0].pos, nil , nil , nil , val[5])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 107)
- def _reduce_40(val, _values, result)
- result = For.new_at(val[0].pos, val[2], val[3], val[5], val[7])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 108)
- def _reduce_41(val, _values, result)
- result = For.new_at(val[0].pos, val[2], val[3], nil , val[6])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 109)
- def _reduce_42(val, _values, result)
- result = For.new_at(val[0].pos, val[2], nil , val[4], val[6])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 110)
- def _reduce_43(val, _values, result)
- result = For.new_at(val[0].pos, val[2], nil , nil , val[5])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 114)
- def _reduce_44(val, _values, result)
- result = Goto .new_at(val[0].pos, val[1].val)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 115)
- def _reduce_45(val, _values, result)
- result = Continue.new_at(val[0].pos )
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 116)
- def _reduce_46(val, _values, result)
- result = Break .new_at(val[0].pos )
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 117)
- def _reduce_47(val, _values, result)
- result = Return .new_at(val[0].pos, val[1] )
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 118)
- def _reduce_48(val, _values, result)
- result = Return .new_at(val[0].pos )
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 120)
- def _reduce_49(val, _values, result)
- result = Goto .new_at(val[0].pos, val[1].name)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 126)
- def _reduce_50(val, _values, result)
- result = make_declaration(val[0][0], val[0][1], val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 127)
- def _reduce_51(val, _values, result)
- result = make_declaration(val[0][0], val[0][1], NodeArray[])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 131)
- def _reduce_52(val, _values, result)
- val[1][1] << val[0][1]; result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 132)
- def _reduce_53(val, _values, result)
- result = [val[0][0], [val[0][1]]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 133)
- def _reduce_54(val, _values, result)
- val[1][1] << val[0][1]; result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 134)
- def _reduce_55(val, _values, result)
- result = [val[0][0], [val[0][1]]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 135)
- def _reduce_56(val, _values, result)
- val[1][1] << val[0][1]; result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 136)
- def _reduce_57(val, _values, result)
- result = [val[0][0], [val[0][1]]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 137)
- def _reduce_58(val, _values, result)
- val[1][1] << val[0][1]; result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 138)
- def _reduce_59(val, _values, result)
- result = [val[0][0], [val[0][1]]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 142)
- def _reduce_60(val, _values, result)
- result = NodeArray[val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 143)
- def _reduce_61(val, _values, result)
- result = val[0] << val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 147)
- def _reduce_62(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 148)
- def _reduce_63(val, _values, result)
- val[0].init = val[2]; result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 152)
- def _reduce_64(val, _values, result)
- result = [val[0].pos, :typedef ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 153)
- def _reduce_65(val, _values, result)
- result = [val[0].pos, :extern ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 154)
- def _reduce_66(val, _values, result)
- result = [val[0].pos, :static ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 155)
- def _reduce_67(val, _values, result)
- result = [val[0].pos, :auto ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 156)
- def _reduce_68(val, _values, result)
- result = [val[0].pos, :register]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 160)
- def _reduce_69(val, _values, result)
- result = [val[0].pos, :void ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 161)
- def _reduce_70(val, _values, result)
- result = [val[0].pos, :char ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 162)
- def _reduce_71(val, _values, result)
- result = [val[0].pos, :short ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 163)
- def _reduce_72(val, _values, result)
- result = [val[0].pos, :int ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 164)
- def _reduce_73(val, _values, result)
- result = [val[0].pos, :long ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 165)
- def _reduce_74(val, _values, result)
- result = [val[0].pos, :float ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 166)
- def _reduce_75(val, _values, result)
- result = [val[0].pos, :double ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 167)
- def _reduce_76(val, _values, result)
- result = [val[0].pos, :signed ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 168)
- def _reduce_77(val, _values, result)
- result = [val[0].pos, :unsigned ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 169)
- def _reduce_78(val, _values, result)
- result = [val[0].pos, :_Bool ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 170)
- def _reduce_79(val, _values, result)
- result = [val[0].pos, :_Complex ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 171)
- def _reduce_80(val, _values, result)
- result = [val[0].pos, :_Imaginary]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 172)
- def _reduce_81(val, _values, result)
- result = [val[0].pos, val[0] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 173)
- def _reduce_82(val, _values, result)
- result = [val[0].pos, val[0] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 174)
- def _reduce_83(val, _values, result)
- result = [val[0].pos, val[0] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 178)
- def _reduce_84(val, _values, result)
- result = val[0][1].new_at(val[0][0], val[1].val, val[3])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 179)
- def _reduce_85(val, _values, result)
- result = val[0][1].new_at(val[0][0], nil , val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 180)
- def _reduce_86(val, _values, result)
- result = val[0][1].new_at(val[0][0], val[1].val, nil )
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 182)
- def _reduce_87(val, _values, result)
- result = val[0][1].new_at(val[0][0], val[1].name, val[3])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 183)
- def _reduce_88(val, _values, result)
- result = val[0][1].new_at(val[0][0], val[1].name, nil )
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 187)
- def _reduce_89(val, _values, result)
- result = [val[0].pos, Struct]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 188)
- def _reduce_90(val, _values, result)
- result = [val[0].pos, Union ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 192)
- def _reduce_91(val, _values, result)
- result = NodeArray[val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 193)
- def _reduce_92(val, _values, result)
- val[0] << val[1]; result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 197)
- def _reduce_93(val, _values, result)
- result = make_declaration(val[0][0], val[0][1], val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 201)
- def _reduce_94(val, _values, result)
- val[1][1] << val[0][1]; result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 202)
- def _reduce_95(val, _values, result)
- result = [val[0][0], [val[0][1]]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 203)
- def _reduce_96(val, _values, result)
- val[1][1] << val[0][1]; result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 204)
- def _reduce_97(val, _values, result)
- result = [val[0][0], [val[0][1]]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 208)
- def _reduce_98(val, _values, result)
- result = NodeArray[val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 209)
- def _reduce_99(val, _values, result)
- result = val[0] << val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 213)
- def _reduce_100(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 214)
- def _reduce_101(val, _values, result)
- result = val[0]; val[0].num_bits = val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 215)
- def _reduce_102(val, _values, result)
- result = Declarator.new_at(val[0].pos, :num_bits => val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 219)
- def _reduce_103(val, _values, result)
- result = Enum.new_at(val[0].pos, val[1].val, val[3])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 220)
- def _reduce_104(val, _values, result)
- result = Enum.new_at(val[0].pos, nil , val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 221)
- def _reduce_105(val, _values, result)
- result = Enum.new_at(val[0].pos, val[1].val, val[3])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 222)
- def _reduce_106(val, _values, result)
- result = Enum.new_at(val[0].pos, nil , val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 223)
- def _reduce_107(val, _values, result)
- result = Enum.new_at(val[0].pos, val[1].val, nil )
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 225)
- def _reduce_108(val, _values, result)
- result = Enum.new_at(val[0].pos, val[1].name, val[3])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 226)
- def _reduce_109(val, _values, result)
- result = Enum.new_at(val[0].pos, val[1].name, val[3])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 227)
- def _reduce_110(val, _values, result)
- result = Enum.new_at(val[0].pos, val[1].name, nil )
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 231)
- def _reduce_111(val, _values, result)
- result = NodeArray[val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 232)
- def _reduce_112(val, _values, result)
- result = val[0] << val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 236)
- def _reduce_113(val, _values, result)
- result = Enumerator.new_at(val[0].pos, val[0].val, nil )
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 237)
- def _reduce_114(val, _values, result)
- result = Enumerator.new_at(val[0].pos, val[0].val, val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 241)
- def _reduce_115(val, _values, result)
- result = [val[0].pos, :const ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 242)
- def _reduce_116(val, _values, result)
- result = [val[0].pos, :restrict]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 243)
- def _reduce_117(val, _values, result)
- result = [val[0].pos, :volatile]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 247)
- def _reduce_118(val, _values, result)
- result = [val[0].pos, :inline]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 251)
- def _reduce_119(val, _values, result)
- result = add_decl_type(val[1], val[0])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 252)
- def _reduce_120(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 256)
- def _reduce_121(val, _values, result)
- result = Declarator.new_at(val[0].pos, nil, val[0].val)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 257)
- def _reduce_122(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 258)
- def _reduce_123(val, _values, result)
- result = add_decl_type(val[0], Array.new_at(val[0].pos ))
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 259)
- def _reduce_124(val, _values, result)
- result = add_decl_type(val[0], Array.new_at(val[0].pos ))
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 260)
- def _reduce_125(val, _values, result)
- result = add_decl_type(val[0], Array.new_at(val[0].pos, nil, val[2]))
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 261)
- def _reduce_126(val, _values, result)
- result = add_decl_type(val[0], Array.new_at(val[0].pos ))
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 262)
- def _reduce_127(val, _values, result)
- result = add_decl_type(val[0], Array.new_at(val[0].pos ))
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 263)
- def _reduce_128(val, _values, result)
- result = add_decl_type(val[0], Array.new_at(val[0].pos ))
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 264)
- def _reduce_129(val, _values, result)
- result = add_decl_type(val[0], Array.new_at(val[0].pos ))
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 265)
- def _reduce_130(val, _values, result)
- result = add_decl_type(val[0], Array.new_at(val[0].pos ))
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 266)
- def _reduce_131(val, _values, result)
- result = add_decl_type(val[0], Array.new_at(val[0].pos ))
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 267)
- def _reduce_132(val, _values, result)
- result = add_decl_type(val[0], Function.new_at(val[0].pos, nil, param_list(*val[2]), :var_args => val[2][1]))
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 268)
- def _reduce_133(val, _values, result)
- result = add_decl_type(val[0], Function.new_at(val[0].pos, nil, val[2]))
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 269)
- def _reduce_134(val, _values, result)
- result = add_decl_type(val[0], Function.new_at(val[0].pos ))
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 273)
- def _reduce_135(val, _values, result)
- result = add_type_quals(Pointer.new_at(val[0].pos), val[1][1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 274)
- def _reduce_136(val, _values, result)
- result = Pointer.new_at(val[0].pos)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 275)
- def _reduce_137(val, _values, result)
- p = add_type_quals(Pointer.new_at(val[0].pos), val[1][1]); val[2].direct_type = p; result = val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 276)
- def _reduce_138(val, _values, result)
- p = Pointer.new_at(val[0].pos) ; val[1].direct_type = p; result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 280)
- def _reduce_139(val, _values, result)
- result = [val[0][0], [val[0][1]]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 281)
- def _reduce_140(val, _values, result)
- val[0][1] << val[1][1]; result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 285)
- def _reduce_141(val, _values, result)
- result = [val[0], false]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 286)
- def _reduce_142(val, _values, result)
- result = [val[0], true ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 290)
- def _reduce_143(val, _values, result)
- result = NodeArray[val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 291)
- def _reduce_144(val, _values, result)
- result = val[0] << val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 295)
- def _reduce_145(val, _values, result)
- ind_type = val[1].indirect_type and ind_type.detach
- result = make_parameter(val[0][0], val[0][1], ind_type, val[1].name)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 297)
- def _reduce_146(val, _values, result)
- result = make_parameter(val[0][0], val[0][1], val[1] , nil )
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 298)
- def _reduce_147(val, _values, result)
- result = make_parameter(val[0][0], val[0][1], nil , nil )
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 302)
- def _reduce_148(val, _values, result)
- result = NodeArray[Parameter.new_at(val[0].pos, nil, val[0].val)]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 303)
- def _reduce_149(val, _values, result)
- result = val[0] << Parameter.new_at(val[2].pos, nil, val[2].val)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 307)
- def _reduce_150(val, _values, result)
- val[1].direct_type = make_direct_type(val[0][0], val[0][1]); result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 308)
- def _reduce_151(val, _values, result)
- result = make_direct_type(val[0][0], val[0][1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 312)
- def _reduce_152(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 313)
- def _reduce_153(val, _values, result)
- val[1].direct_type = val[0]; result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 314)
- def _reduce_154(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 318)
- def _reduce_155(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 319)
- def _reduce_156(val, _values, result)
- val[0].direct_type = Array.new_at(val[0].pos, nil, val[2]); result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 320)
- def _reduce_157(val, _values, result)
- val[0].direct_type = Array.new_at(val[0].pos, nil, nil ); result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 321)
- def _reduce_158(val, _values, result)
- result = Array.new_at(val[0].pos, nil, val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 322)
- def _reduce_159(val, _values, result)
- result = Array.new_at(val[0].pos )
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 323)
- def _reduce_160(val, _values, result)
- val[0].direct_type = Array.new_at(val[0].pos); result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 324)
- def _reduce_161(val, _values, result)
- result = Array.new_at(val[0].pos)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 325)
- def _reduce_162(val, _values, result)
- val[0].direct_type = Function.new_at(val[0].pos, nil, param_list(*val[2]), val[2][1]); result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 326)
- def _reduce_163(val, _values, result)
- val[0].direct_type = Function.new_at(val[0].pos ); result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 327)
- def _reduce_164(val, _values, result)
- result = Function.new_at(val[0].pos, nil, param_list(*val[1]), val[1][1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 328)
- def _reduce_165(val, _values, result)
- result = Function.new_at(val[0].pos )
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 334)
- def _reduce_166(val, _values, result)
- result = CustomType.new_at(val[0].pos, val[0].val)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 338)
- def _reduce_167(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 339)
- def _reduce_168(val, _values, result)
- result = CompoundLiteral.new_at(val[0].pos, nil, val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 340)
- def _reduce_169(val, _values, result)
- result = CompoundLiteral.new_at(val[0].pos, nil, val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 344)
- def _reduce_170(val, _values, result)
- result = NodeArray[MemberInit.new_at(val[0][0] , val[0][1], val[1])]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 345)
- def _reduce_171(val, _values, result)
- result = NodeArray[MemberInit.new_at(val[0].pos, nil , val[0])]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 346)
- def _reduce_172(val, _values, result)
- result = val[0] << MemberInit.new_at(val[2][0] , val[2][1], val[3])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 347)
- def _reduce_173(val, _values, result)
- result = val[0] << MemberInit.new_at(val[2].pos, nil , val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 351)
- def _reduce_174(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 355)
- def _reduce_175(val, _values, result)
- result = val[0]; val[0][1] = NodeArray[val[0][1]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 356)
- def _reduce_176(val, _values, result)
- result = val[0]; val[0][1] << val[1][1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 360)
- def _reduce_177(val, _values, result)
- result = [val[1].pos, val[1] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 361)
- def _reduce_178(val, _values, result)
- result = [val[1].pos, Member.new_at(val[1].pos, val[1].val)]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 367)
- def _reduce_179(val, _values, result)
- result = Variable.new_at(val[0].pos, val[0].val)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 368)
- def _reduce_180(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 369)
- def _reduce_181(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 371)
- def _reduce_182(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 372)
- def _reduce_183(val, _values, result)
- block_expressions_enabled? or parse_error val[0].pos, "compound statement found where expression expected"
- result = BlockExpression.new(val[1]); result.pos = val[0].pos
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 377)
- def _reduce_184(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 378)
- def _reduce_185(val, _values, result)
- result = Index .new_at(val[0].pos, val[0], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 379)
- def _reduce_186(val, _values, result)
- result = Call .new_at(val[0].pos, val[0], val[2] )
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 380)
- def _reduce_187(val, _values, result)
- result = Call .new_at(val[0].pos, val[0], NodeArray[])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 381)
- def _reduce_188(val, _values, result)
- result = Dot .new_at(val[0].pos, val[0], Member.new(val[2].val))
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 382)
- def _reduce_189(val, _values, result)
- result = Arrow .new_at(val[0].pos, val[0], Member.new(val[2].val))
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 383)
- def _reduce_190(val, _values, result)
- result = PostInc .new_at(val[0].pos, val[0] )
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 384)
- def _reduce_191(val, _values, result)
- result = PostDec .new_at(val[0].pos, val[0] )
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 385)
- def _reduce_192(val, _values, result)
- result = CompoundLiteral.new_at(val[0].pos, val[1], val[4])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 386)
- def _reduce_193(val, _values, result)
- result = CompoundLiteral.new_at(val[0].pos, val[1], val[4])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 390)
- def _reduce_194(val, _values, result)
- result = NodeArray[val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 391)
- def _reduce_195(val, _values, result)
- result = val[0] << val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 395)
- def _reduce_196(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 396)
- def _reduce_197(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 400)
- def _reduce_198(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 401)
- def _reduce_199(val, _values, result)
- result = PreInc.new_at(val[0].pos, val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 402)
- def _reduce_200(val, _values, result)
- result = PreDec.new_at(val[0].pos, val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 403)
- def _reduce_201(val, _values, result)
- result = val[0][0].new_at(val[0][1], val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 404)
- def _reduce_202(val, _values, result)
- result = Sizeof.new_at(val[0].pos, val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 405)
- def _reduce_203(val, _values, result)
- result = Sizeof.new_at(val[0].pos, val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 409)
- def _reduce_204(val, _values, result)
- result = [Address , val[0].pos]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 410)
- def _reduce_205(val, _values, result)
- result = [Dereference, val[0].pos]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 411)
- def _reduce_206(val, _values, result)
- result = [Positive , val[0].pos]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 412)
- def _reduce_207(val, _values, result)
- result = [Negative , val[0].pos]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 413)
- def _reduce_208(val, _values, result)
- result = [BitNot , val[0].pos]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 414)
- def _reduce_209(val, _values, result)
- result = [Not , val[0].pos]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 418)
- def _reduce_210(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 419)
- def _reduce_211(val, _values, result)
- result = Cast.new_at(val[0].pos, val[1], val[3])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 423)
- def _reduce_212(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 424)
- def _reduce_213(val, _values, result)
- result = Multiply.new_at(val[0].pos, val[0], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 425)
- def _reduce_214(val, _values, result)
- result = Divide .new_at(val[0].pos, val[0], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 426)
- def _reduce_215(val, _values, result)
- result = Mod .new_at(val[0].pos, val[0], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 430)
- def _reduce_216(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 431)
- def _reduce_217(val, _values, result)
- result = Add .new_at(val[0].pos, val[0], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 432)
- def _reduce_218(val, _values, result)
- result = Subtract.new_at(val[0].pos, val[0], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 436)
- def _reduce_219(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 437)
- def _reduce_220(val, _values, result)
- result = ShiftLeft .new_at(val[0].pos, val[0], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 438)
- def _reduce_221(val, _values, result)
- result = ShiftRight.new_at(val[0].pos, val[0], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 442)
- def _reduce_222(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 443)
- def _reduce_223(val, _values, result)
- result = Less.new_at(val[0].pos, val[0], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 444)
- def _reduce_224(val, _values, result)
- result = More.new_at(val[0].pos, val[0], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 445)
- def _reduce_225(val, _values, result)
- result = LessOrEqual.new_at(val[0].pos, val[0], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 446)
- def _reduce_226(val, _values, result)
- result = MoreOrEqual.new_at(val[0].pos, val[0], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 450)
- def _reduce_227(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 451)
- def _reduce_228(val, _values, result)
- result = Equal .new_at(val[0].pos, val[0], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 452)
- def _reduce_229(val, _values, result)
- result = NotEqual.new_at(val[0].pos, val[0], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 456)
- def _reduce_230(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 457)
- def _reduce_231(val, _values, result)
- result = BitAnd.new_at(val[0].pos, val[0], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 461)
- def _reduce_232(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 462)
- def _reduce_233(val, _values, result)
- result = BitXor.new_at(val[0].pos, val[0], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 466)
- def _reduce_234(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 467)
- def _reduce_235(val, _values, result)
- result = BitOr.new_at(val[0].pos, val[0], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 471)
- def _reduce_236(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 472)
- def _reduce_237(val, _values, result)
- result = And.new_at(val[0].pos, val[0], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 476)
- def _reduce_238(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 477)
- def _reduce_239(val, _values, result)
- result = Or.new_at(val[0].pos, val[0], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 481)
- def _reduce_240(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 482)
- def _reduce_241(val, _values, result)
- result = Conditional.new_at(val[0].pos, val[0], val[2], val[4])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 486)
- def _reduce_242(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 487)
- def _reduce_243(val, _values, result)
- result = val[1].new_at(val[0].pos, val[0], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 491)
- def _reduce_244(val, _values, result)
- result = Assign
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 492)
- def _reduce_245(val, _values, result)
- result = MultiplyAssign
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 493)
- def _reduce_246(val, _values, result)
- result = DivideAssign
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 494)
- def _reduce_247(val, _values, result)
- result = ModAssign
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 495)
- def _reduce_248(val, _values, result)
- result = AddAssign
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 496)
- def _reduce_249(val, _values, result)
- result = SubtractAssign
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 497)
- def _reduce_250(val, _values, result)
- result = ShiftLeftAssign
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 498)
- def _reduce_251(val, _values, result)
- result = ShiftRightAssign
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 499)
- def _reduce_252(val, _values, result)
- result = BitAndAssign
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 500)
- def _reduce_253(val, _values, result)
- result = BitXorAssign
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 501)
- def _reduce_254(val, _values, result)
- result = BitOrAssign
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 505)
- def _reduce_255(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 507)
- def _reduce_256(val, _values, result)
- if val[0].is_a? Comma
- if val[2].is_a? Comma
- val[0].exprs.push(*val[2].exprs)
- else
- val[0].exprs << val[2]
- end
- result = val[0]
- else
- if val[2].is_a? Comma
- val[2].exprs.unshift(val[0])
- val[2].pos = val[0].pos
- result = val[2]
- else
- result = Comma.new_at(val[0].pos, NodeArray[val[0], val[2]])
- end
- end
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 527)
- def _reduce_257(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 542)
- def _reduce_258(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 546)
- def _reduce_259(val, _values, result)
- result = val[0].val; result.pos = val[0].pos
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 547)
- def _reduce_260(val, _values, result)
- result = val[0].val; result.pos = val[0].pos
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 550)
- def _reduce_261(val, _values, result)
- result = val[0].val; result.pos = val[0].pos
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 554)
- def _reduce_262(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 559)
- def _reduce_263(val, _values, result)
- val[0].val << val[1].val.val; result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'cast.y', 560)
- def _reduce_264(val, _values, result)
- result = val[0].val; result.pos = val[0].pos
- result
- end
-.,.,
-
-def _reduce_none(val, _values, result)
- val[0]
-end
-
- end # class Parser
-end # module C
diff --git a/test/racc/regress/csspool b/test/racc/regress/csspool
deleted file mode 100644
index caf6cb3eb1..0000000000
--- a/test/racc/regress/csspool
+++ /dev/null
@@ -1,2316 +0,0 @@
-#
-# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.5.2
-# from Racc grammar file "".
-#
-
-require 'racc/parser.rb'
-module CSSPool
- module CSS
- class Parser < Racc::Parser
-
-module_eval(<<'...end csspool.y/module_eval...', 'csspool.y', 670)
-
-def numeric thing
- thing = thing.gsub(/[^\d.]/, '')
- Integer(thing) rescue Float(thing)
-end
-
-def interpret_identifier s
- interpret_escapes s
-end
-
-def interpret_uri s
- interpret_escapes s.match(/^url\((.*)\)$/mui)[1].strip.match(/^(['"]?)((?:\\.|.)*)\1$/mu)[2]
-end
-
-def interpret_string_no_quote s
- interpret_escapes s.match(/^(.*)\)$/mu)[1].strip.match(/^(['"]?)((?:\\.|.)*)\1$/mu)[2]
-end
-
-def interpret_string s
- interpret_escapes s.match(/^(['"])((?:\\.|.)*)\1$/mu)[2]
-end
-
-def interpret_escapes s
- token_exp = /\\(?:([0-9a-fA-F]{1,6}(?:\r\n|\s)?)|(.))/mu
- return s.gsub(token_exp) do |escape_sequence|
- if !$1.nil?
- code = $1.chomp.to_i 16
- code = 0xFFFD if code > 0x10FFFF
- next [code].pack('U')
- end
- next '' if $2 == "\n"
- next $2
- end
-end
-
-# override racc's on_error so we can have context in our error messages
-def on_error(t, val, vstack)
- errcontext = (@ss.pre_match[-10..-1] || @ss.pre_match) +
- @ss.matched + @ss.post_match[0..9]
- line_number = @ss.pre_match.lines.count
- raise ParseError, sprintf("parse error on value %s (%s) " +
- "on line %s around \"%s\"",
- val.inspect, token_to_str(t) || '?',
- line_number, errcontext)
-end
-
-def before_pos(val)
- # don't include leading whitespace
- return current_pos - val.last.length + val.last[/\A\s*/].size
-end
-
-def after_pos(val)
- # don't include trailing whitespace
- return current_pos - val.last[/\s*\z/].size
-end
-
-# charpos will work with multibyte strings but is not available until ruby 2
-def current_pos
- @ss.respond_to?('charpos') ? @ss.charpos : @ss.pos
-end
-...end csspool.y/module_eval...
-##### State transition tables begin ###
-
-racc_action_table = [
- 9, 10, 137, 129, 37, 31, 55, 139, 130, 39,
- 45, 47, 45, 47, 123, 9, 10, 103, 3, 37,
- 31, 98, 229, 103, 39, 45, 47, 230, 124, 125,
- 224, 20, 113, 56, 37, 31, 23, 114, 104, 39,
- 45, 47, 245, 27, 104, 11, 20, 126, 48, 25,
- 48, 23, 242, 29, 138, 244, 38, 46, 27, 46,
- 11, 108, 113, 48, 25, 9, 10, 114, 29, 37,
- 31, 38, 46, 223, 39, 45, 47, 49, 48, 115,
- 9, 10, 108, 113, 37, 31, 38, 46, 114, 39,
- 45, 47, 55, 339, 243, 155, 20, 108, 113, 37,
- 31, 23, 107, 114, 39, 45, 47, 231, 27, 115,
- 11, 20, 232, 48, 25, 55, 23, 59, 29, 56,
- 344, 38, 46, 27, 340, 11, 20, 53, 48, 25,
- 115, 23, 345, 29, 37, 31, 38, 46, 27, 39,
- 45, 47, 56, 48, 25, 115, -28, 105, 29, 37,
- 31, 38, 46, 116, 39, 45, 47, 250, 75, 120,
- 251, 20, 108, 113, 37, 31, 23, 118, 114, 39,
- 45, 47, 121, 27, 74, 73, 20, 313, 48, 25,
- 314, 23, 128, 29, 108, 113, 38, 46, 27, 215,
- 114, 20, 131, 48, 25, 75, 23, 136, 29, 37,
- 64, 38, 46, 27, 39, 45, 47, 92, 48, 25,
- 115, 74, 73, 29, 37, 31, 38, 46, 77, 39,
- 45, 47, 148, 94, 103, 156, 20, -89, 83, 37,
- 31, 23, 115, 87, 39, 45, 47, 160, 27, 85,
- 153, 20, 151, 48, 25, 104, 23, 84, 29, 157,
- 158, 38, 46, 27, 163, 252, 20, 151, 48, 25,
- 201, 23, 164, 29, 37, 31, 38, 46, 27, 39,
- 45, 47, 203, 48, 25, 188, 202, 59, 29, 37,
- 165, 38, 46, 187, 39, 45, 47, 201, 204, 166,
- 184, 83, 288, 198, 287, 190, 197, 192, 191, 193,
- 194, 195, 85, 202, 37, 45, 47, 37, 48, 39,
- 45, 47, 39, 45, 47, 167, 38, 46, 168, 83,
- 170, 113, 210, 48, 181, 186, 114, 185, 196, 37,
- 85, 38, 46, 92, 39, 45, 47, 203, 84, 150,
- 152, 151, 289, 48, 290, 292, 163, 291, 48, 94,
- -33, 48, 46, 204, 169, 199, 38, 46, 37, 38,
- 46, 200, -33, 39, 45, 47, 92, 188, 115, 59,
- 294, 258, 293, 48, 296, 187, 295, 298, 260, 297,
- 212, 38, 46, 83, 216, 198, 259, 190, 197, 192,
- 191, 193, 194, 195, 85, 45, 47, 45, 47, 45,
- 47, 217, 48, 218, 219, 188, 108, 59, 285, 108,
- 38, 46, 170, 187, 225, 226, 181, 186, 233, 185,
- 196, 83, 129, 198, 234, 190, 197, 192, 191, 193,
- 194, 195, 85, 48, 163, 48, 255, 48, 256, 155,
- 263, 169, 46, 188, 46, 59, 46, 264, 168, 265,
- 170, 187, 266, 92, 181, 186, 92, 185, 196, 83,
- 92, 198, 92, 190, 197, 192, 191, 193, 194, 195,
- 85, 198, 278, 190, 197, 192, 191, 193, 194, 195,
- 279, 188, 281, 59, 241, 235, 236, 237, 170, 187,
- 286, 229, 181, 186, 231, 185, 196, 83, 163, 198,
- 300, 190, 197, 192, 191, 193, 194, 195, 85, 301,
- 302, 238, 239, 240, 303, 306, 307, 255, 141, 188,
- 75, 59, 322, 163, 185, 168, 170, 187, 312, 317,
- 181, 186, 143, 185, 196, 83, 319, 198, 323, 190,
- 197, 192, 191, 193, 194, 195, 85, 324, 325, 145,
- 326, 327, 328, 329, 330, 331, 144, 188, 146, 59,
- 147, 332, 142, 333, 170, 187, 334, 306, 181, 186,
- 163, 185, 196, 83, 338, 198, 163, 190, 197, 192,
- 191, 193, 194, 195, 85, 346, 319, 319, 163, 351,
- 306, 163, 319, 357, 359, 188, nil, 59, nil, nil,
- nil, nil, 170, 187, nil, nil, 181, 186, nil, 185,
- 196, 83, nil, 198, nil, 190, 197, 192, 191, 193,
- 194, 195, 85, 272, nil, 83, nil, 198, nil, 190,
- 197, 192, 191, 193, 194, 195, nil, nil, nil, nil,
- 170, nil, nil, nil, 181, 186, nil, 185, 196, 272,
- nil, 83, nil, 198, nil, 190, 197, 192, 191, 193,
- 194, 195, 272, nil, 83, nil, 198, nil, 190, 197,
- 192, 191, 193, 194, 195, 272, nil, 83, nil, 198,
- nil, 190, 197, 192, 191, 193, 194, 195, 272, nil,
- 83, nil, 198, nil, 190, 197, 192, 191, 193, 194,
- 195, 272, nil, 83, nil, 198, nil, 190, 197, 192,
- 191, 193, 194, 195 ]
-
-racc_action_check = [
- 2, 2, 47, 38, 2, 2, 10, 47, 38, 2,
- 2, 2, 35, 35, 34, 5, 5, 26, 1, 5,
- 5, 26, 128, 217, 5, 5, 5, 128, 34, 34,
- 112, 2, 210, 10, 31, 31, 2, 210, 26, 31,
- 31, 31, 143, 2, 217, 2, 5, 34, 2, 2,
- 35, 5, 142, 2, 47, 143, 2, 2, 5, 35,
- 5, 110, 110, 5, 5, 6, 6, 110, 5, 6,
- 6, 5, 5, 112, 6, 6, 6, 3, 31, 210,
- 7, 7, 221, 221, 7, 7, 31, 31, 221, 7,
- 7, 7, 58, 309, 142, 58, 6, 28, 28, 12,
- 12, 6, 28, 28, 12, 12, 12, 131, 6, 110,
- 6, 7, 131, 6, 6, 11, 7, 11, 6, 58,
- 315, 6, 6, 7, 309, 7, 12, 9, 7, 7,
- 221, 12, 315, 7, 13, 13, 7, 7, 12, 13,
- 13, 13, 11, 12, 12, 28, 20, 27, 12, 14,
- 14, 12, 12, 29, 14, 14, 14, 149, 20, 32,
- 149, 13, 30, 30, 15, 15, 13, 30, 30, 15,
- 15, 15, 33, 13, 20, 20, 14, 269, 13, 13,
- 269, 14, 37, 13, 100, 100, 13, 13, 14, 100,
- 100, 15, 39, 14, 14, 157, 15, 46, 14, 19,
- 19, 14, 14, 15, 19, 19, 19, 25, 15, 15,
- 30, 157, 157, 15, 21, 21, 15, 15, 21, 21,
- 21, 21, 53, 25, 99, 67, 19, 99, 22, 24,
- 24, 19, 100, 24, 24, 24, 24, 71, 19, 22,
- 57, 21, 57, 19, 19, 99, 21, 22, 19, 70,
- 70, 19, 19, 21, 75, 154, 24, 154, 21, 21,
- 90, 24, 76, 21, 64, 64, 21, 21, 24, 64,
- 64, 64, 91, 24, 24, 83, 90, 83, 24, 121,
- 78, 24, 24, 83, 121, 121, 121, 206, 91, 79,
- 83, 83, 235, 83, 235, 83, 83, 83, 83, 83,
- 83, 83, 83, 206, 122, 41, 41, 144, 64, 122,
- 122, 122, 144, 144, 144, 80, 64, 64, 81, 166,
- 83, 92, 92, 121, 83, 83, 92, 83, 83, 146,
- 166, 121, 121, 92, 146, 146, 146, 207, 166, 54,
- 54, 54, 236, 41, 236, 237, 270, 237, 122, 92,
- 270, 144, 41, 207, 82, 86, 122, 122, 302, 144,
- 144, 88, 270, 302, 302, 302, 94, 171, 92, 171,
- 238, 171, 238, 146, 239, 171, 239, 240, 171, 240,
- 97, 146, 146, 171, 101, 171, 171, 171, 171, 171,
- 171, 171, 171, 171, 171, 42, 42, 43, 43, 44,
- 44, 102, 302, 105, 106, 223, 108, 223, 223, 109,
- 302, 302, 171, 223, 114, 117, 171, 171, 137, 171,
- 171, 223, 138, 223, 139, 223, 223, 223, 223, 223,
- 223, 223, 223, 42, 147, 43, 161, 44, 162, 172,
- 175, 176, 42, 261, 43, 261, 44, 177, 179, 183,
- 223, 261, 185, 201, 223, 223, 202, 223, 223, 261,
- 203, 261, 204, 261, 261, 261, 261, 261, 261, 261,
- 261, 189, 208, 189, 189, 189, 189, 189, 189, 189,
- 209, 285, 214, 285, 140, 140, 140, 140, 261, 285,
- 224, 233, 261, 261, 234, 261, 261, 285, 243, 285,
- 245, 285, 285, 285, 285, 285, 285, 285, 285, 246,
- 247, 140, 140, 140, 248, 249, 251, 254, 48, 286,
- 255, 286, 286, 256, 266, 267, 285, 286, 268, 280,
- 285, 285, 48, 285, 285, 286, 284, 286, 287, 286,
- 286, 286, 286, 286, 286, 286, 286, 288, 289, 48,
- 290, 291, 292, 293, 294, 295, 48, 322, 48, 322,
- 48, 296, 48, 297, 286, 322, 298, 299, 286, 286,
- 304, 286, 286, 322, 306, 322, 312, 322, 322, 322,
- 322, 322, 322, 322, 322, 316, 320, 321, 335, 337,
- 338, 340, 349, 350, 358, 353, nil, 353, nil, nil,
- nil, nil, 322, 353, nil, nil, 322, 322, nil, 322,
- 322, 353, nil, 353, nil, 353, 353, 353, 353, 353,
- 353, 353, 353, 186, nil, 186, nil, 186, nil, 186,
- 186, 186, 186, 186, 186, 186, nil, nil, nil, nil,
- 353, nil, nil, nil, 353, 353, nil, 353, 353, 272,
- nil, 272, nil, 272, nil, 272, 272, 272, 272, 272,
- 272, 272, 313, nil, 313, nil, 313, nil, 313, 313,
- 313, 313, 313, 313, 313, 314, nil, 314, nil, 314,
- nil, 314, 314, 314, 314, 314, 314, 314, 344, nil,
- 344, nil, 344, nil, 344, 344, 344, 344, 344, 344,
- 344, 345, nil, 345, nil, 345, nil, 345, 345, 345,
- 345, 345, 345, 345 ]
-
-racc_action_pointer = [
- nil, 18, -2, 77, nil, 13, 63, 78, nil, 123,
- 2, 111, 93, 128, 143, 158, nil, nil, nil, 193,
- 140, 208, 208, nil, 223, 189, 11, 141, 92, 144,
- 157, 28, 150, 164, 7, 0, nil, 124, -3, 134,
- nil, 293, 383, 385, 387, nil, 191, -4, 512, nil,
- nil, nil, nil, 217, 334, nil, nil, 235, 88, nil,
- nil, nil, nil, nil, 258, nil, nil, 215, nil, nil,
- 241, 231, nil, nil, nil, 247, 252, nil, 271, 281,
- 308, 311, 347, 271, nil, nil, 345, nil, 352, nil,
- 224, 236, 315, nil, 348, nil, nil, 370, nil, 218,
- 179, 375, 393, nil, nil, 394, 394, nil, 401, 404,
- 56, nil, 23, nil, 408, nil, nil, 405, nil, nil,
- nil, 273, 298, nil, nil, nil, nil, nil, 16, nil,
- nil, 101, nil, nil, nil, nil, nil, 360, 416, 366,
- 470, nil, 46, 36, 301, nil, 323, 427, nil, 152,
- nil, nil, nil, nil, 250, nil, nil, 177, nil, nil,
- nil, 400, 432, nil, nil, nil, 299, nil, nil, nil,
- nil, 363, 432, nil, nil, 433, 434, 440, nil, 441,
- nil, nil, nil, 430, nil, 444, 605, nil, nil, 449,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 435, 438, 442, 444, nil, 251, 301, 453, 461,
- 26, nil, nil, nil, 472, nil, nil, 17, nil, nil,
- nil, 77, nil, 401, 440, nil, nil, nil, nil, nil,
- nil, nil, nil, 485, 488, 288, 338, 341, 366, 370,
- 373, nil, nil, 491, nil, 481, 490, 502, 495, 509,
- nil, 510, nil, nil, 481, 502, 516, nil, nil, nil,
- nil, 439, nil, nil, nil, nil, 468, 518, 509, 155,
- 339, nil, 631, nil, nil, nil, nil, nil, nil, nil,
- 510, nil, nil, nil, 504, 477, 515, 524, 533, 534,
- 536, 537, 538, 539, 540, 541, 547, 549, 552, 561,
- nil, nil, 352, nil, 563, nil, 566, nil, nil, 74,
- nil, nil, 569, 644, 657, 109, 566, nil, nil, nil,
- 554, 555, 553, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 581, nil, 570, 584, nil,
- 584, nil, nil, nil, 670, 683, nil, nil, nil, 560,
- 574, nil, nil, 591, nil, nil, nil, nil, 575, nil ]
-
-racc_action_default = [
- -1, -229, -10, -229, -2, -6, -7, -8, -9, -229,
- -229, -229, -41, -42, -43, -44, -45, -46, -47, -33,
- -23, -229, -229, -55, -229, -229, -89, -229, -229, -229,
- -229, -229, -229, -103, -105, -111, -112, -115, -229, -120,
- -119, -124, -125, -126, -127, -132, -229, -229, -229, 360,
- -3, -4, -5, -229, -229, -15, -16, -229, -229, -228,
- -37, -38, -39, -40, -32, -48, -49, -229, -99, -21,
- -229, -229, -35, -26, -27, -33, -229, -53, -229, -57,
- -58, -59, -60, -229, -198, -214, -229, -62, -229, -64,
- -65, -66, -229, -71, -229, -73, -74, -229, -82, -85,
- -229, -229, -91, -92, -93, -229, -229, -95, -160, -165,
- -166, -167, -229, -174, -229, -176, -96, -229, -98, -100,
- -101, -229, -229, -106, -107, -108, -109, -110, -229, -117,
- -121, -229, -128, -129, -130, -131, -133, -115, -229, -229,
- -229, -147, -229, -229, -229, -152, -229, -33, -11, -229,
- -13, -14, -20, -18, -229, -227, -50, -28, -51, -35,
- -29, -25, -229, -32, -52, -54, -229, -197, -194, -213,
- -36, -182, -183, -184, -185, -186, -187, -188, -189, -190,
- -191, -192, -193, -229, -196, -200, -229, -212, -216, -229,
- -218, -219, -220, -221, -222, -223, -224, -225, -226, -61,
- -63, -229, -229, -229, -229, -67, -68, -69, -229, -229,
- -229, -72, -81, -84, -229, -87, -88, -89, -83, -94,
- -161, -164, -163, -229, -229, -175, -97, -102, -104, -116,
- -123, -118, -122, -229, -229, -229, -229, -229, -229, -229,
- -229, -146, -148, -33, -149, -229, -229, -114, -229, -156,
- -12, -229, -17, -22, -24, -229, -33, -56, -177, -178,
- -179, -229, -181, -215, -211, -195, -229, -209, -229, -202,
- -205, -208, -229, -217, -76, -78, -75, -77, -70, -79,
- -229, -86, -90, -162, -173, -229, -229, -229, -229, -229,
- -229, -229, -229, -229, -229, -229, -229, -229, -229, -156,
- -150, -151, -229, -153, -33, -157, -158, -19, -34, -229,
- -180, -199, -33, -229, -229, -229, -229, -80, -168, -172,
- -173, -173, -229, -134, -135, -136, -137, -138, -139, -140,
- -141, -142, -143, -144, -145, -33, -113, -229, -229, -30,
- -33, -201, -203, -204, -229, -229, -210, -169, -170, -173,
- -229, -154, -159, -229, -206, -207, -171, -155, -229, -31 ]
-
-racc_goto_table = [
- 81, 248, 183, 68, 106, 91, 117, 271, 78, 246,
- 273, 247, 82, 69, 209, 161, 97, 89, 268, 304,
- 90, 119, 54, 57, 220, 221, 318, 60, 61, 62,
- 63, 354, 355, 1, 65, 127, 76, 2, 149, 86,
- 58, 132, 133, 134, 135, 4, 70, 159, 50, 51,
- 52, 308, 67, 66, 119, 88, 208, 282, 227, 162,
- 228, 122, 347, 348, 140, 352, 261, 311, nil, 335,
- 154, nil, 207, nil, 211, nil, 214, nil, nil, nil,
- nil, nil, nil, nil, 205, nil, 222, 206, nil, 213,
- 262, 356, nil, 271, nil, nil, nil, nil, nil, nil,
- nil, nil, 254, nil, 316, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 249, 280, nil, 271, 271, nil, nil, nil, nil,
- nil, nil, 284, nil, 81, 342, 343, nil, nil, nil,
- 253, nil, 257, nil, nil, nil, 82, 336, nil, nil,
- nil, nil, nil, nil, 267, 271, 271, 247, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 310, 274, 275, 276, 277, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 283, nil, nil,
- nil, nil, nil, nil, 320, 321, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 299, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 309, 349, nil, nil, nil, nil, nil, nil, nil, nil,
- 267, nil, nil, nil, 315, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 358, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 337, nil,
- nil, 267, 267, nil, nil, nil, 341, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 350,
- nil, nil, 267, 267, 353 ]
-
-racc_goto_check = [
- 35, 62, 18, 17, 51, 41, 51, 77, 32, 58,
- 77, 58, 36, 12, 46, 15, 48, 39, 82, 68,
- 40, 55, 8, 8, 70, 70, 73, 7, 7, 7,
- 7, 84, 84, 1, 7, 61, 7, 3, 9, 7,
- 10, 61, 61, 61, 61, 2, 11, 14, 2, 2,
- 2, 16, 27, 28, 55, 38, 42, 52, 56, 17,
- 57, 59, 73, 73, 63, 69, 74, 81, nil, 68,
- 8, nil, 41, nil, 41, nil, 51, nil, nil, nil,
- nil, nil, nil, nil, 39, nil, 51, 40, nil, 48,
- 18, 73, nil, 77, nil, nil, nil, nil, nil, nil,
- nil, nil, 15, nil, 82, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 17, 46, nil, 77, 77, nil, nil, nil, nil,
- nil, nil, 18, nil, 35, 82, 82, nil, nil, nil,
- 12, nil, 32, nil, nil, nil, 36, 62, nil, nil,
- nil, nil, nil, nil, 35, 77, 77, 58, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 18, 41, 41, 41, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 51, nil, nil,
- nil, nil, nil, nil, 18, 18, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 17, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 17, 18, nil, nil, nil, nil, nil, nil, nil, nil,
- 35, nil, nil, nil, 17, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 18, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 17, nil,
- nil, 35, 35, nil, nil, nil, 17, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 17,
- nil, nil, 35, 35, 17 ]
-
-racc_goto_pointer = [
- nil, 33, 43, 37, nil, nil, nil, 15, 12, -16,
- 29, 26, -7, nil, -24, -57, -204, -16, -81, nil,
- nil, nil, nil, nil, nil, nil, nil, 33, 34, nil,
- nil, nil, -14, nil, nil, -22, -10, nil, 30, -8,
- -5, -20, -36, nil, nil, nil, -78, nil, -10, nil,
- nil, -24, -160, nil, nil, -10, -63, -62, -135, 27,
- nil, 0, -145, 17, nil, nil, nil, nil, -230, -273,
- -84, nil, nil, -258, -105, nil, nil, -179, nil, nil,
- nil, -199, -168, nil, -313, nil ]
-
-racc_goto_default = [
- nil, nil, nil, nil, 5, 6, 7, 8, nil, nil,
- 172, nil, nil, 71, nil, nil, 72, nil, nil, 180,
- 12, 13, 14, 15, 16, 17, 18, nil, nil, 19,
- 21, 22, nil, 79, 80, 179, 176, 24, nil, nil,
- nil, nil, nil, 93, 95, 96, 111, 26, nil, 99,
- 100, nil, 101, 102, 28, 30, 32, 33, 34, nil,
- 35, 36, nil, 40, 41, 42, 43, 44, nil, 305,
- 110, 109, 112, nil, nil, 171, 173, 174, 175, 177,
- 178, 182, nil, 269, 270, 189 ]
-
-racc_reduce_table = [
- 0, 0, :racc_error,
- 0, 63, :_reduce_1,
- 2, 61, :_reduce_2,
- 2, 62, :_reduce_none,
- 2, 62, :_reduce_none,
- 2, 62, :_reduce_none,
- 1, 62, :_reduce_none,
- 1, 62, :_reduce_none,
- 1, 62, :_reduce_none,
- 1, 62, :_reduce_none,
- 0, 62, :_reduce_none,
- 3, 64, :_reduce_11,
- 4, 65, :_reduce_12,
- 3, 65, :_reduce_13,
- 2, 68, :_reduce_none,
- 1, 68, :_reduce_15,
- 1, 68, :_reduce_16,
- 4, 66, :_reduce_17,
- 3, 66, :_reduce_18,
- 3, 69, :_reduce_19,
- 1, 69, :_reduce_20,
- 1, 71, :_reduce_21,
- 3, 71, :_reduce_22,
- 0, 71, :_reduce_23,
- 3, 72, :_reduce_24,
- 2, 72, :_reduce_25,
- 1, 73, :_reduce_26,
- 1, 73, :_reduce_27,
- 0, 73, :_reduce_28,
- 1, 74, :_reduce_29,
- 5, 76, :_reduce_30,
- 8, 76, :_reduce_31,
- 1, 77, :_reduce_32,
- 0, 77, :_reduce_33,
- 3, 75, :_reduce_34,
- 0, 75, :_reduce_35,
- 1, 79, :_reduce_36,
- 2, 67, :_reduce_none,
- 2, 67, :_reduce_none,
- 2, 67, :_reduce_none,
- 2, 67, :_reduce_none,
- 1, 67, :_reduce_none,
- 1, 67, :_reduce_none,
- 1, 67, :_reduce_none,
- 1, 67, :_reduce_none,
- 1, 81, :_reduce_none,
- 1, 81, :_reduce_none,
- 1, 81, :_reduce_none,
- 1, 87, :_reduce_none,
- 1, 87, :_reduce_none,
- 3, 84, :_reduce_50,
- 3, 89, :_reduce_51,
- 3, 85, :_reduce_52,
- 2, 85, :_reduce_53,
- 3, 90, :_reduce_54,
- 1, 91, :_reduce_55,
- 3, 92, :_reduce_56,
- 1, 92, :_reduce_57,
- 1, 93, :_reduce_none,
- 1, 93, :_reduce_none,
- 1, 93, :_reduce_none,
- 3, 86, :_reduce_61,
- 2, 86, :_reduce_62,
- 3, 97, :_reduce_63,
- 1, 98, :_reduce_64,
- 1, 98, :_reduce_65,
- 1, 98, :_reduce_66,
- 1, 102, :_reduce_67,
- 1, 102, :_reduce_68,
- 1, 102, :_reduce_69,
- 3, 101, :_reduce_70,
- 1, 101, :_reduce_71,
- 2, 99, :_reduce_72,
- 1, 100, :_reduce_none,
- 1, 100, :_reduce_none,
- 3, 104, :_reduce_75,
- 3, 104, :_reduce_76,
- 3, 105, :_reduce_77,
- 3, 105, :_reduce_78,
- 3, 103, :_reduce_79,
- 4, 103, :_reduce_80,
- 3, 82, :_reduce_none,
- 2, 82, :_reduce_none,
- 3, 107, :_reduce_83,
- 2, 108, :_reduce_none,
- 1, 108, :_reduce_none,
- 3, 109, :_reduce_86,
- 2, 109, :_reduce_87,
- 2, 110, :_reduce_88,
- 0, 112, :_reduce_none,
- 3, 112, :_reduce_90,
- 1, 112, :_reduce_none,
- 1, 113, :_reduce_none,
- 1, 113, :_reduce_93,
- 3, 83, :_reduce_94,
- 2, 83, :_reduce_95,
- 2, 114, :_reduce_96,
- 3, 80, :_reduce_97,
- 2, 80, :_reduce_98,
- 1, 88, :_reduce_99,
- 2, 115, :_reduce_100,
- 2, 115, :_reduce_101,
- 3, 116, :_reduce_102,
- 1, 116, :_reduce_103,
- 3, 117, :_reduce_104,
- 1, 117, :_reduce_none,
- 1, 119, :_reduce_106,
- 1, 119, :_reduce_107,
- 1, 119, :_reduce_108,
- 1, 119, :_reduce_109,
- 2, 118, :_reduce_110,
- 1, 118, :_reduce_111,
- 1, 118, :_reduce_112,
- 3, 122, :_reduce_113,
- 1, 122, :_reduce_none,
- 1, 123, :_reduce_115,
- 3, 123, :_reduce_116,
- 2, 123, :_reduce_117,
- 3, 123, :_reduce_118,
- 1, 120, :_reduce_119,
- 1, 120, :_reduce_120,
- 2, 120, :_reduce_121,
- 3, 120, :_reduce_122,
- 3, 120, :_reduce_123,
- 1, 121, :_reduce_124,
- 1, 121, :_reduce_125,
- 1, 121, :_reduce_126,
- 1, 121, :_reduce_127,
- 2, 121, :_reduce_128,
- 2, 121, :_reduce_129,
- 2, 121, :_reduce_130,
- 2, 121, :_reduce_131,
- 1, 124, :_reduce_132,
- 2, 125, :_reduce_133,
- 5, 126, :_reduce_134,
- 5, 126, :_reduce_135,
- 5, 126, :_reduce_136,
- 5, 126, :_reduce_137,
- 5, 126, :_reduce_138,
- 5, 126, :_reduce_139,
- 5, 126, :_reduce_140,
- 5, 126, :_reduce_141,
- 5, 126, :_reduce_142,
- 5, 126, :_reduce_143,
- 5, 126, :_reduce_144,
- 5, 126, :_reduce_145,
- 3, 126, :_reduce_146,
- 2, 127, :_reduce_147,
- 3, 127, :_reduce_148,
- 3, 127, :_reduce_149,
- 4, 127, :_reduce_150,
- 4, 127, :_reduce_151,
- 2, 127, :_reduce_152,
- 4, 127, :_reduce_153,
- 6, 127, :_reduce_154,
- 7, 127, :_reduce_155,
- 0, 128, :_reduce_none,
- 1, 128, :_reduce_none,
- 1, 129, :_reduce_none,
- 3, 129, :_reduce_none,
- 1, 130, :_reduce_none,
- 2, 130, :_reduce_none,
- 3, 111, :_reduce_none,
- 2, 111, :_reduce_none,
- 2, 111, :_reduce_none,
- 1, 111, :_reduce_none,
- 1, 111, :_reduce_none,
- 1, 131, :_reduce_167,
- 4, 106, :_reduce_168,
- 5, 106, :_reduce_169,
- 5, 106, :_reduce_170,
- 6, 106, :_reduce_171,
- 1, 133, :_reduce_172,
- 0, 133, :_reduce_173,
- 1, 132, :_reduce_174,
- 2, 132, :_reduce_175,
- 1, 132, :_reduce_176,
- 1, 134, :_reduce_none,
- 1, 134, :_reduce_none,
- 1, 134, :_reduce_none,
- 3, 78, :_reduce_180,
- 2, 78, :_reduce_181,
- 1, 78, :_reduce_182,
- 1, 135, :_reduce_none,
- 1, 135, :_reduce_none,
- 1, 135, :_reduce_none,
- 1, 135, :_reduce_none,
- 1, 135, :_reduce_none,
- 1, 135, :_reduce_none,
- 1, 135, :_reduce_none,
- 1, 135, :_reduce_none,
- 1, 135, :_reduce_none,
- 1, 135, :_reduce_none,
- 1, 135, :_reduce_none,
- 2, 95, :_reduce_194,
- 3, 95, :_reduce_195,
- 2, 95, :_reduce_196,
- 2, 94, :_reduce_197,
- 1, 94, :_reduce_198,
- 3, 141, :_reduce_none,
- 1, 141, :_reduce_none,
- 4, 140, :_reduce_201,
- 1, 142, :_reduce_none,
- 3, 142, :_reduce_203,
- 3, 142, :_reduce_204,
- 1, 143, :_reduce_none,
- 4, 143, :_reduce_206,
- 4, 143, :_reduce_207,
- 1, 144, :_reduce_208,
- 1, 144, :_reduce_209,
- 3, 144, :_reduce_210,
- 2, 139, :_reduce_211,
- 1, 139, :_reduce_212,
- 2, 96, :_reduce_213,
- 1, 96, :_reduce_214,
- 2, 138, :_reduce_215,
- 1, 138, :_reduce_216,
- 2, 137, :_reduce_217,
- 1, 137, :_reduce_218,
- 1, 137, :_reduce_219,
- 1, 137, :_reduce_220,
- 1, 137, :_reduce_221,
- 1, 137, :_reduce_222,
- 1, 137, :_reduce_223,
- 1, 136, :_reduce_224,
- 1, 145, :_reduce_225,
- 1, 145, :_reduce_226,
- 2, 70, :_reduce_227,
- 1, 70, :_reduce_228 ]
-
-racc_reduce_n = 229
-
-racc_shift_n = 360
-
-racc_token_table = {
- false => 0,
- :error => 1,
- :CHARSET_SYM => 2,
- :IMPORT_SYM => 3,
- :STRING => 4,
- :SEMI => 5,
- :IDENT => 6,
- :S => 7,
- :COMMA => 8,
- :LBRACE => 9,
- :RBRACE => 10,
- :STAR => 11,
- :HASH => 12,
- :LSQUARE => 13,
- :RSQUARE => 14,
- :EQUAL => 15,
- :INCLUDES => 16,
- :DASHMATCH => 17,
- :LPAREN => 18,
- :RPAREN => 19,
- :FUNCTION => 20,
- :GREATER => 21,
- :PLUS => 22,
- :SLASH => 23,
- :NUMBER => 24,
- :MINUS => 25,
- :LENGTH => 26,
- :PERCENTAGE => 27,
- :ANGLE => 28,
- :TIME => 29,
- :FREQ => 30,
- :URI => 31,
- :IMPORTANT_SYM => 32,
- :MEDIA_SYM => 33,
- :NOT => 34,
- :ONLY => 35,
- :AND => 36,
- :NTH_PSEUDO_CLASS => 37,
- :DOCUMENT_QUERY_SYM => 38,
- :FUNCTION_NO_QUOTE => 39,
- :TILDE => 40,
- :PREFIXMATCH => 41,
- :SUFFIXMATCH => 42,
- :SUBSTRINGMATCH => 43,
- :NOT_PSEUDO_CLASS => 44,
- :KEYFRAMES_SYM => 45,
- :MATCHES_PSEUDO_CLASS => 46,
- :NAMESPACE_SYM => 47,
- :MOZ_PSEUDO_ELEMENT => 48,
- :RESOLUTION => 49,
- :COLON => 50,
- :SUPPORTS_SYM => 51,
- :OR => 52,
- :VARIABLE_NAME => 53,
- :CALC_SYM => 54,
- :FONTFACE_SYM => 55,
- :UNICODE_RANGE => 56,
- :RATIO => 57,
- "|" => 58,
- "." => 59 }
-
-racc_nt_base = 60
-
-racc_use_result_var = true
-
-Racc_arg = [
- racc_action_table,
- racc_action_check,
- racc_action_default,
- racc_action_pointer,
- racc_goto_table,
- racc_goto_check,
- racc_goto_default,
- racc_goto_pointer,
- racc_nt_base,
- racc_reduce_table,
- racc_token_table,
- racc_shift_n,
- racc_reduce_n,
- racc_use_result_var ]
-Ractor.make_shareable(Racc_arg) if defined?(Ractor)
-
-Racc_token_to_s_table = [
- "$end",
- "error",
- "CHARSET_SYM",
- "IMPORT_SYM",
- "STRING",
- "SEMI",
- "IDENT",
- "S",
- "COMMA",
- "LBRACE",
- "RBRACE",
- "STAR",
- "HASH",
- "LSQUARE",
- "RSQUARE",
- "EQUAL",
- "INCLUDES",
- "DASHMATCH",
- "LPAREN",
- "RPAREN",
- "FUNCTION",
- "GREATER",
- "PLUS",
- "SLASH",
- "NUMBER",
- "MINUS",
- "LENGTH",
- "PERCENTAGE",
- "ANGLE",
- "TIME",
- "FREQ",
- "URI",
- "IMPORTANT_SYM",
- "MEDIA_SYM",
- "NOT",
- "ONLY",
- "AND",
- "NTH_PSEUDO_CLASS",
- "DOCUMENT_QUERY_SYM",
- "FUNCTION_NO_QUOTE",
- "TILDE",
- "PREFIXMATCH",
- "SUFFIXMATCH",
- "SUBSTRINGMATCH",
- "NOT_PSEUDO_CLASS",
- "KEYFRAMES_SYM",
- "MATCHES_PSEUDO_CLASS",
- "NAMESPACE_SYM",
- "MOZ_PSEUDO_ELEMENT",
- "RESOLUTION",
- "COLON",
- "SUPPORTS_SYM",
- "OR",
- "VARIABLE_NAME",
- "CALC_SYM",
- "FONTFACE_SYM",
- "UNICODE_RANGE",
- "RATIO",
- "\"|\"",
- "\".\"",
- "$start",
- "document",
- "stylesheet",
- "@1",
- "charset",
- "import",
- "namespace",
- "body",
- "import_location",
- "medium",
- "ident",
- "media_query_list",
- "media_query",
- "optional_only_or_not",
- "media_type",
- "optional_and_exprs",
- "media_expr",
- "optional_space",
- "expr",
- "resolution",
- "ruleset",
- "conditional_rule",
- "keyframes_rule",
- "fontface_rule",
- "media",
- "document_query",
- "supports",
- "body_in_media",
- "empty_ruleset",
- "start_media",
- "start_document_query",
- "start_document_query_pos",
- "url_match_fns",
- "url_match_fn",
- "function_no_quote",
- "function",
- "uri",
- "start_supports",
- "supports_condition_root",
- "supports_negation",
- "supports_conjunction_or_disjunction",
- "supports_condition_in_parens",
- "supports_condition",
- "supports_declaration_condition",
- "supports_conjunction",
- "supports_disjunction",
- "declaration_internal",
- "start_keyframes_rule",
- "keyframes_blocks",
- "keyframes_block",
- "start_keyframes_block",
- "declarations",
- "keyframes_selectors",
- "keyframes_selector",
- "start_fontface_rule",
- "start_selector",
- "selectors",
- "selector",
- "simple_selector",
- "combinator",
- "element_name",
- "hcap",
- "simple_selectors",
- "ident_with_namespace",
- "hash",
- "class",
- "attrib",
- "pseudo",
- "any_number_of_idents",
- "multiple_idents",
- "one_or_more_semis",
- "declaration",
- "property",
- "prio",
- "operator",
- "term",
- "ratio",
- "numeric",
- "string",
- "hexcolor",
- "calc",
- "uranges",
- "calc_sum",
- "calc_product",
- "calc_value",
- "unary_operator" ]
-Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)
-
-Racc_debug_parser = false
-
-##### State transition tables end #####
-
-# reduce 0 omitted
-
-module_eval(<<'.,.,', 'csspool.y', 26)
- def _reduce_1(val, _values, result)
- @handler.start_document
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 28)
- def _reduce_2(val, _values, result)
- @handler.end_document
- result
- end
-.,.,
-
-# reduce 3 omitted
-
-# reduce 4 omitted
-
-# reduce 5 omitted
-
-# reduce 6 omitted
-
-# reduce 7 omitted
-
-# reduce 8 omitted
-
-# reduce 9 omitted
-
-# reduce 10 omitted
-
-module_eval(<<'.,.,', 'csspool.y', 41)
- def _reduce_11(val, _values, result)
- @handler.charset interpret_string(val[1]), {}
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 45)
- def _reduce_12(val, _values, result)
- @handler.import_style val[2], val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 48)
- def _reduce_13(val, _values, result)
- @handler.import_style [], val[1]
-
- result
- end
-.,.,
-
-# reduce 14 omitted
-
-module_eval(<<'.,.,', 'csspool.y', 53)
- def _reduce_15(val, _values, result)
- result = Terms::String.new interpret_string val.first
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 54)
- def _reduce_16(val, _values, result)
- result = Terms::URI.new interpret_uri val.first
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 58)
- def _reduce_17(val, _values, result)
- @handler.namespace val[1], val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 61)
- def _reduce_18(val, _values, result)
- @handler.namespace nil, val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 66)
- def _reduce_19(val, _values, result)
- result = val[0] << MediaType.new(val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 69)
- def _reduce_20(val, _values, result)
- result = [MediaType.new(val[0])]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 73)
- def _reduce_21(val, _values, result)
- result = MediaQueryList.new([ val[0] ])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 74)
- def _reduce_22(val, _values, result)
- result = val[0] << val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 75)
- def _reduce_23(val, _values, result)
- result = MediaQueryList.new
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 78)
- def _reduce_24(val, _values, result)
- result = MediaQuery.new(val[0], val[1], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 79)
- def _reduce_25(val, _values, result)
- result = MediaQuery.new(nil, val[0], val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 82)
- def _reduce_26(val, _values, result)
- result = :only
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 83)
- def _reduce_27(val, _values, result)
- result = :not
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 84)
- def _reduce_28(val, _values, result)
- result = nil
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 87)
- def _reduce_29(val, _values, result)
- result = MediaType.new(val[0])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 90)
- def _reduce_30(val, _values, result)
- result = MediaType.new(val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 91)
- def _reduce_31(val, _values, result)
- result = MediaFeature.new(val[2], val[6][0])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 94)
- def _reduce_32(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 95)
- def _reduce_33(val, _values, result)
- result = nil
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 98)
- def _reduce_34(val, _values, result)
- result = val[0] << val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 99)
- def _reduce_35(val, _values, result)
- result = []
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 103)
- def _reduce_36(val, _values, result)
- unit = val.first.gsub(/[\s\d.]/, '')
- number = numeric(val.first)
- result = Terms::Resolution.new(number, unit)
-
- result
- end
-.,.,
-
-# reduce 37 omitted
-
-# reduce 38 omitted
-
-# reduce 39 omitted
-
-# reduce 40 omitted
-
-# reduce 41 omitted
-
-# reduce 42 omitted
-
-# reduce 43 omitted
-
-# reduce 44 omitted
-
-# reduce 45 omitted
-
-# reduce 46 omitted
-
-# reduce 47 omitted
-
-# reduce 48 omitted
-
-# reduce 49 omitted
-
-module_eval(<<'.,.,', 'csspool.y', 128)
- def _reduce_50(val, _values, result)
- @handler.end_media val.first
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 132)
- def _reduce_51(val, _values, result)
- result = val[1]
- @handler.start_media result
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 137)
- def _reduce_52(val, _values, result)
- @handler.end_document_query(before_pos(val), after_pos(val))
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 138)
- def _reduce_53(val, _values, result)
- @handler.end_document_query(before_pos(val), after_pos(val))
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 142)
- def _reduce_54(val, _values, result)
- @handler.start_document_query(val[1], after_pos(val))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 147)
- def _reduce_55(val, _values, result)
- @handler.node_start_pos = before_pos(val)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 152)
- def _reduce_56(val, _values, result)
- result = [val[0], val[2]].flatten
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 155)
- def _reduce_57(val, _values, result)
- result = val
-
- result
- end
-.,.,
-
-# reduce 58 omitted
-
-# reduce 59 omitted
-
-# reduce 60 omitted
-
-module_eval(<<'.,.,', 'csspool.y', 164)
- def _reduce_61(val, _values, result)
- @handler.end_supports
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 165)
- def _reduce_62(val, _values, result)
- @handler.end_supports
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 169)
- def _reduce_63(val, _values, result)
- @handler.start_supports val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 173)
- def _reduce_64(val, _values, result)
- result = val.join('')
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 174)
- def _reduce_65(val, _values, result)
- result = val.join('')
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 175)
- def _reduce_66(val, _values, result)
- result = val.join('')
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 178)
- def _reduce_67(val, _values, result)
- result = val.join('')
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 179)
- def _reduce_68(val, _values, result)
- result = val.join('')
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 180)
- def _reduce_69(val, _values, result)
- result = val.join('')
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 183)
- def _reduce_70(val, _values, result)
- result = val.join('')
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 184)
- def _reduce_71(val, _values, result)
- result = val.join('')
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 187)
- def _reduce_72(val, _values, result)
- result = val.join('')
- result
- end
-.,.,
-
-# reduce 73 omitted
-
-# reduce 74 omitted
-
-module_eval(<<'.,.,', 'csspool.y', 194)
- def _reduce_75(val, _values, result)
- result = val.join('')
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 195)
- def _reduce_76(val, _values, result)
- result = val.join('')
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 198)
- def _reduce_77(val, _values, result)
- result = val.join('')
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 199)
- def _reduce_78(val, _values, result)
- result = val.join('')
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 202)
- def _reduce_79(val, _values, result)
- result = val.join('')
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 203)
- def _reduce_80(val, _values, result)
- result = val.join('')
- result
- end
-.,.,
-
-# reduce 81 omitted
-
-# reduce 82 omitted
-
-module_eval(<<'.,.,', 'csspool.y', 211)
- def _reduce_83(val, _values, result)
- @handler.start_keyframes_rule val[1]
-
- result
- end
-.,.,
-
-# reduce 84 omitted
-
-# reduce 85 omitted
-
-module_eval(<<'.,.,', 'csspool.y', 219)
- def _reduce_86(val, _values, result)
- @handler.end_keyframes_block
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 220)
- def _reduce_87(val, _values, result)
- @handler.end_keyframes_block
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 224)
- def _reduce_88(val, _values, result)
- @handler.start_keyframes_block val[0]
-
- result
- end
-.,.,
-
-# reduce 89 omitted
-
-module_eval(<<'.,.,', 'csspool.y', 229)
- def _reduce_90(val, _values, result)
- result = val[0] + ', ' + val[2]
-
- result
- end
-.,.,
-
-# reduce 91 omitted
-
-# reduce 92 omitted
-
-module_eval(<<'.,.,', 'csspool.y', 235)
- def _reduce_93(val, _values, result)
- result = val[0].strip
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 238)
- def _reduce_94(val, _values, result)
- @handler.end_fontface_rule
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 239)
- def _reduce_95(val, _values, result)
- @handler.end_fontface_rule
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 243)
- def _reduce_96(val, _values, result)
- @handler.start_fontface_rule
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 248)
- def _reduce_97(val, _values, result)
- @handler.end_selector val.first
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 251)
- def _reduce_98(val, _values, result)
- @handler.end_selector val.first
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 256)
- def _reduce_99(val, _values, result)
- start = @handler.start_selector([])
- @handler.end_selector(start)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 261)
- def _reduce_100(val, _values, result)
- result = val.last
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 263)
- def _reduce_101(val, _values, result)
- @handler.start_selector val.first
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 269)
- def _reduce_102(val, _values, result)
- sel = Selector.new(val.first, {})
- result = [sel].concat(val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 274)
- def _reduce_103(val, _values, result)
- result = [Selector.new(val.first, {})]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 280)
- def _reduce_104(val, _values, result)
- val.flatten!
- val[2].combinator = val.delete_at 1
- result = val
-
- result
- end
-.,.,
-
-# reduce 105 omitted
-
-module_eval(<<'.,.,', 'csspool.y', 287)
- def _reduce_106(val, _values, result)
- result = :s
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 288)
- def _reduce_107(val, _values, result)
- result = :>
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 289)
- def _reduce_108(val, _values, result)
- result = :+
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 290)
- def _reduce_109(val, _values, result)
- result = :~
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 294)
- def _reduce_110(val, _values, result)
- selector = val.first
- selector.additional_selectors = val.last
- result = [selector]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 298)
- def _reduce_111(val, _values, result)
- result = val
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 301)
- def _reduce_112(val, _values, result)
- ss = Selectors::Simple.new nil, nil
- ss.additional_selectors = val.flatten
- result = [ss]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 307)
- def _reduce_113(val, _values, result)
- result = [val[0], val[2]].flatten
- result
- end
-.,.,
-
-# reduce 114 omitted
-
-module_eval(<<'.,.,', 'csspool.y', 311)
- def _reduce_115(val, _values, result)
- result = [interpret_identifier(val[0]), nil]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 312)
- def _reduce_116(val, _values, result)
- result = [interpret_identifier(val[2]), interpret_identifier(val[0])]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 313)
- def _reduce_117(val, _values, result)
- result = [interpret_identifier(val[1]), nil]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 314)
- def _reduce_118(val, _values, result)
- result = [interpret_identifier(val[2]), '*']
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 317)
- def _reduce_119(val, _values, result)
- result = Selectors::Type.new val.first[0], nil, val.first[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 318)
- def _reduce_120(val, _values, result)
- result = Selectors::Universal.new val.first
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 319)
- def _reduce_121(val, _values, result)
- result = Selectors::Universal.new val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 320)
- def _reduce_122(val, _values, result)
- result = Selectors::Universal.new val[2], nil, val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 321)
- def _reduce_123(val, _values, result)
- result = Selectors::Universal.new val[2], nil, interpret_identifier(val[0])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 324)
- def _reduce_124(val, _values, result)
- result = val
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 325)
- def _reduce_125(val, _values, result)
- result = val
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 326)
- def _reduce_126(val, _values, result)
- result = val
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 327)
- def _reduce_127(val, _values, result)
- result = val
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 328)
- def _reduce_128(val, _values, result)
- result = val.flatten
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 329)
- def _reduce_129(val, _values, result)
- result = val.flatten
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 330)
- def _reduce_130(val, _values, result)
- result = val.flatten
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 331)
- def _reduce_131(val, _values, result)
- result = val.flatten
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 335)
- def _reduce_132(val, _values, result)
- result = Selectors::Id.new interpret_identifier val.first.sub(/^#/, '')
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 339)
- def _reduce_133(val, _values, result)
- result = Selectors::Class.new interpret_identifier val.last
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 344)
- def _reduce_134(val, _values, result)
- result = Selectors::Attribute.new(
- val[1][0],
- interpret_identifier(val[3]),
- Selectors::Attribute::EQUALS,
- val[1][1]
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 352)
- def _reduce_135(val, _values, result)
- result = Selectors::Attribute.new(
- val[1][0],
- interpret_string(val[3]),
- Selectors::Attribute::EQUALS,
- val[1][1]
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 360)
- def _reduce_136(val, _values, result)
- result = Selectors::Attribute.new(
- val[1][0],
- interpret_string(val[3]),
- Selectors::Attribute::INCLUDES,
- val[1][1]
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 368)
- def _reduce_137(val, _values, result)
- result = Selectors::Attribute.new(
- val[1][0],
- interpret_identifier(val[3]),
- Selectors::Attribute::INCLUDES,
- val[1][1]
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 376)
- def _reduce_138(val, _values, result)
- result = Selectors::Attribute.new(
- val[1][0],
- interpret_identifier(val[3]),
- Selectors::Attribute::DASHMATCH,
- val[1][1]
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 384)
- def _reduce_139(val, _values, result)
- result = Selectors::Attribute.new(
- val[1][0],
- interpret_string(val[3]),
- Selectors::Attribute::DASHMATCH,
- val[1][1]
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 392)
- def _reduce_140(val, _values, result)
- result = Selectors::Attribute.new(
- val[1][0],
- interpret_identifier(val[3]),
- Selectors::Attribute::PREFIXMATCH,
- val[1][1]
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 400)
- def _reduce_141(val, _values, result)
- result = Selectors::Attribute.new(
- val[1][0],
- interpret_string(val[3]),
- Selectors::Attribute::PREFIXMATCH,
- val[1][1]
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 408)
- def _reduce_142(val, _values, result)
- result = Selectors::Attribute.new(
- val[1][0],
- interpret_identifier(val[3]),
- Selectors::Attribute::SUFFIXMATCH,
- val[1][1]
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 416)
- def _reduce_143(val, _values, result)
- result = Selectors::Attribute.new(
- val[1][0],
- interpret_string(val[3]),
- Selectors::Attribute::SUFFIXMATCH,
- val[1][1]
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 424)
- def _reduce_144(val, _values, result)
- result = Selectors::Attribute.new(
- val[1][0],
- interpret_identifier(val[3]),
- Selectors::Attribute::SUBSTRINGMATCH,
- val[1][1]
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 432)
- def _reduce_145(val, _values, result)
- result = Selectors::Attribute.new(
- val[1][0],
- interpret_string(val[3]),
- Selectors::Attribute::SUBSTRINGMATCH,
- val[1][1]
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 440)
- def _reduce_146(val, _values, result)
- result = Selectors::Attribute.new(
- val[1][0],
- nil,
- Selectors::Attribute::SET,
- val[1][1]
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 450)
- def _reduce_147(val, _values, result)
- result = Selectors::pseudo interpret_identifier(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 453)
- def _reduce_148(val, _values, result)
- result = Selectors::PseudoElement.new(
- interpret_identifier(val[2])
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 458)
- def _reduce_149(val, _values, result)
- result = Selectors::PseudoClass.new(
- interpret_identifier(val[1].sub(/\($/, '')),
- ''
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 464)
- def _reduce_150(val, _values, result)
- result = Selectors::PseudoClass.new(
- interpret_identifier(val[1].sub(/\($/, '')),
- interpret_identifier(val[2])
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 470)
- def _reduce_151(val, _values, result)
- result = Selectors::PseudoClass.new(
- 'not',
- val[2].first.to_s
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 476)
- def _reduce_152(val, _values, result)
- result = Selectors::PseudoClass.new(
- interpret_identifier(val[1].sub(/\(.*/, '')),
- interpret_identifier(val[1].sub(/.*\(/, '').sub(/\).*/, ''))
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 482)
- def _reduce_153(val, _values, result)
- result = Selectors::PseudoClass.new(
- val[1].split('(').first.strip,
- val[2].join(', ')
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 488)
- def _reduce_154(val, _values, result)
- result = Selectors::PseudoElement.new(
- interpret_identifier(val[1].sub(/\($/, ''))
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 493)
- def _reduce_155(val, _values, result)
- result = Selectors::PseudoElement.new(
- interpret_identifier(val[2].sub(/\($/, ''))
- )
-
- result
- end
-.,.,
-
-# reduce 156 omitted
-
-# reduce 157 omitted
-
-# reduce 158 omitted
-
-# reduce 159 omitted
-
-# reduce 160 omitted
-
-# reduce 161 omitted
-
-# reduce 162 omitted
-
-# reduce 163 omitted
-
-# reduce 164 omitted
-
-# reduce 165 omitted
-
-# reduce 166 omitted
-
-module_eval(<<'.,.,', 'csspool.y', 519)
- def _reduce_167(val, _values, result)
- @handler.property val.first
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 523)
- def _reduce_168(val, _values, result)
- result = Declaration.new(val.first, val[2], val[3])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 525)
- def _reduce_169(val, _values, result)
- result = Declaration.new(val.first, val[3], val[4])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 527)
- def _reduce_170(val, _values, result)
- result = Declaration.new(val.first, val[3], val[4])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 529)
- def _reduce_171(val, _values, result)
- result = Declaration.new(val.first, val[4], val[5])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 532)
- def _reduce_172(val, _values, result)
- result = true
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 533)
- def _reduce_173(val, _values, result)
- result = false
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 536)
- def _reduce_174(val, _values, result)
- result = interpret_identifier val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 537)
- def _reduce_175(val, _values, result)
- result = interpret_identifier val.join
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 538)
- def _reduce_176(val, _values, result)
- result = interpret_identifier val[0]
- result
- end
-.,.,
-
-# reduce 177 omitted
-
-# reduce 178 omitted
-
-# reduce 179 omitted
-
-module_eval(<<'.,.,', 'csspool.y', 547)
- def _reduce_180(val, _values, result)
- result = [val.first, val.last].flatten
- val.last.first.operator = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 550)
- def _reduce_181(val, _values, result)
- result = val.flatten
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 551)
- def _reduce_182(val, _values, result)
- result = val
- result
- end
-.,.,
-
-# reduce 183 omitted
-
-# reduce 184 omitted
-
-# reduce 185 omitted
-
-# reduce 186 omitted
-
-# reduce 187 omitted
-
-# reduce 188 omitted
-
-# reduce 189 omitted
-
-# reduce 190 omitted
-
-# reduce 191 omitted
-
-# reduce 192 omitted
-
-# reduce 193 omitted
-
-module_eval(<<'.,.,', 'csspool.y', 567)
- def _reduce_194(val, _values, result)
- result = val.first
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 569)
- def _reduce_195(val, _values, result)
- name = interpret_identifier val.first.sub(/\($/, '')
- if name == 'rgb'
- result = Terms::Rgb.new(*val[1])
- else
- result = Terms::Function.new name, val[1]
- end
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 577)
- def _reduce_196(val, _values, result)
- name = interpret_identifier val.first.sub(/\($/, '')
- result = Terms::Function.new name
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 582)
- def _reduce_197(val, _values, result)
- result = val.first
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 584)
- def _reduce_198(val, _values, result)
- parts = val.first.split('(')
- name = interpret_identifier parts.first
- result = Terms::Function.new(name, [Terms::String.new(interpret_string_no_quote(parts.last))])
-
- result
- end
-.,.,
-
-# reduce 199 omitted
-
-# reduce 200 omitted
-
-module_eval(<<'.,.,', 'csspool.y', 595)
- def _reduce_201(val, _values, result)
- result = Terms::Math.new(val.first.split('(').first, val[1])
-
- result
- end
-.,.,
-
-# reduce 202 omitted
-
-module_eval(<<'.,.,', 'csspool.y', 601)
- def _reduce_203(val, _values, result)
- val.insert(1, ' '); result = val.join('')
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 602)
- def _reduce_204(val, _values, result)
- val.insert(1, ' '); result = val.join('')
- result
- end
-.,.,
-
-# reduce 205 omitted
-
-module_eval(<<'.,.,', 'csspool.y', 606)
- def _reduce_206(val, _values, result)
- result = val.join('')
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 607)
- def _reduce_207(val, _values, result)
- result = val.join('')
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 610)
- def _reduce_208(val, _values, result)
- result = val.join('')
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 611)
- def _reduce_209(val, _values, result)
- result = val.join('')
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 612)
- def _reduce_210(val, _values, result)
- result = val.join('')
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 615)
- def _reduce_211(val, _values, result)
- result = val.first
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 616)
- def _reduce_212(val, _values, result)
- result = Terms::Hash.new val.first.sub(/^#/, '')
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 619)
- def _reduce_213(val, _values, result)
- result = val.first
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 620)
- def _reduce_214(val, _values, result)
- result = Terms::URI.new interpret_uri val.first
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 623)
- def _reduce_215(val, _values, result)
- result = val.first
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 624)
- def _reduce_216(val, _values, result)
- result = Terms::String.new interpret_string val.first
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 628)
- def _reduce_217(val, _values, result)
- result = val[1]
- val[1].unary_operator = val.first
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 632)
- def _reduce_218(val, _values, result)
- result = Terms::Number.new numeric val.first
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 635)
- def _reduce_219(val, _values, result)
- result = Terms::Number.new numeric(val.first), nil, '%'
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 638)
- def _reduce_220(val, _values, result)
- unit = val.first.gsub(/[\s\d.]/, '')
- result = Terms::Number.new numeric(val.first), nil, unit
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 642)
- def _reduce_221(val, _values, result)
- unit = val.first.gsub(/[\s\d.]/, '')
- result = Terms::Number.new numeric(val.first), nil, unit
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 646)
- def _reduce_222(val, _values, result)
- unit = val.first.gsub(/[\s\d.]/, '')
- result = Terms::Number.new numeric(val.first), nil, unit
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 650)
- def _reduce_223(val, _values, result)
- unit = val.first.gsub(/[\s\d.]/, '')
- result = Terms::Number.new numeric(val.first), nil, unit
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 656)
- def _reduce_224(val, _values, result)
- result = Terms::Ratio.new(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 660)
- def _reduce_225(val, _values, result)
- result = :minus
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 661)
- def _reduce_226(val, _values, result)
- result = :plus
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 664)
- def _reduce_227(val, _values, result)
- result = val.first
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'csspool.y', 665)
- def _reduce_228(val, _values, result)
- result = Terms::Ident.new interpret_identifier val.first
- result
- end
-.,.,
-
-def _reduce_none(val, _values, result)
- val[0]
-end
-
- end # class Parser
- end # module CSS
-end # module CSSPool
diff --git a/test/racc/regress/edtf b/test/racc/regress/edtf
deleted file mode 100644
index 2b2cfd5892..0000000000
--- a/test/racc/regress/edtf
+++ /dev/null
@@ -1,1796 +0,0 @@
-#
-# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.5.2
-# from Racc grammar file "".
-#
-
-require 'racc/parser.rb'
-
-require 'strscan'
-
-module EDTF
- class Parser < Racc::Parser
-
-module_eval(<<'...end edtf.y/module_eval...', 'edtf.y', 468)
-
- @defaults = {
- :level => 2,
- :debug => false
- }.freeze
-
- class << self; attr_reader :defaults; end
-
- attr_reader :options
-
- def initialize(options = {})
- @options = Parser.defaults.merge(options)
- end
-
- def debug?
- !!(options[:debug] || ENV['DEBUG'])
- end
-
- def parse(input)
- parse!(input)
- rescue => e
- warn e.message if debug?
- nil
- end
-
- def parse!(input)
- @yydebug = debug?
- @src = StringScanner.new(input)
- do_parse
- end
-
- def on_error(tid, value, stack)
- raise ArgumentError,
- "failed to parse date: unexpected '#{value}' at #{stack.inspect}"
- end
-
- def apply_uncertainty(date, uncertainty, scope = nil)
- uncertainty.each do |u|
- scope.nil? ? date.send(u) : date.send(u, scope)
- end
- date
- end
-
- alias uoa apply_uncertainty
-
- def next_token
- case
- when @src.eos?
- nil
- # when @src.scan(/\s+/)
- # ignore whitespace
- when @src.scan(/\(/)
- ['(', @src.matched]
- # when @src.scan(/\)\?~-/)
- # [:PUA, [:uncertain!, :approximate!]]
- # when @src.scan(/\)\?-/)
- # [:PUA, [:uncertain!]]
- # when @src.scan(/\)~-/)
- # [:PUA, [:approximate!]]
- when @src.scan(/\)/)
- [')', @src.matched]
- when @src.scan(/\[/)
- ['[', @src.matched]
- when @src.scan(/\]/)
- [']', @src.matched]
- when @src.scan(/\{/)
- ['{', @src.matched]
- when @src.scan(/\}/)
- ['}', @src.matched]
- when @src.scan(/T/)
- [:T, @src.matched]
- when @src.scan(/Z/)
- [:Z, @src.matched]
- when @src.scan(/\?~/)
- [:UA, [:uncertain!, :approximate!]]
- when @src.scan(/\?/)
- [:UA, [:uncertain!]]
- when @src.scan(/~/)
- [:UA, [:approximate!]]
- when @src.scan(/open/i)
- [:OPEN, :open]
- when @src.scan(/unkn?own/i) # matches 'unkown' typo too
- [:UNKNOWN, :unknown]
- when @src.scan(/u/)
- [:U, @src.matched]
- when @src.scan(/x/i)
- [:X, @src.matched]
- when @src.scan(/y/)
- [:LONGYEAR, @src.matched]
- when @src.scan(/e/)
- [:E, @src.matched]
- when @src.scan(/\+/)
- ['+', @src.matched]
- when @src.scan(/-\(/)
- ['-(', @src.matched]
- when @src.scan(/-/)
- ['-', @src.matched]
- when @src.scan(/:/)
- [':', @src.matched]
- when @src.scan(/\//)
- ['/', @src.matched]
- when @src.scan(/\s*\.\.\s*/)
- [:DOTS, '..']
- when @src.scan(/\s*,\s*/)
- [',', ',']
- when @src.scan(/\^\w+/)
- ['^', @src.matched[1..-1]]
- when @src.scan(/\d/)
- [@src.matched, @src.matched.to_i]
- else @src.scan(/./)
- [:UNMATCHED, @src.rest]
- end
- end
-
-
-# -*- racc -*-
-...end edtf.y/module_eval...
-##### State transition tables begin ###
-
-racc_action_table = [
- 129, 128, 52, 111, 51, 112, 149, 208, 207, 57,
- -50, 43, 45, 40, 55, 42, 54, 44, 43, 45,
- 40, -48, 42, 53, 44, 64, 58, 46, 47, 48,
- 49, 50, 128, 56, 46, 47, 48, 49, 50, 207,
- 57, 65, 43, 45, 40, 55, 42, 157, 44, 43,
- 45, 40, 55, 42, 214, 44, 92, 58, 46, 47,
- 48, 49, 50, 66, 56, 46, 47, 48, 49, 50,
- 25, 56, 26, 93, 94, 67, 108, 12, -65, 43,
- 45, 40, -66, 42, 159, 44, 110, 33, 111, 34,
- 112, 95, 36, 25, 141, 46, 47, 48, 49, 50,
- 12, 58, 43, 45, 40, 101, 42, 103, 44, 104,
- 96, 148, 55, 133, 147, 36, 124, 125, 46, 47,
- 48, 49, 50, 87, 165, 111, 12, 112, 43, 45,
- 40, 56, 42, 146, 44, 166, 111, 150, 112, 218,
- 167, 36, 152, 153, 46, 47, 48, 49, 50, 87,
- 108, 111, 12, 112, 43, 45, 40, 188, 42, 186,
- 44, 187, 111, 190, 112, 154, 111, 36, 112, 156,
- 46, 47, 48, 49, 50, 69, 158, 43, 45, 189,
- 191, 42, 12, 44, 43, 45, 40, 200, 42, 201,
- 44, 168, 177, 46, 47, 48, 49, 50, 233, 178,
- 46, 47, 48, 49, 50, 12, 180, 43, 45, 40,
- 111, 42, 112, 44, 232, 234, 111, 240, 112, 239,
- 36, 192, 193, 46, 47, 48, 49, 50, 12, 202,
- 43, 45, 40, 118, 42, 117, 44, 104, 118, 121,
- 117, 209, 104, 36, 121, 210, 46, 47, 48, 49,
- 50, 12, 212, 43, 45, 40, 244, 42, 239, 44,
- 213, 43, 45, 40, 215, 42, 36, 44, 229, 46,
- 47, 48, 49, 50, 180, 180, 236, 46, 47, 48,
- 49, 50, 43, 45, 40, 253, 42, 254, 44, 43,
- 45, 40, 255, 42, 258, 44, 261, 264, 46, 47,
- 48, 49, 50, 124, 125, 46, 47, 48, 49, 50,
- 43, 45, 40, 265, 42, 192, 44, 43, 45, 266,
- 269, 42, 270, 44, 275, 280, 46, 47, 48, 49,
- 50, 284, 285, 46, 47, 48, 49, 50, 43, 45,
- 40, 286, 42, 290, 44, 43, 45, 292, 293, 42,
- 295, 44, 296, 297, 46, 47, 48, 49, 50, 300,
- 301, 46, 47, 48, 49, 50, 43, 45, 40, 180,
- 42, 303, 44, 43, 45, 40, 304, 42, 305, 44,
- 281, 306, 46, 47, 48, 49, 50, 307, 308, 46,
- 47, 48, 49, 50, 43, 45, 175, 311, 42, 312,
- 44, 43, 45, 40, 313, 42, 314, 44, 316, 317,
- 46, 47, 48, 49, 50, 318, 319, 46, 47, 48,
- 49, 50, 43, 45, nil, nil, 42, nil, 44, 43,
- 45, nil, nil, 42, nil, 44, nil, nil, 46, 47,
- 48, 49, 50, nil, nil, 46, 47, 48, 49, 50,
- 172, 194, 170, nil, 171, nil, 173, 43, 45, 40,
- nil, 42, nil, 44, nil, nil, 195, 196, 197, 198,
- 199, nil, nil, 46, 47, 48, 49, 50, 43, 45,
- 40, nil, 42, nil, 44, 43, 45, 40, nil, 42,
- nil, 44, nil, nil, 46, 47, 48, 49, 50, nil,
- nil, 46, 47, 48, 49, 50, 43, 45, 40, nil,
- 42, nil, 44, 43, 45, nil, nil, 42, nil, 44,
- nil, nil, 46, 47, 48, 49, 50, nil, nil, 46,
- 47, 48, 49, 50, 43, 45, 40, nil, 42, nil,
- 44, 43, 45, 40, nil, 42, nil, 44, nil, nil,
- 46, 47, 48, 49, 50, nil, nil, 46, 47, 48,
- 49, 50, 43, 45, 40, nil, 42, nil, 44, 43,
- 45, 40, nil, 42, nil, 44, nil, nil, 46, 47,
- 48, 49, 50, nil, nil, 46, 47, 48, 49, 50,
- 43, 45, nil, nil, 42, nil, 44, 43, 45, 40,
- nil, 42, nil, 44, nil, nil, 46, 47, 48, 49,
- 50, nil, nil, 46, 47, 48, 49, 50, 43, 45,
- 40, nil, 42, nil, 44, 43, 45, 273, nil, 42,
- nil, 44, nil, nil, 46, 47, 48, 49, 50, nil,
- nil, 46, 47, 48, 49, 50, 43, 45, 274, nil,
- 42, nil, 44, 43, 45, 276, nil, 42, nil, 44,
- nil, nil, 46, 47, 48, 49, 50, nil, nil, 46,
- 47, 48, 49, 50, 43, 45, 40, nil, 42, nil,
- 44, 43, 45, 40, nil, 42, nil, 44, nil, nil,
- 46, 47, 48, 49, 50, nil, nil, 46, 47, 48,
- 49, 50, 43, 45, 40, nil, 42, nil, 44, 43,
- 45, 40, nil, 42, nil, 44, nil, nil, 46, 47,
- 48, 49, 50, nil, nil, 46, 47, 48, 49, 50,
- 43, 45, 40, nil, 42, nil, 44, 43, 45, 315,
- nil, 42, nil, 44, nil, nil, 46, 47, 48, 49,
- 50, 116, nil, 46, 47, 48, 49, 50, 118, 250,
- 247, 118, 104, 117, 249, 104, 260, 121, nil, 281,
- nil, nil, nil, nil, 251, nil, 118, 288, 117, 118,
- 104, 117, 121, 104, 118, 121, 117, 118, 104, 117,
- 121, 104, nil, 121, 118, 250, 247, nil, 104, nil,
- 249, 118, 250, 247, nil, 104, nil, 249, nil, nil,
- 251, nil, 118, 250, 117, nil, 104, 251, 249, 118,
- 250, 310, nil, 104, nil, 249, nil, nil, 251, nil,
- 172, 169, 170, nil, 171, 251, 173, 182, 184, nil,
- 118, 181, 117, 183, 104, 118, 121, 117, 118, 104,
- 117, 121, 104, 118, 121, 117, 118, 104, 117, 121,
- 104, 118, 121, 117, nil, 104, nil, 121, 188, 271,
- 186, 118, 187, 117, 272, 104, nil, 121 ]
-
-racc_action_check = [
- 63, 63, 5, 56, 1, 56, 73, 127, 127, 73,
- 14, 63, 63, 63, 9, 63, 9, 63, 127, 127,
- 127, 5, 127, 5, 127, 16, 73, 63, 63, 63,
- 63, 63, 151, 9, 127, 127, 127, 127, 127, 224,
- 10, 17, 151, 151, 151, 89, 151, 89, 151, 224,
- 224, 224, 134, 224, 134, 224, 37, 10, 151, 151,
- 151, 151, 151, 18, 89, 224, 224, 224, 224, 224,
- 0, 134, 0, 37, 38, 22, 54, 0, 23, 0,
- 0, 0, 24, 0, 91, 0, 54, 0, 54, 0,
- 54, 38, 0, 67, 67, 0, 0, 0, 0, 0,
- 67, 91, 67, 67, 67, 52, 67, 52, 67, 52,
- 51, 72, 72, 66, 72, 67, 59, 60, 67, 67,
- 67, 67, 67, 33, 98, 66, 33, 66, 33, 33,
- 33, 72, 33, 71, 33, 98, 92, 74, 92, 147,
- 98, 33, 77, 78, 33, 33, 33, 33, 33, 34,
- 214, 147, 34, 147, 34, 34, 34, 112, 34, 112,
- 34, 112, 214, 113, 214, 79, 93, 34, 93, 88,
- 34, 34, 34, 34, 34, 26, 90, 26, 26, 113,
- 113, 26, 87, 26, 87, 87, 87, 121, 87, 121,
- 87, 99, 107, 26, 26, 26, 26, 26, 161, 108,
- 87, 87, 87, 87, 87, 153, 109, 153, 153, 153,
- 124, 153, 124, 153, 161, 161, 157, 166, 157, 166,
- 153, 115, 116, 153, 153, 153, 153, 153, 154, 123,
- 154, 154, 154, 58, 154, 58, 154, 58, 94, 58,
- 94, 128, 94, 154, 94, 129, 154, 154, 154, 154,
- 154, 265, 132, 265, 265, 265, 167, 265, 167, 265,
- 133, 12, 12, 12, 144, 12, 265, 12, 158, 265,
- 265, 265, 265, 265, 160, 162, 163, 12, 12, 12,
- 12, 12, 13, 13, 13, 169, 13, 178, 13, 36,
- 36, 36, 189, 36, 202, 36, 213, 218, 13, 13,
- 13, 13, 13, 220, 222, 36, 36, 36, 36, 36,
- 62, 62, 62, 225, 62, 230, 62, 64, 64, 232,
- 236, 64, 238, 64, 245, 253, 62, 62, 62, 62,
- 62, 256, 257, 64, 64, 64, 64, 64, 68, 68,
- 68, 260, 68, 264, 68, 69, 69, 267, 268, 69,
- 271, 69, 273, 274, 68, 68, 68, 68, 68, 280,
- 281, 69, 69, 69, 69, 69, 70, 70, 70, 283,
- 70, 284, 70, 75, 75, 75, 285, 75, 288, 75,
- 290, 292, 70, 70, 70, 70, 70, 293, 295, 75,
- 75, 75, 75, 75, 103, 103, 103, 300, 103, 304,
- 103, 104, 104, 104, 307, 104, 308, 104, 311, 312,
- 103, 103, 103, 103, 103, 313, 316, 104, 104, 104,
- 104, 104, 111, 111, nil, nil, 111, nil, 111, 117,
- 117, nil, nil, 117, nil, 117, nil, nil, 111, 111,
- 111, 111, 111, nil, nil, 117, 117, 117, 117, 117,
- 118, 118, 118, nil, 118, nil, 118, 126, 126, 126,
- nil, 126, nil, 126, nil, nil, 118, 118, 118, 118,
- 118, nil, nil, 126, 126, 126, 126, 126, 130, 130,
- 130, nil, 130, nil, 130, 143, 143, 143, nil, 143,
- nil, 143, nil, nil, 130, 130, 130, 130, 130, nil,
- nil, 143, 143, 143, 143, 143, 145, 145, 145, nil,
- 145, nil, 145, 146, 146, nil, nil, 146, nil, 146,
- nil, nil, 145, 145, 145, 145, 145, nil, nil, 146,
- 146, 146, 146, 146, 148, 148, 148, nil, 148, nil,
- 148, 149, 149, 149, nil, 149, nil, 149, nil, nil,
- 148, 148, 148, 148, 148, nil, nil, 149, 149, 149,
- 149, 149, 150, 150, 150, nil, 150, nil, 150, 205,
- 205, 205, nil, 205, nil, 205, nil, nil, 150, 150,
- 150, 150, 150, nil, nil, 205, 205, 205, 205, 205,
- 215, 215, nil, nil, 215, nil, 215, 216, 216, 216,
- nil, 216, nil, 216, nil, nil, 215, 215, 215, 215,
- 215, nil, nil, 216, 216, 216, 216, 216, 217, 217,
- 217, nil, 217, nil, 217, 240, 240, 240, nil, 240,
- nil, 240, nil, nil, 217, 217, 217, 217, 217, nil,
- nil, 240, 240, 240, 240, 240, 244, 244, 244, nil,
- 244, nil, 244, 247, 247, 247, nil, 247, nil, 247,
- nil, nil, 244, 244, 244, 244, 244, nil, nil, 247,
- 247, 247, 247, 247, 249, 249, 249, nil, 249, nil,
- 249, 250, 250, 250, nil, 250, nil, 250, nil, nil,
- 249, 249, 249, 249, 249, nil, nil, 250, 250, 250,
- 250, 250, 251, 251, 251, nil, 251, nil, 251, 262,
- 262, 262, nil, 262, nil, 262, nil, nil, 251, 251,
- 251, 251, 251, nil, nil, 262, 262, 262, 262, 262,
- 263, 263, 263, nil, 263, nil, 263, 310, 310, 310,
- nil, 310, nil, 310, nil, nil, 263, 263, 263, 263,
- 263, 57, nil, 310, 310, 310, 310, 310, 168, 168,
- 168, 57, 168, 57, 168, 57, 212, 57, nil, 254,
- nil, nil, nil, nil, 168, nil, 212, 261, 212, 254,
- 212, 254, 212, 254, 95, 254, 95, 261, 95, 261,
- 95, 261, nil, 261, 270, 270, 270, nil, 270, nil,
- 270, 275, 275, 275, nil, 275, nil, 275, nil, nil,
- 270, nil, 296, 296, 296, nil, 296, 275, 296, 297,
- 297, 297, nil, 297, nil, 297, nil, nil, 296, nil,
- 101, 101, 101, nil, 101, 297, 101, 110, 110, nil,
- 125, 110, 125, 110, 125, 159, 125, 159, 190, 159,
- 190, 159, 190, 191, 190, 191, 233, 191, 233, 191,
- 233, 234, 233, 234, nil, 234, nil, 234, 239, 239,
- 239, 255, 239, 255, 239, 255, nil, 255 ]
-
-racc_action_pointer = [
- 63, 4, nil, nil, nil, 0, nil, nil, nil, 2,
- 26, nil, 245, 266, -11, nil, 21, 18, 49, nil,
- nil, nil, 54, 78, 82, nil, 161, nil, nil, nil,
- nil, nil, nil, 112, 138, nil, 273, 42, 60, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 110, 89, nil, 70, nil, -15, 745, 217, 102,
- 103, nil, 294, -5, 301, nil, 107, 86, 322, 329,
- 350, 129, 100, -5, 126, 357, nil, 117, 115, 137,
- nil, nil, nil, nil, nil, nil, nil, 168, 142, 33,
- 146, 70, 118, 148, 222, 768, nil, nil, 121, 176,
- nil, 814, nil, 378, 385, nil, nil, 180, 193, 194,
- 821, 406, 141, 149, nil, 209, 216, 413, 434, nil,
- nil, 169, nil, 199, 192, 824, 441, 2, 235, 240,
- 462, nil, 238, 254, 40, nil, nil, nil, nil, nil,
- nil, nil, nil, 469, 260, 490, 497, 133, 518, 525,
- 546, 26, nil, 191, 214, nil, nil, 198, 256, 829,
- 262, 184, 263, 246, nil, nil, 199, 238, 742, 270,
- nil, nil, nil, nil, nil, nil, nil, nil, 273, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 280,
- 832, 837, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 282, nil, nil, 553, nil, nil, nil, nil,
- nil, nil, 760, 282, 144, 574, 581, 602, 291, nil,
- 289, nil, 290, nil, 33, 285, nil, nil, nil, nil,
- 303, nil, 307, 840, 845, nil, 308, nil, 307, 852,
- 609, nil, nil, nil, 630, 309, nil, 637, nil, 658,
- 665, 686, nil, 307, 763, 855, 301, 302, nil, nil,
- 335, 771, 693, 714, 329, 237, nil, 317, 318, nil,
- 778, 335, nil, 337, 338, 785, nil, nil, nil, nil,
- 341, 354, nil, 357, 359, 364, nil, nil, 372, nil,
- 374, nil, 369, 375, nil, 370, 796, 803, nil, nil,
- 382, nil, nil, nil, 369, nil, nil, 374, 388, nil,
- 721, 390, 397, 403, nil, nil, 398, nil, nil, nil ]
-
-racc_action_default = [
- -176, -176, -1, -2, -3, -4, -5, -6, -7, -8,
- -9, -10, -176, -176, -34, -35, -36, -37, -38, -39,
- -40, -41, -176, -49, -51, -52, -176, -64, -67, -68,
- -69, -70, -71, -176, -176, -107, -176, -109, -110, -111,
- -128, -129, -130, -131, -132, -133, -134, -135, -136, -137,
- -138, -176, -176, -76, -176, -112, -176, -176, -176, -8,
- -9, -11, -176, -176, -176, -72, -176, -176, -55, -176,
- -170, -176, -8, -9, -10, -176, -38, -176, -81, -86,
- -87, -88, -90, -91, -92, -93, -94, -176, -176, -176,
- -176, -176, -176, -176, -176, -176, 320, -12, -13, -176,
- -16, -176, -31, -176, -176, -152, -27, -29, -176, -126,
- -176, -176, -176, -176, -28, -30, -176, -176, -176, -153,
- -160, -176, -162, -176, -176, -176, -176, -176, -176, -176,
- -73, -174, -176, -176, -8, -47, -48, -49, -50, -51,
- -53, -54, -58, -56, -176, -171, -176, -176, -98, -97,
- -96, -176, -79, -176, -176, -95, -80, -176, -176, -176,
- -126, -176, -126, -176, -14, -18, -176, -176, -176, -176,
- -147, -148, -149, -150, -145, -151, -146, -114, -44, -59,
- -127, -60, -61, -62, -63, -139, -140, -141, -142, -176,
- -176, -176, -120, -45, -154, -155, -156, -157, -158, -159,
- -161, -163, -176, -29, -30, -176, -26, -42, -77, -43,
- -78, -175, -176, -176, -176, -176, -172, -74, -176, -101,
- -176, -100, -176, -99, -176, -83, -84, -85, -89, -108,
- -176, -113, -176, -176, -176, -117, -176, -19, -176, -176,
- -176, -143, -20, -21, -176, -176, -32, -176, -164, -176,
- -176, -176, -169, -176, -176, -115, -176, -176, -121, -102,
- -176, -176, -75, -173, -44, -176, -116, -176, -176, -118,
- -176, -176, -144, -176, -176, -176, -168, -165, -166, -167,
- -176, -176, -106, -126, -176, -176, -105, -103, -176, -57,
- -176, -82, -176, -176, -23, -176, -176, -176, -15, -33,
- -176, -46, -119, -122, -176, -104, -124, -176, -176, -25,
- -176, -176, -176, -176, -24, -22, -176, -123, -125, -17 ]
-
-racc_goto_table = [
- 70, 179, 130, 13, 228, 11, 248, 115, 123, 226,
- 227, 113, 245, 5, 14, 9, 63, 11, 68, 10,
- 18, 132, 22, 23, 71, 1, 24, 59, 237, 243,
- 2, 60, 309, 309, 241, 241, 75, 75, 131, 77,
- 88, 3, 4, 70, 162, 163, 6, 160, 161, 61,
- 97, 89, 231, 98, 235, 91, 164, 99, 298, 100,
- 242, 143, 102, 299, 15, 126, 127, 144, 16, 17,
- 75, 142, 11, 145, 135, 204, 109, 174, 151, 203,
- 136, 138, 134, 27, 217, 185, 10, 18, 28, 140,
- 137, 174, 11, 139, 29, 30, 31, 32, 225, 90,
- 155, 105, 59, nil, nil, nil, 60, 176, 248, 230,
- nil, nil, nil, 248, 294, 228, nil, nil, nil, nil,
- 131, 291, nil, nil, nil, nil, nil, nil, nil, 205,
- 206, nil, nil, 211, 248, 248, nil, nil, nil, nil,
- 256, 257, nil, nil, nil, nil, 142, nil, 216, nil,
- nil, nil, nil, 262, 224, 223, 75, 75, nil, nil,
- nil, nil, 259, 219, 220, 220, nil, nil, 221, 222,
- nil, nil, nil, nil, nil, 302, nil, nil, nil, nil,
- nil, nil, nil, 267, 268, nil, nil, nil, nil, 131,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 282, 283, nil, nil, 206, nil,
- nil, 287, nil, nil, 185, nil, nil, nil, 185, 263,
- 211, 174, nil, nil, nil, nil, nil, 206, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 277, 278, 279, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 211, 289, nil, 75, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 174 ]
-
-racc_goto_check = [
- 43, 45, 52, 23, 58, 11, 66, 26, 26, 57,
- 57, 24, 16, 5, 30, 9, 23, 11, 42, 10,
- 34, 24, 38, 40, 53, 1, 41, 9, 19, 19,
- 2, 10, 22, 22, 25, 25, 23, 23, 43, 54,
- 54, 3, 4, 43, 26, 26, 6, 24, 24, 7,
- 12, 9, 45, 13, 45, 10, 14, 15, 17, 18,
- 20, 42, 28, 29, 31, 23, 23, 53, 32, 33,
- 23, 23, 11, 23, 39, 26, 44, 43, 23, 24,
- 5, 30, 9, 46, 52, 43, 10, 34, 47, 38,
- 40, 43, 11, 41, 48, 49, 50, 51, 56, 61,
- 5, 65, 9, nil, nil, nil, 10, 23, 66, 26,
- nil, nil, nil, 66, 16, 58, nil, nil, nil, nil,
- 43, 57, nil, nil, nil, nil, nil, nil, nil, 23,
- 23, nil, nil, 23, 66, 66, nil, nil, nil, nil,
- 26, 26, nil, nil, nil, nil, 23, nil, 23, nil,
- nil, nil, nil, 52, 23, 11, 23, 23, nil, nil,
- nil, nil, 26, 9, 9, 9, nil, nil, 10, 10,
- nil, nil, nil, nil, nil, 45, nil, nil, nil, nil,
- nil, nil, nil, 26, 26, nil, nil, nil, nil, 43,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 26, 26, nil, nil, 23, nil,
- nil, 26, nil, nil, 43, nil, nil, nil, 43, 23,
- 23, 43, nil, nil, nil, nil, nil, 23, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 23, 23, 23, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 23, 23, nil, 23, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 43 ]
-
-racc_goto_pointer = [
- nil, 25, 30, 41, 42, 13, 46, 37, nil, 15,
- 19, 5, -2, 1, -42, 5, -156, -217, 7, -138,
- -107, nil, -264, 3, -45, -132, -50, nil, 10, -212,
- 14, 64, 68, 69, 20, nil, nil, nil, 22, 7,
- 23, 26, -8, -26, 22, -108, 83, 88, 94, 95,
- 96, 97, -62, -2, 6, nil, -55, -144, -150, nil,
- nil, 63, nil, nil, nil, 49, -162, nil ]
-
-racc_goto_default = [
- nil, nil, nil, nil, nil, 84, nil, 7, 8, 72,
- 73, 74, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 238, 252, 62, 107, 106, nil, 114, nil, 246,
- 86, nil, nil, nil, 76, 19, 20, 21, nil, nil,
- 85, nil, nil, 41, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 78, 79, 80, 81, 82,
- 83, 35, 37, 38, 39, 119, 120, 122 ]
-
-racc_reduce_table = [
- 0, 0, :racc_error,
- 1, 38, :_reduce_none,
- 1, 38, :_reduce_none,
- 1, 38, :_reduce_none,
- 1, 39, :_reduce_none,
- 1, 39, :_reduce_none,
- 1, 42, :_reduce_none,
- 1, 42, :_reduce_none,
- 1, 44, :_reduce_8,
- 1, 44, :_reduce_9,
- 1, 44, :_reduce_10,
- 2, 45, :_reduce_11,
- 3, 43, :_reduce_12,
- 1, 49, :_reduce_none,
- 2, 49, :_reduce_14,
- 5, 50, :_reduce_15,
- 1, 50, :_reduce_none,
- 8, 55, :_reduce_17,
- 1, 51, :_reduce_18,
- 2, 51, :_reduce_19,
- 2, 51, :_reduce_20,
- 1, 57, :_reduce_none,
- 5, 57, :_reduce_22,
- 3, 56, :_reduce_23,
- 5, 56, :_reduce_24,
- 4, 56, :_reduce_25,
- 4, 46, :_reduce_26,
- 1, 61, :_reduce_none,
- 1, 63, :_reduce_none,
- 3, 47, :_reduce_29,
- 3, 48, :_reduce_30,
- 1, 52, :_reduce_none,
- 1, 53, :_reduce_none,
- 1, 54, :_reduce_none,
- 1, 40, :_reduce_none,
- 1, 40, :_reduce_none,
- 1, 40, :_reduce_none,
- 1, 40, :_reduce_none,
- 1, 67, :_reduce_38,
- 1, 67, :_reduce_none,
- 1, 67, :_reduce_none,
- 1, 67, :_reduce_none,
- 4, 71, :_reduce_42,
- 4, 71, :_reduce_43,
- 4, 72, :_reduce_44,
- 4, 73, :_reduce_45,
- 7, 74, :_reduce_46,
- 3, 68, :_reduce_47,
- 1, 75, :_reduce_none,
- 1, 75, :_reduce_none,
- 1, 75, :_reduce_none,
- 1, 75, :_reduce_none,
- 1, 75, :_reduce_none,
- 1, 76, :_reduce_none,
- 1, 76, :_reduce_none,
- 2, 69, :_reduce_55,
- 3, 69, :_reduce_56,
- 5, 79, :_reduce_57,
- 2, 79, :_reduce_58,
- 4, 70, :_reduce_59,
- 2, 81, :_reduce_60,
- 2, 81, :_reduce_61,
- 2, 81, :_reduce_62,
- 2, 81, :_reduce_63,
- 1, 41, :_reduce_none,
- 1, 41, :_reduce_none,
- 1, 41, :_reduce_none,
- 1, 41, :_reduce_none,
- 1, 41, :_reduce_none,
- 1, 41, :_reduce_none,
- 1, 41, :_reduce_none,
- 1, 41, :_reduce_none,
- 2, 83, :_reduce_72,
- 3, 88, :_reduce_73,
- 4, 88, :_reduce_74,
- 5, 88, :_reduce_75,
- 2, 87, :_reduce_76,
- 4, 86, :_reduce_77,
- 4, 86, :_reduce_78,
- 3, 84, :_reduce_79,
- 3, 85, :_reduce_80,
- 1, 91, :_reduce_81,
- 5, 91, :_reduce_82,
- 3, 91, :_reduce_83,
- 3, 91, :_reduce_84,
- 3, 91, :_reduce_85,
- 1, 91, :_reduce_86,
- 1, 91, :_reduce_87,
- 1, 93, :_reduce_88,
- 3, 93, :_reduce_89,
- 1, 95, :_reduce_none,
- 1, 95, :_reduce_none,
- 1, 96, :_reduce_none,
- 1, 96, :_reduce_none,
- 1, 96, :_reduce_none,
- 2, 92, :_reduce_95,
- 2, 94, :_reduce_96,
- 2, 94, :_reduce_97,
- 2, 94, :_reduce_98,
- 3, 97, :_reduce_99,
- 3, 97, :_reduce_100,
- 3, 97, :_reduce_101,
- 5, 78, :_reduce_102,
- 6, 78, :_reduce_103,
- 7, 78, :_reduce_104,
- 6, 78, :_reduce_105,
- 6, 78, :_reduce_106,
- 1, 77, :_reduce_none,
- 4, 77, :_reduce_108,
- 1, 98, :_reduce_109,
- 1, 98, :_reduce_110,
- 1, 98, :_reduce_111,
- 2, 99, :_reduce_112,
- 4, 100, :_reduce_113,
- 4, 100, :_reduce_114,
- 5, 100, :_reduce_115,
- 5, 100, :_reduce_116,
- 4, 101, :_reduce_117,
- 5, 101, :_reduce_118,
- 7, 101, :_reduce_119,
- 4, 101, :_reduce_120,
- 5, 101, :_reduce_121,
- 7, 101, :_reduce_122,
- 9, 101, :_reduce_123,
- 7, 101, :_reduce_124,
- 9, 101, :_reduce_125,
- 0, 82, :_reduce_126,
- 1, 82, :_reduce_none,
- 1, 60, :_reduce_128,
- 1, 60, :_reduce_none,
- 1, 80, :_reduce_none,
- 1, 80, :_reduce_none,
- 1, 80, :_reduce_none,
- 1, 80, :_reduce_none,
- 1, 80, :_reduce_none,
- 1, 80, :_reduce_none,
- 1, 80, :_reduce_none,
- 1, 80, :_reduce_none,
- 1, 80, :_reduce_none,
- 2, 62, :_reduce_139,
- 2, 62, :_reduce_140,
- 2, 62, :_reduce_141,
- 2, 62, :_reduce_142,
- 1, 58, :_reduce_none,
- 2, 58, :_reduce_144,
- 2, 102, :_reduce_145,
- 2, 102, :_reduce_146,
- 2, 102, :_reduce_147,
- 2, 102, :_reduce_148,
- 2, 102, :_reduce_149,
- 2, 102, :_reduce_150,
- 2, 65, :_reduce_none,
- 1, 65, :_reduce_none,
- 1, 103, :_reduce_none,
- 2, 103, :_reduce_154,
- 2, 103, :_reduce_155,
- 2, 103, :_reduce_156,
- 2, 103, :_reduce_157,
- 2, 103, :_reduce_158,
- 2, 103, :_reduce_159,
- 1, 104, :_reduce_none,
- 2, 104, :_reduce_161,
- 1, 64, :_reduce_none,
- 2, 64, :_reduce_163,
- 1, 59, :_reduce_none,
- 2, 59, :_reduce_165,
- 2, 59, :_reduce_166,
- 2, 59, :_reduce_167,
- 2, 66, :_reduce_none,
- 1, 66, :_reduce_none,
- 1, 90, :_reduce_170,
- 2, 90, :_reduce_171,
- 3, 90, :_reduce_172,
- 4, 90, :_reduce_173,
- 1, 89, :_reduce_174,
- 2, 89, :_reduce_175 ]
-
-racc_reduce_n = 176
-
-racc_shift_n = 320
-
-racc_token_table = {
- false => 0,
- :error => 1,
- :T => 2,
- :Z => 3,
- :E => 4,
- :X => 5,
- :U => 6,
- :UNKNOWN => 7,
- :OPEN => 8,
- :LONGYEAR => 9,
- :UNMATCHED => 10,
- :DOTS => 11,
- :UA => 12,
- :PUA => 13,
- "-" => 14,
- ":" => 15,
- "2" => 16,
- "4" => 17,
- "0" => 18,
- "+" => 19,
- "1" => 20,
- "/" => 21,
- "3" => 22,
- "^" => 23,
- "[" => 24,
- "]" => 25,
- "{" => 26,
- "}" => 27,
- "," => 28,
- "(" => 29,
- ")" => 30,
- "-(" => 31,
- "5" => 32,
- "6" => 33,
- "7" => 34,
- "8" => 35,
- "9" => 36 }
-
-racc_nt_base = 37
-
-racc_use_result_var = true
-
-Racc_arg = [
- racc_action_table,
- racc_action_check,
- racc_action_default,
- racc_action_pointer,
- racc_goto_table,
- racc_goto_check,
- racc_goto_default,
- racc_goto_pointer,
- racc_nt_base,
- racc_reduce_table,
- racc_token_table,
- racc_shift_n,
- racc_reduce_n,
- racc_use_result_var ]
-Ractor.make_shareable(Racc_arg) if defined?(Ractor)
-
-Racc_token_to_s_table = [
- "$end",
- "error",
- "T",
- "Z",
- "E",
- "X",
- "U",
- "UNKNOWN",
- "OPEN",
- "LONGYEAR",
- "UNMATCHED",
- "DOTS",
- "UA",
- "PUA",
- "\"-\"",
- "\":\"",
- "\"2\"",
- "\"4\"",
- "\"0\"",
- "\"+\"",
- "\"1\"",
- "\"/\"",
- "\"3\"",
- "\"^\"",
- "\"[\"",
- "\"]\"",
- "\"{\"",
- "\"}\"",
- "\",\"",
- "\"(\"",
- "\")\"",
- "\"-(\"",
- "\"5\"",
- "\"6\"",
- "\"7\"",
- "\"8\"",
- "\"9\"",
- "$start",
- "edtf",
- "level_0_expression",
- "level_1_expression",
- "level_2_expression",
- "date",
- "date_time",
- "positive_date",
- "negative_date",
- "year",
- "year_month",
- "year_month_day",
- "time",
- "base_time",
- "zone_offset",
- "hour",
- "minute",
- "second",
- "midnight",
- "zone_offset_hour",
- "positive_zone_offset",
- "d01_13",
- "d01_59",
- "digit",
- "month",
- "d01_12",
- "day",
- "d01_31",
- "d00_23",
- "d00_59",
- "unspecified",
- "level_1_interval",
- "long_year_simple",
- "season",
- "unspecified_year",
- "unspecified_month",
- "unspecified_day",
- "unspecified_day_and_month",
- "level_1_start",
- "level_1_end",
- "partial_uncertain_or_approximate",
- "partial_unspecified",
- "long_year",
- "positive_digit",
- "season_number",
- "ua",
- "season_qualified",
- "choice_list",
- "inclusive_list",
- "masked_precision",
- "date_and_calendar",
- "long_year_scientific",
- "integer",
- "int1_4",
- "list",
- "earlier",
- "list_elements",
- "later",
- "list_element",
- "atomic",
- "consecutives",
- "pua_base",
- "pua_year",
- "pua_year_month",
- "pua_year_month_day",
- "d01_23",
- "d01_29",
- "d01_30" ]
-Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)
-
-Racc_debug_parser = false
-
-##### State transition tables end #####
-
-# reduce 0 omitted
-
-# reduce 1 omitted
-
-# reduce 2 omitted
-
-# reduce 3 omitted
-
-# reduce 4 omitted
-
-# reduce 5 omitted
-
-# reduce 6 omitted
-
-# reduce 7 omitted
-
-module_eval(<<'.,.,', 'edtf.y', 54)
- def _reduce_8(val, _values, result)
- result = Date.new(val[0]).year_precision!
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 55)
- def _reduce_9(val, _values, result)
- result = Date.new(*val.flatten).month_precision!
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 56)
- def _reduce_10(val, _values, result)
- result = Date.new(*val.flatten).day_precision!
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 59)
- def _reduce_11(val, _values, result)
- result = -val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 63)
- def _reduce_12(val, _values, result)
- result = DateTime.new(val[0].year, val[0].month, val[0].day, *val[2])
- result.skip_timezone = (val[2].length == 3)
-
- result
- end
-.,.,
-
-# reduce 13 omitted
-
-module_eval(<<'.,.,', 'edtf.y', 68)
- def _reduce_14(val, _values, result)
- result = val.flatten
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 70)
- def _reduce_15(val, _values, result)
- result = val.values_at(0, 2, 4)
- result
- end
-.,.,
-
-# reduce 16 omitted
-
-module_eval(<<'.,.,', 'edtf.y', 73)
- def _reduce_17(val, _values, result)
- result = [24, 0, 0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 75)
- def _reduce_18(val, _values, result)
- result = 0
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 76)
- def _reduce_19(val, _values, result)
- result = -1 * val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 77)
- def _reduce_20(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-# reduce 21 omitted
-
-module_eval(<<'.,.,', 'edtf.y', 81)
- def _reduce_22(val, _values, result)
- result = 0
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 85)
- def _reduce_23(val, _values, result)
- result = Rational(val[0] * 60 + val[2], 1440)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 86)
- def _reduce_24(val, _values, result)
- result = Rational(840, 1440)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 87)
- def _reduce_25(val, _values, result)
- result = Rational(val[3], 1440)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 91)
- def _reduce_26(val, _values, result)
- result = val.zip([1000,100,10,1]).reduce(0) { |s,(a,b)| s += a * b }
-
- result
- end
-.,.,
-
-# reduce 27 omitted
-
-# reduce 28 omitted
-
-module_eval(<<'.,.,', 'edtf.y', 97)
- def _reduce_29(val, _values, result)
- result = [val[0], val[2]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 104)
- def _reduce_30(val, _values, result)
- result = val[0] << val[2]
- if result[2] > 31 || (result[2] > 30 && [2,4,6,9,11].include?(result[1])) || (result[2] > 29 && result[1] == 2)
- raise ArgumentError, "invalid date (invalid days #{result[2]} for month #{result[1]})"
- end
-
- result
- end
-.,.,
-
-# reduce 31 omitted
-
-# reduce 32 omitted
-
-# reduce 33 omitted
-
-# reduce 34 omitted
-
-# reduce 35 omitted
-
-# reduce 36 omitted
-
-# reduce 37 omitted
-
-module_eval(<<'.,.,', 'edtf.y', 127)
- def _reduce_38(val, _values, result)
- result = Date.new(val[0][0]).year_precision!
- result.unspecified.year[2,2] = val[0][1]
-
- result
- end
-.,.,
-
-# reduce 39 omitted
-
-# reduce 40 omitted
-
-# reduce 41 omitted
-
-module_eval(<<'.,.,', 'edtf.y', 138)
- def _reduce_42(val, _values, result)
- result = [val[0,3].zip([1000,100,10]).reduce(0) { |s,(a,b)| s += a * b }, [false,true]]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 142)
- def _reduce_43(val, _values, result)
- result = [val[0,2].zip([1000,100]).reduce(0) { |s,(a,b)| s += a * b }, [true, true]]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 146)
- def _reduce_44(val, _values, result)
- result = Date.new(val[0]).unspecified!(:month)
- result.precision = :month
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 151)
- def _reduce_45(val, _values, result)
- result = Date.new(*val[0]).unspecified!(:day)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 155)
- def _reduce_46(val, _values, result)
- result = Date.new(val[0]).unspecified!([:day,:month])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 160)
- def _reduce_47(val, _values, result)
- result = Interval.new(val[0], val[2])
-
- result
- end
-.,.,
-
-# reduce 48 omitted
-
-# reduce 49 omitted
-
-# reduce 50 omitted
-
-# reduce 51 omitted
-
-# reduce 52 omitted
-
-# reduce 53 omitted
-
-# reduce 54 omitted
-
-module_eval(<<'.,.,', 'edtf.y', 171)
- def _reduce_55(val, _values, result)
- result = Date.new(val[1])
- result.precision = :year
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 176)
- def _reduce_56(val, _values, result)
- result = Date.new(-1 * val[2])
- result.precision = :year
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 183)
- def _reduce_57(val, _values, result)
- result = val.zip([10000,1000,100,10,1]).reduce(0) { |s,(a,b)| s += a * b }
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 185)
- def _reduce_58(val, _values, result)
- result = 10 * val[0] + val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 190)
- def _reduce_59(val, _values, result)
- result = Season.new(val[0], val[2])
- val[3].each { |ua| result.send(ua) }
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 194)
- def _reduce_60(val, _values, result)
- result = 21
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 195)
- def _reduce_61(val, _values, result)
- result = 22
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 196)
- def _reduce_62(val, _values, result)
- result = 23
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 197)
- def _reduce_63(val, _values, result)
- result = 24
- result
- end
-.,.,
-
-# reduce 64 omitted
-
-# reduce 65 omitted
-
-# reduce 66 omitted
-
-# reduce 67 omitted
-
-# reduce 68 omitted
-
-# reduce 69 omitted
-
-# reduce 70 omitted
-
-# reduce 71 omitted
-
-module_eval(<<'.,.,', 'edtf.y', 215)
- def _reduce_72(val, _values, result)
- result = val[0]; result.qualifier = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 221)
- def _reduce_73(val, _values, result)
- result = Date.new(val[0].year * 10 ** val[2]).year_precision!
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 225)
- def _reduce_74(val, _values, result)
- result = Date.new(val[1] * 10 ** val[3]).year_precision!
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 229)
- def _reduce_75(val, _values, result)
- result = Date.new(-1 * val[2] * 10 ** val[4]).year_precision!
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 234)
- def _reduce_76(val, _values, result)
- result = val[0]; result.calendar = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 240)
- def _reduce_77(val, _values, result)
- d = val[0,3].zip([1000,100,10]).reduce(0) { |s,(a,b)| s += a * b }
- result = EDTF::Decade.new(d)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 245)
- def _reduce_78(val, _values, result)
- d = val[0,2].zip([1000,100]).reduce(0) { |s,(a,b)| s += a * b }
- result = EDTF::Century.new(d)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 251)
- def _reduce_79(val, _values, result)
- result = val[1].choice!
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 253)
- def _reduce_80(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 255)
- def _reduce_81(val, _values, result)
- result = EDTF::Set.new(val[0]).earlier!
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 256)
- def _reduce_82(val, _values, result)
- result = EDTF::Set.new([val[0]] + val[2] + [val[4]]).earlier!.later!
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 257)
- def _reduce_83(val, _values, result)
- result = EDTF::Set.new([val[0]] + val[2]).earlier!
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 258)
- def _reduce_84(val, _values, result)
- result = EDTF::Set.new([val[0]] + [val[2]]).earlier!.later!
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 259)
- def _reduce_85(val, _values, result)
- result = EDTF::Set.new(val[0] + [val[2]]).later!
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 260)
- def _reduce_86(val, _values, result)
- result = EDTF::Set.new(*val[0])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 261)
- def _reduce_87(val, _values, result)
- result = EDTF::Set.new(val[0]).later!
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 264)
- def _reduce_88(val, _values, result)
- result = [val[0]].flatten
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 265)
- def _reduce_89(val, _values, result)
- result = val[0] + [val[2]].flatten
- result
- end
-.,.,
-
-# reduce 90 omitted
-
-# reduce 91 omitted
-
-# reduce 92 omitted
-
-# reduce 93 omitted
-
-# reduce 94 omitted
-
-module_eval(<<'.,.,', 'edtf.y', 277)
- def _reduce_95(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 279)
- def _reduce_96(val, _values, result)
- result = Date.new(*val[0]).year_precision!
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 280)
- def _reduce_97(val, _values, result)
- result = Date.new(*val[0]).month_precision!
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 281)
- def _reduce_98(val, _values, result)
- result = Date.new(val[0]).year_precision!
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 284)
- def _reduce_99(val, _values, result)
- result = (Date.new(val[0]).day_precision! .. Date.new(val[2]).day_precision!)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 285)
- def _reduce_100(val, _values, result)
- result = (Date.new(val[0]).month_precision! .. Date.new(val[2]).month_precision!)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 286)
- def _reduce_101(val, _values, result)
- result = (Date.new(val[0]).year_precision! .. Date.new(val[2]).year_precision!)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 292)
- def _reduce_102(val, _values, result)
- result = Date.new(val[0][0], val[2], val[4])
- result.unspecified.year[2,2] = val[0][1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 297)
- def _reduce_103(val, _values, result)
- result = Date.new(val[0][0], 1, val[5])
- result.unspecified.year[2,2] = val[0][1]
- result.unspecified!(:month)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 303)
- def _reduce_104(val, _values, result)
- result = Date.new(val[0][0], 1, 1)
- result.unspecified.year[2,2] = val[0][1]
- result.unspecified!([:month, :day])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 309)
- def _reduce_105(val, _values, result)
- result = Date.new(val[0][0], val[2], 1)
- result.unspecified.year[2,2] = val[0][1]
- result.unspecified!(:day)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 315)
- def _reduce_106(val, _values, result)
- result = Date.new(val[0], 1, val[5])
- result.unspecified!(:month)
-
- result
- end
-.,.,
-
-# reduce 107 omitted
-
-module_eval(<<'.,.,', 'edtf.y', 322)
- def _reduce_108(val, _values, result)
- result = uoa(val[1], val[3])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 325)
- def _reduce_109(val, _values, result)
- result = val[0].year_precision!
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 326)
- def _reduce_110(val, _values, result)
- result = val[0][0].month_precision!
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 327)
- def _reduce_111(val, _values, result)
- result = val[0].day_precision!
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 329)
- def _reduce_112(val, _values, result)
- result = uoa(Date.new(val[0]), val[1], :year)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 333)
- def _reduce_113(val, _values, result)
- result = [uoa(val[0].change(:month => val[2]), val[3], [:month, :year])]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 336)
- def _reduce_114(val, _values, result)
- result = [uoa(Date.new(val[0], val[2]), val[3], [:year, :month])]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 339)
- def _reduce_115(val, _values, result)
- result = [uoa(Date.new(val[0], val[2]), val[4], [:month]), true]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 342)
- def _reduce_116(val, _values, result)
- result = [uoa(val[0].change(:month => val[2]), val[4], [:month]), true]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 348)
- def _reduce_117(val, _values, result)
- result = uoa(val[0][0].change(:day => val[2]), val[3], val[0][1] ? [:day] : nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 351)
- def _reduce_118(val, _values, result)
- result = uoa(val[0][0].change(:day => val[2]), val[4], [:day])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 354)
- def _reduce_119(val, _values, result)
- result = uoa(uoa(Date.new(val[0], val[2], val[5]), val[4], :month), val[6], :day)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 357)
- def _reduce_120(val, _values, result)
- result = uoa(Date.new(val[0][0], val[0][1], val[2]), val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 360)
- def _reduce_121(val, _values, result)
- result = uoa(Date.new(val[0][0], val[0][1], val[2]), val[4], [:day])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 363)
- def _reduce_122(val, _values, result)
- result = uoa(Date.new(val[0], val[2], val[4]), val[6], [:month, :day])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 366)
- def _reduce_123(val, _values, result)
- result = Date.new(val[0], val[2], val[4])
- result = uoa(result, val[6], [:day])
- result = uoa(result, val[8], [:month, :day])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 371)
- def _reduce_124(val, _values, result)
- result = val[0].change(:month => val[2], :day => val[4])
- result = uoa(result, val[6], [:month, :day])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 375)
- def _reduce_125(val, _values, result)
- result = val[0].change(:month => val[2], :day => val[4])
- result = uoa(result, val[6], [:day])
- result = uoa(result, val[8], [:month, :day])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 386)
- def _reduce_126(val, _values, result)
- result = []
- result
- end
-.,.,
-
-# reduce 127 omitted
-
-module_eval(<<'.,.,', 'edtf.y', 390)
- def _reduce_128(val, _values, result)
- result = 0
- result
- end
-.,.,
-
-# reduce 129 omitted
-
-# reduce 130 omitted
-
-# reduce 131 omitted
-
-# reduce 132 omitted
-
-# reduce 133 omitted
-
-# reduce 134 omitted
-
-# reduce 135 omitted
-
-# reduce 136 omitted
-
-# reduce 137 omitted
-
-# reduce 138 omitted
-
-module_eval(<<'.,.,', 'edtf.y', 396)
- def _reduce_139(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 397)
- def _reduce_140(val, _values, result)
- result = 10
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 398)
- def _reduce_141(val, _values, result)
- result = 11
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 399)
- def _reduce_142(val, _values, result)
- result = 12
- result
- end
-.,.,
-
-# reduce 143 omitted
-
-module_eval(<<'.,.,', 'edtf.y', 403)
- def _reduce_144(val, _values, result)
- result = 13
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 406)
- def _reduce_145(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 407)
- def _reduce_146(val, _values, result)
- result = 10 + val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 408)
- def _reduce_147(val, _values, result)
- result = 20
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 409)
- def _reduce_148(val, _values, result)
- result = 21
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 410)
- def _reduce_149(val, _values, result)
- result = 22
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 411)
- def _reduce_150(val, _values, result)
- result = 23
- result
- end
-.,.,
-
-# reduce 151 omitted
-
-# reduce 152 omitted
-
-# reduce 153 omitted
-
-module_eval(<<'.,.,', 'edtf.y', 419)
- def _reduce_154(val, _values, result)
- result = 24
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 420)
- def _reduce_155(val, _values, result)
- result = 25
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 421)
- def _reduce_156(val, _values, result)
- result = 26
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 422)
- def _reduce_157(val, _values, result)
- result = 27
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 423)
- def _reduce_158(val, _values, result)
- result = 28
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 424)
- def _reduce_159(val, _values, result)
- result = 29
- result
- end
-.,.,
-
-# reduce 160 omitted
-
-module_eval(<<'.,.,', 'edtf.y', 428)
- def _reduce_161(val, _values, result)
- result = 30
- result
- end
-.,.,
-
-# reduce 162 omitted
-
-module_eval(<<'.,.,', 'edtf.y', 432)
- def _reduce_163(val, _values, result)
- result = 31
- result
- end
-.,.,
-
-# reduce 164 omitted
-
-module_eval(<<'.,.,', 'edtf.y', 436)
- def _reduce_165(val, _values, result)
- result = 30 + val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 437)
- def _reduce_166(val, _values, result)
- result = 40 + val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 438)
- def _reduce_167(val, _values, result)
- result = 50 + val[1]
- result
- end
-.,.,
-
-# reduce 168 omitted
-
-# reduce 169 omitted
-
-module_eval(<<'.,.,', 'edtf.y', 445)
- def _reduce_170(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 446)
- def _reduce_171(val, _values, result)
- result = 10 * val[0] + val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 449)
- def _reduce_172(val, _values, result)
- result = val.zip([100,10,1]).reduce(0) { |s,(a,b)| s += a * b }
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 453)
- def _reduce_173(val, _values, result)
- result = val.zip([1000,100,10,1]).reduce(0) { |s,(a,b)| s += a * b }
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 457)
- def _reduce_174(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'edtf.y', 458)
- def _reduce_175(val, _values, result)
- result = 10 * val[0] + val[1]
- result
- end
-.,.,
-
-def _reduce_none(val, _values, result)
- val[0]
-end
-
- end # class Parser
-end # module EDTF
diff --git a/test/racc/regress/huia b/test/racc/regress/huia
deleted file mode 100644
index 121cbe1d5b..0000000000
--- a/test/racc/regress/huia
+++ /dev/null
@@ -1,1683 +0,0 @@
-#
-# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.5.2
-# from Racc grammar file "".
-#
-
-require 'racc/parser.rb'
-module Huia
- class Parser < Racc::Parser
-
-module_eval(<<'...end huia.y/module_eval...', 'huia.y', 211)
-
-attr_accessor :lexer, :scopes, :state
-
-def initialize lexer
- @lexer = lexer
- @state = []
- @scopes = []
- push_scope
-end
-
-def ast
- @ast ||= do_parse
- @scopes.first
-end
-
-def on_error t, val, vstack
- line = lexer.line
- col = lexer.column
- message = "Unexpected #{token_to_str t} at #{lexer.filename} line #{line}:#{col}:\n\n"
-
- start = line - 5 > 0 ? line - 5 : 0
- i_size = line.to_s.size
- (start..(start + 5)).each do |i|
- message << sprintf("\t%#{i_size}d: %s\n", i, lexer.get_line(i))
- message << "\t#{' ' * i_size} #{'-' * (col - 1)}^\n" if i == line
- end
-
- raise SyntaxError, message
-end
-
-def next_token
- nt = lexer.next_computed_token
- # just use a state stack for now, we'll have to do something
- # more sophisticated soon.
- if nt && nt.first == :state
- if nt.last
- state.push << nt.last
- else
- state.pop
- end
- next_token
- else
- nt
- end
-end
-
-def push_scope
- new_scope = Huia::AST::Scope.new scope
- new_scope.file = lexer.filename
- new_scope.line = lexer.line
- new_scope.column = lexer.column
- scopes.push new_scope
- new_scope
-end
-
-def pop_scope
- scopes.pop
-end
-
-def scope
- scopes.last
-end
-
-def binary left, right, method
- node(:MethodCall, left, node(:CallSignature, method, [right]))
-end
-
-def unary left, method
- node(:MethodCall, left, node(:CallSignature, method))
-end
-
-def node type, *args
- Huia::AST.const_get(type).new(*args).tap do |n|
- n.file = lexer.filename
- n.line = lexer.line
- n.column = lexer.column
- end
-end
-alias n node
-
-def allocate_local name
- node(:Variable, name).tap do |n|
- scope.allocate_local n
- end
-end
-
-def allocate_local_assignment name, value
- node(:Assignment, name, value).tap do |n|
- scope.allocate_local n
- end
-end
-
-def this_closure
- allocate_local('@')
-end
-
-def scope_instance
- node(:ScopeInstance, scope)
-end
-
-def constant name
- return scope_instance if name == 'self'
- node(:Constant, name)
-end
-
-def to_string expr
- node(:MethodCall, expr, node(:CallSignature, 'toString'))
-end
-...end huia.y/module_eval...
-##### State transition tables begin ###
-
-racc_action_table = [
- 81, 106, 40, 37, 61, 62, 123, 153, 135, 71,
- 72, 77, 155, 178, 179, 39, 164, 37, 37, 5,
- 6, 137, 152, 73, 74, 75, 36, 36, 76, 28,
- 154, 80, 166, 172, 180, 22, 23, 37, 26, 27,
- 182, 60, 63, 19, 164, 40, 36, 61, 62, 186,
- nil, 33, 71, 72, 77, nil, nil, 134, 39, 133,
- 129, 37, 5, 6, nil, nil, 73, 74, 75, nil,
- 36, 76, 28, 134, 80, 169, 129, nil, 22, 23,
- nil, 26, 27, nil, 60, 63, 19, nil, 40, nil,
- 61, 62, nil, nil, 33, 71, 72, 77, nil, nil,
- nil, 39, nil, nil, 37, nil, nil, nil, nil, 73,
- 74, 75, nil, 36, 76, 28, nil, 80, nil, nil,
- nil, 22, 23, nil, 26, 27, nil, 60, 63, 19,
- nil, 40, nil, 61, 62, nil, nil, 33, 71, 72,
- 77, nil, nil, nil, 39, nil, nil, 37, nil, nil,
- nil, nil, 73, 74, 75, nil, 36, 76, 28, nil,
- 80, nil, nil, nil, 22, 23, nil, 26, 27, nil,
- 60, 63, 19, nil, 40, nil, 61, 62, nil, nil,
- 33, 71, 72, 77, nil, nil, nil, 39, nil, nil,
- 37, nil, nil, nil, nil, 73, 74, 75, nil, 36,
- 76, 28, nil, 80, nil, nil, nil, 22, 23, nil,
- 26, 27, nil, 60, 63, 19, nil, 40, nil, 61,
- 62, nil, nil, 33, 71, 72, 77, nil, 114, nil,
- 39, nil, nil, 37, nil, nil, 113, nil, 73, 74,
- 75, nil, 36, 76, 28, nil, 80, nil, nil, nil,
- 22, 23, nil, 26, 27, nil, 60, 63, 19, nil,
- 40, nil, 61, 62, nil, nil, 33, 71, 72, 77,
- nil, nil, nil, 39, nil, nil, 37, nil, nil, nil,
- nil, 73, 74, 75, nil, 36, 76, 28, nil, 80,
- nil, nil, nil, 22, 23, nil, 26, 27, nil, 60,
- 63, 19, nil, 40, nil, 61, 62, nil, nil, 33,
- 71, 72, 77, nil, nil, nil, 39, nil, nil, 37,
- nil, nil, nil, nil, 73, 74, 75, nil, 36, 76,
- 28, nil, 80, nil, nil, nil, 22, 23, nil, 26,
- 27, nil, 60, 63, 19, nil, 40, nil, 61, 62,
- nil, nil, 33, 71, 72, 77, nil, nil, nil, 39,
- nil, nil, 37, nil, nil, nil, nil, 73, 74, 75,
- nil, 36, 76, 28, nil, 80, nil, nil, nil, 22,
- 23, nil, 26, 27, nil, 60, 63, 19, nil, 40,
- nil, 61, 62, nil, nil, 33, 71, 72, 77, nil,
- nil, nil, 39, nil, nil, 37, nil, nil, nil, nil,
- 73, 74, 75, nil, 36, 76, 28, nil, 80, nil,
- nil, nil, 22, 23, nil, 26, 27, nil, 60, 63,
- 19, nil, 40, nil, 61, 62, nil, nil, 33, 71,
- 72, 77, nil, nil, nil, 39, nil, nil, 37, nil,
- nil, nil, nil, 73, 74, 75, nil, 36, 76, 28,
- nil, 80, nil, nil, nil, 22, 23, nil, 26, 27,
- nil, 60, 63, 19, nil, 40, nil, 61, 62, nil,
- nil, 33, 71, 72, 77, nil, nil, nil, 39, nil,
- nil, 37, nil, nil, nil, nil, 73, 74, 75, nil,
- 36, 76, 28, nil, 80, nil, nil, nil, 22, 23,
- nil, 26, 27, nil, 60, 63, 19, nil, 40, nil,
- 61, 62, nil, nil, 33, 71, 72, 77, nil, nil,
- nil, 39, nil, nil, 37, nil, nil, nil, nil, 73,
- 74, 75, nil, 36, 76, 28, nil, 80, nil, nil,
- nil, 22, 23, nil, 26, 27, nil, 60, 63, 19,
- nil, 40, nil, 61, 62, nil, nil, 33, 71, 72,
- 77, nil, nil, nil, 39, nil, nil, 37, nil, nil,
- nil, nil, 73, 74, 75, nil, 36, 76, 28, nil,
- 80, nil, nil, nil, 22, 23, nil, 26, 27, nil,
- 60, 63, 19, nil, 40, nil, 61, 62, nil, nil,
- 33, 71, 72, 77, nil, nil, nil, 39, nil, nil,
- 37, nil, nil, nil, nil, 73, 74, 75, nil, 36,
- 76, 28, nil, 80, nil, nil, nil, 22, 23, nil,
- 26, 27, nil, 60, 63, 19, nil, 40, nil, 61,
- 62, nil, nil, 33, 71, 72, 77, nil, nil, nil,
- 39, nil, nil, 37, nil, nil, nil, nil, 73, 74,
- 75, nil, 36, 76, 28, nil, 80, nil, nil, nil,
- 22, 23, nil, 26, 27, nil, 60, 63, 19, nil,
- 40, nil, 61, 62, nil, nil, 33, 71, 72, 77,
- nil, nil, nil, 39, nil, nil, 37, nil, nil, nil,
- nil, 73, 74, 75, nil, 36, 76, 28, nil, 80,
- nil, nil, nil, 22, 23, nil, 26, 27, nil, 60,
- 63, 19, nil, 40, nil, 61, 62, nil, nil, 33,
- 71, 72, 77, nil, nil, nil, 39, nil, nil, 37,
- nil, nil, nil, nil, 73, 74, 75, nil, 36, 76,
- 28, nil, 80, nil, nil, nil, 22, 23, nil, 26,
- 27, nil, 60, 63, 19, nil, 40, nil, 61, 62,
- nil, nil, 33, 71, 72, 77, nil, nil, nil, 39,
- nil, nil, 37, nil, nil, nil, nil, 73, 74, 75,
- nil, 36, 76, 28, nil, 80, nil, nil, nil, 22,
- 23, nil, 26, 27, nil, 60, 63, 19, nil, 40,
- nil, 61, 62, nil, nil, 33, 71, 72, 77, nil,
- nil, nil, 39, nil, nil, 37, nil, nil, nil, nil,
- 73, 74, 75, nil, 36, 76, 28, nil, 80, nil,
- nil, nil, 22, 23, nil, 26, 27, nil, 60, 63,
- 19, nil, 40, nil, 61, 62, nil, nil, 33, 71,
- 72, 77, nil, nil, nil, 39, nil, nil, 37, nil,
- nil, nil, nil, 73, 74, 75, nil, 36, 76, 28,
- nil, 80, nil, nil, nil, 22, 23, nil, 26, 27,
- nil, 60, 63, 19, nil, 40, nil, 61, 62, nil,
- nil, 33, 71, 72, 77, nil, nil, nil, 39, nil,
- nil, 37, nil, nil, nil, nil, 73, 74, 75, nil,
- 36, 76, 28, nil, 80, nil, nil, nil, 22, 23,
- nil, 26, 27, nil, 60, 63, 19, nil, 40, nil,
- 61, 62, nil, nil, 33, 71, 72, 77, nil, nil,
- nil, 39, nil, nil, 37, nil, nil, nil, nil, 73,
- 74, 75, nil, 36, 76, 28, nil, 80, nil, nil,
- nil, 22, 23, nil, 26, 27, nil, 60, 63, 19,
- nil, 40, nil, 61, 62, nil, nil, 33, 71, 72,
- 77, nil, nil, nil, 39, nil, nil, 37, nil, nil,
- nil, nil, 73, 74, 75, nil, 36, 76, 28, nil,
- 80, nil, nil, nil, 22, 23, nil, 26, 27, nil,
- 60, 63, 19, nil, 40, nil, 61, 62, nil, nil,
- 33, 71, 72, 77, nil, nil, nil, 39, nil, nil,
- 37, nil, nil, nil, nil, 73, 74, 75, nil, 36,
- 76, 28, nil, 80, nil, nil, nil, 22, 23, nil,
- 26, 27, nil, 60, 63, 19, nil, 40, nil, 61,
- 62, nil, nil, 33, 71, 72, 77, nil, nil, 160,
- 39, nil, nil, 37, 5, 6, nil, nil, 73, 74,
- 75, nil, 36, 76, 28, nil, 80, nil, nil, nil,
- 22, 23, nil, 26, 27, nil, 60, 63, 19, nil,
- 40, nil, 61, 62, nil, nil, 33, 71, 72, 77,
- nil, nil, nil, 39, nil, nil, 37, nil, nil, nil,
- nil, 73, 74, 75, nil, 36, 76, 28, nil, 80,
- nil, nil, nil, 22, 23, nil, 26, 27, nil, 60,
- 63, 19, nil, 40, nil, 61, 62, nil, nil, 33,
- 71, 72, 77, nil, nil, nil, 39, nil, nil, 37,
- nil, nil, nil, nil, 73, 74, 75, nil, 36, 76,
- 28, nil, 80, nil, nil, nil, 22, 23, nil, 26,
- 27, nil, 60, 63, 19, nil, 40, nil, 61, 62,
- nil, nil, 33, 71, 72, 77, nil, nil, nil, 39,
- nil, nil, 37, nil, nil, nil, nil, 73, 74, 75,
- nil, 36, 76, 28, nil, 80, nil, nil, nil, 22,
- 23, nil, 26, 27, nil, 60, 63, 19, nil, 40,
- nil, 61, 62, nil, nil, 33, 71, 72, 77, nil,
- nil, nil, 39, nil, nil, 37, nil, nil, nil, nil,
- 73, 74, 75, nil, 36, 76, 28, nil, 80, nil,
- nil, nil, 22, 23, nil, 26, 27, nil, 60, 63,
- 19, nil, 40, nil, 61, 62, nil, nil, 33, 71,
- 72, 77, nil, nil, nil, 39, nil, nil, 37, nil,
- nil, nil, nil, 73, 74, 75, nil, 36, 76, 28,
- nil, 80, nil, nil, nil, 22, 23, nil, 26, 27,
- nil, 60, 63, 19, nil, 40, nil, 61, 62, nil,
- nil, 33, 71, 72, 77, nil, nil, 160, 39, nil,
- nil, 37, 5, 6, nil, nil, 73, 74, 75, nil,
- 36, 76, 28, nil, 80, nil, nil, nil, 22, 23,
- nil, 26, 27, nil, 60, 63, 19, nil, 40, nil,
- 61, 62, nil, nil, 33, 71, 72, 77, nil, nil,
- nil, 39, nil, nil, 37, nil, nil, nil, nil, 73,
- 74, 75, nil, 36, 76, 28, nil, 80, nil, nil,
- nil, 22, 23, nil, 26, 27, nil, 60, 63, 19,
- 85, 86, 87, 88, nil, 84, nil, 33, 89, nil,
- nil, nil, nil, 84, 91, 5, 6, 85, 86, 87,
- 88, nil, 91, nil, nil, 89, nil, nil, nil, nil,
- 84, nil, 92, 93, 94, 95, 96, 97, 98, 91,
- 92, 93, 94, 95, 96, 97, 98, nil, 90, nil,
- nil, nil, nil, nil, nil, nil, nil, 92, 93, 94,
- 95, 96, 97, 98, nil, 90, 85, 86, 87, 88,
- nil, nil, nil, nil, 89, nil, nil, nil, nil, 84,
- nil, 85, 86, 87, 88, 156, nil, nil, 91, 89,
- nil, nil, nil, nil, 84, nil, nil, nil, nil, nil,
- nil, nil, nil, 91, nil, nil, 92, 93, 94, 95,
- 96, 97, 98, nil, 90, nil, nil, nil, nil, nil,
- nil, 92, 93, 94, 95, 96, 97, 98, nil, 90,
- 85, 86, 87, 88, nil, nil, nil, nil, 89, nil,
- nil, nil, nil, 84, nil, 165, 85, 86, 87, 88,
- nil, nil, 91, nil, 89, nil, nil, nil, 167, 84,
- nil, nil, nil, nil, nil, nil, nil, nil, 91, nil,
- 92, 93, 94, 95, 96, 97, 98, nil, 90, nil,
- nil, nil, nil, nil, nil, nil, 92, 93, 94, 95,
- 96, 97, 98, nil, 90, 85, 86, 87, 88, nil,
- nil, nil, nil, 89, nil, nil, nil, nil, 84, nil,
- 85, 86, 87, 88, nil, nil, nil, 91, 89, nil,
- nil, nil, nil, 84, nil, nil, nil, nil, nil, nil,
- nil, nil, 91, nil, nil, 92, 93, 94, 95, 96,
- 97, 98, nil, 90, nil, nil, nil, nil, nil, nil,
- 92, 93, 94, 95, 96, 97, 98, nil, 90, 85,
- 86, 87, 88, nil, nil, nil, nil, 89, nil, nil,
- nil, nil, 84, nil, 85, 86, 87, 88, nil, nil,
- nil, 91, 89, nil, nil, nil, nil, 84, nil, nil,
- nil, nil, nil, nil, nil, nil, 91, nil, nil, 92,
- 93, 94, 95, 96, 97, 98, nil, 90, nil, nil,
- nil, nil, nil, nil, 92, 93, 94, 95, 96, 97,
- 98, nil, 90, 85, 86, 87, 88, nil, nil, nil,
- nil, 89, nil, nil, nil, nil, 84, nil, 85, 86,
- 87, 88, nil, nil, nil, 91, 89, nil, nil, nil,
- nil, 84, nil, nil, nil, nil, nil, nil, nil, nil,
- 91, nil, nil, 92, 93, 94, 95, 96, 97, 98,
- nil, 90, nil, nil, nil, nil, nil, nil, 92, 93,
- 94, 95, 96, 97, 98, nil, 90, 85, 86, 87,
- 88, nil, nil, nil, nil, 89, nil, nil, nil, nil,
- 84, nil, 85, 86, 87, 88, nil, nil, nil, 91,
- 89, nil, nil, nil, nil, 84, nil, nil, nil, nil,
- nil, nil, nil, nil, 91, nil, nil, 92, 93, 94,
- 95, 96, 97, 98, nil, 90, nil, nil, nil, nil,
- nil, nil, 92, 93, 94, 95, 96, 97, 98, nil,
- 90, 85, 86, 87, 88, nil, nil, nil, nil, 89,
- nil, 160, nil, nil, 84, nil, 5, 6, 85, 86,
- 87, 88, nil, 91, nil, nil, 89, nil, nil, nil,
- nil, 84, nil, nil, nil, nil, nil, nil, nil, nil,
- 91, 92, 93, 94, 95, 96, 97, 98, nil, 90,
- nil, nil, nil, nil, nil, nil, nil, nil, 92, 93,
- 94, 95, 96, 97, 98, nil, 90, 85, 86, 87,
- 88, nil, nil, nil, nil, 89, nil, nil, nil, nil,
- 84, nil, 85, 86, 87, 88, nil, nil, nil, 91,
- 89, nil, nil, nil, nil, 84, nil, 181, nil, nil,
- nil, nil, nil, nil, 91, nil, nil, 92, 93, 94,
- 95, 96, 97, 98, nil, 90, nil, nil, nil, nil,
- nil, nil, 92, 93, 94, 95, 96, 97, 98, nil,
- 90, 85, 86, 87, 88, nil, nil, nil, nil, 89,
- nil, nil, nil, nil, 84, nil, 85, 86, 87, 88,
- nil, nil, nil, 91, 89, nil, nil, nil, nil, 84,
- nil, nil, nil, nil, nil, nil, nil, nil, 91, nil,
- nil, 92, 93, 94, 95, 96, 97, 98, nil, 90,
- nil, nil, nil, nil, nil, nil, 92, 93, 94, 95,
- 96, 97, 98, nil, 90, 87, 88, nil, nil, nil,
- nil, 89, nil, nil, nil, nil, 84, 87, 88, nil,
- nil, nil, nil, 89, nil, 91, nil, nil, 84, nil,
- 87, 88, nil, nil, nil, nil, 89, 91, nil, nil,
- nil, 84, nil, 92, 93, 94, 95, 96, 97, 98,
- 91, 90, nil, nil, nil, 92, 93, 94, 95, 96,
- 97, 98, nil, 90, nil, nil, nil, nil, 92, 93,
- 94, 95, 96, 97, 98, 89, 90, 87, 88, nil,
- 84, nil, nil, 89, nil, nil, nil, nil, 84, 91,
- nil, nil, nil, nil, nil, nil, nil, 91, nil, nil,
- 89, nil, nil, nil, nil, 84, nil, 92, 93, 94,
- 95, 96, 97, 98, 91, 92, 93, 94, 95, 96,
- 97, 98, nil, 90, nil, 89, nil, nil, nil, nil,
- 84, nil, 92, 93, 94, 95, 96, 97, 98, 91,
- 89, nil, nil, nil, nil, 84, nil, nil, nil, nil,
- 89, nil, nil, nil, 91, 84, nil, 92, 93, 94,
- 95, 96, 97, 98, 91, nil, nil, nil, nil, nil,
- nil, nil, 92, 93, 94, 95, 96, 97, 98, nil,
- nil, nil, 92, 93, 94, 95, 96, 97, 98 ]
-
-racc_action_check = [
- 1, 33, 1, 34, 1, 1, 40, 100, 81, 1,
- 1, 1, 102, 161, 161, 1, 112, 33, 1, 1,
- 1, 84, 100, 1, 1, 1, 33, 1, 1, 1,
- 102, 1, 121, 131, 163, 1, 1, 84, 1, 1,
- 178, 1, 1, 1, 179, 0, 84, 0, 0, 183,
- nil, 1, 0, 0, 0, nil, nil, 80, 0, 80,
- 80, 0, 0, 0, nil, nil, 0, 0, 0, nil,
- 0, 0, 0, 128, 0, 128, 128, nil, 0, 0,
- nil, 0, 0, nil, 0, 0, 0, nil, 19, nil,
- 19, 19, nil, nil, 0, 19, 19, 19, nil, nil,
- nil, 19, nil, nil, 19, nil, nil, nil, nil, 19,
- 19, 19, nil, 19, 19, 19, nil, 19, nil, nil,
- nil, 19, 19, nil, 19, 19, nil, 19, 19, 19,
- nil, 23, nil, 23, 23, nil, nil, 19, 23, 23,
- 23, nil, nil, nil, 23, nil, nil, 23, nil, nil,
- nil, nil, 23, 23, 23, nil, 23, 23, 23, nil,
- 23, nil, nil, nil, 23, 23, nil, 23, 23, nil,
- 23, 23, 23, nil, 27, nil, 27, 27, nil, nil,
- 23, 27, 27, 27, nil, nil, nil, 27, nil, nil,
- 27, nil, nil, nil, nil, 27, 27, 27, nil, 27,
- 27, 27, nil, 27, nil, nil, nil, 27, 27, nil,
- 27, 27, nil, 27, 27, 27, nil, 37, nil, 37,
- 37, nil, nil, 27, 37, 37, 37, nil, 37, nil,
- 37, nil, nil, 37, nil, nil, 37, nil, 37, 37,
- 37, nil, 37, 37, 37, nil, 37, nil, nil, nil,
- 37, 37, nil, 37, 37, nil, 37, 37, 37, nil,
- 39, nil, 39, 39, nil, nil, 37, 39, 39, 39,
- nil, nil, nil, 39, nil, nil, 39, nil, nil, nil,
- nil, 39, 39, 39, nil, 39, 39, 39, nil, 39,
- nil, nil, nil, 39, 39, nil, 39, 39, nil, 39,
- 39, 39, nil, 60, nil, 60, 60, nil, nil, 39,
- 60, 60, 60, nil, nil, nil, 60, nil, nil, 60,
- nil, nil, nil, nil, 60, 60, 60, nil, 60, 60,
- 60, nil, 60, nil, nil, nil, 60, 60, nil, 60,
- 60, nil, 60, 60, 60, nil, 61, nil, 61, 61,
- nil, nil, 60, 61, 61, 61, nil, nil, nil, 61,
- nil, nil, 61, nil, nil, nil, nil, 61, 61, 61,
- nil, 61, 61, 61, nil, 61, nil, nil, nil, 61,
- 61, nil, 61, 61, nil, 61, 61, 61, nil, 62,
- nil, 62, 62, nil, nil, 61, 62, 62, 62, nil,
- nil, nil, 62, nil, nil, 62, nil, nil, nil, nil,
- 62, 62, 62, nil, 62, 62, 62, nil, 62, nil,
- nil, nil, 62, 62, nil, 62, 62, nil, 62, 62,
- 62, nil, 63, nil, 63, 63, nil, nil, 62, 63,
- 63, 63, nil, nil, nil, 63, nil, nil, 63, nil,
- nil, nil, nil, 63, 63, 63, nil, 63, 63, 63,
- nil, 63, nil, nil, nil, 63, 63, nil, 63, 63,
- nil, 63, 63, 63, nil, 85, nil, 85, 85, nil,
- nil, 63, 85, 85, 85, nil, nil, nil, 85, nil,
- nil, 85, nil, nil, nil, nil, 85, 85, 85, nil,
- 85, 85, 85, nil, 85, nil, nil, nil, 85, 85,
- nil, 85, 85, nil, 85, 85, 85, nil, 86, nil,
- 86, 86, nil, nil, 85, 86, 86, 86, nil, nil,
- nil, 86, nil, nil, 86, nil, nil, nil, nil, 86,
- 86, 86, nil, 86, 86, 86, nil, 86, nil, nil,
- nil, 86, 86, nil, 86, 86, nil, 86, 86, 86,
- nil, 87, nil, 87, 87, nil, nil, 86, 87, 87,
- 87, nil, nil, nil, 87, nil, nil, 87, nil, nil,
- nil, nil, 87, 87, 87, nil, 87, 87, 87, nil,
- 87, nil, nil, nil, 87, 87, nil, 87, 87, nil,
- 87, 87, 87, nil, 88, nil, 88, 88, nil, nil,
- 87, 88, 88, 88, nil, nil, nil, 88, nil, nil,
- 88, nil, nil, nil, nil, 88, 88, 88, nil, 88,
- 88, 88, nil, 88, nil, nil, nil, 88, 88, nil,
- 88, 88, nil, 88, 88, 88, nil, 89, nil, 89,
- 89, nil, nil, 88, 89, 89, 89, nil, nil, nil,
- 89, nil, nil, 89, nil, nil, nil, nil, 89, 89,
- 89, nil, 89, 89, 89, nil, 89, nil, nil, nil,
- 89, 89, nil, 89, 89, nil, 89, 89, 89, nil,
- 90, nil, 90, 90, nil, nil, 89, 90, 90, 90,
- nil, nil, nil, 90, nil, nil, 90, nil, nil, nil,
- nil, 90, 90, 90, nil, 90, 90, 90, nil, 90,
- nil, nil, nil, 90, 90, nil, 90, 90, nil, 90,
- 90, 90, nil, 91, nil, 91, 91, nil, nil, 90,
- 91, 91, 91, nil, nil, nil, 91, nil, nil, 91,
- nil, nil, nil, nil, 91, 91, 91, nil, 91, 91,
- 91, nil, 91, nil, nil, nil, 91, 91, nil, 91,
- 91, nil, 91, 91, 91, nil, 92, nil, 92, 92,
- nil, nil, 91, 92, 92, 92, nil, nil, nil, 92,
- nil, nil, 92, nil, nil, nil, nil, 92, 92, 92,
- nil, 92, 92, 92, nil, 92, nil, nil, nil, 92,
- 92, nil, 92, 92, nil, 92, 92, 92, nil, 93,
- nil, 93, 93, nil, nil, 92, 93, 93, 93, nil,
- nil, nil, 93, nil, nil, 93, nil, nil, nil, nil,
- 93, 93, 93, nil, 93, 93, 93, nil, 93, nil,
- nil, nil, 93, 93, nil, 93, 93, nil, 93, 93,
- 93, nil, 94, nil, 94, 94, nil, nil, 93, 94,
- 94, 94, nil, nil, nil, 94, nil, nil, 94, nil,
- nil, nil, nil, 94, 94, 94, nil, 94, 94, 94,
- nil, 94, nil, nil, nil, 94, 94, nil, 94, 94,
- nil, 94, 94, 94, nil, 95, nil, 95, 95, nil,
- nil, 94, 95, 95, 95, nil, nil, nil, 95, nil,
- nil, 95, nil, nil, nil, nil, 95, 95, 95, nil,
- 95, 95, 95, nil, 95, nil, nil, nil, 95, 95,
- nil, 95, 95, nil, 95, 95, 95, nil, 96, nil,
- 96, 96, nil, nil, 95, 96, 96, 96, nil, nil,
- nil, 96, nil, nil, 96, nil, nil, nil, nil, 96,
- 96, 96, nil, 96, 96, 96, nil, 96, nil, nil,
- nil, 96, 96, nil, 96, 96, nil, 96, 96, 96,
- nil, 97, nil, 97, 97, nil, nil, 96, 97, 97,
- 97, nil, nil, nil, 97, nil, nil, 97, nil, nil,
- nil, nil, 97, 97, 97, nil, 97, 97, 97, nil,
- 97, nil, nil, nil, 97, 97, nil, 97, 97, nil,
- 97, 97, 97, nil, 98, nil, 98, 98, nil, nil,
- 97, 98, 98, 98, nil, nil, nil, 98, nil, nil,
- 98, nil, nil, nil, nil, 98, 98, 98, nil, 98,
- 98, 98, nil, 98, nil, nil, nil, 98, 98, nil,
- 98, 98, nil, 98, 98, 98, nil, 111, nil, 111,
- 111, nil, nil, 98, 111, 111, 111, nil, nil, 111,
- 111, nil, nil, 111, 111, 111, nil, nil, 111, 111,
- 111, nil, 111, 111, 111, nil, 111, nil, nil, nil,
- 111, 111, nil, 111, 111, nil, 111, 111, 111, nil,
- 123, nil, 123, 123, nil, nil, 111, 123, 123, 123,
- nil, nil, nil, 123, nil, nil, 123, nil, nil, nil,
- nil, 123, 123, 123, nil, 123, 123, 123, nil, 123,
- nil, nil, nil, 123, 123, nil, 123, 123, nil, 123,
- 123, 123, nil, 129, nil, 129, 129, nil, nil, 123,
- 129, 129, 129, nil, nil, nil, 129, nil, nil, 129,
- nil, nil, nil, nil, 129, 129, 129, nil, 129, 129,
- 129, nil, 129, nil, nil, nil, 129, 129, nil, 129,
- 129, nil, 129, 129, 129, nil, 153, nil, 153, 153,
- nil, nil, 129, 153, 153, 153, nil, nil, nil, 153,
- nil, nil, 153, nil, nil, nil, nil, 153, 153, 153,
- nil, 153, 153, 153, nil, 153, nil, nil, nil, 153,
- 153, nil, 153, 153, nil, 153, 153, 153, nil, 155,
- nil, 155, 155, nil, nil, 153, 155, 155, 155, nil,
- nil, nil, 155, nil, nil, 155, nil, nil, nil, nil,
- 155, 155, 155, nil, 155, 155, 155, nil, 155, nil,
- nil, nil, 155, 155, nil, 155, 155, nil, 155, 155,
- 155, nil, 156, nil, 156, 156, nil, nil, 155, 156,
- 156, 156, nil, nil, nil, 156, nil, nil, 156, nil,
- nil, nil, nil, 156, 156, 156, nil, 156, 156, 156,
- nil, 156, nil, nil, nil, 156, 156, nil, 156, 156,
- nil, 156, 156, 156, nil, 157, nil, 157, 157, nil,
- nil, 156, 157, 157, 157, nil, nil, 157, 157, nil,
- nil, 157, 157, 157, nil, nil, 157, 157, 157, nil,
- 157, 157, 157, nil, 157, nil, nil, nil, 157, 157,
- nil, 157, 157, nil, 157, 157, 157, nil, 180, nil,
- 180, 180, nil, nil, 157, 180, 180, 180, nil, nil,
- nil, 180, nil, nil, 180, nil, nil, nil, nil, 180,
- 180, 180, nil, 180, 180, 180, nil, 180, nil, nil,
- nil, 180, 180, nil, 180, 180, nil, 180, 180, 180,
- 3, 3, 3, 3, nil, 142, nil, 180, 3, nil,
- nil, nil, nil, 3, 142, 3, 3, 99, 99, 99,
- 99, nil, 3, nil, nil, 99, nil, nil, nil, nil,
- 99, nil, 142, 142, 142, 142, 142, 142, 142, 99,
- 3, 3, 3, 3, 3, 3, 3, nil, 3, nil,
- nil, nil, nil, nil, nil, nil, nil, 99, 99, 99,
- 99, 99, 99, 99, nil, 99, 101, 101, 101, 101,
- nil, nil, nil, nil, 101, nil, nil, nil, nil, 101,
- nil, 104, 104, 104, 104, 104, nil, nil, 101, 104,
- nil, nil, nil, nil, 104, nil, nil, nil, nil, nil,
- nil, nil, nil, 104, nil, nil, 101, 101, 101, 101,
- 101, 101, 101, nil, 101, nil, nil, nil, nil, nil,
- nil, 104, 104, 104, 104, 104, 104, 104, nil, 104,
- 117, 117, 117, 117, nil, nil, nil, nil, 117, nil,
- nil, nil, nil, 117, nil, 117, 122, 122, 122, 122,
- nil, nil, 117, nil, 122, nil, nil, nil, 122, 122,
- nil, nil, nil, nil, nil, nil, nil, nil, 122, nil,
- 117, 117, 117, 117, 117, 117, 117, nil, 117, nil,
- nil, nil, nil, nil, nil, nil, 122, 122, 122, 122,
- 122, 122, 122, nil, 122, 144, 144, 144, 144, nil,
- nil, nil, nil, 144, nil, nil, nil, nil, 144, nil,
- 145, 145, 145, 145, nil, nil, nil, 144, 145, nil,
- nil, nil, nil, 145, nil, nil, nil, nil, nil, nil,
- nil, nil, 145, nil, nil, 144, 144, 144, 144, 144,
- 144, 144, nil, 144, nil, nil, nil, nil, nil, nil,
- 145, 145, 145, 145, 145, 145, 145, nil, 145, 146,
- 146, 146, 146, nil, nil, nil, nil, 146, nil, nil,
- nil, nil, 146, nil, 147, 147, 147, 147, nil, nil,
- nil, 146, 147, nil, nil, nil, nil, 147, nil, nil,
- nil, nil, nil, nil, nil, nil, 147, nil, nil, 146,
- 146, 146, 146, 146, 146, 146, nil, 146, nil, nil,
- nil, nil, nil, nil, 147, 147, 147, 147, 147, 147,
- 147, nil, 147, 148, 148, 148, 148, nil, nil, nil,
- nil, 148, nil, nil, nil, nil, 148, nil, 149, 149,
- 149, 149, nil, nil, nil, 148, 149, nil, nil, nil,
- nil, 149, nil, nil, nil, nil, nil, nil, nil, nil,
- 149, nil, nil, 148, 148, 148, 148, 148, 148, 148,
- nil, 148, nil, nil, nil, nil, nil, nil, 149, 149,
- 149, 149, 149, 149, 149, nil, 149, 150, 150, 150,
- 150, nil, nil, nil, nil, 150, nil, nil, nil, nil,
- 150, nil, 151, 151, 151, 151, nil, nil, nil, 150,
- 151, nil, nil, nil, nil, 151, nil, nil, nil, nil,
- nil, nil, nil, nil, 151, nil, nil, 150, 150, 150,
- 150, 150, 150, 150, nil, 150, nil, nil, nil, nil,
- nil, nil, 151, 151, 151, 151, 151, 151, 151, nil,
- 151, 158, 158, 158, 158, nil, nil, nil, nil, 158,
- nil, 158, nil, nil, 158, nil, 158, 158, 168, 168,
- 168, 168, nil, 158, nil, nil, 168, nil, nil, nil,
- nil, 168, nil, nil, nil, nil, nil, nil, nil, nil,
- 168, 158, 158, 158, 158, 158, 158, 158, nil, 158,
- nil, nil, nil, nil, nil, nil, nil, nil, 168, 168,
- 168, 168, 168, 168, 168, nil, 168, 171, 171, 171,
- 171, nil, nil, nil, nil, 171, nil, nil, nil, nil,
- 171, nil, 173, 173, 173, 173, nil, nil, nil, 171,
- 173, nil, nil, nil, nil, 173, nil, 171, nil, nil,
- nil, nil, nil, nil, 173, nil, nil, 171, 171, 171,
- 171, 171, 171, 171, nil, 171, nil, nil, nil, nil,
- nil, nil, 173, 173, 173, 173, 173, 173, 173, nil,
- 173, 175, 175, 175, 175, nil, nil, nil, nil, 175,
- nil, nil, nil, nil, 175, nil, 185, 185, 185, 185,
- nil, nil, nil, 175, 185, nil, nil, nil, nil, 185,
- nil, nil, nil, nil, nil, nil, nil, nil, 185, nil,
- nil, 175, 175, 175, 175, 175, 175, 175, nil, 175,
- nil, nil, nil, nil, nil, nil, 185, 185, 185, 185,
- 185, 185, 185, nil, 185, 125, 125, nil, nil, nil,
- nil, 125, nil, nil, nil, nil, 125, 126, 126, nil,
- nil, nil, nil, 126, nil, 125, nil, nil, 126, nil,
- 138, 138, nil, nil, nil, nil, 138, 126, nil, nil,
- nil, 138, nil, 125, 125, 125, 125, 125, 125, 125,
- 138, 125, nil, nil, nil, 126, 126, 126, 126, 126,
- 126, 126, nil, 126, nil, nil, nil, nil, 138, 138,
- 138, 138, 138, 138, 138, 124, 138, 139, 139, nil,
- 124, nil, nil, 139, nil, nil, nil, nil, 139, 124,
- nil, nil, nil, nil, nil, nil, nil, 139, nil, nil,
- 127, nil, nil, nil, nil, 127, nil, 124, 124, 124,
- 124, 124, 124, 124, 127, 139, 139, 139, 139, 139,
- 139, 139, nil, 139, nil, 140, nil, nil, nil, nil,
- 140, nil, 127, 127, 127, 127, 127, 127, 127, 140,
- 141, nil, nil, nil, nil, 141, nil, nil, nil, nil,
- 143, nil, nil, nil, 141, 143, nil, 140, 140, 140,
- 140, 140, 140, 140, 143, nil, nil, nil, nil, nil,
- nil, nil, 141, 141, 141, 141, 141, 141, 141, nil,
- nil, nil, 143, 143, 143, 143, 143, 143, 143 ]
-
-racc_action_pointer = [
- 43, 0, nil, 1416, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 86,
- nil, nil, nil, 129, nil, nil, nil, 172, nil, nil,
- nil, nil, nil, -1, -15, nil, nil, 215, nil, 258,
- 3, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 301, 344, 387, 430, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 27, 8, nil, nil, 19, 473, 516, 559, 602, 645,
- 688, 731, 774, 817, 860, 903, 946, 989, 1032, 1433,
- -15, 1482, -10, nil, 1497, nil, nil, nil, nil, nil,
- nil, 1075, 14, nil, nil, nil, nil, 1546, nil, nil,
- nil, 13, 1562, 1118, 2123, 2059, 2071, 2148, 43, 1161,
- nil, 3, nil, nil, nil, nil, nil, nil, 2084, 2131,
- 2173, 2188, 1408, 2198, 1611, 1626, 1675, 1690, 1739, 1754,
- 1803, 1818, nil, 1204, nil, 1247, 1290, 1333, 1867, nil,
- nil, -8, nil, 31, nil, nil, nil, nil, 1884, nil,
- nil, 1933, nil, 1948, nil, 1997, nil, nil, 21, 42,
- 1376, nil, nil, 36, nil, 2012, nil ]
-
-racc_action_default = [
- -140, -140, -1, -4, -5, -6, -7, -10, -11, -12,
- -13, -14, -15, -16, -17, -18, -19, -20, -21, -23,
- -24, -25, -26, -140, -30, -31, -32, -140, -37, -55,
- -56, -57, -60, -140, -63, -64, -65, -140, -73, -140,
- -76, -77, -78, -79, -80, -81, -82, -83, -84, -85,
- -86, -87, -88, -89, -90, -91, -107, -108, -109, -110,
- -140, -140, -140, -140, -115, -116, -117, -118, -119, -120,
- -121, -122, -123, -124, -125, -126, -127, -128, -129, -130,
- -140, -140, -2, -3, -140, -140, -140, -140, -140, -140,
- -140, -140, -140, -140, -140, -140, -140, -140, -140, -22,
- -140, -28, -140, -34, -140, -61, -62, -74, -38, -39,
- -40, -140, -140, -46, -47, -48, -49, -69, -66, -67,
- -68, -71, -140, -140, -111, -112, -113, -114, -140, -140,
- -133, -135, -136, -137, -138, 187, -58, -59, -93, -94,
- -95, -96, -97, -98, -99, -100, -101, -102, -103, -104,
- -105, -106, -27, -140, -33, -140, -140, -140, -4, -43,
- -44, -140, -50, -52, -54, -70, -72, -75, -92, -131,
- -134, -140, -139, -29, -35, -36, -41, -42, -9, -140,
- -140, -132, -8, -140, -51, -53, -45 ]
-
-racc_goto_table = [
- 99, 82, 103, 83, 101, 1, 105, 130, 104, 183,
- 100, 102, 159, 162, 121, 108, 109, 110, 117, 111,
- 122, 115, 112, 161, 116, 107, 118, 119, 120, 128,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 124, 125, 126, 127, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 170, nil, 136, 176, 177,
- nil, nil, nil, nil, nil, nil, 138, 139, 140, 141,
- 142, 143, 144, 145, 146, 147, 148, 149, 150, 151,
- 184, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 158, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 168, nil, nil, nil, nil, nil,
- 171, nil, nil, nil, nil, nil, 157, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 174, nil, nil, nil, 173, nil, 104, 175, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 82, 83, nil,
- nil, 185 ]
-
-racc_goto_check = [
- 3, 2, 24, 4, 3, 1, 40, 77, 3, 5,
- 20, 23, 30, 35, 25, 26, 27, 28, 3, 29,
- 3, 31, 32, 33, 34, 43, 44, 45, 46, 75,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 3, 3, 3, 3, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 77, nil, 40, 30, 30,
- nil, nil, nil, nil, nil, nil, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 35, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 3, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 3, nil, nil, nil, nil, nil,
- 3, nil, nil, nil, nil, nil, 1, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 24, nil, nil, nil, 3, nil, 3, 3, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 2, 4, nil,
- nil, 3 ]
-
-racc_goto_pointer = [
- nil, 5, 0, -19, 0, -169, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- -13, nil, nil, -16, -25, -23, -22, -21, -20, -18,
- -99, -16, -15, -89, -13, -99, nil, nil, nil, nil,
- -27, nil, nil, -9, -11, -10, -9, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, -51, nil, -73, nil ]
-
-racc_goto_default = [
- nil, nil, 2, 3, 4, nil, 7, 8, 9, 10,
- 11, 12, 13, 14, 15, 16, 17, 18, 20, 21,
- nil, 24, 25, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 163, 29, 30, 31,
- 32, 34, 35, 38, nil, nil, nil, 41, 42, 43,
- 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
- 54, 55, 56, 57, 58, 59, 64, 65, 66, 67,
- 68, 69, 70, 78, 79, nil, 132, nil, 131 ]
-
-racc_reduce_table = [
- 0, 0, :racc_error,
- 1, 54, :_reduce_none,
- 2, 54, :_reduce_2,
- 2, 55, :_reduce_3,
- 1, 55, :_reduce_4,
- 1, 55, :_reduce_5,
- 1, 57, :_reduce_none,
- 1, 57, :_reduce_none,
- 1, 58, :_reduce_none,
- 0, 58, :_reduce_none,
- 1, 56, :_reduce_none,
- 1, 56, :_reduce_none,
- 1, 56, :_reduce_none,
- 1, 56, :_reduce_none,
- 1, 56, :_reduce_none,
- 1, 56, :_reduce_none,
- 1, 56, :_reduce_none,
- 1, 56, :_reduce_none,
- 1, 56, :_reduce_none,
- 1, 56, :_reduce_none,
- 1, 68, :_reduce_none,
- 1, 68, :_reduce_none,
- 2, 69, :_reduce_22,
- 1, 70, :_reduce_23,
- 1, 66, :_reduce_none,
- 1, 66, :_reduce_none,
- 1, 71, :_reduce_26,
- 3, 72, :_reduce_27,
- 1, 73, :_reduce_28,
- 3, 73, :_reduce_29,
- 1, 67, :_reduce_none,
- 1, 67, :_reduce_none,
- 1, 74, :_reduce_32,
- 3, 75, :_reduce_33,
- 1, 76, :_reduce_34,
- 3, 76, :_reduce_35,
- 3, 77, :_reduce_36,
- 1, 64, :_reduce_37,
- 1, 78, :_reduce_none,
- 1, 78, :_reduce_none,
- 1, 78, :_reduce_none,
- 3, 79, :_reduce_41,
- 3, 80, :_reduce_42,
- 2, 81, :_reduce_43,
- 1, 83, :_reduce_44,
- 5, 84, :_reduce_45,
- 1, 85, :_reduce_46,
- 1, 87, :_reduce_47,
- 1, 82, :_reduce_none,
- 1, 82, :_reduce_none,
- 1, 86, :_reduce_none,
- 3, 86, :_reduce_none,
- 1, 88, :_reduce_52,
- 3, 88, :_reduce_53,
- 1, 89, :_reduce_54,
- 1, 63, :_reduce_none,
- 1, 63, :_reduce_none,
- 1, 63, :_reduce_none,
- 3, 90, :_reduce_58,
- 3, 90, :_reduce_59,
- 1, 91, :_reduce_60,
- 2, 92, :_reduce_61,
- 2, 92, :_reduce_62,
- 1, 93, :_reduce_none,
- 1, 93, :_reduce_none,
- 1, 95, :_reduce_65,
- 2, 96, :_reduce_66,
- 1, 97, :_reduce_none,
- 1, 97, :_reduce_none,
- 1, 98, :_reduce_none,
- 2, 98, :_reduce_none,
- 1, 99, :_reduce_none,
- 2, 99, :_reduce_none,
- 1, 94, :_reduce_73,
- 2, 94, :_reduce_74,
- 3, 60, :_reduce_75,
- 1, 65, :_reduce_76,
- 1, 61, :_reduce_none,
- 1, 61, :_reduce_none,
- 1, 61, :_reduce_none,
- 1, 61, :_reduce_none,
- 1, 61, :_reduce_none,
- 1, 61, :_reduce_none,
- 1, 61, :_reduce_none,
- 1, 61, :_reduce_none,
- 1, 61, :_reduce_none,
- 1, 61, :_reduce_none,
- 1, 61, :_reduce_none,
- 1, 61, :_reduce_none,
- 1, 61, :_reduce_none,
- 1, 61, :_reduce_none,
- 1, 61, :_reduce_none,
- 3, 100, :_reduce_92,
- 3, 101, :_reduce_93,
- 3, 102, :_reduce_94,
- 3, 103, :_reduce_95,
- 3, 104, :_reduce_96,
- 3, 105, :_reduce_97,
- 3, 106, :_reduce_98,
- 3, 107, :_reduce_99,
- 3, 108, :_reduce_100,
- 3, 109, :_reduce_101,
- 3, 110, :_reduce_102,
- 3, 111, :_reduce_103,
- 3, 112, :_reduce_104,
- 3, 113, :_reduce_105,
- 3, 114, :_reduce_106,
- 1, 62, :_reduce_none,
- 1, 62, :_reduce_none,
- 1, 62, :_reduce_none,
- 1, 62, :_reduce_none,
- 2, 115, :_reduce_111,
- 2, 116, :_reduce_112,
- 2, 117, :_reduce_113,
- 2, 118, :_reduce_114,
- 1, 59, :_reduce_none,
- 1, 59, :_reduce_none,
- 1, 59, :_reduce_none,
- 1, 59, :_reduce_none,
- 1, 59, :_reduce_none,
- 1, 59, :_reduce_none,
- 1, 59, :_reduce_none,
- 1, 120, :_reduce_122,
- 1, 119, :_reduce_123,
- 1, 122, :_reduce_124,
- 1, 123, :_reduce_125,
- 1, 124, :_reduce_126,
- 1, 125, :_reduce_127,
- 1, 121, :_reduce_128,
- 1, 121, :_reduce_none,
- 1, 121, :_reduce_none,
- 3, 126, :_reduce_131,
- 3, 129, :_reduce_132,
- 1, 128, :_reduce_133,
- 2, 128, :_reduce_134,
- 1, 130, :_reduce_135,
- 1, 130, :_reduce_136,
- 2, 127, :_reduce_137,
- 1, 131, :_reduce_138,
- 2, 131, :_reduce_139 ]
-
-racc_reduce_n = 140
-
-racc_shift_n = 187
-
-racc_token_table = {
- false => 0,
- :error => 1,
- :IDENTIFIER => 2,
- :EQUAL => 3,
- :PLUS => 4,
- :MINUS => 5,
- :ASTERISK => 6,
- :FWD_SLASH => 7,
- :COLON => 8,
- :FLOAT => 9,
- :INTEGER => 10,
- :STRING => 11,
- :EXPO => 12,
- :INDENT => 13,
- :OUTDENT => 14,
- :OPAREN => 15,
- :CPAREN => 16,
- :DOT => 17,
- :SIGNATURE => 18,
- :NL => 19,
- :EOF => 20,
- :PIPE => 21,
- :COMMA => 22,
- :NIL => 23,
- :TRUE => 24,
- :FALSE => 25,
- :EQUALITY => 26,
- :CALL => 27,
- :SELF => 28,
- :CONSTANT => 29,
- :CHAR => 30,
- :DOUBLE_TICK_STRING => 31,
- :DOUBLE_TICK_STRING_END => 32,
- :INTERPOLATE_START => 33,
- :INTERPOLATE_END => 34,
- :BOX => 35,
- :LSQUARE => 36,
- :RSQUARE => 37,
- :FACES => 38,
- :LFACE => 39,
- :RFACE => 40,
- :BANG => 41,
- :TILDE => 42,
- :RETURN => 43,
- :NOT_EQUALITY => 44,
- :OR => 45,
- :AND => 46,
- :GT => 47,
- :LT => 48,
- :GTE => 49,
- :LTE => 50,
- :AT => 51,
- :PERCENT => 52 }
-
-racc_nt_base = 53
-
-racc_use_result_var = true
-
-Racc_arg = [
- racc_action_table,
- racc_action_check,
- racc_action_default,
- racc_action_pointer,
- racc_goto_table,
- racc_goto_check,
- racc_goto_default,
- racc_goto_pointer,
- racc_nt_base,
- racc_reduce_table,
- racc_token_table,
- racc_shift_n,
- racc_reduce_n,
- racc_use_result_var ]
-Ractor.make_shareable(Racc_arg) if defined?(Ractor)
-
-Racc_token_to_s_table = [
- "$end",
- "error",
- "IDENTIFIER",
- "EQUAL",
- "PLUS",
- "MINUS",
- "ASTERISK",
- "FWD_SLASH",
- "COLON",
- "FLOAT",
- "INTEGER",
- "STRING",
- "EXPO",
- "INDENT",
- "OUTDENT",
- "OPAREN",
- "CPAREN",
- "DOT",
- "SIGNATURE",
- "NL",
- "EOF",
- "PIPE",
- "COMMA",
- "NIL",
- "TRUE",
- "FALSE",
- "EQUALITY",
- "CALL",
- "SELF",
- "CONSTANT",
- "CHAR",
- "DOUBLE_TICK_STRING",
- "DOUBLE_TICK_STRING_END",
- "INTERPOLATE_START",
- "INTERPOLATE_END",
- "BOX",
- "LSQUARE",
- "RSQUARE",
- "FACES",
- "LFACE",
- "RFACE",
- "BANG",
- "TILDE",
- "RETURN",
- "NOT_EQUALITY",
- "OR",
- "AND",
- "GT",
- "LT",
- "GTE",
- "LTE",
- "AT",
- "PERCENT",
- "$start",
- "statements",
- "statement",
- "expr",
- "eol",
- "nlq",
- "literal",
- "grouped_expr",
- "binary_op",
- "unary_op",
- "method_call",
- "constant",
- "variable",
- "array",
- "hash",
- "return",
- "return_expr",
- "return_nil",
- "empty_array",
- "array_list",
- "array_items",
- "empty_hash",
- "hash_list",
- "hash_items",
- "hash_item",
- "indented",
- "indented_w_stmts",
- "indented_w_expr",
- "indented_wo_stmts",
- "indent",
- "outdent",
- "indent_w_args",
- "indent_pipe",
- "indent_args",
- "indent_wo_args",
- "indent_arg",
- "arg_var",
- "method_call_on_object",
- "method_call_on_self",
- "method_call_on_closure",
- "call_signature",
- "call_arguments",
- "call_simple_name",
- "call_argument",
- "call_passed_arg",
- "call_passed_simple",
- "call_passed_indented",
- "assignment",
- "addition",
- "subtraction",
- "multiplication",
- "division",
- "exponentiation",
- "modulo",
- "equality",
- "not_equality",
- "logical_or",
- "logical_and",
- "greater_than",
- "less_than",
- "greater_or_eq",
- "less_or_eq",
- "unary_not",
- "unary_plus",
- "unary_minus",
- "unary_complement",
- "integer",
- "float",
- "string",
- "nil",
- "true",
- "false",
- "self",
- "interpolated_string",
- "empty_string",
- "interpolated_string_contents",
- "interpolation",
- "interpolated_string_chunk",
- "chars" ]
-Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)
-
-Racc_debug_parser = false
-
-##### State transition tables end #####
-
-# reduce 0 omitted
-
-# reduce 1 omitted
-
-module_eval(<<'.,.,', 'huia.y', 44)
- def _reduce_2(val, _values, result)
- return scope
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 46)
- def _reduce_3(val, _values, result)
- return scope.append val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 47)
- def _reduce_4(val, _values, result)
- return scope.append val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 48)
- def _reduce_5(val, _values, result)
- return scope
- result
- end
-.,.,
-
-# reduce 6 omitted
-
-# reduce 7 omitted
-
-# reduce 8 omitted
-
-# reduce 9 omitted
-
-# reduce 10 omitted
-
-# reduce 11 omitted
-
-# reduce 12 omitted
-
-# reduce 13 omitted
-
-# reduce 14 omitted
-
-# reduce 15 omitted
-
-# reduce 16 omitted
-
-# reduce 17 omitted
-
-# reduce 18 omitted
-
-# reduce 19 omitted
-
-# reduce 20 omitted
-
-# reduce 21 omitted
-
-module_eval(<<'.,.,', 'huia.y', 66)
- def _reduce_22(val, _values, result)
- return n(:Return, val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 67)
- def _reduce_23(val, _values, result)
- return n(:Return, n(:Nil))
- result
- end
-.,.,
-
-# reduce 24 omitted
-
-# reduce 25 omitted
-
-module_eval(<<'.,.,', 'huia.y', 72)
- def _reduce_26(val, _values, result)
- return n :Array
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 74)
- def _reduce_27(val, _values, result)
- return val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 75)
- def _reduce_28(val, _values, result)
- return n :Array, [val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 76)
- def _reduce_29(val, _values, result)
- val[0].append(val[2]); return val[0]
- result
- end
-.,.,
-
-# reduce 30 omitted
-
-# reduce 31 omitted
-
-module_eval(<<'.,.,', 'huia.y', 80)
- def _reduce_32(val, _values, result)
- return n :Hash
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 81)
- def _reduce_33(val, _values, result)
- return val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 82)
- def _reduce_34(val, _values, result)
- return n :Hash, val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 83)
- def _reduce_35(val, _values, result)
- val[0].append(val[2]); return val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 84)
- def _reduce_36(val, _values, result)
- return n :HashItem, val[0], val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 86)
- def _reduce_37(val, _values, result)
- return constant val[0]
- result
- end
-.,.,
-
-# reduce 38 omitted
-
-# reduce 39 omitted
-
-# reduce 40 omitted
-
-module_eval(<<'.,.,', 'huia.y', 91)
- def _reduce_41(val, _values, result)
- return val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 92)
- def _reduce_42(val, _values, result)
- return val[0].append(val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 93)
- def _reduce_43(val, _values, result)
- return val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 94)
- def _reduce_44(val, _values, result)
- return pop_scope
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 97)
- def _reduce_45(val, _values, result)
- return val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 98)
- def _reduce_46(val, _values, result)
- return push_scope
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 99)
- def _reduce_47(val, _values, result)
- return push_scope
- result
- end
-.,.,
-
-# reduce 48 omitted
-
-# reduce 49 omitted
-
-# reduce 50 omitted
-
-# reduce 51 omitted
-
-module_eval(<<'.,.,', 'huia.y', 105)
- def _reduce_52(val, _values, result)
- return scope.add_argument val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 106)
- def _reduce_53(val, _values, result)
- return n :Assignment, val[0], val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 107)
- def _reduce_54(val, _values, result)
- return n :Variable, val[0]
- result
- end
-.,.,
-
-# reduce 55 omitted
-
-# reduce 56 omitted
-
-# reduce 57 omitted
-
-module_eval(<<'.,.,', 'huia.y', 112)
- def _reduce_58(val, _values, result)
- return n :MethodCall, val[0], val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 113)
- def _reduce_59(val, _values, result)
- return n :MethodCall, val[0], n(:CallSignature, val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 114)
- def _reduce_60(val, _values, result)
- return n :MethodCall, scope_instance, val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 116)
- def _reduce_61(val, _values, result)
- return n :MethodCall, this_closure, val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 117)
- def _reduce_62(val, _values, result)
- return n :MethodCall, this_closure, n(:CallSignature, val[1])
- result
- end
-.,.,
-
-# reduce 63 omitted
-
-# reduce 64 omitted
-
-module_eval(<<'.,.,', 'huia.y', 121)
- def _reduce_65(val, _values, result)
- return n :CallSignature, val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 122)
- def _reduce_66(val, _values, result)
- return n :CallSignature, val[0], [val[1]]
- result
- end
-.,.,
-
-# reduce 67 omitted
-
-# reduce 68 omitted
-
-# reduce 69 omitted
-
-# reduce 70 omitted
-
-# reduce 71 omitted
-
-# reduce 72 omitted
-
-module_eval(<<'.,.,', 'huia.y', 129)
- def _reduce_73(val, _values, result)
- return val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 130)
- def _reduce_74(val, _values, result)
- return val[0].concat_signature val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 132)
- def _reduce_75(val, _values, result)
- return n :Expression, val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 134)
- def _reduce_76(val, _values, result)
- return allocate_local val[0]
- result
- end
-.,.,
-
-# reduce 77 omitted
-
-# reduce 78 omitted
-
-# reduce 79 omitted
-
-# reduce 80 omitted
-
-# reduce 81 omitted
-
-# reduce 82 omitted
-
-# reduce 83 omitted
-
-# reduce 84 omitted
-
-# reduce 85 omitted
-
-# reduce 86 omitted
-
-# reduce 87 omitted
-
-# reduce 88 omitted
-
-# reduce 89 omitted
-
-# reduce 90 omitted
-
-# reduce 91 omitted
-
-module_eval(<<'.,.,', 'huia.y', 152)
- def _reduce_92(val, _values, result)
- return allocate_local_assignment val[0], val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 153)
- def _reduce_93(val, _values, result)
- return binary val[0], val[2], 'plus:'
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 154)
- def _reduce_94(val, _values, result)
- return binary val[0], val[2], 'minus:'
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 155)
- def _reduce_95(val, _values, result)
- return binary val[0], val[2], 'multiplyBy:'
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 156)
- def _reduce_96(val, _values, result)
- return binary val[0], val[2], 'divideBy:'
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 157)
- def _reduce_97(val, _values, result)
- return binary val[0], val[2], 'toThePowerOf:'
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 158)
- def _reduce_98(val, _values, result)
- return binary val[0], val[2], 'moduloOf:'
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 159)
- def _reduce_99(val, _values, result)
- return binary val[0], val[2], 'isEqualTo:'
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 160)
- def _reduce_100(val, _values, result)
- return binary val[0], val[2], 'isNotEqualTo:'
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 161)
- def _reduce_101(val, _values, result)
- return binary val[0], val[2], 'logicalOr:'
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 162)
- def _reduce_102(val, _values, result)
- return binary val[0], val[2], 'logicalAnd:'
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 163)
- def _reduce_103(val, _values, result)
- return binary val[0], val[2], 'isGreaterThan:'
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 164)
- def _reduce_104(val, _values, result)
- return binary val[0], val[2], 'isLessThan:'
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 165)
- def _reduce_105(val, _values, result)
- return binary val[0], val[2], 'isGreaterOrEqualTo:'
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 166)
- def _reduce_106(val, _values, result)
- return binary val[0], val[2], 'isLessOrEqualTo:'
- result
- end
-.,.,
-
-# reduce 107 omitted
-
-# reduce 108 omitted
-
-# reduce 109 omitted
-
-# reduce 110 omitted
-
-module_eval(<<'.,.,', 'huia.y', 173)
- def _reduce_111(val, _values, result)
- return unary val[1], 'unaryNot'
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 174)
- def _reduce_112(val, _values, result)
- return unary val[1], 'unaryPlus'
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 175)
- def _reduce_113(val, _values, result)
- return unary val[1], 'unaryMinus'
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 176)
- def _reduce_114(val, _values, result)
- return unary val[1], 'unaryComplement'
- result
- end
-.,.,
-
-# reduce 115 omitted
-
-# reduce 116 omitted
-
-# reduce 117 omitted
-
-# reduce 118 omitted
-
-# reduce 119 omitted
-
-# reduce 120 omitted
-
-# reduce 121 omitted
-
-module_eval(<<'.,.,', 'huia.y', 186)
- def _reduce_122(val, _values, result)
- return n :Float, val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 187)
- def _reduce_123(val, _values, result)
- return n :Integer, val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 188)
- def _reduce_124(val, _values, result)
- return n :Nil
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 189)
- def _reduce_125(val, _values, result)
- return n :True
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 190)
- def _reduce_126(val, _values, result)
- return n :False
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 191)
- def _reduce_127(val, _values, result)
- return n :Self
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 193)
- def _reduce_128(val, _values, result)
- return n :String, val[0]
- result
- end
-.,.,
-
-# reduce 129 omitted
-
-# reduce 130 omitted
-
-module_eval(<<'.,.,', 'huia.y', 197)
- def _reduce_131(val, _values, result)
- return val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 198)
- def _reduce_132(val, _values, result)
- return val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 199)
- def _reduce_133(val, _values, result)
- return n :InterpolatedString, val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 200)
- def _reduce_134(val, _values, result)
- val[0].append(val[1]); return val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 201)
- def _reduce_135(val, _values, result)
- return val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 202)
- def _reduce_136(val, _values, result)
- return to_string(val[0])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 203)
- def _reduce_137(val, _values, result)
- return n :String, ''
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 205)
- def _reduce_138(val, _values, result)
- return n :String, val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'huia.y', 206)
- def _reduce_139(val, _values, result)
- val[0].append(val[1]); return val[0]
- result
- end
-.,.,
-
-def _reduce_none(val, _values, result)
- val[0]
-end
-
- end # class Parser
-end # module Huia
diff --git a/test/racc/regress/journey b/test/racc/regress/journey
deleted file mode 100644
index 4cc0dcd6aa..0000000000
--- a/test/racc/regress/journey
+++ /dev/null
@@ -1,224 +0,0 @@
-#
-# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.5.2
-# from Racc grammar file "".
-#
-
-require 'racc/parser.rb'
-
-
-require 'journey/parser_extras'
-module Journey
- class Parser < Racc::Parser
-##### State transition tables begin ###
-
-racc_action_table = [
- 17, 21, 13, 15, 14, 7, nil, 16, 8, 19,
- 13, 15, 14, 7, 23, 16, 8, 19, 13, 15,
- 14, 7, nil, 16, 8, 13, 15, 14, 7, nil,
- 16, 8, 13, 15, 14, 7, nil, 16, 8 ]
-
-racc_action_check = [
- 1, 17, 1, 1, 1, 1, nil, 1, 1, 1,
- 20, 20, 20, 20, 20, 20, 20, 20, 0, 0,
- 0, 0, nil, 0, 0, 7, 7, 7, 7, nil,
- 7, 7, 19, 19, 19, 19, nil, 19, 19 ]
-
-racc_action_pointer = [
- 16, 0, nil, nil, nil, nil, nil, 23, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 1, nil, 30,
- 8, nil, nil, nil ]
-
-racc_action_default = [
- -18, -18, -2, -3, -4, -5, -6, -18, -9, -10,
- -11, -12, -13, -14, -15, -16, -17, -18, -1, -18,
- -18, 24, -8, -7 ]
-
-racc_goto_table = [
- 18, 1, nil, nil, nil, nil, nil, nil, 20, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 22, 18 ]
-
-racc_goto_check = [
- 2, 1, nil, nil, nil, nil, nil, nil, 1, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 2, 2 ]
-
-racc_goto_pointer = [
- nil, 1, -1, nil, nil, nil, nil, nil, nil, nil,
- nil ]
-
-racc_goto_default = [
- nil, nil, 2, 3, 4, 5, 6, 9, 10, 11,
- 12 ]
-
-racc_reduce_table = [
- 0, 0, :racc_error,
- 2, 11, :_reduce_1,
- 1, 11, :_reduce_2,
- 1, 11, :_reduce_none,
- 1, 12, :_reduce_none,
- 1, 12, :_reduce_none,
- 1, 12, :_reduce_none,
- 3, 15, :_reduce_7,
- 3, 13, :_reduce_8,
- 1, 16, :_reduce_9,
- 1, 14, :_reduce_none,
- 1, 14, :_reduce_none,
- 1, 14, :_reduce_none,
- 1, 14, :_reduce_none,
- 1, 19, :_reduce_14,
- 1, 17, :_reduce_15,
- 1, 18, :_reduce_16,
- 1, 20, :_reduce_17 ]
-
-racc_reduce_n = 18
-
-racc_shift_n = 24
-
-racc_token_table = {
- false => 0,
- :error => 1,
- :SLASH => 2,
- :LITERAL => 3,
- :SYMBOL => 4,
- :LPAREN => 5,
- :RPAREN => 6,
- :DOT => 7,
- :STAR => 8,
- :OR => 9 }
-
-racc_nt_base = 10
-
-racc_use_result_var = true
-
-Racc_arg = [
- racc_action_table,
- racc_action_check,
- racc_action_default,
- racc_action_pointer,
- racc_goto_table,
- racc_goto_check,
- racc_goto_default,
- racc_goto_pointer,
- racc_nt_base,
- racc_reduce_table,
- racc_token_table,
- racc_shift_n,
- racc_reduce_n,
- racc_use_result_var ]
-Ractor.make_shareable(Racc_arg) if defined?(Ractor)
-
-Racc_token_to_s_table = [
- "$end",
- "error",
- "SLASH",
- "LITERAL",
- "SYMBOL",
- "LPAREN",
- "RPAREN",
- "DOT",
- "STAR",
- "OR",
- "$start",
- "expressions",
- "expression",
- "or",
- "terminal",
- "group",
- "star",
- "symbol",
- "literal",
- "slash",
- "dot" ]
-Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)
-
-Racc_debug_parser = false
-
-##### State transition tables end #####
-
-# reduce 0 omitted
-
-module_eval(<<'.,.,', 'journey.y', 6)
- def _reduce_1(val, _values, result)
- result = Cat.new(val.first, val.last)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'journey.y', 7)
- def _reduce_2(val, _values, result)
- result = val.first
- result
- end
-.,.,
-
-# reduce 3 omitted
-
-# reduce 4 omitted
-
-# reduce 5 omitted
-
-# reduce 6 omitted
-
-module_eval(<<'.,.,', 'journey.y', 16)
- def _reduce_7(val, _values, result)
- result = Group.new(val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'journey.y', 19)
- def _reduce_8(val, _values, result)
- result = Or.new([val.first, val.last])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'journey.y', 22)
- def _reduce_9(val, _values, result)
- result = Star.new(Symbol.new(val.last))
- result
- end
-.,.,
-
-# reduce 10 omitted
-
-# reduce 11 omitted
-
-# reduce 12 omitted
-
-# reduce 13 omitted
-
-module_eval(<<'.,.,', 'journey.y', 31)
- def _reduce_14(val, _values, result)
- result = Slash.new('/')
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'journey.y', 34)
- def _reduce_15(val, _values, result)
- result = Symbol.new(val.first)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'journey.y', 37)
- def _reduce_16(val, _values, result)
- result = Literal.new(val.first)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'journey.y', 39)
- def _reduce_17(val, _values, result)
- result = Dot.new(val.first)
- result
- end
-.,.,
-
-def _reduce_none(val, _values, result)
- val[0]
-end
-
- end # class Parser
-end # module Journey
diff --git a/test/racc/regress/liquor b/test/racc/regress/liquor
deleted file mode 100644
index dec215fdf4..0000000000
--- a/test/racc/regress/liquor
+++ /dev/null
@@ -1,887 +0,0 @@
-#
-# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.5.2
-# from Racc grammar file "".
-#
-
-require 'racc/parser.rb'
-module Liquor
- class Parser < Racc::Parser
-
-module_eval(<<'...end liquor.y/module_eval...', 'liquor.y', 216)
- attr_reader :errors, :ast
-
- def initialize(tags={})
- super()
-
- @errors = []
- @ast = nil
- @tags = tags
- end
-
- def success?
- @errors.empty?
- end
-
- def parse(string, name='(code)')
- @errors.clear
- @name = name
- @ast = nil
-
- begin
- @stream = Lexer.lex(string, @name, @tags)
- @ast = do_parse
- rescue Liquor::SyntaxError => e
- @errors << e
- end
-
- success?
- end
-
- def next_token
- tok = @stream.shift
- [ tok[0], tok ] if tok
- end
-
- TOKEN_NAME_MAP = {
- :comma => ',',
- :dot => '.',
- :lblock => '{%',
- :rblock => '%}',
- :linterp => '{{',
- :rinterp => '}}',
- :lbracket => '[',
- :rbracket => ']',
- :lparen => '(',
- :rparen => ')',
- :pipe => '|',
- :op_not => '!',
- :op_mul => '*',
- :op_div => '/',
- :op_mod => '%',
- :op_plus => '+',
- :op_minus => '-',
- :op_eq => '==',
- :op_neq => '!=',
- :op_lt => '<',
- :op_leq => '<=',
- :op_gt => '>',
- :op_geq => '>=',
- :keyword => 'keyword argument name',
- :kwarg => 'keyword argument',
- :ident => 'identifier',
- }
-
- def on_error(error_token_id, error_token, value_stack)
- if token_to_str(error_token_id) == "$end"
- raise Liquor::SyntaxError.new("unexpected end of program", {
- file: @name
- })
- else
- type, (loc, value) = error_token
- type = TOKEN_NAME_MAP[type] || type
-
- raise Liquor::SyntaxError.new("unexpected token `#{type}'", loc)
- end
- end
-
- def retag(nodes)
- loc = nodes.map { |node| node[1] }.compact
- first, *, last = loc
- return first if last.nil?
-
- {
- file: first[:file],
- line: first[:line],
- start: first[:start],
- end: last[:end],
- }
- end
-
- def reduce_tag_args(list)
- list.each_slice(2).reduce([]) { |args, (k, v)|
- if v[0] == :block
- args << [ :blockarg, retag([ k, v ]), k, v[2] || [] ]
- else
- args << [ :kwarg, retag([ k, v ]), k, v ]
- end
- }
- end
-...end liquor.y/module_eval...
-##### State transition tables begin ###
-
-racc_action_table = [
- 76, 26, 26, 6, 7, 22, 5, 6, 25, 25,
- 5, 28, 32, 36, 37, 34, 35, 31, 29, 27,
- 33, 2, 30, 26, 26, 2, 6, 23, 41, 5,
- 25, 25, 38, 39, 28, 32, 36, 37, 34, 35,
- 31, 29, 27, 33, 2, 30, 40, 26, 96, 6,
- 24, 97, 5, 43, 25, 38, 39, 28, 32, 36,
- 37, 34, 35, 31, 29, 27, 33, 2, 30, 26,
- 54, 70, 77, 26, 75, 26, 25, 52, 38, 39,
- 25, 43, 25, 28, 32, 36, 37, 34, 35, 31,
- 29, 27, 33, 26, 30, 84, 26, 51, 6, 96,
- 25, 5, 97, 25, 38, 39, 28, 32, 36, 37,
- 34, 35, 31, 29, 27, 33, 2, 30, 74, 26,
- 87, 82, 96, 74, 70, 97, 25, 38, 39, 28,
- 32, 36, 37, 34, 35, 31, 29, 27, 33, 94,
- 30, 98, 26, 107, 6, 111, 52, 5, nil, 25,
- 38, 39, 28, 32, 36, 37, 34, 35, 31, 29,
- 27, 33, 2, 30, nil, 26, 51, 6, nil, 74,
- 5, nil, 25, 38, 39, 28, 32, 36, 37, 34,
- 35, 31, 29, 27, 33, 2, 30, nil, 26, nil,
- nil, nil, 102, nil, nil, 25, 38, 39, 28, 32,
- 36, 37, 34, 35, 31, 29, 27, 33, nil, 30,
- nil, 26, 96, nil, nil, 97, nil, nil, 25, 38,
- 39, 28, 32, 36, 37, 34, 35, 31, 29, 27,
- 33, nil, 30, nil, 26, nil, nil, nil, nil, nil,
- nil, 25, 38, 39, 28, 32, 36, 37, 34, 35,
- 31, 29, 27, 33, nil, 30, 13, 15, nil, 13,
- 15, 21, nil, 14, 21, 38, 14, nil, nil, nil,
- 18, nil, nil, 18, 19, nil, nil, 19, nil, 13,
- 15, nil, 16, nil, 21, 16, 14, nil, nil, 13,
- 15, nil, nil, 18, 21, nil, 14, 19, nil, 13,
- 15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
- 15, 52, nil, 18, 21, 16, 14, 19, nil, 13,
- 15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
- 15, 51, nil, 18, 21, 16, 14, 19, nil, 13,
- 15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
- 15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
- 15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
- 15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
- 15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
- 15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
- 15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
- 15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
- 15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
- 15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
- 15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
- 15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
- 15, 74, nil, 18, 21, 16, 14, 19, nil, 13,
- 15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
- 15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
- 15, 81, nil, 18, 21, 16, 14, 19, nil, 13,
- 15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
- 15, nil, 26, 18, 21, 16, 14, 19, nil, 25,
- nil, 101, 28, 18, nil, 16, nil, 19, 31, 29,
- 27, 106, 26, 30, nil, 16, nil, nil, nil, 25,
- nil, nil, 28, nil, 26, nil, nil, nil, 31, 29,
- 27, 25, nil, 30, 28, nil, 26, nil, nil, nil,
- 31, 29, 27, 25, nil, 30, 28, nil, 26, nil,
- nil, nil, 31, 29, 27, 25, nil, 30, 28, nil,
- 26, nil, nil, nil, 31, 29, 27, 25, nil, 30,
- 28, nil, 26, nil, nil, nil, 31, 29, 27, 25,
- nil, 30, 28, 32, 36, 37, 34, 35, 31, 29,
- 27, 33, 26, 30, 26, nil, nil, nil, nil, 25,
- nil, 25, 28, nil, 28, nil, nil, nil, nil, 29,
- 27, 29, 27 ]
-
-racc_action_check = [
- 47, 47, 45, 0, 1, 6, 0, 2, 47, 45,
- 2, 47, 47, 47, 47, 47, 47, 47, 47, 47,
- 47, 0, 47, 46, 11, 2, 3, 7, 12, 3,
- 46, 11, 47, 47, 11, 11, 11, 11, 11, 11,
- 11, 11, 11, 11, 3, 11, 11, 44, 94, 4,
- 11, 94, 4, 13, 44, 11, 11, 44, 44, 44,
- 44, 44, 44, 44, 44, 44, 44, 4, 44, 55,
- 26, 40, 48, 49, 44, 56, 55, 49, 44, 44,
- 49, 54, 56, 49, 49, 49, 49, 49, 49, 49,
- 49, 49, 49, 57, 49, 69, 53, 49, 81, 107,
- 57, 81, 107, 53, 49, 49, 53, 53, 53, 53,
- 53, 53, 53, 53, 53, 53, 81, 53, 70, 71,
- 72, 53, 111, 71, 84, 111, 71, 53, 53, 71,
- 71, 71, 71, 71, 71, 71, 71, 71, 71, 91,
- 71, 96, 79, 103, 101, 109, 79, 101, nil, 79,
- 71, 71, 79, 79, 79, 79, 79, 79, 79, 79,
- 79, 79, 101, 79, nil, 88, 79, 106, nil, 88,
- 106, nil, 88, 79, 79, 88, 88, 88, 88, 88,
- 88, 88, 88, 88, 88, 106, 88, nil, 99, nil,
- nil, nil, 99, nil, nil, 99, 88, 88, 99, 99,
- 99, 99, 99, 99, 99, 99, 99, 99, nil, 99,
- nil, 104, 104, nil, nil, 104, nil, nil, 104, 99,
- 99, 104, 104, 104, 104, 104, 104, 104, 104, 104,
- 104, nil, 104, nil, 67, nil, nil, nil, nil, nil,
- nil, 67, 104, 104, 67, 67, 67, 67, 67, 67,
- 67, 67, 67, 67, nil, 67, 5, 5, nil, 14,
- 14, 5, nil, 5, 14, 67, 14, nil, nil, nil,
- 5, nil, nil, 14, 5, nil, nil, 14, nil, 18,
- 18, nil, 5, nil, 18, 14, 18, nil, nil, 19,
- 19, nil, nil, 18, 19, nil, 19, 18, nil, 21,
- 21, nil, nil, 19, 21, 18, 21, 19, nil, 22,
- 22, 22, nil, 21, 22, 19, 22, 21, nil, 25,
- 25, nil, nil, 22, 25, 21, 25, 22, nil, 27,
- 27, 22, nil, 25, 27, 22, 27, 25, nil, 28,
- 28, nil, nil, 27, 28, 25, 28, 27, nil, 29,
- 29, nil, nil, 28, 29, 27, 29, 28, nil, 30,
- 30, nil, nil, 29, 30, 28, 30, 29, nil, 31,
- 31, nil, nil, 30, 31, 29, 31, 30, nil, 32,
- 32, nil, nil, 31, 32, 30, 32, 31, nil, 33,
- 33, nil, nil, 32, 33, 31, 33, 32, nil, 34,
- 34, nil, nil, 33, 34, 32, 34, 33, nil, 35,
- 35, nil, nil, 34, 35, 33, 35, 34, nil, 36,
- 36, nil, nil, 35, 36, 34, 36, 35, nil, 37,
- 37, nil, nil, 36, 37, 35, 37, 36, nil, 38,
- 38, nil, nil, 37, 38, 36, 38, 37, nil, 39,
- 39, nil, nil, 38, 39, 37, 39, 38, nil, 43,
- 43, 43, nil, 39, 43, 38, 43, 39, nil, 52,
- 52, nil, nil, 43, 52, 39, 52, 43, nil, 74,
- 74, nil, nil, 52, 74, 43, 74, 52, nil, 76,
- 76, 52, nil, 74, 76, 52, 76, 74, nil, 97,
- 97, nil, nil, 76, 97, 74, 97, 76, nil, 102,
- 102, nil, 60, 97, 102, 76, 102, 97, nil, 60,
- nil, 97, 60, 102, nil, 97, nil, 102, 60, 60,
- 60, 102, 61, 60, nil, 102, nil, nil, nil, 61,
- nil, nil, 61, nil, 62, nil, nil, nil, 61, 61,
- 61, 62, nil, 61, 62, nil, 63, nil, nil, nil,
- 62, 62, 62, 63, nil, 62, 63, nil, 64, nil,
- nil, nil, 63, 63, 63, 64, nil, 63, 64, nil,
- 65, nil, nil, nil, 64, 64, 64, 65, nil, 64,
- 65, nil, 66, nil, nil, nil, 65, 65, 65, 66,
- nil, 65, 66, 66, 66, 66, 66, 66, 66, 66,
- 66, 66, 58, 66, 59, nil, nil, nil, nil, 58,
- nil, 59, 58, nil, 59, nil, nil, nil, nil, 58,
- 58, 59, 59 ]
-
-racc_action_pointer = [
- -5, 4, -1, 18, 41, 251, 0, 27, nil, nil,
- nil, 21, -1, 41, 254, nil, nil, nil, 274, 284,
- nil, 294, 304, nil, nil, 314, 65, 324, 334, 344,
- 354, 364, 374, 384, 394, 404, 414, 424, 434, 444,
- 66, nil, nil, 454, 44, -1, 20, -2, 44, 70,
- nil, nil, 464, 93, 69, 66, 72, 90, 609, 611,
- 509, 529, 541, 553, 565, 577, 589, 231, nil, 70,
- 111, 116, 90, nil, 474, nil, 484, nil, nil, 139,
- nil, 90, nil, nil, 119, nil, nil, nil, 162, nil,
- nil, 130, nil, nil, 44, nil, 114, 494, nil, 185,
- nil, 136, 504, 134, 208, nil, 159, 95, nil, 136,
- nil, 118, nil ]
-
-racc_action_default = [
- -1, -57, -1, -1, -1, -57, -57, -57, -2, -3,
- -4, -57, -57, -7, -57, -9, -10, -11, -57, -57,
- -31, -35, -57, 113, -5, -57, -57, -57, -57, -57,
- -57, -57, -57, -57, -57, -57, -57, -57, -57, -57,
- -57, -6, -12, -40, -57, -16, -17, -34, -57, -57,
- -46, -47, -57, -57, -15, -18, -19, -20, -21, -22,
- -23, -24, -25, -26, -27, -28, -29, -30, -41, -43,
- -40, -40, -57, -38, -57, -8, -35, -32, -45, -57,
- -48, -1, -13, -14, -57, -44, -37, -36, -40, -33,
- -50, -57, -42, -39, -57, -49, -57, -57, -51, -57,
- -52, -1, -57, -57, -57, -54, -1, -57, -56, -57,
- -53, -57, -55 ]
-
-racc_goto_table = [
- 1, 11, 8, 9, 10, 48, 68, 12, 42, 50,
- 44, 72, 80, 73, 45, 46, 100, 105, 49, nil,
- nil, 53, nil, 55, 56, 57, 58, 59, 60, 61,
- 62, 63, 64, 65, 66, 67, 78, nil, nil, 71,
- 85, 86, 95, nil, nil, nil, nil, nil, 79, 83,
- 92, nil, 108, nil, nil, 110, nil, nil, 93, 112,
- 89, nil, nil, nil, nil, nil, 90, nil, nil, nil,
- 88, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 91, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 99, nil, nil, nil, nil, 104, nil,
- nil, 103, nil, nil, nil, nil, 109 ]
-
-racc_goto_check = [
- 1, 4, 1, 1, 1, 9, 12, 5, 8, 14,
- 4, 10, 15, 11, 4, 4, 17, 18, 4, nil,
- nil, 4, nil, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 14, nil, nil, 4,
- 11, 11, 16, nil, nil, nil, nil, nil, 4, 8,
- 12, nil, 16, nil, nil, 16, nil, nil, 11, 16,
- 9, nil, nil, nil, nil, nil, 14, nil, nil, nil,
- 4, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 1, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 4, nil, nil, nil, nil, 4, nil,
- nil, 1, nil, nil, nil, nil, 1 ]
-
-racc_goto_pointer = [
- nil, 0, nil, nil, -4, 2, nil, nil, -5, -16,
- -32, -30, -34, nil, -13, -40, -52, -81, -85 ]
-
-racc_goto_default = [
- nil, nil, 3, 4, 47, nil, 20, 17, nil, nil,
- nil, nil, nil, 69, nil, nil, nil, nil, nil ]
-
-racc_reduce_table = [
- 0, 0, :racc_error,
- 0, 37, :_reduce_1,
- 2, 37, :_reduce_2,
- 2, 37, :_reduce_3,
- 2, 37, :_reduce_4,
- 3, 38, :_reduce_5,
- 3, 38, :_reduce_6,
- 1, 42, :_reduce_none,
- 3, 42, :_reduce_8,
- 1, 40, :_reduce_none,
- 1, 40, :_reduce_none,
- 1, 40, :_reduce_none,
- 2, 40, :_reduce_12,
- 4, 40, :_reduce_13,
- 4, 40, :_reduce_14,
- 3, 40, :_reduce_15,
- 2, 40, :_reduce_16,
- 2, 40, :_reduce_17,
- 3, 40, :_reduce_18,
- 3, 40, :_reduce_19,
- 3, 40, :_reduce_20,
- 3, 40, :_reduce_21,
- 3, 40, :_reduce_22,
- 3, 40, :_reduce_23,
- 3, 40, :_reduce_24,
- 3, 40, :_reduce_25,
- 3, 40, :_reduce_26,
- 3, 40, :_reduce_27,
- 3, 40, :_reduce_28,
- 3, 40, :_reduce_29,
- 3, 40, :_reduce_30,
- 1, 40, :_reduce_none,
- 3, 43, :_reduce_32,
- 3, 45, :_reduce_33,
- 1, 45, :_reduce_34,
- 0, 45, :_reduce_35,
- 3, 44, :_reduce_36,
- 2, 46, :_reduce_37,
- 1, 46, :_reduce_38,
- 3, 47, :_reduce_39,
- 0, 47, :_reduce_40,
- 3, 41, :_reduce_41,
- 3, 48, :_reduce_42,
- 1, 48, :_reduce_43,
- 2, 49, :_reduce_44,
- 4, 39, :_reduce_45,
- 3, 39, :_reduce_46,
- 1, 50, :_reduce_47,
- 2, 50, :_reduce_48,
- 4, 51, :_reduce_49,
- 2, 51, :_reduce_50,
- 2, 52, :_reduce_51,
- 2, 52, :_reduce_52,
- 4, 53, :_reduce_53,
- 3, 53, :_reduce_54,
- 4, 54, :_reduce_55,
- 2, 54, :_reduce_56 ]
-
-racc_reduce_n = 57
-
-racc_shift_n = 113
-
-racc_token_table = {
- false => 0,
- :error => 1,
- :comma => 2,
- :dot => 3,
- :endtag => 4,
- :ident => 5,
- :integer => 6,
- :keyword => 7,
- :lblock => 8,
- :lblock2 => 9,
- :lbracket => 10,
- :linterp => 11,
- :lparen => 12,
- :op_div => 13,
- :op_eq => 14,
- :op_gt => 15,
- :op_geq => 16,
- :op_lt => 17,
- :op_leq => 18,
- :op_minus => 19,
- :op_mod => 20,
- :op_mul => 21,
- :op_neq => 22,
- :op_not => 23,
- :op_plus => 24,
- :pipe => 25,
- :plaintext => 26,
- :rblock => 27,
- :rbracket => 28,
- :rinterp => 29,
- :rparen => 30,
- :string => 31,
- :tag_ident => 32,
- :op_uminus => 33,
- :op_and => 34,
- :op_or => 35 }
-
-racc_nt_base = 36
-
-racc_use_result_var = true
-
-Racc_arg = [
- racc_action_table,
- racc_action_check,
- racc_action_default,
- racc_action_pointer,
- racc_goto_table,
- racc_goto_check,
- racc_goto_default,
- racc_goto_pointer,
- racc_nt_base,
- racc_reduce_table,
- racc_token_table,
- racc_shift_n,
- racc_reduce_n,
- racc_use_result_var ]
-Ractor.make_shareable(Racc_arg) if defined?(Ractor)
-
-Racc_token_to_s_table = [
- "$end",
- "error",
- "comma",
- "dot",
- "endtag",
- "ident",
- "integer",
- "keyword",
- "lblock",
- "lblock2",
- "lbracket",
- "linterp",
- "lparen",
- "op_div",
- "op_eq",
- "op_gt",
- "op_geq",
- "op_lt",
- "op_leq",
- "op_minus",
- "op_mod",
- "op_mul",
- "op_neq",
- "op_not",
- "op_plus",
- "pipe",
- "plaintext",
- "rblock",
- "rbracket",
- "rinterp",
- "rparen",
- "string",
- "tag_ident",
- "op_uminus",
- "op_and",
- "op_or",
- "$start",
- "block",
- "interp",
- "tag",
- "expr",
- "filter_chain",
- "primary_expr",
- "tuple",
- "function_args",
- "tuple_content",
- "function_args_inside",
- "function_keywords",
- "filter_chain_cont",
- "filter_call",
- "tag_first_cont",
- "tag_first_cont2",
- "tag_next_cont",
- "tag_next_cont2",
- "tag_next_cont3" ]
-Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)
-
-Racc_debug_parser = false
-
-##### State transition tables end #####
-
-# reduce 0 omitted
-
-module_eval(<<'.,.,', 'liquor.y', 47)
- def _reduce_1(val, _values, result)
- result = []
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 49)
- def _reduce_2(val, _values, result)
- result = [ val[0], *val[1] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 51)
- def _reduce_3(val, _values, result)
- result = [ val[0], *val[1] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 53)
- def _reduce_4(val, _values, result)
- result = [ val[0], *val[1] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 57)
- def _reduce_5(val, _values, result)
- result = [ :interp, retag(val), val[1] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 59)
- def _reduce_6(val, _values, result)
- result = [ :interp, retag(val), val[1] ]
- result
- end
-.,.,
-
-# reduce 7 omitted
-
-module_eval(<<'.,.,', 'liquor.y', 64)
- def _reduce_8(val, _values, result)
- result = [ val[1][0], retag(val), *val[1][2..-1] ]
- result
- end
-.,.,
-
-# reduce 9 omitted
-
-# reduce 10 omitted
-
-# reduce 11 omitted
-
-module_eval(<<'.,.,', 'liquor.y', 71)
- def _reduce_12(val, _values, result)
- result = [ :call, retag(val), val[0], val[1] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 73)
- def _reduce_13(val, _values, result)
- result = [ :index, retag(val), val[0], val[2] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 75)
- def _reduce_14(val, _values, result)
- result = [ :external, retag(val), val[0], val[2], val[3] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 77)
- def _reduce_15(val, _values, result)
- result = [ :external, retag(val), val[0], val[2], nil ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 79)
- def _reduce_16(val, _values, result)
- result = [ :uminus, retag(val), val[1] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 81)
- def _reduce_17(val, _values, result)
- result = [ :not, retag(val), val[1] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 83)
- def _reduce_18(val, _values, result)
- result = [ :mul, retag(val), val[0], val[2] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 85)
- def _reduce_19(val, _values, result)
- result = [ :div, retag(val), val[0], val[2] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 87)
- def _reduce_20(val, _values, result)
- result = [ :mod, retag(val), val[0], val[2] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 89)
- def _reduce_21(val, _values, result)
- result = [ :plus, retag(val), val[0], val[2] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 91)
- def _reduce_22(val, _values, result)
- result = [ :minus, retag(val), val[0], val[2] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 93)
- def _reduce_23(val, _values, result)
- result = [ :eq, retag(val), val[0], val[2] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 95)
- def _reduce_24(val, _values, result)
- result = [ :neq, retag(val), val[0], val[2] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 97)
- def _reduce_25(val, _values, result)
- result = [ :lt, retag(val), val[0], val[2] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 99)
- def _reduce_26(val, _values, result)
- result = [ :leq, retag(val), val[0], val[2] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 101)
- def _reduce_27(val, _values, result)
- result = [ :gt, retag(val), val[0], val[2] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 103)
- def _reduce_28(val, _values, result)
- result = [ :geq, retag(val), val[0], val[2] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 105)
- def _reduce_29(val, _values, result)
- result = [ :and, retag(val), val[0], val[2] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 107)
- def _reduce_30(val, _values, result)
- result = [ :or, retag(val), val[0], val[2] ]
- result
- end
-.,.,
-
-# reduce 31 omitted
-
-module_eval(<<'.,.,', 'liquor.y', 112)
- def _reduce_32(val, _values, result)
- result = [ :tuple, retag(val), val[1].compact ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 116)
- def _reduce_33(val, _values, result)
- result = [ val[0], *val[2] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 118)
- def _reduce_34(val, _values, result)
- result = [ val[0] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 120)
- def _reduce_35(val, _values, result)
- result = [ ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 124)
- def _reduce_36(val, _values, result)
- result = [ :args, retag(val), *val[1] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 128)
- def _reduce_37(val, _values, result)
- result = [ val[0], val[1][2] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 130)
- def _reduce_38(val, _values, result)
- result = [ nil, val[0][2] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 134)
- def _reduce_39(val, _values, result)
- name = val[0][2].to_sym
- tail = val[2][2]
- loc = retag([ val[0], val[1] ])
-
- if tail.include? name
- @errors << SyntaxError.new("duplicate keyword argument `#{val[0][2]}'",
- tail[name][1])
- end
-
- hash = {
- name => [ val[1][0], loc, *val[1][2..-1] ]
- }.merge(tail)
-
- result = [ :keywords, retag([ loc, val[2] ]), hash ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 150)
- def _reduce_40(val, _values, result)
- result = [ :keywords, nil, {} ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 154)
- def _reduce_41(val, _values, result)
- result = [ val[0], *val[2] ].
- reduce { |tree, node| node[3][2] = tree; node }
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 160)
- def _reduce_42(val, _values, result)
- result = [ val[0], *val[2] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 162)
- def _reduce_43(val, _values, result)
- result = [ val[0] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 166)
- def _reduce_44(val, _values, result)
- ident_loc = val[0][1]
- empty_args_loc = { line: ident_loc[:line],
- start: ident_loc[:end] + 1,
- end: ident_loc[:end] + 1, }
- result = [ :call, val[0][1], val[0],
- [ :args, val[1][1] || empty_args_loc, nil, val[1][2] ] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 176)
- def _reduce_45(val, _values, result)
- result = [ :tag, retag(val), val[1], val[2], *reduce_tag_args(val[3][2]) ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 178)
- def _reduce_46(val, _values, result)
- result = [ :tag, retag(val), val[1], nil, *reduce_tag_args(val[2][2]) ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 186)
- def _reduce_47(val, _values, result)
- result = [ :cont, retag(val), [] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 188)
- def _reduce_48(val, _values, result)
- result = [ :cont, retag(val), [ val[0], *val[1][2] ] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 192)
- def _reduce_49(val, _values, result)
- result = [ :cont2, val[0][1], [ [:block, val[0][1], val[1] ], *val[3] ] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 194)
- def _reduce_50(val, _values, result)
- result = [ :cont2, retag(val), [ val[0], *val[1][2] ] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 198)
- def _reduce_51(val, _values, result)
- result = []
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 200)
- def _reduce_52(val, _values, result)
- result = [ val[0], *val[1] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 204)
- def _reduce_53(val, _values, result)
- result = [ [:block, val[0][1], val[1] ], *val[3] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 206)
- def _reduce_54(val, _values, result)
- result = [ val[0], val[1], *val[2] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 210)
- def _reduce_55(val, _values, result)
- result = [ [:block, val[0][1], val[1] ], *val[3] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'liquor.y', 212)
- def _reduce_56(val, _values, result)
- result = [ val[0], *val[1] ]
- result
- end
-.,.,
-
-def _reduce_none(val, _values, result)
- val[0]
-end
-
- end # class Parser
-end # module Liquor
diff --git a/test/racc/regress/machete b/test/racc/regress/machete
deleted file mode 100644
index 2659a3ec9e..0000000000
--- a/test/racc/regress/machete
+++ /dev/null
@@ -1,835 +0,0 @@
-#
-# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.5.2
-# from Racc grammar file "".
-#
-
-require 'racc/parser.rb'
-module Machete
- class Parser < Racc::Parser
-
-module_eval(<<'...end machete.y/module_eval...', 'machete.y', 175)
-
-include Matchers
-
-class SyntaxError < StandardError; end
-
-def parse(input)
- @input = input
- @pos = 0
-
- do_parse
-end
-
-private
-
-def integer_value(value)
- if value =~ /^0[bB]/
- value[2..-1].to_i(2)
- elsif value =~ /^0[oO]/
- value[2..-1].to_i(8)
- elsif value =~ /^0[dD]/
- value[2..-1].to_i(10)
- elsif value =~ /^0[xX]/
- value[2..-1].to_i(16)
- elsif value =~ /^0/
- value.to_i(8)
- else
- value.to_i
- end
-end
-
-def symbol_value(value)
- value[1..-1].to_sym
-end
-
-def string_value(value)
- quote = value[0..0]
- if quote == "'"
- value[1..-2].gsub("\\\\", "\\").gsub("\\'", "'")
- elsif quote == '"'
- value[1..-2].
- gsub("\\\\", "\\").
- gsub('\\"', '"').
- gsub("\\n", "\n").
- gsub("\\t", "\t").
- gsub("\\r", "\r").
- gsub("\\f", "\f").
- gsub("\\v", "\v").
- gsub("\\a", "\a").
- gsub("\\e", "\e").
- gsub("\\b", "\b").
- gsub("\\s", "\s").
- gsub(/\\([0-7]{1,3})/) { $1.to_i(8).chr }.
- gsub(/\\x([0-9a-fA-F]{1,2})/) { $1.to_i(16).chr }
- else
- raise "Unknown quote: #{quote.inspect}."
- end
-end
-
-REGEXP_OPTIONS = {
- 'i' => Regexp::IGNORECASE,
- 'm' => Regexp::MULTILINE,
- 'x' => Regexp::EXTENDED
-}
-
-def regexp_value(value)
- /\A\/(.*)\/([imx]*)\z/ =~ value
- pattern, options = $1, $2
-
- Regexp.new(pattern, options.chars.map { |ch| REGEXP_OPTIONS[ch] }.inject(:|))
-end
-
-# "^" needs to be here because if it were among operators recognized by
-# METHOD_NAME, "^=" would be recognized as two tokens.
-SIMPLE_TOKENS = [
- "|",
- "<",
- ">",
- ",",
- "=",
- "^=",
- "^",
- "$=",
- "[",
- "]",
- "*=",
- "*",
- "+",
- "?",
- "{",
- "}"
-]
-
-COMPLEX_TOKENS = [
- [:NIL, /^nil/],
- [:TRUE, /^true/],
- [:FALSE, /^false/],
- # INTEGER needs to be before METHOD_NAME, otherwise e.g. "+1" would be
- # recognized as two tokens.
- [
- :INTEGER,
- /^
- [+-]? # sign
- (
- 0[bB][01]+(_[01]+)* # binary (prefixed)
- |
- 0[oO][0-7]+(_[0-7]+)* # octal (prefixed)
- |
- 0[dD]\d+(_\d+)* # decimal (prefixed)
- |
- 0[xX][0-9a-fA-F]+(_[0-9a-fA-F]+)* # hexadecimal (prefixed)
- |
- 0[0-7]*(_[0-7]+)* # octal (unprefixed)
- |
- [1-9]\d*(_\d+)* # decimal (unprefixed)
- )
- /x
- ],
- [
- :SYMBOL,
- /^
- :
- (
- # class name
- [A-Z][a-zA-Z0-9_]*
- |
- # regular method name
- [a-z_][a-zA-Z0-9_]*[?!=]?
- |
- # instance variable name
- @[a-zA-Z_][a-zA-Z0-9_]*
- |
- # class variable name
- @@[a-zA-Z_][a-zA-Z0-9_]*
- |
- # operator (sorted by length, then alphabetically)
- (<=>|===|\[\]=|\*\*|\+@|-@|<<|<=|==|=~|>=|>>|\[\]|[%&*+\-\/<>^`|~])
- )
- /x
- ],
- [
- :STRING,
- /^
- (
- ' # sinqle-quoted string
- (
- \\[\\'] # escape
- |
- [^'] # regular character
- )*
- '
- |
- " # double-quoted string
- (
- \\ # escape
- (
- [\\"ntrfvaebs] # one-character escape
- |
- [0-7]{1,3} # octal number escape
- |
- x[0-9a-fA-F]{1,2} # hexadecimal number escape
- )
- |
- [^"] # regular character
- )*
- "
- )
- /x
- ],
- [
- :REGEXP,
- /^
- \/
- (
- \\ # escape
- (
- [\\\/ntrfvaebs\(\)\[\]\{\}\-\.\?\*\+\|\^\$] # one-character escape
- |
- [0-7]{2,3} # octal number escape
- |
- x[0-9a-fA-F]{1,2} # hexadecimal number escape
- )
- |
- [^\/] # regular character
- )*
- \/
- [imx]*
- /x
- ],
- # ANY, EVEN and ODD need to be before METHOD_NAME, otherwise they would be
- # recognized as method names.
- [:ANY, /^any/],
- [:EVEN, /^even/],
- [:ODD, /^odd/],
- # We exclude "*", "+", "<", ">", "^" and "|" from method names since they are
- # lexed as simple tokens. This is because they have also other meanings in
- # Machette patterns beside Ruby method names.
- [
- :METHOD_NAME,
- /^
- (
- # regular name
- [a-z_][a-zA-Z0-9_]*[?!=]?
- |
- # operator (sorted by length, then alphabetically)
- (<=>|===|\[\]=|\*\*|\+@|-@|<<|<=|==|=~|>=|>>|\[\]|[%&\-\/`~])
- )
- /x
- ],
- [:CLASS_NAME, /^[A-Z][a-zA-Z0-9_]*/]
-]
-
-def next_token
- skip_whitespace
-
- return false if remaining_input.empty?
-
- # Complex tokens need to be before simple tokens, otherwise e.g. "<<" would be
- # recognized as two tokens.
-
- COMPLEX_TOKENS.each do |type, regexp|
- if remaining_input =~ regexp
- @pos += $&.length
- return [type, $&]
- end
- end
-
- SIMPLE_TOKENS.each do |token|
- if remaining_input[0...token.length] == token
- @pos += token.length
- return [token, token]
- end
- end
-
- raise SyntaxError, "Unexpected character: #{remaining_input[0..0].inspect}."
-end
-
-def skip_whitespace
- if remaining_input =~ /\A^[ \t\r\n]+/
- @pos += $&.length
- end
-end
-
-def remaining_input
- @input[@pos..-1]
-end
-
-def on_error(error_token_id, error_value, value_stack)
- raise SyntaxError, "Unexpected token: #{error_value.inspect}."
-end
-...end machete.y/module_eval...
-##### State transition tables begin ###
-
-racc_action_table = [
- 75, 19, 9, 10, 11, 12, 13, 14, 15, 16,
- 66, 67, 68, 7, 24, 9, 10, 11, 12, 13,
- 14, 15, 16, 17, 74, 8, 7, 47, 9, 10,
- 11, 12, 13, 14, 15, 16, 48, 18, 8, 7,
- 71, 9, 10, 11, 12, 13, 14, 15, 16, 72,
- 70, 8, 7, 73, 9, 10, 11, 12, 13, 14,
- 15, 16, 69, 18, 8, 7, 30, 31, 32, 51,
- 52, 53, 54, 33, 34, 35, 29, 8, 41, 38,
- 39, 76, 30, 31, 32, 77, 36, 37, 40, 33,
- 34, 35, 29, nil, 41, 38, 39, 18, 49, 50,
- 62, 63, 36, 37, 40, 43, 44, 55, 64, 65,
- 45, 46, 57, 58, nil, nil, nil, nil, nil, 56 ]
-
-racc_action_check = [
- 70, 7, 0, 0, 0, 0, 0, 0, 0, 0,
- 54, 54, 54, 0, 17, 8, 8, 8, 8, 8,
- 8, 8, 8, 1, 70, 0, 8, 21, 18, 18,
- 18, 18, 18, 18, 18, 18, 22, 1, 8, 18,
- 56, 48, 48, 48, 48, 48, 48, 48, 48, 57,
- 55, 18, 48, 58, 51, 51, 51, 51, 51, 51,
- 51, 51, 55, 61, 48, 51, 19, 19, 19, 28,
- 28, 28, 28, 19, 19, 19, 19, 51, 19, 19,
- 19, 71, 50, 50, 50, 75, 19, 19, 19, 50,
- 50, 50, 50, nil, 50, 50, 50, 20, 26, 26,
- 52, 52, 50, 50, 50, 20, 20, 46, 53, 53,
- 20, 20, 46, 46, nil, nil, nil, nil, nil, 46 ]
-
-racc_action_pointer = [
- 0, 23, nil, nil, nil, nil, nil, -14, 13, nil,
- nil, nil, nil, nil, nil, nil, nil, 14, 26, 64,
- 83, 1, 19, nil, nil, nil, 82, nil, 51, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 102, nil, 39, nil,
- 80, 52, 94, 102, 4, 33, 35, 20, 24, nil,
- nil, 49, nil, nil, nil, nil, nil, nil, nil, nil,
- -5, 52, nil, nil, nil, 56, nil, nil ]
-
-racc_action_default = [
- -56, -56, -1, -3, -4, -5, -6, -7, -33, -48,
- -49, -50, -51, -52, -53, -54, -55, -56, -56, -56,
- -37, -56, -34, -35, 78, -2, -56, -9, -56, -19,
- -20, -21, -22, -23, -24, -25, -26, -27, -28, -29,
- -30, -31, -38, -39, -40, -41, -56, -32, -56, -8,
- -56, -56, -56, -56, -56, -56, -56, -56, -56, -36,
- -10, -11, -12, -15, -13, -16, -14, -17, -18, -42,
- -56, -56, -46, -47, -43, -56, -44, -45 ]
-
-racc_goto_table = [
- 1, 23, 27, 25, 26, 21, 22, 42, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 60, nil, nil, nil, nil, nil, nil,
- nil, 59, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 61 ]
-
-racc_goto_check = [
- 1, 12, 8, 2, 7, 10, 11, 13, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 8, nil, nil, nil, nil, nil, nil,
- nil, 12, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 1 ]
-
-racc_goto_pointer = [
- nil, 0, -15, nil, nil, nil, nil, -15, -17, nil,
- -3, -2, -7, -13 ]
-
-racc_goto_default = [
- nil, 20, 2, 3, 4, 5, 6, nil, nil, 28,
- nil, nil, nil, nil ]
-
-racc_reduce_table = [
- 0, 0, :racc_error,
- 1, 31, :_reduce_none,
- 3, 31, :_reduce_2,
- 1, 32, :_reduce_none,
- 1, 32, :_reduce_none,
- 1, 32, :_reduce_none,
- 1, 32, :_reduce_none,
- 1, 33, :_reduce_7,
- 4, 33, :_reduce_8,
- 1, 37, :_reduce_none,
- 3, 37, :_reduce_10,
- 3, 38, :_reduce_11,
- 3, 38, :_reduce_12,
- 3, 38, :_reduce_13,
- 3, 38, :_reduce_14,
- 3, 38, :_reduce_15,
- 3, 38, :_reduce_16,
- 3, 38, :_reduce_17,
- 3, 38, :_reduce_18,
- 1, 39, :_reduce_none,
- 1, 39, :_reduce_none,
- 1, 39, :_reduce_none,
- 1, 39, :_reduce_none,
- 1, 39, :_reduce_none,
- 1, 39, :_reduce_none,
- 1, 39, :_reduce_none,
- 1, 39, :_reduce_none,
- 1, 39, :_reduce_none,
- 1, 39, :_reduce_none,
- 1, 39, :_reduce_none,
- 1, 39, :_reduce_none,
- 1, 39, :_reduce_none,
- 3, 34, :_reduce_32,
- 0, 40, :_reduce_33,
- 1, 40, :_reduce_none,
- 1, 41, :_reduce_35,
- 3, 41, :_reduce_36,
- 1, 42, :_reduce_none,
- 2, 42, :_reduce_38,
- 1, 43, :_reduce_39,
- 1, 43, :_reduce_40,
- 1, 43, :_reduce_41,
- 3, 43, :_reduce_42,
- 4, 43, :_reduce_43,
- 4, 43, :_reduce_44,
- 5, 43, :_reduce_45,
- 3, 43, :_reduce_46,
- 3, 43, :_reduce_47,
- 1, 35, :_reduce_48,
- 1, 35, :_reduce_49,
- 1, 35, :_reduce_50,
- 1, 35, :_reduce_51,
- 1, 35, :_reduce_52,
- 1, 35, :_reduce_53,
- 1, 35, :_reduce_54,
- 1, 36, :_reduce_55 ]
-
-racc_reduce_n = 56
-
-racc_shift_n = 78
-
-racc_token_table = {
- false => 0,
- :error => 1,
- :NIL => 2,
- :TRUE => 3,
- :FALSE => 4,
- :INTEGER => 5,
- :SYMBOL => 6,
- :STRING => 7,
- :REGEXP => 8,
- :ANY => 9,
- :EVEN => 10,
- :ODD => 11,
- :METHOD_NAME => 12,
- :CLASS_NAME => 13,
- "|" => 14,
- "<" => 15,
- ">" => 16,
- "," => 17,
- "=" => 18,
- "^=" => 19,
- "$=" => 20,
- "*=" => 21,
- "*" => 22,
- "+" => 23,
- "^" => 24,
- "[" => 25,
- "]" => 26,
- "?" => 27,
- "{" => 28,
- "}" => 29 }
-
-racc_nt_base = 30
-
-racc_use_result_var = true
-
-Racc_arg = [
- racc_action_table,
- racc_action_check,
- racc_action_default,
- racc_action_pointer,
- racc_goto_table,
- racc_goto_check,
- racc_goto_default,
- racc_goto_pointer,
- racc_nt_base,
- racc_reduce_table,
- racc_token_table,
- racc_shift_n,
- racc_reduce_n,
- racc_use_result_var ]
-Ractor.make_shareable(Racc_arg) if defined?(Ractor)
-
-Racc_token_to_s_table = [
- "$end",
- "error",
- "NIL",
- "TRUE",
- "FALSE",
- "INTEGER",
- "SYMBOL",
- "STRING",
- "REGEXP",
- "ANY",
- "EVEN",
- "ODD",
- "METHOD_NAME",
- "CLASS_NAME",
- "\"|\"",
- "\"<\"",
- "\">\"",
- "\",\"",
- "\"=\"",
- "\"^=\"",
- "\"$=\"",
- "\"*=\"",
- "\"*\"",
- "\"+\"",
- "\"^\"",
- "\"[\"",
- "\"]\"",
- "\"?\"",
- "\"{\"",
- "\"}\"",
- "$start",
- "expression",
- "primary",
- "node",
- "array",
- "literal",
- "any",
- "attrs",
- "attr",
- "method_name",
- "items_opt",
- "items",
- "item",
- "quantifier" ]
-Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)
-
-Racc_debug_parser = false
-
-##### State transition tables end #####
-
-# reduce 0 omitted
-
-# reduce 1 omitted
-
-module_eval(<<'.,.,', 'machete.y', 44)
- def _reduce_2(val, _values, result)
- result = if val[0].is_a?(ChoiceMatcher)
- ChoiceMatcher.new(val[0].alternatives << val[2])
- else
- ChoiceMatcher.new([val[0], val[2]])
- end
-
- result
- end
-.,.,
-
-# reduce 3 omitted
-
-# reduce 4 omitted
-
-# reduce 5 omitted
-
-# reduce 6 omitted
-
-module_eval(<<'.,.,', 'machete.y', 57)
- def _reduce_7(val, _values, result)
- result = NodeMatcher.new(val[0].to_sym)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 60)
- def _reduce_8(val, _values, result)
- result = NodeMatcher.new(val[0].to_sym, val[2])
-
- result
- end
-.,.,
-
-# reduce 9 omitted
-
-module_eval(<<'.,.,', 'machete.y', 64)
- def _reduce_10(val, _values, result)
- result = val[0].merge(val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 66)
- def _reduce_11(val, _values, result)
- result = { val[0].to_sym => val[2] }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 68)
- def _reduce_12(val, _values, result)
- result = {
- val[0].to_sym => SymbolRegexpMatcher.new(
- Regexp.new("^" + Regexp.escape(symbol_value(val[2]).to_s))
- )
- }
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 75)
- def _reduce_13(val, _values, result)
- result = {
- val[0].to_sym => SymbolRegexpMatcher.new(
- Regexp.new(Regexp.escape(symbol_value(val[2]).to_s) + "$")
- )
- }
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 82)
- def _reduce_14(val, _values, result)
- result = {
- val[0].to_sym => SymbolRegexpMatcher.new(
- Regexp.new(Regexp.escape(symbol_value(val[2]).to_s))
- )
- }
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 89)
- def _reduce_15(val, _values, result)
- result = {
- val[0].to_sym => StringRegexpMatcher.new(
- Regexp.new("^" + Regexp.escape(string_value(val[2])))
- )
- }
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 96)
- def _reduce_16(val, _values, result)
- result = {
- val[0].to_sym => StringRegexpMatcher.new(
- Regexp.new(Regexp.escape(string_value(val[2])) + "$")
- )
- }
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 103)
- def _reduce_17(val, _values, result)
- result = {
- val[0].to_sym => StringRegexpMatcher.new(
- Regexp.new(Regexp.escape(string_value(val[2])))
- )
- }
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 110)
- def _reduce_18(val, _values, result)
- result = {
- val[0].to_sym => IndifferentRegexpMatcher.new(
- Regexp.new(regexp_value(val[2]))
- )
- }
-
- result
- end
-.,.,
-
-# reduce 19 omitted
-
-# reduce 20 omitted
-
-# reduce 21 omitted
-
-# reduce 22 omitted
-
-# reduce 23 omitted
-
-# reduce 24 omitted
-
-# reduce 25 omitted
-
-# reduce 26 omitted
-
-# reduce 27 omitted
-
-# reduce 28 omitted
-
-# reduce 29 omitted
-
-# reduce 30 omitted
-
-# reduce 31 omitted
-
-module_eval(<<'.,.,', 'machete.y', 134)
- def _reduce_32(val, _values, result)
- result = ArrayMatcher.new(val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 136)
- def _reduce_33(val, _values, result)
- result = []
- result
- end
-.,.,
-
-# reduce 34 omitted
-
-module_eval(<<'.,.,', 'machete.y', 139)
- def _reduce_35(val, _values, result)
- result = [val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 140)
- def _reduce_36(val, _values, result)
- result = val[0] << val[2]
- result
- end
-.,.,
-
-# reduce 37 omitted
-
-module_eval(<<'.,.,', 'machete.y', 143)
- def _reduce_38(val, _values, result)
- result = Quantifier.new(val[0], *val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 145)
- def _reduce_39(val, _values, result)
- result = [0, nil, 1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 146)
- def _reduce_40(val, _values, result)
- result = [1, nil, 1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 147)
- def _reduce_41(val, _values, result)
- result = [0, 1, 1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 149)
- def _reduce_42(val, _values, result)
- result = [integer_value(val[1]), integer_value(val[1]), 1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 152)
- def _reduce_43(val, _values, result)
- result = [integer_value(val[1]), nil, 1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 155)
- def _reduce_44(val, _values, result)
- result = [0, integer_value(val[2]), 1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 158)
- def _reduce_45(val, _values, result)
- result = [integer_value(val[1]), integer_value(val[3]), 1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 160)
- def _reduce_46(val, _values, result)
- result = [0, nil, 2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 161)
- def _reduce_47(val, _values, result)
- result = [1, nil, 2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 163)
- def _reduce_48(val, _values, result)
- result = LiteralMatcher.new(nil)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 164)
- def _reduce_49(val, _values, result)
- result = LiteralMatcher.new(true)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 165)
- def _reduce_50(val, _values, result)
- result = LiteralMatcher.new(false)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 166)
- def _reduce_51(val, _values, result)
- result = LiteralMatcher.new(integer_value(val[0]))
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 167)
- def _reduce_52(val, _values, result)
- result = LiteralMatcher.new(symbol_value(val[0]))
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 168)
- def _reduce_53(val, _values, result)
- result = LiteralMatcher.new(string_value(val[0]))
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 169)
- def _reduce_54(val, _values, result)
- result = LiteralMatcher.new(regexp_value(val[0]))
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'machete.y', 171)
- def _reduce_55(val, _values, result)
- result = AnyMatcher.new
- result
- end
-.,.,
-
-def _reduce_none(val, _values, result)
- val[0]
-end
-
- end # class Parser
-end # module Machete
diff --git a/test/racc/regress/mediacloth b/test/racc/regress/mediacloth
deleted file mode 100644
index 2d9958f931..0000000000
--- a/test/racc/regress/mediacloth
+++ /dev/null
@@ -1,1465 +0,0 @@
-#
-# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.5.2
-# from Racc grammar file "".
-#
-
-require 'racc/parser.rb'
-
-require 'mediacloth/mediawikiast'
-
-class MediaWikiParser < Racc::Parser
-
-module_eval(<<'...end mediacloth.y/module_eval...', 'mediacloth.y', 564)
-
-attr_accessor :lexer
-
-def initialize
- @nodes = []
- @context = []
- @wiki_ast_length = 0
- super
-end
-
-#Tokenizes input string and parses it.
-def parse(input)
- @yydebug=true
- lexer.tokenize(input)
- do_parse
- return @nodes.last
-end
-
-#Asks the lexer to return the next token.
-def next_token
- token = @lexer.lex
- if token[0].to_s.upcase.include? "_START"
- @context << token[2..3]
- elsif token[0].to_s.upcase.include? "_END"
- @ast_index = @context.last[0]
- @ast_length = token[2] + token[3] - @context.last[0]
- @context.pop
- else
- @ast_index = token[2]
- @ast_length = token[3]
- end
-
- @wiki_ast_length += token[3]
-
- return token[0..1]
-end
-...end mediacloth.y/module_eval...
-##### State transition tables begin ###
-
-racc_action_table = [
- 22, 28, 50, 29, 61, 13, 63, 132, 15, 86,
- 37, 87, 24, 33, 86, 34, 87, 35, 85, 23,
- 26, 25, 27, 12, 86, 30, 87, 31, 86, 42,
- 87, 32, 44, 86, 48, 87, 57, 17, 57, 22,
- 28, 21, 29, 46, 13, 51, 67, 15, 68, 11,
- 36, 24, 33, 14, 34, 69, 35, 36, 23, 26,
- 25, 27, 12, 86, 30, 87, 31, 75, 73, 70,
- 32, 48, 81, 71, 72, 76, 17, 77, 22, 28,
- 21, 29, 82, 13, 45, 51, 15, -65, 11, 36,
- 24, 33, 14, 34, -65, 35, 63, 23, 26, 25,
- 27, 12, 96, 30, 63, 31, 106, 109, 110, 32,
- 113, 114, 48, 117, 118, 17, 124, 22, 28, 21,
- 29, 57, 13, 127, 128, 15, 131, 11, 36, 24,
- 33, 14, 34, 136, 35, 137, 23, 26, 25, 27,
- 12, 138, 30, 75, 31, 51, 141, -63, 32, 106,
- 106, 150, 153, 51, 17, nil, nil, nil, 21, nil,
- 22, 28, 53, 29, nil, 13, 11, 36, 15, nil,
- 14, nil, 24, 33, nil, 34, nil, 35, nil, 23,
- 26, 25, 27, 12, nil, 30, nil, 31, nil, nil,
- nil, 32, nil, nil, nil, nil, nil, 17, nil, 22,
- 28, 21, 29, 55, 13, nil, nil, 15, nil, 11,
- 36, 24, 33, 14, 34, nil, 35, nil, 23, 26,
- 25, 27, 12, nil, 30, nil, 31, nil, nil, nil,
- 32, nil, nil, nil, nil, nil, 17, nil, 22, 28,
- 21, 29, nil, 13, nil, nil, 15, nil, 11, 36,
- 24, 33, 14, 34, nil, 35, nil, 23, 26, 25,
- 27, 12, nil, 30, nil, 31, nil, nil, nil, 32,
- nil, nil, nil, nil, nil, 17, nil, 22, 28, 21,
- 29, nil, 13, nil, nil, 15, nil, 11, 36, 24,
- 33, 14, 34, nil, 35, nil, 23, 26, 25, 27,
- 12, nil, 30, nil, 31, nil, nil, nil, 32, nil,
- nil, nil, nil, nil, 17, nil, 22, 28, 21, 29,
- nil, 13, nil, nil, 15, nil, 11, 36, 24, 33,
- 14, 34, nil, 35, nil, 23, 26, 25, 27, 12,
- nil, 30, nil, 31, nil, nil, nil, 32, nil, nil,
- nil, nil, nil, 17, nil, 22, 28, 21, 29, nil,
- 13, nil, nil, 15, nil, 11, 36, 24, 33, 14,
- 34, nil, 35, nil, 23, 26, 25, 27, 12, nil,
- 30, nil, 31, nil, nil, nil, 32, nil, nil, nil,
- nil, nil, 17, nil, 22, 28, 21, 29, nil, 13,
- nil, nil, 15, nil, 11, 36, 24, 33, 14, 34,
- nil, 35, nil, 23, 26, 25, 27, 12, nil, 30,
- nil, 31, nil, nil, nil, 32, nil, nil, nil, nil,
- nil, 17, 78, nil, nil, 21, nil, 22, 28, 88,
- 29, nil, 13, 11, 36, 15, nil, 14, nil, 24,
- 33, nil, 34, nil, 35, nil, 23, 26, 25, 27,
- 12, nil, 30, nil, 31, nil, nil, nil, 32, nil,
- nil, nil, nil, nil, 17, nil, 22, 28, 21, 29,
- 89, 13, nil, nil, 15, nil, 11, 36, 24, 33,
- 14, 34, nil, 35, nil, 23, 26, 25, 27, 12,
- nil, 30, nil, 31, nil, nil, nil, 32, nil, nil,
- nil, nil, nil, 17, nil, 22, 28, 21, 29, nil,
- 13, nil, nil, 15, nil, 11, 36, 24, 33, 14,
- 34, nil, 35, nil, 23, 26, 25, 27, 12, nil,
- 30, nil, 31, nil, nil, 93, 32, nil, nil, nil,
- nil, nil, 17, nil, 22, 28, 21, 29, nil, 13,
- nil, nil, 15, nil, 11, 36, 24, 33, 14, 34,
- nil, 35, nil, 23, 26, 25, 27, 12, nil, 30,
- nil, 31, nil, nil, nil, 32, nil, nil, 98, nil,
- nil, 17, nil, 22, 28, 21, 29, nil, 13, nil,
- nil, 15, nil, 11, 36, 24, 33, 14, 34, nil,
- 35, nil, 23, 26, 25, 27, 12, nil, 30, nil,
- 31, nil, nil, nil, 32, nil, nil, nil, nil, 101,
- 17, nil, nil, nil, 21, nil, 22, 28, nil, 29,
- nil, 13, 11, 36, 15, nil, 14, nil, 24, 33,
- 102, 34, nil, 35, nil, 23, 26, 25, 27, 12,
- nil, 30, nil, 31, nil, nil, nil, 32, nil, nil,
- nil, nil, nil, 17, nil, 22, 28, 21, 29, nil,
- 13, nil, nil, 15, nil, 11, 36, 24, 33, 14,
- 34, 103, 35, nil, 23, 26, 25, 27, 12, nil,
- 30, nil, 31, nil, nil, nil, 32, nil, nil, nil,
- nil, nil, 17, nil, 22, 28, 21, 29, nil, 13,
- nil, nil, 15, nil, 11, 36, 24, 33, 14, 34,
- nil, 35, 104, 23, 26, 25, 27, 12, nil, 30,
- nil, 31, nil, nil, nil, 32, nil, nil, nil, nil,
- nil, 17, nil, 22, 28, 21, 29, nil, 13, nil,
- nil, 15, nil, 11, 36, 24, 33, 14, 34, nil,
- 35, nil, 23, 26, 25, 27, 12, nil, 30, nil,
- 31, nil, nil, nil, 32, nil, nil, nil, nil, nil,
- 17, nil, 22, 28, 21, 29, nil, 13, nil, nil,
- 15, nil, 11, 36, 24, 33, 14, 34, nil, 35,
- nil, 23, 26, 25, 27, 12, nil, 30, nil, 31,
- nil, nil, nil, 32, nil, nil, nil, nil, nil, 17,
- nil, 22, 28, 21, 29, nil, 13, nil, nil, 15,
- nil, 11, 36, 24, 33, 14, 34, nil, 35, nil,
- 23, 26, 25, 27, 12, nil, 30, nil, 31, nil,
- nil, nil, 32, nil, nil, nil, nil, nil, 17, 115,
- 22, 28, 21, 29, nil, 13, nil, nil, 15, nil,
- 11, 36, 24, 33, 14, 34, nil, 35, nil, 23,
- 26, 25, 27, 12, nil, 30, nil, 31, nil, nil,
- nil, 32, nil, nil, nil, nil, nil, 17, nil, 22,
- 28, 21, 29, nil, 13, nil, 121, 15, nil, 11,
- 36, 24, 33, 14, 34, nil, 35, nil, 23, 26,
- 25, 27, 12, nil, 30, nil, 31, nil, nil, nil,
- 32, nil, nil, nil, nil, nil, 17, nil, 22, 28,
- 21, 29, nil, 13, nil, nil, 15, 123, 11, 36,
- 24, 33, 14, 34, nil, 35, nil, 23, 26, 25,
- 27, 12, nil, 30, nil, 31, nil, nil, 126, 32,
- nil, nil, nil, nil, nil, 17, nil, 22, 28, 21,
- 29, nil, 13, nil, nil, 15, nil, 11, 36, 24,
- 33, 14, 34, nil, 35, nil, 23, 26, 25, 27,
- 12, nil, 30, nil, 31, nil, nil, nil, 32, nil,
- nil, 129, nil, nil, 17, nil, 22, 28, 21, 29,
- nil, 13, nil, nil, 15, nil, 11, 36, 24, 33,
- 14, 34, nil, 35, nil, 23, 26, 25, 27, 12,
- nil, 30, nil, 31, nil, nil, nil, 32, nil, nil,
- nil, nil, 130, 17, nil, 22, 28, 21, 29, nil,
- 13, nil, 134, 15, nil, 11, 36, 24, 33, 14,
- 34, nil, 35, nil, 23, 26, 25, 27, 12, nil,
- 30, nil, 31, nil, nil, nil, 32, nil, nil, nil,
- nil, nil, 17, nil, 22, 28, 21, 29, nil, 13,
- nil, nil, 15, nil, 11, 36, 24, 33, 14, 34,
- nil, 35, nil, 23, 26, 25, 27, 12, nil, 30,
- nil, 31, nil, nil, nil, 32, nil, nil, nil, nil,
- nil, 17, nil, 22, 28, 21, 29, nil, 13, nil,
- nil, 15, nil, 11, 36, 24, 33, 14, 34, nil,
- 35, nil, 23, 26, 25, 27, 12, nil, 30, nil,
- 31, nil, nil, nil, 32, nil, nil, nil, nil, nil,
- 17, nil, 22, 28, 21, 29, nil, 13, nil, 142,
- 15, nil, 11, 36, 24, 33, 14, 34, nil, 35,
- nil, 23, 26, 25, 27, 12, nil, 30, nil, 31,
- nil, nil, nil, 32, nil, nil, nil, nil, nil, 17,
- nil, 22, 28, 21, 29, nil, 13, nil, nil, 15,
- 144, 11, 36, 24, 33, 14, 34, nil, 35, nil,
- 23, 26, 25, 27, 12, nil, 30, nil, 31, nil,
- nil, nil, 32, nil, nil, nil, nil, nil, 17, nil,
- nil, nil, 21, nil, nil, nil, nil, nil, nil, nil,
- 11, 36, 22, 28, 14, 29, nil, 13, nil, nil,
- 15, nil, 136, nil, 24, 33, nil, 34, nil, 35,
- nil, 23, 26, 25, 27, 12, nil, 30, nil, 31,
- nil, nil, nil, 32, nil, nil, nil, nil, nil, 17,
- nil, nil, nil, 21, nil, nil, nil, nil, nil, nil,
- nil, 11, 36, 22, 28, 14, 29, nil, 13, nil,
- nil, 15, nil, 136, nil, 24, 33, nil, 34, nil,
- 35, nil, 23, 26, 25, 27, 12, nil, 30, nil,
- 31, nil, nil, nil, 32, nil, nil, nil, nil, nil,
- 17, nil, nil, nil, 21, nil, nil, nil, nil, nil,
- nil, nil, 11, 36, nil, nil, 14 ]
-
-racc_action_check = [
- 0, 0, 21, 0, 32, 0, 32, 106, 0, 85,
- 1, 85, 0, 0, 121, 0, 121, 0, 51, 0,
- 0, 0, 0, 0, 123, 0, 123, 0, 142, 13,
- 142, 0, 15, 144, 17, 144, 30, 0, 31, 2,
- 2, 0, 2, 15, 2, 21, 36, 2, 37, 0,
- 0, 2, 2, 0, 2, 39, 2, 106, 2, 2,
- 2, 2, 2, 51, 2, 51, 2, 44, 44, 41,
- 2, 48, 48, 42, 43, 45, 2, 46, 12, 12,
- 2, 12, 49, 12, 15, 50, 12, 56, 2, 2,
- 12, 12, 2, 12, 58, 12, 59, 12, 12, 12,
- 12, 12, 60, 12, 62, 12, 67, 73, 74, 12,
- 76, 77, 81, 83, 84, 12, 90, 14, 14, 12,
- 14, 91, 14, 94, 95, 14, 105, 12, 12, 14,
- 14, 12, 14, 109, 14, 112, 14, 14, 14, 14,
- 14, 113, 14, 114, 14, 118, 119, 125, 14, 132,
- 133, 135, 139, 141, 14, nil, nil, nil, 14, nil,
- 28, 28, 28, 28, nil, 28, 14, 14, 28, nil,
- 14, nil, 28, 28, nil, 28, nil, 28, nil, 28,
- 28, 28, 28, 28, nil, 28, nil, 28, nil, nil,
- nil, 28, nil, nil, nil, nil, nil, 28, nil, 29,
- 29, 28, 29, 29, 29, nil, nil, 29, nil, 28,
- 28, 29, 29, 28, 29, nil, 29, nil, 29, 29,
- 29, 29, 29, nil, 29, nil, 29, nil, nil, nil,
- 29, nil, nil, nil, nil, nil, 29, nil, 33, 33,
- 29, 33, nil, 33, nil, nil, 33, nil, 29, 29,
- 33, 33, 29, 33, nil, 33, nil, 33, 33, 33,
- 33, 33, nil, 33, nil, 33, nil, nil, nil, 33,
- nil, nil, nil, nil, nil, 33, nil, 34, 34, 33,
- 34, nil, 34, nil, nil, 34, nil, 33, 33, 34,
- 34, 33, 34, nil, 34, nil, 34, 34, 34, 34,
- 34, nil, 34, nil, 34, nil, nil, nil, 34, nil,
- nil, nil, nil, nil, 34, nil, 35, 35, 34, 35,
- nil, 35, nil, nil, 35, nil, 34, 34, 35, 35,
- 34, 35, nil, 35, nil, 35, 35, 35, 35, 35,
- nil, 35, nil, 35, nil, nil, nil, 35, nil, nil,
- nil, nil, nil, 35, nil, 40, 40, 35, 40, nil,
- 40, nil, nil, 40, nil, 35, 35, 40, 40, 35,
- 40, nil, 40, nil, 40, 40, 40, 40, 40, nil,
- 40, nil, 40, nil, nil, nil, 40, nil, nil, nil,
- nil, nil, 40, nil, 47, 47, 40, 47, nil, 47,
- nil, nil, 47, nil, 40, 40, 47, 47, 40, 47,
- nil, 47, nil, 47, 47, 47, 47, 47, nil, 47,
- nil, 47, nil, nil, nil, 47, nil, nil, nil, nil,
- nil, 47, 47, nil, nil, 47, nil, 52, 52, 52,
- 52, nil, 52, 47, 47, 52, nil, 47, nil, 52,
- 52, nil, 52, nil, 52, nil, 52, 52, 52, 52,
- 52, nil, 52, nil, 52, nil, nil, nil, 52, nil,
- nil, nil, nil, nil, 52, nil, 54, 54, 52, 54,
- 54, 54, nil, nil, 54, nil, 52, 52, 54, 54,
- 52, 54, nil, 54, nil, 54, 54, 54, 54, 54,
- nil, 54, nil, 54, nil, nil, nil, 54, nil, nil,
- nil, nil, nil, 54, nil, 57, 57, 54, 57, nil,
- 57, nil, nil, 57, nil, 54, 54, 57, 57, 54,
- 57, nil, 57, nil, 57, 57, 57, 57, 57, nil,
- 57, nil, 57, nil, nil, 57, 57, nil, nil, nil,
- nil, nil, 57, nil, 61, 61, 57, 61, nil, 61,
- nil, nil, 61, nil, 57, 57, 61, 61, 57, 61,
- nil, 61, nil, 61, 61, 61, 61, 61, nil, 61,
- nil, 61, nil, nil, nil, 61, nil, nil, 61, nil,
- nil, 61, nil, 63, 63, 61, 63, nil, 63, nil,
- nil, 63, nil, 61, 61, 63, 63, 61, 63, nil,
- 63, nil, 63, 63, 63, 63, 63, nil, 63, nil,
- 63, nil, nil, nil, 63, nil, nil, nil, nil, 63,
- 63, nil, nil, nil, 63, nil, 64, 64, nil, 64,
- nil, 64, 63, 63, 64, nil, 63, nil, 64, 64,
- 64, 64, nil, 64, nil, 64, 64, 64, 64, 64,
- nil, 64, nil, 64, nil, nil, nil, 64, nil, nil,
- nil, nil, nil, 64, nil, 65, 65, 64, 65, nil,
- 65, nil, nil, 65, nil, 64, 64, 65, 65, 64,
- 65, 65, 65, nil, 65, 65, 65, 65, 65, nil,
- 65, nil, 65, nil, nil, nil, 65, nil, nil, nil,
- nil, nil, 65, nil, 66, 66, 65, 66, nil, 66,
- nil, nil, 66, nil, 65, 65, 66, 66, 65, 66,
- nil, 66, 66, 66, 66, 66, 66, 66, nil, 66,
- nil, 66, nil, nil, nil, 66, nil, nil, nil, nil,
- nil, 66, nil, 71, 71, 66, 71, nil, 71, nil,
- nil, 71, nil, 66, 66, 71, 71, 66, 71, nil,
- 71, nil, 71, 71, 71, 71, 71, nil, 71, nil,
- 71, nil, nil, nil, 71, nil, nil, nil, nil, nil,
- 71, nil, 75, 75, 71, 75, nil, 75, nil, nil,
- 75, nil, 71, 71, 75, 75, 71, 75, nil, 75,
- nil, 75, 75, 75, 75, 75, nil, 75, nil, 75,
- nil, nil, nil, 75, nil, nil, nil, nil, nil, 75,
- nil, 79, 79, 75, 79, nil, 79, nil, nil, 79,
- nil, 75, 75, 79, 79, 75, 79, nil, 79, nil,
- 79, 79, 79, 79, 79, nil, 79, nil, 79, nil,
- nil, nil, 79, nil, nil, nil, nil, nil, 79, 79,
- 86, 86, 79, 86, nil, 86, nil, nil, 86, nil,
- 79, 79, 86, 86, 79, 86, nil, 86, nil, 86,
- 86, 86, 86, 86, nil, 86, nil, 86, nil, nil,
- nil, 86, nil, nil, nil, nil, nil, 86, nil, 87,
- 87, 86, 87, nil, 87, nil, 86, 87, nil, 86,
- 86, 87, 87, 86, 87, nil, 87, nil, 87, 87,
- 87, 87, 87, nil, 87, nil, 87, nil, nil, nil,
- 87, nil, nil, nil, nil, nil, 87, nil, 92, 92,
- 87, 92, nil, 92, nil, nil, 92, 87, 87, 87,
- 92, 92, 87, 92, nil, 92, nil, 92, 92, 92,
- 92, 92, nil, 92, nil, 92, nil, nil, 92, 92,
- nil, nil, nil, nil, nil, 92, nil, 97, 97, 92,
- 97, nil, 97, nil, nil, 97, nil, 92, 92, 97,
- 97, 92, 97, nil, 97, nil, 97, 97, 97, 97,
- 97, nil, 97, nil, 97, nil, nil, nil, 97, nil,
- nil, 97, nil, nil, 97, nil, 100, 100, 97, 100,
- nil, 100, nil, nil, 100, nil, 97, 97, 100, 100,
- 97, 100, nil, 100, nil, 100, 100, 100, 100, 100,
- nil, 100, nil, 100, nil, nil, nil, 100, nil, nil,
- nil, nil, 100, 100, nil, 108, 108, 100, 108, nil,
- 108, nil, 108, 108, nil, 100, 100, 108, 108, 100,
- 108, nil, 108, nil, 108, 108, 108, 108, 108, nil,
- 108, nil, 108, nil, nil, nil, 108, nil, nil, nil,
- nil, nil, 108, nil, 111, 111, 108, 111, nil, 111,
- nil, nil, 111, nil, 108, 108, 111, 111, 108, 111,
- nil, 111, nil, 111, 111, 111, 111, 111, nil, 111,
- nil, 111, nil, nil, nil, 111, nil, nil, nil, nil,
- nil, 111, nil, 120, 120, 111, 120, nil, 120, nil,
- nil, 120, nil, 111, 111, 120, 120, 111, 120, nil,
- 120, nil, 120, 120, 120, 120, 120, nil, 120, nil,
- 120, nil, nil, nil, 120, nil, nil, nil, nil, nil,
- 120, nil, 122, 122, 120, 122, nil, 122, nil, 120,
- 122, nil, 120, 120, 122, 122, 120, 122, nil, 122,
- nil, 122, 122, 122, 122, 122, nil, 122, nil, 122,
- nil, nil, nil, 122, nil, nil, nil, nil, nil, 122,
- nil, 134, 134, 122, 134, nil, 134, nil, nil, 134,
- 122, 122, 122, 134, 134, 122, 134, nil, 134, nil,
- 134, 134, 134, 134, 134, nil, 134, nil, 134, nil,
- nil, nil, 134, nil, nil, nil, nil, nil, 134, nil,
- nil, nil, 134, nil, nil, nil, nil, nil, nil, nil,
- 134, 134, 136, 136, 134, 136, nil, 136, nil, nil,
- 136, nil, 136, nil, 136, 136, nil, 136, nil, 136,
- nil, 136, 136, 136, 136, 136, nil, 136, nil, 136,
- nil, nil, nil, 136, nil, nil, nil, nil, nil, 136,
- nil, nil, nil, 136, nil, nil, nil, nil, nil, nil,
- nil, 136, 136, 152, 152, 136, 152, nil, 152, nil,
- nil, 152, nil, 152, nil, 152, 152, nil, 152, nil,
- 152, nil, 152, 152, 152, 152, 152, nil, 152, nil,
- 152, nil, nil, nil, 152, nil, nil, nil, nil, nil,
- 152, nil, nil, nil, 152, nil, nil, nil, nil, nil,
- nil, nil, 152, 152, nil, nil, 152 ]
-
-racc_action_pointer = [
- -2, 10, 37, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 76, 27, 115, 30, nil, -7, nil, nil,
- nil, 0, nil, nil, nil, nil, nil, nil, 158, 197,
- 5, 7, -31, 236, 275, 314, 44, 48, nil, 29,
- 353, 61, 64, 18, 55, 73, 23, 392, 30, 38,
- 40, 16, 435, nil, 474, nil, 59, 513, 64, 59,
- 68, 552, 67, 591, 634, 673, 712, 94, nil, nil,
- nil, 751, nil, 105, 97, 790, 98, 109, nil, 829,
- nil, 71, nil, 69, 68, -38, 868, 907, nil, nil,
- 88, 90, 946, nil, 93, 90, nil, 985, nil, nil,
- 1024, nil, nil, nil, nil, 73, 5, nil, 1063, 121,
- nil, 1102, 124, 139, 131, nil, nil, nil, 100, 100,
- 1141, -33, 1180, -23, nil, 116, nil, nil, nil, nil,
- nil, nil, 137, 138, 1219, 140, 1270, nil, nil, 141,
- nil, 108, -19, nil, -14, nil, nil, nil, nil, nil,
- nil, nil, 1321, nil, nil, nil, nil, nil ]
-
-racc_action_default = [
- -83, -83, -1, -2, -3, -4, -5, -6, -7, -8,
- -9, -10, -19, -83, -19, -83, -18, -23, -37, -39,
- -40, -43, -51, -52, -53, -54, -55, -56, -83, -83,
- -83, -83, -73, -83, -83, -83, -83, -83, -38, -83,
- -20, -83, -26, -83, -30, -83, -83, -83, -23, -83,
- -43, -46, -83, -57, -83, -58, -63, -83, -63, -73,
- -83, -83, -73, -83, -83, -83, -83, -80, 158, -11,
- -12, -83, -13, -83, -83, -83, -32, -83, -21, -83,
- -24, -23, -41, -83, -83, -46, -83, -83, -59, -60,
- -83, -83, -83, -66, -83, -83, -69, -83, -70, -72,
- -83, -74, -76, -77, -78, -83, -83, -27, -28, -34,
- -15, -31, -83, -83, -30, -22, -25, -42, -43, -83,
- -83, -46, -83, -46, -61, -65, -67, -62, -68, -71,
- -75, -79, -80, -80, -83, -83, -34, -16, -33, -83,
- -44, -43, -46, -47, -46, -49, -64, -81, -82, -29,
- -14, -35, -34, -17, -45, -48, -50, -36 ]
-
-racc_goto_table = [
- 38, 84, 74, 105, 49, 39, 90, 43, 94, 60,
- 135, 1, 133, 2, 47, 41, 107, 112, 59, 56,
- 58, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 83, nil, 119, 95, 151, 38, 99,
- nil, 52, 54, nil, nil, 80, 64, 65, 66, nil,
- 38, nil, 38, 157, nil, nil, nil, nil, nil, nil,
- 79, nil, 38, 38, 38, nil, nil, nil, 147, 148,
- 92, 143, 139, 145, 97, 146, 100, 38, 116, 149,
- 125, nil, nil, nil, 108, nil, nil, nil, 111, nil,
- 38, nil, 155, nil, 156, 38, nil, nil, 38, 120,
- 122, 140, nil, nil, nil, nil, 38, nil, nil, 38,
- nil, nil, nil, nil, nil, nil, nil, nil, 38, nil,
- 38, nil, nil, nil, 154, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 108, nil, 152,
- 38 ]
-
-racc_goto_check = [
- 3, 23, 15, 30, 22, 12, 25, 12, 25, 28,
- 14, 1, 11, 2, 18, 13, 19, 16, 27, 24,
- 24, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 22, nil, 23, 28, 14, 3, 28,
- nil, 2, 2, nil, nil, 18, 2, 2, 2, nil,
- 3, nil, 3, 14, nil, nil, nil, nil, nil, nil,
- 2, nil, 3, 3, 3, nil, nil, nil, 30, 30,
- 2, 23, 15, 23, 2, 25, 2, 3, 18, 19,
- 24, nil, nil, nil, 2, nil, nil, nil, 2, nil,
- 3, nil, 23, nil, 23, 3, nil, nil, 3, 2,
- 2, 22, nil, nil, nil, nil, 3, nil, nil, 3,
- nil, nil, nil, nil, nil, nil, nil, nil, 3, nil,
- 3, nil, nil, nil, 22, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 2, nil, 2,
- 3 ]
-
-racc_goto_pointer = [
- nil, 11, 13, -2, nil, nil, nil, nil, nil, nil,
- nil, -94, -7, 2, -99, -42, -59, nil, -3, -55,
- nil, nil, -17, -50, -11, -50, nil, -14, -23, nil,
- -64 ]
-
-racc_goto_default = [
- nil, nil, 40, 18, 3, 4, 5, 6, 7, 8,
- 9, 10, nil, nil, nil, nil, nil, 16, nil, nil,
- 19, 20, nil, nil, nil, nil, 91, nil, nil, 62,
- nil ]
-
-racc_reduce_table = [
- 0, 0, :racc_error,
- 1, 58, :_reduce_1,
- 1, 60, :_reduce_2,
- 1, 60, :_reduce_3,
- 1, 60, :_reduce_4,
- 1, 60, :_reduce_5,
- 1, 60, :_reduce_6,
- 1, 60, :_reduce_7,
- 1, 60, :_reduce_8,
- 1, 60, :_reduce_9,
- 1, 60, :_reduce_10,
- 3, 60, :_reduce_11,
- 3, 60, :_reduce_12,
- 3, 60, :_reduce_13,
- 6, 60, :_reduce_14,
- 4, 60, :_reduce_15,
- 5, 60, :_reduce_16,
- 6, 60, :_reduce_17,
- 1, 60, :_reduce_none,
- 0, 69, :_reduce_19,
- 1, 69, :_reduce_20,
- 3, 67, :_reduce_21,
- 4, 67, :_reduce_22,
- 0, 75, :_reduce_23,
- 2, 75, :_reduce_24,
- 3, 75, :_reduce_25,
- 1, 70, :_reduce_26,
- 3, 70, :_reduce_27,
- 1, 76, :_reduce_28,
- 3, 76, :_reduce_29,
- 0, 72, :_reduce_30,
- 2, 72, :_reduce_31,
- 0, 73, :_reduce_32,
- 2, 73, :_reduce_33,
- 0, 71, :_reduce_34,
- 2, 71, :_reduce_35,
- 3, 71, :_reduce_36,
- 1, 59, :_reduce_37,
- 2, 59, :_reduce_38,
- 1, 61, :_reduce_39,
- 1, 61, :_reduce_40,
- 3, 74, :_reduce_41,
- 4, 74, :_reduce_42,
- 0, 79, :_reduce_43,
- 4, 79, :_reduce_44,
- 5, 79, :_reduce_45,
- 0, 80, :_reduce_46,
- 3, 80, :_reduce_47,
- 4, 80, :_reduce_48,
- 3, 80, :_reduce_49,
- 4, 80, :_reduce_50,
- 1, 77, :_reduce_51,
- 1, 77, :_reduce_52,
- 1, 77, :_reduce_53,
- 1, 77, :_reduce_54,
- 1, 77, :_reduce_55,
- 1, 77, :_reduce_56,
- 2, 78, :_reduce_57,
- 2, 78, :_reduce_58,
- 3, 78, :_reduce_59,
- 3, 78, :_reduce_60,
- 4, 62, :_reduce_61,
- 4, 63, :_reduce_62,
- 0, 83, :_reduce_63,
- 3, 82, :_reduce_64,
- 0, 82, :_reduce_65,
- 2, 81, :_reduce_66,
- 3, 81, :_reduce_67,
- 4, 64, :_reduce_68,
- 3, 64, :_reduce_69,
- 2, 84, :_reduce_70,
- 3, 84, :_reduce_71,
- 2, 85, :_reduce_72,
- 0, 85, :_reduce_73,
- 2, 86, :_reduce_74,
- 3, 86, :_reduce_75,
- 3, 65, :_reduce_76,
- 3, 65, :_reduce_77,
- 3, 66, :_reduce_78,
- 4, 68, :_reduce_79,
- 0, 87, :_reduce_80,
- 3, 87, :_reduce_81,
- 3, 87, :_reduce_82 ]
-
-racc_reduce_n = 83
-
-racc_shift_n = 158
-
-racc_token_table = {
- false => 0,
- :error => 1,
- :TEXT => 2,
- :BOLD_START => 3,
- :BOLD_END => 4,
- :ITALIC_START => 5,
- :ITALIC_END => 6,
- :LINK_START => 7,
- :LINK_END => 8,
- :LINKSEP => 9,
- :INTLINK_START => 10,
- :INTLINK_END => 11,
- :INTLINKSEP => 12,
- :RESOURCESEP => 13,
- :CHAR_ENT => 14,
- :PRE_START => 15,
- :PRE_END => 16,
- :PREINDENT_START => 17,
- :PREINDENT_END => 18,
- :SECTION_START => 19,
- :SECTION_END => 20,
- :HLINE => 21,
- :SIGNATURE_NAME => 22,
- :SIGNATURE_DATE => 23,
- :SIGNATURE_FULL => 24,
- :PARA_START => 25,
- :PARA_END => 26,
- :UL_START => 27,
- :UL_END => 28,
- :OL_START => 29,
- :OL_END => 30,
- :LI_START => 31,
- :LI_END => 32,
- :DL_START => 33,
- :DL_END => 34,
- :DT_START => 35,
- :DT_END => 36,
- :DD_START => 37,
- :DD_END => 38,
- :TAG_START => 39,
- :TAG_END => 40,
- :ATTR_NAME => 41,
- :ATTR_VALUE => 42,
- :TABLE_START => 43,
- :TABLE_END => 44,
- :ROW_START => 45,
- :ROW_END => 46,
- :HEAD_START => 47,
- :HEAD_END => 48,
- :CELL_START => 49,
- :CELL_END => 50,
- :KEYWORD => 51,
- :TEMPLATE_START => 52,
- :TEMPLATE_END => 53,
- :CATEGORY => 54,
- :PASTE_START => 55,
- :PASTE_END => 56 }
-
-racc_nt_base = 57
-
-racc_use_result_var = true
-
-Racc_arg = [
- racc_action_table,
- racc_action_check,
- racc_action_default,
- racc_action_pointer,
- racc_goto_table,
- racc_goto_check,
- racc_goto_default,
- racc_goto_pointer,
- racc_nt_base,
- racc_reduce_table,
- racc_token_table,
- racc_shift_n,
- racc_reduce_n,
- racc_use_result_var ]
-Ractor.make_shareable(Racc_arg) if defined?(Ractor)
-
-Racc_token_to_s_table = [
- "$end",
- "error",
- "TEXT",
- "BOLD_START",
- "BOLD_END",
- "ITALIC_START",
- "ITALIC_END",
- "LINK_START",
- "LINK_END",
- "LINKSEP",
- "INTLINK_START",
- "INTLINK_END",
- "INTLINKSEP",
- "RESOURCESEP",
- "CHAR_ENT",
- "PRE_START",
- "PRE_END",
- "PREINDENT_START",
- "PREINDENT_END",
- "SECTION_START",
- "SECTION_END",
- "HLINE",
- "SIGNATURE_NAME",
- "SIGNATURE_DATE",
- "SIGNATURE_FULL",
- "PARA_START",
- "PARA_END",
- "UL_START",
- "UL_END",
- "OL_START",
- "OL_END",
- "LI_START",
- "LI_END",
- "DL_START",
- "DL_END",
- "DT_START",
- "DT_END",
- "DD_START",
- "DD_END",
- "TAG_START",
- "TAG_END",
- "ATTR_NAME",
- "ATTR_VALUE",
- "TABLE_START",
- "TABLE_END",
- "ROW_START",
- "ROW_END",
- "HEAD_START",
- "HEAD_END",
- "CELL_START",
- "CELL_END",
- "KEYWORD",
- "TEMPLATE_START",
- "TEMPLATE_END",
- "CATEGORY",
- "PASTE_START",
- "PASTE_END",
- "$start",
- "wiki",
- "repeated_contents",
- "contents",
- "text",
- "bulleted_list",
- "numbered_list",
- "dictionary_list",
- "preformatted",
- "section",
- "tag",
- "template",
- "para_contents",
- "link_contents",
- "reslink_repeated_contents",
- "intlink_repeated_contents",
- "cat_sort_contents",
- "table",
- "tag_attributes",
- "link_repeated_contents",
- "element",
- "formatted_element",
- "table_contents",
- "row_contents",
- "list_item",
- "list_contents",
- "@1",
- "dictionary_term",
- "dictionary_contents",
- "dictionary_definition",
- "template_parameters" ]
-Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)
-
-Racc_debug_parser = false
-
-##### State transition tables end #####
-
-# reduce 0 omitted
-
-module_eval(<<'.,.,', 'mediacloth.y', 47)
- def _reduce_1(val, _values, result)
- @nodes.push WikiAST.new(0, @wiki_ast_length)
- #@nodes.last.children.insert(0, val[0])
- #puts val[0]
- @nodes.last.children += val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 57)
- def _reduce_2(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 61)
- def _reduce_3(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 65)
- def _reduce_4(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 69)
- def _reduce_5(val, _values, result)
- list = ListAST.new(@ast_index, @ast_length)
- list.list_type = :Dictionary
- list.children = val[0]
- result = list
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 76)
- def _reduce_6(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 80)
- def _reduce_7(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 84)
- def _reduce_8(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 88)
- def _reduce_9(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 92)
- def _reduce_10(val, _values, result)
- k = KeywordAST.new(@ast_index, @ast_length)
- k.text = val[0]
- result = k
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 98)
- def _reduce_11(val, _values, result)
- p = ParagraphAST.new(@ast_index, @ast_length)
- p.children = val[1]
- result = p
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 104)
- def _reduce_12(val, _values, result)
- l = LinkAST.new(@ast_index, @ast_length)
- l.link_type = val[0]
- l.url = val[1][0]
- l.children += val[1][1..-1] if val[1].length > 1
- result = l
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 112)
- def _reduce_13(val, _values, result)
- p = PasteAST.new(@ast_index, @ast_length)
- p.children = val[1]
- result = p
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 118)
- def _reduce_14(val, _values, result)
- l = ResourceLinkAST.new(@ast_index, @ast_length)
- l.prefix = val[1]
- l.locator = val[3]
- l.children = val[4] unless val[4].nil? or val[4].empty?
- result = l
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 126)
- def _reduce_15(val, _values, result)
- l = InternalLinkAST.new(@ast_index, @ast_length)
- l.locator = val[1]
- l.children = val[2] unless val[2].nil? or val[2].empty?
- result = l
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 133)
- def _reduce_16(val, _values, result)
- l = CategoryAST.new(@ast_index, @ast_length)
- l.locator = val[2]
- l.sort_as = val[3]
- result = l
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 140)
- def _reduce_17(val, _values, result)
- l = CategoryLinkAST.new(@ast_index, @ast_length)
- l.locator = val[3]
- l.children = val[4] unless val[4].nil? or val[4].empty?
- result = l
-
- result
- end
-.,.,
-
-# reduce 18 omitted
-
-module_eval(<<'.,.,', 'mediacloth.y', 150)
- def _reduce_19(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 154)
- def _reduce_20(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 161)
- def _reduce_21(val, _values, result)
- if val[0] != val[2]
- raise Racc::ParseError.new("XHTML end tag #{val[2]} does not match start tag #{val[0]}")
- end
- elem = ElementAST.new(@ast_index, @ast_length)
- elem.name = val[0]
- elem.attributes = val[1]
- result = elem
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 171)
- def _reduce_22(val, _values, result)
- if val[0] != val[3]
- raise Racc::ParseError.new("XHTML end tag #{val[3]} does not match start tag #{val[0]}")
- end
- elem = ElementAST.new(@ast_index, @ast_length)
- elem.name = val[0]
- elem.attributes = val[1]
- elem.children += val[2]
- result = elem
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 184)
- def _reduce_23(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 188)
- def _reduce_24(val, _values, result)
- attr_map = val[2] ? val[2] : {}
- attr_map[val[0]] = true
- result = attr_map
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 194)
- def _reduce_25(val, _values, result)
- attr_map = val[2] ? val[2] : {}
- attr_map[val[0]] = val[1]
- result = attr_map
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 204)
- def _reduce_26(val, _values, result)
- result = val
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 208)
- def _reduce_27(val, _values, result)
- result = [val[0]]
- result += val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 217)
- def _reduce_28(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 221)
- def _reduce_29(val, _values, result)
- result = val[0]
- result += val[2] if val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 229)
- def _reduce_30(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 233)
- def _reduce_31(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 239)
- def _reduce_32(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 243)
- def _reduce_33(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 249)
- def _reduce_34(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 253)
- def _reduce_35(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 257)
- def _reduce_36(val, _values, result)
- i = InternalLinkItemAST.new(@ast_index, @ast_length)
- i.children = val[1]
- result = [i]
- result += val[2] if val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 266)
- def _reduce_37(val, _values, result)
- result = []
- result << val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 271)
- def _reduce_38(val, _values, result)
- result = []
- result += val[0]
- result << val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 279)
- def _reduce_39(val, _values, result)
- p = TextAST.new(@ast_index, @ast_length)
- p.formatting = val[0][0]
- p.contents = val[0][1]
- result = p
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 286)
- def _reduce_40(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 293)
- def _reduce_41(val, _values, result)
- table = TableAST.new(@ast_index, @ast_length)
- table.children = val[1] unless val[1].nil? or val[1].empty?
- result = table
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 299)
- def _reduce_42(val, _values, result)
- table = TableAST.new(@ast_index, @ast_length)
- table.options = val[1]
- table.children = val[2] unless val[2].nil? or val[2].empty?
- result = table
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 307)
- def _reduce_43(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 311)
- def _reduce_44(val, _values, result)
- row = TableRowAST.new(@ast_index, @ast_length)
- row.children = val[1] unless val[1].nil? or val[1].empty?
- result = [row]
- result += val[3] unless val[3].nil? or val[3].empty?
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 318)
- def _reduce_45(val, _values, result)
- row = TableRowAST.new(@ast_index, @ast_length)
- row.children = val[2] unless val[2].nil? or val[2].empty?
- row.options = val[1]
- result = [row]
- result += val[4] unless val[4].nil? or val[4].empty?
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 327)
- def _reduce_46(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 331)
- def _reduce_47(val, _values, result)
- cell = TableCellAST.new(@ast_index, @ast_length)
- cell.type = :head
- result = [cell]
- result += val[2] unless val[2].nil? or val[2].empty?
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 338)
- def _reduce_48(val, _values, result)
- cell = TableCellAST.new(@ast_index, @ast_length)
- cell.children = val[1] unless val[1].nil? or val[1].empty?
- cell.type = :head
- result = [cell]
- result += val[3] unless val[3].nil? or val[3].empty?
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 346)
- def _reduce_49(val, _values, result)
- cell = TableCellAST.new(@ast_index, @ast_length)
- cell.type = :body
- result = [cell]
- result += val[2] unless val[2].nil? or val[2].empty?
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 353)
- def _reduce_50(val, _values, result)
- if val[2] == 'attributes'
- result = []
- else
- cell = TableCellAST.new(@ast_index, @ast_length)
- cell.children = val[1] unless val[1].nil? or val[1].empty?
- cell.type = :body
- result = [cell]
- end
- result += val[3] unless val[3].nil? or val[3].empty?
- if val[2] == 'attributes' and val[3] and val[3].first.class == TableCellAST
- val[3].first.attributes = val[1]
- end
- result
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 371)
- def _reduce_51(val, _values, result)
- return [:None, val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 373)
- def _reduce_52(val, _values, result)
- return [:HLine, val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 375)
- def _reduce_53(val, _values, result)
- return [:CharacterEntity, val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 377)
- def _reduce_54(val, _values, result)
- return [:SignatureDate, val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 379)
- def _reduce_55(val, _values, result)
- return [:SignatureName, val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 381)
- def _reduce_56(val, _values, result)
- return [:SignatureFull, val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 387)
- def _reduce_57(val, _values, result)
- result = FormattedAST.new(@ast_index, @ast_length)
- result.formatting = :Bold
- result
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 393)
- def _reduce_58(val, _values, result)
- result = FormattedAST.new(@ast_index, @ast_length)
- result.formatting = :Italic
- result
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 399)
- def _reduce_59(val, _values, result)
- p = FormattedAST.new(@ast_index, @ast_length)
- p.formatting = :Bold
- p.children += val[1]
- result = p
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 406)
- def _reduce_60(val, _values, result)
- p = FormattedAST.new(@ast_index, @ast_length)
- p.formatting = :Italic
- p.children += val[1]
- result = p
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 415)
- def _reduce_61(val, _values, result)
- list = ListAST.new(@ast_index, @ast_length)
- list.list_type = :Bulleted
- list.children << val[1]
- list.children += val[2]
- result = list
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 425)
- def _reduce_62(val, _values, result)
- list = ListAST.new(@ast_index, @ast_length)
- list.list_type = :Numbered
- list.children << val[1]
- list.children += val[2]
- result = list
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 434)
- def _reduce_63(val, _values, result)
- result = []
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 437)
- def _reduce_64(val, _values, result)
- result << val[1]
- result += val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 441)
- def _reduce_65(val, _values, result)
- result = []
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 447)
- def _reduce_66(val, _values, result)
- result = ListItemAST.new(@ast_index, @ast_length)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 451)
- def _reduce_67(val, _values, result)
- li = ListItemAST.new(@ast_index, @ast_length)
- li.children += val[1]
- result = li
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 460)
- def _reduce_68(val, _values, result)
- result = [val[1]]
- result += val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 465)
- def _reduce_69(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 472)
- def _reduce_70(val, _values, result)
- result = ListTermAST.new(@ast_index, @ast_length)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 476)
- def _reduce_71(val, _values, result)
- term = ListTermAST.new(@ast_index, @ast_length)
- term.children += val[1]
- result = term
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 484)
- def _reduce_72(val, _values, result)
- result = [val[0]]
- result += val[1] if val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 489)
- def _reduce_73(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 495)
- def _reduce_74(val, _values, result)
- result = ListDefinitionAST.new(@ast_index, @ast_length)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 499)
- def _reduce_75(val, _values, result)
- term = ListDefinitionAST.new(@ast_index, @ast_length)
- term.children += val[1]
- result = term
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 506)
- def _reduce_76(val, _values, result)
- p = PreformattedAST.new(@ast_index, @ast_length)
- p.children += val[1]
- result = p
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 512)
- def _reduce_77(val, _values, result)
- p = PreformattedAST.new(@ast_index, @ast_length)
- p.indented = true
- p.children += val[1]
- result = p
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 520)
- def _reduce_78(val, _values, result)
- result = [val[1], val[0].length]
- s = SectionAST.new(@ast_index, @ast_length)
- s.children = val[1]
- s.level = val[0].length
- result = s
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 530)
- def _reduce_79(val, _values, result)
- t = TemplateAST.new(@ast_index, @ast_length)
- t.template_name = val[1]
- t.children = val[2] unless val[2].nil? or val[2].empty?
- result = t
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 539)
- def _reduce_80(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 543)
- def _reduce_81(val, _values, result)
- p = TemplateParameterAST.new(@ast_index, @ast_length)
- p.parameter_value = val[1]
- result = [p]
- result += val[2] if val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mediacloth.y', 550)
- def _reduce_82(val, _values, result)
- p = TemplateParameterAST.new(@ast_index, @ast_length)
- p.children << val[1]
- result = [p]
- result += val[2] if val[2]
-
- result
- end
-.,.,
-
-def _reduce_none(val, _values, result)
- val[0]
-end
-
-end # class MediaWikiParser
diff --git a/test/racc/regress/mof b/test/racc/regress/mof
deleted file mode 100644
index afbf8cf2b9..0000000000
--- a/test/racc/regress/mof
+++ /dev/null
@@ -1,1370 +0,0 @@
-#
-# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.5.2
-# from Racc grammar file "".
-#
-
-require 'racc/parser.rb'
-
-
-# parser.rb - generated by racc
-
-require 'strscan'
-require 'rubygems'
-require 'cim'
-require File.join(__dir__, 'result')
-require File.join(__dir__, 'scanner')
-require File.join(__dir__, 'case')
-
-module MOF
- class Parser < Racc::Parser
-
-module_eval(<<'...end mof.y/module_eval...', 'mof.y', 571)
-
-#
-# Initialize MOF::Parser
-# MOF::Parser.new options = {}
-#
-# options -> Hash of options
-# :debug -> boolean
-# :includes -> array of include dirs
-# :style -> :cim or :wmi
-#
-def initialize options = {}
- @yydebug = options[:debug]
- @includes = options[:includes] || []
- @quiet = options[:quiet]
- @style = options[:style] || :cim # default to style CIM v2.2 syntax
-
- @lineno = 1
- @file = nil
- @iconv = nil
- @eol = "\n"
- @fname = nil
- @fstack = []
- @in_comment = false
- @seen_files = []
- @qualifiers = {}
-end
-
-#
-# Make options hash from argv
-#
-# returns [ files, options ]
-#
-
- def self.argv_handler name, argv
- files = []
- options = { :namespace => "" }
- while argv.size > 0
- case opt = argv.shift
- when "-h"
- $stderr.puts "Ruby MOF compiler"
- $stderr.puts "#{name} [-h] [-d] [-I <dir>] [<moffiles>]"
- $stderr.puts "Compiles <moffile>"
- $stderr.puts "\t-d debug"
- $stderr.puts "\t-h this help"
- $stderr.puts "\t-I <dir> include dir"
- $stderr.puts "\t-f force"
- $stderr.puts "\t-n <namespace>"
- $stderr.puts "\t-o <output>"
- $stderr.puts "\t-s <style> syntax style (wmi,cim)"
- $stderr.puts "\t-q quiet"
- $stderr.puts "\t<moffiles> file(s) to read (else use $stdin)"
- exit 0
- when "-f" then options[:force] = true
- when "-s" then options[:style] = argv.shift.to_sym
- when "-d" then options[:debug] = true
- when "-q" then options[:quiet] = true
- when "-I"
- options[:includes] ||= []
- dirname = argv.shift
- unless File.directory?(dirname)
- files << dirname
- dirname = File.dirname(dirname)
- end
- options[:includes] << Pathname.new(dirname)
- when "-n" then options[:namespace] = argv.shift
- when "-o" then options[:output] = argv.shift
- when /^-.+/
- $stderr.puts "Undefined option #{opt}"
- else
- files << opt
- end
- end
- [ files, options ]
- end
-
-include Helper
-include Scanner
-
-...end mof.y/module_eval...
-##### State transition tables begin ###
-
-racc_action_table = [
- 13, 172, 163, 197, 174, 27, 63, 17, 145, 146,
- 147, 62, 28, 172, 11, 173, 174, 173, 148, 11,
- 144, 149, 150, 151, 152, 18, 163, 173, 33, 153,
- 106, 107, 108, 109, 110, 112, 111, 40, 15, 16,
- 42, 55, 57, 68, 69, 71, 72, 52, 53, 54,
- 56, 163, 7, 199, 35, 42, 174, 7, 10, 10,
- 115, 102, 114, 36, 10, 55, 57, 68, 69, 71,
- 72, 52, 53, 54, 56, 51, 190, 44, -77, 42,
- 33, 189, 10, 10, 33, 102, 51, 164, 60, 10,
- 55, 57, 68, 69, 71, 72, 52, 53, 54, 56,
- 170, 35, 10, 21, 42, 22, 23, 10, 29, 31,
- 102, 35, 95, 96, 55, 57, 25, 65, 24, 78,
- 52, 53, 54, 56, 97, 55, 57, 35, 180, 181,
- 100, 52, 53, 54, 56, 93, 79, 80, 81, 82,
- 83, 84, 85, 86, 87, 88, 89, 90, 91, 92,
- -25, 93, 79, 80, 81, 82, 83, 84, 85, 86,
- 87, 88, 89, 90, 91, 92, 191, 192, 102, 60,
- 55, 57, 68, 69, 71, 72, 52, 53, 54, 56,
- 145, 146, 147, 172, 113, 97, 174, 10, 18, 118,
- 148, 119, 144, 149, 150, 151, 152, 173, 121, 124,
- 35, 153, 55, 57, 68, 69, 71, 72, 52, 53,
- 54, 56, 106, 107, 108, 109, 110, 112, 111, 10,
- 55, 57, 68, 69, 71, 72, 52, 53, 54, 56,
- 21, 126, 22, 23, 127, 129, 21, 10, 22, 23,
- 130, 131, 133, 25, 135, 24, 10, 141, 154, 25,
- 35, 24, 93, 79, 80, 81, 82, 83, 84, 85,
- 86, 87, 88, 89, 90, 91, 92, 106, 107, 108,
- 109, 110, 112, 111, 184, 185, 194, 200, 121, 207,
- -59, 121, 209, 211, 135, 135, 218, 220, 221, 226,
- 228, 229, 231, 10, 121, 135 ]
-
-racc_action_check = [
- 1, 140, 135, 186, 140, 12, 37, 8, 131, 131,
- 131, 37, 13, 194, 0, 140, 194, 186, 131, 1,
- 131, 131, 131, 131, 131, 8, 189, 194, 16, 131,
- 65, 65, 65, 65, 65, 65, 65, 20, 7, 7,
- 20, 135, 135, 135, 135, 135, 135, 135, 135, 135,
- 135, 211, 0, 187, 17, 135, 187, 1, 135, 0,
- 75, 135, 75, 18, 1, 189, 189, 189, 189, 189,
- 189, 189, 189, 189, 189, 33, 177, 26, 207, 189,
- 29, 177, 189, 207, 30, 189, 96, 137, 34, 137,
- 211, 211, 211, 211, 211, 211, 211, 211, 211, 211,
- 138, 36, 138, 10, 211, 10, 10, 211, 15, 15,
- 211, 169, 47, 47, 33, 33, 10, 38, 10, 43,
- 33, 33, 33, 33, 49, 96, 96, 216, 142, 142,
- 58, 96, 96, 96, 96, 169, 169, 169, 169, 169,
- 169, 169, 169, 169, 169, 169, 169, 169, 169, 169,
- 42, 216, 216, 216, 216, 216, 216, 216, 216, 216,
- 216, 216, 216, 216, 216, 216, 178, 178, 60, 61,
- 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
- 181, 181, 181, 218, 66, 70, 218, 42, 74, 77,
- 181, 78, 181, 181, 181, 181, 181, 218, 94, 98,
- 100, 181, 40, 40, 40, 40, 40, 40, 40, 40,
- 40, 40, 141, 141, 141, 141, 141, 141, 141, 40,
- 115, 115, 115, 115, 115, 115, 115, 115, 115, 115,
- 11, 102, 11, 11, 103, 116, 63, 115, 63, 63,
- 118, 119, 121, 11, 122, 11, 127, 130, 132, 63,
- 161, 63, 44, 44, 44, 44, 44, 44, 44, 44,
- 44, 44, 44, 44, 44, 44, 44, 192, 192, 192,
- 192, 192, 192, 192, 162, 164, 183, 188, 195, 196,
- 197, 198, 201, 205, 206, 208, 210, 212, 213, 217,
- 221, 222, 223, 229, 230, 233 ]
-
-racc_action_pointer = [
- -5, 0, nil, nil, nil, nil, nil, 32, -2, nil,
- 95, 222, -58, 12, nil, 101, -30, 46, 32, nil,
- -21, nil, nil, nil, nil, nil, 11, nil, nil, 22,
- 26, nil, nil, 67, 60, nil, 93, -54, 51, nil,
- 155, nil, 123, 59, 220, nil, nil, 53, nil, 76,
- nil, nil, nil, nil, nil, nil, nil, nil, 64, nil,
- 101, 141, nil, 228, nil, 18, 125, nil, nil, nil,
- 137, nil, nil, nil, 161, 0, nil, 129, 171, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 134, nil, 78, nil, 138, nil,
- 192, nil, 223, 173, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 173, 172, nil, 214, 183,
- nil, 195, 176, nil, nil, nil, nil, 182, nil, nil,
- 189, -1, 183, nil, nil, -6, nil, 25, 38, nil,
- -7, 200, 69, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 242, 208, nil, 212, nil, nil, nil, nil, 103,
- nil, nil, nil, nil, nil, nil, nil, 13, 107, nil,
- nil, 171, nil, 207, nil, nil, -5, 45, 248, 18,
- nil, nil, 255, nil, 5, 214, 221, 222, 217, nil,
- nil, 219, nil, nil, nil, 215, 216, 19, 217, nil,
- 226, 43, 224, 229, nil, nil, 119, 226, 175, nil,
- nil, 227, 231, 284, nil, nil, nil, nil, nil, 229,
- 230, nil, nil, 227, nil, nil ]
-
-racc_action_default = [
- -1, -25, -2, -4, -5, -6, -7, -161, -161, -26,
- -161, -161, -161, -161, -3, -161, -12, -161, -161, -28,
- -33, -133, -134, -135, -136, -137, -161, -155, 236, -12,
- -12, -11, -10, -161, -44, -48, -161, -161, -31, -34,
- -25, -36, -100, -161, -161, -8, -9, -161, -14, -16,
- -17, -18, -110, -111, -112, -113, -114, -115, -46, -45,
- -161, -44, -27, -161, -30, -161, -161, -103, -104, -105,
- -106, -107, -108, -109, -161, -161, -101, -131, -161, -60,
- -61, -62, -63, -64, -65, -66, -67, -68, -69, -70,
- -71, -72, -73, -74, -86, -13, -161, -116, -161, -47,
- -161, -49, -161, -161, -29, -32, -37, -38, -39, -40,
- -41, -42, -43, -35, -99, -25, -161, -132, -161, -161,
- -87, -91, -93, -15, -20, -51, -50, -25, -102, -130,
- -161, -161, -161, -92, -94, -25, -138, -25, -25, -157,
- -161, -161, -161, -140, -142, -143, -144, -145, -146, -147,
- -148, -149, -150, -151, -90, -95, -96, -97, -98, -117,
- -118, -161, -161, -122, -161, -21, -22, -23, -24, -161,
- -156, -158, -55, -56, -58, -128, -129, -161, -161, -153,
- -139, -161, -119, -161, -121, -19, -161, -161, -75, -25,
- -160, -152, -161, -141, -161, -86, -161, -55, -86, -57,
- -76, -161, -154, -123, -125, -161, -93, -25, -93, -159,
- -124, -25, -161, -161, -78, -80, -161, -161, -161, -127,
- -52, -161, -79, -161, -83, -84, -53, -126, -54, -25,
- -86, -85, -81, -88, -82, -89 ]
-
-racc_goto_table = [
- 8, 8, 34, 94, 122, 105, 136, 155, 48, 66,
- 49, 76, 50, 19, 143, 187, 58, 204, 215, 32,
- 139, 61, 2, 14, 12, 12, 1, 30, 47, 98,
- 137, 171, 45, 46, 165, 166, 167, 168, 37, 26,
- 232, 227, 38, 103, 64, 39, 41, 99, 101, 195,
- 198, 196, 213, 214, 222, 223, 230, 234, 235, 132,
- 75, 201, 225, 182, 193, 203, 104, 210, 177, 43,
- 77, 123, 116, 49, 117, 50, 142, 178, 138, nil,
- nil, 179, nil, 219, 128, 125, nil, nil, nil, nil,
- 212, nil, 217, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 206, nil, nil, 208, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 140, 186, nil,
- nil, nil, 202, nil, nil, nil, nil, 169, 140, nil,
- 233, nil, nil, nil, nil, nil, 183, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 224, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 216, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 216 ]
-
-racc_goto_check = [
- 13, 13, 14, 35, 37, 28, 38, 52, 10, 30,
- 11, 30, 12, 23, 69, 39, 15, 61, 44, 7,
- 72, 14, 2, 2, 55, 55, 1, 8, 9, 16,
- 17, 72, 7, 7, 18, 19, 20, 21, 24, 25,
- 44, 61, 26, 15, 27, 29, 31, 33, 34, 36,
- 40, 41, 42, 43, 45, 46, 47, 48, 50, 51,
- 54, 52, 39, 58, 69, 60, 23, 62, 63, 64,
- 65, 10, 66, 11, 67, 12, 68, 70, 71, nil,
- nil, 28, nil, 52, 30, 14, nil, nil, nil, nil,
- 38, nil, 38, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 37, nil, nil, 37, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 13, 35, nil,
- nil, nil, 28, nil, nil, nil, nil, 13, 13, nil,
- 37, nil, nil, nil, nil, nil, 14, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 35, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 13, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 13 ]
-
-racc_goto_pointer = [
- nil, 26, 22, nil, nil, nil, nil, 3, 12, -5,
- -25, -23, -21, 0, -15, -18, -29, -94, -103, -102,
- -101, -100, nil, 3, 19, 28, 22, 6, -60, 25,
- -31, 26, nil, -11, -12, -41, -137, -90, -116, -154,
- -137, -135, -155, -154, -189, -161, -161, -167, -176, nil,
- -175, -62, -128, nil, 18, 24, nil, nil, -98, nil,
- -129, -177, -137, -72, 43, 27, -5, -3, -55, -117,
- -64, -49, -107 ]
-
-racc_goto_default = [
- nil, nil, nil, 3, 4, 5, 6, nil, nil, nil,
- nil, 70, 67, 74, 188, nil, nil, nil, nil, nil,
- nil, nil, 9, nil, nil, 20, nil, nil, nil, nil,
- 156, 157, 59, nil, 160, nil, 175, nil, nil, nil,
- 176, nil, nil, nil, nil, nil, nil, nil, nil, 120,
- 134, nil, nil, 158, nil, 73, 159, 161, nil, 162,
- nil, nil, nil, 205, nil, nil, nil, nil, nil, nil,
- nil, nil, nil ]
-
-racc_reduce_table = [
- 0, 0, :racc_error,
- 0, 71, :_reduce_1,
- 1, 71, :_reduce_2,
- 2, 71, :_reduce_3,
- 1, 72, :_reduce_none,
- 1, 72, :_reduce_5,
- 1, 72, :_reduce_6,
- 1, 72, :_reduce_7,
- 4, 73, :_reduce_8,
- 4, 73, :_reduce_none,
- 3, 73, :_reduce_10,
- 1, 78, :_reduce_none,
- 0, 77, :_reduce_12,
- 3, 77, :_reduce_13,
- 1, 79, :_reduce_none,
- 3, 79, :_reduce_none,
- 1, 80, :_reduce_none,
- 1, 80, :_reduce_17,
- 1, 80, :_reduce_none,
- 9, 74, :_reduce_19,
- 0, 87, :_reduce_20,
- 2, 87, :_reduce_21,
- 1, 88, :_reduce_none,
- 1, 88, :_reduce_none,
- 1, 88, :_reduce_none,
- 0, 83, :_reduce_none,
- 1, 83, :_reduce_26,
- 4, 92, :_reduce_27,
- 0, 94, :_reduce_28,
- 3, 94, :_reduce_29,
- 3, 93, :_reduce_30,
- 0, 97, :_reduce_none,
- 2, 97, :_reduce_32,
- 0, 96, :_reduce_none,
- 1, 96, :_reduce_none,
- 3, 99, :_reduce_35,
- 1, 99, :_reduce_none,
- 1, 98, :_reduce_none,
- 1, 98, :_reduce_none,
- 1, 98, :_reduce_none,
- 1, 98, :_reduce_none,
- 1, 98, :_reduce_none,
- 1, 98, :_reduce_none,
- 1, 98, :_reduce_43,
- 0, 85, :_reduce_none,
- 1, 85, :_reduce_none,
- 0, 86, :_reduce_none,
- 1, 86, :_reduce_none,
- 1, 84, :_reduce_48,
- 2, 102, :_reduce_49,
- 2, 104, :_reduce_50,
- 2, 103, :_reduce_51,
- 6, 89, :_reduce_52,
- 6, 91, :_reduce_53,
- 7, 90, :_reduce_54,
- 1, 106, :_reduce_none,
- 1, 106, :_reduce_56,
- 1, 110, :_reduce_none,
- 1, 110, :_reduce_58,
- 1, 111, :_reduce_none,
- 1, 105, :_reduce_none,
- 1, 105, :_reduce_none,
- 1, 105, :_reduce_none,
- 1, 105, :_reduce_none,
- 1, 105, :_reduce_none,
- 1, 105, :_reduce_none,
- 1, 105, :_reduce_none,
- 1, 105, :_reduce_none,
- 1, 105, :_reduce_none,
- 1, 105, :_reduce_none,
- 1, 105, :_reduce_none,
- 1, 105, :_reduce_none,
- 1, 105, :_reduce_none,
- 1, 105, :_reduce_none,
- 1, 105, :_reduce_74,
- 1, 109, :_reduce_75,
- 2, 109, :_reduce_76,
- 0, 112, :_reduce_none,
- 1, 112, :_reduce_none,
- 2, 113, :_reduce_79,
- 0, 115, :_reduce_80,
- 3, 115, :_reduce_81,
- 5, 114, :_reduce_82,
- 1, 116, :_reduce_none,
- 1, 116, :_reduce_none,
- 1, 117, :_reduce_none,
- 0, 107, :_reduce_none,
- 1, 107, :_reduce_none,
- 0, 118, :_reduce_none,
- 1, 118, :_reduce_89,
- 3, 119, :_reduce_90,
- 0, 121, :_reduce_91,
- 1, 121, :_reduce_none,
- 0, 108, :_reduce_none,
- 1, 108, :_reduce_none,
- 2, 120, :_reduce_95,
- 1, 122, :_reduce_none,
- 1, 122, :_reduce_none,
- 1, 122, :_reduce_none,
- 3, 101, :_reduce_99,
- 0, 124, :_reduce_none,
- 1, 124, :_reduce_101,
- 3, 124, :_reduce_102,
- 1, 100, :_reduce_none,
- 1, 100, :_reduce_none,
- 1, 100, :_reduce_none,
- 1, 100, :_reduce_none,
- 1, 100, :_reduce_none,
- 1, 100, :_reduce_none,
- 1, 100, :_reduce_109,
- 1, 82, :_reduce_none,
- 1, 82, :_reduce_none,
- 1, 82, :_reduce_none,
- 1, 82, :_reduce_none,
- 1, 82, :_reduce_none,
- 1, 81, :_reduce_none,
- 2, 81, :_reduce_116,
- 1, 123, :_reduce_none,
- 1, 123, :_reduce_none,
- 2, 126, :_reduce_none,
- 0, 127, :_reduce_none,
- 2, 127, :_reduce_none,
- 1, 129, :_reduce_none,
- 3, 128, :_reduce_none,
- 2, 130, :_reduce_none,
- 0, 132, :_reduce_none,
- 3, 132, :_reduce_none,
- 3, 131, :_reduce_none,
- 1, 133, :_reduce_none,
- 1, 133, :_reduce_none,
- 6, 75, :_reduce_130,
- 0, 136, :_reduce_none,
- 1, 136, :_reduce_none,
- 1, 95, :_reduce_none,
- 1, 95, :_reduce_none,
- 1, 95, :_reduce_none,
- 1, 95, :_reduce_none,
- 1, 95, :_reduce_none,
- 4, 134, :_reduce_138,
- 5, 135, :_reduce_139,
- 1, 138, :_reduce_140,
- 3, 138, :_reduce_141,
- 1, 139, :_reduce_none,
- 1, 139, :_reduce_none,
- 1, 139, :_reduce_none,
- 1, 139, :_reduce_none,
- 1, 139, :_reduce_none,
- 1, 139, :_reduce_none,
- 1, 139, :_reduce_none,
- 1, 139, :_reduce_none,
- 1, 139, :_reduce_none,
- 1, 139, :_reduce_none,
- 5, 137, :_reduce_152,
- 1, 140, :_reduce_153,
- 3, 140, :_reduce_154,
- 2, 76, :_reduce_none,
- 8, 125, :_reduce_none,
- 1, 141, :_reduce_none,
- 2, 141, :_reduce_none,
- 5, 142, :_reduce_none,
- 3, 142, :_reduce_160 ]
-
-racc_reduce_n = 161
-
-racc_shift_n = 236
-
-racc_token_table = {
- false => 0,
- :error => 1,
- "*" => 2,
- "/" => 3,
- "+" => 4,
- "-" => 5,
- :PRAGMA => 6,
- :INCLUDE => 7,
- :IDENTIFIER => 8,
- :CLASS => 9,
- :ASSOCIATION => 10,
- :INDICATION => 11,
- :AMENDED => 12,
- :ENABLEOVERRIDE => 13,
- :DISABLEOVERRIDE => 14,
- :RESTRICTED => 15,
- :TOSUBCLASS => 16,
- :TOINSTANCE => 17,
- :TRANSLATABLE => 18,
- :QUALIFIER => 19,
- :SCOPE => 20,
- :SCHEMA => 21,
- :PROPERTY => 22,
- :REFERENCE => 23,
- :METHOD => 24,
- :PARAMETER => 25,
- :FLAVOR => 26,
- :INSTANCE => 27,
- :AS => 28,
- :REF => 29,
- :ANY => 30,
- :OF => 31,
- :DT_VOID => 32,
- :DT_UINT8 => 33,
- :DT_SINT8 => 34,
- :DT_UINT16 => 35,
- :DT_SINT16 => 36,
- :DT_UINT32 => 37,
- :DT_SINT32 => 38,
- :DT_UINT64 => 39,
- :DT_SINT64 => 40,
- :DT_REAL32 => 41,
- :DT_REAL64 => 42,
- :DT_CHAR16 => 43,
- :DT_STR => 44,
- :DT_BOOLEAN => 45,
- :DT_DATETIME => 46,
- :positiveDecimalValue => 47,
- :stringValue => 48,
- :realValue => 49,
- :charValue => 50,
- :booleanValue => 51,
- :nullValue => 52,
- :binaryValue => 53,
- :octalValue => 54,
- :decimalValue => 55,
- :hexValue => 56,
- "#" => 57,
- "(" => 58,
- ")" => 59,
- "," => 60,
- "{" => 61,
- "}" => 62,
- ";" => 63,
- "[" => 64,
- "]" => 65,
- ":" => 66,
- "$" => 67,
- "=" => 68,
- "." => 69 }
-
-racc_nt_base = 70
-
-racc_use_result_var = true
-
-Racc_arg = [
- racc_action_table,
- racc_action_check,
- racc_action_default,
- racc_action_pointer,
- racc_goto_table,
- racc_goto_check,
- racc_goto_default,
- racc_goto_pointer,
- racc_nt_base,
- racc_reduce_table,
- racc_token_table,
- racc_shift_n,
- racc_reduce_n,
- racc_use_result_var ]
-Ractor.make_shareable(Racc_arg) if defined?(Ractor)
-
-Racc_token_to_s_table = [
- "$end",
- "error",
- "\"*\"",
- "\"/\"",
- "\"+\"",
- "\"-\"",
- "PRAGMA",
- "INCLUDE",
- "IDENTIFIER",
- "CLASS",
- "ASSOCIATION",
- "INDICATION",
- "AMENDED",
- "ENABLEOVERRIDE",
- "DISABLEOVERRIDE",
- "RESTRICTED",
- "TOSUBCLASS",
- "TOINSTANCE",
- "TRANSLATABLE",
- "QUALIFIER",
- "SCOPE",
- "SCHEMA",
- "PROPERTY",
- "REFERENCE",
- "METHOD",
- "PARAMETER",
- "FLAVOR",
- "INSTANCE",
- "AS",
- "REF",
- "ANY",
- "OF",
- "DT_VOID",
- "DT_UINT8",
- "DT_SINT8",
- "DT_UINT16",
- "DT_SINT16",
- "DT_UINT32",
- "DT_SINT32",
- "DT_UINT64",
- "DT_SINT64",
- "DT_REAL32",
- "DT_REAL64",
- "DT_CHAR16",
- "DT_STR",
- "DT_BOOLEAN",
- "DT_DATETIME",
- "positiveDecimalValue",
- "stringValue",
- "realValue",
- "charValue",
- "booleanValue",
- "nullValue",
- "binaryValue",
- "octalValue",
- "decimalValue",
- "hexValue",
- "\"#\"",
- "\"(\"",
- "\")\"",
- "\",\"",
- "\"{\"",
- "\"}\"",
- "\";\"",
- "\"[\"",
- "\"]\"",
- "\":\"",
- "\"$\"",
- "\"=\"",
- "\".\"",
- "$start",
- "mofSpecification",
- "mofProduction",
- "compilerDirective",
- "classDeclaration",
- "qualifierDeclaration",
- "instanceDeclaration",
- "pragmaParameters_opt",
- "pragmaName",
- "pragmaParameterValues",
- "pragmaParameterValue",
- "string",
- "integerValue",
- "qualifierList_opt",
- "className",
- "alias_opt",
- "superClass_opt",
- "classFeatures",
- "classFeature",
- "propertyDeclaration",
- "methodDeclaration",
- "referenceDeclaration",
- "qualifierList",
- "qualifier",
- "qualifiers",
- "qualifierName",
- "qualifierParameter_opt",
- "flavor_opt",
- "flavor",
- "qualifierParameter",
- "constantValue",
- "arrayInitializer",
- "alias",
- "superClass",
- "aliasIdentifier",
- "dataType",
- "propertyName",
- "array_opt",
- "defaultValue_opt",
- "objectRef",
- "referenceName",
- "methodName",
- "parameterList_opt",
- "parameterList",
- "parameter",
- "parameters",
- "typespec",
- "parameterName",
- "parameterValue_opt",
- "array",
- "defaultValue",
- "positiveDecimalValue_opt",
- "initializer",
- "referenceInitializer",
- "constantValues",
- "instance",
- "objectHandle",
- "namespace_opt",
- "modelPath",
- "namespaceHandle",
- "keyValuePairList",
- "keyValuePair",
- "keyValuePairs",
- "keyname",
- "qualifierType",
- "scope",
- "defaultFlavor_opt",
- "defaultFlavor",
- "metaElements",
- "metaElement",
- "flavors",
- "valueInitializers",
- "valueInitializer" ]
-Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)
-
-Racc_debug_parser = false
-
-##### State transition tables end #####
-
-# reduce 0 omitted
-
-module_eval(<<'.,.,', 'mof.y', 41)
- def _reduce_1(val, _values, result)
- result = Hash.new
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 43)
- def _reduce_2(val, _values, result)
- result = { @name => @result }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 45)
- def _reduce_3(val, _values, result)
- result = val[0]
- result[@name] = @result
-
- result
- end
-.,.,
-
-# reduce 4 omitted
-
-module_eval(<<'.,.,', 'mof.y', 53)
- def _reduce_5(val, _values, result)
- #puts "Class '#{val[0].name}'"
- @result.classes << val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 57)
- def _reduce_6(val, _values, result)
- @result.qualifiers << val[0]
- @qualifiers[val[0].name.downcase] = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 61)
- def _reduce_7(val, _values, result)
- @result.instances << val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 71)
- def _reduce_8(val, _values, result)
- raise MOF::Helper::Error.new(@name,@lineno,@line,"Missing filename after '#pragma include'") unless val[3]
- open val[3], :pragma
-
- result
- end
-.,.,
-
-# reduce 9 omitted
-
-module_eval(<<'.,.,', 'mof.y', 76)
- def _reduce_10(val, _values, result)
- raise StyleError.new(@name,@lineno,@line,"Use '#pragma include' instead of '#include'") unless @style == :wmi
- raise MOF::Helper::Error.new(@name,@lineno,@line,"Missing filename after '#include'") unless val[2]
- open val[2], :pragma
-
- result
- end
-.,.,
-
-# reduce 11 omitted
-
-module_eval(<<'.,.,', 'mof.y', 88)
- def _reduce_12(val, _values, result)
- raise StyleError.new(@name,@lineno,@line,"#pragma parameter missing") unless @style == :wmi
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 90)
- def _reduce_13(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-# reduce 14 omitted
-
-# reduce 15 omitted
-
-# reduce 16 omitted
-
-module_eval(<<'.,.,', 'mof.y', 101)
- def _reduce_17(val, _values, result)
- raise StyleError.new(@name,@lineno,@line,"#pragma parameter missing") unless @style == :wmi
- result
- end
-.,.,
-
-# reduce 18 omitted
-
-module_eval(<<'.,.,', 'mof.y', 112)
- def _reduce_19(val, _values, result)
- qualifiers = val[0]
- features = val[6]
- # FIXME: features must not include references
- result = CIM::Class.new(val[2],qualifiers,val[3],val[4],features)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 121)
- def _reduce_20(val, _values, result)
- result = []
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 123)
- def _reduce_21(val, _values, result)
- result = val[0] << val[1]
- result
- end
-.,.,
-
-# reduce 22 omitted
-
-# reduce 23 omitted
-
-# reduce 24 omitted
-
-# reduce 25 omitted
-
-module_eval(<<'.,.,', 'mof.y', 136)
- def _reduce_26(val, _values, result)
- result = CIM::QualifierSet.new val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 141)
- def _reduce_27(val, _values, result)
- result = val[2]
- result.unshift val[1] if val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 147)
- def _reduce_28(val, _values, result)
- result = []
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 149)
- def _reduce_29(val, _values, result)
- result = val[0]
- result << val[2] if val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 156)
- def _reduce_30(val, _values, result)
- # Get qualifier decl
- qualifier = case val[0]
- when CIM::Qualifier then val[0].definition
- when CIM::QualifierDeclaration then val[0]
- when String then @qualifiers[val[0].downcase]
- else
- nil
- end
- raise MOF::Helper::Error.new(@name,@lineno,@line,"'#{val[0]}' is not a valid qualifier") unless qualifier
- value = val[1]
- raise MOF::Helper::Error.new(@name,@lineno,@line,"#{value.inspect} does not match qualifier type '#{qualifier.type}'") unless qualifier.type.matches?(value)||@style == :wmi
- # Don't propagate a boolean 'false'
- if qualifier.type == :boolean && value == false
- result = nil
- else
- result = CIM::Qualifier.new(qualifier,value,val[2])
- end
-
- result
- end
-.,.,
-
-# reduce 31 omitted
-
-module_eval(<<'.,.,', 'mof.y', 179)
- def _reduce_32(val, _values, result)
- result = CIM::QualifierFlavors.new val[1]
- result
- end
-.,.,
-
-# reduce 33 omitted
-
-# reduce 34 omitted
-
-module_eval(<<'.,.,', 'mof.y', 189)
- def _reduce_35(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-# reduce 36 omitted
-
-# reduce 37 omitted
-
-# reduce 38 omitted
-
-# reduce 39 omitted
-
-# reduce 40 omitted
-
-# reduce 41 omitted
-
-# reduce 42 omitted
-
-module_eval(<<'.,.,', 'mof.y', 196)
- def _reduce_43(val, _values, result)
- case val[0].to_sym
- when :amended, :toinstance
- raise StyleError.new(@name,@lineno,@line,"'#{val[0]}' is not a valid flavor") unless @style == :wmi
- end
-
- result
- end
-.,.,
-
-# reduce 44 omitted
-
-# reduce 45 omitted
-
-# reduce 46 omitted
-
-# reduce 47 omitted
-
-module_eval(<<'.,.,', 'mof.y', 215)
- def _reduce_48(val, _values, result)
- raise ParseError.new("Class name must be prefixed by '<schema>_'") unless val[0].include?("_") || @style == :wmi
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 220)
- def _reduce_49(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 225)
- def _reduce_50(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 230)
- def _reduce_51(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 236)
- def _reduce_52(val, _values, result)
- if val[3]
- type = CIM::Array.new val[3],val[1]
- else
- type = val[1]
- end
- result = CIM::Property.new(type,val[2],val[0],val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 247)
- def _reduce_53(val, _values, result)
- if val[4]
- raise StyleError.new(@name,@lineno,@line,"Array not allowed in reference declaration") unless @style == :wmi
- end
- result = CIM::Reference.new(val[1],val[2],val[0],val[4])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 255)
- def _reduce_54(val, _values, result)
- result = CIM::Method.new(val[1],val[2],val[0],val[4])
- result
- end
-.,.,
-
-# reduce 55 omitted
-
-module_eval(<<'.,.,', 'mof.y', 261)
- def _reduce_56(val, _values, result)
- # tmplprov.mof has 'string Property;'
- raise StyleError.new(@name,@lineno,@line,"Invalid keyword '#{val[0]}' used for property name") unless @style == :wmi
-
- result
- end
-.,.,
-
-# reduce 57 omitted
-
-module_eval(<<'.,.,', 'mof.y', 269)
- def _reduce_58(val, _values, result)
- result = "Indication"
- result
- end
-.,.,
-
-# reduce 59 omitted
-
-# reduce 60 omitted
-
-# reduce 61 omitted
-
-# reduce 62 omitted
-
-# reduce 63 omitted
-
-# reduce 64 omitted
-
-# reduce 65 omitted
-
-# reduce 66 omitted
-
-# reduce 67 omitted
-
-# reduce 68 omitted
-
-# reduce 69 omitted
-
-# reduce 70 omitted
-
-# reduce 71 omitted
-
-# reduce 72 omitted
-
-# reduce 73 omitted
-
-module_eval(<<'.,.,', 'mof.y', 292)
- def _reduce_74(val, _values, result)
- raise StyleError.new(@name,@lineno,@line,"'void' is not a valid datatype") unless @style == :wmi
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 297)
- def _reduce_75(val, _values, result)
- # WMI uses class names as data types (without REF ?!)
- raise StyleError.new(@name,@lineno,@line,"Expected 'ref' keyword after classname '#{val[0]}'") unless @style == :wmi
- result = CIM::ReferenceType.new val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 303)
- def _reduce_76(val, _values, result)
- result = CIM::ReferenceType.new val[0]
- result
- end
-.,.,
-
-# reduce 77 omitted
-
-# reduce 78 omitted
-
-module_eval(<<'.,.,', 'mof.y', 313)
- def _reduce_79(val, _values, result)
- result = val[1].unshift val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 318)
- def _reduce_80(val, _values, result)
- result = []
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 320)
- def _reduce_81(val, _values, result)
- result = val[0] << val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 325)
- def _reduce_82(val, _values, result)
- if val[3]
- type = CIM::Array.new val[3], val[1]
- else
- type = val[1]
- end
- result = CIM::Property.new(type,val[2],val[0])
-
- result
- end
-.,.,
-
-# reduce 83 omitted
-
-# reduce 84 omitted
-
-# reduce 85 omitted
-
-# reduce 86 omitted
-
-# reduce 87 omitted
-
-# reduce 88 omitted
-
-module_eval(<<'.,.,', 'mof.y', 351)
- def _reduce_89(val, _values, result)
- raise "Default parameter value not allowed in syntax style '{@style}'" unless @style == :wmi
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 356)
- def _reduce_90(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 361)
- def _reduce_91(val, _values, result)
- result = -1
- result
- end
-.,.,
-
-# reduce 92 omitted
-
-# reduce 93 omitted
-
-# reduce 94 omitted
-
-module_eval(<<'.,.,', 'mof.y', 372)
- def _reduce_95(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-# reduce 96 omitted
-
-# reduce 97 omitted
-
-# reduce 98 omitted
-
-module_eval(<<'.,.,', 'mof.y', 383)
- def _reduce_99(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-# reduce 100 omitted
-
-module_eval(<<'.,.,', 'mof.y', 389)
- def _reduce_101(val, _values, result)
- result = [ val[0] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 391)
- def _reduce_102(val, _values, result)
- result = val[0] << val[2]
- result
- end
-.,.,
-
-# reduce 103 omitted
-
-# reduce 104 omitted
-
-# reduce 105 omitted
-
-# reduce 106 omitted
-
-# reduce 107 omitted
-
-# reduce 108 omitted
-
-module_eval(<<'.,.,', 'mof.y', 402)
- def _reduce_109(val, _values, result)
- raise "Instance as property value not allowed in syntax style '{@style}'" unless @style == :wmi
- result
- end
-.,.,
-
-# reduce 110 omitted
-
-# reduce 111 omitted
-
-# reduce 112 omitted
-
-# reduce 113 omitted
-
-# reduce 114 omitted
-
-# reduce 115 omitted
-
-module_eval(<<'.,.,', 'mof.y', 416)
- def _reduce_116(val, _values, result)
- result = val[0] + val[1]
- result
- end
-.,.,
-
-# reduce 117 omitted
-
-# reduce 118 omitted
-
-# reduce 119 omitted
-
-# reduce 120 omitted
-
-# reduce 121 omitted
-
-# reduce 122 omitted
-
-# reduce 123 omitted
-
-# reduce 124 omitted
-
-# reduce 125 omitted
-
-# reduce 126 omitted
-
-# reduce 127 omitted
-
-# reduce 128 omitted
-
-# reduce 129 omitted
-
-module_eval(<<'.,.,', 'mof.y', 471)
- def _reduce_130(val, _values, result)
- result = CIM::QualifierDeclaration.new( val[1], val[2][0], val[2][1], val[3], val[4])
- result
- end
-.,.,
-
-# reduce 131 omitted
-
-# reduce 132 omitted
-
-# reduce 133 omitted
-
-# reduce 134 omitted
-
-# reduce 135 omitted
-
-# reduce 136 omitted
-
-# reduce 137 omitted
-
-module_eval(<<'.,.,', 'mof.y', 490)
- def _reduce_138(val, _values, result)
- type = val[2].nil? ? val[1] : CIM::Array.new(val[2],val[1])
- result = [ type, val[3] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 497)
- def _reduce_139(val, _values, result)
- result = CIM::QualifierScopes.new(val[3])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 502)
- def _reduce_140(val, _values, result)
- result = [ val[0] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 504)
- def _reduce_141(val, _values, result)
- result = val[0] << val[2]
- result
- end
-.,.,
-
-# reduce 142 omitted
-
-# reduce 143 omitted
-
-# reduce 144 omitted
-
-# reduce 145 omitted
-
-# reduce 146 omitted
-
-# reduce 147 omitted
-
-# reduce 148 omitted
-
-# reduce 149 omitted
-
-# reduce 150 omitted
-
-# reduce 151 omitted
-
-module_eval(<<'.,.,', 'mof.y', 522)
- def _reduce_152(val, _values, result)
- result = CIM::QualifierFlavors.new val[3]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 527)
- def _reduce_153(val, _values, result)
- result = [ val[0] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'mof.y', 529)
- def _reduce_154(val, _values, result)
- result = val[0] << val[2]
- result
- end
-.,.,
-
-# reduce 155 omitted
-
-# reduce 156 omitted
-
-# reduce 157 omitted
-
-# reduce 158 omitted
-
-# reduce 159 omitted
-
-module_eval(<<'.,.,', 'mof.y', 553)
- def _reduce_160(val, _values, result)
- raise "Instance property '#{val[1]} must have a value" unless @style == :wmi
- result
- end
-.,.,
-
-def _reduce_none(val, _values, result)
- val[0]
-end
-
- end # class Parser
-end # module MOF
-
-
diff --git a/test/racc/regress/namae b/test/racc/regress/namae
deleted file mode 100644
index 289abc5f9d..0000000000
--- a/test/racc/regress/namae
+++ /dev/null
@@ -1,636 +0,0 @@
-#
-# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.5.2
-# from Racc grammar file "".
-#
-
-require 'racc/parser.rb'
-
-require 'singleton'
-require 'strscan'
-
-module Namae
- class Parser < Racc::Parser
-
-module_eval(<<'...end namae.y/module_eval...', 'namae.y', 135)
-
- include Singleton
-
- attr_reader :options, :input
-
- def initialize
- @input, @options = StringScanner.new(''), {
- :debug => false,
- :prefer_comma_as_separator => false,
- :comma => ',',
- :stops => ',;',
- :separator => /\s*(\band\b|\&|;)\s*/i,
- :title => /\s*\b(sir|lord|count(ess)?|(gen|adm|col|maj|capt|cmdr|lt|sgt|cpl|pvt|prof|dr|md|ph\.?d)\.?)(\s+|$)/i,
- :suffix => /\s*\b(JR|Jr|jr|SR|Sr|sr|[IVX]{2,})(\.|\b)/,
- :appellation => /\s*\b((mrs?|ms|fr|hr)\.?|miss|herr|frau)(\s+|$)/i
- }
- end
-
- def debug?
- options[:debug] || ENV['DEBUG']
- end
-
- def separator
- options[:separator]
- end
-
- def comma
- options[:comma]
- end
-
- def stops
- options[:stops]
- end
-
- def title
- options[:title]
- end
-
- def suffix
- options[:suffix]
- end
-
- def appellation
- options[:appellation]
- end
-
- def prefer_comma_as_separator?
- options[:prefer_comma_as_separator]
- end
-
- def parse(input)
- parse!(input)
- rescue => e
- warn e.message if debug?
- []
- end
-
- def parse!(string)
- input.string = normalize(string)
- reset
- do_parse
- end
-
- def normalize(string)
- string = string.strip
- string
- end
-
- def reset
- @commas, @words, @initials, @suffices, @yydebug = 0, 0, 0, 0, debug?
- self
- end
-
- private
-
- def stack
- @vstack || @racc_vstack || []
- end
-
- def last_token
- stack[-1]
- end
-
- def consume_separator
- return next_token if seen_separator?
- @commas, @words, @initials, @suffices = 0, 0, 0, 0
- [:AND, :AND]
- end
-
- def consume_comma
- @commas += 1
- [:COMMA, :COMMA]
- end
-
- def consume_word(type, word)
- @words += 1
-
- case type
- when :UWORD
- @initials += 1 if word =~ /^[[:upper:]]+\b/
- when :SUFFIX
- @suffices += 1
- end
-
- [type, word]
- end
-
- def seen_separator?
- !stack.empty? && last_token == :AND
- end
-
- def suffix?
- !@suffices.zero? || will_see_suffix?
- end
-
- def will_see_suffix?
- input.peek(8).to_s.strip.split(/\s+/)[0] =~ suffix
- end
-
- def will_see_initial?
- input.peek(6).to_s.strip.split(/\s+/)[0] =~ /^[[:upper:]]+\b/
- end
-
- def seen_full_name?
- prefer_comma_as_separator? && @words > 1 &&
- (@initials > 0 || !will_see_initial?) && !will_see_suffix?
- end
-
- def next_token
- case
- when input.nil?, input.eos?
- nil
- when input.scan(separator)
- consume_separator
- when input.scan(/\s*#{comma}\s*/)
- if @commas.zero? && !seen_full_name? || @commas == 1 && suffix?
- consume_comma
- else
- consume_separator
- end
- when input.scan(/\s+/)
- next_token
- when input.scan(title)
- consume_word(:TITLE, input.matched.strip)
- when input.scan(suffix)
- consume_word(:SUFFIX, input.matched.strip)
- when input.scan(appellation)
- [:APPELLATION, input.matched.strip]
- when input.scan(/((\\\w+)?\{[^\}]*\})*[[:upper:]][^\s#{stops}]*/)
- consume_word(:UWORD, input.matched)
- when input.scan(/((\\\w+)?\{[^\}]*\})*[[:lower:]][^\s#{stops}]*/)
- consume_word(:LWORD, input.matched)
- when input.scan(/(\\\w+)?\{[^\}]*\}[^\s#{stops}]*/)
- consume_word(:PWORD, input.matched)
- when input.scan(/('[^'\n]+')|("[^"\n]+")/)
- consume_word(:NICK, input.matched[1...-1])
- else
- raise ArgumentError,
- "Failed to parse name #{input.string.inspect}: unmatched data at offset #{input.pos}"
- end
- end
-
- def on_error(tid, value, stack)
- raise ArgumentError,
- "Failed to parse name: unexpected '#{value}' at #{stack.inspect}"
- end
-
-# -*- racc -*-
-...end namae.y/module_eval...
-##### State transition tables begin ###
-
-racc_action_table = [
- -39, 16, 32, 30, -40, 31, 33, -39, 17, -39,
- -39, -40, 67, -40, -40, 66, 53, 52, 54, -38,
- 59, -22, 39, -34, 45, 58, -38, 53, 52, 54,
- 53, 52, 54, 59, 39, 39, 62, 39, 53, 52,
- 54, 14, 12, 15, 68, 39, 7, 8, 14, 12,
- 15, 58, 39, 7, 8, 14, 22, 15, 24, 14,
- 22, 15, 24, 14, 22, 15, 30, 28, 31, 30,
- 28, 31, -19, -19, -19, 30, 42, 31, 30, 28,
- 31, -20, -20, -20, 30, 46, 31, 30, 28, 31,
- 30, 28, 31, -19, -19, -19, 53, 52, 54, 53,
- 52, 54, 39, 58, 59 ]
-
-racc_action_check = [
- 14, 1, 11, 43, 15, 43, 16, 14, 1, 14,
- 14, 15, 50, 15, 15, 49, 49, 49, 49, 12,
- 50, 12, 23, 49, 27, 37, 12, 32, 32, 32,
- 45, 45, 45, 38, 32, 40, 44, 45, 62, 62,
- 62, 0, 0, 0, 57, 62, 0, 0, 17, 17,
- 17, 60, 61, 17, 17, 9, 9, 9, 9, 20,
- 20, 20, 20, 5, 5, 5, 10, 10, 10, 21,
- 21, 21, 22, 22, 22, 24, 24, 24, 25, 25,
- 25, 28, 28, 28, 29, 29, 29, 35, 35, 35,
- 41, 41, 41, 42, 42, 42, 67, 67, 67, 73,
- 73, 73, 64, 70, 72 ]
-
-racc_action_pointer = [
- 38, 1, nil, nil, nil, 60, nil, nil, nil, 52,
- 63, 0, 19, nil, 0, 4, 6, 45, nil, nil,
- 56, 66, 69, 12, 72, 75, nil, 22, 78, 81,
- nil, nil, 24, nil, nil, 84, nil, 16, 23, nil,
- 25, 87, 90, 0, 34, 27, nil, nil, nil, 13,
- 10, nil, nil, nil, nil, nil, nil, 35, nil, nil,
- 42, 42, 35, nil, 92, nil, nil, 93, nil, nil,
- 94, nil, 94, 96, nil ]
-
-racc_action_default = [
- -1, -49, -2, -4, -5, -49, -8, -9, -10, -23,
- -49, -49, -19, -28, -30, -31, -49, -49, -6, -7,
- -49, -49, -38, -41, -49, -49, -29, -15, -22, -23,
- -30, -31, -36, 75, -3, -49, -15, -45, -42, -43,
- -41, -49, -22, -23, -14, -36, -21, -16, -24, -37,
- -26, -32, -38, -39, -40, -14, -11, -46, -47, -44,
- -45, -41, -36, -17, -49, -33, -35, -49, -48, -12,
- -45, -18, -25, -27, -13 ]
-
-racc_goto_table = [
- 3, 37, 26, 50, 56, 18, 2, 9, 47, 23,
- 1, 19, 20, 26, 73, 27, 50, 3, 60, 64,
- 23, 63, 26, 34, 9, nil, 36, 69, 21, 40,
- 44, 43, 25, 50, nil, 72, 26, 74, 71, 70,
- 55, nil, nil, 35, nil, nil, 61, 41, nil, 65,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 65 ]
-
-racc_goto_check = [
- 3, 8, 17, 16, 9, 3, 2, 7, 12, 3,
- 1, 4, 7, 17, 14, 10, 16, 3, 8, 15,
- 3, 12, 17, 2, 7, nil, 10, 9, 11, 10,
- 10, 7, 11, 16, nil, 16, 17, 9, 12, 8,
- 10, nil, nil, 11, nil, nil, 10, 11, nil, 3,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 3 ]
-
-racc_goto_pointer = [
- nil, 10, 6, 0, 6, nil, nil, 7, -22, -33,
- 5, 23, -24, nil, -53, -30, -29, -7, nil ]
-
-racc_goto_default = [
- nil, nil, nil, 51, 4, 5, 6, 29, nil, nil,
- 11, 10, nil, 48, 49, nil, 38, 13, 57 ]
-
-racc_reduce_table = [
- 0, 0, :racc_error,
- 0, 12, :_reduce_1,
- 1, 12, :_reduce_2,
- 3, 12, :_reduce_3,
- 1, 13, :_reduce_4,
- 1, 13, :_reduce_none,
- 2, 13, :_reduce_6,
- 2, 13, :_reduce_7,
- 1, 13, :_reduce_none,
- 1, 16, :_reduce_9,
- 1, 16, :_reduce_10,
- 4, 15, :_reduce_11,
- 5, 15, :_reduce_12,
- 6, 15, :_reduce_13,
- 3, 15, :_reduce_14,
- 2, 15, :_reduce_15,
- 3, 17, :_reduce_16,
- 4, 17, :_reduce_17,
- 5, 17, :_reduce_18,
- 1, 22, :_reduce_none,
- 2, 22, :_reduce_20,
- 3, 22, :_reduce_21,
- 1, 21, :_reduce_none,
- 1, 21, :_reduce_none,
- 1, 23, :_reduce_24,
- 3, 23, :_reduce_25,
- 1, 23, :_reduce_26,
- 3, 23, :_reduce_27,
- 1, 18, :_reduce_none,
- 2, 18, :_reduce_29,
- 1, 28, :_reduce_none,
- 1, 28, :_reduce_none,
- 1, 25, :_reduce_none,
- 2, 25, :_reduce_33,
- 0, 26, :_reduce_none,
- 1, 26, :_reduce_none,
- 0, 24, :_reduce_none,
- 1, 24, :_reduce_none,
- 1, 14, :_reduce_none,
- 1, 14, :_reduce_none,
- 1, 14, :_reduce_none,
- 0, 19, :_reduce_none,
- 1, 19, :_reduce_none,
- 1, 27, :_reduce_none,
- 2, 27, :_reduce_44,
- 0, 20, :_reduce_none,
- 1, 20, :_reduce_none,
- 1, 29, :_reduce_none,
- 2, 29, :_reduce_48 ]
-
-racc_reduce_n = 49
-
-racc_shift_n = 75
-
-racc_token_table = {
- false => 0,
- :error => 1,
- :COMMA => 2,
- :UWORD => 3,
- :LWORD => 4,
- :PWORD => 5,
- :NICK => 6,
- :AND => 7,
- :APPELLATION => 8,
- :TITLE => 9,
- :SUFFIX => 10 }
-
-racc_nt_base = 11
-
-racc_use_result_var = true
-
-Racc_arg = [
- racc_action_table,
- racc_action_check,
- racc_action_default,
- racc_action_pointer,
- racc_goto_table,
- racc_goto_check,
- racc_goto_default,
- racc_goto_pointer,
- racc_nt_base,
- racc_reduce_table,
- racc_token_table,
- racc_shift_n,
- racc_reduce_n,
- racc_use_result_var ]
-Ractor.make_shareable(Racc_arg) if defined?(Ractor)
-
-Racc_token_to_s_table = [
- "$end",
- "error",
- "COMMA",
- "UWORD",
- "LWORD",
- "PWORD",
- "NICK",
- "AND",
- "APPELLATION",
- "TITLE",
- "SUFFIX",
- "$start",
- "names",
- "name",
- "word",
- "display_order",
- "honorific",
- "sort_order",
- "u_words",
- "opt_suffices",
- "opt_titles",
- "last",
- "von",
- "first",
- "opt_words",
- "words",
- "opt_comma",
- "suffices",
- "u_word",
- "titles" ]
-Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)
-
-Racc_debug_parser = false
-
-##### State transition tables end #####
-
-# reduce 0 omitted
-
-module_eval(<<'.,.,', 'namae.y', 39)
- def _reduce_1(val, _values, result)
- result = []
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'namae.y', 40)
- def _reduce_2(val, _values, result)
- result = [val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'namae.y', 41)
- def _reduce_3(val, _values, result)
- result = val[0] << val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'namae.y', 43)
- def _reduce_4(val, _values, result)
- result = Name.new(:given => val[0])
- result
- end
-.,.,
-
-# reduce 5 omitted
-
-module_eval(<<'.,.,', 'namae.y', 45)
- def _reduce_6(val, _values, result)
- result = val[0].merge(:family => val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'namae.y', 46)
- def _reduce_7(val, _values, result)
- result = val[1].merge(val[0])
- result
- end
-.,.,
-
-# reduce 8 omitted
-
-module_eval(<<'.,.,', 'namae.y', 49)
- def _reduce_9(val, _values, result)
- result = Name.new(:appellation => val[0])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'namae.y', 50)
- def _reduce_10(val, _values, result)
- result = Name.new(:title => val[0])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'namae.y', 54)
- def _reduce_11(val, _values, result)
- result = Name.new(:given => val[0], :family => val[1],
- :suffix => val[2], :title => val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'namae.y', 59)
- def _reduce_12(val, _values, result)
- result = Name.new(:given => val[0], :nick => val[1],
- :family => val[2], :suffix => val[3], :title => val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'namae.y', 64)
- def _reduce_13(val, _values, result)
- result = Name.new(:given => val[0], :nick => val[1],
- :particle => val[2], :family => val[3],
- :suffix => val[4], :title => val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'namae.y', 70)
- def _reduce_14(val, _values, result)
- result = Name.new(:given => val[0], :particle => val[1],
- :family => val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'namae.y', 75)
- def _reduce_15(val, _values, result)
- result = Name.new(:particle => val[0], :family => val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'namae.y', 80)
- def _reduce_16(val, _values, result)
- result = Name.new({ :family => val[0], :suffix => val[2][0],
- :given => val[2][1] }, !!val[2][0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'namae.y', 85)
- def _reduce_17(val, _values, result)
- result = Name.new({ :particle => val[0], :family => val[1],
- :suffix => val[3][0], :given => val[3][1] }, !!val[3][0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'namae.y', 90)
- def _reduce_18(val, _values, result)
- result = Name.new({ :particle => val[0,2].join(' '), :family => val[2],
- :suffix => val[4][0], :given => val[4][1] }, !!val[4][0])
-
- result
- end
-.,.,
-
-# reduce 19 omitted
-
-module_eval(<<'.,.,', 'namae.y', 96)
- def _reduce_20(val, _values, result)
- result = val.join(' ')
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'namae.y', 97)
- def _reduce_21(val, _values, result)
- result = val.join(' ')
- result
- end
-.,.,
-
-# reduce 22 omitted
-
-# reduce 23 omitted
-
-module_eval(<<'.,.,', 'namae.y', 101)
- def _reduce_24(val, _values, result)
- result = [nil,val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'namae.y', 102)
- def _reduce_25(val, _values, result)
- result = [val[2],val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'namae.y', 103)
- def _reduce_26(val, _values, result)
- result = [val[0],nil]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'namae.y', 104)
- def _reduce_27(val, _values, result)
- result = [val[0],val[2]]
- result
- end
-.,.,
-
-# reduce 28 omitted
-
-module_eval(<<'.,.,', 'namae.y', 107)
- def _reduce_29(val, _values, result)
- result = val.join(' ')
- result
- end
-.,.,
-
-# reduce 30 omitted
-
-# reduce 31 omitted
-
-# reduce 32 omitted
-
-module_eval(<<'.,.,', 'namae.y', 112)
- def _reduce_33(val, _values, result)
- result = val.join(' ')
- result
- end
-.,.,
-
-# reduce 34 omitted
-
-# reduce 35 omitted
-
-# reduce 36 omitted
-
-# reduce 37 omitted
-
-# reduce 38 omitted
-
-# reduce 39 omitted
-
-# reduce 40 omitted
-
-# reduce 41 omitted
-
-# reduce 42 omitted
-
-# reduce 43 omitted
-
-module_eval(<<'.,.,', 'namae.y', 122)
- def _reduce_44(val, _values, result)
- result = val.join(' ')
- result
- end
-.,.,
-
-# reduce 45 omitted
-
-# reduce 46 omitted
-
-# reduce 47 omitted
-
-module_eval(<<'.,.,', 'namae.y', 127)
- def _reduce_48(val, _values, result)
- result = val.join(' ')
- result
- end
-.,.,
-
-def _reduce_none(val, _values, result)
- val[0]
-end
-
- end # class Parser
-end # module Namae
diff --git a/test/racc/regress/nasl b/test/racc/regress/nasl
deleted file mode 100644
index 4be095fda3..0000000000
--- a/test/racc/regress/nasl
+++ /dev/null
@@ -1,2550 +0,0 @@
-#
-# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.5.2
-# from Racc grammar file "".
-#
-
-require 'racc/parser.rb'
-
-
-require 'nasl/parser/tree'
-
-require 'nasl/parser/argument'
-require 'nasl/parser/array'
-require 'nasl/parser/assigment'
-require 'nasl/parser/block'
-require 'nasl/parser/break'
-require 'nasl/parser/call'
-require 'nasl/parser/comment'
-require 'nasl/parser/continue'
-require 'nasl/parser/decrement'
-require 'nasl/parser/empty'
-require 'nasl/parser/export'
-require 'nasl/parser/expression'
-require 'nasl/parser/for'
-require 'nasl/parser/foreach'
-require 'nasl/parser/function'
-require 'nasl/parser/global'
-require 'nasl/parser/identifier'
-require 'nasl/parser/if'
-require 'nasl/parser/import'
-require 'nasl/parser/include'
-require 'nasl/parser/increment'
-require 'nasl/parser/integer'
-require 'nasl/parser/ip'
-require 'nasl/parser/key_value_pair'
-require 'nasl/parser/list'
-require 'nasl/parser/local'
-require 'nasl/parser/lvalue'
-require 'nasl/parser/parameter'
-require 'nasl/parser/reference'
-require 'nasl/parser/repeat'
-require 'nasl/parser/repetition'
-require 'nasl/parser/return'
-require 'nasl/parser/string'
-require 'nasl/parser/undefined'
-require 'nasl/parser/while'
-
-module Nasl
- class Grammar < Racc::Parser
-
-module_eval(<<'...end nasl.y/module_eval...', 'nasl.y', 582)
-
-def n(cls, *args)
- begin
- Nasl.const_get(cls).new(@tree, *args)
- rescue
- puts "An exception occurred during the creation of a #{cls} instance."
- puts
- puts "The arguments passed to the constructor were:"
- puts args
- puts
- puts @tok.last.context
- puts
- raise
- end
-end
-
-def c(*args)
- n(:Comment, *args)
- args[1]
-end
-
-def on_error(type, value, stack)
- raise ParseException, "The language's grammar does not permit #{value.name} to appear here", value.context
-end
-
-def next_token
- @tok = @tkz.get_token
-
- if @first && @tok.first == :COMMENT
- n(:Comment, @tok.last)
- @tok = @tkz.get_token
- end
- @first = false
-
- return @tok
-end
-
-def parse(env, code, path)
- @first = true
- @tree = Tree.new(env)
- @tkz = Tokenizer.new(code, path)
- @tree.concat(do_parse)
-end
-
-...end nasl.y/module_eval...
-##### State transition tables begin ###
-
-racc_action_table = [
- 144, 143, 161, 162, 163, 164, 165, 166, 157, 158,
- 159, 160, 153, 152, 151, 154, 155, 156, 145, 146,
- 147, 149, 150, 82, 54, 111, 148, 81, 218, 83,
- 55, 51, 50, 56, 54, 54, 80, 11, 78, 65,
- 55, 55, 53, 63, 54, 54, 66, 95, 102, 103,
- 55, 55, 53, 53, 64, 54, 218, 269, 67, 94,
- 96, 55, 53, 53, 97, 98, 99, 100, 101, 102,
- 103, 104, 82, 53, 267, 217, 81, 130, 83, 131,
- 51, 50, 130, 54, 131, 80, 130, 54, 131, 55,
- 51, 50, 68, 55, 54, 72, 95, 147, 149, 150,
- 55, 53, 73, 148, 54, 53, 102, 103, 74, 96,
- 55, 109, 53, 97, 98, 99, 100, 101, 102, 103,
- 104, 82, 53, 112, 114, 81, 54, 83, 133, 51,
- 50, 134, 55, 136, 80, 137, 54, 138, 141, 51,
- 50, 167, 55, 54, 53, 95, 147, 149, 150, 55,
- 10, 11, 148, 54, 53, 172, 37, 37, 96, 55,
- 185, 53, 97, 98, 99, 100, 101, 102, 103, 104,
- 82, 53, 192, 195, 81, 225, 83, 226, 51, 50,
- 148, 54, 148, 80, 179, 148, 253, 55, 254, 255,
- 54, 54, 54, 256, 95, 257, 55, 55, 55, 53,
- 97, 98, 99, 100, 101, 102, 103, 96, 53, 53,
- 53, 97, 98, 99, 100, 101, 102, 103, 104, 82,
- 54, 258, 259, 81, 260, 83, 55, 51, 50, 262,
- 54, 266, 80, 268, 270, 43, 55, 273, 53, 54,
- 274, 54, 275, 95, 148, 55, 148, 55, 53, 97,
- 98, 99, 100, 101, 102, 103, 96, 53, 148, 53,
- 97, 98, 99, 100, 101, 102, 103, 104, 82, 148,
- 276, 43, 81, 300, 83, 301, 51, 50, 306, nil,
- nil, 80, 154, 155, 156, 145, 146, 147, 149, 150,
- 54, nil, 95, 148, nil, nil, 55, nil, 145, 146,
- 147, 149, 150, nil, nil, 96, 148, nil, 53, 97,
- 98, 99, 100, 101, 102, 103, 104, 82, nil, nil,
- nil, 81, nil, 83, nil, 51, 50, nil, nil, nil,
- 80, nil, 145, 146, 147, 149, 150, nil, nil, 54,
- 148, 95, nil, nil, nil, 55, nil, nil, 145, 146,
- 147, 149, 150, 94, 96, 184, 148, 53, 97, 98,
- 99, 100, 101, 102, 103, 104, 82, nil, nil, nil,
- 81, nil, 83, nil, 51, 50, nil, nil, nil, 80,
- 97, 98, 99, 100, 101, nil, nil, nil, 54, nil,
- 95, nil, nil, nil, 55, 97, 98, 99, 100, 101,
- nil, nil, nil, 96, nil, nil, 53, 97, 98, 99,
- 100, 101, 102, 103, 104, 82, nil, nil, nil, 81,
- nil, 83, nil, 51, 50, nil, nil, nil, 80, 97,
- 98, 99, 100, 101, nil, nil, nil, 54, nil, 95,
- nil, nil, nil, 55, nil, nil, nil, nil, nil, nil,
- nil, nil, 96, nil, nil, 53, 97, 98, 99, 100,
- 101, 102, 103, 104, 82, nil, nil, nil, 81, nil,
- 83, nil, 51, 50, nil, nil, nil, 80, nil, nil,
- nil, nil, nil, nil, nil, nil, 54, nil, 95, nil,
- nil, nil, 55, nil, nil, nil, nil, nil, nil, nil,
- 94, 96, nil, nil, 53, 97, 98, 99, 100, 101,
- 102, 103, 104, 82, nil, nil, nil, 81, nil, 83,
- nil, 51, 50, nil, nil, nil, 80, nil, nil, nil,
- nil, nil, nil, nil, nil, 54, nil, 95, nil, nil,
- nil, 55, nil, nil, nil, nil, nil, nil, nil, nil,
- 96, nil, nil, 53, 97, 98, 99, 100, 101, 102,
- 103, 104, 82, nil, nil, nil, 81, nil, 83, nil,
- 51, 50, nil, nil, nil, 80, nil, nil, nil, nil,
- nil, nil, nil, nil, 54, nil, 95, nil, nil, nil,
- 55, nil, nil, nil, nil, nil, nil, nil, nil, 96,
- nil, nil, 53, 97, 98, 99, 100, 101, 102, 103,
- 104, 82, nil, nil, nil, 81, nil, 83, nil, 51,
- 50, nil, nil, nil, 80, nil, nil, nil, nil, nil,
- nil, nil, nil, 54, nil, 95, nil, nil, nil, 55,
- nil, nil, nil, nil, nil, nil, nil, nil, 96, nil,
- nil, 53, 97, 98, 99, 100, 101, 102, 103, 104,
- 82, nil, nil, nil, 81, nil, 83, nil, 51, 50,
- nil, nil, nil, 80, nil, nil, nil, nil, nil, nil,
- nil, nil, 54, nil, 95, nil, nil, nil, 55, nil,
- nil, nil, nil, nil, nil, nil, nil, 96, nil, nil,
- 53, 97, 98, 99, 100, 101, 102, 103, 104, 82,
- nil, nil, nil, 81, nil, 83, nil, 51, 50, nil,
- nil, nil, 80, nil, nil, nil, nil, nil, nil, nil,
- nil, 54, nil, 95, nil, nil, nil, 55, nil, nil,
- nil, nil, nil, nil, nil, nil, 96, nil, nil, 53,
- 97, 98, 99, 100, 101, 102, 103, 104, 82, nil,
- nil, nil, 81, nil, 83, nil, 51, 50, nil, nil,
- nil, 80, nil, nil, nil, nil, nil, nil, nil, nil,
- 54, nil, 95, nil, nil, nil, 55, nil, nil, nil,
- nil, nil, nil, nil, nil, 96, nil, nil, 53, 97,
- 98, 99, 100, 101, 102, 103, 104, 82, nil, nil,
- nil, 81, nil, 83, nil, 51, 50, nil, nil, nil,
- 80, nil, nil, nil, nil, nil, nil, nil, nil, 54,
- nil, 95, nil, nil, nil, 55, nil, nil, nil, nil,
- nil, nil, nil, nil, 96, nil, nil, 53, 97, 98,
- 99, 100, 101, 102, 103, 104, 82, nil, nil, nil,
- 81, nil, 83, nil, 51, 50, nil, nil, nil, 80,
- nil, nil, nil, nil, nil, nil, nil, nil, 54, nil,
- 95, nil, nil, nil, 55, nil, nil, nil, nil, nil,
- nil, nil, nil, 96, nil, nil, 53, 97, 98, 99,
- 100, 101, 102, 103, 104, 82, nil, nil, nil, 81,
- nil, 83, nil, 51, 50, nil, nil, nil, 80, 208,
- nil, nil, nil, nil, nil, nil, nil, 54, nil, 95,
- nil, nil, nil, 55, nil, nil, nil, nil, nil, nil,
- nil, 94, 96, nil, nil, 53, 97, 98, 99, 100,
- 101, 102, 103, 104, 82, nil, nil, nil, 81, nil,
- 83, nil, 51, 50, nil, nil, nil, 80, nil, nil,
- nil, nil, nil, nil, nil, nil, 54, nil, 95, nil,
- nil, nil, 55, nil, nil, nil, nil, nil, nil, nil,
- nil, 96, nil, nil, 53, 97, 98, 99, 100, 101,
- 102, 103, 104, 82, nil, nil, nil, 81, nil, 83,
- nil, 51, 50, nil, nil, nil, 80, nil, nil, nil,
- nil, nil, nil, nil, nil, 54, nil, 95, nil, nil,
- nil, 55, nil, nil, nil, nil, nil, nil, nil, 94,
- 96, nil, nil, 53, 97, 98, 99, 100, 101, 102,
- 103, 104, 82, nil, nil, nil, 81, nil, 83, nil,
- 51, 50, nil, nil, nil, 80, nil, nil, nil, nil,
- nil, nil, nil, nil, 54, nil, 95, nil, nil, nil,
- 55, nil, nil, nil, nil, nil, nil, nil, nil, 96,
- nil, nil, 53, 97, 98, 99, 100, 101, 102, 103,
- 104, 82, nil, nil, nil, 81, nil, 83, nil, 51,
- 50, nil, nil, nil, 80, nil, nil, nil, nil, nil,
- nil, nil, nil, 54, nil, 95, nil, nil, nil, 55,
- nil, nil, nil, nil, nil, nil, nil, nil, 96, nil,
- nil, 53, 97, 98, 99, 100, 101, 102, 103, 104,
- 82, nil, nil, nil, 81, nil, 83, nil, 51, 50,
- nil, nil, nil, 80, nil, nil, nil, nil, nil, nil,
- nil, nil, 54, nil, 95, nil, nil, nil, 55, nil,
- nil, nil, nil, nil, nil, nil, nil, 96, nil, nil,
- 53, 97, 98, 99, 100, 101, 102, 103, 104, 82,
- nil, nil, nil, 81, nil, 83, nil, 51, 50, nil,
- nil, nil, 80, nil, nil, nil, nil, nil, nil, nil,
- nil, 54, nil, 95, nil, nil, nil, 55, nil, nil,
- nil, nil, nil, nil, nil, nil, 96, nil, nil, 53,
- 97, 98, 99, 100, 101, 102, 103, 104, 82, nil,
- nil, nil, 81, nil, 83, nil, 51, 50, nil, nil,
- nil, 80, nil, nil, nil, nil, nil, nil, nil, nil,
- 54, nil, 95, nil, nil, nil, 55, nil, nil, nil,
- nil, nil, nil, nil, nil, 96, nil, nil, 53, 97,
- 98, 99, 100, 101, 102, 103, 104, 82, nil, nil,
- nil, 81, nil, 83, nil, 51, 50, nil, nil, nil,
- 80, nil, nil, nil, nil, nil, nil, nil, nil, 54,
- nil, 95, nil, nil, nil, 55, nil, nil, nil, nil,
- nil, nil, nil, nil, 96, nil, nil, 53, 97, 98,
- 99, 100, 101, 102, 103, 104, 82, nil, nil, nil,
- 81, nil, 83, nil, 51, 50, nil, nil, nil, 80,
- nil, nil, nil, nil, nil, nil, nil, nil, 54, nil,
- 95, nil, nil, nil, 55, nil, nil, nil, nil, nil,
- nil, nil, nil, 96, nil, nil, 53, 97, 98, 99,
- 100, 101, 102, 103, 104, 82, nil, nil, nil, 81,
- nil, 83, nil, 51, 50, nil, nil, nil, 80, nil,
- nil, nil, nil, nil, nil, nil, nil, 54, nil, 95,
- nil, nil, nil, 55, nil, nil, nil, nil, nil, nil,
- nil, nil, 96, nil, nil, 53, 97, 98, 99, 100,
- 101, 102, 103, 104, 82, nil, nil, nil, 81, nil,
- 83, nil, 51, 50, nil, nil, nil, 80, nil, nil,
- nil, nil, nil, nil, nil, nil, 54, nil, 95, nil,
- nil, nil, 55, nil, nil, nil, nil, nil, nil, nil,
- nil, 96, nil, nil, 53, 97, 98, 99, 100, 101,
- 102, 103, 104, 82, nil, nil, nil, 81, nil, 83,
- nil, 51, 50, nil, nil, nil, 80, nil, nil, nil,
- nil, nil, nil, nil, nil, 54, nil, 95, nil, nil,
- nil, 55, nil, nil, nil, nil, nil, nil, nil, nil,
- 96, nil, nil, 53, 97, 98, 99, 100, 101, 102,
- 103, 104, 82, nil, nil, nil, 81, nil, 83, nil,
- 51, 50, nil, nil, nil, 80, nil, nil, nil, nil,
- nil, nil, nil, nil, 54, nil, 95, nil, nil, nil,
- 55, nil, nil, nil, nil, nil, nil, nil, nil, 96,
- nil, nil, 53, 97, 98, 99, 100, 101, 102, 103,
- 104, 82, nil, nil, nil, 81, nil, 83, nil, 51,
- 50, nil, nil, nil, 80, nil, nil, nil, nil, nil,
- nil, nil, nil, 54, nil, 95, nil, nil, nil, 55,
- nil, nil, nil, nil, nil, nil, nil, nil, 96, nil,
- nil, 53, 97, 98, 99, 100, 101, 102, 103, 104,
- 82, nil, nil, nil, 81, nil, 83, nil, 51, 50,
- nil, nil, nil, 80, nil, nil, nil, nil, nil, nil,
- nil, nil, 54, nil, 95, nil, nil, nil, 55, nil,
- nil, nil, nil, nil, nil, nil, nil, 96, nil, nil,
- 53, 97, 98, 99, 100, 101, 102, 103, 104, 82,
- nil, nil, nil, 81, nil, 83, nil, 51, 50, nil,
- nil, nil, 80, nil, nil, nil, nil, nil, nil, nil,
- nil, 54, nil, 95, nil, nil, nil, 55, nil, nil,
- nil, nil, nil, nil, nil, nil, 96, nil, nil, 53,
- 97, 98, 99, 100, 101, 102, 103, 104, 82, nil,
- nil, nil, 81, nil, 83, nil, 51, 50, nil, nil,
- nil, 80, nil, nil, nil, nil, nil, nil, nil, nil,
- 54, nil, 95, nil, nil, nil, 55, nil, nil, nil,
- nil, nil, nil, nil, nil, 96, nil, nil, 53, 97,
- 98, 99, 100, 101, 102, 103, 104, 82, nil, nil,
- nil, 81, nil, 83, nil, 51, 50, nil, nil, nil,
- 80, nil, nil, nil, nil, nil, nil, nil, nil, 54,
- nil, 95, nil, nil, nil, 55, nil, nil, nil, nil,
- nil, nil, nil, nil, 96, nil, nil, 53, 97, 98,
- 99, 100, 101, 102, 103, 104, 82, nil, nil, nil,
- 81, nil, 83, nil, 51, 50, nil, nil, nil, 80,
- nil, nil, nil, nil, nil, nil, nil, nil, 54, nil,
- 95, nil, nil, nil, 55, nil, nil, nil, nil, nil,
- nil, nil, nil, 96, nil, nil, 53, 97, 98, 99,
- 100, 101, 102, 103, 104, 82, nil, nil, nil, 81,
- nil, 83, nil, 51, 50, nil, nil, nil, 80, nil,
- nil, nil, nil, nil, nil, nil, nil, 54, nil, 95,
- nil, nil, nil, 55, nil, nil, nil, nil, nil, nil,
- nil, nil, 96, nil, nil, 53, 97, 98, 99, 100,
- 101, 102, 103, 104, 82, nil, nil, nil, 81, nil,
- 83, nil, 51, 50, nil, nil, nil, 80, nil, nil,
- nil, nil, nil, nil, nil, nil, 54, nil, 95, nil,
- nil, nil, 55, nil, nil, nil, nil, nil, nil, nil,
- nil, 96, nil, nil, 53, 97, 98, 99, 100, 101,
- 102, 103, 104, 82, nil, nil, nil, 81, nil, 83,
- nil, 51, 50, nil, nil, nil, 80, nil, nil, nil,
- nil, nil, nil, nil, nil, 54, nil, 95, nil, nil,
- nil, 55, nil, nil, nil, nil, nil, nil, nil, nil,
- 96, nil, nil, 53, 97, 98, 99, 100, 101, 102,
- 103, 104, 82, nil, nil, nil, 81, nil, 83, nil,
- 51, 50, nil, nil, nil, 80, nil, nil, nil, nil,
- nil, nil, nil, nil, 54, nil, 95, nil, nil, nil,
- 55, nil, nil, nil, nil, nil, nil, nil, nil, 96,
- nil, nil, 53, 97, 98, 99, 100, 101, 102, 103,
- 104, 82, nil, nil, nil, 81, nil, 83, nil, 51,
- 50, nil, nil, nil, 80, nil, nil, nil, nil, nil,
- nil, nil, nil, 54, nil, 95, nil, nil, nil, 55,
- nil, nil, nil, nil, nil, nil, nil, nil, 96, nil,
- nil, 53, 97, 98, 99, 100, 101, 102, 103, 104,
- 82, nil, nil, nil, 81, nil, 83, nil, 51, 50,
- nil, nil, nil, 80, nil, nil, nil, nil, nil, nil,
- nil, nil, 54, nil, 95, nil, nil, nil, 55, nil,
- nil, nil, nil, nil, nil, nil, nil, 96, nil, nil,
- 53, 97, 98, 99, 100, 101, 102, 103, 104, 82,
- nil, nil, nil, 81, nil, 83, nil, 51, 50, nil,
- nil, nil, 80, nil, nil, nil, nil, nil, nil, nil,
- nil, 54, nil, 95, nil, nil, nil, 55, nil, nil,
- nil, nil, nil, nil, nil, nil, 96, nil, nil, 53,
- 97, 98, 99, 100, 101, 102, 103, 104, 82, nil,
- nil, nil, 81, nil, 83, nil, 51, 50, nil, nil,
- nil, 80, nil, nil, nil, nil, nil, nil, nil, nil,
- 54, nil, 95, nil, nil, nil, 55, nil, nil, nil,
- nil, nil, nil, nil, nil, 96, nil, nil, 53, 97,
- 98, 99, 100, 101, 102, 103, 104, 82, nil, nil,
- nil, 81, nil, 83, nil, 51, 50, nil, nil, nil,
- 80, nil, nil, nil, nil, nil, nil, nil, nil, 54,
- nil, 95, nil, nil, nil, 55, nil, nil, nil, nil,
- nil, nil, nil, nil, 96, nil, nil, 53, 97, 98,
- 99, 100, 101, 102, 103, 104, 82, nil, nil, nil,
- 81, nil, 83, nil, 51, 50, nil, nil, nil, 80,
- nil, nil, nil, nil, nil, nil, nil, nil, 54, nil,
- 95, nil, nil, nil, 55, nil, nil, nil, nil, nil,
- nil, nil, 94, 96, nil, nil, 53, 97, 98, 99,
- 100, 101, 102, 103, 104, 82, nil, nil, nil, 81,
- nil, 83, nil, 51, 50, nil, nil, nil, 80, nil,
- nil, nil, nil, nil, nil, nil, nil, 54, nil, 95,
- nil, nil, nil, 55, nil, nil, nil, nil, nil, nil,
- nil, 94, 96, nil, nil, 53, 97, 98, 99, 100,
- 101, 102, 103, 104, 82, nil, nil, nil, 81, nil,
- 83, nil, 51, 50, nil, nil, nil, 80, nil, nil,
- nil, nil, nil, nil, nil, nil, 54, nil, 95, nil,
- nil, nil, 55, nil, nil, nil, nil, nil, nil, nil,
- 94, 96, nil, nil, 53, 97, 98, 99, 100, 101,
- 102, 103, 104, 82, nil, nil, nil, 81, nil, 83,
- nil, 51, 50, nil, nil, nil, 80, nil, nil, nil,
- nil, nil, nil, nil, nil, 54, nil, 95, nil, nil,
- nil, 55, nil, nil, nil, nil, nil, nil, nil, 94,
- 96, nil, nil, 53, 97, 98, 99, 100, 101, 102,
- 103, 104, 82, nil, nil, nil, 81, nil, 83, nil,
- 51, 50, nil, nil, nil, 80, nil, nil, nil, nil,
- nil, nil, nil, nil, 54, nil, 95, nil, nil, nil,
- 55, nil, nil, nil, nil, nil, nil, nil, nil, 96,
- nil, nil, 53, 97, 98, 99, 100, 101, 102, 103,
- 104, 82, nil, nil, nil, 81, nil, 83, nil, 51,
- 50, nil, nil, nil, 80, nil, nil, nil, nil, nil,
- nil, nil, nil, 54, nil, 95, nil, nil, nil, 55,
- nil, nil, nil, nil, nil, nil, nil, nil, 96, nil,
- nil, 53, 97, 98, 99, 100, 101, 102, 103, 104,
- 82, nil, nil, nil, 81, nil, 83, nil, 51, 50,
- nil, nil, nil, 80, nil, nil, nil, nil, nil, nil,
- nil, nil, 54, nil, 95, nil, nil, nil, 55, nil,
- nil, nil, nil, nil, nil, nil, 94, 96, nil, nil,
- 53, 97, 98, 99, 100, 101, 102, 103, 104, 82,
- nil, nil, nil, 81, nil, 83, nil, 51, 50, nil,
- nil, nil, 80, nil, nil, nil, nil, nil, nil, nil,
- nil, 54, nil, 95, nil, nil, nil, 55, nil, nil,
- nil, nil, nil, nil, nil, 94, 96, nil, nil, 53,
- 97, 98, 99, 100, 101, 102, 103, 104, 115, 116,
- 117, 118, 119, 120, 123, 122, 121, 115, 116, 117,
- 118, 119, 120, 123, 122, 121, 153, 152, 151, 154,
- 155, 156, 145, 146, 147, 149, 150, nil, nil, nil,
- 148, nil, nil, nil, 126, 125, nil, nil, nil, 124,
- nil, nil, nil, 126, 125, nil, nil, nil, 124, 144,
- 143, 161, 162, 163, 164, 165, 166, 157, 158, 159,
- 160, 153, 152, 151, 154, 155, 156, 145, 146, 147,
- 149, 150, nil, nil, nil, 148, nil, nil, nil, nil,
- nil, nil, nil, 142, 144, 143, 161, 162, 163, 164,
- 165, 166, 157, 158, 159, 160, 153, 152, 151, 154,
- 155, 156, 145, 146, 147, 149, 150, nil, nil, nil,
- 148, nil, nil, nil, nil, nil, nil, nil, 221, 144,
- 143, 161, 162, 163, 164, 165, 166, 157, 158, 159,
- 160, 153, 152, 151, 154, 155, 156, 145, 146, 147,
- 149, 150, nil, nil, nil, 148, nil, nil, nil, nil,
- nil, nil, nil, 289, 144, 143, 161, 162, 163, 164,
- 165, 166, 157, 158, 159, 160, 153, 152, 151, 154,
- 155, 156, 145, 146, 147, 149, 150, nil, nil, nil,
- 148, nil, nil, nil, nil, nil, nil, nil, 297, 144,
- 143, 161, 162, 163, 164, 165, 166, 157, 158, 159,
- 160, 153, 152, 151, 154, 155, 156, 145, 146, 147,
- 149, 150, nil, nil, nil, 148, nil, nil, nil, nil,
- nil, nil, 251, 144, 143, 161, 162, 163, 164, 165,
- 166, 157, 158, 159, 160, 153, 152, 151, 154, 155,
- 156, 145, 146, 147, 149, 150, nil, nil, nil, 148,
- nil, nil, nil, nil, nil, nil, 263, 144, 143, 161,
- 162, 163, 164, 165, 166, 157, 158, 159, 160, 153,
- 152, 151, 154, 155, 156, 145, 146, 147, 149, 150,
- nil, nil, nil, 148, nil, nil, nil, nil, nil, nil,
- 265, 144, 143, 161, 162, 163, 164, 165, 166, 157,
- 158, 159, 160, 153, 152, 151, 154, 155, 156, 145,
- 146, 147, 149, 150, nil, nil, nil, 148, nil, nil,
- nil, nil, nil, nil, 286, 144, 143, 161, 162, 163,
- 164, 165, 166, 157, 158, 159, 160, 153, 152, 151,
- 154, 155, 156, 145, 146, 147, 149, 150, nil, nil,
- nil, 148, 51, 50, 4, 10, 11, nil, 299, 36,
- 32, 34, 37, 39, 40, 41, 54, 42, 43, nil,
- 44, 45, 55, 46, nil, 47, nil, 48, 51, 50,
- 4, 10, 11, nil, 53, 36, 32, 34, 37, 39,
- 40, 41, 54, 42, 43, nil, 44, 45, 55, 46,
- nil, 47, nil, 48, 51, 50, 105, nil, nil, nil,
- 53, 36, 32, 34, 37, 39, 40, 41, 54, 42,
- 43, 107, 44, 45, 55, 46, nil, 47, nil, 48,
- 51, 50, 105, nil, nil, nil, 53, 36, 32, 34,
- 37, 39, 40, 41, 54, 42, 43, nil, 44, 45,
- 55, 46, nil, 47, nil, 48, 51, 50, 105, nil,
- nil, nil, 53, 36, 32, 34, 37, 39, 40, 41,
- 54, 42, 43, nil, 44, 45, 55, 46, nil, 47,
- nil, 48, 51, 50, 105, nil, nil, nil, 53, 36,
- 32, 34, 37, 39, 40, 41, 54, 42, 43, nil,
- 44, 45, 55, 46, nil, 47, nil, 48, 51, 50,
- 105, nil, nil, nil, 53, 36, 32, 34, 37, 39,
- 40, 41, 54, 42, 43, nil, 44, 45, 55, 46,
- nil, 47, nil, 48, 51, 50, 105, nil, nil, nil,
- 53, 36, 32, 34, 37, 39, 40, 41, 54, 42,
- 43, nil, 44, 45, 55, 46, nil, 47, nil, 48,
- 51, 50, 105, nil, nil, nil, 53, 36, 32, 34,
- 37, 39, 40, 41, 54, 42, 43, nil, 44, 45,
- 55, 46, nil, 47, nil, 48, 51, 50, 105, nil,
- nil, nil, 53, 36, 32, 34, 37, 39, 40, 41,
- 54, 42, 43, nil, 44, 45, 55, 46, nil, 47,
- nil, 48, 51, 50, 105, nil, nil, nil, 53, 36,
- 32, 34, 37, 39, 40, 41, 54, 42, 43, nil,
- 44, 45, 55, 46, nil, 47, nil, 48, nil, nil,
- nil, nil, nil, nil, 53, 144, 143, 161, 162, 163,
- 164, 165, 166, 157, 158, 159, 160, 153, 152, 151,
- 154, 155, 156, 145, 146, 147, 149, 150, nil, nil,
- nil, 148, 144, 143, 161, 162, 163, 164, 165, 166,
- 157, 158, 159, 160, 153, 152, 151, 154, 155, 156,
- 145, 146, 147, 149, 150, nil, nil, nil, 148, 144,
- 143, 161, 162, 163, 164, 165, 166, 157, 158, 159,
- 160, 153, 152, 151, 154, 155, 156, 145, 146, 147,
- 149, 150, nil, nil, nil, 148, 144, 143, 161, 162,
- 163, 164, 165, 166, 157, 158, 159, 160, 153, 152,
- 151, 154, 155, 156, 145, 146, 147, 149, 150, nil,
- nil, nil, 148, 144, 143, 161, 162, 163, 164, 165,
- 166, 157, 158, 159, 160, 153, 152, 151, 154, 155,
- 156, 145, 146, 147, 149, 150, nil, nil, nil, 148,
- 144, 143, 161, 162, 163, 164, 165, 166, 157, 158,
- 159, 160, 153, 152, 151, 154, 155, 156, 145, 146,
- 147, 149, 150, nil, nil, nil, 148, 144, 143, 161,
- 162, 163, 164, 165, 166, 157, 158, 159, 160, 153,
- 152, 151, 154, 155, 156, 145, 146, 147, 149, 150,
- nil, nil, nil, 148, 144, 143, 161, 162, 163, 164,
- 165, 166, 157, 158, 159, 160, 153, 152, 151, 154,
- 155, 156, 145, 146, 147, 149, 150, nil, nil, nil,
- 148, 144, 143, 161, 162, 163, 164, 165, 166, 157,
- 158, 159, 160, 153, 152, 151, 154, 155, 156, 145,
- 146, 147, 149, 150, nil, nil, nil, 148, 144, 143,
- 161, 162, 163, 164, 165, 166, 157, 158, 159, 160,
- 153, 152, 151, 154, 155, 156, 145, 146, 147, 149,
- 150, nil, nil, nil, 148, 144, 143, 161, 162, 163,
- 164, 165, 166, 157, 158, 159, 160, 153, 152, 151,
- 154, 155, 156, 145, 146, 147, 149, 150, nil, nil,
- nil, 148, 144, 143, 161, 162, 163, 164, 165, 166,
- 157, 158, 159, 160, 153, 152, 151, 154, 155, 156,
- 145, 146, 147, 149, 150, nil, nil, nil, 148, 144,
- 143, 161, 162, 163, 164, 165, 166, 157, 158, 159,
- 160, 153, 152, 151, 154, 155, 156, 145, 146, 147,
- 149, 150, nil, nil, nil, 148, 144, 143, 161, 162,
- 163, 164, 165, 166, 157, 158, 159, 160, 153, 152,
- 151, 154, 155, 156, 145, 146, 147, 149, 150, nil,
- nil, nil, 148, 144, 143, 161, 162, 163, 164, 165,
- 166, 157, 158, 159, 160, 153, 152, 151, 154, 155,
- 156, 145, 146, 147, 149, 150, nil, nil, nil, 148,
- 144, 143, 161, 162, 163, 164, 165, 166, 157, 158,
- 159, 160, 153, 152, 151, 154, 155, 156, 145, 146,
- 147, 149, 150, nil, nil, nil, 148, 143, 161, 162,
- 163, 164, 165, 166, 157, 158, 159, 160, 153, 152,
- 151, 154, 155, 156, 145, 146, 147, 149, 150, nil,
- nil, nil, 148, 161, 162, 163, 164, 165, 166, 157,
- 158, 159, 160, 153, 152, 151, 154, 155, 156, 145,
- 146, 147, 149, 150, nil, nil, nil, 148, 153, 152,
- 151, 154, 155, 156, 145, 146, 147, 149, 150, nil,
- nil, nil, 148, 153, 152, 151, 154, 155, 156, 145,
- 146, 147, 149, 150, nil, nil, nil, 148, 153, 152,
- 151, 154, 155, 156, 145, 146, 147, 149, 150, nil,
- nil, nil, 148, 153, 152, 151, 154, 155, 156, 145,
- 146, 147, 149, 150, nil, nil, nil, 148, 153, 152,
- 151, 154, 155, 156, 145, 146, 147, 149, 150, nil,
- nil, nil, 148, 153, 152, 151, 154, 155, 156, 145,
- 146, 147, 149, 150, nil, nil, nil, 148, 153, 152,
- 151, 154, 155, 156, 145, 146, 147, 149, 150, nil,
- nil, nil, 148, 153, 152, 151, 154, 155, 156, 145,
- 146, 147, 149, 150, nil, nil, nil, 148, 153, 152,
- 151, 154, 155, 156, 145, 146, 147, 149, 150, nil,
- nil, nil, 148, 152, 151, 154, 155, 156, 145, 146,
- 147, 149, 150, nil, nil, nil, 148, 151, 154, 155,
- 156, 145, 146, 147, 149, 150, nil, nil, nil, 148 ]
-
-racc_action_check = [
- 213, 213, 213, 213, 213, 213, 213, 213, 213, 213,
- 213, 213, 213, 213, 213, 213, 213, 213, 213, 213,
- 213, 213, 213, 42, 11, 45, 213, 42, 273, 42,
- 11, 42, 42, 1, 45, 37, 42, 10, 42, 33,
- 45, 37, 11, 31, 41, 42, 33, 42, 73, 73,
- 41, 42, 45, 37, 32, 273, 134, 213, 34, 42,
- 42, 273, 41, 42, 42, 42, 42, 42, 42, 42,
- 42, 42, 66, 273, 210, 134, 66, 210, 66, 210,
- 66, 66, 52, 134, 52, 66, 132, 50, 132, 134,
- 109, 109, 35, 50, 66, 38, 66, 229, 229, 229,
- 66, 134, 39, 229, 109, 50, 74, 74, 40, 66,
- 109, 44, 66, 66, 66, 66, 66, 66, 66, 66,
- 66, 80, 109, 46, 48, 80, 51, 80, 56, 80,
- 80, 62, 51, 69, 80, 70, 94, 71, 75, 297,
- 297, 77, 94, 80, 51, 80, 230, 230, 230, 80,
- 4, 4, 230, 297, 94, 90, 105, 4, 80, 297,
- 106, 80, 80, 80, 80, 80, 80, 80, 80, 80,
- 81, 297, 110, 113, 81, 139, 81, 140, 81, 81,
- 169, 95, 170, 81, 95, 171, 174, 95, 175, 176,
- 111, 131, 81, 177, 81, 178, 111, 131, 81, 95,
- 95, 95, 95, 95, 95, 95, 95, 81, 111, 131,
- 81, 81, 81, 81, 81, 81, 81, 81, 81, 82,
- 138, 182, 183, 82, 187, 82, 138, 82, 82, 193,
- 256, 207, 82, 212, 216, 217, 256, 220, 138, 218,
- 225, 82, 226, 82, 231, 218, 232, 82, 256, 256,
- 256, 256, 256, 256, 256, 256, 82, 218, 233, 82,
- 82, 82, 82, 82, 82, 82, 82, 82, 83, 234,
- 252, 270, 83, 288, 83, 296, 83, 83, 302, nil,
- nil, 83, 235, 235, 235, 235, 235, 235, 235, 235,
- 83, nil, 83, 235, nil, nil, 83, nil, 238, 238,
- 238, 238, 238, nil, nil, 83, 238, nil, 83, 83,
- 83, 83, 83, 83, 83, 83, 83, 96, nil, nil,
- nil, 96, nil, 96, nil, 96, 96, nil, nil, nil,
- 96, nil, 239, 239, 239, 239, 239, nil, nil, 96,
- 239, 96, nil, nil, nil, 96, nil, nil, 240, 240,
- 240, 240, 240, 96, 96, 96, 240, 96, 96, 96,
- 96, 96, 96, 96, 96, 96, 112, nil, nil, nil,
- 112, nil, 112, nil, 112, 112, nil, nil, nil, 112,
- 172, 172, 172, 172, 172, nil, nil, nil, 112, nil,
- 112, nil, nil, nil, 112, 276, 276, 276, 276, 276,
- nil, nil, nil, 112, nil, nil, 112, 112, 112, 112,
- 112, 112, 112, 112, 112, 114, nil, nil, nil, 114,
- nil, 114, nil, 114, 114, nil, nil, nil, 114, 301,
- 301, 301, 301, 301, nil, nil, nil, 114, nil, 114,
- nil, nil, nil, 114, nil, nil, nil, nil, nil, nil,
- nil, nil, 114, nil, nil, 114, 114, 114, 114, 114,
- 114, 114, 114, 114, 115, nil, nil, nil, 115, nil,
- 115, nil, 115, 115, nil, nil, nil, 115, nil, nil,
- nil, nil, nil, nil, nil, nil, 115, nil, 115, nil,
- nil, nil, 115, nil, nil, nil, nil, nil, nil, nil,
- 115, 115, nil, nil, 115, 115, 115, 115, 115, 115,
- 115, 115, 115, 116, nil, nil, nil, 116, nil, 116,
- nil, 116, 116, nil, nil, nil, 116, nil, nil, nil,
- nil, nil, nil, nil, nil, 116, nil, 116, nil, nil,
- nil, 116, nil, nil, nil, nil, nil, nil, nil, nil,
- 116, nil, nil, 116, 116, 116, 116, 116, 116, 116,
- 116, 116, 117, nil, nil, nil, 117, nil, 117, nil,
- 117, 117, nil, nil, nil, 117, nil, nil, nil, nil,
- nil, nil, nil, nil, 117, nil, 117, nil, nil, nil,
- 117, nil, nil, nil, nil, nil, nil, nil, nil, 117,
- nil, nil, 117, 117, 117, 117, 117, 117, 117, 117,
- 117, 118, nil, nil, nil, 118, nil, 118, nil, 118,
- 118, nil, nil, nil, 118, nil, nil, nil, nil, nil,
- nil, nil, nil, 118, nil, 118, nil, nil, nil, 118,
- nil, nil, nil, nil, nil, nil, nil, nil, 118, nil,
- nil, 118, 118, 118, 118, 118, 118, 118, 118, 118,
- 119, nil, nil, nil, 119, nil, 119, nil, 119, 119,
- nil, nil, nil, 119, nil, nil, nil, nil, nil, nil,
- nil, nil, 119, nil, 119, nil, nil, nil, 119, nil,
- nil, nil, nil, nil, nil, nil, nil, 119, nil, nil,
- 119, 119, 119, 119, 119, 119, 119, 119, 119, 120,
- nil, nil, nil, 120, nil, 120, nil, 120, 120, nil,
- nil, nil, 120, nil, nil, nil, nil, nil, nil, nil,
- nil, 120, nil, 120, nil, nil, nil, 120, nil, nil,
- nil, nil, nil, nil, nil, nil, 120, nil, nil, 120,
- 120, 120, 120, 120, 120, 120, 120, 120, 121, nil,
- nil, nil, 121, nil, 121, nil, 121, 121, nil, nil,
- nil, 121, nil, nil, nil, nil, nil, nil, nil, nil,
- 121, nil, 121, nil, nil, nil, 121, nil, nil, nil,
- nil, nil, nil, nil, nil, 121, nil, nil, 121, 121,
- 121, 121, 121, 121, 121, 121, 121, 122, nil, nil,
- nil, 122, nil, 122, nil, 122, 122, nil, nil, nil,
- 122, nil, nil, nil, nil, nil, nil, nil, nil, 122,
- nil, 122, nil, nil, nil, 122, nil, nil, nil, nil,
- nil, nil, nil, nil, 122, nil, nil, 122, 122, 122,
- 122, 122, 122, 122, 122, 122, 123, nil, nil, nil,
- 123, nil, 123, nil, 123, 123, nil, nil, nil, 123,
- nil, nil, nil, nil, nil, nil, nil, nil, 123, nil,
- 123, nil, nil, nil, 123, nil, nil, nil, nil, nil,
- nil, nil, nil, 123, nil, nil, 123, 123, 123, 123,
- 123, 123, 123, 123, 123, 124, nil, nil, nil, 124,
- nil, 124, nil, 124, 124, nil, nil, nil, 124, 124,
- nil, nil, nil, nil, nil, nil, nil, 124, nil, 124,
- nil, nil, nil, 124, nil, nil, nil, nil, nil, nil,
- nil, 124, 124, nil, nil, 124, 124, 124, 124, 124,
- 124, 124, 124, 124, 130, nil, nil, nil, 130, nil,
- 130, nil, 130, 130, nil, nil, nil, 130, nil, nil,
- nil, nil, nil, nil, nil, nil, 130, nil, 130, nil,
- nil, nil, 130, nil, nil, nil, nil, nil, nil, nil,
- nil, 130, nil, nil, 130, 130, 130, 130, 130, 130,
- 130, 130, 130, 137, nil, nil, nil, 137, nil, 137,
- nil, 137, 137, nil, nil, nil, 137, nil, nil, nil,
- nil, nil, nil, nil, nil, 137, nil, 137, nil, nil,
- nil, 137, nil, nil, nil, nil, nil, nil, nil, 137,
- 137, nil, nil, 137, 137, 137, 137, 137, 137, 137,
- 137, 137, 143, nil, nil, nil, 143, nil, 143, nil,
- 143, 143, nil, nil, nil, 143, nil, nil, nil, nil,
- nil, nil, nil, nil, 143, nil, 143, nil, nil, nil,
- 143, nil, nil, nil, nil, nil, nil, nil, nil, 143,
- nil, nil, 143, 143, 143, 143, 143, 143, 143, 143,
- 143, 144, nil, nil, nil, 144, nil, 144, nil, 144,
- 144, nil, nil, nil, 144, nil, nil, nil, nil, nil,
- nil, nil, nil, 144, nil, 144, nil, nil, nil, 144,
- nil, nil, nil, nil, nil, nil, nil, nil, 144, nil,
- nil, 144, 144, 144, 144, 144, 144, 144, 144, 144,
- 145, nil, nil, nil, 145, nil, 145, nil, 145, 145,
- nil, nil, nil, 145, nil, nil, nil, nil, nil, nil,
- nil, nil, 145, nil, 145, nil, nil, nil, 145, nil,
- nil, nil, nil, nil, nil, nil, nil, 145, nil, nil,
- 145, 145, 145, 145, 145, 145, 145, 145, 145, 146,
- nil, nil, nil, 146, nil, 146, nil, 146, 146, nil,
- nil, nil, 146, nil, nil, nil, nil, nil, nil, nil,
- nil, 146, nil, 146, nil, nil, nil, 146, nil, nil,
- nil, nil, nil, nil, nil, nil, 146, nil, nil, 146,
- 146, 146, 146, 146, 146, 146, 146, 146, 147, nil,
- nil, nil, 147, nil, 147, nil, 147, 147, nil, nil,
- nil, 147, nil, nil, nil, nil, nil, nil, nil, nil,
- 147, nil, 147, nil, nil, nil, 147, nil, nil, nil,
- nil, nil, nil, nil, nil, 147, nil, nil, 147, 147,
- 147, 147, 147, 147, 147, 147, 147, 148, nil, nil,
- nil, 148, nil, 148, nil, 148, 148, nil, nil, nil,
- 148, nil, nil, nil, nil, nil, nil, nil, nil, 148,
- nil, 148, nil, nil, nil, 148, nil, nil, nil, nil,
- nil, nil, nil, nil, 148, nil, nil, 148, 148, 148,
- 148, 148, 148, 148, 148, 148, 149, nil, nil, nil,
- 149, nil, 149, nil, 149, 149, nil, nil, nil, 149,
- nil, nil, nil, nil, nil, nil, nil, nil, 149, nil,
- 149, nil, nil, nil, 149, nil, nil, nil, nil, nil,
- nil, nil, nil, 149, nil, nil, 149, 149, 149, 149,
- 149, 149, 149, 149, 149, 150, nil, nil, nil, 150,
- nil, 150, nil, 150, 150, nil, nil, nil, 150, nil,
- nil, nil, nil, nil, nil, nil, nil, 150, nil, 150,
- nil, nil, nil, 150, nil, nil, nil, nil, nil, nil,
- nil, nil, 150, nil, nil, 150, 150, 150, 150, 150,
- 150, 150, 150, 150, 151, nil, nil, nil, 151, nil,
- 151, nil, 151, 151, nil, nil, nil, 151, nil, nil,
- nil, nil, nil, nil, nil, nil, 151, nil, 151, nil,
- nil, nil, 151, nil, nil, nil, nil, nil, nil, nil,
- nil, 151, nil, nil, 151, 151, 151, 151, 151, 151,
- 151, 151, 151, 152, nil, nil, nil, 152, nil, 152,
- nil, 152, 152, nil, nil, nil, 152, nil, nil, nil,
- nil, nil, nil, nil, nil, 152, nil, 152, nil, nil,
- nil, 152, nil, nil, nil, nil, nil, nil, nil, nil,
- 152, nil, nil, 152, 152, 152, 152, 152, 152, 152,
- 152, 152, 153, nil, nil, nil, 153, nil, 153, nil,
- 153, 153, nil, nil, nil, 153, nil, nil, nil, nil,
- nil, nil, nil, nil, 153, nil, 153, nil, nil, nil,
- 153, nil, nil, nil, nil, nil, nil, nil, nil, 153,
- nil, nil, 153, 153, 153, 153, 153, 153, 153, 153,
- 153, 154, nil, nil, nil, 154, nil, 154, nil, 154,
- 154, nil, nil, nil, 154, nil, nil, nil, nil, nil,
- nil, nil, nil, 154, nil, 154, nil, nil, nil, 154,
- nil, nil, nil, nil, nil, nil, nil, nil, 154, nil,
- nil, 154, 154, 154, 154, 154, 154, 154, 154, 154,
- 155, nil, nil, nil, 155, nil, 155, nil, 155, 155,
- nil, nil, nil, 155, nil, nil, nil, nil, nil, nil,
- nil, nil, 155, nil, 155, nil, nil, nil, 155, nil,
- nil, nil, nil, nil, nil, nil, nil, 155, nil, nil,
- 155, 155, 155, 155, 155, 155, 155, 155, 155, 156,
- nil, nil, nil, 156, nil, 156, nil, 156, 156, nil,
- nil, nil, 156, nil, nil, nil, nil, nil, nil, nil,
- nil, 156, nil, 156, nil, nil, nil, 156, nil, nil,
- nil, nil, nil, nil, nil, nil, 156, nil, nil, 156,
- 156, 156, 156, 156, 156, 156, 156, 156, 157, nil,
- nil, nil, 157, nil, 157, nil, 157, 157, nil, nil,
- nil, 157, nil, nil, nil, nil, nil, nil, nil, nil,
- 157, nil, 157, nil, nil, nil, 157, nil, nil, nil,
- nil, nil, nil, nil, nil, 157, nil, nil, 157, 157,
- 157, 157, 157, 157, 157, 157, 157, 158, nil, nil,
- nil, 158, nil, 158, nil, 158, 158, nil, nil, nil,
- 158, nil, nil, nil, nil, nil, nil, nil, nil, 158,
- nil, 158, nil, nil, nil, 158, nil, nil, nil, nil,
- nil, nil, nil, nil, 158, nil, nil, 158, 158, 158,
- 158, 158, 158, 158, 158, 158, 159, nil, nil, nil,
- 159, nil, 159, nil, 159, 159, nil, nil, nil, 159,
- nil, nil, nil, nil, nil, nil, nil, nil, 159, nil,
- 159, nil, nil, nil, 159, nil, nil, nil, nil, nil,
- nil, nil, nil, 159, nil, nil, 159, 159, 159, 159,
- 159, 159, 159, 159, 159, 160, nil, nil, nil, 160,
- nil, 160, nil, 160, 160, nil, nil, nil, 160, nil,
- nil, nil, nil, nil, nil, nil, nil, 160, nil, 160,
- nil, nil, nil, 160, nil, nil, nil, nil, nil, nil,
- nil, nil, 160, nil, nil, 160, 160, 160, 160, 160,
- 160, 160, 160, 160, 161, nil, nil, nil, 161, nil,
- 161, nil, 161, 161, nil, nil, nil, 161, nil, nil,
- nil, nil, nil, nil, nil, nil, 161, nil, 161, nil,
- nil, nil, 161, nil, nil, nil, nil, nil, nil, nil,
- nil, 161, nil, nil, 161, 161, 161, 161, 161, 161,
- 161, 161, 161, 162, nil, nil, nil, 162, nil, 162,
- nil, 162, 162, nil, nil, nil, 162, nil, nil, nil,
- nil, nil, nil, nil, nil, 162, nil, 162, nil, nil,
- nil, 162, nil, nil, nil, nil, nil, nil, nil, nil,
- 162, nil, nil, 162, 162, 162, 162, 162, 162, 162,
- 162, 162, 163, nil, nil, nil, 163, nil, 163, nil,
- 163, 163, nil, nil, nil, 163, nil, nil, nil, nil,
- nil, nil, nil, nil, 163, nil, 163, nil, nil, nil,
- 163, nil, nil, nil, nil, nil, nil, nil, nil, 163,
- nil, nil, 163, 163, 163, 163, 163, 163, 163, 163,
- 163, 164, nil, nil, nil, 164, nil, 164, nil, 164,
- 164, nil, nil, nil, 164, nil, nil, nil, nil, nil,
- nil, nil, nil, 164, nil, 164, nil, nil, nil, 164,
- nil, nil, nil, nil, nil, nil, nil, nil, 164, nil,
- nil, 164, 164, 164, 164, 164, 164, 164, 164, 164,
- 165, nil, nil, nil, 165, nil, 165, nil, 165, 165,
- nil, nil, nil, 165, nil, nil, nil, nil, nil, nil,
- nil, nil, 165, nil, 165, nil, nil, nil, 165, nil,
- nil, nil, nil, nil, nil, nil, nil, 165, nil, nil,
- 165, 165, 165, 165, 165, 165, 165, 165, 165, 166,
- nil, nil, nil, 166, nil, 166, nil, 166, 166, nil,
- nil, nil, 166, nil, nil, nil, nil, nil, nil, nil,
- nil, 166, nil, 166, nil, nil, nil, 166, nil, nil,
- nil, nil, nil, nil, nil, nil, 166, nil, nil, 166,
- 166, 166, 166, 166, 166, 166, 166, 166, 192, nil,
- nil, nil, 192, nil, 192, nil, 192, 192, nil, nil,
- nil, 192, nil, nil, nil, nil, nil, nil, nil, nil,
- 192, nil, 192, nil, nil, nil, 192, nil, nil, nil,
- nil, nil, nil, nil, nil, 192, nil, nil, 192, 192,
- 192, 192, 192, 192, 192, 192, 192, 195, nil, nil,
- nil, 195, nil, 195, nil, 195, 195, nil, nil, nil,
- 195, nil, nil, nil, nil, nil, nil, nil, nil, 195,
- nil, 195, nil, nil, nil, 195, nil, nil, nil, nil,
- nil, nil, nil, nil, 195, nil, nil, 195, 195, 195,
- 195, 195, 195, 195, 195, 195, 253, nil, nil, nil,
- 253, nil, 253, nil, 253, 253, nil, nil, nil, 253,
- nil, nil, nil, nil, nil, nil, nil, nil, 253, nil,
- 253, nil, nil, nil, 253, nil, nil, nil, nil, nil,
- nil, nil, 253, 253, nil, nil, 253, 253, 253, 253,
- 253, 253, 253, 253, 253, 254, nil, nil, nil, 254,
- nil, 254, nil, 254, 254, nil, nil, nil, 254, nil,
- nil, nil, nil, nil, nil, nil, nil, 254, nil, 254,
- nil, nil, nil, 254, nil, nil, nil, nil, nil, nil,
- nil, 254, 254, nil, nil, 254, 254, 254, 254, 254,
- 254, 254, 254, 254, 255, nil, nil, nil, 255, nil,
- 255, nil, 255, 255, nil, nil, nil, 255, nil, nil,
- nil, nil, nil, nil, nil, nil, 255, nil, 255, nil,
- nil, nil, 255, nil, nil, nil, nil, nil, nil, nil,
- 255, 255, nil, nil, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 258, nil, nil, nil, 258, nil, 258,
- nil, 258, 258, nil, nil, nil, 258, nil, nil, nil,
- nil, nil, nil, nil, nil, 258, nil, 258, nil, nil,
- nil, 258, nil, nil, nil, nil, nil, nil, nil, 258,
- 258, nil, nil, 258, 258, 258, 258, 258, 258, 258,
- 258, 258, 260, nil, nil, nil, 260, nil, 260, nil,
- 260, 260, nil, nil, nil, 260, nil, nil, nil, nil,
- nil, nil, nil, nil, 260, nil, 260, nil, nil, nil,
- 260, nil, nil, nil, nil, nil, nil, nil, nil, 260,
- nil, nil, 260, 260, 260, 260, 260, 260, 260, 260,
- 260, 262, nil, nil, nil, 262, nil, 262, nil, 262,
- 262, nil, nil, nil, 262, nil, nil, nil, nil, nil,
- nil, nil, nil, 262, nil, 262, nil, nil, nil, 262,
- nil, nil, nil, nil, nil, nil, nil, nil, 262, nil,
- nil, 262, 262, 262, 262, 262, 262, 262, 262, 262,
- 267, nil, nil, nil, 267, nil, 267, nil, 267, 267,
- nil, nil, nil, 267, nil, nil, nil, nil, nil, nil,
- nil, nil, 267, nil, 267, nil, nil, nil, 267, nil,
- nil, nil, nil, nil, nil, nil, 267, 267, nil, nil,
- 267, 267, 267, 267, 267, 267, 267, 267, 267, 268,
- nil, nil, nil, 268, nil, 268, nil, 268, 268, nil,
- nil, nil, 268, nil, nil, nil, nil, nil, nil, nil,
- nil, 268, nil, 268, nil, nil, nil, 268, nil, nil,
- nil, nil, nil, nil, nil, 268, 268, nil, nil, 268,
- 268, 268, 268, 268, 268, 268, 268, 268, 49, 49,
- 49, 49, 49, 49, 49, 49, 49, 79, 79, 79,
- 79, 79, 79, 79, 79, 79, 241, 241, 241, 241,
- 241, 241, 241, 241, 241, 241, 241, nil, nil, nil,
- 241, nil, nil, nil, 49, 49, nil, nil, nil, 49,
- nil, nil, nil, 79, 79, nil, nil, nil, 79, 76,
- 76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
- 76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
- 76, 76, nil, nil, nil, 76, nil, nil, nil, nil,
- nil, nil, nil, 76, 135, 135, 135, 135, 135, 135,
- 135, 135, 135, 135, 135, 135, 135, 135, 135, 135,
- 135, 135, 135, 135, 135, 135, 135, nil, nil, nil,
- 135, nil, nil, nil, nil, nil, nil, nil, 135, 264,
- 264, 264, 264, 264, 264, 264, 264, 264, 264, 264,
- 264, 264, 264, 264, 264, 264, 264, 264, 264, 264,
- 264, 264, nil, nil, nil, 264, nil, nil, nil, nil,
- nil, nil, nil, 264, 285, 285, 285, 285, 285, 285,
- 285, 285, 285, 285, 285, 285, 285, 285, 285, 285,
- 285, 285, 285, 285, 285, 285, 285, nil, nil, nil,
- 285, nil, nil, nil, nil, nil, nil, nil, 285, 168,
- 168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
- 168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
- 168, 168, nil, nil, nil, 168, nil, nil, nil, nil,
- nil, nil, 168, 194, 194, 194, 194, 194, 194, 194,
- 194, 194, 194, 194, 194, 194, 194, 194, 194, 194,
- 194, 194, 194, 194, 194, 194, nil, nil, nil, 194,
- nil, nil, nil, nil, nil, nil, 194, 196, 196, 196,
- 196, 196, 196, 196, 196, 196, 196, 196, 196, 196,
- 196, 196, 196, 196, 196, 196, 196, 196, 196, 196,
- nil, nil, nil, 196, nil, nil, nil, nil, nil, nil,
- 196, 261, 261, 261, 261, 261, 261, 261, 261, 261,
- 261, 261, 261, 261, 261, 261, 261, 261, 261, 261,
- 261, 261, 261, 261, nil, nil, nil, 261, nil, nil,
- nil, nil, nil, nil, 261, 287, 287, 287, 287, 287,
- 287, 287, 287, 287, 287, 287, 287, 287, 287, 287,
- 287, 287, 287, 287, 287, 287, 287, 287, nil, nil,
- nil, 287, 0, 0, 0, 0, 0, nil, 287, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, nil,
- 0, 0, 0, 0, nil, 0, nil, 0, 3, 3,
- 3, 3, 3, nil, 0, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, nil, 3, 3, 3, 3,
- nil, 3, nil, 3, 43, 43, 43, nil, nil, nil,
- 3, 43, 43, 43, 43, 43, 43, 43, 43, 43,
- 43, 43, 43, 43, 43, 43, nil, 43, nil, 43,
- 47, 47, 47, nil, nil, nil, 43, 47, 47, 47,
- 47, 47, 47, 47, 47, 47, 47, nil, 47, 47,
- 47, 47, nil, 47, nil, 47, 108, 108, 108, nil,
- nil, nil, 47, 108, 108, 108, 108, 108, 108, 108,
- 108, 108, 108, nil, 108, 108, 108, 108, nil, 108,
- nil, 108, 263, 263, 263, nil, nil, nil, 108, 263,
- 263, 263, 263, 263, 263, 263, 263, 263, 263, nil,
- 263, 263, 263, 263, nil, 263, nil, 263, 265, 265,
- 265, nil, nil, nil, 263, 265, 265, 265, 265, 265,
- 265, 265, 265, 265, 265, nil, 265, 265, 265, 265,
- nil, 265, nil, 265, 286, 286, 286, nil, nil, nil,
- 265, 286, 286, 286, 286, 286, 286, 286, 286, 286,
- 286, nil, 286, 286, 286, 286, nil, 286, nil, 286,
- 299, 299, 299, nil, nil, nil, 286, 299, 299, 299,
- 299, 299, 299, 299, 299, 299, 299, nil, 299, 299,
- 299, 299, nil, 299, nil, 299, 300, 300, 300, nil,
- nil, nil, 299, 300, 300, 300, 300, 300, 300, 300,
- 300, 300, 300, nil, 300, 300, 300, 300, nil, 300,
- nil, 300, 306, 306, 306, nil, nil, nil, 300, 306,
- 306, 306, 306, 306, 306, 306, 306, 306, 306, nil,
- 306, 306, 306, 306, nil, 306, nil, 306, nil, nil,
- nil, nil, nil, nil, 306, 180, 180, 180, 180, 180,
- 180, 180, 180, 180, 180, 180, 180, 180, 180, 180,
- 180, 180, 180, 180, 180, 180, 180, 180, nil, nil,
- nil, 180, 197, 197, 197, 197, 197, 197, 197, 197,
- 197, 197, 197, 197, 197, 197, 197, 197, 197, 197,
- 197, 197, 197, 197, 197, nil, nil, nil, 197, 199,
- 199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
- 199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
- 199, 199, nil, nil, nil, 199, 200, 200, 200, 200,
- 200, 200, 200, 200, 200, 200, 200, 200, 200, 200,
- 200, 200, 200, 200, 200, 200, 200, 200, 200, nil,
- nil, nil, 200, 201, 201, 201, 201, 201, 201, 201,
- 201, 201, 201, 201, 201, 201, 201, 201, 201, 201,
- 201, 201, 201, 201, 201, 201, nil, nil, nil, 201,
- 202, 202, 202, 202, 202, 202, 202, 202, 202, 202,
- 202, 202, 202, 202, 202, 202, 202, 202, 202, 202,
- 202, 202, 202, nil, nil, nil, 202, 203, 203, 203,
- 203, 203, 203, 203, 203, 203, 203, 203, 203, 203,
- 203, 203, 203, 203, 203, 203, 203, 203, 203, 203,
- nil, nil, nil, 203, 204, 204, 204, 204, 204, 204,
- 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
- 204, 204, 204, 204, 204, 204, 204, nil, nil, nil,
- 204, 205, 205, 205, 205, 205, 205, 205, 205, 205,
- 205, 205, 205, 205, 205, 205, 205, 205, 205, 205,
- 205, 205, 205, 205, nil, nil, nil, 205, 206, 206,
- 206, 206, 206, 206, 206, 206, 206, 206, 206, 206,
- 206, 206, 206, 206, 206, 206, 206, 206, 206, 206,
- 206, nil, nil, nil, 206, 209, 209, 209, 209, 209,
- 209, 209, 209, 209, 209, 209, 209, 209, 209, 209,
- 209, 209, 209, 209, 209, 209, 209, 209, nil, nil,
- nil, 209, 222, 222, 222, 222, 222, 222, 222, 222,
- 222, 222, 222, 222, 222, 222, 222, 222, 222, 222,
- 222, 222, 222, 222, 222, nil, nil, nil, 222, 277,
- 277, 277, 277, 277, 277, 277, 277, 277, 277, 277,
- 277, 277, 277, 277, 277, 277, 277, 277, 277, 277,
- 277, 277, nil, nil, nil, 277, 279, 279, 279, 279,
- 279, 279, 279, 279, 279, 279, 279, 279, 279, 279,
- 279, 279, 279, 279, 279, 279, 279, 279, 279, nil,
- nil, nil, 279, 281, 281, 281, 281, 281, 281, 281,
- 281, 281, 281, 281, 281, 281, 281, 281, 281, 281,
- 281, 281, 281, 281, 281, 281, nil, nil, nil, 281,
- 291, 291, 291, 291, 291, 291, 291, 291, 291, 291,
- 291, 291, 291, 291, 291, 291, 291, 291, 291, 291,
- 291, 291, 291, nil, nil, nil, 291, 228, 228, 228,
- 228, 228, 228, 228, 228, 228, 228, 228, 228, 228,
- 228, 228, 228, 228, 228, 228, 228, 228, 228, nil,
- nil, nil, 228, 227, 227, 227, 227, 227, 227, 227,
- 227, 227, 227, 227, 227, 227, 227, 227, 227, 227,
- 227, 227, 227, 227, nil, nil, nil, 227, 242, 242,
- 242, 242, 242, 242, 242, 242, 242, 242, 242, nil,
- nil, nil, 242, 243, 243, 243, 243, 243, 243, 243,
- 243, 243, 243, 243, nil, nil, nil, 243, 244, 244,
- 244, 244, 244, 244, 244, 244, 244, 244, 244, nil,
- nil, nil, 244, 245, 245, 245, 245, 245, 245, 245,
- 245, 245, 245, 245, nil, nil, nil, 245, 246, 246,
- 246, 246, 246, 246, 246, 246, 246, 246, 246, nil,
- nil, nil, 246, 247, 247, 247, 247, 247, 247, 247,
- 247, 247, 247, 247, nil, nil, nil, 247, 248, 248,
- 248, 248, 248, 248, 248, 248, 248, 248, 248, nil,
- nil, nil, 248, 249, 249, 249, 249, 249, 249, 249,
- 249, 249, 249, 249, nil, nil, nil, 249, 250, 250,
- 250, 250, 250, 250, 250, 250, 250, 250, 250, nil,
- nil, nil, 250, 237, 237, 237, 237, 237, 237, 237,
- 237, 237, 237, nil, nil, nil, 237, 236, 236, 236,
- 236, 236, 236, 236, 236, 236, nil, nil, nil, 236 ]
-
-racc_action_pointer = [
- 3034, 33, nil, 3060, 109, nil, nil, nil, nil, nil,
- -5, -28, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, -2, 9, -6, 13, 47, nil, -17, 50, 59,
- 65, -8, -7, 3086, 68, -18, 80, 3112, 81, 2716,
- 35, 74, 15, nil, nil, nil, 128, nil, nil, nil,
- nil, nil, 88, nil, nil, nil, 42, nil, nil, 88,
- 133, 72, nil, -28, 30, 93, 2758, 96, nil, 2725,
- 91, 140, 189, 238, nil, nil, nil, nil, nil, nil,
- 86, nil, nil, nil, 84, 129, 287, nil, nil, nil,
- nil, nil, nil, nil, nil, 108, 105, nil, 3138, 52,
- 129, 138, 336, 111, 385, 434, 483, 532, 581, 630,
- 679, 728, 777, 826, 875, nil, nil, nil, nil, nil,
- 924, 139, 19, nil, 31, 2793, nil, 973, 168, 131,
- 133, nil, nil, 1022, 1071, 1120, 1169, 1218, 1267, 1316,
- 1365, 1414, 1463, 1512, 1561, 1610, 1659, 1708, 1757, 1806,
- 1855, 1904, 1953, 2002, 2051, 2100, 2149, nil, 2898, 143,
- 145, 148, 309, nil, 122, 124, 125, 128, 140, nil,
- 3354, nil, 156, 154, nil, nil, nil, 179, nil, nil,
- nil, nil, 2198, 171, 2932, 2247, 2966, 3381, nil, 3408,
- 3435, 3462, 3489, 3516, 3543, 3570, 3597, 187, nil, 3624,
- 10, nil, 168, -11, nil, nil, 190, 181, 187, nil,
- 172, nil, 3651, nil, nil, 195, 197, 3810, 3785, 66,
- 115, 207, 209, 221, 232, 256, 3972, 3959, 269, 303,
- 319, 2713, 3825, 3840, 3855, 3870, 3885, 3900, 3915, 3930,
- 3945, nil, 201, 2296, 2345, 2394, 178, nil, 2443, nil,
- 2492, 3000, 2541, 3164, 2828, 3190, nil, 2590, 2639, nil,
- 217, nil, nil, 3, nil, nil, 324, 3678, nil, 3705,
- nil, 3732, nil, nil, nil, 2863, 3216, 3034, 213, nil,
- nil, 3759, nil, nil, nil, nil, 206, 101, nil, 3242,
- 3268, 358, 234, nil, nil, nil, 3294, nil ]
-
-racc_action_default = [
- -2, -172, -1, -4, -172, -6, -8, -9, -10, -11,
- -172, -172, -15, -16, -17, -18, -19, -20, -22, -23,
- -24, -25, -26, -27, -28, -29, -30, -31, -32, -33,
- -34, -172, -172, -172, -172, -172, -40, -172, -172, -172,
- -172, -172, -172, -172, -172, -172, -172, -172, -172, -172,
- -172, -172, -128, -160, -161, -162, -172, -3, -5, -7,
- -21, -12, -172, -35, -36, -37, -172, -38, -39, -172,
- -157, -159, -42, -172, -172, -172, -172, -172, -49, -108,
- -172, -172, -172, -172, -93, -94, -105, -106, -107, -109,
- -110, -111, -112, -113, -172, -172, -172, -163, -164, -165,
- -166, -167, -169, -170, -171, -172, -172, -51, -154, -138,
- -172, -172, -172, -172, -172, -172, -172, -172, -172, -172,
- -172, -172, -172, -172, -172, -72, -74, -71, -73, -127,
- -172, -172, -142, 308, -172, -172, -41, -172, -172, -172,
- -172, -45, -47, -172, -172, -172, -172, -172, -172, -172,
- -172, -172, -172, -172, -172, -172, -172, -172, -172, -172,
- -172, -172, -172, -172, -172, -172, -172, -48, -172, -77,
- -81, -82, -172, -129, -172, -172, -172, -126, -172, -133,
- -143, -144, -146, -172, -148, -50, -153, -172, -134, -135,
- -136, -137, -172, -172, -172, -172, -172, -59, -60, -61,
- -62, -63, -64, -65, -66, -67, -68, -172, -70, -116,
- -128, -117, -131, -172, -140, -141, -172, -172, -172, -150,
- -152, -46, -155, -156, -158, -172, -172, -76, -78, -79,
- -80, -83, -84, -85, -86, -87, -88, -89, -90, -91,
- -92, -95, -96, -97, -98, -99, -100, -101, -102, -103,
- -104, -75, -172, -172, -172, -172, -125, -132, -172, -147,
- -172, -172, -172, -172, -172, -172, -69, -172, -172, -139,
- -172, -14, -149, -172, -43, -44, -172, -118, -121, -119,
- -122, -120, -123, -124, -145, -172, -172, -172, -55, -57,
- -58, -114, -115, -130, -13, -151, -172, -138, -53, -172,
- -172, -172, -172, -54, -56, -168, -172, -52 ]
-
-racc_goto_table = [
- 31, 187, 62, 31, 175, 33, 77, 183, 33, 178,
- 35, 139, 140, 35, 207, 38, 216, 106, 38, 60,
- 69, 1, 271, 59, 75, 58, 215, 49, 70, 61,
- 49, 108, 70, 174, 2, 113, 110, 57, nil, nil,
- nil, nil, nil, 31, nil, nil, nil, 31, 33, nil,
- nil, nil, 33, 35, nil, nil, nil, 35, 38, nil,
- nil, nil, 38, nil, nil, nil, nil, nil, nil, nil,
- 49, nil, nil, nil, 49, 294, nil, 127, 128, 198,
- nil, 252, 186, nil, nil, 173, 176, nil, 211, nil,
- nil, nil, nil, nil, nil, nil, 108, nil, nil, nil,
- nil, 223, 193, nil, nil, nil, nil, nil, 31, 188,
- nil, nil, nil, 33, 189, 210, nil, nil, 35, 190,
- 60, 224, 214, 38, 191, 219, nil, nil, nil, 70,
- nil, nil, nil, nil, nil, 49, 49, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 295, nil, nil, 293, nil,
- nil, nil, nil, nil, nil, 175, nil, nil, nil, 284,
- 283, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 296, nil, nil, nil, 302,
- nil, nil, nil, nil, 174, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 272,
- 305, nil, nil, nil, nil, nil, nil, 278, 280, 282,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 292, 211, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 176, nil, nil,
- nil, 288, nil, 290, nil, nil, nil, nil, nil, 210,
- nil, nil, nil, 31, 219, 31, nil, nil, 33, nil,
- 33, nil, nil, 35, 298, 35, nil, nil, 38, 76,
- 38, nil, nil, nil, nil, nil, 31, 303, 304, nil,
- 49, 33, 49, nil, 307, nil, 35, 188, nil, 31,
- 31, 38, 189, 135, 33, 33, 31, 190, nil, 35,
- 35, 33, 191, 49, 38, 38, 35, 168, 169, 170,
- 171, 38, nil, nil, 49, nil, 49, 49, nil, nil,
- nil, nil, nil, 49, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 194,
- nil, 196, 197, 199, 200, 201, 202, 203, 204, 205,
- 206, 209, nil, nil, nil, nil, nil, 213, nil, nil,
- nil, nil, nil, nil, 222, nil, nil, nil, nil, nil,
- 227, 228, 229, 230, 231, 232, 233, 234, 235, 236,
- 237, 238, 239, 240, 241, 242, 243, 244, 245, 246,
- 247, 248, 249, 250, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 261,
- nil, nil, 264, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 277, 279, 281, nil, nil, nil, nil, 285, nil, 287,
- nil, nil, nil, nil, 291, 209 ]
-
-racc_goto_check = [
- 30, 39, 9, 30, 43, 31, 37, 53, 31, 49,
- 32, 35, 35, 32, 41, 34, 10, 38, 34, 18,
- 33, 1, 11, 5, 33, 4, 50, 40, 9, 5,
- 40, 6, 9, 35, 2, 6, 9, 2, nil, nil,
- nil, nil, nil, 30, nil, nil, nil, 30, 31, nil,
- nil, nil, 31, 32, nil, nil, nil, 32, 34, nil,
- nil, nil, 34, nil, nil, nil, nil, nil, nil, nil,
- 40, nil, nil, nil, 40, 11, nil, 40, 40, 37,
- nil, 43, 38, nil, nil, 9, 9, nil, 37, nil,
- nil, nil, nil, nil, nil, nil, 6, nil, nil, nil,
- nil, 37, 9, nil, nil, nil, nil, nil, 30, 30,
- nil, nil, nil, 31, 31, 9, nil, nil, 32, 32,
- 18, 33, 9, 34, 34, 9, nil, nil, nil, 9,
- nil, nil, nil, nil, nil, 40, 40, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 10, nil, nil, 41, nil,
- nil, nil, nil, nil, nil, 43, nil, nil, nil, 53,
- 49, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 43, nil, nil, nil, 39,
- nil, nil, nil, nil, 35, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 9,
- 43, nil, nil, nil, nil, nil, nil, 37, 37, 37,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 37, 37, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 9, nil, nil,
- nil, 6, nil, 6, nil, nil, nil, nil, nil, 9,
- nil, nil, nil, 30, 9, 30, nil, nil, 31, nil,
- 31, nil, nil, 32, 6, 32, nil, nil, 34, 36,
- 34, nil, nil, nil, nil, nil, 30, 6, 6, nil,
- 40, 31, 40, nil, 6, nil, 32, 30, nil, 30,
- 30, 34, 31, 36, 31, 31, 30, 32, nil, 32,
- 32, 31, 34, 40, 34, 34, 32, 36, 36, 36,
- 36, 34, nil, nil, 40, nil, 40, 40, nil, nil,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 36,
- nil, 36, 36, 36, 36, 36, 36, 36, 36, 36,
- 36, 36, nil, nil, nil, nil, nil, 36, nil, nil,
- nil, nil, nil, nil, 36, nil, nil, nil, nil, nil,
- 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
- 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
- 36, 36, 36, 36, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 36,
- nil, nil, 36, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 36, 36, 36, nil, nil, nil, nil, 36, nil, 36,
- nil, nil, nil, nil, 36, 36 ]
-
-racc_goto_pointer = [
- nil, 21, 34, nil, 21, 19, -12, nil, nil, -9,
- -118, -195, nil, nil, nil, nil, nil, nil, 15, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 0, 5, 10, -17, 15, -62, 237, -36, -26, -108,
- 27, -110, nil, -91, nil, nil, nil, nil, nil, -86,
- -106, nil, nil, -89, nil, nil ]
-
-racc_goto_default = [
- nil, nil, nil, 3, 5, 6, 7, 8, 9, 52,
- nil, 25, 12, 13, 14, 15, 16, 17, 18, 19,
- 20, 21, 22, 23, 24, 26, 27, 28, 29, 30,
- 86, 88, 85, nil, 84, 87, 180, 181, nil, nil,
- 79, nil, 89, 90, 91, 92, 93, 212, 177, nil,
- 129, 132, 182, nil, 220, 71 ]
-
-racc_reduce_table = [
- 0, 0, :racc_error,
- 1, 80, :_reduce_1,
- 0, 80, :_reduce_2,
- 2, 81, :_reduce_3,
- 1, 81, :_reduce_4,
- 2, 82, :_reduce_5,
- 1, 82, :_reduce_6,
- 2, 82, :_reduce_7,
- 1, 82, :_reduce_8,
- 1, 82, :_reduce_9,
- 1, 85, :_reduce_10,
- 1, 85, :_reduce_11,
- 2, 83, :_reduce_12,
- 6, 84, :_reduce_13,
- 5, 84, :_reduce_14,
- 1, 86, :_reduce_15,
- 1, 86, :_reduce_16,
- 1, 86, :_reduce_17,
- 1, 86, :_reduce_18,
- 1, 86, :_reduce_19,
- 1, 86, :_reduce_20,
- 2, 86, :_reduce_21,
- 1, 86, :_reduce_22,
- 1, 86, :_reduce_23,
- 1, 86, :_reduce_24,
- 1, 86, :_reduce_25,
- 1, 86, :_reduce_26,
- 1, 86, :_reduce_27,
- 1, 86, :_reduce_28,
- 1, 87, :_reduce_29,
- 1, 87, :_reduce_30,
- 1, 87, :_reduce_31,
- 1, 87, :_reduce_32,
- 1, 87, :_reduce_33,
- 1, 87, :_reduce_34,
- 2, 91, :_reduce_35,
- 2, 92, :_reduce_36,
- 2, 93, :_reduce_37,
- 2, 94, :_reduce_38,
- 2, 95, :_reduce_39,
- 1, 96, :_reduce_40,
- 3, 97, :_reduce_41,
- 2, 100, :_reduce_42,
- 5, 98, :_reduce_43,
- 5, 99, :_reduce_44,
- 3, 101, :_reduce_45,
- 4, 102, :_reduce_46,
- 3, 103, :_reduce_47,
- 3, 103, :_reduce_48,
- 2, 103, :_reduce_49,
- 3, 90, :_reduce_50,
- 2, 90, :_reduce_51,
- 9, 104, :_reduce_52,
- 6, 105, :_reduce_53,
- 7, 105, :_reduce_54,
- 5, 106, :_reduce_55,
- 7, 106, :_reduce_56,
- 5, 107, :_reduce_57,
- 5, 108, :_reduce_58,
- 3, 109, :_reduce_59,
- 3, 109, :_reduce_60,
- 3, 109, :_reduce_61,
- 3, 109, :_reduce_62,
- 3, 109, :_reduce_63,
- 3, 109, :_reduce_64,
- 3, 109, :_reduce_65,
- 3, 109, :_reduce_66,
- 3, 109, :_reduce_67,
- 3, 109, :_reduce_68,
- 4, 110, :_reduce_69,
- 3, 110, :_reduce_70,
- 2, 111, :_reduce_71,
- 2, 111, :_reduce_72,
- 2, 113, :_reduce_73,
- 2, 113, :_reduce_74,
- 3, 115, :_reduce_75,
- 3, 115, :_reduce_76,
- 2, 115, :_reduce_77,
- 3, 115, :_reduce_78,
- 3, 115, :_reduce_79,
- 3, 115, :_reduce_80,
- 2, 115, :_reduce_81,
- 2, 115, :_reduce_82,
- 3, 115, :_reduce_83,
- 3, 115, :_reduce_84,
- 3, 115, :_reduce_85,
- 3, 115, :_reduce_86,
- 3, 115, :_reduce_87,
- 3, 115, :_reduce_88,
- 3, 115, :_reduce_89,
- 3, 115, :_reduce_90,
- 3, 115, :_reduce_91,
- 3, 115, :_reduce_92,
- 1, 115, :_reduce_93,
- 1, 115, :_reduce_94,
- 3, 115, :_reduce_95,
- 3, 115, :_reduce_96,
- 3, 115, :_reduce_97,
- 3, 115, :_reduce_98,
- 3, 115, :_reduce_99,
- 3, 115, :_reduce_100,
- 3, 115, :_reduce_101,
- 3, 115, :_reduce_102,
- 3, 115, :_reduce_103,
- 3, 115, :_reduce_104,
- 1, 115, :_reduce_105,
- 1, 115, :_reduce_106,
- 1, 115, :_reduce_107,
- 1, 115, :_reduce_108,
- 1, 115, :_reduce_109,
- 1, 115, :_reduce_110,
- 1, 115, :_reduce_111,
- 1, 115, :_reduce_112,
- 1, 115, :_reduce_113,
- 3, 126, :_reduce_114,
- 3, 126, :_reduce_115,
- 1, 126, :_reduce_116,
- 1, 126, :_reduce_117,
- 3, 127, :_reduce_118,
- 3, 127, :_reduce_119,
- 3, 127, :_reduce_120,
- 3, 127, :_reduce_121,
- 3, 127, :_reduce_122,
- 3, 127, :_reduce_123,
- 3, 128, :_reduce_124,
- 2, 128, :_reduce_125,
- 1, 128, :_reduce_126,
- 2, 119, :_reduce_127,
- 1, 119, :_reduce_128,
- 2, 116, :_reduce_129,
- 3, 120, :_reduce_130,
- 1, 120, :_reduce_131,
- 3, 125, :_reduce_132,
- 2, 125, :_reduce_133,
- 1, 118, :_reduce_134,
- 1, 118, :_reduce_135,
- 1, 118, :_reduce_136,
- 1, 118, :_reduce_137,
- 0, 118, :_reduce_138,
- 3, 130, :_reduce_139,
- 2, 130, :_reduce_140,
- 2, 129, :_reduce_141,
- 1, 129, :_reduce_142,
- 1, 131, :_reduce_143,
- 1, 131, :_reduce_144,
- 3, 132, :_reduce_145,
- 1, 132, :_reduce_146,
- 3, 124, :_reduce_147,
- 2, 124, :_reduce_148,
- 2, 133, :_reduce_149,
- 1, 133, :_reduce_150,
- 3, 89, :_reduce_151,
- 1, 89, :_reduce_152,
- 2, 117, :_reduce_153,
- 1, 117, :_reduce_154,
- 3, 134, :_reduce_155,
- 3, 134, :_reduce_156,
- 1, 134, :_reduce_157,
- 3, 112, :_reduce_158,
- 1, 112, :_reduce_159,
- 1, 88, :_reduce_160,
- 1, 88, :_reduce_161,
- 1, 88, :_reduce_162,
- 1, 122, :_reduce_163,
- 1, 122, :_reduce_164,
- 1, 122, :_reduce_165,
- 1, 122, :_reduce_166,
- 1, 122, :_reduce_167,
- 7, 121, :_reduce_168,
- 1, 114, :_reduce_169,
- 1, 114, :_reduce_170,
- 1, 123, :_reduce_171 ]
-
-racc_reduce_n = 172
-
-racc_shift_n = 308
-
-racc_token_table = {
- false => 0,
- :error => 1,
- :ASS_EQ => 2,
- :ADD_EQ => 3,
- :SUB_EQ => 4,
- :MUL_EQ => 5,
- :DIV_EQ => 6,
- :MOD_EQ => 7,
- :SLL_EQ => 8,
- :SRA_EQ => 9,
- :SRL_EQ => 10,
- :OR => 11,
- :AND => 12,
- :CMP_LT => 13,
- :CMP_GT => 14,
- :CMP_EQ => 15,
- :CMP_NE => 16,
- :CMP_GE => 17,
- :CMP_LE => 18,
- :SUBSTR_EQ => 19,
- :SUBSTR_NE => 20,
- :REGEX_EQ => 21,
- :REGEX_NE => 22,
- :BIT_OR => 23,
- :BIT_XOR => 24,
- :AMPERSAND => 25,
- :BIT_SRA => 26,
- :BIT_SRL => 27,
- :BIT_SLL => 28,
- :ADD => 29,
- :SUB => 30,
- :MUL => 31,
- :DIV => 32,
- :MOD => 33,
- :NOT => 34,
- :UMINUS => 35,
- :BIT_NOT => 36,
- :EXP => 37,
- :INCR => 38,
- :DECR => 39,
- :COMMENT => 40,
- :EXPORT => 41,
- :FUNCTION => 42,
- :LPAREN => 43,
- :RPAREN => 44,
- :SEMICOLON => 45,
- :BREAK => 46,
- :CONTINUE => 47,
- :GLOBAL => 48,
- :IMPORT => 49,
- :INCLUDE => 50,
- :LOCAL => 51,
- :REP => 52,
- :RETURN => 53,
- :LBRACE => 54,
- :RBRACE => 55,
- :FOR => 56,
- :FOREACH => 57,
- :IN => 58,
- :IF => 59,
- :ELSE => 60,
- :REPEAT => 61,
- :UNTIL => 62,
- :WHILE => 63,
- :COLON => 64,
- :COMMA => 65,
- :AT_SIGN => 66,
- :LBRACK => 67,
- :RBRACK => 68,
- :PERIOD => 69,
- :IDENT => 70,
- :INT_DEC => 71,
- :INT_HEX => 72,
- :INT_OCT => 73,
- :FALSE => 74,
- :TRUE => 75,
- :DATA => 76,
- :STRING => 77,
- :UNDEF => 78 }
-
-racc_nt_base = 79
-
-racc_use_result_var = false
-
-Racc_arg = [
- racc_action_table,
- racc_action_check,
- racc_action_default,
- racc_action_pointer,
- racc_goto_table,
- racc_goto_check,
- racc_goto_default,
- racc_goto_pointer,
- racc_nt_base,
- racc_reduce_table,
- racc_token_table,
- racc_shift_n,
- racc_reduce_n,
- racc_use_result_var ]
-Ractor.make_shareable(Racc_arg) if defined?(Ractor)
-
-Racc_token_to_s_table = [
- "$end",
- "error",
- "ASS_EQ",
- "ADD_EQ",
- "SUB_EQ",
- "MUL_EQ",
- "DIV_EQ",
- "MOD_EQ",
- "SLL_EQ",
- "SRA_EQ",
- "SRL_EQ",
- "OR",
- "AND",
- "CMP_LT",
- "CMP_GT",
- "CMP_EQ",
- "CMP_NE",
- "CMP_GE",
- "CMP_LE",
- "SUBSTR_EQ",
- "SUBSTR_NE",
- "REGEX_EQ",
- "REGEX_NE",
- "BIT_OR",
- "BIT_XOR",
- "AMPERSAND",
- "BIT_SRA",
- "BIT_SRL",
- "BIT_SLL",
- "ADD",
- "SUB",
- "MUL",
- "DIV",
- "MOD",
- "NOT",
- "UMINUS",
- "BIT_NOT",
- "EXP",
- "INCR",
- "DECR",
- "COMMENT",
- "EXPORT",
- "FUNCTION",
- "LPAREN",
- "RPAREN",
- "SEMICOLON",
- "BREAK",
- "CONTINUE",
- "GLOBAL",
- "IMPORT",
- "INCLUDE",
- "LOCAL",
- "REP",
- "RETURN",
- "LBRACE",
- "RBRACE",
- "FOR",
- "FOREACH",
- "IN",
- "IF",
- "ELSE",
- "REPEAT",
- "UNTIL",
- "WHILE",
- "COLON",
- "COMMA",
- "AT_SIGN",
- "LBRACK",
- "RBRACK",
- "PERIOD",
- "IDENT",
- "INT_DEC",
- "INT_HEX",
- "INT_OCT",
- "FALSE",
- "TRUE",
- "DATA",
- "STRING",
- "UNDEF",
- "$start",
- "start",
- "roots",
- "root",
- "export",
- "function",
- "statement",
- "simple",
- "compound",
- "ident",
- "params",
- "block",
- "assign",
- "break",
- "call",
- "continue",
- "decr",
- "empty",
- "global",
- "import",
- "include",
- "incr",
- "local",
- "rep",
- "return",
- "for",
- "foreach",
- "if",
- "repeat",
- "while",
- "assign_exp",
- "call_exp",
- "decr_exp",
- "var_decls",
- "incr_exp",
- "string",
- "expr",
- "ref",
- "statements",
- "field",
- "lval",
- "args",
- "ip",
- "int",
- "undef",
- "list_expr",
- "array_expr",
- "arg",
- "kv_pair",
- "kv_pairs",
- "indexes",
- "index",
- "list_elem",
- "list_elems",
- "param",
- "var_decl" ]
-Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)
-
-Racc_debug_parser = false
-
-##### State transition tables end #####
-
-# reduce 0 omitted
-
-module_eval(<<'.,.,', 'nasl.y', 61)
- def _reduce_1(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 63)
- def _reduce_2(val, _values)
- []
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 67)
- def _reduce_3(val, _values)
- [val[0]] + val[1]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 69)
- def _reduce_4(val, _values)
- [val[0]]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 73)
- def _reduce_5(val, _values)
- c(*val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 75)
- def _reduce_6(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 77)
- def _reduce_7(val, _values)
- c(*val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 79)
- def _reduce_8(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 81)
- def _reduce_9(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 85)
- def _reduce_10(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 87)
- def _reduce_11(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 95)
- def _reduce_12(val, _values)
- n(:Export, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 99)
- def _reduce_13(val, _values)
- n(:Function, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 101)
- def _reduce_14(val, _values)
- n(:Function, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 105)
- def _reduce_15(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 107)
- def _reduce_16(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 109)
- def _reduce_17(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 111)
- def _reduce_18(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 113)
- def _reduce_19(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 115)
- def _reduce_20(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 117)
- def _reduce_21(val, _values)
- c(*val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 119)
- def _reduce_22(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 121)
- def _reduce_23(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 123)
- def _reduce_24(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 125)
- def _reduce_25(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 127)
- def _reduce_26(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 129)
- def _reduce_27(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 131)
- def _reduce_28(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 135)
- def _reduce_29(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 137)
- def _reduce_30(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 139)
- def _reduce_31(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 141)
- def _reduce_32(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 143)
- def _reduce_33(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 145)
- def _reduce_34(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 153)
- def _reduce_35(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 157)
- def _reduce_36(val, _values)
- n(:Break, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 161)
- def _reduce_37(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 165)
- def _reduce_38(val, _values)
- n(:Continue, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 169)
- def _reduce_39(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 173)
- def _reduce_40(val, _values)
- n(:Empty, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 177)
- def _reduce_41(val, _values)
- n(:Global, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 181)
- def _reduce_42(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 185)
- def _reduce_43(val, _values)
- n(:Import, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 189)
- def _reduce_44(val, _values)
- n(:Include, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 193)
- def _reduce_45(val, _values)
- n(:Local, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 197)
- def _reduce_46(val, _values)
- n(:Repetition, *val[0..-1])
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 201)
- def _reduce_47(val, _values)
- n(:Return, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 203)
- def _reduce_48(val, _values)
- n(:Return, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 205)
- def _reduce_49(val, _values)
- n(:Return, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 213)
- def _reduce_50(val, _values)
- n(:Block, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 215)
- def _reduce_51(val, _values)
- n(:Block, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 219)
- def _reduce_52(val, _values)
- n(:For, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 223)
- def _reduce_53(val, _values)
- n(:Foreach, val[0], val[1], val[3], val[5])
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 225)
- def _reduce_54(val, _values)
- n(:Foreach, val[0], val[2], val[4], val[6])
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 229)
- def _reduce_55(val, _values)
- n(:If, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 231)
- def _reduce_56(val, _values)
- n(:If, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 235)
- def _reduce_57(val, _values)
- n(:Repeat, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 239)
- def _reduce_58(val, _values)
- n(:While, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 247)
- def _reduce_59(val, _values)
- n(:Assignment, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 249)
- def _reduce_60(val, _values)
- n(:Assignment, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 251)
- def _reduce_61(val, _values)
- n(:Assignment, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 253)
- def _reduce_62(val, _values)
- n(:Assignment, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 255)
- def _reduce_63(val, _values)
- n(:Assignment, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 257)
- def _reduce_64(val, _values)
- n(:Assignment, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 259)
- def _reduce_65(val, _values)
- n(:Assignment, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 261)
- def _reduce_66(val, _values)
- n(:Assignment, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 263)
- def _reduce_67(val, _values)
- n(:Assignment, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 265)
- def _reduce_68(val, _values)
- n(:Assignment, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 269)
- def _reduce_69(val, _values)
- n(:Call, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 271)
- def _reduce_70(val, _values)
- n(:Call, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 275)
- def _reduce_71(val, _values)
- n(:Decrement, val[0])
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 277)
- def _reduce_72(val, _values)
- n(:Decrement, val[0])
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 281)
- def _reduce_73(val, _values)
- n(:Increment, val[0])
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 283)
- def _reduce_74(val, _values)
- n(:Increment, val[0])
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 287)
- def _reduce_75(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 289)
- def _reduce_76(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 291)
- def _reduce_77(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 293)
- def _reduce_78(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 295)
- def _reduce_79(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 297)
- def _reduce_80(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 299)
- def _reduce_81(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 301)
- def _reduce_82(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 303)
- def _reduce_83(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 305)
- def _reduce_84(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 307)
- def _reduce_85(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 309)
- def _reduce_86(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 311)
- def _reduce_87(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 313)
- def _reduce_88(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 315)
- def _reduce_89(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 317)
- def _reduce_90(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 319)
- def _reduce_91(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 321)
- def _reduce_92(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 323)
- def _reduce_93(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 325)
- def _reduce_94(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 327)
- def _reduce_95(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 329)
- def _reduce_96(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 331)
- def _reduce_97(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 333)
- def _reduce_98(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 335)
- def _reduce_99(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 337)
- def _reduce_100(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 339)
- def _reduce_101(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 341)
- def _reduce_102(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 343)
- def _reduce_103(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 345)
- def _reduce_104(val, _values)
- n(:Expression, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 347)
- def _reduce_105(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 349)
- def _reduce_106(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 351)
- def _reduce_107(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 353)
- def _reduce_108(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 355)
- def _reduce_109(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 357)
- def _reduce_110(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 359)
- def _reduce_111(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 361)
- def _reduce_112(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 363)
- def _reduce_113(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 371)
- def _reduce_114(val, _values)
- n(:Argument, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 373)
- def _reduce_115(val, _values)
- n(:Argument, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 375)
- def _reduce_116(val, _values)
- n(:Argument, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 377)
- def _reduce_117(val, _values)
- n(:Argument, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 381)
- def _reduce_118(val, _values)
- n(:KeyValuePair, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 383)
- def _reduce_119(val, _values)
- n(:KeyValuePair, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 385)
- def _reduce_120(val, _values)
- n(:KeyValuePair, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 387)
- def _reduce_121(val, _values)
- n(:KeyValuePair, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 389)
- def _reduce_122(val, _values)
- n(:KeyValuePair, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 391)
- def _reduce_123(val, _values)
- n(:KeyValuePair, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 395)
- def _reduce_124(val, _values)
- [val[0]] + val[2]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 397)
- def _reduce_125(val, _values)
- [val[0]]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 399)
- def _reduce_126(val, _values)
- [val[0]]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 403)
- def _reduce_127(val, _values)
- n(:Lvalue, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 405)
- def _reduce_128(val, _values)
- n(:Lvalue, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 409)
- def _reduce_129(val, _values)
- n(:Reference, val[1])
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 417)
- def _reduce_130(val, _values)
- [val[0]] + val[2]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 419)
- def _reduce_131(val, _values)
- [val[0]]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 423)
- def _reduce_132(val, _values)
- n(:Array, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 425)
- def _reduce_133(val, _values)
- n(:Array, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 429)
- def _reduce_134(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 431)
- def _reduce_135(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 433)
- def _reduce_136(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 435)
- def _reduce_137(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 437)
- def _reduce_138(val, _values)
- nil
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 441)
- def _reduce_139(val, _values)
- val[1]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 443)
- def _reduce_140(val, _values)
- val[1]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 447)
- def _reduce_141(val, _values)
- [val[0]] + val[1]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 449)
- def _reduce_142(val, _values)
- [val[0]]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 453)
- def _reduce_143(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 455)
- def _reduce_144(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 459)
- def _reduce_145(val, _values)
- [val[0]] + val[2]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 461)
- def _reduce_146(val, _values)
- [val[0]]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 465)
- def _reduce_147(val, _values)
- n(:List, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 467)
- def _reduce_148(val, _values)
- n(:List, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 471)
- def _reduce_149(val, _values)
- n(:Parameter, val[1], 'reference')
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 473)
- def _reduce_150(val, _values)
- n(:Parameter, val[0], 'value')
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 477)
- def _reduce_151(val, _values)
- [val[0]] + val[2]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 479)
- def _reduce_152(val, _values)
- [val[0]]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 483)
- def _reduce_153(val, _values)
- [val[0]] + val[1]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 485)
- def _reduce_154(val, _values)
- [val[0]]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 489)
- def _reduce_155(val, _values)
- n(:Assignment, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 491)
- def _reduce_156(val, _values)
- n(:Assignment, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 493)
- def _reduce_157(val, _values)
- val[0]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 497)
- def _reduce_158(val, _values)
- [val[0]] + val[2]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 499)
- def _reduce_159(val, _values)
- [val[0]]
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 507)
- def _reduce_160(val, _values)
- n(:Identifier, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 509)
- def _reduce_161(val, _values)
- n(:Identifier, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 511)
- def _reduce_162(val, _values)
- n(:Identifier, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 515)
- def _reduce_163(val, _values)
- n(:Integer, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 517)
- def _reduce_164(val, _values)
- n(:Integer, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 519)
- def _reduce_165(val, _values)
- n(:Integer, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 521)
- def _reduce_166(val, _values)
- n(:Integer, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 523)
- def _reduce_167(val, _values)
- n(:Integer, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 527)
- def _reduce_168(val, _values)
- n(:Ip, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 530)
- def _reduce_169(val, _values)
- n(:String, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 532)
- def _reduce_170(val, _values)
- n(:String, *val)
- end
-.,.,
-
-module_eval(<<'.,.,', 'nasl.y', 536)
- def _reduce_171(val, _values)
- n(:Undefined, *val)
- end
-.,.,
-
-def _reduce_none(val, _values)
- val[0]
-end
-
- end # class Grammar
-end # module Nasl
-
-
diff --git a/test/racc/regress/nokogiri-css b/test/racc/regress/nokogiri-css
deleted file mode 100644
index 713a75fb46..0000000000
--- a/test/racc/regress/nokogiri-css
+++ /dev/null
@@ -1,838 +0,0 @@
-#
-# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.5.2
-# from Racc grammar file "".
-#
-
-require 'racc/parser.rb'
-
-
-require 'nokogiri/css/parser_extras'
-module Nokogiri
- module CSS
- class Parser < Racc::Parser
-##### State transition tables begin ###
-
-racc_action_table = [
- 24, 93, 56, 57, 33, 55, 94, 23, 24, 22,
- 12, 93, 33, 27, 35, 52, 44, 22, -23, 25,
- 45, 98, 23, 33, 26, 18, 20, 25, 27, -23,
- 23, 24, 26, 18, 20, 33, 27, 11, 39, 24,
- 22, 23, 74, 33, 18, 91, 90, 27, 22, 12,
- 25, 24, -23, 23, 85, 26, 18, 20, 25, 27,
- 66, 23, 24, 26, 18, 20, 33, 27, 101, 100,
- 51, 22, 86, 88, 24, 26, 56, 87, 89, 60,
- 92, 25, 95, 46, 23, 49, 26, 18, 20, 90,
- 27, 33, 33, 51, 96, 99, 56, 58, 26, 60,
- 102, 103, 33, 33, 33, 93, 39, 39, 105, 23,
- 23, 108, 18, 18, 20, 27, 27, 39, 39, 39,
- 23, 23, 23, 18, 18, 18, 27, 27, 27, 33,
- 33, 56, 87, 109, 60, 22, 56, 87, nil, 60,
- 33, nil, nil, nil, 39, 39, nil, 23, 23, nil,
- 18, 18, 20, 27, 27, 39, 82, 83, 23, 56,
- 87, 18, 60, nil, 27, 82, 83, 78, 79, 80,
- nil, 81, nil, nil, nil, 77, 78, 79, 80, nil,
- 81, 4, 5, 10, 77, 4, 5, 43, nil, nil,
- nil, 6, nil, 8, 7, 6, nil, 8, 7, 4,
- 5, 10, nil, nil, nil, nil, nil, nil, nil, 6,
- nil, 8, 7 ]
-
-racc_action_check = [
- 3, 58, 24, 24, 3, 24, 57, 15, 9, 3,
- 64, 57, 9, 15, 11, 24, 18, 9, 58, 3,
- 21, 64, 3, 14, 3, 3, 3, 9, 3, 22,
- 9, 12, 9, 9, 9, 12, 9, 1, 14, 42,
- 12, 14, 45, 42, 14, 55, 55, 14, 42, 1,
- 12, 27, 46, 12, 49, 12, 12, 12, 42, 12,
- 27, 42, 43, 42, 42, 42, 43, 42, 76, 76,
- 27, 43, 50, 53, 23, 27, 51, 51, 54, 51,
- 56, 43, 59, 23, 43, 23, 43, 43, 43, 60,
- 43, 28, 25, 23, 61, 75, 25, 25, 23, 25,
- 84, 86, 29, 30, 31, 87, 28, 25, 91, 28,
- 25, 94, 28, 25, 25, 28, 25, 29, 30, 31,
- 29, 30, 31, 29, 30, 31, 29, 30, 31, 32,
- 39, 90, 90, 105, 90, 39, 92, 92, nil, 92,
- 62, nil, nil, nil, 32, 39, nil, 32, 39, nil,
- 32, 39, 39, 32, 39, 62, 47, 47, 62, 93,
- 93, 62, 93, nil, 62, 48, 48, 47, 47, 47,
- nil, 47, nil, nil, nil, 47, 48, 48, 48, nil,
- 48, 0, 0, 0, 48, 17, 17, 17, nil, nil,
- nil, 0, nil, 0, 0, 17, nil, 17, 17, 26,
- 26, 26, nil, nil, nil, nil, nil, nil, nil, 26,
- nil, 26, 26 ]
-
-racc_action_pointer = [
- 174, 37, nil, -2, nil, nil, nil, nil, nil, 6,
- nil, 14, 29, nil, 17, -17, nil, 178, 5, nil,
- nil, -9, 0, 72, -8, 86, 192, 49, 85, 96,
- 97, 98, 123, nil, nil, nil, nil, nil, nil, 124,
- nil, nil, 37, 60, nil, 31, 23, 153, 162, 29,
- 43, 66, nil, 50, 55, 34, 68, -1, -11, 59,
- 77, 71, 134, nil, -2, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 70, 58, nil, nil, nil,
- nil, nil, nil, nil, 75, nil, 90, 93, nil, nil,
- 121, 101, 126, 149, 98, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 120, nil, nil, nil, nil ]
-
-racc_action_default = [
- -74, -75, -2, -24, -4, -5, -6, -7, -8, -24,
- -73, -75, -24, -3, -47, -10, -13, -17, -75, -19,
- -20, -75, -22, -24, -75, -24, -74, -75, -53, -54,
- -55, -56, -57, -58, -14, 110, -1, -9, -46, -24,
- -11, -12, -24, -24, -18, -75, -29, -61, -61, -75,
- -75, -75, -30, -75, -75, -38, -39, -40, -22, -75,
- -38, -75, -70, -72, -75, -44, -45, -48, -49, -50,
- -51, -52, -15, -16, -21, -75, -75, -62, -63, -64,
- -65, -66, -67, -68, -75, -27, -75, -40, -31, -32,
- -75, -43, -75, -75, -75, -33, -69, -71, -34, -25,
- -59, -60, -26, -28, -35, -75, -36, -37, -42, -41 ]
-
-racc_goto_table = [
- 53, 38, 13, 1, 42, 48, 62, 37, 34, 65,
- 40, 36, 63, 75, 84, 67, 68, 69, 70, 71,
- 62, 41, 50, 47, 54, nil, 63, nil, nil, 64,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 72, 73, nil, nil, nil, nil, nil, nil, 97,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 104, nil, 106, 107 ]
-
-racc_goto_check = [
- 18, 12, 2, 1, 5, 9, 7, 8, 2, 9,
- 10, 2, 12, 17, 17, 12, 12, 12, 12, 12,
- 7, 11, 15, 16, 19, nil, 12, nil, nil, 1,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 2, 2, nil, nil, nil, nil, nil, nil, 12,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 18, nil, 18, 18 ]
-
-racc_goto_pointer = [
- nil, 3, -1, nil, nil, -13, nil, -19, -7, -18,
- -5, 6, -13, nil, nil, -1, 0, -34, -24, 0,
- nil, nil, nil, nil ]
-
-racc_goto_default = [
- nil, nil, nil, 2, 3, 9, 17, 14, nil, 15,
- 31, 30, 16, 29, 19, 21, nil, nil, 59, nil,
- 28, 32, 76, 61 ]
-
-racc_reduce_table = [
- 0, 0, :racc_error,
- 3, 32, :_reduce_1,
- 1, 32, :_reduce_2,
- 2, 32, :_reduce_3,
- 1, 36, :_reduce_4,
- 1, 36, :_reduce_5,
- 1, 36, :_reduce_6,
- 1, 36, :_reduce_7,
- 1, 36, :_reduce_8,
- 2, 37, :_reduce_9,
- 1, 37, :_reduce_none,
- 2, 37, :_reduce_11,
- 2, 37, :_reduce_12,
- 1, 37, :_reduce_13,
- 2, 34, :_reduce_14,
- 3, 33, :_reduce_15,
- 3, 33, :_reduce_16,
- 1, 33, :_reduce_none,
- 2, 44, :_reduce_18,
- 1, 38, :_reduce_none,
- 1, 38, :_reduce_20,
- 3, 45, :_reduce_21,
- 1, 45, :_reduce_22,
- 1, 46, :_reduce_23,
- 0, 46, :_reduce_none,
- 4, 42, :_reduce_25,
- 4, 42, :_reduce_26,
- 3, 42, :_reduce_27,
- 3, 47, :_reduce_28,
- 1, 47, :_reduce_29,
- 2, 40, :_reduce_30,
- 3, 40, :_reduce_31,
- 3, 40, :_reduce_32,
- 3, 40, :_reduce_33,
- 3, 40, :_reduce_34,
- 3, 49, :_reduce_35,
- 3, 49, :_reduce_36,
- 3, 49, :_reduce_37,
- 1, 49, :_reduce_none,
- 1, 49, :_reduce_none,
- 1, 49, :_reduce_40,
- 4, 50, :_reduce_41,
- 3, 50, :_reduce_42,
- 2, 50, :_reduce_43,
- 2, 41, :_reduce_44,
- 2, 41, :_reduce_45,
- 1, 39, :_reduce_none,
- 0, 39, :_reduce_none,
- 2, 43, :_reduce_48,
- 2, 43, :_reduce_49,
- 2, 43, :_reduce_50,
- 2, 43, :_reduce_51,
- 2, 43, :_reduce_52,
- 1, 43, :_reduce_none,
- 1, 43, :_reduce_none,
- 1, 43, :_reduce_none,
- 1, 43, :_reduce_none,
- 1, 43, :_reduce_none,
- 1, 51, :_reduce_58,
- 2, 48, :_reduce_59,
- 2, 48, :_reduce_60,
- 0, 48, :_reduce_none,
- 1, 53, :_reduce_62,
- 1, 53, :_reduce_63,
- 1, 53, :_reduce_64,
- 1, 53, :_reduce_65,
- 1, 53, :_reduce_66,
- 1, 53, :_reduce_67,
- 1, 53, :_reduce_68,
- 3, 52, :_reduce_69,
- 1, 54, :_reduce_none,
- 2, 54, :_reduce_none,
- 1, 54, :_reduce_none,
- 1, 35, :_reduce_none,
- 0, 35, :_reduce_none ]
-
-racc_reduce_n = 75
-
-racc_shift_n = 110
-
-racc_token_table = {
- false => 0,
- :error => 1,
- :FUNCTION => 2,
- :INCLUDES => 3,
- :DASHMATCH => 4,
- :LBRACE => 5,
- :HASH => 6,
- :PLUS => 7,
- :GREATER => 8,
- :S => 9,
- :STRING => 10,
- :IDENT => 11,
- :COMMA => 12,
- :NUMBER => 13,
- :PREFIXMATCH => 14,
- :SUFFIXMATCH => 15,
- :SUBSTRINGMATCH => 16,
- :TILDE => 17,
- :NOT_EQUAL => 18,
- :SLASH => 19,
- :DOUBLESLASH => 20,
- :NOT => 21,
- :EQUAL => 22,
- :RPAREN => 23,
- :LSQUARE => 24,
- :RSQUARE => 25,
- :HAS => 26,
- "." => 27,
- "*" => 28,
- "|" => 29,
- ":" => 30 }
-
-racc_nt_base = 31
-
-racc_use_result_var = true
-
-Racc_arg = [
- racc_action_table,
- racc_action_check,
- racc_action_default,
- racc_action_pointer,
- racc_goto_table,
- racc_goto_check,
- racc_goto_default,
- racc_goto_pointer,
- racc_nt_base,
- racc_reduce_table,
- racc_token_table,
- racc_shift_n,
- racc_reduce_n,
- racc_use_result_var ]
-Ractor.make_shareable(Racc_arg) if defined?(Ractor)
-
-Racc_token_to_s_table = [
- "$end",
- "error",
- "FUNCTION",
- "INCLUDES",
- "DASHMATCH",
- "LBRACE",
- "HASH",
- "PLUS",
- "GREATER",
- "S",
- "STRING",
- "IDENT",
- "COMMA",
- "NUMBER",
- "PREFIXMATCH",
- "SUFFIXMATCH",
- "SUBSTRINGMATCH",
- "TILDE",
- "NOT_EQUAL",
- "SLASH",
- "DOUBLESLASH",
- "NOT",
- "EQUAL",
- "RPAREN",
- "LSQUARE",
- "RSQUARE",
- "HAS",
- "\".\"",
- "\"*\"",
- "\"|\"",
- "\":\"",
- "$start",
- "selector",
- "simple_selector_1toN",
- "prefixless_combinator_selector",
- "optional_S",
- "combinator",
- "simple_selector",
- "element_name",
- "hcap_0toN",
- "function",
- "pseudo",
- "attrib",
- "hcap_1toN",
- "class",
- "namespaced_ident",
- "namespace",
- "attrib_name",
- "attrib_val_0or1",
- "expr",
- "nth",
- "attribute_id",
- "negation",
- "eql_incl_dash",
- "negation_arg" ]
-Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)
-
-Racc_debug_parser = false
-
-##### State transition tables end #####
-
-# reduce 0 omitted
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 9)
- def _reduce_1(val, _values, result)
- result = [val.first, val.last].flatten
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 11)
- def _reduce_2(val, _values, result)
- result = val.flatten
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 12)
- def _reduce_3(val, _values, result)
- result = [val.last].flatten
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 15)
- def _reduce_4(val, _values, result)
- result = :DIRECT_ADJACENT_SELECTOR
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 16)
- def _reduce_5(val, _values, result)
- result = :CHILD_SELECTOR
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 17)
- def _reduce_6(val, _values, result)
- result = :FOLLOWING_SELECTOR
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 18)
- def _reduce_7(val, _values, result)
- result = :DESCENDANT_SELECTOR
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 19)
- def _reduce_8(val, _values, result)
- result = :CHILD_SELECTOR
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 23)
- def _reduce_9(val, _values, result)
- result = if val[1].nil?
- val.first
- else
- Node.new(:CONDITIONAL_SELECTOR, [val.first, val[1]])
- end
-
- result
- end
-.,.,
-
-# reduce 10 omitted
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 31)
- def _reduce_11(val, _values, result)
- result = Node.new(:CONDITIONAL_SELECTOR, val)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 34)
- def _reduce_12(val, _values, result)
- result = Node.new(:CONDITIONAL_SELECTOR, val)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 37)
- def _reduce_13(val, _values, result)
- result = Node.new(:CONDITIONAL_SELECTOR,
- [Node.new(:ELEMENT_NAME, ['*']), val.first]
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 44)
- def _reduce_14(val, _values, result)
- result = Node.new(val.first, [nil, val.last])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 49)
- def _reduce_15(val, _values, result)
- result = Node.new(val[1], [val.first, val.last])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 52)
- def _reduce_16(val, _values, result)
- result = Node.new(:DESCENDANT_SELECTOR, [val.first, val.last])
-
- result
- end
-.,.,
-
-# reduce 17 omitted
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 57)
- def _reduce_18(val, _values, result)
- result = Node.new(:CLASS_CONDITION, [val[1]])
- result
- end
-.,.,
-
-# reduce 19 omitted
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 61)
- def _reduce_20(val, _values, result)
- result = Node.new(:ELEMENT_NAME, val)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 65)
- def _reduce_21(val, _values, result)
- result = Node.new(:ELEMENT_NAME,
- [[val.first, val.last].compact.join(':')]
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 70)
- def _reduce_22(val, _values, result)
- name = @namespaces.key?('xmlns') ? "xmlns:#{val.first}" : val.first
- result = Node.new(:ELEMENT_NAME, [name])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 75)
- def _reduce_23(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-# reduce 24 omitted
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 80)
- def _reduce_25(val, _values, result)
- result = Node.new(:ATTRIBUTE_CONDITION,
- [val[1]] + (val[2] || [])
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 85)
- def _reduce_26(val, _values, result)
- result = Node.new(:ATTRIBUTE_CONDITION,
- [val[1]] + (val[2] || [])
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 90)
- def _reduce_27(val, _values, result)
- # Non standard, but hpricot supports it.
- result = Node.new(:PSEUDO_CLASS,
- [Node.new(:FUNCTION, ['nth-child(', val[1]])]
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 98)
- def _reduce_28(val, _values, result)
- result = Node.new(:ELEMENT_NAME,
- [[val.first, val.last].compact.join(':')]
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 103)
- def _reduce_29(val, _values, result)
- # Default namespace is not applied to attributes.
- # So we don't add prefix "xmlns:" as in namespaced_ident.
- result = Node.new(:ELEMENT_NAME, [val.first])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 110)
- def _reduce_30(val, _values, result)
- result = Node.new(:FUNCTION, [val.first.strip])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 113)
- def _reduce_31(val, _values, result)
- result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 116)
- def _reduce_32(val, _values, result)
- result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 119)
- def _reduce_33(val, _values, result)
- result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 122)
- def _reduce_34(val, _values, result)
- result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 126)
- def _reduce_35(val, _values, result)
- result = [val.first, val.last]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 127)
- def _reduce_36(val, _values, result)
- result = [val.first, val.last]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 128)
- def _reduce_37(val, _values, result)
- result = [val.first, val.last]
- result
- end
-.,.,
-
-# reduce 38 omitted
-
-# reduce 39 omitted
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 133)
- def _reduce_40(val, _values, result)
- case val[0]
- when 'even'
- result = Node.new(:NTH, ['2','n','+','0'])
- when 'odd'
- result = Node.new(:NTH, ['2','n','+','1'])
- when 'n'
- result = Node.new(:NTH, ['1','n','+','0'])
- else
- # This is not CSS standard. It allows us to support this:
- # assert_xpath("//a[foo(., @href)]", @parser.parse('a:foo(@href)'))
- # assert_xpath("//a[foo(., @a, b)]", @parser.parse('a:foo(@a, b)'))
- # assert_xpath("//a[foo(., a, 10)]", @parser.parse('a:foo(a, 10)'))
- result = val
- end
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 152)
- def _reduce_41(val, _values, result)
- if val[1] == 'n'
- result = Node.new(:NTH, val)
- else
- raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
- end
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 158)
- def _reduce_42(val, _values, result)
- # n+3, -n+3
- if val[0] == 'n'
- val.unshift("1")
- result = Node.new(:NTH, val)
- elsif val[0] == '-n'
- val[0] = 'n'
- val.unshift("-1")
- result = Node.new(:NTH, val)
- else
- raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
- end
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 170)
- def _reduce_43(val, _values, result)
- # 5n, -5n, 10n-1
- n = val[1]
- if n[0, 2] == 'n-'
- val[1] = 'n'
- val << "-"
- # b is contained in n as n is the string "n-b"
- val << n[2, n.size]
- result = Node.new(:NTH, val)
- elsif n == 'n'
- val << "+"
- val << "0"
- result = Node.new(:NTH, val)
- else
- raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
- end
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 189)
- def _reduce_44(val, _values, result)
- result = Node.new(:PSEUDO_CLASS, [val[1]])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 191)
- def _reduce_45(val, _values, result)
- result = Node.new(:PSEUDO_CLASS, [val[1]])
- result
- end
-.,.,
-
-# reduce 46 omitted
-
-# reduce 47 omitted
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 199)
- def _reduce_48(val, _values, result)
- result = Node.new(:COMBINATOR, val)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 202)
- def _reduce_49(val, _values, result)
- result = Node.new(:COMBINATOR, val)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 205)
- def _reduce_50(val, _values, result)
- result = Node.new(:COMBINATOR, val)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 208)
- def _reduce_51(val, _values, result)
- result = Node.new(:COMBINATOR, val)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 211)
- def _reduce_52(val, _values, result)
- result = Node.new(:COMBINATOR, val)
-
- result
- end
-.,.,
-
-# reduce 53 omitted
-
-# reduce 54 omitted
-
-# reduce 55 omitted
-
-# reduce 56 omitted
-
-# reduce 57 omitted
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 220)
- def _reduce_58(val, _values, result)
- result = Node.new(:ID, val)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 223)
- def _reduce_59(val, _values, result)
- result = [val.first, val[1]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 224)
- def _reduce_60(val, _values, result)
- result = [val.first, val[1]]
- result
- end
-.,.,
-
-# reduce 61 omitted
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 228)
- def _reduce_62(val, _values, result)
- result = :equal
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 229)
- def _reduce_63(val, _values, result)
- result = :prefix_match
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 230)
- def _reduce_64(val, _values, result)
- result = :suffix_match
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 231)
- def _reduce_65(val, _values, result)
- result = :substring_match
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 232)
- def _reduce_66(val, _values, result)
- result = :not_equal
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 233)
- def _reduce_67(val, _values, result)
- result = :includes
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 234)
- def _reduce_68(val, _values, result)
- result = :dash_match
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'nokogiri-css.y', 238)
- def _reduce_69(val, _values, result)
- result = Node.new(:NOT, [val[1]])
-
- result
- end
-.,.,
-
-# reduce 70 omitted
-
-# reduce 71 omitted
-
-# reduce 72 omitted
-
-# reduce 73 omitted
-
-# reduce 74 omitted
-
-def _reduce_none(val, _values, result)
- val[0]
-end
-
- end # class Parser
- end # module CSS
-end # module Nokogiri
diff --git a/test/racc/regress/opal b/test/racc/regress/opal
deleted file mode 100644
index 0f3ed22535..0000000000
--- a/test/racc/regress/opal
+++ /dev/null
@@ -1,10109 +0,0 @@
-#
-# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.5.2
-# from Racc grammar file "".
-#
-
-require 'racc/parser.rb'
-module Opal
- class Parser < Racc::Parser
-
-module_eval(<<'...end opal.y/module_eval...', 'opal.y', 1808)
-
-...end opal.y/module_eval...
-##### State transition tables begin ###
-
-racc_action_table = [
- 63, 64, 65, 8, 51, 575, 552, -92, 57, 58,
- 619, 205, 206, 61, 73, 59, 60, 62, 23, 24,
- 66, 67, 74, 544, 754, 607, 543, 22, 28, 27,
- 90, 89, 91, 92, 97, 751, 17, 607, 273, -458,
- 612, 653, 7, 41, 6, 9, 94, 93, 575, 84,
- 50, 86, 85, 87, 273, 88, 95, 96, 653, 81,
- 82, -100, 38, 39, -99, -68, 597, -446, 344, 343,
- -95, 205, 206, 618, -446, 652, -97, 581, 575, 582,
- -94, 205, 206, -96, 575, 36, 609, 608, 30, -92,
- 575, 52, 652, 108, 54, 770, 32, -84, 609, 608,
- 40, 101, -79, -92, 268, 752, 100, 195, 18, -100,
- -99, 551, -84, 79, 73, 75, 76, 77, 78, 101,
- -95, 574, 74, 80, 100, 272, -98, 63, 64, 65,
- 56, 51, -97, 53, 653, 57, 58, 196, 37, 83,
- 61, 272, 59, 60, 62, 23, 24, 66, 67, 454,
- -534, 205, 206, 197, 22, 28, 27, 90, 89, 91,
- 92, 308, 101, 17, 574, -84, -91, 100, 652, -90,
- 41, 308, 596, 94, 93, -86, 84, 50, 86, 85,
- 87, -88, 88, 95, 96, -85, 81, 82, -87, 38,
- 39, 101, 101, -535, 574, 900, 100, 100, 101, -100,
- 574, -449, -84, 100, 101, 198, 574, 649, -449, 100,
- -96, -84, 210, 246, -98, 214, 215, -92, 52, -92,
- -91, 54, -92, -100, -99, -100, -99, 40, -100, -99,
- -94, -89, 273, 607, -95, 18, -95, 299, 726, -95,
- 79, 73, 75, 76, 77, 78, -97, -90, -97, 74,
- 80, -97, -86, 726, 63, 64, 65, 56, 51, -88,
- 53, 544, 57, 58, 546, 37, 83, 61, 531, 59,
- 60, 62, 23, 24, 66, 67, 205, 206, 653, 205,
- 206, 22, 28, 27, 90, 89, 91, 92, -91, -534,
- 219, 777, 300, -535, 609, 608, 605, 41, 268, 227,
- 94, 93, 308, 84, 50, 86, 85, 87, 388, 88,
- 95, 96, 652, 81, 82, -90, 38, 39, -99, 272,
- -86, 726, 301, 302, -96, -91, -96, -88, -98, -96,
- -98, 224, 810, -98, -91, 226, 225, -87, 101, 210,
- 725, -93, 214, 100, -94, 52, -94, -85, 54, -94,
- 391, -89, -90, 101, 40, 725, 788, -86, 100, 402,
- 101, -90, 218, 415, -88, 100, -86, 79, 73, 75,
- 76, 77, 78, -88, 453, 101, 74, 80, 455, 204,
- 100, 63, 64, 65, 56, 51, 607, 53, 544, 57,
- 58, 546, 37, 83, 61, 789, 59, 60, 62, 258,
- 259, 66, 67, 876, -534, -87, -95, 877, 257, 28,
- 27, 90, 89, 91, 92, -85, 456, 219, -535, -89,
- -91, 101, 607, 725, 41, 607, 100, 94, 93, -100,
- 84, 50, 86, 85, 87, 261, 88, 95, 96, -456,
- 81, 82, -87, 38, 39, -97, -456, 609, 608, 610,
- 101, -87, -85, -93, 215, 100, -89, -94, 487, 264,
- 265, -85, 754, -455, -454, -89, 210, 266, 489, 214,
- -455, -454, 52, 751, 813, 54, 582, 256, 491, 254,
- 497, 40, -67, 609, 608, 614, 609, 608, 620, 218,
- 400, 401, 203, 201, 79, 73, 75, 76, 77, 78,
- 202, 514, 544, 74, 80, 546, 344, 343, 63, 64,
- 65, 56, 51, 101, 53, -451, 57, 58, 100, 37,
- 83, 61, -451, 59, 60, 62, 258, 259, 66, 67,
- 515, -532, 527, 528, 516, 257, 28, 27, 90, 89,
- 91, 92, 101, 752, 219, -331, -331, 100, 199, 625,
- 200, 41, 525, -331, 94, 93, 277, 84, 50, 86,
- 85, 87, 261, 88, 95, 96, 308, 81, 82, 268,
- 38, 39, 532, 227, 231, 236, 237, 238, 233, 235,
- 243, 244, 239, 240, -449, -449, 220, 221, -452, 101,
- 241, 242, -449, 210, 100, -452, 214, -532, 533, 52,
- 227, -331, 54, -331, 256, 224, 254, 230, 40, 226,
- 225, 222, 223, 234, 232, 228, 218, 229, -532, 203,
- 449, 79, 73, 75, 76, 77, 78, 450, 308, -458,
- 74, 80, 547, 245, 548, 63, 64, 65, 56, 51,
- -449, 53, -449, 57, 58, 491, 37, 83, 61, 555,
- 59, 60, 62, 258, 259, 66, 67, 103, 104, 105,
- 106, 107, 257, 28, 27, 90, 89, 91, 92, -453,
- -84, 219, 524, 521, 531, 452, -453, 451, 41, -92,
- 522, 94, 93, 558, 84, 50, 86, 85, 87, 261,
- 88, 95, 96, 559, 81, 82, 561, 38, 39, 570,
- 227, 231, 236, 237, 238, 233, 235, 243, 244, 239,
- 240, 524, 534, 220, 221, -90, 412, 241, 242, 535,
- 210, 414, 413, 214, -99, 571, 52, 404, 520, 54,
- 523, 256, 224, 254, 230, 40, 226, 225, 222, 223,
- 234, 232, 228, 218, 229, 587, 524, 584, 79, 73,
- 75, 76, 77, 78, 585, 588, 630, 74, 80, 589,
- 245, 650, -255, -255, -255, 56, -255, 452, 53, 451,
- -255, -255, 613, 37, 83, -255, 617, -255, -255, -255,
- -255, -255, -255, -255, 103, 104, 105, 106, 107, -255,
- -255, -255, -255, -255, -255, -255, 621, -86, -255, 524,
- 593, 624, 583, 625, 586, -255, -95, 591, -255, -255,
- 627, -255, -255, -255, -255, -255, -255, -255, -255, -255,
- -263, -255, -255, 628, -255, -255, 629, 227, 231, 236,
- 237, 238, 233, 235, 243, 244, 239, 240, -284, -284,
- 220, 221, 205, 206, 241, 242, -284, -255, 268, 631,
- -255, 268, 227, -255, 227, 592, -255, 523, -255, 224,
- -255, 230, -255, 226, 225, 222, 223, 234, 232, 228,
- -255, 229, 344, 343, 602, -255, -255, -255, -255, -255,
- -255, 603, 832, 813, -255, -255, 776, 245, 227, -233,
- -88, 227, -255, 915, -284, -255, -284, 308, 754, -97,
- -255, -255, 63, 64, 65, 8, 51, 344, 343, 751,
- 57, 58, 832, 813, 670, 61, 671, 59, 60, 62,
- 23, 24, 66, 67, 679, 681, -79, -85, 682, 22,
- 28, 27, 90, 89, 91, 92, -94, 684, 17, 537,
- 341, 340, 344, 343, 7, 41, 227, 9, 94, 93,
- 694, 84, 50, 86, 85, 87, 700, 88, 95, 96,
- 701, 81, 82, 702, 38, 39, 706, 227, 231, 236,
- 237, 238, 233, 235, 243, 244, 239, 240, 224, 752,
- 220, 221, 226, 225, 241, 242, 716, 36, 718, 721,
- 281, 582, 729, 52, 772, -264, 54, 497, 32, 224,
- 497, 230, 40, 226, 225, 222, 223, 234, 232, 228,
- 18, 229, 497, 524, 593, 79, 73, 75, 76, 77,
- 78, 822, 791, 792, 74, 80, 489, 245, 491, 63,
- 64, 65, 56, 51, 800, 53, 802, 57, 58, 803,
- 37, 83, 61, 694, 59, 60, 62, 258, 259, 66,
- 67, 708, 807, 268, 268, 808, 257, 291, 295, 90,
- 89, 91, 92, 268, 227, 219, -286, -286, 227, 592,
- 813, 523, 292, 819, -286, 94, 93, 820, 84, 50,
- 86, 85, 87, 558, 88, 95, 96, 561, 81, 82,
- 823, 327, 824, 336, 334, 333, 561, 335, 341, 340,
- 344, 343, 827, 866, 867, -283, -283, 868, 95, 96,
- 813, 836, 837, -283, 289, 839, 840, 286, -535, -534,
- 52, 842, -286, 54, -286, 285, 850, 852, 855, 856,
- 338, 858, 915, 860, 862, 864, -265, 754, 341, 340,
- 344, 343, 79, 73, 75, 76, 77, 78, 751, 794,
- 878, 74, 80, 879, 880, 881, 63, 64, 65, 56,
- 51, -283, 53, -283, 57, 58, 883, 296, 83, 61,
- 884, 59, 60, 62, 258, 259, 66, 67, 708, 341,
- 340, 344, 343, 257, 291, 295, 90, 89, 91, 92,
- 524, 946, 219, 103, 104, 105, 106, 107, 947, 292,
- 227, 694, 94, 93, 886, 84, 50, 86, 85, 87,
- -263, 88, 95, 96, 890, 81, 82, 764, 752, 336,
- 334, 333, 754, 335, 895, 341, 340, 344, 343, 897,
- 903, 905, 224, 751, 906, 308, 226, 225, 222, 223,
- 919, 289, -266, 921, 214, -535, 945, 52, 586, 563,
- 54, 336, 334, 333, 561, 335, 338, 749, 931, 932,
- 937, 855, 939, 860, 341, 340, 344, 343, 860, 79,
- 73, 75, 76, 77, 78, 862, 948, 954, 74, 80,
- 700, 964, 860, 298, 966, 967, 56, nil, nil, 53,
- nil, nil, nil, nil, 296, 83, 63, 64, 65, 227,
- 51, nil, nil, 752, 57, 58, nil, nil, nil, 61,
- nil, 59, 60, 62, 258, 259, 66, 67, nil, nil,
- nil, nil, nil, 257, 291, 295, 90, 89, 91, 92,
- nil, 224, 219, nil, nil, 226, 225, 222, 223, 41,
- nil, nil, 94, 93, nil, 84, 50, 86, 85, 87,
- nil, 88, 95, 96, nil, 81, 82, nil, 38, 39,
- nil, 227, 231, 236, 237, 238, 233, 235, 243, 244,
- 239, 240, nil, nil, 220, 221, nil, nil, 241, 242,
- nil, 210, nil, nil, 214, nil, nil, 52, nil, nil,
- 54, nil, nil, 224, nil, 230, 40, 226, 225, 222,
- 223, 234, 232, 228, 218, 229, nil, nil, nil, 79,
- 73, 75, 76, 77, 78, nil, nil, nil, 74, 80,
- nil, 245, nil, 63, 64, 65, 56, 51, nil, 53,
- nil, 57, 58, nil, 37, 83, 61, nil, 59, 60,
- 62, 258, 259, 66, 67, nil, nil, nil, nil, nil,
- 257, 291, 295, 90, 89, 91, 92, nil, 563, 219,
- 336, 334, 333, nil, 335, nil, 41, nil, nil, 94,
- 93, nil, 84, 50, 86, 85, 87, nil, 88, 95,
- 96, nil, 81, 82, nil, 38, 39, nil, 227, 231,
- 236, 237, 238, 233, 235, 243, 244, 239, 240, nil,
- nil, 220, 221, nil, nil, 241, 242, nil, 210, nil,
- nil, 214, nil, nil, 52, nil, nil, 54, nil, nil,
- 224, nil, 230, 40, 226, 225, 222, 223, 234, 232,
- 228, 218, 229, nil, nil, nil, 79, 73, 75, 76,
- 77, 78, nil, nil, nil, 74, 80, nil, 245, nil,
- 63, 64, 65, 56, 51, nil, 53, nil, 57, 58,
- nil, 37, 83, 61, nil, 59, 60, 62, 258, 259,
- 66, 67, nil, nil, nil, nil, nil, 257, 291, 295,
- 90, 89, 91, 92, nil, nil, 219, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, 94, 93, nil, 84,
- 50, 86, 85, 87, nil, 88, 95, 96, nil, 81,
- 82, nil, 38, 39, nil, 227, 231, 236, 237, 238,
- 233, 235, 243, 244, 239, 240, nil, nil, 220, 221,
- nil, nil, 241, 242, nil, 210, nil, nil, 214, nil,
- nil, 52, nil, nil, 54, nil, nil, 224, nil, 230,
- 40, 226, 225, 222, 223, 234, 232, 228, 218, 229,
- nil, nil, nil, 79, 73, 75, 76, 77, 78, nil,
- nil, nil, 74, 80, nil, 245, nil, 63, 64, 65,
- 56, 51, nil, 53, nil, 57, 58, nil, 37, 83,
- 61, nil, 59, 60, 62, 23, 24, 66, 67, nil,
- nil, nil, nil, nil, 22, 28, 27, 90, 89, 91,
- 92, nil, nil, 17, nil, nil, nil, nil, nil, nil,
- 41, nil, nil, 94, 93, nil, 84, 50, 86, 85,
- 87, nil, 88, 95, 96, nil, 81, 82, nil, 38,
- 39, nil, 227, 231, 236, 237, 238, 233, 235, 243,
- 244, 239, 240, nil, nil, 220, 221, nil, nil, 241,
- 242, nil, 210, nil, nil, 214, nil, nil, 52, nil,
- nil, 54, nil, nil, 224, nil, 230, 40, 226, 225,
- 222, 223, 234, 232, 228, 18, 229, nil, nil, nil,
- 79, 73, 75, 76, 77, 78, nil, nil, nil, 74,
- 80, nil, 245, nil, 63, 64, 65, 56, 51, nil,
- 53, nil, 57, 58, nil, 37, 83, 61, nil, 59,
- 60, 62, 258, 259, 66, 67, nil, nil, nil, nil,
- nil, 257, 28, 27, 90, 89, 91, 92, nil, nil,
- 219, nil, nil, nil, nil, nil, nil, 41, nil, nil,
- 94, 93, nil, 84, 50, 86, 85, 87, 261, 88,
- 95, 96, nil, 81, 82, nil, 38, 39, nil, 227,
- 231, 236, 237, 238, 233, 235, 243, 244, 239, 240,
- nil, nil, 220, 221, nil, nil, 241, 242, nil, 210,
- nil, nil, 214, nil, nil, 52, nil, nil, 54, nil,
- 256, 224, nil, 230, 40, 226, 225, 222, 223, 234,
- 232, 228, 218, 229, nil, nil, nil, 79, 73, 75,
- 76, 77, 78, nil, nil, nil, 74, 80, nil, 245,
- nil, 63, 64, 65, 56, 51, nil, 53, nil, 57,
- 58, nil, 37, 83, 61, nil, 59, 60, 62, 258,
- 259, 66, 67, nil, nil, nil, nil, nil, 257, 291,
- 295, 90, 89, 91, 92, nil, nil, 219, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 94, 93, nil,
- 84, 50, 86, 85, 87, 261, 88, 95, 96, nil,
- 81, 82, nil, 38, 39, nil, 227, 231, 236, 237,
- 238, 233, 235, 243, 244, 239, 240, nil, nil, 220,
- 221, nil, nil, 241, 242, nil, 210, nil, nil, 214,
- nil, nil, 52, nil, nil, 54, nil, nil, 224, nil,
- 230, 40, 226, 225, 222, 223, 234, 232, 228, 218,
- 229, nil, nil, nil, 79, 73, 75, 76, 77, 78,
- nil, nil, nil, 74, 80, nil, 245, nil, 63, 64,
- 65, 56, 51, nil, 53, nil, 57, 58, nil, 37,
- 83, 61, nil, 59, 60, 62, 23, 24, 66, 67,
- nil, nil, nil, nil, nil, 22, 28, 27, 90, 89,
- 91, 92, nil, nil, 17, nil, nil, nil, nil, nil,
- nil, 41, nil, nil, 94, 93, nil, 84, 50, 86,
- 85, 87, nil, 88, 95, 96, nil, 81, 82, nil,
- 38, 39, nil, 227, 231, 236, 237, 238, 233, 235,
- 243, 244, 239, 240, nil, nil, 220, 221, nil, nil,
- 241, 242, nil, 210, nil, nil, 214, nil, nil, 52,
- nil, nil, 54, nil, nil, 224, nil, 230, 40, 226,
- 225, 222, 223, 234, 232, 228, 18, 229, nil, nil,
- nil, 79, 73, 75, 76, 77, 78, nil, nil, nil,
- 74, 80, nil, 245, nil, 63, 64, 65, 56, 51,
- nil, 53, nil, 57, 58, nil, 37, 83, 61, nil,
- 59, 60, 62, 23, 24, 66, 67, nil, nil, nil,
- nil, nil, 22, 28, 27, 90, 89, 91, 92, nil,
- nil, 17, nil, nil, nil, nil, nil, nil, 41, nil,
- nil, 94, 93, nil, 84, 50, 86, 85, 87, nil,
- 88, 95, 96, nil, 81, 82, nil, 38, 39, nil,
- 227, 231, 236, 237, 238, 233, 235, 243, 244, 239,
- 240, nil, nil, 220, 221, nil, nil, 241, 242, nil,
- 210, nil, nil, 214, nil, nil, 52, nil, nil, 54,
- nil, nil, 224, nil, 230, 40, 226, 225, 222, 223,
- 234, 232, 228, 18, 229, nil, nil, nil, 79, 73,
- 75, 76, 77, 78, nil, nil, nil, 74, 80, nil,
- 245, nil, 63, 64, 65, 56, 51, nil, 53, nil,
- 57, 58, nil, 37, 83, 61, nil, 59, 60, 62,
- 23, 24, 66, 67, nil, nil, nil, nil, nil, 22,
- 28, 27, 90, 89, 91, 92, nil, nil, 17, nil,
- nil, nil, nil, nil, nil, 41, nil, nil, 94, 93,
- nil, 84, 50, 86, 85, 87, nil, 88, 95, 96,
- nil, 81, 82, nil, 38, 39, nil, 227, 231, 236,
- 237, 238, 233, 235, 243, 244, 239, 240, nil, nil,
- 220, 221, nil, nil, 241, 242, nil, 210, nil, nil,
- 214, nil, nil, 52, nil, nil, 54, nil, nil, 224,
- nil, 230, 40, 226, 225, 222, 223, 234, 232, 228,
- 18, 229, nil, nil, nil, 79, 73, 75, 76, 77,
- 78, nil, nil, nil, 74, 80, 101, 245, nil, -233,
- nil, 100, 56, nil, nil, 53, nil, nil, nil, nil,
- 37, 83, 63, 64, 65, nil, 51, nil, nil, nil,
- 57, 58, nil, nil, nil, 61, nil, 59, 60, 62,
- 258, 259, 66, 67, nil, nil, nil, nil, nil, 257,
- 291, 295, 90, 89, 91, 92, nil, nil, 219, nil,
- nil, nil, nil, nil, nil, 292, nil, nil, 94, 93,
- nil, 84, 50, 86, 85, 87, nil, 88, 95, 96,
- nil, 81, 82, 227, 231, 236, 237, 238, 233, 235,
- 243, 244, 239, 240, 227, nil, 220, 221, nil, nil,
- 241, 242, nil, nil, nil, nil, nil, 356, nil, nil,
- 30, 241, 242, 52, nil, 224, 54, 230, 32, 226,
- 225, 222, 223, 234, 232, 228, 224, 229, 230, nil,
- 226, 225, 222, 223, nil, 79, 73, 75, 76, 77,
- 78, nil, nil, 245, 74, 80, nil, nil, nil, 63,
- 64, 65, 56, 51, nil, 53, nil, 57, 58, nil,
- 296, 83, 61, nil, 59, 60, 62, 258, 259, 66,
- 67, nil, nil, nil, nil, nil, 257, 291, 295, 90,
- 89, 91, 92, nil, nil, 219, nil, nil, nil, nil,
- nil, nil, 292, nil, nil, 94, 93, nil, 84, 50,
- 86, 85, 361, nil, 88, 95, 96, nil, 81, 82,
- 227, 231, 236, 237, 238, 233, 235, 243, 244, 239,
- 240, 227, nil, 220, 221, nil, nil, 241, 242, nil,
- nil, 367, nil, nil, 362, nil, nil, 214, 241, 242,
- 52, nil, 224, 54, 230, nil, 226, 225, 222, 223,
- 234, 232, 228, 224, 229, 230, nil, 226, 225, 222,
- 223, nil, 79, 73, 75, 76, 77, 78, nil, nil,
- 245, 74, 80, nil, nil, nil, -531, -531, -531, 56,
- -531, nil, 53, nil, -531, -531, nil, 296, 83, -531,
- nil, -531, -531, -531, -531, -531, -531, -531, nil, -531,
- nil, nil, nil, -531, -531, -531, -531, -531, -531, -531,
- nil, nil, -531, nil, nil, nil, nil, nil, nil, -531,
- nil, nil, -531, -531, nil, -531, -531, -531, -531, -531,
- -531, -531, -531, -531, nil, -531, -531, nil, -531, -531,
- nil, 227, 231, 236, 237, 238, 233, 235, 243, 244,
- 239, 240, nil, nil, 220, 221, nil, nil, 241, 242,
- nil, -531, nil, nil, -531, -531, nil, -531, nil, nil,
- -531, nil, -531, 224, -531, 230, -531, 226, 225, 222,
- 223, 234, 232, 228, -531, 229, -531, nil, nil, -531,
- -531, -531, -531, -531, -531, nil, nil, nil, -531, -531,
- nil, 245, nil, -532, -532, -532, -531, -532, nil, -531,
- nil, -532, -532, nil, -531, -531, -532, nil, -532, -532,
- -532, -532, -532, -532, -532, nil, -532, nil, nil, nil,
- -532, -532, -532, -532, -532, -532, -532, nil, nil, -532,
- nil, nil, nil, nil, nil, nil, -532, nil, nil, -532,
- -532, nil, -532, -532, -532, -532, -532, -532, -532, -532,
- -532, nil, -532, -532, nil, -532, -532, nil, 227, 231,
- 236, 237, 238, 233, 235, 243, 244, 239, 240, nil,
- nil, 220, 221, nil, nil, 241, 242, nil, -532, nil,
- nil, -532, -532, nil, -532, nil, nil, -532, nil, -532,
- 224, -532, 230, -532, 226, 225, 222, 223, 234, 232,
- 228, -532, 229, -532, nil, nil, -532, -532, -532, -532,
- -532, -532, nil, nil, nil, -532, -532, nil, 245, nil,
- nil, nil, nil, -532, nil, nil, -532, nil, nil, nil,
- nil, -532, -532, 63, 64, 65, 8, 51, nil, nil,
- nil, 57, 58, nil, nil, nil, 61, nil, 59, 60,
- 62, 23, 24, 66, 67, nil, nil, nil, nil, nil,
- 22, 28, 27, 90, 89, 91, 92, nil, nil, 17,
- nil, nil, nil, nil, nil, 7, 41, 6, 9, 94,
- 93, nil, 84, 50, 86, 85, 87, nil, 88, 95,
- 96, nil, 81, 82, nil, 38, 39, nil, 227, 231,
- 236, 237, 238, 233, 235, 243, 244, 239, 240, nil,
- nil, 220, 221, nil, nil, 241, 242, nil, 36, nil,
- nil, 30, nil, nil, 52, nil, nil, 54, nil, 32,
- 224, nil, 230, 40, 226, 225, 222, 223, 234, 232,
- 228, 18, 229, nil, nil, nil, 79, 73, 75, 76,
- 77, 78, nil, nil, nil, 74, 80, nil, 245, nil,
- nil, nil, 404, 56, nil, nil, 53, nil, nil, nil,
- nil, 37, 83, 63, 64, 65, nil, 51, nil, nil,
- nil, 57, 58, nil, nil, nil, 61, nil, 59, 60,
- 62, 23, 24, 66, 67, nil, nil, nil, nil, nil,
- 22, 28, 27, 90, 89, 91, 92, nil, 563, 17,
- 336, 334, 333, nil, 335, nil, 41, nil, nil, 94,
- 93, nil, 84, 50, 86, 85, 87, nil, 88, 95,
- 96, nil, 81, 82, nil, 38, 39, 563, nil, 336,
- 334, 333, nil, 335, nil, nil, 563, 566, 336, 334,
- 333, nil, 335, nil, nil, 569, nil, nil, 210, nil,
- nil, 214, nil, nil, 52, nil, nil, 54, nil, nil,
- nil, nil, nil, 40, nil, nil, 566, nil, nil, nil,
- nil, 18, nil, nil, 569, 566, 79, 73, 75, 76,
- 77, 78, nil, 829, nil, 74, 80, nil, nil, nil,
- 63, 64, 65, 56, 51, nil, 53, nil, 57, 58,
- nil, 37, 83, 61, nil, 59, 60, 62, 23, 24,
- 66, 67, nil, nil, nil, nil, nil, 22, 28, 27,
- 90, 89, 91, 92, nil, nil, 17, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, 94, 93, nil, 84,
- 50, 86, 85, 87, nil, 88, 95, 96, nil, 81,
- 82, nil, 38, 39, nil, 227, 231, 236, 237, 238,
- 233, 235, 243, 244, 239, 240, nil, nil, -555, -555,
- nil, nil, 241, 242, nil, 210, nil, nil, 214, nil,
- nil, 52, nil, nil, 54, nil, nil, 224, nil, 230,
- 40, 226, 225, 222, 223, 234, 232, 228, 18, 229,
- nil, nil, nil, 79, 73, 75, 76, 77, 78, nil,
- nil, nil, 74, 80, nil, nil, nil, 63, 64, 65,
- 56, 51, nil, 53, nil, 57, 58, nil, 37, 83,
- 61, nil, 59, 60, 62, 23, 24, 66, 67, nil,
- nil, nil, nil, nil, 22, 28, 27, 90, 89, 91,
- 92, nil, nil, 17, nil, nil, nil, nil, nil, nil,
- 41, nil, nil, 94, 93, nil, 84, 50, 86, 85,
- 87, nil, 88, 95, 96, nil, 81, 82, nil, 38,
- 39, nil, 227, 231, 236, 237, 238, 233, 235, 243,
- 244, 239, 240, nil, nil, -555, -555, nil, nil, 241,
- 242, nil, 210, nil, nil, 214, nil, nil, 52, nil,
- nil, 54, nil, nil, 224, nil, 230, 40, 226, 225,
- 222, 223, 234, 232, 228, 18, 229, nil, nil, nil,
- 79, 73, 75, 76, 77, 78, nil, nil, nil, 74,
- 80, nil, nil, nil, 63, 64, 65, 56, 51, nil,
- 53, nil, 57, 58, nil, 37, 83, 61, nil, 59,
- 60, 62, 23, 24, 66, 67, nil, nil, nil, nil,
- nil, 22, 28, 27, 90, 89, 91, 92, nil, nil,
- 17, nil, nil, nil, nil, nil, nil, 41, nil, nil,
- 94, 93, nil, 84, 50, 86, 85, 87, nil, 88,
- 95, 96, nil, 81, 82, nil, 38, 39, nil, 227,
- -555, -555, -555, -555, 233, 235, nil, nil, -555, -555,
- nil, nil, nil, nil, nil, nil, 241, 242, nil, 210,
- nil, nil, 214, nil, nil, 52, nil, nil, 54, nil,
- nil, 224, nil, 230, 40, 226, 225, 222, 223, 234,
- 232, 228, 18, 229, nil, nil, nil, 79, 73, 75,
- 76, 77, 78, nil, nil, nil, 74, 80, nil, nil,
- nil, nil, nil, nil, 56, nil, nil, 53, nil, nil,
- nil, nil, 37, 83, 63, 64, 65, 8, 51, nil,
- nil, nil, 57, 58, nil, nil, nil, 61, nil, 59,
- 60, 62, 23, 24, 66, 67, nil, nil, nil, nil,
- nil, 22, 28, 27, 90, 89, 91, 92, nil, nil,
- 17, nil, nil, nil, nil, nil, 7, 41, nil, 9,
- 94, 93, nil, 84, 50, 86, 85, 87, nil, 88,
- 95, 96, nil, 81, 82, nil, 38, 39, nil, 227,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 241, 242, nil, 36,
- nil, nil, 30, nil, nil, 52, nil, nil, 54, nil,
- 32, 224, nil, 230, 40, 226, 225, 222, 223, nil,
- nil, 228, 18, 229, nil, nil, nil, 79, 73, 75,
- 76, 77, 78, nil, nil, nil, 74, 80, nil, nil,
- nil, nil, nil, nil, 56, nil, nil, 53, nil, nil,
- nil, nil, 37, 83, 63, 64, 65, 8, 51, nil,
- nil, nil, 57, 58, nil, nil, nil, 61, nil, 59,
- 60, 62, 23, 24, 66, 67, nil, nil, nil, nil,
- nil, 22, 28, 27, 90, 89, 91, 92, nil, nil,
- 17, nil, nil, nil, nil, nil, 7, 41, 6, 9,
- 94, 93, nil, 84, 50, 86, 85, 87, nil, 88,
- 95, 96, nil, 81, 82, nil, 38, 39, nil, 227,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 241, 242, nil, 36,
- nil, nil, 30, nil, nil, 52, nil, nil, 54, nil,
- 32, 224, nil, 230, 40, 226, 225, 222, 223, nil,
- nil, 228, 18, 229, nil, nil, nil, 79, 73, 75,
- 76, 77, 78, nil, nil, nil, 74, 80, nil, nil,
- nil, nil, nil, nil, 56, nil, nil, 53, nil, nil,
- nil, nil, 37, 83, 63, 64, 65, 8, 51, nil,
- nil, nil, 57, 58, nil, nil, nil, 61, nil, 59,
- 60, 62, 23, 24, 66, 67, nil, nil, nil, nil,
- nil, 22, 28, 27, 90, 89, 91, 92, nil, nil,
- 17, nil, nil, nil, nil, nil, 7, 41, nil, 9,
- 94, 93, nil, 84, 50, 86, 85, 87, nil, 88,
- 95, 96, nil, 81, 82, nil, 38, 39, nil, 227,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 241, 242, nil, 36,
- nil, nil, 30, nil, nil, 52, nil, nil, 54, nil,
- 32, 224, nil, 230, 40, 226, 225, 222, 223, nil,
- nil, 228, 18, 229, nil, nil, nil, 79, 73, 75,
- 76, 77, 78, nil, nil, nil, 74, 80, nil, nil,
- nil, 63, 64, 65, 56, 51, nil, 53, nil, 57,
- 58, nil, 37, 83, 61, nil, 59, 60, 62, 23,
- 24, 66, 67, nil, nil, nil, nil, nil, 22, 28,
- 27, 90, 89, 91, 92, nil, nil, 219, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 94, 93, nil,
- 84, 50, 86, 85, 87, nil, 88, 95, 96, nil,
- 81, 82, nil, 38, 39, nil, 227, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 241, 242, nil, 210, nil, nil, 214,
- nil, nil, 52, nil, nil, 54, nil, 421, 224, nil,
- 230, 40, 226, 225, 222, 223, nil, nil, 228, 218,
- 229, nil, nil, nil, 79, 73, 75, 76, 77, 78,
- nil, nil, nil, 74, 80, nil, nil, nil, 63, 64,
- 65, 56, 51, nil, 53, nil, 57, 58, nil, 37,
- 83, 61, nil, 59, 60, 62, 23, 24, 66, 67,
- nil, nil, nil, nil, nil, 22, 28, 27, 90, 89,
- 91, 92, nil, nil, 219, nil, nil, nil, nil, nil,
- nil, 41, nil, nil, 94, 93, nil, 84, 50, 86,
- 85, 87, nil, 88, 95, 96, nil, 81, 82, nil,
- 38, 39, nil, 227, -555, -555, -555, -555, 233, 235,
- nil, nil, -555, -555, nil, nil, nil, nil, nil, nil,
- 241, 242, nil, 210, nil, nil, 214, nil, nil, 52,
- nil, nil, 54, nil, 421, 224, nil, 230, 40, 226,
- 225, 222, 223, 234, 232, 228, 218, 229, nil, nil,
- nil, 79, 73, 75, 76, 77, 78, nil, nil, nil,
- 74, 80, nil, nil, nil, 63, 64, 65, 56, 51,
- nil, 53, nil, 57, 58, nil, 37, 83, 61, nil,
- 59, 60, 62, 23, 24, 66, 67, nil, nil, nil,
- nil, nil, 22, 28, 27, 90, 89, 91, 92, nil,
- nil, 219, nil, nil, nil, nil, nil, nil, 41, nil,
- nil, 94, 93, nil, 84, 50, 86, 85, 87, nil,
- 88, 95, 96, nil, 81, 82, nil, 38, 39, nil,
- 227, -555, -555, -555, -555, 233, 235, nil, nil, -555,
- -555, nil, nil, nil, nil, nil, nil, 241, 242, nil,
- 210, nil, nil, 214, nil, nil, 52, nil, nil, 54,
- nil, nil, 224, nil, 230, 40, 226, 225, 222, 223,
- 234, 232, 228, 218, 229, nil, nil, nil, 79, 73,
- 75, 76, 77, 78, nil, nil, nil, 74, 80, nil,
- nil, nil, 63, 64, 65, 56, 51, nil, 53, nil,
- 57, 58, nil, 37, 83, 61, nil, 59, 60, 62,
- 258, 259, 66, 67, nil, nil, nil, nil, nil, 257,
- 28, 27, 90, 89, 91, 92, nil, nil, 219, nil,
- nil, nil, nil, nil, nil, 41, nil, nil, 94, 93,
- nil, 84, 50, 86, 85, 87, 261, 88, 95, 96,
- nil, 81, 82, nil, 38, 39, nil, 227, -555, -555,
- -555, -555, 233, 235, nil, nil, -555, -555, nil, nil,
- nil, nil, nil, nil, 241, 242, nil, 210, nil, nil,
- 214, nil, nil, 52, nil, nil, 54, nil, 256, 224,
- nil, 230, 40, 226, 225, 222, 223, 234, 232, 228,
- 218, 229, nil, nil, nil, 79, 73, 75, 76, 77,
- 78, nil, nil, nil, 74, 80, nil, nil, nil, 63,
- 64, 65, 56, 51, nil, 53, nil, 57, 58, nil,
- 37, 83, 61, nil, 59, 60, 62, 258, 259, 66,
- 67, nil, nil, nil, nil, nil, 257, 28, 27, 90,
- 89, 91, 92, nil, nil, 219, nil, nil, nil, nil,
- nil, nil, 41, nil, nil, 94, 93, nil, 84, 50,
- 86, 85, 87, 261, 88, 95, 96, nil, 81, 82,
- nil, 38, 39, nil, 227, -555, -555, -555, -555, 233,
- 235, nil, nil, -555, -555, nil, nil, nil, nil, nil,
- nil, 241, 242, nil, 210, nil, nil, 214, nil, nil,
- 52, nil, nil, 54, nil, 256, 224, nil, 230, 40,
- 226, 225, 222, 223, 234, 232, 228, 218, 229, nil,
- nil, nil, 79, 73, 75, 76, 77, 78, nil, nil,
- nil, 74, 80, nil, nil, nil, 63, 64, 65, 56,
- 51, nil, 53, nil, 57, 58, nil, 37, 83, 61,
- nil, 59, 60, 62, 23, 24, 66, 67, nil, nil,
- nil, nil, nil, 22, 28, 27, 90, 89, 91, 92,
- nil, nil, 219, nil, nil, nil, nil, nil, nil, 41,
- nil, nil, 94, 93, nil, 84, 50, 86, 85, 87,
- nil, 88, 95, 96, nil, 81, 82, nil, 38, 39,
- nil, 227, -555, -555, -555, -555, 233, 235, nil, nil,
- -555, -555, nil, nil, nil, nil, nil, nil, 241, 242,
- nil, 210, nil, nil, 214, nil, nil, 52, nil, nil,
- 54, nil, nil, 224, nil, 230, 40, 226, 225, 222,
- 223, 234, 232, 228, 218, 229, nil, nil, nil, 79,
- 73, 75, 76, 77, 78, nil, nil, nil, 74, 80,
- nil, nil, nil, 63, 64, 65, 56, 51, nil, 53,
- nil, 57, 58, nil, 37, 83, 61, nil, 59, 60,
- 62, 23, 24, 66, 67, nil, nil, nil, nil, nil,
- 22, 28, 27, 90, 89, 91, 92, nil, nil, 17,
- nil, nil, nil, nil, nil, nil, 41, nil, nil, 94,
- 93, nil, 84, 50, 86, 85, 87, nil, 88, 95,
- 96, nil, 81, 82, nil, 38, 39, nil, 227, 231,
- 236, 237, 238, 233, 235, nil, nil, 239, 240, nil,
- nil, nil, nil, nil, nil, 241, 242, nil, 210, nil,
- nil, 214, nil, nil, 52, nil, nil, 54, nil, nil,
- 224, nil, 230, 40, 226, 225, 222, 223, 234, 232,
- 228, 18, 229, nil, nil, nil, 79, 73, 75, 76,
- 77, 78, nil, nil, nil, 74, 80, nil, nil, nil,
- 63, 64, 65, 56, 51, nil, 53, nil, 57, 58,
- nil, 37, 83, 61, nil, 59, 60, 62, 23, 24,
- 66, 67, nil, nil, nil, nil, nil, 22, 28, 27,
- 90, 89, 91, 92, nil, nil, 17, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, 94, 93, nil, 84,
- 50, 86, 85, 87, nil, 88, 95, 96, nil, 81,
- 82, nil, 38, 39, nil, 227, 231, 236, 237, 238,
- 233, 235, 243, nil, 239, 240, nil, nil, nil, nil,
- nil, nil, 241, 242, nil, 210, nil, nil, 214, nil,
- nil, 52, nil, nil, 54, nil, nil, 224, nil, 230,
- 40, 226, 225, 222, 223, 234, 232, 228, 18, 229,
- nil, nil, nil, 79, 73, 75, 76, 77, 78, nil,
- nil, nil, 74, 80, nil, nil, nil, nil, nil, nil,
- 56, nil, nil, 53, nil, nil, nil, nil, 37, 83,
- 63, 64, 65, 8, 51, nil, nil, nil, 57, 58,
- nil, nil, nil, 61, nil, 59, 60, 62, 23, 24,
- 66, 67, nil, nil, nil, nil, nil, 22, 28, 27,
- 90, 89, 91, 92, nil, nil, 17, nil, nil, nil,
- nil, nil, 7, 41, nil, 9, 94, 93, nil, 84,
- 50, 86, 85, 87, nil, 88, 95, 96, nil, 81,
- 82, nil, 38, 39, nil, 227, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 241, 242, nil, 36, nil, nil, 30, nil,
- nil, 52, nil, nil, 54, nil, 32, 224, nil, nil,
- 40, 226, 225, 222, 223, nil, nil, nil, 18, nil,
- nil, nil, nil, 79, 73, 75, 76, 77, 78, nil,
- nil, nil, 74, 80, nil, nil, nil, 63, 64, 65,
- 56, 51, nil, 53, nil, 57, 58, nil, 37, 83,
- 61, nil, 59, 60, 62, 23, 24, 66, 67, nil,
- nil, nil, nil, nil, 22, 28, 27, 90, 89, 91,
- 92, nil, nil, 17, nil, nil, nil, nil, nil, nil,
- 41, nil, nil, 94, 93, nil, 84, 50, 86, 85,
- 87, nil, 88, 95, 96, nil, 81, 82, nil, 38,
- 39, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 210, nil, nil, 214, nil, 458, 52, nil,
- nil, 54, nil, nil, nil, nil, nil, 40, nil, nil,
- nil, nil, nil, nil, nil, 18, nil, nil, nil, nil,
- 79, 73, 75, 76, 77, 78, nil, nil, nil, 74,
- 80, nil, nil, nil, 63, 64, 65, 56, 51, nil,
- 53, nil, 57, 58, nil, 37, 83, 61, nil, 59,
- 60, 62, 258, 259, 66, 67, nil, nil, nil, nil,
- nil, 257, 291, 295, 90, 89, 91, 92, nil, nil,
- 219, nil, nil, nil, nil, nil, nil, 41, nil, nil,
- 94, 93, nil, 84, 50, 86, 85, 87, nil, 88,
- 95, 96, nil, 81, 82, nil, 38, 39, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 210,
- nil, nil, 214, nil, nil, 52, nil, nil, 54, nil,
- nil, nil, nil, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 218, nil, nil, nil, nil, 79, 73, 75,
- 76, 77, 78, nil, nil, nil, 74, 80, nil, nil,
- nil, 63, 64, 65, 56, 51, nil, 53, nil, 57,
- 58, nil, 37, 83, 61, nil, 59, 60, 62, 258,
- 259, 66, 67, nil, nil, nil, nil, nil, 257, 291,
- 295, 90, 89, 91, 92, nil, nil, 219, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 94, 93, nil,
- 84, 50, 86, 85, 87, nil, 88, 95, 96, nil,
- 81, 82, nil, 38, 39, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 210, nil, nil, 214,
- nil, nil, 52, nil, nil, 54, nil, nil, nil, nil,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 218,
- nil, nil, nil, nil, 79, 73, 75, 76, 77, 78,
- nil, nil, nil, 74, 80, nil, nil, nil, 63, 64,
- 65, 56, 51, nil, 53, nil, 57, 58, nil, 37,
- 83, 61, nil, 59, 60, 62, 258, 259, 66, 67,
- nil, nil, nil, nil, nil, 257, 291, 295, 90, 89,
- 91, 92, nil, nil, 219, nil, nil, nil, nil, nil,
- nil, 41, nil, nil, 94, 93, nil, 84, 50, 86,
- 85, 87, nil, 88, 95, 96, nil, 81, 82, nil,
- 38, 39, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 210, nil, nil, 214, nil, nil, 52,
- nil, nil, 54, nil, nil, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 218, nil, nil, nil,
- nil, 79, 73, 75, 76, 77, 78, nil, nil, nil,
- 74, 80, nil, nil, nil, 63, 64, 65, 56, 51,
- nil, 53, nil, 57, 58, nil, 37, 83, 61, nil,
- 59, 60, 62, 258, 259, 66, 67, nil, nil, nil,
- nil, nil, 257, 291, 295, 90, 89, 91, 92, nil,
- nil, 219, nil, nil, nil, nil, nil, nil, 41, nil,
- nil, 94, 93, nil, 84, 50, 86, 85, 87, nil,
- 88, 95, 96, nil, 81, 82, nil, 38, 39, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 210, nil, nil, 214, nil, nil, 52, nil, nil, 54,
- nil, nil, nil, nil, nil, 40, nil, nil, nil, nil,
- nil, nil, nil, 218, nil, nil, nil, nil, 79, 73,
- 75, 76, 77, 78, nil, nil, nil, 74, 80, nil,
- nil, nil, 63, 64, 65, 56, 51, nil, 53, nil,
- 57, 58, nil, 37, 83, 61, nil, 59, 60, 62,
- 258, 259, 66, 67, nil, nil, nil, nil, nil, 257,
- 291, 295, 90, 89, 91, 92, nil, nil, 219, nil,
- nil, nil, nil, nil, nil, 41, nil, nil, 94, 93,
- nil, 84, 50, 86, 85, 87, nil, 88, 95, 96,
- nil, 81, 82, nil, 38, 39, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 210, nil, nil,
- 214, nil, nil, 52, nil, nil, 54, nil, nil, nil,
- nil, nil, 40, nil, nil, nil, nil, nil, nil, nil,
- 218, nil, nil, nil, nil, 79, 73, 75, 76, 77,
- 78, nil, nil, nil, 74, 80, nil, nil, nil, 63,
- 64, 65, 56, 51, nil, 53, nil, 57, 58, nil,
- 37, 83, 61, nil, 59, 60, 62, 258, 259, 66,
- 67, nil, nil, nil, nil, nil, 257, 291, 295, 90,
- 89, 91, 92, nil, nil, 219, nil, nil, nil, nil,
- nil, nil, 41, nil, nil, 94, 93, nil, 84, 50,
- 86, 85, 87, nil, 88, 95, 96, nil, 81, 82,
- nil, 38, 39, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 210, nil, nil, 214, nil, nil,
- 52, nil, nil, 54, nil, nil, nil, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 218, nil, nil,
- nil, nil, 79, 73, 75, 76, 77, 78, nil, nil,
- nil, 74, 80, nil, nil, nil, 63, 64, 65, 56,
- 51, nil, 53, nil, 57, 58, nil, 37, 83, 61,
- nil, 59, 60, 62, 258, 259, 66, 67, nil, nil,
- nil, nil, nil, 257, 291, 295, 90, 89, 91, 92,
- nil, nil, 219, nil, nil, nil, nil, nil, nil, 41,
- nil, nil, 94, 93, nil, 84, 50, 86, 85, 87,
- nil, 88, 95, 96, nil, 81, 82, nil, 38, 39,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 210, nil, nil, 214, nil, nil, 52, nil, nil,
- 54, nil, nil, nil, nil, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 218, nil, nil, nil, nil, 79,
- 73, 75, 76, 77, 78, nil, nil, nil, 74, 80,
- nil, nil, nil, 63, 64, 65, 56, 51, nil, 53,
- nil, 57, 58, nil, 37, 83, 61, nil, 59, 60,
- 62, 258, 259, 66, 67, nil, nil, nil, nil, nil,
- 257, 291, 295, 90, 89, 91, 92, nil, nil, 219,
- nil, nil, nil, nil, nil, nil, 41, nil, nil, 94,
- 93, nil, 84, 50, 86, 85, 87, nil, 88, 95,
- 96, nil, 81, 82, nil, 38, 39, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 210, nil,
- nil, 214, nil, nil, 52, nil, nil, 54, nil, nil,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 218, nil, nil, nil, nil, 79, 73, 75, 76,
- 77, 78, nil, nil, nil, 74, 80, nil, nil, nil,
- 63, 64, 65, 56, 51, nil, 53, nil, 57, 58,
- nil, 37, 83, 61, nil, 59, 60, 62, 258, 259,
- 66, 67, nil, nil, nil, nil, nil, 257, 291, 295,
- 90, 89, 91, 92, nil, nil, 219, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, 94, 93, nil, 84,
- 50, 86, 85, 87, nil, 88, 95, 96, nil, 81,
- 82, nil, 38, 39, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 210, nil, nil, 214, nil,
- nil, 52, nil, nil, 54, nil, nil, nil, nil, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 218, nil,
- nil, nil, nil, 79, 73, 75, 76, 77, 78, nil,
- nil, nil, 74, 80, nil, nil, nil, 63, 64, 65,
- 56, 51, nil, 53, nil, 57, 58, nil, 37, 83,
- 61, nil, 59, 60, 62, 258, 259, 66, 67, nil,
- nil, nil, nil, nil, 257, 291, 295, 90, 89, 91,
- 92, nil, nil, 219, nil, nil, nil, nil, nil, nil,
- 41, nil, nil, 94, 93, nil, 84, 50, 86, 85,
- 87, nil, 88, 95, 96, nil, 81, 82, nil, 38,
- 39, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 210, nil, nil, 214, nil, nil, 52, nil,
- nil, 54, nil, nil, nil, nil, nil, 40, nil, nil,
- nil, nil, nil, nil, nil, 218, nil, nil, nil, nil,
- 79, 73, 75, 76, 77, 78, nil, nil, nil, 74,
- 80, nil, nil, nil, 63, 64, 65, 56, 51, nil,
- 53, nil, 57, 58, nil, 37, 83, 61, nil, 59,
- 60, 62, 258, 259, 66, 67, nil, nil, nil, nil,
- nil, 257, 291, 295, 90, 89, 91, 92, nil, nil,
- 219, nil, nil, nil, nil, nil, nil, 41, nil, nil,
- 94, 93, nil, 84, 50, 86, 85, 87, nil, 88,
- 95, 96, nil, 81, 82, nil, 38, 39, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 210,
- nil, nil, 214, nil, nil, 52, nil, nil, 54, nil,
- nil, nil, nil, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 218, nil, nil, nil, nil, 79, 73, 75,
- 76, 77, 78, nil, nil, nil, 74, 80, nil, nil,
- nil, 63, 64, 65, 56, 51, nil, 53, nil, 57,
- 58, nil, 37, 83, 61, nil, 59, 60, 62, 258,
- 259, 66, 67, nil, nil, nil, nil, nil, 257, 291,
- 295, 90, 89, 91, 92, nil, nil, 219, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 94, 93, nil,
- 84, 50, 86, 85, 87, nil, 88, 95, 96, nil,
- 81, 82, nil, 38, 39, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 210, nil, nil, 214,
- nil, nil, 52, nil, nil, 54, nil, nil, nil, nil,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 218,
- nil, nil, nil, nil, 79, 73, 75, 76, 77, 78,
- nil, nil, nil, 74, 80, nil, nil, nil, 63, 64,
- 65, 56, 51, nil, 53, nil, 57, 58, nil, 37,
- 83, 61, nil, 59, 60, 62, 258, 259, 66, 67,
- nil, nil, nil, nil, nil, 257, 291, 295, 90, 89,
- 91, 92, nil, nil, 219, nil, nil, nil, nil, nil,
- nil, 41, nil, nil, 94, 93, nil, 84, 50, 86,
- 85, 87, nil, 88, 95, 96, nil, 81, 82, nil,
- 38, 39, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 210, nil, nil, 214, nil, nil, 52,
- nil, nil, 54, nil, nil, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 218, nil, nil, nil,
- nil, 79, 73, 75, 76, 77, 78, nil, nil, nil,
- 74, 80, nil, nil, nil, 63, 64, 65, 56, 51,
- nil, 53, nil, 57, 58, nil, 37, 83, 61, nil,
- 59, 60, 62, 258, 259, 66, 67, nil, nil, nil,
- nil, nil, 257, 291, 295, 90, 89, 91, 92, nil,
- nil, 219, nil, nil, nil, nil, nil, nil, 41, nil,
- nil, 94, 93, nil, 84, 50, 86, 85, 87, nil,
- 88, 95, 96, nil, 81, 82, nil, 38, 39, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 210, nil, nil, 214, nil, nil, 52, nil, nil, 54,
- nil, nil, nil, nil, nil, 40, nil, nil, nil, nil,
- nil, nil, nil, 218, nil, nil, nil, nil, 79, 73,
- 75, 76, 77, 78, nil, nil, nil, 74, 80, nil,
- nil, nil, 63, 64, 65, 56, 51, nil, 53, nil,
- 57, 58, nil, 37, 83, 61, nil, 59, 60, 62,
- 258, 259, 66, 67, nil, nil, nil, nil, nil, 257,
- 291, 295, 90, 89, 91, 92, nil, nil, 219, nil,
- nil, nil, nil, nil, nil, 41, nil, nil, 94, 93,
- nil, 84, 50, 86, 85, 87, nil, 88, 95, 96,
- nil, 81, 82, nil, 38, 39, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 210, nil, nil,
- 214, nil, nil, 52, nil, nil, 54, nil, nil, nil,
- nil, nil, 40, nil, nil, nil, nil, nil, nil, nil,
- 218, nil, nil, nil, nil, 79, 73, 75, 76, 77,
- 78, nil, nil, nil, 74, 80, nil, nil, nil, 63,
- 64, 65, 56, 51, nil, 53, nil, 57, 58, nil,
- 37, 83, 61, nil, 59, 60, 62, 258, 259, 66,
- 67, nil, nil, nil, nil, nil, 257, 291, 295, 90,
- 89, 91, 92, nil, nil, 219, nil, nil, nil, nil,
- nil, nil, 41, nil, nil, 94, 93, nil, 84, 50,
- 86, 85, 87, nil, 88, 95, 96, nil, 81, 82,
- nil, 38, 39, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 210, nil, nil, 214, nil, nil,
- 52, nil, nil, 54, nil, nil, nil, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 218, nil, nil,
- nil, nil, 79, 73, 75, 76, 77, 78, nil, nil,
- nil, 74, 80, nil, nil, nil, 63, 64, 65, 56,
- 51, nil, 53, nil, 57, 58, nil, 37, 83, 61,
- nil, 59, 60, 62, 258, 259, 66, 67, nil, nil,
- nil, nil, nil, 257, 291, 295, 90, 89, 91, 92,
- nil, nil, 219, nil, nil, nil, nil, nil, nil, 41,
- nil, nil, 94, 93, nil, 84, 50, 86, 85, 87,
- nil, 88, 95, 96, nil, 81, 82, nil, 38, 39,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 210, nil, nil, 214, nil, nil, 52, nil, nil,
- 54, nil, nil, nil, nil, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 218, nil, nil, nil, nil, 79,
- 73, 75, 76, 77, 78, nil, nil, nil, 74, 80,
- nil, nil, nil, 63, 64, 65, 56, 51, nil, 53,
- nil, 57, 58, nil, 37, 83, 61, nil, 59, 60,
- 62, 258, 259, 66, 67, nil, nil, nil, nil, nil,
- 257, 291, 295, 90, 89, 91, 92, nil, nil, 219,
- nil, nil, nil, nil, nil, nil, 41, nil, nil, 94,
- 93, nil, 84, 50, 86, 85, 87, nil, 88, 95,
- 96, nil, 81, 82, nil, 38, 39, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 210, nil,
- nil, 214, nil, nil, 52, nil, nil, 54, nil, nil,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 218, nil, nil, nil, nil, 79, 73, 75, 76,
- 77, 78, nil, nil, nil, 74, 80, nil, nil, nil,
- 63, 64, 65, 56, 51, nil, 53, nil, 57, 58,
- nil, 37, 83, 61, nil, 59, 60, 62, 258, 259,
- 66, 67, nil, nil, nil, nil, nil, 257, 291, 295,
- 90, 89, 91, 92, nil, nil, 219, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, 94, 93, nil, 84,
- 50, 86, 85, 87, nil, 88, 95, 96, nil, 81,
- 82, nil, 38, 39, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 210, nil, nil, 214, nil,
- nil, 52, nil, nil, 54, nil, nil, nil, nil, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 218, nil,
- nil, nil, nil, 79, 73, 75, 76, 77, 78, nil,
- nil, nil, 74, 80, nil, nil, nil, 63, 64, 65,
- 56, 51, nil, 53, nil, 57, 58, nil, 37, 83,
- 61, nil, 59, 60, 62, 258, 259, 66, 67, nil,
- nil, nil, nil, nil, 257, 291, 295, 90, 89, 91,
- 92, nil, nil, 219, nil, nil, nil, nil, nil, nil,
- 41, nil, nil, 94, 93, nil, 84, 50, 86, 85,
- 87, nil, 88, 95, 96, nil, 81, 82, nil, 38,
- 39, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 210, nil, nil, 214, nil, nil, 52, nil,
- nil, 54, nil, nil, nil, nil, nil, 40, nil, nil,
- nil, nil, nil, nil, nil, 218, nil, nil, nil, nil,
- 79, 73, 75, 76, 77, 78, nil, nil, nil, 74,
- 80, nil, nil, nil, 63, 64, 65, 56, 51, nil,
- 53, nil, 57, 58, nil, 37, 83, 61, nil, 59,
- 60, 62, 258, 259, 66, 67, nil, nil, nil, nil,
- nil, 257, 291, 295, 90, 89, 91, 92, nil, nil,
- 219, nil, nil, nil, nil, nil, nil, 41, nil, nil,
- 94, 93, nil, 84, 50, 86, 85, 87, nil, 88,
- 95, 96, nil, 81, 82, nil, 38, 39, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 210,
- nil, nil, 214, nil, nil, 52, nil, nil, 54, nil,
- nil, nil, nil, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 218, nil, nil, nil, nil, 79, 73, 75,
- 76, 77, 78, nil, nil, nil, 74, 80, nil, nil,
- nil, 63, 64, 65, 56, 51, nil, 53, nil, 57,
- 58, nil, 37, 83, 61, nil, 59, 60, 62, 258,
- 259, 66, 67, nil, nil, nil, nil, nil, 257, 291,
- 295, 90, 89, 91, 92, nil, nil, 219, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 94, 93, nil,
- 84, 50, 86, 85, 87, nil, 88, 95, 96, nil,
- 81, 82, nil, 38, 39, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 210, nil, nil, 214,
- nil, nil, 52, nil, nil, 54, nil, nil, nil, nil,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 218,
- nil, nil, nil, nil, 79, 73, 75, 76, 77, 78,
- nil, nil, nil, 74, 80, nil, nil, nil, 63, 64,
- 65, 56, 51, nil, 53, nil, 57, 58, nil, 37,
- 83, 61, nil, 59, 60, 62, 258, 259, 66, 67,
- nil, nil, nil, nil, nil, 257, 291, 295, 90, 89,
- 91, 92, nil, nil, 219, nil, nil, nil, nil, nil,
- nil, 41, nil, nil, 94, 93, nil, 84, 50, 86,
- 85, 87, nil, 88, 95, 96, nil, 81, 82, nil,
- 38, 39, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 210, nil, nil, 214, nil, nil, 52,
- nil, nil, 54, nil, nil, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 218, nil, nil, nil,
- nil, 79, 73, 75, 76, 77, 78, nil, nil, nil,
- 74, 80, nil, nil, nil, 63, 64, 65, 56, 51,
- nil, 53, nil, 57, 58, nil, 37, 83, 61, nil,
- 59, 60, 62, 258, 259, 66, 67, nil, nil, nil,
- nil, nil, 257, 291, 295, 90, 89, 91, 92, nil,
- nil, 219, nil, nil, nil, nil, nil, nil, 41, nil,
- nil, 94, 93, nil, 84, 50, 86, 85, 87, nil,
- 88, 95, 96, nil, 81, 82, nil, 38, 39, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 210, nil, nil, 214, nil, nil, 52, nil, nil, 54,
- nil, nil, nil, nil, nil, 40, nil, nil, nil, nil,
- nil, nil, nil, 218, nil, nil, nil, nil, 79, 73,
- 75, 76, 77, 78, nil, nil, nil, 74, 80, nil,
- nil, nil, 63, 64, 65, 56, 51, nil, 53, nil,
- 57, 58, nil, 37, 83, 61, nil, 59, 60, 62,
- 258, 259, 66, 67, nil, nil, nil, nil, nil, 257,
- 291, 295, 90, 89, 91, 92, nil, nil, 219, nil,
- nil, nil, nil, nil, nil, 41, nil, nil, 94, 93,
- nil, 84, 50, 86, 85, 87, nil, 88, 95, 96,
- nil, 81, 82, nil, 38, 39, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 210, nil, nil,
- 214, nil, nil, 52, nil, nil, 54, nil, nil, nil,
- nil, nil, 40, nil, nil, nil, nil, nil, nil, nil,
- 218, nil, nil, nil, nil, 79, 73, 75, 76, 77,
- 78, nil, nil, nil, 74, 80, nil, nil, nil, 63,
- 64, 65, 56, 51, nil, 53, nil, 57, 58, nil,
- 37, 83, 61, nil, 59, 60, 62, 258, 259, 66,
- 67, nil, nil, nil, nil, nil, 257, 291, 295, 90,
- 89, 91, 92, nil, nil, 219, nil, nil, nil, nil,
- nil, nil, 41, nil, nil, 94, 93, nil, 84, 50,
- 86, 85, 87, nil, 88, 95, 96, nil, 81, 82,
- nil, 38, 39, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 210, nil, nil, 214, nil, nil,
- 52, nil, nil, 54, nil, nil, nil, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 218, nil, nil,
- nil, nil, 79, 73, 75, 76, 77, 78, nil, nil,
- nil, 74, 80, nil, nil, nil, 63, 64, 65, 56,
- 51, nil, 53, nil, 57, 58, nil, 37, 83, 61,
- nil, 59, 60, 62, 258, 259, 66, 67, nil, nil,
- nil, nil, nil, 257, 291, 295, 90, 89, 91, 92,
- nil, nil, 219, nil, nil, nil, nil, nil, nil, 41,
- nil, nil, 94, 93, nil, 84, 50, 86, 85, 87,
- nil, 88, 95, 96, nil, 81, 82, nil, 38, 39,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 210, nil, nil, 214, nil, nil, 52, nil, nil,
- 54, nil, nil, nil, nil, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 218, nil, nil, nil, nil, 79,
- 73, 75, 76, 77, 78, nil, nil, nil, 74, 80,
- nil, nil, nil, 63, 64, 65, 56, 51, nil, 53,
- nil, 57, 58, nil, 37, 83, 61, nil, 59, 60,
- 62, 258, 259, 66, 67, nil, nil, nil, nil, nil,
- 257, 291, 295, 90, 89, 91, 92, nil, nil, 219,
- nil, nil, nil, nil, nil, nil, 41, nil, nil, 94,
- 93, nil, 84, 50, 86, 85, 87, nil, 88, 95,
- 96, nil, 81, 82, nil, 38, 39, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 210, nil,
- nil, 214, nil, nil, 52, nil, nil, 54, nil, nil,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 218, nil, nil, nil, nil, 79, 73, 75, 76,
- 77, 78, nil, nil, nil, 74, 80, nil, nil, nil,
- 63, 64, 65, 56, 51, nil, 53, nil, 57, 58,
- nil, 37, 83, 61, nil, 59, 60, 62, 258, 259,
- 66, 67, nil, nil, nil, nil, nil, 257, 291, 295,
- 90, 89, 91, 92, nil, nil, 219, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, 94, 93, nil, 84,
- 50, 86, 85, 87, nil, 88, 95, 96, nil, 81,
- 82, nil, 38, 39, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 210, nil, nil, 214, nil,
- nil, 52, nil, nil, 54, nil, nil, nil, nil, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 218, nil,
- nil, nil, nil, 79, 73, 75, 76, 77, 78, nil,
- nil, nil, 74, 80, nil, nil, nil, 63, 64, 65,
- 56, 51, nil, 53, nil, 57, 58, nil, 37, 83,
- 61, nil, 59, 60, 62, 258, 259, 66, 67, nil,
- nil, nil, nil, nil, 257, 291, 295, 90, 89, 91,
- 92, nil, nil, 219, nil, nil, nil, nil, nil, nil,
- 41, nil, nil, 94, 93, nil, 84, 50, 86, 85,
- 87, nil, 88, 95, 96, nil, 81, 82, nil, 38,
- 39, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 210, nil, nil, 214, nil, nil, 52, nil,
- nil, 54, nil, nil, nil, nil, nil, 40, nil, nil,
- nil, nil, nil, nil, nil, 218, nil, nil, nil, nil,
- 79, 73, 75, 76, 77, 78, nil, nil, nil, 74,
- 80, nil, nil, nil, 63, 64, 65, 56, 51, nil,
- 53, nil, 57, 58, nil, 37, 83, 61, nil, 59,
- 60, 62, 258, 259, 66, 67, nil, nil, nil, nil,
- nil, 257, 28, 27, 90, 89, 91, 92, nil, nil,
- 219, nil, nil, nil, nil, nil, nil, 41, nil, nil,
- 94, 93, nil, 84, 50, 86, 85, 87, 261, 88,
- 95, 96, nil, 81, 82, nil, 38, 39, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 210,
- nil, nil, 214, nil, nil, 52, nil, nil, 54, nil,
- 256, nil, 254, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 218, nil, nil, nil, nil, 79, 73, 75,
- 76, 77, 78, nil, nil, nil, 74, 80, nil, nil,
- nil, 63, 64, 65, 56, 51, nil, 53, nil, 57,
- 58, nil, 37, 83, 61, nil, 59, 60, 62, 258,
- 259, 66, 67, nil, nil, nil, nil, nil, 257, 28,
- 27, 90, 89, 91, 92, nil, nil, 219, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 94, 93, nil,
- 84, 50, 86, 85, 87, 261, 88, 95, 96, nil,
- 81, 82, nil, 38, 39, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 210, nil, nil, 214,
- nil, nil, 506, nil, nil, 54, nil, 256, nil, 254,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 218,
- nil, nil, nil, nil, 79, 73, 75, 76, 77, 78,
- nil, nil, nil, 74, 80, nil, nil, nil, 63, 64,
- 65, 56, 51, nil, 53, nil, 57, 58, nil, 37,
- 83, 61, nil, 59, 60, 62, 258, 259, 66, 67,
- nil, nil, nil, nil, nil, 257, 28, 27, 90, 89,
- 91, 92, nil, nil, 219, nil, nil, nil, nil, nil,
- nil, 41, nil, nil, 94, 93, nil, 84, 50, 86,
- 85, 87, 261, 88, 95, 96, nil, 81, 82, nil,
- 38, 39, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 210, nil, nil, 214, nil, 510, 52,
- nil, nil, 54, nil, 256, nil, 254, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 218, nil, nil, nil,
- nil, 79, 73, 75, 76, 77, 78, nil, nil, nil,
- 74, 80, nil, nil, nil, nil, nil, nil, 56, nil,
- nil, 53, nil, nil, nil, nil, 37, 83, 63, 64,
- 65, 8, 51, nil, nil, nil, 57, 58, nil, nil,
- nil, 61, nil, 59, 60, 62, 23, 24, 66, 67,
- nil, nil, nil, nil, nil, 22, 28, 27, 90, 89,
- 91, 92, nil, nil, 17, nil, nil, nil, nil, nil,
- 7, 41, nil, 9, 94, 93, nil, 84, 50, 86,
- 85, 87, nil, 88, 95, 96, nil, 81, 82, nil,
- 38, 39, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 36, nil, nil, 281, nil, nil, 52,
- nil, nil, 54, nil, 32, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 18, nil, nil, nil,
- nil, 79, 73, 75, 76, 77, 78, nil, nil, nil,
- 74, 80, nil, nil, nil, 63, 64, 65, 56, 51,
- nil, 53, nil, 57, 58, nil, 37, 83, 61, nil,
- 59, 60, 62, 258, 259, 66, 67, nil, nil, nil,
- nil, nil, 257, 291, 295, 90, 89, 91, 92, nil,
- nil, 219, nil, nil, nil, nil, nil, nil, 292, nil,
- nil, 94, 93, nil, 84, 50, 86, 85, 87, nil,
- 88, 95, 96, nil, 81, 82, 764, nil, 336, 334,
- 333, 754, 335, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 751, nil, nil, nil, nil, nil, nil, nil,
- 289, nil, nil, 214, nil, nil, 52, nil, nil, 54,
- nil, nil, nil, nil, nil, 338, nil, nil, nil, nil,
- nil, nil, nil, 341, 340, 344, 343, nil, 79, 73,
- 75, 76, 77, 78, nil, nil, nil, 74, 80, nil,
- nil, nil, 518, nil, nil, 56, nil, nil, 53, nil,
- nil, nil, nil, 296, 83, 63, 64, 65, 8, 51,
- nil, nil, 752, 57, 58, nil, nil, nil, 61, nil,
- 59, 60, 62, 23, 24, 66, 67, nil, nil, nil,
- nil, nil, 22, 28, 27, 90, 89, 91, 92, nil,
- nil, 17, nil, nil, nil, nil, nil, 7, 41, nil,
- 9, 94, 93, nil, 84, 50, 86, 85, 87, nil,
- 88, 95, 96, nil, 81, 82, nil, 38, 39, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 36, nil, nil, 281, nil, nil, 52, nil, nil, 54,
- nil, 32, nil, nil, nil, 40, nil, nil, nil, nil,
- nil, nil, nil, 18, nil, nil, nil, nil, 79, 73,
- 75, 76, 77, 78, nil, nil, nil, 74, 80, nil,
- nil, nil, 63, 64, 65, 56, 51, nil, 53, nil,
- 57, 58, nil, 37, 83, 61, nil, 59, 60, 62,
- 258, 259, 66, 67, nil, nil, nil, nil, nil, 257,
- 291, 295, 90, 89, 91, 92, nil, nil, 219, nil,
- nil, nil, nil, nil, nil, 292, nil, nil, 94, 93,
- nil, 84, 50, 86, 85, 87, nil, 88, 95, 96,
- nil, 81, 82, 327, nil, 336, 334, 333, nil, 335,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 289, nil, nil,
- 286, nil, nil, 52, nil, nil, 54, nil, nil, nil,
- nil, nil, 338, 322, nil, nil, nil, nil, nil, nil,
- 341, 340, 344, 343, nil, 79, 73, 75, 76, 77,
- 78, nil, nil, nil, 74, 80, nil, nil, nil, 63,
- 64, 65, 56, 51, nil, 53, nil, 57, 58, nil,
- 296, 83, 61, nil, 59, 60, 62, 258, 259, 66,
- 67, nil, nil, nil, nil, nil, 257, 291, 295, 90,
- 89, 91, 92, nil, nil, 219, nil, nil, nil, nil,
- nil, nil, 41, nil, nil, 94, 93, nil, 84, 50,
- 86, 85, 87, nil, 88, 95, 96, nil, 81, 82,
- nil, 38, 39, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 210, nil, nil, 214, 537, nil,
- 52, nil, nil, 54, nil, nil, nil, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 218, nil, nil,
- nil, nil, 79, 73, 75, 76, 77, 78, nil, nil,
- nil, 74, 80, nil, nil, nil, nil, nil, nil, 56,
- nil, nil, 53, nil, nil, nil, nil, 37, 83, 63,
- 64, 65, 8, 51, nil, nil, nil, 57, 58, nil,
- nil, nil, 61, nil, 59, 60, 62, 23, 24, 66,
- 67, nil, nil, nil, nil, nil, 22, 28, 27, 90,
- 89, 91, 92, nil, nil, 17, nil, nil, nil, nil,
- nil, 7, 41, nil, 9, 94, 93, nil, 84, 50,
- 86, 85, 87, nil, 88, 95, 96, nil, 81, 82,
- nil, 38, 39, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 36, nil, nil, 30, nil, nil,
- 52, nil, nil, 54, nil, 32, nil, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 18, nil, nil,
- nil, nil, 79, 73, 75, 76, 77, 78, nil, nil,
- nil, 74, 80, nil, nil, nil, 63, 64, 65, 56,
- 51, nil, 53, nil, 57, 58, nil, 37, 83, 61,
- nil, 59, 60, 62, 23, 24, 66, 67, nil, nil,
- nil, nil, nil, 22, 28, 27, 90, 89, 91, 92,
- nil, nil, 17, nil, nil, nil, nil, nil, nil, 41,
- nil, nil, 94, 93, nil, 84, 50, 86, 85, 87,
- nil, 88, 95, 96, nil, 81, 82, nil, 38, 39,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 210, nil, nil, 214, nil, nil, 52, nil, nil,
- 54, nil, nil, nil, nil, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 18, nil, nil, nil, nil, 79,
- 73, 75, 76, 77, 78, nil, nil, nil, 74, 80,
- nil, nil, nil, 63, 64, 65, 56, 51, nil, 53,
- nil, 57, 58, nil, 37, 83, 61, nil, 59, 60,
- 62, 23, 24, 66, 67, nil, nil, nil, nil, nil,
- 22, 28, 27, 90, 89, 91, 92, nil, nil, 17,
- nil, nil, nil, nil, nil, nil, 41, nil, nil, 94,
- 93, nil, 84, 50, 86, 85, 87, nil, 88, 95,
- 96, nil, 81, 82, nil, 38, 39, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 210, nil,
- nil, 214, nil, nil, 52, nil, nil, 54, nil, nil,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 18, nil, nil, nil, nil, 79, 73, 75, 76,
- 77, 78, nil, nil, nil, 74, 80, nil, nil, nil,
- 63, 64, 65, 56, 51, nil, 53, nil, 57, 58,
- nil, 37, 83, 61, nil, 59, 60, 62, 258, 259,
- 66, 67, nil, nil, nil, nil, nil, 257, 291, 295,
- 90, 89, 91, 92, nil, nil, 219, nil, nil, nil,
- nil, nil, nil, 292, nil, nil, 94, 93, nil, 84,
- 50, 86, 85, 361, nil, 88, 95, 96, nil, 81,
- 82, 327, nil, 336, 334, 333, nil, 335, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 362, nil, nil, 214, nil,
- nil, 52, nil, nil, 54, nil, nil, nil, nil, nil,
- 338, nil, 554, nil, nil, nil, nil, nil, 341, 340,
- 344, 343, nil, 79, 73, 75, 76, 77, 78, nil,
- nil, nil, 74, 80, nil, nil, nil, 63, 64, 65,
- 56, 51, nil, 53, nil, 57, 58, nil, 296, 83,
- 61, nil, 59, 60, 62, 23, 24, 66, 67, nil,
- nil, nil, nil, nil, 22, 28, 27, 90, 89, 91,
- 92, nil, nil, 17, nil, nil, nil, nil, nil, nil,
- 41, nil, nil, 94, 93, nil, 84, 50, 86, 85,
- 87, nil, 88, 95, 96, nil, 81, 82, nil, 38,
- 39, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 210, nil, nil, 214, nil, nil, 52, nil,
- nil, 54, nil, nil, nil, nil, nil, 40, nil, nil,
- nil, nil, nil, nil, nil, 18, nil, nil, nil, nil,
- 79, 73, 75, 76, 77, 78, nil, nil, nil, 74,
- 80, nil, nil, nil, 63, 64, 65, 56, 51, nil,
- 53, nil, 57, 58, nil, 37, 83, 61, nil, 59,
- 60, 62, 258, 259, 66, 67, nil, nil, nil, nil,
- nil, 257, 291, 295, 90, 89, 91, 92, nil, nil,
- 219, nil, nil, nil, nil, nil, nil, 41, nil, nil,
- 94, 93, nil, 84, 50, 86, 85, 87, nil, 88,
- 95, 96, nil, 81, 82, nil, 38, 39, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 210,
- nil, nil, 214, nil, nil, 52, nil, nil, 54, nil,
- nil, nil, nil, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 218, nil, nil, nil, nil, 79, 73, 75,
- 76, 77, 78, nil, nil, nil, 74, 80, nil, nil,
- nil, -255, -255, -255, 56, -255, nil, 53, nil, -255,
- -255, nil, 37, 83, -255, nil, -255, -255, -255, -255,
- -255, -255, -255, nil, nil, nil, nil, nil, -255, -255,
- -255, -255, -255, -255, -255, nil, nil, -255, nil, nil,
- nil, nil, nil, nil, -255, nil, nil, -255, -255, nil,
- -255, -255, -255, -255, -255, -255, -255, -255, -255, nil,
- -255, -255, nil, -255, -255, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, -255, nil, nil, -255,
- 268, nil, -255, nil, nil, -255, nil, -255, nil, -255,
- nil, -255, nil, nil, nil, nil, nil, nil, nil, -255,
- nil, nil, nil, nil, -255, -255, -255, -255, -255, -255,
- nil, nil, nil, -255, -255, nil, nil, nil, -536, -536,
- -536, -255, -536, nil, -255, nil, -536, -536, nil, -255,
- -255, -536, nil, -536, -536, -536, -536, -536, -536, -536,
- nil, nil, nil, nil, nil, -536, -536, -536, -536, -536,
- -536, -536, nil, nil, -536, nil, nil, nil, nil, nil,
- nil, -536, nil, nil, -536, -536, nil, -536, -536, -536,
- -536, -536, -536, -536, -536, -536, nil, -536, -536, nil,
- -536, -536, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, -536, nil, nil, -536, -536, nil, -536,
- nil, nil, -536, nil, -536, nil, -536, nil, -536, nil,
- nil, nil, nil, nil, nil, nil, -536, nil, nil, nil,
- nil, -536, -536, -536, -536, -536, -536, nil, nil, nil,
- -536, -536, nil, nil, nil, -537, -537, -537, -536, -537,
- nil, -536, nil, -537, -537, nil, -536, -536, -537, nil,
- -537, -537, -537, -537, -537, -537, -537, nil, nil, nil,
- nil, nil, -537, -537, -537, -537, -537, -537, -537, nil,
- nil, -537, nil, nil, nil, nil, nil, nil, -537, nil,
- nil, -537, -537, nil, -537, -537, -537, -537, -537, -537,
- -537, -537, -537, nil, -537, -537, nil, -537, -537, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- -537, nil, nil, -537, -537, nil, -537, nil, nil, -537,
- nil, -537, nil, -537, nil, -537, nil, nil, nil, nil,
- nil, nil, nil, -537, nil, nil, nil, nil, -537, -537,
- -537, -537, -537, -537, nil, nil, nil, -537, -537, nil,
- nil, nil, -255, -255, -255, -537, -255, nil, -537, nil,
- -255, -255, nil, -537, -537, -255, nil, -255, -255, -255,
- -255, -255, -255, -255, nil, nil, nil, nil, nil, -255,
- -255, -255, -255, -255, -255, -255, nil, nil, -255, nil,
- nil, nil, nil, nil, nil, -255, nil, nil, -255, -255,
- nil, -255, -255, -255, -255, -255, -255, -255, -255, -255,
- nil, -255, -255, nil, -255, -255, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, -255, nil, nil,
- -255, 268, nil, -255, nil, nil, -255, nil, -255, nil,
- -255, nil, -255, nil, nil, nil, nil, nil, nil, nil,
- -255, nil, nil, nil, nil, -255, -255, -255, -255, -255,
- -255, nil, nil, nil, -255, -255, nil, nil, nil, 63,
- 64, 65, -255, 51, nil, -255, nil, 57, 58, nil,
- -255, -255, 61, nil, 59, 60, 62, 258, 259, 66,
- 67, nil, nil, nil, nil, nil, 257, 28, 27, 90,
- 89, 91, 92, nil, nil, 219, nil, nil, nil, nil,
- nil, nil, 41, nil, nil, 94, 93, nil, 84, 50,
- 86, 85, 87, 261, 88, 95, 96, nil, 81, 82,
- nil, 38, 39, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 210, nil, nil, 214, nil, nil,
- 52, nil, nil, 54, nil, 256, nil, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 218, nil, nil,
- nil, nil, 79, 73, 75, 76, 77, 78, nil, nil,
- nil, 74, 80, nil, nil, nil, 63, 64, 65, 56,
- 51, nil, 53, nil, 57, 58, nil, 37, 83, 61,
- nil, 59, 60, 62, 258, 259, 66, 67, nil, nil,
- nil, nil, nil, 257, 28, 27, 90, 89, 91, 92,
- nil, nil, 219, nil, nil, nil, nil, nil, nil, 41,
- nil, nil, 94, 93, nil, 84, 50, 86, 85, 87,
- 261, 88, 95, 96, nil, 81, 82, nil, 38, 39,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 210, nil, nil, 214, nil, nil, 52, nil, nil,
- 54, nil, 256, nil, nil, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 218, nil, nil, nil, nil, 79,
- 73, 75, 76, 77, 78, nil, nil, nil, 74, 80,
- nil, nil, nil, 63, 64, 65, 56, 51, nil, 53,
- nil, 57, 58, nil, 37, 83, 61, nil, 59, 60,
- 62, 258, 259, 66, 67, nil, nil, nil, nil, nil,
- 257, 291, 295, 90, 89, 91, 92, nil, nil, 219,
- nil, nil, nil, nil, nil, nil, 41, nil, nil, 94,
- 93, nil, 84, 50, 86, 85, 87, nil, 88, 95,
- 96, nil, 81, 82, nil, 38, 39, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 210, nil,
- nil, 214, nil, nil, 52, nil, nil, 54, nil, nil,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 218, nil, nil, nil, nil, 79, 73, 75, 76,
- 77, 78, nil, nil, nil, 74, 80, nil, nil, nil,
- 63, 64, 65, 56, 51, nil, 53, nil, 57, 58,
- nil, 37, 83, 61, nil, 59, 60, 62, 258, 259,
- 66, 67, nil, nil, nil, nil, nil, 257, 291, 295,
- 90, 89, 91, 92, nil, nil, 219, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, 94, 93, nil, 84,
- 50, 86, 85, 87, nil, 88, 95, 96, nil, 81,
- 82, nil, 38, 39, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 210, nil, nil, 214, nil,
- nil, 52, nil, nil, 54, nil, nil, nil, nil, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 218, nil,
- nil, nil, nil, 79, 73, 75, 76, 77, 78, nil,
- nil, nil, 74, 80, nil, nil, nil, 63, 64, 65,
- 56, 51, nil, 53, nil, 57, 58, nil, 37, 83,
- 61, nil, 59, 60, 62, 258, 259, 66, 67, nil,
- nil, nil, nil, nil, 257, 291, 295, 90, 89, 91,
- 92, nil, nil, 219, nil, nil, nil, nil, nil, nil,
- 41, nil, nil, 94, 93, nil, 84, 50, 86, 85,
- 87, nil, 88, 95, 96, nil, 81, 82, nil, 38,
- 39, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 210, nil, nil, 214, nil, nil, 52, nil,
- nil, 54, nil, nil, nil, nil, nil, 40, nil, nil,
- nil, nil, nil, nil, nil, 218, nil, nil, nil, nil,
- 79, 73, 75, 76, 77, 78, nil, nil, nil, 74,
- 80, nil, nil, nil, 63, 64, 65, 56, 51, nil,
- 53, nil, 57, 58, nil, 37, 83, 61, nil, 59,
- 60, 62, 258, 259, 66, 67, nil, nil, nil, nil,
- nil, 257, 291, 295, 90, 89, 91, 92, nil, nil,
- 219, nil, nil, nil, nil, nil, nil, 41, nil, nil,
- 94, 93, nil, 84, 50, 86, 85, 87, 261, 88,
- 95, 96, nil, 81, 82, nil, 38, 39, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 210,
- nil, nil, 214, nil, nil, 52, nil, nil, 54, nil,
- 658, nil, 254, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 218, nil, nil, nil, nil, 79, 73, 75,
- 76, 77, 78, nil, nil, nil, 74, 80, nil, nil,
- nil, 63, 64, 65, 56, 51, nil, 53, nil, 57,
- 58, nil, 37, 83, 61, nil, 59, 60, 62, 258,
- 259, 66, 67, nil, nil, nil, nil, nil, 257, 291,
- 295, 90, 89, 91, 92, nil, nil, 219, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 94, 93, nil,
- 84, 50, 86, 85, 87, 261, 88, 95, 96, nil,
- 81, 82, nil, 38, 39, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 210, nil, nil, 214,
- nil, nil, 52, nil, nil, 54, nil, nil, nil, 254,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 218,
- nil, nil, nil, nil, 79, 73, 75, 76, 77, 78,
- nil, nil, nil, 74, 80, nil, nil, nil, 63, 64,
- 65, 56, 51, nil, 53, nil, 57, 58, nil, 37,
- 83, 61, nil, 59, 60, 62, 258, 259, 66, 67,
- nil, nil, nil, nil, nil, 257, 291, 295, 90, 89,
- 91, 92, nil, nil, 219, nil, nil, nil, nil, nil,
- nil, 41, nil, nil, 94, 93, nil, 84, 50, 86,
- 85, 87, nil, 88, 95, 96, nil, 81, 82, nil,
- 38, 39, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 210, nil, nil, 214, nil, nil, 52,
- nil, nil, 54, nil, nil, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 218, nil, nil, nil,
- nil, 79, 73, 75, 76, 77, 78, nil, nil, nil,
- 74, 80, nil, nil, nil, 63, 64, 65, 56, 51,
- nil, 53, nil, 57, 58, nil, 37, 83, 61, nil,
- 59, 60, 62, 23, 24, 66, 67, nil, nil, nil,
- nil, nil, 22, 28, 27, 90, 89, 91, 92, nil,
- nil, 17, nil, nil, nil, nil, nil, nil, 41, nil,
- nil, 94, 93, nil, 84, 50, 86, 85, 87, nil,
- 88, 95, 96, nil, 81, 82, nil, 38, 39, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 210, nil, nil, 214, nil, 675, 52, nil, nil, 54,
- nil, nil, nil, 254, nil, 40, nil, nil, nil, nil,
- nil, nil, nil, 18, nil, nil, nil, nil, 79, 73,
- 75, 76, 77, 78, nil, nil, nil, 74, 80, nil,
- nil, nil, nil, nil, nil, 56, nil, nil, 53, nil,
- nil, nil, nil, 37, 83, 63, 64, 65, 8, 51,
- nil, nil, nil, 57, 58, nil, nil, nil, 61, nil,
- 59, 60, 62, 23, 24, 66, 67, nil, nil, nil,
- nil, nil, 22, 28, 27, 90, 89, 91, 92, nil,
- nil, 17, nil, nil, nil, nil, nil, 7, 41, nil,
- 9, 94, 93, nil, 84, 50, 86, 85, 87, nil,
- 88, 95, 96, nil, 81, 82, nil, 38, 39, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 36, nil, nil, 30, nil, nil, 52, nil, nil, 54,
- nil, 32, nil, nil, nil, 40, nil, nil, nil, nil,
- nil, nil, nil, 18, nil, nil, nil, nil, 79, 73,
- 75, 76, 77, 78, nil, nil, nil, 74, 80, nil,
- nil, nil, nil, nil, 404, 56, nil, nil, 53, nil,
- nil, nil, nil, 37, 83, 63, 64, 65, nil, 51,
- nil, nil, nil, 57, 58, nil, nil, nil, 61, nil,
- 59, 60, 62, 258, 259, 66, 67, nil, nil, nil,
- nil, nil, 257, 291, 295, 90, 89, 91, 92, nil,
- nil, 219, nil, nil, nil, nil, nil, nil, 292, nil,
- nil, 94, 93, nil, 84, 50, 86, 85, 87, nil,
- 88, 95, 96, nil, 81, 82, 327, nil, 336, 334,
- 333, nil, 335, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 289, nil, nil, 286, nil, nil, 52, nil, nil, 54,
- nil, nil, nil, nil, nil, 338, nil, nil, nil, nil,
- nil, nil, nil, 341, 340, 344, 343, nil, 79, 73,
- 75, 76, 77, 78, nil, nil, nil, 74, 80, nil,
- nil, nil, 63, 64, 65, 56, 51, nil, 53, nil,
- 57, 58, nil, 296, 83, 61, nil, 59, 60, 62,
- 258, 259, 66, 67, nil, nil, nil, nil, nil, 257,
- 28, 27, 90, 89, 91, 92, nil, nil, 219, nil,
- nil, nil, nil, nil, nil, 41, nil, nil, 94, 93,
- nil, 84, 50, 86, 85, 87, 261, 88, 95, 96,
- nil, 81, 82, nil, 38, 39, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 210, nil, nil,
- 214, nil, nil, 52, nil, nil, 54, nil, 256, nil,
- nil, nil, 40, nil, nil, nil, nil, nil, nil, nil,
- 218, nil, nil, nil, nil, 79, 73, 75, 76, 77,
- 78, nil, nil, nil, 74, 80, nil, nil, nil, 63,
- 64, 65, 56, 51, nil, 53, nil, 57, 58, nil,
- 37, 83, 61, nil, 59, 60, 62, 258, 259, 66,
- 67, nil, nil, nil, nil, nil, 257, 28, 27, 90,
- 89, 91, 92, nil, nil, 219, nil, nil, nil, nil,
- nil, nil, 41, nil, nil, 94, 93, nil, 84, 50,
- 86, 85, 87, 261, 88, 95, 96, nil, 81, 82,
- nil, 38, 39, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 210, nil, nil, 214, nil, nil,
- 52, nil, nil, 54, nil, 256, nil, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 218, nil, nil,
- nil, nil, 79, 73, 75, 76, 77, 78, nil, nil,
- nil, 74, 80, nil, nil, nil, 63, 64, 65, 56,
- 51, nil, 53, nil, 57, 58, nil, 37, 83, 61,
- nil, 59, 60, 62, 258, 259, 66, 67, nil, nil,
- nil, nil, nil, 257, 291, 295, 90, 89, 91, 92,
- nil, nil, 219, nil, nil, nil, nil, nil, nil, 41,
- nil, nil, 94, 93, nil, 84, 50, 86, 85, 87,
- nil, 88, 95, 96, nil, 81, 82, nil, 38, 39,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 210, nil, nil, 214, nil, nil, 52, nil, nil,
- 54, nil, nil, nil, nil, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 218, nil, nil, nil, nil, 79,
- 73, 75, 76, 77, 78, nil, nil, nil, 74, 80,
- nil, nil, nil, 63, 64, 65, 56, 51, nil, 53,
- nil, 57, 58, nil, 37, 83, 61, nil, 59, 60,
- 62, 258, 259, 66, 67, nil, nil, nil, nil, nil,
- 257, 291, 295, 90, 89, 91, 92, nil, nil, 219,
- nil, nil, nil, nil, nil, nil, 41, nil, nil, 94,
- 93, nil, 84, 50, 86, 85, 87, nil, 88, 95,
- 96, nil, 81, 82, nil, 38, 39, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 210, nil,
- nil, 214, nil, nil, 52, nil, nil, 54, nil, nil,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 218, nil, nil, nil, nil, 79, 73, 75, 76,
- 77, 78, nil, nil, nil, 74, 80, nil, nil, nil,
- 63, 64, 65, 56, 51, nil, 53, nil, 57, 58,
- nil, 37, 83, 61, nil, 59, 60, 62, 258, 259,
- 66, 67, nil, nil, nil, nil, nil, 257, 291, 295,
- 90, 89, 91, 92, nil, nil, 219, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, 94, 93, nil, 84,
- 50, 86, 85, 87, nil, 88, 95, 96, nil, 81,
- 82, nil, 38, 39, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 210, nil, nil, 214, nil,
- nil, 52, nil, nil, 54, nil, nil, nil, nil, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 218, nil,
- nil, nil, nil, 79, 73, 75, 76, 77, 78, nil,
- nil, nil, 74, 80, nil, nil, nil, 63, 64, 65,
- 56, 51, nil, 53, nil, 57, 58, nil, 37, 83,
- 61, nil, 59, 60, 62, 23, 24, 66, 67, nil,
- nil, nil, nil, nil, 22, 28, 27, 90, 89, 91,
- 92, nil, nil, 17, nil, nil, nil, nil, nil, nil,
- 41, nil, nil, 94, 93, nil, 84, 50, 86, 85,
- 87, nil, 88, 95, 96, nil, 81, 82, nil, 38,
- 39, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 210, nil, nil, 214, nil, nil, 52, nil,
- nil, 54, nil, nil, nil, nil, nil, 40, nil, nil,
- nil, nil, nil, nil, nil, 18, nil, nil, nil, nil,
- 79, 73, 75, 76, 77, 78, nil, nil, nil, 74,
- 80, nil, nil, nil, 63, 64, 65, 56, 51, nil,
- 53, nil, 57, 58, nil, 37, 83, 61, nil, 59,
- 60, 62, 258, 259, 66, 67, nil, nil, nil, nil,
- nil, 257, 291, 295, 90, 89, 91, 92, nil, nil,
- 219, nil, nil, nil, nil, nil, nil, 41, nil, nil,
- 94, 93, nil, 84, 50, 86, 85, 87, 261, 88,
- 95, 96, nil, 81, 82, nil, 38, 39, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 210,
- nil, nil, 214, nil, nil, 52, nil, nil, 54, nil,
- 658, nil, nil, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 218, nil, nil, nil, nil, 79, 73, 75,
- 76, 77, 78, nil, nil, nil, 74, 80, nil, nil,
- nil, 63, 64, 65, 56, 51, nil, 53, nil, 57,
- 58, nil, 37, 83, 61, nil, 59, 60, 62, 258,
- 259, 66, 67, nil, nil, nil, nil, nil, 257, 291,
- 295, 90, 89, 91, 92, nil, nil, 219, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 94, 93, nil,
- 84, 50, 86, 85, 87, 261, 88, 95, 96, nil,
- 81, 82, nil, 38, 39, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 210, nil, nil, 214,
- nil, nil, 52, nil, nil, 54, nil, nil, nil, nil,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 218,
- nil, nil, nil, nil, 79, 73, 75, 76, 77, 78,
- nil, nil, nil, 74, 80, nil, nil, nil, nil, nil,
- nil, 56, nil, nil, 53, nil, nil, nil, nil, 37,
- 83, 63, 64, 65, 8, 51, nil, nil, nil, 57,
- 58, nil, nil, nil, 61, nil, 59, 60, 62, 23,
- 24, 66, 67, nil, nil, nil, nil, nil, 22, 28,
- 27, 90, 89, 91, 92, nil, nil, 17, nil, nil,
- nil, nil, nil, 7, 41, nil, 9, 94, 93, nil,
- 84, 50, 86, 85, 87, nil, 88, 95, 96, nil,
- 81, 82, nil, 38, 39, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 36, nil, nil, 30,
- nil, nil, 52, nil, nil, 54, nil, 32, nil, nil,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 18,
- nil, nil, nil, nil, 79, 73, 75, 76, 77, 78,
- nil, nil, nil, 74, 80, nil, nil, nil, nil, nil,
- nil, 56, nil, nil, 53, nil, nil, nil, nil, 37,
- 83, 63, 64, 65, 8, 51, nil, nil, nil, 57,
- 58, nil, nil, nil, 61, nil, 59, 60, 62, 23,
- 24, 66, 67, nil, nil, nil, nil, nil, 22, 28,
- 27, 90, 89, 91, 92, nil, nil, 17, nil, nil,
- nil, nil, nil, 7, 41, nil, 9, 94, 93, nil,
- 84, 50, 86, 85, 87, nil, 88, 95, 96, nil,
- 81, 82, nil, 38, 39, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 36, nil, nil, 30,
- nil, nil, 52, nil, nil, 54, nil, 32, nil, nil,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 18,
- nil, nil, nil, nil, 79, 73, 75, 76, 77, 78,
- nil, nil, nil, 74, 80, nil, nil, nil, 63, 64,
- 65, 56, 51, nil, 53, nil, 57, 58, nil, 37,
- 83, 61, nil, 59, 60, 62, 258, 259, 66, 67,
- nil, nil, nil, nil, nil, 257, 291, 295, 90, 89,
- 91, 92, nil, nil, 219, nil, nil, nil, nil, nil,
- nil, 292, nil, nil, 94, 93, nil, 84, 50, 86,
- 85, 87, nil, 88, 95, 96, nil, 81, 82, 764,
- nil, 336, 334, 333, 754, 335, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 751, nil, nil, nil, nil,
- nil, nil, nil, 712, nil, nil, 214, nil, nil, 52,
- nil, nil, 54, nil, nil, nil, nil, nil, 338, nil,
- nil, nil, nil, nil, nil, nil, 341, 340, 344, 343,
- nil, 79, 73, 75, 76, 77, 78, nil, nil, nil,
- 74, 80, nil, nil, nil, nil, nil, nil, 56, nil,
- nil, 53, nil, nil, nil, nil, 296, 83, 63, 64,
- 65, 8, 51, nil, nil, 752, 57, 58, nil, nil,
- nil, 61, nil, 59, 60, 62, 23, 24, 66, 67,
- nil, nil, nil, nil, nil, 22, 28, 27, 90, 89,
- 91, 92, nil, nil, 17, nil, nil, nil, nil, nil,
- 7, 41, nil, 9, 94, 93, nil, 84, 50, 86,
- 85, 87, nil, 88, 95, 96, nil, 81, 82, nil,
- 38, 39, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 36, nil, nil, 30, nil, nil, 52,
- nil, nil, 54, nil, 32, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 18, nil, nil, nil,
- nil, 79, 73, 75, 76, 77, 78, nil, nil, nil,
- 74, 80, nil, nil, nil, nil, nil, nil, 56, nil,
- nil, 53, nil, nil, nil, nil, 37, 83, 63, 64,
- 65, 8, 51, nil, nil, nil, 57, 58, nil, nil,
- nil, 61, nil, 59, 60, 62, 23, 24, 66, 67,
- nil, nil, nil, nil, nil, 22, 28, 27, 90, 89,
- 91, 92, nil, nil, 17, nil, nil, nil, nil, nil,
- 7, 41, nil, 9, 94, 93, nil, 84, 50, 86,
- 85, 87, nil, 88, 95, 96, nil, 81, 82, nil,
- 38, 39, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 36, nil, nil, 30, nil, nil, 52,
- nil, nil, 54, nil, 32, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 18, nil, nil, nil,
- nil, 79, 73, 75, 76, 77, 78, nil, nil, nil,
- 74, 80, nil, nil, nil, nil, nil, nil, 56, nil,
- nil, 53, nil, nil, nil, nil, 37, 83, 63, 64,
- 65, 8, 51, nil, nil, nil, 57, 58, nil, nil,
- nil, 61, nil, 59, 60, 62, 23, 24, 66, 67,
- nil, nil, nil, nil, nil, 22, 28, 27, 90, 89,
- 91, 92, nil, nil, 17, nil, nil, nil, nil, nil,
- 7, 41, nil, 9, 94, 93, nil, 84, 50, 86,
- 85, 87, nil, 88, 95, 96, nil, 81, 82, nil,
- 38, 39, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 36, nil, nil, 30, nil, nil, 52,
- nil, nil, 54, nil, 32, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 18, nil, nil, nil,
- nil, 79, 73, 75, 76, 77, 78, nil, nil, nil,
- 74, 80, nil, nil, nil, 63, 64, 65, 56, 51,
- nil, 53, nil, 57, 58, nil, 37, 83, 61, nil,
- 59, 60, 62, 258, 259, 66, 67, nil, nil, nil,
- nil, nil, 257, 28, 27, 90, 89, 91, 92, nil,
- nil, 219, nil, nil, nil, nil, nil, nil, 41, nil,
- nil, 94, 93, nil, 84, 50, 86, 85, 87, 261,
- 88, 95, 96, nil, 81, 82, nil, 38, 39, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 210, nil, nil, 214, nil, nil, 52, nil, nil, 54,
- nil, 256, nil, nil, nil, 40, nil, nil, nil, nil,
- nil, nil, nil, 218, nil, nil, nil, nil, 79, 73,
- 75, 76, 77, 78, nil, nil, nil, 74, 80, nil,
- nil, nil, 63, 64, 65, 56, 51, nil, 53, nil,
- 57, 58, nil, 37, 83, 61, nil, 59, 60, 62,
- 258, 259, 66, 67, nil, nil, nil, nil, nil, 257,
- 28, 27, 90, 89, 91, 92, nil, nil, 219, nil,
- nil, nil, nil, nil, nil, 41, nil, nil, 94, 93,
- nil, 84, 50, 86, 85, 87, 261, 88, 95, 96,
- nil, 81, 82, nil, 38, 39, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 210, nil, nil,
- 214, nil, nil, 52, nil, nil, 54, nil, 256, nil,
- nil, nil, 40, nil, nil, nil, nil, nil, nil, nil,
- 218, nil, nil, nil, nil, 79, 73, 75, 76, 77,
- 78, nil, nil, nil, 74, 80, nil, nil, nil, 63,
- 64, 65, 56, 51, nil, 53, nil, 57, 58, nil,
- 37, 83, 61, nil, 59, 60, 62, 258, 259, 66,
- 67, nil, nil, nil, nil, nil, 257, 28, 27, 90,
- 89, 91, 92, nil, nil, 219, nil, nil, nil, nil,
- nil, nil, 41, nil, nil, 94, 93, nil, 84, 50,
- 86, 85, 87, 261, 88, 95, 96, nil, 81, 82,
- nil, 38, 39, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 210, nil, nil, 214, nil, nil,
- 52, nil, nil, 54, nil, 256, nil, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 218, nil, nil,
- nil, nil, 79, 73, 75, 76, 77, 78, nil, nil,
- nil, 74, 80, nil, nil, nil, 63, 64, 65, 56,
- 51, nil, 53, nil, 57, 58, nil, 37, 83, 61,
- nil, 59, 60, 62, 23, 24, 66, 67, nil, nil,
- nil, nil, nil, 22, 28, 27, 90, 89, 91, 92,
- nil, nil, 17, nil, nil, nil, nil, nil, nil, 41,
- nil, nil, 94, 93, nil, 84, 50, 86, 85, 87,
- nil, 88, 95, 96, nil, 81, 82, nil, 38, 39,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 210, nil, nil, 214, nil, nil, 52, nil, nil,
- 54, nil, nil, nil, nil, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 18, nil, nil, nil, nil, 79,
- 73, 75, 76, 77, 78, nil, nil, nil, 74, 80,
- nil, nil, nil, 63, 64, 65, 56, 51, nil, 53,
- nil, 57, 58, nil, 37, 83, 61, nil, 59, 60,
- 62, 23, 24, 66, 67, nil, nil, nil, nil, nil,
- 22, 28, 27, 90, 89, 91, 92, nil, nil, 17,
- nil, nil, nil, nil, nil, nil, 41, nil, nil, 94,
- 93, nil, 84, 50, 86, 85, 87, nil, 88, 95,
- 96, nil, 81, 82, nil, 38, 39, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 210, nil,
- nil, 214, nil, nil, 52, nil, nil, 54, nil, nil,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 18, nil, nil, nil, nil, 79, 73, 75, 76,
- 77, 78, nil, nil, nil, 74, 80, nil, nil, nil,
- 63, 64, 65, 56, 51, nil, 53, nil, 57, 58,
- nil, 37, 83, 61, nil, 59, 60, 62, 258, 259,
- 66, 67, nil, nil, nil, nil, nil, 257, 291, 295,
- 90, 89, 91, 92, nil, nil, 219, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, 94, 93, nil, 84,
- 50, 86, 85, 87, nil, 88, 95, 96, nil, 81,
- 82, nil, 38, 39, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 210, nil, nil, 214, nil,
- nil, 52, nil, nil, 54, nil, nil, nil, nil, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 218, nil,
- nil, nil, nil, 79, 73, 75, 76, 77, 78, nil,
- nil, nil, 74, 80, nil, nil, nil, 63, 64, 65,
- 56, 51, nil, 53, nil, 57, 58, nil, 37, 83,
- 61, nil, 59, 60, 62, 258, 259, 66, 67, nil,
- nil, nil, nil, nil, 257, 291, 295, 90, 89, 91,
- 92, nil, nil, 219, nil, nil, nil, nil, nil, nil,
- 41, nil, nil, 94, 93, nil, 84, 50, 86, 85,
- 87, nil, 88, 95, 96, nil, 81, 82, nil, 38,
- 39, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 210, nil, nil, 214, nil, nil, 52, nil,
- nil, 54, nil, 775, nil, nil, nil, 40, nil, nil,
- nil, nil, nil, nil, nil, 218, nil, nil, nil, nil,
- 79, 73, 75, 76, 77, 78, nil, nil, nil, 74,
- 80, nil, nil, nil, 63, 64, 65, 56, 51, nil,
- 53, nil, 57, 58, nil, 37, 83, 61, nil, 59,
- 60, 62, 23, 24, 66, 67, nil, nil, nil, nil,
- nil, 22, 28, 27, 90, 89, 91, 92, nil, nil,
- 219, nil, nil, nil, nil, nil, nil, 41, nil, nil,
- 94, 93, nil, 84, 50, 86, 85, 87, nil, 88,
- 95, 96, nil, 81, 82, nil, 38, 39, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 210,
- nil, nil, 214, nil, nil, 52, nil, nil, 54, nil,
- nil, nil, nil, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 218, nil, nil, nil, nil, 79, 73, 75,
- 76, 77, 78, nil, nil, nil, 74, 80, nil, nil,
- nil, 63, 64, 65, 56, 51, nil, 53, nil, 57,
- 58, nil, 37, 83, 61, nil, 59, 60, 62, 23,
- 24, 66, 67, nil, nil, nil, nil, nil, 22, 28,
- 27, 90, 89, 91, 92, nil, nil, 219, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 94, 93, nil,
- 84, 50, 86, 85, 87, nil, 88, 95, 96, nil,
- 81, 82, nil, 38, 39, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 210, nil, nil, 214,
- nil, nil, 52, nil, nil, 54, nil, nil, nil, nil,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 218,
- nil, nil, nil, nil, 79, 73, 75, 76, 77, 78,
- nil, nil, nil, 74, 80, nil, nil, nil, 63, 64,
- 65, 56, 51, nil, 53, nil, 57, 58, nil, 37,
- 83, 61, nil, 59, 60, 62, 23, 24, 66, 67,
- nil, nil, nil, nil, nil, 22, 28, 27, 90, 89,
- 91, 92, nil, nil, 219, nil, nil, nil, nil, nil,
- nil, 41, nil, nil, 94, 93, nil, 84, 50, 86,
- 85, 87, nil, 88, 95, 96, nil, 81, 82, nil,
- 38, 39, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 210, nil, nil, 214, nil, nil, 52,
- nil, nil, 54, nil, nil, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 218, nil, nil, nil,
- nil, 79, 73, 75, 76, 77, 78, nil, nil, nil,
- 74, 80, nil, nil, nil, 63, 64, 65, 56, 51,
- nil, 53, nil, 57, 58, nil, 37, 83, 61, nil,
- 59, 60, 62, 258, 259, 66, 67, nil, nil, nil,
- nil, nil, 257, 291, 295, 90, 89, 91, 92, nil,
- nil, 219, nil, nil, nil, nil, nil, nil, 41, nil,
- nil, 94, 93, nil, 84, 50, 86, 85, 87, nil,
- 88, 95, 96, nil, 81, 82, nil, 38, 39, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 210, nil, nil, 214, nil, nil, 52, nil, nil, 54,
- nil, nil, nil, nil, nil, 40, nil, nil, nil, nil,
- nil, nil, nil, 218, nil, nil, nil, nil, 79, 73,
- 75, 76, 77, 78, nil, nil, nil, 74, 80, nil,
- nil, nil, 63, 64, 65, 56, 51, nil, 53, nil,
- 57, 58, nil, 37, 83, 61, nil, 59, 60, 62,
- 258, 259, 66, 67, nil, nil, nil, nil, nil, 257,
- 291, 295, 90, 89, 91, 92, nil, nil, 219, nil,
- nil, nil, nil, nil, nil, 41, nil, nil, 94, 93,
- nil, 84, 50, 86, 85, 87, nil, 88, 95, 96,
- nil, 81, 82, nil, 38, 39, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 210, nil, nil,
- 214, nil, nil, 52, nil, nil, 54, nil, nil, nil,
- nil, nil, 40, nil, nil, nil, nil, nil, nil, nil,
- 218, nil, nil, nil, nil, 79, 73, 75, 76, 77,
- 78, nil, nil, nil, 74, 80, nil, nil, nil, nil,
- nil, nil, 56, nil, nil, 53, nil, nil, nil, nil,
- 37, 83, 63, 64, 65, 8, 51, nil, nil, nil,
- 57, 58, nil, nil, nil, 61, nil, 59, 60, 62,
- 23, 24, 66, 67, nil, nil, nil, nil, nil, 22,
- 28, 27, 90, 89, 91, 92, nil, nil, 17, nil,
- nil, nil, nil, nil, 7, 41, nil, 9, 94, 93,
- nil, 84, 50, 86, 85, 87, nil, 88, 95, 96,
- nil, 81, 82, nil, 38, 39, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 36, nil, nil,
- 30, nil, nil, 52, nil, nil, 54, nil, 32, nil,
- nil, nil, 40, nil, nil, nil, nil, nil, nil, nil,
- 18, nil, nil, nil, nil, 79, 73, 75, 76, 77,
- 78, nil, nil, nil, 74, 80, nil, nil, nil, 63,
- 64, 65, 56, 51, nil, 53, nil, 57, 58, nil,
- 37, 83, 61, nil, 59, 60, 62, 258, 259, 66,
- 67, nil, nil, nil, nil, nil, 257, 291, 295, 90,
- 89, 91, 92, nil, nil, 219, nil, nil, nil, nil,
- nil, nil, 41, nil, nil, 94, 93, nil, 84, 50,
- 86, 85, 87, nil, 88, 95, 96, nil, 81, 82,
- nil, 38, 39, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 210, nil, nil, 214, nil, nil,
- 52, nil, nil, 54, nil, nil, nil, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 218, nil, nil,
- nil, nil, 79, 73, 75, 76, 77, 78, nil, nil,
- nil, 74, 80, nil, nil, nil, nil, nil, nil, 56,
- nil, nil, 53, nil, nil, nil, nil, 37, 83, 63,
- 64, 65, 8, 51, nil, nil, nil, 57, 58, nil,
- nil, nil, 61, nil, 59, 60, 62, 23, 24, 66,
- 67, nil, nil, nil, nil, nil, 22, 28, 27, 90,
- 89, 91, 92, nil, nil, 17, nil, nil, nil, nil,
- nil, 7, 41, nil, 9, 94, 93, nil, 84, 50,
- 86, 85, 87, nil, 88, 95, 96, nil, 81, 82,
- nil, 38, 39, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 36, nil, nil, 30, nil, nil,
- 52, nil, nil, 54, nil, 32, nil, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 18, nil, nil,
- nil, nil, 79, 73, 75, 76, 77, 78, nil, nil,
- nil, 74, 80, nil, nil, nil, 63, 64, 65, 56,
- 51, nil, 53, nil, 57, 58, nil, 37, 83, 61,
- nil, 59, 60, 62, 258, 259, 66, 67, nil, nil,
- nil, nil, nil, 257, 291, 295, 90, 89, 91, 92,
- nil, nil, 219, nil, nil, nil, nil, nil, nil, 41,
- nil, nil, 94, 93, nil, 84, 50, 86, 85, 87,
- 261, 88, 95, 96, nil, 81, 82, nil, 38, 39,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 210, nil, nil, 214, nil, nil, 52, nil, nil,
- 54, nil, 658, nil, 254, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 218, nil, nil, nil, nil, 79,
- 73, 75, 76, 77, 78, nil, nil, nil, 74, 80,
- nil, nil, nil, 63, 64, 65, 56, 51, nil, 53,
- nil, 57, 58, nil, 37, 83, 61, nil, 59, 60,
- 62, 258, 259, 66, 67, nil, nil, nil, nil, nil,
- 257, 291, 295, 90, 89, 91, 92, nil, nil, 219,
- nil, nil, nil, nil, nil, nil, 41, nil, nil, 94,
- 93, nil, 84, 50, 86, 85, 87, 261, 88, 95,
- 96, nil, 81, 82, nil, 38, 39, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 210, nil,
- nil, 214, nil, nil, 52, nil, nil, 54, nil, nil,
- nil, 254, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 218, nil, nil, nil, nil, 79, 73, 75, 76,
- 77, 78, nil, nil, nil, 74, 80, nil, nil, nil,
- nil, nil, nil, 56, nil, nil, 53, nil, nil, nil,
- nil, 37, 83, 63, 64, 65, 8, 51, nil, nil,
- nil, 57, 58, nil, nil, nil, 61, nil, 59, 60,
- 62, 23, 24, 66, 67, nil, nil, nil, nil, nil,
- 22, 28, 27, 90, 89, 91, 92, nil, nil, 17,
- nil, nil, nil, nil, nil, 7, 41, nil, 9, 94,
- 93, nil, 84, 50, 86, 85, 87, nil, 88, 95,
- 96, nil, 81, 82, nil, 38, 39, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 36, nil,
- nil, 30, nil, nil, 52, nil, nil, 54, nil, 32,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 18, nil, nil, nil, nil, 79, 73, 75, 76,
- 77, 78, nil, nil, nil, 74, 80, nil, nil, nil,
- nil, nil, nil, 56, nil, nil, 53, nil, nil, nil,
- nil, 37, 83, 63, 64, 65, 8, 51, nil, nil,
- nil, 57, 58, nil, nil, nil, 61, nil, 59, 60,
- 62, 23, 24, 66, 67, nil, nil, nil, nil, nil,
- 22, 28, 27, 90, 89, 91, 92, nil, nil, 17,
- nil, nil, nil, nil, nil, 7, 41, nil, 9, 94,
- 93, nil, 84, 50, 86, 85, 87, nil, 88, 95,
- 96, nil, 81, 82, nil, 38, 39, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 36, nil,
- nil, 30, nil, nil, 52, nil, nil, 54, nil, 32,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 18, nil, nil, nil, nil, 79, 73, 75, 76,
- 77, 78, nil, nil, nil, 74, 80, nil, nil, nil,
- 63, 64, 65, 56, 51, nil, 53, nil, 57, 58,
- nil, 37, 83, 61, nil, 59, 60, 62, 258, 259,
- 66, 67, nil, nil, nil, nil, nil, 257, 291, 295,
- 90, 89, 91, 92, nil, nil, 219, nil, nil, nil,
- nil, nil, nil, 292, nil, nil, 94, 93, nil, 84,
- 50, 86, 85, 87, nil, 88, 95, 96, nil, 81,
- 82, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 289, nil, nil, 286, nil,
- nil, 52, nil, nil, 54, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 79, 73, 75, 76, 77, 78, nil,
- nil, nil, 74, 80, nil, nil, nil, 63, 64, 65,
- 56, 51, nil, 53, nil, 57, 58, nil, 296, 83,
- 61, nil, 59, 60, 62, 258, 259, 66, 67, nil,
- nil, nil, nil, nil, 257, 291, 295, 90, 89, 91,
- 92, nil, nil, 219, nil, nil, nil, nil, nil, nil,
- 292, nil, nil, 94, 93, nil, 84, 50, 86, 85,
- 87, nil, 88, 95, 96, nil, 81, 82, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 289, nil, nil, 286, nil, nil, 52, nil,
- nil, 54, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 79, 73, 75, 76, 77, 78, nil, nil, nil, 74,
- 80, nil, nil, nil, 63, 64, 65, 56, 51, nil,
- 53, nil, 57, 58, nil, 296, 83, 61, nil, 59,
- 60, 62, 258, 259, 66, 67, nil, nil, nil, nil,
- nil, 257, 291, 295, 90, 89, 91, 92, nil, nil,
- 219, nil, nil, nil, nil, nil, nil, 41, nil, nil,
- 94, 93, nil, 84, 50, 86, 85, 87, nil, 88,
- 95, 96, nil, 81, 82, nil, 38, 39, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 210,
- nil, nil, 214, nil, nil, 52, nil, nil, 54, nil,
- 421, nil, nil, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 218, nil, nil, nil, nil, 79, 73, 75,
- 76, 77, 78, nil, nil, nil, 74, 80, nil, nil,
- nil, 63, 64, 65, 56, 51, nil, 53, nil, 57,
- 58, nil, 37, 83, 61, nil, 59, 60, 62, 258,
- 259, 66, 67, nil, nil, nil, nil, nil, 257, 291,
- 295, 90, 89, 91, 92, nil, nil, 219, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 94, 93, nil,
- 84, 50, 86, 85, 87, nil, 88, 95, 96, nil,
- 81, 82, nil, 38, 39, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 210, nil, nil, 214,
- nil, nil, 52, nil, nil, 54, nil, 256, nil, nil,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 218,
- nil, nil, nil, nil, 79, 73, 75, 76, 77, 78,
- nil, nil, nil, 74, 80, nil, nil, nil, 63, 64,
- 65, 56, 51, nil, 53, nil, 57, 58, nil, 37,
- 83, 61, nil, 59, 60, 62, 23, 24, 66, 67,
- nil, nil, nil, nil, nil, 22, 28, 27, 90, 89,
- 91, 92, nil, nil, 17, nil, nil, nil, nil, nil,
- nil, 41, nil, nil, 94, 93, nil, 84, 50, 86,
- 85, 87, nil, 88, 95, 96, nil, 81, 82, nil,
- 38, 39, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 210, nil, nil, 214, nil, nil, 52,
- nil, nil, 54, nil, nil, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 18, nil, nil, nil,
- nil, 79, 73, 75, 76, 77, 78, nil, nil, nil,
- 74, 80, nil, nil, nil, nil, nil, nil, 56, nil,
- nil, 53, nil, nil, nil, nil, 37, 83, 63, 64,
- 65, 8, 51, nil, nil, nil, 57, 58, nil, nil,
- nil, 61, nil, 59, 60, 62, 23, 24, 66, 67,
- nil, nil, nil, nil, nil, 22, 28, 27, 90, 89,
- 91, 92, nil, nil, 17, nil, nil, nil, nil, nil,
- 7, 41, nil, 9, 94, 93, nil, 84, 50, 86,
- 85, 87, nil, 88, 95, 96, nil, 81, 82, nil,
- 38, 39, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 36, nil, nil, 30, nil, nil, 52,
- nil, nil, 54, nil, 32, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 18, nil, nil, nil,
- nil, 79, 73, 75, 76, 77, 78, nil, nil, nil,
- 74, 80, nil, nil, nil, nil, nil, nil, 56, nil,
- nil, 53, nil, nil, nil, nil, 37, 83, 63, 64,
- 65, 8, 51, nil, nil, nil, 57, 58, nil, nil,
- nil, 61, nil, 59, 60, 62, 23, 24, 66, 67,
- nil, nil, nil, nil, nil, 22, 28, 27, 90, 89,
- 91, 92, nil, nil, 17, nil, nil, nil, nil, nil,
- 7, 41, nil, 9, 94, 93, nil, 84, 50, 86,
- 85, 87, nil, 88, 95, 96, nil, 81, 82, nil,
- 38, 39, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 36, nil, nil, 30, nil, nil, 52,
- nil, nil, 54, nil, 32, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 18, nil, nil, nil,
- nil, 79, 73, 75, 76, 77, 78, nil, nil, nil,
- 74, 80, nil, nil, nil, nil, nil, nil, 56, nil,
- nil, 53, nil, nil, nil, nil, 37, 83, 63, 64,
- 65, 8, 51, nil, nil, nil, 57, 58, nil, nil,
- nil, 61, nil, 59, 60, 62, 23, 24, 66, 67,
- nil, nil, nil, nil, nil, 22, 28, 27, 90, 89,
- 91, 92, nil, nil, 17, nil, nil, nil, nil, nil,
- 7, 41, nil, 9, 94, 93, nil, 84, 50, 86,
- 85, 87, nil, 88, 95, 96, nil, 81, 82, nil,
- 38, 39, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 36, nil, nil, 30, nil, nil, 52,
- nil, nil, 54, nil, 32, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 18, nil, nil, nil,
- nil, 79, 73, 75, 76, 77, 78, nil, nil, nil,
- 74, 80, nil, nil, nil, 63, 64, 65, 56, 51,
- nil, 53, nil, 57, 58, nil, 37, 83, 61, nil,
- 59, 60, 62, 258, 259, 66, 67, nil, nil, nil,
- nil, nil, 257, 291, 295, 90, 89, 91, 92, nil,
- nil, 219, nil, nil, nil, nil, nil, nil, 41, nil,
- nil, 94, 93, nil, 84, 50, 86, 85, 87, nil,
- 88, 95, 96, nil, 81, 82, nil, 38, 39, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 210, nil, nil, 214, nil, nil, 52, nil, nil, 54,
- nil, nil, nil, nil, nil, 40, nil, nil, nil, nil,
- nil, nil, nil, 218, nil, nil, nil, nil, 79, 73,
- 75, 76, 77, 78, nil, nil, nil, 74, 80, nil,
- nil, nil, nil, nil, nil, 56, nil, nil, 53, nil,
- nil, nil, nil, 37, 83, 63, 64, 65, 8, 51,
- nil, nil, nil, 57, 58, nil, nil, nil, 61, nil,
- 59, 60, 62, 23, 24, 66, 67, nil, nil, nil,
- nil, nil, 22, 28, 27, 90, 89, 91, 92, nil,
- nil, 17, nil, nil, nil, nil, nil, 7, 41, nil,
- 9, 94, 93, nil, 84, 50, 86, 85, 87, nil,
- 88, 95, 96, nil, 81, 82, nil, 38, 39, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 36, nil, nil, 30, nil, nil, 52, nil, nil, 54,
- nil, 32, nil, nil, nil, 40, nil, nil, nil, nil,
- nil, nil, nil, 18, nil, nil, nil, nil, 79, 73,
- 75, 76, 77, 78, nil, nil, nil, 74, 80, nil,
- nil, nil, 63, 64, 65, 56, 51, nil, 53, nil,
- 57, 58, nil, 37, 83, 61, nil, 59, 60, 62,
- 258, 259, 66, 67, nil, nil, nil, nil, nil, 257,
- 291, 295, 90, 89, 91, 92, nil, nil, 219, nil,
- nil, nil, nil, nil, nil, 41, nil, nil, 94, 93,
- nil, 84, 50, 86, 85, 87, nil, 88, 95, 96,
- nil, 81, 82, nil, 38, 39, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 210, nil, nil,
- 214, nil, nil, 52, nil, nil, 54, nil, nil, nil,
- nil, nil, 40, nil, nil, nil, nil, nil, nil, nil,
- 218, nil, nil, nil, nil, 79, 73, 75, 76, 77,
- 78, nil, nil, nil, 74, 80, nil, nil, nil, 63,
- 64, 65, 56, 51, nil, 53, nil, 57, 58, nil,
- 37, 83, 61, nil, 59, 60, 62, 23, 24, 66,
- 67, nil, nil, nil, nil, nil, 22, 28, 27, 90,
- 89, 91, 92, nil, nil, 219, nil, nil, nil, nil,
- nil, nil, 41, nil, nil, 94, 93, nil, 84, 50,
- 86, 85, 87, nil, 88, 95, 96, nil, 81, 82,
- nil, 38, 39, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 210, nil, nil, 214, nil, nil,
- 52, nil, nil, 54, nil, nil, nil, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 218, nil, nil,
- nil, nil, 79, 73, 75, 76, 77, 78, nil, nil,
- nil, 74, 80, nil, nil, nil, 63, 64, 65, 56,
- 51, nil, 53, nil, 57, 58, nil, 37, 83, 61,
- nil, 59, 60, 62, 23, 24, 66, 67, nil, nil,
- nil, nil, nil, 22, 28, 27, 90, 89, 91, 92,
- nil, nil, 219, nil, nil, nil, nil, nil, nil, 41,
- nil, nil, 94, 93, nil, 84, 50, 86, 85, 87,
- nil, 88, 95, 96, nil, 81, 82, nil, 38, 39,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 210, nil, nil, 214, nil, nil, 52, nil, nil,
- 54, nil, nil, nil, nil, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 218, nil, nil, nil, nil, 79,
- 73, 75, 76, 77, 78, nil, nil, nil, 74, 80,
- nil, nil, nil, 63, 64, 65, 56, 51, nil, 53,
- nil, 57, 58, nil, 37, 83, 61, nil, 59, 60,
- 62, 258, 259, 66, 67, nil, nil, nil, nil, nil,
- 257, 291, 295, 90, 89, 91, 92, nil, nil, 219,
- nil, nil, nil, nil, nil, nil, 41, nil, nil, 94,
- 93, nil, 84, 50, 86, 85, 87, nil, 88, 95,
- 96, nil, 81, 82, nil, 38, 39, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 210, nil,
- nil, 214, nil, nil, 52, nil, nil, 54, nil, nil,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 218, nil, nil, nil, nil, 79, 73, 75, 76,
- 77, 78, nil, nil, nil, 74, 80, nil, nil, nil,
- 63, 64, 65, 56, 51, nil, 53, nil, 57, 58,
- nil, 37, 83, 61, nil, 59, 60, 62, 258, 259,
- 66, 67, nil, nil, nil, nil, nil, 257, 291, 295,
- 90, 89, 91, 92, nil, nil, 219, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, 94, 93, nil, 84,
- 50, 86, 85, 87, nil, 88, 95, 96, nil, 81,
- 82, nil, 38, 39, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 210, nil, nil, 214, nil,
- nil, 52, nil, nil, 54, nil, nil, nil, nil, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 218, nil,
- nil, nil, nil, 79, 73, 75, 76, 77, 78, nil,
- nil, nil, 74, 80, nil, nil, nil, 63, 64, 65,
- 56, 51, nil, 53, nil, 57, 58, nil, 37, 83,
- 61, nil, 59, 60, 62, 258, 259, 66, 67, nil,
- nil, nil, nil, nil, 257, 291, 295, 90, 89, 91,
- 92, nil, nil, 219, nil, nil, nil, nil, nil, nil,
- 41, nil, nil, 94, 93, nil, 84, 50, 86, 85,
- 87, nil, 88, 95, 96, nil, 81, 82, nil, 38,
- 39, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 210, nil, nil, 214, nil, nil, 52, nil,
- nil, 54, nil, nil, nil, nil, nil, 40, nil, nil,
- nil, nil, nil, nil, nil, 218, nil, nil, nil, nil,
- 79, 73, 75, 76, 77, 78, nil, nil, nil, 74,
- 80, nil, nil, nil, 63, 64, 65, 56, 51, nil,
- 53, nil, 57, 58, nil, 37, 83, 61, nil, 59,
- 60, 62, 258, 259, 66, 67, nil, nil, nil, nil,
- nil, 257, 291, 295, 90, 89, 91, 92, nil, nil,
- 219, nil, nil, nil, nil, nil, nil, 41, nil, nil,
- 94, 93, nil, 84, 50, 86, 85, 87, nil, 88,
- 95, 96, nil, 81, 82, nil, 38, 39, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 210,
- nil, nil, 214, nil, nil, 52, nil, nil, 54, nil,
- 256, nil, nil, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 218, nil, nil, nil, nil, 79, 73, 75,
- 76, 77, 78, nil, nil, nil, 74, 80, nil, nil,
- nil, nil, nil, nil, 56, nil, nil, 53, nil, nil,
- nil, nil, 37, 83, 63, 64, 65, 8, 51, nil,
- nil, nil, 57, 58, nil, nil, nil, 61, nil, 59,
- 60, 62, 23, 24, 66, 67, nil, nil, nil, nil,
- nil, 22, 28, 27, 90, 89, 91, 92, nil, nil,
- 17, nil, nil, nil, nil, nil, 7, 41, nil, 9,
- 94, 93, nil, 84, 50, 86, 85, 87, nil, 88,
- 95, 96, nil, 81, 82, nil, 38, 39, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 36,
- nil, nil, 30, nil, nil, 52, nil, nil, 54, nil,
- 32, nil, nil, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 18, nil, nil, nil, nil, 79, 73, 75,
- 76, 77, 78, nil, nil, nil, 74, 80, nil, nil,
- nil, 63, 64, 65, 56, 51, nil, 53, nil, 57,
- 58, nil, 37, 83, 61, nil, 59, 60, 62, 23,
- 24, 66, 67, nil, nil, nil, nil, nil, 22, 28,
- 27, 90, 89, 91, 92, nil, nil, 17, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 94, 93, nil,
- 84, 50, 86, 85, 87, nil, 88, 95, 96, nil,
- 81, 82, nil, 38, 39, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 210, nil, nil, 214,
- nil, nil, 52, nil, nil, 54, nil, nil, nil, nil,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 18,
- nil, nil, nil, nil, 79, 73, 75, 76, 77, 78,
- nil, nil, nil, 74, 80, nil, nil, nil, nil, nil,
- nil, 56, nil, nil, 53, nil, nil, nil, nil, 37,
- 83, 63, 64, 65, 8, 51, nil, nil, nil, 57,
- 58, nil, nil, nil, 61, nil, 59, 60, 62, 23,
- 24, 66, 67, nil, nil, nil, nil, nil, 22, 28,
- 27, 90, 89, 91, 92, nil, nil, 17, nil, nil,
- nil, nil, nil, 7, 41, nil, 9, 94, 93, nil,
- 84, 50, 86, 85, 87, nil, 88, 95, 96, nil,
- 81, 82, nil, 38, 39, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 36, nil, nil, 30,
- nil, nil, 52, nil, nil, 54, nil, 32, nil, nil,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 18,
- nil, nil, nil, nil, 79, 73, 75, 76, 77, 78,
- nil, nil, nil, 74, 80, nil, nil, nil, nil, nil,
- nil, 56, nil, nil, 53, nil, nil, nil, nil, 37,
- 83, 63, 64, 65, 8, 51, nil, nil, nil, 57,
- 58, nil, nil, nil, 61, nil, 59, 60, 62, 23,
- 24, 66, 67, nil, nil, nil, nil, nil, 22, 28,
- 27, 90, 89, 91, 92, nil, nil, 17, nil, nil,
- nil, nil, nil, 7, 41, nil, 9, 94, 93, nil,
- 84, 50, 86, 85, 87, nil, 88, 95, 96, nil,
- 81, 82, nil, 38, 39, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 36, nil, nil, 30,
- nil, nil, 52, nil, nil, 54, nil, 32, nil, nil,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 18,
- nil, nil, nil, nil, 79, 73, 75, 76, 77, 78,
- nil, nil, nil, 74, 80, nil, nil, nil, 63, 64,
- 65, 56, 51, nil, 53, nil, 57, 58, nil, 37,
- 83, 61, nil, 59, 60, 62, 258, 259, 66, 67,
- nil, nil, nil, nil, nil, 257, 291, 295, 90, 89,
- 91, 92, nil, nil, 219, nil, nil, nil, nil, nil,
- nil, 41, nil, nil, 94, 93, nil, 84, 50, 86,
- 85, 87, nil, 88, 95, 96, nil, 81, 82, nil,
- 38, 39, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 210, nil, nil, 214, nil, nil, 52,
- nil, nil, 54, nil, nil, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 218, nil, nil, nil,
- nil, 79, 73, 75, 76, 77, 78, nil, nil, nil,
- 74, 80, nil, nil, nil, 63, 64, 65, 56, 51,
- nil, 53, nil, 57, 58, nil, 37, 83, 61, nil,
- 59, 60, 62, 258, 259, 66, 67, nil, nil, nil,
- nil, nil, 257, 291, 295, 90, 89, 91, 92, nil,
- nil, 219, nil, nil, nil, nil, nil, nil, 41, nil,
- nil, 94, 93, nil, 84, 50, 86, 85, 87, nil,
- 88, 95, 96, nil, 81, 82, nil, 38, 39, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 210, nil, nil, 214, nil, nil, 52, nil, nil, 54,
- nil, nil, nil, nil, nil, 40, nil, nil, nil, nil,
- nil, nil, nil, 218, nil, nil, nil, nil, 79, 73,
- 75, 76, 77, 78, nil, nil, nil, 74, 80, nil,
- nil, nil, 63, 64, 65, 56, 51, nil, 53, nil,
- 57, 58, nil, 37, 83, 61, nil, 59, 60, 62,
- 258, 259, 66, 67, nil, nil, nil, nil, nil, 257,
- 291, 295, 90, 89, 91, 92, nil, nil, 219, nil,
- nil, nil, nil, nil, nil, 41, nil, nil, 94, 93,
- nil, 84, 50, 86, 85, 87, nil, 88, 95, 96,
- nil, 81, 82, nil, 38, 39, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 210, nil, nil,
- 214, nil, nil, 52, nil, nil, 54, nil, nil, nil,
- nil, nil, 40, nil, nil, nil, nil, nil, nil, nil,
- 218, nil, nil, nil, nil, 79, 73, 75, 76, 77,
- 78, nil, nil, nil, 74, 80, nil, nil, nil, 63,
- 64, 65, 56, 51, nil, 53, nil, 57, 58, nil,
- 37, 83, 61, nil, 59, 60, 62, 258, 259, 66,
- 67, nil, nil, nil, nil, nil, 257, 291, 295, 90,
- 89, 91, 92, nil, nil, 219, nil, nil, nil, nil,
- nil, nil, 41, nil, nil, 94, 93, nil, 84, 50,
- 86, 85, 87, 261, 88, 95, 96, nil, 81, 82,
- nil, 38, 39, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 210, nil, nil, 214, nil, nil,
- 52, nil, nil, 54, nil, nil, nil, 254, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 218, nil, nil,
- nil, nil, 79, 73, 75, 76, 77, 78, nil, nil,
- nil, 74, 80, nil, nil, nil, nil, nil, nil, 56,
- nil, nil, 53, nil, nil, nil, nil, 37, 83, 63,
- 64, 65, 8, 51, nil, nil, nil, 57, 58, nil,
- nil, nil, 61, nil, 59, 60, 62, 23, 24, 66,
- 67, nil, nil, nil, nil, nil, 22, 28, 27, 90,
- 89, 91, 92, nil, nil, 17, nil, nil, nil, nil,
- nil, 7, 41, nil, 9, 94, 93, nil, 84, 50,
- 86, 85, 87, nil, 88, 95, 96, nil, 81, 82,
- nil, 38, 39, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 36, nil, nil, 30, nil, nil,
- 52, nil, nil, 54, nil, 32, nil, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 18, nil, nil,
- nil, nil, 79, 73, 75, 76, 77, 78, nil, nil,
- nil, 74, 80, nil, nil, nil, 63, 64, 65, 56,
- 51, nil, 53, nil, 57, 58, nil, 37, 83, 61,
- nil, 59, 60, 62, 258, 259, 66, 67, nil, nil,
- nil, nil, nil, 257, 291, 295, 90, 89, 91, 92,
- nil, nil, 219, nil, nil, nil, nil, nil, nil, 292,
- nil, nil, 94, 93, nil, 84, 50, 86, 85, 87,
- nil, 88, 95, 96, nil, 81, 82, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 926, nil, nil, 214, nil, nil, 52, nil, nil,
- 54, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 79,
- 73, 75, 76, 77, 78, nil, nil, nil, 74, 80,
- nil, nil, nil, 63, 64, 65, 56, 51, nil, 53,
- nil, 57, 58, nil, 296, 83, 61, nil, 59, 60,
- 62, 258, 259, 66, 67, nil, nil, nil, nil, nil,
- 257, 291, 295, 90, 89, 91, 92, nil, nil, 219,
- nil, nil, nil, nil, nil, nil, 41, nil, nil, 94,
- 93, nil, 84, 50, 86, 85, 87, nil, 88, 95,
- 96, nil, 81, 82, nil, 38, 39, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 210, nil,
- nil, 214, nil, nil, 52, nil, nil, 54, nil, 658,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 218, nil, nil, nil, nil, 79, 73, 75, 76,
- 77, 78, nil, nil, nil, 74, 80, nil, nil, nil,
- nil, nil, nil, 56, nil, nil, 53, nil, nil, nil,
- nil, 37, 83, 63, 64, 65, 8, 51, nil, nil,
- nil, 57, 58, nil, nil, nil, 61, nil, 59, 60,
- 62, 23, 24, 66, 67, nil, nil, nil, nil, nil,
- 22, 28, 27, 90, 89, 91, 92, nil, nil, 17,
- nil, nil, nil, nil, nil, 7, 41, nil, 9, 94,
- 93, nil, 84, 50, 86, 85, 87, nil, 88, 95,
- 96, nil, 81, 82, nil, 38, 39, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 36, nil,
- nil, 30, nil, nil, 52, nil, nil, 54, nil, 32,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 18, nil, nil, nil, nil, 79, 73, 75, 76,
- 77, 78, nil, nil, nil, 74, 80, nil, nil, nil,
- nil, nil, nil, 56, nil, nil, 53, nil, nil, nil,
- nil, 37, 83, 63, 64, 65, 8, 51, nil, nil,
- nil, 57, 58, nil, nil, nil, 61, nil, 59, 60,
- 62, 23, 24, 66, 67, nil, nil, nil, nil, nil,
- 22, 28, 27, 90, 89, 91, 92, nil, nil, 17,
- nil, nil, nil, nil, nil, 7, 41, nil, 9, 94,
- 93, nil, 84, 50, 86, 85, 87, nil, 88, 95,
- 96, nil, 81, 82, nil, 38, 39, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 36, nil,
- nil, 30, nil, nil, 52, nil, nil, 54, nil, 32,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 18, nil, nil, nil, nil, 79, 73, 75, 76,
- 77, 78, nil, nil, nil, 74, 80, nil, nil, nil,
- 63, 64, 65, 56, 51, nil, 53, nil, 57, 58,
- nil, 37, 83, 61, nil, 59, 60, 62, 258, 259,
- 66, 67, nil, nil, nil, nil, nil, 257, 291, 295,
- 90, 89, 91, 92, nil, nil, 219, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, 94, 93, nil, 84,
- 50, 86, 85, 87, nil, 88, 95, 96, nil, 81,
- 82, nil, 38, 39, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 210, nil, nil, 214, nil,
- nil, 52, nil, nil, 54, nil, 658, nil, 254, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 218, nil,
- nil, nil, nil, 79, 73, 75, 76, 77, 78, nil,
- nil, nil, 74, 80, nil, nil, nil, nil, nil, nil,
- 56, nil, nil, 53, nil, nil, nil, nil, 37, 83,
- 63, 64, 65, 8, 51, nil, nil, nil, 57, 58,
- nil, nil, nil, 61, nil, 59, 60, 62, 23, 24,
- 66, 67, nil, nil, nil, nil, nil, 22, 28, 27,
- 90, 89, 91, 92, nil, nil, 17, nil, nil, nil,
- nil, nil, 7, 41, nil, 9, 94, 93, nil, 84,
- 50, 86, 85, 87, nil, 88, 95, 96, nil, 81,
- 82, nil, 38, 39, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 36, nil, nil, 30, nil,
- nil, 52, nil, nil, 54, nil, 32, nil, nil, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 18, nil,
- nil, nil, nil, 79, 73, 75, 76, 77, 78, nil,
- nil, nil, 74, 80, nil, nil, nil, nil, nil, nil,
- 56, nil, nil, 53, nil, nil, nil, nil, 37, 83,
- 63, 64, 65, 8, 51, nil, nil, nil, 57, 58,
- nil, nil, nil, 61, nil, 59, 60, 62, 23, 24,
- 66, 67, nil, nil, nil, nil, nil, 22, 28, 27,
- 90, 89, 91, 92, nil, nil, 17, nil, nil, nil,
- nil, nil, 7, 41, nil, 9, 94, 93, nil, 84,
- 50, 86, 85, 87, nil, 88, 95, 96, nil, 81,
- 82, nil, 38, 39, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 36, nil, nil, 30, nil,
- nil, 52, nil, nil, 54, nil, 32, nil, nil, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 18, nil,
- nil, nil, nil, 79, 73, 75, 76, 77, 78, nil,
- nil, nil, 74, 80, nil, nil, nil, nil, nil, nil,
- 56, nil, nil, 53, nil, nil, nil, nil, 37, 83,
- 63, 64, 65, 8, 51, nil, nil, nil, 57, 58,
- nil, nil, nil, 61, nil, 59, 60, 62, 23, 24,
- 66, 67, nil, nil, nil, nil, nil, 22, 28, 27,
- 90, 89, 91, 92, nil, nil, 17, nil, nil, nil,
- nil, nil, 7, 41, nil, 9, 94, 93, nil, 84,
- 50, 86, 85, 87, nil, 88, 95, 96, nil, 81,
- 82, nil, 38, 39, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 36, nil, nil, 30, nil,
- nil, 52, nil, nil, 54, nil, 32, nil, nil, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 18, nil,
- nil, nil, nil, 79, 73, 75, 76, 77, 78, nil,
- nil, nil, 74, 80, nil, nil, nil, nil, nil, nil,
- 56, nil, nil, 53, nil, nil, nil, nil, 37, 83,
- 63, 64, 65, 8, 51, nil, nil, nil, 57, 58,
- nil, nil, nil, 61, nil, 59, 60, 62, 23, 24,
- 66, 67, nil, nil, nil, nil, nil, 22, 28, 27,
- 90, 89, 91, 92, nil, nil, 17, nil, nil, nil,
- nil, nil, 7, 41, nil, 9, 94, 93, nil, 84,
- 50, 86, 85, 87, nil, 88, 95, 96, nil, 81,
- 82, nil, 38, 39, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 36, nil, nil, 30, nil,
- nil, 52, nil, nil, 54, nil, 32, nil, nil, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 18, nil,
- nil, nil, nil, 79, 73, 75, 76, 77, 78, nil,
- nil, nil, 74, 80, nil, nil, nil, 63, 64, 65,
- 56, 51, nil, 53, nil, 57, 58, nil, 37, 83,
- 61, nil, 59, 60, 62, 258, 259, 66, 67, nil,
- nil, nil, nil, nil, 257, 28, 27, 90, 89, 91,
- 92, nil, nil, 219, nil, nil, nil, nil, nil, nil,
- 41, nil, nil, 94, 93, nil, 84, 50, 86, 85,
- 87, 261, 88, 95, 96, nil, 81, 82, nil, 38,
- 39, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 210, nil, nil, 214, nil, nil, 52, nil,
- nil, 54, nil, 256, nil, nil, nil, 40, nil, nil,
- nil, nil, nil, nil, nil, 218, nil, nil, nil, -538,
- 79, 73, 75, 76, 77, 78, -538, -538, -538, 74,
- 80, nil, -538, -538, nil, -538, nil, 56, nil, nil,
- 53, nil, nil, nil, -538, 37, 83, nil, nil, nil,
- nil, nil, nil, nil, nil, -538, -538, nil, -538, -538,
- -538, -538, -538, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, -538, -538, -538, -538,
- -538, -538, -538, -538, -538, -538, -538, -538, -538, -538,
- -538, -283, nil, -538, -538, -538, nil, 636, -283, -283,
- -283, -538, nil, nil, -283, -283, nil, -283, -538, nil,
- -538, nil, -538, -538, -538, -538, -538, -538, -538, nil,
- -538, -538, -538, nil, nil, nil, nil, -283, -283, nil,
- -283, -283, -283, -283, -283, -538, -538, nil, -87, nil,
- -538, nil, nil, -538, nil, -538, nil, -96, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, -283, -283,
- -283, -283, -283, -283, -283, -283, -283, -283, -283, -283,
- -283, -283, -283, nil, nil, -283, -283, -283, nil, 639,
- nil, nil, nil, -283, nil, nil, nil, nil, nil, nil,
- -283, nil, -283, nil, -283, -283, -283, -283, -283, -283,
- -283, nil, -283, nil, -283, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, -283, -283, nil,
- -89, nil, -283, -538, nil, -283, nil, -283, nil, -98,
- -538, -538, -538, nil, nil, -538, -538, -538, nil, -538,
- nil, nil, nil, nil, nil, nil, nil, nil, -538, -538,
- -538, nil, nil, nil, nil, nil, nil, nil, nil, -538,
- -538, nil, -538, -538, -538, -538, -538, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- -538, -538, -538, -538, -538, -538, -538, -538, -538, -538,
- -538, -538, -538, -538, -538, nil, nil, -538, -538, -538,
- nil, 790, -538, nil, nil, -538, nil, nil, -538, nil,
- -538, nil, -538, nil, -538, nil, -538, -538, -538, -538,
- -538, -538, -538, nil, -538, -538, -538, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, -538,
- -538, -538, -538, nil, -538, -283, nil, -538, nil, -538,
- nil, -96, -283, -283, -283, nil, nil, -283, -283, -283,
- nil, -283, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, -283, -283, nil, nil, nil, nil, nil, nil, nil,
- nil, -283, -283, nil, -283, -283, -283, -283, -283, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, -283, -283, -283, -283, -283, -283, -283, -283,
- -283, -283, -283, -283, -283, -283, -283, nil, nil, -283,
- -283, -283, nil, 639, -283, nil, nil, -283, nil, nil,
- -283, nil, -283, nil, -283, nil, -283, nil, -283, -283,
- -283, -283, -283, -283, -283, nil, -283, nil, -283, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, -283, -283, -283, -283, nil, -283, -292, nil, -283,
- nil, -283, nil, -98, -292, -292, -292, nil, nil, -292,
- -292, -292, nil, -292, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, -292, -292, nil, nil, nil, nil, nil,
- nil, nil, nil, -292, -292, nil, -292, -292, -292, -292,
- -292, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, -292, -292, -292, -292, -292, -292,
- -292, -292, -292, -292, -292, -292, -292, -292, -292, nil,
- nil, -292, -292, -292, nil, nil, -292, nil, 277, -292,
- nil, nil, -292, nil, -292, nil, -292, nil, -292, nil,
- -292, -292, -292, -292, -292, -292, -292, nil, -292, nil,
- -292, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, -292, -292, -292, -292, -278, -292, nil,
- nil, -292, nil, -292, -278, -278, -278, nil, nil, -278,
- -278, -278, nil, -278, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, -278, -278, -278, nil, nil, nil, nil,
- nil, nil, nil, -278, -278, nil, -278, -278, -278, -278,
- -278, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, -278, -278, -278, -278, -278, -278,
- -278, -278, -278, -278, -278, -278, -278, -278, -278, nil,
- nil, -278, -278, -278, nil, nil, -278, nil, nil, -278,
- nil, nil, -278, nil, -278, nil, -278, nil, -278, nil,
- -278, -278, -278, -278, -278, -278, -278, nil, -278, nil,
- -278, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, -278, -278, -278, -278, -554, -278, nil,
- -278, -278, nil, -278, -554, -554, -554, nil, nil, -554,
- -554, -554, nil, -554, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, -554, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, -554, -554, nil, -554, -554, -554, -554,
- -554, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, -554, nil, nil, nil, nil, nil,
- nil, -554, -554, -554, nil, nil, -554, -554, -554, nil,
- -554, nil, nil, nil, nil, -554, -554, nil, nil, nil,
- -554, nil, nil, -554, nil, nil, nil, nil, 268, -554,
- -554, -554, nil, -554, -554, -554, -554, -554, nil, nil,
- nil, nil, 764, nil, 336, 334, 333, 754, 335, nil,
- -554, nil, nil, nil, nil, nil, nil, nil, 751, nil,
- nil, nil, -554, -554, nil, -554, nil, nil, -554, -554,
- -554, -554, -554, -554, -554, -554, -554, nil, -554, nil,
- -554, 338, 749, nil, nil, 268, -554, nil, -554, 341,
- 340, 344, 343, nil, nil, nil, nil, nil, -554, -554,
- nil, -554, -554, -554, -554, -554, nil, -554, nil, nil,
- nil, nil, nil, 436, 440, nil, nil, 438, nil, nil,
- -554, nil, -554, nil, nil, -554, 142, 143, 752, 139,
- 121, 122, 123, 130, 127, 129, nil, nil, 124, 125,
- -554, -554, nil, nil, 144, 145, 131, 132, -554, nil,
- nil, nil, nil, 268, -554, nil, nil, nil, nil, nil,
- 136, 135, nil, 120, 141, 138, 137, 133, 134, 128,
- 126, 118, 140, 119, nil, -554, 146, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, -554, nil,
- -554, nil, nil, -554, 156, 167, 157, 180, 153, 173,
- 163, 162, 188, 191, 178, 161, 160, 155, 181, 189,
- 190, 165, 154, 168, 172, 174, 166, 159, nil, nil,
- nil, 175, 182, 177, 176, 169, 179, 164, 152, 171,
- 170, 183, 184, 185, 186, 187, 151, 158, 149, 150,
- 147, 148, nil, 111, 113, 110, nil, 112, nil, nil,
- nil, nil, nil, nil, nil, nil, 142, 143, nil, 139,
- 121, 122, 123, 130, 127, 129, nil, nil, 124, 125,
- nil, nil, nil, nil, 144, 145, 131, 132, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 136, 135, nil, 120, 141, 138, 137, 133, 134, 128,
- 126, 118, 140, 119, nil, nil, 146, 192, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 80, 156, 167,
- 157, 180, 153, 173, 163, 162, 188, 191, 178, 161,
- 160, 155, 181, 189, 190, 165, 154, 168, 172, 174,
- 166, 159, nil, nil, nil, 175, 182, 177, 176, 169,
- 179, 164, 152, 171, 170, 183, 184, 185, 186, 187,
- 151, 158, 149, 150, 147, 148, nil, 111, 113, nil,
- nil, 112, nil, nil, nil, nil, nil, nil, nil, nil,
- 142, 143, nil, 139, 121, 122, 123, 130, 127, 129,
- nil, nil, 124, 125, nil, nil, nil, nil, 144, 145,
- 131, 132, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 136, 135, nil, 120, 141, 138,
- 137, 133, 134, 128, 126, 118, 140, 119, nil, nil,
- 146, 192, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 80, 156, 167, 157, 180, 153, 173, 163, 162,
- 188, 191, 178, 161, 160, 155, 181, 189, 190, 165,
- 154, 168, 172, 174, 166, 159, nil, nil, nil, 175,
- 182, 177, 176, 169, 179, 164, 152, 171, 170, 183,
- 184, 185, 186, 187, 151, 158, 149, 150, 147, 148,
- nil, 111, 113, nil, nil, 112, nil, nil, nil, nil,
- nil, nil, nil, nil, 142, 143, nil, 139, 121, 122,
- 123, 130, 127, 129, nil, nil, 124, 125, nil, nil,
- nil, nil, 144, 145, 131, 132, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 136, 135,
- nil, 120, 141, 138, 137, 133, 134, 128, 126, 118,
- 140, 119, nil, nil, 146, 192, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 80, 156, 167, 157, 180,
- 153, 173, 163, 162, 188, 191, 178, 161, 160, 155,
- 181, 189, 190, 165, 154, 168, 172, 174, 166, 159,
- nil, nil, nil, 175, 182, 177, 176, 169, 179, 164,
- 152, 171, 170, 183, 184, 185, 186, 187, 151, 158,
- 149, 150, 147, 148, nil, 111, 113, nil, nil, 112,
- nil, nil, nil, nil, nil, nil, nil, nil, 142, 143,
- nil, 139, 121, 122, 123, 130, 127, 129, nil, nil,
- 124, 125, nil, nil, nil, nil, 144, 145, 131, 132,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 136, 135, nil, 120, 141, 138, 137, 133,
- 134, 128, 126, 118, 140, 119, nil, nil, 146, 192,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 80,
- 156, 167, 157, 180, 153, 173, 163, 162, 188, 191,
- 178, 161, 160, 155, 181, 189, 190, 165, 154, 168,
- 172, 174, 166, 159, nil, nil, nil, 175, 182, 177,
- 376, 375, 377, 374, 152, 171, 170, 183, 184, 185,
- 186, 187, 151, 158, 149, 150, 372, 373, nil, 370,
- 113, 86, 85, 371, nil, 88, nil, nil, nil, nil,
- nil, nil, 142, 143, nil, 139, 121, 122, 123, 130,
- 127, 129, nil, nil, 124, 125, nil, nil, nil, nil,
- 144, 145, 131, 132, nil, nil, nil, nil, nil, 381,
- nil, nil, nil, nil, nil, nil, 136, 135, nil, 120,
- 141, 138, 137, 133, 134, 128, 126, 118, 140, 119,
- nil, nil, 146, 156, 167, 157, 180, 153, 173, 163,
- 162, 188, 191, 178, 161, 160, 155, 181, 189, 190,
- 165, 154, 168, 172, 174, 166, 159, nil, nil, nil,
- 175, 182, 177, 176, 169, 179, 164, 152, 171, 170,
- 183, 184, 185, 186, 187, 151, 158, 149, 150, 147,
- 148, nil, 111, 113, 398, 397, 112, nil, 399, nil,
- nil, nil, nil, nil, nil, 142, 143, nil, 139, 121,
- 122, 123, 130, 127, 129, nil, nil, 124, 125, nil,
- nil, nil, nil, 144, 145, 131, 132, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 136,
- 135, nil, 120, 141, 138, 137, 133, 134, 128, 126,
- 118, 140, 119, nil, nil, 146, 156, 167, 157, 180,
- 153, 173, 163, 162, 188, 191, 178, 161, 160, 155,
- 181, 189, 190, 165, 154, 168, 172, 174, 166, 159,
- nil, nil, nil, 175, 182, 177, 176, 169, 179, 164,
- 152, 171, 170, 183, 184, 185, 186, 187, 151, 158,
- 149, 150, 147, 148, nil, 111, 113, 398, 397, 112,
- nil, 399, nil, nil, nil, nil, nil, nil, 142, 143,
- nil, 139, 121, 122, 123, 130, 127, 129, nil, nil,
- 124, 125, nil, nil, nil, nil, 144, 145, 131, 132,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 136, 135, nil, 120, 141, 138, 137, 133,
- 134, 128, 126, 118, 140, 119, nil, nil, 146, 156,
- 167, 157, 180, 153, 173, 163, 162, 188, 191, 178,
- 161, 160, 155, 181, 189, 190, 165, 154, 168, 172,
- 174, 166, 159, nil, nil, nil, 175, 182, 177, 176,
- 169, 179, 164, 152, 171, 170, 183, 184, 185, 186,
- 187, 151, 158, 149, 150, 147, 148, nil, 111, 113,
- nil, nil, 112, nil, nil, nil, nil, nil, nil, nil,
- nil, 142, 143, nil, 139, 121, 122, 123, 130, 127,
- 129, nil, nil, 124, 125, nil, nil, nil, nil, 144,
- 145, 131, 132, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 136, 135, nil, 120, 141,
- 138, 137, 133, 134, 128, 126, 118, 140, 119, 430,
- 434, 146, nil, 431, nil, nil, nil, nil, nil, nil,
- nil, nil, 142, 143, nil, 139, 121, 122, 123, 130,
- 127, 129, nil, nil, 124, 125, nil, nil, nil, nil,
- 144, 145, 131, 132, nil, nil, nil, nil, nil, 268,
- nil, nil, nil, nil, nil, nil, 136, 135, nil, 120,
- 141, 138, 137, 133, 134, 128, 126, 118, 140, 119,
- 443, 434, 146, nil, 444, nil, nil, nil, nil, nil,
- nil, nil, nil, 142, 143, nil, 139, 121, 122, 123,
- 130, 127, 129, nil, nil, 124, 125, nil, nil, nil,
- nil, 144, 145, 131, 132, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 136, 135, nil,
- 120, 141, 138, 137, 133, 134, 128, 126, 118, 140,
- 119, 443, 434, 146, nil, 444, nil, nil, nil, nil,
- nil, nil, nil, nil, 142, 143, nil, 139, 121, 122,
- 123, 130, 127, 129, nil, nil, 124, 125, nil, nil,
- nil, nil, 144, 145, 131, 132, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 136, 135,
- nil, 120, 141, 138, 137, 133, 134, 128, 126, 118,
- 140, 119, 443, 434, 146, nil, 444, nil, nil, nil,
- nil, nil, nil, nil, nil, 142, 143, nil, 139, 121,
- 122, 123, 130, 127, 129, nil, nil, 124, 125, nil,
- nil, nil, nil, 144, 145, 131, 132, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 136,
- 135, nil, 120, 141, 138, 137, 133, 134, 128, 126,
- 118, 140, 119, 443, 434, 146, nil, 444, nil, nil,
- nil, nil, nil, nil, nil, nil, 142, 143, nil, 139,
- 121, 122, 123, 130, 127, 129, nil, nil, 124, 125,
- nil, nil, nil, nil, 144, 145, 131, 132, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 136, 135, nil, 120, 141, 138, 137, 133, 134, 128,
- 126, 118, 140, 119, 642, 434, 146, nil, 643, nil,
- nil, nil, nil, nil, nil, nil, nil, 142, 143, nil,
- 139, 121, 122, 123, 130, 127, 129, nil, nil, 124,
- 125, nil, nil, nil, nil, 144, 145, 131, 132, nil,
- nil, nil, nil, nil, 268, nil, nil, nil, nil, nil,
- nil, 136, 135, nil, 120, 141, 138, 137, 133, 134,
- 128, 126, 118, 140, 119, 644, 440, 146, nil, 645,
- nil, nil, nil, nil, nil, nil, nil, nil, 142, 143,
- nil, 139, 121, 122, 123, 130, 127, 129, nil, nil,
- 124, 125, nil, nil, nil, nil, 144, 145, 131, 132,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 136, 135, nil, 120, 141, 138, 137, 133,
- 134, 128, 126, 118, 140, 119, 686, 434, 146, nil,
- 687, nil, nil, nil, nil, nil, nil, nil, nil, 142,
- 143, nil, 139, 121, 122, 123, 130, 127, 129, nil,
- nil, 124, 125, nil, nil, nil, nil, 144, 145, 131,
- 132, nil, nil, nil, nil, nil, 268, nil, nil, nil,
- nil, nil, nil, 136, 135, nil, 120, 141, 138, 137,
- 133, 134, 128, 126, 118, 140, 119, 689, 440, 146,
- nil, 690, nil, nil, nil, nil, nil, nil, nil, nil,
- 142, 143, nil, 139, 121, 122, 123, 130, 127, 129,
- nil, nil, 124, 125, nil, nil, nil, nil, 144, 145,
- 131, 132, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 136, 135, nil, 120, 141, 138,
- 137, 133, 134, 128, 126, 118, 140, 119, 443, 434,
- 146, nil, 444, nil, nil, nil, nil, nil, nil, nil,
- nil, 142, 143, nil, 139, 121, 122, 123, 130, 127,
- 129, nil, nil, 124, 125, nil, nil, nil, nil, 144,
- 145, 131, 132, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 136, 135, nil, 120, 141,
- 138, 137, 133, 134, 128, 126, 118, 140, 119, 642,
- 434, 146, nil, 643, nil, nil, nil, nil, nil, nil,
- nil, nil, 142, 143, nil, 139, 121, 122, 123, 130,
- 127, 129, nil, nil, 124, 125, nil, nil, nil, nil,
- 144, 145, 131, 132, nil, nil, nil, nil, nil, 268,
- nil, nil, nil, nil, nil, nil, 136, 135, nil, 120,
- 141, 138, 137, 133, 134, 128, 126, 118, 140, 119,
- 644, 440, 146, nil, 645, nil, nil, nil, nil, nil,
- nil, nil, nil, 142, 143, nil, 139, 121, 122, 123,
- 130, 127, 129, nil, nil, 124, 125, nil, nil, nil,
- nil, 144, 145, 131, 132, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 136, 135, nil,
- 120, 141, 138, 137, 133, 134, 128, 126, 118, 140,
- 119, 733, 434, 146, nil, 734, nil, nil, nil, nil,
- nil, nil, nil, nil, 142, 143, nil, 139, 121, 122,
- 123, 130, 127, 129, nil, nil, 124, 125, nil, nil,
- nil, nil, 144, 145, 131, 132, nil, nil, nil, nil,
- nil, 268, nil, nil, nil, nil, nil, nil, 136, 135,
- nil, 120, 141, 138, 137, 133, 134, 128, 126, 118,
- 140, 119, 735, 440, 146, nil, 736, nil, nil, nil,
- nil, nil, nil, nil, nil, 142, 143, nil, 139, 121,
- 122, 123, 130, 127, 129, nil, nil, 124, 125, nil,
- nil, nil, nil, 144, 145, 131, 132, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 136,
- 135, nil, 120, 141, 138, 137, 133, 134, 128, 126,
- 118, 140, 119, 741, 440, 146, nil, 739, nil, nil,
- nil, nil, nil, nil, nil, nil, 142, 143, nil, 139,
- 121, 122, 123, 130, 127, 129, nil, nil, 124, 125,
- nil, nil, nil, nil, 144, 145, 131, 132, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 136, 135, nil, 120, 141, 138, 137, 133, 134, 128,
- 126, 118, 140, 119, 443, 434, 146, nil, 444, nil,
- nil, nil, nil, nil, nil, nil, nil, 142, 143, nil,
- 139, 121, 122, 123, 130, 127, 129, nil, nil, 124,
- 125, nil, nil, nil, nil, 144, 145, 131, 132, nil,
- nil, nil, nil, nil, 268, nil, nil, nil, nil, nil,
- nil, 136, 135, nil, 120, 141, 138, 137, 133, 134,
- 128, 126, 118, 140, 119, 741, 440, 146, nil, 892,
- nil, nil, nil, nil, nil, nil, nil, nil, 142, 143,
- nil, 139, 121, 122, 123, 130, 127, 129, nil, nil,
- 124, 125, nil, nil, nil, nil, 144, 145, 131, 132,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 136, 135, nil, 120, 141, 138, 137, 133,
- 134, 128, 126, 118, 140, 119, 959, 434, 146, nil,
- 960, nil, nil, nil, nil, nil, nil, nil, nil, 142,
- 143, nil, 139, 121, 122, 123, 130, 127, 129, nil,
- nil, 124, 125, nil, nil, nil, nil, 144, 145, 131,
- 132, nil, nil, nil, nil, nil, 268, nil, nil, nil,
- nil, nil, nil, 136, 135, nil, 120, 141, 138, 137,
- 133, 134, 128, 126, 118, 140, 119, 961, 440, 146,
- nil, 962, nil, nil, nil, nil, nil, nil, nil, nil,
- 142, 143, nil, 139, 121, 122, 123, 130, 127, 129,
- nil, nil, 124, 125, nil, nil, nil, nil, 144, 145,
- 131, 132, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 136, 135, nil, 120, 141, 138,
- 137, 133, 134, 128, 126, 118, 140, 119, nil, nil,
- 146 ]
-
-racc_action_check = [
- 0, 0, 0, 0, 0, 347, 321, 354, 0, 0,
- 392, 746, 746, 0, 71, 0, 0, 0, 0, 0,
- 0, 0, 71, 313, 852, 615, 313, 0, 0, 0,
- 0, 0, 0, 0, 1, 852, 0, 387, 26, 209,
- 387, 485, 0, 0, 0, 0, 0, 0, 348, 0,
- 0, 0, 0, 0, 55, 0, 0, 0, 497, 0,
- 0, 357, 0, 0, 587, 681, 366, 370, 852, 852,
- 733, 310, 310, 392, 370, 485, 734, 352, 838, 352,
- 839, 604, 604, 735, 889, 0, 615, 615, 0, 209,
- 896, 0, 497, 6, 0, 615, 0, 287, 387, 387,
- 0, 746, 681, 924, 26, 852, 746, 9, 0, 927,
- 948, 321, 354, 0, 0, 0, 0, 0, 0, 347,
- 959, 347, 0, 0, 347, 26, 736, 17, 17, 17,
- 0, 17, 960, 0, 507, 17, 17, 10, 0, 0,
- 17, 55, 17, 17, 17, 17, 17, 17, 17, 211,
- 735, 457, 457, 11, 17, 17, 17, 17, 17, 17,
- 17, 310, 348, 17, 348, 287, 357, 348, 507, 587,
- 17, 604, 366, 17, 17, 733, 17, 17, 17, 17,
- 17, 734, 17, 17, 17, 839, 17, 17, 735, 17,
- 17, 366, 838, 736, 838, 838, 366, 838, 889, 211,
- 889, 371, 287, 889, 896, 12, 896, 457, 371, 896,
- 961, 287, 17, 20, 962, 17, 17, 924, 17, 924,
- 290, 17, 924, 927, 948, 927, 948, 17, 927, 948,
- 967, 736, 293, 383, 959, 17, 959, 34, 577, 959,
- 17, 17, 17, 17, 17, 17, 960, 525, 960, 17,
- 17, 960, 686, 578, 18, 18, 18, 17, 18, 687,
- 17, 314, 18, 18, 314, 17, 17, 18, 453, 18,
- 18, 18, 18, 18, 18, 18, 698, 698, 508, 15,
- 15, 18, 18, 18, 18, 18, 18, 18, 290, 961,
- 18, 629, 36, 962, 383, 383, 383, 18, 293, 461,
- 18, 18, 41, 18, 18, 18, 18, 18, 77, 18,
- 18, 18, 508, 18, 18, 525, 18, 18, 453, 293,
- 686, 841, 37, 37, 961, 290, 961, 687, 962, 961,
- 962, 461, 698, 962, 290, 461, 461, 689, 577, 18,
- 577, 629, 18, 577, 967, 18, 967, 807, 18, 967,
- 78, 690, 525, 578, 18, 578, 642, 686, 578, 97,
- 3, 525, 18, 193, 687, 3, 686, 18, 18, 18,
- 18, 18, 18, 687, 210, 278, 18, 18, 212, 14,
- 278, 22, 22, 22, 18, 22, 385, 18, 317, 22,
- 22, 317, 18, 18, 22, 643, 22, 22, 22, 22,
- 22, 22, 22, 791, 689, 689, 642, 792, 22, 22,
- 22, 22, 22, 22, 22, 807, 213, 22, 690, 690,
- 14, 841, 389, 841, 22, 394, 841, 22, 22, 14,
- 22, 22, 22, 22, 22, 22, 22, 22, 22, 372,
- 22, 22, 689, 22, 22, 643, 372, 385, 385, 385,
- 351, 689, 807, 791, 219, 351, 690, 792, 251, 25,
- 25, 807, 860, 373, 374, 690, 22, 25, 252, 22,
- 373, 374, 22, 860, 934, 22, 934, 22, 255, 22,
- 267, 22, 280, 389, 389, 389, 394, 394, 394, 22,
- 83, 83, 13, 13, 22, 22, 22, 22, 22, 22,
- 13, 282, 703, 22, 22, 703, 860, 860, 23, 23,
- 23, 22, 23, 597, 22, 375, 23, 23, 597, 22,
- 22, 23, 375, 23, 23, 23, 23, 23, 23, 23,
- 283, 361, 296, 296, 284, 23, 23, 23, 23, 23,
- 23, 23, 744, 860, 23, 42, 42, 744, 13, 648,
- 13, 23, 289, 42, 23, 23, 291, 23, 23, 23,
- 23, 23, 23, 23, 23, 23, 292, 23, 23, 295,
- 23, 23, 301, 648, 648, 648, 648, 648, 648, 648,
- 648, 648, 648, 648, 361, 361, 648, 648, 376, 750,
- 648, 648, 361, 23, 750, 376, 23, 361, 302, 23,
- 305, 42, 23, 42, 23, 648, 23, 648, 23, 648,
- 648, 648, 648, 648, 648, 648, 23, 648, 361, 208,
- 208, 23, 23, 23, 23, 23, 23, 208, 312, 35,
- 23, 23, 315, 648, 316, 24, 24, 24, 23, 24,
- 361, 23, 361, 24, 24, 318, 23, 23, 24, 326,
- 24, 24, 24, 24, 24, 24, 24, 5, 5, 5,
- 5, 5, 24, 24, 24, 24, 24, 24, 24, 377,
- 35, 24, 288, 288, 300, 208, 377, 208, 24, 35,
- 288, 24, 24, 327, 24, 24, 24, 24, 24, 24,
- 24, 24, 24, 329, 24, 24, 330, 24, 24, 342,
- 484, 484, 484, 484, 484, 484, 484, 484, 484, 484,
- 484, 303, 303, 484, 484, 300, 110, 484, 484, 303,
- 24, 110, 110, 24, 300, 345, 24, 353, 288, 24,
- 288, 24, 484, 24, 484, 24, 484, 484, 484, 484,
- 484, 484, 484, 24, 484, 356, 355, 355, 24, 24,
- 24, 24, 24, 24, 355, 358, 430, 24, 24, 362,
- 484, 484, 27, 27, 27, 24, 27, 303, 24, 303,
- 27, 27, 388, 24, 24, 27, 391, 27, 27, 27,
- 27, 27, 27, 27, 279, 279, 279, 279, 279, 27,
- 27, 27, 27, 27, 27, 27, 410, 430, 27, 364,
- 364, 416, 355, 419, 355, 27, 430, 364, 27, 27,
- 422, 27, 27, 27, 27, 27, 27, 27, 27, 27,
- 424, 27, 27, 428, 27, 27, 429, 419, 419, 419,
- 419, 419, 419, 419, 419, 419, 419, 419, 589, 589,
- 419, 419, 346, 346, 419, 419, 589, 27, 437, 431,
- 27, 27, 463, 27, 464, 364, 27, 364, 27, 419,
- 27, 419, 27, 419, 419, 419, 419, 419, 419, 419,
- 27, 419, 561, 561, 379, 27, 27, 27, 27, 27,
- 27, 379, 720, 720, 27, 27, 628, 419, 465, 419,
- 431, 466, 27, 858, 589, 27, 589, 499, 858, 431,
- 27, 27, 30, 30, 30, 30, 30, 855, 855, 858,
- 30, 30, 949, 949, 502, 30, 503, 30, 30, 30,
- 30, 30, 30, 30, 509, 513, 514, 628, 517, 30,
- 30, 30, 30, 30, 30, 30, 628, 519, 30, 526,
- 858, 858, 858, 858, 30, 30, 462, 30, 30, 30,
- 529, 30, 30, 30, 30, 30, 538, 30, 30, 30,
- 539, 30, 30, 540, 30, 30, 553, 19, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 462, 858,
- 19, 19, 462, 462, 19, 19, 564, 30, 568, 573,
- 30, 579, 580, 30, 619, 626, 30, 632, 30, 19,
- 637, 19, 30, 19, 19, 19, 19, 19, 19, 19,
- 30, 19, 640, 711, 711, 30, 30, 30, 30, 30,
- 30, 711, 646, 647, 30, 30, 655, 19, 657, 31,
- 31, 31, 30, 31, 669, 30, 673, 31, 31, 676,
- 30, 30, 31, 683, 31, 31, 31, 31, 31, 31,
- 31, 555, 685, 688, 691, 692, 31, 31, 31, 31,
- 31, 31, 31, 693, 696, 31, 840, 840, 697, 711,
- 699, 711, 31, 704, 840, 31, 31, 705, 31, 31,
- 31, 31, 31, 708, 31, 31, 31, 710, 31, 31,
- 712, 652, 713, 652, 652, 652, 714, 652, 555, 555,
- 555, 555, 717, 768, 768, 739, 739, 768, 768, 768,
- 722, 728, 730, 739, 31, 732, 737, 31, 739, 741,
- 31, 742, 840, 31, 840, 31, 753, 757, 758, 760,
- 652, 761, 939, 762, 764, 767, 774, 939, 652, 652,
- 652, 652, 31, 31, 31, 31, 31, 31, 939, 652,
- 795, 31, 31, 796, 799, 801, 32, 32, 32, 31,
- 32, 739, 31, 739, 32, 32, 804, 31, 31, 32,
- 805, 32, 32, 32, 32, 32, 32, 32, 824, 939,
- 939, 939, 939, 32, 32, 32, 32, 32, 32, 32,
- 925, 925, 32, 680, 680, 680, 680, 680, 925, 32,
- 480, 806, 32, 32, 811, 32, 32, 32, 32, 32,
- 814, 32, 32, 32, 815, 32, 32, 600, 939, 600,
- 600, 600, 600, 600, 830, 824, 824, 824, 824, 833,
- 843, 846, 480, 600, 847, 848, 480, 480, 480, 480,
- 870, 32, 871, 882, 32, 892, 925, 32, 925, 569,
- 32, 569, 569, 569, 893, 569, 600, 600, 898, 899,
- 907, 909, 912, 913, 600, 600, 600, 600, 914, 32,
- 32, 32, 32, 32, 32, 915, 926, 936, 32, 32,
- 944, 953, 955, 32, 956, 958, 32, nil, nil, 32,
- nil, nil, nil, nil, 32, 32, 38, 38, 38, 481,
- 38, nil, nil, 600, 38, 38, nil, nil, nil, 38,
- nil, 38, 38, 38, 38, 38, 38, 38, nil, nil,
- nil, nil, nil, 38, 38, 38, 38, 38, 38, 38,
- nil, 481, 38, nil, nil, 481, 481, 481, 481, 38,
- nil, nil, 38, 38, nil, 38, 38, 38, 38, 38,
- nil, 38, 38, 38, nil, 38, 38, nil, 38, 38,
- nil, 249, 249, 249, 249, 249, 249, 249, 249, 249,
- 249, 249, nil, nil, 249, 249, nil, nil, 249, 249,
- nil, 38, nil, nil, 38, nil, nil, 38, nil, nil,
- 38, nil, nil, 249, nil, 249, 38, 249, 249, 249,
- 249, 249, 249, 249, 38, 249, nil, nil, nil, 38,
- 38, 38, 38, 38, 38, nil, nil, nil, 38, 38,
- nil, 249, nil, 39, 39, 39, 38, 39, nil, 38,
- nil, 39, 39, nil, 38, 38, 39, nil, 39, 39,
- 39, 39, 39, 39, 39, nil, nil, nil, nil, nil,
- 39, 39, 39, 39, 39, 39, 39, nil, 829, 39,
- 829, 829, 829, nil, 829, nil, 39, nil, nil, 39,
- 39, nil, 39, 39, 39, 39, 39, nil, 39, 39,
- 39, nil, 39, 39, nil, 39, 39, nil, 427, 427,
- 427, 427, 427, 427, 427, 427, 427, 427, 427, nil,
- nil, 427, 427, nil, nil, 427, 427, nil, 39, nil,
- nil, 39, nil, nil, 39, nil, nil, 39, nil, nil,
- 427, nil, 427, 39, 427, 427, 427, 427, 427, 427,
- 427, 39, 427, nil, nil, nil, 39, 39, 39, 39,
- 39, 39, nil, nil, nil, 39, 39, nil, 427, nil,
- 40, 40, 40, 39, 40, nil, 39, nil, 40, 40,
- nil, 39, 39, 40, nil, 40, 40, 40, 40, 40,
- 40, 40, nil, nil, nil, nil, nil, 40, 40, 40,
- 40, 40, 40, 40, nil, nil, 40, nil, nil, nil,
- nil, nil, nil, 40, nil, nil, 40, 40, nil, 40,
- 40, 40, 40, 40, nil, 40, 40, 40, nil, 40,
- 40, nil, 40, 40, nil, 446, 446, 446, 446, 446,
- 446, 446, 446, 446, 446, 446, nil, nil, 446, 446,
- nil, nil, 446, 446, nil, 40, nil, nil, 40, nil,
- nil, 40, nil, nil, 40, nil, nil, 446, nil, 446,
- 40, 446, 446, 446, 446, 446, 446, 446, 40, 446,
- nil, nil, nil, 40, 40, 40, 40, 40, 40, nil,
- nil, nil, 40, 40, nil, 446, nil, 52, 52, 52,
- 40, 52, nil, 40, nil, 52, 52, nil, 40, 40,
- 52, nil, 52, 52, 52, 52, 52, 52, 52, nil,
- nil, nil, nil, nil, 52, 52, 52, 52, 52, 52,
- 52, nil, nil, 52, nil, nil, nil, nil, nil, nil,
- 52, nil, nil, 52, 52, nil, 52, 52, 52, 52,
- 52, nil, 52, 52, 52, nil, 52, 52, nil, 52,
- 52, nil, 536, 536, 536, 536, 536, 536, 536, 536,
- 536, 536, 536, nil, nil, 536, 536, nil, nil, 536,
- 536, nil, 52, nil, nil, 52, nil, nil, 52, nil,
- nil, 52, nil, nil, 536, nil, 536, 52, 536, 536,
- 536, 536, 536, 536, 536, 52, 536, nil, nil, nil,
- 52, 52, 52, 52, 52, 52, nil, nil, nil, 52,
- 52, nil, 536, nil, 53, 53, 53, 52, 53, nil,
- 52, nil, 53, 53, nil, 52, 52, 53, nil, 53,
- 53, 53, 53, 53, 53, 53, nil, nil, nil, nil,
- nil, 53, 53, 53, 53, 53, 53, 53, nil, nil,
- 53, nil, nil, nil, nil, nil, nil, 53, nil, nil,
- 53, 53, nil, 53, 53, 53, 53, 53, 53, 53,
- 53, 53, nil, 53, 53, nil, 53, 53, nil, 695,
- 695, 695, 695, 695, 695, 695, 695, 695, 695, 695,
- nil, nil, 695, 695, nil, nil, 695, 695, nil, 53,
- nil, nil, 53, nil, nil, 53, nil, nil, 53, nil,
- 53, 695, nil, 695, 53, 695, 695, 695, 695, 695,
- 695, 695, 53, 695, nil, nil, nil, 53, 53, 53,
- 53, 53, 53, nil, nil, nil, 53, 53, nil, 695,
- nil, 54, 54, 54, 53, 54, nil, 53, nil, 54,
- 54, nil, 53, 53, 54, nil, 54, 54, 54, 54,
- 54, 54, 54, nil, nil, nil, nil, nil, 54, 54,
- 54, 54, 54, 54, 54, nil, nil, 54, nil, nil,
- nil, nil, nil, nil, 54, nil, nil, 54, 54, nil,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, nil,
- 54, 54, nil, 54, 54, nil, 773, 773, 773, 773,
- 773, 773, 773, 773, 773, 773, 773, nil, nil, 773,
- 773, nil, nil, 773, 773, nil, 54, nil, nil, 54,
- nil, nil, 54, nil, nil, 54, nil, nil, 773, nil,
- 773, 54, 773, 773, 773, 773, 773, 773, 773, 54,
- 773, nil, nil, nil, 54, 54, 54, 54, 54, 54,
- nil, nil, nil, 54, 54, nil, 773, nil, 57, 57,
- 57, 54, 57, nil, 54, nil, 57, 57, nil, 54,
- 54, 57, nil, 57, 57, 57, 57, 57, 57, 57,
- nil, nil, nil, nil, nil, 57, 57, 57, 57, 57,
- 57, 57, nil, nil, 57, nil, nil, nil, nil, nil,
- nil, 57, nil, nil, 57, 57, nil, 57, 57, 57,
- 57, 57, nil, 57, 57, 57, nil, 57, 57, nil,
- 57, 57, nil, 779, 779, 779, 779, 779, 779, 779,
- 779, 779, 779, 779, nil, nil, 779, 779, nil, nil,
- 779, 779, nil, 57, nil, nil, 57, nil, nil, 57,
- nil, nil, 57, nil, nil, 779, nil, 779, 57, 779,
- 779, 779, 779, 779, 779, 779, 57, 779, nil, nil,
- nil, 57, 57, 57, 57, 57, 57, nil, nil, nil,
- 57, 57, nil, 779, nil, 58, 58, 58, 57, 58,
- nil, 57, nil, 58, 58, nil, 57, 57, 58, nil,
- 58, 58, 58, 58, 58, 58, 58, nil, nil, nil,
- nil, nil, 58, 58, 58, 58, 58, 58, 58, nil,
- nil, 58, nil, nil, nil, nil, nil, nil, 58, nil,
- nil, 58, 58, nil, 58, 58, 58, 58, 58, nil,
- 58, 58, 58, nil, 58, 58, nil, 58, 58, nil,
- 781, 781, 781, 781, 781, 781, 781, 781, 781, 781,
- 781, nil, nil, 781, 781, nil, nil, 781, 781, nil,
- 58, nil, nil, 58, nil, nil, 58, nil, nil, 58,
- nil, nil, 781, nil, 781, 58, 781, 781, 781, 781,
- 781, 781, 781, 58, 781, nil, nil, nil, 58, 58,
- 58, 58, 58, 58, nil, nil, nil, 58, 58, nil,
- 781, nil, 61, 61, 61, 58, 61, nil, 58, nil,
- 61, 61, nil, 58, 58, 61, nil, 61, 61, 61,
- 61, 61, 61, 61, nil, nil, nil, nil, nil, 61,
- 61, 61, 61, 61, 61, 61, nil, nil, 61, nil,
- nil, nil, nil, nil, nil, 61, nil, nil, 61, 61,
- nil, 61, 61, 61, 61, 61, nil, 61, 61, 61,
- nil, 61, 61, nil, 61, 61, nil, 672, 672, 672,
- 672, 672, 672, 672, 672, 672, 672, 672, nil, nil,
- 672, 672, nil, nil, 672, 672, nil, 61, nil, nil,
- 61, nil, nil, 61, nil, nil, 61, nil, nil, 672,
- nil, 672, 61, 672, 672, 672, 672, 672, 672, 672,
- 61, 672, nil, nil, nil, 61, 61, 61, 61, 61,
- 61, nil, nil, nil, 61, 61, 61, 672, nil, 672,
- nil, 61, 61, nil, nil, 61, nil, nil, nil, nil,
- 61, 61, 62, 62, 62, nil, 62, nil, nil, nil,
- 62, 62, nil, nil, nil, 62, nil, 62, 62, 62,
- 62, 62, 62, 62, nil, nil, nil, nil, nil, 62,
- 62, 62, 62, 62, 62, 62, nil, nil, 62, nil,
- nil, nil, nil, nil, nil, 62, nil, nil, 62, 62,
- nil, 62, 62, 62, 62, 62, nil, 62, 62, 62,
- nil, 62, 62, 784, 784, 784, 784, 784, 784, 784,
- 784, 784, 784, 784, 467, nil, 784, 784, nil, nil,
- 784, 784, nil, nil, nil, nil, nil, 62, nil, nil,
- 62, 467, 467, 62, nil, 784, 62, 784, 62, 784,
- 784, 784, 784, 784, 784, 784, 467, 784, 467, nil,
- 467, 467, 467, 467, nil, 62, 62, 62, 62, 62,
- 62, nil, nil, 784, 62, 62, nil, nil, nil, 63,
- 63, 63, 62, 63, nil, 62, nil, 63, 63, nil,
- 62, 62, 63, nil, 63, 63, 63, 63, 63, 63,
- 63, nil, nil, nil, nil, nil, 63, 63, 63, 63,
- 63, 63, 63, nil, nil, 63, nil, nil, nil, nil,
- nil, nil, 63, nil, nil, 63, 63, nil, 63, 63,
- 63, 63, 63, nil, 63, 63, 63, nil, 63, 63,
- 786, 786, 786, 786, 786, 786, 786, 786, 786, 786,
- 786, 468, nil, 786, 786, nil, nil, 786, 786, nil,
- nil, 63, nil, nil, 63, nil, nil, 63, 468, 468,
- 63, nil, 786, 63, 786, nil, 786, 786, 786, 786,
- 786, 786, 786, 468, 786, 468, nil, 468, 468, 468,
- 468, nil, 63, 63, 63, 63, 63, 63, nil, nil,
- 786, 63, 63, nil, nil, nil, 84, 84, 84, 63,
- 84, nil, 63, nil, 84, 84, nil, 63, 63, 84,
- nil, 84, 84, 84, 84, 84, 84, 84, nil, 84,
- nil, nil, nil, 84, 84, 84, 84, 84, 84, 84,
- nil, nil, 84, nil, nil, nil, nil, nil, nil, 84,
- nil, nil, 84, 84, nil, 84, 84, 84, 84, 84,
- 84, 84, 84, 84, nil, 84, 84, nil, 84, 84,
- nil, 793, 793, 793, 793, 793, 793, 793, 793, 793,
- 793, 793, nil, nil, 793, 793, nil, nil, 793, 793,
- nil, 84, nil, nil, 84, 84, nil, 84, nil, nil,
- 84, nil, 84, 793, 84, 793, 84, 793, 793, 793,
- 793, 793, 793, 793, 84, 793, 84, nil, nil, 84,
- 84, 84, 84, 84, 84, nil, nil, nil, 84, 84,
- nil, 793, nil, 87, 87, 87, 84, 87, nil, 84,
- nil, 87, 87, nil, 84, 84, 87, nil, 87, 87,
- 87, 87, 87, 87, 87, nil, 87, nil, nil, nil,
- 87, 87, 87, 87, 87, 87, 87, nil, nil, 87,
- nil, nil, nil, nil, nil, nil, 87, nil, nil, 87,
- 87, nil, 87, 87, 87, 87, 87, 87, 87, 87,
- 87, nil, 87, 87, nil, 87, 87, nil, 873, 873,
- 873, 873, 873, 873, 873, 873, 873, 873, 873, nil,
- nil, 873, 873, nil, nil, 873, 873, nil, 87, nil,
- nil, 87, 87, nil, 87, nil, nil, 87, nil, 87,
- 873, 87, 873, 87, 873, 873, 873, 873, 873, 873,
- 873, 87, 873, 87, nil, nil, 87, 87, 87, 87,
- 87, 87, nil, nil, nil, 87, 87, nil, 873, nil,
- nil, nil, nil, 87, nil, nil, 87, nil, nil, nil,
- nil, 87, 87, 99, 99, 99, 99, 99, nil, nil,
- nil, 99, 99, nil, nil, nil, 99, nil, 99, 99,
- 99, 99, 99, 99, 99, nil, nil, nil, nil, nil,
- 99, 99, 99, 99, 99, 99, 99, nil, nil, 99,
- nil, nil, nil, nil, nil, 99, 99, 99, 99, 99,
- 99, nil, 99, 99, 99, 99, 99, nil, 99, 99,
- 99, nil, 99, 99, nil, 99, 99, nil, 875, 875,
- 875, 875, 875, 875, 875, 875, 875, 875, 875, nil,
- nil, 875, 875, nil, nil, 875, 875, nil, 99, nil,
- nil, 99, nil, nil, 99, nil, nil, 99, nil, 99,
- 875, nil, 875, 99, 875, 875, 875, 875, 875, 875,
- 875, 99, 875, nil, nil, nil, 99, 99, 99, 99,
- 99, 99, nil, nil, nil, 99, 99, nil, 875, nil,
- nil, nil, 99, 99, nil, nil, 99, nil, nil, nil,
- nil, 99, 99, 103, 103, 103, nil, 103, nil, nil,
- nil, 103, 103, nil, nil, nil, 103, nil, 103, 103,
- 103, 103, 103, 103, 103, nil, nil, nil, nil, nil,
- 103, 103, 103, 103, 103, 103, 103, nil, 338, 103,
- 338, 338, 338, nil, 338, nil, 103, nil, nil, 103,
- 103, nil, 103, 103, 103, 103, 103, nil, 103, 103,
- 103, nil, 103, 103, nil, 103, 103, 566, nil, 566,
- 566, 566, nil, 566, nil, nil, 718, 338, 718, 718,
- 718, nil, 718, nil, nil, 338, nil, nil, 103, nil,
- nil, 103, nil, nil, 103, nil, nil, 103, nil, nil,
- nil, nil, nil, 103, nil, nil, 566, nil, nil, nil,
- nil, 103, nil, nil, 566, 718, 103, 103, 103, 103,
- 103, 103, nil, 718, nil, 103, 103, nil, nil, nil,
- 104, 104, 104, 103, 104, nil, 103, nil, 104, 104,
- nil, 103, 103, 104, nil, 104, 104, 104, 104, 104,
- 104, 104, nil, nil, nil, nil, nil, 104, 104, 104,
- 104, 104, 104, 104, nil, nil, 104, nil, nil, nil,
- nil, nil, nil, 104, nil, nil, 104, 104, nil, 104,
- 104, 104, 104, 104, nil, 104, 104, 104, nil, 104,
- 104, nil, 104, 104, nil, 459, 459, 459, 459, 459,
- 459, 459, 459, 459, 459, 459, nil, nil, 459, 459,
- nil, nil, 459, 459, nil, 104, nil, nil, 104, nil,
- nil, 104, nil, nil, 104, nil, nil, 459, nil, 459,
- 104, 459, 459, 459, 459, 459, 459, 459, 104, 459,
- nil, nil, nil, 104, 104, 104, 104, 104, 104, nil,
- nil, nil, 104, 104, nil, nil, nil, 105, 105, 105,
- 104, 105, nil, 104, nil, 105, 105, nil, 104, 104,
- 105, nil, 105, 105, 105, 105, 105, 105, 105, nil,
- nil, nil, nil, nil, 105, 105, 105, 105, 105, 105,
- 105, nil, nil, 105, nil, nil, nil, nil, nil, nil,
- 105, nil, nil, 105, 105, nil, 105, 105, 105, 105,
- 105, nil, 105, 105, 105, nil, 105, 105, nil, 105,
- 105, nil, 460, 460, 460, 460, 460, 460, 460, 460,
- 460, 460, 460, nil, nil, 460, 460, nil, nil, 460,
- 460, nil, 105, nil, nil, 105, nil, nil, 105, nil,
- nil, 105, nil, nil, 460, nil, 460, 105, 460, 460,
- 460, 460, 460, 460, 460, 105, 460, nil, nil, nil,
- 105, 105, 105, 105, 105, 105, nil, nil, nil, 105,
- 105, nil, nil, nil, 106, 106, 106, 105, 106, nil,
- 105, nil, 106, 106, nil, 105, 105, 106, nil, 106,
- 106, 106, 106, 106, 106, 106, nil, nil, nil, nil,
- nil, 106, 106, 106, 106, 106, 106, 106, nil, nil,
- 106, nil, nil, nil, nil, nil, nil, 106, nil, nil,
- 106, 106, nil, 106, 106, 106, 106, 106, nil, 106,
- 106, 106, nil, 106, 106, nil, 106, 106, nil, 470,
- 470, 470, 470, 470, 470, 470, nil, nil, 470, 470,
- nil, nil, nil, nil, nil, nil, 470, 470, nil, 106,
- nil, nil, 106, nil, nil, 106, nil, nil, 106, nil,
- nil, 470, nil, 470, 106, 470, 470, 470, 470, 470,
- 470, 470, 106, 470, nil, nil, nil, 106, 106, 106,
- 106, 106, 106, nil, nil, nil, 106, 106, nil, nil,
- nil, nil, nil, nil, 106, nil, nil, 106, nil, nil,
- nil, nil, 106, 106, 107, 107, 107, 107, 107, nil,
- nil, nil, 107, 107, nil, nil, nil, 107, nil, 107,
- 107, 107, 107, 107, 107, 107, nil, nil, nil, nil,
- nil, 107, 107, 107, 107, 107, 107, 107, nil, nil,
- 107, nil, nil, nil, nil, nil, 107, 107, nil, 107,
- 107, 107, nil, 107, 107, 107, 107, 107, nil, 107,
- 107, 107, nil, 107, 107, nil, 107, 107, nil, 471,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 471, 471, nil, 107,
- nil, nil, 107, nil, nil, 107, nil, nil, 107, nil,
- 107, 471, nil, 471, 107, 471, 471, 471, 471, nil,
- nil, 471, 107, 471, nil, nil, nil, 107, 107, 107,
- 107, 107, 107, nil, nil, nil, 107, 107, nil, nil,
- nil, nil, nil, nil, 107, nil, nil, 107, nil, nil,
- nil, nil, 107, 107, 108, 108, 108, 108, 108, nil,
- nil, nil, 108, 108, nil, nil, nil, 108, nil, 108,
- 108, 108, 108, 108, 108, 108, nil, nil, nil, nil,
- nil, 108, 108, 108, 108, 108, 108, 108, nil, nil,
- 108, nil, nil, nil, nil, nil, 108, 108, 108, 108,
- 108, 108, nil, 108, 108, 108, 108, 108, nil, 108,
- 108, 108, nil, 108, 108, nil, 108, 108, nil, 472,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 472, 472, nil, 108,
- nil, nil, 108, nil, nil, 108, nil, nil, 108, nil,
- 108, 472, nil, 472, 108, 472, 472, 472, 472, nil,
- nil, 472, 108, 472, nil, nil, nil, 108, 108, 108,
- 108, 108, 108, nil, nil, nil, 108, 108, nil, nil,
- nil, nil, nil, nil, 108, nil, nil, 108, nil, nil,
- nil, nil, 108, 108, 195, 195, 195, 195, 195, nil,
- nil, nil, 195, 195, nil, nil, nil, 195, nil, 195,
- 195, 195, 195, 195, 195, 195, nil, nil, nil, nil,
- nil, 195, 195, 195, 195, 195, 195, 195, nil, nil,
- 195, nil, nil, nil, nil, nil, 195, 195, nil, 195,
- 195, 195, nil, 195, 195, 195, 195, 195, nil, 195,
- 195, 195, nil, 195, 195, nil, 195, 195, nil, 473,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 473, 473, nil, 195,
- nil, nil, 195, nil, nil, 195, nil, nil, 195, nil,
- 195, 473, nil, 473, 195, 473, 473, 473, 473, nil,
- nil, 473, 195, 473, nil, nil, nil, 195, 195, 195,
- 195, 195, 195, nil, nil, nil, 195, 195, nil, nil,
- nil, 196, 196, 196, 195, 196, nil, 195, nil, 196,
- 196, nil, 195, 195, 196, nil, 196, 196, 196, 196,
- 196, 196, 196, nil, nil, nil, nil, nil, 196, 196,
- 196, 196, 196, 196, 196, nil, nil, 196, nil, nil,
- nil, nil, nil, nil, 196, nil, nil, 196, 196, nil,
- 196, 196, 196, 196, 196, nil, 196, 196, 196, nil,
- 196, 196, nil, 196, 196, nil, 474, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 474, 474, nil, 196, nil, nil, 196,
- nil, nil, 196, nil, nil, 196, nil, 196, 474, nil,
- 474, 196, 474, 474, 474, 474, nil, nil, 474, 196,
- 474, nil, nil, nil, 196, 196, 196, 196, 196, 196,
- nil, nil, nil, 196, 196, nil, nil, nil, 197, 197,
- 197, 196, 197, nil, 196, nil, 197, 197, nil, 196,
- 196, 197, nil, 197, 197, 197, 197, 197, 197, 197,
- nil, nil, nil, nil, nil, 197, 197, 197, 197, 197,
- 197, 197, nil, nil, 197, nil, nil, nil, nil, nil,
- nil, 197, nil, nil, 197, 197, nil, 197, 197, 197,
- 197, 197, nil, 197, 197, 197, nil, 197, 197, nil,
- 197, 197, nil, 475, 475, 475, 475, 475, 475, 475,
- nil, nil, 475, 475, nil, nil, nil, nil, nil, nil,
- 475, 475, nil, 197, nil, nil, 197, nil, nil, 197,
- nil, nil, 197, nil, 197, 475, nil, 475, 197, 475,
- 475, 475, 475, 475, 475, 475, 197, 475, nil, nil,
- nil, 197, 197, 197, 197, 197, 197, nil, nil, nil,
- 197, 197, nil, nil, nil, 198, 198, 198, 197, 198,
- nil, 197, nil, 198, 198, nil, 197, 197, 198, nil,
- 198, 198, 198, 198, 198, 198, 198, nil, nil, nil,
- nil, nil, 198, 198, 198, 198, 198, 198, 198, nil,
- nil, 198, nil, nil, nil, nil, nil, nil, 198, nil,
- nil, 198, 198, nil, 198, 198, 198, 198, 198, nil,
- 198, 198, 198, nil, 198, 198, nil, 198, 198, nil,
- 476, 476, 476, 476, 476, 476, 476, nil, nil, 476,
- 476, nil, nil, nil, nil, nil, nil, 476, 476, nil,
- 198, nil, nil, 198, nil, nil, 198, nil, nil, 198,
- nil, nil, 476, nil, 476, 198, 476, 476, 476, 476,
- 476, 476, 476, 198, 476, nil, nil, nil, 198, 198,
- 198, 198, 198, 198, nil, nil, nil, 198, 198, nil,
- nil, nil, 199, 199, 199, 198, 199, nil, 198, nil,
- 199, 199, nil, 198, 198, 199, nil, 199, 199, 199,
- 199, 199, 199, 199, nil, nil, nil, nil, nil, 199,
- 199, 199, 199, 199, 199, 199, nil, nil, 199, nil,
- nil, nil, nil, nil, nil, 199, nil, nil, 199, 199,
- nil, 199, 199, 199, 199, 199, 199, 199, 199, 199,
- nil, 199, 199, nil, 199, 199, nil, 477, 477, 477,
- 477, 477, 477, 477, nil, nil, 477, 477, nil, nil,
- nil, nil, nil, nil, 477, 477, nil, 199, nil, nil,
- 199, nil, nil, 199, nil, nil, 199, nil, 199, 477,
- nil, 477, 199, 477, 477, 477, 477, 477, 477, 477,
- 199, 477, nil, nil, nil, 199, 199, 199, 199, 199,
- 199, nil, nil, nil, 199, 199, nil, nil, nil, 200,
- 200, 200, 199, 200, nil, 199, nil, 200, 200, nil,
- 199, 199, 200, nil, 200, 200, 200, 200, 200, 200,
- 200, nil, nil, nil, nil, nil, 200, 200, 200, 200,
- 200, 200, 200, nil, nil, 200, nil, nil, nil, nil,
- nil, nil, 200, nil, nil, 200, 200, nil, 200, 200,
- 200, 200, 200, 200, 200, 200, 200, nil, 200, 200,
- nil, 200, 200, nil, 478, 478, 478, 478, 478, 478,
- 478, nil, nil, 478, 478, nil, nil, nil, nil, nil,
- nil, 478, 478, nil, 200, nil, nil, 200, nil, nil,
- 200, nil, nil, 200, nil, 200, 478, nil, 478, 200,
- 478, 478, 478, 478, 478, 478, 478, 200, 478, nil,
- nil, nil, 200, 200, 200, 200, 200, 200, nil, nil,
- nil, 200, 200, nil, nil, nil, 204, 204, 204, 200,
- 204, nil, 200, nil, 204, 204, nil, 200, 200, 204,
- nil, 204, 204, 204, 204, 204, 204, 204, nil, nil,
- nil, nil, nil, 204, 204, 204, 204, 204, 204, 204,
- nil, nil, 204, nil, nil, nil, nil, nil, nil, 204,
- nil, nil, 204, 204, nil, 204, 204, 204, 204, 204,
- nil, 204, 204, 204, nil, 204, 204, nil, 204, 204,
- nil, 479, 479, 479, 479, 479, 479, 479, nil, nil,
- 479, 479, nil, nil, nil, nil, nil, nil, 479, 479,
- nil, 204, nil, nil, 204, nil, nil, 204, nil, nil,
- 204, nil, nil, 479, nil, 479, 204, 479, 479, 479,
- 479, 479, 479, 479, 204, 479, nil, nil, nil, 204,
- 204, 204, 204, 204, 204, nil, nil, nil, 204, 204,
- nil, nil, nil, 205, 205, 205, 204, 205, nil, 204,
- nil, 205, 205, nil, 204, 204, 205, nil, 205, 205,
- 205, 205, 205, 205, 205, nil, nil, nil, nil, nil,
- 205, 205, 205, 205, 205, 205, 205, nil, nil, 205,
- nil, nil, nil, nil, nil, nil, 205, nil, nil, 205,
- 205, nil, 205, 205, 205, 205, 205, nil, 205, 205,
- 205, nil, 205, 205, nil, 205, 205, nil, 482, 482,
- 482, 482, 482, 482, 482, nil, nil, 482, 482, nil,
- nil, nil, nil, nil, nil, 482, 482, nil, 205, nil,
- nil, 205, nil, nil, 205, nil, nil, 205, nil, nil,
- 482, nil, 482, 205, 482, 482, 482, 482, 482, 482,
- 482, 205, 482, nil, nil, nil, 205, 205, 205, 205,
- 205, 205, nil, nil, nil, 205, 205, nil, nil, nil,
- 206, 206, 206, 205, 206, nil, 205, nil, 206, 206,
- nil, 205, 205, 206, nil, 206, 206, 206, 206, 206,
- 206, 206, nil, nil, nil, nil, nil, 206, 206, 206,
- 206, 206, 206, 206, nil, nil, 206, nil, nil, nil,
- nil, nil, nil, 206, nil, nil, 206, 206, nil, 206,
- 206, 206, 206, 206, nil, 206, 206, 206, nil, 206,
- 206, nil, 206, 206, nil, 483, 483, 483, 483, 483,
- 483, 483, 483, nil, 483, 483, nil, nil, nil, nil,
- nil, nil, 483, 483, nil, 206, nil, nil, 206, nil,
- nil, 206, nil, nil, 206, nil, nil, 483, nil, 483,
- 206, 483, 483, 483, 483, 483, 483, 483, 206, 483,
- nil, nil, nil, 206, 206, 206, 206, 206, 206, nil,
- nil, nil, 206, 206, nil, nil, nil, nil, nil, nil,
- 206, nil, nil, 206, nil, nil, nil, nil, 206, 206,
- 214, 214, 214, 214, 214, nil, nil, nil, 214, 214,
- nil, nil, nil, 214, nil, 214, 214, 214, 214, 214,
- 214, 214, nil, nil, nil, nil, nil, 214, 214, 214,
- 214, 214, 214, 214, nil, nil, 214, nil, nil, nil,
- nil, nil, 214, 214, nil, 214, 214, 214, nil, 214,
- 214, 214, 214, 214, nil, 214, 214, 214, nil, 214,
- 214, nil, 214, 214, nil, 469, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 469, 469, nil, 214, nil, nil, 214, nil,
- nil, 214, nil, nil, 214, nil, 214, 469, nil, nil,
- 214, 469, 469, 469, 469, nil, nil, nil, 214, nil,
- nil, nil, nil, 214, 214, 214, 214, 214, 214, nil,
- nil, nil, 214, 214, nil, nil, nil, 215, 215, 215,
- 214, 215, nil, 214, nil, 215, 215, nil, 214, 214,
- 215, nil, 215, 215, 215, 215, 215, 215, 215, nil,
- nil, nil, nil, nil, 215, 215, 215, 215, 215, 215,
- 215, nil, nil, 215, nil, nil, nil, nil, nil, nil,
- 215, nil, nil, 215, 215, nil, 215, 215, 215, 215,
- 215, nil, 215, 215, 215, nil, 215, 215, nil, 215,
- 215, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 215, nil, nil, 215, nil, 215, 215, nil,
- nil, 215, nil, nil, nil, nil, nil, 215, nil, nil,
- nil, nil, nil, nil, nil, 215, nil, nil, nil, nil,
- 215, 215, 215, 215, 215, 215, nil, nil, nil, 215,
- 215, nil, nil, nil, 218, 218, 218, 215, 218, nil,
- 215, nil, 218, 218, nil, 215, 215, 218, nil, 218,
- 218, 218, 218, 218, 218, 218, nil, nil, nil, nil,
- nil, 218, 218, 218, 218, 218, 218, 218, nil, nil,
- 218, nil, nil, nil, nil, nil, nil, 218, nil, nil,
- 218, 218, nil, 218, 218, 218, 218, 218, nil, 218,
- 218, 218, nil, 218, 218, nil, 218, 218, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 218,
- nil, nil, 218, nil, nil, 218, nil, nil, 218, nil,
- nil, nil, nil, nil, 218, nil, nil, nil, nil, nil,
- nil, nil, 218, nil, nil, nil, nil, 218, 218, 218,
- 218, 218, 218, nil, nil, nil, 218, 218, nil, nil,
- nil, 220, 220, 220, 218, 220, nil, 218, nil, 220,
- 220, nil, 218, 218, 220, nil, 220, 220, 220, 220,
- 220, 220, 220, nil, nil, nil, nil, nil, 220, 220,
- 220, 220, 220, 220, 220, nil, nil, 220, nil, nil,
- nil, nil, nil, nil, 220, nil, nil, 220, 220, nil,
- 220, 220, 220, 220, 220, nil, 220, 220, 220, nil,
- 220, 220, nil, 220, 220, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 220, nil, nil, 220,
- nil, nil, 220, nil, nil, 220, nil, nil, nil, nil,
- nil, 220, nil, nil, nil, nil, nil, nil, nil, 220,
- nil, nil, nil, nil, 220, 220, 220, 220, 220, 220,
- nil, nil, nil, 220, 220, nil, nil, nil, 221, 221,
- 221, 220, 221, nil, 220, nil, 221, 221, nil, 220,
- 220, 221, nil, 221, 221, 221, 221, 221, 221, 221,
- nil, nil, nil, nil, nil, 221, 221, 221, 221, 221,
- 221, 221, nil, nil, 221, nil, nil, nil, nil, nil,
- nil, 221, nil, nil, 221, 221, nil, 221, 221, 221,
- 221, 221, nil, 221, 221, 221, nil, 221, 221, nil,
- 221, 221, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 221, nil, nil, 221, nil, nil, 221,
- nil, nil, 221, nil, nil, nil, nil, nil, 221, nil,
- nil, nil, nil, nil, nil, nil, 221, nil, nil, nil,
- nil, 221, 221, 221, 221, 221, 221, nil, nil, nil,
- 221, 221, nil, nil, nil, 222, 222, 222, 221, 222,
- nil, 221, nil, 222, 222, nil, 221, 221, 222, nil,
- 222, 222, 222, 222, 222, 222, 222, nil, nil, nil,
- nil, nil, 222, 222, 222, 222, 222, 222, 222, nil,
- nil, 222, nil, nil, nil, nil, nil, nil, 222, nil,
- nil, 222, 222, nil, 222, 222, 222, 222, 222, nil,
- 222, 222, 222, nil, 222, 222, nil, 222, 222, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 222, nil, nil, 222, nil, nil, 222, nil, nil, 222,
- nil, nil, nil, nil, nil, 222, nil, nil, nil, nil,
- nil, nil, nil, 222, nil, nil, nil, nil, 222, 222,
- 222, 222, 222, 222, nil, nil, nil, 222, 222, nil,
- nil, nil, 223, 223, 223, 222, 223, nil, 222, nil,
- 223, 223, nil, 222, 222, 223, nil, 223, 223, 223,
- 223, 223, 223, 223, nil, nil, nil, nil, nil, 223,
- 223, 223, 223, 223, 223, 223, nil, nil, 223, nil,
- nil, nil, nil, nil, nil, 223, nil, nil, 223, 223,
- nil, 223, 223, 223, 223, 223, nil, 223, 223, 223,
- nil, 223, 223, nil, 223, 223, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 223, nil, nil,
- 223, nil, nil, 223, nil, nil, 223, nil, nil, nil,
- nil, nil, 223, nil, nil, nil, nil, nil, nil, nil,
- 223, nil, nil, nil, nil, 223, 223, 223, 223, 223,
- 223, nil, nil, nil, 223, 223, nil, nil, nil, 224,
- 224, 224, 223, 224, nil, 223, nil, 224, 224, nil,
- 223, 223, 224, nil, 224, 224, 224, 224, 224, 224,
- 224, nil, nil, nil, nil, nil, 224, 224, 224, 224,
- 224, 224, 224, nil, nil, 224, nil, nil, nil, nil,
- nil, nil, 224, nil, nil, 224, 224, nil, 224, 224,
- 224, 224, 224, nil, 224, 224, 224, nil, 224, 224,
- nil, 224, 224, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 224, nil, nil, 224, nil, nil,
- 224, nil, nil, 224, nil, nil, nil, nil, nil, 224,
- nil, nil, nil, nil, nil, nil, nil, 224, nil, nil,
- nil, nil, 224, 224, 224, 224, 224, 224, nil, nil,
- nil, 224, 224, nil, nil, nil, 225, 225, 225, 224,
- 225, nil, 224, nil, 225, 225, nil, 224, 224, 225,
- nil, 225, 225, 225, 225, 225, 225, 225, nil, nil,
- nil, nil, nil, 225, 225, 225, 225, 225, 225, 225,
- nil, nil, 225, nil, nil, nil, nil, nil, nil, 225,
- nil, nil, 225, 225, nil, 225, 225, 225, 225, 225,
- nil, 225, 225, 225, nil, 225, 225, nil, 225, 225,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 225, nil, nil, 225, nil, nil, 225, nil, nil,
- 225, nil, nil, nil, nil, nil, 225, nil, nil, nil,
- nil, nil, nil, nil, 225, nil, nil, nil, nil, 225,
- 225, 225, 225, 225, 225, nil, nil, nil, 225, 225,
- nil, nil, nil, 226, 226, 226, 225, 226, nil, 225,
- nil, 226, 226, nil, 225, 225, 226, nil, 226, 226,
- 226, 226, 226, 226, 226, nil, nil, nil, nil, nil,
- 226, 226, 226, 226, 226, 226, 226, nil, nil, 226,
- nil, nil, nil, nil, nil, nil, 226, nil, nil, 226,
- 226, nil, 226, 226, 226, 226, 226, nil, 226, 226,
- 226, nil, 226, 226, nil, 226, 226, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 226, nil,
- nil, 226, nil, nil, 226, nil, nil, 226, nil, nil,
- nil, nil, nil, 226, nil, nil, nil, nil, nil, nil,
- nil, 226, nil, nil, nil, nil, 226, 226, 226, 226,
- 226, 226, nil, nil, nil, 226, 226, nil, nil, nil,
- 227, 227, 227, 226, 227, nil, 226, nil, 227, 227,
- nil, 226, 226, 227, nil, 227, 227, 227, 227, 227,
- 227, 227, nil, nil, nil, nil, nil, 227, 227, 227,
- 227, 227, 227, 227, nil, nil, 227, nil, nil, nil,
- nil, nil, nil, 227, nil, nil, 227, 227, nil, 227,
- 227, 227, 227, 227, nil, 227, 227, 227, nil, 227,
- 227, nil, 227, 227, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 227, nil, nil, 227, nil,
- nil, 227, nil, nil, 227, nil, nil, nil, nil, nil,
- 227, nil, nil, nil, nil, nil, nil, nil, 227, nil,
- nil, nil, nil, 227, 227, 227, 227, 227, 227, nil,
- nil, nil, 227, 227, nil, nil, nil, 228, 228, 228,
- 227, 228, nil, 227, nil, 228, 228, nil, 227, 227,
- 228, nil, 228, 228, 228, 228, 228, 228, 228, nil,
- nil, nil, nil, nil, 228, 228, 228, 228, 228, 228,
- 228, nil, nil, 228, nil, nil, nil, nil, nil, nil,
- 228, nil, nil, 228, 228, nil, 228, 228, 228, 228,
- 228, nil, 228, 228, 228, nil, 228, 228, nil, 228,
- 228, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 228, nil, nil, 228, nil, nil, 228, nil,
- nil, 228, nil, nil, nil, nil, nil, 228, nil, nil,
- nil, nil, nil, nil, nil, 228, nil, nil, nil, nil,
- 228, 228, 228, 228, 228, 228, nil, nil, nil, 228,
- 228, nil, nil, nil, 229, 229, 229, 228, 229, nil,
- 228, nil, 229, 229, nil, 228, 228, 229, nil, 229,
- 229, 229, 229, 229, 229, 229, nil, nil, nil, nil,
- nil, 229, 229, 229, 229, 229, 229, 229, nil, nil,
- 229, nil, nil, nil, nil, nil, nil, 229, nil, nil,
- 229, 229, nil, 229, 229, 229, 229, 229, nil, 229,
- 229, 229, nil, 229, 229, nil, 229, 229, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 229,
- nil, nil, 229, nil, nil, 229, nil, nil, 229, nil,
- nil, nil, nil, nil, 229, nil, nil, nil, nil, nil,
- nil, nil, 229, nil, nil, nil, nil, 229, 229, 229,
- 229, 229, 229, nil, nil, nil, 229, 229, nil, nil,
- nil, 230, 230, 230, 229, 230, nil, 229, nil, 230,
- 230, nil, 229, 229, 230, nil, 230, 230, 230, 230,
- 230, 230, 230, nil, nil, nil, nil, nil, 230, 230,
- 230, 230, 230, 230, 230, nil, nil, 230, nil, nil,
- nil, nil, nil, nil, 230, nil, nil, 230, 230, nil,
- 230, 230, 230, 230, 230, nil, 230, 230, 230, nil,
- 230, 230, nil, 230, 230, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 230, nil, nil, 230,
- nil, nil, 230, nil, nil, 230, nil, nil, nil, nil,
- nil, 230, nil, nil, nil, nil, nil, nil, nil, 230,
- nil, nil, nil, nil, 230, 230, 230, 230, 230, 230,
- nil, nil, nil, 230, 230, nil, nil, nil, 231, 231,
- 231, 230, 231, nil, 230, nil, 231, 231, nil, 230,
- 230, 231, nil, 231, 231, 231, 231, 231, 231, 231,
- nil, nil, nil, nil, nil, 231, 231, 231, 231, 231,
- 231, 231, nil, nil, 231, nil, nil, nil, nil, nil,
- nil, 231, nil, nil, 231, 231, nil, 231, 231, 231,
- 231, 231, nil, 231, 231, 231, nil, 231, 231, nil,
- 231, 231, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 231, nil, nil, 231, nil, nil, 231,
- nil, nil, 231, nil, nil, nil, nil, nil, 231, nil,
- nil, nil, nil, nil, nil, nil, 231, nil, nil, nil,
- nil, 231, 231, 231, 231, 231, 231, nil, nil, nil,
- 231, 231, nil, nil, nil, 232, 232, 232, 231, 232,
- nil, 231, nil, 232, 232, nil, 231, 231, 232, nil,
- 232, 232, 232, 232, 232, 232, 232, nil, nil, nil,
- nil, nil, 232, 232, 232, 232, 232, 232, 232, nil,
- nil, 232, nil, nil, nil, nil, nil, nil, 232, nil,
- nil, 232, 232, nil, 232, 232, 232, 232, 232, nil,
- 232, 232, 232, nil, 232, 232, nil, 232, 232, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 232, nil, nil, 232, nil, nil, 232, nil, nil, 232,
- nil, nil, nil, nil, nil, 232, nil, nil, nil, nil,
- nil, nil, nil, 232, nil, nil, nil, nil, 232, 232,
- 232, 232, 232, 232, nil, nil, nil, 232, 232, nil,
- nil, nil, 233, 233, 233, 232, 233, nil, 232, nil,
- 233, 233, nil, 232, 232, 233, nil, 233, 233, 233,
- 233, 233, 233, 233, nil, nil, nil, nil, nil, 233,
- 233, 233, 233, 233, 233, 233, nil, nil, 233, nil,
- nil, nil, nil, nil, nil, 233, nil, nil, 233, 233,
- nil, 233, 233, 233, 233, 233, nil, 233, 233, 233,
- nil, 233, 233, nil, 233, 233, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 233, nil, nil,
- 233, nil, nil, 233, nil, nil, 233, nil, nil, nil,
- nil, nil, 233, nil, nil, nil, nil, nil, nil, nil,
- 233, nil, nil, nil, nil, 233, 233, 233, 233, 233,
- 233, nil, nil, nil, 233, 233, nil, nil, nil, 234,
- 234, 234, 233, 234, nil, 233, nil, 234, 234, nil,
- 233, 233, 234, nil, 234, 234, 234, 234, 234, 234,
- 234, nil, nil, nil, nil, nil, 234, 234, 234, 234,
- 234, 234, 234, nil, nil, 234, nil, nil, nil, nil,
- nil, nil, 234, nil, nil, 234, 234, nil, 234, 234,
- 234, 234, 234, nil, 234, 234, 234, nil, 234, 234,
- nil, 234, 234, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 234, nil, nil, 234, nil, nil,
- 234, nil, nil, 234, nil, nil, nil, nil, nil, 234,
- nil, nil, nil, nil, nil, nil, nil, 234, nil, nil,
- nil, nil, 234, 234, 234, 234, 234, 234, nil, nil,
- nil, 234, 234, nil, nil, nil, 235, 235, 235, 234,
- 235, nil, 234, nil, 235, 235, nil, 234, 234, 235,
- nil, 235, 235, 235, 235, 235, 235, 235, nil, nil,
- nil, nil, nil, 235, 235, 235, 235, 235, 235, 235,
- nil, nil, 235, nil, nil, nil, nil, nil, nil, 235,
- nil, nil, 235, 235, nil, 235, 235, 235, 235, 235,
- nil, 235, 235, 235, nil, 235, 235, nil, 235, 235,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 235, nil, nil, 235, nil, nil, 235, nil, nil,
- 235, nil, nil, nil, nil, nil, 235, nil, nil, nil,
- nil, nil, nil, nil, 235, nil, nil, nil, nil, 235,
- 235, 235, 235, 235, 235, nil, nil, nil, 235, 235,
- nil, nil, nil, 236, 236, 236, 235, 236, nil, 235,
- nil, 236, 236, nil, 235, 235, 236, nil, 236, 236,
- 236, 236, 236, 236, 236, nil, nil, nil, nil, nil,
- 236, 236, 236, 236, 236, 236, 236, nil, nil, 236,
- nil, nil, nil, nil, nil, nil, 236, nil, nil, 236,
- 236, nil, 236, 236, 236, 236, 236, nil, 236, 236,
- 236, nil, 236, 236, nil, 236, 236, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 236, nil,
- nil, 236, nil, nil, 236, nil, nil, 236, nil, nil,
- nil, nil, nil, 236, nil, nil, nil, nil, nil, nil,
- nil, 236, nil, nil, nil, nil, 236, 236, 236, 236,
- 236, 236, nil, nil, nil, 236, 236, nil, nil, nil,
- 237, 237, 237, 236, 237, nil, 236, nil, 237, 237,
- nil, 236, 236, 237, nil, 237, 237, 237, 237, 237,
- 237, 237, nil, nil, nil, nil, nil, 237, 237, 237,
- 237, 237, 237, 237, nil, nil, 237, nil, nil, nil,
- nil, nil, nil, 237, nil, nil, 237, 237, nil, 237,
- 237, 237, 237, 237, nil, 237, 237, 237, nil, 237,
- 237, nil, 237, 237, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 237, nil, nil, 237, nil,
- nil, 237, nil, nil, 237, nil, nil, nil, nil, nil,
- 237, nil, nil, nil, nil, nil, nil, nil, 237, nil,
- nil, nil, nil, 237, 237, 237, 237, 237, 237, nil,
- nil, nil, 237, 237, nil, nil, nil, 238, 238, 238,
- 237, 238, nil, 237, nil, 238, 238, nil, 237, 237,
- 238, nil, 238, 238, 238, 238, 238, 238, 238, nil,
- nil, nil, nil, nil, 238, 238, 238, 238, 238, 238,
- 238, nil, nil, 238, nil, nil, nil, nil, nil, nil,
- 238, nil, nil, 238, 238, nil, 238, 238, 238, 238,
- 238, nil, 238, 238, 238, nil, 238, 238, nil, 238,
- 238, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 238, nil, nil, 238, nil, nil, 238, nil,
- nil, 238, nil, nil, nil, nil, nil, 238, nil, nil,
- nil, nil, nil, nil, nil, 238, nil, nil, nil, nil,
- 238, 238, 238, 238, 238, 238, nil, nil, nil, 238,
- 238, nil, nil, nil, 239, 239, 239, 238, 239, nil,
- 238, nil, 239, 239, nil, 238, 238, 239, nil, 239,
- 239, 239, 239, 239, 239, 239, nil, nil, nil, nil,
- nil, 239, 239, 239, 239, 239, 239, 239, nil, nil,
- 239, nil, nil, nil, nil, nil, nil, 239, nil, nil,
- 239, 239, nil, 239, 239, 239, 239, 239, nil, 239,
- 239, 239, nil, 239, 239, nil, 239, 239, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 239,
- nil, nil, 239, nil, nil, 239, nil, nil, 239, nil,
- nil, nil, nil, nil, 239, nil, nil, nil, nil, nil,
- nil, nil, 239, nil, nil, nil, nil, 239, 239, 239,
- 239, 239, 239, nil, nil, nil, 239, 239, nil, nil,
- nil, 240, 240, 240, 239, 240, nil, 239, nil, 240,
- 240, nil, 239, 239, 240, nil, 240, 240, 240, 240,
- 240, 240, 240, nil, nil, nil, nil, nil, 240, 240,
- 240, 240, 240, 240, 240, nil, nil, 240, nil, nil,
- nil, nil, nil, nil, 240, nil, nil, 240, 240, nil,
- 240, 240, 240, 240, 240, nil, 240, 240, 240, nil,
- 240, 240, nil, 240, 240, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 240, nil, nil, 240,
- nil, nil, 240, nil, nil, 240, nil, nil, nil, nil,
- nil, 240, nil, nil, nil, nil, nil, nil, nil, 240,
- nil, nil, nil, nil, 240, 240, 240, 240, 240, 240,
- nil, nil, nil, 240, 240, nil, nil, nil, 241, 241,
- 241, 240, 241, nil, 240, nil, 241, 241, nil, 240,
- 240, 241, nil, 241, 241, 241, 241, 241, 241, 241,
- nil, nil, nil, nil, nil, 241, 241, 241, 241, 241,
- 241, 241, nil, nil, 241, nil, nil, nil, nil, nil,
- nil, 241, nil, nil, 241, 241, nil, 241, 241, 241,
- 241, 241, nil, 241, 241, 241, nil, 241, 241, nil,
- 241, 241, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 241, nil, nil, 241, nil, nil, 241,
- nil, nil, 241, nil, nil, nil, nil, nil, 241, nil,
- nil, nil, nil, nil, nil, nil, 241, nil, nil, nil,
- nil, 241, 241, 241, 241, 241, 241, nil, nil, nil,
- 241, 241, nil, nil, nil, 242, 242, 242, 241, 242,
- nil, 241, nil, 242, 242, nil, 241, 241, 242, nil,
- 242, 242, 242, 242, 242, 242, 242, nil, nil, nil,
- nil, nil, 242, 242, 242, 242, 242, 242, 242, nil,
- nil, 242, nil, nil, nil, nil, nil, nil, 242, nil,
- nil, 242, 242, nil, 242, 242, 242, 242, 242, nil,
- 242, 242, 242, nil, 242, 242, nil, 242, 242, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 242, nil, nil, 242, nil, nil, 242, nil, nil, 242,
- nil, nil, nil, nil, nil, 242, nil, nil, nil, nil,
- nil, nil, nil, 242, nil, nil, nil, nil, 242, 242,
- 242, 242, 242, 242, nil, nil, nil, 242, 242, nil,
- nil, nil, 243, 243, 243, 242, 243, nil, 242, nil,
- 243, 243, nil, 242, 242, 243, nil, 243, 243, 243,
- 243, 243, 243, 243, nil, nil, nil, nil, nil, 243,
- 243, 243, 243, 243, 243, 243, nil, nil, 243, nil,
- nil, nil, nil, nil, nil, 243, nil, nil, 243, 243,
- nil, 243, 243, 243, 243, 243, nil, 243, 243, 243,
- nil, 243, 243, nil, 243, 243, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 243, nil, nil,
- 243, nil, nil, 243, nil, nil, 243, nil, nil, nil,
- nil, nil, 243, nil, nil, nil, nil, nil, nil, nil,
- 243, nil, nil, nil, nil, 243, 243, 243, 243, 243,
- 243, nil, nil, nil, 243, 243, nil, nil, nil, 244,
- 244, 244, 243, 244, nil, 243, nil, 244, 244, nil,
- 243, 243, 244, nil, 244, 244, 244, 244, 244, 244,
- 244, nil, nil, nil, nil, nil, 244, 244, 244, 244,
- 244, 244, 244, nil, nil, 244, nil, nil, nil, nil,
- nil, nil, 244, nil, nil, 244, 244, nil, 244, 244,
- 244, 244, 244, nil, 244, 244, 244, nil, 244, 244,
- nil, 244, 244, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 244, nil, nil, 244, nil, nil,
- 244, nil, nil, 244, nil, nil, nil, nil, nil, 244,
- nil, nil, nil, nil, nil, nil, nil, 244, nil, nil,
- nil, nil, 244, 244, 244, 244, 244, 244, nil, nil,
- nil, 244, 244, nil, nil, nil, 245, 245, 245, 244,
- 245, nil, 244, nil, 245, 245, nil, 244, 244, 245,
- nil, 245, 245, 245, 245, 245, 245, 245, nil, nil,
- nil, nil, nil, 245, 245, 245, 245, 245, 245, 245,
- nil, nil, 245, nil, nil, nil, nil, nil, nil, 245,
- nil, nil, 245, 245, nil, 245, 245, 245, 245, 245,
- nil, 245, 245, 245, nil, 245, 245, nil, 245, 245,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 245, nil, nil, 245, nil, nil, 245, nil, nil,
- 245, nil, nil, nil, nil, nil, 245, nil, nil, nil,
- nil, nil, nil, nil, 245, nil, nil, nil, nil, 245,
- 245, 245, 245, 245, 245, nil, nil, nil, 245, 245,
- nil, nil, nil, 254, 254, 254, 245, 254, nil, 245,
- nil, 254, 254, nil, 245, 245, 254, nil, 254, 254,
- 254, 254, 254, 254, 254, nil, nil, nil, nil, nil,
- 254, 254, 254, 254, 254, 254, 254, nil, nil, 254,
- nil, nil, nil, nil, nil, nil, 254, nil, nil, 254,
- 254, nil, 254, 254, 254, 254, 254, nil, 254, 254,
- 254, nil, 254, 254, nil, 254, 254, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 254, nil,
- nil, 254, nil, nil, 254, nil, nil, 254, nil, nil,
- nil, nil, nil, 254, nil, nil, nil, nil, nil, nil,
- nil, 254, nil, nil, nil, nil, 254, 254, 254, 254,
- 254, 254, nil, nil, nil, 254, 254, nil, nil, nil,
- 256, 256, 256, 254, 256, nil, 254, nil, 256, 256,
- nil, 254, 254, 256, nil, 256, 256, 256, 256, 256,
- 256, 256, nil, nil, nil, nil, nil, 256, 256, 256,
- 256, 256, 256, 256, nil, nil, 256, nil, nil, nil,
- nil, nil, nil, 256, nil, nil, 256, 256, nil, 256,
- 256, 256, 256, 256, nil, 256, 256, 256, nil, 256,
- 256, nil, 256, 256, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 256, nil, nil, 256, nil,
- nil, 256, nil, nil, 256, nil, nil, nil, nil, nil,
- 256, nil, nil, nil, nil, nil, nil, nil, 256, nil,
- nil, nil, nil, 256, 256, 256, 256, 256, 256, nil,
- nil, nil, 256, 256, nil, nil, nil, 261, 261, 261,
- 256, 261, nil, 256, nil, 261, 261, nil, 256, 256,
- 261, nil, 261, 261, 261, 261, 261, 261, 261, nil,
- nil, nil, nil, nil, 261, 261, 261, 261, 261, 261,
- 261, nil, nil, 261, nil, nil, nil, nil, nil, nil,
- 261, nil, nil, 261, 261, nil, 261, 261, 261, 261,
- 261, nil, 261, 261, 261, nil, 261, 261, nil, 261,
- 261, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 261, nil, nil, 261, nil, nil, 261, nil,
- nil, 261, nil, nil, nil, nil, nil, 261, nil, nil,
- nil, nil, nil, nil, nil, 261, nil, nil, nil, nil,
- 261, 261, 261, 261, 261, 261, nil, nil, nil, 261,
- 261, nil, nil, nil, 268, 268, 268, 261, 268, nil,
- 261, nil, 268, 268, nil, 261, 261, 268, nil, 268,
- 268, 268, 268, 268, 268, 268, nil, nil, nil, nil,
- nil, 268, 268, 268, 268, 268, 268, 268, nil, nil,
- 268, nil, nil, nil, nil, nil, nil, 268, nil, nil,
- 268, 268, nil, 268, 268, 268, 268, 268, 268, 268,
- 268, 268, nil, 268, 268, nil, 268, 268, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 268,
- nil, nil, 268, nil, nil, 268, nil, nil, 268, nil,
- 268, nil, 268, nil, 268, nil, nil, nil, nil, nil,
- nil, nil, 268, nil, nil, nil, nil, 268, 268, 268,
- 268, 268, 268, nil, nil, nil, 268, 268, nil, nil,
- nil, 269, 269, 269, 268, 269, nil, 268, nil, 269,
- 269, nil, 268, 268, 269, nil, 269, 269, 269, 269,
- 269, 269, 269, nil, nil, nil, nil, nil, 269, 269,
- 269, 269, 269, 269, 269, nil, nil, 269, nil, nil,
- nil, nil, nil, nil, 269, nil, nil, 269, 269, nil,
- 269, 269, 269, 269, 269, 269, 269, 269, 269, nil,
- 269, 269, nil, 269, 269, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 269, nil, nil, 269,
- nil, nil, 269, nil, nil, 269, nil, 269, nil, 269,
- nil, 269, nil, nil, nil, nil, nil, nil, nil, 269,
- nil, nil, nil, nil, 269, 269, 269, 269, 269, 269,
- nil, nil, nil, 269, 269, nil, nil, nil, 277, 277,
- 277, 269, 277, nil, 269, nil, 277, 277, nil, 269,
- 269, 277, nil, 277, 277, 277, 277, 277, 277, 277,
- nil, nil, nil, nil, nil, 277, 277, 277, 277, 277,
- 277, 277, nil, nil, 277, nil, nil, nil, nil, nil,
- nil, 277, nil, nil, 277, 277, nil, 277, 277, 277,
- 277, 277, 277, 277, 277, 277, nil, 277, 277, nil,
- 277, 277, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 277, nil, nil, 277, nil, 277, 277,
- nil, nil, 277, nil, 277, nil, 277, nil, 277, nil,
- nil, nil, nil, nil, nil, nil, 277, nil, nil, nil,
- nil, 277, 277, 277, 277, 277, 277, nil, nil, nil,
- 277, 277, nil, nil, nil, nil, nil, nil, 277, nil,
- nil, 277, nil, nil, nil, nil, 277, 277, 281, 281,
- 281, 281, 281, nil, nil, nil, 281, 281, nil, nil,
- nil, 281, nil, 281, 281, 281, 281, 281, 281, 281,
- nil, nil, nil, nil, nil, 281, 281, 281, 281, 281,
- 281, 281, nil, nil, 281, nil, nil, nil, nil, nil,
- 281, 281, nil, 281, 281, 281, nil, 281, 281, 281,
- 281, 281, nil, 281, 281, 281, nil, 281, 281, nil,
- 281, 281, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 281, nil, nil, 281, nil, nil, 281,
- nil, nil, 281, nil, 281, nil, nil, nil, 281, nil,
- nil, nil, nil, nil, nil, nil, 281, nil, nil, nil,
- nil, 281, 281, 281, 281, 281, 281, nil, nil, nil,
- 281, 281, nil, nil, nil, 285, 285, 285, 281, 285,
- nil, 281, nil, 285, 285, nil, 281, 281, 285, nil,
- 285, 285, 285, 285, 285, 285, 285, nil, nil, nil,
- nil, nil, 285, 285, 285, 285, 285, 285, 285, nil,
- nil, 285, nil, nil, nil, nil, nil, nil, 285, nil,
- nil, 285, 285, nil, 285, 285, 285, 285, 285, nil,
- 285, 285, 285, nil, 285, 285, 749, nil, 749, 749,
- 749, 749, 749, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 749, nil, nil, nil, nil, nil, nil, nil,
- 285, nil, nil, 285, nil, nil, 285, nil, nil, 285,
- nil, nil, nil, nil, nil, 749, nil, nil, nil, nil,
- nil, nil, nil, 749, 749, 749, 749, nil, 285, 285,
- 285, 285, 285, 285, nil, nil, nil, 285, 285, nil,
- nil, nil, 285, nil, nil, 285, nil, nil, 285, nil,
- nil, nil, nil, 285, 285, 286, 286, 286, 286, 286,
- nil, nil, 749, 286, 286, nil, nil, nil, 286, nil,
- 286, 286, 286, 286, 286, 286, 286, nil, nil, nil,
- nil, nil, 286, 286, 286, 286, 286, 286, 286, nil,
- nil, 286, nil, nil, nil, nil, nil, 286, 286, nil,
- 286, 286, 286, nil, 286, 286, 286, 286, 286, nil,
- 286, 286, 286, nil, 286, 286, nil, 286, 286, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 286, nil, nil, 286, nil, nil, 286, nil, nil, 286,
- nil, 286, nil, nil, nil, 286, nil, nil, nil, nil,
- nil, nil, nil, 286, nil, nil, nil, nil, 286, 286,
- 286, 286, 286, 286, nil, nil, nil, 286, 286, nil,
- nil, nil, 298, 298, 298, 286, 298, nil, 286, nil,
- 298, 298, nil, 286, 286, 298, nil, 298, 298, 298,
- 298, 298, 298, 298, nil, nil, nil, nil, nil, 298,
- 298, 298, 298, 298, 298, 298, nil, nil, 298, nil,
- nil, nil, nil, nil, nil, 298, nil, nil, 298, 298,
- nil, 298, 298, 298, 298, 298, nil, 298, 298, 298,
- nil, 298, 298, 56, nil, 56, 56, 56, nil, 56,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 298, nil, nil,
- 298, nil, nil, 298, nil, nil, 298, nil, nil, nil,
- nil, nil, 56, 56, nil, nil, nil, nil, nil, nil,
- 56, 56, 56, 56, nil, 298, 298, 298, 298, 298,
- 298, nil, nil, nil, 298, 298, nil, nil, nil, 307,
- 307, 307, 298, 307, nil, 298, nil, 307, 307, nil,
- 298, 298, 307, nil, 307, 307, 307, 307, 307, 307,
- 307, nil, nil, nil, nil, nil, 307, 307, 307, 307,
- 307, 307, 307, nil, nil, 307, nil, nil, nil, nil,
- nil, nil, 307, nil, nil, 307, 307, nil, 307, 307,
- 307, 307, 307, nil, 307, 307, 307, nil, 307, 307,
- nil, 307, 307, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 307, nil, nil, 307, 307, nil,
- 307, nil, nil, 307, nil, nil, nil, nil, nil, 307,
- nil, nil, nil, nil, nil, nil, nil, 307, nil, nil,
- nil, nil, 307, 307, 307, 307, 307, 307, nil, nil,
- nil, 307, 307, nil, nil, nil, nil, nil, nil, 307,
- nil, nil, 307, nil, nil, nil, nil, 307, 307, 309,
- 309, 309, 309, 309, nil, nil, nil, 309, 309, nil,
- nil, nil, 309, nil, 309, 309, 309, 309, 309, 309,
- 309, nil, nil, nil, nil, nil, 309, 309, 309, 309,
- 309, 309, 309, nil, nil, 309, nil, nil, nil, nil,
- nil, 309, 309, nil, 309, 309, 309, nil, 309, 309,
- 309, 309, 309, nil, 309, 309, 309, nil, 309, 309,
- nil, 309, 309, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 309, nil, nil, 309, nil, nil,
- 309, nil, nil, 309, nil, 309, nil, nil, nil, 309,
- nil, nil, nil, nil, nil, nil, nil, 309, nil, nil,
- nil, nil, 309, 309, 309, 309, 309, 309, nil, nil,
- nil, 309, 309, nil, nil, nil, 349, 349, 349, 309,
- 349, nil, 309, nil, 349, 349, nil, 309, 309, 349,
- nil, 349, 349, 349, 349, 349, 349, 349, nil, nil,
- nil, nil, nil, 349, 349, 349, 349, 349, 349, 349,
- nil, nil, 349, nil, nil, nil, nil, nil, nil, 349,
- nil, nil, 349, 349, nil, 349, 349, 349, 349, 349,
- nil, 349, 349, 349, nil, 349, 349, nil, 349, 349,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 349, nil, nil, 349, nil, nil, 349, nil, nil,
- 349, nil, nil, nil, nil, nil, 349, nil, nil, nil,
- nil, nil, nil, nil, 349, nil, nil, nil, nil, 349,
- 349, 349, 349, 349, 349, nil, nil, nil, 349, 349,
- nil, nil, nil, 350, 350, 350, 349, 350, nil, 349,
- nil, 350, 350, nil, 349, 349, 350, nil, 350, 350,
- 350, 350, 350, 350, 350, nil, nil, nil, nil, nil,
- 350, 350, 350, 350, 350, 350, 350, nil, nil, 350,
- nil, nil, nil, nil, nil, nil, 350, nil, nil, 350,
- 350, nil, 350, 350, 350, 350, 350, nil, 350, 350,
- 350, nil, 350, 350, nil, 350, 350, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 350, nil,
- nil, 350, nil, nil, 350, nil, nil, 350, nil, nil,
- nil, nil, nil, 350, nil, nil, nil, nil, nil, nil,
- nil, 350, nil, nil, nil, nil, 350, 350, 350, 350,
- 350, 350, nil, nil, nil, 350, 350, nil, nil, nil,
- 369, 369, 369, 350, 369, nil, 350, nil, 369, 369,
- nil, 350, 350, 369, nil, 369, 369, 369, 369, 369,
- 369, 369, nil, nil, nil, nil, nil, 369, 369, 369,
- 369, 369, 369, 369, nil, nil, 369, nil, nil, nil,
- nil, nil, nil, 369, nil, nil, 369, 369, nil, 369,
- 369, 369, 369, 369, nil, 369, 369, 369, nil, 369,
- 369, 322, nil, 322, 322, 322, nil, 322, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 369, nil, nil, 369, nil,
- nil, 369, nil, nil, 369, nil, nil, nil, nil, nil,
- 322, nil, 322, nil, nil, nil, nil, nil, 322, 322,
- 322, 322, nil, 369, 369, 369, 369, 369, 369, nil,
- nil, nil, 369, 369, nil, nil, nil, 381, 381, 381,
- 369, 381, nil, 369, nil, 381, 381, nil, 369, 369,
- 381, nil, 381, 381, 381, 381, 381, 381, 381, nil,
- nil, nil, nil, nil, 381, 381, 381, 381, 381, 381,
- 381, nil, nil, 381, nil, nil, nil, nil, nil, nil,
- 381, nil, nil, 381, 381, nil, 381, 381, 381, 381,
- 381, nil, 381, 381, 381, nil, 381, 381, nil, 381,
- 381, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 381, nil, nil, 381, nil, nil, 381, nil,
- nil, 381, nil, nil, nil, nil, nil, 381, nil, nil,
- nil, nil, nil, nil, nil, 381, nil, nil, nil, nil,
- 381, 381, 381, 381, 381, 381, nil, nil, nil, 381,
- 381, nil, nil, nil, 421, 421, 421, 381, 421, nil,
- 381, nil, 421, 421, nil, 381, 381, 421, nil, 421,
- 421, 421, 421, 421, 421, 421, nil, nil, nil, nil,
- nil, 421, 421, 421, 421, 421, 421, 421, nil, nil,
- 421, nil, nil, nil, nil, nil, nil, 421, nil, nil,
- 421, 421, nil, 421, 421, 421, 421, 421, nil, 421,
- 421, 421, nil, 421, 421, nil, 421, 421, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 421,
- nil, nil, 421, nil, nil, 421, nil, nil, 421, nil,
- nil, nil, nil, nil, 421, nil, nil, nil, nil, nil,
- nil, nil, 421, nil, nil, nil, nil, 421, 421, 421,
- 421, 421, 421, nil, nil, nil, 421, 421, nil, nil,
- nil, 432, 432, 432, 421, 432, nil, 421, nil, 432,
- 432, nil, 421, 421, 432, nil, 432, 432, 432, 432,
- 432, 432, 432, nil, nil, nil, nil, nil, 432, 432,
- 432, 432, 432, 432, 432, nil, nil, 432, nil, nil,
- nil, nil, nil, nil, 432, nil, nil, 432, 432, nil,
- 432, 432, 432, 432, 432, 432, 432, 432, 432, nil,
- 432, 432, nil, 432, 432, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 432, nil, nil, 432,
- 432, nil, 432, nil, nil, 432, nil, 432, nil, 432,
- nil, 432, nil, nil, nil, nil, nil, nil, nil, 432,
- nil, nil, nil, nil, 432, 432, 432, 432, 432, 432,
- nil, nil, nil, 432, 432, nil, nil, nil, 440, 440,
- 440, 432, 440, nil, 432, nil, 440, 440, nil, 432,
- 432, 440, nil, 440, 440, 440, 440, 440, 440, 440,
- nil, nil, nil, nil, nil, 440, 440, 440, 440, 440,
- 440, 440, nil, nil, 440, nil, nil, nil, nil, nil,
- nil, 440, nil, nil, 440, 440, nil, 440, 440, 440,
- 440, 440, 440, 440, 440, 440, nil, 440, 440, nil,
- 440, 440, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 440, nil, nil, 440, 440, nil, 440,
- nil, nil, 440, nil, 440, nil, 440, nil, 440, nil,
- nil, nil, nil, nil, nil, nil, 440, nil, nil, nil,
- nil, 440, 440, 440, 440, 440, 440, nil, nil, nil,
- 440, 440, nil, nil, nil, 441, 441, 441, 440, 441,
- nil, 440, nil, 441, 441, nil, 440, 440, 441, nil,
- 441, 441, 441, 441, 441, 441, 441, nil, nil, nil,
- nil, nil, 441, 441, 441, 441, 441, 441, 441, nil,
- nil, 441, nil, nil, nil, nil, nil, nil, 441, nil,
- nil, 441, 441, nil, 441, 441, 441, 441, 441, 441,
- 441, 441, 441, nil, 441, 441, nil, 441, 441, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 441, nil, nil, 441, 441, nil, 441, nil, nil, 441,
- nil, 441, nil, 441, nil, 441, nil, nil, nil, nil,
- nil, nil, nil, 441, nil, nil, nil, nil, 441, 441,
- 441, 441, 441, 441, nil, nil, nil, 441, 441, nil,
- nil, nil, 442, 442, 442, 441, 442, nil, 441, nil,
- 442, 442, nil, 441, 441, 442, nil, 442, 442, 442,
- 442, 442, 442, 442, nil, nil, nil, nil, nil, 442,
- 442, 442, 442, 442, 442, 442, nil, nil, 442, nil,
- nil, nil, nil, nil, nil, 442, nil, nil, 442, 442,
- nil, 442, 442, 442, 442, 442, 442, 442, 442, 442,
- nil, 442, 442, nil, 442, 442, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 442, nil, nil,
- 442, 442, nil, 442, nil, nil, 442, nil, 442, nil,
- 442, nil, 442, nil, nil, nil, nil, nil, nil, nil,
- 442, nil, nil, nil, nil, 442, 442, 442, 442, 442,
- 442, nil, nil, nil, 442, 442, nil, nil, nil, 451,
- 451, 451, 442, 451, nil, 442, nil, 451, 451, nil,
- 442, 442, 451, nil, 451, 451, 451, 451, 451, 451,
- 451, nil, nil, nil, nil, nil, 451, 451, 451, 451,
- 451, 451, 451, nil, nil, 451, nil, nil, nil, nil,
- nil, nil, 451, nil, nil, 451, 451, nil, 451, 451,
- 451, 451, 451, 451, 451, 451, 451, nil, 451, 451,
- nil, 451, 451, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 451, nil, nil, 451, nil, nil,
- 451, nil, nil, 451, nil, 451, nil, nil, nil, 451,
- nil, nil, nil, nil, nil, nil, nil, 451, nil, nil,
- nil, nil, 451, 451, 451, 451, 451, 451, nil, nil,
- nil, 451, 451, nil, nil, nil, 452, 452, 452, 451,
- 452, nil, 451, nil, 452, 452, nil, 451, 451, 452,
- nil, 452, 452, 452, 452, 452, 452, 452, nil, nil,
- nil, nil, nil, 452, 452, 452, 452, 452, 452, 452,
- nil, nil, 452, nil, nil, nil, nil, nil, nil, 452,
- nil, nil, 452, 452, nil, 452, 452, 452, 452, 452,
- 452, 452, 452, 452, nil, 452, 452, nil, 452, 452,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 452, nil, nil, 452, nil, nil, 452, nil, nil,
- 452, nil, 452, nil, nil, nil, 452, nil, nil, nil,
- nil, nil, nil, nil, 452, nil, nil, nil, nil, 452,
- 452, 452, 452, 452, 452, nil, nil, nil, 452, 452,
- nil, nil, nil, 454, 454, 454, 452, 454, nil, 452,
- nil, 454, 454, nil, 452, 452, 454, nil, 454, 454,
- 454, 454, 454, 454, 454, nil, nil, nil, nil, nil,
- 454, 454, 454, 454, 454, 454, 454, nil, nil, 454,
- nil, nil, nil, nil, nil, nil, 454, nil, nil, 454,
- 454, nil, 454, 454, 454, 454, 454, nil, 454, 454,
- 454, nil, 454, 454, nil, 454, 454, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 454, nil,
- nil, 454, nil, nil, 454, nil, nil, 454, nil, nil,
- nil, nil, nil, 454, nil, nil, nil, nil, nil, nil,
- nil, 454, nil, nil, nil, nil, 454, 454, 454, 454,
- 454, 454, nil, nil, nil, 454, 454, nil, nil, nil,
- 455, 455, 455, 454, 455, nil, 454, nil, 455, 455,
- nil, 454, 454, 455, nil, 455, 455, 455, 455, 455,
- 455, 455, nil, nil, nil, nil, nil, 455, 455, 455,
- 455, 455, 455, 455, nil, nil, 455, nil, nil, nil,
- nil, nil, nil, 455, nil, nil, 455, 455, nil, 455,
- 455, 455, 455, 455, nil, 455, 455, 455, nil, 455,
- 455, nil, 455, 455, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 455, nil, nil, 455, nil,
- nil, 455, nil, nil, 455, nil, nil, nil, nil, nil,
- 455, nil, nil, nil, nil, nil, nil, nil, 455, nil,
- nil, nil, nil, 455, 455, 455, 455, 455, 455, nil,
- nil, nil, 455, 455, nil, nil, nil, 456, 456, 456,
- 455, 456, nil, 455, nil, 456, 456, nil, 455, 455,
- 456, nil, 456, 456, 456, 456, 456, 456, 456, nil,
- nil, nil, nil, nil, 456, 456, 456, 456, 456, 456,
- 456, nil, nil, 456, nil, nil, nil, nil, nil, nil,
- 456, nil, nil, 456, 456, nil, 456, 456, 456, 456,
- 456, nil, 456, 456, 456, nil, 456, 456, nil, 456,
- 456, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 456, nil, nil, 456, nil, nil, 456, nil,
- nil, 456, nil, nil, nil, nil, nil, 456, nil, nil,
- nil, nil, nil, nil, nil, 456, nil, nil, nil, nil,
- 456, 456, 456, 456, 456, 456, nil, nil, nil, 456,
- 456, nil, nil, nil, 487, 487, 487, 456, 487, nil,
- 456, nil, 487, 487, nil, 456, 456, 487, nil, 487,
- 487, 487, 487, 487, 487, 487, nil, nil, nil, nil,
- nil, 487, 487, 487, 487, 487, 487, 487, nil, nil,
- 487, nil, nil, nil, nil, nil, nil, 487, nil, nil,
- 487, 487, nil, 487, 487, 487, 487, 487, 487, 487,
- 487, 487, nil, 487, 487, nil, 487, 487, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 487,
- nil, nil, 487, nil, nil, 487, nil, nil, 487, nil,
- 487, nil, 487, nil, 487, nil, nil, nil, nil, nil,
- nil, nil, 487, nil, nil, nil, nil, 487, 487, 487,
- 487, 487, 487, nil, nil, nil, 487, 487, nil, nil,
- nil, 489, 489, 489, 487, 489, nil, 487, nil, 489,
- 489, nil, 487, 487, 489, nil, 489, 489, 489, 489,
- 489, 489, 489, nil, nil, nil, nil, nil, 489, 489,
- 489, 489, 489, 489, 489, nil, nil, 489, nil, nil,
- nil, nil, nil, nil, 489, nil, nil, 489, 489, nil,
- 489, 489, 489, 489, 489, 489, 489, 489, 489, nil,
- 489, 489, nil, 489, 489, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 489, nil, nil, 489,
- nil, nil, 489, nil, nil, 489, nil, nil, nil, 489,
- nil, 489, nil, nil, nil, nil, nil, nil, nil, 489,
- nil, nil, nil, nil, 489, 489, 489, 489, 489, 489,
- nil, nil, nil, 489, 489, nil, nil, nil, 491, 491,
- 491, 489, 491, nil, 489, nil, 491, 491, nil, 489,
- 489, 491, nil, 491, 491, 491, 491, 491, 491, 491,
- nil, nil, nil, nil, nil, 491, 491, 491, 491, 491,
- 491, 491, nil, nil, 491, nil, nil, nil, nil, nil,
- nil, 491, nil, nil, 491, 491, nil, 491, 491, 491,
- 491, 491, nil, 491, 491, 491, nil, 491, 491, nil,
- 491, 491, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 491, nil, nil, 491, nil, nil, 491,
- nil, nil, 491, nil, nil, nil, nil, nil, 491, nil,
- nil, nil, nil, nil, nil, nil, 491, nil, nil, nil,
- nil, 491, 491, 491, 491, 491, 491, nil, nil, nil,
- 491, 491, nil, nil, nil, 506, 506, 506, 491, 506,
- nil, 491, nil, 506, 506, nil, 491, 491, 506, nil,
- 506, 506, 506, 506, 506, 506, 506, nil, nil, nil,
- nil, nil, 506, 506, 506, 506, 506, 506, 506, nil,
- nil, 506, nil, nil, nil, nil, nil, nil, 506, nil,
- nil, 506, 506, nil, 506, 506, 506, 506, 506, nil,
- 506, 506, 506, nil, 506, 506, nil, 506, 506, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 506, nil, nil, 506, nil, 506, 506, nil, nil, 506,
- nil, nil, nil, 506, nil, 506, nil, nil, nil, nil,
- nil, nil, nil, 506, nil, nil, nil, nil, 506, 506,
- 506, 506, 506, 506, nil, nil, nil, 506, 506, nil,
- nil, nil, nil, nil, nil, 506, nil, nil, 506, nil,
- nil, nil, nil, 506, 506, 512, 512, 512, 512, 512,
- nil, nil, nil, 512, 512, nil, nil, nil, 512, nil,
- 512, 512, 512, 512, 512, 512, 512, nil, nil, nil,
- nil, nil, 512, 512, 512, 512, 512, 512, 512, nil,
- nil, 512, nil, nil, nil, nil, nil, 512, 512, nil,
- 512, 512, 512, nil, 512, 512, 512, 512, 512, nil,
- 512, 512, 512, nil, 512, 512, nil, 512, 512, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 512, nil, nil, 512, nil, nil, 512, nil, nil, 512,
- nil, 512, nil, nil, nil, 512, nil, nil, nil, nil,
- nil, nil, nil, 512, nil, nil, nil, nil, 512, 512,
- 512, 512, 512, 512, nil, nil, nil, 512, 512, nil,
- nil, nil, nil, nil, 512, 512, nil, nil, 512, nil,
- nil, nil, nil, 512, 512, 518, 518, 518, nil, 518,
- nil, nil, nil, 518, 518, nil, nil, nil, 518, nil,
- 518, 518, 518, 518, 518, 518, 518, nil, nil, nil,
- nil, nil, 518, 518, 518, 518, 518, 518, 518, nil,
- nil, 518, nil, nil, nil, nil, nil, nil, 518, nil,
- nil, 518, 518, nil, 518, 518, 518, 518, 518, nil,
- 518, 518, 518, nil, 518, 518, 559, nil, 559, 559,
- 559, nil, 559, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 518, nil, nil, 518, nil, nil, 518, nil, nil, 518,
- nil, nil, nil, nil, nil, 559, nil, nil, nil, nil,
- nil, nil, nil, 559, 559, 559, 559, nil, 518, 518,
- 518, 518, 518, 518, nil, nil, nil, 518, 518, nil,
- nil, nil, 520, 520, 520, 518, 520, nil, 518, nil,
- 520, 520, nil, 518, 518, 520, nil, 520, 520, 520,
- 520, 520, 520, 520, nil, nil, nil, nil, nil, 520,
- 520, 520, 520, 520, 520, 520, nil, nil, 520, nil,
- nil, nil, nil, nil, nil, 520, nil, nil, 520, 520,
- nil, 520, 520, 520, 520, 520, 520, 520, 520, 520,
- nil, 520, 520, nil, 520, 520, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 520, nil, nil,
- 520, nil, nil, 520, nil, nil, 520, nil, 520, nil,
- nil, nil, 520, nil, nil, nil, nil, nil, nil, nil,
- 520, nil, nil, nil, nil, 520, 520, 520, 520, 520,
- 520, nil, nil, nil, 520, 520, nil, nil, nil, 523,
- 523, 523, 520, 523, nil, 520, nil, 523, 523, nil,
- 520, 520, 523, nil, 523, 523, 523, 523, 523, 523,
- 523, nil, nil, nil, nil, nil, 523, 523, 523, 523,
- 523, 523, 523, nil, nil, 523, nil, nil, nil, nil,
- nil, nil, 523, nil, nil, 523, 523, nil, 523, 523,
- 523, 523, 523, 523, 523, 523, 523, nil, 523, 523,
- nil, 523, 523, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 523, nil, nil, 523, nil, nil,
- 523, nil, nil, 523, nil, 523, nil, nil, nil, 523,
- nil, nil, nil, nil, nil, nil, nil, 523, nil, nil,
- nil, nil, 523, 523, 523, 523, 523, 523, nil, nil,
- nil, 523, 523, nil, nil, nil, 531, 531, 531, 523,
- 531, nil, 523, nil, 531, 531, nil, 523, 523, 531,
- nil, 531, 531, 531, 531, 531, 531, 531, nil, nil,
- nil, nil, nil, 531, 531, 531, 531, 531, 531, 531,
- nil, nil, 531, nil, nil, nil, nil, nil, nil, 531,
- nil, nil, 531, 531, nil, 531, 531, 531, 531, 531,
- nil, 531, 531, 531, nil, 531, 531, nil, 531, 531,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 531, nil, nil, 531, nil, nil, 531, nil, nil,
- 531, nil, nil, nil, nil, nil, 531, nil, nil, nil,
- nil, nil, nil, nil, 531, nil, nil, nil, nil, 531,
- 531, 531, 531, 531, 531, nil, nil, nil, 531, 531,
- nil, nil, nil, 532, 532, 532, 531, 532, nil, 531,
- nil, 532, 532, nil, 531, 531, 532, nil, 532, 532,
- 532, 532, 532, 532, 532, nil, nil, nil, nil, nil,
- 532, 532, 532, 532, 532, 532, 532, nil, nil, 532,
- nil, nil, nil, nil, nil, nil, 532, nil, nil, 532,
- 532, nil, 532, 532, 532, 532, 532, nil, 532, 532,
- 532, nil, 532, 532, nil, 532, 532, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 532, nil,
- nil, 532, nil, nil, 532, nil, nil, 532, nil, nil,
- nil, nil, nil, 532, nil, nil, nil, nil, nil, nil,
- nil, 532, nil, nil, nil, nil, 532, 532, 532, 532,
- 532, 532, nil, nil, nil, 532, 532, nil, nil, nil,
- 533, 533, 533, 532, 533, nil, 532, nil, 533, 533,
- nil, 532, 532, 533, nil, 533, 533, 533, 533, 533,
- 533, 533, nil, nil, nil, nil, nil, 533, 533, 533,
- 533, 533, 533, 533, nil, nil, 533, nil, nil, nil,
- nil, nil, nil, 533, nil, nil, 533, 533, nil, 533,
- 533, 533, 533, 533, nil, 533, 533, 533, nil, 533,
- 533, nil, 533, 533, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 533, nil, nil, 533, nil,
- nil, 533, nil, nil, 533, nil, nil, nil, nil, nil,
- 533, nil, nil, nil, nil, nil, nil, nil, 533, nil,
- nil, nil, nil, 533, 533, 533, 533, 533, 533, nil,
- nil, nil, 533, 533, nil, nil, nil, 537, 537, 537,
- 533, 537, nil, 533, nil, 537, 537, nil, 533, 533,
- 537, nil, 537, 537, 537, 537, 537, 537, 537, nil,
- nil, nil, nil, nil, 537, 537, 537, 537, 537, 537,
- 537, nil, nil, 537, nil, nil, nil, nil, nil, nil,
- 537, nil, nil, 537, 537, nil, 537, 537, 537, 537,
- 537, nil, 537, 537, 537, nil, 537, 537, nil, 537,
- 537, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 537, nil, nil, 537, nil, nil, 537, nil,
- nil, 537, nil, nil, nil, nil, nil, 537, nil, nil,
- nil, nil, nil, nil, nil, 537, nil, nil, nil, nil,
- 537, 537, 537, 537, 537, 537, nil, nil, nil, 537,
- 537, nil, nil, nil, 543, 543, 543, 537, 543, nil,
- 537, nil, 543, 543, nil, 537, 537, 543, nil, 543,
- 543, 543, 543, 543, 543, 543, nil, nil, nil, nil,
- nil, 543, 543, 543, 543, 543, 543, 543, nil, nil,
- 543, nil, nil, nil, nil, nil, nil, 543, nil, nil,
- 543, 543, nil, 543, 543, 543, 543, 543, 543, 543,
- 543, 543, nil, 543, 543, nil, 543, 543, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 543,
- nil, nil, 543, nil, nil, 543, nil, nil, 543, nil,
- 543, nil, nil, nil, 543, nil, nil, nil, nil, nil,
- nil, nil, 543, nil, nil, nil, nil, 543, 543, 543,
- 543, 543, 543, nil, nil, nil, 543, 543, nil, nil,
- nil, 546, 546, 546, 543, 546, nil, 543, nil, 546,
- 546, nil, 543, 543, 546, nil, 546, 546, 546, 546,
- 546, 546, 546, nil, nil, nil, nil, nil, 546, 546,
- 546, 546, 546, 546, 546, nil, nil, 546, nil, nil,
- nil, nil, nil, nil, 546, nil, nil, 546, 546, nil,
- 546, 546, 546, 546, 546, 546, 546, 546, 546, nil,
- 546, 546, nil, 546, 546, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 546, nil, nil, 546,
- nil, nil, 546, nil, nil, 546, nil, nil, nil, nil,
- nil, 546, nil, nil, nil, nil, nil, nil, nil, 546,
- nil, nil, nil, nil, 546, 546, 546, 546, 546, 546,
- nil, nil, nil, 546, 546, nil, nil, nil, nil, nil,
- nil, 546, nil, nil, 546, nil, nil, nil, nil, 546,
- 546, 551, 551, 551, 551, 551, nil, nil, nil, 551,
- 551, nil, nil, nil, 551, nil, 551, 551, 551, 551,
- 551, 551, 551, nil, nil, nil, nil, nil, 551, 551,
- 551, 551, 551, 551, 551, nil, nil, 551, nil, nil,
- nil, nil, nil, 551, 551, nil, 551, 551, 551, nil,
- 551, 551, 551, 551, 551, nil, 551, 551, 551, nil,
- 551, 551, nil, 551, 551, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 551, nil, nil, 551,
- nil, nil, 551, nil, nil, 551, nil, 551, nil, nil,
- nil, 551, nil, nil, nil, nil, nil, nil, nil, 551,
- nil, nil, nil, nil, 551, 551, 551, 551, 551, 551,
- nil, nil, nil, 551, 551, nil, nil, nil, nil, nil,
- nil, 551, nil, nil, 551, nil, nil, nil, nil, 551,
- 551, 552, 552, 552, 552, 552, nil, nil, nil, 552,
- 552, nil, nil, nil, 552, nil, 552, 552, 552, 552,
- 552, 552, 552, nil, nil, nil, nil, nil, 552, 552,
- 552, 552, 552, 552, 552, nil, nil, 552, nil, nil,
- nil, nil, nil, 552, 552, nil, 552, 552, 552, nil,
- 552, 552, 552, 552, 552, nil, 552, 552, 552, nil,
- 552, 552, nil, 552, 552, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 552, nil, nil, 552,
- nil, nil, 552, nil, nil, 552, nil, 552, nil, nil,
- nil, 552, nil, nil, nil, nil, nil, nil, nil, 552,
- nil, nil, nil, nil, 552, 552, 552, 552, 552, 552,
- nil, nil, nil, 552, 552, nil, nil, nil, 558, 558,
- 558, 552, 558, nil, 552, nil, 558, 558, nil, 552,
- 552, 558, nil, 558, 558, 558, 558, 558, 558, 558,
- nil, nil, nil, nil, nil, 558, 558, 558, 558, 558,
- 558, 558, nil, nil, 558, nil, nil, nil, nil, nil,
- nil, 558, nil, nil, 558, 558, nil, 558, 558, 558,
- 558, 558, nil, 558, 558, 558, nil, 558, 558, 856,
- nil, 856, 856, 856, 856, 856, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 856, nil, nil, nil, nil,
- nil, nil, nil, 558, nil, nil, 558, nil, nil, 558,
- nil, nil, 558, nil, nil, nil, nil, nil, 856, nil,
- nil, nil, nil, nil, nil, nil, 856, 856, 856, 856,
- nil, 558, 558, 558, 558, 558, 558, nil, nil, nil,
- 558, 558, nil, nil, nil, nil, nil, nil, 558, nil,
- nil, 558, nil, nil, nil, nil, 558, 558, 572, 572,
- 572, 572, 572, nil, nil, 856, 572, 572, nil, nil,
- nil, 572, nil, 572, 572, 572, 572, 572, 572, 572,
- nil, nil, nil, nil, nil, 572, 572, 572, 572, 572,
- 572, 572, nil, nil, 572, nil, nil, nil, nil, nil,
- 572, 572, nil, 572, 572, 572, nil, 572, 572, 572,
- 572, 572, nil, 572, 572, 572, nil, 572, 572, nil,
- 572, 572, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 572, nil, nil, 572, nil, nil, 572,
- nil, nil, 572, nil, 572, nil, nil, nil, 572, nil,
- nil, nil, nil, nil, nil, nil, 572, nil, nil, nil,
- nil, 572, 572, 572, 572, 572, 572, nil, nil, nil,
- 572, 572, nil, nil, nil, nil, nil, nil, 572, nil,
- nil, 572, nil, nil, nil, nil, 572, 572, 576, 576,
- 576, 576, 576, nil, nil, nil, 576, 576, nil, nil,
- nil, 576, nil, 576, 576, 576, 576, 576, 576, 576,
- nil, nil, nil, nil, nil, 576, 576, 576, 576, 576,
- 576, 576, nil, nil, 576, nil, nil, nil, nil, nil,
- 576, 576, nil, 576, 576, 576, nil, 576, 576, 576,
- 576, 576, nil, 576, 576, 576, nil, 576, 576, nil,
- 576, 576, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 576, nil, nil, 576, nil, nil, 576,
- nil, nil, 576, nil, 576, nil, nil, nil, 576, nil,
- nil, nil, nil, nil, nil, nil, 576, nil, nil, nil,
- nil, 576, 576, 576, 576, 576, 576, nil, nil, nil,
- 576, 576, nil, nil, nil, nil, nil, nil, 576, nil,
- nil, 576, nil, nil, nil, nil, 576, 576, 581, 581,
- 581, 581, 581, nil, nil, nil, 581, 581, nil, nil,
- nil, 581, nil, 581, 581, 581, 581, 581, 581, 581,
- nil, nil, nil, nil, nil, 581, 581, 581, 581, 581,
- 581, 581, nil, nil, 581, nil, nil, nil, nil, nil,
- 581, 581, nil, 581, 581, 581, nil, 581, 581, 581,
- 581, 581, nil, 581, 581, 581, nil, 581, 581, nil,
- 581, 581, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 581, nil, nil, 581, nil, nil, 581,
- nil, nil, 581, nil, 581, nil, nil, nil, 581, nil,
- nil, nil, nil, nil, nil, nil, 581, nil, nil, nil,
- nil, 581, 581, 581, 581, 581, 581, nil, nil, nil,
- 581, 581, nil, nil, nil, 583, 583, 583, 581, 583,
- nil, 581, nil, 583, 583, nil, 581, 581, 583, nil,
- 583, 583, 583, 583, 583, 583, 583, nil, nil, nil,
- nil, nil, 583, 583, 583, 583, 583, 583, 583, nil,
- nil, 583, nil, nil, nil, nil, nil, nil, 583, nil,
- nil, 583, 583, nil, 583, 583, 583, 583, 583, 583,
- 583, 583, 583, nil, 583, 583, nil, 583, 583, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 583, nil, nil, 583, nil, nil, 583, nil, nil, 583,
- nil, 583, nil, nil, nil, 583, nil, nil, nil, nil,
- nil, nil, nil, 583, nil, nil, nil, nil, 583, 583,
- 583, 583, 583, 583, nil, nil, nil, 583, 583, nil,
- nil, nil, 586, 586, 586, 583, 586, nil, 583, nil,
- 586, 586, nil, 583, 583, 586, nil, 586, 586, 586,
- 586, 586, 586, 586, nil, nil, nil, nil, nil, 586,
- 586, 586, 586, 586, 586, 586, nil, nil, 586, nil,
- nil, nil, nil, nil, nil, 586, nil, nil, 586, 586,
- nil, 586, 586, 586, 586, 586, 586, 586, 586, 586,
- nil, 586, 586, nil, 586, 586, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 586, nil, nil,
- 586, nil, nil, 586, nil, nil, 586, nil, 586, nil,
- nil, nil, 586, nil, nil, nil, nil, nil, nil, nil,
- 586, nil, nil, nil, nil, 586, 586, 586, 586, 586,
- 586, nil, nil, nil, 586, 586, nil, nil, nil, 592,
- 592, 592, 586, 592, nil, 586, nil, 592, 592, nil,
- 586, 586, 592, nil, 592, 592, 592, 592, 592, 592,
- 592, nil, nil, nil, nil, nil, 592, 592, 592, 592,
- 592, 592, 592, nil, nil, 592, nil, nil, nil, nil,
- nil, nil, 592, nil, nil, 592, 592, nil, 592, 592,
- 592, 592, 592, 592, 592, 592, 592, nil, 592, 592,
- nil, 592, 592, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 592, nil, nil, 592, nil, nil,
- 592, nil, nil, 592, nil, 592, nil, nil, nil, 592,
- nil, nil, nil, nil, nil, nil, nil, 592, nil, nil,
- nil, nil, 592, 592, 592, 592, 592, 592, nil, nil,
- nil, 592, 592, nil, nil, nil, 596, 596, 596, 592,
- 596, nil, 592, nil, 596, 596, nil, 592, 592, 596,
- nil, 596, 596, 596, 596, 596, 596, 596, nil, nil,
- nil, nil, nil, 596, 596, 596, 596, 596, 596, 596,
- nil, nil, 596, nil, nil, nil, nil, nil, nil, 596,
- nil, nil, 596, 596, nil, 596, 596, 596, 596, 596,
- nil, 596, 596, 596, nil, 596, 596, nil, 596, 596,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 596, nil, nil, 596, nil, nil, 596, nil, nil,
- 596, nil, nil, nil, nil, nil, 596, nil, nil, nil,
- nil, nil, nil, nil, 596, nil, nil, nil, nil, 596,
- 596, 596, 596, 596, 596, nil, nil, nil, 596, 596,
- nil, nil, nil, 598, 598, 598, 596, 598, nil, 596,
- nil, 598, 598, nil, 596, 596, 598, nil, 598, 598,
- 598, 598, 598, 598, 598, nil, nil, nil, nil, nil,
- 598, 598, 598, 598, 598, 598, 598, nil, nil, 598,
- nil, nil, nil, nil, nil, nil, 598, nil, nil, 598,
- 598, nil, 598, 598, 598, 598, 598, nil, 598, 598,
- 598, nil, 598, 598, nil, 598, 598, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 598, nil,
- nil, 598, nil, nil, 598, nil, nil, 598, nil, nil,
- nil, nil, nil, 598, nil, nil, nil, nil, nil, nil,
- nil, 598, nil, nil, nil, nil, 598, 598, 598, 598,
- 598, 598, nil, nil, nil, 598, 598, nil, nil, nil,
- 625, 625, 625, 598, 625, nil, 598, nil, 625, 625,
- nil, 598, 598, 625, nil, 625, 625, 625, 625, 625,
- 625, 625, nil, nil, nil, nil, nil, 625, 625, 625,
- 625, 625, 625, 625, nil, nil, 625, nil, nil, nil,
- nil, nil, nil, 625, nil, nil, 625, 625, nil, 625,
- 625, 625, 625, 625, nil, 625, 625, 625, nil, 625,
- 625, nil, 625, 625, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 625, nil, nil, 625, nil,
- nil, 625, nil, nil, 625, nil, nil, nil, nil, nil,
- 625, nil, nil, nil, nil, nil, nil, nil, 625, nil,
- nil, nil, nil, 625, 625, 625, 625, 625, 625, nil,
- nil, nil, 625, 625, nil, nil, nil, 627, 627, 627,
- 625, 627, nil, 625, nil, 627, 627, nil, 625, 625,
- 627, nil, 627, 627, 627, 627, 627, 627, 627, nil,
- nil, nil, nil, nil, 627, 627, 627, 627, 627, 627,
- 627, nil, nil, 627, nil, nil, nil, nil, nil, nil,
- 627, nil, nil, 627, 627, nil, 627, 627, 627, 627,
- 627, nil, 627, 627, 627, nil, 627, 627, nil, 627,
- 627, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 627, nil, nil, 627, nil, nil, 627, nil,
- nil, 627, nil, 627, nil, nil, nil, 627, nil, nil,
- nil, nil, nil, nil, nil, 627, nil, nil, nil, nil,
- 627, 627, 627, 627, 627, 627, nil, nil, nil, 627,
- 627, nil, nil, nil, 630, 630, 630, 627, 630, nil,
- 627, nil, 630, 630, nil, 627, 627, 630, nil, 630,
- 630, 630, 630, 630, 630, 630, nil, nil, nil, nil,
- nil, 630, 630, 630, 630, 630, 630, 630, nil, nil,
- 630, nil, nil, nil, nil, nil, nil, 630, nil, nil,
- 630, 630, nil, 630, 630, 630, 630, 630, nil, 630,
- 630, 630, nil, 630, 630, nil, 630, 630, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 630,
- nil, nil, 630, nil, nil, 630, nil, nil, 630, nil,
- nil, nil, nil, nil, 630, nil, nil, nil, nil, nil,
- nil, nil, 630, nil, nil, nil, nil, 630, 630, 630,
- 630, 630, 630, nil, nil, nil, 630, 630, nil, nil,
- nil, 631, 631, 631, 630, 631, nil, 630, nil, 631,
- 631, nil, 630, 630, 631, nil, 631, 631, 631, 631,
- 631, 631, 631, nil, nil, nil, nil, nil, 631, 631,
- 631, 631, 631, 631, 631, nil, nil, 631, nil, nil,
- nil, nil, nil, nil, 631, nil, nil, 631, 631, nil,
- 631, 631, 631, 631, 631, nil, 631, 631, 631, nil,
- 631, 631, nil, 631, 631, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 631, nil, nil, 631,
- nil, nil, 631, nil, nil, 631, nil, nil, nil, nil,
- nil, 631, nil, nil, nil, nil, nil, nil, nil, 631,
- nil, nil, nil, nil, 631, 631, 631, 631, 631, 631,
- nil, nil, nil, 631, 631, nil, nil, nil, 636, 636,
- 636, 631, 636, nil, 631, nil, 636, 636, nil, 631,
- 631, 636, nil, 636, 636, 636, 636, 636, 636, 636,
- nil, nil, nil, nil, nil, 636, 636, 636, 636, 636,
- 636, 636, nil, nil, 636, nil, nil, nil, nil, nil,
- nil, 636, nil, nil, 636, 636, nil, 636, 636, 636,
- 636, 636, nil, 636, 636, 636, nil, 636, 636, nil,
- 636, 636, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 636, nil, nil, 636, nil, nil, 636,
- nil, nil, 636, nil, nil, nil, nil, nil, 636, nil,
- nil, nil, nil, nil, nil, nil, 636, nil, nil, nil,
- nil, 636, 636, 636, 636, 636, 636, nil, nil, nil,
- 636, 636, nil, nil, nil, 639, 639, 639, 636, 639,
- nil, 636, nil, 639, 639, nil, 636, 636, 639, nil,
- 639, 639, 639, 639, 639, 639, 639, nil, nil, nil,
- nil, nil, 639, 639, 639, 639, 639, 639, 639, nil,
- nil, 639, nil, nil, nil, nil, nil, nil, 639, nil,
- nil, 639, 639, nil, 639, 639, 639, 639, 639, nil,
- 639, 639, 639, nil, 639, 639, nil, 639, 639, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 639, nil, nil, 639, nil, nil, 639, nil, nil, 639,
- nil, nil, nil, nil, nil, 639, nil, nil, nil, nil,
- nil, nil, nil, 639, nil, nil, nil, nil, 639, 639,
- 639, 639, 639, 639, nil, nil, nil, 639, 639, nil,
- nil, nil, 650, 650, 650, 639, 650, nil, 639, nil,
- 650, 650, nil, 639, 639, 650, nil, 650, 650, 650,
- 650, 650, 650, 650, nil, nil, nil, nil, nil, 650,
- 650, 650, 650, 650, 650, 650, nil, nil, 650, nil,
- nil, nil, nil, nil, nil, 650, nil, nil, 650, 650,
- nil, 650, 650, 650, 650, 650, nil, 650, 650, 650,
- nil, 650, 650, nil, 650, 650, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 650, nil, nil,
- 650, nil, nil, 650, nil, nil, 650, nil, nil, nil,
- nil, nil, 650, nil, nil, nil, nil, nil, nil, nil,
- 650, nil, nil, nil, nil, 650, 650, 650, 650, 650,
- 650, nil, nil, nil, 650, 650, nil, nil, nil, nil,
- nil, nil, 650, nil, nil, 650, nil, nil, nil, nil,
- 650, 650, 654, 654, 654, 654, 654, nil, nil, nil,
- 654, 654, nil, nil, nil, 654, nil, 654, 654, 654,
- 654, 654, 654, 654, nil, nil, nil, nil, nil, 654,
- 654, 654, 654, 654, 654, 654, nil, nil, 654, nil,
- nil, nil, nil, nil, 654, 654, nil, 654, 654, 654,
- nil, 654, 654, 654, 654, 654, nil, 654, 654, 654,
- nil, 654, 654, nil, 654, 654, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 654, nil, nil,
- 654, nil, nil, 654, nil, nil, 654, nil, 654, nil,
- nil, nil, 654, nil, nil, nil, nil, nil, nil, nil,
- 654, nil, nil, nil, nil, 654, 654, 654, 654, 654,
- 654, nil, nil, nil, 654, 654, nil, nil, nil, 658,
- 658, 658, 654, 658, nil, 654, nil, 658, 658, nil,
- 654, 654, 658, nil, 658, 658, 658, 658, 658, 658,
- 658, nil, nil, nil, nil, nil, 658, 658, 658, 658,
- 658, 658, 658, nil, nil, 658, nil, nil, nil, nil,
- nil, nil, 658, nil, nil, 658, 658, nil, 658, 658,
- 658, 658, 658, nil, 658, 658, 658, nil, 658, 658,
- nil, 658, 658, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 658, nil, nil, 658, nil, nil,
- 658, nil, nil, 658, nil, nil, nil, nil, nil, 658,
- nil, nil, nil, nil, nil, nil, nil, 658, nil, nil,
- nil, nil, 658, 658, 658, 658, 658, 658, nil, nil,
- nil, 658, 658, nil, nil, nil, nil, nil, nil, 658,
- nil, nil, 658, nil, nil, nil, nil, 658, 658, 667,
- 667, 667, 667, 667, nil, nil, nil, 667, 667, nil,
- nil, nil, 667, nil, 667, 667, 667, 667, 667, 667,
- 667, nil, nil, nil, nil, nil, 667, 667, 667, 667,
- 667, 667, 667, nil, nil, 667, nil, nil, nil, nil,
- nil, 667, 667, nil, 667, 667, 667, nil, 667, 667,
- 667, 667, 667, nil, 667, 667, 667, nil, 667, 667,
- nil, 667, 667, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 667, nil, nil, 667, nil, nil,
- 667, nil, nil, 667, nil, 667, nil, nil, nil, 667,
- nil, nil, nil, nil, nil, nil, nil, 667, nil, nil,
- nil, nil, 667, 667, 667, 667, 667, 667, nil, nil,
- nil, 667, 667, nil, nil, nil, 670, 670, 670, 667,
- 670, nil, 667, nil, 670, 670, nil, 667, 667, 670,
- nil, 670, 670, 670, 670, 670, 670, 670, nil, nil,
- nil, nil, nil, 670, 670, 670, 670, 670, 670, 670,
- nil, nil, 670, nil, nil, nil, nil, nil, nil, 670,
- nil, nil, 670, 670, nil, 670, 670, 670, 670, 670,
- 670, 670, 670, 670, nil, 670, 670, nil, 670, 670,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 670, nil, nil, 670, nil, nil, 670, nil, nil,
- 670, nil, 670, nil, 670, nil, 670, nil, nil, nil,
- nil, nil, nil, nil, 670, nil, nil, nil, nil, 670,
- 670, 670, 670, 670, 670, nil, nil, nil, 670, 670,
- nil, nil, nil, 671, 671, 671, 670, 671, nil, 670,
- nil, 671, 671, nil, 670, 670, 671, nil, 671, 671,
- 671, 671, 671, 671, 671, nil, nil, nil, nil, nil,
- 671, 671, 671, 671, 671, 671, 671, nil, nil, 671,
- nil, nil, nil, nil, nil, nil, 671, nil, nil, 671,
- 671, nil, 671, 671, 671, 671, 671, 671, 671, 671,
- 671, nil, 671, 671, nil, 671, 671, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 671, nil,
- nil, 671, nil, nil, 671, nil, nil, 671, nil, nil,
- nil, 671, nil, 671, nil, nil, nil, nil, nil, nil,
- nil, 671, nil, nil, nil, nil, 671, 671, 671, 671,
- 671, 671, nil, nil, nil, 671, 671, nil, nil, nil,
- nil, nil, nil, 671, nil, nil, 671, nil, nil, nil,
- nil, 671, 671, 677, 677, 677, 677, 677, nil, nil,
- nil, 677, 677, nil, nil, nil, 677, nil, 677, 677,
- 677, 677, 677, 677, 677, nil, nil, nil, nil, nil,
- 677, 677, 677, 677, 677, 677, 677, nil, nil, 677,
- nil, nil, nil, nil, nil, 677, 677, nil, 677, 677,
- 677, nil, 677, 677, 677, 677, 677, nil, 677, 677,
- 677, nil, 677, 677, nil, 677, 677, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 677, nil,
- nil, 677, nil, nil, 677, nil, nil, 677, nil, 677,
- nil, nil, nil, 677, nil, nil, nil, nil, nil, nil,
- nil, 677, nil, nil, nil, nil, 677, 677, 677, 677,
- 677, 677, nil, nil, nil, 677, 677, nil, nil, nil,
- nil, nil, nil, 677, nil, nil, 677, nil, nil, nil,
- nil, 677, 677, 678, 678, 678, 678, 678, nil, nil,
- nil, 678, 678, nil, nil, nil, 678, nil, 678, 678,
- 678, 678, 678, 678, 678, nil, nil, nil, nil, nil,
- 678, 678, 678, 678, 678, 678, 678, nil, nil, 678,
- nil, nil, nil, nil, nil, 678, 678, nil, 678, 678,
- 678, nil, 678, 678, 678, 678, 678, nil, 678, 678,
- 678, nil, 678, 678, nil, 678, 678, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 678, nil,
- nil, 678, nil, nil, 678, nil, nil, 678, nil, 678,
- nil, nil, nil, 678, nil, nil, nil, nil, nil, nil,
- nil, 678, nil, nil, nil, nil, 678, 678, 678, 678,
- 678, 678, nil, nil, nil, 678, 678, nil, nil, nil,
- 682, 682, 682, 678, 682, nil, 678, nil, 682, 682,
- nil, 678, 678, 682, nil, 682, 682, 682, 682, 682,
- 682, 682, nil, nil, nil, nil, nil, 682, 682, 682,
- 682, 682, 682, 682, nil, nil, 682, nil, nil, nil,
- nil, nil, nil, 682, nil, nil, 682, 682, nil, 682,
- 682, 682, 682, 682, nil, 682, 682, 682, nil, 682,
- 682, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 682, nil, nil, 682, nil,
- nil, 682, nil, nil, 682, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 682, 682, 682, 682, 682, 682, nil,
- nil, nil, 682, 682, nil, nil, nil, 694, 694, 694,
- 682, 694, nil, 682, nil, 694, 694, nil, 682, 682,
- 694, nil, 694, 694, 694, 694, 694, 694, 694, nil,
- nil, nil, nil, nil, 694, 694, 694, 694, 694, 694,
- 694, nil, nil, 694, nil, nil, nil, nil, nil, nil,
- 694, nil, nil, 694, 694, nil, 694, 694, 694, 694,
- 694, nil, 694, 694, 694, nil, 694, 694, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 694, nil, nil, 694, nil, nil, 694, nil,
- nil, 694, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 694, 694, 694, 694, 694, 694, nil, nil, nil, 694,
- 694, nil, nil, nil, 700, 700, 700, 694, 700, nil,
- 694, nil, 700, 700, nil, 694, 694, 700, nil, 700,
- 700, 700, 700, 700, 700, 700, nil, nil, nil, nil,
- nil, 700, 700, 700, 700, 700, 700, 700, nil, nil,
- 700, nil, nil, nil, nil, nil, nil, 700, nil, nil,
- 700, 700, nil, 700, 700, 700, 700, 700, nil, 700,
- 700, 700, nil, 700, 700, nil, 700, 700, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 700,
- nil, nil, 700, nil, nil, 700, nil, nil, 700, nil,
- 700, nil, nil, nil, 700, nil, nil, nil, nil, nil,
- nil, nil, 700, nil, nil, nil, nil, 700, 700, 700,
- 700, 700, 700, nil, nil, nil, 700, 700, nil, nil,
- nil, 731, 731, 731, 700, 731, nil, 700, nil, 731,
- 731, nil, 700, 700, 731, nil, 731, 731, 731, 731,
- 731, 731, 731, nil, nil, nil, nil, nil, 731, 731,
- 731, 731, 731, 731, 731, nil, nil, 731, nil, nil,
- nil, nil, nil, nil, 731, nil, nil, 731, 731, nil,
- 731, 731, 731, 731, 731, nil, 731, 731, 731, nil,
- 731, 731, nil, 731, 731, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 731, nil, nil, 731,
- nil, nil, 731, nil, nil, 731, nil, 731, nil, nil,
- nil, 731, nil, nil, nil, nil, nil, nil, nil, 731,
- nil, nil, nil, nil, 731, 731, 731, 731, 731, 731,
- nil, nil, nil, 731, 731, nil, nil, nil, 738, 738,
- 738, 731, 738, nil, 731, nil, 738, 738, nil, 731,
- 731, 738, nil, 738, 738, 738, 738, 738, 738, 738,
- nil, nil, nil, nil, nil, 738, 738, 738, 738, 738,
- 738, 738, nil, nil, 738, nil, nil, nil, nil, nil,
- nil, 738, nil, nil, 738, 738, nil, 738, 738, 738,
- 738, 738, nil, 738, 738, 738, nil, 738, 738, nil,
- 738, 738, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 738, nil, nil, 738, nil, nil, 738,
- nil, nil, 738, nil, nil, nil, nil, nil, 738, nil,
- nil, nil, nil, nil, nil, nil, 738, nil, nil, nil,
- nil, 738, 738, 738, 738, 738, 738, nil, nil, nil,
- 738, 738, nil, nil, nil, nil, nil, nil, 738, nil,
- nil, 738, nil, nil, nil, nil, 738, 738, 743, 743,
- 743, 743, 743, nil, nil, nil, 743, 743, nil, nil,
- nil, 743, nil, 743, 743, 743, 743, 743, 743, 743,
- nil, nil, nil, nil, nil, 743, 743, 743, 743, 743,
- 743, 743, nil, nil, 743, nil, nil, nil, nil, nil,
- 743, 743, nil, 743, 743, 743, nil, 743, 743, 743,
- 743, 743, nil, 743, 743, 743, nil, 743, 743, nil,
- 743, 743, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 743, nil, nil, 743, nil, nil, 743,
- nil, nil, 743, nil, 743, nil, nil, nil, 743, nil,
- nil, nil, nil, nil, nil, nil, 743, nil, nil, nil,
- nil, 743, 743, 743, 743, 743, 743, nil, nil, nil,
- 743, 743, nil, nil, nil, nil, nil, nil, 743, nil,
- nil, 743, nil, nil, nil, nil, 743, 743, 747, 747,
- 747, 747, 747, nil, nil, nil, 747, 747, nil, nil,
- nil, 747, nil, 747, 747, 747, 747, 747, 747, 747,
- nil, nil, nil, nil, nil, 747, 747, 747, 747, 747,
- 747, 747, nil, nil, 747, nil, nil, nil, nil, nil,
- 747, 747, nil, 747, 747, 747, nil, 747, 747, 747,
- 747, 747, nil, 747, 747, 747, nil, 747, 747, nil,
- 747, 747, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 747, nil, nil, 747, nil, nil, 747,
- nil, nil, 747, nil, 747, nil, nil, nil, 747, nil,
- nil, nil, nil, nil, nil, nil, 747, nil, nil, nil,
- nil, 747, 747, 747, 747, 747, 747, nil, nil, nil,
- 747, 747, nil, nil, nil, nil, nil, nil, 747, nil,
- nil, 747, nil, nil, nil, nil, 747, 747, 748, 748,
- 748, 748, 748, nil, nil, nil, 748, 748, nil, nil,
- nil, 748, nil, 748, 748, 748, 748, 748, 748, 748,
- nil, nil, nil, nil, nil, 748, 748, 748, 748, 748,
- 748, 748, nil, nil, 748, nil, nil, nil, nil, nil,
- 748, 748, nil, 748, 748, 748, nil, 748, 748, 748,
- 748, 748, nil, 748, 748, 748, nil, 748, 748, nil,
- 748, 748, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 748, nil, nil, 748, nil, nil, 748,
- nil, nil, 748, nil, 748, nil, nil, nil, 748, nil,
- nil, nil, nil, nil, nil, nil, 748, nil, nil, nil,
- nil, 748, 748, 748, 748, 748, 748, nil, nil, nil,
- 748, 748, nil, nil, nil, 755, 755, 755, 748, 755,
- nil, 748, nil, 755, 755, nil, 748, 748, 755, nil,
- 755, 755, 755, 755, 755, 755, 755, nil, nil, nil,
- nil, nil, 755, 755, 755, 755, 755, 755, 755, nil,
- nil, 755, nil, nil, nil, nil, nil, nil, 755, nil,
- nil, 755, 755, nil, 755, 755, 755, 755, 755, nil,
- 755, 755, 755, nil, 755, 755, nil, 755, 755, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 755, nil, nil, 755, nil, nil, 755, nil, nil, 755,
- nil, nil, nil, nil, nil, 755, nil, nil, nil, nil,
- nil, nil, nil, 755, nil, nil, nil, nil, 755, 755,
- 755, 755, 755, 755, nil, nil, nil, 755, 755, nil,
- nil, nil, nil, nil, nil, 755, nil, nil, 755, nil,
- nil, nil, nil, 755, 755, 769, 769, 769, 769, 769,
- nil, nil, nil, 769, 769, nil, nil, nil, 769, nil,
- 769, 769, 769, 769, 769, 769, 769, nil, nil, nil,
- nil, nil, 769, 769, 769, 769, 769, 769, 769, nil,
- nil, 769, nil, nil, nil, nil, nil, 769, 769, nil,
- 769, 769, 769, nil, 769, 769, 769, 769, 769, nil,
- 769, 769, 769, nil, 769, 769, nil, 769, 769, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 769, nil, nil, 769, nil, nil, 769, nil, nil, 769,
- nil, 769, nil, nil, nil, 769, nil, nil, nil, nil,
- nil, nil, nil, 769, nil, nil, nil, nil, 769, 769,
- 769, 769, 769, 769, nil, nil, nil, 769, 769, nil,
- nil, nil, 775, 775, 775, 769, 775, nil, 769, nil,
- 775, 775, nil, 769, 769, 775, nil, 775, 775, 775,
- 775, 775, 775, 775, nil, nil, nil, nil, nil, 775,
- 775, 775, 775, 775, 775, 775, nil, nil, 775, nil,
- nil, nil, nil, nil, nil, 775, nil, nil, 775, 775,
- nil, 775, 775, 775, 775, 775, nil, 775, 775, 775,
- nil, 775, 775, nil, 775, 775, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 775, nil, nil,
- 775, nil, nil, 775, nil, nil, 775, nil, nil, nil,
- nil, nil, 775, nil, nil, nil, nil, nil, nil, nil,
- 775, nil, nil, nil, nil, 775, 775, 775, 775, 775,
- 775, nil, nil, nil, 775, 775, nil, nil, nil, 776,
- 776, 776, 775, 776, nil, 775, nil, 776, 776, nil,
- 775, 775, 776, nil, 776, 776, 776, 776, 776, 776,
- 776, nil, nil, nil, nil, nil, 776, 776, 776, 776,
- 776, 776, 776, nil, nil, 776, nil, nil, nil, nil,
- nil, nil, 776, nil, nil, 776, 776, nil, 776, 776,
- 776, 776, 776, nil, 776, 776, 776, nil, 776, 776,
- nil, 776, 776, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 776, nil, nil, 776, nil, nil,
- 776, nil, nil, 776, nil, nil, nil, nil, nil, 776,
- nil, nil, nil, nil, nil, nil, nil, 776, nil, nil,
- nil, nil, 776, 776, 776, 776, 776, 776, nil, nil,
- nil, 776, 776, nil, nil, nil, 777, 777, 777, 776,
- 777, nil, 776, nil, 777, 777, nil, 776, 776, 777,
- nil, 777, 777, 777, 777, 777, 777, 777, nil, nil,
- nil, nil, nil, 777, 777, 777, 777, 777, 777, 777,
- nil, nil, 777, nil, nil, nil, nil, nil, nil, 777,
- nil, nil, 777, 777, nil, 777, 777, 777, 777, 777,
- nil, 777, 777, 777, nil, 777, 777, nil, 777, 777,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 777, nil, nil, 777, nil, nil, 777, nil, nil,
- 777, nil, nil, nil, nil, nil, 777, nil, nil, nil,
- nil, nil, nil, nil, 777, nil, nil, nil, nil, 777,
- 777, 777, 777, 777, 777, nil, nil, nil, 777, 777,
- nil, nil, nil, 788, 788, 788, 777, 788, nil, 777,
- nil, 788, 788, nil, 777, 777, 788, nil, 788, 788,
- 788, 788, 788, 788, 788, nil, nil, nil, nil, nil,
- 788, 788, 788, 788, 788, 788, 788, nil, nil, 788,
- nil, nil, nil, nil, nil, nil, 788, nil, nil, 788,
- 788, nil, 788, 788, 788, 788, 788, nil, 788, 788,
- 788, nil, 788, 788, nil, 788, 788, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 788, nil,
- nil, 788, nil, nil, 788, nil, nil, 788, nil, nil,
- nil, nil, nil, 788, nil, nil, nil, nil, nil, nil,
- nil, 788, nil, nil, nil, nil, 788, 788, 788, 788,
- 788, 788, nil, nil, nil, 788, 788, nil, nil, nil,
- 789, 789, 789, 788, 789, nil, 788, nil, 789, 789,
- nil, 788, 788, 789, nil, 789, 789, 789, 789, 789,
- 789, 789, nil, nil, nil, nil, nil, 789, 789, 789,
- 789, 789, 789, 789, nil, nil, 789, nil, nil, nil,
- nil, nil, nil, 789, nil, nil, 789, 789, nil, 789,
- 789, 789, 789, 789, nil, 789, 789, 789, nil, 789,
- 789, nil, 789, 789, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 789, nil, nil, 789, nil,
- nil, 789, nil, nil, 789, nil, nil, nil, nil, nil,
- 789, nil, nil, nil, nil, nil, nil, nil, 789, nil,
- nil, nil, nil, 789, 789, 789, 789, 789, 789, nil,
- nil, nil, 789, 789, nil, nil, nil, 790, 790, 790,
- 789, 790, nil, 789, nil, 790, 790, nil, 789, 789,
- 790, nil, 790, 790, 790, 790, 790, 790, 790, nil,
- nil, nil, nil, nil, 790, 790, 790, 790, 790, 790,
- 790, nil, nil, 790, nil, nil, nil, nil, nil, nil,
- 790, nil, nil, 790, 790, nil, 790, 790, 790, 790,
- 790, nil, 790, 790, 790, nil, 790, 790, nil, 790,
- 790, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 790, nil, nil, 790, nil, nil, 790, nil,
- nil, 790, nil, nil, nil, nil, nil, 790, nil, nil,
- nil, nil, nil, nil, nil, 790, nil, nil, nil, nil,
- 790, 790, 790, 790, 790, 790, nil, nil, nil, 790,
- 790, nil, nil, nil, 802, 802, 802, 790, 802, nil,
- 790, nil, 802, 802, nil, 790, 790, 802, nil, 802,
- 802, 802, 802, 802, 802, 802, nil, nil, nil, nil,
- nil, 802, 802, 802, 802, 802, 802, 802, nil, nil,
- 802, nil, nil, nil, nil, nil, nil, 802, nil, nil,
- 802, 802, nil, 802, 802, 802, 802, 802, nil, 802,
- 802, 802, nil, 802, 802, nil, 802, 802, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 802,
- nil, nil, 802, nil, nil, 802, nil, nil, 802, nil,
- 802, nil, nil, nil, 802, nil, nil, nil, nil, nil,
- nil, nil, 802, nil, nil, nil, nil, 802, 802, 802,
- 802, 802, 802, nil, nil, nil, 802, 802, nil, nil,
- nil, nil, nil, nil, 802, nil, nil, 802, nil, nil,
- nil, nil, 802, 802, 813, 813, 813, 813, 813, nil,
- nil, nil, 813, 813, nil, nil, nil, 813, nil, 813,
- 813, 813, 813, 813, 813, 813, nil, nil, nil, nil,
- nil, 813, 813, 813, 813, 813, 813, 813, nil, nil,
- 813, nil, nil, nil, nil, nil, 813, 813, nil, 813,
- 813, 813, nil, 813, 813, 813, 813, 813, nil, 813,
- 813, 813, nil, 813, 813, nil, 813, 813, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 813,
- nil, nil, 813, nil, nil, 813, nil, nil, 813, nil,
- 813, nil, nil, nil, 813, nil, nil, nil, nil, nil,
- nil, nil, 813, nil, nil, nil, nil, 813, 813, 813,
- 813, 813, 813, nil, nil, nil, 813, 813, nil, nil,
- nil, 832, 832, 832, 813, 832, nil, 813, nil, 832,
- 832, nil, 813, 813, 832, nil, 832, 832, 832, 832,
- 832, 832, 832, nil, nil, nil, nil, nil, 832, 832,
- 832, 832, 832, 832, 832, nil, nil, 832, nil, nil,
- nil, nil, nil, nil, 832, nil, nil, 832, 832, nil,
- 832, 832, 832, 832, 832, nil, 832, 832, 832, nil,
- 832, 832, nil, 832, 832, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 832, nil, nil, 832,
- nil, nil, 832, nil, nil, 832, nil, nil, nil, nil,
- nil, 832, nil, nil, nil, nil, nil, nil, nil, 832,
- nil, nil, nil, nil, 832, 832, 832, 832, 832, 832,
- nil, nil, nil, 832, 832, nil, nil, nil, nil, nil,
- nil, 832, nil, nil, 832, nil, nil, nil, nil, 832,
- 832, 834, 834, 834, 834, 834, nil, nil, nil, 834,
- 834, nil, nil, nil, 834, nil, 834, 834, 834, 834,
- 834, 834, 834, nil, nil, nil, nil, nil, 834, 834,
- 834, 834, 834, 834, 834, nil, nil, 834, nil, nil,
- nil, nil, nil, 834, 834, nil, 834, 834, 834, nil,
- 834, 834, 834, 834, 834, nil, 834, 834, 834, nil,
- 834, 834, nil, 834, 834, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 834, nil, nil, 834,
- nil, nil, 834, nil, nil, 834, nil, 834, nil, nil,
- nil, 834, nil, nil, nil, nil, nil, nil, nil, 834,
- nil, nil, nil, nil, 834, 834, 834, 834, 834, 834,
- nil, nil, nil, 834, 834, nil, nil, nil, nil, nil,
- nil, 834, nil, nil, 834, nil, nil, nil, nil, 834,
- 834, 835, 835, 835, 835, 835, nil, nil, nil, 835,
- 835, nil, nil, nil, 835, nil, 835, 835, 835, 835,
- 835, 835, 835, nil, nil, nil, nil, nil, 835, 835,
- 835, 835, 835, 835, 835, nil, nil, 835, nil, nil,
- nil, nil, nil, 835, 835, nil, 835, 835, 835, nil,
- 835, 835, 835, 835, 835, nil, 835, 835, 835, nil,
- 835, 835, nil, 835, 835, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 835, nil, nil, 835,
- nil, nil, 835, nil, nil, 835, nil, 835, nil, nil,
- nil, 835, nil, nil, nil, nil, nil, nil, nil, 835,
- nil, nil, nil, nil, 835, 835, 835, 835, 835, 835,
- nil, nil, nil, 835, 835, nil, nil, nil, 862, 862,
- 862, 835, 862, nil, 835, nil, 862, 862, nil, 835,
- 835, 862, nil, 862, 862, 862, 862, 862, 862, 862,
- nil, nil, nil, nil, nil, 862, 862, 862, 862, 862,
- 862, 862, nil, nil, 862, nil, nil, nil, nil, nil,
- nil, 862, nil, nil, 862, 862, nil, 862, 862, 862,
- 862, 862, nil, 862, 862, 862, nil, 862, 862, nil,
- 862, 862, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 862, nil, nil, 862, nil, nil, 862,
- nil, nil, 862, nil, nil, nil, nil, nil, 862, nil,
- nil, nil, nil, nil, nil, nil, 862, nil, nil, nil,
- nil, 862, 862, 862, 862, 862, 862, nil, nil, nil,
- 862, 862, nil, nil, nil, 876, 876, 876, 862, 876,
- nil, 862, nil, 876, 876, nil, 862, 862, 876, nil,
- 876, 876, 876, 876, 876, 876, 876, nil, nil, nil,
- nil, nil, 876, 876, 876, 876, 876, 876, 876, nil,
- nil, 876, nil, nil, nil, nil, nil, nil, 876, nil,
- nil, 876, 876, nil, 876, 876, 876, 876, 876, nil,
- 876, 876, 876, nil, 876, 876, nil, 876, 876, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 876, nil, nil, 876, nil, nil, 876, nil, nil, 876,
- nil, nil, nil, nil, nil, 876, nil, nil, nil, nil,
- nil, nil, nil, 876, nil, nil, nil, nil, 876, 876,
- 876, 876, 876, 876, nil, nil, nil, 876, 876, nil,
- nil, nil, 877, 877, 877, 876, 877, nil, 876, nil,
- 877, 877, nil, 876, 876, 877, nil, 877, 877, 877,
- 877, 877, 877, 877, nil, nil, nil, nil, nil, 877,
- 877, 877, 877, 877, 877, 877, nil, nil, 877, nil,
- nil, nil, nil, nil, nil, 877, nil, nil, 877, 877,
- nil, 877, 877, 877, 877, 877, nil, 877, 877, 877,
- nil, 877, 877, nil, 877, 877, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 877, nil, nil,
- 877, nil, nil, 877, nil, nil, 877, nil, nil, nil,
- nil, nil, 877, nil, nil, nil, nil, nil, nil, nil,
- 877, nil, nil, nil, nil, 877, 877, 877, 877, 877,
- 877, nil, nil, nil, 877, 877, nil, nil, nil, 881,
- 881, 881, 877, 881, nil, 877, nil, 881, 881, nil,
- 877, 877, 881, nil, 881, 881, 881, 881, 881, 881,
- 881, nil, nil, nil, nil, nil, 881, 881, 881, 881,
- 881, 881, 881, nil, nil, 881, nil, nil, nil, nil,
- nil, nil, 881, nil, nil, 881, 881, nil, 881, 881,
- 881, 881, 881, 881, 881, 881, 881, nil, 881, 881,
- nil, 881, 881, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 881, nil, nil, 881, nil, nil,
- 881, nil, nil, 881, nil, nil, nil, 881, nil, 881,
- nil, nil, nil, nil, nil, nil, nil, 881, nil, nil,
- nil, nil, 881, 881, 881, 881, 881, 881, nil, nil,
- nil, 881, 881, nil, nil, nil, nil, nil, nil, 881,
- nil, nil, 881, nil, nil, nil, nil, 881, 881, 886,
- 886, 886, 886, 886, nil, nil, nil, 886, 886, nil,
- nil, nil, 886, nil, 886, 886, 886, 886, 886, 886,
- 886, nil, nil, nil, nil, nil, 886, 886, 886, 886,
- 886, 886, 886, nil, nil, 886, nil, nil, nil, nil,
- nil, 886, 886, nil, 886, 886, 886, nil, 886, 886,
- 886, 886, 886, nil, 886, 886, 886, nil, 886, 886,
- nil, 886, 886, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 886, nil, nil, 886, nil, nil,
- 886, nil, nil, 886, nil, 886, nil, nil, nil, 886,
- nil, nil, nil, nil, nil, nil, nil, 886, nil, nil,
- nil, nil, 886, 886, 886, 886, 886, 886, nil, nil,
- nil, 886, 886, nil, nil, nil, 890, 890, 890, 886,
- 890, nil, 886, nil, 890, 890, nil, 886, 886, 890,
- nil, 890, 890, 890, 890, 890, 890, 890, nil, nil,
- nil, nil, nil, 890, 890, 890, 890, 890, 890, 890,
- nil, nil, 890, nil, nil, nil, nil, nil, nil, 890,
- nil, nil, 890, 890, nil, 890, 890, 890, 890, 890,
- nil, 890, 890, 890, nil, 890, 890, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 890, nil, nil, 890, nil, nil, 890, nil, nil,
- 890, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 890,
- 890, 890, 890, 890, 890, nil, nil, nil, 890, 890,
- nil, nil, nil, 900, 900, 900, 890, 900, nil, 890,
- nil, 900, 900, nil, 890, 890, 900, nil, 900, 900,
- 900, 900, 900, 900, 900, nil, nil, nil, nil, nil,
- 900, 900, 900, 900, 900, 900, 900, nil, nil, 900,
- nil, nil, nil, nil, nil, nil, 900, nil, nil, 900,
- 900, nil, 900, 900, 900, 900, 900, nil, 900, 900,
- 900, nil, 900, 900, nil, 900, 900, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 900, nil,
- nil, 900, nil, nil, 900, nil, nil, 900, nil, 900,
- nil, nil, nil, 900, nil, nil, nil, nil, nil, nil,
- nil, 900, nil, nil, nil, nil, 900, 900, 900, 900,
- 900, 900, nil, nil, nil, 900, 900, nil, nil, nil,
- nil, nil, nil, 900, nil, nil, 900, nil, nil, nil,
- nil, 900, 900, 901, 901, 901, 901, 901, nil, nil,
- nil, 901, 901, nil, nil, nil, 901, nil, 901, 901,
- 901, 901, 901, 901, 901, nil, nil, nil, nil, nil,
- 901, 901, 901, 901, 901, 901, 901, nil, nil, 901,
- nil, nil, nil, nil, nil, 901, 901, nil, 901, 901,
- 901, nil, 901, 901, 901, 901, 901, nil, 901, 901,
- 901, nil, 901, 901, nil, 901, 901, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 901, nil,
- nil, 901, nil, nil, 901, nil, nil, 901, nil, 901,
- nil, nil, nil, 901, nil, nil, nil, nil, nil, nil,
- nil, 901, nil, nil, nil, nil, 901, 901, 901, 901,
- 901, 901, nil, nil, nil, 901, 901, nil, nil, nil,
- nil, nil, nil, 901, nil, nil, 901, nil, nil, nil,
- nil, 901, 901, 904, 904, 904, 904, 904, nil, nil,
- nil, 904, 904, nil, nil, nil, 904, nil, 904, 904,
- 904, 904, 904, 904, 904, nil, nil, nil, nil, nil,
- 904, 904, 904, 904, 904, 904, 904, nil, nil, 904,
- nil, nil, nil, nil, nil, 904, 904, nil, 904, 904,
- 904, nil, 904, 904, 904, 904, 904, nil, 904, 904,
- 904, nil, 904, 904, nil, 904, 904, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 904, nil,
- nil, 904, nil, nil, 904, nil, nil, 904, nil, 904,
- nil, nil, nil, 904, nil, nil, nil, nil, nil, nil,
- nil, 904, nil, nil, nil, nil, 904, 904, 904, 904,
- 904, 904, nil, nil, nil, 904, 904, nil, nil, nil,
- 921, 921, 921, 904, 921, nil, 904, nil, 921, 921,
- nil, 904, 904, 921, nil, 921, 921, 921, 921, 921,
- 921, 921, nil, nil, nil, nil, nil, 921, 921, 921,
- 921, 921, 921, 921, nil, nil, 921, nil, nil, nil,
- nil, nil, nil, 921, nil, nil, 921, 921, nil, 921,
- 921, 921, 921, 921, nil, 921, 921, 921, nil, 921,
- 921, nil, 921, 921, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 921, nil, nil, 921, nil,
- nil, 921, nil, nil, 921, nil, 921, nil, 921, nil,
- 921, nil, nil, nil, nil, nil, nil, nil, 921, nil,
- nil, nil, nil, 921, 921, 921, 921, 921, 921, nil,
- nil, nil, 921, 921, nil, nil, nil, nil, nil, nil,
- 921, nil, nil, 921, nil, nil, nil, nil, 921, 921,
- 923, 923, 923, 923, 923, nil, nil, nil, 923, 923,
- nil, nil, nil, 923, nil, 923, 923, 923, 923, 923,
- 923, 923, nil, nil, nil, nil, nil, 923, 923, 923,
- 923, 923, 923, 923, nil, nil, 923, nil, nil, nil,
- nil, nil, 923, 923, nil, 923, 923, 923, nil, 923,
- 923, 923, 923, 923, nil, 923, 923, 923, nil, 923,
- 923, nil, 923, 923, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 923, nil, nil, 923, nil,
- nil, 923, nil, nil, 923, nil, 923, nil, nil, nil,
- 923, nil, nil, nil, nil, nil, nil, nil, 923, nil,
- nil, nil, nil, 923, 923, 923, 923, 923, 923, nil,
- nil, nil, 923, 923, nil, nil, nil, nil, nil, nil,
- 923, nil, nil, 923, nil, nil, nil, nil, 923, 923,
- 930, 930, 930, 930, 930, nil, nil, nil, 930, 930,
- nil, nil, nil, 930, nil, 930, 930, 930, 930, 930,
- 930, 930, nil, nil, nil, nil, nil, 930, 930, 930,
- 930, 930, 930, 930, nil, nil, 930, nil, nil, nil,
- nil, nil, 930, 930, nil, 930, 930, 930, nil, 930,
- 930, 930, 930, 930, nil, 930, 930, 930, nil, 930,
- 930, nil, 930, 930, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 930, nil, nil, 930, nil,
- nil, 930, nil, nil, 930, nil, 930, nil, nil, nil,
- 930, nil, nil, nil, nil, nil, nil, nil, 930, nil,
- nil, nil, nil, 930, 930, 930, 930, 930, 930, nil,
- nil, nil, 930, 930, nil, nil, nil, nil, nil, nil,
- 930, nil, nil, 930, nil, nil, nil, nil, 930, 930,
- 935, 935, 935, 935, 935, nil, nil, nil, 935, 935,
- nil, nil, nil, 935, nil, 935, 935, 935, 935, 935,
- 935, 935, nil, nil, nil, nil, nil, 935, 935, 935,
- 935, 935, 935, 935, nil, nil, 935, nil, nil, nil,
- nil, nil, 935, 935, nil, 935, 935, 935, nil, 935,
- 935, 935, 935, 935, nil, 935, 935, 935, nil, 935,
- 935, nil, 935, 935, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 935, nil, nil, 935, nil,
- nil, 935, nil, nil, 935, nil, 935, nil, nil, nil,
- 935, nil, nil, nil, nil, nil, nil, nil, 935, nil,
- nil, nil, nil, 935, 935, 935, 935, 935, 935, nil,
- nil, nil, 935, 935, nil, nil, nil, nil, nil, nil,
- 935, nil, nil, 935, nil, nil, nil, nil, 935, 935,
- 943, 943, 943, 943, 943, nil, nil, nil, 943, 943,
- nil, nil, nil, 943, nil, 943, 943, 943, 943, 943,
- 943, 943, nil, nil, nil, nil, nil, 943, 943, 943,
- 943, 943, 943, 943, nil, nil, 943, nil, nil, nil,
- nil, nil, 943, 943, nil, 943, 943, 943, nil, 943,
- 943, 943, 943, 943, nil, 943, 943, 943, nil, 943,
- 943, nil, 943, 943, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 943, nil, nil, 943, nil,
- nil, 943, nil, nil, 943, nil, 943, nil, nil, nil,
- 943, nil, nil, nil, nil, nil, nil, nil, 943, nil,
- nil, nil, nil, 943, 943, 943, 943, 943, 943, nil,
- nil, nil, 943, 943, nil, nil, nil, 945, 945, 945,
- 943, 945, nil, 943, nil, 945, 945, nil, 943, 943,
- 945, nil, 945, 945, 945, 945, 945, 945, 945, nil,
- nil, nil, nil, nil, 945, 945, 945, 945, 945, 945,
- 945, nil, nil, 945, nil, nil, nil, nil, nil, nil,
- 945, nil, nil, 945, 945, nil, 945, 945, 945, 945,
- 945, 945, 945, 945, 945, nil, 945, 945, nil, 945,
- 945, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 945, nil, nil, 945, nil, nil, 945, nil,
- nil, 945, nil, 945, nil, nil, nil, 945, nil, nil,
- nil, nil, nil, nil, nil, 945, nil, nil, nil, 436,
- 945, 945, 945, 945, 945, 945, 436, 436, 436, 945,
- 945, nil, 436, 436, nil, 436, nil, 945, nil, nil,
- 945, nil, nil, nil, 436, 945, 945, nil, nil, nil,
- nil, nil, nil, nil, nil, 436, 436, nil, 436, 436,
- 436, 436, 436, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 436, 436, 436, 436,
- 436, 436, 436, 436, 436, 436, 436, 436, 436, 436,
- 436, 438, nil, 436, 436, 436, nil, 436, 438, 438,
- 438, 436, nil, nil, 438, 438, nil, 438, 436, nil,
- 436, nil, 436, 436, 436, 436, 436, 436, 436, nil,
- 436, 436, 436, nil, nil, nil, nil, 438, 438, nil,
- 438, 438, 438, 438, 438, 436, 436, nil, 436, nil,
- 436, nil, nil, 436, nil, 436, nil, 436, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 438, 438,
- 438, 438, 438, 438, 438, 438, 438, 438, 438, 438,
- 438, 438, 438, nil, nil, 438, 438, 438, nil, 438,
- nil, nil, nil, 438, nil, nil, nil, nil, nil, nil,
- 438, nil, 438, nil, 438, 438, 438, 438, 438, 438,
- 438, nil, 438, nil, 438, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 438, 438, nil,
- 438, nil, 438, 644, nil, 438, nil, 438, nil, 438,
- 644, 644, 644, nil, nil, 644, 644, 644, nil, 644,
- nil, nil, nil, nil, nil, nil, nil, nil, 644, 644,
- 644, nil, nil, nil, nil, nil, nil, nil, nil, 644,
- 644, nil, 644, 644, 644, 644, 644, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 644, 644, 644, 644, 644, 644, 644, 644, 644, 644,
- 644, 644, 644, 644, 644, nil, nil, 644, 644, 644,
- nil, 644, 644, nil, nil, 644, nil, nil, 644, nil,
- 644, nil, 644, nil, 644, nil, 644, 644, 644, 644,
- 644, 644, 644, nil, 644, 644, 644, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 644,
- 644, 644, 644, nil, 644, 645, nil, 644, nil, 644,
- nil, 644, 645, 645, 645, nil, nil, 645, 645, 645,
- nil, 645, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 645, 645, nil, nil, nil, nil, nil, nil, nil,
- nil, 645, 645, nil, 645, 645, 645, 645, 645, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 645, 645, 645, 645, 645, 645, 645, 645,
- 645, 645, 645, 645, 645, 645, 645, nil, nil, 645,
- 645, 645, nil, 645, 645, nil, nil, 645, nil, nil,
- 645, nil, 645, nil, 645, nil, 645, nil, 645, 645,
- 645, 645, 645, 645, 645, nil, 645, nil, 645, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 645, 645, 645, 645, nil, 645, 28, nil, 645,
- nil, 645, nil, 645, 28, 28, 28, nil, nil, 28,
- 28, 28, nil, 28, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 28, 28, nil, nil, nil, nil, nil,
- nil, nil, nil, 28, 28, nil, 28, 28, 28, 28,
- 28, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 28, 28, 28, 28, 28, 28,
- 28, 28, 28, 28, 28, 28, 28, 28, 28, nil,
- nil, 28, 28, 28, nil, nil, 28, nil, 28, 28,
- nil, nil, 28, nil, 28, nil, 28, nil, 28, nil,
- 28, 28, 28, 28, 28, 28, 28, nil, 28, nil,
- 28, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 28, 28, 28, 28, 50, 28, nil,
- nil, 28, nil, 28, 50, 50, 50, nil, nil, 50,
- 50, 50, nil, 50, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 50, 50, 50, nil, nil, nil, nil,
- nil, nil, nil, 50, 50, nil, 50, 50, 50, 50,
- 50, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 50, 50, 50, 50, 50, 50,
- 50, 50, 50, 50, 50, 50, 50, 50, 50, nil,
- nil, 50, 50, 50, nil, nil, 50, nil, nil, 50,
- nil, nil, 50, nil, 50, nil, 50, nil, 50, nil,
- 50, 50, 50, 50, 50, 50, 50, nil, 50, nil,
- 50, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 50, 50, 50, 50, 494, 50, nil,
- 50, 50, nil, 50, 494, 494, 494, nil, nil, 494,
- 494, 494, nil, 494, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 494, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 494, 494, nil, 494, 494, 494, 494,
- 494, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 495, nil, nil, nil, nil, nil,
- nil, 495, 495, 495, nil, nil, 495, 495, 495, nil,
- 495, nil, nil, nil, nil, 494, 494, nil, nil, nil,
- 495, nil, nil, 494, nil, nil, nil, nil, 494, 494,
- 495, 495, nil, 495, 495, 495, 495, 495, nil, nil,
- nil, nil, 918, nil, 918, 918, 918, 918, 918, nil,
- 494, nil, nil, nil, nil, nil, nil, nil, 918, nil,
- nil, nil, 496, 494, nil, 494, nil, nil, 494, 496,
- 496, 496, 495, 495, 496, 496, 496, nil, 496, nil,
- 495, 918, 918, nil, nil, 495, 495, nil, 496, 918,
- 918, 918, 918, nil, nil, nil, nil, nil, 496, 496,
- nil, 496, 496, 496, 496, 496, nil, 495, nil, nil,
- nil, nil, nil, 202, 202, nil, nil, 202, nil, nil,
- 495, nil, 495, nil, nil, 495, 202, 202, 918, 202,
- 202, 202, 202, 202, 202, 202, nil, nil, 202, 202,
- 496, 496, nil, nil, 202, 202, 202, 202, 496, nil,
- nil, nil, nil, 496, 496, nil, nil, nil, nil, nil,
- 202, 202, nil, 202, 202, 202, 202, 202, 202, 202,
- 202, 202, 202, 202, nil, 496, 202, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 496, nil,
- 496, nil, nil, 496, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, nil, nil,
- nil, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, nil, 7, 7, 7, nil, 7, nil, nil,
- nil, nil, nil, nil, nil, nil, 7, 7, nil, 7,
- 7, 7, 7, 7, 7, 7, nil, nil, 7, 7,
- nil, nil, nil, nil, 7, 7, 7, 7, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 7, 7, nil, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, nil, nil, 7, 7, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 7, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, nil, nil, nil, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, nil, 8, 8, nil,
- nil, 8, nil, nil, nil, nil, nil, nil, nil, nil,
- 8, 8, nil, 8, 8, 8, 8, 8, 8, 8,
- nil, nil, 8, 8, nil, nil, nil, nil, 8, 8,
- 8, 8, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 8, 8, nil, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, nil, nil,
- 8, 8, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 8, 411, 411, 411, 411, 411, 411, 411, 411,
- 411, 411, 411, 411, 411, 411, 411, 411, 411, 411,
- 411, 411, 411, 411, 411, 411, nil, nil, nil, 411,
- 411, 411, 411, 411, 411, 411, 411, 411, 411, 411,
- 411, 411, 411, 411, 411, 411, 411, 411, 411, 411,
- nil, 411, 411, nil, nil, 411, nil, nil, nil, nil,
- nil, nil, nil, nil, 411, 411, nil, 411, 411, 411,
- 411, 411, 411, 411, nil, nil, 411, 411, nil, nil,
- nil, nil, 411, 411, 411, 411, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 411, 411,
- nil, 411, 411, 411, 411, 411, 411, 411, 411, 411,
- 411, 411, nil, nil, 411, 411, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 411, 415, 415, 415, 415,
- 415, 415, 415, 415, 415, 415, 415, 415, 415, 415,
- 415, 415, 415, 415, 415, 415, 415, 415, 415, 415,
- nil, nil, nil, 415, 415, 415, 415, 415, 415, 415,
- 415, 415, 415, 415, 415, 415, 415, 415, 415, 415,
- 415, 415, 415, 415, nil, 415, 415, nil, nil, 415,
- nil, nil, nil, nil, nil, nil, nil, nil, 415, 415,
- nil, 415, 415, 415, 415, 415, 415, 415, nil, nil,
- 415, 415, nil, nil, nil, nil, 415, 415, 415, 415,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 415, 415, nil, 415, 415, 415, 415, 415,
- 415, 415, 415, 415, 415, 415, nil, nil, 415, 415,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 415,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 65, nil, nil, nil, 65, 65, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, nil, 65,
- 65, 65, 65, 65, nil, 65, nil, nil, nil, nil,
- nil, nil, 65, 65, nil, 65, 65, 65, 65, 65,
- 65, 65, nil, nil, 65, 65, nil, nil, nil, nil,
- 65, 65, 65, 65, nil, nil, nil, nil, nil, 65,
- nil, nil, nil, nil, nil, nil, 65, 65, nil, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- nil, nil, 65, 79, 79, 79, 79, 79, 79, 79,
- 79, 79, 79, 79, 79, 79, 79, 79, 79, 79,
- 79, 79, 79, 79, 79, 79, 79, nil, nil, nil,
- 79, 79, 79, 79, 79, 79, 79, 79, 79, 79,
- 79, 79, 79, 79, 79, 79, 79, 79, 79, 79,
- 79, nil, 79, 79, 79, 79, 79, nil, 79, nil,
- nil, nil, nil, nil, nil, 79, 79, nil, 79, 79,
- 79, 79, 79, 79, 79, nil, nil, 79, 79, nil,
- nil, nil, nil, 79, 79, 79, 79, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 79,
- 79, nil, 79, 79, 79, 79, 79, 79, 79, 79,
- 79, 79, 79, nil, nil, 79, 192, 192, 192, 192,
- 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
- 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
- nil, nil, nil, 192, 192, 192, 192, 192, 192, 192,
- 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
- 192, 192, 192, 192, nil, 192, 192, 192, 192, 192,
- nil, 192, nil, nil, nil, nil, nil, nil, 192, 192,
- nil, 192, 192, 192, 192, 192, 192, 192, nil, nil,
- 192, 192, nil, nil, nil, nil, 192, 192, 192, 192,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 192, 192, nil, 192, 192, 192, 192, 192,
- 192, 192, 192, 192, 192, 192, nil, nil, 192, 766,
- 766, 766, 766, 766, 766, 766, 766, 766, 766, 766,
- 766, 766, 766, 766, 766, 766, 766, 766, 766, 766,
- 766, 766, 766, nil, nil, nil, 766, 766, 766, 766,
- 766, 766, 766, 766, 766, 766, 766, 766, 766, 766,
- 766, 766, 766, 766, 766, 766, 766, nil, 766, 766,
- nil, nil, 766, nil, nil, nil, nil, nil, nil, nil,
- nil, 766, 766, nil, 766, 766, 766, 766, 766, 766,
- 766, nil, nil, 766, 766, nil, nil, nil, nil, 766,
- 766, 766, 766, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 766, 766, nil, 766, 766,
- 766, 766, 766, 766, 766, 766, 766, 766, 766, 201,
- 201, 766, nil, 201, nil, nil, nil, nil, nil, nil,
- nil, nil, 201, 201, nil, 201, 201, 201, 201, 201,
- 201, 201, nil, nil, 201, 201, nil, nil, nil, nil,
- 201, 201, 201, 201, nil, nil, nil, nil, nil, 201,
- nil, nil, nil, nil, nil, nil, 201, 201, nil, 201,
- 201, 201, 201, 201, 201, 201, 201, 201, 201, 201,
- 203, 203, 201, nil, 203, nil, nil, nil, nil, nil,
- nil, nil, nil, 203, 203, nil, 203, 203, 203, 203,
- 203, 203, 203, nil, nil, 203, 203, nil, nil, nil,
- nil, 203, 203, 203, 203, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 203, 203, nil,
- 203, 203, 203, 203, 203, 203, 203, 203, 203, 203,
- 203, 264, 264, 203, nil, 264, nil, nil, nil, nil,
- nil, nil, nil, nil, 264, 264, nil, 264, 264, 264,
- 264, 264, 264, 264, nil, nil, 264, 264, nil, nil,
- nil, nil, 264, 264, 264, 264, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 264, 264,
- nil, 264, 264, 264, 264, 264, 264, 264, 264, 264,
- 264, 264, 265, 265, 264, nil, 265, nil, nil, nil,
- nil, nil, nil, nil, nil, 265, 265, nil, 265, 265,
- 265, 265, 265, 265, 265, nil, nil, 265, 265, nil,
- nil, nil, nil, 265, 265, 265, 265, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 265,
- 265, nil, 265, 265, 265, 265, 265, 265, 265, 265,
- 265, 265, 265, 266, 266, 265, nil, 266, nil, nil,
- nil, nil, nil, nil, nil, nil, 266, 266, nil, 266,
- 266, 266, 266, 266, 266, 266, nil, nil, 266, 266,
- nil, nil, nil, nil, 266, 266, 266, 266, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 266, 266, nil, 266, 266, 266, 266, 266, 266, 266,
- 266, 266, 266, 266, 449, 449, 266, nil, 449, nil,
- nil, nil, nil, nil, nil, nil, nil, 449, 449, nil,
- 449, 449, 449, 449, 449, 449, 449, nil, nil, 449,
- 449, nil, nil, nil, nil, 449, 449, 449, 449, nil,
- nil, nil, nil, nil, 449, nil, nil, nil, nil, nil,
- nil, 449, 449, nil, 449, 449, 449, 449, 449, 449,
- 449, 449, 449, 449, 449, 450, 450, 449, nil, 450,
- nil, nil, nil, nil, nil, nil, nil, nil, 450, 450,
- nil, 450, 450, 450, 450, 450, 450, 450, nil, nil,
- 450, 450, nil, nil, nil, nil, 450, 450, 450, 450,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 450, 450, nil, 450, 450, 450, 450, 450,
- 450, 450, 450, 450, 450, 450, 521, 521, 450, nil,
- 521, nil, nil, nil, nil, nil, nil, nil, nil, 521,
- 521, nil, 521, 521, 521, 521, 521, 521, 521, nil,
- nil, 521, 521, nil, nil, nil, nil, 521, 521, 521,
- 521, nil, nil, nil, nil, nil, 521, nil, nil, nil,
- nil, nil, nil, 521, 521, nil, 521, 521, 521, 521,
- 521, 521, 521, 521, 521, 521, 521, 522, 522, 521,
- nil, 522, nil, nil, nil, nil, nil, nil, nil, nil,
- 522, 522, nil, 522, 522, 522, 522, 522, 522, 522,
- nil, nil, 522, 522, nil, nil, nil, nil, 522, 522,
- 522, 522, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 522, 522, nil, 522, 522, 522,
- 522, 522, 522, 522, 522, 522, 522, 522, 524, 524,
- 522, nil, 524, nil, nil, nil, nil, nil, nil, nil,
- nil, 524, 524, nil, 524, 524, 524, 524, 524, 524,
- 524, nil, nil, 524, 524, nil, nil, nil, nil, 524,
- 524, 524, 524, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 524, 524, nil, 524, 524,
- 524, 524, 524, 524, 524, 524, 524, 524, 524, 534,
- 534, 524, nil, 534, nil, nil, nil, nil, nil, nil,
- nil, nil, 534, 534, nil, 534, 534, 534, 534, 534,
- 534, 534, nil, nil, 534, 534, nil, nil, nil, nil,
- 534, 534, 534, 534, nil, nil, nil, nil, nil, 534,
- nil, nil, nil, nil, nil, nil, 534, 534, nil, 534,
- 534, 534, 534, 534, 534, 534, 534, 534, 534, 534,
- 535, 535, 534, nil, 535, nil, nil, nil, nil, nil,
- nil, nil, nil, 535, 535, nil, 535, 535, 535, 535,
- 535, 535, 535, nil, nil, 535, 535, nil, nil, nil,
- nil, 535, 535, 535, 535, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 535, 535, nil,
- 535, 535, 535, 535, 535, 535, 535, 535, 535, 535,
- 535, 584, 584, 535, nil, 584, nil, nil, nil, nil,
- nil, nil, nil, nil, 584, 584, nil, 584, 584, 584,
- 584, 584, 584, 584, nil, nil, 584, 584, nil, nil,
- nil, nil, 584, 584, 584, 584, nil, nil, nil, nil,
- nil, 584, nil, nil, nil, nil, nil, nil, 584, 584,
- nil, 584, 584, 584, 584, 584, 584, 584, 584, 584,
- 584, 584, 585, 585, 584, nil, 585, nil, nil, nil,
- nil, nil, nil, nil, nil, 585, 585, nil, 585, 585,
- 585, 585, 585, 585, 585, nil, nil, 585, 585, nil,
- nil, nil, nil, 585, 585, 585, 585, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 585,
- 585, nil, 585, 585, 585, 585, 585, 585, 585, 585,
- 585, 585, 585, 591, 591, 585, nil, 591, nil, nil,
- nil, nil, nil, nil, nil, nil, 591, 591, nil, 591,
- 591, 591, 591, 591, 591, 591, nil, nil, 591, 591,
- nil, nil, nil, nil, 591, 591, 591, 591, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 591, 591, nil, 591, 591, 591, 591, 591, 591, 591,
- 591, 591, 591, 591, 593, 593, 591, nil, 593, nil,
- nil, nil, nil, nil, nil, nil, nil, 593, 593, nil,
- 593, 593, 593, 593, 593, 593, 593, nil, nil, 593,
- 593, nil, nil, nil, nil, 593, 593, 593, 593, nil,
- nil, nil, nil, nil, 593, nil, nil, nil, nil, nil,
- nil, 593, 593, nil, 593, 593, 593, 593, 593, 593,
- 593, 593, 593, 593, 593, 822, 822, 593, nil, 822,
- nil, nil, nil, nil, nil, nil, nil, nil, 822, 822,
- nil, 822, 822, 822, 822, 822, 822, 822, nil, nil,
- 822, 822, nil, nil, nil, nil, 822, 822, 822, 822,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 822, 822, nil, 822, 822, 822, 822, 822,
- 822, 822, 822, 822, 822, 822, 946, 946, 822, nil,
- 946, nil, nil, nil, nil, nil, nil, nil, nil, 946,
- 946, nil, 946, 946, 946, 946, 946, 946, 946, nil,
- nil, 946, 946, nil, nil, nil, nil, 946, 946, 946,
- 946, nil, nil, nil, nil, nil, 946, nil, nil, nil,
- nil, nil, nil, 946, 946, nil, 946, 946, 946, 946,
- 946, 946, 946, 946, 946, 946, 946, 947, 947, 946,
- nil, 947, nil, nil, nil, nil, nil, nil, nil, nil,
- 947, 947, nil, 947, 947, 947, 947, 947, 947, 947,
- nil, nil, 947, 947, nil, nil, nil, nil, 947, 947,
- 947, 947, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 947, 947, nil, 947, 947, 947,
- 947, 947, 947, 947, 947, 947, 947, 947, nil, nil,
- 947 ]
-
-racc_action_pointer = [
- -2, 34, nil, 234, nil, 618, -19, 22922, 23046, -5,
- -1, 15, 117, 414, 291, 243, nil, 125, 252, 900,
- 186, nil, 379, 506, 633, 381, 13, 760, 22407, nil,
- 900, 1027, 1154, nil, 108, 541, 237, 261, 1294, 1421,
- 1548, 176, 467, nil, nil, nil, nil, nil, nil, nil,
- 22537, nil, 1675, 1802, 1929, 29, 9932, 2056, 2183, nil,
- nil, 2310, 2450, 2577, nil, 23418, nil, nil, nil, nil,
- nil, -102, nil, nil, nil, nil, nil, 178, 220, 23531,
- nil, nil, nil, 429, 2704, nil, nil, 2831, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 359, nil, 2971,
- nil, nil, nil, 3111, 3238, 3365, 3492, 3632, 3772, nil,
- 663, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 23644, 234, nil, 3912, 4039, 4166, 4293, 4420,
- 4547, 23818, 22792, 23879, 4674, 4801, 4928, nil, 541, -49,
- 319, 61, 240, 328, 5068, 5195, nil, nil, 5322, 363,
- 5449, 5576, 5703, 5830, 5957, 6084, 6211, 6338, 6465, 6592,
- 6719, 6846, 6973, 7100, 7227, 7354, 7481, 7608, 7735, 7862,
- 7989, 8116, 8243, 8370, 8497, 8624, nil, nil, nil, 1294,
- nil, 329, 339, nil, 8751, 389, 8878, nil, nil, nil,
- nil, 9005, nil, nil, 23940, 24001, 24062, 383, 9132, 9259,
- nil, nil, nil, nil, nil, nil, nil, 9386, 249, 745,
- 390, 9526, 409, 438, 405, 9653, 9793, 73, 594, 497,
- 196, 465, 440, 207, nil, 478, 471, nil, 9920, nil,
- 586, 505, 531, 633, nil, 533, nil, 10047, nil, 10187,
- 35, nil, 502, -103, 135, 537, 521, 262, 556, nil,
- nil, -22, 10580, nil, nil, nil, 520, 545, nil, 564,
- 567, nil, nil, nil, nil, nil, nil, nil, 3097, nil,
- nil, nil, 648, nil, nil, 674, 806, -7, 36, 10314,
- 10441, 324, 63, 596, -17, 668, 690, 37, 731, nil,
- nil, 506, 704, nil, 721, nil, 65, nil, nil, 10568,
- -12, 122, 360, 384, 385, 436, 509, 590, nil, 795,
- nil, 10695, nil, 173, nil, 326, nil, -23, 649, 362,
- nil, 653, -50, nil, 365, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 683, 23170, nil, nil, nil, 23294, 688, nil, nil, 760,
- nil, 10822, 681, nil, 691, nil, nil, 1421, 728, 731,
- 668, 761, 10949, nil, nil, nil, 21929, 757, 22011, nil,
- 11076, 11203, 11330, nil, nil, nil, 1548, nil, nil, 24123,
- 24184, 11457, 11584, 180, 11711, 11838, 11965, 115, nil, 3238,
- 3365, 232, 879, 785, 787, 821, 824, 2457, 2584, 5068,
- 3492, 3632, 3772, 3912, 4039, 4166, 4293, 4420, 4547, 4674,
- 1133, 1232, 4801, 4928, 633, -34, nil, 12092, nil, 12219,
- nil, 12346, nil, nil, 22667, 22724, 22792, -17, nil, 771,
- nil, nil, 785, 787, nil, nil, 12473, 59, 203, 832,
- nil, nil, 12613, 833, 797, nil, nil, 799, 12753, 845,
- 12880, 24245, 24306, 13007, 24367, 223, 848, nil, nil, 821,
- nil, 13134, 13261, 13388, 24428, 24489, 1675, 13515, 949, 951,
- 871, nil, nil, 13642, nil, nil, 13769, nil, nil, nil,
- nil, 13909, 14049, 874, nil, 1000, nil, nil, 14176, 12765,
- nil, 772, nil, nil, 894, nil, 3126, nil, 859, 1198,
- nil, nil, 14316, 977, nil, nil, 14456, 212, 227, 975,
- 983, 14596, nil, 14723, 24550, 24611, 14850, 40, nil, 760,
- nil, 24672, 14977, 24733, nil, nil, 15104, 387, 15231, nil,
- 1166, nil, nil, nil, 45, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, -35, nil, nil, nil, 864,
- nil, nil, nil, nil, nil, 15358, 866, 15485, 798, 203,
- 15612, 15739, 900, nil, nil, nil, 15866, 903, nil, 15993,
- 915, nil, 268, 307, 22143, 22275, 927, 928, 506, nil,
- 16120, nil, 1040, nil, 16260, 897, nil, 939, 16387, nil,
- nil, nil, nil, nil, nil, nil, nil, 16527, nil, 942,
- 16654, 16781, 2310, 907, nil, nil, 947, 16921, 17061, nil,
- 1154, -27, 17188, 914, nil, 957, 228, 235, 962, 313,
- 327, 963, 960, 972, 17315, 1802, 997, 1001, 240, 1056,
- 17442, nil, nil, 376, 960, 1068, nil, nil, 945, nil,
- 958, 935, 1035, 963, 967, nil, nil, 1010, 3135, nil,
- 869, nil, 1096, nil, nil, nil, nil, nil, 1102, nil,
- 1103, 17569, 1020, 46, 52, 59, 102, 1021, 17696, 1027,
- nil, 1028, 1026, 17836, 416, nil, -25, 17976, 18116, 9665,
- 463, nil, nil, 1075, nil, 18243, nil, 998, 999, nil,
- 1000, 1002, 1004, nil, 996, nil, 23757, 1043, 1050, 18383,
- nil, nil, nil, 1929, 1007, 18510, 18637, 18764, nil, 2056,
- nil, 2183, nil, nil, 2446, nil, 2573, nil, 18891, 19018,
- 19145, 315, 319, 2704, nil, 1041, 1144, nil, nil, 1041,
- nil, 1026, 19272, nil, 1053, 1161, 1072, 323, nil, nil,
- nil, 1196, nil, 19412, 1081, 1125, nil, nil, nil, nil,
- nil, nil, 24794, nil, 1127, nil, nil, nil, nil, 1407,
- 1215, nil, 19539, 1220, 19679, 19819, nil, nil, 66, 56,
- 988, 295, nil, 1221, nil, nil, 1222, 1225, 1109, nil,
- nil, nil, -32, nil, nil, 807, 14188, nil, 842, nil,
- 406, nil, 19946, nil, nil, nil, nil, nil, nil, nil,
- 1127, 1113, nil, 2831, nil, 2971, 20073, 20200, nil, nil,
- nil, 20327, 1114, nil, nil, nil, 20467, nil, nil, 72,
- 20594, nil, 1154, 1125, nil, nil, 78, nil, 1249, 1250,
- 20721, 20861, nil, nil, 21001, nil, nil, 1168, nil, 1132,
- nil, nil, 1133, 1134, 1139, 1137, nil, nil, 22721, nil,
- nil, 21128, nil, 21268, 91, 1112, 1221, 97, nil, nil,
- 21408, nil, nil, nil, 460, 21548, 1268, nil, nil, 1081,
- nil, nil, nil, 21688, 1273, 21815, 24855, 24916, 98, 899,
- nil, nil, nil, 1272, nil, 1153, 1275, nil, 1190, 108,
- 120, 198, 202, nil, nil, nil, nil, 218 ]
-
-racc_action_default = [
- -3, -555, -1, -543, -4, -6, -555, -555, -555, -555,
- -555, -555, -555, -555, -277, -37, -38, -555, -555, -43,
- -45, -46, -289, -327, -328, -50, -255, -382, -255, -65,
- -10, -69, -76, -78, -555, -457, -555, -555, -555, -555,
- -555, -545, -232, -270, -271, -272, -273, -274, -275, -276,
- -533, -279, -555, -554, -525, -297, -554, -555, -555, -302,
- -305, -543, -555, -555, -319, -555, -329, -330, -400, -401,
- -402, -403, -404, -554, -407, -554, -554, -554, -554, -554,
- -434, -440, -441, -555, -446, -447, -448, -449, -450, -451,
- -452, -453, -454, -455, -456, -459, -460, -555, -2, -544,
- -550, -551, -552, -555, -555, -555, -555, -555, -3, -13,
- -555, -105, -106, -107, -108, -109, -110, -111, -114, -115,
- -116, -117, -118, -119, -120, -121, -122, -123, -124, -125,
- -126, -127, -128, -129, -130, -131, -132, -133, -134, -135,
- -136, -137, -138, -139, -140, -141, -142, -143, -144, -145,
- -146, -147, -148, -149, -150, -151, -152, -153, -154, -155,
- -156, -157, -158, -159, -160, -161, -162, -163, -164, -165,
- -166, -167, -168, -169, -170, -171, -172, -173, -174, -175,
- -176, -177, -178, -179, -180, -181, -182, -183, -184, -185,
- -186, -187, -555, -18, -112, -10, -555, -555, -555, -554,
- -554, -555, -555, -555, -555, -555, -555, -41, -555, -457,
- -555, -277, -555, -555, -10, -555, -42, -224, -555, -555,
- -555, -555, -555, -555, -555, -555, -555, -555, -555, -555,
- -555, -555, -555, -555, -555, -555, -555, -555, -555, -555,
- -555, -555, -555, -555, -555, -555, -369, -371, -47, -233,
- -248, -262, -262, -252, -555, -263, -555, -289, -327, -328,
- -527, -555, -48, -49, -555, -555, -555, -55, -554, -555,
- -296, -375, -383, -385, -63, -381, -64, -555, -543, -11,
- -65, -10, -555, -555, -70, -73, -10, -457, -555, -555,
- -277, -292, -545, -555, -331, -382, -555, -75, -555, -80,
- -284, -442, -443, -555, -209, -210, -225, -555, -546, -10,
- -545, -234, -545, -547, -547, -555, -555, -547, -555, -298,
- -299, -555, -555, -342, -343, -350, -554, -491, -357, -554,
- -554, -368, -490, -492, -493, -494, -495, -496, -555, -509,
- -514, -515, -517, -518, -519, -555, -44, -555, -555, -555,
- -555, -543, -555, -544, -457, -555, -555, -277, -555, -498,
- -499, -101, -555, -103, -555, -277, -555, -316, -457, -555,
- -105, -106, -143, -144, -160, -165, -172, -175, -322, -555,
- -523, -555, -405, -555, -420, -555, -422, -555, -555, -555,
- -412, -555, -555, -418, -555, -433, -435, -436, -437, -438,
- -444, -445, 968, -5, -553, -19, -20, -21, -22, -23,
- -555, -555, -15, -16, -17, -555, -555, -25, -34, -188,
- -263, -555, -555, -26, -35, -36, -27, -190, -555, -555,
- -534, -535, -554, -378, -536, -537, -534, -255, -535, -380,
- -539, -540, -554, -534, -535, -33, -198, -39, -40, -555,
- -555, -554, -554, -284, -555, -555, -555, -555, -295, -199,
- -200, -201, -202, -203, -204, -205, -206, -211, -212, -213,
- -214, -215, -216, -217, -218, -219, -220, -221, -222, -223,
- -226, -227, -228, -229, -555, -554, -249, -555, -250, -555,
- -260, -555, -264, -530, -255, -255, -255, -554, -56, -545,
- -243, -244, -262, -262, -256, -257, -555, -554, -554, -555,
- -291, -9, -544, -555, -66, -282, -81, -71, -555, -555,
- -554, -555, -555, -554, -555, -284, -555, -442, -443, -77,
- -82, -555, -555, -555, -555, -555, -230, -555, -392, -555,
- -555, -235, -236, -549, -548, -238, -549, -287, -288, -526,
- -339, -10, -10, -555, -341, -555, -359, -366, -555, -363,
- -364, -555, -367, -491, -555, -500, -555, -502, -504, -508,
- -516, -520, -10, -332, -333, -334, -10, -555, -555, -555,
- -555, -10, -387, -554, -555, -555, -554, -284, -311, -101,
- -102, -555, -554, -555, -314, -461, -555, -555, -555, -320,
- -489, -324, -541, -542, -545, -406, -421, -424, -425, -427,
- -408, -423, -409, -410, -411, -555, -414, -416, -417, -555,
- -439, -7, -14, -113, -24, -555, -269, -555, -285, -286,
- -555, -555, -59, -241, -242, -376, -555, -61, -379, -555,
- -57, -377, -534, -535, -534, -535, -555, -555, -188, -294,
- -555, -353, -555, -355, -10, -262, -261, -265, -555, -528,
- -529, -51, -372, -52, -373, -53, -374, -10, -239, -555,
- -245, -247, -43, -555, -254, -258, -555, -10, -10, -290,
- -12, -66, -555, -74, -79, -555, -534, -535, -554, -538,
- -283, -555, -555, -554, -555, -197, -207, -208, -555, -554,
- -554, -280, -281, -547, -555, -555, -340, -351, -555, -358,
- -554, -352, -555, -554, -554, -510, -497, -555, -555, -507,
- -554, -335, -554, -303, -336, -337, -338, -306, -555, -309,
- -555, -555, -555, -534, -535, -538, -283, -555, -555, -101,
- -104, -538, -555, -10, -555, -463, -555, -10, -10, -489,
- -555, -466, -467, -469, -470, -472, -473, -522, -522, -478,
- -480, -480, -480, -488, -491, -512, -555, -555, -555, -10,
- -413, -415, -419, -189, -267, -555, -555, -555, -30, -193,
- -31, -194, -60, -32, -195, -62, -196, -58, -555, -555,
- -555, -286, -285, -231, -354, -555, -555, -251, -266, -555,
- -240, -262, -555, -259, -555, -555, -72, -285, -286, -83,
- -293, -554, -348, -10, -393, -554, -394, -395, -237, -344,
- -345, -365, -555, -284, -555, -361, -362, -501, -503, -506,
- -555, -346, -555, -555, -10, -10, -308, -310, -555, -285,
- -93, -555, -285, -555, -462, -317, -555, -555, -545, -465,
- -468, -471, -555, -476, -477, -555, -555, -484, -555, -486,
- -555, -487, -555, -325, -524, -426, -429, -430, -431, -432,
- -555, -268, -28, -191, -29, -192, -555, -555, -356, -370,
- -54, -246, -262, -384, -386, -8, -10, -399, -349, -555,
- -555, -397, -283, -554, -505, -300, -555, -301, -555, -555,
- -555, -10, -312, -315, -10, -321, -323, -555, -474, -522,
- -521, -479, -480, -480, -480, -555, -513, -511, -489, -428,
- -253, -555, -398, -10, -457, -555, -555, -277, -396, -360,
- -10, -304, -307, -265, -554, -10, -555, -464, -475, -555,
- -482, -483, -485, -10, -392, -554, -555, -555, -284, -554,
- -388, -389, -390, -555, -318, -480, -555, -391, -555, -534,
- -535, -538, -283, -347, -313, -481, -326, -285 ]
-
-racc_goto_table = [
- 13, 315, 307, 699, 323, 378, 498, 114, 114, 539,
- 250, 250, 250, 432, 437, 442, 5, 208, 208, 396,
- 284, 488, 208, 208, 208, 659, 331, 102, 294, 294,
- 13, 288, 288, 572, 576, 529, 10, 98, 12, 557,
- 748, 312, 560, 562, 251, 251, 251, 366, 565, 109,
- 194, 580, 208, 208, 117, 117, 216, 208, 208, 294,
- 294, 208, 355, 364, 99, 114, 10, 715, 12, 418,
- 425, 760, 707, 267, 274, 276, 494, 495, 496, 114,
- 542, 545, 659, 2, 549, 102, 280, 297, 252, 252,
- 252, 606, 763, 723, 727, 352, 759, 616, 359, 13,
- 1, 916, 590, 208, 208, 208, 208, 13, 13, 347,
- 348, 403, 282, 351, 641, 5, 564, 710, 385, 387,
- 317, 714, 394, 409, 5, 885, 830, 248, 262, 263,
- 193, 360, 396, 411, 499, 10, 668, 12, 676, 504,
- 207, 654, 811, 10, 10, 12, 12, 428, 429, 853,
- 854, 380, 316, 667, 319, 405, 406, 407, 408, 320,
- 358, 594, 762, 677, 678, 833, 662, 664, 666, 379,
- 601, 309, 349, 834, 350, 310, 835, 738, 935, 743,
- 346, 346, 916, 598, 346, 904, 369, 312, 312, 747,
- 600, 410, 114, 766, 918, 13, 208, 208, 208, 208,
- 208, 321, 441, 550, 208, 208, 208, 659, 713, 247,
- 485, 656, 507, 656, 13, 208, 508, 950, 422, 422,
- 760, 731, 815, 889, 382, 383, 346, 346, 346, 346,
- 674, 10, 389, 12, 417, 423, 426, 615, 392, 865,
- 768, 763, 445, 769, 848, 759, 909, 908, 912, 828,
- 10, nil, 12, 526, nil, 683, 250, 250, nil, nil,
- nil, 432, 437, nil, nil, 250, nil, nil, 208, 208,
- 553, 540, 488, 541, nil, nil, 565, 208, 728, 719,
- nil, 13, 294, nil, nil, 288, 13, 530, nil, nil,
- 502, 251, 331, nil, nil, 294, nil, nil, 288, 251,
- nil, 938, 102, nil, nil, nil, nil, nil, nil, 13,
- nil, 762, 511, nil, nil, nil, nil, 10, nil, 12,
- nil, nil, 10, 771, 12, nil, 271, 275, 447, 448,
- 14, 740, nil, 688, 503, 252, 693, 280, 457, 512,
- 517, 707, 280, 252, 717, 10, 688, 12, 910, 208,
- 208, 910, 759, 599, 759, 963, 759, 902, 943, nil,
- 14, 290, 290, 513, 715, 641, 294, nil, 519, 364,
- nil, 782, nil, 501, 505, 102, 785, 951, nil, 787,
- nil, 208, 509, nil, nil, 579, 893, nil, nil, 760,
- 595, nil, 357, 365, 656, 656, 688, nil, nil, 646,
- 647, 577, 578, nil, nil, 688, nil, nil, nil, 957,
- 763, 114, nil, nil, 759, 114, nil, 659, 913, 806,
- 914, nil, nil, 821, 797, nil, 825, 826, 565, 14,
- nil, nil, nil, nil, nil, 759, nil, 14, 14, 312,
- 312, nil, nil, 843, nil, nil, nil, 846, 847, nil,
- 441, 208, 208, 622, nil, nil, nil, 623, 117, nil,
- 669, nil, 117, nil, nil, nil, nil, nil, 685, nil,
- 818, 692, 346, 346, nil, nil, nil, nil, nil, 632,
- 762, nil, nil, nil, 637, nil, nil, nil, nil, 640,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 955, nil, nil, 604, nil, 208, 530, 312, nil,
- nil, 312, 13, nil, nil, 294, nil, nil, 288, nil,
- 208, nil, 441, 208, 901, 14, nil, nil, 680, nil,
- nil, 732, nil, nil, 737, 441, nil, 208, nil, 894,
- 742, 661, 663, 665, 14, nil, nil, nil, 10, nil,
- 12, 13, 13, 655, nil, 294, nil, nil, 711, nil,
- nil, nil, nil, nil, nil, 767, nil, nil, nil, nil,
- 797, 312, 13, 816, 312, 923, 13, 857, 859, 861,
- 312, 13, 930, 208, nil, 441, 208, 10, 10, 12,
- 12, 441, 208, 271, 35, 275, 208, nil, 208, nil,
- 795, 724, 724, nil, 936, 656, 929, nil, 10, 703,
- 12, 14, 10, nil, 12, 290, 14, 10, nil, 12,
- nil, 745, 331, nil, 35, 287, 287, nil, 290, 310,
- 208, 208, nil, 952, nil, nil, 208, nil, nil, 14,
- nil, nil, nil, 956, nil, 656, nil, nil, 744, nil,
- nil, 920, nil, nil, 13, nil, 354, 368, nil, 368,
- 698, nil, 283, nil, nil, nil, nil, 13, 778, 780,
- nil, 530, nil, nil, 783, nil, nil, 13, 13, 294,
- nil, nil, 288, 809, nil, nil, nil, nil, nil, nil,
- 10, 294, 12, 35, 288, nil, nil, nil, nil, 365,
- nil, 35, 35, 10, nil, 12, 863, nil, nil, nil,
- nil, nil, nil, 10, 10, 12, 12, nil, nil, 346,
- nil, 746, 422, nil, nil, nil, nil, nil, nil, 940,
- 941, 942, 634, nil, nil, nil, 801, 638, 208, nil,
- nil, nil, 634, 13, nil, nil, nil, 13, 13, nil,
- nil, nil, nil, 838, nil, nil, nil, nil, 688, nil,
- nil, nil, nil, nil, nil, nil, 114, nil, 844, 13,
- 845, nil, 965, nil, 849, nil, 208, 208, nil, 10,
- nil, 12, nil, 10, 10, 12, 12, nil, nil, 35,
- 841, nil, nil, nil, 634, 634, 634, nil, nil, nil,
- nil, nil, nil, nil, nil, 10, nil, 12, 35, 907,
- nil, nil, nil, 13, 872, 874, nil, nil, nil, nil,
- nil, nil, 441, nil, 882, nil, nil, 416, nil, nil,
- nil, nil, 208, 318, 13, 13, nil, nil, nil, nil,
- nil, nil, 14, nil, nil, nil, 283, nil, 290, 10,
- nil, 12, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 346, nil, nil, nil, 724, nil, nil, nil, nil,
- 10, 10, 12, 12, nil, 35, nil, nil, nil, 287,
- 35, 14, 14, nil, 896, nil, 13, 294, 365, nil,
- 925, nil, 287, 958, nil, nil, nil, nil, nil, nil,
- nil, 13, 14, 35, 13, nil, 14, nil, nil, nil,
- nil, 14, nil, 283, nil, nil, nil, nil, 283, nil,
- nil, nil, 10, 13, 12, nil, 928, nil, nil, nil,
- 13, nil, nil, 312, nil, 13, nil, 10, nil, 12,
- 10, nil, 12, 13, nil, 208, nil, 441, nil, nil,
- nil, nil, nil, nil, nil, 346, nil, nil, nil, 10,
- nil, 12, nil, 368, nil, nil, 10, nil, 12, nil,
- nil, 10, nil, 12, nil, 420, 424, nil, nil, 10,
- nil, 12, nil, nil, 14, nil, nil, nil, 634, nil,
- nil, 638, nil, 634, nil, nil, nil, 14, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 14, 14, nil,
- nil, nil, 290, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 290, nil, nil, nil, nil, nil,
- nil, nil, nil, 490, nil, 492, nil, nil, nil, nil,
- 493, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 324, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 14, nil, nil, nil, 14, 14, 384,
- nil, 386, 386, 390, 393, 386, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 869, 14,
- nil, nil, nil, nil, nil, nil, 35, nil, nil, nil,
- nil, nil, 287, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 14, nil, 35, 35, nil, nil, nil,
- nil, nil, 368, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 14, 14, 35, 217, nil, nil,
- 35, 249, 249, 249, nil, 35, nil, nil, nil, nil,
- nil, nil, nil, 704, 705, nil, nil, 304, 305, 306,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 626, nil, 249, 249, 720, nil, nil, nil, 722, nil,
- nil, nil, nil, 730, nil, nil, 14, nil, nil, nil,
- 927, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 14, nil, nil, 14, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 35, nil,
- nil, nil, nil, 14, nil, nil, nil, nil, nil, nil,
- 14, 35, nil, nil, nil, 14, 657, nil, 318, nil,
- 660, 35, 35, 14, 500, nil, 287, nil, nil, nil,
- nil, nil, nil, nil, nil, 673, 796, nil, 287, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 799,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 804,
- 805, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 657, nil, nil, 318, nil, nil, nil, nil,
- nil, nil, 556, nil, nil, 556, 556, 35, nil, nil,
- nil, 35, 35, nil, nil, 419, 249, 427, 249, 249,
- nil, nil, nil, 446, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 35, nil, nil, nil, 217, nil, 459,
- 460, 461, 462, 463, 464, 465, 466, 467, 468, 469,
- 470, 471, 472, 473, 474, 475, 476, 477, 478, 479,
- 480, 481, 482, 483, 484, nil, nil, nil, nil, nil,
- nil, 870, nil, 249, nil, 249, 774, 35, nil, nil,
- 249, nil, nil, nil, nil, nil, nil, 249, 249, nil,
- nil, nil, nil, nil, nil, nil, 249, nil, 35, 35,
- nil, nil, nil, nil, nil, nil, nil, 798, 633, nil,
- nil, nil, nil, nil, nil, 888, nil, nil, 633, 657,
- 318, nil, nil, nil, nil, nil, 536, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 898, 899, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 814,
- 35, nil, nil, nil, 924, nil, nil, nil, nil, nil,
- nil, 651, nil, nil, nil, 35, nil, nil, 35, nil,
- 633, 633, 633, 651, nil, nil, nil, nil, nil, nil,
- 420, nil, nil, 651, 651, nil, nil, 35, 922, nil,
- nil, nil, nil, nil, 35, nil, nil, nil, nil, 35,
- nil, nil, nil, 934, 851, nil, nil, 35, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 871, 944, nil, nil, nil, nil,
- nil, nil, 949, nil, nil, nil, nil, 953, nil, nil,
- 249, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 420, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 249, 249, nil, 446, 648, 427, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 249, nil, 249, nil,
- 249, 917, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 672, nil, nil, nil, nil,
- 318, nil, nil, nil, nil, nil, nil, nil, nil, 249,
- nil, nil, 249, nil, nil, nil, nil, nil, nil, 933,
- 695, 696, 697, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 249, nil, 633, 249, nil, nil, nil, 633,
- 933, nil, nil, nil, nil, 812, 817, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 556, nil, nil, 556,
- 556, nil, nil, nil, nil, nil, 812, nil, 812, nil,
- nil, nil, 249, nil, nil, 249, nil, nil, nil, nil,
- nil, 249, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 773, nil, 249, nil, nil, 779,
- 781, nil, nil, 293, 293, 784, nil, nil, 786, nil,
- 293, 293, 293, nil, nil, nil, nil, nil, nil, 793,
- nil, nil, nil, nil, nil, nil, 293, 249, nil, nil,
- nil, nil, nil, nil, 293, 293, nil, 887, nil, 249,
- 249, 891, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 249,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 249, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 556,
- nil, nil, nil, nil, 249, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 249, 873, 875, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 779, 781, 784,
- 812, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 249, nil, nil, nil, 812, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 293, nil, 293, 293, 293, 293, 293, 293, 293, 293,
- 293, 293, 293, 293, 293, 293, 293, 293, 293, 293,
- 293, 293, 293, 293, 293, 293, 293, 293, nil, nil,
- nil, nil, nil, nil, nil, nil, 293, nil, 293, nil,
- nil, 249, nil, 293, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 875, 873, nil, nil, nil,
- 249, nil, nil, nil, nil, nil, nil, 293, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 249,
- 293, nil, nil, nil, nil, nil, nil, nil, nil, 293,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 249, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 249, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 293, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 293, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 293, 293, 293, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 293,
- nil, 293, nil, 293, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 293, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 293, 293, 293, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 293, nil, nil, 293, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 293, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 293, nil, 293,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 293, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 293, nil, nil, nil, nil, nil, nil, nil,
- 293, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 293, 293, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 293, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 293, nil, nil, nil,
- nil, nil, 293, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 293, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 293, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 293, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 293, 293, 293, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 293, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 293, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 293, 293,
- nil, nil, nil, 293, nil, nil, nil, nil, nil, nil,
- nil, nil, 293, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 293, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 293 ]
-
-racc_goto_check = [
- 21, 22, 51, 10, 104, 47, 35, 48, 48, 8,
- 29, 29, 29, 33, 33, 33, 7, 21, 21, 47,
- 41, 61, 21, 21, 21, 154, 107, 83, 52, 52,
- 21, 21, 21, 77, 77, 43, 17, 4, 20, 109,
- 84, 29, 109, 109, 54, 54, 54, 46, 145, 14,
- 14, 80, 21, 21, 50, 50, 18, 21, 21, 52,
- 52, 21, 21, 21, 6, 48, 17, 147, 20, 24,
- 24, 110, 106, 34, 34, 34, 33, 33, 33, 48,
- 55, 55, 154, 2, 55, 83, 38, 42, 56, 56,
- 56, 129, 141, 79, 79, 4, 108, 129, 17, 21,
- 1, 151, 45, 21, 21, 21, 21, 21, 21, 16,
- 16, 5, 39, 16, 60, 7, 148, 111, 126, 126,
- 56, 111, 126, 7, 7, 12, 78, 31, 31, 31,
- 15, 19, 47, 27, 58, 17, 59, 20, 63, 64,
- 26, 36, 11, 17, 17, 20, 20, 22, 22, 142,
- 142, 72, 73, 36, 74, 16, 16, 16, 16, 76,
- 81, 82, 111, 36, 36, 11, 60, 60, 60, 85,
- 86, 87, 88, 89, 90, 26, 91, 92, 93, 94,
- 26, 26, 151, 95, 26, 96, 97, 29, 29, 98,
- 99, 2, 48, 100, 101, 21, 21, 21, 21, 21,
- 21, 102, 48, 103, 21, 21, 21, 154, 105, 112,
- 113, 62, 115, 62, 21, 21, 116, 117, 54, 54,
- 110, 118, 119, 120, 124, 125, 26, 26, 26, 26,
- 62, 17, 127, 20, 18, 18, 18, 128, 130, 131,
- 132, 141, 18, 133, 135, 108, 137, 139, 144, 149,
- 17, nil, 20, 51, nil, 43, 29, 29, nil, nil,
- nil, 33, 33, nil, nil, 29, nil, nil, 21, 21,
- 104, 51, 61, 51, nil, nil, 145, 21, 80, 145,
- nil, 21, 52, nil, nil, 21, 21, 41, nil, nil,
- 54, 54, 107, nil, nil, 52, nil, nil, 21, 54,
- nil, 142, 83, nil, nil, nil, nil, nil, nil, 21,
- nil, 111, 4, nil, nil, nil, nil, 17, nil, 20,
- nil, nil, 17, 129, 20, nil, 57, 57, 26, 26,
- 23, 45, nil, 33, 56, 56, 33, 38, 26, 6,
- 42, 106, 38, 56, 148, 17, 33, 20, 108, 21,
- 21, 108, 108, 46, 108, 78, 108, 79, 84, nil,
- 23, 23, 23, 39, 147, 60, 52, nil, 39, 21,
- nil, 35, nil, 31, 31, 83, 35, 11, nil, 35,
- nil, 21, 31, nil, nil, 4, 111, nil, nil, 110,
- 83, nil, 23, 23, 62, 62, 33, nil, nil, 22,
- 22, 16, 16, nil, nil, 33, nil, nil, nil, 10,
- 141, 48, nil, nil, 108, 48, nil, 154, 111, 43,
- 111, nil, nil, 109, 61, nil, 109, 109, 145, 23,
- nil, nil, nil, nil, nil, 108, nil, 23, 23, 29,
- 29, nil, nil, 8, nil, nil, nil, 8, 8, nil,
- 48, 21, 21, 14, nil, nil, nil, 14, 50, nil,
- 51, nil, 50, nil, nil, nil, nil, nil, 22, nil,
- 55, 22, 26, 26, nil, nil, nil, nil, nil, 34,
- 111, nil, nil, nil, 34, nil, nil, nil, nil, 34,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 111, nil, nil, 26, nil, 21, 41, 29, nil,
- nil, 29, 21, nil, nil, 52, nil, nil, 21, nil,
- 21, nil, 48, 21, 77, 23, nil, nil, 7, nil,
- nil, 22, nil, nil, 22, 48, nil, 21, nil, 145,
- 22, 34, 34, 34, 23, nil, nil, nil, 17, nil,
- 20, 21, 21, 56, nil, 52, nil, nil, 21, nil,
- nil, nil, nil, nil, nil, 51, nil, nil, nil, nil,
- 61, 29, 21, 24, 29, 77, 21, 143, 143, 143,
- 29, 21, 77, 21, nil, 48, 21, 17, 17, 20,
- 20, 48, 21, 57, 44, 57, 21, nil, 21, nil,
- 104, 83, 83, nil, 8, 62, 109, nil, 17, 56,
- 20, 23, 17, nil, 20, 23, 23, 17, nil, 20,
- nil, 83, 107, nil, 44, 44, 44, nil, 23, 26,
- 21, 21, nil, 80, nil, nil, 21, nil, nil, 23,
- nil, nil, nil, 8, nil, 62, nil, nil, 16, nil,
- nil, 61, nil, nil, 21, nil, 44, 44, nil, 44,
- 26, nil, 9, nil, nil, nil, nil, 21, 18, 18,
- nil, 41, nil, nil, 18, nil, nil, 21, 21, 52,
- nil, nil, 21, 41, nil, nil, nil, nil, nil, nil,
- 17, 52, 20, 44, 21, nil, nil, nil, nil, 23,
- nil, 44, 44, 17, nil, 20, 47, nil, nil, nil,
- nil, nil, nil, 17, 17, 20, 20, nil, nil, 26,
- nil, 26, 54, nil, nil, nil, nil, nil, nil, 143,
- 143, 143, 57, nil, nil, nil, 56, 57, 21, nil,
- nil, nil, 57, 21, nil, nil, nil, 21, 21, nil,
- nil, nil, nil, 54, nil, nil, nil, nil, 33, nil,
- nil, nil, nil, nil, nil, nil, 48, nil, 83, 21,
- 83, nil, 143, nil, 83, nil, 21, 21, nil, 17,
- nil, 20, nil, 17, 17, 20, 20, nil, nil, 44,
- 16, nil, nil, nil, 57, 57, 57, nil, nil, nil,
- nil, nil, nil, nil, nil, 17, nil, 20, 44, 51,
- nil, nil, nil, 21, 18, 18, nil, nil, nil, nil,
- nil, nil, 48, nil, 54, nil, nil, 9, nil, nil,
- nil, nil, 21, 25, 21, 21, nil, nil, nil, nil,
- nil, nil, 23, nil, nil, nil, 9, nil, 23, 17,
- nil, 20, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 26, nil, nil, nil, 83, nil, nil, nil, nil,
- 17, 17, 20, 20, nil, 44, nil, nil, nil, 44,
- 44, 23, 23, nil, 16, nil, 21, 52, 23, nil,
- 21, nil, 44, 22, nil, nil, nil, nil, nil, nil,
- nil, 21, 23, 44, 21, nil, 23, nil, nil, nil,
- nil, 23, nil, 9, nil, nil, nil, nil, 9, nil,
- nil, nil, 17, 21, 20, nil, 17, nil, nil, nil,
- 21, nil, nil, 29, nil, 21, nil, 17, nil, 20,
- 17, nil, 20, 21, nil, 21, nil, 48, nil, nil,
- nil, nil, nil, nil, nil, 26, nil, nil, nil, 17,
- nil, 20, nil, 44, nil, nil, 17, nil, 20, nil,
- nil, 17, nil, 20, nil, 25, 25, nil, nil, 17,
- nil, 20, nil, nil, 23, nil, nil, nil, 57, nil,
- nil, 57, nil, 57, nil, nil, nil, 23, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 23, 23, nil,
- nil, nil, 23, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 23, nil, nil, nil, nil, nil,
- nil, nil, nil, 25, nil, 25, nil, nil, nil, nil,
- 25, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 53, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 23, nil, nil, nil, 23, 23, 53,
- nil, 53, 53, 53, 53, 53, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 23, 23,
- nil, nil, nil, nil, nil, nil, 44, nil, nil, nil,
- nil, nil, 44, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 23, nil, 44, 44, nil, nil, nil,
- nil, nil, 44, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 23, 23, 44, 28, nil, nil,
- 44, 28, 28, 28, nil, 44, nil, nil, nil, nil,
- nil, nil, nil, 9, 9, nil, nil, 28, 28, 28,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 25, nil, 28, 28, 9, nil, nil, nil, 9, nil,
- nil, nil, nil, 9, nil, nil, 23, nil, nil, nil,
- 23, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 23, nil, nil, 23, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 44, nil,
- nil, nil, nil, 23, nil, nil, nil, nil, nil, nil,
- 23, 44, nil, nil, nil, 23, 25, nil, 25, nil,
- 25, 44, 44, 23, 53, nil, 44, nil, nil, nil,
- nil, nil, nil, nil, nil, 25, 9, nil, 44, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 9,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 9,
- 9, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 25, nil, nil, 25, nil, nil, nil, nil,
- nil, nil, 53, nil, nil, 53, 53, 44, nil, nil,
- nil, 44, 44, nil, nil, 28, 28, 28, 28, 28,
- nil, nil, nil, 28, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 44, nil, nil, nil, 28, nil, 28,
- 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
- 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
- 28, 28, 28, 28, 28, nil, nil, nil, nil, nil,
- nil, 9, nil, 28, nil, 28, 25, 44, nil, nil,
- 28, nil, nil, nil, nil, nil, nil, 28, 28, nil,
- nil, nil, nil, nil, nil, nil, 28, nil, 44, 44,
- nil, nil, nil, nil, nil, nil, nil, 25, 53, nil,
- nil, nil, nil, nil, nil, 9, nil, nil, 53, 25,
- 25, nil, nil, nil, nil, nil, 28, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 9, 9, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 25,
- 44, nil, nil, nil, 44, nil, nil, nil, nil, nil,
- nil, 53, nil, nil, nil, 44, nil, nil, 44, nil,
- 53, 53, 53, 53, nil, nil, nil, nil, nil, nil,
- 25, nil, nil, 53, 53, nil, nil, 44, 9, nil,
- nil, nil, nil, nil, 44, nil, nil, nil, nil, 44,
- nil, nil, nil, 9, 25, nil, nil, 44, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 25, 9, nil, nil, nil, nil,
- nil, nil, 9, nil, nil, nil, nil, 9, nil, nil,
- 28, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 25, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 28, 28, nil, 28, 28, 28, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 28, nil, 28, nil,
- 28, 25, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 28, nil, nil, nil, nil,
- 25, nil, nil, nil, nil, nil, nil, nil, nil, 28,
- nil, nil, 28, nil, nil, nil, nil, nil, nil, 25,
- 28, 28, 28, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 28, nil, 53, 28, nil, nil, nil, 53,
- 25, nil, nil, nil, nil, 53, 53, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 53, nil, nil, 53,
- 53, nil, nil, nil, nil, nil, 53, nil, 53, nil,
- nil, nil, 28, nil, nil, 28, nil, nil, nil, nil,
- nil, 28, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 28, nil, 28, nil, nil, 28,
- 28, nil, nil, 37, 37, 28, nil, nil, 28, nil,
- 37, 37, 37, nil, nil, nil, nil, nil, nil, 28,
- nil, nil, nil, nil, nil, nil, 37, 28, nil, nil,
- nil, nil, nil, nil, 37, 37, nil, 53, nil, 28,
- 28, 53, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 28,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 28, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 53,
- nil, nil, nil, nil, 28, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 28, 28, 28, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 28, 28, 28,
- 53, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 28, nil, nil, nil, 53, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 37, nil, 37, 37, 37, 37, 37, 37, 37, 37,
- 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
- 37, 37, 37, 37, 37, 37, 37, 37, nil, nil,
- nil, nil, nil, nil, nil, nil, 37, nil, 37, nil,
- nil, 28, nil, 37, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 28, 28, nil, nil, nil,
- 28, nil, nil, nil, nil, nil, nil, 37, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 28,
- 37, nil, nil, nil, nil, nil, nil, nil, nil, 37,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 28, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 28, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 37, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 37, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 37, 37, 37, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 37,
- nil, 37, nil, 37, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 37, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 37, 37, 37, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 37, nil, nil, 37, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 37, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 37, nil, 37,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 37, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 37, nil, nil, nil, nil, nil, nil, nil,
- 37, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 37, 37, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 37, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 37, nil, nil, nil,
- nil, nil, 37, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 37, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 37, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 37, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 37, 37, 37, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 37, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 37, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 37, 37,
- nil, nil, nil, 37, nil, nil, nil, nil, nil, nil,
- nil, nil, 37, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 37, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 37 ]
-
-racc_goto_pointer = [
- nil, 100, 83, nil, 34, 12, 61, 16, -300, 632,
- -535, -557, -686, nil, 42, 122, 52, 36, 38, 69,
- 38, 0, -52, 330, -127, 779, 123, 24, 1149, -12,
- nil, 105, nil, -188, 47, -261, -344, 1752, 56, 82,
- nil, -11, 55, -263, 594, -260, -16, -60, 0, nil,
- 47, -39, -3, 1006, 22, -233, 66, 300, -134, -363,
- -328, -231, -276, -368, -130, nil, nil, nil, nil, nil,
- nil, nil, 86, 98, 99, nil, 103, -314, -594, -484,
- -301, 98, -205, 24, -560, 104, -209, 120, 113, -550,
- 114, -551, -411, -724, -415, -184, -660, 122, -410, -188,
- -408, -669, 145, -118, -52, -351, -483, -30, -504, -287,
- -529, -438, 189, -36, nil, -60, -57, -717, -361, -478,
- -592, nil, nil, nil, 153, 152, 43, 155, -152, -292,
- 160, -529, -368, -366, nil, -505, nil, -606, nil, -605,
- nil, -508, -608, -183, -608, -290, nil, -492, -222, -469,
- nil, -757, nil, nil, -464 ]
-
-racc_goto_default = [
- nil, nil, nil, 3, nil, 4, 353, 279, nil, 538,
- nil, 831, nil, 278, nil, nil, nil, 212, 16, 11,
- 213, 303, nil, 211, nil, 255, 15, nil, 19, 20,
- 21, nil, 25, 691, nil, nil, nil, 26, 29, nil,
- 31, 34, 33, nil, 209, 363, nil, 116, 435, 115,
- 69, nil, 42, 311, 313, nil, 314, 433, nil, nil,
- 635, 486, 253, nil, nil, 269, 43, 44, 45, 46,
- 47, 48, 49, nil, 270, 55, nil, nil, nil, nil,
- nil, nil, nil, 573, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 326, 325, 709, 328, nil,
- 329, 330, nil, nil, 439, nil, nil, nil, nil, nil,
- nil, 68, 70, 71, 72, nil, nil, nil, nil, 611,
- nil, nil, nil, nil, 395, 750, 753, 758, 755, 756,
- 757, 911, nil, nil, 761, 337, 332, 339, nil, 567,
- 568, 765, 342, 345, 260 ]
-
-racc_reduce_table = [
- 0, 0, :racc_error,
- 1, 143, :_reduce_none,
- 2, 144, :_reduce_2,
- 0, 145, :_reduce_3,
- 1, 145, :_reduce_4,
- 3, 145, :_reduce_5,
- 1, 147, :_reduce_none,
- 4, 147, :_reduce_7,
- 4, 150, :_reduce_8,
- 2, 151, :_reduce_9,
- 0, 155, :_reduce_10,
- 1, 155, :_reduce_11,
- 3, 155, :_reduce_12,
- 0, 169, :_reduce_13,
- 4, 149, :_reduce_14,
- 3, 149, :_reduce_15,
- 3, 149, :_reduce_none,
- 3, 149, :_reduce_17,
- 2, 149, :_reduce_18,
- 3, 149, :_reduce_19,
- 3, 149, :_reduce_20,
- 3, 149, :_reduce_21,
- 3, 149, :_reduce_22,
- 3, 149, :_reduce_23,
- 4, 149, :_reduce_none,
- 3, 149, :_reduce_25,
- 3, 149, :_reduce_26,
- 3, 149, :_reduce_27,
- 6, 149, :_reduce_none,
- 6, 149, :_reduce_none,
- 5, 149, :_reduce_30,
- 5, 149, :_reduce_none,
- 5, 149, :_reduce_none,
- 3, 149, :_reduce_none,
- 3, 149, :_reduce_34,
- 3, 149, :_reduce_35,
- 3, 149, :_reduce_36,
- 1, 149, :_reduce_none,
- 1, 168, :_reduce_none,
- 3, 168, :_reduce_39,
- 3, 168, :_reduce_40,
- 2, 168, :_reduce_41,
- 2, 168, :_reduce_42,
- 1, 168, :_reduce_none,
- 1, 158, :_reduce_none,
- 1, 160, :_reduce_none,
- 1, 160, :_reduce_none,
- 2, 160, :_reduce_47,
- 2, 160, :_reduce_48,
- 2, 160, :_reduce_49,
- 1, 172, :_reduce_none,
- 4, 172, :_reduce_none,
- 4, 172, :_reduce_none,
- 4, 172, :_reduce_none,
- 4, 177, :_reduce_none,
- 2, 171, :_reduce_55,
- 3, 171, :_reduce_none,
- 4, 171, :_reduce_57,
- 5, 171, :_reduce_none,
- 4, 171, :_reduce_59,
- 5, 171, :_reduce_none,
- 4, 171, :_reduce_61,
- 5, 171, :_reduce_none,
- 2, 171, :_reduce_63,
- 2, 171, :_reduce_64,
- 1, 161, :_reduce_65,
- 3, 161, :_reduce_66,
- 1, 181, :_reduce_67,
- 3, 181, :_reduce_68,
- 1, 180, :_reduce_69,
- 2, 180, :_reduce_70,
- 3, 180, :_reduce_71,
- 5, 180, :_reduce_none,
- 2, 180, :_reduce_73,
- 4, 180, :_reduce_none,
- 2, 180, :_reduce_75,
- 1, 180, :_reduce_76,
- 3, 180, :_reduce_none,
- 1, 183, :_reduce_78,
- 3, 183, :_reduce_79,
- 2, 182, :_reduce_80,
- 3, 182, :_reduce_81,
- 1, 185, :_reduce_none,
- 3, 185, :_reduce_none,
- 1, 184, :_reduce_84,
- 4, 184, :_reduce_85,
- 3, 184, :_reduce_86,
- 3, 184, :_reduce_none,
- 3, 184, :_reduce_none,
- 3, 184, :_reduce_none,
- 2, 184, :_reduce_none,
- 1, 184, :_reduce_none,
- 1, 159, :_reduce_92,
- 4, 159, :_reduce_93,
- 4, 159, :_reduce_94,
- 3, 159, :_reduce_95,
- 3, 159, :_reduce_96,
- 3, 159, :_reduce_97,
- 3, 159, :_reduce_98,
- 2, 159, :_reduce_99,
- 1, 159, :_reduce_none,
- 1, 187, :_reduce_none,
- 2, 188, :_reduce_102,
- 1, 188, :_reduce_103,
- 3, 188, :_reduce_104,
- 1, 189, :_reduce_none,
- 1, 189, :_reduce_none,
- 1, 189, :_reduce_none,
- 1, 189, :_reduce_108,
- 1, 189, :_reduce_109,
- 1, 156, :_reduce_110,
- 1, 156, :_reduce_none,
- 1, 157, :_reduce_112,
- 3, 157, :_reduce_113,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 190, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 1, 191, :_reduce_none,
- 3, 170, :_reduce_188,
- 5, 170, :_reduce_189,
- 3, 170, :_reduce_190,
- 6, 170, :_reduce_191,
- 6, 170, :_reduce_192,
- 5, 170, :_reduce_193,
- 5, 170, :_reduce_none,
- 5, 170, :_reduce_none,
- 5, 170, :_reduce_none,
- 4, 170, :_reduce_none,
- 3, 170, :_reduce_none,
- 3, 170, :_reduce_199,
- 3, 170, :_reduce_200,
- 3, 170, :_reduce_201,
- 3, 170, :_reduce_202,
- 3, 170, :_reduce_203,
- 3, 170, :_reduce_204,
- 3, 170, :_reduce_205,
- 3, 170, :_reduce_206,
- 4, 170, :_reduce_207,
- 4, 170, :_reduce_208,
- 2, 170, :_reduce_209,
- 2, 170, :_reduce_210,
- 3, 170, :_reduce_211,
- 3, 170, :_reduce_212,
- 3, 170, :_reduce_213,
- 3, 170, :_reduce_214,
- 3, 170, :_reduce_215,
- 3, 170, :_reduce_216,
- 3, 170, :_reduce_217,
- 3, 170, :_reduce_218,
- 3, 170, :_reduce_219,
- 3, 170, :_reduce_220,
- 3, 170, :_reduce_221,
- 3, 170, :_reduce_222,
- 3, 170, :_reduce_223,
- 2, 170, :_reduce_224,
- 2, 170, :_reduce_225,
- 3, 170, :_reduce_226,
- 3, 170, :_reduce_227,
- 3, 170, :_reduce_228,
- 3, 170, :_reduce_229,
- 3, 170, :_reduce_230,
- 5, 170, :_reduce_231,
- 1, 170, :_reduce_none,
- 1, 167, :_reduce_none,
- 1, 164, :_reduce_234,
- 2, 164, :_reduce_235,
- 2, 164, :_reduce_236,
- 4, 164, :_reduce_237,
- 2, 164, :_reduce_238,
- 3, 199, :_reduce_239,
- 2, 201, :_reduce_none,
- 1, 202, :_reduce_241,
- 1, 202, :_reduce_none,
- 1, 200, :_reduce_243,
- 1, 200, :_reduce_none,
- 2, 200, :_reduce_245,
- 4, 200, :_reduce_246,
- 2, 200, :_reduce_247,
- 1, 173, :_reduce_248,
- 2, 173, :_reduce_249,
- 2, 173, :_reduce_250,
- 4, 173, :_reduce_251,
- 1, 173, :_reduce_252,
- 4, 205, :_reduce_none,
- 1, 205, :_reduce_none,
- 0, 207, :_reduce_255,
- 2, 176, :_reduce_256,
- 1, 206, :_reduce_none,
- 2, 206, :_reduce_258,
- 3, 206, :_reduce_259,
- 2, 204, :_reduce_260,
- 2, 203, :_reduce_261,
- 0, 203, :_reduce_262,
- 1, 196, :_reduce_263,
- 2, 196, :_reduce_264,
- 3, 196, :_reduce_265,
- 4, 196, :_reduce_266,
- 3, 166, :_reduce_267,
- 4, 166, :_reduce_268,
- 2, 166, :_reduce_269,
- 1, 194, :_reduce_none,
- 1, 194, :_reduce_none,
- 1, 194, :_reduce_none,
- 1, 194, :_reduce_none,
- 1, 194, :_reduce_none,
- 1, 194, :_reduce_none,
- 1, 194, :_reduce_none,
- 1, 194, :_reduce_none,
- 1, 194, :_reduce_none,
- 0, 229, :_reduce_279,
- 4, 194, :_reduce_280,
- 4, 194, :_reduce_281,
- 3, 194, :_reduce_282,
- 3, 194, :_reduce_283,
- 2, 194, :_reduce_284,
- 4, 194, :_reduce_285,
- 4, 194, :_reduce_286,
- 3, 194, :_reduce_287,
- 3, 194, :_reduce_288,
- 1, 194, :_reduce_289,
- 4, 194, :_reduce_290,
- 3, 194, :_reduce_291,
- 1, 194, :_reduce_292,
- 5, 194, :_reduce_293,
- 4, 194, :_reduce_294,
- 3, 194, :_reduce_295,
- 2, 194, :_reduce_296,
- 1, 194, :_reduce_none,
- 2, 194, :_reduce_298,
- 2, 194, :_reduce_299,
- 6, 194, :_reduce_300,
- 6, 194, :_reduce_301,
- 0, 230, :_reduce_302,
- 0, 231, :_reduce_303,
- 7, 194, :_reduce_304,
- 0, 232, :_reduce_305,
- 0, 233, :_reduce_306,
- 7, 194, :_reduce_307,
- 5, 194, :_reduce_308,
- 4, 194, :_reduce_309,
- 5, 194, :_reduce_310,
- 0, 234, :_reduce_311,
- 0, 235, :_reduce_312,
- 9, 194, :_reduce_313,
- 0, 236, :_reduce_314,
- 6, 194, :_reduce_315,
- 0, 237, :_reduce_316,
- 0, 238, :_reduce_317,
- 8, 194, :_reduce_318,
- 0, 239, :_reduce_319,
- 0, 240, :_reduce_320,
- 6, 194, :_reduce_321,
- 0, 241, :_reduce_322,
- 6, 194, :_reduce_323,
- 0, 242, :_reduce_324,
- 0, 243, :_reduce_325,
- 9, 194, :_reduce_326,
- 1, 194, :_reduce_327,
- 1, 194, :_reduce_328,
- 1, 194, :_reduce_329,
- 1, 194, :_reduce_none,
- 1, 163, :_reduce_none,
- 1, 219, :_reduce_none,
- 1, 219, :_reduce_none,
- 1, 219, :_reduce_none,
- 2, 219, :_reduce_none,
- 1, 221, :_reduce_none,
- 1, 221, :_reduce_none,
- 1, 221, :_reduce_none,
- 2, 218, :_reduce_339,
- 3, 244, :_reduce_340,
- 2, 244, :_reduce_341,
- 1, 244, :_reduce_none,
- 1, 244, :_reduce_none,
- 3, 245, :_reduce_344,
- 3, 245, :_reduce_345,
- 1, 220, :_reduce_346,
- 5, 220, :_reduce_347,
- 1, 153, :_reduce_none,
- 2, 153, :_reduce_349,
- 1, 247, :_reduce_350,
- 3, 247, :_reduce_351,
- 3, 248, :_reduce_352,
- 1, 178, :_reduce_none,
- 2, 178, :_reduce_354,
- 1, 178, :_reduce_355,
- 3, 178, :_reduce_356,
- 1, 249, :_reduce_357,
- 2, 251, :_reduce_358,
- 1, 251, :_reduce_359,
- 6, 246, :_reduce_360,
- 4, 246, :_reduce_361,
- 4, 246, :_reduce_362,
- 2, 246, :_reduce_363,
- 2, 246, :_reduce_364,
- 4, 246, :_reduce_365,
- 2, 246, :_reduce_366,
- 2, 246, :_reduce_367,
- 1, 246, :_reduce_368,
- 0, 255, :_reduce_369,
- 5, 254, :_reduce_370,
- 2, 174, :_reduce_371,
- 4, 174, :_reduce_none,
- 4, 174, :_reduce_none,
- 4, 174, :_reduce_none,
- 2, 217, :_reduce_375,
- 4, 217, :_reduce_376,
- 4, 217, :_reduce_377,
- 3, 217, :_reduce_378,
- 4, 217, :_reduce_379,
- 3, 217, :_reduce_380,
- 2, 217, :_reduce_381,
- 1, 217, :_reduce_382,
- 0, 257, :_reduce_383,
- 5, 216, :_reduce_384,
- 0, 258, :_reduce_385,
- 5, 216, :_reduce_386,
- 0, 260, :_reduce_387,
- 6, 222, :_reduce_388,
- 1, 259, :_reduce_389,
- 1, 259, :_reduce_none,
- 6, 152, :_reduce_391,
- 0, 152, :_reduce_392,
- 1, 261, :_reduce_393,
- 1, 261, :_reduce_none,
- 1, 261, :_reduce_none,
- 2, 262, :_reduce_396,
- 1, 262, :_reduce_397,
- 2, 154, :_reduce_398,
- 1, 154, :_reduce_none,
- 1, 208, :_reduce_none,
- 1, 208, :_reduce_none,
- 1, 208, :_reduce_none,
- 1, 209, :_reduce_403,
- 1, 265, :_reduce_none,
- 2, 265, :_reduce_405,
- 3, 266, :_reduce_406,
- 1, 266, :_reduce_407,
- 3, 210, :_reduce_408,
- 3, 211, :_reduce_409,
- 3, 212, :_reduce_410,
- 3, 212, :_reduce_411,
- 1, 269, :_reduce_412,
- 3, 269, :_reduce_413,
- 1, 270, :_reduce_414,
- 2, 270, :_reduce_415,
- 3, 213, :_reduce_416,
- 3, 213, :_reduce_417,
- 1, 272, :_reduce_418,
- 3, 272, :_reduce_419,
- 1, 267, :_reduce_420,
- 2, 267, :_reduce_421,
- 1, 268, :_reduce_422,
- 2, 268, :_reduce_423,
- 1, 271, :_reduce_424,
- 0, 274, :_reduce_425,
- 3, 271, :_reduce_426,
- 0, 275, :_reduce_427,
- 4, 271, :_reduce_428,
- 1, 273, :_reduce_429,
- 1, 273, :_reduce_430,
- 1, 273, :_reduce_431,
- 1, 273, :_reduce_none,
- 2, 192, :_reduce_433,
- 1, 192, :_reduce_434,
- 1, 276, :_reduce_none,
- 1, 276, :_reduce_none,
- 1, 276, :_reduce_none,
- 1, 276, :_reduce_none,
- 3, 264, :_reduce_439,
- 1, 263, :_reduce_440,
- 1, 263, :_reduce_441,
- 2, 263, :_reduce_442,
- 2, 263, :_reduce_443,
- 2, 263, :_reduce_444,
- 2, 263, :_reduce_445,
- 1, 186, :_reduce_446,
- 1, 186, :_reduce_447,
- 1, 186, :_reduce_448,
- 1, 186, :_reduce_449,
- 1, 186, :_reduce_450,
- 1, 186, :_reduce_451,
- 1, 186, :_reduce_452,
- 1, 186, :_reduce_453,
- 1, 186, :_reduce_454,
- 1, 186, :_reduce_455,
- 1, 186, :_reduce_456,
- 1, 214, :_reduce_457,
- 1, 162, :_reduce_458,
- 1, 165, :_reduce_459,
- 1, 165, :_reduce_none,
- 1, 224, :_reduce_461,
- 3, 224, :_reduce_462,
- 2, 224, :_reduce_463,
- 4, 226, :_reduce_464,
- 2, 226, :_reduce_465,
- 1, 278, :_reduce_none,
- 1, 278, :_reduce_none,
- 2, 279, :_reduce_468,
- 1, 279, :_reduce_469,
- 1, 280, :_reduce_470,
- 2, 281, :_reduce_471,
- 1, 281, :_reduce_472,
- 1, 282, :_reduce_473,
- 3, 282, :_reduce_474,
- 4, 283, :_reduce_475,
- 2, 283, :_reduce_476,
- 2, 283, :_reduce_477,
- 1, 283, :_reduce_478,
- 2, 285, :_reduce_479,
- 0, 285, :_reduce_480,
- 6, 277, :_reduce_481,
- 4, 277, :_reduce_482,
- 4, 277, :_reduce_483,
- 2, 277, :_reduce_484,
- 4, 277, :_reduce_485,
- 2, 277, :_reduce_486,
- 2, 277, :_reduce_487,
- 1, 277, :_reduce_488,
- 0, 277, :_reduce_489,
- 1, 287, :_reduce_none,
- 1, 287, :_reduce_491,
- 1, 288, :_reduce_492,
- 1, 288, :_reduce_493,
- 1, 288, :_reduce_494,
- 1, 288, :_reduce_495,
- 1, 289, :_reduce_496,
- 3, 289, :_reduce_497,
- 1, 223, :_reduce_none,
- 1, 223, :_reduce_none,
- 1, 291, :_reduce_500,
- 3, 291, :_reduce_none,
- 1, 292, :_reduce_502,
- 3, 292, :_reduce_503,
- 1, 290, :_reduce_none,
- 4, 290, :_reduce_none,
- 3, 290, :_reduce_none,
- 2, 290, :_reduce_none,
- 1, 290, :_reduce_none,
- 1, 252, :_reduce_509,
- 3, 252, :_reduce_510,
- 3, 293, :_reduce_511,
- 1, 286, :_reduce_512,
- 3, 286, :_reduce_513,
- 1, 294, :_reduce_none,
- 1, 294, :_reduce_none,
- 2, 253, :_reduce_516,
- 1, 253, :_reduce_517,
- 1, 295, :_reduce_none,
- 1, 295, :_reduce_none,
- 2, 250, :_reduce_520,
- 2, 284, :_reduce_521,
- 0, 284, :_reduce_522,
- 1, 227, :_reduce_523,
- 4, 227, :_reduce_524,
- 0, 215, :_reduce_525,
- 2, 215, :_reduce_526,
- 1, 198, :_reduce_527,
- 3, 198, :_reduce_528,
- 3, 296, :_reduce_529,
- 2, 296, :_reduce_530,
- 1, 179, :_reduce_none,
- 1, 179, :_reduce_none,
- 1, 179, :_reduce_none,
- 1, 175, :_reduce_none,
- 1, 175, :_reduce_none,
- 1, 175, :_reduce_none,
- 1, 175, :_reduce_none,
- 1, 256, :_reduce_none,
- 1, 256, :_reduce_none,
- 1, 256, :_reduce_none,
- 1, 228, :_reduce_none,
- 1, 228, :_reduce_none,
- 0, 146, :_reduce_none,
- 1, 146, :_reduce_none,
- 0, 193, :_reduce_none,
- 1, 193, :_reduce_none,
- 0, 197, :_reduce_none,
- 1, 197, :_reduce_none,
- 1, 197, :_reduce_none,
- 1, 225, :_reduce_none,
- 1, 225, :_reduce_none,
- 1, 148, :_reduce_none,
- 2, 148, :_reduce_none,
- 0, 195, :_reduce_554 ]
-
-racc_reduce_n = 555
-
-racc_shift_n = 968
-
-racc_token_table = {
- false => 0,
- :error => 1,
- :kCLASS => 2,
- :kMODULE => 3,
- :kDEF => 4,
- :kUNDEF => 5,
- :kBEGIN => 6,
- :kRESCUE => 7,
- :kENSURE => 8,
- :kEND => 9,
- :kIF => 10,
- :kUNLESS => 11,
- :kTHEN => 12,
- :kELSIF => 13,
- :kELSE => 14,
- :kCASE => 15,
- :kWHEN => 16,
- :kWHILE => 17,
- :kUNTIL => 18,
- :kFOR => 19,
- :kBREAK => 20,
- :kNEXT => 21,
- :kREDO => 22,
- :kRETRY => 23,
- :kIN => 24,
- :kDO => 25,
- :kDO_COND => 26,
- :kDO_BLOCK => 27,
- :kDO_LAMBDA => 28,
- :kRETURN => 29,
- :kYIELD => 30,
- :kSUPER => 31,
- :kSELF => 32,
- :kNIL => 33,
- :kTRUE => 34,
- :kFALSE => 35,
- :kAND => 36,
- :kOR => 37,
- :kNOT => 38,
- :kIF_MOD => 39,
- :kUNLESS_MOD => 40,
- :kWHILE_MOD => 41,
- :kUNTIL_MOD => 42,
- :kRESCUE_MOD => 43,
- :kALIAS => 44,
- :kDEFINED => 45,
- :klBEGIN => 46,
- :klEND => 47,
- :k__LINE__ => 48,
- :k__FILE__ => 49,
- :k__ENCODING__ => 50,
- :tIDENTIFIER => 51,
- :tFID => 52,
- :tGVAR => 53,
- :tIVAR => 54,
- :tCONSTANT => 55,
- :tLABEL => 56,
- :tCVAR => 57,
- :tNTH_REF => 58,
- :tBACK_REF => 59,
- :tSTRING_CONTENT => 60,
- :tINTEGER => 61,
- :tFLOAT => 62,
- :tREGEXP_END => 63,
- :tUPLUS => 64,
- :tUMINUS => 65,
- :tUMINUS_NUM => 66,
- :tPOW => 67,
- :tCMP => 68,
- :tEQ => 69,
- :tEQQ => 70,
- :tNEQ => 71,
- :tGEQ => 72,
- :tLEQ => 73,
- :tANDOP => 74,
- :tOROP => 75,
- :tMATCH => 76,
- :tNMATCH => 77,
- :tJSDOT => 78,
- :tDOT => 79,
- :tDOT2 => 80,
- :tDOT3 => 81,
- :tAREF => 82,
- :tASET => 83,
- :tLSHFT => 84,
- :tRSHFT => 85,
- :tCOLON2 => 86,
- :tCOLON3 => 87,
- :tOP_ASGN => 88,
- :tASSOC => 89,
- :tLPAREN => 90,
- :tLPAREN2 => 91,
- :tRPAREN => 92,
- :tLPAREN_ARG => 93,
- :ARRAY_BEG => 94,
- :tRBRACK => 95,
- :tLBRACE => 96,
- :tLBRACE_ARG => 97,
- :tSTAR => 98,
- :tSTAR2 => 99,
- :tAMPER => 100,
- :tAMPER2 => 101,
- :tTILDE => 102,
- :tPERCENT => 103,
- :tDIVIDE => 104,
- :tPLUS => 105,
- :tMINUS => 106,
- :tLT => 107,
- :tGT => 108,
- :tPIPE => 109,
- :tBANG => 110,
- :tCARET => 111,
- :tLCURLY => 112,
- :tRCURLY => 113,
- :tBACK_REF2 => 114,
- :tSYMBEG => 115,
- :tSTRING_BEG => 116,
- :tXSTRING_BEG => 117,
- :tREGEXP_BEG => 118,
- :tWORDS_BEG => 119,
- :tAWORDS_BEG => 120,
- :tSTRING_DBEG => 121,
- :tSTRING_DVAR => 122,
- :tSTRING_END => 123,
- :tSTRING => 124,
- :tSYMBOL => 125,
- :tNL => 126,
- :tEH => 127,
- :tCOLON => 128,
- :tCOMMA => 129,
- :tSPACE => 130,
- :tSEMI => 131,
- :tLAMBDA => 132,
- :tLAMBEG => 133,
- :tLBRACK2 => 134,
- :tLBRACK => 135,
- :tJSLBRACK => 136,
- :tDSTAR => 137,
- :tEQL => 138,
- :tLOWEST => 139,
- "-@NUM" => 140,
- "+@NUM" => 141 }
-
-racc_nt_base = 142
-
-racc_use_result_var = true
-
-Racc_arg = [
- racc_action_table,
- racc_action_check,
- racc_action_default,
- racc_action_pointer,
- racc_goto_table,
- racc_goto_check,
- racc_goto_default,
- racc_goto_pointer,
- racc_nt_base,
- racc_reduce_table,
- racc_token_table,
- racc_shift_n,
- racc_reduce_n,
- racc_use_result_var ]
-Ractor.make_shareable(Racc_arg) if defined?(Ractor)
-
-Racc_token_to_s_table = [
- "$end",
- "error",
- "kCLASS",
- "kMODULE",
- "kDEF",
- "kUNDEF",
- "kBEGIN",
- "kRESCUE",
- "kENSURE",
- "kEND",
- "kIF",
- "kUNLESS",
- "kTHEN",
- "kELSIF",
- "kELSE",
- "kCASE",
- "kWHEN",
- "kWHILE",
- "kUNTIL",
- "kFOR",
- "kBREAK",
- "kNEXT",
- "kREDO",
- "kRETRY",
- "kIN",
- "kDO",
- "kDO_COND",
- "kDO_BLOCK",
- "kDO_LAMBDA",
- "kRETURN",
- "kYIELD",
- "kSUPER",
- "kSELF",
- "kNIL",
- "kTRUE",
- "kFALSE",
- "kAND",
- "kOR",
- "kNOT",
- "kIF_MOD",
- "kUNLESS_MOD",
- "kWHILE_MOD",
- "kUNTIL_MOD",
- "kRESCUE_MOD",
- "kALIAS",
- "kDEFINED",
- "klBEGIN",
- "klEND",
- "k__LINE__",
- "k__FILE__",
- "k__ENCODING__",
- "tIDENTIFIER",
- "tFID",
- "tGVAR",
- "tIVAR",
- "tCONSTANT",
- "tLABEL",
- "tCVAR",
- "tNTH_REF",
- "tBACK_REF",
- "tSTRING_CONTENT",
- "tINTEGER",
- "tFLOAT",
- "tREGEXP_END",
- "tUPLUS",
- "tUMINUS",
- "tUMINUS_NUM",
- "tPOW",
- "tCMP",
- "tEQ",
- "tEQQ",
- "tNEQ",
- "tGEQ",
- "tLEQ",
- "tANDOP",
- "tOROP",
- "tMATCH",
- "tNMATCH",
- "tJSDOT",
- "tDOT",
- "tDOT2",
- "tDOT3",
- "tAREF",
- "tASET",
- "tLSHFT",
- "tRSHFT",
- "tCOLON2",
- "tCOLON3",
- "tOP_ASGN",
- "tASSOC",
- "tLPAREN",
- "tLPAREN2",
- "tRPAREN",
- "tLPAREN_ARG",
- "ARRAY_BEG",
- "tRBRACK",
- "tLBRACE",
- "tLBRACE_ARG",
- "tSTAR",
- "tSTAR2",
- "tAMPER",
- "tAMPER2",
- "tTILDE",
- "tPERCENT",
- "tDIVIDE",
- "tPLUS",
- "tMINUS",
- "tLT",
- "tGT",
- "tPIPE",
- "tBANG",
- "tCARET",
- "tLCURLY",
- "tRCURLY",
- "tBACK_REF2",
- "tSYMBEG",
- "tSTRING_BEG",
- "tXSTRING_BEG",
- "tREGEXP_BEG",
- "tWORDS_BEG",
- "tAWORDS_BEG",
- "tSTRING_DBEG",
- "tSTRING_DVAR",
- "tSTRING_END",
- "tSTRING",
- "tSYMBOL",
- "tNL",
- "tEH",
- "tCOLON",
- "tCOMMA",
- "tSPACE",
- "tSEMI",
- "tLAMBDA",
- "tLAMBEG",
- "tLBRACK2",
- "tLBRACK",
- "tJSLBRACK",
- "tDSTAR",
- "tEQL",
- "tLOWEST",
- "\"-@NUM\"",
- "\"+@NUM\"",
- "$start",
- "program",
- "top_compstmt",
- "top_stmts",
- "opt_terms",
- "top_stmt",
- "terms",
- "stmt",
- "bodystmt",
- "compstmt",
- "opt_rescue",
- "opt_else",
- "opt_ensure",
- "stmts",
- "fitem",
- "undef_list",
- "expr_value",
- "lhs",
- "command_call",
- "mlhs",
- "var_lhs",
- "primary_value",
- "aref_args",
- "backref",
- "mrhs",
- "arg_value",
- "expr",
- "@1",
- "arg",
- "command",
- "block_command",
- "call_args",
- "block_call",
- "operation2",
- "command_args",
- "cmd_brace_block",
- "opt_block_var",
- "operation",
- "mlhs_basic",
- "mlhs_entry",
- "mlhs_head",
- "mlhs_item",
- "mlhs_node",
- "mlhs_post",
- "variable",
- "cname",
- "cpath",
- "fname",
- "op",
- "reswords",
- "symbol",
- "opt_nl",
- "primary",
- "none",
- "args",
- "trailer",
- "assocs",
- "paren_args",
- "opt_call_args",
- "rparen",
- "opt_paren_args",
- "opt_block_arg",
- "block_arg",
- "call_args2",
- "open_args",
- "@2",
- "literal",
- "strings",
- "xstring",
- "regexp",
- "words",
- "awords",
- "var_ref",
- "assoc_list",
- "brace_block",
- "method_call",
- "lambda",
- "then",
- "if_tail",
- "do",
- "case_body",
- "for_var",
- "superclass",
- "term",
- "f_arglist",
- "singleton",
- "dot_or_colon",
- "@3",
- "@4",
- "@5",
- "@6",
- "@7",
- "@8",
- "@9",
- "@10",
- "@11",
- "@12",
- "@13",
- "@14",
- "@15",
- "@16",
- "@17",
- "f_larglist",
- "lambda_body",
- "block_param",
- "f_block_optarg",
- "f_block_opt",
- "block_args_tail",
- "f_block_arg",
- "opt_block_args_tail",
- "f_arg",
- "f_rest_arg",
- "do_block",
- "@18",
- "operation3",
- "@19",
- "@20",
- "cases",
- "@21",
- "exc_list",
- "exc_var",
- "numeric",
- "dsym",
- "string",
- "string1",
- "string_contents",
- "xstring_contents",
- "word_list",
- "word",
- "string_content",
- "qword_list",
- "string_dvar",
- "@22",
- "@23",
- "sym",
- "f_args",
- "kwrest_mark",
- "f_kwrest",
- "f_label",
- "f_kw",
- "f_kwarg",
- "args_tail",
- "opt_f_block_arg",
- "opt_args_tail",
- "f_optarg",
- "f_norm_arg",
- "f_bad_arg",
- "f_arg_item",
- "f_margs",
- "f_marg",
- "f_marg_list",
- "f_opt",
- "restarg_mark",
- "blkarg_mark",
- "assoc" ]
-Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)
-
-Racc_debug_parser = false
-
-##### State transition tables end #####
-
-# reduce 0 omitted
-
-# reduce 1 omitted
-
-module_eval(<<'.,.,', 'opal.y', 70)
- def _reduce_2(val, _values, result)
- result = new_compstmt val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 75)
- def _reduce_3(val, _values, result)
- result = new_block
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 79)
- def _reduce_4(val, _values, result)
- result = new_block val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 83)
- def _reduce_5(val, _values, result)
- val[0] << val[2]
- result = val[0]
-
- result
- end
-.,.,
-
-# reduce 6 omitted
-
-module_eval(<<'.,.,', 'opal.y', 90)
- def _reduce_7(val, _values, result)
- result = val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 95)
- def _reduce_8(val, _values, result)
- result = new_body(val[0], val[1], val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 100)
- def _reduce_9(val, _values, result)
- result = new_compstmt val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 105)
- def _reduce_10(val, _values, result)
- result = new_block
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 109)
- def _reduce_11(val, _values, result)
- result = new_block val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 113)
- def _reduce_12(val, _values, result)
- val[0] << val[2]
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 119)
- def _reduce_13(val, _values, result)
- lexer.lex_state = :expr_fname
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 123)
- def _reduce_14(val, _values, result)
- result = new_alias(val[0], val[1], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 127)
- def _reduce_15(val, _values, result)
- result = s(:valias, value(val[1]).to_sym, value(val[2]).to_sym)
-
- result
- end
-.,.,
-
-# reduce 16 omitted
-
-module_eval(<<'.,.,', 'opal.y', 132)
- def _reduce_17(val, _values, result)
- result = s(:valias, value(val[1]).to_sym, value(val[2]).to_sym)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 136)
- def _reduce_18(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 140)
- def _reduce_19(val, _values, result)
- result = new_if(val[1], val[2], val[0], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 144)
- def _reduce_20(val, _values, result)
- result = new_if(val[1], val[2], nil, val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 148)
- def _reduce_21(val, _values, result)
- result = new_while(val[1], val[2], val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 152)
- def _reduce_22(val, _values, result)
- result = new_until(val[1], val[2], val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 156)
- def _reduce_23(val, _values, result)
- result = new_rescue_mod(val[1], val[0], val[2])
-
- result
- end
-.,.,
-
-# reduce 24 omitted
-
-module_eval(<<'.,.,', 'opal.y', 161)
- def _reduce_25(val, _values, result)
- result = new_assign(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 165)
- def _reduce_26(val, _values, result)
- result = s(:masgn, val[0], s(:to_ary, val[2]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 169)
- def _reduce_27(val, _values, result)
- result = new_op_asgn val[1], val[0], val[2]
-
- result
- end
-.,.,
-
-# reduce 28 omitted
-
-# reduce 29 omitted
-
-module_eval(<<'.,.,', 'opal.y', 175)
- def _reduce_30(val, _values, result)
- result = s(:op_asgn2, val[0], op_to_setter(val[2]), value(val[3]).to_sym, val[4])
-
- result
- end
-.,.,
-
-# reduce 31 omitted
-
-# reduce 32 omitted
-
-# reduce 33 omitted
-
-module_eval(<<'.,.,', 'opal.y', 182)
- def _reduce_34(val, _values, result)
- result = new_assign val[0], val[1], s(:svalue, val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 186)
- def _reduce_35(val, _values, result)
- result = s(:masgn, val[0], s(:to_ary, val[2]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 190)
- def _reduce_36(val, _values, result)
- result = s(:masgn, val[0], val[2])
-
- result
- end
-.,.,
-
-# reduce 37 omitted
-
-# reduce 38 omitted
-
-module_eval(<<'.,.,', 'opal.y', 197)
- def _reduce_39(val, _values, result)
- result = s(:and, val[0], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 201)
- def _reduce_40(val, _values, result)
- result = s(:or, val[0], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 205)
- def _reduce_41(val, _values, result)
- result = new_unary_call(['!', []], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 209)
- def _reduce_42(val, _values, result)
- result = new_unary_call(val[0], val[1])
-
- result
- end
-.,.,
-
-# reduce 43 omitted
-
-# reduce 44 omitted
-
-# reduce 45 omitted
-
-# reduce 46 omitted
-
-module_eval(<<'.,.,', 'opal.y', 219)
- def _reduce_47(val, _values, result)
- result = new_return(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 223)
- def _reduce_48(val, _values, result)
- result = new_break(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 227)
- def _reduce_49(val, _values, result)
- result = new_next(val[0], val[1])
-
- result
- end
-.,.,
-
-# reduce 50 omitted
-
-# reduce 51 omitted
-
-# reduce 52 omitted
-
-# reduce 53 omitted
-
-# reduce 54 omitted
-
-module_eval(<<'.,.,', 'opal.y', 239)
- def _reduce_55(val, _values, result)
- result = new_call(nil, val[0], val[1])
-
- result
- end
-.,.,
-
-# reduce 56 omitted
-
-module_eval(<<'.,.,', 'opal.y', 244)
- def _reduce_57(val, _values, result)
- result = new_js_call(val[0], val[2], val[3])
-
- result
- end
-.,.,
-
-# reduce 58 omitted
-
-module_eval(<<'.,.,', 'opal.y', 249)
- def _reduce_59(val, _values, result)
- result = new_call(val[0], val[2], val[3])
-
- result
- end
-.,.,
-
-# reduce 60 omitted
-
-module_eval(<<'.,.,', 'opal.y', 254)
- def _reduce_61(val, _values, result)
- result = new_call(val[0], val[2], val[3])
-
- result
- end
-.,.,
-
-# reduce 62 omitted
-
-module_eval(<<'.,.,', 'opal.y', 259)
- def _reduce_63(val, _values, result)
- result = new_super(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 263)
- def _reduce_64(val, _values, result)
- result = new_yield val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 268)
- def _reduce_65(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 272)
- def _reduce_66(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 277)
- def _reduce_67(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 281)
- def _reduce_68(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 286)
- def _reduce_69(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 290)
- def _reduce_70(val, _values, result)
- result = val[0] << val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 294)
- def _reduce_71(val, _values, result)
- result = val[0] << s(:splat, val[2])
-
- result
- end
-.,.,
-
-# reduce 72 omitted
-
-module_eval(<<'.,.,', 'opal.y', 299)
- def _reduce_73(val, _values, result)
- result = val[0] << s(:splat)
-
- result
- end
-.,.,
-
-# reduce 74 omitted
-
-module_eval(<<'.,.,', 'opal.y', 304)
- def _reduce_75(val, _values, result)
- result = s(:array, s(:splat, val[1]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 308)
- def _reduce_76(val, _values, result)
- result = s(:array, s(:splat))
-
- result
- end
-.,.,
-
-# reduce 77 omitted
-
-module_eval(<<'.,.,', 'opal.y', 314)
- def _reduce_78(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 318)
- def _reduce_79(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 323)
- def _reduce_80(val, _values, result)
- result = s(:array, val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 327)
- def _reduce_81(val, _values, result)
- result = val[0] << val[1]
-
- result
- end
-.,.,
-
-# reduce 82 omitted
-
-# reduce 83 omitted
-
-module_eval(<<'.,.,', 'opal.y', 335)
- def _reduce_84(val, _values, result)
- result = new_assignable val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 339)
- def _reduce_85(val, _values, result)
- args = val[2] ? val[2] : []
- result = s(:attrasgn, val[0], :[]=, s(:arglist, *args))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 344)
- def _reduce_86(val, _values, result)
- result = new_call val[0], val[2], []
-
- result
- end
-.,.,
-
-# reduce 87 omitted
-
-# reduce 88 omitted
-
-# reduce 89 omitted
-
-# reduce 90 omitted
-
-# reduce 91 omitted
-
-module_eval(<<'.,.,', 'opal.y', 354)
- def _reduce_92(val, _values, result)
- result = new_assignable val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 358)
- def _reduce_93(val, _values, result)
- result = new_js_attrasgn(val[0], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 362)
- def _reduce_94(val, _values, result)
- result = new_attrasgn(val[0], :[]=, val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 366)
- def _reduce_95(val, _values, result)
- result = new_attrasgn(val[0], op_to_setter(val[2]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 370)
- def _reduce_96(val, _values, result)
- result = new_attrasgn(val[0], op_to_setter(val[2]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 374)
- def _reduce_97(val, _values, result)
- result = new_attrasgn(val[0], op_to_setter(val[2]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 378)
- def _reduce_98(val, _values, result)
- result = new_colon2(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 382)
- def _reduce_99(val, _values, result)
- result = new_colon3(val[0], val[1])
-
- result
- end
-.,.,
-
-# reduce 100 omitted
-
-# reduce 101 omitted
-
-module_eval(<<'.,.,', 'opal.y', 390)
- def _reduce_102(val, _values, result)
- result = new_colon3(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 394)
- def _reduce_103(val, _values, result)
- result = new_const(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 398)
- def _reduce_104(val, _values, result)
- result = new_colon2(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-# reduce 105 omitted
-
-# reduce 106 omitted
-
-# reduce 107 omitted
-
-module_eval(<<'.,.,', 'opal.y', 406)
- def _reduce_108(val, _values, result)
- lexer.lex_state = :expr_end
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 411)
- def _reduce_109(val, _values, result)
- lexer.lex_state = :expr_end
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 417)
- def _reduce_110(val, _values, result)
- result = new_sym(val[0])
-
- result
- end
-.,.,
-
-# reduce 111 omitted
-
-module_eval(<<'.,.,', 'opal.y', 423)
- def _reduce_112(val, _values, result)
- result = s(:undef, val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 427)
- def _reduce_113(val, _values, result)
- result = val[0] << val[2]
-
- result
- end
-.,.,
-
-# reduce 114 omitted
-
-# reduce 115 omitted
-
-# reduce 116 omitted
-
-# reduce 117 omitted
-
-# reduce 118 omitted
-
-# reduce 119 omitted
-
-# reduce 120 omitted
-
-# reduce 121 omitted
-
-# reduce 122 omitted
-
-# reduce 123 omitted
-
-# reduce 124 omitted
-
-# reduce 125 omitted
-
-# reduce 126 omitted
-
-# reduce 127 omitted
-
-# reduce 128 omitted
-
-# reduce 129 omitted
-
-# reduce 130 omitted
-
-# reduce 131 omitted
-
-# reduce 132 omitted
-
-# reduce 133 omitted
-
-# reduce 134 omitted
-
-# reduce 135 omitted
-
-# reduce 136 omitted
-
-# reduce 137 omitted
-
-# reduce 138 omitted
-
-# reduce 139 omitted
-
-# reduce 140 omitted
-
-# reduce 141 omitted
-
-# reduce 142 omitted
-
-# reduce 143 omitted
-
-# reduce 144 omitted
-
-# reduce 145 omitted
-
-# reduce 146 omitted
-
-# reduce 147 omitted
-
-# reduce 148 omitted
-
-# reduce 149 omitted
-
-# reduce 150 omitted
-
-# reduce 151 omitted
-
-# reduce 152 omitted
-
-# reduce 153 omitted
-
-# reduce 154 omitted
-
-# reduce 155 omitted
-
-# reduce 156 omitted
-
-# reduce 157 omitted
-
-# reduce 158 omitted
-
-# reduce 159 omitted
-
-# reduce 160 omitted
-
-# reduce 161 omitted
-
-# reduce 162 omitted
-
-# reduce 163 omitted
-
-# reduce 164 omitted
-
-# reduce 165 omitted
-
-# reduce 166 omitted
-
-# reduce 167 omitted
-
-# reduce 168 omitted
-
-# reduce 169 omitted
-
-# reduce 170 omitted
-
-# reduce 171 omitted
-
-# reduce 172 omitted
-
-# reduce 173 omitted
-
-# reduce 174 omitted
-
-# reduce 175 omitted
-
-# reduce 176 omitted
-
-# reduce 177 omitted
-
-# reduce 178 omitted
-
-# reduce 179 omitted
-
-# reduce 180 omitted
-
-# reduce 181 omitted
-
-# reduce 182 omitted
-
-# reduce 183 omitted
-
-# reduce 184 omitted
-
-# reduce 185 omitted
-
-# reduce 186 omitted
-
-# reduce 187 omitted
-
-module_eval(<<'.,.,', 'opal.y', 447)
- def _reduce_188(val, _values, result)
- result = new_assign(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 451)
- def _reduce_189(val, _values, result)
- result = new_assign val[0], val[1], s(:rescue_mod, val[2], val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 455)
- def _reduce_190(val, _values, result)
- result = new_op_asgn val[1], val[0], val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 459)
- def _reduce_191(val, _values, result)
- result = new_op_asgn1(val[0], val[2], val[4], val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 463)
- def _reduce_192(val, _values, result)
- raise ".JS[...] #{val[4]} is not supported"
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 467)
- def _reduce_193(val, _values, result)
- result = s(:op_asgn2, val[0], op_to_setter(val[2]), value(val[3]).to_sym, val[4])
-
- result
- end
-.,.,
-
-# reduce 194 omitted
-
-# reduce 195 omitted
-
-# reduce 196 omitted
-
-# reduce 197 omitted
-
-# reduce 198 omitted
-
-module_eval(<<'.,.,', 'opal.y', 476)
- def _reduce_199(val, _values, result)
- result = new_irange(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 480)
- def _reduce_200(val, _values, result)
- result = new_erange(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 484)
- def _reduce_201(val, _values, result)
- result = new_binary_call(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 488)
- def _reduce_202(val, _values, result)
- result = new_binary_call(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 492)
- def _reduce_203(val, _values, result)
- result = new_binary_call(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 496)
- def _reduce_204(val, _values, result)
- result = new_binary_call(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 500)
- def _reduce_205(val, _values, result)
- result = new_binary_call(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 504)
- def _reduce_206(val, _values, result)
- result = new_binary_call(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 508)
- def _reduce_207(val, _values, result)
- result = new_call new_binary_call(new_int(val[1]), val[2], val[3]), [:"-@", []], []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 512)
- def _reduce_208(val, _values, result)
- result = new_call new_binary_call(new_float(val[1]), val[2], val[3]), [:"-@", []], []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 516)
- def _reduce_209(val, _values, result)
- result = new_call val[1], [:"+@", []], []
- if [:int, :float].include? val[1].type
- result = val[1]
- end
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 523)
- def _reduce_210(val, _values, result)
- result = new_call val[1], [:"-@", []], []
- if val[1].type == :int
- val[1][1] = -val[1][1]
- result = val[1]
- elsif val[1].type == :float
- val[1][1] = -val[1][1].to_f
- result = val[1]
- end
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 534)
- def _reduce_211(val, _values, result)
- result = new_binary_call(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 538)
- def _reduce_212(val, _values, result)
- result = new_binary_call(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 542)
- def _reduce_213(val, _values, result)
- result = new_binary_call(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 546)
- def _reduce_214(val, _values, result)
- result = new_binary_call(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 550)
- def _reduce_215(val, _values, result)
- result = new_binary_call(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 554)
- def _reduce_216(val, _values, result)
- result = new_binary_call(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 558)
- def _reduce_217(val, _values, result)
- result = new_binary_call(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 562)
- def _reduce_218(val, _values, result)
- result = new_binary_call(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 566)
- def _reduce_219(val, _values, result)
- result = new_binary_call(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 570)
- def _reduce_220(val, _values, result)
- result = new_binary_call(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 574)
- def _reduce_221(val, _values, result)
- result = new_binary_call(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 578)
- def _reduce_222(val, _values, result)
- result = new_binary_call(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 582)
- def _reduce_223(val, _values, result)
- result = new_binary_call(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 586)
- def _reduce_224(val, _values, result)
- result = new_unary_call(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 590)
- def _reduce_225(val, _values, result)
- result = new_unary_call(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 594)
- def _reduce_226(val, _values, result)
- result = new_binary_call(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 598)
- def _reduce_227(val, _values, result)
- result = new_binary_call(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 602)
- def _reduce_228(val, _values, result)
- result = new_and(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 606)
- def _reduce_229(val, _values, result)
- result = new_or(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 610)
- def _reduce_230(val, _values, result)
- result = s(:defined, val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 614)
- def _reduce_231(val, _values, result)
- result = new_if(val[1], val[0], val[2], val[4])
-
- result
- end
-.,.,
-
-# reduce 232 omitted
-
-# reduce 233 omitted
-
-module_eval(<<'.,.,', 'opal.y', 622)
- def _reduce_234(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 626)
- def _reduce_235(val, _values, result)
- result = [val[0]]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 630)
- def _reduce_236(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 634)
- def _reduce_237(val, _values, result)
- val[0] << s(:hash, *val[2])
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 639)
- def _reduce_238(val, _values, result)
- result = [s(:hash, *val[0])]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 644)
- def _reduce_239(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-# reduce 240 omitted
-
-module_eval(<<'.,.,', 'opal.y', 651)
- def _reduce_241(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-# reduce 242 omitted
-
-module_eval(<<'.,.,', 'opal.y', 657)
- def _reduce_243(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-# reduce 244 omitted
-
-module_eval(<<'.,.,', 'opal.y', 662)
- def _reduce_245(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 666)
- def _reduce_246(val, _values, result)
- result = val[0]
- result << new_hash(nil, val[2], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 671)
- def _reduce_247(val, _values, result)
- result = [new_hash(nil, val[0], nil)]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 676)
- def _reduce_248(val, _values, result)
- result = [val[0]]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 680)
- def _reduce_249(val, _values, result)
- result = val[0]
- add_block_pass val[0], val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 685)
- def _reduce_250(val, _values, result)
- result = [new_hash(nil, val[0], nil)]
- add_block_pass result, val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 690)
- def _reduce_251(val, _values, result)
- result = val[0]
- result << new_hash(nil, val[2], nil)
- result << val[3] if val[3]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 696)
- def _reduce_252(val, _values, result)
- result = []
- add_block_pass result, val[0]
-
- result
- end
-.,.,
-
-# reduce 253 omitted
-
-# reduce 254 omitted
-
-module_eval(<<'.,.,', 'opal.y', 704)
- def _reduce_255(val, _values, result)
- lexer.cmdarg_push 1
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 708)
- def _reduce_256(val, _values, result)
- lexer.cmdarg_pop
- result = val[1]
-
- result
- end
-.,.,
-
-# reduce 257 omitted
-
-module_eval(<<'.,.,', 'opal.y', 715)
- def _reduce_258(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 719)
- def _reduce_259(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 724)
- def _reduce_260(val, _values, result)
- result = new_block_pass(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 729)
- def _reduce_261(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 733)
- def _reduce_262(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 738)
- def _reduce_263(val, _values, result)
- result = [val[0]]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 742)
- def _reduce_264(val, _values, result)
- result = [new_splat(val[0], val[1])]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 746)
- def _reduce_265(val, _values, result)
- result = val[0] << val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 750)
- def _reduce_266(val, _values, result)
- result = val[0] << new_splat(val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 755)
- def _reduce_267(val, _values, result)
- val[0] << val[2]
- result = s(:array, *val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 760)
- def _reduce_268(val, _values, result)
- val[0] << s(:splat, val[3])
- result = s(:array, *val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 765)
- def _reduce_269(val, _values, result)
- result = s(:splat, val[1])
-
- result
- end
-.,.,
-
-# reduce 270 omitted
-
-# reduce 271 omitted
-
-# reduce 272 omitted
-
-# reduce 273 omitted
-
-# reduce 274 omitted
-
-# reduce 275 omitted
-
-# reduce 276 omitted
-
-# reduce 277 omitted
-
-# reduce 278 omitted
-
-module_eval(<<'.,.,', 'opal.y', 779)
- def _reduce_279(val, _values, result)
- result = lexer.line
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 783)
- def _reduce_280(val, _values, result)
- result = s(:begin, val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 787)
- def _reduce_281(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 791)
- def _reduce_282(val, _values, result)
- result = new_paren(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 795)
- def _reduce_283(val, _values, result)
- result = new_colon2(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 799)
- def _reduce_284(val, _values, result)
- result = new_colon3(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 803)
- def _reduce_285(val, _values, result)
- result = new_call val[0], [:[], []], val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 807)
- def _reduce_286(val, _values, result)
- result = new_js_call val[0], [:[], []], val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 811)
- def _reduce_287(val, _values, result)
- result = new_array(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 815)
- def _reduce_288(val, _values, result)
- result = new_hash(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 819)
- def _reduce_289(val, _values, result)
- result = new_return(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 823)
- def _reduce_290(val, _values, result)
- result = new_yield val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 827)
- def _reduce_291(val, _values, result)
- result = s(:yield)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 831)
- def _reduce_292(val, _values, result)
- result = s(:yield)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 835)
- def _reduce_293(val, _values, result)
- result = s(:defined, val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 839)
- def _reduce_294(val, _values, result)
- result = new_unary_call(['!', []], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 843)
- def _reduce_295(val, _values, result)
- result = new_unary_call(['!', []], new_nil(val[0]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 847)
- def _reduce_296(val, _values, result)
- result = new_call(nil, val[0], [])
- result << val[1]
-
- result
- end
-.,.,
-
-# reduce 297 omitted
-
-module_eval(<<'.,.,', 'opal.y', 853)
- def _reduce_298(val, _values, result)
- val[0] << val[1]
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 858)
- def _reduce_299(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 862)
- def _reduce_300(val, _values, result)
- result = new_if(val[0], val[1], val[3], val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 866)
- def _reduce_301(val, _values, result)
- result = new_if(val[0], val[1], val[4], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 870)
- def _reduce_302(val, _values, result)
- lexer.cond_push 1
- result = lexer.line
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 875)
- def _reduce_303(val, _values, result)
- lexer.cond_pop
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 879)
- def _reduce_304(val, _values, result)
- result = s(:while, val[2], val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 883)
- def _reduce_305(val, _values, result)
- lexer.cond_push 1
- result = lexer.line
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 888)
- def _reduce_306(val, _values, result)
- lexer.cond_pop
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 892)
- def _reduce_307(val, _values, result)
- result = s(:until, val[2], val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 896)
- def _reduce_308(val, _values, result)
- result = s(:case, val[1], *val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 900)
- def _reduce_309(val, _values, result)
- result = s(:case, nil, *val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 904)
- def _reduce_310(val, _values, result)
- result = s(:case, nil, val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 908)
- def _reduce_311(val, _values, result)
- lexer.cond_push 1
- result = lexer.line
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 913)
- def _reduce_312(val, _values, result)
- lexer.cond_pop
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 917)
- def _reduce_313(val, _values, result)
- result = s(:for, val[4], val[1], val[7])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 921)
- def _reduce_314(val, _values, result)
- # ...
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 925)
- def _reduce_315(val, _values, result)
- result = new_class val[0], val[1], val[2], val[4], val[5]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 929)
- def _reduce_316(val, _values, result)
- result = lexer.line
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 933)
- def _reduce_317(val, _values, result)
- # ...
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 937)
- def _reduce_318(val, _values, result)
- result = new_sclass(val[0], val[3], val[6], val[7])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 941)
- def _reduce_319(val, _values, result)
- result = lexer.line
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 945)
- def _reduce_320(val, _values, result)
- # ...
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 949)
- def _reduce_321(val, _values, result)
- result = new_module(val[0], val[2], val[4], val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 953)
- def _reduce_322(val, _values, result)
- push_scope
- lexer.lex_state = :expr_endfn
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 958)
- def _reduce_323(val, _values, result)
- result = new_def(val[0], nil, val[1], val[3], val[4], val[5])
- pop_scope
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 963)
- def _reduce_324(val, _values, result)
- lexer.lex_state = :expr_fname
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 967)
- def _reduce_325(val, _values, result)
- push_scope
- lexer.lex_state = :expr_endfn
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 972)
- def _reduce_326(val, _values, result)
- result = new_def(val[0], val[1], val[4], val[6], val[7], val[8])
- pop_scope
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 977)
- def _reduce_327(val, _values, result)
- result = new_break(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 981)
- def _reduce_328(val, _values, result)
- result = s(:next)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 985)
- def _reduce_329(val, _values, result)
- result = s(:redo)
-
- result
- end
-.,.,
-
-# reduce 330 omitted
-
-# reduce 331 omitted
-
-# reduce 332 omitted
-
-# reduce 333 omitted
-
-# reduce 334 omitted
-
-# reduce 335 omitted
-
-# reduce 336 omitted
-
-# reduce 337 omitted
-
-# reduce 338 omitted
-
-module_eval(<<'.,.,', 'opal.y', 1002)
- def _reduce_339(val, _values, result)
- result = new_call nil, [:lambda, []], []
- result << new_iter(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1008)
- def _reduce_340(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1012)
- def _reduce_341(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-# reduce 342 omitted
-
-# reduce 343 omitted
-
-module_eval(<<'.,.,', 'opal.y', 1019)
- def _reduce_344(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1023)
- def _reduce_345(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1028)
- def _reduce_346(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1032)
- def _reduce_347(val, _values, result)
- result = new_if(val[0], val[1], val[3], val[4])
-
- result
- end
-.,.,
-
-# reduce 348 omitted
-
-module_eval(<<'.,.,', 'opal.y', 1038)
- def _reduce_349(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1043)
- def _reduce_350(val, _values, result)
- result = s(:block, val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1047)
- def _reduce_351(val, _values, result)
- val[0] << val[2]
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1053)
- def _reduce_352(val, _values, result)
- result = new_assign(new_assignable(new_ident(
- val[0])), val[1], val[2])
-
- result
- end
-.,.,
-
-# reduce 353 omitted
-
-module_eval(<<'.,.,', 'opal.y', 1060)
- def _reduce_354(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1064)
- def _reduce_355(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1068)
- def _reduce_356(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1073)
- def _reduce_357(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1078)
- def _reduce_358(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1082)
- def _reduce_359(val, _values, result)
- nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1087)
- def _reduce_360(val, _values, result)
- result = new_block_args(val[0], val[2], val[4], val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1091)
- def _reduce_361(val, _values, result)
- result = new_block_args(val[0], val[2], nil, val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1095)
- def _reduce_362(val, _values, result)
- result = new_block_args(val[0], nil, val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1099)
- def _reduce_363(val, _values, result)
- result = new_block_args(val[0], nil, nil, nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1103)
- def _reduce_364(val, _values, result)
- result = new_block_args(val[0], nil, nil, val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1107)
- def _reduce_365(val, _values, result)
- result = new_block_args(nil, val[0], val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1111)
- def _reduce_366(val, _values, result)
- result = new_block_args(nil, val[0], nil, val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1115)
- def _reduce_367(val, _values, result)
- result = new_block_args(nil, nil, val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1119)
- def _reduce_368(val, _values, result)
- result = new_block_args(nil, nil, nil, val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1124)
- def _reduce_369(val, _values, result)
- push_scope :block
- result = lexer.line
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1129)
- def _reduce_370(val, _values, result)
- result = new_iter val[2], val[3]
- pop_scope
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1135)
- def _reduce_371(val, _values, result)
- val[0] << val[1]
- result = val[0]
-
- result
- end
-.,.,
-
-# reduce 372 omitted
-
-# reduce 373 omitted
-
-# reduce 374 omitted
-
-module_eval(<<'.,.,', 'opal.y', 1144)
- def _reduce_375(val, _values, result)
- result = new_call(nil, val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1148)
- def _reduce_376(val, _values, result)
- result = new_call(val[0], val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1152)
- def _reduce_377(val, _values, result)
- result = new_js_call(val[0], val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1156)
- def _reduce_378(val, _values, result)
- result = new_call(val[0], [:call, []], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1160)
- def _reduce_379(val, _values, result)
- result = new_call(val[0], val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1164)
- def _reduce_380(val, _values, result)
- result = new_call(val[0], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1168)
- def _reduce_381(val, _values, result)
- result = new_super(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1172)
- def _reduce_382(val, _values, result)
- result = new_super(val[0], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1177)
- def _reduce_383(val, _values, result)
- push_scope :block
- result = lexer.line
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1182)
- def _reduce_384(val, _values, result)
- result = new_iter val[2], val[3]
- pop_scope
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1187)
- def _reduce_385(val, _values, result)
- push_scope :block
- result = lexer.line
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1192)
- def _reduce_386(val, _values, result)
- result = new_iter val[2], val[3]
- pop_scope
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1198)
- def _reduce_387(val, _values, result)
- result = lexer.line
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1202)
- def _reduce_388(val, _values, result)
- part = s(:when, s(:array, *val[2]), val[4])
- result = [part]
- result.push(*val[5]) if val[5]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1209)
- def _reduce_389(val, _values, result)
- result = [val[0]]
-
- result
- end
-.,.,
-
-# reduce 390 omitted
-
-module_eval(<<'.,.,', 'opal.y', 1215)
- def _reduce_391(val, _values, result)
- exc = val[1] || s(:array)
- exc << new_assign(val[2], val[2], s(:gvar, '$!'.intern)) if val[2]
- result = [s(:resbody, exc, val[4])]
- result.push val[5].first if val[5]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1222)
- def _reduce_392(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1227)
- def _reduce_393(val, _values, result)
- result = s(:array, val[0])
-
- result
- end
-.,.,
-
-# reduce 394 omitted
-
-# reduce 395 omitted
-
-module_eval(<<'.,.,', 'opal.y', 1234)
- def _reduce_396(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1238)
- def _reduce_397(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1243)
- def _reduce_398(val, _values, result)
- result = val[1].nil? ? s(:nil) : val[1]
-
- result
- end
-.,.,
-
-# reduce 399 omitted
-
-# reduce 400 omitted
-
-# reduce 401 omitted
-
-# reduce 402 omitted
-
-module_eval(<<'.,.,', 'opal.y', 1253)
- def _reduce_403(val, _values, result)
- result = new_str val[0]
-
- result
- end
-.,.,
-
-# reduce 404 omitted
-
-module_eval(<<'.,.,', 'opal.y', 1259)
- def _reduce_405(val, _values, result)
- result = str_append val[0], val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1264)
- def _reduce_406(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1268)
- def _reduce_407(val, _values, result)
- result = s(:str, value(val[0]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1273)
- def _reduce_408(val, _values, result)
- result = new_xstr(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1278)
- def _reduce_409(val, _values, result)
- result = new_regexp val[1], val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1283)
- def _reduce_410(val, _values, result)
- result = s(:array)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1287)
- def _reduce_411(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1292)
- def _reduce_412(val, _values, result)
- result = s(:array)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1296)
- def _reduce_413(val, _values, result)
- part = val[1]
- part = s(:dstr, "", val[1]) if part.type == :evstr
- result = val[0] << part
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1303)
- def _reduce_414(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1307)
- def _reduce_415(val, _values, result)
- result = val[0].concat([val[1]])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1312)
- def _reduce_416(val, _values, result)
- result = s(:array)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1316)
- def _reduce_417(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1321)
- def _reduce_418(val, _values, result)
- result = s(:array)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1325)
- def _reduce_419(val, _values, result)
- result = val[0] << s(:str, value(val[1]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1330)
- def _reduce_420(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1334)
- def _reduce_421(val, _values, result)
- result = str_append val[0], val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1339)
- def _reduce_422(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1343)
- def _reduce_423(val, _values, result)
- result = str_append val[0], val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1348)
- def _reduce_424(val, _values, result)
- result = new_str_content(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1352)
- def _reduce_425(val, _values, result)
- result = lexer.strterm
- lexer.strterm = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1357)
- def _reduce_426(val, _values, result)
- lexer.strterm = val[1]
- result = new_evstr(val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1362)
- def _reduce_427(val, _values, result)
- lexer.cond_push 0
- lexer.cmdarg_push 0
- result = lexer.strterm
- lexer.strterm = nil
- lexer.lex_state = :expr_beg
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1370)
- def _reduce_428(val, _values, result)
- lexer.strterm = val[1]
- lexer.cond_lexpop
- lexer.cmdarg_lexpop
- result = new_evstr(val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1378)
- def _reduce_429(val, _values, result)
- result = new_gvar(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1382)
- def _reduce_430(val, _values, result)
- result = new_ivar(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1386)
- def _reduce_431(val, _values, result)
- result = new_cvar(val[0])
-
- result
- end
-.,.,
-
-# reduce 432 omitted
-
-module_eval(<<'.,.,', 'opal.y', 1393)
- def _reduce_433(val, _values, result)
- result = new_sym(val[1])
- lexer.lex_state = :expr_end
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1398)
- def _reduce_434(val, _values, result)
- result = new_sym(val[0])
-
- result
- end
-.,.,
-
-# reduce 435 omitted
-
-# reduce 436 omitted
-
-# reduce 437 omitted
-
-# reduce 438 omitted
-
-module_eval(<<'.,.,', 'opal.y', 1408)
- def _reduce_439(val, _values, result)
- result = new_dsym val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1413)
- def _reduce_440(val, _values, result)
- result = new_int(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1417)
- def _reduce_441(val, _values, result)
- result = new_float(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1421)
- def _reduce_442(val, _values, result)
- result = negate_num(new_int(val[1]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1425)
- def _reduce_443(val, _values, result)
- result = negate_num(new_float(val[1]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1429)
- def _reduce_444(val, _values, result)
- result = new_int(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1433)
- def _reduce_445(val, _values, result)
- result = new_float(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1438)
- def _reduce_446(val, _values, result)
- result = new_ident(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1442)
- def _reduce_447(val, _values, result)
- result = new_ivar(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1446)
- def _reduce_448(val, _values, result)
- result = new_gvar(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1450)
- def _reduce_449(val, _values, result)
- result = new_const(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1454)
- def _reduce_450(val, _values, result)
- result = new_cvar(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1458)
- def _reduce_451(val, _values, result)
- result = new_nil(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1462)
- def _reduce_452(val, _values, result)
- result = new_self(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1466)
- def _reduce_453(val, _values, result)
- result = new_true(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1470)
- def _reduce_454(val, _values, result)
- result = new_false(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1474)
- def _reduce_455(val, _values, result)
- result = new___FILE__(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1478)
- def _reduce_456(val, _values, result)
- result = new___LINE__(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1483)
- def _reduce_457(val, _values, result)
- result = new_var_ref(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1488)
- def _reduce_458(val, _values, result)
- result = new_assignable val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1493)
- def _reduce_459(val, _values, result)
- result = s(:nth_ref, value(val[0]))
-
- result
- end
-.,.,
-
-# reduce 460 omitted
-
-module_eval(<<'.,.,', 'opal.y', 1499)
- def _reduce_461(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1503)
- def _reduce_462(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1507)
- def _reduce_463(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1512)
- def _reduce_464(val, _values, result)
- result = val[1]
- lexer.lex_state = :expr_beg
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1517)
- def _reduce_465(val, _values, result)
- result = val[0]
- lexer.lex_state = :expr_beg
-
- result
- end
-.,.,
-
-# reduce 466 omitted
-
-# reduce 467 omitted
-
-module_eval(<<'.,.,', 'opal.y', 1526)
- def _reduce_468(val, _values, result)
- result = new_kwrestarg(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1530)
- def _reduce_469(val, _values, result)
- result = new_kwrestarg()
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1535)
- def _reduce_470(val, _values, result)
- result = new_sym(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1540)
- def _reduce_471(val, _values, result)
- result = new_kwoptarg(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1544)
- def _reduce_472(val, _values, result)
- result = new_kwarg(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1549)
- def _reduce_473(val, _values, result)
- result = [val[0]]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1553)
- def _reduce_474(val, _values, result)
- result = val[0]
- result << val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1559)
- def _reduce_475(val, _values, result)
- result = new_args_tail(val[0], val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1563)
- def _reduce_476(val, _values, result)
- result = new_args_tail(val[0], nil, val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1567)
- def _reduce_477(val, _values, result)
- result = new_args_tail(nil, val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1571)
- def _reduce_478(val, _values, result)
- result = new_args_tail(nil, nil, val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1576)
- def _reduce_479(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1580)
- def _reduce_480(val, _values, result)
- result = new_args_tail(nil, nil, nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1585)
- def _reduce_481(val, _values, result)
- result = new_args(val[0], val[2], val[4], val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1589)
- def _reduce_482(val, _values, result)
- result = new_args(val[0], val[2], nil, val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1593)
- def _reduce_483(val, _values, result)
- result = new_args(val[0], nil, val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1597)
- def _reduce_484(val, _values, result)
- result = new_args(val[0], nil, nil, val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1601)
- def _reduce_485(val, _values, result)
- result = new_args(nil, val[0], val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1605)
- def _reduce_486(val, _values, result)
- result = new_args(nil, val[0], nil, val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1609)
- def _reduce_487(val, _values, result)
- result = new_args(nil, nil, val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1613)
- def _reduce_488(val, _values, result)
- result = new_args(nil, nil, nil, val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1617)
- def _reduce_489(val, _values, result)
- result = new_args(nil, nil, nil, nil)
-
- result
- end
-.,.,
-
-# reduce 490 omitted
-
-module_eval(<<'.,.,', 'opal.y', 1623)
- def _reduce_491(val, _values, result)
- result = value(val[0]).to_sym
- scope.add_local result
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1629)
- def _reduce_492(val, _values, result)
- raise 'formal argument cannot be a constant'
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1633)
- def _reduce_493(val, _values, result)
- raise 'formal argument cannot be an instance variable'
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1637)
- def _reduce_494(val, _values, result)
- raise 'formal argument cannot be a class variable'
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1641)
- def _reduce_495(val, _values, result)
- raise 'formal argument cannot be a global variable'
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1646)
- def _reduce_496(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1650)
- def _reduce_497(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-# reduce 498 omitted
-
-# reduce 499 omitted
-
-module_eval(<<'.,.,', 'opal.y', 1658)
- def _reduce_500(val, _values, result)
- result = s(:lasgn, val[0])
-
- result
- end
-.,.,
-
-# reduce 501 omitted
-
-module_eval(<<'.,.,', 'opal.y', 1664)
- def _reduce_502(val, _values, result)
- result = s(:array, val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1668)
- def _reduce_503(val, _values, result)
- val[0] << val[2]
- result = val[0]
-
- result
- end
-.,.,
-
-# reduce 504 omitted
-
-# reduce 505 omitted
-
-# reduce 506 omitted
-
-# reduce 507 omitted
-
-# reduce 508 omitted
-
-module_eval(<<'.,.,', 'opal.y', 1680)
- def _reduce_509(val, _values, result)
- result = [val[0]]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1684)
- def _reduce_510(val, _values, result)
- val[0] << val[2]
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1690)
- def _reduce_511(val, _values, result)
- result = new_assign(new_assignable(new_ident(val[0])), val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1695)
- def _reduce_512(val, _values, result)
- result = s(:block, val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1699)
- def _reduce_513(val, _values, result)
- result = val[0]
- val[0] << val[2]
-
- result
- end
-.,.,
-
-# reduce 514 omitted
-
-# reduce 515 omitted
-
-module_eval(<<'.,.,', 'opal.y', 1708)
- def _reduce_516(val, _values, result)
- result = "*#{value(val[1])}".to_sym
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1712)
- def _reduce_517(val, _values, result)
- result = :"*"
-
- result
- end
-.,.,
-
-# reduce 518 omitted
-
-# reduce 519 omitted
-
-module_eval(<<'.,.,', 'opal.y', 1720)
- def _reduce_520(val, _values, result)
- result = "&#{value(val[1])}".to_sym
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1725)
- def _reduce_521(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1729)
- def _reduce_522(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1734)
- def _reduce_523(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1738)
- def _reduce_524(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1743)
- def _reduce_525(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1747)
- def _reduce_526(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1752)
- def _reduce_527(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1756)
- def _reduce_528(val, _values, result)
- result = val[0].push(*val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1761)
- def _reduce_529(val, _values, result)
- result = [val[0], val[2]]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'opal.y', 1765)
- def _reduce_530(val, _values, result)
- result = [new_sym(val[0]), val[1]]
-
- result
- end
-.,.,
-
-# reduce 531 omitted
-
-# reduce 532 omitted
-
-# reduce 533 omitted
-
-# reduce 534 omitted
-
-# reduce 535 omitted
-
-# reduce 536 omitted
-
-# reduce 537 omitted
-
-# reduce 538 omitted
-
-# reduce 539 omitted
-
-# reduce 540 omitted
-
-# reduce 541 omitted
-
-# reduce 542 omitted
-
-# reduce 543 omitted
-
-# reduce 544 omitted
-
-# reduce 545 omitted
-
-# reduce 546 omitted
-
-# reduce 547 omitted
-
-# reduce 548 omitted
-
-# reduce 549 omitted
-
-# reduce 550 omitted
-
-# reduce 551 omitted
-
-# reduce 552 omitted
-
-# reduce 553 omitted
-
-module_eval(<<'.,.,', 'opal.y', 1802)
- def _reduce_554(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-def _reduce_none(val, _values, result)
- val[0]
-end
-
- end # class Parser
-end # module Opal
diff --git a/test/racc/regress/php_serialization b/test/racc/regress/php_serialization
deleted file mode 100644
index b55d75a371..0000000000
--- a/test/racc/regress/php_serialization
+++ /dev/null
@@ -1,338 +0,0 @@
-#
-# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.5.2
-# from Racc grammar file "".
-#
-
-require 'racc/parser.rb'
-
-require 'php_serialization/tokenizer'
-
-module PhpSerialization
- class Unserializer < Racc::Parser
-
-module_eval(<<'...end php_serialization.y/module_eval...', 'php_serialization.y', 84)
- def initialize(tokenizer_klass = Tokenizer)
- @tokenizer_klass = tokenizer_klass
- end
-
- def run(string)
- @tokenizer = @tokenizer_klass.new(string)
- yyparse(@tokenizer, :each)
- return @object
- ensure
- @tokenizer = nil
- end
-
- def next_token
- @tokenizer.next_token
- end
-...end php_serialization.y/module_eval...
-##### State transition tables begin ###
-
-racc_action_table = [
- 9, 10, 16, 17, 11, 12, 13, 18, 14, 9,
- 10, 15, 19, 11, 12, 13, 20, 14, 21, 46,
- 15, 9, 10, 22, 23, 11, 12, 13, 24, 14,
- 9, 10, 15, 25, 11, 12, 13, 26, 14, 27,
- 51, 15, 28, 29, 30, 31, 32, 33, 34, 35,
- 36, 37, 38, 39, 40, 41, 43, 47, 49 ]
-
-racc_action_check = [
- 0, 0, 1, 2, 0, 0, 0, 3, 0, 42,
- 42, 0, 4, 42, 42, 42, 5, 42, 6, 42,
- 42, 45, 45, 10, 11, 45, 45, 45, 12, 45,
- 50, 50, 45, 13, 50, 50, 50, 14, 50, 15,
- 50, 50, 16, 22, 23, 24, 25, 26, 27, 32,
- 33, 34, 35, 36, 37, 39, 41, 43, 47 ]
-
-racc_action_pointer = [
- -3, 2, 1, 5, 10, 14, 16, nil, nil, nil,
- 18, 19, 23, 28, 32, 34, 42, nil, nil, nil,
- nil, nil, 37, 38, 39, 40, 41, 42, nil, nil,
- nil, nil, 44, 45, 46, 42, 43, 42, nil, 50,
- nil, 50, 6, 52, nil, 18, nil, 46, nil, nil,
- 27, nil ]
-
-racc_action_default = [
- -18, -18, -18, -18, -18, -18, -18, -6, -7, -8,
- -18, -18, -18, -18, -18, -18, -18, -1, -2, -3,
- -4, -5, -18, -18, -18, -18, -18, -18, 52, -9,
- -10, -11, -18, -18, -18, -18, -18, -18, -12, -18,
- -15, -18, -18, -18, -14, -18, -17, -18, -16, -15,
- -18, -13 ]
-
-racc_goto_table = [
- 1, 42, nil, nil, nil, nil, nil, nil, nil, nil,
- 50, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 48 ]
-
-racc_goto_check = [
- 1, 9, nil, nil, nil, nil, nil, nil, nil, nil,
- 9, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 1 ]
-
-racc_goto_pointer = [
- nil, 0, nil, nil, nil, nil, nil, nil, nil, -39,
- nil ]
-
-racc_goto_default = [
- nil, 45, 2, 3, 4, 5, 6, 7, 8, nil,
- 44 ]
-
-racc_reduce_table = [
- 0, 0, :racc_error,
- 2, 16, :_reduce_1,
- 2, 16, :_reduce_2,
- 2, 16, :_reduce_3,
- 2, 16, :_reduce_4,
- 2, 16, :_reduce_5,
- 1, 16, :_reduce_6,
- 1, 16, :_reduce_7,
- 1, 17, :_reduce_8,
- 3, 18, :_reduce_9,
- 3, 19, :_reduce_10,
- 3, 20, :_reduce_11,
- 5, 21, :_reduce_12,
- 11, 23, :_reduce_13,
- 2, 24, :_reduce_14,
- 0, 24, :_reduce_15,
- 2, 25, :_reduce_16,
- 7, 22, :_reduce_17 ]
-
-racc_reduce_n = 18
-
-racc_shift_n = 52
-
-racc_token_table = {
- false => 0,
- :error => 1,
- ";" => 2,
- "N" => 3,
- "b" => 4,
- ":" => 5,
- :NUMBER => 6,
- "i" => 7,
- "d" => 8,
- "s" => 9,
- :STRING => 10,
- "O" => 11,
- "{" => 12,
- "}" => 13,
- "a" => 14 }
-
-racc_nt_base = 15
-
-racc_use_result_var = true
-
-Racc_arg = [
- racc_action_table,
- racc_action_check,
- racc_action_default,
- racc_action_pointer,
- racc_goto_table,
- racc_goto_check,
- racc_goto_default,
- racc_goto_pointer,
- racc_nt_base,
- racc_reduce_table,
- racc_token_table,
- racc_shift_n,
- racc_reduce_n,
- racc_use_result_var ]
-Ractor.make_shareable(Racc_arg) if defined?(Ractor)
-
-Racc_token_to_s_table = [
- "$end",
- "error",
- "\";\"",
- "\"N\"",
- "\"b\"",
- "\":\"",
- "NUMBER",
- "\"i\"",
- "\"d\"",
- "\"s\"",
- "STRING",
- "\"O\"",
- "\"{\"",
- "\"}\"",
- "\"a\"",
- "$start",
- "data",
- "null",
- "bool",
- "integer",
- "double",
- "string",
- "assoc_array",
- "object",
- "attribute_list",
- "attribute" ]
-Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)
-
-Racc_debug_parser = false
-
-##### State transition tables end #####
-
-# reduce 0 omitted
-
-module_eval(<<'.,.,', 'php_serialization.y', 6)
- def _reduce_1(val, _values, result)
- @object = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'php_serialization.y', 7)
- def _reduce_2(val, _values, result)
- @object = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'php_serialization.y', 8)
- def _reduce_3(val, _values, result)
- @object = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'php_serialization.y', 9)
- def _reduce_4(val, _values, result)
- @object = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'php_serialization.y', 10)
- def _reduce_5(val, _values, result)
- @object = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'php_serialization.y', 11)
- def _reduce_6(val, _values, result)
- @object = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'php_serialization.y', 12)
- def _reduce_7(val, _values, result)
- @object = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'php_serialization.y', 15)
- def _reduce_8(val, _values, result)
- result = nil
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'php_serialization.y', 18)
- def _reduce_9(val, _values, result)
- result = Integer(val[2]) > 0
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'php_serialization.y', 21)
- def _reduce_10(val, _values, result)
- result = Integer(val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'php_serialization.y', 24)
- def _reduce_11(val, _values, result)
- result = Float(val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'php_serialization.y', 27)
- def _reduce_12(val, _values, result)
- result = val[4]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'php_serialization.y', 32)
- def _reduce_13(val, _values, result)
- if eval("defined?(#{val[4]})")
- result = Object.const_get(val[4]).new
-
- val[9].each do |(attr_name, value)|
- # Protected and private attributes will have a \0..\0 prefix
- attr_name = attr_name.gsub(/\A\\0[^\\]+\\0/, '')
- result.instance_variable_set("@#{attr_name}", value)
- end
- else
- klass_name = val[4].gsub(/^Struct::/, '')
- attr_names, values = [], []
-
- val[9].each do |(attr_name, value)|
- # Protected and private attributes will have a \0..\0 prefix
- attr_names << attr_name.gsub(/\A\\0[^\\]+\\0/, '')
- values << value
- end
-
- result = Struct.new(klass_name, *attr_names).new(*values)
- result.instance_variable_set("@_php_class", klass_name)
- end
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'php_serialization.y', 56)
- def _reduce_14(val, _values, result)
- result = val[0] << val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'php_serialization.y', 57)
- def _reduce_15(val, _values, result)
- result = []
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'php_serialization.y', 60)
- def _reduce_16(val, _values, result)
- result = val
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'php_serialization.y', 65)
- def _reduce_17(val, _values, result)
- # Checks if the keys are a sequence of integers
- idx = -1
- arr = val[5].all? { |(k,v)| k == (idx += 1) }
-
- if arr
- result = val[5].map { |(k,v)| v }
- else
- result = Hash[val[5]]
- end
-
- result
- end
-.,.,
-
-def _reduce_none(val, _values, result)
- val[0]
-end
-
- end # class Unserializer
-end # module PhpSerialization
diff --git a/test/racc/regress/riml b/test/racc/regress/riml
deleted file mode 100644
index ce24c1c77f..0000000000
--- a/test/racc/regress/riml
+++ /dev/null
@@ -1,4039 +0,0 @@
-#
-# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.5.2
-# from Racc grammar file "".
-#
-
-require 'racc/parser.rb'
-
- require File.expand_path("../lexer", __FILE__)
- require File.expand_path("../nodes", __FILE__)
- require File.expand_path("../errors", __FILE__)
- require File.expand_path("../ast_rewriter", __FILE__)
-module Riml
- class Parser < Racc::Parser
-
-module_eval(<<'...end riml.y/module_eval...', 'riml.y', 592)
- # This code will be put as-is in the parser class
-
- attr_accessor :ast_rewriter
- attr_writer :options
-
- # The Parser and AST_Rewriter share this same hash of options
- def options
- @options ||= {}
- end
-
- def self.ast_cache
- @ast_cache
- end
- @ast_cache = {}
-
- # parses tokens or code into output nodes
- def parse(object, ast_rewriter = Riml::AST_Rewriter.new, filename = nil, included = false)
- if (ast = self.class.ast_cache[filename])
- else
- if tokens?(object)
- @tokens = object
- elsif code?(object)
- @lexer = Riml::Lexer.new(object, filename, true)
- end
-
- begin
- ast = do_parse
- rescue Racc::ParseError => e
- raise unless @lexer
- if (invalid_token = @lexer.prev_token_is_keyword?)
- warning = "#{invalid_token.inspect} is a keyword, and cannot " \
- "be used as a variable name"
- end
- error_msg = e.message
- error_msg << "\nWARNING: #{warning}" if warning
- error = Riml::ParseError.new(error_msg, @lexer.filename, @lexer.lineno)
- raise error
- end
- self.class.ast_cache[filename] = ast if filename
- end
- @ast_rewriter ||= ast_rewriter
- return ast unless @ast_rewriter
- @ast_rewriter.ast = ast.dup
- @ast_rewriter.options ||= options
- @ast_rewriter.rewrite(filename, included)
- @ast_rewriter.ast
- end
-
- # get the next token from either the list of tokens provided, or
- # the lexer getting the next token
- def next_token
- return @tokens.shift unless @lexer
- token = @lexer.next_token
- if token && @lexer.parser_info
- @current_parser_info = token.pop
- end
- token
- end
-
- private
-
- def tokens?(object)
- Array === object
- end
-
- def code?(object)
- String === object
- end
-
- def make_node(racc_val)
- node = yield racc_val
- node.parser_info = @current_parser_info
- node
- end
-...end riml.y/module_eval...
-##### State transition tables begin ###
-
-racc_action_table = [
- 135, 265, 60, 139, 136, 211, 417, 418, 48, 47,
- 411, 211, 448, 274, 146, 56, 199, 89, 272, 60,
- 273, 270, 409, 271, 37, 46, 48, 47, 37, 49,
- 44, 45, 392, 412, 37, 449, 384, 60, 50, 70,
- 316, 61, 386, 385, 317, 62, 63, 129, 130, 132,
- 127, 128, 131, 115, 116, 117, 121, 122, 123, 118,
- 119, 120, 124, 125, 126, 102, 104, 103, 109, 111,
- 110, 112, 114, 113, 106, 108, 107, 133, 134, 101,
- 100, 137, 171, 80, 38, 173, 37, 81, 38, 82,
- 85, 83, 84, 87, 38, 105, 86, 37, 75, 76,
- 153, 152, 57, 56, 88, 89, 77, 37, 90, 58,
- 59, 78, 37, 46, 48, 47, 91, 49, 44, 45,
- 64, 72, 73, -116, 161, 60, 50, 70, 79, 61,
- 272, 37, 92, 62, 63, 271, 48, 47, -178, -178,
- -178, -178, 211, 147, 211, 211, 38, 60, -43, -43,
- 153, 152, 329, 409, 99, 409, 409, 38, 331, 153,
- 152, -42, -42, 161, 60, 50, 70, 38, 139, 313,
- 29, 313, 38, 52, 80, 54, 164, 277, 81, 277,
- 82, 85, 83, 84, 87, 279, 279, 86, 149, 75,
- 76, 38, 182, 57, 56, 88, 89, 77, 149, 90,
- 58, 59, 78, 37, 46, 48, 47, 91, 49, 44,
- 45, 64, 72, 73, -116, 164, 60, 50, 70, 79,
- 61, 164, 60, 164, 62, 63, -204, 277, 149, 164,
- 279, 164, -203, 305, 149, 366, 141, 164, 164, -44,
- -44, 129, 130, 132, -85, -85, -85, -85, 142, -48,
- -48, -116, 258, 60, 50, 70, -99, -98, 60, 50,
- 70, 29, 149, 38, 52, 80, 54, 371, 149, 81,
- 52, 82, 85, 83, 84, 87, 149, 139, 86, 164,
- 75, 76, 164, 139, 57, 56, 88, 89, 77, 105,
- 90, 58, 59, 78, 37, 46, 48, 47, 91, 49,
- 44, 45, 64, 72, 73, -99, -98, 60, 50, 70,
- 79, 61, -46, -46, 370, 62, 63, -175, -175, -175,
- -175, 139, 129, 130, 132, -47, -47, -45, -45, 433,
- 195, 432, 129, 130, 132, 127, 128, 131, 115, 116,
- 117, 121, 122, 123, 118, 119, 120, 124, 125, 126,
- 164, 354, 29, 211, 38, 52, 80, 54, 153, 152,
- 81, 60, 82, 85, 83, 84, 87, 149, 60, 86,
- 105, 75, 76, 417, 437, 57, 56, 88, 89, 77,
- 105, 90, 58, 59, 78, 215, 46, 48, 47, 91,
- 49, 44, 45, 64, 72, 73, -116, 216, 60, 50,
- 70, 79, 61, 139, 218, 319, 62, 63, -175, -175,
- -175, -175, -178, -178, -178, -178, 211, 147, 48, 47,
- 211, 49, 48, 47, 262, 49, -176, -176, -176, -176,
- 263, 320, 191, 192, 193, 194, 267, 129, 130, 132,
- 127, 128, 131, 29, 269, 269, 52, 269, 54, 129,
- 130, 132, 127, 128, 131, 115, 116, 117, 121, 122,
- 123, 118, 119, 120, 124, 125, 126, 102, 104, 103,
- 109, 111, 110, 112, 114, 113, 106, 108, 107, 133,
- 134, 101, 100, 137, 323, 105, 258, 276, 294, 295,
- 149, 139, 302, 305, 306, 105, 139, 105, 312, 326,
- 327, 328, 333, 105, 105, 105, 342, 346, 357, 361,
- 320, 258, 362, 363, 60, 129, 130, 132, 127, 128,
- 131, 129, 130, 132, 127, 128, 131, 368, 129, 130,
- 132, 127, 128, 131, 115, 116, 117, 121, 122, 123,
- 118, 119, 120, 124, 125, 126, 102, 104, 103, 109,
- 111, 110, 112, 114, 113, 106, 108, 107, 133, 134,
- 101, 100, 137, 105, 80, 129, 130, 132, 81, 105,
- 82, 85, 83, 84, 87, 374, 105, 86, 377, 75,
- 76, 379, 380, 57, 56, 88, 89, 77, 393, 90,
- 58, 59, 78, 37, 46, 48, 47, 91, 49, 44,
- 45, 64, 72, 73, -116, 302, 60, 50, 70, 79,
- 61, 394, 397, 105, 62, 63, 269, 258, 129, 130,
- 132, 127, 128, 131, 115, 116, 117, 121, 122, 123,
- 118, 119, 120, 124, 125, 126, 102, 104, 103, 109,
- 111, 110, 112, 114, 113, 106, 108, 107, 133, 134,
- 101, 29, 400, 38, 52, 80, 54, 401, 402, 81,
- -245, 82, 85, 83, 84, 87, 105, 403, 86, 406,
- 75, 76, 60, 211, 57, 56, 88, 89, 77, 211,
- 90, 58, 59, 78, 37, 46, 48, 47, 91, 49,
- 44, 45, 64, 72, 73, 427, 60, 60, 50, 70,
- 79, 61, 430, 431, 434, 62, 63, 435, 438, 129,
- 130, 132, 127, 128, 131, 115, 116, 117, 121, 122,
- 123, 118, 119, 120, 124, 125, 126, 102, 104, 103,
- 109, 111, 110, 112, 114, 113, 106, 108, 107, 133,
- 134, 440, 29, 441, 38, 52, 80, 54, 442, 443,
- 81, 211, 82, 85, 83, 84, 87, 105, 445, 86,
- 446, 75, 76, 371, 371, 57, 56, 88, 89, 77,
- 211, 90, 58, 59, 78, 37, 46, 48, 47, 91,
- 49, 44, 45, 64, 72, 73, 458, 435, 60, 50,
- 70, 79, 61, 462, 463, 371, 62, 63, 465, 468,
- 129, 130, 132, 127, 128, 131, 115, 116, 117, 121,
- 122, 123, 118, 119, 120, 124, 125, 126, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 129,
- 130, 132, nil, 29, nil, 38, 52, 80, 54, nil,
- nil, 81, nil, 82, 85, 83, 84, 87, 105, nil,
- 86, nil, 75, 76, nil, nil, 57, 56, 88, 89,
- 77, nil, 90, 58, 59, 78, 37, 46, 48, 47,
- 91, 49, 44, 45, 64, 72, 73, 105, nil, 60,
- 50, 70, 79, 61, nil, nil, nil, 62, 63, nil,
- nil, 129, 130, 132, 127, 128, 131, 115, 116, 117,
- 121, 122, 123, 118, 119, 120, 124, 125, 126, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 129, 130, 132, nil, 29, nil, 38, 52, 80, 54,
- nil, nil, 81, nil, 82, 85, 83, 84, 87, 105,
- nil, 86, nil, 75, 76, nil, nil, 57, 56, 88,
- 89, 77, nil, 90, 58, 59, 78, 37, 46, 48,
- 47, 91, 49, 44, 45, 64, 72, 73, 105, nil,
- 60, 50, 70, 79, 61, nil, nil, nil, 62, 63,
- 414, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 29, nil, 38, 52, nil,
- 54, nil, 129, 130, 132, 127, 128, 131, 115, 116,
- 117, 121, 122, 123, 118, 119, 120, 124, 125, 126,
- 102, 104, 103, 109, 111, 110, 112, 114, 113, 106,
- 108, 107, 133, 134, 101, 100, 137, 419, nil, nil,
- nil, nil, nil, nil, nil, nil, 146, 56, nil, 89,
- 105, nil, 90, nil, nil, nil, nil, 46, 48, 47,
- nil, 49, 44, 45, 64, nil, nil, nil, nil, 60,
- 50, 70, nil, 61, nil, nil, nil, 62, 63, 129,
- 130, 132, 127, 128, 131, 115, 116, 117, 121, 122,
- 123, 118, 119, 120, 124, 125, 126, 102, 104, 103,
- 109, 111, 110, 112, 114, 113, 106, 108, 107, 133,
- 134, 101, 100, 137, 29, nil, nil, 52, nil, 54,
- nil, 261, 146, 56, nil, 89, nil, 105, 90, nil,
- nil, nil, nil, 46, 48, 47, nil, 49, 44, 45,
- 64, nil, nil, nil, nil, 60, 50, 70, nil, 61,
- nil, nil, nil, 62, 63, 180, 146, 56, nil, 89,
- nil, nil, 90, nil, nil, nil, nil, 46, 48, 47,
- nil, 49, 44, 45, 64, nil, nil, -116, nil, 60,
- 50, 70, nil, 61, nil, nil, nil, 62, 63, nil,
- 29, nil, nil, 52, nil, 54, nil, 261, nil, 129,
- 130, 132, 127, 128, 131, 115, 116, 117, 121, 122,
- 123, 118, 119, 120, 124, 125, 126, nil, 180, 146,
- 56, nil, 89, nil, 176, 90, nil, 52, nil, 54,
- 46, 48, 47, nil, 49, 44, 45, 64, nil, nil,
- nil, nil, 60, 50, 70, nil, 61, 105, nil, nil,
- 62, 63, 180, 146, 56, nil, 89, nil, nil, 90,
- nil, nil, nil, nil, 46, 48, 47, nil, 49, 44,
- 45, 64, nil, nil, nil, nil, 60, 50, 70, nil,
- 61, nil, nil, nil, 62, 63, nil, 29, -119, nil,
- 52, nil, 54, nil, nil, 129, 130, 132, 127, 128,
- 131, 115, 116, 117, 121, 122, 123, 118, 119, 120,
- 124, 125, 126, nil, nil, nil, 180, 146, 56, nil,
- 89, 29, -119, 90, 52, nil, 54, nil, 46, 48,
- 47, nil, 49, 44, 45, 64, nil, nil, nil, nil,
- 60, 50, 70, 105, 61, nil, nil, nil, 62, 63,
- 180, 146, 56, nil, 89, nil, nil, 90, nil, nil,
- nil, nil, 46, 48, 47, nil, 49, 44, 45, 64,
- nil, nil, nil, nil, 60, 50, 70, nil, 61, nil,
- nil, nil, 62, 63, nil, 29, -119, nil, 52, nil,
- 54, nil, nil, 129, 130, 132, 127, 128, 131, 115,
- 116, 117, 121, 122, 123, 118, 119, 120, 124, 125,
- 126, nil, nil, nil, 180, 146, 56, nil, 89, 29,
- -119, 90, 52, nil, 54, nil, 46, 48, 47, nil,
- 49, 44, 45, 64, nil, nil, nil, nil, 60, 50,
- 70, 105, 61, nil, nil, nil, 62, 63, 180, 146,
- 56, nil, 89, nil, nil, 90, nil, nil, nil, nil,
- 46, 48, 47, nil, 49, 44, 45, 64, nil, nil,
- nil, nil, 60, 50, 70, nil, 61, nil, nil, nil,
- 62, 63, nil, 29, -121, nil, 52, nil, 54, nil,
- nil, 129, 130, 132, 127, 128, 131, 115, 116, 117,
- 121, 122, 123, 118, 119, 120, 124, 125, 126, nil,
- nil, nil, nil, 146, 56, nil, 89, 29, -119, 90,
- 52, nil, 54, nil, 46, 48, 47, nil, 49, 44,
- 45, 64, nil, nil, nil, nil, 60, 50, 70, 105,
- 61, nil, nil, nil, 62, 63, 180, 146, 56, nil,
- 89, nil, nil, 90, nil, nil, nil, nil, 46, 48,
- 47, nil, 49, 44, 45, 64, nil, nil, nil, nil,
- 60, 50, 70, nil, 61, nil, nil, nil, 62, 63,
- nil, 29, nil, nil, 52, nil, 54, 350, nil, 129,
- 130, 132, 127, 128, 131, 115, 116, 117, 121, 122,
- 123, 118, 119, 120, 124, 125, 126, nil, nil, nil,
- 180, 146, 56, nil, 89, 29, -119, 90, 52, nil,
- 54, nil, 46, 48, 47, nil, 49, 44, 45, 64,
- nil, nil, nil, nil, 60, 50, 70, 105, 61, nil,
- nil, nil, 62, 63, 365, 146, 56, nil, 89, nil,
- nil, 90, nil, nil, nil, nil, 46, 48, 47, nil,
- 49, 44, 45, 64, nil, nil, nil, nil, 60, 50,
- 70, nil, 61, nil, nil, nil, 62, 63, nil, 29,
- -119, nil, 52, nil, 54, nil, 129, 130, 132, 127,
- 128, 131, 115, 116, 117, 121, 122, 123, 118, 119,
- 120, 124, 125, 126, nil, nil, nil, 180, 146, 56,
- nil, 89, nil, 29, 90, nil, 52, nil, 54, 46,
- 48, 47, nil, 49, 44, 45, 64, nil, nil, nil,
- nil, 60, 50, 70, 105, 61, nil, nil, nil, 62,
- 63, 180, 146, 56, nil, 89, nil, nil, 90, nil,
- nil, nil, nil, 46, 48, 47, nil, 49, 44, 45,
- 64, nil, nil, nil, nil, 60, 50, 70, nil, 61,
- nil, nil, nil, 62, 63, nil, 29, -119, nil, 52,
- nil, 54, nil, nil, 129, 130, 132, 127, 128, 131,
- 115, 116, 117, 121, 122, 123, 118, 119, 120, 124,
- 125, 126, nil, nil, nil, nil, 146, 56, nil, 89,
- 29, -119, 90, 52, nil, 54, nil, 46, 48, 47,
- nil, 49, 44, 45, 64, nil, nil, nil, nil, 60,
- 50, 70, 105, 61, nil, nil, nil, 62, 63, 146,
- 56, nil, 89, nil, nil, 90, nil, nil, nil, nil,
- 46, 48, 47, nil, 49, 44, 45, 64, nil, nil,
- -116, nil, 60, 50, 70, nil, 61, nil, nil, nil,
- 62, 63, 146, 56, 29, 89, nil, 52, 90, 54,
- nil, nil, nil, 46, 48, 47, nil, 49, 44, 45,
- 64, nil, nil, -116, nil, 60, 50, 70, nil, 61,
- nil, nil, nil, 62, 63, 146, 56, 29, 89, nil,
- 52, 90, 54, nil, nil, 167, 46, 48, 47, nil,
- 49, 44, 45, 64, nil, nil, nil, nil, 60, 50,
- 70, nil, 61, nil, nil, nil, 62, 63, 146, 56,
- 29, 89, nil, 52, 90, 54, nil, nil, nil, 46,
- 48, 47, nil, 49, 44, 45, 64, nil, nil, nil,
- nil, 60, 50, 70, nil, 61, nil, nil, nil, 62,
- 63, 146, 56, 165, 89, nil, 52, 90, 54, nil,
- nil, nil, 46, 48, 47, nil, 49, 44, 45, 64,
- nil, nil, nil, nil, 60, 50, 70, nil, 61, nil,
- nil, nil, 62, 63, 146, 56, 29, 89, nil, 52,
- 90, 54, nil, nil, nil, 46, 48, 47, nil, 49,
- 44, 45, 64, nil, nil, nil, nil, 60, 50, 70,
- nil, 61, nil, nil, nil, 62, 63, 146, 56, 29,
- 89, nil, 52, 90, 54, nil, nil, nil, 46, 48,
- 47, nil, 49, 44, 45, 64, nil, nil, -116, nil,
- 60, 50, 70, nil, 61, nil, nil, nil, 62, 63,
- 146, 56, 29, 89, nil, 52, 90, 54, nil, nil,
- nil, 46, 48, 47, nil, 49, 44, 45, 64, nil,
- nil, nil, nil, 60, 50, 70, nil, 61, nil, nil,
- nil, 62, 63, 146, 56, 29, 89, nil, 52, 90,
- 54, nil, nil, nil, 46, 48, 47, nil, 49, 44,
- 45, 64, nil, nil, nil, nil, 60, 50, 70, nil,
- 61, nil, nil, nil, 62, 63, 146, 56, 29, 89,
- nil, 52, 90, 54, nil, nil, nil, 46, 48, 47,
- nil, 49, 44, 45, 64, nil, nil, nil, nil, 60,
- 50, 70, nil, 61, nil, nil, nil, 62, 63, 146,
- 56, 29, 89, nil, 52, 90, 54, nil, nil, nil,
- 46, 48, 47, nil, 49, 44, 45, 64, nil, nil,
- nil, nil, 60, 50, 70, nil, 61, nil, nil, nil,
- 62, 63, 146, 56, 29, 89, nil, 52, 90, 54,
- nil, nil, nil, 46, 48, 47, nil, 49, 44, 45,
- 64, nil, nil, nil, nil, 60, 50, 70, nil, 61,
- nil, nil, nil, 62, 63, 146, 56, 29, 89, nil,
- 52, 90, 54, nil, nil, nil, 46, 48, 47, nil,
- 49, 44, 45, 64, nil, nil, nil, nil, 60, 50,
- 70, nil, 61, nil, nil, nil, 62, 63, 146, 56,
- 29, 89, nil, 52, 90, 54, nil, nil, nil, 46,
- 48, 47, nil, 49, 44, 45, 64, nil, nil, nil,
- nil, 60, 50, 70, nil, 61, nil, nil, nil, 62,
- 63, 146, 56, 29, 89, nil, 52, 90, 54, nil,
- nil, nil, 46, 48, 47, nil, 49, 44, 45, 64,
- nil, nil, nil, nil, 60, 50, 70, nil, 61, nil,
- nil, nil, 62, 63, 146, 56, 29, 89, nil, 52,
- 90, 54, nil, nil, nil, 46, 48, 47, nil, 49,
- 44, 45, 64, nil, nil, nil, nil, 60, 50, 70,
- nil, 61, nil, nil, nil, 62, 63, 146, 56, 29,
- 89, nil, 52, 90, 54, nil, nil, nil, 46, 48,
- 47, nil, 49, 44, 45, 64, nil, nil, nil, nil,
- 60, 50, 70, nil, 61, nil, nil, nil, 62, 63,
- 146, 56, 29, 89, nil, 52, 90, 54, nil, nil,
- nil, 46, 48, 47, nil, 49, 44, 45, 64, nil,
- nil, nil, nil, 60, 50, 70, nil, 61, nil, nil,
- nil, 62, 63, 146, 56, 29, 89, nil, 52, 90,
- 54, nil, nil, nil, 46, 48, 47, nil, 49, 44,
- 45, 64, nil, nil, nil, nil, 60, 50, 70, nil,
- 61, nil, nil, nil, 62, 63, 146, 56, 29, 89,
- nil, 52, 90, 54, nil, nil, nil, 46, 48, 47,
- nil, 49, 44, 45, 64, nil, nil, nil, nil, 60,
- 50, 70, nil, 61, nil, nil, nil, 62, 63, 146,
- 56, 29, 89, nil, 52, 90, 54, nil, nil, nil,
- 46, 48, 47, nil, 49, 44, 45, 64, nil, nil,
- nil, nil, 60, 50, 70, nil, 61, nil, nil, nil,
- 62, 63, 146, 56, 29, 89, nil, 52, 90, 54,
- nil, nil, nil, 46, 48, 47, nil, 49, 44, 45,
- 64, nil, nil, nil, nil, 60, 50, 70, nil, 61,
- nil, nil, nil, 62, 63, 146, 56, 29, 89, nil,
- 52, 90, 54, nil, nil, nil, 46, 48, 47, nil,
- 49, 44, 45, 64, nil, nil, nil, nil, 60, 50,
- 70, nil, 61, nil, nil, nil, 62, 63, 146, 56,
- 29, 89, nil, 52, 90, 54, nil, nil, nil, 46,
- 48, 47, nil, 49, 44, 45, 64, nil, nil, nil,
- nil, 60, 50, 70, nil, 61, nil, nil, nil, 62,
- 63, 146, 56, 29, 89, nil, 52, 90, 54, nil,
- nil, nil, 46, 48, 47, nil, 49, 44, 45, 64,
- nil, nil, nil, nil, 60, 50, 70, nil, 61, nil,
- nil, nil, 62, 63, 146, 56, 29, 89, nil, 52,
- 90, 54, nil, nil, nil, 46, 48, 47, nil, 49,
- 44, 45, 64, nil, nil, nil, nil, 60, 50, 70,
- nil, 61, nil, nil, nil, 62, 63, 146, 56, 29,
- 89, nil, 52, 90, 54, nil, nil, nil, 46, 48,
- 47, nil, 49, 44, 45, 64, nil, nil, nil, nil,
- 60, 50, 70, nil, 61, nil, nil, nil, 62, 63,
- 146, 56, 29, 89, nil, 52, 90, 54, nil, nil,
- nil, 46, 48, 47, nil, 49, 44, 45, 64, nil,
- nil, nil, nil, 60, 50, 70, nil, 61, nil, nil,
- nil, 62, 63, 146, 56, 29, 89, nil, 52, 90,
- 54, nil, nil, nil, 46, 48, 47, nil, 49, 44,
- 45, 64, nil, nil, nil, nil, 60, 50, 70, nil,
- 61, nil, nil, nil, 62, 63, 146, 56, 29, 89,
- nil, 52, 90, 54, nil, nil, nil, 46, 48, 47,
- nil, 49, 44, 45, 64, nil, nil, nil, nil, 60,
- 50, 70, nil, 61, nil, nil, nil, 62, 63, 146,
- 56, 29, 89, nil, 52, 90, 54, nil, nil, nil,
- 46, 48, 47, nil, 49, 44, 45, 64, nil, nil,
- nil, nil, 60, 50, 70, nil, 61, nil, nil, nil,
- 62, 63, 146, 56, 29, 89, nil, 52, 90, 54,
- nil, nil, nil, 46, 48, 47, nil, 49, 44, 45,
- 64, nil, nil, nil, nil, 60, 50, 70, nil, 61,
- nil, nil, nil, 62, 63, 146, 56, 29, 89, nil,
- 52, 90, 54, nil, nil, nil, 46, 48, 47, nil,
- 49, 44, 45, 64, nil, nil, nil, nil, 60, 50,
- 70, nil, 61, nil, nil, nil, 62, 63, 146, 56,
- 29, 89, nil, 52, 90, 54, nil, nil, nil, 46,
- 48, 47, nil, 49, 44, 45, 64, nil, nil, nil,
- nil, 60, 50, 70, nil, 61, nil, nil, nil, 62,
- 63, 146, 56, 29, 89, nil, 52, 90, 54, nil,
- nil, nil, 46, 48, 47, nil, 49, 44, 45, 64,
- nil, nil, nil, nil, 60, 50, 70, nil, 61, nil,
- nil, nil, 62, 63, 146, 56, 29, 89, nil, 52,
- 90, 54, nil, nil, nil, 46, 48, 47, nil, 49,
- 44, 45, 64, nil, nil, nil, nil, 60, 50, 70,
- nil, 61, nil, nil, nil, 62, 63, 146, 56, 29,
- 89, nil, 52, 90, 54, nil, nil, nil, 46, 48,
- 47, nil, 49, 44, 45, 64, nil, nil, nil, nil,
- 60, 50, 70, nil, 61, nil, nil, nil, 62, 63,
- 146, 56, 29, 89, nil, 52, 90, 54, nil, nil,
- nil, 46, 48, 47, nil, 49, 44, 45, 64, nil,
- nil, nil, nil, 60, 50, 70, nil, 61, nil, nil,
- nil, 62, 63, 146, 56, 29, 89, nil, 52, 90,
- 54, nil, nil, nil, 46, 48, 47, nil, 49, 44,
- 45, 64, nil, nil, nil, nil, 60, 50, 70, nil,
- 61, nil, nil, nil, 62, 63, 146, 56, 29, 89,
- nil, 52, 90, 54, nil, nil, nil, 46, 48, 47,
- nil, 49, 44, 45, 64, nil, nil, nil, nil, 60,
- 50, 70, nil, 61, nil, nil, nil, 62, 63, 146,
- 56, 29, 89, nil, 52, 90, 54, nil, nil, nil,
- 46, 48, 47, nil, 49, 44, 45, 64, nil, nil,
- nil, nil, 60, 50, 70, nil, 61, nil, nil, nil,
- 62, 63, 146, 56, 29, 89, nil, 52, 90, 54,
- nil, nil, nil, 46, 48, 47, nil, 49, 44, 45,
- 64, nil, nil, nil, nil, 60, 50, 70, nil, 61,
- nil, nil, nil, 62, 63, 146, 56, 29, 89, nil,
- 52, 90, 54, nil, nil, nil, 46, 48, 47, nil,
- 49, 44, 45, 64, nil, nil, nil, nil, 60, 50,
- 70, nil, 61, nil, nil, nil, 62, 63, 146, 56,
- 29, 89, nil, 52, 90, 54, nil, nil, nil, 46,
- 48, 47, nil, 49, 44, 45, 64, nil, nil, nil,
- nil, 60, 50, 70, nil, 61, nil, nil, nil, 62,
- 63, 146, 56, 29, 89, nil, 52, 90, 54, nil,
- nil, nil, 46, 48, 47, nil, 49, 44, 45, 64,
- nil, nil, nil, nil, 60, 50, 70, nil, 61, nil,
- nil, nil, 62, 63, 146, 56, 29, 89, nil, 52,
- 90, 54, nil, nil, nil, 46, 48, 47, nil, 49,
- 44, 45, 64, nil, nil, nil, nil, 60, 50, 70,
- nil, 61, nil, nil, nil, 62, 63, 146, 56, 29,
- 89, nil, 52, 90, 54, nil, nil, nil, 46, 48,
- 47, nil, 49, 44, 45, 64, nil, nil, nil, nil,
- 60, 50, 70, nil, 61, nil, nil, nil, 62, 63,
- 146, 56, 29, 89, nil, 52, 90, 54, nil, nil,
- nil, 46, 48, 47, nil, 49, 44, 45, 64, nil,
- nil, nil, nil, 60, 50, 70, nil, 61, nil, nil,
- nil, 62, 63, 146, 56, 29, 89, nil, 52, 90,
- 54, nil, nil, nil, 46, 48, 47, nil, 49, 44,
- 45, 64, nil, nil, nil, nil, 60, 50, 70, nil,
- 61, nil, nil, nil, 62, 63, 146, 56, 29, 89,
- nil, 52, 90, 54, nil, nil, nil, 46, 48, 47,
- nil, 49, 44, 45, 64, nil, nil, nil, nil, 60,
- 50, 70, nil, 61, nil, nil, nil, 62, 63, 146,
- 56, 29, 89, nil, 52, 90, 54, nil, nil, nil,
- 46, 48, 47, nil, 49, 44, 45, 64, nil, nil,
- -116, nil, 60, 50, 70, nil, 61, nil, nil, nil,
- 62, 63, 146, 56, 29, 89, nil, 52, 90, 288,
- nil, nil, nil, 46, 48, 47, nil, 49, 44, 45,
- 64, nil, nil, nil, nil, 60, 50, 70, nil, 61,
- nil, nil, nil, 62, 63, 146, 56, 29, 89, nil,
- 52, 90, 54, nil, nil, nil, 46, 48, 47, nil,
- 49, 44, 45, 64, nil, nil, nil, nil, 60, 50,
- 70, nil, 61, nil, nil, nil, 62, 63, 146, 56,
- 29, 89, nil, 52, 90, 54, nil, nil, nil, 46,
- 48, 47, nil, 49, 44, 45, 64, nil, nil, nil,
- nil, 60, 50, 70, nil, 61, nil, nil, nil, 62,
- 63, 146, 56, 29, 89, nil, 52, 90, 54, nil,
- nil, nil, 46, 48, 47, nil, 49, 44, 45, 64,
- nil, nil, nil, nil, 60, 50, 70, nil, 61, nil,
- nil, nil, 62, 63, 146, 56, 29, 89, nil, 52,
- 90, 54, nil, nil, nil, 46, 48, 47, nil, 49,
- 44, 45, 64, nil, nil, nil, nil, 60, 50, 70,
- nil, 61, nil, nil, nil, 62, 63, 146, 56, 29,
- 89, nil, 52, 90, 54, nil, nil, nil, 46, 48,
- 47, nil, 49, 44, 45, 64, nil, nil, nil, nil,
- 60, 50, 70, nil, 61, nil, nil, nil, 62, 63,
- 146, 56, 29, 89, nil, 52, 90, 54, nil, nil,
- nil, 46, 48, 47, nil, 49, 44, 45, 64, nil,
- nil, -116, nil, 60, 50, 70, nil, 61, nil, nil,
- nil, 62, 63, 146, 56, 29, 89, nil, 52, 90,
- 54, nil, nil, nil, 46, 48, 47, nil, 49, 44,
- 45, 64, nil, nil, nil, nil, 60, 50, 70, nil,
- 61, nil, nil, nil, 62, 63, 146, 56, 29, 89,
- nil, 52, 90, 54, nil, nil, nil, 46, 48, 47,
- nil, 49, 44, 45, 64, nil, nil, nil, nil, 60,
- 50, 70, nil, 61, nil, nil, nil, 62, 63, 146,
- 56, 29, 89, nil, 52, 90, 54, nil, nil, nil,
- 46, 48, 47, nil, 49, 44, 45, 64, nil, nil,
- nil, nil, 60, 50, 70, nil, 61, nil, nil, nil,
- 62, 63, nil, nil, 29, nil, nil, 52, 348, 54,
- 129, 130, 132, 127, 128, 131, 115, 116, 117, 121,
- 122, 123, 118, 119, 120, 124, 125, 126, nil, nil,
- nil, nil, 146, 56, nil, 89, nil, 29, 90, nil,
- 52, nil, 54, 46, 48, 47, nil, 49, 44, 45,
- 64, nil, nil, -116, nil, 60, 50, 70, 105, 61,
- nil, nil, nil, 62, 63, 146, 56, nil, 89, nil,
- nil, 90, nil, nil, nil, nil, 46, 48, 47, nil,
- 49, 44, 45, 64, nil, nil, nil, nil, 60, 50,
- 70, nil, 61, nil, nil, nil, 62, 63, 146, 56,
- 29, 89, nil, 52, 90, 288, nil, nil, nil, 46,
- 48, 47, nil, 49, 44, 45, 64, nil, nil, nil,
- nil, 60, 50, 70, nil, 61, nil, nil, nil, 62,
- 63, 146, 56, 29, 89, nil, 52, 90, 54, nil,
- nil, nil, 46, 48, 47, nil, 49, 44, 45, 64,
- nil, nil, nil, nil, 60, 50, 70, nil, 61, nil,
- nil, nil, 62, 63, 146, 56, 29, 89, nil, 52,
- 90, 54, nil, nil, nil, 46, 48, 47, nil, 49,
- 44, 45, 64, nil, nil, nil, nil, 60, 50, 70,
- nil, 61, nil, nil, nil, 62, 63, 146, 56, 29,
- 89, nil, 52, 90, 54, nil, nil, nil, 46, 48,
- 47, nil, 49, 44, 45, 64, nil, nil, nil, nil,
- 60, 50, 70, nil, 61, nil, nil, nil, 62, 63,
- 146, 56, 29, 89, nil, 52, 90, 54, nil, nil,
- nil, 46, 48, 47, nil, 49, 44, 45, 64, nil,
- nil, nil, nil, 60, 50, 70, nil, 61, nil, nil,
- nil, 62, 63, 146, 56, 29, 89, nil, 52, 90,
- 54, nil, nil, nil, 46, 48, 47, nil, 49, 44,
- 45, 64, nil, nil, nil, nil, 60, 50, 70, nil,
- 61, nil, nil, nil, 62, 63, 146, 56, 29, 89,
- nil, 52, 90, 54, nil, nil, nil, 46, 48, 47,
- nil, 49, 44, 45, 64, nil, nil, nil, nil, 60,
- 50, 70, nil, 61, nil, nil, nil, 62, 63, 146,
- 56, 29, 89, nil, 52, 90, 54, nil, nil, nil,
- 46, 48, 47, nil, 49, 44, 45, 64, nil, nil,
- nil, nil, 60, 50, 70, nil, 61, nil, nil, nil,
- 62, 63, 146, 56, 29, 89, nil, 52, 90, 54,
- nil, nil, nil, 46, 48, 47, nil, 49, 44, 45,
- 64, nil, nil, nil, nil, 60, 50, 70, nil, 61,
- nil, nil, nil, 62, 63, 146, 56, 29, 89, nil,
- 52, 90, 54, nil, nil, nil, 46, 48, 47, nil,
- 49, 44, 45, 64, nil, nil, -116, nil, 60, 50,
- 70, nil, 61, nil, nil, nil, 62, 63, 146, 56,
- 29, 89, nil, 52, 90, 54, nil, nil, nil, 46,
- 48, 47, nil, 49, 44, 45, 64, nil, nil, nil,
- nil, 60, 50, 70, nil, 61, nil, nil, nil, 62,
- 63, 146, 56, 29, 89, nil, 52, 90, 54, nil,
- nil, nil, 46, 48, 47, nil, 49, 44, 45, 64,
- nil, nil, nil, nil, 60, 50, 70, nil, 61, nil,
- nil, nil, 62, 63, 146, 56, 29, 89, nil, 52,
- 90, 54, nil, nil, nil, 46, 48, 47, nil, 49,
- 44, 45, 64, nil, nil, nil, nil, 60, 50, 70,
- nil, 61, nil, nil, nil, 62, 63, 146, 56, 29,
- 89, nil, 52, 90, 54, nil, nil, nil, 46, 48,
- 47, nil, 49, 44, 45, 64, nil, nil, nil, nil,
- 60, 50, 70, nil, 61, nil, nil, nil, 62, 63,
- 146, 56, 29, 89, nil, 52, nil, 54, nil, nil,
- nil, 46, 48, 47, nil, 49, 44, 45, nil, nil,
- nil, nil, nil, 60, 50, 70, nil, 61, nil, nil,
- nil, 62, 63, 146, 56, 29, 89, nil, 52, nil,
- 54, nil, nil, nil, 46, 48, 47, nil, 49, 44,
- 45, 64, nil, nil, nil, nil, 60, 50, 70, nil,
- 61, nil, nil, nil, 62, 63, 146, 56, 188, 89,
- nil, 52, nil, nil, nil, nil, nil, 46, 48, 47,
- nil, 49, 44, 45, 64, nil, nil, nil, nil, 60,
- 50, 70, nil, 61, nil, nil, nil, 62, 63, nil,
- nil, 188, nil, nil, 52, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 211, nil, nil, nil, nil, nil, 129, 130, 132, 127,
- 128, 131, nil, nil, 188, nil, nil, 52, 129, 130,
- 132, 127, 128, 131, 115, 116, 117, 121, 122, 123,
- 118, 119, 120, 124, 125, 126, 102, 104, 103, 109,
- 111, 110, 112, 114, 113, 106, 108, 107, 133, 134,
- 101, 100, 137, 211, 105, nil, nil, nil, nil, 129,
- 130, 132, 127, 128, 131, nil, 105, nil, nil, nil,
- nil, 129, 130, 132, 127, 128, 131, 115, 116, 117,
- 121, 122, 123, 118, 119, 120, 124, 125, 126, 102,
- 104, 103, 109, 111, 110, 112, 114, 113, 106, 108,
- 107, 133, 134, 101, 100, 137, 211, 105, nil, nil,
- nil, nil, 129, 130, 132, 127, 128, 131, nil, 105,
- nil, nil, nil, nil, 129, 130, 132, 127, 128, 131,
- 115, 116, 117, 121, 122, 123, 118, 119, 120, 124,
- 125, 126, 102, 104, 103, 109, 111, 110, 112, 114,
- 113, 106, 108, 107, 133, 134, 101, 100, 137, 211,
- 105, nil, nil, nil, nil, 129, 130, 132, 127, 128,
- 131, nil, 105, nil, nil, nil, nil, 129, 130, 132,
- 127, 128, 131, 115, 116, 117, 121, 122, 123, 118,
- 119, 120, 124, 125, 126, 102, 104, 103, 109, 111,
- 110, 112, 114, 113, 106, 108, 107, 133, 134, 101,
- 100, 137, 211, 105, nil, nil, nil, nil, 129, 130,
- 132, 127, 128, 131, nil, 105, nil, nil, nil, nil,
- 129, 130, 132, 127, 128, 131, 115, 116, 117, 121,
- 122, 123, 118, 119, 120, 124, 125, 126, 102, 104,
- 103, 109, 111, 110, 112, 114, 113, 106, 108, 107,
- 133, 134, 101, 100, 137, 455, 105, nil, nil, nil,
- nil, 129, 130, 132, 127, 128, 131, nil, 105, nil,
- nil, nil, nil, 129, 130, 132, 127, 128, 131, 115,
- 116, 117, 121, 122, 123, 118, 119, 120, 124, 125,
- 126, 102, 104, 103, 109, 111, 110, 112, 114, 113,
- 106, 108, 107, 133, 134, 101, 100, 137, 460, 105,
- nil, nil, nil, 129, 130, 132, 127, 128, 131, nil,
- nil, 105, nil, nil, nil, nil, 129, 130, 132, 127,
- 128, 131, 115, 116, 117, 121, 122, 123, 118, 119,
- 120, 124, 125, 126, 102, 104, 103, 109, 111, 110,
- 112, 114, 113, 106, 108, 107, 133, 134, 101, 100,
- 137, 105, 129, 130, 132, 127, 128, 131, nil, nil,
- nil, nil, nil, nil, 105, 129, 130, 132, 127, 128,
- 131, 115, 116, 117, 121, 122, 123, 118, 119, 120,
- 124, 125, 126, 102, 104, 103, 109, 111, 110, 112,
- 114, 113, 106, 108, 107, 133, 134, 101, 100, 137,
- 105, nil, nil, nil, nil, nil, 266, 129, 130, 132,
- 127, 128, 131, 105, 129, 130, 132, 127, 128, 131,
- 115, 116, 117, 121, 122, 123, 118, 119, 120, 124,
- 125, 126, 102, 104, 103, 109, 111, 110, 112, 114,
- 113, 106, 108, 107, 133, 134, 101, 100, 137, nil,
- nil, nil, nil, nil, nil, 105, nil, nil, nil, nil,
- nil, nil, 105, 129, 130, 132, 127, 128, 131, 115,
- 116, 117, 121, 122, 123, 118, 119, 120, 124, 125,
- 126, 102, 104, 103, 109, 111, 110, 112, 114, 113,
- 106, 108, 107, 133, 134, 101, 100, 137, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 275, 105, 129, 130, 132, 127, 128, 131, 115, 116,
- 117, 121, 122, 123, 118, 119, 120, 124, 125, 126,
- 102, 104, 103, 109, 111, 110, 112, 114, 113, 106,
- 108, 107, 133, 134, 101, 100, 137, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 105, 129, 130, 132, 127, 128, 131, 115, 116, 117,
- 121, 122, 123, 118, 119, 120, 124, 125, 126, 102,
- 104, 103, 109, 111, 110, 112, 114, 113, 106, 108,
- 107, 133, 134, 101, 100, 137, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 105,
- 129, 130, 132, 127, 128, 131, 115, 116, 117, 121,
- 122, 123, 118, 119, 120, 124, 125, 126, 102, 104,
- 103, 109, 111, 110, 112, 114, 113, 106, 108, 107,
- 133, 134, 101, 100, 137, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 105, 129,
- 130, 132, 127, 128, 131, 115, 116, 117, 121, 122,
- 123, 118, 119, 120, 124, 125, 126, 102, 104, 103,
- 109, 111, 110, 112, 114, 113, 106, 108, 107, 133,
- 134, 101, 100, 137, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 105, 129, 130,
- 132, 127, 128, 131, 115, 116, 117, 121, 122, 123,
- 118, 119, 120, 124, 125, 126, 129, 130, 132, 127,
- 128, 131, 115, 116, 117, 121, 122, 123, 118, 119,
- 120, 124, 125, 126, 102, 104, 103, 109, 111, 110,
- 112, 114, 113, 106, 108, 107, 105, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 105, 129, 130, 132, 127, 128,
- 131, 115, 116, 117, 121, 122, 123, 118, 119, 120,
- 124, 125, 126, 102, 104, 103, 109, 111, 110, 112,
- 114, 113, 106, 108, 107, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 105, 129, 130, 132, 127, 128, 131,
- 115, 116, 117, 121, 122, 123, 118, 119, 120, 124,
- 125, 126, 102, 104, 103, 109, 111, 110, 112, 114,
- 113, 106, 108, 107, 133, 134, 101, 100, 137, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 105, 129, 130, 132, 127, 128, 131, 115,
- 116, 117, 121, 122, 123, 118, 119, 120, 124, 125,
- 126, 102, 104, 103, 109, 111, 110, 112, 114, 113,
- 106, 108, 107, 133, 134, 101, 100, 137, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 105, 129, 130, 132, 127, 128, 131, 115, 116,
- 117, 121, 122, 123, 118, 119, 120, 124, 125, 126,
- 102, 104, 103, 109, 111, 110, 112, 114, 113, 106,
- 108, 107, 133, 134, 101, 100, 137, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 337,
- 105, 129, 130, 132, 127, 128, 131, 115, 116, 117,
- 121, 122, 123, 118, 119, 120, 124, 125, 126, 102,
- 104, 103, 109, 111, 110, 112, 114, 113, 106, 108,
- 107, 133, 134, 101, 100, 137, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 340, nil, nil, 341, 105,
- 129, 130, 132, 127, 128, 131, 115, 116, 117, 121,
- 122, 123, 118, 119, 120, 124, 125, 126, 102, 104,
- 103, 109, 111, 110, 112, 114, 113, 106, 108, 107,
- 133, 134, 101, 100, 137, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 105, 129,
- 130, 132, 127, 128, 131, 115, 116, 117, 121, 122,
- 123, 118, 119, 120, 124, 125, 126, 102, 104, 103,
- 109, 111, 110, 112, 114, 113, 106, 108, 107, 133,
- 134, 101, 100, 137, nil, nil, nil, nil, nil, nil,
- 266, nil, nil, nil, nil, nil, nil, 105, 129, 130,
- 132, 127, 128, 131, 115, 116, 117, 121, 122, 123,
- 118, 119, 120, 124, 125, 126, 102, 104, 103, 109,
- 111, 110, 112, 114, 113, 106, 108, 107, 133, 134,
- 101, 100, 137, nil, nil, nil, nil, nil, nil, 360,
- nil, nil, nil, nil, nil, nil, 105, 129, 130, 132,
- 127, 128, 131, 115, 116, 117, 121, 122, 123, 118,
- 119, 120, 124, 125, 126, 102, 104, 103, 109, 111,
- 110, 112, 114, 113, 106, 108, 107, 133, 134, 101,
- 100, 137, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 105, 129, 130, 132, 127,
- 128, 131, 115, 116, 117, 121, 122, 123, 118, 119,
- 120, 124, 125, 126, 102, 104, 103, 109, 111, 110,
- 112, 114, 113, 106, 108, 107, 133, 134, 101, 100,
- 137, nil, nil, nil, nil, nil, nil, 360, nil, nil,
- nil, nil, nil, nil, 105, 129, 130, 132, 127, 128,
- 131, 115, 116, 117, 121, 122, 123, 118, 119, 120,
- 124, 125, 126, 102, 104, 103, 109, 111, 110, 112,
- 114, 113, 106, 108, 107, 133, 134, 101, 100, 137,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 105, 129, 130, 132, 127, 128, 131,
- 115, 116, 117, 121, 122, 123, 118, 119, 120, 124,
- 125, 126, 102, 104, 103, 109, 111, 110, 112, 114,
- 113, 106, 108, 107, 133, 134, 101, 100, 137, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 105, 129, 130, 132, 127, 128, 131, 115,
- 116, 117, 121, 122, 123, 118, 119, 120, 124, 125,
- 126, 102, 104, 103, 109, 111, 110, 112, 114, 113,
- 106, 108, 107, 133, 134, 101, 100, 137, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 105, 129, 130, 132, 127, 128, 131, 115, 116,
- 117, 121, 122, 123, 118, 119, 120, 124, 125, 126,
- 102, 104, 103, 109, 111, 110, 112, 114, 113, 106,
- 108, 107, 133, 134, 101, 100, 137, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 105, 129, 130, 132, 127, 128, 131, 115, 116, 117,
- 121, 122, 123, 118, 119, 120, 124, 125, 126, 102,
- 104, 103, 109, 111, 110, 112, 114, 113, 106, 108,
- 107, 133, 134, 101, 100, 137, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 396, nil, nil, 341, 105,
- 129, 130, 132, 127, 128, 131, 115, 116, 117, 121,
- 122, 123, 118, 119, 120, 124, 125, 126, 102, 104,
- 103, 109, 111, 110, 112, 114, 113, 106, 108, 107,
- 133, 134, 101, 100, 137, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 105, 129,
- 130, 132, 127, 128, 131, 115, 116, 117, 121, 122,
- 123, 118, 119, 120, 124, 125, 126, 102, 104, 103,
- 109, 111, 110, 112, 114, 113, 106, 108, 107, 133,
- 134, 101, 100, 137, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 399, nil, nil, nil, 105, 129, 130,
- 132, 127, 128, 131, 115, 116, 117, 121, 122, 123,
- 118, 119, 120, 124, 125, 126, 102, 104, 103, 109,
- 111, 110, 112, 114, 113, 106, 108, 107, 133, 134,
- 101, 100, 137, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 105, 129, 130, 132,
- 127, 128, 131, 115, 116, 117, 121, 122, 123, 118,
- 119, 120, 124, 125, 126, 102, 104, 103, 109, 111,
- 110, 112, 114, 113, 106, 108, 107, 133, 134, 101,
- 100, 137, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 105, 129, 130, 132, 127,
- 128, 131, 115, 116, 117, 121, 122, 123, 118, 119,
- 120, 124, 125, 126, 102, 104, 103, 109, 111, 110,
- 112, 114, 113, 106, 108, 107, 133, 134, 101, 100,
- 137, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 105, 129, 130, 132, 127, 128,
- 131, 115, 116, 117, 121, 122, 123, 118, 119, 120,
- 124, 125, 126, 102, 104, 103, 109, 111, 110, 112,
- 114, 113, 106, 108, 107, 133, 134, 101, 100, 137,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 105, 129, 130, 132, 127, 128, 131,
- 115, 116, 117, 121, 122, 123, 118, 119, 120, 124,
- 125, 126, 102, 104, 103, 109, 111, 110, 112, 114,
- 113, 106, 108, 107, 133, 134, 101, 100, 137, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 105, 129, 130, 132, 127, 128, 131, 115,
- 116, 117, 121, 122, 123, 118, 119, 120, 124, 125,
- 126, 102, 104, 103, 109, 111, 110, 112, 114, 113,
- 106, 108, 107, 133, 134, 101, 100, 137, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 105, 129, 130, 132, 127, 128, 131, 115, 116,
- 117, 121, 122, 123, 118, 119, 120, 124, 125, 126,
- 102, 104, 103, 109, 111, 110, 112, 114, 113, 106,
- 108, 107, 133, 134, 101, 100, 137, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 105, 129, 130, 132, 127, 128, 131, 115, 116, 117,
- 121, 122, 123, 118, 119, 120, 124, 125, 126, 102,
- 104, 103, 109, 111, 110, 112, 114, 113, 106, 108,
- 107, 133, 134, 101, 100, 137, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 105,
- 129, 130, 132, 127, 128, 131, 115, 116, 117, 121,
- 122, 123, 118, 119, 120, 124, 125, 126, 102, 104,
- 103, 109, 111, 110, 112, 114, 113, 106, 108, 107,
- 133, 134, 101, 100, 137, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 105, 129,
- 130, 132, 127, 128, 131, 115, 116, 117, 121, 122,
- 123, 118, 119, 120, 124, 125, 126, 102, 104, 103,
- 109, 111, 110, 112, 114, 113, 106, 108, 107, 133,
- 134, 101, 100, 137, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 105 ]
-
-racc_action_check = [
- 22, 143, 74, 143, 22, 333, 376, 376, 182, 182,
- 370, 369, 433, 157, 57, 57, 74, 57, 155, 182,
- 157, 155, 369, 155, 3, 57, 57, 57, 93, 57,
- 57, 57, 333, 370, 376, 433, 330, 57, 57, 57,
- 202, 57, 330, 330, 202, 57, 57, 22, 22, 22,
- 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
- 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
- 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
- 22, 22, 57, 0, 3, 57, 332, 0, 93, 0,
- 0, 0, 0, 0, 376, 22, 0, 454, 0, 0,
- 287, 287, 0, 0, 0, 0, 0, 461, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 55, 0, 0, 0, 0, 0,
- 298, 466, 1, 0, 0, 298, 59, 59, 283, 283,
- 283, 283, 447, 283, 451, 464, 332, 59, 283, 283,
- 40, 40, 210, 447, 19, 451, 464, 454, 210, 181,
- 181, 287, 287, 175, 72, 72, 72, 461, 23, 200,
- 0, 213, 0, 0, 2, 0, 55, 277, 2, 161,
- 2, 2, 2, 2, 2, 162, 278, 2, 284, 2,
- 2, 466, 59, 2, 2, 2, 2, 2, 286, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 175, 2, 2, 2, 2,
- 2, 200, 86, 213, 2, 2, 161, 313, 266, 277,
- 314, 161, 162, 304, 360, 304, 24, 162, 278, 284,
- 284, 186, 186, 186, 138, 138, 138, 138, 27, 286,
- 286, 8, 138, 8, 8, 8, 403, 430, 73, 73,
- 73, 2, 32, 2, 2, 94, 2, 315, 35, 94,
- 86, 94, 94, 94, 94, 94, 285, 266, 94, 313,
- 94, 94, 314, 360, 94, 94, 94, 94, 94, 186,
- 94, 94, 94, 94, 94, 94, 94, 94, 94, 94,
- 94, 94, 94, 94, 94, 403, 430, 94, 94, 94,
- 94, 94, 281, 281, 315, 94, 94, 285, 285, 285,
- 285, 43, 187, 187, 187, 282, 282, 285, 285, 410,
- 70, 410, 222, 222, 222, 222, 222, 222, 222, 222,
- 222, 222, 222, 222, 222, 222, 222, 222, 222, 222,
- 290, 290, 94, 87, 94, 94, 95, 94, 387, 387,
- 95, 88, 95, 95, 95, 95, 95, 33, 89, 95,
- 187, 95, 95, 416, 416, 95, 95, 95, 95, 95,
- 222, 95, 95, 95, 95, 90, 95, 95, 95, 95,
- 95, 95, 95, 95, 95, 95, 95, 92, 95, 95,
- 95, 95, 95, 96, 97, 203, 95, 95, 33, 33,
- 33, 33, 31, 31, 31, 31, 331, 31, 331, 331,
- 386, 331, 386, 386, 140, 386, 42, 42, 42, 42,
- 141, 203, 66, 66, 66, 66, 146, 235, 235, 235,
- 235, 235, 235, 95, 148, 150, 95, 151, 95, 203,
- 203, 203, 203, 203, 203, 203, 203, 203, 203, 203,
- 203, 203, 203, 203, 203, 203, 203, 203, 203, 203,
- 203, 203, 203, 203, 203, 203, 203, 203, 203, 203,
- 203, 203, 203, 203, 204, 235, 154, 160, 166, 169,
- 170, 174, 178, 183, 184, 185, 189, 203, 199, 207,
- 208, 209, 212, 249, 250, 252, 260, 268, 293, 297,
- 204, 299, 300, 301, 305, 236, 236, 236, 236, 236,
- 236, 237, 237, 237, 237, 237, 237, 312, 204, 204,
- 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
- 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
- 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
- 204, 204, 204, 236, 211, 247, 247, 247, 211, 237,
- 211, 211, 211, 211, 211, 318, 204, 211, 322, 211,
- 211, 324, 325, 211, 211, 211, 211, 211, 334, 211,
- 211, 211, 211, 211, 211, 211, 211, 211, 211, 211,
- 211, 211, 211, 211, 211, 335, 211, 211, 211, 211,
- 211, 336, 339, 247, 211, 211, 344, 345, 220, 220,
- 220, 220, 220, 220, 220, 220, 220, 220, 220, 220,
- 220, 220, 220, 220, 220, 220, 220, 220, 220, 220,
- 220, 220, 220, 220, 220, 220, 220, 220, 220, 220,
- 220, 211, 353, 211, 211, 320, 211, 355, 356, 320,
- 320, 320, 320, 320, 320, 320, 220, 359, 320, 367,
- 320, 320, 368, 385, 320, 320, 320, 320, 320, 389,
- 320, 320, 320, 320, 320, 320, 320, 320, 320, 320,
- 320, 320, 320, 320, 320, 391, 392, 320, 320, 320,
- 320, 320, 404, 408, 411, 320, 320, 412, 417, 221,
- 221, 221, 221, 221, 221, 221, 221, 221, 221, 221,
- 221, 221, 221, 221, 221, 221, 221, 221, 221, 221,
- 221, 221, 221, 221, 221, 221, 221, 221, 221, 221,
- 221, 420, 320, 421, 320, 320, 438, 320, 422, 423,
- 438, 425, 438, 438, 438, 438, 438, 221, 428, 438,
- 429, 438, 438, 432, 434, 438, 438, 438, 438, 438,
- 445, 438, 438, 438, 438, 438, 438, 438, 438, 438,
- 438, 438, 438, 438, 438, 438, 448, 449, 438, 438,
- 438, 438, 438, 456, 457, 458, 438, 438, 459, 467,
- 223, 223, 223, 223, 223, 223, 223, 223, 223, 223,
- 223, 223, 223, 223, 223, 223, 223, 223, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 248,
- 248, 248, nil, 438, nil, 438, 438, 455, 438, nil,
- nil, 455, nil, 455, 455, 455, 455, 455, 223, nil,
- 455, nil, 455, 455, nil, nil, 455, 455, 455, 455,
- 455, nil, 455, 455, 455, 455, 455, 455, 455, 455,
- 455, 455, 455, 455, 455, 455, 455, 248, nil, 455,
- 455, 455, 455, 455, nil, nil, nil, 455, 455, nil,
- nil, 224, 224, 224, 224, 224, 224, 224, 224, 224,
- 224, 224, 224, 224, 224, 224, 224, 224, 224, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 251, 251, 251, nil, 455, nil, 455, 455, 460, 455,
- nil, nil, 460, nil, 460, 460, 460, 460, 460, 224,
- nil, 460, nil, 460, 460, nil, nil, 460, 460, 460,
- 460, 460, nil, 460, 460, 460, 460, 460, 460, 460,
- 460, 460, 460, 460, 460, 460, 460, 460, 251, nil,
- 460, 460, 460, 460, 460, nil, nil, nil, 460, 460,
- 375, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 460, nil, 460, 460, nil,
- 460, nil, 375, 375, 375, 375, 375, 375, 375, 375,
- 375, 375, 375, 375, 375, 375, 375, 375, 375, 375,
- 375, 375, 375, 375, 375, 375, 375, 375, 375, 375,
- 375, 375, 375, 375, 375, 375, 375, 378, nil, nil,
- nil, nil, nil, nil, nil, nil, 139, 139, nil, 139,
- 375, nil, 139, nil, nil, nil, nil, 139, 139, 139,
- nil, 139, 139, 139, 139, nil, nil, nil, nil, 139,
- 139, 139, nil, 139, nil, nil, nil, 139, 139, 378,
- 378, 378, 378, 378, 378, 378, 378, 378, 378, 378,
- 378, 378, 378, 378, 378, 378, 378, 378, 378, 378,
- 378, 378, 378, 378, 378, 378, 378, 378, 378, 378,
- 378, 378, 378, 378, 139, nil, nil, 139, nil, 139,
- nil, 139, 258, 258, nil, 258, nil, 378, 258, nil,
- nil, nil, nil, 258, 258, 258, nil, 258, 258, 258,
- 258, nil, nil, nil, nil, 258, 258, 258, nil, 258,
- nil, nil, nil, 258, 258, 58, 58, 58, nil, 58,
- nil, nil, 58, nil, nil, nil, nil, 58, 58, 58,
- nil, 58, 58, 58, 58, nil, nil, 58, nil, 58,
- 58, 58, nil, 58, nil, nil, nil, 58, 58, nil,
- 258, nil, nil, 258, nil, 258, nil, 258, nil, 226,
- 226, 226, 226, 226, 226, 226, 226, 226, 226, 226,
- 226, 226, 226, 226, 226, 226, 226, nil, 147, 147,
- 147, nil, 147, nil, 58, 147, nil, 58, nil, 58,
- 147, 147, 147, nil, 147, 147, 147, 147, nil, nil,
- nil, nil, 147, 147, 147, nil, 147, 226, nil, nil,
- 147, 147, 165, 165, 165, nil, 165, nil, nil, 165,
- nil, nil, nil, nil, 165, 165, 165, nil, 165, 165,
- 165, 165, nil, nil, nil, nil, 165, 165, 165, nil,
- 165, nil, nil, nil, 165, 165, nil, 147, 147, nil,
- 147, nil, 147, nil, nil, 227, 227, 227, 227, 227,
- 227, 227, 227, 227, 227, 227, 227, 227, 227, 227,
- 227, 227, 227, nil, nil, nil, 171, 171, 171, nil,
- 171, 165, 165, 171, 165, nil, 165, nil, 171, 171,
- 171, nil, 171, 171, 171, 171, nil, nil, nil, nil,
- 171, 171, 171, 227, 171, nil, nil, nil, 171, 171,
- 176, 176, 176, nil, 176, nil, nil, 176, nil, nil,
- nil, nil, 176, 176, 176, nil, 176, 176, 176, 176,
- nil, nil, nil, nil, 176, 176, 176, nil, 176, nil,
- nil, nil, 176, 176, nil, 171, 171, nil, 171, nil,
- 171, nil, nil, 228, 228, 228, 228, 228, 228, 228,
- 228, 228, 228, 228, 228, 228, 228, 228, 228, 228,
- 228, nil, nil, nil, 215, 215, 215, nil, 215, 176,
- 176, 215, 176, nil, 176, nil, 215, 215, 215, nil,
- 215, 215, 215, 215, nil, nil, nil, nil, 215, 215,
- 215, 228, 215, nil, nil, nil, 215, 215, 267, 267,
- 267, nil, 267, nil, nil, 267, nil, nil, nil, nil,
- 267, 267, 267, nil, 267, 267, 267, 267, nil, nil,
- nil, nil, 267, 267, 267, nil, 267, nil, nil, nil,
- 267, 267, nil, 215, 215, nil, 215, nil, 215, nil,
- nil, 229, 229, 229, 229, 229, 229, 229, 229, 229,
- 229, 229, 229, 229, 229, 229, 229, 229, 229, nil,
- nil, nil, nil, 274, 274, nil, 274, 267, 267, 274,
- 267, nil, 267, nil, 274, 274, 274, nil, 274, 274,
- 274, 274, nil, nil, nil, nil, 274, 274, 274, 229,
- 274, nil, nil, nil, 274, 274, 276, 276, 276, nil,
- 276, nil, nil, 276, nil, nil, nil, nil, 276, 276,
- 276, nil, 276, 276, 276, 276, nil, nil, nil, nil,
- 276, 276, 276, nil, 276, nil, nil, nil, 276, 276,
- nil, 274, nil, nil, 274, nil, 274, 274, nil, 230,
- 230, 230, 230, 230, 230, 230, 230, 230, 230, 230,
- 230, 230, 230, 230, 230, 230, 230, nil, nil, nil,
- 295, 295, 295, nil, 295, 276, 276, 295, 276, nil,
- 276, nil, 295, 295, 295, nil, 295, 295, 295, 295,
- nil, nil, nil, nil, 295, 295, 295, 230, 295, nil,
- nil, nil, 295, 295, 302, 302, 302, nil, 302, nil,
- nil, 302, nil, nil, nil, nil, 302, 302, 302, nil,
- 302, 302, 302, 302, nil, nil, nil, nil, 302, 302,
- 302, nil, 302, nil, nil, nil, 302, 302, nil, 295,
- 295, nil, 295, nil, 295, nil, 231, 231, 231, 231,
- 231, 231, 231, 231, 231, 231, 231, 231, 231, 231,
- 231, 231, 231, 231, nil, nil, nil, 362, 362, 362,
- nil, 362, nil, 302, 362, nil, 302, nil, 302, 362,
- 362, 362, nil, 362, 362, 362, 362, nil, nil, nil,
- nil, 362, 362, 362, 231, 362, nil, nil, nil, 362,
- 362, 393, 393, 393, nil, 393, nil, nil, 393, nil,
- nil, nil, nil, 393, 393, 393, nil, 393, 393, 393,
- 393, nil, nil, nil, nil, 393, 393, 393, nil, 393,
- nil, nil, nil, 393, 393, nil, 362, 362, nil, 362,
- nil, 362, nil, nil, 232, 232, 232, 232, 232, 232,
- 232, 232, 232, 232, 232, 232, 232, 232, 232, 232,
- 232, 232, nil, nil, nil, nil, 29, 29, nil, 29,
- 393, 393, 29, 393, nil, 393, nil, 29, 29, 29,
- nil, 29, 29, 29, 29, nil, nil, nil, nil, 29,
- 29, 29, 232, 29, nil, nil, nil, 29, 29, 52,
- 52, nil, 52, nil, nil, 52, nil, nil, nil, nil,
- 52, 52, 52, nil, 52, 52, 52, 52, nil, nil,
- 52, nil, 52, 52, 52, nil, 52, nil, nil, nil,
- 52, 52, 54, 54, 29, 54, nil, 29, 54, 29,
- nil, nil, nil, 54, 54, 54, nil, 54, 54, 54,
- 54, nil, nil, 54, nil, 54, 54, 54, nil, 54,
- nil, nil, nil, 54, 54, 56, 56, 52, 56, nil,
- 52, 56, 52, nil, nil, 56, 56, 56, 56, nil,
- 56, 56, 56, 56, nil, nil, nil, nil, 56, 56,
- 56, nil, 56, nil, nil, nil, 56, 56, 61, 61,
- 54, 61, nil, 54, 61, 54, nil, nil, nil, 61,
- 61, 61, nil, 61, 61, 61, 61, nil, nil, nil,
- nil, 61, 61, 61, nil, 61, nil, nil, nil, 61,
- 61, 62, 62, 56, 62, nil, 56, 62, 56, nil,
- nil, nil, 62, 62, 62, nil, 62, 62, 62, 62,
- nil, nil, nil, nil, 62, 62, 62, nil, 62, nil,
- nil, nil, 62, 62, 63, 63, 61, 63, nil, 61,
- 63, 61, nil, nil, nil, 63, 63, 63, nil, 63,
- 63, 63, 63, nil, nil, nil, nil, 63, 63, 63,
- nil, 63, nil, nil, nil, 63, 63, 78, 78, 62,
- 78, nil, 62, 78, 62, nil, nil, nil, 78, 78,
- 78, nil, 78, 78, 78, 78, nil, nil, 78, nil,
- 78, 78, 78, nil, 78, nil, nil, nil, 78, 78,
- 80, 80, 63, 80, nil, 63, 80, 63, nil, nil,
- nil, 80, 80, 80, nil, 80, 80, 80, 80, nil,
- nil, nil, nil, 80, 80, 80, nil, 80, nil, nil,
- nil, 80, 80, 81, 81, 78, 81, nil, 78, 81,
- 78, nil, nil, nil, 81, 81, 81, nil, 81, 81,
- 81, 81, nil, nil, nil, nil, 81, 81, 81, nil,
- 81, nil, nil, nil, 81, 81, 82, 82, 80, 82,
- nil, 80, 82, 80, nil, nil, nil, 82, 82, 82,
- nil, 82, 82, 82, 82, nil, nil, nil, nil, 82,
- 82, 82, nil, 82, nil, nil, nil, 82, 82, 85,
- 85, 81, 85, nil, 81, 85, 81, nil, nil, nil,
- 85, 85, 85, nil, 85, 85, 85, 85, nil, nil,
- nil, nil, 85, 85, 85, nil, 85, nil, nil, nil,
- 85, 85, 100, 100, 82, 100, nil, 82, 100, 82,
- nil, nil, nil, 100, 100, 100, nil, 100, 100, 100,
- 100, nil, nil, nil, nil, 100, 100, 100, nil, 100,
- nil, nil, nil, 100, 100, 101, 101, 85, 101, nil,
- 85, 101, 85, nil, nil, nil, 101, 101, 101, nil,
- 101, 101, 101, 101, nil, nil, nil, nil, 101, 101,
- 101, nil, 101, nil, nil, nil, 101, 101, 102, 102,
- 100, 102, nil, 100, 102, 100, nil, nil, nil, 102,
- 102, 102, nil, 102, 102, 102, 102, nil, nil, nil,
- nil, 102, 102, 102, nil, 102, nil, nil, nil, 102,
- 102, 103, 103, 101, 103, nil, 101, 103, 101, nil,
- nil, nil, 103, 103, 103, nil, 103, 103, 103, 103,
- nil, nil, nil, nil, 103, 103, 103, nil, 103, nil,
- nil, nil, 103, 103, 104, 104, 102, 104, nil, 102,
- 104, 102, nil, nil, nil, 104, 104, 104, nil, 104,
- 104, 104, 104, nil, nil, nil, nil, 104, 104, 104,
- nil, 104, nil, nil, nil, 104, 104, 105, 105, 103,
- 105, nil, 103, 105, 103, nil, nil, nil, 105, 105,
- 105, nil, 105, 105, 105, 105, nil, nil, nil, nil,
- 105, 105, 105, nil, 105, nil, nil, nil, 105, 105,
- 106, 106, 104, 106, nil, 104, 106, 104, nil, nil,
- nil, 106, 106, 106, nil, 106, 106, 106, 106, nil,
- nil, nil, nil, 106, 106, 106, nil, 106, nil, nil,
- nil, 106, 106, 107, 107, 105, 107, nil, 105, 107,
- 105, nil, nil, nil, 107, 107, 107, nil, 107, 107,
- 107, 107, nil, nil, nil, nil, 107, 107, 107, nil,
- 107, nil, nil, nil, 107, 107, 108, 108, 106, 108,
- nil, 106, 108, 106, nil, nil, nil, 108, 108, 108,
- nil, 108, 108, 108, 108, nil, nil, nil, nil, 108,
- 108, 108, nil, 108, nil, nil, nil, 108, 108, 109,
- 109, 107, 109, nil, 107, 109, 107, nil, nil, nil,
- 109, 109, 109, nil, 109, 109, 109, 109, nil, nil,
- nil, nil, 109, 109, 109, nil, 109, nil, nil, nil,
- 109, 109, 110, 110, 108, 110, nil, 108, 110, 108,
- nil, nil, nil, 110, 110, 110, nil, 110, 110, 110,
- 110, nil, nil, nil, nil, 110, 110, 110, nil, 110,
- nil, nil, nil, 110, 110, 111, 111, 109, 111, nil,
- 109, 111, 109, nil, nil, nil, 111, 111, 111, nil,
- 111, 111, 111, 111, nil, nil, nil, nil, 111, 111,
- 111, nil, 111, nil, nil, nil, 111, 111, 112, 112,
- 110, 112, nil, 110, 112, 110, nil, nil, nil, 112,
- 112, 112, nil, 112, 112, 112, 112, nil, nil, nil,
- nil, 112, 112, 112, nil, 112, nil, nil, nil, 112,
- 112, 113, 113, 111, 113, nil, 111, 113, 111, nil,
- nil, nil, 113, 113, 113, nil, 113, 113, 113, 113,
- nil, nil, nil, nil, 113, 113, 113, nil, 113, nil,
- nil, nil, 113, 113, 114, 114, 112, 114, nil, 112,
- 114, 112, nil, nil, nil, 114, 114, 114, nil, 114,
- 114, 114, 114, nil, nil, nil, nil, 114, 114, 114,
- nil, 114, nil, nil, nil, 114, 114, 115, 115, 113,
- 115, nil, 113, 115, 113, nil, nil, nil, 115, 115,
- 115, nil, 115, 115, 115, 115, nil, nil, nil, nil,
- 115, 115, 115, nil, 115, nil, nil, nil, 115, 115,
- 116, 116, 114, 116, nil, 114, 116, 114, nil, nil,
- nil, 116, 116, 116, nil, 116, 116, 116, 116, nil,
- nil, nil, nil, 116, 116, 116, nil, 116, nil, nil,
- nil, 116, 116, 117, 117, 115, 117, nil, 115, 117,
- 115, nil, nil, nil, 117, 117, 117, nil, 117, 117,
- 117, 117, nil, nil, nil, nil, 117, 117, 117, nil,
- 117, nil, nil, nil, 117, 117, 118, 118, 116, 118,
- nil, 116, 118, 116, nil, nil, nil, 118, 118, 118,
- nil, 118, 118, 118, 118, nil, nil, nil, nil, 118,
- 118, 118, nil, 118, nil, nil, nil, 118, 118, 119,
- 119, 117, 119, nil, 117, 119, 117, nil, nil, nil,
- 119, 119, 119, nil, 119, 119, 119, 119, nil, nil,
- nil, nil, 119, 119, 119, nil, 119, nil, nil, nil,
- 119, 119, 120, 120, 118, 120, nil, 118, 120, 118,
- nil, nil, nil, 120, 120, 120, nil, 120, 120, 120,
- 120, nil, nil, nil, nil, 120, 120, 120, nil, 120,
- nil, nil, nil, 120, 120, 121, 121, 119, 121, nil,
- 119, 121, 119, nil, nil, nil, 121, 121, 121, nil,
- 121, 121, 121, 121, nil, nil, nil, nil, 121, 121,
- 121, nil, 121, nil, nil, nil, 121, 121, 122, 122,
- 120, 122, nil, 120, 122, 120, nil, nil, nil, 122,
- 122, 122, nil, 122, 122, 122, 122, nil, nil, nil,
- nil, 122, 122, 122, nil, 122, nil, nil, nil, 122,
- 122, 123, 123, 121, 123, nil, 121, 123, 121, nil,
- nil, nil, 123, 123, 123, nil, 123, 123, 123, 123,
- nil, nil, nil, nil, 123, 123, 123, nil, 123, nil,
- nil, nil, 123, 123, 124, 124, 122, 124, nil, 122,
- 124, 122, nil, nil, nil, 124, 124, 124, nil, 124,
- 124, 124, 124, nil, nil, nil, nil, 124, 124, 124,
- nil, 124, nil, nil, nil, 124, 124, 125, 125, 123,
- 125, nil, 123, 125, 123, nil, nil, nil, 125, 125,
- 125, nil, 125, 125, 125, 125, nil, nil, nil, nil,
- 125, 125, 125, nil, 125, nil, nil, nil, 125, 125,
- 126, 126, 124, 126, nil, 124, 126, 124, nil, nil,
- nil, 126, 126, 126, nil, 126, 126, 126, 126, nil,
- nil, nil, nil, 126, 126, 126, nil, 126, nil, nil,
- nil, 126, 126, 127, 127, 125, 127, nil, 125, 127,
- 125, nil, nil, nil, 127, 127, 127, nil, 127, 127,
- 127, 127, nil, nil, nil, nil, 127, 127, 127, nil,
- 127, nil, nil, nil, 127, 127, 128, 128, 126, 128,
- nil, 126, 128, 126, nil, nil, nil, 128, 128, 128,
- nil, 128, 128, 128, 128, nil, nil, nil, nil, 128,
- 128, 128, nil, 128, nil, nil, nil, 128, 128, 129,
- 129, 127, 129, nil, 127, 129, 127, nil, nil, nil,
- 129, 129, 129, nil, 129, 129, 129, 129, nil, nil,
- nil, nil, 129, 129, 129, nil, 129, nil, nil, nil,
- 129, 129, 130, 130, 128, 130, nil, 128, 130, 128,
- nil, nil, nil, 130, 130, 130, nil, 130, 130, 130,
- 130, nil, nil, nil, nil, 130, 130, 130, nil, 130,
- nil, nil, nil, 130, 130, 131, 131, 129, 131, nil,
- 129, 131, 129, nil, nil, nil, 131, 131, 131, nil,
- 131, 131, 131, 131, nil, nil, nil, nil, 131, 131,
- 131, nil, 131, nil, nil, nil, 131, 131, 132, 132,
- 130, 132, nil, 130, 132, 130, nil, nil, nil, 132,
- 132, 132, nil, 132, 132, 132, 132, nil, nil, nil,
- nil, 132, 132, 132, nil, 132, nil, nil, nil, 132,
- 132, 133, 133, 131, 133, nil, 131, 133, 131, nil,
- nil, nil, 133, 133, 133, nil, 133, 133, 133, 133,
- nil, nil, nil, nil, 133, 133, 133, nil, 133, nil,
- nil, nil, 133, 133, 134, 134, 132, 134, nil, 132,
- 134, 132, nil, nil, nil, 134, 134, 134, nil, 134,
- 134, 134, 134, nil, nil, nil, nil, 134, 134, 134,
- nil, 134, nil, nil, nil, 134, 134, 135, 135, 133,
- 135, nil, 133, 135, 133, nil, nil, nil, 135, 135,
- 135, nil, 135, 135, 135, 135, nil, nil, nil, nil,
- 135, 135, 135, nil, 135, nil, nil, nil, 135, 135,
- 136, 136, 134, 136, nil, 134, 136, 134, nil, nil,
- nil, 136, 136, 136, nil, 136, 136, 136, 136, nil,
- nil, nil, nil, 136, 136, 136, nil, 136, nil, nil,
- nil, 136, 136, 137, 137, 135, 137, nil, 135, 137,
- 135, nil, nil, nil, 137, 137, 137, nil, 137, 137,
- 137, 137, nil, nil, nil, nil, 137, 137, 137, nil,
- 137, nil, nil, nil, 137, 137, 164, 164, 136, 164,
- nil, 136, 164, 136, nil, nil, nil, 164, 164, 164,
- nil, 164, 164, 164, 164, nil, nil, nil, nil, 164,
- 164, 164, nil, 164, nil, nil, nil, 164, 164, 173,
- 173, 137, 173, nil, 137, 173, 137, nil, nil, nil,
- 173, 173, 173, nil, 173, 173, 173, 173, nil, nil,
- 173, nil, 173, 173, 173, nil, 173, nil, nil, nil,
- 173, 173, 180, 180, 164, 180, nil, 164, 180, 164,
- nil, nil, nil, 180, 180, 180, nil, 180, 180, 180,
- 180, nil, nil, nil, nil, 180, 180, 180, nil, 180,
- nil, nil, nil, 180, 180, 188, 188, 173, 188, nil,
- 173, 188, 173, nil, nil, nil, 188, 188, 188, nil,
- 188, 188, 188, 188, nil, nil, nil, nil, 188, 188,
- 188, nil, 188, nil, nil, nil, 188, 188, 191, 191,
- 180, 191, nil, 180, 191, 180, nil, nil, nil, 191,
- 191, 191, nil, 191, 191, 191, 191, nil, nil, nil,
- nil, 191, 191, 191, nil, 191, nil, nil, nil, 191,
- 191, 192, 192, 188, 192, nil, 188, 192, 188, nil,
- nil, nil, 192, 192, 192, nil, 192, 192, 192, 192,
- nil, nil, nil, nil, 192, 192, 192, nil, 192, nil,
- nil, nil, 192, 192, 193, 193, 191, 193, nil, 191,
- 193, 191, nil, nil, nil, 193, 193, 193, nil, 193,
- 193, 193, 193, nil, nil, nil, nil, 193, 193, 193,
- nil, 193, nil, nil, nil, 193, 193, 194, 194, 192,
- 194, nil, 192, 194, 192, nil, nil, nil, 194, 194,
- 194, nil, 194, 194, 194, 194, nil, nil, nil, nil,
- 194, 194, 194, nil, 194, nil, nil, nil, 194, 194,
- 261, 261, 193, 261, nil, 193, 261, 193, nil, nil,
- nil, 261, 261, 261, nil, 261, 261, 261, 261, nil,
- nil, 261, nil, 261, 261, 261, nil, 261, nil, nil,
- nil, 261, 261, 270, 270, 194, 270, nil, 194, 270,
- 194, nil, nil, nil, 270, 270, 270, nil, 270, 270,
- 270, 270, nil, nil, nil, nil, 270, 270, 270, nil,
- 270, nil, nil, nil, 270, 270, 272, 272, 261, 272,
- nil, 261, 272, 261, nil, nil, nil, 272, 272, 272,
- nil, 272, 272, 272, 272, nil, nil, nil, nil, 272,
- 272, 272, nil, 272, nil, nil, nil, 272, 272, 275,
- 275, 270, 275, nil, 270, 275, 270, nil, nil, nil,
- 275, 275, 275, nil, 275, 275, 275, 275, nil, nil,
- nil, nil, 275, 275, 275, nil, 275, nil, nil, nil,
- 275, 275, nil, nil, 272, nil, nil, 272, 272, 272,
- 233, 233, 233, 233, 233, 233, 233, 233, 233, 233,
- 233, 233, 233, 233, 233, 233, 233, 233, nil, nil,
- nil, nil, 288, 288, nil, 288, nil, 275, 288, nil,
- 275, nil, 275, 288, 288, 288, nil, 288, 288, 288,
- 288, nil, nil, 288, nil, 288, 288, 288, 233, 288,
- nil, nil, nil, 288, 288, 291, 291, nil, 291, nil,
- nil, 291, nil, nil, nil, nil, 291, 291, 291, nil,
- 291, 291, 291, 291, nil, nil, nil, nil, 291, 291,
- 291, nil, 291, nil, nil, nil, 291, 291, 294, 294,
- 288, 294, nil, 288, 294, 288, nil, nil, nil, 294,
- 294, 294, nil, 294, 294, 294, 294, nil, nil, nil,
- nil, 294, 294, 294, nil, 294, nil, nil, nil, 294,
- 294, 316, 316, 291, 316, nil, 291, 316, 291, nil,
- nil, nil, 316, 316, 316, nil, 316, 316, 316, 316,
- nil, nil, nil, nil, 316, 316, 316, nil, 316, nil,
- nil, nil, 316, 316, 317, 317, 294, 317, nil, 294,
- 317, 294, nil, nil, nil, 317, 317, 317, nil, 317,
- 317, 317, 317, nil, nil, nil, nil, 317, 317, 317,
- nil, 317, nil, nil, nil, 317, 317, 319, 319, 316,
- 319, nil, 316, 319, 316, nil, nil, nil, 319, 319,
- 319, nil, 319, 319, 319, 319, nil, nil, nil, nil,
- 319, 319, 319, nil, 319, nil, nil, nil, 319, 319,
- 323, 323, 317, 323, nil, 317, 323, 317, nil, nil,
- nil, 323, 323, 323, nil, 323, 323, 323, 323, nil,
- nil, nil, nil, 323, 323, 323, nil, 323, nil, nil,
- nil, 323, 323, 326, 326, 319, 326, nil, 319, 326,
- 319, nil, nil, nil, 326, 326, 326, nil, 326, 326,
- 326, 326, nil, nil, nil, nil, 326, 326, 326, nil,
- 326, nil, nil, nil, 326, 326, 327, 327, 323, 327,
- nil, 323, 327, 323, nil, nil, nil, 327, 327, 327,
- nil, 327, 327, 327, 327, nil, nil, nil, nil, 327,
- 327, 327, nil, 327, nil, nil, nil, 327, 327, 328,
- 328, 326, 328, nil, 326, 328, 326, nil, nil, nil,
- 328, 328, 328, nil, 328, 328, 328, 328, nil, nil,
- nil, nil, 328, 328, 328, nil, 328, nil, nil, nil,
- 328, 328, 337, 337, 327, 337, nil, 327, 337, 327,
- nil, nil, nil, 337, 337, 337, nil, 337, 337, 337,
- 337, nil, nil, nil, nil, 337, 337, 337, nil, 337,
- nil, nil, nil, 337, 337, 341, 341, 328, 341, nil,
- 328, 341, 328, nil, nil, nil, 341, 341, 341, nil,
- 341, 341, 341, 341, nil, nil, 341, nil, 341, 341,
- 341, nil, 341, nil, nil, nil, 341, 341, 365, 365,
- 337, 365, nil, 337, 365, 337, nil, nil, nil, 365,
- 365, 365, nil, 365, 365, 365, 365, nil, nil, nil,
- nil, 365, 365, 365, nil, 365, nil, nil, nil, 365,
- 365, 418, 418, 341, 418, nil, 341, 418, 341, nil,
- nil, nil, 418, 418, 418, nil, 418, 418, 418, 418,
- nil, nil, nil, nil, 418, 418, 418, nil, 418, nil,
- nil, nil, 418, 418, 435, 435, 365, 435, nil, 365,
- 435, 365, nil, nil, nil, 435, 435, 435, nil, 435,
- 435, 435, 435, nil, nil, nil, nil, 435, 435, 435,
- nil, 435, nil, nil, nil, 435, 435, 437, 437, 418,
- 437, nil, 418, 437, 418, nil, nil, nil, 437, 437,
- 437, nil, 437, 437, 437, 437, nil, nil, nil, nil,
- 437, 437, 437, nil, 437, nil, nil, nil, 437, 437,
- 64, 64, 435, 64, nil, 435, nil, 435, nil, nil,
- nil, 64, 64, 64, nil, 64, 64, 64, nil, nil,
- nil, nil, nil, 64, 64, 64, nil, 64, nil, nil,
- nil, 64, 64, 99, 99, 437, 99, nil, 437, nil,
- 437, nil, nil, nil, 99, 99, 99, nil, 99, 99,
- 99, 99, nil, nil, nil, nil, 99, 99, 99, nil,
- 99, nil, nil, nil, 99, 99, 142, 142, 64, 142,
- nil, 64, nil, nil, nil, nil, nil, 142, 142, 142,
- nil, 142, 142, 142, 142, nil, nil, nil, nil, 142,
- 142, 142, nil, 142, nil, nil, nil, 142, 142, nil,
- nil, 99, nil, nil, 99, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 205, nil, nil, nil, nil, nil, 238, 238, 238, 238,
- 238, 238, nil, nil, 142, nil, nil, 142, 205, 205,
- 205, 205, 205, 205, 205, 205, 205, 205, 205, 205,
- 205, 205, 205, 205, 205, 205, 205, 205, 205, 205,
- 205, 205, 205, 205, 205, 205, 205, 205, 205, 205,
- 205, 205, 205, 206, 238, nil, nil, nil, nil, 239,
- 239, 239, 239, 239, 239, nil, 205, nil, nil, nil,
- nil, 206, 206, 206, 206, 206, 206, 206, 206, 206,
- 206, 206, 206, 206, 206, 206, 206, 206, 206, 206,
- 206, 206, 206, 206, 206, 206, 206, 206, 206, 206,
- 206, 206, 206, 206, 206, 206, 381, 239, nil, nil,
- nil, nil, 240, 240, 240, 240, 240, 240, nil, 206,
- nil, nil, nil, nil, 381, 381, 381, 381, 381, 381,
- 381, 381, 381, 381, 381, 381, 381, 381, 381, 381,
- 381, 381, 381, 381, 381, 381, 381, 381, 381, 381,
- 381, 381, 381, 381, 381, 381, 381, 381, 381, 382,
- 240, nil, nil, nil, nil, 241, 241, 241, 241, 241,
- 241, nil, 381, nil, nil, nil, nil, 382, 382, 382,
- 382, 382, 382, 382, 382, 382, 382, 382, 382, 382,
- 382, 382, 382, 382, 382, 382, 382, 382, 382, 382,
- 382, 382, 382, 382, 382, 382, 382, 382, 382, 382,
- 382, 382, 383, 241, nil, nil, nil, nil, 242, 242,
- 242, 242, 242, 242, nil, 382, nil, nil, nil, nil,
- 383, 383, 383, 383, 383, 383, 383, 383, 383, 383,
- 383, 383, 383, 383, 383, 383, 383, 383, 383, 383,
- 383, 383, 383, 383, 383, 383, 383, 383, 383, 383,
- 383, 383, 383, 383, 383, 439, 242, nil, nil, nil,
- nil, 243, 243, 243, 243, 243, 243, nil, 383, nil,
- nil, nil, nil, 439, 439, 439, 439, 439, 439, 439,
- 439, 439, 439, 439, 439, 439, 439, 439, 439, 439,
- 439, 439, 439, 439, 439, 439, 439, 439, 439, 439,
- 439, 439, 439, 439, 439, 439, 439, 439, 453, 243,
- nil, nil, nil, 244, 244, 244, 244, 244, 244, nil,
- nil, 439, nil, nil, nil, nil, 453, 453, 453, 453,
- 453, 453, 453, 453, 453, 453, 453, 453, 453, 453,
- 453, 453, 453, 453, 453, 453, 453, 453, 453, 453,
- 453, 453, 453, 453, 453, 453, 453, 453, 453, 453,
- 453, 244, 245, 245, 245, 245, 245, 245, nil, nil,
- nil, nil, nil, nil, 453, 145, 145, 145, 145, 145,
- 145, 145, 145, 145, 145, 145, 145, 145, 145, 145,
- 145, 145, 145, 145, 145, 145, 145, 145, 145, 145,
- 145, 145, 145, 145, 145, 145, 145, 145, 145, 145,
- 245, nil, nil, nil, nil, nil, 145, 246, 246, 246,
- 246, 246, 246, 145, 156, 156, 156, 156, 156, 156,
- 156, 156, 156, 156, 156, 156, 156, 156, 156, 156,
- 156, 156, 156, 156, 156, 156, 156, 156, 156, 156,
- 156, 156, 156, 156, 156, 156, 156, 156, 156, nil,
- nil, nil, nil, nil, nil, 246, nil, nil, nil, nil,
- nil, nil, 156, 159, 159, 159, 159, 159, 159, 159,
- 159, 159, 159, 159, 159, 159, 159, 159, 159, 159,
- 159, 159, 159, 159, 159, 159, 159, 159, 159, 159,
- 159, 159, 159, 159, 159, 159, 159, 159, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 159, 159, 168, 168, 168, 168, 168, 168, 168, 168,
- 168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
- 168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
- 168, 168, 168, 168, 168, 168, 168, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 168, 179, 179, 179, 179, 179, 179, 179, 179, 179,
- 179, 179, 179, 179, 179, 179, 179, 179, 179, 179,
- 179, 179, 179, 179, 179, 179, 179, 179, 179, 179,
- 179, 179, 179, 179, 179, 179, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 179,
- 201, 201, 201, 201, 201, 201, 201, 201, 201, 201,
- 201, 201, 201, 201, 201, 201, 201, 201, 201, 201,
- 201, 201, 201, 201, 201, 201, 201, 201, 201, 201,
- 201, 201, 201, 201, 201, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 201, 225,
- 225, 225, 225, 225, 225, 225, 225, 225, 225, 225,
- 225, 225, 225, 225, 225, 225, 225, 225, 225, 225,
- 225, 225, 225, 225, 225, 225, 225, 225, 225, 225,
- 225, 225, 225, 225, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 225, 234, 234,
- 234, 234, 234, 234, 234, 234, 234, 234, 234, 234,
- 234, 234, 234, 234, 234, 234, 253, 253, 253, 253,
- 253, 253, 253, 253, 253, 253, 253, 253, 253, 253,
- 253, 253, 253, 253, 253, 253, 253, 253, 253, 253,
- 253, 253, 253, 253, 253, 253, 234, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 253, 254, 254, 254, 254, 254,
- 254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
- 254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
- 254, 254, 254, 254, 254, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 254, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 255, 256, 256, 256, 256, 256, 256, 256,
- 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
- 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
- 256, 256, 256, 256, 256, 256, 256, 256, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 256, 257, 257, 257, 257, 257, 257, 257, 257,
- 257, 257, 257, 257, 257, 257, 257, 257, 257, 257,
- 257, 257, 257, 257, 257, 257, 257, 257, 257, 257,
- 257, 257, 257, 257, 257, 257, 257, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 257,
- 257, 259, 259, 259, 259, 259, 259, 259, 259, 259,
- 259, 259, 259, 259, 259, 259, 259, 259, 259, 259,
- 259, 259, 259, 259, 259, 259, 259, 259, 259, 259,
- 259, 259, 259, 259, 259, 259, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 259, nil, nil, 259, 259,
- 289, 289, 289, 289, 289, 289, 289, 289, 289, 289,
- 289, 289, 289, 289, 289, 289, 289, 289, 289, 289,
- 289, 289, 289, 289, 289, 289, 289, 289, 289, 289,
- 289, 289, 289, 289, 289, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 289, 292,
- 292, 292, 292, 292, 292, 292, 292, 292, 292, 292,
- 292, 292, 292, 292, 292, 292, 292, 292, 292, 292,
- 292, 292, 292, 292, 292, 292, 292, 292, 292, 292,
- 292, 292, 292, 292, nil, nil, nil, nil, nil, nil,
- 292, nil, nil, nil, nil, nil, nil, 292, 296, 296,
- 296, 296, 296, 296, 296, 296, 296, 296, 296, 296,
- 296, 296, 296, 296, 296, 296, 296, 296, 296, 296,
- 296, 296, 296, 296, 296, 296, 296, 296, 296, 296,
- 296, 296, 296, nil, nil, nil, nil, nil, nil, 296,
- nil, nil, nil, nil, nil, nil, 296, 303, 303, 303,
- 303, 303, 303, 303, 303, 303, 303, 303, 303, 303,
- 303, 303, 303, 303, 303, 303, 303, 303, 303, 303,
- 303, 303, 303, 303, 303, 303, 303, 303, 303, 303,
- 303, 303, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 303, 307, 307, 307, 307,
- 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
- 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
- 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
- 307, nil, nil, nil, nil, nil, nil, 307, nil, nil,
- nil, nil, nil, nil, 307, 308, 308, 308, 308, 308,
- 308, 308, 308, 308, 308, 308, 308, 308, 308, 308,
- 308, 308, 308, 308, 308, 308, 308, 308, 308, 308,
- 308, 308, 308, 308, 308, 308, 308, 308, 308, 308,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 308, 309, 309, 309, 309, 309, 309,
- 309, 309, 309, 309, 309, 309, 309, 309, 309, 309,
- 309, 309, 309, 309, 309, 309, 309, 309, 309, 309,
- 309, 309, 309, 309, 309, 309, 309, 309, 309, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 309, 310, 310, 310, 310, 310, 310, 310,
- 310, 310, 310, 310, 310, 310, 310, 310, 310, 310,
- 310, 310, 310, 310, 310, 310, 310, 310, 310, 310,
- 310, 310, 310, 310, 310, 310, 310, 310, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 310, 311, 311, 311, 311, 311, 311, 311, 311,
- 311, 311, 311, 311, 311, 311, 311, 311, 311, 311,
- 311, 311, 311, 311, 311, 311, 311, 311, 311, 311,
- 311, 311, 311, 311, 311, 311, 311, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 311, 338, 338, 338, 338, 338, 338, 338, 338, 338,
- 338, 338, 338, 338, 338, 338, 338, 338, 338, 338,
- 338, 338, 338, 338, 338, 338, 338, 338, 338, 338,
- 338, 338, 338, 338, 338, 338, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 338, nil, nil, 338, 338,
- 343, 343, 343, 343, 343, 343, 343, 343, 343, 343,
- 343, 343, 343, 343, 343, 343, 343, 343, 343, 343,
- 343, 343, 343, 343, 343, 343, 343, 343, 343, 343,
- 343, 343, 343, 343, 343, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 343, 347,
- 347, 347, 347, 347, 347, 347, 347, 347, 347, 347,
- 347, 347, 347, 347, 347, 347, 347, 347, 347, 347,
- 347, 347, 347, 347, 347, 347, 347, 347, 347, 347,
- 347, 347, 347, 347, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 347, nil, nil, nil, 347, 349, 349,
- 349, 349, 349, 349, 349, 349, 349, 349, 349, 349,
- 349, 349, 349, 349, 349, 349, 349, 349, 349, 349,
- 349, 349, 349, 349, 349, 349, 349, 349, 349, 349,
- 349, 349, 349, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 349, 352, 352, 352,
- 352, 352, 352, 352, 352, 352, 352, 352, 352, 352,
- 352, 352, 352, 352, 352, 352, 352, 352, 352, 352,
- 352, 352, 352, 352, 352, 352, 352, 352, 352, 352,
- 352, 352, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 352, 358, 358, 358, 358,
- 358, 358, 358, 358, 358, 358, 358, 358, 358, 358,
- 358, 358, 358, 358, 358, 358, 358, 358, 358, 358,
- 358, 358, 358, 358, 358, 358, 358, 358, 358, 358,
- 358, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 358, 364, 364, 364, 364, 364,
- 364, 364, 364, 364, 364, 364, 364, 364, 364, 364,
- 364, 364, 364, 364, 364, 364, 364, 364, 364, 364,
- 364, 364, 364, 364, 364, 364, 364, 364, 364, 364,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 364, 372, 372, 372, 372, 372, 372,
- 372, 372, 372, 372, 372, 372, 372, 372, 372, 372,
- 372, 372, 372, 372, 372, 372, 372, 372, 372, 372,
- 372, 372, 372, 372, 372, 372, 372, 372, 372, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 372, 373, 373, 373, 373, 373, 373, 373,
- 373, 373, 373, 373, 373, 373, 373, 373, 373, 373,
- 373, 373, 373, 373, 373, 373, 373, 373, 373, 373,
- 373, 373, 373, 373, 373, 373, 373, 373, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 373, 395, 395, 395, 395, 395, 395, 395, 395,
- 395, 395, 395, 395, 395, 395, 395, 395, 395, 395,
- 395, 395, 395, 395, 395, 395, 395, 395, 395, 395,
- 395, 395, 395, 395, 395, 395, 395, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 395, 398, 398, 398, 398, 398, 398, 398, 398, 398,
- 398, 398, 398, 398, 398, 398, 398, 398, 398, 398,
- 398, 398, 398, 398, 398, 398, 398, 398, 398, 398,
- 398, 398, 398, 398, 398, 398, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 398,
- 405, 405, 405, 405, 405, 405, 405, 405, 405, 405,
- 405, 405, 405, 405, 405, 405, 405, 405, 405, 405,
- 405, 405, 405, 405, 405, 405, 405, 405, 405, 405,
- 405, 405, 405, 405, 405, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 405, 452,
- 452, 452, 452, 452, 452, 452, 452, 452, 452, 452,
- 452, 452, 452, 452, 452, 452, 452, 452, 452, 452,
- 452, 452, 452, 452, 452, 452, 452, 452, 452, 452,
- 452, 452, 452, 452, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 452 ]
-
-racc_action_pointer = [
- 81, 132, 172, -7, nil, nil, nil, nil, 209, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 66,
- nil, nil, -2, 76, 182, nil, nil, 160, nil, 1805,
- nil, 328, 219, 324, nil, 225, nil, nil, nil, nil,
- 117, nil, 342, 229, nil, nil, nil, nil, nil, nil,
- nil, nil, 1838, nil, 1871, 82, 1904, -7, 1155, 103,
- nil, 1937, 1970, 2003, 4409, nil, 348, nil, nil, nil,
- 288, nil, 120, 214, -42, nil, nil, nil, 2036, nil,
- 2069, 2102, 2135, nil, nil, 2168, 178, 322, 317, 324,
- 296, nil, 397, -3, 263, 354, 311, 362, nil, 4442,
- 2201, 2234, 2267, 2300, 2333, 2366, 2399, 2432, 2465, 2498,
- 2531, 2564, 2597, 2630, 2663, 2696, 2729, 2762, 2795, 2828,
- 2861, 2894, 2927, 2960, 2993, 3026, 3059, 3092, 3125, 3158,
- 3191, 3224, 3257, 3290, 3323, 3356, 3389, 3422, 160, 1045,
- 381, 388, 4475, -89, nil, 4886, 347, 1218, 401, nil,
- 402, 404, nil, nil, 394, -70, 4935, -75, nil, 4984,
- 398, 137, 143, nil, 3455, 1252, 400, nil, 5033, 400,
- 447, 1316, nil, 3488, 399, 121, 1350, nil, 404, 5082,
- 3521, 126, -25, 405, 452, 398, 192, 273, 3554, 404,
- nil, 3587, 3620, 3653, 3686, nil, nil, nil, nil, 456,
- 127, 5131, 38, 400, 479, 4519, 4572, 483, 484, 485,
- 145, 562, 460, 129, nil, 1414, nil, nil, nil, nil,
- 569, 660, 283, 751, 842, 5180, 1170, 1266, 1364, 1462,
- 1560, 1657, 1755, 3831, 5229, 388, 466, 472, 4507, 4560,
- 4613, 4666, 4719, 4772, 4824, 4873, 4928, 516, 780, 406,
- 407, 871, 408, 5247, 5296, 5345, 5394, 5443, 1121, 5492,
- 413, 3719, nil, nil, nil, nil, 185, 1448, 417, nil,
- 3752, nil, 3785, nil, 1512, 3818, 1546, 135, 144, nil,
- nil, 218, 231, 54, 145, 233, 155, 67, 3881, 5541,
- 256, 3914, 5590, 418, 3947, 1610, 5639, 419, 42, 419,
- 423, 423, 1644, 5688, 145, 470, nil, 5737, 5786, 5835,
- 5884, 5933, 472, 185, 188, 225, 3980, 4013, 568, 4046,
- 653, nil, 571, 4079, 574, 575, 4112, 4145, 4178, nil,
- 29, 385, 55, -26, 499, 517, 521, 4211, 5982, 519,
- nil, 4244, nil, 6031, 573, 525, nil, 6080, nil, 6129,
- nil, nil, 6178, 562, nil, 562, 563, nil, 6227, 577,
- 191, nil, 1707, nil, 6276, 4277, nil, 627, 628, -20,
- -9, nil, 6325, 6374, nil, 973, 3, nil, 1050, nil,
- nil, 4625, 4678, 4731, nil, 642, 389, 325, nil, 648,
- nil, 688, 652, 1741, nil, 6423, nil, nil, 6472, nil,
- nil, nil, nil, 213, 612, 6521, nil, nil, 696, nil,
- 241, 614, 623, nil, nil, nil, 370, 677, 4310, nil,
- 734, 736, 741, 742, nil, 720, nil, nil, 716, 670,
- 214, nil, 721, -7, 722, 4343, nil, 4376, 744, 4784,
- nil, nil, nil, nil, nil, 739, nil, 111, 696, 703,
- nil, 113, 6570, 4837, 66, 835, 786, 787, 753, 791,
- 926, 76, nil, nil, 114, nil, 100, 792, nil ]
-
-racc_action_default = [
- -1, -259, -2, -3, -4, -8, -9, -10, -11, -12,
- -13, -14, -15, -16, -17, -18, -19, -20, -21, -22,
- -23, -24, -25, -26, -27, -29, -30, -31, -32, -116,
- -34, -35, -36, -37, -38, -39, -40, -49, -50, -51,
- -52, -53, -54, -55, -56, -57, -58, -59, -60, -63,
- -64, -65, -69, -72, -75, -259, -116, -116, -119, -116,
- -115, -116, -116, -116, -116, -168, -259, -177, -179, -180,
- -259, -184, -116, -116, -116, -200, -201, -202, -217, -219,
- -116, -116, -116, -228, -229, -116, -116, -259, -116, -116,
- -257, -258, -259, -7, -116, -6, -259, -259, -188, -116,
- -116, -116, -116, -116, -116, -116, -116, -116, -116, -116,
- -116, -116, -116, -116, -116, -116, -116, -116, -116, -116,
- -116, -116, -116, -116, -116, -116, -116, -116, -116, -116,
- -116, -116, -116, -116, -116, -116, -116, -116, -83, -116,
- -28, -259, -116, -26, -31, -259, -259, -116, -80, -94,
- -79, -81, -61, -62, -182, -259, -70, -259, -76, -259,
- -259, -183, -185, -189, -116, -116, -101, -102, -127, -35,
- -37, -116, -54, -69, -259, -259, -116, -107, -120, -123,
- -116, -111, -116, -109, -259, -164, -165, -166, -116, -259,
- -167, -116, -116, -116, -116, -181, -186, -187, -117, -259,
- -259, -218, -214, -259, -259, -259, -259, -259, -259, -259,
- -237, -245, -259, -259, -255, -116, 469, -5, -183, -170,
- -129, -130, -131, -132, -133, -134, -135, -136, -137, -138,
- -139, -140, -141, -142, -143, -144, -145, -146, -147, -148,
- -149, -150, -151, -152, -153, -154, -155, -156, -157, -158,
- -159, -160, -161, -162, -163, -222, -225, -259, -116, -259,
- -259, -93, -97, -96, -169, -41, -33, -116, -259, -95,
- -116, -67, -116, -73, -116, -116, -116, -259, -190, -191,
- -192, -29, -30, -35, -36, -37, -39, -52, -75, -259,
- -259, -116, -123, -259, -116, -116, -123, -259, -259, -83,
- -259, -259, -116, -124, -259, -116, -110, -259, -171, -172,
- -173, -174, -259, -204, -203, -207, -116, -116, -259, -116,
- -116, -246, -259, -116, -259, -259, -116, -116, -116, -234,
- -259, -259, -244, -259, -105, -122, -259, -116, -259, -259,
- -86, -91, -87, -92, -82, -84, -99, -259, -68, -71,
- -74, -77, -78, -259, -193, -259, -259, -100, -128, -259,
- -259, -103, -116, -106, -125, -116, -108, -259, -116, -259,
- -208, -205, -215, -216, -220, -259, -244, -223, -259, -227,
- -230, -259, -259, -259, -235, -259, -259, -243, -238, -259,
- -242, -259, -116, -116, -256, -226, -88, -89, -90, -66,
- -98, -194, -195, -114, -259, -126, -112, -118, -259, -206,
- -259, -259, -209, -210, -221, -247, -248, -259, -116, -224,
- -259, -259, -259, -259, -240, -259, -239, -253, -259, -259,
- -113, -196, -207, -259, -207, -116, -249, -116, -116, -259,
- -231, -232, -233, -236, -241, -259, -104, -259, -259, -211,
- -212, -259, -213, -259, -250, -116, -259, -259, -207, -259,
- -116, -251, -254, -197, -259, -198, -252, -259, -199 ]
-
-racc_goto_table = [
- 2, 154, 3, 95, 27, 169, 27, 96, 210, 98,
- 390, 170, 148, 281, 155, 151, 183, 369, 413, 260,
- 389, 415, 172, 318, 322, 1, 282, 217, 140, 356,
- 284, 286, 209, 351, 166, 214, 97, 200, 335, 336,
- 190, 207, 410, 202, 177, 330, 416, nil, nil, nil,
- nil, 208, nil, nil, 154, nil, nil, nil, nil, nil,
- nil, 436, nil, nil, nil, 390, nil, nil, nil, nil,
- nil, 96, 96, 196, 197, 425, nil, nil, nil, nil,
- nil, 450, nil, nil, nil, 175, nil, 184, nil, nil,
- nil, nil, 160, 95, nil, nil, 145, nil, 27, 27,
- 97, 97, 198, 219, nil, nil, 181, nil, nil, nil,
- nil, nil, 283, nil, 97, nil, 212, 213, 285, 156,
- nil, 159, nil, 168, nil, 179, 324, 325, 185, 186,
- 187, 162, 299, 268, 447, 298, 451, 281, 339, 304,
- 281, 280, nil, 291, nil, 201, 264, 203, 204, 205,
- 282, 293, 206, 282, 284, 286, nil, 284, 286, nil,
- 464, nil, 301, nil, nil, nil, nil, 220, 221, 222,
- 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
- 233, 234, 235, 236, 237, 238, 239, 240, 241, 242,
- 243, 244, 245, 246, 247, 248, 249, 250, 251, 252,
- 253, 254, 255, 256, 257, nil, 259, nil, nil, nil,
- 184, 287, 300, 332, 179, 27, nil, nil, nil, nil,
- nil, nil, nil, nil, 345, nil, nil, nil, nil, 181,
- nil, 289, 292, nil, nil, nil, 283, 315, 296, 283,
- 156, nil, 285, 292, nil, 285, 344, 303, nil, nil,
- 334, 162, 388, nil, 391, 307, nil, 280, 308, 309,
- 310, 311, 353, nil, 148, nil, 151, 291, nil, 355,
- nil, nil, nil, nil, nil, nil, 314, nil, nil, nil,
- nil, 359, 179, nil, nil, nil, nil, nil, nil, 314,
- 408, nil, nil, 280, nil, nil, nil, nil, nil, nil,
- nil, nil, 420, 421, 422, nil, 423, 424, nil, nil,
- 426, nil, nil, nil, nil, nil, nil, nil, 345, nil,
- nil, nil, 376, nil, 27, 338, nil, nil, 343, nil,
- nil, nil, 95, 367, 179, 287, 143, 347, 287, 349,
- 344, 159, 352, 179, nil, nil, 444, nil, 404, nil,
- nil, nil, nil, nil, nil, 159, nil, nil, 289, nil,
- nil, 358, 179, nil, 174, nil, 456, nil, 457, 364,
- nil, 189, 459, nil, nil, nil, 95, nil, 387, 429,
- nil, nil, nil, 372, 373, 467, 375, nil, nil, nil,
- 378, nil, nil, 381, 382, 383, 407, nil, nil, nil,
- nil, nil, nil, nil, 395, nil, 189, nil, 398, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 428, nil, nil, nil, nil, nil, nil, nil, nil, 179,
- nil, nil, 405, 387, nil, nil, nil, nil, nil, nil,
- 454, nil, 27, nil, nil, nil, nil, nil, nil, 189,
- nil, nil, nil, nil, 95, nil, nil, 461, nil, 27,
- 179, 95, 466, nil, 27, nil, 95, nil, nil, nil,
- nil, nil, 143, nil, nil, nil, nil, nil, 143, nil,
- nil, nil, nil, 143, nil, 439, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 143, nil, nil, nil, nil,
- nil, nil, 452, nil, 453 ]
-
-racc_goto_check = [
- 2, 50, 3, 2, 28, 31, 28, 42, 70, 64,
- 40, 33, 49, 26, 45, 49, 58, 69, 72, 52,
- 76, 77, 41, 74, 74, 1, 27, 4, 25, 37,
- 32, 35, 44, 48, 56, 57, 53, 59, 60, 61,
- 62, 65, 71, 73, 55, 75, 78, nil, nil, nil,
- nil, 41, nil, nil, 50, nil, nil, nil, nil, nil,
- nil, 77, nil, nil, nil, 40, nil, nil, nil, nil,
- nil, 42, 42, 64, 64, 76, nil, nil, nil, nil,
- nil, 72, nil, nil, nil, 53, nil, 53, nil, nil,
- nil, nil, 54, 2, nil, nil, 22, nil, 28, 28,
- 53, 53, 53, 28, nil, nil, 38, nil, nil, nil,
- nil, nil, 31, nil, 53, nil, 53, 53, 33, 22,
- nil, 22, nil, 22, nil, 22, 70, 70, 22, 22,
- 22, 66, 50, 55, 69, 45, 69, 26, 52, 58,
- 26, 67, nil, 67, nil, 22, 28, 22, 22, 22,
- 27, 55, 22, 27, 32, 35, nil, 32, 35, nil,
- 69, nil, 55, nil, nil, nil, nil, 22, 22, 22,
- 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
- 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
- 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
- 22, 22, 22, 22, 22, nil, 22, nil, nil, nil,
- 53, 38, 54, 3, 22, 28, nil, nil, nil, nil,
- nil, nil, nil, nil, 50, nil, nil, nil, nil, 38,
- nil, 22, 22, nil, nil, nil, 31, 54, 22, 31,
- 22, nil, 33, 22, nil, 33, 49, 22, nil, nil,
- 54, 66, 70, nil, 70, 22, nil, 67, 22, 22,
- 22, 22, 55, nil, 49, nil, 49, 67, nil, 67,
- nil, nil, nil, nil, nil, nil, 66, nil, nil, nil,
- nil, 55, 22, nil, nil, nil, nil, nil, nil, 66,
- 70, nil, nil, 67, nil, nil, nil, nil, nil, nil,
- nil, nil, 70, 70, 70, nil, 70, 70, nil, nil,
- 70, nil, nil, nil, nil, nil, nil, nil, 50, nil,
- nil, nil, 3, nil, 28, 22, nil, nil, 22, nil,
- nil, nil, 2, 53, 22, 38, 23, 22, 38, 22,
- 49, 22, 22, 22, nil, nil, 70, nil, 55, nil,
- nil, nil, nil, nil, nil, 22, nil, nil, 22, nil,
- nil, 22, 22, nil, 23, nil, 70, nil, 70, 22,
- nil, 23, 70, nil, nil, nil, 2, nil, 38, 55,
- nil, nil, nil, 22, 22, 70, 22, nil, nil, nil,
- 22, nil, nil, 22, 22, 22, 53, nil, nil, nil,
- nil, nil, nil, nil, 22, nil, 23, nil, 22, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 53, nil, nil, nil, nil, nil, nil, nil, nil, 22,
- nil, nil, 22, 38, nil, nil, nil, nil, nil, nil,
- 3, nil, 28, nil, nil, nil, nil, nil, nil, 23,
- nil, nil, nil, nil, 2, nil, nil, 3, nil, 28,
- 22, 2, 3, nil, 28, nil, 2, nil, nil, nil,
- nil, nil, 23, nil, nil, nil, nil, nil, 23, nil,
- nil, nil, nil, 23, nil, 22, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 23, nil, nil, nil, nil,
- nil, nil, 22, nil, 22 ]
-
-racc_goto_pointer = [
- nil, 25, 0, 2, -68, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 67, 307, nil, 4, -151, -138, 4, nil,
- nil, -52, -134, -46, nil, -133, nil, -262, 47, nil,
- -321, -35, -1, nil, -54, -38, nil, nil, -241, -20,
- -42, nil, -120, 28, 37, -14, -22, -54, -43, -37,
- -177, -176, -24, nil, 1, -45, 76, -21, nil, -298,
- -79, -328, -352, -35, -180, -165, -311, -355, -330 ]
-
-racc_goto_default = [
- nil, nil, 94, 93, 4, 5, 6, 7, 8, 9,
- 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
- 20, 21, 22, 23, 24, nil, 25, 26, 144, 28,
- 30, 31, 32, 33, 34, 35, 36, 290, 40, 39,
- 41, 42, 43, 51, 67, nil, 53, 157, 158, 150,
- 138, 68, nil, 55, nil, 297, nil, nil, nil, nil,
- 178, nil, 65, 66, 71, 69, 278, 163, 74, nil,
- 321, nil, nil, nil, nil, nil, nil, nil, nil ]
-
-racc_reduce_table = [
- 0, 0, :racc_error,
- 0, 99, :_reduce_1,
- 1, 99, :_reduce_2,
- 1, 99, :_reduce_3,
- 1, 101, :_reduce_4,
- 3, 101, :_reduce_5,
- 2, 101, :_reduce_6,
- 2, 101, :_reduce_7,
- 1, 102, :_reduce_8,
- 1, 102, :_reduce_9,
- 1, 102, :_reduce_10,
- 1, 102, :_reduce_11,
- 1, 102, :_reduce_12,
- 1, 102, :_reduce_13,
- 1, 102, :_reduce_14,
- 1, 102, :_reduce_15,
- 1, 102, :_reduce_16,
- 1, 102, :_reduce_17,
- 1, 102, :_reduce_18,
- 1, 102, :_reduce_19,
- 1, 102, :_reduce_20,
- 1, 102, :_reduce_21,
- 1, 102, :_reduce_22,
- 1, 102, :_reduce_23,
- 1, 102, :_reduce_24,
- 1, 102, :_reduce_25,
- 1, 120, :_reduce_26,
- 1, 120, :_reduce_27,
- 2, 120, :_reduce_28,
- 1, 120, :_reduce_29,
- 1, 120, :_reduce_30,
- 1, 120, :_reduce_31,
- 1, 120, :_reduce_32,
- 3, 120, :_reduce_33,
- 1, 121, :_reduce_34,
- 1, 121, :_reduce_35,
- 1, 121, :_reduce_36,
- 1, 121, :_reduce_37,
- 1, 121, :_reduce_38,
- 1, 121, :_reduce_39,
- 1, 121, :_reduce_40,
- 3, 121, :_reduce_41,
- 1, 135, :_reduce_42,
- 1, 135, :_reduce_43,
- 1, 135, :_reduce_44,
- 1, 135, :_reduce_45,
- 1, 135, :_reduce_46,
- 1, 135, :_reduce_47,
- 1, 135, :_reduce_48,
- 1, 100, :_reduce_49,
- 1, 100, :_reduce_50,
- 1, 132, :_reduce_51,
- 1, 132, :_reduce_52,
- 1, 132, :_reduce_53,
- 1, 132, :_reduce_54,
- 1, 132, :_reduce_55,
- 1, 132, :_reduce_56,
- 1, 132, :_reduce_57,
- 1, 137, :_reduce_58,
- 1, 136, :_reduce_59,
- 1, 136, :_reduce_60,
- 2, 136, :_reduce_61,
- 2, 136, :_reduce_62,
- 1, 138, :_reduce_63,
- 1, 140, :_reduce_64,
- 1, 139, :_reduce_65,
- 5, 142, :_reduce_66,
- 3, 141, :_reduce_67,
- 4, 141, :_reduce_68,
- 0, 143, :_reduce_69,
- 1, 143, :_reduce_70,
- 3, 143, :_reduce_71,
- 1, 122, :_reduce_72,
- 3, 144, :_reduce_73,
- 4, 144, :_reduce_74,
- 0, 145, :_reduce_75,
- 1, 145, :_reduce_76,
- 3, 145, :_reduce_77,
- 3, 146, :_reduce_78,
- 2, 129, :_reduce_79,
- 2, 129, :_reduce_80,
- 2, 129, :_reduce_81,
- 4, 129, :_reduce_82,
- 2, 130, :_reduce_83,
- 4, 130, :_reduce_84,
- 2, 149, :_reduce_85,
- 3, 148, :_reduce_86,
- 3, 148, :_reduce_87,
- 4, 148, :_reduce_88,
- 4, 148, :_reduce_89,
- 3, 150, :_reduce_90,
- 2, 150, :_reduce_91,
- 2, 150, :_reduce_92,
- 1, 150, :_reduce_93,
- 1, 147, :_reduce_94,
- 2, 147, :_reduce_95,
- 2, 123, :_reduce_96,
- 2, 123, :_reduce_97,
- 5, 133, :_reduce_98,
- 4, 133, :_reduce_99,
- 4, 133, :_reduce_100,
- 2, 133, :_reduce_101,
- 2, 133, :_reduce_102,
- 4, 133, :_reduce_103,
- 5, 155, :_reduce_104,
- 2, 155, :_reduce_105,
- 4, 115, :_reduce_106,
- 2, 115, :_reduce_107,
- 4, 116, :_reduce_108,
- 2, 116, :_reduce_109,
- 2, 156, :_reduce_110,
- 1, 156, :_reduce_111,
- 4, 156, :_reduce_112,
- 6, 103, :_reduce_113,
- 5, 103, :_reduce_114,
- 1, 151, :_reduce_115,
- 0, 151, :_reduce_116,
- 1, 157, :_reduce_117,
- 4, 157, :_reduce_118,
- 0, 153, :_reduce_119,
- 1, 153, :_reduce_120,
- 0, 159, :_reduce_121,
- 1, 159, :_reduce_122,
- 1, 158, :_reduce_123,
- 2, 158, :_reduce_124,
- 3, 158, :_reduce_125,
- 4, 158, :_reduce_126,
- 1, 154, :_reduce_127,
- 3, 154, :_reduce_128,
- 3, 124, :_reduce_129,
- 3, 124, :_reduce_130,
- 3, 124, :_reduce_131,
- 3, 124, :_reduce_132,
- 3, 124, :_reduce_133,
- 3, 124, :_reduce_134,
- 3, 124, :_reduce_135,
- 3, 124, :_reduce_136,
- 3, 124, :_reduce_137,
- 3, 124, :_reduce_138,
- 3, 124, :_reduce_139,
- 3, 124, :_reduce_140,
- 3, 124, :_reduce_141,
- 3, 124, :_reduce_142,
- 3, 124, :_reduce_143,
- 3, 124, :_reduce_144,
- 3, 124, :_reduce_145,
- 3, 124, :_reduce_146,
- 3, 124, :_reduce_147,
- 3, 124, :_reduce_148,
- 3, 124, :_reduce_149,
- 3, 124, :_reduce_150,
- 3, 124, :_reduce_151,
- 3, 124, :_reduce_152,
- 3, 124, :_reduce_153,
- 3, 124, :_reduce_154,
- 3, 124, :_reduce_155,
- 3, 124, :_reduce_156,
- 3, 124, :_reduce_157,
- 3, 124, :_reduce_158,
- 3, 124, :_reduce_159,
- 3, 124, :_reduce_160,
- 3, 124, :_reduce_161,
- 3, 124, :_reduce_162,
- 3, 124, :_reduce_163,
- 2, 128, :_reduce_164,
- 2, 128, :_reduce_165,
- 2, 128, :_reduce_166,
- 2, 126, :_reduce_167,
- 1, 126, :_reduce_168,
- 3, 117, :_reduce_169,
- 3, 117, :_reduce_170,
- 3, 160, :_reduce_171,
- 3, 160, :_reduce_172,
- 3, 160, :_reduce_173,
- 3, 160, :_reduce_174,
- 1, 161, :_reduce_175,
- 1, 161, :_reduce_176,
- 1, 161, :_reduce_177,
- 1, 161, :_reduce_178,
- 1, 161, :_reduce_179,
- 1, 162, :_reduce_180,
- 2, 162, :_reduce_181,
- 2, 162, :_reduce_182,
- 2, 163, :_reduce_183,
- 1, 131, :_reduce_184,
- 2, 131, :_reduce_185,
- 2, 106, :_reduce_186,
- 2, 106, :_reduce_187,
- 2, 106, :_reduce_188,
- 1, 164, :_reduce_189,
- 2, 164, :_reduce_190,
- 2, 164, :_reduce_191,
- 2, 164, :_reduce_192,
- 3, 165, :_reduce_193,
- 4, 165, :_reduce_194,
- 4, 165, :_reduce_195,
- 6, 104, :_reduce_196,
- 9, 104, :_reduce_197,
- 9, 104, :_reduce_198,
- 11, 104, :_reduce_199,
- 1, 166, :_reduce_200,
- 1, 166, :_reduce_201,
- 1, 166, :_reduce_202,
- 1, 152, :_reduce_203,
- 1, 152, :_reduce_204,
- 1, 167, :_reduce_205,
- 2, 167, :_reduce_206,
- 0, 167, :_reduce_207,
- 0, 169, :_reduce_208,
- 1, 169, :_reduce_209,
- 1, 169, :_reduce_210,
- 3, 169, :_reduce_211,
- 3, 169, :_reduce_212,
- 3, 170, :_reduce_213,
- 2, 105, :_reduce_214,
- 4, 105, :_reduce_215,
- 4, 105, :_reduce_216,
- 0, 171, :_reduce_217,
- 1, 171, :_reduce_218,
- 1, 114, :_reduce_219,
- 4, 118, :_reduce_220,
- 5, 118, :_reduce_221,
- 3, 118, :_reduce_222,
- 4, 119, :_reduce_223,
- 5, 119, :_reduce_224,
- 3, 119, :_reduce_225,
- 5, 125, :_reduce_226,
- 4, 109, :_reduce_227,
- 1, 113, :_reduce_228,
- 1, 113, :_reduce_229,
- 4, 110, :_reduce_230,
- 6, 108, :_reduce_231,
- 6, 108, :_reduce_232,
- 6, 108, :_reduce_233,
- 3, 111, :_reduce_234,
- 4, 111, :_reduce_235,
- 6, 111, :_reduce_236,
- 0, 173, :_reduce_237,
- 2, 173, :_reduce_238,
- 3, 173, :_reduce_239,
- 3, 173, :_reduce_240,
- 4, 173, :_reduce_241,
- 1, 174, :_reduce_242,
- 1, 174, :_reduce_243,
- 2, 168, :_reduce_244,
- 1, 168, :_reduce_245,
- 1, 172, :_reduce_246,
- 3, 172, :_reduce_247,
- 3, 172, :_reduce_248,
- 4, 172, :_reduce_249,
- 3, 175, :_reduce_250,
- 4, 176, :_reduce_251,
- 5, 176, :_reduce_252,
- 5, 112, :_reduce_253,
- 8, 112, :_reduce_254,
- 2, 134, :_reduce_255,
- 4, 127, :_reduce_256,
- 1, 127, :_reduce_257,
- 1, 107, :_reduce_258 ]
-
-racc_reduce_n = 259
-
-racc_shift_n = 469
-
-racc_token_table = {
- false => 0,
- :error => 1,
- :IF => 2,
- :ELSE => 3,
- :ELSEIF => 4,
- :THEN => 5,
- :UNLESS => 6,
- :END => 7,
- :WHILE => 8,
- :UNTIL => 9,
- :BREAK => 10,
- :CONTINUE => 11,
- :TRY => 12,
- :CATCH => 13,
- :FINALLY => 14,
- :FOR => 15,
- :IN => 16,
- :DEF => 17,
- :DEF_BANG => 18,
- :SPLAT_PARAM => 19,
- :SPLAT_ARG => 20,
- :CALL => 21,
- :BUILTIN_COMMAND => 22,
- :CLASS => 23,
- :NEW => 24,
- :DEFM => 25,
- :DEFM_BANG => 26,
- :SUPER => 27,
- :RIML_FILE_COMMAND => 28,
- :RIML_CLASS_COMMAND => 29,
- :RETURN => 30,
- :NEWLINE => 31,
- :NUMBER => 32,
- :STRING_D => 33,
- :STRING_S => 34,
- :EX_LITERAL => 35,
- :REGEXP => 36,
- :TRUE => 37,
- :FALSE => 38,
- :LET => 39,
- :UNLET => 40,
- :UNLET_BANG => 41,
- :IDENTIFIER => 42,
- :DICT_VAL => 43,
- :SCOPE_MODIFIER => 44,
- :SCOPE_MODIFIER_LITERAL => 45,
- :SPECIAL_VAR_PREFIX => 46,
- :FINISH => 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,
- :IS => 79,
- :ISNOT => 80,
- "&&" => 81,
- "||" => 82,
- "?" => 83,
- "=" => 84,
- "+=" => 85,
- "-=" => 86,
- ".=" => 87,
- "," => 88,
- "(" => 89,
- ")" => 90,
- ";" => 91,
- "[" => 92,
- "]" => 93,
- "{" => 94,
- "}" => 95,
- ":" => 96,
- "===" => 97 }
-
-racc_nt_base = 98
-
-racc_use_result_var = true
-
-Racc_arg = [
- racc_action_table,
- racc_action_check,
- racc_action_default,
- racc_action_pointer,
- racc_goto_table,
- racc_goto_check,
- racc_goto_default,
- racc_goto_pointer,
- racc_nt_base,
- racc_reduce_table,
- racc_token_table,
- racc_shift_n,
- racc_reduce_n,
- racc_use_result_var ]
-Ractor.make_shareable(Racc_arg) if defined?(Ractor)
-
-Racc_token_to_s_table = [
- "$end",
- "error",
- "IF",
- "ELSE",
- "ELSEIF",
- "THEN",
- "UNLESS",
- "END",
- "WHILE",
- "UNTIL",
- "BREAK",
- "CONTINUE",
- "TRY",
- "CATCH",
- "FINALLY",
- "FOR",
- "IN",
- "DEF",
- "DEF_BANG",
- "SPLAT_PARAM",
- "SPLAT_ARG",
- "CALL",
- "BUILTIN_COMMAND",
- "CLASS",
- "NEW",
- "DEFM",
- "DEFM_BANG",
- "SUPER",
- "RIML_FILE_COMMAND",
- "RIML_CLASS_COMMAND",
- "RETURN",
- "NEWLINE",
- "NUMBER",
- "STRING_D",
- "STRING_S",
- "EX_LITERAL",
- "REGEXP",
- "TRUE",
- "FALSE",
- "LET",
- "UNLET",
- "UNLET_BANG",
- "IDENTIFIER",
- "DICT_VAL",
- "SCOPE_MODIFIER",
- "SCOPE_MODIFIER_LITERAL",
- "SPECIAL_VAR_PREFIX",
- "FINISH",
- "\"!\"",
- "\"*\"",
- "\"/\"",
- "\"%\"",
- "\"+\"",
- "\"-\"",
- "\".\"",
- "\">\"",
- "\">#\"",
- "\">?\"",
- "\"<\"",
- "\"<#\"",
- "\"<?\"",
- "\">=\"",
- "\">=#\"",
- "\">=?\"",
- "\"<=\"",
- "\"<=#\"",
- "\"<=?\"",
- "\"==\"",
- "\"==?\"",
- "\"==#\"",
- "\"=~\"",
- "\"=~?\"",
- "\"=~#\"",
- "\"!~\"",
- "\"!~?\"",
- "\"!~#\"",
- "\"!=\"",
- "\"!=?\"",
- "\"!=#\"",
- "IS",
- "ISNOT",
- "\"&&\"",
- "\"||\"",
- "\"?\"",
- "\"=\"",
- "\"+=\"",
- "\"-=\"",
- "\".=\"",
- "\",\"",
- "\"(\"",
- "\")\"",
- "\";\"",
- "\"[\"",
- "\"]\"",
- "\"{\"",
- "\"}\"",
- "\":\"",
- "\"===\"",
- "$start",
- "Root",
- "Terminator",
- "Statements",
- "Statement",
- "ExplicitCall",
- "Def",
- "Return",
- "UnletVariable",
- "ExLiteral",
- "For",
- "While",
- "Until",
- "Try",
- "ClassDefinition",
- "LoopKeyword",
- "EndScript",
- "RimlFileCommand",
- "RimlClassCommand",
- "MultiAssign",
- "If",
- "Unless",
- "Expression",
- "ExpressionWithoutDictLiteral",
- "Dictionary",
- "DictGetWithDotLiteral",
- "BinaryOperator",
- "Ternary",
- "Assign",
- "Super",
- "UnaryOperator",
- "DictGet",
- "ListOrDictGet",
- "AllVariableRetrieval",
- "LiteralWithoutDictLiteral",
- "Call",
- "ObjectInstantiation",
- "PossibleStringValue",
- "String",
- "Number",
- "Regexp",
- "List",
- "ScopeModifierLiteral",
- "ListLiteral",
- "ListUnpack",
- "ListItems",
- "DictionaryLiteral",
- "DictItems",
- "DictItem",
- "DictGetWithDot",
- "ListOrDictGetWithBrackets",
- "ListOrDictGetAssign",
- "SubList",
- "Scope",
- "DefCallIdentifier",
- "ArgList",
- "ArgListWithoutNothing",
- "ObjectInstantiationCall",
- "ClassArgList",
- "SIDAndScope",
- "ArgListWithoutNothingWithSplat",
- "ArgListWithSplat",
- "AssignExpression",
- "AssignLHS",
- "VariableRetrieval",
- "SimpleVariableRetrieval",
- "CurlyBraceName",
- "CurlyBraceVarPart",
- "FunctionType",
- "DefKeywords",
- "Block",
- "ParamList",
- "DefaultParam",
- "Returnable",
- "IfBlock",
- "Catch",
- "Catchable",
- "ElseBlock",
- "ElseifBlock" ]
-Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)
-
-Racc_debug_parser = false
-
-##### State transition tables end #####
-
-# reduce 0 omitted
-
-module_eval(<<'.,.,', 'riml.y', 61)
- def _reduce_1(val, _values, result)
- result = make_node(val) { |_| Riml::Nodes.new([]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 62)
- def _reduce_2(val, _values, result)
- result = make_node(val) { |_| Riml::Nodes.new([]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 63)
- def _reduce_3(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 68)
- def _reduce_4(val, _values, result)
- result = make_node(val) { |v| Riml::Nodes.new([ v[0] ]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 69)
- def _reduce_5(val, _values, result)
- result = val[0] << val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 70)
- def _reduce_6(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 71)
- def _reduce_7(val, _values, result)
- result = make_node(val) { |v| Riml::Nodes.new(v[1]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 76)
- def _reduce_8(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 77)
- def _reduce_9(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 78)
- def _reduce_10(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 79)
- def _reduce_11(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 80)
- def _reduce_12(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 81)
- def _reduce_13(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 82)
- def _reduce_14(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 83)
- def _reduce_15(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 84)
- def _reduce_16(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 85)
- def _reduce_17(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 86)
- def _reduce_18(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 87)
- def _reduce_19(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 88)
- def _reduce_20(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 89)
- def _reduce_21(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 90)
- def _reduce_22(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 91)
- def _reduce_23(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 92)
- def _reduce_24(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 93)
- def _reduce_25(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 97)
- def _reduce_26(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 98)
- def _reduce_27(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 99)
- def _reduce_28(val, _values, result)
- result = make_node(val) { |v| Riml::DictGetDotNode.new(v[0], v[1]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 100)
- def _reduce_29(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 101)
- def _reduce_30(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 102)
- def _reduce_31(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 103)
- def _reduce_32(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 104)
- def _reduce_33(val, _values, result)
- result = make_node(val) { |v| Riml::WrapInParensNode.new(v[1]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 108)
- def _reduce_34(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 109)
- def _reduce_35(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 110)
- def _reduce_36(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 111)
- def _reduce_37(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 112)
- def _reduce_38(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 113)
- def _reduce_39(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 114)
- def _reduce_40(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 115)
- def _reduce_41(val, _values, result)
- result = make_node(val) { |v| Riml::WrapInParensNode.new(v[1]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 120)
- def _reduce_42(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 121)
- def _reduce_43(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 122)
- def _reduce_44(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 123)
- def _reduce_45(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 124)
- def _reduce_46(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 125)
- def _reduce_47(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 126)
- def _reduce_48(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 130)
- def _reduce_49(val, _values, result)
- result = nil
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 131)
- def _reduce_50(val, _values, result)
- result = nil
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 135)
- def _reduce_51(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 136)
- def _reduce_52(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 137)
- def _reduce_53(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 138)
- def _reduce_54(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 139)
- def _reduce_55(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 140)
- def _reduce_56(val, _values, result)
- result = make_node(val) { |_| Riml::TrueNode.new }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 141)
- def _reduce_57(val, _values, result)
- result = make_node(val) { |_| Riml::FalseNode.new }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 145)
- def _reduce_58(val, _values, result)
- result = make_node(val) { |v| Riml::NumberNode.new(v[0]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 149)
- def _reduce_59(val, _values, result)
- result = make_node(val) { |v| Riml::StringNode.new(v[0], :s) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 150)
- def _reduce_60(val, _values, result)
- result = make_node(val) { |v| Riml::StringNode.new(v[0], :d) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 151)
- def _reduce_61(val, _values, result)
- result = make_node(val) { |v| Riml::StringLiteralConcatNode.new(v[0], Riml::StringNode.new(v[1], :s)) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 152)
- def _reduce_62(val, _values, result)
- result = make_node(val) { |v| Riml::StringLiteralConcatNode.new(v[0], Riml::StringNode.new(v[1], :d)) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 156)
- def _reduce_63(val, _values, result)
- result = make_node(val) { |v| Riml::RegexpNode.new(v[0]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 160)
- def _reduce_64(val, _values, result)
- result = make_node(val) { |v| Riml::ScopeModifierLiteralNode.new(v[0]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 164)
- def _reduce_65(val, _values, result)
- result = make_node(val) { |v| Riml::ListNode.new(v[0]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 168)
- def _reduce_66(val, _values, result)
- result = make_node(val) { |v| Riml::ListUnpackNode.new(v[1] << v[3]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 172)
- def _reduce_67(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 173)
- def _reduce_68(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 177)
- def _reduce_69(val, _values, result)
- result = []
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 178)
- def _reduce_70(val, _values, result)
- result = [val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 179)
- def _reduce_71(val, _values, result)
- result = val[0] << val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 183)
- def _reduce_72(val, _values, result)
- result = make_node(val) { |v| Riml::DictionaryNode.new(v[0]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 190)
- def _reduce_73(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 191)
- def _reduce_74(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 196)
- def _reduce_75(val, _values, result)
- result = []
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 197)
- def _reduce_76(val, _values, result)
- result = val
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 198)
- def _reduce_77(val, _values, result)
- result = val[0] << val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 203)
- def _reduce_78(val, _values, result)
- result = [val[0], val[2]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 207)
- def _reduce_79(val, _values, result)
- result = make_node(val) { |v| Riml::DictGetDotNode.new(v[0], v[1]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 208)
- def _reduce_80(val, _values, result)
- result = make_node(val) { |v| Riml::DictGetDotNode.new(v[0], v[1]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 209)
- def _reduce_81(val, _values, result)
- result = make_node(val) { |v| Riml::DictGetDotNode.new(v[0], v[1]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 210)
- def _reduce_82(val, _values, result)
- result = make_node(val) { |v| Riml::DictGetDotNode.new(Riml::WrapInParensNode.new(v[1]), v[3]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 214)
- def _reduce_83(val, _values, result)
- result = make_node(val) { |v| Riml::ListOrDictGetNode.new(v[0], v[1]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 215)
- def _reduce_84(val, _values, result)
- result = make_node(val) { |v| Riml::ListOrDictGetNode.new(Riml::WrapInParensNode.new(v[1]), v[3]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 219)
- def _reduce_85(val, _values, result)
- result = make_node(val) { |v| Riml::ListOrDictGetNode.new(v[0], v[1]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 223)
- def _reduce_86(val, _values, result)
- result = [val[1]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 224)
- def _reduce_87(val, _values, result)
- result = [val[1]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 225)
- def _reduce_88(val, _values, result)
- result = val[0] << val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 226)
- def _reduce_89(val, _values, result)
- result = val[0] << val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 230)
- def _reduce_90(val, _values, result)
- result = make_node(val) { |v| Riml::SublistNode.new([v[0], Riml::LiteralNode.new(' : '), v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 231)
- def _reduce_91(val, _values, result)
- result = make_node(val) { |v| Riml::SublistNode.new([v[0], Riml::LiteralNode.new(' :')]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 232)
- def _reduce_92(val, _values, result)
- result = make_node(val) { |v| Riml::SublistNode.new([Riml::LiteralNode.new(': '), v[1]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 233)
- def _reduce_93(val, _values, result)
- result = make_node(val) { |_| Riml::SublistNode.new([Riml::LiteralNode.new(':')]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 237)
- def _reduce_94(val, _values, result)
- result = [val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 238)
- def _reduce_95(val, _values, result)
- result = val[0] << val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 242)
- def _reduce_96(val, _values, result)
- result = [val[1]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 243)
- def _reduce_97(val, _values, result)
- result = val[0] << val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 247)
- def _reduce_98(val, _values, result)
- result = make_node(val) { |v| Riml::CallNode.new(v[0], v[1], v[3]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 248)
- def _reduce_99(val, _values, result)
- result = make_node(val) { |v| Riml::CallNode.new(nil, v[0], v[2]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 249)
- def _reduce_100(val, _values, result)
- result = make_node(val) { |v| Riml::CallNode.new(nil, v[0], v[2]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 250)
- def _reduce_101(val, _values, result)
- result = make_node(val) { |v| Riml::CallNode.new(nil, v[0], v[1]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 251)
- def _reduce_102(val, _values, result)
- result = make_node(val) { |v| Riml::CallNode.new(nil, v[0], []) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 252)
- def _reduce_103(val, _values, result)
- result = make_node(val) { |v| Riml::ExplicitCallNode.new(nil, nil, v[2]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 256)
- def _reduce_104(val, _values, result)
- result = make_node(val) { |v| Riml::CallNode.new(v[0], v[1], v[3]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 257)
- def _reduce_105(val, _values, result)
- result = make_node(val) { |v| Riml::CallNode.new(v[0], v[1], []) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 261)
- def _reduce_106(val, _values, result)
- result = make_node(val) { |v| Riml::RimlFileCommandNode.new(nil, v[0], v[2]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 262)
- def _reduce_107(val, _values, result)
- result = make_node(val) { |v| Riml::RimlFileCommandNode.new(nil, v[0], v[1]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 266)
- def _reduce_108(val, _values, result)
- result = make_node(val) { |v| Riml::RimlClassCommandNode.new(nil, v[0], v[2]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 267)
- def _reduce_109(val, _values, result)
- result = make_node(val) { |v| Riml::RimlClassCommandNode.new(nil, v[0], v[1]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 271)
- def _reduce_110(val, _values, result)
- result = ["#{val[0]}#{val[1]}"]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 272)
- def _reduce_111(val, _values, result)
- result = val
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 273)
- def _reduce_112(val, _values, result)
- result = val[0].concat ["#{val[2]}#{val[3]}"]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 277)
- def _reduce_113(val, _values, result)
- result = make_node(val) { |v| Riml::ExplicitCallNode.new(v[1], v[2], v[4]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 278)
- def _reduce_114(val, _values, result)
- result = make_node(val) { |v| Riml::ExplicitCallNode.new(nil, v[1], v[3]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 282)
- def _reduce_115(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 283)
- def _reduce_116(val, _values, result)
- result = nil
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 288)
- def _reduce_117(val, _values, result)
- result = [ nil, val[0] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 289)
- def _reduce_118(val, _values, result)
- result = [ make_node(val) { |v| Riml::SIDNode.new(v[1]) }, val[3] ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 293)
- def _reduce_119(val, _values, result)
- result = []
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 294)
- def _reduce_120(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 298)
- def _reduce_121(val, _values, result)
- result = []
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 299)
- def _reduce_122(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 303)
- def _reduce_123(val, _values, result)
- result = val
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 304)
- def _reduce_124(val, _values, result)
- result = [ make_node(val) { |v| Riml::SplatNode.new(v[1]) } ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 305)
- def _reduce_125(val, _values, result)
- result = val[0] << val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 306)
- def _reduce_126(val, _values, result)
- result = val[0] << make_node(val) { |v| Riml::SplatNode.new(v[3]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 310)
- def _reduce_127(val, _values, result)
- result = val
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 311)
- def _reduce_128(val, _values, result)
- result = val[0] << val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 315)
- def _reduce_129(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 316)
- def _reduce_130(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 318)
- def _reduce_131(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 319)
- def _reduce_132(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 320)
- def _reduce_133(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 323)
- def _reduce_134(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 325)
- def _reduce_135(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 326)
- def _reduce_136(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 327)
- def _reduce_137(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 329)
- def _reduce_138(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 330)
- def _reduce_139(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 331)
- def _reduce_140(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 333)
- def _reduce_141(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 334)
- def _reduce_142(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 335)
- def _reduce_143(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 337)
- def _reduce_144(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 338)
- def _reduce_145(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 339)
- def _reduce_146(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 341)
- def _reduce_147(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 342)
- def _reduce_148(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 343)
- def _reduce_149(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 345)
- def _reduce_150(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 346)
- def _reduce_151(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 347)
- def _reduce_152(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 349)
- def _reduce_153(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 350)
- def _reduce_154(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 351)
- def _reduce_155(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 353)
- def _reduce_156(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 354)
- def _reduce_157(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 355)
- def _reduce_158(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 356)
- def _reduce_159(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 357)
- def _reduce_160(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 358)
- def _reduce_161(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 360)
- def _reduce_162(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 361)
- def _reduce_163(val, _values, result)
- result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 365)
- def _reduce_164(val, _values, result)
- result = make_node(val) { |v| Riml::UnaryOperatorNode.new(val[0], [val[1]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 366)
- def _reduce_165(val, _values, result)
- result = make_node(val) { |v| Riml::UnaryOperatorNode.new(val[0], [val[1]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 367)
- def _reduce_166(val, _values, result)
- result = make_node(val) { |v| Riml::UnaryOperatorNode.new(val[0], [val[1]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 372)
- def _reduce_167(val, _values, result)
- result = make_node(val) { |v| Riml::AssignNode.new(v[1][0], v[1][1], v[1][2]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 373)
- def _reduce_168(val, _values, result)
- result = make_node(val) { |v| Riml::AssignNode.new(v[0][0], v[0][1], v[0][2]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 377)
- def _reduce_169(val, _values, result)
- result = make_node(val) { |v| Riml::MultiAssignNode.new([v[0], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 378)
- def _reduce_170(val, _values, result)
- val[0].assigns << val[2]; result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 383)
- def _reduce_171(val, _values, result)
- result = [val[1], val[0], val[2]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 384)
- def _reduce_172(val, _values, result)
- result = [val[1], val[0], val[2]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 385)
- def _reduce_173(val, _values, result)
- result = [val[1], val[0], val[2]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 386)
- def _reduce_174(val, _values, result)
- result = [val[1], val[0], val[2]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 390)
- def _reduce_175(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 391)
- def _reduce_176(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 392)
- def _reduce_177(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 393)
- def _reduce_178(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 394)
- def _reduce_179(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 399)
- def _reduce_180(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 400)
- def _reduce_181(val, _values, result)
- result = make_node(val) { |v| Riml::GetSpecialVariableNode.new(v[0], v[1]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 401)
- def _reduce_182(val, _values, result)
- result = make_node(val) { |v| Riml::GetVariableByScopeAndDictNameNode.new(v[0], v[1]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 405)
- def _reduce_183(val, _values, result)
- result = make_node(val) { |v| Riml::GetVariableNode.new(v[0], v[1]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 409)
- def _reduce_184(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 410)
- def _reduce_185(val, _values, result)
- result = make_node(val) { |v| Riml::GetCurlyBraceNameNode.new(v[0], v[1]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 414)
- def _reduce_186(val, _values, result)
- result = make_node(val) { |v| Riml::UnletVariableNode.new('!', [ v[1] ]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 415)
- def _reduce_187(val, _values, result)
- result = make_node(val) { |v| Riml::UnletVariableNode.new('!', [ v[1] ]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 416)
- def _reduce_188(val, _values, result)
- result = val[0] << val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 420)
- def _reduce_189(val, _values, result)
- result = make_node(val) { |v| Riml::CurlyBraceVariable.new([ v[0] ]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 421)
- def _reduce_190(val, _values, result)
- result = make_node(val) { |v| Riml::CurlyBraceVariable.new([ Riml::CurlyBracePart.new(v[0]), v[1] ]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 422)
- def _reduce_191(val, _values, result)
- result = val[0] << make_node(val) { |v| Riml::CurlyBracePart.new(v[1]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 423)
- def _reduce_192(val, _values, result)
- result = val[0] << val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 427)
- def _reduce_193(val, _values, result)
- result = make_node(val) { |v| Riml::CurlyBracePart.new(v[1]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 428)
- def _reduce_194(val, _values, result)
- result = make_node(val) { |v| Riml::CurlyBracePart.new([v[1], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 429)
- def _reduce_195(val, _values, result)
- result = make_node(val) { |v| Riml::CurlyBracePart.new([v[1], v[2]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 435)
- def _reduce_196(val, _values, result)
- result = make_node(val) { |v| Riml.const_get(val[0]).new('!', v[1][0], v[1][1], v[2], [], v[3], v[4]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 436)
- def _reduce_197(val, _values, result)
- result = make_node(val) { |v| Riml.const_get(val[0]).new('!', v[1][0], v[1][1], v[2], v[4], v[6], v[7]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 437)
- def _reduce_198(val, _values, result)
- result = make_node(val) { |v| Riml.const_get(val[0]).new('!', v[1][0], v[1][1], v[2], [v[4]], v[6], v[7]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 438)
- def _reduce_199(val, _values, result)
- result = make_node(val) { |v| Riml.const_get(val[0]).new('!', v[1][0], v[1][1], v[2], v[4] << v[6], v[8], v[9]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 442)
- def _reduce_200(val, _values, result)
- result = "DefNode"
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 443)
- def _reduce_201(val, _values, result)
- result = "DefNode"
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 444)
- def _reduce_202(val, _values, result)
- result = "DefMethodNode"
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 449)
- def _reduce_203(val, _values, result)
- result = make_node(val) { |v| Riml::GetCurlyBraceNameNode.new('', v[0]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 450)
- def _reduce_204(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 455)
- def _reduce_205(val, _values, result)
- result = [val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 456)
- def _reduce_206(val, _values, result)
- result = val[0] << val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 457)
- def _reduce_207(val, _values, result)
- result = nil
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 461)
- def _reduce_208(val, _values, result)
- result = []
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 462)
- def _reduce_209(val, _values, result)
- result = val
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 463)
- def _reduce_210(val, _values, result)
- result = val
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 464)
- def _reduce_211(val, _values, result)
- result = val[0] << val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 465)
- def _reduce_212(val, _values, result)
- result = val[0] << val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 469)
- def _reduce_213(val, _values, result)
- result = make_node(val) { |v| Riml::DefaultParamNode.new(v[0], v[2]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 473)
- def _reduce_214(val, _values, result)
- result = make_node(val) { |v| Riml::ReturnNode.new(v[1]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 474)
- def _reduce_215(val, _values, result)
- result = make_node(val) { |v| Riml::IfNode.new(v[3], Nodes.new([ReturnNode.new(v[1])])) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 475)
- def _reduce_216(val, _values, result)
- result = make_node(val) { |v| Riml::UnlessNode.new(v[3], Nodes.new([ReturnNode.new(v[1])])) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 479)
- def _reduce_217(val, _values, result)
- result = nil
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 480)
- def _reduce_218(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 484)
- def _reduce_219(val, _values, result)
- result = make_node(val) { |_| Riml::FinishNode.new }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 489)
- def _reduce_220(val, _values, result)
- result = make_node(val) { |v| Riml::IfNode.new(v[1], v[2]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 490)
- def _reduce_221(val, _values, result)
- result = make_node(val) { |v| Riml::IfNode.new(v[1], Riml::Nodes.new([v[3]])) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 491)
- def _reduce_222(val, _values, result)
- result = make_node(val) { |v| Riml::IfNode.new(v[2], Riml::Nodes.new([v[0]])) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 495)
- def _reduce_223(val, _values, result)
- result = make_node(val) { |v| Riml::UnlessNode.new(v[1], v[2]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 496)
- def _reduce_224(val, _values, result)
- result = make_node(val) { |v| Riml::UnlessNode.new(v[1], Riml::Nodes.new([v[3]])) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 497)
- def _reduce_225(val, _values, result)
- result = make_node(val) { |v| Riml::UnlessNode.new(v[2], Riml::Nodes.new([v[0]])) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 501)
- def _reduce_226(val, _values, result)
- result = make_node(val) { |v| Riml::TernaryOperatorNode.new([v[0], v[2], v[4]]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 505)
- def _reduce_227(val, _values, result)
- result = make_node(val) { |v| Riml::WhileNode.new(v[1], v[2]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 509)
- def _reduce_228(val, _values, result)
- result = make_node(val) { |_| Riml::BreakNode.new }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 510)
- def _reduce_229(val, _values, result)
- result = make_node(val) { |_| Riml::ContinueNode.new }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 514)
- def _reduce_230(val, _values, result)
- result = make_node(val) { |v| Riml::UntilNode.new(v[1], v[2]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 518)
- def _reduce_231(val, _values, result)
- result = make_node(val) { |v| Riml::ForNode.new(v[1], v[3], v[4]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 519)
- def _reduce_232(val, _values, result)
- result = make_node(val) { |v| Riml::ForNode.new(v[1], v[3], v[4]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 520)
- def _reduce_233(val, _values, result)
- result = make_node(val) { |v| Riml::ForNode.new(v[1], v[3], v[4]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 524)
- def _reduce_234(val, _values, result)
- result = make_node(val) { |v| Riml::TryNode.new(v[1], nil, nil) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 525)
- def _reduce_235(val, _values, result)
- result = make_node(val) { |v| Riml::TryNode.new(v[1], v[2], nil) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 526)
- def _reduce_236(val, _values, result)
- result = make_node(val) { |v| Riml::TryNode.new(v[1], v[2], v[4]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 530)
- def _reduce_237(val, _values, result)
- result = nil
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 531)
- def _reduce_238(val, _values, result)
- result = [ make_node(val) { |v| Riml::CatchNode.new(nil, v[1]) } ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 532)
- def _reduce_239(val, _values, result)
- result = [ make_node(val) { |v| Riml::CatchNode.new(v[1], v[2]) } ]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 533)
- def _reduce_240(val, _values, result)
- result = val[0] << make_node(val) { |v| Riml::CatchNode.new(nil, v[2]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 534)
- def _reduce_241(val, _values, result)
- result = val[0] << make_node(val) { |v| Riml::CatchNode.new(v[2], v[3]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 538)
- def _reduce_242(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 539)
- def _reduce_243(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 546)
- def _reduce_244(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 547)
- def _reduce_245(val, _values, result)
- result = make_node(val) { |_| Riml::Nodes.new([]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 551)
- def _reduce_246(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 552)
- def _reduce_247(val, _values, result)
- result = val[1] << val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 553)
- def _reduce_248(val, _values, result)
- result = val[1] << val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 554)
- def _reduce_249(val, _values, result)
- result = val[1] << val[2] << val[3]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 558)
- def _reduce_250(val, _values, result)
- result = make_node(val) { |v| Riml::ElseNode.new(v[2]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 562)
- def _reduce_251(val, _values, result)
- result = make_node(val) { |v| Riml::Nodes.new([Riml::ElseifNode.new(v[1], v[3])]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 563)
- def _reduce_252(val, _values, result)
- result = val[0] << make_node(val) { |v| Riml::ElseifNode.new(v[2], v[4]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 567)
- def _reduce_253(val, _values, result)
- result = make_node(val) { |v| Riml::ClassDefinitionNode.new(v[1], v[2], nil, v[3]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 568)
- def _reduce_254(val, _values, result)
- result = make_node(val) { |v| Riml::ClassDefinitionNode.new(v[1], v[2], (v[4] || ClassDefinitionNode::DEFAULT_SCOPE_MODIFIER) + v[5], v[6]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 572)
- def _reduce_255(val, _values, result)
- result = make_node(val) { |v| Riml::ObjectInstantiationNode.new(v[1]) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 576)
- def _reduce_256(val, _values, result)
- result = make_node(val) { |v| Riml::SuperNode.new(v[2], true) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 577)
- def _reduce_257(val, _values, result)
- result = make_node(val) { |_| Riml::SuperNode.new([], false) }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'riml.y', 581)
- def _reduce_258(val, _values, result)
- result = make_node(val) { |v| Riml::ExLiteralNode.new(v[0]) }
- result
- end
-.,.,
-
-def _reduce_none(val, _values, result)
- val[0]
-end
-
- end # class Parser
-end # module Riml
diff --git a/test/racc/regress/ruby18 b/test/racc/regress/ruby18
deleted file mode 100644
index 669ab2449d..0000000000
--- a/test/racc/regress/ruby18
+++ /dev/null
@@ -1,9947 +0,0 @@
-#
-# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.5.2
-# from Racc grammar file "".
-#
-
-require 'racc/parser.rb'
-
-
-require 'parser'
-
-module Parser
- class Ruby18 < Racc::Parser
-
-module_eval(<<'...end ruby18.y/module_eval...', 'ruby18.y', 1936)
-
- def version
- 18
- end
-
- def default_encoding
- Encoding::BINARY if defined? Encoding
- end
-...end ruby18.y/module_eval...
-##### State transition tables begin ###
-
-racc_action_table = [
- -480, 195, 196, 195, 196, 489, 814, -480, -480, -480,
- 511, 578, 578, -480, -480, -80, -480, -429, 579, 579,
- 489, 72, 531, -87, 558, -480, 99, 489, -86, 73,
- 95, 98, 395, 195, 196, -480, -480, -82, -480, -480,
- -480, -480, -480, 489, 489, 558, 495, -84, 496, -83,
- -81, 463, 659, 658, 662, 661, 186, 99, 557, 558,
- 291, 291, 98, -80, -480, -480, -480, -480, -480, -480,
- -480, -480, -480, -480, -480, -480, -480, -480, -87, 557,
- -480, -480, -480, 259, 546, 530, 722, -74, -480, -61,
- 99, -480, 291, 557, 621, 98, -480, -86, -480, -85,
- -480, -480, -480, -480, -480, -480, -480, -277, -480, -480,
- -480, 187, -476, 510, -277, -277, -277, 99, -72, 488,
- -277, -277, 98, -277, -480, -480, -79, -75, -69, -480,
- -83, -78, 99, 99, 488, 621, -76, 98, 98, 99,
- -74, 488, -277, -277, 98, -277, -277, -277, -277, -277,
- -76, -74, -75, -73, 431, 99, 99, 488, 488, 621,
- 98, 98, -477, 259, 502, 195, 196, 258, 503, -74,
- 690, -277, -277, -277, -277, -277, -277, -277, -277, -277,
- -277, -277, -277, -277, -277, 558, 259, -277, -277, -277,
- -74, 549, 99, -74, 620, -277, 713, 98, -277, 691,
- -76, 188, -77, -277, 521, -277, 521, -277, -277, -277,
- -277, -277, -277, -277, -272, -277, -82, -277, -76, 557,
- 189, -272, -272, -272, 99, 254, -272, -272, -272, 98,
- -272, -277, -277, 99, -77, 620, -277, -85, 98, -76,
- -272, -272, -76, 477, 190, -84, 476, 258, 254, -272,
- -272, 768, -272, -272, -272, -272, -272, 99, 194, 620,
- 477, 234, 98, 479, 521, 523, 522, 523, 522, 519,
- 258, 477, 358, 282, 482, 673, 360, 359, -272, -272,
- -272, -272, -272, -272, -272, -272, -272, -272, -272, -272,
- -272, -272, 521, 192, -272, -272, -272, -81, 283, -272,
- 193, -79, -272, 521, -87, -272, -272, 291, -272, 191,
- -272, 349, -272, 215, -272, -272, -272, -272, -272, -272,
- -272, -226, -272, 361, -272, 523, 522, 524, -226, -226,
- -226, 814, 394, -226, -226, -226, 521, -226, -272, -272,
- -272, -272, 396, -272, -473, 212, 606, -226, 496, 214,
- 213, 210, 211, 523, 522, 526, -226, -226, 397, -226,
- -226, -226, -226, -226, 523, 522, 527, 251, 477, 426,
- -474, 479, -480, -480, 252, -319, -226, 659, 658, 662,
- 661, 428, -319, -226, -226, -226, -417, -476, -226, -226,
- -226, -319, -226, -417, -417, 431, -226, 523, 522, 532,
- -429, -417, -226, -226, 284, 285, -473, 436, 254, -226,
- -417, -226, -226, -60, -226, -226, -226, -226, -226, 463,
- -421, 451, -480, -480, 541, -477, 542, -421, -473, -480,
- -480, -226, -474, 680, -476, -476, -421, -476, -480, -480,
- 452, 215, 453, -72, -476, -226, -80, -226, 215, -476,
- -226, -226, 391, -476, -474, 99, -480, -480, -226, 392,
- 98, 99, -78, 254, -226, -86, 98, -74, 393, -76,
- -82, -476, -84, 212, -428, -477, -73, 214, 213, -81,
- 212, -428, -477, 459, 214, 213, -226, -477, 461, 462,
- -428, -477, 195, 196, -421, -476, -476, -476, 263, -476,
- -226, -421, -226, -476, -476, -226, 291, -480, -476, -477,
- -476, -476, -476, -476, -476, -476, -476, 195, 196, 734,
- 606, -476, -476, -476, -476, -476, -476, -476, -427, 215,
- 254, -426, -425, 662, 661, -427, 464, -476, -426, -425,
- -476, -476, -476, -476, -476, -476, -476, -476, -476, -476,
- 465, -476, -476, -422, -476, -476, -476, -480, 734, 606,
- -422, 212, 215, -423, -480, 214, 213, 210, 211, -476,
- -423, 457, 471, -480, -424, 472, -476, 692, 458, -476,
- -476, -424, -476, -476, 291, 481, -476, 456, -476, 484,
- -476, -480, -476, 351, 516, -271, 466, 498, 500, 501,
- -476, 517, -271, 467, 499, -476, -476, -476, -476, -476,
- -476, -271, 393, 497, -476, -476, -477, -477, -477, 535,
- -477, 536, 538, -83, -477, -477, -259, 540, 254, -477,
- 215, -477, -477, -477, -477, -477, -477, -477, 215, 215,
- 215, 568, -477, -477, -477, -477, -477, -477, -477, 667,
- 668, 575, 669, 93, 94, 291, 580, 234, -477, 590,
- 591, -477, -477, -477, -477, -477, -477, -477, -477, -477,
- -477, -69, -477, -477, 592, -477, -477, -477, 215, 219,
- 224, 225, 226, 221, 223, 231, 232, 227, 228, 507,
- -497, -497, -278, 469, 229, 230, 505, -477, 549, -278,
- -477, -477, 606, -477, -477, 506, 291, -477, -278, -477,
- 212, -477, 218, -477, 214, 213, 210, 211, 222, 220,
- 216, -477, 217, 616, 496, 624, -477, -477, -477, -477,
- -477, -477, 672, -278, 675, -477, -477, 62, 63, 64,
- -278, 51, 436, 436, -85, 56, 57, 693, 704, -278,
- 60, 431, 58, 59, 61, 23, 24, 65, 66, 431,
- 243, 707, 708, 22, 28, 27, 88, 87, 89, 90,
- 715, 717, 17, 721, 254, 254, 215, 537, 215, 41,
- 724, -259, 92, 91, 82, 50, 84, 83, 86, 85,
- 93, 94, 728, 80, 81, 730, 38, 39, 37, 215,
- 219, 224, 225, 226, 221, 223, 231, 232, 227, 228,
- -277, 208, 209, -279, 606, 229, 230, -277, 200, 738,
- -279, 204, -477, 739, 52, 53, -277, 740, 54, -279,
- 743, 212, 745, 218, 40, 214, 213, 210, 211, 222,
- 220, 216, 18, 217, 749, 753, 755, 79, 72, 74,
- 75, 76, 77, 758, 759, 760, 73, 78, 761, 99,
- 233, 763, -215, -277, 98, 62, 63, 64, 7, 51,
- -277, -260, 769, 56, 57, -477, 777, 778, 60, -277,
- 58, 59, 61, 23, 24, 65, 66, 568, 568, 254,
- 254, 22, 28, 27, 88, 87, 89, 90, 234, 568,
- 17, 101, 102, 103, 104, 105, 6, 41, 8, 9,
- 92, 91, 82, 50, 84, 83, 86, 85, 93, 94,
- 790, 80, 81, 791, 38, 39, 37, 215, 219, 224,
- 225, 226, 221, 223, 231, 232, 227, 228, -428, -497,
- -497, 823, 792, 229, 230, -428, 36, 797, 824, 30,
- 799, 805, 52, 53, -428, 807, 54, 822, 32, 212,
- 291, 218, 40, 214, 213, 210, 211, 222, 220, 216,
- 18, 217, 818, 825, 826, 79, 72, 74, 75, 76,
- 77, 827, -271, 829, 73, 78, 62, 63, 64, -271,
- 51, 830, 351, -278, 56, 57, 832, 835, -271, 60,
- -278, 58, 59, 61, 246, 247, 65, 66, 839, -278,
- 840, 846, 245, 275, 279, 88, 87, 89, 90, 101,
- 102, 103, 104, 105, 847, 848, 758, 758, 276, 759,
- 861, 92, 91, 82, 50, 84, 83, 86, 85, 93,
- 94, 568, 80, 81, 215, 568, 655, 280, 653, 652,
- 651, 654, -277, 471, 874, -279, 875, 876, 880, -277,
- 229, 230, -279, 883, -477, 758, 885, 772, -277, 886,
- 204, -279, 568, 52, 53, 568, 212, 54, 218, 568,
- 214, 213, 210, 211, 645, nil, 216, nil, 217, nil,
- nil, nil, 659, 658, 662, 661, 79, 72, 74, 75,
- 76, 77, nil, nil, nil, 73, 78, nil, 62, 63,
- 64, 775, 51, nil, nil, nil, 56, 57, nil, nil,
- nil, 60, nil, 58, 59, 61, 246, 247, 65, 66,
- nil, nil, nil, nil, 245, 275, 279, 88, 87, 89,
- 90, 101, 102, 103, 104, 105, nil, nil, 537, nil,
- 276, nil, nil, 92, 91, 82, 50, 84, 83, 86,
- 85, 93, 94, nil, 80, 81, nil, nil, nil, 280,
- 215, 219, 224, 225, 226, 221, 223, 231, 232, 227,
- 228, nil, 208, 209, nil, nil, 229, 230, nil, 772,
- nil, nil, 204, nil, nil, 52, 53, nil, nil, 54,
- nil, nil, 212, nil, 218, nil, 214, 213, 210, 211,
- 222, 220, 216, nil, 217, nil, nil, nil, 79, 72,
- 74, 75, 76, 77, nil, nil, nil, 73, 78, nil,
- nil, 233, nil, 855, 5, 62, 63, 64, 7, 51,
- nil, nil, nil, 56, 57, nil, nil, nil, 60, nil,
- 58, 59, 61, 23, 24, 65, 66, nil, nil, nil,
- nil, 22, 28, 27, 88, 87, 89, 90, nil, nil,
- 17, nil, nil, nil, nil, nil, 6, 41, 8, 9,
- 92, 91, 82, 50, 84, 83, 86, 85, 93, 94,
- nil, 80, 81, nil, 38, 39, 37, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 36, nil, nil, 30,
- nil, nil, 52, 53, nil, nil, 54, nil, 32, nil,
- nil, nil, 40, 655, nil, 653, 652, 651, 654, nil,
- 18, nil, nil, nil, nil, 79, 72, 74, 75, 76,
- 77, nil, nil, nil, 73, 78, 5, 62, 63, 64,
- 7, 51, nil, nil, nil, 56, 57, nil, nil, nil,
- 60, nil, 58, 59, 61, 23, 24, 65, 66, 659,
- 658, 662, 661, 22, 28, 27, 88, 87, 89, 90,
- nil, nil, 17, nil, nil, nil, nil, nil, 6, 41,
- 8, 9, 92, 91, 82, 50, 84, 83, 86, 85,
- 93, 94, nil, 80, 81, nil, 38, 39, 37, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 36, nil,
- nil, 265, nil, nil, 52, 53, nil, nil, 54, nil,
- 32, nil, nil, nil, 40, 655, nil, 653, 652, 651,
- 654, nil, 18, nil, nil, nil, nil, 79, 72, 74,
- 75, 76, 77, nil, nil, nil, 73, 78, 5, 62,
- 63, 64, 7, 51, nil, nil, nil, 56, 57, nil,
- nil, nil, 60, nil, 58, 59, 61, 23, 24, 65,
- 66, 659, 658, 662, 661, 22, 28, 27, 88, 87,
- 89, 90, nil, nil, 17, nil, nil, nil, nil, nil,
- 6, 41, 8, 9, 92, 91, 82, 50, 84, 83,
- 86, 85, 93, 94, nil, 80, 81, nil, 38, 39,
- 37, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 36, nil, nil, 30, nil, nil, 52, 53, nil, nil,
- 54, nil, 32, nil, nil, nil, 40, 655, nil, 653,
- 652, 651, 654, nil, 18, nil, nil, nil, nil, 79,
- 72, 74, 75, 76, 77, nil, nil, nil, 73, 78,
- 5, 62, 63, 64, 7, 51, nil, nil, nil, 56,
- 57, nil, nil, nil, 60, 645, 58, 59, 61, 23,
- 24, 65, 66, 659, 658, 662, 661, 22, 28, 27,
- 88, 87, 89, 90, nil, nil, 17, nil, nil, nil,
- nil, nil, 6, 41, 8, 9, 92, 91, 82, 50,
- 84, 83, 86, 85, 93, 94, nil, 80, 81, nil,
- 38, 39, 37, 215, -497, -497, -497, -497, 221, 223,
- nil, nil, -497, -497, nil, nil, nil, nil, nil, 229,
- 230, nil, 36, nil, nil, 30, nil, nil, 52, 53,
- nil, nil, 54, nil, 32, 212, nil, 218, 40, 214,
- 213, 210, 211, 222, 220, 216, 18, 217, nil, nil,
- nil, 79, 72, 74, 75, 76, 77, nil, nil, nil,
- 73, 78, 5, 62, 63, 64, 7, 51, nil, nil,
- nil, 56, 57, nil, nil, nil, 60, nil, 58, 59,
- 61, 23, 24, 65, 66, nil, nil, nil, nil, 22,
- 28, 27, 88, 87, 89, 90, nil, nil, 17, nil,
- nil, nil, nil, nil, 6, 41, 8, 9, 92, 91,
- 82, 50, 84, 83, 86, 85, 93, 94, nil, 80,
- 81, nil, 38, 39, 37, 215, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 229, 230, nil, 36, nil, nil, 30, nil, nil,
- 52, 53, nil, nil, 54, nil, 32, 212, nil, 218,
- 40, 214, 213, 210, 211, nil, nil, 216, 18, 217,
- nil, nil, nil, 79, 72, 74, 75, 76, 77, nil,
- nil, nil, 73, 78, 5, 62, 63, 64, 7, 51,
- nil, nil, nil, 56, 57, nil, nil, nil, 60, nil,
- 58, 59, 61, 23, 24, 65, 66, nil, nil, nil,
- nil, 22, 28, 27, 88, 87, 89, 90, nil, nil,
- 17, nil, nil, nil, nil, nil, 6, 41, 8, 9,
- 92, 91, 82, 50, 84, 83, 86, 85, 93, 94,
- nil, 80, 81, nil, 38, 39, 37, 215, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 229, 230, nil, 36, nil, nil, 30,
- nil, nil, 52, 53, nil, nil, 54, nil, 32, 212,
- nil, 218, 40, 214, 213, 210, 211, nil, nil, 216,
- 18, 217, nil, nil, nil, 79, 72, 74, 75, 76,
- 77, nil, nil, nil, 73, 78, 5, 62, 63, 64,
- 7, 51, nil, nil, nil, 56, 57, nil, nil, nil,
- 60, nil, 58, 59, 61, 23, 24, 65, 66, nil,
- nil, nil, nil, 22, 28, 27, 88, 87, 89, 90,
- nil, nil, 17, nil, nil, nil, nil, nil, 6, 41,
- 8, 9, 92, 91, 82, 50, 84, 83, 86, 85,
- 93, 94, nil, 80, 81, nil, 38, 39, 37, 215,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 229, 230, nil, 36, nil,
- nil, 265, nil, nil, 52, 53, nil, nil, 54, nil,
- 32, 212, nil, 218, 40, 214, 213, 210, 211, nil,
- nil, 216, 18, 217, nil, nil, nil, 79, 72, 74,
- 75, 76, 77, nil, nil, nil, 73, 78, 5, 62,
- 63, 64, 7, 51, nil, nil, nil, 56, 57, nil,
- nil, nil, 60, nil, 58, 59, 61, 23, 24, 65,
- 66, nil, nil, nil, nil, 22, 28, 27, 88, 87,
- 89, 90, nil, nil, 17, nil, nil, nil, nil, nil,
- 6, 41, 8, 9, 92, 91, 82, 50, 84, 83,
- 86, 85, 93, 94, nil, 80, 81, nil, 38, 39,
- 37, 215, -497, -497, -497, -497, 221, 223, nil, nil,
- -497, -497, nil, nil, nil, nil, nil, 229, 230, nil,
- 36, nil, nil, 265, nil, nil, 52, 53, nil, nil,
- 54, nil, 32, 212, nil, 218, 40, 214, 213, 210,
- 211, 222, 220, 216, 18, 217, nil, nil, nil, 79,
- 72, 74, 75, 76, 77, nil, nil, nil, 73, 78,
- 5, 62, 63, 64, 7, 51, nil, nil, nil, 56,
- 57, nil, nil, nil, 60, nil, 58, 59, 61, 23,
- 24, 65, 66, nil, nil, nil, nil, 22, 28, 27,
- 88, 87, 89, 90, nil, nil, 17, nil, nil, nil,
- nil, nil, 6, 41, 8, 9, 92, 91, 82, 50,
- 84, 83, 86, 85, 93, 94, nil, 80, 81, nil,
- 38, 39, 37, 215, -497, -497, -497, -497, 221, 223,
- nil, nil, -497, -497, nil, nil, nil, nil, nil, 229,
- 230, nil, 36, nil, nil, 30, nil, nil, 52, 53,
- nil, nil, 54, nil, 32, 212, nil, 218, 40, 214,
- 213, 210, 211, 222, 220, 216, 18, 217, nil, nil,
- nil, 79, 72, 74, 75, 76, 77, nil, nil, nil,
- 73, 78, 5, 62, 63, 64, 7, 51, nil, nil,
- nil, 56, 57, nil, nil, nil, 60, nil, 58, 59,
- 61, 23, 24, 65, 66, nil, nil, nil, nil, 22,
- 28, 27, 88, 87, 89, 90, nil, nil, 17, nil,
- nil, nil, nil, nil, 6, 41, 8, 9, 92, 91,
- 82, 50, 84, 83, 86, 85, 93, 94, nil, 80,
- 81, nil, 38, 39, 37, 215, -497, -497, -497, -497,
- 221, 223, nil, nil, -497, -497, nil, nil, nil, nil,
- nil, 229, 230, nil, 36, nil, nil, 30, nil, nil,
- 52, 53, nil, nil, 54, nil, 32, 212, nil, 218,
- 40, 214, 213, 210, 211, 222, 220, 216, 18, 217,
- nil, nil, nil, 79, 72, 74, 75, 76, 77, nil,
- nil, nil, 73, 78, 5, 62, 63, 64, 7, 51,
- nil, nil, nil, 56, 57, nil, nil, nil, 60, nil,
- 58, 59, 61, 23, 24, 65, 66, nil, nil, nil,
- nil, 22, 28, 27, 88, 87, 89, 90, nil, nil,
- 17, nil, nil, nil, nil, nil, 6, 41, 8, 9,
- 92, 91, 82, 50, 84, 83, 86, 85, 93, 94,
- nil, 80, 81, nil, 38, 39, 37, 215, -497, -497,
- -497, -497, 221, 223, nil, nil, -497, -497, nil, nil,
- nil, nil, nil, 229, 230, nil, 36, nil, nil, 30,
- nil, nil, 52, 53, nil, nil, 54, nil, 32, 212,
- nil, 218, 40, 214, 213, 210, 211, 222, 220, 216,
- 18, 217, nil, nil, nil, 79, 72, 74, 75, 76,
- 77, nil, nil, nil, 73, 78, 5, 62, 63, 64,
- 7, 51, nil, nil, nil, 56, 57, nil, nil, nil,
- 60, nil, 58, 59, 61, 23, 24, 65, 66, nil,
- nil, nil, nil, 22, 28, 27, 88, 87, 89, 90,
- nil, nil, 17, nil, nil, nil, nil, nil, 6, 41,
- 8, 9, 92, 91, 82, 50, 84, 83, 86, 85,
- 93, 94, nil, 80, 81, nil, 38, 39, 37, 215,
- -497, -497, -497, -497, 221, 223, nil, nil, -497, -497,
- nil, nil, nil, nil, nil, 229, 230, nil, 36, nil,
- nil, 30, nil, nil, 52, 53, nil, nil, 54, nil,
- 32, 212, nil, 218, 40, 214, 213, 210, 211, 222,
- 220, 216, 18, 217, nil, nil, nil, 79, 72, 74,
- 75, 76, 77, nil, nil, nil, 73, 78, 5, 62,
- 63, 64, 7, 51, nil, nil, nil, 56, 57, nil,
- nil, nil, 60, nil, 58, 59, 61, 23, 24, 65,
- 66, nil, nil, nil, nil, 22, 28, 27, 88, 87,
- 89, 90, nil, nil, 17, nil, nil, nil, nil, nil,
- 6, 41, 8, 9, 92, 91, 82, 50, 84, 83,
- 86, 85, 93, 94, nil, 80, 81, nil, 38, 39,
- 37, 215, 219, 224, 225, 226, 221, 223, nil, nil,
- 227, 228, nil, nil, nil, nil, nil, 229, 230, nil,
- 36, nil, nil, 30, nil, nil, 52, 53, nil, nil,
- 54, nil, 32, 212, nil, 218, 40, 214, 213, 210,
- 211, 222, 220, 216, 18, 217, nil, nil, nil, 79,
- 72, 74, 75, 76, 77, nil, nil, nil, 73, 78,
- 5, 62, 63, 64, 7, 51, nil, nil, nil, 56,
- 57, nil, nil, nil, 60, nil, 58, 59, 61, 23,
- 24, 65, 66, nil, nil, nil, nil, 22, 28, 27,
- 88, 87, 89, 90, nil, nil, 17, nil, nil, nil,
- nil, nil, 6, 41, 8, 9, 92, 91, 82, 50,
- 84, 83, 86, 85, 93, 94, nil, 80, 81, nil,
- 38, 39, 37, 215, 219, 224, 225, 226, 221, 223,
- 231, nil, 227, 228, nil, nil, nil, nil, nil, 229,
- 230, nil, 36, nil, nil, 30, nil, nil, 52, 53,
- nil, nil, 54, nil, 32, 212, nil, 218, 40, 214,
- 213, 210, 211, 222, 220, 216, 18, 217, nil, nil,
- nil, 79, 72, 74, 75, 76, 77, nil, nil, nil,
- 73, 78, 5, 62, 63, 64, 7, 51, nil, nil,
- nil, 56, 57, nil, nil, nil, 60, nil, 58, 59,
- 61, 23, 24, 65, 66, nil, nil, nil, nil, 22,
- 28, 27, 88, 87, 89, 90, nil, nil, 17, nil,
- nil, nil, nil, nil, 6, 41, 8, 9, 92, 91,
- 82, 50, 84, 83, 86, 85, 93, 94, nil, 80,
- 81, nil, 38, 39, 37, 215, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 229, 230, nil, 36, nil, nil, 30, nil, nil,
- 52, 53, nil, nil, 54, nil, 32, 212, nil, 218,
- 40, 214, 213, 210, 211, nil, nil, nil, 18, nil,
- nil, nil, nil, 79, 72, 74, 75, 76, 77, nil,
- nil, nil, 73, 78, 5, 62, 63, 64, 7, 51,
- nil, nil, nil, 56, 57, nil, nil, nil, 60, nil,
- 58, 59, 61, 23, 24, 65, 66, nil, nil, nil,
- nil, 22, 28, 27, 88, 87, 89, 90, nil, nil,
- 17, nil, nil, nil, nil, nil, 6, 41, 8, 9,
- 92, 91, 82, 50, 84, 83, 86, 85, 93, 94,
- nil, 80, 81, nil, 38, 39, 37, 215, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 229, 230, nil, 36, nil, nil, 30,
- nil, nil, 52, 53, nil, nil, 54, nil, 32, 212,
- nil, 218, 40, 214, 213, 210, 211, nil, nil, nil,
- 18, nil, nil, nil, nil, 79, 72, 74, 75, 76,
- 77, nil, nil, nil, 73, 78, 5, 62, 63, 64,
- 7, 51, nil, nil, nil, 56, 57, nil, nil, nil,
- 60, nil, 58, 59, 61, 23, 24, 65, 66, nil,
- nil, nil, nil, 22, 28, 27, 88, 87, 89, 90,
- nil, nil, 17, nil, nil, nil, nil, nil, 6, 41,
- 8, 9, 92, 91, 82, 50, 84, 83, 86, 85,
- 93, 94, nil, 80, 81, nil, 38, 39, 37, 215,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 229, 230, nil, 36, nil,
- nil, 30, nil, nil, 52, 53, nil, nil, 54, nil,
- 32, 212, nil, nil, 40, 214, 213, 210, 211, nil,
- nil, nil, 18, nil, nil, nil, nil, 79, 72, 74,
- 75, 76, 77, nil, nil, nil, 73, 78, 5, 62,
- 63, 64, 7, 51, nil, nil, nil, 56, 57, nil,
- nil, nil, 60, nil, 58, 59, 61, 23, 24, 65,
- 66, nil, nil, nil, nil, 22, 28, 27, 88, 87,
- 89, 90, nil, nil, 17, nil, nil, nil, nil, nil,
- 6, 41, 8, 9, 92, 91, 82, 50, 84, 83,
- 86, 85, 93, 94, nil, 80, 81, nil, 38, 39,
- 37, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 36, nil, nil, 30, nil, nil, 52, 53, nil, nil,
- 54, nil, 32, nil, nil, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 18, nil, nil, nil, nil, 79,
- 72, 74, 75, 76, 77, nil, nil, nil, 73, 78,
- 5, 62, 63, 64, 7, 51, nil, nil, nil, 56,
- 57, nil, nil, nil, 60, nil, 58, 59, 61, 23,
- 24, 65, 66, nil, nil, nil, nil, 22, 28, 27,
- 88, 87, 89, 90, nil, nil, 17, nil, nil, nil,
- nil, nil, 6, 41, 8, 9, 92, 91, 82, 50,
- 84, 83, 86, 85, 93, 94, nil, 80, 81, nil,
- 38, 39, 37, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 36, nil, nil, 30, nil, nil, 52, 53,
- nil, nil, 54, nil, 32, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 18, nil, nil, nil,
- nil, 79, 72, 74, 75, 76, 77, nil, nil, nil,
- 73, 78, 5, 62, 63, 64, 7, 51, nil, nil,
- nil, 56, 57, nil, nil, nil, 60, nil, 58, 59,
- 61, 23, 24, 65, 66, nil, nil, nil, nil, 22,
- 28, 27, 88, 87, 89, 90, nil, nil, 17, nil,
- nil, nil, nil, nil, 6, 41, 8, 9, 92, 91,
- 82, 50, 84, 83, 86, 85, 93, 94, nil, 80,
- 81, nil, 38, 39, 37, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 36, nil, nil, 30, nil, nil,
- 52, 53, nil, nil, 54, nil, 32, nil, nil, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 18, nil,
- nil, nil, nil, 79, 72, 74, 75, 76, 77, nil,
- nil, nil, 73, 78, 5, 62, 63, 64, 7, 51,
- nil, nil, nil, 56, 57, nil, nil, nil, 60, nil,
- 58, 59, 61, 23, 24, 65, 66, nil, nil, nil,
- nil, 22, 28, 27, 88, 87, 89, 90, nil, nil,
- 17, nil, nil, nil, nil, nil, 6, 41, 8, 9,
- 92, 91, 82, 50, 84, 83, 86, 85, 93, 94,
- nil, 80, 81, nil, 38, 39, 37, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 36, nil, nil, 30,
- nil, nil, 52, 53, nil, nil, 54, nil, 32, nil,
- nil, nil, 40, nil, nil, nil, nil, nil, nil, nil,
- 18, nil, nil, nil, nil, 79, 72, 74, 75, 76,
- 77, nil, nil, nil, 73, 78, 5, 62, 63, 64,
- 7, 51, nil, nil, nil, 56, 57, nil, nil, nil,
- 60, nil, 58, 59, 61, 23, 24, 65, 66, nil,
- nil, nil, nil, 22, 28, 27, 88, 87, 89, 90,
- nil, nil, 17, nil, nil, nil, nil, nil, 6, 41,
- 8, 9, 92, 91, 82, 50, 84, 83, 86, 85,
- 93, 94, nil, 80, 81, nil, 38, 39, 37, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 36, nil,
- nil, 30, nil, nil, 52, 53, nil, nil, 54, nil,
- 32, nil, nil, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 18, nil, nil, nil, nil, 79, 72, 74,
- 75, 76, 77, nil, nil, nil, 73, 78, 5, 62,
- 63, 64, 7, 51, nil, nil, nil, 56, 57, nil,
- nil, nil, 60, nil, 58, 59, 61, 23, 24, 65,
- 66, nil, nil, nil, nil, 22, 28, 27, 88, 87,
- 89, 90, nil, nil, 17, nil, nil, nil, nil, nil,
- 6, 41, 8, 9, 92, 91, 82, 50, 84, 83,
- 86, 85, 93, 94, nil, 80, 81, nil, 38, 39,
- 37, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 36, nil, nil, 30, nil, nil, 52, 53, nil, nil,
- 54, nil, 32, nil, nil, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 18, nil, nil, nil, nil, 79,
- 72, 74, 75, 76, 77, nil, nil, nil, 73, 78,
- 5, 62, 63, 64, 7, 51, nil, nil, nil, 56,
- 57, nil, nil, nil, 60, nil, 58, 59, 61, 23,
- 24, 65, 66, nil, nil, nil, nil, 22, 28, 27,
- 88, 87, 89, 90, nil, nil, 17, nil, nil, nil,
- nil, nil, 6, 41, 8, 9, 92, 91, 82, 50,
- 84, 83, 86, 85, 93, 94, nil, 80, 81, nil,
- 38, 39, 37, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 36, nil, nil, 30, nil, nil, 52, 53,
- nil, nil, 54, nil, 32, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 18, nil, nil, nil,
- nil, 79, 72, 74, 75, 76, 77, nil, nil, nil,
- 73, 78, 5, 62, 63, 64, 7, 51, nil, nil,
- nil, 56, 57, nil, nil, nil, 60, nil, 58, 59,
- 61, 23, 24, 65, 66, nil, nil, nil, nil, 22,
- 28, 27, 88, 87, 89, 90, nil, nil, 17, nil,
- nil, nil, nil, nil, 6, 41, 8, 9, 92, 91,
- 82, 50, 84, 83, 86, 85, 93, 94, nil, 80,
- 81, nil, 38, 39, 37, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 36, nil, nil, 30, nil, nil,
- 52, 53, nil, nil, 54, nil, 32, nil, nil, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 18, nil,
- nil, nil, nil, 79, 72, 74, 75, 76, 77, nil,
- nil, nil, 73, 78, 5, 62, 63, 64, 7, 51,
- nil, nil, nil, 56, 57, nil, nil, nil, 60, nil,
- 58, 59, 61, 23, 24, 65, 66, nil, nil, nil,
- nil, 22, 28, 27, 88, 87, 89, 90, nil, nil,
- 17, nil, nil, nil, nil, nil, 6, 41, 8, 9,
- 92, 91, 82, 50, 84, 83, 86, 85, 93, 94,
- nil, 80, 81, nil, 38, 39, 37, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 36, nil, nil, 30,
- nil, nil, 52, 53, nil, nil, 54, nil, 32, nil,
- nil, nil, 40, nil, nil, nil, nil, nil, nil, nil,
- 18, nil, nil, nil, nil, 79, 72, 74, 75, 76,
- 77, nil, nil, nil, 73, 78, 5, 62, 63, 64,
- 7, 51, nil, nil, nil, 56, 57, nil, nil, nil,
- 60, nil, 58, 59, 61, 23, 24, 65, 66, nil,
- nil, nil, nil, 22, 28, 27, 88, 87, 89, 90,
- nil, nil, 17, nil, nil, nil, nil, nil, 6, 41,
- 8, 9, 92, 91, 82, 50, 84, 83, 86, 85,
- 93, 94, nil, 80, 81, nil, 38, 39, 37, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 36, nil,
- nil, 30, nil, nil, 52, 53, nil, nil, 54, nil,
- 32, nil, nil, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 18, nil, nil, nil, nil, 79, 72, 74,
- 75, 76, 77, nil, nil, nil, 73, 78, 5, 62,
- 63, 64, 7, 51, nil, nil, nil, 56, 57, nil,
- nil, nil, 60, nil, 58, 59, 61, 23, 24, 65,
- 66, nil, nil, nil, nil, 22, 28, 27, 88, 87,
- 89, 90, nil, nil, 17, nil, nil, nil, nil, nil,
- 6, 41, 8, 9, 92, 91, 82, 50, 84, 83,
- 86, 85, 93, 94, nil, 80, 81, nil, 38, 39,
- 37, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 36, nil, nil, 30, nil, nil, 52, 53, nil, nil,
- 54, nil, 32, nil, nil, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 18, nil, nil, nil, nil, 79,
- 72, 74, 75, 76, 77, nil, nil, nil, 73, 78,
- 5, 62, 63, 64, 7, 51, nil, nil, nil, 56,
- 57, nil, nil, nil, 60, nil, 58, 59, 61, 23,
- 24, 65, 66, nil, nil, nil, nil, 22, 28, 27,
- 88, 87, 89, 90, nil, nil, 17, nil, nil, nil,
- nil, nil, 6, 41, 8, 9, 92, 91, 82, 50,
- 84, 83, 86, 85, 93, 94, nil, 80, 81, nil,
- 38, 39, 37, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 36, nil, nil, 30, nil, nil, 52, 53,
- nil, nil, 54, nil, 32, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 18, nil, nil, nil,
- nil, 79, 72, 74, 75, 76, 77, nil, nil, nil,
- 73, 78, 62, 63, 64, 7, 51, nil, nil, nil,
- 56, 57, nil, nil, nil, 60, nil, 58, 59, 61,
- 23, 24, 65, 66, nil, nil, nil, nil, 22, 28,
- 27, 88, 87, 89, 90, nil, nil, 17, nil, nil,
- nil, nil, nil, 6, 41, 8, 9, 92, 91, 82,
- 50, 84, 83, 86, 85, 93, 94, nil, 80, 81,
- nil, 38, 39, 37, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 36, nil, nil, 30, nil, nil, 52,
- 53, nil, nil, 54, nil, 32, nil, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 18, nil, nil,
- nil, nil, 79, 72, 74, 75, 76, 77, nil, nil,
- nil, 73, 78, 153, 164, 154, 177, 150, 170, 160,
- 159, 180, 181, 175, 158, 157, 152, 178, 182, 183,
- 162, 151, 165, 169, 171, 163, 156, nil, nil, 172,
- 179, 174, 173, 166, 176, 161, 149, 168, 167, nil,
- nil, nil, nil, nil, 148, 155, 146, 147, 144, 145,
- 109, 111, 108, nil, 110, nil, nil, nil, nil, nil,
- nil, nil, 139, 140, nil, 137, 121, 122, 123, nil,
- 126, 128, nil, nil, 124, nil, nil, nil, nil, 141,
- 142, 129, 130, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 134, 133, nil, 120,
- 138, 136, 135, 131, 132, 127, 125, 118, nil, 119,
- nil, nil, 143, 79, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 78, 153, 164, 154, 177, 150, 170,
- 160, 159, 180, 181, 175, 158, 157, 152, 178, 182,
- 183, 162, 151, 165, 169, 171, 163, 156, nil, nil,
- 172, 179, 174, 173, 166, 176, 161, 149, 168, 167,
- nil, nil, nil, nil, nil, 148, 155, 146, 147, 144,
- 145, 109, 111, nil, nil, 110, nil, nil, nil, nil,
- nil, nil, nil, 139, 140, nil, 137, 121, 122, 123,
- nil, 126, 128, nil, nil, 124, nil, nil, nil, nil,
- 141, 142, 129, 130, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 134, 133, nil,
- 120, 138, 136, 135, 131, 132, 127, 125, 118, nil,
- 119, nil, nil, 143, 79, nil, nil, 62, 63, 64,
- nil, 51, nil, nil, 78, 56, 57, nil, nil, nil,
- 60, nil, 58, 59, 61, 23, 24, 65, 66, nil,
- nil, nil, nil, 22, 28, 27, 88, 87, 89, 90,
- nil, nil, 17, nil, nil, nil, nil, nil, nil, 41,
- nil, nil, 92, 91, 82, 50, 84, 83, 86, 85,
- 93, 94, nil, 80, 81, nil, 38, 39, 37, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 200, nil,
- nil, 204, nil, nil, 52, 53, nil, nil, 54, nil,
- nil, nil, nil, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 18, nil, nil, nil, nil, 79, 72, 74,
- 75, 76, 77, nil, nil, nil, 73, 78, 62, 63,
- 64, nil, 51, nil, nil, nil, 56, 57, nil, nil,
- nil, 60, nil, 58, 59, 61, 23, 24, 65, 66,
- nil, nil, nil, nil, 22, 28, 27, 88, 87, 89,
- 90, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 41, nil, nil, 92, 91, 82, 50, 84, 83, 86,
- 85, 93, 94, nil, 80, 81, nil, 38, 39, 37,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 200,
- nil, nil, 204, nil, nil, 52, 53, nil, nil, 54,
- nil, nil, nil, nil, nil, 40, nil, nil, nil, nil,
- nil, nil, nil, 207, nil, nil, nil, nil, 79, 72,
- 74, 75, 76, 77, nil, nil, nil, 73, 78, 62,
- 63, 64, nil, 51, nil, nil, nil, 56, 57, nil,
- nil, nil, 60, nil, 58, 59, 61, 246, 247, 65,
- 66, nil, nil, nil, nil, 245, 28, 27, 88, 87,
- 89, 90, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 41, nil, nil, 92, 91, 82, 50, 84, 83,
- 86, 85, 93, 94, nil, 80, 81, nil, 38, 39,
- 37, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 200, nil, nil, 204, nil, nil, 52, 53, nil, nil,
- 54, nil, 241, nil, 243, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 207, nil, nil, nil, nil, 79,
- 72, 74, 75, 76, 77, nil, nil, nil, 73, 78,
- 62, 63, 64, nil, 51, nil, nil, nil, 56, 57,
- nil, nil, nil, 60, nil, 58, 59, 61, 246, 247,
- 65, 66, nil, nil, nil, nil, 245, 28, 27, 88,
- 87, 89, 90, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 41, nil, nil, 92, 91, 82, 50, 84,
- 83, 86, 85, 93, 94, nil, 80, 81, nil, 38,
- 39, 37, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 200, nil, nil, 204, nil, nil, 52, 53, nil,
- nil, 54, nil, 241, nil, 243, nil, 40, nil, nil,
- nil, nil, nil, nil, nil, 207, nil, nil, nil, nil,
- 79, 72, 74, 75, 76, 77, nil, nil, nil, 73,
- 78, 62, 63, 64, nil, 51, nil, nil, nil, 56,
- 57, nil, nil, nil, 60, nil, 58, 59, 61, 246,
- 247, 65, 66, nil, nil, nil, nil, 245, 28, 27,
- 88, 87, 89, 90, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, 92, 91, 82, 50,
- 84, 83, 86, 85, 93, 94, nil, 80, 81, nil,
- 38, 39, 37, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 200, nil, nil, 204, nil, nil, 52, 53,
- nil, nil, 54, nil, 241, nil, 243, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 207, nil, nil, nil,
- nil, 79, 72, 74, 75, 76, 77, nil, nil, nil,
- 73, 78, -249, -249, -249, nil, -249, nil, nil, nil,
- -249, -249, nil, nil, nil, -249, nil, -249, -249, -249,
- -249, -249, -249, -249, nil, nil, nil, nil, -249, -249,
- -249, -249, -249, -249, -249, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, -249, nil, nil, -249, -249, -249,
- -249, -249, -249, -249, -249, -249, -249, nil, -249, -249,
- nil, -249, -249, -249, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, -249, nil, nil, -249, 254, nil, -249,
- -249, nil, nil, -249, nil, -249, nil, -249, nil, -249,
- nil, nil, nil, nil, nil, nil, nil, -249, nil, nil,
- nil, nil, -249, -249, -249, -249, -249, -249, nil, nil,
- nil, -249, -249, -249, -249, -249, nil, -249, nil, nil,
- nil, -249, -249, nil, nil, nil, -249, nil, -249, -249,
- -249, -249, -249, -249, -249, nil, nil, nil, nil, -249,
- -249, -249, -249, -249, -249, -249, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, -249, nil, nil, -249, -249,
- -249, -249, -249, -249, -249, -249, -249, -249, nil, -249,
- -249, nil, -249, -249, -249, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, -249, nil, nil, -249, 263, nil,
- -249, -249, nil, nil, -249, nil, -249, nil, -249, nil,
- -249, nil, nil, nil, nil, nil, nil, nil, -249, nil,
- nil, nil, nil, -249, -249, -249, -249, -249, -249, nil,
- nil, nil, -249, -249, 62, 63, 64, nil, 51, nil,
- nil, nil, 56, 57, nil, nil, nil, 60, nil, 58,
- 59, 61, 246, 247, 65, 66, nil, nil, nil, nil,
- 245, 275, 279, 88, 87, 89, 90, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 276, nil, nil, 92,
- 91, 82, 50, 84, 83, 86, 85, 93, 94, nil,
- 80, 81, nil, nil, nil, 280, nil, 215, 219, 224,
- 225, 226, 221, 223, 231, 232, 227, 228, nil, 208,
- 209, nil, nil, 229, 230, 273, nil, nil, 270, nil,
- nil, 52, 53, nil, nil, 54, nil, 269, nil, 212,
- nil, 218, nil, 214, 213, 210, 211, 222, 220, 216,
- nil, 217, nil, nil, 79, 72, 74, 75, 76, 77,
- nil, nil, nil, 73, 78, 62, 63, 64, 233, 51,
- 568, nil, nil, 56, 57, nil, nil, nil, 60, nil,
- 58, 59, 61, 246, 247, 65, 66, nil, nil, nil,
- nil, 245, 275, 279, 88, 87, 89, 90, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 276, nil, nil,
- 92, 91, 82, 50, 84, 83, 86, 85, 93, 94,
- nil, 80, 81, nil, nil, nil, 280, nil, 215, 219,
- 224, 225, 226, 221, 223, 231, 232, 227, 228, nil,
- 208, 209, nil, nil, 229, 230, 273, nil, nil, 204,
- nil, nil, 52, 53, nil, nil, 54, nil, nil, nil,
- 212, nil, 218, nil, 214, 213, 210, 211, 222, 220,
- 216, nil, 217, nil, nil, 79, 72, 74, 75, 76,
- 77, nil, nil, nil, 73, 78, 62, 63, 64, 233,
- 51, nil, nil, nil, 56, 57, nil, nil, nil, 60,
- nil, 58, 59, 61, 246, 247, 65, 66, nil, nil,
- nil, nil, 245, 275, 279, 88, 87, 89, 90, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 41, nil,
- nil, 92, 91, 82, 50, 84, 83, 86, 85, 93,
- 94, nil, 80, 81, nil, 38, 39, 37, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 200, nil, nil,
- 204, nil, nil, 52, 53, nil, nil, 54, nil, nil,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 207, nil, nil, nil, nil, 79, 72, 74, 75,
- 76, 77, nil, nil, nil, 73, 78, 62, 63, 64,
- nil, 51, nil, nil, nil, 56, 57, nil, nil, nil,
- 60, nil, 58, 59, 61, 246, 247, 65, 66, nil,
- nil, nil, nil, 245, 275, 279, 88, 87, 89, 90,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 41,
- nil, nil, 92, 91, 82, 50, 84, 83, 86, 85,
- 93, 94, nil, 80, 81, nil, 38, 39, 37, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 200, nil,
- nil, 204, nil, nil, 52, 53, nil, nil, 54, nil,
- nil, nil, nil, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 207, nil, nil, nil, nil, 79, 72, 74,
- 75, 76, 77, nil, nil, nil, 73, 78, 62, 63,
- 64, nil, 51, nil, nil, nil, 56, 57, nil, nil,
- nil, 60, nil, 58, 59, 61, 246, 247, 65, 66,
- nil, nil, nil, nil, 245, 275, 279, 88, 87, 89,
- 90, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 41, nil, nil, 92, 91, 82, 50, 84, 83, 86,
- 85, 93, 94, nil, 80, 81, nil, 38, 39, 37,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 200,
- nil, nil, 204, nil, nil, 52, 53, nil, nil, 54,
- nil, nil, nil, nil, nil, 40, nil, nil, nil, nil,
- nil, nil, nil, 207, nil, nil, nil, nil, 79, 72,
- 74, 75, 76, 77, nil, nil, nil, 73, 78, 62,
- 63, 64, nil, 51, nil, nil, nil, 56, 57, nil,
- nil, nil, 60, nil, 58, 59, 61, 23, 24, 65,
- 66, nil, nil, nil, nil, 22, 28, 27, 88, 87,
- 89, 90, nil, nil, 17, nil, nil, nil, nil, nil,
- nil, 41, nil, nil, 92, 91, 82, 50, 84, 83,
- 86, 85, 93, 94, nil, 80, 81, nil, 38, 39,
- 37, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 200, nil, nil, 204, nil, nil, 52, 53, nil, nil,
- 54, nil, nil, nil, nil, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 18, nil, nil, nil, nil, 79,
- 72, 74, 75, 76, 77, nil, nil, nil, 73, 78,
- 62, 63, 64, nil, 51, nil, nil, nil, 56, 57,
- nil, nil, nil, 60, nil, 58, 59, 61, 246, 247,
- 65, 66, nil, nil, nil, nil, 245, 28, 27, 88,
- 87, 89, 90, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 41, nil, nil, 92, 91, 82, 50, 84,
- 83, 86, 85, 93, 94, nil, 80, 81, nil, 38,
- 39, 37, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 200, nil, nil, 204, nil, nil, 52, 53, nil,
- nil, 54, nil, 299, nil, nil, nil, 40, nil, nil,
- nil, nil, nil, nil, nil, 207, nil, nil, nil, nil,
- 79, 72, 74, 75, 76, 77, nil, nil, nil, 73,
- 78, 62, 63, 64, nil, 51, nil, nil, nil, 56,
- 57, nil, nil, nil, 60, nil, 58, 59, 61, 246,
- 247, 65, 66, nil, nil, nil, nil, 245, 275, 279,
- 88, 87, 89, 90, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, 92, 91, 82, 50,
- 84, 83, 86, 85, 93, 94, nil, 80, 81, nil,
- 38, 39, 37, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 200, nil, nil, 204, nil, nil, 52, 53,
- nil, nil, 54, nil, nil, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 207, nil, nil, nil,
- nil, 79, 72, 74, 75, 76, 77, nil, nil, nil,
- 73, 78, 62, 63, 64, nil, 51, nil, nil, nil,
- 56, 57, nil, nil, nil, 60, nil, 58, 59, 61,
- 23, 24, 65, 66, nil, nil, nil, nil, 22, 28,
- 27, 88, 87, 89, 90, nil, nil, 17, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 92, 91, 82,
- 50, 84, 83, 86, 85, 93, 94, nil, 80, 81,
- nil, 38, 39, 37, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 200, nil, nil, 204, nil, nil, 52,
- 53, nil, nil, 54, nil, nil, nil, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 18, nil, nil,
- nil, nil, 79, 72, 74, 75, 76, 77, nil, nil,
- nil, 73, 78, 62, 63, 64, nil, 51, nil, nil,
- nil, 56, 57, nil, nil, nil, 60, nil, 58, 59,
- 61, 23, 24, 65, 66, nil, nil, nil, nil, 22,
- 28, 27, 88, 87, 89, 90, nil, nil, 17, nil,
- nil, nil, nil, nil, nil, 41, nil, nil, 92, 91,
- 82, 50, 84, 83, 86, 85, 93, 94, nil, 80,
- 81, nil, 38, 39, 37, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 200, nil, nil, 204, nil, nil,
- 52, 53, nil, nil, 54, nil, nil, nil, nil, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 18, nil,
- nil, nil, nil, 79, 72, 74, 75, 76, 77, nil,
- nil, nil, 73, 78, 62, 63, 64, nil, 51, nil,
- nil, nil, 56, 57, nil, nil, nil, 60, nil, 58,
- 59, 61, 246, 247, 65, 66, nil, nil, nil, nil,
- 245, 275, 279, 88, 87, 89, 90, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 276, nil, nil, 92,
- 91, 82, 50, 84, 83, 86, 85, 93, 94, nil,
- 80, 81, nil, nil, nil, 280, nil, 215, 219, 224,
- 225, 226, 221, 223, 231, 232, 227, 228, nil, 208,
- 209, nil, nil, 229, 230, 315, nil, nil, 30, nil,
- nil, 52, 53, nil, nil, 54, nil, 32, nil, 212,
- nil, 218, nil, 214, 213, 210, 211, 222, 220, 216,
- nil, 217, nil, nil, 79, 72, 74, 75, 76, 77,
- nil, nil, nil, 73, 78, 62, 63, 64, 233, 51,
- nil, nil, nil, 56, 57, nil, nil, nil, 60, nil,
- 58, 59, 61, 246, 247, 65, 66, nil, nil, nil,
- nil, 245, 275, 279, 88, 87, 89, 90, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 276, nil, nil,
- 92, 91, 320, 50, 84, 83, 321, 85, 93, 94,
- nil, 80, 81, nil, nil, nil, 280, nil, 215, 219,
- 224, 225, 226, 221, 223, 231, 232, 227, 228, nil,
- 208, 209, nil, 327, 229, 230, 322, nil, nil, 204,
- nil, nil, 52, 53, nil, nil, 54, nil, nil, nil,
- 212, nil, 218, nil, 214, 213, 210, 211, 222, 220,
- 216, nil, 217, nil, nil, 79, 72, 74, 75, 76,
- 77, nil, nil, nil, 73, 78, 62, 63, 64, 233,
- 51, nil, nil, nil, 56, 57, nil, nil, nil, 60,
- nil, 58, 59, 61, 246, 247, 65, 66, nil, nil,
- nil, nil, 245, 275, 279, 88, 87, 89, 90, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 276, nil,
- nil, 92, 91, 320, 50, 84, 83, 321, 85, 93,
- 94, nil, 80, 81, nil, nil, nil, 280, nil, 215,
- 219, 224, 225, 226, 221, 223, 231, 232, 227, 228,
- nil, 208, 209, nil, nil, 229, 230, 322, nil, nil,
- 204, nil, nil, 52, 53, nil, nil, 54, nil, nil,
- nil, 212, nil, 218, nil, 214, 213, 210, 211, 222,
- 220, 216, nil, 217, nil, nil, 79, 72, 74, 75,
- 76, 77, nil, nil, nil, 73, 78, -473, -473, -473,
- 233, -473, nil, nil, nil, -473, -473, nil, nil, nil,
- -473, nil, -473, -473, -473, -473, -473, -473, -473, nil,
- -473, nil, nil, -473, -473, -473, -473, -473, -473, -473,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, -473,
- nil, nil, -473, -473, -473, -473, -473, -473, -473, -473,
- -473, -473, nil, -473, -473, nil, -473, -473, -473, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, -473, nil,
- nil, -473, -473, nil, -473, -473, nil, nil, -473, nil,
- -473, nil, -473, nil, -473, nil, nil, nil, nil, nil,
- nil, nil, -473, nil, -473, nil, nil, -473, -473, -473,
- -473, -473, -473, nil, nil, nil, -473, -473, -474, -474,
- -474, nil, -474, nil, nil, nil, -474, -474, nil, nil,
- nil, -474, nil, -474, -474, -474, -474, -474, -474, -474,
- nil, -474, nil, nil, -474, -474, -474, -474, -474, -474,
- -474, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- -474, nil, nil, -474, -474, -474, -474, -474, -474, -474,
- -474, -474, -474, nil, -474, -474, nil, -474, -474, -474,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, -474,
- nil, nil, -474, -474, nil, -474, -474, nil, nil, -474,
- nil, -474, nil, -474, nil, -474, nil, nil, nil, nil,
- nil, nil, nil, -474, nil, -474, nil, nil, -474, -474,
- -474, -474, -474, -474, nil, nil, nil, -474, -474, 62,
- 63, 64, nil, 51, nil, nil, nil, 56, 57, nil,
- nil, nil, 60, nil, 58, 59, 61, 23, 24, 65,
- 66, nil, nil, nil, nil, 22, 28, 27, 88, 87,
- 89, 90, nil, nil, 17, nil, nil, nil, nil, nil,
- nil, 41, nil, nil, 92, 91, 82, 50, 84, 83,
- 86, 85, 93, 94, nil, 80, 81, nil, 38, 39,
- 37, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 200, nil, nil, 204, nil, nil, 52, 53, nil, nil,
- 54, nil, nil, nil, nil, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 18, nil, nil, nil, nil, 79,
- 72, 74, 75, 76, 77, nil, nil, nil, 73, 78,
- 62, 63, 64, nil, 51, nil, nil, nil, 56, 57,
- nil, nil, nil, 60, nil, 58, 59, 61, 23, 24,
- 65, 66, nil, nil, nil, nil, 22, 28, 27, 88,
- 87, 89, 90, nil, nil, 17, nil, nil, nil, nil,
- nil, nil, 41, nil, nil, 92, 91, 82, 50, 84,
- 83, 86, 85, 93, 94, nil, 80, 81, nil, 38,
- 39, 37, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 200, nil, nil, 204, nil, nil, 52, 53, nil,
- nil, 54, nil, nil, nil, nil, nil, 40, nil, nil,
- nil, nil, nil, nil, nil, 18, nil, nil, nil, nil,
- 79, 72, 74, 75, 76, 77, nil, nil, nil, 73,
- 78, 62, 63, 64, nil, 51, nil, nil, nil, 56,
- 57, nil, nil, nil, 60, nil, 58, 59, 61, 23,
- 24, 65, 66, nil, nil, nil, nil, 22, 28, 27,
- 88, 87, 89, 90, nil, nil, 17, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, 92, 91, 82, 50,
- 84, 83, 86, 85, 93, 94, nil, 80, 81, nil,
- 38, 39, 37, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 200, nil, nil, 204, nil, nil, 52, 53,
- nil, nil, 54, nil, nil, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 18, nil, nil, nil,
- nil, 79, 72, 74, 75, 76, 77, nil, nil, nil,
- 73, 78, 62, 63, 64, nil, 51, nil, nil, nil,
- 56, 57, nil, nil, nil, 60, nil, 58, 59, 61,
- 23, 24, 65, 66, nil, nil, nil, nil, 22, 28,
- 27, 88, 87, 89, 90, nil, nil, 17, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 92, 91, 82,
- 50, 84, 83, 86, 85, 93, 94, nil, 80, 81,
- nil, 38, 39, 37, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 200, nil, nil, 204, nil, nil, 52,
- 53, nil, nil, 54, nil, nil, nil, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 18, nil, nil,
- nil, nil, 79, 72, 74, 75, 76, 77, nil, nil,
- nil, 73, 78, 62, 63, 64, 7, 51, nil, nil,
- nil, 56, 57, nil, nil, nil, 60, nil, 58, 59,
- 61, 23, 24, 65, 66, nil, nil, nil, nil, 22,
- 28, 27, 88, 87, 89, 90, nil, nil, 17, nil,
- nil, nil, nil, nil, 6, 41, 8, 9, 92, 91,
- 82, 50, 84, 83, 86, 85, 93, 94, nil, 80,
- 81, nil, 38, 39, 37, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 36, nil, nil, 30, nil, nil,
- 52, 53, nil, nil, 54, nil, 32, nil, nil, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 18, nil,
- nil, nil, nil, 79, 72, 74, 75, 76, 77, nil,
- nil, nil, 73, 78, 62, 63, 64, nil, 51, nil,
- nil, nil, 56, 57, nil, nil, nil, 60, nil, 58,
- 59, 61, 23, 24, 65, 66, nil, nil, nil, nil,
- 22, 28, 27, 88, 87, 89, 90, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 41, nil, nil, 92,
- 91, 82, 50, 84, 83, 86, 85, 93, 94, nil,
- 80, 81, nil, 38, 39, 37, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 200, nil, nil, 204, nil,
- nil, 52, 53, nil, nil, 54, nil, 369, nil, nil,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 207,
- nil, nil, nil, nil, 79, 72, 74, 75, 76, 77,
- nil, nil, nil, 73, 78, 62, 63, 64, nil, 51,
- nil, nil, nil, 56, 57, nil, nil, nil, 60, nil,
- 58, 59, 61, 23, 24, 65, 66, nil, nil, nil,
- nil, 22, 28, 27, 88, 87, 89, 90, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 41, nil, nil,
- 92, 91, 82, 50, 84, 83, 86, 85, 93, 94,
- nil, 80, 81, nil, 38, 39, 37, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 200, nil, nil, 204,
- nil, nil, 52, 53, nil, nil, 54, nil, 369, nil,
- nil, nil, 40, nil, nil, nil, nil, nil, nil, nil,
- 207, nil, nil, nil, nil, 79, 72, 74, 75, 76,
- 77, nil, nil, nil, 73, 78, 62, 63, 64, nil,
- 51, nil, nil, nil, 56, 57, nil, nil, nil, 60,
- nil, 58, 59, 61, 23, 24, 65, 66, nil, nil,
- nil, nil, 22, 28, 27, 88, 87, 89, 90, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 41, nil,
- nil, 92, 91, 82, 50, 84, 83, 86, 85, 93,
- 94, nil, 80, 81, nil, 38, 39, 37, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 200, nil, nil,
- 204, nil, nil, 52, 53, nil, nil, 54, nil, nil,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 207, nil, nil, nil, nil, 79, 72, 74, 75,
- 76, 77, nil, nil, nil, 73, 78, 62, 63, 64,
- nil, 51, nil, nil, nil, 56, 57, nil, nil, nil,
- 60, nil, 58, 59, 61, 246, 247, 65, 66, nil,
- nil, nil, nil, 245, 28, 27, 88, 87, 89, 90,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 41,
- nil, nil, 92, 91, 82, 50, 84, 83, 86, 85,
- 93, 94, nil, 80, 81, nil, 38, 39, 37, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 200, nil,
- nil, 204, nil, nil, 52, 53, nil, nil, 54, nil,
- 299, nil, nil, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 207, nil, nil, nil, nil, 79, 72, 74,
- 75, 76, 77, nil, nil, nil, 73, 78, 62, 63,
- 64, nil, 51, nil, nil, nil, 56, 57, nil, nil,
- nil, 60, nil, 58, 59, 61, 23, 24, 65, 66,
- nil, nil, nil, nil, 22, 28, 27, 88, 87, 89,
- 90, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 41, nil, nil, 92, 91, 82, 50, 84, 83, 86,
- 85, 93, 94, nil, 80, 81, nil, 38, 39, 37,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 200,
- nil, nil, 204, nil, nil, 52, 53, nil, nil, 54,
- nil, nil, nil, nil, nil, 40, nil, nil, nil, nil,
- nil, nil, nil, 207, nil, nil, nil, nil, 79, 72,
- 74, 75, 76, 77, nil, nil, nil, 73, 78, 62,
- 63, 64, nil, 51, nil, nil, nil, 56, 57, nil,
- nil, nil, 60, nil, 58, 59, 61, 23, 24, 65,
- 66, nil, nil, nil, nil, 22, 28, 27, 88, 87,
- 89, 90, nil, nil, 17, nil, nil, nil, nil, nil,
- nil, 41, nil, nil, 92, 91, 82, 50, 84, 83,
- 86, 85, 93, 94, nil, 80, 81, nil, 38, 39,
- 37, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 200, nil, nil, 204, nil, nil, 52, 53, nil, nil,
- 54, nil, nil, nil, nil, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 18, nil, nil, nil, nil, 79,
- 72, 74, 75, 76, 77, nil, nil, nil, 73, 78,
- 62, 63, 64, nil, 51, nil, nil, nil, 56, 57,
- nil, nil, nil, 60, nil, 58, 59, 61, 23, 24,
- 65, 66, nil, nil, nil, nil, 22, 28, 27, 88,
- 87, 89, 90, nil, nil, 17, nil, nil, nil, nil,
- nil, nil, 41, nil, nil, 92, 91, 82, 50, 84,
- 83, 86, 85, 93, 94, nil, 80, 81, nil, 38,
- 39, 37, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 200, nil, nil, 204, nil, nil, 52, 53, nil,
- nil, 54, nil, nil, nil, nil, nil, 40, nil, nil,
- nil, nil, nil, nil, nil, 18, nil, nil, nil, nil,
- 79, 72, 74, 75, 76, 77, nil, nil, nil, 73,
- 78, 62, 63, 64, nil, 51, nil, nil, nil, 56,
- 57, nil, nil, nil, 60, nil, 58, 59, 61, 246,
- 247, 65, 66, nil, nil, nil, nil, 245, 275, 279,
- 88, 87, 89, 90, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, 92, 91, 82, 50,
- 84, 83, 86, 85, 93, 94, nil, 80, 81, nil,
- 38, 39, 37, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 200, nil, nil, 204, nil, nil, 52, 53,
- nil, nil, 54, nil, nil, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 207, nil, nil, nil,
- nil, 79, 72, 74, 75, 76, 77, nil, nil, nil,
- 73, 78, 62, 63, 64, nil, 51, nil, nil, nil,
- 56, 57, nil, nil, nil, 60, nil, 58, 59, 61,
- 246, 247, 65, 66, nil, nil, nil, nil, 245, 275,
- 279, 88, 87, 89, 90, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 92, 91, 82,
- 50, 84, 83, 86, 85, 93, 94, nil, 80, 81,
- nil, 38, 39, 37, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 200, nil, nil, 204, nil, nil, 52,
- 53, nil, nil, 54, nil, nil, nil, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 207, nil, nil,
- nil, nil, 79, 72, 74, 75, 76, 77, nil, nil,
- nil, 73, 78, 62, 63, 64, nil, 51, nil, nil,
- nil, 56, 57, nil, nil, nil, 60, nil, 58, 59,
- 61, 246, 247, 65, 66, nil, nil, nil, nil, 245,
- 275, 279, 88, 87, 89, 90, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 41, nil, nil, 92, 91,
- 82, 50, 84, 83, 86, 85, 93, 94, nil, 80,
- 81, nil, 38, 39, 37, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 200, nil, nil, 204, nil, nil,
- 52, 53, nil, nil, 54, nil, nil, nil, nil, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 207, nil,
- nil, nil, nil, 79, 72, 74, 75, 76, 77, nil,
- nil, nil, 73, 78, 62, 63, 64, nil, 51, nil,
- nil, nil, 56, 57, nil, nil, nil, 60, nil, 58,
- 59, 61, 246, 247, 65, 66, nil, nil, nil, nil,
- 245, 275, 279, 88, 87, 89, 90, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 41, nil, nil, 92,
- 91, 82, 50, 84, 83, 86, 85, 93, 94, nil,
- 80, 81, nil, 38, 39, 37, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 200, nil, nil, 204, nil,
- nil, 52, 53, nil, nil, 54, nil, nil, nil, nil,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 207,
- nil, nil, nil, nil, 79, 72, 74, 75, 76, 77,
- nil, nil, nil, 73, 78, 62, 63, 64, nil, 51,
- nil, nil, nil, 56, 57, nil, nil, nil, 60, nil,
- 58, 59, 61, 246, 247, 65, 66, nil, nil, nil,
- nil, 245, 275, 279, 88, 87, 89, 90, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 41, nil, nil,
- 92, 91, 82, 50, 84, 83, 86, 85, 93, 94,
- nil, 80, 81, nil, 38, 39, 37, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 200, nil, nil, 204,
- nil, nil, 52, 53, nil, nil, 54, nil, nil, nil,
- nil, nil, 40, nil, nil, nil, nil, nil, nil, nil,
- 207, nil, nil, nil, nil, 79, 72, 74, 75, 76,
- 77, nil, nil, nil, 73, 78, 62, 63, 64, nil,
- 51, nil, nil, nil, 56, 57, nil, nil, nil, 60,
- nil, 58, 59, 61, 246, 247, 65, 66, nil, nil,
- nil, nil, 245, 275, 279, 88, 87, 89, 90, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 41, nil,
- nil, 92, 91, 82, 50, 84, 83, 86, 85, 93,
- 94, nil, 80, 81, nil, 38, 39, 37, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 200, nil, nil,
- 204, nil, nil, 52, 53, nil, nil, 54, nil, nil,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 207, nil, nil, nil, nil, 79, 72, 74, 75,
- 76, 77, nil, nil, nil, 73, 78, 62, 63, 64,
- nil, 51, nil, nil, nil, 56, 57, nil, nil, nil,
- 60, nil, 58, 59, 61, 246, 247, 65, 66, nil,
- nil, nil, nil, 245, 275, 279, 88, 87, 89, 90,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 41,
- nil, nil, 92, 91, 82, 50, 84, 83, 86, 85,
- 93, 94, nil, 80, 81, nil, 38, 39, 37, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 200, nil,
- nil, 204, nil, nil, 52, 53, nil, nil, 54, nil,
- nil, nil, nil, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 207, nil, nil, nil, nil, 79, 72, 74,
- 75, 76, 77, nil, nil, nil, 73, 78, 62, 63,
- 64, nil, 51, nil, nil, nil, 56, 57, nil, nil,
- nil, 60, nil, 58, 59, 61, 246, 247, 65, 66,
- nil, nil, nil, nil, 245, 275, 279, 88, 87, 89,
- 90, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 41, nil, nil, 92, 91, 82, 50, 84, 83, 86,
- 85, 93, 94, nil, 80, 81, nil, 38, 39, 37,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 200,
- nil, nil, 204, nil, nil, 52, 53, nil, nil, 54,
- nil, nil, nil, nil, nil, 40, nil, nil, nil, nil,
- nil, nil, nil, 207, nil, nil, nil, nil, 79, 72,
- 74, 75, 76, 77, nil, nil, nil, 73, 78, 62,
- 63, 64, nil, 51, nil, nil, nil, 56, 57, nil,
- nil, nil, 60, nil, 58, 59, 61, 246, 247, 65,
- 66, nil, nil, nil, nil, 245, 275, 279, 88, 87,
- 89, 90, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 41, nil, nil, 92, 91, 82, 50, 84, 83,
- 86, 85, 93, 94, nil, 80, 81, nil, 38, 39,
- 37, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 200, nil, nil, 204, nil, nil, 52, 53, nil, nil,
- 54, nil, nil, nil, nil, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 207, nil, nil, nil, nil, 79,
- 72, 74, 75, 76, 77, nil, nil, nil, 73, 78,
- 62, 63, 64, nil, 51, nil, nil, nil, 56, 57,
- nil, nil, nil, 60, nil, 58, 59, 61, 246, 247,
- 65, 66, nil, nil, nil, nil, 245, 275, 279, 88,
- 87, 89, 90, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 41, nil, nil, 92, 91, 82, 50, 84,
- 83, 86, 85, 93, 94, nil, 80, 81, nil, 38,
- 39, 37, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 200, nil, nil, 204, nil, nil, 52, 53, nil,
- nil, 54, nil, nil, nil, nil, nil, 40, nil, nil,
- nil, nil, nil, nil, nil, 207, nil, nil, nil, nil,
- 79, 72, 74, 75, 76, 77, nil, nil, nil, 73,
- 78, 62, 63, 64, nil, 51, nil, nil, nil, 56,
- 57, nil, nil, nil, 60, nil, 58, 59, 61, 246,
- 247, 65, 66, nil, nil, nil, nil, 245, 275, 279,
- 88, 87, 89, 90, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, 92, 91, 82, 50,
- 84, 83, 86, 85, 93, 94, nil, 80, 81, nil,
- 38, 39, 37, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 200, nil, nil, 204, nil, nil, 52, 53,
- nil, nil, 54, nil, nil, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 207, nil, nil, nil,
- nil, 79, 72, 74, 75, 76, 77, nil, nil, nil,
- 73, 78, 62, 63, 64, nil, 51, nil, nil, nil,
- 56, 57, nil, nil, nil, 60, nil, 58, 59, 61,
- 246, 247, 65, 66, nil, nil, nil, nil, 245, 275,
- 279, 88, 87, 89, 90, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 92, 91, 82,
- 50, 84, 83, 86, 85, 93, 94, nil, 80, 81,
- nil, 38, 39, 37, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 200, nil, nil, 204, nil, nil, 52,
- 53, nil, nil, 54, nil, nil, nil, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 207, nil, nil,
- nil, nil, 79, 72, 74, 75, 76, 77, nil, nil,
- nil, 73, 78, 62, 63, 64, nil, 51, nil, nil,
- nil, 56, 57, nil, nil, nil, 60, nil, 58, 59,
- 61, 246, 247, 65, 66, nil, nil, nil, nil, 245,
- 275, 279, 88, 87, 89, 90, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 41, nil, nil, 92, 91,
- 82, 50, 84, 83, 86, 85, 93, 94, nil, 80,
- 81, nil, 38, 39, 37, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 200, nil, nil, 204, nil, nil,
- 52, 53, nil, nil, 54, nil, nil, nil, nil, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 207, nil,
- nil, nil, nil, 79, 72, 74, 75, 76, 77, nil,
- nil, nil, 73, 78, 62, 63, 64, nil, 51, nil,
- nil, nil, 56, 57, nil, nil, nil, 60, nil, 58,
- 59, 61, 246, 247, 65, 66, nil, nil, nil, nil,
- 245, 275, 279, 88, 87, 89, 90, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 41, nil, nil, 92,
- 91, 82, 50, 84, 83, 86, 85, 93, 94, nil,
- 80, 81, nil, 38, 39, 37, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 200, nil, nil, 204, nil,
- nil, 52, 53, nil, nil, 54, nil, nil, nil, nil,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 207,
- nil, nil, nil, nil, 79, 72, 74, 75, 76, 77,
- nil, nil, nil, 73, 78, 62, 63, 64, nil, 51,
- nil, nil, nil, 56, 57, nil, nil, nil, 60, nil,
- 58, 59, 61, 246, 247, 65, 66, nil, nil, nil,
- nil, 245, 275, 279, 88, 87, 89, 90, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 41, nil, nil,
- 92, 91, 82, 50, 84, 83, 86, 85, 93, 94,
- nil, 80, 81, nil, 38, 39, 37, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 200, nil, nil, 204,
- nil, nil, 52, 53, nil, nil, 54, nil, nil, nil,
- nil, nil, 40, nil, nil, nil, nil, nil, nil, nil,
- 207, nil, nil, nil, nil, 79, 72, 74, 75, 76,
- 77, nil, nil, nil, 73, 78, 62, 63, 64, nil,
- 51, nil, nil, nil, 56, 57, nil, nil, nil, 60,
- nil, 58, 59, 61, 246, 247, 65, 66, nil, nil,
- nil, nil, 245, 275, 279, 88, 87, 89, 90, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 41, nil,
- nil, 92, 91, 82, 50, 84, 83, 86, 85, 93,
- 94, nil, 80, 81, nil, 38, 39, 37, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 200, nil, nil,
- 204, nil, nil, 52, 53, nil, nil, 54, nil, nil,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 207, nil, nil, nil, nil, 79, 72, 74, 75,
- 76, 77, nil, nil, nil, 73, 78, 62, 63, 64,
- nil, 51, nil, nil, nil, 56, 57, nil, nil, nil,
- 60, nil, 58, 59, 61, 246, 247, 65, 66, nil,
- nil, nil, nil, 245, 275, 279, 88, 87, 89, 90,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 41,
- nil, nil, 92, 91, 82, 50, 84, 83, 86, 85,
- 93, 94, nil, 80, 81, nil, 38, 39, 37, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 200, nil,
- nil, 204, nil, nil, 52, 53, nil, nil, 54, nil,
- nil, nil, nil, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 207, nil, nil, nil, nil, 79, 72, 74,
- 75, 76, 77, nil, nil, nil, 73, 78, 62, 63,
- 64, nil, 51, nil, nil, nil, 56, 57, nil, nil,
- nil, 60, nil, 58, 59, 61, 246, 247, 65, 66,
- nil, nil, nil, nil, 245, 275, 279, 88, 87, 89,
- 90, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 41, nil, nil, 92, 91, 82, 50, 84, 83, 86,
- 85, 93, 94, nil, 80, 81, nil, 38, 39, 37,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 200,
- nil, nil, 204, nil, nil, 52, 53, nil, nil, 54,
- nil, nil, nil, nil, nil, 40, nil, nil, nil, nil,
- nil, nil, nil, 207, nil, nil, nil, nil, 79, 72,
- 74, 75, 76, 77, nil, nil, nil, 73, 78, 62,
- 63, 64, nil, 51, nil, nil, nil, 56, 57, nil,
- nil, nil, 60, nil, 58, 59, 61, 246, 247, 65,
- 66, nil, nil, nil, nil, 245, 275, 279, 88, 87,
- 89, 90, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 41, nil, nil, 92, 91, 82, 50, 84, 83,
- 86, 85, 93, 94, nil, 80, 81, nil, 38, 39,
- 37, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 200, nil, nil, 204, nil, nil, 52, 53, nil, nil,
- 54, nil, nil, nil, nil, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 207, nil, nil, nil, nil, 79,
- 72, 74, 75, 76, 77, nil, nil, nil, 73, 78,
- 62, 63, 64, nil, 51, nil, nil, nil, 56, 57,
- nil, nil, nil, 60, nil, 58, 59, 61, 246, 247,
- 65, 66, nil, nil, nil, nil, 245, 275, 279, 88,
- 87, 89, 90, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 41, nil, nil, 92, 91, 82, 50, 84,
- 83, 86, 85, 93, 94, nil, 80, 81, nil, 38,
- 39, 37, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 200, nil, nil, 204, nil, nil, 52, 53, nil,
- nil, 54, nil, nil, nil, nil, nil, 40, nil, nil,
- nil, nil, nil, nil, nil, 207, nil, nil, nil, nil,
- 79, 72, 74, 75, 76, 77, nil, nil, nil, 73,
- 78, 62, 63, 64, nil, 51, nil, nil, nil, 56,
- 57, nil, nil, nil, 60, nil, 58, 59, 61, 246,
- 247, 65, 66, nil, nil, nil, nil, 245, 275, 279,
- 88, 87, 89, 90, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, 92, 91, 82, 50,
- 84, 83, 86, 85, 93, 94, nil, 80, 81, nil,
- 38, 39, 37, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 200, nil, nil, 204, nil, nil, 52, 53,
- nil, nil, 54, nil, nil, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 207, nil, nil, nil,
- nil, 79, 72, 74, 75, 76, 77, nil, nil, nil,
- 73, 78, 62, 63, 64, nil, 51, nil, nil, nil,
- 56, 57, nil, nil, nil, 60, nil, 58, 59, 61,
- 246, 247, 65, 66, nil, nil, nil, nil, 245, 275,
- 279, 88, 87, 89, 90, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 92, 91, 82,
- 50, 84, 83, 86, 85, 93, 94, nil, 80, 81,
- nil, 38, 39, 37, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 200, nil, nil, 204, nil, nil, 52,
- 53, nil, nil, 54, nil, nil, nil, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 207, nil, nil,
- nil, nil, 79, 72, 74, 75, 76, 77, nil, nil,
- nil, 73, 78, 62, 63, 64, nil, 51, nil, nil,
- nil, 56, 57, nil, nil, nil, 60, nil, 58, 59,
- 61, 246, 247, 65, 66, nil, nil, nil, nil, 245,
- 275, 279, 88, 87, 89, 90, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 41, nil, nil, 92, 91,
- 82, 50, 84, 83, 86, 85, 93, 94, nil, 80,
- 81, nil, 38, 39, 37, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 200, nil, nil, 204, nil, nil,
- 52, 53, nil, nil, 54, nil, nil, nil, nil, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 207, nil,
- nil, nil, nil, 79, 72, 74, 75, 76, 77, nil,
- nil, nil, 73, 78, 62, 63, 64, nil, 51, nil,
- nil, nil, 56, 57, nil, nil, nil, 60, nil, 58,
- 59, 61, 246, 247, 65, 66, nil, nil, nil, nil,
- 245, 275, 279, 88, 87, 89, 90, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 41, nil, nil, 92,
- 91, 82, 50, 84, 83, 86, 85, 93, 94, nil,
- 80, 81, nil, 38, 39, 37, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 200, nil, nil, 204, nil,
- nil, 52, 53, nil, nil, 54, nil, nil, nil, nil,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 207,
- nil, nil, nil, nil, 79, 72, 74, 75, 76, 77,
- nil, nil, nil, 73, 78, 62, 63, 64, nil, 51,
- nil, nil, nil, 56, 57, nil, nil, nil, 60, nil,
- 58, 59, 61, 246, 247, 65, 66, nil, nil, nil,
- nil, 245, 275, 279, 88, 87, 89, 90, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 41, nil, nil,
- 92, 91, 82, 50, 84, 83, 86, 85, 93, 94,
- nil, 80, 81, nil, 38, 39, 37, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 200, nil, nil, 204,
- nil, nil, 52, 53, nil, nil, 54, nil, nil, nil,
- nil, nil, 40, nil, nil, nil, nil, nil, nil, nil,
- 207, nil, nil, nil, nil, 79, 72, 74, 75, 76,
- 77, nil, nil, nil, 73, 78, 62, 63, 64, nil,
- 51, nil, nil, nil, 56, 57, nil, nil, nil, 60,
- nil, 58, 59, 61, 246, 247, 65, 66, nil, nil,
- nil, nil, 245, 275, 279, 88, 87, 89, 90, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 41, nil,
- nil, 92, 91, 82, 50, 84, 83, 86, 85, 93,
- 94, nil, 80, 81, nil, 38, 39, 37, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 200, nil, nil,
- 204, nil, nil, 52, 53, nil, nil, 54, nil, nil,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 207, nil, nil, nil, nil, 79, 72, 74, 75,
- 76, 77, nil, nil, nil, 73, 78, 62, 63, 64,
- nil, 51, nil, nil, nil, 56, 57, nil, nil, nil,
- 60, nil, 58, 59, 61, 246, 247, 65, 66, nil,
- nil, nil, nil, 245, 275, 279, 88, 87, 89, 90,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 41,
- nil, nil, 92, 91, 82, 50, 84, 83, 86, 85,
- 93, 94, nil, 80, 81, nil, 38, 39, 37, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 200, nil,
- nil, 204, nil, nil, 52, 53, nil, nil, 54, nil,
- nil, nil, nil, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 207, nil, nil, nil, nil, 79, 72, 74,
- 75, 76, 77, nil, nil, nil, 73, 78, 62, 63,
- 64, nil, 51, nil, nil, nil, 56, 57, nil, nil,
- nil, 60, nil, 58, 59, 61, 246, 247, 65, 66,
- nil, nil, nil, nil, 245, 275, 279, 88, 87, 89,
- 90, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 41, nil, nil, 92, 91, 82, 50, 84, 83, 86,
- 85, 93, 94, nil, 80, 81, nil, 38, 39, 37,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 200,
- nil, nil, 204, nil, nil, 52, 53, nil, nil, 54,
- nil, nil, nil, nil, nil, 40, nil, nil, nil, nil,
- nil, nil, nil, 207, nil, nil, nil, nil, 79, 72,
- 74, 75, 76, 77, nil, nil, nil, 73, 78, 62,
- 63, 64, nil, 51, nil, nil, nil, 56, 57, nil,
- nil, nil, 60, nil, 58, 59, 61, 246, 247, 65,
- 66, nil, nil, nil, nil, 245, 275, 279, 88, 87,
- 89, 90, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 41, nil, nil, 92, 91, 82, 50, 84, 83,
- 86, 85, 93, 94, nil, 80, 81, nil, 38, 39,
- 37, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 200, nil, nil, 204, nil, nil, 52, 53, nil, nil,
- 54, nil, nil, nil, nil, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 207, nil, nil, nil, nil, 79,
- 72, 74, 75, 76, 77, nil, nil, nil, 73, 78,
- 62, 63, 64, nil, 51, nil, nil, nil, 56, 57,
- nil, nil, nil, 60, nil, 58, 59, 61, 246, 247,
- 65, 66, nil, nil, nil, nil, 245, 28, 27, 88,
- 87, 89, 90, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 41, nil, nil, 92, 91, 82, 50, 84,
- 83, 86, 85, 93, 94, nil, 80, 81, nil, 38,
- 39, 37, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 200, nil, nil, 204, nil, nil, 52, 53, nil,
- nil, 54, nil, 241, nil, 243, nil, 40, nil, nil,
- nil, nil, nil, nil, nil, 207, nil, nil, nil, nil,
- 79, 72, 74, 75, 76, 77, nil, nil, nil, 73,
- 78, 62, 63, 64, nil, 51, nil, nil, nil, 56,
- 57, nil, nil, nil, 60, nil, 58, 59, 61, 246,
- 247, 65, 66, nil, nil, nil, nil, 245, 28, 27,
- 88, 87, 89, 90, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, 92, 91, 82, 50,
- 84, 83, 86, 85, 93, 94, nil, 80, 81, nil,
- 38, 39, 37, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 200, nil, nil, 204, nil, nil, 445, 53,
- nil, nil, 54, nil, 241, nil, 243, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 207, nil, nil, nil,
- nil, 79, 72, 74, 75, 76, 77, nil, nil, nil,
- 73, 78, 62, 63, 64, nil, 51, nil, nil, nil,
- 56, 57, nil, nil, nil, 60, nil, 58, 59, 61,
- 246, 247, 65, 66, nil, nil, nil, nil, 245, 28,
- 27, 88, 87, 89, 90, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 92, 91, 82,
- 50, 84, 83, 86, 85, 93, 94, nil, 80, 81,
- nil, 38, 39, 37, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 200, nil, nil, 204, nil, 449, 52,
- 53, nil, nil, 54, nil, 241, nil, 243, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 207, nil, nil,
- nil, nil, 79, 72, 74, 75, 76, 77, nil, nil,
- nil, 73, 78, 62, 63, 64, nil, 51, nil, nil,
- nil, 56, 57, nil, nil, nil, 60, nil, 58, 59,
- 61, 246, 247, 65, 66, nil, nil, nil, nil, 245,
- 275, 279, 88, 87, 89, 90, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 276, nil, nil, 92, 91,
- 82, 50, 84, 83, 86, 85, 93, 94, nil, 80,
- 81, nil, nil, nil, 280, nil, 215, 219, 224, 225,
- 226, 221, 223, 231, 232, 227, 228, nil, 208, 209,
- nil, nil, 229, 230, 273, nil, nil, 204, nil, nil,
- 52, 53, nil, nil, 54, nil, nil, nil, 212, nil,
- 218, nil, 214, 213, 210, 211, 222, 220, 216, nil,
- 217, nil, nil, 79, 72, 74, 75, 76, 77, nil,
- nil, nil, 73, 78, 62, 63, 64, 233, 51, nil,
- nil, nil, 56, 57, nil, nil, nil, 60, nil, 58,
- 59, 61, 246, 247, 65, 66, nil, nil, nil, nil,
- 245, 275, 279, 88, 87, 89, 90, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 41, nil, nil, 92,
- 91, 82, 50, 84, 83, 86, 85, 93, 94, nil,
- 80, 81, nil, 38, 39, 37, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 200, nil, nil, 204, 469,
- nil, 52, 53, nil, nil, 54, nil, nil, nil, nil,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 207,
- nil, nil, nil, nil, 79, 72, 74, 75, 76, 77,
- nil, nil, nil, 73, 78, 62, 63, 64, nil, 51,
- nil, nil, nil, 56, 57, nil, nil, nil, 60, nil,
- 58, 59, 61, 246, 247, 65, 66, nil, nil, nil,
- nil, 245, 275, 279, 88, 87, 89, 90, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 41, nil, nil,
- 92, 91, 82, 50, 84, 83, 86, 85, 93, 94,
- nil, 80, 81, nil, 38, 39, 37, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 200, nil, nil, 204,
- nil, nil, 52, 53, nil, nil, 54, nil, nil, nil,
- nil, nil, 40, nil, nil, nil, nil, nil, nil, nil,
- 207, nil, nil, nil, nil, 79, 72, 74, 75, 76,
- 77, nil, nil, nil, 73, 78, 62, 63, 64, nil,
- 51, nil, nil, nil, 56, 57, nil, nil, nil, 60,
- nil, 58, 59, 61, 23, 24, 65, 66, nil, nil,
- nil, nil, 22, 28, 27, 88, 87, 89, 90, nil,
- nil, 17, nil, nil, nil, nil, nil, nil, 41, nil,
- nil, 92, 91, 82, 50, 84, 83, 86, 85, 93,
- 94, nil, 80, 81, nil, 38, 39, 37, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 200, nil, nil,
- 204, nil, nil, 52, 53, nil, nil, 54, nil, nil,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 18, nil, nil, nil, nil, 79, 72, 74, 75,
- 76, 77, nil, nil, nil, 73, 78, 62, 63, 64,
- nil, 51, nil, nil, nil, 56, 57, nil, nil, nil,
- 60, nil, 58, 59, 61, 23, 24, 65, 66, nil,
- nil, nil, nil, 22, 28, 27, 88, 87, 89, 90,
- nil, nil, 17, nil, nil, nil, nil, nil, nil, 41,
- nil, nil, 92, 91, 82, 50, 84, 83, 86, 85,
- 93, 94, nil, 80, 81, nil, 38, 39, 37, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 200, nil,
- nil, 204, nil, nil, 52, 53, nil, nil, 54, nil,
- nil, nil, nil, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 18, nil, nil, nil, nil, 79, 72, 74,
- 75, 76, 77, nil, nil, nil, 73, 78, 62, 63,
- 64, nil, 51, nil, nil, nil, 56, 57, nil, nil,
- nil, 60, nil, 58, 59, 61, 23, 24, 65, 66,
- nil, nil, nil, nil, 22, 28, 27, 88, 87, 89,
- 90, nil, nil, 17, nil, nil, nil, nil, nil, nil,
- 41, nil, nil, 92, 91, 82, 50, 84, 83, 86,
- 85, 93, 94, nil, 80, 81, nil, 38, 39, 37,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 200,
- nil, nil, 204, nil, nil, 52, 53, nil, nil, 54,
- nil, nil, nil, nil, nil, 40, nil, nil, nil, nil,
- nil, nil, nil, 18, nil, nil, nil, nil, 79, 72,
- 74, 75, 76, 77, nil, nil, nil, 73, 78, 62,
- 63, 64, nil, 51, nil, nil, nil, 56, 57, nil,
- nil, nil, 60, nil, 58, 59, 61, 23, 24, 65,
- 66, nil, nil, nil, nil, 22, 28, 27, 88, 87,
- 89, 90, nil, nil, 17, nil, nil, nil, nil, nil,
- nil, 41, nil, nil, 92, 91, 82, 50, 84, 83,
- 86, 85, 93, 94, nil, 80, 81, nil, 38, 39,
- 37, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 200, nil, nil, 204, nil, nil, 52, 53, nil, nil,
- 54, nil, nil, nil, nil, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 18, nil, nil, nil, nil, 79,
- 72, 74, 75, 76, 77, nil, nil, nil, 73, 78,
- 153, 164, 154, 177, 150, 170, 160, 159, 180, 181,
- 175, 158, 157, 152, 178, 182, 183, 162, 151, 165,
- 169, 171, 163, 156, nil, nil, 172, 179, 174, 173,
- 166, 176, 161, 149, 168, 167, nil, nil, nil, nil,
- nil, 148, 155, 146, 147, 144, 145, 109, 111, nil,
- nil, 110, nil, nil, nil, nil, nil, nil, nil, 139,
- 140, nil, 137, 121, 122, 123, nil, 126, 128, nil,
- nil, 124, nil, nil, nil, nil, 141, 142, 129, 130,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 134, 133, nil, 120, 138, 136, 135,
- 131, 132, 127, 125, 118, nil, 119, nil, nil, 143,
- 79, nil, nil, 62, 63, 64, nil, 51, nil, nil,
- 78, 56, 57, nil, nil, nil, 60, nil, 58, 59,
- 61, 246, 247, 65, 66, nil, nil, nil, nil, 245,
- 275, 279, 88, 87, 89, 90, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 41, nil, nil, 92, 91,
- 82, 50, 84, 83, 86, 85, 93, 94, nil, 80,
- 81, nil, 38, 39, 37, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 200, nil, nil, 204, nil, nil,
- 52, 53, nil, nil, 54, nil, nil, nil, nil, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 207, nil,
- nil, nil, nil, 79, 72, 74, 75, 76, 77, nil,
- nil, nil, 73, 78, -249, -249, -249, nil, -249, nil,
- nil, nil, -249, -249, nil, nil, nil, -249, nil, -249,
- -249, -249, -249, -249, -249, -249, nil, nil, nil, nil,
- -249, -249, -249, -249, -249, -249, -249, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, -249, nil, nil, -249,
- -249, -249, -249, -249, -249, -249, -249, -249, -249, nil,
- -249, -249, nil, -249, -249, -249, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, -249, nil, nil, -249, 254,
- nil, -249, -249, nil, nil, -249, nil, -249, nil, -249,
- nil, -249, nil, nil, nil, nil, nil, nil, nil, -249,
- nil, nil, nil, nil, -249, -249, -249, -249, -249, -249,
- nil, nil, nil, -249, -249, -478, -478, -478, nil, -478,
- nil, nil, nil, -478, -478, nil, nil, nil, -478, nil,
- -478, -478, -478, -478, -478, -478, -478, nil, nil, nil,
- nil, -478, -478, -478, -478, -478, -478, -478, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, -478, nil, nil,
- -478, -478, -478, -478, -478, -478, -478, -478, -478, -478,
- nil, -478, -478, nil, -478, -478, -478, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, -478, nil, nil, -478,
- -478, nil, -478, -478, nil, nil, -478, nil, -478, nil,
- -478, nil, -478, nil, nil, nil, nil, nil, nil, nil,
- -478, nil, nil, nil, nil, -478, -478, -478, -478, -478,
- -478, nil, nil, nil, -478, -478, -479, -479, -479, nil,
- -479, nil, nil, nil, -479, -479, nil, nil, nil, -479,
- nil, -479, -479, -479, -479, -479, -479, -479, nil, nil,
- nil, nil, -479, -479, -479, -479, -479, -479, -479, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, -479, nil,
- nil, -479, -479, -479, -479, -479, -479, -479, -479, -479,
- -479, nil, -479, -479, nil, -479, -479, -479, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, -479, nil, nil,
- -479, -479, nil, -479, -479, nil, nil, -479, nil, -479,
- nil, -479, nil, -479, nil, nil, nil, nil, nil, nil,
- nil, -479, nil, nil, nil, nil, -479, -479, -479, -479,
- -479, -479, nil, nil, nil, -479, -479, 62, 63, 64,
- nil, 51, nil, nil, nil, 56, 57, nil, nil, nil,
- 60, nil, 58, 59, 61, 246, 247, 65, 66, nil,
- nil, nil, nil, 245, 28, 27, 88, 87, 89, 90,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 41,
- nil, nil, 92, 91, 82, 50, 84, 83, 86, 85,
- 93, 94, nil, 80, 81, nil, 38, 39, 37, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 200, nil,
- nil, 204, nil, nil, 52, 53, nil, nil, 54, nil,
- 299, nil, nil, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 207, nil, nil, nil, nil, 79, 72, 74,
- 75, 76, 77, nil, nil, nil, 73, 78, 62, 63,
- 64, nil, 51, nil, nil, nil, 56, 57, nil, nil,
- nil, 60, nil, 58, 59, 61, 246, 247, 65, 66,
- nil, nil, nil, nil, 245, 275, 279, 88, 87, 89,
- 90, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 41, nil, nil, 92, 91, 82, 50, 84, 83, 86,
- 85, 93, 94, nil, 80, 81, nil, 38, 39, 37,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 200,
- nil, nil, 204, nil, nil, 52, 53, nil, nil, 54,
- nil, nil, nil, nil, nil, 40, nil, nil, nil, nil,
- nil, nil, nil, 207, nil, nil, nil, nil, 79, 72,
- 74, 75, 76, 77, nil, nil, nil, 73, 78, 62,
- 63, 64, nil, 51, nil, nil, nil, 56, 57, nil,
- nil, nil, 60, nil, 58, 59, 61, 246, 247, 65,
- 66, nil, nil, nil, nil, 245, 275, 279, 88, 87,
- 89, 90, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 41, nil, nil, 92, 91, 82, 50, 84, 83,
- 86, 85, 93, 94, nil, 80, 81, nil, 38, 39,
- 37, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 200, nil, nil, 204, nil, nil, 52, 53, nil, nil,
- 54, nil, nil, nil, nil, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 207, nil, nil, nil, nil, 79,
- 72, 74, 75, 76, 77, nil, nil, nil, 73, 78,
- 62, 63, 64, nil, 51, nil, nil, nil, 56, 57,
- nil, nil, nil, 60, nil, 58, 59, 61, 246, 247,
- 65, 66, nil, nil, nil, nil, 245, 275, 279, 88,
- 87, 89, 90, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 41, nil, nil, 92, 91, 82, 50, 84,
- 83, 86, 85, 93, 94, nil, 80, 81, nil, 38,
- 39, 37, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 200, nil, nil, 204, nil, nil, 52, 53, nil,
- nil, 54, nil, nil, nil, nil, nil, 40, nil, nil,
- nil, nil, nil, nil, nil, 207, nil, nil, nil, nil,
- 79, 72, 74, 75, 76, 77, nil, nil, nil, 73,
- 78, 62, 63, 64, nil, 51, nil, nil, nil, 56,
- 57, nil, nil, nil, 60, nil, 58, 59, 61, 246,
- 247, 65, 66, nil, nil, nil, nil, 245, 275, 279,
- 88, 87, 89, 90, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, 92, 91, 82, 50,
- 84, 83, 86, 85, 93, 94, nil, 80, 81, nil,
- 38, 39, 37, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 200, nil, nil, 204, nil, nil, 52, 53,
- nil, nil, 54, nil, 560, nil, 243, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 207, nil, nil, nil,
- nil, 79, 72, 74, 75, 76, 77, nil, nil, nil,
- 73, 78, 62, 63, 64, nil, 51, nil, nil, nil,
- 56, 57, nil, nil, nil, 60, nil, 58, 59, 61,
- 246, 247, 65, 66, nil, nil, nil, nil, 245, 275,
- 279, 88, 87, 89, 90, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 92, 91, 82,
- 50, 84, 83, 86, 85, 93, 94, nil, 80, 81,
- nil, 38, 39, 37, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 200, nil, nil, 204, nil, nil, 52,
- 53, nil, nil, 54, nil, 564, nil, 243, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 207, nil, nil,
- nil, nil, 79, 72, 74, 75, 76, 77, nil, nil,
- nil, 73, 78, 62, 63, 64, nil, 51, nil, nil,
- nil, 56, 57, nil, nil, nil, 60, nil, 58, 59,
- 61, 246, 247, 65, 66, nil, nil, nil, nil, 245,
- 275, 279, 88, 87, 89, 90, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 41, nil, nil, 92, 91,
- 82, 50, 84, 83, 86, 85, 93, 94, nil, 80,
- 81, nil, 38, 39, 37, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 200, nil, nil, 204, nil, nil,
- 52, 53, nil, nil, 54, nil, nil, nil, nil, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 207, nil,
- nil, nil, nil, 79, 72, 74, 75, 76, 77, nil,
- nil, nil, 73, 78, 62, 63, 64, nil, 51, nil,
- nil, nil, 56, 57, nil, nil, nil, 60, nil, 58,
- 59, 61, 23, 24, 65, 66, nil, nil, nil, nil,
- 22, 28, 27, 88, 87, 89, 90, nil, nil, 17,
- nil, nil, nil, nil, nil, nil, 41, nil, nil, 92,
- 91, 82, 50, 84, 83, 86, 85, 93, 94, nil,
- 80, 81, nil, 38, 39, 37, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 200, nil, nil, 204, nil,
- nil, 52, 53, nil, nil, 54, nil, 584, nil, 243,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 18,
- nil, nil, nil, nil, 79, 72, 74, 75, 76, 77,
- nil, nil, nil, 73, 78, 62, 63, 64, nil, 51,
- nil, nil, nil, 56, 57, nil, nil, nil, 60, nil,
- 58, 59, 61, 246, 247, 65, 66, nil, nil, nil,
- nil, 245, 28, 27, 88, 87, 89, 90, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 41, nil, nil,
- 92, 91, 82, 50, 84, 83, 86, 85, 93, 94,
- nil, 80, 81, nil, 38, 39, 37, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 200, nil, nil, 204,
- nil, nil, 52, 53, nil, nil, 54, nil, 299, nil,
- nil, nil, 40, nil, nil, nil, nil, nil, nil, nil,
- 207, nil, nil, nil, nil, 79, 72, 74, 75, 76,
- 77, nil, nil, nil, 73, 78, 62, 63, 64, nil,
- 51, nil, nil, nil, 56, 57, nil, nil, nil, 60,
- nil, 58, 59, 61, 246, 247, 65, 66, nil, nil,
- nil, nil, 245, 275, 279, 88, 87, 89, 90, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 41, nil,
- nil, 92, 91, 82, 50, 84, 83, 86, 85, 93,
- 94, nil, 80, 81, nil, 38, 39, 37, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 200, nil, nil,
- 204, nil, nil, 52, 53, nil, nil, 54, nil, nil,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 207, nil, nil, nil, nil, 79, 72, 74, 75,
- 76, 77, nil, nil, nil, 73, 78, 62, 63, 64,
- nil, 51, nil, nil, nil, 56, 57, nil, nil, nil,
- 60, nil, 58, 59, 61, 246, 247, 65, 66, nil,
- nil, nil, nil, 245, 275, 279, 88, 87, 89, 90,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 41,
- nil, nil, 92, 91, 82, 50, 84, 83, 86, 85,
- 93, 94, nil, 80, 81, nil, 38, 39, 37, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 200, nil,
- nil, 204, nil, nil, 52, 53, nil, nil, 54, nil,
- nil, nil, nil, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 207, nil, nil, nil, nil, 79, 72, 74,
- 75, 76, 77, nil, nil, nil, 73, 78, 62, 63,
- 64, nil, 51, nil, nil, nil, 56, 57, nil, nil,
- nil, 60, nil, 58, 59, 61, 246, 247, 65, 66,
- nil, nil, nil, nil, 245, 275, 279, 88, 87, 89,
- 90, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 41, nil, nil, 92, 91, 82, 50, 84, 83, 86,
- 85, 93, 94, nil, 80, 81, nil, 38, 39, 37,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 200,
- nil, nil, 204, nil, nil, 52, 53, nil, nil, 54,
- nil, nil, nil, nil, nil, 40, nil, nil, nil, nil,
- nil, nil, nil, 207, nil, nil, nil, nil, 79, 72,
- 74, 75, 76, 77, nil, nil, nil, 73, 78, 62,
- 63, 64, nil, 51, nil, nil, nil, 56, 57, nil,
- nil, nil, 60, nil, 58, 59, 61, 23, 24, 65,
- 66, nil, nil, nil, nil, 22, 28, 27, 88, 87,
- 89, 90, nil, nil, 17, nil, nil, nil, nil, nil,
- nil, 41, nil, nil, 92, 91, 82, 50, 84, 83,
- 86, 85, 93, 94, nil, 80, 81, nil, 38, 39,
- 37, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 200, nil, nil, 204, nil, nil, 52, 53, nil, nil,
- 54, nil, nil, nil, nil, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 18, nil, nil, nil, nil, 79,
- 72, 74, 75, 76, 77, nil, nil, nil, 73, 78,
- 62, 63, 64, nil, 51, nil, nil, nil, 56, 57,
- nil, nil, nil, 60, nil, 58, 59, 61, 246, 247,
- 65, 66, nil, nil, nil, nil, 245, 275, 279, 88,
- 87, 89, 90, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 41, nil, nil, 92, 91, 82, 50, 84,
- 83, 86, 85, 93, 94, nil, 80, 81, nil, 38,
- 39, 37, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 200, nil, nil, 204, nil, nil, 52, 53, nil,
- nil, 54, nil, 369, nil, nil, nil, 40, nil, nil,
- nil, nil, nil, nil, nil, 207, nil, nil, nil, nil,
- 79, 72, 74, 75, 76, 77, nil, nil, nil, 73,
- 78, 62, 63, 64, nil, 51, nil, nil, nil, 56,
- 57, nil, nil, nil, 60, nil, 58, 59, 61, 246,
- 247, 65, 66, nil, nil, nil, nil, 245, 275, 279,
- 88, 87, 89, 90, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, 92, 91, 82, 50,
- 84, 83, 86, 85, 93, 94, nil, 80, 81, nil,
- 38, 39, 37, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 200, nil, nil, 204, nil, nil, 52, 53,
- nil, nil, 54, nil, 612, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 207, nil, nil, nil,
- nil, 79, 72, 74, 75, 76, 77, nil, nil, nil,
- 73, 78, 62, 63, 64, nil, 51, nil, nil, nil,
- 56, 57, nil, nil, nil, 60, nil, 58, 59, 61,
- 246, 247, 65, 66, nil, nil, nil, nil, 245, 275,
- 279, 88, 87, 89, 90, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 92, 91, 82,
- 50, 84, 83, 86, 85, 93, 94, nil, 80, 81,
- nil, 38, 39, 37, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 200, nil, nil, 204, nil, nil, 52,
- 53, nil, nil, 54, nil, nil, nil, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 207, nil, nil,
- nil, nil, 79, 72, 74, 75, 76, 77, nil, nil,
- nil, 73, 78, 62, 63, 64, nil, 51, nil, nil,
- nil, 56, 57, nil, nil, nil, 60, nil, 58, 59,
- 61, 246, 247, 65, 66, nil, nil, nil, nil, 245,
- 275, 279, 88, 87, 89, 90, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 41, nil, nil, 92, 91,
- 82, 50, 84, 83, 86, 85, 93, 94, nil, 80,
- 81, nil, 38, 39, 37, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 200, nil, nil, 204, nil, nil,
- 52, 53, nil, nil, 54, nil, nil, nil, nil, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 207, nil,
- nil, nil, nil, 79, 72, 74, 75, 76, 77, nil,
- nil, nil, 73, 78, 62, 63, 64, nil, 51, nil,
- nil, nil, 56, 57, nil, nil, nil, 60, nil, 58,
- 59, 61, 246, 247, 65, 66, nil, nil, nil, nil,
- 245, 275, 279, 88, 87, 89, 90, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 41, nil, nil, 92,
- 91, 82, 50, 84, 83, 86, 85, 93, 94, nil,
- 80, 81, nil, 38, 39, 37, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 200, nil, nil, 204, nil,
- nil, 52, 53, nil, nil, 54, nil, 628, nil, nil,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 207,
- nil, nil, nil, nil, 79, 72, 74, 75, 76, 77,
- nil, nil, nil, 73, 78, 62, 63, 64, nil, 51,
- nil, nil, nil, 56, 57, nil, nil, nil, 60, nil,
- 58, 59, 61, 246, 247, 65, 66, nil, nil, nil,
- nil, 245, 28, 27, 88, 87, 89, 90, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 41, nil, nil,
- 92, 91, 82, 50, 84, 83, 86, 85, 93, 94,
- nil, 80, 81, nil, 38, 39, 37, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 200, nil, nil, 204,
- nil, nil, 52, 53, nil, nil, 54, nil, 299, nil,
- nil, nil, 40, nil, nil, nil, nil, nil, nil, nil,
- 207, nil, nil, nil, nil, 79, 72, 74, 75, 76,
- 77, nil, nil, nil, 73, 78, 62, 63, 64, nil,
- 51, nil, nil, nil, 56, 57, nil, nil, nil, 60,
- nil, 58, 59, 61, 246, 247, 65, 66, nil, nil,
- nil, nil, 245, 28, 27, 88, 87, 89, 90, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 41, nil,
- nil, 92, 91, 82, 50, 84, 83, 86, 85, 93,
- 94, nil, 80, 81, nil, 38, 39, 37, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 200, nil, nil,
- 204, nil, nil, 52, 53, nil, nil, 54, nil, 299,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 207, nil, nil, nil, nil, 79, 72, 74, 75,
- 76, 77, nil, nil, nil, 73, 78, 62, 63, 64,
- nil, 51, nil, nil, nil, 56, 57, nil, nil, nil,
- 60, nil, 58, 59, 61, 23, 24, 65, 66, nil,
- nil, nil, nil, 22, 28, 27, 88, 87, 89, 90,
- nil, nil, 17, nil, nil, nil, nil, nil, nil, 41,
- nil, nil, 92, 91, 82, 50, 84, 83, 86, 85,
- 93, 94, nil, 80, 81, nil, 38, 39, 37, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 200, nil,
- nil, 204, nil, nil, 52, 53, nil, nil, 54, nil,
- nil, nil, nil, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 18, nil, nil, nil, nil, 79, 72, 74,
- 75, 76, 77, nil, nil, nil, 73, 78, 153, 164,
- 154, 177, 150, 170, 160, 159, 180, 181, 175, 158,
- 157, 152, 178, 182, 183, 162, 151, 165, 169, 171,
- 163, 156, nil, nil, 172, 179, 174, 173, 166, 176,
- 161, 149, 168, 167, nil, nil, nil, nil, nil, 148,
- 155, 146, 147, 144, 145, 109, 111, nil, nil, 110,
- nil, nil, nil, nil, nil, nil, nil, 139, 140, nil,
- 137, 121, 122, 123, nil, 126, 128, nil, nil, 124,
- nil, nil, nil, nil, 141, 142, 129, 130, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 134, 133, nil, 120, 138, 136, 135, 131, 132,
- 127, 125, 118, nil, 119, nil, nil, 143, 79, nil,
- nil, 62, 63, 64, nil, 51, nil, nil, 78, 56,
- 57, nil, nil, nil, 60, nil, 58, 59, 61, 246,
- 247, 65, 66, nil, nil, nil, nil, 245, 275, 279,
- 88, 87, 89, 90, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, 92, 91, 82, 50,
- 84, 83, 86, 85, 93, 94, nil, 80, 81, nil,
- 38, 39, 37, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 200, nil, nil, 204, nil, nil, 52, 53,
- nil, nil, 54, nil, nil, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 207, nil, nil, nil,
- nil, 79, 72, 74, 75, 76, 77, nil, nil, nil,
- 73, 78, 62, 63, 64, nil, 51, nil, nil, nil,
- 56, 57, nil, nil, nil, 60, nil, 58, 59, 61,
- 246, 247, 65, 66, nil, nil, nil, nil, 245, 275,
- 279, 88, 87, 89, 90, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 92, 91, 82,
- 50, 84, 83, 86, 85, 93, 94, nil, 80, 81,
- nil, 38, 39, 37, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 200, nil, nil, 204, nil, nil, 52,
- 53, nil, nil, 54, nil, 679, nil, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 207, nil, nil,
- nil, nil, 79, 72, 74, 75, 76, 77, nil, nil,
- nil, 73, 78, 62, 63, 64, nil, 51, nil, nil,
- nil, 56, 57, nil, nil, nil, 60, nil, 58, 59,
- 61, 23, 24, 65, 66, nil, nil, nil, nil, 22,
- 28, 27, 88, 87, 89, 90, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 41, nil, nil, 92, 91,
- 82, 50, 84, 83, 86, 85, 93, 94, nil, 80,
- 81, nil, 38, 39, 37, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 200, nil, nil, 204, nil, nil,
- 52, 53, nil, nil, 54, nil, nil, nil, nil, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 207, nil,
- nil, nil, nil, 79, 72, 74, 75, 76, 77, nil,
- nil, nil, 73, 78, 62, 63, 64, nil, 51, nil,
- nil, nil, 56, 57, nil, nil, nil, 60, nil, 58,
- 59, 61, 23, 24, 65, 66, nil, nil, nil, nil,
- 22, 28, 27, 88, 87, 89, 90, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 41, nil, nil, 92,
- 91, 82, 50, 84, 83, 86, 85, 93, 94, nil,
- 80, 81, nil, 38, 39, 37, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 200, nil, nil, 204, nil,
- nil, 52, 53, nil, nil, 54, nil, nil, nil, nil,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 207,
- nil, nil, nil, nil, 79, 72, 74, 75, 76, 77,
- nil, nil, nil, 73, 78, 62, 63, 64, nil, 51,
- nil, nil, nil, 56, 57, nil, nil, nil, 60, nil,
- 58, 59, 61, 23, 24, 65, 66, nil, nil, nil,
- nil, 22, 28, 27, 88, 87, 89, 90, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 41, nil, nil,
- 92, 91, 82, 50, 84, 83, 86, 85, 93, 94,
- nil, 80, 81, nil, 38, 39, 37, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 200, nil, nil, 204,
- nil, nil, 52, 53, nil, nil, 54, nil, nil, nil,
- nil, nil, 40, nil, nil, nil, nil, nil, nil, nil,
- 207, nil, nil, nil, nil, 79, 72, 74, 75, 76,
- 77, nil, nil, nil, 73, 78, 62, 63, 64, nil,
- 51, nil, nil, nil, 56, 57, nil, nil, nil, 60,
- nil, 58, 59, 61, 246, 247, 65, 66, nil, nil,
- nil, nil, 245, 275, 279, 88, 87, 89, 90, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 41, nil,
- nil, 92, 91, 82, 50, 84, 83, 86, 85, 93,
- 94, nil, 80, 81, nil, 38, 39, 37, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 200, nil, nil,
- 204, nil, nil, 52, 53, nil, nil, 54, nil, nil,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 207, nil, nil, nil, nil, 79, 72, 74, 75,
- 76, 77, nil, nil, nil, 73, 78, 62, 63, 64,
- nil, 51, nil, nil, nil, 56, 57, nil, nil, nil,
- 60, nil, 58, 59, 61, 246, 247, 65, 66, nil,
- nil, nil, nil, 245, 275, 279, 88, 87, 89, 90,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 41,
- nil, nil, 92, 91, 82, 50, 84, 83, 86, 85,
- 93, 94, nil, 80, 81, nil, 38, 39, 37, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 200, nil,
- nil, 204, nil, nil, 52, 53, nil, nil, 54, nil,
- nil, nil, nil, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 207, nil, nil, nil, nil, 79, 72, 74,
- 75, 76, 77, nil, nil, nil, 73, 78, 62, 63,
- 64, nil, 51, nil, nil, nil, 56, 57, nil, nil,
- nil, 60, nil, 58, 59, 61, 246, 247, 65, 66,
- nil, nil, nil, nil, 245, 275, 279, 88, 87, 89,
- 90, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 276, nil, nil, 92, 91, 82, 50, 84, 83, 86,
- 85, 93, 94, nil, 80, 81, nil, nil, nil, 280,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 273,
- nil, nil, 270, nil, nil, 52, 53, nil, nil, 54,
- nil, 697, nil, 698, nil, nil, nil, nil, nil, nil,
- nil, nil, 699, nil, nil, nil, nil, nil, 79, 72,
- 74, 75, 76, 77, nil, nil, nil, 73, 78, 62,
- 63, 64, nil, 51, nil, nil, nil, 56, 57, nil,
- nil, nil, 60, nil, 58, 59, 61, 246, 247, 65,
- 66, nil, nil, nil, nil, 245, 275, 279, 88, 87,
- 89, 90, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 41, nil, nil, 92, 91, 82, 50, 84, 83,
- 86, 85, 93, 94, nil, 80, 81, nil, 38, 39,
- 37, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 200, nil, nil, 204, nil, nil, 52, 53, nil, nil,
- 54, nil, nil, nil, nil, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 207, nil, nil, nil, nil, 79,
- 72, 74, 75, 76, 77, nil, nil, nil, 73, 78,
- 62, 63, 64, nil, 51, nil, nil, nil, 56, 57,
- nil, nil, nil, 60, nil, 58, 59, 61, 246, 247,
- 65, 66, nil, nil, nil, nil, 245, 275, 279, 88,
- 87, 89, 90, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 41, nil, nil, 92, 91, 82, 50, 84,
- 83, 86, 85, 93, 94, nil, 80, 81, nil, 38,
- 39, 37, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 200, nil, nil, 204, nil, nil, 52, 53, nil,
- nil, 54, nil, nil, nil, nil, nil, 40, nil, nil,
- nil, nil, nil, nil, nil, 207, nil, nil, nil, nil,
- 79, 72, 74, 75, 76, 77, nil, nil, nil, 73,
- 78, 62, 63, 64, nil, 51, nil, nil, nil, 56,
- 57, nil, nil, nil, 60, nil, 58, 59, 61, 246,
- 247, 65, 66, nil, nil, nil, nil, 245, 28, 27,
- 88, 87, 89, 90, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, 92, 91, 82, 50,
- 84, 83, 86, 85, 93, 94, nil, 80, 81, nil,
- 38, 39, 37, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 200, nil, nil, 204, nil, nil, 52, 53,
- nil, nil, 54, nil, 560, nil, 243, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 207, nil, nil, nil,
- nil, 79, 72, 74, 75, 76, 77, nil, nil, nil,
- 73, 78, 62, 63, 64, nil, 51, nil, nil, nil,
- 56, 57, nil, nil, nil, 60, nil, 58, 59, 61,
- 246, 247, 65, 66, nil, nil, nil, nil, 245, 275,
- 279, 88, 87, 89, 90, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 92, 91, 82,
- 50, 84, 83, 86, 85, 93, 94, nil, 80, 81,
- nil, 38, 39, 37, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 200, nil, nil, 204, nil, nil, 52,
- 53, nil, nil, 54, nil, nil, nil, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 207, nil, nil,
- nil, nil, 79, 72, 74, 75, 76, 77, nil, nil,
- nil, 73, 78, 62, 63, 64, nil, 51, nil, nil,
- nil, 56, 57, nil, nil, nil, 60, nil, 58, 59,
- 61, 246, 247, 65, 66, nil, nil, nil, nil, 245,
- 275, 279, 88, 87, 89, 90, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 41, nil, nil, 92, 91,
- 82, 50, 84, 83, 86, 85, 93, 94, nil, 80,
- 81, nil, 38, 39, 37, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 200, nil, nil, 204, nil, nil,
- 52, 53, nil, nil, 54, nil, nil, nil, nil, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 207, nil,
- nil, nil, nil, 79, 72, 74, 75, 76, 77, nil,
- nil, nil, 73, 78, 62, 63, 64, nil, 51, nil,
- nil, nil, 56, 57, nil, nil, nil, 60, nil, 58,
- 59, 61, 246, 247, 65, 66, nil, nil, nil, nil,
- 245, 275, 279, 88, 87, 89, 90, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 41, nil, nil, 92,
- 91, 82, 50, 84, 83, 86, 85, 93, 94, nil,
- 80, 81, nil, 38, 39, 37, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 200, nil, nil, 204, nil,
- nil, 52, 53, nil, nil, 54, nil, nil, nil, nil,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 207,
- nil, nil, nil, nil, 79, 72, 74, 75, 76, 77,
- nil, nil, nil, 73, 78, 62, 63, 64, nil, 51,
- nil, nil, nil, 56, 57, nil, nil, nil, 60, nil,
- 58, 59, 61, 23, 24, 65, 66, nil, nil, nil,
- nil, 22, 28, 27, 88, 87, 89, 90, nil, nil,
- 17, nil, nil, nil, nil, nil, nil, 41, nil, nil,
- 92, 91, 82, 50, 84, 83, 86, 85, 93, 94,
- nil, 80, 81, nil, 38, 39, 37, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 200, nil, nil, 204,
- nil, nil, 52, 53, nil, nil, 54, nil, nil, nil,
- nil, nil, 40, nil, nil, nil, nil, nil, nil, nil,
- 18, nil, nil, nil, nil, 79, 72, 74, 75, 76,
- 77, nil, nil, nil, 73, 78, 62, 63, 64, nil,
- 51, nil, nil, nil, 56, 57, nil, nil, nil, 60,
- nil, 58, 59, 61, 246, 247, 65, 66, nil, nil,
- nil, nil, 245, 275, 279, 88, 87, 89, 90, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 41, nil,
- nil, 92, 91, 82, 50, 84, 83, 86, 85, 93,
- 94, nil, 80, 81, nil, 38, 39, 37, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 200, nil, nil,
- 204, nil, nil, 52, 53, nil, nil, 54, nil, nil,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 207, nil, nil, nil, nil, 79, 72, 74, 75,
- 76, 77, nil, nil, nil, 73, 78, 62, 63, 64,
- nil, 51, nil, nil, nil, 56, 57, nil, nil, nil,
- 60, nil, 58, 59, 61, 23, 24, 65, 66, nil,
- nil, nil, nil, 22, 28, 27, 88, 87, 89, 90,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 41,
- nil, nil, 92, 91, 82, 50, 84, 83, 86, 85,
- 93, 94, nil, 80, 81, nil, 38, 39, 37, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 200, nil,
- nil, 204, nil, nil, 52, 53, nil, nil, 54, nil,
- nil, nil, nil, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 207, nil, nil, nil, nil, 79, 72, 74,
- 75, 76, 77, nil, nil, nil, 73, 78, 62, 63,
- 64, nil, 51, nil, nil, nil, 56, 57, nil, nil,
- nil, 60, nil, 58, 59, 61, 246, 247, 65, 66,
- nil, nil, nil, nil, 245, 275, 279, 88, 87, 89,
- 90, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 41, nil, nil, 92, 91, 82, 50, 84, 83, 86,
- 85, 93, 94, nil, 80, 81, nil, 38, 39, 37,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 200,
- nil, nil, 204, nil, nil, 52, 53, nil, nil, 54,
- nil, nil, nil, nil, nil, 40, nil, nil, nil, nil,
- nil, nil, nil, 207, nil, nil, nil, nil, 79, 72,
- 74, 75, 76, 77, nil, nil, nil, 73, 78, 62,
- 63, 64, nil, 51, nil, nil, nil, 56, 57, nil,
- nil, nil, 60, nil, 58, 59, 61, 246, 247, 65,
- 66, nil, nil, nil, nil, 245, 275, 279, 88, 87,
- 89, 90, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 41, nil, nil, 92, 91, 82, 50, 84, 83,
- 86, 85, 93, 94, nil, 80, 81, nil, 38, 39,
- 37, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 200, nil, nil, 204, nil, nil, 52, 53, nil, nil,
- 54, nil, nil, nil, nil, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 207, nil, nil, nil, nil, 79,
- 72, 74, 75, 76, 77, nil, nil, nil, 73, 78,
- 62, 63, 64, nil, 51, nil, nil, nil, 56, 57,
- nil, nil, nil, 60, nil, 58, 59, 61, 246, 247,
- 65, 66, nil, nil, nil, nil, 245, 275, 279, 88,
- 87, 89, 90, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 41, nil, nil, 92, 91, 82, 50, 84,
- 83, 86, 85, 93, 94, nil, 80, 81, nil, 38,
- 39, 37, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 200, nil, nil, 204, nil, nil, 52, 53, nil,
- nil, 54, nil, nil, nil, nil, nil, 40, nil, nil,
- nil, nil, nil, nil, nil, 207, nil, nil, nil, nil,
- 79, 72, 74, 75, 76, 77, nil, nil, nil, 73,
- 78, 62, 63, 64, nil, 51, nil, nil, nil, 56,
- 57, nil, nil, nil, 60, nil, 58, 59, 61, 246,
- 247, 65, 66, nil, nil, nil, nil, 245, 275, 279,
- 88, 87, 89, 90, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 276, nil, nil, 92, 91, 82, 50,
- 84, 83, 86, 85, 93, 94, nil, 80, 81, nil,
- nil, nil, 280, nil, 215, 219, 224, 225, 226, 221,
- 223, 231, 232, 227, 228, nil, 208, 209, nil, nil,
- 229, 230, 772, nil, nil, 204, nil, nil, 52, 53,
- nil, nil, 54, nil, nil, nil, 212, nil, 218, nil,
- 214, 213, 210, 211, 222, 220, 216, nil, 217, nil,
- nil, 79, 72, 74, 75, 76, 77, nil, nil, nil,
- 73, 78, 62, 63, 64, 233, 51, nil, nil, nil,
- 56, 57, nil, nil, nil, 60, nil, 58, 59, 61,
- 246, 247, 65, 66, nil, nil, nil, nil, 245, 275,
- 279, 88, 87, 89, 90, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 92, 91, 82,
- 50, 84, 83, 86, 85, 93, 94, nil, 80, 81,
- nil, 38, 39, 37, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 200, nil, nil, 204, nil, nil, 52,
- 53, nil, nil, 54, nil, 780, nil, 243, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 207, nil, nil,
- nil, nil, 79, 72, 74, 75, 76, 77, nil, nil,
- nil, 73, 78, 62, 63, 64, nil, 51, nil, nil,
- nil, 56, 57, nil, nil, nil, 60, nil, 58, 59,
- 61, 246, 247, 65, 66, nil, nil, nil, nil, 245,
- 275, 279, 88, 87, 89, 90, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 41, nil, nil, 92, 91,
- 82, 50, 84, 83, 86, 85, 93, 94, nil, 80,
- 81, nil, 38, 39, 37, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 200, nil, nil, 204, nil, nil,
- 52, 53, nil, nil, 54, nil, 786, nil, 243, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 207, nil,
- nil, nil, nil, 79, 72, 74, 75, 76, 77, nil,
- nil, nil, 73, 78, 62, 63, 64, nil, 51, nil,
- nil, nil, 56, 57, nil, nil, nil, 60, nil, 58,
- 59, 61, 246, 247, 65, 66, nil, nil, nil, nil,
- 245, 275, 279, 88, 87, 89, 90, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 41, nil, nil, 92,
- 91, 82, 50, 84, 83, 86, 85, 93, 94, nil,
- 80, 81, nil, 38, 39, 37, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 200, nil, nil, 204, nil,
- nil, 52, 53, nil, nil, 54, nil, 788, nil, 243,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 207,
- nil, nil, nil, nil, 79, 72, 74, 75, 76, 77,
- nil, nil, nil, 73, 78, 62, 63, 64, nil, 51,
- nil, nil, nil, 56, 57, nil, nil, nil, 60, nil,
- 58, 59, 61, 246, 247, 65, 66, nil, nil, nil,
- nil, 245, 275, 279, 88, 87, 89, 90, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 276, nil, nil,
- 92, 91, 82, 50, 84, 83, 86, 85, 93, 94,
- nil, 80, 81, nil, nil, nil, 280, nil, 215, 219,
- 224, 225, 226, 221, 223, 231, 232, 227, 228, nil,
- 208, 209, nil, nil, 229, 230, 772, nil, nil, 204,
- nil, nil, 52, 53, nil, nil, 54, nil, nil, nil,
- 212, nil, 218, nil, 214, 213, 210, 211, 222, 220,
- 216, nil, 217, nil, nil, 79, 72, 74, 75, 76,
- 77, nil, nil, nil, 73, 78, 62, 63, 64, 233,
- 51, nil, nil, nil, 56, 57, nil, nil, nil, 60,
- nil, 58, 59, 61, 23, 24, 65, 66, nil, nil,
- nil, nil, 22, 28, 27, 88, 87, 89, 90, nil,
- nil, 17, nil, nil, nil, nil, nil, nil, 41, nil,
- nil, 92, 91, 82, 50, 84, 83, 86, 85, 93,
- 94, nil, 80, 81, nil, 38, 39, 37, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 200, nil, nil,
- 204, nil, nil, 52, 53, nil, nil, 54, nil, nil,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 18, nil, nil, nil, nil, 79, 72, 74, 75,
- 76, 77, nil, nil, nil, 73, 78, 62, 63, 64,
- nil, 51, nil, nil, nil, 56, 57, nil, nil, nil,
- 60, nil, 58, 59, 61, 246, 247, 65, 66, nil,
- nil, nil, nil, 245, 275, 279, 88, 87, 89, 90,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 41,
- nil, nil, 92, 91, 82, 50, 84, 83, 86, 85,
- 93, 94, nil, 80, 81, nil, 38, 39, 37, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 200, nil,
- nil, 204, nil, nil, 52, 53, nil, nil, 54, nil,
- 802, nil, nil, nil, 40, nil, nil, nil, nil, nil,
- nil, nil, 207, nil, nil, nil, nil, 79, 72, 74,
- 75, 76, 77, nil, nil, nil, 73, 78, 62, 63,
- 64, nil, 51, nil, nil, nil, 56, 57, nil, nil,
- nil, 60, nil, 58, 59, 61, 246, 247, 65, 66,
- nil, nil, nil, nil, 245, 275, 279, 88, 87, 89,
- 90, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 41, nil, nil, 92, 91, 82, 50, 84, 83, 86,
- 85, 93, 94, nil, 80, 81, nil, 38, 39, 37,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 200,
- nil, nil, 204, nil, nil, 52, 53, nil, nil, 54,
- nil, nil, nil, nil, nil, 40, nil, nil, nil, nil,
- nil, nil, nil, 207, nil, nil, nil, nil, 79, 72,
- 74, 75, 76, 77, nil, nil, nil, 73, 78, 62,
- 63, 64, nil, 51, nil, nil, nil, 56, 57, nil,
- nil, nil, 60, nil, 58, 59, 61, 246, 247, 65,
- 66, nil, nil, nil, nil, 245, 275, 279, 88, 87,
- 89, 90, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 41, nil, nil, 92, 91, 82, 50, 84, 83,
- 86, 85, 93, 94, nil, 80, 81, nil, 38, 39,
- 37, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 200, nil, nil, 204, nil, nil, 52, 53, nil, nil,
- 54, nil, nil, nil, nil, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 207, nil, nil, nil, nil, 79,
- 72, 74, 75, 76, 77, nil, nil, nil, 73, 78,
- 62, 63, 64, nil, 51, nil, nil, nil, 56, 57,
- nil, nil, nil, 60, nil, 58, 59, 61, 246, 247,
- 65, 66, nil, nil, nil, nil, 245, 275, 279, 88,
- 87, 89, 90, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 276, nil, nil, 92, 91, 82, 50, 84,
- 83, 86, 85, 93, 94, nil, 80, 81, nil, nil,
- nil, 280, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 273, nil, nil, 270, nil, nil, 52, 53, nil,
- nil, 54, nil, 821, nil, 820, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 79, 72, 74, 75, 76, 77, nil, nil, nil, 73,
- 78, 62, 63, 64, nil, 51, nil, nil, nil, 56,
- 57, nil, nil, nil, 60, nil, 58, 59, 61, 246,
- 247, 65, 66, nil, nil, nil, nil, 245, 275, 279,
- 88, 87, 89, 90, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, 92, 91, 82, 50,
- 84, 83, 86, 85, 93, 94, nil, 80, 81, nil,
- 38, 39, 37, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 200, nil, nil, 204, nil, nil, 52, 53,
- nil, nil, 54, nil, nil, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 207, nil, nil, nil,
- nil, 79, 72, 74, 75, 76, 77, nil, nil, nil,
- 73, 78, 62, 63, 64, nil, 51, nil, nil, nil,
- 56, 57, nil, nil, nil, 60, nil, 58, 59, 61,
- 246, 247, 65, 66, nil, nil, nil, nil, 245, 275,
- 279, 88, 87, 89, 90, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 92, 91, 82,
- 50, 84, 83, 86, 85, 93, 94, nil, 80, 81,
- nil, 38, 39, 37, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 200, nil, nil, 204, nil, nil, 52,
- 53, nil, nil, 54, nil, nil, nil, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 207, nil, nil,
- nil, nil, 79, 72, 74, 75, 76, 77, nil, nil,
- nil, 73, 78, 62, 63, 64, nil, 51, nil, nil,
- nil, 56, 57, nil, nil, nil, 60, nil, 58, 59,
- 61, 246, 247, 65, 66, nil, nil, nil, nil, 245,
- 275, 279, 88, 87, 89, 90, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 41, nil, nil, 92, 91,
- 82, 50, 84, 83, 86, 85, 93, 94, nil, 80,
- 81, nil, 38, 39, 37, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 200, nil, nil, 204, nil, nil,
- 52, 53, nil, nil, 54, nil, nil, nil, nil, nil,
- 40, nil, nil, nil, nil, nil, nil, nil, 207, nil,
- nil, nil, nil, 79, 72, 74, 75, 76, 77, nil,
- nil, nil, 73, 78, 62, 63, 64, nil, 51, nil,
- nil, nil, 56, 57, nil, nil, nil, 60, nil, 58,
- 59, 61, 246, 247, 65, 66, nil, nil, nil, nil,
- 245, 275, 279, 88, 87, 89, 90, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 41, nil, nil, 92,
- 91, 82, 50, 84, 83, 86, 85, 93, 94, nil,
- 80, 81, nil, 38, 39, 37, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 200, nil, nil, 204, nil,
- nil, 52, 53, nil, nil, 54, nil, nil, nil, nil,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 207,
- nil, nil, nil, nil, 79, 72, 74, 75, 76, 77,
- nil, nil, nil, 73, 78, 62, 63, 64, nil, 51,
- nil, nil, nil, 56, 57, nil, nil, nil, 60, nil,
- 58, 59, 61, 246, 247, 65, 66, nil, nil, nil,
- nil, 245, 275, 279, 88, 87, 89, 90, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 276, nil, nil,
- 92, 91, 82, 50, 84, 83, 86, 85, 93, 94,
- nil, 80, 81, nil, nil, nil, 280, nil, 215, 219,
- 224, 225, 226, 221, 223, 231, 232, 227, 228, nil,
- 208, 209, nil, nil, 229, 230, 772, nil, nil, 204,
- nil, nil, 52, 53, nil, nil, 54, nil, nil, nil,
- 212, nil, 218, nil, 214, 213, 210, 211, 222, 220,
- 216, nil, 217, nil, nil, 79, 72, 74, 75, 76,
- 77, nil, nil, nil, 73, 78, 62, 63, 64, 233,
- 51, nil, nil, nil, 56, 57, nil, nil, nil, 60,
- nil, 58, 59, 61, 246, 247, 65, 66, nil, nil,
- nil, nil, 245, 28, 27, 88, 87, 89, 90, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 41, nil,
- nil, 92, 91, 82, 50, 84, 83, 86, 85, 93,
- 94, nil, 80, 81, nil, 38, 39, 37, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 200, nil, nil,
- 204, nil, nil, 52, 53, nil, nil, 54, nil, 299,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 207, nil, nil, nil, nil, 79, 72, 74, 75,
- 76, 77, nil, nil, nil, 73, 78, 62, 63, 64,
- nil, 51, nil, nil, nil, 56, 57, nil, nil, nil,
- 60, nil, 58, 59, 61, 246, 247, 65, 66, nil,
- nil, nil, nil, 245, 275, 279, 88, 87, 89, 90,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 276,
- nil, nil, 92, 91, 82, 50, 84, 83, 86, 85,
- 93, 94, nil, 80, 81, nil, nil, nil, 280, nil,
- 215, 219, 224, 225, 226, 221, 223, 231, 232, 227,
- 228, nil, 208, 209, nil, nil, 229, 230, 772, nil,
- nil, 204, nil, nil, 52, 53, nil, nil, 54, nil,
- nil, nil, 212, nil, 218, nil, 214, 213, 210, 211,
- 222, 220, 216, nil, 217, nil, nil, 79, 72, 74,
- 75, 76, 77, nil, nil, nil, 73, 78, 62, 63,
- 64, 233, 51, nil, nil, nil, 56, 57, nil, nil,
- nil, 60, nil, 58, 59, 61, 246, 247, 65, 66,
- nil, nil, nil, nil, 245, 275, 279, 88, 87, 89,
- 90, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 41, nil, nil, 92, 91, 82, 50, 84, 83, 86,
- 85, 93, 94, nil, 80, 81, nil, 38, 39, 37,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 200,
- nil, nil, 204, nil, nil, 52, 53, nil, nil, 54,
- nil, 864, nil, 243, nil, 40, nil, nil, nil, nil,
- nil, nil, nil, 207, nil, nil, nil, nil, 79, 72,
- 74, 75, 76, 77, nil, nil, nil, 73, 78, 62,
- 63, 64, nil, 51, nil, nil, nil, 56, 57, nil,
- nil, nil, 60, nil, 58, 59, 61, 246, 247, 65,
- 66, nil, nil, nil, nil, 245, 275, 279, 88, 87,
- 89, 90, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 41, nil, nil, 92, 91, 82, 50, 84, 83,
- 86, 85, 93, 94, nil, 80, 81, nil, 38, 39,
- 37, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 200, nil, nil, 204, nil, nil, 52, 53, nil, nil,
- 54, nil, 867, nil, 243, nil, 40, nil, nil, nil,
- nil, nil, nil, nil, 207, nil, nil, nil, nil, 79,
- 72, 74, 75, 76, 77, nil, nil, nil, 73, 78,
- 62, 63, 64, nil, 51, nil, nil, nil, 56, 57,
- nil, nil, nil, 60, nil, 58, 59, 61, 246, 247,
- 65, 66, nil, nil, nil, nil, 245, 275, 279, 88,
- 87, 89, 90, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 276, nil, nil, 92, 91, 82, 50, 84,
- 83, 86, 85, 93, 94, nil, 80, 81, nil, nil,
- nil, 280, nil, 215, 219, 224, 225, 226, 221, 223,
- 231, 232, 227, 228, nil, 208, 209, nil, nil, 229,
- 230, 772, nil, nil, 204, nil, nil, 52, 53, nil,
- nil, 54, nil, nil, nil, 212, nil, 218, nil, 214,
- 213, 210, 211, 222, 220, 216, nil, 217, nil, nil,
- 79, 72, 74, 75, 76, 77, nil, nil, nil, 73,
- 78, 62, 63, 64, 233, 51, nil, nil, nil, 56,
- 57, nil, nil, nil, 60, nil, 58, 59, 61, 246,
- 247, 65, 66, nil, nil, nil, nil, 245, 275, 279,
- 88, 87, 89, 90, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, 92, 91, 82, 50,
- 84, 83, 86, 85, 93, 94, nil, 80, 81, nil,
- 38, 39, 37, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 200, nil, nil, 204, nil, nil, 52, 53,
- nil, nil, 54, nil, nil, nil, nil, nil, 40, nil,
- nil, nil, nil, nil, nil, nil, 207, nil, nil, nil,
- nil, 79, 72, 74, 75, 76, 77, nil, nil, nil,
- 73, 78, 62, 63, 64, nil, 51, nil, nil, nil,
- 56, 57, nil, nil, nil, 60, nil, 58, 59, 61,
- 246, 247, 65, 66, nil, nil, nil, nil, 245, 275,
- 279, 88, 87, 89, 90, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 92, 91, 82,
- 50, 84, 83, 86, 85, 93, 94, nil, 80, 81,
- nil, 38, 39, 37, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 200, nil, nil, 204, nil, nil, 52,
- 53, nil, nil, 54, nil, nil, nil, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, 207, nil, nil,
- nil, nil, 79, 72, 74, 75, 76, 77, nil, nil,
- nil, 73, 78, 62, 63, 64, nil, 51, nil, nil,
- nil, 56, 57, nil, nil, nil, 60, nil, 58, 59,
- 61, 246, 247, 65, 66, nil, nil, nil, nil, 245,
- 275, 279, 88, 87, 89, 90, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 276, nil, nil, 92, 91,
- 82, 50, 84, 83, 86, 85, 93, 94, nil, 80,
- 81, nil, nil, nil, 280, nil, 215, 219, 224, 225,
- 226, 221, 223, 231, 232, 227, 228, nil, 208, 209,
- nil, nil, 229, 230, 772, nil, nil, 204, nil, nil,
- 52, 53, nil, nil, 54, nil, nil, nil, 212, nil,
- 218, nil, 214, 213, 210, 211, 222, 220, 216, nil,
- 217, nil, nil, 79, 72, 74, 75, 76, 77, nil,
- nil, nil, 73, 78, 62, 63, 64, 233, 51, nil,
- nil, nil, 56, 57, nil, nil, nil, 60, nil, 58,
- 59, 61, 246, 247, 65, 66, nil, nil, nil, nil,
- 245, 275, 279, 88, 87, 89, 90, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 41, nil, nil, 92,
- 91, 82, 50, 84, 83, 86, 85, 93, 94, nil,
- 80, 81, nil, 38, 39, 37, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 200, nil, nil, 204, nil,
- nil, 52, 53, nil, nil, 54, nil, 889, nil, 243,
- nil, 40, nil, nil, nil, nil, nil, nil, nil, 207,
- nil, nil, nil, nil, 79, 72, 74, 75, 76, 77,
- nil, nil, nil, 73, 78, 62, 63, 64, nil, 51,
- nil, nil, nil, 56, 57, nil, nil, nil, 60, nil,
- 58, 59, 61, 246, 247, 65, 66, nil, nil, nil,
- nil, 245, 275, 279, 88, 87, 89, 90, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 276, nil, nil,
- 92, 91, 82, 50, 84, 83, 86, 85, 93, 94,
- nil, 80, 81, nil, nil, nil, 280, nil, 215, 219,
- 224, 225, 226, 221, 223, 231, 232, 227, 228, nil,
- 208, 209, nil, nil, 229, 230, 772, nil, nil, 204,
- nil, nil, 52, 53, nil, nil, 54, nil, nil, nil,
- 212, nil, 218, nil, 214, 213, 210, 211, 222, 220,
- 216, nil, 217, nil, nil, 79, 72, 74, 75, 76,
- 77, nil, nil, nil, 73, 78, 62, 63, 64, 233,
- 51, nil, nil, nil, 56, 57, nil, nil, nil, 60,
- nil, 58, 59, 61, 246, 247, 65, 66, nil, nil,
- nil, nil, 245, 275, 279, 88, 87, 89, 90, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 41, nil,
- nil, 92, 91, 82, 50, 84, 83, 86, 85, 93,
- 94, nil, 80, 81, nil, 38, 39, 37, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 200, nil, nil,
- 204, nil, nil, 52, 53, nil, nil, 54, nil, nil,
- nil, nil, nil, 40, nil, nil, nil, nil, nil, nil,
- nil, 207, nil, nil, nil, nil, 79, 72, 74, 75,
- 76, 77, nil, nil, nil, 73, 78, 153, 164, 154,
- 177, 150, 170, 160, 159, 180, 181, 175, 158, 157,
- 152, 178, 182, 183, 162, 151, 165, 169, 171, 163,
- 156, nil, nil, 172, 179, 174, 336, 335, 337, 334,
- 149, 168, 167, nil, nil, nil, nil, nil, 148, 155,
- 146, 147, 332, 333, 330, 111, 84, 83, 331, 85,
- nil, nil, nil, nil, nil, nil, 139, 140, nil, 137,
- 121, 122, 123, nil, 126, 128, nil, nil, 124, nil,
- nil, nil, nil, 141, 142, 129, 130, nil, nil, nil,
- nil, nil, 341, nil, nil, nil, nil, nil, nil, nil,
- 134, 133, nil, 120, 138, 136, 135, 131, 132, 127,
- 125, 118, nil, 119, nil, nil, 143, 153, 164, 154,
- 177, 150, 170, 160, 159, 180, 181, 175, 158, 157,
- 152, 178, 182, 183, 162, 151, 165, 169, 171, 163,
- 156, nil, nil, 172, 179, 174, 173, 166, 176, 161,
- 149, 168, 167, nil, nil, nil, nil, nil, 148, 155,
- 146, 147, 144, 145, 109, 111, nil, nil, 110, nil,
- nil, nil, nil, nil, nil, nil, 139, 140, nil, 137,
- 121, 122, 123, nil, 126, 128, nil, nil, 124, nil,
- nil, nil, nil, 141, 142, 129, 130, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 134, 133, nil, 120, 138, 136, 135, 131, 132, 127,
- 125, 118, nil, 119, nil, nil, 143, 215, 219, 224,
- 225, 226, 221, 223, 231, 232, 227, 228, nil, 208,
- 209, nil, nil, 229, 230, nil, nil, nil, -215, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 212,
- nil, 218, nil, 214, 213, 210, 211, 222, 220, 216,
- nil, 217, nil, nil, nil, nil, nil, nil, nil, 376,
- 379, nil, nil, 377, nil, nil, nil, nil, 233, nil,
- -215, 139, 140, nil, 137, 121, 122, 123, nil, 126,
- 128, nil, nil, 124, nil, nil, nil, nil, 141, 142,
- 129, 130, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 134, 133, nil, 120, 138,
- 136, 135, 131, 132, 127, 125, 118, nil, 119, 381,
- 385, 143, nil, 383, nil, nil, nil, nil, nil, nil,
- nil, 139, 140, nil, 137, 121, 122, 123, nil, 126,
- 128, nil, nil, 124, nil, nil, nil, nil, 141, 142,
- 129, 130, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 134, 133, nil, 120, 138,
- 136, 135, 131, 132, 127, 125, 118, nil, 119, 433,
- 379, 143, nil, 434, nil, nil, nil, nil, nil, nil,
- nil, 139, 140, nil, 137, 121, 122, 123, nil, 126,
- 128, nil, nil, 124, nil, nil, nil, nil, 141, 142,
- 129, 130, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 134, 133, nil, 120, 138,
- 136, 135, 131, 132, 127, 125, 118, nil, 119, 433,
- 379, 143, nil, 434, nil, nil, nil, nil, nil, nil,
- nil, 139, 140, nil, 137, 121, 122, 123, nil, 126,
- 128, nil, nil, 124, nil, nil, nil, nil, 141, 142,
- 129, 130, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 134, 133, nil, 120, 138,
- 136, 135, 131, 132, 127, 125, 118, nil, 119, 550,
- 379, 143, nil, 551, nil, nil, nil, nil, nil, nil,
- nil, 139, 140, nil, 137, 121, 122, 123, nil, 126,
- 128, nil, nil, 124, nil, nil, nil, nil, 141, 142,
- 129, 130, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 134, 133, nil, 120, 138,
- 136, 135, 131, 132, 127, 125, 118, nil, 119, 552,
- 385, 143, nil, 553, nil, nil, nil, nil, nil, nil,
- nil, 139, 140, nil, 137, 121, 122, 123, nil, 126,
- 128, nil, nil, 124, nil, nil, nil, nil, 141, 142,
- 129, 130, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 134, 133, nil, 120, 138,
- 136, 135, 131, 132, 127, 125, 118, nil, 119, nil,
- nil, 143, 215, 219, 224, 225, 226, 221, 223, 231,
- 232, 227, 228, nil, 208, 209, nil, nil, 229, 230,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 212, nil, 218, nil, 214, 213,
- 210, 211, 222, 220, 216, nil, 217, nil, nil, nil,
- nil, nil, nil, 594, 379, nil, nil, 595, nil, nil,
- nil, nil, nil, 233, 556, 139, 140, nil, 137, 121,
- 122, 123, nil, 126, 128, nil, nil, 124, nil, nil,
- nil, nil, 141, 142, 129, 130, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 134,
- 133, nil, 120, 138, 136, 135, 131, 132, 127, 125,
- 118, nil, 119, 597, 385, 143, nil, 598, nil, nil,
- nil, nil, nil, nil, nil, 139, 140, nil, 137, 121,
- 122, 123, nil, 126, 128, nil, nil, 124, nil, nil,
- nil, nil, 141, 142, 129, 130, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 134,
- 133, nil, 120, 138, 136, 135, 131, 132, 127, 125,
- 118, nil, 119, 550, 379, 143, nil, 551, nil, nil,
- nil, nil, nil, nil, nil, 139, 140, nil, 137, 121,
- 122, 123, nil, 126, 128, nil, nil, 124, nil, nil,
- nil, nil, 141, 142, 129, 130, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 134,
- 133, nil, 120, 138, 136, 135, 131, 132, 127, 125,
- 118, nil, 119, 552, 385, 143, nil, 553, nil, nil,
- nil, nil, nil, nil, nil, 139, 140, nil, 137, 121,
- 122, 123, nil, 126, 128, nil, nil, 124, nil, nil,
- nil, nil, 141, 142, 129, 130, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 134,
- 133, nil, 120, 138, 136, 135, 131, 132, 127, 125,
- 118, nil, 119, 630, 379, 143, nil, 631, nil, nil,
- nil, nil, nil, nil, nil, 139, 140, nil, 137, 121,
- 122, 123, nil, 126, 128, nil, nil, 124, nil, nil,
- nil, nil, 141, 142, 129, 130, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 134,
- 133, nil, 120, 138, 136, 135, 131, 132, 127, 125,
- 118, nil, 119, 632, 385, 143, nil, 633, nil, nil,
- nil, nil, nil, nil, nil, 139, 140, nil, 137, 121,
- 122, 123, nil, 126, 128, nil, nil, 124, nil, nil,
- nil, nil, 141, 142, 129, 130, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 134,
- 133, nil, 120, 138, 136, 135, 131, 132, 127, 125,
- 118, nil, 119, 635, 385, 143, nil, 636, nil, nil,
- nil, nil, nil, nil, nil, 139, 140, nil, 137, 121,
- 122, 123, nil, 126, 128, nil, nil, 124, nil, nil,
- nil, nil, 141, 142, 129, 130, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 134,
- 133, nil, 120, 138, 136, 135, 131, 132, 127, 125,
- 118, nil, 119, 433, 379, 143, nil, 434, nil, nil,
- nil, nil, nil, nil, nil, 139, 140, nil, 137, 121,
- 122, 123, nil, 126, 128, nil, nil, 124, nil, nil,
- nil, nil, 141, 142, 129, 130, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 134,
- 133, nil, 120, 138, 136, 135, 131, 132, 127, 125,
- 118, nil, 119, 433, 379, 143, nil, 434, nil, nil,
- nil, nil, nil, nil, nil, 139, 140, nil, 137, 121,
- 122, 123, nil, 126, 128, nil, nil, 124, nil, nil,
- nil, nil, 141, 142, 129, 130, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 134,
- 133, nil, 120, 138, 136, 135, 131, 132, 127, 125,
- 118, nil, 119, 433, 379, 143, nil, 434, nil, nil,
- nil, nil, nil, nil, nil, 139, 140, nil, 137, 121,
- 122, 123, nil, 126, 128, nil, nil, 124, nil, nil,
- nil, nil, 141, 142, 129, 130, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 134,
- 133, nil, 120, 138, 136, 135, 131, 132, 127, 125,
- 118, nil, 119, 857, 379, 143, nil, 858, nil, nil,
- nil, nil, nil, nil, nil, 139, 140, nil, 137, 121,
- 122, 123, nil, 126, 128, nil, nil, 124, nil, nil,
- nil, nil, 141, 142, 129, 130, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 134,
- 133, nil, 120, 138, 136, 135, 131, 132, 127, 125,
- 118, nil, 119, 859, 385, 143, nil, 860, nil, nil,
- nil, nil, nil, nil, nil, 139, 140, nil, 137, 121,
- 122, 123, nil, 126, 128, nil, nil, 124, nil, nil,
- nil, nil, 141, 142, 129, 130, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 134,
- 133, nil, 120, 138, 136, 135, 131, 132, 127, 125,
- 118, nil, 119, nil, nil, 143, 215, 219, 224, 225,
- 226, 221, 223, 231, 232, 227, 228, nil, 208, 209,
- nil, nil, 229, 230, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 212, nil,
- 218, nil, 214, 213, 210, 211, 222, 220, 216, nil,
- 217, nil, 215, 219, 224, 225, 226, 221, 223, 231,
- 232, 227, 228, nil, 208, 209, 291, 233, 229, 230,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 212, nil, 218, nil, 214, 213,
- 210, 211, 222, 220, 216, nil, 217, nil, 215, 219,
- 224, 225, 226, 221, 223, 231, 232, 227, 228, nil,
- 208, 209, 291, 233, 229, 230, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 212, nil, 218, nil, 214, 213, 210, 211, 222, 220,
- 216, nil, 217, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 233 ]
-
-racc_action_check = [
- 381, 512, 512, 518, 518, 306, 755, 381, 381, 381,
- 326, 440, 711, 381, 381, 313, 381, 199, 440, 711,
- 307, 70, 347, 316, 424, 381, 3, 627, 500, 70,
- 1, 3, 201, 603, 603, 381, 381, 630, 381, 381,
- 381, 381, 381, 727, 798, 446, 311, 631, 311, 632,
- 743, 394, 755, 755, 755, 755, 8, 310, 424, 447,
- 440, 711, 310, 199, 381, 381, 381, 381, 381, 381,
- 381, 381, 381, 381, 381, 381, 381, 381, 201, 446,
- 381, 381, 381, 55, 381, 347, 603, 594, 381, 591,
- 512, 381, 518, 447, 491, 512, 381, 394, 381, 633,
- 381, 381, 381, 381, 381, 381, 381, 383, 381, 381,
- 381, 9, 632, 326, 383, 383, 383, 306, 313, 306,
- 383, 383, 306, 383, 381, 381, 316, 381, 591, 381,
- 381, 500, 307, 326, 307, 492, 595, 307, 326, 627,
- 630, 627, 383, 383, 627, 383, 383, 383, 383, 383,
- 631, 594, 632, 743, 582, 727, 798, 727, 798, 744,
- 727, 798, 633, 26, 322, 15, 15, 55, 322, 594,
- 550, 383, 383, 383, 383, 383, 383, 383, 383, 383,
- 383, 383, 383, 383, 383, 574, 277, 383, 383, 383,
- 594, 383, 491, 594, 491, 383, 582, 491, 383, 551,
- 595, 10, 633, 383, 528, 383, 343, 383, 383, 383,
- 383, 383, 383, 383, 50, 383, 550, 383, 595, 574,
- 11, 50, 50, 50, 511, 26, 50, 50, 50, 511,
- 50, 383, 383, 492, 383, 492, 383, 383, 492, 595,
- 50, 50, 595, 297, 12, 551, 297, 26, 277, 50,
- 50, 693, 50, 50, 50, 50, 50, 744, 14, 744,
- 298, 20, 744, 298, 344, 528, 528, 343, 343, 343,
- 277, 301, 108, 34, 301, 528, 108, 108, 50, 50,
- 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
- 50, 50, 345, 13, 50, 50, 50, 693, 36, 50,
- 13, 14, 50, 346, 14, 50, 50, 41, 50, 13,
- 50, 95, 50, 419, 50, 50, 50, 50, 50, 50,
- 50, 432, 50, 184, 50, 344, 344, 344, 432, 432,
- 432, 848, 200, 432, 432, 432, 348, 432, 50, 50,
- 50, 50, 202, 50, 320, 419, 803, 432, 803, 419,
- 419, 419, 419, 345, 345, 345, 432, 432, 203, 432,
- 432, 432, 432, 432, 346, 346, 346, 25, 303, 239,
- 321, 303, 597, 635, 25, 42, 435, 848, 848, 848,
- 848, 240, 42, 435, 435, 435, 330, 857, 435, 435,
- 435, 42, 435, 330, 320, 244, 432, 348, 348, 348,
- 35, 320, 435, 432, 37, 37, 320, 253, 432, 432,
- 320, 435, 435, 264, 435, 435, 435, 435, 435, 283,
- 321, 266, 597, 635, 376, 858, 377, 321, 320, 597,
- 635, 432, 321, 540, 597, 635, 321, 857, 597, 635,
- 267, 400, 268, 35, 857, 432, 35, 432, 401, 857,
- 432, 435, 198, 857, 321, 640, 597, 635, 435, 198,
- 640, 646, 283, 435, 435, 283, 646, 376, 198, 377,
- 376, 857, 377, 400, 271, 858, 540, 400, 400, 540,
- 401, 271, 858, 273, 401, 401, 435, 858, 280, 280,
- 271, 858, 294, 294, 331, 552, 552, 552, 275, 552,
- 435, 331, 435, 552, 552, 435, 276, 859, 552, 858,
- 552, 552, 552, 552, 552, 552, 552, 305, 305, 615,
- 615, 552, 552, 552, 552, 552, 552, 552, 332, 420,
- 279, 333, 334, 758, 758, 332, 284, 552, 333, 334,
- 552, 552, 552, 552, 552, 552, 552, 552, 552, 552,
- 285, 552, 552, 335, 552, 552, 552, 859, 870, 870,
- 335, 420, 288, 336, 859, 420, 420, 420, 420, 859,
- 336, 272, 292, 859, 337, 293, 552, 552, 272, 552,
- 552, 337, 552, 552, 296, 300, 552, 272, 552, 302,
- 552, 859, 552, 312, 339, 274, 286, 314, 315, 317,
- 552, 339, 274, 286, 314, 552, 552, 552, 552, 552,
- 552, 274, 286, 314, 552, 552, 553, 553, 553, 362,
- 553, 363, 368, 552, 553, 553, 371, 375, 382, 553,
- 402, 553, 553, 553, 553, 553, 553, 553, 403, 404,
- 405, 429, 553, 553, 553, 553, 553, 553, 553, 522,
- 522, 438, 522, 522, 522, 439, 441, 442, 553, 448,
- 450, 553, 553, 553, 553, 553, 553, 553, 553, 553,
- 553, 451, 553, 553, 455, 553, 553, 553, 398, 398,
- 398, 398, 398, 398, 398, 398, 398, 398, 398, 324,
- 398, 398, 459, 460, 398, 398, 324, 553, 553, 459,
- 553, 553, 470, 553, 553, 324, 473, 553, 459, 553,
- 398, 553, 398, 553, 398, 398, 398, 398, 398, 398,
- 398, 553, 398, 487, 493, 494, 553, 553, 553, 553,
- 553, 553, 526, 503, 531, 553, 553, 60, 60, 60,
- 503, 60, 543, 547, 553, 60, 60, 554, 561, 503,
- 60, 563, 60, 60, 60, 60, 60, 60, 60, 566,
- 568, 576, 577, 60, 60, 60, 60, 60, 60, 60,
- 583, 586, 60, 593, 596, 599, 601, 366, 602, 60,
- 604, 607, 60, 60, 60, 60, 60, 60, 60, 60,
- 60, 60, 608, 60, 60, 611, 60, 60, 60, 366,
- 366, 366, 366, 366, 366, 366, 366, 366, 366, 366,
- 598, 366, 366, 721, 617, 366, 366, 598, 60, 623,
- 721, 60, 598, 625, 60, 60, 598, 626, 60, 721,
- 629, 366, 638, 366, 60, 366, 366, 366, 366, 366,
- 366, 366, 60, 366, 643, 647, 648, 60, 60, 60,
- 60, 60, 60, 649, 655, 660, 60, 60, 663, 60,
- 366, 665, 366, 636, 60, 97, 97, 97, 97, 97,
- 636, 678, 696, 97, 97, 636, 700, 701, 97, 636,
- 97, 97, 97, 97, 97, 97, 97, 702, 705, 709,
- 710, 97, 97, 97, 97, 97, 97, 97, 712, 716,
- 97, 4, 4, 4, 4, 4, 97, 97, 97, 97,
- 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,
- 718, 97, 97, 719, 97, 97, 97, 399, 399, 399,
- 399, 399, 399, 399, 399, 399, 399, 399, 770, 399,
- 399, 771, 720, 399, 399, 770, 97, 732, 771, 97,
- 735, 746, 97, 97, 770, 750, 97, 771, 97, 399,
- 751, 399, 97, 399, 399, 399, 399, 399, 399, 399,
- 97, 399, 764, 772, 774, 97, 97, 97, 97, 97,
- 97, 775, 773, 782, 97, 97, 697, 697, 697, 773,
- 697, 783, 97, 825, 697, 697, 784, 787, 773, 697,
- 825, 697, 697, 697, 697, 697, 697, 697, 800, 825,
- 801, 806, 697, 697, 697, 697, 697, 697, 697, 106,
- 106, 106, 106, 106, 808, 809, 810, 813, 697, 814,
- 826, 697, 697, 697, 697, 697, 697, 697, 697, 697,
- 697, 833, 697, 697, 410, 836, 514, 697, 514, 514,
- 514, 514, 860, 837, 854, 876, 855, 856, 865, 860,
- 410, 410, 876, 871, 860, 872, 873, 697, 860, 874,
- 697, 876, 878, 697, 697, 881, 410, 697, 410, 892,
- 410, 410, 410, 410, 514, nil, 410, nil, 410, nil,
- nil, nil, 514, 514, 514, 514, 697, 697, 697, 697,
- 697, 697, nil, nil, nil, 697, 697, nil, 821, 821,
- 821, 697, 821, nil, nil, nil, 821, 821, nil, nil,
- nil, 821, nil, 821, 821, 821, 821, 821, 821, 821,
- nil, nil, nil, nil, 821, 821, 821, 821, 821, 821,
- 821, 350, 350, 350, 350, 350, nil, nil, 555, nil,
- 821, nil, nil, 821, 821, 821, 821, 821, 821, 821,
- 821, 821, 821, nil, 821, 821, nil, nil, nil, 821,
- 555, 555, 555, 555, 555, 555, 555, 555, 555, 555,
- 555, nil, 555, 555, nil, nil, 555, 555, nil, 821,
- nil, nil, 821, nil, nil, 821, 821, nil, nil, 821,
- nil, nil, 555, nil, 555, nil, 555, 555, 555, 555,
- 555, 555, 555, nil, 555, nil, nil, nil, 821, 821,
- 821, 821, 821, 821, nil, nil, nil, 821, 821, nil,
- nil, 555, nil, 821, 0, 0, 0, 0, 0, 0,
- nil, nil, nil, 0, 0, nil, nil, nil, 0, nil,
- 0, 0, 0, 0, 0, 0, 0, nil, nil, nil,
- nil, 0, 0, 0, 0, 0, 0, 0, nil, nil,
- 0, nil, nil, nil, nil, nil, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- nil, 0, 0, nil, 0, 0, 0, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 0, nil, nil, 0,
- nil, nil, 0, 0, nil, nil, 0, nil, 0, nil,
- nil, nil, 0, 645, nil, 645, 645, 645, 645, nil,
- 0, nil, nil, nil, nil, 0, 0, 0, 0, 0,
- 0, nil, nil, nil, 0, 0, 30, 30, 30, 30,
- 30, 30, nil, nil, nil, 30, 30, nil, nil, nil,
- 30, nil, 30, 30, 30, 30, 30, 30, 30, 645,
- 645, 645, 645, 30, 30, 30, 30, 30, 30, 30,
- nil, nil, 30, nil, nil, nil, nil, nil, 30, 30,
- 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
- 30, 30, nil, 30, 30, nil, 30, 30, 30, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 30, nil,
- nil, 30, nil, nil, 30, 30, nil, nil, 30, nil,
- 30, nil, nil, nil, 30, 753, nil, 753, 753, 753,
- 753, nil, 30, nil, nil, nil, nil, 30, 30, 30,
- 30, 30, 30, nil, nil, nil, 30, 30, 51, 51,
- 51, 51, 51, 51, nil, nil, nil, 51, 51, nil,
- nil, nil, 51, nil, 51, 51, 51, 51, 51, 51,
- 51, 753, 753, 753, 753, 51, 51, 51, 51, 51,
- 51, 51, nil, nil, 51, nil, nil, nil, nil, nil,
- 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, nil, 51, 51, nil, 51, 51,
- 51, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 51, nil, nil, 51, nil, nil, 51, 51, nil, nil,
- 51, nil, 51, nil, nil, nil, 51, 817, nil, 817,
- 817, 817, 817, nil, 51, nil, nil, nil, nil, 51,
- 51, 51, 51, 51, 51, nil, nil, nil, 51, 51,
- 186, 186, 186, 186, 186, 186, nil, nil, nil, 186,
- 186, nil, nil, nil, 186, 817, 186, 186, 186, 186,
- 186, 186, 186, 817, 817, 817, 817, 186, 186, 186,
- 186, 186, 186, 186, nil, nil, 186, nil, nil, nil,
- nil, nil, 186, 186, 186, 186, 186, 186, 186, 186,
- 186, 186, 186, 186, 186, 186, nil, 186, 186, nil,
- 186, 186, 186, 409, 409, 409, 409, 409, 409, 409,
- nil, nil, 409, 409, nil, nil, nil, nil, nil, 409,
- 409, nil, 186, nil, nil, 186, nil, nil, 186, 186,
- nil, nil, 186, nil, 186, 409, nil, 409, 186, 409,
- 409, 409, 409, 409, 409, 409, 186, 409, nil, nil,
- nil, 186, 186, 186, 186, 186, 186, nil, nil, nil,
- 186, 186, 187, 187, 187, 187, 187, 187, nil, nil,
- nil, 187, 187, nil, nil, nil, 187, nil, 187, 187,
- 187, 187, 187, 187, 187, nil, nil, nil, nil, 187,
- 187, 187, 187, 187, 187, 187, nil, nil, 187, nil,
- nil, nil, nil, nil, 187, 187, 187, 187, 187, 187,
- 187, 187, 187, 187, 187, 187, 187, 187, nil, 187,
- 187, nil, 187, 187, 187, 411, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 411, 411, nil, 187, nil, nil, 187, nil, nil,
- 187, 187, nil, nil, 187, nil, 187, 411, nil, 411,
- 187, 411, 411, 411, 411, nil, nil, 411, 187, 411,
- nil, nil, nil, 187, 187, 187, 187, 187, 187, nil,
- nil, nil, 187, 187, 204, 204, 204, 204, 204, 204,
- nil, nil, nil, 204, 204, nil, nil, nil, 204, nil,
- 204, 204, 204, 204, 204, 204, 204, nil, nil, nil,
- nil, 204, 204, 204, 204, 204, 204, 204, nil, nil,
- 204, nil, nil, nil, nil, nil, 204, 204, 204, 204,
- 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
- nil, 204, 204, nil, 204, 204, 204, 412, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 412, 412, nil, 204, nil, nil, 204,
- nil, nil, 204, 204, nil, nil, 204, nil, 204, 412,
- nil, 412, 204, 412, 412, 412, 412, nil, nil, 412,
- 204, 412, nil, nil, nil, 204, 204, 204, 204, 204,
- 204, nil, nil, nil, 204, 204, 265, 265, 265, 265,
- 265, 265, nil, nil, nil, 265, 265, nil, nil, nil,
- 265, nil, 265, 265, 265, 265, 265, 265, 265, nil,
- nil, nil, nil, 265, 265, 265, 265, 265, 265, 265,
- nil, nil, 265, nil, nil, nil, nil, nil, 265, 265,
- 265, 265, 265, 265, 265, 265, 265, 265, 265, 265,
- 265, 265, nil, 265, 265, nil, 265, 265, 265, 413,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 413, 413, nil, 265, nil,
- nil, 265, nil, nil, 265, 265, nil, nil, 265, nil,
- 265, 413, nil, 413, 265, 413, 413, 413, 413, nil,
- nil, 413, 265, 413, nil, nil, nil, 265, 265, 265,
- 265, 265, 265, nil, nil, nil, 265, 265, 270, 270,
- 270, 270, 270, 270, nil, nil, nil, 270, 270, nil,
- nil, nil, 270, nil, 270, 270, 270, 270, 270, 270,
- 270, nil, nil, nil, nil, 270, 270, 270, 270, 270,
- 270, 270, nil, nil, 270, nil, nil, nil, nil, nil,
- 270, 270, 270, 270, 270, 270, 270, 270, 270, 270,
- 270, 270, 270, 270, nil, 270, 270, nil, 270, 270,
- 270, 414, 414, 414, 414, 414, 414, 414, nil, nil,
- 414, 414, nil, nil, nil, nil, nil, 414, 414, nil,
- 270, nil, nil, 270, nil, nil, 270, 270, nil, nil,
- 270, nil, 270, 414, nil, 414, 270, 414, 414, 414,
- 414, 414, 414, 414, 270, 414, nil, nil, nil, 270,
- 270, 270, 270, 270, 270, nil, nil, nil, 270, 270,
- 486, 486, 486, 486, 486, 486, nil, nil, nil, 486,
- 486, nil, nil, nil, 486, nil, 486, 486, 486, 486,
- 486, 486, 486, nil, nil, nil, nil, 486, 486, 486,
- 486, 486, 486, 486, nil, nil, 486, nil, nil, nil,
- nil, nil, 486, 486, 486, 486, 486, 486, 486, 486,
- 486, 486, 486, 486, 486, 486, nil, 486, 486, nil,
- 486, 486, 486, 415, 415, 415, 415, 415, 415, 415,
- nil, nil, 415, 415, nil, nil, nil, nil, nil, 415,
- 415, nil, 486, nil, nil, 486, nil, nil, 486, 486,
- nil, nil, 486, nil, 486, 415, nil, 415, 486, 415,
- 415, 415, 415, 415, 415, 415, 486, 415, nil, nil,
- nil, 486, 486, 486, 486, 486, 486, nil, nil, nil,
- 486, 486, 490, 490, 490, 490, 490, 490, nil, nil,
- nil, 490, 490, nil, nil, nil, 490, nil, 490, 490,
- 490, 490, 490, 490, 490, nil, nil, nil, nil, 490,
- 490, 490, 490, 490, 490, 490, nil, nil, 490, nil,
- nil, nil, nil, nil, 490, 490, 490, 490, 490, 490,
- 490, 490, 490, 490, 490, 490, 490, 490, nil, 490,
- 490, nil, 490, 490, 490, 416, 416, 416, 416, 416,
- 416, 416, nil, nil, 416, 416, nil, nil, nil, nil,
- nil, 416, 416, nil, 490, nil, nil, 490, nil, nil,
- 490, 490, nil, nil, 490, nil, 490, 416, nil, 416,
- 490, 416, 416, 416, 416, 416, 416, 416, 490, 416,
- nil, nil, nil, 490, 490, 490, 490, 490, 490, nil,
- nil, nil, 490, 490, 495, 495, 495, 495, 495, 495,
- nil, nil, nil, 495, 495, nil, nil, nil, 495, nil,
- 495, 495, 495, 495, 495, 495, 495, nil, nil, nil,
- nil, 495, 495, 495, 495, 495, 495, 495, nil, nil,
- 495, nil, nil, nil, nil, nil, 495, 495, 495, 495,
- 495, 495, 495, 495, 495, 495, 495, 495, 495, 495,
- nil, 495, 495, nil, 495, 495, 495, 417, 417, 417,
- 417, 417, 417, 417, nil, nil, 417, 417, nil, nil,
- nil, nil, nil, 417, 417, nil, 495, nil, nil, 495,
- nil, nil, 495, 495, nil, nil, 495, nil, 495, 417,
- nil, 417, 495, 417, 417, 417, 417, 417, 417, 417,
- 495, 417, nil, nil, nil, 495, 495, 495, 495, 495,
- 495, nil, nil, nil, 495, 495, 513, 513, 513, 513,
- 513, 513, nil, nil, nil, 513, 513, nil, nil, nil,
- 513, nil, 513, 513, 513, 513, 513, 513, 513, nil,
- nil, nil, nil, 513, 513, 513, 513, 513, 513, 513,
- nil, nil, 513, nil, nil, nil, nil, nil, 513, 513,
- 513, 513, 513, 513, 513, 513, 513, 513, 513, 513,
- 513, 513, nil, 513, 513, nil, 513, 513, 513, 418,
- 418, 418, 418, 418, 418, 418, nil, nil, 418, 418,
- nil, nil, nil, nil, nil, 418, 418, nil, 513, nil,
- nil, 513, nil, nil, 513, 513, nil, nil, 513, nil,
- 513, 418, nil, 418, 513, 418, 418, 418, 418, 418,
- 418, 418, 513, 418, nil, nil, nil, 513, 513, 513,
- 513, 513, 513, nil, nil, nil, 513, 513, 559, 559,
- 559, 559, 559, 559, nil, nil, nil, 559, 559, nil,
- nil, nil, 559, nil, 559, 559, 559, 559, 559, 559,
- 559, nil, nil, nil, nil, 559, 559, 559, 559, 559,
- 559, 559, nil, nil, 559, nil, nil, nil, nil, nil,
- 559, 559, 559, 559, 559, 559, 559, 559, 559, 559,
- 559, 559, 559, 559, nil, 559, 559, nil, 559, 559,
- 559, 421, 421, 421, 421, 421, 421, 421, nil, nil,
- 421, 421, nil, nil, nil, nil, nil, 421, 421, nil,
- 559, nil, nil, 559, nil, nil, 559, 559, nil, nil,
- 559, nil, 559, 421, nil, 421, 559, 421, 421, 421,
- 421, 421, 421, 421, 559, 421, nil, nil, nil, 559,
- 559, 559, 559, 559, 559, nil, nil, nil, 559, 559,
- 588, 588, 588, 588, 588, 588, nil, nil, nil, 588,
- 588, nil, nil, nil, 588, nil, 588, 588, 588, 588,
- 588, 588, 588, nil, nil, nil, nil, 588, 588, 588,
- 588, 588, 588, 588, nil, nil, 588, nil, nil, nil,
- nil, nil, 588, 588, 588, 588, 588, 588, 588, 588,
- 588, 588, 588, 588, 588, 588, nil, 588, 588, nil,
- 588, 588, 588, 422, 422, 422, 422, 422, 422, 422,
- 422, nil, 422, 422, nil, nil, nil, nil, nil, 422,
- 422, nil, 588, nil, nil, 588, nil, nil, 588, 588,
- nil, nil, 588, nil, 588, 422, nil, 422, 588, 422,
- 422, 422, 422, 422, 422, 422, 588, 422, nil, nil,
- nil, 588, 588, 588, 588, 588, 588, nil, nil, nil,
- 588, 588, 589, 589, 589, 589, 589, 589, nil, nil,
- nil, 589, 589, nil, nil, nil, 589, nil, 589, 589,
- 589, 589, 589, 589, 589, nil, nil, nil, nil, 589,
- 589, 589, 589, 589, 589, 589, nil, nil, 589, nil,
- nil, nil, nil, nil, 589, 589, 589, 589, 589, 589,
- 589, 589, 589, 589, 589, 589, 589, 589, nil, 589,
- 589, nil, 589, 589, 589, 406, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 406, 406, nil, 589, nil, nil, 589, nil, nil,
- 589, 589, nil, nil, 589, nil, 589, 406, nil, 406,
- 589, 406, 406, 406, 406, nil, nil, nil, 589, nil,
- nil, nil, nil, 589, 589, 589, 589, 589, 589, nil,
- nil, nil, 589, 589, 606, 606, 606, 606, 606, 606,
- nil, nil, nil, 606, 606, nil, nil, nil, 606, nil,
- 606, 606, 606, 606, 606, 606, 606, nil, nil, nil,
- nil, 606, 606, 606, 606, 606, 606, 606, nil, nil,
- 606, nil, nil, nil, nil, nil, 606, 606, 606, 606,
- 606, 606, 606, 606, 606, 606, 606, 606, 606, 606,
- nil, 606, 606, nil, 606, 606, 606, 407, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 407, 407, nil, 606, nil, nil, 606,
- nil, nil, 606, 606, nil, nil, 606, nil, 606, 407,
- nil, 407, 606, 407, 407, 407, 407, nil, nil, nil,
- 606, nil, nil, nil, nil, 606, 606, 606, 606, 606,
- 606, nil, nil, nil, 606, 606, 639, 639, 639, 639,
- 639, 639, nil, nil, nil, 639, 639, nil, nil, nil,
- 639, nil, 639, 639, 639, 639, 639, 639, 639, nil,
- nil, nil, nil, 639, 639, 639, 639, 639, 639, 639,
- nil, nil, 639, nil, nil, nil, nil, nil, 639, 639,
- 639, 639, 639, 639, 639, 639, 639, 639, 639, 639,
- 639, 639, nil, 639, 639, nil, 639, 639, 639, 408,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 408, 408, nil, 639, nil,
- nil, 639, nil, nil, 639, 639, nil, nil, 639, nil,
- 639, 408, nil, nil, 639, 408, 408, 408, 408, nil,
- nil, nil, 639, nil, nil, nil, nil, 639, 639, 639,
- 639, 639, 639, nil, nil, nil, 639, 639, 644, 644,
- 644, 644, 644, 644, nil, nil, nil, 644, 644, nil,
- nil, nil, 644, nil, 644, 644, 644, 644, 644, 644,
- 644, nil, nil, nil, nil, 644, 644, 644, 644, 644,
- 644, 644, nil, nil, 644, nil, nil, nil, nil, nil,
- 644, 644, 644, 644, 644, 644, 644, 644, 644, 644,
- 644, 644, 644, 644, nil, 644, 644, nil, 644, 644,
- 644, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 644, nil, nil, 644, nil, nil, 644, 644, nil, nil,
- 644, nil, 644, nil, nil, nil, 644, nil, nil, nil,
- nil, nil, nil, nil, 644, nil, nil, nil, nil, 644,
- 644, 644, 644, 644, 644, nil, nil, nil, 644, 644,
- 671, 671, 671, 671, 671, 671, nil, nil, nil, 671,
- 671, nil, nil, nil, 671, nil, 671, 671, 671, 671,
- 671, 671, 671, nil, nil, nil, nil, 671, 671, 671,
- 671, 671, 671, 671, nil, nil, 671, nil, nil, nil,
- nil, nil, 671, 671, 671, 671, 671, 671, 671, 671,
- 671, 671, 671, 671, 671, 671, nil, 671, 671, nil,
- 671, 671, 671, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 671, nil, nil, 671, nil, nil, 671, 671,
- nil, nil, 671, nil, 671, nil, nil, nil, 671, nil,
- nil, nil, nil, nil, nil, nil, 671, nil, nil, nil,
- nil, 671, 671, 671, 671, 671, 671, nil, nil, nil,
- 671, 671, 706, 706, 706, 706, 706, 706, nil, nil,
- nil, 706, 706, nil, nil, nil, 706, nil, 706, 706,
- 706, 706, 706, 706, 706, nil, nil, nil, nil, 706,
- 706, 706, 706, 706, 706, 706, nil, nil, 706, nil,
- nil, nil, nil, nil, 706, 706, 706, 706, 706, 706,
- 706, 706, 706, 706, 706, 706, 706, 706, nil, 706,
- 706, nil, 706, 706, 706, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 706, nil, nil, 706, nil, nil,
- 706, 706, nil, nil, 706, nil, 706, nil, nil, nil,
- 706, nil, nil, nil, nil, nil, nil, nil, 706, nil,
- nil, nil, nil, 706, 706, 706, 706, 706, 706, nil,
- nil, nil, 706, 706, 724, 724, 724, 724, 724, 724,
- nil, nil, nil, 724, 724, nil, nil, nil, 724, nil,
- 724, 724, 724, 724, 724, 724, 724, nil, nil, nil,
- nil, 724, 724, 724, 724, 724, 724, 724, nil, nil,
- 724, nil, nil, nil, nil, nil, 724, 724, 724, 724,
- 724, 724, 724, 724, 724, 724, 724, 724, 724, 724,
- nil, 724, 724, nil, 724, 724, 724, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 724, nil, nil, 724,
- nil, nil, 724, 724, nil, nil, 724, nil, 724, nil,
- nil, nil, 724, nil, nil, nil, nil, nil, nil, nil,
- 724, nil, nil, nil, nil, 724, 724, 724, 724, 724,
- 724, nil, nil, nil, 724, 724, 736, 736, 736, 736,
- 736, 736, nil, nil, nil, 736, 736, nil, nil, nil,
- 736, nil, 736, 736, 736, 736, 736, 736, 736, nil,
- nil, nil, nil, 736, 736, 736, 736, 736, 736, 736,
- nil, nil, 736, nil, nil, nil, nil, nil, 736, 736,
- 736, 736, 736, 736, 736, 736, 736, 736, 736, 736,
- 736, 736, nil, 736, 736, nil, 736, 736, 736, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 736, nil,
- nil, 736, nil, nil, 736, 736, nil, nil, 736, nil,
- 736, nil, nil, nil, 736, nil, nil, nil, nil, nil,
- nil, nil, 736, nil, nil, nil, nil, 736, 736, 736,
- 736, 736, 736, nil, nil, nil, 736, 736, 737, 737,
- 737, 737, 737, 737, nil, nil, nil, 737, 737, nil,
- nil, nil, 737, nil, 737, 737, 737, 737, 737, 737,
- 737, nil, nil, nil, nil, 737, 737, 737, 737, 737,
- 737, 737, nil, nil, 737, nil, nil, nil, nil, nil,
- 737, 737, 737, 737, 737, 737, 737, 737, 737, 737,
- 737, 737, 737, 737, nil, 737, 737, nil, 737, 737,
- 737, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 737, nil, nil, 737, nil, nil, 737, 737, nil, nil,
- 737, nil, 737, nil, nil, nil, 737, nil, nil, nil,
- nil, nil, nil, nil, 737, nil, nil, nil, nil, 737,
- 737, 737, 737, 737, 737, nil, nil, nil, 737, 737,
- 741, 741, 741, 741, 741, 741, nil, nil, nil, 741,
- 741, nil, nil, nil, 741, nil, 741, 741, 741, 741,
- 741, 741, 741, nil, nil, nil, nil, 741, 741, 741,
- 741, 741, 741, 741, nil, nil, 741, nil, nil, nil,
- nil, nil, 741, 741, 741, 741, 741, 741, 741, 741,
- 741, 741, 741, 741, 741, 741, nil, 741, 741, nil,
- 741, 741, 741, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 741, nil, nil, 741, nil, nil, 741, 741,
- nil, nil, 741, nil, 741, nil, nil, nil, 741, nil,
- nil, nil, nil, nil, nil, nil, 741, nil, nil, nil,
- nil, 741, 741, 741, 741, 741, 741, nil, nil, nil,
- 741, 741, 748, 748, 748, 748, 748, 748, nil, nil,
- nil, 748, 748, nil, nil, nil, 748, nil, 748, 748,
- 748, 748, 748, 748, 748, nil, nil, nil, nil, 748,
- 748, 748, 748, 748, 748, 748, nil, nil, 748, nil,
- nil, nil, nil, nil, 748, 748, 748, 748, 748, 748,
- 748, 748, 748, 748, 748, 748, 748, 748, nil, 748,
- 748, nil, 748, 748, 748, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 748, nil, nil, 748, nil, nil,
- 748, 748, nil, nil, 748, nil, 748, nil, nil, nil,
- 748, nil, nil, nil, nil, nil, nil, nil, 748, nil,
- nil, nil, nil, 748, 748, 748, 748, 748, 748, nil,
- nil, nil, 748, 748, 794, 794, 794, 794, 794, 794,
- nil, nil, nil, 794, 794, nil, nil, nil, 794, nil,
- 794, 794, 794, 794, 794, 794, 794, nil, nil, nil,
- nil, 794, 794, 794, 794, 794, 794, 794, nil, nil,
- 794, nil, nil, nil, nil, nil, 794, 794, 794, 794,
- 794, 794, 794, 794, 794, 794, 794, 794, 794, 794,
- nil, 794, 794, nil, 794, 794, 794, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 794, nil, nil, 794,
- nil, nil, 794, 794, nil, nil, 794, nil, 794, nil,
- nil, nil, 794, nil, nil, nil, nil, nil, nil, nil,
- 794, nil, nil, nil, nil, 794, 794, 794, 794, 794,
- 794, nil, nil, nil, 794, 794, 838, 838, 838, 838,
- 838, 838, nil, nil, nil, 838, 838, nil, nil, nil,
- 838, nil, 838, 838, 838, 838, 838, 838, 838, nil,
- nil, nil, nil, 838, 838, 838, 838, 838, 838, 838,
- nil, nil, 838, nil, nil, nil, nil, nil, 838, 838,
- 838, 838, 838, 838, 838, 838, 838, 838, 838, 838,
- 838, 838, nil, 838, 838, nil, 838, 838, 838, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 838, nil,
- nil, 838, nil, nil, 838, 838, nil, nil, 838, nil,
- 838, nil, nil, nil, 838, nil, nil, nil, nil, nil,
- nil, nil, 838, nil, nil, nil, nil, 838, 838, 838,
- 838, 838, 838, nil, nil, nil, 838, 838, 845, 845,
- 845, 845, 845, 845, nil, nil, nil, 845, 845, nil,
- nil, nil, 845, nil, 845, 845, 845, 845, 845, 845,
- 845, nil, nil, nil, nil, 845, 845, 845, 845, 845,
- 845, 845, nil, nil, 845, nil, nil, nil, nil, nil,
- 845, 845, 845, 845, 845, 845, 845, 845, 845, 845,
- 845, 845, 845, 845, nil, 845, 845, nil, 845, 845,
- 845, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 845, nil, nil, 845, nil, nil, 845, 845, nil, nil,
- 845, nil, 845, nil, nil, nil, 845, nil, nil, nil,
- nil, nil, nil, nil, 845, nil, nil, nil, nil, 845,
- 845, 845, 845, 845, 845, nil, nil, nil, 845, 845,
- 852, 852, 852, 852, 852, 852, nil, nil, nil, 852,
- 852, nil, nil, nil, 852, nil, 852, 852, 852, 852,
- 852, 852, 852, nil, nil, nil, nil, 852, 852, 852,
- 852, 852, 852, 852, nil, nil, 852, nil, nil, nil,
- nil, nil, 852, 852, 852, 852, 852, 852, 852, 852,
- 852, 852, 852, 852, 852, 852, nil, 852, 852, nil,
- 852, 852, 852, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 852, nil, nil, 852, nil, nil, 852, 852,
- nil, nil, 852, nil, 852, nil, nil, nil, 852, nil,
- nil, nil, nil, nil, nil, nil, 852, nil, nil, nil,
- nil, 852, 852, 852, 852, 852, 852, nil, nil, nil,
- 852, 852, 5, 5, 5, 5, 5, nil, nil, nil,
- 5, 5, nil, nil, nil, 5, nil, 5, 5, 5,
- 5, 5, 5, 5, nil, nil, nil, nil, 5, 5,
- 5, 5, 5, 5, 5, nil, nil, 5, nil, nil,
- nil, nil, nil, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, nil, 5, 5,
- nil, 5, 5, 5, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 5, nil, nil, 5, nil, nil, 5,
- 5, nil, nil, 5, nil, 5, nil, nil, nil, 5,
- nil, nil, nil, nil, nil, nil, nil, 5, nil, nil,
- nil, nil, 5, 5, 5, 5, 5, 5, nil, nil,
- nil, 5, 5, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, nil, nil, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, nil,
- nil, nil, nil, nil, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, nil, 6, nil, nil, nil, nil, nil,
- nil, nil, 6, 6, nil, 6, 6, 6, 6, nil,
- 6, 6, nil, nil, 6, nil, nil, nil, nil, 6,
- 6, 6, 6, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 6, 6, nil, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, nil, 6,
- nil, nil, 6, 6, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 6, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, nil, nil,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- nil, nil, nil, nil, nil, 7, 7, 7, 7, 7,
- 7, 7, 7, nil, nil, 7, nil, nil, nil, nil,
- nil, nil, nil, 7, 7, nil, 7, 7, 7, 7,
- nil, 7, 7, nil, nil, 7, nil, nil, nil, nil,
- 7, 7, 7, 7, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 7, 7, nil,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, nil,
- 7, nil, nil, 7, 7, nil, nil, 17, 17, 17,
- nil, 17, nil, nil, 7, 17, 17, nil, nil, nil,
- 17, nil, 17, 17, 17, 17, 17, 17, 17, nil,
- nil, nil, nil, 17, 17, 17, 17, 17, 17, 17,
- nil, nil, 17, nil, nil, nil, nil, nil, nil, 17,
- nil, nil, 17, 17, 17, 17, 17, 17, 17, 17,
- 17, 17, nil, 17, 17, nil, 17, 17, 17, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 17, nil,
- nil, 17, nil, nil, 17, 17, nil, nil, 17, nil,
- nil, nil, nil, nil, 17, nil, nil, nil, nil, nil,
- nil, nil, 17, nil, nil, nil, nil, 17, 17, 17,
- 17, 17, 17, nil, nil, nil, 17, 17, 18, 18,
- 18, nil, 18, nil, nil, nil, 18, 18, nil, nil,
- nil, 18, nil, 18, 18, 18, 18, 18, 18, 18,
- nil, nil, nil, nil, 18, 18, 18, 18, 18, 18,
- 18, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 18, nil, nil, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 18, nil, 18, 18, nil, 18, 18, 18,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 18,
- nil, nil, 18, nil, nil, 18, 18, nil, nil, 18,
- nil, nil, nil, nil, nil, 18, nil, nil, nil, nil,
- nil, nil, nil, 18, nil, nil, nil, nil, 18, 18,
- 18, 18, 18, 18, nil, nil, nil, 18, 18, 22,
- 22, 22, nil, 22, nil, nil, nil, 22, 22, nil,
- nil, nil, 22, nil, 22, 22, 22, 22, 22, 22,
- 22, nil, nil, nil, nil, 22, 22, 22, 22, 22,
- 22, 22, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 22, nil, nil, 22, 22, 22, 22, 22, 22,
- 22, 22, 22, 22, nil, 22, 22, nil, 22, 22,
- 22, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 22, nil, nil, 22, nil, nil, 22, 22, nil, nil,
- 22, nil, 22, nil, 22, nil, 22, nil, nil, nil,
- nil, nil, nil, nil, 22, nil, nil, nil, nil, 22,
- 22, 22, 22, 22, 22, nil, nil, nil, 22, 22,
- 23, 23, 23, nil, 23, nil, nil, nil, 23, 23,
- nil, nil, nil, 23, nil, 23, 23, 23, 23, 23,
- 23, 23, nil, nil, nil, nil, 23, 23, 23, 23,
- 23, 23, 23, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 23, nil, nil, 23, 23, 23, 23, 23,
- 23, 23, 23, 23, 23, nil, 23, 23, nil, 23,
- 23, 23, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 23, nil, nil, 23, nil, nil, 23, 23, nil,
- nil, 23, nil, 23, nil, 23, nil, 23, nil, nil,
- nil, nil, nil, nil, nil, 23, nil, nil, nil, nil,
- 23, 23, 23, 23, 23, 23, nil, nil, nil, 23,
- 23, 24, 24, 24, nil, 24, nil, nil, nil, 24,
- 24, nil, nil, nil, 24, nil, 24, 24, 24, 24,
- 24, 24, 24, nil, nil, nil, nil, 24, 24, 24,
- 24, 24, 24, 24, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 24, nil, nil, 24, 24, 24, 24,
- 24, 24, 24, 24, 24, 24, nil, 24, 24, nil,
- 24, 24, 24, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 24, nil, nil, 24, nil, nil, 24, 24,
- nil, nil, 24, nil, 24, nil, 24, nil, 24, nil,
- nil, nil, nil, nil, nil, nil, 24, nil, nil, nil,
- nil, 24, 24, 24, 24, 24, 24, nil, nil, nil,
- 24, 24, 27, 27, 27, nil, 27, nil, nil, nil,
- 27, 27, nil, nil, nil, 27, nil, 27, 27, 27,
- 27, 27, 27, 27, nil, nil, nil, nil, 27, 27,
- 27, 27, 27, 27, 27, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 27, nil, nil, 27, 27, 27,
- 27, 27, 27, 27, 27, 27, 27, nil, 27, 27,
- nil, 27, 27, 27, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 27, nil, nil, 27, 27, nil, 27,
- 27, nil, nil, 27, nil, 27, nil, 27, nil, 27,
- nil, nil, nil, nil, nil, nil, nil, 27, nil, nil,
- nil, nil, 27, 27, 27, 27, 27, 27, nil, nil,
- nil, 27, 27, 28, 28, 28, nil, 28, nil, nil,
- nil, 28, 28, nil, nil, nil, 28, nil, 28, 28,
- 28, 28, 28, 28, 28, nil, nil, nil, nil, 28,
- 28, 28, 28, 28, 28, 28, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 28, nil, nil, 28, 28,
- 28, 28, 28, 28, 28, 28, 28, 28, nil, 28,
- 28, nil, 28, 28, 28, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 28, nil, nil, 28, 28, nil,
- 28, 28, nil, nil, 28, nil, 28, nil, 28, nil,
- 28, nil, nil, nil, nil, nil, nil, nil, 28, nil,
- nil, nil, nil, 28, 28, 28, 28, 28, 28, nil,
- nil, nil, 28, 28, 31, 31, 31, nil, 31, nil,
- nil, nil, 31, 31, nil, nil, nil, 31, nil, 31,
- 31, 31, 31, 31, 31, 31, nil, nil, nil, nil,
- 31, 31, 31, 31, 31, 31, 31, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 31, nil, nil, 31,
- 31, 31, 31, 31, 31, 31, 31, 31, 31, nil,
- 31, 31, nil, nil, nil, 31, nil, 828, 828, 828,
- 828, 828, 828, 828, 828, 828, 828, 828, nil, 828,
- 828, nil, nil, 828, 828, 31, nil, nil, 31, nil,
- nil, 31, 31, nil, nil, 31, nil, 31, nil, 828,
- nil, 828, nil, 828, 828, 828, 828, 828, 828, 828,
- nil, 828, nil, nil, 31, 31, 31, 31, 31, 31,
- nil, nil, nil, 31, 31, 32, 32, 32, 828, 32,
- 828, nil, nil, 32, 32, nil, nil, nil, 32, nil,
- 32, 32, 32, 32, 32, 32, 32, nil, nil, nil,
- nil, 32, 32, 32, 32, 32, 32, 32, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 32, nil, nil,
- 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
- nil, 32, 32, nil, nil, nil, 32, nil, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, nil,
- 19, 19, nil, nil, 19, 19, 32, nil, nil, 32,
- nil, nil, 32, 32, nil, nil, 32, nil, nil, nil,
- 19, nil, 19, nil, 19, 19, 19, 19, 19, 19,
- 19, nil, 19, nil, nil, 32, 32, 32, 32, 32,
- 32, nil, nil, nil, 32, 32, 38, 38, 38, 19,
- 38, nil, nil, nil, 38, 38, nil, nil, nil, 38,
- nil, 38, 38, 38, 38, 38, 38, 38, nil, nil,
- nil, nil, 38, 38, 38, 38, 38, 38, 38, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 38, nil,
- nil, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, nil, 38, 38, nil, 38, 38, 38, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 38, nil, nil,
- 38, nil, nil, 38, 38, nil, nil, 38, nil, nil,
- nil, nil, nil, 38, nil, nil, nil, nil, nil, nil,
- nil, 38, nil, nil, nil, nil, 38, 38, 38, 38,
- 38, 38, nil, nil, nil, 38, 38, 39, 39, 39,
- nil, 39, nil, nil, nil, 39, 39, nil, nil, nil,
- 39, nil, 39, 39, 39, 39, 39, 39, 39, nil,
- nil, nil, nil, 39, 39, 39, 39, 39, 39, 39,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 39,
- nil, nil, 39, 39, 39, 39, 39, 39, 39, 39,
- 39, 39, nil, 39, 39, nil, 39, 39, 39, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 39, nil,
- nil, 39, nil, nil, 39, 39, nil, nil, 39, nil,
- nil, nil, nil, nil, 39, nil, nil, nil, nil, nil,
- nil, nil, 39, nil, nil, nil, nil, 39, 39, 39,
- 39, 39, 39, nil, nil, nil, 39, 39, 40, 40,
- 40, nil, 40, nil, nil, nil, 40, 40, nil, nil,
- nil, 40, nil, 40, 40, 40, 40, 40, 40, 40,
- nil, nil, nil, nil, 40, 40, 40, 40, 40, 40,
- 40, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 40, nil, nil, 40, 40, 40, 40, 40, 40, 40,
- 40, 40, 40, nil, 40, 40, nil, 40, 40, 40,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 40,
- nil, nil, 40, nil, nil, 40, 40, nil, nil, 40,
- nil, nil, nil, nil, nil, 40, nil, nil, nil, nil,
- nil, nil, nil, 40, nil, nil, nil, nil, 40, 40,
- 40, 40, 40, 40, nil, nil, nil, 40, 40, 52,
- 52, 52, nil, 52, nil, nil, nil, 52, 52, nil,
- nil, nil, 52, nil, 52, 52, 52, 52, 52, 52,
- 52, nil, nil, nil, nil, 52, 52, 52, 52, 52,
- 52, 52, nil, nil, 52, nil, nil, nil, nil, nil,
- nil, 52, nil, nil, 52, 52, 52, 52, 52, 52,
- 52, 52, 52, 52, nil, 52, 52, nil, 52, 52,
- 52, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 52, nil, nil, 52, nil, nil, 52, 52, nil, nil,
- 52, nil, nil, nil, nil, nil, 52, nil, nil, nil,
- nil, nil, nil, nil, 52, nil, nil, nil, nil, 52,
- 52, 52, 52, 52, 52, nil, nil, nil, 52, 52,
- 53, 53, 53, nil, 53, nil, nil, nil, 53, 53,
- nil, nil, nil, 53, nil, 53, 53, 53, 53, 53,
- 53, 53, nil, nil, nil, nil, 53, 53, 53, 53,
- 53, 53, 53, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 53, nil, nil, 53, 53, 53, 53, 53,
- 53, 53, 53, 53, 53, nil, 53, 53, nil, 53,
- 53, 53, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 53, nil, nil, 53, nil, nil, 53, 53, nil,
- nil, 53, nil, 53, nil, nil, nil, 53, nil, nil,
- nil, nil, nil, nil, nil, 53, nil, nil, nil, nil,
- 53, 53, 53, 53, 53, 53, nil, nil, nil, 53,
- 53, 54, 54, 54, nil, 54, nil, nil, nil, 54,
- 54, nil, nil, nil, 54, nil, 54, 54, 54, 54,
- 54, 54, 54, nil, nil, nil, nil, 54, 54, 54,
- 54, 54, 54, 54, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 54, nil, nil, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, nil, 54, 54, nil,
- 54, 54, 54, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 54, nil, nil, 54, nil, nil, 54, 54,
- nil, nil, 54, nil, nil, nil, nil, nil, 54, nil,
- nil, nil, nil, nil, nil, nil, 54, nil, nil, nil,
- nil, 54, 54, 54, 54, 54, 54, nil, nil, nil,
- 54, 54, 56, 56, 56, nil, 56, nil, nil, nil,
- 56, 56, nil, nil, nil, 56, nil, 56, 56, 56,
- 56, 56, 56, 56, nil, nil, nil, nil, 56, 56,
- 56, 56, 56, 56, 56, nil, nil, 56, nil, nil,
- nil, nil, nil, nil, 56, nil, nil, 56, 56, 56,
- 56, 56, 56, 56, 56, 56, 56, nil, 56, 56,
- nil, 56, 56, 56, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 56, nil, nil, 56, nil, nil, 56,
- 56, nil, nil, 56, nil, nil, nil, nil, nil, 56,
- nil, nil, nil, nil, nil, nil, nil, 56, nil, nil,
- nil, nil, 56, 56, 56, 56, 56, 56, nil, nil,
- nil, 56, 56, 57, 57, 57, nil, 57, nil, nil,
- nil, 57, 57, nil, nil, nil, 57, nil, 57, 57,
- 57, 57, 57, 57, 57, nil, nil, nil, nil, 57,
- 57, 57, 57, 57, 57, 57, nil, nil, 57, nil,
- nil, nil, nil, nil, nil, 57, nil, nil, 57, 57,
- 57, 57, 57, 57, 57, 57, 57, 57, nil, 57,
- 57, nil, 57, 57, 57, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 57, nil, nil, 57, nil, nil,
- 57, 57, nil, nil, 57, nil, nil, nil, nil, nil,
- 57, nil, nil, nil, nil, nil, nil, nil, 57, nil,
- nil, nil, nil, 57, 57, 57, 57, 57, 57, nil,
- nil, nil, 57, 57, 61, 61, 61, nil, 61, nil,
- nil, nil, 61, 61, nil, nil, nil, 61, nil, 61,
- 61, 61, 61, 61, 61, 61, nil, nil, nil, nil,
- 61, 61, 61, 61, 61, 61, 61, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 61, nil, nil, 61,
- 61, 61, 61, 61, 61, 61, 61, 61, 61, nil,
- 61, 61, nil, nil, nil, 61, nil, 237, 237, 237,
- 237, 237, 237, 237, 237, 237, 237, 237, nil, 237,
- 237, nil, nil, 237, 237, 61, nil, nil, 61, nil,
- nil, 61, 61, nil, nil, 61, nil, 61, nil, 237,
- nil, 237, nil, 237, 237, 237, 237, 237, 237, 237,
- nil, 237, nil, nil, 61, 61, 61, 61, 61, 61,
- nil, nil, nil, 61, 61, 62, 62, 62, 237, 62,
- nil, nil, nil, 62, 62, nil, nil, nil, 62, nil,
- 62, 62, 62, 62, 62, 62, 62, nil, nil, nil,
- nil, 62, 62, 62, 62, 62, 62, 62, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 62, nil, nil,
- 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
- nil, 62, 62, nil, nil, nil, 62, nil, 374, 374,
- 374, 374, 374, 374, 374, 374, 374, 374, 374, nil,
- 374, 374, nil, 62, 374, 374, 62, nil, nil, 62,
- nil, nil, 62, 62, nil, nil, 62, nil, nil, nil,
- 374, nil, 374, nil, 374, 374, 374, 374, 374, 374,
- 374, nil, 374, nil, nil, 62, 62, 62, 62, 62,
- 62, nil, nil, nil, 62, 62, 63, 63, 63, 374,
- 63, nil, nil, nil, 63, 63, nil, nil, nil, 63,
- nil, 63, 63, 63, 63, 63, 63, 63, nil, nil,
- nil, nil, 63, 63, 63, 63, 63, 63, 63, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 63, nil,
- nil, 63, 63, 63, 63, 63, 63, 63, 63, 63,
- 63, nil, 63, 63, nil, nil, nil, 63, nil, 388,
- 388, 388, 388, 388, 388, 388, 388, 388, 388, 388,
- nil, 388, 388, nil, nil, 388, 388, 63, nil, nil,
- 63, nil, nil, 63, 63, nil, nil, 63, nil, nil,
- nil, 388, nil, 388, nil, 388, 388, 388, 388, 388,
- 388, 388, nil, 388, nil, nil, 63, 63, 63, 63,
- 63, 63, nil, nil, nil, 63, 63, 82, 82, 82,
- 388, 82, nil, nil, nil, 82, 82, nil, nil, nil,
- 82, nil, 82, 82, 82, 82, 82, 82, 82, nil,
- 82, nil, nil, 82, 82, 82, 82, 82, 82, 82,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 82,
- nil, nil, 82, 82, 82, 82, 82, 82, 82, 82,
- 82, 82, nil, 82, 82, nil, 82, 82, 82, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 82, nil,
- nil, 82, 82, nil, 82, 82, nil, nil, 82, nil,
- 82, nil, 82, nil, 82, nil, nil, nil, nil, nil,
- nil, nil, 82, nil, 82, nil, nil, 82, 82, 82,
- 82, 82, 82, nil, nil, nil, 82, 82, 86, 86,
- 86, nil, 86, nil, nil, nil, 86, 86, nil, nil,
- nil, 86, nil, 86, 86, 86, 86, 86, 86, 86,
- nil, 86, nil, nil, 86, 86, 86, 86, 86, 86,
- 86, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 86, nil, nil, 86, 86, 86, 86, 86, 86, 86,
- 86, 86, 86, nil, 86, 86, nil, 86, 86, 86,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 86,
- nil, nil, 86, 86, nil, 86, 86, nil, nil, 86,
- nil, 86, nil, 86, nil, 86, nil, nil, nil, nil,
- nil, nil, nil, 86, nil, 86, nil, nil, 86, 86,
- 86, 86, 86, 86, nil, nil, nil, 86, 86, 101,
- 101, 101, nil, 101, nil, nil, nil, 101, 101, nil,
- nil, nil, 101, nil, 101, 101, 101, 101, 101, 101,
- 101, nil, nil, nil, nil, 101, 101, 101, 101, 101,
- 101, 101, nil, nil, 101, nil, nil, nil, nil, nil,
- nil, 101, nil, nil, 101, 101, 101, 101, 101, 101,
- 101, 101, 101, 101, nil, 101, 101, nil, 101, 101,
- 101, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 101, nil, nil, 101, nil, nil, 101, 101, nil, nil,
- 101, nil, nil, nil, nil, nil, 101, nil, nil, nil,
- nil, nil, nil, nil, 101, nil, nil, nil, nil, 101,
- 101, 101, 101, 101, 101, nil, nil, nil, 101, 101,
- 102, 102, 102, nil, 102, nil, nil, nil, 102, 102,
- nil, nil, nil, 102, nil, 102, 102, 102, 102, 102,
- 102, 102, nil, nil, nil, nil, 102, 102, 102, 102,
- 102, 102, 102, nil, nil, 102, nil, nil, nil, nil,
- nil, nil, 102, nil, nil, 102, 102, 102, 102, 102,
- 102, 102, 102, 102, 102, nil, 102, 102, nil, 102,
- 102, 102, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 102, nil, nil, 102, nil, nil, 102, 102, nil,
- nil, 102, nil, nil, nil, nil, nil, 102, nil, nil,
- nil, nil, nil, nil, nil, 102, nil, nil, nil, nil,
- 102, 102, 102, 102, 102, 102, nil, nil, nil, 102,
- 102, 103, 103, 103, nil, 103, nil, nil, nil, 103,
- 103, nil, nil, nil, 103, nil, 103, 103, 103, 103,
- 103, 103, 103, nil, nil, nil, nil, 103, 103, 103,
- 103, 103, 103, 103, nil, nil, 103, nil, nil, nil,
- nil, nil, nil, 103, nil, nil, 103, 103, 103, 103,
- 103, 103, 103, 103, 103, 103, nil, 103, 103, nil,
- 103, 103, 103, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 103, nil, nil, 103, nil, nil, 103, 103,
- nil, nil, 103, nil, nil, nil, nil, nil, 103, nil,
- nil, nil, nil, nil, nil, nil, 103, nil, nil, nil,
- nil, 103, 103, 103, 103, 103, 103, nil, nil, nil,
- 103, 103, 104, 104, 104, nil, 104, nil, nil, nil,
- 104, 104, nil, nil, nil, 104, nil, 104, 104, 104,
- 104, 104, 104, 104, nil, nil, nil, nil, 104, 104,
- 104, 104, 104, 104, 104, nil, nil, 104, nil, nil,
- nil, nil, nil, nil, 104, nil, nil, 104, 104, 104,
- 104, 104, 104, 104, 104, 104, 104, nil, 104, 104,
- nil, 104, 104, 104, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 104, nil, nil, 104, nil, nil, 104,
- 104, nil, nil, 104, nil, nil, nil, nil, nil, 104,
- nil, nil, nil, nil, nil, nil, nil, 104, nil, nil,
- nil, nil, 104, 104, 104, 104, 104, 104, nil, nil,
- nil, 104, 104, 105, 105, 105, 105, 105, nil, nil,
- nil, 105, 105, nil, nil, nil, 105, nil, 105, 105,
- 105, 105, 105, 105, 105, nil, nil, nil, nil, 105,
- 105, 105, 105, 105, 105, 105, nil, nil, 105, nil,
- nil, nil, nil, nil, 105, 105, 105, 105, 105, 105,
- 105, 105, 105, 105, 105, 105, 105, 105, nil, 105,
- 105, nil, 105, 105, 105, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 105, nil, nil, 105, nil, nil,
- 105, 105, nil, nil, 105, nil, 105, nil, nil, nil,
- 105, nil, nil, nil, nil, nil, nil, nil, 105, nil,
- nil, nil, nil, 105, 105, 105, 105, 105, 105, nil,
- nil, nil, 105, 105, 188, 188, 188, nil, 188, nil,
- nil, nil, 188, 188, nil, nil, nil, 188, nil, 188,
- 188, 188, 188, 188, 188, 188, nil, nil, nil, nil,
- 188, 188, 188, 188, 188, 188, 188, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 188, nil, nil, 188,
- 188, 188, 188, 188, 188, 188, 188, 188, 188, nil,
- 188, 188, nil, 188, 188, 188, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 188, nil, nil, 188, nil,
- nil, 188, 188, nil, nil, 188, nil, 188, nil, nil,
- nil, 188, nil, nil, nil, nil, nil, nil, nil, 188,
- nil, nil, nil, nil, 188, 188, 188, 188, 188, 188,
- nil, nil, nil, 188, 188, 189, 189, 189, nil, 189,
- nil, nil, nil, 189, 189, nil, nil, nil, 189, nil,
- 189, 189, 189, 189, 189, 189, 189, nil, nil, nil,
- nil, 189, 189, 189, 189, 189, 189, 189, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 189, nil, nil,
- 189, 189, 189, 189, 189, 189, 189, 189, 189, 189,
- nil, 189, 189, nil, 189, 189, 189, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 189, nil, nil, 189,
- nil, nil, 189, 189, nil, nil, 189, nil, 189, nil,
- nil, nil, 189, nil, nil, nil, nil, nil, nil, nil,
- 189, nil, nil, nil, nil, 189, 189, 189, 189, 189,
- 189, nil, nil, nil, 189, 189, 190, 190, 190, nil,
- 190, nil, nil, nil, 190, 190, nil, nil, nil, 190,
- nil, 190, 190, 190, 190, 190, 190, 190, nil, nil,
- nil, nil, 190, 190, 190, 190, 190, 190, 190, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 190, nil,
- nil, 190, 190, 190, 190, 190, 190, 190, 190, 190,
- 190, nil, 190, 190, nil, 190, 190, 190, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 190, nil, nil,
- 190, nil, nil, 190, 190, nil, nil, 190, nil, nil,
- nil, nil, nil, 190, nil, nil, nil, nil, nil, nil,
- nil, 190, nil, nil, nil, nil, 190, 190, 190, 190,
- 190, 190, nil, nil, nil, 190, 190, 191, 191, 191,
- nil, 191, nil, nil, nil, 191, 191, nil, nil, nil,
- 191, nil, 191, 191, 191, 191, 191, 191, 191, nil,
- nil, nil, nil, 191, 191, 191, 191, 191, 191, 191,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 191,
- nil, nil, 191, 191, 191, 191, 191, 191, 191, 191,
- 191, 191, nil, 191, 191, nil, 191, 191, 191, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 191, nil,
- nil, 191, nil, nil, 191, 191, nil, nil, 191, nil,
- 191, nil, nil, nil, 191, nil, nil, nil, nil, nil,
- nil, nil, 191, nil, nil, nil, nil, 191, 191, 191,
- 191, 191, 191, nil, nil, nil, 191, 191, 194, 194,
- 194, nil, 194, nil, nil, nil, 194, 194, nil, nil,
- nil, 194, nil, 194, 194, 194, 194, 194, 194, 194,
- nil, nil, nil, nil, 194, 194, 194, 194, 194, 194,
- 194, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 194, nil, nil, 194, 194, 194, 194, 194, 194, 194,
- 194, 194, 194, nil, 194, 194, nil, 194, 194, 194,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 194,
- nil, nil, 194, nil, nil, 194, 194, nil, nil, 194,
- nil, nil, nil, nil, nil, 194, nil, nil, nil, nil,
- nil, nil, nil, 194, nil, nil, nil, nil, 194, 194,
- 194, 194, 194, 194, nil, nil, nil, 194, 194, 195,
- 195, 195, nil, 195, nil, nil, nil, 195, 195, nil,
- nil, nil, 195, nil, 195, 195, 195, 195, 195, 195,
- 195, nil, nil, nil, nil, 195, 195, 195, 195, 195,
- 195, 195, nil, nil, 195, nil, nil, nil, nil, nil,
- nil, 195, nil, nil, 195, 195, 195, 195, 195, 195,
- 195, 195, 195, 195, nil, 195, 195, nil, 195, 195,
- 195, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 195, nil, nil, 195, nil, nil, 195, 195, nil, nil,
- 195, nil, nil, nil, nil, nil, 195, nil, nil, nil,
- nil, nil, nil, nil, 195, nil, nil, nil, nil, 195,
- 195, 195, 195, 195, 195, nil, nil, nil, 195, 195,
- 196, 196, 196, nil, 196, nil, nil, nil, 196, 196,
- nil, nil, nil, 196, nil, 196, 196, 196, 196, 196,
- 196, 196, nil, nil, nil, nil, 196, 196, 196, 196,
- 196, 196, 196, nil, nil, 196, nil, nil, nil, nil,
- nil, nil, 196, nil, nil, 196, 196, 196, 196, 196,
- 196, 196, 196, 196, 196, nil, 196, 196, nil, 196,
- 196, 196, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 196, nil, nil, 196, nil, nil, 196, 196, nil,
- nil, 196, nil, nil, nil, nil, nil, 196, nil, nil,
- nil, nil, nil, nil, nil, 196, nil, nil, nil, nil,
- 196, 196, 196, 196, 196, 196, nil, nil, nil, 196,
- 196, 207, 207, 207, nil, 207, nil, nil, nil, 207,
- 207, nil, nil, nil, 207, nil, 207, 207, 207, 207,
- 207, 207, 207, nil, nil, nil, nil, 207, 207, 207,
- 207, 207, 207, 207, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 207, nil, nil, 207, 207, 207, 207,
- 207, 207, 207, 207, 207, 207, nil, 207, 207, nil,
- 207, 207, 207, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 207, nil, nil, 207, nil, nil, 207, 207,
- nil, nil, 207, nil, nil, nil, nil, nil, 207, nil,
- nil, nil, nil, nil, nil, nil, 207, nil, nil, nil,
- nil, 207, 207, 207, 207, 207, 207, nil, nil, nil,
- 207, 207, 208, 208, 208, nil, 208, nil, nil, nil,
- 208, 208, nil, nil, nil, 208, nil, 208, 208, 208,
- 208, 208, 208, 208, nil, nil, nil, nil, 208, 208,
- 208, 208, 208, 208, 208, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 208, nil, nil, 208, 208, 208,
- 208, 208, 208, 208, 208, 208, 208, nil, 208, 208,
- nil, 208, 208, 208, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 208, nil, nil, 208, nil, nil, 208,
- 208, nil, nil, 208, nil, nil, nil, nil, nil, 208,
- nil, nil, nil, nil, nil, nil, nil, 208, nil, nil,
- nil, nil, 208, 208, 208, 208, 208, 208, nil, nil,
- nil, 208, 208, 209, 209, 209, nil, 209, nil, nil,
- nil, 209, 209, nil, nil, nil, 209, nil, 209, 209,
- 209, 209, 209, 209, 209, nil, nil, nil, nil, 209,
- 209, 209, 209, 209, 209, 209, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 209, nil, nil, 209, 209,
- 209, 209, 209, 209, 209, 209, 209, 209, nil, 209,
- 209, nil, 209, 209, 209, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 209, nil, nil, 209, nil, nil,
- 209, 209, nil, nil, 209, nil, nil, nil, nil, nil,
- 209, nil, nil, nil, nil, nil, nil, nil, 209, nil,
- nil, nil, nil, 209, 209, 209, 209, 209, 209, nil,
- nil, nil, 209, 209, 210, 210, 210, nil, 210, nil,
- nil, nil, 210, 210, nil, nil, nil, 210, nil, 210,
- 210, 210, 210, 210, 210, 210, nil, nil, nil, nil,
- 210, 210, 210, 210, 210, 210, 210, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 210, nil, nil, 210,
- 210, 210, 210, 210, 210, 210, 210, 210, 210, nil,
- 210, 210, nil, 210, 210, 210, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 210, nil, nil, 210, nil,
- nil, 210, 210, nil, nil, 210, nil, nil, nil, nil,
- nil, 210, nil, nil, nil, nil, nil, nil, nil, 210,
- nil, nil, nil, nil, 210, 210, 210, 210, 210, 210,
- nil, nil, nil, 210, 210, 211, 211, 211, nil, 211,
- nil, nil, nil, 211, 211, nil, nil, nil, 211, nil,
- 211, 211, 211, 211, 211, 211, 211, nil, nil, nil,
- nil, 211, 211, 211, 211, 211, 211, 211, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 211, nil, nil,
- 211, 211, 211, 211, 211, 211, 211, 211, 211, 211,
- nil, 211, 211, nil, 211, 211, 211, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 211, nil, nil, 211,
- nil, nil, 211, 211, nil, nil, 211, nil, nil, nil,
- nil, nil, 211, nil, nil, nil, nil, nil, nil, nil,
- 211, nil, nil, nil, nil, 211, 211, 211, 211, 211,
- 211, nil, nil, nil, 211, 211, 212, 212, 212, nil,
- 212, nil, nil, nil, 212, 212, nil, nil, nil, 212,
- nil, 212, 212, 212, 212, 212, 212, 212, nil, nil,
- nil, nil, 212, 212, 212, 212, 212, 212, 212, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 212, nil,
- nil, 212, 212, 212, 212, 212, 212, 212, 212, 212,
- 212, nil, 212, 212, nil, 212, 212, 212, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 212, nil, nil,
- 212, nil, nil, 212, 212, nil, nil, 212, nil, nil,
- nil, nil, nil, 212, nil, nil, nil, nil, nil, nil,
- nil, 212, nil, nil, nil, nil, 212, 212, 212, 212,
- 212, 212, nil, nil, nil, 212, 212, 213, 213, 213,
- nil, 213, nil, nil, nil, 213, 213, nil, nil, nil,
- 213, nil, 213, 213, 213, 213, 213, 213, 213, nil,
- nil, nil, nil, 213, 213, 213, 213, 213, 213, 213,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 213,
- nil, nil, 213, 213, 213, 213, 213, 213, 213, 213,
- 213, 213, nil, 213, 213, nil, 213, 213, 213, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 213, nil,
- nil, 213, nil, nil, 213, 213, nil, nil, 213, nil,
- nil, nil, nil, nil, 213, nil, nil, nil, nil, nil,
- nil, nil, 213, nil, nil, nil, nil, 213, 213, 213,
- 213, 213, 213, nil, nil, nil, 213, 213, 214, 214,
- 214, nil, 214, nil, nil, nil, 214, 214, nil, nil,
- nil, 214, nil, 214, 214, 214, 214, 214, 214, 214,
- nil, nil, nil, nil, 214, 214, 214, 214, 214, 214,
- 214, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 214, nil, nil, 214, 214, 214, 214, 214, 214, 214,
- 214, 214, 214, nil, 214, 214, nil, 214, 214, 214,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 214,
- nil, nil, 214, nil, nil, 214, 214, nil, nil, 214,
- nil, nil, nil, nil, nil, 214, nil, nil, nil, nil,
- nil, nil, nil, 214, nil, nil, nil, nil, 214, 214,
- 214, 214, 214, 214, nil, nil, nil, 214, 214, 215,
- 215, 215, nil, 215, nil, nil, nil, 215, 215, nil,
- nil, nil, 215, nil, 215, 215, 215, 215, 215, 215,
- 215, nil, nil, nil, nil, 215, 215, 215, 215, 215,
- 215, 215, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 215, nil, nil, 215, 215, 215, 215, 215, 215,
- 215, 215, 215, 215, nil, 215, 215, nil, 215, 215,
- 215, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 215, nil, nil, 215, nil, nil, 215, 215, nil, nil,
- 215, nil, nil, nil, nil, nil, 215, nil, nil, nil,
- nil, nil, nil, nil, 215, nil, nil, nil, nil, 215,
- 215, 215, 215, 215, 215, nil, nil, nil, 215, 215,
- 216, 216, 216, nil, 216, nil, nil, nil, 216, 216,
- nil, nil, nil, 216, nil, 216, 216, 216, 216, 216,
- 216, 216, nil, nil, nil, nil, 216, 216, 216, 216,
- 216, 216, 216, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 216, nil, nil, 216, 216, 216, 216, 216,
- 216, 216, 216, 216, 216, nil, 216, 216, nil, 216,
- 216, 216, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 216, nil, nil, 216, nil, nil, 216, 216, nil,
- nil, 216, nil, nil, nil, nil, nil, 216, nil, nil,
- nil, nil, nil, nil, nil, 216, nil, nil, nil, nil,
- 216, 216, 216, 216, 216, 216, nil, nil, nil, 216,
- 216, 217, 217, 217, nil, 217, nil, nil, nil, 217,
- 217, nil, nil, nil, 217, nil, 217, 217, 217, 217,
- 217, 217, 217, nil, nil, nil, nil, 217, 217, 217,
- 217, 217, 217, 217, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 217, nil, nil, 217, 217, 217, 217,
- 217, 217, 217, 217, 217, 217, nil, 217, 217, nil,
- 217, 217, 217, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 217, nil, nil, 217, nil, nil, 217, 217,
- nil, nil, 217, nil, nil, nil, nil, nil, 217, nil,
- nil, nil, nil, nil, nil, nil, 217, nil, nil, nil,
- nil, 217, 217, 217, 217, 217, 217, nil, nil, nil,
- 217, 217, 218, 218, 218, nil, 218, nil, nil, nil,
- 218, 218, nil, nil, nil, 218, nil, 218, 218, 218,
- 218, 218, 218, 218, nil, nil, nil, nil, 218, 218,
- 218, 218, 218, 218, 218, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 218, nil, nil, 218, 218, 218,
- 218, 218, 218, 218, 218, 218, 218, nil, 218, 218,
- nil, 218, 218, 218, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 218, nil, nil, 218, nil, nil, 218,
- 218, nil, nil, 218, nil, nil, nil, nil, nil, 218,
- nil, nil, nil, nil, nil, nil, nil, 218, nil, nil,
- nil, nil, 218, 218, 218, 218, 218, 218, nil, nil,
- nil, 218, 218, 219, 219, 219, nil, 219, nil, nil,
- nil, 219, 219, nil, nil, nil, 219, nil, 219, 219,
- 219, 219, 219, 219, 219, nil, nil, nil, nil, 219,
- 219, 219, 219, 219, 219, 219, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 219, nil, nil, 219, 219,
- 219, 219, 219, 219, 219, 219, 219, 219, nil, 219,
- 219, nil, 219, 219, 219, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 219, nil, nil, 219, nil, nil,
- 219, 219, nil, nil, 219, nil, nil, nil, nil, nil,
- 219, nil, nil, nil, nil, nil, nil, nil, 219, nil,
- nil, nil, nil, 219, 219, 219, 219, 219, 219, nil,
- nil, nil, 219, 219, 220, 220, 220, nil, 220, nil,
- nil, nil, 220, 220, nil, nil, nil, 220, nil, 220,
- 220, 220, 220, 220, 220, 220, nil, nil, nil, nil,
- 220, 220, 220, 220, 220, 220, 220, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 220, nil, nil, 220,
- 220, 220, 220, 220, 220, 220, 220, 220, 220, nil,
- 220, 220, nil, 220, 220, 220, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 220, nil, nil, 220, nil,
- nil, 220, 220, nil, nil, 220, nil, nil, nil, nil,
- nil, 220, nil, nil, nil, nil, nil, nil, nil, 220,
- nil, nil, nil, nil, 220, 220, 220, 220, 220, 220,
- nil, nil, nil, 220, 220, 221, 221, 221, nil, 221,
- nil, nil, nil, 221, 221, nil, nil, nil, 221, nil,
- 221, 221, 221, 221, 221, 221, 221, nil, nil, nil,
- nil, 221, 221, 221, 221, 221, 221, 221, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 221, nil, nil,
- 221, 221, 221, 221, 221, 221, 221, 221, 221, 221,
- nil, 221, 221, nil, 221, 221, 221, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 221, nil, nil, 221,
- nil, nil, 221, 221, nil, nil, 221, nil, nil, nil,
- nil, nil, 221, nil, nil, nil, nil, nil, nil, nil,
- 221, nil, nil, nil, nil, 221, 221, 221, 221, 221,
- 221, nil, nil, nil, 221, 221, 222, 222, 222, nil,
- 222, nil, nil, nil, 222, 222, nil, nil, nil, 222,
- nil, 222, 222, 222, 222, 222, 222, 222, nil, nil,
- nil, nil, 222, 222, 222, 222, 222, 222, 222, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 222, nil,
- nil, 222, 222, 222, 222, 222, 222, 222, 222, 222,
- 222, nil, 222, 222, nil, 222, 222, 222, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 222, nil, nil,
- 222, nil, nil, 222, 222, nil, nil, 222, nil, nil,
- nil, nil, nil, 222, nil, nil, nil, nil, nil, nil,
- nil, 222, nil, nil, nil, nil, 222, 222, 222, 222,
- 222, 222, nil, nil, nil, 222, 222, 223, 223, 223,
- nil, 223, nil, nil, nil, 223, 223, nil, nil, nil,
- 223, nil, 223, 223, 223, 223, 223, 223, 223, nil,
- nil, nil, nil, 223, 223, 223, 223, 223, 223, 223,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 223,
- nil, nil, 223, 223, 223, 223, 223, 223, 223, 223,
- 223, 223, nil, 223, 223, nil, 223, 223, 223, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 223, nil,
- nil, 223, nil, nil, 223, 223, nil, nil, 223, nil,
- nil, nil, nil, nil, 223, nil, nil, nil, nil, nil,
- nil, nil, 223, nil, nil, nil, nil, 223, 223, 223,
- 223, 223, 223, nil, nil, nil, 223, 223, 224, 224,
- 224, nil, 224, nil, nil, nil, 224, 224, nil, nil,
- nil, 224, nil, 224, 224, 224, 224, 224, 224, 224,
- nil, nil, nil, nil, 224, 224, 224, 224, 224, 224,
- 224, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 224, nil, nil, 224, 224, 224, 224, 224, 224, 224,
- 224, 224, 224, nil, 224, 224, nil, 224, 224, 224,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 224,
- nil, nil, 224, nil, nil, 224, 224, nil, nil, 224,
- nil, nil, nil, nil, nil, 224, nil, nil, nil, nil,
- nil, nil, nil, 224, nil, nil, nil, nil, 224, 224,
- 224, 224, 224, 224, nil, nil, nil, 224, 224, 225,
- 225, 225, nil, 225, nil, nil, nil, 225, 225, nil,
- nil, nil, 225, nil, 225, 225, 225, 225, 225, 225,
- 225, nil, nil, nil, nil, 225, 225, 225, 225, 225,
- 225, 225, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 225, nil, nil, 225, 225, 225, 225, 225, 225,
- 225, 225, 225, 225, nil, 225, 225, nil, 225, 225,
- 225, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 225, nil, nil, 225, nil, nil, 225, 225, nil, nil,
- 225, nil, nil, nil, nil, nil, 225, nil, nil, nil,
- nil, nil, nil, nil, 225, nil, nil, nil, nil, 225,
- 225, 225, 225, 225, 225, nil, nil, nil, 225, 225,
- 226, 226, 226, nil, 226, nil, nil, nil, 226, 226,
- nil, nil, nil, 226, nil, 226, 226, 226, 226, 226,
- 226, 226, nil, nil, nil, nil, 226, 226, 226, 226,
- 226, 226, 226, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 226, nil, nil, 226, 226, 226, 226, 226,
- 226, 226, 226, 226, 226, nil, 226, 226, nil, 226,
- 226, 226, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 226, nil, nil, 226, nil, nil, 226, 226, nil,
- nil, 226, nil, nil, nil, nil, nil, 226, nil, nil,
- nil, nil, nil, nil, nil, 226, nil, nil, nil, nil,
- 226, 226, 226, 226, 226, 226, nil, nil, nil, 226,
- 226, 227, 227, 227, nil, 227, nil, nil, nil, 227,
- 227, nil, nil, nil, 227, nil, 227, 227, 227, 227,
- 227, 227, 227, nil, nil, nil, nil, 227, 227, 227,
- 227, 227, 227, 227, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 227, nil, nil, 227, 227, 227, 227,
- 227, 227, 227, 227, 227, 227, nil, 227, 227, nil,
- 227, 227, 227, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 227, nil, nil, 227, nil, nil, 227, 227,
- nil, nil, 227, nil, nil, nil, nil, nil, 227, nil,
- nil, nil, nil, nil, nil, nil, 227, nil, nil, nil,
- nil, 227, 227, 227, 227, 227, 227, nil, nil, nil,
- 227, 227, 228, 228, 228, nil, 228, nil, nil, nil,
- 228, 228, nil, nil, nil, 228, nil, 228, 228, 228,
- 228, 228, 228, 228, nil, nil, nil, nil, 228, 228,
- 228, 228, 228, 228, 228, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 228, nil, nil, 228, 228, 228,
- 228, 228, 228, 228, 228, 228, 228, nil, 228, 228,
- nil, 228, 228, 228, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 228, nil, nil, 228, nil, nil, 228,
- 228, nil, nil, 228, nil, nil, nil, nil, nil, 228,
- nil, nil, nil, nil, nil, nil, nil, 228, nil, nil,
- nil, nil, 228, 228, 228, 228, 228, 228, nil, nil,
- nil, 228, 228, 229, 229, 229, nil, 229, nil, nil,
- nil, 229, 229, nil, nil, nil, 229, nil, 229, 229,
- 229, 229, 229, 229, 229, nil, nil, nil, nil, 229,
- 229, 229, 229, 229, 229, 229, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 229, nil, nil, 229, 229,
- 229, 229, 229, 229, 229, 229, 229, 229, nil, 229,
- 229, nil, 229, 229, 229, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 229, nil, nil, 229, nil, nil,
- 229, 229, nil, nil, 229, nil, nil, nil, nil, nil,
- 229, nil, nil, nil, nil, nil, nil, nil, 229, nil,
- nil, nil, nil, 229, 229, 229, 229, 229, 229, nil,
- nil, nil, 229, 229, 230, 230, 230, nil, 230, nil,
- nil, nil, 230, 230, nil, nil, nil, 230, nil, 230,
- 230, 230, 230, 230, 230, 230, nil, nil, nil, nil,
- 230, 230, 230, 230, 230, 230, 230, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 230, nil, nil, 230,
- 230, 230, 230, 230, 230, 230, 230, 230, 230, nil,
- 230, 230, nil, 230, 230, 230, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 230, nil, nil, 230, nil,
- nil, 230, 230, nil, nil, 230, nil, nil, nil, nil,
- nil, 230, nil, nil, nil, nil, nil, nil, nil, 230,
- nil, nil, nil, nil, 230, 230, 230, 230, 230, 230,
- nil, nil, nil, 230, 230, 231, 231, 231, nil, 231,
- nil, nil, nil, 231, 231, nil, nil, nil, 231, nil,
- 231, 231, 231, 231, 231, 231, 231, nil, nil, nil,
- nil, 231, 231, 231, 231, 231, 231, 231, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 231, nil, nil,
- 231, 231, 231, 231, 231, 231, 231, 231, 231, 231,
- nil, 231, 231, nil, 231, 231, 231, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 231, nil, nil, 231,
- nil, nil, 231, 231, nil, nil, 231, nil, nil, nil,
- nil, nil, 231, nil, nil, nil, nil, nil, nil, nil,
- 231, nil, nil, nil, nil, 231, 231, 231, 231, 231,
- 231, nil, nil, nil, 231, 231, 232, 232, 232, nil,
- 232, nil, nil, nil, 232, 232, nil, nil, nil, 232,
- nil, 232, 232, 232, 232, 232, 232, 232, nil, nil,
- nil, nil, 232, 232, 232, 232, 232, 232, 232, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 232, nil,
- nil, 232, 232, 232, 232, 232, 232, 232, 232, 232,
- 232, nil, 232, 232, nil, 232, 232, 232, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 232, nil, nil,
- 232, nil, nil, 232, 232, nil, nil, 232, nil, nil,
- nil, nil, nil, 232, nil, nil, nil, nil, nil, nil,
- nil, 232, nil, nil, nil, nil, 232, 232, 232, 232,
- 232, 232, nil, nil, nil, 232, 232, 233, 233, 233,
- nil, 233, nil, nil, nil, 233, 233, nil, nil, nil,
- 233, nil, 233, 233, 233, 233, 233, 233, 233, nil,
- nil, nil, nil, 233, 233, 233, 233, 233, 233, 233,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 233,
- nil, nil, 233, 233, 233, 233, 233, 233, 233, 233,
- 233, 233, nil, 233, 233, nil, 233, 233, 233, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 233, nil,
- nil, 233, nil, nil, 233, 233, nil, nil, 233, nil,
- nil, nil, nil, nil, 233, nil, nil, nil, nil, nil,
- nil, nil, 233, nil, nil, nil, nil, 233, 233, 233,
- 233, 233, 233, nil, nil, nil, 233, 233, 241, 241,
- 241, nil, 241, nil, nil, nil, 241, 241, nil, nil,
- nil, 241, nil, 241, 241, 241, 241, 241, 241, 241,
- nil, nil, nil, nil, 241, 241, 241, 241, 241, 241,
- 241, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 241, nil, nil, 241, 241, 241, 241, 241, 241, 241,
- 241, 241, 241, nil, 241, 241, nil, 241, 241, 241,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 241,
- nil, nil, 241, nil, nil, 241, 241, nil, nil, 241,
- nil, nil, nil, nil, nil, 241, nil, nil, nil, nil,
- nil, nil, nil, 241, nil, nil, nil, nil, 241, 241,
- 241, 241, 241, 241, nil, nil, nil, 241, 241, 243,
- 243, 243, nil, 243, nil, nil, nil, 243, 243, nil,
- nil, nil, 243, nil, 243, 243, 243, 243, 243, 243,
- 243, nil, nil, nil, nil, 243, 243, 243, 243, 243,
- 243, 243, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 243, nil, nil, 243, 243, 243, 243, 243, 243,
- 243, 243, 243, 243, nil, 243, 243, nil, 243, 243,
- 243, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 243, nil, nil, 243, nil, nil, 243, 243, nil, nil,
- 243, nil, nil, nil, nil, nil, 243, nil, nil, nil,
- nil, nil, nil, nil, 243, nil, nil, nil, nil, 243,
- 243, 243, 243, 243, 243, nil, nil, nil, 243, 243,
- 254, 254, 254, nil, 254, nil, nil, nil, 254, 254,
- nil, nil, nil, 254, nil, 254, 254, 254, 254, 254,
- 254, 254, nil, nil, nil, nil, 254, 254, 254, 254,
- 254, 254, 254, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 254, nil, nil, 254, 254, 254, 254, 254,
- 254, 254, 254, 254, 254, nil, 254, 254, nil, 254,
- 254, 254, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 254, nil, nil, 254, nil, nil, 254, 254, nil,
- nil, 254, nil, 254, nil, 254, nil, 254, nil, nil,
- nil, nil, nil, nil, nil, 254, nil, nil, nil, nil,
- 254, 254, 254, 254, 254, 254, nil, nil, nil, 254,
- 254, 255, 255, 255, nil, 255, nil, nil, nil, 255,
- 255, nil, nil, nil, 255, nil, 255, 255, 255, 255,
- 255, 255, 255, nil, nil, nil, nil, 255, 255, 255,
- 255, 255, 255, 255, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 255, nil, nil, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, nil, 255, 255, nil,
- 255, 255, 255, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 255, nil, nil, 255, nil, nil, 255, 255,
- nil, nil, 255, nil, 255, nil, 255, nil, 255, nil,
- nil, nil, nil, nil, nil, nil, 255, nil, nil, nil,
- nil, 255, 255, 255, 255, 255, 255, nil, nil, nil,
- 255, 255, 263, 263, 263, nil, 263, nil, nil, nil,
- 263, 263, nil, nil, nil, 263, nil, 263, 263, 263,
- 263, 263, 263, 263, nil, nil, nil, nil, 263, 263,
- 263, 263, 263, 263, 263, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 263, nil, nil, 263, 263, 263,
- 263, 263, 263, 263, 263, 263, 263, nil, 263, 263,
- nil, 263, 263, 263, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 263, nil, nil, 263, nil, 263, 263,
- 263, nil, nil, 263, nil, 263, nil, 263, nil, 263,
- nil, nil, nil, nil, nil, nil, nil, 263, nil, nil,
- nil, nil, 263, 263, 263, 263, 263, 263, nil, nil,
- nil, 263, 263, 269, 269, 269, nil, 269, nil, nil,
- nil, 269, 269, nil, nil, nil, 269, nil, 269, 269,
- 269, 269, 269, 269, 269, nil, nil, nil, nil, 269,
- 269, 269, 269, 269, 269, 269, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 269, nil, nil, 269, 269,
- 269, 269, 269, 269, 269, 269, 269, 269, nil, 269,
- 269, nil, nil, nil, 269, nil, 468, 468, 468, 468,
- 468, 468, 468, 468, 468, 468, 468, nil, 468, 468,
- nil, nil, 468, 468, 269, nil, nil, 269, nil, nil,
- 269, 269, nil, nil, 269, nil, nil, nil, 468, nil,
- 468, nil, 468, 468, 468, 468, 468, 468, 468, nil,
- 468, nil, nil, 269, 269, 269, 269, 269, 269, nil,
- nil, nil, 269, 269, 290, 290, 290, 468, 290, nil,
- nil, nil, 290, 290, nil, nil, nil, 290, nil, 290,
- 290, 290, 290, 290, 290, 290, nil, nil, nil, nil,
- 290, 290, 290, 290, 290, 290, 290, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 290, nil, nil, 290,
- 290, 290, 290, 290, 290, 290, 290, 290, 290, nil,
- 290, 290, nil, 290, 290, 290, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 290, nil, nil, 290, 290,
- nil, 290, 290, nil, nil, 290, nil, nil, nil, nil,
- nil, 290, nil, nil, nil, nil, nil, nil, nil, 290,
- nil, nil, nil, nil, 290, 290, 290, 290, 290, 290,
- nil, nil, nil, 290, 290, 299, 299, 299, nil, 299,
- nil, nil, nil, 299, 299, nil, nil, nil, 299, nil,
- 299, 299, 299, 299, 299, 299, 299, nil, nil, nil,
- nil, 299, 299, 299, 299, 299, 299, 299, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 299, nil, nil,
- 299, 299, 299, 299, 299, 299, 299, 299, 299, 299,
- nil, 299, 299, nil, 299, 299, 299, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 299, nil, nil, 299,
- nil, nil, 299, 299, nil, nil, 299, nil, nil, nil,
- nil, nil, 299, nil, nil, nil, nil, nil, nil, nil,
- 299, nil, nil, nil, nil, 299, 299, 299, 299, 299,
- 299, nil, nil, nil, 299, 299, 308, 308, 308, nil,
- 308, nil, nil, nil, 308, 308, nil, nil, nil, 308,
- nil, 308, 308, 308, 308, 308, 308, 308, nil, nil,
- nil, nil, 308, 308, 308, 308, 308, 308, 308, nil,
- nil, 308, nil, nil, nil, nil, nil, nil, 308, nil,
- nil, 308, 308, 308, 308, 308, 308, 308, 308, 308,
- 308, nil, 308, 308, nil, 308, 308, 308, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 308, nil, nil,
- 308, nil, nil, 308, 308, nil, nil, 308, nil, nil,
- nil, nil, nil, 308, nil, nil, nil, nil, nil, nil,
- nil, 308, nil, nil, nil, nil, 308, 308, 308, 308,
- 308, 308, nil, nil, nil, 308, 308, 309, 309, 309,
- nil, 309, nil, nil, nil, 309, 309, nil, nil, nil,
- 309, nil, 309, 309, 309, 309, 309, 309, 309, nil,
- nil, nil, nil, 309, 309, 309, 309, 309, 309, 309,
- nil, nil, 309, nil, nil, nil, nil, nil, nil, 309,
- nil, nil, 309, 309, 309, 309, 309, 309, 309, 309,
- 309, 309, nil, 309, 309, nil, 309, 309, 309, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 309, nil,
- nil, 309, nil, nil, 309, 309, nil, nil, 309, nil,
- nil, nil, nil, nil, 309, nil, nil, nil, nil, nil,
- nil, nil, 309, nil, nil, nil, nil, 309, 309, 309,
- 309, 309, 309, nil, nil, nil, 309, 309, 327, 327,
- 327, nil, 327, nil, nil, nil, 327, 327, nil, nil,
- nil, 327, nil, 327, 327, 327, 327, 327, 327, 327,
- nil, nil, nil, nil, 327, 327, 327, 327, 327, 327,
- 327, nil, nil, 327, nil, nil, nil, nil, nil, nil,
- 327, nil, nil, 327, 327, 327, 327, 327, 327, 327,
- 327, 327, 327, nil, 327, 327, nil, 327, 327, 327,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 327,
- nil, nil, 327, nil, nil, 327, 327, nil, nil, 327,
- nil, nil, nil, nil, nil, 327, nil, nil, nil, nil,
- nil, nil, nil, 327, nil, nil, nil, nil, 327, 327,
- 327, 327, 327, 327, nil, nil, nil, 327, 327, 341,
- 341, 341, nil, 341, nil, nil, nil, 341, 341, nil,
- nil, nil, 341, nil, 341, 341, 341, 341, 341, 341,
- 341, nil, nil, nil, nil, 341, 341, 341, 341, 341,
- 341, 341, nil, nil, 341, nil, nil, nil, nil, nil,
- nil, 341, nil, nil, 341, 341, 341, 341, 341, 341,
- 341, 341, 341, 341, nil, 341, 341, nil, 341, 341,
- 341, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 341, nil, nil, 341, nil, nil, 341, 341, nil, nil,
- 341, nil, nil, nil, nil, nil, 341, nil, nil, nil,
- nil, nil, nil, nil, 341, nil, nil, nil, nil, 341,
- 341, 341, 341, 341, 341, nil, nil, nil, 341, 341,
- 357, 357, 357, 357, 357, 357, 357, 357, 357, 357,
- 357, 357, 357, 357, 357, 357, 357, 357, 357, 357,
- 357, 357, 357, 357, nil, nil, 357, 357, 357, 357,
- 357, 357, 357, 357, 357, 357, nil, nil, nil, nil,
- nil, 357, 357, 357, 357, 357, 357, 357, 357, nil,
- nil, 357, nil, nil, nil, nil, nil, nil, nil, 357,
- 357, nil, 357, 357, 357, 357, nil, 357, 357, nil,
- nil, 357, nil, nil, nil, nil, 357, 357, 357, 357,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 357, 357, nil, 357, 357, 357, 357,
- 357, 357, 357, 357, 357, nil, 357, nil, nil, 357,
- 357, nil, nil, 369, 369, 369, nil, 369, nil, nil,
- 357, 369, 369, nil, nil, nil, 369, nil, 369, 369,
- 369, 369, 369, 369, 369, nil, nil, nil, nil, 369,
- 369, 369, 369, 369, 369, 369, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 369, nil, nil, 369, 369,
- 369, 369, 369, 369, 369, 369, 369, 369, nil, 369,
- 369, nil, 369, 369, 369, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 369, nil, nil, 369, nil, nil,
- 369, 369, nil, nil, 369, nil, nil, nil, nil, nil,
- 369, nil, nil, nil, nil, nil, nil, nil, 369, nil,
- nil, nil, nil, 369, 369, 369, 369, 369, 369, nil,
- nil, nil, 369, 369, 378, 378, 378, nil, 378, nil,
- nil, nil, 378, 378, nil, nil, nil, 378, nil, 378,
- 378, 378, 378, 378, 378, 378, nil, nil, nil, nil,
- 378, 378, 378, 378, 378, 378, 378, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 378, nil, nil, 378,
- 378, 378, 378, 378, 378, 378, 378, 378, 378, nil,
- 378, 378, nil, 378, 378, 378, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 378, nil, nil, 378, 378,
- nil, 378, 378, nil, nil, 378, nil, 378, nil, 378,
- nil, 378, nil, nil, nil, nil, nil, nil, nil, 378,
- nil, nil, nil, nil, 378, 378, 378, 378, 378, 378,
- nil, nil, nil, 378, 378, 385, 385, 385, nil, 385,
- nil, nil, nil, 385, 385, nil, nil, nil, 385, nil,
- 385, 385, 385, 385, 385, 385, 385, nil, nil, nil,
- nil, 385, 385, 385, 385, 385, 385, 385, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 385, nil, nil,
- 385, 385, 385, 385, 385, 385, 385, 385, 385, 385,
- nil, 385, 385, nil, 385, 385, 385, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 385, nil, nil, 385,
- 385, nil, 385, 385, nil, nil, 385, nil, 385, nil,
- 385, nil, 385, nil, nil, nil, nil, nil, nil, nil,
- 385, nil, nil, nil, nil, 385, 385, 385, 385, 385,
- 385, nil, nil, nil, 385, 385, 386, 386, 386, nil,
- 386, nil, nil, nil, 386, 386, nil, nil, nil, 386,
- nil, 386, 386, 386, 386, 386, 386, 386, nil, nil,
- nil, nil, 386, 386, 386, 386, 386, 386, 386, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 386, nil,
- nil, 386, 386, 386, 386, 386, 386, 386, 386, 386,
- 386, nil, 386, 386, nil, 386, 386, 386, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 386, nil, nil,
- 386, 386, nil, 386, 386, nil, nil, 386, nil, 386,
- nil, 386, nil, 386, nil, nil, nil, nil, nil, nil,
- nil, 386, nil, nil, nil, nil, 386, 386, 386, 386,
- 386, 386, nil, nil, nil, 386, 386, 393, 393, 393,
- nil, 393, nil, nil, nil, 393, 393, nil, nil, nil,
- 393, nil, 393, 393, 393, 393, 393, 393, 393, nil,
- nil, nil, nil, 393, 393, 393, 393, 393, 393, 393,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 393,
- nil, nil, 393, 393, 393, 393, 393, 393, 393, 393,
- 393, 393, nil, 393, 393, nil, 393, 393, 393, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 393, nil,
- nil, 393, nil, nil, 393, 393, nil, nil, 393, nil,
- 393, nil, nil, nil, 393, nil, nil, nil, nil, nil,
- nil, nil, 393, nil, nil, nil, nil, 393, 393, 393,
- 393, 393, 393, nil, nil, nil, 393, 393, 395, 395,
- 395, nil, 395, nil, nil, nil, 395, 395, nil, nil,
- nil, 395, nil, 395, 395, 395, 395, 395, 395, 395,
- nil, nil, nil, nil, 395, 395, 395, 395, 395, 395,
- 395, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 395, nil, nil, 395, 395, 395, 395, 395, 395, 395,
- 395, 395, 395, nil, 395, 395, nil, 395, 395, 395,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 395,
- nil, nil, 395, nil, nil, 395, 395, nil, nil, 395,
- nil, nil, nil, nil, nil, 395, nil, nil, nil, nil,
- nil, nil, nil, 395, nil, nil, nil, nil, 395, 395,
- 395, 395, 395, 395, nil, nil, nil, 395, 395, 396,
- 396, 396, nil, 396, nil, nil, nil, 396, 396, nil,
- nil, nil, 396, nil, 396, 396, 396, 396, 396, 396,
- 396, nil, nil, nil, nil, 396, 396, 396, 396, 396,
- 396, 396, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 396, nil, nil, 396, 396, 396, 396, 396, 396,
- 396, 396, 396, 396, nil, 396, 396, nil, 396, 396,
- 396, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 396, nil, nil, 396, nil, nil, 396, 396, nil, nil,
- 396, nil, nil, nil, nil, nil, 396, nil, nil, nil,
- nil, nil, nil, nil, 396, nil, nil, nil, nil, 396,
- 396, 396, 396, 396, 396, nil, nil, nil, 396, 396,
- 397, 397, 397, nil, 397, nil, nil, nil, 397, 397,
- nil, nil, nil, 397, nil, 397, 397, 397, 397, 397,
- 397, 397, nil, nil, nil, nil, 397, 397, 397, 397,
- 397, 397, 397, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 397, nil, nil, 397, 397, 397, 397, 397,
- 397, 397, 397, 397, 397, nil, 397, 397, nil, 397,
- 397, 397, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 397, nil, nil, 397, nil, nil, 397, 397, nil,
- nil, 397, nil, nil, nil, nil, nil, 397, nil, nil,
- nil, nil, nil, nil, nil, 397, nil, nil, nil, nil,
- 397, 397, 397, 397, 397, 397, nil, nil, nil, 397,
- 397, 426, 426, 426, nil, 426, nil, nil, nil, 426,
- 426, nil, nil, nil, 426, nil, 426, 426, 426, 426,
- 426, 426, 426, nil, nil, nil, nil, 426, 426, 426,
- 426, 426, 426, 426, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 426, nil, nil, 426, 426, 426, 426,
- 426, 426, 426, 426, 426, 426, nil, 426, 426, nil,
- 426, 426, 426, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 426, nil, nil, 426, nil, nil, 426, 426,
- nil, nil, 426, nil, 426, nil, 426, nil, 426, nil,
- nil, nil, nil, nil, nil, nil, 426, nil, nil, nil,
- nil, 426, 426, 426, 426, 426, 426, nil, nil, nil,
- 426, 426, 428, 428, 428, nil, 428, nil, nil, nil,
- 428, 428, nil, nil, nil, 428, nil, 428, 428, 428,
- 428, 428, 428, 428, nil, nil, nil, nil, 428, 428,
- 428, 428, 428, 428, 428, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 428, nil, nil, 428, 428, 428,
- 428, 428, 428, 428, 428, 428, 428, nil, 428, 428,
- nil, 428, 428, 428, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 428, nil, nil, 428, nil, nil, 428,
- 428, nil, nil, 428, nil, 428, nil, 428, nil, 428,
- nil, nil, nil, nil, nil, nil, nil, 428, nil, nil,
- nil, nil, 428, 428, 428, 428, 428, 428, nil, nil,
- nil, 428, 428, 431, 431, 431, nil, 431, nil, nil,
- nil, 431, 431, nil, nil, nil, 431, nil, 431, 431,
- 431, 431, 431, 431, 431, nil, nil, nil, nil, 431,
- 431, 431, 431, 431, 431, 431, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 431, nil, nil, 431, 431,
- 431, 431, 431, 431, 431, 431, 431, 431, nil, 431,
- 431, nil, 431, 431, 431, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 431, nil, nil, 431, nil, nil,
- 431, 431, nil, nil, 431, nil, nil, nil, nil, nil,
- 431, nil, nil, nil, nil, nil, nil, nil, 431, nil,
- nil, nil, nil, 431, 431, 431, 431, 431, 431, nil,
- nil, nil, 431, 431, 445, 445, 445, nil, 445, nil,
- nil, nil, 445, 445, nil, nil, nil, 445, nil, 445,
- 445, 445, 445, 445, 445, 445, nil, nil, nil, nil,
- 445, 445, 445, 445, 445, 445, 445, nil, nil, 445,
- nil, nil, nil, nil, nil, nil, 445, nil, nil, 445,
- 445, 445, 445, 445, 445, 445, 445, 445, 445, nil,
- 445, 445, nil, 445, 445, 445, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 445, nil, nil, 445, nil,
- nil, 445, 445, nil, nil, 445, nil, 445, nil, 445,
- nil, 445, nil, nil, nil, nil, nil, nil, nil, 445,
- nil, nil, nil, nil, 445, 445, 445, 445, 445, 445,
- nil, nil, nil, 445, 445, 456, 456, 456, nil, 456,
- nil, nil, nil, 456, 456, nil, nil, nil, 456, nil,
- 456, 456, 456, 456, 456, 456, 456, nil, nil, nil,
- nil, 456, 456, 456, 456, 456, 456, 456, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 456, nil, nil,
- 456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
- nil, 456, 456, nil, 456, 456, 456, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 456, nil, nil, 456,
- nil, nil, 456, 456, nil, nil, 456, nil, 456, nil,
- nil, nil, 456, nil, nil, nil, nil, nil, nil, nil,
- 456, nil, nil, nil, nil, 456, 456, 456, 456, 456,
- 456, nil, nil, nil, 456, 456, 463, 463, 463, nil,
- 463, nil, nil, nil, 463, 463, nil, nil, nil, 463,
- nil, 463, 463, 463, 463, 463, 463, 463, nil, nil,
- nil, nil, 463, 463, 463, 463, 463, 463, 463, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 463, nil,
- nil, 463, 463, 463, 463, 463, 463, 463, 463, 463,
- 463, nil, 463, 463, nil, 463, 463, 463, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 463, nil, nil,
- 463, nil, nil, 463, 463, nil, nil, 463, nil, nil,
- nil, nil, nil, 463, nil, nil, nil, nil, nil, nil,
- nil, 463, nil, nil, nil, nil, 463, 463, 463, 463,
- 463, 463, nil, nil, nil, 463, 463, 464, 464, 464,
- nil, 464, nil, nil, nil, 464, 464, nil, nil, nil,
- 464, nil, 464, 464, 464, 464, 464, 464, 464, nil,
- nil, nil, nil, 464, 464, 464, 464, 464, 464, 464,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 464,
- nil, nil, 464, 464, 464, 464, 464, 464, 464, 464,
- 464, 464, nil, 464, 464, nil, 464, 464, 464, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 464, nil,
- nil, 464, nil, nil, 464, 464, nil, nil, 464, nil,
- nil, nil, nil, nil, 464, nil, nil, nil, nil, nil,
- nil, nil, 464, nil, nil, nil, nil, 464, 464, 464,
- 464, 464, 464, nil, nil, nil, 464, 464, 465, 465,
- 465, nil, 465, nil, nil, nil, 465, 465, nil, nil,
- nil, 465, nil, 465, 465, 465, 465, 465, 465, 465,
- nil, nil, nil, nil, 465, 465, 465, 465, 465, 465,
- 465, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 465, nil, nil, 465, 465, 465, 465, 465, 465, 465,
- 465, 465, 465, nil, 465, 465, nil, 465, 465, 465,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 465,
- nil, nil, 465, nil, nil, 465, 465, nil, nil, 465,
- nil, nil, nil, nil, nil, 465, nil, nil, nil, nil,
- nil, nil, nil, 465, nil, nil, nil, nil, 465, 465,
- 465, 465, 465, 465, nil, nil, nil, 465, 465, 469,
- 469, 469, nil, 469, nil, nil, nil, 469, 469, nil,
- nil, nil, 469, nil, 469, 469, 469, 469, 469, 469,
- 469, nil, nil, nil, nil, 469, 469, 469, 469, 469,
- 469, 469, nil, nil, 469, nil, nil, nil, nil, nil,
- nil, 469, nil, nil, 469, 469, 469, 469, 469, 469,
- 469, 469, 469, 469, nil, 469, 469, nil, 469, 469,
- 469, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 469, nil, nil, 469, nil, nil, 469, 469, nil, nil,
- 469, nil, nil, nil, nil, nil, 469, nil, nil, nil,
- nil, nil, nil, nil, 469, nil, nil, nil, nil, 469,
- 469, 469, 469, 469, 469, nil, nil, nil, 469, 469,
- 471, 471, 471, nil, 471, nil, nil, nil, 471, 471,
- nil, nil, nil, 471, nil, 471, 471, 471, 471, 471,
- 471, 471, nil, nil, nil, nil, 471, 471, 471, 471,
- 471, 471, 471, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 471, nil, nil, 471, 471, 471, 471, 471,
- 471, 471, 471, 471, 471, nil, 471, 471, nil, 471,
- 471, 471, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 471, nil, nil, 471, nil, nil, 471, 471, nil,
- nil, 471, nil, 471, nil, nil, nil, 471, nil, nil,
- nil, nil, nil, nil, nil, 471, nil, nil, nil, nil,
- 471, 471, 471, 471, 471, 471, nil, nil, nil, 471,
- 471, 476, 476, 476, nil, 476, nil, nil, nil, 476,
- 476, nil, nil, nil, 476, nil, 476, 476, 476, 476,
- 476, 476, 476, nil, nil, nil, nil, 476, 476, 476,
- 476, 476, 476, 476, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 476, nil, nil, 476, 476, 476, 476,
- 476, 476, 476, 476, 476, 476, nil, 476, 476, nil,
- 476, 476, 476, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 476, nil, nil, 476, nil, nil, 476, 476,
- nil, nil, 476, nil, 476, nil, nil, nil, 476, nil,
- nil, nil, nil, nil, nil, nil, 476, nil, nil, nil,
- nil, 476, 476, 476, 476, 476, 476, nil, nil, nil,
- 476, 476, 479, 479, 479, nil, 479, nil, nil, nil,
- 479, 479, nil, nil, nil, 479, nil, 479, 479, 479,
- 479, 479, 479, 479, nil, nil, nil, nil, 479, 479,
- 479, 479, 479, 479, 479, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 479, nil, nil, 479, 479, 479,
- 479, 479, 479, 479, 479, 479, 479, nil, 479, 479,
- nil, 479, 479, 479, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 479, nil, nil, 479, nil, nil, 479,
- 479, nil, nil, 479, nil, nil, nil, nil, nil, 479,
- nil, nil, nil, nil, nil, nil, nil, 479, nil, nil,
- nil, nil, 479, 479, 479, 479, 479, 479, nil, nil,
- nil, 479, 479, 482, 482, 482, nil, 482, nil, nil,
- nil, 482, 482, nil, nil, nil, 482, nil, 482, 482,
- 482, 482, 482, 482, 482, nil, nil, nil, nil, 482,
- 482, 482, 482, 482, 482, 482, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 482, nil, nil, 482, 482,
- 482, 482, 482, 482, 482, 482, 482, 482, nil, 482,
- 482, nil, 482, 482, 482, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 482, nil, nil, 482, nil, nil,
- 482, 482, nil, nil, 482, nil, nil, nil, nil, nil,
- 482, nil, nil, nil, nil, nil, nil, nil, 482, nil,
- nil, nil, nil, 482, 482, 482, 482, 482, 482, nil,
- nil, nil, 482, 482, 496, 496, 496, nil, 496, nil,
- nil, nil, 496, 496, nil, nil, nil, 496, nil, 496,
- 496, 496, 496, 496, 496, 496, nil, nil, nil, nil,
- 496, 496, 496, 496, 496, 496, 496, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 496, nil, nil, 496,
- 496, 496, 496, 496, 496, 496, 496, 496, 496, nil,
- 496, 496, nil, 496, 496, 496, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 496, nil, nil, 496, nil,
- nil, 496, 496, nil, nil, 496, nil, 496, nil, nil,
- nil, 496, nil, nil, nil, nil, nil, nil, nil, 496,
- nil, nil, nil, nil, 496, 496, 496, 496, 496, 496,
- nil, nil, nil, 496, 496, 497, 497, 497, nil, 497,
- nil, nil, nil, 497, 497, nil, nil, nil, 497, nil,
- 497, 497, 497, 497, 497, 497, 497, nil, nil, nil,
- nil, 497, 497, 497, 497, 497, 497, 497, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 497, nil, nil,
- 497, 497, 497, 497, 497, 497, 497, 497, 497, 497,
- nil, 497, 497, nil, 497, 497, 497, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 497, nil, nil, 497,
- nil, nil, 497, 497, nil, nil, 497, nil, 497, nil,
- nil, nil, 497, nil, nil, nil, nil, nil, nil, nil,
- 497, nil, nil, nil, nil, 497, 497, 497, 497, 497,
- 497, nil, nil, nil, 497, 497, 506, 506, 506, nil,
- 506, nil, nil, nil, 506, 506, nil, nil, nil, 506,
- nil, 506, 506, 506, 506, 506, 506, 506, nil, nil,
- nil, nil, 506, 506, 506, 506, 506, 506, 506, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 506, nil,
- nil, 506, 506, 506, 506, 506, 506, 506, 506, 506,
- 506, nil, 506, 506, nil, 506, 506, 506, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 506, nil, nil,
- 506, nil, nil, 506, 506, nil, nil, 506, nil, 506,
- nil, nil, nil, 506, nil, nil, nil, nil, nil, nil,
- nil, 506, nil, nil, nil, nil, 506, 506, 506, 506,
- 506, 506, nil, nil, nil, 506, 506, 510, 510, 510,
- nil, 510, nil, nil, nil, 510, 510, nil, nil, nil,
- 510, nil, 510, 510, 510, 510, 510, 510, 510, nil,
- nil, nil, nil, 510, 510, 510, 510, 510, 510, 510,
- nil, nil, 510, nil, nil, nil, nil, nil, nil, 510,
- nil, nil, 510, 510, 510, 510, 510, 510, 510, 510,
- 510, 510, nil, 510, 510, nil, 510, 510, 510, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 510, nil,
- nil, 510, nil, nil, 510, 510, nil, nil, 510, nil,
- nil, nil, nil, nil, 510, nil, nil, nil, nil, nil,
- nil, nil, 510, nil, nil, nil, nil, 510, 510, 510,
- 510, 510, 510, nil, nil, nil, 510, 510, 534, 534,
- 534, 534, 534, 534, 534, 534, 534, 534, 534, 534,
- 534, 534, 534, 534, 534, 534, 534, 534, 534, 534,
- 534, 534, nil, nil, 534, 534, 534, 534, 534, 534,
- 534, 534, 534, 534, nil, nil, nil, nil, nil, 534,
- 534, 534, 534, 534, 534, 534, 534, nil, nil, 534,
- nil, nil, nil, nil, nil, nil, nil, 534, 534, nil,
- 534, 534, 534, 534, nil, 534, 534, nil, nil, 534,
- nil, nil, nil, nil, 534, 534, 534, 534, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 534, 534, nil, 534, 534, 534, 534, 534, 534,
- 534, 534, 534, nil, 534, nil, nil, 534, 534, nil,
- nil, 537, 537, 537, nil, 537, nil, nil, 534, 537,
- 537, nil, nil, nil, 537, nil, 537, 537, 537, 537,
- 537, 537, 537, nil, nil, nil, nil, 537, 537, 537,
- 537, 537, 537, 537, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 537, nil, nil, 537, 537, 537, 537,
- 537, 537, 537, 537, 537, 537, nil, 537, 537, nil,
- 537, 537, 537, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 537, nil, nil, 537, nil, nil, 537, 537,
- nil, nil, 537, nil, nil, nil, nil, nil, 537, nil,
- nil, nil, nil, nil, nil, nil, 537, nil, nil, nil,
- nil, 537, 537, 537, 537, 537, 537, nil, nil, nil,
- 537, 537, 538, 538, 538, nil, 538, nil, nil, nil,
- 538, 538, nil, nil, nil, 538, nil, 538, 538, 538,
- 538, 538, 538, 538, nil, nil, nil, nil, 538, 538,
- 538, 538, 538, 538, 538, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 538, nil, nil, 538, 538, 538,
- 538, 538, 538, 538, 538, 538, 538, nil, 538, 538,
- nil, 538, 538, 538, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 538, nil, nil, 538, nil, nil, 538,
- 538, nil, nil, 538, nil, 538, nil, nil, nil, 538,
- nil, nil, nil, nil, nil, nil, nil, 538, nil, nil,
- nil, nil, 538, 538, 538, 538, 538, 538, nil, nil,
- nil, 538, 538, 541, 541, 541, nil, 541, nil, nil,
- nil, 541, 541, nil, nil, nil, 541, nil, 541, 541,
- 541, 541, 541, 541, 541, nil, nil, nil, nil, 541,
- 541, 541, 541, 541, 541, 541, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 541, nil, nil, 541, 541,
- 541, 541, 541, 541, 541, 541, 541, 541, nil, 541,
- 541, nil, 541, 541, 541, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 541, nil, nil, 541, nil, nil,
- 541, 541, nil, nil, 541, nil, nil, nil, nil, nil,
- 541, nil, nil, nil, nil, nil, nil, nil, 541, nil,
- nil, nil, nil, 541, 541, 541, 541, 541, 541, nil,
- nil, nil, 541, 541, 542, 542, 542, nil, 542, nil,
- nil, nil, 542, 542, nil, nil, nil, 542, nil, 542,
- 542, 542, 542, 542, 542, 542, nil, nil, nil, nil,
- 542, 542, 542, 542, 542, 542, 542, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 542, nil, nil, 542,
- 542, 542, 542, 542, 542, 542, 542, 542, 542, nil,
- 542, 542, nil, 542, 542, 542, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 542, nil, nil, 542, nil,
- nil, 542, 542, nil, nil, 542, nil, nil, nil, nil,
- nil, 542, nil, nil, nil, nil, nil, nil, nil, 542,
- nil, nil, nil, nil, 542, 542, 542, 542, 542, 542,
- nil, nil, nil, 542, 542, 546, 546, 546, nil, 546,
- nil, nil, nil, 546, 546, nil, nil, nil, 546, nil,
- 546, 546, 546, 546, 546, 546, 546, nil, nil, nil,
- nil, 546, 546, 546, 546, 546, 546, 546, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 546, nil, nil,
- 546, 546, 546, 546, 546, 546, 546, 546, 546, 546,
- nil, 546, 546, nil, 546, 546, 546, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 546, nil, nil, 546,
- nil, nil, 546, 546, nil, nil, 546, nil, nil, nil,
- nil, nil, 546, nil, nil, nil, nil, nil, nil, nil,
- 546, nil, nil, nil, nil, 546, 546, 546, 546, 546,
- 546, nil, nil, nil, 546, 546, 549, 549, 549, nil,
- 549, nil, nil, nil, 549, 549, nil, nil, nil, 549,
- nil, 549, 549, 549, 549, 549, 549, 549, nil, nil,
- nil, nil, 549, 549, 549, 549, 549, 549, 549, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 549, nil,
- nil, 549, 549, 549, 549, 549, 549, 549, 549, 549,
- 549, nil, 549, 549, nil, 549, 549, 549, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 549, nil, nil,
- 549, nil, nil, 549, 549, nil, nil, 549, nil, nil,
- nil, nil, nil, 549, nil, nil, nil, nil, nil, nil,
- nil, 549, nil, nil, nil, nil, 549, 549, 549, 549,
- 549, 549, nil, nil, nil, 549, 549, 556, 556, 556,
- nil, 556, nil, nil, nil, 556, 556, nil, nil, nil,
- 556, nil, 556, 556, 556, 556, 556, 556, 556, nil,
- nil, nil, nil, 556, 556, 556, 556, 556, 556, 556,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 556,
- nil, nil, 556, 556, 556, 556, 556, 556, 556, 556,
- 556, 556, nil, 556, 556, nil, 556, 556, 556, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 556, nil,
- nil, 556, nil, nil, 556, 556, nil, nil, 556, nil,
- nil, nil, nil, nil, 556, nil, nil, nil, nil, nil,
- nil, nil, 556, nil, nil, nil, nil, 556, 556, 556,
- 556, 556, 556, nil, nil, nil, 556, 556, 557, 557,
- 557, nil, 557, nil, nil, nil, 557, 557, nil, nil,
- nil, 557, nil, 557, 557, 557, 557, 557, 557, 557,
- nil, nil, nil, nil, 557, 557, 557, 557, 557, 557,
- 557, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 557, nil, nil, 557, 557, 557, 557, 557, 557, 557,
- 557, 557, 557, nil, 557, 557, nil, nil, nil, 557,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 557,
- nil, nil, 557, nil, nil, 557, 557, nil, nil, 557,
- nil, 557, nil, 557, nil, nil, nil, nil, nil, nil,
- nil, nil, 557, nil, nil, nil, nil, nil, 557, 557,
- 557, 557, 557, 557, nil, nil, nil, 557, 557, 560,
- 560, 560, nil, 560, nil, nil, nil, 560, 560, nil,
- nil, nil, 560, nil, 560, 560, 560, 560, 560, 560,
- 560, nil, nil, nil, nil, 560, 560, 560, 560, 560,
- 560, 560, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 560, nil, nil, 560, 560, 560, 560, 560, 560,
- 560, 560, 560, 560, nil, 560, 560, nil, 560, 560,
- 560, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 560, nil, nil, 560, nil, nil, 560, 560, nil, nil,
- 560, nil, nil, nil, nil, nil, 560, nil, nil, nil,
- nil, nil, nil, nil, 560, nil, nil, nil, nil, 560,
- 560, 560, 560, 560, 560, nil, nil, nil, 560, 560,
- 564, 564, 564, nil, 564, nil, nil, nil, 564, 564,
- nil, nil, nil, 564, nil, 564, 564, 564, 564, 564,
- 564, 564, nil, nil, nil, nil, 564, 564, 564, 564,
- 564, 564, 564, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 564, nil, nil, 564, 564, 564, 564, 564,
- 564, 564, 564, 564, 564, nil, 564, 564, nil, 564,
- 564, 564, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 564, nil, nil, 564, nil, nil, 564, 564, nil,
- nil, 564, nil, nil, nil, nil, nil, 564, nil, nil,
- nil, nil, nil, nil, nil, 564, nil, nil, nil, nil,
- 564, 564, 564, 564, 564, 564, nil, nil, nil, 564,
- 564, 580, 580, 580, nil, 580, nil, nil, nil, 580,
- 580, nil, nil, nil, 580, nil, 580, 580, 580, 580,
- 580, 580, 580, nil, nil, nil, nil, 580, 580, 580,
- 580, 580, 580, 580, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 580, nil, nil, 580, 580, 580, 580,
- 580, 580, 580, 580, 580, 580, nil, 580, 580, nil,
- 580, 580, 580, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 580, nil, nil, 580, nil, nil, 580, 580,
- nil, nil, 580, nil, 580, nil, 580, nil, 580, nil,
- nil, nil, nil, nil, nil, nil, 580, nil, nil, nil,
- nil, 580, 580, 580, 580, 580, 580, nil, nil, nil,
- 580, 580, 584, 584, 584, nil, 584, nil, nil, nil,
- 584, 584, nil, nil, nil, 584, nil, 584, 584, 584,
- 584, 584, 584, 584, nil, nil, nil, nil, 584, 584,
- 584, 584, 584, 584, 584, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 584, nil, nil, 584, 584, 584,
- 584, 584, 584, 584, 584, 584, 584, nil, 584, 584,
- nil, 584, 584, 584, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 584, nil, nil, 584, nil, nil, 584,
- 584, nil, nil, 584, nil, nil, nil, nil, nil, 584,
- nil, nil, nil, nil, nil, nil, nil, 584, nil, nil,
- nil, nil, 584, 584, 584, 584, 584, 584, nil, nil,
- nil, 584, 584, 612, 612, 612, nil, 612, nil, nil,
- nil, 612, 612, nil, nil, nil, 612, nil, 612, 612,
- 612, 612, 612, 612, 612, nil, nil, nil, nil, 612,
- 612, 612, 612, 612, 612, 612, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 612, nil, nil, 612, 612,
- 612, 612, 612, 612, 612, 612, 612, 612, nil, 612,
- 612, nil, 612, 612, 612, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 612, nil, nil, 612, nil, nil,
- 612, 612, nil, nil, 612, nil, nil, nil, nil, nil,
- 612, nil, nil, nil, nil, nil, nil, nil, 612, nil,
- nil, nil, nil, 612, 612, 612, 612, 612, 612, nil,
- nil, nil, 612, 612, 628, 628, 628, nil, 628, nil,
- nil, nil, 628, 628, nil, nil, nil, 628, nil, 628,
- 628, 628, 628, 628, 628, 628, nil, nil, nil, nil,
- 628, 628, 628, 628, 628, 628, 628, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 628, nil, nil, 628,
- 628, 628, 628, 628, 628, 628, 628, 628, 628, nil,
- 628, 628, nil, 628, 628, 628, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 628, nil, nil, 628, nil,
- nil, 628, 628, nil, nil, 628, nil, nil, nil, nil,
- nil, 628, nil, nil, nil, nil, nil, nil, nil, 628,
- nil, nil, nil, nil, 628, 628, 628, 628, 628, 628,
- nil, nil, nil, 628, 628, 634, 634, 634, nil, 634,
- nil, nil, nil, 634, 634, nil, nil, nil, 634, nil,
- 634, 634, 634, 634, 634, 634, 634, nil, nil, nil,
- nil, 634, 634, 634, 634, 634, 634, 634, nil, nil,
- 634, nil, nil, nil, nil, nil, nil, 634, nil, nil,
- 634, 634, 634, 634, 634, 634, 634, 634, 634, 634,
- nil, 634, 634, nil, 634, 634, 634, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 634, nil, nil, 634,
- nil, nil, 634, 634, nil, nil, 634, nil, nil, nil,
- nil, nil, 634, nil, nil, nil, nil, nil, nil, nil,
- 634, nil, nil, nil, nil, 634, 634, 634, 634, 634,
- 634, nil, nil, nil, 634, 634, 679, 679, 679, nil,
- 679, nil, nil, nil, 679, 679, nil, nil, nil, 679,
- nil, 679, 679, 679, 679, 679, 679, 679, nil, nil,
- nil, nil, 679, 679, 679, 679, 679, 679, 679, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 679, nil,
- nil, 679, 679, 679, 679, 679, 679, 679, 679, 679,
- 679, nil, 679, 679, nil, 679, 679, 679, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 679, nil, nil,
- 679, nil, nil, 679, 679, nil, nil, 679, nil, nil,
- nil, nil, nil, 679, nil, nil, nil, nil, nil, nil,
- nil, 679, nil, nil, nil, nil, 679, 679, 679, 679,
- 679, 679, nil, nil, nil, 679, 679, 680, 680, 680,
- nil, 680, nil, nil, nil, 680, 680, nil, nil, nil,
- 680, nil, 680, 680, 680, 680, 680, 680, 680, nil,
- nil, nil, nil, 680, 680, 680, 680, 680, 680, 680,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 680,
- nil, nil, 680, 680, 680, 680, 680, 680, 680, 680,
- 680, 680, nil, 680, 680, nil, 680, 680, 680, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 680, nil,
- nil, 680, nil, nil, 680, 680, nil, nil, 680, nil,
- nil, nil, nil, nil, 680, nil, nil, nil, nil, nil,
- nil, nil, 680, nil, nil, nil, nil, 680, 680, 680,
- 680, 680, 680, nil, nil, nil, 680, 680, 690, 690,
- 690, nil, 690, nil, nil, nil, 690, 690, nil, nil,
- nil, 690, nil, 690, 690, 690, 690, 690, 690, 690,
- nil, nil, nil, nil, 690, 690, 690, 690, 690, 690,
- 690, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 690, nil, nil, 690, 690, 690, 690, 690, 690, 690,
- 690, 690, 690, nil, 690, 690, nil, 690, 690, 690,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 690,
- nil, nil, 690, nil, nil, 690, 690, nil, nil, 690,
- nil, nil, nil, nil, nil, 690, nil, nil, nil, nil,
- nil, nil, nil, 690, nil, nil, nil, nil, 690, 690,
- 690, 690, 690, 690, nil, nil, nil, 690, 690, 691,
- 691, 691, nil, 691, nil, nil, nil, 691, 691, nil,
- nil, nil, 691, nil, 691, 691, 691, 691, 691, 691,
- 691, nil, nil, nil, nil, 691, 691, 691, 691, 691,
- 691, 691, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 691, nil, nil, 691, 691, 691, 691, 691, 691,
- 691, 691, 691, 691, nil, 691, 691, nil, 691, 691,
- 691, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 691, nil, nil, 691, nil, nil, 691, 691, nil, nil,
- 691, nil, nil, nil, nil, nil, 691, nil, nil, nil,
- nil, nil, nil, nil, 691, nil, nil, nil, nil, 691,
- 691, 691, 691, 691, 691, nil, nil, nil, 691, 691,
- 692, 692, 692, nil, 692, nil, nil, nil, 692, 692,
- nil, nil, nil, 692, nil, 692, 692, 692, 692, 692,
- 692, 692, nil, nil, nil, nil, 692, 692, 692, 692,
- 692, 692, 692, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 692, nil, nil, 692, 692, 692, 692, 692,
- 692, 692, 692, 692, 692, nil, 692, 692, nil, 692,
- 692, 692, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 692, nil, nil, 692, nil, nil, 692, 692, nil,
- nil, 692, nil, nil, nil, nil, nil, 692, nil, nil,
- nil, nil, nil, nil, nil, 692, nil, nil, nil, nil,
- 692, 692, 692, 692, 692, 692, nil, nil, nil, 692,
- 692, 698, 698, 698, nil, 698, nil, nil, nil, 698,
- 698, nil, nil, nil, 698, nil, 698, 698, 698, 698,
- 698, 698, 698, nil, nil, nil, nil, 698, 698, 698,
- 698, 698, 698, 698, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 698, nil, nil, 698, 698, 698, 698,
- 698, 698, 698, 698, 698, 698, nil, 698, 698, nil,
- nil, nil, 698, nil, 600, 600, 600, 600, 600, 600,
- 600, 600, 600, 600, 600, nil, 600, 600, nil, nil,
- 600, 600, 698, nil, nil, 698, nil, nil, 698, 698,
- nil, nil, 698, nil, nil, nil, 600, nil, 600, nil,
- 600, 600, 600, 600, 600, 600, 600, nil, 600, nil,
- nil, 698, 698, 698, 698, 698, 698, nil, nil, nil,
- 698, 698, 704, 704, 704, 600, 704, nil, nil, nil,
- 704, 704, nil, nil, nil, 704, nil, 704, 704, 704,
- 704, 704, 704, 704, nil, nil, nil, nil, 704, 704,
- 704, 704, 704, 704, 704, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 704, nil, nil, 704, 704, 704,
- 704, 704, 704, 704, 704, 704, 704, nil, 704, 704,
- nil, 704, 704, 704, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 704, nil, nil, 704, nil, nil, 704,
- 704, nil, nil, 704, nil, 704, nil, 704, nil, 704,
- nil, nil, nil, nil, nil, nil, nil, 704, nil, nil,
- nil, nil, 704, 704, 704, 704, 704, 704, nil, nil,
- nil, 704, 704, 713, 713, 713, nil, 713, nil, nil,
- nil, 713, 713, nil, nil, nil, 713, nil, 713, 713,
- 713, 713, 713, 713, 713, nil, nil, nil, nil, 713,
- 713, 713, 713, 713, 713, 713, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 713, nil, nil, 713, 713,
- 713, 713, 713, 713, 713, 713, 713, 713, nil, 713,
- 713, nil, 713, 713, 713, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 713, nil, nil, 713, nil, nil,
- 713, 713, nil, nil, 713, nil, 713, nil, 713, nil,
- 713, nil, nil, nil, nil, nil, nil, nil, 713, nil,
- nil, nil, nil, 713, 713, 713, 713, 713, 713, nil,
- nil, nil, 713, 713, 715, 715, 715, nil, 715, nil,
- nil, nil, 715, 715, nil, nil, nil, 715, nil, 715,
- 715, 715, 715, 715, 715, 715, nil, nil, nil, nil,
- 715, 715, 715, 715, 715, 715, 715, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 715, nil, nil, 715,
- 715, 715, 715, 715, 715, 715, 715, 715, 715, nil,
- 715, 715, nil, 715, 715, 715, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 715, nil, nil, 715, nil,
- nil, 715, 715, nil, nil, 715, nil, 715, nil, 715,
- nil, 715, nil, nil, nil, nil, nil, nil, nil, 715,
- nil, nil, nil, nil, 715, 715, 715, 715, 715, 715,
- nil, nil, nil, 715, 715, 728, 728, 728, nil, 728,
- nil, nil, nil, 728, 728, nil, nil, nil, 728, nil,
- 728, 728, 728, 728, 728, 728, 728, nil, nil, nil,
- nil, 728, 728, 728, 728, 728, 728, 728, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 728, nil, nil,
- 728, 728, 728, 728, 728, 728, 728, 728, 728, 728,
- nil, 728, 728, nil, nil, nil, 728, nil, 677, 677,
- 677, 677, 677, 677, 677, 677, 677, 677, 677, nil,
- 677, 677, nil, nil, 677, 677, 728, nil, nil, 728,
- nil, nil, 728, 728, nil, nil, 728, nil, nil, nil,
- 677, nil, 677, nil, 677, 677, 677, 677, 677, 677,
- 677, nil, 677, nil, nil, 728, 728, 728, 728, 728,
- 728, nil, nil, nil, 728, 728, 734, 734, 734, 677,
- 734, nil, nil, nil, 734, 734, nil, nil, nil, 734,
- nil, 734, 734, 734, 734, 734, 734, 734, nil, nil,
- nil, nil, 734, 734, 734, 734, 734, 734, 734, nil,
- nil, 734, nil, nil, nil, nil, nil, nil, 734, nil,
- nil, 734, 734, 734, 734, 734, 734, 734, 734, 734,
- 734, nil, 734, 734, nil, 734, 734, 734, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 734, nil, nil,
- 734, nil, nil, 734, 734, nil, nil, 734, nil, nil,
- nil, nil, nil, 734, nil, nil, nil, nil, nil, nil,
- nil, 734, nil, nil, nil, nil, 734, 734, 734, 734,
- 734, 734, nil, nil, nil, 734, 734, 740, 740, 740,
- nil, 740, nil, nil, nil, 740, 740, nil, nil, nil,
- 740, nil, 740, 740, 740, 740, 740, 740, 740, nil,
- nil, nil, nil, 740, 740, 740, 740, 740, 740, 740,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 740,
- nil, nil, 740, 740, 740, 740, 740, 740, 740, 740,
- 740, 740, nil, 740, 740, nil, 740, 740, 740, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 740, nil,
- nil, 740, nil, nil, 740, 740, nil, nil, 740, nil,
- 740, nil, nil, nil, 740, nil, nil, nil, nil, nil,
- nil, nil, 740, nil, nil, nil, nil, 740, 740, 740,
- 740, 740, 740, nil, nil, nil, 740, 740, 759, 759,
- 759, nil, 759, nil, nil, nil, 759, 759, nil, nil,
- nil, 759, nil, 759, 759, 759, 759, 759, 759, 759,
- nil, nil, nil, nil, 759, 759, 759, 759, 759, 759,
- 759, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 759, nil, nil, 759, 759, 759, 759, 759, 759, 759,
- 759, 759, 759, nil, 759, 759, nil, 759, 759, 759,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 759,
- nil, nil, 759, nil, nil, 759, 759, nil, nil, 759,
- nil, nil, nil, nil, nil, 759, nil, nil, nil, nil,
- nil, nil, nil, 759, nil, nil, nil, nil, 759, 759,
- 759, 759, 759, 759, nil, nil, nil, 759, 759, 768,
- 768, 768, nil, 768, nil, nil, nil, 768, 768, nil,
- nil, nil, 768, nil, 768, 768, 768, 768, 768, 768,
- 768, nil, nil, nil, nil, 768, 768, 768, 768, 768,
- 768, 768, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 768, nil, nil, 768, 768, 768, 768, 768, 768,
- 768, 768, 768, 768, nil, 768, 768, nil, 768, 768,
- 768, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 768, nil, nil, 768, nil, nil, 768, 768, nil, nil,
- 768, nil, nil, nil, nil, nil, 768, nil, nil, nil,
- nil, nil, nil, nil, 768, nil, nil, nil, nil, 768,
- 768, 768, 768, 768, 768, nil, nil, nil, 768, 768,
- 769, 769, 769, nil, 769, nil, nil, nil, 769, 769,
- nil, nil, nil, 769, nil, 769, 769, 769, 769, 769,
- 769, 769, nil, nil, nil, nil, 769, 769, 769, 769,
- 769, 769, 769, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 769, nil, nil, 769, 769, 769, 769, 769,
- 769, 769, 769, 769, 769, nil, 769, 769, nil, nil,
- nil, 769, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 769, nil, nil, 769, nil, nil, 769, 769, nil,
- nil, 769, nil, 769, nil, 769, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 769, 769, 769, 769, 769, 769, nil, nil, nil, 769,
- 769, 780, 780, 780, nil, 780, nil, nil, nil, 780,
- 780, nil, nil, nil, 780, nil, 780, 780, 780, 780,
- 780, 780, 780, nil, nil, nil, nil, 780, 780, 780,
- 780, 780, 780, 780, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 780, nil, nil, 780, 780, 780, 780,
- 780, 780, 780, 780, 780, 780, nil, 780, 780, nil,
- 780, 780, 780, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 780, nil, nil, 780, nil, nil, 780, 780,
- nil, nil, 780, nil, nil, nil, nil, nil, 780, nil,
- nil, nil, nil, nil, nil, nil, 780, nil, nil, nil,
- nil, 780, 780, 780, 780, 780, 780, nil, nil, nil,
- 780, 780, 786, 786, 786, nil, 786, nil, nil, nil,
- 786, 786, nil, nil, nil, 786, nil, 786, 786, 786,
- 786, 786, 786, 786, nil, nil, nil, nil, 786, 786,
- 786, 786, 786, 786, 786, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 786, nil, nil, 786, 786, 786,
- 786, 786, 786, 786, 786, 786, 786, nil, 786, 786,
- nil, 786, 786, 786, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 786, nil, nil, 786, nil, nil, 786,
- 786, nil, nil, 786, nil, nil, nil, nil, nil, 786,
- nil, nil, nil, nil, nil, nil, nil, 786, nil, nil,
- nil, nil, 786, 786, 786, 786, 786, 786, nil, nil,
- nil, 786, 786, 788, 788, 788, nil, 788, nil, nil,
- nil, 788, 788, nil, nil, nil, 788, nil, 788, 788,
- 788, 788, 788, 788, 788, nil, nil, nil, nil, 788,
- 788, 788, 788, 788, 788, 788, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 788, nil, nil, 788, 788,
- 788, 788, 788, 788, 788, 788, 788, 788, nil, 788,
- 788, nil, 788, 788, 788, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 788, nil, nil, 788, nil, nil,
- 788, 788, nil, nil, 788, nil, nil, nil, nil, nil,
- 788, nil, nil, nil, nil, nil, nil, nil, 788, nil,
- nil, nil, nil, 788, 788, 788, 788, 788, 788, nil,
- nil, nil, 788, 788, 802, 802, 802, nil, 802, nil,
- nil, nil, 802, 802, nil, nil, nil, 802, nil, 802,
- 802, 802, 802, 802, 802, 802, nil, nil, nil, nil,
- 802, 802, 802, 802, 802, 802, 802, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 802, nil, nil, 802,
- 802, 802, 802, 802, 802, 802, 802, 802, 802, nil,
- 802, 802, nil, 802, 802, 802, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 802, nil, nil, 802, nil,
- nil, 802, 802, nil, nil, 802, nil, nil, nil, nil,
- nil, 802, nil, nil, nil, nil, nil, nil, nil, 802,
- nil, nil, nil, nil, 802, 802, 802, 802, 802, 802,
- nil, nil, nil, 802, 802, 820, 820, 820, nil, 820,
- nil, nil, nil, 820, 820, nil, nil, nil, 820, nil,
- 820, 820, 820, 820, 820, 820, 820, nil, nil, nil,
- nil, 820, 820, 820, 820, 820, 820, 820, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 820, nil, nil,
- 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
- nil, 820, 820, nil, nil, nil, 820, nil, 682, 682,
- 682, 682, 682, 682, 682, 682, 682, 682, 682, nil,
- 682, 682, nil, nil, 682, 682, 820, nil, nil, 820,
- nil, nil, 820, 820, nil, nil, 820, nil, nil, nil,
- 682, nil, 682, nil, 682, 682, 682, 682, 682, 682,
- 682, nil, 682, nil, nil, 820, 820, 820, 820, 820,
- 820, nil, nil, nil, 820, 820, 822, 822, 822, 682,
- 822, nil, nil, nil, 822, 822, nil, nil, nil, 822,
- nil, 822, 822, 822, 822, 822, 822, 822, nil, nil,
- nil, nil, 822, 822, 822, 822, 822, 822, 822, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 822, nil,
- nil, 822, 822, 822, 822, 822, 822, 822, 822, 822,
- 822, nil, 822, 822, nil, 822, 822, 822, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 822, nil, nil,
- 822, nil, nil, 822, 822, nil, nil, 822, nil, 822,
- nil, nil, nil, 822, nil, nil, nil, nil, nil, nil,
- nil, 822, nil, nil, nil, nil, 822, 822, 822, 822,
- 822, 822, nil, nil, nil, 822, 822, 827, 827, 827,
- nil, 827, nil, nil, nil, 827, 827, nil, nil, nil,
- 827, nil, 827, 827, 827, 827, 827, 827, 827, nil,
- nil, nil, nil, 827, 827, 827, 827, 827, 827, 827,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 827,
- nil, nil, 827, 827, 827, 827, 827, 827, 827, 827,
- 827, 827, nil, 827, 827, nil, nil, nil, 827, nil,
- 684, 684, 684, 684, 684, 684, 684, 684, 684, 684,
- 684, nil, 684, 684, nil, nil, 684, 684, 827, nil,
- nil, 827, nil, nil, 827, 827, nil, nil, 827, nil,
- nil, nil, 684, nil, 684, nil, 684, 684, 684, 684,
- 684, 684, 684, nil, 684, nil, nil, 827, 827, 827,
- 827, 827, 827, nil, nil, nil, 827, 827, 832, 832,
- 832, 684, 832, nil, nil, nil, 832, 832, nil, nil,
- nil, 832, nil, 832, 832, 832, 832, 832, 832, 832,
- nil, nil, nil, nil, 832, 832, 832, 832, 832, 832,
- 832, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 832, nil, nil, 832, 832, 832, 832, 832, 832, 832,
- 832, 832, 832, nil, 832, 832, nil, 832, 832, 832,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 832,
- nil, nil, 832, nil, nil, 832, 832, nil, nil, 832,
- nil, 832, nil, 832, nil, 832, nil, nil, nil, nil,
- nil, nil, nil, 832, nil, nil, nil, nil, 832, 832,
- 832, 832, 832, 832, nil, nil, nil, 832, 832, 835,
- 835, 835, nil, 835, nil, nil, nil, 835, 835, nil,
- nil, nil, 835, nil, 835, 835, 835, 835, 835, 835,
- 835, nil, nil, nil, nil, 835, 835, 835, 835, 835,
- 835, 835, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 835, nil, nil, 835, 835, 835, 835, 835, 835,
- 835, 835, 835, 835, nil, 835, 835, nil, 835, 835,
- 835, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 835, nil, nil, 835, nil, nil, 835, 835, nil, nil,
- 835, nil, 835, nil, 835, nil, 835, nil, nil, nil,
- nil, nil, nil, nil, 835, nil, nil, nil, nil, 835,
- 835, 835, 835, 835, 835, nil, nil, nil, 835, 835,
- 861, 861, 861, nil, 861, nil, nil, nil, 861, 861,
- nil, nil, nil, 861, nil, 861, 861, 861, 861, 861,
- 861, 861, nil, nil, nil, nil, 861, 861, 861, 861,
- 861, 861, 861, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 861, nil, nil, 861, 861, 861, 861, 861,
- 861, 861, 861, 861, 861, nil, 861, 861, nil, nil,
- nil, 861, nil, 687, 687, 687, 687, 687, 687, 687,
- 687, 687, 687, 687, nil, 687, 687, nil, nil, 687,
- 687, 861, nil, nil, 861, nil, nil, 861, 861, nil,
- nil, 861, nil, nil, nil, 687, nil, 687, nil, 687,
- 687, 687, 687, 687, 687, 687, nil, 687, nil, nil,
- 861, 861, 861, 861, 861, 861, nil, nil, nil, 861,
- 861, 864, 864, 864, 687, 864, nil, nil, nil, 864,
- 864, nil, nil, nil, 864, nil, 864, 864, 864, 864,
- 864, 864, 864, nil, nil, nil, nil, 864, 864, 864,
- 864, 864, 864, 864, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 864, nil, nil, 864, 864, 864, 864,
- 864, 864, 864, 864, 864, 864, nil, 864, 864, nil,
- 864, 864, 864, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 864, nil, nil, 864, nil, nil, 864, 864,
- nil, nil, 864, nil, nil, nil, nil, nil, 864, nil,
- nil, nil, nil, nil, nil, nil, 864, nil, nil, nil,
- nil, 864, 864, 864, 864, 864, 864, nil, nil, nil,
- 864, 864, 867, 867, 867, nil, 867, nil, nil, nil,
- 867, 867, nil, nil, nil, 867, nil, 867, 867, 867,
- 867, 867, 867, 867, nil, nil, nil, nil, 867, 867,
- 867, 867, 867, 867, 867, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 867, nil, nil, 867, 867, 867,
- 867, 867, 867, 867, 867, 867, 867, nil, 867, 867,
- nil, 867, 867, 867, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 867, nil, nil, 867, nil, nil, 867,
- 867, nil, nil, 867, nil, nil, nil, nil, nil, 867,
- nil, nil, nil, nil, nil, nil, nil, 867, nil, nil,
- nil, nil, 867, 867, 867, 867, 867, 867, nil, nil,
- nil, 867, 867, 875, 875, 875, nil, 875, nil, nil,
- nil, 875, 875, nil, nil, nil, 875, nil, 875, 875,
- 875, 875, 875, 875, 875, nil, nil, nil, nil, 875,
- 875, 875, 875, 875, 875, 875, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 875, nil, nil, 875, 875,
- 875, 875, 875, 875, 875, 875, 875, 875, nil, 875,
- 875, nil, nil, nil, 875, nil, 689, 689, 689, 689,
- 689, 689, 689, 689, 689, 689, 689, nil, 689, 689,
- nil, nil, 689, 689, 875, nil, nil, 875, nil, nil,
- 875, 875, nil, nil, 875, nil, nil, nil, 689, nil,
- 689, nil, 689, 689, 689, 689, 689, 689, 689, nil,
- 689, nil, nil, 875, 875, 875, 875, 875, 875, nil,
- nil, nil, 875, 875, 880, 880, 880, 689, 880, nil,
- nil, nil, 880, 880, nil, nil, nil, 880, nil, 880,
- 880, 880, 880, 880, 880, 880, nil, nil, nil, nil,
- 880, 880, 880, 880, 880, 880, 880, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 880, nil, nil, 880,
- 880, 880, 880, 880, 880, 880, 880, 880, 880, nil,
- 880, 880, nil, 880, 880, 880, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 880, nil, nil, 880, nil,
- nil, 880, 880, nil, nil, 880, nil, 880, nil, 880,
- nil, 880, nil, nil, nil, nil, nil, nil, nil, 880,
- nil, nil, nil, nil, 880, 880, 880, 880, 880, 880,
- nil, nil, nil, 880, 880, 886, 886, 886, nil, 886,
- nil, nil, nil, 886, 886, nil, nil, nil, 886, nil,
- 886, 886, 886, 886, 886, 886, 886, nil, nil, nil,
- nil, 886, 886, 886, 886, 886, 886, 886, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 886, nil, nil,
- 886, 886, 886, 886, 886, 886, 886, 886, 886, 886,
- nil, 886, 886, nil, nil, nil, 886, nil, 694, 694,
- 694, 694, 694, 694, 694, 694, 694, 694, 694, nil,
- 694, 694, nil, nil, 694, 694, 886, nil, nil, 886,
- nil, nil, 886, 886, nil, nil, 886, nil, nil, nil,
- 694, nil, 694, nil, 694, 694, 694, 694, 694, 694,
- 694, nil, 694, nil, nil, 886, 886, 886, 886, 886,
- 886, nil, nil, nil, 886, 886, 889, 889, 889, 694,
- 889, nil, nil, nil, 889, 889, nil, nil, nil, 889,
- nil, 889, 889, 889, 889, 889, 889, 889, nil, nil,
- nil, nil, 889, 889, 889, 889, 889, 889, 889, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 889, nil,
- nil, 889, 889, 889, 889, 889, 889, 889, 889, 889,
- 889, nil, 889, 889, nil, 889, 889, 889, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 889, nil, nil,
- 889, nil, nil, 889, 889, nil, nil, 889, nil, nil,
- nil, nil, nil, 889, nil, nil, nil, nil, nil, nil,
- nil, 889, nil, nil, nil, nil, 889, 889, 889, 889,
- 889, 889, nil, nil, nil, 889, 889, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
- 64, nil, nil, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, nil, nil, nil, nil, nil, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
- nil, nil, nil, nil, nil, nil, 64, 64, nil, 64,
- 64, 64, 64, nil, 64, 64, nil, nil, 64, nil,
- nil, nil, nil, 64, 64, 64, 64, nil, nil, nil,
- nil, nil, 64, nil, nil, nil, nil, nil, nil, nil,
- 64, 64, nil, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, nil, 64, nil, nil, 64, 664, 664, 664,
- 664, 664, 664, 664, 664, 664, 664, 664, 664, 664,
- 664, 664, 664, 664, 664, 664, 664, 664, 664, 664,
- 664, nil, nil, 664, 664, 664, 664, 664, 664, 664,
- 664, 664, 664, nil, nil, nil, nil, nil, 664, 664,
- 664, 664, 664, 664, 664, 664, nil, nil, 664, nil,
- nil, nil, nil, nil, nil, nil, 664, 664, nil, 664,
- 664, 664, 664, nil, 664, 664, nil, nil, 664, nil,
- nil, nil, nil, 664, 664, 664, 664, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 664, 664, nil, 664, 664, 664, 664, 664, 664, 664,
- 664, 664, nil, 664, nil, nil, 664, 581, 581, 581,
- 581, 581, 581, 581, 581, 581, 581, 581, nil, 581,
- 581, nil, nil, 581, 581, nil, nil, nil, 581, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 581,
- nil, 581, nil, 581, 581, 581, 581, 581, 581, 581,
- nil, 581, nil, nil, nil, nil, nil, nil, nil, 192,
- 192, nil, nil, 192, nil, nil, nil, nil, 581, nil,
- 581, 192, 192, nil, 192, 192, 192, 192, nil, 192,
- 192, nil, nil, 192, nil, nil, nil, nil, 192, 192,
- 192, 192, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 192, 192, nil, 192, 192,
- 192, 192, 192, 192, 192, 192, 192, nil, 192, 193,
- 193, 192, nil, 193, nil, nil, nil, nil, nil, nil,
- nil, 193, 193, nil, 193, 193, 193, 193, nil, 193,
- 193, nil, nil, 193, nil, nil, nil, nil, 193, 193,
- 193, 193, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 193, 193, nil, 193, 193,
- 193, 193, 193, 193, 193, 193, 193, nil, 193, 251,
- 251, 193, nil, 251, nil, nil, nil, nil, nil, nil,
- nil, 251, 251, nil, 251, 251, 251, 251, nil, 251,
- 251, nil, nil, 251, nil, nil, nil, nil, 251, 251,
- 251, 251, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 251, 251, nil, 251, 251,
- 251, 251, 251, 251, 251, 251, 251, nil, 251, 252,
- 252, 251, nil, 252, nil, nil, nil, nil, nil, nil,
- nil, 252, 252, nil, 252, 252, 252, 252, nil, 252,
- 252, nil, nil, 252, nil, nil, nil, nil, 252, 252,
- 252, 252, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 252, 252, nil, 252, 252,
- 252, 252, 252, 252, 252, 252, 252, nil, 252, 391,
- 391, 252, nil, 391, nil, nil, nil, nil, nil, nil,
- nil, 391, 391, nil, 391, 391, 391, 391, nil, 391,
- 391, nil, nil, 391, nil, nil, nil, nil, 391, 391,
- 391, 391, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 391, 391, nil, 391, 391,
- 391, 391, 391, 391, 391, 391, 391, nil, 391, 392,
- 392, 391, nil, 392, nil, nil, nil, nil, nil, nil,
- nil, 392, 392, nil, 392, 392, 392, 392, nil, 392,
- 392, nil, nil, 392, nil, nil, nil, nil, 392, 392,
- 392, 392, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 392, 392, nil, 392, 392,
- 392, 392, 392, 392, 392, 392, 392, nil, 392, nil,
- nil, 392, 423, 423, 423, 423, 423, 423, 423, 423,
- 423, 423, 423, nil, 423, 423, nil, nil, 423, 423,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 423, nil, 423, nil, 423, 423,
- 423, 423, 423, 423, 423, nil, 423, nil, nil, nil,
- nil, nil, nil, 457, 457, nil, nil, 457, nil, nil,
- nil, nil, nil, 423, 423, 457, 457, nil, 457, 457,
- 457, 457, nil, 457, 457, nil, nil, 457, nil, nil,
- nil, nil, 457, 457, 457, 457, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 457,
- 457, nil, 457, 457, 457, 457, 457, 457, 457, 457,
- 457, nil, 457, 458, 458, 457, nil, 458, nil, nil,
- nil, nil, nil, nil, nil, 458, 458, nil, 458, 458,
- 458, 458, nil, 458, 458, nil, nil, 458, nil, nil,
- nil, nil, 458, 458, 458, 458, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 458,
- 458, nil, 458, 458, 458, 458, 458, 458, 458, 458,
- 458, nil, 458, 466, 466, 458, nil, 466, nil, nil,
- nil, nil, nil, nil, nil, 466, 466, nil, 466, 466,
- 466, 466, nil, 466, 466, nil, nil, 466, nil, nil,
- nil, nil, 466, 466, 466, 466, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 466,
- 466, nil, 466, 466, 466, 466, 466, 466, 466, 466,
- 466, nil, 466, 467, 467, 466, nil, 467, nil, nil,
- nil, nil, nil, nil, nil, 467, 467, nil, 467, 467,
- 467, 467, nil, 467, 467, nil, nil, 467, nil, nil,
- nil, nil, 467, 467, 467, 467, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 467,
- 467, nil, 467, 467, 467, 467, 467, 467, 467, 467,
- 467, nil, 467, 498, 498, 467, nil, 498, nil, nil,
- nil, nil, nil, nil, nil, 498, 498, nil, 498, 498,
- 498, 498, nil, 498, 498, nil, nil, 498, nil, nil,
- nil, nil, 498, 498, 498, 498, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 498,
- 498, nil, 498, 498, 498, 498, 498, 498, 498, 498,
- 498, nil, 498, 499, 499, 498, nil, 499, nil, nil,
- nil, nil, nil, nil, nil, 499, 499, nil, 499, 499,
- 499, 499, nil, 499, 499, nil, nil, 499, nil, nil,
- nil, nil, 499, 499, 499, 499, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 499,
- 499, nil, 499, 499, 499, 499, 499, 499, 499, 499,
- 499, nil, 499, 505, 505, 499, nil, 505, nil, nil,
- nil, nil, nil, nil, nil, 505, 505, nil, 505, 505,
- 505, 505, nil, 505, 505, nil, nil, 505, nil, nil,
- nil, nil, 505, 505, 505, 505, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 505,
- 505, nil, 505, 505, 505, 505, 505, 505, 505, 505,
- 505, nil, 505, 507, 507, 505, nil, 507, nil, nil,
- nil, nil, nil, nil, nil, 507, 507, nil, 507, 507,
- 507, 507, nil, 507, 507, nil, nil, 507, nil, nil,
- nil, nil, 507, 507, 507, 507, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 507,
- 507, nil, 507, 507, 507, 507, 507, 507, 507, 507,
- 507, nil, 507, 578, 578, 507, nil, 578, nil, nil,
- nil, nil, nil, nil, nil, 578, 578, nil, 578, 578,
- 578, 578, nil, 578, 578, nil, nil, 578, nil, nil,
- nil, nil, 578, 578, 578, 578, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 578,
- 578, nil, 578, 578, 578, 578, 578, 578, 578, 578,
- 578, nil, 578, 579, 579, 578, nil, 579, nil, nil,
- nil, nil, nil, nil, nil, 579, 579, nil, 579, 579,
- 579, 579, nil, 579, 579, nil, nil, 579, nil, nil,
- nil, nil, 579, 579, 579, 579, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 579,
- 579, nil, 579, 579, 579, 579, 579, 579, 579, 579,
- 579, nil, 579, 823, 823, 579, nil, 823, nil, nil,
- nil, nil, nil, nil, nil, 823, 823, nil, 823, 823,
- 823, 823, nil, 823, 823, nil, nil, 823, nil, nil,
- nil, nil, 823, 823, 823, 823, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 823,
- 823, nil, 823, 823, 823, 823, 823, 823, 823, 823,
- 823, nil, 823, 824, 824, 823, nil, 824, nil, nil,
- nil, nil, nil, nil, nil, 824, 824, nil, 824, 824,
- 824, 824, nil, 824, 824, nil, nil, 824, nil, nil,
- nil, nil, 824, 824, 824, 824, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 824,
- 824, nil, 824, 824, 824, 824, 824, 824, 824, 824,
- 824, nil, 824, nil, nil, 824, 480, 480, 480, 480,
- 480, 480, 480, 480, 480, 480, 480, nil, 480, 480,
- nil, nil, 480, 480, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 480, nil,
- 480, nil, 480, 480, 480, 480, 480, 480, 480, nil,
- 480, nil, 731, 731, 731, 731, 731, 731, 731, 731,
- 731, 731, 731, nil, 731, 731, 480, 480, 731, 731,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 731, nil, 731, nil, 731, 731,
- 731, 731, 731, 731, 731, nil, 731, nil, 767, 767,
- 767, 767, 767, 767, 767, 767, 767, 767, 767, nil,
- 767, 767, 731, 731, 767, 767, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 767, nil, 767, nil, 767, 767, 767, 767, 767, 767,
- 767, nil, 767, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 767 ]
-
-racc_action_pointer = [
- 1233, 30, nil, -98, 863, 4770, 4891, 5012, -53, 2,
- 71, 90, 160, 218, 174, 130, nil, 5125, 5246, 6094,
- 234, nil, 5367, 5488, 5609, 292, 138, 5730, 5851, nil,
- 1355, 5972, 6093, nil, 146, 316, 245, 346, 6214, 6335,
- 6456, 183, 300, nil, nil, nil, nil, nil, nil, nil,
- 214, 1477, 6577, 6698, 6819, 58, 6940, 7061, nil, nil,
- 735, 7182, 7303, 7424, 22775, nil, nil, nil, nil, nil,
- -92, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 7545, nil, nil, nil, 7666, nil, nil, nil,
- nil, nil, nil, nil, nil, 311, nil, 863, nil, nil,
- nil, 7787, 7908, 8029, 8150, 8271, 981, nil, 221, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 196, nil, 1599, 1721, 8392, 8513,
- 8634, 8755, 23000, 23060, 8876, 8997, 9118, nil, 377, -67,
- 279, -52, 212, 274, 1843, nil, nil, 9239, 9360, 9481,
- 9602, 9723, 9844, 9965, 10086, 10207, 10328, 10449, 10570, 10691,
- 10812, 10933, 11054, 11175, 11296, 11417, 11538, 11659, 11780, 11901,
- 12022, 12143, 12264, 12385, nil, nil, nil, 7183, nil, 242,
- 254, 12506, nil, 12627, 310, nil, nil, nil, nil, nil,
- nil, 23120, 23180, 313, 12748, 12869, nil, nil, nil, nil,
- nil, nil, nil, 12990, 325, 1965, 333, 352, 315, 13111,
- 2087, 399, 496, 430, 520, 411, 382, 161, nil, 443,
- 430, nil, nil, 335, 472, 486, 521, nil, 498, nil,
- 13232, nil, 565, 566, 457, nil, 460, 119, 136, 13353,
- 493, 147, 479, 244, nil, 482, -7, 8, 13474, 13595,
- -67, 32, 464, -9, 522, 545, -1, 575, nil, nil,
- 319, 345, 115, nil, 614, nil, 9, 13716, nil, nil,
- 311, 419, 453, 456, 457, 478, 488, 499, nil, 519,
- nil, 13837, nil, 149, 207, 235, 246, -35, 279, nil,
- 1103, nil, nil, nil, nil, nil, nil, 13958, nil, nil,
- nil, nil, 509, 511, nil, nil, 735, nil, 495, 14071,
- nil, 499, nil, nil, 7304, 535, 340, 342, 14192, nil,
- nil, 0, 541, 107, nil, 14313, 14434, nil, 7425, nil,
- nil, 23240, 23300, 14555, -33, 14676, 14797, 14918, 614, 863,
- 377, 384, 566, 574, 575, 576, 2941, 3063, 3185, 1599,
- 980, 1721, 1843, 1965, 2087, 2209, 2331, 2453, 2575, 249,
- 465, 2697, 2819, 23348, -48, nil, 15039, nil, 15160, 514,
- nil, 15281, 321, nil, nil, 376, nil, nil, 563, 531,
- -64, 529, 630, nil, nil, 15402, -27, -13, 571, nil,
- 572, 544, nil, nil, nil, 586, 15523, 23414, 23474, 617,
- 606, nil, nil, 15644, 15765, 15886, 23534, 23594, 13112, 16007,
- 688, 16128, nil, 582, nil, nil, 16249, nil, nil, 16370,
- 24122, nil, 16491, nil, nil, nil, 2209, 711, nil, nil,
- 2331, 68, 109, 708, 716, 2453, 16612, 16733, 23654, 23714,
- 4, nil, nil, 658, nil, 23774, 16854, 23834, nil, nil,
- 16975, 100, -34, 2575, 997, nil, nil, nil, -32, nil,
- nil, nil, 598, nil, nil, nil, 609, nil, 147, nil,
- nil, 606, nil, nil, 17096, nil, nil, 17209, 17330, nil,
- 349, 17451, 17572, 648, nil, nil, 17693, 649, nil, 17814,
- 86, 115, 493, 614, 655, 1106, 17935, 18056, nil, 2697,
- 18177, 621, nil, 666, 18298, nil, 674, nil, 663, nil,
- nil, nil, nil, nil, 113, nil, 673, 674, 23894, 23954,
- 18419, 22933, 69, 643, 18540, nil, 683, nil, 2819, 2941,
- nil, 1, nil, 681, 63, 112, 687, 347, 735, 688,
- 19630, 712, 714, -2, 772, nil, 3063, 654, 707, nil,
- nil, 707, 18661, nil, nil, 506, nil, 800, nil, nil,
- nil, nil, nil, 810, nil, 814, 700, 15, 18782, 738,
- 13, 23, 25, 75, 18903, 348, 788, nil, 740, 3185,
- 331, nil, nil, 835, 3307, 1284, 337, 718, 719, 726,
- nil, nil, nil, nil, nil, 724, nil, nil, nil, nil,
- 806, nil, nil, 809, 22885, 773, nil, nil, nil, nil,
- nil, 3429, nil, nil, nil, nil, nil, 20114, 744, 19024,
- 19145, nil, 21324, nil, 21566, nil, nil, 21929, nil, 22292,
- 19266, 19387, 19508, 167, 22534, nil, 745, 984, 19629, nil,
- 770, 868, 760, nil, 19750, 761, 3551, nil, nil, 802,
- 803, -63, 871, 19871, nil, 19992, 772, nil, 832, 813,
- 933, 738, nil, nil, 3673, nil, nil, 31, 20113, nil,
- nil, 24168, 938, nil, 20234, 941, 3795, 3917, nil, nil,
- 20355, 4039, nil, 26, 133, nil, 942, nil, 4161, nil,
- 946, 836, nil, 1406, nil, -43, nil, nil, 436, 20476,
- nil, nil, nil, nil, 862, nil, nil, 24214, 20597, 20718,
- 863, 866, 920, 907, 847, 884, nil, nil, nil, nil,
- 20839, nil, 873, 903, 869, nil, 20960, 870, 21081, nil,
- nil, nil, nil, nil, 4283, nil, nil, nil, 32, nil,
- 999, 1001, 21202, 332, nil, nil, 1002, nil, 936, 898,
- 899, nil, nil, 900, 899, nil, nil, 1528, nil, nil,
- 21323, 1106, 21444, 24014, 24074, 918, 933, 21565, 5973, nil,
- nil, nil, 21686, 914, nil, 21807, 918, 1046, 4405, nil,
- nil, nil, nil, nil, nil, 4527, nil, nil, 282, nil,
- nil, nil, 4649, nil, 927, 959, 965, 362, 400, 482,
- 977, 21928, nil, nil, 22049, 931, nil, 22170, nil, nil,
- 545, 1054, 938, 1057, 972, 22291, 980, nil, 945, nil,
- 22412, 948, nil, nil, nil, nil, 22533, nil, nil, 22654,
- nil, nil, 952, nil ]
-
-racc_action_default = [
- -4, -497, -1, -485, -5, -497, -497, -497, -497, -497,
- -497, -497, -497, -497, -271, -32, -33, -497, -497, -38,
- -40, -41, -282, -315, -316, -45, -249, -361, -285, -58,
- -4, -62, -67, -68, -497, -428, -497, -497, -497, -497,
- -497, -487, -214, -264, -265, -266, -267, -268, -269, -270,
- -475, -4, -497, -496, -467, -288, -497, -497, -292, -295,
- -485, -497, -497, -497, -497, -317, -318, -381, -382, -383,
- -384, -385, -399, -388, -401, -401, -392, -397, -411, -401,
- -413, -414, -417, -418, -419, -420, -421, -422, -423, -424,
- -425, -426, -427, -430, -431, -497, -3, -486, -492, -493,
- -494, -497, -497, -497, -497, -497, -6, -8, -497, -93,
- -94, -95, -96, -97, -98, -99, -100, -101, -105, -106,
- -107, -108, -109, -110, -111, -112, -113, -114, -115, -116,
- -117, -118, -119, -120, -121, -122, -123, -124, -125, -126,
- -127, -128, -129, -130, -131, -132, -133, -134, -135, -136,
- -137, -138, -139, -140, -141, -142, -143, -144, -145, -146,
- -147, -148, -149, -150, -151, -152, -153, -154, -155, -156,
- -157, -158, -159, -160, -161, -162, -163, -164, -165, -166,
- -167, -168, -169, -170, -13, -102, -4, -4, -497, -497,
- -497, -496, -497, -497, -497, -497, -497, -36, -497, -428,
- -497, -271, -497, -497, -4, -37, -206, -497, -497, -497,
- -497, -497, -497, -497, -497, -497, -497, -497, -497, -497,
- -497, -497, -497, -497, -497, -497, -497, -497, -497, -497,
- -497, -497, -497, -497, -351, -353, -42, -215, -228, -258,
- -258, -497, -236, -497, -259, -282, -315, -316, -470, -43,
- -44, -497, -497, -50, -496, -497, -287, -356, -362, -364,
- -56, -360, -57, -497, -58, -4, -497, -497, -63, -65,
- -4, -72, -497, -497, -79, -285, -487, -497, -319, -361,
- -497, -66, -70, -278, -415, -416, -497, -191, -192, -207,
- -497, -488, -373, -497, -274, -216, -487, -489, -489, -497,
- -497, -489, -497, -489, -289, -39, -497, -497, -497, -497,
- -485, -497, -486, -428, -497, -497, -271, -497, -331, -332,
- -88, -89, -497, -91, -497, -271, -497, -497, -428, -308,
- -93, -94, -131, -132, -148, -153, -160, -163, -310, -497,
- -465, -497, -386, -497, -497, -497, -497, -497, -497, 894,
- -7, -495, -14, -15, -16, -17, -18, -497, -10, -11,
- -12, -103, -497, -497, -21, -29, -171, -259, -497, -497,
- -22, -30, -31, -23, -173, -497, -476, -477, -226, -478,
- -479, -476, -249, -477, -359, -481, -482, -28, -180, -34,
- -35, -497, -497, -496, -278, -497, -497, -497, -181, -182,
- -183, -184, -185, -186, -187, -188, -193, -194, -195, -196,
- -197, -198, -199, -200, -201, -202, -203, -204, -205, -208,
- -209, -210, -211, -497, -347, -229, -497, -231, -497, -258,
- -256, -497, -249, -476, -477, -249, -48, -51, -497, -487,
- -487, -258, -228, -250, -251, -252, -347, -347, -497, -284,
- -497, -59, -276, -71, -64, -497, -496, -497, -497, -78,
- -497, -415, -416, -497, -497, -497, -497, -497, -212, -497,
- -496, -496, -273, -487, -217, -218, -491, -490, -220, -491,
- -487, -280, -491, -469, -281, -468, -4, -320, -321, -322,
- -4, -497, -497, -497, -497, -4, -497, -496, -497, -497,
- -278, -301, -88, -89, -90, -497, -496, -497, -304, -432,
- -497, -497, -497, -4, -445, -312, -483, -484, -487, -387,
- -400, -403, -497, -405, -389, -402, -497, -391, -497, -394,
- -396, -497, -412, -9, -497, -19, -20, -497, -497, -263,
- -279, -497, -497, -52, -227, -357, -497, -54, -358, -497,
- -476, -477, -480, -277, -497, -171, -497, -497, -349, -4,
- -497, -258, -257, -260, -497, -471, -497, -235, -497, -472,
- -46, -354, -47, -355, -347, -222, -497, -497, -497, -497,
- -497, -38, -497, -258, -497, -248, -497, -254, -4, -4,
- -283, -59, -69, -497, -476, -477, -226, -75, -77, -497,
- -179, -189, -190, -497, -496, -329, -4, -374, -496, -375,
- -376, -497, -497, -260, -221, -496, -323, -496, -293, -324,
- -325, -326, -296, -497, -299, -497, -367, -497, -497, -497,
- -476, -477, -480, -277, -497, -88, -89, -92, -497, -4,
- -497, -434, -306, -497, -4, -445, -497, -464, -464, -464,
- -444, -446, -447, -448, -449, -450, -451, -454, -456, -457,
- -459, -460, -461, -497, -497, -497, -404, -407, -408, -409,
- -410, -4, -390, -393, -395, -398, -104, -172, -261, -497,
- -497, -25, -175, -26, -176, -53, -27, -177, -55, -178,
- -497, -497, -497, -279, -213, -333, -335, -345, -497, -348,
- -497, -497, -258, -233, -497, -258, -4, -223, -224, -226,
- -226, -487, -497, -497, -241, -497, -258, -253, -497, -497,
- -497, -73, -286, -2, -4, -380, -330, -497, -497, -378,
- -275, -487, -497, -327, -497, -497, -4, -4, -298, -300,
- -497, -4, -369, -279, -497, -279, -497, -433, -4, -309,
- -497, -487, -436, -497, -440, -497, -442, -443, -497, -497,
- -458, -462, -313, -466, -497, -262, -24, -174, -497, -336,
- -80, -497, -497, -87, -344, -497, -346, -350, -352, -230,
- -497, -232, -497, -497, -258, -238, -497, -258, -497, -247,
- -255, -363, -365, -379, -4, -377, -219, -290, -497, -291,
- -497, -497, -497, -496, -302, -305, -497, -311, -497, -464,
- -464, -452, -463, -464, -497, -455, -453, -445, -406, -334,
- -497, -341, -496, -497, -497, -86, -497, -497, -258, -49,
- -225, -237, -497, -258, -243, -497, -258, -373, -4, -294,
- -297, -368, -366, -370, -371, -4, -307, -435, -497, -438,
- -439, -441, -4, -337, -340, -497, -497, -82, -84, -83,
- -85, -497, -343, -234, -497, -258, -239, -497, -242, -372,
- -496, -497, -464, -497, -497, -497, -81, -342, -258, -244,
- -497, -258, -328, -303, -437, -314, -497, -339, -240, -497,
- -245, -338, -258, -246 ]
-
-racc_goto_table = [
- 10, 205, 240, 240, 240, 10, 268, 438, 112, 112,
- 100, 300, 338, 470, 293, 644, 242, 242, 242, 440,
- 107, 185, 238, 238, 238, 115, 115, 117, 117, 494,
- 10, 486, 490, 650, 303, 437, 239, 239, 239, 253,
- 260, 262, 604, 290, 365, 372, 732, 618, 622, 96,
- 565, 10, 815, 296, 257, 261, 281, 571, 504, 520,
- 573, 318, 529, 236, 249, 250, 112, 100, 301, 559,
- 810, 264, 813, 266, 106, 475, 478, 344, 345, 483,
- 1, 485, 348, 723, 754, 756, 757, 326, 329, 306,
- 307, 588, 589, 310, 97, 13, 184, 10, 319, 357,
- 13, 565, 574, 534, 587, 10, 311, 443, 586, 718,
- 340, 302, 198, 198, 304, 317, 508, 198, 198, 198,
- 339, 515, 473, 308, 736, 13, 272, 272, 309, 737,
- 634, 278, 278, 845, 352, 353, 354, 355, 639, 748,
- 513, 197, 514, 664, 817, 815, 13, 198, 198, 375,
- 696, 198, 198, 700, 424, 198, 314, 324, 324, 446,
- 447, 278, 278, 278, 650, 872, 350, 627, 842, 608,
- 727, 364, 370, 373, 356, 342, 294, 387, 343, 346,
- 305, 305, 528, 347, 305, 666, 10, 10, 671, 735,
- 751, 296, 13, 809, 811, 386, 198, 198, 198, 198,
- 13, 2, 368, 368, 10, nil, nil, nil, nil, nil,
- nil, 623, nil, nil, nil, nil, nil, nil, nil, 706,
- nil, nil, nil, 605, 610, 305, 305, 305, 305, nil,
- nil, 267, nil, nil, 240, 240, nil, nil, nil, nil,
- nil, 637, nil, 240, 674, nil, 849, 850, 242, 242,
- 851, nil, nil, nil, 442, 238, nil, 242, nil, nil,
- nil, nil, nil, 238, nil, 10, nil, nil, 441, 239,
- 10, nil, nil, nil, nil, nil, nil, 239, 460, nil,
- nil, 13, 13, 198, 198, 198, 198, nil, nil, 198,
- 198, 198, 14, 454, nil, 439, 444, 14, 474, 13,
- 804, 882, nil, 427, 448, 257, 264, 261, 450, 884,
- nil, 264, nil, 455, nil, nil, nil, 100, 852, 389,
- 390, nil, 14, 274, 274, 685, 565, 609, nil, 688,
- nil, nil, nil, 509, 571, 573, 650, 565, nil, nil,
- nil, 491, 492, 14, nil, 711, nil, nil, nil, 198,
- 198, 554, 741, 316, 325, 325, 493, 725, 198, 112,
- 13, 729, nil, nil, 272, 13, nil, nil, 605, 278,
- 605, 533, nil, nil, nil, 843, 115, nil, 117, nil,
- nil, nil, nil, nil, nil, nil, nil, 362, 363, 14,
- nil, 543, nil, 296, 386, 547, nil, 14, nil, nil,
- nil, nil, nil, 198, 198, 267, 561, nil, nil, nil,
- 548, nil, nil, nil, 593, nil, nil, nil, nil, nil,
- nil, nil, 198, nil, nil, 583, nil, nil, nil, nil,
- nil, nil, 305, 305, nil, nil, 198, nil, nil, 585,
- nil, 576, 577, nil, nil, 570, nil, nil, 572, nil,
- nil, 512, 794, nil, nil, 629, 296, 565, nil, nil,
- 386, nil, nil, nil, 638, 518, 267, nil, nil, 386,
- nil, 267, nil, nil, nil, 611, 643, nil, 14, 14,
- nil, nil, 614, nil, nil, 368, 10, nil, 198, nil,
- 10, nil, 567, nil, nil, 10, 14, 296, 619, 619,
- nil, 386, 565, nil, nil, nil, 296, 386, nil, nil,
- 626, nil, nil, 10, nil, nil, nil, nil, 641, 642,
- 665, 844, nil, 838, 681, 683, nil, nil, nil, 686,
- nil, nil, 695, nil, nil, nil, 112, nil, nil, nil,
- 198, nil, nil, 640, nil, nil, nil, nil, 676, nil,
- nil, 198, nil, 115, nil, 117, 605, 14, 869, 10,
- 561, 274, 14, nil, 198, nil, nil, nil, nil, 294,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 712, 13, nil, nil, nil, 13, nil, nil, 10, 10,
- 13, nil, 198, 603, nil, nil, 378, 382, 35, nil,
- nil, 198, 746, 35, nil, 198, 10, 750, 13, nil,
- nil, nil, 762, 12, nil, nil, nil, nil, 12, nil,
- nil, nil, nil, 605, 703, nil, nil, 548, 35, 271,
- 271, nil, nil, nil, 305, nil, 198, 198, nil, 10,
- nil, 198, nil, 12, 10, nil, 714, 747, nil, 35,
- nil, nil, 272, 752, 13, 432, 435, 278, nil, 313,
- 328, 328, 328, 766, 12, nil, 112, 744, nil, nil,
- nil, 10, nil, nil, nil, 198, nil, nil, nil, nil,
- nil, nil, nil, 13, 13, nil, nil, 615, nil, nil,
- nil, 617, nil, 787, nil, 35, 625, 774, 776, nil,
- nil, 13, nil, 35, nil, nil, 10, 785, nil, nil,
- 12, 806, nil, 783, nil, nil, nil, nil, 12, nil,
- nil, nil, nil, nil, 10, nil, nil, 784, 795, 198,
- nil, nil, nil, 796, 13, nil, 10, 10, nil, 13,
- nil, 10, nil, nil, 819, nil, nil, nil, 10, nil,
- nil, 619, nil, 808, nil, nil, nil, nil, 305, nil,
- 701, nil, nil, nil, nil, 779, 13, 798, 781, nil,
- nil, nil, nil, nil, nil, 198, nil, nil, 14, 789,
- 856, nil, 14, nil, 35, 35, nil, 14, nil, 719,
- 720, nil, 771, 771, 10, 378, 382, 278, 278, 12,
- 12, 13, 35, nil, nil, 14, nil, 726, nil, nil,
- nil, nil, 865, nil, 670, 873, nil, 12, nil, 13,
- 853, 854, 296, 771, nil, nil, 386, 862, 278, 198,
- nil, 13, 13, nil, nil, nil, 13, nil, 10, nil,
- nil, nil, nil, 13, nil, 10, nil, 831, nil, 274,
- 834, 14, 10, nil, nil, nil, nil, nil, 305, nil,
- nil, 877, 599, 35, 272, nil, nil, 271, 35, 278,
- nil, 599, 764, nil, nil, 887, nil, nil, 12, nil,
- 14, 14, nil, 12, nil, nil, 891, nil, nil, 13,
- nil, 863, nil, nil, nil, nil, 866, nil, 14, 868,
- nil, nil, nil, 599, nil, nil, nil, 782, nil, 599,
- nil, nil, nil, nil, nil, 771, 771, 198, nil, nil,
- 278, 278, 771, nil, nil, 793, nil, 278, 879, nil,
- nil, 14, nil, 13, nil, nil, 14, 800, 801, nil,
- 13, 888, 803, nil, 890, nil, nil, 13, nil, nil,
- nil, nil, nil, nil, nil, 893, 771, nil, nil, nil,
- nil, 278, nil, 14, nil, nil, nil, nil, nil, nil,
- 771, nil, nil, nil, nil, 278, nil, nil, nil, nil,
- nil, 771, 709, 710, nil, nil, 278, nil, nil, 773,
- 773, nil, nil, nil, nil, 837, nil, nil, 14, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 14, nil, nil, nil,
- 773, nil, nil, nil, nil, nil, nil, nil, 14, 14,
- nil, nil, nil, 14, nil, nil, nil, nil, nil, 870,
- 14, nil, nil, nil, nil, nil, 871, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 274, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 35, nil, 14, nil, 35, nil,
- nil, nil, nil, 35, nil, nil, nil, nil, nil, 12,
- nil, nil, nil, 12, nil, nil, nil, nil, 12, nil,
- nil, 35, 773, 773, nil, nil, nil, nil, nil, 773,
- nil, nil, nil, nil, nil, nil, 12, nil, nil, nil,
- 14, nil, nil, nil, nil, nil, nil, 14, nil, nil,
- nil, nil, nil, nil, 14, nil, nil, nil, nil, nil,
- nil, nil, nil, 773, nil, 271, nil, 35, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 773, nil, nil,
- nil, nil, 12, nil, nil, nil, nil, nil, 773, nil,
- nil, nil, nil, nil, nil, nil, 35, 35, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 12, 12, nil, 35, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 12,
- nil, nil, nil, nil, nil, nil, nil, nil, 599, nil,
- nil, nil, nil, nil, nil, nil, nil, 35, nil, nil,
- nil, nil, 35, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 12, nil, nil, nil, nil, 12, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 35,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 12, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 770, 770, nil, nil, nil,
- nil, nil, nil, nil, 35, nil, nil, 206, nil, nil,
- nil, 237, 237, 237, nil, nil, nil, nil, nil, 12,
- nil, nil, 35, nil, nil, nil, 770, 287, 288, 289,
- nil, nil, nil, nil, 35, 35, nil, 12, nil, 35,
- nil, nil, 237, 237, nil, nil, 35, nil, nil, 12,
- 12, nil, nil, nil, 12, nil, nil, nil, nil, 367,
- 371, 12, nil, nil, nil, nil, nil, 271, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 35, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 12, nil, nil,
- nil, nil, 429, nil, 430, nil, nil, nil, 770, 770,
- nil, nil, nil, nil, nil, 770, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 35, nil, nil, nil,
- nil, nil, nil, 35, nil, nil, nil, nil, nil, nil,
- 35, 12, nil, nil, nil, nil, nil, nil, 12, 770,
- nil, nil, nil, nil, nil, 12, nil, nil, nil, nil,
- nil, nil, nil, 770, nil, nil, nil, 366, 237, 374,
- 237, nil, nil, 388, 770, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 206, 398, 399, 400,
- 401, 402, 403, 404, 405, 406, 407, 408, 409, 410,
- 411, 412, 413, 414, 415, 416, 417, 418, 419, 420,
- 421, 422, 423, nil, nil, nil, nil, nil, nil, nil,
- 237, nil, 237, nil, nil, nil, nil, nil, nil, nil,
- 539, nil, nil, 237, 237, nil, nil, nil, nil, nil,
- nil, nil, 237, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 468,
- nil, nil, nil, nil, nil, nil, nil, nil, 480, nil,
- nil, nil, nil, nil, nil, nil, nil, 563, nil, 566,
- nil, nil, 569, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 582, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 607, nil, nil, nil, nil, 613, nil, nil,
- 566, nil, nil, 613, nil, nil, nil, nil, 237, nil,
- nil, nil, nil, nil, nil, nil, nil, 367, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 237, nil, 388, 555, 374, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 678,
- nil, nil, nil, nil, nil, 237, nil, 237, nil, nil,
- 237, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 702, nil, nil, 581, 705, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 237, nil, nil, nil, nil,
- nil, 563, 600, 601, 602, 716, nil, 26, nil, nil,
- 237, nil, 26, nil, nil, 237, nil, nil, 237, nil,
- nil, 237, nil, nil, 26, 26, nil, nil, nil, 26,
- 26, 26, nil, nil, nil, 237, 237, 26, nil, nil,
- nil, nil, nil, nil, nil, 237, nil, nil, nil, 742,
- nil, nil, nil, nil, nil, nil, nil, nil, 26, 26,
- 26, nil, nil, 26, 26, nil, nil, 26, nil, nil,
- nil, nil, nil, nil, nil, nil, 677, 237, nil, nil,
- 682, 684, nil, nil, nil, 687, nil, nil, 689, nil,
- nil, nil, nil, nil, nil, 694, nil, nil, nil, 237,
- 765, nil, nil, 237, 26, nil, nil, nil, 26, 26,
- 26, 26, 26, nil, nil, nil, nil, nil, nil, 237,
- nil, nil, nil, 237, nil, 566, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 566, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 731, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 613, nil, nil, nil, nil, nil, 237, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 816, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 26, 26, 26, 26, 26, 26, nil,
- nil, 26, 26, 26, nil, nil, nil, 833, nil, 836,
- nil, 26, nil, nil, nil, nil, nil, nil, 237, 767,
- nil, nil, nil, 841, nil, nil, nil, nil, nil, 682,
- 684, 687, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 237, nil, nil, nil, nil, nil, nil,
- nil, nil, 237, 563, 237, nil, 566, nil, nil, nil,
- nil, 26, 26, nil, nil, nil, nil, nil, nil, nil,
- 26, nil, 26, nil, nil, nil, nil, 26, nil, 237,
- nil, nil, nil, nil, nil, 878, nil, nil, 881, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 237, nil,
- nil, 566, nil, nil, nil, nil, nil, 767, nil, nil,
- 892, nil, nil, nil, nil, 26, 26, nil, nil, 828,
- nil, nil, nil, nil, nil, 237, nil, 237, nil, nil,
- nil, nil, nil, nil, 26, nil, nil, nil, nil, nil,
- nil, 237, nil, nil, nil, nil, nil, nil, 26, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 237, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 237, nil, nil, 237, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 26, nil, nil, 237, nil, nil, 237, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 237,
- nil, nil, nil, nil, nil, nil, nil, nil, 237, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 26, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 26, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 26, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 26, nil, nil, nil, 26, nil, nil,
- nil, nil, 26, nil, 26, nil, nil, nil, nil, nil,
- nil, nil, nil, 26, nil, nil, nil, 26, nil, nil,
- 26, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 26, 26,
- nil, nil, nil, 26, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 26, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 26, nil, nil,
- nil, nil, nil, nil, nil, 26, 26, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 26, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 26, nil, nil, nil, nil, 26, nil, nil, nil,
- nil, 26, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 26, nil,
- nil, nil, nil, nil, nil, nil, nil, 26, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 26, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 26, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 26, nil, 26, 26, nil, nil, nil, 26, nil,
- nil, nil, nil, nil, nil, 26, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 26, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 26,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 26, nil, nil, nil, nil,
- nil, nil, 26, nil, nil, nil, nil, nil, nil, 26 ]
-
-racc_goto_check = [
- 14, 15, 56, 56, 56, 14, 39, 53, 45, 45,
- 82, 19, 44, 4, 3, 83, 60, 60, 60, 29,
- 11, 11, 26, 26, 26, 48, 48, 49, 49, 79,
- 14, 76, 76, 126, 56, 32, 54, 54, 54, 31,
- 31, 31, 5, 51, 21, 21, 77, 78, 78, 8,
- 131, 14, 128, 26, 57, 57, 40, 58, 42, 117,
- 58, 14, 117, 28, 28, 28, 45, 82, 54, 33,
- 124, 36, 124, 37, 9, 55, 55, 114, 114, 55,
- 1, 55, 114, 6, 125, 125, 125, 43, 43, 13,
- 13, 33, 33, 13, 10, 18, 12, 14, 16, 24,
- 18, 131, 34, 50, 61, 14, 8, 62, 64, 65,
- 72, 73, 18, 18, 74, 80, 81, 18, 18, 18,
- 84, 85, 86, 87, 88, 18, 18, 18, 89, 90,
- 91, 52, 52, 92, 13, 13, 13, 13, 93, 94,
- 95, 23, 96, 97, 98, 128, 18, 18, 18, 19,
- 99, 18, 18, 100, 102, 18, 18, 18, 18, 104,
- 105, 52, 52, 52, 126, 124, 9, 106, 107, 108,
- 109, 15, 15, 15, 9, 112, 23, 15, 113, 115,
- 23, 23, 116, 118, 23, 119, 14, 14, 120, 5,
- 121, 26, 18, 123, 127, 45, 18, 18, 18, 18,
- 18, 2, 54, 54, 14, nil, nil, nil, nil, nil,
- nil, 79, nil, nil, nil, nil, nil, nil, nil, 33,
- nil, nil, nil, 53, 53, 23, 23, 23, 23, nil,
- nil, 2, nil, nil, 56, 56, nil, nil, nil, nil,
- nil, 42, nil, 56, 117, nil, 125, 125, 60, 60,
- 125, nil, nil, nil, 26, 26, nil, 60, nil, nil,
- nil, nil, nil, 26, nil, 14, nil, nil, 54, 54,
- 14, nil, nil, nil, nil, nil, nil, 54, 51, nil,
- nil, 18, 18, 18, 18, 18, 18, nil, nil, 18,
- 18, 18, 20, 40, nil, 28, 28, 20, 51, 18,
- 78, 77, nil, 59, 28, 57, 36, 57, 37, 125,
- nil, 36, nil, 37, nil, nil, nil, 82, 83, 23,
- 23, nil, 20, 20, 20, 32, 131, 21, nil, 32,
- nil, nil, nil, 82, 58, 58, 126, 131, nil, nil,
- nil, 13, 13, 20, nil, 29, nil, nil, nil, 18,
- 18, 19, 76, 20, 20, 20, 8, 53, 18, 45,
- 18, 53, nil, nil, 18, 18, nil, nil, 53, 52,
- 53, 11, nil, nil, nil, 5, 48, nil, 49, nil,
- nil, nil, nil, nil, nil, nil, nil, 2, 2, 20,
- nil, 31, nil, 26, 45, 31, nil, 20, nil, nil,
- nil, nil, nil, 18, 18, 2, 56, nil, nil, nil,
- 57, nil, nil, nil, 19, nil, nil, nil, nil, nil,
- nil, nil, 18, nil, nil, 56, nil, nil, nil, nil,
- nil, nil, 23, 23, nil, nil, 18, nil, nil, 60,
- nil, 51, 51, nil, nil, 31, nil, nil, 31, nil,
- nil, 23, 76, nil, nil, 19, 26, 131, nil, nil,
- 45, nil, nil, nil, 19, 23, 2, nil, nil, 45,
- nil, 2, nil, nil, nil, 51, 3, nil, 20, 20,
- nil, nil, 51, nil, nil, 54, 14, nil, 18, nil,
- 14, nil, 59, nil, nil, 14, 20, 26, 82, 82,
- nil, 45, 131, nil, nil, nil, 26, 45, nil, nil,
- 54, nil, nil, 14, nil, nil, nil, nil, 82, 82,
- 51, 79, nil, 76, 15, 15, nil, nil, nil, 15,
- nil, nil, 39, nil, nil, nil, 45, nil, nil, nil,
- 18, nil, nil, 13, nil, nil, nil, nil, 11, nil,
- nil, 18, nil, 48, nil, 49, 53, 20, 4, 14,
- 56, 20, 20, nil, 18, nil, nil, nil, nil, 23,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 26, 18, nil, nil, nil, 18, nil, nil, 14, 14,
- 18, nil, 18, 23, nil, nil, 30, 30, 41, nil,
- nil, 18, 3, 41, nil, 18, 14, 3, 18, nil,
- nil, nil, 44, 17, nil, nil, nil, nil, 17, nil,
- nil, nil, nil, 53, 59, nil, nil, 57, 41, 41,
- 41, nil, nil, nil, 23, nil, 18, 18, nil, 14,
- nil, 18, nil, 17, 14, nil, 59, 82, nil, 41,
- nil, nil, 18, 82, 18, 30, 30, 52, nil, 41,
- 41, 41, 41, 15, 17, nil, 45, 13, nil, nil,
- nil, 14, nil, nil, nil, 18, nil, nil, nil, nil,
- nil, nil, nil, 18, 18, nil, nil, 2, nil, nil,
- nil, 2, nil, 56, nil, 41, 2, 14, 14, nil,
- nil, 18, nil, 41, nil, nil, 14, 60, nil, nil,
- 17, 3, nil, 51, nil, nil, nil, nil, 17, nil,
- nil, nil, nil, nil, 14, nil, nil, 54, 14, 18,
- nil, nil, nil, 51, 18, nil, 14, 14, nil, 18,
- nil, 14, nil, nil, 39, nil, nil, nil, 14, nil,
- nil, 82, nil, 51, nil, nil, nil, nil, 23, nil,
- 2, nil, nil, nil, nil, 59, 18, 13, 59, nil,
- nil, nil, nil, nil, nil, 18, nil, nil, 20, 59,
- 19, nil, 20, nil, 41, 41, nil, 20, nil, 2,
- 2, nil, 18, 18, 14, 30, 30, 52, 52, 17,
- 17, 18, 41, nil, nil, 20, nil, 2, nil, nil,
- nil, nil, 56, nil, 20, 3, nil, 17, nil, 18,
- 14, 14, 26, 18, nil, nil, 45, 14, 52, 18,
- nil, 18, 18, nil, nil, nil, 18, nil, 14, nil,
- nil, nil, nil, 18, nil, 14, nil, 59, nil, 20,
- 59, 20, 14, nil, nil, nil, nil, nil, 23, nil,
- nil, 14, 30, 41, 18, nil, nil, 41, 41, 52,
- nil, 30, 2, nil, nil, 14, nil, nil, 17, nil,
- 20, 20, nil, 17, nil, nil, 14, nil, nil, 18,
- nil, 59, nil, nil, nil, nil, 59, nil, 20, 59,
- nil, nil, nil, 30, nil, nil, nil, 2, nil, 30,
- nil, nil, nil, nil, nil, 18, 18, 18, nil, nil,
- 52, 52, 18, nil, nil, 2, nil, 52, 59, nil,
- nil, 20, nil, 18, nil, nil, 20, 2, 2, nil,
- 18, 59, 2, nil, 59, nil, nil, 18, nil, nil,
- nil, nil, nil, nil, nil, 59, 18, nil, nil, nil,
- nil, 52, nil, 20, nil, nil, nil, nil, nil, nil,
- 18, nil, nil, nil, nil, 52, nil, nil, nil, nil,
- nil, 18, 30, 30, nil, nil, 52, nil, nil, 20,
- 20, nil, nil, nil, nil, 2, nil, nil, 20, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 20, nil, nil, nil,
- 20, nil, nil, nil, nil, nil, nil, nil, 20, 20,
- nil, nil, nil, 20, nil, nil, nil, nil, nil, 2,
- 20, nil, nil, nil, nil, nil, 2, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 20, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 41, nil, 20, nil, 41, nil,
- nil, nil, nil, 41, nil, nil, nil, nil, nil, 17,
- nil, nil, nil, 17, nil, nil, nil, nil, 17, nil,
- nil, 41, 20, 20, nil, nil, nil, nil, nil, 20,
- nil, nil, nil, nil, nil, nil, 17, nil, nil, nil,
- 20, nil, nil, nil, nil, nil, nil, 20, nil, nil,
- nil, nil, nil, nil, 20, nil, nil, nil, nil, nil,
- nil, nil, nil, 20, nil, 41, nil, 41, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 20, nil, nil,
- nil, nil, 17, nil, nil, nil, nil, nil, 20, nil,
- nil, nil, nil, nil, nil, nil, 41, 41, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 17, 17, nil, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 17,
- nil, nil, nil, nil, nil, nil, nil, nil, 30, nil,
- nil, nil, nil, nil, nil, nil, nil, 41, nil, nil,
- nil, nil, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 17, nil, nil, nil, nil, 17, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 41,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 17, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 41, 41, nil, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, 25, nil, nil,
- nil, 25, 25, 25, nil, nil, nil, nil, nil, 17,
- nil, nil, 41, nil, nil, nil, 41, 25, 25, 25,
- nil, nil, nil, nil, 41, 41, nil, 17, nil, 41,
- nil, nil, 25, 25, nil, nil, 41, nil, nil, 17,
- 17, nil, nil, nil, 17, nil, nil, nil, nil, 22,
- 22, 17, nil, nil, nil, nil, nil, 41, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 17, nil, nil,
- nil, nil, 22, nil, 22, nil, nil, nil, 41, 41,
- nil, nil, nil, nil, nil, 41, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 41, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, nil, nil, nil, nil,
- 41, 17, nil, nil, nil, nil, nil, nil, 17, 41,
- nil, nil, nil, nil, nil, 17, nil, nil, nil, nil,
- nil, nil, nil, 41, nil, nil, nil, 25, 25, 25,
- 25, nil, nil, 25, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 25, 25, 25, 25,
- 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
- 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
- 25, 25, 25, nil, nil, nil, nil, nil, nil, nil,
- 25, nil, 25, nil, nil, nil, nil, nil, nil, nil,
- 22, nil, nil, 25, 25, nil, nil, nil, nil, nil,
- nil, nil, 25, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 25,
- nil, nil, nil, nil, nil, nil, nil, nil, 25, nil,
- nil, nil, nil, nil, nil, nil, nil, 22, nil, 22,
- nil, nil, 22, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 22, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 22, nil, nil, nil, nil, 22, nil, nil,
- 22, nil, nil, 22, nil, nil, nil, nil, 25, nil,
- nil, nil, nil, nil, nil, nil, nil, 22, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 25, nil, 25, 25, 25, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 22,
- nil, nil, nil, nil, nil, 25, nil, 25, nil, nil,
- 25, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 22, nil, nil, 25, 22, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 25, nil, nil, nil, nil,
- nil, 22, 25, 25, 25, 22, nil, 35, nil, nil,
- 25, nil, 35, nil, nil, 25, nil, nil, 25, nil,
- nil, 25, nil, nil, 35, 35, nil, nil, nil, 35,
- 35, 35, nil, nil, nil, 25, 25, 35, nil, nil,
- nil, nil, nil, nil, nil, 25, nil, nil, nil, 22,
- nil, nil, nil, nil, nil, nil, nil, nil, 35, 35,
- 35, nil, nil, 35, 35, nil, nil, 35, nil, nil,
- nil, nil, nil, nil, nil, nil, 25, 25, nil, nil,
- 25, 25, nil, nil, nil, 25, nil, nil, 25, nil,
- nil, nil, nil, nil, nil, 25, nil, nil, nil, 25,
- 22, nil, nil, 25, 35, nil, nil, nil, 35, 35,
- 35, 35, 35, nil, nil, nil, nil, nil, nil, 25,
- nil, nil, nil, 25, nil, 22, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 22, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 25, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 22, nil, nil, nil, nil, nil, 25, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 22, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 35, 35, 35, 35, 35, 35, nil,
- nil, 35, 35, 35, nil, nil, nil, 22, nil, 22,
- nil, 35, nil, nil, nil, nil, nil, nil, 25, 25,
- nil, nil, nil, 22, nil, nil, nil, nil, nil, 25,
- 25, 25, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 25, nil, nil, nil, nil, nil, nil,
- nil, nil, 25, 22, 25, nil, 22, nil, nil, nil,
- nil, 35, 35, nil, nil, nil, nil, nil, nil, nil,
- 35, nil, 35, nil, nil, nil, nil, 35, nil, 25,
- nil, nil, nil, nil, nil, 22, nil, nil, 22, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 25, nil,
- nil, 22, nil, nil, nil, nil, nil, 25, nil, nil,
- 22, nil, nil, nil, nil, 35, 35, nil, nil, 25,
- nil, nil, nil, nil, nil, 25, nil, 25, nil, nil,
- nil, nil, nil, nil, 35, nil, nil, nil, nil, nil,
- nil, 25, nil, nil, nil, nil, nil, nil, 35, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 25, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 25, nil, nil, 25, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 35, nil, nil, 25, nil, nil, 25, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 25,
- nil, nil, nil, nil, nil, nil, nil, nil, 25, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 35, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 35, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 35, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 35, nil, nil, nil, 35, nil, nil,
- nil, nil, 35, nil, 35, nil, nil, nil, nil, nil,
- nil, nil, nil, 35, nil, nil, nil, 35, nil, nil,
- 35, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 35, 35,
- nil, nil, nil, 35, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 35, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 35, nil, nil,
- nil, nil, nil, nil, nil, 35, 35, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 35, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 35, nil, nil, nil, nil, 35, nil, nil, nil,
- nil, 35, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 35, nil,
- nil, nil, nil, nil, nil, nil, nil, 35, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 35, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 35, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 35, nil, 35, 35, nil, nil, nil, 35, nil,
- nil, nil, nil, nil, nil, 35, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 35, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 35,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 35, nil, nil, nil, nil,
- nil, nil, 35, nil, nil, nil, nil, nil, nil, 35 ]
-
-racc_goto_pointer = [
- nil, 80, 201, -37, -279, -428, -521, nil, 46, 69,
- 91, 14, 89, 33, 0, -17, 37, 613, 95, -42,
- 292, -144, 1171, 124, -8, 1289, 0, nil, 41, -235,
- 404, 13, -218, -355, -334, 1757, 41, 43, nil, -25,
- 24, 598, -264, 25, -52, 2, nil, nil, 19, 21,
- -258, 2, 100, -247, 14, -222, -20, 28, -375, 63,
- -6, -341, -148, nil, -337, -478, nil, nil, nil, nil,
- nil, nil, 46, 57, 59, nil, -275, -569, -444, -282,
- 54, -210, 7, -499, 56, -218, -172, 65, -494, 69,
- -493, -371, -671, -370, -503, -189, -196, -372, -618, -407,
- -404, nil, -80, nil, -99, -99, -329, -635, -302, -438,
- nil, nil, 105, 106, 3, 103, -164, -284, 106, -337,
- -335, -455, nil, -560, -683, -563, -481, -559, -703, nil,
- nil, -378 ]
-
-racc_goto_default = [
- nil, nil, 292, nil, nil, 733, nil, 3, nil, 4,
- 312, nil, nil, nil, 202, 16, 11, 203, 286, nil,
- 201, nil, 244, 15, nil, 19, 20, 21, nil, 25,
- 596, nil, nil, nil, nil, 277, 29, nil, 31, 34,
- 33, 199, 323, nil, 114, 380, 113, 116, 68, 69,
- nil, nil, 42, 295, 297, nil, 298, 544, 545, 425,
- 562, nil, nil, 255, nil, nil, 43, 44, 45, 46,
- 47, 48, 49, nil, 256, 55, nil, nil, nil, nil,
- nil, nil, 487, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 235, nil, 384, nil, nil, nil, nil, nil, nil,
- 67, 70, 71, nil, nil, nil, nil, 525, nil, nil,
- nil, 646, 647, 648, 649, nil, 812, 656, 657, 660,
- 663, 248 ]
-
-racc_reduce_table = [
- 0, 0, :racc_error,
- 1, 133, :_reduce_1,
- 4, 135, :_reduce_2,
- 2, 134, :_reduce_3,
- 0, 139, :_reduce_4,
- 1, 139, :_reduce_5,
- 2, 139, :_reduce_6,
- 3, 139, :_reduce_7,
- 0, 156, :_reduce_8,
- 4, 141, :_reduce_9,
- 3, 141, :_reduce_10,
- 3, 141, :_reduce_11,
- 3, 141, :_reduce_12,
- 2, 141, :_reduce_13,
- 3, 141, :_reduce_14,
- 3, 141, :_reduce_15,
- 3, 141, :_reduce_16,
- 3, 141, :_reduce_17,
- 3, 141, :_reduce_18,
- 4, 141, :_reduce_19,
- 4, 141, :_reduce_20,
- 3, 141, :_reduce_21,
- 3, 141, :_reduce_22,
- 3, 141, :_reduce_23,
- 6, 141, :_reduce_24,
- 5, 141, :_reduce_25,
- 5, 141, :_reduce_26,
- 5, 141, :_reduce_27,
- 3, 141, :_reduce_28,
- 3, 141, :_reduce_29,
- 3, 141, :_reduce_30,
- 3, 141, :_reduce_31,
- 1, 141, :_reduce_none,
- 1, 155, :_reduce_none,
- 3, 155, :_reduce_34,
- 3, 155, :_reduce_35,
- 2, 155, :_reduce_36,
- 2, 155, :_reduce_37,
- 1, 155, :_reduce_none,
- 1, 145, :_reduce_none,
- 1, 147, :_reduce_none,
- 1, 147, :_reduce_none,
- 2, 147, :_reduce_42,
- 2, 147, :_reduce_43,
- 2, 147, :_reduce_44,
- 1, 159, :_reduce_none,
- 4, 159, :_reduce_46,
- 4, 159, :_reduce_47,
- 0, 166, :_reduce_48,
- 5, 164, :_reduce_49,
- 2, 158, :_reduce_50,
- 3, 158, :_reduce_51,
- 4, 158, :_reduce_52,
- 5, 158, :_reduce_53,
- 4, 158, :_reduce_54,
- 5, 158, :_reduce_55,
- 2, 158, :_reduce_56,
- 2, 158, :_reduce_57,
- 1, 148, :_reduce_58,
- 3, 148, :_reduce_59,
- 1, 169, :_reduce_60,
- 3, 169, :_reduce_61,
- 1, 168, :_reduce_62,
- 2, 168, :_reduce_63,
- 3, 168, :_reduce_64,
- 2, 168, :_reduce_65,
- 2, 168, :_reduce_66,
- 1, 168, :_reduce_67,
- 1, 171, :_reduce_none,
- 3, 171, :_reduce_69,
- 2, 170, :_reduce_70,
- 3, 170, :_reduce_71,
- 1, 172, :_reduce_72,
- 4, 172, :_reduce_73,
- 3, 172, :_reduce_74,
- 3, 172, :_reduce_75,
- 3, 172, :_reduce_76,
- 3, 172, :_reduce_77,
- 2, 172, :_reduce_78,
- 1, 172, :_reduce_79,
- 1, 146, :_reduce_80,
- 4, 146, :_reduce_81,
- 3, 146, :_reduce_82,
- 3, 146, :_reduce_83,
- 3, 146, :_reduce_84,
- 3, 146, :_reduce_85,
- 2, 146, :_reduce_86,
- 1, 146, :_reduce_87,
- 1, 174, :_reduce_88,
- 1, 174, :_reduce_none,
- 2, 175, :_reduce_90,
- 1, 175, :_reduce_91,
- 3, 175, :_reduce_92,
- 1, 176, :_reduce_none,
- 1, 176, :_reduce_none,
- 1, 176, :_reduce_none,
- 1, 176, :_reduce_none,
- 1, 176, :_reduce_none,
- 1, 179, :_reduce_98,
- 1, 179, :_reduce_none,
- 1, 143, :_reduce_none,
- 1, 143, :_reduce_none,
- 1, 144, :_reduce_102,
- 0, 182, :_reduce_103,
- 4, 144, :_reduce_104,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 177, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 1, 178, :_reduce_none,
- 3, 157, :_reduce_171,
- 5, 157, :_reduce_172,
- 3, 157, :_reduce_173,
- 6, 157, :_reduce_174,
- 5, 157, :_reduce_175,
- 5, 157, :_reduce_176,
- 5, 157, :_reduce_177,
- 5, 157, :_reduce_178,
- 4, 157, :_reduce_179,
- 3, 157, :_reduce_180,
- 3, 157, :_reduce_181,
- 3, 157, :_reduce_182,
- 3, 157, :_reduce_183,
- 3, 157, :_reduce_184,
- 3, 157, :_reduce_185,
- 3, 157, :_reduce_186,
- 3, 157, :_reduce_187,
- 3, 157, :_reduce_188,
- 4, 157, :_reduce_189,
- 4, 157, :_reduce_190,
- 2, 157, :_reduce_191,
- 2, 157, :_reduce_192,
- 3, 157, :_reduce_193,
- 3, 157, :_reduce_194,
- 3, 157, :_reduce_195,
- 3, 157, :_reduce_196,
- 3, 157, :_reduce_197,
- 3, 157, :_reduce_198,
- 3, 157, :_reduce_199,
- 3, 157, :_reduce_200,
- 3, 157, :_reduce_201,
- 3, 157, :_reduce_202,
- 3, 157, :_reduce_203,
- 3, 157, :_reduce_204,
- 3, 157, :_reduce_205,
- 2, 157, :_reduce_206,
- 2, 157, :_reduce_207,
- 3, 157, :_reduce_208,
- 3, 157, :_reduce_209,
- 3, 157, :_reduce_210,
- 3, 157, :_reduce_211,
- 3, 157, :_reduce_212,
- 5, 157, :_reduce_213,
- 1, 157, :_reduce_none,
- 1, 154, :_reduce_none,
- 1, 151, :_reduce_none,
- 2, 151, :_reduce_217,
- 2, 151, :_reduce_218,
- 5, 151, :_reduce_219,
- 2, 151, :_reduce_220,
- 3, 151, :_reduce_221,
- 3, 189, :_reduce_222,
- 4, 189, :_reduce_223,
- 4, 189, :_reduce_224,
- 6, 189, :_reduce_225,
- 0, 190, :_reduce_226,
- 1, 190, :_reduce_none,
- 1, 160, :_reduce_228,
- 2, 160, :_reduce_229,
- 5, 160, :_reduce_230,
- 2, 160, :_reduce_231,
- 5, 160, :_reduce_232,
- 4, 160, :_reduce_233,
- 7, 160, :_reduce_234,
- 3, 160, :_reduce_235,
- 1, 160, :_reduce_236,
- 4, 193, :_reduce_237,
- 3, 193, :_reduce_238,
- 5, 193, :_reduce_239,
- 7, 193, :_reduce_240,
- 2, 193, :_reduce_241,
- 5, 193, :_reduce_242,
- 4, 193, :_reduce_243,
- 6, 193, :_reduce_244,
- 7, 193, :_reduce_245,
- 9, 193, :_reduce_246,
- 3, 193, :_reduce_247,
- 1, 193, :_reduce_248,
- 0, 195, :_reduce_249,
- 2, 163, :_reduce_250,
- 1, 194, :_reduce_251,
- 0, 196, :_reduce_252,
- 3, 194, :_reduce_253,
- 0, 197, :_reduce_254,
- 4, 194, :_reduce_255,
- 2, 192, :_reduce_256,
- 2, 191, :_reduce_257,
- 0, 191, :_reduce_258,
- 1, 186, :_reduce_259,
- 3, 186, :_reduce_260,
- 3, 153, :_reduce_261,
- 4, 153, :_reduce_262,
- 2, 153, :_reduce_263,
- 1, 184, :_reduce_none,
- 1, 184, :_reduce_none,
- 1, 184, :_reduce_none,
- 1, 184, :_reduce_none,
- 1, 184, :_reduce_none,
- 1, 184, :_reduce_none,
- 1, 184, :_reduce_none,
- 1, 184, :_reduce_none,
- 1, 184, :_reduce_272,
- 3, 184, :_reduce_273,
- 0, 218, :_reduce_274,
- 5, 184, :_reduce_275,
- 3, 184, :_reduce_276,
- 3, 184, :_reduce_277,
- 2, 184, :_reduce_278,
- 4, 184, :_reduce_279,
- 3, 184, :_reduce_280,
- 3, 184, :_reduce_281,
- 1, 184, :_reduce_282,
- 4, 184, :_reduce_283,
- 3, 184, :_reduce_284,
- 1, 184, :_reduce_285,
- 5, 184, :_reduce_286,
- 2, 184, :_reduce_287,
- 1, 184, :_reduce_none,
- 2, 184, :_reduce_289,
- 6, 184, :_reduce_290,
- 6, 184, :_reduce_291,
- 0, 219, :_reduce_292,
- 0, 220, :_reduce_293,
- 7, 184, :_reduce_294,
- 0, 221, :_reduce_295,
- 0, 222, :_reduce_296,
- 7, 184, :_reduce_297,
- 5, 184, :_reduce_298,
- 4, 184, :_reduce_299,
- 5, 184, :_reduce_300,
- 0, 223, :_reduce_301,
- 0, 224, :_reduce_302,
- 9, 184, :_reduce_303,
- 0, 225, :_reduce_304,
- 6, 184, :_reduce_305,
- 0, 226, :_reduce_306,
- 7, 184, :_reduce_307,
- 0, 227, :_reduce_308,
- 5, 184, :_reduce_309,
- 0, 228, :_reduce_310,
- 6, 184, :_reduce_311,
- 0, 229, :_reduce_312,
- 0, 230, :_reduce_313,
- 9, 184, :_reduce_314,
- 1, 184, :_reduce_315,
- 1, 184, :_reduce_316,
- 1, 184, :_reduce_317,
- 1, 184, :_reduce_318,
- 1, 150, :_reduce_none,
- 1, 208, :_reduce_none,
- 1, 208, :_reduce_none,
- 1, 208, :_reduce_none,
- 2, 208, :_reduce_323,
- 1, 210, :_reduce_none,
- 1, 210, :_reduce_none,
- 1, 210, :_reduce_none,
- 1, 209, :_reduce_none,
- 5, 209, :_reduce_328,
- 1, 137, :_reduce_none,
- 2, 137, :_reduce_330,
- 1, 212, :_reduce_none,
- 1, 212, :_reduce_none,
- 1, 231, :_reduce_333,
- 3, 231, :_reduce_334,
- 1, 232, :_reduce_none,
- 2, 232, :_reduce_none,
- 4, 232, :_reduce_337,
- 7, 232, :_reduce_338,
- 6, 232, :_reduce_339,
- 4, 232, :_reduce_340,
- 3, 232, :_reduce_341,
- 5, 232, :_reduce_342,
- 4, 232, :_reduce_343,
- 2, 232, :_reduce_344,
- 1, 232, :_reduce_345,
- 2, 232, :_reduce_346,
- 0, 165, :_reduce_347,
- 2, 165, :_reduce_348,
- 1, 165, :_reduce_349,
- 3, 165, :_reduce_350,
- 0, 234, :_reduce_351,
- 5, 233, :_reduce_352,
- 2, 161, :_reduce_353,
- 4, 161, :_reduce_354,
- 4, 161, :_reduce_355,
- 2, 207, :_reduce_356,
- 4, 207, :_reduce_357,
- 4, 207, :_reduce_358,
- 3, 207, :_reduce_359,
- 2, 207, :_reduce_360,
- 1, 207, :_reduce_361,
- 0, 236, :_reduce_362,
- 5, 206, :_reduce_363,
- 0, 237, :_reduce_364,
- 5, 206, :_reduce_365,
- 5, 211, :_reduce_366,
- 1, 238, :_reduce_none,
- 4, 238, :_reduce_368,
- 2, 238, :_reduce_369,
- 1, 239, :_reduce_370,
- 1, 239, :_reduce_none,
- 6, 136, :_reduce_372,
- 0, 136, :_reduce_373,
- 1, 240, :_reduce_374,
- 1, 240, :_reduce_none,
- 1, 240, :_reduce_none,
- 2, 241, :_reduce_377,
- 1, 241, :_reduce_none,
- 2, 138, :_reduce_379,
- 1, 138, :_reduce_none,
- 1, 198, :_reduce_none,
- 1, 198, :_reduce_none,
- 1, 198, :_reduce_none,
- 1, 199, :_reduce_384,
- 1, 243, :_reduce_385,
- 2, 243, :_reduce_386,
- 3, 244, :_reduce_387,
- 1, 244, :_reduce_388,
- 3, 200, :_reduce_389,
- 4, 201, :_reduce_390,
- 3, 202, :_reduce_391,
- 0, 247, :_reduce_392,
- 3, 247, :_reduce_393,
- 1, 248, :_reduce_394,
- 2, 248, :_reduce_395,
- 3, 203, :_reduce_396,
- 0, 250, :_reduce_397,
- 3, 250, :_reduce_398,
- 0, 245, :_reduce_399,
- 2, 245, :_reduce_400,
- 0, 246, :_reduce_401,
- 2, 246, :_reduce_402,
- 1, 249, :_reduce_403,
- 2, 249, :_reduce_404,
- 0, 252, :_reduce_405,
- 4, 249, :_reduce_406,
- 1, 251, :_reduce_407,
- 1, 251, :_reduce_408,
- 1, 251, :_reduce_409,
- 1, 251, :_reduce_none,
- 1, 180, :_reduce_411,
- 3, 181, :_reduce_412,
- 1, 242, :_reduce_413,
- 1, 242, :_reduce_414,
- 2, 242, :_reduce_415,
- 2, 242, :_reduce_416,
- 1, 173, :_reduce_417,
- 1, 173, :_reduce_418,
- 1, 173, :_reduce_419,
- 1, 173, :_reduce_420,
- 1, 173, :_reduce_421,
- 1, 173, :_reduce_422,
- 1, 173, :_reduce_423,
- 1, 173, :_reduce_424,
- 1, 173, :_reduce_425,
- 1, 173, :_reduce_426,
- 1, 173, :_reduce_427,
- 1, 204, :_reduce_428,
- 1, 149, :_reduce_429,
- 1, 152, :_reduce_430,
- 1, 152, :_reduce_431,
- 1, 213, :_reduce_432,
- 3, 213, :_reduce_433,
- 2, 213, :_reduce_434,
- 4, 215, :_reduce_435,
- 2, 215, :_reduce_436,
- 6, 253, :_reduce_437,
- 4, 253, :_reduce_438,
- 4, 253, :_reduce_439,
- 2, 253, :_reduce_440,
- 4, 253, :_reduce_441,
- 2, 253, :_reduce_442,
- 2, 253, :_reduce_443,
- 1, 253, :_reduce_444,
- 0, 253, :_reduce_445,
- 1, 259, :_reduce_446,
- 1, 259, :_reduce_447,
- 1, 259, :_reduce_448,
- 1, 259, :_reduce_449,
- 1, 259, :_reduce_450,
- 1, 254, :_reduce_451,
- 3, 254, :_reduce_452,
- 3, 260, :_reduce_453,
- 1, 255, :_reduce_454,
- 3, 255, :_reduce_455,
- 1, 261, :_reduce_none,
- 1, 261, :_reduce_none,
- 2, 256, :_reduce_458,
- 1, 256, :_reduce_459,
- 1, 262, :_reduce_none,
- 1, 262, :_reduce_none,
- 2, 258, :_reduce_462,
- 2, 257, :_reduce_463,
- 0, 257, :_reduce_464,
- 1, 216, :_reduce_none,
- 4, 216, :_reduce_466,
- 0, 205, :_reduce_467,
- 2, 205, :_reduce_468,
- 2, 205, :_reduce_469,
- 1, 188, :_reduce_470,
- 3, 188, :_reduce_471,
- 3, 263, :_reduce_472,
- 1, 167, :_reduce_none,
- 1, 167, :_reduce_none,
- 1, 167, :_reduce_none,
- 1, 162, :_reduce_none,
- 1, 162, :_reduce_none,
- 1, 162, :_reduce_none,
- 1, 162, :_reduce_none,
- 1, 235, :_reduce_none,
- 1, 235, :_reduce_none,
- 1, 235, :_reduce_none,
- 1, 217, :_reduce_none,
- 1, 217, :_reduce_none,
- 0, 140, :_reduce_none,
- 1, 140, :_reduce_none,
- 0, 183, :_reduce_none,
- 1, 183, :_reduce_none,
- 0, 187, :_reduce_none,
- 1, 187, :_reduce_none,
- 1, 187, :_reduce_none,
- 1, 214, :_reduce_492,
- 1, 214, :_reduce_none,
- 1, 142, :_reduce_none,
- 2, 142, :_reduce_none,
- 0, 185, :_reduce_496 ]
-
-racc_reduce_n = 497
-
-racc_shift_n = 894
-
-racc_token_table = {
- false => 0,
- :error => 1,
- :kCLASS => 2,
- :kMODULE => 3,
- :kDEF => 4,
- :kUNDEF => 5,
- :kBEGIN => 6,
- :kRESCUE => 7,
- :kENSURE => 8,
- :kEND => 9,
- :kIF => 10,
- :kUNLESS => 11,
- :kTHEN => 12,
- :kELSIF => 13,
- :kELSE => 14,
- :kCASE => 15,
- :kWHEN => 16,
- :kWHILE => 17,
- :kUNTIL => 18,
- :kFOR => 19,
- :kBREAK => 20,
- :kNEXT => 21,
- :kREDO => 22,
- :kRETRY => 23,
- :kIN => 24,
- :kDO => 25,
- :kDO_COND => 26,
- :kDO_BLOCK => 27,
- :kRETURN => 28,
- :kYIELD => 29,
- :kSUPER => 30,
- :kSELF => 31,
- :kNIL => 32,
- :kTRUE => 33,
- :kFALSE => 34,
- :kAND => 35,
- :kOR => 36,
- :kNOT => 37,
- :kIF_MOD => 38,
- :kUNLESS_MOD => 39,
- :kWHILE_MOD => 40,
- :kUNTIL_MOD => 41,
- :kRESCUE_MOD => 42,
- :kALIAS => 43,
- :kDEFINED => 44,
- :klBEGIN => 45,
- :klEND => 46,
- :k__LINE__ => 47,
- :k__FILE__ => 48,
- :tIDENTIFIER => 49,
- :tFID => 50,
- :tGVAR => 51,
- :tIVAR => 52,
- :tCONSTANT => 53,
- :tCVAR => 54,
- :tNTH_REF => 55,
- :tBACK_REF => 56,
- :tSTRING_CONTENT => 57,
- :tINTEGER => 58,
- :tFLOAT => 59,
- :tREGEXP_END => 60,
- :tUPLUS => 61,
- :tUMINUS => 62,
- :tUMINUS_NUM => 63,
- :tPOW => 64,
- :tCMP => 65,
- :tEQ => 66,
- :tEQQ => 67,
- :tNEQ => 68,
- :tGEQ => 69,
- :tLEQ => 70,
- :tANDOP => 71,
- :tOROP => 72,
- :tMATCH => 73,
- :tNMATCH => 74,
- :tDOT => 75,
- :tDOT2 => 76,
- :tDOT3 => 77,
- :tAREF => 78,
- :tASET => 79,
- :tLSHFT => 80,
- :tRSHFT => 81,
- :tCOLON2 => 82,
- :tCOLON3 => 83,
- :tOP_ASGN => 84,
- :tASSOC => 85,
- :tLPAREN => 86,
- :tLPAREN2 => 87,
- :tRPAREN => 88,
- :tLPAREN_ARG => 89,
- :tLBRACK => 90,
- :tLBRACK2 => 91,
- :tRBRACK => 92,
- :tLBRACE => 93,
- :tLBRACE_ARG => 94,
- :tSTAR => 95,
- :tSTAR2 => 96,
- :tAMPER => 97,
- :tAMPER2 => 98,
- :tTILDE => 99,
- :tPERCENT => 100,
- :tDIVIDE => 101,
- :tPLUS => 102,
- :tMINUS => 103,
- :tLT => 104,
- :tGT => 105,
- :tPIPE => 106,
- :tBANG => 107,
- :tCARET => 108,
- :tLCURLY => 109,
- :tRCURLY => 110,
- :tBACK_REF2 => 111,
- :tSYMBEG => 112,
- :tSTRING_BEG => 113,
- :tXSTRING_BEG => 114,
- :tREGEXP_BEG => 115,
- :tWORDS_BEG => 116,
- :tQWORDS_BEG => 117,
- :tSTRING_DBEG => 118,
- :tSTRING_DVAR => 119,
- :tSTRING_END => 120,
- :tSTRING => 121,
- :tSYMBOL => 122,
- :tREGEXP_OPT => 123,
- :tNL => 124,
- :tEH => 125,
- :tCOLON => 126,
- :tCOMMA => 127,
- :tSPACE => 128,
- :tSEMI => 129,
- :tEQL => 130,
- :tLOWEST => 131 }
-
-racc_nt_base = 132
-
-racc_use_result_var = true
-
-Racc_arg = [
- racc_action_table,
- racc_action_check,
- racc_action_default,
- racc_action_pointer,
- racc_goto_table,
- racc_goto_check,
- racc_goto_default,
- racc_goto_pointer,
- racc_nt_base,
- racc_reduce_table,
- racc_token_table,
- racc_shift_n,
- racc_reduce_n,
- racc_use_result_var ]
-Ractor.make_shareable(Racc_arg) if defined?(Ractor)
-
-Racc_token_to_s_table = [
- "$end",
- "error",
- "kCLASS",
- "kMODULE",
- "kDEF",
- "kUNDEF",
- "kBEGIN",
- "kRESCUE",
- "kENSURE",
- "kEND",
- "kIF",
- "kUNLESS",
- "kTHEN",
- "kELSIF",
- "kELSE",
- "kCASE",
- "kWHEN",
- "kWHILE",
- "kUNTIL",
- "kFOR",
- "kBREAK",
- "kNEXT",
- "kREDO",
- "kRETRY",
- "kIN",
- "kDO",
- "kDO_COND",
- "kDO_BLOCK",
- "kRETURN",
- "kYIELD",
- "kSUPER",
- "kSELF",
- "kNIL",
- "kTRUE",
- "kFALSE",
- "kAND",
- "kOR",
- "kNOT",
- "kIF_MOD",
- "kUNLESS_MOD",
- "kWHILE_MOD",
- "kUNTIL_MOD",
- "kRESCUE_MOD",
- "kALIAS",
- "kDEFINED",
- "klBEGIN",
- "klEND",
- "k__LINE__",
- "k__FILE__",
- "tIDENTIFIER",
- "tFID",
- "tGVAR",
- "tIVAR",
- "tCONSTANT",
- "tCVAR",
- "tNTH_REF",
- "tBACK_REF",
- "tSTRING_CONTENT",
- "tINTEGER",
- "tFLOAT",
- "tREGEXP_END",
- "tUPLUS",
- "tUMINUS",
- "tUMINUS_NUM",
- "tPOW",
- "tCMP",
- "tEQ",
- "tEQQ",
- "tNEQ",
- "tGEQ",
- "tLEQ",
- "tANDOP",
- "tOROP",
- "tMATCH",
- "tNMATCH",
- "tDOT",
- "tDOT2",
- "tDOT3",
- "tAREF",
- "tASET",
- "tLSHFT",
- "tRSHFT",
- "tCOLON2",
- "tCOLON3",
- "tOP_ASGN",
- "tASSOC",
- "tLPAREN",
- "tLPAREN2",
- "tRPAREN",
- "tLPAREN_ARG",
- "tLBRACK",
- "tLBRACK2",
- "tRBRACK",
- "tLBRACE",
- "tLBRACE_ARG",
- "tSTAR",
- "tSTAR2",
- "tAMPER",
- "tAMPER2",
- "tTILDE",
- "tPERCENT",
- "tDIVIDE",
- "tPLUS",
- "tMINUS",
- "tLT",
- "tGT",
- "tPIPE",
- "tBANG",
- "tCARET",
- "tLCURLY",
- "tRCURLY",
- "tBACK_REF2",
- "tSYMBEG",
- "tSTRING_BEG",
- "tXSTRING_BEG",
- "tREGEXP_BEG",
- "tWORDS_BEG",
- "tQWORDS_BEG",
- "tSTRING_DBEG",
- "tSTRING_DVAR",
- "tSTRING_END",
- "tSTRING",
- "tSYMBOL",
- "tREGEXP_OPT",
- "tNL",
- "tEH",
- "tCOLON",
- "tCOMMA",
- "tSPACE",
- "tSEMI",
- "tEQL",
- "tLOWEST",
- "$start",
- "program",
- "compstmt",
- "bodystmt",
- "opt_rescue",
- "opt_else",
- "opt_ensure",
- "stmts",
- "opt_terms",
- "stmt",
- "terms",
- "fitem",
- "undef_list",
- "expr_value",
- "lhs",
- "command_call",
- "mlhs",
- "var_lhs",
- "primary_value",
- "aref_args",
- "backref",
- "mrhs",
- "arg_value",
- "expr",
- "@1",
- "arg",
- "command",
- "block_command",
- "call_args",
- "block_call",
- "operation2",
- "command_args",
- "cmd_brace_block",
- "opt_block_var",
- "@2",
- "operation",
- "mlhs_basic",
- "mlhs_entry",
- "mlhs_head",
- "mlhs_item",
- "mlhs_node",
- "variable",
- "cname",
- "cpath",
- "fname",
- "op",
- "reswords",
- "fsym",
- "symbol",
- "dsym",
- "@3",
- "opt_nl",
- "primary",
- "none",
- "args",
- "trailer",
- "assocs",
- "paren_args",
- "opt_paren_args",
- "opt_block_arg",
- "block_arg",
- "call_args2",
- "open_args",
- "@4",
- "@5",
- "@6",
- "literal",
- "strings",
- "xstring",
- "regexp",
- "words",
- "qwords",
- "var_ref",
- "assoc_list",
- "brace_block",
- "method_call",
- "then",
- "if_tail",
- "do",
- "case_body",
- "for_var",
- "superclass",
- "term",
- "f_arglist",
- "singleton",
- "dot_or_colon",
- "@7",
- "@8",
- "@9",
- "@10",
- "@11",
- "@12",
- "@13",
- "@14",
- "@15",
- "@16",
- "@17",
- "@18",
- "@19",
- "block_par",
- "block_var",
- "do_block",
- "@20",
- "operation3",
- "@21",
- "@22",
- "when_args",
- "cases",
- "exc_list",
- "exc_var",
- "numeric",
- "string",
- "string1",
- "string_contents",
- "xstring_contents",
- "word_list",
- "word",
- "string_content",
- "qword_list",
- "string_dvar",
- "@23",
- "f_args",
- "f_arg",
- "f_optarg",
- "f_rest_arg",
- "opt_f_block_arg",
- "f_block_arg",
- "f_norm_arg",
- "f_opt",
- "restarg_mark",
- "blkarg_mark",
- "assoc" ]
-Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)
-
-Racc_debug_parser = false
-
-##### State transition tables end #####
-
-# reduce 0 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 73)
- def _reduce_1(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 78)
- def _reduce_2(val, _values, result)
- rescue_bodies = val[1]
- else_t, else_ = val[2]
- ensure_t, ensure_ = val[3]
-
- if rescue_bodies.empty? && !else_.nil?
- diagnostic :warning, :useless_else, nil, else_t
- end
-
- result = @builder.begin_body(val[0],
- rescue_bodies,
- else_t, else_,
- ensure_t, ensure_)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 94)
- def _reduce_3(val, _values, result)
- result = @builder.compstmt(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 99)
- def _reduce_4(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 103)
- def _reduce_5(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 107)
- def _reduce_6(val, _values, result)
- result = [ val[1] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 111)
- def _reduce_7(val, _values, result)
- result = val[0] << val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 116)
- def _reduce_8(val, _values, result)
- @lexer.state = :expr_fname
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 120)
- def _reduce_9(val, _values, result)
- result = @builder.alias(val[0], val[1], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 124)
- def _reduce_10(val, _values, result)
- result = @builder.alias(val[0],
- @builder.gvar(val[1]),
- @builder.gvar(val[2]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 130)
- def _reduce_11(val, _values, result)
- result = @builder.alias(val[0],
- @builder.gvar(val[1]),
- @builder.back_ref(val[2]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 136)
- def _reduce_12(val, _values, result)
- diagnostic :error, :nth_ref_alias, nil, val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 140)
- def _reduce_13(val, _values, result)
- result = @builder.undef_method(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 144)
- def _reduce_14(val, _values, result)
- result = @builder.condition_mod(val[0], nil,
- val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 149)
- def _reduce_15(val, _values, result)
- result = @builder.condition_mod(nil, val[0],
- val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 154)
- def _reduce_16(val, _values, result)
- result = @builder.loop_mod(:while, val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 158)
- def _reduce_17(val, _values, result)
- result = @builder.loop_mod(:until, val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 162)
- def _reduce_18(val, _values, result)
- rescue_body = @builder.rescue_body(val[1],
- nil, nil, nil,
- nil, val[2])
-
- result = @builder.begin_body(val[0], [ rescue_body ])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 170)
- def _reduce_19(val, _values, result)
- if in_def?
- diagnostic :error, :begin_in_method, nil, val[0]
- end
-
- result = @builder.preexe(val[0], val[1], val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 178)
- def _reduce_20(val, _values, result)
- result = @builder.postexe(val[0], val[1], val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 182)
- def _reduce_21(val, _values, result)
- result = @builder.assign(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 186)
- def _reduce_22(val, _values, result)
- result = @builder.multi_assign(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 190)
- def _reduce_23(val, _values, result)
- result = @builder.op_assign(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 194)
- def _reduce_24(val, _values, result)
- result = @builder.op_assign(
- @builder.index(
- val[0], val[1], val[2], val[3]),
- val[4], val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 201)
- def _reduce_25(val, _values, result)
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 208)
- def _reduce_26(val, _values, result)
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 215)
- def _reduce_27(val, _values, result)
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 222)
- def _reduce_28(val, _values, result)
- @builder.op_assign(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 226)
- def _reduce_29(val, _values, result)
- result = @builder.assign(val[0], val[1],
- @builder.array(nil, val[2], nil))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 231)
- def _reduce_30(val, _values, result)
- result = @builder.multi_assign(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 235)
- def _reduce_31(val, _values, result)
- result = @builder.multi_assign(val[0], val[1],
- @builder.array(nil, val[2], nil))
-
- result
- end
-.,.,
-
-# reduce 32 omitted
-
-# reduce 33 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 243)
- def _reduce_34(val, _values, result)
- result = @builder.logical_op(:and, val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 247)
- def _reduce_35(val, _values, result)
- result = @builder.logical_op(:or, val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 251)
- def _reduce_36(val, _values, result)
- result = @builder.not_op(val[0], nil, val[1], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 255)
- def _reduce_37(val, _values, result)
- result = @builder.not_op(val[0], nil, val[1], nil)
-
- result
- end
-.,.,
-
-# reduce 38 omitted
-
-# reduce 39 omitted
-
-# reduce 40 omitted
-
-# reduce 41 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 265)
- def _reduce_42(val, _values, result)
- result = @builder.keyword_cmd(:return, val[0],
- nil, val[1], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 270)
- def _reduce_43(val, _values, result)
- result = @builder.keyword_cmd(:break, val[0],
- nil, val[1], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 275)
- def _reduce_44(val, _values, result)
- result = @builder.keyword_cmd(:next, val[0],
- nil, val[1], nil)
-
- result
- end
-.,.,
-
-# reduce 45 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 282)
- def _reduce_46(val, _values, result)
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 288)
- def _reduce_47(val, _values, result)
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 295)
- def _reduce_48(val, _values, result)
- @static_env.extend_dynamic
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 299)
- def _reduce_49(val, _values, result)
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 306)
- def _reduce_50(val, _values, result)
- lparen_t, args, rparen_t = val[1]
- result = @builder.call_method(nil, nil, val[0],
- lparen_t, args, rparen_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 312)
- def _reduce_51(val, _values, result)
- lparen_t, args, rparen_t = val[1]
- method_call = @builder.call_method(nil, nil, val[0],
- lparen_t, args, rparen_t)
-
- begin_t, block_args, body, end_t = val[2]
- result = @builder.block(method_call,
- begin_t, block_args, body, end_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 322)
- def _reduce_52(val, _values, result)
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
-
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 329)
- def _reduce_53(val, _values, result)
- lparen_t, args, rparen_t = val[3]
- method_call = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
-
- begin_t, block_args, body, end_t = val[4]
- result = @builder.block(method_call,
- begin_t, block_args, body, end_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 339)
- def _reduce_54(val, _values, result)
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 345)
- def _reduce_55(val, _values, result)
- lparen_t, args, rparen_t = val[3]
- method_call = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
-
- begin_t, block_args, body, end_t = val[4]
- result = @builder.block(method_call,
- begin_t, block_args, body, end_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 355)
- def _reduce_56(val, _values, result)
- lparen_t, args, rparen_t = val[1]
- result = @builder.keyword_cmd(:super, val[0],
- lparen_t, args, rparen_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 361)
- def _reduce_57(val, _values, result)
- lparen_t, args, rparen_t = val[1]
- result = @builder.keyword_cmd(:yield, val[0],
- lparen_t, args, rparen_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 368)
- def _reduce_58(val, _values, result)
- result = @builder.multi_lhs(nil, val[0], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 372)
- def _reduce_59(val, _values, result)
- result = @builder.begin(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 377)
- def _reduce_60(val, _values, result)
- result = @builder.multi_lhs(nil, val[0], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 381)
- def _reduce_61(val, _values, result)
- result = @builder.multi_lhs(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 386)
- def _reduce_62(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 390)
- def _reduce_63(val, _values, result)
- result = val[0] << val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 394)
- def _reduce_64(val, _values, result)
- result = val[0] << @builder.splat(val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 398)
- def _reduce_65(val, _values, result)
- result = val[0] << @builder.splat(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 402)
- def _reduce_66(val, _values, result)
- result = [ @builder.splat(val[0], val[1]) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 406)
- def _reduce_67(val, _values, result)
- result = [ @builder.splat(val[0]) ]
-
- result
- end
-.,.,
-
-# reduce 68 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 412)
- def _reduce_69(val, _values, result)
- result = @builder.begin(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 417)
- def _reduce_70(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 421)
- def _reduce_71(val, _values, result)
- result = val[0] << val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 426)
- def _reduce_72(val, _values, result)
- result = @builder.assignable(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 430)
- def _reduce_73(val, _values, result)
- result = @builder.index_asgn(val[0], val[1], val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 434)
- def _reduce_74(val, _values, result)
- result = @builder.attr_asgn(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 438)
- def _reduce_75(val, _values, result)
- result = @builder.attr_asgn(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 442)
- def _reduce_76(val, _values, result)
- result = @builder.attr_asgn(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 446)
- def _reduce_77(val, _values, result)
- result = @builder.assignable(
- @builder.const_fetch(val[0], val[1], val[2]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 451)
- def _reduce_78(val, _values, result)
- result = @builder.assignable(
- @builder.const_global(val[0], val[1]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 456)
- def _reduce_79(val, _values, result)
- result = @builder.assignable(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 461)
- def _reduce_80(val, _values, result)
- result = @builder.assignable(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 465)
- def _reduce_81(val, _values, result)
- result = @builder.index_asgn(val[0], val[1], val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 469)
- def _reduce_82(val, _values, result)
- result = @builder.attr_asgn(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 473)
- def _reduce_83(val, _values, result)
- result = @builder.attr_asgn(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 477)
- def _reduce_84(val, _values, result)
- result = @builder.attr_asgn(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 481)
- def _reduce_85(val, _values, result)
- result = @builder.assignable(
- @builder.const_fetch(val[0], val[1], val[2]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 486)
- def _reduce_86(val, _values, result)
- result = @builder.assignable(
- @builder.const_global(val[0], val[1]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 491)
- def _reduce_87(val, _values, result)
- result = @builder.assignable(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 496)
- def _reduce_88(val, _values, result)
- diagnostic :error, :module_name_const, nil, val[0]
-
- result
- end
-.,.,
-
-# reduce 89 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 502)
- def _reduce_90(val, _values, result)
- result = @builder.const_global(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 506)
- def _reduce_91(val, _values, result)
- result = @builder.const(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 510)
- def _reduce_92(val, _values, result)
- result = @builder.const_fetch(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-# reduce 93 omitted
-
-# reduce 94 omitted
-
-# reduce 95 omitted
-
-# reduce 96 omitted
-
-# reduce 97 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 519)
- def _reduce_98(val, _values, result)
- result = @builder.symbol(val[0])
-
- result
- end
-.,.,
-
-# reduce 99 omitted
-
-# reduce 100 omitted
-
-# reduce 101 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 528)
- def _reduce_102(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 532)
- def _reduce_103(val, _values, result)
- @lexer.state = :expr_fname
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 536)
- def _reduce_104(val, _values, result)
- result = val[0] << val[3]
-
- result
- end
-.,.,
-
-# reduce 105 omitted
-
-# reduce 106 omitted
-
-# reduce 107 omitted
-
-# reduce 108 omitted
-
-# reduce 109 omitted
-
-# reduce 110 omitted
-
-# reduce 111 omitted
-
-# reduce 112 omitted
-
-# reduce 113 omitted
-
-# reduce 114 omitted
-
-# reduce 115 omitted
-
-# reduce 116 omitted
-
-# reduce 117 omitted
-
-# reduce 118 omitted
-
-# reduce 119 omitted
-
-# reduce 120 omitted
-
-# reduce 121 omitted
-
-# reduce 122 omitted
-
-# reduce 123 omitted
-
-# reduce 124 omitted
-
-# reduce 125 omitted
-
-# reduce 126 omitted
-
-# reduce 127 omitted
-
-# reduce 128 omitted
-
-# reduce 129 omitted
-
-# reduce 130 omitted
-
-# reduce 131 omitted
-
-# reduce 132 omitted
-
-# reduce 133 omitted
-
-# reduce 134 omitted
-
-# reduce 135 omitted
-
-# reduce 136 omitted
-
-# reduce 137 omitted
-
-# reduce 138 omitted
-
-# reduce 139 omitted
-
-# reduce 140 omitted
-
-# reduce 141 omitted
-
-# reduce 142 omitted
-
-# reduce 143 omitted
-
-# reduce 144 omitted
-
-# reduce 145 omitted
-
-# reduce 146 omitted
-
-# reduce 147 omitted
-
-# reduce 148 omitted
-
-# reduce 149 omitted
-
-# reduce 150 omitted
-
-# reduce 151 omitted
-
-# reduce 152 omitted
-
-# reduce 153 omitted
-
-# reduce 154 omitted
-
-# reduce 155 omitted
-
-# reduce 156 omitted
-
-# reduce 157 omitted
-
-# reduce 158 omitted
-
-# reduce 159 omitted
-
-# reduce 160 omitted
-
-# reduce 161 omitted
-
-# reduce 162 omitted
-
-# reduce 163 omitted
-
-# reduce 164 omitted
-
-# reduce 165 omitted
-
-# reduce 166 omitted
-
-# reduce 167 omitted
-
-# reduce 168 omitted
-
-# reduce 169 omitted
-
-# reduce 170 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 555)
- def _reduce_171(val, _values, result)
- result = @builder.assign(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 559)
- def _reduce_172(val, _values, result)
- rescue_body = @builder.rescue_body(val[3],
- nil, nil, nil,
- nil, val[4])
-
- rescue_ = @builder.begin_body(val[2], [ rescue_body ])
-
- result = @builder.assign(val[0], val[1], rescue_)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 569)
- def _reduce_173(val, _values, result)
- result = @builder.op_assign(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 573)
- def _reduce_174(val, _values, result)
- result = @builder.op_assign(
- @builder.index(
- val[0], val[1], val[2], val[3]),
- val[4], val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 580)
- def _reduce_175(val, _values, result)
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 587)
- def _reduce_176(val, _values, result)
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 594)
- def _reduce_177(val, _values, result)
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 601)
- def _reduce_178(val, _values, result)
- diagnostic :error, :dynamic_const, nil, val[2], [ val[3] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 605)
- def _reduce_179(val, _values, result)
- diagnostic :error, :dynamic_const, nil, val[1], [ val[2] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 609)
- def _reduce_180(val, _values, result)
- result = @builder.op_assign(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 613)
- def _reduce_181(val, _values, result)
- result = @builder.range_inclusive(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 617)
- def _reduce_182(val, _values, result)
- result = @builder.range_exclusive(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 621)
- def _reduce_183(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 625)
- def _reduce_184(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 629)
- def _reduce_185(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 633)
- def _reduce_186(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 637)
- def _reduce_187(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 641)
- def _reduce_188(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 645)
- def _reduce_189(val, _values, result)
- result = @builder.unary_op(val[0],
- @builder.binary_op(
- @builder.integer(val[1]),
- val[2], val[3]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 652)
- def _reduce_190(val, _values, result)
- result = @builder.unary_op(val[0],
- @builder.binary_op(
- @builder.float(val[1]),
- val[2], val[3]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 659)
- def _reduce_191(val, _values, result)
- result = @builder.unary_op(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 663)
- def _reduce_192(val, _values, result)
- result = @builder.unary_op(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 667)
- def _reduce_193(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 671)
- def _reduce_194(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 675)
- def _reduce_195(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 679)
- def _reduce_196(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 683)
- def _reduce_197(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 687)
- def _reduce_198(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 691)
- def _reduce_199(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 695)
- def _reduce_200(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 699)
- def _reduce_201(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 703)
- def _reduce_202(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 707)
- def _reduce_203(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 711)
- def _reduce_204(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 715)
- def _reduce_205(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 719)
- def _reduce_206(val, _values, result)
- result = @builder.not_op(val[0], nil, val[1], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 723)
- def _reduce_207(val, _values, result)
- result = @builder.unary_op(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 727)
- def _reduce_208(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 731)
- def _reduce_209(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 735)
- def _reduce_210(val, _values, result)
- result = @builder.logical_op(:and, val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 739)
- def _reduce_211(val, _values, result)
- result = @builder.logical_op(:or, val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 743)
- def _reduce_212(val, _values, result)
- result = @builder.keyword_cmd(:defined?, val[0], nil, [ val[2] ], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 747)
- def _reduce_213(val, _values, result)
- result = @builder.ternary(val[0], val[1],
- val[2], val[3], val[4])
-
- result
- end
-.,.,
-
-# reduce 214 omitted
-
-# reduce 215 omitted
-
-# reduce 216 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 757)
- def _reduce_217(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 761)
- def _reduce_218(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 765)
- def _reduce_219(val, _values, result)
- result = val[0] << @builder.splat(val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 769)
- def _reduce_220(val, _values, result)
- result = [ @builder.associate(nil, val[0], nil) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 773)
- def _reduce_221(val, _values, result)
- result = [ @builder.splat(val[0], val[1]) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 778)
- def _reduce_222(val, _values, result)
- result = [ val[0], [], val[2] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 782)
- def _reduce_223(val, _values, result)
- result = [ val[0], val[1], val[3] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 786)
- def _reduce_224(val, _values, result)
- result = [ val[0], [ val[1] ], val[3] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 790)
- def _reduce_225(val, _values, result)
- result = [ val[0], val[1] << val[3], val[5] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 795)
- def _reduce_226(val, _values, result)
- result = [ nil, [], nil ]
-
- result
- end
-.,.,
-
-# reduce 227 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 801)
- def _reduce_228(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 805)
- def _reduce_229(val, _values, result)
- result = val[0].concat(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 809)
- def _reduce_230(val, _values, result)
- result = val[0].concat(
- [ @builder.splat(val[2], val[3]),
- *val[4] ])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 815)
- def _reduce_231(val, _values, result)
- result = [ @builder.associate(nil, val[0], nil),
- *val[1] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 820)
- def _reduce_232(val, _values, result)
- result = [ @builder.associate(nil, val[0], nil),
- @builder.splat(val[2], val[3]),
- *val[4] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 826)
- def _reduce_233(val, _values, result)
- result = val[0].concat(
- [ @builder.associate(nil, val[2], nil),
- *val[3] ])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 832)
- def _reduce_234(val, _values, result)
- result = val[0].concat(
- [ @builder.associate(nil, val[2], nil),
- @builder.splat(val[4], val[5]),
- *val[6] ])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 839)
- def _reduce_235(val, _values, result)
- result = [ @builder.splat(val[0], val[1]),
- *val[2] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 844)
- def _reduce_236(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 849)
- def _reduce_237(val, _values, result)
- result = [ val[0], *val[2].concat(val[3]) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 853)
- def _reduce_238(val, _values, result)
- result = [ val[0], val[2] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 857)
- def _reduce_239(val, _values, result)
- result = [ val[0],
- @builder.splat(val[2], val[3]),
- *val[4] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 863)
- def _reduce_240(val, _values, result)
- result = [ val[0],
- *val[2].
- push(@builder.splat(val[4], val[5])).
- concat(val[6]) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 870)
- def _reduce_241(val, _values, result)
- result = [ @builder.associate(nil, val[0], nil),
- *val[1] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 875)
- def _reduce_242(val, _values, result)
- result = [ @builder.associate(nil, val[0], nil),
- @builder.splat(val[2], val[3]),
- *val[4] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 881)
- def _reduce_243(val, _values, result)
- result = [ val[0],
- @builder.associate(nil, val[2], nil),
- *val[3] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 887)
- def _reduce_244(val, _values, result)
- result = [ val[0],
- *val[2].
- push(@builder.associate(nil, val[4], nil)).
- concat(val[5]) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 894)
- def _reduce_245(val, _values, result)
- result = [ val[0],
- @builder.associate(nil, val[2], nil),
- @builder.splat(val[4], val[5]),
- *val[6] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 901)
- def _reduce_246(val, _values, result)
- result = [ val[0],
- *val[2].
- push(@builder.associate(nil, val[4], nil)).
- push(@builder.splat(val[6], val[7])).
- concat(val[8]) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 909)
- def _reduce_247(val, _values, result)
- result = [ @builder.splat(val[0], val[1]),
- *val[2] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 914)
- def _reduce_248(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 918)
- def _reduce_249(val, _values, result)
- result = @lexer.cmdarg.dup
- @lexer.cmdarg.push(true)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 923)
- def _reduce_250(val, _values, result)
- @lexer.cmdarg = val[0]
-
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 930)
- def _reduce_251(val, _values, result)
- result = [ nil, val[0], nil ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 934)
- def _reduce_252(val, _values, result)
- @lexer.state = :expr_endarg
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 938)
- def _reduce_253(val, _values, result)
- result = [ val[0], [], val[2] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 942)
- def _reduce_254(val, _values, result)
- @lexer.state = :expr_endarg
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 946)
- def _reduce_255(val, _values, result)
- result = [ val[0], val[1], val[3] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 951)
- def _reduce_256(val, _values, result)
- result = @builder.block_pass(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 956)
- def _reduce_257(val, _values, result)
- result = [ val[1] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 960)
- def _reduce_258(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 965)
- def _reduce_259(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 969)
- def _reduce_260(val, _values, result)
- result = val[0] << val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 974)
- def _reduce_261(val, _values, result)
- result = val[0] << val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 978)
- def _reduce_262(val, _values, result)
- result = val[0] << @builder.splat(val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 982)
- def _reduce_263(val, _values, result)
- result = [ @builder.splat(val[0], val[1]) ]
-
- result
- end
-.,.,
-
-# reduce 264 omitted
-
-# reduce 265 omitted
-
-# reduce 266 omitted
-
-# reduce 267 omitted
-
-# reduce 268 omitted
-
-# reduce 269 omitted
-
-# reduce 270 omitted
-
-# reduce 271 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 995)
- def _reduce_272(val, _values, result)
- result = @builder.call_method(nil, nil, val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 999)
- def _reduce_273(val, _values, result)
- result = @builder.begin_keyword(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1003)
- def _reduce_274(val, _values, result)
- @lexer.state = :expr_endarg
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1007)
- def _reduce_275(val, _values, result)
- result = @builder.begin(val[0], val[1], val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1011)
- def _reduce_276(val, _values, result)
- result = @builder.begin(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1015)
- def _reduce_277(val, _values, result)
- result = @builder.const_fetch(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1019)
- def _reduce_278(val, _values, result)
- result = @builder.const_global(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1023)
- def _reduce_279(val, _values, result)
- result = @builder.index(val[0], val[1], val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1027)
- def _reduce_280(val, _values, result)
- result = @builder.array(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1031)
- def _reduce_281(val, _values, result)
- result = @builder.associate(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1035)
- def _reduce_282(val, _values, result)
- result = @builder.keyword_cmd(:return, val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1039)
- def _reduce_283(val, _values, result)
- result = @builder.keyword_cmd(:yield, val[0], val[1], val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1043)
- def _reduce_284(val, _values, result)
- result = @builder.keyword_cmd(:yield, val[0], val[1], [], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1047)
- def _reduce_285(val, _values, result)
- result = @builder.keyword_cmd(:yield, val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1051)
- def _reduce_286(val, _values, result)
- result = @builder.keyword_cmd(:defined?, val[0],
- val[2], [ val[3] ], val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1056)
- def _reduce_287(val, _values, result)
- method_call = @builder.call_method(nil, nil, val[0])
-
- begin_t, args, body, end_t = val[1]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
-
- result
- end
-.,.,
-
-# reduce 288 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 1065)
- def _reduce_289(val, _values, result)
- begin_t, args, body, end_t = val[1]
- result = @builder.block(val[0],
- begin_t, args, body, end_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1071)
- def _reduce_290(val, _values, result)
- else_t, else_ = val[4]
- result = @builder.condition(val[0], val[1], val[2],
- val[3], else_t,
- else_, val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1078)
- def _reduce_291(val, _values, result)
- else_t, else_ = val[4]
- result = @builder.condition(val[0], val[1], val[2],
- else_, else_t,
- val[3], val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1085)
- def _reduce_292(val, _values, result)
- @lexer.cond.push(true)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1089)
- def _reduce_293(val, _values, result)
- @lexer.cond.pop
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1093)
- def _reduce_294(val, _values, result)
- result = @builder.loop(:while, val[0], val[2], val[3],
- val[5], val[6])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1098)
- def _reduce_295(val, _values, result)
- @lexer.cond.push(true)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1102)
- def _reduce_296(val, _values, result)
- @lexer.cond.pop
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1106)
- def _reduce_297(val, _values, result)
- result = @builder.loop(:until, val[0], val[2], val[3],
- val[5], val[6])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1111)
- def _reduce_298(val, _values, result)
- when_bodies = val[3][0..-2]
- else_t, else_body = val[3][-1]
-
- result = @builder.case(val[0], val[1],
- when_bodies, else_t, else_body,
- val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1120)
- def _reduce_299(val, _values, result)
- when_bodies = val[2][0..-2]
- else_t, else_body = val[2][-1]
-
- result = @builder.case(val[0], nil,
- when_bodies, else_t, else_body,
- val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1129)
- def _reduce_300(val, _values, result)
- result = @builder.case(val[0], nil,
- [], val[2], val[3],
- val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1135)
- def _reduce_301(val, _values, result)
- @lexer.cond.push(true)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1139)
- def _reduce_302(val, _values, result)
- @lexer.cond.pop
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1143)
- def _reduce_303(val, _values, result)
- result = @builder.for(val[0], val[1],
- val[2], val[4],
- val[5], val[7], val[8])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1149)
- def _reduce_304(val, _values, result)
- @static_env.extend_static
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1153)
- def _reduce_305(val, _values, result)
- if in_def?
- diagnostic :error, :class_in_def, nil, val[0]
- end
-
- lt_t, superclass = val[2]
- result = @builder.def_class(val[0], val[1],
- lt_t, superclass,
- val[4], val[5])
-
- @static_env.unextend
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1166)
- def _reduce_306(val, _values, result)
- result = @def_level
- @def_level = 0
-
- @static_env.extend_static
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1173)
- def _reduce_307(val, _values, result)
- result = @builder.def_sclass(val[0], val[1], val[2],
- val[5], val[6])
-
- @static_env.unextend
-
- @def_level = val[4]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1182)
- def _reduce_308(val, _values, result)
- @static_env.extend_static
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1186)
- def _reduce_309(val, _values, result)
- if in_def?
- diagnostic :error, :module_in_def, nil, val[0]
- end
-
- result = @builder.def_module(val[0], val[1],
- val[3], val[4])
-
- @static_env.unextend
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1197)
- def _reduce_310(val, _values, result)
- @def_level += 1
- @static_env.extend_static
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1202)
- def _reduce_311(val, _values, result)
- result = @builder.def_method(val[0], val[1],
- val[3], val[4], val[5])
-
- @static_env.unextend
- @def_level -= 1
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1210)
- def _reduce_312(val, _values, result)
- @lexer.state = :expr_fname
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1214)
- def _reduce_313(val, _values, result)
- @def_level += 1
- @static_env.extend_static
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1219)
- def _reduce_314(val, _values, result)
- result = @builder.def_singleton(val[0], val[1], val[2],
- val[4], val[6], val[7], val[8])
-
- @static_env.unextend
- @def_level -= 1
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1227)
- def _reduce_315(val, _values, result)
- result = @builder.keyword_cmd(:break, val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1231)
- def _reduce_316(val, _values, result)
- result = @builder.keyword_cmd(:next, val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1235)
- def _reduce_317(val, _values, result)
- result = @builder.keyword_cmd(:redo, val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1239)
- def _reduce_318(val, _values, result)
- result = @builder.keyword_cmd(:retry, val[0])
-
- result
- end
-.,.,
-
-# reduce 319 omitted
-
-# reduce 320 omitted
-
-# reduce 321 omitted
-
-# reduce 322 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 1249)
- def _reduce_323(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-# reduce 324 omitted
-
-# reduce 325 omitted
-
-# reduce 326 omitted
-
-# reduce 327 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 1259)
- def _reduce_328(val, _values, result)
- else_t, else_ = val[4]
- result = [ val[0],
- @builder.condition(val[0], val[1], val[2],
- val[3], else_t,
- else_, nil),
- ]
-
- result
- end
-.,.,
-
-# reduce 329 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 1270)
- def _reduce_330(val, _values, result)
- result = val
-
- result
- end
-.,.,
-
-# reduce 331 omitted
-
-# reduce 332 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 1278)
- def _reduce_333(val, _values, result)
- result = [ @builder.arg_expr(val[0]) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1282)
- def _reduce_334(val, _values, result)
- result = val[0] << @builder.arg_expr(val[2])
-
- result
- end
-.,.,
-
-# reduce 335 omitted
-
-# reduce 336 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 1289)
- def _reduce_337(val, _values, result)
- result = val[0].
- push(@builder.blockarg_expr(val[2], val[3]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1294)
- def _reduce_338(val, _values, result)
- result = val[0].
- push(@builder.restarg_expr(val[2], val[3])).
- push(@builder.blockarg_expr(val[5], val[6]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1300)
- def _reduce_339(val, _values, result)
- result = val[0].
- push(@builder.restarg_expr(val[2])).
- push(@builder.blockarg_expr(val[4], val[5]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1306)
- def _reduce_340(val, _values, result)
- result = val[0].
- push(@builder.restarg_expr(val[2], val[3]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1311)
- def _reduce_341(val, _values, result)
- result = val[0].
- push(@builder.restarg_expr(val[2]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1316)
- def _reduce_342(val, _values, result)
- result = [ @builder.restarg_expr(val[0], val[1]),
- @builder.blockarg_expr(val[3], val[4]) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1321)
- def _reduce_343(val, _values, result)
- result = [ @builder.restarg_expr(val[0]),
- @builder.blockarg_expr(val[2], val[3]) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1326)
- def _reduce_344(val, _values, result)
- result = [ @builder.restarg_expr(val[0], val[1]) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1330)
- def _reduce_345(val, _values, result)
- result = [ @builder.restarg_expr(val[0]) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1334)
- def _reduce_346(val, _values, result)
- result = [ @builder.blockarg_expr(val[0], val[1]) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1340)
- def _reduce_347(val, _values, result)
- result = @builder.args(nil, [], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1344)
- def _reduce_348(val, _values, result)
- result = @builder.args(val[0], [], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1348)
- def _reduce_349(val, _values, result)
- result = @builder.args(val[0], [], val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1352)
- def _reduce_350(val, _values, result)
- result = @builder.args(val[0], val[1], val[2], false)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1357)
- def _reduce_351(val, _values, result)
- @static_env.extend_dynamic
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1361)
- def _reduce_352(val, _values, result)
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1368)
- def _reduce_353(val, _values, result)
- begin_t, block_args, body, end_t = val[1]
- result = @builder.block(val[0],
- begin_t, block_args, body, end_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1374)
- def _reduce_354(val, _values, result)
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1380)
- def _reduce_355(val, _values, result)
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1387)
- def _reduce_356(val, _values, result)
- lparen_t, args, rparen_t = val[1]
- result = @builder.call_method(nil, nil, val[0],
- lparen_t, args, rparen_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1393)
- def _reduce_357(val, _values, result)
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1399)
- def _reduce_358(val, _values, result)
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1405)
- def _reduce_359(val, _values, result)
- result = @builder.call_method(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1409)
- def _reduce_360(val, _values, result)
- lparen_t, args, rparen_t = val[1]
- result = @builder.keyword_cmd(:super, val[0],
- lparen_t, args, rparen_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1415)
- def _reduce_361(val, _values, result)
- result = @builder.keyword_cmd(:zsuper, val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1420)
- def _reduce_362(val, _values, result)
- @static_env.extend_dynamic
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1424)
- def _reduce_363(val, _values, result)
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1430)
- def _reduce_364(val, _values, result)
- @static_env.extend_dynamic
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1434)
- def _reduce_365(val, _values, result)
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1441)
- def _reduce_366(val, _values, result)
- result = [ @builder.when(val[0], val[1], val[2], val[3]),
- *val[4] ]
-
- result
- end
-.,.,
-
-# reduce 367 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 1448)
- def _reduce_368(val, _values, result)
- result = val[0] << @builder.splat(val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1452)
- def _reduce_369(val, _values, result)
- result = [ @builder.splat(val[0], val[1]) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1457)
- def _reduce_370(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-# reduce 371 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 1463)
- def _reduce_372(val, _values, result)
- assoc_t, exc_var = val[2]
-
- if val[1]
- exc_list = @builder.array(nil, val[1], nil)
- end
-
- result = [ @builder.rescue_body(val[0],
- exc_list, assoc_t, exc_var,
- val[3], val[4]),
- *val[5] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1476)
- def _reduce_373(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1481)
- def _reduce_374(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-# reduce 375 omitted
-
-# reduce 376 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 1488)
- def _reduce_377(val, _values, result)
- result = [ val[0], val[1] ]
-
- result
- end
-.,.,
-
-# reduce 378 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 1494)
- def _reduce_379(val, _values, result)
- result = [ val[0], val[1] ]
-
- result
- end
-.,.,
-
-# reduce 380 omitted
-
-# reduce 381 omitted
-
-# reduce 382 omitted
-
-# reduce 383 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 1504)
- def _reduce_384(val, _values, result)
- result = @builder.string_compose(nil, val[0], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1509)
- def _reduce_385(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1513)
- def _reduce_386(val, _values, result)
- result = val[0] << val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1518)
- def _reduce_387(val, _values, result)
- result = @builder.string_compose(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1522)
- def _reduce_388(val, _values, result)
- result = @builder.string(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1527)
- def _reduce_389(val, _values, result)
- result = @builder.xstring_compose(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1532)
- def _reduce_390(val, _values, result)
- opts = @builder.regexp_options(val[3])
- result = @builder.regexp_compose(val[0], val[1], val[2], opts)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1538)
- def _reduce_391(val, _values, result)
- result = @builder.words_compose(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1543)
- def _reduce_392(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1547)
- def _reduce_393(val, _values, result)
- result = val[0] << @builder.word(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1552)
- def _reduce_394(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1556)
- def _reduce_395(val, _values, result)
- result = val[0] << val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1561)
- def _reduce_396(val, _values, result)
- result = @builder.words_compose(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1566)
- def _reduce_397(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1570)
- def _reduce_398(val, _values, result)
- result = val[0] << @builder.string_internal(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1575)
- def _reduce_399(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1579)
- def _reduce_400(val, _values, result)
- result = val[0] << val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1584)
- def _reduce_401(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1588)
- def _reduce_402(val, _values, result)
- result = val[0] << val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1593)
- def _reduce_403(val, _values, result)
- result = @builder.string_internal(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1597)
- def _reduce_404(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1601)
- def _reduce_405(val, _values, result)
- @lexer.cond.push(false)
- @lexer.cmdarg.push(false)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1606)
- def _reduce_406(val, _values, result)
- @lexer.cond.lexpop
- @lexer.cmdarg.lexpop
-
- result = @builder.begin(val[0], val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1614)
- def _reduce_407(val, _values, result)
- result = @builder.gvar(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1618)
- def _reduce_408(val, _values, result)
- result = @builder.ivar(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1622)
- def _reduce_409(val, _values, result)
- result = @builder.cvar(val[0])
-
- result
- end
-.,.,
-
-# reduce 410 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 1629)
- def _reduce_411(val, _values, result)
- result = @builder.symbol(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1634)
- def _reduce_412(val, _values, result)
- result = @builder.symbol_compose(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1639)
- def _reduce_413(val, _values, result)
- result = @builder.integer(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1643)
- def _reduce_414(val, _values, result)
- result = @builder.float(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1647)
- def _reduce_415(val, _values, result)
- result = @builder.negate(val[0],
- @builder.integer(val[1]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1652)
- def _reduce_416(val, _values, result)
- result = @builder.negate(val[0],
- @builder.float(val[1]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1658)
- def _reduce_417(val, _values, result)
- result = @builder.ident(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1662)
- def _reduce_418(val, _values, result)
- result = @builder.ivar(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1666)
- def _reduce_419(val, _values, result)
- result = @builder.gvar(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1670)
- def _reduce_420(val, _values, result)
- result = @builder.cvar(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1674)
- def _reduce_421(val, _values, result)
- result = @builder.const(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1678)
- def _reduce_422(val, _values, result)
- result = @builder.nil(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1682)
- def _reduce_423(val, _values, result)
- result = @builder.self(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1686)
- def _reduce_424(val, _values, result)
- result = @builder.true(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1690)
- def _reduce_425(val, _values, result)
- result = @builder.false(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1694)
- def _reduce_426(val, _values, result)
- result = @builder.__FILE__(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1698)
- def _reduce_427(val, _values, result)
- result = @builder.__LINE__(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1703)
- def _reduce_428(val, _values, result)
- result = @builder.accessible(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1708)
- def _reduce_429(val, _values, result)
- result = @builder.assignable(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1713)
- def _reduce_430(val, _values, result)
- result = @builder.nth_ref(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1717)
- def _reduce_431(val, _values, result)
- result = @builder.back_ref(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1722)
- def _reduce_432(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1726)
- def _reduce_433(val, _values, result)
- result = [ val[0], val[1] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1730)
- def _reduce_434(val, _values, result)
- yyerrok
- result = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1736)
- def _reduce_435(val, _values, result)
- result = @builder.args(val[0], val[1], val[3])
-
- @lexer.state = :expr_beg
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1742)
- def _reduce_436(val, _values, result)
- result = @builder.args(nil, val[0], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1747)
- def _reduce_437(val, _values, result)
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1754)
- def _reduce_438(val, _values, result)
- result = val[0].
- concat(val[2]).
- concat(val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1760)
- def _reduce_439(val, _values, result)
- result = val[0].
- concat(val[2]).
- concat(val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1766)
- def _reduce_440(val, _values, result)
- result = val[0].
- concat(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1771)
- def _reduce_441(val, _values, result)
- result = val[0].
- concat(val[2]).
- concat(val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1777)
- def _reduce_442(val, _values, result)
- result = val[0].
- concat(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1782)
- def _reduce_443(val, _values, result)
- result = val[0].
- concat(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1787)
- def _reduce_444(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1791)
- def _reduce_445(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1796)
- def _reduce_446(val, _values, result)
- diagnostic :error, :argument_const, nil, val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1800)
- def _reduce_447(val, _values, result)
- diagnostic :error, :argument_ivar, nil, val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1804)
- def _reduce_448(val, _values, result)
- diagnostic :error, :argument_gvar, nil, val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1808)
- def _reduce_449(val, _values, result)
- diagnostic :error, :argument_cvar, nil, val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1812)
- def _reduce_450(val, _values, result)
- @static_env.declare val[0][0]
-
- result = @builder.arg(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1819)
- def _reduce_451(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1823)
- def _reduce_452(val, _values, result)
- result = val[0] << val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1828)
- def _reduce_453(val, _values, result)
- @static_env.declare val[0][0]
-
- result = @builder.optarg(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1835)
- def _reduce_454(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1839)
- def _reduce_455(val, _values, result)
- result = val[0] << val[2]
-
- result
- end
-.,.,
-
-# reduce 456 omitted
-
-# reduce 457 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 1846)
- def _reduce_458(val, _values, result)
- @static_env.declare val[1][0]
-
- result = [ @builder.restarg(val[0], val[1]) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1852)
- def _reduce_459(val, _values, result)
- result = [ @builder.restarg(val[0]) ]
-
- result
- end
-.,.,
-
-# reduce 460 omitted
-
-# reduce 461 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 1859)
- def _reduce_462(val, _values, result)
- @static_env.declare val[1][0]
-
- result = @builder.blockarg(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1866)
- def _reduce_463(val, _values, result)
- result = [ val[1] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1870)
- def _reduce_464(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-# reduce 465 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 1876)
- def _reduce_466(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1881)
- def _reduce_467(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1885)
- def _reduce_468(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1889)
- def _reduce_469(val, _values, result)
- result = @builder.pair_list_18(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1894)
- def _reduce_470(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1898)
- def _reduce_471(val, _values, result)
- result = val[0] << val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby18.y', 1903)
- def _reduce_472(val, _values, result)
- result = @builder.pair(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-# reduce 473 omitted
-
-# reduce 474 omitted
-
-# reduce 475 omitted
-
-# reduce 476 omitted
-
-# reduce 477 omitted
-
-# reduce 478 omitted
-
-# reduce 479 omitted
-
-# reduce 480 omitted
-
-# reduce 481 omitted
-
-# reduce 482 omitted
-
-# reduce 483 omitted
-
-# reduce 484 omitted
-
-# reduce 485 omitted
-
-# reduce 486 omitted
-
-# reduce 487 omitted
-
-# reduce 488 omitted
-
-# reduce 489 omitted
-
-# reduce 490 omitted
-
-# reduce 491 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 1916)
- def _reduce_492(val, _values, result)
- yyerrok
-
- result
- end
-.,.,
-
-# reduce 493 omitted
-
-# reduce 494 omitted
-
-# reduce 495 omitted
-
-module_eval(<<'.,.,', 'ruby18.y', 1925)
- def _reduce_496(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-def _reduce_none(val, _values, result)
- val[0]
-end
-
- end # class Ruby18
-end # module Parser
diff --git a/test/racc/regress/ruby22 b/test/racc/regress/ruby22
deleted file mode 100644
index 9eec38fd28..0000000000
--- a/test/racc/regress/ruby22
+++ /dev/null
@@ -1,11182 +0,0 @@
-#
-# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.5.2
-# from Racc grammar file "".
-#
-
-require 'racc/parser.rb'
-
-
-require 'parser'
-
-Parser.check_for_encoding_support
-
-module Parser
- class Ruby22 < Racc::Parser
-
-module_eval(<<'...end ruby22.y/module_eval...', 'ruby22.y', 2374)
-
- def version
- 22
- end
-
- def default_encoding
- Encoding::UTF_8
- end
-...end ruby22.y/module_eval...
-##### State transition tables begin ###
-
-racc_action_table = [
- -476, -97, 268, 214, 215, -98, -105, -476, -476, -476,
- -490, 568, -476, -476, -476, 610, -476, 580, 217, 612,
- -288, 581, 214, 215, -476, -491, -476, -476, -476, 647,
- 268, 268, 214, 215, -104, 589, -476, -476, 568, -476,
- -476, -476, -476, -476, 568, 568, 212, 109, 568, 815,
- -100, 121, -100, -83, -102, -99, 214, 215, 268, 218,
- -288, 806, -69, 574, 646, -102, -97, -476, -476, -476,
- -476, -476, -476, -476, -476, -476, -476, -476, -476, -476,
- -476, -98, 609, -476, -476, -476, 611, -476, -476, 715,
- 267, -476, 206, -96, -476, -476, 263, -476, 218, -476,
- 207, -476, -105, -476, -476, -99, -476, -476, -476, -476,
- -476, -88, -476, -479, -476, -89, -96, 218, 267, 267,
- -479, -479, -479, 263, -101, -479, -479, -479, -476, -479,
- 113, -476, -476, -476, -476, 112, -476, -479, -476, -479,
- -479, -479, 588, -476, -95, 715, 267, -101, 715, -479,
- -479, 218, -479, -479, -479, -479, -479, 113, 208, 814,
- -91, -91, 112, 113, 113, 113, 842, 113, 112, 112,
- 112, -100, 112, -102, -99, -93, -100, 213, -102, -99,
- -479, -479, -479, -479, -479, -479, -479, -479, -479, -479,
- -479, -479, -479, -479, 113, 257, -479, -479, -479, 112,
- -479, -479, -574, -93, -479, 316, -103, -479, -479, 317,
- -479, 597, -479, -574, -479, -90, -479, -479, 444, -479,
- -479, -479, -479, -479, -291, -479, 218, -479, -91, 214,
- 215, -291, -291, -291, 647, 260, 527, -291, -291, 526,
- -291, -479, 261, -101, -479, -479, -479, -479, -101, -479,
- 113, -479, -571, 113, 113, 112, -479, -92, 112, 112,
- -291, -291, 386, -291, -291, -291, -291, -291, -91, 646,
- -93, -91, -575, 396, -105, 399, 599, 598, 398, 397,
- -91, 548, 597, 545, 544, 543, 747, 546, 443, 91,
- 92, -291, -291, -291, -291, -291, -291, -291, -291, -291,
- -291, -291, -291, -291, -291, -476, -572, -291, -291, -291,
- -93, 630, -476, -93, -490, -291, -94, -571, -291, 91,
- 92, -476, -93, -291, 113, -291, 515, -291, -291, 112,
- -291, -291, -291, -291, -291, 597, -291, -578, -291, 445,
- -571, -491, -476, 446, -578, -578, -578, 599, 598, -476,
- -578, -578, -291, -578, 647, -291, -291, 749, -94, -479,
- -291, -88, -578, 217, 833, -578, -479, -103, 93, 94,
- -97, -572, 477, -578, -578, -479, -578, -578, -578, -578,
- -578, 548, -104, 545, 544, 543, 515, 546, -89, 646,
- 527, 647, 597, 529, -572, 597, 486, -98, 93, 94,
- 599, 598, 595, -412, -578, -578, -578, -578, -578, -578,
- -578, -578, -578, -578, -578, -578, -578, -578, -578, 625,
- -578, -578, -578, 597, 631, -578, 646, 113, -578, 626,
- -574, -578, 112, -95, -578, 597, -578, 770, -578, -578,
- -578, -578, -104, -578, -578, -578, -578, -578, 488, -578,
- -578, -578, 597, -578, 490, 597, -412, 599, 598, 595,
- 599, 598, 600, -412, 957, -578, -91, 756, -578, -578,
- -578, -92, -412, -578, 662, -100, -93, -578, -578, -578,
- -101, 498, -578, -578, -578, -102, -578, -479, 599, 598,
- 602, -412, -578, -100, -479, -578, -578, -578, -578, -578,
- 599, 598, 604, 771, -574, -68, -578, -578, -578, -578,
- -578, -578, -578, -578, -90, 218, 860, 599, 598, 608,
- 599, 598, 613, -99, 214, 215, -486, -578, 548, -485,
- 545, 544, 543, -486, 546, 501, -485, -578, -578, -578,
- -578, -578, -578, -578, -578, -578, -578, -578, -578, -578,
- -578, 553, 502, -578, -578, -578, -487, 772, -578, -102,
- 509, -578, 556, -487, -578, -578, 701, -578, 272, -578,
- 218, -578, -99, -578, -578, 704, -578, -578, -578, -578,
- -578, 553, -578, -578, -578, 527, -484, 79, 529, 113,
- -481, 263, 556, -484, 112, 564, 563, -481, -578, 80,
- 557, -578, -578, -578, -578, 238, -578, -291, -578, 81,
- -482, 210, 512, -101, -291, -291, -291, -482, 211, -291,
- -291, -291, -334, -291, 440, 564, 563, 209, 516, -334,
- 557, 441, -483, -291, -291, -291, 238, 235, -334, -483,
- 442, 237, 236, -291, -291, 260, -291, -291, -291, -291,
- -291, 548, 261, 545, 544, 543, -488, 546, 548, 218,
- 545, 544, 543, -488, 546, 548, 530, 545, 544, 543,
- 527, 546, -488, 529, -291, -291, -291, -291, -291, -291,
- -291, -291, -291, -291, -291, -291, -291, -291, 238, 701,
- -291, -291, -291, -489, 773, -291, 701, 238, -291, 531,
- -489, -291, -291, 701, -291, 704, -291, 490, -291, -489,
- -291, -291, 904, -291, -291, -291, -291, -291, 113, -291,
- 235, -291, 574, 112, 237, 236, 238, 233, 234, 235,
- 214, 215, 388, 237, 236, -291, 233, 234, -291, -291,
- -291, -291, 113, -291, 113, -291, 578, 112, 579, 112,
- -103, 5, 69, 70, 71, 9, 57, 614, 235, 507,
- 63, 64, 237, 236, 617, 67, 508, 65, 66, 68,
- 30, 31, 72, 73, 218, 506, -263, 981, 619, 29,
- 28, 27, 101, 100, 102, 103, 741, 742, 19, 218,
- 743, 107, 108, 635, 8, 45, 7, 10, 105, 104,
- 106, 95, 56, 97, 96, 98, 623, 99, 107, 108,
- 624, 91, 92, 263, 42, 43, 41, 238, 242, 247,
- 248, 249, 244, 246, 254, 255, 250, 251, -281, 231,
- 232, 517, 634, 252, 253, -281, 40, 637, 518, 33,
- 564, 563, 58, 59, -281, 238, 60, 442, 35, 235,
- 238, 241, 44, 237, 236, 238, 233, 234, 245, 243,
- 239, 20, 240, 837, 806, 238, 89, 79, 82, 83,
- 576, 84, 86, 85, 87, 837, 806, 577, 218, 80,
- 88, 218, 256, 218, -240, -83, 575, 62, 666, 81,
- 93, 94, 290, 69, 70, 71, 9, 57, 218, 520,
- 584, 63, 64, 677, 682, 683, 67, 583, 65, 66,
- 68, 30, 31, 72, 73, 685, 585, 689, 692, 693,
- 29, 28, 27, 101, 100, 102, 103, 695, 697, 19,
- 699, 707, 708, 709, 620, 8, 45, 292, 10, 105,
- 104, 106, 95, 56, 97, 96, 98, 711, 99, 107,
- 108, 574, 91, 92, 718, 42, 43, 41, 238, 242,
- 247, 248, 249, 244, 246, 254, 255, 250, 251, -292,
- 231, 232, -292, 736, 252, 253, -292, 40, 746, -292,
- 294, 750, 751, 58, 59, -292, -264, 60, -292, 35,
- 235, 757, 241, 44, 237, 236, 477, 233, 234, 245,
- 243, 239, 20, 240, 477, 218, 257, 89, 79, 82,
- 83, 584, 84, 86, 85, 87, 488, 490, 939, 799,
- 80, 88, 677, 256, 218, 263, 263, 585, 62, 677,
- 81, 93, 94, 5, 69, 70, 71, 9, 57, 238,
- 806, 584, 63, 64, 218, 218, 831, 67, 939, 65,
- 66, 68, 30, 31, 72, 73, 218, 585, 806, 841,
- 218, 29, 28, 27, 101, 100, 102, 103, 218, 850,
- 19, -265, 859, 861, 862, 635, 8, 45, 7, 10,
- 105, 104, 106, 95, 56, 97, 96, 98, 692, 99,
- 107, 108, 865, 91, 92, 867, 42, 43, 41, 238,
- 242, 247, 248, 249, 244, 246, 254, 255, 250, 251,
- -291, 231, 232, -488, 869, 252, 253, -291, 40, 871,
- -488, 33, -575, 218, 58, 59, -291, 873, 60, -488,
- 35, 235, 874, 241, 44, 237, 236, 877, 233, 234,
- 245, 243, 239, 20, 240, 879, 880, 677, 89, 79,
- 82, 83, -489, 84, 86, 85, 87, 882, -263, -489,
- 886, 80, 88, 888, 256, 891, 692, 893, -489, 62,
- 895, 81, 93, 94, 290, 69, 70, 71, 9, 57,
- 897, 899, 986, 63, 64, 899, 218, 905, 67, 987,
- 65, 66, 68, 30, 31, 72, 73, 907, 985, 909,
- 915, 918, 29, 28, 27, 101, 100, 102, 103, 218,
- 936, 19, 545, 544, 543, 922, 546, 8, 45, 292,
- 10, 105, 104, 106, 95, 56, 97, 96, 98, -266,
- 99, 107, 108, 933, 91, 92, 940, 42, 43, 41,
- 238, 242, 247, 248, 249, 244, 246, 254, 255, 250,
- 251, -291, 231, 232, -281, 941, 252, 253, -291, 40,
- 950, -281, 33, -575, 951, 58, 59, -291, 959, 60,
- -281, 35, 235, 961, 241, 44, 237, 236, 962, 233,
- 234, 245, 243, 239, 20, 240, 967, 736, 692, 89,
- 79, 82, 83, -292, 84, 86, 85, 87, 971, 973,
- -292, 975, 80, 88, 977, 256, 977, 988, 989, -292,
- 62, 899, 81, 93, 94, 290, 69, 70, 71, 9,
- 57, 899, 899, 994, 63, 64, 959, -575, -574, 67,
- 682, 65, 66, 68, 30, 31, 72, 73, 116, 117,
- 118, 119, 120, 29, 28, 27, 101, 100, 102, 103,
- 959, 936, 19, 545, 544, 543, 1013, 546, 8, 45,
- 292, 10, 105, 104, 106, 95, 56, 97, 96, 98,
- 1014, 99, 107, 108, 1015, 91, 92, 977, 42, 43,
- 41, 238, 242, 247, 248, 249, 244, 246, 254, 255,
- 250, 251, -291, 231, 232, 977, 977, 252, 253, -291,
- 40, 218, 899, 33, -575, 959, 58, 59, -291, 977,
- 60, nil, 35, 235, nil, 241, 44, 237, 236, nil,
- 233, 234, 245, 243, 239, 20, 240, nil, nil, nil,
- 89, 79, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, 256, nil, nil, nil,
- nil, 62, nil, 81, 93, 94, 290, 69, 70, 71,
- 9, 57, nil, nil, nil, 63, 64, nil, nil, nil,
- 67, nil, 65, 66, 68, 30, 31, 72, 73, 116,
- 117, 118, 119, 120, 29, 28, 27, 101, 100, 102,
- 103, nil, nil, 19, 116, 117, 118, 119, 120, 8,
- 45, 292, 10, 105, 104, 106, 95, 56, 97, 96,
- 98, nil, 99, 107, 108, nil, 91, 92, nil, 42,
- 43, 41, 238, 242, 247, 248, 249, 244, 246, 254,
- 255, 250, 251, nil, 231, 232, nil, nil, 252, 253,
- nil, 40, nil, nil, 294, nil, nil, 58, 59, nil,
- nil, 60, nil, 35, 235, nil, 241, 44, 237, 236,
- nil, 233, 234, 245, 243, 239, 20, 240, nil, nil,
- nil, 89, 79, 82, 83, nil, 84, 86, 85, 87,
- nil, nil, nil, nil, 80, 88, nil, 256, nil, nil,
- nil, nil, 62, nil, 81, 93, 94, 290, 69, 70,
- 71, 9, 57, nil, nil, nil, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 30, 31, 72, 73,
- nil, nil, nil, nil, nil, 29, 28, 27, 101, 100,
- 102, 103, nil, nil, 19, nil, nil, nil, nil, nil,
- 8, 45, 292, 10, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, 238, 242, 247, 248, 249, 244, 246,
- 254, 255, 250, 251, nil, 231, 232, nil, nil, 252,
- 253, nil, 40, nil, nil, 294, nil, nil, 58, 59,
- nil, nil, 60, nil, 35, 235, nil, 241, 44, 237,
- 236, nil, 233, 234, 245, 243, 239, 20, 240, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, 256, nil,
- nil, nil, nil, 62, nil, 81, 93, 94, 290, 69,
- 70, 71, 9, 57, nil, nil, nil, 63, 64, nil,
- nil, nil, 67, nil, 65, 66, 68, 30, 31, 72,
- 73, nil, nil, nil, nil, nil, 29, 28, 27, 101,
- 100, 102, 103, nil, nil, 19, nil, nil, nil, nil,
- nil, 8, 45, 292, 10, 105, 104, 106, 95, 56,
- 97, 96, 98, nil, 99, 107, 108, nil, 91, 92,
- nil, 42, 43, 41, 238, 242, 247, 248, 249, 244,
- 246, 254, 255, 250, 251, nil, 231, 232, nil, nil,
- 252, 253, nil, 40, nil, nil, 33, nil, nil, 58,
- 59, nil, nil, 60, nil, 35, 235, nil, 241, 44,
- 237, 236, nil, 233, 234, 245, 243, 239, 20, 240,
- nil, nil, nil, 89, 79, 82, 83, nil, 84, 86,
- 85, 87, nil, nil, nil, nil, 80, 88, 218, 256,
- nil, nil, nil, nil, 62, nil, 81, 93, 94, 5,
- 69, 70, 71, 9, 57, nil, nil, nil, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 30, 31,
- 72, 73, nil, nil, nil, nil, nil, 29, 28, 27,
- 101, 100, 102, 103, nil, nil, 19, nil, nil, nil,
- nil, nil, 8, 45, 7, 10, 105, 104, 106, 95,
- 56, 97, 96, 98, nil, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, 238, 242, 247, 248, 249,
- 244, 246, 254, 255, 250, 251, nil, 231, 232, nil,
- nil, 252, 253, nil, 40, nil, nil, 33, nil, nil,
- 58, 59, nil, nil, 60, nil, 35, 235, nil, 241,
- 44, 237, 236, nil, 233, 234, 245, 243, 239, 20,
- 240, nil, nil, nil, 89, 79, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- 256, nil, nil, nil, nil, 62, nil, 81, 93, 94,
- 290, 69, 70, 71, 9, 57, nil, nil, nil, 63,
- 64, nil, nil, nil, 67, nil, 65, 66, 68, 30,
- 31, 72, 73, nil, nil, nil, nil, nil, 29, 28,
- 27, 101, 100, 102, 103, nil, nil, 19, nil, nil,
- nil, nil, nil, 8, 45, 292, 10, 105, 104, 106,
- 95, 56, 97, 96, 98, nil, 99, 107, 108, nil,
- 91, 92, nil, 42, 43, 41, 238, 242, 247, 248,
- 249, 244, 246, 254, 255, 250, 251, nil, 231, 232,
- nil, nil, 252, 253, nil, 40, nil, nil, 33, nil,
- nil, 58, 59, nil, nil, 60, nil, 35, 235, nil,
- 241, 44, 237, 236, nil, 233, 234, 245, 243, 239,
- 20, 240, nil, nil, nil, 89, 79, 82, 83, nil,
- 84, 86, 85, 87, nil, nil, nil, nil, 80, 88,
- nil, 256, nil, nil, nil, nil, 62, nil, 81, 93,
- 94, 290, 69, 70, 71, 9, 57, nil, nil, nil,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 30, 31, 72, 73, nil, nil, nil, nil, nil, 29,
- 28, 27, 101, 100, 102, 103, nil, nil, 19, nil,
- nil, nil, nil, nil, 8, 45, 292, 10, 105, 104,
- 106, 95, 56, 97, 96, 98, nil, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, 238, 242, 247,
- 248, 249, 244, 246, 254, 255, 250, 251, nil, 231,
- 232, nil, nil, 252, 253, nil, 40, nil, nil, 33,
- nil, nil, 58, 59, nil, nil, 60, nil, 35, 235,
- nil, 241, 44, 237, 236, nil, 233, 234, 245, 243,
- 239, 20, 240, nil, nil, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, 256, nil, nil, nil, nil, 62, nil, 81,
- 93, 94, 290, 69, 70, 71, 9, 57, nil, nil,
- nil, 63, 64, nil, nil, nil, 67, nil, 65, 66,
- 68, 30, 31, 72, 73, nil, nil, nil, nil, nil,
- 29, 28, 27, 101, 100, 102, 103, nil, nil, 19,
- nil, nil, nil, nil, nil, 8, 45, 292, 10, 105,
- 104, 106, 95, 56, 97, 96, 98, nil, 99, 107,
- 108, nil, 91, 92, nil, 42, 43, 41, 238, 242,
- 247, 248, 249, 244, 246, 254, 255, 250, 251, nil,
- 231, 232, nil, nil, 252, 253, nil, 40, nil, nil,
- 33, nil, nil, 58, 59, nil, nil, 60, nil, 35,
- 235, nil, 241, 44, 237, 236, nil, 233, 234, 245,
- 243, 239, 20, 240, nil, nil, nil, 89, 79, 82,
- 83, nil, 84, 86, 85, 87, nil, nil, nil, nil,
- 80, 88, nil, 256, nil, nil, nil, nil, 62, nil,
- 81, 93, 94, 290, 69, 70, 71, 9, 57, nil,
- nil, nil, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 30, 31, 72, 73, nil, nil, nil, nil,
- nil, 29, 28, 27, 101, 100, 102, 103, nil, nil,
- 19, nil, nil, nil, nil, nil, 8, 45, 292, 10,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, 238,
- 242, 247, 248, 249, 244, 246, 254, 255, 250, 251,
- nil, 231, 232, nil, nil, 252, 253, nil, 40, nil,
- nil, 33, nil, nil, 58, 59, nil, nil, 60, nil,
- 35, 235, nil, 241, 44, 237, 236, nil, 233, 234,
- 245, 243, 239, 20, 240, nil, nil, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, 256, nil, nil, nil, nil, 62,
- nil, 81, 93, 94, 290, 69, 70, 71, 9, 57,
- nil, nil, nil, 63, 64, nil, nil, nil, 67, nil,
- 65, 66, 68, 30, 31, 72, 73, nil, nil, nil,
- nil, nil, 29, 28, 27, 101, 100, 102, 103, nil,
- nil, 19, nil, nil, nil, nil, nil, 8, 45, 292,
- 10, 105, 104, 106, 95, 56, 97, 96, 98, nil,
- 99, 107, 108, nil, 91, 92, nil, 42, 43, 41,
- 238, 242, 247, 248, 249, 244, 246, 254, 255, 250,
- 251, nil, 231, 232, nil, nil, 252, 253, nil, 40,
- nil, nil, 33, nil, nil, 58, 59, nil, nil, 60,
- nil, 35, 235, nil, 241, 44, 237, 236, nil, 233,
- 234, 245, 243, 239, 20, 240, nil, nil, nil, 89,
- 79, 82, 83, nil, 84, 86, 85, 87, nil, nil,
- nil, nil, 80, 88, nil, 256, nil, nil, nil, nil,
- 62, nil, 81, 93, 94, 290, 69, 70, 71, 9,
- 57, nil, nil, nil, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 30, 31, 72, 73, nil, nil,
- nil, nil, nil, 29, 28, 27, 101, 100, 102, 103,
- nil, nil, 19, nil, nil, nil, nil, nil, 8, 45,
- 292, 10, 105, 104, 106, 95, 56, 97, 96, 98,
- nil, 99, 107, 108, nil, 91, 92, nil, 42, 43,
- 41, 238, 242, 247, 248, 249, 244, 246, 254, 255,
- 250, 251, nil, 231, 232, nil, nil, 252, 253, nil,
- 40, nil, nil, 33, nil, nil, 58, 59, nil, nil,
- 60, nil, 35, 235, nil, 241, 44, 237, 236, nil,
- 233, 234, 245, 243, 239, 20, 240, nil, nil, nil,
- 89, 79, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, 256, nil, nil, nil,
- nil, 62, nil, 81, 93, 94, 290, 69, 70, 71,
- 9, 57, nil, nil, nil, 63, 64, nil, nil, nil,
- 67, nil, 65, 66, 68, 30, 31, 72, 73, nil,
- nil, nil, nil, nil, 29, 28, 27, 101, 100, 102,
- 103, nil, nil, 19, nil, nil, nil, nil, nil, 8,
- 45, 292, 10, 105, 104, 106, 95, 56, 97, 96,
- 98, nil, 99, 107, 108, nil, 91, 92, nil, 42,
- 43, 41, 238, 242, 247, 248, 249, 244, 246, 254,
- 255, 250, 251, nil, 231, 232, nil, nil, 252, 253,
- nil, 40, nil, nil, 33, nil, nil, 58, 59, nil,
- nil, 60, nil, 35, 235, nil, 241, 44, 237, 236,
- nil, 233, 234, 245, 243, 239, 20, 240, nil, nil,
- nil, 89, 79, 82, 83, nil, 84, 86, 85, 87,
- nil, nil, nil, nil, 80, 88, nil, 256, nil, nil,
- nil, nil, 62, nil, 81, 93, 94, 290, 69, 70,
- 71, 9, 57, nil, nil, nil, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 30, 31, 72, 73,
- nil, nil, nil, nil, nil, 29, 28, 27, 101, 100,
- 102, 103, nil, nil, 19, nil, nil, nil, nil, nil,
- 8, 45, 292, 10, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, 238, 242, 247, 248, 249, 244, 246,
- 254, 255, 250, 251, nil, 231, 232, nil, nil, 252,
- 253, nil, 40, nil, nil, 33, nil, nil, 58, 59,
- nil, nil, 60, nil, 35, 235, nil, 241, 44, 237,
- 236, nil, 233, 234, 245, 243, 239, 20, 240, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, 256, nil,
- nil, nil, nil, 62, nil, 81, 93, 94, 290, 69,
- 70, 71, 9, 57, nil, nil, nil, 63, 64, nil,
- nil, nil, 67, nil, 65, 66, 68, 30, 31, 72,
- 73, nil, nil, nil, nil, nil, 29, 28, 27, 101,
- 100, 102, 103, nil, nil, 19, nil, nil, nil, nil,
- nil, 8, 45, 292, 10, 105, 104, 106, 95, 56,
- 97, 96, 98, nil, 99, 107, 108, nil, 91, 92,
- nil, 42, 43, 41, 238, 242, 247, 248, 249, 244,
- 246, 254, 255, 250, 251, nil, -597, -597, nil, nil,
- 252, 253, nil, 40, nil, nil, 33, nil, nil, 58,
- 59, nil, nil, 60, nil, 35, 235, nil, 241, 44,
- 237, 236, nil, 233, 234, 245, 243, 239, 20, 240,
- nil, nil, nil, 89, 79, 82, 83, nil, 84, 86,
- 85, 87, nil, nil, nil, nil, 80, 88, nil, nil,
- nil, nil, nil, nil, 62, nil, 81, 93, 94, 290,
- 69, 70, 71, 9, 57, nil, nil, nil, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 30, 31,
- 72, 73, nil, nil, nil, nil, nil, 29, 28, 27,
- 101, 100, 102, 103, nil, nil, 19, nil, nil, nil,
- nil, nil, 8, 45, 292, 10, 105, 104, 106, 95,
- 56, 97, 96, 98, nil, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, 238, 242, 247, 248, 249,
- 244, 246, 254, 255, 250, 251, nil, -597, -597, nil,
- nil, 252, 253, nil, 40, nil, nil, 33, nil, nil,
- 58, 59, nil, nil, 60, nil, 35, 235, nil, 241,
- 44, 237, 236, nil, 233, 234, 245, 243, 239, 20,
- 240, nil, nil, nil, 89, 79, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, nil, nil, nil, 62, nil, 81, 93, 94,
- 290, 69, 70, 71, 9, 57, nil, nil, nil, 63,
- 64, nil, nil, nil, 67, nil, 65, 66, 68, 30,
- 31, 72, 73, nil, nil, nil, nil, nil, 29, 28,
- 27, 101, 100, 102, 103, nil, 548, 19, 545, 544,
- 543, nil, 546, 8, 45, 292, 10, 105, 104, 106,
- 95, 56, 97, 96, 98, nil, 99, 107, 108, nil,
- 91, 92, nil, 42, 43, 41, 238, -597, -597, -597,
- -597, 244, 246, nil, 701, -597, -597, nil, nil, nil,
- nil, nil, 252, 253, nil, 40, nil, nil, 33, nil,
- nil, 58, 59, nil, nil, 60, nil, 35, 235, nil,
- 241, 44, 237, 236, nil, 233, 234, 245, 243, 239,
- 20, 240, nil, nil, nil, 89, 79, 82, 83, nil,
- 84, 86, 85, 87, nil, nil, nil, nil, 80, 88,
- nil, nil, nil, nil, nil, nil, 62, nil, 81, 93,
- 94, 290, 69, 70, 71, 9, 57, nil, nil, nil,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 30, 31, 72, 73, nil, nil, nil, nil, nil, 29,
- 28, 27, 101, 100, 102, 103, nil, 548, 19, 545,
- 544, 543, nil, 546, 8, 45, 292, 10, 105, 104,
- 106, 95, 56, 97, 96, 98, nil, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, 238, nil, nil,
- 548, nil, 545, 544, 543, 701, 546, nil, nil, nil,
- nil, nil, nil, 252, 253, nil, 40, nil, nil, 33,
- nil, nil, 58, 59, nil, nil, 60, nil, 35, 235,
- nil, 241, 44, 237, 236, nil, 233, 234, 701, nil,
- 239, 20, 240, nil, nil, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, nil, nil, nil, 62, nil, 81,
- 93, 94, 290, 69, 70, 71, 9, 57, nil, nil,
- nil, 63, 64, nil, nil, nil, 67, nil, 65, 66,
- 68, 30, 31, 72, 73, nil, nil, nil, nil, nil,
- 29, 28, 27, 101, 100, 102, 103, nil, 548, 19,
- 545, 544, 543, nil, 546, 8, 45, 292, 10, 105,
- 104, 106, 95, 56, 97, 96, 98, nil, 99, 107,
- 108, nil, 91, 92, nil, 42, 43, 41, 238, nil,
- nil, nil, nil, nil, nil, nil, 701, nil, nil, nil,
- nil, nil, nil, nil, 252, 253, nil, 40, nil, nil,
- 33, nil, nil, 58, 59, nil, nil, 60, nil, 35,
- 235, nil, 241, 44, 237, 236, nil, 233, 234, nil,
- nil, 239, 20, 240, nil, nil, nil, 89, 79, 82,
- 83, nil, 84, 86, 85, 87, nil, nil, nil, nil,
- 80, 88, nil, nil, nil, nil, nil, nil, 62, nil,
- 81, 93, 94, 290, 69, 70, 71, 9, 57, nil,
- nil, nil, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 30, 31, 72, 73, nil, nil, nil, nil,
- nil, 29, 28, 27, 101, 100, 102, 103, nil, nil,
- 19, nil, nil, nil, nil, nil, 8, 45, 292, 10,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, 238,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 252, 253, nil, 40, nil,
- nil, 33, nil, nil, 58, 59, nil, nil, 60, nil,
- 35, 235, nil, 241, 44, 237, 236, nil, 233, 234,
- nil, nil, 239, 20, 240, nil, nil, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, nil, nil, nil, 62,
- nil, 81, 93, 94, 290, 69, 70, 71, 9, 57,
- nil, nil, nil, 63, 64, nil, nil, nil, 67, nil,
- 65, 66, 68, 30, 31, 72, 73, nil, nil, nil,
- nil, nil, 29, 28, 27, 101, 100, 102, 103, nil,
- nil, 19, nil, nil, nil, nil, nil, 8, 45, 292,
- 10, 105, 104, 106, 95, 56, 97, 96, 98, nil,
- 99, 107, 108, nil, 91, 92, nil, 42, 43, 41,
- 238, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 252, 253, nil, 40,
- nil, nil, 33, nil, nil, 58, 59, nil, nil, 60,
- nil, 35, 235, nil, 241, 44, 237, 236, nil, 233,
- 234, nil, nil, 239, 20, 240, nil, nil, nil, 89,
- 79, 82, 83, nil, 84, 86, 85, 87, nil, nil,
- nil, nil, 80, 88, nil, nil, nil, nil, nil, nil,
- 62, nil, 81, 93, 94, 290, 69, 70, 71, 9,
- 57, nil, nil, nil, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 30, 31, 72, 73, nil, nil,
- nil, nil, nil, 29, 28, 27, 101, 100, 102, 103,
- nil, nil, 19, nil, nil, nil, nil, nil, 8, 45,
- 292, 10, 105, 104, 106, 95, 56, 97, 96, 98,
- nil, 99, 107, 108, nil, 91, 92, nil, 42, 43,
- 41, 238, -597, -597, -597, -597, 244, 246, nil, nil,
- -597, -597, nil, nil, nil, nil, nil, 252, 253, nil,
- 40, nil, nil, 33, nil, nil, 58, 59, nil, nil,
- 60, nil, 35, 235, nil, 241, 44, 237, 236, nil,
- 233, 234, 245, 243, 239, 20, 240, nil, nil, nil,
- 89, 79, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, nil, nil, nil, nil,
- nil, 62, nil, 81, 93, 94, 290, 69, 70, 71,
- 9, 57, nil, nil, nil, 63, 64, nil, nil, nil,
- 67, nil, 65, 66, 68, 30, 31, 72, 73, nil,
- nil, nil, nil, nil, 29, 28, 27, 101, 100, 102,
- 103, nil, nil, 19, nil, nil, nil, nil, nil, 8,
- 45, 292, 10, 105, 104, 106, 95, 56, 97, 96,
- 98, nil, 99, 107, 108, nil, 91, 92, nil, 42,
- 43, 41, 238, -597, -597, -597, -597, 244, 246, nil,
- nil, -597, -597, nil, nil, nil, nil, nil, 252, 253,
- nil, 40, nil, nil, 33, nil, nil, 58, 59, nil,
- nil, 60, nil, 35, 235, nil, 241, 44, 237, 236,
- nil, 233, 234, 245, 243, 239, 20, 240, nil, nil,
- nil, 89, 79, 82, 83, nil, 84, 86, 85, 87,
- nil, nil, nil, nil, 80, 88, nil, nil, nil, nil,
- nil, nil, 62, nil, 81, 93, 94, 290, 69, 70,
- 71, 9, 57, nil, nil, nil, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 30, 31, 72, 73,
- nil, nil, nil, nil, nil, 29, 28, 27, 101, 100,
- 102, 103, nil, nil, 19, nil, nil, nil, nil, nil,
- 8, 45, 292, 10, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, 238, -597, -597, -597, -597, 244, 246,
- nil, nil, -597, -597, nil, nil, nil, nil, nil, 252,
- 253, nil, 40, nil, nil, 33, nil, nil, 58, 59,
- nil, nil, 60, nil, 35, 235, nil, 241, 44, 237,
- 236, nil, 233, 234, 245, 243, 239, 20, 240, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- nil, nil, nil, 62, nil, 81, 93, 94, 290, 69,
- 70, 71, 9, 57, nil, nil, nil, 63, 64, nil,
- nil, nil, 67, nil, 65, 66, 68, 30, 31, 72,
- 73, nil, nil, nil, nil, nil, 29, 28, 27, 101,
- 100, 102, 103, nil, nil, 19, nil, nil, nil, nil,
- nil, 8, 45, 292, 10, 105, 104, 106, 95, 56,
- 97, 96, 98, nil, 99, 107, 108, nil, 91, 92,
- nil, 42, 43, 41, 238, -597, -597, -597, -597, 244,
- 246, nil, nil, -597, -597, nil, nil, nil, nil, nil,
- 252, 253, nil, 40, nil, nil, 33, nil, nil, 58,
- 59, nil, nil, 60, nil, 35, 235, nil, 241, 44,
- 237, 236, nil, 233, 234, 245, 243, 239, 20, 240,
- nil, nil, nil, 89, 79, 82, 83, nil, 84, 86,
- 85, 87, nil, nil, nil, nil, 80, 88, nil, nil,
- nil, nil, nil, nil, 62, nil, 81, 93, 94, 290,
- 69, 70, 71, 9, 57, nil, nil, nil, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 30, 31,
- 72, 73, nil, nil, nil, nil, nil, 29, 28, 27,
- 101, 100, 102, 103, nil, nil, 19, nil, nil, nil,
- nil, nil, 8, 45, 292, 10, 105, 104, 106, 95,
- 56, 97, 96, 98, nil, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, 238, -597, -597, -597, -597,
- 244, 246, nil, nil, -597, -597, nil, nil, nil, nil,
- nil, 252, 253, nil, 40, nil, nil, 33, nil, nil,
- 58, 59, nil, nil, 60, nil, 35, 235, nil, 241,
- 44, 237, 236, nil, 233, 234, 245, 243, 239, 20,
- 240, nil, nil, nil, 89, 79, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, nil, nil, nil, 62, nil, 81, 93, 94,
- 290, 69, 70, 71, 9, 57, nil, nil, nil, 63,
- 64, nil, nil, nil, 67, nil, 65, 66, 68, 30,
- 31, 72, 73, nil, nil, nil, nil, nil, 29, 28,
- 27, 101, 100, 102, 103, nil, nil, 19, nil, nil,
- nil, nil, nil, 8, 45, 292, 10, 105, 104, 106,
- 95, 56, 97, 96, 98, nil, 99, 107, 108, nil,
- 91, 92, nil, 42, 43, 41, 238, 242, 247, 248,
- 249, 244, 246, nil, nil, 250, 251, nil, nil, nil,
- nil, nil, 252, 253, nil, 40, nil, nil, 33, nil,
- nil, 58, 59, nil, nil, 60, nil, 35, 235, nil,
- 241, 44, 237, 236, nil, 233, 234, 245, 243, 239,
- 20, 240, nil, nil, nil, 89, 79, 82, 83, nil,
- 84, 86, 85, 87, nil, nil, nil, nil, 80, 88,
- nil, nil, nil, nil, nil, nil, 62, nil, 81, 93,
- 94, 290, 69, 70, 71, 9, 57, nil, nil, nil,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 30, 31, 72, 73, nil, nil, nil, nil, nil, 29,
- 28, 27, 101, 100, 102, 103, nil, nil, 19, nil,
- nil, nil, nil, nil, 8, 45, 292, 10, 105, 104,
- 106, 95, 56, 97, 96, 98, nil, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, 238, 242, 247,
- 248, 249, 244, 246, 254, nil, 250, 251, nil, nil,
- nil, nil, nil, 252, 253, nil, 40, nil, nil, 33,
- nil, nil, 58, 59, nil, nil, 60, nil, 35, 235,
- nil, 241, 44, 237, 236, nil, 233, 234, 245, 243,
- 239, 20, 240, nil, nil, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, nil, nil, nil, 62, nil, 81,
- 93, 94, 69, 70, 71, 9, 57, nil, nil, nil,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 30, 31, 72, 73, nil, nil, nil, nil, nil, 29,
- 28, 27, 101, 100, 102, 103, nil, nil, 19, nil,
- nil, nil, nil, nil, 8, 45, 7, 10, 105, 104,
- 106, 95, 56, 97, 96, 98, nil, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, 238, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 252, 253, nil, 40, nil, nil, 33,
- nil, nil, 58, 59, nil, nil, 60, nil, 35, 235,
- nil, 241, 44, 237, 236, nil, 233, 234, nil, nil,
- nil, 20, nil, nil, nil, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, 69, 70, 71, 62, 57, 81,
- 93, 94, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 30, 31, 72, 73, nil, nil, nil, nil,
- nil, 29, 28, 27, 101, 100, 102, 103, nil, nil,
- 230, nil, nil, nil, nil, nil, nil, 45, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, 238,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 252, 253, nil, 223, nil,
- nil, 229, nil, nil, 58, 59, nil, nil, 60, nil,
- nil, 235, nil, 241, 44, 237, 236, nil, 233, 234,
- nil, nil, nil, 228, nil, nil, nil, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 69, 70, 71, 62,
- 57, 81, 93, 94, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 30, 31, 72, 73, nil, nil,
- nil, nil, nil, 29, 28, 27, 101, 100, 102, 103,
- nil, nil, 230, nil, nil, nil, nil, nil, nil, 45,
- nil, nil, 105, 104, 106, 95, 56, 97, 96, 98,
- 284, 99, 107, 108, nil, 91, 92, nil, 42, 43,
- 41, 238, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 252, 253, nil,
- 223, nil, nil, 229, nil, nil, 58, 59, nil, nil,
- 60, nil, 281, 235, 279, nil, 44, 237, 236, 285,
- 233, 234, nil, nil, nil, 228, nil, nil, nil, nil,
- 89, 282, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, nil, nil, 69, 70,
- 71, 62, 57, 81, 93, 94, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 30, 31, 72, 73,
- nil, nil, nil, nil, nil, 29, 28, 27, 101, 100,
- 102, 103, nil, nil, 230, nil, nil, nil, nil, nil,
- nil, 45, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, 284, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 223, nil, nil, 229, nil, nil, 58, 59,
- nil, nil, 60, nil, 281, nil, 279, nil, 44, nil,
- nil, 285, nil, nil, nil, nil, nil, 228, nil, nil,
- nil, nil, 89, 282, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- 69, 70, 71, 62, 57, 81, 93, 94, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 30, 31,
- 72, 73, nil, nil, nil, nil, nil, 29, 28, 27,
- 101, 100, 102, 103, nil, nil, 230, nil, nil, nil,
- nil, nil, nil, 45, nil, nil, 105, 104, 106, 95,
- 56, 97, 96, 98, 284, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 223, nil, nil, 229, nil, nil,
- 58, 59, nil, nil, 60, nil, 281, nil, 279, nil,
- 44, nil, nil, 285, nil, nil, nil, nil, nil, 228,
- nil, nil, nil, nil, 89, 282, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, 69, 70, 71, 62, 57, 81, 93, 94,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 309, 310, 72, 73, nil, nil, nil, nil, nil, 305,
- 306, 312, 101, 100, 102, 103, nil, nil, 230, nil,
- nil, nil, nil, nil, nil, 307, nil, nil, 105, 104,
- 106, 95, 56, 97, 96, 98, nil, 99, 107, 108,
- nil, 91, 92, nil, nil, nil, 313, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 303, nil, nil, 299,
- nil, nil, 58, 59, nil, nil, 60, nil, 298, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, 69, 70, 71, 62, 57, 81,
- 93, 94, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 309, 310, 72, 73, nil, nil, nil, nil,
- nil, 305, 306, 312, 101, 100, 102, 103, nil, nil,
- 230, nil, nil, nil, nil, nil, nil, 307, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, nil, nil, 313, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 303, nil,
- nil, 229, nil, nil, 58, 59, nil, nil, 60, nil,
- 548, nil, 545, 544, 543, 553, 546, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 556, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 315, nil, 551, 62,
- nil, 81, 93, 94, 69, 70, 71, nil, 57, 564,
- 563, nil, 63, 64, 557, nil, nil, 67, nil, 65,
- 66, 68, 309, 310, 72, 73, nil, nil, nil, nil,
- nil, 305, 306, 312, 101, 100, 102, 103, nil, nil,
- 230, nil, nil, nil, nil, nil, nil, 45, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 223, nil,
- nil, 229, nil, nil, 58, 59, nil, nil, 60, nil,
- nil, nil, nil, nil, 44, nil, nil, nil, nil, nil,
- nil, nil, nil, 228, nil, nil, nil, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 69, 70, 71, 62,
- 57, 81, 93, 94, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 309, 310, 72, 73, nil, nil,
- nil, nil, nil, 305, 306, 312, 101, 100, 102, 103,
- nil, nil, 230, nil, nil, nil, nil, nil, nil, 45,
- nil, nil, 105, 104, 106, 95, 56, 97, 96, 98,
- nil, 99, 107, 108, nil, 91, 92, nil, 42, 43,
- 41, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 223, nil, nil, 229, nil, nil, 58, 59, nil, nil,
- 60, nil, nil, nil, nil, nil, 44, nil, nil, nil,
- nil, nil, nil, nil, nil, 228, nil, nil, nil, nil,
- 89, 79, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, nil, nil, 69, 70,
- 71, 62, 57, 81, 93, 94, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 309, 310, 72, 73,
- nil, nil, nil, nil, nil, 305, 306, 312, 101, 100,
- 102, 103, nil, nil, 230, nil, nil, nil, nil, nil,
- nil, 45, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 223, nil, nil, 229, nil, nil, 58, 59,
- nil, nil, 60, nil, nil, nil, nil, nil, 44, nil,
- nil, nil, nil, nil, nil, nil, nil, 228, nil, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- 69, 70, 71, 62, 57, 81, 93, 94, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 309, 310,
- 72, 73, nil, nil, nil, nil, nil, 305, 306, 312,
- 101, 100, 102, 103, nil, nil, 230, nil, nil, nil,
- nil, nil, nil, 45, nil, nil, 105, 104, 106, 95,
- 56, 97, 96, 98, 284, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 223, nil, nil, 229, nil, nil,
- 58, 59, nil, nil, 60, nil, 281, nil, nil, nil,
- 44, nil, nil, 285, nil, nil, nil, nil, nil, 228,
- nil, nil, nil, nil, 89, 282, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, 69, 70, 71, 62, 57, 81, 93, 94,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 309, 310, 72, 73, nil, nil, nil, nil, nil, 305,
- 306, 312, 101, 100, 102, 103, nil, nil, 230, nil,
- nil, nil, nil, nil, nil, 45, nil, nil, 105, 104,
- 106, 95, 56, 97, 96, 98, 284, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 223, nil, nil, 229,
- nil, nil, 58, 59, nil, nil, 60, nil, nil, nil,
- nil, nil, 44, nil, nil, 285, nil, nil, nil, nil,
- nil, 228, nil, nil, nil, nil, 89, 282, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, 69, 70, 71, 62, 57, 81,
- 93, 94, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 30, 31, 72, 73, nil, nil, nil, nil,
- nil, 29, 28, 27, 101, 100, 102, 103, nil, nil,
- 19, nil, nil, nil, nil, nil, nil, 45, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 223, nil,
- nil, 229, nil, nil, 58, 59, nil, nil, 60, nil,
- nil, nil, nil, nil, 44, nil, nil, nil, nil, nil,
- nil, nil, nil, 20, nil, nil, nil, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 69, 70, 71, 62,
- 57, 81, 93, 94, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 30, 31, 72, 73, nil, nil,
- nil, nil, nil, 29, 28, 27, 101, 100, 102, 103,
- nil, nil, 19, nil, nil, nil, nil, nil, nil, 45,
- nil, nil, 105, 104, 106, 95, 56, 97, 96, 98,
- nil, 99, 107, 108, nil, 91, 92, nil, 42, 43,
- 41, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 223, nil, nil, 229, nil, nil, 58, 59, nil, nil,
- 60, nil, nil, nil, nil, nil, 44, nil, nil, nil,
- nil, nil, nil, nil, nil, 20, nil, nil, nil, nil,
- 89, 79, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, nil, nil, 69, 70,
- 71, 62, 57, 81, 93, 94, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 30, 31, 72, 73,
- nil, nil, nil, nil, nil, 29, 28, 27, 101, 100,
- 102, 103, nil, nil, 19, nil, nil, nil, nil, nil,
- nil, 45, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 223, nil, nil, 229, nil, nil, 58, 59,
- nil, nil, 60, nil, nil, nil, nil, nil, 44, nil,
- nil, nil, nil, nil, nil, nil, nil, 20, nil, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, 113, nil, nil,
- nil, nil, 112, 62, nil, 81, 93, 94, 69, 70,
- 71, nil, 57, nil, nil, nil, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 309, 310, 72, 73,
- nil, nil, nil, nil, nil, 305, 306, 312, 101, 100,
- 102, 103, nil, nil, 230, nil, nil, nil, nil, nil,
- nil, 307, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- nil, nil, 313, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 348, nil, nil, 33, nil, nil, 58, 59,
- nil, nil, 60, nil, 35, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- 69, 70, 71, 62, 57, 81, 93, 94, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 309, 310,
- 72, 73, nil, nil, nil, nil, nil, 305, 306, 312,
- 101, 100, 102, 103, nil, nil, 230, nil, nil, nil,
- nil, nil, nil, 307, nil, nil, 105, 104, 106, 353,
- 56, 97, 96, 354, nil, 99, 107, 108, nil, 91,
- 92, nil, nil, nil, 313, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 360, nil, nil, 355, nil, nil, 229, nil, nil,
- 58, 59, nil, nil, 60, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 89, 79, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, 69, 70, 71, 62, 57, 81, 93, 94,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 309, 310, 72, 73, nil, nil, nil, nil, nil, 305,
- 306, 312, 101, 100, 102, 103, nil, nil, 230, nil,
- nil, nil, nil, nil, nil, 307, nil, nil, 105, 104,
- 106, 353, 56, 97, 96, 354, nil, 99, 107, 108,
- nil, 91, 92, nil, nil, nil, 313, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 355, nil, nil, 229,
- nil, nil, 58, 59, nil, nil, 60, nil, 548, nil,
- 545, 544, 543, 553, 546, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 556, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, nil, nil, 551, 62, nil, 81,
- 93, 94, 69, 70, 71, 9, 57, 564, 563, nil,
- 63, 64, 557, nil, nil, 67, nil, 65, 66, 68,
- 30, 31, 72, 73, nil, nil, nil, nil, nil, 29,
- 28, 27, 101, 100, 102, 103, nil, nil, 19, nil,
- nil, nil, nil, nil, 8, 45, 7, 10, 105, 104,
- 106, 95, 56, 97, 96, 98, nil, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 40, nil, nil, 33,
- nil, nil, 58, 59, nil, nil, 60, nil, 35, nil,
- nil, nil, 44, nil, nil, nil, nil, nil, nil, nil,
- nil, 20, nil, nil, nil, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, nil, nil, 388, 62, nil, 81,
- 93, 94, 69, 70, 71, nil, 57, nil, nil, nil,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 30, 31, 72, 73, nil, nil, nil, nil, nil, 29,
- 28, 27, 101, 100, 102, 103, nil, nil, 19, nil,
- nil, nil, nil, nil, nil, 45, nil, nil, 105, 104,
- 106, 95, 56, 97, 96, 98, nil, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 223, nil, nil, 229,
- nil, nil, 58, 59, nil, nil, 60, nil, nil, nil,
- nil, nil, 44, nil, nil, nil, nil, nil, nil, nil,
- nil, 20, nil, nil, nil, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, 69, 70, 71, 62, 57, 81,
- 93, 94, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 30, 31, 72, 73, nil, nil, nil, nil,
- nil, 29, 28, 27, 101, 100, 102, 103, nil, nil,
- 19, nil, nil, nil, nil, nil, nil, 45, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 223, nil,
- nil, 229, nil, nil, 58, 59, nil, nil, 60, nil,
- nil, nil, nil, nil, 44, nil, nil, nil, nil, nil,
- nil, nil, nil, 20, nil, nil, nil, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 69, 70, 71, 62,
- 57, 81, 93, 94, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 30, 31, 72, 73, nil, nil,
- nil, nil, nil, 29, 28, 27, 101, 100, 102, 103,
- nil, nil, 19, nil, nil, nil, nil, nil, nil, 45,
- nil, nil, 105, 104, 106, 95, 56, 97, 96, 98,
- nil, 99, 107, 108, nil, 91, 92, nil, 42, 43,
- 41, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 223, nil, nil, 229, nil, nil, 58, 59, nil, nil,
- 60, nil, nil, nil, nil, nil, 44, nil, nil, nil,
- nil, nil, nil, nil, nil, 20, nil, nil, nil, nil,
- 89, 79, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, nil, nil, 69, 70,
- 71, 62, 57, 81, 93, 94, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 30, 31, 72, 73,
- nil, nil, nil, nil, nil, 29, 28, 27, 101, 100,
- 102, 103, nil, nil, 19, nil, nil, nil, nil, nil,
- nil, 45, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 223, nil, nil, 229, nil, nil, 58, 59,
- nil, nil, 60, nil, nil, nil, nil, nil, 44, nil,
- nil, nil, nil, nil, nil, nil, nil, 20, nil, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- nil, nil, nil, 62, nil, 81, 93, 94, 69, 70,
- 71, 9, 57, nil, nil, nil, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 30, 31, 72, 73,
- nil, nil, nil, nil, nil, 29, 28, 27, 101, 100,
- 102, 103, nil, nil, 19, nil, nil, nil, nil, nil,
- 8, 45, nil, 10, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 40, nil, nil, 33, nil, nil, 58, 59,
- nil, nil, 60, nil, 35, nil, nil, nil, 44, nil,
- nil, nil, nil, nil, nil, nil, nil, 20, nil, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- 69, 70, 71, 62, 57, 81, 93, 94, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 30, 31,
- 72, 73, nil, nil, nil, nil, nil, 29, 28, 27,
- 101, 100, 102, 103, nil, nil, 230, nil, nil, nil,
- nil, nil, nil, 45, nil, nil, 105, 104, 106, 95,
- 56, 97, 96, 98, nil, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 223, nil, nil, 229, nil, nil,
- 58, 59, nil, nil, 60, nil, 404, nil, nil, nil,
- 44, nil, nil, nil, nil, nil, nil, nil, nil, 228,
- nil, nil, nil, nil, 89, 79, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, 69, 70, 71, 62, 57, 81, 93, 94,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 30, 31, 72, 73, nil, nil, nil, nil, nil, 29,
- 28, 27, 101, 100, 102, 103, nil, nil, 230, nil,
- nil, nil, nil, nil, nil, 45, nil, nil, 105, 104,
- 106, 95, 56, 97, 96, 98, nil, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 223, nil, nil, 229,
- nil, nil, 58, 59, nil, nil, 60, nil, nil, nil,
- nil, nil, 44, nil, nil, nil, nil, nil, nil, nil,
- nil, 228, nil, nil, nil, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, 69, 70, 71, 62, 57, 81,
- 93, 94, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 30, 31, 72, 73, nil, nil, nil, nil,
- nil, 29, 28, 27, 101, 100, 102, 103, nil, nil,
- 230, nil, nil, nil, nil, nil, nil, 45, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, 284, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 223, nil,
- nil, 229, nil, nil, 58, 59, nil, nil, 60, nil,
- 281, nil, 279, nil, 44, nil, nil, 285, nil, nil,
- nil, nil, nil, 228, nil, nil, nil, nil, 89, 282,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 69, 70, 71, 62,
- 57, 81, 93, 94, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 30, 31, 72, 73, nil, nil,
- nil, nil, nil, 29, 28, 27, 101, 100, 102, 103,
- nil, nil, 230, nil, nil, nil, nil, nil, nil, 45,
- nil, nil, 105, 104, 106, 95, 56, 97, 96, 98,
- nil, 99, 107, 108, nil, 91, 92, nil, 42, 43,
- 41, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 223, nil, nil, 229, nil, nil, 58, 59, nil, nil,
- 60, nil, nil, nil, nil, nil, 44, nil, nil, nil,
- nil, nil, nil, nil, nil, 228, nil, nil, nil, nil,
- 89, 79, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, nil, nil, 69, 70,
- 71, 62, 57, 81, 93, 94, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 30, 31, 72, 73,
- nil, nil, nil, nil, nil, 29, 28, 27, 101, 100,
- 102, 103, nil, nil, 230, nil, nil, nil, nil, nil,
- nil, 45, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 223, nil, nil, 229, nil, nil, 58, 59,
- nil, nil, 60, nil, 404, nil, nil, nil, 44, nil,
- nil, nil, nil, nil, nil, nil, nil, 228, nil, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- 69, 70, 71, 62, 57, 81, 93, 94, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 30, 31,
- 72, 73, nil, nil, nil, nil, nil, 29, 28, 27,
- 101, 100, 102, 103, nil, nil, 19, nil, nil, nil,
- nil, nil, nil, 45, nil, nil, 105, 104, 106, 95,
- 56, 97, 96, 98, nil, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 223, nil, nil, 229, nil, nil,
- 58, 59, nil, nil, 60, nil, nil, nil, nil, nil,
- 44, nil, nil, nil, nil, nil, nil, nil, nil, 20,
- nil, nil, nil, nil, 89, 79, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, 69, 70, 71, 62, 57, 81, 93, 94,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 30, 31, 72, 73, nil, nil, nil, nil, nil, 29,
- 28, 27, 101, 100, 102, 103, nil, nil, 19, nil,
- nil, nil, nil, nil, nil, 45, nil, nil, 105, 104,
- 106, 95, 56, 97, 96, 98, nil, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 223, nil, nil, 229,
- nil, nil, 58, 59, nil, nil, 60, nil, nil, nil,
- nil, nil, 44, nil, nil, nil, nil, nil, nil, nil,
- nil, 20, nil, nil, nil, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, 69, 70, 71, 62, 57, 81,
- 93, 94, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 30, 31, 72, 73, nil, nil, nil, nil,
- nil, 29, 28, 27, 101, 100, 102, 103, nil, nil,
- 19, nil, nil, nil, nil, nil, nil, 45, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 223, nil,
- nil, 229, nil, nil, 58, 59, nil, nil, 60, nil,
- nil, nil, nil, nil, 44, nil, nil, nil, nil, nil,
- nil, nil, nil, 20, nil, nil, nil, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 69, 70, 71, 62,
- 57, 81, 93, 94, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 30, 31, 72, 73, nil, nil,
- nil, nil, nil, 29, 28, 27, 101, 100, 102, 103,
- nil, nil, 19, nil, nil, nil, nil, nil, nil, 45,
- nil, nil, 105, 104, 106, 95, 56, 97, 96, 98,
- nil, 99, 107, 108, nil, 91, 92, nil, 42, 43,
- 41, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 223, nil, nil, 229, nil, nil, 58, 59, nil, nil,
- 60, nil, nil, nil, nil, nil, 44, nil, nil, nil,
- nil, nil, nil, nil, nil, 20, nil, nil, nil, nil,
- 89, 79, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, 218, nil, nil, 69, 70,
- 71, 62, 57, 81, 93, 94, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 309, 310, 72, 73,
- nil, nil, nil, nil, nil, 305, 306, 312, 101, 100,
- 102, 103, nil, nil, 230, nil, nil, nil, nil, nil,
- nil, 45, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 223, nil, nil, 229, nil, nil, 58, 59,
- nil, nil, 60, nil, nil, nil, nil, nil, 44, nil,
- nil, nil, nil, nil, nil, nil, nil, 228, nil, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- 69, 70, 71, 62, 57, 81, 93, 94, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 309, 310,
- 72, 73, nil, nil, nil, nil, nil, 305, 306, 312,
- 101, 100, 102, 103, nil, nil, 230, nil, nil, nil,
- nil, nil, nil, 45, nil, nil, 105, 104, 106, 95,
- 56, 97, 96, 98, nil, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 223, nil, nil, 229, nil, nil,
- 58, 59, nil, nil, 60, nil, nil, nil, nil, nil,
- 44, nil, nil, nil, nil, nil, nil, nil, nil, 228,
- nil, nil, nil, nil, 89, 79, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, 69, 70, 71, 62, 57, 81, 93, 94,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 309, 310, 72, 73, nil, nil, nil, nil, nil, 305,
- 306, 312, 101, 100, 102, 103, nil, nil, 230, nil,
- nil, nil, nil, nil, nil, 45, nil, nil, 105, 104,
- 106, 95, 56, 97, 96, 98, nil, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 223, nil, nil, 229,
- nil, nil, 58, 59, nil, nil, 60, nil, nil, nil,
- nil, nil, 44, nil, nil, nil, nil, nil, nil, nil,
- nil, 228, nil, nil, nil, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, 69, 70, 71, 62, 57, 81,
- 93, 94, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 309, 310, 72, 73, nil, nil, nil, nil,
- nil, 305, 306, 312, 101, 100, 102, 103, nil, nil,
- 230, nil, nil, nil, nil, nil, nil, 45, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 223, nil,
- nil, 229, nil, nil, 58, 59, nil, nil, 60, nil,
- nil, nil, nil, nil, 44, nil, nil, nil, nil, nil,
- nil, nil, nil, 228, nil, nil, nil, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 69, 70, 71, 62,
- 57, 81, 93, 94, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 309, 310, 72, 73, nil, nil,
- nil, nil, nil, 305, 306, 312, 101, 100, 102, 103,
- nil, nil, 230, nil, nil, nil, nil, nil, nil, 45,
- nil, nil, 105, 104, 106, 95, 56, 97, 96, 98,
- nil, 99, 107, 108, nil, 91, 92, nil, 42, 43,
- 41, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 223, nil, nil, 229, nil, nil, 58, 59, nil, nil,
- 60, nil, nil, nil, nil, nil, 44, nil, nil, nil,
- nil, nil, nil, nil, nil, 228, nil, nil, nil, nil,
- 89, 79, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, nil, nil, 69, 70,
- 71, 62, 57, 81, 93, 94, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 309, 310, 72, 73,
- nil, nil, nil, nil, nil, 305, 306, 312, 101, 100,
- 102, 103, nil, nil, 230, nil, nil, nil, nil, nil,
- nil, 45, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 223, nil, nil, 229, nil, nil, 58, 59,
- nil, nil, 60, nil, nil, nil, nil, nil, 44, nil,
- nil, nil, nil, nil, nil, nil, nil, 228, nil, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- 69, 70, 71, 62, 57, 81, 93, 94, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 309, 310,
- 72, 73, nil, nil, nil, nil, nil, 305, 306, 312,
- 101, 100, 102, 103, nil, nil, 230, nil, nil, nil,
- nil, nil, nil, 45, nil, nil, 105, 104, 106, 95,
- 56, 97, 96, 98, nil, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 223, nil, nil, 229, nil, nil,
- 58, 59, nil, nil, 60, nil, nil, nil, nil, nil,
- 44, nil, nil, nil, nil, nil, nil, nil, nil, 228,
- nil, nil, nil, nil, 89, 79, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, 69, 70, 71, 62, 57, 81, 93, 94,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 309, 310, 72, 73, nil, nil, nil, nil, nil, 305,
- 306, 312, 101, 100, 102, 103, nil, nil, 230, nil,
- nil, nil, nil, nil, nil, 45, nil, nil, 105, 104,
- 106, 95, 56, 97, 96, 98, nil, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 223, nil, nil, 229,
- nil, nil, 58, 59, nil, nil, 60, nil, nil, nil,
- nil, nil, 44, nil, nil, nil, nil, nil, nil, nil,
- nil, 228, nil, nil, nil, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, 69, 70, 71, 62, 57, 81,
- 93, 94, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 309, 310, 72, 73, nil, nil, nil, nil,
- nil, 305, 306, 312, 101, 100, 102, 103, nil, nil,
- 230, nil, nil, nil, nil, nil, nil, 45, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 223, nil,
- nil, 229, nil, nil, 58, 59, nil, nil, 60, nil,
- nil, nil, nil, nil, 44, nil, nil, nil, nil, nil,
- nil, nil, nil, 228, nil, nil, nil, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 69, 70, 71, 62,
- 57, 81, 93, 94, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 309, 310, 72, 73, nil, nil,
- nil, nil, nil, 305, 306, 312, 101, 100, 102, 103,
- nil, nil, 230, nil, nil, nil, nil, nil, nil, 45,
- nil, nil, 105, 104, 106, 95, 56, 97, 96, 98,
- nil, 99, 107, 108, nil, 91, 92, nil, 42, 43,
- 41, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 223, nil, nil, 229, nil, nil, 58, 59, nil, nil,
- 60, nil, nil, nil, nil, nil, 44, nil, nil, nil,
- nil, nil, nil, nil, nil, 228, nil, nil, nil, nil,
- 89, 79, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, nil, nil, 69, 70,
- 71, 62, 57, 81, 93, 94, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 309, 310, 72, 73,
- nil, nil, nil, nil, nil, 305, 306, 312, 101, 100,
- 102, 103, nil, nil, 230, nil, nil, nil, nil, nil,
- nil, 45, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 223, nil, nil, 229, nil, nil, 58, 59,
- nil, nil, 60, nil, nil, nil, nil, nil, 44, nil,
- nil, nil, nil, nil, nil, nil, nil, 228, nil, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- 69, 70, 71, 62, 57, 81, 93, 94, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 309, 310,
- 72, 73, nil, nil, nil, nil, nil, 305, 306, 312,
- 101, 100, 102, 103, nil, nil, 230, nil, nil, nil,
- nil, nil, nil, 45, nil, nil, 105, 104, 106, 95,
- 56, 97, 96, 98, nil, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 223, nil, nil, 229, nil, nil,
- 58, 59, nil, nil, 60, nil, nil, nil, nil, nil,
- 44, nil, nil, nil, nil, nil, nil, nil, nil, 228,
- nil, nil, nil, nil, 89, 79, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, 69, 70, 71, 62, 57, 81, 93, 94,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 309, 310, 72, 73, nil, nil, nil, nil, nil, 305,
- 306, 312, 101, 100, 102, 103, nil, nil, 230, nil,
- nil, nil, nil, nil, nil, 45, nil, nil, 105, 104,
- 106, 95, 56, 97, 96, 98, nil, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 223, nil, nil, 229,
- nil, nil, 58, 59, nil, nil, 60, nil, nil, nil,
- nil, nil, 44, nil, nil, nil, nil, nil, nil, nil,
- nil, 228, nil, nil, nil, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, 69, 70, 71, 62, 57, 81,
- 93, 94, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 309, 310, 72, 73, nil, nil, nil, nil,
- nil, 305, 306, 312, 101, 100, 102, 103, nil, nil,
- 230, nil, nil, nil, nil, nil, nil, 45, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 223, nil,
- nil, 229, nil, nil, 58, 59, nil, nil, 60, nil,
- nil, nil, nil, nil, 44, nil, nil, nil, nil, nil,
- nil, nil, nil, 228, nil, nil, nil, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 69, 70, 71, 62,
- 57, 81, 93, 94, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 309, 310, 72, 73, nil, nil,
- nil, nil, nil, 305, 306, 312, 101, 100, 102, 103,
- nil, nil, 230, nil, nil, nil, nil, nil, nil, 45,
- nil, nil, 105, 104, 106, 95, 56, 97, 96, 98,
- nil, 99, 107, 108, nil, 91, 92, nil, 42, 43,
- 41, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 223, nil, nil, 229, nil, nil, 58, 59, nil, nil,
- 60, nil, nil, nil, nil, nil, 44, nil, nil, nil,
- nil, nil, nil, nil, nil, 228, nil, nil, nil, nil,
- 89, 79, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, nil, nil, 69, 70,
- 71, 62, 57, 81, 93, 94, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 309, 310, 72, 73,
- nil, nil, nil, nil, nil, 305, 306, 312, 101, 100,
- 102, 103, nil, nil, 230, nil, nil, nil, nil, nil,
- nil, 45, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 223, nil, nil, 229, nil, nil, 58, 59,
- nil, nil, 60, nil, nil, nil, nil, nil, 44, nil,
- nil, nil, nil, nil, nil, nil, nil, 228, nil, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- 69, 70, 71, 62, 57, 81, 93, 94, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 309, 310,
- 72, 73, nil, nil, nil, nil, nil, 305, 306, 312,
- 101, 100, 102, 103, nil, nil, 230, nil, nil, nil,
- nil, nil, nil, 45, nil, nil, 105, 104, 106, 95,
- 56, 97, 96, 98, nil, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 223, nil, nil, 229, nil, nil,
- 58, 59, nil, nil, 60, nil, nil, nil, nil, nil,
- 44, nil, nil, nil, nil, nil, nil, nil, nil, 228,
- nil, nil, nil, nil, 89, 79, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, 69, 70, 71, 62, 57, 81, 93, 94,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 309, 310, 72, 73, nil, nil, nil, nil, nil, 305,
- 306, 312, 101, 100, 102, 103, nil, nil, 230, nil,
- nil, nil, nil, nil, nil, 45, nil, nil, 105, 104,
- 106, 95, 56, 97, 96, 98, nil, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 223, nil, nil, 229,
- nil, nil, 58, 59, nil, nil, 60, nil, nil, nil,
- nil, nil, 44, nil, nil, nil, nil, nil, nil, nil,
- nil, 228, nil, nil, nil, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, 69, 70, 71, 62, 57, 81,
- 93, 94, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 309, 310, 72, 73, nil, nil, nil, nil,
- nil, 305, 306, 312, 101, 100, 102, 103, nil, nil,
- 230, nil, nil, nil, nil, nil, nil, 45, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 223, nil,
- nil, 229, nil, nil, 58, 59, nil, nil, 60, nil,
- nil, nil, nil, nil, 44, nil, nil, nil, nil, nil,
- nil, nil, nil, 228, nil, nil, nil, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 69, 70, 71, 62,
- 57, 81, 93, 94, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 309, 310, 72, 73, nil, nil,
- nil, nil, nil, 305, 306, 312, 101, 100, 102, 103,
- nil, nil, 230, nil, nil, nil, nil, nil, nil, 45,
- nil, nil, 105, 104, 106, 95, 56, 97, 96, 98,
- nil, 99, 107, 108, nil, 91, 92, nil, 42, 43,
- 41, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 223, nil, nil, 229, nil, nil, 58, 59, nil, nil,
- 60, nil, nil, nil, nil, nil, 44, nil, nil, nil,
- nil, nil, nil, nil, nil, 228, nil, nil, nil, nil,
- 89, 79, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, nil, nil, 69, 70,
- 71, 62, 57, 81, 93, 94, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 309, 310, 72, 73,
- nil, nil, nil, nil, nil, 305, 306, 312, 101, 100,
- 102, 103, nil, nil, 230, nil, nil, nil, nil, nil,
- nil, 45, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 223, nil, nil, 229, nil, nil, 58, 59,
- nil, nil, 60, nil, nil, nil, nil, nil, 44, nil,
- nil, nil, nil, nil, nil, nil, nil, 228, nil, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- 69, 70, 71, 62, 57, 81, 93, 94, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 309, 310,
- 72, 73, nil, nil, nil, nil, nil, 305, 306, 312,
- 101, 100, 102, 103, nil, nil, 230, nil, nil, nil,
- nil, nil, nil, 45, nil, nil, 105, 104, 106, 95,
- 56, 97, 96, 98, nil, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 223, nil, nil, 229, nil, nil,
- 58, 59, nil, nil, 60, nil, nil, nil, nil, nil,
- 44, nil, nil, nil, nil, nil, nil, nil, nil, 228,
- nil, nil, nil, nil, 89, 79, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, 69, 70, 71, 62, 57, 81, 93, 94,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 309, 310, 72, 73, nil, nil, nil, nil, nil, 305,
- 306, 312, 101, 100, 102, 103, nil, nil, 230, nil,
- nil, nil, nil, nil, nil, 45, nil, nil, 105, 104,
- 106, 95, 56, 97, 96, 98, nil, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 223, nil, nil, 229,
- nil, nil, 58, 59, nil, nil, 60, nil, nil, nil,
- nil, nil, 44, nil, nil, nil, nil, nil, nil, nil,
- nil, 228, nil, nil, nil, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, 69, 70, 71, 62, 57, 81,
- 93, 94, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 309, 310, 72, 73, nil, nil, nil, nil,
- nil, 305, 306, 312, 101, 100, 102, 103, nil, nil,
- 230, nil, nil, nil, nil, nil, nil, 45, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 223, nil,
- nil, 229, nil, nil, 58, 59, nil, nil, 60, nil,
- nil, nil, nil, nil, 44, nil, nil, nil, nil, nil,
- nil, nil, nil, 228, nil, nil, nil, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 69, 70, 71, 62,
- 57, 81, 93, 94, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 309, 310, 72, 73, nil, nil,
- nil, nil, nil, 305, 306, 312, 101, 100, 102, 103,
- nil, nil, 230, nil, nil, nil, nil, nil, nil, 45,
- nil, nil, 105, 104, 106, 95, 56, 97, 96, 98,
- nil, 99, 107, 108, nil, 91, 92, nil, 42, 43,
- 41, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 223, nil, nil, 229, nil, nil, 58, 59, nil, nil,
- 60, nil, nil, nil, nil, nil, 44, nil, nil, nil,
- nil, nil, nil, nil, nil, 228, nil, nil, nil, nil,
- 89, 79, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, nil, nil, 69, 70,
- 71, 62, 57, 81, 93, 94, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 309, 310, 72, 73,
- nil, nil, nil, nil, nil, 305, 306, 312, 101, 100,
- 102, 103, nil, nil, 230, nil, nil, nil, nil, nil,
- nil, 45, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 223, nil, nil, 229, nil, nil, 58, 59,
- nil, nil, 60, nil, nil, nil, nil, nil, 44, nil,
- nil, nil, nil, nil, nil, nil, nil, 228, nil, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- 69, 70, 71, 62, 57, 81, 93, 94, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 30, 31,
- 72, 73, nil, nil, nil, nil, nil, 29, 28, 27,
- 101, 100, 102, 103, nil, nil, 230, nil, nil, nil,
- nil, nil, nil, 45, nil, nil, 105, 104, 106, 95,
- 56, 97, 96, 98, 284, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 223, nil, nil, 229, nil, nil,
- 58, 59, nil, nil, 60, nil, 281, nil, 279, nil,
- 44, nil, nil, 285, nil, nil, nil, nil, nil, 228,
- nil, nil, nil, nil, 89, 282, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, 69, 70, 71, 62, 57, 81, 93, 94,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 30, 31, 72, 73, nil, nil, nil, nil, nil, 29,
- 28, 27, 101, 100, 102, 103, nil, nil, 230, nil,
- nil, nil, nil, nil, nil, 45, nil, nil, 105, 104,
- 106, 95, 56, 97, 96, 98, 284, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 223, nil, nil, 229,
- nil, nil, 58, 59, nil, nil, 60, nil, 281, nil,
- 279, nil, 44, nil, nil, 285, nil, nil, nil, nil,
- nil, 228, nil, nil, nil, nil, 89, 282, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, 69, 70, 71, 62, 57, 81,
- 93, 94, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 30, 31, 72, 73, nil, nil, nil, nil,
- nil, 29, 28, 27, 101, 100, 102, 103, nil, nil,
- 230, nil, nil, nil, nil, nil, nil, 45, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, 284, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 223, nil,
- nil, 229, nil, nil, 58, 59, nil, nil, 60, nil,
- 281, nil, 279, nil, 44, nil, nil, 285, nil, nil,
- nil, nil, nil, 228, nil, nil, nil, nil, 89, 282,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, 218, nil, nil, 69, 70, 71, 62,
- 57, 81, 93, 94, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 309, 310, 72, 73, nil, nil,
- nil, nil, nil, 305, 306, 312, 101, 100, 102, 103,
- nil, nil, 230, nil, nil, nil, nil, nil, nil, 45,
- nil, nil, 105, 104, 106, 95, 56, 97, 96, 98,
- nil, 99, 107, 108, nil, 91, 92, nil, 42, 43,
- 41, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 223, nil, nil, 229, nil, nil, 58, 59, nil, nil,
- 60, nil, nil, nil, nil, nil, 44, nil, nil, nil,
- nil, nil, nil, nil, nil, 228, nil, nil, nil, nil,
- 89, 79, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, nil, nil, 69, 70,
- 71, 62, 57, 81, 93, 94, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 309, 310, 72, 73,
- nil, nil, nil, nil, nil, 305, 306, 312, 101, 100,
- 102, 103, nil, nil, 230, nil, nil, nil, nil, nil,
- nil, 45, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 223, nil, nil, 229, nil, nil, 58, 59,
- nil, nil, 60, nil, nil, nil, nil, nil, 44, nil,
- nil, nil, nil, nil, nil, nil, nil, 228, nil, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- 69, 70, 71, 62, 57, 81, 93, 94, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 309, 310,
- 72, 73, nil, nil, nil, nil, nil, 305, 306, 312,
- 101, 100, 102, 103, nil, nil, 230, nil, nil, nil,
- nil, nil, nil, 45, nil, nil, 105, 104, 106, 95,
- 56, 97, 96, 98, nil, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 223, nil, nil, 229, nil, nil,
- 58, 59, nil, nil, 60, nil, nil, nil, nil, nil,
- 44, nil, nil, nil, nil, nil, nil, nil, nil, 228,
- nil, nil, nil, nil, 89, 79, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, 69, 70, 71, 62, 57, 81, 93, 94,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 309, 310, 72, 73, nil, nil, nil, nil, nil, 305,
- 306, 312, 101, 100, 102, 103, nil, nil, 230, nil,
- nil, nil, nil, nil, nil, 45, nil, nil, 105, 104,
- 106, 95, 56, 97, 96, 98, nil, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 223, nil, nil, 229,
- nil, nil, 58, 59, nil, nil, 60, nil, nil, nil,
- nil, nil, 44, nil, nil, nil, nil, nil, nil, nil,
- nil, 228, nil, nil, nil, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, nil, nil, nil, 62, nil, 81,
- 93, 94, 69, 70, 71, 9, 57, nil, nil, nil,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 30, 31, 72, 73, nil, nil, nil, nil, nil, 29,
- 28, 27, 101, 100, 102, 103, nil, nil, 19, nil,
- nil, nil, nil, nil, 8, 45, nil, 10, 105, 104,
- 106, 95, 56, 97, 96, 98, nil, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 40, nil, nil, 33,
- nil, nil, 58, 59, nil, nil, 60, nil, 35, nil,
- nil, nil, 44, nil, nil, nil, nil, nil, nil, nil,
- nil, 20, nil, nil, nil, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, 69, 70, 71, 62, 57, 81,
- 93, 94, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 309, 310, 72, 73, nil, nil, nil, nil,
- nil, 305, 306, 312, 101, 100, 102, 103, nil, nil,
- 230, nil, nil, nil, nil, nil, nil, 307, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, nil, nil, 313, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 303, nil,
- nil, 229, nil, nil, 58, 59, nil, nil, 60, nil,
- 548, nil, 545, 544, 543, 553, 546, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 556, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 504, nil, 551, 62,
- nil, 81, 93, 94, 69, 70, 71, nil, 57, 564,
- 563, nil, 63, 64, 557, nil, nil, 67, nil, 65,
- 66, 68, 309, 310, 72, 73, nil, nil, nil, nil,
- nil, 305, 306, 312, 101, 100, 102, 103, nil, nil,
- 230, nil, nil, nil, nil, nil, nil, 307, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, nil, nil, 313, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 303, nil,
- nil, 299, nil, nil, 58, 59, nil, nil, 60, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 69, 70, 71, 62,
- 57, 81, 93, 94, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 309, 310, 72, 73, nil, nil,
- nil, nil, nil, 305, 306, 312, 101, 100, 102, 103,
- nil, nil, 230, nil, nil, nil, nil, nil, nil, 45,
- nil, nil, 105, 104, 106, 95, 56, 97, 96, 98,
- nil, 99, 107, 108, nil, 91, 92, nil, 42, 43,
- 41, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 223, nil, nil, 229, 520, nil, 58, 59, nil, nil,
- 60, nil, nil, nil, nil, nil, 44, nil, nil, nil,
- nil, nil, nil, nil, nil, 228, nil, nil, nil, nil,
- 89, 79, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, nil, nil, 69, 70,
- 71, 62, 57, 81, 93, 94, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 30, 31, 72, 73,
- nil, nil, nil, nil, nil, 29, 28, 27, 101, 100,
- 102, 103, nil, nil, 19, nil, nil, nil, nil, nil,
- nil, 45, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 223, nil, nil, 229, nil, nil, 58, 59,
- nil, nil, 60, nil, nil, nil, nil, nil, 44, nil,
- nil, nil, nil, nil, nil, nil, nil, 20, nil, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- 69, 70, 71, 62, 57, 81, 93, 94, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 30, 31,
- 72, 73, nil, nil, nil, nil, nil, 29, 28, 27,
- 101, 100, 102, 103, nil, nil, 19, nil, nil, nil,
- nil, nil, nil, 45, nil, nil, 105, 104, 106, 95,
- 56, 97, 96, 98, nil, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 223, nil, nil, 229, nil, nil,
- 58, 59, nil, nil, 60, nil, nil, nil, nil, nil,
- 44, nil, nil, nil, nil, nil, nil, nil, nil, 20,
- nil, nil, nil, nil, 89, 79, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, 69, 70, 71, 62, 57, 81, 93, 94,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 30, 31, 72, 73, nil, nil, nil, nil, nil, 29,
- 28, 27, 101, 100, 102, 103, nil, nil, 19, nil,
- nil, nil, nil, nil, nil, 45, nil, nil, 105, 104,
- 106, 95, 56, 97, 96, 98, nil, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 223, nil, nil, 229,
- nil, nil, 58, 59, nil, nil, 60, nil, nil, nil,
- nil, nil, 44, nil, nil, nil, nil, nil, nil, nil,
- nil, 20, nil, nil, nil, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, 69, 70, 71, 62, 57, 81,
- 93, 94, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 30, 31, 72, 73, nil, nil, nil, nil,
- nil, 29, 28, 27, 101, 100, 102, 103, nil, nil,
- 19, nil, nil, nil, nil, nil, nil, 45, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 223, nil,
- nil, 229, nil, nil, 58, 59, nil, nil, 60, nil,
- nil, nil, nil, nil, 44, nil, nil, nil, nil, nil,
- nil, nil, nil, 20, nil, nil, nil, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 69, 70, 71, 62,
- 57, 81, 93, 94, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 30, 31, 72, 73, nil, nil,
- nil, nil, nil, 29, 28, 27, 101, 100, 102, 103,
- nil, nil, 19, nil, nil, nil, nil, nil, nil, 45,
- nil, nil, 105, 104, 106, 95, 56, 97, 96, 98,
- nil, 99, 107, 108, nil, 91, 92, nil, 42, 43,
- 41, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 223, nil, nil, 229, nil, nil, 58, 59, nil, nil,
- 60, nil, nil, nil, nil, nil, 44, nil, nil, nil,
- nil, nil, nil, nil, nil, 20, nil, nil, nil, nil,
- 89, 79, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, nil, nil, 69, 70,
- 71, 62, 57, 81, 93, 94, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 309, 310, 72, 73,
- nil, nil, nil, nil, nil, 305, 306, 312, 101, 100,
- 102, 103, nil, nil, 230, nil, nil, nil, nil, nil,
- nil, 45, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 223, nil, nil, 229, nil, nil, 58, 59,
- nil, nil, 60, nil, nil, nil, nil, nil, 44, nil,
- nil, nil, nil, nil, nil, nil, nil, 228, nil, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- 69, 70, 71, 62, 57, 81, 93, 94, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 30, 31,
- 72, 73, nil, nil, nil, nil, nil, 29, 28, 27,
- 101, 100, 102, 103, nil, nil, 230, nil, nil, nil,
- nil, nil, nil, 45, nil, nil, 105, 104, 106, 95,
- 56, 97, 96, 98, 284, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 223, nil, nil, 229, nil, nil,
- 58, 59, nil, nil, 60, nil, 281, nil, 279, nil,
- 44, nil, nil, 285, nil, nil, nil, nil, nil, 228,
- nil, nil, nil, nil, 89, 282, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, 69, 70, 71, 62, 57, 81, 93, 94,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 309, 310, 72, 73, nil, nil, nil, nil, nil, 305,
- 306, 312, 101, 100, 102, 103, nil, nil, 230, nil,
- nil, nil, nil, nil, nil, 45, nil, nil, 105, 104,
- 106, 95, 56, 97, 96, 98, nil, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 223, nil, nil, 229,
- nil, nil, 58, 59, nil, nil, 60, nil, nil, nil,
- nil, nil, 44, nil, nil, nil, nil, nil, nil, nil,
- nil, 228, nil, nil, nil, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, 69, 70, 71, 62, 57, 81,
- 93, 94, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 309, 310, 72, 73, nil, nil, nil, nil,
- nil, 305, 306, 312, 101, 100, 102, 103, nil, nil,
- 230, nil, nil, nil, nil, nil, nil, 45, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 223, nil,
- nil, 229, nil, nil, 58, 59, nil, nil, 60, nil,
- nil, nil, nil, nil, 44, nil, nil, nil, nil, nil,
- nil, nil, nil, 228, nil, nil, nil, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 69, 70, 71, 62,
- 57, 81, 93, 94, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 309, 310, 72, 73, nil, nil,
- nil, nil, nil, 305, 306, 312, 101, 100, 102, 103,
- nil, nil, 230, nil, nil, nil, nil, nil, nil, 45,
- nil, nil, 105, 104, 106, 95, 56, 97, 96, 98,
- nil, 99, 107, 108, nil, 91, 92, nil, 42, 43,
- 41, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 223, nil, nil, 229, nil, nil, 58, 59, nil, nil,
- 60, nil, nil, nil, nil, nil, 44, nil, nil, nil,
- nil, nil, nil, nil, nil, 228, nil, nil, nil, nil,
- 89, 79, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, nil, nil, 69, 70,
- 71, 62, 57, 81, 93, 94, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 309, 310, 72, 73,
- nil, nil, nil, nil, nil, 305, 306, 312, 101, 100,
- 102, 103, nil, nil, 230, nil, nil, nil, nil, nil,
- nil, 45, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 223, nil, nil, 229, nil, nil, 58, 59,
- nil, nil, 60, nil, nil, nil, nil, nil, 44, nil,
- nil, nil, nil, nil, nil, nil, nil, 228, nil, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- 69, 70, 71, 62, 57, 81, 93, 94, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 309, 310,
- 72, 73, nil, nil, nil, nil, nil, 305, 306, 312,
- 101, 100, 102, 103, nil, nil, 230, nil, nil, nil,
- nil, nil, nil, 45, nil, nil, 105, 104, 106, 95,
- 56, 97, 96, 98, 284, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 223, nil, nil, 229, nil, nil,
- 58, 59, nil, nil, 60, nil, 659, nil, 279, nil,
- 44, nil, nil, 285, nil, nil, nil, nil, nil, 228,
- nil, nil, nil, nil, 89, 282, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, 69, 70, 71, 62, 57, 81, 93, 94,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 309, 310, 72, 73, nil, nil, nil, nil, nil, 305,
- 306, 312, 101, 100, 102, 103, nil, nil, 230, nil,
- nil, nil, nil, nil, nil, 45, nil, nil, 105, 104,
- 106, 95, 56, 97, 96, 98, 284, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 223, nil, nil, 229,
- nil, nil, 58, 59, nil, nil, 60, nil, nil, nil,
- 279, nil, 44, nil, nil, 285, nil, nil, nil, nil,
- nil, 228, nil, nil, nil, nil, 89, 282, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, 69, 70, 71, 62, 57, 81,
- 93, 94, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 309, 310, 72, 73, nil, nil, nil, nil,
- nil, 305, 306, 312, 101, 100, 102, 103, nil, nil,
- 230, nil, nil, nil, nil, nil, nil, 45, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 223, nil,
- nil, 229, nil, nil, 58, 59, nil, nil, 60, nil,
- nil, nil, nil, nil, 44, nil, nil, nil, nil, nil,
- nil, nil, nil, 228, nil, nil, nil, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, nil, nil, nil, 62,
- nil, 81, 93, 94, 69, 70, 71, 9, 57, nil,
- nil, nil, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 30, 31, 72, 73, nil, nil, nil, nil,
- nil, 29, 28, 27, 101, 100, 102, 103, nil, nil,
- 19, nil, nil, nil, nil, nil, 8, 45, 292, 10,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 40, nil,
- nil, 33, nil, nil, 58, 59, nil, nil, 60, nil,
- 35, nil, nil, nil, 44, nil, nil, nil, nil, nil,
- nil, nil, nil, 20, nil, nil, nil, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, nil, nil, 388, 62,
- nil, 81, 93, 94, 69, 70, 71, nil, 57, nil,
- nil, nil, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 309, 310, 72, 73, nil, nil, nil, nil,
- nil, 305, 306, 312, 101, 100, 102, 103, nil, nil,
- 230, nil, nil, nil, nil, nil, nil, 307, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, nil, nil, 313, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 303, nil,
- nil, 299, nil, nil, 58, 59, nil, nil, 60, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 69, 70, 71, 62,
- 57, 81, 93, 94, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 30, 31, 72, 73, nil, nil,
- nil, nil, nil, 29, 28, 27, 101, 100, 102, 103,
- nil, nil, 230, nil, nil, nil, nil, nil, nil, 45,
- nil, nil, 105, 104, 106, 95, 56, 97, 96, 98,
- 284, 99, 107, 108, nil, 91, 92, nil, 42, 43,
- 41, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 223, nil, nil, 229, nil, nil, 58, 59, nil, nil,
- 60, nil, 281, nil, 279, nil, 44, nil, nil, 285,
- nil, nil, nil, nil, nil, 228, nil, nil, nil, nil,
- 89, 282, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, nil, nil, 69, 70,
- 71, 62, 57, 81, 93, 94, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 309, 310, 72, 73,
- nil, nil, nil, nil, nil, 305, 306, 312, 101, 100,
- 102, 103, nil, nil, 230, nil, nil, nil, nil, nil,
- nil, 307, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- nil, nil, 313, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 303, nil, nil, 299, nil, nil, 58, 59,
- nil, nil, 60, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- 69, 70, 71, 62, 57, 81, 93, 94, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 309, 310,
- 72, 73, nil, nil, nil, nil, nil, 305, 306, 312,
- 101, 100, 102, 103, nil, nil, 230, nil, nil, nil,
- nil, nil, nil, 45, nil, nil, 105, 104, 106, 95,
- 56, 97, 96, 98, nil, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 223, nil, nil, 229, nil, nil,
- 58, 59, nil, nil, 60, nil, nil, nil, nil, nil,
- 44, nil, nil, nil, nil, nil, nil, nil, nil, 228,
- nil, nil, nil, nil, 89, 79, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, 69, 70, 71, 62, 57, 81, 93, 94,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 309, 310, 72, 73, nil, nil, nil, nil, nil, 305,
- 306, 312, 101, 100, 102, 103, nil, nil, 230, nil,
- nil, nil, nil, nil, nil, 45, nil, nil, 105, 104,
- 106, 95, 56, 97, 96, 98, nil, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 223, nil, nil, 229,
- nil, nil, 58, 59, nil, nil, 60, nil, nil, nil,
- nil, nil, 44, nil, nil, nil, nil, nil, nil, nil,
- nil, 228, nil, nil, nil, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, 69, 70, 71, 62, 57, 81,
- 93, 94, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 30, 31, 72, 73, nil, nil, nil, nil,
- nil, 29, 28, 27, 101, 100, 102, 103, nil, nil,
- 19, nil, nil, nil, nil, nil, nil, 45, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 223, nil,
- nil, 229, nil, nil, 58, 59, nil, nil, 60, nil,
- nil, nil, nil, nil, 44, nil, nil, nil, nil, nil,
- nil, nil, nil, 20, nil, nil, nil, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 69, 70, 71, 62,
- 57, 81, 93, 94, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 309, 310, 72, 73, nil, nil,
- nil, nil, nil, 305, 306, 312, 101, 100, 102, 103,
- nil, nil, 230, nil, nil, nil, nil, nil, nil, 45,
- nil, nil, 105, 104, 106, 95, 56, 97, 96, 98,
- 284, 99, 107, 108, nil, 91, 92, nil, 42, 43,
- 41, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 223, nil, nil, 229, nil, nil, 58, 59, nil, nil,
- 60, nil, 659, nil, nil, nil, 44, nil, nil, 285,
- nil, nil, nil, nil, nil, 228, nil, nil, nil, nil,
- 89, 282, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, nil, nil, 69, 70,
- 71, 62, 57, 81, 93, 94, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 309, 310, 72, 73,
- nil, nil, nil, nil, nil, 305, 306, 312, 101, 100,
- 102, 103, nil, nil, 230, nil, nil, nil, nil, nil,
- nil, 45, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, 284, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 223, nil, nil, 229, nil, nil, 58, 59,
- nil, nil, 60, nil, nil, nil, nil, nil, 44, nil,
- nil, 285, nil, nil, nil, nil, nil, 228, nil, nil,
- nil, nil, 89, 282, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- 69, 70, 71, 62, 57, 81, 93, 94, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 309, 310,
- 72, 73, nil, nil, nil, nil, nil, 305, 306, 312,
- 101, 100, 102, 103, nil, nil, 230, nil, nil, nil,
- nil, nil, nil, 45, nil, nil, 105, 104, 106, 95,
- 56, 97, 96, 98, nil, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 223, nil, nil, 229, nil, nil,
- 58, 59, nil, nil, 60, nil, nil, nil, nil, nil,
- 44, nil, nil, nil, nil, nil, nil, nil, nil, 228,
- nil, nil, nil, nil, 89, 79, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, 69, 70, 71, 62, 57, 81, 93, 94,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 309, 310, 72, 73, nil, nil, nil, nil, nil, 305,
- 306, 312, 101, 100, 102, 103, nil, nil, 230, nil,
- nil, nil, nil, nil, nil, 45, nil, nil, 105, 104,
- 106, 95, 56, 97, 96, 98, nil, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 223, nil, nil, 229,
- nil, nil, 58, 59, nil, nil, 60, nil, 281, nil,
- nil, nil, 44, nil, nil, nil, nil, nil, nil, nil,
- nil, 228, nil, nil, nil, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, 69, 70, 71, 62, 57, 81,
- 93, 94, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 30, 31, 72, 73, nil, nil, nil, nil,
- nil, 29, 28, 27, 101, 100, 102, 103, nil, nil,
- 230, nil, nil, nil, nil, nil, nil, 45, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, 284, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 223, nil,
- nil, 229, nil, nil, 58, 59, nil, nil, 60, nil,
- 281, nil, 279, nil, 44, nil, nil, 285, nil, nil,
- nil, nil, nil, 228, nil, nil, nil, nil, 89, 282,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 69, 70, 71, 62,
- 57, 81, 93, 94, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 30, 31, 72, 73, nil, nil,
- nil, nil, nil, 29, 28, 27, 101, 100, 102, 103,
- nil, nil, 230, nil, nil, nil, nil, nil, nil, 45,
- nil, nil, 105, 104, 106, 95, 56, 97, 96, 98,
- 284, 99, 107, 108, nil, 91, 92, nil, 42, 43,
- 41, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 223, nil, nil, 229, nil, nil, 58, 59, nil, nil,
- 60, nil, 281, nil, 279, nil, 44, nil, nil, 285,
- nil, nil, nil, nil, nil, 228, nil, nil, nil, nil,
- 89, 282, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, nil, nil, 69, 70,
- 71, 62, 57, 81, 93, 94, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 309, 310, 72, 73,
- nil, nil, nil, nil, nil, 305, 306, 312, 101, 100,
- 102, 103, nil, nil, 230, nil, nil, nil, nil, nil,
- nil, 45, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 223, nil, nil, 229, nil, nil, 58, 59,
- nil, nil, 60, nil, 754, nil, nil, nil, 44, nil,
- nil, nil, nil, nil, nil, nil, nil, 228, nil, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- 69, 70, 71, 62, 57, 81, 93, 94, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 309, 310,
- 72, 73, nil, nil, nil, nil, nil, 305, 306, 312,
- 101, 100, 102, 103, nil, nil, 230, nil, nil, nil,
- nil, nil, nil, 45, nil, nil, 105, 104, 106, 95,
- 56, 97, 96, 98, nil, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 223, nil, nil, 229, nil, nil,
- 58, 59, nil, nil, 60, nil, nil, nil, nil, nil,
- 44, nil, nil, nil, nil, nil, nil, nil, nil, 228,
- nil, nil, nil, nil, 89, 79, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, 69, 70, 71, 62, 57, 81, 93, 94,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 309, 310, 72, 73, nil, nil, nil, nil, nil, 305,
- 306, 312, 101, 100, 102, 103, nil, nil, 230, nil,
- nil, nil, nil, nil, nil, 45, nil, nil, 105, 104,
- 106, 95, 56, 97, 96, 98, 284, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 223, nil, nil, 229,
- nil, nil, 58, 59, nil, nil, 60, nil, 659, nil,
- 279, nil, 44, nil, nil, 285, nil, nil, nil, nil,
- nil, 228, nil, nil, nil, nil, 89, 282, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, 69, 70, 71, 62, 57, 81,
- 93, 94, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 309, 310, 72, 73, nil, nil, nil, nil,
- nil, 305, 306, 312, 101, 100, 102, 103, nil, nil,
- 230, nil, nil, nil, nil, nil, nil, 45, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, 284, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 223, nil,
- nil, 229, nil, nil, 58, 59, nil, nil, 60, nil,
- nil, nil, 279, nil, 44, nil, nil, 285, nil, nil,
- nil, nil, nil, 228, nil, nil, nil, nil, 89, 282,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 69, 70, 71, 62,
- 57, 81, 93, 94, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 30, 31, 72, 73, nil, nil,
- nil, nil, nil, 29, 28, 27, 101, 100, 102, 103,
- nil, nil, 230, nil, nil, nil, nil, nil, nil, 45,
- nil, nil, 105, 104, 106, 95, 56, 97, 96, 98,
- nil, 99, 107, 108, nil, 91, 92, nil, 42, 43,
- 41, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 223, nil, nil, 229, nil, nil, 58, 59, nil, nil,
- 60, nil, nil, nil, nil, nil, 44, nil, nil, nil,
- nil, nil, nil, nil, nil, 228, nil, nil, nil, nil,
- 89, 79, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, nil, nil, 69, 70,
- 71, 62, 57, 81, 93, 94, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 30, 31, 72, 73,
- nil, nil, nil, nil, nil, 29, 28, 27, 101, 100,
- 102, 103, nil, nil, 230, nil, nil, nil, nil, nil,
- nil, 45, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 223, nil, nil, 229, nil, nil, 58, 59,
- nil, nil, 60, nil, nil, nil, nil, nil, 44, nil,
- nil, nil, nil, nil, nil, nil, nil, 228, nil, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- 69, 70, 71, 62, 57, 81, 93, 94, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 30, 31,
- 72, 73, nil, nil, nil, nil, nil, 29, 28, 27,
- 101, 100, 102, 103, nil, nil, 230, nil, nil, nil,
- nil, nil, nil, 45, nil, nil, 105, 104, 106, 95,
- 56, 97, 96, 98, nil, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 223, nil, nil, 229, nil, nil,
- 58, 59, nil, nil, 60, nil, nil, nil, nil, nil,
- 44, nil, nil, nil, nil, nil, nil, nil, nil, 228,
- nil, nil, nil, nil, 89, 79, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, 69, 70, 71, 62, 57, 81, 93, 94,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 30, 31, 72, 73, nil, nil, nil, nil, nil, 29,
- 28, 27, 101, 100, 102, 103, nil, nil, 230, nil,
- nil, nil, nil, nil, nil, 45, nil, nil, 105, 104,
- 106, 95, 56, 97, 96, 98, nil, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 223, nil, nil, 229,
- nil, nil, 58, 59, nil, nil, 60, nil, nil, nil,
- nil, nil, 44, nil, nil, nil, nil, nil, nil, nil,
- nil, 228, nil, nil, nil, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, 69, 70, 71, 62, 57, 81,
- 93, 94, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 30, 31, 72, 73, nil, nil, nil, nil,
- nil, 29, 28, 27, 101, 100, 102, 103, nil, nil,
- 230, nil, nil, nil, nil, nil, nil, 45, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 223, nil,
- nil, 229, nil, nil, 58, 59, nil, nil, 60, nil,
- nil, nil, nil, nil, 44, nil, nil, nil, nil, nil,
- nil, nil, nil, 228, nil, nil, nil, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 69, 70, 71, 62,
- 57, 81, 93, 94, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 309, 310, 72, 73, nil, nil,
- nil, nil, nil, 305, 306, 312, 101, 100, 102, 103,
- nil, nil, 230, nil, nil, nil, nil, nil, nil, 45,
- nil, nil, 105, 104, 106, 95, 56, 97, 96, 98,
- nil, 99, 107, 108, nil, 91, 92, nil, 42, 43,
- 41, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 223, nil, nil, 229, nil, nil, 58, 59, nil, nil,
- 60, nil, nil, nil, nil, nil, 44, nil, nil, nil,
- nil, nil, nil, nil, nil, 228, nil, nil, nil, nil,
- 89, 79, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, nil, nil, 69, 70,
- 71, 62, 57, 81, 93, 94, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 309, 310, 72, 73,
- nil, nil, nil, nil, nil, 305, 306, 312, 101, 100,
- 102, 103, nil, nil, 230, nil, nil, nil, nil, nil,
- nil, 45, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 223, nil, nil, 229, nil, nil, 58, 59,
- nil, nil, 60, nil, nil, nil, nil, nil, 44, nil,
- nil, nil, nil, nil, nil, nil, nil, 228, nil, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- 69, 70, 71, 62, 57, 81, 93, 94, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 309, 310,
- 72, 73, nil, nil, nil, nil, nil, 305, 306, 312,
- 101, 100, 102, 103, nil, nil, 230, nil, nil, nil,
- nil, nil, nil, 45, nil, nil, 105, 104, 106, 95,
- 56, 97, 96, 98, nil, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 223, nil, nil, 229, nil, nil,
- 58, 59, nil, nil, 60, nil, nil, nil, nil, nil,
- 44, nil, nil, nil, nil, nil, nil, nil, nil, 228,
- nil, nil, nil, nil, 89, 79, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, 69, 70, 71, 62, 57, 81, 93, 94,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 309, 310, 72, 73, nil, nil, nil, nil, nil, 305,
- 306, 312, 101, 100, 102, 103, nil, nil, 230, nil,
- nil, nil, nil, nil, nil, 307, nil, nil, 105, 104,
- 106, 95, 56, 97, 96, 98, nil, 99, 107, 108,
- nil, 91, 92, nil, nil, nil, 313, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 303, nil, nil, 299,
- nil, nil, 58, 59, nil, nil, 60, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, 69, 70, 71, 62, 57, 81,
- 93, 94, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 309, 310, 72, 73, nil, nil, nil, nil,
- nil, 305, 306, 312, 101, 100, 102, 103, nil, nil,
- 230, nil, nil, nil, nil, nil, nil, 307, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, nil, nil, 313, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 303, nil,
- nil, 299, nil, nil, 58, 59, nil, nil, 60, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 69, 70, 71, 62,
- 57, 81, 93, 94, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 309, 310, 72, 73, nil, nil,
- nil, nil, nil, 305, 306, 312, 101, 100, 102, 103,
- nil, nil, 230, nil, nil, nil, nil, nil, nil, 45,
- nil, nil, 105, 104, 106, 95, 56, 97, 96, 98,
- nil, 99, 107, 108, nil, 91, 92, nil, 42, 43,
- 41, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 223, nil, nil, 229, nil, nil, 58, 59, nil, nil,
- 60, nil, 404, nil, nil, nil, 44, nil, nil, nil,
- nil, nil, nil, nil, nil, 228, nil, nil, nil, nil,
- 89, 79, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, nil, nil, 69, 70,
- 71, 62, 57, 81, 93, 94, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 309, 310, 72, 73,
- nil, nil, nil, nil, nil, 305, 306, 312, 101, 100,
- 102, 103, nil, nil, 230, nil, nil, nil, nil, nil,
- nil, 45, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 223, nil, nil, 229, nil, nil, 58, 59,
- nil, nil, 60, nil, nil, nil, nil, nil, 44, nil,
- nil, nil, nil, nil, nil, nil, nil, 228, nil, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- 69, 70, 71, 62, 57, 81, 93, 94, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 30, 31,
- 72, 73, nil, nil, nil, nil, nil, 29, 28, 27,
- 101, 100, 102, 103, nil, nil, 19, nil, nil, nil,
- nil, nil, nil, 45, nil, nil, 105, 104, 106, 95,
- 56, 97, 96, 98, nil, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 223, nil, nil, 229, nil, nil,
- 58, 59, nil, nil, 60, nil, nil, nil, nil, nil,
- 44, nil, nil, nil, nil, nil, nil, nil, nil, 20,
- nil, nil, nil, nil, 89, 79, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, 69, 70, 71, 62, 57, 81, 93, 94,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 30, 31, 72, 73, nil, nil, nil, nil, nil, 29,
- 28, 27, 101, 100, 102, 103, nil, nil, 19, nil,
- nil, nil, nil, nil, nil, 45, nil, nil, 105, 104,
- 106, 95, 56, 97, 96, 98, nil, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 223, nil, nil, 229,
- nil, nil, 58, 59, nil, nil, 60, nil, nil, nil,
- nil, nil, 44, nil, nil, nil, nil, nil, nil, nil,
- nil, 20, nil, nil, nil, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, 69, 70, 71, 62, 57, 81,
- 93, 94, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 309, 310, 72, 73, nil, nil, nil, nil,
- nil, 305, 306, 312, 101, 100, 102, 103, nil, nil,
- 230, nil, nil, nil, nil, nil, nil, 45, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 223, nil,
- nil, 229, nil, nil, 58, 59, nil, nil, 60, nil,
- nil, nil, nil, nil, 44, nil, nil, nil, nil, nil,
- nil, nil, nil, 228, nil, nil, nil, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 69, 70, 71, 62,
- 57, 81, 93, 94, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 30, 31, 72, 73, nil, nil,
- nil, nil, nil, 29, 28, 27, 101, 100, 102, 103,
- nil, nil, 230, nil, nil, nil, nil, nil, nil, 45,
- nil, nil, 105, 104, 106, 95, 56, 97, 96, 98,
- nil, 99, 107, 108, nil, 91, 92, nil, 42, 43,
- 41, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 223, nil, nil, 229, nil, nil, 58, 59, nil, nil,
- 60, nil, nil, nil, nil, nil, 44, nil, nil, nil,
- nil, nil, nil, nil, nil, 228, nil, nil, nil, nil,
- 89, 79, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, nil, nil, 69, 70,
- 71, 62, 57, 81, 93, 94, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 309, 310, 72, 73,
- nil, nil, nil, nil, nil, 305, 306, 312, 101, 100,
- 102, 103, nil, nil, 230, nil, nil, nil, nil, nil,
- nil, 45, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 223, nil, nil, 229, nil, nil, 58, 59,
- nil, nil, 60, nil, nil, nil, nil, nil, 44, nil,
- nil, nil, nil, nil, nil, nil, nil, 228, nil, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- 69, 70, 71, 62, 57, 81, 93, 94, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 309, 310,
- 72, 73, nil, nil, nil, nil, nil, 305, 306, 312,
- 101, 100, 102, 103, nil, nil, 230, nil, nil, nil,
- nil, nil, nil, 45, nil, nil, 105, 104, 106, 95,
- 56, 97, 96, 98, nil, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 223, nil, nil, 229, nil, nil,
- 58, 59, nil, nil, 60, nil, nil, nil, nil, nil,
- 44, nil, nil, nil, nil, nil, nil, nil, nil, 228,
- nil, nil, nil, nil, 89, 79, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, 69, 70, 71, 62, 57, 81, 93, 94,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 309, 310, 72, 73, nil, nil, nil, nil, nil, 305,
- 306, 312, 101, 100, 102, 103, nil, nil, 230, nil,
- nil, nil, nil, nil, nil, 45, nil, nil, 105, 104,
- 106, 95, 56, 97, 96, 98, nil, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 223, nil, nil, 229,
- nil, nil, 58, 59, nil, nil, 60, nil, nil, nil,
- nil, nil, 44, nil, nil, nil, nil, nil, nil, nil,
- nil, 228, nil, nil, nil, nil, 89, 79, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, 69, 70, 71, 62, 57, 81,
- 93, 94, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 309, 310, 72, 73, nil, nil, nil, nil,
- nil, 305, 306, 312, 101, 100, 102, 103, nil, nil,
- 230, nil, nil, nil, nil, nil, nil, 45, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 223, nil,
- nil, 229, nil, nil, 58, 59, nil, nil, 60, nil,
- nil, nil, nil, nil, 44, nil, nil, nil, nil, nil,
- nil, nil, nil, 228, nil, nil, nil, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 69, 70, 71, 62,
- 57, 81, 93, 94, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 309, 310, 72, 73, nil, nil,
- nil, nil, nil, 305, 306, 312, 101, 100, 102, 103,
- nil, nil, 230, nil, nil, nil, nil, nil, nil, 307,
- nil, nil, 105, 104, 106, 95, 56, 97, 96, 98,
- nil, 99, 107, 108, nil, 91, 92, nil, nil, nil,
- 313, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 876, nil, nil, 229, nil, nil, 58, 59, nil, nil,
- 60, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 89, 79, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, nil, nil, 69, 70,
- 71, 62, 57, 81, 93, 94, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 30, 31, 72, 73,
- nil, nil, nil, nil, nil, 29, 28, 27, 101, 100,
- 102, 103, nil, nil, 19, nil, nil, nil, nil, nil,
- nil, 45, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- 42, 43, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 223, nil, nil, 229, nil, nil, 58, 59,
- nil, nil, 60, nil, nil, nil, nil, nil, 44, nil,
- nil, nil, nil, nil, nil, nil, nil, 20, nil, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- 69, 70, 71, 62, 57, 81, 93, 94, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 309, 310,
- 72, 73, nil, nil, nil, nil, nil, 305, 306, 312,
- 101, 100, 102, 103, nil, nil, 230, nil, nil, nil,
- nil, nil, nil, 45, nil, nil, 105, 104, 106, 95,
- 56, 97, 96, 98, nil, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 223, nil, nil, 229, nil, nil,
- 58, 59, nil, nil, 60, nil, 659, nil, nil, nil,
- 44, nil, nil, nil, nil, nil, nil, nil, nil, 228,
- nil, nil, nil, nil, 89, 79, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, 69, 70, 71, 62, 57, 81, 93, 94,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 309, 310, 72, 73, nil, nil, nil, nil, nil, 305,
- 306, 312, 101, 100, 102, 103, nil, nil, 230, nil,
- nil, nil, nil, nil, nil, 45, nil, nil, 105, 104,
- 106, 95, 56, 97, 96, 98, 284, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 223, nil, nil, 229,
- nil, nil, 58, 59, nil, nil, 60, nil, nil, nil,
- 279, nil, 44, nil, nil, 285, nil, nil, nil, nil,
- nil, 228, nil, nil, nil, nil, 89, 282, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, 69, 70, 71, 62, 57, 81,
- 93, 94, 63, 64, nil, nil, nil, 67, nil, 65,
- 66, 68, 309, 310, 72, 73, nil, nil, nil, nil,
- nil, 305, 306, 312, 101, 100, 102, 103, nil, nil,
- 230, nil, nil, nil, nil, nil, nil, 45, nil, nil,
- 105, 104, 106, 95, 56, 97, 96, 98, nil, 99,
- 107, 108, nil, 91, 92, nil, 42, 43, 41, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 223, nil,
- nil, 229, nil, nil, 58, 59, nil, nil, 60, nil,
- nil, nil, nil, nil, 44, nil, nil, nil, nil, nil,
- nil, nil, nil, 228, nil, nil, nil, nil, 89, 79,
- 82, 83, nil, 84, 86, 85, 87, nil, nil, nil,
- nil, 80, 88, nil, nil, nil, 69, 70, 71, 62,
- 57, 81, 93, 94, 63, 64, nil, nil, nil, 67,
- nil, 65, 66, 68, 309, 310, 72, 73, nil, nil,
- nil, nil, nil, 305, 306, 312, 101, 100, 102, 103,
- nil, nil, 230, nil, nil, nil, nil, nil, nil, 307,
- nil, nil, 105, 104, 106, 95, 56, 97, 96, 98,
- nil, 99, 107, 108, nil, 91, 92, nil, nil, nil,
- 313, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 876, nil, nil, 229, nil, nil, 58, 59, nil, nil,
- 60, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 89, 79, 82, 83, nil, 84, 86, 85, 87, nil,
- nil, nil, nil, 80, 88, nil, nil, nil, 69, 70,
- 71, 62, 57, 81, 93, 94, 63, 64, nil, nil,
- nil, 67, nil, 65, 66, 68, 309, 310, 72, 73,
- nil, nil, nil, nil, nil, 305, 306, 312, 101, 100,
- 102, 103, nil, nil, 230, nil, nil, nil, nil, nil,
- nil, 307, nil, nil, 105, 104, 106, 95, 56, 97,
- 96, 98, nil, 99, 107, 108, nil, 91, 92, nil,
- nil, nil, 313, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 947, nil, nil, 229, nil, nil, 58, 59,
- nil, nil, 60, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 89, 79, 82, 83, nil, 84, 86, 85,
- 87, nil, nil, nil, nil, 80, 88, nil, nil, nil,
- 69, 70, 71, 62, 57, 81, 93, 94, 63, 64,
- nil, nil, nil, 67, nil, 65, 66, 68, 309, 310,
- 72, 73, nil, nil, nil, nil, nil, 305, 306, 312,
- 101, 100, 102, 103, nil, nil, 230, nil, nil, nil,
- nil, nil, nil, 45, nil, nil, 105, 104, 106, 95,
- 56, 97, 96, 98, nil, 99, 107, 108, nil, 91,
- 92, nil, 42, 43, 41, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 223, nil, nil, 229, nil, nil,
- 58, 59, nil, nil, 60, nil, nil, nil, nil, nil,
- 44, nil, nil, nil, nil, nil, nil, nil, nil, 228,
- nil, nil, nil, nil, 89, 79, 82, 83, nil, 84,
- 86, 85, 87, nil, nil, nil, nil, 80, 88, nil,
- nil, nil, 69, 70, 71, 62, 57, 81, 93, 94,
- 63, 64, nil, nil, nil, 67, nil, 65, 66, 68,
- 30, 31, 72, 73, nil, nil, nil, nil, nil, 29,
- 28, 27, 101, 100, 102, 103, nil, nil, 230, nil,
- nil, nil, nil, nil, nil, 45, nil, nil, 105, 104,
- 106, 95, 56, 97, 96, 98, 284, 99, 107, 108,
- nil, 91, 92, nil, 42, 43, 41, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 223, nil, nil, 229,
- nil, nil, 58, 59, nil, nil, 60, nil, 281, nil,
- 279, nil, 44, nil, nil, 285, nil, nil, nil, nil,
- nil, 228, nil, nil, nil, nil, 89, 282, 82, 83,
- nil, 84, 86, 85, 87, nil, nil, nil, nil, 80,
- 88, nil, nil, nil, nil, -282, nil, 62, nil, 81,
- 93, 94, -282, -282, -282, nil, nil, -282, -282, -282,
- nil, -282, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, -282, -282, -282, nil, nil, nil, nil, nil, nil,
- nil, -282, -282, nil, -282, -282, -282, -282, -282, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, -282, -282, -282, -282, -282, -282, -282, -282,
- -282, -282, -282, -282, -282, -282, nil, nil, -282, -282,
- -282, nil, nil, -282, nil, nil, -282, nil, nil, -282,
- -282, nil, -282, nil, -282, nil, -282, nil, -282, -282,
- nil, -282, -282, -282, -282, -282, nil, -282, nil, -282,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, -282, nil, nil, -282, -282, -282, -282,
- -579, -282, nil, -282, nil, nil, nil, -579, -579, -579,
- nil, nil, -579, -579, -579, nil, -579, nil, nil, nil,
- nil, nil, nil, nil, nil, -579, -579, -579, -579, nil,
- nil, nil, nil, nil, nil, nil, -579, -579, nil, -579,
- -579, -579, -579, -579, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, -579, -579, -579,
- -579, -579, -579, -579, -579, -579, -579, -579, -579, -579,
- -579, nil, nil, -579, -579, -579, nil, nil, -579, nil,
- nil, -579, nil, nil, -579, -579, nil, -579, nil, -579,
- nil, -579, nil, -579, -579, nil, -579, -579, -579, -579,
- -579, nil, -579, -579, -579, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, -579, nil,
- nil, -579, -579, -579, -579, -580, -579, nil, -579, nil,
- nil, nil, -580, -580, -580, nil, nil, -580, -580, -580,
- nil, -580, nil, nil, nil, nil, nil, nil, nil, nil,
- -580, -580, -580, -580, nil, nil, nil, nil, nil, nil,
- nil, -580, -580, nil, -580, -580, -580, -580, -580, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, -580, -580, -580, -580, -580, -580, -580, -580,
- -580, -580, -580, -580, -580, -580, nil, nil, -580, -580,
- -580, nil, nil, -580, nil, nil, -580, nil, nil, -580,
- -580, nil, -580, nil, -580, nil, -580, nil, -580, -580,
- nil, -580, -580, -580, -580, -580, nil, -580, -580, -580,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, -580, nil, nil, -580, -580, -580, -580,
- -411, -580, nil, -580, nil, nil, nil, -411, -411, -411,
- nil, nil, -411, -411, -411, nil, -411, nil, nil, nil,
- nil, nil, nil, nil, nil, -411, -411, -411, nil, nil,
- nil, nil, nil, nil, nil, nil, -411, -411, nil, -411,
- -411, -411, -411, -411, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, -411, -411, -411,
- -411, -411, -411, -411, -411, -411, -411, -411, -411, -411,
- -411, nil, nil, -411, -411, -411, nil, nil, -411, nil,
- 263, -411, nil, nil, -411, -411, nil, -411, nil, -411,
- nil, -411, nil, -411, -411, nil, -411, -411, -411, -411,
- -411, -298, -411, -411, -411, nil, nil, nil, -298, -298,
- -298, nil, nil, -298, -298, -298, nil, -298, -411, nil,
- nil, -411, -411, nil, -411, nil, -411, -298, -298, nil,
- nil, nil, nil, nil, nil, nil, nil, -298, -298, nil,
- -298, -298, -298, -298, -298, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, -298, -298,
- -298, -298, -298, -298, -298, -298, -298, -298, -298, -298,
- -298, -298, nil, nil, -298, -298, -298, nil, nil, -298,
- nil, 272, -298, nil, nil, -298, -298, nil, -298, nil,
- -298, nil, -298, nil, -298, -298, nil, -298, -298, -298,
- -298, -298, nil, -298, -246, -298, nil, nil, nil, nil,
- nil, -246, -246, -246, nil, nil, -246, -246, -246, -298,
- -246, nil, -298, -298, nil, -298, nil, -298, nil, -246,
- -246, -246, nil, nil, nil, nil, nil, nil, nil, nil,
- -246, -246, nil, -246, -246, -246, -246, -246, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, -246, -246, -246, -246, -246, -246, -246, -246, -246,
- -246, -246, -246, -246, -246, nil, nil, -246, -246, -246,
- nil, nil, -246, nil, 263, -246, nil, nil, -246, -246,
- nil, -246, nil, -246, nil, -246, nil, -246, -246, nil,
- -246, -246, -246, -246, -246, nil, -246, -246, -246, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, -246, nil, -246, -246, -246, nil, -246, nil,
- -246, -246, -246, -246, nil, nil, -246, -246, -246, 548,
- -246, 545, 544, 543, 553, 546, nil, nil, nil, -246,
- -246, nil, nil, nil, nil, 556, nil, nil, nil, nil,
- -246, -246, nil, -246, -246, -246, -246, -246, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 551, 548, nil,
- 545, 544, 543, 553, 546, nil, 561, 560, 564, 563,
- nil, nil, nil, 557, 556, 548, nil, 545, 544, 543,
- 553, 546, -246, nil, nil, nil, nil, nil, nil, -246,
- nil, 556, nil, nil, 263, -246, 551, 534, nil, 218,
- nil, nil, nil, nil, nil, 561, 560, 564, 563, nil,
- nil, nil, 557, 551, nil, nil, nil, -246, -246, nil,
- nil, nil, 561, 560, 564, 563, nil, nil, nil, 557,
- nil, nil, -246, nil, nil, -246, nil, nil, nil, nil,
- -246, 173, 184, 174, 197, 170, 190, 180, 179, 200,
- 201, 195, 178, 177, 172, 198, 202, 203, 182, 171,
- 185, 189, 191, 183, 176, nil, nil, nil, 192, 199,
- 194, 193, 186, 196, 181, 169, 188, 187, nil, nil,
- nil, nil, nil, 168, 175, 166, 167, 163, 164, 165,
- 124, 126, 123, nil, 125, nil, nil, nil, nil, nil,
- nil, nil, nil, 157, 158, nil, 154, 136, 137, 138,
- 145, 142, 144, nil, nil, 139, 140, nil, nil, nil,
- 159, 160, 146, 147, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 151, 150, nil,
- 135, 156, 153, 152, 161, 148, 149, 143, 141, 133,
- 155, 134, nil, nil, 162, 89, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 88,
- 173, 184, 174, 197, 170, 190, 180, 179, 200, 201,
- 195, 178, 177, 172, 198, 202, 203, 182, 171, 185,
- 189, 191, 183, 176, nil, nil, nil, 192, 199, 194,
- 193, 186, 196, 181, 169, 188, 187, nil, nil, nil,
- nil, nil, 168, 175, 166, 167, 163, 164, 165, 124,
- 126, nil, nil, 125, nil, nil, nil, nil, nil, nil,
- nil, nil, 157, 158, nil, 154, 136, 137, 138, 145,
- 142, 144, nil, nil, 139, 140, nil, nil, nil, 159,
- 160, 146, 147, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 151, 150, nil, 135,
- 156, 153, 152, 161, 148, 149, 143, 141, 133, 155,
- 134, nil, nil, 162, 89, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 88, 173,
- 184, 174, 197, 170, 190, 180, 179, 200, 201, 195,
- 178, 177, 172, 198, 202, 203, 182, 171, 185, 189,
- 191, 183, 176, nil, nil, nil, 192, 199, 194, 193,
- 186, 196, 181, 169, 188, 187, nil, nil, nil, nil,
- nil, 168, 175, 166, 167, 163, 164, 165, 124, 126,
- nil, nil, 125, nil, nil, nil, nil, nil, nil, nil,
- nil, 157, 158, nil, 154, 136, 137, 138, 145, 142,
- 144, nil, nil, 139, 140, nil, nil, nil, 159, 160,
- 146, 147, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 151, 150, nil, 135, 156,
- 153, 152, 161, 148, 149, 143, 141, 133, 155, 134,
- nil, nil, 162, 89, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 88, 173, 184,
- 174, 197, 170, 190, 180, 179, 200, 201, 195, 178,
- 177, 172, 198, 202, 203, 182, 171, 185, 189, 191,
- 183, 176, nil, nil, nil, 192, 199, 194, 193, 186,
- 196, 181, 169, 188, 187, nil, nil, nil, nil, nil,
- 168, 175, 166, 167, 163, 164, 165, 124, 126, nil,
- nil, 125, nil, nil, nil, nil, nil, nil, nil, nil,
- 157, 158, nil, 154, 136, 137, 138, 145, 142, 144,
- nil, nil, 139, 140, nil, nil, nil, 159, 160, 146,
- 147, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 151, 150, nil, 135, 156, 153,
- 152, 161, 148, 149, 143, 141, 133, 155, 134, nil,
- nil, 162, 89, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 88, 173, 184, 174,
- 197, 170, 190, 180, 179, 200, 201, 195, 178, 177,
- 172, 198, 202, 203, 182, 171, 185, 189, 191, 183,
- 176, nil, nil, nil, 192, 199, 194, 371, 370, 372,
- 369, 169, 188, 187, nil, nil, nil, nil, nil, 168,
- 175, 166, 167, 366, 367, 368, 364, 126, 97, 96,
- 365, nil, 99, nil, nil, nil, nil, nil, nil, 157,
- 158, nil, 154, 136, 137, 138, 145, 142, 144, nil,
- nil, 139, 140, nil, nil, nil, 159, 160, 146, 147,
- nil, nil, nil, nil, nil, 376, nil, nil, nil, nil,
- nil, nil, nil, 151, 150, nil, 135, 156, 153, 152,
- 161, 148, 149, 143, 141, 133, 155, 134, nil, nil,
- 162, 173, 184, 174, 197, 170, 190, 180, 179, 200,
- 201, 195, 178, 177, 172, 198, 202, 203, 182, 171,
- 185, 189, 191, 183, 176, nil, nil, nil, 192, 199,
- 194, 193, 186, 196, 181, 169, 188, 187, nil, nil,
- nil, nil, nil, 168, 175, 166, 167, 163, 164, 165,
- 124, 126, nil, nil, 125, nil, nil, nil, nil, nil,
- nil, nil, nil, 157, 158, nil, 154, 136, 137, 138,
- 145, 142, 144, nil, nil, 139, 140, nil, nil, nil,
- 159, 160, 146, 147, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 151, 150, nil,
- 135, 156, 153, 152, 161, 148, 149, 143, 141, 133,
- 155, 134, 413, 417, 162, nil, 414, nil, nil, nil,
- nil, nil, nil, nil, nil, 157, 158, nil, 154, 136,
- 137, 138, 145, 142, 144, nil, nil, 139, 140, nil,
- nil, nil, 159, 160, 146, 147, nil, nil, nil, nil,
- nil, 263, nil, nil, nil, nil, nil, nil, nil, 151,
- 150, nil, 135, 156, 153, 152, 161, 148, 149, 143,
- 141, 133, 155, 134, 420, 424, 162, nil, 419, nil,
- nil, nil, nil, nil, nil, nil, nil, 157, 158, nil,
- 154, 136, 137, 138, 145, 142, 144, nil, nil, 139,
- 140, nil, nil, nil, 159, 160, 146, 147, nil, nil,
- nil, nil, nil, 263, nil, nil, nil, nil, nil, nil,
- nil, 151, 150, nil, 135, 156, 153, 152, 161, 148,
- 149, 143, 141, 133, 155, 134, 475, 417, 162, nil,
- 476, nil, nil, nil, nil, nil, nil, nil, nil, 157,
- 158, nil, 154, 136, 137, 138, 145, 142, 144, nil,
- nil, 139, 140, nil, nil, nil, 159, 160, 146, 147,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 151, 150, nil, 135, 156, 153, 152,
- 161, 148, 149, 143, 141, 133, 155, 134, 638, 417,
- 162, nil, 639, nil, nil, nil, nil, nil, nil, nil,
- nil, 157, 158, nil, 154, 136, 137, 138, 145, 142,
- 144, nil, nil, 139, 140, nil, nil, nil, 159, 160,
- 146, 147, nil, nil, nil, nil, nil, 263, nil, nil,
- nil, nil, nil, nil, nil, 151, 150, nil, 135, 156,
- 153, 152, 161, 148, 149, 143, 141, 133, 155, 134,
- 640, 424, 162, nil, 641, nil, nil, nil, nil, nil,
- nil, nil, nil, 157, 158, nil, 154, 136, 137, 138,
- 145, 142, 144, nil, nil, 139, 140, nil, nil, nil,
- 159, 160, 146, 147, nil, nil, nil, nil, nil, 263,
- nil, nil, nil, nil, nil, nil, nil, 151, 150, nil,
- 135, 156, 153, 152, 161, 148, 149, 143, 141, 133,
- 155, 134, 670, 417, 162, nil, 671, nil, nil, nil,
- nil, nil, nil, nil, nil, 157, 158, nil, 154, 136,
- 137, 138, 145, 142, 144, nil, nil, 139, 140, nil,
- nil, nil, 159, 160, 146, 147, nil, nil, nil, nil,
- nil, 263, nil, nil, nil, nil, nil, nil, nil, 151,
- 150, nil, 135, 156, 153, 152, 161, 148, 149, 143,
- 141, 133, 155, 134, 673, 424, 162, nil, 674, nil,
- nil, nil, nil, nil, nil, nil, nil, 157, 158, nil,
- 154, 136, 137, 138, 145, 142, 144, nil, nil, 139,
- 140, nil, nil, nil, 159, 160, 146, 147, nil, nil,
- nil, nil, nil, 263, nil, nil, nil, nil, nil, nil,
- nil, 151, 150, nil, 135, 156, 153, 152, 161, 148,
- 149, 143, 141, 133, 155, 134, 638, 417, 162, nil,
- 639, nil, nil, nil, nil, nil, nil, nil, nil, 157,
- 158, nil, 154, 136, 137, 138, 145, 142, 144, nil,
- nil, 139, 140, nil, nil, nil, 159, 160, 146, 147,
- nil, nil, nil, nil, nil, 263, nil, nil, nil, nil,
- nil, nil, nil, 151, 150, nil, 135, 156, 153, 152,
- 161, 148, 149, 143, 141, 133, 155, 134, 640, 424,
- 162, nil, 641, nil, nil, nil, nil, nil, nil, nil,
- nil, 157, 158, nil, 154, 136, 137, 138, 145, 142,
- 144, nil, nil, 139, 140, nil, nil, nil, 159, 160,
- 146, 147, nil, nil, nil, nil, nil, 263, nil, nil,
- nil, nil, nil, nil, nil, 151, 150, nil, 135, 156,
- 153, 152, 161, 148, 149, 143, 141, 133, 155, 134,
- 721, 417, 162, nil, 722, nil, nil, nil, nil, nil,
- nil, nil, nil, 157, 158, nil, 154, 136, 137, 138,
- 145, 142, 144, nil, nil, 139, 140, nil, nil, nil,
- 159, 160, 146, 147, nil, nil, nil, nil, nil, 263,
- nil, nil, nil, nil, nil, nil, nil, 151, 150, nil,
- 135, 156, 153, 152, 161, 148, 149, 143, 141, 133,
- 155, 134, 723, 424, 162, nil, 724, nil, nil, nil,
- nil, nil, nil, nil, nil, 157, 158, nil, 154, 136,
- 137, 138, 145, 142, 144, nil, nil, 139, 140, nil,
- nil, nil, 159, 160, 146, 147, nil, nil, nil, nil,
- nil, 263, nil, nil, nil, nil, nil, nil, nil, 151,
- 150, nil, 135, 156, 153, 152, 161, 148, 149, 143,
- 141, 133, 155, 134, 726, 424, 162, nil, 727, nil,
- nil, nil, nil, nil, nil, nil, nil, 157, 158, nil,
- 154, 136, 137, 138, 145, 142, 144, nil, nil, 139,
- 140, nil, nil, nil, 159, 160, 146, 147, nil, nil,
- nil, nil, nil, 263, nil, nil, nil, nil, nil, nil,
- nil, 151, 150, nil, 135, 156, 153, 152, 161, 148,
- 149, 143, 141, 133, 155, 134, 475, 417, 162, nil,
- 476, nil, nil, nil, nil, nil, nil, nil, nil, 157,
- 158, nil, 154, 136, 137, 138, 145, 142, 144, nil,
- nil, 139, 140, nil, nil, nil, 159, 160, 146, 147,
- nil, nil, nil, nil, nil, 263, nil, nil, nil, nil,
- nil, nil, nil, 151, 150, nil, 135, 156, 153, 152,
- 161, 148, 149, 143, 141, 133, 155, 134, 983, 424,
- 162, nil, 982, nil, nil, nil, nil, nil, nil, nil,
- nil, 157, 158, nil, 154, 136, 137, 138, 145, 142,
- 144, nil, nil, 139, 140, nil, nil, nil, 159, 160,
- 146, 147, nil, nil, nil, nil, nil, 263, nil, nil,
- nil, nil, nil, nil, nil, 151, 150, nil, 135, 156,
- 153, 152, 161, 148, 149, 143, 141, 133, 155, 134,
- 1006, 417, 162, nil, 1007, nil, nil, nil, nil, nil,
- nil, nil, nil, 157, 158, nil, 154, 136, 137, 138,
- 145, 142, 144, nil, nil, 139, 140, nil, nil, nil,
- 159, 160, 146, 147, nil, nil, nil, nil, nil, 263,
- nil, nil, nil, nil, nil, nil, nil, 151, 150, nil,
- 135, 156, 153, 152, 161, 148, 149, 143, 141, 133,
- 155, 134, 1008, 424, 162, nil, 1009, nil, nil, nil,
- nil, nil, nil, nil, nil, 157, 158, nil, 154, 136,
- 137, 138, 145, 142, 144, nil, nil, 139, 140, nil,
- nil, nil, 159, 160, 146, 147, nil, nil, nil, nil,
- nil, 263, nil, nil, nil, nil, nil, nil, nil, 151,
- 150, nil, 135, 156, 153, 152, 161, 148, 149, 143,
- 141, 133, 155, 134, nil, 548, 162, 545, 544, 543,
- 553, 546, nil, 548, nil, 545, 544, 543, 553, 546,
- nil, 556, nil, nil, nil, nil, nil, nil, 548, 556,
- 545, 544, 543, 553, 546, nil, nil, nil, nil, nil,
- nil, nil, nil, 551, 556, nil, nil, nil, nil, nil,
- nil, 551, 561, 560, 564, 563, nil, nil, nil, 557,
- 561, 560, 564, 563, nil, nil, 551, 557, 548, nil,
- 545, 544, 543, 553, 546, 561, 560, 564, 563, nil,
- nil, nil, 557, 548, 556, 545, 544, 543, 553, 546,
- nil, 548, nil, 545, 544, 543, 553, 546, nil, 556,
- nil, nil, nil, nil, nil, nil, 551, 556, 548, nil,
- 545, 544, 543, 553, 546, 561, 560, 564, 563, nil,
- nil, 551, 557, nil, 556, nil, nil, nil, nil, 551,
- 561, 560, 564, 563, nil, nil, nil, 557, 561, 560,
- 564, 563, nil, nil, nil, 557, 551, 548, nil, 545,
- 544, 543, 553, 546, nil, 561, 560, 564, 563, nil,
- nil, nil, 557, 556, 548, nil, 545, 544, 543, 553,
- 546, nil, 548, nil, 545, 544, 543, 553, 546, nil,
- 556, nil, nil, nil, nil, 551, nil, 548, 556, 545,
- 544, 543, 553, 546, nil, nil, 564, 563, nil, nil,
- nil, 557, 551, 556, nil, nil, nil, nil, nil, nil,
- 551, nil, nil, 564, 563, nil, nil, nil, 557, 561,
- 560, 564, 563, nil, nil, 551, 557, 548, nil, 545,
- 544, 543, 553, 546, nil, nil, 564, 563, nil, nil,
- nil, 557, 548, 556, 545, 544, 543, 553, 546, nil,
- 548, nil, 545, 544, 543, 553, 546, 548, 556, 545,
- 544, 543, 553, 546, nil, 551, 556, nil, nil, nil,
- nil, nil, nil, 556, nil, nil, 564, 563, nil, nil,
- 551, 557, nil, nil, nil, nil, nil, nil, 551, nil,
- nil, 564, 563, nil, nil, 551, 557, nil, nil, 564,
- 563, nil, nil, nil, 557, nil, 564, 563, nil, nil,
- nil, 557 ]
-
-racc_action_check = [
- 95, 345, 61, 437, 437, 346, 349, 95, 95, 95,
- 221, 338, 95, 95, 95, 383, 95, 355, 19, 384,
- 58, 355, 594, 594, 95, 222, 95, 95, 95, 473,
- 650, 26, 17, 17, 578, 359, 95, 95, 339, 95,
- 95, 95, 95, 95, 719, 885, 15, 1, 908, 687,
- 721, 7, 1006, 665, 1007, 1020, 680, 680, 308, 19,
- 58, 913, 665, 913, 473, 722, 221, 95, 95, 95,
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
- 95, 222, 383, 95, 95, 95, 384, 95, 95, 570,
- 61, 95, 10, 15, 95, 95, 26, 95, 437, 95,
- 12, 95, 15, 95, 95, 844, 95, 95, 95, 95,
- 95, 345, 95, 98, 95, 346, 349, 594, 650, 26,
- 98, 98, 98, 308, 1008, 98, 98, 98, 95, 98,
- 338, 95, 95, 95, 95, 338, 95, 98, 95, 98,
- 98, 98, 359, 95, 578, 571, 308, 723, 845, 98,
- 98, 680, 98, 98, 98, 98, 98, 339, 13, 687,
- 721, 670, 339, 719, 885, 359, 719, 908, 719, 885,
- 359, 1006, 908, 1007, 1020, 722, 1006, 16, 1007, 1020,
- 98, 98, 98, 98, 98, 98, 98, 98, 98, 98,
- 98, 98, 98, 98, 570, 22, 98, 98, 98, 570,
- 98, 98, 1008, 671, 98, 37, 724, 98, 98, 40,
- 98, 605, 98, 723, 98, 844, 98, 98, 224, 98,
- 98, 98, 98, 98, 419, 98, 45, 98, 670, 590,
- 590, 419, 419, 419, 481, 24, 328, 419, 419, 328,
- 419, 98, 24, 1008, 98, 98, 98, 98, 1008, 98,
- 571, 98, 353, 845, 3, 571, 98, 723, 845, 3,
- 419, 419, 109, 419, 419, 419, 419, 419, 670, 481,
- 671, 670, 724, 123, 224, 204, 605, 605, 123, 123,
- 670, 704, 607, 704, 704, 704, 605, 704, 223, 41,
- 41, 419, 419, 419, 419, 419, 419, 419, 419, 419,
- 419, 419, 419, 419, 419, 353, 354, 419, 419, 419,
- 671, 419, 353, 671, 38, 419, 724, 353, 419, 313,
- 313, 353, 671, 419, 590, 419, 443, 419, 419, 590,
- 419, 419, 419, 419, 419, 378, 419, 420, 419, 225,
- 353, 39, 364, 226, 420, 420, 420, 607, 607, 364,
- 420, 420, 419, 420, 482, 419, 419, 607, 419, 354,
- 419, 38, 420, 230, 704, 673, 354, 419, 41, 41,
- 38, 354, 262, 420, 420, 354, 420, 420, 420, 420,
- 420, 904, 443, 904, 904, 904, 317, 904, 39, 482,
- 329, 651, 492, 329, 354, 379, 276, 39, 313, 313,
- 378, 378, 378, 801, 420, 420, 420, 420, 420, 420,
- 420, 420, 420, 420, 420, 420, 420, 420, 673, 413,
- 420, 420, 420, 380, 420, 673, 651, 288, 420, 414,
- 673, 420, 288, 317, 673, 381, 420, 638, 420, 726,
- 420, 420, 317, 420, 420, 420, 420, 420, 277, 420,
- 420, 420, 382, 673, 280, 385, 801, 492, 492, 492,
- 379, 379, 379, 801, 904, 420, 413, 621, 420, 420,
- 640, 420, 801, 420, 492, 413, 414, 640, 640, 640,
- 420, 292, 640, 640, 640, 414, 640, 365, 380, 380,
- 380, 801, 726, 638, 365, 640, 640, 640, 640, 726,
- 381, 381, 381, 639, 726, 293, 640, 640, 726, 640,
- 640, 640, 640, 640, 621, 295, 774, 382, 382, 382,
- 385, 385, 385, 621, 337, 337, 366, 726, 551, 367,
- 551, 551, 551, 366, 551, 296, 367, 640, 640, 640,
- 640, 640, 640, 640, 640, 640, 640, 640, 640, 640,
- 640, 689, 297, 640, 640, 640, 368, 640, 640, 639,
- 303, 640, 689, 368, 640, 640, 551, 640, 306, 640,
- 307, 640, 774, 640, 640, 551, 640, 640, 640, 640,
- 640, 862, 640, 640, 640, 332, 369, 77, 332, 342,
- 370, 312, 862, 369, 342, 689, 689, 370, 640, 77,
- 689, 640, 640, 640, 640, 449, 640, 641, 640, 77,
- 371, 14, 314, 640, 641, 641, 641, 371, 14, 641,
- 641, 641, 46, 641, 220, 862, 862, 14, 318, 46,
- 862, 220, 372, 641, 641, 641, 321, 449, 46, 372,
- 220, 449, 449, 641, 641, 374, 641, 641, 641, 641,
- 641, 833, 374, 833, 833, 833, 300, 833, 701, 326,
- 701, 701, 701, 300, 701, 831, 330, 831, 831, 831,
- 686, 831, 300, 686, 641, 641, 641, 641, 641, 641,
- 641, 641, 641, 641, 641, 641, 641, 641, 468, 833,
- 641, 641, 641, 301, 641, 641, 701, 469, 641, 331,
- 301, 641, 641, 831, 641, 701, 641, 333, 641, 301,
- 641, 641, 831, 641, 641, 641, 641, 641, 589, 641,
- 468, 641, 343, 589, 468, 468, 450, 468, 468, 469,
- 523, 523, 344, 469, 469, 641, 469, 469, 641, 641,
- 641, 641, 848, 641, 853, 641, 348, 848, 350, 853,
- 641, 0, 0, 0, 0, 0, 0, 394, 450, 302,
- 0, 0, 450, 450, 400, 0, 302, 0, 0, 0,
- 0, 0, 0, 0, 934, 302, 403, 934, 405, 0,
- 0, 0, 0, 0, 0, 0, 598, 598, 0, 409,
- 598, 598, 598, 432, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 411, 0, 0, 0,
- 412, 0, 0, 421, 0, 0, 0, 432, 432, 432,
- 432, 432, 432, 432, 432, 432, 432, 432, 304, 432,
- 432, 319, 429, 432, 432, 304, 0, 439, 319, 0,
- 692, 692, 0, 0, 304, 451, 0, 319, 0, 432,
- 452, 432, 0, 432, 432, 453, 432, 432, 432, 432,
- 432, 0, 432, 710, 710, 454, 0, 0, 0, 0,
- 347, 0, 0, 0, 0, 996, 996, 347, 479, 0,
- 0, 483, 432, 499, 432, 500, 347, 0, 503, 0,
- 0, 0, 33, 33, 33, 33, 33, 33, 505, 510,
- 357, 33, 33, 513, 521, 522, 33, 357, 33, 33,
- 33, 33, 33, 33, 33, 524, 357, 536, 537, 539,
- 33, 33, 33, 33, 33, 33, 33, 540, 541, 33,
- 550, 558, 562, 565, 408, 33, 33, 33, 33, 33,
- 33, 33, 33, 33, 33, 33, 33, 567, 33, 33,
- 33, 572, 33, 33, 573, 33, 33, 33, 408, 408,
- 408, 408, 408, 408, 408, 408, 408, 408, 408, 509,
- 408, 408, 581, 592, 408, 408, 509, 33, 602, 581,
- 33, 610, 612, 33, 33, 509, 618, 33, 581, 33,
- 408, 622, 408, 33, 408, 408, 627, 408, 408, 408,
- 408, 408, 33, 408, 632, 642, 649, 33, 33, 33,
- 33, 875, 33, 33, 33, 33, 656, 658, 875, 664,
- 33, 33, 667, 408, 669, 672, 675, 875, 33, 676,
- 33, 33, 33, 121, 121, 121, 121, 121, 121, 679,
- 681, 938, 121, 121, 684, 688, 703, 121, 938, 121,
- 121, 121, 121, 121, 121, 121, 705, 938, 712, 717,
- 720, 121, 121, 121, 121, 121, 121, 121, 729, 734,
- 121, 753, 758, 775, 776, 643, 121, 121, 121, 121,
- 121, 121, 121, 121, 121, 121, 121, 121, 777, 121,
- 121, 121, 779, 121, 121, 780, 121, 121, 121, 643,
- 643, 643, 643, 643, 643, 643, 643, 643, 643, 643,
- 674, 643, 643, 944, 781, 643, 643, 674, 121, 783,
- 944, 121, 674, 784, 121, 121, 674, 785, 121, 944,
- 121, 643, 786, 643, 121, 643, 643, 790, 643, 643,
- 643, 643, 643, 121, 643, 794, 795, 800, 121, 121,
- 121, 121, 945, 121, 121, 121, 121, 804, 807, 945,
- 808, 121, 121, 811, 643, 816, 817, 821, 945, 121,
- 822, 121, 121, 121, 206, 206, 206, 206, 206, 206,
- 824, 825, 946, 206, 206, 827, 830, 832, 206, 946,
- 206, 206, 206, 206, 206, 206, 206, 835, 946, 838,
- 847, 851, 206, 206, 206, 206, 206, 206, 206, 852,
- 873, 206, 873, 873, 873, 855, 873, 206, 206, 206,
- 206, 206, 206, 206, 206, 206, 206, 206, 206, 856,
- 206, 206, 206, 872, 206, 206, 876, 206, 206, 206,
- 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
- 21, 727, 21, 21, 948, 878, 21, 21, 727, 206,
- 889, 948, 206, 727, 890, 206, 206, 727, 906, 206,
- 948, 206, 21, 910, 21, 206, 21, 21, 911, 21,
- 21, 21, 21, 21, 206, 21, 917, 921, 924, 206,
- 206, 206, 206, 988, 206, 206, 206, 206, 927, 928,
- 988, 929, 206, 206, 930, 21, 932, 947, 952, 988,
- 206, 953, 206, 206, 206, 229, 229, 229, 229, 229,
- 229, 954, 955, 956, 229, 229, 958, 982, 983, 229,
- 984, 229, 229, 229, 229, 229, 229, 229, 6, 6,
- 6, 6, 6, 229, 229, 229, 229, 229, 229, 229,
- 995, 981, 229, 981, 981, 981, 997, 981, 229, 229,
- 229, 229, 229, 229, 229, 229, 229, 229, 229, 229,
- 998, 229, 229, 229, 999, 229, 229, 1000, 229, 229,
- 229, 274, 274, 274, 274, 274, 274, 274, 274, 274,
- 274, 274, 1009, 274, 274, 1001, 1002, 274, 274, 1009,
- 229, 1005, 1010, 229, 1009, 1011, 229, 229, 1009, 1022,
- 229, nil, 229, 274, nil, 274, 229, 274, 274, nil,
- 274, 274, 274, 274, 274, 229, 274, nil, nil, nil,
- 229, 229, 229, 229, nil, 229, 229, 229, 229, nil,
- nil, nil, nil, 229, 229, nil, 274, nil, nil, nil,
- nil, 229, nil, 229, 229, 229, 294, 294, 294, 294,
- 294, 294, nil, nil, nil, 294, 294, nil, nil, nil,
- 294, nil, 294, 294, 294, 294, 294, 294, 294, 291,
- 291, 291, 291, 291, 294, 294, 294, 294, 294, 294,
- 294, nil, nil, 294, 497, 497, 497, 497, 497, 294,
- 294, 294, 294, 294, 294, 294, 294, 294, 294, 294,
- 294, nil, 294, 294, 294, nil, 294, 294, nil, 294,
- 294, 294, 427, 427, 427, 427, 427, 427, 427, 427,
- 427, 427, 427, nil, 427, 427, nil, nil, 427, 427,
- nil, 294, nil, nil, 294, nil, nil, 294, 294, nil,
- nil, 294, nil, 294, 427, nil, 427, 294, 427, 427,
- nil, 427, 427, 427, 427, 427, 294, 427, nil, nil,
- nil, 294, 294, 294, 294, nil, 294, 294, 294, 294,
- nil, nil, nil, nil, 294, 294, nil, 427, nil, nil,
- nil, nil, 294, nil, 294, 294, 294, 299, 299, 299,
- 299, 299, 299, nil, nil, nil, 299, 299, nil, nil,
- nil, 299, nil, 299, 299, 299, 299, 299, 299, 299,
- nil, nil, nil, nil, nil, 299, 299, 299, 299, 299,
- 299, 299, nil, nil, 299, nil, nil, nil, nil, nil,
- 299, 299, 299, 299, 299, 299, 299, 299, 299, 299,
- 299, 299, nil, 299, 299, 299, nil, 299, 299, nil,
- 299, 299, 299, 519, 519, 519, 519, 519, 519, 519,
- 519, 519, 519, 519, nil, 519, 519, nil, nil, 519,
- 519, nil, 299, nil, nil, 299, nil, nil, 299, 299,
- nil, nil, 299, nil, 299, 519, nil, 519, 299, 519,
- 519, nil, 519, 519, 519, 519, 519, 299, 519, nil,
- nil, nil, 299, 299, 299, 299, nil, 299, 299, 299,
- 299, nil, nil, nil, nil, 299, 299, nil, 519, nil,
- nil, nil, nil, 299, nil, 299, 299, 299, 324, 324,
- 324, 324, 324, 324, nil, nil, nil, 324, 324, nil,
- nil, nil, 324, nil, 324, 324, 324, 324, 324, 324,
- 324, nil, nil, nil, nil, nil, 324, 324, 324, 324,
- 324, 324, 324, nil, nil, 324, nil, nil, nil, nil,
- nil, 324, 324, 324, 324, 324, 324, 324, 324, 324,
- 324, 324, 324, nil, 324, 324, 324, nil, 324, 324,
- nil, 324, 324, 324, 644, 644, 644, 644, 644, 644,
- 644, 644, 644, 644, 644, nil, 644, 644, nil, nil,
- 644, 644, nil, 324, nil, nil, 324, nil, nil, 324,
- 324, nil, nil, 324, nil, 324, 644, nil, 644, 324,
- 644, 644, nil, 644, 644, 644, 644, 644, 324, 644,
- nil, nil, nil, 324, 324, 324, 324, nil, 324, 324,
- 324, 324, nil, nil, nil, nil, 324, 324, 644, 644,
- nil, nil, nil, nil, 324, nil, 324, 324, 324, 498,
- 498, 498, 498, 498, 498, nil, nil, nil, 498, 498,
- nil, nil, nil, 498, nil, 498, 498, 498, 498, 498,
- 498, 498, nil, nil, nil, nil, nil, 498, 498, 498,
- 498, 498, 498, 498, nil, nil, 498, nil, nil, nil,
- nil, nil, 498, 498, 498, 498, 498, 498, 498, 498,
- 498, 498, 498, 498, nil, 498, 498, 498, nil, 498,
- 498, nil, 498, 498, 498, 678, 678, 678, 678, 678,
- 678, 678, 678, 678, 678, 678, nil, 678, 678, nil,
- nil, 678, 678, nil, 498, nil, nil, 498, nil, nil,
- 498, 498, nil, nil, 498, nil, 498, 678, nil, 678,
- 498, 678, 678, nil, 678, 678, 678, 678, 678, 498,
- 678, nil, nil, nil, 498, 498, 498, 498, nil, 498,
- 498, 498, 498, nil, nil, nil, nil, 498, 498, nil,
- 678, nil, nil, nil, nil, 498, nil, 498, 498, 498,
- 566, 566, 566, 566, 566, 566, nil, nil, nil, 566,
- 566, nil, nil, nil, 566, nil, 566, 566, 566, 566,
- 566, 566, 566, nil, nil, nil, nil, nil, 566, 566,
- 566, 566, 566, 566, 566, nil, nil, 566, nil, nil,
- nil, nil, nil, 566, 566, 566, 566, 566, 566, 566,
- 566, 566, 566, 566, 566, nil, 566, 566, 566, nil,
- 566, 566, nil, 566, 566, 566, 755, 755, 755, 755,
- 755, 755, 755, 755, 755, 755, 755, nil, 755, 755,
- nil, nil, 755, 755, nil, 566, nil, nil, 566, nil,
- nil, 566, 566, nil, nil, 566, nil, 566, 755, nil,
- 755, 566, 755, 755, nil, 755, 755, 755, 755, 755,
- 566, 755, nil, nil, nil, 566, 566, 566, 566, nil,
- 566, 566, 566, 566, nil, nil, nil, nil, 566, 566,
- nil, 755, nil, nil, nil, nil, 566, nil, 566, 566,
- 566, 569, 569, 569, 569, 569, 569, nil, nil, nil,
- 569, 569, nil, nil, nil, 569, nil, 569, 569, 569,
- 569, 569, 569, 569, nil, nil, nil, nil, nil, 569,
- 569, 569, 569, 569, 569, 569, nil, nil, 569, nil,
- nil, nil, nil, nil, 569, 569, 569, 569, 569, 569,
- 569, 569, 569, 569, 569, 569, nil, 569, 569, 569,
- nil, 569, 569, nil, 569, 569, 569, 760, 760, 760,
- 760, 760, 760, 760, 760, 760, 760, 760, nil, 760,
- 760, nil, nil, 760, 760, nil, 569, nil, nil, 569,
- nil, nil, 569, 569, nil, nil, 569, nil, 569, 760,
- nil, 760, 569, 760, 760, nil, 760, 760, 760, 760,
- 760, 569, 760, nil, nil, nil, 569, 569, 569, 569,
- nil, 569, 569, 569, 569, nil, nil, nil, nil, 569,
- 569, nil, 760, nil, nil, nil, nil, 569, nil, 569,
- 569, 569, 591, 591, 591, 591, 591, 591, nil, nil,
- nil, 591, 591, nil, nil, nil, 591, nil, 591, 591,
- 591, 591, 591, 591, 591, nil, nil, nil, nil, nil,
- 591, 591, 591, 591, 591, 591, 591, nil, nil, 591,
- nil, nil, nil, nil, nil, 591, 591, 591, 591, 591,
- 591, 591, 591, 591, 591, 591, 591, nil, 591, 591,
- 591, nil, 591, 591, nil, 591, 591, 591, 762, 762,
- 762, 762, 762, 762, 762, 762, 762, 762, 762, nil,
- 762, 762, nil, nil, 762, 762, nil, 591, nil, nil,
- 591, nil, nil, 591, 591, nil, nil, 591, nil, 591,
- 762, nil, 762, 591, 762, 762, nil, 762, 762, 762,
- 762, 762, 591, 762, nil, nil, nil, 591, 591, 591,
- 591, nil, 591, 591, 591, 591, nil, nil, nil, nil,
- 591, 591, nil, 762, nil, nil, nil, nil, 591, nil,
- 591, 591, 591, 648, 648, 648, 648, 648, 648, nil,
- nil, nil, 648, 648, nil, nil, nil, 648, nil, 648,
- 648, 648, 648, 648, 648, 648, nil, nil, nil, nil,
- nil, 648, 648, 648, 648, 648, 648, 648, nil, nil,
- 648, nil, nil, nil, nil, nil, 648, 648, 648, 648,
- 648, 648, 648, 648, 648, 648, 648, 648, nil, 648,
- 648, 648, nil, 648, 648, nil, 648, 648, 648, 765,
- 765, 765, 765, 765, 765, 765, 765, 765, 765, 765,
- nil, 765, 765, nil, nil, 765, 765, nil, 648, nil,
- nil, 648, nil, nil, 648, 648, nil, nil, 648, nil,
- 648, 765, nil, 765, 648, 765, 765, nil, 765, 765,
- 765, 765, 765, 648, 765, nil, nil, nil, 648, 648,
- 648, 648, nil, 648, 648, 648, 648, nil, nil, nil,
- nil, 648, 648, nil, 765, nil, nil, nil, nil, 648,
- nil, 648, 648, 648, 653, 653, 653, 653, 653, 653,
- nil, nil, nil, 653, 653, nil, nil, nil, 653, nil,
- 653, 653, 653, 653, 653, 653, 653, nil, nil, nil,
- nil, nil, 653, 653, 653, 653, 653, 653, 653, nil,
- nil, 653, nil, nil, nil, nil, nil, 653, 653, 653,
- 653, 653, 653, 653, 653, 653, 653, 653, 653, nil,
- 653, 653, 653, nil, 653, 653, nil, 653, 653, 653,
- 767, 767, 767, 767, 767, 767, 767, 767, 767, 767,
- 767, nil, 767, 767, nil, nil, 767, 767, nil, 653,
- nil, nil, 653, nil, nil, 653, 653, nil, nil, 653,
- nil, 653, 767, nil, 767, 653, 767, 767, nil, 767,
- 767, 767, 767, 767, 653, 767, nil, nil, nil, 653,
- 653, 653, 653, nil, 653, 653, 653, 653, nil, nil,
- nil, nil, 653, 653, nil, 767, nil, nil, nil, nil,
- 653, nil, 653, 653, 653, 654, 654, 654, 654, 654,
- 654, nil, nil, nil, 654, 654, nil, nil, nil, 654,
- nil, 654, 654, 654, 654, 654, 654, 654, nil, nil,
- nil, nil, nil, 654, 654, 654, 654, 654, 654, 654,
- nil, nil, 654, nil, nil, nil, nil, nil, 654, 654,
- 654, 654, 654, 654, 654, 654, 654, 654, 654, 654,
- nil, 654, 654, 654, nil, 654, 654, nil, 654, 654,
- 654, 769, 769, 769, 769, 769, 769, 769, 769, 769,
- 769, 769, nil, 769, 769, nil, nil, 769, 769, nil,
- 654, nil, nil, 654, nil, nil, 654, 654, nil, nil,
- 654, nil, 654, 769, nil, 769, 654, 769, 769, nil,
- 769, 769, 769, 769, 769, 654, 769, nil, nil, nil,
- 654, 654, 654, 654, nil, 654, 654, 654, 654, nil,
- nil, nil, nil, 654, 654, nil, 769, nil, nil, nil,
- nil, 654, nil, 654, 654, 654, 730, 730, 730, 730,
- 730, 730, nil, nil, nil, 730, 730, nil, nil, nil,
- 730, nil, 730, 730, 730, 730, 730, 730, 730, nil,
- nil, nil, nil, nil, 730, 730, 730, 730, 730, 730,
- 730, nil, nil, 730, nil, nil, nil, nil, nil, 730,
- 730, 730, 730, 730, 730, 730, 730, 730, 730, 730,
- 730, nil, 730, 730, 730, nil, 730, 730, nil, 730,
- 730, 730, 858, 858, 858, 858, 858, 858, 858, 858,
- 858, 858, 858, nil, 858, 858, nil, nil, 858, 858,
- nil, 730, nil, nil, 730, nil, nil, 730, 730, nil,
- nil, 730, nil, 730, 858, nil, 858, 730, 858, 858,
- nil, 858, 858, 858, 858, 858, 730, 858, nil, nil,
- nil, 730, 730, 730, 730, nil, 730, 730, 730, 730,
- nil, nil, nil, nil, 730, 730, nil, 858, nil, nil,
- nil, nil, 730, nil, 730, 730, 730, 735, 735, 735,
- 735, 735, 735, nil, nil, nil, 735, 735, nil, nil,
- nil, 735, nil, 735, 735, 735, 735, 735, 735, 735,
- nil, nil, nil, nil, nil, 735, 735, 735, 735, 735,
- 735, 735, nil, nil, 735, nil, nil, nil, nil, nil,
- 735, 735, 735, 735, 735, 735, 735, 735, 735, 735,
- 735, 735, nil, 735, 735, 735, nil, 735, 735, nil,
- 735, 735, 735, 969, 969, 969, 969, 969, 969, 969,
- 969, 969, 969, 969, nil, 969, 969, nil, nil, 969,
- 969, nil, 735, nil, nil, 735, nil, nil, 735, 735,
- nil, nil, 735, nil, 735, 969, nil, 969, 735, 969,
- 969, nil, 969, 969, 969, 969, 969, 735, 969, nil,
- nil, nil, 735, 735, 735, 735, nil, 735, 735, 735,
- 735, nil, nil, nil, nil, 735, 735, nil, 969, nil,
- nil, nil, nil, 735, nil, 735, 735, 735, 745, 745,
- 745, 745, 745, 745, nil, nil, nil, 745, 745, nil,
- nil, nil, 745, nil, 745, 745, 745, 745, 745, 745,
- 745, nil, nil, nil, nil, nil, 745, 745, 745, 745,
- 745, 745, 745, nil, nil, 745, nil, nil, nil, nil,
- nil, 745, 745, 745, 745, 745, 745, 745, 745, 745,
- 745, 745, 745, nil, 745, 745, 745, nil, 745, 745,
- nil, 745, 745, 745, 447, 447, 447, 447, 447, 447,
- 447, 447, 447, 447, 447, nil, 447, 447, nil, nil,
- 447, 447, nil, 745, nil, nil, 745, nil, nil, 745,
- 745, nil, nil, 745, nil, 745, 447, nil, 447, 745,
- 447, 447, nil, 447, 447, 447, 447, 447, 745, 447,
- nil, nil, nil, 745, 745, 745, 745, nil, 745, 745,
- 745, 745, nil, nil, nil, nil, 745, 745, nil, nil,
- nil, nil, nil, nil, 745, nil, 745, 745, 745, 793,
- 793, 793, 793, 793, 793, nil, nil, nil, 793, 793,
- nil, nil, nil, 793, nil, 793, 793, 793, 793, 793,
- 793, 793, nil, nil, nil, nil, nil, 793, 793, 793,
- 793, 793, 793, 793, nil, nil, 793, nil, nil, nil,
- nil, nil, 793, 793, 793, 793, 793, 793, 793, 793,
- 793, 793, 793, 793, nil, 793, 793, 793, nil, 793,
- 793, nil, 793, 793, 793, 448, 448, 448, 448, 448,
- 448, 448, 448, 448, 448, 448, nil, 448, 448, nil,
- nil, 448, 448, nil, 793, nil, nil, 793, nil, nil,
- 793, 793, nil, nil, 793, nil, 793, 448, nil, 448,
- 793, 448, 448, nil, 448, 448, 448, 448, 448, 793,
- 448, nil, nil, nil, 793, 793, 793, 793, nil, 793,
- 793, 793, 793, nil, nil, nil, nil, 793, 793, nil,
- nil, nil, nil, nil, nil, 793, nil, 793, 793, 793,
- 806, 806, 806, 806, 806, 806, nil, nil, nil, 806,
- 806, nil, nil, nil, 806, nil, 806, 806, 806, 806,
- 806, 806, 806, nil, nil, nil, nil, nil, 806, 806,
- 806, 806, 806, 806, 806, nil, 905, 806, 905, 905,
- 905, nil, 905, 806, 806, 806, 806, 806, 806, 806,
- 806, 806, 806, 806, 806, nil, 806, 806, 806, nil,
- 806, 806, nil, 806, 806, 806, 458, 458, 458, 458,
- 458, 458, 458, nil, 905, 458, 458, nil, nil, nil,
- nil, nil, 458, 458, nil, 806, nil, nil, 806, nil,
- nil, 806, 806, nil, nil, 806, nil, 806, 458, nil,
- 458, 806, 458, 458, nil, 458, 458, 458, 458, 458,
- 806, 458, nil, nil, nil, 806, 806, 806, 806, nil,
- 806, 806, 806, 806, nil, nil, nil, nil, 806, 806,
- nil, nil, nil, nil, nil, nil, 806, nil, 806, 806,
- 806, 814, 814, 814, 814, 814, 814, nil, nil, nil,
- 814, 814, nil, nil, nil, 814, nil, 814, 814, 814,
- 814, 814, 814, 814, nil, nil, nil, nil, nil, 814,
- 814, 814, 814, 814, 814, 814, nil, 957, 814, 957,
- 957, 957, nil, 957, 814, 814, 814, 814, 814, 814,
- 814, 814, 814, 814, 814, 814, nil, 814, 814, 814,
- nil, 814, 814, nil, 814, 814, 814, 459, nil, nil,
- 959, nil, 959, 959, 959, 957, 959, nil, nil, nil,
- nil, nil, nil, 459, 459, nil, 814, nil, nil, 814,
- nil, nil, 814, 814, nil, nil, 814, nil, 814, 459,
- nil, 459, 814, 459, 459, nil, 459, 459, 959, nil,
- 459, 814, 459, nil, nil, nil, 814, 814, 814, 814,
- nil, 814, 814, 814, 814, nil, nil, nil, nil, 814,
- 814, nil, nil, nil, nil, nil, nil, 814, nil, 814,
- 814, 814, 815, 815, 815, 815, 815, 815, nil, nil,
- nil, 815, 815, nil, nil, nil, 815, nil, 815, 815,
- 815, 815, 815, 815, 815, nil, nil, nil, nil, nil,
- 815, 815, 815, 815, 815, 815, 815, nil, 994, 815,
- 994, 994, 994, nil, 994, 815, 815, 815, 815, 815,
- 815, 815, 815, 815, 815, 815, 815, nil, 815, 815,
- 815, nil, 815, 815, nil, 815, 815, 815, 460, nil,
- nil, nil, nil, nil, nil, nil, 994, nil, nil, nil,
- nil, nil, nil, nil, 460, 460, nil, 815, nil, nil,
- 815, nil, nil, 815, 815, nil, nil, 815, nil, 815,
- 460, nil, 460, 815, 460, 460, nil, 460, 460, nil,
- nil, 460, 815, 460, nil, nil, nil, 815, 815, 815,
- 815, nil, 815, 815, 815, 815, nil, nil, nil, nil,
- 815, 815, nil, nil, nil, nil, nil, nil, 815, nil,
- 815, 815, 815, 839, 839, 839, 839, 839, 839, nil,
- nil, nil, 839, 839, nil, nil, nil, 839, nil, 839,
- 839, 839, 839, 839, 839, 839, nil, nil, nil, nil,
- nil, 839, 839, 839, 839, 839, 839, 839, nil, nil,
- 839, nil, nil, nil, nil, nil, 839, 839, 839, 839,
- 839, 839, 839, 839, 839, 839, 839, 839, nil, 839,
- 839, 839, nil, 839, 839, nil, 839, 839, 839, 461,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 461, 461, nil, 839, nil,
- nil, 839, nil, nil, 839, 839, nil, nil, 839, nil,
- 839, 461, nil, 461, 839, 461, 461, nil, 461, 461,
- nil, nil, 461, 839, 461, nil, nil, nil, 839, 839,
- 839, 839, nil, 839, 839, 839, 839, nil, nil, nil,
- nil, 839, 839, nil, nil, nil, nil, nil, nil, 839,
- nil, 839, 839, 839, 840, 840, 840, 840, 840, 840,
- nil, nil, nil, 840, 840, nil, nil, nil, 840, nil,
- 840, 840, 840, 840, 840, 840, 840, nil, nil, nil,
- nil, nil, 840, 840, 840, 840, 840, 840, 840, nil,
- nil, 840, nil, nil, nil, nil, nil, 840, 840, 840,
- 840, 840, 840, 840, 840, 840, 840, 840, 840, nil,
- 840, 840, 840, nil, 840, 840, nil, 840, 840, 840,
- 462, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 462, 462, nil, 840,
- nil, nil, 840, nil, nil, 840, 840, nil, nil, 840,
- nil, 840, 462, nil, 462, 840, 462, 462, nil, 462,
- 462, nil, nil, 462, 840, 462, nil, nil, nil, 840,
- 840, 840, 840, nil, 840, 840, 840, 840, nil, nil,
- nil, nil, 840, 840, nil, nil, nil, nil, nil, nil,
- 840, nil, 840, 840, 840, 843, 843, 843, 843, 843,
- 843, nil, nil, nil, 843, 843, nil, nil, nil, 843,
- nil, 843, 843, 843, 843, 843, 843, 843, nil, nil,
- nil, nil, nil, 843, 843, 843, 843, 843, 843, 843,
- nil, nil, 843, nil, nil, nil, nil, nil, 843, 843,
- 843, 843, 843, 843, 843, 843, 843, 843, 843, 843,
- nil, 843, 843, 843, nil, 843, 843, nil, 843, 843,
- 843, 463, 463, 463, 463, 463, 463, 463, nil, nil,
- 463, 463, nil, nil, nil, nil, nil, 463, 463, nil,
- 843, nil, nil, 843, nil, nil, 843, 843, nil, nil,
- 843, nil, 843, 463, nil, 463, 843, 463, 463, nil,
- 463, 463, 463, 463, 463, 843, 463, nil, nil, nil,
- 843, 843, 843, 843, nil, 843, 843, 843, 843, nil,
- nil, nil, nil, 843, 843, nil, nil, nil, nil, nil,
- nil, 843, nil, 843, 843, 843, 849, 849, 849, 849,
- 849, 849, nil, nil, nil, 849, 849, nil, nil, nil,
- 849, nil, 849, 849, 849, 849, 849, 849, 849, nil,
- nil, nil, nil, nil, 849, 849, 849, 849, 849, 849,
- 849, nil, nil, 849, nil, nil, nil, nil, nil, 849,
- 849, 849, 849, 849, 849, 849, 849, 849, 849, 849,
- 849, nil, 849, 849, 849, nil, 849, 849, nil, 849,
- 849, 849, 464, 464, 464, 464, 464, 464, 464, nil,
- nil, 464, 464, nil, nil, nil, nil, nil, 464, 464,
- nil, 849, nil, nil, 849, nil, nil, 849, 849, nil,
- nil, 849, nil, 849, 464, nil, 464, 849, 464, 464,
- nil, 464, 464, 464, 464, 464, 849, 464, nil, nil,
- nil, 849, 849, 849, 849, nil, 849, 849, 849, 849,
- nil, nil, nil, nil, 849, 849, nil, nil, nil, nil,
- nil, nil, 849, nil, 849, 849, 849, 882, 882, 882,
- 882, 882, 882, nil, nil, nil, 882, 882, nil, nil,
- nil, 882, nil, 882, 882, 882, 882, 882, 882, 882,
- nil, nil, nil, nil, nil, 882, 882, 882, 882, 882,
- 882, 882, nil, nil, 882, nil, nil, nil, nil, nil,
- 882, 882, 882, 882, 882, 882, 882, 882, 882, 882,
- 882, 882, nil, 882, 882, 882, nil, 882, 882, nil,
- 882, 882, 882, 465, 465, 465, 465, 465, 465, 465,
- nil, nil, 465, 465, nil, nil, nil, nil, nil, 465,
- 465, nil, 882, nil, nil, 882, nil, nil, 882, 882,
- nil, nil, 882, nil, 882, 465, nil, 465, 882, 465,
- 465, nil, 465, 465, 465, 465, 465, 882, 465, nil,
- nil, nil, 882, 882, 882, 882, nil, 882, 882, 882,
- 882, nil, nil, nil, nil, 882, 882, nil, nil, nil,
- nil, nil, nil, 882, nil, 882, 882, 882, 943, 943,
- 943, 943, 943, 943, nil, nil, nil, 943, 943, nil,
- nil, nil, 943, nil, 943, 943, 943, 943, 943, 943,
- 943, nil, nil, nil, nil, nil, 943, 943, 943, 943,
- 943, 943, 943, nil, nil, 943, nil, nil, nil, nil,
- nil, 943, 943, 943, 943, 943, 943, 943, 943, 943,
- 943, 943, 943, nil, 943, 943, 943, nil, 943, 943,
- nil, 943, 943, 943, 466, 466, 466, 466, 466, 466,
- 466, nil, nil, 466, 466, nil, nil, nil, nil, nil,
- 466, 466, nil, 943, nil, nil, 943, nil, nil, 943,
- 943, nil, nil, 943, nil, 943, 466, nil, 466, 943,
- 466, 466, nil, 466, 466, 466, 466, 466, 943, 466,
- nil, nil, nil, 943, 943, 943, 943, nil, 943, 943,
- 943, 943, nil, nil, nil, nil, 943, 943, nil, nil,
- nil, nil, nil, nil, 943, nil, 943, 943, 943, 960,
- 960, 960, 960, 960, 960, nil, nil, nil, 960, 960,
- nil, nil, nil, 960, nil, 960, 960, 960, 960, 960,
- 960, 960, nil, nil, nil, nil, nil, 960, 960, 960,
- 960, 960, 960, 960, nil, nil, 960, nil, nil, nil,
- nil, nil, 960, 960, 960, 960, 960, 960, 960, 960,
- 960, 960, 960, 960, nil, 960, 960, 960, nil, 960,
- 960, nil, 960, 960, 960, 467, 467, 467, 467, 467,
- 467, 467, nil, nil, 467, 467, nil, nil, nil, nil,
- nil, 467, 467, nil, 960, nil, nil, 960, nil, nil,
- 960, 960, nil, nil, 960, nil, 960, 467, nil, 467,
- 960, 467, 467, nil, 467, 467, 467, 467, 467, 960,
- 467, nil, nil, nil, 960, 960, 960, 960, nil, 960,
- 960, 960, 960, nil, nil, nil, nil, 960, 960, nil,
- nil, nil, nil, nil, nil, 960, nil, 960, 960, 960,
- 966, 966, 966, 966, 966, 966, nil, nil, nil, 966,
- 966, nil, nil, nil, 966, nil, 966, 966, 966, 966,
- 966, 966, 966, nil, nil, nil, nil, nil, 966, 966,
- 966, 966, 966, 966, 966, nil, nil, 966, nil, nil,
- nil, nil, nil, 966, 966, 966, 966, 966, 966, 966,
- 966, 966, 966, 966, 966, nil, 966, 966, 966, nil,
- 966, 966, nil, 966, 966, 966, 470, 470, 470, 470,
- 470, 470, 470, nil, nil, 470, 470, nil, nil, nil,
- nil, nil, 470, 470, nil, 966, nil, nil, 966, nil,
- nil, 966, 966, nil, nil, 966, nil, 966, 470, nil,
- 470, 966, 470, 470, nil, 470, 470, 470, 470, 470,
- 966, 470, nil, nil, nil, 966, 966, 966, 966, nil,
- 966, 966, 966, 966, nil, nil, nil, nil, 966, 966,
- nil, nil, nil, nil, nil, nil, 966, nil, 966, 966,
- 966, 968, 968, 968, 968, 968, 968, nil, nil, nil,
- 968, 968, nil, nil, nil, 968, nil, 968, 968, 968,
- 968, 968, 968, 968, nil, nil, nil, nil, nil, 968,
- 968, 968, 968, 968, 968, 968, nil, nil, 968, nil,
- nil, nil, nil, nil, 968, 968, 968, 968, 968, 968,
- 968, 968, 968, 968, 968, 968, nil, 968, 968, 968,
- nil, 968, 968, nil, 968, 968, 968, 471, 471, 471,
- 471, 471, 471, 471, 471, nil, 471, 471, nil, nil,
- nil, nil, nil, 471, 471, nil, 968, nil, nil, 968,
- nil, nil, 968, 968, nil, nil, 968, nil, 968, 471,
- nil, 471, 968, 471, 471, nil, 471, 471, 471, 471,
- 471, 968, 471, nil, nil, nil, 968, 968, 968, 968,
- nil, 968, 968, 968, 968, nil, nil, nil, nil, 968,
- 968, nil, nil, nil, nil, nil, nil, 968, nil, 968,
- 968, 968, 5, 5, 5, 5, 5, nil, nil, nil,
- 5, 5, nil, nil, nil, 5, nil, 5, 5, 5,
- 5, 5, 5, 5, nil, nil, nil, nil, nil, 5,
- 5, 5, 5, 5, 5, 5, nil, nil, 5, nil,
- nil, nil, nil, nil, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, nil, 5, 5, 5,
- nil, 5, 5, nil, 5, 5, 5, 455, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 455, 455, nil, 5, nil, nil, 5,
- nil, nil, 5, 5, nil, nil, 5, nil, 5, 455,
- nil, 455, 5, 455, 455, nil, 455, 455, nil, nil,
- nil, 5, nil, nil, nil, nil, 5, 5, 5, 5,
- nil, 5, 5, 5, 5, nil, nil, nil, nil, 5,
- 5, nil, nil, nil, 20, 20, 20, 5, 20, 5,
- 5, 5, 20, 20, nil, nil, nil, 20, nil, 20,
- 20, 20, 20, 20, 20, 20, nil, nil, nil, nil,
- nil, 20, 20, 20, 20, 20, 20, 20, nil, nil,
- 20, nil, nil, nil, nil, nil, nil, 20, nil, nil,
- 20, 20, 20, 20, 20, 20, 20, 20, nil, 20,
- 20, 20, nil, 20, 20, nil, 20, 20, 20, 456,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 456, 456, nil, 20, nil,
- nil, 20, nil, nil, 20, 20, nil, nil, 20, nil,
- nil, 456, nil, 456, 20, 456, 456, nil, 456, 456,
- nil, nil, nil, 20, nil, nil, nil, nil, 20, 20,
- 20, 20, nil, 20, 20, 20, 20, nil, nil, nil,
- nil, 20, 20, nil, nil, nil, 29, 29, 29, 20,
- 29, 20, 20, 20, 29, 29, nil, nil, nil, 29,
- nil, 29, 29, 29, 29, 29, 29, 29, nil, nil,
- nil, nil, nil, 29, 29, 29, 29, 29, 29, 29,
- nil, nil, 29, nil, nil, nil, nil, nil, nil, 29,
- nil, nil, 29, 29, 29, 29, 29, 29, 29, 29,
- 29, 29, 29, 29, nil, 29, 29, nil, 29, 29,
- 29, 457, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 457, 457, nil,
- 29, nil, nil, 29, nil, nil, 29, 29, nil, nil,
- 29, nil, 29, 457, 29, nil, 29, 457, 457, 29,
- 457, 457, nil, nil, nil, 29, nil, nil, nil, nil,
- 29, 29, 29, 29, nil, 29, 29, 29, 29, nil,
- nil, nil, nil, 29, 29, nil, nil, nil, 30, 30,
- 30, 29, 30, 29, 29, 29, 30, 30, nil, nil,
- nil, 30, nil, 30, 30, 30, 30, 30, 30, 30,
- nil, nil, nil, nil, nil, 30, 30, 30, 30, 30,
- 30, 30, nil, nil, 30, nil, nil, nil, nil, nil,
- nil, 30, nil, nil, 30, 30, 30, 30, 30, 30,
- 30, 30, 30, 30, 30, 30, nil, 30, 30, nil,
- 30, 30, 30, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 30, nil, nil, 30, nil, nil, 30, 30,
- nil, nil, 30, nil, 30, nil, 30, nil, 30, nil,
- nil, 30, nil, nil, nil, nil, nil, 30, nil, nil,
- nil, nil, 30, 30, 30, 30, nil, 30, 30, 30,
- 30, nil, nil, nil, nil, 30, 30, nil, nil, nil,
- 31, 31, 31, 30, 31, 30, 30, 30, 31, 31,
- nil, nil, nil, 31, nil, 31, 31, 31, 31, 31,
- 31, 31, nil, nil, nil, nil, nil, 31, 31, 31,
- 31, 31, 31, 31, nil, nil, 31, nil, nil, nil,
- nil, nil, nil, 31, nil, nil, 31, 31, 31, 31,
- 31, 31, 31, 31, 31, 31, 31, 31, nil, 31,
- 31, nil, 31, 31, 31, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 31, nil, nil, 31, nil, nil,
- 31, 31, nil, nil, 31, nil, 31, nil, 31, nil,
- 31, nil, nil, 31, nil, nil, nil, nil, nil, 31,
- nil, nil, nil, nil, 31, 31, 31, 31, nil, 31,
- 31, 31, 31, nil, nil, nil, nil, 31, 31, nil,
- nil, nil, 34, 34, 34, 31, 34, 31, 31, 31,
- 34, 34, nil, nil, nil, 34, nil, 34, 34, 34,
- 34, 34, 34, 34, nil, nil, nil, nil, nil, 34,
- 34, 34, 34, 34, 34, 34, nil, nil, 34, nil,
- nil, nil, nil, nil, nil, 34, nil, nil, 34, 34,
- 34, 34, 34, 34, 34, 34, nil, 34, 34, 34,
- nil, 34, 34, nil, nil, nil, 34, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 34, nil, nil, 34,
- nil, nil, 34, 34, nil, nil, 34, nil, 34, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 34, 34, 34, 34,
- nil, 34, 34, 34, 34, nil, nil, nil, nil, 34,
- 34, nil, nil, nil, 35, 35, 35, 34, 35, 34,
- 34, 34, 35, 35, nil, nil, nil, 35, nil, 35,
- 35, 35, 35, 35, 35, 35, nil, nil, nil, nil,
- nil, 35, 35, 35, 35, 35, 35, 35, nil, nil,
- 35, nil, nil, nil, nil, nil, nil, 35, nil, nil,
- 35, 35, 35, 35, 35, 35, 35, 35, nil, 35,
- 35, 35, nil, 35, 35, nil, nil, nil, 35, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 35, nil,
- nil, 35, nil, nil, 35, 35, nil, nil, 35, nil,
- 697, nil, 697, 697, 697, 697, 697, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 697, nil, 35, 35,
- 35, 35, nil, 35, 35, 35, 35, nil, nil, nil,
- nil, 35, 35, nil, nil, nil, 35, nil, 697, 35,
- nil, 35, 35, 35, 42, 42, 42, nil, 42, 697,
- 697, nil, 42, 42, 697, nil, nil, 42, nil, 42,
- 42, 42, 42, 42, 42, 42, nil, nil, nil, nil,
- nil, 42, 42, 42, 42, 42, 42, 42, nil, nil,
- 42, nil, nil, nil, nil, nil, nil, 42, nil, nil,
- 42, 42, 42, 42, 42, 42, 42, 42, nil, 42,
- 42, 42, nil, 42, 42, nil, 42, 42, 42, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 42, nil,
- nil, 42, nil, nil, 42, 42, nil, nil, 42, nil,
- nil, nil, nil, nil, 42, nil, nil, nil, nil, nil,
- nil, nil, nil, 42, nil, nil, nil, nil, 42, 42,
- 42, 42, nil, 42, 42, 42, 42, nil, nil, nil,
- nil, 42, 42, nil, nil, nil, 43, 43, 43, 42,
- 43, 42, 42, 42, 43, 43, nil, nil, nil, 43,
- nil, 43, 43, 43, 43, 43, 43, 43, nil, nil,
- nil, nil, nil, 43, 43, 43, 43, 43, 43, 43,
- nil, nil, 43, nil, nil, nil, nil, nil, nil, 43,
- nil, nil, 43, 43, 43, 43, 43, 43, 43, 43,
- nil, 43, 43, 43, nil, 43, 43, nil, 43, 43,
- 43, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 43, nil, nil, 43, nil, nil, 43, 43, nil, nil,
- 43, nil, nil, nil, nil, nil, 43, nil, nil, nil,
- nil, nil, nil, nil, nil, 43, nil, nil, nil, nil,
- 43, 43, 43, 43, nil, 43, 43, 43, 43, nil,
- nil, nil, nil, 43, 43, nil, nil, nil, 44, 44,
- 44, 43, 44, 43, 43, 43, 44, 44, nil, nil,
- nil, 44, nil, 44, 44, 44, 44, 44, 44, 44,
- nil, nil, nil, nil, nil, 44, 44, 44, 44, 44,
- 44, 44, nil, nil, 44, nil, nil, nil, nil, nil,
- nil, 44, nil, nil, 44, 44, 44, 44, 44, 44,
- 44, 44, nil, 44, 44, 44, nil, 44, 44, nil,
- 44, 44, 44, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 44, nil, nil, 44, nil, nil, 44, 44,
- nil, nil, 44, nil, nil, nil, nil, nil, 44, nil,
- nil, nil, nil, nil, nil, nil, nil, 44, nil, nil,
- nil, nil, 44, 44, 44, 44, nil, 44, 44, 44,
- 44, nil, nil, nil, nil, 44, 44, nil, nil, nil,
- 59, 59, 59, 44, 59, 44, 44, 44, 59, 59,
- nil, nil, nil, 59, nil, 59, 59, 59, 59, 59,
- 59, 59, nil, nil, nil, nil, nil, 59, 59, 59,
- 59, 59, 59, 59, nil, nil, 59, nil, nil, nil,
- nil, nil, nil, 59, nil, nil, 59, 59, 59, 59,
- 59, 59, 59, 59, 59, 59, 59, 59, nil, 59,
- 59, nil, 59, 59, 59, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 59, nil, nil, 59, nil, nil,
- 59, 59, nil, nil, 59, nil, 59, nil, nil, nil,
- 59, nil, nil, 59, nil, nil, nil, nil, nil, 59,
- nil, nil, nil, nil, 59, 59, 59, 59, nil, 59,
- 59, 59, 59, nil, nil, nil, nil, 59, 59, nil,
- nil, nil, 60, 60, 60, 59, 60, 59, 59, 59,
- 60, 60, nil, nil, nil, 60, nil, 60, 60, 60,
- 60, 60, 60, 60, nil, nil, nil, nil, nil, 60,
- 60, 60, 60, 60, 60, 60, nil, nil, 60, nil,
- nil, nil, nil, nil, nil, 60, nil, nil, 60, 60,
- 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,
- nil, 60, 60, nil, 60, 60, 60, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 60, nil, nil, 60,
- nil, nil, 60, 60, nil, nil, 60, nil, nil, nil,
- nil, nil, 60, nil, nil, 60, nil, nil, nil, nil,
- nil, 60, nil, nil, nil, nil, 60, 60, 60, 60,
- nil, 60, 60, 60, 60, nil, nil, nil, nil, 60,
- 60, nil, nil, nil, 63, 63, 63, 60, 63, 60,
- 60, 60, 63, 63, nil, nil, nil, 63, nil, 63,
- 63, 63, 63, 63, 63, 63, nil, nil, nil, nil,
- nil, 63, 63, 63, 63, 63, 63, 63, nil, nil,
- 63, nil, nil, nil, nil, nil, nil, 63, nil, nil,
- 63, 63, 63, 63, 63, 63, 63, 63, nil, 63,
- 63, 63, nil, 63, 63, nil, 63, 63, 63, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 63, nil,
- nil, 63, nil, nil, 63, 63, nil, nil, 63, nil,
- nil, nil, nil, nil, 63, nil, nil, nil, nil, nil,
- nil, nil, nil, 63, nil, nil, nil, nil, 63, 63,
- 63, 63, nil, 63, 63, 63, 63, nil, nil, nil,
- nil, 63, 63, nil, nil, nil, 64, 64, 64, 63,
- 64, 63, 63, 63, 64, 64, nil, nil, nil, 64,
- nil, 64, 64, 64, 64, 64, 64, 64, nil, nil,
- nil, nil, nil, 64, 64, 64, 64, 64, 64, 64,
- nil, nil, 64, nil, nil, nil, nil, nil, nil, 64,
- nil, nil, 64, 64, 64, 64, 64, 64, 64, 64,
- nil, 64, 64, 64, nil, 64, 64, nil, 64, 64,
- 64, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 64, nil, nil, 64, nil, nil, 64, 64, nil, nil,
- 64, nil, nil, nil, nil, nil, 64, nil, nil, nil,
- nil, nil, nil, nil, nil, 64, nil, nil, nil, nil,
- 64, 64, 64, 64, nil, 64, 64, 64, 64, nil,
- nil, nil, nil, 64, 64, nil, nil, nil, 67, 67,
- 67, 64, 67, 64, 64, 64, 67, 67, nil, nil,
- nil, 67, nil, 67, 67, 67, 67, 67, 67, 67,
- nil, nil, nil, nil, nil, 67, 67, 67, 67, 67,
- 67, 67, nil, nil, 67, nil, nil, nil, nil, nil,
- nil, 67, nil, nil, 67, 67, 67, 67, 67, 67,
- 67, 67, nil, 67, 67, 67, nil, 67, 67, nil,
- 67, 67, 67, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 67, nil, nil, 67, nil, nil, 67, 67,
- nil, nil, 67, nil, nil, nil, nil, nil, 67, nil,
- nil, nil, nil, nil, nil, nil, nil, 67, nil, nil,
- nil, nil, 67, 67, 67, 67, nil, 67, 67, 67,
- 67, nil, nil, nil, nil, 67, 67, 67, nil, nil,
- nil, nil, 67, 67, nil, 67, 67, 67, 68, 68,
- 68, nil, 68, nil, nil, nil, 68, 68, nil, nil,
- nil, 68, nil, 68, 68, 68, 68, 68, 68, 68,
- nil, nil, nil, nil, nil, 68, 68, 68, 68, 68,
- 68, 68, nil, nil, 68, nil, nil, nil, nil, nil,
- nil, 68, nil, nil, 68, 68, 68, 68, 68, 68,
- 68, 68, nil, 68, 68, 68, nil, 68, 68, nil,
- nil, nil, 68, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 68, nil, nil, 68, nil, nil, 68, 68,
- nil, nil, 68, nil, 68, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 68, 68, 68, 68, nil, 68, 68, 68,
- 68, nil, nil, nil, nil, 68, 68, nil, nil, nil,
- 69, 69, 69, 68, 69, 68, 68, 68, 69, 69,
- nil, nil, nil, 69, nil, 69, 69, 69, 69, 69,
- 69, 69, nil, nil, nil, nil, nil, 69, 69, 69,
- 69, 69, 69, 69, nil, nil, 69, nil, nil, nil,
- nil, nil, nil, 69, nil, nil, 69, 69, 69, 69,
- 69, 69, 69, 69, nil, 69, 69, 69, nil, 69,
- 69, nil, nil, nil, 69, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 69, nil, nil, 69, nil, nil, 69, nil, nil,
- 69, 69, nil, nil, 69, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 69, 69, 69, 69, nil, 69,
- 69, 69, 69, nil, nil, nil, nil, 69, 69, nil,
- nil, nil, 70, 70, 70, 69, 70, 69, 69, 69,
- 70, 70, nil, nil, nil, 70, nil, 70, 70, 70,
- 70, 70, 70, 70, nil, nil, nil, nil, nil, 70,
- 70, 70, 70, 70, 70, 70, nil, nil, 70, nil,
- nil, nil, nil, nil, nil, 70, nil, nil, 70, 70,
- 70, 70, 70, 70, 70, 70, nil, 70, 70, 70,
- nil, 70, 70, nil, nil, nil, 70, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 70, nil, nil, 70,
- nil, nil, 70, 70, nil, nil, 70, nil, 869, nil,
- 869, 869, 869, 869, 869, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 869, nil, 70, 70, 70, 70,
- nil, 70, 70, 70, 70, nil, nil, nil, nil, 70,
- 70, nil, nil, nil, nil, nil, 869, 70, nil, 70,
- 70, 70, 111, 111, 111, 111, 111, 869, 869, nil,
- 111, 111, 869, nil, nil, 111, nil, 111, 111, 111,
- 111, 111, 111, 111, nil, nil, nil, nil, nil, 111,
- 111, 111, 111, 111, 111, 111, nil, nil, 111, nil,
- nil, nil, nil, nil, 111, 111, 111, 111, 111, 111,
- 111, 111, 111, 111, 111, 111, nil, 111, 111, 111,
- nil, 111, 111, nil, 111, 111, 111, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 111, nil, nil, 111,
- nil, nil, 111, 111, nil, nil, 111, nil, 111, nil,
- nil, nil, 111, nil, nil, nil, nil, nil, nil, nil,
- nil, 111, nil, nil, nil, nil, 111, 111, 111, 111,
- nil, 111, 111, 111, 111, nil, nil, nil, nil, 111,
- 111, nil, nil, nil, nil, nil, 111, 111, nil, 111,
- 111, 111, 116, 116, 116, nil, 116, nil, nil, nil,
- 116, 116, nil, nil, nil, 116, nil, 116, 116, 116,
- 116, 116, 116, 116, nil, nil, nil, nil, nil, 116,
- 116, 116, 116, 116, 116, 116, nil, nil, 116, nil,
- nil, nil, nil, nil, nil, 116, nil, nil, 116, 116,
- 116, 116, 116, 116, 116, 116, nil, 116, 116, 116,
- nil, 116, 116, nil, 116, 116, 116, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 116, nil, nil, 116,
- nil, nil, 116, 116, nil, nil, 116, nil, nil, nil,
- nil, nil, 116, nil, nil, nil, nil, nil, nil, nil,
- nil, 116, nil, nil, nil, nil, 116, 116, 116, 116,
- nil, 116, 116, 116, 116, nil, nil, nil, nil, 116,
- 116, nil, nil, nil, 117, 117, 117, 116, 117, 116,
- 116, 116, 117, 117, nil, nil, nil, 117, nil, 117,
- 117, 117, 117, 117, 117, 117, nil, nil, nil, nil,
- nil, 117, 117, 117, 117, 117, 117, 117, nil, nil,
- 117, nil, nil, nil, nil, nil, nil, 117, nil, nil,
- 117, 117, 117, 117, 117, 117, 117, 117, nil, 117,
- 117, 117, nil, 117, 117, nil, 117, 117, 117, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 117, nil,
- nil, 117, nil, nil, 117, 117, nil, nil, 117, nil,
- nil, nil, nil, nil, 117, nil, nil, nil, nil, nil,
- nil, nil, nil, 117, nil, nil, nil, nil, 117, 117,
- 117, 117, nil, 117, 117, 117, 117, nil, nil, nil,
- nil, 117, 117, nil, nil, nil, 118, 118, 118, 117,
- 118, 117, 117, 117, 118, 118, nil, nil, nil, 118,
- nil, 118, 118, 118, 118, 118, 118, 118, nil, nil,
- nil, nil, nil, 118, 118, 118, 118, 118, 118, 118,
- nil, nil, 118, nil, nil, nil, nil, nil, nil, 118,
- nil, nil, 118, 118, 118, 118, 118, 118, 118, 118,
- nil, 118, 118, 118, nil, 118, 118, nil, 118, 118,
- 118, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 118, nil, nil, 118, nil, nil, 118, 118, nil, nil,
- 118, nil, nil, nil, nil, nil, 118, nil, nil, nil,
- nil, nil, nil, nil, nil, 118, nil, nil, nil, nil,
- 118, 118, 118, 118, nil, 118, 118, 118, 118, nil,
- nil, nil, nil, 118, 118, nil, nil, nil, 119, 119,
- 119, 118, 119, 118, 118, 118, 119, 119, nil, nil,
- nil, 119, nil, 119, 119, 119, 119, 119, 119, 119,
- nil, nil, nil, nil, nil, 119, 119, 119, 119, 119,
- 119, 119, nil, nil, 119, nil, nil, nil, nil, nil,
- nil, 119, nil, nil, 119, 119, 119, 119, 119, 119,
- 119, 119, nil, 119, 119, 119, nil, 119, 119, nil,
- 119, 119, 119, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 119, nil, nil, 119, nil, nil, 119, 119,
- nil, nil, 119, nil, nil, nil, nil, nil, 119, nil,
- nil, nil, nil, nil, nil, nil, nil, 119, nil, nil,
- nil, nil, 119, 119, 119, 119, nil, 119, 119, 119,
- 119, nil, nil, nil, nil, 119, 119, nil, nil, nil,
- nil, nil, nil, 119, nil, 119, 119, 119, 120, 120,
- 120, 120, 120, nil, nil, nil, 120, 120, nil, nil,
- nil, 120, nil, 120, 120, 120, 120, 120, 120, 120,
- nil, nil, nil, nil, nil, 120, 120, 120, 120, 120,
- 120, 120, nil, nil, 120, nil, nil, nil, nil, nil,
- 120, 120, nil, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, nil, 120, 120, 120, nil, 120, 120, nil,
- 120, 120, 120, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 120, nil, nil, 120, nil, nil, 120, 120,
- nil, nil, 120, nil, 120, nil, nil, nil, 120, nil,
- nil, nil, nil, nil, nil, nil, nil, 120, nil, nil,
- nil, nil, 120, 120, 120, 120, nil, 120, 120, 120,
- 120, nil, nil, nil, nil, 120, 120, nil, nil, nil,
- 207, 207, 207, 120, 207, 120, 120, 120, 207, 207,
- nil, nil, nil, 207, nil, 207, 207, 207, 207, 207,
- 207, 207, nil, nil, nil, nil, nil, 207, 207, 207,
- 207, 207, 207, 207, nil, nil, 207, nil, nil, nil,
- nil, nil, nil, 207, nil, nil, 207, 207, 207, 207,
- 207, 207, 207, 207, nil, 207, 207, 207, nil, 207,
- 207, nil, 207, 207, 207, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 207, nil, nil, 207, nil, nil,
- 207, 207, nil, nil, 207, nil, 207, nil, nil, nil,
- 207, nil, nil, nil, nil, nil, nil, nil, nil, 207,
- nil, nil, nil, nil, 207, 207, 207, 207, nil, 207,
- 207, 207, 207, nil, nil, nil, nil, 207, 207, nil,
- nil, nil, 208, 208, 208, 207, 208, 207, 207, 207,
- 208, 208, nil, nil, nil, 208, nil, 208, 208, 208,
- 208, 208, 208, 208, nil, nil, nil, nil, nil, 208,
- 208, 208, 208, 208, 208, 208, nil, nil, 208, nil,
- nil, nil, nil, nil, nil, 208, nil, nil, 208, 208,
- 208, 208, 208, 208, 208, 208, nil, 208, 208, 208,
- nil, 208, 208, nil, 208, 208, 208, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 208, nil, nil, 208,
- nil, nil, 208, 208, nil, nil, 208, nil, nil, nil,
- nil, nil, 208, nil, nil, nil, nil, nil, nil, nil,
- nil, 208, nil, nil, nil, nil, 208, 208, 208, 208,
- nil, 208, 208, 208, 208, nil, nil, nil, nil, 208,
- 208, nil, nil, nil, 209, 209, 209, 208, 209, 208,
- 208, 208, 209, 209, nil, nil, nil, 209, nil, 209,
- 209, 209, 209, 209, 209, 209, nil, nil, nil, nil,
- nil, 209, 209, 209, 209, 209, 209, 209, nil, nil,
- 209, nil, nil, nil, nil, nil, nil, 209, nil, nil,
- 209, 209, 209, 209, 209, 209, 209, 209, 209, 209,
- 209, 209, nil, 209, 209, nil, 209, 209, 209, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 209, nil,
- nil, 209, nil, nil, 209, 209, nil, nil, 209, nil,
- 209, nil, 209, nil, 209, nil, nil, 209, nil, nil,
- nil, nil, nil, 209, nil, nil, nil, nil, 209, 209,
- 209, 209, nil, 209, 209, 209, 209, nil, nil, nil,
- nil, 209, 209, nil, nil, nil, 212, 212, 212, 209,
- 212, 209, 209, 209, 212, 212, nil, nil, nil, 212,
- nil, 212, 212, 212, 212, 212, 212, 212, nil, nil,
- nil, nil, nil, 212, 212, 212, 212, 212, 212, 212,
- nil, nil, 212, nil, nil, nil, nil, nil, nil, 212,
- nil, nil, 212, 212, 212, 212, 212, 212, 212, 212,
- nil, 212, 212, 212, nil, 212, 212, nil, 212, 212,
- 212, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 212, nil, nil, 212, nil, nil, 212, 212, nil, nil,
- 212, nil, nil, nil, nil, nil, 212, nil, nil, nil,
- nil, nil, nil, nil, nil, 212, nil, nil, nil, nil,
- 212, 212, 212, 212, nil, 212, 212, 212, 212, nil,
- nil, nil, nil, 212, 212, nil, nil, nil, 213, 213,
- 213, 212, 213, 212, 212, 212, 213, 213, nil, nil,
- nil, 213, nil, 213, 213, 213, 213, 213, 213, 213,
- nil, nil, nil, nil, nil, 213, 213, 213, 213, 213,
- 213, 213, nil, nil, 213, nil, nil, nil, nil, nil,
- nil, 213, nil, nil, 213, 213, 213, 213, 213, 213,
- 213, 213, nil, 213, 213, 213, nil, 213, 213, nil,
- 213, 213, 213, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 213, nil, nil, 213, nil, nil, 213, 213,
- nil, nil, 213, nil, 213, nil, nil, nil, 213, nil,
- nil, nil, nil, nil, nil, nil, nil, 213, nil, nil,
- nil, nil, 213, 213, 213, 213, nil, 213, 213, 213,
- 213, nil, nil, nil, nil, 213, 213, nil, nil, nil,
- 214, 214, 214, 213, 214, 213, 213, 213, 214, 214,
- nil, nil, nil, 214, nil, 214, 214, 214, 214, 214,
- 214, 214, nil, nil, nil, nil, nil, 214, 214, 214,
- 214, 214, 214, 214, nil, nil, 214, nil, nil, nil,
- nil, nil, nil, 214, nil, nil, 214, 214, 214, 214,
- 214, 214, 214, 214, nil, 214, 214, 214, nil, 214,
- 214, nil, 214, 214, 214, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 214, nil, nil, 214, nil, nil,
- 214, 214, nil, nil, 214, nil, nil, nil, nil, nil,
- 214, nil, nil, nil, nil, nil, nil, nil, nil, 214,
- nil, nil, nil, nil, 214, 214, 214, 214, nil, 214,
- 214, 214, 214, nil, nil, nil, nil, 214, 214, nil,
- nil, nil, 215, 215, 215, 214, 215, 214, 214, 214,
- 215, 215, nil, nil, nil, 215, nil, 215, 215, 215,
- 215, 215, 215, 215, nil, nil, nil, nil, nil, 215,
- 215, 215, 215, 215, 215, 215, nil, nil, 215, nil,
- nil, nil, nil, nil, nil, 215, nil, nil, 215, 215,
- 215, 215, 215, 215, 215, 215, nil, 215, 215, 215,
- nil, 215, 215, nil, 215, 215, 215, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 215, nil, nil, 215,
- nil, nil, 215, 215, nil, nil, 215, nil, nil, nil,
- nil, nil, 215, nil, nil, nil, nil, nil, nil, nil,
- nil, 215, nil, nil, nil, nil, 215, 215, 215, 215,
- nil, 215, 215, 215, 215, nil, nil, nil, nil, 215,
- 215, nil, nil, nil, 216, 216, 216, 215, 216, 215,
- 215, 215, 216, 216, nil, nil, nil, 216, nil, 216,
- 216, 216, 216, 216, 216, 216, nil, nil, nil, nil,
- nil, 216, 216, 216, 216, 216, 216, 216, nil, nil,
- 216, nil, nil, nil, nil, nil, nil, 216, nil, nil,
- 216, 216, 216, 216, 216, 216, 216, 216, nil, 216,
- 216, 216, nil, 216, 216, nil, 216, 216, 216, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 216, nil,
- nil, 216, nil, nil, 216, 216, nil, nil, 216, nil,
- nil, nil, nil, nil, 216, nil, nil, nil, nil, nil,
- nil, nil, nil, 216, nil, nil, nil, nil, 216, 216,
- 216, 216, nil, 216, 216, 216, 216, nil, nil, nil,
- nil, 216, 216, nil, nil, nil, 217, 217, 217, 216,
- 217, 216, 216, 216, 217, 217, nil, nil, nil, 217,
- nil, 217, 217, 217, 217, 217, 217, 217, nil, nil,
- nil, nil, nil, 217, 217, 217, 217, 217, 217, 217,
- nil, nil, 217, nil, nil, nil, nil, nil, nil, 217,
- nil, nil, 217, 217, 217, 217, 217, 217, 217, 217,
- nil, 217, 217, 217, nil, 217, 217, nil, 217, 217,
- 217, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 217, nil, nil, 217, nil, nil, 217, 217, nil, nil,
- 217, nil, nil, nil, nil, nil, 217, nil, nil, nil,
- nil, nil, nil, nil, nil, 217, nil, nil, nil, nil,
- 217, 217, 217, 217, nil, 217, 217, 217, 217, nil,
- nil, nil, nil, 217, 217, 217, nil, nil, 228, 228,
- 228, 217, 228, 217, 217, 217, 228, 228, nil, nil,
- nil, 228, nil, 228, 228, 228, 228, 228, 228, 228,
- nil, nil, nil, nil, nil, 228, 228, 228, 228, 228,
- 228, 228, nil, nil, 228, nil, nil, nil, nil, nil,
- nil, 228, nil, nil, 228, 228, 228, 228, 228, 228,
- 228, 228, nil, 228, 228, 228, nil, 228, 228, nil,
- 228, 228, 228, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 228, nil, nil, 228, nil, nil, 228, 228,
- nil, nil, 228, nil, nil, nil, nil, nil, 228, nil,
- nil, nil, nil, nil, nil, nil, nil, 228, nil, nil,
- nil, nil, 228, 228, 228, 228, nil, 228, 228, 228,
- 228, nil, nil, nil, nil, 228, 228, nil, nil, nil,
- 231, 231, 231, 228, 231, 228, 228, 228, 231, 231,
- nil, nil, nil, 231, nil, 231, 231, 231, 231, 231,
- 231, 231, nil, nil, nil, nil, nil, 231, 231, 231,
- 231, 231, 231, 231, nil, nil, 231, nil, nil, nil,
- nil, nil, nil, 231, nil, nil, 231, 231, 231, 231,
- 231, 231, 231, 231, nil, 231, 231, 231, nil, 231,
- 231, nil, 231, 231, 231, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 231, nil, nil, 231, nil, nil,
- 231, 231, nil, nil, 231, nil, nil, nil, nil, nil,
- 231, nil, nil, nil, nil, nil, nil, nil, nil, 231,
- nil, nil, nil, nil, 231, 231, 231, 231, nil, 231,
- 231, 231, 231, nil, nil, nil, nil, 231, 231, nil,
- nil, nil, 232, 232, 232, 231, 232, 231, 231, 231,
- 232, 232, nil, nil, nil, 232, nil, 232, 232, 232,
- 232, 232, 232, 232, nil, nil, nil, nil, nil, 232,
- 232, 232, 232, 232, 232, 232, nil, nil, 232, nil,
- nil, nil, nil, nil, nil, 232, nil, nil, 232, 232,
- 232, 232, 232, 232, 232, 232, nil, 232, 232, 232,
- nil, 232, 232, nil, 232, 232, 232, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 232, nil, nil, 232,
- nil, nil, 232, 232, nil, nil, 232, nil, nil, nil,
- nil, nil, 232, nil, nil, nil, nil, nil, nil, nil,
- nil, 232, nil, nil, nil, nil, 232, 232, 232, 232,
- nil, 232, 232, 232, 232, nil, nil, nil, nil, 232,
- 232, nil, nil, nil, 233, 233, 233, 232, 233, 232,
- 232, 232, 233, 233, nil, nil, nil, 233, nil, 233,
- 233, 233, 233, 233, 233, 233, nil, nil, nil, nil,
- nil, 233, 233, 233, 233, 233, 233, 233, nil, nil,
- 233, nil, nil, nil, nil, nil, nil, 233, nil, nil,
- 233, 233, 233, 233, 233, 233, 233, 233, nil, 233,
- 233, 233, nil, 233, 233, nil, 233, 233, 233, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 233, nil,
- nil, 233, nil, nil, 233, 233, nil, nil, 233, nil,
- nil, nil, nil, nil, 233, nil, nil, nil, nil, nil,
- nil, nil, nil, 233, nil, nil, nil, nil, 233, 233,
- 233, 233, nil, 233, 233, 233, 233, nil, nil, nil,
- nil, 233, 233, nil, nil, nil, 234, 234, 234, 233,
- 234, 233, 233, 233, 234, 234, nil, nil, nil, 234,
- nil, 234, 234, 234, 234, 234, 234, 234, nil, nil,
- nil, nil, nil, 234, 234, 234, 234, 234, 234, 234,
- nil, nil, 234, nil, nil, nil, nil, nil, nil, 234,
- nil, nil, 234, 234, 234, 234, 234, 234, 234, 234,
- nil, 234, 234, 234, nil, 234, 234, nil, 234, 234,
- 234, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 234, nil, nil, 234, nil, nil, 234, 234, nil, nil,
- 234, nil, nil, nil, nil, nil, 234, nil, nil, nil,
- nil, nil, nil, nil, nil, 234, nil, nil, nil, nil,
- 234, 234, 234, 234, nil, 234, 234, 234, 234, nil,
- nil, nil, nil, 234, 234, nil, nil, nil, 235, 235,
- 235, 234, 235, 234, 234, 234, 235, 235, nil, nil,
- nil, 235, nil, 235, 235, 235, 235, 235, 235, 235,
- nil, nil, nil, nil, nil, 235, 235, 235, 235, 235,
- 235, 235, nil, nil, 235, nil, nil, nil, nil, nil,
- nil, 235, nil, nil, 235, 235, 235, 235, 235, 235,
- 235, 235, nil, 235, 235, 235, nil, 235, 235, nil,
- 235, 235, 235, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 235, nil, nil, 235, nil, nil, 235, 235,
- nil, nil, 235, nil, nil, nil, nil, nil, 235, nil,
- nil, nil, nil, nil, nil, nil, nil, 235, nil, nil,
- nil, nil, 235, 235, 235, 235, nil, 235, 235, 235,
- 235, nil, nil, nil, nil, 235, 235, nil, nil, nil,
- 236, 236, 236, 235, 236, 235, 235, 235, 236, 236,
- nil, nil, nil, 236, nil, 236, 236, 236, 236, 236,
- 236, 236, nil, nil, nil, nil, nil, 236, 236, 236,
- 236, 236, 236, 236, nil, nil, 236, nil, nil, nil,
- nil, nil, nil, 236, nil, nil, 236, 236, 236, 236,
- 236, 236, 236, 236, nil, 236, 236, 236, nil, 236,
- 236, nil, 236, 236, 236, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 236, nil, nil, 236, nil, nil,
- 236, 236, nil, nil, 236, nil, nil, nil, nil, nil,
- 236, nil, nil, nil, nil, nil, nil, nil, nil, 236,
- nil, nil, nil, nil, 236, 236, 236, 236, nil, 236,
- 236, 236, 236, nil, nil, nil, nil, 236, 236, nil,
- nil, nil, 237, 237, 237, 236, 237, 236, 236, 236,
- 237, 237, nil, nil, nil, 237, nil, 237, 237, 237,
- 237, 237, 237, 237, nil, nil, nil, nil, nil, 237,
- 237, 237, 237, 237, 237, 237, nil, nil, 237, nil,
- nil, nil, nil, nil, nil, 237, nil, nil, 237, 237,
- 237, 237, 237, 237, 237, 237, nil, 237, 237, 237,
- nil, 237, 237, nil, 237, 237, 237, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 237, nil, nil, 237,
- nil, nil, 237, 237, nil, nil, 237, nil, nil, nil,
- nil, nil, 237, nil, nil, nil, nil, nil, nil, nil,
- nil, 237, nil, nil, nil, nil, 237, 237, 237, 237,
- nil, 237, 237, 237, 237, nil, nil, nil, nil, 237,
- 237, nil, nil, nil, 238, 238, 238, 237, 238, 237,
- 237, 237, 238, 238, nil, nil, nil, 238, nil, 238,
- 238, 238, 238, 238, 238, 238, nil, nil, nil, nil,
- nil, 238, 238, 238, 238, 238, 238, 238, nil, nil,
- 238, nil, nil, nil, nil, nil, nil, 238, nil, nil,
- 238, 238, 238, 238, 238, 238, 238, 238, nil, 238,
- 238, 238, nil, 238, 238, nil, 238, 238, 238, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 238, nil,
- nil, 238, nil, nil, 238, 238, nil, nil, 238, nil,
- nil, nil, nil, nil, 238, nil, nil, nil, nil, nil,
- nil, nil, nil, 238, nil, nil, nil, nil, 238, 238,
- 238, 238, nil, 238, 238, 238, 238, nil, nil, nil,
- nil, 238, 238, nil, nil, nil, 239, 239, 239, 238,
- 239, 238, 238, 238, 239, 239, nil, nil, nil, 239,
- nil, 239, 239, 239, 239, 239, 239, 239, nil, nil,
- nil, nil, nil, 239, 239, 239, 239, 239, 239, 239,
- nil, nil, 239, nil, nil, nil, nil, nil, nil, 239,
- nil, nil, 239, 239, 239, 239, 239, 239, 239, 239,
- nil, 239, 239, 239, nil, 239, 239, nil, 239, 239,
- 239, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 239, nil, nil, 239, nil, nil, 239, 239, nil, nil,
- 239, nil, nil, nil, nil, nil, 239, nil, nil, nil,
- nil, nil, nil, nil, nil, 239, nil, nil, nil, nil,
- 239, 239, 239, 239, nil, 239, 239, 239, 239, nil,
- nil, nil, nil, 239, 239, nil, nil, nil, 240, 240,
- 240, 239, 240, 239, 239, 239, 240, 240, nil, nil,
- nil, 240, nil, 240, 240, 240, 240, 240, 240, 240,
- nil, nil, nil, nil, nil, 240, 240, 240, 240, 240,
- 240, 240, nil, nil, 240, nil, nil, nil, nil, nil,
- nil, 240, nil, nil, 240, 240, 240, 240, 240, 240,
- 240, 240, nil, 240, 240, 240, nil, 240, 240, nil,
- 240, 240, 240, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 240, nil, nil, 240, nil, nil, 240, 240,
- nil, nil, 240, nil, nil, nil, nil, nil, 240, nil,
- nil, nil, nil, nil, nil, nil, nil, 240, nil, nil,
- nil, nil, 240, 240, 240, 240, nil, 240, 240, 240,
- 240, nil, nil, nil, nil, 240, 240, nil, nil, nil,
- 241, 241, 241, 240, 241, 240, 240, 240, 241, 241,
- nil, nil, nil, 241, nil, 241, 241, 241, 241, 241,
- 241, 241, nil, nil, nil, nil, nil, 241, 241, 241,
- 241, 241, 241, 241, nil, nil, 241, nil, nil, nil,
- nil, nil, nil, 241, nil, nil, 241, 241, 241, 241,
- 241, 241, 241, 241, nil, 241, 241, 241, nil, 241,
- 241, nil, 241, 241, 241, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 241, nil, nil, 241, nil, nil,
- 241, 241, nil, nil, 241, nil, nil, nil, nil, nil,
- 241, nil, nil, nil, nil, nil, nil, nil, nil, 241,
- nil, nil, nil, nil, 241, 241, 241, 241, nil, 241,
- 241, 241, 241, nil, nil, nil, nil, 241, 241, nil,
- nil, nil, 242, 242, 242, 241, 242, 241, 241, 241,
- 242, 242, nil, nil, nil, 242, nil, 242, 242, 242,
- 242, 242, 242, 242, nil, nil, nil, nil, nil, 242,
- 242, 242, 242, 242, 242, 242, nil, nil, 242, nil,
- nil, nil, nil, nil, nil, 242, nil, nil, 242, 242,
- 242, 242, 242, 242, 242, 242, nil, 242, 242, 242,
- nil, 242, 242, nil, 242, 242, 242, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 242, nil, nil, 242,
- nil, nil, 242, 242, nil, nil, 242, nil, nil, nil,
- nil, nil, 242, nil, nil, nil, nil, nil, nil, nil,
- nil, 242, nil, nil, nil, nil, 242, 242, 242, 242,
- nil, 242, 242, 242, 242, nil, nil, nil, nil, 242,
- 242, nil, nil, nil, 243, 243, 243, 242, 243, 242,
- 242, 242, 243, 243, nil, nil, nil, 243, nil, 243,
- 243, 243, 243, 243, 243, 243, nil, nil, nil, nil,
- nil, 243, 243, 243, 243, 243, 243, 243, nil, nil,
- 243, nil, nil, nil, nil, nil, nil, 243, nil, nil,
- 243, 243, 243, 243, 243, 243, 243, 243, nil, 243,
- 243, 243, nil, 243, 243, nil, 243, 243, 243, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 243, nil,
- nil, 243, nil, nil, 243, 243, nil, nil, 243, nil,
- nil, nil, nil, nil, 243, nil, nil, nil, nil, nil,
- nil, nil, nil, 243, nil, nil, nil, nil, 243, 243,
- 243, 243, nil, 243, 243, 243, 243, nil, nil, nil,
- nil, 243, 243, nil, nil, nil, 244, 244, 244, 243,
- 244, 243, 243, 243, 244, 244, nil, nil, nil, 244,
- nil, 244, 244, 244, 244, 244, 244, 244, nil, nil,
- nil, nil, nil, 244, 244, 244, 244, 244, 244, 244,
- nil, nil, 244, nil, nil, nil, nil, nil, nil, 244,
- nil, nil, 244, 244, 244, 244, 244, 244, 244, 244,
- nil, 244, 244, 244, nil, 244, 244, nil, 244, 244,
- 244, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 244, nil, nil, 244, nil, nil, 244, 244, nil, nil,
- 244, nil, nil, nil, nil, nil, 244, nil, nil, nil,
- nil, nil, nil, nil, nil, 244, nil, nil, nil, nil,
- 244, 244, 244, 244, nil, 244, 244, 244, 244, nil,
- nil, nil, nil, 244, 244, nil, nil, nil, 245, 245,
- 245, 244, 245, 244, 244, 244, 245, 245, nil, nil,
- nil, 245, nil, 245, 245, 245, 245, 245, 245, 245,
- nil, nil, nil, nil, nil, 245, 245, 245, 245, 245,
- 245, 245, nil, nil, 245, nil, nil, nil, nil, nil,
- nil, 245, nil, nil, 245, 245, 245, 245, 245, 245,
- 245, 245, nil, 245, 245, 245, nil, 245, 245, nil,
- 245, 245, 245, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 245, nil, nil, 245, nil, nil, 245, 245,
- nil, nil, 245, nil, nil, nil, nil, nil, 245, nil,
- nil, nil, nil, nil, nil, nil, nil, 245, nil, nil,
- nil, nil, 245, 245, 245, 245, nil, 245, 245, 245,
- 245, nil, nil, nil, nil, 245, 245, nil, nil, nil,
- 246, 246, 246, 245, 246, 245, 245, 245, 246, 246,
- nil, nil, nil, 246, nil, 246, 246, 246, 246, 246,
- 246, 246, nil, nil, nil, nil, nil, 246, 246, 246,
- 246, 246, 246, 246, nil, nil, 246, nil, nil, nil,
- nil, nil, nil, 246, nil, nil, 246, 246, 246, 246,
- 246, 246, 246, 246, nil, 246, 246, 246, nil, 246,
- 246, nil, 246, 246, 246, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 246, nil, nil, 246, nil, nil,
- 246, 246, nil, nil, 246, nil, nil, nil, nil, nil,
- 246, nil, nil, nil, nil, nil, nil, nil, nil, 246,
- nil, nil, nil, nil, 246, 246, 246, 246, nil, 246,
- 246, 246, 246, nil, nil, nil, nil, 246, 246, nil,
- nil, nil, 247, 247, 247, 246, 247, 246, 246, 246,
- 247, 247, nil, nil, nil, 247, nil, 247, 247, 247,
- 247, 247, 247, 247, nil, nil, nil, nil, nil, 247,
- 247, 247, 247, 247, 247, 247, nil, nil, 247, nil,
- nil, nil, nil, nil, nil, 247, nil, nil, 247, 247,
- 247, 247, 247, 247, 247, 247, nil, 247, 247, 247,
- nil, 247, 247, nil, 247, 247, 247, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 247, nil, nil, 247,
- nil, nil, 247, 247, nil, nil, 247, nil, nil, nil,
- nil, nil, 247, nil, nil, nil, nil, nil, nil, nil,
- nil, 247, nil, nil, nil, nil, 247, 247, 247, 247,
- nil, 247, 247, 247, 247, nil, nil, nil, nil, 247,
- 247, nil, nil, nil, 248, 248, 248, 247, 248, 247,
- 247, 247, 248, 248, nil, nil, nil, 248, nil, 248,
- 248, 248, 248, 248, 248, 248, nil, nil, nil, nil,
- nil, 248, 248, 248, 248, 248, 248, 248, nil, nil,
- 248, nil, nil, nil, nil, nil, nil, 248, nil, nil,
- 248, 248, 248, 248, 248, 248, 248, 248, nil, 248,
- 248, 248, nil, 248, 248, nil, 248, 248, 248, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 248, nil,
- nil, 248, nil, nil, 248, 248, nil, nil, 248, nil,
- nil, nil, nil, nil, 248, nil, nil, nil, nil, nil,
- nil, nil, nil, 248, nil, nil, nil, nil, 248, 248,
- 248, 248, nil, 248, 248, 248, 248, nil, nil, nil,
- nil, 248, 248, nil, nil, nil, 249, 249, 249, 248,
- 249, 248, 248, 248, 249, 249, nil, nil, nil, 249,
- nil, 249, 249, 249, 249, 249, 249, 249, nil, nil,
- nil, nil, nil, 249, 249, 249, 249, 249, 249, 249,
- nil, nil, 249, nil, nil, nil, nil, nil, nil, 249,
- nil, nil, 249, 249, 249, 249, 249, 249, 249, 249,
- nil, 249, 249, 249, nil, 249, 249, nil, 249, 249,
- 249, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 249, nil, nil, 249, nil, nil, 249, 249, nil, nil,
- 249, nil, nil, nil, nil, nil, 249, nil, nil, nil,
- nil, nil, nil, nil, nil, 249, nil, nil, nil, nil,
- 249, 249, 249, 249, nil, 249, 249, 249, 249, nil,
- nil, nil, nil, 249, 249, nil, nil, nil, 250, 250,
- 250, 249, 250, 249, 249, 249, 250, 250, nil, nil,
- nil, 250, nil, 250, 250, 250, 250, 250, 250, 250,
- nil, nil, nil, nil, nil, 250, 250, 250, 250, 250,
- 250, 250, nil, nil, 250, nil, nil, nil, nil, nil,
- nil, 250, nil, nil, 250, 250, 250, 250, 250, 250,
- 250, 250, nil, 250, 250, 250, nil, 250, 250, nil,
- 250, 250, 250, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 250, nil, nil, 250, nil, nil, 250, 250,
- nil, nil, 250, nil, nil, nil, nil, nil, 250, nil,
- nil, nil, nil, nil, nil, nil, nil, 250, nil, nil,
- nil, nil, 250, 250, 250, 250, nil, 250, 250, 250,
- 250, nil, nil, nil, nil, 250, 250, nil, nil, nil,
- 251, 251, 251, 250, 251, 250, 250, 250, 251, 251,
- nil, nil, nil, 251, nil, 251, 251, 251, 251, 251,
- 251, 251, nil, nil, nil, nil, nil, 251, 251, 251,
- 251, 251, 251, 251, nil, nil, 251, nil, nil, nil,
- nil, nil, nil, 251, nil, nil, 251, 251, 251, 251,
- 251, 251, 251, 251, nil, 251, 251, 251, nil, 251,
- 251, nil, 251, 251, 251, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 251, nil, nil, 251, nil, nil,
- 251, 251, nil, nil, 251, nil, nil, nil, nil, nil,
- 251, nil, nil, nil, nil, nil, nil, nil, nil, 251,
- nil, nil, nil, nil, 251, 251, 251, 251, nil, 251,
- 251, 251, 251, nil, nil, nil, nil, 251, 251, nil,
- nil, nil, 252, 252, 252, 251, 252, 251, 251, 251,
- 252, 252, nil, nil, nil, 252, nil, 252, 252, 252,
- 252, 252, 252, 252, nil, nil, nil, nil, nil, 252,
- 252, 252, 252, 252, 252, 252, nil, nil, 252, nil,
- nil, nil, nil, nil, nil, 252, nil, nil, 252, 252,
- 252, 252, 252, 252, 252, 252, nil, 252, 252, 252,
- nil, 252, 252, nil, 252, 252, 252, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 252, nil, nil, 252,
- nil, nil, 252, 252, nil, nil, 252, nil, nil, nil,
- nil, nil, 252, nil, nil, nil, nil, nil, nil, nil,
- nil, 252, nil, nil, nil, nil, 252, 252, 252, 252,
- nil, 252, 252, 252, 252, nil, nil, nil, nil, 252,
- 252, nil, nil, nil, 253, 253, 253, 252, 253, 252,
- 252, 252, 253, 253, nil, nil, nil, 253, nil, 253,
- 253, 253, 253, 253, 253, 253, nil, nil, nil, nil,
- nil, 253, 253, 253, 253, 253, 253, 253, nil, nil,
- 253, nil, nil, nil, nil, nil, nil, 253, nil, nil,
- 253, 253, 253, 253, 253, 253, 253, 253, nil, 253,
- 253, 253, nil, 253, 253, nil, 253, 253, 253, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 253, nil,
- nil, 253, nil, nil, 253, 253, nil, nil, 253, nil,
- nil, nil, nil, nil, 253, nil, nil, nil, nil, nil,
- nil, nil, nil, 253, nil, nil, nil, nil, 253, 253,
- 253, 253, nil, 253, 253, 253, 253, nil, nil, nil,
- nil, 253, 253, nil, nil, nil, 254, 254, 254, 253,
- 254, 253, 253, 253, 254, 254, nil, nil, nil, 254,
- nil, 254, 254, 254, 254, 254, 254, 254, nil, nil,
- nil, nil, nil, 254, 254, 254, 254, 254, 254, 254,
- nil, nil, 254, nil, nil, nil, nil, nil, nil, 254,
- nil, nil, 254, 254, 254, 254, 254, 254, 254, 254,
- nil, 254, 254, 254, nil, 254, 254, nil, 254, 254,
- 254, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 254, nil, nil, 254, nil, nil, 254, 254, nil, nil,
- 254, nil, nil, nil, nil, nil, 254, nil, nil, nil,
- nil, nil, nil, nil, nil, 254, nil, nil, nil, nil,
- 254, 254, 254, 254, nil, 254, 254, 254, 254, nil,
- nil, nil, nil, 254, 254, nil, nil, nil, 255, 255,
- 255, 254, 255, 254, 254, 254, 255, 255, nil, nil,
- nil, 255, nil, 255, 255, 255, 255, 255, 255, 255,
- nil, nil, nil, nil, nil, 255, 255, 255, 255, 255,
- 255, 255, nil, nil, 255, nil, nil, nil, nil, nil,
- nil, 255, nil, nil, 255, 255, 255, 255, 255, 255,
- 255, 255, nil, 255, 255, 255, nil, 255, 255, nil,
- 255, 255, 255, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 255, nil, nil, 255, nil, nil, 255, 255,
- nil, nil, 255, nil, nil, nil, nil, nil, 255, nil,
- nil, nil, nil, nil, nil, nil, nil, 255, nil, nil,
- nil, nil, 255, 255, 255, 255, nil, 255, 255, 255,
- 255, nil, nil, nil, nil, 255, 255, nil, nil, nil,
- 263, 263, 263, 255, 263, 255, 255, 255, 263, 263,
- nil, nil, nil, 263, nil, 263, 263, 263, 263, 263,
- 263, 263, nil, nil, nil, nil, nil, 263, 263, 263,
- 263, 263, 263, 263, nil, nil, 263, nil, nil, nil,
- nil, nil, nil, 263, nil, nil, 263, 263, 263, 263,
- 263, 263, 263, 263, 263, 263, 263, 263, nil, 263,
- 263, nil, 263, 263, 263, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 263, nil, nil, 263, nil, nil,
- 263, 263, nil, nil, 263, nil, 263, nil, 263, nil,
- 263, nil, nil, 263, nil, nil, nil, nil, nil, 263,
- nil, nil, nil, nil, 263, 263, 263, 263, nil, 263,
- 263, 263, 263, nil, nil, nil, nil, 263, 263, nil,
- nil, nil, 264, 264, 264, 263, 264, 263, 263, 263,
- 264, 264, nil, nil, nil, 264, nil, 264, 264, 264,
- 264, 264, 264, 264, nil, nil, nil, nil, nil, 264,
- 264, 264, 264, 264, 264, 264, nil, nil, 264, nil,
- nil, nil, nil, nil, nil, 264, nil, nil, 264, 264,
- 264, 264, 264, 264, 264, 264, 264, 264, 264, 264,
- nil, 264, 264, nil, 264, 264, 264, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 264, nil, nil, 264,
- nil, nil, 264, 264, nil, nil, 264, nil, 264, nil,
- 264, nil, 264, nil, nil, 264, nil, nil, nil, nil,
- nil, 264, nil, nil, nil, nil, 264, 264, 264, 264,
- nil, 264, 264, 264, 264, nil, nil, nil, nil, 264,
- 264, nil, nil, nil, 272, 272, 272, 264, 272, 264,
- 264, 264, 272, 272, nil, nil, nil, 272, nil, 272,
- 272, 272, 272, 272, 272, 272, nil, nil, nil, nil,
- nil, 272, 272, 272, 272, 272, 272, 272, nil, nil,
- 272, nil, nil, nil, nil, nil, nil, 272, nil, nil,
- 272, 272, 272, 272, 272, 272, 272, 272, 272, 272,
- 272, 272, nil, 272, 272, nil, 272, 272, 272, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 272, nil,
- nil, 272, nil, nil, 272, 272, nil, nil, 272, nil,
- 272, nil, 272, nil, 272, nil, nil, 272, nil, nil,
- nil, nil, nil, 272, nil, nil, nil, nil, 272, 272,
- 272, 272, nil, 272, 272, 272, 272, nil, nil, nil,
- nil, 272, 272, 272, nil, nil, 279, 279, 279, 272,
- 279, 272, 272, 272, 279, 279, nil, nil, nil, 279,
- nil, 279, 279, 279, 279, 279, 279, 279, nil, nil,
- nil, nil, nil, 279, 279, 279, 279, 279, 279, 279,
- nil, nil, 279, nil, nil, nil, nil, nil, nil, 279,
- nil, nil, 279, 279, 279, 279, 279, 279, 279, 279,
- nil, 279, 279, 279, nil, 279, 279, nil, 279, 279,
- 279, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 279, nil, nil, 279, nil, nil, 279, 279, nil, nil,
- 279, nil, nil, nil, nil, nil, 279, nil, nil, nil,
- nil, nil, nil, nil, nil, 279, nil, nil, nil, nil,
- 279, 279, 279, 279, nil, 279, 279, 279, 279, nil,
- nil, nil, nil, 279, 279, nil, nil, nil, 281, 281,
- 281, 279, 281, 279, 279, 279, 281, 281, nil, nil,
- nil, 281, nil, 281, 281, 281, 281, 281, 281, 281,
- nil, nil, nil, nil, nil, 281, 281, 281, 281, 281,
- 281, 281, nil, nil, 281, nil, nil, nil, nil, nil,
- nil, 281, nil, nil, 281, 281, 281, 281, 281, 281,
- 281, 281, nil, 281, 281, 281, nil, 281, 281, nil,
- 281, 281, 281, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 281, nil, nil, 281, nil, nil, 281, 281,
- nil, nil, 281, nil, nil, nil, nil, nil, 281, nil,
- nil, nil, nil, nil, nil, nil, nil, 281, nil, nil,
- nil, nil, 281, 281, 281, 281, nil, 281, 281, 281,
- 281, nil, nil, nil, nil, 281, 281, nil, nil, nil,
- 284, 284, 284, 281, 284, 281, 281, 281, 284, 284,
- nil, nil, nil, 284, nil, 284, 284, 284, 284, 284,
- 284, 284, nil, nil, nil, nil, nil, 284, 284, 284,
- 284, 284, 284, 284, nil, nil, 284, nil, nil, nil,
- nil, nil, nil, 284, nil, nil, 284, 284, 284, 284,
- 284, 284, 284, 284, nil, 284, 284, 284, nil, 284,
- 284, nil, 284, 284, 284, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 284, nil, nil, 284, nil, nil,
- 284, 284, nil, nil, 284, nil, nil, nil, nil, nil,
- 284, nil, nil, nil, nil, nil, nil, nil, nil, 284,
- nil, nil, nil, nil, 284, 284, 284, 284, nil, 284,
- 284, 284, 284, nil, nil, nil, nil, 284, 284, nil,
- nil, nil, 285, 285, 285, 284, 285, 284, 284, 284,
- 285, 285, nil, nil, nil, 285, nil, 285, 285, 285,
- 285, 285, 285, 285, nil, nil, nil, nil, nil, 285,
- 285, 285, 285, 285, 285, 285, nil, nil, 285, nil,
- nil, nil, nil, nil, nil, 285, nil, nil, 285, 285,
- 285, 285, 285, 285, 285, 285, nil, 285, 285, 285,
- nil, 285, 285, nil, 285, 285, 285, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 285, nil, nil, 285,
- nil, nil, 285, 285, nil, nil, 285, nil, nil, nil,
- nil, nil, 285, nil, nil, nil, nil, nil, nil, nil,
- nil, 285, nil, nil, nil, nil, 285, 285, 285, 285,
- nil, 285, 285, 285, 285, nil, nil, nil, nil, 285,
- 285, nil, nil, nil, nil, nil, nil, 285, nil, 285,
- 285, 285, 290, 290, 290, 290, 290, nil, nil, nil,
- 290, 290, nil, nil, nil, 290, nil, 290, 290, 290,
- 290, 290, 290, 290, nil, nil, nil, nil, nil, 290,
- 290, 290, 290, 290, 290, 290, nil, nil, 290, nil,
- nil, nil, nil, nil, 290, 290, nil, 290, 290, 290,
- 290, 290, 290, 290, 290, 290, nil, 290, 290, 290,
- nil, 290, 290, nil, 290, 290, 290, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 290, nil, nil, 290,
- nil, nil, 290, 290, nil, nil, 290, nil, 290, nil,
- nil, nil, 290, nil, nil, nil, nil, nil, nil, nil,
- nil, 290, nil, nil, nil, nil, 290, 290, 290, 290,
- nil, 290, 290, 290, 290, nil, nil, nil, nil, 290,
- 290, nil, nil, nil, 298, 298, 298, 290, 298, 290,
- 290, 290, 298, 298, nil, nil, nil, 298, nil, 298,
- 298, 298, 298, 298, 298, 298, nil, nil, nil, nil,
- nil, 298, 298, 298, 298, 298, 298, 298, nil, nil,
- 298, nil, nil, nil, nil, nil, nil, 298, nil, nil,
- 298, 298, 298, 298, 298, 298, 298, 298, nil, 298,
- 298, 298, nil, 298, 298, nil, nil, nil, 298, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 298, nil,
- nil, 298, nil, nil, 298, 298, nil, nil, 298, nil,
- 895, nil, 895, 895, 895, 895, 895, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 895, nil, 298, 298,
- 298, 298, nil, 298, 298, 298, 298, nil, nil, nil,
- nil, 298, 298, nil, nil, nil, 298, nil, 895, 298,
- nil, 298, 298, 298, 315, 315, 315, nil, 315, 895,
- 895, nil, 315, 315, 895, nil, nil, 315, nil, 315,
- 315, 315, 315, 315, 315, 315, nil, nil, nil, nil,
- nil, 315, 315, 315, 315, 315, 315, 315, nil, nil,
- 315, nil, nil, nil, nil, nil, nil, 315, nil, nil,
- 315, 315, 315, 315, 315, 315, 315, 315, nil, 315,
- 315, 315, nil, 315, 315, nil, nil, nil, 315, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 315, nil,
- nil, 315, nil, nil, 315, 315, nil, nil, 315, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 315, 315,
- 315, 315, nil, 315, 315, 315, 315, nil, nil, nil,
- nil, 315, 315, nil, nil, nil, 323, 323, 323, 315,
- 323, 315, 315, 315, 323, 323, nil, nil, nil, 323,
- nil, 323, 323, 323, 323, 323, 323, 323, nil, nil,
- nil, nil, nil, 323, 323, 323, 323, 323, 323, 323,
- nil, nil, 323, nil, nil, nil, nil, nil, nil, 323,
- nil, nil, 323, 323, 323, 323, 323, 323, 323, 323,
- nil, 323, 323, 323, nil, 323, 323, nil, 323, 323,
- 323, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 323, nil, nil, 323, 323, nil, 323, 323, nil, nil,
- 323, nil, nil, nil, nil, nil, 323, nil, nil, nil,
- nil, nil, nil, nil, nil, 323, nil, nil, nil, nil,
- 323, 323, 323, 323, nil, 323, 323, 323, 323, nil,
- nil, nil, nil, 323, 323, nil, nil, nil, 325, 325,
- 325, 323, 325, 323, 323, 323, 325, 325, nil, nil,
- nil, 325, nil, 325, 325, 325, 325, 325, 325, 325,
- nil, nil, nil, nil, nil, 325, 325, 325, 325, 325,
- 325, 325, nil, nil, 325, nil, nil, nil, nil, nil,
- nil, 325, nil, nil, 325, 325, 325, 325, 325, 325,
- 325, 325, nil, 325, 325, 325, nil, 325, 325, nil,
- 325, 325, 325, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 325, nil, nil, 325, nil, nil, 325, 325,
- nil, nil, 325, nil, nil, nil, nil, nil, 325, nil,
- nil, nil, nil, nil, nil, nil, nil, 325, nil, nil,
- nil, nil, 325, 325, 325, 325, nil, 325, 325, 325,
- 325, nil, nil, nil, nil, 325, 325, nil, nil, nil,
- 340, 340, 340, 325, 340, 325, 325, 325, 340, 340,
- nil, nil, nil, 340, nil, 340, 340, 340, 340, 340,
- 340, 340, nil, nil, nil, nil, nil, 340, 340, 340,
- 340, 340, 340, 340, nil, nil, 340, nil, nil, nil,
- nil, nil, nil, 340, nil, nil, 340, 340, 340, 340,
- 340, 340, 340, 340, nil, 340, 340, 340, nil, 340,
- 340, nil, 340, 340, 340, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 340, nil, nil, 340, nil, nil,
- 340, 340, nil, nil, 340, nil, nil, nil, nil, nil,
- 340, nil, nil, nil, nil, nil, nil, nil, nil, 340,
- nil, nil, nil, nil, 340, 340, 340, 340, nil, 340,
- 340, 340, 340, nil, nil, nil, nil, 340, 340, nil,
- nil, nil, 341, 341, 341, 340, 341, 340, 340, 340,
- 341, 341, nil, nil, nil, 341, nil, 341, 341, 341,
- 341, 341, 341, 341, nil, nil, nil, nil, nil, 341,
- 341, 341, 341, 341, 341, 341, nil, nil, 341, nil,
- nil, nil, nil, nil, nil, 341, nil, nil, 341, 341,
- 341, 341, 341, 341, 341, 341, nil, 341, 341, 341,
- nil, 341, 341, nil, 341, 341, 341, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 341, nil, nil, 341,
- nil, nil, 341, 341, nil, nil, 341, nil, nil, nil,
- nil, nil, 341, nil, nil, nil, nil, nil, nil, nil,
- nil, 341, nil, nil, nil, nil, 341, 341, 341, 341,
- nil, 341, 341, 341, 341, nil, nil, nil, nil, 341,
- 341, nil, nil, nil, 360, 360, 360, 341, 360, 341,
- 341, 341, 360, 360, nil, nil, nil, 360, nil, 360,
- 360, 360, 360, 360, 360, 360, nil, nil, nil, nil,
- nil, 360, 360, 360, 360, 360, 360, 360, nil, nil,
- 360, nil, nil, nil, nil, nil, nil, 360, nil, nil,
- 360, 360, 360, 360, 360, 360, 360, 360, nil, 360,
- 360, 360, nil, 360, 360, nil, 360, 360, 360, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 360, nil,
- nil, 360, nil, nil, 360, 360, nil, nil, 360, nil,
- nil, nil, nil, nil, 360, nil, nil, nil, nil, nil,
- nil, nil, nil, 360, nil, nil, nil, nil, 360, 360,
- 360, 360, nil, 360, 360, 360, 360, nil, nil, nil,
- nil, 360, 360, nil, nil, nil, 376, 376, 376, 360,
- 376, 360, 360, 360, 376, 376, nil, nil, nil, 376,
- nil, 376, 376, 376, 376, 376, 376, 376, nil, nil,
- nil, nil, nil, 376, 376, 376, 376, 376, 376, 376,
- nil, nil, 376, nil, nil, nil, nil, nil, nil, 376,
- nil, nil, 376, 376, 376, 376, 376, 376, 376, 376,
- nil, 376, 376, 376, nil, 376, 376, nil, 376, 376,
- 376, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 376, nil, nil, 376, nil, nil, 376, 376, nil, nil,
- 376, nil, nil, nil, nil, nil, 376, nil, nil, nil,
- nil, nil, nil, nil, nil, 376, nil, nil, nil, nil,
- 376, 376, 376, 376, nil, 376, 376, 376, 376, nil,
- nil, nil, nil, 376, 376, nil, nil, nil, 404, 404,
- 404, 376, 404, 376, 376, 376, 404, 404, nil, nil,
- nil, 404, nil, 404, 404, 404, 404, 404, 404, 404,
- nil, nil, nil, nil, nil, 404, 404, 404, 404, 404,
- 404, 404, nil, nil, 404, nil, nil, nil, nil, nil,
- nil, 404, nil, nil, 404, 404, 404, 404, 404, 404,
- 404, 404, nil, 404, 404, 404, nil, 404, 404, nil,
- 404, 404, 404, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 404, nil, nil, 404, nil, nil, 404, 404,
- nil, nil, 404, nil, nil, nil, nil, nil, 404, nil,
- nil, nil, nil, nil, nil, nil, nil, 404, nil, nil,
- nil, nil, 404, 404, 404, 404, nil, 404, 404, 404,
- 404, nil, nil, nil, nil, 404, 404, nil, nil, nil,
- 442, 442, 442, 404, 442, 404, 404, 404, 442, 442,
- nil, nil, nil, 442, nil, 442, 442, 442, 442, 442,
- 442, 442, nil, nil, nil, nil, nil, 442, 442, 442,
- 442, 442, 442, 442, nil, nil, 442, nil, nil, nil,
- nil, nil, nil, 442, nil, nil, 442, 442, 442, 442,
- 442, 442, 442, 442, 442, 442, 442, 442, nil, 442,
- 442, nil, 442, 442, 442, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 442, nil, nil, 442, nil, nil,
- 442, 442, nil, nil, 442, nil, 442, nil, 442, nil,
- 442, nil, nil, 442, nil, nil, nil, nil, nil, 442,
- nil, nil, nil, nil, 442, 442, 442, 442, nil, 442,
- 442, 442, 442, nil, nil, nil, nil, 442, 442, nil,
- nil, nil, 444, 444, 444, 442, 444, 442, 442, 442,
- 444, 444, nil, nil, nil, 444, nil, 444, 444, 444,
- 444, 444, 444, 444, nil, nil, nil, nil, nil, 444,
- 444, 444, 444, 444, 444, 444, nil, nil, 444, nil,
- nil, nil, nil, nil, nil, 444, nil, nil, 444, 444,
- 444, 444, 444, 444, 444, 444, nil, 444, 444, 444,
- nil, 444, 444, nil, 444, 444, 444, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 444, nil, nil, 444,
- nil, nil, 444, 444, nil, nil, 444, nil, nil, nil,
- nil, nil, 444, nil, nil, nil, nil, nil, nil, nil,
- nil, 444, nil, nil, nil, nil, 444, 444, 444, 444,
- nil, 444, 444, 444, 444, nil, nil, nil, nil, 444,
- 444, nil, nil, nil, 445, 445, 445, 444, 445, 444,
- 444, 444, 445, 445, nil, nil, nil, 445, nil, 445,
- 445, 445, 445, 445, 445, 445, nil, nil, nil, nil,
- nil, 445, 445, 445, 445, 445, 445, 445, nil, nil,
- 445, nil, nil, nil, nil, nil, nil, 445, nil, nil,
- 445, 445, 445, 445, 445, 445, 445, 445, nil, 445,
- 445, 445, nil, 445, 445, nil, 445, 445, 445, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 445, nil,
- nil, 445, nil, nil, 445, 445, nil, nil, 445, nil,
- nil, nil, nil, nil, 445, nil, nil, nil, nil, nil,
- nil, nil, nil, 445, nil, nil, nil, nil, 445, 445,
- 445, 445, nil, 445, 445, 445, 445, nil, nil, nil,
- nil, 445, 445, nil, nil, nil, 446, 446, 446, 445,
- 446, 445, 445, 445, 446, 446, nil, nil, nil, 446,
- nil, 446, 446, 446, 446, 446, 446, 446, nil, nil,
- nil, nil, nil, 446, 446, 446, 446, 446, 446, 446,
- nil, nil, 446, nil, nil, nil, nil, nil, nil, 446,
- nil, nil, 446, 446, 446, 446, 446, 446, 446, 446,
- nil, 446, 446, 446, nil, 446, 446, nil, 446, 446,
- 446, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 446, nil, nil, 446, nil, nil, 446, 446, nil, nil,
- 446, nil, nil, nil, nil, nil, 446, nil, nil, nil,
- nil, nil, nil, nil, nil, 446, nil, nil, nil, nil,
- 446, 446, 446, 446, nil, 446, 446, 446, 446, nil,
- nil, nil, nil, 446, 446, nil, nil, nil, 472, 472,
- 472, 446, 472, 446, 446, 446, 472, 472, nil, nil,
- nil, 472, nil, 472, 472, 472, 472, 472, 472, 472,
- nil, nil, nil, nil, nil, 472, 472, 472, 472, 472,
- 472, 472, nil, nil, 472, nil, nil, nil, nil, nil,
- nil, 472, nil, nil, 472, 472, 472, 472, 472, 472,
- 472, 472, nil, 472, 472, 472, nil, 472, 472, nil,
- 472, 472, 472, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 472, nil, nil, 472, nil, nil, 472, 472,
- nil, nil, 472, nil, nil, nil, nil, nil, 472, nil,
- nil, nil, nil, nil, nil, nil, nil, 472, nil, nil,
- nil, nil, 472, 472, 472, 472, nil, 472, 472, 472,
- 472, nil, nil, nil, nil, 472, 472, nil, nil, nil,
- 486, 486, 486, 472, 486, 472, 472, 472, 486, 486,
- nil, nil, nil, 486, nil, 486, 486, 486, 486, 486,
- 486, 486, nil, nil, nil, nil, nil, 486, 486, 486,
- 486, 486, 486, 486, nil, nil, 486, nil, nil, nil,
- nil, nil, nil, 486, nil, nil, 486, 486, 486, 486,
- 486, 486, 486, 486, 486, 486, 486, 486, nil, 486,
- 486, nil, 486, 486, 486, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 486, nil, nil, 486, nil, nil,
- 486, 486, nil, nil, 486, nil, 486, nil, 486, nil,
- 486, nil, nil, 486, nil, nil, nil, nil, nil, 486,
- nil, nil, nil, nil, 486, 486, 486, 486, nil, 486,
- 486, 486, 486, nil, nil, nil, nil, 486, 486, nil,
- nil, nil, 488, 488, 488, 486, 488, 486, 486, 486,
- 488, 488, nil, nil, nil, 488, nil, 488, 488, 488,
- 488, 488, 488, 488, nil, nil, nil, nil, nil, 488,
- 488, 488, 488, 488, 488, 488, nil, nil, 488, nil,
- nil, nil, nil, nil, nil, 488, nil, nil, 488, 488,
- 488, 488, 488, 488, 488, 488, 488, 488, 488, 488,
- nil, 488, 488, nil, 488, 488, 488, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 488, nil, nil, 488,
- nil, nil, 488, 488, nil, nil, 488, nil, nil, nil,
- 488, nil, 488, nil, nil, 488, nil, nil, nil, nil,
- nil, 488, nil, nil, nil, nil, 488, 488, 488, 488,
- nil, 488, 488, 488, 488, nil, nil, nil, nil, 488,
- 488, nil, nil, nil, 490, 490, 490, 488, 490, 488,
- 488, 488, 490, 490, nil, nil, nil, 490, nil, 490,
- 490, 490, 490, 490, 490, 490, nil, nil, nil, nil,
- nil, 490, 490, 490, 490, 490, 490, 490, nil, nil,
- 490, nil, nil, nil, nil, nil, nil, 490, nil, nil,
- 490, 490, 490, 490, 490, 490, 490, 490, nil, 490,
- 490, 490, nil, 490, 490, nil, 490, 490, 490, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 490, nil,
- nil, 490, nil, nil, 490, 490, nil, nil, 490, nil,
- nil, nil, nil, nil, 490, nil, nil, nil, nil, nil,
- nil, nil, nil, 490, nil, nil, nil, nil, 490, 490,
- 490, 490, nil, 490, 490, 490, 490, nil, nil, nil,
- nil, 490, 490, nil, nil, nil, nil, nil, nil, 490,
- nil, 490, 490, 490, 496, 496, 496, 496, 496, nil,
- nil, nil, 496, 496, nil, nil, nil, 496, nil, 496,
- 496, 496, 496, 496, 496, 496, nil, nil, nil, nil,
- nil, 496, 496, 496, 496, 496, 496, 496, nil, nil,
- 496, nil, nil, nil, nil, nil, 496, 496, 496, 496,
- 496, 496, 496, 496, 496, 496, 496, 496, nil, 496,
- 496, 496, nil, 496, 496, nil, 496, 496, 496, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 496, nil,
- nil, 496, nil, nil, 496, 496, nil, nil, 496, nil,
- 496, nil, nil, nil, 496, nil, nil, nil, nil, nil,
- nil, nil, nil, 496, nil, nil, nil, nil, 496, 496,
- 496, 496, nil, 496, 496, 496, 496, nil, nil, nil,
- nil, 496, 496, nil, nil, nil, nil, nil, 496, 496,
- nil, 496, 496, 496, 504, 504, 504, nil, 504, nil,
- nil, nil, 504, 504, nil, nil, nil, 504, nil, 504,
- 504, 504, 504, 504, 504, 504, nil, nil, nil, nil,
- nil, 504, 504, 504, 504, 504, 504, 504, nil, nil,
- 504, nil, nil, nil, nil, nil, nil, 504, nil, nil,
- 504, 504, 504, 504, 504, 504, 504, 504, nil, 504,
- 504, 504, nil, 504, 504, nil, nil, nil, 504, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 504, nil,
- nil, 504, nil, nil, 504, 504, nil, nil, 504, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 504, 504,
- 504, 504, nil, 504, 504, 504, 504, nil, nil, nil,
- nil, 504, 504, nil, nil, nil, 506, 506, 506, 504,
- 506, 504, 504, 504, 506, 506, nil, nil, nil, 506,
- nil, 506, 506, 506, 506, 506, 506, 506, nil, nil,
- nil, nil, nil, 506, 506, 506, 506, 506, 506, 506,
- nil, nil, 506, nil, nil, nil, nil, nil, nil, 506,
- nil, nil, 506, 506, 506, 506, 506, 506, 506, 506,
- 506, 506, 506, 506, nil, 506, 506, nil, 506, 506,
- 506, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 506, nil, nil, 506, nil, nil, 506, 506, nil, nil,
- 506, nil, 506, nil, 506, nil, 506, nil, nil, 506,
- nil, nil, nil, nil, nil, 506, nil, nil, nil, nil,
- 506, 506, 506, 506, nil, 506, 506, 506, 506, nil,
- nil, nil, nil, 506, 506, nil, nil, nil, 512, 512,
- 512, 506, 512, 506, 506, 506, 512, 512, nil, nil,
- nil, 512, nil, 512, 512, 512, 512, 512, 512, 512,
- nil, nil, nil, nil, nil, 512, 512, 512, 512, 512,
- 512, 512, nil, nil, 512, nil, nil, nil, nil, nil,
- nil, 512, nil, nil, 512, 512, 512, 512, 512, 512,
- 512, 512, nil, 512, 512, 512, nil, 512, 512, nil,
- nil, nil, 512, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 512, nil, nil, 512, nil, nil, 512, 512,
- nil, nil, 512, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 512, 512, 512, 512, nil, 512, 512, 512,
- 512, nil, nil, nil, nil, 512, 512, nil, nil, nil,
- 515, 515, 515, 512, 515, 512, 512, 512, 515, 515,
- nil, nil, nil, 515, nil, 515, 515, 515, 515, 515,
- 515, 515, nil, nil, nil, nil, nil, 515, 515, 515,
- 515, 515, 515, 515, nil, nil, 515, nil, nil, nil,
- nil, nil, nil, 515, nil, nil, 515, 515, 515, 515,
- 515, 515, 515, 515, nil, 515, 515, 515, nil, 515,
- 515, nil, 515, 515, 515, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 515, nil, nil, 515, nil, nil,
- 515, 515, nil, nil, 515, nil, nil, nil, nil, nil,
- 515, nil, nil, nil, nil, nil, nil, nil, nil, 515,
- nil, nil, nil, nil, 515, 515, 515, 515, nil, 515,
- 515, 515, 515, nil, nil, nil, nil, 515, 515, nil,
- nil, nil, 516, 516, 516, 515, 516, 515, 515, 515,
- 516, 516, nil, nil, nil, 516, nil, 516, 516, 516,
- 516, 516, 516, 516, nil, nil, nil, nil, nil, 516,
- 516, 516, 516, 516, 516, 516, nil, nil, 516, nil,
- nil, nil, nil, nil, nil, 516, nil, nil, 516, 516,
- 516, 516, 516, 516, 516, 516, nil, 516, 516, 516,
- nil, 516, 516, nil, 516, 516, 516, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 516, nil, nil, 516,
- nil, nil, 516, 516, nil, nil, 516, nil, nil, nil,
- nil, nil, 516, nil, nil, nil, nil, nil, nil, nil,
- nil, 516, nil, nil, nil, nil, 516, 516, 516, 516,
- nil, 516, 516, 516, 516, nil, nil, nil, nil, 516,
- 516, nil, nil, nil, 520, 520, 520, 516, 520, 516,
- 516, 516, 520, 520, nil, nil, nil, 520, nil, 520,
- 520, 520, 520, 520, 520, 520, nil, nil, nil, nil,
- nil, 520, 520, 520, 520, 520, 520, 520, nil, nil,
- 520, nil, nil, nil, nil, nil, nil, 520, nil, nil,
- 520, 520, 520, 520, 520, 520, 520, 520, nil, 520,
- 520, 520, nil, 520, 520, nil, 520, 520, 520, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 520, nil,
- nil, 520, nil, nil, 520, 520, nil, nil, 520, nil,
- nil, nil, nil, nil, 520, nil, nil, nil, nil, nil,
- nil, nil, nil, 520, nil, nil, nil, nil, 520, 520,
- 520, 520, nil, 520, 520, 520, 520, nil, nil, nil,
- nil, 520, 520, nil, nil, nil, 526, 526, 526, 520,
- 526, 520, 520, 520, 526, 526, nil, nil, nil, 526,
- nil, 526, 526, 526, 526, 526, 526, 526, nil, nil,
- nil, nil, nil, 526, 526, 526, 526, 526, 526, 526,
- nil, nil, 526, nil, nil, nil, nil, nil, nil, 526,
- nil, nil, 526, 526, 526, 526, 526, 526, 526, 526,
- 526, 526, 526, 526, nil, 526, 526, nil, 526, 526,
- 526, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 526, nil, nil, 526, nil, nil, 526, 526, nil, nil,
- 526, nil, 526, nil, nil, nil, 526, nil, nil, 526,
- nil, nil, nil, nil, nil, 526, nil, nil, nil, nil,
- 526, 526, 526, 526, nil, 526, 526, 526, 526, nil,
- nil, nil, nil, 526, 526, nil, nil, nil, 529, 529,
- 529, 526, 529, 526, 526, 526, 529, 529, nil, nil,
- nil, 529, nil, 529, 529, 529, 529, 529, 529, 529,
- nil, nil, nil, nil, nil, 529, 529, 529, 529, 529,
- 529, 529, nil, nil, 529, nil, nil, nil, nil, nil,
- nil, 529, nil, nil, 529, 529, 529, 529, 529, 529,
- 529, 529, 529, 529, 529, 529, nil, 529, 529, nil,
- 529, 529, 529, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 529, nil, nil, 529, nil, nil, 529, 529,
- nil, nil, 529, nil, nil, nil, nil, nil, 529, nil,
- nil, 529, nil, nil, nil, nil, nil, 529, nil, nil,
- nil, nil, 529, 529, 529, 529, nil, 529, 529, 529,
- 529, nil, nil, nil, nil, 529, 529, nil, nil, nil,
- 554, 554, 554, 529, 554, 529, 529, 529, 554, 554,
- nil, nil, nil, 554, nil, 554, 554, 554, 554, 554,
- 554, 554, nil, nil, nil, nil, nil, 554, 554, 554,
- 554, 554, 554, 554, nil, nil, 554, nil, nil, nil,
- nil, nil, nil, 554, nil, nil, 554, 554, 554, 554,
- 554, 554, 554, 554, nil, 554, 554, 554, nil, 554,
- 554, nil, 554, 554, 554, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 554, nil, nil, 554, nil, nil,
- 554, 554, nil, nil, 554, nil, nil, nil, nil, nil,
- 554, nil, nil, nil, nil, nil, nil, nil, nil, 554,
- nil, nil, nil, nil, 554, 554, 554, 554, nil, 554,
- 554, 554, 554, nil, nil, nil, nil, 554, 554, nil,
- nil, nil, 574, 574, 574, 554, 574, 554, 554, 554,
- 574, 574, nil, nil, nil, 574, nil, 574, 574, 574,
- 574, 574, 574, 574, nil, nil, nil, nil, nil, 574,
- 574, 574, 574, 574, 574, 574, nil, nil, 574, nil,
- nil, nil, nil, nil, nil, 574, nil, nil, 574, 574,
- 574, 574, 574, 574, 574, 574, nil, 574, 574, 574,
- nil, 574, 574, nil, 574, 574, 574, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 574, nil, nil, 574,
- nil, nil, 574, 574, nil, nil, 574, nil, 574, nil,
- nil, nil, 574, nil, nil, nil, nil, nil, nil, nil,
- nil, 574, nil, nil, nil, nil, 574, 574, 574, 574,
- nil, 574, 574, 574, 574, nil, nil, nil, nil, 574,
- 574, nil, nil, nil, 575, 575, 575, 574, 575, 574,
- 574, 574, 575, 575, nil, nil, nil, 575, nil, 575,
- 575, 575, 575, 575, 575, 575, nil, nil, nil, nil,
- nil, 575, 575, 575, 575, 575, 575, 575, nil, nil,
- 575, nil, nil, nil, nil, nil, nil, 575, nil, nil,
- 575, 575, 575, 575, 575, 575, 575, 575, 575, 575,
- 575, 575, nil, 575, 575, nil, 575, 575, 575, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 575, nil,
- nil, 575, nil, nil, 575, 575, nil, nil, 575, nil,
- 575, nil, 575, nil, 575, nil, nil, 575, nil, nil,
- nil, nil, nil, 575, nil, nil, nil, nil, 575, 575,
- 575, 575, nil, 575, 575, 575, 575, nil, nil, nil,
- nil, 575, 575, nil, nil, nil, 585, 585, 585, 575,
- 585, 575, 575, 575, 585, 585, nil, nil, nil, 585,
- nil, 585, 585, 585, 585, 585, 585, 585, nil, nil,
- nil, nil, nil, 585, 585, 585, 585, 585, 585, 585,
- nil, nil, 585, nil, nil, nil, nil, nil, nil, 585,
- nil, nil, 585, 585, 585, 585, 585, 585, 585, 585,
- 585, 585, 585, 585, nil, 585, 585, nil, 585, 585,
- 585, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 585, nil, nil, 585, nil, nil, 585, 585, nil, nil,
- 585, nil, 585, nil, 585, nil, 585, nil, nil, 585,
- nil, nil, nil, nil, nil, 585, nil, nil, nil, nil,
- 585, 585, 585, 585, nil, 585, 585, 585, 585, nil,
- nil, nil, nil, 585, 585, nil, nil, nil, 619, 619,
- 619, 585, 619, 585, 585, 585, 619, 619, nil, nil,
- nil, 619, nil, 619, 619, 619, 619, 619, 619, 619,
- nil, nil, nil, nil, nil, 619, 619, 619, 619, 619,
- 619, 619, nil, nil, 619, nil, nil, nil, nil, nil,
- nil, 619, nil, nil, 619, 619, 619, 619, 619, 619,
- 619, 619, nil, 619, 619, 619, nil, 619, 619, nil,
- 619, 619, 619, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 619, nil, nil, 619, nil, nil, 619, 619,
- nil, nil, 619, nil, 619, nil, nil, nil, 619, nil,
- nil, nil, nil, nil, nil, nil, nil, 619, nil, nil,
- nil, nil, 619, 619, 619, 619, nil, 619, 619, 619,
- 619, nil, nil, nil, nil, 619, 619, nil, nil, nil,
- 620, 620, 620, 619, 620, 619, 619, 619, 620, 620,
- nil, nil, nil, 620, nil, 620, 620, 620, 620, 620,
- 620, 620, nil, nil, nil, nil, nil, 620, 620, 620,
- 620, 620, 620, 620, nil, nil, 620, nil, nil, nil,
- nil, nil, nil, 620, nil, nil, 620, 620, 620, 620,
- 620, 620, 620, 620, nil, 620, 620, 620, nil, 620,
- 620, nil, 620, 620, 620, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 620, nil, nil, 620, nil, nil,
- 620, 620, nil, nil, 620, nil, nil, nil, nil, nil,
- 620, nil, nil, nil, nil, nil, nil, nil, nil, 620,
- nil, nil, nil, nil, 620, 620, 620, 620, nil, 620,
- 620, 620, 620, nil, nil, nil, nil, 620, 620, nil,
- nil, nil, 623, 623, 623, 620, 623, 620, 620, 620,
- 623, 623, nil, nil, nil, 623, nil, 623, 623, 623,
- 623, 623, 623, 623, nil, nil, nil, nil, nil, 623,
- 623, 623, 623, 623, 623, 623, nil, nil, 623, nil,
- nil, nil, nil, nil, nil, 623, nil, nil, 623, 623,
- 623, 623, 623, 623, 623, 623, 623, 623, 623, 623,
- nil, 623, 623, nil, 623, 623, 623, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 623, nil, nil, 623,
- nil, nil, 623, 623, nil, nil, 623, nil, 623, nil,
- 623, nil, 623, nil, nil, 623, nil, nil, nil, nil,
- nil, 623, nil, nil, nil, nil, 623, 623, 623, 623,
- nil, 623, 623, 623, 623, nil, nil, nil, nil, 623,
- 623, nil, nil, nil, 624, 624, 624, 623, 624, 623,
- 623, 623, 624, 624, nil, nil, nil, 624, nil, 624,
- 624, 624, 624, 624, 624, 624, nil, nil, nil, nil,
- nil, 624, 624, 624, 624, 624, 624, 624, nil, nil,
- 624, nil, nil, nil, nil, nil, nil, 624, nil, nil,
- 624, 624, 624, 624, 624, 624, 624, 624, 624, 624,
- 624, 624, nil, 624, 624, nil, 624, 624, 624, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 624, nil,
- nil, 624, nil, nil, 624, 624, nil, nil, 624, nil,
- nil, nil, 624, nil, 624, nil, nil, 624, nil, nil,
- nil, nil, nil, 624, nil, nil, nil, nil, 624, 624,
- 624, 624, nil, 624, 624, 624, 624, nil, nil, nil,
- nil, 624, 624, nil, nil, nil, 625, 625, 625, 624,
- 625, 624, 624, 624, 625, 625, nil, nil, nil, 625,
- nil, 625, 625, 625, 625, 625, 625, 625, nil, nil,
- nil, nil, nil, 625, 625, 625, 625, 625, 625, 625,
- nil, nil, 625, nil, nil, nil, nil, nil, nil, 625,
- nil, nil, 625, 625, 625, 625, 625, 625, 625, 625,
- nil, 625, 625, 625, nil, 625, 625, nil, 625, 625,
- 625, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 625, nil, nil, 625, nil, nil, 625, 625, nil, nil,
- 625, nil, nil, nil, nil, nil, 625, nil, nil, nil,
- nil, nil, nil, nil, nil, 625, nil, nil, nil, nil,
- 625, 625, 625, 625, nil, 625, 625, 625, 625, nil,
- nil, nil, nil, 625, 625, nil, nil, nil, 626, 626,
- 626, 625, 626, 625, 625, 625, 626, 626, nil, nil,
- nil, 626, nil, 626, 626, 626, 626, 626, 626, 626,
- nil, nil, nil, nil, nil, 626, 626, 626, 626, 626,
- 626, 626, nil, nil, 626, nil, nil, nil, nil, nil,
- nil, 626, nil, nil, 626, 626, 626, 626, 626, 626,
- 626, 626, nil, 626, 626, 626, nil, 626, 626, nil,
- 626, 626, 626, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 626, nil, nil, 626, nil, nil, 626, 626,
- nil, nil, 626, nil, nil, nil, nil, nil, 626, nil,
- nil, nil, nil, nil, nil, nil, nil, 626, nil, nil,
- nil, nil, 626, 626, 626, 626, nil, 626, 626, 626,
- 626, nil, nil, nil, nil, 626, 626, nil, nil, nil,
- 630, 630, 630, 626, 630, 626, 626, 626, 630, 630,
- nil, nil, nil, 630, nil, 630, 630, 630, 630, 630,
- 630, 630, nil, nil, nil, nil, nil, 630, 630, 630,
- 630, 630, 630, 630, nil, nil, 630, nil, nil, nil,
- nil, nil, nil, 630, nil, nil, 630, 630, 630, 630,
- 630, 630, 630, 630, nil, 630, 630, 630, nil, 630,
- 630, nil, 630, 630, 630, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 630, nil, nil, 630, nil, nil,
- 630, 630, nil, nil, 630, nil, nil, nil, nil, nil,
- 630, nil, nil, nil, nil, nil, nil, nil, nil, 630,
- nil, nil, nil, nil, 630, 630, 630, 630, nil, 630,
- 630, 630, 630, nil, nil, nil, nil, 630, 630, nil,
- nil, nil, 631, 631, 631, 630, 631, 630, 630, 630,
- 631, 631, nil, nil, nil, 631, nil, 631, 631, 631,
- 631, 631, 631, 631, nil, nil, nil, nil, nil, 631,
- 631, 631, 631, 631, 631, 631, nil, nil, 631, nil,
- nil, nil, nil, nil, nil, 631, nil, nil, 631, 631,
- 631, 631, 631, 631, 631, 631, nil, 631, 631, 631,
- nil, 631, 631, nil, 631, 631, 631, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 631, nil, nil, 631,
- nil, nil, 631, 631, nil, nil, 631, nil, nil, nil,
- nil, nil, 631, nil, nil, nil, nil, nil, nil, nil,
- nil, 631, nil, nil, nil, nil, 631, 631, 631, 631,
- nil, 631, 631, 631, 631, nil, nil, nil, nil, 631,
- 631, nil, nil, nil, 634, 634, 634, 631, 634, 631,
- 631, 631, 634, 634, nil, nil, nil, 634, nil, 634,
- 634, 634, 634, 634, 634, 634, nil, nil, nil, nil,
- nil, 634, 634, 634, 634, 634, 634, 634, nil, nil,
- 634, nil, nil, nil, nil, nil, nil, 634, nil, nil,
- 634, 634, 634, 634, 634, 634, 634, 634, nil, 634,
- 634, 634, nil, 634, 634, nil, 634, 634, 634, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 634, nil,
- nil, 634, nil, nil, 634, 634, nil, nil, 634, nil,
- nil, nil, nil, nil, 634, nil, nil, nil, nil, nil,
- nil, nil, nil, 634, nil, nil, nil, nil, 634, 634,
- 634, 634, nil, 634, 634, 634, 634, nil, nil, nil,
- nil, 634, 634, nil, nil, nil, 635, 635, 635, 634,
- 635, 634, 634, 634, 635, 635, nil, nil, nil, 635,
- nil, 635, 635, 635, 635, 635, 635, 635, nil, nil,
- nil, nil, nil, 635, 635, 635, 635, 635, 635, 635,
- nil, nil, 635, nil, nil, nil, nil, nil, nil, 635,
- nil, nil, 635, 635, 635, 635, 635, 635, 635, 635,
- nil, 635, 635, 635, nil, 635, 635, nil, 635, 635,
- 635, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 635, nil, nil, 635, nil, nil, 635, 635, nil, nil,
- 635, nil, nil, nil, nil, nil, 635, nil, nil, nil,
- nil, nil, nil, nil, nil, 635, nil, nil, nil, nil,
- 635, 635, 635, 635, nil, 635, 635, 635, 635, nil,
- nil, nil, nil, 635, 635, nil, nil, nil, 659, 659,
- 659, 635, 659, 635, 635, 635, 659, 659, nil, nil,
- nil, 659, nil, 659, 659, 659, 659, 659, 659, 659,
- nil, nil, nil, nil, nil, 659, 659, 659, 659, 659,
- 659, 659, nil, nil, 659, nil, nil, nil, nil, nil,
- nil, 659, nil, nil, 659, 659, 659, 659, 659, 659,
- 659, 659, nil, 659, 659, 659, nil, 659, 659, nil,
- 659, 659, 659, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 659, nil, nil, 659, nil, nil, 659, 659,
- nil, nil, 659, nil, nil, nil, nil, nil, 659, nil,
- nil, nil, nil, nil, nil, nil, nil, 659, nil, nil,
- nil, nil, 659, 659, 659, 659, nil, 659, 659, 659,
- 659, nil, nil, nil, nil, 659, 659, nil, nil, nil,
- 662, 662, 662, 659, 662, 659, 659, 659, 662, 662,
- nil, nil, nil, 662, nil, 662, 662, 662, 662, 662,
- 662, 662, nil, nil, nil, nil, nil, 662, 662, 662,
- 662, 662, 662, 662, nil, nil, 662, nil, nil, nil,
- nil, nil, nil, 662, nil, nil, 662, 662, 662, 662,
- 662, 662, 662, 662, nil, 662, 662, 662, nil, 662,
- 662, nil, 662, 662, 662, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 662, nil, nil, 662, nil, nil,
- 662, 662, nil, nil, 662, nil, nil, nil, nil, nil,
- 662, nil, nil, nil, nil, nil, nil, nil, nil, 662,
- nil, nil, nil, nil, 662, 662, 662, 662, nil, 662,
- 662, 662, 662, nil, nil, nil, nil, 662, 662, nil,
- nil, nil, 666, 666, 666, 662, 666, 662, 662, 662,
- 666, 666, nil, nil, nil, 666, nil, 666, 666, 666,
- 666, 666, 666, 666, nil, nil, nil, nil, nil, 666,
- 666, 666, 666, 666, 666, 666, nil, nil, 666, nil,
- nil, nil, nil, nil, nil, 666, nil, nil, 666, 666,
- 666, 666, 666, 666, 666, 666, nil, 666, 666, 666,
- nil, 666, 666, nil, nil, nil, 666, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 666, nil, nil, 666,
- nil, nil, 666, 666, nil, nil, 666, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 666, 666, 666, 666,
- nil, 666, 666, 666, 666, nil, nil, nil, nil, 666,
- 666, nil, nil, nil, 677, 677, 677, 666, 677, 666,
- 666, 666, 677, 677, nil, nil, nil, 677, nil, 677,
- 677, 677, 677, 677, 677, 677, nil, nil, nil, nil,
- nil, 677, 677, 677, 677, 677, 677, 677, nil, nil,
- 677, nil, nil, nil, nil, nil, nil, 677, nil, nil,
- 677, 677, 677, 677, 677, 677, 677, 677, nil, 677,
- 677, 677, nil, 677, 677, nil, nil, nil, 677, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 677, nil,
- nil, 677, nil, nil, 677, 677, nil, nil, 677, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 677, 677,
- 677, 677, nil, 677, 677, 677, 677, nil, nil, nil,
- nil, 677, 677, nil, nil, nil, 682, 682, 682, 677,
- 682, 677, 677, 677, 682, 682, nil, nil, nil, 682,
- nil, 682, 682, 682, 682, 682, 682, 682, nil, nil,
- nil, nil, nil, 682, 682, 682, 682, 682, 682, 682,
- nil, nil, 682, nil, nil, nil, nil, nil, nil, 682,
- nil, nil, 682, 682, 682, 682, 682, 682, 682, 682,
- nil, 682, 682, 682, nil, 682, 682, nil, 682, 682,
- 682, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 682, nil, nil, 682, nil, nil, 682, 682, nil, nil,
- 682, nil, 682, nil, nil, nil, 682, nil, nil, nil,
- nil, nil, nil, nil, nil, 682, nil, nil, nil, nil,
- 682, 682, 682, 682, nil, 682, 682, 682, 682, nil,
- nil, nil, nil, 682, 682, nil, nil, nil, 699, 699,
- 699, 682, 699, 682, 682, 682, 699, 699, nil, nil,
- nil, 699, nil, 699, 699, 699, 699, 699, 699, 699,
- nil, nil, nil, nil, nil, 699, 699, 699, 699, 699,
- 699, 699, nil, nil, 699, nil, nil, nil, nil, nil,
- nil, 699, nil, nil, 699, 699, 699, 699, 699, 699,
- 699, 699, nil, 699, 699, 699, nil, 699, 699, nil,
- 699, 699, 699, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 699, nil, nil, 699, nil, nil, 699, 699,
- nil, nil, 699, nil, nil, nil, nil, nil, 699, nil,
- nil, nil, nil, nil, nil, nil, nil, 699, nil, nil,
- nil, nil, 699, 699, 699, 699, nil, 699, 699, 699,
- 699, nil, nil, nil, nil, 699, 699, nil, nil, nil,
- 725, 725, 725, 699, 725, 699, 699, 699, 725, 725,
- nil, nil, nil, 725, nil, 725, 725, 725, 725, 725,
- 725, 725, nil, nil, nil, nil, nil, 725, 725, 725,
- 725, 725, 725, 725, nil, nil, 725, nil, nil, nil,
- nil, nil, nil, 725, nil, nil, 725, 725, 725, 725,
- 725, 725, 725, 725, nil, 725, 725, 725, nil, 725,
- 725, nil, 725, 725, 725, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 725, nil, nil, 725, nil, nil,
- 725, 725, nil, nil, 725, nil, nil, nil, nil, nil,
- 725, nil, nil, nil, nil, nil, nil, nil, nil, 725,
- nil, nil, nil, nil, 725, 725, 725, 725, nil, 725,
- 725, 725, 725, nil, nil, nil, nil, 725, 725, nil,
- nil, nil, 731, 731, 731, 725, 731, 725, 725, 725,
- 731, 731, nil, nil, nil, 731, nil, 731, 731, 731,
- 731, 731, 731, 731, nil, nil, nil, nil, nil, 731,
- 731, 731, 731, 731, 731, 731, nil, nil, 731, nil,
- nil, nil, nil, nil, nil, 731, nil, nil, 731, 731,
- 731, 731, 731, 731, 731, 731, nil, 731, 731, 731,
- nil, 731, 731, nil, 731, 731, 731, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 731, nil, nil, 731,
- nil, nil, 731, 731, nil, nil, 731, nil, nil, nil,
- nil, nil, 731, nil, nil, nil, nil, nil, nil, nil,
- nil, 731, nil, nil, nil, nil, 731, 731, 731, 731,
- nil, 731, 731, 731, 731, nil, nil, nil, nil, 731,
- 731, nil, nil, nil, 754, 754, 754, 731, 754, 731,
- 731, 731, 754, 754, nil, nil, nil, 754, nil, 754,
- 754, 754, 754, 754, 754, 754, nil, nil, nil, nil,
- nil, 754, 754, 754, 754, 754, 754, 754, nil, nil,
- 754, nil, nil, nil, nil, nil, nil, 754, nil, nil,
- 754, 754, 754, 754, 754, 754, 754, 754, nil, 754,
- 754, 754, nil, 754, 754, nil, 754, 754, 754, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 754, nil,
- nil, 754, nil, nil, 754, 754, nil, nil, 754, nil,
- nil, nil, nil, nil, 754, nil, nil, nil, nil, nil,
- nil, nil, nil, 754, nil, nil, nil, nil, 754, 754,
- 754, 754, nil, 754, 754, 754, 754, nil, nil, nil,
- nil, 754, 754, nil, nil, nil, 756, 756, 756, 754,
- 756, 754, 754, 754, 756, 756, nil, nil, nil, 756,
- nil, 756, 756, 756, 756, 756, 756, 756, nil, nil,
- nil, nil, nil, 756, 756, 756, 756, 756, 756, 756,
- nil, nil, 756, nil, nil, nil, nil, nil, nil, 756,
- nil, nil, 756, 756, 756, 756, 756, 756, 756, 756,
- nil, 756, 756, 756, nil, 756, 756, nil, 756, 756,
- 756, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 756, nil, nil, 756, nil, nil, 756, 756, nil, nil,
- 756, nil, nil, nil, nil, nil, 756, nil, nil, nil,
- nil, nil, nil, nil, nil, 756, nil, nil, nil, nil,
- 756, 756, 756, 756, nil, 756, 756, 756, 756, nil,
- nil, nil, nil, 756, 756, nil, nil, nil, 770, 770,
- 770, 756, 770, 756, 756, 756, 770, 770, nil, nil,
- nil, 770, nil, 770, 770, 770, 770, 770, 770, 770,
- nil, nil, nil, nil, nil, 770, 770, 770, 770, 770,
- 770, 770, nil, nil, 770, nil, nil, nil, nil, nil,
- nil, 770, nil, nil, 770, 770, 770, 770, 770, 770,
- 770, 770, nil, 770, 770, 770, nil, 770, 770, nil,
- 770, 770, 770, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 770, nil, nil, 770, nil, nil, 770, 770,
- nil, nil, 770, nil, nil, nil, nil, nil, 770, nil,
- nil, nil, nil, nil, nil, nil, nil, 770, nil, nil,
- nil, nil, 770, 770, 770, 770, nil, 770, 770, 770,
- 770, nil, nil, nil, nil, 770, 770, nil, nil, nil,
- 771, 771, 771, 770, 771, 770, 770, 770, 771, 771,
- nil, nil, nil, 771, nil, 771, 771, 771, 771, 771,
- 771, 771, nil, nil, nil, nil, nil, 771, 771, 771,
- 771, 771, 771, 771, nil, nil, 771, nil, nil, nil,
- nil, nil, nil, 771, nil, nil, 771, 771, 771, 771,
- 771, 771, 771, 771, nil, 771, 771, 771, nil, 771,
- 771, nil, 771, 771, 771, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 771, nil, nil, 771, nil, nil,
- 771, 771, nil, nil, 771, nil, nil, nil, nil, nil,
- 771, nil, nil, nil, nil, nil, nil, nil, nil, 771,
- nil, nil, nil, nil, 771, 771, 771, 771, nil, 771,
- 771, 771, 771, nil, nil, nil, nil, 771, 771, nil,
- nil, nil, 772, 772, 772, 771, 772, 771, 771, 771,
- 772, 772, nil, nil, nil, 772, nil, 772, 772, 772,
- 772, 772, 772, 772, nil, nil, nil, nil, nil, 772,
- 772, 772, 772, 772, 772, 772, nil, nil, 772, nil,
- nil, nil, nil, nil, nil, 772, nil, nil, 772, 772,
- 772, 772, 772, 772, 772, 772, nil, 772, 772, 772,
- nil, 772, 772, nil, 772, 772, 772, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 772, nil, nil, 772,
- nil, nil, 772, 772, nil, nil, 772, nil, nil, nil,
- nil, nil, 772, nil, nil, nil, nil, nil, nil, nil,
- nil, 772, nil, nil, nil, nil, 772, 772, 772, 772,
- nil, 772, 772, 772, 772, nil, nil, nil, nil, 772,
- 772, nil, nil, nil, 773, 773, 773, 772, 773, 772,
- 772, 772, 773, 773, nil, nil, nil, 773, nil, 773,
- 773, 773, 773, 773, 773, 773, nil, nil, nil, nil,
- nil, 773, 773, 773, 773, 773, 773, 773, nil, nil,
- 773, nil, nil, nil, nil, nil, nil, 773, nil, nil,
- 773, 773, 773, 773, 773, 773, 773, 773, nil, 773,
- 773, 773, nil, 773, 773, nil, 773, 773, 773, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 773, nil,
- nil, 773, nil, nil, 773, 773, nil, nil, 773, nil,
- nil, nil, nil, nil, 773, nil, nil, nil, nil, nil,
- nil, nil, nil, 773, nil, nil, nil, nil, 773, 773,
- 773, 773, nil, 773, 773, 773, 773, nil, nil, nil,
- nil, 773, 773, nil, nil, nil, 787, 787, 787, 773,
- 787, 773, 773, 773, 787, 787, nil, nil, nil, 787,
- nil, 787, 787, 787, 787, 787, 787, 787, nil, nil,
- nil, nil, nil, 787, 787, 787, 787, 787, 787, 787,
- nil, nil, 787, nil, nil, nil, nil, nil, nil, 787,
- nil, nil, 787, 787, 787, 787, 787, 787, 787, 787,
- nil, 787, 787, 787, nil, 787, 787, nil, nil, nil,
- 787, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 787, nil, nil, 787, nil, nil, 787, 787, nil, nil,
- 787, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 787, 787, 787, 787, nil, 787, 787, 787, 787, nil,
- nil, nil, nil, 787, 787, nil, nil, nil, 837, 837,
- 837, 787, 837, 787, 787, 787, 837, 837, nil, nil,
- nil, 837, nil, 837, 837, 837, 837, 837, 837, 837,
- nil, nil, nil, nil, nil, 837, 837, 837, 837, 837,
- 837, 837, nil, nil, 837, nil, nil, nil, nil, nil,
- nil, 837, nil, nil, 837, 837, 837, 837, 837, 837,
- 837, 837, nil, 837, 837, 837, nil, 837, 837, nil,
- 837, 837, 837, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 837, nil, nil, 837, nil, nil, 837, 837,
- nil, nil, 837, nil, nil, nil, nil, nil, 837, nil,
- nil, nil, nil, nil, nil, nil, nil, 837, nil, nil,
- nil, nil, 837, 837, 837, 837, nil, 837, 837, 837,
- 837, nil, nil, nil, nil, 837, 837, nil, nil, nil,
- 842, 842, 842, 837, 842, 837, 837, 837, 842, 842,
- nil, nil, nil, 842, nil, 842, 842, 842, 842, 842,
- 842, 842, nil, nil, nil, nil, nil, 842, 842, 842,
- 842, 842, 842, 842, nil, nil, 842, nil, nil, nil,
- nil, nil, nil, 842, nil, nil, 842, 842, 842, 842,
- 842, 842, 842, 842, nil, 842, 842, 842, nil, 842,
- 842, nil, 842, 842, 842, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 842, nil, nil, 842, nil, nil,
- 842, 842, nil, nil, 842, nil, 842, nil, nil, nil,
- 842, nil, nil, nil, nil, nil, nil, nil, nil, 842,
- nil, nil, nil, nil, 842, 842, 842, 842, nil, 842,
- 842, 842, 842, nil, nil, nil, nil, 842, 842, nil,
- nil, nil, 859, 859, 859, 842, 859, 842, 842, 842,
- 859, 859, nil, nil, nil, 859, nil, 859, 859, 859,
- 859, 859, 859, 859, nil, nil, nil, nil, nil, 859,
- 859, 859, 859, 859, 859, 859, nil, nil, 859, nil,
- nil, nil, nil, nil, nil, 859, nil, nil, 859, 859,
- 859, 859, 859, 859, 859, 859, 859, 859, 859, 859,
- nil, 859, 859, nil, 859, 859, 859, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 859, nil, nil, 859,
- nil, nil, 859, 859, nil, nil, 859, nil, nil, nil,
- 859, nil, 859, nil, nil, 859, nil, nil, nil, nil,
- nil, 859, nil, nil, nil, nil, 859, 859, 859, 859,
- nil, 859, 859, 859, 859, nil, nil, nil, nil, 859,
- 859, nil, nil, nil, 860, 860, 860, 859, 860, 859,
- 859, 859, 860, 860, nil, nil, nil, 860, nil, 860,
- 860, 860, 860, 860, 860, 860, nil, nil, nil, nil,
- nil, 860, 860, 860, 860, 860, 860, 860, nil, nil,
- 860, nil, nil, nil, nil, nil, nil, 860, nil, nil,
- 860, 860, 860, 860, 860, 860, 860, 860, nil, 860,
- 860, 860, nil, 860, 860, nil, 860, 860, 860, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 860, nil,
- nil, 860, nil, nil, 860, 860, nil, nil, 860, nil,
- nil, nil, nil, nil, 860, nil, nil, nil, nil, nil,
- nil, nil, nil, 860, nil, nil, nil, nil, 860, 860,
- 860, 860, nil, 860, 860, 860, 860, nil, nil, nil,
- nil, 860, 860, nil, nil, nil, 874, 874, 874, 860,
- 874, 860, 860, 860, 874, 874, nil, nil, nil, 874,
- nil, 874, 874, 874, 874, 874, 874, 874, nil, nil,
- nil, nil, nil, 874, 874, 874, 874, 874, 874, 874,
- nil, nil, 874, nil, nil, nil, nil, nil, nil, 874,
- nil, nil, 874, 874, 874, 874, 874, 874, 874, 874,
- nil, 874, 874, 874, nil, 874, 874, nil, nil, nil,
- 874, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 874, nil, nil, 874, nil, nil, 874, 874, nil, nil,
- 874, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 874, 874, 874, 874, nil, 874, 874, 874, 874, nil,
- nil, nil, nil, 874, 874, nil, nil, nil, 886, 886,
- 886, 874, 886, 874, 874, 874, 886, 886, nil, nil,
- nil, 886, nil, 886, 886, 886, 886, 886, 886, 886,
- nil, nil, nil, nil, nil, 886, 886, 886, 886, 886,
- 886, 886, nil, nil, 886, nil, nil, nil, nil, nil,
- nil, 886, nil, nil, 886, 886, 886, 886, 886, 886,
- 886, 886, nil, 886, 886, 886, nil, 886, 886, nil,
- nil, nil, 886, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 886, nil, nil, 886, nil, nil, 886, 886,
- nil, nil, 886, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 886, 886, 886, 886, nil, 886, 886, 886,
- 886, nil, nil, nil, nil, 886, 886, nil, nil, nil,
- 923, 923, 923, 886, 923, 886, 886, 886, 923, 923,
- nil, nil, nil, 923, nil, 923, 923, 923, 923, 923,
- 923, 923, nil, nil, nil, nil, nil, 923, 923, 923,
- 923, 923, 923, 923, nil, nil, 923, nil, nil, nil,
- nil, nil, nil, 923, nil, nil, 923, 923, 923, 923,
- 923, 923, 923, 923, nil, 923, 923, 923, nil, 923,
- 923, nil, 923, 923, 923, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 923, nil, nil, 923, nil, nil,
- 923, 923, nil, nil, 923, nil, nil, nil, nil, nil,
- 923, nil, nil, nil, nil, nil, nil, nil, nil, 923,
- nil, nil, nil, nil, 923, 923, 923, 923, nil, 923,
- 923, 923, 923, nil, nil, nil, nil, 923, 923, nil,
- nil, nil, 985, 985, 985, 923, 985, 923, 923, 923,
- 985, 985, nil, nil, nil, 985, nil, 985, 985, 985,
- 985, 985, 985, 985, nil, nil, nil, nil, nil, 985,
- 985, 985, 985, 985, 985, 985, nil, nil, 985, nil,
- nil, nil, nil, nil, nil, 985, nil, nil, 985, 985,
- 985, 985, 985, 985, 985, 985, 985, 985, 985, 985,
- nil, 985, 985, nil, 985, 985, 985, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 985, nil, nil, 985,
- nil, nil, 985, 985, nil, nil, 985, nil, 985, nil,
- 985, nil, 985, nil, nil, 985, nil, nil, nil, nil,
- nil, 985, nil, nil, nil, nil, 985, 985, 985, 985,
- nil, 985, 985, 985, 985, nil, nil, nil, nil, 985,
- 985, nil, nil, nil, nil, 56, nil, 985, nil, 985,
- 985, 985, 56, 56, 56, nil, nil, 56, 56, 56,
- nil, 56, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 56, 56, 56, nil, nil, nil, nil, nil, nil,
- nil, 56, 56, nil, 56, 56, 56, 56, 56, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 56, 56, 56, 56, 56, 56, 56, 56,
- 56, 56, 56, 56, 56, 56, nil, nil, 56, 56,
- 56, nil, nil, 56, nil, nil, 56, nil, nil, 56,
- 56, nil, 56, nil, 56, nil, 56, nil, 56, 56,
- nil, 56, 56, 56, 56, 56, nil, 56, nil, 56,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 56, nil, nil, 56, 56, 56, 56,
- 424, 56, nil, 56, nil, nil, nil, 424, 424, 424,
- nil, nil, 424, 424, 424, nil, 424, nil, nil, nil,
- nil, nil, nil, nil, nil, 424, 424, 424, 424, nil,
- nil, nil, nil, nil, nil, nil, 424, 424, nil, 424,
- 424, 424, 424, 424, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 424, 424, 424,
- 424, 424, 424, 424, 424, 424, 424, 424, 424, 424,
- 424, nil, nil, 424, 424, 424, nil, nil, 424, nil,
- nil, 424, nil, nil, 424, 424, nil, 424, nil, 424,
- nil, 424, nil, 424, 424, nil, 424, 424, 424, 424,
- 424, nil, 424, 424, 424, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 424, nil,
- nil, 424, 424, 424, 424, 425, 424, nil, 424, nil,
- nil, nil, 425, 425, 425, nil, nil, 425, 425, 425,
- nil, 425, nil, nil, nil, nil, nil, nil, nil, nil,
- 425, 425, 425, 425, nil, nil, nil, nil, nil, nil,
- nil, 425, 425, nil, 425, 425, 425, 425, 425, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 425, 425, 425, 425, 425, 425, 425, 425,
- 425, 425, 425, 425, 425, 425, nil, nil, 425, 425,
- 425, nil, nil, 425, nil, nil, 425, nil, nil, 425,
- 425, nil, 425, nil, 425, nil, 425, nil, 425, 425,
- nil, 425, 425, 425, 425, 425, nil, 425, 425, 425,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 425, nil, nil, 425, 425, 425, 425,
- 27, 425, nil, 425, nil, nil, nil, 27, 27, 27,
- nil, nil, 27, 27, 27, nil, 27, nil, nil, nil,
- nil, nil, nil, nil, nil, 27, 27, 27, nil, nil,
- nil, nil, nil, nil, nil, nil, 27, 27, nil, 27,
- 27, 27, 27, 27, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 27, 27, 27,
- 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
- 27, nil, nil, 27, 27, 27, nil, nil, 27, nil,
- 27, 27, nil, nil, 27, 27, nil, 27, nil, 27,
- nil, 27, nil, 27, 27, nil, 27, 27, 27, 27,
- 27, 28, 27, 27, 27, nil, nil, nil, 28, 28,
- 28, nil, nil, 28, 28, 28, nil, 28, 27, nil,
- nil, 27, 27, nil, 27, nil, 27, 28, 28, nil,
- nil, nil, nil, nil, nil, nil, nil, 28, 28, nil,
- 28, 28, 28, 28, 28, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 28, 28,
- 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
- 28, 28, nil, nil, 28, 28, 28, nil, nil, 28,
- nil, 28, 28, nil, nil, 28, 28, nil, 28, nil,
- 28, nil, 28, nil, 28, 28, nil, 28, 28, 28,
- 28, 28, nil, 28, 415, 28, nil, nil, nil, nil,
- nil, 415, 415, 415, nil, nil, 415, 415, 415, 28,
- 415, nil, 28, 28, nil, 28, nil, 28, nil, 415,
- 415, 415, nil, nil, nil, nil, nil, nil, nil, nil,
- 415, 415, nil, 415, 415, 415, 415, 415, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 415, 415, 415, 415, 415, 415, 415, 415, 415,
- 415, 415, 415, 415, 415, nil, nil, 415, 415, 415,
- nil, nil, 415, nil, 415, 415, nil, nil, 415, 415,
- nil, 415, nil, 415, nil, 415, nil, 415, 415, nil,
- 415, 415, 415, 415, 415, nil, 415, 415, 415, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 415, nil, 474, 415, 415, nil, 415, nil,
- 415, 474, 474, 474, nil, nil, 474, 474, 474, 646,
- 474, 646, 646, 646, 646, 646, nil, nil, nil, 474,
- 474, nil, nil, nil, nil, 646, nil, nil, nil, nil,
- 474, 474, nil, 474, 474, 474, 474, 474, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 646, 336, nil,
- 336, 336, 336, 336, 336, nil, 646, 646, 646, 646,
- nil, nil, nil, 646, 336, 534, nil, 534, 534, 534,
- 534, 534, 474, nil, nil, nil, nil, nil, nil, 474,
- nil, 534, nil, nil, 474, 474, 336, 336, nil, 646,
- nil, nil, nil, nil, nil, 336, 336, 336, 336, nil,
- nil, nil, 336, 534, nil, nil, nil, 474, 474, nil,
- nil, nil, 534, 534, 534, 534, nil, nil, nil, 534,
- nil, nil, 474, nil, nil, 474, nil, nil, nil, nil,
- 474, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, nil, nil, nil, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, nil, nil,
- nil, nil, nil, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, nil, 8, nil, nil, nil, nil, nil,
- nil, nil, nil, 8, 8, nil, 8, 8, 8, 8,
- 8, 8, 8, nil, nil, 8, 8, nil, nil, nil,
- 8, 8, 8, 8, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 8, 8, nil,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, nil, nil, 8, 8, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 8,
- 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
- 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
- 9, 9, 9, 9, nil, nil, nil, 9, 9, 9,
- 9, 9, 9, 9, 9, 9, 9, nil, nil, nil,
- nil, nil, 9, 9, 9, 9, 9, 9, 9, 9,
- 9, nil, nil, 9, nil, nil, nil, nil, nil, nil,
- nil, nil, 9, 9, nil, 9, 9, 9, 9, 9,
- 9, 9, nil, nil, 9, 9, nil, nil, nil, 9,
- 9, 9, 9, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 9, 9, nil, 9,
- 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
- 9, nil, nil, 9, 9, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 9, 395,
- 395, 395, 395, 395, 395, 395, 395, 395, 395, 395,
- 395, 395, 395, 395, 395, 395, 395, 395, 395, 395,
- 395, 395, 395, nil, nil, nil, 395, 395, 395, 395,
- 395, 395, 395, 395, 395, 395, nil, nil, nil, nil,
- nil, 395, 395, 395, 395, 395, 395, 395, 395, 395,
- nil, nil, 395, nil, nil, nil, nil, nil, nil, nil,
- nil, 395, 395, nil, 395, 395, 395, 395, 395, 395,
- 395, nil, nil, 395, 395, nil, nil, nil, 395, 395,
- 395, 395, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 395, 395, nil, 395, 395,
- 395, 395, 395, 395, 395, 395, 395, 395, 395, 395,
- nil, nil, 395, 395, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 395, 616, 616,
- 616, 616, 616, 616, 616, 616, 616, 616, 616, 616,
- 616, 616, 616, 616, 616, 616, 616, 616, 616, 616,
- 616, 616, nil, nil, nil, 616, 616, 616, 616, 616,
- 616, 616, 616, 616, 616, nil, nil, nil, nil, nil,
- 616, 616, 616, 616, 616, 616, 616, 616, 616, nil,
- nil, 616, nil, nil, nil, nil, nil, nil, nil, nil,
- 616, 616, nil, 616, 616, 616, 616, 616, 616, 616,
- nil, nil, 616, 616, nil, nil, nil, 616, 616, 616,
- 616, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 616, 616, nil, 616, 616, 616,
- 616, 616, 616, 616, 616, 616, 616, 616, 616, nil,
- nil, 616, 616, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 616, 71, 71, 71,
- 71, 71, 71, 71, 71, 71, 71, 71, 71, 71,
- 71, 71, 71, 71, 71, 71, 71, 71, 71, 71,
- 71, nil, nil, nil, 71, 71, 71, 71, 71, 71,
- 71, 71, 71, 71, nil, nil, nil, nil, nil, 71,
- 71, 71, 71, 71, 71, 71, 71, 71, 71, 71,
- 71, nil, 71, nil, nil, nil, nil, nil, nil, 71,
- 71, nil, 71, 71, 71, 71, 71, 71, 71, nil,
- nil, 71, 71, nil, nil, nil, 71, 71, 71, 71,
- nil, nil, nil, nil, nil, 71, nil, nil, nil, nil,
- nil, nil, nil, 71, 71, nil, 71, 71, 71, 71,
- 71, 71, 71, 71, 71, 71, 71, 71, nil, nil,
- 71, 738, 738, 738, 738, 738, 738, 738, 738, 738,
- 738, 738, 738, 738, 738, 738, 738, 738, 738, 738,
- 738, 738, 738, 738, 738, nil, nil, nil, 738, 738,
- 738, 738, 738, 738, 738, 738, 738, 738, nil, nil,
- nil, nil, nil, 738, 738, 738, 738, 738, 738, 738,
- 738, 738, nil, nil, 738, nil, nil, nil, nil, nil,
- nil, nil, nil, 738, 738, nil, 738, 738, 738, 738,
- 738, 738, 738, nil, nil, 738, 738, nil, nil, nil,
- 738, 738, 738, 738, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 738, 738, nil,
- 738, 738, 738, 738, 738, 738, 738, 738, 738, 738,
- 738, 738, 210, 210, 738, nil, 210, nil, nil, nil,
- nil, nil, nil, nil, nil, 210, 210, nil, 210, 210,
- 210, 210, 210, 210, 210, nil, nil, 210, 210, nil,
- nil, nil, 210, 210, 210, 210, nil, nil, nil, nil,
- nil, 210, nil, nil, nil, nil, nil, nil, nil, 210,
- 210, nil, 210, 210, 210, 210, 210, 210, 210, 210,
- 210, 210, 210, 210, 211, 211, 210, nil, 211, nil,
- nil, nil, nil, nil, nil, nil, nil, 211, 211, nil,
- 211, 211, 211, 211, 211, 211, 211, nil, nil, 211,
- 211, nil, nil, nil, 211, 211, 211, 211, nil, nil,
- nil, nil, nil, 211, nil, nil, nil, nil, nil, nil,
- nil, 211, 211, nil, 211, 211, 211, 211, 211, 211,
- 211, 211, 211, 211, 211, 211, 259, 259, 211, nil,
- 259, nil, nil, nil, nil, nil, nil, nil, nil, 259,
- 259, nil, 259, 259, 259, 259, 259, 259, 259, nil,
- nil, 259, 259, nil, nil, nil, 259, 259, 259, 259,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 259, 259, nil, 259, 259, 259, 259,
- 259, 259, 259, 259, 259, 259, 259, 259, 440, 440,
- 259, nil, 440, nil, nil, nil, nil, nil, nil, nil,
- nil, 440, 440, nil, 440, 440, 440, 440, 440, 440,
- 440, nil, nil, 440, 440, nil, nil, nil, 440, 440,
- 440, 440, nil, nil, nil, nil, nil, 440, nil, nil,
- nil, nil, nil, nil, nil, 440, 440, nil, 440, 440,
- 440, 440, 440, 440, 440, 440, 440, 440, 440, 440,
- 441, 441, 440, nil, 441, nil, nil, nil, nil, nil,
- nil, nil, nil, 441, 441, nil, 441, 441, 441, 441,
- 441, 441, 441, nil, nil, 441, 441, nil, nil, nil,
- 441, 441, 441, 441, nil, nil, nil, nil, nil, 441,
- nil, nil, nil, nil, nil, nil, nil, 441, 441, nil,
- 441, 441, 441, 441, 441, 441, 441, 441, 441, 441,
- 441, 441, 507, 507, 441, nil, 507, nil, nil, nil,
- nil, nil, nil, nil, nil, 507, 507, nil, 507, 507,
- 507, 507, 507, 507, 507, nil, nil, 507, 507, nil,
- nil, nil, 507, 507, 507, 507, nil, nil, nil, nil,
- nil, 507, nil, nil, nil, nil, nil, nil, nil, 507,
- 507, nil, 507, 507, 507, 507, 507, 507, 507, 507,
- 507, 507, 507, 507, 508, 508, 507, nil, 508, nil,
- nil, nil, nil, nil, nil, nil, nil, 508, 508, nil,
- 508, 508, 508, 508, 508, 508, 508, nil, nil, 508,
- 508, nil, nil, nil, 508, 508, 508, 508, nil, nil,
- nil, nil, nil, 508, nil, nil, nil, nil, nil, nil,
- nil, 508, 508, nil, 508, 508, 508, 508, 508, 508,
- 508, 508, 508, 508, 508, 508, 517, 517, 508, nil,
- 517, nil, nil, nil, nil, nil, nil, nil, nil, 517,
- 517, nil, 517, 517, 517, 517, 517, 517, 517, nil,
- nil, 517, 517, nil, nil, nil, 517, 517, 517, 517,
- nil, nil, nil, nil, nil, 517, nil, nil, nil, nil,
- nil, nil, nil, 517, 517, nil, 517, 517, 517, 517,
- 517, 517, 517, 517, 517, 517, 517, 517, 518, 518,
- 517, nil, 518, nil, nil, nil, nil, nil, nil, nil,
- nil, 518, 518, nil, 518, 518, 518, 518, 518, 518,
- 518, nil, nil, 518, 518, nil, nil, nil, 518, 518,
- 518, 518, nil, nil, nil, nil, nil, 518, nil, nil,
- nil, nil, nil, nil, nil, 518, 518, nil, 518, 518,
- 518, 518, 518, 518, 518, 518, 518, 518, 518, 518,
- 576, 576, 518, nil, 576, nil, nil, nil, nil, nil,
- nil, nil, nil, 576, 576, nil, 576, 576, 576, 576,
- 576, 576, 576, nil, nil, 576, 576, nil, nil, nil,
- 576, 576, 576, 576, nil, nil, nil, nil, nil, 576,
- nil, nil, nil, nil, nil, nil, nil, 576, 576, nil,
- 576, 576, 576, 576, 576, 576, 576, 576, 576, 576,
- 576, 576, 577, 577, 576, nil, 577, nil, nil, nil,
- nil, nil, nil, nil, nil, 577, 577, nil, 577, 577,
- 577, 577, 577, 577, 577, nil, nil, 577, 577, nil,
- nil, nil, 577, 577, 577, 577, nil, nil, nil, nil,
- nil, 577, nil, nil, nil, nil, nil, nil, nil, 577,
- 577, nil, 577, 577, 577, 577, 577, 577, 577, 577,
- 577, 577, 577, 577, 583, 583, 577, nil, 583, nil,
- nil, nil, nil, nil, nil, nil, nil, 583, 583, nil,
- 583, 583, 583, 583, 583, 583, 583, nil, nil, 583,
- 583, nil, nil, nil, 583, 583, 583, 583, nil, nil,
- nil, nil, nil, 583, nil, nil, nil, nil, nil, nil,
- nil, 583, 583, nil, 583, 583, 583, 583, 583, 583,
- 583, 583, 583, 583, 583, 583, 584, 584, 583, nil,
- 584, nil, nil, nil, nil, nil, nil, nil, nil, 584,
- 584, nil, 584, 584, 584, 584, 584, 584, 584, nil,
- nil, 584, 584, nil, nil, nil, 584, 584, 584, 584,
- nil, nil, nil, nil, nil, 584, nil, nil, nil, nil,
- nil, nil, nil, 584, 584, nil, 584, 584, 584, 584,
- 584, 584, 584, 584, 584, 584, 584, 584, 939, 939,
- 584, nil, 939, nil, nil, nil, nil, nil, nil, nil,
- nil, 939, 939, nil, 939, 939, 939, 939, 939, 939,
- 939, nil, nil, 939, 939, nil, nil, nil, 939, 939,
- 939, 939, nil, nil, nil, nil, nil, 939, nil, nil,
- nil, nil, nil, nil, nil, 939, 939, nil, 939, 939,
- 939, 939, 939, 939, 939, 939, 939, 939, 939, 939,
- 986, 986, 939, nil, 986, nil, nil, nil, nil, nil,
- nil, nil, nil, 986, 986, nil, 986, 986, 986, 986,
- 986, 986, 986, nil, nil, 986, 986, nil, nil, nil,
- 986, 986, 986, 986, nil, nil, nil, nil, nil, 986,
- nil, nil, nil, nil, nil, nil, nil, 986, 986, nil,
- 986, 986, 986, 986, 986, 986, 986, 986, 986, 986,
- 986, 986, 987, 987, 986, nil, 987, nil, nil, nil,
- nil, nil, nil, nil, nil, 987, 987, nil, 987, 987,
- 987, 987, 987, 987, 987, nil, nil, 987, 987, nil,
- nil, nil, 987, 987, 987, 987, nil, nil, nil, nil,
- nil, 987, nil, nil, nil, nil, nil, nil, nil, 987,
- 987, nil, 987, 987, 987, 987, 987, 987, 987, 987,
- 987, 987, 987, 987, nil, 693, 987, 693, 693, 693,
- 693, 693, nil, 695, nil, 695, 695, 695, 695, 695,
- nil, 693, nil, nil, nil, nil, nil, nil, 736, 695,
- 736, 736, 736, 736, 736, nil, nil, nil, nil, nil,
- nil, nil, nil, 693, 736, nil, nil, nil, nil, nil,
- nil, 695, 693, 693, 693, 693, nil, nil, nil, 693,
- 695, 695, 695, 695, nil, nil, 736, 695, 737, nil,
- 737, 737, 737, 737, 737, 736, 736, 736, 736, nil,
- nil, nil, 736, 865, 737, 865, 865, 865, 865, 865,
- nil, 867, nil, 867, 867, 867, 867, 867, nil, 865,
- nil, nil, nil, nil, nil, nil, 737, 867, 893, nil,
- 893, 893, 893, 893, 893, 737, 737, 737, 737, nil,
- nil, 865, 737, nil, 893, nil, nil, nil, nil, 867,
- 865, 865, 865, 865, nil, nil, nil, 865, 867, 867,
- 867, 867, nil, nil, nil, 867, 893, 897, nil, 897,
- 897, 897, 897, 897, nil, 893, 893, 893, 893, nil,
- nil, nil, 893, 897, 899, nil, 899, 899, 899, 899,
- 899, nil, 971, nil, 971, 971, 971, 971, 971, nil,
- 899, nil, nil, nil, nil, 897, nil, 973, 971, 973,
- 973, 973, 973, 973, nil, nil, 897, 897, nil, nil,
- nil, 897, 899, 973, nil, nil, nil, nil, nil, nil,
- 971, nil, nil, 899, 899, nil, nil, nil, 899, 971,
- 971, 971, 971, nil, nil, 973, 971, 975, nil, 975,
- 975, 975, 975, 975, nil, nil, 973, 973, nil, nil,
- nil, 973, 977, 975, 977, 977, 977, 977, 977, nil,
- 989, nil, 989, 989, 989, 989, 989, 1015, 977, 1015,
- 1015, 1015, 1015, 1015, nil, 975, 989, nil, nil, nil,
- nil, nil, nil, 1015, nil, nil, 975, 975, nil, nil,
- 977, 975, nil, nil, nil, nil, nil, nil, 989, nil,
- nil, 977, 977, nil, nil, 1015, 977, nil, nil, 989,
- 989, nil, nil, nil, 989, nil, 1015, 1015, nil, nil,
- nil, 1015 ]
-
-racc_action_pointer = [
- 750, 47, nil, 123, nil, 5120, 1299, -62, 23249, 23378,
- -21, nil, -43, 71, 533, -41, 34, -4, nil, -72,
- 5252, 1173, 168, nil, 157, nil, 6, 22760, 22871, 5384,
- 5516, 5648, nil, 891, 5780, 5912, nil, 71, 227, 254,
- 154, 228, 6052, 6184, 6316, 95, 544, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 22355, nil, -71, 6448,
- 6580, -23, nil, 6712, 6844, nil, nil, 6976, 7116, 7248,
- 7380, 23765, nil, nil, nil, nil, nil, 470, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 0, nil, nil, 113, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 262,
- nil, 7520, nil, nil, nil, nil, 7660, 7792, 7924, 8056,
- 8196, 1032, nil, 220, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 141, nil, 1173, 8328, 8460, 8592,
- 23941, 24003, 8724, 8856, 8988, 9120, 9252, 9384, nil, nil,
- 546, -77, -62, 233, 131, 196, 256, nil, 9516, 1314,
- 273, 9648, 9780, 9912, 10044, 10176, 10308, 10440, 10572, 10704,
- 10836, 10968, 11100, 11232, 11364, 11496, 11628, 11760, 11892, 12024,
- 12156, 12288, 12420, 12552, 12684, 12816, nil, nil, nil, 24065,
- nil, nil, 275, 12948, 13080, nil, nil, nil, nil, nil,
- nil, nil, 13212, nil, 1314, nil, 262, 314, nil, 13344,
- 366, 13476, nil, nil, 13608, 13740, nil, nil, 296, nil,
- 13880, 1440, 368, 362, 1455, 384, 444, 418, 14012, 1596,
- 578, 615, 681, 505, 750, nil, 478, 439, 33, nil,
- nil, nil, 501, 258, 478, 14152, nil, 299, 561, 753,
- nil, 569, nil, 14284, 1737, 14416, 528, nil, 105, 259,
- 571, 585, 454, 619, nil, nil, 23117, 488, -1, 26,
- 14548, 14680, 458, 706, 596, -23, -19, 792, 691, -18,
- 724, nil, nil, 227, 281, -34, nil, 822, nil, 34,
- 14812, nil, nil, nil, 264, 409, 448, 451, 478, 508,
- 512, 532, 554, nil, 567, nil, 14944, nil, 275, 335,
- 363, 375, 392, -45, -41, 395, nil, nil, nil, nil,
- nil, nil, nil, nil, 643, 23507, nil, nil, nil, nil,
- 650, nil, nil, 642, 15076, 644, nil, nil, 891, 658,
- nil, 672, 676, 332, 342, 22984, nil, nil, nil, 224,
- 337, 723, nil, nil, 22490, 22625, nil, 1455, nil, 689,
- nil, nil, 750, nil, nil, nil, nil, -33, nil, 746,
- 24127, 24189, 15208, 239, 15340, 15472, 15604, 3147, 3288, 538,
- 659, 778, 783, 788, 798, 5120, 5252, 5384, 3429, 3570,
- 3711, 3852, 3993, 4134, 4275, 4416, 4557, 4698, 621, 630,
- 4839, 4980, 15736, -46, 23114, nil, nil, nil, nil, 747,
- nil, 159, 279, 750, nil, nil, 15868, nil, 16000, nil,
- 16132, nil, 332, nil, nil, nil, 16272, 1455, 1878, 752,
- 751, nil, nil, 754, 16412, 767, 16544, 24251, 24313, 891,
- 809, nil, 16676, 769, nil, 16808, 16940, 24375, 24437, 1596,
- 17072, 897, 896, 694, 824, nil, 17204, nil, nil, 17336,
- nil, nil, nil, nil, 23134, nil, 783, 784, nil, 785,
- 793, 794, nil, nil, nil, nil, nil, nil, nil, nil,
- 787, 477, nil, nil, 17468, nil, nil, nil, 880, nil,
- nil, nil, 881, nil, nil, 882, 2019, 935, nil, 2160,
- 63, 119, 935, 945, 17600, 17732, 24499, 24561, 10, nil,
- nil, 894, nil, 24623, 24685, 17864, nil, nil, nil, 587,
- 193, 2301, 883, nil, -14, nil, nil, nil, 733, nil,
- nil, nil, 858, nil, nil, 151, nil, 222, nil, nil,
- 846, nil, 847, nil, nil, nil, 23636, nil, 852, 17996,
- 18128, 380, 896, 18260, 18392, 18524, 18656, 899, nil, nil,
- 18788, 18920, 907, nil, 19052, 19184, nil, nil, 350, 416,
- 470, 607, 874, 1032, 1737, nil, 23078, nil, 2442, 979,
- 5, 316, nil, 2583, 2724, nil, 882, nil, 929, 19316,
- nil, nil, 19448, nil, 905, -81, 19580, 888, nil, 893,
- 137, 179, 935, 340, 1032, 936, 895, 19712, 1878, 972,
- 20, 1026, 19844, nil, 913, nil, 539, 21, 914, 495,
- nil, nil, 740, 24934, nil, 24942, nil, 5959, nil, 19976,
- nil, 607, nil, 912, 230, 925, nil, nil, nil, nil,
- 850, nil, 1044, nil, nil, nil, nil, 1050, nil, 32,
- 929, 26, 41, 123, 182, 20108, 414, 1173, nil, 937,
- 2865, 20240, nil, nil, 1060, 3006, 24957, 24997, 23879, nil,
- nil, nil, nil, nil, nil, 3147, nil, nil, nil, nil,
- nil, nil, nil, 937, 20372, 2019, 20504, nil, 938, nil,
- 2160, nil, 2301, nil, nil, 2442, nil, 2583, nil, 2724,
- 20636, 20768, 20900, 21032, 429, 940, 940, 954, nil, 958,
- 961, 980, nil, 1009, 992, 991, 989, 21164, nil, nil,
- 1128, nil, nil, 3288, 1031, 1137, nil, nil, nil, nil,
- 1013, 378, nil, nil, 1149, nil, 3429, 1024, 1072, nil,
- nil, 1072, nil, nil, 3570, 3711, 1074, 1032, nil, nil,
- nil, 1033, 1036, nil, 1046, 1047, nil, 1051, nil, nil,
- 1055, 614, 1053, 600, nil, 1188, nil, 21296, 1190, 3852,
- 3993, nil, 21428, 4134, 81, 122, nil, 1191, 611, 4275,
- nil, 1192, 1078, 613, nil, 1087, 1095, nil, 2865, 21560,
- 21692, nil, 525, nil, nil, 25012, nil, 25020, nil, 7427,
- nil, nil, 1123, 1159, 21824, 933, 1181, nil, 1141, nil,
- nil, nil, 4416, nil, nil, 33, 21956, nil, nil, 1146,
- 1255, nil, nil, 25037, nil, 14059, nil, 25076, nil, 25093,
- nil, nil, nil, nil, 330, 3415, 1134, nil, 36, nil,
- 1264, 1269, nil, 47, nil, nil, nil, 1277, nil, nil,
- nil, 1197, nil, 22088, 1154, nil, nil, 1164, 1165, 1167,
- 1170, nil, 1172, nil, 643, nil, nil, nil, 963, 24747,
- nil, nil, nil, 4557, 1035, 1074, 1104, 1252, 1176, nil,
- nil, nil, 1174, 1177, 1187, 1188, 1189, 3556, 1192, 3589,
- 4698, nil, nil, nil, nil, nil, 4839, nil, 4980, 3006,
- nil, 25101, nil, 25116, nil, 25156, nil, 25171, nil, nil,
- nil, 1300, 1237, 1238, 1323, 22220, 24809, 24871, 1215, 25179,
- nil, nil, nil, nil, 3697, 1216, 862, 1347, 1361, 1240,
- 1243, 1261, 1262, nil, nil, 1270, 40, 42, 112, 1314,
- 1268, 1271, nil, nil, nil, 25186, nil, nil, nil, nil,
- 43, nil, 1275, nil ]
-
-racc_action_default = [
- -3, -597, -1, -583, -4, -597, -7, -597, -597, -597,
- -597, -29, -597, -597, -597, -281, -597, -40, -43, -585,
- -597, -48, -50, -51, -52, -56, -258, -258, -258, -295,
- -330, -331, -68, -11, -72, -80, -82, -597, -488, -489,
- -597, -597, -597, -597, -597, -585, -239, -272, -273, -274,
- -275, -276, -277, -278, -279, -280, -573, -283, -285, -596,
- -563, -303, -391, -597, -597, -308, -311, -583, -597, -597,
- -597, -597, -332, -333, -429, -430, -431, -432, -433, -454,
- -436, -437, -456, -458, -441, -446, -450, -452, -468, -456,
- -470, -472, -473, -474, -475, -571, -477, -478, -572, -480,
- -481, -482, -483, -484, -485, -486, -487, -492, -493, -597,
- -2, -584, -592, -593, -594, -6, -597, -597, -597, -597,
- -597, -3, -17, -597, -111, -112, -113, -114, -115, -116,
- -117, -118, -119, -123, -124, -125, -126, -127, -128, -129,
- -130, -131, -132, -133, -134, -135, -136, -137, -138, -139,
- -140, -141, -142, -143, -144, -145, -146, -147, -148, -149,
- -150, -151, -152, -153, -154, -155, -156, -157, -158, -159,
- -160, -161, -162, -163, -164, -165, -166, -167, -168, -169,
- -170, -171, -172, -173, -174, -175, -176, -177, -178, -179,
- -180, -181, -182, -183, -184, -185, -186, -187, -188, -189,
- -190, -191, -192, -193, -22, -120, -11, -597, -597, -248,
- -597, -597, -597, -597, -597, -597, -597, -585, -586, -47,
- -597, -488, -489, -597, -281, -597, -597, -229, -597, -11,
- -597, -597, -597, -597, -597, -597, -597, -597, -597, -597,
- -597, -597, -597, -597, -597, -597, -597, -597, -597, -597,
- -597, -597, -597, -597, -597, -597, -236, -398, -400, -597,
- -581, -582, -57, -248, -597, -302, -404, -413, -415, -63,
- -410, -64, -585, -65, -240, -253, -262, -262, -257, -597,
- -263, -597, -454, -565, -597, -597, -66, -67, -583, -12,
- -597, -15, -597, -70, -11, -585, -597, -73, -76, -11,
- -88, -89, -597, -597, -96, -295, -298, -585, -597, -330,
- -331, -334, -411, -597, -78, -597, -84, -292, -471, -597,
- -214, -215, -230, -597, -11, -597, -585, -241, -589, -589,
- -597, -597, -589, -597, -304, -305, -521, -49, -597, -597,
- -597, -597, -583, -597, -584, -488, -489, -597, -597, -281,
- -597, -344, -345, -106, -107, -597, -109, -597, -281, -597,
- -597, -488, -489, -323, -111, -112, -153, -154, -155, -171,
- -176, -183, -186, -325, -597, -561, -597, -434, -597, -597,
- -597, -597, -597, -597, -597, -597, 1024, -5, -595, -23,
- -24, -25, -26, -27, -597, -597, -19, -20, -21, -121,
- -597, -30, -39, -268, -597, -597, -267, -31, -196, -585,
- -249, -262, -262, -574, -575, -258, -408, -576, -577, -575,
- -574, -258, -407, -409, -576, -577, -37, -204, -38, -597,
- -41, -42, -194, -263, -44, -45, -46, -585, -301, -597,
- -597, -597, -248, -292, -597, -597, -597, -205, -206, -207,
- -208, -209, -210, -211, -212, -216, -217, -218, -219, -220,
- -221, -222, -223, -224, -225, -226, -227, -228, -231, -232,
- -233, -234, -597, -380, -258, -574, -575, -54, -58, -585,
- -259, -380, -380, -585, -297, -254, -597, -255, -597, -260,
- -597, -264, -597, -568, -570, -10, -584, -14, -3, -585,
- -69, -290, -85, -74, -597, -585, -248, -597, -597, -95,
- -597, -471, -597, -81, -86, -597, -597, -597, -597, -235,
- -597, -421, -597, -286, -597, -242, -591, -590, -244, -591,
- -293, -294, -564, -392, -521, -395, -560, -560, -504, -506,
- -506, -506, -520, -522, -523, -524, -525, -526, -527, -528,
- -529, -597, -531, -533, -535, -540, -542, -543, -545, -550,
- -552, -553, -555, -556, -557, -597, -11, -335, -336, -11,
- -597, -597, -597, -597, -597, -248, -597, -597, -292, -316,
- -106, -107, -108, -597, -597, -248, -319, -494, -495, -597,
- -597, -11, -499, -327, -585, -435, -455, -460, -597, -462,
- -438, -457, -597, -459, -440, -597, -443, -597, -445, -448,
- -597, -449, -597, -469, -8, -18, -597, -28, -271, -597,
- -597, -412, -597, -250, -252, -597, -597, -59, -247, -405,
- -597, -597, -61, -406, -597, -597, -300, -587, -574, -575,
- -574, -575, -585, -194, -585, -381, -585, -383, -11, -53,
- -401, -380, -245, -11, -11, -296, -262, -261, -265, -597,
- -566, -567, -597, -13, -597, -71, -597, -77, -83, -585,
- -574, -575, -246, -92, -94, -597, -79, -597, -203, -213,
- -585, -596, -596, -284, -585, -289, -589, -597, -585, -597,
- -502, -503, -597, -597, -513, -597, -516, -597, -518, -597,
- -346, -597, -348, -350, -357, -585, -534, -544, -554, -558,
- -596, -337, -596, -309, -338, -339, -312, -597, -315, -597,
- -585, -574, -575, -578, -291, -597, -106, -107, -110, -585,
- -11, -597, -497, -321, -597, -11, -521, -521, -597, -562,
- -461, -464, -465, -466, -467, -11, -439, -442, -444, -447,
- -451, -453, -122, -269, -597, -197, -597, -588, -262, -33,
- -199, -34, -200, -60, -35, -202, -36, -201, -62, -195,
- -597, -597, -597, -597, -412, -597, -560, -560, -362, -364,
- -364, -364, -379, -597, -585, -385, -529, -537, -538, -548,
- -597, -403, -402, -11, -597, -597, -256, -266, -569, -16,
- -75, -90, -87, -299, -596, -342, -11, -422, -596, -423,
- -424, -597, -243, -393, -11, -11, -597, -560, -541, -559,
- -505, -506, -506, -532, -506, -506, -551, -506, -529, -546,
- -585, -597, -355, -597, -530, -597, -340, -597, -597, -11,
- -11, -314, -597, -11, -412, -597, -412, -597, -597, -11,
- -324, -597, -585, -597, -328, -597, -270, -32, -198, -251,
- -597, -237, -597, -360, -361, -370, -372, -597, -375, -597,
- -377, -382, -597, -597, -597, -536, -597, -399, -597, -414,
- -416, -9, -11, -428, -343, -597, -597, -426, -287, -597,
- -597, -394, -501, -597, -509, -597, -511, -597, -514, -597,
- -517, -519, -347, -349, -353, -597, -358, -306, -597, -307,
- -597, -597, -265, -596, -317, -320, -496, -597, -326, -498,
- -500, -499, -463, -597, -560, -539, -363, -364, -364, -364,
- -364, -549, -364, -384, -585, -387, -389, -390, -547, -597,
- -292, -55, -427, -11, -97, -98, -597, -597, -105, -425,
- -396, -397, -506, -506, -506, -506, -351, -597, -356, -597,
- -11, -310, -313, -417, -418, -419, -11, -322, -11, -238,
- -359, -597, -367, -597, -369, -597, -373, -597, -376, -378,
- -386, -597, -291, -578, -421, -248, -597, -597, -104, -597,
- -507, -510, -512, -515, -597, -354, -596, -597, -597, -364,
- -364, -364, -364, -388, -420, -585, -574, -575, -578, -103,
- -506, -352, -341, -318, -329, -597, -365, -368, -371, -374,
- -412, -508, -364, -366 ]
-
-racc_goto_table = [
- 216, 275, 275, 275, 14, 327, 373, 409, 573, 14,
- 522, 276, 276, 276, 266, 270, 311, 311, 258, 2,
- 415, 421, 334, 431, 220, 681, 323, 259, 122, 205,
- 535, 127, 127, 220, 220, 220, 406, 14, 302, 302,
- 550, 328, 428, 478, 297, 130, 130, 132, 132, 542,
- 311, 311, 311, 110, 114, 338, 339, 487, 438, 342,
- 513, 479, 735, 835, 582, 318, 660, 220, 220, 474,
- 111, 220, 347, 357, 357, 705, 621, 826, 219, 6,
- 314, 690, 691, 804, 6, 783, 566, 569, 713, 716,
- 378, 293, 295, 780, 127, 262, 269, 271, 906, 903,
- 525, 528, 115, 931, 532, 379, 935, 660, 389, 390,
- 391, 392, 385, 484, 838, 14, 937, 343, 114, 1,
- 220, 220, 220, 220, 14, 14, 881, 816, 359, 363,
- 648, 273, 286, 287, 605, 607, 500, 663, 653, 654,
- 394, 823, 204, 352, 402, 395, 277, 277, 277, 651,
- 616, 472, 657, 13, 657, 923, 330, 650, 13, 375,
- 331, 335, 350, 586, 374, 324, 325, 684, 326, 340,
- 958, 694, 696, 698, 839, 341, 329, 332, 840, 725,
- 966, 275, 730, 849, 591, 592, 13, 738, 921, 405,
- 6, 782, 487, 784, 934, 405, 533, 813, 416, 393,
- 6, 700, 660, 336, 687, 473, 481, 931, 387, 482,
- 14, 220, 220, 220, 1003, 963, 220, 220, 220, 220,
- 220, 220, 995, 872, 937, 830, 808, 903, 688, 885,
- 377, 296, 380, 14, 425, 275, 275, 717, 550, 381,
- 642, 382, 383, 384, 275, 740, 276, 542, 745, 667,
- 415, 421, 731, 821, 276, 818, 925, 676, nil, 1011,
- nil, nil, nil, nil, 13, 401, 407, 220, 220, nil,
- 426, 430, nil, 13, 13, 826, 220, 734, 636, nil,
- 311, 601, 603, 606, 606, nil, nil, 601, 510, 657,
- 657, nil, 728, 492, 14, nil, 266, 311, 14, nil,
- 270, nil, 302, 14, 669, nil, nil, 524, 793, 774,
- nil, nil, 927, 823, nil, 964, nil, 672, nil, 302,
- 652, 863, 864, nil, 655, 514, nil, 672, 14, 220,
- nil, nil, 570, 571, nil, nil, 801, 511, 495, 114,
- 665, nil, nil, 503, 220, 220, 668, 823, nil, 1012,
- 786, 700, 293, 499, 832, 496, nil, 293, 505, 13,
- nil, nil, 892, 914, 220, nil, 480, nil, nil, 497,
- nil, nil, nil, 720, 483, nil, nil, 593, nil, nil,
- 220, 277, 13, 729, nil, nil, 672, 844, nil, 277,
- 622, 968, 572, 114, 672, nil, 846, 550, nil, 550,
- nil, nil, nil, 628, 400, nil, nil, nil, 763, 633,
- 587, 800, nil, 768, 275, 615, 847, nil, 127, nil,
- nil, 851, nil, nil, nil, 823, nil, 296, 416, nil,
- 852, 853, 130, nil, 132, 739, 796, 660, nil, nil,
- 550, 550, nil, 13, 431, nil, 220, 13, nil, 542,
- 542, nil, 13, 894, 896, nil, 898, 900, 812, 901,
- nil, 333, 628, nil, 425, nil, nil, 843, nil, 970,
- nil, nil, nil, nil, nil, nil, nil, 13, 275, nil,
- nil, 700, nil, 700, 627, nil, 311, nil, 1004, nil,
- 632, nil, 296, nil, 311, 416, nil, 296, nil, 26,
- 14, nil, 14, nil, 26, 416, nil, 748, 302, 748,
- 220, 809, 781, nil, 514, nil, 302, 664, nil, 26,
- nil, 803, 514, nil, 220, 657, nil, nil, 26, 26,
- 26, 425, 26, nil, nil, 917, nil, nil, 796, nil,
- nil, 425, nil, 649, nil, nil, 834, 275, nil, nil,
- nil, nil, nil, nil, 956, 700, 719, 275, nil, 822,
- nil, 824, 26, 26, 416, nil, 26, nil, nil, 786,
- 14, 786, 416, 14, nil, nil, nil, 6, 965, 220,
- nil, nil, nil, nil, 990, 991, 992, 993, nil, 220,
- nil, nil, nil, nil, nil, 14, nil, 550, nil, nil,
- 425, nil, nil, 656, nil, nil, 425, 700, 403, 700,
- 26, 792, nil, nil, 433, 26, 26, 26, 26, 26,
- 26, 714, 714, 622, nil, 775, nil, 785, 810, 220,
- 220, nil, nil, 943, 220, 220, 752, nil, 220, 127,
- 732, 733, 1021, 686, 700, 791, nil, nil, 311, 13,
- 622, 13, 14, 130, 998, 132, 960, 14, 14, 311,
- 628, nil, nil, 633, 405, 811, nil, nil, nil, 785,
- 302, 902, 1020, 854, nil, 786, 514, nil, nil, nil,
- 489, 302, 491, 759, 761, 493, 494, 802, 764, 766,
- nil, nil, 430, 919, nil, nil, nil, nil, nil, nil,
- nil, 622, nil, nil, nil, 26, 26, 26, 26, nil,
- 622, 26, 26, 26, 26, 26, 26, 845, nil, 13,
- nil, nil, 13, 848, nil, nil, nil, nil, 26, 220,
- nil, 928, nil, 929, 14, 220, nil, nil, nil, 14,
- 758, nil, nil, nil, 13, nil, nil, nil, nil, 14,
- 883, nil, 15, nil, 887, nil, nil, 15, nil, 952,
- 220, 127, 26, 26, 710, 785, nil, 712, nil, 311,
- nil, 26, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 1005, nil, 15, 304, 304, nil, 26,
- nil, 875, nil, 26, nil, nil, 672, 14, 26, nil,
- nil, 13, nil, 16, nil, 618, 13, 13, 16, nil,
- 14, nil, nil, nil, 857, nil, nil, nil, 14, 14,
- 349, 358, 358, 26, 26, nil, nil, nil, nil, 908,
- nil, nil, nil, nil, nil, nil, 16, 999, nil, 26,
- 26, 220, nil, 14, 14, nil, 790, 14, nil, nil,
- nil, 794, 795, 14, 777, nil, 311, nil, nil, 26,
- nil, nil, nil, 15, nil, nil, 778, nil, 311, nil,
- nil, 351, 15, 15, nil, 26, nil, nil, 938, nil,
- nil, nil, nil, 13, nil, nil, 14, 658, 13, 333,
- 946, 661, nil, 779, nil, nil, 714, 817, 13, 916,
- nil, nil, nil, nil, 920, nil, nil, nil, nil, 819,
- nil, nil, 819, nil, 16, 980, nil, nil, nil, nil,
- nil, nil, nil, 16, 16, nil, nil, 658, nil, nil,
- 333, nil, nil, nil, nil, nil, nil, nil, 787, nil,
- nil, 26, 825, 855, 827, nil, 13, 14, nil, nil,
- nil, nil, nil, nil, nil, 706, nil, 275, 15, 13,
- nil, nil, 425, nil, 14, nil, nil, 13, 13, nil,
- 14, nil, 14, nil, 416, 433, nil, nil, nil, nil,
- nil, 15, nil, nil, nil, nil, 622, nil, nil, 220,
- nil, 878, 13, 13, nil, 26, 13, 26, nil, nil,
- nil, nil, 13, nil, 884, 26, nil, nil, nil, 16,
- 425, nil, 889, 890, nil, nil, 429, nil, nil, 26,
- 753, nil, nil, nil, 658, 333, nil, nil, nil, nil,
- nil, nil, 16, nil, nil, 13, nil, 910, 911, nil,
- nil, 913, 15, nil, nil, nil, 15, nil, nil, nil,
- 304, 15, nil, nil, nil, 337, 337, nil, nil, 337,
- 797, nil, nil, 798, nil, 26, nil, 304, 26, nil,
- 924, nil, nil, 777, 26, 777, 15, 777, nil, nil,
- 942, nil, 819, 807, 26, 778, nil, 778, nil, 778,
- 26, nil, nil, 16, nil, nil, 13, 16, nil, nil,
- 829, nil, 16, nil, nil, nil, nil, nil, 337, 337,
- 337, 337, nil, 13, 930, nil, 932, nil, nil, 13,
- nil, 13, nil, nil, 26, 26, nil, 16, nil, 26,
- 26, nil, nil, 26, 866, 868, 870, nil, nil, nil,
- 953, 984, 954, nil, 955, nil, nil, 26, nil, nil,
- nil, nil, 26, 26, 787, 856, nil, 787, 996, 787,
- nil, 787, nil, nil, 997, nil, nil, nil, nil, nil,
- 38, nil, nil, nil, nil, 38, nil, nil, nil, 777,
- nil, 777, nil, 777, nil, 777, nil, nil, nil, nil,
- nil, 778, nil, 778, nil, 778, nil, 778, nil, nil,
- nil, nil, nil, 38, 300, 300, 434, 435, 436, 437,
- nil, nil, nil, nil, nil, nil, nil, nil, 1000, nil,
- 1001, nil, 1002, 777, 26, nil, nil, nil, nil, 26,
- 26, nil, nil, nil, 26, 778, 1010, nil, 345, 361,
- 361, 361, nil, 912, 26, nil, nil, nil, 15, nil,
- 15, nil, nil, nil, nil, 26, 304, nil, nil, nil,
- 333, nil, 1022, 787, 304, 787, nil, 787, nil, 787,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 38, 972, 974, 976, 978, nil, 979, nil, nil,
- 38, 38, 26, nil, nil, nil, nil, nil, nil, 16,
- nil, 16, nil, nil, nil, 26, nil, 787, nil, nil,
- nil, nil, nil, 26, 26, nil, nil, 523, 15, nil,
- nil, 15, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 337, 337, nil, nil, 26, nil, 26, 26,
- nil, nil, 26, 15, nil, nil, nil, nil, 26, nil,
- 744, nil, 590, nil, 1016, 1017, 1018, 1019, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 594, 16,
- nil, nil, 16, nil, nil, nil, 38, 1023, nil, nil,
- nil, 26, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 16, nil, nil, nil, nil, 38,
- 15, nil, nil, nil, nil, 15, 15, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 304, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 304,
- nil, nil, nil, nil, nil, nil, nil, 429, nil, nil,
- nil, nil, 26, nil, nil, nil, nil, nil, nil, nil,
- nil, 16, nil, nil, nil, nil, 16, 16, 39, 26,
- 38, nil, nil, 39, 38, 26, nil, 26, 300, 38,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 15, nil, 26, 300, nil, 15, nil, nil,
- nil, 39, 301, 301, 38, nil, nil, 15, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 680, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 346, 362, 362, 362,
- nil, nil, nil, 16, nil, nil, nil, nil, 16, 358,
- nil, nil, nil, nil, nil, 15, nil, nil, 16, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 15, nil,
- nil, nil, nil, nil, nil, nil, 15, 15, nil, 39,
- nil, nil, nil, nil, nil, nil, nil, nil, 39, 39,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 15, 15, nil, nil, 15, 16, nil, nil, nil,
- nil, 15, nil, nil, nil, nil, nil, nil, nil, 16,
- nil, nil, nil, nil, nil, nil, nil, 16, 16, nil,
- nil, nil, nil, nil, nil, nil, 358, nil, nil, nil,
- nil, nil, nil, nil, 15, nil, nil, nil, 948, nil,
- nil, nil, 16, 16, nil, nil, 16, nil, nil, nil,
- nil, nil, 16, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 39, nil, 38, nil, 38, nil,
- nil, nil, nil, nil, 300, nil, nil, nil, nil, nil,
- nil, nil, 300, nil, nil, 16, nil, 39, nil, 949,
- nil, nil, nil, nil, nil, 15, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 15, nil, nil, nil, nil, 337, 15, nil,
- 15, nil, nil, 337, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 38, nil, nil, 38,
- nil, nil, nil, nil, nil, nil, 16, nil, 39, nil,
- nil, nil, 39, nil, nil, nil, 301, 39, nil, nil,
- nil, 38, nil, 16, nil, nil, nil, nil, nil, 16,
- nil, 16, nil, 301, nil, nil, nil, nil, nil, nil,
- nil, nil, 39, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 38, nil,
- nil, nil, nil, 38, 38, nil, nil, nil, nil, 337,
- nil, nil, nil, nil, nil, nil, 300, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 300, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 38, nil, nil, nil, nil, 38, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 38, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 39, nil, 39, 361, nil, nil,
- nil, nil, 301, 38, nil, nil, nil, nil, nil, nil,
- 301, nil, nil, nil, nil, nil, 38, nil, nil, nil,
- nil, nil, nil, nil, 38, 38, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 38,
- 38, nil, nil, 38, nil, nil, nil, nil, nil, 38,
- nil, nil, nil, nil, 39, nil, nil, 39, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 361, nil, nil, nil, nil, 39,
- nil, nil, 38, nil, nil, nil, 944, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 39, nil, nil, nil,
- nil, 39, 39, 38, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 301, nil, nil, nil, nil, nil,
- 38, nil, nil, nil, 227, 301, 38, nil, 38, nil,
- nil, nil, nil, 274, 274, 274, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 320, 321, 322, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 274, 274, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 39, nil,
- nil, nil, nil, 39, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 39, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 362, nil, nil, nil, nil,
- nil, 39, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 39, nil, nil, nil, nil, nil,
- nil, nil, 39, 39, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 39, 39, nil,
- nil, 39, nil, nil, nil, nil, nil, 39, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 274, 408, 274, nil, nil, 427, 432, nil, nil,
- nil, nil, 362, nil, nil, nil, nil, nil, nil, nil,
- 39, nil, 227, nil, 945, 447, 448, 449, 450, 451,
- 452, 453, 454, 455, 456, 457, 458, 459, 460, 461,
- 462, 463, 464, 465, 466, 467, 468, 469, 470, 471,
- nil, nil, nil, nil, nil, nil, nil, 274, 274, nil,
- nil, nil, nil, nil, nil, nil, 274, nil, nil, nil,
- nil, nil, nil, 274, nil, 274, nil, nil, 274, 274,
- nil, 39, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 39, nil,
- nil, nil, nil, nil, 39, nil, 39, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 519, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 274, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 274, nil, 427, 643,
- 408, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 644, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 274, nil, 274, nil, 274, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 274, nil, nil, nil, nil, nil, nil, nil, nil, 678,
- 679, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 274, nil, nil, 274, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 274, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 274, 274,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 274,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 274, 755, nil, nil, 274, 274, 760,
- 762, nil, nil, nil, 765, 767, nil, nil, 643, 769,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 274, nil, nil, 274, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 274, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 274, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 274, nil,
- 858, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 760, 762, 767, 765, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 274, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 274, 858, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 969, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 274 ]
-
-racc_goto_check = [
- 31, 33, 33, 33, 22, 69, 56, 23, 94, 22,
- 8, 70, 70, 70, 73, 73, 64, 64, 140, 2,
- 37, 37, 88, 18, 22, 10, 31, 36, 15, 15,
- 139, 57, 57, 22, 22, 22, 27, 22, 22, 22,
- 168, 70, 27, 39, 49, 60, 60, 61, 61, 163,
- 64, 64, 64, 4, 97, 17, 17, 75, 47, 17,
- 51, 23, 98, 92, 54, 63, 178, 22, 22, 37,
- 6, 22, 22, 22, 22, 118, 24, 174, 20, 7,
- 50, 123, 123, 11, 7, 131, 91, 91, 93, 93,
- 151, 45, 46, 128, 57, 38, 38, 38, 119, 116,
- 71, 71, 5, 175, 71, 152, 133, 178, 17, 17,
- 17, 17, 152, 47, 11, 22, 134, 4, 97, 1,
- 22, 22, 22, 22, 22, 22, 12, 131, 55, 55,
- 40, 44, 44, 44, 155, 155, 47, 14, 40, 40,
- 2, 169, 16, 19, 28, 30, 72, 72, 72, 42,
- 62, 66, 76, 21, 76, 67, 68, 74, 21, 86,
- 87, 90, 95, 96, 99, 100, 101, 102, 103, 104,
- 119, 166, 166, 166, 105, 106, 72, 72, 107, 108,
- 109, 33, 110, 111, 112, 113, 21, 114, 115, 70,
- 7, 120, 75, 126, 132, 70, 135, 136, 73, 7,
- 7, 117, 178, 137, 138, 141, 143, 175, 5, 144,
- 22, 22, 22, 22, 133, 145, 22, 22, 22, 22,
- 22, 22, 119, 131, 134, 118, 146, 116, 139, 147,
- 150, 9, 153, 22, 57, 33, 33, 94, 168, 154,
- 23, 157, 158, 159, 33, 160, 70, 163, 161, 51,
- 37, 37, 162, 167, 70, 171, 172, 51, nil, 119,
- nil, nil, nil, nil, 21, 20, 20, 22, 22, nil,
- 20, 20, nil, 21, 21, 174, 22, 8, 47, nil,
- 64, 156, 156, 156, 156, nil, nil, 156, 31, 76,
- 76, nil, 54, 151, 22, nil, 73, 64, 22, nil,
- 73, nil, 22, 22, 23, nil, nil, 31, 40, 24,
- nil, nil, 128, 169, nil, 11, nil, 37, nil, 22,
- 47, 123, 123, nil, 47, 49, nil, 37, 22, 22,
- nil, nil, 17, 17, nil, nil, 24, 63, 4, 97,
- 47, nil, nil, 50, 22, 22, 47, 169, nil, 92,
- 168, 117, 45, 46, 117, 6, nil, 45, 46, 21,
- nil, nil, 123, 93, 22, nil, 44, nil, nil, 7,
- nil, nil, nil, 23, 44, nil, nil, 36, nil, nil,
- 22, 72, 21, 23, nil, nil, 37, 24, nil, 72,
- 31, 98, 4, 97, 37, nil, 24, 168, nil, 168,
- nil, nil, nil, 73, 9, nil, nil, nil, 39, 73,
- 97, 51, nil, 39, 33, 15, 8, nil, 57, nil,
- nil, 8, nil, nil, nil, 169, nil, 9, 73, nil,
- 139, 139, 60, nil, 61, 47, 75, 178, nil, nil,
- 168, 168, nil, 21, 18, nil, 22, 21, nil, 163,
- 163, nil, 21, 166, 166, nil, 166, 166, 71, 166,
- nil, 65, 73, nil, 57, nil, nil, 91, nil, 123,
- nil, nil, nil, nil, nil, nil, nil, 21, 33, nil,
- nil, 117, nil, 117, 38, nil, 64, nil, 10, nil,
- 38, nil, 9, nil, 64, 73, nil, 9, nil, 41,
- 22, nil, 22, nil, 41, 73, nil, 156, 22, 156,
- 22, 27, 129, nil, 49, nil, 22, 2, nil, 41,
- nil, 47, 49, nil, 22, 76, nil, nil, 41, 41,
- 41, 57, 41, nil, nil, 8, nil, nil, 75, nil,
- nil, 57, nil, 38, nil, nil, 47, 33, nil, nil,
- nil, nil, nil, nil, 117, 117, 70, 33, nil, 129,
- nil, 129, 41, 41, 73, nil, 41, nil, nil, 168,
- 22, 168, 73, 22, nil, nil, nil, 7, 94, 22,
- nil, nil, nil, nil, 166, 166, 166, 166, nil, 22,
- nil, nil, nil, nil, nil, 22, nil, 168, nil, nil,
- 57, nil, nil, 72, nil, nil, 57, 117, 65, 117,
- 41, 88, nil, nil, 65, 41, 41, 41, 41, 41,
- 41, 97, 97, 31, nil, 31, nil, 31, 69, 22,
- 22, nil, nil, 91, 22, 22, 15, nil, 22, 57,
- 97, 97, 166, 72, 117, 140, nil, nil, 64, 21,
- 31, 21, 22, 60, 8, 61, 91, 22, 22, 64,
- 73, nil, nil, 73, 70, 31, nil, nil, nil, 31,
- 22, 47, 24, 56, nil, 168, 49, nil, nil, nil,
- 65, 22, 65, 20, 20, 65, 65, 49, 20, 20,
- nil, nil, 20, 47, nil, nil, nil, nil, nil, nil,
- nil, 31, nil, nil, nil, 41, 41, 41, 41, nil,
- 31, 41, 41, 41, 41, 41, 41, 17, nil, 21,
- nil, nil, 21, 17, nil, nil, nil, nil, 41, 22,
- nil, 129, nil, 129, 22, 22, nil, nil, nil, 22,
- 72, nil, nil, nil, 21, nil, nil, nil, nil, 22,
- 69, nil, 25, nil, 69, nil, nil, 25, nil, 129,
- 22, 57, 41, 41, 9, 31, nil, 9, nil, 64,
- nil, 41, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 23, nil, 25, 25, 25, nil, 41,
- nil, 22, nil, 41, nil, nil, 37, 22, 41, nil,
- nil, 21, nil, 26, nil, 65, 21, 21, 26, nil,
- 22, nil, nil, nil, 20, nil, nil, nil, 22, 22,
- 25, 25, 25, 41, 41, nil, nil, nil, nil, 17,
- nil, nil, nil, nil, nil, nil, 26, 129, nil, 41,
- 41, 22, nil, 22, 22, nil, 9, 22, nil, nil,
- nil, 9, 9, 22, 122, nil, 64, nil, nil, 41,
- nil, nil, nil, 25, nil, nil, 124, nil, 64, nil,
- nil, 26, 25, 25, nil, 41, nil, nil, 22, nil,
- nil, nil, nil, 21, nil, nil, 22, 65, 21, 65,
- 22, 65, nil, 127, nil, nil, 97, 122, 21, 97,
- nil, nil, nil, nil, 97, nil, nil, nil, nil, 124,
- nil, nil, 124, nil, 26, 31, nil, nil, nil, nil,
- nil, nil, nil, 26, 26, nil, nil, 65, nil, nil,
- 65, nil, nil, nil, nil, nil, nil, nil, 170, nil,
- nil, 41, 127, 9, 127, nil, 21, 22, nil, nil,
- nil, nil, nil, nil, nil, 65, nil, 33, 25, 21,
- nil, nil, 57, nil, 22, nil, nil, 21, 21, nil,
- 22, nil, 22, nil, 73, 65, nil, nil, nil, nil,
- nil, 25, nil, nil, nil, nil, 31, nil, nil, 22,
- nil, 9, 21, 21, nil, 41, 21, 41, nil, nil,
- nil, nil, 21, nil, 9, 41, nil, nil, nil, 26,
- 57, nil, 9, 9, nil, nil, 26, nil, nil, 41,
- 65, nil, nil, nil, 65, 65, nil, nil, nil, nil,
- nil, nil, 26, nil, nil, 21, nil, 9, 9, nil,
- nil, 9, 25, nil, nil, nil, 25, nil, nil, nil,
- 25, 25, nil, nil, nil, 29, 29, nil, nil, 29,
- 65, nil, nil, 65, nil, 41, nil, 25, 41, nil,
- 122, nil, nil, 122, 41, 122, 25, 122, nil, nil,
- 9, nil, 124, 65, 41, 124, nil, 124, nil, 124,
- 41, nil, nil, 26, nil, nil, 21, 26, nil, nil,
- 65, nil, 26, nil, nil, nil, nil, nil, 29, 29,
- 29, 29, nil, 21, 127, nil, 127, nil, nil, 21,
- nil, 21, nil, nil, 41, 41, nil, 26, nil, 41,
- 41, nil, nil, 41, 125, 125, 125, nil, nil, nil,
- 127, 9, 127, nil, 127, nil, nil, 41, nil, nil,
- nil, nil, 41, 41, 170, 65, nil, 170, 9, 170,
- nil, 170, nil, nil, 9, nil, nil, nil, nil, nil,
- 52, nil, nil, nil, nil, 52, nil, nil, nil, 122,
- nil, 122, nil, 122, nil, 122, nil, nil, nil, nil,
- nil, 124, nil, 124, nil, 124, nil, 124, nil, nil,
- nil, nil, nil, 52, 52, 52, 29, 29, 29, 29,
- nil, nil, nil, nil, nil, nil, nil, nil, 127, nil,
- 127, nil, 127, 122, 41, nil, nil, nil, nil, 41,
- 41, nil, nil, nil, 41, 124, 127, nil, 52, 52,
- 52, 52, nil, 65, 41, nil, nil, nil, 25, nil,
- 25, nil, nil, nil, nil, 41, 25, nil, nil, nil,
- 65, nil, 127, 170, 25, 170, nil, 170, nil, 170,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 52, 125, 125, 125, 125, nil, 125, nil, nil,
- 52, 52, 41, nil, nil, nil, nil, nil, nil, 26,
- nil, 26, nil, nil, nil, 41, nil, 170, nil, nil,
- nil, nil, nil, 41, 41, nil, nil, 29, 25, nil,
- nil, 25, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 29, 29, nil, nil, 41, nil, 41, 41,
- nil, nil, 41, 25, nil, nil, nil, nil, 41, nil,
- 25, nil, 29, nil, 125, 125, 125, 125, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 29, 26,
- nil, nil, 26, nil, nil, nil, 52, 125, nil, nil,
- nil, 41, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 26, nil, nil, nil, nil, 52,
- 25, nil, nil, nil, nil, 25, 25, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 25, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 25,
- nil, nil, nil, nil, nil, nil, nil, 26, nil, nil,
- nil, nil, 41, nil, nil, nil, nil, nil, nil, nil,
- nil, 26, nil, nil, nil, nil, 26, 26, 53, 41,
- 52, nil, nil, 53, 52, 41, nil, 41, 52, 52,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 25, nil, 41, 52, nil, 25, nil, nil,
- nil, 53, 53, 53, 52, nil, nil, 25, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 29, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 53, 53, 53, 53,
- nil, nil, nil, 26, nil, nil, nil, nil, 26, 25,
- nil, nil, nil, nil, nil, 25, nil, nil, 26, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 25, nil,
- nil, nil, nil, nil, nil, nil, 25, 25, nil, 53,
- nil, nil, nil, nil, nil, nil, nil, nil, 53, 53,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 25, 25, nil, nil, 25, 26, nil, nil, nil,
- nil, 25, nil, nil, nil, nil, nil, nil, nil, 26,
- nil, nil, nil, nil, nil, nil, nil, 26, 26, nil,
- nil, nil, nil, nil, nil, nil, 25, nil, nil, nil,
- nil, nil, nil, nil, 25, nil, nil, nil, 25, nil,
- nil, nil, 26, 26, nil, nil, 26, nil, nil, nil,
- nil, nil, 26, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 53, nil, 52, nil, 52, nil,
- nil, nil, nil, nil, 52, nil, nil, nil, nil, nil,
- nil, nil, 52, nil, nil, 26, nil, 53, nil, 26,
- nil, nil, nil, nil, nil, 25, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 25, nil, nil, nil, nil, 29, 25, nil,
- 25, nil, nil, 29, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 52, nil, nil, 52,
- nil, nil, nil, nil, nil, nil, 26, nil, 53, nil,
- nil, nil, 53, nil, nil, nil, 53, 53, nil, nil,
- nil, 52, nil, 26, nil, nil, nil, nil, nil, 26,
- nil, 26, nil, 53, nil, nil, nil, nil, nil, nil,
- nil, nil, 53, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 52, nil,
- nil, nil, nil, 52, 52, nil, nil, nil, nil, 29,
- nil, nil, nil, nil, nil, nil, 52, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 52, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 52, nil, nil, nil, nil, 52, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 52, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 53, nil, 53, 52, nil, nil,
- nil, nil, 53, 52, nil, nil, nil, nil, nil, nil,
- 53, nil, nil, nil, nil, nil, 52, nil, nil, nil,
- nil, nil, nil, nil, 52, 52, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 52,
- 52, nil, nil, 52, nil, nil, nil, nil, nil, 52,
- nil, nil, nil, nil, 53, nil, nil, 53, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 52, nil, nil, nil, nil, 53,
- nil, nil, 52, nil, nil, nil, 52, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 53, nil, nil, nil,
- nil, 53, 53, 52, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 53, nil, nil, nil, nil, nil,
- 52, nil, nil, nil, 32, 53, 52, nil, 52, nil,
- nil, nil, nil, 32, 32, 32, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 32, 32, 32, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 32, 32, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 53, nil,
- nil, nil, nil, 53, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 53, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 53, nil, nil, nil, nil,
- nil, 53, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 53, nil, nil, nil, nil, nil,
- nil, nil, 53, 53, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 53, 53, nil,
- nil, 53, nil, nil, nil, nil, nil, 53, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 32, 32, 32, nil, nil, 32, 32, nil, nil,
- nil, nil, 53, nil, nil, nil, nil, nil, nil, nil,
- 53, nil, 32, nil, 53, 32, 32, 32, 32, 32,
- 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
- 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
- nil, nil, nil, nil, nil, nil, nil, 32, 32, nil,
- nil, nil, nil, nil, nil, nil, 32, nil, nil, nil,
- nil, nil, nil, 32, nil, 32, nil, nil, 32, 32,
- nil, 53, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 53, nil,
- nil, nil, nil, nil, 53, nil, 53, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 32, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 32, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 32, nil, 32, 32,
- 32, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 32, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 32, nil, 32, nil, 32, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 32, nil, nil, nil, nil, nil, nil, nil, nil, 32,
- 32, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 32, nil, nil, 32, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 32, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 32, 32,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 32,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 32, 32, nil, nil, 32, 32, 32,
- 32, nil, nil, nil, 32, 32, nil, nil, 32, 32,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 32, nil, nil, 32, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 32, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 32, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 32, nil,
- 32, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 32, 32, 32, 32, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 32, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 32, 32, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 32, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 32 ]
-
-racc_goto_pointer = [
- nil, 119, 19, nil, 50, 97, 67, 79, -314, 198,
- -496, -598, -678, nil, -359, 20, 133, -8, -190, 75,
- 58, 153, 4, -202, -333, 752, 803, -171, -63, 992,
- 23, -19, 2114, -28, nil, nil, 3, -190, 69, -219,
- -343, 499, -328, nil, 102, 58, 59, -159, nil, 10,
- 45, -255, 1170, 1458, -291, 59, -65, 23, nil, nil,
- 37, 39, -249, 24, -18, 401, -105, -706, 97, -54,
- -18, -228, 117, -12, -317, -220, -334, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 88, 100, -39, nil,
- 99, -252, -647, -482, -335, 94, -196, 51, -530, 93,
- 108, 108, -356, 110, 104, -539, 109, -538, -400, -734,
- -404, -550, -179, -188, -406, -666, -732, -350, -476, -735,
- -455, nil, 208, -455, 220, 355, -453, 247, -553, -134,
- nil, -561, -679, -767, -757, -140, -490, 141, -329, -306,
- -4, -52, nil, -61, -59, -698, -456, -579, nil, nil,
- 153, 11, 23, 149, 155, -247, -98, 156, 156, 156,
- -353, -351, -336, -287, nil, nil, -368, -440, -296, -552,
- 292, -434, -606, nil, -618, -764, nil, nil, -422 ]
-
-racc_goto_default = [
- nil, nil, nil, 3, nil, 4, 344, 291, nil, 521,
- nil, 836, nil, 288, 289, nil, nil, nil, 11, 12,
- 18, 226, 319, nil, nil, 224, 225, nil, nil, 17,
- nil, 439, 21, 22, 23, 24, nil, 675, nil, nil,
- nil, 308, nil, 25, 410, 32, nil, nil, 34, 37,
- 36, nil, 221, 222, 356, nil, 129, 418, 128, 131,
- 75, 76, nil, 90, 46, 280, nil, nil, nil, 805,
- 411, nil, 412, 423, 629, 485, 278, 264, 47, 48,
- 49, 50, 51, 52, 53, 54, 55, nil, 265, 61,
- nil, nil, nil, nil, nil, nil, nil, 567, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 702, 549, nil, 703,
- 926, 776, 537, nil, 538, nil, nil, 539, nil, 541,
- 645, nil, nil, nil, 547, nil, nil, nil, nil, nil,
- nil, nil, 422, nil, nil, nil, nil, nil, 74, 77,
- 78, nil, nil, nil, nil, nil, 596, nil, nil, nil,
- nil, nil, nil, 820, 737, 536, nil, 540, 828, 552,
- 554, 555, 788, 558, 559, 789, 562, 565, 283 ]
-
-racc_reduce_table = [
- 0, 0, :racc_error,
- 1, 146, :_reduce_none,
- 2, 147, :_reduce_2,
- 0, 148, :_reduce_3,
- 1, 148, :_reduce_4,
- 3, 148, :_reduce_5,
- 2, 148, :_reduce_6,
- 1, 150, :_reduce_none,
- 4, 150, :_reduce_8,
- 4, 153, :_reduce_9,
- 2, 154, :_reduce_10,
- 0, 158, :_reduce_11,
- 1, 158, :_reduce_12,
- 3, 158, :_reduce_13,
- 2, 158, :_reduce_14,
- 1, 159, :_reduce_none,
- 4, 159, :_reduce_16,
- 0, 175, :_reduce_17,
- 4, 152, :_reduce_18,
- 3, 152, :_reduce_19,
- 3, 152, :_reduce_20,
- 3, 152, :_reduce_21,
- 2, 152, :_reduce_22,
- 3, 152, :_reduce_23,
- 3, 152, :_reduce_24,
- 3, 152, :_reduce_25,
- 3, 152, :_reduce_26,
- 3, 152, :_reduce_27,
- 4, 152, :_reduce_28,
- 1, 152, :_reduce_none,
- 3, 152, :_reduce_30,
- 3, 152, :_reduce_31,
- 6, 152, :_reduce_32,
- 5, 152, :_reduce_33,
- 5, 152, :_reduce_34,
- 5, 152, :_reduce_35,
- 5, 152, :_reduce_36,
- 3, 152, :_reduce_37,
- 3, 152, :_reduce_38,
- 3, 152, :_reduce_39,
- 1, 152, :_reduce_none,
- 3, 163, :_reduce_41,
- 3, 163, :_reduce_42,
- 1, 174, :_reduce_none,
- 3, 174, :_reduce_44,
- 3, 174, :_reduce_45,
- 3, 174, :_reduce_46,
- 2, 174, :_reduce_47,
- 1, 174, :_reduce_none,
- 1, 162, :_reduce_none,
- 1, 165, :_reduce_none,
- 1, 165, :_reduce_none,
- 1, 179, :_reduce_none,
- 4, 179, :_reduce_53,
- 0, 187, :_reduce_54,
- 5, 184, :_reduce_55,
- 1, 186, :_reduce_none,
- 2, 178, :_reduce_57,
- 3, 178, :_reduce_58,
- 4, 178, :_reduce_59,
- 5, 178, :_reduce_60,
- 4, 178, :_reduce_61,
- 5, 178, :_reduce_62,
- 2, 178, :_reduce_63,
- 2, 178, :_reduce_64,
- 2, 178, :_reduce_65,
- 2, 178, :_reduce_66,
- 2, 178, :_reduce_67,
- 1, 164, :_reduce_68,
- 3, 164, :_reduce_69,
- 1, 191, :_reduce_70,
- 3, 191, :_reduce_71,
- 1, 190, :_reduce_none,
- 2, 190, :_reduce_73,
- 3, 190, :_reduce_74,
- 5, 190, :_reduce_75,
- 2, 190, :_reduce_76,
- 4, 190, :_reduce_77,
- 2, 190, :_reduce_78,
- 4, 190, :_reduce_79,
- 1, 190, :_reduce_80,
- 3, 190, :_reduce_81,
- 1, 194, :_reduce_none,
- 3, 194, :_reduce_83,
- 2, 193, :_reduce_84,
- 3, 193, :_reduce_85,
- 1, 196, :_reduce_86,
- 3, 196, :_reduce_87,
- 1, 195, :_reduce_88,
- 1, 195, :_reduce_89,
- 4, 195, :_reduce_90,
- 3, 195, :_reduce_91,
- 3, 195, :_reduce_92,
- 3, 195, :_reduce_93,
- 3, 195, :_reduce_94,
- 2, 195, :_reduce_95,
- 1, 195, :_reduce_96,
- 1, 171, :_reduce_97,
- 1, 171, :_reduce_98,
- 4, 171, :_reduce_99,
- 3, 171, :_reduce_100,
- 3, 171, :_reduce_101,
- 3, 171, :_reduce_102,
- 3, 171, :_reduce_103,
- 2, 171, :_reduce_104,
- 1, 171, :_reduce_105,
- 1, 199, :_reduce_106,
- 1, 199, :_reduce_none,
- 2, 200, :_reduce_108,
- 1, 200, :_reduce_109,
- 3, 200, :_reduce_110,
- 1, 201, :_reduce_none,
- 1, 201, :_reduce_none,
- 1, 201, :_reduce_none,
- 1, 201, :_reduce_none,
- 1, 201, :_reduce_none,
- 1, 204, :_reduce_116,
- 1, 204, :_reduce_none,
- 1, 160, :_reduce_none,
- 1, 160, :_reduce_none,
- 1, 161, :_reduce_120,
- 0, 207, :_reduce_121,
- 4, 161, :_reduce_122,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 202, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 1, 203, :_reduce_none,
- 3, 177, :_reduce_194,
- 5, 177, :_reduce_195,
- 3, 177, :_reduce_196,
- 5, 177, :_reduce_197,
- 6, 177, :_reduce_198,
- 5, 177, :_reduce_199,
- 5, 177, :_reduce_200,
- 5, 177, :_reduce_201,
- 5, 177, :_reduce_202,
- 4, 177, :_reduce_203,
- 3, 177, :_reduce_204,
- 3, 177, :_reduce_205,
- 3, 177, :_reduce_206,
- 3, 177, :_reduce_207,
- 3, 177, :_reduce_208,
- 3, 177, :_reduce_209,
- 3, 177, :_reduce_210,
- 3, 177, :_reduce_211,
- 3, 177, :_reduce_212,
- 4, 177, :_reduce_213,
- 2, 177, :_reduce_214,
- 2, 177, :_reduce_215,
- 3, 177, :_reduce_216,
- 3, 177, :_reduce_217,
- 3, 177, :_reduce_218,
- 3, 177, :_reduce_219,
- 3, 177, :_reduce_220,
- 3, 177, :_reduce_221,
- 3, 177, :_reduce_222,
- 3, 177, :_reduce_223,
- 3, 177, :_reduce_224,
- 3, 177, :_reduce_225,
- 3, 177, :_reduce_226,
- 3, 177, :_reduce_227,
- 3, 177, :_reduce_228,
- 2, 177, :_reduce_229,
- 2, 177, :_reduce_230,
- 3, 177, :_reduce_231,
- 3, 177, :_reduce_232,
- 3, 177, :_reduce_233,
- 3, 177, :_reduce_234,
- 3, 177, :_reduce_235,
- 0, 211, :_reduce_236,
- 0, 212, :_reduce_237,
- 8, 177, :_reduce_238,
- 1, 177, :_reduce_none,
- 1, 210, :_reduce_none,
- 1, 213, :_reduce_none,
- 2, 213, :_reduce_none,
- 4, 213, :_reduce_243,
- 2, 213, :_reduce_244,
- 3, 218, :_reduce_245,
- 0, 219, :_reduce_246,
- 1, 219, :_reduce_none,
- 0, 168, :_reduce_248,
- 1, 168, :_reduce_none,
- 2, 168, :_reduce_none,
- 4, 168, :_reduce_251,
- 2, 168, :_reduce_252,
- 1, 189, :_reduce_253,
- 2, 189, :_reduce_254,
- 2, 189, :_reduce_255,
- 4, 189, :_reduce_256,
- 1, 189, :_reduce_257,
- 0, 222, :_reduce_258,
- 2, 183, :_reduce_259,
- 2, 221, :_reduce_260,
- 2, 220, :_reduce_261,
- 0, 220, :_reduce_262,
- 1, 215, :_reduce_263,
- 2, 215, :_reduce_264,
- 3, 215, :_reduce_265,
- 4, 215, :_reduce_266,
- 1, 173, :_reduce_267,
- 1, 173, :_reduce_none,
- 3, 172, :_reduce_269,
- 4, 172, :_reduce_270,
- 2, 172, :_reduce_271,
- 1, 209, :_reduce_none,
- 1, 209, :_reduce_none,
- 1, 209, :_reduce_none,
- 1, 209, :_reduce_none,
- 1, 209, :_reduce_none,
- 1, 209, :_reduce_none,
- 1, 209, :_reduce_none,
- 1, 209, :_reduce_none,
- 1, 209, :_reduce_none,
- 1, 209, :_reduce_none,
- 1, 209, :_reduce_282,
- 0, 245, :_reduce_283,
- 4, 209, :_reduce_284,
- 0, 246, :_reduce_285,
- 0, 247, :_reduce_286,
- 6, 209, :_reduce_287,
- 0, 248, :_reduce_288,
- 4, 209, :_reduce_289,
- 3, 209, :_reduce_290,
- 3, 209, :_reduce_291,
- 2, 209, :_reduce_292,
- 3, 209, :_reduce_293,
- 3, 209, :_reduce_294,
- 1, 209, :_reduce_295,
- 4, 209, :_reduce_296,
- 3, 209, :_reduce_297,
- 1, 209, :_reduce_298,
- 5, 209, :_reduce_299,
- 4, 209, :_reduce_300,
- 3, 209, :_reduce_301,
- 2, 209, :_reduce_302,
- 1, 209, :_reduce_none,
- 2, 209, :_reduce_304,
- 2, 209, :_reduce_305,
- 6, 209, :_reduce_306,
- 6, 209, :_reduce_307,
- 0, 249, :_reduce_308,
- 0, 250, :_reduce_309,
- 7, 209, :_reduce_310,
- 0, 251, :_reduce_311,
- 0, 252, :_reduce_312,
- 7, 209, :_reduce_313,
- 5, 209, :_reduce_314,
- 4, 209, :_reduce_315,
- 0, 253, :_reduce_316,
- 0, 254, :_reduce_317,
- 9, 209, :_reduce_318,
- 0, 255, :_reduce_319,
- 6, 209, :_reduce_320,
- 0, 256, :_reduce_321,
- 7, 209, :_reduce_322,
- 0, 257, :_reduce_323,
- 5, 209, :_reduce_324,
- 0, 258, :_reduce_325,
- 6, 209, :_reduce_326,
- 0, 259, :_reduce_327,
- 0, 260, :_reduce_328,
- 9, 209, :_reduce_329,
- 1, 209, :_reduce_330,
- 1, 209, :_reduce_331,
- 1, 209, :_reduce_332,
- 1, 209, :_reduce_333,
- 1, 167, :_reduce_none,
- 1, 236, :_reduce_none,
- 1, 236, :_reduce_none,
- 2, 236, :_reduce_337,
- 1, 238, :_reduce_none,
- 1, 238, :_reduce_none,
- 1, 237, :_reduce_none,
- 5, 237, :_reduce_341,
- 1, 156, :_reduce_none,
- 2, 156, :_reduce_343,
- 1, 240, :_reduce_none,
- 1, 240, :_reduce_none,
- 1, 261, :_reduce_346,
- 3, 261, :_reduce_347,
- 1, 264, :_reduce_348,
- 3, 264, :_reduce_349,
- 1, 263, :_reduce_none,
- 4, 263, :_reduce_351,
- 6, 263, :_reduce_352,
- 3, 263, :_reduce_353,
- 5, 263, :_reduce_354,
- 2, 263, :_reduce_355,
- 4, 263, :_reduce_356,
- 1, 263, :_reduce_357,
- 3, 263, :_reduce_358,
- 4, 265, :_reduce_359,
- 2, 265, :_reduce_360,
- 2, 265, :_reduce_361,
- 1, 265, :_reduce_362,
- 2, 270, :_reduce_363,
- 0, 270, :_reduce_364,
- 6, 271, :_reduce_365,
- 8, 271, :_reduce_366,
- 4, 271, :_reduce_367,
- 6, 271, :_reduce_368,
- 4, 271, :_reduce_369,
- 2, 271, :_reduce_none,
- 6, 271, :_reduce_371,
- 2, 271, :_reduce_372,
- 4, 271, :_reduce_373,
- 6, 271, :_reduce_374,
- 2, 271, :_reduce_375,
- 4, 271, :_reduce_376,
- 2, 271, :_reduce_377,
- 4, 271, :_reduce_378,
- 1, 271, :_reduce_none,
- 0, 185, :_reduce_380,
- 1, 185, :_reduce_381,
- 3, 275, :_reduce_382,
- 1, 275, :_reduce_383,
- 4, 275, :_reduce_384,
- 1, 276, :_reduce_385,
- 4, 276, :_reduce_386,
- 1, 277, :_reduce_387,
- 3, 277, :_reduce_388,
- 1, 278, :_reduce_389,
- 1, 278, :_reduce_none,
- 0, 282, :_reduce_391,
- 0, 283, :_reduce_392,
- 4, 235, :_reduce_393,
- 4, 280, :_reduce_394,
- 1, 280, :_reduce_395,
- 3, 281, :_reduce_396,
- 3, 281, :_reduce_397,
- 0, 286, :_reduce_398,
- 5, 285, :_reduce_399,
- 2, 180, :_reduce_400,
- 4, 180, :_reduce_401,
- 5, 180, :_reduce_402,
- 5, 180, :_reduce_403,
- 2, 234, :_reduce_404,
- 4, 234, :_reduce_405,
- 4, 234, :_reduce_406,
- 3, 234, :_reduce_407,
- 3, 234, :_reduce_408,
- 3, 234, :_reduce_409,
- 2, 234, :_reduce_410,
- 1, 234, :_reduce_411,
- 4, 234, :_reduce_412,
- 0, 288, :_reduce_413,
- 5, 233, :_reduce_414,
- 0, 289, :_reduce_415,
- 5, 233, :_reduce_416,
- 5, 239, :_reduce_417,
- 1, 290, :_reduce_418,
- 1, 290, :_reduce_none,
- 6, 155, :_reduce_420,
- 0, 155, :_reduce_421,
- 1, 291, :_reduce_422,
- 1, 291, :_reduce_none,
- 1, 291, :_reduce_none,
- 2, 292, :_reduce_425,
- 1, 292, :_reduce_none,
- 2, 157, :_reduce_427,
- 1, 157, :_reduce_none,
- 1, 223, :_reduce_none,
- 1, 223, :_reduce_none,
- 1, 223, :_reduce_none,
- 1, 224, :_reduce_432,
- 1, 294, :_reduce_433,
- 2, 294, :_reduce_434,
- 3, 295, :_reduce_435,
- 1, 295, :_reduce_436,
- 1, 295, :_reduce_437,
- 3, 225, :_reduce_438,
- 4, 226, :_reduce_439,
- 3, 227, :_reduce_440,
- 0, 299, :_reduce_441,
- 3, 299, :_reduce_442,
- 1, 300, :_reduce_443,
- 2, 300, :_reduce_444,
- 3, 229, :_reduce_445,
- 0, 302, :_reduce_446,
- 3, 302, :_reduce_447,
- 3, 228, :_reduce_448,
- 3, 230, :_reduce_449,
- 0, 303, :_reduce_450,
- 3, 303, :_reduce_451,
- 0, 304, :_reduce_452,
- 3, 304, :_reduce_453,
- 0, 296, :_reduce_454,
- 2, 296, :_reduce_455,
- 0, 297, :_reduce_456,
- 2, 297, :_reduce_457,
- 0, 298, :_reduce_458,
- 2, 298, :_reduce_459,
- 1, 301, :_reduce_460,
- 2, 301, :_reduce_461,
- 0, 306, :_reduce_462,
- 4, 301, :_reduce_463,
- 1, 305, :_reduce_464,
- 1, 305, :_reduce_465,
- 1, 305, :_reduce_466,
- 1, 305, :_reduce_none,
- 1, 205, :_reduce_468,
- 3, 206, :_reduce_469,
- 1, 293, :_reduce_470,
- 2, 293, :_reduce_471,
- 1, 208, :_reduce_472,
- 1, 208, :_reduce_473,
- 1, 208, :_reduce_474,
- 1, 208, :_reduce_475,
- 1, 197, :_reduce_476,
- 1, 197, :_reduce_477,
- 1, 197, :_reduce_478,
- 1, 197, :_reduce_479,
- 1, 197, :_reduce_480,
- 1, 198, :_reduce_481,
- 1, 198, :_reduce_482,
- 1, 198, :_reduce_483,
- 1, 198, :_reduce_484,
- 1, 198, :_reduce_485,
- 1, 198, :_reduce_486,
- 1, 198, :_reduce_487,
- 1, 231, :_reduce_488,
- 1, 231, :_reduce_489,
- 1, 166, :_reduce_490,
- 1, 166, :_reduce_491,
- 1, 170, :_reduce_492,
- 1, 170, :_reduce_493,
- 1, 241, :_reduce_494,
- 0, 307, :_reduce_495,
- 4, 241, :_reduce_496,
- 2, 241, :_reduce_497,
- 3, 243, :_reduce_498,
- 0, 309, :_reduce_499,
- 3, 243, :_reduce_500,
- 4, 308, :_reduce_501,
- 2, 308, :_reduce_502,
- 2, 308, :_reduce_503,
- 1, 308, :_reduce_504,
- 2, 311, :_reduce_505,
- 0, 311, :_reduce_506,
- 6, 284, :_reduce_507,
- 8, 284, :_reduce_508,
- 4, 284, :_reduce_509,
- 6, 284, :_reduce_510,
- 4, 284, :_reduce_511,
- 6, 284, :_reduce_512,
- 2, 284, :_reduce_513,
- 4, 284, :_reduce_514,
- 6, 284, :_reduce_515,
- 2, 284, :_reduce_516,
- 4, 284, :_reduce_517,
- 2, 284, :_reduce_518,
- 4, 284, :_reduce_519,
- 1, 284, :_reduce_520,
- 0, 284, :_reduce_521,
- 1, 279, :_reduce_522,
- 1, 279, :_reduce_523,
- 1, 279, :_reduce_524,
- 1, 279, :_reduce_525,
- 1, 262, :_reduce_none,
- 1, 262, :_reduce_527,
- 1, 313, :_reduce_528,
- 1, 314, :_reduce_529,
- 3, 314, :_reduce_530,
- 1, 272, :_reduce_531,
- 3, 272, :_reduce_532,
- 1, 315, :_reduce_533,
- 2, 316, :_reduce_534,
- 1, 316, :_reduce_535,
- 2, 317, :_reduce_536,
- 1, 317, :_reduce_537,
- 1, 266, :_reduce_538,
- 3, 266, :_reduce_539,
- 1, 310, :_reduce_540,
- 3, 310, :_reduce_541,
- 1, 318, :_reduce_none,
- 1, 318, :_reduce_none,
- 2, 267, :_reduce_544,
- 1, 267, :_reduce_545,
- 3, 319, :_reduce_546,
- 3, 320, :_reduce_547,
- 1, 273, :_reduce_548,
- 3, 273, :_reduce_549,
- 1, 312, :_reduce_550,
- 3, 312, :_reduce_551,
- 1, 321, :_reduce_none,
- 1, 321, :_reduce_none,
- 2, 274, :_reduce_554,
- 1, 274, :_reduce_555,
- 1, 322, :_reduce_none,
- 1, 322, :_reduce_none,
- 2, 269, :_reduce_558,
- 2, 268, :_reduce_559,
- 0, 268, :_reduce_560,
- 1, 244, :_reduce_none,
- 3, 244, :_reduce_562,
- 0, 232, :_reduce_563,
- 2, 232, :_reduce_none,
- 1, 217, :_reduce_565,
- 3, 217, :_reduce_566,
- 3, 323, :_reduce_567,
- 2, 323, :_reduce_568,
- 4, 323, :_reduce_569,
- 2, 323, :_reduce_570,
- 1, 188, :_reduce_none,
- 1, 188, :_reduce_none,
- 1, 188, :_reduce_none,
- 1, 182, :_reduce_none,
- 1, 182, :_reduce_none,
- 1, 182, :_reduce_none,
- 1, 182, :_reduce_none,
- 1, 287, :_reduce_none,
- 1, 287, :_reduce_none,
- 1, 287, :_reduce_none,
- 1, 181, :_reduce_none,
- 1, 181, :_reduce_none,
- 0, 149, :_reduce_none,
- 1, 149, :_reduce_none,
- 0, 176, :_reduce_none,
- 1, 176, :_reduce_none,
- 2, 192, :_reduce_587,
- 2, 169, :_reduce_588,
- 0, 216, :_reduce_none,
- 1, 216, :_reduce_none,
- 1, 216, :_reduce_none,
- 1, 242, :_reduce_592,
- 1, 242, :_reduce_none,
- 1, 151, :_reduce_none,
- 2, 151, :_reduce_none,
- 0, 214, :_reduce_596 ]
-
-racc_reduce_n = 597
-
-racc_shift_n = 1024
-
-racc_token_table = {
- false => 0,
- :error => 1,
- :kCLASS => 2,
- :kMODULE => 3,
- :kDEF => 4,
- :kUNDEF => 5,
- :kBEGIN => 6,
- :kRESCUE => 7,
- :kENSURE => 8,
- :kEND => 9,
- :kIF => 10,
- :kUNLESS => 11,
- :kTHEN => 12,
- :kELSIF => 13,
- :kELSE => 14,
- :kCASE => 15,
- :kWHEN => 16,
- :kWHILE => 17,
- :kUNTIL => 18,
- :kFOR => 19,
- :kBREAK => 20,
- :kNEXT => 21,
- :kREDO => 22,
- :kRETRY => 23,
- :kIN => 24,
- :kDO => 25,
- :kDO_COND => 26,
- :kDO_BLOCK => 27,
- :kDO_LAMBDA => 28,
- :kRETURN => 29,
- :kYIELD => 30,
- :kSUPER => 31,
- :kSELF => 32,
- :kNIL => 33,
- :kTRUE => 34,
- :kFALSE => 35,
- :kAND => 36,
- :kOR => 37,
- :kNOT => 38,
- :kIF_MOD => 39,
- :kUNLESS_MOD => 40,
- :kWHILE_MOD => 41,
- :kUNTIL_MOD => 42,
- :kRESCUE_MOD => 43,
- :kALIAS => 44,
- :kDEFINED => 45,
- :klBEGIN => 46,
- :klEND => 47,
- :k__LINE__ => 48,
- :k__FILE__ => 49,
- :k__ENCODING__ => 50,
- :tIDENTIFIER => 51,
- :tFID => 52,
- :tGVAR => 53,
- :tIVAR => 54,
- :tCONSTANT => 55,
- :tLABEL => 56,
- :tCVAR => 57,
- :tNTH_REF => 58,
- :tBACK_REF => 59,
- :tSTRING_CONTENT => 60,
- :tINTEGER => 61,
- :tFLOAT => 62,
- :tREGEXP_END => 63,
- :tUPLUS => 64,
- :tUMINUS => 65,
- :tUMINUS_NUM => 66,
- :tPOW => 67,
- :tCMP => 68,
- :tEQ => 69,
- :tEQQ => 70,
- :tNEQ => 71,
- :tGEQ => 72,
- :tLEQ => 73,
- :tANDOP => 74,
- :tOROP => 75,
- :tMATCH => 76,
- :tNMATCH => 77,
- :tDOT => 78,
- :tDOT2 => 79,
- :tDOT3 => 80,
- :tAREF => 81,
- :tASET => 82,
- :tLSHFT => 83,
- :tRSHFT => 84,
- :tCOLON2 => 85,
- :tCOLON3 => 86,
- :tOP_ASGN => 87,
- :tASSOC => 88,
- :tLPAREN => 89,
- :tLPAREN2 => 90,
- :tRPAREN => 91,
- :tLPAREN_ARG => 92,
- :tLBRACK => 93,
- :tLBRACK2 => 94,
- :tRBRACK => 95,
- :tLBRACE => 96,
- :tLBRACE_ARG => 97,
- :tSTAR => 98,
- :tSTAR2 => 99,
- :tAMPER => 100,
- :tAMPER2 => 101,
- :tTILDE => 102,
- :tPERCENT => 103,
- :tDIVIDE => 104,
- :tDSTAR => 105,
- :tPLUS => 106,
- :tMINUS => 107,
- :tLT => 108,
- :tGT => 109,
- :tPIPE => 110,
- :tBANG => 111,
- :tCARET => 112,
- :tLCURLY => 113,
- :tRCURLY => 114,
- :tBACK_REF2 => 115,
- :tSYMBEG => 116,
- :tSTRING_BEG => 117,
- :tXSTRING_BEG => 118,
- :tREGEXP_BEG => 119,
- :tREGEXP_OPT => 120,
- :tWORDS_BEG => 121,
- :tQWORDS_BEG => 122,
- :tSYMBOLS_BEG => 123,
- :tQSYMBOLS_BEG => 124,
- :tSTRING_DBEG => 125,
- :tSTRING_DVAR => 126,
- :tSTRING_END => 127,
- :tSTRING_DEND => 128,
- :tSTRING => 129,
- :tSYMBOL => 130,
- :tNL => 131,
- :tEH => 132,
- :tCOLON => 133,
- :tCOMMA => 134,
- :tSPACE => 135,
- :tSEMI => 136,
- :tLAMBDA => 137,
- :tLAMBEG => 138,
- :tCHARACTER => 139,
- :tRATIONAL => 140,
- :tIMAGINARY => 141,
- :tLABEL_END => 142,
- :tEQL => 143,
- :tLOWEST => 144 }
-
-racc_nt_base = 145
-
-racc_use_result_var = true
-
-Racc_arg = [
- racc_action_table,
- racc_action_check,
- racc_action_default,
- racc_action_pointer,
- racc_goto_table,
- racc_goto_check,
- racc_goto_default,
- racc_goto_pointer,
- racc_nt_base,
- racc_reduce_table,
- racc_token_table,
- racc_shift_n,
- racc_reduce_n,
- racc_use_result_var ]
-Ractor.make_shareable(Racc_arg) if defined?(Ractor)
-
-Racc_token_to_s_table = [
- "$end",
- "error",
- "kCLASS",
- "kMODULE",
- "kDEF",
- "kUNDEF",
- "kBEGIN",
- "kRESCUE",
- "kENSURE",
- "kEND",
- "kIF",
- "kUNLESS",
- "kTHEN",
- "kELSIF",
- "kELSE",
- "kCASE",
- "kWHEN",
- "kWHILE",
- "kUNTIL",
- "kFOR",
- "kBREAK",
- "kNEXT",
- "kREDO",
- "kRETRY",
- "kIN",
- "kDO",
- "kDO_COND",
- "kDO_BLOCK",
- "kDO_LAMBDA",
- "kRETURN",
- "kYIELD",
- "kSUPER",
- "kSELF",
- "kNIL",
- "kTRUE",
- "kFALSE",
- "kAND",
- "kOR",
- "kNOT",
- "kIF_MOD",
- "kUNLESS_MOD",
- "kWHILE_MOD",
- "kUNTIL_MOD",
- "kRESCUE_MOD",
- "kALIAS",
- "kDEFINED",
- "klBEGIN",
- "klEND",
- "k__LINE__",
- "k__FILE__",
- "k__ENCODING__",
- "tIDENTIFIER",
- "tFID",
- "tGVAR",
- "tIVAR",
- "tCONSTANT",
- "tLABEL",
- "tCVAR",
- "tNTH_REF",
- "tBACK_REF",
- "tSTRING_CONTENT",
- "tINTEGER",
- "tFLOAT",
- "tREGEXP_END",
- "tUPLUS",
- "tUMINUS",
- "tUMINUS_NUM",
- "tPOW",
- "tCMP",
- "tEQ",
- "tEQQ",
- "tNEQ",
- "tGEQ",
- "tLEQ",
- "tANDOP",
- "tOROP",
- "tMATCH",
- "tNMATCH",
- "tDOT",
- "tDOT2",
- "tDOT3",
- "tAREF",
- "tASET",
- "tLSHFT",
- "tRSHFT",
- "tCOLON2",
- "tCOLON3",
- "tOP_ASGN",
- "tASSOC",
- "tLPAREN",
- "tLPAREN2",
- "tRPAREN",
- "tLPAREN_ARG",
- "tLBRACK",
- "tLBRACK2",
- "tRBRACK",
- "tLBRACE",
- "tLBRACE_ARG",
- "tSTAR",
- "tSTAR2",
- "tAMPER",
- "tAMPER2",
- "tTILDE",
- "tPERCENT",
- "tDIVIDE",
- "tDSTAR",
- "tPLUS",
- "tMINUS",
- "tLT",
- "tGT",
- "tPIPE",
- "tBANG",
- "tCARET",
- "tLCURLY",
- "tRCURLY",
- "tBACK_REF2",
- "tSYMBEG",
- "tSTRING_BEG",
- "tXSTRING_BEG",
- "tREGEXP_BEG",
- "tREGEXP_OPT",
- "tWORDS_BEG",
- "tQWORDS_BEG",
- "tSYMBOLS_BEG",
- "tQSYMBOLS_BEG",
- "tSTRING_DBEG",
- "tSTRING_DVAR",
- "tSTRING_END",
- "tSTRING_DEND",
- "tSTRING",
- "tSYMBOL",
- "tNL",
- "tEH",
- "tCOLON",
- "tCOMMA",
- "tSPACE",
- "tSEMI",
- "tLAMBDA",
- "tLAMBEG",
- "tCHARACTER",
- "tRATIONAL",
- "tIMAGINARY",
- "tLABEL_END",
- "tEQL",
- "tLOWEST",
- "$start",
- "program",
- "top_compstmt",
- "top_stmts",
- "opt_terms",
- "top_stmt",
- "terms",
- "stmt",
- "bodystmt",
- "compstmt",
- "opt_rescue",
- "opt_else",
- "opt_ensure",
- "stmts",
- "stmt_or_begin",
- "fitem",
- "undef_list",
- "expr_value",
- "command_asgn",
- "mlhs",
- "command_call",
- "var_lhs",
- "primary_value",
- "opt_call_args",
- "rbracket",
- "backref",
- "lhs",
- "mrhs",
- "mrhs_arg",
- "expr",
- "@1",
- "opt_nl",
- "arg",
- "command",
- "block_command",
- "block_call",
- "dot_or_colon",
- "operation2",
- "command_args",
- "cmd_brace_block",
- "opt_block_param",
- "fcall",
- "@2",
- "operation",
- "call_args",
- "mlhs_basic",
- "mlhs_inner",
- "rparen",
- "mlhs_head",
- "mlhs_item",
- "mlhs_node",
- "mlhs_post",
- "user_variable",
- "keyword_variable",
- "cname",
- "cpath",
- "fname",
- "op",
- "reswords",
- "fsym",
- "symbol",
- "dsym",
- "@3",
- "simple_numeric",
- "primary",
- "arg_value",
- "@4",
- "@5",
- "aref_args",
- "none",
- "args",
- "trailer",
- "assocs",
- "paren_args",
- "opt_paren_args",
- "opt_block_arg",
- "block_arg",
- "@6",
- "literal",
- "strings",
- "xstring",
- "regexp",
- "words",
- "qwords",
- "symbols",
- "qsymbols",
- "var_ref",
- "assoc_list",
- "brace_block",
- "method_call",
- "lambda",
- "then",
- "if_tail",
- "do",
- "case_body",
- "for_var",
- "superclass",
- "term",
- "f_arglist",
- "singleton",
- "@7",
- "@8",
- "@9",
- "@10",
- "@11",
- "@12",
- "@13",
- "@14",
- "@15",
- "@16",
- "@17",
- "@18",
- "@19",
- "@20",
- "@21",
- "@22",
- "f_marg",
- "f_norm_arg",
- "f_margs",
- "f_marg_list",
- "block_args_tail",
- "f_block_kwarg",
- "f_kwrest",
- "opt_f_block_arg",
- "f_block_arg",
- "opt_block_args_tail",
- "block_param",
- "f_arg",
- "f_block_optarg",
- "f_rest_arg",
- "block_param_def",
- "opt_bv_decl",
- "bv_decls",
- "bvar",
- "f_bad_arg",
- "f_larglist",
- "lambda_body",
- "@23",
- "@24",
- "f_args",
- "do_block",
- "@25",
- "operation3",
- "@26",
- "@27",
- "cases",
- "exc_list",
- "exc_var",
- "numeric",
- "string",
- "string1",
- "string_contents",
- "xstring_contents",
- "regexp_contents",
- "word_list",
- "word",
- "string_content",
- "symbol_list",
- "qword_list",
- "qsym_list",
- "string_dvar",
- "@28",
- "@29",
- "args_tail",
- "@30",
- "f_kwarg",
- "opt_args_tail",
- "f_optarg",
- "f_arg_asgn",
- "f_arg_item",
- "f_label",
- "f_kw",
- "f_block_kw",
- "kwrest_mark",
- "f_opt",
- "f_block_opt",
- "restarg_mark",
- "blkarg_mark",
- "assoc" ]
-Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)
-
-Racc_debug_parser = false
-
-##### State transition tables end #####
-
-# reduce 0 omitted
-
-# reduce 1 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 78)
- def _reduce_2(val, _values, result)
- result = @builder.compstmt(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 83)
- def _reduce_3(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 87)
- def _reduce_4(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 91)
- def _reduce_5(val, _values, result)
- result = val[0] << val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 95)
- def _reduce_6(val, _values, result)
- result = [ val[1] ]
-
- result
- end
-.,.,
-
-# reduce 7 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 101)
- def _reduce_8(val, _values, result)
- result = @builder.preexe(val[0], val[1], val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 106)
- def _reduce_9(val, _values, result)
- rescue_bodies = val[1]
- else_t, else_ = val[2]
- ensure_t, ensure_ = val[3]
-
- if rescue_bodies.empty? && !else_.nil?
- diagnostic :warning, :useless_else, nil, else_t
- end
-
- result = @builder.begin_body(val[0],
- rescue_bodies,
- else_t, else_,
- ensure_t, ensure_)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 122)
- def _reduce_10(val, _values, result)
- result = @builder.compstmt(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 127)
- def _reduce_11(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 131)
- def _reduce_12(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 135)
- def _reduce_13(val, _values, result)
- result = val[0] << val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 139)
- def _reduce_14(val, _values, result)
- result = [ val[1] ]
-
- result
- end
-.,.,
-
-# reduce 15 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 145)
- def _reduce_16(val, _values, result)
- diagnostic :error, :begin_in_method, nil, val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 150)
- def _reduce_17(val, _values, result)
- @lexer.state = :expr_fname
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 154)
- def _reduce_18(val, _values, result)
- result = @builder.alias(val[0], val[1], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 158)
- def _reduce_19(val, _values, result)
- result = @builder.alias(val[0],
- @builder.gvar(val[1]),
- @builder.gvar(val[2]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 164)
- def _reduce_20(val, _values, result)
- result = @builder.alias(val[0],
- @builder.gvar(val[1]),
- @builder.back_ref(val[2]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 170)
- def _reduce_21(val, _values, result)
- diagnostic :error, :nth_ref_alias, nil, val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 174)
- def _reduce_22(val, _values, result)
- result = @builder.undef_method(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 178)
- def _reduce_23(val, _values, result)
- result = @builder.condition_mod(val[0], nil,
- val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 183)
- def _reduce_24(val, _values, result)
- result = @builder.condition_mod(nil, val[0],
- val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 188)
- def _reduce_25(val, _values, result)
- result = @builder.loop_mod(:while, val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 192)
- def _reduce_26(val, _values, result)
- result = @builder.loop_mod(:until, val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 196)
- def _reduce_27(val, _values, result)
- rescue_body = @builder.rescue_body(val[1],
- nil, nil, nil,
- nil, val[2])
-
- result = @builder.begin_body(val[0], [ rescue_body ])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 204)
- def _reduce_28(val, _values, result)
- result = @builder.postexe(val[0], val[1], val[2], val[3])
-
- result
- end
-.,.,
-
-# reduce 29 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 209)
- def _reduce_30(val, _values, result)
- result = @builder.multi_assign(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 213)
- def _reduce_31(val, _values, result)
- result = @builder.op_assign(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 217)
- def _reduce_32(val, _values, result)
- result = @builder.op_assign(
- @builder.index(
- val[0], val[1], val[2], val[3]),
- val[4], val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 224)
- def _reduce_33(val, _values, result)
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 231)
- def _reduce_34(val, _values, result)
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 238)
- def _reduce_35(val, _values, result)
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 245)
- def _reduce_36(val, _values, result)
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 252)
- def _reduce_37(val, _values, result)
- @builder.op_assign(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 256)
- def _reduce_38(val, _values, result)
- result = @builder.assign(val[0], val[1],
- @builder.array(nil, val[2], nil))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 261)
- def _reduce_39(val, _values, result)
- result = @builder.multi_assign(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-# reduce 40 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 267)
- def _reduce_41(val, _values, result)
- result = @builder.assign(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 271)
- def _reduce_42(val, _values, result)
- result = @builder.assign(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-# reduce 43 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 277)
- def _reduce_44(val, _values, result)
- result = @builder.logical_op(:and, val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 281)
- def _reduce_45(val, _values, result)
- result = @builder.logical_op(:or, val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 285)
- def _reduce_46(val, _values, result)
- result = @builder.not_op(val[0], nil, val[2], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 289)
- def _reduce_47(val, _values, result)
- result = @builder.not_op(val[0], nil, val[1], nil)
-
- result
- end
-.,.,
-
-# reduce 48 omitted
-
-# reduce 49 omitted
-
-# reduce 50 omitted
-
-# reduce 51 omitted
-
-# reduce 52 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 301)
- def _reduce_53(val, _values, result)
- result = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 307)
- def _reduce_54(val, _values, result)
- @static_env.extend_dynamic
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 311)
- def _reduce_55(val, _values, result)
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
-
- result
- end
-.,.,
-
-# reduce 56 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 320)
- def _reduce_57(val, _values, result)
- result = @builder.call_method(nil, nil, val[0],
- nil, val[1], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 325)
- def _reduce_58(val, _values, result)
- method_call = @builder.call_method(nil, nil, val[0],
- nil, val[1], nil)
-
- begin_t, args, body, end_t = val[2]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 334)
- def _reduce_59(val, _values, result)
- result = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 339)
- def _reduce_60(val, _values, result)
- method_call = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
-
- begin_t, args, body, end_t = val[4]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 348)
- def _reduce_61(val, _values, result)
- result = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 353)
- def _reduce_62(val, _values, result)
- method_call = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
-
- begin_t, args, body, end_t = val[4]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 362)
- def _reduce_63(val, _values, result)
- result = @builder.keyword_cmd(:super, val[0],
- nil, val[1], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 367)
- def _reduce_64(val, _values, result)
- result = @builder.keyword_cmd(:yield, val[0],
- nil, val[1], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 372)
- def _reduce_65(val, _values, result)
- result = @builder.keyword_cmd(:return, val[0],
- nil, val[1], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 377)
- def _reduce_66(val, _values, result)
- result = @builder.keyword_cmd(:break, val[0],
- nil, val[1], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 382)
- def _reduce_67(val, _values, result)
- result = @builder.keyword_cmd(:next, val[0],
- nil, val[1], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 388)
- def _reduce_68(val, _values, result)
- result = @builder.multi_lhs(nil, val[0], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 392)
- def _reduce_69(val, _values, result)
- result = @builder.begin(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 397)
- def _reduce_70(val, _values, result)
- result = @builder.multi_lhs(nil, val[0], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 401)
- def _reduce_71(val, _values, result)
- result = @builder.multi_lhs(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-# reduce 72 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 407)
- def _reduce_73(val, _values, result)
- result = val[0].
- push(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 412)
- def _reduce_74(val, _values, result)
- result = val[0].
- push(@builder.splat(val[1], val[2]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 417)
- def _reduce_75(val, _values, result)
- result = val[0].
- push(@builder.splat(val[1], val[2])).
- concat(val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 423)
- def _reduce_76(val, _values, result)
- result = val[0].
- push(@builder.splat(val[1]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 428)
- def _reduce_77(val, _values, result)
- result = val[0].
- push(@builder.splat(val[1])).
- concat(val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 434)
- def _reduce_78(val, _values, result)
- result = [ @builder.splat(val[0], val[1]) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 438)
- def _reduce_79(val, _values, result)
- result = [ @builder.splat(val[0], val[1]),
- *val[3] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 443)
- def _reduce_80(val, _values, result)
- result = [ @builder.splat(val[0]) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 447)
- def _reduce_81(val, _values, result)
- result = [ @builder.splat(val[0]),
- *val[2] ]
-
- result
- end
-.,.,
-
-# reduce 82 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 454)
- def _reduce_83(val, _values, result)
- result = @builder.begin(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 459)
- def _reduce_84(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 463)
- def _reduce_85(val, _values, result)
- result = val[0] << val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 468)
- def _reduce_86(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 472)
- def _reduce_87(val, _values, result)
- result = val[0] << val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 477)
- def _reduce_88(val, _values, result)
- result = @builder.assignable(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 481)
- def _reduce_89(val, _values, result)
- result = @builder.assignable(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 485)
- def _reduce_90(val, _values, result)
- result = @builder.index_asgn(val[0], val[1], val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 489)
- def _reduce_91(val, _values, result)
- result = @builder.attr_asgn(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 493)
- def _reduce_92(val, _values, result)
- result = @builder.attr_asgn(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 497)
- def _reduce_93(val, _values, result)
- result = @builder.attr_asgn(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 501)
- def _reduce_94(val, _values, result)
- result = @builder.assignable(
- @builder.const_fetch(val[0], val[1], val[2]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 506)
- def _reduce_95(val, _values, result)
- result = @builder.assignable(
- @builder.const_global(val[0], val[1]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 511)
- def _reduce_96(val, _values, result)
- result = @builder.assignable(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 516)
- def _reduce_97(val, _values, result)
- result = @builder.assignable(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 520)
- def _reduce_98(val, _values, result)
- result = @builder.assignable(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 524)
- def _reduce_99(val, _values, result)
- result = @builder.index_asgn(val[0], val[1], val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 528)
- def _reduce_100(val, _values, result)
- result = @builder.attr_asgn(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 532)
- def _reduce_101(val, _values, result)
- result = @builder.attr_asgn(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 536)
- def _reduce_102(val, _values, result)
- result = @builder.attr_asgn(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 540)
- def _reduce_103(val, _values, result)
- result = @builder.assignable(
- @builder.const_fetch(val[0], val[1], val[2]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 545)
- def _reduce_104(val, _values, result)
- result = @builder.assignable(
- @builder.const_global(val[0], val[1]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 550)
- def _reduce_105(val, _values, result)
- result = @builder.assignable(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 555)
- def _reduce_106(val, _values, result)
- diagnostic :error, :module_name_const, nil, val[0]
-
- result
- end
-.,.,
-
-# reduce 107 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 561)
- def _reduce_108(val, _values, result)
- result = @builder.const_global(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 565)
- def _reduce_109(val, _values, result)
- result = @builder.const(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 569)
- def _reduce_110(val, _values, result)
- result = @builder.const_fetch(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-# reduce 111 omitted
-
-# reduce 112 omitted
-
-# reduce 113 omitted
-
-# reduce 114 omitted
-
-# reduce 115 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 578)
- def _reduce_116(val, _values, result)
- result = @builder.symbol(val[0])
-
- result
- end
-.,.,
-
-# reduce 117 omitted
-
-# reduce 118 omitted
-
-# reduce 119 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 587)
- def _reduce_120(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 591)
- def _reduce_121(val, _values, result)
- @lexer.state = :expr_fname
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 595)
- def _reduce_122(val, _values, result)
- result = val[0] << val[3]
-
- result
- end
-.,.,
-
-# reduce 123 omitted
-
-# reduce 124 omitted
-
-# reduce 125 omitted
-
-# reduce 126 omitted
-
-# reduce 127 omitted
-
-# reduce 128 omitted
-
-# reduce 129 omitted
-
-# reduce 130 omitted
-
-# reduce 131 omitted
-
-# reduce 132 omitted
-
-# reduce 133 omitted
-
-# reduce 134 omitted
-
-# reduce 135 omitted
-
-# reduce 136 omitted
-
-# reduce 137 omitted
-
-# reduce 138 omitted
-
-# reduce 139 omitted
-
-# reduce 140 omitted
-
-# reduce 141 omitted
-
-# reduce 142 omitted
-
-# reduce 143 omitted
-
-# reduce 144 omitted
-
-# reduce 145 omitted
-
-# reduce 146 omitted
-
-# reduce 147 omitted
-
-# reduce 148 omitted
-
-# reduce 149 omitted
-
-# reduce 150 omitted
-
-# reduce 151 omitted
-
-# reduce 152 omitted
-
-# reduce 153 omitted
-
-# reduce 154 omitted
-
-# reduce 155 omitted
-
-# reduce 156 omitted
-
-# reduce 157 omitted
-
-# reduce 158 omitted
-
-# reduce 159 omitted
-
-# reduce 160 omitted
-
-# reduce 161 omitted
-
-# reduce 162 omitted
-
-# reduce 163 omitted
-
-# reduce 164 omitted
-
-# reduce 165 omitted
-
-# reduce 166 omitted
-
-# reduce 167 omitted
-
-# reduce 168 omitted
-
-# reduce 169 omitted
-
-# reduce 170 omitted
-
-# reduce 171 omitted
-
-# reduce 172 omitted
-
-# reduce 173 omitted
-
-# reduce 174 omitted
-
-# reduce 175 omitted
-
-# reduce 176 omitted
-
-# reduce 177 omitted
-
-# reduce 178 omitted
-
-# reduce 179 omitted
-
-# reduce 180 omitted
-
-# reduce 181 omitted
-
-# reduce 182 omitted
-
-# reduce 183 omitted
-
-# reduce 184 omitted
-
-# reduce 185 omitted
-
-# reduce 186 omitted
-
-# reduce 187 omitted
-
-# reduce 188 omitted
-
-# reduce 189 omitted
-
-# reduce 190 omitted
-
-# reduce 191 omitted
-
-# reduce 192 omitted
-
-# reduce 193 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 616)
- def _reduce_194(val, _values, result)
- result = @builder.assign(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 620)
- def _reduce_195(val, _values, result)
- rescue_body = @builder.rescue_body(val[3],
- nil, nil, nil,
- nil, val[4])
-
- rescue_ = @builder.begin_body(val[2], [ rescue_body ])
-
- result = @builder.assign(val[0], val[1], rescue_)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 630)
- def _reduce_196(val, _values, result)
- result = @builder.op_assign(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 634)
- def _reduce_197(val, _values, result)
- rescue_body = @builder.rescue_body(val[3],
- nil, nil, nil,
- nil, val[4])
-
- rescue_ = @builder.begin_body(val[2], [ rescue_body ])
-
- result = @builder.op_assign(val[0], val[1], rescue_)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 644)
- def _reduce_198(val, _values, result)
- result = @builder.op_assign(
- @builder.index(
- val[0], val[1], val[2], val[3]),
- val[4], val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 651)
- def _reduce_199(val, _values, result)
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 658)
- def _reduce_200(val, _values, result)
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 665)
- def _reduce_201(val, _values, result)
- result = @builder.op_assign(
- @builder.call_method(
- val[0], val[1], val[2]),
- val[3], val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 672)
- def _reduce_202(val, _values, result)
- const = @builder.const_op_assignable(
- @builder.const_fetch(val[0], val[1], val[2]))
- result = @builder.op_assign(const, val[3], val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 678)
- def _reduce_203(val, _values, result)
- const = @builder.const_op_assignable(
- @builder.const_global(val[0], val[1]))
- result = @builder.op_assign(const, val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 684)
- def _reduce_204(val, _values, result)
- result = @builder.op_assign(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 688)
- def _reduce_205(val, _values, result)
- result = @builder.range_inclusive(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 692)
- def _reduce_206(val, _values, result)
- result = @builder.range_exclusive(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 696)
- def _reduce_207(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 700)
- def _reduce_208(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 704)
- def _reduce_209(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 708)
- def _reduce_210(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 712)
- def _reduce_211(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 716)
- def _reduce_212(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 720)
- def _reduce_213(val, _values, result)
- result = @builder.unary_op(val[0],
- @builder.binary_op(
- val[1], val[2], val[3]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 726)
- def _reduce_214(val, _values, result)
- result = @builder.unary_op(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 730)
- def _reduce_215(val, _values, result)
- result = @builder.unary_op(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 734)
- def _reduce_216(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 738)
- def _reduce_217(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 742)
- def _reduce_218(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 746)
- def _reduce_219(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 750)
- def _reduce_220(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 754)
- def _reduce_221(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 758)
- def _reduce_222(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 762)
- def _reduce_223(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 766)
- def _reduce_224(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 770)
- def _reduce_225(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 774)
- def _reduce_226(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 778)
- def _reduce_227(val, _values, result)
- result = @builder.match_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 782)
- def _reduce_228(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 786)
- def _reduce_229(val, _values, result)
- result = @builder.not_op(val[0], nil, val[1], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 790)
- def _reduce_230(val, _values, result)
- result = @builder.unary_op(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 794)
- def _reduce_231(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 798)
- def _reduce_232(val, _values, result)
- result = @builder.binary_op(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 802)
- def _reduce_233(val, _values, result)
- result = @builder.logical_op(:and, val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 806)
- def _reduce_234(val, _values, result)
- result = @builder.logical_op(:or, val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 810)
- def _reduce_235(val, _values, result)
- result = @builder.keyword_cmd(:defined?, val[0], nil, [ val[2] ], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 820)
- def _reduce_236(val, _values, result)
- @lexer.push_cond
- @lexer.cond.push(true)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 825)
- def _reduce_237(val, _values, result)
- @lexer.pop_cond
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 829)
- def _reduce_238(val, _values, result)
- result = @builder.ternary(val[0], val[1],
- val[3], val[5], val[7])
-
- result
- end
-.,.,
-
-# reduce 239 omitted
-
-# reduce 240 omitted
-
-# reduce 241 omitted
-
-# reduce 242 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 840)
- def _reduce_243(val, _values, result)
- result = val[0] << @builder.associate(nil, val[2], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 844)
- def _reduce_244(val, _values, result)
- result = [ @builder.associate(nil, val[0], nil) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 849)
- def _reduce_245(val, _values, result)
- result = val
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 854)
- def _reduce_246(val, _values, result)
- result = [ nil, [], nil ]
-
- result
- end
-.,.,
-
-# reduce 247 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 860)
- def _reduce_248(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-# reduce 249 omitted
-
-# reduce 250 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 866)
- def _reduce_251(val, _values, result)
- result = val[0] << @builder.associate(nil, val[2], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 870)
- def _reduce_252(val, _values, result)
- result = [ @builder.associate(nil, val[0], nil) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 875)
- def _reduce_253(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 879)
- def _reduce_254(val, _values, result)
- result = val[0].concat(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 883)
- def _reduce_255(val, _values, result)
- result = [ @builder.associate(nil, val[0], nil) ]
- result.concat(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 888)
- def _reduce_256(val, _values, result)
- assocs = @builder.associate(nil, val[2], nil)
- result = val[0] << assocs
- result.concat(val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 894)
- def _reduce_257(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 898)
- def _reduce_258(val, _values, result)
- result = @lexer.cmdarg.dup
- @lexer.cmdarg.push(true)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 903)
- def _reduce_259(val, _values, result)
- @lexer.cmdarg = val[0]
-
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 910)
- def _reduce_260(val, _values, result)
- result = @builder.block_pass(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 915)
- def _reduce_261(val, _values, result)
- result = [ val[1] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 919)
- def _reduce_262(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 924)
- def _reduce_263(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 928)
- def _reduce_264(val, _values, result)
- result = [ @builder.splat(val[0], val[1]) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 932)
- def _reduce_265(val, _values, result)
- result = val[0] << val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 936)
- def _reduce_266(val, _values, result)
- result = val[0] << @builder.splat(val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 941)
- def _reduce_267(val, _values, result)
- result = @builder.array(nil, val[0], nil)
-
- result
- end
-.,.,
-
-# reduce 268 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 947)
- def _reduce_269(val, _values, result)
- result = val[0] << val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 951)
- def _reduce_270(val, _values, result)
- result = val[0] << @builder.splat(val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 955)
- def _reduce_271(val, _values, result)
- result = [ @builder.splat(val[0], val[1]) ]
-
- result
- end
-.,.,
-
-# reduce 272 omitted
-
-# reduce 273 omitted
-
-# reduce 274 omitted
-
-# reduce 275 omitted
-
-# reduce 276 omitted
-
-# reduce 277 omitted
-
-# reduce 278 omitted
-
-# reduce 279 omitted
-
-# reduce 280 omitted
-
-# reduce 281 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 970)
- def _reduce_282(val, _values, result)
- result = @builder.call_method(nil, nil, val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 974)
- def _reduce_283(val, _values, result)
- result = @lexer.cmdarg.dup
- @lexer.cmdarg.clear
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 979)
- def _reduce_284(val, _values, result)
- @lexer.cmdarg = val[1]
-
- result = @builder.begin_keyword(val[0], val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 985)
- def _reduce_285(val, _values, result)
- result = @lexer.cmdarg.dup
- @lexer.cmdarg.clear
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 990)
- def _reduce_286(val, _values, result)
- @lexer.state = :expr_endarg
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 994)
- def _reduce_287(val, _values, result)
- @lexer.cmdarg = val[1]
-
- result = @builder.begin(val[0], val[2], val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1000)
- def _reduce_288(val, _values, result)
- @lexer.state = :expr_endarg
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1004)
- def _reduce_289(val, _values, result)
- result = @builder.begin(val[0], nil, val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1008)
- def _reduce_290(val, _values, result)
- result = @builder.begin(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1012)
- def _reduce_291(val, _values, result)
- result = @builder.const_fetch(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1016)
- def _reduce_292(val, _values, result)
- result = @builder.const_global(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1020)
- def _reduce_293(val, _values, result)
- result = @builder.array(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1024)
- def _reduce_294(val, _values, result)
- result = @builder.associate(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1028)
- def _reduce_295(val, _values, result)
- result = @builder.keyword_cmd(:return, val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1032)
- def _reduce_296(val, _values, result)
- result = @builder.keyword_cmd(:yield, val[0], val[1], val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1036)
- def _reduce_297(val, _values, result)
- result = @builder.keyword_cmd(:yield, val[0], val[1], [], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1040)
- def _reduce_298(val, _values, result)
- result = @builder.keyword_cmd(:yield, val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1044)
- def _reduce_299(val, _values, result)
- result = @builder.keyword_cmd(:defined?, val[0],
- val[2], [ val[3] ], val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1049)
- def _reduce_300(val, _values, result)
- result = @builder.not_op(val[0], val[1], val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1053)
- def _reduce_301(val, _values, result)
- result = @builder.not_op(val[0], val[1], nil, val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1057)
- def _reduce_302(val, _values, result)
- method_call = @builder.call_method(nil, nil, val[0])
-
- begin_t, args, body, end_t = val[1]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
-
- result
- end
-.,.,
-
-# reduce 303 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 1066)
- def _reduce_304(val, _values, result)
- begin_t, args, body, end_t = val[1]
- result = @builder.block(val[0],
- begin_t, args, body, end_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1072)
- def _reduce_305(val, _values, result)
- lambda_call = @builder.call_lambda(val[0])
-
- args, (begin_t, body, end_t) = val[1]
- result = @builder.block(lambda_call,
- begin_t, args, body, end_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1080)
- def _reduce_306(val, _values, result)
- else_t, else_ = val[4]
- result = @builder.condition(val[0], val[1], val[2],
- val[3], else_t,
- else_, val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1087)
- def _reduce_307(val, _values, result)
- else_t, else_ = val[4]
- result = @builder.condition(val[0], val[1], val[2],
- else_, else_t,
- val[3], val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1094)
- def _reduce_308(val, _values, result)
- @lexer.cond.push(true)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1098)
- def _reduce_309(val, _values, result)
- @lexer.cond.pop
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1102)
- def _reduce_310(val, _values, result)
- result = @builder.loop(:while, val[0], val[2], val[3],
- val[5], val[6])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1107)
- def _reduce_311(val, _values, result)
- @lexer.cond.push(true)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1111)
- def _reduce_312(val, _values, result)
- @lexer.cond.pop
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1115)
- def _reduce_313(val, _values, result)
- result = @builder.loop(:until, val[0], val[2], val[3],
- val[5], val[6])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1120)
- def _reduce_314(val, _values, result)
- *when_bodies, (else_t, else_body) = *val[3]
-
- result = @builder.case(val[0], val[1],
- when_bodies, else_t, else_body,
- val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1128)
- def _reduce_315(val, _values, result)
- *when_bodies, (else_t, else_body) = *val[2]
-
- result = @builder.case(val[0], nil,
- when_bodies, else_t, else_body,
- val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1136)
- def _reduce_316(val, _values, result)
- @lexer.cond.push(true)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1140)
- def _reduce_317(val, _values, result)
- @lexer.cond.pop
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1144)
- def _reduce_318(val, _values, result)
- result = @builder.for(val[0], val[1],
- val[2], val[4],
- val[5], val[7], val[8])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1150)
- def _reduce_319(val, _values, result)
- @static_env.extend_static
- @lexer.push_cmdarg
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1155)
- def _reduce_320(val, _values, result)
- if in_def?
- diagnostic :error, :class_in_def, nil, val[0]
- end
-
- lt_t, superclass = val[2]
- result = @builder.def_class(val[0], val[1],
- lt_t, superclass,
- val[4], val[5])
-
- @lexer.pop_cmdarg
- @static_env.unextend
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1169)
- def _reduce_321(val, _values, result)
- result = @def_level
- @def_level = 0
-
- @static_env.extend_static
- @lexer.push_cmdarg
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1177)
- def _reduce_322(val, _values, result)
- result = @builder.def_sclass(val[0], val[1], val[2],
- val[5], val[6])
-
- @lexer.pop_cmdarg
- @static_env.unextend
-
- @def_level = val[4]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1187)
- def _reduce_323(val, _values, result)
- @static_env.extend_static
- @lexer.push_cmdarg
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1192)
- def _reduce_324(val, _values, result)
- if in_def?
- diagnostic :error, :module_in_def, nil, val[0]
- end
-
- result = @builder.def_module(val[0], val[1],
- val[3], val[4])
-
- @lexer.pop_cmdarg
- @static_env.unextend
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1204)
- def _reduce_325(val, _values, result)
- @def_level += 1
- @static_env.extend_static
- @lexer.push_cmdarg
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1210)
- def _reduce_326(val, _values, result)
- result = @builder.def_method(val[0], val[1],
- val[3], val[4], val[5])
-
- @lexer.pop_cmdarg
- @static_env.unextend
- @def_level -= 1
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1219)
- def _reduce_327(val, _values, result)
- @lexer.state = :expr_fname
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1223)
- def _reduce_328(val, _values, result)
- @def_level += 1
- @static_env.extend_static
- @lexer.push_cmdarg
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1229)
- def _reduce_329(val, _values, result)
- result = @builder.def_singleton(val[0], val[1], val[2],
- val[4], val[6], val[7], val[8])
-
- @lexer.pop_cmdarg
- @static_env.unextend
- @def_level -= 1
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1238)
- def _reduce_330(val, _values, result)
- result = @builder.keyword_cmd(:break, val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1242)
- def _reduce_331(val, _values, result)
- result = @builder.keyword_cmd(:next, val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1246)
- def _reduce_332(val, _values, result)
- result = @builder.keyword_cmd(:redo, val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1250)
- def _reduce_333(val, _values, result)
- result = @builder.keyword_cmd(:retry, val[0])
-
- result
- end
-.,.,
-
-# reduce 334 omitted
-
-# reduce 335 omitted
-
-# reduce 336 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 1259)
- def _reduce_337(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-# reduce 338 omitted
-
-# reduce 339 omitted
-
-# reduce 340 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 1268)
- def _reduce_341(val, _values, result)
- else_t, else_ = val[4]
- result = [ val[0],
- @builder.condition(val[0], val[1], val[2],
- val[3], else_t,
- else_, nil),
- ]
-
- result
- end
-.,.,
-
-# reduce 342 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 1279)
- def _reduce_343(val, _values, result)
- result = val
-
- result
- end
-.,.,
-
-# reduce 344 omitted
-
-# reduce 345 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 1287)
- def _reduce_346(val, _values, result)
- result = @builder.arg(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1291)
- def _reduce_347(val, _values, result)
- result = @builder.multi_lhs(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1296)
- def _reduce_348(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1300)
- def _reduce_349(val, _values, result)
- result = val[0] << val[2]
-
- result
- end
-.,.,
-
-# reduce 350 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 1306)
- def _reduce_351(val, _values, result)
- result = val[0].
- push(@builder.restarg(val[2], val[3]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1311)
- def _reduce_352(val, _values, result)
- result = val[0].
- push(@builder.restarg(val[2], val[3])).
- concat(val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1317)
- def _reduce_353(val, _values, result)
- result = val[0].
- push(@builder.restarg(val[2]))
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1322)
- def _reduce_354(val, _values, result)
- result = val[0].
- push(@builder.restarg(val[2])).
- concat(val[4])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1328)
- def _reduce_355(val, _values, result)
- result = [ @builder.restarg(val[0], val[1]) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1332)
- def _reduce_356(val, _values, result)
- result = [ @builder.restarg(val[0], val[1]),
- *val[3] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1337)
- def _reduce_357(val, _values, result)
- result = [ @builder.restarg(val[0]) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1341)
- def _reduce_358(val, _values, result)
- result = [ @builder.restarg(val[0]),
- *val[2] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1347)
- def _reduce_359(val, _values, result)
- result = val[0].concat(val[2]).concat(val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1351)
- def _reduce_360(val, _values, result)
- result = val[0].concat(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1355)
- def _reduce_361(val, _values, result)
- result = val[0].concat(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1359)
- def _reduce_362(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1365)
- def _reduce_363(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1369)
- def _reduce_364(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1374)
- def _reduce_365(val, _values, result)
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1381)
- def _reduce_366(val, _values, result)
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[6]).
- concat(val[7])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1389)
- def _reduce_367(val, _values, result)
- result = val[0].
- concat(val[2]).
- concat(val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1395)
- def _reduce_368(val, _values, result)
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1402)
- def _reduce_369(val, _values, result)
- result = val[0].
- concat(val[2]).
- concat(val[3])
-
- result
- end
-.,.,
-
-# reduce 370 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 1409)
- def _reduce_371(val, _values, result)
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1416)
- def _reduce_372(val, _values, result)
- result = val[0].concat(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1420)
- def _reduce_373(val, _values, result)
- result = val[0].
- concat(val[2]).
- concat(val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1426)
- def _reduce_374(val, _values, result)
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1433)
- def _reduce_375(val, _values, result)
- result = val[0].
- concat(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1438)
- def _reduce_376(val, _values, result)
- result = val[0].
- concat(val[2]).
- concat(val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1444)
- def _reduce_377(val, _values, result)
- result = val[0].
- concat(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1449)
- def _reduce_378(val, _values, result)
- result = val[0].
- concat(val[2]).
- concat(val[3])
-
- result
- end
-.,.,
-
-# reduce 379 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 1457)
- def _reduce_380(val, _values, result)
- result = @builder.args(nil, [], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1461)
- def _reduce_381(val, _values, result)
- @lexer.state = :expr_value
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1466)
- def _reduce_382(val, _values, result)
- result = @builder.args(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1470)
- def _reduce_383(val, _values, result)
- result = @builder.args(val[0], [], val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1474)
- def _reduce_384(val, _values, result)
- result = @builder.args(val[0], val[1].concat(val[2]), val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1479)
- def _reduce_385(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1483)
- def _reduce_386(val, _values, result)
- result = val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1488)
- def _reduce_387(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1492)
- def _reduce_388(val, _values, result)
- result = val[0] << val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1497)
- def _reduce_389(val, _values, result)
- result = @builder.shadowarg(val[0])
-
- result
- end
-.,.,
-
-# reduce 390 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 1502)
- def _reduce_391(val, _values, result)
- @static_env.extend_dynamic
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1506)
- def _reduce_392(val, _values, result)
- result = @lexer.cmdarg.dup
- @lexer.cmdarg.clear
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1511)
- def _reduce_393(val, _values, result)
- @lexer.cmdarg = val[2]
- @lexer.cmdarg.lexpop
-
- result = [ val[1], val[3] ]
-
- @static_env.unextend
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1521)
- def _reduce_394(val, _values, result)
- result = @builder.args(val[0], val[1].concat(val[2]), val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1525)
- def _reduce_395(val, _values, result)
- result = @builder.args(nil, val[0], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1530)
- def _reduce_396(val, _values, result)
- result = [ val[0], val[1], val[2] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1534)
- def _reduce_397(val, _values, result)
- result = [ val[0], val[1], val[2] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1539)
- def _reduce_398(val, _values, result)
- @static_env.extend_dynamic
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1543)
- def _reduce_399(val, _values, result)
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1550)
- def _reduce_400(val, _values, result)
- begin_t, block_args, body, end_t = val[1]
- result = @builder.block(val[0],
- begin_t, block_args, body, end_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1556)
- def _reduce_401(val, _values, result)
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1562)
- def _reduce_402(val, _values, result)
- lparen_t, args, rparen_t = val[3]
- method_call = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
-
- begin_t, args, body, end_t = val[4]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1572)
- def _reduce_403(val, _values, result)
- method_call = @builder.call_method(val[0], val[1], val[2],
- nil, val[3], nil)
-
- begin_t, args, body, end_t = val[4]
- result = @builder.block(method_call,
- begin_t, args, body, end_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1582)
- def _reduce_404(val, _values, result)
- lparen_t, args, rparen_t = val[1]
- result = @builder.call_method(nil, nil, val[0],
- lparen_t, args, rparen_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1588)
- def _reduce_405(val, _values, result)
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1594)
- def _reduce_406(val, _values, result)
- lparen_t, args, rparen_t = val[3]
- result = @builder.call_method(val[0], val[1], val[2],
- lparen_t, args, rparen_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1600)
- def _reduce_407(val, _values, result)
- result = @builder.call_method(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1604)
- def _reduce_408(val, _values, result)
- lparen_t, args, rparen_t = val[2]
- result = @builder.call_method(val[0], val[1], nil,
- lparen_t, args, rparen_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1610)
- def _reduce_409(val, _values, result)
- lparen_t, args, rparen_t = val[2]
- result = @builder.call_method(val[0], val[1], nil,
- lparen_t, args, rparen_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1616)
- def _reduce_410(val, _values, result)
- lparen_t, args, rparen_t = val[1]
- result = @builder.keyword_cmd(:super, val[0],
- lparen_t, args, rparen_t)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1622)
- def _reduce_411(val, _values, result)
- result = @builder.keyword_cmd(:zsuper, val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1626)
- def _reduce_412(val, _values, result)
- result = @builder.index(val[0], val[1], val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1631)
- def _reduce_413(val, _values, result)
- @static_env.extend_dynamic
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1635)
- def _reduce_414(val, _values, result)
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1641)
- def _reduce_415(val, _values, result)
- @static_env.extend_dynamic
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1645)
- def _reduce_416(val, _values, result)
- result = [ val[0], val[2], val[3], val[4] ]
-
- @static_env.unextend
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1652)
- def _reduce_417(val, _values, result)
- result = [ @builder.when(val[0], val[1], val[2], val[3]),
- *val[4] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1658)
- def _reduce_418(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-# reduce 419 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 1664)
- def _reduce_420(val, _values, result)
- assoc_t, exc_var = val[2]
-
- if val[1]
- exc_list = @builder.array(nil, val[1], nil)
- end
-
- result = [ @builder.rescue_body(val[0],
- exc_list, assoc_t, exc_var,
- val[3], val[4]),
- *val[5] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1677)
- def _reduce_421(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1682)
- def _reduce_422(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-# reduce 423 omitted
-
-# reduce 424 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 1689)
- def _reduce_425(val, _values, result)
- result = [ val[0], val[1] ]
-
- result
- end
-.,.,
-
-# reduce 426 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 1695)
- def _reduce_427(val, _values, result)
- result = [ val[0], val[1] ]
-
- result
- end
-.,.,
-
-# reduce 428 omitted
-
-# reduce 429 omitted
-
-# reduce 430 omitted
-
-# reduce 431 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 1705)
- def _reduce_432(val, _values, result)
- result = @builder.string_compose(nil, val[0], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1710)
- def _reduce_433(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1714)
- def _reduce_434(val, _values, result)
- result = val[0] << val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1719)
- def _reduce_435(val, _values, result)
- result = @builder.string_compose(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1723)
- def _reduce_436(val, _values, result)
- result = @builder.string(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1727)
- def _reduce_437(val, _values, result)
- result = @builder.character(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1732)
- def _reduce_438(val, _values, result)
- result = @builder.xstring_compose(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1737)
- def _reduce_439(val, _values, result)
- opts = @builder.regexp_options(val[3])
- result = @builder.regexp_compose(val[0], val[1], val[2], opts)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1743)
- def _reduce_440(val, _values, result)
- result = @builder.words_compose(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1748)
- def _reduce_441(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1752)
- def _reduce_442(val, _values, result)
- result = val[0] << @builder.word(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1757)
- def _reduce_443(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1761)
- def _reduce_444(val, _values, result)
- result = val[0] << val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1766)
- def _reduce_445(val, _values, result)
- result = @builder.symbols_compose(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1771)
- def _reduce_446(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1775)
- def _reduce_447(val, _values, result)
- result = val[0] << @builder.word(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1780)
- def _reduce_448(val, _values, result)
- result = @builder.words_compose(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1785)
- def _reduce_449(val, _values, result)
- result = @builder.symbols_compose(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1790)
- def _reduce_450(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1794)
- def _reduce_451(val, _values, result)
- result = val[0] << @builder.string_internal(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1799)
- def _reduce_452(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1803)
- def _reduce_453(val, _values, result)
- result = val[0] << @builder.symbol_internal(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1808)
- def _reduce_454(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1812)
- def _reduce_455(val, _values, result)
- result = val[0] << val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1817)
- def _reduce_456(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1821)
- def _reduce_457(val, _values, result)
- result = val[0] << val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1826)
- def _reduce_458(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1830)
- def _reduce_459(val, _values, result)
- result = val[0] << val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1835)
- def _reduce_460(val, _values, result)
- result = @builder.string_internal(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1839)
- def _reduce_461(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1843)
- def _reduce_462(val, _values, result)
- @lexer.cond.push(false)
- @lexer.cmdarg.push(false)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1848)
- def _reduce_463(val, _values, result)
- @lexer.cond.lexpop
- @lexer.cmdarg.lexpop
-
- result = @builder.begin(val[0], val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1856)
- def _reduce_464(val, _values, result)
- result = @builder.gvar(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1860)
- def _reduce_465(val, _values, result)
- result = @builder.ivar(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1864)
- def _reduce_466(val, _values, result)
- result = @builder.cvar(val[0])
-
- result
- end
-.,.,
-
-# reduce 467 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 1871)
- def _reduce_468(val, _values, result)
- result = @builder.symbol(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1876)
- def _reduce_469(val, _values, result)
- result = @builder.symbol_compose(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1881)
- def _reduce_470(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1885)
- def _reduce_471(val, _values, result)
- result = @builder.negate(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1890)
- def _reduce_472(val, _values, result)
- result = @builder.integer(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1894)
- def _reduce_473(val, _values, result)
- result = @builder.float(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1898)
- def _reduce_474(val, _values, result)
- result = @builder.rational(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1902)
- def _reduce_475(val, _values, result)
- result = @builder.complex(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1907)
- def _reduce_476(val, _values, result)
- result = @builder.ident(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1911)
- def _reduce_477(val, _values, result)
- result = @builder.ivar(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1915)
- def _reduce_478(val, _values, result)
- result = @builder.gvar(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1919)
- def _reduce_479(val, _values, result)
- result = @builder.const(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1923)
- def _reduce_480(val, _values, result)
- result = @builder.cvar(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1928)
- def _reduce_481(val, _values, result)
- result = @builder.nil(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1932)
- def _reduce_482(val, _values, result)
- result = @builder.self(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1936)
- def _reduce_483(val, _values, result)
- result = @builder.true(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1940)
- def _reduce_484(val, _values, result)
- result = @builder.false(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1944)
- def _reduce_485(val, _values, result)
- result = @builder.__FILE__(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1948)
- def _reduce_486(val, _values, result)
- result = @builder.__LINE__(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1952)
- def _reduce_487(val, _values, result)
- result = @builder.__ENCODING__(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1957)
- def _reduce_488(val, _values, result)
- result = @builder.accessible(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1961)
- def _reduce_489(val, _values, result)
- result = @builder.accessible(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1966)
- def _reduce_490(val, _values, result)
- result = @builder.assignable(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1970)
- def _reduce_491(val, _values, result)
- result = @builder.assignable(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1975)
- def _reduce_492(val, _values, result)
- result = @builder.nth_ref(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1979)
- def _reduce_493(val, _values, result)
- result = @builder.back_ref(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1984)
- def _reduce_494(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1988)
- def _reduce_495(val, _values, result)
- @lexer.state = :expr_value
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1992)
- def _reduce_496(val, _values, result)
- result = [ val[0], val[2] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 1996)
- def _reduce_497(val, _values, result)
- yyerrok
- result = nil
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2002)
- def _reduce_498(val, _values, result)
- result = @builder.args(val[0], val[1], val[2])
-
- @lexer.state = :expr_value
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2007)
- def _reduce_499(val, _values, result)
- result = @lexer.in_kwarg
- @lexer.in_kwarg = true
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2012)
- def _reduce_500(val, _values, result)
- @lexer.in_kwarg = val[0]
- result = @builder.args(nil, val[1], nil)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2018)
- def _reduce_501(val, _values, result)
- result = val[0].concat(val[2]).concat(val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2022)
- def _reduce_502(val, _values, result)
- result = val[0].concat(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2026)
- def _reduce_503(val, _values, result)
- result = val[0].concat(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2030)
- def _reduce_504(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2035)
- def _reduce_505(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2039)
- def _reduce_506(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2044)
- def _reduce_507(val, _values, result)
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2051)
- def _reduce_508(val, _values, result)
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[6]).
- concat(val[7])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2059)
- def _reduce_509(val, _values, result)
- result = val[0].
- concat(val[2]).
- concat(val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2065)
- def _reduce_510(val, _values, result)
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2072)
- def _reduce_511(val, _values, result)
- result = val[0].
- concat(val[2]).
- concat(val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2078)
- def _reduce_512(val, _values, result)
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2085)
- def _reduce_513(val, _values, result)
- result = val[0].
- concat(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2090)
- def _reduce_514(val, _values, result)
- result = val[0].
- concat(val[2]).
- concat(val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2096)
- def _reduce_515(val, _values, result)
- result = val[0].
- concat(val[2]).
- concat(val[4]).
- concat(val[5])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2103)
- def _reduce_516(val, _values, result)
- result = val[0].
- concat(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2108)
- def _reduce_517(val, _values, result)
- result = val[0].
- concat(val[2]).
- concat(val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2114)
- def _reduce_518(val, _values, result)
- result = val[0].
- concat(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2119)
- def _reduce_519(val, _values, result)
- result = val[0].
- concat(val[2]).
- concat(val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2125)
- def _reduce_520(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2129)
- def _reduce_521(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2134)
- def _reduce_522(val, _values, result)
- diagnostic :error, :argument_const, nil, val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2138)
- def _reduce_523(val, _values, result)
- diagnostic :error, :argument_ivar, nil, val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2142)
- def _reduce_524(val, _values, result)
- diagnostic :error, :argument_gvar, nil, val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2146)
- def _reduce_525(val, _values, result)
- diagnostic :error, :argument_cvar, nil, val[0]
-
- result
- end
-.,.,
-
-# reduce 526 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 2152)
- def _reduce_527(val, _values, result)
- @static_env.declare val[0][0]
-
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2159)
- def _reduce_528(val, _values, result)
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2164)
- def _reduce_529(val, _values, result)
- result = @builder.arg(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2168)
- def _reduce_530(val, _values, result)
- result = @builder.multi_lhs(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2173)
- def _reduce_531(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2177)
- def _reduce_532(val, _values, result)
- result = val[0] << val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2182)
- def _reduce_533(val, _values, result)
- check_kwarg_name(val[0])
-
- @static_env.declare val[0][0]
-
- result = val[0]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2191)
- def _reduce_534(val, _values, result)
- result = @builder.kwoptarg(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2195)
- def _reduce_535(val, _values, result)
- result = @builder.kwarg(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2200)
- def _reduce_536(val, _values, result)
- result = @builder.kwoptarg(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2204)
- def _reduce_537(val, _values, result)
- result = @builder.kwarg(val[0])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2209)
- def _reduce_538(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2213)
- def _reduce_539(val, _values, result)
- result = val[0] << val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2218)
- def _reduce_540(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2222)
- def _reduce_541(val, _values, result)
- result = val[0] << val[2]
-
- result
- end
-.,.,
-
-# reduce 542 omitted
-
-# reduce 543 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 2229)
- def _reduce_544(val, _values, result)
- @static_env.declare val[1][0]
-
- result = [ @builder.kwrestarg(val[0], val[1]) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2235)
- def _reduce_545(val, _values, result)
- result = [ @builder.kwrestarg(val[0]) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2240)
- def _reduce_546(val, _values, result)
- result = @builder.optarg(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2245)
- def _reduce_547(val, _values, result)
- result = @builder.optarg(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2250)
- def _reduce_548(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2254)
- def _reduce_549(val, _values, result)
- result = val[0] << val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2259)
- def _reduce_550(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2263)
- def _reduce_551(val, _values, result)
- result = val[0] << val[2]
-
- result
- end
-.,.,
-
-# reduce 552 omitted
-
-# reduce 553 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 2270)
- def _reduce_554(val, _values, result)
- @static_env.declare val[1][0]
-
- result = [ @builder.restarg(val[0], val[1]) ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2276)
- def _reduce_555(val, _values, result)
- result = [ @builder.restarg(val[0]) ]
-
- result
- end
-.,.,
-
-# reduce 556 omitted
-
-# reduce 557 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 2283)
- def _reduce_558(val, _values, result)
- @static_env.declare val[1][0]
-
- result = @builder.blockarg(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2290)
- def _reduce_559(val, _values, result)
- result = [ val[1] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2294)
- def _reduce_560(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-# reduce 561 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 2300)
- def _reduce_562(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2305)
- def _reduce_563(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-# reduce 564 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 2311)
- def _reduce_565(val, _values, result)
- result = [ val[0] ]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2315)
- def _reduce_566(val, _values, result)
- result = val[0] << val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2320)
- def _reduce_567(val, _values, result)
- result = @builder.pair(val[0], val[1], val[2])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2324)
- def _reduce_568(val, _values, result)
- result = @builder.pair_keyword(val[0], val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2328)
- def _reduce_569(val, _values, result)
- result = @builder.pair_quoted(val[0], val[1], val[2], val[3])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2332)
- def _reduce_570(val, _values, result)
- result = @builder.kwsplat(val[0], val[1])
-
- result
- end
-.,.,
-
-# reduce 571 omitted
-
-# reduce 572 omitted
-
-# reduce 573 omitted
-
-# reduce 574 omitted
-
-# reduce 575 omitted
-
-# reduce 576 omitted
-
-# reduce 577 omitted
-
-# reduce 578 omitted
-
-# reduce 579 omitted
-
-# reduce 580 omitted
-
-# reduce 581 omitted
-
-# reduce 582 omitted
-
-# reduce 583 omitted
-
-# reduce 584 omitted
-
-# reduce 585 omitted
-
-# reduce 586 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 2343)
- def _reduce_587(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'ruby22.y', 2347)
- def _reduce_588(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-# reduce 589 omitted
-
-# reduce 590 omitted
-
-# reduce 591 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 2353)
- def _reduce_592(val, _values, result)
- yyerrok
-
- result
- end
-.,.,
-
-# reduce 593 omitted
-
-# reduce 594 omitted
-
-# reduce 595 omitted
-
-module_eval(<<'.,.,', 'ruby22.y', 2362)
- def _reduce_596(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-def _reduce_none(val, _values, result)
- val[0]
-end
-
- end # class Ruby22
-end # module Parser
diff --git a/test/racc/regress/tp_plus b/test/racc/regress/tp_plus
deleted file mode 100644
index e57f316343..0000000000
--- a/test/racc/regress/tp_plus
+++ /dev/null
@@ -1,1935 +0,0 @@
-#
-# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.5.2
-# from Racc grammar file "".
-#
-
-require 'racc/parser.rb'
-module TPPlus
- class Parser < Racc::Parser
-
-module_eval(<<'...end tp_plus.y/module_eval...', 'tp_plus.y', 592)
-
- include TPPlus::Nodes
-
- attr_reader :interpreter
- def initialize(scanner, interpreter = TPPlus::Interpreter.new)
- @scanner = scanner
- @interpreter = interpreter
- super()
- end
-
- def next_token
- t = @scanner.next_token
- @interpreter.line_count += 1 if t && t[0] == :NEWLINE
-
- #puts t.inspect
- t
- end
-
- def parse
- #@yydebug =true
-
- do_parse
- @interpreter
- end
-
- def on_error(t, val, vstack)
- raise ParseError, sprintf("Parse error on line #{@scanner.tok_line} column #{@scanner.tok_col}: %s (%s)",
- val.inspect, token_to_str(t) || '?')
- end
-
- class ParseError < StandardError ; end
-...end tp_plus.y/module_eval...
-##### State transition tables begin ###
-
-racc_action_table = [
- 62, 62, 62, 62, 101, 122, 62, 41, 38, 130,
- 275, 265, 72, 41, 72, 98, 113, 72, 53, 114,
- 41, 67, 67, 67, 67, 234, 38, 26, 152, 151,
- 101, 36, 64, 159, 81, 82, 72, 308, 159, 81,
- 82, 72, 122, 70, 308, 60, 74, 60, 42, 308,
- 60, 43, 44, 131, 45, 31, 32, 300, 75, 34,
- 35, 46, 47, 102, 60, 273, 30, 72, 29, 28,
- 25, 63, 76, 37, 27, 24, 62, 41, 38, 37,
- 69, 69, 69, 69, 33, 61, 37, 97, 53, 102,
- 61, 37, 81, 82, 355, 61, 103, 26, 82, 72,
- 60, 36, 159, 81, 82, 72, 208, 221, 209, 371,
- 82, 72, 105, 220, 33, 81, 82, 72, 42, 72,
- 317, 43, 44, 111, 45, 31, 32, 96, 72, 34,
- 35, 46, 47, 96, 60, 223, 30, 224, 29, 28,
- 25, 63, 115, 37, 27, 24, 62, 41, 38, 72,
- 60, 81, 82, 72, 33, 61, 121, 116, 53, 61,
- 122, 88, 94, 96, 321, 117, 118, 26, 317, 352,
- 125, 36, 323, 127, 203, 349, 350, 351, 353, 303,
- 304, 96, 367, 81, 82, 72, 60, 255, 42, 209,
- 101, 43, 44, 122, 45, 31, 32, 185, 94, 34,
- 35, 46, 47, 186, 60, 191, 30, 61, 29, 28,
- 25, 63, 321, 37, 27, 24, 62, 41, 38, 188,
- 323, 122, 203, 122, 33, 61, 82, 72, 53, 336,
- 335, 182, 181, 179, 180, 177, 173, 26, 176, 174,
- 199, 36, 81, 82, 72, 81, 82, 72, 81, 82,
- 72, 81, 82, 81, 82, 72, 200, 201, 42, 203,
- 204, 43, 44, 96, 45, 31, 32, 210, 211, 34,
- 35, 46, 47, 212, 60, 213, 30, 214, 29, 28,
- 25, 63, 215, 37, 27, 24, 216, 178, 217, 175,
- 81, 82, 72, 218, 33, 61, 81, 82, 72, 219,
- 88, 317, 96, 81, 82, 72, 88, 227, 96, 81,
- 82, 72, 227, 88, 96, 96, 81, 82, 72, 88,
- 229, 96, 81, 82, 72, 60, 88, 230, 96, 231,
- 234, 60, 88, 235, 96, 238, 122, 94, 60, 81,
- 82, 72, 122, 94, 60, 321, 61, 81, 82, 72,
- 94, 60, 61, 323, 241, 203, 94, 60, 242, 61,
- 244, 245, 246, 94, 247, 61, 248, 249, 250, 94,
- 251, 252, 61, 81, 82, 72, 253, 254, 61, 81,
- 82, 72, 257, 88, 188, 96, 81, 82, 72, 88,
- 259, 96, 81, 82, 72, 269, 88, 271, 96, 81,
- 82, 72, 88, 276, 96, 122, 281, 282, 60, 88,
- 283, 96, 284, 285, 60, 286, 287, 288, 289, 290,
- 94, 60, 82, 292, 293, 294, 94, 60, 122, 61,
- 122, 72, 298, 94, 60, 61, 301, 302, 305, 94,
- 306, 313, 61, 314, 122, 122, 94, 325, 61, 135,
- 136, 139, 140, 137, 138, 61, 141, 142, 144, 145,
- 146, 148, 143, 147, 135, 136, 139, 140, 137, 138,
- 326, 141, 142, 144, 145, 146, 148, 143, 147, 327,
- 328, 188, 97, 333, 275, 122, 33, 135, 136, 139,
- 140, 137, 138, 205, 141, 142, 144, 145, 146, 148,
- 143, 147, 188, 188, 333, 122, 346, 347, 207, 135,
- 136, 139, 140, 137, 138, 348, 141, 142, 144, 145,
- 146, 148, 143, 147, 188, 356, 357, 358, 359, 360,
- 135, 136, 139, 140, 137, 138, 361, 141, 142, 144,
- 145, 146, 148, 143, 147, 135, 136, 139, 140, 137,
- 138, 362, 141, 142, 144, 145, 146, 148, 143, 147,
- 135, 136, 139, 140, 137, 138, 122, 141, 142, 144,
- 145, 146, 148, 143, 147, 135, 136, 139, 140, 137,
- 138, 364, 141, 142, 144, 145, 146, 148, 143, 147,
- 135, 136, 139, 140, 137, 138, 72, 141, 142, 144,
- 145, 146, 148, 143, 147, 135, 136, 139, 140, 137,
- 138, 33, 141, 142, 144, 145, 146, 148, 143, 147,
- 135, 136, 139, 140, 137, 138, 378, 141, 142, 144,
- 145, 146, 148, 143, 147, 135, 136, 139, 140, 137,
- 138, 379, 141, 142, 144, 145, 146, 148, 143, 147,
- 380, 381, 382, 383, 385, 386, 390, 72, 392 ]
-
-racc_action_check = [
- 3, 65, 309, 312, 72, 272, 0, 0, 0, 70,
- 240, 232, 28, 295, 29, 36, 48, 105, 0, 48,
- 296, 3, 65, 309, 312, 232, 383, 0, 95, 95,
- 36, 0, 1, 97, 97, 97, 97, 295, 186, 186,
- 186, 186, 343, 27, 296, 28, 30, 29, 0, 383,
- 105, 0, 0, 70, 0, 0, 0, 272, 31, 0,
- 0, 0, 0, 72, 0, 240, 0, 38, 0, 0,
- 0, 0, 32, 0, 0, 0, 2, 2, 2, 295,
- 3, 65, 309, 312, 0, 0, 296, 36, 2, 36,
- 97, 383, 224, 224, 343, 186, 37, 2, 358, 358,
- 38, 2, 209, 209, 209, 209, 153, 184, 153, 358,
- 229, 229, 38, 184, 41, 88, 88, 88, 2, 44,
- 301, 2, 2, 46, 2, 2, 2, 88, 47, 2,
- 2, 2, 2, 301, 2, 187, 2, 187, 2, 2,
- 2, 2, 53, 2, 2, 2, 188, 188, 188, 58,
- 88, 35, 35, 35, 2, 2, 60, 55, 188, 209,
- 63, 35, 88, 35, 301, 55, 55, 188, 337, 342,
- 64, 188, 301, 68, 301, 342, 342, 342, 342, 280,
- 280, 337, 357, 357, 357, 357, 35, 222, 188, 222,
- 99, 188, 188, 101, 188, 188, 188, 102, 35, 188,
- 188, 188, 188, 103, 188, 109, 188, 35, 188, 188,
- 188, 188, 337, 188, 188, 188, 225, 225, 225, 111,
- 337, 112, 337, 115, 188, 188, 293, 293, 225, 320,
- 320, 98, 98, 98, 98, 98, 98, 225, 98, 98,
- 117, 225, 34, 34, 34, 75, 75, 75, 234, 234,
- 234, 98, 98, 238, 238, 238, 118, 121, 225, 123,
- 132, 225, 225, 98, 225, 225, 225, 173, 174, 225,
- 225, 225, 225, 175, 225, 176, 225, 177, 225, 225,
- 225, 225, 178, 225, 225, 225, 179, 98, 180, 98,
- 42, 42, 42, 181, 225, 225, 43, 43, 43, 182,
- 42, 363, 42, 45, 45, 45, 43, 189, 43, 76,
- 76, 76, 190, 45, 363, 45, 94, 94, 94, 76,
- 191, 76, 113, 113, 113, 42, 94, 192, 94, 193,
- 194, 43, 113, 197, 113, 201, 202, 42, 45, 326,
- 326, 326, 203, 43, 76, 363, 42, 360, 360, 360,
- 45, 94, 43, 363, 204, 363, 76, 113, 205, 45,
- 210, 211, 212, 94, 213, 76, 214, 215, 216, 113,
- 217, 218, 94, 114, 114, 114, 219, 221, 113, 116,
- 116, 116, 226, 114, 227, 114, 134, 134, 134, 116,
- 228, 116, 199, 199, 199, 235, 134, 239, 134, 200,
- 200, 200, 199, 241, 199, 242, 244, 245, 114, 200,
- 246, 200, 247, 248, 116, 249, 250, 251, 252, 253,
- 114, 134, 254, 256, 260, 263, 116, 199, 265, 114,
- 266, 269, 270, 134, 200, 116, 275, 277, 291, 199,
- 292, 297, 134, 299, 300, 302, 200, 303, 199, 133,
- 133, 133, 133, 133, 133, 200, 133, 133, 133, 133,
- 133, 133, 133, 133, 150, 150, 150, 150, 150, 150,
- 304, 150, 150, 150, 150, 150, 150, 150, 150, 306,
- 307, 107, 308, 313, 315, 323, 325, 107, 107, 107,
- 107, 107, 107, 133, 107, 107, 107, 107, 107, 107,
- 107, 107, 328, 108, 331, 333, 338, 339, 150, 108,
- 108, 108, 108, 108, 108, 340, 108, 108, 108, 108,
- 108, 108, 108, 108, 110, 347, 349, 350, 351, 352,
- 110, 110, 110, 110, 110, 110, 353, 110, 110, 110,
- 110, 110, 110, 110, 110, 83, 83, 83, 83, 83,
- 83, 354, 83, 83, 83, 83, 83, 83, 83, 83,
- 195, 195, 195, 195, 195, 195, 355, 195, 195, 195,
- 195, 195, 195, 195, 195, 196, 196, 196, 196, 196,
- 196, 356, 196, 196, 196, 196, 196, 196, 196, 196,
- 198, 198, 198, 198, 198, 198, 359, 198, 198, 198,
- 198, 198, 198, 198, 198, 206, 206, 206, 206, 206,
- 206, 361, 206, 206, 206, 206, 206, 206, 206, 206,
- 236, 236, 236, 236, 236, 236, 365, 236, 236, 236,
- 236, 236, 236, 236, 236, 237, 237, 237, 237, 237,
- 237, 366, 237, 237, 237, 237, 237, 237, 237, 237,
- 368, 371, 372, 373, 376, 379, 384, 385, 387 ]
-
-racc_action_pointer = [
- 2, 32, 72, -4, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 13, -17, -15,
- 20, -16, -2, nil, 215, 124, 13, 67, 38, nil,
- nil, 32, 263, 269, 90, 276, 94, 99, -30, nil,
- nil, nil, nil, 125, nil, 127, nil, nil, 120, nil,
- 82, nil, nil, 135, 170, -3, nil, nil, 148, nil,
- -17, nil, -13, nil, nil, 218, 282, nil, nil, nil,
- nil, nil, nil, 514, nil, nil, nil, nil, 88, nil,
- nil, nil, nil, nil, 289, 1, nil, 7, 224, 173,
- nil, 168, 121, 129, nil, -12, nil, 456, 478, 154,
- 499, 194, 196, 295, 346, 198, 352, 210, 226, nil,
- nil, 231, nil, 179, nil, nil, nil, nil, nil, nil,
- nil, nil, 183, 418, 359, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 433, nil, nil, 31, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 189, 190, 195, 197, 199, 204, 208,
- 210, 215, 221, nil, 84, nil, 12, 60, 142, 260,
- 265, 246, 279, 281, 269, 529, 544, 315, 559, 365,
- 372, 258, 311, 317, 328, 341, 574, nil, nil, 76,
- 332, 333, 334, 336, 338, 339, 340, 342, 343, 348,
- nil, 303, 112, nil, 65, 212, 334, 359, 342, 82,
- nil, nil, -36, nil, 221, 321, 589, 604, 226, 349,
- -16, 328, 380, nil, 327, 328, 331, 333, 334, 336,
- 337, 338, 339, 340, 394, nil, 346, nil, nil, nil,
- 406, nil, nil, 377, nil, 403, 405, nil, nil, 402,
- 357, nil, -20, nil, nil, 360, nil, 420, nil, nil,
- 124, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 363, 414, 198, nil, 8, 15, 366, nil, 362,
- 419, 94, 420, 373, 396, nil, 404, 405, 408, -2,
- nil, nil, -1, 466, nil, 458, nil, nil, nil, nil,
- 202, nil, nil, 460, nil, 404, 312, nil, 477, nil,
- nil, 487, nil, 480, nil, nil, nil, 142, 431, 430,
- 467, nil, 156, 17, nil, nil, nil, 499, nil, 452,
- 453, 454, 455, 462, 472, 541, 506, 156, 70, 567,
- 320, 529, nil, 275, nil, 551, 564, nil, 575, nil,
- nil, 623, 577, 576, nil, nil, 577, nil, nil, 629,
- nil, nil, nil, 20, 581, 628, nil, 583, nil, nil,
- nil, nil, nil ]
-
-racc_action_default = [
- -2, -210, -1, -188, -8, -9, -10, -11, -12, -13,
- -14, -15, -16, -17, -18, -19, -20, -21, -22, -23,
- -24, -25, -26, -27, -28, -29, -30, -210, -210, -210,
- -210, -210, -210, -45, -210, -210, -118, -210, -210, -61,
- -62, -210, -210, -210, -210, -210, -210, -210, -81, -84,
- -85, -86, -87, -210, -111, -210, -116, -117, -210, -125,
- -210, -183, -184, -190, -210, -188, -3, -185, -7, -187,
- -210, -34, -118, -35, -36, -210, -210, -46, -103, -104,
- -158, -159, -160, -47, -128, -129, -130, -131, -210, -148,
- -149, -150, -151, -152, -210, -210, -157, -52, -210, -119,
- -121, -190, -210, -210, -58, -210, -63, -210, -210, -210,
- -210, -210, -190, -210, -210, -190, -210, -210, -210, -120,
- -126, -210, -189, -210, -192, 393, -4, -6, -186, -31,
- -32, -33, -210, -210, -210, -134, -135, -136, -137, -138,
- -139, -140, -141, -142, -143, -144, -145, -146, -147, -132,
- -210, -155, -156, -210, -50, -53, -54, -55, -56, -57,
- -112, -161, -162, -163, -164, -165, -166, -167, -168, -169,
- -170, -171, -172, -210, -210, -210, -210, -210, -210, -210,
- -210, -210, -210, -122, -210, -127, -52, -210, -210, -89,
- -89, -210, -210, -210, -210, -82, -83, -210, -113, -210,
- -210, -210, -190, -190, -210, -38, -133, -153, -48, -210,
- -210, -210, -210, -210, -210, -210, -210, -210, -210, -210,
- -123, -210, -210, -59, -210, -5, -210, -210, -210, -210,
- -67, -70, -78, -72, -210, -210, -114, -115, -210, -210,
- -210, -210, -190, -51, -210, -210, -210, -210, -210, -210,
- -210, -210, -210, -210, -210, -49, -210, -64, -88, -65,
- -210, -68, -69, -210, -73, -190, -190, -75, -76, -210,
- -210, -191, -190, -194, -195, -210, -37, -39, -41, -42,
- -210, -173, -174, -175, -176, -177, -178, -179, -180, -181,
- -182, -210, -210, -210, -71, -210, -210, -210, -154, -210,
- -190, -205, -190, -210, -210, -124, -210, -210, -210, -188,
- -79, -80, -188, -210, -193, -210, -197, -198, -199, -200,
- -210, -203, -204, -190, -40, -210, -210, -60, -210, -77,
- -74, -90, -91, -190, -196, -201, -202, -205, -210, -210,
- -210, -92, -210, -190, -207, -209, -43, -210, -66, -210,
- -210, -210, -210, -210, -210, -190, -210, -210, -210, -210,
- -210, -210, -206, -205, -44, -210, -210, -110, -210, -98,
- -99, -210, -210, -210, -107, -108, -102, -208, -93, -210,
- -94, -100, -95, -210, -210, -210, -109, -210, -105, -106,
- -97, -101, -96 ]
-
-racc_goto_table = [
- 39, 106, 39, 66, 78, 65, 123, 68, 77, 184,
- 9, 2, 9, 153, 40, 261, 40, 274, 310, 310,
- 194, 189, 190, 197, 192, 193, 71, 73, 39, 39,
- 277, 260, 233, 344, 79, 1, 104, 322, 39, 332,
- 291, 128, 40, 40, 109, 78, 129, 112, 243, 132,
- 311, 311, 40, 83, 226, 228, 389, 341, 232, 377,
- 107, 108, 263, 110, 266, 126, 331, 155, 167, 68,
- 264, 309, 312, 322, 365, 79, 368, 373, 387, 261,
- 384, 160, 119, 183, 120, 149, 168, 161, 162, 163,
- 324, 164, 334, 165, 133, 307, 166, 156, 169, 322,
- 170, 171, 222, 187, 172, 39, 388, 158, 202, 272,
- 316, 343, 150, nil, nil, nil, nil, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 195, 196, nil, 198, nil, nil, nil, nil, nil,
- nil, 258, nil, nil, 369, 239, 240, nil, nil, nil,
- 280, nil, 206, nil, nil, nil, 155, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 295, 296, nil, nil, nil, nil, 155,
- nil, nil, nil, nil, nil, nil, 156, nil, 39, nil,
- nil, nil, nil, nil, 256, nil, 158, nil, 9, 225,
- nil, nil, 40, nil, 267, nil, nil, nil, 78, 156,
- 280, nil, 270, nil, nil, 299, nil, 236, 237, 158,
- nil, nil, nil, nil, nil, 39, nil, nil, 65, 262,
- nil, nil, nil, nil, 268, 9, nil, nil, 79, 40,
- nil, 342, 340, 315, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 337, nil, nil, 297,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 338, 354, nil, nil, nil,
- nil, nil, nil, 262, nil, nil, 78, nil, 363, nil,
- 339, nil, nil, nil, nil, nil, nil, nil, nil, 329,
- nil, nil, 330, 68, nil, nil, 68, nil, nil, nil,
- nil, 376, nil, nil, nil, nil, 79, 78, nil, nil,
- 375, 366, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 79, 370, 372,
- 374, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 391 ]
-
-racc_goto_check = [
- 40, 35, 40, 4, 39, 3, 86, 7, 29, 34,
- 12, 2, 12, 37, 43, 47, 43, 89, 36, 36,
- 34, 5, 5, 34, 5, 5, 28, 28, 40, 40,
- 31, 46, 50, 94, 40, 1, 28, 75, 40, 56,
- 47, 6, 43, 43, 40, 39, 27, 40, 38, 29,
- 44, 44, 43, 30, 45, 45, 42, 56, 48, 94,
- 30, 30, 49, 30, 51, 4, 55, 39, 39, 7,
- 50, 52, 52, 75, 57, 40, 58, 59, 60, 47,
- 61, 62, 63, 67, 68, 71, 73, 76, 77, 78,
- 31, 79, 89, 80, 30, 46, 81, 40, 82, 75,
- 83, 84, 37, 28, 85, 40, 36, 12, 87, 88,
- 90, 93, 30, nil, nil, nil, nil, nil, nil, 43,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 30, 30, nil, 30, nil, nil, nil, nil, nil,
- nil, 5, nil, nil, 47, 86, 86, nil, nil, nil,
- 34, nil, 30, nil, nil, nil, 39, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 34, 34, nil, nil, nil, nil, 39,
- nil, nil, nil, nil, nil, nil, 40, nil, 40, nil,
- nil, nil, nil, nil, 39, nil, 12, nil, 12, 2,
- nil, nil, 43, nil, 39, nil, nil, nil, 39, 40,
- 34, nil, 29, nil, nil, 86, nil, 30, 30, 12,
- nil, nil, nil, nil, nil, 40, nil, nil, 3, 40,
- nil, nil, nil, nil, 40, 12, nil, nil, 40, 43,
- nil, 34, 5, 86, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 86, nil, nil, 40,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 35, 86, nil, nil, nil,
- nil, nil, nil, 40, nil, nil, 39, nil, 86, nil,
- 29, nil, nil, nil, nil, nil, nil, nil, nil, 4,
- nil, nil, 4, 7, nil, nil, 7, nil, nil, nil,
- nil, 35, nil, nil, nil, nil, 40, 39, nil, nil,
- 39, 29, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 40, 40, 40,
- 40, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 40 ]
-
-racc_goto_pointer = [
- nil, 35, 11, 3, 0, -86, -27, 4, nil, nil,
- nil, nil, 10, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, -24, -2, -26,
- 18, -212, nil, nil, -92, -40, -277, -84, -161, -30,
- 0, nil, -327, 14, -245, -135, -198, -214, -136, -170,
- -162, -170, -224, nil, nil, -247, -274, -283, -282, -283,
- -305, -296, -17, 24, nil, nil, nil, -16, 26, nil,
- nil, -3, nil, -12, nil, -264, -11, -10, -9, -7,
- -5, -2, 0, 2, 3, 6, -57, -15, -131, -223,
- -191, nil, nil, -226, -304 ]
-
-racc_goto_default = [
- nil, nil, nil, 3, nil, nil, nil, 4, 5, 6,
- 7, 8, 87, 10, 11, 12, 13, 14, 15, 16,
- 17, 18, 19, 20, 21, 22, 23, nil, 55, nil,
- nil, nil, 278, 279, 124, 54, 52, nil, 154, 89,
- 91, 157, 51, 92, 49, nil, nil, 80, nil, nil,
- nil, nil, nil, 48, 50, nil, nil, nil, nil, nil,
- nil, nil, nil, 56, 57, 99, 58, 100, 59, 84,
- 85, 86, 134, 90, 93, 95, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 318, nil, nil,
- 345, 319, 320, nil, nil ]
-
-racc_reduce_table = [
- 0, 0, :racc_error,
- 1, 86, :_reduce_1,
- 0, 86, :_reduce_none,
- 2, 87, :_reduce_3,
- 3, 87, :_reduce_4,
- 2, 90, :_reduce_5,
- 1, 91, :_reduce_none,
- 0, 91, :_reduce_none,
- 1, 88, :_reduce_none,
- 1, 88, :_reduce_none,
- 1, 88, :_reduce_none,
- 1, 88, :_reduce_none,
- 1, 88, :_reduce_none,
- 1, 88, :_reduce_none,
- 1, 88, :_reduce_none,
- 1, 88, :_reduce_none,
- 1, 88, :_reduce_none,
- 1, 88, :_reduce_none,
- 1, 88, :_reduce_none,
- 1, 88, :_reduce_none,
- 1, 88, :_reduce_none,
- 1, 88, :_reduce_none,
- 1, 88, :_reduce_none,
- 1, 88, :_reduce_none,
- 1, 88, :_reduce_none,
- 1, 88, :_reduce_none,
- 1, 88, :_reduce_none,
- 1, 88, :_reduce_none,
- 1, 88, :_reduce_28,
- 1, 88, :_reduce_29,
- 1, 111, :_reduce_30,
- 3, 110, :_reduce_31,
- 1, 112, :_reduce_none,
- 1, 112, :_reduce_none,
- 2, 109, :_reduce_34,
- 2, 107, :_reduce_35,
- 2, 106, :_reduce_36,
- 6, 104, :_reduce_37,
- 4, 104, :_reduce_38,
- 6, 104, :_reduce_39,
- 8, 104, :_reduce_40,
- 1, 116, :_reduce_none,
- 1, 116, :_reduce_none,
- 5, 117, :_reduce_43,
- 7, 118, :_reduce_44,
- 1, 120, :_reduce_45,
- 2, 102, :_reduce_46,
- 2, 103, :_reduce_47,
- 4, 121, :_reduce_48,
- 5, 121, :_reduce_49,
- 1, 122, :_reduce_50,
- 3, 122, :_reduce_51,
- 0, 122, :_reduce_52,
- 1, 123, :_reduce_none,
- 1, 123, :_reduce_none,
- 1, 123, :_reduce_none,
- 1, 123, :_reduce_none,
- 1, 126, :_reduce_57,
- 2, 127, :_reduce_58,
- 4, 127, :_reduce_59,
- 8, 127, :_reduce_60,
- 1, 113, :_reduce_none,
- 1, 113, :_reduce_none,
- 2, 129, :_reduce_63,
- 5, 98, :_reduce_64,
- 5, 98, :_reduce_65,
- 10, 100, :_reduce_66,
- 4, 101, :_reduce_67,
- 1, 131, :_reduce_none,
- 1, 131, :_reduce_none,
- 4, 94, :_reduce_70,
- 6, 105, :_reduce_71,
- 1, 133, :_reduce_72,
- 2, 133, :_reduce_73,
- 5, 135, :_reduce_74,
- 1, 136, :_reduce_none,
- 1, 136, :_reduce_none,
- 4, 134, :_reduce_77,
- 0, 134, :_reduce_none,
- 1, 137, :_reduce_none,
- 1, 137, :_reduce_none,
- 1, 99, :_reduce_none,
- 3, 99, :_reduce_82,
- 3, 99, :_reduce_83,
- 1, 138, :_reduce_none,
- 1, 138, :_reduce_none,
- 1, 138, :_reduce_none,
- 1, 138, :_reduce_none,
- 2, 130, :_reduce_88,
- 0, 130, :_reduce_89,
- 8, 95, :_reduce_90,
- 1, 140, :_reduce_91,
- 2, 140, :_reduce_92,
- 6, 141, :_reduce_93,
- 6, 141, :_reduce_94,
- 6, 141, :_reduce_95,
- 8, 141, :_reduce_96,
- 7, 141, :_reduce_97,
- 1, 143, :_reduce_none,
- 1, 143, :_reduce_none,
- 2, 143, :_reduce_100,
- 2, 146, :_reduce_101,
- 0, 146, :_reduce_none,
- 1, 114, :_reduce_none,
- 1, 114, :_reduce_none,
- 1, 145, :_reduce_none,
- 1, 145, :_reduce_none,
- 1, 144, :_reduce_none,
- 1, 144, :_reduce_none,
- 3, 142, :_reduce_109,
- 1, 142, :_reduce_110,
- 1, 96, :_reduce_111,
- 3, 93, :_reduce_112,
- 3, 139, :_reduce_113,
- 4, 139, :_reduce_114,
- 4, 139, :_reduce_115,
- 1, 125, :_reduce_none,
- 1, 125, :_reduce_none,
- 1, 148, :_reduce_118,
- 2, 148, :_reduce_119,
- 2, 149, :_reduce_120,
- 1, 150, :_reduce_121,
- 2, 150, :_reduce_122,
- 3, 152, :_reduce_123,
- 6, 152, :_reduce_124,
- 1, 151, :_reduce_125,
- 2, 151, :_reduce_126,
- 3, 153, :_reduce_127,
- 1, 115, :_reduce_none,
- 1, 115, :_reduce_none,
- 1, 154, :_reduce_130,
- 1, 154, :_reduce_none,
- 2, 154, :_reduce_132,
- 3, 155, :_reduce_133,
- 1, 157, :_reduce_134,
- 1, 157, :_reduce_135,
- 1, 157, :_reduce_136,
- 1, 157, :_reduce_137,
- 1, 157, :_reduce_138,
- 1, 157, :_reduce_139,
- 1, 157, :_reduce_140,
- 1, 157, :_reduce_141,
- 1, 157, :_reduce_142,
- 1, 157, :_reduce_143,
- 1, 157, :_reduce_144,
- 1, 157, :_reduce_145,
- 1, 157, :_reduce_146,
- 1, 157, :_reduce_147,
- 1, 156, :_reduce_none,
- 1, 156, :_reduce_none,
- 1, 156, :_reduce_none,
- 1, 156, :_reduce_none,
- 1, 156, :_reduce_none,
- 3, 159, :_reduce_153,
- 6, 128, :_reduce_154,
- 2, 158, :_reduce_155,
- 2, 158, :_reduce_156,
- 1, 160, :_reduce_157,
- 1, 124, :_reduce_none,
- 1, 124, :_reduce_159,
- 1, 132, :_reduce_160,
- 1, 147, :_reduce_none,
- 1, 147, :_reduce_none,
- 1, 147, :_reduce_none,
- 1, 147, :_reduce_none,
- 1, 147, :_reduce_none,
- 1, 147, :_reduce_none,
- 1, 147, :_reduce_none,
- 1, 147, :_reduce_none,
- 1, 147, :_reduce_none,
- 1, 147, :_reduce_none,
- 1, 147, :_reduce_none,
- 1, 147, :_reduce_none,
- 4, 170, :_reduce_173,
- 4, 169, :_reduce_174,
- 4, 168, :_reduce_175,
- 4, 167, :_reduce_176,
- 4, 166, :_reduce_177,
- 4, 165, :_reduce_178,
- 4, 161, :_reduce_179,
- 4, 164, :_reduce_180,
- 4, 162, :_reduce_181,
- 4, 163, :_reduce_182,
- 1, 97, :_reduce_183,
- 1, 92, :_reduce_184,
- 1, 89, :_reduce_185,
- 2, 89, :_reduce_186,
- 1, 89, :_reduce_none,
- 0, 89, :_reduce_none,
- 1, 119, :_reduce_189,
- 0, 119, :_reduce_none,
- 5, 108, :_reduce_191,
- 1, 171, :_reduce_none,
- 5, 172, :_reduce_193,
- 3, 172, :_reduce_194,
- 1, 173, :_reduce_195,
- 4, 173, :_reduce_196,
- 3, 174, :_reduce_197,
- 1, 175, :_reduce_none,
- 1, 175, :_reduce_none,
- 1, 175, :_reduce_none,
- 2, 175, :_reduce_201,
- 2, 175, :_reduce_202,
- 1, 175, :_reduce_203,
- 1, 177, :_reduce_none,
- 0, 177, :_reduce_none,
- 5, 176, :_reduce_206,
- 1, 178, :_reduce_207,
- 4, 178, :_reduce_208,
- 1, 179, :_reduce_none ]
-
-racc_reduce_n = 210
-
-racc_shift_n = 393
-
-racc_token_table = {
- false => 0,
- :error => 1,
- :ASSIGN => 2,
- :AT_SYM => 3,
- :COMMENT => 4,
- :JUMP => 5,
- :IO_METHOD => 6,
- :INPUT => 7,
- :OUTPUT => 8,
- :NUMREG => 9,
- :POSREG => 10,
- :VREG => 11,
- :SREG => 12,
- :TIME_SEGMENT => 13,
- :ARG => 14,
- :UALM => 15,
- :MOVE => 16,
- :DOT => 17,
- :TO => 18,
- :AT => 19,
- :TERM => 20,
- :OFFSET => 21,
- :SKIP => 22,
- :GROUP => 23,
- :SEMICOLON => 24,
- :NEWLINE => 25,
- :STRING => 26,
- :REAL => 27,
- :DIGIT => 28,
- :WORD => 29,
- :EQUAL => 30,
- :EEQUAL => 31,
- :NOTEQUAL => 32,
- :GTE => 33,
- :LTE => 34,
- :LT => 35,
- :GT => 36,
- :BANG => 37,
- :PLUS => 38,
- :MINUS => 39,
- :STAR => 40,
- :SLASH => 41,
- :DIV => 42,
- :AND => 43,
- :OR => 44,
- :MOD => 45,
- :IF => 46,
- :ELSE => 47,
- :END => 48,
- :UNLESS => 49,
- :FOR => 50,
- :IN => 51,
- :WHILE => 52,
- :WAIT_FOR => 53,
- :WAIT_UNTIL => 54,
- :TIMEOUT => 55,
- :AFTER => 56,
- :FANUC_USE => 57,
- :SET_SKIP_CONDITION => 58,
- :NAMESPACE => 59,
- :CASE => 60,
- :WHEN => 61,
- :INDIRECT => 62,
- :POSITION => 63,
- :EVAL => 64,
- :TIMER => 65,
- :TIMER_METHOD => 66,
- :RAISE => 67,
- :ABORT => 68,
- :POSITION_DATA => 69,
- :TRUE_FALSE => 70,
- :RUN => 71,
- :TP_HEADER => 72,
- :PAUSE => 73,
- :LPAREN => 74,
- :RPAREN => 75,
- :COLON => 76,
- :COMMA => 77,
- :LBRACK => 78,
- :RBRACK => 79,
- :LBRACE => 80,
- :RBRACE => 81,
- :LABEL => 82,
- :ADDRESS => 83,
- :false => 84 }
-
-racc_nt_base = 85
-
-racc_use_result_var = true
-
-Racc_arg = [
- racc_action_table,
- racc_action_check,
- racc_action_default,
- racc_action_pointer,
- racc_goto_table,
- racc_goto_check,
- racc_goto_default,
- racc_goto_pointer,
- racc_nt_base,
- racc_reduce_table,
- racc_token_table,
- racc_shift_n,
- racc_reduce_n,
- racc_use_result_var ]
-Ractor.make_shareable(Racc_arg) if defined?(Ractor)
-
-Racc_token_to_s_table = [
- "$end",
- "error",
- "ASSIGN",
- "AT_SYM",
- "COMMENT",
- "JUMP",
- "IO_METHOD",
- "INPUT",
- "OUTPUT",
- "NUMREG",
- "POSREG",
- "VREG",
- "SREG",
- "TIME_SEGMENT",
- "ARG",
- "UALM",
- "MOVE",
- "DOT",
- "TO",
- "AT",
- "TERM",
- "OFFSET",
- "SKIP",
- "GROUP",
- "SEMICOLON",
- "NEWLINE",
- "STRING",
- "REAL",
- "DIGIT",
- "WORD",
- "EQUAL",
- "EEQUAL",
- "NOTEQUAL",
- "GTE",
- "LTE",
- "LT",
- "GT",
- "BANG",
- "PLUS",
- "MINUS",
- "STAR",
- "SLASH",
- "DIV",
- "AND",
- "OR",
- "MOD",
- "IF",
- "ELSE",
- "END",
- "UNLESS",
- "FOR",
- "IN",
- "WHILE",
- "WAIT_FOR",
- "WAIT_UNTIL",
- "TIMEOUT",
- "AFTER",
- "FANUC_USE",
- "SET_SKIP_CONDITION",
- "NAMESPACE",
- "CASE",
- "WHEN",
- "INDIRECT",
- "POSITION",
- "EVAL",
- "TIMER",
- "TIMER_METHOD",
- "RAISE",
- "ABORT",
- "POSITION_DATA",
- "TRUE_FALSE",
- "RUN",
- "TP_HEADER",
- "PAUSE",
- "LPAREN",
- "RPAREN",
- "COLON",
- "COMMA",
- "LBRACK",
- "RBRACK",
- "LBRACE",
- "RBRACE",
- "LABEL",
- "ADDRESS",
- "false",
- "$start",
- "program",
- "statements",
- "statement",
- "terminator",
- "block",
- "optional_newline",
- "comment",
- "definition",
- "namespace",
- "motion_statement",
- "label_definition",
- "address",
- "conditional",
- "inline_conditional",
- "forloop",
- "while_loop",
- "use_statement",
- "set_skip_statement",
- "wait_statement",
- "case_statement",
- "fanuc_eval",
- "timer_method",
- "position_data",
- "raise",
- "tp_header_definition",
- "empty_stmt",
- "tp_header_value",
- "var_or_indirect",
- "indirectable",
- "expression",
- "wait_modifier",
- "timeout_modifier",
- "after_modifier",
- "swallow_newlines",
- "label",
- "program_call",
- "args",
- "arg",
- "number",
- "var",
- "string",
- "io_method",
- "indirect_thing",
- "jump",
- "else_block",
- "minmax_val",
- "integer",
- "case_conditions",
- "case_else",
- "case_condition",
- "case_allowed_condition",
- "case_allowed_statement",
- "inlineable",
- "assignment",
- "motion_modifiers",
- "motion_modifier",
- "speed",
- "valid_terminations",
- "time",
- "time_seg_actions",
- "optional_lpos_arg",
- "definable",
- "var_without_namespaces",
- "var_with_namespaces",
- "var_method_modifiers",
- "namespaces",
- "var_method_modifier",
- "ns",
- "unary_expression",
- "binary_expression",
- "factor",
- "operator",
- "signed_number",
- "paren_expr",
- "sign",
- "numreg",
- "output",
- "input",
- "posreg",
- "position",
- "vreg",
- "argument",
- "timer",
- "ualm",
- "sreg",
- "sn",
- "hash",
- "hash_attributes",
- "hash_attribute",
- "hash_value",
- "array",
- "optional_sign",
- "array_values",
- "array_value" ]
-Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)
-
-Racc_debug_parser = false
-
-##### State transition tables end #####
-
-# reduce 0 omitted
-
-module_eval(<<'.,.,', 'tp_plus.y', 35)
- def _reduce_1(val, _values, result)
- @interpreter.nodes = val[0]
- result
- end
-.,.,
-
-# reduce 2 omitted
-
-module_eval(<<'.,.,', 'tp_plus.y', 42)
- def _reduce_3(val, _values, result)
- result = [val[0]]
- result << val[1] unless val[1].nil?
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 46)
- def _reduce_4(val, _values, result)
- result = val[0] << val[1]
- result << val[2] unless val[2].nil?
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 52)
- def _reduce_5(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-# reduce 6 omitted
-
-# reduce 7 omitted
-
-# reduce 8 omitted
-
-# reduce 9 omitted
-
-# reduce 10 omitted
-
-# reduce 11 omitted
-
-# reduce 12 omitted
-
-# reduce 13 omitted
-
-# reduce 14 omitted
-
-# reduce 15 omitted
-
-# reduce 16 omitted
-
-# reduce 17 omitted
-
-# reduce 18 omitted
-
-# reduce 19 omitted
-
-# reduce 20 omitted
-
-# reduce 21 omitted
-
-# reduce 22 omitted
-
-# reduce 23 omitted
-
-# reduce 24 omitted
-
-# reduce 25 omitted
-
-# reduce 26 omitted
-
-# reduce 27 omitted
-
-module_eval(<<'.,.,', 'tp_plus.y', 85)
- def _reduce_28(val, _values, result)
- result = PauseNode.new
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 86)
- def _reduce_29(val, _values, result)
- result = AbortNode.new
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 90)
- def _reduce_30(val, _values, result)
- result = EmptyStmtNode.new()
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 94)
- def _reduce_31(val, _values, result)
- result = HeaderNode.new(val[0],val[2])
- result
- end
-.,.,
-
-# reduce 32 omitted
-
-# reduce 33 omitted
-
-module_eval(<<'.,.,', 'tp_plus.y', 103)
- def _reduce_34(val, _values, result)
- result = RaiseNode.new(val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 107)
- def _reduce_35(val, _values, result)
- result = TimerMethodNode.new(val[0],val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 111)
- def _reduce_36(val, _values, result)
- result = EvalNode.new(val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 116)
- def _reduce_37(val, _values, result)
- result = WaitForNode.new(val[2], val[4])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 118)
- def _reduce_38(val, _values, result)
- result = WaitUntilNode.new(val[2], nil)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 120)
- def _reduce_39(val, _values, result)
- result = WaitUntilNode.new(val[2],val[5])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 122)
- def _reduce_40(val, _values, result)
- result = WaitUntilNode.new(val[2],val[5].merge(val[7]))
- result
- end
-.,.,
-
-# reduce 41 omitted
-
-# reduce 42 omitted
-
-module_eval(<<'.,.,', 'tp_plus.y', 132)
- def _reduce_43(val, _values, result)
- result = { label: val[3] }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 137)
- def _reduce_44(val, _values, result)
- result = { timeout: [val[3],val[5]] }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 141)
- def _reduce_45(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 145)
- def _reduce_46(val, _values, result)
- result = UseNode.new(val[0],val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 150)
- def _reduce_47(val, _values, result)
- result = SetSkipNode.new(val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 154)
- def _reduce_48(val, _values, result)
- result = CallNode.new(val[0],val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 155)
- def _reduce_49(val, _values, result)
- result = CallNode.new(val[1],val[3],async: true)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 159)
- def _reduce_50(val, _values, result)
- result = [val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 160)
- def _reduce_51(val, _values, result)
- result = val[0] << val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 161)
- def _reduce_52(val, _values, result)
- result = []
- result
- end
-.,.,
-
-# reduce 53 omitted
-
-# reduce 54 omitted
-
-# reduce 55 omitted
-
-# reduce 56 omitted
-
-module_eval(<<'.,.,', 'tp_plus.y', 172)
- def _reduce_57(val, _values, result)
- result = StringNode.new(val[0])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 176)
- def _reduce_58(val, _values, result)
- result = IOMethodNode.new(val[0],val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 178)
- def _reduce_59(val, _values, result)
- result = IOMethodNode.new(val[0],val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 180)
- def _reduce_60(val, _values, result)
- result = IOMethodNode.new(val[0],val[2],{ pulse_time: val[4], pulse_units: val[6] })
- result
- end
-.,.,
-
-# reduce 61 omitted
-
-# reduce 62 omitted
-
-module_eval(<<'.,.,', 'tp_plus.y', 190)
- def _reduce_63(val, _values, result)
- result = JumpNode.new(val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 195)
- def _reduce_64(val, _values, result)
- result = ConditionalNode.new("if",val[1],val[2],val[3])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 197)
- def _reduce_65(val, _values, result)
- result = ConditionalNode.new("unless",val[1],val[2],val[3])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 202)
- def _reduce_66(val, _values, result)
- result = ForNode.new(val[1],val[4],val[6],val[8])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 206)
- def _reduce_67(val, _values, result)
- result = WhileNode.new(val[1],val[2])
- result
- end
-.,.,
-
-# reduce 68 omitted
-
-# reduce 69 omitted
-
-module_eval(<<'.,.,', 'tp_plus.y', 215)
- def _reduce_70(val, _values, result)
- result = NamespaceNode.new(val[1],val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 222)
- def _reduce_71(val, _values, result)
- result = CaseNode.new(val[1],val[3],val[4])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 226)
- def _reduce_72(val, _values, result)
- result = val
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 228)
- def _reduce_73(val, _values, result)
- result = val[0] << val[1] << val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 233)
- def _reduce_74(val, _values, result)
- result = CaseConditionNode.new(val[1],val[3])
- result
- end
-.,.,
-
-# reduce 75 omitted
-
-# reduce 76 omitted
-
-module_eval(<<'.,.,', 'tp_plus.y', 243)
- def _reduce_77(val, _values, result)
- result = CaseConditionNode.new(nil,val[2])
- result
- end
-.,.,
-
-# reduce 78 omitted
-
-# reduce 79 omitted
-
-# reduce 80 omitted
-
-# reduce 81 omitted
-
-module_eval(<<'.,.,', 'tp_plus.y', 254)
- def _reduce_82(val, _values, result)
- result = InlineConditionalNode.new(val[1], val[2], val[0])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 255)
- def _reduce_83(val, _values, result)
- result = InlineConditionalNode.new(val[1], val[2], val[0])
- result
- end
-.,.,
-
-# reduce 84 omitted
-
-# reduce 85 omitted
-
-# reduce 86 omitted
-
-# reduce 87 omitted
-
-module_eval(<<'.,.,', 'tp_plus.y', 266)
- def _reduce_88(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 267)
- def _reduce_89(val, _values, result)
- result = []
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 272)
- def _reduce_90(val, _values, result)
- result = MotionNode.new(val[0],val[5],val[7])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 276)
- def _reduce_91(val, _values, result)
- result = val
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 278)
- def _reduce_92(val, _values, result)
- result = val[0] << val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 283)
- def _reduce_93(val, _values, result)
- result = SpeedNode.new(val[4])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 285)
- def _reduce_94(val, _values, result)
- result = TerminationNode.new(val[4])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 287)
- def _reduce_95(val, _values, result)
- result = OffsetNode.new(val[2],val[4])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 289)
- def _reduce_96(val, _values, result)
- result = TimeNode.new(val[2],val[4],val[6])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 291)
- def _reduce_97(val, _values, result)
- result = SkipNode.new(val[4],val[5])
- result
- end
-.,.,
-
-# reduce 98 omitted
-
-# reduce 99 omitted
-
-module_eval(<<'.,.,', 'tp_plus.y', 298)
- def _reduce_100(val, _values, result)
- raise Racc::ParseError, sprintf("\ninvalid termination type: (%s)", val[1]) if val[1] != 1
-
- result = DigitNode.new(val[1].to_i * -1)
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 305)
- def _reduce_101(val, _values, result)
- result = val[1]
- result
- end
-.,.,
-
-# reduce 102 omitted
-
-# reduce 103 omitted
-
-# reduce 104 omitted
-
-# reduce 105 omitted
-
-# reduce 106 omitted
-
-# reduce 107 omitted
-
-# reduce 108 omitted
-
-module_eval(<<'.,.,', 'tp_plus.y', 325)
- def _reduce_109(val, _values, result)
- result = { speed: val[0], units: val[2] }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 326)
- def _reduce_110(val, _values, result)
- result = { speed: val[0], units: nil }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 330)
- def _reduce_111(val, _values, result)
- result = LabelDefinitionNode.new(val[0])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 334)
- def _reduce_112(val, _values, result)
- result = DefinitionNode.new(val[0],val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 338)
- def _reduce_113(val, _values, result)
- result = AssignmentNode.new(val[0],val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 339)
- def _reduce_114(val, _values, result)
- result = AssignmentNode.new(
- val[0],
- ExpressionNode.new(val[0],"+",val[3])
- )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 344)
- def _reduce_115(val, _values, result)
- result = AssignmentNode.new(
- val[0],
- ExpressionNode.new(val[0],"-",val[3])
- )
-
- result
- end
-.,.,
-
-# reduce 116 omitted
-
-# reduce 117 omitted
-
-module_eval(<<'.,.,', 'tp_plus.y', 357)
- def _reduce_118(val, _values, result)
- result = VarNode.new(val[0])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 358)
- def _reduce_119(val, _values, result)
- result = VarMethodNode.new(val[0],val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 363)
- def _reduce_120(val, _values, result)
- result = NamespacedVarNode.new(val[0],val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 367)
- def _reduce_121(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 369)
- def _reduce_122(val, _values, result)
- result = val[0].merge(val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 373)
- def _reduce_123(val, _values, result)
- result = { method: val[2] }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 375)
- def _reduce_124(val, _values, result)
- result = { group: val[4] }
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 379)
- def _reduce_125(val, _values, result)
- result = [val[0]]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 380)
- def _reduce_126(val, _values, result)
- result = val[0] << val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 384)
- def _reduce_127(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-# reduce 128 omitted
-
-# reduce 129 omitted
-
-module_eval(<<'.,.,', 'tp_plus.y', 394)
- def _reduce_130(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-# reduce 131 omitted
-
-module_eval(<<'.,.,', 'tp_plus.y', 396)
- def _reduce_132(val, _values, result)
- result = ExpressionNode.new(val[1], "!", nil)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 401)
- def _reduce_133(val, _values, result)
- result = ExpressionNode.new(val[0], val[1], val[2])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 405)
- def _reduce_134(val, _values, result)
- result = "=="
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 406)
- def _reduce_135(val, _values, result)
- result = "<>"
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 407)
- def _reduce_136(val, _values, result)
- result = "<"
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 408)
- def _reduce_137(val, _values, result)
- result = ">"
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 409)
- def _reduce_138(val, _values, result)
- result = ">="
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 410)
- def _reduce_139(val, _values, result)
- result = "<="
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 411)
- def _reduce_140(val, _values, result)
- result = "+"
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 412)
- def _reduce_141(val, _values, result)
- result = "-"
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 413)
- def _reduce_142(val, _values, result)
- result = "||"
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 414)
- def _reduce_143(val, _values, result)
- result = "*"
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 415)
- def _reduce_144(val, _values, result)
- result = "/"
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 416)
- def _reduce_145(val, _values, result)
- result = "DIV"
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 417)
- def _reduce_146(val, _values, result)
- result = "%"
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 418)
- def _reduce_147(val, _values, result)
- result = "&&"
- result
- end
-.,.,
-
-# reduce 148 omitted
-
-# reduce 149 omitted
-
-# reduce 150 omitted
-
-# reduce 151 omitted
-
-# reduce 152 omitted
-
-module_eval(<<'.,.,', 'tp_plus.y', 430)
- def _reduce_153(val, _values, result)
- result = ParenExpressionNode.new(val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 435)
- def _reduce_154(val, _values, result)
- result = IndirectNode.new(val[2].to_sym, val[4])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 440)
- def _reduce_155(val, _values, result)
- val[1] = val[1].to_i * -1 if val[0] == "-"
- result = DigitNode.new(val[1])
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 443)
- def _reduce_156(val, _values, result)
- val[1] = val[1].to_f * -1 if val[0] == "-"; result = RealNode.new(val[1])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 447)
- def _reduce_157(val, _values, result)
- result = "-"
- result
- end
-.,.,
-
-# reduce 158 omitted
-
-module_eval(<<'.,.,', 'tp_plus.y', 452)
- def _reduce_159(val, _values, result)
- result = RealNode.new(val[0])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 456)
- def _reduce_160(val, _values, result)
- result = DigitNode.new(val[0])
- result
- end
-.,.,
-
-# reduce 161 omitted
-
-# reduce 162 omitted
-
-# reduce 163 omitted
-
-# reduce 164 omitted
-
-# reduce 165 omitted
-
-# reduce 166 omitted
-
-# reduce 167 omitted
-
-# reduce 168 omitted
-
-# reduce 169 omitted
-
-# reduce 170 omitted
-
-# reduce 171 omitted
-
-# reduce 172 omitted
-
-module_eval(<<'.,.,', 'tp_plus.y', 476)
- def _reduce_173(val, _values, result)
- result = StringRegisterNode.new(val[2].to_i)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 480)
- def _reduce_174(val, _values, result)
- result = UserAlarmNode.new(val[2].to_i)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 484)
- def _reduce_175(val, _values, result)
- result = TimerNode.new(val[2].to_i)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 488)
- def _reduce_176(val, _values, result)
- result = ArgumentNode.new(val[2].to_i)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 492)
- def _reduce_177(val, _values, result)
- result = VisionRegisterNode.new(val[2].to_i)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 496)
- def _reduce_178(val, _values, result)
- result = PositionNode.new(val[2].to_i)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 500)
- def _reduce_179(val, _values, result)
- result = NumregNode.new(val[2].to_i)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 504)
- def _reduce_180(val, _values, result)
- result = PosregNode.new(val[2].to_i)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 508)
- def _reduce_181(val, _values, result)
- result = IONode.new(val[0], val[2].to_i)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 512)
- def _reduce_182(val, _values, result)
- result = IONode.new(val[0], val[2].to_i)
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 516)
- def _reduce_183(val, _values, result)
- result = AddressNode.new(val[0])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 520)
- def _reduce_184(val, _values, result)
- result = CommentNode.new(val[0])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 524)
- def _reduce_185(val, _values, result)
- result = TerminatorNode.new
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 525)
- def _reduce_186(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-# reduce 187 omitted
-
-# reduce 188 omitted
-
-module_eval(<<'.,.,', 'tp_plus.y', 532)
- def _reduce_189(val, _values, result)
- result = TerminatorNode.new
- result
- end
-.,.,
-
-# reduce 190 omitted
-
-module_eval(<<'.,.,', 'tp_plus.y', 538)
- def _reduce_191(val, _values, result)
- result = PositionDataNode.new(val[2])
- result
- end
-.,.,
-
-# reduce 192 omitted
-
-module_eval(<<'.,.,', 'tp_plus.y', 546)
- def _reduce_193(val, _values, result)
- result = val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 547)
- def _reduce_194(val, _values, result)
- result = {}
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 551)
- def _reduce_195(val, _values, result)
- result = val[0]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 553)
- def _reduce_196(val, _values, result)
- result = val[0].merge(val[3])
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 557)
- def _reduce_197(val, _values, result)
- result = { val[0].to_sym => val[2] }
- result
- end
-.,.,
-
-# reduce 198 omitted
-
-# reduce 199 omitted
-
-# reduce 200 omitted
-
-module_eval(<<'.,.,', 'tp_plus.y', 564)
- def _reduce_201(val, _values, result)
- val[1] = val[1].to_i * -1 if val[0] == "-"; result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 565)
- def _reduce_202(val, _values, result)
- val[1] = val[1].to_f * -1 if val[0] == "-"; result = val[1]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 566)
- def _reduce_203(val, _values, result)
- result = val[0] == "true"
- result
- end
-.,.,
-
-# reduce 204 omitted
-
-# reduce 205 omitted
-
-module_eval(<<'.,.,', 'tp_plus.y', 575)
- def _reduce_206(val, _values, result)
- result = val[2]
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 579)
- def _reduce_207(val, _values, result)
- result = val
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'tp_plus.y', 580)
- def _reduce_208(val, _values, result)
- result = val[0] << val[3]
- result
- end
-.,.,
-
-# reduce 209 omitted
-
-def _reduce_none(val, _values, result)
- val[0]
-end
-
- end # class Parser
-end # module TPPlus
diff --git a/test/racc/regress/twowaysql b/test/racc/regress/twowaysql
deleted file mode 100644
index 9bdd77237d..0000000000
--- a/test/racc/regress/twowaysql
+++ /dev/null
@@ -1,558 +0,0 @@
-#
-# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.5.2
-# from Racc grammar file "".
-#
-
-require 'racc/parser.rb'
-module TwoWaySQL
- class Parser < Racc::Parser
-
-module_eval(<<'...end twowaysql.y/module_eval...', 'twowaysql.y', 148)
-
-require 'strscan'
-
-def initialize(opts={})
- opts = {
- :debug => false,
- :preserve_space => true,
- :preserve_comment => false
- }.merge(opts)
- @yydebug = opts[:debug]
- @preserve_space = opts[:preserve_space]
- @preserve_comment = opts[:preserve_comment]
- @num_questions = 0
-end
-
-
-PAREN_EXAMPLE = '\([^\)]+\)'
-BEGIN_BIND_VARIABLE = '(\/|\#)\*([^\*]+)\*\1'
-BIND_VARIABLE_PATTERN = /\A#{BEGIN_BIND_VARIABLE}\s*/
-PAREN_BIND_VARIABLE_PATTERN = /\A#{BEGIN_BIND_VARIABLE}\s*#{PAREN_EXAMPLE}/
-EMBED_VARIABLE_PATTERN = /\A(\/|\#)\*\$([^\*]+)\*\1\s*/
-
-CONDITIONAL_PATTERN = /\A(\/|\#)\*(IF)\s+([^\*]+)\s*\*\1/
-BEGIN_END_PATTERN = /\A(\/|\#)\*(BEGIN|END)\s*\*\1/
-STRING_LITERAL_PATTERN = /\A(\'(?:[^\']+|\'\')*\')/ ## quoted string
-SPLIT_TOKEN_PATTERN = /\A(\S+?)(?=\s*(?:(?:\/|\#)\*|-{2,}|\(|\)|\,))/ ## stop on delimiters --,/*,#*,',',(,)
-LITERAL_PATTERN = /\A([^;\s]+)/
-SPACES_PATTERN = /\A(\s+)/
-QUESTION_PATTERN = /\A\?/
-COMMA_PATTERN = /\A\,/
-LPAREN_PATTERN = /\A\(/
-RPAREN_PATTERN = /\A\)/
-ACTUAL_COMMENT_PATTERN = /\A(\/|\#)\*(\s{1,}(?:.*?))\*\1/m ## start with spaces
-SEMICOLON_AT_INPUT_END_PATTERN = /\A\;\s*\Z/
-UNMATCHED_COMMENT_START_PATTERN = /\A(?:(?:\/|\#)\*)/
-
-#TODO: remove trailing spaces for S2Dao compatibility, but this spec sometimes causes SQL bugs...
-ELSE_PATTERN = /\A\-{2,}\s*ELSE\s*/
-AND_PATTERN = /\A(\ *AND)\b/i
-OR_PATTERN = /\A(\ *OR)\b/i
-
-
-def parse( io )
- @q = []
- io.each_line(nil) do |whole|
- @s = StringScanner.new(whole)
- end
- scan_str
-
- # @q.push [ false, nil ]
- @q.push [ false, [@s.pos, nil] ]
-
- ## call racc's private parse method
- do_parse
-end
-
-
-## called by racc
-def next_token
- @q.shift
-end
-
-
-def scan_str
- until @s.eos? do
- case
- when @s.scan(AND_PATTERN)
- @q.push [ :AND, [@s.pos, @s[1]] ]
- when @s.scan(OR_PATTERN)
- @q.push [ :OR, [@s.pos, @s[1]] ]
- when @s.scan(SPACES_PATTERN)
- @q.push [ :SPACES, [@s.pos, @s[1]] ]
- when @s.scan(QUESTION_PATTERN)
- @q.push [ :QUESTION, [@s.pos, nil] ]
- when @s.scan(COMMA_PATTERN)
- @q.push [ :COMMA, [@s.pos, ','] ]
- when @s.scan(LPAREN_PATTERN)
- @q.push [ :LPAREN, [@s.pos, '('] ]
- when @s.scan(RPAREN_PATTERN)
- @q.push [ :RPAREN, [@s.pos, ')'] ]
- when @s.scan(ELSE_PATTERN)
- @q.push [ :ELSE, [@s.pos, nil] ]
- when @s.scan(ACTUAL_COMMENT_PATTERN)
- @q.push [ :ACTUAL_COMMENT, [@s.pos, @s[1], @s[2]] ] if @preserve_comment
- when @s.scan(BEGIN_END_PATTERN)
- @q.push [ @s[2].intern, [@s.pos, nil] ]
- when @s.scan(CONDITIONAL_PATTERN)
- @q.push [ @s[2].intern, [@s.pos, @s[3]] ]
- when @s.scan(EMBED_VARIABLE_PATTERN)
- @q.push [ :EMBED_VARIABLE, [@s.pos, @s[2]] ]
- when @s.scan(PAREN_BIND_VARIABLE_PATTERN)
- @q.push [ :PAREN_BIND_VARIABLE, [@s.pos, @s[2]] ]
- when @s.scan(BIND_VARIABLE_PATTERN)
- @q.push [ :BIND_VARIABLE, [@s.pos, @s[2]] ]
- when @s.scan(STRING_LITERAL_PATTERN)
- @q.push [ :STRING_LITERAL, [@s.pos, @s[1]] ]
- when @s.scan(SPLIT_TOKEN_PATTERN)
- @q.push [ :IDENT, [@s.pos, @s[1]] ]
- when @s.scan(UNMATCHED_COMMENT_START_PATTERN) ## unmatched comment start, '/*','#*'
- raise Racc::ParseError, "unmatched comment. line:[#{line_no(@s.pos)}], str:[#{@s.rest}]"
- when @s.scan(LITERAL_PATTERN) ## other string token
- @q.push [ :IDENT, [@s.pos, @s[1]] ]
- when @s.scan(SEMICOLON_AT_INPUT_END_PATTERN)
- #drop semicolon at input end
- else
- raise Racc::ParseError, "syntax error at or near line:[#{line_no(@s.pos)}], str:[#{@s.rest}]"
- end
- end
-end
-
-
-## override racc's default on_error method
-def on_error(t, v, vstack)
- ## cursor in value-stack is an array of two items,
- ## that have position value as 0th item. like [731, "ctx[:limit] "]
- cursor = vstack.find do |tokens|
- tokens.size == 2 and tokens[0].kind_of?(Fixnum)
- end
- pos = cursor[0]
- line = line_no(pos)
- rest = @s.string[pos .. -1]
- raise Racc::ParseError, "syntax error at or near line:[#{line}], str:[#{rest}]"
-end
-
-
-def line_no(pos)
- lines = 0
- scanned = @s.string[0..(pos)]
- scanned.each_line { lines += 1 }
- lines
-end
-...end twowaysql.y/module_eval...
-##### State transition tables begin ###
-
-racc_action_table = [
- 8, 36, 9, 37, 12, 13, 10, 11, 14, 15,
- 16, 17, 18, 19, 22, 23, 24, 8, 38, 9,
- 3, 12, 13, 10, 11, 14, 15, 16, 17, 18,
- 19, 22, 23, 24, 8, 25, 9, 40, 12, 13,
- 10, 11, 14, 15, 16, 17, 18, 19, 22, 23,
- 24, 8, 45, 9, 46, 12, 13, 10, 11, 14,
- 15, 16, 17, 18, 19, 22, 23, 24, 8, nil,
- 9, nil, 12, 13, 10, 11, 14, 15, 16, 17,
- 18, 19, 22, 23, 24, 35, 33, 34, 31, 32,
- 44, 43, 31, 32 ]
-
-racc_action_check = [
- 2, 24, 2, 24, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 26, 26, 26,
- 1, 26, 26, 26, 26, 26, 26, 26, 26, 26,
- 26, 26, 26, 26, 27, 3, 27, 28, 27, 27,
- 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
- 27, 41, 37, 41, 39, 41, 41, 41, 41, 41,
- 41, 41, 41, 41, 41, 41, 41, 41, 42, nil,
- 42, nil, 42, 42, 42, 42, 42, 42, 42, 42,
- 42, 42, 42, 42, 42, 22, 22, 22, 9, 9,
- 34, 34, 40, 40 ]
-
-racc_action_pointer = [
- nil, 20, -2, 35, nil, nil, nil, nil, nil, 82,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 77, nil, -7, nil, 15, 32, 32, nil,
- nil, nil, nil, nil, 82, nil, nil, 44, nil, 51,
- 86, 49, 66, nil, nil, nil, nil, nil ]
-
-racc_action_default = [
- -2, -35, -1, -35, -3, -4, -5, -6, -2, -2,
- -16, -17, -18, -19, -20, -21, -22, -23, -24, -25,
- -26, -27, -35, -32, -35, 48, -35, -13, -10, -11,
- -12, -2, -2, -28, -35, -30, -33, -35, -7, -35,
- -2, -14, -15, -29, -31, -34, -8, -9 ]
-
-racc_goto_table = [
- 2, 1, 28, 39, nil, nil, nil, nil, 26, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 41, 42, 47 ]
-
-racc_goto_check = [
- 2, 1, 7, 8, nil, nil, nil, nil, 2, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 2, 2, 7 ]
-
-racc_goto_pointer = [
- nil, 1, 0, nil, nil, nil, nil, -7, -25, nil,
- nil, nil, nil ]
-
-racc_goto_default = [
- nil, nil, 27, 4, 5, 6, 7, nil, nil, 29,
- 30, 20, 21 ]
-
-racc_reduce_table = [
- 0, 0, :racc_error,
- 1, 20, :_reduce_1,
- 0, 21, :_reduce_2,
- 2, 21, :_reduce_3,
- 1, 22, :_reduce_none,
- 1, 22, :_reduce_none,
- 1, 22, :_reduce_none,
- 3, 25, :_reduce_7,
- 4, 24, :_reduce_8,
- 2, 27, :_reduce_9,
- 0, 27, :_reduce_10,
- 1, 26, :_reduce_none,
- 1, 26, :_reduce_none,
- 1, 26, :_reduce_none,
- 2, 28, :_reduce_14,
- 2, 29, :_reduce_15,
- 1, 23, :_reduce_16,
- 1, 23, :_reduce_17,
- 1, 23, :_reduce_18,
- 1, 23, :_reduce_19,
- 1, 23, :_reduce_20,
- 1, 23, :_reduce_21,
- 1, 23, :_reduce_22,
- 1, 23, :_reduce_23,
- 1, 23, :_reduce_24,
- 1, 23, :_reduce_25,
- 1, 23, :_reduce_none,
- 1, 23, :_reduce_none,
- 2, 30, :_reduce_28,
- 3, 30, :_reduce_29,
- 2, 30, :_reduce_30,
- 3, 30, :_reduce_31,
- 1, 30, :_reduce_32,
- 2, 31, :_reduce_33,
- 3, 31, :_reduce_34 ]
-
-racc_reduce_n = 35
-
-racc_shift_n = 48
-
-racc_token_table = {
- false => 0,
- :error => 1,
- :BEGIN => 2,
- :END => 3,
- :IF => 4,
- :ELSE => 5,
- :AND => 6,
- :OR => 7,
- :IDENT => 8,
- :STRING_LITERAL => 9,
- :SPACES => 10,
- :COMMA => 11,
- :LPAREN => 12,
- :RPAREN => 13,
- :QUESTION => 14,
- :ACTUAL_COMMENT => 15,
- :BIND_VARIABLE => 16,
- :PAREN_BIND_VARIABLE => 17,
- :EMBED_VARIABLE => 18 }
-
-racc_nt_base = 19
-
-racc_use_result_var = true
-
-Racc_arg = [
- racc_action_table,
- racc_action_check,
- racc_action_default,
- racc_action_pointer,
- racc_goto_table,
- racc_goto_check,
- racc_goto_default,
- racc_goto_pointer,
- racc_nt_base,
- racc_reduce_table,
- racc_token_table,
- racc_shift_n,
- racc_reduce_n,
- racc_use_result_var ]
-Ractor.make_shareable(Racc_arg) if defined?(Ractor)
-
-Racc_token_to_s_table = [
- "$end",
- "error",
- "BEGIN",
- "END",
- "IF",
- "ELSE",
- "AND",
- "OR",
- "IDENT",
- "STRING_LITERAL",
- "SPACES",
- "COMMA",
- "LPAREN",
- "RPAREN",
- "QUESTION",
- "ACTUAL_COMMENT",
- "BIND_VARIABLE",
- "PAREN_BIND_VARIABLE",
- "EMBED_VARIABLE",
- "$start",
- "sql",
- "stmt_list",
- "stmt",
- "primary",
- "if_stmt",
- "begin_stmt",
- "sub_stmt",
- "else_stmt",
- "and_stmt",
- "or_stmt",
- "bind_var",
- "embed_var" ]
-Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)
-
-Racc_debug_parser = false
-
-##### State transition tables end #####
-
-# reduce 0 omitted
-
-module_eval(<<'.,.,', 'twowaysql.y', 20)
- def _reduce_1(val, _values, result)
- result = RootNode.new( val[0] )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'twowaysql.y', 25)
- def _reduce_2(val, _values, result)
- result = []
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'twowaysql.y', 29)
- def _reduce_3(val, _values, result)
- result.push val[1]
-
- result
- end
-.,.,
-
-# reduce 4 omitted
-
-# reduce 5 omitted
-
-# reduce 6 omitted
-
-module_eval(<<'.,.,', 'twowaysql.y', 38)
- def _reduce_7(val, _values, result)
- result = BeginNode.new( val[1] )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'twowaysql.y', 43)
- def _reduce_8(val, _values, result)
- result = IfNode.new( val[0][1], val[1], val[2] )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'twowaysql.y', 48)
- def _reduce_9(val, _values, result)
- result = val[1]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'twowaysql.y', 52)
- def _reduce_10(val, _values, result)
- result = nil
-
- result
- end
-.,.,
-
-# reduce 11 omitted
-
-# reduce 12 omitted
-
-# reduce 13 omitted
-
-module_eval(<<'.,.,', 'twowaysql.y', 61)
- def _reduce_14(val, _values, result)
- result = SubStatementNode.new( val[0][1], val[1] )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'twowaysql.y', 66)
- def _reduce_15(val, _values, result)
- result = SubStatementNode.new( val[0][1], val[1] )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'twowaysql.y', 71)
- def _reduce_16(val, _values, result)
- result = LiteralNode.new( val[0][1] )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'twowaysql.y', 75)
- def _reduce_17(val, _values, result)
- result = LiteralNode.new( val[0][1] )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'twowaysql.y', 79)
- def _reduce_18(val, _values, result)
- result = LiteralNode.new( val[0][1] )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'twowaysql.y', 83)
- def _reduce_19(val, _values, result)
- result = LiteralNode.new( val[0][1] )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'twowaysql.y', 87)
- def _reduce_20(val, _values, result)
- result = WhiteSpaceNode.new( val[0][1], @preserve_space )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'twowaysql.y', 91)
- def _reduce_21(val, _values, result)
- result = LiteralNode.new( val[0][1] )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'twowaysql.y', 95)
- def _reduce_22(val, _values, result)
- result = LiteralNode.new( val[0][1] )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'twowaysql.y', 99)
- def _reduce_23(val, _values, result)
- result = LiteralNode.new( val[0][1] )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'twowaysql.y', 103)
- def _reduce_24(val, _values, result)
- @num_questions += 1
- result = QuestionNode.new( @num_questions )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'twowaysql.y', 108)
- def _reduce_25(val, _values, result)
- result = ActualCommentNode.new( val[0][1] , val[0][2] )
-
- result
- end
-.,.,
-
-# reduce 26 omitted
-
-# reduce 27 omitted
-
-module_eval(<<'.,.,', 'twowaysql.y', 115)
- def _reduce_28(val, _values, result)
- result = BindVariableNode.new( val[0][1] )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'twowaysql.y', 119)
- def _reduce_29(val, _values, result)
- result = BindVariableNode.new( val[0][1] )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'twowaysql.y', 123)
- def _reduce_30(val, _values, result)
- result = BindVariableNode.new( val[0][1] )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'twowaysql.y', 127)
- def _reduce_31(val, _values, result)
- result = BindVariableNode.new( val[0][1] )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'twowaysql.y', 131)
- def _reduce_32(val, _values, result)
- result = ParenBindVariableNode.new( val[0][1] )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'twowaysql.y', 136)
- def _reduce_33(val, _values, result)
- result = EmbedVariableNode.new( val[0][1] )
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'twowaysql.y', 140)
- def _reduce_34(val, _values, result)
- result = EmbedVariableNode.new( val[0][1] )
-
- result
- end
-.,.,
-
-def _reduce_none(val, _values, result)
- val[0]
-end
-
- end # class Parser
-end # module TwoWaySQL
diff --git a/test/racc/scandata/brace b/test/racc/scandata/brace
deleted file mode 100644
index f6c843853e..0000000000
--- a/test/racc/scandata/brace
+++ /dev/null
@@ -1,7 +0,0 @@
-{ {
- } { } {
- { { { } } }
- { { { {} } } }
- {} {} {}
- }
-}
diff --git a/test/racc/scandata/gvar b/test/racc/scandata/gvar
deleted file mode 100644
index 50528ce97b..0000000000
--- a/test/racc/scandata/gvar
+++ /dev/null
@@ -1 +0,0 @@
-{ $' $" $& $-a $/ $\ $( $1 $2 $3 $? $-i }
diff --git a/test/racc/scandata/normal b/test/racc/scandata/normal
deleted file mode 100644
index e705131536..0000000000
--- a/test/racc/scandata/normal
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- # comment
- result = "string".match(/regexp/)[0]
-}
diff --git a/test/racc/scandata/percent b/test/racc/scandata/percent
deleted file mode 100644
index fded9a385c..0000000000
--- a/test/racc/scandata/percent
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- 3 % 5 # mod
- 3%5 # mod
- 3% 5 # mod
- i % 5 # mod
- i%5 # mod
- i% 5 # mod
- call %{str} # string
- call(%{str}) # string
- %q{string} # string
- %Q{string} # string
- %r{string} # string
- %w(array) # array
- %x{array} # command string
- %{string} # string
- %_string_ # string
- %/string/ # regexp
-}
diff --git a/test/racc/scandata/slash b/test/racc/scandata/slash
deleted file mode 100644
index 190135b3bd..0000000000
--- a/test/racc/scandata/slash
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- # here's many '/'s
- i = 5/1 # div
- re = /regex/ # regexp
- i /= 5 # div
- result = 5 / 1 # div
- result = 5/ 1 # div
- call(/regex/) # regexp
- call /regex/ # regexp
-}
diff --git a/test/racc/src.intp b/test/racc/src.intp
deleted file mode 100644
index 4d2460e8ed..0000000000
--- a/test/racc/src.intp
+++ /dev/null
@@ -1,34 +0,0 @@
-def assert( no, cond )
- if cond then
- else
- raise( 'assert ' + to_s(no) + ' failed' )
- end
-end
-
-assert( 1, concat(concat(concat('str=', 'a'), "b"), 'c') == 'str=abc' )
-assert( 2, 'operator' + ' ok' == 'operator ok' )
-assert( 3, 1 + 1 == 2 )
-assert( 4, 4 * 1 + 10 * 1 == 14 )
-
-if true then
- assert( 5, true )
-else
- assert( 6, false )
-end
-
-i = 1
-while i == 1 do
- i = false
-end
-assert( 7, i == false )
-assert( 8, nil == nil )
-
-def func
- assert( 9, true )
-end
-func
-
-def argfunc( str )
- assert( 10, str == 'ok' )
-end
-argfunc 'ok'
diff --git a/test/racc/start.y b/test/racc/start.y
deleted file mode 100644
index 86296899b8..0000000000
--- a/test/racc/start.y
+++ /dev/null
@@ -1,20 +0,0 @@
-class S
-
-start st
-
-rule
-
-n: D { result = 'no' }
-st : A B C n { result = 'ok' }
-
-end
-
----- inner
-
- def parse
- do_parse
- end
-
----- footer
-
-S.new.parse == 'ok' or raise 'start stmt not worked'
diff --git a/test/racc/test_chk_y.rb b/test/racc/test_chk_y.rb
deleted file mode 100644
index 883737c45f..0000000000
--- a/test/racc/test_chk_y.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-require File.expand_path(File.join(__dir__, 'case'))
-
-module Racc
- class TestChkY < TestCase
- def setup
- super
- file = File.join(ASSET_DIR, 'chk.y')
- @debug_flags = Racc::DebugFlags.parse_option_string('o')
- parser = Racc::GrammarFileParser.new(@debug_flags)
- @result = parser.parse(File.read(file), File.basename(file))
- @states = Racc::States.new(@result.grammar).nfa
- @states.dfa
- end
-
- def test_compile_chk_y
- generator = Racc::ParserFileGenerator.new(@states, @result.params.dup)
-
- # it generates valid ruby
- assert Module.new {
- self.instance_eval(generator.generate_parser, __FILE__, __LINE__)
- }
-
- grammar = @states.grammar
-
- assert_equal 0, @states.n_srconflicts
- assert_equal 0, @states.n_rrconflicts
- assert_equal 0, grammar.n_useless_nonterminals
- assert_equal 0, grammar.n_useless_rules
- assert_nil grammar.n_expected_srconflicts
- end
-
- def test_compile_chk_y_line_convert
- params = @result.params.dup
- params.convert_line_all = true
-
- generator = Racc::ParserFileGenerator.new(@states, @result.params.dup)
-
- # it generates valid ruby
- assert Module.new {
- self.instance_eval(generator.generate_parser, __FILE__, __LINE__)
- }
-
- grammar = @states.grammar
-
- assert_equal 0, @states.n_srconflicts
- assert_equal 0, @states.n_rrconflicts
- assert_equal 0, grammar.n_useless_nonterminals
- assert_equal 0, grammar.n_useless_rules
- assert_nil grammar.n_expected_srconflicts
- end
- end
-end
diff --git a/test/racc/test_grammar_file_parser.rb b/test/racc/test_grammar_file_parser.rb
deleted file mode 100644
index 8f6e090cb2..0000000000
--- a/test/racc/test_grammar_file_parser.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require File.expand_path(File.join(__dir__, 'case'))
-
-module Racc
- class TestGrammarFileParser < TestCase
- def test_parse
- file = File.join(ASSET_DIR, 'yyerr.y')
-
- debug_flags = Racc::DebugFlags.parse_option_string('o')
- assert debug_flags.status_logging
-
- parser = Racc::GrammarFileParser.new(debug_flags)
- parser.parse(File.read(file), File.basename(file))
- end
- end
-end
diff --git a/test/racc/test_racc_command.rb b/test/racc/test_racc_command.rb
deleted file mode 100644
index 5bfebf150b..0000000000
--- a/test/racc/test_racc_command.rb
+++ /dev/null
@@ -1,339 +0,0 @@
-require File.expand_path(File.join(__dir__, 'case'))
-
-module Racc
- class TestRaccCommand < TestCase
- def test_syntax_y
- assert_compile 'syntax.y', '-v'
- assert_debugfile 'syntax.y', [0,0,0,0,0]
- end
-
- def test_percent_y
- assert_compile 'percent.y'
- assert_debugfile 'percent.y', []
- assert_exec 'percent.y'
- end
-
- def test_scan_y
- assert_compile 'scan.y'
- assert_debugfile 'scan.y', []
- assert_exec 'scan.y'
- end
-
- def test_newsyn_y
- assert_compile 'newsyn.y'
- assert_debugfile 'newsyn.y', []
- end
-
- def test_normal_y
- assert_compile 'normal.y'
- assert_debugfile 'normal.y', []
-
- assert_compile 'normal.y', '-vg'
- assert_debugfile 'normal.y', []
- end
-
- def test_chk_y
- assert_compile 'chk.y', '-vg'
- assert_debugfile 'chk.y', []
- assert_exec 'chk.y'
-
- assert_compile 'chk.y', '--line-convert-all'
- assert_debugfile 'chk.y', []
- assert_exec 'chk.y'
- end
-
- def test_echk_y
- assert_compile 'echk.y', '-E'
- assert_debugfile 'echk.y', []
- assert_exec 'echk.y'
- end
-
- def test_err_y
- assert_compile 'err.y'
- assert_debugfile 'err.y', []
- assert_exec 'err.y'
- end
-
- def test_mailp_y
- assert_compile 'mailp.y'
- assert_debugfile 'mailp.y', []
- end
-
- def test_conf_y
- assert_compile 'conf.y', '-v'
- assert_debugfile 'conf.y', [4,1,1,2]
- end
-
- def test_rrconf_y
- assert_compile 'rrconf.y'
- assert_debugfile 'rrconf.y', [1,1,0,0]
- end
-
- def test_useless_y
- assert_compile 'useless.y'
- assert_debugfile 'useless.y', [0,0,1,2]
- end
-
- def test_opt_y
- assert_compile 'opt.y'
- assert_debugfile 'opt.y', []
- assert_exec 'opt.y'
- end
-
- def test_yyerr_y
- assert_compile 'yyerr.y'
- assert_debugfile 'yyerr.y', []
- assert_exec 'yyerr.y'
- end
-
- def test_recv_y
- assert_compile 'recv.y'
- assert_debugfile 'recv.y', [5,10,1,4]
- end
-
- def test_ichk_y
- assert_compile 'ichk.y'
- assert_debugfile 'ichk.y', []
- assert_exec 'ichk.y'
- end
-
- def test_intp_y
- assert_compile 'intp.y'
- assert_debugfile 'intp.y', []
- assert_exec 'intp.y'
- end
-
- def test_expect_y
- assert_compile 'expect.y'
- assert_debugfile 'expect.y', [1,0,0,0,1]
- end
-
- def test_nullbug1_y
- assert_compile 'nullbug1.y'
- assert_debugfile 'nullbug1.y', [0,0,0,0]
- end
-
- def test_nullbug2_y
- assert_compile 'nullbug2.y'
- assert_debugfile 'nullbug2.y', [0,0,0,0]
- end
-
- def test_firstline_y
- assert_compile 'firstline.y'
- assert_debugfile 'firstline.y', []
- end
-
- def test_nonass_y
- assert_compile 'nonass.y'
- assert_debugfile 'nonass.y', []
- assert_exec 'nonass.y'
- end
-
- def test_digraph_y
- assert_compile 'digraph.y'
- assert_debugfile 'digraph.y', []
- assert_exec 'digraph.y'
- end
-
- def test_noend_y
- assert_compile 'noend.y'
- assert_debugfile 'noend.y', []
- end
-
- def test_norule_y
- assert_raise(Test::Unit::AssertionFailedError) {
- assert_compile 'norule.y'
- }
- end
-
- def test_unterm_y
- assert_raise(Test::Unit::AssertionFailedError) {
- assert_compile 'unterm.y'
- }
- end
-
- # Regression test for a problem where error recovery at EOF would cause
- # a Racc-generated parser to go into an infinite loop (on some grammars)
- def test_error_recovery_y
- assert_compile 'error_recovery.y'
- Timeout.timeout(10) do
- assert_exec 'error_recovery.y'
- end
- end
-
- # .y files from `parser` gem
-
- def test_ruby18
- assert_compile 'ruby18.y', [], timeout: 60
- assert_debugfile 'ruby18.y', []
- assert_output_unchanged 'ruby18.y'
- end
-
- def test_ruby22
- assert_compile 'ruby22.y', [], timeout: 60
- assert_debugfile 'ruby22.y', []
- assert_output_unchanged 'ruby22.y'
- end
-
- # .y file from csspool gem
-
- def test_csspool
- assert_compile 'csspool.y'
- assert_debugfile 'csspool.y', [5, 3]
- assert_output_unchanged 'csspool.y'
- end
-
- # .y file from opal gem
-
- def test_opal
- assert_compile 'opal.y', [], timeout: 60
- assert_debugfile 'opal.y', []
- assert_output_unchanged 'opal.y'
- end
-
- # .y file from journey gem
-
- def test_journey
- assert_compile 'journey.y'
- assert_debugfile 'journey.y', []
- assert_output_unchanged 'journey.y'
- end
-
- # .y file from nokogiri gem
-
- def test_nokogiri_css
- assert_compile 'nokogiri-css.y'
- assert_debugfile 'nokogiri-css.y', [0, 1]
- assert_output_unchanged 'nokogiri-css.y'
- end
-
- # .y file from edtf-ruby gem
-
- def test_edtf
- assert_compile 'edtf.y'
- assert_debugfile 'edtf.y', [0, 0, 0, 0, 0]
- assert_output_unchanged 'edtf.y'
- end
-
- # .y file from namae gem
-
- def test_namae
- assert_compile 'namae.y'
- assert_debugfile 'namae.y', [0, 0, 0, 0, 0]
- assert_output_unchanged 'namae.y'
- end
-
- # .y file from liquor gem
-
- def test_liquor
- assert_compile 'liquor.y'
- assert_debugfile 'liquor.y', [0, 0, 0, 0, 15]
- assert_output_unchanged 'liquor.y'
- end
-
- # .y file from nasl gem
-
- def test_nasl
- assert_compile 'nasl.y'
- assert_debugfile 'nasl.y', [0, 0, 0, 0, 1]
- assert_output_unchanged 'nasl.y'
- end
-
- # .y file from riml gem
-
- def test_riml
- assert_compile 'riml.y'
- assert_debugfile 'riml.y', [289, 0, 0, 0]
- assert_output_unchanged 'riml.y'
- end
-
- # .y file from ruby-php-serialization gem
-
- def test_php_serialization
- assert_compile 'php_serialization.y'
- assert_debugfile 'php_serialization.y', [0, 0, 0, 0]
- assert_output_unchanged 'php_serialization.y'
- end
-
- # .y file from huia language implementation
-
- def test_huia
- assert_compile 'huia.y'
- assert_debugfile 'huia.y', [285, 0, 0, 0]
- assert_output_unchanged 'huia.y'
- end
-
- # .y file from cast gem
-
- def test_cast
- assert_compile 'cast.y'
- assert_debugfile 'cast.y', [0, 0, 0, 0, 1]
- assert_output_unchanged 'cast.y'
- end
-
- # .y file from cadenza gem
-
- def test_cadenza
- assert_compile 'cadenza.y'
- assert_debugfile 'cadenza.y', [0, 0, 0, 0, 37]
- assert_output_unchanged 'cadenza.y'
- end
-
- # .y file from mediacloth gem
-
- def test_mediacloth
- assert_compile 'mediacloth.y'
- assert_debugfile 'mediacloth.y', [0, 0, 0, 0]
- assert_output_unchanged 'mediacloth.y'
- end
-
- # .y file from twowaysql gem
-
- def test_twowaysql
- assert_compile 'twowaysql.y'
- assert_debugfile 'twowaysql.y', [4, 0, 0, 0]
- assert_output_unchanged 'twowaysql.y'
- end
-
- # .y file from machete gem
-
- def test_machete
- assert_compile 'machete.y'
- assert_debugfile 'machete.y', [0, 0, 0, 0]
- assert_output_unchanged 'machete.y'
- end
-
- # .y file from mof gem
-
- def test_mof
- assert_compile 'mof.y'
- assert_debugfile 'mof.y', [7, 4, 0, 0]
- assert_output_unchanged 'mof.y'
- end
-
- # .y file from tp_plus gem
-
- def test_tp_plus
- assert_compile 'tp_plus.y'
- assert_debugfile 'tp_plus.y', [21, 0, 0, 0]
- assert_output_unchanged 'tp_plus.y'
- end
-
- def test_ifelse
- omit if RUBY_PLATFORM =~ /java/
-
- stderr = nil
- racc "-o#{@TAB_DIR}/ifelse", "#{ASSET_DIR}/ifelse.y", stdout_filter: ->(s) { stderr = s }
- stderr = stderr.lines[1..-1].join if RUBY_PLATFORM.match?(/java/)
- assert_equal(<<~STDERR, stderr)
- 1 useless nonterminals:
- dummy
- 2 useless rules:
- #4 (dummy)
- #5 (dummy)
- 1 shift/reduce conflicts
- Turn on logging with "-v" and check ".output" file for details
- STDERR
- end
- end
-end
diff --git a/test/racc/test_scan_y.rb b/test/racc/test_scan_y.rb
deleted file mode 100644
index fcd7e53c99..0000000000
--- a/test/racc/test_scan_y.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-require File.expand_path(File.join(__dir__, 'case'))
-
-module Racc
- class TestScanY < TestCase
- def setup
- super
- file = File.join(ASSET_DIR, 'scan.y')
- @debug_flags = Racc::DebugFlags.parse_option_string('o')
- parser = Racc::GrammarFileParser.new(@debug_flags)
- @result = parser.parse(File.read(file), File.basename(file))
- @states = Racc::States.new(@result.grammar).nfa
- @states.dfa
- end
-
- def test_compile
- generator = Racc::ParserFileGenerator.new(@states, @result.params.dup)
-
- # it generates valid ruby
- assert Module.new {
- self.class_eval(generator.generate_parser)
- }
-
- grammar = @states.grammar
-
- assert_equal 0, @states.n_srconflicts
- assert_equal 0, @states.n_rrconflicts
- assert_equal 0, grammar.n_useless_nonterminals
- assert_equal 0, grammar.n_useless_rules
- assert_nil grammar.n_expected_srconflicts
- end
-
- def test_compile_line_convert
- params = @result.params.dup
- params.convert_line_all = true
-
- generator = Racc::ParserFileGenerator.new(@states, @result.params.dup)
-
- # it generates valid ruby
- assert Module.new {
- self.class_eval(generator.generate_parser)
- }
-
- grammar = @states.grammar
-
- assert_equal 0, @states.n_srconflicts
- assert_equal 0, @states.n_rrconflicts
- assert_equal 0, grammar.n_useless_nonterminals
- assert_equal 0, grammar.n_useless_rules
- assert_nil grammar.n_expected_srconflicts
- end
- end
-end
diff --git a/test/racc/testscanner.rb b/test/racc/testscanner.rb
deleted file mode 100644
index d7877511ec..0000000000
--- a/test/racc/testscanner.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-#
-# racc scanner tester
-#
-
-require 'racc/raccs'
-
-
-class ScanError < StandardError; end
-
-def testdata( dir, argv )
- if argv.empty? then
- Dir.glob( dir + '/*' ) -
- Dir.glob( dir + '/*.swp' ) -
- [ dir + '/CVS' ]
- else
- argv.collect {|i| dir + '/' + i }
- end
-end
-
-
-if ARGV.delete '--print' then
- $raccs_print_type = true
- printonly = true
-else
- printonly = false
-end
-
-testdata( File.dirname($0) + '/scandata', ARGV ).each do |file|
- $stderr.print File.basename(file) + ': '
- begin
- ok = File.read(file)
- s = Racc::GrammarFileScanner.new( ok )
- sym, (val, _lineno) = s.scan
- if printonly then
- $stderr.puts
- $stderr.puts val
- next
- end
-
- val = '{' + val + "}\n"
- sym == :ACTION or raise ScanError, 'is not action!'
- val == ok or raise ScanError, "\n>>>\n#{ok}----\n#{val}<<<"
-
- $stderr.puts 'ok'
- rescue => err
- $stderr.puts 'fail (' + err.type.to_s + ')'
- $stderr.puts err.message
- $stderr.puts err.backtrace
- $stderr.puts
- end
-end
diff --git a/test/rdoc/test_rdoc_any_method.rb b/test/rdoc/test_rdoc_any_method.rb
index 6915b466f0..b11c15420c 100644
--- a/test/rdoc/test_rdoc_any_method.rb
+++ b/test/rdoc/test_rdoc_any_method.rb
@@ -69,6 +69,20 @@ each_line(foo)
assert_equal 'C1::m', @c1.method_list.first.full_name
end
+ def test_has_call_seq?
+ m = RDoc::AnyMethod.new nil, "each_line"
+ m2 = RDoc::AnyMethod.new nil, "each"
+ assert_equal false, m.has_call_seq?
+ m.call_seq = "each_line()"
+ assert_equal true, m.has_call_seq?
+
+ m = RDoc::AnyMethod.new nil, "each_line"
+ m.is_alias_for = m2
+ assert_equal false, m.has_call_seq?
+ m2.call_seq = "each_line()"
+ assert_equal true, m.has_call_seq?
+ end
+
def test_is_alias_for
assert_equal @c2_b, @c2_a.is_alias_for
@@ -515,6 +529,30 @@ method(a, b) { |c, d| ... }
assert_equal 'C1', @c1.method_list.last.parent_name
end
+ def test_skip_description?
+ m = RDoc::AnyMethod.new nil, "each_line"
+ m2 = RDoc::AnyMethod.new nil, "each"
+ assert_equal false, m.skip_description?
+ assert_equal false, m2.skip_description?
+
+ m.is_alias_for = m2
+ m2.aliases << m
+ assert_equal false, m.skip_description?
+ assert_equal false, m2.skip_description?
+
+ m2.call_seq = "each()"
+ assert_equal true, m.skip_description?
+ assert_equal false, m2.skip_description?
+
+ m2.call_seq = "each_line()"
+ assert_equal false, m.skip_description?
+ assert_equal true, m2.skip_description?
+
+ m2.call_seq = "each()\neach_line()"
+ assert_equal false, m.skip_description?
+ assert_equal false, m2.skip_description?
+ end
+
def test_store_equals
loaded = Marshal.load Marshal.dump(@c1.method_list.last)
diff --git a/test/rdoc/test_rdoc_context.rb b/test/rdoc/test_rdoc_context.rb
index 85665599fb..c4de04e083 100644
--- a/test/rdoc/test_rdoc_context.rb
+++ b/test/rdoc/test_rdoc_context.rb
@@ -927,6 +927,12 @@ class TestRDocContext < XrefTestCase
assert_equal :private, @c6.find_method_named('priv6').visibility
assert_equal :protected, @c6.find_method_named('prot6').visibility
assert_equal :public, @c6.find_method_named('pub6').visibility
+ assert_equal :public, @c6.find_method_named('s_pub1').visibility
+ assert_equal :public, @c6.find_method_named('s_pub2').visibility
+ assert_equal :public, @c6.find_method_named('s_pub3').visibility
+ assert_equal :public, @c6.find_method_named('s_pub4').visibility
+ assert_equal :private, @c6.find_method_named('s_priv1').visibility
+ assert_equal :protected, @c6.find_method_named('s_prot1').visibility
end
def util_visibilities
diff --git a/test/rdoc/test_rdoc_generator_json_index.rb b/test/rdoc/test_rdoc_generator_json_index.rb
index 6b69337b45..62d1ccec95 100644
--- a/test/rdoc/test_rdoc_generator_json_index.rb
+++ b/test/rdoc/test_rdoc_generator_json_index.rb
@@ -104,8 +104,20 @@ class TestRDocGeneratorJsonIndex < RDoc::TestCase
orig_file = Pathname(File.join srcdir, 'generator/template/json_index/js/navigation.js')
generated_file = Pathname(File.join @tmpdir, 'js/navigation.js')
+ # The following assertion for the generated file's modified time randomly
+ # fails in a ppc64le environment.
+ # https://github.com/ruby/rdoc/issues/1048
+ if orig_file.mtime.inspect != generated_file.mtime.inspect &&
+ RUBY_PLATFORM =~ /powerpc64le/
+ pend <<~EOC
+ Unstable test in ppc64le.
+ <#{orig_file.mtime.inspect}> expected but was
+ <#{generated_file.mtime.inspect}>.
+ EOC
+ end
+
# This is dirty hack on JRuby
- assert orig_file.mtime.inspect == generated_file.mtime.inspect,
+ assert_equal orig_file.mtime.inspect, generated_file.mtime.inspect,
'.js files should be the same timestamp of original'
json = File.read 'js/search_index.js'
diff --git a/test/rdoc/test_rdoc_markdown.rb b/test/rdoc/test_rdoc_markdown.rb
index dd6f312fa9..31d5b068f9 100644
--- a/test/rdoc/test_rdoc_markdown.rb
+++ b/test/rdoc/test_rdoc_markdown.rb
@@ -305,6 +305,25 @@ that also extends to two lines
assert_equal expected, doc
end
+ def test_parse_definition_list_rich_label
+ doc = parse <<-MD
+`one`
+: This is a definition
+
+**two**
+: This is another definition
+ MD
+
+ expected = doc(
+ list(:NOTE,
+ item(%w[<code>one</code>],
+ para("This is a definition")),
+ item(%w[*two*],
+ para("This is another definition"))))
+
+ assert_equal expected, doc
+ end
+
def test_parse_definition_list_no
@parser.definition_lists = false
diff --git a/test/rdoc/test_rdoc_markup_formatter.rb b/test/rdoc/test_rdoc_markup_formatter.rb
index 8702db379d..af19d832ca 100644
--- a/test/rdoc/test_rdoc_markup_formatter.rb
+++ b/test/rdoc/test_rdoc_markup_formatter.rb
@@ -104,6 +104,12 @@ class TestRDocMarkupFormatter < RDoc::TestCase
formatted = document.accept @to
assert_equal '<{foo}[rdoc-label:bar]>.', formatted
+
+ document = doc(para('<tt>{abc}</tt>: {foo}[rdoc-label:bar].'))
+
+ formatted = document.accept @to
+
+ assert_equal '<code>{abc}</code>: <{foo}[rdoc-label:bar]>.', formatted
end
def test_parse_url
diff --git a/test/rdoc/test_rdoc_markup_to_html.rb b/test/rdoc/test_rdoc_markup_to_html.rb
index 935a7d70c5..e3affa533c 100644
--- a/test/rdoc/test_rdoc_markup_to_html.rb
+++ b/test/rdoc/test_rdoc_markup_to_html.rb
@@ -391,11 +391,43 @@ class TestRDocMarkupToHtml < RDoc::Markup::FormatterTestCase
end
def test_accept_paragraph_newline
+ hellos = ["hello", "\u{393 3b5 3b9 3ac} \u{3c3 3bf 3c5}"]
+ worlds = ["world", "\u{3ba 3cc 3c3 3bc 3bf 3c2}"]
+ ohayo, sekai = %W"\u{304a 306f 3088 3046} \u{4e16 754c}"
+
+ hellos.product(worlds) do |hello, world|
+ @to.start_accepting
+ @to.accept_paragraph para("#{hello}\n", "#{world}\n")
+ assert_equal "\n<p>#{hello} #{world}</p>\n", @to.res.join
+ end
+
+ hellos.each do |hello|
+ @to.start_accepting
+ @to.accept_paragraph para("#{hello}\n", "#{sekai}\n")
+ assert_equal "\n<p>#{hello}#{sekai}</p>\n", @to.res.join
+ end
+
+ worlds.each do |world|
+ @to.start_accepting
+ @to.accept_paragraph para("#{ohayo}\n", "#{world}\n")
+ assert_equal "\n<p>#{ohayo}#{world}</p>\n", @to.res.join
+ end
+
@to.start_accepting
+ @to.accept_paragraph para("#{ohayo}\n", "#{sekai}\n")
+ assert_equal "\n<p>#{ohayo}#{sekai}</p>\n", @to.res.join
- @to.accept_paragraph para("hello\n", "world\n")
+ @to.start_accepting
+ @to.accept_paragraph para("+hello+\n", "world\n")
+ assert_equal "\n<p><code>hello</code> world</p>\n", @to.res.join
- assert_equal "\n<p>hello world </p>\n", @to.res.join
+ @to.start_accepting
+ @to.accept_paragraph para("hello\n", "+world+\n")
+ assert_equal "\n<p>hello <code>world</code></p>\n", @to.res.join
+
+ @to.start_accepting
+ @to.accept_paragraph para("+hello+\n", "+world+\n")
+ assert_equal "\n<p><code>hello</code> <code>world</code></p>\n", @to.res.join
end
def test_accept_heading_output_decoration
@@ -602,9 +634,9 @@ end
end
def test_accept_verbatim_redefinable_operators
- functions = %w[| ^ & <=> == === =~ > >= < <= << >> + - * / % ** ~ +@ -@ [] []= ` ! != !~].map { |redefinable_op|
+ functions = %w[| ^ & <=> == === =~ > >= < <= << >> + - * / % ** ~ +@ -@ [] []= ` ! != !~].flat_map { |redefinable_op|
["def #{redefinable_op}\n", "end\n"]
- }.flatten
+ }
verb = @RM::Verbatim.new(*functions)
diff --git a/test/rdoc/test_rdoc_markup_to_html_crossref.rb b/test/rdoc/test_rdoc_markup_to_html_crossref.rb
index 4b87dd5da5..dc4488195a 100644
--- a/test/rdoc/test_rdoc_markup_to_html_crossref.rb
+++ b/test/rdoc/test_rdoc_markup_to_html_crossref.rb
@@ -133,6 +133,18 @@ class TestRDocMarkupToHtmlCrossref < XrefTestCase
'rdoc-ref:C1@foo'
end
+ def test_convert_RDOCLINK_rdoc_ref_label_in_current_file
+ result = @to.convert 'rdoc-ref:@foo'
+
+ assert_equal para("<a href=\"#label-foo\">foo</a>"), result,
+ 'rdoc-ref:@foo'
+
+ result = @to.convert '{Foo}[rdoc-ref:@foo]'
+
+ assert_equal para("<a href=\"#label-foo\">Foo</a>"), result,
+ '{Foo}[rdoc-ref:@foo]'
+ end
+
def test_gen_url
assert_equal '<a href="C1.html">Some class</a>',
@to.gen_url('rdoc-ref:C1', 'Some class')
diff --git a/test/rdoc/test_rdoc_markup_to_markdown.rb b/test/rdoc/test_rdoc_markup_to_markdown.rb
index ff47364faa..92ed37bc50 100644
--- a/test/rdoc/test_rdoc_markup_to_markdown.rb
+++ b/test/rdoc/test_rdoc_markup_to_markdown.rb
@@ -69,7 +69,7 @@ class TestRDocMarkupToMarkdown < RDoc::Markup::TextFormatterTestCase
end
def accept_list_item_end_label
- assert_equal "cat\n: ", @to.res.join
+ assert_equal "cat\n: \n", @to.res.join
assert_equal 0, @to.indent, 'indent'
end
@@ -79,7 +79,7 @@ class TestRDocMarkupToMarkdown < RDoc::Markup::TextFormatterTestCase
end
def accept_list_item_end_note
- assert_equal "cat\n: ", @to.res.join
+ assert_equal "cat\n: \n", @to.res.join
assert_equal 0, @to.indent, 'indent'
end
@@ -319,9 +319,7 @@ words words words words
expected = <<-EXPECTED
* l1
* l1.1
-
* l2
-
EXPECTED
assert_equal expected, @to.end_accepting
@@ -343,7 +341,6 @@ words words words words
* second
-
EXPECTED
assert_equal expected, @to.end_accepting
diff --git a/test/rdoc/test_rdoc_markup_to_rdoc.rb b/test/rdoc/test_rdoc_markup_to_rdoc.rb
index bb30ee5226..50f4b6dc8b 100644
--- a/test/rdoc/test_rdoc_markup_to_rdoc.rb
+++ b/test/rdoc/test_rdoc_markup_to_rdoc.rb
@@ -69,7 +69,7 @@ class TestRDocMarkupToRDoc < RDoc::Markup::TextFormatterTestCase
end
def accept_list_item_end_label
- assert_equal "cat:\n", @to.res.join
+ assert_equal "[cat]\n", @to.res.join
assert_equal 0, @to.indent, 'indent'
end
@@ -79,7 +79,7 @@ class TestRDocMarkupToRDoc < RDoc::Markup::TextFormatterTestCase
end
def accept_list_item_end_note
- assert_equal "cat:\n", @to.res.join
+ assert_equal "cat::\n", @to.res.join
assert_equal 0, @to.indent, 'indent'
end
@@ -100,7 +100,7 @@ class TestRDocMarkupToRDoc < RDoc::Markup::TextFormatterTestCase
def accept_list_item_start_label
assert_equal [""], @to.res
- assert_equal "cat:\n ", @to.prefix
+ assert_equal "[cat]\n ", @to.prefix
assert_equal 2, @to.indent
end
@@ -115,7 +115,7 @@ class TestRDocMarkupToRDoc < RDoc::Markup::TextFormatterTestCase
def accept_list_item_start_note
assert_equal [""], @to.res
- assert_equal "cat:\n ", @to.prefix
+ assert_equal "cat::\n ", @to.prefix
assert_equal 2, @to.indent
end
@@ -243,16 +243,16 @@ class TestRDocMarkupToRDoc < RDoc::Markup::TextFormatterTestCase
end
def accept_list_item_start_note_2
- assert_equal "<tt>teletype</tt>:\n teletype description\n\n", @to.res.join
+ assert_equal "<tt>teletype</tt>::\n teletype description\n\n", @to.res.join
end
def accept_list_item_start_note_multi_description
- assert_equal "label:\n description one\n\n description two\n\n",
+ assert_equal "label::\n description one\n\n description two\n\n",
@to.res.join
end
def accept_list_item_start_note_multi_label
- assert_equal "one\ntwo:\n two headers\n\n", @to.res.join
+ assert_equal "one::\ntwo::\n two headers\n\n", @to.res.join
end
def accept_paragraph_b
@@ -355,8 +355,8 @@ bar ::
NOTE_LIST
expected = <<-EXPECTED
-foo
-bar:
+foo::
+bar::
hi
EXPECTED
diff --git a/test/rdoc/test_rdoc_options.rb b/test/rdoc/test_rdoc_options.rb
index 443d595ca6..5e8b1ae3d8 100644
--- a/test/rdoc/test_rdoc_options.rb
+++ b/test/rdoc/test_rdoc_options.rb
@@ -68,7 +68,6 @@ class TestRDocOptions < RDoc::TestCase
'exclude' => %w[~\z \.orig\z \.rej\z \.bak\z \.gemspec\z],
'hyperlink_all' => false,
'line_numbers' => false,
- 'locale' => nil,
'locale_dir' => 'locale',
'locale_name' => nil,
'main_page' => nil,
@@ -720,6 +719,28 @@ rdoc_include:
assert_empty err
end
+ def test_parse_locale_name_default
+ temp_dir do
+ @options.parse %w[]
+ assert_equal 'locale', @options.instance_variable_get(:@locale_dir)
+ assert_nil @options.instance_variable_get(:@locale_name)
+ assert_nil @options.locale
+ @options.finish
+ assert_nil @options.locale
+ end
+ end
+
+ def test_parse_locale_name
+ temp_dir do
+ @options.parse %w[--locale fr]
+ assert_equal 'locale', @options.instance_variable_get(:@locale_dir)
+ assert_equal 'fr', @options.instance_variable_get(:@locale_name)
+ assert_nil @options.locale
+ @options.finish
+ assert_equal 'fr', @options.locale.name
+ end
+ end
+
def test_setup_generator
test_generator = Class.new do
def self.setup_options op
diff --git a/test/rdoc/test_rdoc_parser.rb b/test/rdoc/test_rdoc_parser.rb
index 7cc3c2d926..fa6443f3d3 100644
--- a/test/rdoc/test_rdoc_parser.rb
+++ b/test/rdoc/test_rdoc_parser.rb
@@ -151,6 +151,20 @@ class TestRDocParser < RDoc::TestCase
File.unlink readme_ext
end
+ def test_can_parse_modeline_c
+ readme_inc = File.join Dir.tmpdir, "README.inc.#{$$}"
+
+ File.open readme_inc, 'w' do |io|
+ io.puts "/* README.inc - -*- c -*- created at: Mon Aug 7 16:45:54 JST 1995 */"
+ io.puts
+ io.puts "/* This document explains how to make extension libraries for Ruby. */"
+ end
+
+ assert_equal RDoc::Parser::C, @RP.can_parse(readme_inc)
+ ensure
+ File.unlink readme_inc
+ end
+
##
# Selenium hides a .jar file using a .txt extension.
diff --git a/test/rdoc/test_rdoc_parser_c.rb b/test/rdoc/test_rdoc_parser_c.rb
index ed3a18e45f..ab4f149869 100644
--- a/test/rdoc/test_rdoc_parser_c.rb
+++ b/test/rdoc/test_rdoc_parser_c.rb
@@ -460,7 +460,7 @@ VALUE mFoo = rb_define_module_under(rb_mKernel, "Foo");
end
def test_do_constants
- content = <<-EOF
+ content = <<-'EOF'
#include <ruby.h>
void Init_foo(){
@@ -475,6 +475,9 @@ void Init_foo(){
/* TEST\:TEST: Checking to see if escaped colon works */
rb_define_const(cFoo, "TEST", rb_str_new2("TEST:TEST"));
+ /* TEST: TEST:Checking to see if only word-ending colon works */
+ rb_define_const(cFoo, "TEST2", rb_str_new2("TEST:TEST"));
+
/* \\: The file separator on MS Windows */
rb_define_const(cFoo, "MSEPARATOR", rb_str_new2("\\"));
@@ -538,6 +541,9 @@ void Init_foo(){
assert_equal ['TEST', 'TEST:TEST',
'Checking to see if escaped colon works '],
constants.shift
+ assert_equal ['TEST2', 'TEST',
+ 'TEST:Checking to see if only word-ending colon works '],
+ constants.shift
assert_equal ['MSEPARATOR', '\\',
'The file separator on MS Windows '],
constants.shift
@@ -577,8 +583,6 @@ void Init_curses(){
mCurses = rb_define_module("Curses");
/*
- * Document-const: Curses::COLOR_BLACK
- *
* Value of the color black
*/
rb_curses_define_const(COLOR_BLACK);
@@ -603,8 +607,7 @@ void Init_curses(){
def test_do_constants_file
content = <<-EOF
void Init_File(void) {
- /* Document-const: LOCK_SH
- *
+ /*
* Shared lock
*/
rb_file_const("LOCK_SH", INT2FIX(LOCK_SH));
@@ -1373,6 +1376,36 @@ Init_Foo(void) {
assert_equal "DLL_LOCAL VALUE\nother_function() {\n}", code
end
+ def test_find_body_static_inline
+ content = <<-EOF
+/*
+ * a comment for other_function
+ */
+static inline VALUE
+other_function() {
+}
+
+void
+Init_Foo(void) {
+ VALUE foo = rb_define_class("Foo", rb_cObject);
+
+ rb_define_method(foo, "my_method", other_function, 0);
+}
+ EOF
+
+ klass = util_get_class content, 'foo'
+ other_function = klass.method_list.first
+
+ assert_equal 'my_method', other_function.name
+ assert_equal "a comment for other_function",
+ other_function.comment.text
+ assert_equal '()', other_function.params
+
+ code = other_function.token_stream.first[:text]
+
+ assert_equal "static inline VALUE\nother_function() {\n}", code
+ end
+
def test_find_modifiers_call_seq
comment = RDoc::Comment.new <<-COMMENT
call-seq:
diff --git a/test/rdoc/test_rdoc_parser_ruby.rb b/test/rdoc/test_rdoc_parser_ruby.rb
index aea923765c..3e2a85ffba 100644
--- a/test/rdoc/test_rdoc_parser_ruby.rb
+++ b/test/rdoc/test_rdoc_parser_ruby.rb
@@ -3065,6 +3065,28 @@ RUBY
assert_nil m.params, 'Module parameter not removed'
end
+ def test_parse_statements_nodoc_identifier_alias
+ klass = @top_level.add_class RDoc::NormalClass, 'Foo'
+
+ util_parser "\nalias :old :new # :nodoc:"
+
+ @parser.parse_statements klass, RDoc::Parser::Ruby::NORMAL, nil
+
+ assert_empty klass.aliases
+ assert_empty klass.unmatched_alias_lists
+ end
+
+ def test_parse_statements_nodoc_identifier_alias_method
+ klass = @top_level.add_class RDoc::NormalClass, 'Foo'
+
+ util_parser "\nalias_method :old :new # :nodoc:"
+
+ @parser.parse_statements klass, RDoc::Parser::Ruby::NORMAL, nil
+
+ assert_empty klass.aliases
+ assert_empty klass.unmatched_alias_lists
+ end
+
def test_parse_statements_stopdoc_alias
klass = @top_level.add_class RDoc::NormalClass, 'Foo'
@@ -3205,6 +3227,14 @@ RUBY
assert_nil @parser.parse_symbol_in_arg
end
+ def test_parse_percent_symbol
+ content = '%s[foo bar]'
+ util_parser content
+ tk = @parser.get_tk
+ assert_equal :on_symbol, tk[:kind]
+ assert_equal content, tk[:text]
+ end
+
def test_parse_statements_alias_method
content = <<-CONTENT
class A
diff --git a/test/rdoc/test_rdoc_rdoc.rb b/test/rdoc/test_rdoc_rdoc.rb
index 853d7dad22..5168932430 100644
--- a/test/rdoc/test_rdoc_rdoc.rb
+++ b/test/rdoc/test_rdoc_rdoc.rb
@@ -165,12 +165,7 @@ class TestRDocRDoc < RDoc::TestCase
b = Dir.glob(b).first
c = Dir.glob(c).first
- dot_doc = File.expand_path('.document')
- FileUtils.touch dot_doc
- open(dot_doc, 'w') do |f|
- f.puts 'a.rb'
- f.puts 'b.rb'
- end
+ File.write('.document', "a.rb\n""b.rb\n")
expected_files << a
expected_files << b
@@ -196,12 +191,7 @@ class TestRDocRDoc < RDoc::TestCase
b = Dir.glob(b).first
c = Dir.glob(c).first
- dot_doc = File.expand_path('.document')
- FileUtils.touch dot_doc
- open(dot_doc, 'w') do |f|
- f.puts 'a.rb'
- f.puts 'b.rb'
- end
+ File.write('.document', "a.rb\n""b.rb\n")
expected_files << a
@rdoc.options.exclude = Regexp.new(['b.rb'].join('|'))
diff --git a/test/rdoc/test_rdoc_store.rb b/test/rdoc/test_rdoc_store.rb
index 8350d257b3..50e8667d81 100644
--- a/test/rdoc/test_rdoc_store.rb
+++ b/test/rdoc/test_rdoc_store.rb
@@ -373,9 +373,9 @@ class TestRDocStore < XrefTestCase
assert_equal [@mod], s.all_modules.sort
assert_equal [@page, @top_level], s.all_files.sort
- methods = s.all_classes_and_modules.map do |mod|
+ methods = s.all_classes_and_modules.flat_map do |mod|
mod.method_list
- end.flatten.sort
+ end.sort
_meth_bang_alias = RDoc::AnyMethod.new nil, 'method_bang'
_meth_bang_alias.parent = @klass
@@ -388,9 +388,9 @@ class TestRDocStore < XrefTestCase
assert_equal @klass, methods.last.parent
- attributes = s.all_classes_and_modules.map do |mod|
+ attributes = s.all_classes_and_modules.flat_map do |mod|
mod.attributes
- end.flatten.sort
+ end.sort
assert_equal [@attr], attributes
diff --git a/test/rdoc/test_rdoc_token_stream.rb b/test/rdoc/test_rdoc_token_stream.rb
index 29c5047fb5..8fcd3d8f68 100644
--- a/test/rdoc/test_rdoc_token_stream.rb
+++ b/test/rdoc/test_rdoc_token_stream.rb
@@ -39,6 +39,49 @@ class TestRDocTokenStream < RDoc::TestCase
assert_equal '', RDoc::TokenStream.to_html([])
end
+ def test_add_tokens
+ foo = Class.new do
+ include RDoc::TokenStream
+ end.new
+ foo.collect_tokens
+ foo.add_tokens([:token])
+ assert_equal [:token], foo.token_stream
+ end
+
+ def test_add_token
+ foo = Class.new do
+ include RDoc::TokenStream
+ end.new
+ foo.collect_tokens
+ foo.add_token(:token)
+ assert_equal [:token], foo.token_stream
+ end
+
+ def test_collect_tokens
+ foo = Class.new do
+ include RDoc::TokenStream
+ end.new
+ foo.collect_tokens
+ assert_equal [], foo.token_stream
+ end
+
+ def test_pop_token
+ foo = Class.new do
+ include RDoc::TokenStream
+ end.new
+ foo.collect_tokens
+ foo.add_token(:token)
+ foo.pop_token
+ assert_equal [], foo.token_stream
+ end
+
+ def test_token_stream
+ foo = Class.new do
+ include RDoc::TokenStream
+ end.new
+ assert_equal nil, foo.token_stream
+ end
+
def test_tokens_to_s
foo = Class.new do
include RDoc::TokenStream
@@ -53,5 +96,14 @@ class TestRDocTokenStream < RDoc::TestCase
end.new
assert_equal "foo 'bar'", foo.tokens_to_s
+
+ foo = Class.new do
+ include RDoc::TokenStream
+
+ def initialize
+ @token_stream = nil
+ end
+ end.new
+ assert_equal "", foo.tokens_to_s
end
end
diff --git a/test/rdoc/xref_data.rb b/test/rdoc/xref_data.rb
index c9315b7806..257b821f4f 100644
--- a/test/rdoc/xref_data.rb
+++ b/test/rdoc/xref_data.rb
@@ -74,6 +74,12 @@ class C6
def priv4() end
public def pub5() end
def priv5() end
+ def self.s_pub1() end
+ class << self
+ def s_pub2() end
+ private
+ def s_priv1() end
+ end
protected
private def priv6() end
@@ -82,6 +88,12 @@ class C6
def prot5() end
public def pub6() end
def prot6() end
+ def self.s_pub3() end
+ class << self
+ def s_pub4() end
+ protected
+ def s_prot1() end
+ end
end
class C7
diff --git a/test/reline/helper.rb b/test/reline/helper.rb
index b8751ff16e..26fe834482 100644
--- a/test/reline/helper.rb
+++ b/test/reline/helper.rb
@@ -21,21 +21,30 @@ end
module Reline
class <<self
def test_mode(ansi: false)
- remove_const('IOGate') if const_defined?('IOGate')
- const_set('IOGate', ansi ? Reline::ANSI : Reline::GeneralIO)
- if ENV['RELINE_TEST_ENCODING']
- encoding = Encoding.find(ENV['RELINE_TEST_ENCODING'])
- else
- encoding = Encoding::UTF_8
- end
- Reline::GeneralIO.reset(encoding: encoding) unless ansi
- send(:core).config.instance_variable_set(:@test_mode, true)
- send(:core).config.reset
+ @original_iogate = IOGate
+ remove_const('IOGate')
+ const_set('IOGate', ansi ? Reline::ANSI : Reline::GeneralIO)
+ if ENV['RELINE_TEST_ENCODING']
+ encoding = Encoding.find(ENV['RELINE_TEST_ENCODING'])
+ else
+ encoding = Encoding::UTF_8
+ end
+ @original_get_screen_size = IOGate.method(:get_screen_size)
+ IOGate.singleton_class.remove_method(:get_screen_size)
+ def IOGate.get_screen_size
+ [24, 80]
+ end
+ Reline::GeneralIO.reset(encoding: encoding) unless ansi
+ core.config.instance_variable_set(:@test_mode, true)
+ core.config.reset
end
def test_reset
- remove_const('IOGate') if const_defined?('IOGate')
- const_set('IOGate', Reline::GeneralIO)
+ IOGate.singleton_class.remove_method(:get_screen_size)
+ IOGate.define_singleton_method(:get_screen_size, @original_get_screen_size)
+ remove_const('IOGate')
+ const_set('IOGate', @original_iogate)
+ Reline::GeneralIO.reset
Reline.instance_variable_set(:@core, nil)
end
@@ -69,14 +78,6 @@ module Reline
end
end
-def start_pasting
- Reline::GeneralIO.start_pasting
-end
-
-def finish_pasting
- Reline::GeneralIO.finish_pasting
-end
-
class Reline::TestCase < Test::Unit::TestCase
private def convert_str(input, options = {}, normalized = nil)
return nil if input.nil?
@@ -127,9 +128,14 @@ class Reline::TestCase < Test::Unit::TestCase
end
end
- def assert_line(expected)
- expected = convert_str(expected)
- assert_equal(expected, @line_editor.line)
+ def assert_line_around_cursor(before, after)
+ before = convert_str(before)
+ after = convert_str(after)
+ line = @line_editor.current_line
+ byte_pointer = @line_editor.instance_variable_get(:@byte_pointer)
+ actual_before = line.byteslice(0, byte_pointer)
+ actual_after = line.byteslice(byte_pointer..)
+ assert_equal([before, after], [actual_before, actual_after])
end
def assert_byte_pointer_size(expected)
@@ -144,14 +150,6 @@ class Reline::TestCase < Test::Unit::TestCase
EOM
end
- def assert_cursor(expected)
- assert_equal(expected, @line_editor.instance_variable_get(:@cursor))
- end
-
- def assert_cursor_max(expected)
- assert_equal(expected, @line_editor.instance_variable_get(:@cursor_max))
- end
-
def assert_line_index(expected)
assert_equal(expected, @line_editor.instance_variable_get(:@line_index))
end
diff --git a/test/reline/test_ansi_with_terminfo.rb b/test/reline/test_ansi_with_terminfo.rb
index d7f61806a6..e1c56b9ee1 100644
--- a/test/reline/test_ansi_with_terminfo.rb
+++ b/test/reline/test_ansi_with_terminfo.rb
@@ -5,7 +5,7 @@ class Reline::ANSI::TestWithTerminfo < Reline::TestCase
def setup
Reline.send(:test_mode, ansi: true)
@config = Reline::Config.new
- Reline::IOGate.set_default_key_bindings(@config, allow_terminfo: true)
+ Reline.core.io_gate.set_default_key_bindings(@config, allow_terminfo: true)
end
def teardown
@@ -75,12 +75,28 @@ class Reline::ANSI::TestWithTerminfo < Reline::TestCase
omit e.message
end
+ # Home and End; always mapped regardless of terminfo enabled or not
+ def test_home_end
+ assert_key_binding("\e[H", :ed_move_to_beg)
+ assert_key_binding("\e[F", :ed_move_to_end)
+ end
+
+ # Arrow; always mapped regardless of terminfo enabled or not
+ def test_arrow
+ assert_key_binding("\e[A", :ed_prev_history)
+ assert_key_binding("\e[B", :ed_next_history)
+ assert_key_binding("\e[C", :ed_next_char)
+ assert_key_binding("\e[D", :ed_prev_char)
+ end
+
# Ctrl+arrow and Meta+arrow; always mapped regardless of terminfo enabled or not
def test_extended
assert_key_binding("\e[1;5C", :em_next_word) # Ctrl+→
assert_key_binding("\e[1;5D", :ed_prev_word) # Ctrl+â†
assert_key_binding("\e[1;3C", :em_next_word) # Meta+→
assert_key_binding("\e[1;3D", :ed_prev_word) # Meta+â†
+ assert_key_binding("\e\e[C", :em_next_word) # Meta+→
+ assert_key_binding("\e\e[D", :ed_prev_word) # Meta+â†
end
# Shift-Tab; always mapped regardless of terminfo enabled or not
@@ -93,4 +109,4 @@ class Reline::ANSI::TestWithTerminfo < Reline::TestCase
assert_key_binding("\e ", :em_set_mark, [:emacs])
assert_key_binding("\C-x\C-x", :em_exchange_mark, [:emacs])
end
-end if Reline::Terminfo.enabled?
+end if Reline::Terminfo.enabled? && Reline::Terminfo.term_supported?
diff --git a/test/reline/test_ansi_without_terminfo.rb b/test/reline/test_ansi_without_terminfo.rb
index 28b929849b..3d153514f3 100644
--- a/test/reline/test_ansi_without_terminfo.rb
+++ b/test/reline/test_ansi_without_terminfo.rb
@@ -5,7 +5,7 @@ class Reline::ANSI::TestWithoutTerminfo < Reline::TestCase
def setup
Reline.send(:test_mode, ansi: true)
@config = Reline::Config.new
- Reline::IOGate.set_default_key_bindings(@config, allow_terminfo: false)
+ Reline.core.io_gate.set_default_key_bindings(@config, allow_terminfo: false)
end
def teardown
@@ -60,6 +60,8 @@ class Reline::ANSI::TestWithoutTerminfo < Reline::TestCase
assert_key_binding("\e[1;5D", :ed_prev_word) # Ctrl+â†
assert_key_binding("\e[1;3C", :em_next_word) # Meta+→
assert_key_binding("\e[1;3D", :ed_prev_word) # Meta+â†
+ assert_key_binding("\e\e[C", :em_next_word) # Meta+→
+ assert_key_binding("\e\e[D", :ed_prev_word) # Meta+â†
end
# Shift-Tab; always mapped regardless of terminfo enabled or not
diff --git a/test/reline/test_config.rb b/test/reline/test_config.rb
index c08d7ba498..6d6f25dff3 100644
--- a/test/reline/test_config.rb
+++ b/test/reline/test_config.rb
@@ -85,7 +85,7 @@ class Reline::Config::Test < Reline::TestCase
def test_encoding_is_ascii
@config.reset
- Reline::IOGate.reset(encoding: Encoding::US_ASCII)
+ Reline.core.io_gate.reset(encoding: Encoding::US_ASCII)
@config = Reline::Config.new
assert_equal true, @config.convert_meta
@@ -93,7 +93,7 @@ class Reline::Config::Test < Reline::TestCase
def test_encoding_is_not_ascii
@config.reset
- Reline::IOGate.reset(encoding: Encoding::UTF_8)
+ Reline.core.io_gate.reset(encoding: Encoding::UTF_8)
@config = Reline::Config.new
assert_equal nil, @config.convert_meta
@@ -160,6 +160,23 @@ class Reline::Config::Test < Reline::TestCase
assert_equal :audible, @config.instance_variable_get(:@bell_style)
end
+ def test_include_expand_path
+ home_backup = ENV['HOME']
+ File.open('included_partial', 'wt') do |f|
+ f.write(<<~PARTIAL_LINES)
+ set bell-style on
+ PARTIAL_LINES
+ end
+ ENV['HOME'] = Dir.pwd
+ @config.read_lines(<<~LINES.lines)
+ $include ~/included_partial
+ LINES
+
+ assert_equal :audible, @config.instance_variable_get(:@bell_style)
+ ensure
+ ENV['HOME'] = home_backup
+ end
+
def test_if
@config.read_lines(<<~LINES.lines)
$if Ruby
@@ -199,6 +216,38 @@ class Reline::Config::Test < Reline::TestCase
end
end
+ def test_nested_if_else
+ @config.read_lines(<<~LINES.lines)
+ $if Ruby
+ "\x1": "O"
+ $if NotRuby
+ "\x2": "X"
+ $else
+ "\x3": "O"
+ $if Ruby
+ "\x4": "O"
+ $else
+ "\x5": "X"
+ $endif
+ "\x6": "O"
+ $endif
+ "\x7": "O"
+ $else
+ "\x8": "X"
+ $if NotRuby
+ "\x9": "X"
+ $else
+ "\xA": "X"
+ $endif
+ "\xB": "X"
+ $endif
+ "\xC": "O"
+ LINES
+ keys = [0x1, 0x3, 0x4, 0x6, 0x7, 0xC]
+ key_bindings = keys.to_h { |k| [[k.ord], ['O'.ord]] }
+ assert_equal(key_bindings, @config.instance_variable_get(:@additional_key_bindings)[:emacs])
+ end
+
def test_unclosed_if
e = assert_raise(Reline::Config::InvalidInputrc) do
@config.read_lines(<<~LINES.lines, "INPUTRC")
@@ -226,6 +275,78 @@ class Reline::Config::Test < Reline::TestCase
assert_equal "INPUTRC:1: unmatched endif", e.message
end
+ def test_if_with_mode
+ @config.read_lines(<<~LINES.lines)
+ $if mode=emacs
+ "\C-e": history-search-backward # comment
+ $else
+ "\C-f": history-search-forward
+ $endif
+ LINES
+
+ assert_equal({[5] => :history_search_backward}, @config.instance_variable_get(:@additional_key_bindings)[:emacs])
+ assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_insert])
+ assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_command])
+ end
+
+ def test_else
+ @config.read_lines(<<~LINES.lines)
+ $if mode=vi
+ "\C-e": history-search-backward # comment
+ $else
+ "\C-f": history-search-forward
+ $endif
+ LINES
+
+ assert_equal({[6] => :history_search_forward}, @config.instance_variable_get(:@additional_key_bindings)[:emacs])
+ assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_insert])
+ assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_command])
+ end
+
+ def test_if_with_invalid_mode
+ @config.read_lines(<<~LINES.lines)
+ $if mode=vim
+ "\C-e": history-search-backward
+ $else
+ "\C-f": history-search-forward # comment
+ $endif
+ LINES
+
+ assert_equal({[6] => :history_search_forward}, @config.instance_variable_get(:@additional_key_bindings)[:emacs])
+ assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_insert])
+ assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_command])
+ end
+
+ def test_mode_label_differs_from_keymap_label
+ @config.read_lines(<<~LINES.lines)
+ # Sets mode_label and keymap_label to vi
+ set editing-mode vi
+ # Change keymap_label to emacs. mode_label is still vi.
+ set keymap emacs
+ # condition=true because current mode_label is vi
+ $if mode=vi
+ # sets keybinding to current keymap_label=emacs
+ "\C-e": history-search-backward
+ $endif
+ LINES
+ assert_equal({[5] => :history_search_backward}, @config.instance_variable_get(:@additional_key_bindings)[:emacs])
+ assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_insert])
+ assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_command])
+ end
+
+ def test_if_without_else_condition
+ @config.read_lines(<<~LINES.lines)
+ set editing-mode vi
+ $if mode=vi
+ "\C-e": history-search-backward
+ $endif
+ LINES
+
+ assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:emacs])
+ assert_equal({[5] => :history_search_backward}, @config.instance_variable_get(:@additional_key_bindings)[:vi_insert])
+ assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_command])
+ end
+
def test_default_key_bindings
@config.add_default_key_binding('abcd'.bytes, 'EFGH'.bytes)
@config.read_lines(<<~'LINES'.lines)
diff --git a/test/reline/test_face.rb b/test/reline/test_face.rb
new file mode 100644
index 0000000000..8fa2be8fa4
--- /dev/null
+++ b/test/reline/test_face.rb
@@ -0,0 +1,257 @@
+# frozen_string_literal: true
+
+require_relative 'helper'
+
+class Reline::Face::Test < Reline::TestCase
+ RESET_SGR = "\e[0m"
+
+ def setup
+ @colorterm_backup = ENV['COLORTERM']
+ ENV['COLORTERM'] = 'truecolor'
+ end
+
+ def teardown
+ Reline::Face.reset_to_initial_configs
+ ENV['COLORTERM'] = @colorterm_backup
+ end
+
+ class WithInsufficientSetupTest < self
+ def setup
+ super
+ Reline::Face.config(:my_insufficient_config) do |face|
+ end
+ @face = Reline::Face[:my_insufficient_config]
+ end
+
+ def test_my_insufficient_config_line
+ assert_equal RESET_SGR, @face[:default]
+ assert_equal RESET_SGR, @face[:enhanced]
+ assert_equal RESET_SGR, @face[:scrollbar]
+ end
+
+ def test_my_insufficient_configs
+ my_configs = Reline::Face.configs[:my_insufficient_config]
+ assert_equal(
+ {
+ default: { style: :reset, escape_sequence: RESET_SGR },
+ enhanced: { style: :reset, escape_sequence: RESET_SGR },
+ scrollbar: { style: :reset, escape_sequence: RESET_SGR }
+ },
+ my_configs
+ )
+ end
+ end
+
+ class WithSetupTest < self
+ def setup
+ super
+ Reline::Face.config(:my_config) do |face|
+ face.define :default, foreground: :blue
+ face.define :enhanced, foreground: "#FF1020", background: :black, style: [:bold, :underlined]
+ end
+ Reline::Face.config(:another_config) do |face|
+ face.define :another_label, foreground: :red
+ end
+ @face = Reline::Face[:my_config]
+ end
+
+ def test_now_there_are_four_configs
+ assert_equal %i(default completion_dialog my_config another_config), Reline::Face.configs.keys
+ end
+
+ def test_resetting_config_discards_user_defined_configs
+ Reline::Face.reset_to_initial_configs
+ assert_equal %i(default completion_dialog), Reline::Face.configs.keys
+ end
+
+ def test_my_configs
+ my_configs = Reline::Face.configs[:my_config]
+ assert_equal(
+ {
+ default: {
+ escape_sequence: "#{RESET_SGR}\e[34m", foreground: :blue
+ },
+ enhanced: {
+ background: :black,
+ foreground: "#FF1020",
+ style: [:bold, :underlined],
+ escape_sequence: "\e[0m\e[38;2;255;16;32;40;1;4m"
+ },
+ scrollbar: {
+ style: :reset,
+ escape_sequence: "\e[0m"
+ }
+ },
+ my_configs
+ )
+ end
+
+ def test_my_config_line
+ assert_equal "#{RESET_SGR}\e[34m", @face[:default]
+ end
+
+ def test_my_config_enhanced
+ assert_equal "#{RESET_SGR}\e[38;2;255;16;32;40;1;4m", @face[:enhanced]
+ end
+
+ def test_not_respond_to_another_label
+ assert_equal false, @face.respond_to?(:another_label)
+ end
+ end
+
+ class WithoutSetupTest < self
+ def test_my_config_default
+ Reline::Face.config(:my_config) do |face|
+ # do nothing
+ end
+ face = Reline::Face[:my_config]
+ assert_equal RESET_SGR, face[:default]
+ end
+
+ def test_style_does_not_exist
+ face = Reline::Face[:default]
+ assert_raise ArgumentError do
+ face[:style_does_not_exist]
+ end
+ end
+
+ def test_invalid_keyword
+ assert_raise ArgumentError do
+ Reline::Face.config(:invalid_config) do |face|
+ face.define :default, invalid_keyword: :red
+ end
+ end
+ end
+
+ def test_invalid_foreground_name
+ assert_raise ArgumentError do
+ Reline::Face.config(:invalid_config) do |face|
+ face.define :default, foreground: :invalid_name
+ end
+ end
+ end
+
+ def test_invalid_background_name
+ assert_raise ArgumentError do
+ Reline::Face.config(:invalid_config) do |face|
+ face.define :default, background: :invalid_name
+ end
+ end
+ end
+
+ def test_invalid_style_name
+ assert_raise ArgumentError do
+ Reline::Face.config(:invalid_config) do |face|
+ face.define :default, style: :invalid_name
+ end
+ end
+ end
+
+ def test_private_constants
+ [:SGR_PARAMETER, :Config, :CONFIGS].each do |name|
+ assert_equal false, Reline::Face.constants.include?(name)
+ end
+ end
+ end
+
+ class ConfigTest < self
+ def setup
+ super
+ @config = Reline::Face.const_get(:Config).new(:my_config) { }
+ end
+
+ def teardown
+ super
+ Reline::Face.instance_variable_set(:@force_truecolor, nil)
+ end
+
+ def test_rgb?
+ assert_equal true, @config.send(:rgb_expression?, "#FFFFFF")
+ end
+
+ def test_invalid_rgb?
+ assert_equal false, @config.send(:rgb_expression?, "FFFFFF")
+ assert_equal false, @config.send(:rgb_expression?, "#FFFFF")
+ end
+
+ def test_format_to_sgr_preserves_order
+ assert_equal(
+ "#{RESET_SGR}\e[37;41;1;3m",
+ @config.send(:format_to_sgr, foreground: :white, background: :red, style: [:bold, :italicized])
+ )
+
+ assert_equal(
+ "#{RESET_SGR}\e[37;1;3;41m",
+ @config.send(:format_to_sgr, foreground: :white, style: [:bold, :italicized], background: :red)
+ )
+ end
+
+ def test_format_to_sgr_with_reset
+ assert_equal(
+ RESET_SGR,
+ @config.send(:format_to_sgr, style: :reset)
+ )
+ assert_equal(
+ "#{RESET_SGR}\e[37;0;41m",
+ @config.send(:format_to_sgr, foreground: :white, style: :reset, background: :red)
+ )
+ end
+
+ def test_format_to_sgr_with_single_style
+ assert_equal(
+ "#{RESET_SGR}\e[37;41;1m",
+ @config.send(:format_to_sgr, foreground: :white, background: :red, style: :bold)
+ )
+ end
+
+ def test_truecolor
+ ENV['COLORTERM'] = 'truecolor'
+ assert_equal true, Reline::Face.truecolor?
+ ENV['COLORTERM'] = '24bit'
+ assert_equal true, Reline::Face.truecolor?
+ ENV['COLORTERM'] = nil
+ assert_equal false, Reline::Face.truecolor?
+ Reline::Face.force_truecolor
+ assert_equal true, Reline::Face.truecolor?
+ end
+
+ def test_sgr_rgb_truecolor
+ ENV['COLORTERM'] = 'truecolor'
+ assert_equal "38;2;255;255;255", @config.send(:sgr_rgb, :foreground, "#ffffff")
+ assert_equal "48;2;18;52;86", @config.send(:sgr_rgb, :background, "#123456")
+ end
+
+ def test_sgr_rgb_256color
+ ENV['COLORTERM'] = nil
+ assert_equal '38;5;231', @config.send(:sgr_rgb, :foreground, '#ffffff')
+ assert_equal '48;5;16', @config.send(:sgr_rgb, :background, '#000000')
+ # Color steps are [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]
+ assert_equal '38;5;24', @config.send(:sgr_rgb, :foreground, '#005f87')
+ assert_equal '38;5;67', @config.send(:sgr_rgb, :foreground, '#5f87af')
+ assert_equal '48;5;110', @config.send(:sgr_rgb, :background, '#87afd7')
+ assert_equal '48;5;153', @config.send(:sgr_rgb, :background, '#afd7ff')
+ # Boundary values are [0x30, 0x73, 0x9b, 0xc3, 0xeb]
+ assert_equal '38;5;24', @config.send(:sgr_rgb, :foreground, '#2f729a')
+ assert_equal '38;5;67', @config.send(:sgr_rgb, :foreground, '#30739b')
+ assert_equal '48;5;110', @config.send(:sgr_rgb, :background, '#9ac2ea')
+ assert_equal '48;5;153', @config.send(:sgr_rgb, :background, '#9bc3eb')
+ end
+
+ def test_force_truecolor_reconfigure
+ ENV['COLORTERM'] = nil
+
+ Reline::Face.config(:my_config) do |face|
+ face.define :default, foreground: '#005f87'
+ face.define :enhanced, background: '#afd7ff'
+ end
+
+ assert_equal "\e[0m\e[38;5;24m", Reline::Face[:my_config][:default]
+ assert_equal "\e[0m\e[48;5;153m", Reline::Face[:my_config][:enhanced]
+
+ Reline::Face.force_truecolor
+
+ assert_equal "\e[0m\e[38;2;0;95;135m", Reline::Face[:my_config][:default]
+ assert_equal "\e[0m\e[48;2;175;215;255m", Reline::Face[:my_config][:enhanced]
+ end
+ end
+end
diff --git a/test/reline/test_history.rb b/test/reline/test_history.rb
index 53360409bc..ddf8fb1472 100644
--- a/test/reline/test_history.rb
+++ b/test/reline/test_history.rb
@@ -297,7 +297,7 @@ class Reline::History::Test < Reline::TestCase
end
def get_default_internal_encoding
- if encoding = Reline::IOGate.encoding
+ if encoding = Reline.core.encoding
encoding
elsif RUBY_PLATFORM =~ /mswin|mingw/
Encoding.default_internal || Encoding::UTF_8
diff --git a/test/reline/test_key_actor_emacs.rb b/test/reline/test_key_actor_emacs.rb
index 18a2448539..a9baf9ad37 100644
--- a/test/reline/test_key_actor_emacs.rb
+++ b/test/reline/test_key_actor_emacs.rb
@@ -8,7 +8,7 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
@config.autocompletion = false
Reline::HISTORY.instance_variable_set(:@config, @config)
Reline::HISTORY.clear
- @encoding = Reline::IOGate.encoding
+ @encoding = Reline.core.encoding
@line_editor = Reline::LineEditor.new(@config, @encoding)
@line_editor.reset(@prompt, encoding: @encoding)
end
@@ -19,1259 +19,699 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
def test_ed_insert_one
input_keys('a')
- assert_line('a')
- assert_byte_pointer_size('a')
- assert_cursor(1)
- assert_cursor_max(1)
+ assert_line_around_cursor('a', '')
end
def test_ed_insert_two
input_keys('ab')
- assert_line('ab')
- assert_byte_pointer_size('ab')
- assert_cursor(2)
- assert_cursor_max(2)
+ assert_line_around_cursor('ab', '')
end
def test_ed_insert_mbchar_one
input_keys('ã‹')
- assert_line('ã‹')
- assert_byte_pointer_size('ã‹')
- assert_cursor(2)
- assert_cursor_max(2)
+ assert_line_around_cursor('ã‹', '')
end
def test_ed_insert_mbchar_two
input_keys('ã‹ã')
- assert_line('ã‹ã')
- assert_byte_pointer_size('ã‹ã')
- assert_cursor(4)
- assert_cursor_max(4)
+ assert_line_around_cursor('ã‹ã', '')
end
def test_ed_insert_for_mbchar_by_plural_code_points
input_keys("ã‹\u3099")
- assert_line("ã‹\u3099")
- assert_byte_pointer_size("ã‹\u3099")
- assert_cursor(2)
- assert_cursor_max(2)
+ assert_line_around_cursor("ã‹\u3099", '')
end
def test_ed_insert_for_plural_mbchar_by_plural_code_points
input_keys("ã‹\u3099ã\u3099")
- assert_line("ã‹\u3099ã\u3099")
- assert_byte_pointer_size("ã‹\u3099ã\u3099")
- assert_cursor(4)
- assert_cursor_max(4)
+ assert_line_around_cursor("ã‹\u3099ã\u3099", '')
end
def test_move_next_and_prev
input_keys('abd')
- assert_byte_pointer_size('abd')
- assert_cursor(3)
- assert_cursor_max(3)
+ assert_line_around_cursor('abd', '')
input_keys("\C-b", false)
- assert_byte_pointer_size('ab')
- assert_cursor(2)
- assert_cursor_max(3)
+ assert_line_around_cursor('ab', 'd')
input_keys("\C-b", false)
- assert_byte_pointer_size('a')
- assert_cursor(1)
- assert_cursor_max(3)
+ assert_line_around_cursor('a', 'bd')
input_keys("\C-f", false)
- assert_byte_pointer_size('ab')
- assert_cursor(2)
- assert_cursor_max(3)
+ assert_line_around_cursor('ab', 'd')
input_keys('c')
- assert_byte_pointer_size('abc')
- assert_cursor(3)
- assert_cursor_max(4)
- assert_line('abcd')
+ assert_line_around_cursor('abc', 'd')
end
def test_move_next_and_prev_for_mbchar
input_keys('ã‹ãã‘')
- assert_byte_pointer_size('ã‹ãã‘')
- assert_cursor(6)
- assert_cursor_max(6)
+ assert_line_around_cursor('ã‹ãã‘', '')
input_keys("\C-b", false)
- assert_byte_pointer_size('ã‹ã')
- assert_cursor(4)
- assert_cursor_max(6)
+ assert_line_around_cursor('ã‹ã', 'ã‘')
input_keys("\C-b", false)
- assert_byte_pointer_size('ã‹')
- assert_cursor(2)
- assert_cursor_max(6)
+ assert_line_around_cursor('ã‹', 'ãã‘')
input_keys("\C-f", false)
- assert_byte_pointer_size('ã‹ã')
- assert_cursor(4)
- assert_cursor_max(6)
+ assert_line_around_cursor('ã‹ã', 'ã‘')
input_keys('ã')
- assert_byte_pointer_size('ã‹ãã')
- assert_cursor(6)
- assert_cursor_max(8)
- assert_line('ã‹ããã‘')
+ assert_line_around_cursor('ã‹ãã', 'ã‘')
end
def test_move_next_and_prev_for_mbchar_by_plural_code_points
input_keys("ã‹\u3099ã\u3099ã‘\u3099")
- assert_byte_pointer_size("ã‹\u3099ã\u3099ã‘\u3099")
- assert_cursor(6)
- assert_cursor_max(6)
+ assert_line_around_cursor("ã‹\u3099ã\u3099ã‘\u3099", '')
input_keys("\C-b", false)
- assert_byte_pointer_size("ã‹\u3099ã\u3099")
- assert_cursor(4)
- assert_cursor_max(6)
+ assert_line_around_cursor("ã‹\u3099ã\u3099", "ã‘\u3099")
input_keys("\C-b", false)
- assert_byte_pointer_size("ã‹\u3099")
- assert_cursor(2)
- assert_cursor_max(6)
+ assert_line_around_cursor("ã‹\u3099", "ã\u3099ã‘\u3099")
input_keys("\C-f", false)
- assert_byte_pointer_size("ã‹\u3099ã\u3099")
- assert_cursor(4)
- assert_cursor_max(6)
+ assert_line_around_cursor("ã‹\u3099ã\u3099", "ã‘\u3099")
input_keys("ã\u3099")
- assert_byte_pointer_size("ã‹\u3099ã\u3099ã\u3099")
- assert_cursor(6)
- assert_cursor_max(8)
- assert_line("ã‹\u3099ã\u3099ã\u3099ã‘\u3099")
+ assert_line_around_cursor("ã‹\u3099ã\u3099ã\u3099", "ã‘\u3099")
end
def test_move_to_beg_end
input_keys('bcd')
- assert_byte_pointer_size('bcd')
- assert_cursor(3)
- assert_cursor_max(3)
+ assert_line_around_cursor('bcd', '')
input_keys("\C-a", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(3)
+ assert_line_around_cursor('', 'bcd')
input_keys('a')
- assert_byte_pointer_size('a')
- assert_cursor(1)
- assert_cursor_max(4)
+ assert_line_around_cursor('a', 'bcd')
input_keys("\C-e", false)
- assert_byte_pointer_size('abcd')
- assert_cursor(4)
- assert_cursor_max(4)
+ assert_line_around_cursor('abcd', '')
input_keys('e')
- assert_byte_pointer_size('abcde')
- assert_cursor(5)
- assert_cursor_max(5)
- assert_line('abcde')
+ assert_line_around_cursor('abcde', '')
end
def test_ed_newline_with_cr
input_keys('ab')
- assert_byte_pointer_size('ab')
- assert_cursor(2)
- assert_cursor_max(2)
+ assert_line_around_cursor('ab', '')
refute(@line_editor.finished?)
input_keys("\C-m", false)
- assert_line('ab')
+ assert_line_around_cursor('ab', '')
assert(@line_editor.finished?)
end
def test_ed_newline_with_lf
input_keys('ab')
- assert_byte_pointer_size('ab')
- assert_cursor(2)
- assert_cursor_max(2)
+ assert_line_around_cursor('ab', '')
refute(@line_editor.finished?)
input_keys("\C-j", false)
- assert_line('ab')
+ assert_line_around_cursor('ab', '')
assert(@line_editor.finished?)
end
def test_em_delete_prev_char
input_keys('ab')
- assert_byte_pointer_size('ab')
- assert_cursor(2)
- assert_cursor_max(2)
+ assert_line_around_cursor('ab', '')
input_keys("\C-h", false)
- assert_byte_pointer_size('a')
- assert_cursor(1)
- assert_cursor_max(1)
- assert_line('a')
+ assert_line_around_cursor('a', '')
end
def test_em_delete_prev_char_for_mbchar
input_keys('ã‹ã')
- assert_byte_pointer_size('ã‹ã')
- assert_cursor(4)
- assert_cursor_max(4)
+ assert_line_around_cursor('ã‹ã', '')
input_keys("\C-h", false)
- assert_byte_pointer_size('ã‹')
- assert_cursor(2)
- assert_cursor_max(2)
- assert_line('ã‹')
+ assert_line_around_cursor('ã‹', '')
end
def test_em_delete_prev_char_for_mbchar_by_plural_code_points
input_keys("ã‹\u3099ã\u3099")
- assert_byte_pointer_size("ã‹\u3099ã\u3099")
- assert_cursor(4)
- assert_cursor_max(4)
+ assert_line_around_cursor("ã‹\u3099ã\u3099", '')
input_keys("\C-h", false)
- assert_byte_pointer_size("ã‹\u3099")
- assert_cursor(2)
- assert_cursor_max(2)
- assert_line("ã‹\u3099")
+ assert_line_around_cursor("ã‹\u3099", '')
end
def test_ed_quoted_insert
input_keys("ab\C-v\C-acd")
- assert_line("ab\C-acd")
- assert_byte_pointer_size("ab\C-acd")
- assert_cursor(6)
- assert_cursor_max(6)
+ assert_line_around_cursor("ab\C-acd", '')
input_keys("\C-q\C-b")
- assert_line("ab\C-acd\C-b")
- assert_byte_pointer_size("ab\C-acd\C-b")
- assert_cursor(8)
- assert_cursor_max(8)
+ assert_line_around_cursor("ab\C-acd\C-b", '')
end
def test_ed_kill_line
input_keys("\C-k", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
input_keys('abc')
- assert_byte_pointer_size('abc')
- assert_cursor(3)
- assert_cursor_max(3)
+ assert_line_around_cursor('abc', '')
input_keys("\C-k", false)
- assert_byte_pointer_size('abc')
- assert_cursor(3)
- assert_cursor_max(3)
- assert_line('abc')
+ assert_line_around_cursor('abc', '')
input_keys("\C-b\C-k", false)
- assert_byte_pointer_size('ab')
- assert_cursor(2)
- assert_cursor_max(2)
- assert_line('ab')
+ assert_line_around_cursor('ab', '')
end
def test_em_kill_line
@line_editor.input_key(Reline::Key.new(:em_kill_line, :em_kill_line, false))
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
input_keys('abc')
@line_editor.input_key(Reline::Key.new(:em_kill_line, :em_kill_line, false))
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
input_keys('abc')
input_keys("\C-b", false)
@line_editor.input_key(Reline::Key.new(:em_kill_line, :em_kill_line, false))
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
input_keys('abc')
input_keys("\C-a", false)
@line_editor.input_key(Reline::Key.new(:em_kill_line, :em_kill_line, false))
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
end
def test_ed_move_to_beg
input_keys('abd')
- assert_byte_pointer_size('abd')
- assert_cursor(3)
- assert_cursor_max(3)
+ assert_line_around_cursor('abd', '')
input_keys("\C-b", false)
- assert_byte_pointer_size('ab')
- assert_cursor(2)
- assert_cursor_max(3)
+ assert_line_around_cursor('ab', 'd')
input_keys('c')
- assert_byte_pointer_size('abc')
- assert_cursor(3)
- assert_cursor_max(4)
+ assert_line_around_cursor('abc', 'd')
input_keys("\C-a", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(4)
+ assert_line_around_cursor('', 'abcd')
input_keys('012')
- assert_byte_pointer_size('012')
- assert_cursor(3)
- assert_cursor_max(7)
- assert_line('012abcd')
+ assert_line_around_cursor('012', 'abcd')
input_keys("\C-a", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(7)
+ assert_line_around_cursor('', '012abcd')
input_keys('ABC')
- assert_byte_pointer_size('ABC')
- assert_cursor(3)
- assert_cursor_max(10)
- assert_line('ABC012abcd')
+ assert_line_around_cursor('ABC', '012abcd')
input_keys("\C-f" * 10 + "\C-a", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(10)
+ assert_line_around_cursor('', 'ABC012abcd')
input_keys('a')
- assert_byte_pointer_size('a')
- assert_cursor(1)
- assert_cursor_max(11)
- assert_line('aABC012abcd')
+ assert_line_around_cursor('a', 'ABC012abcd')
end
def test_ed_move_to_beg_with_blank
input_keys(' abc')
- assert_byte_pointer_size(' abc')
- assert_cursor(5)
- assert_cursor_max(5)
+ assert_line_around_cursor(' abc', '')
input_keys("\C-a", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(5)
+ assert_line_around_cursor('', ' abc')
end
def test_ed_move_to_end
input_keys('abd')
- assert_byte_pointer_size('abd')
- assert_cursor(3)
- assert_cursor_max(3)
+ assert_line_around_cursor('abd', '')
input_keys("\C-b", false)
- assert_byte_pointer_size('ab')
- assert_cursor(2)
- assert_cursor_max(3)
+ assert_line_around_cursor('ab', 'd')
input_keys('c')
- assert_byte_pointer_size('abc')
- assert_cursor(3)
- assert_cursor_max(4)
+ assert_line_around_cursor('abc', 'd')
input_keys("\C-e", false)
- assert_byte_pointer_size('abcd')
- assert_cursor(4)
- assert_cursor_max(4)
+ assert_line_around_cursor('abcd', '')
input_keys('012')
- assert_byte_pointer_size('abcd012')
- assert_cursor(7)
- assert_cursor_max(7)
- assert_line('abcd012')
+ assert_line_around_cursor('abcd012', '')
input_keys("\C-e", false)
- assert_byte_pointer_size('abcd012')
- assert_cursor(7)
- assert_cursor_max(7)
+ assert_line_around_cursor('abcd012', '')
input_keys('ABC')
- assert_byte_pointer_size('abcd012ABC')
- assert_cursor(10)
- assert_cursor_max(10)
- assert_line('abcd012ABC')
+ assert_line_around_cursor('abcd012ABC', '')
input_keys("\C-b" * 10 + "\C-e", false)
- assert_byte_pointer_size('abcd012ABC')
- assert_cursor(10)
- assert_cursor_max(10)
+ assert_line_around_cursor('abcd012ABC', '')
input_keys('a')
- assert_byte_pointer_size('abcd012ABCa')
- assert_cursor(11)
- assert_cursor_max(11)
- assert_line('abcd012ABCa')
+ assert_line_around_cursor('abcd012ABCa', '')
end
def test_em_delete
input_keys('ab')
- assert_byte_pointer_size('ab')
- assert_cursor(2)
- assert_cursor_max(2)
+ assert_line_around_cursor('ab', '')
input_keys("\C-a", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(2)
+ assert_line_around_cursor('', 'ab')
input_keys("\C-d", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(1)
- assert_line('b')
+ assert_line_around_cursor('', 'b')
end
def test_em_delete_for_mbchar
input_keys('ã‹ã')
- assert_byte_pointer_size('ã‹ã')
- assert_cursor(4)
- assert_cursor_max(4)
+ assert_line_around_cursor('ã‹ã', '')
input_keys("\C-a", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(4)
+ assert_line_around_cursor('', 'ã‹ã')
input_keys("\C-d", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(2)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(2)
- assert_line('ã')
+ assert_line_around_cursor('', 'ã')
end
def test_em_delete_for_mbchar_by_plural_code_points
input_keys("ã‹\u3099ã\u3099")
- assert_byte_pointer_size("ã‹\u3099ã\u3099")
- assert_cursor(4)
- assert_cursor_max(4)
+ assert_line_around_cursor("ã‹\u3099ã\u3099", '')
input_keys("\C-a", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(4)
+ assert_line_around_cursor('', "ã‹\u3099ã\u3099")
input_keys("\C-d", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(2)
- assert_line("ã\u3099")
+ assert_line_around_cursor('', "ã\u3099")
end
def test_em_delete_ends_editing
input_keys("\C-d") # quit from inputing
- assert_line(nil)
+ assert_nil(@line_editor.line)
assert(@line_editor.finished?)
end
def test_ed_clear_screen
- refute(@line_editor.instance_variable_get(:@cleared))
+ @line_editor.instance_variable_get(:@rendered_screen).lines = [[]]
input_keys("\C-l", false)
- assert(@line_editor.instance_variable_get(:@cleared))
+ assert_empty(@line_editor.instance_variable_get(:@rendered_screen).lines)
end
def test_ed_clear_screen_with_inputed
input_keys('abc')
input_keys("\C-b", false)
- refute(@line_editor.instance_variable_get(:@cleared))
- assert_byte_pointer_size('ab')
- assert_cursor(2)
- assert_cursor_max(3)
+ @line_editor.instance_variable_get(:@rendered_screen).lines = [[]]
+ assert_line_around_cursor('ab', 'c')
input_keys("\C-l", false)
- assert(@line_editor.instance_variable_get(:@cleared))
- assert_byte_pointer_size('ab')
- assert_cursor(2)
- assert_cursor_max(3)
- assert_line('abc')
+ assert_empty(@line_editor.instance_variable_get(:@rendered_screen).lines)
+ assert_line_around_cursor('ab', 'c')
end
def test_key_delete
input_keys('abc')
- assert_cursor(3)
- assert_cursor_max(3)
+ assert_line_around_cursor('abc', '')
@line_editor.input_key(Reline::Key.new(:key_delete, :key_delete, false))
- assert_cursor(3)
- assert_cursor_max(3)
- assert_line('abc')
+ assert_line_around_cursor('abc', '')
end
def test_key_delete_does_not_end_editing
@line_editor.input_key(Reline::Key.new(:key_delete, :key_delete, false))
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
refute(@line_editor.finished?)
end
def test_key_delete_preserves_cursor
input_keys('abc')
input_keys("\C-b", false)
- assert_cursor(2)
- assert_cursor_max(3)
+ assert_line_around_cursor('ab', 'c')
@line_editor.input_key(Reline::Key.new(:key_delete, :key_delete, false))
- assert_cursor(2)
- assert_cursor_max(2)
- assert_line('ab')
+ assert_line_around_cursor('ab', '')
end
def test_em_next_word
- assert_byte_pointer_size('')
- assert_cursor(0)
+ assert_line_around_cursor('', '')
input_keys('abc def{bbb}ccc')
input_keys("\C-a\M-F", false)
- assert_byte_pointer_size('abc')
- assert_cursor(3)
+ assert_line_around_cursor('abc', ' def{bbb}ccc')
input_keys("\M-F", false)
- assert_byte_pointer_size('abc def')
- assert_cursor(7)
+ assert_line_around_cursor('abc def', '{bbb}ccc')
input_keys("\M-F", false)
- assert_byte_pointer_size('abc def{bbb')
- assert_cursor(11)
+ assert_line_around_cursor('abc def{bbb', '}ccc')
input_keys("\M-F", false)
- assert_byte_pointer_size('abc def{bbb}ccc')
- assert_cursor(15)
+ assert_line_around_cursor('abc def{bbb}ccc', '')
input_keys("\M-F", false)
- assert_byte_pointer_size('abc def{bbb}ccc')
- assert_cursor(15)
+ assert_line_around_cursor('abc def{bbb}ccc', '')
end
def test_em_next_word_for_mbchar
- assert_cursor(0)
+ assert_line_around_cursor('', '')
input_keys('ã‚ã„ㆠã‹ãã{ã•ã—ã™}ãŸã¡ã¤')
input_keys("\C-a\M-F", false)
- assert_byte_pointer_size('ã‚ã„ã†')
- assert_cursor(6)
+ assert_line_around_cursor('ã‚ã„ã†', ' ã‹ãã{ã•ã—ã™}ãŸã¡ã¤')
input_keys("\M-F", false)
- assert_byte_pointer_size('ã‚ã„ㆠã‹ãã')
- assert_cursor(13)
+ assert_line_around_cursor('ã‚ã„ㆠã‹ãã', '{ã•ã—ã™}ãŸã¡ã¤')
input_keys("\M-F", false)
- assert_byte_pointer_size('ã‚ã„ㆠã‹ãã{ã•ã—ã™')
- assert_cursor(20)
+ assert_line_around_cursor('ã‚ã„ㆠã‹ãã{ã•ã—ã™', '}ãŸã¡ã¤')
input_keys("\M-F", false)
- assert_byte_pointer_size('ã‚ã„ㆠã‹ãã{ã•ã—ã™}ãŸã¡ã¤')
- assert_cursor(27)
+ assert_line_around_cursor('ã‚ã„ㆠã‹ãã{ã•ã—ã™}ãŸã¡ã¤', '')
input_keys("\M-F", false)
- assert_byte_pointer_size('ã‚ã„ㆠã‹ãã{ã•ã—ã™}ãŸã¡ã¤')
- assert_cursor(27)
+ assert_line_around_cursor('ã‚ã„ㆠã‹ãã{ã•ã—ã™}ãŸã¡ã¤', '')
end
def test_em_next_word_for_mbchar_by_plural_code_points
- assert_cursor(0)
+ assert_line_around_cursor("", "")
input_keys("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}ãŸã¡ã¤")
input_keys("\C-a\M-F", false)
- assert_byte_pointer_size("ã‚ã„ã†")
- assert_cursor(6)
+ assert_line_around_cursor("ã‚ã„ã†", " ã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}ãŸã¡ã¤")
input_keys("\M-F", false)
- assert_byte_pointer_size("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099")
- assert_cursor(13)
+ assert_line_around_cursor("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099", "{ã•ã—ã™}ãŸã¡ã¤")
input_keys("\M-F", false)
- assert_byte_pointer_size("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™")
- assert_cursor(20)
+ assert_line_around_cursor("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™", "}ãŸã¡ã¤")
input_keys("\M-F", false)
- assert_byte_pointer_size("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}ãŸã¡ã¤")
- assert_cursor(27)
+ assert_line_around_cursor("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}ãŸã¡ã¤", "")
input_keys("\M-F", false)
- assert_byte_pointer_size("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}ãŸã¡ã¤")
- assert_cursor(27)
+ assert_line_around_cursor("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}ãŸã¡ã¤", "")
end
def test_em_prev_word
input_keys('abc def{bbb}ccc')
- assert_byte_pointer_size('abc def{bbb}ccc')
- assert_cursor(15)
+ assert_line_around_cursor('abc def{bbb}ccc', '')
input_keys("\M-B", false)
- assert_byte_pointer_size('abc def{bbb}')
- assert_cursor(12)
+ assert_line_around_cursor('abc def{bbb}', 'ccc')
input_keys("\M-B", false)
- assert_byte_pointer_size('abc def{')
- assert_cursor(8)
+ assert_line_around_cursor('abc def{', 'bbb}ccc')
input_keys("\M-B", false)
- assert_byte_pointer_size('abc ')
- assert_cursor(4)
+ assert_line_around_cursor('abc ', 'def{bbb}ccc')
input_keys("\M-B", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
+ assert_line_around_cursor('', 'abc def{bbb}ccc')
input_keys("\M-B", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
+ assert_line_around_cursor('', 'abc def{bbb}ccc')
end
def test_em_prev_word_for_mbchar
input_keys('ã‚ã„ㆠã‹ãã{ã•ã—ã™}ãŸã¡ã¤')
- assert_byte_pointer_size('ã‚ã„ㆠã‹ãã{ã•ã—ã™}ãŸã¡ã¤')
- assert_cursor(27)
+ assert_line_around_cursor('ã‚ã„ㆠã‹ãã{ã•ã—ã™}ãŸã¡ã¤', '')
input_keys("\M-B", false)
- assert_byte_pointer_size('ã‚ã„ㆠã‹ãã{ã•ã—ã™}')
- assert_cursor(21)
+ assert_line_around_cursor('ã‚ã„ㆠã‹ãã{ã•ã—ã™}', 'ãŸã¡ã¤')
input_keys("\M-B", false)
- assert_byte_pointer_size('ã‚ã„ㆠã‹ãã{')
- assert_cursor(14)
+ assert_line_around_cursor('ã‚ã„ㆠã‹ãã{', 'ã•ã—ã™}ãŸã¡ã¤')
input_keys("\M-B", false)
- assert_byte_pointer_size('ã‚ã„ㆠ')
- assert_cursor(7)
+ assert_line_around_cursor('ã‚ã„ㆠ', 'ã‹ãã{ã•ã—ã™}ãŸã¡ã¤')
input_keys("\M-B", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
+ assert_line_around_cursor('', 'ã‚ã„ㆠã‹ãã{ã•ã—ã™}ãŸã¡ã¤')
input_keys("\M-B", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
+ assert_line_around_cursor('', 'ã‚ã„ㆠã‹ãã{ã•ã—ã™}ãŸã¡ã¤')
end
def test_em_prev_word_for_mbchar_by_plural_code_points
input_keys("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}ãŸã¡ã¤")
- assert_byte_pointer_size("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}ãŸã¡ã¤")
- assert_cursor(27)
+ assert_line_around_cursor("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}ãŸã¡ã¤", "")
input_keys("\M-B", false)
- assert_byte_pointer_size("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}")
- assert_cursor(21)
+ assert_line_around_cursor("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}", "ãŸã¡ã¤")
input_keys("\M-B", false)
- assert_byte_pointer_size("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{")
- assert_cursor(14)
+ assert_line_around_cursor("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{", "ã•ã—ã™}ãŸã¡ã¤")
input_keys("\M-B", false)
- assert_byte_pointer_size('ã‚ã„ㆠ')
- assert_cursor(7)
+ assert_line_around_cursor("ã‚ã„ㆠ", "ã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}ãŸã¡ã¤")
input_keys("\M-B", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
+ assert_line_around_cursor("", "ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}ãŸã¡ã¤")
input_keys("\M-B", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
+ assert_line_around_cursor("", "ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}ãŸã¡ã¤")
end
def test_em_delete_next_word
input_keys('abc def{bbb}ccc')
input_keys("\C-a", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(15)
+ assert_line_around_cursor('', 'abc def{bbb}ccc')
input_keys("\M-d", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(12)
- assert_line(' def{bbb}ccc')
+ assert_line_around_cursor('', ' def{bbb}ccc')
input_keys("\M-d", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(8)
- assert_line('{bbb}ccc')
+ assert_line_around_cursor('', '{bbb}ccc')
input_keys("\M-d", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(4)
- assert_line('}ccc')
+ assert_line_around_cursor('', '}ccc')
input_keys("\M-d", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
end
def test_em_delete_next_word_for_mbchar
input_keys('ã‚ã„ㆠã‹ãã{ã•ã—ã™}ãŸã¡ã¤')
input_keys("\C-a", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(27)
+ assert_line_around_cursor('', 'ã‚ã„ㆠã‹ãã{ã•ã—ã™}ãŸã¡ã¤')
input_keys("\M-d", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(21)
- assert_line(' ã‹ãã{ã•ã—ã™}ãŸã¡ã¤')
+ assert_line_around_cursor('', ' ã‹ãã{ã•ã—ã™}ãŸã¡ã¤')
input_keys("\M-d", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(14)
- assert_line('{ã•ã—ã™}ãŸã¡ã¤')
+ assert_line_around_cursor('', '{ã•ã—ã™}ãŸã¡ã¤')
input_keys("\M-d", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(7)
- assert_line('}ãŸã¡ã¤')
+ assert_line_around_cursor('', '}ãŸã¡ã¤')
input_keys("\M-d", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
end
def test_em_delete_next_word_for_mbchar_by_plural_code_points
input_keys("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}ãŸã¡ã¤")
input_keys("\C-a", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(27)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(27)
+ assert_line_around_cursor('', "ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}ãŸã¡ã¤")
input_keys("\M-d", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(21)
- assert_line(" ã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}ãŸã¡ã¤")
+ assert_line_around_cursor('', " ã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}ãŸã¡ã¤")
input_keys("\M-d", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(14)
- assert_line('{ã•ã—ã™}ãŸã¡ã¤')
+ assert_line_around_cursor('', '{ã•ã—ã™}ãŸã¡ã¤')
input_keys("\M-d", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(7)
- assert_line('}ãŸã¡ã¤')
+ assert_line_around_cursor('', '}ãŸã¡ã¤')
input_keys("\M-d", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
end
def test_ed_delete_prev_word
input_keys('abc def{bbb}ccc')
- assert_byte_pointer_size('abc def{bbb}ccc')
- assert_cursor(15)
- assert_cursor_max(15)
+ assert_line_around_cursor('abc def{bbb}ccc', '')
input_keys("\M-\C-H", false)
- assert_byte_pointer_size('abc def{bbb}')
- assert_cursor(12)
- assert_cursor_max(12)
- assert_line('abc def{bbb}')
+ assert_line_around_cursor('abc def{bbb}', '')
input_keys("\M-\C-H", false)
- assert_byte_pointer_size('abc def{')
- assert_cursor(8)
- assert_cursor_max(8)
- assert_line('abc def{')
+ assert_line_around_cursor('abc def{', '')
input_keys("\M-\C-H", false)
- assert_byte_pointer_size('abc ')
- assert_cursor(4)
- assert_cursor_max(4)
- assert_line('abc ')
+ assert_line_around_cursor('abc ', '')
input_keys("\M-\C-H", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
end
def test_ed_delete_prev_word_for_mbchar
input_keys('ã‚ã„ㆠã‹ãã{ã•ã—ã™}ãŸã¡ã¤')
- assert_byte_pointer_size('ã‚ã„ㆠã‹ãã{ã•ã—ã™}ãŸã¡ã¤')
- assert_cursor(27)
- assert_cursor_max(27)
+ assert_line_around_cursor('ã‚ã„ㆠã‹ãã{ã•ã—ã™}ãŸã¡ã¤', '')
input_keys("\M-\C-H", false)
- assert_byte_pointer_size('ã‚ã„ㆠã‹ãã{ã•ã—ã™}')
- assert_cursor(21)
- assert_cursor_max(21)
- assert_line('ã‚ã„ㆠã‹ãã{ã•ã—ã™}')
+ assert_line_around_cursor('ã‚ã„ㆠã‹ãã{ã•ã—ã™}', '')
input_keys("\M-\C-H", false)
- assert_byte_pointer_size('ã‚ã„ㆠã‹ãã{')
- assert_cursor(14)
- assert_cursor_max(14)
- assert_line('ã‚ã„ㆠã‹ãã{')
+ assert_line_around_cursor('ã‚ã„ㆠã‹ãã{', '')
input_keys("\M-\C-H", false)
- assert_byte_pointer_size('ã‚ã„ㆠ')
- assert_cursor(7)
- assert_cursor_max(7)
- assert_line('ã‚ã„ㆠ')
+ assert_line_around_cursor('ã‚ã„ㆠ', '')
input_keys("\M-\C-H", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
end
def test_ed_delete_prev_word_for_mbchar_by_plural_code_points
input_keys("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}ãŸã¡ã¤")
- assert_byte_pointer_size("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}ãŸã¡ã¤")
- assert_cursor(27)
- assert_cursor_max(27)
+ assert_line_around_cursor("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}ãŸã¡ã¤", '')
input_keys("\M-\C-H", false)
- assert_byte_pointer_size("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}")
- assert_cursor(21)
- assert_cursor_max(21)
- assert_line("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}")
+ assert_line_around_cursor("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}", '')
input_keys("\M-\C-H", false)
- assert_byte_pointer_size("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{")
- assert_cursor(14)
- assert_cursor_max(14)
- assert_line("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{")
+ assert_line_around_cursor("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{", '')
input_keys("\M-\C-H", false)
- assert_byte_pointer_size("ã‚ã„ㆠ")
- assert_cursor(7)
- assert_cursor_max(7)
- assert_line('ã‚ã„ㆠ')
+ assert_line_around_cursor('ã‚ã„ㆠ', '')
input_keys("\M-\C-H", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
end
def test_ed_transpose_chars
input_keys('abc')
input_keys("\C-a", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(3)
+ assert_line_around_cursor('', 'abc')
input_keys("\C-t", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(3)
- assert_line('abc')
+ assert_line_around_cursor('', 'abc')
input_keys("\C-f\C-t", false)
- assert_byte_pointer_size('ba')
- assert_cursor(2)
- assert_cursor_max(3)
- assert_line('bac')
+ assert_line_around_cursor('ba', 'c')
input_keys("\C-t", false)
- assert_byte_pointer_size('bca')
- assert_cursor(3)
- assert_cursor_max(3)
- assert_line('bca')
+ assert_line_around_cursor('bca', '')
input_keys("\C-t", false)
- assert_byte_pointer_size('bac')
- assert_cursor(3)
- assert_cursor_max(3)
- assert_line('bac')
+ assert_line_around_cursor('bac', '')
end
def test_ed_transpose_chars_for_mbchar
input_keys('ã‚ã‹ã•')
input_keys("\C-a", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(6)
+ assert_line_around_cursor('', 'ã‚ã‹ã•')
input_keys("\C-t", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(6)
- assert_line('ã‚ã‹ã•')
+ assert_line_around_cursor('', 'ã‚ã‹ã•')
input_keys("\C-f\C-t", false)
- assert_byte_pointer_size('ã‹ã‚')
- assert_cursor(4)
- assert_cursor_max(6)
- assert_line('ã‹ã‚ã•')
+ assert_line_around_cursor('ã‹ã‚', 'ã•')
input_keys("\C-t", false)
- assert_byte_pointer_size('ã‹ã•ã‚')
- assert_cursor(6)
- assert_cursor_max(6)
- assert_line('ã‹ã•ã‚')
+ assert_line_around_cursor('ã‹ã•ã‚', '')
input_keys("\C-t", false)
- assert_byte_pointer_size('ã‹ã‚ã•')
- assert_cursor(6)
- assert_cursor_max(6)
- assert_line('ã‹ã‚ã•')
+ assert_line_around_cursor('ã‹ã‚ã•', '')
end
def test_ed_transpose_chars_for_mbchar_by_plural_code_points
input_keys("ã‚ã‹\u3099ã•")
input_keys("\C-a", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(6)
+ assert_line_around_cursor('', "ã‚ã‹\u3099ã•")
input_keys("\C-t", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(6)
- assert_line("ã‚ã‹\u3099ã•")
+ assert_line_around_cursor('', "ã‚ã‹\u3099ã•")
input_keys("\C-f\C-t", false)
- assert_byte_pointer_size("ã‹\u3099ã‚")
- assert_cursor(4)
- assert_cursor_max(6)
- assert_line("ã‹\u3099ã‚ã•")
+ assert_line_around_cursor("ã‹\u3099ã‚", 'ã•')
input_keys("\C-t", false)
- assert_byte_pointer_size("ã‹\u3099ã•ã‚")
- assert_cursor(6)
- assert_cursor_max(6)
- assert_line("ã‹\u3099ã•ã‚")
+ assert_line_around_cursor("ã‹\u3099ã•ã‚", '')
input_keys("\C-t", false)
- assert_byte_pointer_size("ã‹\u3099ã‚ã•")
- assert_cursor(6)
- assert_cursor_max(6)
- assert_line("ã‹\u3099ã‚ã•")
+ assert_line_around_cursor("ã‹\u3099ã‚ã•", '')
end
def test_ed_transpose_words
input_keys('abc def')
- assert_line('abc def')
- assert_byte_pointer_size('abc def')
- assert_cursor(7)
- assert_cursor_max(7)
+ assert_line_around_cursor('abc def', '')
input_keys("\M-t", false)
- assert_line('def abc')
- assert_byte_pointer_size('def abc')
- assert_cursor(7)
- assert_cursor_max(7)
+ assert_line_around_cursor('def abc', '')
input_keys("\C-a\C-k", false)
input_keys(' abc def ')
input_keys("\C-b" * 4, false)
- assert_line(' abc def ')
- assert_byte_pointer_size(' abc de')
- assert_cursor(8)
- assert_cursor_max(12)
+ assert_line_around_cursor(' abc de', 'f ')
input_keys("\M-t", false)
- assert_line(' def abc ')
- assert_byte_pointer_size(' def abc')
- assert_cursor(9)
- assert_cursor_max(12)
+ assert_line_around_cursor(' def abc', ' ')
input_keys("\C-a\C-k", false)
input_keys(' abc def ')
input_keys("\C-b" * 6, false)
- assert_line(' abc def ')
- assert_byte_pointer_size(' abc ')
- assert_cursor(6)
- assert_cursor_max(12)
+ assert_line_around_cursor(' abc ', 'def ')
input_keys("\M-t", false)
- assert_line(' def abc ')
- assert_byte_pointer_size(' def abc')
- assert_cursor(9)
- assert_cursor_max(12)
+ assert_line_around_cursor(' def abc', ' ')
input_keys("\M-t", false)
- assert_line(' abc def')
- assert_byte_pointer_size(' abc def')
- assert_cursor(12)
- assert_cursor_max(12)
+ assert_line_around_cursor(' abc def', '')
end
def test_ed_transpose_words_for_mbchar
input_keys('ã‚ã„ㆠã‹ãã')
- assert_line('ã‚ã„ㆠã‹ãã')
- assert_byte_pointer_size('ã‚ã„ㆠã‹ãã')
- assert_cursor(13)
- assert_cursor_max(13)
+ assert_line_around_cursor('ã‚ã„ㆠã‹ãã', '')
input_keys("\M-t", false)
- assert_line('ã‹ãã ã‚ã„ã†')
- assert_byte_pointer_size('ã‹ãã ã‚ã„ã†')
- assert_cursor(13)
- assert_cursor_max(13)
+ assert_line_around_cursor('ã‹ãã ã‚ã„ã†', '')
input_keys("\C-a\C-k", false)
input_keys(' ã‚ã„ㆠã‹ãã ')
input_keys("\C-b" * 4, false)
- assert_line(' ã‚ã„ㆠã‹ãã ')
- assert_byte_pointer_size(' ã‚ã„ㆠã‹ã')
- assert_cursor(13)
- assert_cursor_max(18)
+ assert_line_around_cursor(' ã‚ã„ㆠã‹ã', 'ã ')
input_keys("\M-t", false)
- assert_line(' ã‹ãã ã‚ã„ㆠ')
- assert_byte_pointer_size(' ã‹ãã ã‚ã„ã†')
- assert_cursor(15)
- assert_cursor_max(18)
+ assert_line_around_cursor(' ã‹ãã ã‚ã„ã†', ' ')
input_keys("\C-a\C-k", false)
input_keys(' ã‚ã„ㆠã‹ãã ')
input_keys("\C-b" * 6, false)
- assert_line(' ã‚ã„ㆠã‹ãã ')
- assert_byte_pointer_size(' ã‚ã„ㆠ')
- assert_cursor(9)
- assert_cursor_max(18)
+ assert_line_around_cursor(' ã‚ã„ㆠ', 'ã‹ãã ')
input_keys("\M-t", false)
- assert_line(' ã‹ãã ã‚ã„ㆠ')
- assert_byte_pointer_size(' ã‹ãã ã‚ã„ã†')
- assert_cursor(15)
- assert_cursor_max(18)
+ assert_line_around_cursor(' ã‹ãã ã‚ã„ã†', ' ')
input_keys("\M-t", false)
- assert_line(' ã‚ã„ㆠã‹ãã')
- assert_byte_pointer_size(' ã‚ã„ㆠã‹ãã')
- assert_cursor(18)
- assert_cursor_max(18)
+ assert_line_around_cursor(' ã‚ã„ㆠã‹ãã', '')
end
def test_ed_transpose_words_with_one_word
input_keys('abc ')
- assert_line('abc ')
- assert_byte_pointer_size('abc ')
- assert_cursor(5)
- assert_cursor_max(5)
+ assert_line_around_cursor('abc ', '')
input_keys("\M-t", false)
- assert_line('abc ')
- assert_byte_pointer_size('abc ')
- assert_cursor(5)
- assert_cursor_max(5)
+ assert_line_around_cursor('abc ', '')
input_keys("\C-b", false)
- assert_line('abc ')
- assert_byte_pointer_size('abc ')
- assert_cursor(4)
- assert_cursor_max(5)
+ assert_line_around_cursor('abc ', ' ')
input_keys("\M-t", false)
- assert_line('abc ')
- assert_byte_pointer_size('abc ')
- assert_cursor(4)
- assert_cursor_max(5)
+ assert_line_around_cursor('abc ', ' ')
input_keys("\C-b" * 2, false)
- assert_line('abc ')
- assert_byte_pointer_size('ab')
- assert_cursor(2)
- assert_cursor_max(5)
+ assert_line_around_cursor('ab', 'c ')
input_keys("\M-t", false)
- assert_line('abc ')
- assert_byte_pointer_size('ab')
- assert_cursor(2)
- assert_cursor_max(5)
+ assert_line_around_cursor('ab', 'c ')
input_keys("\M-t", false)
- assert_line('abc ')
- assert_byte_pointer_size('ab')
- assert_cursor(2)
- assert_cursor_max(5)
+ assert_line_around_cursor('ab', 'c ')
end
def test_ed_transpose_words_with_one_word_for_mbchar
input_keys('ã‚ã„ㆠ')
- assert_line('ã‚ã„ㆠ')
- assert_byte_pointer_size('ã‚ã„ㆠ')
- assert_cursor(8)
- assert_cursor_max(8)
+ assert_line_around_cursor('ã‚ã„ㆠ', '')
input_keys("\M-t", false)
- assert_line('ã‚ã„ㆠ')
- assert_byte_pointer_size('ã‚ã„ㆠ')
- assert_cursor(8)
- assert_cursor_max(8)
+ assert_line_around_cursor('ã‚ã„ㆠ', '')
input_keys("\C-b", false)
- assert_line('ã‚ã„ㆠ')
- assert_byte_pointer_size('ã‚ã„ㆠ')
- assert_cursor(7)
- assert_cursor_max(8)
+ assert_line_around_cursor('ã‚ã„ㆠ', ' ')
input_keys("\M-t", false)
- assert_line('ã‚ã„ㆠ')
- assert_byte_pointer_size('ã‚ã„ㆠ')
- assert_cursor(7)
- assert_cursor_max(8)
+ assert_line_around_cursor('ã‚ã„ㆠ', ' ')
input_keys("\C-b" * 2, false)
- assert_line('ã‚ã„ㆠ')
- assert_byte_pointer_size('ã‚ã„')
- assert_cursor(4)
- assert_cursor_max(8)
+ assert_line_around_cursor('ã‚ã„', 'ㆠ')
input_keys("\M-t", false)
- assert_line('ã‚ã„ㆠ')
- assert_byte_pointer_size('ã‚ã„')
- assert_cursor(4)
- assert_cursor_max(8)
+ assert_line_around_cursor('ã‚ã„', 'ㆠ')
input_keys("\M-t", false)
- assert_line('ã‚ã„ㆠ')
- assert_byte_pointer_size('ã‚ã„')
- assert_cursor(4)
- assert_cursor_max(8)
+ assert_line_around_cursor('ã‚ã„', 'ㆠ')
end
def test_ed_digit
input_keys('0123')
- assert_byte_pointer_size('0123')
- assert_cursor(4)
- assert_cursor_max(4)
- assert_line('0123')
+ assert_line_around_cursor('0123', '')
end
def test_ed_next_and_prev_char
input_keys('abc')
- assert_byte_pointer_size('abc')
- assert_cursor(3)
- assert_cursor_max(3)
+ assert_line_around_cursor('abc', '')
input_keys("\C-b", false)
- assert_byte_pointer_size('ab')
- assert_cursor(2)
- assert_cursor_max(3)
+ assert_line_around_cursor('ab', 'c')
input_keys("\C-b", false)
- assert_byte_pointer_size('a')
- assert_cursor(1)
- assert_cursor_max(3)
+ assert_line_around_cursor('a', 'bc')
input_keys("\C-b", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(3)
+ assert_line_around_cursor('', 'abc')
input_keys("\C-b", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(3)
+ assert_line_around_cursor('', 'abc')
input_keys("\C-f", false)
- assert_byte_pointer_size('a')
- assert_cursor(1)
- assert_cursor_max(3)
+ assert_line_around_cursor('a', 'bc')
input_keys("\C-f", false)
- assert_byte_pointer_size('ab')
- assert_cursor(2)
- assert_cursor_max(3)
+ assert_line_around_cursor('ab', 'c')
input_keys("\C-f", false)
- assert_byte_pointer_size('abc')
- assert_cursor(3)
- assert_cursor_max(3)
+ assert_line_around_cursor('abc', '')
input_keys("\C-f", false)
- assert_byte_pointer_size('abc')
- assert_cursor(3)
- assert_cursor_max(3)
+ assert_line_around_cursor('abc', '')
end
def test_ed_next_and_prev_char_for_mbchar
input_keys('ã‚ã„ã†')
- assert_byte_pointer_size('ã‚ã„ã†')
- assert_cursor(6)
- assert_cursor_max(6)
+ assert_line_around_cursor('ã‚ã„ã†', '')
input_keys("\C-b", false)
- assert_byte_pointer_size('ã‚ã„')
- assert_cursor(4)
- assert_cursor_max(6)
+ assert_line_around_cursor('ã‚ã„', 'ã†')
input_keys("\C-b", false)
- assert_byte_pointer_size('ã‚')
- assert_cursor(2)
- assert_cursor_max(6)
+ assert_line_around_cursor('ã‚', 'ã„ã†')
input_keys("\C-b", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(6)
+ assert_line_around_cursor('', 'ã‚ã„ã†')
input_keys("\C-b", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(6)
+ assert_line_around_cursor('', 'ã‚ã„ã†')
input_keys("\C-f", false)
- assert_byte_pointer_size('ã‚')
- assert_cursor(2)
- assert_cursor_max(6)
+ assert_line_around_cursor('ã‚', 'ã„ã†')
input_keys("\C-f", false)
- assert_byte_pointer_size('ã‚ã„')
- assert_cursor(4)
- assert_cursor_max(6)
+ assert_line_around_cursor('ã‚ã„', 'ã†')
input_keys("\C-f", false)
- assert_byte_pointer_size('ã‚ã„ã†')
- assert_cursor(6)
- assert_cursor_max(6)
+ assert_line_around_cursor('ã‚ã„ã†', '')
input_keys("\C-f", false)
- assert_byte_pointer_size('ã‚ã„ã†')
- assert_cursor(6)
- assert_cursor_max(6)
+ assert_line_around_cursor('ã‚ã„ã†', '')
end
def test_ed_next_and_prev_char_for_mbchar_by_plural_code_points
input_keys("ã‹\u3099ã\u3099ã\u3099")
- assert_byte_pointer_size("ã‹\u3099ã\u3099ã\u3099")
- assert_cursor(6)
- assert_cursor_max(6)
+ assert_line_around_cursor("ã‹\u3099ã\u3099ã\u3099", '')
input_keys("\C-b", false)
- assert_byte_pointer_size("ã‹\u3099ã\u3099")
- assert_cursor(4)
- assert_cursor_max(6)
+ assert_line_around_cursor("ã‹\u3099ã\u3099", "ã\u3099")
input_keys("\C-b", false)
- assert_byte_pointer_size("ã‹\u3099")
- assert_cursor(2)
- assert_cursor_max(6)
+ assert_line_around_cursor("ã‹\u3099", "ã\u3099ã\u3099")
input_keys("\C-b", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(6)
+ assert_line_around_cursor('', "ã‹\u3099ã\u3099ã\u3099")
input_keys("\C-b", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(6)
+ assert_line_around_cursor('', "ã‹\u3099ã\u3099ã\u3099")
input_keys("\C-f", false)
- assert_byte_pointer_size("ã‹\u3099")
- assert_cursor(2)
- assert_cursor_max(6)
+ assert_line_around_cursor("ã‹\u3099", "ã\u3099ã\u3099")
input_keys("\C-f", false)
- assert_byte_pointer_size("ã‹\u3099ã\u3099")
- assert_cursor(4)
- assert_cursor_max(6)
+ assert_line_around_cursor("ã‹\u3099ã\u3099", "ã\u3099")
input_keys("\C-f", false)
- assert_byte_pointer_size("ã‹\u3099ã\u3099ã\u3099")
- assert_cursor(6)
- assert_cursor_max(6)
+ assert_line_around_cursor("ã‹\u3099ã\u3099ã\u3099", '')
input_keys("\C-f", false)
- assert_byte_pointer_size("ã‹\u3099ã\u3099ã\u3099")
- assert_cursor(6)
- assert_cursor_max(6)
+ assert_line_around_cursor("ã‹\u3099ã\u3099ã\u3099", '')
end
def test_em_capitol_case
input_keys('abc def{bbb}ccc')
input_keys("\C-a\M-c", false)
- assert_byte_pointer_size('Abc')
- assert_cursor(3)
- assert_cursor_max(15)
- assert_line('Abc def{bbb}ccc')
+ assert_line_around_cursor('Abc', ' def{bbb}ccc')
input_keys("\M-c", false)
- assert_byte_pointer_size('Abc Def')
- assert_cursor(7)
- assert_cursor_max(15)
- assert_line('Abc Def{bbb}ccc')
+ assert_line_around_cursor('Abc Def', '{bbb}ccc')
input_keys("\M-c", false)
- assert_byte_pointer_size('Abc Def{Bbb')
- assert_cursor(11)
- assert_cursor_max(15)
- assert_line('Abc Def{Bbb}ccc')
+ assert_line_around_cursor('Abc Def{Bbb', '}ccc')
input_keys("\M-c", false)
- assert_byte_pointer_size('Abc Def{Bbb}Ccc')
- assert_cursor(15)
- assert_cursor_max(15)
- assert_line('Abc Def{Bbb}Ccc')
+ assert_line_around_cursor('Abc Def{Bbb}Ccc', '')
end
def test_em_capitol_case_with_complex_example
input_keys('{}#* AaA!!!cCc ')
input_keys("\C-a\M-c", false)
- assert_byte_pointer_size('{}#* Aaa')
- assert_cursor(11)
- assert_cursor_max(20)
- assert_line('{}#* Aaa!!!cCc ')
+ assert_line_around_cursor('{}#* Aaa', '!!!cCc ')
input_keys("\M-c", false)
- assert_byte_pointer_size('{}#* Aaa!!!Ccc')
- assert_cursor(17)
- assert_cursor_max(20)
- assert_line('{}#* Aaa!!!Ccc ')
+ assert_line_around_cursor('{}#* Aaa!!!Ccc', ' ')
input_keys("\M-c", false)
- assert_byte_pointer_size('{}#* Aaa!!!Ccc ')
- assert_cursor(20)
- assert_cursor_max(20)
- assert_line('{}#* Aaa!!!Ccc ')
+ assert_line_around_cursor('{}#* Aaa!!!Ccc ', '')
end
def test_em_lower_case
input_keys('AbC def{bBb}CCC')
input_keys("\C-a\M-l", false)
- assert_byte_pointer_size('abc')
- assert_cursor(3)
- assert_cursor_max(15)
- assert_line('abc def{bBb}CCC')
+ assert_line_around_cursor('abc', ' def{bBb}CCC')
input_keys("\M-l", false)
- assert_byte_pointer_size('abc def')
- assert_cursor(7)
- assert_cursor_max(15)
- assert_line('abc def{bBb}CCC')
+ assert_line_around_cursor('abc def', '{bBb}CCC')
input_keys("\M-l", false)
- assert_byte_pointer_size('abc def{bbb')
- assert_cursor(11)
- assert_cursor_max(15)
- assert_line('abc def{bbb}CCC')
+ assert_line_around_cursor('abc def{bbb', '}CCC')
input_keys("\M-l", false)
- assert_byte_pointer_size('abc def{bbb}ccc')
- assert_cursor(15)
- assert_cursor_max(15)
- assert_line('abc def{bbb}ccc')
+ assert_line_around_cursor('abc def{bbb}ccc', '')
end
def test_em_lower_case_with_complex_example
input_keys('{}#* AaA!!!cCc ')
input_keys("\C-a\M-l", false)
- assert_byte_pointer_size('{}#* aaa')
- assert_cursor(11)
- assert_cursor_max(20)
- assert_line('{}#* aaa!!!cCc ')
+ assert_line_around_cursor('{}#* aaa', '!!!cCc ')
input_keys("\M-l", false)
- assert_byte_pointer_size('{}#* aaa!!!ccc')
- assert_cursor(17)
- assert_cursor_max(20)
- assert_line('{}#* aaa!!!ccc ')
+ assert_line_around_cursor('{}#* aaa!!!ccc', ' ')
input_keys("\M-l", false)
- assert_byte_pointer_size('{}#* aaa!!!ccc ')
- assert_cursor(20)
- assert_cursor_max(20)
- assert_line('{}#* aaa!!!ccc ')
+ assert_line_around_cursor('{}#* aaa!!!ccc ', '')
end
def test_em_upper_case
input_keys('AbC def{bBb}CCC')
input_keys("\C-a\M-u", false)
- assert_byte_pointer_size('ABC')
- assert_cursor(3)
- assert_cursor_max(15)
- assert_line('ABC def{bBb}CCC')
+ assert_line_around_cursor('ABC', ' def{bBb}CCC')
input_keys("\M-u", false)
- assert_byte_pointer_size('ABC DEF')
- assert_cursor(7)
- assert_cursor_max(15)
- assert_line('ABC DEF{bBb}CCC')
+ assert_line_around_cursor('ABC DEF', '{bBb}CCC')
input_keys("\M-u", false)
- assert_byte_pointer_size('ABC DEF{BBB')
- assert_cursor(11)
- assert_cursor_max(15)
- assert_line('ABC DEF{BBB}CCC')
+ assert_line_around_cursor('ABC DEF{BBB', '}CCC')
input_keys("\M-u", false)
- assert_byte_pointer_size('ABC DEF{BBB}CCC')
- assert_cursor(15)
- assert_cursor_max(15)
- assert_line('ABC DEF{BBB}CCC')
+ assert_line_around_cursor('ABC DEF{BBB}CCC', '')
end
def test_em_upper_case_with_complex_example
input_keys('{}#* AaA!!!cCc ')
input_keys("\C-a\M-u", false)
- assert_byte_pointer_size('{}#* AAA')
- assert_cursor(11)
- assert_cursor_max(20)
- assert_line('{}#* AAA!!!cCc ')
+ assert_line_around_cursor('{}#* AAA', '!!!cCc ')
input_keys("\M-u", false)
- assert_byte_pointer_size('{}#* AAA!!!CCC')
- assert_cursor(17)
- assert_cursor_max(20)
- assert_line('{}#* AAA!!!CCC ')
+ assert_line_around_cursor('{}#* AAA!!!CCC', ' ')
input_keys("\M-u", false)
- assert_byte_pointer_size('{}#* AAA!!!CCC ')
- assert_cursor(20)
- assert_cursor_max(20)
- assert_line('{}#* AAA!!!CCC ')
+ assert_line_around_cursor('{}#* AAA!!!CCC ', '')
end
def test_em_delete_or_list
@@ -1286,28 +726,16 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
}
}
input_keys('fooo')
- assert_byte_pointer_size('fooo')
- assert_cursor(4)
- assert_cursor_max(4)
- assert_line('fooo')
+ assert_line_around_cursor('fooo', '')
assert_equal(nil, @line_editor.instance_variable_get(:@menu_info))
input_keys("\C-b", false)
- assert_byte_pointer_size('foo')
- assert_cursor(3)
- assert_cursor_max(4)
- assert_line('fooo')
+ assert_line_around_cursor('foo', 'o')
assert_equal(nil, @line_editor.instance_variable_get(:@menu_info))
@line_editor.input_key(Reline::Key.new(:em_delete_or_list, :em_delete_or_list, false))
- assert_byte_pointer_size('foo')
- assert_cursor(3)
- assert_cursor_max(3)
- assert_line('foo')
+ assert_line_around_cursor('foo', '')
assert_equal(nil, @line_editor.instance_variable_get(:@menu_info))
@line_editor.input_key(Reline::Key.new(:em_delete_or_list, :em_delete_or_list, false))
- assert_byte_pointer_size('foo')
- assert_cursor(3)
- assert_cursor_max(3)
- assert_line('foo')
+ assert_line_around_cursor('foo', '')
assert_equal(%w{foo_foo foo_bar foo_baz}, @line_editor.instance_variable_get(:@menu_info).list)
end
@@ -1322,22 +750,13 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
}
}
input_keys('foo_')
- assert_byte_pointer_size('foo_')
- assert_cursor(4)
- assert_cursor_max(4)
- assert_line('foo_')
+ assert_line_around_cursor('foo_', '')
assert_equal(nil, @line_editor.instance_variable_get(:@menu_info))
input_keys("\C-i", false)
- assert_byte_pointer_size('foo_')
- assert_cursor(4)
- assert_cursor_max(4)
- assert_line('foo_')
+ assert_line_around_cursor('foo_', '')
assert_equal(nil, @line_editor.instance_variable_get(:@menu_info))
input_keys("\C-i", false)
- assert_byte_pointer_size('foo_')
- assert_cursor(4)
- assert_cursor_max(4)
- assert_line('foo_')
+ assert_line_around_cursor('foo_', '')
assert_equal(%w{foo_foo foo_bar}, @line_editor.instance_variable_get(:@menu_info).list)
end
@@ -1353,36 +772,67 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
}
}
input_keys('fo')
- assert_byte_pointer_size('fo')
- assert_cursor(2)
- assert_cursor_max(2)
- assert_line('fo')
+ assert_line_around_cursor('fo', '')
assert_equal(nil, @line_editor.instance_variable_get(:@menu_info))
input_keys("\C-i", false)
- assert_byte_pointer_size('foo_')
- assert_cursor(4)
- assert_cursor_max(4)
- assert_line('foo_')
+ assert_line_around_cursor('foo_', '')
assert_equal(nil, @line_editor.instance_variable_get(:@menu_info))
input_keys("\C-i", false)
- assert_byte_pointer_size('foo_')
- assert_cursor(4)
- assert_cursor_max(4)
- assert_line('foo_')
+ assert_line_around_cursor('foo_', '')
assert_equal(%w{foo_foo foo_bar foo_baz}, @line_editor.instance_variable_get(:@menu_info).list)
input_keys('a')
input_keys("\C-i", false)
- assert_byte_pointer_size('foo_a')
- assert_cursor(5)
- assert_cursor_max(5)
- assert_line('foo_a')
+ assert_line_around_cursor('foo_a', '')
input_keys("\C-h", false)
input_keys('b')
input_keys("\C-i", false)
- assert_byte_pointer_size('foo_ba')
- assert_cursor(6)
- assert_cursor_max(6)
- assert_line('foo_ba')
+ assert_line_around_cursor('foo_ba', '')
+ end
+
+ def test_autocompletion_with_upward_navigation
+ @config.autocompletion = true
+ @line_editor.completion_proc = proc { |word|
+ %w{
+ Readline
+ Regexp
+ RegexpError
+ }.map { |i|
+ i.encode(@encoding)
+ }
+ }
+ input_keys('Re')
+ assert_line_around_cursor('Re', '')
+ input_keys("\C-i", false)
+ assert_line_around_cursor('Readline', '')
+ input_keys("\C-i", false)
+ assert_line_around_cursor('Regexp', '')
+ @line_editor.input_key(Reline::Key.new(:completion_journey_up, :completion_journey_up, false))
+ assert_line_around_cursor('Readline', '')
+ ensure
+ @config.autocompletion = false
+ end
+
+ def test_autocompletion_with_upward_navigation_and_menu_complete_backward
+ @config.autocompletion = true
+ @line_editor.completion_proc = proc { |word|
+ %w{
+ Readline
+ Regexp
+ RegexpError
+ }.map { |i|
+ i.encode(@encoding)
+ }
+ }
+ input_keys('Re')
+ assert_line_around_cursor('Re', '')
+ input_keys("\C-i", false)
+ assert_line_around_cursor('Readline', '')
+ input_keys("\C-i", false)
+ assert_line_around_cursor('Regexp', '')
+ @line_editor.input_key(Reline::Key.new(:menu_complete_backward, :menu_complete_backward, false))
+ assert_line_around_cursor('Readline', '')
+ ensure
+ @config.autocompletion = false
end
def test_completion_with_indent
@@ -1397,22 +847,13 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
}
}
input_keys(' fo')
- assert_byte_pointer_size(' fo')
- assert_cursor(4)
- assert_cursor_max(4)
- assert_line(' fo')
+ assert_line_around_cursor(' fo', '')
assert_equal(nil, @line_editor.instance_variable_get(:@menu_info))
input_keys("\C-i", false)
- assert_byte_pointer_size(' foo_')
- assert_cursor(6)
- assert_cursor_max(6)
- assert_line(' foo_')
+ assert_line_around_cursor(' foo_', '')
assert_equal(nil, @line_editor.instance_variable_get(:@menu_info))
input_keys("\C-i", false)
- assert_byte_pointer_size(' foo_')
- assert_cursor(6)
- assert_cursor_max(6)
- assert_line(' foo_')
+ assert_line_around_cursor(' foo_', '')
assert_equal(%w{foo_foo foo_bar foo_baz}, @line_editor.instance_variable_get(:@menu_info).list)
end
@@ -1428,22 +869,13 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
}
}
input_keys(' "".fo')
- assert_byte_pointer_size(' "".fo')
- assert_cursor(7)
- assert_cursor_max(7)
- assert_line(' "".fo')
+ assert_line_around_cursor(' "".fo', '')
assert_equal(nil, @line_editor.instance_variable_get(:@menu_info))
input_keys("\C-i", false)
- assert_byte_pointer_size(' "".foo_')
- assert_cursor(9)
- assert_cursor_max(9)
- assert_line(' "".foo_')
+ assert_line_around_cursor(' "".foo_', '')
assert_equal(nil, @line_editor.instance_variable_get(:@menu_info))
input_keys("\C-i", false)
- assert_byte_pointer_size(' "".foo_')
- assert_cursor(9)
- assert_cursor_max(9)
- assert_line(' "".foo_')
+ assert_line_around_cursor(' "".foo_', '')
assert_equal(%w{"".foo_foo "".foo_bar "".foo_baz}, @line_editor.instance_variable_get(:@menu_info).list)
end
@@ -1461,54 +893,33 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
matched = m
}
input_keys('fo')
- assert_byte_pointer_size('fo')
- assert_cursor(2)
- assert_cursor_max(2)
- assert_line('fo')
+ assert_line_around_cursor('fo', '')
assert_equal(Reline::LineEditor::CompletionState::NORMAL, @line_editor.instance_variable_get(:@completion_state))
assert_equal(nil, matched)
input_keys("\C-i", false)
- assert_byte_pointer_size('foo')
- assert_cursor(3)
- assert_cursor_max(3)
- assert_line('foo')
+ assert_line_around_cursor('foo', '')
assert_equal(Reline::LineEditor::CompletionState::MENU_WITH_PERFECT_MATCH, @line_editor.instance_variable_get(:@completion_state))
assert_equal(nil, matched)
input_keys("\C-i", false)
- assert_byte_pointer_size('foo')
- assert_cursor(3)
- assert_cursor_max(3)
- assert_line('foo')
+ assert_line_around_cursor('foo', '')
assert_equal(Reline::LineEditor::CompletionState::PERFECT_MATCH, @line_editor.instance_variable_get(:@completion_state))
assert_equal(nil, matched)
input_keys("\C-i", false)
- assert_byte_pointer_size('foo')
- assert_cursor(3)
- assert_cursor_max(3)
- assert_line('foo')
+ assert_line_around_cursor('foo', '')
assert_equal(Reline::LineEditor::CompletionState::PERFECT_MATCH, @line_editor.instance_variable_get(:@completion_state))
assert_equal('foo', matched)
matched = nil
input_keys('_')
input_keys("\C-i", false)
- assert_byte_pointer_size('foo_bar')
- assert_cursor(7)
- assert_cursor_max(7)
- assert_line('foo_bar')
+ assert_line_around_cursor('foo_bar', '')
assert_equal(Reline::LineEditor::CompletionState::MENU_WITH_PERFECT_MATCH, @line_editor.instance_variable_get(:@completion_state))
assert_equal(nil, matched)
input_keys("\C-i", false)
- assert_byte_pointer_size('foo_bar')
- assert_cursor(7)
- assert_cursor_max(7)
- assert_line('foo_bar')
+ assert_line_around_cursor('foo_bar', '')
assert_equal(Reline::LineEditor::CompletionState::PERFECT_MATCH, @line_editor.instance_variable_get(:@completion_state))
assert_equal(nil, matched)
input_keys("\C-i", false)
- assert_byte_pointer_size('foo_bar')
- assert_cursor(7)
- assert_cursor_max(7)
- assert_line('foo_bar')
+ assert_line_around_cursor('foo_bar', '')
assert_equal(Reline::LineEditor::CompletionState::PERFECT_MATCH, @line_editor.instance_variable_get(:@completion_state))
assert_equal('foo_bar', matched)
end
@@ -1525,43 +936,25 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
}
}
input_keys('fo')
- assert_byte_pointer_size('fo')
- assert_cursor(2)
- assert_cursor_max(2)
- assert_line('fo')
+ assert_line_around_cursor('fo', '')
assert_equal(nil, @line_editor.instance_variable_get(:@menu_info))
input_keys("\C-i", false)
- assert_byte_pointer_size('foo_')
- assert_cursor(4)
- assert_cursor_max(4)
- assert_line('foo_')
+ assert_line_around_cursor('foo_', '')
assert_equal(nil, @line_editor.instance_variable_get(:@menu_info))
input_keys("\C-i", false)
- assert_byte_pointer_size('foo_')
- assert_cursor(4)
- assert_cursor_max(4)
- assert_line('foo_')
+ assert_line_around_cursor('foo_', '')
assert_equal(%w{foo_foo foo_bar}, @line_editor.instance_variable_get(:@menu_info).list)
@config.completion_ignore_case = true
input_keys("\C-i", false)
- assert_byte_pointer_size('foo_')
- assert_cursor(4)
- assert_cursor_max(4)
- assert_line('foo_')
+ assert_line_around_cursor('foo_', '')
assert_equal(%w{foo_foo foo_bar Foo_baz}, @line_editor.instance_variable_get(:@menu_info).list)
input_keys('a')
input_keys("\C-i", false)
- assert_byte_pointer_size('foo_a')
- assert_cursor(5)
- assert_cursor_max(5)
- assert_line('foo_a')
+ assert_line_around_cursor('foo_a', '')
input_keys("\C-h", false)
input_keys('b')
input_keys("\C-i", false)
- assert_byte_pointer_size('foo_ba')
- assert_cursor(6)
- assert_cursor_max(6)
- assert_line('foo_ba')
+ assert_line_around_cursor('foo_ba', '')
end
def test_completion_in_middle_of_line
@@ -1576,17 +969,11 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
}
}
input_keys('abcde fo ABCDE')
- assert_line('abcde fo ABCDE')
+ assert_line_around_cursor('abcde fo ABCDE', '')
input_keys("\C-b" * 6 + "\C-i", false)
- assert_byte_pointer_size('abcde foo_')
- assert_cursor(10)
- assert_cursor_max(16)
- assert_line('abcde foo_ ABCDE')
+ assert_line_around_cursor('abcde foo_', ' ABCDE')
input_keys("\C-b" * 2 + "\C-i", false)
- assert_byte_pointer_size('abcde foo_')
- assert_cursor(10)
- assert_cursor_max(18)
- assert_line('abcde foo_o_ ABCDE')
+ assert_line_around_cursor('abcde foo_', 'o_ ABCDE')
end
def test_completion_with_nil_value
@@ -1602,125 +989,65 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
}
@config.completion_ignore_case = true
input_keys('fo')
- assert_byte_pointer_size('fo')
- assert_cursor(2)
- assert_cursor_max(2)
- assert_line('fo')
+ assert_line_around_cursor('fo', '')
assert_equal(nil, @line_editor.instance_variable_get(:@menu_info))
input_keys("\C-i", false)
- assert_byte_pointer_size('foo_')
- assert_cursor(4)
- assert_cursor_max(4)
- assert_line('foo_')
+ assert_line_around_cursor('foo_', '')
assert_equal(nil, @line_editor.instance_variable_get(:@menu_info))
input_keys("\C-i", false)
- assert_byte_pointer_size('foo_')
- assert_cursor(4)
- assert_cursor_max(4)
- assert_line('foo_')
+ assert_line_around_cursor('foo_', '')
assert_equal(%w{foo_foo foo_bar Foo_baz}, @line_editor.instance_variable_get(:@menu_info).list)
input_keys('a')
input_keys("\C-i", false)
- assert_byte_pointer_size('foo_a')
- assert_cursor(5)
- assert_cursor_max(5)
- assert_line('foo_a')
+ assert_line_around_cursor('foo_a', '')
input_keys("\C-h", false)
input_keys('b')
input_keys("\C-i", false)
- assert_byte_pointer_size('foo_ba')
- assert_cursor(6)
- assert_cursor_max(6)
- assert_line('foo_ba')
+ assert_line_around_cursor('foo_ba', '')
end
def test_em_kill_region
input_keys('abc def{bbb}ccc ddd ')
- assert_byte_pointer_size('abc def{bbb}ccc ddd ')
- assert_cursor(26)
- assert_cursor_max(26)
- assert_line('abc def{bbb}ccc ddd ')
+ assert_line_around_cursor('abc def{bbb}ccc ddd ', '')
input_keys("\C-w", false)
- assert_byte_pointer_size('abc def{bbb}ccc ')
- assert_cursor(20)
- assert_cursor_max(20)
- assert_line('abc def{bbb}ccc ')
+ assert_line_around_cursor('abc def{bbb}ccc ', '')
input_keys("\C-w", false)
- assert_byte_pointer_size('abc ')
- assert_cursor(6)
- assert_cursor_max(6)
- assert_line('abc ')
+ assert_line_around_cursor('abc ', '')
input_keys("\C-w", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
input_keys("\C-w", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
end
def test_em_kill_region_mbchar
input_keys('ã‚ ã„ ã†{ã†}ㆠ')
- assert_byte_pointer_size('ã‚ ã„ ã†{ã†}ㆠ')
- assert_cursor(21)
- assert_cursor_max(21)
- assert_line('ã‚ ã„ ã†{ã†}ㆠ')
+ assert_line_around_cursor('ã‚ ã„ ã†{ã†}ㆠ', '')
input_keys("\C-w", false)
- assert_byte_pointer_size('ã‚ ã„ ')
- assert_cursor(10)
- assert_cursor_max(10)
- assert_line('ã‚ ã„ ')
+ assert_line_around_cursor('ã‚ ã„ ', '')
input_keys("\C-w", false)
- assert_byte_pointer_size('ã‚ ')
- assert_cursor(5)
- assert_cursor_max(5)
- assert_line('ã‚ ')
+ assert_line_around_cursor('ã‚ ', '')
input_keys("\C-w", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
end
def test_vi_search_prev
Reline::HISTORY.concat(%w{abc 123 AAA})
- assert_line('')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('', '')
input_keys("\C-ra\C-j")
- assert_line('abc')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(3)
+ assert_line_around_cursor('', 'abc')
end
def test_larger_histories_than_history_size
history_size = @config.history_size
@config.history_size = 2
Reline::HISTORY.concat(%w{abc 123 AAA})
- assert_line('')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('', '')
input_keys("\C-p")
- assert_line('AAA')
- assert_byte_pointer_size('AAA')
- assert_cursor(3)
- assert_cursor_max(3)
+ assert_line_around_cursor('AAA', '')
input_keys("\C-p")
- assert_line('123')
- assert_byte_pointer_size('123')
- assert_cursor(3)
- assert_cursor_max(3)
+ assert_line_around_cursor('123', '')
input_keys("\C-p")
- assert_line('123')
- assert_byte_pointer_size('123')
- assert_cursor(3)
- assert_cursor_max(3)
+ assert_line_around_cursor('123', '')
ensure
@config.history_size = history_size
end
@@ -1731,25 +1058,13 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
'12aa',
'1234' # new
])
- assert_line('')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('', '')
input_keys("\C-r123")
- assert_line('1234')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0) # doesn't determine yet
+ assert_line_around_cursor('1234', '')
input_keys("\C-ha")
- assert_line('12aa')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('12aa', '')
input_keys("\C-h3")
- assert_line('1235')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('1235', '')
end
def test_search_history_to_front
@@ -1758,25 +1073,13 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
'12aa',
'1234' # new
])
- assert_line('')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('', '')
input_keys("\C-s123")
- assert_line('1235')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0) # doesn't determine yet
+ assert_line_around_cursor('1235', '')
input_keys("\C-ha")
- assert_line('12aa')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('12aa', '')
input_keys("\C-h3")
- assert_line('1234')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('1234', '')
end
def test_search_history_front_and_back
@@ -1785,30 +1088,15 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
'12aa',
'1234' # new
])
- assert_line('')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('', '')
input_keys("\C-s12")
- assert_line('1235')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0) # doesn't determine yet
+ assert_line_around_cursor('1235', '')
input_keys("\C-s")
- assert_line('12aa')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('12aa', '')
input_keys("\C-r")
- assert_line('12aa')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('12aa', '')
input_keys("\C-r")
- assert_line('1235')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('1235', '')
end
def test_search_history_back_and_front
@@ -1817,30 +1105,15 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
'12aa',
'1234' # new
])
- assert_line('')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('', '')
input_keys("\C-r12")
- assert_line('1234')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0) # doesn't determine yet
+ assert_line_around_cursor('1234', '')
input_keys("\C-r")
- assert_line('12aa')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('12aa', '')
input_keys("\C-s")
- assert_line('12aa')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('12aa', '')
input_keys("\C-s")
- assert_line('1234')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('1234', '')
end
def test_search_history_to_back_in_the_middle_of_histories
@@ -1849,20 +1122,11 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
'12aa',
'1234' # new
])
- assert_line('')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('', '')
input_keys("\C-p\C-p")
- assert_line('12aa')
- assert_byte_pointer_size('12aa')
- assert_cursor(4)
- assert_cursor_max(4)
+ assert_line_around_cursor('12aa', '')
input_keys("\C-r123")
- assert_line('1235')
- assert_byte_pointer_size('1235')
- assert_cursor(4)
- assert_cursor_max(4)
+ assert_line_around_cursor('1235', '')
end
def test_search_history_twice
@@ -1871,20 +1135,11 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
'12aa',
'1234' # new
])
- assert_line('')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('', '')
input_keys("\C-r123")
- assert_line('1234')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0) # doesn't determine yet
+ assert_line_around_cursor('1234', '')
input_keys("\C-r")
- assert_line('1235')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('1235', '')
end
def test_search_history_by_last_determined
@@ -1893,35 +1148,17 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
'12aa',
'1234' # new
])
- assert_line('')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('', '')
input_keys("\C-r123")
- assert_line('1234')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0) # doesn't determine yet
+ assert_line_around_cursor('1234', '')
input_keys("\C-j")
- assert_line('1234')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(4)
+ assert_line_around_cursor('', '1234')
input_keys("\C-k") # delete
- assert_line('')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('', '')
input_keys("\C-r")
- assert_line('')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('', '')
input_keys("\C-r")
- assert_line('1235')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('1235', '')
end
def test_search_history_with_isearch_terminator
@@ -1933,76 +1170,40 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
'12aa',
'1234' # new
])
- assert_line('')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('', '')
input_keys("\C-r12a")
- assert_line('12aa')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0) # doesn't determine yet
+ assert_line_around_cursor('12aa', '')
input_keys('Y')
- assert_line('12aa')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(4)
+ assert_line_around_cursor('', '12aa')
input_keys('x')
- assert_line('x12aa')
- assert_byte_pointer_size('x')
- assert_cursor(1)
- assert_cursor_max(5)
+ assert_line_around_cursor('x', '12aa')
end
def test_em_set_mark_and_em_exchange_mark
input_keys('aaa bbb ccc ddd')
- assert_byte_pointer_size('aaa bbb ccc ddd')
- assert_cursor(15)
- assert_cursor_max(15)
- assert_line('aaa bbb ccc ddd')
+ assert_line_around_cursor('aaa bbb ccc ddd', '')
input_keys("\C-a\M-F\M-F", false)
- assert_byte_pointer_size('aaa bbb')
- assert_cursor(7)
- assert_cursor_max(15)
- assert_line('aaa bbb ccc ddd')
+ assert_line_around_cursor('aaa bbb', ' ccc ddd')
assert_equal(nil, @line_editor.instance_variable_get(:@mark_pointer))
input_keys("\x00", false) # C-Space
- assert_byte_pointer_size('aaa bbb')
- assert_cursor(7)
- assert_cursor_max(15)
- assert_line('aaa bbb ccc ddd')
+ assert_line_around_cursor('aaa bbb', ' ccc ddd')
assert_equal([7, 0], @line_editor.instance_variable_get(:@mark_pointer))
input_keys("\C-a", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(15)
- assert_line('aaa bbb ccc ddd')
+ assert_line_around_cursor('', 'aaa bbb ccc ddd')
assert_equal([7, 0], @line_editor.instance_variable_get(:@mark_pointer))
input_key_by_symbol(:em_exchange_mark)
- assert_byte_pointer_size('aaa bbb')
- assert_cursor(7)
- assert_cursor_max(15)
- assert_line('aaa bbb ccc ddd')
+ assert_line_around_cursor('aaa bbb', ' ccc ddd')
assert_equal([0, 0], @line_editor.instance_variable_get(:@mark_pointer))
end
def test_em_exchange_mark_without_mark
input_keys('aaa bbb ccc ddd')
- assert_byte_pointer_size('aaa bbb ccc ddd')
- assert_cursor(15)
- assert_cursor_max(15)
- assert_line('aaa bbb ccc ddd')
+ assert_line_around_cursor('aaa bbb ccc ddd', '')
input_keys("\C-a\M-f", false)
- assert_byte_pointer_size('aaa')
- assert_cursor(3)
- assert_cursor_max(15)
- assert_line('aaa bbb ccc ddd')
+ assert_line_around_cursor('aaa', ' bbb ccc ddd')
assert_equal(nil, @line_editor.instance_variable_get(:@mark_pointer))
input_key_by_symbol(:em_exchange_mark)
- assert_byte_pointer_size('aaa')
- assert_cursor(3)
- assert_cursor_max(15)
- assert_line('aaa bbb ccc ddd')
+ assert_line_around_cursor('aaa', ' bbb ccc ddd')
assert_equal(nil, @line_editor.instance_variable_get(:@mark_pointer))
end
@@ -2013,7 +1214,7 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
$VERBOSE = verbose
@line_editor.output_modifier_proc = proc { |output| Reline::Unicode.escape_for_print(output) }
input_keys("abcdef\n")
- result = @line_editor.__send__(:modify_lines, @line_editor.whole_lines)
+ result = @line_editor.__send__(:modify_lines, @line_editor.whole_lines, @line_editor.finished?)
$/ = nil
assert_equal(['abcdef'], result)
ensure
@@ -2031,20 +1232,11 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
input_keys('123')
# The ed_search_prev_history doesn't have default binding
@line_editor.__send__(:ed_search_prev_history, "\C-p".ord)
- assert_byte_pointer_size('123')
- assert_cursor(3)
- assert_cursor_max(5)
- assert_line('12345')
+ assert_line_around_cursor('123', '45')
@line_editor.__send__(:ed_search_prev_history, "\C-p".ord)
- assert_byte_pointer_size('123')
- assert_cursor(3)
- assert_cursor_max(5)
- assert_line('12356')
+ assert_line_around_cursor('123', '56')
@line_editor.__send__(:ed_search_prev_history, "\C-p".ord)
- assert_byte_pointer_size('123')
- assert_cursor(3)
- assert_cursor_max(5)
- assert_line('12356')
+ assert_line_around_cursor('123', '56')
end
def test_ed_search_prev_history_with_empty
@@ -2055,25 +1247,13 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
])
# The ed_search_prev_history doesn't have default binding
@line_editor.__send__(:ed_search_prev_history, "\C-p".ord)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(5)
- assert_line('12345')
+ assert_line_around_cursor('', '12345')
@line_editor.__send__(:ed_search_prev_history, "\C-p".ord)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(5)
- assert_line('12aaa')
+ assert_line_around_cursor('', '12aaa')
@line_editor.__send__(:ed_search_prev_history, "\C-p".ord)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(5)
- assert_line('12356')
+ assert_line_around_cursor('', '12356')
@line_editor.__send__(:ed_search_prev_history, "\C-p".ord)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(5)
- assert_line('12356')
+ assert_line_around_cursor('', '12356')
end
def test_ed_search_prev_history_without_match
@@ -2085,10 +1265,7 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
input_keys('ABC')
# The ed_search_prev_history doesn't have default binding
@line_editor.__send__(:ed_search_prev_history, "\C-p".ord)
- assert_byte_pointer_size('ABC')
- assert_cursor(3)
- assert_cursor_max(3)
- assert_line('ABC')
+ assert_line_around_cursor('ABC', '')
end
def test_ed_search_next_history
@@ -2100,30 +1277,15 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
input_keys('123')
# The ed_search_prev_history and ed_search_next_history doesn't have default binding
@line_editor.__send__(:ed_search_prev_history, "\C-p".ord)
- assert_byte_pointer_size('123')
- assert_cursor(3)
- assert_cursor_max(5)
- assert_line('12345')
+ assert_line_around_cursor('123', '45')
@line_editor.__send__(:ed_search_prev_history, "\C-p".ord)
- assert_byte_pointer_size('123')
- assert_cursor(3)
- assert_cursor_max(5)
- assert_line('12356')
+ assert_line_around_cursor('123', '56')
@line_editor.__send__(:ed_search_prev_history, "\C-p".ord)
- assert_byte_pointer_size('123')
- assert_cursor(3)
- assert_cursor_max(5)
- assert_line('12356')
+ assert_line_around_cursor('123', '56')
@line_editor.__send__(:ed_search_next_history, "\C-n".ord)
- assert_byte_pointer_size('123')
- assert_cursor(3)
- assert_cursor_max(5)
- assert_line('12345')
+ assert_line_around_cursor('123', '45')
@line_editor.__send__(:ed_search_next_history, "\C-n".ord)
- assert_byte_pointer_size('123')
- assert_cursor(3)
- assert_cursor_max(5)
- assert_line('12345')
+ assert_line_around_cursor('123', '45')
end
def test_ed_search_next_history_with_empty
@@ -2134,133 +1296,75 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
])
# The ed_search_prev_history and ed_search_next_history doesn't have default binding
@line_editor.__send__(:ed_search_prev_history, "\C-p".ord)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(5)
- assert_line('12345')
+ assert_line_around_cursor('', '12345')
@line_editor.__send__(:ed_search_prev_history, "\C-p".ord)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(5)
- assert_line('12aaa')
+ assert_line_around_cursor('', '12aaa')
@line_editor.__send__(:ed_search_prev_history, "\C-p".ord)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(5)
- assert_line('12356')
+ assert_line_around_cursor('', '12356')
@line_editor.__send__(:ed_search_next_history, "\C-n".ord)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(5)
- assert_line('12aaa')
+ assert_line_around_cursor('', '12aaa')
@line_editor.__send__(:ed_search_next_history, "\C-n".ord)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(5)
- assert_line('12345')
+ assert_line_around_cursor('', '12345')
@line_editor.__send__(:ed_search_next_history, "\C-n".ord)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
+ end
+
+ def test_incremental_search_history_cancel_by_symbol_key
+ # ed_prev_char should move cursor left and cancel incremental search
+ input_keys("abc\C-r")
+ input_key_by_symbol(:ed_prev_char)
+ input_keys('d')
+ assert_line_around_cursor('abd', 'c')
end
# Unicode emoji test
def test_ed_insert_for_include_zwj_emoji
- omit "This test is for UTF-8 but the locale is #{Reline::IOGate.encoding}" if Reline::IOGate.encoding != Encoding::UTF_8
+ omit "This test is for UTF-8 but the locale is #{Reline.core.encoding}" if Reline.core.encoding != Encoding::UTF_8
# U+1F468 U+200D U+1F469 U+200D U+1F467 U+200D U+1F466 is family: man, woman, girl, boy "👨â€ðŸ‘©â€ðŸ‘§â€ðŸ‘¦"
input_keys("\u{1F468}") # U+1F468 is man "👨"
- assert_line("\u{1F468}")
- assert_byte_pointer_size("\u{1F468}")
- assert_cursor(2)
- assert_cursor_max(2)
+ assert_line_around_cursor('👨', '')
input_keys("\u200D") # U+200D is ZERO WIDTH JOINER
- assert_line("\u{1F468 200D}")
- assert_byte_pointer_size("\u{1F468 200D}")
- assert_cursor(2)
- assert_cursor_max(2)
+ assert_line_around_cursor('👨â€', '')
input_keys("\u{1F469}") # U+1F469 is woman "👩"
- assert_line("\u{1F468 200D 1F469}")
- assert_byte_pointer_size("\u{1F468 200D 1F469}")
- assert_cursor(2)
- assert_cursor_max(2)
+ assert_line_around_cursor('👨â€ðŸ‘©', '')
input_keys("\u200D") # U+200D is ZERO WIDTH JOINER
- assert_line("\u{1F468 200D 1F469 200D}")
- assert_byte_pointer_size("\u{1F468 200D 1F469 200D}")
- assert_cursor(2)
- assert_cursor_max(2)
+ assert_line_around_cursor('👨â€ðŸ‘©â€', '')
input_keys("\u{1F467}") # U+1F467 is girl "👧"
- assert_line("\u{1F468 200D 1F469 200D 1F467}")
- assert_byte_pointer_size("\u{1F468 200D 1F469 200D 1F467}")
- assert_cursor(2)
- assert_cursor_max(2)
+ assert_line_around_cursor('👨â€ðŸ‘©â€ðŸ‘§', '')
input_keys("\u200D") # U+200D is ZERO WIDTH JOINER
- assert_line("\u{1F468 200D 1F469 200D 1F467 200D}")
- assert_byte_pointer_size("\u{1F468 200D 1F469 200D 1F467 200D}")
- assert_cursor(2)
- assert_cursor_max(2)
+ assert_line_around_cursor('👨â€ðŸ‘©â€ðŸ‘§â€', '')
input_keys("\u{1F466}") # U+1F466 is boy "👦"
- assert_line("\u{1F468 200D 1F469 200D 1F467 200D 1F466}")
- assert_byte_pointer_size("\u{1F468 200D 1F469 200D 1F467 200D 1F466}")
- assert_cursor(2)
- assert_cursor_max(2)
+ assert_line_around_cursor('👨â€ðŸ‘©â€ðŸ‘§â€ðŸ‘¦', '')
# U+1F468 U+200D U+1F469 U+200D U+1F467 U+200D U+1F466 is family: man, woman, girl, boy "👨â€ðŸ‘©â€ðŸ‘§â€ðŸ‘¦"
input_keys("\u{1F468 200D 1F469 200D 1F467 200D 1F466}")
- assert_line("\u{1F468 200D 1F469 200D 1F467 200D 1F466 1F468 200D 1F469 200D 1F467 200D 1F466}")
- assert_byte_pointer_size("\u{1F468 200D 1F469 200D 1F467 200D 1F466 1F468 200D 1F469 200D 1F467 200D 1F466}")
- assert_cursor(4)
- assert_cursor_max(4)
+ assert_line_around_cursor('👨â€ðŸ‘©â€ðŸ‘§â€ðŸ‘¦ðŸ‘¨â€ðŸ‘©â€ðŸ‘§â€ðŸ‘¦', '')
end
def test_ed_insert_for_include_valiation_selector
- omit "This test is for UTF-8 but the locale is #{Reline::IOGate.encoding}" if Reline::IOGate.encoding != Encoding::UTF_8
+ omit "This test is for UTF-8 but the locale is #{Reline.core.encoding}" if Reline.core.encoding != Encoding::UTF_8
# U+0030 U+FE00 is DIGIT ZERO + VARIATION SELECTOR-1 "0︀"
input_keys("\u0030") # U+0030 is DIGIT ZERO
- assert_line("\u0030")
- assert_byte_pointer_size("\u0030")
- assert_cursor(1)
- assert_cursor_max(1)
+ assert_line_around_cursor('0', '')
input_keys("\uFE00") # U+FE00 is VARIATION SELECTOR-1
- assert_line("\u{0030 FE00}")
- assert_byte_pointer_size("\u{0030 FE00}")
- assert_cursor(1)
- assert_cursor_max(1)
+ assert_line_around_cursor('0︀', '')
end
def test_em_yank_pop
input_keys("def hoge\C-w\C-b\C-f\C-w", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
input_keys("\C-y", false)
- assert_byte_pointer_size('def ')
- assert_cursor(4)
- assert_cursor_max(4)
- assert_line('def ')
+ assert_line_around_cursor('def ', '')
input_keys("\M-\C-y", false)
- assert_byte_pointer_size('hoge')
- assert_cursor(4)
- assert_cursor_max(4)
- assert_line('hoge')
+ assert_line_around_cursor('hoge', '')
end
def test_em_kill_region_with_kill_ring
input_keys("def hoge\C-b\C-b\C-b\C-b", false)
- assert_byte_pointer_size('def ')
- assert_cursor(4)
- assert_cursor_max(8)
- assert_line('def hoge')
+ assert_line_around_cursor('def ', 'hoge')
input_keys("\C-k\C-w", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
input_keys("\C-y", false)
- assert_byte_pointer_size('def hoge')
- assert_cursor(8)
- assert_cursor_max(8)
- assert_line('def hoge')
+ assert_line_around_cursor('def hoge', '')
end
def test_ed_search_prev_next_history_in_multibyte
@@ -2276,104 +1380,65 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
assert_whole_lines(['def foo', ' 12345', 'end'])
assert_line_index(1)
assert_whole_lines(['def foo', ' 12345', 'end'])
- assert_byte_pointer_size(' 123')
- assert_cursor(5)
- assert_cursor_max(7)
- assert_line(' 12345')
+ assert_line_around_cursor(' 123', '45')
@line_editor.__send__(:ed_search_prev_history, "\C-p".ord)
assert_line_index(2)
assert_whole_lines(['def hoge', ' 67890', ' 12345', 'end'])
- assert_byte_pointer_size(' 123')
- assert_cursor(5)
- assert_cursor_max(7)
- assert_line(' 12345')
+ assert_line_around_cursor(' 123', '45')
@line_editor.__send__(:ed_search_prev_history, "\C-p".ord)
assert_line_index(2)
assert_whole_lines(['def hoge', ' 67890', ' 12345', 'end'])
- assert_byte_pointer_size(' 123')
- assert_cursor(5)
- assert_cursor_max(7)
- assert_line(' 12345')
+ assert_line_around_cursor(' 123', '45')
@line_editor.__send__(:ed_search_next_history, "\C-n".ord)
assert_line_index(1)
assert_whole_lines(['def foo', ' 12345', 'end'])
- assert_byte_pointer_size(' 123')
- assert_cursor(5)
- assert_cursor_max(7)
- assert_line(' 12345')
+ assert_line_around_cursor(' 123', '45')
@line_editor.__send__(:ed_search_next_history, "\C-n".ord)
assert_line_index(1)
assert_whole_lines(['def foo', ' 12345', 'end'])
- assert_byte_pointer_size(' 123')
- assert_cursor(5)
- assert_cursor_max(7)
- assert_line(' 12345')
+ assert_line_around_cursor(' 123', '45')
end
def test_ignore_NUL_by_ed_quoted_insert
input_keys(%Q{"\C-v\C-@"}, false)
- assert_byte_pointer_size('""')
- assert_cursor(2)
- assert_cursor_max(2)
+ assert_line_around_cursor('""', '')
end
def test_ed_argument_digit_by_meta_num
input_keys('abcdef')
- assert_byte_pointer_size('abcdef')
- assert_cursor(6)
- assert_cursor_max(6)
- assert_line('abcdef')
+ assert_line_around_cursor('abcdef', '')
input_keys("\M-2", false)
input_keys("\C-h", false)
- assert_byte_pointer_size('abcd')
- assert_cursor(4)
- assert_cursor_max(4)
- assert_line('abcd')
+ assert_line_around_cursor('abcd', '')
end
def test_halfwidth_kana_width_dakuten
input_raw_keys('ガギゲゴ')
- assert_byte_pointer_size('ガギゲゴ')
- assert_cursor(8)
- assert_cursor_max(8)
+ assert_line_around_cursor('ガギゲゴ', '')
input_keys("\C-b\C-b", false)
- assert_byte_pointer_size('ガギ')
- assert_cursor(4)
- assert_cursor_max(8)
+ assert_line_around_cursor('ガギ', 'ゲゴ')
input_raw_keys('グ', false)
- assert_byte_pointer_size('ガギグ')
- assert_cursor(6)
- assert_cursor_max(10)
- assert_line('ガギグゲゴ')
+ assert_line_around_cursor('ガギグ', 'ゲゴ')
end
def test_input_unknown_char
input_keys('͸') # U+0378 (unassigned)
- assert_line('͸')
- assert_byte_pointer_size('͸')
- assert_cursor(1)
- assert_cursor_max(1)
+ assert_line_around_cursor('͸', '')
end
def test_unix_line_discard
input_keys("\C-u", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
input_keys('abc')
- assert_byte_pointer_size('abc')
- assert_cursor(3)
- assert_cursor_max(3)
+ assert_line_around_cursor('abc', '')
input_keys("\C-b\C-u", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(1)
- assert_line('c')
+ assert_line_around_cursor('', 'c')
input_keys("\C-f\C-u", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
+ end
+
+ def test_vi_editing_mode
+ @line_editor.__send__(:vi_editing_mode, nil)
+ assert(@config.editing_mode_is?(:vi_insert))
end
end
diff --git a/test/reline/test_key_actor_vi.rb b/test/reline/test_key_actor_vi.rb
index 6e1d9dbbda..4deae2dd83 100644
--- a/test/reline/test_key_actor_vi.rb
+++ b/test/reline/test_key_actor_vi.rb
@@ -8,7 +8,7 @@ class Reline::KeyActor::ViInsert::Test < Reline::TestCase
@config.read_lines(<<~LINES.split(/(?<=\n)/))
set editing-mode vi
LINES
- @encoding = Reline::IOGate.encoding
+ @encoding = Reline.core.encoding
@line_editor = Reline::LineEditor.new(@config, @encoding)
@line_editor.reset(@prompt, encoding: @encoding)
end
@@ -25,950 +25,513 @@ class Reline::KeyActor::ViInsert::Test < Reline::TestCase
def test_vi_command_mode_with_input
input_keys("abc\C-[")
assert_instance_of(Reline::KeyActor::ViCommand, @config.editing_mode)
- assert_line('abc')
+ assert_line_around_cursor('ab', 'c')
end
def test_vi_insert
assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode)
input_keys('i')
- assert_line('i')
- assert_cursor(1)
+ assert_line_around_cursor('i', '')
assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode)
input_keys("\C-[")
- assert_line('i')
- assert_cursor(0)
+ assert_line_around_cursor('', 'i')
assert_instance_of(Reline::KeyActor::ViCommand, @config.editing_mode)
input_keys('i')
- assert_line('i')
- assert_cursor(0)
+ assert_line_around_cursor('', 'i')
assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode)
end
def test_vi_add
assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode)
input_keys('a')
- assert_line('a')
- assert_cursor(1)
+ assert_line_around_cursor('a', '')
assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode)
input_keys("\C-[")
- assert_line('a')
- assert_cursor(0)
+ assert_line_around_cursor('', 'a')
assert_instance_of(Reline::KeyActor::ViCommand, @config.editing_mode)
input_keys('a')
- assert_line('a')
- assert_cursor(1)
+ assert_line_around_cursor('a', '')
assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode)
end
def test_vi_insert_at_bol
input_keys('I')
- assert_line('I')
+ assert_line_around_cursor('I', '')
assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode)
input_keys("12345\C-[hh")
- assert_line('I12345')
- assert_byte_pointer_size('I12')
- assert_cursor(3)
- assert_cursor_max(6)
+ assert_line_around_cursor('I12', '345')
assert_instance_of(Reline::KeyActor::ViCommand, @config.editing_mode)
input_keys('I')
- assert_line('I12345')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(6)
+ assert_line_around_cursor('', 'I12345')
assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode)
end
def test_vi_add_at_eol
input_keys('A')
- assert_line('A')
+ assert_line_around_cursor('A', '')
assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode)
input_keys("12345\C-[hh")
- assert_line('A12345')
- assert_byte_pointer_size('A12')
- assert_cursor(3)
- assert_cursor_max(6)
+ assert_line_around_cursor('A12', '345')
assert_instance_of(Reline::KeyActor::ViCommand, @config.editing_mode)
input_keys('A')
- assert_line('A12345')
- assert_byte_pointer_size('A12345')
- assert_cursor(6)
- assert_cursor_max(6)
+ assert_line_around_cursor('A12345', '')
assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode)
end
def test_ed_insert_one
input_keys('a')
- assert_line('a')
- assert_byte_pointer_size('a')
- assert_cursor(1)
- assert_cursor_max(1)
+ assert_line_around_cursor('a', '')
end
def test_ed_insert_two
input_keys('ab')
- assert_line('ab')
- assert_byte_pointer_size('ab')
- assert_cursor(2)
- assert_cursor_max(2)
+ assert_line_around_cursor('ab', '')
end
def test_ed_insert_mbchar_one
input_keys('ã‹')
- assert_line('ã‹')
- assert_byte_pointer_size('ã‹')
- assert_cursor(2)
- assert_cursor_max(2)
+ assert_line_around_cursor('ã‹', '')
end
def test_ed_insert_mbchar_two
input_keys('ã‹ã')
- assert_line('ã‹ã')
- assert_byte_pointer_size('ã‹ã')
- assert_cursor(4)
- assert_cursor_max(4)
+ assert_line_around_cursor('ã‹ã', '')
end
def test_ed_insert_for_mbchar_by_plural_code_points
input_keys("ã‹\u3099")
- assert_line("ã‹\u3099")
- assert_byte_pointer_size("ã‹\u3099")
- assert_cursor(2)
- assert_cursor_max(2)
+ assert_line_around_cursor("ã‹\u3099", '')
end
def test_ed_insert_for_plural_mbchar_by_plural_code_points
input_keys("ã‹\u3099ã\u3099")
- assert_line("ã‹\u3099ã\u3099")
- assert_byte_pointer_size("ã‹\u3099ã\u3099")
- assert_cursor(4)
- assert_cursor_max(4)
+ assert_line_around_cursor("ã‹\u3099ã\u3099", '')
end
def test_ed_next_char
input_keys("abcdef\C-[0")
- assert_line('abcdef')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(6)
+ assert_line_around_cursor('', 'abcdef')
input_keys('l')
- assert_line('abcdef')
- assert_byte_pointer_size('a')
- assert_cursor(1)
- assert_cursor_max(6)
+ assert_line_around_cursor('a', 'bcdef')
input_keys('2l')
- assert_line('abcdef')
- assert_byte_pointer_size('abc')
- assert_cursor(3)
- assert_cursor_max(6)
+ assert_line_around_cursor('abc', 'def')
end
def test_ed_prev_char
input_keys("abcdef\C-[")
- assert_line('abcdef')
- assert_byte_pointer_size('abcde')
- assert_cursor(5)
- assert_cursor_max(6)
+ assert_line_around_cursor('abcde', 'f')
input_keys('h')
- assert_line('abcdef')
- assert_byte_pointer_size('abcd')
- assert_cursor(4)
- assert_cursor_max(6)
+ assert_line_around_cursor('abcd', 'ef')
input_keys('2h')
- assert_line('abcdef')
- assert_byte_pointer_size('ab')
- assert_cursor(2)
- assert_cursor_max(6)
+ assert_line_around_cursor('ab', 'cdef')
end
def test_history
Reline::HISTORY.concat(%w{abc 123 AAA})
input_keys("\C-[")
- assert_line('')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('', '')
input_keys('k')
- assert_line('AAA')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(3)
+ assert_line_around_cursor('', 'AAA')
input_keys('2k')
- assert_line('abc')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(3)
+ assert_line_around_cursor('', 'abc')
input_keys('j')
- assert_line('123')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(3)
+ assert_line_around_cursor('', '123')
input_keys('2j')
- assert_line('')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('', '')
end
def test_vi_paste_prev
input_keys("abcde\C-[3h")
- assert_line('abcde')
- assert_byte_pointer_size('a')
- assert_cursor(1)
- assert_cursor_max(5)
+ assert_line_around_cursor('a', 'bcde')
input_keys('P')
- assert_line('abcde')
- assert_byte_pointer_size('a')
- assert_cursor(1)
- assert_cursor_max(5)
+ assert_line_around_cursor('a', 'bcde')
input_keys('d$')
- assert_line('a')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(1)
+ assert_line_around_cursor('', 'a')
input_keys('P')
- assert_line('bcdea')
- assert_byte_pointer_size('bcd')
- assert_cursor(3)
- assert_cursor_max(5)
+ assert_line_around_cursor('bcd', 'ea')
input_keys('2P')
- assert_line('bcdbcdbcdeeea')
- assert_byte_pointer_size('bcdbcdbcd')
- assert_cursor(9)
- assert_cursor_max(13)
+ assert_line_around_cursor('bcdbcdbcd', 'eeea')
end
def test_vi_paste_next
input_keys("abcde\C-[3h")
- assert_line('abcde')
- assert_byte_pointer_size('a')
- assert_cursor(1)
- assert_cursor_max(5)
+ assert_line_around_cursor('a', 'bcde')
input_keys('p')
- assert_line('abcde')
- assert_byte_pointer_size('a')
- assert_cursor(1)
- assert_cursor_max(5)
+ assert_line_around_cursor('a', 'bcde')
input_keys('d$')
- assert_line('a')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(1)
+ assert_line_around_cursor('', 'a')
input_keys('p')
- assert_line('abcde')
- assert_byte_pointer_size('abcd')
- assert_cursor(4)
- assert_cursor_max(5)
+ assert_line_around_cursor('abcd', 'e')
input_keys('2p')
- assert_line('abcdebcdebcde')
- assert_byte_pointer_size('abcdebcdebcd')
- assert_cursor(12)
- assert_cursor_max(13)
+ assert_line_around_cursor('abcdebcdebcd', 'e')
end
def test_vi_paste_prev_for_mbchar
input_keys("ã‚ã„ã†ãˆãŠ\C-[3h")
- assert_line('ã‚ã„ã†ãˆãŠ')
- assert_byte_pointer_size('ã‚')
- assert_cursor(2)
- assert_cursor_max(10)
+ assert_line_around_cursor('ã‚', 'ã„ã†ãˆãŠ')
input_keys('P')
- assert_line('ã‚ã„ã†ãˆãŠ')
- assert_byte_pointer_size('ã‚')
- assert_cursor(2)
- assert_cursor_max(10)
+ assert_line_around_cursor('ã‚', 'ã„ã†ãˆãŠ')
input_keys('d$')
- assert_line('ã‚')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(2)
+ assert_line_around_cursor('', 'ã‚')
input_keys('P')
- assert_line('ã„ã†ãˆãŠã‚')
- assert_byte_pointer_size('ã„ã†ãˆ')
- assert_cursor(6)
- assert_cursor_max(10)
+ assert_line_around_cursor('ã„ã†ãˆ', 'ãŠã‚')
input_keys('2P')
- assert_line('ã„ã†ãˆã„ã†ãˆã„ã†ãˆãŠãŠãŠã‚')
- assert_byte_pointer_size('ã„ã†ãˆã„ã†ãˆã„ã†ãˆ')
- assert_cursor(18)
- assert_cursor_max(26)
+ assert_line_around_cursor('ã„ã†ãˆã„ã†ãˆã„ã†ãˆ', 'ãŠãŠãŠã‚')
end
def test_vi_paste_next_for_mbchar
input_keys("ã‚ã„ã†ãˆãŠ\C-[3h")
- assert_line('ã‚ã„ã†ãˆãŠ')
- assert_byte_pointer_size('ã‚')
- assert_cursor(2)
- assert_cursor_max(10)
+ assert_line_around_cursor('ã‚', 'ã„ã†ãˆãŠ')
input_keys('p')
- assert_line('ã‚ã„ã†ãˆãŠ')
- assert_byte_pointer_size('ã‚')
- assert_cursor(2)
- assert_cursor_max(10)
+ assert_line_around_cursor('ã‚', 'ã„ã†ãˆãŠ')
input_keys('d$')
- assert_line('ã‚')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(2)
+ assert_line_around_cursor('', 'ã‚')
input_keys('p')
- assert_line('ã‚ã„ã†ãˆãŠ')
- assert_byte_pointer_size('ã‚ã„ã†ãˆ')
- assert_cursor(8)
- assert_cursor_max(10)
+ assert_line_around_cursor('ã‚ã„ã†ãˆ', 'ãŠ')
input_keys('2p')
- assert_line('ã‚ã„ã†ãˆãŠã„ã†ãˆãŠã„ã†ãˆãŠ')
- assert_byte_pointer_size('ã‚ã„ã†ãˆãŠã„ã†ãˆãŠã„ã†ãˆ')
- assert_cursor(24)
- assert_cursor_max(26)
+ assert_line_around_cursor('ã‚ã„ã†ãˆãŠã„ã†ãˆãŠã„ã†ãˆ', 'ãŠ')
end
def test_vi_paste_prev_for_mbchar_by_plural_code_points
input_keys("ã‹\u3099ã\u3099ã\u3099ã‘\u3099ã“\u3099\C-[3h")
- assert_line("ã‹\u3099ã\u3099ã\u3099ã‘\u3099ã“\u3099")
- assert_byte_pointer_size("ã‹\u3099")
- assert_cursor(2)
- assert_cursor_max(10)
+ assert_line_around_cursor("ã‹\u3099", "ã\u3099ã\u3099ã‘\u3099ã“\u3099")
input_keys('P')
- assert_line("ã‹\u3099ã\u3099ã\u3099ã‘\u3099ã“\u3099")
- assert_byte_pointer_size("ã‹\u3099")
- assert_cursor(2)
- assert_cursor_max(10)
+ assert_line_around_cursor("ã‹\u3099", "ã\u3099ã\u3099ã‘\u3099ã“\u3099")
input_keys('d$')
- assert_line("ã‹\u3099")
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(2)
+ assert_line_around_cursor('', "ã‹\u3099")
input_keys('P')
- assert_line("ã\u3099ã\u3099ã‘\u3099ã“\u3099ã‹\u3099")
- assert_byte_pointer_size("ã\u3099ã\u3099ã‘\u3099")
- assert_cursor(6)
- assert_cursor_max(10)
+ assert_line_around_cursor("ã\u3099ã\u3099ã‘\u3099", "ã“\u3099ã‹\u3099")
input_keys('2P')
- assert_line("ã\u3099ã\u3099ã‘\u3099ã\u3099ã\u3099ã‘\u3099ã\u3099ã\u3099ã‘\u3099ã“\u3099ã“\u3099ã“\u3099ã‹\u3099")
- assert_byte_pointer_size("ã\u3099ã\u3099ã‘\u3099ã\u3099ã\u3099ã‘\u3099ã\u3099ã\u3099ã‘\u3099")
- assert_cursor(18)
- assert_cursor_max(26)
+ assert_line_around_cursor("ã\u3099ã\u3099ã‘\u3099ã\u3099ã\u3099ã‘\u3099ã\u3099ã\u3099ã‘\u3099", "ã“\u3099ã“\u3099ã“\u3099ã‹\u3099")
end
def test_vi_paste_next_for_mbchar_by_plural_code_points
input_keys("ã‹\u3099ã\u3099ã\u3099ã‘\u3099ã“\u3099\C-[3h")
- assert_line("ã‹\u3099ã\u3099ã\u3099ã‘\u3099ã“\u3099")
- assert_byte_pointer_size("ã‹\u3099")
- assert_cursor(2)
- assert_cursor_max(10)
+ assert_line_around_cursor("ã‹\u3099", "ã\u3099ã\u3099ã‘\u3099ã“\u3099")
input_keys('p')
- assert_line("ã‹\u3099ã\u3099ã\u3099ã‘\u3099ã“\u3099")
- assert_byte_pointer_size("ã‹\u3099")
- assert_cursor(2)
- assert_cursor_max(10)
+ assert_line_around_cursor("ã‹\u3099", "ã\u3099ã\u3099ã‘\u3099ã“\u3099")
input_keys('d$')
- assert_line("ã‹\u3099")
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(2)
+ assert_line_around_cursor('', "ã‹\u3099")
input_keys('p')
- assert_line("ã‹\u3099ã\u3099ã\u3099ã‘\u3099ã“\u3099")
- assert_byte_pointer_size("ã‹\u3099ã\u3099ã\u3099ã‘\u3099")
- assert_cursor(8)
- assert_cursor_max(10)
+ assert_line_around_cursor("ã‹\u3099ã\u3099ã\u3099ã‘\u3099", "ã“\u3099")
input_keys('2p')
- assert_line("ã‹\u3099ã\u3099ã\u3099ã‘\u3099ã“\u3099ã\u3099ã\u3099ã‘\u3099ã“\u3099ã\u3099ã\u3099ã‘\u3099ã“\u3099")
- assert_byte_pointer_size("ã‹\u3099ã\u3099ã\u3099ã‘\u3099ã“\u3099ã\u3099ã\u3099ã‘\u3099ã“\u3099ã\u3099ã\u3099ã‘\u3099")
- assert_cursor(24)
- assert_cursor_max(26)
+ assert_line_around_cursor("ã‹\u3099ã\u3099ã\u3099ã‘\u3099ã“\u3099ã\u3099ã\u3099ã‘\u3099ã“\u3099ã\u3099ã\u3099ã‘\u3099", "ã“\u3099")
end
def test_vi_prev_next_word
input_keys("aaa b{b}b ccc\C-[0")
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(13)
+ assert_line_around_cursor('', 'aaa b{b}b ccc')
input_keys('w')
- assert_byte_pointer_size('aaa ')
- assert_cursor(4)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa ', 'b{b}b ccc')
input_keys('w')
- assert_byte_pointer_size('aaa b')
- assert_cursor(5)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa b', '{b}b ccc')
input_keys('w')
- assert_byte_pointer_size('aaa b{')
- assert_cursor(6)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa b{', 'b}b ccc')
input_keys('w')
- assert_byte_pointer_size('aaa b{b')
- assert_cursor(7)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa b{b', '}b ccc')
input_keys('w')
- assert_byte_pointer_size('aaa b{b}')
- assert_cursor(8)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa b{b}', 'b ccc')
input_keys('w')
- assert_byte_pointer_size('aaa b{b}b ')
- assert_cursor(10)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa b{b}b ', 'ccc')
input_keys('w')
- assert_byte_pointer_size('aaa b{b}b cc')
- assert_cursor(12)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa b{b}b cc', 'c')
input_keys('b')
- assert_byte_pointer_size('aaa b{b}b ')
- assert_cursor(10)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa b{b}b ', 'ccc')
input_keys('b')
- assert_byte_pointer_size('aaa b{b}')
- assert_cursor(8)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa b{b}', 'b ccc')
input_keys('b')
- assert_byte_pointer_size('aaa b{b')
- assert_cursor(7)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa b{b', '}b ccc')
input_keys('b')
- assert_byte_pointer_size('aaa b{')
- assert_cursor(6)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa b{', 'b}b ccc')
input_keys('b')
- assert_byte_pointer_size('aaa b')
- assert_cursor(5)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa b', '{b}b ccc')
input_keys('b')
- assert_byte_pointer_size('aaa ')
- assert_cursor(4)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa ', 'b{b}b ccc')
input_keys('b')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(13)
+ assert_line_around_cursor('', 'aaa b{b}b ccc')
input_keys('3w')
- assert_byte_pointer_size('aaa b{')
- assert_cursor(6)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa b{', 'b}b ccc')
input_keys('3w')
- assert_byte_pointer_size('aaa b{b}b ')
- assert_cursor(10)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa b{b}b ', 'ccc')
input_keys('3w')
- assert_byte_pointer_size('aaa b{b}b cc')
- assert_cursor(12)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa b{b}b cc', 'c')
input_keys('3b')
- assert_byte_pointer_size('aaa b{b')
- assert_cursor(7)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa b{b', '}b ccc')
input_keys('3b')
- assert_byte_pointer_size('aaa ')
- assert_cursor(4)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa ', 'b{b}b ccc')
input_keys('3b')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(13)
+ assert_line_around_cursor('', 'aaa b{b}b ccc')
end
def test_vi_end_word
input_keys("aaa b{b}}}b ccc\C-[0")
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(19)
+ assert_line_around_cursor('', 'aaa b{b}}}b ccc')
input_keys('e')
- assert_byte_pointer_size('aa')
- assert_cursor(2)
- assert_cursor_max(19)
+ assert_line_around_cursor('aa', 'a b{b}}}b ccc')
input_keys('e')
- assert_byte_pointer_size('aaa ')
- assert_cursor(6)
- assert_cursor_max(19)
+ assert_line_around_cursor('aaa ', 'b{b}}}b ccc')
input_keys('e')
- assert_byte_pointer_size('aaa b')
- assert_cursor(7)
- assert_cursor_max(19)
+ assert_line_around_cursor('aaa b', '{b}}}b ccc')
input_keys('e')
- assert_byte_pointer_size('aaa b{')
- assert_cursor(8)
- assert_cursor_max(19)
+ assert_line_around_cursor('aaa b{', 'b}}}b ccc')
input_keys('e')
- assert_byte_pointer_size('aaa b{b}}')
- assert_cursor(11)
- assert_cursor_max(19)
+ assert_line_around_cursor('aaa b{b}}', '}b ccc')
input_keys('e')
- assert_byte_pointer_size('aaa b{b}}}')
- assert_cursor(12)
- assert_cursor_max(19)
+ assert_line_around_cursor('aaa b{b}}}', 'b ccc')
input_keys('e')
- assert_byte_pointer_size('aaa b{b}}}b cc')
- assert_cursor(18)
- assert_cursor_max(19)
+ assert_line_around_cursor('aaa b{b}}}b cc', 'c')
input_keys('e')
- assert_byte_pointer_size('aaa b{b}}}b cc')
- assert_cursor(18)
- assert_cursor_max(19)
+ assert_line_around_cursor('aaa b{b}}}b cc', 'c')
input_keys('03e')
- assert_byte_pointer_size('aaa b')
- assert_cursor(7)
- assert_cursor_max(19)
+ assert_line_around_cursor('aaa b', '{b}}}b ccc')
input_keys('3e')
- assert_byte_pointer_size('aaa b{b}}}')
- assert_cursor(12)
- assert_cursor_max(19)
+ assert_line_around_cursor('aaa b{b}}}', 'b ccc')
input_keys('3e')
- assert_byte_pointer_size('aaa b{b}}}b cc')
- assert_cursor(18)
- assert_cursor_max(19)
+ assert_line_around_cursor('aaa b{b}}}b cc', 'c')
end
def test_vi_prev_next_big_word
input_keys("aaa b{b}b ccc\C-[0")
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(13)
+ assert_line_around_cursor('', 'aaa b{b}b ccc')
input_keys('W')
- assert_byte_pointer_size('aaa ')
- assert_cursor(4)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa ', 'b{b}b ccc')
input_keys('W')
- assert_byte_pointer_size('aaa b{b}b ')
- assert_cursor(10)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa b{b}b ', 'ccc')
input_keys('W')
- assert_byte_pointer_size('aaa b{b}b cc')
- assert_cursor(12)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa b{b}b cc', 'c')
input_keys('B')
- assert_byte_pointer_size('aaa b{b}b ')
- assert_cursor(10)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa b{b}b ', 'ccc')
input_keys('B')
- assert_byte_pointer_size('aaa ')
- assert_cursor(4)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa ', 'b{b}b ccc')
input_keys('B')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(13)
+ assert_line_around_cursor('', 'aaa b{b}b ccc')
input_keys('2W')
- assert_byte_pointer_size('aaa b{b}b ')
- assert_cursor(10)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa b{b}b ', 'ccc')
input_keys('2W')
- assert_byte_pointer_size('aaa b{b}b cc')
- assert_cursor(12)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa b{b}b cc', 'c')
input_keys('2B')
- assert_byte_pointer_size('aaa ')
- assert_cursor(4)
- assert_cursor_max(13)
+ assert_line_around_cursor('aaa ', 'b{b}b ccc')
input_keys('2B')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(13)
+ assert_line_around_cursor('', 'aaa b{b}b ccc')
end
def test_vi_end_big_word
input_keys("aaa b{b}}}b ccc\C-[0")
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(19)
+ assert_line_around_cursor('', 'aaa b{b}}}b ccc')
input_keys('E')
- assert_byte_pointer_size('aa')
- assert_cursor(2)
- assert_cursor_max(19)
+ assert_line_around_cursor('aa', 'a b{b}}}b ccc')
input_keys('E')
- assert_byte_pointer_size('aaa b{b}}}')
- assert_cursor(12)
- assert_cursor_max(19)
+ assert_line_around_cursor('aaa b{b}}}', 'b ccc')
input_keys('E')
- assert_byte_pointer_size('aaa b{b}}}b cc')
- assert_cursor(18)
- assert_cursor_max(19)
+ assert_line_around_cursor('aaa b{b}}}b cc', 'c')
input_keys('E')
- assert_byte_pointer_size('aaa b{b}}}b cc')
- assert_cursor(18)
- assert_cursor_max(19)
+ assert_line_around_cursor('aaa b{b}}}b cc', 'c')
end
def test_ed_quoted_insert
input_keys("ab\C-v\C-acd")
- assert_line("ab\C-acd")
- assert_byte_pointer_size("ab\C-acd")
- assert_cursor(6)
- assert_cursor_max(6)
+ assert_line_around_cursor("ab\C-acd", '')
end
def test_ed_quoted_insert_with_vi_arg
input_keys("ab\C-[3\C-v\C-aacd")
- assert_line("a\C-a\C-a\C-abcd")
- assert_byte_pointer_size("a\C-a\C-a\C-abcd")
- assert_cursor(10)
- assert_cursor_max(10)
+ assert_line_around_cursor("a\C-a\C-a\C-abcd", '')
end
def test_vi_replace_char
input_keys("abcdef\C-[03l")
- assert_line('abcdef')
- assert_byte_pointer_size('abc')
- assert_cursor(3)
- assert_cursor_max(6)
+ assert_line_around_cursor('abc', 'def')
input_keys('rz')
- assert_line('abczef')
- assert_byte_pointer_size('abc')
- assert_cursor(3)
- assert_cursor_max(6)
+ assert_line_around_cursor('abc', 'zef')
input_keys('2rx')
- assert_line('abcxxf')
- assert_byte_pointer_size('abcxx')
- assert_cursor(5)
- assert_cursor_max(6)
+ assert_line_around_cursor('abcxx', 'f')
end
def test_vi_replace_char_with_mbchar
input_keys("ã‚ã„ã†ãˆãŠ\C-[0l")
- assert_line('ã‚ã„ã†ãˆãŠ')
- assert_byte_pointer_size('ã‚')
- assert_cursor(2)
- assert_cursor_max(10)
+ assert_line_around_cursor('ã‚', 'ã„ã†ãˆãŠ')
input_keys('rx')
- assert_line('ã‚xã†ãˆãŠ')
- assert_byte_pointer_size('ã‚')
- assert_cursor(2)
- assert_cursor_max(9)
+ assert_line_around_cursor('ã‚', 'xã†ãˆãŠ')
input_keys('l2ry')
- assert_line('ã‚xyyãŠ')
- assert_byte_pointer_size('ã‚xyy')
- assert_cursor(5)
- assert_cursor_max(7)
+ assert_line_around_cursor('ã‚xyy', 'ãŠ')
end
def test_vi_next_char
input_keys("abcdef\C-[0")
- assert_line('abcdef')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(6)
+ assert_line_around_cursor('', 'abcdef')
input_keys('fz')
- assert_line('abcdef')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(6)
+ assert_line_around_cursor('', 'abcdef')
input_keys('fe')
- assert_line('abcdef')
- assert_byte_pointer_size('abcd')
- assert_cursor(4)
- assert_cursor_max(6)
+ assert_line_around_cursor('abcd', 'ef')
end
def test_vi_to_next_char
input_keys("abcdef\C-[0")
- assert_line('abcdef')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(6)
+ assert_line_around_cursor('', 'abcdef')
input_keys('tz')
- assert_line('abcdef')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(6)
+ assert_line_around_cursor('', 'abcdef')
input_keys('te')
- assert_line('abcdef')
- assert_byte_pointer_size('abc')
- assert_cursor(3)
- assert_cursor_max(6)
+ assert_line_around_cursor('abc', 'def')
end
def test_vi_prev_char
input_keys("abcdef\C-[")
- assert_line('abcdef')
- assert_byte_pointer_size('abcde')
- assert_cursor(5)
- assert_cursor_max(6)
+ assert_line_around_cursor('abcde', 'f')
input_keys('Fz')
- assert_line('abcdef')
- assert_byte_pointer_size('abcde')
- assert_cursor(5)
- assert_cursor_max(6)
+ assert_line_around_cursor('abcde', 'f')
input_keys('Fa')
- assert_line('abcdef')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(6)
+ assert_line_around_cursor('', 'abcdef')
end
def test_vi_to_prev_char
input_keys("abcdef\C-[")
- assert_line('abcdef')
- assert_byte_pointer_size('abcde')
- assert_cursor(5)
- assert_cursor_max(6)
+ assert_line_around_cursor('abcde', 'f')
input_keys('Tz')
- assert_line('abcdef')
- assert_byte_pointer_size('abcde')
- assert_cursor(5)
- assert_cursor_max(6)
+ assert_line_around_cursor('abcde', 'f')
input_keys('Ta')
- assert_line('abcdef')
- assert_byte_pointer_size('a')
- assert_cursor(1)
- assert_cursor_max(6)
+ assert_line_around_cursor('a', 'bcdef')
end
def test_vi_delete_next_char
input_keys("abc\C-[h")
- assert_byte_pointer_size('a')
- assert_cursor(1)
- assert_cursor_max(3)
- assert_line('abc')
+ assert_line_around_cursor('a', 'bc')
input_keys('x')
- assert_byte_pointer_size('a')
- assert_cursor(1)
- assert_cursor_max(2)
- assert_line('ac')
+ assert_line_around_cursor('a', 'c')
input_keys('x')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(1)
- assert_line('a')
+ assert_line_around_cursor('', 'a')
input_keys('x')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
input_keys('x')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
end
def test_vi_delete_next_char_for_mbchar
input_keys("ã‚ã„ã†\C-[h")
- assert_byte_pointer_size('ã‚')
- assert_cursor(2)
- assert_cursor_max(6)
- assert_line('ã‚ã„ã†')
+ assert_line_around_cursor('ã‚', 'ã„ã†')
input_keys('x')
- assert_byte_pointer_size('ã‚')
- assert_cursor(2)
- assert_cursor_max(4)
- assert_line('ã‚ã†')
+ assert_line_around_cursor('ã‚', 'ã†')
input_keys('x')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(2)
- assert_line('ã‚')
+ assert_line_around_cursor('', 'ã‚')
input_keys('x')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
input_keys('x')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
end
def test_vi_delete_next_char_for_mbchar_by_plural_code_points
input_keys("ã‹\u3099ã\u3099ã\u3099\C-[h")
- assert_byte_pointer_size("ã‹\u3099")
- assert_cursor(2)
- assert_cursor_max(6)
- assert_line("ã‹\u3099ã\u3099ã\u3099")
+ assert_line_around_cursor("ã‹\u3099", "ã\u3099ã\u3099")
input_keys('x')
- assert_byte_pointer_size("ã‹\u3099")
- assert_cursor(2)
- assert_cursor_max(4)
- assert_line("ã‹\u3099ã\u3099")
+ assert_line_around_cursor("ã‹\u3099", "ã\u3099")
input_keys('x')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(2)
- assert_line("ã‹\u3099")
+ assert_line_around_cursor('', "ã‹\u3099")
input_keys('x')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
input_keys('x')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
end
def test_vi_delete_prev_char
input_keys('ab')
- assert_byte_pointer_size('ab')
- assert_cursor(2)
- assert_cursor_max(2)
+ assert_line_around_cursor('ab', '')
input_keys("\C-h")
- assert_byte_pointer_size('a')
- assert_cursor(1)
- assert_cursor_max(1)
- assert_line('a')
+ assert_line_around_cursor('a', '')
end
def test_vi_delete_prev_char_for_mbchar
input_keys('ã‹ã')
- assert_byte_pointer_size('ã‹ã')
- assert_cursor(4)
- assert_cursor_max(4)
+ assert_line_around_cursor('ã‹ã', '')
input_keys("\C-h")
- assert_byte_pointer_size('ã‹')
- assert_cursor(2)
- assert_cursor_max(2)
- assert_line('ã‹')
+ assert_line_around_cursor('ã‹', '')
end
def test_vi_delete_prev_char_for_mbchar_by_plural_code_points
input_keys("ã‹\u3099ã\u3099")
- assert_byte_pointer_size("ã‹\u3099ã\u3099")
- assert_cursor(4)
- assert_cursor_max(4)
+ assert_line_around_cursor("ã‹\u3099ã\u3099", '')
input_keys("\C-h")
- assert_byte_pointer_size("ã‹\u3099")
- assert_cursor(2)
- assert_cursor_max(2)
- assert_line("ã‹\u3099")
+ assert_line_around_cursor("ã‹\u3099", '')
end
def test_ed_delete_prev_char
input_keys("abcdefg\C-[h")
- assert_byte_pointer_size('abcde')
- assert_cursor(5)
- assert_cursor_max(7)
- assert_line('abcdefg')
+ assert_line_around_cursor('abcde', 'fg')
input_keys('X')
- assert_byte_pointer_size('abcd')
- assert_cursor(4)
- assert_cursor_max(6)
- assert_line('abcdfg')
+ assert_line_around_cursor('abcd', 'fg')
input_keys('3X')
- assert_byte_pointer_size('a')
- assert_cursor(1)
- assert_cursor_max(3)
- assert_line('afg')
+ assert_line_around_cursor('a', 'fg')
input_keys('p')
- assert_byte_pointer_size('abcd')
- assert_cursor(4)
- assert_cursor_max(6)
- assert_line('afbcdg')
+ assert_line_around_cursor('afbc', 'dg')
end
def test_ed_delete_prev_word
input_keys('abc def{bbb}ccc')
- assert_byte_pointer_size('abc def{bbb}ccc')
- assert_cursor(15)
- assert_cursor_max(15)
+ assert_line_around_cursor('abc def{bbb}ccc', '')
input_keys("\C-w")
- assert_byte_pointer_size('abc def{bbb}')
- assert_cursor(12)
- assert_cursor_max(12)
- assert_line('abc def{bbb}')
+ assert_line_around_cursor('abc def{bbb}', '')
input_keys("\C-w")
- assert_byte_pointer_size('abc def{')
- assert_cursor(8)
- assert_cursor_max(8)
- assert_line('abc def{')
+ assert_line_around_cursor('abc def{', '')
input_keys("\C-w")
- assert_byte_pointer_size('abc ')
- assert_cursor(4)
- assert_cursor_max(4)
- assert_line('abc ')
+ assert_line_around_cursor('abc ', '')
input_keys("\C-w")
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
end
def test_ed_delete_prev_word_for_mbchar
input_keys('ã‚ã„ㆠã‹ãã{ã•ã—ã™}ãŸã¡ã¤')
- assert_byte_pointer_size('ã‚ã„ㆠã‹ãã{ã•ã—ã™}ãŸã¡ã¤')
- assert_cursor(27)
- assert_cursor_max(27)
+ assert_line_around_cursor('ã‚ã„ㆠã‹ãã{ã•ã—ã™}ãŸã¡ã¤', '')
input_keys("\C-w")
- assert_byte_pointer_size('ã‚ã„ㆠã‹ãã{ã•ã—ã™}')
- assert_cursor(21)
- assert_cursor_max(21)
- assert_line('ã‚ã„ㆠã‹ãã{ã•ã—ã™}')
+ assert_line_around_cursor('ã‚ã„ㆠã‹ãã{ã•ã—ã™}', '')
input_keys("\C-w")
- assert_byte_pointer_size('ã‚ã„ㆠã‹ãã{')
- assert_cursor(14)
- assert_cursor_max(14)
- assert_line('ã‚ã„ㆠã‹ãã{')
+ assert_line_around_cursor('ã‚ã„ㆠã‹ãã{', '')
input_keys("\C-w")
- assert_byte_pointer_size('ã‚ã„ㆠ')
- assert_cursor(7)
- assert_cursor_max(7)
- assert_line('ã‚ã„ㆠ')
+ assert_line_around_cursor('ã‚ã„ㆠ', '')
input_keys("\C-w")
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
end
def test_ed_delete_prev_word_for_mbchar_by_plural_code_points
input_keys("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}ãŸã¡ã¤")
- assert_byte_pointer_size("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}ãŸã¡ã¤")
- assert_cursor(27)
- assert_cursor_max(27)
+ assert_line_around_cursor("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}ãŸã¡ã¤", '')
input_keys("\C-w")
- assert_byte_pointer_size("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}")
- assert_cursor(21)
- assert_cursor_max(21)
- assert_line("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}")
+ assert_line_around_cursor("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{ã•ã—ã™}", '')
input_keys("\C-w")
- assert_byte_pointer_size("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{")
- assert_cursor(14)
- assert_cursor_max(14)
- assert_line("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{")
+ assert_line_around_cursor("ã‚ã„ㆠã‹\u3099ã\u3099ã\u3099{", '')
input_keys("\C-w")
- assert_byte_pointer_size('ã‚ã„ㆠ')
- assert_cursor(7)
- assert_cursor_max(7)
- assert_line('ã‚ã„ㆠ')
+ assert_line_around_cursor('ã‚ã„ㆠ', '')
input_keys("\C-w")
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
end
def test_ed_newline_with_cr
input_keys('ab')
- assert_byte_pointer_size('ab')
- assert_cursor(2)
- assert_cursor_max(2)
+ assert_line_around_cursor('ab', '')
refute(@line_editor.finished?)
input_keys("\C-m")
- assert_line('ab')
+ assert_line_around_cursor('ab', '')
assert(@line_editor.finished?)
end
def test_ed_newline_with_lf
input_keys('ab')
- assert_byte_pointer_size('ab')
- assert_cursor(2)
- assert_cursor_max(2)
+ assert_line_around_cursor('ab', '')
refute(@line_editor.finished?)
input_keys("\C-j")
- assert_line('ab')
+ assert_line_around_cursor('ab', '')
assert(@line_editor.finished?)
end
def test_vi_list_or_eof
input_keys("\C-d") # quit from inputing
- assert_line(nil)
+ assert_nil(@line_editor.line)
assert(@line_editor.finished?)
end
def test_vi_list_or_eof_with_non_empty_line
input_keys('ab')
- assert_byte_pointer_size('ab')
- assert_cursor(2)
- assert_cursor_max(2)
+ assert_line_around_cursor('ab', '')
refute(@line_editor.finished?)
input_keys("\C-d")
- assert_line('ab')
+ assert_line_around_cursor('ab', '')
assert(@line_editor.finished?)
end
@@ -982,40 +545,19 @@ class Reline::KeyActor::ViInsert::Test < Reline::TestCase
}
}
input_keys('foo')
- assert_byte_pointer_size('foo')
- assert_cursor(3)
- assert_cursor_max(3)
- assert_line('foo')
+ assert_line_around_cursor('foo', '')
input_keys("\C-n")
- assert_byte_pointer_size('foo_bar')
- assert_cursor(7)
- assert_cursor_max(7)
- assert_line('foo_bar')
+ assert_line_around_cursor('foo_bar', '')
input_keys("\C-n")
- assert_byte_pointer_size('foo_bar_baz')
- assert_cursor(11)
- assert_cursor_max(11)
- assert_line('foo_bar_baz')
+ assert_line_around_cursor('foo_bar_baz', '')
input_keys("\C-n")
- assert_byte_pointer_size('foo')
- assert_cursor(3)
- assert_cursor_max(3)
- assert_line('foo')
+ assert_line_around_cursor('foo', '')
input_keys("\C-n")
- assert_byte_pointer_size('foo_bar')
- assert_cursor(7)
- assert_cursor_max(7)
- assert_line('foo_bar')
+ assert_line_around_cursor('foo_bar', '')
input_keys("_\C-n")
- assert_byte_pointer_size('foo_bar_baz')
- assert_cursor(11)
- assert_cursor_max(11)
- assert_line('foo_bar_baz')
+ assert_line_around_cursor('foo_bar_baz', '')
input_keys("\C-n")
- assert_byte_pointer_size('foo_bar_')
- assert_cursor(8)
- assert_cursor_max(8)
- assert_line('foo_bar_')
+ assert_line_around_cursor('foo_bar_', '')
end
def test_completion_journey_reverse
@@ -1028,40 +570,19 @@ class Reline::KeyActor::ViInsert::Test < Reline::TestCase
}
}
input_keys('foo')
- assert_byte_pointer_size('foo')
- assert_cursor(3)
- assert_cursor_max(3)
- assert_line('foo')
+ assert_line_around_cursor('foo', '')
input_keys("\C-p")
- assert_byte_pointer_size('foo_bar_baz')
- assert_cursor(11)
- assert_cursor_max(11)
- assert_line('foo_bar_baz')
+ assert_line_around_cursor('foo_bar_baz', '')
input_keys("\C-p")
- assert_byte_pointer_size('foo_bar')
- assert_cursor(7)
- assert_cursor_max(7)
- assert_line('foo_bar')
+ assert_line_around_cursor('foo_bar', '')
input_keys("\C-p")
- assert_byte_pointer_size('foo')
- assert_cursor(3)
- assert_cursor_max(3)
- assert_line('foo')
+ assert_line_around_cursor('foo', '')
input_keys("\C-p")
- assert_byte_pointer_size('foo_bar_baz')
- assert_cursor(11)
- assert_cursor_max(11)
- assert_line('foo_bar_baz')
+ assert_line_around_cursor('foo_bar_baz', '')
input_keys("\C-h\C-p")
- assert_byte_pointer_size('foo_bar_baz')
- assert_cursor(11)
- assert_cursor_max(11)
- assert_line('foo_bar_baz')
+ assert_line_around_cursor('foo_bar_baz', '')
input_keys("\C-p")
- assert_byte_pointer_size('foo_bar_ba')
- assert_cursor(10)
- assert_cursor_max(10)
- assert_line('foo_bar_ba')
+ assert_line_around_cursor('foo_bar_ba', '')
end
def test_completion_journey_in_middle_of_line
@@ -1074,42 +595,21 @@ class Reline::KeyActor::ViInsert::Test < Reline::TestCase
}
}
input_keys('abcde fo ABCDE')
- assert_line('abcde fo ABCDE')
+ assert_line_around_cursor('abcde fo ABCDE', '')
input_keys("\C-[" + 'h' * 5 + "i\C-n")
- assert_byte_pointer_size('abcde foo_bar')
- assert_cursor(13)
- assert_cursor_max(19)
- assert_line('abcde foo_bar ABCDE')
+ assert_line_around_cursor('abcde foo_bar', ' ABCDE')
input_keys("\C-n")
- assert_byte_pointer_size('abcde foo_bar_baz')
- assert_cursor(17)
- assert_cursor_max(23)
- assert_line('abcde foo_bar_baz ABCDE')
+ assert_line_around_cursor('abcde foo_bar_baz', ' ABCDE')
input_keys("\C-n")
- assert_byte_pointer_size('abcde fo')
- assert_cursor(8)
- assert_cursor_max(14)
- assert_line('abcde fo ABCDE')
+ assert_line_around_cursor('abcde fo', ' ABCDE')
input_keys("\C-n")
- assert_byte_pointer_size('abcde foo_bar')
- assert_cursor(13)
- assert_cursor_max(19)
- assert_line('abcde foo_bar ABCDE')
+ assert_line_around_cursor('abcde foo_bar', ' ABCDE')
input_keys("_\C-n")
- assert_byte_pointer_size('abcde foo_bar_baz')
- assert_cursor(17)
- assert_cursor_max(23)
- assert_line('abcde foo_bar_baz ABCDE')
+ assert_line_around_cursor('abcde foo_bar_baz', ' ABCDE')
input_keys("\C-n")
- assert_byte_pointer_size('abcde foo_bar_')
- assert_cursor(14)
- assert_cursor_max(20)
- assert_line('abcde foo_bar_ ABCDE')
+ assert_line_around_cursor('abcde foo_bar_', ' ABCDE')
input_keys("\C-n")
- assert_byte_pointer_size('abcde foo_bar_baz')
- assert_cursor(17)
- assert_cursor_max(23)
- assert_line('abcde foo_bar_baz ABCDE')
+ assert_line_around_cursor('abcde foo_bar_baz', ' ABCDE')
end
def test_completion
@@ -1122,15 +622,55 @@ class Reline::KeyActor::ViInsert::Test < Reline::TestCase
}
}
input_keys('foo')
- assert_byte_pointer_size('foo')
- assert_cursor(3)
- assert_cursor_max(3)
- assert_line('foo')
+ assert_line_around_cursor('foo', '')
input_keys("\C-i")
- assert_byte_pointer_size('foo_bar')
- assert_cursor(7)
- assert_cursor_max(7)
- assert_line('foo_bar')
+ assert_line_around_cursor('foo_bar', '')
+ end
+
+ def test_autocompletion_with_upward_navigation
+ @config.autocompletion = true
+ @line_editor.completion_proc = proc { |word|
+ %w{
+ Readline
+ Regexp
+ RegexpError
+ }.map { |i|
+ i.encode(@encoding)
+ }
+ }
+ input_keys('Re')
+ assert_line_around_cursor('Re', '')
+ input_keys("\C-i", false)
+ assert_line_around_cursor('Readline', '')
+ input_keys("\C-i", false)
+ assert_line_around_cursor('Regexp', '')
+ @line_editor.input_key(Reline::Key.new(:completion_journey_up, :completion_journey_up, false))
+ assert_line_around_cursor('Readline', '')
+ ensure
+ @config.autocompletion = false
+ end
+
+ def test_autocompletion_with_upward_navigation_and_menu_complete_backward
+ @config.autocompletion = true
+ @line_editor.completion_proc = proc { |word|
+ %w{
+ Readline
+ Regexp
+ RegexpError
+ }.map { |i|
+ i.encode(@encoding)
+ }
+ }
+ input_keys('Re')
+ assert_line_around_cursor('Re', '')
+ input_keys("\C-i", false)
+ assert_line_around_cursor('Readline', '')
+ input_keys("\C-i", false)
+ assert_line_around_cursor('Regexp', '')
+ @line_editor.input_key(Reline::Key.new(:menu_complete_backward, :menu_complete_backward, false))
+ assert_line_around_cursor('Readline', '')
+ ensure
+ @config.autocompletion = false
end
def test_completion_with_disable_completion
@@ -1144,315 +684,224 @@ class Reline::KeyActor::ViInsert::Test < Reline::TestCase
}
}
input_keys('foo')
- assert_byte_pointer_size('foo')
- assert_cursor(3)
- assert_cursor_max(3)
- assert_line('foo')
+ assert_line_around_cursor('foo', '')
input_keys("\C-i")
- assert_byte_pointer_size('foo')
- assert_cursor(3)
- assert_cursor_max(3)
- assert_line('foo')
+ assert_line_around_cursor('foo', '')
end
def test_vi_first_print
input_keys("abcde\C-[^")
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(5)
+ assert_line_around_cursor('', 'abcde')
input_keys("0\C-ki")
input_keys(" abcde\C-[^")
- assert_byte_pointer_size(' ')
- assert_cursor(1)
- assert_cursor_max(6)
+ assert_line_around_cursor(' ', 'abcde')
input_keys("0\C-ki")
input_keys(" abcde ABCDE \C-[^")
- assert_byte_pointer_size(' ')
- assert_cursor(3)
- assert_cursor_max(17)
+ assert_line_around_cursor(' ', 'abcde ABCDE ')
end
def test_ed_move_to_beg
input_keys("abcde\C-[0")
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(5)
+ assert_line_around_cursor('', 'abcde')
input_keys("0\C-ki")
input_keys(" abcde\C-[0")
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(6)
+ assert_line_around_cursor('', ' abcde')
input_keys("0\C-ki")
input_keys(" abcde ABCDE \C-[0")
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(17)
+ assert_line_around_cursor('', ' abcde ABCDE ')
+ end
+
+ def test_vi_to_column
+ input_keys("a一二三\C-[0")
+ input_keys('1|')
+ assert_line_around_cursor('', 'a一二三')
+ input_keys('2|')
+ assert_line_around_cursor('a', '一二三')
+ input_keys('3|')
+ assert_line_around_cursor('a', '一二三')
+ input_keys('4|')
+ assert_line_around_cursor('a一', '二三')
+ input_keys('9|')
+ assert_line_around_cursor('a一二', '三')
end
def test_vi_delete_meta
input_keys("aaa bbb ccc ddd eee\C-[02w")
- assert_byte_pointer_size('aaa bbb ')
- assert_cursor(8)
- assert_cursor_max(19)
- assert_line('aaa bbb ccc ddd eee')
+ assert_line_around_cursor('aaa bbb ', 'ccc ddd eee')
input_keys('dw')
- assert_byte_pointer_size('aaa bbb ')
- assert_cursor(8)
- assert_cursor_max(15)
- assert_line('aaa bbb ddd eee')
+ assert_line_around_cursor('aaa bbb ', 'ddd eee')
input_keys('db')
- assert_byte_pointer_size('aaa ')
- assert_cursor(4)
- assert_cursor_max(11)
- assert_line('aaa ddd eee')
+ assert_line_around_cursor('aaa ', 'ddd eee')
end
def test_vi_delete_meta_with_vi_next_word_at_eol
input_keys("foo bar\C-[0w")
- assert_byte_pointer_size('foo ')
- assert_cursor(4)
- assert_cursor_max(7)
- assert_line('foo bar')
+ assert_line_around_cursor('foo ', 'bar')
input_keys('w')
- assert_byte_pointer_size('foo ba')
- assert_cursor(6)
- assert_cursor_max(7)
- assert_line('foo bar')
+ assert_line_around_cursor('foo ba', 'r')
input_keys('0dw')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(3)
- assert_line('bar')
+ assert_line_around_cursor('', 'bar')
input_keys('dw')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
end
def test_vi_delete_meta_with_vi_next_char
input_keys("aaa bbb ccc ___ ddd\C-[02w")
- assert_byte_pointer_size('aaa bbb ')
- assert_cursor(8)
- assert_cursor_max(19)
- assert_line('aaa bbb ccc ___ ddd')
+ assert_line_around_cursor('aaa bbb ', 'ccc ___ ddd')
input_keys('df_')
- assert_byte_pointer_size('aaa bbb ')
- assert_cursor(8)
- assert_cursor_max(14)
- assert_line('aaa bbb __ ddd')
+ assert_line_around_cursor('aaa bbb ', '__ ddd')
end
def test_vi_delete_meta_with_arg
- input_keys("aaa bbb ccc\C-[02w")
- assert_byte_pointer_size('aaa bbb ')
- assert_cursor(8)
- assert_cursor_max(11)
- assert_line('aaa bbb ccc')
+ input_keys("aaa bbb ccc ddd\C-[03w")
+ assert_line_around_cursor('aaa bbb ccc ', 'ddd')
input_keys('2dl')
- assert_byte_pointer_size('aaa bbb ')
- assert_cursor(8)
- assert_cursor_max(9)
- assert_line('aaa bbb c')
+ assert_line_around_cursor('aaa bbb ccc ', 'd')
+ input_keys('d2h')
+ assert_line_around_cursor('aaa bbb cc', 'd')
+ input_keys('2d3h')
+ assert_line_around_cursor('aaa ', 'd')
+ input_keys('dd')
+ assert_line_around_cursor('', '')
end
def test_vi_change_meta
input_keys("aaa bbb ccc ddd eee\C-[02w")
- assert_byte_pointer_size('aaa bbb ')
- assert_cursor(8)
- assert_cursor_max(19)
- assert_line('aaa bbb ccc ddd eee')
+ assert_line_around_cursor('aaa bbb ', 'ccc ddd eee')
input_keys('cwaiueo')
- assert_byte_pointer_size('aaa bbb aiueo')
- assert_cursor(13)
- assert_cursor_max(21)
- assert_line('aaa bbb aiueo ddd eee')
+ assert_line_around_cursor('aaa bbb aiueo', ' ddd eee')
input_keys("\C-[")
- assert_byte_pointer_size('aaa bbb aiue')
- assert_cursor(12)
- assert_cursor_max(21)
- assert_line('aaa bbb aiueo ddd eee')
+ assert_line_around_cursor('aaa bbb aiue', 'o ddd eee')
input_keys('cb')
- assert_byte_pointer_size('aaa bbb ')
- assert_cursor(8)
- assert_cursor_max(17)
- assert_line('aaa bbb o ddd eee')
+ assert_line_around_cursor('aaa bbb ', 'o ddd eee')
end
def test_vi_change_meta_with_vi_next_word
input_keys("foo bar baz\C-[0w")
- assert_byte_pointer_size('foo ')
- assert_cursor(5)
- assert_cursor_max(13)
- assert_line('foo bar baz')
+ assert_line_around_cursor('foo ', 'bar baz')
input_keys('cwhoge')
- assert_byte_pointer_size('foo hoge')
- assert_cursor(9)
- assert_cursor_max(14)
- assert_line('foo hoge baz')
+ assert_line_around_cursor('foo hoge', ' baz')
input_keys("\C-[")
- assert_byte_pointer_size('foo hog')
- assert_cursor(8)
- assert_cursor_max(14)
- assert_line('foo hoge baz')
+ assert_line_around_cursor('foo hog', 'e baz')
+ end
+
+ def test_vi_waiting_operator_with_waiting_proc
+ input_keys("foo foo foo foo foo\C-[0")
+ input_keys('2d3fo')
+ assert_line_around_cursor('', ' foo foo')
+ input_keys('fo')
+ assert_line_around_cursor(' f', 'oo foo')
+ end
+
+ def test_vi_waiting_operator_cancel
+ input_keys("aaa bbb ccc\C-[02w")
+ assert_line_around_cursor('aaa bbb ', 'ccc')
+ # dc dy should cancel delete_meta
+ input_keys('dch')
+ input_keys('dyh')
+ # cd cy should cancel change_meta
+ input_keys('cdh')
+ input_keys('cyh')
+ # yd yc should cancel yank_meta
+ # P should not paste yanked text because yank_meta is canceled
+ input_keys('ydhP')
+ input_keys('ychP')
+ assert_line_around_cursor('aa', 'a bbb ccc')
+ end
+
+ def test_cancel_waiting_with_symbol_key
+ input_keys("aaa bbb lll\C-[0")
+ assert_line_around_cursor('', 'aaa bbb lll')
+ # ed_next_char should move cursor right and cancel vi_next_char
+ input_keys('f')
+ input_key_by_symbol(:ed_next_char)
+ input_keys('l')
+ assert_line_around_cursor('aa', 'a bbb lll')
+ # ed_next_char should move cursor right and cancel delete_meta
+ input_keys('d')
+ input_key_by_symbol(:ed_next_char)
+ input_keys('l')
+ assert_line_around_cursor('aaa ', 'bbb lll')
end
def test_unimplemented_vi_command_should_be_no_op
input_keys("abc\C-[h")
- assert_byte_pointer_size('a')
- assert_cursor(1)
- assert_cursor_max(3)
- assert_line('abc')
+ assert_line_around_cursor('a', 'bc')
input_keys('@')
- assert_byte_pointer_size('a')
- assert_cursor(1)
- assert_cursor_max(3)
- assert_line('abc')
+ assert_line_around_cursor('a', 'bc')
end
def test_vi_yank
- input_keys("foo bar\C-[0")
- assert_line('foo bar')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(7)
+ input_keys("foo bar\C-[2h")
+ assert_line_around_cursor('foo ', 'bar')
input_keys('y3l')
- assert_line('foo bar')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(7)
+ assert_line_around_cursor('foo ', 'bar')
input_keys('P')
- assert_line('foofoo bar')
- assert_byte_pointer_size('fo')
- assert_cursor(2)
- assert_cursor_max(10)
+ assert_line_around_cursor('foo ba', 'rbar')
+ input_keys('3h3yhP')
+ assert_line_around_cursor('foofo', 'o barbar')
+ input_keys('yyP')
+ assert_line_around_cursor('foofofoofoo barba', 'ro barbar')
end
def test_vi_end_word_with_operator
input_keys("foo bar\C-[0")
- assert_line('foo bar')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(7)
+ assert_line_around_cursor('', 'foo bar')
input_keys('de')
- assert_line(' bar')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(4)
+ assert_line_around_cursor('', ' bar')
input_keys('de')
- assert_line('')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('', '')
input_keys('de')
- assert_line('')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('', '')
end
def test_vi_end_big_word_with_operator
input_keys("aaa b{b}}}b\C-[0")
- assert_line('aaa b{b}}}b')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(13)
+ assert_line_around_cursor('', 'aaa b{b}}}b')
input_keys('dE')
- assert_line(' b{b}}}b')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(10)
+ assert_line_around_cursor('', ' b{b}}}b')
input_keys('dE')
- assert_line('')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('', '')
input_keys('dE')
- assert_line('')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
+ assert_line_around_cursor('', '')
end
def test_vi_next_char_with_operator
input_keys("foo bar\C-[0")
- assert_line('foo bar')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(7)
+ assert_line_around_cursor('', 'foo bar')
input_keys('df ')
- assert_line('bar')
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(3)
- end
-
- def test_pasting
- start_pasting
- input_keys('ab')
- finish_pasting
- input_keys('c')
- assert_line('abc')
- assert_byte_pointer_size('abc')
- assert_cursor(3)
- assert_cursor_max(3)
- end
-
- def test_pasting_fullwidth
- start_pasting
- input_keys('ã‚')
- finish_pasting
- input_keys('ã„')
- assert_line('ã‚ã„')
- assert_byte_pointer_size('ã‚ã„')
- assert_cursor(4)
- assert_cursor_max(4)
+ assert_line_around_cursor('', 'bar')
end
def test_ed_delete_next_char_at_eol
input_keys('"ã‚"')
- assert_line('"ã‚"')
- assert_byte_pointer_size('"ã‚"')
- assert_cursor(4)
- assert_cursor_max(4)
+ assert_line_around_cursor('"ã‚"', '')
input_keys("\C-[")
- assert_line('"ã‚"')
- assert_byte_pointer_size('"ã‚')
- assert_cursor(3)
- assert_cursor_max(4)
+ assert_line_around_cursor('"ã‚', '"')
input_keys('xa"')
- assert_line('"ã‚"')
- assert_byte_pointer_size('"ã‚"')
- assert_cursor(4)
- assert_cursor_max(4)
+ assert_line_around_cursor('"ã‚"', '')
end
def test_vi_kill_line_prev
input_keys("\C-u", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
input_keys('abc')
- assert_byte_pointer_size('abc')
- assert_cursor(3)
- assert_cursor_max(3)
+ assert_line_around_cursor('abc', '')
input_keys("\C-u", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(0)
- assert_line('')
+ assert_line_around_cursor('', '')
input_keys('abc')
input_keys("\C-[\C-u", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(1)
- assert_line('c')
+ assert_line_around_cursor('', 'c')
input_keys("\C-u", false)
- assert_byte_pointer_size('')
- assert_cursor(0)
- assert_cursor_max(1)
- assert_line('c')
+ assert_line_around_cursor('', 'c')
+ end
+
+ def test_vi_change_to_eol
+ input_keys("abcdef\C-[2hC")
+ assert_line_around_cursor('abc', '')
+ input_keys("\C-[0C")
+ assert_line_around_cursor('', '')
+ assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode)
end
def test_vi_motion_operators
@@ -1462,4 +911,9 @@ class Reline::KeyActor::ViInsert::Test < Reline::TestCase
input_keys("test = { foo: bar }\C-[BBBldt}b")
end
end
+
+ def test_emacs_editing_mode
+ @line_editor.__send__(:emacs_editing_mode, nil)
+ assert(@config.editing_mode_is?(:emacs))
+ end
end
diff --git a/test/reline/test_key_stroke.rb b/test/reline/test_key_stroke.rb
index d377a1e972..cd205c7d9e 100644
--- a/test/reline/test_key_stroke.rb
+++ b/test/reline/test_key_stroke.rb
@@ -36,6 +36,27 @@ class Reline::KeyStroke::Test < Reline::TestCase
assert_equal(:matched, stroke.match_status("abzwabk".bytes))
end
+ def test_match_unknown
+ config = Reline::Config.new
+ config.add_default_key_binding("\e[9abc".bytes, 'x')
+ stroke = Reline::KeyStroke.new(config)
+ sequences = [
+ "\e[9abc",
+ "\e[9d",
+ "\e[A", # Up
+ "\e[1;1R", # Cursor position report
+ "\e[15~", # F5
+ "\eOP", # F1
+ "\e\e[A" # Option+Up
+ ]
+ sequences.each do |seq|
+ assert_equal(:matched, stroke.match_status(seq.bytes))
+ (1...seq.size).each do |i|
+ assert_equal(:matching, stroke.match_status(seq.bytes.take(i)))
+ end
+ end
+ end
+
def test_expand
config = Reline::Config.new
{
@@ -45,6 +66,11 @@ class Reline::KeyStroke::Test < Reline::TestCase
end
stroke = Reline::KeyStroke.new(config)
assert_equal('123'.bytes, stroke.expand('abc'.bytes))
+ # CSI sequence
+ assert_equal([:ed_unassigned] + 'bc'.bytes, stroke.expand("\e[1;2;3;4;5abc".bytes))
+ assert_equal([:ed_unassigned] + 'BC'.bytes, stroke.expand("\e\e[ABC".bytes))
+ # SS3 sequence
+ assert_equal([:ed_unassigned] + 'QR'.bytes, stroke.expand("\eOPQR".bytes))
end
def test_oneshot_key_bindings
diff --git a/test/reline/test_line_editor.rb b/test/reline/test_line_editor.rb
index 8399e76e92..7a38ecd596 100644
--- a/test/reline/test_line_editor.rb
+++ b/test/reline/test_line_editor.rb
@@ -1,13 +1,185 @@
require_relative 'helper'
require 'reline/line_editor'
+require 'stringio'
-class Reline::LineEditor::Test < Reline::TestCase
- def test_range_subtract
- dummy_config = nil
- editor = Reline::LineEditor.new(dummy_config, 'ascii-8bit')
- base_ranges = [3...5, 4...10, 6...8, 12...15, 15...20]
- subtract_ranges = [5...7, 8...9, 11...13, 17...18, 18...19]
- expected_result = [3...5, 7...8, 9...10, 13...17, 19...20]
- assert_equal expected_result, editor.send(:range_subtract, base_ranges, subtract_ranges)
+class Reline::LineEditor
+ class RenderLineDifferentialTest < Reline::TestCase
+ module TestIO
+ RESET_COLOR = "\e[0m"
+
+ def self.move_cursor_column(col)
+ @output << "[COL_#{col}]"
+ end
+
+ def self.erase_after_cursor
+ @output << '[ERASE]'
+ end
+ end
+
+ def setup
+ verbose, $VERBOSE = $VERBOSE, nil
+ @line_editor = Reline::LineEditor.new(nil, Encoding::UTF_8)
+ @original_iogate = Reline::IOGate
+ @output = StringIO.new
+ @line_editor.instance_variable_set(:@screen_size, [24, 80])
+ @line_editor.instance_variable_set(:@output, @output)
+ Reline.send(:remove_const, :IOGate)
+ Reline.const_set(:IOGate, TestIO)
+ Reline::IOGate.instance_variable_set(:@output, @output)
+ ensure
+ $VERBOSE = verbose
+ end
+
+ def assert_output(expected)
+ @output.reopen(+'')
+ yield
+ actual = @output.string
+ assert_equal(expected, actual.gsub("\e[0m", ''))
+ end
+
+ def teardown
+ Reline.send(:remove_const, :IOGate)
+ Reline.const_set(:IOGate, @original_iogate)
+ end
+
+ def test_line_increase_decrease
+ assert_output '[COL_0]bb' do
+ @line_editor.render_line_differential([[0, 1, 'a']], [[0, 2, 'bb']])
+ end
+
+ assert_output '[COL_0]b[COL_1][ERASE]' do
+ @line_editor.render_line_differential([[0, 2, 'aa']], [[0, 1, 'b']])
+ end
+ end
+
+ def test_dialog_appear_disappear
+ assert_output '[COL_3]dialog' do
+ @line_editor.render_line_differential([[0, 1, 'a']], [[0, 1, 'a'], [3, 6, 'dialog']])
+ end
+
+ assert_output '[COL_3]dialog' do
+ @line_editor.render_line_differential([[0, 10, 'a' * 10]], [[0, 10, 'a' * 10], [3, 6, 'dialog']])
+ end
+
+ assert_output '[COL_1][ERASE]' do
+ @line_editor.render_line_differential([[0, 1, 'a'], [3, 6, 'dialog']], [[0, 1, 'a']])
+ end
+
+ assert_output '[COL_3]aaaaaa' do
+ @line_editor.render_line_differential([[0, 10, 'a' * 10], [3, 6, 'dialog']], [[0, 10, 'a' * 10]])
+ end
+ end
+
+ def test_dialog_change
+ assert_output '[COL_3]DIALOG' do
+ @line_editor.render_line_differential([[0, 2, 'a'], [3, 6, 'dialog']], [[0, 2, 'a'], [3, 6, 'DIALOG']])
+ end
+
+ assert_output '[COL_3]DIALOG' do
+ @line_editor.render_line_differential([[0, 10, 'a' * 10], [3, 6, 'dialog']], [[0, 10, 'a' * 10], [3, 6, 'DIALOG']])
+ end
+ end
+
+ def test_update_under_dialog
+ assert_output '[COL_0]b[COL_1] ' do
+ @line_editor.render_line_differential([[0, 2, 'aa'], [4, 6, 'dialog']], [[0, 1, 'b'], [4, 6, 'dialog']])
+ end
+
+ assert_output '[COL_0]bbb[COL_9]b' do
+ @line_editor.render_line_differential([[0, 10, 'a' * 10], [3, 6, 'dialog']], [[0, 10, 'b' * 10], [3, 6, 'dialog']])
+ end
+
+ assert_output '[COL_0]b[COL_1] [COL_9][ERASE]' do
+ @line_editor.render_line_differential([[0, 10, 'a' * 10], [3, 6, 'dialog']], [[0, 1, 'b'], [3, 6, 'dialog']])
+ end
+ end
+
+ def test_dialog_move
+ assert_output '[COL_3]dialog[COL_9][ERASE]' do
+ @line_editor.render_line_differential([[0, 1, 'a'], [4, 6, 'dialog']], [[0, 1, 'a'], [3, 6, 'dialog']])
+ end
+
+ assert_output '[COL_4] [COL_5]dialog' do
+ @line_editor.render_line_differential([[0, 1, 'a'], [4, 6, 'dialog']], [[0, 1, 'a'], [5, 6, 'dialog']])
+ end
+
+ assert_output '[COL_2]dialog[COL_8]a' do
+ @line_editor.render_line_differential([[0, 10, 'a' * 10], [3, 6, 'dialog']], [[0, 10, 'a' * 10], [2, 6, 'dialog']])
+ end
+
+ assert_output '[COL_2]a[COL_3]dialog' do
+ @line_editor.render_line_differential([[0, 10, 'a' * 10], [2, 6, 'dialog']], [[0, 10, 'a' * 10], [3, 6, 'dialog']])
+ end
+ end
+
+ def test_multibyte
+ base = [0, 12, '一二三一二三']
+ left = [0, 3, 'LLL']
+ right = [9, 3, 'RRR']
+ front = [3, 6, 'FFFFFF']
+ # 一 FFFFFF 三
+ # 一二三一二三
+ assert_output '[COL_2]二三一二' do
+ @line_editor.render_line_differential([base, front], [base, nil])
+ end
+
+ # LLLFFFFFF 三
+ # LLL 三一二三
+ assert_output '[COL_3] 三一二' do
+ @line_editor.render_line_differential([base, left, front], [base, left, nil])
+ end
+
+ # 一 FFFFFFRRR
+ # 一二三一 RRR
+ assert_output '[COL_2]二三一 ' do
+ @line_editor.render_line_differential([base, right, front], [base, right, nil])
+ end
+
+ # LLLFFFFFFRRR
+ # LLL 三一 RRR
+ assert_output '[COL_3] 三一 ' do
+ @line_editor.render_line_differential([base, left, right, front], [base, left, right, nil])
+ end
+ end
+
+ def test_complicated
+ state_a = [nil, [19, 7, 'bbbbbbb'], [15, 8, 'cccccccc'], [10, 5, 'ddddd'], [18, 4, 'eeee'], [1, 3, 'fff'], [17, 2, 'gg'], [7, 1, 'h']]
+ state_b = [[5, 9, 'aaaaaaaaa'], nil, [15, 8, 'cccccccc'], nil, [18, 4, 'EEEE'], [25, 4, 'ffff'], [17, 2, 'gg'], [2, 2, 'hh']]
+ # state_a: " fff h dddddccggeeecbbb"
+ # state_b: " hh aaaaaaaaa ccggEEEc ffff"
+
+ assert_output '[COL_1] [COL_2]hh[COL_5]aaaaaaaaa[COL_14] [COL_19]EEE[COL_23] [COL_25]ffff' do
+ @line_editor.render_line_differential(state_a, state_b)
+ end
+
+ assert_output '[COL_1]fff[COL_5] [COL_7]h[COL_8] [COL_10]ddddd[COL_19]eee[COL_23]bbb[COL_26][ERASE]' do
+ @line_editor.render_line_differential(state_b, state_a)
+ end
+ end
+ end
+
+ def test_menu_info_format
+ list = %w[aa b c d e f g hhh i j k]
+ col3 = [
+ 'aa e i',
+ 'b f j',
+ 'c g k',
+ 'd hhh'
+ ]
+ col2 = [
+ 'aa g',
+ 'b hhh',
+ 'c i',
+ 'd j',
+ 'e k',
+ 'f'
+ ]
+ assert_equal(col3, Reline::LineEditor::MenuInfo.new(list).lines(19))
+ assert_equal(col3, Reline::LineEditor::MenuInfo.new(list).lines(15))
+ assert_equal(col2, Reline::LineEditor::MenuInfo.new(list).lines(14))
+ assert_equal(col2, Reline::LineEditor::MenuInfo.new(list).lines(10))
+ assert_equal(list, Reline::LineEditor::MenuInfo.new(list).lines(9))
+ assert_equal(list, Reline::LineEditor::MenuInfo.new(list).lines(0))
+ assert_equal([], Reline::LineEditor::MenuInfo.new([]).lines(10))
end
end
diff --git a/test/reline/test_macro.rb b/test/reline/test_macro.rb
index 76a677c834..04aa6474b4 100644
--- a/test/reline/test_macro.rb
+++ b/test/reline/test_macro.rb
@@ -4,9 +4,8 @@ class Reline::MacroTest < Reline::TestCase
def setup
Reline.send(:test_mode)
@config = Reline::Config.new
- @encoding = Reline::IOGate.encoding
+ @encoding = Reline.core.encoding
@line_editor = Reline::LineEditor.new(@config, @encoding)
- @line_editor.instance_variable_set(:@screen_size, [24, 80])
@output = @line_editor.output = File.open(IO::NULL, "w")
end
diff --git a/test/reline/test_reline.rb b/test/reline/test_reline.rb
index 460fb7872d..40c880c11f 100644
--- a/test/reline/test_reline.rb
+++ b/test/reline/test_reline.rb
@@ -8,6 +8,7 @@ class Reline::Test < Reline::TestCase
end
def setup
+ Reline.send(:test_mode)
Reline.output_modifier_proc = nil
Reline.completion_proc = nil
Reline.prompt_proc = nil
@@ -284,7 +285,7 @@ class Reline::Test < Reline::TestCase
input, to_write = IO.pipe
to_read, output = IO.pipe
unless Reline.__send__(:input=, input)
- omit "Setting to input is not effective on #{Reline::IOGate}"
+ omit "Setting to input is not effective on #{Reline.core.io_gate}"
end
Reline.output = output
@@ -302,12 +303,12 @@ class Reline::Test < Reline::TestCase
def test_vi_editing_mode
Reline.vi_editing_mode
- assert_equal(Reline::KeyActor::ViInsert, Reline.send(:core).config.editing_mode.class)
+ assert_equal(Reline::KeyActor::ViInsert, Reline.core.config.editing_mode.class)
end
def test_emacs_editing_mode
Reline.emacs_editing_mode
- assert_equal(Reline::KeyActor::Emacs, Reline.send(:core).config.editing_mode.class)
+ assert_equal(Reline::KeyActor::Emacs, Reline.core.config.editing_mode.class)
end
def test_add_dialog_proc
@@ -373,12 +374,12 @@ class Reline::Test < Reline::TestCase
def test_dumb_terminal
lib = File.expand_path("../../lib", __dir__)
- out = IO.popen([{"TERM"=>"dumb"}, Reline.test_rubybin, "-I#{lib}", "-rreline", "-e", "p Reline::IOGate"], &:read)
+ out = IO.popen([{"TERM"=>"dumb"}, Reline.test_rubybin, "-I#{lib}", "-rreline", "-e", "p Reline.core.io_gate"], &:read)
assert_equal("Reline::GeneralIO", out.chomp)
end
def get_reline_encoding
- if encoding = Reline::IOGate.encoding
+ if encoding = Reline.core.encoding
encoding
elsif RUBY_PLATFORM =~ /mswin|mingw/
Encoding::UTF_8
diff --git a/test/reline/test_reline_key.rb b/test/reline/test_reline_key.rb
index fb700a6f2e..7f9a11394a 100644
--- a/test/reline/test_reline_key.rb
+++ b/test/reline/test_reline_key.rb
@@ -3,6 +3,7 @@ require "reline"
class Reline::TestKey < Reline::TestCase
def setup
+ Reline.test_mode
end
def teardown
diff --git a/test/reline/test_string_processing.rb b/test/reline/test_string_processing.rb
index 5db97545da..c9b9e38643 100644
--- a/test/reline/test_string_processing.rb
+++ b/test/reline/test_string_processing.rb
@@ -6,7 +6,7 @@ class Reline::LineEditor::StringProcessingTest < Reline::TestCase
@prompt = '> '
@config = Reline::Config.new
Reline::HISTORY.instance_variable_set(:@config, @config)
- @encoding = Reline::IOGate.encoding
+ @encoding = Reline.core.encoding
@line_editor = Reline::LineEditor.new(@config, @encoding)
@line_editor.reset(@prompt, encoding: @encoding)
end
@@ -30,10 +30,7 @@ class Reline::LineEditor::StringProcessingTest < Reline::TestCase
@line_editor.instance_variable_set(:@is_multiline, true)
@line_editor.instance_variable_set(:@buffer_of_lines, buf)
- @line_editor.instance_variable_set(:@line, buf[1])
@line_editor.instance_variable_set(:@byte_pointer, 3)
- @line_editor.instance_variable_set(:@cursor, 3)
- @line_editor.instance_variable_set(:@cursor_max, 11)
@line_editor.instance_variable_set(:@line_index, 1)
@line_editor.instance_variable_set(:@completion_proc, proc { |target|
assert_equal('p', target)
@@ -42,10 +39,7 @@ class Reline::LineEditor::StringProcessingTest < Reline::TestCase
@line_editor.instance_variable_set(:@is_multiline, true)
@line_editor.instance_variable_set(:@buffer_of_lines, buf)
- @line_editor.instance_variable_set(:@line, buf[1])
@line_editor.instance_variable_set(:@byte_pointer, 6)
- @line_editor.instance_variable_set(:@cursor, 6)
- @line_editor.instance_variable_set(:@cursor_max, 11)
@line_editor.instance_variable_set(:@line_index, 1)
@line_editor.instance_variable_set(:@completion_proc, proc { |target, pre, post|
assert_equal('puts', target)
@@ -54,10 +48,7 @@ class Reline::LineEditor::StringProcessingTest < Reline::TestCase
})
@line_editor.__send__(:call_completion_proc)
- @line_editor.instance_variable_set(:@line, buf[0])
@line_editor.instance_variable_set(:@byte_pointer, 6)
- @line_editor.instance_variable_set(:@cursor, 6)
- @line_editor.instance_variable_set(:@cursor_max, 8)
@line_editor.instance_variable_set(:@line_index, 0)
@line_editor.instance_variable_set(:@completion_proc, proc { |target, pre, post|
assert_equal('ho', target)
@@ -66,10 +57,7 @@ class Reline::LineEditor::StringProcessingTest < Reline::TestCase
})
@line_editor.__send__(:call_completion_proc)
- @line_editor.instance_variable_set(:@line, buf[2])
@line_editor.instance_variable_set(:@byte_pointer, 1)
- @line_editor.instance_variable_set(:@cursor, 1)
- @line_editor.instance_variable_set(:@cursor_max, 3)
@line_editor.instance_variable_set(:@line_index, 2)
@line_editor.instance_variable_set(:@completion_proc, proc { |target, pre, post|
assert_equal('e', target)
diff --git a/test/reline/test_terminfo.rb b/test/reline/test_terminfo.rb
index dda9b32495..4e59c54838 100644
--- a/test/reline/test_terminfo.rb
+++ b/test/reline/test_terminfo.rb
@@ -58,4 +58,4 @@ class Reline::Terminfo::Test < Reline::TestCase
assert_raise(Reline::Terminfo::TerminfoError) { Reline::Terminfo.tigetnum('unknown') }
assert_raise(Reline::Terminfo::TerminfoError) { Reline::Terminfo.tigetnum(nil) }
end
-end if Reline::Terminfo.enabled?
+end if Reline::Terminfo.enabled? && Reline::Terminfo.term_supported?
diff --git a/test/reline/test_unicode.rb b/test/reline/test_unicode.rb
index 834f7114c4..deba4d4681 100644
--- a/test/reline/test_unicode.rb
+++ b/test/reline/test_unicode.rb
@@ -38,11 +38,16 @@ class Reline::Unicode::Test < Reline::TestCase
assert_equal [["ab\e]0;1\ac", nil, "\e]0;1\ad"], 2], Reline::Unicode.split_by_width("ab\e]0;1\acd", 3)
end
+ def test_split_by_width_csi_reset_sgr_optimization
+ assert_equal [["\e[1ma\e[mb\e[2mc", nil, "\e[2md\e[0me\e[3mf", nil, "\e[3mg"], 3], Reline::Unicode.split_by_width("\e[1ma\e[mb\e[2mcd\e[0me\e[3mfg", 3)
+ assert_equal [["\e[1ma\1\e[mzero\e[0m\2\e[2mb", nil, "\e[1m\e[2mc"], 2], Reline::Unicode.split_by_width("\e[1ma\1\e[mzero\e[0m\2\e[2mbc", 2)
+ end
+
def test_take_range
assert_equal 'cdef', Reline::Unicode.take_range('abcdefghi', 2, 4)
assert_equal 'ã‚de', Reline::Unicode.take_range('abã‚def', 2, 4)
- assert_equal 'zerocdef', Reline::Unicode.take_range("ab\1zero\2cdef", 2, 4)
- assert_equal 'bzerocde', Reline::Unicode.take_range("ab\1zero\2cdef", 1, 4)
+ assert_equal "\1zero\2cdef", Reline::Unicode.take_range("ab\1zero\2cdef", 2, 4)
+ assert_equal "b\1zero\2cde", Reline::Unicode.take_range("ab\1zero\2cdef", 1, 4)
assert_equal "\e[31mcd\e[42mef", Reline::Unicode.take_range("\e[31mabcd\e[42mefg", 2, 4)
assert_equal "\e]0;1\acd", Reline::Unicode.take_range("ab\e]0;1\acd", 2, 3)
assert_equal 'ã„ã†', Reline::Unicode.take_range('ã‚ã„ã†ãˆãŠ', 2, 4)
@@ -62,4 +67,26 @@ class Reline::Unicode::Test < Reline::TestCase
assert_equal 10, Reline::Unicode.calculate_width('ã‚ã„ã†ãˆãŠ')
assert_equal 10, Reline::Unicode.calculate_width('ã‚ã„ã†ãˆãŠ', true)
end
+
+ def test_take_mbchar_range
+ assert_equal ['cdef', 2, 4], Reline::Unicode.take_mbchar_range('abcdefghi', 2, 4)
+ assert_equal ['cdef', 2, 4], Reline::Unicode.take_mbchar_range('abcdefghi', 2, 4, padding: true)
+ assert_equal ['cdef', 2, 4], Reline::Unicode.take_mbchar_range('abcdefghi', 2, 4, cover_begin: true)
+ assert_equal ['cdef', 2, 4], Reline::Unicode.take_mbchar_range('abcdefghi', 2, 4, cover_end: true)
+ assert_equal ['ã„ã†', 2, 4], Reline::Unicode.take_mbchar_range('ã‚ã„ã†ãˆãŠ', 2, 4)
+ assert_equal ['ã„ã†', 2, 4], Reline::Unicode.take_mbchar_range('ã‚ã„ã†ãˆãŠ', 2, 4, padding: true)
+ assert_equal ['ã„ã†', 2, 4], Reline::Unicode.take_mbchar_range('ã‚ã„ã†ãˆãŠ', 2, 4, cover_begin: true)
+ assert_equal ['ã„ã†', 2, 4], Reline::Unicode.take_mbchar_range('ã‚ã„ã†ãˆãŠ', 2, 4, cover_end: true)
+ assert_equal ['ã†', 4, 2], Reline::Unicode.take_mbchar_range('ã‚ã„ã†ãˆãŠ', 3, 4)
+ assert_equal [' ㆠ', 3, 4], Reline::Unicode.take_mbchar_range('ã‚ã„ã†ãˆãŠ', 3, 4, padding: true)
+ assert_equal ['ã„ã†', 2, 4], Reline::Unicode.take_mbchar_range('ã‚ã„ã†ãˆãŠ', 3, 4, cover_begin: true)
+ assert_equal ['ã†ãˆ', 4, 4], Reline::Unicode.take_mbchar_range('ã‚ã„ã†ãˆãŠ', 3, 4, cover_end: true)
+ assert_equal ['ã„ㆠ', 2, 5], Reline::Unicode.take_mbchar_range('ã‚ã„ã†ãˆãŠ', 3, 4, cover_begin: true, padding: true)
+ assert_equal [' ã†ãˆ', 3, 5], Reline::Unicode.take_mbchar_range('ã‚ã„ã†ãˆãŠ', 3, 4, cover_end: true, padding: true)
+ assert_equal [' ã†ãˆãŠ ', 3, 10], Reline::Unicode.take_mbchar_range('ã‚ã„ã†ãˆãŠ', 3, 10, padding: true)
+ assert_equal [" \e[41mã†ãˆãŠ\e[0m ", 3, 10], Reline::Unicode.take_mbchar_range("ã‚ã„\e[41mã†ãˆãŠ", 3, 10, padding: true)
+ assert_equal ["\e[41m \e[42mã„\e[43m ", 1, 4], Reline::Unicode.take_mbchar_range("\e[41mã‚\e[42mã„\e[43mã†", 1, 4, padding: true)
+ assert_equal ["\e[31mc\1ABC\2d\e[0mef", 2, 4], Reline::Unicode.take_mbchar_range("\e[31mabc\1ABC\2d\e[0mefghi", 2, 4)
+ assert_equal ["\e[41m \e[42mã„\e[43m ", 1, 4], Reline::Unicode.take_mbchar_range("\e[41mã‚\e[42mã„\e[43mã†", 1, 4, padding: true)
+ end
end
diff --git a/test/reline/test_within_pipe.rb b/test/reline/test_within_pipe.rb
index 930f99c32d..a42ca755fc 100644
--- a/test/reline/test_within_pipe.rb
+++ b/test/reline/test_within_pipe.rb
@@ -3,14 +3,14 @@ require_relative 'helper'
class Reline::WithinPipeTest < Reline::TestCase
def setup
Reline.send(:test_mode)
- @encoding = Reline::IOGate.encoding
+ @encoding = Reline.core.encoding
@input_reader, @writer = IO.pipe(@encoding)
Reline.input = @input_reader
@reader, @output_writer = IO.pipe(@encoding)
@output = Reline.output = @output_writer
- @config = Reline.send(:core).config
+ @config = Reline.core.config
@config.keyseq_timeout *= 600 if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? # for --jit-wait CI
- @line_editor = Reline.send(:core).line_editor
+ @line_editor = Reline.core.line_editor
end
def teardown
diff --git a/test/reline/yamatanooroti/multiline_repl b/test/reline/yamatanooroti/multiline_repl
index 73f5159ee2..eba410f6dd 100755
--- a/test/reline/yamatanooroti/multiline_repl
+++ b/test/reline/yamatanooroti/multiline_repl
@@ -9,10 +9,6 @@ require 'optparse'
require_relative 'termination_checker'
opt = OptionParser.new
-opt.on('--prompt-list-cache-timeout VAL') { |v|
- Reline::LineEditor.__send__(:remove_const, :PROMPT_LIST_CACHE_TIMEOUT)
- Reline::LineEditor::PROMPT_LIST_CACHE_TIMEOUT = v.to_f
-}
opt.on('--dynamic-prompt') {
Reline.prompt_proc = proc { |lines|
lines.each_with_index.map { |l, i|
@@ -59,8 +55,24 @@ opt.on('--dynamic-prompt-show-line') {
}
}
}
+
+def assert_auto_indent_params(lines, line_index, byte_pointer, is_newline)
+ raise 'Wrong lines type' unless lines.all?(String)
+
+ line = lines[line_index]
+ raise 'Wrong line_index value' unless line
+
+ # The condition `byte_pointer <= line.bytesize` is not satisfied. Maybe bug.
+ # Instead, loose constraint `byte_pointer <= line.bytesize + 1` seems to be satisfied when is_newline is false.
+ return if is_newline
+
+ raise 'byte_pointer out of bounds' unless byte_pointer <= line.bytesize + 1
+ raise 'Invalid byte_pointer' unless line.byteslice(0, byte_pointer).valid_encoding?
+end
+
opt.on('--auto-indent') {
Reline.auto_indent_proc = lambda do |lines, line_index, byte_pointer, is_newline|
+ assert_auto_indent_params(lines, line_index, byte_pointer, is_newline)
AutoIndent.calculate_indent(lines, line_index, byte_pointer, is_newline)
end
}
@@ -120,7 +132,7 @@ opt.on('--dialog VAL') { |v|
if v.include?('alt-scrollbar')
scrollbar = true
end
- Reline::DialogRenderInfo.new(pos: cursor_pos, contents: contents, height: height, scrollbar: scrollbar)
+ Reline::DialogRenderInfo.new(pos: cursor_pos, contents: contents, height: height, scrollbar: scrollbar, face: :completion_dialog)
})
if v.include?('alt-scrollbar')
ENV['RELINE_ALT_SCROLLBAR'] = '1'
@@ -131,12 +143,21 @@ opt.on('--complete') {
%w{String ScriptError SyntaxError Signal}.select{ |c| c.start_with?(target) }
}
}
+opt.on('--complete-menu-with-perfect-match') {
+ Reline.completion_proc = lambda { |target, preposing = nil, postposing = nil|
+ %w{abs abs2}.select{ |c| c.start_with?(target) }
+ }
+}
opt.on('--autocomplete') {
Reline.autocompletion = true
Reline.completion_proc = lambda { |target, preposing = nil, postposing = nil|
%w{String Struct Symbol ScriptError SyntaxError Signal}.select{ |c| c.start_with?(target) }
}
}
+opt.on('--autocomplete-empty') {
+ Reline.autocompletion = true
+ Reline.completion_proc = lambda { |target, preposing = nil, postposing = nil| [] }
+}
opt.on('--autocomplete-long') {
Reline.autocompletion = true
Reline.completion_proc = lambda { |target, preposing = nil, postposing = nil|
@@ -167,7 +188,7 @@ opt.on('--autocomplete-long') {
opt.on('--autocomplete-super-long') {
Reline.autocompletion = true
Reline.completion_proc = lambda { |target, preposing = nil, postposing = nil|
- c = 'A'
+ c = +'A'
2000.times.map{ s = "Str_#{c}"; c.succ!; s }.select{ |c| c.start_with?(target) }
}
}
diff --git a/test/reline/yamatanooroti/test_rendering.rb b/test/reline/yamatanooroti/test_rendering.rb
index d2053b3d85..74798c338f 100644
--- a/test/reline/yamatanooroti/test_rendering.rb
+++ b/test/reline/yamatanooroti/test_rendering.rb
@@ -3,7 +3,34 @@ require 'reline'
begin
require 'yamatanooroti'
- class Reline::TestRendering < Yamatanooroti::TestCase
+ class Reline::RenderingTest < Yamatanooroti::TestCase
+
+ FACE_CONFIGS = { no_config: "", valid_config: <<~VALID_CONFIG, incomplete_config: <<~INCOMPLETE_CONFIG }
+ require "reline"
+ Reline::Face.config(:completion_dialog) do |face|
+ face.define :default, foreground: :white, background: :blue
+ face.define :enhanced, foreground: :white, background: :magenta
+ face.define :scrollbar, foreground: :white, background: :blue
+ end
+ VALID_CONFIG
+ require "reline"
+ Reline::Face.config(:completion_dialog) do |face|
+ face.define :default, foreground: :white, background: :black
+ face.define :scrollbar, foreground: :white, background: :cyan
+ end
+ INCOMPLETE_CONFIG
+
+ def iterate_over_face_configs(&block)
+ FACE_CONFIGS.each do |config_name, face_config|
+ config_file = Tempfile.create(%w{face_config- .rb})
+ config_file.write face_config
+ block.call(config_name, config_file)
+ config_file.close
+ ensure
+ File.delete(config_file)
+ end
+ end
+
def setup
@pwd = Dir.pwd
suffix = '%010d' % Random.rand(0..65535)
@@ -170,9 +197,12 @@ begin
LINES
start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.')
write(":a\n\C-[k")
+ write("i\n:a")
+ write("\C-[h")
close
assert_screen(<<~EOC)
- Multiline REPL.
+ (ins)prompt> :a
+ => :a
(ins)prompt> :a
=> :a
(cmd)prompt> :a
@@ -237,6 +267,21 @@ begin
EOC
end
+ def test_esc_input
+ omit if Reline::IOGate.win?
+ start_terminal(5, 20, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.')
+ write("def\C-aabc")
+ write("\e") # single ESC
+ sleep 1
+ write("A")
+ write("B\eAC") # ESC + A (M-A, specified ed_unassigned in Reline::KeyActor::Emacs)
+ close
+ assert_screen(<<~EOC)
+ Multiline REPL.
+ prompt> abcABCdef
+ EOC
+ end
+
def test_prompt_with_escape_sequence
ENV['RELINE_TEST_PROMPT'] = "\1\e[30m\2prompt> \1\e[m\2"
start_terminal(5, 20, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.')
@@ -264,6 +309,21 @@ begin
EOC
end
+ def test_readline_with_multiline_input
+ start_terminal(5, 50, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --dynamic-prompt}, startup_message: 'Multiline REPL.')
+ write("def foo\n bar\nend\n")
+ write("Reline.readline('prompt> ')\n")
+ write("\C-p\C-p")
+ close
+ assert_screen(<<~EOC)
+ => :foo
+ [0000]> Reline.readline('prompt> ')
+ prompt> def foo
+ bar
+ end
+ EOC
+ end
+
def test_multiline_and_autowrap
start_terminal(10, 20, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.')
write("def aaaaaaaaaa\n 33333333\n end\C-a\C-pputs\C-e\e\C-m888888888888888")
@@ -422,6 +482,9 @@ begin
write("def a\n 8\nend\ndef b\n 3\nend\C-s8")
close
assert_screen(<<~EOC)
+ prompt> 8
+ prompt> end
+ => :a
(i-search)`8'def a
(i-search)`8' 8
(i-search)`8'end
@@ -433,6 +496,9 @@ begin
write("def a\n 8\nend\ndef b\n 3\nend\C-r8\C-j")
close
assert_screen(<<~EOC)
+ prompt> 8
+ prompt> end
+ => :a
prompt> def a
prompt> 8
prompt> end
@@ -453,18 +519,6 @@ begin
EOC
end
- def test_prompt_list_caching
- start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --prompt-list-cache-timeout 10 --dynamic-prompt}, startup_message: 'Multiline REPL.')
- write("def hoge\n 3\nend")
- close
- assert_screen(<<~EOC)
- Multiline REPL.
- [0000]> def hoge
- [0001]> 3
- [0002]> end
- EOC
- end
-
def test_broken_prompt_list
start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --broken-dynamic-prompt}, startup_message: 'Multiline REPL.')
write("def hoge\n 3\nend")
@@ -490,7 +544,7 @@ begin
end
def test_enable_bracketed_paste
- omit if Reline::IOGate.win?
+ omit if Reline.core.io_gate.win?
write_inputrc <<~LINES
set enable-bracketed-paste on
LINES
@@ -676,6 +730,7 @@ begin
EOC
write("\C-p" * 4 + "\C-e" + "\C-p" * 4)
write("2")
+ close
assert_screen(<<~EOC)
prompt> if 12
prompt> if 2
@@ -731,6 +786,24 @@ begin
EOC
end
+ def test_auto_indent_multibyte_insert_line
+ start_terminal(10, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --auto-indent}, startup_message: 'Multiline REPL.')
+ write "if true\n"
+ write "ã‚ã„ã†ãˆãŠ\n"
+ 4.times { write "\C-b\C-b\C-b\C-b\e\r" }
+ close
+ assert_screen(<<~EOC)
+ Multiline REPL.
+ prompt> if true
+ prompt> ã‚
+ prompt> ã„
+ prompt> ã†
+ prompt> ãˆ
+ prompt> ãŠ
+ prompt>
+ EOC
+ end
+
def test_newline_after_wrong_indent
start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --auto-indent}, startup_message: 'Multiline REPL.')
write "if 1\n aa"
@@ -773,6 +846,20 @@ begin
EOC
end
+ def test_auto_indent_with_various_spaces
+ start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --auto-indent}, startup_message: 'Multiline REPL.')
+ write "(\n\C-v"
+ write "\C-k\n\C-v"
+ write "\C-k)"
+ close
+ assert_screen(<<~EOC)
+ Multiline REPL.
+ prompt> (
+ prompt> ^K
+ prompt> )
+ EOC
+ end
+
def test_autowrap_in_the_middle_of_a_line
start_terminal(5, 20, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.')
write("def abcdefg; end\C-b\C-b\C-b\C-b\C-b")
@@ -858,18 +945,30 @@ begin
EOC
end
- def test_with_newline
- omit if Reline::IOGate.win?
+ def test_eof_with_newline
+ omit if Reline.core.io_gate.win?
cmd = %Q{ruby -e 'print(%Q{abc def \\e\\r})' | ruby -I#{@pwd}/lib -rreline -e 'p Reline.readline(%{> })'}
start_terminal(40, 50, ['bash', '-c', cmd])
sleep 1
- close
+ close rescue nil
assert_screen(<<~'EOC')
> abc def
"abc def "
EOC
end
+ def test_eof_without_newline
+ omit if Reline.core.io_gate.win?
+ cmd = %Q{ruby -e 'print(%{hello})' | ruby -I#{@pwd}/lib -rreline -e 'p Reline.readline(%{> })'}
+ start_terminal(40, 50, ['bash', '-c', cmd])
+ sleep 1
+ close rescue nil
+ assert_screen(<<~'EOC')
+ > hello
+ "hello"
+ EOC
+ end
+
def test_em_set_mark_and_em_exchange_mark
start_terminal(10, 50, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.')
write("aaa bbb ccc ddd\M-b\M-b\M-\x20\M-b\C-x\C-xX\C-x\C-xY")
@@ -919,75 +1018,125 @@ begin
EOC
end
- def test_simple_dialog
- start_terminal(20, 50, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --dialog simple}, startup_message: 'Multiline REPL.')
- write('a')
- write('b')
- write('c')
- write("\C-h")
+ def test_completion_menu_is_displayed_horizontally
+ start_terminal(20, 50, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --complete}, startup_message: 'Multiline REPL.')
+ write("S\t\t")
close
assert_screen(<<~'EOC')
Multiline REPL.
- prompt> ab
- Ruby is...
- A dynamic, open source programming
- language with a focus on simplicity
- and productivity. It has an elegant
- syntax that is natural to read and
- easy to write.
+ prompt> S
+ ScriptError String
+ Signal SyntaxError
EOC
end
- def test_simple_dialog_at_right_edge
- start_terminal(20, 40, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --dialog simple}, startup_message: 'Multiline REPL.')
- write('a')
- write('b')
- write('c')
- write("\C-h")
+ def test_show_all_if_ambiguous_on
+ write_inputrc <<~LINES
+ set show-all-if-ambiguous on
+ LINES
+ start_terminal(20, 50, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --complete}, startup_message: 'Multiline REPL.')
+ write("S\t")
close
assert_screen(<<~'EOC')
Multiline REPL.
- prompt> ab
- Ruby is...
- A dynamic, open source programming
- language with a focus on simplicity
- and productivity. It has an elegant
- syntax that is natural to read and
- easy to write.
+ prompt> S
+ ScriptError String
+ Signal SyntaxError
EOC
end
- def test_dialog_scroll_pushup_condition
- start_terminal(10, 50, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete}, startup_message: 'Multiline REPL.')
- write("\n" * 10)
- write("if 1\n sSt\nend")
- write("\C-p\C-h\C-e")
+ def test_show_all_if_ambiguous_on_and_menu_with_perfect_match
+ write_inputrc <<~LINES
+ set show-all-if-ambiguous on
+ LINES
+ start_terminal(20, 50, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --complete-menu-with-perfect-match}, startup_message: 'Multiline REPL.')
+ write("a\t")
+ close
assert_screen(<<~'EOC')
- prompt>
- prompt>
- prompt>
- prompt>
- prompt>
- prompt>
- prompt> if 1
- prompt> St
- prompt> enString
- Struct
+ Multiline REPL.
+ prompt> abs
+ abs abs2
EOC
end
+ def test_simple_dialog
+ iterate_over_face_configs do |config_name, config_file|
+ start_terminal(20, 50, %W{ruby -I#{@pwd}/lib -r#{config_file.path} #{@pwd}/test/reline/yamatanooroti/multiline_repl --dialog simple}, startup_message: 'Multiline REPL.')
+ write('a')
+ write('b')
+ write('c')
+ write("\C-h")
+ close
+ assert_screen(<<~'EOC', "Failed with `#{config_name}` in Face")
+ Multiline REPL.
+ prompt> ab
+ Ruby is...
+ A dynamic, open source programming
+ language with a focus on simplicity
+ and productivity. It has an elegant
+ syntax that is natural to read and
+ easy to write.
+ EOC
+ end
+ end
+
+ def test_simple_dialog_at_right_edge
+ iterate_over_face_configs do |config_name, config_file|
+ start_terminal(20, 40, %W{ruby -I#{@pwd}/lib -r#{config_file.path} #{@pwd}/test/reline/yamatanooroti/multiline_repl --dialog simple}, startup_message: 'Multiline REPL.')
+ write('a')
+ write('b')
+ write('c')
+ write("\C-h")
+ close
+ assert_screen(<<~'EOC')
+ Multiline REPL.
+ prompt> ab
+ Ruby is...
+ A dynamic, open source programming
+ language with a focus on simplicity
+ and productivity. It has an elegant
+ syntax that is natural to read and
+ easy to write.
+ EOC
+ end
+ end
+
+ def test_dialog_scroll_pushup_condition
+ iterate_over_face_configs do |config_name, config_file|
+ start_terminal(10, 50, %W{ruby -I#{@pwd}/lib -r#{config_file.path} #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete}, startup_message: 'Multiline REPL.')
+ write("\n" * 10)
+ write("if 1\n sSts\nend")
+ write("\C-p\C-h\C-e\C-h")
+ close
+ assert_screen(<<~'EOC')
+ prompt>
+ prompt>
+ prompt>
+ prompt>
+ prompt>
+ prompt>
+ prompt> if 1
+ prompt> St
+ prompt> enString
+ Struct
+ EOC
+ end
+ end
+
def test_simple_dialog_with_scroll_screen
- start_terminal(5, 50, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --dialog simple}, startup_message: 'Multiline REPL.')
- write("if 1\n 2\n 3\n 4\n 5\n 6")
- write("\C-p\C-n\C-p\C-p\C-p#")
- close
- assert_screen(<<~'EOC')
- prompt> 2
- prompt> 3#
- prompt> 4
- prompt> 5
- prompt> 6 Ruby is...
- EOC
+ iterate_over_face_configs do |config_name, config_file|
+ start_terminal(5, 50, %W{ruby -I#{@pwd}/lib -r#{config_file.path} #{@pwd}/test/reline/yamatanooroti/multiline_repl --dialog simple}, startup_message: 'Multiline REPL.')
+ write("if 1\n 2\n 3\n 4\n 5\n 6")
+ write("\C-p\C-n\C-p\C-p\C-p#")
+ close
+ assert_screen(<<~'EOC')
+ prompt> 2
+ prompt> 3#
+ prompt> 4
+ prompt> 5 Ruby is...
+ prompt> 6 A dynamic, open source programming
+ EOC
+ end
end
def test_autocomplete_at_bottom
@@ -1036,11 +1185,53 @@ begin
assert_screen(<<~'EOC')
Multiline REPL.
prompt> St
- r String
+ r
+ String
+ Struct
+ EOC
+ end
+
+ def test_autocomplete_target_at_end_of_line
+ start_terminal(20, 20, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete}, startup_message: 'Multiline REPL.')
+ write(' ')
+ write('Str')
+ write("\C-i")
+ close
+ assert_screen(<<~'EOC')
+ Multiline REPL.
+ prompt> Str
+ ing String
+ Struct
+ EOC
+ end
+
+ def test_autocomplete_completed_input_is_wrapped
+ start_terminal(20, 20, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete}, startup_message: 'Multiline REPL.')
+ write(' ')
+ write('Str')
+ write("\C-i")
+ close
+ assert_screen(<<~'EOC')
+ Multiline REPL.
+ prompt> Stri
+ ng String
Struct
EOC
end
+ def test_force_insert_before_autocomplete
+ start_terminal(20, 20, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete}, startup_message: 'Multiline REPL.')
+ write('Sy')
+ write(";St\t\t")
+ close
+ assert_screen(<<~'EOC')
+ Multiline REPL.
+ prompt> Sy;Struct
+ String
+ Struct
+ EOC
+ end
+
def test_simple_dialog_with_scroll_key
start_terminal(20, 50, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --dialog long,scrollkey}, startup_message: 'Multiline REPL.')
write('a')
@@ -1143,6 +1334,32 @@ begin
EOC
end
+ def test_autocomplete_empty_string
+ start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete}, startup_message: 'Multiline REPL.')
+ write("\C-i")
+ close
+ assert_screen(<<~'EOC')
+ Multiline REPL.
+ prompt> String
+ String â–ˆ
+ Struct â–€
+ Symbol
+ EOC
+ end
+
+ def test_paste_code_with_tab_indent_does_not_fail
+ start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete-empty}, startup_message: 'Multiline REPL.')
+ write("2.times do\n\tputs\n\tputs\nend")
+ close
+ assert_screen(<<~'EOC')
+ Multiline REPL.
+ prompt> 2.times do
+ prompt> puts
+ prompt> puts
+ prompt> end
+ EOC
+ end
+
def test_autocomplete_after_2nd_line
start_terminal(20, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete}, startup_message: 'Multiline REPL.')
write("def hoge\n Str")
@@ -1556,6 +1773,37 @@ begin
EOC
end
+ def test_exit_with_ctrl_d
+ start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --auto-indent}, startup_message: 'Multiline REPL.')
+ begin
+ write("\C-d")
+ close
+ rescue EOFError
+ # EOFError is raised when process terminated.
+ end
+ assert_screen(<<~EOC)
+ Multiline REPL.
+ prompt>
+ EOC
+ end
+
+ def test_thread_safe
+ start_terminal(6, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --auto-indent}, startup_message: 'Multiline REPL.')
+ write("[Thread.new{Reline.readline'>'},Thread.new{Reline.readmultiline('>'){true}}].map(&:join).size\n")
+ write("exit\n")
+ write("exit\n")
+ write("42\n")
+ close
+ assert_screen(<<~EOC)
+ >exit
+ >exit
+ => 2
+ prompt> 42
+ => 42
+ prompt>
+ EOC
+ end
+
def write_inputrc(content)
File.open(@inputrc_file, 'w') do |f|
f.write content
diff --git a/test/resolv/test_dns.rb b/test/resolv/test_dns.rb
index 9d243bbf50..40c5406db8 100644
--- a/test/resolv/test_dns.rb
+++ b/test/resolv/test_dns.rb
@@ -44,6 +44,16 @@ class TestResolvDNS < Test::Unit::TestCase
BasicSocket.do_not_reverse_lookup = @save_do_not_reverse_lookup
end
+ def with_tcp(host, port)
+ t = TCPServer.new(host, port)
+ begin
+ t.listen(1)
+ yield t
+ ensure
+ t.close
+ end
+ end
+
def with_udp(host, port)
u = UDPSocket.new
begin
@@ -54,6 +64,50 @@ class TestResolvDNS < Test::Unit::TestCase
end
end
+ def with_udp_and_tcp(host, port)
+ if port == 0
+ # Automatic port; we might need to retry until we find a port which is free on both UDP _and_ TCP.
+ retries_remaining = 5
+ t = nil
+ u = nil
+ begin
+ begin
+ u = UDPSocket.new
+ u.bind(host, 0)
+ _, udp_port, _, _ = u.addr
+ t = TCPServer.new(host, udp_port)
+ t.listen(1)
+ rescue Errno::EADDRINUSE, Errno::EACCES
+ # ADDRINUSE is what should get thrown if we try and bind a port which is already bound on UNIXen,
+ # but windows can sometimes throw EACCESS.
+ # See: https://stackoverflow.com/questions/48478869/cannot-bind-to-some-ports-due-to-permission-denied
+ retries_remaining -= 1
+ if retries_remaining > 0
+ t&.close
+ t = nil
+ u&.close
+ u = nil
+ retry
+ end
+ raise
+ end
+
+ # If we get to this point, we have a valid t & u socket
+ yield u, t
+ ensure
+ t&.close
+ u&.close
+ end
+ else
+ # Explicitly specified port, don't retry the bind.
+ with_udp(host, port) do |u|
+ with_tcp(host, port) do |t|
+ yield u, t
+ end
+ end
+ end
+ end
+
# [ruby-core:65836]
def test_resolve_with_2_ndots
conf = Resolv::DNS::Config.new :nameserver => ['127.0.0.1'], :ndots => 2
@@ -157,6 +211,166 @@ class TestResolvDNS < Test::Unit::TestCase
}
end
+ def test_query_ipv4_address_truncated_tcp_fallback
+ begin
+ OpenSSL
+ rescue LoadError
+ skip 'autoload problem. see [ruby-dev:45021][Bug #5786]'
+ end if defined?(OpenSSL)
+
+ num_records = 50
+
+ with_udp_and_tcp('127.0.0.1', 0) {|u, t|
+ _, server_port, _, server_address = u.addr
+ client_thread = Thread.new {
+ Resolv::DNS.open(:nameserver_port => [[server_address, server_port]]) {|dns|
+ dns.getresources("foo.example.org", Resolv::DNS::Resource::IN::A)
+ }
+ }
+ udp_server_thread = Thread.new {
+ msg, (_, client_port, _, client_address) = Timeout.timeout(5) {u.recvfrom(4096)}
+ id, word2, qdcount, ancount, nscount, arcount = msg.unpack("nnnnnn")
+ qr = (word2 & 0x8000) >> 15
+ opcode = (word2 & 0x7800) >> 11
+ aa = (word2 & 0x0400) >> 10
+ tc = (word2 & 0x0200) >> 9
+ rd = (word2 & 0x0100) >> 8
+ ra = (word2 & 0x0080) >> 7
+ z = (word2 & 0x0070) >> 4
+ rcode = word2 & 0x000f
+ rest = msg[12..-1]
+ assert_equal(0, qr) # 0:query 1:response
+ assert_equal(0, opcode) # 0:QUERY 1:IQUERY 2:STATUS
+ assert_equal(0, aa) # Authoritative Answer
+ assert_equal(0, tc) # TrunCation
+ assert_equal(1, rd) # Recursion Desired
+ assert_equal(0, ra) # Recursion Available
+ assert_equal(0, z) # Reserved for future use
+ assert_equal(0, rcode) # 0:No-error 1:Format-error 2:Server-failure 3:Name-Error 4:Not-Implemented 5:Refused
+ assert_equal(1, qdcount) # number of entries in the question section.
+ assert_equal(0, ancount) # number of entries in the answer section.
+ assert_equal(0, nscount) # number of entries in the authority records section.
+ assert_equal(0, arcount) # number of entries in the additional records section.
+ name = [3, "foo", 7, "example", 3, "org", 0].pack("Ca*Ca*Ca*C")
+ assert_operator(rest, :start_with?, name)
+ rest = rest[name.length..-1]
+ assert_equal(4, rest.length)
+ qtype, _ = rest.unpack("nn")
+ assert_equal(1, qtype) # A
+ assert_equal(1, qtype) # IN
+ id = id
+ qr = 1
+ opcode = opcode
+ aa = 0
+ tc = 1
+ rd = rd
+ ra = 1
+ z = 0
+ rcode = 0
+ qdcount = 0
+ ancount = num_records
+ nscount = 0
+ arcount = 0
+ word2 = (qr << 15) |
+ (opcode << 11) |
+ (aa << 10) |
+ (tc << 9) |
+ (rd << 8) |
+ (ra << 7) |
+ (z << 4) |
+ rcode
+ msg = [id, word2, qdcount, ancount, nscount, arcount].pack("nnnnnn")
+ type = 1
+ klass = 1
+ ttl = 3600
+ rdlength = 4
+ num_records.times do |i|
+ rdata = [192,0,2,i].pack("CCCC") # 192.0.2.x (TEST-NET address) RFC 3330
+ rr = [name, type, klass, ttl, rdlength, rdata].pack("a*nnNna*")
+ msg << rr
+ end
+ u.send(msg[0...512], 0, client_address, client_port)
+ }
+ tcp_server_thread = Thread.new {
+ ct = t.accept
+ msg = ct.recv(512)
+ msg.slice!(0..1) # Size (only for TCP)
+ id, word2, qdcount, ancount, nscount, arcount = msg.unpack("nnnnnn")
+ qr = (word2 & 0x8000) >> 15
+ opcode = (word2 & 0x7800) >> 11
+ aa = (word2 & 0x0400) >> 10
+ tc = (word2 & 0x0200) >> 9
+ rd = (word2 & 0x0100) >> 8
+ ra = (word2 & 0x0080) >> 7
+ z = (word2 & 0x0070) >> 4
+ rcode = word2 & 0x000f
+ rest = msg[12..-1]
+ assert_equal(0, qr) # 0:query 1:response
+ assert_equal(0, opcode) # 0:QUERY 1:IQUERY 2:STATUS
+ assert_equal(0, aa) # Authoritative Answer
+ assert_equal(0, tc) # TrunCation
+ assert_equal(1, rd) # Recursion Desired
+ assert_equal(0, ra) # Recursion Available
+ assert_equal(0, z) # Reserved for future use
+ assert_equal(0, rcode) # 0:No-error 1:Format-error 2:Server-failure 3:Name-Error 4:Not-Implemented 5:Refused
+ assert_equal(1, qdcount) # number of entries in the question section.
+ assert_equal(0, ancount) # number of entries in the answer section.
+ assert_equal(0, nscount) # number of entries in the authority records section.
+ assert_equal(0, arcount) # number of entries in the additional records section.
+ name = [3, "foo", 7, "example", 3, "org", 0].pack("Ca*Ca*Ca*C")
+ assert_operator(rest, :start_with?, name)
+ rest = rest[name.length..-1]
+ assert_equal(4, rest.length)
+ qtype, _ = rest.unpack("nn")
+ assert_equal(1, qtype) # A
+ assert_equal(1, qtype) # IN
+ id = id
+ qr = 1
+ opcode = opcode
+ aa = 0
+ tc = 0
+ rd = rd
+ ra = 1
+ z = 0
+ rcode = 0
+ qdcount = 0
+ ancount = num_records
+ nscount = 0
+ arcount = 0
+ word2 = (qr << 15) |
+ (opcode << 11) |
+ (aa << 10) |
+ (tc << 9) |
+ (rd << 8) |
+ (ra << 7) |
+ (z << 4) |
+ rcode
+ msg = [id, word2, qdcount, ancount, nscount, arcount].pack("nnnnnn")
+ type = 1
+ klass = 1
+ ttl = 3600
+ rdlength = 4
+ num_records.times do |i|
+ rdata = [192,0,2,i].pack("CCCC") # 192.0.2.x (TEST-NET address) RFC 3330
+ rr = [name, type, klass, ttl, rdlength, rdata].pack("a*nnNna*")
+ msg << rr
+ end
+ msg = "#{[msg.bytesize].pack("n")}#{msg}" # Prefix with size
+ ct.send(msg, 0)
+ ct.close
+ }
+ result, _ = assert_join_threads([client_thread, udp_server_thread, tcp_server_thread])
+ assert_instance_of(Array, result)
+ assert_equal(50, result.length)
+ result.each_with_index do |rr, i|
+ assert_instance_of(Resolv::DNS::Resource::IN::A, rr)
+ assert_instance_of(Resolv::IPv4, rr.address)
+ assert_equal("192.0.2.#{i}", rr.address.to_s)
+ assert_equal(3600, rr.ttl)
+ end
+ }
+ end
+
def test_query_ipv4_duplicate_responses
begin
OpenSSL
@@ -374,7 +588,8 @@ class TestResolvDNS < Test::Unit::TestCase
["2001:db8::1", "2001:db8::0:1"],
["::", "0:0:0:0:0:0:0:0"],
["2001::", "2001::0"],
- ["2001:db8::1:1:1:1:1", "2001:db8:0:1:1:1:1:1"],
+ ["2001:db8:0:1:1:1:1:1", "2001:db8:0:1:1:1:1:1"], # RFC 5952 Section 4.2.2.
+ ["2001:db8::1:1:1:1", "2001:db8:0:0:1:1:1:1"],
["1::1:0:0:0:1", "1:0:0:1:0:0:0:1"],
["1::1:0:0:1", "1:0:0:0:1:0:0:1"],
]
@@ -457,4 +672,30 @@ class TestResolvDNS < Test::Unit::TestCase
end
assert_raise(Resolv::ResolvError) { dns.each_name('example.com') }
end
+
+ def test_unreachable_server
+ unreachable_ip = '127.0.0.1'
+ sock = UDPSocket.new
+ sock.connect(unreachable_ip, 53)
+ begin
+ sock.send('1', 0)
+ rescue Errno::ENETUNREACH, Errno::EHOSTUNREACH
+ else
+ omit('cannot test unreachable server, as IP used is reachable')
+ end
+
+ config = {
+ :nameserver => [unreachable_ip],
+ :search => ['lan'],
+ :ndots => 1
+ }
+ r = Resolv.new([Resolv::DNS.new(config)])
+ assert_equal([], r.getaddresses('www.google.com'))
+
+ config[:raise_timeout_errors] = true
+ r = Resolv.new([Resolv::DNS.new(config)])
+ assert_raise(Resolv::ResolvError) { r.getaddresses('www.google.com') }
+ ensure
+ sock&.close
+ end
end
diff --git a/test/resolv/test_resource.rb b/test/resolv/test_resource.rb
index b688155dc2..434380236e 100644
--- a/test/resolv/test_resource.rb
+++ b/test/resolv/test_resource.rb
@@ -32,3 +32,71 @@ class TestResolvResource < Test::Unit::TestCase
assert_equal "\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x07example\x03com\x00\x00\x21\x00\x01\x00\x00\x00\x00\x00\x17\x00\x00\x00\x00\x00\x00\x03www\x07example\x03com\x00", m.encode, issue29
end
end
+
+class TestResolvResourceCAA < Test::Unit::TestCase
+ def test_caa_roundtrip
+ raw_msg = "\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x03new\x07example\x03com\x00\x01\x01\x00\x01\x00\x00\x00\x00\x00\x16\x00\x05issueca1.example.net\xC0\x0C\x01\x01\x00\x01\x00\x00\x00\x00\x00\x0C\x80\x03tbsUnknown".b
+
+ m = Resolv::DNS::Message.new(0)
+ m.add_answer('new.example.com', 0, Resolv::DNS::Resource::IN::CAA.new(0, 'issue', 'ca1.example.net'))
+ m.add_answer('new.example.com', 0, Resolv::DNS::Resource::IN::CAA.new(128, 'tbs', 'Unknown'))
+ assert_equal raw_msg, m.encode
+
+ m = Resolv::DNS::Message.decode(raw_msg)
+ assert_equal 2, m.answer.size
+ _, _, caa0 = m.answer[0]
+ assert_equal 0, caa0.flags
+ assert_equal false, caa0.critical?
+ assert_equal 'issue', caa0.tag
+ assert_equal 'ca1.example.net', caa0.value
+ _, _, caa1 = m.answer[1]
+ assert_equal true, caa1.critical?
+ assert_equal 128, caa1.flags
+ assert_equal 'tbs', caa1.tag
+ assert_equal 'Unknown', caa1.value
+ end
+
+ def test_caa_stackoverflow
+ # gathered in the wild
+ raw_msg = "\x8D\x32\x81\x80\x00\x01\x00\x0B\x00\x00\x00\x00\x0Dstackoverflow\x03com\x00\x01\x01\x00\x01\xC0\x0C\x01\x01\x00\x01\x00\x00\x01\x2C\x00\x13\x00\x05issuecomodoca.com\xC0\x0C\x01\x01\x00\x01\x00\x00\x01\x2C\x00\x2D\x00\x05issuedigicert.com; cansignhttpexchanges=yes\xC0\x0C\x01\x01\x00\x01\x00\x00\x01\x2C\x00\x16\x00\x05issueletsencrypt.org\xC0\x0C\x01\x01\x00\x01\x00\x00\x01\x2C\x00\x29\x00\x05issuepki.goog; cansignhttpexchanges=yes\xC0\x0C\x01\x01\x00\x01\x00\x00\x01\x2C\x00\x12\x00\x05issuesectigo.com\xC0\x0C\x01\x01\x00\x01\x00\x00\x01\x2C\x00\x17\x00\x09issuewildcomodoca.com\xC0\x0C\x01\x01\x00\x01\x00\x00\x01\x2C\x00\x31\x00\x09issuewilddigicert.com; cansignhttpexchanges=yes\xC0\x0C\x01\x01\x00\x01\x00\x00\x01\x2C\x00\x1A\x00\x09issuewildletsencrypt.org\xC0\x0C\x01\x01\x00\x01\x00\x00\x01\x2C\x00\x2D\x00\x09issuewildpki.goog; cansignhttpexchanges=yes\xC0\x0C\x01\x01\x00\x01\x00\x00\x01\x2C\x00\x16\x00\x09issuewildsectigo.com\xC0\x0C\x01\x01\x00\x01\x00\x00\x01\x2C\x00\x2D\x80\x05iodefmailto:sysadmin-team@stackoverflow.com".b
+
+ m = Resolv::DNS::Message.decode(raw_msg)
+ assert_equal 11, m.answer.size
+ _, _, caa3 = m.answer[3]
+ assert_equal 0, caa3.flags
+ assert_equal 'issue', caa3.tag
+ assert_equal 'pki.goog; cansignhttpexchanges=yes', caa3.value
+ _, _, caa8 = m.answer[8]
+ assert_equal 0, caa8.flags
+ assert_equal 'issuewild', caa8.tag
+ assert_equal 'pki.goog; cansignhttpexchanges=yes', caa8.value
+ _, _, caa10 = m.answer[10]
+ assert_equal 128, caa10.flags
+ assert_equal 'iodef', caa10.tag
+ assert_equal 'mailto:sysadmin-team@stackoverflow.com', caa10.value
+ end
+
+ def test_caa_flags
+ assert_equal 255,
+ Resolv::DNS::Resource::IN::CAA.new(255, 'issue', 'ca1.example.net').flags
+ assert_raise(ArgumentError) do
+ Resolv::DNS::Resource::IN::CAA.new(256, 'issue', 'ca1.example.net')
+ end
+
+ assert_raise(ArgumentError) do
+ Resolv::DNS::Resource::IN::CAA.new(-1, 'issue', 'ca1.example.net')
+ end
+ end
+
+ def test_caa_tag
+ assert_raise(ArgumentError, 'Empty tag should be rejected') do
+ Resolv::DNS::Resource::IN::CAA.new(0, '', 'ca1.example.net')
+ end
+
+ assert_equal '123456789012345',
+ Resolv::DNS::Resource::IN::CAA.new(0, '123456789012345', 'ca1.example.net').tag
+ assert_raise(ArgumentError, 'Tag longer than 15 bytes should be rejected') do
+ Resolv::DNS::Resource::IN::CAA.new(0, '1234567890123456', 'ca1.example.net')
+ end
+ end
+end
diff --git a/test/resolv/test_svcb_https.rb b/test/resolv/test_svcb_https.rb
new file mode 100644
index 0000000000..5dc3163d9e
--- /dev/null
+++ b/test/resolv/test_svcb_https.rb
@@ -0,0 +1,231 @@
+# frozen_string_literal: false
+require 'test/unit'
+require 'resolv'
+
+class TestResolvSvcbHttps < Test::Unit::TestCase
+ # Wraps a RR in answer section
+ def wrap_rdata(rrtype, rrclass, rdata)
+ [
+ "\x00\x00\x00\x00", # ID/FLAGS
+ [0, 1, 0, 0].pack('nnnn'), # QDCOUNT/ANCOUNT/NSCOUNT/ARCOUNT
+ "\x07example\x03com\x00", # NAME
+ [rrtype, rrclass, 0, rdata.bytesize].pack('nnNn'), # TYPE/CLASS/TTL/RDLENGTH
+ rdata,
+ ].join.b
+ end
+
+ def test_svcparams
+ params = Resolv::DNS::SvcParams.new([Resolv::DNS::SvcParam::Mandatory.new([1])])
+
+ assert_equal 1, params.count
+
+ params.add Resolv::DNS::SvcParam::NoDefaultALPN.new
+ params.add Resolv::DNS::SvcParam::ALPN.new(%w[h2 h3])
+
+ assert_equal 3, params.count
+
+ assert_equal [1], params[:mandatory].keys
+ assert_equal [1], params[0].keys
+
+ assert_equal %w[h2 h3], params[:alpn].protocol_ids
+ assert_equal %w[h2 h3], params[1].protocol_ids
+
+ params.delete :mandatory
+ params.delete :alpn
+
+ assert_equal 1, params.count
+
+ assert_nil params[:mandatory]
+ assert_nil params[1]
+
+ ary = params.each.to_a
+
+ assert_instance_of Resolv::DNS::SvcParam::NoDefaultALPN, ary.first
+ end
+
+ def test_svcb
+ rr = Resolv::DNS::Resource::IN::SVCB.new(0, 'example.com.')
+
+ assert_equal 0, rr.priority
+ assert rr.alias_mode?
+ assert !rr.service_mode?
+ assert_equal Resolv::DNS::Name.create('example.com.'), rr.target
+ assert rr.params.empty?
+
+ rr = Resolv::DNS::Resource::IN::SVCB.new(16, 'example.com.', [
+ Resolv::DNS::SvcParam::ALPN.new(%w[h2 h3]),
+ ])
+
+ assert_equal 16, rr.priority
+ assert !rr.alias_mode?
+ assert rr.service_mode?
+
+ assert_equal 1, rr.params.count
+ assert_instance_of Resolv::DNS::SvcParam::ALPN, rr.params[:alpn]
+ end
+
+ def test_svcb_encode_order
+ msg = Resolv::DNS::Message.new(0)
+ msg.add_answer(
+ 'example.com.', 0,
+ Resolv::DNS::Resource::IN::SVCB.new(16, 'foo.example.org.', [
+ Resolv::DNS::SvcParam::ALPN.new(%w[h2 h3-19]),
+ Resolv::DNS::SvcParam::Mandatory.new([4, 1]),
+ Resolv::DNS::SvcParam::IPv4Hint.new(['192.0.2.1']),
+ ])
+ )
+
+ expected = wrap_rdata 64, 1, "\x00\x10\x03foo\x07example\x03org\x00" +
+ "\x00\x00\x00\x04\x00\x01\x00\x04" +
+ "\x00\x01\x00\x09\x02h2\x05h3-19" +
+ "\x00\x04\x00\x04\xc0\x00\x02\x01"
+
+ assert_equal expected, msg.encode
+ end
+
+ ## Test vectors from [RFC9460]
+
+ def test_alias_mode
+ wire = wrap_rdata 65, 1, "\x00\x00\x03foo\x07example\x03com\x00"
+ msg = Resolv::DNS::Message.decode(wire)
+ _, _, rr = msg.answer.first
+
+ assert_equal 0, rr.priority
+ assert_equal Resolv::DNS::Name.create('foo.example.com.'), rr.target
+ assert_equal 0, rr.params.count
+
+ assert_equal wire, msg.encode
+ end
+
+ def test_target_name_is_root
+ wire = wrap_rdata 64, 1, "\x00\x01\x00"
+ msg = Resolv::DNS::Message.decode(wire)
+ _, _, rr = msg.answer.first
+
+ assert_equal 1, rr.priority
+ assert_equal Resolv::DNS::Name.create('.'), rr.target
+ assert_equal 0, rr.params.count
+
+ assert_equal wire, msg.encode
+ end
+
+ def test_specifies_port
+ wire = wrap_rdata 64, 1, "\x00\x10\x03foo\x07example\x03com\x00" +
+ "\x00\x03\x00\x02\x00\x35"
+ msg = Resolv::DNS::Message.decode(wire)
+ _, _, rr = msg.answer.first
+
+ assert_equal 16, rr.priority
+ assert_equal Resolv::DNS::Name.create('foo.example.com.'), rr.target
+ assert_equal 1, rr.params.count
+ assert_equal 53, rr.params[:port].port
+
+ assert_equal wire, msg.encode
+ end
+
+ def test_generic_key
+ wire = wrap_rdata 64, 1, "\x00\x01\x03foo\x07example\x03com\x00" +
+ "\x02\x9b\x00\x05hello"
+ msg = Resolv::DNS::Message.decode(wire)
+ _, _, rr = msg.answer.first
+
+ assert_equal 1, rr.priority
+ assert_equal Resolv::DNS::Name.create('foo.example.com.'), rr.target
+ assert_equal 1, rr.params.count
+ assert_equal 'hello', rr.params[:key667].value
+
+ assert_equal wire, msg.encode
+ end
+
+ def test_two_ipv6hints
+ wire = wrap_rdata 64, 1, "\x00\x01\x03foo\x07example\x03com\x00" +
+ "\x00\x06\x00\x20" +
+ ("\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" +
+ "\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x53\x00\x01")
+ msg = Resolv::DNS::Message.decode(wire)
+ _, _, rr = msg.answer.first
+
+ assert_equal 1, rr.priority
+ assert_equal Resolv::DNS::Name.create('foo.example.com.'), rr.target
+ assert_equal 1, rr.params.count
+ assert_equal [Resolv::IPv6.create('2001:db8::1'), Resolv::IPv6.create('2001:db8::53:1')],
+ rr.params[:ipv6hint].addresses
+
+ assert_equal wire, msg.encode
+ end
+
+ def test_ipv6hint_embedded_ipv4
+ wire = wrap_rdata 64, 1, "\x00\x01\x07example\x03com\x00" +
+ "\x00\x06\x00\x10\x20\x01\x0d\xb8\x01\x22\x03\x44\x00\x00\x00\x00\xc0\x00\x02\x21"
+ msg = Resolv::DNS::Message.decode(wire)
+ _, _, rr = msg.answer.first
+
+ assert_equal 1, rr.priority
+ assert_equal Resolv::DNS::Name.create('example.com.'), rr.target
+ assert_equal 1, rr.params.count
+ assert_equal [Resolv::IPv6.create('2001:db8:122:344::192.0.2.33')],
+ rr.params[:ipv6hint].addresses
+
+ assert_equal wire, msg.encode
+ end
+
+ def test_mandatory_alpn_ipv4hint
+ wire = wrap_rdata 64, 1, "\x00\x10\x03foo\x07example\x03org\x00" +
+ "\x00\x00\x00\x04\x00\x01\x00\x04" +
+ "\x00\x01\x00\x09\x02h2\x05h3-19" +
+ "\x00\x04\x00\x04\xc0\x00\x02\x01"
+ msg = Resolv::DNS::Message.decode(wire)
+ _, _, rr = msg.answer.first
+
+ assert_equal 16, rr.priority
+ assert_equal Resolv::DNS::Name.create('foo.example.org.'), rr.target
+ assert_equal 3, rr.params.count
+ assert_equal [1, 4], rr.params[:mandatory].keys
+ assert_equal ['h2', 'h3-19'], rr.params[:alpn].protocol_ids
+ assert_equal [Resolv::IPv4.create('192.0.2.1')], rr.params[:ipv4hint].addresses
+
+ assert_equal wire, msg.encode
+ end
+
+ def test_alpn_comma_backslash
+ wire = wrap_rdata 64, 1, "\x00\x10\x03foo\x07example\x03org\x00" +
+ "\x00\x01\x00\x0c\x08f\\oo,bar\x02h2"
+ msg = Resolv::DNS::Message.decode(wire)
+ _, _, rr = msg.answer.first
+
+ assert_equal 16, rr.priority
+ assert_equal Resolv::DNS::Name.create('foo.example.org.'), rr.target
+ assert_equal 1, rr.params.count
+ assert_equal ['f\oo,bar', 'h2'], rr.params[:alpn].protocol_ids
+
+ assert_equal wire, msg.encode
+ end
+
+ ## For [RFC9461]
+
+ def test_dohpath
+ wire = wrap_rdata 64, 1, "\x00\x01\x03one\x03one\x03one\x03one\x00" +
+ "\x00\x01\x00\x03\x02h2" +
+ "\x00\x03\x00\x02\x01\xbb" +
+ "\x00\x04\x00\x08\x01\x01\x01\x01\x01\x00\x00\x01" +
+ "\x00\x06\x00\x20" +
+ ("\x26\x06\x47\x00\x47\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11" +
+ "\x26\x06\x47\x00\x47\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x01") +
+ "\x00\x07\x00\x10/dns-query{?dns}"
+ msg = Resolv::DNS::Message.decode(wire)
+ _, _, rr = msg.answer.first
+
+ assert_equal 1, rr.priority
+ assert_equal Resolv::DNS::Name.create('one.one.one.one.'), rr.target
+ assert_equal 5, rr.params.count
+ assert_equal ['h2'], rr.params[:alpn].protocol_ids
+ assert_equal 443, rr.params[:port].port
+ assert_equal [Resolv::IPv4.create('1.1.1.1'), Resolv::IPv4.create('1.0.0.1')],
+ rr.params[:ipv4hint].addresses
+ assert_equal [Resolv::IPv6.create('2606:4700:4700::1111'), Resolv::IPv6.create('2606:4700:4700::1001')],
+ rr.params[:ipv6hint].addresses
+ assert_equal '/dns-query{?dns}', rr.params[:dohpath].template
+
+ assert_equal wire, msg.encode
+ end
+end
diff --git a/test/rinda/test_rinda.rb b/test/rinda/test_rinda.rb
deleted file mode 100644
index 00404e39df..0000000000
--- a/test/rinda/test_rinda.rb
+++ /dev/null
@@ -1,912 +0,0 @@
-# frozen_string_literal: false
-require 'test/unit'
-require 'envutil'
-
-require 'drb/drb'
-require 'drb/eq'
-require 'rinda/ring'
-require 'rinda/tuplespace'
-require 'timeout'
-require 'singleton'
-
-module Rinda
-
-class MockClock
- include Singleton
-
- class MyTS < Rinda::TupleSpace
- def keeper_thread
- nil
- end
-
- def stop_keeper
- if @keeper
- @keeper.kill
- @keeper.join
- @keeper = nil
- end
- end
- end
-
- def initialize
- @now = 2
- @reso = 1
- @ts = nil
- @inf = 2**31 - 1
- end
-
- def start_keeper
- @now = 2
- @reso = 1
- @ts&.stop_keeper
- @ts = MyTS.new
- @ts.write([2, :now])
- @inf = 2**31 - 1
- end
-
- def stop_keeper
- @ts.stop_keeper
- end
-
- def now
- @now.to_f
- end
-
- def at(n)
- n
- end
-
- def _forward(n=nil)
- now ,= @ts.take([nil, :now])
- @now = now + n
- @ts.write([@now, :now])
- end
-
- def forward(n)
- while n > 0
- _forward(@reso)
- n -= @reso
- Thread.pass
- end
- end
-
- def rewind
- @ts.take([nil, :now])
- @ts.write([@inf, :now])
- @ts.take([nil, :now])
- @now = 2
- @ts.write([2, :now])
- end
-
- def sleep(n=nil)
- now ,= @ts.read([nil, :now])
- @ts.read([(now + n)..@inf, :now])
- 0
- end
-end
-
-module Time
- def sleep(n)
- @m.sleep(n)
- end
- module_function :sleep
-
- def at(n)
- n
- end
- module_function :at
-
- def now
- defined?(@m) && @m ? @m.now : 2
- end
- module_function :now
-
- def rewind
- @m.rewind
- end
- module_function :rewind
-
- def forward(n)
- @m.forward(n)
- end
- module_function :forward
-
- @m = MockClock.instance
-end
-
-class TupleSpace
- def sleep(n)
- Kernel.sleep(n * 0.01)
- end
-end
-
-module TupleSpaceTestModule
- def setup
- MockClock.instance.start_keeper
- end
-
- def teardown
- MockClock.instance.stop_keeper
- end
-
- def sleep(n)
- if Thread.current == Thread.main
- Time.forward(n)
- else
- Time.sleep(n)
- end
- end
-
- def thread_join(th)
- while th.alive?
- Kernel.sleep(0.1)
- sleep(1)
- end
- th.value
- end
-
- def test_00_tuple
- tuple = Rinda::TupleEntry.new([1,2,3])
- assert(!tuple.canceled?)
- assert(!tuple.expired?)
- assert(tuple.alive?)
- end
-
- def test_00_template
- tmpl = Rinda::Template.new([1,2,3])
- assert_equal(3, tmpl.size)
- assert_equal(3, tmpl[2])
- assert(tmpl.match([1,2,3]))
- assert(!tmpl.match([1,nil,3]))
-
- tmpl = Rinda::Template.new([/^rinda/i, nil, :hello])
- assert_equal(3, tmpl.size)
- assert(tmpl.match(['Rinda', 2, :hello]))
- assert(!tmpl.match(['Rinda', 2, Symbol]))
- assert(!tmpl.match([1, 2, :hello]))
- assert(tmpl.match([/^rinda/i, 2, :hello]))
-
- tmpl = Rinda::Template.new([Symbol])
- assert_equal(1, tmpl.size)
- assert(tmpl.match([:hello]))
- assert(tmpl.match([Symbol]))
- assert(!tmpl.match(['Symbol']))
-
- tmpl = Rinda::Template.new({"message"=>String, "name"=>String})
- assert_equal(2, tmpl.size)
- assert(tmpl.match({"message"=>"Hello", "name"=>"Foo"}))
- assert(!tmpl.match({"message"=>"Hello", "name"=>"Foo", "1"=>2}))
- assert(!tmpl.match({"message"=>"Hi", "name"=>"Foo", "age"=>1}))
- assert(!tmpl.match({"message"=>"Hello", "no_name"=>"Foo"}))
-
- assert_raise(Rinda::InvalidHashTupleKey) do
- Rinda::Template.new({:message=>String, "name"=>String})
- end
- tmpl = Rinda::Template.new({"name"=>String})
- assert_equal(1, tmpl.size)
- assert(tmpl.match({"name"=>"Foo"}))
- assert(!tmpl.match({"message"=>"Hello", "name"=>"Foo"}))
- assert(!tmpl.match({"message"=>:symbol, "name"=>"Foo", "1"=>2}))
- assert(!tmpl.match({"message"=>"Hi", "name"=>"Foo", "age"=>1}))
- assert(!tmpl.match({"message"=>"Hello", "no_name"=>"Foo"}))
-
- tmpl = Rinda::Template.new({"message"=>String, "name"=>String})
- assert_equal(2, tmpl.size)
- assert(tmpl.match({"message"=>"Hello", "name"=>"Foo"}))
- assert(!tmpl.match({"message"=>"Hello", "name"=>"Foo", "1"=>2}))
- assert(!tmpl.match({"message"=>"Hi", "name"=>"Foo", "age"=>1}))
- assert(!tmpl.match({"message"=>"Hello", "no_name"=>"Foo"}))
-
- tmpl = Rinda::Template.new({"message"=>String})
- assert_equal(1, tmpl.size)
- assert(tmpl.match({"message"=>"Hello"}))
- assert(!tmpl.match({"message"=>"Hello", "name"=>"Foo"}))
- assert(!tmpl.match({"message"=>"Hello", "name"=>"Foo", "1"=>2}))
- assert(!tmpl.match({"message"=>"Hi", "name"=>"Foo", "age"=>1}))
- assert(!tmpl.match({"message"=>"Hello", "no_name"=>"Foo"}))
-
- tmpl = Rinda::Template.new({"message"=>String, "name"=>nil})
- assert_equal(2, tmpl.size)
- assert(tmpl.match({"message"=>"Hello", "name"=>"Foo"}))
- assert(!tmpl.match({"message"=>"Hello", "name"=>"Foo", "1"=>2}))
- assert(!tmpl.match({"message"=>"Hi", "name"=>"Foo", "age"=>1}))
- assert(!tmpl.match({"message"=>"Hello", "no_name"=>"Foo"}))
-
- assert_raise(Rinda::InvalidHashTupleKey) do
- @ts.write({:message=>String, "name"=>String})
- end
-
- @ts.write([1, 2, 3])
- assert_equal([1, 2, 3], @ts.take([1, 2, 3]))
-
- @ts.write({'1'=>1, '2'=>2, '3'=>3})
- assert_equal({'1'=>1, '2'=>2, '3'=>3}, @ts.take({'1'=>1, '2'=>2, '3'=>3}))
-
- entry = @ts.write(['1'=>1, '2'=>2, '3'=>3])
- assert_raise(Rinda::RequestExpiredError) do
- assert_equal({'1'=>1, '2'=>2, '3'=>3}, @ts.read({'1'=>1}, 0))
- end
- entry.cancel
- end
-
- def test_00_DRbObject
- ro = DRbObject.new(nil, "druby://host:1234")
- tmpl = Rinda::DRbObjectTemplate.new
- assert(tmpl === ro)
-
- tmpl = Rinda::DRbObjectTemplate.new("druby://host:1234")
- assert(tmpl === ro)
-
- tmpl = Rinda::DRbObjectTemplate.new("druby://host:12345")
- assert(!(tmpl === ro))
-
- tmpl = Rinda::DRbObjectTemplate.new(/^druby:\/\/host:/)
- assert(tmpl === ro)
-
- ro = DRbObject.new_with(12345, 1234)
- assert(!(tmpl === ro))
-
- ro = DRbObject.new_with("druby://foo:12345", 1234)
- assert(!(tmpl === ro))
-
- tmpl = Rinda::DRbObjectTemplate.new(/^druby:\/\/(foo|bar):/)
- assert(tmpl === ro)
-
- ro = DRbObject.new_with("druby://bar:12345", 1234)
- assert(tmpl === ro)
-
- ro = DRbObject.new_with("druby://baz:12345", 1234)
- assert(!(tmpl === ro))
- end
-
- def test_inp_rdp
- assert_raise(Rinda::RequestExpiredError) do
- @ts.take([:empty], 0)
- end
-
- assert_raise(Rinda::RequestExpiredError) do
- @ts.read([:empty], 0)
- end
- end
-
- def test_ruby_talk_264062
- th = Thread.new {
- assert_raise(Rinda::RequestExpiredError) do
- @ts.take([:empty], 1)
- end
- }
- sleep(10)
- thread_join(th)
-
- th = Thread.new {
- assert_raise(Rinda::RequestExpiredError) do
- @ts.read([:empty], 1)
- end
- }
- sleep(10)
- thread_join(th)
- end
-
- def test_symbol_tuple
- @ts.write([:symbol, :symbol])
- @ts.write(['string', :string])
- assert_equal([[:symbol, :symbol]], @ts.read_all([:symbol, nil]))
- assert_equal([[:symbol, :symbol]], @ts.read_all([Symbol, nil]))
- assert_equal([], @ts.read_all([:nil, nil]))
- end
-
- def test_core_01
- 5.times do
- @ts.write([:req, 2])
- end
-
- assert_equal([[:req, 2], [:req, 2], [:req, 2], [:req, 2], [:req, 2]],
- @ts.read_all([nil, nil]))
-
- taker = Thread.new(5) do |count|
- s = 0
- count.times do
- tuple = @ts.take([:req, Integer])
- assert_equal(2, tuple[1])
- s += tuple[1]
- end
- @ts.write([:ans, s])
- s
- end
-
- assert_equal(10, thread_join(taker))
- assert_equal([:ans, 10], @ts.take([:ans, 10]))
- assert_equal([], @ts.read_all([nil, nil]))
- end
-
- def test_core_02
- taker = Thread.new(5) do |count|
- s = 0
- count.times do
- tuple = @ts.take([:req, Integer])
- assert_equal(2, tuple[1])
- s += tuple[1]
- end
- @ts.write([:ans, s])
- s
- end
-
- 5.times do
- @ts.write([:req, 2])
- end
-
- assert_equal(10, thread_join(taker))
- assert_equal([:ans, 10], @ts.take([:ans, 10]))
- assert_equal([], @ts.read_all([nil, nil]))
- end
-
- def test_core_03_notify
- notify1 = @ts.notify(nil, [:req, Integer])
- notify2 = @ts.notify(nil, {"message"=>String, "name"=>String})
-
- 5.times do
- @ts.write([:req, 2])
- end
-
- 5.times do
- tuple = @ts.take([:req, Integer])
- assert_equal(2, tuple[1])
- end
-
- 5.times do
- assert_equal(['write', [:req, 2]], notify1.pop)
- end
- 5.times do
- assert_equal(['take', [:req, 2]], notify1.pop)
- end
-
- @ts.write({"message"=>"first", "name"=>"3"})
- @ts.write({"message"=>"second", "name"=>"1"})
- @ts.write({"message"=>"third", "name"=>"0"})
- @ts.take({"message"=>"third", "name"=>"0"})
- @ts.take({"message"=>"first", "name"=>"3"})
-
- assert_equal(["write", {"message"=>"first", "name"=>"3"}], notify2.pop)
- assert_equal(["write", {"message"=>"second", "name"=>"1"}], notify2.pop)
- assert_equal(["write", {"message"=>"third", "name"=>"0"}], notify2.pop)
- assert_equal(["take", {"message"=>"third", "name"=>"0"}], notify2.pop)
- assert_equal(["take", {"message"=>"first", "name"=>"3"}], notify2.pop)
- end
-
- def test_cancel_01
- entry = @ts.write([:removeme, 1])
- assert_equal([[:removeme, 1]], @ts.read_all([nil, nil]))
- entry.cancel
- assert_equal([], @ts.read_all([nil, nil]))
-
- template = nil
- taker = Thread.new do
- assert_raise(Rinda::RequestCanceledError) do
- @ts.take([:take, nil], 10) do |t|
- template = t
- Thread.new do
- template.cancel
- end
- end
- end
- end
-
- sleep(2)
- thread_join(taker)
-
- assert(template.canceled?)
-
- @ts.write([:take, 1])
-
- assert_equal([[:take, 1]], @ts.read_all([nil, nil]))
- end
-
- def test_cancel_02
- omit 'this test is unstable with --jit-wait' if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?
- entry = @ts.write([:removeme, 1])
- assert_equal([[:removeme, 1]], @ts.read_all([nil, nil]))
- entry.cancel
- assert_equal([], @ts.read_all([nil, nil]))
-
- template = nil
- reader = Thread.new do
- assert_raise(Rinda::RequestCanceledError) do
- @ts.read([:take, nil], 10) do |t|
- template = t
- Thread.new do
- template.cancel
- end
- end
- end
- end
-
- sleep(2)
- thread_join(reader)
-
- assert(template.canceled?)
-
- @ts.write([:take, 1])
-
- assert_equal([[:take, 1]], @ts.read_all([nil, nil]))
- end
-
- class SimpleRenewer
- def initialize(sec, n = 1)
- @sec = sec
- @n = n
- end
-
- def renew
- return -1 if @n <= 0
- @n -= 1
- return @sec
- end
- end
-
- def test_00_renewer
- tuple = Rinda::TupleEntry.new([1,2,3], true)
- assert(!tuple.canceled?)
- assert(tuple.expired?)
- assert(!tuple.alive?)
-
- tuple = Rinda::TupleEntry.new([1,2,3], 1)
- assert(!tuple.canceled?)
- assert(!tuple.expired?)
- assert(tuple.alive?)
- sleep(2)
- assert(tuple.expired?)
- assert(!tuple.alive?)
-
- @renewer = SimpleRenewer.new(1,2)
- tuple = Rinda::TupleEntry.new([1,2,3], @renewer)
- assert(!tuple.canceled?)
- assert(!tuple.expired?)
- assert(tuple.alive?)
- sleep(1)
- assert(!tuple.canceled?)
- assert(!tuple.expired?)
- assert(tuple.alive?)
- sleep(2)
- assert(tuple.expired?)
- assert(!tuple.alive?)
- end
-end
-
-class TupleSpaceTest < Test::Unit::TestCase
- include TupleSpaceTestModule
-
- def setup
- super
- ThreadGroup.new.add(Thread.current)
- @ts = Rinda::TupleSpace.new(1)
- end
- def teardown
- # implementation-dependent
- @ts.instance_eval{
- if th = @keeper
- th.kill
- th.join
- end
- }
- super
- end
-end
-
-class TupleSpaceProxyTest < Test::Unit::TestCase
- include TupleSpaceTestModule
-
- def setup
- if RUBY_PLATFORM.match?(/mingw/)
- @omitted = true
- omit 'This test seems to randomly hang on GitHub Actions MinGW'
- end
- super
- ThreadGroup.new.add(Thread.current)
- @ts_base = Rinda::TupleSpace.new(1)
- @ts = Rinda::TupleSpaceProxy.new(@ts_base)
- @server = DRb.start_service("druby://localhost:0")
- end
- def teardown
- return if @omitted
- @omitted = false
-
- # implementation-dependent
- @ts_base.instance_eval{
- if th = @keeper
- th.kill
- th.join
- end
- }
- @server.stop_service
- DRb::DRbConn.stop_pool
- super
- end
-
- def test_remote_array_and_hash
- # Don't remove ary/hsh local variables.
- # These are necessary to protect objects from GC.
- ary = [1, 2, 3]
- @ts.write(DRbObject.new(ary))
- assert_equal([1, 2, 3], @ts.take([1, 2, 3], 0))
- hsh = {'head' => 1, 'tail' => 2}
- @ts.write(DRbObject.new(hsh))
- assert_equal({'head' => 1, 'tail' => 2},
- @ts.take({'head' => 1, 'tail' => 2}, 0))
- end
-
- def test_take_bug_8215
- omit "this test randomly fails on mswin" if /mswin/ =~ RUBY_PLATFORM
- service = DRb.start_service("druby://localhost:0", @ts_base)
-
- uri = service.uri
-
- args = [EnvUtil.rubybin, *%W[-rdrb/drb -rdrb/eq -rrinda/ring -rrinda/tuplespace -e]]
-
- take = spawn(*args, <<-'end;', uri)
- uri = ARGV[0]
- DRb.start_service("druby://localhost:0")
- ro = DRbObject.new_with_uri(uri)
- ts = Rinda::TupleSpaceProxy.new(ro)
- th = Thread.new do
- ts.take([:test_take, nil])
- rescue Interrupt
- # Expected
- end
- Kernel.sleep(0.1)
- th.raise(Interrupt) # causes loss of the taken tuple
- ts.write([:barrier, :continue])
- Kernel.sleep
- end;
-
- @ts_base.take([:barrier, :continue])
-
- write = spawn(*args, <<-'end;', uri)
- uri = ARGV[0]
- DRb.start_service("druby://localhost:0")
- ro = DRbObject.new_with_uri(uri)
- ts = Rinda::TupleSpaceProxy.new(ro)
- ts.write([:test_take, 42])
- end;
-
- status = Process.wait(write)
-
- assert_equal([[:test_take, 42]], @ts_base.read_all([:test_take, nil]),
- '[bug:8215] tuple lost')
- ensure
- service.stop_service if service
- DRb::DRbConn.stop_pool
- signal = /mswin|mingw/ =~ RUBY_PLATFORM ? "KILL" : "TERM"
- Process.kill(signal, write) if write && status.nil?
- Process.kill(signal, take) if take
- Process.wait(write) if write && status.nil?
- Process.wait(take) if take
- end
-end
-
-module RingIPv4
- def ipv4_mc(rf)
- begin
- v4mc = rf.make_socket('239.0.0.1')
- rescue Errno::ENETUNREACH, Errno::ENOBUFS, Errno::ENODEV
- omit 'IPv4 multicast not available'
- end
-
- begin
- yield v4mc
- ensure
- v4mc.close
- end
- end
-end
-
-module RingIPv6
- def prepare_ipv6(r)
- begin
- Socket.getifaddrs.each do |ifaddr|
- next unless ifaddr.addr
- next unless ifaddr.addr.ipv6_linklocal?
- next if ifaddr.name[0, 2] == "lo"
- r.multicast_interface = ifaddr.ifindex
- return ifaddr
- end
- rescue NotImplementedError
- # ifindex() function may not be implemented on Windows.
- return if
- Socket.ip_address_list.any? { |addrinfo| addrinfo.ipv6? && !addrinfo.ipv6_loopback? }
- end
- omit 'IPv6 not available'
- end
-
- def ipv6_mc(rf, hops = nil)
- ifaddr = prepare_ipv6(rf)
- rf.multicast_hops = hops if hops
- begin
- v6mc = rf.make_socket("ff02::1")
- rescue Errno::EINVAL
- # somehow Debian 6.0.7 needs ifname
- v6mc = rf.make_socket("ff02::1%#{ifaddr.name}")
- rescue Errno::EADDRNOTAVAIL
- return # IPv6 address for multicast not available
- rescue Errno::ENETDOWN
- return # Network is down
- rescue Errno::EHOSTUNREACH
- return # Unreachable for some reason
- end
- begin
- yield v6mc
- ensure
- v6mc.close
- end
- end
-end
-
-class TestRingServer < Test::Unit::TestCase
- include RingIPv4
-
- def setup
- @port = Rinda::Ring_PORT
-
- @ts = Rinda::TupleSpace.new
- @rs = Rinda::RingServer.new(@ts, [], @port)
- @server = DRb.start_service("druby://localhost:0")
- end
- def teardown
- @rs.shutdown
- # implementation-dependent
- @ts.instance_eval{
- if th = @keeper
- th.kill
- th.join
- end
- }
- @server.stop_service
- DRb::DRbConn.stop_pool
- end
-
- def test_do_reply
- with_timeout(30) {_test_do_reply}
- end
-
- def _test_do_reply
- called = nil
-
- callback_orig = proc { |ts|
- called = ts
- }
-
- callback = DRb::DRbObject.new callback_orig
-
- @ts.write [:lookup_ring, callback]
-
- @rs.do_reply
-
- wait_for(30) {called}
-
- assert_same @ts, called
- end
-
- def test_do_reply_local
- omit 'timeout-based test becomes unstable with --jit-wait' if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?
- with_timeout(30) {_test_do_reply_local}
- end
-
- def _test_do_reply_local
- called = nil
-
- callback = proc { |ts|
- called = ts
- }
-
- @ts.write [:lookup_ring, callback]
-
- @rs.do_reply
-
- wait_for(30) {called}
-
- assert_same @ts, called
- end
-
- def test_make_socket_unicast
- v4 = @rs.make_socket('127.0.0.1')
-
- assert_equal('127.0.0.1', v4.local_address.ip_address)
- assert_equal(@port, v4.local_address.ip_port)
- end
-
- def test_make_socket_ipv4_multicast
- ipv4_mc(@rs) do |v4mc|
- begin
- if Socket.const_defined?(:SO_REUSEPORT) then
- assert(v4mc.getsockopt(:SOCKET, :SO_REUSEPORT).bool)
- else
- assert(v4mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool)
- end
- rescue TypeError
- if /aix/ =~ RUBY_PLATFORM
- omit "Known bug in getsockopt(2) on AIX"
- end
- raise $!
- end
-
- assert_equal('0.0.0.0', v4mc.local_address.ip_address)
- assert_equal(@port, v4mc.local_address.ip_port)
- end
- end
-
- def test_make_socket_ipv6_multicast
- omit 'IPv6 not available' unless
- Socket.ip_address_list.any? { |addrinfo| addrinfo.ipv6? && !addrinfo.ipv6_loopback? }
-
- begin
- v6mc = @rs.make_socket('ff02::1')
- rescue Errno::EADDRNOTAVAIL
- return # IPv6 address for multicast not available
- rescue Errno::ENOBUFS => e
- omit "Missing multicast support in OS: #{e.message}"
- end
-
- if Socket.const_defined?(:SO_REUSEPORT) then
- assert v6mc.getsockopt(:SOCKET, :SO_REUSEPORT).bool
- else
- assert v6mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool
- end
-
- assert_equal('::1', v6mc.local_address.ip_address)
- assert_equal(@port, v6mc.local_address.ip_port)
- end
-
- def test_ring_server_ipv4_multicast
- @rs.shutdown
- begin
- @rs = Rinda::RingServer.new(@ts, [['239.0.0.1', '0.0.0.0']], @port)
- rescue Errno::ENOBUFS, Errno::ENODEV => e
- omit "Missing multicast support in OS: #{e.message}"
- end
-
- v4mc = @rs.instance_variable_get('@sockets').first
-
- begin
- if Socket.const_defined?(:SO_REUSEPORT) then
- assert(v4mc.getsockopt(:SOCKET, :SO_REUSEPORT).bool)
- else
- assert(v4mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool)
- end
- rescue TypeError
- if /aix/ =~ RUBY_PLATFORM
- omit "Known bug in getsockopt(2) on AIX"
- end
- raise $!
- end
-
- assert_equal('0.0.0.0', v4mc.local_address.ip_address)
- assert_equal(@port, v4mc.local_address.ip_port)
- end
-
- def test_ring_server_ipv6_multicast
- omit 'IPv6 not available' unless
- Socket.ip_address_list.any? { |addrinfo| addrinfo.ipv6? && !addrinfo.ipv6_loopback? }
-
- @rs.shutdown
- begin
- @rs = Rinda::RingServer.new(@ts, [['ff02::1', '::1', 0]], @port)
- rescue Errno::EADDRNOTAVAIL
- return # IPv6 address for multicast not available
- end
-
- v6mc = @rs.instance_variable_get('@sockets').first
-
- if Socket.const_defined?(:SO_REUSEPORT) then
- assert v6mc.getsockopt(:SOCKET, :SO_REUSEPORT).bool
- else
- assert v6mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool
- end
-
- assert_equal('::1', v6mc.local_address.ip_address)
- assert_equal(@port, v6mc.local_address.ip_port)
- end
-
- def test_shutdown
- @rs.shutdown
-
- assert_nil(@rs.do_reply, 'otherwise should hang forever')
- end
-
- private
-
- def with_timeout(n)
- aoe = Thread.abort_on_exception
- Thread.abort_on_exception = true
- tl0 = Thread.list
- tl = nil
- th = Thread.new(Thread.current) do |mth|
- sleep n
- (tl = Thread.list - tl0).each {|t|t.raise(Timeout::Error)}
- mth.raise(Timeout::Error)
- end
- tl0 << th
- yield
- rescue Timeout::Error => e
- $stderr.puts "TestRingServer#with_timeout: timeout in #{n}s:"
- $stderr.puts caller
- if tl
- bt = e.backtrace
- tl.each do |t|
- begin
- t.value
- rescue Timeout::Error => e
- bt.unshift("")
- bt[0, 0] = e.backtrace
- end
- end
- end
- raise Timeout::Error, "timeout", bt
- ensure
- if th
- th.kill
- th.join
- end
- Thread.abort_on_exception = aoe
- end
-
- def wait_for(n)
- t = n + Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)
- until yield
- if t < Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)
- flunk "timeout during waiting call"
- end
- sleep 0.1
- end
- end
-end
-
-class TestRingFinger < Test::Unit::TestCase
- include RingIPv6
- include RingIPv4
-
- def setup
- @rf = Rinda::RingFinger.new
- end
-
- def test_make_socket_unicast
- v4 = @rf.make_socket('127.0.0.1')
-
- assert(v4.getsockopt(:SOL_SOCKET, :SO_BROADCAST).bool)
- rescue TypeError
- if /aix/ =~ RUBY_PLATFORM
- omit "Known bug in getsockopt(2) on AIX"
- end
- raise $!
- ensure
- v4.close if v4
- end
-
- def test_make_socket_ipv4_multicast
- ipv4_mc(@rf) do |v4mc|
- assert_equal(1, v4mc.getsockopt(:IPPROTO_IP, :IP_MULTICAST_LOOP).ipv4_multicast_loop)
- assert_equal(1, v4mc.getsockopt(:IPPROTO_IP, :IP_MULTICAST_TTL).ipv4_multicast_ttl)
- end
- end
-
- def test_make_socket_ipv6_multicast
- ipv6_mc(@rf) do |v6mc|
- assert_equal(1, v6mc.getsockopt(:IPPROTO_IPV6, :IPV6_MULTICAST_LOOP).int)
- assert_equal(1, v6mc.getsockopt(:IPPROTO_IPV6, :IPV6_MULTICAST_HOPS).int)
- end
- end
-
- def test_make_socket_ipv4_multicast_hops
- @rf.multicast_hops = 2
- ipv4_mc(@rf) do |v4mc|
- assert_equal(2, v4mc.getsockopt(:IPPROTO_IP, :IP_MULTICAST_TTL).ipv4_multicast_ttl)
- end
- end
-
- def test_make_socket_ipv6_multicast_hops
- ipv6_mc(@rf, 2) do |v6mc|
- assert_equal(2, v6mc.getsockopt(:IPPROTO_IPV6, :IPV6_MULTICAST_HOPS).int)
- end
- end
-
-end
-
-end
diff --git a/test/rinda/test_tuplebag.rb b/test/rinda/test_tuplebag.rb
deleted file mode 100644
index ab17ca047c..0000000000
--- a/test/rinda/test_tuplebag.rb
+++ /dev/null
@@ -1,173 +0,0 @@
-# frozen_string_literal: false
-require 'test/unit'
-require 'rinda/tuplespace'
-
-class TestTupleBag < Test::Unit::TestCase
-
- def setup
- @tb = Rinda::TupleBag.new
- end
-
- def test_delete
- assert_nothing_raised do
- val = @tb.delete tup(:val, 1)
- assert_equal nil, val
- end
-
- t = tup(:val, 1)
- @tb.push t
-
- val = @tb.delete t
-
- assert_equal t, val
-
- assert_equal [], @tb.find_all(tem(:val, 1))
-
- t1 = tup(:val, 1)
- t2 = tup(:val, 1)
- @tb.push t1
- @tb.push t2
-
- val = @tb.delete t1
-
- assert_equal t1, val
-
- assert_equal [t2], @tb.find_all(tem(:val, 1))
- end
-
- def test_delete_unless_alive
- assert_equal [], @tb.delete_unless_alive
-
- t1 = tup(:val, nil)
- t2 = tup(:val, nil)
-
- @tb.push t1
- @tb.push t2
-
- assert_equal [], @tb.delete_unless_alive
-
- t1.cancel
-
- assert_equal [t1], @tb.delete_unless_alive, 'canceled'
-
- t2.renew Object.new
-
- assert_equal [t2], @tb.delete_unless_alive, 'expired'
- end
-
- def test_find
- template = tem(:val, nil)
-
- assert_equal nil, @tb.find(template)
-
- t1 = tup(:other, 1)
- @tb.push t1
-
- assert_equal nil, @tb.find(template)
-
- t2 = tup(:val, 1)
- @tb.push t2
-
- assert_equal t2, @tb.find(template)
-
- t2.cancel
-
- assert_equal nil, @tb.find(template), 'canceled'
-
- t3 = tup(:val, 3)
- @tb.push t3
-
- assert_equal t3, @tb.find(template)
-
- t3.renew Object.new
-
- assert_equal nil, @tb.find(template), 'expired'
- end
-
- def test_find_all
- template = tem(:val, nil)
-
- t1 = tup(:other, 1)
- @tb.push t1
-
- assert_equal [], @tb.find_all(template)
-
- t2 = tup(:val, 2)
- t3 = tup(:val, 3)
-
- @tb.push t2
- @tb.push t3
-
- assert_equal [t2, t3], @tb.find_all(template)
-
- t2.cancel
-
- assert_equal [t3], @tb.find_all(template), 'canceled'
-
- t3.renew Object.new
-
- assert_equal [], @tb.find_all(template), 'expired'
- end
-
- def test_find_all_template
- tuple = tup(:val, 1)
-
- t1 = tem(:other, nil)
- @tb.push t1
-
- assert_equal [], @tb.find_all_template(tuple)
-
- t2 = tem(:val, nil)
- t3 = tem(:val, nil)
-
- @tb.push t2
- @tb.push t3
-
- assert_equal [t2, t3], @tb.find_all_template(tuple)
-
- t2.cancel
-
- assert_equal [t3], @tb.find_all_template(tuple), 'canceled'
-
- t3.renew Object.new
-
- assert_equal [], @tb.find_all_template(tuple), 'expired'
- end
-
- def test_has_expires_eh
- assert !@tb.has_expires?
-
- t = tup(:val, 1)
- @tb.push t
-
- assert @tb.has_expires?
-
- t.renew Object.new
-
- assert !@tb.has_expires?
- end
-
- def test_push
- t = tup(:val, 1)
-
- @tb.push t
-
- assert_equal t, @tb.find(tem(:val, 1))
- end
-
- ##
- # Create a tuple with +ary+ for its contents
-
- def tup(*ary)
- Rinda::TupleEntry.new ary
- end
-
- ##
- # Create a template with +ary+ for its contents
-
- def tem(*ary)
- Rinda::TemplateEntry.new ary
- end
-
-end
-
diff --git a/test/ripper/assert_parse_files.rb b/test/ripper/assert_parse_files.rb
index f59e028182..0d583a99e3 100644
--- a/test/ripper/assert_parse_files.rb
+++ b/test/ripper/assert_parse_files.rb
@@ -7,7 +7,7 @@ class TestRipper::Generic < Test::Unit::TestCase
def assert_parse_files(dir, pattern = "**/*.rb", exclude: nil, gc_stress: GC.stress, test_ratio: nil)
test_ratio ||= ENV["TEST_RIPPER_RATIO"]&.tap {|s|break s.to_f} || 0.05 # testing all files needs too long time...
- assert_separately(%W[--disable-gem -rripper - #{SRCDIR}/#{dir} #{pattern}],
+ assert_separately(%W[-rripper - #{SRCDIR}/#{dir} #{pattern}],
__FILE__, __LINE__, "#{<<-"begin;"}\n#{<<-'end;'}", timeout: Float::INFINITY)
GC.stress = false
pattern = "#{pattern}"
diff --git a/test/ripper/dummyparser.rb b/test/ripper/dummyparser.rb
index fa834bd0f7..ef5ea49b1f 100644
--- a/test/ripper/dummyparser.rb
+++ b/test/ripper/dummyparser.rb
@@ -4,8 +4,9 @@
#
require 'ripper'
+module TestRipper; end
-class Node
+class TestRipper::Node
def initialize(name, *nodes)
@name = name
@children = nodes
@@ -14,7 +15,7 @@ class Node
attr_reader :name, :children
def to_s
- "#{@name}(#{Node.trim_nil(@children).map {|n| n.to_s }.join(',')})"
+ "#{@name}(#{TestRipper::Node.trim_nil(@children).map {|n| n.to_s }.join(',')})"
end
def self.trim_nil(list)
@@ -36,7 +37,7 @@ class Node
end
end
-class NodeList
+class TestRipper::NodeList
def initialize(list = [])
@list = list
end
@@ -62,7 +63,7 @@ class NodeList
end
end
-class DummyParser < Ripper
+class TestRipper::DummyParser < Ripper
def hook(*names)
class << self; self; end.class_eval do
names.each do |name|
@@ -81,7 +82,7 @@ class DummyParser < Ripper
end
def on_stmts_new
- NodeList.new
+ TestRipper::NodeList.new
end
def on_stmts_add(stmts, st)
@@ -90,23 +91,23 @@ class DummyParser < Ripper
end
def on_void_stmt
- Node.new('void')
+ TestRipper::Node.new('void')
end
def on_var_ref(name)
- Node.new('ref', name)
+ TestRipper::Node.new('ref', name)
end
def on_var_alias(a, b)
- Node.new('valias', a, b)
+ TestRipper::Node.new('valias', a, b)
end
def on_assign_error(mesg = nil, a)
- Node.new('assign_error', a)
+ TestRipper::Node.new('assign_error', a)
end
def on_alias_error(mesg = nil, a)
- Node.new('aliaserr', a)
+ TestRipper::Node.new('aliaserr', a)
end
def on_arg_paren(args)
@@ -114,7 +115,7 @@ class DummyParser < Ripper
end
def on_args_new
- NodeList.new
+ TestRipper::NodeList.new
end
def on_args_add(list, arg)
@@ -156,7 +157,7 @@ class DummyParser < Ripper
end
def on_brace_block(params, code)
- Node.new('block', params, code)
+ TestRipper::Node.new('block', params, code)
end
def on_block_var(params, shadow)
@@ -176,7 +177,7 @@ class DummyParser < Ripper
end
def on_params(required, optional, rest, more, keyword, keyword_rest, block)
- args = NodeList.new
+ args = TestRipper::NodeList.new
required.each do |req|
args.push(req)
@@ -197,15 +198,15 @@ class DummyParser < Ripper
end
def on_assoc_new(a, b)
- Node.new('assoc', a, b)
+ TestRipper::Node.new('assoc', a, b)
end
def on_bare_assoc_hash(assoc_list)
- Node.new('assocs', *assoc_list)
+ TestRipper::Node.new('assocs', *assoc_list)
end
def on_assoclist_from_args(a)
- Node.new('assocs', *a)
+ TestRipper::Node.new('assocs', *a)
end
def on_word_new
@@ -217,7 +218,7 @@ class DummyParser < Ripper
end
def on_words_new
- NodeList.new
+ TestRipper::NodeList.new
end
def on_words_add(words, word)
@@ -225,7 +226,7 @@ class DummyParser < Ripper
end
def on_qwords_new
- NodeList.new
+ TestRipper::NodeList.new
end
def on_qwords_add(words, word)
@@ -233,27 +234,27 @@ class DummyParser < Ripper
end
def on_symbols_new
- NodeList.new
+ TestRipper::NodeList.new
end
def on_symbols_add(symbols, symbol)
- symbols.push Node::Sym.new(symbol)
+ symbols.push TestRipper::Node::Sym.new(symbol)
end
def on_qsymbols_new
- NodeList.new
+ TestRipper::NodeList.new
end
def on_qsymbols_add(symbols, symbol)
- symbols.push Node::Sym.new(symbol)
+ symbols.push TestRipper::Node::Sym.new(symbol)
end
def on_mlhs_new
- NodeList.new
+ TestRipper::NodeList.new
end
def on_mlhs_paren(list)
- Node.new(:mlhs, list)
+ TestRipper::Node.new(:mlhs, list)
end
def on_mlhs_add(list, node)
@@ -277,12 +278,12 @@ class DummyParser < Ripper
end
def on_rescue(exc, *rest)
- Node.new('rescue', (exc && NodeList.new(exc)), *rest)
+ TestRipper::Node.new('rescue', (exc && TestRipper::NodeList.new(exc)), *rest)
end
(Ripper::PARSER_EVENTS.map(&:to_s) - instance_methods(false).map {|n|n.to_s.sub(/^on_/, '')}).each do |event|
define_method(:"on_#{event}") do |*args|
- Node.new(event, *args)
+ TestRipper::Node.new(event, *args)
end
end
end
diff --git a/test/ripper/test_lexer.rb b/test/ripper/test_lexer.rb
index 8e8a616627..7d62a7ee28 100644
--- a/test/ripper/test_lexer.rb
+++ b/test/ripper/test_lexer.rb
@@ -171,7 +171,7 @@ class TestRipper::Lexer < Test::Unit::TestCase
end
BAD_CODE = [
- [:parse_error, 'def req(true) end', %r[unexpected `true'], 'true'],
+ [:parse_error, 'def req(true) end', %r[unexpected 'true'], 'true'],
[:parse_error, 'def req(a, a) end', %r[duplicated argument name], 'a'],
[:assign_error, 'begin; nil = 1; end', %r[assign to nil], 'nil'],
[:alias_error, 'begin; alias $x $1; end', %r[number variables], '$1'],
@@ -264,4 +264,74 @@ world"
CODE
assert_equal(code, Ripper.tokenize(code).join(""), bug)
end
+
+ def test_heredoc_unterminated_interpolation
+ code = <<~'HEREDOC'
+ <<A+1
+ #{
+ HEREDOC
+
+ assert_include(Ripper.tokenize(code).join(""), "+1")
+ end
+
+ def test_nested_heredoc
+ code = <<~'HEREDOC'
+ <<~H1
+ 1
+ #{<<~H2}
+ 2
+ H2
+ 3
+ H1
+ HEREDOC
+
+ expected = [
+ [[1, 0], :on_heredoc_beg, "<<~H1", state(:EXPR_BEG)],
+ [[1, 5], :on_nl, "\n", state(:EXPR_BEG)],
+ [[2, 0], :on_ignored_sp, " ", state(:EXPR_BEG)],
+ [[2, 2], :on_tstring_content, "1\n", state(:EXPR_BEG)],
+ [[3, 0], :on_ignored_sp, " ", state(:EXPR_BEG)],
+ [[3, 2], :on_embexpr_beg, "\#{", state(:EXPR_BEG)],
+ [[3, 4], :on_heredoc_beg, "<<~H2", state(:EXPR_BEG)],
+ [[3, 9], :on_embexpr_end, "}", state(:EXPR_END)],
+ [[3, 10], :on_tstring_content, "\n", state(:EXPR_BEG)],
+ [[4, 0], :on_ignored_sp, " ", state(:EXPR_BEG)],
+ [[4, 4], :on_tstring_content, "2\n", state(:EXPR_BEG)],
+ [[5, 0], :on_heredoc_end, " H2\n", state(:EXPR_BEG)],
+ [[6, 0], :on_ignored_sp, " ", state(:EXPR_BEG)],
+ [[6, 2], :on_tstring_content, "3\n", state(:EXPR_BEG)],
+ [[7, 0], :on_heredoc_end, "H1\n", state(:EXPR_BEG)],
+ ]
+ assert_equal(code, Ripper.tokenize(code).join(""))
+ assert_equal(expected, result = Ripper.lex(code),
+ proc {expected.zip(result) {|e, r| break diff(e, r) unless e == r}})
+
+ code = <<~'HEREDOC'
+ <<-H1
+ 1
+ #{<<~H2}
+ 2
+ H2
+ 3
+ H1
+ HEREDOC
+
+ expected = [
+ [[1, 0], :on_heredoc_beg, "<<-H1", state(:EXPR_BEG)],
+ [[1, 5], :on_nl, "\n", state(:EXPR_BEG)],
+ [[2, 0], :on_tstring_content, " 1\n ", state(:EXPR_BEG)],
+ [[3, 2], :on_embexpr_beg, "\#{", state(:EXPR_BEG)],
+ [[3, 4], :on_heredoc_beg, "<<~H2", state(:EXPR_BEG)],
+ [[3, 9], :on_embexpr_end, "}", state(:EXPR_END)],
+ [[3, 10], :on_tstring_content, "\n", state(:EXPR_BEG)],
+ [[4, 0], :on_ignored_sp, " ", state(:EXPR_BEG)],
+ [[4, 4], :on_tstring_content, "2\n", state(:EXPR_BEG)],
+ [[5, 0], :on_heredoc_end, " H2\n", state(:EXPR_BEG)],
+ [[6, 0], :on_tstring_content, " 3\n", state(:EXPR_BEG)],
+ [[7, 0], :on_heredoc_end, "H1\n", state(:EXPR_BEG)],
+ ]
+ assert_equal(code, Ripper.tokenize(code).join(""))
+ assert_equal(expected, result = Ripper.lex(code),
+ proc {expected.zip(result) {|e, r| break diff(e, r) unless e == r}})
+ end
end
diff --git a/test/ripper/test_parser_events.rb b/test/ripper/test_parser_events.rb
index 1ea8d23378..cbae6e7ed5 100644
--- a/test/ripper/test_parser_events.rb
+++ b/test/ripper/test_parser_events.rb
@@ -16,7 +16,7 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
end
def parse(str, nm = nil, &bl)
- dp = DummyParser.new(str)
+ dp = TestRipper::DummyParser.new(str)
dp.hook(*nm, &bl) if nm
dp.parse.to_s
end
@@ -152,6 +152,7 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
thru_args_forward = false
parse(code, :on_args_forward) {thru_args_forward = true}
assert_equal true, thru_args_forward, "no args_forward for: #{code}"
+ parse(code, :on_params) {|*, block| assert_nil(block)}
end
end
@@ -499,6 +500,23 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
assert_equal "[call(ref(self),&.,foo,[])]", tree
end
+ def test_call_colon2
+ hook = Module.new do
+ def on_op(op)
+ super("(op: #{op.inspect})")
+ end
+ def on_call(recv, name, *args)
+ super(recv, "(method: #{name})", *args)
+ end
+ def on_ident(name)
+ super("(ident: #{name.inspect})")
+ end
+ end
+
+ parser = TestRipper::DummyParser.new("a::b").extend(hook)
+ assert_equal '[call(vcall((ident: "a")),(method: (op: "::")),(ident: "b"))]', parser.parse.to_s
+ end
+
def test_excessed_comma
thru_excessed_comma = false
parse("proc{|x,|}", :on_excessed_comma) {thru_excessed_comma = true}
@@ -1626,20 +1644,20 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
end
def test_invalid_instance_variable_name
- assert_equal("`@1' is not allowed as an instance variable name", compile_error('proc{@1}'))
- assert_equal("`@' without identifiers is not allowed as an instance variable name", compile_error('@%'))
- assert_equal("`@' without identifiers is not allowed as an instance variable name", compile_error('@'))
+ assert_equal("'@1' is not allowed as an instance variable name", compile_error('proc{@1}'))
+ assert_equal("'@' without identifiers is not allowed as an instance variable name", compile_error('@%'))
+ assert_equal("'@' without identifiers is not allowed as an instance variable name", compile_error('@'))
end
def test_invalid_class_variable_name
- assert_equal("`@@1' is not allowed as a class variable name", compile_error('@@1'))
- assert_equal("`@@' without identifiers is not allowed as a class variable name", compile_error('@@%'))
- assert_equal("`@@' without identifiers is not allowed as a class variable name", compile_error('@@'))
+ assert_equal("'@@1' is not allowed as a class variable name", compile_error('@@1'))
+ assert_equal("'@@' without identifiers is not allowed as a class variable name", compile_error('@@%'))
+ assert_equal("'@@' without identifiers is not allowed as a class variable name", compile_error('@@'))
end
def test_invalid_global_variable_name
- assert_equal("`$%' is not allowed as a global variable name", compile_error('$%'))
- assert_equal("`$' without identifiers is not allowed as a global variable name", compile_error('$'))
+ assert_equal("'$%' is not allowed as a global variable name", compile_error('$%'))
+ assert_equal("'$' without identifiers is not allowed as a global variable name", compile_error('$'))
end
def test_warning_ignored_magic_comment
@@ -1654,6 +1672,26 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
assert_equal(%w"frozen_string_literal nottrue", args)
end
+ def test_warning_duplicated_when_clause
+ fmt, *args = warning(<<~STR)
+ a = 1
+ case a
+ when 1
+ when 1
+ when 2
+ else
+ end
+ STR
+ assert_match(/duplicated 'when' clause/, fmt)
+ assert_equal([3], args)
+ end
+
+ def test_warn_duplicated_hash_keys
+ fmt, *args = warn("{ a: 1, a: 2 }")
+ assert_match(/is duplicated and overwritten on line/, fmt)
+ assert_equal([:a, 1], args)
+ end
+
def test_warn_cr_in_middle
fmt = nil
assert_warn("") {fmt, = warn("\r;")}
diff --git a/test/ripper/test_ripper.rb b/test/ripper/test_ripper.rb
index 76276c54ef..6061496d9c 100644
--- a/test/ripper/test_ripper.rb
+++ b/test/ripper/test_ripper.rb
@@ -14,6 +14,13 @@ class TestRipper::Ripper < Test::Unit::TestCase
@ripper = Ripper.new '1 + 1'
end
+ def test_new
+ assert_separately(%w[-rripper], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ assert_nil EnvUtil.under_gc_stress {Ripper.new("")}.state
+ end;
+ end
+
def test_column
assert_nil @ripper.column
end
@@ -81,7 +88,7 @@ class TestRipper::Ripper < Test::Unit::TestCase
ripper.yydebug = true
ripper.debug_output = out
ripper.parse
- assert_include out.string[/.*"literal content".*/], 'woot'
+ assert_include out.string[/.*"literal content".*/], '1.1-1.5'
end
def test_regexp_with_option
@@ -141,6 +148,28 @@ end
assert_nothing_raised { Ripper.lex src }
end
+ def test_no_memory_leak
+ assert_no_memory_leak(%w(-rripper), "", "#{<<~'end;'}", rss: true)
+ 2_000_000.times do
+ Ripper.parse("")
+ end
+ end;
+
+ # [Bug #19835]
+ assert_no_memory_leak(%w(-rripper), "", "#{<<~'end;'}", rss: true)
+ 1_000_000.times do
+ Ripper.parse("class Foo")
+ end
+ end;
+
+ # [Bug #19836]
+ assert_no_memory_leak(%w(-rripper), "", "#{<<~'end;'}", rss: true)
+ 1_000_000.times do
+ Ripper.parse("-> {")
+ end
+ end;
+ end
+
class TestInput < self
Input = Struct.new(:lines) do
def gets
diff --git a/test/ripper/test_scanner_events.rb b/test/ripper/test_scanner_events.rb
index 5d6ac615ca..792f19ef1a 100644
--- a/test/ripper/test_scanner_events.rb
+++ b/test/ripper/test_scanner_events.rb
@@ -179,6 +179,11 @@ class TestRipper::ScannerEvents < Test::Unit::TestCase
scan('backtick', %q[p `make all`])
end
+ def test_colon2_call
+ assert_equal ["::"],
+ scan('op', %q[ a::b ])
+ end
+
def test_comma
assert_equal [','] * 6,
scan('comma', %q[ m(0,1,2,3,4,5,6) ])
@@ -986,13 +991,21 @@ class TestRipper::ScannerEvents < Test::Unit::TestCase
assert_equal("\e", err[2])
end
- def test_invalid_hex_escape
+ def test_invalid_escape
+ err = nil
+ assert_equal ["\\C-\u{3042}"], scan('tstring_content', %["\\C-\u{3042}"]) {|*e| err = e}
+ assert_equal [:on_parse_error, "Invalid escape character syntax", "\\C-\u{3042}"], err
+ end
+
+ def test_invalid_hex_escape_string
err = nil
- assert_equal ['U'], scan('tstring_content', '"\\xU"') {|*e| err = e}
+ assert_equal ['\\x', 'U'], scan('tstring_content', '"\\xU"') {|*e| err = e}
assert_equal [:on_parse_error, "invalid hex escape", "\\x"], err
+ end
+ def test_invalid_hex_escape_regexp
err = nil
- assert_equal ['U'], scan('tstring_content', '/\\xU/') {|*e| err = e}
+ assert_equal ['\\x', 'U'], scan('tstring_content', '/\\xU/') {|*e| err = e}
assert_equal [:on_parse_error, "invalid hex escape", "\\x"], err
end
diff --git a/test/ripper/test_sexp.rb b/test/ripper/test_sexp.rb
index a1cf5e4f0a..3ebcf3062e 100644
--- a/test/ripper/test_sexp.rb
+++ b/test/ripper/test_sexp.rb
@@ -18,6 +18,11 @@ class TestRipper::Sexp < Test::Unit::TestCase
assert_nil Ripper.sexp("/*")
assert_nil Ripper.sexp("/*/")
assert_nil Ripper.sexp("/+/")
+ assert_nil Ripper.sexp("m(&nil) {}"), '[Bug #10436]'
+ assert_nil Ripper.sexp("/(?<a>)/ =~ ''; x = a **a, **a if false"), '[Bug #18988]'
+ assert_nil Ripper.sexp("return + return"), '[Bug #20055]'
+ assert_nil Ripper.sexp("1 in [a, a]"), '[Bug #20055]'
+ assert_nil Ripper.sexp("1 + (1 => [a, a])"), '[Bug #20055]'
end
def test_regexp_content
@@ -34,6 +39,14 @@ class TestRipper::Sexp < Test::Unit::TestCase
assert_equal '(?<n>a(b|\g<n>))', search_sexp(:@tstring_content, search_sexp(:regexp_literal, sexp))[1]
end
+ def test_regexp_named_capture
+ sexp = Ripper.sexp("/(?<a>)/ =~ ''; x = a **a, a if false")
+ assert_not_nil sexp, '[Bug #18988]'
+
+ sexp = Ripper.sexp("/(?<a>)/ =~ ''; a %(exit)")
+ assert_equal 'exit', search_sexp(:@ident, search_sexp(:paren, sexp))[1], '[Bug #18988]'
+ end
+
def test_heredoc_content
sexp = Ripper.sexp("<<E\nfoo\nE")
assert_equal "foo\n", search_sexp(:@tstring_content, sexp)[1]
@@ -110,6 +123,21 @@ eot
assert_equal(exp, named)
end
+ def test_command
+ sexp = Ripper.sexp("a::C {}")
+ assert_equal(
+ [:program,
+ [
+ [:method_add_block,
+ [:command_call,
+ [:vcall, [:@ident, "a", [1, 0]]],
+ [:@op, "::", [1, 1]],
+ [:@const, "C", [1, 3]],
+ nil],
+ [:brace_block, nil, [[:void_stmt]]]]]],
+ sexp)
+ end
+
def search_sexp(sym, sexp)
return sexp if !sexp or sexp[0] == sym
sexp.find do |e|
diff --git a/test/ruby/enc/test_case_mapping.rb b/test/ruby/enc/test_case_mapping.rb
index 31acdc4331..a7d1ed0d16 100644
--- a/test/ruby/enc/test_case_mapping.rb
+++ b/test/ruby/enc/test_case_mapping.rb
@@ -47,7 +47,7 @@ class TestCaseMappingPreliminary < Test::Unit::TestCase
# different properties; careful: roundtrip isn't always guaranteed
def check_swapcase_properties(expected, start, *flags)
assert_equal expected, start.swapcase(*flags)
- temp = start
+ temp = +start
assert_equal expected, temp.swapcase!(*flags)
assert_equal start, start.swapcase(*flags).swapcase(*flags)
assert_equal expected, expected.swapcase(*flags).swapcase(*flags)
@@ -61,10 +61,10 @@ class TestCaseMappingPreliminary < Test::Unit::TestCase
end
def test_invalid
- assert_raise(ArgumentError, "Should not be possible to upcase invalid string.") { "\xEB".force_encoding('UTF-8').upcase }
- assert_raise(ArgumentError, "Should not be possible to downcase invalid string.") { "\xEB".force_encoding('UTF-8').downcase }
- assert_raise(ArgumentError, "Should not be possible to capitalize invalid string.") { "\xEB".force_encoding('UTF-8').capitalize }
- assert_raise(ArgumentError, "Should not be possible to swapcase invalid string.") { "\xEB".force_encoding('UTF-8').swapcase }
+ assert_raise(ArgumentError, "Should not be possible to upcase invalid string.") { "\xEB".dup.force_encoding('UTF-8').upcase }
+ assert_raise(ArgumentError, "Should not be possible to downcase invalid string.") { "\xEB".dup.force_encoding('UTF-8').downcase }
+ assert_raise(ArgumentError, "Should not be possible to capitalize invalid string.") { "\xEB".dup.force_encoding('UTF-8').capitalize }
+ assert_raise(ArgumentError, "Should not be possible to swapcase invalid string.") { "\xEB".dup.force_encoding('UTF-8').swapcase }
end
def test_general
diff --git a/test/ruby/enc/test_case_options.rb b/test/ruby/enc/test_case_options.rb
index e9bf50fcfc..e9c81d804e 100644
--- a/test/ruby/enc/test_case_options.rb
+++ b/test/ruby/enc/test_case_options.rb
@@ -19,7 +19,7 @@ class TestCaseOptions < Test::Unit::TestCase
def assert_raise_both_types(*options)
assert_raise_functional_operations 'a', *options
- assert_raise_bang_operations 'a', *options
+ assert_raise_bang_operations(+'a', *options)
assert_raise_functional_operations :a, *options
end
@@ -51,7 +51,7 @@ class TestCaseOptions < Test::Unit::TestCase
def assert_okay_both_types(*options)
assert_okay_functional_operations 'a', *options
- assert_okay_bang_operations 'a', *options
+ assert_okay_bang_operations(+'a', *options)
assert_okay_functional_operations :a, *options
end
@@ -69,10 +69,10 @@ class TestCaseOptions < Test::Unit::TestCase
assert_raise(ArgumentError) { 'a'.upcase :fold }
assert_raise(ArgumentError) { 'a'.capitalize :fold }
assert_raise(ArgumentError) { 'a'.swapcase :fold }
- assert_nothing_raised { 'a'.downcase! :fold }
- assert_raise(ArgumentError) { 'a'.upcase! :fold }
- assert_raise(ArgumentError) { 'a'.capitalize! :fold }
- assert_raise(ArgumentError) { 'a'.swapcase! :fold }
+ assert_nothing_raised { 'a'.dup.downcase! :fold }
+ assert_raise(ArgumentError) { 'a'.dup.upcase! :fold }
+ assert_raise(ArgumentError) { 'a'.dup.capitalize! :fold }
+ assert_raise(ArgumentError) { 'a'.dup.swapcase! :fold }
assert_nothing_raised { :a.downcase :fold }
assert_raise(ArgumentError) { :a.upcase :fold }
assert_raise(ArgumentError) { :a.capitalize :fold }
diff --git a/test/ruby/rjit/test_assembler.rb b/test/ruby/rjit/test_assembler.rb
index a9ed6ce39e..fbf780d6c3 100644
--- a/test/ruby/rjit/test_assembler.rb
+++ b/test/ruby/rjit/test_assembler.rb
@@ -122,6 +122,7 @@ module RubyVM::RJIT
asm.cmp(BytePtr[:rax, 8], 8) # CMP r/m8, imm8 (Mod 01: [reg]+disp8)
asm.cmp(DwordPtr[:rax, 8], 0x100) # CMP r/m32, imm32 (Mod 01: [reg]+disp8)
asm.cmp([:rax, 8], 8) # CMP r/m64, imm8 (Mod 01: [reg]+disp8)
+ asm.cmp([:rbx, 8], 0x100) # CMP r/m64, imm32 (Mod 01: [reg]+disp8)
asm.cmp([:rax, 0x100], 8) # CMP r/m64, imm8 (Mod 10: [reg]+disp32)
asm.cmp(:rax, 8) # CMP r/m64, imm8 (Mod 11: reg)
asm.cmp(:rax, 0x100) # CMP r/m64, imm32 (Mod 11: reg)
@@ -132,12 +133,13 @@ module RubyVM::RJIT
0x0: cmp byte ptr [rax + 8], 8
0x4: cmp dword ptr [rax + 8], 0x100
0xb: cmp qword ptr [rax + 8], 8
- 0x10: cmp qword ptr [rax + 0x100], 8
- 0x18: cmp rax, 8
- 0x1c: cmp rax, 0x100
- 0x23: cmp qword ptr [rax + 8], rbx
- 0x27: cmp qword ptr [rax - 0x100], rbx
- 0x2e: cmp rax, rbx
+ 0x10: cmp qword ptr [rbx + 8], 0x100
+ 0x18: cmp qword ptr [rax + 0x100], 8
+ 0x20: cmp rax, 8
+ 0x24: cmp rax, 0x100
+ 0x2b: cmp qword ptr [rax + 8], rbx
+ 0x2f: cmp qword ptr [rax - 0x100], rbx
+ 0x36: cmp rax, rbx
EOS
end
diff --git a/test/ruby/test_allocation.rb b/test/ruby/test_allocation.rb
new file mode 100644
index 0000000000..48348c0fbd
--- /dev/null
+++ b/test/ruby/test_allocation.rb
@@ -0,0 +1,656 @@
+# frozen_string_literal: false
+require 'test/unit'
+
+class TestAllocation < Test::Unit::TestCase
+ def check_allocations(checks)
+ assert_separately([], <<~RUBY)
+ $allocations = [0, 0]
+ $counts = {}
+ failures = []
+
+ def self.num_allocations
+ ObjectSpace.count_objects($counts)
+ arrays = $counts[:T_ARRAY]
+ hashes = $counts[:T_HASH]
+ yield
+ ObjectSpace.count_objects($counts)
+ arrays -= $counts[:T_ARRAY]
+ hashes -= $counts[:T_HASH]
+ $allocations[0] = -arrays
+ $allocations[1] = -hashes
+ end
+
+ define_singleton_method(:check_allocations) do |num_arrays, num_hashes, check_code|
+ instance_eval <<~RB
+ empty_array = empty_array = []
+ empty_hash = empty_hash = {}
+ array1 = array1 = [1]
+ hash1 = hash1 = {a: 2}
+ nill = nill = nil
+ block = block = lambda{}
+
+ num_allocations do
+ \#{check_code}
+ end
+ RB
+
+ if num_arrays != $allocations[0]
+ failures << "Expected \#{num_arrays} array allocations for \#{check_code.inspect}, but \#{$allocations[0]} arrays allocated"
+ end
+ if num_hashes != $allocations[1]
+ failures << "Expected \#{num_hashes} hash allocations for \#{check_code.inspect}, but \#{$allocations[1]} hashes allocated"
+ end
+ end
+
+ GC.start
+ GC.disable
+
+ #{checks}
+
+ unless failures.empty?
+ assert_equal(true, false, failures.join("\n"))
+ end
+ RUBY
+ end
+
+ class Literal < self
+ def test_array_literal
+ check_allocations(<<~RUBY)
+ check_allocations(1, 0, "[]")
+ check_allocations(1, 0, "[1]")
+ check_allocations(1, 0, "[*empty_array]")
+ check_allocations(1, 0, "[*empty_array, 1, *empty_array]")
+ check_allocations(1, 0, "[*empty_array, *empty_array]")
+ check_allocations(1, 0, "[#{'1,'*100000}]")
+ RUBY
+ end
+
+ def test_hash_literal
+ check_allocations(<<~RUBY)
+ check_allocations(0, 1, "{}")
+ check_allocations(0, 1, "{a: 1}")
+ check_allocations(0, 1, "{**empty_hash}")
+ check_allocations(0, 1, "{**empty_hash, a: 1, **empty_hash}")
+ check_allocations(0, 1, "{**empty_hash, **empty_hash}")
+ check_allocations(0, 1, "{#{100000.times.map{|i| "a#{i}: 1"}.join(',')}}")
+ RUBY
+ end
+ end
+
+ class MethodCall < self
+ def block
+ ''
+ end
+
+ def test_no_parameters
+ only_block = block.empty? ? block : block[2..]
+ check_allocations(<<~RUBY)
+ def self.none(#{only_block}); end
+
+ check_allocations(0, 0, "none(#{only_block})")
+ check_allocations(0, 0, "none(*empty_array#{block})")
+ check_allocations(0, 0, "none(**empty_hash#{block})")
+ check_allocations(0, 0, "none(*empty_array, **empty_hash#{block})")
+
+ check_allocations(1, 0, "none(*empty_array, *empty_array#{block})")
+ check_allocations(0, 1, "none(**empty_hash, **empty_hash#{block})")
+ check_allocations(1, 1, "none(*empty_array, *empty_array, **empty_hash, **empty_hash#{block})")
+ RUBY
+ end
+
+ def test_required_parameter
+ check_allocations(<<~RUBY)
+ def self.required(x#{block}); end
+
+ check_allocations(0, 0, "required(1#{block})")
+ check_allocations(0, 0, "required(1, *empty_array#{block})")
+ check_allocations(0, 0, "required(1, **empty_hash#{block})")
+ check_allocations(0, 0, "required(1, *empty_array, **empty_hash#{block})")
+
+ check_allocations(0, 0, "required(*array1#{block})")
+ check_allocations(0, 1, "required(**hash1#{block})")
+
+ check_allocations(1, 0, "required(*array1, *empty_array#{block})")
+ check_allocations(0, 1, "required(**hash1, **empty_hash#{block})")
+ check_allocations(1, 0, "required(*array1, *empty_array, **empty_hash#{block})")
+
+ # Currently allocates 1 array unnecessarily due to splatarray true
+ check_allocations(1, 1, "required(*empty_array, **hash1, **empty_hash#{block})")
+ RUBY
+ end
+
+ def test_optional_parameter
+ check_allocations(<<~RUBY)
+ def self.optional(x=nil#{block}); end
+
+ check_allocations(0, 0, "optional(1#{block})")
+ check_allocations(0, 0, "optional(1, *empty_array#{block})")
+ check_allocations(0, 0, "optional(1, **empty_hash#{block})")
+ check_allocations(0, 0, "optional(1, *empty_array, **empty_hash#{block})")
+
+ check_allocations(0, 0, "optional(*array1#{block})")
+ check_allocations(0, 1, "optional(**hash1#{block})")
+
+ check_allocations(1, 0, "optional(*array1, *empty_array#{block})")
+ check_allocations(0, 1, "optional(**hash1, **empty_hash#{block})")
+ check_allocations(1, 0, "optional(*array1, *empty_array, **empty_hash#{block})")
+
+ # Currently allocates 1 array unnecessarily due to splatarray true
+ check_allocations(1, 1, "optional(*empty_array, **hash1, **empty_hash#{block})")
+ RUBY
+ end
+
+ def test_positional_splat_parameter
+ check_allocations(<<~RUBY)
+ def self.splat(*x#{block}); end
+
+ check_allocations(1, 0, "splat(1#{block})")
+ check_allocations(1, 0, "splat(1, *empty_array#{block})")
+ check_allocations(1, 0, "splat(1, **empty_hash#{block})")
+ check_allocations(1, 0, "splat(1, *empty_array, **empty_hash#{block})")
+
+ check_allocations(1, 0, "splat(*array1#{block})")
+ check_allocations(1, 0, "splat(*array1, *empty_array#{block})")
+ check_allocations(1, 0, "splat(*array1, **empty_hash#{block})")
+ check_allocations(1, 0, "splat(*array1, *empty_array, **empty_hash#{block})")
+
+ check_allocations(1, 0, "splat(1, *array1#{block})")
+ check_allocations(1, 0, "splat(1, *array1, *empty_array#{block})")
+ check_allocations(1, 0, "splat(1, *array1, **empty_hash#{block})")
+ check_allocations(1, 0, "splat(1, *array1, *empty_array, **empty_hash#{block})")
+
+ check_allocations(1, 0, "splat(*array1#{block})")
+ check_allocations(1, 1, "splat(**hash1#{block})")
+
+ check_allocations(1, 0, "splat(*array1, *empty_array#{block})")
+ check_allocations(1, 1, "splat(**hash1, **empty_hash#{block})")
+ check_allocations(1, 0, "splat(*array1, *empty_array, **empty_hash#{block})")
+ check_allocations(1, 1, "splat(*empty_array, **hash1, **empty_hash#{block})")
+ RUBY
+ end
+
+ def test_required_and_positional_splat_parameters
+ check_allocations(<<~RUBY)
+ def self.req_splat(x, *y#{block}); end
+
+ check_allocations(1, 0, "req_splat(1#{block})")
+ check_allocations(1, 0, "req_splat(1, *empty_array#{block})")
+ check_allocations(1, 0, "req_splat(1, **empty_hash#{block})")
+ check_allocations(1, 0, "req_splat(1, *empty_array, **empty_hash#{block})")
+
+ check_allocations(1, 0, "req_splat(*array1#{block})")
+ check_allocations(1, 0, "req_splat(*array1, *empty_array#{block})")
+ check_allocations(1, 0, "req_splat(*array1, **empty_hash#{block})")
+ check_allocations(1, 0, "req_splat(*array1, *empty_array, **empty_hash#{block})")
+
+ check_allocations(1, 0, "req_splat(1, *array1#{block})")
+ check_allocations(1, 0, "req_splat(1, *array1, *empty_array#{block})")
+ check_allocations(1, 0, "req_splat(1, *array1, **empty_hash#{block})")
+ check_allocations(1, 0, "req_splat(1, *array1, *empty_array, **empty_hash#{block})")
+
+ check_allocations(1, 0, "req_splat(*array1#{block})")
+ check_allocations(1, 1, "req_splat(**hash1#{block})")
+
+ check_allocations(1, 0, "req_splat(*array1, *empty_array#{block})")
+ check_allocations(1, 1, "req_splat(**hash1, **empty_hash#{block})")
+ check_allocations(1, 0, "req_splat(*array1, *empty_array, **empty_hash#{block})")
+ check_allocations(1, 1, "req_splat(*empty_array, **hash1, **empty_hash#{block})")
+ RUBY
+ end
+
+ def test_positional_splat_and_post_parameters
+ check_allocations(<<~RUBY)
+ def self.splat_post(*x, y#{block}); end
+
+ check_allocations(1, 0, "splat_post(1#{block})")
+ check_allocations(1, 0, "splat_post(1, *empty_array#{block})")
+ check_allocations(1, 0, "splat_post(1, **empty_hash#{block})")
+ check_allocations(1, 0, "splat_post(1, *empty_array, **empty_hash#{block})")
+
+ check_allocations(1, 0, "splat_post(*array1#{block})")
+ check_allocations(1, 0, "splat_post(*array1, *empty_array#{block})")
+ check_allocations(1, 0, "splat_post(*array1, **empty_hash#{block})")
+ check_allocations(1, 0, "splat_post(*array1, *empty_array, **empty_hash#{block})")
+
+ check_allocations(1, 0, "splat_post(1, *array1#{block})")
+ check_allocations(1, 0, "splat_post(1, *array1, *empty_array#{block})")
+ check_allocations(1, 0, "splat_post(1, *array1, **empty_hash#{block})")
+ check_allocations(1, 0, "splat_post(1, *array1, *empty_array, **empty_hash#{block})")
+
+ check_allocations(1, 0, "splat_post(*array1#{block})")
+ check_allocations(1, 1, "splat_post(**hash1#{block})")
+
+ check_allocations(1, 0, "splat_post(*array1, *empty_array#{block})")
+ check_allocations(1, 1, "splat_post(**hash1, **empty_hash#{block})")
+ check_allocations(1, 0, "splat_post(*array1, *empty_array, **empty_hash#{block})")
+ check_allocations(1, 1, "splat_post(*empty_array, **hash1, **empty_hash#{block})")
+ RUBY
+ end
+
+ def test_keyword_parameter
+ check_allocations(<<~RUBY)
+ def self.keyword(a: nil#{block}); end
+
+ check_allocations(0, 0, "keyword(a: 2#{block})")
+ check_allocations(0, 0, "keyword(*empty_array, a: 2#{block})")
+ check_allocations(0, 1, "keyword(a:2, **empty_hash#{block})")
+ check_allocations(0, 1, "keyword(**empty_hash, a: 2#{block})")
+
+ check_allocations(0, 0, "keyword(**nil#{block})")
+ check_allocations(0, 0, "keyword(**empty_hash#{block})")
+ check_allocations(0, 0, "keyword(**hash1#{block})")
+ check_allocations(0, 0, "keyword(*empty_array, **hash1#{block})")
+ check_allocations(0, 1, "keyword(**hash1, **empty_hash#{block})")
+ check_allocations(0, 1, "keyword(**empty_hash, **hash1#{block})")
+
+ check_allocations(0, 0, "keyword(*empty_array#{block})")
+ check_allocations(0, 1, "keyword(**hash1, **empty_hash#{block})")
+ check_allocations(1, 0, "keyword(*empty_array, *empty_array, **empty_hash#{block})")
+
+ check_allocations(0, 0, "keyword(*empty_array#{block})")
+ check_allocations(0, 1, "keyword(**hash1, **empty_hash#{block})")
+ check_allocations(1, 0, "keyword(*empty_array, *empty_array, **empty_hash#{block})")
+
+ # Currently allocates 1 array unnecessarily due to splatarray true
+ check_allocations(1, 1, "keyword(*empty_array, a: 2, **empty_hash#{block})")
+ check_allocations(1, 1, "keyword(*empty_array, **hash1, **empty_hash#{block})")
+ RUBY
+ end
+
+ def test_keyword_splat_parameter
+ check_allocations(<<~RUBY)
+ def self.keyword_splat(**kw#{block}); end
+
+ check_allocations(0, 1, "keyword_splat(a: 2#{block})")
+ check_allocations(0, 1, "keyword_splat(*empty_array, a: 2#{block})")
+ check_allocations(0, 1, "keyword_splat(a:2, **empty_hash#{block})")
+ check_allocations(0, 1, "keyword_splat(**empty_hash, a: 2#{block})")
+
+ check_allocations(0, 1, "keyword_splat(**nil#{block})")
+ check_allocations(0, 1, "keyword_splat(**empty_hash#{block})")
+ check_allocations(0, 1, "keyword_splat(**hash1#{block})")
+ check_allocations(0, 1, "keyword_splat(*empty_array, **hash1#{block})")
+ check_allocations(0, 1, "keyword_splat(**hash1, **empty_hash#{block})")
+ check_allocations(0, 1, "keyword_splat(**empty_hash, **hash1#{block})")
+
+ check_allocations(0, 1, "keyword_splat(*empty_array#{block})")
+ check_allocations(0, 1, "keyword_splat(**hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "keyword_splat(*empty_array, *empty_array, **empty_hash#{block})")
+
+ check_allocations(0, 1, "keyword_splat(*empty_array#{block})")
+ check_allocations(0, 1, "keyword_splat(**hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "keyword_splat(*empty_array, *empty_array, **empty_hash#{block})")
+
+ # Currently allocates 1 array unnecessarily due to splatarray true
+ check_allocations(1, 1, "keyword_splat(*empty_array, a: 2, **empty_hash#{block})")
+ check_allocations(1, 1, "keyword_splat(*empty_array, **hash1, **empty_hash#{block})")
+ RUBY
+ end
+
+ def test_keyword_and_keyword_splat_parameter
+ check_allocations(<<~RUBY)
+ def self.keyword_and_keyword_splat(a: 1, **kw#{block}); end
+
+ check_allocations(0, 1, "keyword_and_keyword_splat(a: 2#{block})")
+ check_allocations(0, 1, "keyword_and_keyword_splat(*empty_array, a: 2#{block})")
+ check_allocations(0, 1, "keyword_and_keyword_splat(a:2, **empty_hash#{block})")
+ check_allocations(0, 1, "keyword_and_keyword_splat(**empty_hash, a: 2#{block})")
+
+ check_allocations(0, 1, "keyword_and_keyword_splat(**nil#{block})")
+ check_allocations(0, 1, "keyword_and_keyword_splat(**empty_hash#{block})")
+ check_allocations(0, 1, "keyword_and_keyword_splat(**hash1#{block})")
+ check_allocations(0, 1, "keyword_and_keyword_splat(*empty_array, **hash1#{block})")
+ check_allocations(0, 1, "keyword_and_keyword_splat(**hash1, **empty_hash#{block})")
+ check_allocations(0, 1, "keyword_and_keyword_splat(**empty_hash, **hash1#{block})")
+
+ check_allocations(0, 1, "keyword_and_keyword_splat(*empty_array#{block})")
+ check_allocations(0, 1, "keyword_and_keyword_splat(**hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "keyword_and_keyword_splat(*empty_array, *empty_array, **empty_hash#{block})")
+
+ check_allocations(0, 1, "keyword_and_keyword_splat(*empty_array#{block})")
+ check_allocations(0, 1, "keyword_and_keyword_splat(**hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "keyword_and_keyword_splat(*empty_array, *empty_array, **empty_hash#{block})")
+
+ # Currently allocates 1 array unnecessarily due to splatarray true
+ check_allocations(1, 1, "keyword_and_keyword_splat(*empty_array, a: 2, **empty_hash#{block})")
+ check_allocations(1, 1, "keyword_and_keyword_splat(*empty_array, **hash1, **empty_hash#{block})")
+ RUBY
+ end
+
+ def test_required_positional_and_keyword_parameter
+ check_allocations(<<~RUBY)
+ def self.required_and_keyword(b, a: nil#{block}); end
+
+ check_allocations(0, 0, "required_and_keyword(1, a: 2#{block})")
+ check_allocations(0, 0, "required_and_keyword(1, *empty_array, a: 2#{block})")
+ check_allocations(0, 1, "required_and_keyword(1, a:2, **empty_hash#{block})")
+ check_allocations(0, 1, "required_and_keyword(1, **empty_hash, a: 2#{block})")
+
+ check_allocations(0, 0, "required_and_keyword(1, **nil#{block})")
+ check_allocations(0, 0, "required_and_keyword(1, **empty_hash#{block})")
+ check_allocations(0, 0, "required_and_keyword(1, **hash1#{block})")
+ check_allocations(0, 0, "required_and_keyword(1, *empty_array, **hash1#{block})")
+ check_allocations(0, 1, "required_and_keyword(1, **hash1, **empty_hash#{block})")
+ check_allocations(0, 1, "required_and_keyword(1, **empty_hash, **hash1#{block})")
+
+ check_allocations(0, 0, "required_and_keyword(1, *empty_array#{block})")
+ check_allocations(0, 1, "required_and_keyword(1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 0, "required_and_keyword(1, *empty_array, *empty_array, **empty_hash#{block})")
+
+ check_allocations(0, 0, "required_and_keyword(*array1, a: 2#{block})")
+
+ check_allocations(0, 0, "required_and_keyword(*array1, **nill#{block})")
+ check_allocations(0, 0, "required_and_keyword(*array1, **empty_hash#{block})")
+ check_allocations(0, 0, "required_and_keyword(*array1, **hash1#{block})")
+ check_allocations(1, 0, "required_and_keyword(*array1, *empty_array, **hash1#{block})")
+
+ check_allocations(1, 0, "required_and_keyword(*array1, *empty_array#{block})")
+ check_allocations(1, 0, "required_and_keyword(*array1, *empty_array, **empty_hash#{block})")
+
+ check_allocations(1, 1, "required_and_keyword(*array1, *empty_array, a: 2, **empty_hash#{block})")
+ check_allocations(1, 1, "required_and_keyword(*array1, *empty_array, **hash1, **empty_hash#{block})")
+
+ # Currently allocates 1 array unnecessarily due to splatarray true
+ check_allocations(1, 1, "required_and_keyword(1, *empty_array, a: 2, **empty_hash#{block})")
+ check_allocations(1, 1, "required_and_keyword(1, *empty_array, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "required_and_keyword(*array1, **empty_hash, a: 2#{block})")
+ check_allocations(1, 1, "required_and_keyword(*array1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 0, "required_and_keyword(*array1, **nil#{block})")
+ RUBY
+ end
+
+ def test_positional_splat_and_keyword_parameter
+ check_allocations(<<~RUBY)
+ def self.splat_and_keyword(*b, a: nil#{block}); end
+
+ check_allocations(1, 0, "splat_and_keyword(1, a: 2#{block})")
+ check_allocations(1, 0, "splat_and_keyword(1, *empty_array, a: 2#{block})")
+ check_allocations(1, 1, "splat_and_keyword(1, a:2, **empty_hash#{block})")
+ check_allocations(1, 1, "splat_and_keyword(1, **empty_hash, a: 2#{block})")
+
+ check_allocations(1, 0, "splat_and_keyword(1, **nil#{block})")
+ check_allocations(1, 0, "splat_and_keyword(1, **empty_hash#{block})")
+ check_allocations(1, 0, "splat_and_keyword(1, **hash1#{block})")
+ check_allocations(1, 0, "splat_and_keyword(1, *empty_array, **hash1#{block})")
+ check_allocations(1, 1, "splat_and_keyword(1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "splat_and_keyword(1, **empty_hash, **hash1#{block})")
+
+ check_allocations(1, 0, "splat_and_keyword(1, *empty_array#{block})")
+ check_allocations(1, 1, "splat_and_keyword(1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 0, "splat_and_keyword(1, *empty_array, *empty_array, **empty_hash#{block})")
+
+ check_allocations(1, 0, "splat_and_keyword(*array1, a: 2#{block})")
+
+ check_allocations(1, 0, "splat_and_keyword(*array1, **nill#{block})")
+ check_allocations(1, 0, "splat_and_keyword(*array1, **empty_hash#{block})")
+ check_allocations(1, 0, "splat_and_keyword(*array1, **hash1#{block})")
+ check_allocations(1, 0, "splat_and_keyword(*array1, *empty_array, **hash1#{block})")
+
+ check_allocations(1, 0, "splat_and_keyword(*array1, *empty_array#{block})")
+ check_allocations(1, 0, "splat_and_keyword(*array1, *empty_array, **empty_hash#{block})")
+
+ check_allocations(1, 1, "splat_and_keyword(*array1, *empty_array, a: 2, **empty_hash#{block})")
+ check_allocations(1, 1, "splat_and_keyword(*array1, *empty_array, **hash1, **empty_hash#{block})")
+
+ check_allocations(1, 1, "splat_and_keyword(1, *empty_array, a: 2, **empty_hash#{block})")
+ check_allocations(1, 1, "splat_and_keyword(1, *empty_array, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "splat_and_keyword(*array1, **empty_hash, a: 2#{block})")
+ check_allocations(1, 1, "splat_and_keyword(*array1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 0, "splat_and_keyword(*array1, **nil#{block})")
+ RUBY
+ end
+
+ def test_required_and_keyword_splat_parameter
+ check_allocations(<<~RUBY)
+ def self.required_and_keyword_splat(b, **kw#{block}); end
+
+ check_allocations(0, 1, "required_and_keyword_splat(1, a: 2#{block})")
+ check_allocations(0, 1, "required_and_keyword_splat(1, *empty_array, a: 2#{block})")
+ check_allocations(0, 1, "required_and_keyword_splat(1, a:2, **empty_hash#{block})")
+ check_allocations(0, 1, "required_and_keyword_splat(1, **empty_hash, a: 2#{block})")
+
+ check_allocations(0, 1, "required_and_keyword_splat(1, **nil#{block})")
+ check_allocations(0, 1, "required_and_keyword_splat(1, **empty_hash#{block})")
+ check_allocations(0, 1, "required_and_keyword_splat(1, **hash1#{block})")
+ check_allocations(0, 1, "required_and_keyword_splat(1, *empty_array, **hash1#{block})")
+ check_allocations(0, 1, "required_and_keyword_splat(1, **hash1, **empty_hash#{block})")
+ check_allocations(0, 1, "required_and_keyword_splat(1, **empty_hash, **hash1#{block})")
+
+ check_allocations(0, 1, "required_and_keyword_splat(1, *empty_array#{block})")
+ check_allocations(0, 1, "required_and_keyword_splat(1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "required_and_keyword_splat(1, *empty_array, *empty_array, **empty_hash#{block})")
+
+ check_allocations(0, 1, "required_and_keyword_splat(*array1, a: 2#{block})")
+
+ check_allocations(0, 1, "required_and_keyword_splat(*array1, **nill#{block})")
+ check_allocations(0, 1, "required_and_keyword_splat(*array1, **empty_hash#{block})")
+ check_allocations(0, 1, "required_and_keyword_splat(*array1, **hash1#{block})")
+ check_allocations(1, 1, "required_and_keyword_splat(*array1, *empty_array, **hash1#{block})")
+
+ check_allocations(1, 1, "required_and_keyword_splat(*array1, *empty_array#{block})")
+ check_allocations(1, 1, "required_and_keyword_splat(*array1, *empty_array, **empty_hash#{block})")
+
+ check_allocations(1, 1, "required_and_keyword_splat(*array1, *empty_array, a: 2, **empty_hash#{block})")
+ check_allocations(1, 1, "required_and_keyword_splat(*array1, *empty_array, **hash1, **empty_hash#{block})")
+
+ # Currently allocates 1 array unnecessarily due to splatarray true
+ check_allocations(1, 1, "required_and_keyword_splat(1, *empty_array, a: 2, **empty_hash#{block})")
+ check_allocations(1, 1, "required_and_keyword_splat(1, *empty_array, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "required_and_keyword_splat(*array1, **empty_hash, a: 2#{block})")
+ check_allocations(1, 1, "required_and_keyword_splat(*array1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "required_and_keyword_splat(*array1, **nil#{block})")
+ RUBY
+ end
+
+ def test_positional_splat_and_keyword_splat_parameter
+ check_allocations(<<~RUBY)
+ def self.splat_and_keyword_splat(*b, **kw#{block}); end
+
+ check_allocations(1, 1, "splat_and_keyword_splat(1, a: 2#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(1, *empty_array, a: 2#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(1, a:2, **empty_hash#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(1, **empty_hash, a: 2#{block})")
+
+ check_allocations(1, 1, "splat_and_keyword_splat(1, **nil#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(1, **empty_hash#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(1, **hash1#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(1, *empty_array, **hash1#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(1, **empty_hash, **hash1#{block})")
+
+ check_allocations(1, 1, "splat_and_keyword_splat(1, *empty_array#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(1, *empty_array, *empty_array, **empty_hash#{block})")
+
+ check_allocations(1, 1, "splat_and_keyword_splat(*array1, a: 2#{block})")
+
+ check_allocations(1, 1, "splat_and_keyword_splat(*array1, **nill#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(*array1, **empty_hash#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(*array1, **hash1#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(*array1, *empty_array, **hash1#{block})")
+
+ check_allocations(1, 1, "splat_and_keyword_splat(*array1, *empty_array#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(*array1, *empty_array, **empty_hash#{block})")
+
+ check_allocations(1, 1, "splat_and_keyword_splat(*array1, *empty_array, a: 2, **empty_hash#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(*array1, *empty_array, **hash1, **empty_hash#{block})")
+
+ check_allocations(1, 1, "splat_and_keyword_splat(1, *empty_array, a: 2, **empty_hash#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(1, *empty_array, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(*array1, **empty_hash, a: 2#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(*array1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(*array1, **nil#{block})")
+ RUBY
+ end
+
+ def test_anonymous_splat_and_anonymous_keyword_splat_parameters
+ check_allocations(<<~RUBY)
+ def self.anon_splat_and_anon_keyword_splat(*, **#{block}); end
+
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, a: 2#{block})")
+ check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, *empty_array, a: 2#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, a:2, **empty_hash#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, **empty_hash, a: 2#{block})")
+
+ check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, **nil#{block})")
+ check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, **empty_hash#{block})")
+ check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, **hash1#{block})")
+ check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, *empty_array, **hash1#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, **empty_hash, **hash1#{block})")
+
+ check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, *empty_array#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, *empty_array, *empty_array, **empty_hash#{block})")
+
+ check_allocations(0, 0, "anon_splat_and_anon_keyword_splat(*array1, a: 2#{block})")
+
+ check_allocations(0, 0, "anon_splat_and_anon_keyword_splat(*array1, **nill#{block})")
+ check_allocations(0, 0, "anon_splat_and_anon_keyword_splat(*array1, **empty_hash#{block})")
+ check_allocations(0, 0, "anon_splat_and_anon_keyword_splat(*array1, **hash1#{block})")
+ check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(*array1, *empty_array, **hash1#{block})")
+
+ check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(*array1, *empty_array#{block})")
+ check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(*array1, *empty_array, **empty_hash#{block})")
+
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, *empty_array, a: 2, **empty_hash#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, *empty_array, **hash1, **empty_hash#{block})")
+
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, *empty_array, a: 2, **empty_hash#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, *empty_array, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, **empty_hash, a: 2#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(*array1, **nil#{block})")
+ RUBY
+ end
+
+ def test_nested_anonymous_splat_and_anonymous_keyword_splat_parameters
+ check_allocations(<<~RUBY)
+ def self.anon_splat_and_anon_keyword_splat(*, **#{block}); t(*, **) end; def self.t(*, **#{block}); end
+
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, a: 2#{block})")
+ check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, *empty_array, a: 2#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, a:2, **empty_hash#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, **empty_hash, a: 2#{block})")
+
+ check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, **nil#{block})")
+ check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, **empty_hash#{block})")
+ check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, **hash1#{block})")
+ check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, *empty_array, **hash1#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, **empty_hash, **hash1#{block})")
+
+ check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, *empty_array#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, *empty_array, *empty_array, **empty_hash#{block})")
+
+ check_allocations(0, 0, "anon_splat_and_anon_keyword_splat(*array1, a: 2#{block})")
+
+ check_allocations(0, 0, "anon_splat_and_anon_keyword_splat(*array1, **nill#{block})")
+ check_allocations(0, 0, "anon_splat_and_anon_keyword_splat(*array1, **empty_hash#{block})")
+ check_allocations(0, 0, "anon_splat_and_anon_keyword_splat(*array1, **hash1#{block})")
+ check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(*array1, *empty_array, **hash1#{block})")
+
+ check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(*array1, *empty_array#{block})")
+ check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(*array1, *empty_array, **empty_hash#{block})")
+
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, *empty_array, a: 2, **empty_hash#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, *empty_array, **hash1, **empty_hash#{block})")
+
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, *empty_array, a: 2, **empty_hash#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, *empty_array, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, **empty_hash, a: 2#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(*array1, **nil#{block})")
+ RUBY
+ end
+
+ def test_argument_forwarding
+ check_allocations(<<~RUBY)
+ def self.argument_forwarding(...); end
+
+ check_allocations(1, 1, "argument_forwarding(1, a: 2#{block})")
+ check_allocations(1, 0, "argument_forwarding(1, *empty_array, a: 2#{block})")
+ check_allocations(1, 1, "argument_forwarding(1, a:2, **empty_hash#{block})")
+ check_allocations(1, 1, "argument_forwarding(1, **empty_hash, a: 2#{block})")
+
+ check_allocations(1, 0, "argument_forwarding(1, **nil#{block})")
+ check_allocations(1, 0, "argument_forwarding(1, **empty_hash#{block})")
+ check_allocations(1, 0, "argument_forwarding(1, **hash1#{block})")
+ check_allocations(1, 0, "argument_forwarding(1, *empty_array, **hash1#{block})")
+ check_allocations(1, 1, "argument_forwarding(1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "argument_forwarding(1, **empty_hash, **hash1#{block})")
+
+ check_allocations(1, 0, "argument_forwarding(1, *empty_array#{block})")
+ check_allocations(1, 1, "argument_forwarding(1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 0, "argument_forwarding(1, *empty_array, *empty_array, **empty_hash#{block})")
+
+ check_allocations(0, 0, "argument_forwarding(*array1, a: 2#{block})")
+
+ check_allocations(0, 0, "argument_forwarding(*array1, **nill#{block})")
+ check_allocations(0, 0, "argument_forwarding(*array1, **empty_hash#{block})")
+ check_allocations(0, 0, "argument_forwarding(*array1, **hash1#{block})")
+ check_allocations(1, 0, "argument_forwarding(*array1, *empty_array, **hash1#{block})")
+
+ check_allocations(1, 0, "argument_forwarding(*array1, *empty_array#{block})")
+ check_allocations(1, 0, "argument_forwarding(*array1, *empty_array, **empty_hash#{block})")
+
+ check_allocations(1, 1, "argument_forwarding(*array1, *empty_array, a: 2, **empty_hash#{block})")
+ check_allocations(1, 1, "argument_forwarding(*array1, *empty_array, **hash1, **empty_hash#{block})")
+
+ check_allocations(1, 1, "argument_forwarding(1, *empty_array, a: 2, **empty_hash#{block})")
+ check_allocations(1, 1, "argument_forwarding(1, *empty_array, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "argument_forwarding(*array1, **empty_hash, a: 2#{block})")
+ check_allocations(1, 1, "argument_forwarding(*array1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 0, "argument_forwarding(*array1, **nil#{block})")
+ RUBY
+ end
+
+ def test_nested_argument_forwarding
+ check_allocations(<<~RUBY)
+ def self.argument_forwarding(...); t(...) end; def self.t(...) end
+
+ check_allocations(1, 1, "argument_forwarding(1, a: 2#{block})")
+ check_allocations(1, 0, "argument_forwarding(1, *empty_array, a: 2#{block})")
+ check_allocations(1, 1, "argument_forwarding(1, a:2, **empty_hash#{block})")
+ check_allocations(1, 1, "argument_forwarding(1, **empty_hash, a: 2#{block})")
+
+ check_allocations(1, 0, "argument_forwarding(1, **nil#{block})")
+ check_allocations(1, 0, "argument_forwarding(1, **empty_hash#{block})")
+ check_allocations(1, 0, "argument_forwarding(1, **hash1#{block})")
+ check_allocations(1, 0, "argument_forwarding(1, *empty_array, **hash1#{block})")
+ check_allocations(1, 1, "argument_forwarding(1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "argument_forwarding(1, **empty_hash, **hash1#{block})")
+
+ check_allocations(1, 0, "argument_forwarding(1, *empty_array#{block})")
+ check_allocations(1, 1, "argument_forwarding(1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 0, "argument_forwarding(1, *empty_array, *empty_array, **empty_hash#{block})")
+
+ check_allocations(0, 0, "argument_forwarding(*array1, a: 2#{block})")
+
+ check_allocations(0, 0, "argument_forwarding(*array1, **nill#{block})")
+ check_allocations(0, 0, "argument_forwarding(*array1, **empty_hash#{block})")
+ check_allocations(0, 0, "argument_forwarding(*array1, **hash1#{block})")
+ check_allocations(1, 0, "argument_forwarding(*array1, *empty_array, **hash1#{block})")
+
+ check_allocations(1, 0, "argument_forwarding(*array1, *empty_array#{block})")
+ check_allocations(1, 0, "argument_forwarding(*array1, *empty_array, **empty_hash#{block})")
+
+ check_allocations(1, 1, "argument_forwarding(*array1, *empty_array, a: 2, **empty_hash#{block})")
+ check_allocations(1, 1, "argument_forwarding(*array1, *empty_array, **hash1, **empty_hash#{block})")
+
+ check_allocations(1, 1, "argument_forwarding(1, *empty_array, a: 2, **empty_hash#{block})")
+ check_allocations(1, 1, "argument_forwarding(1, *empty_array, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "argument_forwarding(*array1, **empty_hash, a: 2#{block})")
+ check_allocations(1, 1, "argument_forwarding(*array1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 0, "argument_forwarding(*array1, **nil#{block})")
+ RUBY
+ end
+
+ class WithBlock < self
+ def block
+ ', &block'
+ end
+ end
+ end
+end
diff --git a/test/ruby/test_argf.rb b/test/ruby/test_argf.rb
index 12f7d6485a..55a06296aa 100644
--- a/test/ruby/test_argf.rb
+++ b/test/ruby/test_argf.rb
@@ -9,39 +9,23 @@ class TestArgf < Test::Unit::TestCase
def setup
@tmpdir = Dir.mktmpdir
@tmp_count = 0
- @t1 = make_tempfile0("argf-foo")
- @t1.binmode
- @t1.puts "1"
- @t1.puts "2"
- @t1.close
- @t2 = make_tempfile0("argf-bar")
- @t2.binmode
- @t2.puts "3"
- @t2.puts "4"
- @t2.close
- @t3 = make_tempfile0("argf-baz")
- @t3.binmode
- @t3.puts "5"
- @t3.puts "6"
- @t3.close
+ @t1 = make_tempfile("argf-foo", %w"1 2", binmode: true)
+ @t2 = make_tempfile("argf-bar", %w"3 4", binmode: true)
+ @t3 = make_tempfile("argf-baz", %w"5 6", binmode: true)
end
def teardown
FileUtils.rmtree(@tmpdir)
end
- def make_tempfile0(basename)
+ def make_tempfile(basename = "argf-qux", data = %w[foo bar baz], binmode: false)
@tmp_count += 1
- open("#{@tmpdir}/#{basename}-#{@tmp_count}", "w")
- end
-
- def make_tempfile(basename = "argf-qux")
- t = make_tempfile0(basename)
- t.puts "foo"
- t.puts "bar"
- t.puts "baz"
- t.close
- t
+ path = "#{@tmpdir}/#{basename}-#{@tmp_count}"
+ File.open(path, "w") do |f|
+ f.binmode if binmode
+ f.puts(*data)
+ f
+ end
end
def ruby(*args, external_encoding: Encoding::UTF_8)
@@ -571,15 +555,11 @@ class TestArgf < Test::Unit::TestCase
end
end
- t1 = open("#{@tmpdir}/argf-hoge", "w")
- t1.binmode
- t1.puts "foo"
- t1.close
- t2 = open("#{@tmpdir}/argf-moge", "w")
- t2.binmode
- t2.puts "bar"
- t2.close
- ruby('-e', 'STDERR.reopen(STDOUT); ARGF.gets; ARGF.skip; p ARGF.eof?', t1.path, t2.path) do |f|
+ t1 = "#{@tmpdir}/argf-hoge"
+ t2 = "#{@tmpdir}/argf-moge"
+ File.binwrite(t1, "foo\n")
+ File.binwrite(t2, "bar\n")
+ ruby('-e', 'STDERR.reopen(STDOUT); ARGF.gets; ARGF.skip; p ARGF.eof?', t1, t2) do |f|
assert_equal(%w(false), f.read.split(/\n/))
end
end
@@ -593,7 +573,7 @@ class TestArgf < Test::Unit::TestCase
def test_read2
ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
{#
- s = ""
+ s = +""
ARGF.read(8, s)
p s
};
@@ -604,7 +584,7 @@ class TestArgf < Test::Unit::TestCase
def test_read2_with_not_empty_buffer
ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
{#
- s = "0123456789"
+ s = +"0123456789"
ARGF.read(8, s)
p s
};
@@ -617,7 +597,7 @@ class TestArgf < Test::Unit::TestCase
{#
nil while ARGF.gets
p ARGF.read
- p ARGF.read(0, "")
+ p ARGF.read(0, +"")
};
assert_equal("nil\n\"\"\n", f.read)
end
@@ -626,13 +606,13 @@ class TestArgf < Test::Unit::TestCase
def test_readpartial
ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
{#
- s = ""
+ s = +""
begin
loop do
s << ARGF.readpartial(1)
- t = ""; ARGF.readpartial(1, t); s << t
+ t = +""; ARGF.readpartial(1, t); s << t
# not empty buffer
- u = "abcdef"; ARGF.readpartial(1, u); s << u
+ u = +"abcdef"; ARGF.readpartial(1, u); s << u
end
rescue EOFError
puts s
@@ -645,11 +625,11 @@ class TestArgf < Test::Unit::TestCase
def test_readpartial2
ruby('-e', "#{<<~"{#"}\n#{<<~'};'}") do |f|
{#
- s = ""
+ s = +""
begin
loop do
s << ARGF.readpartial(1)
- t = ""; ARGF.readpartial(1, t); s << t
+ t = +""; ARGF.readpartial(1, t); s << t
end
rescue EOFError
$stdout.binmode
@@ -680,7 +660,7 @@ class TestArgf < Test::Unit::TestCase
def test_getc
ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
{#
- s = ""
+ s = +""
while c = ARGF.getc
s << c
end
@@ -706,7 +686,7 @@ class TestArgf < Test::Unit::TestCase
def test_readchar
ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
{#
- s = ""
+ s = +""
begin
while c = ARGF.readchar
s << c
@@ -784,7 +764,7 @@ class TestArgf < Test::Unit::TestCase
def test_each_char
ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
{#
- s = ""
+ s = +""
ARGF.each_char {|c| s << c }
puts s
};
@@ -854,7 +834,7 @@ class TestArgf < Test::Unit::TestCase
def test_binmode
bug5268 = '[ruby-core:39234]'
- open(@t3.path, "wb") {|f| f.write "5\r\n6\r\n"}
+ File.binwrite(@t3.path, "5\r\n6\r\n")
ruby('-e', "ARGF.binmode; STDOUT.binmode; puts ARGF.read", @t1.path, @t2.path, @t3.path) do |f|
f.binmode
assert_equal("1\n2\n3\n4\n5\r\n6\r\n", f.read, bug5268)
@@ -863,7 +843,7 @@ class TestArgf < Test::Unit::TestCase
def test_textmode
bug5268 = '[ruby-core:39234]'
- open(@t3.path, "wb") {|f| f.write "5\r\n6\r\n"}
+ File.binwrite(@t3.path, "5\r\n6\r\n")
ruby('-e', "STDOUT.binmode; puts ARGF.read", @t1.path, @t2.path, @t3.path) do |f|
f.binmode
assert_equal("1\n2\n3\n4\n5\n6\n", f.read, bug5268)
@@ -1073,7 +1053,7 @@ class TestArgf < Test::Unit::TestCase
ruby('-e', "#{<<~"{#"}\n#{<<~'};'}") do |f|
{#
$stdout.sync = true
- :wait_readable == ARGF.read_nonblock(1, "", exception: false) or
+ :wait_readable == ARGF.read_nonblock(1, +"", exception: false) or
abort "did not return :wait_readable"
begin
@@ -1086,7 +1066,7 @@ class TestArgf < Test::Unit::TestCase
IO.select([ARGF]) == [[ARGF], [], []] or
abort 'did not awaken for readability (before byte)'
- buf = ''
+ buf = +''
buf.object_id == ARGF.read_nonblock(1, buf).object_id or
abort "read destination buffer failed"
print buf
@@ -1140,4 +1120,34 @@ class TestArgf < Test::Unit::TestCase
argf.close
end
end
+
+ def test_putc
+ t = make_tempfile("argf-#{__method__}", 'bar')
+ ruby('-pi-', '-e', "print ARGF.putc('x')", t.path) do |f|
+ end
+ assert_equal("xxbar\n", File.read(t.path))
+ end
+
+ def test_puts
+ t = make_tempfile("argf-#{__method__}", 'bar')
+ err = "#{@tmpdir}/errout"
+ ruby('-pi-', '-W2', '-e', "print ARGF.puts('foo')", t.path, {err: err}) do |f|
+ end
+ assert_equal("foo\nbar\n", File.read(t.path))
+ assert_empty File.read(err)
+ end
+
+ def test_print
+ t = make_tempfile("argf-#{__method__}", 'bar')
+ ruby('-pi-', '-e', "print ARGF.print('foo')", t.path) do |f|
+ end
+ assert_equal("foobar\n", File.read(t.path))
+ end
+
+ def test_printf
+ t = make_tempfile("argf-#{__method__}", 'bar')
+ ruby('-pi-', '-e', "print ARGF.printf('%s', 'foo')", t.path) do |f|
+ end
+ assert_equal("foobar\n", File.read(t.path))
+ end
end
diff --git a/test/ruby/test_array.rb b/test/ruby/test_array.rb
index f58f8a2778..9560fca958 100644
--- a/test/ruby/test_array.rb
+++ b/test/ruby/test_array.rb
@@ -529,14 +529,19 @@ class TestArray < Test::Unit::TestCase
end
def test_assoc
+ def (a4 = Object.new).to_ary
+ %w( pork porcine )
+ end
+
a1 = @cls[*%w( cat feline )]
a2 = @cls[*%w( dog canine )]
a3 = @cls[*%w( mule asinine )]
- a = @cls[ a1, a2, a3 ]
+ a = @cls[ a1, a2, a3, a4 ]
assert_equal(a1, a.assoc('cat'))
assert_equal(a3, a.assoc('mule'))
+ assert_equal(%w( pork porcine ), a.assoc("pork"))
assert_equal(nil, a.assoc('asinine'))
assert_equal(nil, a.assoc('wombat'))
assert_equal(nil, a.assoc(1..2))
@@ -1329,13 +1334,17 @@ class TestArray < Test::Unit::TestCase
end
def test_rassoc
+ def (a4 = Object.new).to_ary
+ %w( pork porcine )
+ end
a1 = @cls[*%w( cat feline )]
a2 = @cls[*%w( dog canine )]
a3 = @cls[*%w( mule asinine )]
- a = @cls[ a1, a2, a3 ]
+ a = @cls[ a1, a2, a3, a4 ]
assert_equal(a1, a.rassoc('feline'))
assert_equal(a3, a.rassoc('asinine'))
+ assert_equal(%w( pork porcine ), a.rassoc("porcine"))
assert_equal(nil, a.rassoc('dog'))
assert_equal(nil, a.rassoc('mule'))
assert_equal(nil, a.rassoc(1..2))
@@ -1693,6 +1702,15 @@ class TestArray < Test::Unit::TestCase
assert_equal([100], a.slice(-1, 1_000_000_000))
end
+ def test_slice_gc_compact_stress
+ omit "compaction doesn't work well on s390x" if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077
+ EnvUtil.under_gc_compact_stress { assert_equal([1, 2, 3, 4, 5], (0..10).to_a[1, 5]) }
+ EnvUtil.under_gc_compact_stress do
+ a = [0, 1, 2, 3, 4, 5]
+ assert_equal([2, 1, 0], a.slice((2..).step(-1)))
+ end
+ end
+
def test_slice!
a = @cls[1, 2, 3, 4, 5]
assert_equal(3, a.slice!(2))
@@ -3336,6 +3354,8 @@ class TestArray < Test::Unit::TestCase
assert_equal(nil, a.bsearch {|x| 1 * (2**100) })
assert_equal(nil, a.bsearch {|x| (-1) * (2**100) })
+ assert_equal(4, a.bsearch {|x| (4 - x).to_r })
+
assert_include([4, 7], a.bsearch {|x| (2**100).coerce((1 - x / 4) * (2**100)).first })
end
@@ -3371,6 +3391,8 @@ class TestArray < Test::Unit::TestCase
assert_equal(nil, a.bsearch_index {|x| 1 * (2**100) })
assert_equal(nil, a.bsearch_index {|x| (-1) * (2**100) })
+ assert_equal(1, a.bsearch_index {|x| (4 - x).to_r })
+
assert_include([1, 2], a.bsearch_index {|x| (2**100).coerce((1 - x / 4) * (2**100)).first })
end
@@ -3534,11 +3556,21 @@ class TestArray < Test::Unit::TestCase
assert_equal(10000, eval(lit).size)
end
+ def test_array_safely_modified_by_sort_block
+ var_0 = (1..70).to_a
+ var_0.sort! do |var_0_block_129, var_1_block_129|
+ var_0.pop
+ var_1_block_129 <=> var_0_block_129
+ end.shift(3)
+ assert_equal((1..67).to_a.reverse, var_0)
+ end
+
private
def need_continuation
unless respond_to?(:callcc, true)
EnvUtil.suppress_warning {require 'continuation'}
end
+ omit 'requires callcc support' unless respond_to?(:callcc, true)
end
end
diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb
index 8b565f531d..29da607fc5 100644
--- a/test/ruby/test_ast.rb
+++ b/test/ruby/test_ast.rb
@@ -214,6 +214,128 @@ class TestAst < Test::Unit::TestCase
end
end
+ def assert_parse(code, warning: '')
+ node = assert_warning(warning) {RubyVM::AbstractSyntaxTree.parse(code)}
+ assert_kind_of(RubyVM::AbstractSyntaxTree::Node, node, code)
+ end
+
+ def assert_invalid_parse(msg, code)
+ assert_raise_with_message(SyntaxError, msg, code) do
+ RubyVM::AbstractSyntaxTree.parse(code)
+ end
+ end
+
+ def test_invalid_exit
+ [
+ "break",
+ "break true",
+ "next",
+ "next true",
+ "redo",
+ ].each do |code, *args|
+ msg = /Invalid #{code[/\A\w+/]}/
+ assert_parse("while false; #{code}; end")
+ assert_parse("until true; #{code}; end")
+ assert_parse("begin #{code}; end while false")
+ assert_parse("begin #{code}; end until true")
+ assert_parse("->{#{code}}")
+ assert_parse("->{class X; #{code}; end}")
+ assert_invalid_parse(msg, "#{code}")
+ assert_invalid_parse(msg, "def m; #{code}; end")
+ assert_invalid_parse(msg, "begin; #{code}; end")
+ assert_parse("END {#{code}}")
+
+ assert_parse("!defined?(#{code})")
+ assert_parse("def m; defined?(#{code}); end")
+ assert_parse("!begin; defined?(#{code}); end")
+
+ next if code.include?(" ")
+ assert_parse("!defined? #{code}")
+ assert_parse("def m; defined? #{code}; end")
+ assert_parse("!begin; defined? #{code}; end")
+ end
+ end
+
+ def test_invalid_retry
+ msg = /Invalid retry/
+ assert_invalid_parse(msg, "retry")
+ assert_invalid_parse(msg, "def m; retry; end")
+ assert_invalid_parse(msg, "begin retry; end")
+ assert_parse("begin rescue; retry; end")
+ assert_invalid_parse(msg, "begin rescue; else; retry; end")
+ assert_invalid_parse(msg, "begin rescue; ensure; retry; end")
+ assert_parse("nil rescue retry")
+ assert_invalid_parse(msg, "END {retry}")
+ assert_invalid_parse(msg, "begin rescue; END {retry}; end")
+
+ assert_parse("!defined?(retry)")
+ assert_parse("def m; defined?(retry); end")
+ assert_parse("!begin defined?(retry); end")
+ assert_parse("begin rescue; else; defined?(retry); end")
+ assert_parse("begin rescue; ensure; defined?(retry); end")
+ assert_parse("END {defined?(retry)}")
+ assert_parse("begin rescue; END {defined?(retry)}; end")
+ assert_parse("!defined? retry")
+
+ assert_parse("def m; defined? retry; end")
+ assert_parse("!begin defined? retry; end")
+ assert_parse("begin rescue; else; defined? retry; end")
+ assert_parse("begin rescue; ensure; defined? retry; end")
+ assert_parse("END {defined? retry}")
+ assert_parse("begin rescue; END {defined? retry}; end")
+
+ assert_parse("#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
+ def foo
+ begin
+ yield
+ rescue StandardError => e
+ begin
+ puts "hi"
+ retry
+ rescue
+ retry unless e
+ raise e
+ else
+ retry
+ ensure
+ retry
+ end
+ end
+ end
+ end;
+ end
+
+ def test_invalid_yield
+ msg = /Invalid yield/
+ assert_invalid_parse(msg, "yield")
+ assert_invalid_parse(msg, "class C; yield; end")
+ assert_invalid_parse(msg, "BEGIN {yield}")
+ assert_invalid_parse(msg, "END {yield}")
+ assert_invalid_parse(msg, "-> {yield}")
+
+ assert_invalid_parse(msg, "yield true")
+ assert_invalid_parse(msg, "class C; yield true; end")
+ assert_invalid_parse(msg, "BEGIN {yield true}")
+ assert_invalid_parse(msg, "END {yield true}")
+ assert_invalid_parse(msg, "-> {yield true}")
+
+ assert_parse("!defined?(yield)")
+ assert_parse("class C; defined?(yield); end")
+ assert_parse("BEGIN {defined?(yield)}")
+ assert_parse("END {defined?(yield)}")
+
+ assert_parse("!defined?(yield true)")
+ assert_parse("class C; defined?(yield true); end")
+ assert_parse("BEGIN {defined?(yield true)}")
+ assert_parse("END {defined?(yield true)}")
+
+ assert_parse("!defined? yield")
+ assert_parse("class C; defined? yield; end")
+ assert_parse("BEGIN {defined? yield}")
+ assert_parse("END {defined? yield}")
+ end
+
def test_node_id_for_location
exception = begin
raise
@@ -467,7 +589,7 @@ class TestAst < Test::Unit::TestCase
assert_equal("foo", head)
assert_equal(:EVSTR, body.type)
body, = body.children
- assert_equal(:LIT, body.type)
+ assert_equal(:INTEGER, body.type)
assert_equal([1], body.children)
end
@@ -539,7 +661,7 @@ class TestAst < Test::Unit::TestCase
node ? [node.children[-4], node.children[-2]&.children, node.children[-1]] : []
end
- assert_equal([:*, nil, :&], forwarding.call('...'))
+ assert_equal([:*, [:**], :&], forwarding.call('...'))
end
def test_ranges_numbered_parameter
@@ -624,6 +746,27 @@ dummy
assert_equal("def test_keep_script_lines_for_of\n", node_method.source.lines.first)
end
+ def test_keep_script_lines_for_of_with_existing_SCRIPT_LINES__that_has__FILE__as_a_key
+ # This test confirms that the bug that previously occurred because of
+ # `AbstractSyntaxTree.of`s unnecessary dependence on SCRIPT_LINES__ does not reproduce.
+ # The bug occurred only if SCRIPT_LINES__ included __FILE__ as a key.
+ lines = [
+ "SCRIPT_LINES__ = {__FILE__ => []}",
+ "puts RubyVM::AbstractSyntaxTree.of(->{ 1 + 2 }, keep_script_lines: true).script_lines",
+ "p SCRIPT_LINES__"
+ ]
+ test_stdout = lines + ['{"-e"=>[]}']
+ assert_in_out_err(["-e", lines.join("\n")], "", test_stdout, [])
+ end
+
+ def test_source_with_multibyte_characters
+ ast = RubyVM::AbstractSyntaxTree.parse(%{a("\u00a7");b("\u00a9")}, keep_script_lines: true)
+ a_fcall, b_fcall = ast.children[2].children
+
+ assert_equal(%{a("\u00a7")}, a_fcall.source)
+ assert_equal(%{b("\u00a9")}, b_fcall.source)
+ end
+
def test_keep_tokens_for_parse
node = RubyVM::AbstractSyntaxTree.parse(<<~END, keep_tokens: true)
1.times do
@@ -755,7 +898,7 @@ dummy
begin
a = 1
STR
- (SCOPE@1:0-2:7 tbl: [:a] args: nil body: (LASGN@2:2-2:7 :a (LIT@2:6-2:7 1)))
+ (SCOPE@1:0-2:7 tbl: [:a] args: nil body: (LASGN@2:2-2:7 :a (INTEGER@2:6-2:7 1)))
EXP
end
@@ -768,7 +911,8 @@ dummy
tbl: [:a]
args: nil
body:
- (IF@1:0-2:7 (VCALL@1:3-1:7 :cond) (LASGN@2:2-2:7 :a (LIT@2:6-2:7 1)) nil))
+ (IF@1:0-2:7 (VCALL@1:3-1:7 :cond) (LASGN@2:2-2:7 :a (INTEGER@2:6-2:7 1))
+ nil))
EXP
assert_error_tolerant(<<~STR, <<~EXP)
@@ -780,7 +924,7 @@ dummy
tbl: [:a]
args: nil
body:
- (IF@1:0-3:4 (VCALL@1:3-1:7 :cond) (LASGN@2:2-2:7 :a (LIT@2:6-2:7 1))
+ (IF@1:0-3:4 (VCALL@1:3-1:7 :cond) (LASGN@2:2-2:7 :a (INTEGER@2:6-2:7 1))
(BEGIN@3:4-3:4 nil)))
EXP
end
@@ -794,7 +938,7 @@ dummy
tbl: [:a]
args: nil
body:
- (UNLESS@1:0-2:7 (VCALL@1:7-1:11 :cond) (LASGN@2:2-2:7 :a (LIT@2:6-2:7 1))
+ (UNLESS@1:0-2:7 (VCALL@1:7-1:11 :cond) (LASGN@2:2-2:7 :a (INTEGER@2:6-2:7 1))
nil))
EXP
@@ -807,7 +951,7 @@ dummy
tbl: [:a]
args: nil
body:
- (UNLESS@1:0-3:4 (VCALL@1:7-1:11 :cond) (LASGN@2:2-2:7 :a (LIT@2:6-2:7 1))
+ (UNLESS@1:0-3:4 (VCALL@1:7-1:11 :cond) (LASGN@2:2-2:7 :a (INTEGER@2:6-2:7 1))
(BEGIN@3:4-3:4 nil)))
EXP
end
@@ -846,7 +990,7 @@ dummy
args: nil
body:
(CASE@1:0-2:6 (VCALL@1:5-1:6 :a)
- (WHEN@2:0-2:6 (LIST@2:5-2:6 (LIT@2:5-2:6 1) nil) (BEGIN@2:6-2:6 nil)
+ (WHEN@2:0-2:6 (LIST@2:5-2:6 (INTEGER@2:5-2:6 1) nil) (BEGIN@2:6-2:6 nil)
nil)))
EXP
@@ -863,7 +1007,7 @@ dummy
(WHEN@2:0-2:11
(LIST@2:5-2:11
(OPCALL@2:5-2:11 (VCALL@2:5-2:6 :a) :==
- (LIST@2:10-2:11 (LIT@2:10-2:11 1) nil)) nil)
+ (LIST@2:10-2:11 (INTEGER@2:10-2:11 1) nil)) nil)
(BEGIN@2:11-2:11 nil) nil)))
EXP
@@ -882,7 +1026,7 @@ dummy
const: nil
kw:
(HASH@2:4-2:13
- (LIST@2:4-2:13 (LIT@2:4-2:6 :a) (CONST@2:7-2:13 :String) nil))
+ (LIST@2:4-2:13 (SYM@2:4-2:6 :a) (CONST@2:7-2:13 :String) nil))
kwrest: nil) (BEGIN@2:14-2:14 nil) nil)))
EXP
end
@@ -964,7 +1108,7 @@ dummy
tbl: []
args: nil
body:
- (ITER@1:0-2:3 (FCALL@1:0-1:3 :m (LIST@1:2-1:3 (LIT@1:2-1:3 1) nil))
+ (ITER@1:0-2:3 (FCALL@1:0-1:3 :m (LIST@1:2-1:3 (INTEGER@1:2-1:3 1) nil))
(SCOPE@1:4-2:3 tbl: [] args: nil body: (VCALL@2:2-2:3 :a))))
EXP
end
@@ -1082,6 +1226,18 @@ dummy
assert_equal([[0, :backslash, "\\", [1, 0, 1, 1]]], node.children.last.tokens)
end
+ def test_with_bom
+ assert_error_tolerant("\u{feff}nil", <<~EXP)
+ (SCOPE@1:0-1:3 tbl: [] args: nil body: (NIL@1:0-1:3))
+ EXP
+ end
+
+ def test_unused_block_local_variable
+ assert_warning('') do
+ RubyVM::AbstractSyntaxTree.parse(%{->(; foo) {}})
+ end
+ end
+
def assert_error_tolerant(src, expected, keep_tokens: false)
begin
verbose_bak, $VERBOSE = $VERBOSE, false
diff --git a/test/ruby/test_autoload.rb b/test/ruby/test_autoload.rb
index e475520321..1eb3551e57 100644
--- a/test/ruby/test_autoload.rb
+++ b/test/ruby/test_autoload.rb
@@ -274,6 +274,11 @@ p Foo::Bar
end
def test_bug_13526
+ # Skip this on macOS 10.13 because of the following error:
+ # http://rubyci.s3.amazonaws.com/osx1013/ruby-master/log/20231011T014505Z.fail.html.gz
+ require "rbconfig"
+ omit if RbConfig::CONFIG["target_os"] == "darwin17"
+
script = File.join(__dir__, 'bug-13526.rb')
assert_ruby_status([script], '', '[ruby-core:81016] [Bug #13526]')
end
diff --git a/test/ruby/test_backtrace.rb b/test/ruby/test_backtrace.rb
index bb0562c0bb..fca7b62030 100644
--- a/test/ruby/test_backtrace.rb
+++ b/test/ruby/test_backtrace.rb
@@ -155,6 +155,10 @@ class TestBacktrace < Test::Unit::TestCase
end
def test_each_backtrace_location
+ assert_nil(Thread.each_caller_location {})
+
+ assert_raise(LocalJumpError) {Thread.each_caller_location}
+
i = 0
cl = caller_locations(1, 1)[0]; ecl = Thread.each_caller_location{|x| i+=1; break x if i == 1}
assert_equal(cl.to_s, ecl.to_s)
@@ -181,6 +185,10 @@ class TestBacktrace < Test::Unit::TestCase
assert_raise(StopIteration) {
ecl.next
}
+
+ ary = []
+ cl = caller_locations(1, 2); Thread.each_caller_location(1, 2) {|x| ary << x}
+ assert_equal(cl.map(&:to_s), ary.map(&:to_s))
end
def test_caller_locations_first_label
@@ -215,15 +223,15 @@ class TestBacktrace < Test::Unit::TestCase
@res = caller_locations(2, 1).inspect
end
@line = __LINE__ + 1
- 1.times.map { 1.times.map { foo } }
- assert_equal("[\"#{__FILE__}:#{@line}:in `times'\"]", @res)
+ [1].map.map { [1].map.map { foo } }
+ assert_equal("[\"#{__FILE__}:#{@line}:in 'Array#map'\"]", @res)
end
def test_caller_location_path_cfunc_iseq_no_pc
def self.foo
@res = caller_locations(2, 1)[0].path
end
- 1.times.map { 1.times.map { foo } }
+ [1].map.map { [1].map.map { foo } }
assert_equal(__FILE__, @res)
end
@@ -292,13 +300,13 @@ class TestBacktrace < Test::Unit::TestCase
end
def test_caller_locations_label
- assert_equal("#{__method__}", caller_locations(0, 1)[0].label)
+ assert_equal("TestBacktrace##{__method__}", caller_locations(0, 1)[0].label)
loc, = tap {break caller_locations(0, 1)}
- assert_equal("block in #{__method__}", loc.label)
+ assert_equal("block in TestBacktrace##{__method__}", loc.label)
begin
raise
rescue
- assert_equal("rescue in #{__method__}", caller_locations(0, 1)[0].label)
+ assert_equal("TestBacktrace##{__method__}", caller_locations(0, 1)[0].label)
end
end
@@ -378,17 +386,17 @@ class TestBacktrace < Test::Unit::TestCase
def test_core_backtrace_hash_merge
e = assert_raise(TypeError) do
- {**nil}
+ {**1}
end
assert_not_match(/\Acore#/, e.backtrace_locations[0].base_label)
end
def test_notty_backtrace
- err = ["-:1:in `<main>': unhandled exception"]
+ err = ["-:1:in '<main>': unhandled exception"]
assert_in_out_err([], "raise", [], err)
- err = ["-:2:in `foo': foo! (RuntimeError)",
- "\tfrom -:4:in `<main>'"]
+ err = ["-:2:in 'Object#foo': foo! (RuntimeError)",
+ "\tfrom -:4:in '<main>'"]
assert_in_out_err([], <<-"end;", [], err)
def foo
raise "foo!"
@@ -396,12 +404,11 @@ class TestBacktrace < Test::Unit::TestCase
foo
end;
- err = ["-:7:in `rescue in bar': bar! (RuntimeError)",
- "\tfrom -:4:in `bar'",
- "\tfrom -:9:in `<main>'",
- "-:2:in `foo': foo! (RuntimeError)",
- "\tfrom -:5:in `bar'",
- "\tfrom -:9:in `<main>'"]
+ err = ["-:7:in 'Object#bar': bar! (RuntimeError)",
+ "\tfrom -:9:in '<main>'",
+ "-:2:in 'Object#foo': foo! (RuntimeError)",
+ "\tfrom -:5:in 'Object#bar'",
+ "\tfrom -:9:in '<main>'"]
assert_in_out_err([], <<-"end;", [], err)
def foo
raise "foo!"
@@ -416,7 +423,7 @@ class TestBacktrace < Test::Unit::TestCase
end
def test_caller_to_enum
- err = ["-:3:in `foo': unhandled exception", "\tfrom -:in `each'"]
+ err = ["-:3:in 'Object#foo': unhandled exception", "\tfrom -:in 'Enumerator#each'"]
assert_in_out_err([], <<-"end;", [], err, "[ruby-core:91911]")
def foo
return to_enum(__method__) unless block_given?
@@ -428,4 +435,23 @@ class TestBacktrace < Test::Unit::TestCase
enum.next
end;
end
+
+ def test_no_receiver_for_anonymous_class
+ err = ["-:2:in 'bar': unhandled exception", # Not '#<Class:0xXXX>.bar'
+ "\tfrom -:3:in '<main>'"]
+ assert_in_out_err([], <<-"end;", [], err)
+ foo = Class.new
+ def foo.bar = raise
+ foo.bar
+ end;
+
+ err = ["-:3:in 'baz': unhandled exception", # Not '#<Class:0xXXX>::Bar.baz'
+ "\tfrom -:4:in '<main>'"]
+ assert_in_out_err([], <<-"end;", [], err)
+ foo = Class.new
+ foo::Bar = Class.new
+ def (foo::Bar).baz = raise
+ foo::Bar.baz
+ end;
+ end
end
diff --git a/test/ruby/test_beginendblock.rb b/test/ruby/test_beginendblock.rb
index 2b281645b5..3706efab52 100644
--- a/test/ruby/test_beginendblock.rb
+++ b/test/ruby/test_beginendblock.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: false
require 'test/unit'
+EnvUtil.suppress_warning {require 'continuation'}
class TestBeginEndBlock < Test::Unit::TestCase
DIR = File.dirname(File.expand_path(__FILE__))
@@ -45,9 +46,9 @@ class TestBeginEndBlock < Test::Unit::TestCase
end
def test_endblockwarn_in_eval
- assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", [], ['(eval):2: warning: END in method; use at_exit'])
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", [], ['test.rb:1: warning: END in method; use at_exit'])
begin;
- eval <<-EOE
+ eval <<-EOE, nil, "test.rb", 0
def end2
END {}
end
@@ -67,7 +68,7 @@ class TestBeginEndBlock < Test::Unit::TestCase
bug8501 = '[ruby-core:55365] [Bug #8501]'
args = ['-e', 'o = Object.new; def o.inspect; raise "[Bug #8501]"; end',
'-e', 'at_exit{o.nope}']
- status = assert_in_out_err(args, '', [], /undefined method `nope'/, bug8501)
+ status = assert_in_out_err(args, '', [], /undefined method 'nope'/, bug8501)
assert_not_predicate(status, :success?, bug8501)
end
@@ -131,6 +132,8 @@ class TestBeginEndBlock < Test::Unit::TestCase
end
def test_callcc_at_exit
+ omit 'requires callcc support' unless respond_to?(:callcc)
+
bug9110 = '[ruby-core:58329][Bug #9110]'
assert_ruby_status([], "#{<<~"begin;"}\n#{<<~'end;'}", bug9110)
begin;
diff --git a/test/ruby/test_bignum.rb b/test/ruby/test_bignum.rb
index 065a944853..1f882c6cb9 100644
--- a/test/ruby/test_bignum.rb
+++ b/test/ruby/test_bignum.rb
@@ -207,7 +207,7 @@ class TestBignum < Test::Unit::TestCase
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
begin;
digits = [["3", 700], ["0", 2700], ["1", 1], ["0", 26599]]
- num = digits.inject("") {|s,(c,n)|s << c*n}.to_i
+ num = digits.inject(+"") {|s,(c,n)|s << c*n}.to_i
assert_equal digits.sum {|c,n|n}, num.to_s.size
end;
end
diff --git a/test/ruby/test_call.rb b/test/ruby/test_call.rb
index 05a362e72f..a52b75c267 100644
--- a/test/ruby/test_call.rb
+++ b/test/ruby/test_call.rb
@@ -100,12 +100,20 @@ class TestCall < Test::Unit::TestCase
}
end
+ def test_frozen_splat_and_keywords
+ a = [1, 2].freeze
+ def self.f(*a); a end
+ assert_equal([1, 2, {kw: 3}], f(*a, kw: 3))
+ end
+
def test_call_bmethod_proc
pr = proc{|sym| sym}
define_singleton_method(:a, &pr)
ary = [10]
assert_equal(10, a(*ary))
+ end
+ def test_call_bmethod_proc_restarg
pr = proc{|*sym| sym}
define_singleton_method(:a, &pr)
ary = [10]
@@ -113,6 +121,188 @@ class TestCall < Test::Unit::TestCase
assert_equal([10], a(10))
end
+ def test_call_op_asgn_keywords
+ h = Class.new do
+ attr_reader :get, :set
+ def v; yield; [*@get, *@set] end
+ def [](*a, **b, &c) @get = [a, b, c]; @set = []; 3 end
+ def []=(*a, **b, &c) @set = [a, b, c] end
+ end.new
+
+ a = []
+ kw = {}
+ b = lambda{}
+
+ # Prevent "assigned but unused variable" warnings
+ _ = [h, a, kw, b]
+
+ message = /keyword arg given in index/
+
+ # +=, without block, non-popped
+ assert_syntax_error(%q{h[**kw] += 1}, message)
+ assert_syntax_error(%q{h[0, **kw] += 1}, message)
+ assert_syntax_error(%q{h[0, *a, **kw] += 1}, message)
+ assert_syntax_error(%q{h[kw: 5] += 1}, message)
+ assert_syntax_error(%q{h[kw: 5, a: 2] += 1}, message)
+ assert_syntax_error(%q{h[kw: 5, a: 2] += 1}, message)
+ assert_syntax_error(%q{h[0, kw: 5, a: 2] += 1}, message)
+ assert_syntax_error(%q{h[0, *a, kw: 5, a: 2, nil: 3] += 1}, message)
+
+ # +=, with block, non-popped
+ assert_syntax_error(%q{h[**kw, &b] += 1}, message)
+ assert_syntax_error(%q{h[0, **kw, &b] += 1}, message)
+ assert_syntax_error(%q{h[0, *a, **kw, &b] += 1}, message)
+ assert_syntax_error(%q{h[kw: 5, &b] += 1}, message)
+ assert_syntax_error(%q{h[kw: 5, a: 2, &b] += 1}, message)
+ assert_syntax_error(%q{h[kw: 5, a: 2, &b] += 1}, message)
+ assert_syntax_error(%q{h[0, kw: 5, a: 2, &b] += 1}, message)
+ assert_syntax_error(%q{h[0, *a, kw: 5, a: 2, b: 3, &b] += 1}, message)
+
+ # +=, without block, popped
+ assert_syntax_error(%q{h[**kw] += 1; nil}, message)
+ assert_syntax_error(%q{h[0, **kw] += 1; nil}, message)
+ assert_syntax_error(%q{h[0, *a, **kw] += 1; nil}, message)
+ assert_syntax_error(%q{h[kw: 5] += 1; nil}, message)
+ assert_syntax_error(%q{h[kw: 5, a: 2] += 1; nil}, message)
+ assert_syntax_error(%q{h[kw: 5, a: 2] += 1; nil}, message)
+ assert_syntax_error(%q{h[0, kw: 5, a: 2] += 1; nil}, message)
+ assert_syntax_error(%q{h[0, *a, kw: 5, a: 2, nil: 3] += 1; nil}, message)
+
+ # +=, with block, popped
+ assert_syntax_error(%q{h[**kw, &b] += 1; nil}, message)
+ assert_syntax_error(%q{h[0, **kw, &b] += 1; nil}, message)
+ assert_syntax_error(%q{h[0, *a, **kw, &b] += 1; nil}, message)
+ assert_syntax_error(%q{h[kw: 5, &b] += 1; nil}, message)
+ assert_syntax_error(%q{h[kw: 5, a: 2, &b] += 1; nil}, message)
+ assert_syntax_error(%q{h[kw: 5, a: 2, &b] += 1; nil}, message)
+ assert_syntax_error(%q{h[0, kw: 5, a: 2, &b] += 1; nil}, message)
+ assert_syntax_error(%q{h[0, *a, kw: 5, a: 2, b: 3, &b] += 1; nil}, message)
+
+ # &&=, without block, non-popped
+ assert_syntax_error(%q{h[**kw] &&= 1}, message)
+ assert_syntax_error(%q{h[0, **kw] &&= 1}, message)
+ assert_syntax_error(%q{h[0, *a, **kw] &&= 1}, message)
+ assert_syntax_error(%q{h[kw: 5] &&= 1}, message)
+ assert_syntax_error(%q{h[kw: 5, a: 2] &&= 1}, message)
+ assert_syntax_error(%q{h[kw: 5, a: 2] &&= 1}, message)
+ assert_syntax_error(%q{h[0, kw: 5, a: 2] &&= 1}, message)
+ assert_syntax_error(%q{h[0, *a, kw: 5, a: 2, nil: 3] &&= 1}, message)
+
+ # &&=, with block, non-popped
+ assert_syntax_error(%q{h[**kw, &b] &&= 1}, message)
+ assert_syntax_error(%q{h[0, **kw, &b] &&= 1}, message)
+ assert_syntax_error(%q{h[0, *a, **kw, &b] &&= 1}, message)
+ assert_syntax_error(%q{h[kw: 5, &b] &&= 1}, message)
+ assert_syntax_error(%q{h[kw: 5, a: 2, &b] &&= 1}, message)
+ assert_syntax_error(%q{h[kw: 5, a: 2, &b] &&= 1}, message)
+ assert_syntax_error(%q{h[0, kw: 5, a: 2, &b] &&= 1}, message)
+ assert_syntax_error(%q{h[0, *a, kw: 5, a: 2, b: 3, &b] &&= 1}, message)
+
+ # &&=, without block, popped
+ assert_syntax_error(%q{h[**kw] &&= 1; nil}, message)
+ assert_syntax_error(%q{h[0, **kw] &&= 1; nil}, message)
+ assert_syntax_error(%q{h[0, *a, **kw] &&= 1; nil}, message)
+ assert_syntax_error(%q{h[kw: 5] &&= 1; nil}, message)
+ assert_syntax_error(%q{h[kw: 5, a: 2] &&= 1; nil}, message)
+ assert_syntax_error(%q{h[kw: 5, a: 2] &&= 1; nil}, message)
+ assert_syntax_error(%q{h[0, kw: 5, a: 2] &&= 1; nil}, message)
+ assert_syntax_error(%q{h[0, *a, kw: 5, a: 2, nil: 3] &&= 1; nil}, message)
+
+ # &&=, with block, popped
+ assert_syntax_error(%q{h[**kw, &b] &&= 1; nil}, message)
+ assert_syntax_error(%q{h[0, **kw, &b] &&= 1; nil}, message)
+ assert_syntax_error(%q{h[0, *a, **kw, &b] &&= 1; nil}, message)
+ assert_syntax_error(%q{h[kw: 5, &b] &&= 1; nil}, message)
+ assert_syntax_error(%q{h[kw: 5, a: 2, &b] &&= 1; nil}, message)
+ assert_syntax_error(%q{h[kw: 5, a: 2, &b] &&= 1; nil}, message)
+ assert_syntax_error(%q{h[0, kw: 5, a: 2, &b] &&= 1; nil}, message)
+ assert_syntax_error(%q{h[0, *a, kw: 5, a: 2, b: 3, &b] &&= 1; nil}, message)
+
+ # ||=, without block, non-popped
+ assert_syntax_error(%q{h[**kw] ||= 1}, message)
+ assert_syntax_error(%q{h[0, **kw] ||= 1}, message)
+ assert_syntax_error(%q{h[0, *a, **kw] ||= 1}, message)
+ assert_syntax_error(%q{h[kw: 5] ||= 1}, message)
+ assert_syntax_error(%q{h[kw: 5, a: 2] ||= 1}, message)
+ assert_syntax_error(%q{h[kw: 5, a: 2] ||= 1}, message)
+ assert_syntax_error(%q{h[0, kw: 5, a: 2] ||= 1}, message)
+ assert_syntax_error(%q{h[0, *a, kw: 5, a: 2, nil: 3] ||= 1}, message)
+
+ # ||=, with block, non-popped
+ assert_syntax_error(%q{h[**kw, &b] ||= 1}, message)
+ assert_syntax_error(%q{h[0, **kw, &b] ||= 1}, message)
+ assert_syntax_error(%q{h[0, *a, **kw, &b] ||= 1}, message)
+ assert_syntax_error(%q{h[kw: 5, &b] ||= 1}, message)
+ assert_syntax_error(%q{h[kw: 5, a: 2, &b] ||= 1}, message)
+ assert_syntax_error(%q{h[kw: 5, a: 2, &b] ||= 1}, message)
+ assert_syntax_error(%q{h[0, kw: 5, a: 2, &b] ||= 1}, message)
+ assert_syntax_error(%q{h[0, *a, kw: 5, a: 2, b: 3, &b] ||= 1}, message)
+
+ # ||=, without block, popped
+ assert_syntax_error(%q{h[**kw] ||= 1; nil}, message)
+ assert_syntax_error(%q{h[0, **kw] ||= 1; nil}, message)
+ assert_syntax_error(%q{h[0, *a, **kw] ||= 1; nil}, message)
+ assert_syntax_error(%q{h[kw: 5] ||= 1; nil}, message)
+ assert_syntax_error(%q{h[kw: 5, a: 2] ||= 1; nil}, message)
+ assert_syntax_error(%q{h[kw: 5, a: 2] ||= 1; nil}, message)
+ assert_syntax_error(%q{h[0, kw: 5, a: 2] ||= 1; nil}, message)
+ assert_syntax_error(%q{h[0, *a, kw: 5, a: 2, nil: 3] ||= 1; nil}, message)
+
+ # ||=, with block, popped
+ assert_syntax_error(%q{h[**kw, &b] ||= 1; nil}, message)
+ assert_syntax_error(%q{h[0, **kw, &b] ||= 1; nil}, message)
+ assert_syntax_error(%q{h[0, *a, **kw, &b] ||= 1; nil}, message)
+ assert_syntax_error(%q{h[kw: 5, &b] ||= 1; nil}, message)
+ assert_syntax_error(%q{h[kw: 5, a: 2, &b] ||= 1; nil}, message)
+ assert_syntax_error(%q{h[kw: 5, a: 2, &b] ||= 1; nil}, message)
+ assert_syntax_error(%q{h[0, kw: 5, a: 2, &b] ||= 1; nil}, message)
+ assert_syntax_error(%q{h[0, *a, kw: 5, a: 2, b: 3, &b] ||= 1; nil}, message)
+
+ end
+
+ def test_kwsplat_block_order_op_asgn
+ o = Object.new
+ ary = []
+ o.define_singleton_method(:to_a) {ary << :to_a; []}
+ o.define_singleton_method(:to_hash) {ary << :to_hash; {}}
+ o.define_singleton_method(:to_proc) {ary << :to_proc; lambda{}}
+
+ def o.[](...) 2 end
+ def o.[]=(...) end
+
+ message = /keyword arg given in index/
+
+ assert_syntax_error(%q{o[kw: 1] += 1}, message)
+ assert_syntax_error(%q{o[**o] += 1}, message)
+ assert_syntax_error(%q{o[**o, &o] += 1}, message)
+ assert_syntax_error(%q{o[*o, **o, &o] += 1}, message)
+ end
+
+ def test_call_op_asgn_keywords_mutable
+ h = Class.new do
+ attr_reader :get, :set
+ def v; yield; [*@get, *@set] end
+ def [](*a, **b)
+ @get = [a.dup, b.dup]
+ a << :splat_modified
+ b[:kw_splat_modified] = true
+ @set = []
+ 3
+ end
+ def []=(*a, **b) @set = [a, b] end
+ end.new
+
+ message = /keyword arg given in index/
+
+ a = []
+ kw = {}
+
+ # Prevent "assigned but unused variable" warnings
+ _ = [h, a, kw]
+
+ assert_syntax_error(%q{h[*a, 2, b: 5, **kw] += 1}, message)
+ end
+
def test_call_splat_order
bug12860 = '[ruby-core:77701] [Bug# 12860]'
ary = [1, 2]
@@ -130,6 +320,52 @@ class TestCall < Test::Unit::TestCase
assert_equal([0, 1, 2, b], aaa(0, *ary, &ary.pop), bug16504)
end
+ def test_call_args_splat_with_nonhash_keyword_splat
+ o = Object.new
+ def o.to_hash; {a: 1} end
+ def self.f(*a, **kw)
+ kw
+ end
+ assert_equal Hash, f(*[], **o).class
+ end
+
+ def test_kwsplat_block_order
+ o = Object.new
+ ary = []
+ o.define_singleton_method(:to_a) {ary << :to_a; []}
+ o.define_singleton_method(:to_hash) {ary << :to_hash; {}}
+ o.define_singleton_method(:to_proc) {ary << :to_proc; lambda{}}
+
+ def self.t(...) end
+
+ t(**o, &o)
+ assert_equal([:to_hash, :to_proc], ary)
+
+ ary.clear
+ t(*o, **o, &o)
+ assert_equal([:to_a, :to_hash, :to_proc], ary)
+ end
+
+ def test_kwsplat_block_order_super
+ def self.t(splat)
+ o = Object.new
+ ary = []
+ o.define_singleton_method(:to_a) {ary << :to_a; []}
+ o.define_singleton_method(:to_hash) {ary << :to_hash; {}}
+ o.define_singleton_method(:to_proc) {ary << :to_proc; lambda{}}
+ if splat
+ super(*o, **o, &o)
+ else
+ super(**o, &o)
+ end
+ ary
+ end
+ extend Module.new{def t(...) end}
+
+ assert_equal([:to_hash, :to_proc], t(false))
+ assert_equal([:to_a, :to_hash, :to_proc], t(true))
+ end
+
OVER_STACK_LEN = (ENV['RUBY_OVER_STACK_LEN'] || 150).to_i # Greater than VM_ARGC_STACK_MAX
OVER_STACK_ARGV = OVER_STACK_LEN.times.to_a.freeze
diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb
index 98acbaf67b..710b8a6f7b 100644
--- a/test/ruby/test_class.rb
+++ b/test/ruby/test_class.rb
@@ -96,6 +96,13 @@ class TestClass < Test::Unit::TestCase
def test_superclass_of_basicobject
assert_equal(nil, BasicObject.superclass)
+
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ module Mod end
+ BasicObject.include(Mod)
+ assert_equal(nil, BasicObject.superclass)
+ end;
end
def test_module_function
@@ -782,15 +789,15 @@ class TestClass < Test::Unit::TestCase
c.attached_object
end
- assert_raise_with_message(TypeError, /`NilClass' is not a singleton class/) do
+ assert_raise_with_message(TypeError, /'NilClass' is not a singleton class/) do
nil.singleton_class.attached_object
end
- assert_raise_with_message(TypeError, /`FalseClass' is not a singleton class/) do
+ assert_raise_with_message(TypeError, /'FalseClass' is not a singleton class/) do
false.singleton_class.attached_object
end
- assert_raise_with_message(TypeError, /`TrueClass' is not a singleton class/) do
+ assert_raise_with_message(TypeError, /'TrueClass' is not a singleton class/) do
true.singleton_class.attached_object
end
end
diff --git a/test/ruby/test_clone.rb b/test/ruby/test_clone.rb
index 216eaa39d2..775c9ed848 100644
--- a/test/ruby/test_clone.rb
+++ b/test/ruby/test_clone.rb
@@ -73,6 +73,13 @@ class TestClone < Test::Unit::TestCase
assert_equal(cloned_obj.instance_variable_get(:@a), 1)
end
+ def test_proc_obj_id_flag_reset
+ # [Bug #20250]
+ proc = Proc.new { }
+ proc.object_id
+ proc.clone.object_id # Would crash with RUBY_DEBUG=1
+ end
+
def test_user_flags
assert_separately([], <<-EOS)
#
diff --git a/test/ruby/test_comparable.rb b/test/ruby/test_comparable.rb
index 4a90d443bf..b689469d9e 100644
--- a/test/ruby/test_comparable.rb
+++ b/test/ruby/test_comparable.rb
@@ -85,6 +85,12 @@ class TestComparable < Test::Unit::TestCase
assert_equal(1, @o.clamp(1, 1))
assert_equal(@o, @o.clamp(0, 0))
+ assert_equal(@o, @o.clamp(nil, 2))
+ assert_equal(-2, @o.clamp(nil, -2))
+ assert_equal(@o, @o.clamp(-2, nil))
+ assert_equal(2, @o.clamp(2, nil))
+ assert_equal(@o, @o.clamp(nil, nil))
+
assert_raise_with_message(ArgumentError, 'min argument must be less than or equal to max argument') {
@o.clamp(2, 1)
}
diff --git a/test/ruby/test_compile_prism.rb b/test/ruby/test_compile_prism.rb
new file mode 100644
index 0000000000..d13b150f93
--- /dev/null
+++ b/test/ruby/test_compile_prism.rb
@@ -0,0 +1,2671 @@
+# frozen_string_literal: true
+
+# This file is organized to match itemization in https://github.com/ruby/prism/issues/1335
+module Prism
+ class TestCompilePrism < Test::Unit::TestCase
+ # Subclass is used for tests which need it
+ class Subclass; end
+ ############################################################################
+ # Literals #
+ ############################################################################
+
+ def test_FalseNode
+ assert_prism_eval("false")
+ end
+
+ def test_FloatNode
+ assert_prism_eval("1.2")
+ assert_prism_eval("1.2e3")
+ assert_prism_eval("+1.2e+3")
+ assert_prism_eval("-1.2e-3")
+ end
+
+ def test_ImaginaryNode
+ assert_prism_eval("1i")
+ assert_prism_eval("+1.0i")
+ assert_prism_eval("1ri")
+ end
+
+ def test_IntegerNode
+ assert_prism_eval("1")
+ assert_prism_eval("+1")
+ assert_prism_eval("-1")
+ assert_prism_eval("0x10")
+ assert_prism_eval("0b10")
+ assert_prism_eval("0o10")
+ assert_prism_eval("010")
+ assert_prism_eval("(0o00)")
+ end
+
+ def test_NilNode
+ assert_prism_eval("nil")
+ end
+
+ def test_RationalNode
+ assert_prism_eval("1.2r")
+ assert_prism_eval("+1.2r")
+ end
+
+ def test_SelfNode
+ assert_prism_eval("self")
+ end
+
+ def test_SourceEncodingNode
+ assert_prism_eval("__ENCODING__")
+ end
+
+ def test_SourceFileNode
+ assert_prism_eval("__FILE__")
+ end
+
+ def test_SourceLineNode
+ assert_prism_eval("__LINE__", raw: true)
+ end
+
+ def test_TrueNode
+ assert_prism_eval("true")
+ end
+
+ ############################################################################
+ # Reads #
+ ############################################################################
+
+ def test_BackReferenceReadNode
+ assert_prism_eval("$+")
+ end
+
+ def test_ClassVariableReadNode
+ assert_prism_eval("class Prism::TestCompilePrism; @@pit = 1; @@pit; end")
+ end
+
+ def test_ConstantPathNode
+ assert_prism_eval("Prism::TestCompilePrism")
+ end
+
+ def test_ConstantReadNode
+ assert_prism_eval("Prism")
+ end
+
+ Z = 1
+
+ def test_DefinedNode
+ assert_prism_eval("defined? nil")
+ assert_prism_eval("defined? self")
+ assert_prism_eval("defined? true")
+ assert_prism_eval("defined? false")
+ assert_prism_eval("defined? 1")
+ assert_prism_eval("defined? 1i")
+ assert_prism_eval("defined? 1.0")
+ assert_prism_eval("defined? 1..2")
+ assert_prism_eval("defined? [A, B, C]")
+ assert_prism_eval("defined? [1, 2, 3]")
+ assert_prism_eval("defined?({ a: 1 })")
+ assert_prism_eval("defined? 'str'")
+ assert_prism_eval('defined?("#{expr}")')
+ assert_prism_eval("defined? :sym")
+ assert_prism_eval("defined? /foo/")
+ assert_prism_eval('defined?(/#{1}/)')
+ assert_prism_eval("defined? -> { 1 + 1 }")
+ assert_prism_eval("defined? a && b")
+ assert_prism_eval("defined? a || b")
+ assert_prism_eval("defined? __ENCODING__")
+ assert_prism_eval("defined? __FILE__")
+ assert_prism_eval("defined? __LINE__")
+
+ assert_prism_eval("defined? %[1,2,3]")
+ assert_prism_eval("defined? %q[1,2,3]")
+ assert_prism_eval("defined? %Q[1,2,3]")
+ assert_prism_eval("defined? %r[1,2,3]")
+ assert_prism_eval("defined? %i[1,2,3]")
+ assert_prism_eval("defined? %I[1,2,3]")
+ assert_prism_eval("defined? %w[1,2,3]")
+ assert_prism_eval("defined? %W[1,2,3]")
+ assert_prism_eval("defined? %s[1,2,3]")
+ assert_prism_eval("defined? %x[1,2,3]")
+
+ assert_prism_eval("defined? [*b]")
+ assert_prism_eval("defined? [[*1..2], 3, *4..5]")
+ assert_prism_eval("defined? [a: [:b, :c]]")
+ assert_prism_eval("defined? 1 in 1")
+
+ assert_prism_eval("defined? @a")
+ assert_prism_eval("defined? $a")
+ assert_prism_eval("defined? @@a")
+ assert_prism_eval("defined? A")
+ assert_prism_eval("defined? ::A")
+ assert_prism_eval("defined? A::B")
+ assert_prism_eval("defined? A::B::C")
+ assert_prism_eval("defined? #{self.class.name}::Z::A")
+ assert_prism_eval("defined? yield")
+ assert_prism_eval("defined? super")
+
+ assert_prism_eval("defined? X = 1")
+ assert_prism_eval("defined? X *= 1")
+ assert_prism_eval("defined? X /= 1")
+ assert_prism_eval("defined? X &= 1")
+ assert_prism_eval("defined? X ||= 1")
+
+ assert_prism_eval("defined? $1")
+ assert_prism_eval("defined? $2")
+ assert_prism_eval("defined? $`")
+ assert_prism_eval("defined? $'")
+ assert_prism_eval("defined? $+")
+
+ assert_prism_eval("defined? $X = 1")
+ assert_prism_eval("defined? $X *= 1")
+ assert_prism_eval("defined? $X /= 1")
+ assert_prism_eval("defined? $X &= 1")
+ assert_prism_eval("defined? $X ||= 1")
+
+ assert_prism_eval("defined? @@X = 1")
+ assert_prism_eval("defined? @@X *= 1")
+ assert_prism_eval("defined? @@X /= 1")
+ assert_prism_eval("defined? @@X &= 1")
+ assert_prism_eval("defined? @@X ||= 1")
+
+ assert_prism_eval("defined? @X = 1")
+ assert_prism_eval("defined? @X *= 1")
+ assert_prism_eval("defined? @X /= 1")
+ assert_prism_eval("defined? @X &= 1")
+ assert_prism_eval("defined? @X ||= 1")
+
+ assert_prism_eval("x = 1; defined? x = 1")
+ assert_prism_eval("x = 1; defined? x *= 1")
+ assert_prism_eval("x = 1; defined? x /= 1")
+ assert_prism_eval("x = 1; defined? x &= 1")
+ assert_prism_eval("x = 1; defined? x ||= 1")
+
+ assert_prism_eval("if defined? A; end")
+
+ assert_prism_eval("defined?(())")
+ assert_prism_eval("defined?(('1'))")
+
+ # method chain starting with self that's truthy
+ assert_prism_eval("defined?(self.itself.itself.itself)")
+
+ # method chain starting with self that's false (exception swallowed)
+ assert_prism_eval("defined?(self.itself.itself.neat)")
+
+ # single self with method, truthy
+ assert_prism_eval("defined?(self.itself)")
+
+ # single self with method, false
+ assert_prism_eval("defined?(self.neat!)")
+
+ # method chain implicit self that's truthy
+ assert_prism_eval("defined?(itself.itself.itself)")
+
+ # method chain implicit self that's false
+ assert_prism_eval("defined?(itself.neat.itself)")
+
+ ## single method implicit self that's truthy
+ assert_prism_eval("defined?(itself)")
+
+ ## single method implicit self that's false
+ assert_prism_eval("defined?(neatneat)")
+
+ assert_prism_eval("defined?(a(itself))")
+ assert_prism_eval("defined?(itself(itself))")
+
+ # Method chain on a constant
+ assert_prism_eval(<<~RUBY)
+ class PrismDefinedNode
+ def m1; end
+ end
+
+ defined?(PrismDefinedNode.new.m1)
+ RUBY
+
+ assert_prism_eval("defined?(next)")
+ assert_prism_eval("defined?(break)")
+ assert_prism_eval("defined?(redo)")
+ assert_prism_eval("defined?(retry)")
+
+ assert_prism_eval(<<~RUBY)
+ class PrismDefinedReturnNode
+ def self.m1; defined?(return) end
+ end
+
+ PrismDefinedReturnNode.m1
+ RUBY
+
+ assert_prism_eval("defined?(begin; 1; end)")
+
+ assert_prism_eval("defined?(defined?(a))")
+ assert_prism_eval('defined?(:"#{1}")')
+ assert_prism_eval("defined?(`echo #{1}`)")
+
+ assert_prism_eval("defined?(PrismTestSubclass.test_call_and_write_node &&= 1)")
+ assert_prism_eval("defined?(PrismTestSubclass.test_call_operator_write_node += 1)")
+ assert_prism_eval("defined?(PrismTestSubclass.test_call_or_write_node ||= 1)")
+ assert_prism_eval("defined?(Prism::CPAWN &&= 1)")
+ assert_prism_eval("defined?(Prism::CPOWN += 1)")
+ assert_prism_eval("defined?(Prism::CPOrWN ||= 1)")
+ assert_prism_eval("defined?(Prism::CPWN = 1)")
+ assert_prism_eval("defined?([0][0] &&= 1)")
+ assert_prism_eval("defined?([0][0] += 1)")
+ assert_prism_eval("defined?([0][0] ||= 1)")
+
+ assert_prism_eval("defined?(case :a; when :a; 1; else; 2; end)")
+ assert_prism_eval("defined?(case [1, 2, 3]; in [1, 2, 3]; 4; end)")
+ assert_prism_eval("defined?(class PrismClassA; end)")
+ assert_prism_eval("defined?(def prism_test_def_node; end)")
+ assert_prism_eval("defined?(for i in [1,2] do; i; end)")
+ assert_prism_eval("defined?(if true; 1; end)")
+ assert_prism_eval("defined?(/(?<foo>bar)/ =~ 'barbar')")
+ assert_prism_eval("defined?(1 => 1)")
+ assert_prism_eval("defined?(module M; end)")
+ assert_prism_eval("defined?(1.2r)")
+ assert_prism_eval("defined?(class << self; end)")
+ assert_prism_eval("defined?(while a != 1; end)")
+ assert_prism_eval("defined?(until a == 1; end)")
+ assert_prism_eval("defined?(unless true; 1; end)")
+ end
+
+ def test_GlobalVariableReadNode
+ assert_prism_eval("$pit = 1; $pit")
+ end
+
+ def test_InstanceVariableReadNode
+ assert_prism_eval("class Prism::TestCompilePrism; @pit = 1; @pit; end")
+ end
+
+ def test_LocalVariableReadNode
+ assert_prism_eval("pit = 1; pit")
+ end
+
+ def test_NumberedReferenceReadNode
+ assert_prism_eval("$1")
+ assert_prism_eval("$99999")
+ end
+
+ ############################################################################
+ # Writes #
+ ############################################################################
+
+ def test_ClassVariableAndWriteNode
+ assert_prism_eval("class Prism::TestCompilePrism; @@pit = 0; @@pit &&= 1; end")
+ end
+
+ def test_ClassVariableOperatorWriteNode
+ assert_prism_eval("class Prism::TestCompilePrism; @@pit = 0; @@pit += 1; end")
+ end
+
+ def test_ClassVariableOrWriteNode
+ assert_prism_eval("class Prism::TestCompilePrism; @@pit = 1; @@pit ||= 0; end")
+ assert_prism_eval("class Prism::TestCompilePrism; @@pit = nil; @@pit ||= 1; end")
+ end
+
+ def test_ClassVariableWriteNode
+ assert_prism_eval("class Prism::TestCompilePrism; @@pit = 1; end")
+ end
+
+ def test_ConstantAndWriteNode
+ assert_prism_eval("Constant = 1; Constant &&= 1")
+ end
+
+ def test_ConstantOperatorWriteNode
+ assert_prism_eval("Constant = 1; Constant += 1")
+ end
+
+ def test_ConstantOrWriteNode
+ assert_prism_eval("Constant = 1; Constant ||= 1")
+ end
+
+ def test_ConstantWriteNode
+ # We don't call assert_prism_eval directly in this case because we
+ # don't want to assign the constant multiple times if we run
+ # with `--repeat-count`
+ # Instead, we eval manually here, and remove the constant to
+ constant_name = "YCT"
+ source = "#{constant_name} = 1"
+ prism_eval = RubyVM::InstructionSequence.compile_prism(source).eval
+ assert_equal prism_eval, 1
+ Object.send(:remove_const, constant_name)
+ end
+
+ def test_ConstantPathWriteNode
+ assert_prism_eval("Prism::CPWN = 1")
+ assert_prism_eval("::CPWN = 1")
+ end
+
+ def test_ConstantPathAndWriteNode
+ assert_prism_eval("Prism::CPAWN = 1; Prism::CPAWN &&= 2")
+ assert_prism_eval("Prism::CPAWN &&= 1")
+ assert_prism_eval("::CPAWN = 1; ::CPAWN &&= 2")
+ end
+
+ def test_ConstantPathOrWriteNode
+ assert_prism_eval("Prism::CPOrWN = nil; Prism::CPOrWN ||= 1")
+ assert_prism_eval("Prism::CPOrWN ||= 1")
+ assert_prism_eval("::CPOrWN = nil; ::CPOrWN ||= 1")
+ end
+
+ def test_ConstantPathOperatorWriteNode
+ assert_prism_eval("Prism::CPOWN = 0; Prism::CPOWN += 1")
+ assert_prism_eval("::CPOWN = 0; ::CPOWN += 1")
+ end
+
+ def test_GlobalVariableAndWriteNode
+ assert_prism_eval("$pit = 0; $pit &&= 1")
+ end
+
+ def test_GlobalVariableOperatorWriteNode
+ assert_prism_eval("$pit = 0; $pit += 1")
+ end
+
+ def test_GlobalVariableOrWriteNode
+ assert_prism_eval("$pit ||= 1")
+ end
+
+ def test_GlobalVariableWriteNode
+ assert_prism_eval("$pit = 1")
+ end
+
+ def test_InstanceVariableAndWriteNode
+ assert_prism_eval("@pit = 0; @pit &&= 1")
+ end
+
+ def test_InstanceVariableOperatorWriteNode
+ assert_prism_eval("@pit = 0; @pit += 1")
+ end
+
+ def test_InstanceVariableOrWriteNode
+ assert_prism_eval("@pit ||= 1")
+ end
+
+ def test_InstanceVariableWriteNode
+ assert_prism_eval("class Prism::TestCompilePrism; @pit = 1; end")
+ end
+
+ def test_LocalVariableAndWriteNode
+ assert_prism_eval("pit = 0; pit &&= 1")
+ end
+
+ def test_LocalVariableOperatorWriteNode
+ assert_prism_eval("pit = 0; pit += 1")
+ end
+
+ def test_LocalVariableOrWriteNode
+ assert_prism_eval("pit ||= 1")
+ end
+
+ def test_LocalVariableWriteNode
+ assert_prism_eval("pit = 1")
+ assert_prism_eval(<<-CODE)
+ a = 0
+ [].each do
+ a = 1
+ end
+ a
+ CODE
+
+ assert_prism_eval(<<-CODE)
+ a = 1
+ d = 1
+ [1].each do
+ b = 2
+ a = 2
+ [2].each do
+ c = 3
+ d = 4
+ a = 2
+ end
+ end
+ [a, d]
+ CODE
+ end
+
+ def test_MatchWriteNode
+ assert_prism_eval("/(?<foo>bar)(?<baz>bar>)/ =~ 'barbar'")
+ assert_prism_eval("/(?<foo>bar)/ =~ 'barbar'")
+ end
+
+ ############################################################################
+ # Multi-writes #
+ ############################################################################
+
+ def test_ClassVariableTargetNode
+ assert_prism_eval("class Prism::TestCompilePrism; @@pit, @@pit1 = 1; end")
+ end
+
+ def test_ConstantTargetNode
+ # We don't call assert_prism_eval directly in this case because we
+ # don't want to assign the constant multiple times if we run
+ # with `--repeat-count`
+ # Instead, we eval manually here, and remove the constant to
+ constant_names = ["YCT", "YCT2"]
+ source = "#{constant_names.join(",")} = 1"
+ prism_eval = RubyVM::InstructionSequence.compile_prism(source).eval
+ assert_equal prism_eval, 1
+ constant_names.map { |name|
+ Object.send(:remove_const, name)
+ }
+ end
+
+ def test_ConstantPathTargetNode
+ assert_separately([], <<~'RUBY')
+ verbose = $VERBOSE
+ # Create some temporary nested constants
+ Object.send(:const_set, "MyFoo", Object)
+ Object.const_get("MyFoo").send(:const_set, "Bar", Object)
+
+ constant_names = ["MyBar", "MyFoo::Bar", "MyFoo::Bar::Baz"]
+ source = "#{constant_names.join(",")} = Object"
+ iseq = RubyVM::InstructionSequence.compile_prism(source)
+ $VERBOSE = nil
+ prism_eval = iseq.eval
+ $VERBOSE = verbose
+ assert_equal prism_eval, Object
+ RUBY
+ end
+
+ def test_GlobalVariableTargetNode
+ assert_prism_eval("$pit, $pit1 = 1")
+ end
+
+ def test_InstanceVariableTargetNode
+ assert_prism_eval("class Prism::TestCompilePrism; @pit, @pit1 = 1; end")
+ end
+
+ def test_LocalVariableTargetNode
+ assert_prism_eval("pit, pit1 = 1")
+ assert_prism_eval(<<-CODE)
+ a = 1
+ [1].each do
+ c = 2
+ a, b = 2
+ end
+ a
+ CODE
+ end
+
+ def test_MultiTargetNode
+ assert_prism_eval("a, (b, c) = [1, 2, 3]")
+ assert_prism_eval("a, (b, c) = [1, 2, 3]; a")
+ assert_prism_eval("a, (b, c) = [1, 2, 3]; b")
+ assert_prism_eval("a, (b, c) = [1, 2, 3]; c")
+ assert_prism_eval("a, (b, c) = [1, [2, 3]]; c")
+ assert_prism_eval("a, (b, *c) = [1, [2, 3]]; c")
+ assert_prism_eval("a, (b, *c) = 1, [2, 3]; c")
+ assert_prism_eval("a, (b, *) = 1, [2, 3]; b")
+ assert_prism_eval("a, (b, *c, d) = 1, [2, 3, 4]; [a, b, c, d]")
+ assert_prism_eval("(a, (b, c, d, e), f, g), h = [1, [2, 3]], 4, 5, [6, 7]; c")
+ end
+
+ def test_MultiWriteNode
+ assert_prism_eval("foo, bar = [1, 2]")
+ assert_prism_eval("foo, = [1, 2]")
+ assert_prism_eval("foo, *, bar = [1, 2]")
+ assert_prism_eval("foo, bar = 1, 2")
+ assert_prism_eval("foo, *, bar = 1, 2")
+ assert_prism_eval("foo, *, bar = 1, 2, 3, 4")
+ assert_prism_eval("a, b, *, d = 1, 2, 3, 4")
+ assert_prism_eval("a, b, *, d = 1, 2")
+ assert_prism_eval("(a, b), *, c = [1, 3], 4, 5")
+ assert_prism_eval("(a, b), *, c = [1, 3], 4, 5; a")
+ assert_prism_eval("(a, b), *, c = [1, 3], 4, 5; b")
+ assert_prism_eval("(a, b), *, c = [1, 3], 4, 5; c")
+ assert_prism_eval("a, *, (c, d) = [1, 3], 4, 5; a")
+ assert_prism_eval("a, *, (c, d) = [1, 3], 4, 5; c")
+ assert_prism_eval("(a, b, c), *, (d, e) = [1, 3], 4, 5, [6, 7]")
+ assert_prism_eval("(a, b, c), *, (d, e) = [1, 3], 4, 5, [6, 7]; b")
+ assert_prism_eval("(a, b, c), *, (d, e) = [1, 3], 4, 5, [6, 7]; d")
+ assert_prism_eval("((a, *, b), *, (c, *, (d, *, e, f, g))), *, ((h, i, *, j), *, (k, l, m, *, n, o, p), q, r) = 1; a")
+ assert_prism_eval("*a = 1; a")
+ assert_prism_eval("_, {}[:foo] = 1")
+ assert_prism_eval("_, {}[:foo], _ = 1")
+ assert_prism_eval("_, {}[:foo], _ = 1")
+ assert_prism_eval("_,{}[:foo], _, {}[:bar] = 1")
+ assert_prism_eval("* = :foo")
+ assert_prism_eval("* = *[]")
+ assert_prism_eval("a, * = :foo")
+
+
+ assert_prism_eval(<<~CODE)
+ class Foo
+ def bar=(x); end
+ def baz=(c); end
+ end
+ foo = Foo.new
+ foo.bar, foo.baz = 1
+ CODE
+ assert_prism_eval(<<~CODE)
+ class Foo
+ def bar=(x); end
+ def baz=(c); end
+ end
+ foo = Foo.new
+ _, foo.bar, foo.baz = 1
+ CODE
+ assert_prism_eval(<<~CODE)
+ class Foo
+ def bar=(x); end
+ def baz=(c); end
+ end
+ foo = Foo.new
+ _, foo.bar, _, foo.baz = 1
+ CODE
+
+ # Test nested writes with method calls
+ assert_prism_eval(<<~RUBY)
+ class Foo
+ attr_accessor :bar
+ end
+
+ a = Foo.new
+
+ (a.bar, a.bar), b = [1], 2
+ RUBY
+ assert_prism_eval(<<~RUBY)
+ h = {}
+ (h[:foo], h[:bar]), a = [1], 2
+ RUBY
+ end
+
+ ############################################################################
+ # String-likes #
+ ############################################################################
+
+ def test_EmbeddedStatementsNode
+ assert_prism_eval('"foo #{to_s} baz"')
+ end
+
+ def test_EmbeddedVariableNode
+ assert_prism_eval('class Prism::TestCompilePrism; @pit = 1; "#@pit"; end')
+ assert_prism_eval('class Prism::TestCompilePrism; @@pit = 1; "#@@pit"; end')
+ assert_prism_eval('$pit = 1; "#$pit"')
+ end
+
+ def test_InterpolatedMatchLastLineNode
+ assert_prism_eval('$pit = ".oo"; if /"#{$pit}"/mix; end')
+ end
+
+ def test_InterpolatedRegularExpressionNode
+ assert_prism_eval('$pit = 1; /1 #$pit 1/')
+ assert_prism_eval('$pit = 1; /#$pit/i')
+ assert_prism_eval('/1 #{1 + 2} 1/')
+ assert_prism_eval('/1 #{"2"} #{1 + 2} 1/')
+ end
+
+ def test_InterpolatedStringNode
+ assert_prism_eval('$pit = 1; "1 #$pit 1"')
+ assert_prism_eval('"1 #{1 + 2} 1"')
+ assert_prism_eval('"Prism" "::" "TestCompilePrism"')
+ assert_prism_eval(<<-'RUBY')
+ # frozen_string_literal: true
+
+ !("a""b""#{1}").frozen?
+ RUBY
+ assert_prism_eval(<<-'RUBY')
+ # frozen_string_literal: true
+
+ !("a""#{1}""b").frozen?
+ RUBY
+
+ # Test encoding of interpolated strings
+ assert_prism_eval(<<~'RUBY')
+ "#{"foo"}s".encoding
+ RUBY
+ assert_prism_eval(<<~'RUBY')
+ a = "foo"
+ b = "#{a}" << "Bar"
+ [a, b, b.encoding]
+ RUBY
+ end
+
+ def test_concatenated_StringNode
+ assert_prism_eval('("a""b").frozen?')
+ assert_prism_eval(<<-CODE)
+ # frozen_string_literal: true
+
+ ("a""b").frozen?
+ CODE
+ end
+
+ def test_InterpolatedSymbolNode
+ assert_prism_eval('$pit = 1; :"1 #$pit 1"')
+ assert_prism_eval(':"1 #{1 + 2} 1"')
+ end
+
+ def test_InterpolatedXStringNode
+ assert_prism_eval(<<~RUBY)
+ def self.`(command) = command * 2
+ `echo \#{1}`
+ RUBY
+
+ assert_prism_eval(<<~RUBY)
+ def self.`(command) = command * 2
+ `echo \#{"100"}`
+ RUBY
+ end
+
+ def test_MatchLastLineNode
+ assert_prism_eval("if /foo/; end")
+ assert_prism_eval("if /foo/i; end")
+ assert_prism_eval("if /foo/x; end")
+ assert_prism_eval("if /foo/m; end")
+ assert_prism_eval("if /foo/im; end")
+ assert_prism_eval("if /foo/mx; end")
+ assert_prism_eval("if /foo/xi; end")
+ assert_prism_eval("if /foo/ixm; end")
+ end
+
+ def test_RegularExpressionNode
+ assert_prism_eval('/pit/')
+ assert_prism_eval('/pit/i')
+ assert_prism_eval('/pit/x')
+ assert_prism_eval('/pit/m')
+ assert_prism_eval('/pit/im')
+ assert_prism_eval('/pit/mx')
+ assert_prism_eval('/pit/xi')
+ assert_prism_eval('/pit/ixm')
+
+ assert_prism_eval('/pit/u')
+ assert_prism_eval('/pit/e')
+ assert_prism_eval('/pit/s')
+ assert_prism_eval('/pit/n')
+
+ assert_prism_eval('/pit/me')
+ assert_prism_eval('/pit/ne')
+
+ assert_prism_eval('2.times.map { /#{1}/o }')
+ assert_prism_eval('2.times.map { foo = 1; /#{foo}/o }')
+ end
+
+ def test_StringNode
+ assert_prism_eval('"pit"')
+ assert_prism_eval('"a".frozen?')
+ end
+
+ def test_StringNode_frozen_string_literal_true
+ [
+ # Test that string literal is frozen
+ <<~RUBY,
+ # frozen_string_literal: true
+ "a".frozen?
+ RUBY
+ # Test that two string literals with the same contents are the same string
+ <<~RUBY,
+ # frozen_string_literal: true
+ "hello".equal?("hello")
+ RUBY
+ ].each do |src|
+ assert_prism_eval(src, raw: true)
+ end
+ end
+
+ def test_StringNode_frozen_string_literal_false
+ [
+ # Test that string literal is frozen
+ <<~RUBY,
+ # frozen_string_literal: false
+ !"a".frozen?
+ RUBY
+ # Test that two string literals with the same contents are the same string
+ <<~RUBY,
+ # frozen_string_literal: false
+ !"hello".equal?("hello")
+ RUBY
+ ].each do |src|
+ assert_prism_eval(src, raw: true)
+ end
+ end
+
+ def test_StringNode_frozen_string_literal_default
+ # Test that string literal is chilled
+ assert_prism_eval('"a".frozen?')
+
+ # Test that two identical chilled string literals aren't the same object
+ assert_prism_eval('!"hello".equal?("hello")')
+ end
+
+ def test_SymbolNode
+ assert_prism_eval(":pit")
+
+ # Test UTF-8 symbol in a US-ASCII file
+ assert_prism_eval(<<~'RUBY', raw: true)
+ # -*- coding: us-ascii -*-
+ :"\u{e9}"
+ RUBY
+
+ # Test ASCII-8BIT symbol in a US-ASCII file
+ assert_prism_eval(<<~'RUBY', raw: true)
+ # -*- coding: us-ascii -*-
+ :"\xff"
+ RUBY
+
+ # Test US-ASCII symbol in a ASCII-8BIT file
+ assert_prism_eval(<<~'RUBY', raw: true)
+ # -*- coding: ascii-8bit -*-
+ :a
+ RUBY
+ end
+
+ def test_XStringNode
+ assert_prism_eval(<<~RUBY)
+ class Prism::TestCompilePrism
+ def self.`(command) = command * 2
+ `pit`
+ end
+ RUBY
+ end
+
+ ############################################################################
+ # Structures #
+ ############################################################################
+
+ def test_ArrayNode
+ assert_prism_eval("[]")
+ assert_prism_eval("[1, 2, 3]")
+ assert_prism_eval("%i[foo bar baz]")
+ assert_prism_eval("%w[foo bar baz]")
+ assert_prism_eval("[*1..2]")
+ assert_prism_eval("[*1..2, 3, 4, *5..6, 7, 8]")
+ assert_prism_eval("[*1..2, 3, 4, *5..6, 7, 8, *9..11]")
+ assert_prism_eval("[0, *1..2, 3, 4, *5..6, 7, 8, *9..11]")
+ assert_prism_eval("[-1, true, 0, *1..2, 3, 4, *5..6, 7, 8, *9..11]")
+ assert_prism_eval("a = [1,2]; [0, *a, 3, 4, *5..6, 7, 8, *9..11]")
+ assert_prism_eval("[[*1..2], 3, *4..5]")
+
+ # Test keyword splat inside of array
+ assert_prism_eval("[**{x: 'hello'}]")
+
+ # Test UTF-8 string array literal in a US-ASCII file
+ assert_prism_eval(<<~'RUBY', raw: true)
+ # -*- coding: us-ascii -*-
+ # frozen_string_literal: true
+ %W"\u{1f44b} \u{1f409}"
+ RUBY
+ end
+
+ def test_AssocNode
+ assert_prism_eval("{ foo: :bar }")
+ end
+
+ def test_AssocSplatNode
+ assert_prism_eval("foo = { a: 1 }; { **foo }")
+ assert_prism_eval("foo = { a: 1 }; bar = foo; { **foo, b: 2, **bar, c: 3 }")
+ assert_prism_eval("foo = { a: 1 }; { b: 2, **foo, c: 3}")
+
+ # Test anonymous AssocSplatNode
+ assert_prism_eval(<<~RUBY)
+ o = Object.new
+ def o.bar(**) = Hash(**)
+
+ o.bar(hello: "world")
+ RUBY
+
+ # Test that AssocSplatNode is evaluated before BlockArgumentNode using
+ # the splatkw instruction
+ assert_prism_eval(<<~RUBY)
+ o = Struct.new(:ary) do
+ def to_hash
+ ary << :to_hash
+ {}
+ end
+
+ def to_proc
+ ary << :to_proc
+ -> {}
+ end
+
+ def t(...); end
+ end.new
+ o.ary = []
+
+ o.t(**o, &o)
+ o.ary
+ RUBY
+ end
+
+ def test_HashNode
+ assert_prism_eval("{}")
+ assert_prism_eval("{ a: :a }")
+ assert_prism_eval("{ a: :a, b: :b }")
+ assert_prism_eval("a = 1; { a: a }")
+ assert_prism_eval("a = 1; { a: }")
+ assert_prism_eval("{ to_s: }")
+ assert_prism_eval("{ Prism: }")
+ assert_prism_eval("[ Prism: [:b, :c]]")
+ assert_prism_eval("{ [] => 1}")
+ end
+
+ def test_ImplicitNode
+ assert_prism_eval("{ to_s: }")
+ end
+
+ def test_RangeNode
+ assert_prism_eval("1..2")
+ assert_prism_eval("1...2")
+ assert_prism_eval("..2")
+ assert_prism_eval("...2")
+ assert_prism_eval("1..")
+ assert_prism_eval("1...")
+ end
+
+ def test_SplatNode
+ assert_prism_eval("*b = []; b")
+ assert_prism_eval("*b = [1, 2, 3]; b")
+ assert_prism_eval("a, *b = [1, 2, 3]; a")
+ assert_prism_eval("a, *b = [1, 2, 3]; b")
+ assert_prism_eval("a, *b, c = [1, 2, 3]; a")
+ assert_prism_eval("a, *b, c = [1, 2, 3]; b")
+ assert_prism_eval("a, *b, c = [1, 2, 3]; c")
+ assert_prism_eval("*b, c = [1, 2, 3]; b")
+ assert_prism_eval("*b, c = [1, 2, 3]; c")
+ assert_prism_eval("a, *, c = [1, 2, 3]; a")
+ assert_prism_eval("a, *, c = [1, 2, 3]; c")
+
+ # Test anonymous splat node
+ assert_prism_eval(<<~RUBY)
+ def self.bar(*) = Array(*)
+
+ bar([1, 2, 3])
+ RUBY
+ end
+
+ ############################################################################
+ # Jumps #
+ ############################################################################
+
+ def test_AndNode
+ assert_prism_eval("true && 1")
+ assert_prism_eval("false && 1")
+ end
+
+ def test_CaseNode
+ assert_prism_eval("case :a; when :a; 1; else; 2; end")
+ assert_prism_eval("case :a; when :b; 1; else; 2; end")
+ assert_prism_eval("case :a; when :a; 1; else; 2; end")
+ assert_prism_eval("case :a; when :a; end")
+ assert_prism_eval("case :a; when :b, :c; end")
+ assert_prism_eval("case; when :a; end")
+ assert_prism_eval("case; when :a, :b; 1; else; 2 end")
+ assert_prism_eval("case :a; when :b; else; end")
+ assert_prism_eval("b = 1; case :a; when b; else; end")
+ assert_prism_eval(<<-CODE)
+ def self.prism_test_case_node
+ case :a
+ when :b
+ else
+ return 2
+ end
+ 1
+ end
+ prism_test_case_node
+ CODE
+
+ # Test splat in when
+ assert_prism_eval(<<~RUBY)
+ ary = [1, 2]
+ case 1
+ when *ary
+ :ok
+ else
+ :ng
+ end
+ RUBY
+
+ # Test splat in when
+ assert_prism_eval(<<~RUBY)
+ ary = [1, 2]
+ case 1
+ when :foo, *ary
+ :ok
+ else
+ :ng
+ end
+ RUBY
+
+ # Test case without predicate
+ assert_prism_eval(<<~RUBY)
+ case
+ when 1 == 2
+ :ng
+ else
+ :ok
+ end
+ RUBY
+
+ # test splat with no predicate
+ assert_prism_eval(<<~RUBY)
+ case
+ when *[true]
+ :ok
+ else
+ :ng
+ end
+ RUBY
+ end
+
+ def test_ElseNode
+ assert_prism_eval("if false; 0; else; 1; end")
+ assert_prism_eval("if true; 0; else; 1; end")
+ assert_prism_eval("true ? 1 : 0")
+ assert_prism_eval("false ? 0 : 1")
+ end
+
+ def test_FlipFlopNode
+ assert_prism_eval("not (1 == 1) .. (2 == 2)")
+ assert_prism_eval("not (1 == 1) ... (2 == 2)")
+ end
+
+ def test_IfNode
+ assert_prism_eval("if true; 1; end")
+ assert_prism_eval("1 if true")
+ assert_prism_eval('a = b = 1; if a..b; end')
+ assert_prism_eval('if "a".."b"; end')
+ assert_prism_eval('if "a"..; end')
+ assert_prism_eval('if .."b"; end')
+ assert_prism_eval('if ..1; end')
+ assert_prism_eval('if 1..; end')
+ assert_prism_eval('if 1..2; end')
+ assert_prism_eval('if true or true; end');
+ end
+
+ def test_OrNode
+ assert_prism_eval("true || 1")
+ assert_prism_eval("false || 1")
+ end
+
+ def test_UnlessNode
+ assert_prism_eval("1 unless true")
+ assert_prism_eval("1 unless false")
+ assert_prism_eval("unless true; 1; end")
+ assert_prism_eval("unless false; 1; end")
+ end
+
+ def test_UntilNode
+ assert_prism_eval("a = 0; until a == 1; a = a + 1; end")
+
+ # Test UntilNode in rescue
+ assert_prism_eval(<<~RUBY)
+ o = Object.new
+ o.instance_variable_set(:@ret, [])
+ def o.foo = @ret << @ret.length
+ def o.bar = @ret.length > 3
+ begin
+ raise
+ rescue
+ o.foo until o.bar
+ end
+ o.instance_variable_get(:@ret)
+ RUBY
+ end
+
+ def test_WhileNode
+ assert_prism_eval("a = 0; while a != 1; a = a + 1; end")
+
+ # Test WhileNode in rescue
+ assert_prism_eval(<<~RUBY)
+ o = Object.new
+ o.instance_variable_set(:@ret, [])
+ def o.foo = @ret << @ret.length
+ def o.bar = @ret.length < 3
+ begin
+ raise
+ rescue
+ o.foo while o.bar
+ end
+ o.instance_variable_get(:@ret)
+ RUBY
+ end
+
+ def test_ForNode
+ assert_prism_eval("for i in [1,2] do; i; end")
+ assert_prism_eval("for @i in [1,2] do; @i; end")
+ assert_prism_eval("for $i in [1,2] do; $i; end")
+
+ assert_prism_eval("for foo, in [1,2,3] do end")
+
+ assert_prism_eval("for i, j in {a: 'b'} do; i; j; end")
+ end
+
+ ############################################################################
+ # Throws #
+ ############################################################################
+
+ def test_BeginNode
+ assert_prism_eval("begin; 1; end")
+ assert_prism_eval("begin; end; 1")
+ end
+
+ def test_BreakNode
+ assert_prism_eval("while true; break; end")
+ assert_prism_eval("while true; break 1; end")
+ assert_prism_eval("while true; break 1, 2; end")
+
+ assert_prism_eval("[].each { break }")
+ assert_prism_eval("[true].map { break }")
+ end
+
+ def test_ensure_in_methods
+ assert_prism_eval(<<-CODE)
+def self.m
+ a = []
+ensure
+ a << 5
+ return a
+end
+m
+ CODE
+ end
+
+ def test_break_runs_ensure
+ assert_prism_eval(<<-CODE)
+a = []
+while true
+ begin
+ break
+ ensure
+ a << 1
+ end
+end
+a
+ CODE
+ end
+
+ def test_EnsureNode
+ assert_prism_eval("begin; 1; ensure; 2; end")
+ assert_prism_eval("begin; 1; begin; 3; ensure; 4; end; ensure; 2; end")
+ assert_prism_eval(<<-CODE)
+ begin
+ a = 2
+ ensure
+ end
+ CODE
+ assert_prism_eval(<<-CODE)
+ begin
+ a = 2
+ ensure
+ a = 3
+ end
+ a
+ CODE
+
+ # Test that ensure block only evaluated once
+ assert_prism_eval(<<~RUBY)
+ res = []
+ begin
+ begin
+ raise
+ ensure
+ res << $!.to_s
+ end
+ rescue
+ res
+ end
+ RUBY
+
+ assert_prism_eval(<<-CODE)
+ a = 1
+ begin
+ a = 2
+ ensure
+ a = 3
+ end
+ a
+ CODE
+ assert_prism_eval(<<-CODE)
+ a = 1
+ begin
+ b = 2
+ ensure
+ c = 3
+ end
+ a + b + c
+ CODE
+ assert_prism_eval(<<~CODE)
+ foo = 1
+ begin
+ ensure
+ begin
+ ensure
+ foo.nil?
+ end
+ end
+ CODE
+ assert_prism_eval(<<~CODE)
+ def test
+ ensure
+ {}.each do |key, value|
+ {}[key] = value
+ end
+ end
+ CODE
+ assert_prism_eval(<<~CODE)
+ def test
+ a = 1
+ ensure
+ {}.each do |key, value|
+ {}[key] = a
+ end
+ end
+ CODE
+ assert_prism_eval(<<-CODE)
+ def self.prism_test_ensure_node
+ begin
+ ensure
+ end
+ return
+ end
+ prism_test_ensure_node
+ CODE
+
+ # Test empty ensure block
+ assert_prism_eval(<<~RUBY)
+ res = []
+
+ begin
+ begin
+ raise
+ ensure
+ end
+ rescue
+ res << "rescue"
+ end
+
+ res
+ RUBY
+ end
+
+ def test_NextNode
+ assert_prism_eval("2.times do |i|; next if i == 1; end")
+
+ assert_prism_eval(<<-CODE)
+ res = []
+ i = 0
+ while i < 5
+ i += 1
+ next if i == 3
+ res << i
+ end
+ res
+ CODE
+
+ assert_prism_eval(<<-CODE)
+ res = []
+ (1..5).each do |i|
+ next if i.even?
+ res << i
+ end
+ res
+ CODE
+
+ assert_prism_eval(<<-CODE)
+ (1..5).map do |i|
+ next i, :even if i.even?
+ i
+ end
+ CODE
+
+ assert_prism_eval(<<-CODE)
+ res = []
+ i = 0
+ begin
+ i += 1
+ next if i == 3
+ res << i
+ end while i < 5
+ res
+ CODE
+
+ assert_prism_eval(<<-CODE)
+ while false
+ begin
+ ensure
+ end
+ next
+ end
+ CODE
+
+ assert_prism_eval(<<~CODE)
+ [].each do
+ begin
+ rescue
+ next
+ end
+ end
+ CODE
+ end
+
+ def test_RedoNode
+ assert_prism_eval(<<-CODE)
+ counter = 0
+
+ 5.times do |i|
+ counter += 1
+ if i == 2 && counter < 3
+ redo
+ end
+ end
+ CODE
+
+ assert_prism_eval(<<-CODE)
+ for i in 1..5
+ if i == 3
+ i = 0
+ redo
+ end
+ end
+ CODE
+
+ assert_prism_eval(<<-CODE)
+ i = 0
+ begin
+ i += 1
+ redo if i == 3
+ end while i < 5
+ CODE
+ end
+
+ def test_RescueNode
+ assert_prism_eval("begin; 1; rescue; 2; end")
+ assert_prism_eval(<<~CODE)
+ begin
+ 1
+ rescue SyntaxError
+ 2
+ end
+ CODE
+ assert_prism_eval(<<~CODE)
+ begin
+ 1
+ raise 'boom'
+ rescue StandardError
+ 2
+ end
+ CODE
+ assert_prism_eval(<<~CODE)
+ begin
+ a = 1
+ rescue StandardError => e
+ end
+ CODE
+ assert_prism_eval(<<~CODE)
+ begin
+ raise StandardError
+ rescue StandardError => e
+ end
+ CODE
+ assert_prism_eval(<<~CODE)
+ begin
+ 1
+ rescue StandardError => e
+ e
+ rescue SyntaxError => f
+ f
+ else
+ 4
+ end
+ CODE
+ assert_prism_eval(<<-CODE)
+ begin
+ a = 2
+ rescue
+ a = 3
+ end
+ a
+ CODE
+ assert_prism_eval(<<-CODE)
+ a = 1
+ begin
+ a = 2
+ rescue
+ a = 3
+ end
+ a
+ CODE
+ assert_prism_eval(<<-CODE)
+ a = 1
+ begin
+ b = 2
+ raise "bang"
+ rescue
+ c = 3
+ end
+ a + b + c
+ CODE
+ assert_prism_eval("begin; rescue; end")
+
+ assert_prism_eval(<<~CODE)
+ begin
+ rescue
+ args.each do |key, value|
+ tmp[key] = 1
+ end
+ end
+ CODE
+ assert_prism_eval(<<~CODE)
+ 10.times do
+ begin
+ rescue
+ break
+ end
+ end
+ CODE
+
+ # Test RescueNode with ElseNode
+ assert_prism_eval(<<~RUBY)
+ calls = []
+ begin
+ begin
+ rescue RuntimeError
+ calls << 1
+ else
+ calls << 2
+ raise RuntimeError
+ end
+ rescue RuntimeError
+ end
+
+ calls
+ RUBY
+ end
+
+ def test_RescueModifierNode
+ assert_prism_eval("1.nil? rescue false")
+ assert_prism_eval("1.nil? rescue 1")
+ assert_prism_eval("raise 'bang' rescue nil")
+ assert_prism_eval("raise 'bang' rescue a = 1; a.nil?")
+ assert_prism_eval("a = 0 rescue (a += 1 && retry if a <= 1)")
+ end
+
+ def test_RetryNode
+ assert_prism_eval(<<~CODE)
+ a = 1
+ begin
+ a
+ raise "boom"
+ rescue
+ a += 1
+ retry unless a > 1
+ ensure
+ a = 3
+ end
+ CODE
+
+ assert_prism_eval(<<~CODE)
+ begin
+ rescue
+ foo = 2
+ retry
+ end
+ CODE
+
+ assert_prism_eval(<<~CODE)
+ begin
+ a = 2
+ rescue
+ retry
+ end
+ CODE
+ end
+
+ def test_ReturnNode
+ assert_prism_eval(<<-CODE)
+ def self.prism_test_return_node
+ return 1
+ end
+ prism_test_return_node
+ CODE
+
+ assert_prism_eval(<<-CODE)
+ def self.prism_test_return_node
+ return 1, 2
+ end
+ prism_test_return_node
+ CODE
+
+ assert_prism_eval(<<-CODE)
+ def self.prism_test_return_node
+ [1].each do |e|
+ return true
+ end
+ end
+ prism_test_return_node
+ CODE
+
+ assert_prism_eval(<<-CODE)
+ def self.prism_test_return_node
+ [1].map do |i|
+ return i if i == 1
+ 2
+ end
+ end
+ prism_test_return_node
+ CODE
+
+ assert_prism_eval(<<-CODE)
+ def self.prism_test_return_node(*args, **kwargs)
+ return *args, *args, **kwargs
+ end
+ prism_test_return_node(1, foo: 0)
+ CODE
+ end
+
+ ############################################################################
+ # Scopes/statements #
+ ############################################################################
+
+ def test_BlockNode
+ assert_prism_eval("[1, 2, 3].each { |num| num }")
+
+ assert_prism_eval("[].tap { _1 }")
+
+ assert_prism_eval("[].each { |a,| }")
+ assert_prism_eval("[[1, 2, 3]].map { |_, _, a| a }")
+ assert_prism_eval("[[1, 2, 3]].map { |_, a| a }")
+
+ assert_prism_eval("[[]].map { |a| a }")
+ assert_prism_eval("[[]].map { |a| a }")
+ assert_prism_eval("[[]].map { |a, &block| a }")
+ assert_prism_eval("[[]].map { |a, &block| a }")
+ assert_prism_eval("[{}].map { |a,| }")
+ assert_prism_eval("[[]].map { |a,b=1| a }")
+ assert_prism_eval("[{}].map { |a,| }")
+ assert_prism_eval("[{}].map { |a| a }")
+
+ # Test blocks with MultiTargetNode
+ assert_prism_eval("[[1, 2]].each.map { |(a), (b)| [a, b] }")
+ end
+
+ def test_ClassNode
+ assert_prism_eval("class PrismClassA; end")
+ assert_prism_eval("class PrismClassA; end; class PrismClassB < PrismClassA; end")
+ assert_prism_eval("class PrismClassA; end; class PrismClassA::PrismClassC; end")
+ assert_prism_eval(<<-HERE
+ class PrismClassA; end
+ class PrismClassA::PrismClassC; end
+ class PrismClassB; end
+ class PrismClassB::PrismClassD < PrismClassA::PrismClassC; end
+ HERE
+ )
+ end
+
+ # Many of these tests are versions of tests at bootstraptest/test_method.rb
+ def test_DefNode
+ assert_prism_eval("def prism_test_def_node; end")
+ assert_prism_eval("a = Object.new; def a.prism_singleton; :ok; end; a.prism_singleton")
+ assert_prism_eval("def self.prism_test_def_node() 1 end; prism_test_def_node()")
+ assert_prism_eval("def self.prism_test_def_node(a,b) [a, b] end; prism_test_def_node(1,2)")
+ assert_prism_eval("def self.prism_test_def_node(a,x=7,y=1) x end; prism_test_def_node(7,1)")
+ assert_prism_eval("def self.prism_test_def_node(a = 1); x = 2; end; prism_test_def_node")
+
+ # rest argument
+ assert_prism_eval("def self.prism_test_def_node(*a) a end; prism_test_def_node().inspect")
+ assert_prism_eval("def self.prism_test_def_node(*a) a end; prism_test_def_node(1).inspect")
+ assert_prism_eval("def self.prism_test_def_node(x,y,*a) a end; prism_test_def_node(7,7,1,2).inspect")
+ assert_prism_eval("def self.prism_test_def_node(x,y=7,*a) a end; prism_test_def_node(7).inspect")
+ assert_prism_eval("def self.prism_test_def_node(x,y,z=7,*a) a end; prism_test_def_node(7,7).inspect")
+ assert_prism_eval("def self.prism_test_def_node(x,y,z=7,zz=7,*a) a end; prism_test_def_node(7,7,7).inspect")
+
+ # keyword arguments
+ assert_prism_eval("def self.prism_test_def_node(a: 1, b: 2, c: 4) a + b + c; end; prism_test_def_node(a: 2)")
+ assert_prism_eval("def self.prism_test_def_node(a: 1, b: 2, c: 4) a + b + c; end; prism_test_def_node(b: 3)")
+ assert_prism_eval(<<-CODE)
+ def self.prism_test_def_node(x = 1, y, a: 8, b: 2, c: 4)
+ a + b + c + x + y
+ end
+ prism_test_def_node(10, b: 3)
+ CODE
+ assert_prism_eval(<<-CODE)
+ def self.prism_test_def_node(a: [])
+ a
+ end
+ prism_test_def_node
+ CODE
+
+ # block arguments
+ assert_prism_eval("def self.prism_test_def_node(&block) block end; prism_test_def_node{}.class")
+ assert_prism_eval("def self.prism_test_def_node(&block) block end; prism_test_def_node().inspect")
+ assert_prism_eval("def self.prism_test_def_node(a,b=7,*c,&block) b end; prism_test_def_node(7,1).inspect")
+ assert_prism_eval("def self.prism_test_def_node(a,b=7,*c,&block) c end; prism_test_def_node(7,7,1).inspect")
+
+ # splat
+ assert_prism_eval("def self.prism_test_def_node(a) a end; prism_test_def_node(*[1])")
+ assert_prism_eval("def self.prism_test_def_node(x,a) a end; prism_test_def_node(7,*[1])")
+ assert_prism_eval("def self.prism_test_def_node(x,y,a) a end; prism_test_def_node(7,7,*[1])")
+ assert_prism_eval("def self.prism_test_def_node(x,y,a,b,c) a end; prism_test_def_node(7,7,*[1,7,7])")
+
+ # recursive call
+ assert_prism_eval("def self.prism_test_def_node(n) n == 0 ? 1 : prism_test_def_node(n-1) end; prism_test_def_node(5)")
+
+ # instance method
+ assert_prism_eval("class PrismTestDefNode; def prism_test_def_node() 1 end end; PrismTestDefNode.new.prism_test_def_node")
+ assert_prism_eval("class PrismTestDefNode; def prism_test_def_node(*a) a end end; PrismTestDefNode.new.prism_test_def_node(1).inspect")
+
+ # block argument
+ assert_prism_eval(<<-CODE)
+ def self.prism_test_def_node(&block) prism_test_def_node2(&block) end
+ def self.prism_test_def_node2() yield 1 end
+ prism_test_def_node2 {|a| a }
+ CODE
+
+ # multi argument
+ assert_prism_eval(<<-CODE)
+ def self.prism_test_def_node(a, (b, *c, d))
+ [a, b, c, d]
+ end
+ prism_test_def_node("a", ["b", "c", "d"])
+ CODE
+ assert_prism_eval(<<-CODE)
+ def self.prism_test_def_node(a, (b, c, *))
+ [a, b, c]
+ end
+ prism_test_def_node("a", ["b", "c"])
+ CODE
+ assert_prism_eval(<<-CODE)
+ def self.prism_test_def_node(a, (*, b, c))
+ [a, b, c]
+ end
+ prism_test_def_node("a", ["b", "c"])
+ CODE
+
+ # recursive multis
+ assert_prism_eval(<<-CODE)
+ def self.prism_test_def_node(a, (b, *c, (d, *e, f)))
+ [a, b, c, d, d, e, f]
+ end
+ prism_test_def_node("a", ["b", "c", ["d", "e", "f"]])
+ CODE
+
+ # Many arguments
+ assert_prism_eval(<<-CODE)
+ def self.prism_test_def_node(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m)
+ [a, b, c, d, e, f, g, h, i, j, k, l, m]
+ end
+ prism_test_def_node(
+ "a",
+ ["b", "c1", "c2", "d"],
+ "e",
+ "f1", "f2",
+ "g",
+ ["h", "i1", "i2", "j"],
+ k: "k",
+ l: "l",
+ m1: "m1",
+ m2: "m2"
+ )
+ CODE
+ end
+
+ def test_pow_parameters
+ assert_prism_eval("def self.m(a, **); end; method(:m).parameters")
+ end
+
+ def test_star_parameters
+ assert_prism_eval("def self.m(a, *, b); end; method(:m).parameters")
+ end
+
+ def test_repeated_block_params
+ assert_prism_eval("def self.x(&blk); blk; end; x { |_, _, _ = 1, *_, _:, _: 2, **_, &_| }.parameters")
+ end
+
+ def test_repeated_proc_params
+ assert_prism_eval("proc {|_, _, _ = 1, *_, _:, _: 2, **_, &_| }.parameters")
+ end
+
+ def test_forward_parameters_block
+ assert_prism_eval("def self.m(&); end; method(:m).parameters")
+ end
+
+ def test_forward_parameters
+ assert_prism_eval("def self.m(...); end; method(:m).parameters")
+ end
+
+ def test_repeated_block_underscore
+ assert_prism_eval("def self.m(_, **_, &_); _; end; method(:m).parameters")
+ end
+
+ def test_repeated_kw_rest_underscore
+ assert_prism_eval("def self.m(_, **_); _; end; method(:m).parameters")
+ end
+
+ def test_repeated_required_keyword_underscore
+ assert_prism_eval("def self.m(_, _, *_, _, _:); _; end; method(:m).parameters")
+ assert_prism_eval("def self.m(_, _, *_, _, _:, _: 2); _; end; method(:m).parameters")
+ end
+
+ def test_repeated_required_post_underscore
+ assert_prism_eval("def self.m(_, _, *_, _); _; end; method(:m).parameters")
+ end
+
+ def test_repeated_splat_underscore
+ assert_prism_eval("def self.m(_, _, _ = 1, _ = 2, *_); end; method(:m).parameters")
+ end
+
+ def test_repeated_optional_underscore
+ assert_prism_eval("def self.m(a, _, _, _ = 1, _ = 2, b); end; method(:m).parameters")
+ end
+
+ def test_repeated_required_underscore
+ assert_prism_eval("def self.m(a, _, _, b); end; method(:m).parameters")
+ end
+
+ def test_locals_in_parameters
+ assert_prism_eval("def self.m(a = b = c = 1); [a, b, c]; end; self.m")
+ end
+
+ def test_trailing_comma_on_block
+ assert_prism_eval("def self.m; yield [:ok]; end; m {|v0,| v0 }")
+ end
+
+ def test_complex_default_params
+ assert_prism_eval("def self.foo(a:, b: '2'.to_i); [a, b]; end; foo(a: 1)")
+ assert_prism_eval("def self.foo(a:, b: 2, c: '3'.to_i); [a, b, c]; end; foo(a: 1)")
+ end
+
+ def test_numbered_params
+ assert_prism_eval("[1, 2, 3].then { _3 }")
+ assert_prism_eval("1.then { one = 1; one + _1 }")
+ end
+
+ def test_rescue_with_ensure
+ assert_prism_eval(<<-CODE)
+begin
+ begin
+ raise "a"
+ rescue
+ raise "b"
+ ensure
+ raise "c"
+ end
+rescue => e
+ e.message
+end
+ CODE
+ end
+
+ def test_required_kwarg_ordering
+ assert_prism_eval("def self.foo(a: 1, b:); [a, b]; end; foo(b: 2)")
+ end
+
+ def test_trailing_keyword_method_params
+ # foo(1, b: 2, c: 3) # argc -> 3
+ assert_prism_eval("def self.foo(a, b:, c:); [a, b, c]; end; foo(1, b: 2, c: 3)")
+ end
+
+ def test_keyword_method_params_only
+ # foo(a: 1, b: 2) # argc -> 2
+ assert_prism_eval("def self.foo(a:, b:); [a, b]; end; foo(a: 1, b: 2)")
+ end
+
+ def test_keyword_method_params_with_splat
+ # foo(a: 1, **b) # argc -> 1
+ assert_prism_eval("def self.foo(a:, b:); [a, b]; end; b = { b: 2 }; foo(a: 1, **b)")
+ end
+
+ def test_positional_and_splat_keyword_method_params
+ # foo(a, **b) # argc -> 2
+ assert_prism_eval("def self.foo(a, b); [a, b]; end; b = { b: 2 }; foo(1, **b)")
+ end
+
+ def test_positional_and_splat_method_params
+ # foo(a, *b, c, *d, e) # argc -> 2
+ assert_prism_eval("def self.foo(a, b, c, d, e); [a, b, c, d, e]; end; b = [2]; d = [4]; foo(1, *b, 3, *d, 5)")
+ end
+
+ def test_positional_with_splat_and_splat_keyword_method_params
+ # foo(a, *b, c, *d, **e) # argc -> 3
+ assert_prism_eval("def self.foo(a, b, c, d, e); [a, b, c, d, e]; end; b = [2]; d = [4]; e = { e: 5 }; foo(1, *b, 3, *d, **e)")
+ end
+
+ def test_positional_with_splat_and_keyword_method_params
+ # foo(a, *b, c, *d, e:) # argc -> 3
+ assert_prism_eval("def self.foo(a, b, c, d, e:); [a, b, c, d, e]; end; b = [2]; d = [4]; foo(1, *b, 3, *d, e: 5)")
+ end
+
+ def test_leading_splat_and_keyword_method_params
+ # foo(*a, b:) # argc -> 2
+ assert_prism_eval("def self.foo(a, b:); [a, b]; end; a = [1]; foo(*a, b: 2)")
+ end
+
+ def test_repeated_method_params
+ assert_prism_eval("def self.foo(_a, _a); _a; end; foo(1, 2)")
+ end
+
+ def test_splat_params_with_no_lefties
+ assert_prism_eval("def self.foo(v, (*)); v; end; foo(1, [2, 3, 4])")
+ end
+
+ def test_method_parameters
+ assert_prism_eval(<<-CODE)
+ def self.prism_test_method_parameters(a, b=1, *c, d:, e: 2, **f, &g)
+ end
+
+ method(:prism_test_method_parameters).parameters
+ CODE
+
+ assert_prism_eval(<<-CODE)
+ def self.prism_test_method_parameters(d:, e: 2, **f, &g)
+ end
+
+ method(:prism_test_method_parameters).parameters
+ CODE
+
+ assert_prism_eval(<<-CODE)
+ def self.prism_test_method_parameters(**f, &g)
+ end
+
+ method(:prism_test_method_parameters).parameters
+ CODE
+
+ assert_prism_eval(<<-CODE)
+ def self.prism_test_method_parameters(&g)
+ end
+
+ method(:prism_test_method_parameters).parameters
+ CODE
+ end
+
+ def test_LambdaNode
+ assert_prism_eval("-> { to_s }.call")
+ end
+
+ def test_LambdaNode_with_multiline_args
+ assert_prism_eval(<<-CODE)
+ -> (a,
+ b) {
+ a + b
+ }.call(1, 2)
+ CODE
+ end
+
+ def test_ModuleNode
+ assert_prism_eval("module M; end")
+ assert_prism_eval("module M::N; end")
+ assert_prism_eval("module ::O; end")
+ end
+
+ def test_ParenthesesNode
+ assert_prism_eval("()")
+ assert_prism_eval("(1)")
+ end
+
+ def test_PreExecutionNode
+ assert_prism_eval("BEGIN { a = 1 }; 2", raw: true)
+ assert_prism_eval("b = 2; BEGIN { a = 1 }; a + b", raw: true)
+ end
+
+ def test_PostExecutionNode
+ assert_prism_eval("END { 1 }")
+ assert_prism_eval("END { @b }; @b = 1")
+ assert_prism_eval("END { @b; 0 }; @b = 1")
+ assert_prism_eval("foo = 1; END { foo.nil? }")
+ assert_prism_eval("foo = 1; END { END { foo.nil? }}")
+ end
+
+ def test_ProgramNode
+ assert_prism_eval("")
+ assert_prism_eval("1")
+ end
+
+ def test_SingletonClassNode
+ assert_prism_eval("class << self; end")
+ end
+
+ def test_StatementsNode
+ assert_prism_eval("1")
+ end
+
+ def test_YieldNode
+ assert_prism_eval("def prism_test_yield_node; yield; end")
+ assert_prism_eval("def prism_test_yield_node; yield 1, 2; end")
+ assert_prism_eval("def prism_test_yield_node; yield **kw if condition; end")
+
+ # Test case where there's a call directly after the yield call
+ assert_prism_eval("def prism_test_yield_node; yield; 1; end")
+ assert_prism_eval("def prism_test_yield_node; yield 1, 2; 1; end")
+ end
+
+ ############################################################################
+ # Calls / arguments #
+ ############################################################################
+
+ def test_ArgumentsNode
+ # assert_prism_eval("[].push 1")
+ end
+
+ def test_BlockArgumentNode
+ assert_prism_eval("1.then(&:to_s)")
+
+ # Test anonymous block forwarding
+ assert_prism_eval(<<~RUBY)
+ o = Object.new
+ def o.foo(&) = yield
+ def o.bar(&) = foo(&)
+
+ o.bar { :ok }
+ RUBY
+ end
+
+ def test_BlockLocalVariableNode
+ assert_prism_eval(<<-CODE
+ pm_var = "outer scope variable"
+
+ 1.times { |;pm_var| pm_var = "inner scope variable"; pm_var }
+ CODE
+ )
+
+ assert_prism_eval(<<-CODE
+ pm_var = "outer scope variable"
+
+ 1.times { |;pm_var| pm_var = "inner scope variable"; pm_var }
+ pm_var
+ CODE
+ )
+ end
+
+ def test_CallNode
+ assert_prism_eval("to_s")
+
+ # with arguments
+ assert_prism_eval("eval '1'")
+
+ # with arguments and popped
+ assert_prism_eval("eval '1'; 1")
+
+ # With different types of calling arguments
+ assert_prism_eval(<<-CODE)
+ def self.prism_test_call_node_double_splat(**); end
+ prism_test_call_node_double_splat(b: 1, **{})
+ CODE
+ assert_prism_eval(<<-CODE)
+ prism_test_call_node_double_splat(:b => 1)
+ CODE
+
+ assert_prism_eval(<<-CODE)
+ def self.prism_test_call_node_splat(*); end
+ prism_test_call_node_splat(*[], 1)
+ CODE
+
+ assert_prism_eval("prism_test_call_node_splat(*[], 1, 2)")
+
+ assert_prism_eval(<<~RUBY)
+ def self.prism_test_call_node_splat_and_double_splat(a, b, **opts); end
+ prism_test_call_node_splat_and_double_splat(*[1], 2, **{})
+ RUBY
+
+ assert_prism_eval(<<-CODE)
+ class Foo
+ def []=(a, b)
+ 1234
+ end
+ end
+
+ def self.foo(i, j)
+ tbl = Foo.new
+ tbl[i] = j
+ end
+ foo(1, 2)
+ CODE
+
+ assert_prism_eval(<<-CODE)
+ class Foo
+ def i=(a)
+ 1234
+ end
+ end
+
+ def self.foo(j)
+ tbl = Foo.new
+ tbl.i = j
+ end
+ foo(1)
+ CODE
+
+ assert_prism_eval(<<-CODE)
+ foo = Object.new
+ def foo.[]=(k,v); 42; end
+ foo.[]=(1,2)
+ CODE
+
+ # With splat inside of []=
+ assert_prism_eval(<<~RUBY)
+ obj = Object.new
+ def obj.[]=(a, b); 10; end
+ obj[*[1]] = 3
+ RUBY
+
+ assert_prism_eval(<<-CODE)
+ def self.prism_opt_var_trail_hash(a = nil, *b, c, **d); end
+ prism_opt_var_trail_hash("a")
+ prism_opt_var_trail_hash("a", c: 1)
+ prism_opt_var_trail_hash("a", "b")
+ prism_opt_var_trail_hash("a", "b", "c")
+ prism_opt_var_trail_hash("a", "b", "c", c: 1)
+ prism_opt_var_trail_hash("a", "b", "c", "c" => 0, c: 1)
+ CODE
+
+ assert_prism_eval(<<-CODE)
+ def self.foo(*args, **kwargs) = [args, kwargs]
+
+ [
+ foo(2 => 3),
+ foo([] => 42),
+ foo(a: 42, b: 61),
+ foo(1, 2, 3, a: 42, "b" => 61),
+ foo(:a => 42, :b => 61),
+ ]
+ CODE
+
+ assert_prism_eval(<<-CODE)
+ class PrivateMethod
+ def initialize
+ self.instance_var
+ end
+ private
+ attr_accessor :instance_var
+ end
+ pm = PrivateMethod.new
+ pm.send(:instance_var)
+ CODE
+
+ # Testing safe navigation operator
+ assert_prism_eval(<<-CODE)
+ def self.test_prism_call_node
+ if [][0]&.first
+ 1
+ end
+ end
+ test_prism_call_node
+ CODE
+
+ # Test opt_str_freeze instruction when calling #freeze on a string literal
+ assert_prism_eval(<<~RUBY)
+ "foo".freeze.equal?("foo".freeze)
+ RUBY
+ # Test encoding in opt_str_freeze
+ assert_prism_eval(<<~'RUBY', raw: true)
+ # -*- coding: us-ascii -*-
+ "\xff".freeze.encoding
+ RUBY
+
+ # Test opt_aref_with instruction when calling [] with a string
+ assert_prism_eval(<<~RUBY)
+ ObjectSpace.count_objects
+
+ h = {"abc" => 1}
+ before = ObjectSpace.count_objects[:T_STRING]
+ 5.times{ h["abc"] }
+ after = ObjectSpace.count_objects[:T_STRING]
+
+ before == after
+ RUBY
+
+ # Test opt_aset_with instruction when calling []= with a string key
+ assert_prism_eval(<<~RUBY)
+ ObjectSpace.count_objects
+
+ h = {"abc" => 1}
+ before = ObjectSpace.count_objects[:T_STRING]
+ 5.times{ h["abc"] = 2}
+ after = ObjectSpace.count_objects[:T_STRING]
+
+ before == after
+ RUBY
+ end
+
+ def test_CallAndWriteNode
+ assert_prism_eval(<<-CODE
+ class PrismTestSubclass; end
+ def PrismTestSubclass.test_call_and_write_node; end;
+ PrismTestSubclass.test_call_and_write_node &&= 1
+ CODE
+ )
+
+ assert_prism_eval(<<-CODE
+ def PrismTestSubclass.test_call_and_write_node
+ "str"
+ end
+ def PrismTestSubclass.test_call_and_write_node=(val)
+ val
+ end
+ PrismTestSubclass.test_call_and_write_node &&= 1
+ CODE
+ )
+
+ assert_prism_eval(<<-CODE
+ def self.test_call_and_write_node; end;
+ self.test_call_and_write_node &&= 1
+ CODE
+ )
+
+ assert_prism_eval(<<-CODE
+ def self.test_call_and_write_node
+ "str"
+ end
+ def self.test_call_and_write_node=(val)
+ val
+ end
+ self.test_call_and_write_node &&= 1
+ CODE
+ )
+
+ assert_prism_eval(<<-CODE)
+ def self.test_prism_call_node; end
+ def self.test_prism_call_node=(val)
+ val
+ end
+ self&.test_prism_call_node &&= 1
+ CODE
+
+ assert_prism_eval(<<-CODE)
+ def self.test_prism_call_node
+ 2
+ end
+ def self.test_prism_call_node=(val)
+ val
+ end
+ self&.test_prism_call_node &&= 1
+ CODE
+ end
+
+ def test_CallOrWriteNode
+ assert_prism_eval(<<-CODE
+ class PrismTestSubclass; end
+ def PrismTestSubclass.test_call_or_write_node; end;
+ def PrismTestSubclass.test_call_or_write_node=(val)
+ val
+ end
+ PrismTestSubclass.test_call_or_write_node ||= 1
+ CODE
+ )
+
+ assert_prism_eval(<<-CODE
+ def PrismTestSubclass.test_call_or_write_node
+ "str"
+ end
+ PrismTestSubclass.test_call_or_write_node ||= 1
+ CODE
+ )
+
+ assert_prism_eval(<<-CODE
+ def self.test_call_or_write_node; end;
+ def self.test_call_or_write_node=(val)
+ val
+ end
+ self.test_call_or_write_node ||= 1
+ CODE
+ )
+
+ assert_prism_eval(<<-CODE
+ def self.test_call_or_write_node
+ "str"
+ end
+ self.test_call_or_write_node ||= 1
+ CODE
+ )
+
+ assert_prism_eval(<<-CODE)
+ def self.test_prism_call_node
+ 2
+ end
+ def self.test_prism_call_node=(val)
+ val
+ end
+ self&.test_prism_call_node ||= 1
+ CODE
+
+ assert_prism_eval(<<-CODE)
+ def self.test_prism_call_node; end
+ def self.test_prism_call_node=(val)
+ val
+ end
+ self&.test_prism_call_node ||= 1
+ CODE
+ end
+
+ def test_CallOperatorWriteNode
+ assert_prism_eval(<<-CODE
+ class PrismTestSubclass; end
+ def PrismTestSubclass.test_call_operator_write_node
+ 2
+ end
+ def PrismTestSubclass.test_call_operator_write_node=(val)
+ val
+ end
+ PrismTestSubclass.test_call_operator_write_node += 1
+ CODE
+ )
+ end
+
+ def test_ForwardingArgumentsNode
+ assert_prism_eval(<<-CODE)
+ def prism_test_forwarding_arguments_node(...); end;
+ def prism_test_forwarding_arguments_node1(...)
+ prism_test_forwarding_arguments_node(...)
+ end
+ CODE
+
+ assert_prism_eval(<<-CODE)
+ def prism_test_forwarding_arguments_node(...); end;
+ def prism_test_forwarding_arguments_node1(a, ...)
+ prism_test_forwarding_arguments_node(1,2, 3, ...)
+ end
+ CODE
+
+ assert_prism_eval(<<~RUBY)
+ o = Object.new
+ def o.bar(a, b, c) = [a, b, c]
+ def o.foo(...) = 1.times { bar(...) }
+
+ o.foo(1, 2, 3)
+ RUBY
+ end
+
+ def test_ForwardingSuperNode
+ assert_prism_eval("class Forwarding; def to_s; super; end; end")
+ assert_prism_eval("class Forwarding; def eval(code); super { code }; end; end")
+ assert_prism_eval(<<-CODE)
+ class A
+ def initialize(a, b)
+ end
+ end
+
+ class B < A
+ attr_reader :res
+ def initialize(a, b, *)
+ super
+ @res = [a, b]
+ end
+ end
+
+ B.new(1, 2).res
+ CODE
+ end
+
+ def test_KeywordHashNode
+ assert_prism_eval("[a: [:b, :c]]")
+ end
+
+ def test_SuperNode
+ assert_prism_eval("def to_s; super 1; end")
+ assert_prism_eval("def to_s; super(); end")
+ assert_prism_eval("def to_s; super('a', :b, [1,2,3]); end")
+ assert_prism_eval("def to_s; super(1, 2, 3, &:foo); end")
+ end
+
+ ############################################################################
+ # Methods / parameters #
+ ############################################################################
+
+ def test_AliasGlobalVariableNode
+ assert_prism_eval("alias $prism_foo $prism_bar")
+ end
+
+ def test_AliasMethodNode
+ assert_prism_eval("alias :prism_a :to_s")
+ end
+
+ def test_BlockParameterNode
+ assert_prism_eval("def prism_test_block_parameter_node(&bar) end")
+ assert_prism_eval("->(b, c=1, *d, e, &f){}")
+
+ # Test BlockParameterNode with no name
+ assert_prism_eval("->(&){}")
+ assert_prism_eval("def prism_test_block_parameter_node(&); end")
+ end
+
+ def test_BlockParametersNode
+ assert_prism_eval("Object.tap { || }")
+ assert_prism_eval("[1].map { |num| num }")
+ assert_prism_eval("[1].map { |a; b| b = 2; a + b}")
+
+ # Test block parameters with multiple _
+ assert_prism_eval(<<~RUBY)
+ [[1, 2, 3, 4, 5, 6]].map { |(_, _, _, _, _, _)| _ }
+ RUBY
+ end
+
+ def test_FowardingParameterNode
+ assert_prism_eval("def prism_test_forwarding_parameter_node(...); end")
+ end
+
+ def test_KeywordRestParameterNode
+ assert_prism_eval("def prism_test_keyword_rest_parameter_node(a, **b); end")
+ assert_prism_eval("Object.tap { |**| }")
+
+ # Test that KeywordRestParameterNode creates a copy
+ assert_prism_eval(<<~RUBY)
+ hash = {}
+ o = Object.new
+ def o.foo(**a) = a[:foo] = 1
+
+ o.foo(**hash)
+ hash
+ RUBY
+ end
+
+ def test_NoKeywordsParameterNode
+ assert_prism_eval("def prism_test_no_keywords(**nil); end")
+ assert_prism_eval("def prism_test_no_keywords(a, b = 2, **nil); end")
+ end
+
+ def test_OptionalParameterNode
+ assert_prism_eval("def prism_test_optional_param_node(bar = nil); end")
+ end
+
+ def test_OptionalKeywordParameterNode
+ assert_prism_eval("def prism_test_optional_keyword_param_node(bar: nil); end")
+
+ # Test with optional argument and method call in OptionalKeywordParameterNode
+ assert_prism_eval(<<~RUBY)
+ o = Object.new
+ def o.foo = 1
+ def o.bar(a = nil, b: foo) = b
+ o.bar
+ RUBY
+ end
+
+ def test_ParametersNode
+ assert_prism_eval("def prism_test_parameters_node(bar, baz); end")
+ assert_prism_eval("def prism_test_parameters_node(a, b = 2); end")
+ end
+
+ def test_RequiredParameterNode
+ assert_prism_eval("def prism_test_required_param_node(bar); end")
+ assert_prism_eval("def prism_test_required_param_node(foo, bar); end")
+ end
+
+ def test_RequiredKeywordParameterNode
+ assert_prism_eval("def prism_test_required_param_node(bar:); end")
+ assert_prism_eval("def prism_test_required_param_node(foo:, bar:); end")
+ assert_prism_eval("-> a, b = 1, c:, d:, &e { a }")
+ end
+
+ def test_RestParameterNode
+ assert_prism_eval("def prism_test_rest_parameter_node(*a); end")
+ end
+
+ def test_UndefNode
+ assert_prism_eval("def prism_undef_node_1; end; undef prism_undef_node_1")
+ assert_prism_eval(<<-HERE
+ def prism_undef_node_2
+ end
+ def prism_undef_node_3
+ end
+ undef prism_undef_node_2, prism_undef_node_3
+ HERE
+ )
+ assert_prism_eval(<<-HERE
+ def prism_undef_node_4
+ end
+ undef :'prism_undef_node_#{4}'
+ HERE
+ )
+ end
+
+ ############################################################################
+ # Pattern matching #
+ ############################################################################
+
+ def test_AlternationPatternNode
+ assert_prism_eval("1 in 1 | 2")
+ assert_prism_eval("1 in 2 | 1")
+ assert_prism_eval("1 in 2 | 3 | 4 | 1")
+ assert_prism_eval("1 in 2 | 3")
+ end
+
+ def test_ArrayPatternNode
+ assert_prism_eval("[] => []")
+
+ ["in", "=>"].each do |operator|
+ ["", "Array"].each do |constant|
+ assert_prism_eval("[1, 2, 3] #{operator} #{constant}[1, 2, 3]")
+
+ assert_prism_eval("[1, 2, 3] #{operator} #{constant}[*]")
+ assert_prism_eval("[1, 2, 3] #{operator} #{constant}[1, *]")
+ assert_prism_eval("[1, 2, 3] #{operator} #{constant}[1, 2, *]")
+ assert_prism_eval("[1, 2, 3] #{operator} #{constant}[1, 2, 3, *]")
+
+ assert_prism_eval("[1, 2, 3] #{operator} #{constant}[*foo]")
+ assert_prism_eval("[1, 2, 3] #{operator} #{constant}[1, *foo]")
+ assert_prism_eval("[1, 2, 3] #{operator} #{constant}[1, 2, *foo]")
+ assert_prism_eval("[1, 2, 3] #{operator} #{constant}[1, 2, 3, *foo]")
+
+ assert_prism_eval("[1, 2, 3] #{operator} #{constant}[*, 3]")
+ assert_prism_eval("[1, 2, 3] #{operator} #{constant}[*, 2, 3]")
+ assert_prism_eval("[1, 2, 3] #{operator} #{constant}[*, 1, 2, 3]")
+
+ assert_prism_eval("[1, 2, 3] #{operator} #{constant}[*foo, 3]")
+ assert_prism_eval("[1, 2, 3] #{operator} #{constant}[*foo, 2, 3]")
+ assert_prism_eval("[1, 2, 3] #{operator} #{constant}[*foo, 1, 2, 3]")
+ end
+ end
+
+ assert_prism_eval("begin; Object.new => [1, 2, 3]; rescue NoMatchingPatternError; true; end")
+ assert_prism_eval("begin; [1, 2, 3] => Object[1, 2, 3]; rescue NoMatchingPatternError; true; end")
+ end
+
+ def test_CapturePatternNode
+ assert_prism_eval("[1] => [Integer => foo]")
+ end
+
+ def test_CaseMatchNode
+ assert_prism_eval(<<~RUBY)
+ case [1, 2, 3]
+ in [1, 2, 3]
+ 4
+ end
+ RUBY
+
+ assert_prism_eval(<<~RUBY)
+ case { a: 5, b: 6 }
+ in [1, 2, 3]
+ 4
+ in { a: 5, b: 6 }
+ 7
+ end
+ RUBY
+
+ assert_prism_eval(<<~RUBY)
+ case [1, 2, 3, 4]
+ in [1, 2, 3]
+ 4
+ in { a: 5, b: 6 }
+ 7
+ else
+ end
+ RUBY
+
+ assert_prism_eval(<<~RUBY)
+ case [1, 2, 3, 4]
+ in [1, 2, 3]
+ 4
+ in { a: 5, b: 6 }
+ 7
+ else
+ 8
+ end
+ RUBY
+
+ assert_prism_eval(<<~RUBY)
+ case [1, 2, 3]
+ in [1, 2, 3] unless to_s
+ in [1, 2, 3] if to_s.nil?
+ in [1, 2, 3]
+ true
+ end
+ RUBY
+ end
+
+ def test_FindPatternNode
+ ["in", "=>"].each do |operator|
+ ["", "Array"].each do |constant|
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 1, 2, 3, 4, 5, *]")
+
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 1, *]")
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 3, *]")
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 5, *]")
+
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 1, 2, *]")
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 2, 3, *]")
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 3, 4, *]")
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 4, 5, *]")
+
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 1, 2, 3, *]")
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 2, 3, 4, *]")
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 3, 4, 5, *]")
+
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 1, 2, 3, 4, *]")
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 2, 3, 4, 5, *]")
+
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*foo, 3, *]")
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*foo, 3, 4, *]")
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*foo, 3, 4, 5, *]")
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*foo, 1, 2, 3, 4, *]")
+
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 3, *foo]")
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 3, 4, *foo]")
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 3, 4, 5, *foo]")
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 1, 2, 3, 4, *foo]")
+
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*foo, 3, *bar]")
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*foo, 3, 4, *bar]")
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*foo, 3, 4, 5, *bar]")
+ assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*foo, 1, 2, 3, 4, *bar]")
+ end
+ end
+
+ assert_prism_eval("[1, [2, [3, [4, [5]]]]] => [*, [*, [*, [*, [*]]]]]")
+ assert_prism_eval("[1, [2, [3, [4, [5]]]]] => [1, [2, [3, [4, [5]]]]]")
+
+ assert_prism_eval("begin; Object.new => [*, 2, *]; rescue NoMatchingPatternError; true; end")
+ assert_prism_eval("begin; [1, 2, 3] => Object[*, 2, *]; rescue NoMatchingPatternError; true; end")
+ end
+
+ def test_HashPatternNode
+ assert_prism_eval("{} => {}")
+
+ [["{ ", " }"], ["Hash[", "]"]].each do |(prefix, suffix)|
+ assert_prism_eval("{} => #{prefix} **nil #{suffix}")
+
+ assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} a: 1 #{suffix}")
+ assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} a: 1, b: 2 #{suffix}")
+ assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} b: 2, c: 3 #{suffix}")
+ assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} a: 1, b: 2, c: 3 #{suffix}")
+
+ assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} ** #{suffix}")
+ assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} a: 1, ** #{suffix}")
+ assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} a: 1, b: 2, ** #{suffix}")
+ assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} b: 2, c: 3, ** #{suffix}")
+ assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} a: 1, b: 2, c: 3, ** #{suffix}")
+
+ assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} **foo #{suffix}")
+ assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} a: 1, **foo #{suffix}")
+ assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} a: 1, b: 2, **foo #{suffix}")
+ assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} b: 2, c: 3, **foo #{suffix}")
+ assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} a: 1, b: 2, c: 3, **foo #{suffix}")
+
+ assert_prism_eval("{ a: 1 } => #{prefix} a: 1, **nil #{suffix}")
+ assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} a: 1, b: 2, c: 3, **nil #{suffix}")
+ end
+
+ assert_prism_eval("{ a: { b: { c: 1 } } } => { a: { b: { c: 1 } } }")
+ end
+
+ def test_MatchPredicateNode
+ assert_prism_eval("1 in 1")
+ assert_prism_eval("1.0 in 1.0")
+ assert_prism_eval("1i in 1i")
+ assert_prism_eval("1r in 1r")
+
+ assert_prism_eval("\"foo\" in \"foo\"")
+ assert_prism_eval("\"foo \#{1}\" in \"foo \#{1}\"")
+
+ assert_prism_eval("false in false")
+ assert_prism_eval("nil in nil")
+ assert_prism_eval("self in self")
+ assert_prism_eval("true in true")
+
+ assert_prism_eval("5 in 0..10")
+ assert_prism_eval("5 in 0...10")
+
+ assert_prism_eval("[\"5\"] in %w[5]")
+
+ assert_prism_eval("Prism in Prism")
+ assert_prism_eval("Prism in ::Prism")
+
+ assert_prism_eval(":prism in :prism")
+ assert_prism_eval("%s[prism\#{1}] in %s[prism\#{1}]")
+ assert_prism_eval("\"foo\" in /.../")
+ assert_prism_eval("\"foo1\" in /...\#{1}/")
+ assert_prism_eval("4 in ->(v) { v.even? }")
+
+ assert_prism_eval("5 in foo")
+
+ assert_prism_eval("1 in 2")
+ end
+
+ def test_MatchRequiredNode
+ assert_prism_eval("1 => 1")
+ assert_prism_eval("1.0 => 1.0")
+ assert_prism_eval("1i => 1i")
+ assert_prism_eval("1r => 1r")
+
+ assert_prism_eval("\"foo\" => \"foo\"")
+ assert_prism_eval("\"foo \#{1}\" => \"foo \#{1}\"")
+
+ assert_prism_eval("false => false")
+ assert_prism_eval("nil => nil")
+ assert_prism_eval("true => true")
+
+ assert_prism_eval("5 => 0..10")
+ assert_prism_eval("5 => 0...10")
+
+ assert_prism_eval("[\"5\"] => %w[5]")
+
+ assert_prism_eval(":prism => :prism")
+ assert_prism_eval("%s[prism\#{1}] => %s[prism\#{1}]")
+ assert_prism_eval("\"foo\" => /.../")
+ assert_prism_eval("\"foo1\" => /...\#{1}/")
+ assert_prism_eval("4 => ->(v) { v.even? }")
+
+ assert_prism_eval("5 => foo")
+ end
+
+ def test_PinnedExpressionNode
+ assert_prism_eval("4 in ^(4)")
+ end
+
+ def test_PinnedVariableNode
+ assert_prism_eval("module Prism; @@prism = 1; 1 in ^@@prism; end")
+ assert_prism_eval("module Prism; @prism = 1; 1 in ^@prism; end")
+ assert_prism_eval("$prism = 1; 1 in ^$prism")
+ assert_prism_eval("prism = 1; 1 in ^prism")
+ end
+
+ ############################################################################
+ # Miscellaneous #
+ ############################################################################
+
+ def test_eval
+ assert_prism_eval("eval('1 + 1')", raw: true)
+ assert_prism_eval("a = 1; eval('a + 1')", raw: true)
+
+ assert_prism_eval(<<~CODE, raw: true)
+ def prism_eval_splat(**bar)
+ eval("bar")
+ end
+ prism_eval_splat(bar: 10)
+ CODE
+
+ assert_prism_eval(<<~CODE, raw: true)
+ def prism_eval_keywords(baz:)
+ eval("baz")
+ end
+ prism_eval_keywords(baz: 10)
+ CODE
+
+ assert_prism_eval(<<~CODE, raw: true)
+ [1].each do |a|
+ [2].each do |b|
+ c = 3
+ eval("a + b + c")
+ end
+ end
+ CODE
+
+ assert_prism_eval(<<~CODE, raw: true)
+ def prism_eval_binding(b)
+ eval("bar", b)
+ end
+
+ bar = :ok
+ prism_eval_binding(binding)
+ CODE
+ end
+
+ def test_ScopeNode
+ assert_separately(%w[], <<~'RUBY')
+ def compare_eval(source)
+ ruby_eval = RubyVM::InstructionSequence.compile("module A; " + source + "; end").eval
+ prism_eval = RubyVM::InstructionSequence.compile_prism("module B; " + source + "; end").eval
+
+ assert_equal ruby_eval, prism_eval
+ end
+
+ def assert_prism_eval(source)
+ $VERBOSE, verbose_bak = nil, $VERBOSE
+
+ begin
+ compare_eval(source)
+
+ # Test "popped" functionality
+ compare_eval("#{source}; 1")
+ ensure
+ $VERBOSE = verbose_bak
+ end
+ end
+
+ assert_prism_eval("a = 1; 1.times do; { a: }; end")
+ assert_prism_eval("a = 1; def foo(a); a; end")
+ RUBY
+ end
+
+ ############################################################################
+ # Errors #
+ ############################################################################
+
+ def test_MissingNode
+ # TODO
+ end
+
+ ############################################################################
+ # Encoding #
+ ############################################################################
+
+ def test_encoding
+ assert_prism_eval('"però"')
+ assert_prism_eval(":però")
+ end
+
+ def test_parse_file
+ assert_nothing_raised do
+ RubyVM::InstructionSequence.compile_file_prism(__FILE__)
+ end
+
+ error = assert_raise Errno::ENOENT do
+ RubyVM::InstructionSequence.compile_file_prism("idontexist.rb")
+ end
+
+ assert_equal "No such file or directory - idontexist.rb", error.message
+
+ assert_raise TypeError do
+ RubyVM::InstructionSequence.compile_file_prism(nil)
+ end
+ end
+
+ private
+
+ def compare_eval(source, raw:, location:)
+ source = raw ? source : "class Prism::TestCompilePrism\n#{source}\nend"
+
+ ruby_eval = RubyVM::InstructionSequence.compile(source).eval
+ prism_eval = RubyVM::InstructionSequence.compile_prism(source).eval
+
+ if ruby_eval.is_a? Proc
+ assert_equal ruby_eval.class, prism_eval.class, "@#{location.path}:#{location.lineno}"
+ else
+ assert_equal ruby_eval, prism_eval, "@#{location.path}:#{location.lineno}"
+ end
+ end
+
+ def assert_prism_eval(source, raw: false)
+ location = caller_locations(1, 1).first
+ $VERBOSE, verbose_bak = nil, $VERBOSE
+
+ begin
+ compare_eval(source, raw:, location:)
+
+ # Test "popped" functionality
+ compare_eval("#{source}; 1", raw:, location:)
+ ensure
+ $VERBOSE = verbose_bak
+ end
+ end
+ end
+end
diff --git a/test/ruby/test_complex.rb b/test/ruby/test_complex.rb
index edbdffd069..c0cfb73235 100644
--- a/test/ruby/test_complex.rb
+++ b/test/ruby/test_complex.rb
@@ -526,6 +526,71 @@ class Complex_Test < Test::Unit::TestCase
r = c ** Rational(-2,3)
assert_in_delta(0.432, r.real, 0.001)
assert_in_delta(-0.393, r.imag, 0.001)
+ end
+
+ def test_expt_for_special_angle
+ c = Complex(1, 0) ** 100000000000000000000000000000000
+ assert_equal(Complex(1, 0), c)
+
+ c = Complex(-1, 0) ** 10000000000000000000000000000000
+ assert_equal(Complex(1, 0), c)
+
+ c = Complex(-1, 0) ** 10000000000000000000000000000001
+ assert_equal(Complex(-1, 0), c)
+
+ c = Complex(0, 1) ** 100000000000000000000000000000000
+ assert_equal(Complex(1, 0), c)
+
+ c = Complex(0, 1) ** 100000000000000000000000000000001
+ assert_equal(Complex(0, 1), c)
+
+ c = Complex(0, 1) ** 100000000000000000000000000000002
+ assert_equal(Complex(-1, 0), c)
+
+ c = Complex(0, 1) ** 100000000000000000000000000000003
+ assert_equal(Complex(0, -1), c)
+
+ c = Complex(0, -1) ** 100000000000000000000000000000000
+ assert_equal(Complex(1, 0), c)
+
+ c = Complex(0, -1) ** 100000000000000000000000000000001
+ assert_equal(Complex(0, -1), c)
+
+ c = Complex(0, -1) ** 100000000000000000000000000000002
+ assert_equal(Complex(-1, 0), c)
+
+ c = Complex(0, -1) ** 100000000000000000000000000000003
+ assert_equal(Complex(0, 1), c)
+
+ c = Complex(1, 1) ** 1
+ assert_equal(Complex(1, 1), c)
+
+ c = Complex(1, 1) ** 2
+ assert_equal(Complex(0, 2), c)
+
+ c = Complex(1, 1) ** 3
+ assert_equal(Complex(-2, 2), c)
+
+ c = Complex(1, 1) ** 4
+ assert_equal(Complex(-4, 0), c)
+
+ c = Complex(1, 1) ** 5
+ assert_equal(Complex(-4, -4), c)
+
+ c = Complex(1, 1) ** 6
+ assert_equal(Complex(0, -8), c)
+
+ c = Complex(1, 1) ** 7
+ assert_equal(Complex(8, -8), c)
+
+ c = Complex(-2, -2) ** 3
+ assert_equal(Complex(16, -16), c)
+
+ c = Complex(2, -2) ** 3
+ assert_equal(Complex(-16, -16), c)
+
+ c = Complex(-2, 2) ** 3
+ assert_equal(Complex(16, 16), c)
c = Complex(0.0, -888888888888888.0)**8888
assert_not_predicate(c.real, :nan?)
@@ -915,31 +980,27 @@ class Complex_Test < Test::Unit::TestCase
}
end
- def test_Complex_without_exception
- assert_nothing_raised(ArgumentError){
- assert_equal(nil, Complex('5x', exception: false))
- }
- assert_nothing_raised(ArgumentError){
- assert_equal(nil, Complex(nil, exception: false))
- }
- assert_nothing_raised(ArgumentError){
- assert_equal(nil, Complex(Object.new, exception: false))
- }
- assert_nothing_raised(ArgumentError){
- assert_equal(nil, Complex(1, nil, exception: false))
- }
- assert_nothing_raised(ArgumentError){
- assert_equal(nil, Complex(1, Object.new, exception: false))
- }
+ def assert_complex_with_exception(error, *args, message: "")
+ assert_raise(error, message) do
+ Complex(*args, exception: true)
+ end
+ assert_nothing_raised(error, message) do
+ assert_nil(Complex(*args, exception: false))
+ assert_nil($!)
+ end
+ end
+
+ def test_Complex_with_exception
+ assert_complex_with_exception(ArgumentError, '5x')
+ assert_complex_with_exception(TypeError, nil)
+ assert_complex_with_exception(TypeError, Object.new)
+ assert_complex_with_exception(TypeError, 1, nil)
+ assert_complex_with_exception(TypeError, 1, Object.new)
o = Object.new
def o.to_c; raise; end
- assert_nothing_raised(ArgumentError){
- assert_equal(nil, Complex(o, exception: false))
- }
- assert_nothing_raised(ArgumentError){
- assert_equal(nil, Complex(1, o, exception: false))
- }
+ assert_complex_with_exception(RuntimeError, o)
+ assert_complex_with_exception(TypeError, 1, o)
end
def test_respond
@@ -993,6 +1054,29 @@ class Complex_Test < Test::Unit::TestCase
assert_raise(RangeError){Rational(Complex(3,2))}
end
+ def test_to_r_with_float
+ assert_equal(Rational(3), Complex(3, 0.0).to_r)
+ assert_raise(RangeError){Complex(3, 1.0).to_r}
+ end
+
+ def test_to_r_with_numeric_obj
+ c = Class.new(Numeric)
+
+ num = 0
+ c.define_method(:to_s) { num.to_s }
+ c.define_method(:==) { num == it }
+ c.define_method(:<) { num < it }
+
+ o = c.new
+ assert_equal(Rational(3), Complex(3, o).to_r)
+
+ num = 1
+ assert_raise(RangeError){Complex(3, o).to_r}
+
+ c.define_method(:to_r) { 0r }
+ assert_equal(Rational(3), Complex(3, o).to_r)
+ end
+
def test_to_c
c = nil.to_c
assert_equal([0,0], [c.real, c.imag])
diff --git a/test/ruby/test_continuation.rb b/test/ruby/test_continuation.rb
index 8c62d20840..612dbf28c9 100644
--- a/test/ruby/test_continuation.rb
+++ b/test/ruby/test_continuation.rb
@@ -4,6 +4,10 @@ EnvUtil.suppress_warning {require 'continuation'}
require 'fiber'
class TestContinuation < Test::Unit::TestCase
+ def setup
+ omit 'requires callcc support' unless respond_to?(:callcc)
+ end
+
def test_create
assert_equal(:ok, callcc{:ok})
assert_equal(:ok, callcc{|c| c.call :ok})
diff --git a/test/ruby/test_data.rb b/test/ruby/test_data.rb
index b911776bde..bb38f8ec91 100644
--- a/test/ruby/test_data.rb
+++ b/test/ruby/test_data.rb
@@ -117,14 +117,31 @@ class TestData < Test::Unit::TestCase
assert_equal({foo: 1, bar: 2}, test.to_h)
assert_equal({"foo"=>"1", "bar"=>"2"}, test.to_h { [_1.to_s, _2.to_s] })
+ assert_equal([1, 2], test.deconstruct)
assert_equal({foo: 1, bar: 2}, test.deconstruct_keys(nil))
assert_equal({foo: 1}, test.deconstruct_keys(%i[foo]))
assert_equal({foo: 1}, test.deconstruct_keys(%i[foo baz]))
+ assert_equal({}, test.deconstruct_keys(%i[foo bar baz]))
assert_raise(TypeError) { test.deconstruct_keys(0) }
assert_kind_of(Integer, test.hash)
end
+ def test_hash
+ measure = Data.define(:amount, :unit)
+
+ assert_equal(measure[1, 'km'].hash, measure[1, 'km'].hash)
+ assert_not_equal(measure[1, 'km'].hash, measure[10, 'km'].hash)
+ assert_not_equal(measure[1, 'km'].hash, measure[1, 'm'].hash)
+ assert_not_equal(measure[1, 'km'].hash, measure[1.0, 'km'].hash)
+
+ # Structurally similar data class, but shouldn't be considered
+ # the same hash key
+ measurement = Data.define(:amount, :unit)
+
+ assert_not_equal(measure[1, 'km'].hash, measurement[1, 'km'].hash)
+ end
+
def test_inspect
klass = Data.define(:a)
o = klass.new(1)
diff --git a/test/ruby/test_default_gems.rb b/test/ruby/test_default_gems.rb
index d8e8226253..b82e304cbd 100644
--- a/test/ruby/test_default_gems.rb
+++ b/test/ruby/test_default_gems.rb
@@ -5,9 +5,11 @@ class TestDefaultGems < Test::Unit::TestCase
def self.load(file)
code = File.read(file, mode: "r:UTF-8:-", &:read)
+ # These regex patterns are from load_gemspec method of rbinstall.rb.
# - `git ls-files` is useless under ruby's repository
# - `2>/dev/null` works only on Unix-like platforms
- code.gsub!(/`git.*?`/, '""')
+ code.gsub!(/(?:`git[^\`]*`|%x\[git[^\]]*\])\.split\([^\)]*\)/m, '[]')
+ code.gsub!(/IO\.popen\(.*git.*?\)/, '[] || itself')
eval(code, binding, file)
end
diff --git a/test/ruby/test_defined.rb b/test/ruby/test_defined.rb
index b9bf939394..0505bdada6 100644
--- a/test/ruby/test_defined.rb
+++ b/test/ruby/test_defined.rb
@@ -127,6 +127,18 @@ class TestDefined < Test::Unit::TestCase
assert_equal nil, defined?($2)
end
+ def test_defined_assignment
+ assert_equal("assignment", defined?(a = 1))
+ assert_equal("assignment", defined?(a += 1))
+ assert_equal("assignment", defined?(a &&= 1))
+ assert_equal("assignment", eval('defined?(A = 1)'))
+ assert_equal("assignment", eval('defined?(A += 1)'))
+ assert_equal("assignment", eval('defined?(A &&= 1)'))
+ assert_equal("assignment", eval('defined?(A::B = 1)'))
+ assert_equal("assignment", eval('defined?(A::B += 1)'))
+ assert_equal("assignment", eval('defined?(A::B &&= 1)'))
+ end
+
def test_defined_literal
assert_equal("nil", defined?(nil))
assert_equal("true", defined?(true))
diff --git a/test/ruby/test_dir.rb b/test/ruby/test_dir.rb
index 65803d0bc5..2cc1c3ef4a 100644
--- a/test/ruby/test_dir.rb
+++ b/test/ruby/test_dir.rb
@@ -104,22 +104,23 @@ class TestDir < Test::Unit::TestCase
assert_raise(ArgumentError) { Dir.chdir }
ENV["HOME"] = pwd
Dir.chdir do
- assert_warning(/conflicting chdir during another chdir block/) { Dir.chdir(pwd) }
+ conflicting = /conflicting chdir during another chdir block\n^#{Regexp.quote(__FILE__)}:#{__LINE__-1}:/
+ assert_warning(conflicting) { Dir.chdir(pwd) }
- assert_warning(/conflicting chdir during another chdir block/) { Dir.chdir(@root) }
+ assert_warning(conflicting) { Dir.chdir(@root) }
assert_equal(@root, Dir.pwd)
- assert_warning(/conflicting chdir during another chdir block/) { Dir.chdir(pwd) }
+ assert_warning(conflicting) { Dir.chdir(pwd) }
assert_raise(RuntimeError) { Thread.new { Thread.current.report_on_exception = false; Dir.chdir(@root) }.join }
assert_raise(RuntimeError) { Thread.new { Thread.current.report_on_exception = false; Dir.chdir(@root) { } }.join }
- assert_warning(/conflicting chdir during another chdir block/) { Dir.chdir(pwd) }
+ assert_warning(conflicting) { Dir.chdir(pwd) }
- assert_warning(/conflicting chdir during another chdir block/) { Dir.chdir(@root) }
+ assert_warning(conflicting) { Dir.chdir(@root) }
assert_equal(@root, Dir.pwd)
- assert_warning(/conflicting chdir during another chdir block/) { Dir.chdir(pwd) }
+ assert_warning(conflicting) { Dir.chdir(pwd) }
Dir.chdir(@root) do
assert_equal(@root, Dir.pwd)
end
@@ -141,31 +142,59 @@ class TestDir < Test::Unit::TestCase
setup_envs
ENV["HOME"] = pwd
- root_dir.chdir do
- assert_warning(/conflicting chdir during another chdir block/) { dir.chdir }
- assert_warning(/conflicting chdir during another chdir block/) { root_dir.chdir }
+ ret = root_dir.chdir do |*a|
+ conflicting = /conflicting chdir during another chdir block\n^#{Regexp.quote(__FILE__)}:#{__LINE__-1}:/
+
+ assert_empty(a)
+
+ assert_warning(conflicting) { dir.chdir }
+ assert_warning(conflicting) { root_dir.chdir }
assert_equal(@root, Dir.pwd)
assert_raise(RuntimeError) { Thread.new { Thread.current.report_on_exception = false; dir.chdir }.join }
assert_raise(RuntimeError) { Thread.new { Thread.current.report_on_exception = false; dir.chdir{} }.join }
- assert_warning(/conflicting chdir during another chdir block/) { dir.chdir }
+ assert_warning(conflicting) { dir.chdir }
assert_equal(pwd, Dir.pwd)
- assert_warning(/conflicting chdir during another chdir block/) { root_dir.chdir }
+ assert_warning(conflicting) { root_dir.chdir }
assert_equal(@root, Dir.pwd)
- assert_warning(/conflicting chdir during another chdir block/) { dir.chdir }
+ assert_warning(conflicting) { dir.chdir }
root_dir.chdir do
assert_equal(@root, Dir.pwd)
end
assert_equal(pwd, Dir.pwd)
+
+ 42
end
+
+ assert_separately(["-", @root], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ root = ARGV.shift
+
+ $dir_warnings = []
+
+ def Warning.warn(message)
+ $dir_warnings << message
+ end
+
+ line2 = line1 = __LINE__; Dir.chdir(root) do
+ line2 = __LINE__; Dir.chdir
+ end
+
+ message = $dir_warnings.shift
+ assert_include(message, "#{__FILE__}:#{line2}:")
+ assert_include(message, "#{__FILE__}:#{line1}:")
+ assert_empty($dir_warnings)
+ end;
+
+ assert_equal(42, ret)
ensure
begin
- dir.chdir
+ assert_equal(0, dir.chdir)
rescue
abort("cannot return the original directory: #{ pwd }")
end
@@ -578,6 +607,40 @@ class TestDir < Test::Unit::TestCase
ENV.delete('USERPROFILE')
assert_equal("C:/ruby/homepath", Dir.home)
end
+
+ def test_home_at_startup_windows
+ env = {'HOME' => "C:\\ruby\\home"}
+ args = [env]
+ assert_separately(args, "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ assert_equal("C:/ruby/home", Dir.home)
+ end;
+
+ env['USERPROFILE'] = "C:\\ruby\\userprofile"
+ assert_separately(args, "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ assert_equal("C:/ruby/home", Dir.home)
+ end;
+
+ env['HOME'] = nil
+ assert_separately(args, "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ assert_equal("C:/ruby/userprofile", Dir.home)
+ end;
+
+ env['HOMEDRIVE'] = "C:"
+ env['HOMEPATH'] = "\\ruby\\homepath"
+ assert_separately(args, "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ assert_equal("C:/ruby/userprofile", Dir.home)
+ end;
+
+ env['USERPROFILE'] = nil
+ assert_separately(args, "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ assert_equal("C:/ruby/homepath", Dir.home)
+ end;
+ end
end
def test_home
diff --git a/test/ruby/test_dir_m17n.rb b/test/ruby/test_dir_m17n.rb
index 67bad8a514..cdf8b44ef2 100644
--- a/test/ruby/test_dir_m17n.rb
+++ b/test/ruby/test_dir_m17n.rb
@@ -56,7 +56,7 @@ class TestDir_M17N < Test::Unit::TestCase
return if Bug::File::Fs.fsname(Dir.tmpdir) == "apfs"
with_tmpdir {|d|
assert_separately(%w[-EASCII-8BIT], <<-'EOS', :chdir=>d)
- filename = "\xff".force_encoding("ASCII-8BIT") # invalid byte sequence as UTF-8
+ filename = "\xff".dup.force_encoding("ASCII-8BIT") # invalid byte sequence as UTF-8
File.open(filename, "w") {}
opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
ents = Dir.entries(".", **(opts||{}))
@@ -64,7 +64,7 @@ class TestDir_M17N < Test::Unit::TestCase
assert_include(ents, filename)
EOS
assert_separately(%w[-EUTF-8], <<-'EOS', :chdir=>d)
- filename = "\xff".force_encoding("UTF-8") # invalid byte sequence as UTF-8
+ filename = "\xff".dup.force_encoding("UTF-8") # invalid byte sequence as UTF-8
File.open(filename, "w") {}
opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
ents = Dir.entries(".", **(opts||{}))
@@ -77,7 +77,7 @@ class TestDir_M17N < Test::Unit::TestCase
def test_filename_as_bytes_extutf8
with_tmpdir {|d|
assert_separately(%w[-EUTF-8], <<-'EOS', :chdir=>d)
- filename = "\xc2\xa1".force_encoding("utf-8")
+ filename = "\xc2\xa1".dup.force_encoding("utf-8")
File.open(filename, "w") {}
opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
ents = Dir.entries(".", **(opts||{}))
@@ -85,9 +85,9 @@ class TestDir_M17N < Test::Unit::TestCase
EOS
assert_separately(%w[-EUTF-8], <<-'EOS', :chdir=>d)
if /mswin|mingw|darwin/ =~ RUBY_PLATFORM
- filename = "\x8f\xa2\xc2".force_encoding("euc-jp")
+ filename = "\x8f\xa2\xc2".dup.force_encoding("euc-jp")
else
- filename = "\xc2\xa1".force_encoding("euc-jp")
+ filename = "\xc2\xa1".dup.force_encoding("euc-jp")
end
assert_nothing_raised(Errno::ENOENT) do
open(filename) {}
@@ -96,8 +96,8 @@ class TestDir_M17N < Test::Unit::TestCase
# no meaning test on windows
unless /mswin|mingw|darwin/ =~ RUBY_PLATFORM
assert_separately(%W[-EUTF-8], <<-'EOS', :chdir=>d)
- filename1 = "\xc2\xa1".force_encoding("utf-8")
- filename2 = "\xc2\xa1".force_encoding("euc-jp")
+ filename1 = "\xc2\xa1".dup.force_encoding("utf-8")
+ filename2 = "\xc2\xa1".dup.force_encoding("euc-jp")
filename3 = filename1.encode("euc-jp")
filename4 = filename2.encode("utf-8")
assert_file.stat(filename1)
@@ -121,13 +121,13 @@ class TestDir_M17N < Test::Unit::TestCase
assert_include(ents, filename)
EOS
assert_separately(%w[-EUTF-8:EUC-JP], <<-'EOS', :chdir=>d)
- filename = "\xA4\xA2".force_encoding("euc-jp")
+ filename = "\xA4\xA2".dup.force_encoding("euc-jp")
opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
ents = Dir.entries(".", **(opts||{}))
assert_include(ents, filename)
EOS
assert_separately(%w[-EUTF-8:EUC-JP], <<-'EOS', :chdir=>d)
- filename = "\xA4\xA2".force_encoding("euc-jp")
+ filename = "\xA4\xA2".dup.force_encoding("euc-jp")
assert_nothing_raised(Errno::ENOENT) do
open(filename) {}
end
@@ -149,7 +149,7 @@ class TestDir_M17N < Test::Unit::TestCase
EOS
assert_separately(%w[-EUTF-8:EUC-JP], <<-'EOS', :chdir=>d)
filename1 = "\u2661" # WHITE HEART SUIT which is not representable in EUC-JP
- filename2 = "\xA4\xA2".force_encoding("euc-jp") # HIRAGANA LETTER A in EUC-JP
+ filename2 = "\xA4\xA2".dup.force_encoding("euc-jp") # HIRAGANA LETTER A in EUC-JP
opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
ents = Dir.entries(".", **(opts||{}))
assert_include(ents, filename1)
@@ -158,7 +158,7 @@ class TestDir_M17N < Test::Unit::TestCase
assert_separately(%w[-EUTF-8:EUC-JP], <<-'EOS', :chdir=>d)
filename1 = "\u2661" # WHITE HEART SUIT which is not representable in EUC-JP
filename2 = "\u3042" # HIRAGANA LETTER A which is representable in EUC-JP
- filename3 = "\xA4\xA2".force_encoding("euc-jp") # HIRAGANA LETTER A in EUC-JP
+ filename3 = "\xA4\xA2".dup.force_encoding("euc-jp") # HIRAGANA LETTER A in EUC-JP
assert_file.stat(filename1)
assert_file.stat(filename2)
assert_file.stat(filename3)
@@ -172,7 +172,7 @@ class TestDir_M17N < Test::Unit::TestCase
return if /cygwin/ =~ RUBY_PLATFORM
with_tmpdir {|d|
assert_separately(%w[-EEUC-JP], <<-'EOS', :chdir=>d)
- filename = "\xA4\xA2".force_encoding("euc-jp")
+ filename = "\xA4\xA2".dup.force_encoding("euc-jp")
File.open(filename, "w") {}
opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
ents = Dir.entries(".", **(opts||{}))
@@ -189,7 +189,7 @@ class TestDir_M17N < Test::Unit::TestCase
return if /cygwin/ =~ RUBY_PLATFORM
with_tmpdir {|d|
assert_separately(%w[-EEUC-JP], <<-'EOS', :chdir=>d)
- filename = "\xA4\xA2".force_encoding("euc-jp")
+ filename = "\xA4\xA2".dup.force_encoding("euc-jp")
File.open(filename, "w") {}
ents = Dir.entries(".")
if /darwin/ =~ RUBY_PLATFORM
@@ -200,7 +200,7 @@ class TestDir_M17N < Test::Unit::TestCase
assert_include(ents, filename)
EOS
assert_separately(%w[-EASCII-8BIT], <<-'EOS', :chdir=>d)
- filename = "\xA4\xA2".force_encoding('ASCII-8BIT')
+ filename = "\xA4\xA2".dup.force_encoding('ASCII-8BIT')
ents = Dir.entries(".")
unless ents.include?(filename)
case RUBY_PLATFORM
@@ -231,7 +231,7 @@ class TestDir_M17N < Test::Unit::TestCase
return if /cygwin/ =~ RUBY_PLATFORM
with_tmpdir {|d|
assert_separately(%w[-EEUC-JP], <<-'EOS', :chdir=>d)
- filename = "\xA4\xA2".force_encoding("euc-jp")
+ filename = "\xA4\xA2".dup.force_encoding("euc-jp")
File.open(filename, "w") {}
opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
ents = Dir.entries(".", **(opts||{}))
@@ -241,7 +241,7 @@ class TestDir_M17N < Test::Unit::TestCase
assert_include(ents, filename)
EOS
assert_separately(%w[-EEUC-JP:UTF-8], <<-'EOS', :chdir=>d)
- filename = "\u3042"
+ filename = "\u3042".dup
opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
ents = Dir.entries(".", **(opts||{}))
if /darwin/ =~ RUBY_PLATFORM
@@ -318,7 +318,7 @@ class TestDir_M17N < Test::Unit::TestCase
def test_glob_warning_opendir
with_enc_path do |dir|
- open("#{dir}/x", "w") {}
+ File.binwrite("#{dir}/x", "")
File.chmod(0300, dir)
next if File.readable?(dir)
assert_warning(/#{dir}/) do
@@ -329,7 +329,7 @@ class TestDir_M17N < Test::Unit::TestCase
def test_glob_warning_match_all
with_enc_path do |dir|
- open("#{dir}/x", "w") {}
+ File.binwrite("#{dir}/x", "")
File.chmod(0000, dir)
next if File.readable?(dir)
assert_warning(/#{dir}/) do
@@ -350,7 +350,7 @@ class TestDir_M17N < Test::Unit::TestCase
end
def test_glob_escape_multibyte
- name = "\x81\\".force_encoding(Encoding::Shift_JIS)
+ name = "\x81\\".dup.force_encoding(Encoding::Shift_JIS)
with_tmpdir do
open(name, "w") {} rescue next
match, = Dir.glob("#{name}*")
@@ -362,9 +362,9 @@ class TestDir_M17N < Test::Unit::TestCase
def test_glob_encoding
with_tmpdir do
list = %W"file_one.ext file_two.ext \u{6587 4ef6}1.txt \u{6587 4ef6}2.txt"
- list.each {|f| open(f, "w") {}}
- a = "file_one*".force_encoding Encoding::IBM437
- b = "file_two*".force_encoding Encoding::EUC_JP
+ list.each {|f| File.binwrite(f, "")}
+ a = "file_one*".dup.force_encoding Encoding::IBM437
+ b = "file_two*".dup.force_encoding Encoding::EUC_JP
assert_equal([a, b].map(&:encoding), Dir[a, b].map(&:encoding))
if Bug::File::Fs.fsname(Dir.pwd) == "apfs"
# High Sierra's APFS cannot use filenames with undefined character
@@ -375,7 +375,7 @@ class TestDir_M17N < Test::Unit::TestCase
Dir.mkdir(dir)
list << dir
bug12081 = '[ruby-core:73868] [Bug #12081]'
- a = "*".force_encoding("us-ascii")
+ a = "*".dup.force_encoding("us-ascii")
result = Dir[a].map {|n|
if n.encoding == Encoding::ASCII_8BIT ||
n.encoding == Encoding::ISO_8859_1 ||
diff --git a/test/ruby/test_econv.rb b/test/ruby/test_econv.rb
index 1aad0de347..1d0641e918 100644
--- a/test/ruby/test_econv.rb
+++ b/test/ruby/test_econv.rb
@@ -931,7 +931,7 @@ class TestEncodingConverter < Test::Unit::TestCase
def test_default_external
Encoding.list.grep(->(enc) {/\AISO-8859-\d+\z/i =~ enc.name}) do |enc|
- assert_separately(%W[--disable=gems -d - #{enc.name}], <<-EOS, ignore_stderr: true)
+ assert_separately(%W[-d - #{enc.name}], <<-EOS, ignore_stderr: true)
Encoding.default_external = ext = ARGV[0]
Encoding.default_internal = int ='utf-8'
assert_nothing_raised do
diff --git a/test/ruby/test_encoding.rb b/test/ruby/test_encoding.rb
index e1a5ac4a5f..f2c609a4cd 100644
--- a/test/ruby/test_encoding.rb
+++ b/test/ruby/test_encoding.rb
@@ -106,7 +106,7 @@ class TestEncoding < Test::Unit::TestCase
end
def test_errinfo_after_autoload
- assert_separately(%w[--disable=gems], "#{<<~"begin;"}\n#{<<~'end;'}")
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
bug9038 = '[ruby-core:57949] [Bug #9038]'
begin;
e = assert_raise_with_message(SyntaxError, /unknown regexp option - Q/, bug9038) {
diff --git a/test/ruby/test_enum.rb b/test/ruby/test_enum.rb
index f7c8f012d8..7503e06272 100644
--- a/test/ruby/test_enum.rb
+++ b/test/ruby/test_enum.rb
@@ -843,6 +843,8 @@ class TestEnumerable < Test::Unit::TestCase
end
def test_callcc
+ omit 'requires callcc support' unless respond_to?(:callcc)
+
assert_raise(RuntimeError) do
c = nil
@obj.sort_by {|x| callcc {|c2| c ||= c2 }; x }
diff --git a/test/ruby/test_enumerator.rb b/test/ruby/test_enumerator.rb
index 010c1e4969..7599d43463 100644
--- a/test/ruby/test_enumerator.rb
+++ b/test/ruby/test_enumerator.rb
@@ -127,6 +127,17 @@ class TestEnumerator < Test::Unit::TestCase
assert_equal([[1,5],[2,6],[3,7]], @obj.to_enum(:foo, 1, 2, 3).with_index(5).to_a)
end
+ def test_with_index_under_gc_compact_stress
+ omit "compaction doesn't work well on s390x" if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077
+ EnvUtil.under_gc_compact_stress do
+ assert_equal([[1, 0], [2, 1], [3, 2]], @obj.to_enum(:foo, 1, 2, 3).with_index.to_a)
+ assert_equal([[1, 5], [2, 6], [3, 7]], @obj.to_enum(:foo, 1, 2, 3).with_index(5).to_a)
+
+ s = 1 << (8 * 1.size - 2)
+ assert_equal([[1, s], [2, s + 1], [3, s + 2]], @obj.to_enum(:foo, 1, 2, 3).with_index(s).to_a)
+ end
+ end
+
def test_with_index_large_offset
bug8010 = '[ruby-dev:47131] [Bug #8010]'
s = 1 << (8*1.size-2)
@@ -244,6 +255,26 @@ class TestEnumerator < Test::Unit::TestCase
assert_equal(res, exc.result)
end
+ def test_stopiteration_rescue
+ e = [1].each
+ res = e.each {}
+ e.next
+ exc0 = assert_raise(StopIteration) { e.peek }
+ assert_include(exc0.backtrace.first, "test_enumerator.rb:#{__LINE__-1}:")
+ assert_nil(exc0.cause)
+ assert_equal(res, exc0.result)
+
+ exc1 = assert_raise(StopIteration) { e.next }
+ assert_include(exc1.backtrace.first, "test_enumerator.rb:#{__LINE__-1}:")
+ assert_same(exc0, exc1.cause)
+ assert_equal(res, exc1.result)
+
+ exc2 = assert_raise(StopIteration) { e.next }
+ assert_include(exc2.backtrace.first, "test_enumerator.rb:#{__LINE__-1}:")
+ assert_same(exc0, exc2.cause)
+ assert_equal(res, exc2.result)
+ end
+
def test_next_values
o = Object.new
def o.each
@@ -832,6 +863,21 @@ class TestEnumerator < Test::Unit::TestCase
assert_equal(33, chain.next)
end
+ def test_lazy_chain_under_gc_compact_stress
+ omit "compaction doesn't work well on s390x" if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077
+ EnvUtil.under_gc_compact_stress do
+ ea = (10..).lazy.select(&:even?).take(10)
+ ed = (20..).lazy.select(&:odd?)
+ chain = (ea + ed).select{|x| x % 3 == 0}
+ assert_equal(12, chain.next)
+ assert_equal(18, chain.next)
+ assert_equal(24, chain.next)
+ assert_equal(21, chain.next)
+ assert_equal(27, chain.next)
+ assert_equal(33, chain.next)
+ end
+ end
+
def test_chain_undef_methods
chain = [1].to_enum + [2].to_enum
meths = (chain.methods & [:feed, :next, :next_values, :peek, :peek_values])
@@ -907,11 +953,7 @@ class TestEnumerator < Test::Unit::TestCase
assert_equal(true, e.is_lambda)
end
- def test_product
- ##
- ## Enumerator::Product
- ##
-
+ def test_product_new
# 0-dimensional
e = Enumerator::Product.new
assert_instance_of(Enumerator::Product, e)
@@ -948,15 +990,16 @@ class TestEnumerator < Test::Unit::TestCase
e.each { |x,| heads << x }
assert_equal [1, 1, 2, 2, 3, 3], heads
+ # Any enumerable is 0 size
+ assert_equal(0, Enumerator::Product.new([], 1..).size)
+
# Reject keyword arguments
assert_raise(ArgumentError) {
Enumerator::Product.new(1..3, foo: 1, bar: 2)
}
+ end
- ##
- ## Enumerator.product
- ##
-
+ def test_s_product
# without a block
e = Enumerator.product(1..3, %w[a b])
assert_instance_of(Enumerator::Product, e)
@@ -983,9 +1026,21 @@ class TestEnumerator < Test::Unit::TestCase
assert_equal(nil, e.size)
assert_equal [[1, "a"], [1, "b"], [2, "a"], [2, "b"]], e.take(4)
+ assert_equal(0, Enumerator.product([], 1..).size)
+
# Reject keyword arguments
assert_raise(ArgumentError) {
Enumerator.product(1..3, foo: 1, bar: 2)
}
end
+
+ def test_freeze
+ e = 3.times.freeze
+ assert_raise(FrozenError) { e.next }
+ assert_raise(FrozenError) { e.next_values }
+ assert_raise(FrozenError) { e.peek }
+ assert_raise(FrozenError) { e.peek_values }
+ assert_raise(FrozenError) { e.feed 1 }
+ assert_raise(FrozenError) { e.rewind }
+ end
end
diff --git a/test/ruby/test_env.rb b/test/ruby/test_env.rb
index cdadeac148..4b5f18e7bb 100644
--- a/test/ruby/test_env.rb
+++ b/test/ruby/test_env.rb
@@ -612,8 +612,8 @@ class TestEnv < Test::Unit::TestCase
<<-"end;"
envvars_to_check = [
"foo\0bar",
- "#{'\xa1\xa1'}".force_encoding(Encoding::UTF_16LE),
- "foo".force_encoding(Encoding::ISO_2022_JP),
+ "#{'\xa1\xa1'}".dup.force_encoding(Encoding::UTF_16LE),
+ "foo".dup.force_encoding(Encoding::ISO_2022_JP),
]
envvars_to_check.each do |#{var_name}|
#{str_for_yielding_exception_class(code_str)}
diff --git a/test/ruby/test_eval.rb b/test/ruby/test_eval.rb
index af255c05c8..082d1dc03c 100644
--- a/test/ruby/test_eval.rb
+++ b/test/ruby/test_eval.rb
@@ -547,8 +547,8 @@ class TestEval < Test::Unit::TestCase
end
def test_eval_location_binding
- assert_equal(['(eval)', 1], eval("[__FILE__, __LINE__]", nil))
- assert_equal(['(eval)', 1], eval("[__FILE__, __LINE__]", binding))
+ assert_equal(["(eval at #{__FILE__}:#{__LINE__})", 1], eval("[__FILE__, __LINE__]", nil))
+ assert_equal(["(eval at #{__FILE__}:#{__LINE__})", 1], eval("[__FILE__, __LINE__]", binding))
assert_equal(['foo', 1], eval("[__FILE__, __LINE__]", nil, 'foo'))
assert_equal(['foo', 1], eval("[__FILE__, __LINE__]", binding, 'foo'))
assert_equal(['foo', 2], eval("[__FILE__, __LINE__]", nil, 'foo', 2))
diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb
index 0f39a15b2d..4b7d709906 100644
--- a/test/ruby/test_exception.rb
+++ b/test/ruby/test_exception.rb
@@ -416,7 +416,7 @@ class TestException < Test::Unit::TestCase
assert_in_out_err([], "$@ = 1", [], /\$! not set \(ArgumentError\)$/)
- assert_in_out_err([], <<-INPUT, [], /backtrace must be Array of String \(TypeError\)$/)
+ assert_in_out_err([], <<-INPUT, [], /backtrace must be an Array of String or an Array of Thread::Backtrace::Location \(TypeError\)$/)
begin
raise
rescue
@@ -508,6 +508,16 @@ end.join
assert_raise(TypeError) { e.set_backtrace(1) }
assert_raise(TypeError) { e.set_backtrace([1]) }
+
+ error = assert_raise(TypeError) do
+ e.set_backtrace(caller_locations(1, 1) + ["foo"])
+ end
+ assert_include error.message, "backtrace must be an Array of String or an Array of Thread::Backtrace::Location"
+
+ error = assert_raise(TypeError) do
+ e.set_backtrace(["foo"] + caller_locations(1, 1))
+ end
+ assert_include error.message, "backtrace must be an Array of String or an Array of Thread::Backtrace::Location"
end
def test_exit_success_p
@@ -540,6 +550,14 @@ end.join
assert_equal(Encoding.find("locale"), Errno::EINVAL.new.message.encoding)
end
+ def test_errno_constants
+ assert_equal [:NOERROR], Errno.constants.grep_v(/\AE/)
+ all_assertions_foreach("should be a subclass of SystemCallError", *Errno.constants) do |c|
+ e = Errno.const_get(c)
+ assert_operator e, :<, SystemCallError, proc {e.ancestors.inspect}
+ end
+ end
+
def test_too_many_args_in_eval
bug5720 = '[ruby-core:41520]'
arg_string = (0...140000).to_a.join(", ")
@@ -685,7 +703,7 @@ end.join
def test_machine_stackoverflow
bug9109 = '[ruby-dev:47804] [Bug #9109]'
- assert_separately(%w[--disable-gem], <<-SRC)
+ assert_separately([], <<-SRC)
assert_raise(SystemStackError, #{bug9109.dump}) {
h = {a: ->{h[:a].call}}
h[:a].call
@@ -696,7 +714,7 @@ end.join
def test_machine_stackoverflow_by_define_method
bug9454 = '[ruby-core:60113] [Bug #9454]'
- assert_separately(%w[--disable-gem], <<-SRC)
+ assert_separately([], <<-SRC)
assert_raise(SystemStackError, #{bug9454.dump}) {
define_method(:foo) {self.foo}
self.foo
@@ -801,7 +819,7 @@ end.join
def test_cause_at_end
errs = [
/-: unexpected return\n/,
- /.*undefined local variable or method `n'.*\n/,
+ /.*undefined local variable or method 'n'.*\n/,
]
assert_in_out_err([], <<-'end;', [], errs)
END{n}; END{return}
@@ -1037,7 +1055,7 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
end
def test_message_of_name_error
- assert_raise_with_message(NameError, /\Aundefined method `foo' for module `#<Module:.*>'$/) do
+ assert_raise_with_message(NameError, /\Aundefined method 'foo' for module '#<Module:.*>'$/) do
Module.new do
module_function :foo
end
@@ -1046,8 +1064,7 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
def capture_warning_warn(category: false)
verbose = $VERBOSE
- deprecated = Warning[:deprecated]
- experimental = Warning[:experimental]
+ categories = Warning.categories.to_h {|cat| [cat, Warning[cat]]}
warning = []
::Warning.class_eval do
@@ -1066,15 +1083,13 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
end
$VERBOSE = true
- Warning[:deprecated] = true
- Warning[:experimental] = true
+ Warning.categories.each {|cat| Warning[cat] = true}
yield
return warning
ensure
$VERBOSE = verbose
- Warning[:deprecated] = deprecated
- Warning[:experimental] = experimental
+ categories.each {|cat, flag| Warning[cat] = flag}
::Warning.class_eval do
remove_method :warn
@@ -1085,7 +1100,7 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
def test_warning_warn
warning = capture_warning_warn {$asdfasdsda_test_warning_warn}
- assert_match(/global variable `\$asdfasdsda_test_warning_warn' not initialized/, warning[0])
+ assert_match(/global variable '\$asdfasdsda_test_warning_warn' not initialized/, warning[0])
assert_equal(["a\nz\n"], capture_warning_warn {warn "a\n", "z"})
assert_equal([], capture_warning_warn {warn})
@@ -1093,19 +1108,13 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
end
def test_warn_deprecated_backwards_compatibility_category
- omit "no method to test"
-
- warning = capture_warning_warn { }
-
- assert_match(/deprecated/, warning[0])
- end
-
- def test_warn_deprecated_category
- omit "no method to test"
-
- warning = capture_warning_warn(category: true) { }
+ (message, category), = capture_warning_warn(category: true) do
+ $; = "www"
+ $; = nil
+ end
- assert_equal :deprecated, warning[0][1]
+ assert_include message, 'deprecated'
+ assert_equal :deprecated, category
end
def test_kernel_warn_uplevel
@@ -1161,7 +1170,7 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
end
def test_warning_warn_super
- assert_in_out_err(%[-W0], "#{<<~"{#"}\n#{<<~'};'}", [], /global variable `\$asdfiasdofa_test_warning_warn_super' not initialized/)
+ assert_in_out_err(%[-W0], "#{<<~"{#"}\n#{<<~'};'}", [], /global variable '\$asdfiasdofa_test_warning_warn_super' not initialized/)
{#
module Warning
def warn(message)
@@ -1177,48 +1186,24 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
def test_warning_category
assert_raise(TypeError) {Warning[nil]}
assert_raise(ArgumentError) {Warning[:XXXX]}
- assert_include([true, false], Warning[:deprecated])
- assert_include([true, false], Warning[:experimental])
- end
- def test_warning_category_deprecated
- warning = EnvUtil.verbose_warning do
- deprecated = Warning[:deprecated]
- Warning[:deprecated] = true
- Warning.warn "deprecated feature", category: :deprecated
- ensure
- Warning[:deprecated] = deprecated
- end
- assert_equal "deprecated feature", warning
+ all_assertions_foreach("categories", *Warning.categories) do |cat|
+ value = Warning[cat]
+ assert_include([true, false], value)
- warning = EnvUtil.verbose_warning do
- deprecated = Warning[:deprecated]
- Warning[:deprecated] = false
- Warning.warn "deprecated feature", category: :deprecated
- ensure
- Warning[:deprecated] = deprecated
- end
- assert_empty warning
- end
-
- def test_warning_category_experimental
- warning = EnvUtil.verbose_warning do
- experimental = Warning[:experimental]
- Warning[:experimental] = true
- Warning.warn "experimental feature", category: :experimental
- ensure
- Warning[:experimental] = experimental
- end
- assert_equal "experimental feature", warning
-
- warning = EnvUtil.verbose_warning do
- experimental = Warning[:experimental]
- Warning[:experimental] = false
- Warning.warn "experimental feature", category: :experimental
+ enabled = EnvUtil.verbose_warning do
+ Warning[cat] = true
+ Warning.warn "#{cat} feature", category: cat
+ end
+ disabled = EnvUtil.verbose_warning do
+ Warning[cat] = false
+ Warning.warn "#{cat} feature", category: cat
+ end
ensure
- Warning[:experimental] = experimental
+ Warning[cat] = value
+ assert_equal "#{cat} feature", enabled
+ assert_empty disabled
end
- assert_empty warning
end
def test_undef_Warning_warn
@@ -1310,7 +1295,7 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
def test_backtrace_in_eval
bug = '[ruby-core:84434] [Bug #14229]'
- assert_in_out_err(['-e', 'eval("raise")'], "", [], /^\(eval\):1:/, bug)
+ assert_in_out_err(['-e', 'eval("raise")'], "", [], /^\(eval at .*\):1:/, bug)
end
def test_full_message
@@ -1413,11 +1398,7 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
end
def test_marshal_circular_cause
- begin
- raise RuntimeError, "err", [], cause: Exception.new
- rescue => e
- end
- dump = Marshal.dump(e).sub(/o:\x0EException\x08;.0;.0;.0/, "@\x05")
+ dump = "\x04\bo:\x11RuntimeError\b:\tmesgI\"\berr\x06:\x06ET:\abt[\x00:\ncause@\x05"
assert_raise_with_message(ArgumentError, /circular cause/, ->{dump.inspect}) do
e = Marshal.load(dump)
assert_same(e, e.cause)
@@ -1435,7 +1416,7 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
end
bug14670 = '[ruby-dev:50522] [Bug #14670]'
- assert_raise_with_message(NoMethodError, /`foo'/, bug14670) do
+ assert_raise_with_message(NoMethodError, /'foo'/, bug14670) do
Object.new.foo
end
end;
@@ -1459,6 +1440,15 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
assert_equal("\e[1mRuntimeError (\e[1;4mRuntimeError\e[m\e[1m)\e[m", e.detailed_message(highlight: true))
end
+ def test_detailed_message_under_gc_compact_stress
+ omit "compaction doesn't work well on s390x" if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077
+ EnvUtil.under_gc_compact_stress do
+ e = RuntimeError.new("foo\nbar\nbaz")
+ assert_equal("foo (RuntimeError)\nbar\nbaz", e.detailed_message)
+ assert_equal("\e[1mfoo (\e[1;4mRuntimeError\e[m\e[1m)\e[m\n\e[1mbar\e[m\n\e[1mbaz\e[m", e.detailed_message(highlight: true))
+ end
+ end
+
def test_full_message_with_custom_detailed_message
e = RuntimeError.new("message")
opt_ = nil
diff --git a/test/ruby/test_fiber.rb b/test/ruby/test_fiber.rb
index cb6e846bc6..45e5d12092 100644
--- a/test/ruby/test_fiber.rb
+++ b/test/ruby/test_fiber.rb
@@ -82,12 +82,14 @@ class TestFiber < Test::Unit::TestCase
f.resume
f.resume
}
- assert_raise(RuntimeError){
- Fiber.new{
- @c = callcc{|c| @c = c}
- }.resume
- @c.call # cross fiber callcc
- }
+ if respond_to?(:callcc)
+ assert_raise(RuntimeError){
+ Fiber.new{
+ @c = callcc{|c| @c = c}
+ }.resume
+ @c.call # cross fiber callcc
+ }
+ end
assert_raise(RuntimeError){
Fiber.new{
raise
@@ -422,7 +424,7 @@ class TestFiber < Test::Unit::TestCase
end
def test_fatal_in_fiber
- assert_in_out_err(["-r-test-/fatal/rb_fatal", "-e", <<-EOS], "", [], /ok/)
+ assert_in_out_err(["-r-test-/fatal", "-e", <<-EOS], "", [], /ok/)
Fiber.new{
Bug.rb_fatal "ok"
}.resume
diff --git a/test/ruby/test_file.rb b/test/ruby/test_file.rb
index 409d21fc4e..aa10566bfa 100644
--- a/test/ruby/test_file.rb
+++ b/test/ruby/test_file.rb
@@ -554,4 +554,250 @@ class TestFile < Test::Unit::TestCase
assert_file.absolute_path?("/foo/bar\\baz")
end
end
+
+ class NewlineConvTests < Test::Unit::TestCase
+ TEST_STRING_WITH_CRLF = "line1\r\nline2\r\n".freeze
+ TEST_STRING_WITH_LF = "line1\nline2\n".freeze
+
+ def setup
+ @tmpdir = Dir.mktmpdir(self.class.name)
+ @read_path_with_crlf = File.join(@tmpdir, "read_path_with_crlf")
+ File.binwrite(@read_path_with_crlf, TEST_STRING_WITH_CRLF)
+ @read_path_with_lf = File.join(@tmpdir, "read_path_with_lf")
+ File.binwrite(@read_path_with_lf, TEST_STRING_WITH_LF)
+ @write_path = File.join(@tmpdir, "write_path")
+ File.binwrite(@write_path, '')
+ end
+
+ def teardown
+ FileUtils.rm_rf @tmpdir
+ end
+
+ def windows?
+ /cygwin|mswin|mingw/ =~ RUBY_PLATFORM
+ end
+
+ def open_file_with(method, filename, mode)
+ read_or_write = mode.include?('w') ? :write : :read
+ binary_or_text = mode.include?('b') ? :binary : :text
+
+ f = case method
+ when :ruby_file_open
+ File.open(filename, mode)
+ when :c_rb_file_open
+ Bug::File::NewlineConv.rb_file_open(filename, read_or_write, binary_or_text)
+ when :c_rb_io_fdopen
+ Bug::File::NewlineConv.rb_io_fdopen(filename, read_or_write, binary_or_text)
+ else
+ raise "Don't know how to open with #{method}"
+ end
+
+ begin
+ yield f
+ ensure
+ f.close
+ end
+ end
+
+ def assert_file_contents_has_lf(f)
+ assert_equal TEST_STRING_WITH_LF, f.read
+ end
+
+ def assert_file_contents_has_crlf(f)
+ assert_equal TEST_STRING_WITH_CRLF, f.read
+ end
+
+ def assert_file_contents_has_lf_on_windows(f)
+ if windows?
+ assert_file_contents_has_lf(f)
+ else
+ assert_file_contents_has_crlf(f)
+ end
+ end
+
+ def assert_file_contents_has_crlf_on_windows(f)
+ if windows?
+ assert_file_contents_has_crlf(f)
+ else
+ assert_file_contents_has_lf(f)
+ end
+ end
+
+ def test_ruby_file_open_text_mode_read_crlf
+ open_file_with(:ruby_file_open, @read_path_with_crlf, 'r') { |f| assert_file_contents_has_lf_on_windows(f) }
+ end
+
+ def test_ruby_file_open_bin_mode_read_crlf
+ open_file_with(:ruby_file_open, @read_path_with_crlf, 'rb') { |f| assert_file_contents_has_crlf(f) }
+ end
+
+ def test_ruby_file_open_text_mode_read_lf
+ open_file_with(:ruby_file_open, @read_path_with_lf, 'r') { |f| assert_file_contents_has_lf(f) }
+ end
+
+ def test_ruby_file_open_bin_mode_read_lf
+ open_file_with(:ruby_file_open, @read_path_with_lf, 'rb') { |f| assert_file_contents_has_lf(f) }
+ end
+
+ def test_ruby_file_open_text_mode_read_crlf_with_utf8_encoding
+ open_file_with(:ruby_file_open, @read_path_with_crlf, 'r') do |f|
+ f.set_encoding Encoding::UTF_8, '-'
+ assert_file_contents_has_lf_on_windows(f)
+ end
+ end
+
+ def test_ruby_file_open_bin_mode_read_crlf_with_utf8_encoding
+ open_file_with(:ruby_file_open, @read_path_with_crlf, 'rb') do |f|
+ f.set_encoding Encoding::UTF_8, '-'
+ assert_file_contents_has_crlf(f)
+ end
+ end
+
+ def test_ruby_file_open_text_mode_read_lf_with_utf8_encoding
+ open_file_with(:ruby_file_open, @read_path_with_lf, 'r') do |f|
+ f.set_encoding Encoding::UTF_8, '-'
+ assert_file_contents_has_lf(f)
+ end
+ end
+
+ def test_ruby_file_open_bin_mode_read_lf_with_utf8_encoding
+ open_file_with(:ruby_file_open, @read_path_with_lf, 'rb') do |f|
+ f.set_encoding Encoding::UTF_8, '-'
+ assert_file_contents_has_lf(f)
+ end
+ end
+
+ def test_ruby_file_open_text_mode_write_lf
+ open_file_with(:ruby_file_open, @write_path, 'w') { |f| f.write TEST_STRING_WITH_LF }
+ File.open(@write_path, 'rb') { |f| assert_file_contents_has_crlf_on_windows(f) }
+ end
+
+ def test_ruby_file_open_bin_mode_write_lf
+ open_file_with(:ruby_file_open, @write_path, 'wb') { |f| f.write TEST_STRING_WITH_LF }
+ File.open(@write_path, 'rb') { |f| assert_file_contents_has_lf(f) }
+ end
+
+ def test_ruby_file_open_bin_mode_write_crlf
+ open_file_with(:ruby_file_open, @write_path, 'wb') { |f| f.write TEST_STRING_WITH_CRLF }
+ File.open(@write_path, 'rb') { |f| assert_file_contents_has_crlf(f) }
+ end
+
+ def test_c_rb_file_open_text_mode_read_crlf
+ open_file_with(:c_rb_file_open, @read_path_with_crlf, 'r') { |f| assert_file_contents_has_lf_on_windows(f) }
+ end
+
+ def test_c_rb_file_open_bin_mode_read_crlf
+ open_file_with(:c_rb_file_open, @read_path_with_crlf, 'rb') { |f| assert_file_contents_has_crlf(f) }
+ end
+
+ def test_c_rb_file_open_text_mode_read_lf
+ open_file_with(:c_rb_file_open, @read_path_with_lf, 'r') { |f| assert_file_contents_has_lf(f) }
+ end
+
+ def test_c_rb_file_open_bin_mode_read_lf
+ open_file_with(:c_rb_file_open, @read_path_with_lf, 'rb') { |f| assert_file_contents_has_lf(f) }
+ end
+
+ def test_c_rb_file_open_text_mode_write_lf
+ open_file_with(:c_rb_file_open, @write_path, 'w') { |f| f.write TEST_STRING_WITH_LF }
+ File.open(@write_path, 'rb') { |f| assert_file_contents_has_crlf_on_windows(f) }
+ end
+
+ def test_c_rb_file_open_bin_mode_write_lf
+ open_file_with(:c_rb_file_open, @write_path, 'wb') { |f| f.write TEST_STRING_WITH_LF }
+ File.open(@write_path, 'rb') { |f| assert_file_contents_has_lf(f) }
+ end
+
+ def test_c_rb_file_open_bin_mode_write_crlf
+ open_file_with(:c_rb_file_open, @write_path, 'wb') { |f| f.write TEST_STRING_WITH_CRLF }
+ File.open(@write_path, 'rb') { |f| assert_file_contents_has_crlf(f) }
+ end
+
+ def test_c_rb_file_open_text_mode_read_crlf_with_utf8_encoding
+ open_file_with(:c_rb_file_open, @read_path_with_crlf, 'r') do |f|
+ f.set_encoding Encoding::UTF_8, '-'
+ assert_file_contents_has_lf_on_windows(f)
+ end
+ end
+
+ def test_c_rb_file_open_bin_mode_read_crlf_with_utf8_encoding
+ open_file_with(:c_rb_file_open, @read_path_with_crlf, 'rb') do |f|
+ f.set_encoding Encoding::UTF_8, '-'
+ assert_file_contents_has_crlf(f)
+ end
+ end
+
+ def test_c_rb_file_open_text_mode_read_lf_with_utf8_encoding
+ open_file_with(:c_rb_file_open, @read_path_with_lf, 'r') do |f|
+ f.set_encoding Encoding::UTF_8, '-'
+ assert_file_contents_has_lf(f)
+ end
+ end
+
+ def test_c_rb_file_open_bin_mode_read_lf_with_utf8_encoding
+ open_file_with(:c_rb_file_open, @read_path_with_lf, 'rb') do |f|
+ f.set_encoding Encoding::UTF_8, '-'
+ assert_file_contents_has_lf(f)
+ end
+ end
+
+ def test_c_rb_io_fdopen_text_mode_read_crlf
+ open_file_with(:c_rb_io_fdopen, @read_path_with_crlf, 'r') { |f| assert_file_contents_has_lf_on_windows(f) }
+ end
+
+ def test_c_rb_io_fdopen_bin_mode_read_crlf
+ open_file_with(:c_rb_io_fdopen, @read_path_with_crlf, 'rb') { |f| assert_file_contents_has_crlf(f) }
+ end
+
+ def test_c_rb_io_fdopen_text_mode_read_lf
+ open_file_with(:c_rb_io_fdopen, @read_path_with_lf, 'r') { |f| assert_file_contents_has_lf(f) }
+ end
+
+ def test_c_rb_io_fdopen_bin_mode_read_lf
+ open_file_with(:c_rb_io_fdopen, @read_path_with_lf, 'rb') { |f| assert_file_contents_has_lf(f) }
+ end
+
+ def test_c_rb_io_fdopen_text_mode_write_lf
+ open_file_with(:c_rb_io_fdopen, @write_path, 'w') { |f| f.write TEST_STRING_WITH_LF }
+ File.open(@write_path, 'rb') { |f| assert_file_contents_has_crlf_on_windows(f) }
+ end
+
+ def test_c_rb_io_fdopen_bin_mode_write_lf
+ open_file_with(:c_rb_io_fdopen, @write_path, 'wb') { |f| f.write TEST_STRING_WITH_LF }
+ File.open(@write_path, 'rb') { |f| assert_file_contents_has_lf(f) }
+ end
+
+ def test_c_rb_io_fdopen_bin_mode_write_crlf
+ open_file_with(:c_rb_io_fdopen, @write_path, 'wb') { |f| f.write TEST_STRING_WITH_CRLF }
+ File.open(@write_path, 'rb') { |f| assert_file_contents_has_crlf(f) }
+ end
+
+ def test_c_rb_io_fdopen_text_mode_read_crlf_with_utf8_encoding
+ open_file_with(:c_rb_io_fdopen, @read_path_with_crlf, 'r') do |f|
+ f.set_encoding Encoding::UTF_8, '-'
+ assert_file_contents_has_lf_on_windows(f)
+ end
+ end
+
+ def test_c_rb_io_fdopen_bin_mode_read_crlf_with_utf8_encoding
+ open_file_with(:c_rb_io_fdopen, @read_path_with_crlf, 'rb') do |f|
+ f.set_encoding Encoding::UTF_8, '-'
+ assert_file_contents_has_crlf(f)
+ end
+ end
+
+ def test_c_rb_io_fdopen_text_mode_read_lf_with_utf8_encoding
+ open_file_with(:c_rb_io_fdopen, @read_path_with_lf, 'r') do |f|
+ f.set_encoding Encoding::UTF_8, '-'
+ assert_file_contents_has_lf(f)
+ end
+ end
+
+ def test_c_rb_io_fdopen_bin_mode_read_lf_with_utf8_encoding
+ open_file_with(:c_rb_io_fdopen, @read_path_with_lf, 'rb') do |f|
+ f.set_encoding Encoding::UTF_8, '-'
+ assert_file_contents_has_lf(f)
+ end
+ end
+ end
end
diff --git a/test/ruby/test_file_exhaustive.rb b/test/ruby/test_file_exhaustive.rb
index be6e1f2326..fbb18f07f9 100644
--- a/test/ruby/test_file_exhaustive.rb
+++ b/test/ruby/test_file_exhaustive.rb
@@ -1518,7 +1518,7 @@ class TestFileExhaustive < Test::Unit::TestCase
stat = File.stat(f)
unless stat.chardev?
- # /dev/null may be accessed by other processes
+ # null device may be accessed by other processes
assert_equal(stat.atime, File.atime(f), f)
assert_equal(stat.ctime, File.ctime(f), f)
assert_equal(stat.mtime, File.mtime(f), f)
diff --git a/test/ruby/test_gc.rb b/test/ruby/test_gc.rb
index 2a7c1b507f..39b001c3d0 100644
--- a/test/ruby/test_gc.rb
+++ b/test/ruby/test_gc.rb
@@ -151,8 +151,13 @@ class TestGc < Test::Unit::TestCase
GC.stat(stat)
GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT].times do |i|
- GC.stat_heap(i, stat_heap)
- GC.stat(stat)
+ begin
+ reenable_gc = !GC.disable
+ GC.stat_heap(i, stat_heap)
+ GC.stat(stat)
+ ensure
+ GC.enable if reenable_gc
+ end
assert_equal GC::INTERNAL_CONSTANTS[:RVALUE_SIZE] * (2**i), stat_heap[:slot_size]
assert_operator stat_heap[:heap_allocatable_pages], :<=, stat[:heap_allocatable_pages]
@@ -163,6 +168,10 @@ class TestGc < Test::Unit::TestCase
assert_operator stat_heap[:total_allocated_pages], :>=, 0
assert_operator stat_heap[:total_freed_pages], :>=, 0
assert_operator stat_heap[:force_major_gc_count], :>=, 0
+ assert_operator stat_heap[:force_incremental_marking_finish_count], :>=, 0
+ assert_operator stat_heap[:total_allocated_objects], :>=, 0
+ assert_operator stat_heap[:total_freed_objects], :>=, 0
+ assert_operator stat_heap[:total_freed_objects], :<=, stat_heap[:total_allocated_objects]
end
GC.stat_heap(0, stat_heap)
@@ -174,6 +183,7 @@ class TestGc < Test::Unit::TestCase
end
def test_stat_heap_all
+ omit "flaky with RJIT, which allocates objects itself" if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?
stat_heap_all = {}
stat_heap = {}
@@ -185,6 +195,11 @@ class TestGc < Test::Unit::TestCase
GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT].times do |i|
GC.stat_heap(i, stat_heap)
+ # Remove keys that can vary between invocations
+ %i(total_allocated_objects).each do |sym|
+ stat_heap[sym] = stat_heap_all[i][sym] = 0
+ end
+
assert_equal stat_heap, stat_heap_all[i]
end
@@ -196,8 +211,10 @@ class TestGc < Test::Unit::TestCase
stat = GC.stat
stat_heap = GC.stat_heap
- GC.stat(stat)
- GC.stat_heap(nil, stat_heap)
+ 2.times do
+ GC.stat(stat)
+ GC.stat_heap(nil, stat_heap)
+ end
stat_heap_sum = Hash.new(0)
stat_heap.values.each do |hash|
@@ -210,17 +227,36 @@ class TestGc < Test::Unit::TestCase
assert_equal stat[:heap_available_slots], stat_heap_sum[:heap_eden_slots] + stat_heap_sum[:heap_tomb_slots]
assert_equal stat[:total_allocated_pages], stat_heap_sum[:total_allocated_pages]
assert_equal stat[:total_freed_pages], stat_heap_sum[:total_freed_pages]
+ assert_equal stat[:total_allocated_objects], stat_heap_sum[:total_allocated_objects]
+ assert_equal stat[:total_freed_objects], stat_heap_sum[:total_freed_objects]
+ end
+
+ def test_measure_total_time
+ assert_separately([], __FILE__, __LINE__, <<~RUBY)
+ GC.measure_total_time = false
+
+ time_before = GC.stat(:time)
+
+ # Generate some garbage
+ Random.new.bytes(100 * 1024 * 1024)
+ GC.start
+
+ time_after = GC.stat(:time)
+
+ # If time measurement is disabled, the time stat should not change
+ assert_equal time_before, time_after
+ RUBY
end
def test_latest_gc_info
omit 'stress' if GC.stress
- assert_separately %w[--disable-gem], __FILE__, __LINE__, <<-'eom'
- GC.start
- count = GC.stat(:heap_free_slots) + GC.stat(:heap_allocatable_pages) * GC::INTERNAL_CONSTANTS[:HEAP_PAGE_OBJ_LIMIT]
- count.times{ "a" + "b" }
- assert_equal :newobj, GC.latest_gc_info[:gc_by]
- eom
+ assert_separately([], __FILE__, __LINE__, <<-'RUBY')
+ GC.start
+ count = GC.stat(:heap_free_slots) + GC.stat(:heap_allocatable_pages) * GC::INTERNAL_CONSTANTS[:HEAP_PAGE_OBJ_LIMIT]
+ count.times{ "a" + "b" }
+ assert_equal :newobj, GC.latest_gc_info[:gc_by]
+ RUBY
GC.latest_gc_info(h = {}) # allocate hash and rehearsal
GC.start
@@ -257,19 +293,74 @@ class TestGc < Test::Unit::TestCase
assert_nil GC.latest_gc_info(:need_major_by)
# allocate objects until need_major_by is set or major GC happens
- major_count = GC.stat(:major_gc_count)
objects = []
while GC.latest_gc_info(:need_major_by).nil?
objects.append(100.times.map { '*' })
end
- assert_not_nil GC.latest_gc_info(:need_major_by)
+ # We need to ensure that no GC gets ran before the call to GC.start since
+ # it would trigger a major GC. Assertions could allocate objects and
+ # trigger a GC so we don't run assertions until we perform the major GC.
+ need_major_by = GC.latest_gc_info(:need_major_by)
GC.start(full_mark: false) # should be upgraded to major
- assert_not_nil GC.latest_gc_info(:major_by)
+ major_by = GC.latest_gc_info(:major_by)
+
+ assert_not_nil(need_major_by)
+ assert_not_nil(major_by)
+ end
+
+ def test_latest_gc_info_weak_references_count
+ assert_separately([], __FILE__, __LINE__, <<~RUBY)
+ count = 10_000
+ # Some weak references may be created, so allow some margin of error
+ error_tolerance = 100
+
+ # Run full GC to clear out weak references
+ GC.start
+ # Run full GC again to collect stats about weak references
+ GC.start
+
+ before_weak_references_count = GC.latest_gc_info(:weak_references_count)
+ before_retained_weak_references_count = GC.latest_gc_info(:retained_weak_references_count)
+
+ # Create some objects and place it in a WeakMap
+ wmap = ObjectSpace::WeakMap.new
+ ary = Array.new(count)
+ enum = count.times
+ enum.each.with_index do |i|
+ obj = Object.new
+ ary[i] = obj
+ wmap[obj] = nil
+ end
+
+ # Run full GC to collect stats about weak references
+ GC.start
+
+ assert_operator(GC.latest_gc_info(:weak_references_count), :>=, before_weak_references_count + count - error_tolerance)
+ assert_operator(GC.latest_gc_info(:retained_weak_references_count), :>=, before_retained_weak_references_count + count - error_tolerance)
+ assert_operator(GC.latest_gc_info(:retained_weak_references_count), :<=, GC.latest_gc_info(:weak_references_count))
+
+ before_weak_references_count = GC.latest_gc_info(:weak_references_count)
+ before_retained_weak_references_count = GC.latest_gc_info(:retained_weak_references_count)
+
+ ary = nil
+
+ # Free ary, which should empty out the wmap
+ GC.start
+ # Run full GC again to collect stats about weak references
+ GC.start
+
+ # Sometimes the WeakMap has one element, which might be held on by registers.
+ assert_operator(wmap.size, :<=, 1)
+
+ assert_operator(GC.latest_gc_info(:weak_references_count), :<=, before_weak_references_count - count + error_tolerance)
+ assert_operator(GC.latest_gc_info(:retained_weak_references_count), :<=, before_retained_weak_references_count - count + error_tolerance)
+ assert_operator(GC.latest_gc_info(:retained_weak_references_count), :<=, GC.latest_gc_info(:weak_references_count))
+ RUBY
end
def test_stress_compile_send
- assert_in_out_err(%w[--disable-gems], <<-EOS, [], [], "")
+ assert_in_out_err([], <<-EOS, [], [], "")
GC.stress = true
begin
eval("A::B.c(1, 1, d: 234)")
@@ -279,7 +370,7 @@ class TestGc < Test::Unit::TestCase
end
def test_singleton_method
- assert_in_out_err(%w[--disable-gems], <<-EOS, [], [], "[ruby-dev:42832]")
+ assert_in_out_err([], <<-EOS, [], [], "[ruby-dev:42832]")
GC.stress = true
10.times do
obj = Object.new
@@ -291,7 +382,7 @@ class TestGc < Test::Unit::TestCase
end
def test_singleton_method_added
- assert_in_out_err(%w[--disable-gems], <<-EOS, [], [], "[ruby-dev:44436]")
+ assert_in_out_err([], <<-EOS, [], [], "[ruby-dev:44436]")
class BasicObject
undef singleton_method_added
def singleton_method_added(mid)
@@ -309,39 +400,23 @@ class TestGc < Test::Unit::TestCase
env = {
"RUBY_GC_HEAP_INIT_SLOTS" => "100"
}
- assert_in_out_err([env, "-W0", "-e", "exit"], "", [], [], "[Bug #19284]")
-
- env = {
- "RUBY_GC_MALLOC_LIMIT" => "60000000",
- "RUBY_GC_HEAP_INIT_SLOTS" => "100000"
- }
- assert_normal_exit("exit", "[ruby-core:39777]", :child_env => env)
+ assert_in_out_err([env, "-W0", "-e", "exit"], "", [], [])
+ assert_in_out_err([env, "-W:deprecated", "-e", "exit"], "", [],
+ /The environment variable RUBY_GC_HEAP_INIT_SLOTS is deprecated; use environment variables RUBY_GC_HEAP_%d_INIT_SLOTS instead/)
env = {}
- GC.stat_heap.each do |_, s|
- env["RUBY_GC_HEAP_INIT_SIZE_#{s[:slot_size]}_SLOTS"] = "200000"
+ GC.stat_heap.keys.each do |heap|
+ env["RUBY_GC_HEAP_#{heap}_INIT_SLOTS"] = "200000"
end
assert_normal_exit("exit", "", :child_env => env)
- env["RUBY_GC_HEAP_INIT_SLOTS"] = "100000"
- assert_normal_exit("exit", "", :child_env => env)
-
env = {}
- GC.stat_heap.each do |_, s|
- env["RUBY_GC_HEAP_INIT_SIZE_#{s[:slot_size]}_SLOTS"] = "0"
+ GC.stat_heap.keys.each do |heap|
+ env["RUBY_GC_HEAP_#{heap}_INIT_SLOTS"] = "0"
end
assert_normal_exit("exit", "", :child_env => env)
env = {
- "RUBYOPT" => "",
- "RUBY_GC_HEAP_INIT_SLOTS" => "100000"
- }
- assert_in_out_err([env, "-e", "exit"], "", [], [], "[ruby-core:39795]")
- assert_in_out_err([env, "-W0", "-e", "exit"], "", [], [], "[ruby-core:39795]")
- assert_in_out_err([env, "-W1", "-e", "exit"], "", [], [], "[ruby-core:39795]")
- assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_HEAP_INIT_SLOTS=100000/, "[ruby-core:39795]")
-
- env = {
"RUBY_GC_HEAP_GROWTH_FACTOR" => "2.0",
"RUBY_GC_HEAP_GROWTH_MAX_SLOTS" => "10000"
}
@@ -349,17 +424,12 @@ class TestGc < Test::Unit::TestCase
assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_HEAP_GROWTH_FACTOR=2.0/, "")
assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_HEAP_GROWTH_MAX_SLOTS=10000/, "[ruby-core:57928]")
- env = {
- "RUBY_GC_HEAP_INIT_SLOTS" => "100000",
- "RUBY_GC_HEAP_FREE_SLOTS" => "10000",
- "RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR" => "0.4",
- }
- assert_normal_exit("exit", "", :child_env => env)
- assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR=0\.4/, "")
-
if use_rgengc?
+ env = {
+ "RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR" => "0.4",
+ }
# always full GC when RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR < 1.0
- assert_in_out_err([env, "--disable-gems", "-e", "GC.start; 1000_000.times{Object.new}; p(GC.stat[:minor_gc_count] < GC.stat[:major_gc_count])"], "", ['true'], //, "")
+ assert_in_out_err([env, "-e", "GC.start; 1000_000.times{Object.new}; p(GC.stat[:minor_gc_count] < GC.stat[:major_gc_count])"], "", ['true'], //, "")
end
env = {
@@ -394,6 +464,118 @@ class TestGc < Test::Unit::TestCase
end
end
+ def test_gc_parameter_init_slots
+ assert_separately([], __FILE__, __LINE__, <<~RUBY)
+ # Constant from gc.c.
+ GC_HEAP_INIT_SLOTS = 10_000
+ GC.stat_heap.each do |_, s|
+ multiple = s[:slot_size] / (GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE] + GC::INTERNAL_CONSTANTS[:RVALUE_OVERHEAD])
+ # Allocatable pages are assumed to have lost 1 slot due to alignment.
+ slots_per_page = (GC::INTERNAL_CONSTANTS[:HEAP_PAGE_OBJ_LIMIT] / multiple) - 1
+
+ total_slots = s[:heap_eden_slots] + s[:heap_allocatable_pages] * slots_per_page
+ assert_operator(total_slots, :>=, GC_HEAP_INIT_SLOTS, s)
+ end
+ RUBY
+
+ env = {}
+ # Make the heap big enough to ensure the heap never needs to grow.
+ sizes = GC.stat_heap.keys.reverse.map { |i| (i + 1) * 100_000 }
+ GC.stat_heap.keys.each do |heap|
+ env["RUBY_GC_HEAP_#{heap}_INIT_SLOTS"] = sizes[heap].to_s
+ end
+ assert_separately([env, "-W0"], __FILE__, __LINE__, <<~RUBY)
+ SIZES = #{sizes}
+ GC.stat_heap.each do |i, s|
+ multiple = s[:slot_size] / (GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE] + GC::INTERNAL_CONSTANTS[:RVALUE_OVERHEAD])
+ # Allocatable pages are assumed to have lost 1 slot due to alignment.
+ slots_per_page = (GC::INTERNAL_CONSTANTS[:HEAP_PAGE_OBJ_LIMIT] / multiple) - 1
+
+ total_slots = s[:heap_eden_slots] + s[:heap_allocatable_pages] * slots_per_page
+
+ # The delta is calculated as follows:
+ # - For allocated pages, each page can vary by 1 slot due to alignment.
+ # - For allocatable pages, we can end up with at most 1 extra page of slots.
+ assert_in_delta(SIZES[i], total_slots, s[:heap_eden_pages] + slots_per_page, s)
+ end
+ RUBY
+
+ # Check that the configured sizes are "remembered" across GC invocations.
+ assert_separately([env, "-W0"], __FILE__, __LINE__, <<~RUBY)
+ SIZES = #{sizes}
+
+ # Fill size pool 0 with transient objects.
+ ary = []
+ while GC.stat_heap(0, :heap_allocatable_pages) != 0
+ ary << Object.new
+ end
+ ary.clear
+ ary = nil
+
+ # Clear all the objects that were allocated.
+ GC.start
+
+ # Check that we still have the same number of slots as initially configured.
+ GC.stat_heap.each do |i, s|
+ multiple = s[:slot_size] / (GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE] + GC::INTERNAL_CONSTANTS[:RVALUE_OVERHEAD])
+ # Allocatable pages are assumed to have lost 1 slot due to alignment.
+ slots_per_page = (GC::INTERNAL_CONSTANTS[:HEAP_PAGE_OBJ_LIMIT] / multiple) - 1
+
+ total_slots = s[:heap_eden_slots] + s[:heap_allocatable_pages] * slots_per_page
+
+ # The delta is calculated as follows:
+ # - For allocated pages, each page can vary by 1 slot due to alignment.
+ # - For allocatable pages, we can end up with at most 1 extra page of slots.
+ assert_in_delta(SIZES[i], total_slots, s[:heap_eden_pages] + slots_per_page, s)
+ end
+ RUBY
+
+ # Check that we don't grow the heap in minor GC if we have alloctable pages.
+ env["RUBY_GC_HEAP_FREE_SLOTS_MIN_RATIO"] = "0.3"
+ env["RUBY_GC_HEAP_FREE_SLOTS_GOAL_RATIO"] = "0.99"
+ env["RUBY_GC_HEAP_FREE_SLOTS_MAX_RATIO"] = "1.0"
+ env["RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR"] = "100" # Large value to disable major GC
+ assert_separately([env, "-W0"], __FILE__, __LINE__, <<~RUBY)
+ SIZES = #{sizes}
+
+ # Run a major GC to clear out dead objects.
+ GC.start
+
+ # Disable GC so we can control when GC is ran.
+ GC.disable
+
+ # Run minor GC enough times so that we don't grow the heap because we
+ # haven't yet ran RVALUE_OLD_AGE minor GC cycles.
+ GC::INTERNAL_CONSTANTS[:RVALUE_OLD_AGE].times { GC.start(full_mark: false) }
+
+ # Fill size pool 0 to over 50% full so that the number of allocatable
+ # pages that will be created will be over the number in heap_allocatable_pages
+ # (calculated using RUBY_GC_HEAP_FREE_SLOTS_MIN_RATIO).
+ # 70% was chosen here to guarantee that.
+ ary = []
+ while GC.stat_heap(0, :heap_allocatable_pages) >
+ (GC.stat_heap(0, :heap_allocatable_pages) + GC.stat_heap(0, :heap_eden_pages)) * 0.3
+ ary << Object.new
+ end
+
+ GC.start(full_mark: false)
+
+ # Check that we still have the same number of slots as initially configured.
+ GC.stat_heap.each do |i, s|
+ multiple = s[:slot_size] / (GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE] + GC::INTERNAL_CONSTANTS[:RVALUE_OVERHEAD])
+ # Allocatable pages are assumed to have lost 1 slot due to alignment.
+ slots_per_page = (GC::INTERNAL_CONSTANTS[:HEAP_PAGE_OBJ_LIMIT] / multiple) - 1
+
+ total_slots = s[:heap_eden_slots] + s[:heap_allocatable_pages] * slots_per_page
+
+ # The delta is calculated as follows:
+ # - For allocated pages, each page can vary by 1 slot due to alignment.
+ # - For allocatable pages, we can end up with at most 1 extra page of slots.
+ assert_in_delta(SIZES[i], total_slots, s[:heap_eden_pages] + slots_per_page, s)
+ end
+ RUBY
+ end
+
def test_profiler_enabled
GC::Profiler.enable
assert_equal(true, GC::Profiler.enabled?)
@@ -405,19 +587,27 @@ class TestGc < Test::Unit::TestCase
def test_profiler_clear
omit "for now"
- assert_separately %w[--disable-gem], __FILE__, __LINE__, <<-'eom', timeout: 30
- GC::Profiler.enable
+ assert_separately([], __FILE__, __LINE__, <<-'RUBY', timeout: 30)
+ GC::Profiler.enable
- GC.start
- assert_equal(1, GC::Profiler.raw_data.size)
- GC::Profiler.clear
- assert_equal(0, GC::Profiler.raw_data.size)
+ GC.start
+ assert_equal(1, GC::Profiler.raw_data.size)
+ GC::Profiler.clear
+ assert_equal(0, GC::Profiler.raw_data.size)
+
+ 200.times{ GC.start }
+ assert_equal(200, GC::Profiler.raw_data.size)
+ GC::Profiler.clear
+ assert_equal(0, GC::Profiler.raw_data.size)
+ RUBY
+ end
- 200.times{ GC.start }
- assert_equal(200, GC::Profiler.raw_data.size)
- GC::Profiler.clear
- assert_equal(0, GC::Profiler.raw_data.size)
- eom
+ def test_profiler_raw_data
+ GC::Profiler.enable
+ GC.start
+ assert GC::Profiler.raw_data
+ ensure
+ GC::Profiler.disable
end
def test_profiler_total_time
@@ -431,34 +621,34 @@ class TestGc < Test::Unit::TestCase
end
def test_finalizing_main_thread
- assert_in_out_err(%w[--disable-gems], <<-EOS, ["\"finalize\""], [], "[ruby-dev:46647]")
+ assert_in_out_err([], <<-EOS, ["\"finalize\""], [], "[ruby-dev:46647]")
ObjectSpace.define_finalizer(Thread.main) { p 'finalize' }
EOS
end
def test_expand_heap
- assert_separately %w[--disable-gem], __FILE__, __LINE__, <<-'eom'
- GC.start
- base_length = GC.stat[:heap_eden_pages]
- (base_length * 500).times{ 'a' }
- GC.start
- base_length = GC.stat[:heap_eden_pages]
- (base_length * 500).times{ 'a' }
- GC.start
- assert_in_epsilon base_length, (v = GC.stat[:heap_eden_pages]), 1/8r,
- "invalid heap expanding (base_length: #{base_length}, GC.stat[:heap_eden_pages]: #{v})"
+ assert_separately([], __FILE__, __LINE__, <<~'RUBY')
+ GC.start
+ base_length = GC.stat[:heap_eden_pages]
+ (base_length * 500).times{ 'a' }
+ GC.start
+ base_length = GC.stat[:heap_eden_pages]
+ (base_length * 500).times{ 'a' }
+ GC.start
+ assert_in_epsilon base_length, (v = GC.stat[:heap_eden_pages]), 1/8r,
+ "invalid heap expanding (base_length: #{base_length}, GC.stat[:heap_eden_pages]: #{v})"
- a = []
- (base_length * 500).times{ a << 'a'; nil }
- GC.start
- assert_operator base_length, :<, GC.stat[:heap_eden_pages] + 1
- eom
+ a = []
+ (base_length * 500).times{ a << 'a'; nil }
+ GC.start
+ assert_operator base_length, :<, GC.stat[:heap_eden_pages] + 1
+ RUBY
end
def test_thrashing_for_young_objects
# This test prevents bugs like [Bug #18929]
- assert_separately %w[--disable-gem], __FILE__, __LINE__, <<-'RUBY'
+ assert_separately([], __FILE__, __LINE__, <<-'RUBY')
# Grow the heap
@ary = 100_000.times.map { Object.new }
@@ -548,11 +738,11 @@ class TestGc < Test::Unit::TestCase
end
def test_finalizer_passed_object_id
- assert_in_out_err(%w[--disable-gems], <<-EOS, ["true"], [])
+ assert_in_out_err([], <<~RUBY, ["true"], [])
o = Object.new
obj_id = o.object_id
ObjectSpace.define_finalizer(o, ->(id){ p id == obj_id })
- EOS
+ RUBY
end
def test_verify_internal_consistency
@@ -637,6 +827,15 @@ class TestGc < Test::Unit::TestCase
obj = nil
end
end;
+
+ assert_normal_exit "#{<<~"begin;"}\n#{<<~'end;'}", '[Bug #20042]'
+ begin;
+ def (f = Object.new).call = nil # missing ID
+ o = Object.new
+ ObjectSpace.define_finalizer(o, f)
+ o = nil
+ GC.start
+ end;
end
def test_object_ids_never_repeat
diff --git a/test/ruby/test_gc_compact.rb b/test/ruby/test_gc_compact.rb
index f36f0ee883..f47c0b046b 100644
--- a/test/ruby/test_gc_compact.rb
+++ b/test/ruby/test_gc_compact.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
require 'test/unit'
-require 'fiddle'
-require 'etc'
if RUBY_PLATFORM =~ /s390x/
warn "Currently, it is known that the compaction does not work well on s390x; contribution is welcome https://github.com/ruby/ruby/pull/5077"
@@ -112,10 +110,6 @@ class TestGCCompact < Test::Unit::TestCase
end
end
- def os_page_size
- return true unless defined?(Etc::SC_PAGE_SIZE)
- end
-
def test_gc_compact_stats
list = []
@@ -130,10 +124,6 @@ class TestGCCompact < Test::Unit::TestCase
refute_predicate compact_stats[:moved], :empty?
end
- def memory_location(obj)
- (Fiddle.dlwrap(obj) >> 1)
- end
-
def big_list(level = 10)
if level > 0
big_list(level - 1)
@@ -146,21 +136,6 @@ class TestGCCompact < Test::Unit::TestCase
end
end
- # Find an object that's allocated in a slot that had a previous
- # tenant, and that tenant moved and is still alive
- def find_object_in_recycled_slot(addresses)
- new_object = nil
-
- 100_000.times do
- new_object = Object.new
- if addresses.index memory_location(new_object)
- break
- end
- end
-
- new_object
- end
-
def test_complex_hash_keys
list_of_objects = big_list
hash = list_of_objects.hash
@@ -313,18 +288,20 @@ class TestGCCompact < Test::Unit::TestCase
assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV)
begin;
- ARY_COUNT = 500
+ ARY_COUNT = 50000
GC.verify_compaction_references(expand_heap: true, toward: :empty)
- arys = ARY_COUNT.times.map do
- ary = "abbbbbbbbbb".chars
- ary.uniq!
- end
+ Fiber.new {
+ $arys = ARY_COUNT.times.map do
+ ary = "abbbbbbbbbb".chars
+ ary.uniq!
+ end
+ }.resume
stats = GC.verify_compaction_references(expand_heap: true, toward: :empty)
- assert_operator(stats.dig(:moved_down, :T_ARRAY) || 0, :>=, ARY_COUNT)
- assert_include(ObjectSpace.dump(arys[0]), '"embedded":true')
+ assert_operator(stats.dig(:moved_down, :T_ARRAY) || 0, :>=, ARY_COUNT - 10)
+ refute_empty($arys.keep_if { |o| ObjectSpace.dump(o).include?('"embedded":true') })
end;
end
@@ -333,20 +310,22 @@ class TestGCCompact < Test::Unit::TestCase
assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV)
begin;
- ARY_COUNT = 500
+ ARY_COUNT = 50000
GC.verify_compaction_references(expand_heap: true, toward: :empty)
- ary = "hello".chars
- arys = ARY_COUNT.times.map do
- x = []
- ary.each { |e| x << e }
- x
- end
+ Fiber.new {
+ ary = "hello".chars
+ $arys = ARY_COUNT.times.map do
+ x = []
+ ary.each { |e| x << e }
+ x
+ end
+ }.resume
stats = GC.verify_compaction_references(expand_heap: true, toward: :empty)
- assert_operator(stats.dig(:moved_up, :T_ARRAY) || 0, :>=, ARY_COUNT)
- assert_include(ObjectSpace.dump(arys[0]), '"embedded":true')
+ assert_operator(stats.dig(:moved_up, :T_ARRAY) || 0, :>=, ARY_COUNT - 10)
+ refute_empty($arys.keep_if { |o| ObjectSpace.dump(o).include?('"embedded":true') })
end;
end
@@ -363,57 +342,63 @@ class TestGCCompact < Test::Unit::TestCase
end
end
- OBJ_COUNT = 500
+ OBJ_COUNT = 50000
GC.verify_compaction_references(expand_heap: true, toward: :empty)
- ary = OBJ_COUNT.times.map { Foo.new }
- ary.each(&:add_ivars)
+ Fiber.new {
+ $ary = OBJ_COUNT.times.map { Foo.new }
+ $ary.each(&:add_ivars)
- GC.start
- Foo.new.add_ivars
+ GC.start
+ Foo.new.add_ivars
+ }.resume
stats = GC.verify_compaction_references(expand_heap: true, toward: :empty)
- assert_operator(stats.dig(:moved_up, :T_OBJECT) || 0, :>=, OBJ_COUNT)
- assert_include(ObjectSpace.dump(ary[0]), '"embedded":true')
+ assert_operator(stats.dig(:moved_up, :T_OBJECT) || 0, :>=, OBJ_COUNT - 10)
+ refute_empty($ary.keep_if { |o| ObjectSpace.dump(o).include?('"embedded":true') })
end;
end
def test_moving_strings_up_size_pools
omit if GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] == 1
- assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV)
+ assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 30, signal: :SEGV)
begin;
- STR_COUNT = 500
+ STR_COUNT = 50000
GC.verify_compaction_references(expand_heap: true, toward: :empty)
- str = "a" * GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE]
- ary = STR_COUNT.times.map { "" << str }
+ Fiber.new {
+ str = "a" * GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE] * 4
+ $ary = STR_COUNT.times.map { +"" << str }
+ }.resume
stats = GC.verify_compaction_references(expand_heap: true, toward: :empty)
- assert_operator(stats[:moved_up][:T_STRING], :>=, STR_COUNT)
- assert_include(ObjectSpace.dump(ary[0]), '"embedded":true')
+ assert_operator(stats[:moved_up][:T_STRING], :>=, STR_COUNT - 10)
+ refute_empty($ary.keep_if { |o| ObjectSpace.dump(o).include?('"embedded":true') })
end;
end
def test_moving_strings_down_size_pools
omit if GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] == 1
- assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV)
+ assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 30, signal: :SEGV)
begin;
- STR_COUNT = 500
+ STR_COUNT = 50000
GC.verify_compaction_references(expand_heap: true, toward: :empty)
- ary = STR_COUNT.times.map { ("a" * GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE]).squeeze! }
+ Fiber.new {
+ $ary = STR_COUNT.times.map { ("a" * GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE] * 4).squeeze! }
+ }.resume
stats = GC.verify_compaction_references(expand_heap: true, toward: :empty)
- assert_operator(stats[:moved_down][:T_STRING], :>=, STR_COUNT)
- assert_include(ObjectSpace.dump(ary[0]), '"embedded":true')
+ assert_operator(stats[:moved_down][:T_STRING], :>=, STR_COUNT - 10)
+ refute_empty($ary.keep_if { |o| ObjectSpace.dump(o).include?('"embedded":true') })
end;
end
@@ -422,23 +407,21 @@ class TestGCCompact < Test::Unit::TestCase
# AR and ST hashes are in the same size pool on 32 bit
omit unless RbConfig::SIZEOF["uint64_t"] <= RbConfig::SIZEOF["void*"]
- assert_separately(%w[-robjspace], "#{<<~'begin;'}\n#{<<~'end;'}", timeout: 10, signal: :SEGV)
+ assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 30, signal: :SEGV)
begin;
- HASH_COUNT = 500
+ HASH_COUNT = 50000
GC.verify_compaction_references(expand_heap: true, toward: :empty)
- before_read_barrier_faults = GC.stat(:read_barrier_faults)
-
- base_hash = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8 }
- ary = HASH_COUNT.times.map { base_hash.dup }
- ary.each { |h| h[:i] = 9 }
+ Fiber.new {
+ base_hash = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8 }
+ $ary = HASH_COUNT.times.map { base_hash.dup }
+ $ary.each_with_index { |h, i| h[:i] = 9 }
+ }.resume
- before_stat_heap = GC.stat_heap
stats = GC.verify_compaction_references(expand_heap: true, toward: :empty)
- after_stat_heap = GC.stat_heap
- assert_operator(stats[:moved_down][:T_HASH], :>=, 500, "read barrier faults: before #{before_read_barrier_faults}, after #{GC.stat(:read_barrier_faults)}, stat_heap: before #{before_stat_heap}, after: #{after_stat_heap}, stats: #{stats}")
+ assert_operator(stats[:moved_down][:T_HASH], :>=, HASH_COUNT - 10)
end;
end
diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb
index d9d1ca7dde..f60ba0cffd 100644
--- a/test/ruby/test_hash.rb
+++ b/test/ruby/test_hash.rb
@@ -4,7 +4,6 @@ require 'test/unit'
EnvUtil.suppress_warning {require 'continuation'}
class TestHash < Test::Unit::TestCase
-
def test_hash
x = @cls[1=>2, 2=>4, 3=>6]
y = @cls[1=>2, 2=>4, 3=>6] # y = {1, 2, 2, 4, 3, 6} # 1.9 doesn't support
@@ -85,20 +84,9 @@ class TestHash < Test::Unit::TestCase
self => 'self', true => 'true', nil => 'nil',
'nil' => nil
]
- @verbose = $VERBOSE
end
def teardown
- $VERBOSE = @verbose
- end
-
- def test_bad_initialize_copy
- h = Class.new(Hash) {
- def initialize_copy(h)
- super(Object.new)
- end
- }.new
- assert_raise(TypeError) { h.dup }
end
def test_clear_initialize_copy
@@ -113,35 +101,6 @@ class TestHash < Test::Unit::TestCase
assert_equal(2, h[1])
end
- def test_dup_will_not_rehash
- assert_hash_does_not_rehash(&:dup)
- end
-
- def assert_hash_does_not_rehash
- obj = Object.new
- class << obj
- attr_accessor :hash_calls
- def hash
- @hash_calls += 1
- super
- end
- end
- obj.hash_calls = 0
- hash = {obj => 42}
- assert_equal(1, obj.hash_calls)
- yield hash
- assert_equal(1, obj.hash_calls)
- end
-
- def test_select_reject_will_not_rehash
- assert_hash_does_not_rehash do |hash|
- hash.select { true }
- end
- assert_hash_does_not_rehash do |hash|
- hash.reject { false }
- end
- end
-
def test_s_AREF_from_hash
h = @cls["a" => 100, "b" => 200]
assert_equal(100, h['a'])
@@ -219,6 +178,16 @@ class TestHash < Test::Unit::TestCase
assert_equal('default', h['spurious'])
end
+ def test_st_literal_memory_leak
+ assert_no_memory_leak([], "", "#{<<~"begin;"}\n#{<<~'end;'}", rss: true)
+ begin;
+ 1_000_000.times do
+ # >8 element hashes are ST allocated rather than AR allocated
+ {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9}
+ end
+ end;
+ end
+
def test_try_convert
assert_equal({1=>2}, Hash.try_convert({1=>2}))
assert_equal(nil, Hash.try_convert("1=>2"))
@@ -294,78 +263,6 @@ class TestHash < Test::Unit::TestCase
assert_equal(256, h[z])
end
- def test_AREF_fstring_key
- # warmup ObjectSpace.count_objects
- ObjectSpace.count_objects
-
- h = {"abc" => 1}
- before = ObjectSpace.count_objects[:T_STRING]
- 5.times{ h["abc"] }
- assert_equal before, ObjectSpace.count_objects[:T_STRING]
- end
-
- def test_AREF_fstring_key_default_proc
- assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
- begin;
- h = Hash.new do |h, k|
- k.frozen?
- end
-
- str = "foo"
- refute str.frozen? # assumes this file is frozen_string_literal: false
- refute h[str]
- refute h["foo"]
- end;
- end
-
- def test_ASET_fstring_key
- a, b = {}, {}
- assert_equal 1, a["abc"] = 1
- assert_equal 1, b["abc"] = 1
- assert_same a.keys[0], b.keys[0]
- end
-
- def test_ASET_fstring_non_literal_key
- underscore = "_"
- non_literal_strings = Proc.new{ ["abc#{underscore}def", "abc" * 5, "abc" + "def", "" << "ghi" << "jkl"] }
-
- a, b = {}, {}
- non_literal_strings.call.each do |string|
- assert_equal 1, a[string] = 1
- end
-
- non_literal_strings.call.each do |string|
- assert_equal 1, b[string] = 1
- end
-
- [a.keys, b.keys].transpose.each do |key_a, key_b|
- assert_same key_a, key_b
- end
- end
-
- def test_hash_aset_fstring_identity
- h = {}.compare_by_identity
- h['abc'] = 1
- h['abc'] = 2
- assert_equal 2, h.size, '[ruby-core:78783] [Bug #12855]'
- end
-
- def test_hash_aref_fstring_identity
- h = {}.compare_by_identity
- h['abc'] = 1
- assert_nil h['abc'], '[ruby-core:78783] [Bug #12855]'
- end
-
- def test_NEWHASH_fstring_key
- a = {"ABC" => :t}
- b = {"ABC" => :t}
- assert_same a.keys[0], b.keys[0]
- assert_same "ABC".freeze, a.keys[0]
- var = +'ABC'
- c = { var => :t }
- assert_same "ABC".freeze, c.keys[0]
- end
-
def test_EQUAL # '=='
h1 = @cls[ "a" => 1, "c" => 2 ]
h2 = @cls[ "a" => 1, "c" => 2, 7 => 35 ]
@@ -472,6 +369,10 @@ class TestHash < Test::Unit::TestCase
end
end
assert_equal(base.dup, h)
+
+ h = base.dup
+ assert_same h, h.delete_if {h.assoc(nil); true}
+ assert_empty h
end
def test_keep_if
@@ -842,24 +743,6 @@ class TestHash < Test::Unit::TestCase
assert_predicate(h, :compare_by_identity?)
end
- def test_replace_bug15358
- h1 = {}
- h2 = {a:1,b:2,c:3,d:4,e:5}
- h2.replace(h1)
- GC.start
- assert(true)
- end
-
- def test_replace_st_with_ar
- # ST hash
- h1 = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9 }
- # AR hash
- h2 = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7 }
- # Replace ST hash with AR hash
- h1.replace(h2)
- assert_equal(h2, h1)
- end
-
def test_shift
h = @h.dup
@@ -975,13 +858,6 @@ class TestHash < Test::Unit::TestCase
assert_instance_of(Hash, h)
end
- def test_nil_to_h
- h = nil.to_h
- assert_equal({}, h)
- assert_nil(h.default)
- assert_nil(h.default_proc)
- end
-
def test_to_s
h = @cls[ 1 => 2, "cat" => "dog", 1.5 => :fred ]
assert_equal(h.inspect, h.to_s)
@@ -1028,12 +904,6 @@ class TestHash < Test::Unit::TestCase
assert_equal([], expected - vals)
end
- def test_initialize_wrong_arguments
- assert_raise(ArgumentError) do
- Hash.new(0) { }
- end
- end
-
def test_create
assert_equal({1=>2, 3=>4}, @cls[[[1,2],[3,4]]])
assert_raise(ArgumentError) { @cls[0, 1, 2] }
@@ -1315,15 +1185,6 @@ class TestHash < Test::Unit::TestCase
assert_raise(FrozenError) { h2.replace(42) }
end
- def test_replace_memory_leak
- assert_no_memory_leak([], "#{<<-"begin;"}", "#{<<-'end;'}")
- h = ("aa".."zz").each_with_index.to_h
- 10_000.times {h.dup}
- begin;
- 500_000.times {h.dup.replace(h)}
- end;
- end
-
def test_size2
assert_equal(0, @cls[].size)
end
@@ -1407,6 +1268,15 @@ class TestHash < Test::Unit::TestCase
assert_equal(@cls[a: 10, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10], h)
end
+ def test_update_on_identhash
+ key = +'a'
+ i = @cls[].compare_by_identity
+ i[key] = 0
+ h = @cls[].update(i)
+ key.upcase!
+ assert_equal(0, h.fetch('a'))
+ end
+
def test_merge
h1 = @cls[1=>2, 3=>4]
h2 = {1=>3, 5=>7}
@@ -1429,10 +1299,10 @@ class TestHash < Test::Unit::TestCase
expected[7] = 8
h2 = h.merge(7=>8)
assert_equal(expected, h2)
- assert_equal(true, h2.compare_by_identity?)
+ assert_predicate(h2, :compare_by_identity?)
h2 = h.merge({})
assert_equal(h, h2)
- assert_equal(true, h2.compare_by_identity?)
+ assert_predicate(h2, :compare_by_identity?)
h = @cls[]
h.compare_by_identity
@@ -1440,10 +1310,10 @@ class TestHash < Test::Unit::TestCase
h1.compare_by_identity
h2 = h.merge(7=>8)
assert_equal(h1, h2)
- assert_equal(true, h2.compare_by_identity?)
+ assert_predicate(h2, :compare_by_identity?)
h2 = h.merge({})
assert_equal(h, h2)
- assert_equal(true, h2.compare_by_identity?)
+ assert_predicate(h2, :compare_by_identity?)
end
def test_merge!
@@ -1498,6 +1368,8 @@ class TestHash < Test::Unit::TestCase
end
def test_callcc
+ omit 'requires callcc support' unless respond_to?(:callcc)
+
h = @cls[1=>2]
c = nil
f = false
@@ -1518,6 +1390,8 @@ class TestHash < Test::Unit::TestCase
end
def test_callcc_iter_level
+ omit 'requires callcc support' unless respond_to?(:callcc)
+
bug9105 = '[ruby-dev:47803] [Bug #9105]'
h = @cls[1=>2, 3=>4]
c = nil
@@ -1536,6 +1410,8 @@ class TestHash < Test::Unit::TestCase
end
def test_callcc_escape
+ omit 'requires callcc support' unless respond_to?(:callcc)
+
bug9105 = '[ruby-dev:47803] [Bug #9105]'
assert_nothing_raised(RuntimeError, bug9105) do
h=@cls[]
@@ -1550,6 +1426,8 @@ class TestHash < Test::Unit::TestCase
end
def test_callcc_reenter
+ omit 'requires callcc support' unless respond_to?(:callcc)
+
bug9105 = '[ruby-dev:47803] [Bug #9105]'
assert_nothing_raised(RuntimeError, bug9105) do
h = @cls[1=>2,3=>4]
@@ -1566,17 +1444,6 @@ class TestHash < Test::Unit::TestCase
end
end
- def hash_iter_recursion(h, level)
- return if level == 0
- h.each_key {}
- h.each_value { hash_iter_recursion(h, level - 1) }
- end
-
- def test_iterlevel_in_ivar_bug19589
- h = { a: nil }
- hash_iter_recursion(h, 200)
- end
-
def test_threaded_iter_level
bug9105 = '[ruby-dev:47807] [Bug #9105]'
h = @cls[1=>2]
@@ -1608,6 +1475,16 @@ class TestHash < Test::Unit::TestCase
assert_predicate(h.dup, :compare_by_identity?, bug8703)
end
+ def test_compare_by_identy_memory_leak
+ assert_no_memory_leak([], "", "#{<<~"begin;"}\n#{<<~'end;'}", "[Bug #20145]", rss: true)
+ begin;
+ h = { 1 => 2 }.compare_by_identity
+ 1_000_000.times do
+ h.select { false }
+ end
+ end;
+ end
+
def test_same_key
bug9646 = '[ruby-dev:48047] [Bug #9646] Infinite loop at Hash#each'
h = @cls[a=[], 1]
@@ -1738,115 +1615,6 @@ class TestHash < Test::Unit::TestCase
end
end
- def test_exception_in_rehash_memory_leak
- return unless @cls == Hash
-
- bug9187 = '[ruby-core:58728] [Bug #9187]'
-
- prepare = <<-EOS
- class Foo
- def initialize
- @raise = false
- end
-
- def hash
- raise if @raise
- @raise = true
- return 0
- end
- end
- h = {Foo.new => true}
- EOS
-
- code = <<-EOS
- 10_0000.times do
- h.rehash rescue nil
- end
- GC.start
- EOS
-
- assert_no_memory_leak([], prepare, code, bug9187)
- end
-
- def test_wrapper
- bug9381 = '[ruby-core:59638] [Bug #9381]'
-
- wrapper = Class.new do
- def initialize(obj)
- @obj = obj
- end
-
- def hash
- @obj.hash
- end
-
- def eql?(other)
- @obj.eql?(other)
- end
- end
-
- bad = [
- 5, true, false, nil,
- 0.0, 1.72723e-77,
- :foo, "dsym_#{self.object_id.to_s(16)}_#{Time.now.to_i.to_s(16)}".to_sym,
- "str",
- ].select do |x|
- hash = {x => bug9381}
- hash[wrapper.new(x)] != bug9381
- end
- assert_empty(bad, bug9381)
- end
-
- def assert_hash_random(obj, dump = obj.inspect)
- a = [obj.hash.to_s]
- 3.times {
- assert_in_out_err(["-e", "print (#{dump}).hash"], "") do |r, e|
- a += r
- assert_equal([], e)
- end
- }
- assert_not_equal([obj.hash.to_s], a.uniq)
- assert_operator(a.uniq.size, :>, 2, proc {a.inspect})
- end
-
- def test_string_hash_random
- assert_hash_random('abc')
- end
-
- def test_symbol_hash_random
- assert_hash_random(:-)
- assert_hash_random(:foo)
- assert_hash_random("dsym_#{self.object_id.to_s(16)}_#{Time.now.to_i.to_s(16)}".to_sym)
- end
-
- def test_integer_hash_random
- assert_hash_random(0)
- assert_hash_random(+1)
- assert_hash_random(-1)
- assert_hash_random(+(1<<100))
- assert_hash_random(-(1<<100))
- end
-
- def test_float_hash_random
- assert_hash_random(0.0)
- assert_hash_random(+1.0)
- assert_hash_random(-1.0)
- assert_hash_random(1.72723e-77)
- assert_hash_random(Float::INFINITY, "Float::INFINITY")
- end
-
- def test_label_syntax
- return unless @cls == Hash
-
- feature4935 = '[ruby-core:37553] [Feature #4935]'
- x = 'world'
- hash = assert_nothing_raised(SyntaxError, feature4935) do
- break eval(%q({foo: 1, "foo-bar": 2, "hello-#{x}": 3, 'hello-#{x}': 4, 'bar': {}}))
- end
- assert_equal({:foo => 1, :'foo-bar' => 2, :'hello-world' => 3, :'hello-#{x}' => 4, :bar => {}}, hash, feature4935)
- x = x
- end
-
def test_dig
h = @cls[a: @cls[b: [1, 2, 3]], c: 4]
assert_equal(1, h.dig(:a, :b, 0))
@@ -1866,12 +1634,12 @@ class TestHash < Test::Unit::TestCase
def o.respond_to?(*args)
super
end
- assert_raise(TypeError, bug12030) {{foo: o}.dig(:foo, :foo)}
+ assert_raise(TypeError, bug12030) {@cls[foo: o].dig(:foo, :foo)}
end
def test_cmp
- h1 = {a:1, b:2}
- h2 = {a:1, b:2, c:3}
+ h1 = @cls[a:1, b:2]
+ h2 = @cls[a:1, b:2, c:3]
assert_operator(h1, :<=, h1)
assert_operator(h1, :<=, h2)
@@ -1895,8 +1663,8 @@ class TestHash < Test::Unit::TestCase
end
def test_cmp_samekeys
- h1 = {a:1}
- h2 = {a:2}
+ h1 = @cls[a:1]
+ h2 = @cls[a:2]
assert_operator(h1, :<=, h1)
assert_not_operator(h1, :<=, h2)
@@ -1920,15 +1688,15 @@ class TestHash < Test::Unit::TestCase
end
def test_to_proc
- h = {
+ h = @cls[
1 => 10,
2 => 20,
3 => 30,
- }
+ ]
assert_equal([10, 20, 30], [1, 2, 3].map(&h))
- assert_equal(true, h.to_proc.lambda?)
+ assert_predicate(h.to_proc, :lambda?)
end
def test_transform_keys
@@ -2058,22 +1826,6 @@ class TestHash < Test::Unit::TestCase
assert_equal(@cls[a: 2, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10], x)
end
- def test_broken_hash_value
- bug14218 = '[ruby-core:84395] [Bug #14218]'
-
- assert_equal(0, 1_000_000.times.count{a=Object.new.hash; b=Object.new.hash; a < 0 && b < 0 && a + b > 0}, bug14218)
- assert_equal(0, 1_000_000.times.count{a=Object.new.hash; b=Object.new.hash; 0 + a + b != 0 + b + a}, bug14218)
- end
-
- def test_reserved_hash_val
- s = Struct.new(:hash)
- h = {}
- keys = [*0..8]
- keys.each {|i| h[s.new(i)]=true}
- msg = proc {h.inspect}
- assert_equal(keys, h.keys.map(&:hash), msg)
- end
-
def hrec h, n, &b
if n > 0
h.each{hrec(h, n-1, &b)}
@@ -2103,6 +1855,27 @@ class TestHash < Test::Unit::TestCase
# ignore
end
+ # Previously this test would fail because rb_hash inside opt_aref would look
+ # at the current method name
+ def test_hash_recursion_independent_of_mid
+ o = Class.new do
+ def hash(h, k)
+ h[k]
+ end
+
+ def any_other_name(h, k)
+ h[k]
+ end
+ end.new
+
+ rec = []; rec << rec
+
+ h = @cls[]
+ h[rec] = 1
+ assert o.hash(h, rec)
+ assert o.any_other_name(h, rec)
+ end
+
class TestSubHash < TestHash
class SubHash < Hash
end
@@ -2112,6 +1885,341 @@ class TestHash < Test::Unit::TestCase
super
end
end
+end
+
+class TestHashOnly < Test::Unit::TestCase
+ def test_bad_initialize_copy
+ h = Class.new(Hash) {
+ def initialize_copy(h)
+ super(Object.new)
+ end
+ }.new
+ assert_raise(TypeError) { h.dup }
+ end
+
+ def test_dup_will_not_rehash
+ assert_hash_does_not_rehash(&:dup)
+ end
+
+ def assert_hash_does_not_rehash
+ obj = Object.new
+ class << obj
+ attr_accessor :hash_calls
+ def hash
+ @hash_calls += 1
+ super
+ end
+ end
+ obj.hash_calls = 0
+ hash = {obj => 42}
+ assert_equal(1, obj.hash_calls)
+ yield hash
+ assert_equal(1, obj.hash_calls)
+ end
+
+ def test_select_reject_will_not_rehash
+ assert_hash_does_not_rehash do |hash|
+ hash.select { true }
+ end
+ assert_hash_does_not_rehash do |hash|
+ hash.reject { false }
+ end
+ end
+
+ def test_st_literal_memory_leak
+ assert_no_memory_leak([], "", "#{<<~"begin;"}\n#{<<~'end;'}", rss: true)
+ begin;
+ 1_000_000.times do
+ # >8 element hashes are ST allocated rather than AR allocated
+ {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9}
+ end
+ end;
+ end
+
+ def test_compare_by_id_memory_leak
+ assert_no_memory_leak([], "", <<~RUBY, rss: true)
+ 1_000_000.times do
+ {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8}.compare_by_identity
+ end
+ RUBY
+ end
+
+ def test_try_convert
+ assert_equal({1=>2}, Hash.try_convert({1=>2}))
+ assert_equal(nil, Hash.try_convert("1=>2"))
+ o = Object.new
+ def o.to_hash; {3=>4} end
+ assert_equal({3=>4}, Hash.try_convert(o))
+ end
+
+ def test_AREF_fstring_key
+ # warmup ObjectSpace.count_objects
+ ObjectSpace.count_objects
+
+ h = {"abc" => 1}
+ before = ObjectSpace.count_objects[:T_STRING]
+ 5.times{ h["abc"] }
+ assert_equal before, ObjectSpace.count_objects[:T_STRING]
+ end
+
+ def test_AREF_fstring_key_default_proc
+ assert_separately(['--disable-frozen-string-literal'], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ h = Hash.new do |h, k|
+ k.frozen?
+ end
+
+ str = "foo"
+ refute str.frozen?
+ refute h[str]
+ refute h["foo"]
+ end;
+ end
+
+ def test_ASET_fstring_key
+ a, b = {}, {}
+ assert_equal 1, a["abc"] = 1
+ assert_equal 1, b["abc"] = 1
+ assert_same a.keys[0], b.keys[0]
+ end
+
+ def test_ASET_fstring_non_literal_key
+ underscore = "_"
+ non_literal_strings = Proc.new{ ["abc#{underscore}def", "abc" * 5, "abc" + "def", "" << "ghi" << "jkl"] }
+
+ a, b = {}, {}
+ non_literal_strings.call.each do |string|
+ assert_equal 1, a[string] = 1
+ end
+
+ non_literal_strings.call.each do |string|
+ assert_equal 1, b[string] = 1
+ end
+
+ [a.keys, b.keys].transpose.each do |key_a, key_b|
+ assert_same key_a, key_b
+ end
+ end
+
+ def test_hash_aset_fstring_identity
+ h = {}.compare_by_identity
+ h['abc'] = 1
+ h['abc'] = 2
+ assert_equal 2, h.size, '[ruby-core:78783] [Bug #12855]'
+ end
+
+ def test_hash_aref_fstring_identity
+ h = {}.compare_by_identity
+ h['abc'] = 1
+ assert_nil h['abc'], '[ruby-core:78783] [Bug #12855]'
+ end
+
+ def test_NEWHASH_fstring_key
+ a = {"ABC" => :t}
+ b = {"ABC" => :t}
+ assert_same a.keys[0], b.keys[0]
+ assert_same "ABC".freeze, a.keys[0]
+ var = +'ABC'
+ c = { var => :t }
+ assert_same "ABC".freeze, c.keys[0]
+ end
+
+ def test_rehash_memory_leak
+ assert_no_memory_leak([], <<~PREP, <<~CODE, rss: true)
+ ar_hash = 1.times.map { |i| [i, i] }.to_h
+ st_hash = 10.times.map { |i| [i, i] }.to_h
+
+ code = proc do
+ ar_hash.rehash
+ st_hash.rehash
+ end
+ 1_000.times(&code)
+ PREP
+ 1_000_000.times(&code)
+ CODE
+ end
+
+ def test_replace_bug15358
+ h1 = {}
+ h2 = {a:1,b:2,c:3,d:4,e:5}
+ h2.replace(h1)
+ GC.start
+ assert(true)
+ end
+
+ def test_replace_st_with_ar
+ # ST hash
+ h1 = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9 }
+ # AR hash
+ h2 = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7 }
+ # Replace ST hash with AR hash
+ h1.replace(h2)
+ assert_equal(h2, h1)
+ end
+
+ def test_nil_to_h
+ h = nil.to_h
+ assert_equal({}, h)
+ assert_nil(h.default)
+ assert_nil(h.default_proc)
+ end
+
+ def test_initialize_wrong_arguments
+ assert_raise(ArgumentError) do
+ Hash.new(0) { }
+ end
+ end
+
+ def test_replace_memory_leak
+ assert_no_memory_leak([], "#{<<-"begin;"}", "#{<<-'end;'}", rss: true)
+ h = ("aa".."zz").each_with_index.to_h
+ 10_000.times {h.dup}
+ begin;
+ 500_000.times {h.dup.replace(h)}
+ end;
+ end
+
+ def hash_iter_recursion(h, level)
+ return if level == 0
+ h.each_key {}
+ h.each_value { hash_iter_recursion(h, level - 1) }
+ end
+
+ def test_iterlevel_in_ivar_bug19589
+ h = { a: nil }
+ hash_iter_recursion(h, 200)
+ assert true
+ end
+
+ def test_exception_in_rehash_memory_leak
+ bug9187 = '[ruby-core:58728] [Bug #9187]'
+
+ prepare = <<-EOS
+ class Foo
+ def initialize
+ @raise = false
+ end
+
+ def hash
+ raise if @raise
+ @raise = true
+ return 0
+ end
+ end
+ h = {Foo.new => true}
+ EOS
+
+ code = <<-EOS
+ 10_0000.times do
+ h.rehash rescue nil
+ end
+ GC.start
+ EOS
+
+ assert_no_memory_leak([], prepare, code, bug9187)
+ end
+
+ def test_memory_size_after_delete
+ require 'objspace'
+ h = {}
+ 1000.times {|i| h[i] = true}
+ big = ObjectSpace.memsize_of(h)
+ 1000.times {|i| h.delete(i)}
+ assert_operator ObjectSpace.memsize_of(h), :<, big/10
+ end
+
+ def test_wrapper
+ bug9381 = '[ruby-core:59638] [Bug #9381]'
+
+ wrapper = Class.new do
+ def initialize(obj)
+ @obj = obj
+ end
+
+ def hash
+ @obj.hash
+ end
+
+ def eql?(other)
+ @obj.eql?(other)
+ end
+ end
+
+ bad = [
+ 5, true, false, nil,
+ 0.0, 1.72723e-77,
+ :foo, "dsym_#{self.object_id.to_s(16)}_#{Time.now.to_i.to_s(16)}".to_sym,
+ "str",
+ ].select do |x|
+ hash = {x => bug9381}
+ hash[wrapper.new(x)] != bug9381
+ end
+ assert_empty(bad, bug9381)
+ end
+
+ def assert_hash_random(obj, dump = obj.inspect)
+ a = [obj.hash.to_s]
+ 3.times {
+ assert_in_out_err(["-e", "print (#{dump}).hash"], "") do |r, e|
+ a += r
+ assert_equal([], e)
+ end
+ }
+ assert_not_equal([obj.hash.to_s], a.uniq)
+ assert_operator(a.uniq.size, :>, 2, proc {a.inspect})
+ end
+
+ def test_string_hash_random
+ assert_hash_random('abc')
+ end
+
+ def test_symbol_hash_random
+ assert_hash_random(:-)
+ assert_hash_random(:foo)
+ assert_hash_random("dsym_#{self.object_id.to_s(16)}_#{Time.now.to_i.to_s(16)}".to_sym)
+ end
+
+ def test_integer_hash_random
+ assert_hash_random(0)
+ assert_hash_random(+1)
+ assert_hash_random(-1)
+ assert_hash_random(+(1<<100))
+ assert_hash_random(-(1<<100))
+ end
+
+ def test_float_hash_random
+ assert_hash_random(0.0)
+ assert_hash_random(+1.0)
+ assert_hash_random(-1.0)
+ assert_hash_random(1.72723e-77)
+ assert_hash_random(Float::INFINITY, "Float::INFINITY")
+ end
+
+ def test_label_syntax
+ feature4935 = '[ruby-core:37553] [Feature #4935]'
+ x = 'world'
+ hash = assert_nothing_raised(SyntaxError, feature4935) do
+ break eval(%q({foo: 1, "foo-bar": 2, "hello-#{x}": 3, 'hello-#{x}': 4, 'bar': {}}))
+ end
+ assert_equal({:foo => 1, :'foo-bar' => 2, :'hello-world' => 3, :'hello-#{x}' => 4, :bar => {}}, hash, feature4935)
+ x = x
+ end
+
+ def test_broken_hash_value
+ bug14218 = '[ruby-core:84395] [Bug #14218]'
+
+ assert_equal(0, 1_000_000.times.count{a=Object.new.hash; b=Object.new.hash; a < 0 && b < 0 && a + b > 0}, bug14218)
+ assert_equal(0, 1_000_000.times.count{a=Object.new.hash; b=Object.new.hash; 0 + a + b != 0 + b + a}, bug14218)
+ end
+
+ def test_reserved_hash_val
+ s = Struct.new(:hash)
+ h = {}
+ keys = [*0..8]
+ keys.each {|i| h[s.new(i)]=true}
+ msg = proc {h.inspect}
+ assert_equal(keys, h.keys.map(&:hash), msg)
+ end
ruby2_keywords def get_flagged_hash(*args)
args.last
@@ -2137,23 +2245,11 @@ class TestHash < Test::Unit::TestCase
assert_raise(TypeError) { Hash.ruby2_keywords_hash(1) }
end
- def test_ar2st
- # insert
- obj = Object.new
- obj.instance_variable_set(:@h, h = {})
- def obj.hash
- 10.times{|i| @h[i] = i}
- 0
- end
- def obj.inspect
- 'test'
+ def ar2st_object
+ class << (obj = Object.new)
+ attr_reader :h
end
- h[obj] = true
- assert_equal '{0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5, 6=>6, 7=>7, 8=>8, 9=>9, test=>true}', h.inspect
-
- # delete
- obj = Object.new
- obj.instance_variable_set(:@h, h = {})
+ obj.instance_variable_set(:@h, {})
def obj.hash
10.times{|i| @h[i] = i}
0
@@ -2164,6 +2260,21 @@ class TestHash < Test::Unit::TestCase
def obj.eql? other
other.class == Object
end
+ obj
+ end
+
+ def test_ar2st_insert
+ obj = ar2st_object
+ h = obj.h
+
+ h[obj] = true
+ assert_equal '{0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5, 6=>6, 7=>7, 8=>8, 9=>9, test=>true}', h.inspect
+ end
+
+ def test_ar2st_delete
+ obj = ar2st_object
+ h = obj.h
+
obj2 = Object.new
def obj2.hash
0
@@ -2172,20 +2283,12 @@ class TestHash < Test::Unit::TestCase
h[obj2] = true
h.delete obj
assert_equal '{0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5, 6=>6, 7=>7, 8=>8, 9=>9}', h.inspect
+ end
+
+ def test_ar2st_lookup
+ obj = ar2st_object
+ h = obj.h
- # lookup
- obj = Object.new
- obj.instance_variable_set(:@h, h = {})
- def obj.hash
- 10.times{|i| @h[i] = i}
- 0
- end
- def obj.inspect
- 'test'
- end
- def obj.eql? other
- other.class == Object
- end
obj2 = Object.new
def obj2.hash
0
@@ -2201,27 +2304,6 @@ class TestHash < Test::Unit::TestCase
end
end
- # Previously this test would fail because rb_hash inside opt_aref would look
- # at the current method name
- def test_hash_recursion_independent_of_mid
- o = Class.new do
- def hash(h, k)
- h[k]
- end
-
- def any_other_name(h, k)
- h[k]
- end
- end.new
-
- rec = []; rec << rec
-
- h = @cls[]
- h[rec] = 1
- assert o.hash(h, rec)
- assert o.any_other_name(h, rec)
- end
-
def test_any_hash_fixable
20.times do
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
@@ -2247,4 +2329,35 @@ class TestHash < Test::Unit::TestCase
end;
end
end
+
+ def test_compare_by_identity_during_iteration
+ h = { 1 => 1 }
+ h.each do
+ assert_raise(RuntimeError, "compare_by_identity during iteration") do
+ h.compare_by_identity
+ end
+ end
+ end
+
+ def test_ar_hash_to_st_hash
+ assert_normal_exit("#{<<~"begin;"}\n#{<<~'end;'}", 'https://bugs.ruby-lang.org/issues/20050#note-5')
+ begin;
+ srand(0)
+ class Foo
+ def to_a
+ []
+ end
+
+ def hash
+ $h.delete($h.keys.sample) if rand < 0.1
+ to_a.hash
+ end
+ end
+
+ 1000.times do
+ $h = {}
+ (0..10).each {|i| $h[Foo.new] ||= {} }
+ end
+ end;
+ end
end
diff --git a/test/ruby/test_integer.rb b/test/ruby/test_integer.rb
index a3e64ddb38..3349a1c493 100644
--- a/test/ruby/test_integer.rb
+++ b/test/ruby/test_integer.rb
@@ -138,20 +138,6 @@ class TestInteger < Test::Unit::TestCase
assert_equal(1234, Integer(1234))
assert_equal(1, Integer(1.234))
- # base argument
- assert_equal(1234, Integer("1234", 10))
- assert_equal(668, Integer("1234", 8))
- assert_equal(4660, Integer("1234", 16))
- assert_equal(49360, Integer("1234", 36))
- # decimal, not octal
- assert_equal(1234, Integer("01234", 10))
- assert_raise(ArgumentError) { Integer("0x123", 10) }
- assert_raise(ArgumentError) { Integer(1234, 10) }
- assert_raise(ArgumentError) { Integer(12.34, 10) }
- assert_raise(ArgumentError) { Integer(Object.new, 1) }
-
- assert_raise(ArgumentError) { Integer(1, 1, 1) }
-
assert_equal(2 ** 50, Integer(2.0 ** 50))
assert_raise(TypeError) { Integer(nil) }
@@ -252,6 +238,32 @@ class TestInteger < Test::Unit::TestCase
assert_equal(16, Integer(obj))
end
+ def test_Integer_with_base
+ assert_equal(1234, Integer("1234", 10))
+ assert_equal(668, Integer("1234", 8))
+ assert_equal(4660, Integer("1234", 16))
+ assert_equal(49360, Integer("1234", 36))
+ # decimal, not octal
+ assert_equal(1234, Integer("01234", 10))
+ assert_raise(ArgumentError) { Integer("0x123", 10) }
+ assert_raise(ArgumentError) { Integer(1234, 10) }
+ assert_raise(ArgumentError) { Integer(12.34, 10) }
+ assert_raise(ArgumentError) { Integer(Object.new, 1) }
+
+ assert_raise(ArgumentError) { Integer(1, 1, 1) }
+
+ def (base = Object.new).to_int
+ 8
+ end
+ assert_equal(8, Integer("10", base))
+
+ assert_raise(TypeError) { Integer("10", "8") }
+ def (base = Object.new).to_int
+ "8"
+ end
+ assert_raise(TypeError) { Integer("10", base) }
+ end
+
def test_int_p
assert_not_predicate(1.0, :integer?)
assert_predicate(1, :integer?)
@@ -309,23 +321,34 @@ class TestInteger < Test::Unit::TestCase
begin;
called = false
Integer.class_eval do
- alias old_plus +
- undef +
- define_method(:+){|x| called = true; 1}
+ alias old_succ succ
+ undef succ
+ define_method(:succ){|x| called = true; x+1}
alias old_lt <
undef <
define_method(:<){|x| called = true}
end
+
+ fix = 1
+ fix.times{break 0}
+ fix_called = called
+
+ called = false
+
big = 2**65
big.times{break 0}
+ big_called = called
+
Integer.class_eval do
- undef +
- alias + old_plus
+ undef succ
+ alias succ old_succ
undef <
alias < old_lt
end
+
+ # Asssert that Fixnum and Bignum behave consistently
bug18377 = "[ruby-core:106361]"
- assert_equal(false, called, bug18377)
+ assert_equal(fix_called, big_called, bug18377)
end;
end
@@ -681,6 +704,14 @@ class TestInteger < Test::Unit::TestCase
def test_fdiv
assert_equal(1.0, 1.fdiv(1))
assert_equal(0.5, 1.fdiv(2))
+
+ m = 50 << Float::MANT_DIG
+ prev = 1.0
+ (1..100).each do |i|
+ val = (m + i).fdiv(m)
+ assert_operator val, :>=, prev, "1+epsilon*(#{i}/100)"
+ prev = val
+ end
end
def test_obj_fdiv
diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb
index aad1530588..476d9f882f 100644
--- a/test/ruby/test_io.rb
+++ b/test/ruby/test_io.rb
@@ -1898,6 +1898,110 @@ class TestIO < Test::Unit::TestCase
end)
end
+ def test_readline_bad_param_raises
+ File.open(__FILE__) do |f|
+ assert_raise(TypeError) do
+ f.readline Object.new
+ end
+ end
+
+ File.open(__FILE__) do |f|
+ assert_raise(TypeError) do
+ f.readline 1, 2
+ end
+ end
+ end
+
+ def test_readline_raises
+ File.open(__FILE__) do |f|
+ assert_equal File.read(__FILE__), f.readline(nil)
+ assert_raise(EOFError) do
+ f.readline
+ end
+ end
+ end
+
+ def test_readline_separators
+ File.open(__FILE__) do |f|
+ line = f.readline("def")
+ assert_equal File.read(__FILE__)[/\A.*?def/m], line
+ end
+
+ File.open(__FILE__) do |f|
+ line = f.readline("def", chomp: true)
+ assert_equal File.read(__FILE__)[/\A.*?(?=def)/m], line
+ end
+ end
+
+ def test_readline_separators_limits
+ t = Tempfile.open("readline_limit")
+ str = "#" * 50
+ sep = "def"
+
+ t.write str
+ t.write sep
+ t.write str
+ t.flush
+
+ # over limit
+ File.open(t.path) do |f|
+ line = f.readline sep, str.bytesize
+ assert_equal(str, line)
+ end
+
+ # under limit
+ File.open(t.path) do |f|
+ line = f.readline(sep, str.bytesize + 5)
+ assert_equal(str + sep, line)
+ end
+
+ # under limit + chomp
+ File.open(t.path) do |f|
+ line = f.readline(sep, str.bytesize + 5, chomp: true)
+ assert_equal(str, line)
+ end
+ ensure
+ t&.close!
+ end
+
+ def test_readline_limit_without_separator
+ t = Tempfile.open("readline_limit")
+ str = "#" * 50
+ sep = "\n"
+
+ t.write str
+ t.write sep
+ t.write str
+ t.flush
+
+ # over limit
+ File.open(t.path) do |f|
+ line = f.readline str.bytesize
+ assert_equal(str, line)
+ end
+
+ # under limit
+ File.open(t.path) do |f|
+ line = f.readline(str.bytesize + 5)
+ assert_equal(str + sep, line)
+ end
+
+ # under limit + chomp
+ File.open(t.path) do |f|
+ line = f.readline(str.bytesize + 5, chomp: true)
+ assert_equal(str, line)
+ end
+ ensure
+ t&.close!
+ end
+
+ def test_readline_chomp_true
+ File.open(__FILE__) do |f|
+ line = f.readline(chomp: true)
+ assert_equal File.readlines(__FILE__).first.chomp, line
+ end
+ end
+
def test_set_lineno_readline
pipe(proc do |w|
w.puts "foo"
@@ -2412,15 +2516,19 @@ class TestIO < Test::Unit::TestCase
end
def test_open_pipe
- open("|" + EnvUtil.rubybin, "r+") do |f|
- f.puts "puts 'foo'"
- f.close_write
- assert_equal("foo\n", f.read)
+ assert_deprecated_warning(/Kernel#open with a leading '\|'/) do # https://bugs.ruby-lang.org/issues/19630
+ open("|" + EnvUtil.rubybin, "r+") do |f|
+ f.puts "puts 'foo'"
+ f.close_write
+ assert_equal("foo\n", f.read)
+ end
end
end
def test_read_command
- assert_equal("foo\n", IO.read("|echo foo"))
+ assert_deprecated_warning(/IO process creation with a leading '\|'/) do # https://bugs.ruby-lang.org/issues/19630
+ assert_equal("foo\n", IO.read("|echo foo"))
+ end
assert_raise(Errno::ENOENT, Errno::EINVAL) do
File.read("|#{EnvUtil.rubybin} -e puts")
end
@@ -2434,7 +2542,9 @@ class TestIO < Test::Unit::TestCase
Class.new(IO).binread("|#{EnvUtil.rubybin} -e puts")
end
assert_raise(Errno::ESPIPE) do
- IO.read("|echo foo", 1, 1)
+ assert_deprecated_warning(/IO process creation with a leading '\|'/) do # https://bugs.ruby-lang.org/issues/19630
+ IO.read("|echo foo", 1, 1)
+ end
end
end
@@ -2619,11 +2729,16 @@ class TestIO < Test::Unit::TestCase
def test_foreach
a = []
- IO.foreach("|" + EnvUtil.rubybin + " -e 'puts :foo; puts :bar; puts :baz'") {|x| a << x }
+
+ assert_deprecated_warning(/IO process creation with a leading '\|'/) do # https://bugs.ruby-lang.org/issues/19630
+ IO.foreach("|" + EnvUtil.rubybin + " -e 'puts :foo; puts :bar; puts :baz'") {|x| a << x }
+ end
assert_equal(["foo\n", "bar\n", "baz\n"], a)
a = []
- IO.foreach("|" + EnvUtil.rubybin + " -e 'puts :zot'", :open_args => ["r"]) {|x| a << x }
+ assert_deprecated_warning(/IO process creation with a leading '\|'/) do # https://bugs.ruby-lang.org/issues/19630
+ IO.foreach("|" + EnvUtil.rubybin + " -e 'puts :zot'", :open_args => ["r"]) {|x| a << x }
+ end
assert_equal(["zot\n"], a)
make_tempfile {|t|
diff --git a/test/ruby/test_io_buffer.rb b/test/ruby/test_io_buffer.rb
index 75ec4016fa..7a58ec0c5a 100644
--- a/test/ruby/test_io_buffer.rb
+++ b/test/ruby/test_io_buffer.rb
@@ -80,7 +80,7 @@ class TestIOBuffer < Test::Unit::TestCase
end
def test_file_mapped_invalid
- assert_raise NoMethodError do
+ assert_raise TypeError do
IO::Buffer.map("foobar")
end
end
@@ -102,11 +102,6 @@ class TestIOBuffer < Test::Unit::TestCase
IO::Buffer.for(string) do |buffer|
refute buffer.readonly?
- # Cannot modify string as it's locked by the buffer:
- assert_raise RuntimeError do
- string[0] = "h"
- end
-
buffer.set_value(:U8, 0, "h".ord)
# Buffer releases it's ownership of the string:
@@ -116,6 +111,16 @@ class TestIOBuffer < Test::Unit::TestCase
end
end
+ def test_string_mapped_buffer_locked
+ string = "Hello World"
+ IO::Buffer.for(string) do |buffer|
+ # Cannot modify string as it's locked by the buffer:
+ assert_raise RuntimeError do
+ string[0] = "h"
+ end
+ end
+ end
+
def test_non_string
not_string = Object.new
@@ -194,6 +199,14 @@ class TestIOBuffer < Test::Unit::TestCase
assert_positive buffer2 <=> buffer1
end
+ def test_compare_zero_length
+ buffer1 = IO::Buffer.new(0)
+ buffer2 = IO::Buffer.new(1)
+
+ assert_negative buffer1 <=> buffer2
+ assert_positive buffer2 <=> buffer1
+ end
+
def test_slice
buffer = IO::Buffer.new(128)
slice = buffer.slice(8, 32)
@@ -251,6 +264,26 @@ class TestIOBuffer < Test::Unit::TestCase
chunk = buffer.get_string(0, message.bytesize, Encoding::BINARY)
assert_equal Encoding::BINARY, chunk.encoding
+
+ assert_raise_with_message(ArgumentError, /bigger than the buffer size/) do
+ buffer.get_string(0, 129)
+ end
+
+ assert_raise_with_message(ArgumentError, /bigger than the buffer size/) do
+ buffer.get_string(129)
+ end
+
+ assert_raise_with_message(ArgumentError, /Offset can't be negative/) do
+ buffer.get_string(-1)
+ end
+ end
+
+ def test_zero_length_get_string
+ buffer = IO::Buffer.new.slice(0, 0)
+ assert_equal "", buffer.get_string
+
+ buffer = IO::Buffer.new(0)
+ assert_equal "", buffer.get_string
end
# We check that values are correctly round tripped.
@@ -299,6 +332,13 @@ class TestIOBuffer < Test::Unit::TestCase
end
end
+ def test_zero_length_get_set_values
+ buffer = IO::Buffer.new(0)
+
+ assert_equal [], buffer.get_values([], 0)
+ assert_equal 0, buffer.set_values([], 0, [])
+ end
+
def test_values
buffer = IO::Buffer.new(128)
@@ -323,6 +363,12 @@ class TestIOBuffer < Test::Unit::TestCase
end
end
+ def test_zero_length_each
+ buffer = IO::Buffer.new(0)
+
+ assert_equal [], buffer.each(:U8).to_a
+ end
+
def test_each_byte
string = "The quick brown fox jumped over the lazy dog."
buffer = IO::Buffer.for(string)
@@ -330,6 +376,12 @@ class TestIOBuffer < Test::Unit::TestCase
assert_equal string.bytes, buffer.each_byte.to_a
end
+ def test_zero_length_each_byte
+ buffer = IO::Buffer.new(0)
+
+ assert_equal [], buffer.each_byte.to_a
+ end
+
def test_clear
buffer = IO::Buffer.new(16)
buffer.set_string("Hello World!")
@@ -500,4 +552,24 @@ class TestIOBuffer < Test::Unit::TestCase
rescue NotImplementedError
omit "Fork/shared memory is not supported."
end
+
+ def test_private
+ Tempfile.create(%w"buffer .txt") do |file|
+ file.write("Hello World")
+
+ buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::PRIVATE)
+ begin
+ assert buffer.private?
+ refute buffer.readonly?
+
+ buffer.set_string("J")
+
+ # It was not changed because the mapping was private:
+ file.seek(0)
+ assert_equal "Hello World", file.read
+ ensure
+ buffer&.free
+ end
+ end
+ end
end
diff --git a/test/ruby/test_io_m17n.rb b/test/ruby/test_io_m17n.rb
index 267975d4ac..b01d627d92 100644
--- a/test/ruby/test_io_m17n.rb
+++ b/test/ruby/test_io_m17n.rb
@@ -1396,23 +1396,27 @@ EOT
end
def test_open_pipe_r_enc
- open("|#{EnvUtil.rubybin} -e 'putc 255'", "r:ascii-8bit") {|f|
- assert_equal(Encoding::ASCII_8BIT, f.external_encoding)
- assert_equal(nil, f.internal_encoding)
- s = f.read
- assert_equal(Encoding::ASCII_8BIT, s.encoding)
- assert_equal("\xff".force_encoding("ascii-8bit"), s)
- }
+ EnvUtil.suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ open("|#{EnvUtil.rubybin} -e 'putc 255'", "r:ascii-8bit") {|f|
+ assert_equal(Encoding::ASCII_8BIT, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ s = f.read
+ assert_equal(Encoding::ASCII_8BIT, s.encoding)
+ assert_equal("\xff".force_encoding("ascii-8bit"), s)
+ }
+ end
end
def test_open_pipe_r_enc2
- open("|#{EnvUtil.rubybin} -e 'putc \"\\u3042\"'", "r:UTF-8") {|f|
- assert_equal(Encoding::UTF_8, f.external_encoding)
- assert_equal(nil, f.internal_encoding)
- s = f.read
- assert_equal(Encoding::UTF_8, s.encoding)
- assert_equal("\u3042", s)
- }
+ EnvUtil.suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ open("|#{EnvUtil.rubybin} -e 'putc \"\\u3042\"'", "r:UTF-8") {|f|
+ assert_equal(Encoding::UTF_8, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ s = f.read
+ assert_equal(Encoding::UTF_8, s.encoding)
+ assert_equal("\u3042", s)
+ }
+ end
end
def test_s_foreach_enc
diff --git a/test/ruby/test_iseq.rb b/test/ruby/test_iseq.rb
index 682fa52570..d2a39e673f 100644
--- a/test/ruby/test_iseq.rb
+++ b/test/ruby/test_iseq.rb
@@ -157,18 +157,18 @@ class TestISeq < Test::Unit::TestCase
y = nil.instance_eval do
eval("proc {#{name} = []; proc {|x| #{name}}}").call
end
- assert_raise_with_message(Ractor::IsolationError, /`#{name}'/) do
+ assert_raise_with_message(Ractor::IsolationError, /'#{name}'/) do
Ractor.make_shareable(y)
end
obj = Object.new
def obj.foo(*) nil.instance_eval{ ->{super} } end
- assert_raise_with_message(Ractor::IsolationError, /refer unshareable object \[\] from variable `\*'/) do
+ assert_raise_with_message(Ractor::IsolationError, /refer unshareable object \[\] from variable '\*'/) do
Ractor.make_shareable(obj.foo)
end
end
def test_disasm_encoding
- src = "\u{3042} = 1; \u{3042}; \u{3043}"
+ src = +"\u{3042} = 1; \u{3042}; \u{3043}"
asm = compile(src).disasm
assert_equal(src.encoding, asm.encoding)
assert_predicate(asm, :valid_encoding?)
@@ -367,7 +367,7 @@ class TestISeq < Test::Unit::TestCase
f.puts "end"
f.close
path = f.path
- assert_in_out_err(%W[- #{path}], "#{<<-"begin;"}\n#{<<-"end;"}", /unexpected `end'/, [], success: true)
+ assert_in_out_err(%W[- #{path}], "#{<<-"begin;"}\n#{<<-"end;"}", /unexpected 'end'/, [], success: true)
begin;
path = ARGV[0]
begin
@@ -497,7 +497,8 @@ class TestISeq < Test::Unit::TestCase
[7, :line],
[9, :return]]],
[["ensure in foo@2", [[7, :line]]]],
- [["rescue in foo@4", [[5, :line]]]]]],
+ [["rescue in foo@4", [[5, :line],
+ [5, :rescue]]]]]],
[["<class:D>@17", [[17, :class],
[18, :end]]]]], collect_iseq.call(sample_iseq)
end
@@ -565,6 +566,23 @@ class TestISeq < Test::Unit::TestCase
iseq2
end
+ def test_to_binary_with_hidden_local_variables
+ assert_iseq_to_binary("for foo in bar; end")
+
+ bin = RubyVM::InstructionSequence.compile(<<-RUBY).to_binary
+ Object.new.instance_eval do
+ a = []
+ def self.bar; [1] end
+ for foo in bar
+ a << (foo * 2)
+ end
+ a
+ end
+ RUBY
+ v = RubyVM::InstructionSequence.load_from_binary(bin).eval
+ assert_equal([2], v)
+ end
+
def test_to_binary_with_objects
assert_iseq_to_binary("[]"+100.times.map{|i|"<</#{i}/"}.join)
assert_iseq_to_binary("@x ||= (1..2)")
@@ -769,4 +787,74 @@ class TestISeq < Test::Unit::TestCase
assert_syntax_error("false and break", mesg)
assert_syntax_error("if false and break; end", mesg)
end
+
+ def test_unreachable_pattern_matching
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", %w[1])
+ begin;
+ if true or {a: 0} in {a:}
+ p 1
+ else
+ p a
+ end
+ end;
+ end
+
+ def test_loading_kwargs_memory_leak
+ assert_no_memory_leak([], "#{<<~"begin;"}", "#{<<~'end;'}", rss: true)
+ a = RubyVM::InstructionSequence.compile("foo(bar: :baz)").to_binary
+ begin;
+ 1_000_000.times do
+ RubyVM::InstructionSequence.load_from_binary(a)
+ end
+ end;
+ end
+
+ def test_ibf_bignum
+ iseq = RubyVM::InstructionSequence.compile("0x0"+"_0123_4567_89ab_cdef"*5)
+ expected = iseq.eval
+ result = RubyVM::InstructionSequence.load_from_binary(iseq.to_binary).eval
+ assert_equal expected, result, proc {sprintf("expected: %x, result: %x", expected, result)}
+ end
+
+ def test_compile_prism_with_file
+ Tempfile.create(%w"test_iseq .rb") do |f|
+ f.puts "name = 'Prism'; puts 'hello'"
+ f.close
+
+ assert_nothing_raised(TypeError) do
+ RubyVM::InstructionSequence.compile_prism(f)
+ end
+ end
+ end
+
+ def block_using_method
+ yield
+ end
+
+ def block_unused_method
+ end
+
+ def test_unused_param
+ a = RubyVM::InstructionSequence.of(method(:block_using_method)).to_a
+
+ omit 'TODO: Prism' if a.dig(4, :parser) != :"parse.y"
+
+ assert_equal true, a.dig(11, :use_block)
+
+ b = RubyVM::InstructionSequence.of(method(:block_unused_method)).to_a
+ assert_equal nil, b.dig(11, :use_block)
+ end
+
+ def test_compile_prism_with_invalid_object_type
+ assert_raise(TypeError) do
+ RubyVM::InstructionSequence.compile_prism(Object.new)
+ end
+ end
+
+ def test_load_from_binary_only_accepts_string_param
+ assert_raise(TypeError) do
+ var_0 = 0
+ RubyVM::InstructionSequence.load_from_binary(var_0)
+ end
+ end
end
diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb
index ef594bd52e..34a80c3729 100644
--- a/test/ruby/test_keyword.rb
+++ b/test/ruby/test_keyword.rb
@@ -182,6 +182,52 @@ class TestKeywordArguments < Test::Unit::TestCase
[:keyrest, :kw], [:block, :b]], method(:f9).parameters)
end
+ def test_keyword_with_anonymous_keyword_splat
+ def self.a(b: 1, **) [b, **] end
+ kw = {b: 2, c: 3}
+ assert_equal([2, {c: 3}], a(**kw))
+ assert_equal({b: 2, c: 3}, kw)
+ end
+
+ def test_keyword_splat_nil
+ # cfunc call
+ assert_equal(nil, p(**nil))
+
+ def self.a0; end
+ assert_equal(nil, a0(**nil))
+ assert_equal(nil, :a0.to_proc.call(self, **nil))
+ assert_equal(nil, a0(**nil, &:block))
+
+ def self.o(x=1); x end
+ assert_equal(1, o(**nil))
+ assert_equal(2, o(2, **nil))
+ assert_equal(1, o(*nil, **nil))
+ assert_equal(1, o(**nil, **nil))
+ assert_equal({a: 1}, o(a: 1, **nil))
+ assert_equal({a: 1}, o(**nil, a: 1))
+
+ # symproc call
+ assert_equal(1, :o.to_proc.call(self, **nil))
+
+ def self.s(*a); a end
+ assert_equal([], s(**nil))
+ assert_equal([1], s(1, **nil))
+ assert_equal([], s(*nil, **nil))
+
+ def self.kws(**a); a end
+ assert_equal({}, kws(**nil))
+ assert_equal({}, kws(*nil, **nil))
+
+ def self.skws(*a, **kw); [a, kw] end
+ assert_equal([[], {}], skws(**nil))
+ assert_equal([[1], {}], skws(1, **nil))
+ assert_equal([[], {}], skws(*nil, **nil))
+
+ assert_equal({}, {**nil})
+ assert_equal({a: 1}, {a: 1, **nil})
+ assert_equal({a: 1}, {**nil, a: 1})
+ end
+
def test_lambda
f = ->(str: "foo", num: 424242) { [str, num] }
assert_equal(["foo", 424242], f[])
@@ -237,16 +283,16 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(true, Hash.ruby2_keywords_hash?(marked))
end
+ def assert_equal_not_same(kw, res)
+ assert_instance_of(Hash, res)
+ assert_equal(kw, res)
+ assert_not_same(kw, res)
+ end
+
def test_keyword_splat_new
kw = {}
h = {a: 1}
- def self.assert_equal_not_same(kw, res)
- assert_instance_of(Hash, res)
- assert_equal(kw, res)
- assert_not_same(kw, res)
- end
-
def self.yo(**kw) kw end
m = method(:yo)
assert_equal(false, yo(**{}).frozen?)
@@ -459,6 +505,20 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal_not_same h, yo(*a, **h)
end
+ def test_keyword_splat_to_non_keyword_method
+ h = {a: 1}.freeze
+
+ def self.yo(kw) kw end
+ assert_equal_not_same(h, yo(**h))
+ assert_equal_not_same(h, method(:yo).(**h))
+ assert_equal_not_same(h, :yo.to_proc.(self, **h))
+
+ def self.yoa(*kw) kw[0] end
+ assert_equal_not_same(h, yoa(**h))
+ assert_equal_not_same(h, method(:yoa).(**h))
+ assert_equal_not_same(h, :yoa.to_proc.(self, **h))
+ end
+
def test_regular_kwsplat
kw = {}
h = {:a=>1}
@@ -2757,6 +2817,37 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_raise(FrozenError) { c.send(:ruby2_keywords, :baz) }
end
+ def test_anon_splat_ruby2_keywords
+ singleton_class.class_exec do
+ def bar(*a, **kw)
+ [a, kw]
+ end
+
+ ruby2_keywords def bar_anon(*)
+ bar(*)
+ end
+ end
+
+ a = [1, 2]
+ kw = {a: 1}
+ assert_equal([[1, 2], {a: 1}], bar_anon(*a, **kw))
+ assert_equal([1, 2], a)
+ assert_equal({a: 1}, kw)
+ end
+
+ def test_anon_splat_ruby2_keywords_bug_20388
+ extend(Module.new{def process(action, ...) 1 end})
+ extend(Module.new do
+ def process(action, *args)
+ args.freeze
+ super
+ end
+ ruby2_keywords :process
+ end)
+
+ assert_equal(1, process(:foo, bar: :baz))
+ end
+
def test_top_ruby2_keywords
assert_in_out_err([], <<-INPUT, ["[1, 2, 3]", "{:k=>1}"], [])
def bar(*a, **kw)
@@ -3932,6 +4023,20 @@ class TestKeywordArguments < Test::Unit::TestCase
}, bug8964
end
+ def test_large_kwsplat_to_method_taking_kw_and_kwsplat
+ assert_separately(['-'], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ n = 100000
+ x = Fiber.new do
+ h = {kw: 2}
+ n.times{|i| h[i.to_s.to_sym] = i}
+ def self.f(kw: 1, **kws) kws.size end
+ f(**h)
+ end.resume
+ assert_equal(n, x)
+ end;
+ end
+
def test_dynamic_symbol_keyword
bug10266 = '[ruby-dev:48564] [Bug #10266]'
assert_separately(['-', bug10266], "#{<<~"begin;"}\n#{<<~'end;'}")
diff --git a/test/ruby/test_lambda.rb b/test/ruby/test_lambda.rb
index 9949fab8c7..7738034240 100644
--- a/test/ruby/test_lambda.rb
+++ b/test/ruby/test_lambda.rb
@@ -177,32 +177,6 @@ class TestLambdaParameters < Test::Unit::TestCase
RUBY
end
- def pass_along(&block)
- lambda(&block)
- end
-
- def pass_along2(&block)
- pass_along(&block)
- end
-
- def test_create_non_lambda_for_proc_one_level
- prev_warning, Warning[:deprecated] = Warning[:deprecated], false
- f = pass_along {}
- refute_predicate(f, :lambda?, '[Bug #15620]')
- assert_nothing_raised(ArgumentError) { f.call(:extra_arg) }
- ensure
- Warning[:deprecated] = prev_warning
- end
-
- def test_create_non_lambda_for_proc_two_levels
- prev_warning, Warning[:deprecated] = Warning[:deprecated], false
- f = pass_along2 {}
- refute_predicate(f, :lambda?, '[Bug #15620]')
- assert_nothing_raised(ArgumentError) { f.call(:extra_arg) }
- ensure
- Warning[:deprecated] = prev_warning
- end
-
def test_instance_exec
bug12568 = '[ruby-core:76300] [Bug #12568]'
assert_nothing_raised(ArgumentError, bug12568) do
diff --git a/test/ruby/test_lazy_enumerator.rb b/test/ruby/test_lazy_enumerator.rb
index 32bf097318..22127e903a 100644
--- a/test/ruby/test_lazy_enumerator.rb
+++ b/test/ruby/test_lazy_enumerator.rb
@@ -282,6 +282,11 @@ class TestLazyEnumerator < Test::Unit::TestCase
assert_equal(3, a.current)
end
+ def test_zip_map_lambda_bug_19569
+ ary = [1, 2, 3].to_enum.lazy.zip([:a, :b, :c]).map(&:last).to_a
+ assert_equal([:a, :b, :c], ary)
+ end
+
def test_take
a = Step.new(1..10)
assert_equal(1, a.take(5).first)
diff --git a/test/ruby/test_literal.rb b/test/ruby/test_literal.rb
index 99dd3a0c56..c6154af1f6 100644
--- a/test/ruby/test_literal.rb
+++ b/test/ruby/test_literal.rb
@@ -142,6 +142,8 @@ class TestRubyLiteral < Test::Unit::TestCase
end
def test_frozen_string
+ default = eval("'test'").frozen?
+
all_assertions do |a|
a.for("false with indicator") do
str = eval("# -*- frozen-string-literal: false -*-\n""'foo'")
@@ -161,19 +163,19 @@ class TestRubyLiteral < Test::Unit::TestCase
end
a.for("false with preceding garbage") do
str = eval("# x frozen-string-literal: false\n""'foo'")
- assert_not_predicate(str, :frozen?)
+ assert_equal(default, str.frozen?)
end
a.for("true with preceding garbage") do
str = eval("# x frozen-string-literal: true\n""'foo'")
- assert_not_predicate(str, :frozen?)
+ assert_equal(default, str.frozen?)
end
a.for("false with succeeding garbage") do
str = eval("# frozen-string-literal: false x\n""'foo'")
- assert_not_predicate(str, :frozen?)
+ assert_equal(default, str.frozen?)
end
a.for("true with succeeding garbage") do
str = eval("# frozen-string-literal: true x\n""'foo'")
- assert_not_predicate(str, :frozen?)
+ assert_equal(default, str.frozen?)
end
end
end
@@ -184,6 +186,11 @@ class TestRubyLiteral < Test::Unit::TestCase
list.each { |str| assert_predicate str, :frozen? }
end
+ def test_string_in_hash_literal
+ hash = eval("# frozen-string-literal: false\n""{foo: 'foo'}")
+ assert_not_predicate(hash[:foo], :frozen?)
+ end
+
if defined?(RubyVM::InstructionSequence.compile_option) and
RubyVM::InstructionSequence.compile_option.key?(:debug_frozen_string_literal)
def test_debug_frozen_string
@@ -496,10 +503,11 @@ class TestRubyLiteral < Test::Unit::TestCase
'1.0i',
'1.72723e-77',
'//',
+ '__LINE__',
+ '__FILE__',
+ '__ENCODING__',
) do |key|
- assert_warning(/key #{Regexp.quote(eval(key).inspect)} is duplicated/) do
- eval("{#{key} => :bar, #{key} => :foo}")
- end
+ assert_warning(/key #{Regexp.quote(eval(key).inspect)} is duplicated/) { eval("{#{key} => :bar, #{key} => :foo}") }
end
end
diff --git a/test/ruby/test_m17n.rb b/test/ruby/test_m17n.rb
index 28293ffffc..a6493374b5 100644
--- a/test/ruby/test_m17n.rb
+++ b/test/ruby/test_m17n.rb
@@ -1090,7 +1090,23 @@ class TestM17N < Test::Unit::TestCase
assert_nil(e("\xa1\xa2\xa3\xa4").index(e("\xa3")))
assert_nil(e("\xa1\xa2\xa3\xa4").rindex(e("\xa3")))
s = e("\xa3\xb0\xa3\xb1\xa3\xb2\xa3\xb3\xa3\xb4")
- assert_raise(Encoding::CompatibilityError){s.rindex(a("\xb1\xa3"))}
+
+ a_with_e = /EUC-JP and BINARY \(ASCII-8BIT\)/
+ assert_raise_with_message(Encoding::CompatibilityError, a_with_e) do
+ s.index(a("\xb1\xa3"))
+ end
+ assert_raise_with_message(Encoding::CompatibilityError, a_with_e) do
+ s.rindex(a("\xb1\xa3"))
+ end
+
+ a_with_e = /BINARY \(ASCII-8BIT\) regexp with EUC-JP string/
+ assert_raise_with_message(Encoding::CompatibilityError, a_with_e) do
+ s.index(Regexp.new(a("\xb1\xa3")))
+ end
+ assert_raise_with_message(Encoding::CompatibilityError, a_with_e) do
+ s.rindex(Regexp.new(a("\xb1\xa3")))
+ end
+
bug11488 = '[ruby-core:70592] [Bug #11488]'
each_encoding("abcdef", "def") do |str, substr|
assert_equal(3, str.index(substr), bug11488)
diff --git a/test/ruby/test_marshal.rb b/test/ruby/test_marshal.rb
index 6cd0b9acc3..79d9577737 100644
--- a/test/ruby/test_marshal.rb
+++ b/test/ruby/test_marshal.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: false
require 'test/unit'
-require 'tempfile'
require_relative 'marshaltestlib'
class TestMarshal < Test::Unit::TestCase
@@ -314,11 +313,10 @@ class TestMarshal < Test::Unit::TestCase
assert_equal(c, Marshal.load(Marshal.dump(c)), bug2109)
assert_nothing_raised(ArgumentError, '[ruby-dev:40386]') do
- re = Tempfile.create("marshal_regexp") do |f|
- f.binmode.write("\x04\bI/\x00\x00\x06:\rencoding\"\rUS-ASCII")
- f.rewind
- re2 = Marshal.load(f)
- re2
+ re = IO.pipe do |r, w|
+ w.write("\x04\bI/\x00\x00\x06:\rencoding\"\rUS-ASCII")
+ # Marshal.load would not overread and block
+ Marshal.load(r)
end
assert_equal(//, re)
end
@@ -611,6 +609,8 @@ class TestMarshal < Test::Unit::TestCase
def test_continuation
EnvUtil.suppress_warning {require "continuation"}
+ omit 'requires callcc support' unless respond_to?(:callcc)
+
c = Bug9523.new
assert_raise_with_message(RuntimeError, /Marshal\.dump reentered at marshal_dump/) do
Marshal.dump(c)
diff --git a/test/ruby/test_math.rb b/test/ruby/test_math.rb
index 73f44c6ae3..6e67099c6b 100644
--- a/test/ruby/test_math.rb
+++ b/test/ruby/test_math.rb
@@ -5,6 +5,7 @@ class TestMath < Test::Unit::TestCase
def assert_infinity(a, *rest)
rest = ["not infinity: #{a.inspect}"] if rest.empty?
assert_predicate(a, :infinite?, *rest)
+ assert_predicate(a, :positive?, *rest)
end
def assert_nan(a, *rest)
@@ -165,6 +166,9 @@ class TestMath < Test::Unit::TestCase
assert_nothing_raised { assert_nan(Math.log(0.0, 0.0)) }
assert_nothing_raised { assert_nan(Math.log(Float::NAN)) }
assert_nothing_raised { assert_nan(Math.log(1.0, Float::NAN)) }
+ assert_nothing_raised { assert_infinity(-Math.log(0)) }
+ assert_nothing_raised { assert_infinity(-Math.log(0, 2)) }
+ check(307.95368556425274, Math.log(2**1023, 10))
end
def test_log2
@@ -179,6 +183,7 @@ class TestMath < Test::Unit::TestCase
assert_raise_with_message(Math::DomainError, /\blog2\b/) { Math.log2(-1.0) }
assert_raise_with_message(Math::DomainError, /\blog2\b/) { Math.log2(-Float::EPSILON) }
assert_nothing_raised { assert_nan(Math.log2(Float::NAN)) }
+ assert_nothing_raised { assert_infinity(-Math.log2(0)) }
end
def test_log10
@@ -193,6 +198,7 @@ class TestMath < Test::Unit::TestCase
assert_raise_with_message(Math::DomainError, /\blog10\b/) { Math.log10(-1.0) }
assert_raise_with_message(Math::DomainError, /\blog10\b/) { Math.log10(-Float::EPSILON) }
assert_nothing_raised { assert_nan(Math.log10(Float::NAN)) }
+ assert_nothing_raised { assert_infinity(-Math.log10(0)) }
end
def test_sqrt
@@ -277,8 +283,7 @@ class TestMath < Test::Unit::TestCase
assert_raise_with_message(Math::DomainError, /\bgamma\b/) { Math.gamma(-1.0) }
x = Math.gamma(-0.0)
mesg = "Math.gamma(-0.0) should be -INF"
- assert_infinity(x, mesg)
- assert_predicate(x, :negative?, mesg)
+ assert_infinity(-x, mesg)
assert_nan(Math.gamma(Float::NAN))
end
@@ -299,7 +304,6 @@ class TestMath < Test::Unit::TestCase
x, sign = Math.lgamma(-0.0)
mesg = "Math.lgamma(-0.0) should be [INF, -1]"
assert_infinity(x, mesg)
- assert_predicate(x, :positive?, mesg)
assert_equal(-1, sign, mesg)
x, sign = Math.lgamma(Float::NAN)
assert_nan(x)
diff --git a/test/ruby/test_method.rb b/test/ruby/test_method.rb
index 80b8fe277b..5301b51650 100644
--- a/test/ruby/test_method.rb
+++ b/test/ruby/test_method.rb
@@ -450,6 +450,18 @@ class TestMethod < Test::Unit::TestCase
assert_equal(:bar, m.clone.bar)
end
+ def test_clone_under_gc_compact_stress
+ omit "compaction doesn't work well on s390x" if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077
+ EnvUtil.under_gc_compact_stress do
+ o = Object.new
+ def o.foo; :foo; end
+ m = o.method(:foo)
+ def m.bar; :bar; end
+ assert_equal(:foo, m.clone.call)
+ assert_equal(:bar, m.clone.bar)
+ end
+ end
+
def test_inspect
o = Object.new
def o.foo; end; line_no = __LINE__
@@ -1431,25 +1443,25 @@ class TestMethod < Test::Unit::TestCase
end
def test_argument_error_location
- body = <<-'END_OF_BODY'
- eval <<-'EOS'
- $line_lambda = __LINE__; $f = lambda do
- _x = 1
- end
- $line_method = __LINE__; def foo
- _x = 1
- end
- begin
- $f.call(1)
- rescue ArgumentError => e
- assert_equal "(eval):#{$line_lambda.to_s}:in `block in <main>'", e.backtrace.first
- end
- begin
- foo(1)
- rescue ArgumentError => e
- assert_equal "(eval):#{$line_method}:in `foo'", e.backtrace.first
- end
- EOS
+ body = <<~'END_OF_BODY'
+ eval <<~'EOS', nil, "main.rb"
+ $line_lambda = __LINE__; $f = lambda do
+ _x = 1
+ end
+ $line_method = __LINE__; def foo
+ _x = 1
+ end
+ begin
+ $f.call(1)
+ rescue ArgumentError => e
+ assert_equal "main.rb:#{$line_lambda}:in 'block in <main>'", e.backtrace.first
+ end
+ begin
+ foo(1)
+ rescue ArgumentError => e
+ assert_equal "main.rb:#{$line_method}:in 'foo'", e.backtrace.first
+ end
+ EOS
END_OF_BODY
assert_separately [], body
@@ -1458,7 +1470,7 @@ class TestMethod < Test::Unit::TestCase
end
def test_zsuper_private_override_instance_method
- assert_separately(%w(--disable-gems), <<-'end;', timeout: 30)
+ assert_separately([], <<-'end;', timeout: 30)
# Bug #16942 [ruby-core:98691]
module M
def x
@@ -1479,7 +1491,7 @@ class TestMethod < Test::Unit::TestCase
end
def test_override_optimized_method_on_class_using_prepend
- assert_separately(%w(--disable-gems), <<-'end;', timeout: 30)
+ assert_separately([], <<-'end;', timeout: 30)
# Bug #17725 [ruby-core:102884]
$VERBOSE = nil
String.prepend(Module.new)
@@ -1603,4 +1615,96 @@ class TestMethod < Test::Unit::TestCase
def test_invalidating_CC_ASAN
assert_ruby_status(['-e', 'using Module.new'])
end
+
+ def test_kwarg_eval_memory_leak
+ assert_no_memory_leak([], "", <<~RUBY, rss: true, limit: 1.2)
+ 100_000.times do
+ eval("Hash.new(foo: 123)")
+ end
+ RUBY
+ end
+
+ def test_warn_unused_block
+ assert_in_out_err '-w', <<-'RUBY' do |_out, err, _status|
+ def foo = nil
+ foo{} # warn
+ send(:foo){} # warn
+ b = Proc.new{}
+ foo(&b) # warn
+ RUBY
+ assert_equal 3, err.size
+ err = err.join
+ assert_match(/-:2: warning/, err)
+ assert_match(/-:3: warning/, err)
+ assert_match(/-:5: warning/, err)
+ end
+
+ assert_in_out_err '-w', <<-'RUBY' do |_out, err, _status|
+ def foo = nil
+ 10.times{foo{}} # warn once
+ RUBY
+ assert_equal 1, err.size
+ end
+
+ assert_in_out_err '-w', <<-'RUBY' do |_out, err, _status|
+ def foo = nil; b = nil
+ foo(&b) # no warning
+ 1.object_id{} # no warning because it is written in C
+
+ class C
+ def initialize
+ end
+ end
+ C.new{} # no warning
+
+ RUBY
+ assert_equal 0, err.size
+ end
+
+ assert_in_out_err '-w', <<-'RUBY' do |_out, err, _status|
+ class C0
+ def f1 = nil
+ def f2 = nil
+ def f3 = nil
+ def f4 = nil
+ def f5 = nil
+ def f6 = nil
+ end
+
+ class C1 < C0
+ def f1 = super # zsuper / use
+ def f2 = super() # super / use
+ def f3(&_) = super(&_) # super / use
+ def f4 = super(&nil) # super / unuse
+ def f5 = super(){} # super / unuse
+ def f6 = super{} # zsuper / unuse
+ end
+
+ C1.new.f1{} # no warning
+ C1.new.f2{} # no warning
+ C1.new.f3{} # no warning
+ C1.new.f4{} # warning
+ C1.new.f5{} # warning
+ C1.new.f6{} # warning
+ RUBY
+ assert_equal 3, err.size, err.join("\n")
+ assert_match(/-:22: warning.+f4/, err.join)
+ assert_match(/-:23: warning.+f5/, err.join)
+ assert_match(/-:24: warning.+f6/, err.join)
+ end
+
+ assert_in_out_err '-w', <<-'RUBY' do |_out, err, _status|
+ class C0
+ def f = yield
+ end
+
+ class C1 < C0
+ def f = nil
+ end
+
+ C1.new.f{} # do not warn on duck typing
+ RUBY
+ assert_equal 0, err.size, err.join("\n")
+ end
+ end
end
diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb
index 81c5345a5f..75d8d909d7 100644
--- a/test/ruby/test_module.rb
+++ b/test/ruby/test_module.rb
@@ -253,6 +253,14 @@ class TestModule < Test::Unit::TestCase
assert_operator(Math, :const_defined?, "PI")
assert_not_operator(Math, :const_defined?, :IP)
assert_not_operator(Math, :const_defined?, "IP")
+
+ # Test invalid symbol name
+ # [Bug #20245]
+ EnvUtil.under_gc_stress do
+ assert_raise(EncodingError) do
+ Math.const_defined?("\xC3")
+ end
+ end
end
def each_bad_constants(m, &b)
@@ -1469,7 +1477,7 @@ class TestModule < Test::Unit::TestCase
end
%w(object_id __send__ initialize).each do |n|
- assert_in_out_err([], <<-INPUT, [], %r"warning: undefining `#{n}' may cause serious problems$")
+ assert_in_out_err([], <<-INPUT, [], %r"warning: undefining '#{n}' may cause serious problems$")
$VERBOSE = false
Class.new.instance_eval { undef_method(:#{n}) }
INPUT
@@ -3292,6 +3300,47 @@ class TestModule < Test::Unit::TestCase
CODE
end
+ def test_complemented_method_entry_memory_leak
+ # [Bug #19894] [Bug #19896]
+ assert_no_memory_leak([], <<~PREP, <<~CODE, rss: true)
+ code = proc do
+ $c = Class.new do
+ def foo; end
+ end
+
+ $m = Module.new do
+ refine $c do
+ def foo; end
+ end
+ end
+
+ Class.new do
+ using $m
+
+ def initialize
+ o = $c.new
+ o.method(:foo).unbind
+ end
+ end.new
+ end
+ 1_000.times(&code)
+ PREP
+ 300_000.times(&code)
+ CODE
+ end
+
+ def test_module_clone_memory_leak
+ # [Bug #19901]
+ assert_no_memory_leak([], <<~PREP, <<~CODE, rss: true)
+ code = proc do
+ Module.new.clone
+ end
+ 1_000.times(&code)
+ PREP
+ 1_000_000.times(&code)
+ CODE
+ end
+
private
def assert_top_method_is_private(method)
@@ -3299,7 +3348,7 @@ class TestModule < Test::Unit::TestCase
methods = singleton_class.private_instance_methods(false)
assert_include(methods, :#{method}, ":#{method} should be private")
- assert_raise_with_message(NoMethodError, /^private method `#{method}' called for /) {
+ assert_raise_with_message(NoMethodError, /^private method '#{method}' called for /) {
recv = self
recv.#{method}
}
diff --git a/test/ruby/test_nomethod_error.rb b/test/ruby/test_nomethod_error.rb
index 0306535943..6d413e6391 100644
--- a/test/ruby/test_nomethod_error.rb
+++ b/test/ruby/test_nomethod_error.rb
@@ -85,7 +85,7 @@ class TestNoMethodError < Test::Unit::TestCase
bug3237 = '[ruby-core:29948]'
str = "\u2600"
id = :"\u2604"
- msg = "undefined method `#{id}' for an instance of String"
+ msg = "undefined method '#{id}' for an instance of String"
assert_raise_with_message(NoMethodError, Regexp.compile(Regexp.quote(msg)), bug3237) do
str.__send__(id)
end
diff --git a/test/ruby/test_object.rb b/test/ruby/test_object.rb
index 3c5b6424ba..37c09596a2 100644
--- a/test/ruby/test_object.rb
+++ b/test/ruby/test_object.rb
@@ -355,6 +355,41 @@ class TestObject < Test::Unit::TestCase
end
end
+ def test_remove_instance_variable_re_embed
+ require "objspace"
+
+ c = Class.new do
+ def a = @a
+
+ def b = @b
+
+ def c = @c
+ end
+
+ o1 = c.new
+ o2 = c.new
+
+ o1.instance_variable_set(:@foo, 5)
+ o1.instance_variable_set(:@a, 0)
+ o1.instance_variable_set(:@b, 1)
+ o1.instance_variable_set(:@c, 2)
+ refute_includes ObjectSpace.dump(o1), '"embedded":true'
+ o1.remove_instance_variable(:@foo)
+ assert_includes ObjectSpace.dump(o1), '"embedded":true'
+
+ o2.instance_variable_set(:@a, 0)
+ o2.instance_variable_set(:@b, 1)
+ o2.instance_variable_set(:@c, 2)
+ assert_includes ObjectSpace.dump(o2), '"embedded":true'
+
+ assert_equal(0, o1.a)
+ assert_equal(1, o1.b)
+ assert_equal(2, o1.c)
+ assert_equal(0, o2.a)
+ assert_equal(1, o2.b)
+ assert_equal(2, o2.c)
+ end
+
def test_convert_string
o = Object.new
def o.to_s; 1; end
@@ -423,7 +458,7 @@ class TestObject < Test::Unit::TestCase
end
def test_max_shape_variation_with_performance_warnings
- assert_in_out_err([], <<-INPUT, %w(), /Maximum shapes variations \(8\) reached by Foo, instance variables accesses will be slower\.$/)
+ assert_in_out_err([], <<-INPUT, %w(), /The class Foo reached 8 shape variations, instance variables accesses will be slower and memory usage increased/)
$VERBOSE = false
Warning[:performance] = true
@@ -445,12 +480,12 @@ class TestObject < Test::Unit::TestCase
end
def test_redefine_method_which_may_case_serious_problem
- assert_in_out_err([], <<-INPUT, [], %r"warning: redefining `object_id' may cause serious problems$")
+ assert_in_out_err([], <<-INPUT, [], %r"warning: redefining 'object_id' may cause serious problems$")
$VERBOSE = false
def (Object.new).object_id; end
INPUT
- assert_in_out_err([], <<-INPUT, [], %r"warning: redefining `__send__' may cause serious problems$")
+ assert_in_out_err([], <<-INPUT, [], %r"warning: redefining '__send__' may cause serious problems$")
$VERBOSE = false
def (Object.new).__send__; end
INPUT
@@ -493,7 +528,7 @@ class TestObject < Test::Unit::TestCase
assert_raise(NoMethodError, bug2202) {o2.meth2}
%w(object_id __send__ initialize).each do |m|
- assert_in_out_err([], <<-INPUT, %w(:ok), %r"warning: removing `#{m}' may cause serious problems$")
+ assert_in_out_err([], <<-INPUT, %w(:ok), %r"warning: removing '#{m}' may cause serious problems$")
$VERBOSE = false
begin
Class.new.instance_eval { remove_method(:#{m}) }
diff --git a/test/ruby/test_objectspace.rb b/test/ruby/test_objectspace.rb
index a7cfb064a8..1c97bd517e 100644
--- a/test/ruby/test_objectspace.rb
+++ b/test/ruby/test_objectspace.rb
@@ -66,8 +66,11 @@ End
end
def test_id2ref_invalid_symbol_id
+ # RB_STATIC_SYM_P checks for static symbols by checking that the bottom
+ # 8 bits of the object is equal to RUBY_SYMBOL_FLAG, so we need to make
+ # sure that the bottom 8 bits remain unchanged.
msg = /is not symbol id value/
- assert_raise_with_message(RangeError, msg) { ObjectSpace._id2ref(:a.object_id + GC::INTERNAL_CONSTANTS[:RVALUE_SIZE]) }
+ assert_raise_with_message(RangeError, msg) { ObjectSpace._id2ref(:a.object_id + 256) }
end
def test_count_objects
diff --git a/test/ruby/test_optimization.rb b/test/ruby/test_optimization.rb
index 9ce5371dd0..a7a0582dbb 100644
--- a/test/ruby/test_optimization.rb
+++ b/test/ruby/test_optimization.rb
@@ -16,6 +16,18 @@ class TestRubyOptimization < Test::Unit::TestCase
end;
end
+ def assert_performance_warning(klass, method)
+ assert_in_out_err([], "#{<<-"begin;"}\n#{<<~"end;"}", [], ["-:4: warning: Redefining '#{klass}##{method}' disables interpreter and JIT optimizations"])
+ begin;
+ Warning[:performance] = true
+ class #{klass}
+ undef #{method}
+ def #{method}
+ end
+ end
+ end;
+ end
+
def disasm(name)
RubyVM::InstructionSequence.of(method(name)).disasm
end
@@ -23,102 +35,122 @@ class TestRubyOptimization < Test::Unit::TestCase
def test_fixnum_plus
assert_equal 21, 10 + 11
assert_redefine_method('Integer', '+', 'assert_equal 11, 10 + 11')
+ assert_performance_warning('Integer', '+')
end
def test_fixnum_minus
assert_equal 5, 8 - 3
assert_redefine_method('Integer', '-', 'assert_equal 3, 8 - 3')
+ assert_performance_warning('Integer', '-')
end
def test_fixnum_mul
assert_equal 15, 3 * 5
assert_redefine_method('Integer', '*', 'assert_equal 5, 3 * 5')
+ assert_performance_warning('Integer', '*')
end
def test_fixnum_div
assert_equal 3, 15 / 5
assert_redefine_method('Integer', '/', 'assert_equal 5, 15 / 5')
+ assert_performance_warning('Integer', '/')
end
def test_fixnum_mod
assert_equal 1, 8 % 7
assert_redefine_method('Integer', '%', 'assert_equal 7, 8 % 7')
+ assert_performance_warning('Integer', '%')
end
def test_fixnum_lt
assert_equal true, 1 < 2
assert_redefine_method('Integer', '<', 'assert_equal 2, 1 < 2')
+ assert_performance_warning('Integer', '<')
end
def test_fixnum_le
assert_equal true, 1 <= 2
assert_redefine_method('Integer', '<=', 'assert_equal 2, 1 <= 2')
+ assert_performance_warning('Integer', '<=')
end
def test_fixnum_gt
assert_equal false, 1 > 2
assert_redefine_method('Integer', '>', 'assert_equal 2, 1 > 2')
+ assert_performance_warning('Integer', '>')
end
def test_fixnum_ge
assert_equal false, 1 >= 2
assert_redefine_method('Integer', '>=', 'assert_equal 2, 1 >= 2')
+ assert_performance_warning('Integer', '>=')
end
def test_float_plus
assert_equal 4.0, 2.0 + 2.0
assert_redefine_method('Float', '+', 'assert_equal 2.0, 2.0 + 2.0')
+ assert_performance_warning('Float', '+')
end
def test_float_minus
assert_equal 4.0, 2.0 + 2.0
- assert_redefine_method('Float', '+', 'assert_equal 2.0, 2.0 + 2.0')
+ assert_redefine_method('Float', '-', 'assert_equal 2.0, 4.0 - 2.0')
+ assert_performance_warning('Float', '-')
end
def test_float_mul
assert_equal 29.25, 4.5 * 6.5
assert_redefine_method('Float', '*', 'assert_equal 6.5, 4.5 * 6.5')
+ assert_performance_warning('Float', '*')
end
def test_float_div
assert_in_delta 0.63063063063063063, 4.2 / 6.66
assert_redefine_method('Float', '/', 'assert_equal 6.66, 4.2 / 6.66', "[Bug #9238]")
+ assert_performance_warning('Float', '/')
end
def test_float_lt
assert_equal true, 1.1 < 2.2
assert_redefine_method('Float', '<', 'assert_equal 2.2, 1.1 < 2.2')
+ assert_performance_warning('Float', '<')
end
def test_float_le
assert_equal true, 1.1 <= 2.2
assert_redefine_method('Float', '<=', 'assert_equal 2.2, 1.1 <= 2.2')
+ assert_performance_warning('Float', '<=')
end
def test_float_gt
assert_equal false, 1.1 > 2.2
assert_redefine_method('Float', '>', 'assert_equal 2.2, 1.1 > 2.2')
+ assert_performance_warning('Float', '>')
end
def test_float_ge
assert_equal false, 1.1 >= 2.2
assert_redefine_method('Float', '>=', 'assert_equal 2.2, 1.1 >= 2.2')
+ assert_performance_warning('Float', '>=')
end
def test_string_length
assert_equal 6, "string".length
assert_redefine_method('String', 'length', 'assert_nil "string".length')
+ assert_performance_warning('String', 'length')
end
def test_string_size
assert_equal 6, "string".size
assert_redefine_method('String', 'size', 'assert_nil "string".size')
+ assert_performance_warning('String', 'size')
end
def test_string_empty?
assert_equal true, "".empty?
assert_equal false, "string".empty?
assert_redefine_method('String', 'empty?', 'assert_nil "string".empty?')
+ assert_performance_warning('String', 'empty?')
end
def test_string_plus
@@ -127,39 +159,50 @@ class TestRubyOptimization < Test::Unit::TestCase
assert_equal "x", "" + "x"
assert_equal "ab", "a" + "b"
assert_redefine_method('String', '+', 'assert_equal "b", "a" + "b"')
+ assert_performance_warning('String', '+')
end
def test_string_succ
assert_equal 'b', 'a'.succ
assert_equal 'B', 'A'.succ
+ assert_performance_warning('String', 'succ')
end
def test_string_format
assert_equal '2', '%d' % 2
assert_redefine_method('String', '%', 'assert_equal 2, "%d" % 2')
+ assert_performance_warning('String', '%')
end
def test_string_freeze
assert_equal "foo", "foo".freeze
assert_equal "foo".freeze.object_id, "foo".freeze.object_id
assert_redefine_method('String', 'freeze', 'assert_nil "foo".freeze')
+ assert_performance_warning('String', 'freeze')
end
def test_string_uminus
assert_same "foo".freeze, -"foo"
assert_redefine_method('String', '-@', 'assert_nil(-"foo")')
+ assert_performance_warning('String', '-@')
end
def test_array_min
assert_equal 1, [1, 2, 4].min
assert_redefine_method('Array', 'min', 'assert_nil([1, 2, 4].min)')
assert_redefine_method('Array', 'min', 'assert_nil([1 + 0, 2, 4].min)')
+ assert_performance_warning('Array', 'min')
end
def test_array_max
assert_equal 4, [1, 2, 4].max
assert_redefine_method('Array', 'max', 'assert_nil([1, 2, 4].max)')
assert_redefine_method('Array', 'max', 'assert_nil([1 + 0, 2, 4].max)')
+ assert_performance_warning('Array', 'max')
+ end
+
+ def test_array_hash
+ assert_performance_warning('Array', 'hash')
end
def test_trace_optimized_methods
@@ -235,6 +278,8 @@ class TestRubyOptimization < Test::Unit::TestCase
assert_equal :b, (b #{m} "b").to_sym
end
end
+
+ assert_performance_warning('String', '==')
end
def test_string_ltlt
@@ -243,50 +288,59 @@ class TestRubyOptimization < Test::Unit::TestCase
assert_equal "x", "" << "x"
assert_equal "ab", "a" << "b"
assert_redefine_method('String', '<<', 'assert_equal "b", "a" << "b"')
+ assert_performance_warning('String', '<<')
end
def test_fixnum_and
assert_equal 1, 1&3
assert_redefine_method('Integer', '&', 'assert_equal 3, 1&3')
+ assert_performance_warning('Integer', '&')
end
def test_fixnum_or
assert_equal 3, 1|3
assert_redefine_method('Integer', '|', 'assert_equal 1, 3|1')
+ assert_performance_warning('Integer', '|')
end
def test_array_plus
assert_equal [1,2], [1]+[2]
assert_redefine_method('Array', '+', 'assert_equal [2], [1]+[2]')
+ assert_performance_warning('Array', '+')
end
def test_array_minus
assert_equal [2], [1,2] - [1]
assert_redefine_method('Array', '-', 'assert_equal [1], [1,2]-[1]')
+ assert_performance_warning('Array', '-')
end
def test_array_length
assert_equal 0, [].length
assert_equal 3, [1,2,3].length
assert_redefine_method('Array', 'length', 'assert_nil([].length); assert_nil([1,2,3].length)')
+ assert_performance_warning('Array', 'length')
end
def test_array_empty?
assert_equal true, [].empty?
assert_equal false, [1,2,3].empty?
assert_redefine_method('Array', 'empty?', 'assert_nil([].empty?); assert_nil([1,2,3].empty?)')
+ assert_performance_warning('Array', 'empty?')
end
def test_hash_length
assert_equal 0, {}.length
assert_equal 1, {1=>1}.length
assert_redefine_method('Hash', 'length', 'assert_nil({}.length); assert_nil({1=>1}.length)')
+ assert_performance_warning('Hash', 'length')
end
def test_hash_empty?
assert_equal true, {}.empty?
assert_equal false, {1=>1}.empty?
assert_redefine_method('Hash', 'empty?', 'assert_nil({}.empty?); assert_nil({1=>1}.empty?)')
+ assert_performance_warning('Hash', 'empty?')
end
def test_hash_aref_with
@@ -297,6 +351,7 @@ class TestRubyOptimization < Test::Unit::TestCase
h = { "foo" => 1 }
assert_equal "foo", h["foo"]
end;
+ assert_performance_warning('Hash', '[]')
end
def test_hash_aset_with
@@ -308,6 +363,7 @@ class TestRubyOptimization < Test::Unit::TestCase
assert_equal 1, h["foo"] = 1, "assignment always returns value set"
assert_nil h["foo"]
end;
+ assert_performance_warning('Hash', '[]=')
end
class MyObj
@@ -437,6 +493,31 @@ class TestRubyOptimization < Test::Unit::TestCase
message(bug12565) {disasm(:add_one_and_two)})
end
+ def test_c_func_with_sp_offset_under_tailcall
+ tailcall("#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
+ def calc_one_plus_two
+ 1 + 2.abs
+ end
+
+ def one_plus_two
+ calc_one_plus_two
+ end
+ end;
+ assert_equal(3, one_plus_two)
+ end
+
+ def test_tailcall_and_post_arg
+ tailcall(<<~RUBY)
+ def ret_const = :ok
+
+ def post_arg(_a = 1, _b) = ret_const
+ RUBY
+
+ # YJIT probably uses a fallback on the call to post_arg
+ assert_equal(:ok, post_arg(0))
+ end
+
def test_tailcall_interrupted_by_sigint
bug12576 = 'ruby-core:76327'
script = "#{<<-"begin;"}\n#{<<~'end;'}"
@@ -552,7 +633,7 @@ class TestRubyOptimization < Test::Unit::TestCase
begin;
class String
undef freeze
- def freeze
+ def freeze(&)
block_given?
end
end
@@ -614,6 +695,7 @@ class TestRubyOptimization < Test::Unit::TestCase
[ nil, true, false, 0.1, :sym, 'str', 0xffffffffffffffff ].each do |v|
k = v.class.to_s
assert_redefine_method(k, '===', "assert_equal(#{v.inspect} === 0, 0)")
+ assert_performance_warning(k, '===')
end
end
diff --git a/test/ruby/test_pack.rb b/test/ruby/test_pack.rb
index 27573ef457..1ce46e8916 100644
--- a/test/ruby/test_pack.rb
+++ b/test/ruby/test_pack.rb
@@ -1,8 +1,13 @@
# coding: US-ASCII
# frozen_string_literal: false
require 'test/unit'
+require 'rbconfig'
+require 'rbconfig/sizeof'
class TestPack < Test::Unit::TestCase
+ # Note: the size of intptr_t and uintptr_t should be equal.
+ J_SIZE = RbConfig::SIZEOF['uintptr_t']
+
def test_pack
format = "c2x5CCxsdils_l_a6";
# Need the expression in here to force ary[5] to be numeric. This avoids
@@ -93,11 +98,11 @@ class TestPack < Test::Unit::TestCase
assert_equal("\x01\x02\x03\x04", [0x01020304].pack("L"+mod))
assert_equal("\x01\x02\x03\x04\x05\x06\x07\x08", [0x0102030405060708].pack("q"+mod))
assert_equal("\x01\x02\x03\x04\x05\x06\x07\x08", [0x0102030405060708].pack("Q"+mod))
- psize = [nil].pack('p').bytesize
- if psize == 4
+ case J_SIZE
+ when 4
assert_equal("\x01\x02\x03\x04", [0x01020304].pack("j"+mod))
assert_equal("\x01\x02\x03\x04", [0x01020304].pack("J"+mod))
- elsif psize == 8
+ when 8
assert_equal("\x01\x02\x03\x04\x05\x06\x07\x08", [0x0102030405060708].pack("j"+mod))
assert_equal("\x01\x02\x03\x04\x05\x06\x07\x08", [0x0102030405060708].pack("J"+mod))
end
@@ -109,10 +114,11 @@ class TestPack < Test::Unit::TestCase
assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("I!"+mod))
assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("l!"+mod))
assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("L!"+mod))
- if psize == 4
+ case J_SIZE
+ when 4
assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("j!"+mod))
assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("J!"+mod))
- elsif psize == 8
+ when 8
assert_match(/\A\x00*\x01\x02\x03\x04\x05\x06\x07\x08\z/, [0x0102030405060708].pack("j!"+mod))
assert_match(/\A\x00*\x01\x02\x03\x04\x05\x06\x07\x08\z/, [0x0102030405060708].pack("J!"+mod))
end
@@ -141,11 +147,11 @@ class TestPack < Test::Unit::TestCase
assert_equal("\x04\x03\x02\x01", [0x01020304].pack("L"+mod))
assert_equal("\x08\x07\x06\x05\x04\x03\x02\x01", [0x0102030405060708].pack("q"+mod))
assert_equal("\x08\x07\x06\x05\x04\x03\x02\x01", [0x0102030405060708].pack("Q"+mod))
- psize = [nil].pack('p').bytesize
- if psize == 4
+ case J_SIZE
+ when 4
assert_equal("\x04\x03\x02\x01", [0x01020304].pack("j"+mod))
assert_equal("\x04\x03\x02\x01", [0x01020304].pack("J"+mod))
- elsif psize == 8
+ when 8
assert_equal("\x08\x07\x06\x05\x04\x03\x02\x01", [0x0102030405060708].pack("j"+mod))
assert_equal("\x08\x07\x06\x05\x04\x03\x02\x01", [0x0102030405060708].pack("J"+mod))
end
@@ -157,10 +163,11 @@ class TestPack < Test::Unit::TestCase
assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("I!"+mod))
assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("l!"+mod))
assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("L!"+mod))
- if psize == 4
+ case J_SIZE
+ when 4
assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("j!"+mod))
assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("J!"+mod))
- elsif psize == 8
+ when 8
assert_match(/\A\x08\x07\x06\x05\x04\x03\x02\x01\x00*\z/, [0x0102030405060708].pack("j!"+mod))
assert_match(/\A\x08\x07\x06\x05\x04\x03\x02\x01\x00*\z/, [0x0102030405060708].pack("J!"+mod))
end
@@ -196,8 +203,8 @@ class TestPack < Test::Unit::TestCase
end
def test_integer_endian_explicit
- _integer_big_endian('>')
- _integer_little_endian('<')
+ _integer_big_endian('>')
+ _integer_little_endian('<')
end
def test_pack_U
@@ -442,7 +449,6 @@ class TestPack < Test::Unit::TestCase
assert_operator(4, :<=, [1].pack("L!").bytesize)
end
- require 'rbconfig'
def test_pack_unpack_qQ
s1 = [578437695752307201, -506097522914230529].pack("q*")
s2 = [578437695752307201, 17940646550795321087].pack("Q*")
@@ -465,10 +471,8 @@ class TestPack < Test::Unit::TestCase
end if RbConfig::CONFIG['HAVE_LONG_LONG']
def test_pack_unpack_jJ
- # Note: we assume that the size of intptr_t and uintptr_t equals to the size
- # of real pointer.
- psize = [nil].pack("p").bytesize
- if psize == 4
+ case J_SIZE
+ when 4
s1 = [67305985, -50462977].pack("j*")
s2 = [67305985, 4244504319].pack("J*")
assert_equal(s1, s2)
@@ -482,7 +486,7 @@ class TestPack < Test::Unit::TestCase
assert_equal(4, [1].pack("j").bytesize)
assert_equal(4, [1].pack("J").bytesize)
- elsif psize == 8
+ when 8
s1 = [578437695752307201, -506097522914230529].pack("j*")
s2 = [578437695752307201, 17940646550795321087].pack("J*")
assert_equal(s1, s2)
diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb
index 49b25fcaf0..fe649cddb9 100644
--- a/test/ruby/test_parse.rb
+++ b/test/ruby/test_parse.rb
@@ -378,10 +378,10 @@ class TestParse < Test::Unit::TestCase
def assert_disallowed_variable(type, noname, invalid)
noname.each do |name|
- assert_syntax_error("proc{a = #{name} }", "`#{noname[0]}' without identifiers is not allowed as #{type} variable name")
+ assert_syntax_error("proc{a = #{name} }", "'#{noname[0]}' without identifiers is not allowed as #{type} variable name")
end
invalid.each do |name|
- assert_syntax_error("proc {a = #{name} }", "`#{name}' is not allowed as #{type} variable name")
+ assert_syntax_error("proc {a = #{name} }", "'#{name}' is not allowed as #{type} variable name")
end
end
@@ -453,10 +453,44 @@ class TestParse < Test::Unit::TestCase
end
def test_define_singleton_error
- assert_syntax_error("#{<<~"begin;"}\n#{<<~'end;'}", /singleton method for literals/) do
- begin;
- def ("foo").foo; end
- end;
+ msg = /singleton method for literals/
+ assert_parse_error(%q[def ("foo").foo; end], msg)
+ assert_parse_error(%q[def (1).foo; end], msg)
+ assert_parse_error(%q[def ((1;1)).foo; end], msg)
+ assert_parse_error(%q[def ((;1)).foo; end], msg)
+ assert_parse_error(%q[def ((1+1;1)).foo; end], msg)
+ assert_parse_error(%q[def ((%s();1)).foo; end], msg)
+ assert_parse_error(%q[def ((%w();1)).foo; end], msg)
+ assert_parse_error(%q[def ("#{42}").foo; end], msg)
+ assert_parse_error(%q[def (:"#{42}").foo; end], msg)
+ end
+
+ def test_flip_flop
+ all_assertions_foreach(nil,
+ ['(cond1..cond2)', true],
+ ['((cond1..cond2))', true],
+
+ # '(;;;cond1..cond2)', # don't care
+
+ '(1; cond1..cond2)',
+ '(%s(); cond1..cond2)',
+ '(%w(); cond1..cond2)',
+ '(1; (2; (3; 4; cond1..cond2)))',
+ '(1+1; cond1..cond2)',
+ ) do |code, pass|
+ code = code.sub("cond1", "n==4").sub("cond2", "n==5")
+ if pass
+ assert_equal([4,5], eval("(1..9).select {|n| true if #{code}}"))
+ else
+ assert_raise_with_message(ArgumentError, /bad value for range/, code) {
+ verbose_bak, $VERBOSE = $VERBOSE, nil # disable "warning: possibly useless use of a literal in void context"
+ begin
+ eval("[4].each {|n| true if #{code}}")
+ ensure
+ $VERBOSE = verbose_bak
+ end
+ }
+ end
end
end
@@ -464,6 +498,10 @@ class TestParse < Test::Unit::TestCase
t = Object.new
a = []
blk = proc {|x| a << x }
+
+ # Prevent an "assigned but unused variable" warning
+ _ = blk
+
def t.[](_)
yield(:aref)
nil
@@ -473,16 +511,16 @@ class TestParse < Test::Unit::TestCase
end
def t.dummy(_)
end
- eval <<-END, nil, __FILE__, __LINE__+1
+
+ assert_syntax_error("#{<<~"begin;"}\n#{<<~'end;'}", /block arg given in index/)
+ begin;
t[42, &blk] ||= 42
- END
- assert_equal([:aref, :aset], a)
- a.clear
- eval <<-END, nil, __FILE__, __LINE__+1
- t[42, &blk] ||= t.dummy 42 # command_asgn test
- END
- assert_equal([:aref, :aset], a)
- blk
+ end;
+
+ assert_syntax_error("#{<<~"begin;"}\n#{<<~'end;'}", /block arg given in index/)
+ begin;
+ t[42, &blk] ||= t.dummy 42 # command_asgn test
+ end;
end
def test_backquote
@@ -493,7 +531,7 @@ class TestParse < Test::Unit::TestCase
def t.`(x); "foo" + x + "bar"; end
END
end
- a = b = nil
+ a = b = c = nil
assert_nothing_raised do
eval <<-END, nil, __FILE__, __LINE__+1
a = t.` "zzz"
@@ -501,10 +539,12 @@ class TestParse < Test::Unit::TestCase
END
t.instance_eval <<-END, __FILE__, __LINE__+1
b = `zzz`
+ c = %x(ccc)
END
end
assert_equal("foozzzbar", a)
assert_equal("foozzzbar", b)
+ assert_equal("foocccbar", c)
end
def test_carrige_return
@@ -578,6 +618,10 @@ class TestParse < Test::Unit::TestCase
assert_equal(' ^~~~~'"\n", e.message.lines.last)
e = assert_syntax_error('"\M-\U0000"', 'Invalid escape character syntax')
assert_equal(' ^~~~~'"\n", e.message.lines.last)
+
+ e = assert_syntax_error(%["\\C-\u3042"], 'Invalid escape character syntax')
+ assert_match(/^\s \^(?# \\ ) ~(?# C ) ~(?# - ) ~+(?# U+3042 )$/x, e.message.lines.last)
+ assert_not_include(e.message, "invalid multibyte char")
end
def test_question
@@ -608,6 +652,8 @@ class TestParse < Test::Unit::TestCase
assert_syntax_error("?\\M-\x01", 'Invalid escape character syntax')
assert_syntax_error("?\\M-\\C-\x01", 'Invalid escape character syntax')
assert_syntax_error("?\\C-\\M-\x01", 'Invalid escape character syntax')
+
+ assert_equal("\xff", eval("# encoding: ascii-8bit\n""?\\\xFF"))
end
def test_percent
@@ -641,6 +687,7 @@ class TestParse < Test::Unit::TestCase
assert_syntax_error(':@@1', /is not allowed/)
assert_syntax_error(':@', /is not allowed/)
assert_syntax_error(':@1', /is not allowed/)
+ assert_syntax_error(':$01234', /is not allowed/)
end
def test_parse_string
@@ -726,6 +773,54 @@ x = __ENCODING__
END
end
assert_equal(__ENCODING__, x)
+
+ assert_raise(ArgumentError) do
+ EnvUtil.with_default_external(Encoding::US_ASCII) {eval <<-END, nil, __FILE__, __LINE__+1}
+# coding = external
+x = __ENCODING__
+ END
+ end
+
+ assert_raise(ArgumentError) do
+ EnvUtil.with_default_internal(Encoding::US_ASCII) {eval <<-END, nil, __FILE__, __LINE__+1}
+# coding = internal
+x = __ENCODING__
+ END
+ end
+
+ assert_raise(ArgumentError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+# coding = filesystem
+x = __ENCODING__
+ END
+ end
+
+ assert_raise(ArgumentError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+# coding = locale
+x = __ENCODING__
+ END
+ end
+
+ e = assert_raise(ArgumentError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+# coding: foo
+ END
+ end
+
+ message = e.message.gsub(/\033\[.*?m/, "")
+ assert_include(message, "# coding: foo\n")
+ assert_include(message, " ^")
+
+ e = assert_raise(ArgumentError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+# coding = foo
+ END
+ end
+
+ message = e.message.gsub(/\033\[.*?m/, "")
+ assert_include(message, "# coding = foo\n")
+ assert_include(message, " ^")
end
def test_utf8_bom
@@ -769,7 +864,7 @@ x = __ENCODING__
def test_float
assert_predicate(assert_warning(/out of range/) {eval("1e10000")}, :infinite?)
- assert_syntax_error('1_E', /trailing `_'/)
+ assert_syntax_error('1_E', /trailing '_'/)
assert_syntax_error('1E1E1', /unexpected constant/)
end
@@ -797,7 +892,7 @@ x = __ENCODING__
def test_invalid_char
bug10117 = '[ruby-core:64243] [Bug #10117]'
- invalid_char = /Invalid char `\\x01'/
+ invalid_char = /Invalid char '\\x01'/
x = 1
assert_in_out_err(%W"-e \x01x", "", [], invalid_char, bug10117)
assert_syntax_error("\x01x", invalid_char, bug10117)
@@ -831,24 +926,9 @@ x = __ENCODING__
assert_syntax_error("$& = 1", /Can't set variable/)
end
- def test_arg_concat
- o = Object.new
- class << o; self; end.instance_eval do
- define_method(:[]=) {|*r, &b| b.call(r) }
- end
- r = nil
- assert_nothing_raised do
- eval <<-END, nil, __FILE__, __LINE__+1
- o[&proc{|x| r = x }] = 1
- END
- end
- assert_equal([1], r)
- end
-
def test_void_expr_stmts_value
x = 1
useless_use = /useless use/
- unused = /unused/
assert_nil assert_warning(useless_use) {eval("x; nil")}
assert_nil assert_warning(useless_use) {eval("1+1; nil")}
assert_nil assert_warning('') {eval("1.+(1); nil")}
@@ -856,10 +936,10 @@ x = __ENCODING__
assert_nil assert_warning(useless_use) {eval("::TestParse; nil")}
assert_nil assert_warning(useless_use) {eval("x..x; nil")}
assert_nil assert_warning(useless_use) {eval("x...x; nil")}
- assert_nil assert_warning(unused) {eval("self; nil")}
- assert_nil assert_warning(unused) {eval("nil; nil")}
- assert_nil assert_warning(unused) {eval("true; nil")}
- assert_nil assert_warning(unused) {eval("false; nil")}
+ assert_nil assert_warning(useless_use) {eval("self; nil")}
+ assert_nil assert_warning(useless_use) {eval("nil; nil")}
+ assert_nil assert_warning(useless_use) {eval("true; nil")}
+ assert_nil assert_warning(useless_use) {eval("false; nil")}
assert_nil assert_warning(useless_use) {eval("defined?(1); nil")}
assert_equal 1, x
@@ -867,13 +947,15 @@ x = __ENCODING__
end
def test_assign_in_conditional
- assert_warning(/`= literal' in conditional/) do
+ # multiple assignment
+ assert_warning(/'= literal' in conditional/) do
eval <<-END, nil, __FILE__, __LINE__+1
(x, y = 1, 2) ? 1 : 2
END
end
- assert_warning(/`= literal' in conditional/) do
+ # instance variable assignment
+ assert_warning(/'= literal' in conditional/) do
eval <<-END, nil, __FILE__, __LINE__+1
if @x = true
1
@@ -882,6 +964,71 @@ x = __ENCODING__
end
END
end
+
+ # local variable assignment
+ assert_warning(/'= literal' in conditional/) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ def m
+ if x = true
+ 1
+ else
+ 2
+ end
+ end
+ END
+ end
+
+ # global variable assignment
+ assert_separately([], <<-RUBY)
+ assert_warning(/'= literal' in conditional/) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ if $x = true
+ 1
+ else
+ 2
+ end
+ END
+ end
+ RUBY
+
+ # dynamic variable assignment
+ assert_warning(/'= literal' in conditional/) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ y = 1
+
+ 1.times do
+ if y = true
+ 1
+ else
+ 2
+ end
+ end
+ END
+ end
+
+ # class variable assignment
+ assert_warning(/'= literal' in conditional/) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ c = Class.new
+ class << c
+ if @@a = 1
+ end
+ end
+ END
+ end
+
+ # constant declaration
+ assert_separately([], <<-RUBY)
+ assert_warning(/'= literal' in conditional/) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ if Const = true
+ 1
+ else
+ 2
+ end
+ END
+ end
+ RUBY
end
def test_literal_in_conditional
@@ -958,6 +1105,20 @@ x = __ENCODING__
assert_warning('') {o.instance_eval("def marg2((a)); nil; end")}
end
+ def test_parsing_begin_statement_inside_method_definition
+ assert_equal :bug_20234, eval("def (begin;end).bug_20234; end")
+ NilClass.remove_method(:bug_20234)
+ assert_equal :bug_20234, eval("def (begin;rescue;end).bug_20234; end")
+ NilClass.remove_method(:bug_20234)
+ assert_equal :bug_20234, eval("def (begin;ensure;end).bug_20234; end")
+ NilClass.remove_method(:bug_20234)
+ assert_equal :bug_20234, eval("def (begin;rescue;else;end).bug_20234; end")
+ NilClass.remove_method(:bug_20234)
+
+ assert_raise(SyntaxError) { eval("def (begin;else;end).bug_20234; end") }
+ assert_raise(SyntaxError) { eval("def (begin;ensure;else;end).bug_20234; end") }
+ end
+
def test_named_capture_conflict
a = 1
assert_warning('') {eval("a = 1; /(?<a>)/ =~ ''")}
@@ -965,6 +1126,30 @@ x = __ENCODING__
assert_warning('') {eval("#{a} = 1; /(?<#{a}>)/ =~ ''")}
end
+ def test_named_capture_in_block
+ all_assertions_foreach(nil,
+ '(/(?<a>.*)/)',
+ '(;/(?<a>.*)/)',
+ '(%s();/(?<a>.*)/)',
+ '(%w();/(?<a>.*)/)',
+ '(1; (2; 3; (4; /(?<a>.*)/)))',
+ '(1+1; /(?<a>.*)/)',
+ '/#{""}(?<a>.*)/',
+ ) do |code, pass|
+ token = Random.bytes(4).unpack1("H*")
+ if pass
+ assert_equal(token, eval("#{code} =~ #{token.dump}; a"))
+ else
+ verbose_bak, $VERBOSE = $VERBOSE, nil # disable "warning: possibly useless use of a literal in void context"
+ begin
+ assert_nil(eval("#{code} =~ #{token.dump}; defined?(a)"), code)
+ ensure
+ $VERBOSE = verbose_bak
+ end
+ end
+ end
+ end
+
def test_rescue_in_command_assignment
bug = '[ruby-core:75621] [Bug #12402]'
all_assertions(bug) do |a|
@@ -1052,6 +1237,22 @@ x = __ENCODING__
assert_syntax_error(" 0b\n", /\^/)
end
+ def test_unclosed_unicode_escape_at_eol_bug_19750
+ assert_separately([], "#{<<-"begin;"}\n#{<<~'end;'}")
+ begin;
+ assert_syntax_error("/\\u", /too short escape sequence/)
+ assert_syntax_error("/\\u{", /unterminated regexp meets end of file/)
+ assert_syntax_error("/\\u{\\n", /invalid Unicode list/)
+ assert_syntax_error("/a#\\u{\\n/", /invalid Unicode list/)
+ re = eval("/a#\\u{\n$/x")
+ assert_match(re, 'a')
+ assert_not_match(re, 'a#')
+ re = eval("/a#\\u\n$/x")
+ assert_match(re, 'a')
+ assert_not_match(re, 'a#')
+ end;
+ end
+
def test_error_def_in_argument
assert_separately([], "#{<<-"begin;"}\n#{<<~"end;"}")
begin;
@@ -1095,31 +1296,40 @@ x = __ENCODING__
end;
end
- def test_heredoc_interpolation
- var = 1
+ def test_heredoc_interpolation
+ var = 1
- v1 = <<~HEREDOC
- something
- #{"/#{var}"}
- HEREDOC
+ v1 = <<~HEREDOC
+ something
+ #{"/#{var}"}
+ HEREDOC
- v2 = <<~HEREDOC
- something
- #{_other = "/#{var}"}
- HEREDOC
+ v2 = <<~HEREDOC
+ something
+ #{_other = "/#{var}"}
+ HEREDOC
- v3 = <<~HEREDOC
- something
- #{("/#{var}")}
- HEREDOC
+ v3 = <<~HEREDOC
+ something
+ #{("/#{var}")}
+ HEREDOC
- assert_equal "something\n/1\n", v1
- assert_equal "something\n/1\n", v2
- assert_equal "something\n/1\n", v3
- assert_equal v1, v2
- assert_equal v2, v3
- assert_equal v1, v3
- end
+ assert_equal "something\n/1\n", v1
+ assert_equal "something\n/1\n", v2
+ assert_equal "something\n/1\n", v3
+ assert_equal v1, v2
+ assert_equal v2, v3
+ assert_equal v1, v3
+ end
+
+ def test_heredoc_unterminated_interpolation
+ code = <<~'HEREDOC'
+ <<A+1
+ #{
+ HEREDOC
+
+ assert_syntax_error(code, /can't find string "A"/)
+ end
def test_unexpected_token_error
assert_syntax_error('"x"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', /unexpected/)
@@ -1129,6 +1339,8 @@ x = __ENCODING__
assert_syntax_error('0000xyz', /^ \^~~\Z/)
assert_syntax_error('1.2i1.1', /^ \^~~\Z/)
assert_syntax_error('1.2.3', /^ \^~\Z/)
+ assert_syntax_error('1.', /unexpected end-of-input/)
+ assert_syntax_error('1e', /expecting end-of-input/)
end
def test_truncated_source_line
@@ -1248,10 +1460,23 @@ x = __ENCODING__
def test_void_value_in_rhs
w = "void value expression"
- ["x = return 1", "x = return, 1", "x = 1, return", "x, y = return"].each do |code|
+ [
+ "x = return 1", "x = return, 1", "x = 1, return", "x, y = return",
+ "x = begin return ensure end",
+ "x = begin ensure return end",
+ "x = begin return ensure return end",
+ "x = begin return; rescue; return end",
+ "x = begin return; rescue; return; else return end",
+ ].each do |code|
ex = assert_syntax_error(code, w)
assert_equal(1, ex.message.scan(w).size, ->{"same #{w.inspect} warning should be just once\n#{w.message}"})
end
+ [
+ "x = begin return; rescue; end",
+ "x = begin return; rescue; return; else end",
+ ].each do |code|
+ assert_valid_syntax(code)
+ end
end
def eval_separately(code)
@@ -1315,6 +1540,24 @@ x = __ENCODING__
assert_not_ractor_shareable(obj)
assert_equal obj, a
assert !obj.equal?(a)
+
+ bug_20339 = '[ruby-core:117186] [Bug #20339]'
+ bug_20341 = '[ruby-core:117197] [Bug #20341]'
+ a, b = eval_separately(<<~'end;')
+ # shareable_constant_value: literal
+ foo = 1
+ bar = 2
+ A = { foo => bar }
+ B = [foo, bar]
+ [A, B]
+ end;
+
+ assert_ractor_shareable(a)
+ assert_ractor_shareable(b)
+ assert_equal([1], a.keys, bug_20339)
+ assert_equal([2], a.values, bug_20339)
+ assert_equal(1, b[0], bug_20341)
+ assert_equal(2, b[1], bug_20341)
end
def test_shareable_constant_value_nested
@@ -1336,12 +1579,71 @@ x = __ENCODING__
end
def test_shareable_constant_value_unshareable_literal
- assert_raise_separately(Ractor::IsolationError, /unshareable/,
+ assert_raise_separately(Ractor::IsolationError, /unshareable object to C/,
"#{<<~"begin;"}\n#{<<~'end;'}")
begin;
# shareable_constant_value: literal
C = ["Not " + "shareable"]
end;
+
+ assert_raise_separately(Ractor::IsolationError, /unshareable object to B::C/,
+ "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ # shareable_constant_value: literal
+ B = Class.new
+ B::C = ["Not " + "shareable"]
+ end;
+
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ assert_raise_with_message(Ractor::IsolationError, /unshareable object to ::C/) do
+ # shareable_constant_value: literal
+ ::C = ["Not " + "shareable"]
+ end
+ end;
+
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ assert_raise_with_message(Ractor::IsolationError, /unshareable object to ::B::C/) do
+ # shareable_constant_value: literal
+ ::B = Class.new
+ ::B::C = ["Not " + "shareable"]
+ end
+ end;
+
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ assert_raise_with_message(Ractor::IsolationError, /unshareable object to ::C/) do
+ # shareable_constant_value: literal
+ ::C ||= ["Not " + "shareable"]
+ end
+ end;
+
+ assert_raise_separately(Ractor::IsolationError, /unshareable object to B::C/,
+ "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ # shareable_constant_value: literal
+ B = Class.new
+ B::C ||= ["Not " + "shareable"]
+ end;
+
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ assert_raise_with_message(Ractor::IsolationError, /unshareable object to ::B::C/) do
+ # shareable_constant_value: literal
+ ::B = Class.new
+ ::B::C ||= ["Not " + "shareable"]
+ end
+ end;
+
+ assert_raise_separately(Ractor::IsolationError, /unshareable object to ...::C/,
+ "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ # shareable_constant_value: literal
+ B = Class.new
+ def self.expr; B; end
+ expr::C ||= ["Not " + "shareable"]
+ end;
end
def test_shareable_constant_value_nonliteral
@@ -1416,9 +1718,29 @@ x = __ENCODING__
assert_equal(expected, obj.arg)
end
+ def test_ungettable_gvar
+ assert_syntax_error('$01234', /not allowed/)
+ assert_syntax_error('"#$01234"', /not allowed/)
+ end
+
=begin
def test_past_scope_variable
assert_warning(/past scope/) {catch {|tag| eval("BEGIN{throw tag}; tap {a = 1}; a")}}
end
=end
+
+ def assert_parse(code)
+ assert_kind_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.parse(code))
+ end
+
+ def assert_parse_error(code, message)
+ assert_raise_with_message(SyntaxError, message) do
+ $VERBOSE, verbose_bak = nil, $VERBOSE
+ begin
+ RubyVM::AbstractSyntaxTree.parse(code)
+ ensure
+ $VERBOSE = verbose_bak
+ end
+ end
+ end
end
diff --git a/test/ruby/test_pattern_matching.rb b/test/ruby/test_pattern_matching.rb
index 5e27593f25..db6ad06b82 100644
--- a/test/ruby/test_pattern_matching.rb
+++ b/test/ruby/test_pattern_matching.rb
@@ -109,16 +109,12 @@ class TestPatternMatching < Test::Unit::TestCase
end
assert_block do
- # suppress "warning: Pattern matching is experimental, and the behavior may change in future versions of Ruby!"
- experimental, Warning[:experimental] = Warning[:experimental], false
eval(%q{
case true
in a
a
end
})
- ensure
- Warning[:experimental] = experimental
end
assert_block do
@@ -358,6 +354,14 @@ END
end
assert_block do
+ a = "abc"
+ case 'abc'
+ in /#{a}/o
+ true
+ end
+ end
+
+ assert_block do
case 0
in ->(i) { i == 0 }
true
@@ -1165,7 +1169,7 @@ END
end
end
- bug18890 = assert_warning(/(?:.*:[47]: warning: unused literal ignored\n){2}/) do
+ bug18890 = assert_warning(/(?:.*:[47]: warning: possibly useless use of a literal in void context\n){2}/) do
eval("#{<<~';;;'}")
proc do |i|
case i
diff --git a/test/ruby/test_proc.rb b/test/ruby/test_proc.rb
index 05c6e03aac..2f91da8aa8 100644
--- a/test/ruby/test_proc.rb
+++ b/test/ruby/test_proc.rb
@@ -289,7 +289,6 @@ class TestProc < Test::Unit::TestCase
assert_equal(false, l.lambda?)
assert_equal(false, l.curry.lambda?, '[ruby-core:24127]')
assert_equal(false, proc(&l).lambda?)
- assert_equal(false, assert_deprecated_warning {lambda(&l)}.lambda?)
assert_equal(false, Proc.new(&l).lambda?)
l = lambda {}
assert_equal(true, l.lambda?)
@@ -299,47 +298,21 @@ class TestProc < Test::Unit::TestCase
assert_equal(true, Proc.new(&l).lambda?)
end
- def self.helper_test_warn_lamda_with_passed_block &b
+ def helper_test_warn_lambda_with_passed_block &b
lambda(&b)
end
- def self.def_lambda_warning name, warn
- define_method(name, proc do
- prev = Warning[:deprecated]
- assert_warn warn do
- Warning[:deprecated] = true
- yield
- end
- ensure
- Warning[:deprecated] = prev
- end)
- end
-
- def_lambda_warning 'test_lambda_warning_normal', '' do
- lambda{}
- end
-
- def_lambda_warning 'test_lambda_warning_pass_lambda', '' do
- b = lambda{}
- lambda(&b)
- end
-
- def_lambda_warning 'test_lambda_warning_pass_symbol_proc', '' do
- lambda(&:to_s)
- end
-
- def_lambda_warning 'test_lambda_warning_pass_proc', /deprecated/ do
- b = proc{}
- lambda(&b)
- end
-
- def_lambda_warning 'test_lambda_warning_pass_block', /deprecated/ do
- helper_test_warn_lamda_with_passed_block{}
+ def test_lambda_warning_pass_proc
+ assert_raise(ArgumentError) do
+ b = proc{}
+ lambda(&b)
+ end
end
- def_lambda_warning 'test_lambda_warning_pass_block_symbol_proc', '' do
- # Symbol#to_proc returns lambda
- helper_test_warn_lamda_with_passed_block(&:to_s)
+ def test_lambda_warning_pass_block
+ assert_raise(ArgumentError) do
+ helper_test_warn_lambda_with_passed_block{}
+ end
end
def test_curry_ski_fib
@@ -415,6 +388,15 @@ class TestProc < Test::Unit::TestCase
def test_dup_subclass
c1 = Class.new(Proc)
assert_equal c1, c1.new{}.dup.class, '[Bug #17545]'
+ c1 = Class.new(Proc) {def initialize_dup(*) throw :initialize_dup; end}
+ assert_throw(:initialize_dup) {c1.new{}.dup}
+ end
+
+ def test_clone_subclass
+ c1 = Class.new(Proc)
+ assert_equal c1, c1.new{}.clone.class, '[Bug #17545]'
+ c1 = Class.new(Proc) {def initialize_clone(*) throw :initialize_clone; end}
+ assert_throw(:initialize_clone) {c1.new{}.clone}
end
def test_binding
@@ -1321,6 +1303,32 @@ class TestProc < Test::Unit::TestCase
assert_empty(pr.parameters.map{|_,n|n}.compact)
end
+ def test_proc_autosplat_with_multiple_args_with_ruby2_keywords_splat_bug_19759
+ def self.yielder_ab(splat)
+ yield([:a, :b], *splat)
+ end
+
+ res = yielder_ab([[:aa, :bb], Hash.ruby2_keywords_hash({k: :k})]) do |a, b, k:|
+ [a, b, k]
+ end
+ assert_equal([[:a, :b], [:aa, :bb], :k], res)
+
+ def self.yielder(splat)
+ yield(*splat)
+ end
+ res = yielder([ [:a, :b] ]){|a, b, **| [a, b]}
+ assert_equal([:a, :b], res)
+
+ res = yielder([ [:a, :b], Hash.ruby2_keywords_hash({}) ]){|a, b, **| [a, b]}
+ assert_equal([[:a, :b], nil], res)
+
+ res = yielder([ [:a, :b], Hash.ruby2_keywords_hash({c: 1}) ]){|a, b, **| [a, b]}
+ assert_equal([[:a, :b], nil], res)
+
+ res = yielder([ [:a, :b], Hash.ruby2_keywords_hash({}) ]){|a, b, **nil| [a, b]}
+ assert_equal([[:a, :b], nil], res)
+ end
+
def test_parameters_lambda
assert_equal([], proc {}.parameters(lambda: true))
assert_equal([], proc {||}.parameters(lambda: true))
@@ -1838,4 +1846,3 @@ class TestProcKeywords < Test::Unit::TestCase
assert_raise(ArgumentError) { (f >> g).call(**{})[:a] }
end
end
-
diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb
index 635ffef4ee..7ef184d639 100644
--- a/test/ruby/test_process.rb
+++ b/test/ruby/test_process.rb
@@ -23,12 +23,6 @@ class TestProcess < Test::Unit::TestCase
return /mswin|mingw|bccwin/ =~ RUBY_PLATFORM
end
- def write_file(filename, content)
- File.open(filename, "w") {|f|
- f << content
- }
- end
-
def with_tmpchdir
Dir.mktmpdir {|d|
d = File.realpath(d)
@@ -39,7 +33,7 @@ class TestProcess < Test::Unit::TestCase
end
def run_in_child(str) # should be called in a temporary directory
- write_file("test-script", str)
+ File.write("test-script", str)
Process.wait spawn(RUBY, "test-script")
$?
end
@@ -65,7 +59,7 @@ class TestProcess < Test::Unit::TestCase
def test_rlimit_nofile
return unless rlimit_exist?
with_tmpchdir {
- write_file 's', <<-"End"
+ File.write 's', <<-"End"
# Too small RLIMIT_NOFILE, such as zero, causes problems.
# [OpenBSD] Setting to zero freezes this test.
# [GNU/Linux] EINVAL on poll(). EINVAL on ruby's internal poll() ruby with "[ASYNC BUG] thread_timer: select".
@@ -205,58 +199,67 @@ class TestProcess < Test::Unit::TestCase
max = Process.getrlimit(:CORE).last
+ # When running under ASAN, we need to set disable_coredump=0 for this test; by default
+ # the ASAN runtime library sets RLIMIT_CORE to 0, "to avoid dumping a 16T+ core file", and
+ # that inteferes with this test.
+ asan_options = ENV['ASAN_OPTIONS'] || ''
+ asan_options << ':' unless asan_options.empty?
+ env = {
+ 'ASAN_OPTIONS' => "#{asan_options}disable_coredump=0"
+ }
+
n = max
- IO.popen([RUBY, "-e",
+ IO.popen([env, RUBY, "-e",
"puts Process.getrlimit(:CORE)", :rlimit_core=>n]) {|io|
assert_equal("#{n}\n#{n}\n", io.read)
}
n = 0
- IO.popen([RUBY, "-e",
+ IO.popen([env, RUBY, "-e",
"puts Process.getrlimit(:CORE)", :rlimit_core=>n]) {|io|
assert_equal("#{n}\n#{n}\n", io.read)
}
n = max
- IO.popen([RUBY, "-e",
+ IO.popen([env, RUBY, "-e",
"puts Process.getrlimit(:CORE)", :rlimit_core=>[n]]) {|io|
assert_equal("#{n}\n#{n}\n", io.read)
}
m, n = 0, max
- IO.popen([RUBY, "-e",
+ IO.popen([env, RUBY, "-e",
"puts Process.getrlimit(:CORE)", :rlimit_core=>[m,n]]) {|io|
assert_equal("#{m}\n#{n}\n", io.read)
}
m, n = 0, 0
- IO.popen([RUBY, "-e",
+ IO.popen([env, RUBY, "-e",
"puts Process.getrlimit(:CORE)", :rlimit_core=>[m,n]]) {|io|
assert_equal("#{m}\n#{n}\n", io.read)
}
n = max
- IO.popen([RUBY, "-e",
+ IO.popen([env, RUBY, "-e",
"puts Process.getrlimit(:CORE), Process.getrlimit(:CPU)",
:rlimit_core=>n, :rlimit_cpu=>3600]) {|io|
assert_equal("#{n}\n#{n}\n""3600\n3600\n", io.read)
}
assert_raise(ArgumentError) do
- system(RUBY, '-e', 'exit', 'rlimit_bogus'.to_sym => 123)
+ system(env, RUBY, '-e', 'exit', 'rlimit_bogus'.to_sym => 123)
end
- assert_separately([],"#{<<~"begin;"}\n#{<<~'end;'}", 'rlimit_cpu'.to_sym => 3600)
+ assert_separately([env],"#{<<~"begin;"}\n#{<<~'end;'}", 'rlimit_cpu'.to_sym => 3600)
BUG = "[ruby-core:82033] [Bug #13744]"
begin;
assert_equal([3600,3600], Process.getrlimit(:CPU), BUG)
end;
assert_raise_with_message(ArgumentError, /bogus/) do
- system(RUBY, '-e', 'exit', :rlimit_bogus => 123)
+ system(env, RUBY, '-e', 'exit', :rlimit_bogus => 123)
end
assert_raise_with_message(ArgumentError, /rlimit_cpu/) {
- system(RUBY, '-e', 'exit', "rlimit_cpu\0".to_sym => 3600)
+ system(env, RUBY, '-e', 'exit', "rlimit_cpu\0".to_sym => 3600)
}
end
@@ -361,7 +364,7 @@ class TestProcess < Test::Unit::TestCase
def test_execopt_env_path
bug8004 = '[ruby-core:53103] [Bug #8004]'
Dir.mktmpdir do |d|
- open("#{d}/tmp_script.cmd", "w") {|f| f.puts ": ;"; f.chmod(0755)}
+ File.write("#{d}/tmp_script.cmd", ": ;\n", perm: 0o755)
assert_not_nil(pid = Process.spawn({"PATH" => d}, "tmp_script.cmd"), bug8004)
wpid, st = Process.waitpid2(pid)
assert_equal([pid, true], [wpid, st.success?], bug8004)
@@ -399,7 +402,7 @@ class TestProcess < Test::Unit::TestCase
def test_execopts_env_popen_string
with_tmpchdir do |d|
- open('test-script', 'w') do |f|
+ File.open('test-script', 'w') do |f|
ENVCOMMAND.each_with_index do |cmd, i|
next if i.zero? or cmd == "-e"
f.puts cmd
@@ -411,16 +414,14 @@ class TestProcess < Test::Unit::TestCase
def test_execopts_preserve_env_on_exec_failure
with_tmpchdir {|d|
- write_file 's', <<-"End"
+ File.write 's', <<-"End"
ENV["mgg"] = nil
prog = "./nonexistent"
begin
Process.exec({"mgg" => "mggoo"}, [prog, prog])
rescue Errno::ENOENT
end
- open('out', 'w') {|f|
- f.print ENV["mgg"].inspect
- }
+ File.write('out', ENV["mgg"].inspect)
End
system(RUBY, 's')
assert_equal(nil.inspect, File.read('out'),
@@ -430,9 +431,7 @@ class TestProcess < Test::Unit::TestCase
def test_execopts_env_single_word
with_tmpchdir {|d|
- open("test_execopts_env_single_word.rb", "w") {|f|
- f.puts "print ENV['hgga']"
- }
+ File.write("test_execopts_env_single_word.rb", "print ENV['hgga']\n")
system({"hgga"=>"ugu"}, RUBY,
:in => 'test_execopts_env_single_word.rb',
:out => 'test_execopts_env_single_word.out')
@@ -554,7 +553,7 @@ class TestProcess < Test::Unit::TestCase
assert_equal("a", File.read("out").chomp)
if windows?
# currently telling to child the file modes is not supported.
- open("out", "a") {|f| f.write "0\n"}
+ File.write("out", "0\n", mode: "a")
else
Process.wait Process.spawn(*ECHO["0"], STDOUT=>["out", File::WRONLY|File::CREAT|File::APPEND, 0644])
assert_equal("a\n0\n", File.read("out"))
@@ -665,6 +664,7 @@ class TestProcess < Test::Unit::TestCase
end unless windows? # does not support fifo
def test_execopts_redirect_open_fifo_interrupt_raise
+ pid = nil
with_tmpchdir {|d|
begin
File.mkfifo("fifo")
@@ -682,15 +682,21 @@ class TestProcess < Test::Unit::TestCase
puts "ok"
end
EOS
+ pid = io.pid
assert_equal("start\n", io.gets)
sleep 0.5
Process.kill(:USR1, io.pid)
assert_equal("ok\n", io.read)
}
+ assert_equal(pid, $?.pid)
+ assert_predicate($?, :success?)
}
+ ensure
+ assert_raise(Errno::ESRCH) {Process.kill(:KILL, pid)} if pid
end unless windows? # does not support fifo
def test_execopts_redirect_open_fifo_interrupt_print
+ pid = nil
with_tmpchdir {|d|
begin
File.mkfifo("fifo")
@@ -703,14 +709,25 @@ class TestProcess < Test::Unit::TestCase
puts "start"
system("cat", :in => "fifo")
EOS
+ pid = io.pid
assert_equal("start\n", io.gets)
sleep 0.2 # wait for the child to stop at opening "fifo"
Process.kill(:USR1, io.pid)
assert_equal("trap\n", io.readpartial(8))
+ sleep 0.2 # wait for the child to return to opening "fifo".
+ # On arm64-darwin22, often deadlocks while the child is
+ # opening "fifo". Not sure to where "ok" line being written
+ # at the next has gone.
File.write("fifo", "ok\n")
assert_equal("ok\n", io.read)
}
+ assert_equal(pid, $?.pid)
+ assert_predicate($?, :success?)
}
+ ensure
+ if pid
+ assert_raise(Errno::ESRCH) {Process.kill(:KILL, pid)}
+ end
end unless windows? # does not support fifo
def test_execopts_redirect_pipe
@@ -858,7 +875,7 @@ class TestProcess < Test::Unit::TestCase
def test_execopts_exec
with_tmpchdir {|d|
- write_file("s", 'exec "echo aaa", STDOUT=>"foo"')
+ File.write("s", 'exec "echo aaa", STDOUT=>"foo"')
pid = spawn RUBY, 's'
Process.wait pid
assert_equal("aaa\n", File.read("foo"))
@@ -932,7 +949,7 @@ class TestProcess < Test::Unit::TestCase
}
with_pipe {|r, w|
with_tmpchdir {|d|
- write_file("s", <<-"End")
+ File.write("s", <<-"End")
exec(#{RUBY.dump}, '-e',
'IO.new(ARGV[0].to_i, "w").puts("bu") rescue nil',
#{w.fileno.to_s.dump}, :close_others=>false)
@@ -986,7 +1003,7 @@ class TestProcess < Test::Unit::TestCase
assert_equal("bi\n", r.read)
}
with_pipe {|r, w|
- write_file("s", <<-"End")
+ File.write("s", <<-"End")
exec(#{RUBY.dump}, '-e',
'STDERR.reopen("err", "w"); IO.new(ARGV[0].to_i, "w").puts("mu")',
#{w.fileno.to_s.dump},
@@ -1112,7 +1129,7 @@ class TestProcess < Test::Unit::TestCase
def test_exec_noshell
with_tmpchdir {|d|
- write_file("s", <<-"End")
+ File.write("s", <<-"End")
str = "echo non existing command name which contains spaces"
STDERR.reopen(STDOUT)
begin
@@ -1128,7 +1145,7 @@ class TestProcess < Test::Unit::TestCase
def test_system_wordsplit
with_tmpchdir {|d|
- write_file("script", <<-'End')
+ File.write("script", <<-'End')
File.open("result", "w") {|t| t << "haha pid=#{$$} ppid=#{Process.ppid}" }
exit 5
End
@@ -1144,7 +1161,7 @@ class TestProcess < Test::Unit::TestCase
def test_spawn_wordsplit
with_tmpchdir {|d|
- write_file("script", <<-'End')
+ File.write("script", <<-'End')
File.open("result", "w") {|t| t << "hihi pid=#{$$} ppid=#{Process.ppid}" }
exit 6
End
@@ -1161,7 +1178,7 @@ class TestProcess < Test::Unit::TestCase
def test_popen_wordsplit
with_tmpchdir {|d|
- write_file("script", <<-'End')
+ File.write("script", <<-'End')
print "fufu pid=#{$$} ppid=#{Process.ppid}"
exit 7
End
@@ -1180,7 +1197,7 @@ class TestProcess < Test::Unit::TestCase
def test_popen_wordsplit_beginning_and_trailing_spaces
with_tmpchdir {|d|
- write_file("script", <<-'End')
+ File.write("script", <<-'End')
print "fufumm pid=#{$$} ppid=#{Process.ppid}"
exit 7
End
@@ -1199,7 +1216,7 @@ class TestProcess < Test::Unit::TestCase
def test_exec_wordsplit
with_tmpchdir {|d|
- write_file("script", <<-'End')
+ File.write("script", <<-'End')
File.open("result", "w") {|t|
if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
t << "hehe ppid=#{Process.ppid}"
@@ -1209,7 +1226,7 @@ class TestProcess < Test::Unit::TestCase
}
exit 6
End
- write_file("s", <<-"End")
+ File.write("s", <<-"End")
ruby = #{RUBY.dump}
exec "\#{ruby} script"
End
@@ -1230,11 +1247,11 @@ class TestProcess < Test::Unit::TestCase
def test_system_shell
with_tmpchdir {|d|
- write_file("script1", <<-'End')
+ File.write("script1", <<-'End')
File.open("result1", "w") {|t| t << "taka pid=#{$$} ppid=#{Process.ppid}" }
exit 7
End
- write_file("script2", <<-'End')
+ File.write("script2", <<-'End')
File.open("result2", "w") {|t| t << "taki pid=#{$$} ppid=#{Process.ppid}" }
exit 8
End
@@ -1250,7 +1267,7 @@ class TestProcess < Test::Unit::TestCase
if windows?
Dir.mkdir(path = "path with space")
- write_file(bat = path + "/bat test.bat", "@echo %1>out")
+ File.write(bat = path + "/bat test.bat", "@echo %1>out")
system(bat, "foo 'bar'")
assert_equal(%["foo 'bar'"\n], File.read("out"), '[ruby-core:22960]')
system(%[#{bat.dump} "foo 'bar'"])
@@ -1261,11 +1278,11 @@ class TestProcess < Test::Unit::TestCase
def test_spawn_shell
with_tmpchdir {|d|
- write_file("script1", <<-'End')
+ File.write("script1", <<-'End')
File.open("result1", "w") {|t| t << "taku pid=#{$$} ppid=#{Process.ppid}" }
exit 7
End
- write_file("script2", <<-'End')
+ File.write("script2", <<-'End')
File.open("result2", "w") {|t| t << "take pid=#{$$} ppid=#{Process.ppid}" }
exit 8
End
@@ -1282,7 +1299,7 @@ class TestProcess < Test::Unit::TestCase
if windows?
Dir.mkdir(path = "path with space")
- write_file(bat = path + "/bat test.bat", "@echo %1>out")
+ File.write(bat = path + "/bat test.bat", "@echo %1>out")
pid = spawn(bat, "foo 'bar'")
Process.wait pid
status = $?
@@ -1301,11 +1318,11 @@ class TestProcess < Test::Unit::TestCase
def test_popen_shell
with_tmpchdir {|d|
- write_file("script1", <<-'End')
+ File.write("script1", <<-'End')
puts "tako pid=#{$$} ppid=#{Process.ppid}"
exit 7
End
- write_file("script2", <<-'End')
+ File.write("script2", <<-'End')
puts "tika pid=#{$$} ppid=#{Process.ppid}"
exit 8
End
@@ -1320,7 +1337,7 @@ class TestProcess < Test::Unit::TestCase
if windows?
Dir.mkdir(path = "path with space")
- write_file(bat = path + "/bat test.bat", "@echo %1")
+ File.write(bat = path + "/bat test.bat", "@echo %1")
r = IO.popen([bat, "foo 'bar'"]) {|f| f.read}
assert_equal(%["foo 'bar'"\n], r, '[ruby-core:22960]')
r = IO.popen(%[#{bat.dump} "foo 'bar'"]) {|f| f.read}
@@ -1331,15 +1348,15 @@ class TestProcess < Test::Unit::TestCase
def test_exec_shell
with_tmpchdir {|d|
- write_file("script1", <<-'End')
+ File.write("script1", <<-'End')
File.open("result1", "w") {|t| t << "tiki pid=#{$$} ppid=#{Process.ppid}" }
exit 7
End
- write_file("script2", <<-'End')
+ File.write("script2", <<-'End')
File.open("result2", "w") {|t| t << "tiku pid=#{$$} ppid=#{Process.ppid}" }
exit 8
End
- write_file("s", <<-"End")
+ File.write("s", <<-"End")
ruby = #{RUBY.dump}
exec("\#{ruby} script1 || \#{ruby} script2")
End
@@ -1366,7 +1383,7 @@ class TestProcess < Test::Unit::TestCase
assert_equal("1", IO.popen([[RUBY, "qwerty"], "-e", "print 1"]) {|f| f.read })
- write_file("s", <<-"End")
+ File.write("s", <<-"End")
exec([#{RUBY.dump}, "lkjh"], "-e", "exit 5")
End
pid = spawn RUBY, "s"
@@ -1376,7 +1393,7 @@ class TestProcess < Test::Unit::TestCase
end
def with_stdin(filename)
- open(filename) {|f|
+ File.open(filename) {|f|
begin
old = STDIN.dup
begin
@@ -1393,8 +1410,8 @@ class TestProcess < Test::Unit::TestCase
def test_argv0_noarg
with_tmpchdir {|d|
- open("t", "w") {|f| f.print "exit true" }
- open("f", "w") {|f| f.print "exit false" }
+ File.write("t", "exit true")
+ File.write("f", "exit false")
with_stdin("t") { assert_equal(true, system([RUBY, "qaz"])) }
with_stdin("f") { assert_equal(false, system([RUBY, "wsx"])) }
@@ -1437,8 +1454,15 @@ class TestProcess < Test::Unit::TestCase
assert_equal(s, s)
assert_equal(s, s.to_i)
- assert_equal(s.to_i & 0x55555555, s & 0x55555555)
- assert_equal(s.to_i >> 1, s >> 1)
+ assert_deprecated_warn(/\buse .*Process::Status/) do
+ assert_equal(s.to_i & 0x55555555, s & 0x55555555)
+ end
+ assert_deprecated_warn(/\buse .*Process::Status/) do
+ assert_equal(s.to_i >> 1, s >> 1)
+ end
+ assert_raise(ArgumentError) do
+ s >> -1
+ end
assert_equal(false, s.stopped?)
assert_equal(nil, s.stopsig)
@@ -1454,7 +1478,7 @@ class TestProcess < Test::Unit::TestCase
expected = Signal.list.include?("QUIT") ? [false, true, false, nil] : [true, false, false, true]
with_tmpchdir do
- write_file("foo", "Process.kill(:KILL, $$); exit(42)")
+ File.write("foo", "Process.kill(:KILL, $$); exit(42)")
system(RUBY, "foo")
s = $?
assert_equal(expected,
@@ -1502,7 +1526,7 @@ class TestProcess < Test::Unit::TestCase
def test_wait_without_arg
with_tmpchdir do
- write_file("foo", "sleep 0.1")
+ File.write("foo", "sleep 0.1")
pid = spawn(RUBY, "foo")
assert_equal(pid, Process.wait)
end
@@ -1510,7 +1534,7 @@ class TestProcess < Test::Unit::TestCase
def test_wait2
with_tmpchdir do
- write_file("foo", "sleep 0.1")
+ File.write("foo", "sleep 0.1")
pid = spawn(RUBY, "foo")
assert_equal([pid, 0], Process.wait2)
end
@@ -1518,7 +1542,7 @@ class TestProcess < Test::Unit::TestCase
def test_waitall
with_tmpchdir do
- write_file("foo", "sleep 0.1")
+ File.write("foo", "sleep 0.1")
ps = (0...3).map { spawn(RUBY, "foo") }.sort
ss = Process.waitall.sort
ps.zip(ss) do |p1, (p2, s)|
@@ -1560,7 +1584,7 @@ class TestProcess < Test::Unit::TestCase
with_tmpchdir do
s = run_in_child("abort")
assert_not_predicate(s, :success?)
- write_file("test-script", "#{<<~"begin;"}\n#{<<~'end;'}")
+ File.write("test-script", "#{<<~"begin;"}\n#{<<~'end;'}")
begin;
STDERR.reopen(STDOUT)
begin
@@ -1753,16 +1777,16 @@ class TestProcess < Test::Unit::TestCase
def test_fallback_to_sh
feature = '[ruby-core:32745]'
with_tmpchdir do |d|
- open("tmp_script.#{$$}", "w") {|f| f.puts ": ;"; f.chmod(0755)}
+ File.write("tmp_script.#{$$}", ": ;\n", perm: 0o755)
assert_not_nil(pid = Process.spawn("./tmp_script.#{$$}"), feature)
wpid, st = Process.waitpid2(pid)
assert_equal([pid, true], [wpid, st.success?], feature)
- open("tmp_script.#{$$}", "w") {|f| f.puts "echo $#: $@"; f.chmod(0755)}
+ File.write("tmp_script.#{$$}", "echo $#: $@", perm: 0o755)
result = IO.popen(["./tmp_script.#{$$}", "a b", "c"]) {|f| f.read}
assert_equal("2: a b c\n", result, feature)
- open("tmp_script.#{$$}", "w") {|f| f.puts "echo $hghg"; f.chmod(0755)}
+ File.write("tmp_script.#{$$}", "echo $hghg", perm: 0o755)
result = IO.popen([{"hghg" => "mogomogo"}, "./tmp_script.#{$$}", "a b", "c"]) {|f| f.read}
assert_equal("mogomogo\n", result, feature)
@@ -1799,7 +1823,11 @@ class TestProcess < Test::Unit::TestCase
loop do
Process.spawn(cmds.join(sep), opts)
min = [cmds.size, min].max
- cmds *= 100
+ begin
+ cmds *= 100
+ rescue ArgumentError
+ raise NoMemoryError
+ end
end
rescue NoMemoryError
size = cmds.size
@@ -2141,7 +2169,7 @@ EOS
"c\u{1EE7}a",
].each do |arg|
begin
- arg = arg.encode(Encoding.find("locale"))
+ arg = arg.encode(Encoding.local_charmap)
rescue
else
assert_in_out_err([], "#{<<-"begin;"}\n#{<<-"end;"}", [arg], [], bug12841)
@@ -2355,7 +2383,7 @@ EOS
end
def test_deadlock_by_signal_at_forking
- assert_separately(%W(--disable=gems - #{RUBY}), <<-INPUT, timeout: 100)
+ assert_separately(%W(- #{RUBY}), <<-INPUT, timeout: 100)
ruby = ARGV.shift
GC.start # reduce garbage
GC.disable # avoid triggering CoW after forks
@@ -2686,4 +2714,148 @@ EOS
end
end;
end if Process.respond_to?(:_fork)
+
+ def test_warmup_promote_all_objects_to_oldgen
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ require 'objspace'
+ begin;
+ obj = Object.new
+
+ assert_not_include(ObjectSpace.dump(obj), '"old":true')
+ Process.warmup
+ assert_include(ObjectSpace.dump(obj), '"old":true')
+ end;
+ end
+
+ def test_warmup_run_major_gc_and_compact
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ # Run a GC to ensure that we are not in the middle of a GC run
+ GC.start
+
+ major_gc_count = GC.stat(:major_gc_count)
+ compact_count = GC.stat(:compact_count)
+ Process.warmup
+ assert_equal major_gc_count + 1, GC.stat(:major_gc_count)
+ assert_equal compact_count + 1, GC.stat(:compact_count)
+ end;
+ end
+
+ def test_warmup_precompute_string_coderange
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ require 'objspace'
+ begin;
+ obj = "a" * 12
+ obj.force_encoding(Encoding::UTF_16LE)
+ obj.force_encoding(Encoding::BINARY)
+ assert_include(ObjectSpace.dump(obj), '"coderange":"unknown"')
+ Process.warmup
+ assert_include(ObjectSpace.dump(obj), '"coderange":"7bit"')
+ end;
+ end
+
+ def test_warmup_frees_pages
+ assert_separately([{"RUBY_GC_HEAP_FREE_SLOTS_MAX_RATIO" => "1.0"}, "-W0"], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ GC.start
+
+ TIMES = 10_000
+ ary = Array.new(TIMES)
+ TIMES.times do |i|
+ ary[i] = Object.new
+ end
+ ary.clear
+ ary = nil
+
+ # Disable GC so we can make sure GC only runs in Process.warmup
+ GC.disable
+
+ total_pages_before = GC.stat_heap.map { |_, v| v[:heap_eden_pages] + v[:heap_allocatable_pages] }
+
+ Process.warmup
+
+ # Number of pages freed should cause equal increase in number of allocatable pages.
+ total_pages_before.each_with_index do |val, i|
+ assert_equal(val, GC.stat_heap(i, :heap_eden_pages) + GC.stat_heap(i, :heap_allocatable_pages), "size pool: #{i}")
+ end
+ assert_equal(0, GC.stat(:heap_tomb_pages))
+ assert_operator(GC.stat(:total_freed_pages), :>, 0)
+ end;
+ end
+
+ def test_concurrent_group_and_pid_wait
+ # Use a pair of pipes that will make long_pid exit when this test exits, to avoid
+ # leaking temp processes.
+ long_rpipe, long_wpipe = IO.pipe
+ short_rpipe, short_wpipe = IO.pipe
+ # This process should run forever
+ long_pid = fork do
+ [short_rpipe, short_wpipe, long_wpipe].each(&:close)
+ long_rpipe.read
+ end
+ # This process will exit
+ short_pid = fork do
+ [long_rpipe, long_wpipe, short_wpipe].each(&:close)
+ short_rpipe.read
+ end
+ t1, t2, t3 = nil
+ EnvUtil.timeout(5) do
+ t1 = Thread.new do
+ Process.waitpid long_pid
+ end
+ # Wait for us to be blocking in a call to waitpid2
+ Thread.pass until t1.stop?
+ short_wpipe.close # Make short_pid exit
+
+ # The short pid has exited, so -1 should pick that up.
+ assert_equal short_pid, Process.waitpid(-1)
+
+ # Terminate t1 for the next phase of the test.
+ t1.kill
+ t1.join
+
+ t2 = Thread.new do
+ Process.waitpid(-1)
+ rescue Errno::ECHILD
+ nil
+ end
+ Thread.pass until t2.stop?
+ t3 = Thread.new do
+ Process.waitpid long_pid
+ rescue Errno::ECHILD
+ nil
+ end
+ Thread.pass until t3.stop?
+
+ # it's actually nondeterministic which of t2 or t3 will receive the wait (this
+ # nondeterminism comes from the behaviour of the underlying system calls)
+ long_wpipe.close
+ assert_equal [long_pid], [t2, t3].map(&:value).compact
+ end
+ ensure
+ [t1, t2, t3].each { _1&.kill rescue nil }
+ [t1, t2, t3].each { _1&.join rescue nil }
+ [long_rpipe, long_wpipe, short_rpipe, short_wpipe].each { _1&.close rescue nil }
+ end if defined?(fork)
+
+ def test_handle_interrupt_with_fork
+ Thread.handle_interrupt(RuntimeError => :never) do
+ Thread.current.raise(RuntimeError, "Queued error")
+
+ assert_predicate Thread, :pending_interrupt?
+
+ pid = Process.fork do
+ if Thread.pending_interrupt?
+ exit 1
+ end
+ end
+
+ _, status = Process.waitpid2(pid)
+ assert_predicate status, :success?
+
+ assert_predicate Thread, :pending_interrupt?
+ end
+ rescue RuntimeError
+ # Ignore.
+ end if defined?(fork)
end
diff --git a/test/ruby/test_random_formatter.rb b/test/ruby/test_random_formatter.rb
index a5072099e1..f927522d96 100644
--- a/test/ruby/test_random_formatter.rb
+++ b/test/ruby/test_random_formatter.rb
@@ -75,6 +75,47 @@ module Random::Formatter
assert_match(/\A\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\z/, uuid)
end
+ def assert_uuid_v7(**opts)
+ t1 = current_uuid7_time(**opts)
+ uuid = @it.uuid_v7(**opts)
+ t3 = current_uuid7_time(**opts)
+
+ assert_match(/\A\h{8}-\h{4}-7\h{3}-[89ab]\h{3}-\h{12}\z/, uuid)
+
+ t2 = get_uuid7_time(uuid, **opts)
+ assert_operator(t1, :<=, t2)
+ assert_operator(t2, :<=, t3)
+ end
+
+ def test_uuid_v7
+ assert_uuid_v7
+ 0.upto(12) do |extra_timestamp_bits|
+ assert_uuid_v7 extra_timestamp_bits: extra_timestamp_bits
+ end
+ end
+
+ # It would be nice to simply use Time#floor here. But that is problematic
+ # due to the difference between decimal vs binary fractions.
+ def current_uuid7_time(extra_timestamp_bits: 0)
+ denominator = (1 << extra_timestamp_bits).to_r
+ Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
+ .then {|ns| ((ns / 1_000_000r) * denominator).floor / denominator }
+ .then {|ms| Time.at(ms / 1000r, in: "+00:00") }
+ end
+
+ def get_uuid7_time(uuid, extra_timestamp_bits: 0)
+ denominator = (1 << extra_timestamp_bits) * 1000r
+ extra_chars = extra_timestamp_bits / 4
+ last_char_bits = extra_timestamp_bits % 4
+ extra_chars += 1 if last_char_bits != 0
+ timestamp_re = /\A(\h{8})-(\h{4})-7(\h{#{extra_chars}})/
+ timestamp_chars = uuid.match(timestamp_re).captures.join
+ timestamp = timestamp_chars.to_i(16)
+ timestamp >>= 4 - last_char_bits unless last_char_bits == 0
+ timestamp /= denominator
+ Time.at timestamp, in: "+00:00"
+ end
+
def test_alphanumeric
65.times do |n|
an = @it.alphanumeric(n)
@@ -83,6 +124,20 @@ module Random::Formatter
end
end
+ def test_alphanumeric_chars
+ [
+ [[*"0".."9"], /\A\d*\z/],
+ [[*"a".."t"], /\A[a-t]*\z/],
+ ["一二三四五六七八ä¹å".chars, /\A[一二三四五六七八ä¹å]*\z/],
+ ].each do |chars, pattern|
+ 10.times do |n|
+ an = @it.alphanumeric(n, chars: chars)
+ assert_match(pattern, an)
+ assert_equal(n, an.length)
+ end
+ end
+ end
+
def assert_in_range(range, result, mesg = nil)
assert(range.cover?(result), build_message(mesg, "Expected #{result} to be in #{range}"))
end
diff --git a/test/ruby/test_range.rb b/test/ruby/test_range.rb
index 0a68824f11..84b3b205f0 100644
--- a/test/ruby/test_range.rb
+++ b/test/ruby/test_range.rb
@@ -2,7 +2,7 @@
require 'test/unit'
require 'delegate'
require 'timeout'
-require 'bigdecimal'
+require 'date'
require 'rbconfig/sizeof'
class TestRange < Test::Unit::TestCase
@@ -496,6 +496,151 @@ class TestRange < Test::Unit::TestCase
assert_equal([0, 1, 2, 3, 4], result)
end
+ def test_reverse_each
+ a = []
+ (1..3).reverse_each {|x| a << x }
+ assert_equal([3, 2, 1], a)
+
+ a = []
+ (1...3).reverse_each {|x| a << x }
+ assert_equal([2, 1], a)
+
+ fmax = RbConfig::LIMITS['FIXNUM_MAX']
+ fmin = RbConfig::LIMITS['FIXNUM_MIN']
+
+ a = []
+ (fmax+1..fmax+3).reverse_each {|x| a << x }
+ assert_equal([fmax+3, fmax+2, fmax+1], a)
+
+ a = []
+ (fmax+1...fmax+3).reverse_each {|x| a << x }
+ assert_equal([fmax+2, fmax+1], a)
+
+ a = []
+ (fmax-1..fmax+1).reverse_each {|x| a << x }
+ assert_equal([fmax+1, fmax, fmax-1], a)
+
+ a = []
+ (fmax-1...fmax+1).reverse_each {|x| a << x }
+ assert_equal([fmax, fmax-1], a)
+
+ a = []
+ (fmin-1..fmin+1).reverse_each{|x| a << x }
+ assert_equal([fmin+1, fmin, fmin-1], a)
+
+ a = []
+ (fmin-1...fmin+1).reverse_each{|x| a << x }
+ assert_equal([fmin, fmin-1], a)
+
+ a = []
+ (fmin-3..fmin-1).reverse_each{|x| a << x }
+ assert_equal([fmin-1, fmin-2, fmin-3], a)
+
+ a = []
+ (fmin-3...fmin-1).reverse_each{|x| a << x }
+ assert_equal([fmin-2, fmin-3], a)
+
+ a = []
+ ("a".."c").reverse_each {|x| a << x }
+ assert_equal(["c", "b", "a"], a)
+ end
+
+ def test_reverse_each_for_beginless_range
+ fmax = RbConfig::LIMITS['FIXNUM_MAX']
+ fmin = RbConfig::LIMITS['FIXNUM_MIN']
+
+ a = []
+ (..3).reverse_each {|x| a << x; break if x <= 0 }
+ assert_equal([3, 2, 1, 0], a)
+
+ a = []
+ (...3).reverse_each {|x| a << x; break if x <= 0 }
+ assert_equal([2, 1, 0], a)
+
+ a = []
+ (..fmax+1).reverse_each {|x| a << x; break if x <= fmax-1 }
+ assert_equal([fmax+1, fmax, fmax-1], a)
+
+ a = []
+ (...fmax+1).reverse_each {|x| a << x; break if x <= fmax-1 }
+ assert_equal([fmax, fmax-1], a)
+
+ a = []
+ (..fmin+1).reverse_each {|x| a << x; break if x <= fmin-1 }
+ assert_equal([fmin+1, fmin, fmin-1], a)
+
+ a = []
+ (...fmin+1).reverse_each {|x| a << x; break if x <= fmin-1 }
+ assert_equal([fmin, fmin-1], a)
+
+ a = []
+ (..fmin-1).reverse_each {|x| a << x; break if x <= fmin-3 }
+ assert_equal([fmin-1, fmin-2, fmin-3], a)
+
+ a = []
+ (...fmin-1).reverse_each {|x| a << x; break if x <= fmin-3 }
+ assert_equal([fmin-2, fmin-3], a)
+ end
+
+ def test_reverse_each_for_endless_range
+ assert_raise(TypeError) { (1..).reverse_each {} }
+
+ enum = nil
+ assert_nothing_raised { enum = (1..).reverse_each }
+ assert_raise(TypeError) { enum.each {} }
+ end
+
+ def test_reverse_each_for_single_point_range
+ fmin = RbConfig::LIMITS['FIXNUM_MIN']
+ fmax = RbConfig::LIMITS['FIXNUM_MAX']
+
+ values = [fmin*2, fmin-1, fmin, 0, fmax, fmax+1, fmax*2]
+
+ values.each do |b|
+ r = b..b
+ a = []
+ r.reverse_each {|x| a << x }
+ assert_equal([b], a, "failed on #{r}")
+
+ r = b...b+1
+ a = []
+ r.reverse_each {|x| a << x }
+ assert_equal([b], a, "failed on #{r}")
+ end
+ end
+
+ def test_reverse_each_for_empty_range
+ fmin = RbConfig::LIMITS['FIXNUM_MIN']
+ fmax = RbConfig::LIMITS['FIXNUM_MAX']
+
+ values = [fmin*2, fmin-1, fmin, 0, fmax, fmax+1, fmax*2]
+
+ values.each do |b|
+ r = b..b-1
+ a = []
+ r.reverse_each {|x| a << x }
+ assert_equal([], a, "failed on #{r}")
+ end
+
+ values.repeated_permutation(2).to_a.product([true, false]).each do |(b, e), excl|
+ next unless b > e || (b == e && excl)
+
+ r = Range.new(b, e, excl)
+ a = []
+ r.reverse_each {|x| a << x }
+ assert_equal([], a, "failed on #{r}")
+ end
+ end
+
+ def test_reverse_each_with_no_block
+ enum = (1..5).reverse_each
+ assert_equal 5, enum.size
+
+ a = []
+ enum.each {|x| a << x }
+ assert_equal [5, 4, 3, 2, 1], a
+ end
+
def test_begin_end
assert_equal(0, (0..1).begin)
assert_equal(1, (0..1).end)
@@ -581,6 +726,8 @@ class TestRange < Test::Unit::TestCase
assert_not_operator(5..nil, :===, 0)
assert_operator(nil..10, :===, 0)
assert_operator(nil..nil, :===, 0)
+ assert_operator(nil..nil, :===, Object.new)
+ assert_not_operator(0..10, :===, 0..10)
end
def test_eqq_string
@@ -588,7 +735,7 @@ class TestRange < Test::Unit::TestCase
assert_not_operator('A'..'Z', :===, 'ana')
assert_operator('A'.., :===, 'ANA')
assert_operator(..'Z', :===, 'ANA')
- assert_raise(TypeError) {(nil..nil) === 'ANA'}
+ assert_operator(nil..nil, :===, 'ANA')
end
def test_eqq_time
@@ -625,6 +772,28 @@ class TestRange < Test::Unit::TestCase
assert_operator(c.new(0)..c.new(10), :===, c.new(5), bug12003)
end
+ def test_eqq_unbounded_ruby_bug_19864
+ t1 = Date.today
+ t2 = t1 + 1
+ assert_equal(true, (..t1) === t1)
+ assert_equal(false, (..t1) === t2)
+ assert_equal(true, (..t2) === t1)
+ assert_equal(true, (..t2) === t2)
+ assert_equal(false, (...t1) === t1)
+ assert_equal(false, (...t1) === t2)
+ assert_equal(true, (...t2) === t1)
+ assert_equal(false, (...t2) === t2)
+
+ assert_equal(true, (t1..) === t1)
+ assert_equal(true, (t1..) === t2)
+ assert_equal(false, (t2..) === t1)
+ assert_equal(true, (t2..) === t2)
+ assert_equal(true, (t1...) === t1)
+ assert_equal(true, (t1...) === t2)
+ assert_equal(false, (t2...) === t1)
+ assert_equal(true, (t2...) === t2)
+ end
+
def test_eqq_non_iteratable
k = Class.new do
include Comparable
@@ -814,21 +983,38 @@ class TestRange < Test::Unit::TestCase
end
def test_size
- assert_equal 42, (1..42).size
- assert_equal 41, (1...42).size
- assert_equal 6, (1...6.3).size
- assert_equal 5, (1.1...6).size
- assert_equal 42, (1..42).each.size
+ Enumerator.product([:to_i, :to_f, :to_r].repeated_permutation(2), [1, 10], [5, 5.5], [true, false]) do |(m1, m2), beg, ende, exclude_end|
+ r = Range.new(beg.send(m1), ende.send(m2), exclude_end)
+ iterable = true
+ yielded = []
+ begin
+ r.each { yielded << _1 }
+ rescue TypeError
+ iterable = false
+ end
+
+ if iterable
+ assert_equal(yielded.size, r.size, "failed on #{r}")
+ assert_equal(yielded.size, r.each.size, "failed on #{r}")
+ else
+ assert_raise(TypeError, "failed on #{r}") { r.size }
+ assert_raise(TypeError, "failed on #{r}") { r.each.size }
+ end
+ end
+
assert_nil ("a"..."z").size
- assert_nil ("a"...).size
- assert_nil (..."z").size # [Bug #18983]
- assert_nil (nil...nil).size # [Bug #18983]
-
- assert_equal Float::INFINITY, (1...).size
- assert_equal Float::INFINITY, (1.0...).size
- assert_equal Float::INFINITY, (...1).size
- assert_equal Float::INFINITY, (...1.0).size
- assert_nil ("a"...).size
+
+ assert_equal Float::INFINITY, (1..).size
+ assert_raise(TypeError) { (1.0..).size }
+ assert_raise(TypeError) { (1r..).size }
+ assert_nil ("a"..).size
+
+ assert_raise(TypeError) { (..1).size }
+ assert_raise(TypeError) { (..1.0).size }
+ assert_raise(TypeError) { (..1r).size }
+ assert_raise(TypeError) { (..'z').size }
+
+ assert_raise(TypeError) { (nil...nil).size }
end
def test_bsearch_typechecks_return_values
@@ -852,9 +1038,6 @@ class TestRange < Test::Unit::TestCase
assert_raise(TypeError) {
(Rational(-1,2)..Rational(9,4)).bsearch
}
- assert_raise(TypeError) {
- (BigDecimal('0.5')..BigDecimal('2.25')).bsearch
- }
end
def test_bsearch_for_fixnum
@@ -1028,7 +1211,10 @@ class TestRange < Test::Unit::TestCase
assert_equal(nil, (bignum...bignum+ary.size).bsearch {|i| ary[i - bignum] >= 100 })
assert_equal(bignum + 0, (bignum...bignum+ary.size).bsearch {|i| true })
assert_equal(nil, (bignum...bignum+ary.size).bsearch {|i| false })
+
+ assert_equal(bignum * 2 + 1, (0...).bsearch {|i| i > bignum * 2 })
assert_equal(bignum * 2 + 1, (bignum...).bsearch {|i| i > bignum * 2 })
+ assert_equal(-bignum * 2 + 1, (...0).bsearch {|i| i > -bignum * 2 })
assert_equal(-bignum * 2 + 1, (...-bignum).bsearch {|i| i > -bignum * 2 })
assert_raise(TypeError) { ("a".."z").bsearch {} }
@@ -1053,6 +1239,81 @@ class TestRange < Test::Unit::TestCase
end
def test_count
+ assert_equal 42, (1..42).count
+ assert_equal 41, (1...42).count
+ assert_equal 0, (42..1).count
+ assert_equal 0, (42...1).count
+ assert_equal 2**100, (1..2**100).count
+ assert_equal 6, (1...6.3).count
+ assert_equal 4, ('a'..'d').count
+ assert_equal 3, ('a'...'d').count
+
assert_equal(Float::INFINITY, (1..).count)
+ assert_equal(Float::INFINITY, (..1).count)
+ end
+
+ def test_overlap?
+ assert_not_operator(0..2, :overlap?, -2..-1)
+ assert_not_operator(0..2, :overlap?, -2...0)
+ assert_operator(0..2, :overlap?, -1..0)
+ assert_operator(0..2, :overlap?, 1..2)
+ assert_operator(0..2, :overlap?, 2..3)
+ assert_not_operator(0..2, :overlap?, 3..4)
+ assert_not_operator(0...2, :overlap?, 2..3)
+
+ assert_operator(..0, :overlap?, -1..0)
+ assert_operator(...0, :overlap?, -1..0)
+ assert_operator(..0, :overlap?, 0..1)
+ assert_operator(..0, :overlap?, ..1)
+ assert_not_operator(..0, :overlap?, 1..2)
+ assert_not_operator(...0, :overlap?, 0..1)
+
+ assert_not_operator(0.., :overlap?, -2..-1)
+ assert_not_operator(0.., :overlap?, ...0)
+ assert_operator(0.., :overlap?, -1..0)
+ assert_operator(0.., :overlap?, ..0)
+ assert_operator(0.., :overlap?, 0..1)
+ assert_operator(0.., :overlap?, 1..2)
+ assert_operator(0.., :overlap?, 1..)
+
+ assert_not_operator((1..3), :overlap?, ('a'..'d'))
+ assert_not_operator((1..), :overlap?, ('a'..))
+ assert_not_operator((..1), :overlap?, (..'a'))
+
+ assert_raise(TypeError) { (0..).overlap?(1) }
+ assert_raise(TypeError) { (0..).overlap?(nil) }
+
+ assert_operator((1..3), :overlap?, (2..4))
+ assert_operator((1...3), :overlap?, (2..3))
+ assert_operator((2..3), :overlap?, (1..2))
+ assert_operator((..3), :overlap?, (3..))
+ assert_operator((nil..nil), :overlap?, (3..))
+ assert_operator((nil...nil), :overlap?, (nil..))
+
+ assert_raise(TypeError) { (1..3).overlap?(1) }
+
+ assert_not_operator((1..2), :overlap?, (2...2))
+ assert_not_operator((2...2), :overlap?, (1..2))
+
+ assert_not_operator((4..1), :overlap?, (2..3))
+ assert_not_operator((4..1), :overlap?, (..3))
+ assert_not_operator((4..1), :overlap?, (2..))
+
+ assert_not_operator((1..4), :overlap?, (3..2))
+ assert_not_operator((..4), :overlap?, (3..2))
+ assert_not_operator((1..), :overlap?, (3..2))
+
+ assert_not_operator((4..5), :overlap?, (2..3))
+ assert_not_operator((4..5), :overlap?, (2...4))
+
+ assert_not_operator((1..2), :overlap?, (3..4))
+ assert_not_operator((1...3), :overlap?, (3..4))
+
+ assert_not_operator((4..5), :overlap?, (2..3))
+ assert_not_operator((4..5), :overlap?, (2...4))
+
+ assert_not_operator((1..2), :overlap?, (3..4))
+ assert_not_operator((1...3), :overlap?, (3..4))
+ assert_not_operator((...3), :overlap?, (3..))
end
end
diff --git a/test/ruby/test_refinement.rb b/test/ruby/test_refinement.rb
index 56f33ae00a..11acf31f21 100644
--- a/test/ruby/test_refinement.rb
+++ b/test/ruby/test_refinement.rb
@@ -1044,7 +1044,7 @@ class TestRefinement < Test::Unit::TestCase
end
using Test
def t
- 'Refinements are broken!'.chop!
+ 'Refinements are broken!'.dup.chop!
end
t
module Test
@@ -1606,18 +1606,35 @@ class TestRefinement < Test::Unit::TestCase
end
using R
+ def m
+ C.new.m
+ end
+
assert_equal(:foo, C.new.m)
+ assert_equal(:foo, m)
module R
refine C do
+
+ assert_equal(:foo, C.new.m)
+ assert_equal(:foo, m)
+
alias m m
+
+ assert_equal(:foo, C.new.m)
+ assert_equal(:foo, m)
+
def m
:bar
end
+
+ assert_equal(:bar, C.new.m, "[ruby-core:71423] [Bug #11672]")
+ assert_equal(:bar, m, "[Bug #20285]")
end
end
assert_equal(:bar, C.new.m, "[ruby-core:71423] [Bug #11672]")
+ assert_equal(:bar, m, "[Bug #20285]")
end;
end
@@ -1798,7 +1815,7 @@ class TestRefinement < Test::Unit::TestCase
assert_equal([int_refinement, str_refinement], m.refinements)
end
- def test_refined_class
+ def test_target
refinements = Module.new {
refine Integer do
end
@@ -1806,8 +1823,14 @@ class TestRefinement < Test::Unit::TestCase
refine String do
end
}.refinements
- assert_equal(Integer, refinements[0].refined_class)
- assert_equal(String, refinements[1].refined_class)
+ assert_equal(Integer, refinements[0].target)
+ assert_warn(/Refinement#refined_class is deprecated and will be removed in Ruby 3.4; use Refinement#target instead/) do
+ assert_equal(Integer, refinements[0].refined_class)
+ end
+ assert_equal(String, refinements[1].target)
+ assert_warn(/Refinement#refined_class is deprecated and will be removed in Ruby 3.4; use Refinement#target instead/) do
+ assert_equal(String, refinements[1].refined_class)
+ end
end
def test_warn_setconst_in_refinmenet
@@ -2626,6 +2649,75 @@ class TestRefinement < Test::Unit::TestCase
assert_equal([], Refinement.used_modules)
end
+ def test_inlinecache
+ assert_separately([], <<-"end;")
+ module R
+ refine String do
+ def to_s = :R
+ end
+ end
+
+ 2.times{|i|
+ s = ''.to_s
+ assert_equal '', s if i == 0
+ assert_equal :R, s if i == 1
+ using R if i == 0
+ assert_equal :R, ''.to_s
+ }
+ end;
+ end
+
+ def test_inline_cache_invalidation
+ klass = Class.new do
+ def cached_foo_callsite = foo
+
+ def foo = :v1
+
+ host = self
+ @refinement = Module.new do
+ refine(host) do
+ def foo = :unused
+ end
+ end
+ end
+
+ obj = klass.new
+ obj.cached_foo_callsite # prime cache
+ klass.class_eval do
+ def foo = :v2 # invalidate
+ end
+ assert_equal(:v2, obj.cached_foo_callsite)
+ end
+
+ # [Bug #20302]
+ def test_multiple_refinements_for_same_module
+ assert_in_out_err([], <<-INPUT, %w(:f2 :f1), [])
+ module M1
+ refine(Kernel) do
+ def f1 = :f1
+ end
+ end
+
+ module M2
+ refine(Kernel) do
+ def f2 = :f2
+ end
+ end
+
+ class Foo
+ using M1
+ using M2
+
+ def test
+ p f2
+ p f1
+ end
+ end
+
+ Foo.new.test
+ INPUT
+ end
+
private
def eval_using(mod, s)
diff --git a/test/ruby/test_regexp.rb b/test/ruby/test_regexp.rb
index 0a72caba45..c72029ca80 100644
--- a/test/ruby/test_regexp.rb
+++ b/test/ruby/test_regexp.rb
@@ -72,6 +72,19 @@ class TestRegexp < Test::Unit::TestCase
end
end
+ def test_to_s_under_gc_compact_stress
+ omit "compaction doesn't work well on s390x" if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077
+ EnvUtil.under_gc_compact_stress do
+ str = "abcd\u3042"
+ [:UTF_16BE, :UTF_16LE, :UTF_32BE, :UTF_32LE].each do |es|
+ enc = Encoding.const_get(es)
+ rs = Regexp.new(str.encode(enc)).to_s
+ assert_equal("(?-mix:abcd\u3042)".encode(enc), rs)
+ assert_equal(enc, rs.encoding)
+ end
+ end
+ end
+
def test_to_s_extended_subexp
re = /#\g#{"\n"}/x
re = /#{re}/
@@ -457,6 +470,13 @@ class TestRegexp < Test::Unit::TestCase
assert_equal('/\/\xF1\xF2\xF3/i', /\/#{s}/i.inspect)
end
+ def test_inspect_under_gc_compact_stress
+ omit "compaction doesn't work well on s390x" if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077
+ EnvUtil.under_gc_compact_stress do
+ assert_equal('/(?-mix:\\/)|/', Regexp.union(/\//, "").inspect)
+ end
+ end
+
def test_char_to_option
assert_equal("BAR", "FOOBARBAZ"[/b../i])
assert_equal("bar", "foobarbaz"[/ b . . /x])
@@ -693,6 +713,18 @@ class TestRegexp < Test::Unit::TestCase
}
end
+ def test_match_no_match_no_matchdata
+ EnvUtil.without_gc do
+ h = {}
+ ObjectSpace.count_objects(h)
+ prev_matches = h[:T_MATCH] || 0
+ _md = /[A-Z]/.match('1') # no match
+ ObjectSpace.count_objects(h)
+ new_matches = h[:T_MATCH] || 0
+ assert_equal prev_matches, new_matches, "Bug [#20104]"
+ end
+ end
+
def test_initialize
assert_raise(ArgumentError) { Regexp.new }
assert_equal(/foo/, assert_warning(/ignored/) {Regexp.new(/foo/, Regexp::IGNORECASE)})
@@ -723,6 +755,12 @@ class TestRegexp < Test::Unit::TestCase
assert_raise(RegexpError) { Regexp.new("((?<v>))\\g<0>") }
end
+ def test_initialize_from_regex_memory_corruption
+ assert_ruby_status([], <<-'end;')
+ 10_000.times { Regexp.new(Regexp.new("(?<name>)")) }
+ end;
+ end
+
def test_initialize_bool_warning
assert_warning(/expected true or false as ignorecase/) do
Regexp.new("foo", :i)
@@ -854,6 +892,14 @@ class TestRegexp < Test::Unit::TestCase
$_ = nil; assert_nil(~/./)
end
+ def test_match_under_gc_compact_stress
+ omit "compaction doesn't work well on s390x" if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077
+ EnvUtil.under_gc_compact_stress do
+ m = /(?<foo>.)(?<n>[^aeiou])?(?<bar>.+)/.match("hoge\u3042")
+ assert_equal("h", m.match(:foo))
+ end
+ end
+
def test_match_p
/backref/ =~ 'backref'
# must match here, but not in a separate method, e.g., assert_send,
@@ -1360,30 +1406,109 @@ class TestRegexp < Test::Unit::TestCase
end
def test_unicode_age
- assert_match(/^\p{Age=6.0}$/u, "\u261c")
- assert_match(/^\p{Age=1.1}$/u, "\u261c")
- assert_no_match(/^\P{age=6.0}$/u, "\u261c")
-
- assert_match(/^\p{age=6.0}$/u, "\u31f6")
- assert_match(/^\p{age=3.2}$/u, "\u31f6")
- assert_no_match(/^\p{age=3.1}$/u, "\u31f6")
- assert_no_match(/^\p{age=3.0}$/u, "\u31f6")
- assert_no_match(/^\p{age=1.1}$/u, "\u31f6")
-
- assert_match(/^\p{age=6.0}$/u, "\u2754")
- assert_no_match(/^\p{age=5.0}$/u, "\u2754")
- assert_no_match(/^\p{age=4.0}$/u, "\u2754")
- assert_no_match(/^\p{age=3.0}$/u, "\u2754")
- assert_no_match(/^\p{age=2.0}$/u, "\u2754")
- assert_no_match(/^\p{age=1.1}$/u, "\u2754")
-
- assert_no_match(/^\p{age=12.0}$/u, "\u32FF")
- assert_match(/^\p{age=12.1}$/u, "\u32FF")
- assert_no_match(/^\p{age=13.0}$/u, "\u{10570}")
- assert_match(/^\p{age=14.0}$/u, "\u{10570}")
- assert_match(/^\p{age=14.0}$/u, "\u9FFF")
- assert_match(/^\p{age=14.0}$/u, "\u{2A6DF}")
- assert_match(/^\p{age=14.0}$/u, "\u{2B738}")
+ assert_unicode_age("\u261c", matches: %w"6.0 1.1", unmatches: [])
+
+ assert_unicode_age("\u31f6", matches: %w"6.0 3.2", unmatches: %w"3.1 3.0 1.1")
+ assert_unicode_age("\u2754", matches: %w"6.0", unmatches: %w"5.0 4.0 3.0 2.0 1.1")
+
+ assert_unicode_age("\u32FF", matches: %w"12.1", unmatches: %w"12.0")
+ end
+
+ def test_unicode_age_14_0
+ @matches = %w"14.0"
+ @unmatches = %w"13.0"
+
+ assert_unicode_age("\u{10570}")
+ assert_unicode_age("\u9FFF")
+ assert_unicode_age("\u{2A6DF}")
+ assert_unicode_age("\u{2B738}")
+ end
+
+ def test_unicode_age_15_0
+ @matches = %w"15.0"
+ @unmatches = %w"14.0"
+
+ assert_unicode_age("\u{0CF3}",
+ "KANNADA SIGN COMBINING ANUSVARA ABOVE RIGHT")
+ assert_unicode_age("\u{0ECE}", "LAO YAMAKKAN")
+ assert_unicode_age("\u{10EFD}".."\u{10EFF}",
+ "ARABIC SMALL LOW WORD SAKTA..ARABIC SMALL LOW WORD MADDA")
+ assert_unicode_age("\u{1123F}".."\u{11241}",
+ "KHOJKI LETTER QA..KHOJKI VOWEL SIGN VOCALIC R")
+ assert_unicode_age("\u{11B00}".."\u{11B09}",
+ "DEVANAGARI HEAD MARK..DEVANAGARI SIGN MINDU")
+ assert_unicode_age("\u{11F00}".."\u{11F10}",
+ "KAWI SIGN CANDRABINDU..KAWI LETTER O")
+ assert_unicode_age("\u{11F12}".."\u{11F3A}",
+ "KAWI LETTER KA..KAWI VOWEL SIGN VOCALIC R")
+ assert_unicode_age("\u{11F3E}".."\u{11F59}",
+ "KAWI VOWEL SIGN E..KAWI DIGIT NINE")
+ assert_unicode_age("\u{1342F}",
+ "EGYPTIAN HIEROGLYPH V011D")
+ assert_unicode_age("\u{13439}".."\u{1343F}",
+ "EGYPTIAN HIEROGLYPH INSERT AT MIDDLE..EGYPTIAN HIEROGLYPH END WALLED ENCLOSURE")
+ assert_unicode_age("\u{13440}".."\u{13455}",
+ "EGYPTIAN HIEROGLYPH MIRROR HORIZONTALLY..EGYPTIAN HIEROGLYPH MODIFIER DAMAGED")
+ assert_unicode_age("\u{1B132}", "HIRAGANA LETTER SMALL KO")
+ assert_unicode_age("\u{1B155}", "KATAKANA LETTER SMALL KO")
+ assert_unicode_age("\u{1D2C0}".."\u{1D2D3}",
+ "KAKTOVIK NUMERAL ZERO..KAKTOVIK NUMERAL NINETEEN")
+ assert_unicode_age("\u{1DF25}".."\u{1DF2A}",
+ "LATIN SMALL LETTER D WITH MID-HEIGHT LEFT HOOK..LATIN SMALL LETTER T WITH MID-HEIGHT LEFT HOOK")
+ assert_unicode_age("\u{1E030}".."\u{1E06D}",
+ "MODIFIER LETTER CYRILLIC SMALL A..MODIFIER LETTER CYRILLIC SMALL STRAIGHT U WITH STROKE")
+ assert_unicode_age("\u{1E08F}",
+ "COMBINING CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I")
+ assert_unicode_age("\u{1E4D0}".."\u{1E4F9}",
+ "NAG MUNDARI LETTER O..NAG MUNDARI DIGIT NINE")
+ assert_unicode_age("\u{1F6DC}", "WIRELESS")
+ assert_unicode_age("\u{1F774}".."\u{1F776}",
+ "LOT OF FORTUNE..LUNAR ECLIPSE")
+ assert_unicode_age("\u{1F77B}".."\u{1F77F}",
+ "HAUMEA..ORCUS")
+ assert_unicode_age("\u{1F7D9}", "NINE POINTED WHITE STAR")
+ assert_unicode_age("\u{1FA75}".."\u{1FA77}",
+ "LIGHT BLUE HEART..PINK HEART")
+ assert_unicode_age("\u{1FA87}".."\u{1FA88}",
+ "MARACAS..FLUTE")
+ assert_unicode_age("\u{1FAAD}".."\u{1FAAF}",
+ "FOLDING HAND FAN..KHANDA")
+ assert_unicode_age("\u{1FABB}".."\u{1FABD}",
+ "HYACINTH..WING")
+ assert_unicode_age("\u{1FABF}", "GOOSE")
+ assert_unicode_age("\u{1FACE}".."\u{1FACF}",
+ "MOOSE..DONKEY")
+ assert_unicode_age("\u{1FADA}".."\u{1FADB}",
+ "GINGER ROOT..PEA POD")
+ assert_unicode_age("\u{1FAE8}", "SHAKING FACE")
+ assert_unicode_age("\u{1FAF7}".."\u{1FAF8}",
+ "LEFTWARDS PUSHING HAND..RIGHTWARDS PUSHING HAND")
+ assert_unicode_age("\u{2B739}",
+ "CJK UNIFIED IDEOGRAPH-2B739")
+ assert_unicode_age("\u{31350}".."\u{323AF}",
+ "CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-323AF")
+ end
+
+ UnicodeAgeRegexps = Hash.new do |h, age|
+ h[age] = [/\A\p{age=#{age}}+\z/u, /\A\P{age=#{age}}+\z/u].freeze
+ end
+
+ def assert_unicode_age(char, mesg = nil, matches: @matches, unmatches: @unmatches)
+ if Range === char
+ char = char.to_a.join("")
+ end
+
+ matches.each do |age|
+ pos, neg = UnicodeAgeRegexps[age]
+ assert_match(pos, char, mesg)
+ assert_not_match(neg, char, mesg)
+ end
+
+ unmatches.each do |age|
+ pos, neg = UnicodeAgeRegexps[age]
+ assert_not_match(pos, char, mesg)
+ assert_match(neg, char, mesg)
+ end
end
MatchData_A = eval("class MatchData_\u{3042} < MatchData; self; end")
@@ -1685,6 +1810,31 @@ class TestRegexp < Test::Unit::TestCase
end;
end
+ def test_s_timeout_memory_leak
+ assert_no_memory_leak([], "#{<<~"begin;"}", "#{<<~"end;"}", "[Bug #20228]", rss: true)
+ Regexp.timeout = 0.001
+ regex = /^(a*)*$/
+ str = "a" * 1000000 + "x"
+
+ code = proc do
+ regex =~ str
+ rescue
+ end
+
+ 10.times(&code)
+ begin;
+ 1_000.times(&code)
+ end;
+ end
+
+ def test_bug_20453
+ re = Regexp.new("^(a*)x$", timeout: 0.001)
+
+ assert_raise(Regexp::TimeoutError) do
+ re =~ "a" * 1000000 + "x"
+ end
+ end
+
def per_instance_redos_test(global_timeout, per_instance_timeout, expected_timeout)
assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
global_timeout = #{ EnvUtil.apply_timeout_scale(global_timeout).inspect }
@@ -1711,10 +1861,12 @@ class TestRegexp < Test::Unit::TestCase
end
def test_timeout_shorter_than_global
+ omit "timeout test is too unstable on s390x" if RUBY_PLATFORM =~ /s390x/
per_instance_redos_test(10, 0.2, 0.2)
end
def test_timeout_longer_than_global
+ omit "timeout test is too unstable on s390x" if RUBY_PLATFORM =~ /s390x/
per_instance_redos_test(0.01, 0.5, 0.5)
end
@@ -1745,7 +1897,6 @@ class TestRegexp < Test::Unit::TestCase
timeout = #{ EnvUtil.apply_timeout_scale(10).inspect }
begin;
Regexp.timeout = timeout
-
assert_nil(/^(a*)*$/ =~ "a" * 1000000 + "x")
end;
end
@@ -1755,7 +1906,6 @@ class TestRegexp < Test::Unit::TestCase
timeout = #{ EnvUtil.apply_timeout_scale(10).inspect }
begin;
Regexp.timeout = timeout
-
assert_nil(/^a*b?a*$/ =~ "a" * 1000000 + "x")
end;
end
@@ -1765,8 +1915,70 @@ class TestRegexp < Test::Unit::TestCase
timeout = #{ EnvUtil.apply_timeout_scale(10).inspect }
begin;
Regexp.timeout = timeout
+ assert_nil(/^a*?(?>a*a*)$/ =~ "a" * 1000000 + "x")
+ end;
+ end
+
+ def test_match_cache_atomic_complex
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ timeout = #{ EnvUtil.apply_timeout_scale(10).inspect }
+ begin;
+ Regexp.timeout = timeout
+ assert_nil(/a*(?>a*)ab/ =~ "a" * 1000000 + "b")
+ end;
+ end
+
+ def test_match_cache_positive_look_ahead
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ timeout = #{ EnvUtil.apply_timeout_scale(10).inspect }
+ begin;
+ Regexp.timeout = timeout
+ assert_nil(/^a*?(?=a*a*)$/ =~ "a" * 1000000 + "x")
+ end;
+ end
+
+ def test_match_cache_positive_look_ahead_complex
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ timeout = #{ EnvUtil.apply_timeout_scale(10).inspect }
+ begin;
+ Regexp.timeout = timeout
+ assert_equal(/(?:(?=a*)a)*/ =~ "a" * 1000000, 0)
+ end;
+ end
+
+ def test_match_cache_negative_look_ahead
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ timeout = #{ EnvUtil.apply_timeout_scale(10).inspect }
+ begin;
+ Regexp.timeout = timeout
+ assert_nil(/^a*?(?!a*a*)$/ =~ "a" * 1000000 + "x")
+ end;
+ end
- assert_nil(/^(?>a?a?)(a|a)*$/ =~ "a" * 1000000 + "x")
+ def test_match_cache_positive_look_behind
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ timeout = #{ EnvUtil.apply_timeout_scale(10).inspect }
+ begin;
+ Regexp.timeout = timeout
+ assert_nil(/(?<=abc|def)(a|a)*$/ =~ "abc" + "a" * 1000000 + "x")
+ end;
+ end
+
+ def test_match_cache_negative_look_behind
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ timeout = #{ EnvUtil.apply_timeout_scale(10).inspect }
+ begin;
+ Regexp.timeout = timeout
+ assert_nil(/(?<!x)(a|a)*$/ =~ "a" * 1000000 + "x")
+ end;
+ end
+
+ def test_match_cache_with_peek_optimization
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ timeout = #{ EnvUtil.apply_timeout_scale(10).inspect }
+ begin;
+ Regexp.timeout = timeout
+ assert_nil(/a+z/ =~ "a" * 1000000 + "xz")
end;
end
@@ -1783,7 +1995,7 @@ class TestRegexp < Test::Unit::TestCase
assert_equal("10:0:0".match(pattern)[0], "10:0:0")
end
- def test_bug_19467
+ def test_bug_19467 # [Bug #19467]
assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
timeout = #{ EnvUtil.apply_timeout_scale(10).inspect }
begin;
@@ -1798,7 +2010,17 @@ class TestRegexp < Test::Unit::TestCase
assert_equal("123456789".match(/(?:x?\dx?){2,}/)[0], "123456789")
end
- def test_bug_19537
+ def test_encoding_flags_are_preserved_when_initialized_with_another_regexp
+ re = Regexp.new("\u2018hello\u2019".encode("UTF-8"))
+ str = "".encode("US-ASCII")
+
+ assert_nothing_raised do
+ str.match?(re)
+ str.match?(Regexp.new(re))
+ end
+ end
+
+ def test_bug_19537 # [Bug #19537]
str = 'aac'
re = '^([ab]{1,3})(a?)*$'
100.times do
@@ -1806,6 +2028,39 @@ class TestRegexp < Test::Unit::TestCase
end
end
+ def test_bug_20083 # [Bug #20083]
+ re = /([\s]*ABC)$/i
+ (1..100).each do |n|
+ text = "#{"0" * n}ABC"
+ assert text.match?(re)
+ end
+ end
+
+ def test_bug_20098 # [Bug #20098]
+ assert(/a((.|.)|bc){,4}z/.match? 'abcbcbcbcz')
+ assert(/a(b+?c*){4,5}z/.match? 'abbbccbbbccbcbcz')
+ assert(/a(b+?(.|.)){2,3}z/.match? 'abbbcbbbcbbbcz')
+ assert(/a(b*?(.|.)[bc]){2,5}z/.match? 'abcbbbcbcccbcz')
+ assert(/^(?:.+){2,4}?b|b/.match? "aaaabaa")
+ end
+
+ def test_bug_20207 # [Bug #20207]
+ assert(!'clan'.match?(/(?=.*a)(?!.*n)/))
+ end
+
+ def test_bug_20212 # [Bug #20212]
+ regex = Regexp.new(
+ /\A((?=.*?[a-z])(?!.*--)[a-z\d]+[a-z\d-]*[a-z\d]+).((?=.*?[a-z])(?!.*--)[a-z\d]+[a-z\d-]*[a-z\d]+).((?=.*?[a-z])(?!.*--)[a-z]+[a-z-]*[a-z]+).((?=.*?[a-z])(?!.*--)[a-z]+[a-z-]*[a-z]+)\Z/x
+ )
+ string = "www.google.com"
+ 100.times.each { assert(regex.match?(string)) }
+ end
+
+ def test_bug_20246 # [Bug #20246]
+ assert_equal '1.2.3', '1.2.3'[/(\d+)(\.\g<1>){2}/]
+ assert_equal '1.2.3', '1.2.3'[/((?:\d|foo|bar)+)(\.\g<1>){2}/]
+ end
+
def test_linear_time_p
assert_send [Regexp, :linear_time?, /a/]
assert_send [Regexp, :linear_time?, 'a']
@@ -1813,6 +2068,9 @@ class TestRegexp < Test::Unit::TestCase
assert_not_send [Regexp, :linear_time?, /(a)\1/]
assert_not_send [Regexp, :linear_time?, "(a)\\1"]
+ assert_not_send [Regexp, :linear_time?, /(?=(a))/]
+ assert_not_send [Regexp, :linear_time?, /(?!(a))/]
+
assert_raise(TypeError) {Regexp.linear_time?(nil)}
assert_raise(TypeError) {Regexp.linear_time?(Regexp.allocate)}
end
diff --git a/test/ruby/test_require.rb b/test/ruby/test_require.rb
index 60e5b0f8b1..ef33928376 100644
--- a/test/ruby/test_require.rb
+++ b/test/ruby/test_require.rb
@@ -6,11 +6,27 @@ require 'tmpdir'
class TestRequire < Test::Unit::TestCase
def test_load_error_path
- filename = "should_not_exist"
- error = assert_raise(LoadError) do
- require filename
- end
- assert_equal filename, error.path
+ Tempfile.create(["should_not_exist", ".rb"]) {|t|
+ filename = t.path
+ t.close
+ File.unlink(filename)
+
+ error = assert_raise(LoadError) do
+ require filename
+ end
+ assert_equal filename, error.path
+
+ # with --disable=gems
+ assert_separately(["-", filename], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ filename = ARGV[0]
+ path = Struct.new(:to_path).new(filename)
+ error = assert_raise(LoadError) do
+ require path
+ end
+ assert_equal filename, error.path
+ end;
+ }
end
def test_require_invalid_shared_object
@@ -52,7 +68,8 @@ class TestRequire < Test::Unit::TestCase
def test_require_nonascii
bug3758 = '[ruby-core:31915]'
["\u{221e}", "\x82\xa0".force_encoding("cp932")].each do |path|
- assert_raise_with_message(LoadError, /#{path}\z/, bug3758) {require path}
+ e = assert_raise(LoadError, bug3758) {require path}
+ assert_operator(e.message, :end_with?, path, bug3758)
end
end
@@ -192,7 +209,7 @@ class TestRequire < Test::Unit::TestCase
File.write(req, "p :ok\n")
assert_file.exist?(req)
req[/.rb$/i] = ""
- assert_in_out_err(['--disable-gems'], <<-INPUT, %w(:ok), [])
+ assert_in_out_err([], <<-INPUT, %w(:ok), [])
require "#{req}"
require "#{req}"
INPUT
@@ -353,6 +370,26 @@ class TestRequire < Test::Unit::TestCase
end
end
+ def test_public_in_wrapped_load
+ Tempfile.create(["test_public_in_wrapped_load", ".rb"]) do |t|
+ t.puts "def foo; end", "public :foo"
+ t.close
+ assert_warning(/main\.public/) do
+ assert load(t.path, true)
+ end
+ end
+ end
+
+ def test_private_in_wrapped_load
+ Tempfile.create(["test_private_in_wrapped_load", ".rb"]) do |t|
+ t.puts "def foo; end", "private :foo"
+ t.close
+ assert_warning(/main\.private/) do
+ assert load(t.path, true)
+ end
+ end
+ end
+
def test_load_scope
bug1982 = '[ruby-core:25039] [Bug #1982]'
Tempfile.create(["test_ruby_test_require", ".rb"]) {|t|
@@ -680,7 +717,7 @@ class TestRequire < Test::Unit::TestCase
Dir.mktmpdir {|tmp|
Dir.chdir(tmp) {
open("foo.rb", "w") {}
- assert_in_out_err([{"RUBYOPT"=>nil}, '--disable-gems'], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7158)
+ assert_in_out_err([{"RUBYOPT"=>nil}], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7158)
begin;
$:.replace([IO::NULL])
a = Object.new
@@ -708,7 +745,7 @@ class TestRequire < Test::Unit::TestCase
Dir.mktmpdir {|tmp|
Dir.chdir(tmp) {
open("foo.rb", "w") {}
- assert_in_out_err([{"RUBYOPT"=>nil}, '--disable-gems'], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7158)
+ assert_in_out_err([{"RUBYOPT"=>nil}], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7158)
begin;
$:.replace([IO::NULL])
a = Object.new
@@ -738,7 +775,7 @@ class TestRequire < Test::Unit::TestCase
open("foo.rb", "w") {}
Dir.mkdir("a")
open(File.join("a", "bar.rb"), "w") {}
- assert_in_out_err(['--disable-gems'], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7383)
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7383)
begin;
$:.replace([IO::NULL])
$:.#{add} "#{tmp}"
@@ -959,4 +996,19 @@ class TestRequire < Test::Unit::TestCase
assert_nil($LOAD_PATH.resolve_feature_path("superkalifragilisticoespialidoso"))
end
end
+
+ def test_require_with_public_method_missing
+ # [Bug #19793]
+ assert_separately(["-W0", "-rtempfile"], __FILE__, __LINE__, <<~RUBY, timeout: 60)
+ GC.stress = true
+
+ class Object
+ public :method_missing
+ end
+
+ Tempfile.create(["empty", ".rb"]) do |file|
+ require file.path
+ end
+ RUBY
+ end
end
diff --git a/test/ruby/test_require_lib.rb b/test/ruby/test_require_lib.rb
index 95fa3f29e1..a88279727e 100644
--- a/test/ruby/test_require_lib.rb
+++ b/test/ruby/test_require_lib.rb
@@ -1,25 +1,26 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
class TestRequireLib < Test::Unit::TestCase
- TEST_RATIO = ENV["TEST_REQUIRE_THREAD_RATIO"]&.tap {|s|break s.to_f} || 0.05 # testing all files needs too long time...
+ libdir = __dir__ + '/../../lib'
- Dir.glob(File.expand_path('../../lib/**/*.rb', __dir__)).each do |lib|
- # skip some problems
- next if %r!/lib/(?:bundler|rubygems)\b! =~ lib
- next if %r!/lib/(?:debug|mkmf)\.rb\z! =~ lib
- next if %r!/lib/irb/ext/tracer\.rb\z! =~ lib
- # skip many files that almost use no threads
- next if TEST_RATIO < rand(0.0..1.0)
+ # .rb files at lib
+ scripts = Dir.glob('*.rb', base: libdir).map {|f| f.chomp('.rb')}
+
+ # .rb files in subdirectories of lib without same name script
+ dirs = Dir.glob('*/', base: libdir).map {|d| d.chomp('/')}
+ dirs -= scripts
+ scripts.concat(Dir.glob(dirs.map {|d| d + '/*.rb'}, base: libdir).map {|f| f.chomp('.rb')})
+
+ # skip some problems
+ scripts -= %w[bundler bundled_gems rubygems mkmf]
+
+ scripts.each do |lib|
define_method "test_thread_size:#{lib}" do
- assert_separately(['--disable-gems', '-W0'], "#{<<~"begin;"}\n#{<<~"end;"}")
+ assert_separately(['-W0'], "#{<<~"begin;"}\n#{<<~"end;"}")
begin;
n = Thread.list.size
- begin
- require #{lib.dump}
- rescue Exception
- omit $!
- end
+ require #{lib.dump}
assert_equal n, Thread.list.size
end;
end
diff --git a/test/ruby/test_rubyoptions.rb b/test/ruby/test_rubyoptions.rb
index 4c2dc31240..76be9152a7 100644
--- a/test/ruby/test_rubyoptions.rb
+++ b/test/ruby/test_rubyoptions.rb
@@ -8,7 +8,12 @@ require_relative '../lib/jit_support'
class TestRubyOptions < Test::Unit::TestCase
def self.rjit_enabled? = defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?
- def self.yjit_enabled? = defined?(RubyVM::YJIT.enabled?) && RubyVM::YJIT.enabled?
+ def self.yjit_enabled? = defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled?
+
+ # Here we're defining our own RUBY_DESCRIPTION without "+PRISM". We do this
+ # here so that the various tests that reference RUBY_DESCRIPTION don't have to
+ # worry about it. The flag itself is tested in its own test.
+ RUBY_DESCRIPTION = ::RUBY_DESCRIPTION.sub(/\+PRISM /, '')
NO_JIT_DESCRIPTION =
if rjit_enabled?
@@ -41,7 +46,7 @@ class TestRubyOptions < Test::Unit::TestCase
def test_usage
assert_in_out_err(%w(-h)) do |r, e|
assert_operator(r.size, :<=, 25)
- longer = r[1..-1].select {|x| x.size > 80}
+ longer = r[1..-1].select {|x| x.size >= 80}
assert_equal([], longer)
assert_equal([], e)
end
@@ -74,7 +79,7 @@ class TestRubyOptions < Test::Unit::TestCase
def test_backtrace_limit
assert_in_out_err(%w(--backtrace-limit), "", [], /missing argument for --backtrace-limit/)
assert_in_out_err(%w(--backtrace-limit= 1), "", [], /missing argument for --backtrace-limit/)
- assert_in_out_err(%w(--backtrace-limit=-1), "", [], /wrong limit for backtrace length/)
+ assert_in_out_err(%w(--backtrace-limit=-2), "", [], /wrong limit for backtrace length/)
code = 'def f(n);n > 0 ? f(n-1) : raise;end;f(5)'
assert_in_out_err(%w(--backtrace-limit=1), code, [],
[/.*unhandled exception\n/, /^\tfrom .*\n/,
@@ -84,51 +89,71 @@ class TestRubyOptions < Test::Unit::TestCase
/^\t \.{3} \d+ levels\.{3}\n/])
assert_kind_of(Integer, Thread::Backtrace.limit)
assert_in_out_err(%w(--backtrace-limit=1), "p Thread::Backtrace.limit", ['1'], [])
+ assert_in_out_err(%w(--backtrace-limit 1), "p Thread::Backtrace.limit", ['1'], [])
+ env = {"RUBYOPT" => "--backtrace-limit=5"}
+ assert_in_out_err([env], "p Thread::Backtrace.limit", ['5'], [])
+ assert_in_out_err([env, "--backtrace-limit=1"], "p Thread::Backtrace.limit", ['1'], [])
+ assert_in_out_err([env, "--backtrace-limit=-1"], "p Thread::Backtrace.limit", ['-1'], [])
+ assert_in_out_err([env, "--backtrace-limit=3", "--backtrace-limit=1"],
+ "p Thread::Backtrace.limit", ['1'], [])
+ assert_in_out_err([{"RUBYOPT" => "--backtrace-limit=5 --backtrace-limit=3"}],
+ "p Thread::Backtrace.limit", ['3'], [])
+ long_max = RbConfig::LIMITS["LONG_MAX"]
+ assert_in_out_err(%W(--backtrace-limit=#{long_max}), "p Thread::Backtrace.limit",
+ ["#{long_max}"], [])
end
def test_warning
- save_rubyopt = ENV['RUBYOPT']
- ENV['RUBYOPT'] = nil
+ save_rubyopt = ENV.delete('RUBYOPT')
assert_in_out_err(%w(-W0 -e) + ['p $-W'], "", %w(0), [])
assert_in_out_err(%w(-W1 -e) + ['p $-W'], "", %w(1), [])
assert_in_out_err(%w(-Wx -e) + ['p $-W'], "", %w(2), [])
assert_in_out_err(%w(-W -e) + ['p $-W'], "", %w(2), [])
assert_in_out_err(%w(-We) + ['p $-W'], "", %w(2), [])
assert_in_out_err(%w(-w -W0 -e) + ['p $-W'], "", %w(0), [])
- assert_in_out_err(%w(-W:deprecated -e) + ['p Warning[:deprecated]'], "", %w(true), [])
- assert_in_out_err(%w(-W:no-deprecated -e) + ['p Warning[:deprecated]'], "", %w(false), [])
- assert_in_out_err(%w(-W:experimental -e) + ['p Warning[:experimental]'], "", %w(true), [])
- assert_in_out_err(%w(-W:no-experimental -e) + ['p Warning[:experimental]'], "", %w(false), [])
- assert_in_out_err(%w(-W -e) + ['p Warning[:performance]'], "", %w(false), [])
- assert_in_out_err(%w(-W:performance -e) + ['p Warning[:performance]'], "", %w(true), [])
- assert_in_out_err(%w(-W:qux), "", [], /unknown warning category: `qux'/)
- assert_in_out_err(%w(-w -e) + ['p Warning[:deprecated]'], "", %w(true), [])
- assert_in_out_err(%w(-W -e) + ['p Warning[:deprecated]'], "", %w(true), [])
- assert_in_out_err(%w(-We) + ['p Warning[:deprecated]'], "", %w(true), [])
- assert_in_out_err(%w(-e) + ['p Warning[:deprecated]'], "", %w(false), [])
- assert_in_out_err(%w(-w -e) + ['p Warning[:performance]'], "", %w(false), [])
- assert_in_out_err(%w(-W -e) + ['p Warning[:performance]'], "", %w(false), [])
- code = 'puts "#{$VERBOSE}:#{Warning[:deprecated]}:#{Warning[:experimental]}:#{Warning[:performance]}"'
+
+ categories = {deprecated: 1, experimental: 0, performance: 2}
+ assert_equal categories.keys.sort, Warning.categories.sort
+
+ categories.each do |category, level|
+ assert_in_out_err(["-W:#{category}", "-e", "p Warning[:#{category}]"], "", %w(true), [])
+ assert_in_out_err(["-W:no-#{category}", "-e", "p Warning[:#{category}]"], "", %w(false), [])
+ assert_in_out_err(["-e", "p Warning[:#{category}]"], "", level > 0 ? %w(false) : %w(true), [])
+ assert_in_out_err(["-w", "-e", "p Warning[:#{category}]"], "", level > 1 ? %w(false) : %w(true), [])
+ assert_in_out_err(["-W", "-e", "p Warning[:#{category}]"], "", level > 1 ? %w(false) : %w(true), [])
+ assert_in_out_err(["-We", "p Warning[:#{category}]"], "", level > 1 ? %w(false) : %w(true), [])
+ end
+ assert_in_out_err(%w(-W:qux), "", [], /unknown warning category: 'qux'/)
+
+ def categories.expected(lev = 1, **warnings)
+ [
+ (lev > 1).to_s,
+ *map {|category, level| warnings.fetch(category, lev > level).to_s}
+ ].join(':')
+ end
+ code = ['#{$VERBOSE}', *categories.map {|category, | "\#{Warning[:#{category}]}"}].join(':')
+ code = %[puts "#{code}"]
Tempfile.create(["test_ruby_test_rubyoption", ".rb"]) do |t|
t.puts code
t.close
- assert_in_out_err(["-r#{t.path}", '-e', code], "", %w(false:false:true:false false:false:true:false), [])
- assert_in_out_err(["-r#{t.path}", '-w', '-e', code], "", %w(true:true:true:false true:true:true:false), [])
- assert_in_out_err(["-r#{t.path}", '-W:deprecated', '-e', code], "", %w(false:true:true:false false:true:true:false), [])
- assert_in_out_err(["-r#{t.path}", '-W:no-experimental', '-e', code], "", %w(false:false:false:false false:false:false:false), [])
- assert_in_out_err(["-r#{t.path}", '-W:performance', '-e', code], "", %w(false:false:true:true false:false:true:true), [])
+ assert_in_out_err(["-r#{t.path}", '-e', code], "", [categories.expected(1)]*2, [])
+ assert_in_out_err(["-r#{t.path}", '-w', '-e', code], "", [categories.expected(2)]*2, [])
+ categories.each do |category, |
+ assert_in_out_err(["-r#{t.path}", "-W:#{category}", '-e', code], "", [categories.expected(category => 'true')]*2, [])
+ assert_in_out_err(["-r#{t.path}", "-W:no-#{category}", '-e', code], "", [categories.expected(category => 'false')]*2, [])
+ end
end
ensure
ENV['RUBYOPT'] = save_rubyopt
end
def test_debug
- assert_in_out_err(["--disable-gems", "-de", "p $DEBUG"], "", %w(true), [])
+ assert_in_out_err(["-de", "p $DEBUG"], "", %w(true), [])
- assert_in_out_err(["--disable-gems", "--debug", "-e", "p $DEBUG"],
+ assert_in_out_err(["--debug", "-e", "p $DEBUG"],
"", %w(true), [])
- assert_in_out_err(["--disable-gems", "--debug-", "-e", "p $DEBUG"], "", %w(), /invalid option --debug-/)
+ assert_in_out_err(["--debug-", "-e", "p $DEBUG"], "", %w(), /invalid option --debug-/)
end
q = Regexp.method(:quote)
@@ -145,7 +170,7 @@ class TestRubyOptions < Test::Unit::TestCase
VERSION_PATTERN_WITH_RJIT =
case RUBY_ENGINE
when 'ruby'
- /^ruby #{q[RUBY_VERSION]}(?:[p ]|dev|rc).*? \+RJIT \[#{q[RUBY_PLATFORM]}\]$/
+ /^ruby #{q[RUBY_VERSION]}(?:[p ]|dev|rc).*? \+RJIT (\+MN )?\[#{q[RUBY_PLATFORM]}\]$/
else
VERSION_PATTERN
end
@@ -188,7 +213,7 @@ class TestRubyOptions < Test::Unit::TestCase
assert_in_out_err(%w(--enable=all --disable=rjit -e) + [""], "", [], [])
end
assert_in_out_err(%w(--enable foobarbazqux -e) + [""], "", [],
- /unknown argument for --enable: `foobarbazqux'/)
+ /unknown argument for --enable: 'foobarbazqux'/)
assert_in_out_err(%w(--enable), "", [], /missing argument for --enable/)
end
@@ -197,11 +222,11 @@ class TestRubyOptions < Test::Unit::TestCase
assert_in_out_err(%w(--disable-all -e) + [""], "", [], [])
assert_in_out_err(%w(--disable=all -e) + [""], "", [], [])
assert_in_out_err(%w(--disable foobarbazqux -e) + [""], "", [],
- /unknown argument for --disable: `foobarbazqux'/)
+ /unknown argument for --disable: 'foobarbazqux'/)
assert_in_out_err(%w(--disable), "", [], /missing argument for --disable/)
- assert_in_out_err(%w(--disable-gems -e) + ['p defined? Gem'], "", ["nil"], [])
+ assert_in_out_err(%w(-e) + ['p defined? Gem'], "", ["nil"], [])
assert_in_out_err(%w(--disable-did_you_mean -e) + ['p defined? DidYouMean'], "", ["nil"], [])
- assert_in_out_err(%w(--disable-gems -e) + ['p defined? DidYouMean'], "", ["nil"], [])
+ assert_in_out_err(%w(-e) + ['p defined? DidYouMean'], "", ["nil"], [])
end
def test_kanji
@@ -276,6 +301,21 @@ class TestRubyOptions < Test::Unit::TestCase
end
end
+ def test_parser_flag
+ warning = /compiler based on the Prism parser is currently experimental/
+
+ assert_in_out_err(%w(--parser=prism -e) + ["puts :hi"], "", %w(hi), warning)
+ assert_in_out_err(%w(--parser=prism -W:no-experimental -e) + ["puts :hi"], "", %w(hi), [])
+ assert_in_out_err(%w(--parser=prism -W:no-experimental --dump=parsetree -e _=:hi), "", /"hi"/, [])
+
+ assert_in_out_err(%w(--parser=parse.y -e) + ["puts :hi"], "", %w(hi), [])
+ assert_norun_with_rflag('--parser=parse.y', '--version', "")
+
+ assert_in_out_err(%w(--parser=notreal -e) + ["puts :hi"], "", [], /unknown parser notreal/)
+
+ assert_in_out_err(%w(--parser=prism --version), "", /\+PRISM/, [])
+ end
+
def test_eval
assert_in_out_err(%w(-e), "", [], /no code specified for -e \(RuntimeError\)/)
end
@@ -342,22 +382,32 @@ class TestRubyOptions < Test::Unit::TestCase
assert_in_out_err(%w(--encoding test_ruby_test_rubyoptions_foobarbazqux), "", [],
/unknown encoding name - test_ruby_test_rubyoptions_foobarbazqux \(RuntimeError\)/)
- if /mswin|mingw|aix|android/ =~ RUBY_PLATFORM &&
- (str = "\u3042".force_encoding(Encoding.find("external"))).valid_encoding?
- # This result depends on locale because LANG=C doesn't affect locale
- # on Windows.
- # On AIX, the source encoding of stdin with LANG=C is ISO-8859-1,
- # which allows \u3042.
- out, err = [str], []
- else
- out, err = [], /invalid multibyte char/
- end
- assert_in_out_err(%w(-Eutf-8), "puts '\u3042'", out, err)
- assert_in_out_err(%w(--encoding utf-8), "puts '\u3042'", out, err)
+ assert_in_out_err(%w(-Eutf-8), 'puts Encoding::default_external', ["UTF-8"])
+ assert_in_out_err(%w(-Ecesu-8), 'puts Encoding::default_external', ["CESU-8"])
+ assert_in_out_err(%w(--encoding utf-8), 'puts Encoding::default_external', ["UTF-8"])
+ assert_in_out_err(%w(--encoding cesu-8), 'puts Encoding::default_external', ["CESU-8"])
end
def test_syntax_check
- assert_in_out_err(%w(-c -e a=1+1 -e !a), "", ["Syntax OK"], [])
+ assert_in_out_err(%w(-cw -e a=1+1 -e !a), "", ["Syntax OK"], [])
+ assert_in_out_err(%w(-cw -e break), "", [], ["-e:1: Invalid break", :*])
+ assert_in_out_err(%w(-cw -e next), "", [], ["-e:1: Invalid next", :*])
+ assert_in_out_err(%w(-cw -e redo), "", [], ["-e:1: Invalid redo", :*])
+ assert_in_out_err(%w(-cw -e retry), "", [], ["-e:1: Invalid retry", :*])
+ assert_in_out_err(%w(-cw -e yield), "", [], ["-e:1: Invalid yield", :*])
+ assert_in_out_err(%w(-cw -e begin -e break -e end), "", [], ["-e:2: Invalid break", :*])
+ assert_in_out_err(%w(-cw -e begin -e next -e end), "", [], ["-e:2: Invalid next", :*])
+ assert_in_out_err(%w(-cw -e begin -e redo -e end), "", [], ["-e:2: Invalid redo", :*])
+ assert_in_out_err(%w(-cw -e begin -e retry -e end), "", [], ["-e:2: Invalid retry", :*])
+ assert_in_out_err(%w(-cw -e begin -e yield -e end), "", [], ["-e:2: Invalid yield", :*])
+ assert_in_out_err(%w(-cw -e !defined?(break)), "", ["Syntax OK"], [])
+ assert_in_out_err(%w(-cw -e !defined?(next)), "", ["Syntax OK"], [])
+ assert_in_out_err(%w(-cw -e !defined?(redo)), "", ["Syntax OK"], [])
+ assert_in_out_err(%w(-cw -e !defined?(retry)), "", ["Syntax OK"], [])
+ assert_in_out_err(%w(-cw -e !defined?(yield)), "", ["Syntax OK"], [])
+ assert_in_out_err(%w(-n -cw -e break), "", ["Syntax OK"], [])
+ assert_in_out_err(%w(-n -cw -e next), "", ["Syntax OK"], [])
+ assert_in_out_err(%w(-n -cw -e redo), "", ["Syntax OK"], [])
end
def test_invalid_option
@@ -404,13 +454,12 @@ class TestRubyOptions < Test::Unit::TestCase
ENV['RUBYOPT'] = '-W:no-experimental'
assert_in_out_err(%w(), "p Warning[:experimental]", ["false"])
ENV['RUBYOPT'] = '-W:qux'
- assert_in_out_err(%w(), "", [], /unknown warning category: `qux'/)
+ assert_in_out_err(%w(), "", [], /unknown warning category: 'qux'/)
+
+ ENV['RUBYOPT'] = 'w'
+ assert_in_out_err(%w(), "p $VERBOSE", ["true"])
ensure
- if rubyopt_orig
- ENV['RUBYOPT'] = rubyopt_orig
- else
- ENV.delete('RUBYOPT')
- end
+ ENV['RUBYOPT'] = rubyopt_orig
end
def test_search
@@ -500,6 +549,16 @@ class TestRubyOptions < Test::Unit::TestCase
/invalid name for global variable - -# \(NameError\)/)
end
+ def test_option_missing_argument
+ assert_in_out_err(%w(-0 --enable), "", [], /missing argument for --enable/)
+ assert_in_out_err(%w(-0 --disable), "", [], /missing argument for --disable/)
+ assert_in_out_err(%w(-0 --dump), "", [], /missing argument for --dump/)
+ assert_in_out_err(%w(-0 --encoding), "", [], /missing argument for --encoding/)
+ assert_in_out_err(%w(-0 --external-encoding), "", [], /missing argument for --external-encoding/)
+ assert_in_out_err(%w(-0 --internal-encoding), "", [], /missing argument for --internal-encoding/)
+ assert_in_out_err(%w(-0 --backtrace-limit), "", [], /missing argument for --backtrace-limit/)
+ end
+
def test_assignment_in_conditional
Tempfile.create(["test_ruby_test_rubyoption", ".rb"]) {|t|
t.puts "if a = 1"
@@ -510,7 +569,7 @@ class TestRubyOptions < Test::Unit::TestCase
t.puts " end"
t.puts "end"
t.flush
- warning = ' warning: found `= literal\' in conditional, should be =='
+ warning = ' warning: found \'= literal\' in conditional, should be =='
err = ["#{t.path}:1:#{warning}",
"#{t.path}:4:#{warning}",
]
@@ -527,16 +586,29 @@ class TestRubyOptions < Test::Unit::TestCase
t.puts "if a = {}; end"
t.puts "if a = {1=>2}; end"
t.puts "if a = {3=>a}; end"
+ t.puts "if a = :sym; end"
t.flush
err = ["#{t.path}:1:#{warning}",
"#{t.path}:2:#{warning}",
"#{t.path}:3:#{warning}",
"#{t.path}:5:#{warning}",
"#{t.path}:6:#{warning}",
+ "#{t.path}:8:#{warning}",
]
feature4299 = '[ruby-dev:43083]'
assert_in_out_err(["-w", t.path], "", [], err, feature4299)
assert_in_out_err(["-wr", t.path, "-e", ""], "", [], err, feature4299)
+
+ t.rewind
+ t.truncate(0)
+ t.puts "if a = __LINE__; end"
+ t.puts "if a = __FILE__; end"
+ t.flush
+ err = ["#{t.path}:1:#{warning}",
+ "#{t.path}:2:#{warning}",
+ ]
+ assert_in_out_err(["-w", t.path], "", [], err)
+ assert_in_out_err(["-wr", t.path, "-e", ""], "", [], err)
}
end
@@ -759,8 +831,8 @@ class TestRubyOptions < Test::Unit::TestCase
%r(
(?:
--\sRuby\slevel\sbacktrace\sinformation\s----------------------------------------\n
- (?:-e:1:in\s\`(?:block\sin\s)?<main>\'\n)*
- -e:1:in\s\`kill\'\n
+ (?:-e:1:in\s\'(?:block\sin\s)?<main>\'\n)*
+ -e:1:in\s\'kill\'\n
\n
)?
)x,
@@ -783,30 +855,36 @@ class TestRubyOptions < Test::Unit::TestCase
)?
)x,
]
- end
- def assert_segv(args, message=nil)
- omit if ENV['RUBY_ON_BUG']
+ KILL_SELF = "Process.kill :SEGV, $$"
+ end
+ def assert_segv(args, message=nil, list: SEGVTest::ExpectedStderrList, **opt, &block)
# We want YJIT to be enabled in the subprocess if it's enabled for us
# so that the Ruby description matches.
+ env = Hash === args.first ? args.shift : {}
args.unshift("--yjit") if self.class.yjit_enabled?
+ env.update({'RUBY_ON_BUG' => nil})
+ # ASAN registers a segv handler which prints out "AddressSanitizer: DEADLYSIGNAL" when
+ # catching sigsegv; we don't expect that output, so suppress it.
+ env.update({'ASAN_OPTIONS' => 'handle_segv=0'})
+ args.unshift(env)
test_stdin = ""
- opt = SEGVTest::ExecOptions.dup
- list = SEGVTest::ExpectedStderrList
+ tests = [//, list] unless block
- assert_in_out_err(args, test_stdin, //, list, encoding: "ASCII-8BIT", **opt)
+ assert_in_out_err(args, test_stdin, *tests, encoding: "ASCII-8BIT",
+ **SEGVTest::ExecOptions, **opt, &block)
end
def test_segv_test
- assert_segv(["--disable-gems", "-e", "Process.kill :SEGV, $$"])
+ assert_segv(["--disable-gems", "-e", SEGVTest::KILL_SELF])
end
def test_segv_loaded_features
bug7402 = '[ruby-core:49573]'
- status = assert_segv(['-e', 'END {Process.kill :SEGV, $$}',
+ status = assert_segv(['-e', "END {#{SEGVTest::KILL_SELF}}",
'-e', 'class Bogus; def to_str; exit true; end; end',
'-e', '$".clear',
'-e', '$".unshift Bogus.new',
@@ -820,10 +898,64 @@ class TestRubyOptions < Test::Unit::TestCase
Tempfile.create(["test_ruby_test_bug7597", ".rb"]) {|t|
t.write "f" * 100
t.flush
- assert_segv(["--disable-gems", "-e", "$0=ARGV[0]; Process.kill :SEGV, $$", t.path], bug7597)
+ assert_segv(["--disable-gems", "-e", "$0=ARGV[0]; #{SEGVTest::KILL_SELF}", t.path], bug7597)
}
end
+ def assert_crash_report(path, cmd = nil, &block)
+ Dir.mktmpdir("ruby_crash_report") do |dir|
+ list = SEGVTest::ExpectedStderrList
+ if cmd
+ FileUtils.mkpath(File.join(dir, File.dirname(cmd)))
+ File.write(File.join(dir, cmd), SEGVTest::KILL_SELF+"\n")
+ c = Regexp.quote(cmd)
+ list = list.map {|re| Regexp.new(re.source.gsub(/^\s*(\(\?:)?\K-e(?=:)/) {c}, re.options)}
+ else
+ cmd = ['-e', SEGVTest::KILL_SELF]
+ end
+ status = assert_segv([{"RUBY_CRASH_REPORT"=>path}, *cmd], list: [], chdir: dir, &block)
+ next if block
+ reports = Dir.glob("*.log", File::FNM_DOTMATCH, base: dir)
+ assert_equal(1, reports.size)
+ assert_pattern_list(list, File.read(File.join(dir, reports.first)))
+ break status, reports.first
+ end
+ end
+
+ def test_crash_report
+ status, report = assert_crash_report("%e.%f.%p.log")
+ assert_equal("#{File.basename(EnvUtil.rubybin)}.-e.#{status.pid}.log", report)
+ end
+
+ def test_crash_report_script
+ status, report = assert_crash_report("%e.%f.%p.log", "bug.rb")
+ assert_equal("#{File.basename(EnvUtil.rubybin)}.bug.rb.#{status.pid}.log", report)
+ end
+
+ def test_crash_report_executable_path
+ omit if EnvUtil.rubybin.size > 245
+ status, report = assert_crash_report("%E.%p.log")
+ path = EnvUtil.rubybin.sub(/\A\w\K:[\/\\]/, '!').tr_s('/', '!')
+ assert_equal("#{path}.#{status.pid}.log", report)
+ end
+
+ def test_crash_report_script_path
+ status, report = assert_crash_report("%F.%p.log", "test/bug.rb")
+ assert_equal("test!bug.rb.#{status.pid}.log", report)
+ end
+
+ def test_crash_report_pipe
+ if File.executable?(echo = "/bin/echo")
+ elsif /mswin|ming/ =~ RUBY_PLATFORM
+ echo = "echo"
+ else
+ omit "/bin/echo not found"
+ end
+ assert_crash_report("| #{echo} %e:%f:%p") do |stdin, stdout, status|
+ assert_equal(["#{File.basename(EnvUtil.rubybin)}:-e:#{status.pid}"], stdin)
+ end
+ end
+
def test_DATA
Tempfile.create(["test_ruby_test_rubyoption", ".rb"]) {|t|
t.puts "puts DATA.read.inspect"
@@ -1006,17 +1138,17 @@ class TestRubyOptions < Test::Unit::TestCase
assert_in_out_err(['-p', '-e', 'sub(/t.*/){"TEST"}'], %[test], %w[TEST], [], bug7157)
end
- def assert_norun_with_rflag(*opt)
+ def assert_norun_with_rflag(*opt, test_stderr: [])
bug10435 = "[ruby-dev:48712] [Bug #10435]: should not run with #{opt} option"
stderr = []
Tempfile.create(%w"bug10435- .rb") do |script|
dir, base = File.split(script.path)
File.write(script, "abort ':run'\n")
opts = ['-C', dir, '-r', "./#{base}", *opt]
- _, e = assert_in_out_err([*opts, '-ep'], "", //)
+ _, e = assert_in_out_err([*opts, '-ep'], "", //, test_stderr)
stderr.concat(e) if e
stderr << "---"
- _, e = assert_in_out_err([*opts, base], "", //)
+ _, e = assert_in_out_err([*opts, base], "", //, test_stderr)
stderr.concat(e) if e
end
assert_not_include(stderr, ":run", bug10435)
@@ -1039,6 +1171,15 @@ class TestRubyOptions < Test::Unit::TestCase
assert_norun_with_rflag('--dump=parse+error_tolerant')
end
+ def test_dump_parsetree_error_tolerant
+ assert_in_out_err(['--dump=parse', '-e', 'begin'],
+ "", [], /unexpected end-of-input/, success: false)
+ assert_in_out_err(['--dump=parse', '--dump=+error_tolerant', '-e', 'begin'],
+ "", /^# @/, /unexpected end-of-input/, success: true)
+ assert_in_out_err(['--dump=+error_tolerant', '-e', 'begin p :run'],
+ "", [], /unexpected end-of-input/, success: false)
+ end
+
def test_dump_insns_with_rflag
assert_norun_with_rflag('--dump=insns')
end
@@ -1067,13 +1208,16 @@ class TestRubyOptions < Test::Unit::TestCase
end
def test_frozen_string_literal_debug
+ default_frozen = eval("'test'").frozen?
+
with_debug_pat = /created at/
wo_debug_pat = /can\'t modify frozen String: "\w+" \(FrozenError\)\n\z/
frozen = [
["--enable-frozen-string-literal", true],
["--disable-frozen-string-literal", false],
- [nil, false],
]
+ frozen << [nil, false] unless default_frozen
+
debugs = [
["--debug-frozen-string-literal", true],
["--debug=frozen-string-literal", true],
@@ -1137,4 +1281,10 @@ class TestRubyOptions < Test::Unit::TestCase
omit "#{IO::NULL} is not a character device" unless File.chardev?(IO::NULL)
assert_in_out_err([IO::NULL], success: true)
end
+
+ def test_free_at_exit_env_var
+ env = {"RUBY_FREE_AT_EXIT"=>"1"}
+ assert_ruby_status([env, "-e;"])
+ assert_in_out_err([env, "-W"], "", [], /Free at exit is experimental and may be unstable/)
+ end
end
diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb
index a30c4309ad..1251f8879f 100644
--- a/test/ruby/test_settracefunc.rb
+++ b/test/ruby/test_settracefunc.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: false
require 'test/unit'
+EnvUtil.suppress_warning {require 'continuation'}
class TestSetTraceFunc < Test::Unit::TestCase
def setup
@@ -358,18 +359,18 @@ class TestSetTraceFunc < Test::Unit::TestCase
def test_thread_trace
events = {:set => [], :add => []}
+ name = "#{self.class}\##{__method__}"
prc = Proc.new { |event, file, lineno, mid, binding, klass|
- events[:set] << [event, lineno, mid, klass, :set]
+ events[:set] << [event, lineno, mid, klass, :set] if file == name
}
prc = prc # suppress warning
prc2 = Proc.new { |event, file, lineno, mid, binding, klass|
- events[:add] << [event, lineno, mid, klass, :add]
+ events[:add] << [event, lineno, mid, klass, :add] if file == name
}
prc2 = prc2 # suppress warning
th = Thread.new do
th = Thread.current
- name = "#{self.class}\##{__method__}"
eval <<-EOF.gsub(/^.*?: /, ""), nil, name
1: th.set_trace_func(prc)
2: th.add_trace_func(prc2)
@@ -504,7 +505,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
1: trace = TracePoint.trace(*trace_events){|tp| next if !target_thread?
2: events << [tp.event, tp.lineno, tp.path, _defined_class.(tp), tp.method_id, tp.self, tp.binding&.eval("_local_var"), _get_data.(tp)] if tp.path == 'xyzzy'
3: }
- 4: 1.times{|;_local_var| _local_var = :inner
+ 4: [1].reverse_each{|;_local_var| _local_var = :inner
5: tap{}
6: }
7: class XYZZY
@@ -531,10 +532,10 @@ class TestSetTraceFunc < Test::Unit::TestCase
answer_events = [
#
[:line, 4, 'xyzzy', self.class, method, self, :outer, :nothing],
- [:c_call, 4, 'xyzzy', Integer, :times, 1, nil, :nothing],
+ [:c_call, 4, 'xyzzy', Array, :reverse_each, [1], nil, :nothing],
[:line, 4, 'xyzzy', self.class, method, self, nil, :nothing],
[:line, 5, 'xyzzy', self.class, method, self, :inner, :nothing],
- [:c_return, 4, "xyzzy", Integer, :times, 1, nil, 1],
+ [:c_return, 4, "xyzzy", Array, :reverse_each, [1], nil, [1]],
[:line, 7, 'xyzzy', self.class, method, self, :outer, :nothing],
[:c_call, 7, "xyzzy", Module, :const_added, TestSetTraceFunc, nil, :nothing],
[:c_return, 7, "xyzzy", Module, :const_added, TestSetTraceFunc, nil, nil],
@@ -625,6 +626,19 @@ PREP
CODE
end
+ def test_tracepoint_bmethod_memory_leak
+ assert_no_memory_leak([], '', "#{<<~"begin;"}\n#{<<~'end;'}", "[Bug #20194]", rss: true)
+ obj = Object.new
+ obj.define_singleton_method(:foo) {}
+ bmethod = obj.method(:foo)
+ tp = TracePoint.new(:return) {}
+ begin;
+ 1_000_000.times do
+ tp.enable(target: bmethod) {}
+ end
+ end;
+ end
+
def trace_by_set_trace_func
events = []
trace = nil
@@ -639,7 +653,7 @@ CODE
1: set_trace_func(lambda{|event, file, line, id, binding, klass|
2: events << [event, line, file, klass, id, binding&.eval('self'), binding&.eval("_local_var")] if file == 'xyzzy'
3: })
- 4: 1.times{|;_local_var| _local_var = :inner
+ 4: [1].map{|;_local_var| _local_var = :inner
5: tap{}
6: }
7: class XYZZY
@@ -955,6 +969,55 @@ CODE
assert_equal(expected*2, events)
end
+ def test_tracepoint_struct
+ c = Struct.new(:x) do
+ alias y x
+ alias y= x=
+ end
+ obj = c.new
+
+ ar_meth = obj.method(:x)
+ aw_meth = obj.method(:x=)
+ aar_meth = obj.method(:y)
+ aaw_meth = obj.method(:y=)
+ events = []
+ trace = TracePoint.new(:c_call, :c_return){|tp|
+ next if !target_thread?
+ next if tp.path != __FILE__
+ next if tp.method_id == :call
+ case tp.event
+ when :c_call
+ assert_raise(RuntimeError) {tp.return_value}
+ events << [tp.event, tp.method_id, tp.callee_id]
+ when :c_return
+ events << [tp.event, tp.method_id, tp.callee_id, tp.return_value]
+ end
+ }
+ test_proc = proc do
+ obj.x = 1
+ obj.x
+ obj.y = 2
+ obj.y
+ aw_meth.call(1)
+ ar_meth.call
+ aaw_meth.call(2)
+ aar_meth.call
+ end
+ test_proc.call # populate call caches
+ trace.enable(&test_proc)
+ expected = [
+ [:c_call, :x=, :x=],
+ [:c_return, :x=, :x=, 1],
+ [:c_call, :x, :x],
+ [:c_return, :x, :x, 1],
+ [:c_call, :x=, :y=],
+ [:c_return, :x=, :y=, 2],
+ [:c_call, :x, :y],
+ [:c_return, :x, :y, 2],
+ ]
+ assert_equal(expected*2, events)
+ end
+
class XYZZYException < Exception; end
def method_test_tracepoint_raised_exception err
raise err
@@ -994,7 +1057,7 @@ CODE
/return/ =~ tp.event ? tp.return_value : nil
]
}.enable{
- 1.times{
+ [1].map{
3
}
method_for_test_tracepoint_block{
@@ -1004,10 +1067,10 @@ CODE
# pp events
# expected_events =
[[:b_call, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
- [:c_call, :times, Integer, Integer, nil],
+ [:c_call, :map, Array, Array, nil],
[:b_call, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
[:b_return, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, 3],
- [:c_return, :times, Integer, Integer, 1],
+ [:c_return, :map, Array, Array, [3]],
[:call, :method_for_test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
[:b_call, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
[:b_return, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, 4],
@@ -1061,9 +1124,9 @@ CODE
when :line
assert_match(/ in /, str)
when :call, :c_call
- assert_match(/call \`/, str) # #<TracePoint:c_call `inherited' ../trunk/test.rb:11>
+ assert_match(/call \'/, str) # #<TracePoint:c_call 'inherited' ../trunk/test.rb:11>
when :return, :c_return
- assert_match(/return \`/, str) # #<TracePoint:return `m' ../trunk/test.rb:3>
+ assert_match(/return \'/, str) # #<TracePoint:return 'm' ../trunk/test.rb:3>
when /thread/
assert_match(/\#<Thread:/, str) # #<TracePoint:thread_end of #<Thread:0x87076c0>>
else
@@ -1196,15 +1259,17 @@ CODE
end
}
assert_normal_exit src % %q{obj.zip({}) {}}, bug7774
- assert_normal_exit src % %q{
- require 'continuation'
- begin
- c = nil
- obj.sort_by {|x| callcc {|c2| c ||= c2 }; x }
- c.call
- rescue RuntimeError
- end
- }, bug7774
+ if respond_to?(:callcc)
+ assert_normal_exit src % %q{
+ require 'continuation'
+ begin
+ c = nil
+ obj.sort_by {|x| callcc {|c2| c ||= c2 }; x }
+ c.call
+ rescue RuntimeError
+ end
+ }, bug7774
+ end
# TracePoint
tp_b = nil
@@ -1310,7 +1375,7 @@ CODE
next if !target_thread?
events << tp.event
}.enable{
- 1.times{
+ [1].map{
3
}
method_for_test_tracepoint_block{
@@ -1332,7 +1397,7 @@ CODE
next if !target_thread?
events << tp.event
}.enable{
- 1.times{
+ [1].map{
3
}
method_for_test_tracepoint_block{
@@ -1923,7 +1988,11 @@ CODE
def tp_return_value mid
ary = []
- TracePoint.new(:return, :b_return){|tp| next if !target_thread?; ary << [tp.event, tp.method_id, tp.return_value]}.enable{
+ TracePoint.new(:return, :b_return){|tp|
+ next if !target_thread?
+ next if tp.path != __FILE__
+ ary << [tp.event, tp.method_id, tp.return_value]
+ }.enable{
send mid
}
ary.pop # last b_return event is not required.
@@ -2132,7 +2201,7 @@ CODE
q = Thread::Queue.new
t = Thread.new{
Thread.current.add_trace_func proc{|ev, file, line, *args|
- events << [ev, line]
+ events << [ev, line] if file == __FILE__
} # do not stop trace. They will be stopped at Thread termination.
q.push 1
_x = 1
@@ -2406,6 +2475,18 @@ CODE
assert_equal [:tp1, 1, 2, :tp2, 3], events
end
+ def test_multiple_enable
+ ary = []
+ trace = TracePoint.new(:call) do |tp|
+ ary << tp.method_id
+ end
+ trace.enable
+ trace.enable
+ foo
+ trace.disable
+ assert_equal(1, ary.count(:foo), '[Bug #19114]')
+ end
+
def test_multiple_tracepoints_same_bmethod
events = []
tp1 = TracePoint.new(:return) do |tp|
@@ -2725,4 +2806,123 @@ CODE
Foo.foo
RUBY
end
+
+ def helper_cant_rescue
+ begin
+ raise SyntaxError
+ rescue
+ cant_rescue
+ end
+ end
+
+ def test_tp_rescue
+ lines = []
+ TracePoint.new(:line){|tp|
+ next unless target_thread?
+ lines << tp.lineno
+ }.enable{
+ begin
+ helper_cant_rescue
+ rescue SyntaxError
+ end
+ }
+ _call_line = lines.shift
+ _raise_line = lines.shift
+ assert_equal [], lines
+ end
+
+ def helper_can_rescue
+ begin
+ raise __LINE__.to_s
+ rescue SyntaxError
+ :ng
+ rescue
+ :ok
+ end
+ end
+
+ def helper_can_rescue_empty_body
+ begin
+ raise __LINE__.to_s
+ rescue SyntaxError
+ :ng
+ rescue
+ end
+ end
+
+ def test_tp_rescue_event
+ lines = []
+ TracePoint.new(:rescue){|tp|
+ next unless target_thread?
+ lines << [tp.lineno, tp.raised_exception]
+ }.enable{
+ helper_can_rescue
+ }
+
+ line, err, = lines.pop
+ assert_equal [], lines
+ assert err.kind_of?(RuntimeError)
+ assert_equal err.message.to_i + 4, line
+
+ lines = []
+ TracePoint.new(:rescue){|tp|
+ next unless target_thread?
+ lines << [tp.lineno, tp.raised_exception]
+ }.enable{
+ helper_can_rescue_empty_body
+ }
+
+ line, err, = lines.pop
+ assert_equal [], lines
+ assert err.kind_of?(RuntimeError)
+ assert_equal err.message.to_i + 3, line
+ end
+
+ def test_tracepoint_thread_begin
+ target_thread = nil
+
+ trace = TracePoint.new(:thread_begin) do |tp|
+ target_thread = tp.self
+ end
+
+ trace.enable(target_thread: nil) do
+ Thread.new{}.join
+ end
+
+ assert_kind_of(Thread, target_thread)
+ end
+
+ def test_tracepoint_thread_end
+ target_thread = nil
+
+ trace = TracePoint.new(:thread_end) do |tp|
+ target_thread = tp.self
+ end
+
+ trace.enable(target_thread: nil) do
+ Thread.new{}.join
+ end
+
+ assert_kind_of(Thread, target_thread)
+ end
+
+ def test_tracepoint_thread_end_with_exception
+ target_thread = nil
+
+ trace = TracePoint.new(:thread_end) do |tp|
+ target_thread = tp.self
+ end
+
+ trace.enable(target_thread: nil) do
+ thread = Thread.new do
+ Thread.current.report_on_exception = false
+ raise
+ end
+
+ # Ignore the exception raised by the thread:
+ thread.join rescue nil
+ end
+
+ assert_kind_of(Thread, target_thread)
+ end
end
diff --git a/test/ruby/test_shapes.rb b/test/ruby/test_shapes.rb
index ebc94a12d2..9b02504384 100644
--- a/test/ruby/test_shapes.rb
+++ b/test/ruby/test_shapes.rb
@@ -5,6 +5,8 @@ require 'json'
# These test the functionality of object shapes
class TestShapes < Test::Unit::TestCase
+ MANY_IVS = 80
+
class IVOrder
def expected_ivs
%w{ @a @b @c @d @e @f @g @h @i @j @k }
@@ -126,19 +128,25 @@ class TestShapes < Test::Unit::TestCase
end
def test_too_many_ivs_on_obj
- obj = Object.new
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ class Hi; end
- (RubyVM::Shape::SHAPE_MAX_NUM_IVS + 1).times do
- obj.instance_variable_set(:"@a#{_1}", 1)
- end
+ RubyVM::Shape.exhaust_shapes(2)
+
+ obj = Hi.new
+ obj.instance_variable_set(:@b, 1)
+ obj.instance_variable_set(:@c, 1)
+ obj.instance_variable_set(:@d, 1)
- assert_predicate RubyVM::Shape.of(obj), :too_complex?
+ assert_predicate RubyVM::Shape.of(obj), :too_complex?
+ end;
end
def test_too_many_ivs_on_class
obj = Class.new
- (RubyVM::Shape::SHAPE_MAX_NUM_IVS + 1).times do
+ (MANY_IVS + 1).times do
obj.instance_variable_set(:"@a#{_1}", 1)
end
@@ -148,10 +156,10 @@ class TestShapes < Test::Unit::TestCase
def test_removing_when_too_many_ivs_on_class
obj = Class.new
- (RubyVM::Shape::SHAPE_MAX_NUM_IVS + 2).times do
+ (MANY_IVS + 2).times do
obj.instance_variable_set(:"@a#{_1}", 1)
end
- (RubyVM::Shape::SHAPE_MAX_NUM_IVS + 2).times do
+ (MANY_IVS + 2).times do
obj.remove_instance_variable(:"@a#{_1}")
end
@@ -161,16 +169,416 @@ class TestShapes < Test::Unit::TestCase
def test_removing_when_too_many_ivs_on_module
obj = Module.new
- (RubyVM::Shape::SHAPE_MAX_NUM_IVS + 2).times do
+ (MANY_IVS + 2).times do
obj.instance_variable_set(:"@a#{_1}", 1)
end
- (RubyVM::Shape::SHAPE_MAX_NUM_IVS + 2).times do
+ (MANY_IVS + 2).times do
obj.remove_instance_variable(:"@a#{_1}")
end
assert_empty obj.instance_variables
end
+ def test_too_complex_geniv
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ class TooComplex < Hash
+ attr_reader :very_unique
+ end
+
+ RubyVM::Shape.exhaust_shapes
+
+ (RubyVM::Shape::SHAPE_MAX_VARIATIONS * 2).times do
+ TooComplex.new.instance_variable_set(:"@unique_#{_1}", 1)
+ end
+
+ tc = TooComplex.new
+ tc.instance_variable_set(:@very_unique, 3)
+ tc.instance_variable_set(:@very_unique2, 4)
+ assert_equal 3, tc.instance_variable_get(:@very_unique)
+ assert_equal 4, tc.instance_variable_get(:@very_unique2)
+
+ assert_equal [:@very_unique, :@very_unique2], tc.instance_variables
+ end;
+ end
+
+ def test_use_all_shapes_then_freeze
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ class Hi; end
+ RubyVM::Shape.exhaust_shapes(3)
+
+ obj = Hi.new
+ i = 0
+ while RubyVM::Shape.shapes_available > 0
+ obj.instance_variable_set(:"@b#{i}", 1)
+ i += 1
+ end
+ obj.freeze
+
+ assert obj.frozen?
+ end;
+ end
+
+ def test_run_out_of_shape_for_object
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ class A
+ def initialize
+ @a = 1
+ end
+ end
+ RubyVM::Shape.exhaust_shapes
+
+ A.new
+ end;
+ end
+
+ def test_run_out_of_shape_for_class_ivar
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ RubyVM::Shape.exhaust_shapes
+
+ c = Class.new
+ c.instance_variable_set(:@a, 1)
+ assert_equal(1, c.instance_variable_get(:@a))
+
+ c.remove_instance_variable(:@a)
+ assert_nil(c.instance_variable_get(:@a))
+
+ assert_raise(NameError) do
+ c.remove_instance_variable(:@a)
+ end
+ end;
+ end
+
+ def test_evacuate_class_ivar_and_compaction
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ count = 20
+
+ c = Class.new
+ count.times do |ivar|
+ c.instance_variable_set("@i#{ivar}", "ivar-#{ivar}")
+ end
+
+ RubyVM::Shape.exhaust_shapes
+
+ GC.auto_compact = true
+ GC.stress = true
+ # Cause evacuation
+ c.instance_variable_set(:@a, o = Object.new)
+ assert_equal(o, c.instance_variable_get(:@a))
+ GC.stress = false
+
+ count.times do |ivar|
+ assert_equal "ivar-#{ivar}", c.instance_variable_get("@i#{ivar}")
+ end
+ end;
+ end
+
+ def test_evacuate_generic_ivar_and_compaction
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ count = 20
+
+ c = Hash.new
+ count.times do |ivar|
+ c.instance_variable_set("@i#{ivar}", "ivar-#{ivar}")
+ end
+
+ RubyVM::Shape.exhaust_shapes
+
+ GC.auto_compact = true
+ GC.stress = true
+
+ # Cause evacuation
+ c.instance_variable_set(:@a, o = Object.new)
+ assert_equal(o, c.instance_variable_get(:@a))
+
+ GC.stress = false
+
+ count.times do |ivar|
+ assert_equal "ivar-#{ivar}", c.instance_variable_get("@i#{ivar}")
+ end
+ end;
+ end
+
+ def test_evacuate_object_ivar_and_compaction
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ count = 20
+
+ c = Object.new
+ count.times do |ivar|
+ c.instance_variable_set("@i#{ivar}", "ivar-#{ivar}")
+ end
+
+ RubyVM::Shape.exhaust_shapes
+
+ GC.auto_compact = true
+ GC.stress = true
+
+ # Cause evacuation
+ c.instance_variable_set(:@a, o = Object.new)
+ assert_equal(o, c.instance_variable_get(:@a))
+
+ GC.stress = false
+
+ count.times do |ivar|
+ assert_equal "ivar-#{ivar}", c.instance_variable_get("@i#{ivar}")
+ end
+ end;
+ end
+
+ def test_gc_stress_during_evacuate_generic_ivar
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ [].instance_variable_set(:@a, 1)
+
+ RubyVM::Shape.exhaust_shapes
+
+ ary = 10.times.map { [] }
+
+ GC.stress = true
+ ary.each do |o|
+ o.instance_variable_set(:@a, 1)
+ o.instance_variable_set(:@b, 1)
+ end
+ end;
+ end
+
+ def test_run_out_of_shape_for_module_ivar
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ RubyVM::Shape.exhaust_shapes
+
+ module Foo
+ @a = 1
+ @b = 2
+ assert_equal 1, @a
+ assert_equal 2, @b
+ end
+ end;
+ end
+
+ def test_run_out_of_shape_for_class_cvar
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ RubyVM::Shape.exhaust_shapes
+
+ c = Class.new
+
+ c.class_variable_set(:@@a, 1)
+ assert_equal(1, c.class_variable_get(:@@a))
+
+ c.class_eval { remove_class_variable(:@@a) }
+ assert_false(c.class_variable_defined?(:@@a))
+
+ assert_raise(NameError) do
+ c.class_eval { remove_class_variable(:@@a) }
+ end
+ end;
+ end
+
+ def test_run_out_of_shape_generic_instance_variable_set
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ class TooComplex < Hash
+ end
+
+ RubyVM::Shape.exhaust_shapes
+
+ tc = TooComplex.new
+ tc.instance_variable_set(:@a, 1)
+ tc.instance_variable_set(:@b, 2)
+
+ tc.remove_instance_variable(:@a)
+ assert_nil(tc.instance_variable_get(:@a))
+
+ assert_raise(NameError) do
+ tc.remove_instance_variable(:@a)
+ end
+ end;
+ end
+
+ def test_run_out_of_shape_generic_ivar_set
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ class Hi < String
+ def initialize
+ 8.times do |i|
+ instance_variable_set("@ivar_#{i}", i)
+ end
+ end
+
+ def transition
+ @hi_transition ||= 1
+ end
+ end
+
+ a = Hi.new
+
+ # Try to run out of shapes
+ RubyVM::Shape.exhaust_shapes
+
+ assert_equal 1, a.transition
+ assert_equal 1, a.transition
+ end;
+ end
+
+ def test_run_out_of_shape_instance_variable_defined
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ class A
+ attr_reader :a, :b, :c, :d
+ def initialize
+ @a = @b = @c = @d = 1
+ end
+ end
+
+ RubyVM::Shape.exhaust_shapes
+
+ a = A.new
+ assert_equal true, a.instance_variable_defined?(:@a)
+ end;
+ end
+
+ def test_run_out_of_shape_instance_variable_defined_on_module
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ RubyVM::Shape.exhaust_shapes
+
+ module A
+ @a = @b = @c = @d = 1
+ end
+
+ assert_equal true, A.instance_variable_defined?(:@a)
+ end;
+ end
+
+ def test_run_out_of_shape_during_remove_instance_variable
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ o = Object.new
+ 10.times { |i| o.instance_variable_set(:"@a#{i}", i) }
+
+ RubyVM::Shape.exhaust_shapes
+
+ o.remove_instance_variable(:@a0)
+ (1...10).each do |i|
+ assert_equal(i, o.instance_variable_get(:"@a#{i}"))
+ end
+ end;
+ end
+
+ def test_run_out_of_shape_remove_instance_variable
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ class A
+ attr_reader :a, :b, :c, :d
+ def initialize
+ @a = @b = @c = @d = 1
+ end
+ end
+
+ a = A.new
+
+ RubyVM::Shape.exhaust_shapes
+
+ a.remove_instance_variable(:@b)
+ assert_nil a.b
+
+ a.remove_instance_variable(:@a)
+ assert_nil a.a
+
+ a.remove_instance_variable(:@c)
+ assert_nil a.c
+
+ assert_equal 1, a.d
+ end;
+ end
+
+ def test_run_out_of_shape_rb_obj_copy_ivar
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ class A
+ def initialize
+ init # Avoid right sizing
+ end
+
+ def init
+ @a = @b = @c = @d = @e = @f = 1
+ end
+ end
+
+ a = A.new
+
+ RubyVM::Shape.exhaust_shapes
+
+ a.dup
+ end;
+ end
+
+ def test_evacuate_generic_ivar_memory_leak
+ assert_no_memory_leak([], "#{<<~'begin;'}", "#{<<~'end;'}", rss: true)
+ o = []
+ o.instance_variable_set(:@a, 1)
+
+ RubyVM::Shape.exhaust_shapes
+
+ ary = 1_000_000.times.map { [] }
+ begin;
+ ary.each do |o|
+ o.instance_variable_set(:@a, 1)
+ o.instance_variable_set(:@b, 1)
+ end
+ ary.clear
+ ary = nil
+ GC.start
+ end;
+ end
+
+ def test_use_all_shapes_module
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ class Hi; end
+
+ RubyVM::Shape.exhaust_shapes(2)
+
+ obj = Module.new
+ 3.times do
+ obj.instance_variable_set(:"@a#{_1}", _1)
+ end
+
+ ivs = 3.times.map do
+ obj.instance_variable_get(:"@a#{_1}")
+ end
+
+ assert_equal [0, 1, 2], ivs
+ end;
+ end
+
+ def test_complex_freeze_after_clone
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ class Hi; end
+
+ RubyVM::Shape.exhaust_shapes(2)
+
+ obj = Object.new
+ i = 0
+ while RubyVM::Shape.shapes_available > 0
+ obj.instance_variable_set(:"@b#{i}", i)
+ i += 1
+ end
+
+ v = obj.clone(freeze: true)
+ assert_predicate v, :frozen?
+ assert_equal 0, v.instance_variable_get(:@b0)
+ end;
+ end
+
def test_too_complex_ractor
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
begin;
@@ -214,6 +622,42 @@ class TestShapes < Test::Unit::TestCase
end;
end
+ def test_too_complex_obj_ivar_ractor_share
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ $VERBOSE = nil
+
+ RubyVM::Shape.exhaust_shapes
+
+ r = Ractor.new do
+ o = Object.new
+ o.instance_variable_set(:@a, "hello")
+ Ractor.yield(o)
+ end
+
+ o = r.take
+ assert_equal "hello", o.instance_variable_get(:@a)
+ end;
+ end
+
+ def test_too_complex_generic_ivar_ractor_share
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ $VERBOSE = nil
+
+ RubyVM::Shape.exhaust_shapes
+
+ r = Ractor.new do
+ o = []
+ o.instance_variable_set(:@a, "hello")
+ Ractor.yield(o)
+ end
+
+ o = r.take
+ assert_equal "hello", o.instance_variable_get(:@a)
+ end;
+ end
+
def test_read_iv_after_complex
ensure_complex
@@ -294,6 +738,84 @@ class TestShapes < Test::Unit::TestCase
assert_nil tc.a3
end
+ def test_remove_instance_variable
+ ivars_count = 5
+ object = Object.new
+ ivars_count.times do |i|
+ object.instance_variable_set("@ivar_#{i}", i)
+ end
+
+ ivars = ivars_count.times.map do |i|
+ object.instance_variable_get("@ivar_#{i}")
+ end
+ assert_equal [0, 1, 2, 3, 4], ivars
+
+ object.remove_instance_variable(:@ivar_2)
+
+ ivars = ivars_count.times.map do |i|
+ object.instance_variable_get("@ivar_#{i}")
+ end
+ assert_equal [0, 1, nil, 3, 4], ivars
+ end
+
+ def test_remove_instance_variable_when_out_of_shapes
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ ivars_count = 5
+ object = Object.new
+ ivars_count.times do |i|
+ object.instance_variable_set("@ivar_#{i}", i)
+ end
+
+ ivars = ivars_count.times.map do |i|
+ object.instance_variable_get("@ivar_#{i}")
+ end
+ assert_equal [0, 1, 2, 3, 4], ivars
+
+ RubyVM::Shape.exhaust_shapes
+
+ object.remove_instance_variable(:@ivar_2)
+
+ ivars = ivars_count.times.map do |i|
+ object.instance_variable_get("@ivar_#{i}")
+ end
+ assert_equal [0, 1, nil, 3, 4], ivars
+ end;
+ end
+
+ def test_remove_instance_variable_capacity_transition
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ t_object_shape = RubyVM::Shape.find_by_id(RubyVM::Shape::FIRST_T_OBJECT_SHAPE_ID)
+ assert_equal(RubyVM::Shape::SHAPE_T_OBJECT, t_object_shape.type)
+
+ initial_capacity = t_object_shape.capacity
+
+ # a does not transition in capacity
+ a = Class.new.new
+ initial_capacity.times do |i|
+ a.instance_variable_set(:"@ivar#{i + 1}", i)
+ end
+
+ # b transitions in capacity
+ b = Class.new.new
+ (initial_capacity + 1).times do |i|
+ b.instance_variable_set(:"@ivar#{i}", i)
+ end
+
+ assert_operator(RubyVM::Shape.of(a).capacity, :<, RubyVM::Shape.of(b).capacity)
+
+ # b will now have the same tree as a
+ b.remove_instance_variable(:@ivar0)
+
+ a.instance_variable_set(:@foo, 1)
+ a.instance_variable_set(:@bar, 1)
+
+ # Check that there is no heap corruption
+ GC.verify_internal_consistency
+ end;
+ end
+
def test_freeze_after_complex
ensure_complex
@@ -302,6 +824,8 @@ class TestShapes < Test::Unit::TestCase
assert_predicate RubyVM::Shape.of(tc), :too_complex?
tc.freeze
assert_raise(FrozenError) { tc.a3_m }
+ # doesn't transition to frozen shape in this case
+ assert_predicate RubyVM::Shape.of(tc), :too_complex?
end
def test_read_undefined_iv_after_complex
@@ -311,6 +835,7 @@ class TestShapes < Test::Unit::TestCase
tc.send("a#{RubyVM::Shape::SHAPE_MAX_VARIATIONS}_m")
assert_predicate RubyVM::Shape.of(tc), :too_complex?
assert_equal nil, tc.iv_not_defined
+ assert_predicate RubyVM::Shape.of(tc), :too_complex?
end
def test_shape_order
@@ -361,7 +886,10 @@ class TestShapes < Test::Unit::TestCase
class TestObject; end
def test_new_obj_has_t_object_shape
- assert_shape_equal(RubyVM::Shape.root_shape, RubyVM::Shape.of(TestObject.new).parent)
+ obj = TestObject.new
+ shape = RubyVM::Shape.of(obj)
+ assert_equal RubyVM::Shape::SHAPE_T_OBJECT, shape.type
+ assert_nil shape.parent
end
def test_str_has_root_shape
@@ -372,14 +900,6 @@ class TestShapes < Test::Unit::TestCase
assert_shape_equal(RubyVM::Shape.root_shape, RubyVM::Shape.of([]))
end
- def test_hash_has_correct_pool_shape
- omit "SHAPE_IN_BASIC_FLAGS == 0" unless RbConfig::SIZEOF["uint64_t"] <= RbConfig::SIZEOF["void*"]
-
- # All hashes are now allocated their own ar_table, so start in a
- # larger pool, and have already transitioned once.
- assert_shape_equal(RubyVM::Shape.root_shape, RubyVM::Shape.of({}).parent)
- end
-
def test_true_has_special_const_shape_id
assert_equal(RubyVM::Shape::SPECIAL_CONST_SHAPE_ID, RubyVM::Shape.of(true).id)
end
@@ -388,6 +908,10 @@ class TestShapes < Test::Unit::TestCase
assert_equal(RubyVM::Shape::SPECIAL_CONST_SHAPE_ID, RubyVM::Shape.of(nil).id)
end
+ def test_root_shape_transition_to_special_const_on_frozen
+ assert_equal(RubyVM::Shape::SPECIAL_CONST_SHAPE_ID, RubyVM::Shape.of([].freeze).id)
+ end
+
def test_basic_shape_transition
omit "Failing with RJIT for some reason" if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?
obj = Example.new
@@ -398,10 +922,9 @@ class TestShapes < Test::Unit::TestCase
shape = shape.parent
assert_equal RubyVM::Shape::SHAPE_T_OBJECT, shape.type
+ assert_nil shape.parent
- shape = shape.parent
- assert_equal(RubyVM::Shape.root_shape.id, shape.id)
- assert_equal(obj.instance_variable_get(:@a), 1)
+ assert_equal(1, obj.instance_variable_get(:@a))
end
def test_different_objects_make_same_transition
@@ -418,6 +941,19 @@ class TestShapes < Test::Unit::TestCase
assert_shape_equal(RubyVM::Shape.of(obj), RubyVM::Shape.of(obj2))
end
+ def test_duplicating_too_complex_objects_memory_leak
+ assert_no_memory_leak([], "#{<<~'begin;'}", "#{<<~'end;'}", "[Bug #20162]", rss: true)
+ RubyVM::Shape.exhaust_shapes
+
+ o = Object.new
+ o.instance_variable_set(:@a, 0)
+ begin;
+ 1_000_000.times do
+ o.dup
+ end
+ end;
+ end
+
def test_freezing_and_duplicating_object
obj = Object.new.freeze
obj2 = obj.dup
@@ -452,6 +988,15 @@ class TestShapes < Test::Unit::TestCase
assert_shape_equal(RubyVM::Shape.of(obj), RubyVM::Shape.of(obj2))
end
+ def test_cloning_with_freeze_option
+ obj = Object.new
+ obj2 = obj.clone(freeze: true)
+ assert_predicate(obj2, :frozen?)
+ refute_shape_equal(RubyVM::Shape.of(obj), RubyVM::Shape.of(obj2))
+ assert_equal(RubyVM::Shape::SHAPE_FROZEN, RubyVM::Shape.of(obj2).type)
+ assert_shape_equal(RubyVM::Shape.of(obj), RubyVM::Shape.of(obj2).parent)
+ end
+
def test_freezing_and_cloning_object_with_ivars
obj = Example.new.freeze
obj2 = obj.clone(freeze: true)
@@ -461,7 +1006,7 @@ class TestShapes < Test::Unit::TestCase
end
def test_freezing_and_cloning_string
- str = "str".freeze
+ str = ("str" + "str").freeze
str2 = str.clone(freeze: true)
assert_predicate(str2, :frozen?)
assert_shape_equal(RubyVM::Shape.of(str), RubyVM::Shape.of(str2))
diff --git a/test/ruby/test_stack.rb b/test/ruby/test_stack.rb
index 763aeb6bc2..8a78848322 100644
--- a/test/ruby/test_stack.rb
+++ b/test/ruby/test_stack.rb
@@ -18,7 +18,6 @@ class TestStack < Test::Unit::TestCase
env = {}
env['RUBY_FIBER_VM_STACK_SIZE'] = vm_stack_size.to_s if vm_stack_size
env['RUBY_FIBER_MACHINE_STACK_SIZE'] = machine_stack_size.to_s if machine_stack_size
- env['ASAN_OPTIONS'] = ENV['ASAN_OPTIONS'] if ENV['ASAN_OPTIONS']
stdout, stderr, status = EnvUtil.invoke_ruby([env, '-e', script], '', true, true, timeout: 30)
assert(!status.signaled?, FailDesc[status, nil, stderr])
diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb
index a916c8049e..ebe85dac82 100644
--- a/test/ruby/test_string.rb
+++ b/test/ruby/test_string.rb
@@ -9,9 +9,6 @@ class TestString < Test::Unit::TestCase
def initialize(*args)
@cls = String
- @aref_re_nth = true
- @aref_re_silent = false
- @aref_slicebang_silent = true
super
end
@@ -80,6 +77,13 @@ class TestString < Test::Unit::TestCase
assert_equal("mystring", str.__send__(:initialize, "mystring", capacity: 1000))
str = S("mystring")
assert_equal("mystring", str.__send__(:initialize, str, capacity: 1000))
+
+ if @cls == String
+ 100.times {
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".
+ __send__(:initialize, capacity: -1)
+ }
+ end
end
def test_initialize_shared
@@ -100,7 +104,7 @@ class TestString < Test::Unit::TestCase
return unless @cls == String
assert_no_memory_leak([], <<-PREP, <<-CODE, rss: true)
-code = proc {('x'*100000).__send__(:initialize, '')}
+code = proc {('x'*100_000).__send__(:initialize, '')}
1_000.times(&code)
PREP
100_000.times(&code)
@@ -112,7 +116,7 @@ CODE
return unless @cls == String
assert_no_memory_leak([], <<-PREP, <<-CODE, rss: true)
-code = proc {0.to_s.__send__(:initialize, capacity: 10000)}
+code = proc {0.to_s.__send__(:initialize, capacity: 100_000)}
1_000.times(&code)
PREP
100_000.times(&code)
@@ -146,14 +150,12 @@ CODE
assert_equal(nil, S("FooBar")[S("xyzzy")])
assert_equal(nil, S("FooBar")[S("plugh")])
- if @aref_re_nth
- assert_equal(S("Foo"), S("FooBar")[/([A-Z]..)([A-Z]..)/, 1])
- assert_equal(S("Bar"), S("FooBar")[/([A-Z]..)([A-Z]..)/, 2])
- assert_equal(nil, S("FooBar")[/([A-Z]..)([A-Z]..)/, 3])
- assert_equal(S("Bar"), S("FooBar")[/([A-Z]..)([A-Z]..)/, -1])
- assert_equal(S("Foo"), S("FooBar")[/([A-Z]..)([A-Z]..)/, -2])
- assert_equal(nil, S("FooBar")[/([A-Z]..)([A-Z]..)/, -3])
- end
+ assert_equal(S("Foo"), S("FooBar")[/([A-Z]..)([A-Z]..)/, 1])
+ assert_equal(S("Bar"), S("FooBar")[/([A-Z]..)([A-Z]..)/, 2])
+ assert_equal(nil, S("FooBar")[/([A-Z]..)([A-Z]..)/, 3])
+ assert_equal(S("Bar"), S("FooBar")[/([A-Z]..)([A-Z]..)/, -1])
+ assert_equal(S("Foo"), S("FooBar")[/([A-Z]..)([A-Z]..)/, -2])
+ assert_equal(nil, S("FooBar")[/([A-Z]..)([A-Z]..)/, -3])
o = Object.new
def o.to_int; 2; end
@@ -199,24 +201,18 @@ CODE
assert_equal(S("BarBar"), s)
s[/..r$/] = S("Foo")
assert_equal(S("BarFoo"), s)
- if @aref_re_silent
- s[/xyzzy/] = S("None")
- assert_equal(S("BarFoo"), s)
- else
- assert_raise(IndexError) { s[/xyzzy/] = S("None") }
- end
- if @aref_re_nth
- s[/([A-Z]..)([A-Z]..)/, 1] = S("Foo")
- assert_equal(S("FooFoo"), s)
- s[/([A-Z]..)([A-Z]..)/, 2] = S("Bar")
- assert_equal(S("FooBar"), s)
- assert_raise(IndexError) { s[/([A-Z]..)([A-Z]..)/, 3] = "None" }
- s[/([A-Z]..)([A-Z]..)/, -1] = S("Foo")
- assert_equal(S("FooFoo"), s)
- s[/([A-Z]..)([A-Z]..)/, -2] = S("Bar")
- assert_equal(S("BarFoo"), s)
- assert_raise(IndexError) { s[/([A-Z]..)([A-Z]..)/, -3] = "None" }
- end
+ assert_raise(IndexError) { s[/xyzzy/] = S("None") }
+
+ s[/([A-Z]..)([A-Z]..)/, 1] = S("Foo")
+ assert_equal(S("FooFoo"), s)
+ s[/([A-Z]..)([A-Z]..)/, 2] = S("Bar")
+ assert_equal(S("FooBar"), s)
+ assert_raise(IndexError) { s[/([A-Z]..)([A-Z]..)/, 3] = "None" }
+ s[/([A-Z]..)([A-Z]..)/, -1] = S("Foo")
+ assert_equal(S("FooFoo"), s)
+ s[/([A-Z]..)([A-Z]..)/, -2] = S("Bar")
+ assert_equal(S("BarFoo"), s)
+ assert_raise(IndexError) { s[/([A-Z]..)([A-Z]..)/, -3] = "None" }
s = S("FooBar")
s[S("Foo")] = S("Bar")
@@ -301,6 +297,9 @@ CODE
assert_raise(RangeError, bug) {S("a".force_encoding(Encoding::UTF_8)) << -1}
assert_raise(RangeError, bug) {S("a".force_encoding(Encoding::UTF_8)) << 0x81308130}
assert_nothing_raised {S("a".force_encoding(Encoding::GB18030)) << 0x81308130}
+
+ s = "\x95".force_encoding(Encoding::SJIS).tap(&:valid_encoding?)
+ assert_predicate(s << 0x5c, :valid_encoding?)
end
def test_MATCH # '=~'
@@ -587,6 +586,8 @@ CODE
assert_equal("foo", s.chomp!("\n"))
s = "foo\r"
assert_equal("foo", s.chomp!("\n"))
+
+ assert_raise(ArgumentError) {String.new.chomp!("", "")}
ensure
$/ = save
$VERBOSE = verbose
@@ -896,6 +897,18 @@ CODE
}
end
+ def test_undump_gc_compact_stress
+ omit "compaction doesn't work well on s390x" if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077
+ a = S("Test") << 1 << 2 << 3 << 9 << 13 << 10
+ EnvUtil.under_gc_compact_stress do
+ assert_equal(a, S('"Test\\x01\\x02\\x03\\t\\r\\n"').undump)
+ end
+
+ EnvUtil.under_gc_compact_stress do
+ assert_equal(S("\u{ABCDE 10ABCD}"), S('"\\u{ABCDE 10ABCD}"').undump)
+ end
+ end
+
def test_dup
for frozen in [ false, true ]
a = S("hello")
@@ -1095,6 +1108,22 @@ CODE
assert_equal("C", res[2])
end
+ def test_grapheme_clusters_memory_leak
+ assert_no_memory_leak([], "", "#{<<~"begin;"}\n#{<<~'end;'}", "[Bug #todo]", rss: true)
+ begin;
+ str = "hello world".encode(Encoding::UTF_32LE)
+
+ 10_000.times do
+ str.grapheme_clusters
+ end
+ end;
+ end
+
+ def test_byteslice_grapheme_clusters
+ string = "안녕"
+ assert_equal(["안"], string.byteslice(0,4).grapheme_clusters)
+ end
+
def test_each_line
verbose, $VERBOSE = $VERBOSE, nil
@@ -1249,6 +1278,11 @@ CODE
assert_raise(ArgumentError) { S("foo").gsub }
end
+ def test_gsub_gc_compact_stress
+ omit "compaction doesn't work well on s390x" if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077
+ EnvUtil.under_gc_compact_stress { assert_equal(S("h<e>ll<o>"), S("hello").gsub(/([aeiou])/, S('<\1>'))) }
+ end
+
def test_gsub_encoding
a = S("hello world")
a.force_encoding Encoding::UTF_8
@@ -1292,6 +1326,15 @@ CODE
assert_nil(a.sub!(S('X'), S('Y')))
end
+ def test_gsub_bang_gc_compact_stress
+ omit "compaction doesn't work well on s390x" if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077
+ EnvUtil.under_gc_compact_stress do
+ a = S("hello")
+ a.gsub!(/([aeiou])/, S('<\1>'))
+ assert_equal(S("h<e>ll<o>"), a)
+ end
+ end
+
def test_sub_hash
assert_equal('azc', S('abc').sub(/b/, "b" => "z"))
assert_equal('ac', S('abc').sub(/b/, {}))
@@ -1319,6 +1362,9 @@ CODE
assert_not_equal(S("a").hash, S("a\0").hash, bug4104)
bug9172 = '[ruby-core:58658] [Bug #9172]'
assert_not_equal(S("sub-setter").hash, S("discover").hash, bug9172)
+ assert_equal(S("").hash, S("".encode(Encoding::UTF_32BE)).hash)
+ h1, h2 = ["\x80", "\x81"].map {|c| c.b.hash ^ c.hash}
+ assert_not_equal(h1, h2)
end
def test_hex
@@ -1339,54 +1385,54 @@ CODE
end
def test_index
- assert_equal(0, S("hello").index(?h))
- assert_equal(1, S("hello").index(S("ell")))
- assert_equal(2, S("hello").index(/ll./))
+ assert_index(0, S("hello"), ?h)
+ assert_index(1, S("hello"), S("ell"))
+ assert_index(2, S("hello"), /ll./)
- assert_equal(3, S("hello").index(?l, 3))
- assert_equal(3, S("hello").index(S("l"), 3))
- assert_equal(3, S("hello").index(/l./, 3))
+ assert_index(3, S("hello"), ?l, 3)
+ assert_index(3, S("hello"), S("l"), 3)
+ assert_index(3, S("hello"), /l./, 3)
- assert_nil(S("hello").index(?z, 3))
- assert_nil(S("hello").index(S("z"), 3))
- assert_nil(S("hello").index(/z./, 3))
+ assert_index(nil, S("hello"), ?z, 3)
+ assert_index(nil, S("hello"), S("z"), 3)
+ assert_index(nil, S("hello"), /z./, 3)
- assert_nil(S("hello").index(?z))
- assert_nil(S("hello").index(S("z")))
- assert_nil(S("hello").index(/z./))
+ assert_index(nil, S("hello"), ?z)
+ assert_index(nil, S("hello"), S("z"))
+ assert_index(nil, S("hello"), /z./)
- assert_equal(0, S("").index(S("")))
- assert_equal(0, S("").index(//))
- assert_nil(S("").index(S("hello")))
- assert_nil(S("").index(/hello/))
- assert_equal(0, S("hello").index(S("")))
- assert_equal(0, S("hello").index(//))
+ assert_index(0, S(""), S(""))
+ assert_index(0, S(""), //)
+ assert_index(nil, S(""), S("hello"))
+ assert_index(nil, S(""), /hello/)
+ assert_index(0, S("hello"), S(""))
+ assert_index(0, S("hello"), //)
s = S("long") * 1000 << "x"
- assert_nil(s.index(S("y")))
- assert_equal(4 * 1000, s.index(S("x")))
+ assert_index(nil, s, S("y"))
+ assert_index(4 * 1000, s, S("x"))
s << "yx"
- assert_equal(4 * 1000, s.index(S("x")))
- assert_equal(4 * 1000, s.index(S("xyx")))
+ assert_index(4 * 1000, s, S("x"))
+ assert_index(4 * 1000, s, S("xyx"))
o = Object.new
def o.to_str; "bar"; end
- assert_equal(3, S("foobarbarbaz").index(o))
+ assert_index(3, S("foobarbarbaz"), o)
assert_raise(TypeError) { S("foo").index(Object.new) }
- assert_nil(S("foo").index(//, -100))
- assert_nil($~)
+ assert_index(nil, S("foo"), //, -100)
+ assert_index(nil, S("foo"), //, 4)
- assert_equal(2, S("abcdbce").index(/b\Kc/))
+ assert_index(2, S("abcdbce"), /b\Kc/)
- assert_equal(0, S("ã“ã‚“ã«ã¡ã¯").index(?ã“))
- assert_equal(1, S("ã“ã‚“ã«ã¡ã¯").index(S("ã‚“ã«ã¡")))
- assert_equal(2, S("ã“ã‚“ã«ã¡ã¯").index(/ã«ã¡./))
+ assert_index(0, S("ã“ã‚“ã«ã¡ã¯"), ?ã“)
+ assert_index(1, S("ã“ã‚“ã«ã¡ã¯"), S("ã‚“ã«ã¡"))
+ assert_index(2, S("ã“ã‚“ã«ã¡ã¯"), /ã«ã¡./)
- assert_equal(0, S("ã«ã‚“ã«ã¡ã¯").index(?ã«, 0))
- assert_equal(2, S("ã«ã‚“ã«ã¡ã¯").index(?ã«, 1))
- assert_equal(2, S("ã«ã‚“ã«ã¡ã¯").index(?ã«, 2))
- assert_nil(S("ã«ã‚“ã«ã¡ã¯").index(?ã«, 3))
+ assert_index(0, S("ã«ã‚“ã«ã¡ã¯"), ?ã«, 0)
+ assert_index(2, S("ã«ã‚“ã«ã¡ã¯"), ?ã«, 1)
+ assert_index(2, S("ã«ã‚“ã«ã¡ã¯"), ?ã«, 2)
+ assert_index(nil, S("ã«ã‚“ã«ã¡ã¯"), ?ã«, 3)
end
def test_insert
@@ -1533,57 +1579,57 @@ CODE
end
def test_rindex
- assert_equal(3, S("hello").rindex(?l))
- assert_equal(6, S("ell, hello").rindex(S("ell")))
- assert_equal(7, S("ell, hello").rindex(/ll./))
+ assert_rindex(3, S("hello"), ?l)
+ assert_rindex(6, S("ell, hello"), S("ell"))
+ assert_rindex(7, S("ell, hello"), /ll./)
- assert_equal(3, S("hello,lo").rindex(?l, 3))
- assert_equal(3, S("hello,lo").rindex(S("l"), 3))
- assert_equal(3, S("hello,lo").rindex(/l./, 3))
+ assert_rindex(3, S("hello,lo"), ?l, 3)
+ assert_rindex(3, S("hello,lo"), S("l"), 3)
+ assert_rindex(3, S("hello,lo"), /l./, 3)
- assert_nil(S("hello").rindex(?z, 3))
- assert_nil(S("hello").rindex(S("z"), 3))
- assert_nil(S("hello").rindex(/z./, 3))
+ assert_rindex(nil, S("hello"), ?z, 3)
+ assert_rindex(nil, S("hello"), S("z"), 3)
+ assert_rindex(nil, S("hello"), /z./, 3)
- assert_nil(S("hello").rindex(?z))
- assert_nil(S("hello").rindex(S("z")))
- assert_nil(S("hello").rindex(/z./))
+ assert_rindex(nil, S("hello"), ?z)
+ assert_rindex(nil, S("hello"), S("z"))
+ assert_rindex(nil, S("hello"), /z./)
- assert_equal(5, S("hello").rindex(S("")))
- assert_equal(5, S("hello").rindex(S(""), 5))
- assert_equal(4, S("hello").rindex(S(""), 4))
- assert_equal(0, S("hello").rindex(S(""), 0))
+ assert_rindex(5, S("hello"), S(""))
+ assert_rindex(5, S("hello"), S(""), 5)
+ assert_rindex(4, S("hello"), S(""), 4)
+ assert_rindex(0, S("hello"), S(""), 0)
o = Object.new
def o.to_str; "bar"; end
- assert_equal(6, S("foobarbarbaz").rindex(o))
+ assert_rindex(6, S("foobarbarbaz"), o)
assert_raise(TypeError) { S("foo").rindex(Object.new) }
- assert_nil(S("foo").rindex(//, -100))
- assert_nil($~)
+ assert_rindex(nil, S("foo"), //, -100)
- assert_equal(3, S("foo").rindex(//))
- assert_equal([3, 3], $~.offset(0))
+ m = assert_rindex(3, S("foo"), //)
+ assert_equal([3, 3], m.offset(0))
+ assert_rindex(3, S("foo"), //, 4)
- assert_equal(5, S("abcdbce").rindex(/b\Kc/))
+ assert_rindex(5, S("abcdbce"), /b\Kc/)
- assert_equal(2, S("ã“ã‚“ã«ã¡ã¯").rindex(?ã«))
- assert_equal(6, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯").rindex(S("ã«ã¡ã¯")))
- assert_equal(6, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯").rindex(/ã«ã¡./))
+ assert_rindex(2, S("ã“ã‚“ã«ã¡ã¯"), ?ã«)
+ assert_rindex(6, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯"), S("ã«ã¡ã¯"))
+ assert_rindex(6, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯"), /ã«ã¡./)
- assert_equal(6, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯").rindex(S("ã«ã¡ã¯"), 7))
- assert_equal(6, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯").rindex(S("ã«ã¡ã¯"), -2))
- assert_equal(6, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯").rindex(S("ã«ã¡ã¯"), 6))
- assert_equal(6, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯").rindex(S("ã«ã¡ã¯"), -3))
- assert_equal(0, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯").rindex(S("ã«ã¡ã¯"), 5))
- assert_equal(0, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯").rindex(S("ã«ã¡ã¯"), -4))
- assert_equal(0, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯").rindex(S("ã«ã¡ã¯"), 1))
- assert_equal(0, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯").rindex(S("ã«ã¡ã¯"), 0))
+ assert_rindex(6, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯"), S("ã«ã¡ã¯"), 7)
+ assert_rindex(6, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯"), S("ã«ã¡ã¯"), -2)
+ assert_rindex(6, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯"), S("ã«ã¡ã¯"), 6)
+ assert_rindex(6, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯"), S("ã«ã¡ã¯"), -3)
+ assert_rindex(0, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯"), S("ã«ã¡ã¯"), 5)
+ assert_rindex(0, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯"), S("ã«ã¡ã¯"), -4)
+ assert_rindex(0, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯"), S("ã«ã¡ã¯"), 1)
+ assert_rindex(0, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯"), S("ã«ã¡ã¯"), 0)
- assert_equal(0, S("ã“ã‚“ã«ã¡ã¯").rindex(S("ã“ã‚“ã«ã¡ã¯")))
- assert_nil(S("ã“ã‚“ã«ã¡").rindex(S("ã“ã‚“ã«ã¡ã¯")))
- assert_nil(S("ã“").rindex(S("ã“ã‚“ã«ã¡ã¯")))
- assert_nil(S("").rindex(S("ã“ã‚“ã«ã¡ã¯")))
+ assert_rindex(0, S("ã“ã‚“ã«ã¡ã¯"), S("ã“ã‚“ã«ã¡ã¯"))
+ assert_rindex(nil, S("ã“ã‚“ã«ã¡"), S("ã“ã‚“ã«ã¡ã¯"))
+ assert_rindex(nil, S("ã“"), S("ã“ã‚“ã«ã¡ã¯"))
+ assert_rindex(nil, S(""), S("ã“ã‚“ã«ã¡ã¯"))
end
def test_rjust
@@ -1622,6 +1668,11 @@ CODE
assert_equal(%w[1 2 3], S("a1 a2 a3").scan(/a\K./))
end
+ def test_scan_gc_compact_stress
+ omit "compaction doesn't work well on s390x" if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077
+ EnvUtil.under_gc_compact_stress { assert_equal([["1a"], ["2b"], ["3c"]], S("1a2b3c").scan(/(\d.)/)) }
+ end
+
def test_scan_segv
bug19159 = '[Bug #19159]'
assert_nothing_raised(Exception, bug19159) do
@@ -1682,20 +1733,11 @@ CODE
assert_equal(S("FooBa"), a)
a = S("FooBar")
- if @aref_slicebang_silent
- assert_nil( a.slice!(6) )
- assert_nil( a.slice!(6r) )
- else
- assert_raise(IndexError) { a.slice!(6) }
- assert_raise(IndexError) { a.slice!(6r) }
- end
+ assert_nil( a.slice!(6) )
+ assert_nil( a.slice!(6r) )
assert_equal(S("FooBar"), a)
- if @aref_slicebang_silent
- assert_nil( a.slice!(-7) )
- else
- assert_raise(IndexError) { a.slice!(-7) }
- end
+ assert_nil( a.slice!(-7) )
assert_equal(S("FooBar"), a)
a = S("FooBar")
@@ -1707,17 +1749,9 @@ CODE
assert_equal(S("Foo"), a)
a=S("FooBar")
- if @aref_slicebang_silent
assert_nil(a.slice!(7,2)) # Maybe should be six?
- else
- assert_raise(IndexError) {a.slice!(7,2)} # Maybe should be six?
- end
assert_equal(S("FooBar"), a)
- if @aref_slicebang_silent
assert_nil(a.slice!(-7,10))
- else
- assert_raise(IndexError) {a.slice!(-7,10)}
- end
assert_equal(S("FooBar"), a)
a=S("FooBar")
@@ -1729,17 +1763,9 @@ CODE
assert_equal(S("Foo"), a)
a=S("FooBar")
- if @aref_slicebang_silent
assert_equal(S(""), a.slice!(6..2))
- else
- assert_raise(RangeError) {a.slice!(6..2)}
- end
assert_equal(S("FooBar"), a)
- if @aref_slicebang_silent
assert_nil(a.slice!(-10..-7))
- else
- assert_raise(RangeError) {a.slice!(-10..-7)}
- end
assert_equal(S("FooBar"), a)
a=S("FooBar")
@@ -1751,17 +1777,9 @@ CODE
assert_equal(S("Foo"), a)
a=S("FooBar")
- if @aref_slicebang_silent
- assert_nil(a.slice!(/xyzzy/))
- else
- assert_raise(IndexError) {a.slice!(/xyzzy/)}
- end
+ assert_nil(a.slice!(/xyzzy/))
assert_equal(S("FooBar"), a)
- if @aref_slicebang_silent
- assert_nil(a.slice!(/plugh/))
- else
- assert_raise(IndexError) {a.slice!(/plugh/)}
- end
+ assert_nil(a.slice!(/plugh/))
assert_equal(S("FooBar"), a)
a=S("FooBar")
@@ -1938,10 +1956,15 @@ CODE
assert_send([S("hello"), :start_with?, S("hel")])
assert_not_send([S("hello"), :start_with?, S("el")])
assert_send([S("hello"), :start_with?, S("el"), S("he")])
+ assert_send([S("\xFF\xFE"), :start_with?, S("\xFF")])
+ assert_send([S("hello\xBE"), :start_with?, S("hello")])
+ assert_not_send([S("\u{c4}"), :start_with?, S("\xC3")])
bug5536 = '[ruby-core:40623]'
assert_raise(TypeError, bug5536) {S("str").start_with? :not_convertible_to_string}
+ end
+ def test_start_with_regexp
assert_equal(true, S("hello").start_with?(/hel/))
assert_equal("hel", $&)
assert_equal(false, S("hello").start_with?(/el/))
@@ -2044,6 +2067,16 @@ CODE
}
end
+ def test_sub_gc_compact_stress
+ omit "compaction doesn't work well on s390x" if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077
+ EnvUtil.under_gc_compact_stress do
+ m = /&(?<foo>.*?);/.match(S("aaa &amp; yyy"))
+ assert_equal("amp", m["foo"])
+
+ assert_equal("aaa [amp] yyy", S("aaa &amp; yyy").sub(/&(?<foo>.*?);/, S('[\k<foo>]')))
+ end
+ end
+
def test_sub!
a = S("hello")
b = a.dup
@@ -2301,6 +2334,8 @@ CODE
assert_not_predicate(str, :ascii_only?)
assert_not_predicate(star, :ascii_only?)
assert_not_predicate(result, :ascii_only?, bug13950)
+
+ assert_equal(S("XYC"), S("ABC").tr("A-AB", "XY"))
end
def test_tr!
@@ -2325,6 +2360,8 @@ CODE
a = S("abc".force_encoding(Encoding::US_ASCII))
assert_nil(a.tr!(S("z"), S("\u0101")), '[ruby-core:22326]')
assert_equal(Encoding::US_ASCII, a.encoding, '[ruby-core:22326]')
+
+ assert_equal(S("XYC"), S("ABC").tr!("A-AB", "XY"))
end
def test_tr_s
@@ -2332,6 +2369,8 @@ CODE
assert_equal(S("h*o"), S("hello").tr_s(S("el"), S("*")))
assert_equal("a".hash, S("\u0101\u0101").tr_s("\u0101", "a").hash)
assert_equal(true, S("\u3041\u3041").tr("\u3041", "a").ascii_only?)
+
+ assert_equal(S("XYC"), S("ABC").tr_s("A-AB", "XY"))
end
def test_tr_s!
@@ -2344,6 +2383,8 @@ CODE
a = S("hello")
assert_equal(S("h*o"), a.tr_s!(S("el"), S("*")))
assert_equal(S("h*o"), a)
+
+ assert_equal(S("XYC"), S("ABC").tr_s!("A-AB", "XY"))
end
def test_unpack
@@ -2883,11 +2924,13 @@ CODE
end
- def test_delete_prefix
+ def test_delete_prefix_type_error
assert_raise(TypeError) { S('hello').delete_prefix(nil) }
assert_raise(TypeError) { S('hello').delete_prefix(1) }
assert_raise(TypeError) { S('hello').delete_prefix(/hel/) }
+ end
+ def test_delete_prefix
s = S("hello")
assert_equal("lo", s.delete_prefix('hel'))
assert_equal("hello", s)
@@ -2907,8 +2950,9 @@ CODE
s = S("hello")
assert_equal("hello", s.delete_prefix("\u{3053 3093}"))
assert_equal("hello", s)
+ end
- # skip if argument is a broken string
+ def test_delete_prefix_broken_encoding
s = S("\xe3\x81\x82")
assert_equal("\xe3\x81\x82", s.delete_prefix("\xe3"))
assert_equal("\xe3\x81\x82", s)
@@ -2917,23 +2961,31 @@ CODE
assert_equal("\x95\x5c".force_encoding("Shift_JIS"), s.delete_prefix("\x95"))
assert_equal("\x95\x5c".force_encoding("Shift_JIS"), s)
- # clear coderange
+ assert_equal("\xFE", S("\xFF\xFE").delete_prefix("\xFF"))
+ assert_equal("\xBE", S("hello\xBE").delete_prefix("hello"))
+ assert_equal("\xBE", S("\xFFhello\xBE").delete_prefix("\xFFhello"))
+ end
+
+ def test_delete_prefix_clear_coderange
s = S("\u{3053 3093}hello")
assert_not_predicate(s, :ascii_only?)
assert_predicate(s.delete_prefix("\u{3053 3093}"), :ascii_only?)
+ end
- # argument should be converted to String
+ def test_delete_prefix_argument_conversion
klass = Class.new { def to_str; 'a'; end }
s = S("abba")
assert_equal("bba", s.delete_prefix(klass.new))
assert_equal("abba", s)
end
- def test_delete_prefix_bang
+ def test_delete_prefix_bang_type_error
assert_raise(TypeError) { S('hello').delete_prefix!(nil) }
assert_raise(TypeError) { S('hello').delete_prefix!(1) }
assert_raise(TypeError) { S('hello').delete_prefix!(/hel/) }
+ end
+ def test_delete_prefix_bang
s = S("hello")
assert_equal("lo", s.delete_prefix!('hel'))
assert_equal("lo", s)
@@ -2953,23 +3005,32 @@ CODE
s = S("hello")
assert_equal(nil, s.delete_prefix!("\u{3053 3093}"))
assert_equal("hello", s)
+ end
- # skip if argument is a broken string
+ def test_delete_prefix_bang_broken_encoding
s = S("\xe3\x81\x82")
assert_equal(nil, s.delete_prefix!("\xe3"))
assert_equal("\xe3\x81\x82", s)
- # clear coderange
+ s = S("\xFF\xFE")
+ assert_equal("\xFE", s.delete_prefix!("\xFF"))
+ assert_equal("\xFE", s)
+ end
+
+ def test_delete_prefix_bang_clear_coderange
s = S("\u{3053 3093}hello")
assert_not_predicate(s, :ascii_only?)
assert_predicate(s.delete_prefix!("\u{3053 3093}"), :ascii_only?)
+ end
- # argument should be converted to String
+ def test_delete_prefix_bang_argument_conversion
klass = Class.new { def to_str; 'a'; end }
s = S("abba")
assert_equal("bba", s.delete_prefix!(klass.new))
assert_equal("bba", s)
+ end
+ def test_delete_prefix_bang_frozen_error
s = S("ax").freeze
assert_raise_with_message(FrozenError, /frozen/) {s.delete_prefix!("a")}
@@ -2982,11 +3043,13 @@ CODE
assert_raise_with_message(FrozenError, /frozen/) {s.delete_prefix!(o)}
end
- def test_delete_suffix
+ def test_delete_suffix_type_error
assert_raise(TypeError) { S('hello').delete_suffix(nil) }
assert_raise(TypeError) { S('hello').delete_suffix(1) }
assert_raise(TypeError) { S('hello').delete_suffix(/hel/) }
+ end
+ def test_delete_suffix
s = S("hello")
assert_equal("hel", s.delete_suffix('lo'))
assert_equal("hello", s)
@@ -3006,23 +3069,28 @@ CODE
s = S("hello")
assert_equal("hello", s.delete_suffix("\u{3061 306f}"))
assert_equal("hello", s)
+ end
- # skip if argument is a broken string
+ def test_delete_suffix_broken_encoding
s = S("\xe3\x81\x82")
assert_equal("\xe3\x81\x82", s.delete_suffix("\x82"))
assert_equal("\xe3\x81\x82", s)
+ end
- # clear coderange
+ def test_delete_suffix_clear_coderange
s = S("hello\u{3053 3093}")
assert_not_predicate(s, :ascii_only?)
assert_predicate(s.delete_suffix("\u{3053 3093}"), :ascii_only?)
+ end
- # argument should be converted to String
+ def test_delete_suffix_argument_conversion
klass = Class.new { def to_str; 'a'; end }
s = S("abba")
assert_equal("abb", s.delete_suffix(klass.new))
assert_equal("abba", s)
+ end
+ def test_delete_suffix_newline
# chomp removes any of "\n", "\r\n", "\r" when "\n" is specified,
# but delete_suffix does not
s = "foo\n"
@@ -3033,11 +3101,13 @@ CODE
assert_equal("foo\r", s.delete_suffix("\n"))
end
- def test_delete_suffix_bang
+ def test_delete_suffix_bang_type_error
assert_raise(TypeError) { S('hello').delete_suffix!(nil) }
assert_raise(TypeError) { S('hello').delete_suffix!(1) }
assert_raise(TypeError) { S('hello').delete_suffix!(/hel/) }
+ end
+ def test_delete_suffix_bang_frozen_error
s = S("hello").freeze
assert_raise_with_message(FrozenError, /frozen/) {s.delete_suffix!('lo')}
@@ -3048,7 +3118,9 @@ CODE
"x"
end
assert_raise_with_message(FrozenError, /frozen/) {s.delete_suffix!(o)}
+ end
+ def test_delete_suffix_bang
s = S("hello")
assert_equal("hel", s.delete_suffix!('lo'))
assert_equal("hel", s)
@@ -3068,8 +3140,9 @@ CODE
s = S("hello")
assert_equal(nil, s.delete_suffix!("\u{3061 306f}"))
assert_equal("hello", s)
+ end
- # skip if argument is a broken string
+ def test_delete_suffix_bang_broken_encoding
s = S("\xe3\x81\x82")
assert_equal(nil, s.delete_suffix!("\x82"))
assert_equal("\xe3\x81\x82", s)
@@ -3077,18 +3150,22 @@ CODE
s = S("\x95\x5c").force_encoding("Shift_JIS")
assert_equal(nil, s.delete_suffix!("\x5c"))
assert_equal("\x95\x5c".force_encoding("Shift_JIS"), s)
+ end
- # clear coderange
+ def test_delete_suffix_bang_clear_coderange
s = S("hello\u{3053 3093}")
assert_not_predicate(s, :ascii_only?)
assert_predicate(s.delete_suffix!("\u{3053 3093}"), :ascii_only?)
+ end
- # argument should be converted to String
+ def test_delete_suffix_bang_argument_conversion
klass = Class.new { def to_str; 'a'; end }
s = S("abba")
assert_equal("abb", s.delete_suffix!(klass.new))
assert_equal("abb", s)
+ end
+ def test_delete_suffix_bang_newline
# chomp removes any of "\n", "\r\n", "\r" when "\n" is specified,
# but delete_suffix does not
s = "foo\n"
@@ -3279,7 +3356,11 @@ CODE
assert_same(str, +str)
assert_not_same(str, -str)
- str = "bar".freeze
+ require 'objspace'
+
+ str = "test_uplus_minus_str".freeze
+ assert_includes ObjectSpace.dump(str), '"fstring":true'
+
assert_predicate(str, :frozen?)
assert_not_predicate(+str, :frozen?)
assert_predicate(-str, :frozen?)
@@ -3287,8 +3368,8 @@ CODE
assert_not_same(str, +str)
assert_same(str, -str)
- bar = %w(b a r).join('')
- assert_same(str, -bar, "uminus deduplicates [Feature #13077]")
+ bar = -%w(test uplus minus str).join('_')
+ assert_same(str, bar, "uminus deduplicates [Feature #13077] str: #{ObjectSpace.dump(str)} bar: #{ObjectSpace.dump(bar)}")
end
def test_uminus_frozen
@@ -3346,111 +3427,114 @@ CODE
end
def test_byteindex
- assert_equal(0, S("hello").byteindex(?h))
- assert_equal(1, S("hello").byteindex(S("ell")))
- assert_equal(2, S("hello").byteindex(/ll./))
+ assert_byteindex(0, S("hello"), ?h)
+ assert_byteindex(1, S("hello"), S("ell"))
+ assert_byteindex(2, S("hello"), /ll./)
- assert_equal(3, S("hello").byteindex(?l, 3))
- assert_equal(3, S("hello").byteindex(S("l"), 3))
- assert_equal(3, S("hello").byteindex(/l./, 3))
+ assert_byteindex(3, S("hello"), ?l, 3)
+ assert_byteindex(3, S("hello"), S("l"), 3)
+ assert_byteindex(3, S("hello"), /l./, 3)
- assert_nil(S("hello").byteindex(?z, 3))
- assert_nil(S("hello").byteindex(S("z"), 3))
- assert_nil(S("hello").byteindex(/z./, 3))
+ assert_byteindex(nil, S("hello"), ?z, 3)
+ assert_byteindex(nil, S("hello"), S("z"), 3)
+ assert_byteindex(nil, S("hello"), /z./, 3)
- assert_nil(S("hello").byteindex(?z))
- assert_nil(S("hello").byteindex(S("z")))
- assert_nil(S("hello").byteindex(/z./))
+ assert_byteindex(nil, S("hello"), ?z)
+ assert_byteindex(nil, S("hello"), S("z"))
+ assert_byteindex(nil, S("hello"), /z./)
- assert_equal(0, S("").byteindex(S("")))
- assert_equal(0, S("").byteindex(//))
- assert_nil(S("").byteindex(S("hello")))
- assert_nil(S("").byteindex(/hello/))
- assert_equal(0, S("hello").byteindex(S("")))
- assert_equal(0, S("hello").byteindex(//))
+ assert_byteindex(0, S(""), S(""))
+ assert_byteindex(0, S(""), //)
+ assert_byteindex(nil, S(""), S("hello"))
+ assert_byteindex(nil, S(""), /hello/)
+ assert_byteindex(0, S("hello"), S(""))
+ assert_byteindex(0, S("hello"), //)
s = S("long") * 1000 << "x"
- assert_nil(s.byteindex(S("y")))
- assert_equal(4 * 1000, s.byteindex(S("x")))
+ assert_byteindex(nil, s, S("y"))
+ assert_byteindex(4 * 1000, s, S("x"))
s << "yx"
- assert_equal(4 * 1000, s.byteindex(S("x")))
- assert_equal(4 * 1000, s.byteindex(S("xyx")))
+ assert_byteindex(4 * 1000, s, S("x"))
+ assert_byteindex(4 * 1000, s, S("xyx"))
o = Object.new
def o.to_str; "bar"; end
- assert_equal(3, S("foobarbarbaz").byteindex(o))
+ assert_byteindex(3, S("foobarbarbaz"), o)
assert_raise(TypeError) { S("foo").byteindex(Object.new) }
- assert_nil(S("foo").byteindex(//, -100))
- assert_nil($~)
+ assert_byteindex(nil, S("foo"), //, -100)
+ assert_byteindex(nil, S("foo"), //, -4)
- assert_equal(2, S("abcdbce").byteindex(/b\Kc/))
+ assert_byteindex(2, S("abcdbce"), /b\Kc/)
- assert_equal(0, S("ã“ã‚“ã«ã¡ã¯").byteindex(?ã“))
- assert_equal(3, S("ã“ã‚“ã«ã¡ã¯").byteindex(S("ã‚“ã«ã¡")))
- assert_equal(6, S("ã“ã‚“ã«ã¡ã¯").byteindex(/ã«ã¡./))
+ assert_byteindex(0, S("ã“ã‚“ã«ã¡ã¯"), ?ã“)
+ assert_byteindex(3, S("ã“ã‚“ã«ã¡ã¯"), S("ã‚“ã«ã¡"))
+ assert_byteindex(6, S("ã“ã‚“ã«ã¡ã¯"), /ã«ã¡./)
- assert_equal(0, S("ã«ã‚“ã«ã¡ã¯").byteindex(?ã«, 0))
+ assert_byteindex(0, S("ã«ã‚“ã«ã¡ã¯"), ?ã«, 0)
assert_raise(IndexError) { S("ã«ã‚“ã«ã¡ã¯").byteindex(?ã«, 1) }
assert_raise(IndexError) { S("ã«ã‚“ã«ã¡ã¯").byteindex(?ã«, 5) }
- assert_equal(6, S("ã«ã‚“ã«ã¡ã¯").byteindex(?ã«, 6))
- assert_equal(6, S("ã«ã‚“ã«ã¡ã¯").byteindex(S("ã«"), 6))
- assert_equal(6, S("ã«ã‚“ã«ã¡ã¯").byteindex(/ã«./, 6))
+ assert_byteindex(6, S("ã«ã‚“ã«ã¡ã¯"), ?ã«, 6)
+ assert_byteindex(6, S("ã«ã‚“ã«ã¡ã¯"), S("ã«"), 6)
+ assert_byteindex(6, S("ã«ã‚“ã«ã¡ã¯"), /ã«./, 6)
assert_raise(IndexError) { S("ã«ã‚“ã«ã¡ã¯").byteindex(?ã«, 7) }
+
+ s = S("foobarbarbaz")
+ assert !1000.times.any? {s.byteindex("", 100_000_000)}
end
def test_byterindex
- assert_equal(3, S("hello").byterindex(?l))
- assert_equal(6, S("ell, hello").byterindex(S("ell")))
- assert_equal(7, S("ell, hello").byterindex(/ll./))
+ assert_byterindex(3, S("hello"), ?l)
+ assert_byterindex(6, S("ell, hello"), S("ell"))
+ assert_byterindex(7, S("ell, hello"), /ll./)
- assert_equal(3, S("hello,lo").byterindex(?l, 3))
- assert_equal(3, S("hello,lo").byterindex(S("l"), 3))
- assert_equal(3, S("hello,lo").byterindex(/l./, 3))
+ assert_byterindex(3, S("hello,lo"), ?l, 3)
+ assert_byterindex(3, S("hello,lo"), S("l"), 3)
+ assert_byterindex(3, S("hello,lo"), /l./, 3)
- assert_nil(S("hello").byterindex(?z, 3))
- assert_nil(S("hello").byterindex(S("z"), 3))
- assert_nil(S("hello").byterindex(/z./, 3))
+ assert_byterindex(nil, S("hello"), ?z, 3)
+ assert_byterindex(nil, S("hello"), S("z"), 3)
+ assert_byterindex(nil, S("hello"), /z./, 3)
- assert_nil(S("hello").byterindex(?z))
- assert_nil(S("hello").byterindex(S("z")))
- assert_nil(S("hello").byterindex(/z./))
+ assert_byterindex(nil, S("hello"), ?z)
+ assert_byterindex(nil, S("hello"), S("z"))
+ assert_byterindex(nil, S("hello"), /z./)
- assert_equal(5, S("hello").byterindex(S("")))
- assert_equal(5, S("hello").byterindex(S(""), 5))
- assert_equal(4, S("hello").byterindex(S(""), 4))
- assert_equal(0, S("hello").byterindex(S(""), 0))
+ assert_byterindex(5, S("hello"), S(""))
+ assert_byterindex(5, S("hello"), S(""), 5)
+ assert_byterindex(4, S("hello"), S(""), 4)
+ assert_byterindex(0, S("hello"), S(""), 0)
o = Object.new
def o.to_str; "bar"; end
- assert_equal(6, S("foobarbarbaz").byterindex(o))
+ assert_byterindex(6, S("foobarbarbaz"), o)
assert_raise(TypeError) { S("foo").byterindex(Object.new) }
- assert_nil(S("foo").byterindex(//, -100))
- assert_nil($~)
+ assert_byterindex(nil, S("foo"), //, -100)
- assert_equal(3, S("foo").byterindex(//))
- assert_equal([3, 3], $~.offset(0))
+ m = assert_byterindex(3, S("foo"), //)
+ assert_equal([3, 3], m.offset(0))
+ assert_byterindex(3, S("foo"), //, 4)
- assert_equal(5, S("abcdbce").byterindex(/b\Kc/))
+ assert_byterindex(5, S("abcdbce"), /b\Kc/)
- assert_equal(6, S("ã“ã‚“ã«ã¡ã¯").byterindex(?ã«))
- assert_equal(18, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯").byterindex(S("ã«ã¡ã¯")))
- assert_equal(18, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯").byterindex(/ã«ã¡./))
+ assert_byterindex(6, S("ã“ã‚“ã«ã¡ã¯"), ?ã«)
+ assert_byterindex(18, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯"), S("ã«ã¡ã¯"))
+ assert_byterindex(18, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯"), /ã«ã¡./)
assert_raise(IndexError) { S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯").byterindex(S("ã«ã¡ã¯"), 19) }
assert_raise(IndexError) { S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯").byterindex(S("ã«ã¡ã¯"), -2) }
- assert_equal(18, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯").byterindex(S("ã«ã¡ã¯"), 18))
- assert_equal(18, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯").byterindex(S("ã«ã¡ã¯"), -3))
+ assert_byterindex(18, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯"), S("ã«ã¡ã¯"), 18)
+ assert_byterindex(18, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯"), S("ã«ã¡ã¯"), -3)
assert_raise(IndexError) { S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯").byterindex(S("ã«ã¡ã¯"), 17) }
assert_raise(IndexError) { S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯").byterindex(S("ã«ã¡ã¯"), -4) }
assert_raise(IndexError) { S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯").byterindex(S("ã«ã¡ã¯"), 1) }
- assert_equal(0, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯").byterindex(S("ã«ã¡ã¯"), 0))
+ assert_byterindex(0, S("ã«ã¡ã¯ã€ã“ã‚“ã«ã¡ã¯"), S("ã«ã¡ã¯"), 0)
- assert_equal(0, S("ã“ã‚“ã«ã¡ã¯").byterindex(S("ã“ã‚“ã«ã¡ã¯")))
- assert_nil(S("ã“ã‚“ã«ã¡").byterindex(S("ã“ã‚“ã«ã¡ã¯")))
- assert_nil(S("ã“").byterindex(S("ã“ã‚“ã«ã¡ã¯")))
- assert_nil(S("").byterindex(S("ã“ã‚“ã«ã¡ã¯")))
+ assert_byterindex(0, S("ã“ã‚“ã«ã¡ã¯"), S("ã“ã‚“ã«ã¡ã¯"))
+ assert_byterindex(nil, S("ã“ã‚“ã«ã¡"), S("ã“ã‚“ã«ã¡ã¯"))
+ assert_byterindex(nil, S("ã“"), S("ã“ã‚“ã«ã¡ã¯"))
+ assert_byterindex(nil, S(""), S("ã“ã‚“ã«ã¡ã¯"))
end
def test_bytesplice
@@ -3530,6 +3614,56 @@ CODE
assert_bytesplice_raise(ArgumentError, S("hello"), 0..-1, "bye", 0, 3)
end
+ def test_chilled_string
+ chilled_string = eval('"chilled"')
+
+ # Chilled strings pretend to be frozen
+ assert_predicate chilled_string, :frozen?
+
+ assert_not_predicate chilled_string.dup, :frozen?
+ assert_predicate chilled_string.clone, :frozen?
+
+ # @+ treat the original string as frozen
+ assert_not_predicate(+chilled_string, :frozen?)
+ assert_not_same chilled_string, +chilled_string
+
+ # @- the original string as mutable
+ assert_predicate(-chilled_string, :frozen?)
+ assert_not_same chilled_string, -chilled_string
+ end
+
+ def test_chilled_string_setivar
+ deprecated = Warning[:deprecated]
+ Warning[:deprecated] = false
+
+ String.class_eval <<~RUBY, __FILE__, __LINE__ + 1
+ def setivar!
+ @ivar = 42
+ @ivar
+ end
+ RUBY
+ chilled_string = eval('"chilled"')
+ begin
+ assert_equal 42, chilled_string.setivar!
+ ensure
+ String.undef_method(:setivar!)
+ end
+ ensure
+ Warning[:deprecated] = deprecated
+ end
+
+ def test_chilled_string_substring
+ deprecated = Warning[:deprecated]
+ Warning[:deprecated] = false
+ chilled_string = eval('"a chilled string."')
+ substring = chilled_string[0..-1]
+ assert_equal("a chilled string.", substring)
+ chilled_string[0..-1] = "This string is defrosted."
+ assert_equal("a chilled string.", substring)
+ ensure
+ Warning[:deprecated] = deprecated
+ end
+
private
def assert_bytesplice_result(expected, s, *args)
@@ -3540,6 +3674,42 @@ CODE
def assert_bytesplice_raise(e, s, *args)
assert_raise(e) { s.send(:bytesplice, *args) }
end
+
+ def assert_index_like(method, expected, string, match, *rest)
+ message = "#{method} with string does not affect $~"
+ /.*/ =~ message
+ md_before = $~
+ assert_equal(expected, string.__send__(method, match, *rest))
+ md_after = $~
+ case match
+ when Regexp
+ if expected
+ assert_not_nil(md_after)
+ assert_not_same(md_before, md_after)
+ else
+ assert_nil(md_after)
+ end
+ else
+ assert_same(md_before, md_after)
+ end
+ md_after
+ end
+
+ def assert_index(expected, string, match, *rest)
+ assert_index_like(:index, expected, string, match, *rest)
+ end
+
+ def assert_rindex(expected, string, match, *rest)
+ assert_index_like(:rindex, expected, string, match, *rest)
+ end
+
+ def assert_byteindex(expected, string, match, *rest)
+ assert_index_like(:byteindex, expected, string, match, *rest)
+ end
+
+ def assert_byterindex(expected, string, match, *rest)
+ assert_index_like(:byterindex, expected, string, match, *rest)
+ end
end
class TestString2 < TestString
diff --git a/test/ruby/test_string_memory.rb b/test/ruby/test_string_memory.rb
new file mode 100644
index 0000000000..3b4694f36f
--- /dev/null
+++ b/test/ruby/test_string_memory.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: false
+require 'test/unit'
+require 'objspace'
+
+class TestStringMemory < Test::Unit::TestCase
+ def capture_allocations(klass)
+ allocations = []
+
+ GC.start
+ GC.disable
+ generation = GC.count
+
+ ObjectSpace.trace_object_allocations do
+ yield
+
+ ObjectSpace.each_object(klass) do |instance|
+ allocations << instance if ObjectSpace.allocation_generation(instance) == generation
+ end
+ end
+
+ return allocations
+ ensure
+ GC.enable
+ end
+
+ def test_byteslice_prefix
+ string = ("a" * 100_000).freeze
+
+ allocations = capture_allocations(String) do
+ string.byteslice(0, 50_000)
+ end
+
+ assert_equal 1, allocations.size
+ end
+
+ def test_byteslice_postfix
+ string = ("a" * 100_000).freeze
+
+ allocations = capture_allocations(String) do
+ string.byteslice(50_000, 100_000)
+ end
+
+ assert_equal 1, allocations.size
+ end
+
+ def test_byteslice_postfix_twice
+ string = ("a" * 100_000).freeze
+
+ allocations = capture_allocations(String) do
+ string.byteslice(50_000, 100_000).byteslice(25_000, 50_000)
+ end
+
+ assert_equal 2, allocations.size
+ end
+end
diff --git a/test/ruby/test_struct.rb b/test/ruby/test_struct.rb
index 78a81c5200..3d727adf04 100644
--- a/test/ruby/test_struct.rb
+++ b/test/ruby/test_struct.rb
@@ -41,6 +41,14 @@ module TestStruct
end
end
+ def test_larger_than_largest_pool
+ count = (GC::INTERNAL_CONSTANTS[:RVARGC_MAX_ALLOCATE_SIZE] / RbConfig::SIZEOF["void*"]) + 1
+ list = Array(0..count)
+ klass = @Struct.new(*list.map { |i| :"a_#{i}"})
+ struct = klass.new(*list)
+ assert_equal 0, struct.a_0
+ end
+
def test_small_structs
names = [:a, :b, :c, :d]
1.upto(4) {|n|
@@ -526,6 +534,20 @@ module TestStruct
assert_equal [[:req, :_]], klass.instance_method(:c=).parameters
end
+ def test_named_structs_are_not_rooted
+ # [Bug #20311]
+ assert_no_memory_leak([], <<~PREP, <<~CODE, rss: true)
+ code = proc do
+ Struct.new("A")
+ Struct.send(:remove_const, :A)
+ end
+
+ 1_000.times(&code)
+ PREP
+ 50_000.times(&code)
+ CODE
+ end
+
class TopStruct < Test::Unit::TestCase
include TestStruct
diff --git a/test/ruby/test_super.rb b/test/ruby/test_super.rb
index 6a575b88c5..ce78e66c52 100644
--- a/test/ruby/test_super.rb
+++ b/test/ruby/test_super.rb
@@ -558,6 +558,18 @@ class TestSuper < Test::Unit::TestCase
end
end
+ def test_zsuper_kw_splat_not_mutable
+ extend(Module.new{def a(**k) k[:a] = 1 end})
+ extend(Module.new do
+ def a(**k)
+ before = k.dup
+ super
+ [before, k]
+ end
+ end)
+ assert_equal(*a)
+ end
+
def test_from_eval
bug10263 = '[ruby-core:65122] [Bug #10263a]'
a = Class.new do
diff --git a/test/ruby/test_symbol.rb b/test/ruby/test_symbol.rb
index 1d2a18d734..41b71097d1 100644
--- a/test/ruby/test_symbol.rb
+++ b/test/ruby/test_symbol.rb
@@ -118,6 +118,15 @@ class TestSymbol < Test::Unit::TestCase
end
end
+ def test_inspect_under_gc_compact_stress
+ omit "compaction doesn't work well on s390x" if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077
+ omit "very flaky on many platforms, more so with YJIT enabled" if defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled?
+ omit "very flaky on many platforms, more so with RJIT enabled" if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?
+ EnvUtil.under_gc_compact_stress do
+ assert_inspect_evaled(':testing')
+ end
+ end
+
def test_name
assert_equal("foo", :foo.name)
assert_same(:foo.name, :foo.name)
diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb
index b920dacc4a..44162f06cb 100644
--- a/test/ruby/test_syntax.rb
+++ b/test/ruby/test_syntax.rb
@@ -13,8 +13,7 @@ class TestSyntax < Test::Unit::TestCase
def assert_syntax_files(test)
srcdir = File.expand_path("../../..", __FILE__)
srcdir = File.join(srcdir, test)
- assert_separately(%W[--disable-gem - #{srcdir}],
- __FILE__, __LINE__, <<-'eom', timeout: Float::INFINITY)
+ assert_separately(%W[- #{srcdir}], __FILE__, __LINE__, <<-'eom', timeout: Float::INFINITY)
dir = ARGV.shift
for script in Dir["#{dir}/**/*.rb"].sort
assert_valid_syntax(IO::read(script), script)
@@ -77,97 +76,111 @@ class TestSyntax < Test::Unit::TestCase
def test_anonymous_block_forwarding
assert_syntax_error("def b; c(&); end", /no anonymous block parameter/)
+ assert_syntax_error("def b(&) ->(&) {c(&)} end", /anonymous block parameter is also used/)
+ assert_valid_syntax("def b(&) ->() {c(&)} end")
assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
begin;
- def b(&); c(&) end
- def c(&); yield 1 end
- a = nil
- b{|c| a = c}
- assert_equal(1, a)
-
- def inner
- yield
- end
+ def b(&); c(&) end
+ def c(&); yield 1 end
+ a = nil
+ b{|c| a = c}
+ assert_equal(1, a)
+
+ def inner
+ yield
+ end
- def block_only(&)
- inner(&)
- end
- assert_equal(1, block_only{1})
+ def block_only(&)
+ inner(&)
+ end
+ assert_equal(1, block_only{1})
- def pos(arg1, &)
- inner(&)
- end
- assert_equal(2, pos(nil){2})
+ def pos(arg1, &)
+ inner(&)
+ end
+ assert_equal(2, pos(nil){2})
- def pos_kwrest(arg1, **kw, &)
- inner(&)
- end
- assert_equal(3, pos_kwrest(nil){3})
+ def pos_kwrest(arg1, **kw, &)
+ inner(&)
+ end
+ assert_equal(3, pos_kwrest(nil){3})
- def no_kw(arg1, **nil, &)
- inner(&)
- end
- assert_equal(4, no_kw(nil){4})
+ def no_kw(arg1, **nil, &)
+ inner(&)
+ end
+ assert_equal(4, no_kw(nil){4})
- def rest_kw(*a, kwarg: 1, &)
- inner(&)
- end
- assert_equal(5, rest_kw{5})
+ def rest_kw(*a, kwarg: 1, &)
+ inner(&)
+ end
+ assert_equal(5, rest_kw{5})
- def kw(kwarg:1, &)
- inner(&)
- end
- assert_equal(6, kw{6})
+ def kw(kwarg:1, &)
+ inner(&)
+ end
+ assert_equal(6, kw{6})
- def pos_kw_kwrest(arg1, kwarg:1, **kw, &)
- inner(&)
- end
- assert_equal(7, pos_kw_kwrest(nil){7})
+ def pos_kw_kwrest(arg1, kwarg:1, **kw, &)
+ inner(&)
+ end
+ assert_equal(7, pos_kw_kwrest(nil){7})
- def pos_rkw(arg1, kwarg1:, &)
- inner(&)
- end
- assert_equal(8, pos_rkw(nil, kwarg1: nil){8})
+ def pos_rkw(arg1, kwarg1:, &)
+ inner(&)
+ end
+ assert_equal(8, pos_rkw(nil, kwarg1: nil){8})
- def all(arg1, arg2, *rest, post1, post2, kw1: 1, kw2: 2, okw1:, okw2:, &)
- inner(&)
- end
- assert_equal(9, all(nil, nil, nil, nil, okw1: nil, okw2: nil){9})
+ def all(arg1, arg2, *rest, post1, post2, kw1: 1, kw2: 2, okw1:, okw2:, &)
+ inner(&)
+ end
+ assert_equal(9, all(nil, nil, nil, nil, okw1: nil, okw2: nil){9})
- def all_kwrest(arg1, arg2, *rest, post1, post2, kw1: 1, kw2: 2, okw1:, okw2:, **kw, &)
- inner(&)
- end
- assert_equal(10, all_kwrest(nil, nil, nil, nil, okw1: nil, okw2: nil){10})
+ def all_kwrest(arg1, arg2, *rest, post1, post2, kw1: 1, kw2: 2, okw1:, okw2:, **kw, &)
+ inner(&)
+ end
+ assert_equal(10, all_kwrest(nil, nil, nil, nil, okw1: nil, okw2: nil){10})
end;
end
def test_anonymous_rest_forwarding
assert_syntax_error("def b; c(*); end", /no anonymous rest parameter/)
assert_syntax_error("def b; c(1, *); end", /no anonymous rest parameter/)
+ assert_syntax_error("def b(*) ->(*) {c(*)} end", /anonymous rest parameter is also used/)
+ assert_syntax_error("def b(a, *) ->(*) {c(1, *)} end", /anonymous rest parameter is also used/)
+ assert_syntax_error("def b(*) ->(a, *) {c(*)} end", /anonymous rest parameter is also used/)
+ assert_valid_syntax("def b(*) ->() {c(*)} end")
+ assert_valid_syntax("def b(a, *) ->() {c(1, *)} end")
+ assert_valid_syntax("def b(*) ->(a) {c(*)} end")
assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
begin;
- def b(*); c(*) end
- def c(*a); a end
- def d(*); b(*, *) end
- assert_equal([1, 2], b(1, 2))
- assert_equal([1, 2, 1, 2], d(1, 2))
+ def b(*); c(*) end
+ def c(*a); a end
+ def d(*); b(*, *) end
+ assert_equal([1, 2], b(1, 2))
+ assert_equal([1, 2, 1, 2], d(1, 2))
end;
end
def test_anonymous_keyword_rest_forwarding
assert_syntax_error("def b; c(**); end", /no anonymous keyword rest parameter/)
assert_syntax_error("def b; c(k: 1, **); end", /no anonymous keyword rest parameter/)
+ assert_syntax_error("def b(**) ->(**) {c(**)} end", /anonymous keyword rest parameter is also used/)
+ assert_syntax_error("def b(k:, **) ->(**) {c(k: 1, **)} end", /anonymous keyword rest parameter is also used/)
+ assert_syntax_error("def b(**) ->(k:, **) {c(**)} end", /anonymous keyword rest parameter is also used/)
+ assert_valid_syntax("def b(**) ->() {c(**)} end")
+ assert_valid_syntax("def b(k:, **) ->() {c(k: 1, **)} end")
+ assert_valid_syntax("def b(**) ->(k:) {c(**)} end")
assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
begin;
- def b(**); c(**) end
- def c(**kw); kw end
- def d(**); b(k: 1, **) end
- def e(**); b(**, k: 1) end
- def f(a: nil, **); b(**) end
- assert_equal({a: 1, k: 3}, b(a: 1, k: 3))
- assert_equal({a: 1, k: 3}, d(a: 1, k: 3))
- assert_equal({a: 1, k: 1}, e(a: 1, k: 3))
- assert_equal({k: 3}, f(a: 1, k: 3))
+ def b(**); c(**) end
+ def c(**kw); kw end
+ def d(**); b(k: 1, **) end
+ def e(**); b(**, k: 1) end
+ def f(a: nil, **); b(**) end
+ assert_equal({a: 1, k: 3}, b(a: 1, k: 3))
+ assert_equal({a: 1, k: 3}, d(a: 1, k: 3))
+ assert_equal({a: 1, k: 1}, e(a: 1, k: 3))
+ assert_equal({k: 3}, f(a: 1, k: 3))
end;
end
@@ -177,6 +190,7 @@ class TestSyntax < Test::Unit::TestCase
assert_syntax_error("def f(...); g(0, *); end", /no anonymous rest parameter/)
assert_syntax_error("def f(...); g(**); end", /no anonymous keyword rest parameter/)
assert_syntax_error("def f(...); g(x: 1, **); end", /no anonymous keyword rest parameter/)
+ assert_syntax_error("def f(...); g(&); end", /no anonymous block parameter/)
end
def test_newline_in_block_parameters
@@ -220,6 +234,7 @@ class TestSyntax < Test::Unit::TestCase
def test_array_kwsplat_hash
kw = {}
h = {a: 1}
+ a = []
assert_equal([], [**{}])
assert_equal([], [**kw])
assert_equal([h], [**h])
@@ -234,6 +249,20 @@ class TestSyntax < Test::Unit::TestCase
assert_equal([1, kw], [1, kw])
assert_equal([1, h], [1, h])
+ assert_equal([], [*a, **{}])
+ assert_equal([], [*a, **kw])
+ assert_equal([h], [*a, **h])
+ assert_equal([{}], [*a, {}])
+ assert_equal([kw], [*a, kw])
+ assert_equal([h], [*a, h])
+
+ assert_equal([1], [1, *a, **{}])
+ assert_equal([1], [1, *a, **kw])
+ assert_equal([1, h], [1, *a, **h])
+ assert_equal([1, {}], [1, *a, {}])
+ assert_equal([1, kw], [1, *a, kw])
+ assert_equal([1, h], [1, *a, h])
+
assert_equal([], [**kw, **kw])
assert_equal([], [**kw, **{}, **kw])
assert_equal([1], [1, **kw, **{}, **kw])
@@ -336,6 +365,12 @@ class TestSyntax < Test::Unit::TestCase
assert_warn(/duplicated/) {r = eval("a.f(**{k: a.add(1), j: a.add(2), k: a.add(3), k: a.add(4)})")}
assert_equal(4, r)
assert_equal([1, 2, 3, 4], a)
+ a.clear
+ r = nil
+ _z = {}
+ assert_warn(/duplicated/) {r = eval("a.f(k: a.add(1), **_z, k: a.add(2))")}
+ assert_equal(2, r)
+ assert_equal([1, 2], a)
end
def test_keyword_empty_splat
@@ -362,6 +397,7 @@ class TestSyntax < Test::Unit::TestCase
assert_syntax_error("def foo(var: var) var end", message)
assert_syntax_error("def foo(var: bar(var)) var end", message)
assert_syntax_error("def foo(var: bar {var}) var end", message)
+ assert_syntax_error("def foo(var: (1 in ^var)); end", message)
o = Object.new
assert_warn("") do
@@ -427,6 +463,7 @@ class TestSyntax < Test::Unit::TestCase
assert_syntax_error("def foo(var = bar {var}) var end", message)
assert_syntax_error("def foo(var = (def bar;end; var)) var end", message)
assert_syntax_error("def foo(var = (def self.bar;end; var)) var end", message)
+ assert_syntax_error("def foo(var = (1 in ^var)); end", message)
o = Object.new
assert_warn("") do
@@ -470,7 +507,7 @@ class TestSyntax < Test::Unit::TestCase
def test_warn_balanced
warning = <<WARN
-test:1: warning: `%s' after local variable or literal is interpreted as binary operator
+test:1: warning: '%s' after local variable or literal is interpreted as binary operator
test:1: warning: even though it seems like %s
WARN
[
@@ -637,6 +674,8 @@ WARN
assert_equal(42, obj.foo(42))
assert_equal(42, obj.foo(2, _: 0))
assert_equal(2, obj.foo(x: 2, _: 0))
+ ensure
+ self.class.remove_method(:foo)
end
def test_duplicated_opt_kw
@@ -676,7 +715,7 @@ WARN
end
def test_duplicated_when
- w = 'warning: duplicated `when\' clause with line 3 is ignored'
+ w = 'warning: duplicated \'when\' clause with line 3 is ignored'
assert_warning(/3: #{w}.+4: #{w}.+4: #{w}.+5: #{w}.+5: #{w}/m) {
eval %q{
case 1
@@ -696,10 +735,28 @@ WARN
end
}
}
+ assert_warning(/3: #{w}/m) {
+ eval %q{
+ case 1
+ when __LINE__, __LINE__
+ when 3, 3
+ when 3, 3
+ end
+ }
+ }
+ assert_warning(/3: #{w}/m) {
+ eval %q{
+ case 1
+ when __FILE__, __FILE__
+ when "filename", "filename"
+ when "filename", "filename"
+ end
+ }, binding, "filename"
+ }
end
def test_duplicated_when_check_option
- w = /duplicated `when\' clause with line 3 is ignored/
+ w = /duplicated \'when\' clause with line 3 is ignored/
assert_in_out_err(%[-wc], "#{<<~"begin;"}\n#{<<~'end;'}", ["Syntax OK"], w)
begin;
case 1
@@ -978,6 +1035,14 @@ eom
assert_not_match(/end-of-input/, e.message)
end
+ def test_invalid_regexp
+ bug20295 = '[ruby-core:116913] [Bug #20295]'
+
+ assert_syntax_error("/[/=~s", /premature end of char-class/, bug20295)
+ assert_syntax_error("/(?<>)/=~s", /group name is empty/, bug20295)
+ assert_syntax_error("/(?<a>[)/=~s", /premature end of char-class/, bug20295)
+ end
+
def test_lineno_operation_brace_block
expected = __LINE__ + 1
actual = caller_lineno\
@@ -1189,12 +1254,18 @@ eom
assert_warn(/string literal in condition/) do
eval('1 if ""')
end
+ assert_warning(/string literal in condition/) do
+ eval('1 if __FILE__')
+ end
assert_warn(/regex literal in condition/) do
eval('1 if //')
end
assert_warning(/literal in condition/) do
eval('1 if 1')
end
+ assert_warning(/literal in condition/) do
+ eval('1 if __LINE__')
+ end
assert_warning(/symbol literal in condition/) do
eval('1 if :foo')
end
@@ -1268,7 +1339,7 @@ eom
end
def test_parenthesised_statement_argument
- assert_syntax_error("foo(bar rescue nil)", /unexpected `rescue' modifier/)
+ assert_syntax_error("foo(bar rescue nil)", /unexpected 'rescue' modifier/)
assert_valid_syntax("foo (bar rescue nil)")
end
@@ -1318,6 +1389,25 @@ eom
assert_valid_syntax 'p :foo, {proc do end => proc do end, b: proc do end}', bug13073
end
+ def test_invalid_encoding_symbol
+ assert_syntax_error('{"\xC3": 1}', "invalid symbol")
+ end
+
+ def test_invalid_symbol_in_hash_memory_leak
+ assert_no_memory_leak([], "#{<<-'begin;'}", "#{<<-'end;'}", rss: true)
+ str = '{"\xC3": 1}'.force_encoding("UTF-8")
+ code = proc do
+ eval(str)
+ raise "unreachable"
+ rescue SyntaxError
+ end
+
+ 1_000.times(&code)
+ begin;
+ 1_000_000.times(&code)
+ end;
+ end
+
def test_do_after_local_variable
obj = Object.new
def obj.m; yield; end
@@ -1366,9 +1456,10 @@ eom
"#{return}"
raise((return; "should not raise"))
begin raise; ensure return; end; self
- begin raise; ensure return; end and self
nil&defined?0--begin e=no_method_error(); return; 0;end
return puts('ignored') #=> ignored
+ BEGIN {return}
+ END {return if false}
end;
.split(/\n/).map {|s|[(line+=1), *s.split(/#=> /, 2)]}
failed = proc do |n, s|
@@ -1398,6 +1489,54 @@ eom
end
end
+ def test_eval_return_toplevel
+ feature4840 = '[ruby-core:36785] [Feature #4840]'
+ line = __LINE__+2
+ code = "#{<<~"begin;"}#{<<~'end;'}"
+ begin;
+ eval "return"; raise
+ begin eval "return"; rescue SystemExit; exit false; end
+ begin eval "return"; ensure puts "ensured"; end #=> ensured
+ begin ensure eval "return"; end
+ begin raise; ensure; eval "return"; end
+ begin raise; rescue; eval "return"; end
+ eval "return false"; raise
+ eval "return 1"; raise
+ "#{eval "return"}"
+ raise((eval "return"; "should not raise"))
+ begin raise; ensure eval "return"; end; self
+ begin raise; ensure eval "return"; end and self
+ eval "return puts('ignored')" #=> ignored
+ BEGIN {eval "return"}
+ end;
+ .split(/\n/).map {|s|[(line+=1), *s.split(/#=> /, 2)]}
+ failed = proc do |n, s|
+ RubyVM::InstructionSequence.compile(s, __FILE__, nil, n).disasm
+ end
+ Tempfile.create(%w"test_return_ .rb") do |lib|
+ lib.close
+ args = %W[-W0 -r#{lib.path}]
+ all_assertions_foreach(feature4840, *[:main, :lib].product([:class, :top], code)) do |main, klass, (n, s, *ex)|
+ if klass == :class
+ s = "class X; #{s}; end"
+ if main == :main
+ assert_in_out_err(%[-W0], s, ex, /return/, proc {failed[n, s]}, success: false)
+ else
+ File.write(lib, s)
+ assert_in_out_err(args, "", ex, /return/, proc {failed[n, s]}, success: false)
+ end
+ else
+ if main == :main
+ assert_in_out_err(%[-W0], s, ex, [], proc {failed[n, s]}, success: true)
+ else
+ File.write(lib, s)
+ assert_in_out_err(args, "", ex, [], proc {failed[n, s]}, success: true)
+ end
+ end
+ end
+ end
+ end
+
def test_return_toplevel_with_argument
assert_warn(/argument of top-level return is ignored/) {eval("return 1")}
end
@@ -1406,6 +1545,20 @@ eom
assert_in_out_err(['-e', 'class TestSyntax; proc{ return }.call; end'], "", [], /^-e:1:.*unexpected return \(LocalJumpError\)/)
end
+ def test_return_in_END
+ assert_normal_exit('END {return}')
+ end
+
+ def test_return_in_BEGIN_in_eval
+ # `BEGIN` in `eval` is allowed, even inside a method, and `return`
+ # from that block exits from that method without `LocalJumpError`.
+ obj = Object.new
+ def obj.ok
+ eval("BEGIN {return :ok}")
+ end
+ assert_equal :ok, assert_nothing_raised(LocalJumpError) {obj.ok}
+ end
+
def test_syntax_error_in_rescue
bug12613 = '[ruby-core:76531] [Bug #12613]'
assert_syntax_error("#{<<-"begin;"}\n#{<<-"end;"}", /Invalid retry/, bug12613)
@@ -1625,6 +1778,29 @@ eom
def test_command_with_cmd_brace_block
assert_valid_syntax('obj.foo (1) {}')
assert_valid_syntax('obj::foo (1) {}')
+ assert_valid_syntax('bar {}')
+ assert_valid_syntax('Bar {}')
+ assert_valid_syntax('bar() {}')
+ assert_valid_syntax('Bar() {}')
+ assert_valid_syntax('Foo::bar {}')
+ assert_valid_syntax('Foo::Bar {}')
+ assert_valid_syntax('Foo::bar() {}')
+ assert_valid_syntax('Foo::Bar() {}')
+ end
+
+ def test_command_newline_in_tlparen_args
+ assert_valid_syntax("p (1\n2\n),(3),(4)")
+ assert_valid_syntax("p (\n),(),()")
+ assert_valid_syntax("a.b (1\n2\n),(3),(4)")
+ assert_valid_syntax("a.b (\n),(),()")
+ end
+
+ def test_command_semicolon_in_tlparen_at_the_first_arg
+ bug19281 = '[ruby-core:111499] [Bug #19281]'
+ assert_valid_syntax('p (1;2),(3),(4)', bug19281)
+ assert_valid_syntax('p (;),(),()', bug19281)
+ assert_valid_syntax('a.b (1;2),(3),(4)', bug19281)
+ assert_valid_syntax('a.b (;),(),()', bug19281)
end
def test_numbered_parameter
@@ -1655,7 +1831,7 @@ eom
assert_syntax_error('def x(_4) end', /_4 is reserved for numbered parameter/)
assert_syntax_error('def _5; end', /_5 is reserved for numbered parameter/)
assert_syntax_error('def self._6; end', /_6 is reserved for numbered parameter/)
- assert_raise_with_message(NameError, /undefined local variable or method `_1'/) {
+ assert_raise_with_message(NameError, /undefined local variable or method '_1'/) {
eval('_1')
}
['class C', 'class << C', 'module M', 'def m', 'def o.m'].each do |c|
@@ -1673,6 +1849,58 @@ eom
assert_valid_syntax("proc {def foo(_);end;_1}")
assert_valid_syntax("p { [_1 **2] }")
+ assert_valid_syntax("proc {_1;def foo();end;_1}")
+ end
+
+ def test_it
+ assert_valid_syntax('proc {it}')
+ assert_syntax_error('[1,2].then {it+_2}', /'it' is already used/)
+ assert_syntax_error('[1,2].then {_2+it}', /numbered parameter is already used/)
+ assert_equal([1, 2], eval('[1,2].then {it}'))
+ assert_syntax_error('[1,2].then {"#{it}#{_2}"}', /'it' is already used/)
+ assert_syntax_error('[1,2].then {"#{_2}#{it}"}', /numbered parameter is already used/)
+ assert_syntax_error('->{it+_2}.call(1,2)', /'it' is already used/)
+ assert_syntax_error('->{_2+it}.call(1,2)', /numbered parameter is already used/)
+ assert_equal(4, eval('->(a=->{it}){a}.call.call(4)'))
+ assert_equal(5, eval('-> a: ->{it} {a}.call.call(5)'))
+ assert_syntax_error('proc {|| it}', /ordinary parameter is defined/)
+ assert_syntax_error('proc {|;a| it}', /ordinary parameter is defined/)
+ assert_syntax_error("proc {|\n| it}", /ordinary parameter is defined/)
+ assert_syntax_error('proc {|x| it}', /ordinary parameter is defined/)
+ assert_equal([1, 2], eval('1.then {[it, 2.then {_1}]}'))
+ assert_equal([2, 1], eval('1.then {[2.then {_1}, it]}'))
+ assert_syntax_error('->(){it}', /ordinary parameter is defined/)
+ assert_syntax_error('->(x){it}', /ordinary parameter is defined/)
+ assert_syntax_error('->x{it}', /ordinary parameter is defined/)
+ assert_syntax_error('->x:_1{}', /ordinary parameter is defined/)
+ assert_syntax_error('->x=it{}', /ordinary parameter is defined/)
+ assert_valid_syntax('-> {it; -> {_2}}')
+ assert_valid_syntax('-> {-> {it}; _2}')
+ assert_equal([1, nil], eval('proc {that=it; it=nil; [that, it]}.call(1)'))
+ assert_equal(1, eval('proc {it = 1}.call'))
+ assert_warning(/1: warning: assigned but unused variable - it/) {
+ assert_equal(2, eval('a=Object.new; def a.foo; it = 2; end; a.foo'))
+ }
+ assert_equal(3, eval('proc {|it| it}.call(3)'))
+ assert_equal(4, eval('a=Object.new; def a.foo(it); it; end; a.foo(4)'))
+ assert_equal(5, eval('a=Object.new; def a.it; 5; end; a.it'))
+ assert_equal(6, eval('a=Class.new; a.class_eval{ def it; 6; end }; a.new.it'))
+ assert_raise_with_message(NameError, /undefined local variable or method 'it'/) do
+ eval('it')
+ end
+ ['class C', 'class << C', 'module M', 'def m', 'def o.m'].each do |c|
+ assert_valid_syntax("->{#{c};->{it};end;it}\n")
+ assert_valid_syntax("->{it;#{c};->{it};end}\n")
+ end
+ 1.times do
+ [
+ assert_equal(0, it),
+ assert_equal([:a], eval('[:a].map{it}')),
+ assert_raise(NameError) {eval('it')},
+ ]
+ end
+ assert_valid_syntax('proc {def foo(_);end;it}')
+ assert_syntax_error('p { [it **2] }', /unexpected \*\*arg/)
end
def test_value_expr_in_condition
@@ -1683,6 +1911,11 @@ eom
assert_valid_syntax("tap {a = (break unless true)}")
end
+ def test_value_expr_in_singleton
+ mesg = /void value expression/
+ assert_syntax_error("class << (return); end", mesg)
+ end
+
def test_tautological_condition
assert_valid_syntax("def f() return if false and invalid; nil end")
assert_valid_syntax("def f() return unless true or invalid; nil end")
@@ -1922,6 +2155,13 @@ eom
assert_equal 0...1, exp.call(a: 0)
end
+ def test_argument_forwarding_with_super
+ assert_valid_syntax('def foo(...) super {}; end')
+ assert_valid_syntax('def foo(...) super() {}; end')
+ assert_syntax_error('def foo(...) super(...) {}; end', /both block arg and actual block/)
+ assert_syntax_error('def foo(...) super(1, ...) {}; end', /both block arg and actual block/)
+ end
+
def test_class_module_Object_ancestors
assert_separately([], <<-RUBY)
m = Module.new
diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb
index e6f347c486..da14c429e6 100644
--- a/test/ruby/test_thread.rb
+++ b/test/ruby/test_thread.rb
@@ -3,6 +3,7 @@
require 'test/unit'
require "rbconfig/sizeof"
require "timeout"
+require "fiddle"
class TestThread < Test::Unit::TestCase
class Thread < ::Thread
@@ -395,7 +396,7 @@ class TestThread < Test::Unit::TestCase
end
INPUT
- assert_in_out_err(%w(--disable-gems -d), <<-INPUT, %w(false 2), %r".+")
+ assert_in_out_err(%w(-d), <<-INPUT, %w(false 2), %r".+")
p Thread.abort_on_exception
begin
t = Thread.new { raise }
@@ -1434,7 +1435,8 @@ q.pop
Thread.pass until th1.stop?
# After a thread starts (and execute `sleep`), it returns native_thread_id
- assert_instance_of Integer, th1.native_thread_id
+ native_tid = th1.native_thread_id
+ assert_instance_of Integer, native_tid if native_tid # it can be nil
th1.wakeup
Thread.pass while th1.alive?
@@ -1443,6 +1445,35 @@ q.pop
assert_nil th1.native_thread_id
end
+ def test_thread_native_thread_id_across_fork_on_linux
+ rtld_default = Fiddle.dlopen(nil)
+ omit "this test is only for Linux" unless rtld_default.sym_defined?('gettid')
+
+ gettid = Fiddle::Function.new(rtld_default['gettid'], [], Fiddle::TYPE_INT)
+
+ parent_thread_id = Thread.main.native_thread_id
+ real_parent_thread_id = gettid.call
+
+ assert_equal real_parent_thread_id, parent_thread_id
+
+ child_lines = nil
+ IO.popen('-') do |pipe|
+ if pipe
+ # parent
+ child_lines = pipe.read.lines
+ else
+ # child
+ puts Thread.main.native_thread_id
+ puts gettid.call
+ end
+ end
+ child_thread_id = child_lines[0].chomp.to_i
+ real_child_thread_id = child_lines[1].chomp.to_i
+
+ assert_equal real_child_thread_id, child_thread_id
+ refute_equal parent_thread_id, child_thread_id
+ end
+
def test_thread_interrupt_for_killed_thread
opts = { timeout: 5, timeout_error: nil }
diff --git a/test/ruby/test_thread_queue.rb b/test/ruby/test_thread_queue.rb
index fd77853f0e..545bf98888 100644
--- a/test/ruby/test_thread_queue.rb
+++ b/test/ruby/test_thread_queue.rb
@@ -19,6 +19,15 @@ class TestThreadQueue < Test::Unit::TestCase
}
end
+ def test_freeze
+ assert_raise(TypeError) {
+ Queue.new.freeze
+ }
+ assert_raise(TypeError) {
+ SizedQueue.new(5).freeze
+ }
+ end
+
def test_queue
grind(5, 1000, 15, Queue)
end
diff --git a/test/ruby/test_time.rb b/test/ruby/test_time.rb
index f47a49d3e0..2a541bbe8c 100644
--- a/test/ruby/test_time.rb
+++ b/test/ruby/test_time.rb
@@ -75,7 +75,9 @@ class TestTime < Test::Unit::TestCase
Time.new("2020-12-25 00 +09:00")
}
+ assert_equal(Time.new(2021), Time.new("2021"))
assert_equal(Time.new(2021, 12, 25, in: "+09:00"), Time.new("2021-12-25+09:00"))
+ assert_equal(Time.new(2021, 12, 25, in: "+09:00"), Time.new("2021-12-25+09:00", in: "-01:00"))
assert_equal(0.123456r, Time.new("2021-12-25 00:00:00.123456 +09:00").subsec)
assert_equal(0.123456789r, Time.new("2021-12-25 00:00:00.123456789876 +09:00").subsec)
@@ -138,6 +140,18 @@ class TestTime < Test::Unit::TestCase
assert_raise_with_message(ArgumentError, /mon out of range/) {
Time.new("2020-17-25 00:56:17 +0900")
}
+ assert_raise_with_message(ArgumentError, /no time information/) {
+ Time.new("2020-12")
+ }
+ assert_raise_with_message(ArgumentError, /no time information/) {
+ Time.new("2020-12-02")
+ }
+ assert_raise_with_message(ArgumentError, /can't parse/) {
+ Time.new(" 2020-12-02 00:00:00")
+ }
+ assert_raise_with_message(ArgumentError, /can't parse/) {
+ Time.new("2020-12-02 00:00:00 ")
+ }
end
def test_time_add()
@@ -425,7 +439,7 @@ class TestTime < Test::Unit::TestCase
end
def test_marshal_zone_gc
- assert_separately(%w(--disable-gems), <<-'end;', timeout: 30)
+ assert_separately([], <<-'end;', timeout: 30)
ENV["TZ"] = "JST-9"
s = Marshal.dump(Time.now)
t = Marshal.load(s)
@@ -1394,7 +1408,10 @@ class TestTime < Test::Unit::TestCase
def test_memsize
# Time objects are common in some code, try to keep them small
omit "Time object size test" if /^(?:i.?86|x86_64)-linux/ !~ RUBY_PLATFORM
- omit "GC is in debug" if GC::INTERNAL_CONSTANTS[:DEBUG]
+ omit "GC is in debug" if GC::INTERNAL_CONSTANTS[:RVALUE_OVERHEAD] > 0
+ omit "memsize is not accurate due to using malloc_usable_size" if GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] == 1
+ omit "Only run this test on 64-bit" if RbConfig::SIZEOF["void*"] != 8
+
require 'objspace'
t = Time.at(0)
sizeof_timew =
@@ -1405,7 +1422,7 @@ class TestTime < Test::Unit::TestCase
end
sizeof_vtm = RbConfig::SIZEOF["void*"] * 4 + 8
expect = GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE] + sizeof_timew + sizeof_vtm
- assert_equal expect, ObjectSpace.memsize_of(t)
+ assert_operator ObjectSpace.memsize_of(t), :<=, expect
rescue LoadError => e
omit "failed to load objspace: #{e.message}"
end
diff --git a/test/ruby/test_time_tz.rb b/test/ruby/test_time_tz.rb
index 531d76b040..f66cd9bec2 100644
--- a/test/ruby/test_time_tz.rb
+++ b/test/ruby/test_time_tz.rb
@@ -695,6 +695,13 @@ module TestTimeTZ::WithTZ
assert_equal(t.dst?, t2.dst?)
end
+ def subtest_fractional_second(time_class, tz, tzarg, tzname, abbr, utc_offset)
+ t = time_class.new(2024, 1, 1, 23, 59, 59.9r, tzarg)
+ assert_equal(utc_offset[t.dst? ? 1 : 0], t.utc_offset)
+ t = time_class.new(2024, 7, 1, 23, 59, 59.9r, tzarg)
+ assert_equal(utc_offset[t.dst? ? 1 : 0], t.utc_offset)
+ end
+
def test_invalid_zone
make_timezone("INVALID", "INV", 0)
rescue => e
@@ -719,6 +726,7 @@ module TestTimeTZ::WithTZ
"Asia/Tokyo" => ["JST", +9*3600],
"America/Los_Angeles" => ["PST", -8*3600, "PDT", -7*3600],
"Africa/Ndjamena" => ["WAT", +1*3600],
+ "Etc/UTC" => ["UTC", 0],
}
def make_timezone(tzname, abbr, utc_offset, abbr2 = nil, utc_offset2 = nil)
diff --git a/test/ruby/test_transcode.rb b/test/ruby/test_transcode.rb
index 24ee9b9533..ceef19e7ea 100644
--- a/test/ruby/test_transcode.rb
+++ b/test/ruby/test_transcode.rb
@@ -10,9 +10,9 @@ class TestTranscode < Test::Unit::TestCase
assert_raise(Encoding::ConverterNotFoundError) { 'abc'.encode!('foo', 'bar') }
assert_raise(Encoding::ConverterNotFoundError) { 'abc'.force_encoding('utf-8').encode('foo') }
assert_raise(Encoding::ConverterNotFoundError) { 'abc'.force_encoding('utf-8').encode!('foo') }
- assert_raise(Encoding::UndefinedConversionError) { "\x80".encode('utf-8','ASCII-8BIT') }
- assert_raise(Encoding::InvalidByteSequenceError) { "\x80".encode('utf-8','US-ASCII') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA5".encode('utf-8','iso-8859-3') }
+ assert_undefined_in("\x80", 'ASCII-8BIT')
+ assert_invalid_in("\x80", 'US-ASCII')
+ assert_undefined_in("\xA5", 'iso-8859-3')
assert_raise(FrozenError) { 'hello'.freeze.encode!('iso-8859-1') }
assert_raise(FrozenError) { '\u3053\u3093\u306b\u3061\u306f'.freeze.encode!('iso-8859-1') } # ã“ã‚“ã«ã¡ã¯
end
@@ -52,16 +52,6 @@ class TestTranscode < Test::Unit::TestCase
assert_equal("\u20AC"*200000, ("\xA4"*200000).encode!('utf-8', 'iso-8859-15'))
end
- def check_both_ways(utf8, raw, encoding)
- assert_equal(utf8.force_encoding('utf-8'), raw.encode('utf-8', encoding),utf8.dump+raw.dump)
- assert_equal(raw.force_encoding(encoding), utf8.encode(encoding, 'utf-8'))
- end
-
- def check_both_ways2(str1, enc1, str2, enc2)
- assert_equal(str1.force_encoding(enc1), str2.encode(enc1, enc2))
- assert_equal(str2.force_encoding(enc2), str1.encode(enc2, enc1))
- end
-
def test_encoding_of_ascii_originating_from_binary
binary_string = [0x82, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20,
0x61, 0x20, 0x76, 0x65, 0x72, 0x79, 0x20, 0x6c, 0x6f,
@@ -188,16 +178,16 @@ class TestTranscode < Test::Unit::TestCase
def test_windows_874
check_both_ways("\u20AC", "\x80", 'windows-874') # €
- assert_raise(Encoding::UndefinedConversionError) { "\x81".encode("utf-8", 'windows-874') }
- assert_raise(Encoding::UndefinedConversionError) { "\x84".encode("utf-8", 'windows-874') }
+ assert_undefined_in("\x81", 'windows-874')
+ assert_undefined_in("\x84", 'windows-874')
check_both_ways("\u2026", "\x85", 'windows-874') # …
- assert_raise(Encoding::UndefinedConversionError) { "\x86".encode("utf-8", 'windows-874') }
- assert_raise(Encoding::UndefinedConversionError) { "\x8F".encode("utf-8", 'windows-874') }
- assert_raise(Encoding::UndefinedConversionError) { "\x90".encode("utf-8", 'windows-874') }
+ assert_undefined_in("\x86", 'windows-874')
+ assert_undefined_in("\x8F", 'windows-874')
+ assert_undefined_in("\x90", 'windows-874')
check_both_ways("\u2018", "\x91", 'windows-874') # ‘
check_both_ways("\u2014", "\x97", 'windows-874') # —
- assert_raise(Encoding::UndefinedConversionError) { "\x98".encode("utf-8", 'windows-874') }
- assert_raise(Encoding::UndefinedConversionError) { "\x9F".encode("utf-8", 'windows-874') }
+ assert_undefined_in("\x98", 'windows-874')
+ assert_undefined_in("\x9F", 'windows-874')
check_both_ways("\u00A0", "\xA0", 'windows-874') # non-breaking space
check_both_ways("\u0E0F", "\xAF", 'windows-874') # à¸
check_both_ways("\u0E10", "\xB0", 'windows-874') # à¸
@@ -206,31 +196,31 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u0E2F", "\xCF", 'windows-874') # ฯ
check_both_ways("\u0E30", "\xD0", 'windows-874') # ะ
check_both_ways("\u0E3A", "\xDA", 'windows-874') # ฺ
- assert_raise(Encoding::UndefinedConversionError) { "\xDB".encode("utf-8", 'windows-874') }
- assert_raise(Encoding::UndefinedConversionError) { "\xDE".encode("utf-8", 'windows-874') }
+ assert_undefined_in("\xDB", 'windows-874')
+ assert_undefined_in("\xDE", 'windows-874')
check_both_ways("\u0E3F", "\xDF", 'windows-874') # ฿
check_both_ways("\u0E40", "\xE0", 'windows-874') # เ
check_both_ways("\u0E4F", "\xEF", 'windows-874') # à¹
check_both_ways("\u0E50", "\xF0", 'windows-874') # à¹
check_both_ways("\u0E5B", "\xFB", 'windows-874') # ๛
- assert_raise(Encoding::UndefinedConversionError) { "\xFC".encode("utf-8", 'windows-874') }
- assert_raise(Encoding::UndefinedConversionError) { "\xFF".encode("utf-8", 'windows-874') }
+ assert_undefined_in("\xFC", 'windows-874')
+ assert_undefined_in("\xFF", 'windows-874')
end
def test_windows_1250
check_both_ways("\u20AC", "\x80", 'windows-1250') # €
- assert_raise(Encoding::UndefinedConversionError) { "\x81".encode("utf-8", 'windows-1250') }
+ assert_undefined_in("\x81", 'windows-1250')
check_both_ways("\u201A", "\x82", 'windows-1250') # ‚
- assert_raise(Encoding::UndefinedConversionError) { "\x83".encode("utf-8", 'windows-1250') }
+ assert_undefined_in("\x83", 'windows-1250')
check_both_ways("\u201E", "\x84", 'windows-1250') # „
check_both_ways("\u2021", "\x87", 'windows-1250') # ‡
- assert_raise(Encoding::UndefinedConversionError) { "\x88".encode("utf-8", 'windows-1250') }
+ assert_undefined_in("\x88", 'windows-1250')
check_both_ways("\u2030", "\x89", 'windows-1250') # ‰
check_both_ways("\u0179", "\x8F", 'windows-1250') # Ź
- assert_raise(Encoding::UndefinedConversionError) { "\x90".encode("utf-8", 'windows-1250') }
+ assert_undefined_in("\x90", 'windows-1250')
check_both_ways("\u2018", "\x91", 'windows-1250') # ‘
check_both_ways("\u2014", "\x97", 'windows-1250') # —
- assert_raise(Encoding::UndefinedConversionError) { "\x98".encode("utf-8", 'windows-1250') }
+ assert_undefined_in("\x98", 'windows-1250')
check_both_ways("\u2122", "\x99", 'windows-1250') # â„¢
check_both_ways("\u00A0", "\xA0", 'windows-1250') # non-breaking space
check_both_ways("\u017B", "\xAF", 'windows-1250') # Å»
@@ -251,7 +241,7 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u20AC", "\x88", 'windows-1251') # €
check_both_ways("\u040F", "\x8F", 'windows-1251') # Ð
check_both_ways("\u0452", "\x90", 'windows-1251') # Ñ’
- assert_raise(Encoding::UndefinedConversionError) { "\x98".encode("utf-8", 'windows-1251') }
+ assert_undefined_in("\x98", 'windows-1251')
check_both_ways("\u045F", "\x9F", 'windows-1251') # ÑŸ
check_both_ways("\u00A0", "\xA0", 'windows-1251') # non-breaking space
check_both_ways("\u0407", "\xAF", 'windows-1251') # Ї
@@ -269,16 +259,16 @@ class TestTranscode < Test::Unit::TestCase
def test_windows_1252
check_both_ways("\u20AC", "\x80", 'windows-1252') # €
- assert_raise(Encoding::UndefinedConversionError) { "\x81".encode("utf-8", 'windows-1252') }
+ assert_undefined_in("\x81", 'windows-1252')
check_both_ways("\u201A", "\x82", 'windows-1252') # ‚
check_both_ways("\u0152", "\x8C", 'windows-1252') # >Å’
- assert_raise(Encoding::UndefinedConversionError) { "\x8D".encode("utf-8", 'windows-1252') }
+ assert_undefined_in("\x8D", 'windows-1252')
check_both_ways("\u017D", "\x8E", 'windows-1252') # Ž
- assert_raise(Encoding::UndefinedConversionError) { "\x8F".encode("utf-8", 'windows-1252') }
- assert_raise(Encoding::UndefinedConversionError) { "\x90".encode("utf-8", 'windows-1252') }
+ assert_undefined_in("\x8F", 'windows-1252')
+ assert_undefined_in("\x90", 'windows-1252')
check_both_ways("\u2018", "\x91", 'windows-1252') #‘
check_both_ways("\u0153", "\x9C", 'windows-1252') # Å“
- assert_raise(Encoding::UndefinedConversionError) { "\x9D".encode("utf-8", 'windows-1252') }
+ assert_undefined_in("\x9D", 'windows-1252')
check_both_ways("\u017E", "\x9E", 'windows-1252') # ž
check_both_ways("\u00A0", "\xA0", 'windows-1252') # non-breaking space
check_both_ways("\u00AF", "\xAF", 'windows-1252') # ¯
@@ -296,24 +286,24 @@ class TestTranscode < Test::Unit::TestCase
def test_windows_1253
check_both_ways("\u20AC", "\x80", 'windows-1253') # €
- assert_raise(Encoding::UndefinedConversionError) { "\x81".encode("utf-8", 'windows-1253') }
+ assert_undefined_in("\x81", 'windows-1253')
check_both_ways("\u201A", "\x82", 'windows-1253') # ‚
check_both_ways("\u2021", "\x87", 'windows-1253') # ‡
- assert_raise(Encoding::UndefinedConversionError) { "\x88".encode("utf-8", 'windows-1253') }
+ assert_undefined_in("\x88", 'windows-1253')
check_both_ways("\u2030", "\x89", 'windows-1253') # ‰
- assert_raise(Encoding::UndefinedConversionError) { "\x8A".encode("utf-8", 'windows-1253') }
+ assert_undefined_in("\x8A", 'windows-1253')
check_both_ways("\u2039", "\x8B", 'windows-1253') # ‹
- assert_raise(Encoding::UndefinedConversionError) { "\x8C".encode("utf-8", 'windows-1253') }
- assert_raise(Encoding::UndefinedConversionError) { "\x8F".encode("utf-8", 'windows-1253') }
- assert_raise(Encoding::UndefinedConversionError) { "\x90".encode("utf-8", 'windows-1253') }
+ assert_undefined_in("\x8C", 'windows-1253')
+ assert_undefined_in("\x8F", 'windows-1253')
+ assert_undefined_in("\x90", 'windows-1253')
check_both_ways("\u2018", "\x91", 'windows-1253') # ‘
check_both_ways("\u2014", "\x97", 'windows-1253') # —
- assert_raise(Encoding::UndefinedConversionError) { "\x98".encode("utf-8", 'windows-1253') }
+ assert_undefined_in("\x98", 'windows-1253')
check_both_ways("\u2122", "\x99", 'windows-1253') # â„¢
- assert_raise(Encoding::UndefinedConversionError) { "\x9A".encode("utf-8", 'windows-1253') }
+ assert_undefined_in("\x9A", 'windows-1253')
check_both_ways("\u203A", "\x9B", 'windows-1253') # ›
- assert_raise(Encoding::UndefinedConversionError) { "\x9C".encode("utf-8", 'windows-1253') }
- assert_raise(Encoding::UndefinedConversionError) { "\x9F".encode("utf-8", 'windows-1253') }
+ assert_undefined_in("\x9C", 'windows-1253')
+ assert_undefined_in("\x9F", 'windows-1253')
check_both_ways("\u00A0", "\xA0", 'windows-1253') # non-breaking space
check_both_ways("\u2015", "\xAF", 'windows-1253') # ―
check_both_ways("\u00B0", "\xB0", 'windows-1253') # °
@@ -322,28 +312,28 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u039F", "\xCF", 'windows-1253') # Ο
check_both_ways("\u03A0", "\xD0", 'windows-1253') # Π
check_both_ways("\u03A1", "\xD1", 'windows-1253') # Ρ
- assert_raise(Encoding::UndefinedConversionError) { "\xD2".encode("utf-8", 'windows-1253') }
+ assert_undefined_in("\xD2", 'windows-1253')
check_both_ways("\u03A3", "\xD3", 'windows-1253') # Σ
check_both_ways("\u03AF", "\xDF", 'windows-1253') # ί
check_both_ways("\u03B0", "\xE0", 'windows-1253') # ΰ
check_both_ways("\u03BF", "\xEF", 'windows-1253') # ο
check_both_ways("\u03C0", "\xF0", 'windows-1253') # π
check_both_ways("\u03CE", "\xFE", 'windows-1253') # ÏŽ
- assert_raise(Encoding::UndefinedConversionError) { "\xFF".encode("utf-8", 'windows-1253') }
+ assert_undefined_in("\xFF", 'windows-1253')
end
def test_windows_1254
check_both_ways("\u20AC", "\x80", 'windows-1254') # €
- assert_raise(Encoding::UndefinedConversionError) { "\x81".encode("utf-8", 'windows-1254') }
+ assert_undefined_in("\x81", 'windows-1254')
check_both_ways("\u201A", "\x82", 'windows-1254') # ‚
check_both_ways("\u0152", "\x8C", 'windows-1254') # Å’
- assert_raise(Encoding::UndefinedConversionError) { "\x8D".encode("utf-8", 'windows-1254') }
- assert_raise(Encoding::UndefinedConversionError) { "\x8F".encode("utf-8", 'windows-1254') }
- assert_raise(Encoding::UndefinedConversionError) { "\x90".encode("utf-8", 'windows-1254') }
+ assert_undefined_in("\x8D", 'windows-1254')
+ assert_undefined_in("\x8F", 'windows-1254')
+ assert_undefined_in("\x90", 'windows-1254')
check_both_ways("\u2018", "\x91", 'windows-1254') # ‘
check_both_ways("\u0153", "\x9C", 'windows-1254') # Å“
- assert_raise(Encoding::UndefinedConversionError) { "\x9D".encode("utf-8", 'windows-1254') }
- assert_raise(Encoding::UndefinedConversionError) { "\x9E".encode("utf-8", 'windows-1254') }
+ assert_undefined_in("\x9D", 'windows-1254')
+ assert_undefined_in("\x9E", 'windows-1254')
check_both_ways("\u0178", "\x9F", 'windows-1254') # Ÿ
check_both_ways("\u00A0", "\xA0", 'windows-1254') # non-breaking space
check_both_ways("\u00AF", "\xAF", 'windows-1254') # ¯
@@ -361,20 +351,20 @@ class TestTranscode < Test::Unit::TestCase
def test_windows_1255
check_both_ways("\u20AC", "\x80", 'windows-1255') # €
- assert_raise(Encoding::UndefinedConversionError) { "\x81".encode("utf-8", 'windows-1255') }
+ assert_undefined_in("\x81", 'windows-1255')
check_both_ways("\u201A", "\x82", 'windows-1255') # ‚
check_both_ways("\u2030", "\x89", 'windows-1255') # ‰
- assert_raise(Encoding::UndefinedConversionError) { "\x8A".encode("utf-8", 'windows-1255') }
+ assert_undefined_in("\x8A", 'windows-1255')
check_both_ways("\u2039", "\x8B", 'windows-1255') # ‹
- assert_raise(Encoding::UndefinedConversionError) { "\x8C".encode("utf-8", 'windows-1255') }
- assert_raise(Encoding::UndefinedConversionError) { "\x8F".encode("utf-8", 'windows-1255') }
- assert_raise(Encoding::UndefinedConversionError) { "\x90".encode("utf-8", 'windows-1255') }
+ assert_undefined_in("\x8C", 'windows-1255')
+ assert_undefined_in("\x8F", 'windows-1255')
+ assert_undefined_in("\x90", 'windows-1255')
check_both_ways("\u2018", "\x91", 'windows-1255') # ‘
check_both_ways("\u2122", "\x99", 'windows-1255') # â„¢
- assert_raise(Encoding::UndefinedConversionError) { "\x9A".encode("utf-8", 'windows-1255') }
+ assert_undefined_in("\x9A", 'windows-1255')
check_both_ways("\u203A", "\x9B", 'windows-1255') # ›
- assert_raise(Encoding::UndefinedConversionError) { "\x9C".encode("utf-8", 'windows-1255') }
- assert_raise(Encoding::UndefinedConversionError) { "\x9F".encode("utf-8", 'windows-1255') }
+ assert_undefined_in("\x9C", 'windows-1255')
+ assert_undefined_in("\x9F", 'windows-1255')
check_both_ways("\u00A0", "\xA0", 'windows-1255') # non-breaking space
check_both_ways("\u00A1", "\xA1", 'windows-1255') # ¡
check_both_ways("\u00D7", "\xAA", 'windows-1255') # ×
@@ -391,17 +381,17 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u05C0", "\xD0", 'windows-1255') # ×€
check_both_ways("\u05F3", "\xD7", 'windows-1255') # ׳
check_both_ways("\u05F4", "\xD8", 'windows-1255') # ×´
- assert_raise(Encoding::UndefinedConversionError) { "\xD9".encode("utf-8", 'windows-1255') }
- assert_raise(Encoding::UndefinedConversionError) { "\xDF".encode("utf-8", 'windows-1255') }
+ assert_undefined_in("\xD9", 'windows-1255')
+ assert_undefined_in("\xDF", 'windows-1255')
check_both_ways("\u05D0", "\xE0", 'windows-1255') # ×
check_both_ways("\u05DF", "\xEF", 'windows-1255') # ן
check_both_ways("\u05E0", "\xF0", 'windows-1255') # × 
check_both_ways("\u05EA", "\xFA", 'windows-1255') # ת
- assert_raise(Encoding::UndefinedConversionError) { "\xFB".encode("utf-8", 'windows-1255') }
- assert_raise(Encoding::UndefinedConversionError) { "\xFC".encode("utf-8", 'windows-1255') }
+ assert_undefined_in("\xFB", 'windows-1255')
+ assert_undefined_in("\xFC", 'windows-1255')
check_both_ways("\u200E", "\xFD", 'windows-1255') # left-to-right mark
check_both_ways("\u200F", "\xFE", 'windows-1255') # right-to-left mark
- assert_raise(Encoding::UndefinedConversionError) { "\xFF".encode("utf-8", 'windows-1255') }
+ assert_undefined_in("\xFF", 'windows-1255')
end
def test_windows_1256
@@ -429,35 +419,35 @@ class TestTranscode < Test::Unit::TestCase
def test_windows_1257
check_both_ways("\u20AC", "\x80", 'windows-1257') # €
- assert_raise(Encoding::UndefinedConversionError) { "\x81".encode("utf-8", 'windows-1257') }
+ assert_undefined_in("\x81", 'windows-1257')
check_both_ways("\u201A", "\x82", 'windows-1257') # ‚
- assert_raise(Encoding::UndefinedConversionError) { "\x83".encode("utf-8", 'windows-1257') }
+ assert_undefined_in("\x83", 'windows-1257')
check_both_ways("\u201E", "\x84", 'windows-1257') # „
check_both_ways("\u2021", "\x87", 'windows-1257') # ‡
- assert_raise(Encoding::UndefinedConversionError) { "\x88".encode("utf-8", 'windows-1257') }
+ assert_undefined_in("\x88", 'windows-1257')
check_both_ways("\u2030", "\x89", 'windows-1257') # ‰
- assert_raise(Encoding::UndefinedConversionError) { "\x8A".encode("utf-8", 'windows-1257') }
+ assert_undefined_in("\x8A", 'windows-1257')
check_both_ways("\u2039", "\x8B", 'windows-1257') # ‹
- assert_raise(Encoding::UndefinedConversionError) { "\x8C".encode("utf-8", 'windows-1257') }
+ assert_undefined_in("\x8C", 'windows-1257')
check_both_ways("\u00A8", "\x8D", 'windows-1257') # ¨
check_both_ways("\u02C7", "\x8E", 'windows-1257') # ˇ
check_both_ways("\u00B8", "\x8F", 'windows-1257') # ¸
- assert_raise(Encoding::UndefinedConversionError) { "\x90".encode("utf-8", 'windows-1257') }
+ assert_undefined_in("\x90", 'windows-1257')
check_both_ways("\u2018", "\x91", 'windows-1257') # ‘
check_both_ways("\u2014", "\x97", 'windows-1257') # —
- assert_raise(Encoding::UndefinedConversionError) { "\x98".encode("utf-8", 'windows-1257') }
+ assert_undefined_in("\x98", 'windows-1257')
check_both_ways("\u2122", "\x99", 'windows-1257') # â„¢
- assert_raise(Encoding::UndefinedConversionError) { "\x9A".encode("utf-8", 'windows-1257') }
+ assert_undefined_in("\x9A", 'windows-1257')
check_both_ways("\u203A", "\x9B", 'windows-1257') # ›
- assert_raise(Encoding::UndefinedConversionError) { "\x9C".encode("utf-8", 'windows-1257') }
+ assert_undefined_in("\x9C", 'windows-1257')
check_both_ways("\u00AF", "\x9D", 'windows-1257') # ¯
check_both_ways("\u02DB", "\x9E", 'windows-1257') # Ë›
- assert_raise(Encoding::UndefinedConversionError) { "\x9F".encode("utf-8", 'windows-1257') }
+ assert_undefined_in("\x9F", 'windows-1257')
check_both_ways("\u00A0", "\xA0", 'windows-1257') # non-breaking space
- assert_raise(Encoding::UndefinedConversionError) { "\xA1".encode("utf-8", 'windows-1257') }
+ assert_undefined_in("\xA1", 'windows-1257')
check_both_ways("\u00A2", "\xA2", 'windows-1257') # ¢
check_both_ways("\u00A4", "\xA4", 'windows-1257') # ¤
- assert_raise(Encoding::UndefinedConversionError) { "\xA5".encode("utf-8", 'windows-1257') }
+ assert_undefined_in("\xA5", 'windows-1257')
check_both_ways("\u00A6", "\xA6", 'windows-1257') # ¦
check_both_ways("\u00C6", "\xAF", 'windows-1257') # Æ
check_both_ways("\u00B0", "\xB0", 'windows-1257') # °
@@ -492,9 +482,9 @@ class TestTranscode < Test::Unit::TestCase
end
def test_IBM720
- assert_raise(Encoding::UndefinedConversionError) { "\x80".encode("utf-8", 'IBM720') }
- assert_raise(Encoding::UndefinedConversionError) { "\x8F".encode("utf-8", 'IBM720') }
- assert_raise(Encoding::UndefinedConversionError) { "\x90".encode("utf-8", 'IBM720') }
+ assert_undefined_in("\x80", 'IBM720')
+ assert_undefined_in("\x8F", 'IBM720')
+ assert_undefined_in("\x90", 'IBM720')
check_both_ways("\u0627", "\x9F", 'IBM720') # ا
check_both_ways("\u0628", "\xA0", 'IBM720') # ب
check_both_ways("\u00BB", "\xAF", 'IBM720') # »
@@ -580,17 +570,17 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u00A4", "\xCF", 'IBM857') # ¤
check_both_ways("\u00BA", "\xD0", 'IBM857') # º
check_both_ways("\u00C8", "\xD4", 'IBM857') # È
- assert_raise(Encoding::UndefinedConversionError) { "\xD5".encode("utf-8", 'IBM857') }
+ assert_undefined_in("\xD5", 'IBM857')
check_both_ways("\u00CD", "\xD6", 'IBM857') # Ã
check_both_ways("\u2580", "\xDF", 'IBM857') # â–€
check_both_ways("\u00D3", "\xE0", 'IBM857') # Ó
check_both_ways("\u00B5", "\xE6", 'IBM857') # µ
- assert_raise(Encoding::UndefinedConversionError) { "\xE7".encode("utf-8", 'IBM857') }
+ assert_undefined_in("\xE7", 'IBM857')
check_both_ways("\u00D7", "\xE8", 'IBM857') # ×
check_both_ways("\u00B4", "\xEF", 'IBM857') # ´
check_both_ways("\u00AD", "\xF0", 'IBM857') # soft hyphen
check_both_ways("\u00B1", "\xF1", 'IBM857') # ±
- assert_raise(Encoding::UndefinedConversionError) { "\xF2".encode("utf-8", 'IBM857') }
+ assert_undefined_in("\xF2", 'IBM857')
check_both_ways("\u00BE", "\xF3", 'IBM857') # ¾
check_both_ways("\u00A0", "\xFF", 'IBM857') # non-breaking space
end
@@ -710,16 +700,16 @@ class TestTranscode < Test::Unit::TestCase
end
def test_IBM869
- assert_raise(Encoding::UndefinedConversionError) { "\x80".encode("utf-8", 'IBM869') }
- assert_raise(Encoding::UndefinedConversionError) { "\x85".encode("utf-8", 'IBM869') }
+ assert_undefined_in("\x80", 'IBM869')
+ assert_undefined_in("\x85", 'IBM869')
check_both_ways("\u0386", "\x86", 'IBM869') # Ά
- assert_raise(Encoding::UndefinedConversionError) { "\x87".encode("utf-8", 'IBM869') }
+ assert_undefined_in("\x87", 'IBM869')
check_both_ways("\u00B7", "\x88", 'IBM869') # ·
check_both_ways("\u0389", "\x8F", 'IBM869') # Ή
check_both_ways("\u038A", "\x90", 'IBM869') # Ί
check_both_ways("\u038C", "\x92", 'IBM869') # Ό
- assert_raise(Encoding::UndefinedConversionError) { "\x93".encode("utf-8", 'IBM869') }
- assert_raise(Encoding::UndefinedConversionError) { "\x94".encode("utf-8", 'IBM869') }
+ assert_undefined_in("\x93", 'IBM869')
+ assert_undefined_in("\x94", 'IBM869')
check_both_ways("\u038E", "\x95", 'IBM869') # ÎŽ
check_both_ways("\u03AF", "\x9F", 'IBM869') # ί
check_both_ways("\u03CA", "\xA0", 'IBM869') # ÏŠ
@@ -808,7 +798,7 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u03BF", "\xEF", 'macGreek') # ο
check_both_ways("\u03C0", "\xF0", 'macGreek') # π
check_both_ways("\u03B0", "\xFE", 'macGreek') # ΰ
- assert_raise(Encoding::UndefinedConversionError) { "\xFF".encode("utf-8", 'macGreek') }
+ assert_undefined_in("\xFF", 'macGreek')
end
def test_macIceland
@@ -887,7 +877,7 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u00D4", "\xEF", 'macTurkish') # Ô
#check_both_ways("\uF8FF", "\xF0", 'macTurkish') # Apple logo
check_both_ways("\u00D9", "\xF4", 'macTurkish') # Ù
- assert_raise(Encoding::UndefinedConversionError) { "\xF5".encode("utf-8", 'macTurkish') }
+ assert_undefined_in("\xF5", 'macTurkish')
check_both_ways("\u02C6", "\xF6", 'macTurkish') # ˆ
check_both_ways("\u02C7", "\xFF", 'macTurkish') # ˇ
end
@@ -958,11 +948,11 @@ class TestTranscode < Test::Unit::TestCase
end
def test_TIS_620
- assert_raise(Encoding::UndefinedConversionError) { "\x80".encode("utf-8", 'TIS-620') }
- assert_raise(Encoding::UndefinedConversionError) { "\x8F".encode("utf-8", 'TIS-620') }
- assert_raise(Encoding::UndefinedConversionError) { "\x90".encode("utf-8", 'TIS-620') }
- assert_raise(Encoding::UndefinedConversionError) { "\x9F".encode("utf-8", 'TIS-620') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA0".encode("utf-8", 'TIS-620') }
+ assert_undefined_in("\x80", 'TIS-620')
+ assert_undefined_in("\x8F", 'TIS-620')
+ assert_undefined_in("\x90", 'TIS-620')
+ assert_undefined_in("\x9F", 'TIS-620')
+ assert_undefined_in("\xA0", 'TIS-620')
check_both_ways("\u0E01", "\xA1", 'TIS-620') # à¸
check_both_ways("\u0E0F", "\xAF", 'TIS-620') # à¸
check_both_ways("\u0E10", "\xB0", 'TIS-620') # à¸
@@ -971,15 +961,15 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u0E2F", "\xCF", 'TIS-620') # ฯ
check_both_ways("\u0E30", "\xD0", 'TIS-620') # ะ
check_both_ways("\u0E3A", "\xDA", 'TIS-620') # ฺ
- assert_raise(Encoding::UndefinedConversionError) { "\xDB".encode("utf-8", 'TIS-620') }
- assert_raise(Encoding::UndefinedConversionError) { "\xDE".encode("utf-8", 'TIS-620') }
+ assert_undefined_in("\xDB", 'TIS-620')
+ assert_undefined_in("\xDE", 'TIS-620')
check_both_ways("\u0E3F", "\xDF", 'TIS-620') # ฿
check_both_ways("\u0E40", "\xE0", 'TIS-620') # เ
check_both_ways("\u0E4F", "\xEF", 'TIS-620') # à¹
check_both_ways("\u0E50", "\xF0", 'TIS-620') # à¹
check_both_ways("\u0E5B", "\xFB", 'TIS-620') # ๛
- assert_raise(Encoding::UndefinedConversionError) { "\xFC".encode("utf-8", 'TIS-620') }
- assert_raise(Encoding::UndefinedConversionError) { "\xFF".encode("utf-8", 'TIS-620') }
+ assert_undefined_in("\xFC", 'TIS-620')
+ assert_undefined_in("\xFF", 'TIS-620')
end
def test_CP850
@@ -1182,15 +1172,15 @@ class TestTranscode < Test::Unit::TestCase
expected = "\u{3042}\u{3044}\u{20bb7}"
assert_equal(expected, %w/fffe4230443042d8b7df/.pack("H*").encode("UTF-8","UTF-16"))
check_both_ways(expected, %w/feff30423044d842dfb7/.pack("H*"), "UTF-16")
- assert_raise(Encoding::InvalidByteSequenceError){%w/feffdfb7/.pack("H*").encode("UTF-8","UTF-16")}
- assert_raise(Encoding::InvalidByteSequenceError){%w/fffeb7df/.pack("H*").encode("UTF-8","UTF-16")}
+ assert_invalid_in(%w/feffdfb7/.pack("H*"), "UTF-16")
+ assert_invalid_in(%w/fffeb7df/.pack("H*"), "UTF-16")
end
def test_utf_32_bom
expected = "\u{3042}\u{3044}\u{20bb7}"
assert_equal(expected, %w/fffe00004230000044300000b70b0200/.pack("H*").encode("UTF-8","UTF-32"))
check_both_ways(expected, %w/0000feff000030420000304400020bb7/.pack("H*"), "UTF-32")
- assert_raise(Encoding::InvalidByteSequenceError){%w/0000feff00110000/.pack("H*").encode("UTF-8","UTF-32")}
+ assert_invalid_in(%w/0000feff00110000/.pack("H*"), "UTF-32")
end
def check_utf_32_both_ways(utf8, raw)
@@ -1372,24 +1362,24 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u71FC", "\xE0\x9E", 'shift_jis') # 燼
check_both_ways("\u71F9", "\xE0\x9F", 'shift_jis') # 燹
check_both_ways("\u73F1", "\xE0\xFC", 'shift_jis') # ç±
- assert_raise(Encoding::UndefinedConversionError) { "\xEF\x40".encode("utf-8", 'shift_jis') }
- assert_raise(Encoding::UndefinedConversionError) { "\xEF\x7E".encode("utf-8", 'shift_jis') }
- assert_raise(Encoding::UndefinedConversionError) { "\xEF\x80".encode("utf-8", 'shift_jis') }
- assert_raise(Encoding::UndefinedConversionError) { "\xEF\x9E".encode("utf-8", 'shift_jis') }
- assert_raise(Encoding::UndefinedConversionError) { "\xEF\x9F".encode("utf-8", 'shift_jis') }
- assert_raise(Encoding::UndefinedConversionError) { "\xEF\xFC".encode("utf-8", 'shift_jis') }
- assert_raise(Encoding::UndefinedConversionError) { "\xF0\x40".encode("utf-8", 'shift_jis') }
- assert_raise(Encoding::UndefinedConversionError) { "\xF0\x7E".encode("utf-8", 'shift_jis') }
- assert_raise(Encoding::UndefinedConversionError) { "\xF0\x80".encode("utf-8", 'shift_jis') }
- assert_raise(Encoding::UndefinedConversionError) { "\xF0\x9E".encode("utf-8", 'shift_jis') }
- assert_raise(Encoding::UndefinedConversionError) { "\xF0\x9F".encode("utf-8", 'shift_jis') }
- assert_raise(Encoding::UndefinedConversionError) { "\xF0\xFC".encode("utf-8", 'shift_jis') }
+ assert_undefined_in("\xEF\x40", 'shift_jis')
+ assert_undefined_in("\xEF\x7E", 'shift_jis')
+ assert_undefined_in("\xEF\x80", 'shift_jis')
+ assert_undefined_in("\xEF\x9E", 'shift_jis')
+ assert_undefined_in("\xEF\x9F", 'shift_jis')
+ assert_undefined_in("\xEF\xFC", 'shift_jis')
+ assert_undefined_in("\xF0\x40", 'shift_jis')
+ assert_undefined_in("\xF0\x7E", 'shift_jis')
+ assert_undefined_in("\xF0\x80", 'shift_jis')
+ assert_undefined_in("\xF0\x9E", 'shift_jis')
+ assert_undefined_in("\xF0\x9F", 'shift_jis')
+ assert_undefined_in("\xF0\xFC", 'shift_jis')
#check_both_ways("\u9ADC", "\xFC\x40", 'shift_jis') # 髜 (IBM extended)
- assert_raise(Encoding::UndefinedConversionError) { "\xFC\x7E".encode("utf-8", 'shift_jis') }
- assert_raise(Encoding::UndefinedConversionError) { "\xFC\x80".encode("utf-8", 'shift_jis') }
- assert_raise(Encoding::UndefinedConversionError) { "\xFC\x9E".encode("utf-8", 'shift_jis') }
- assert_raise(Encoding::UndefinedConversionError) { "\xFC\x9F".encode("utf-8", 'shift_jis') }
- assert_raise(Encoding::UndefinedConversionError) { "\xFC\xFC".encode("utf-8", 'shift_jis') }
+ assert_undefined_in("\xFC\x7E", 'shift_jis')
+ assert_undefined_in("\xFC\x80", 'shift_jis')
+ assert_undefined_in("\xFC\x9E", 'shift_jis')
+ assert_undefined_in("\xFC\x9F", 'shift_jis')
+ assert_undefined_in("\xFC\xFC", 'shift_jis')
check_both_ways("\u677E\u672C\u884C\u5F18", "\x8f\xbc\x96\x7b\x8d\x73\x8d\x4f", 'shift_jis') # æ¾æœ¬è¡Œå¼˜
check_both_ways("\u9752\u5C71\u5B66\u9662\u5927\u5B66", "\x90\xC2\x8E\x52\x8A\x77\x89\x40\x91\xE5\x8A\x77", 'shift_jis') # é’山学院大学
check_both_ways("\u795E\u6797\u7FA9\u535A", "\x90\x5F\x97\xD1\x8B\x60\x94\x8E", 'shift_jis') # 神林義åš
@@ -1409,34 +1399,34 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u00F7", "\xA1\xE0", 'euc-jp') # ÷
check_both_ways("\u25C7", "\xA1\xFE", 'euc-jp') # â—‡
check_both_ways("\u25C6", "\xA2\xA1", 'euc-jp') # â—†
- assert_raise(Encoding::UndefinedConversionError) { "\xA2\xAF".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA2\xB9".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA2\xC2".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA2\xC9".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA2\xD1".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA2\xDB".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA2\xEB".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA2\xF1".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA2\xFA".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA2\xFD".encode("utf-8", 'euc-jp') }
+ assert_undefined_in("\xA2\xAF", 'euc-jp')
+ assert_undefined_in("\xA2\xB9", 'euc-jp')
+ assert_undefined_in("\xA2\xC2", 'euc-jp')
+ assert_undefined_in("\xA2\xC9", 'euc-jp')
+ assert_undefined_in("\xA2\xD1", 'euc-jp')
+ assert_undefined_in("\xA2\xDB", 'euc-jp')
+ assert_undefined_in("\xA2\xEB", 'euc-jp')
+ assert_undefined_in("\xA2\xF1", 'euc-jp')
+ assert_undefined_in("\xA2\xFA", 'euc-jp')
+ assert_undefined_in("\xA2\xFD", 'euc-jp')
check_both_ways("\u25EF", "\xA2\xFE", 'euc-jp') # â—¯
- assert_raise(Encoding::UndefinedConversionError) { "\xA3\xAF".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA3\xBA".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA3\xC0".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA3\xDB".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA3\xE0".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA3\xFB".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA4\xF4".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA5\xF7".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA6\xB9".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA6\xC0".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA6\xD9".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA7\xC2".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA7\xD0".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA7\xF2".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA8\xC1".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xCF\xD4".encode("utf-8", 'euc-jp') }
- assert_raise(Encoding::UndefinedConversionError) { "\xCF\xFE".encode("utf-8", 'euc-jp') }
+ assert_undefined_in("\xA3\xAF", 'euc-jp')
+ assert_undefined_in("\xA3\xBA", 'euc-jp')
+ assert_undefined_in("\xA3\xC0", 'euc-jp')
+ assert_undefined_in("\xA3\xDB", 'euc-jp')
+ assert_undefined_in("\xA3\xE0", 'euc-jp')
+ assert_undefined_in("\xA3\xFB", 'euc-jp')
+ assert_undefined_in("\xA4\xF4", 'euc-jp')
+ assert_undefined_in("\xA5\xF7", 'euc-jp')
+ assert_undefined_in("\xA6\xB9", 'euc-jp')
+ assert_undefined_in("\xA6\xC0", 'euc-jp')
+ assert_undefined_in("\xA6\xD9", 'euc-jp')
+ assert_undefined_in("\xA7\xC2", 'euc-jp')
+ assert_undefined_in("\xA7\xD0", 'euc-jp')
+ assert_undefined_in("\xA7\xF2", 'euc-jp')
+ assert_undefined_in("\xA8\xC1", 'euc-jp')
+ assert_undefined_in("\xCF\xD4", 'euc-jp')
+ assert_undefined_in("\xCF\xFE", 'euc-jp')
check_both_ways("\u6A97", "\xDD\xA1", 'euc-jp') # 檗
check_both_ways("\u6BEF", "\xDD\xDF", 'euc-jp') # 毯
check_both_ways("\u9EBE", "\xDD\xE0", 'euc-jp') # 麾
@@ -1449,7 +1439,7 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u71FC", "\xDF\xFE", 'euc-jp') # 燼
check_both_ways("\u71F9", "\xE0\xA1", 'euc-jp') # 燹
check_both_ways("\u73F1", "\xE0\xFE", 'euc-jp') # ç±
- assert_raise(Encoding::UndefinedConversionError) { "\xF4\xA7".encode("utf-8", 'euc-jp') }
+ assert_undefined_in("\xF4\xA7", 'euc-jp')
#check_both_ways("\u9ADC", "\xFC\xE3", 'euc-jp') # 髜 (IBM extended)
check_both_ways("\u677E\u672C\u884C\u5F18", "\xBE\xBE\xCB\xDC\xB9\xD4\xB9\xB0", 'euc-jp') # æ¾æœ¬è¡Œå¼˜
@@ -1481,7 +1471,7 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u2127", "\xA3\xE0", 'euc-jis-2004') # ℧
check_both_ways("\u30A0", "\xA3\xFB", 'euc-jis-2004') # ã‚ 
check_both_ways("\uFF54", "\xA3\xF4", 'euc-jis-2004') # ï½”
- assert_raise(Encoding::UndefinedConversionError) { "\xA5\xF7".encode("utf-8", 'euc-jis-2004') }
+ assert_undefined_in("\xA5\xF7", 'euc-jis-2004')
check_both_ways("\u2664", "\xA6\xB9", 'euc-jis-2004') # ♤
check_both_ways("\u2663", "\xA6\xC0", 'euc-jis-2004') # ♣
check_both_ways("\u03C2", "\xA6\xD9", 'euc-jis-2004') # Ï‚
@@ -1566,33 +1556,33 @@ class TestTranscode < Test::Unit::TestCase
end
def test_eucjp_sjis_undef
- assert_raise(Encoding::UndefinedConversionError) { "\x8e\xe0".encode("Shift_JIS", "EUC-JP") }
- assert_raise(Encoding::UndefinedConversionError) { "\x8e\xfe".encode("Shift_JIS", "EUC-JP") }
- assert_raise(Encoding::UndefinedConversionError) { "\x8f\xa1\xa1".encode("Shift_JIS", "EUC-JP") }
- assert_raise(Encoding::UndefinedConversionError) { "\x8f\xa1\xfe".encode("Shift_JIS", "EUC-JP") }
- assert_raise(Encoding::UndefinedConversionError) { "\x8f\xfe\xa1".encode("Shift_JIS", "EUC-JP") }
- assert_raise(Encoding::UndefinedConversionError) { "\x8f\xfe\xfe".encode("Shift_JIS", "EUC-JP") }
-
- assert_raise(Encoding::UndefinedConversionError) { "\xf0\x40".encode("EUC-JP", "Shift_JIS") }
- assert_raise(Encoding::UndefinedConversionError) { "\xf0\x7e".encode("EUC-JP", "Shift_JIS") }
- assert_raise(Encoding::UndefinedConversionError) { "\xf0\x80".encode("EUC-JP", "Shift_JIS") }
- assert_raise(Encoding::UndefinedConversionError) { "\xf0\xfc".encode("EUC-JP", "Shift_JIS") }
- assert_raise(Encoding::UndefinedConversionError) { "\xfc\x40".encode("EUC-JP", "Shift_JIS") }
- assert_raise(Encoding::UndefinedConversionError) { "\xfc\x7e".encode("EUC-JP", "Shift_JIS") }
- assert_raise(Encoding::UndefinedConversionError) { "\xfc\x80".encode("EUC-JP", "Shift_JIS") }
- assert_raise(Encoding::UndefinedConversionError) { "\xfc\xfc".encode("EUC-JP", "Shift_JIS") }
+ assert_undefined_conversion("\x8e\xe0", "Shift_JIS", "EUC-JP")
+ assert_undefined_conversion("\x8e\xfe", "Shift_JIS", "EUC-JP")
+ assert_undefined_conversion("\x8f\xa1\xa1", "Shift_JIS", "EUC-JP")
+ assert_undefined_conversion("\x8f\xa1\xfe", "Shift_JIS", "EUC-JP")
+ assert_undefined_conversion("\x8f\xfe\xa1", "Shift_JIS", "EUC-JP")
+ assert_undefined_conversion("\x8f\xfe\xfe", "Shift_JIS", "EUC-JP")
+
+ assert_undefined_conversion("\xf0\x40", "EUC-JP", "Shift_JIS")
+ assert_undefined_conversion("\xf0\x7e", "EUC-JP", "Shift_JIS")
+ assert_undefined_conversion("\xf0\x80", "EUC-JP", "Shift_JIS")
+ assert_undefined_conversion("\xf0\xfc", "EUC-JP", "Shift_JIS")
+ assert_undefined_conversion("\xfc\x40", "EUC-JP", "Shift_JIS")
+ assert_undefined_conversion("\xfc\x7e", "EUC-JP", "Shift_JIS")
+ assert_undefined_conversion("\xfc\x80", "EUC-JP", "Shift_JIS")
+ assert_undefined_conversion("\xfc\xfc", "EUC-JP", "Shift_JIS")
end
def test_iso_2022_jp
- assert_raise(Encoding::InvalidByteSequenceError) { "\x1b(A".encode("utf-8", "iso-2022-jp") }
- assert_raise(Encoding::InvalidByteSequenceError) { "\x1b$(A".encode("utf-8", "iso-2022-jp") }
- assert_raise(Encoding::InvalidByteSequenceError) { "\x1b$C".encode("utf-8", "iso-2022-jp") }
- assert_raise(Encoding::InvalidByteSequenceError) { "\x0e".encode("utf-8", "iso-2022-jp") }
- assert_raise(Encoding::InvalidByteSequenceError) { "\x80".encode("utf-8", "iso-2022-jp") }
- assert_raise(Encoding::InvalidByteSequenceError) { "\x1b$(Dd!\x1b(B".encode("utf-8", "iso-2022-jp") }
- assert_raise(Encoding::UndefinedConversionError) { "\u9299".encode("iso-2022-jp") }
- assert_raise(Encoding::UndefinedConversionError) { "\uff71\uff72\uff73\uff74\uff75".encode("iso-2022-jp") }
- assert_raise(Encoding::InvalidByteSequenceError) { "\x1b(I12345\x1b(B".encode("utf-8", "iso-2022-jp") }
+ assert_invalid_in("\x1b(A", "iso-2022-jp")
+ assert_invalid_in("\x1b$(A", "iso-2022-jp")
+ assert_invalid_in("\x1b$C", "iso-2022-jp")
+ assert_invalid_in("\x0e", "iso-2022-jp")
+ assert_invalid_in("\x80", "iso-2022-jp")
+ assert_invalid_in("\x1b$(Dd!\x1b(B", "iso-2022-jp")
+ assert_undefined_conversion("\u9299", "iso-2022-jp")
+ assert_undefined_conversion("\uff71\uff72\uff73\uff74\uff75", "iso-2022-jp")
+ assert_invalid_in("\x1b(I12345\x1b(B", "iso-2022-jp")
assert_equal("\xA1\xA1".force_encoding("euc-jp"),
"\e$B!!\e(B".encode("EUC-JP", "ISO-2022-JP"))
assert_equal("\e$B!!\e(B".force_encoding("ISO-2022-JP"),
@@ -1655,11 +1645,11 @@ class TestTranscode < Test::Unit::TestCase
assert_equal("\u005C", "\e(J\x5C\e(B".encode("UTF-8", "ISO-2022-JP"))
assert_equal("\u005C", "\x5C".encode("stateless-ISO-2022-JP", "ISO-2022-JP"))
assert_equal("\u005C", "\e(J\x5C\e(B".encode("stateless-ISO-2022-JP", "ISO-2022-JP"))
- assert_raise(Encoding::UndefinedConversionError) { "\u00A5".encode("Shift_JIS") }
- assert_raise(Encoding::UndefinedConversionError) { "\u00A5".encode("Windows-31J") }
- assert_raise(Encoding::UndefinedConversionError) { "\u00A5".encode("EUC-JP") }
- assert_raise(Encoding::UndefinedConversionError) { "\u00A5".encode("eucJP-ms") }
- assert_raise(Encoding::UndefinedConversionError) { "\u00A5".encode("CP51932") }
+ assert_undefined_conversion("\u00A5", "Shift_JIS")
+ assert_undefined_conversion("\u00A5", "Windows-31J")
+ assert_undefined_conversion("\u00A5", "EUC-JP")
+ assert_undefined_conversion("\u00A5", "eucJP-ms")
+ assert_undefined_conversion("\u00A5", "CP51932")
# FULLWIDTH REVERSE SOLIDUS
check_both_ways("\uFF3C", "\x81\x5F", "Shift_JIS")
@@ -1680,21 +1670,21 @@ class TestTranscode < Test::Unit::TestCase
assert_equal("\u007E", "\e(J\x7E\e(B".encode("UTF-8", "ISO-2022-JP"))
assert_equal("\u007E", "\x7E".encode("stateless-ISO-2022-JP", "ISO-2022-JP"))
assert_equal("\u007E", "\e(J\x7E\e(B".encode("stateless-ISO-2022-JP", "ISO-2022-JP"))
- assert_raise(Encoding::UndefinedConversionError) { "\u203E".encode("Shift_JIS") }
- assert_raise(Encoding::UndefinedConversionError) { "\u203E".encode("Windows-31J") }
- assert_raise(Encoding::UndefinedConversionError) { "\u203E".encode("EUC-JP") }
- assert_raise(Encoding::UndefinedConversionError) { "\u203E".encode("eucJP-ms") }
- assert_raise(Encoding::UndefinedConversionError) { "\u203E".encode("CP51932") }
+ assert_undefined_conversion("\u203E", "Shift_JIS")
+ assert_undefined_conversion("\u203E", "Windows-31J")
+ assert_undefined_conversion("\u203E", "EUC-JP")
+ assert_undefined_conversion("\u203E", "eucJP-ms")
+ assert_undefined_conversion("\u203E", "CP51932")
end
def test_gb2312
check_both_ways("\u3000", "\xA1\xA1", 'GB2312') # full-width space
check_both_ways("\u3013", "\xA1\xFE", 'GB2312') # 〓
- assert_raise(Encoding::UndefinedConversionError) { "\xA2\xB0".encode("utf-8", 'GB2312') }
+ assert_undefined_in("\xA2\xB0", 'GB2312')
check_both_ways("\u2488", "\xA2\xB1", 'GB2312') # â’ˆ
- assert_raise(Encoding::UndefinedConversionError) { "\xA2\xE4".encode("utf-8", 'GB2312') }
+ assert_undefined_in("\xA2\xE4", 'GB2312')
check_both_ways("\u3220", "\xA2\xE5", 'GB2312') # ㈠
- assert_raise(Encoding::UndefinedConversionError) { "\xA2\xF0".encode("utf-8", 'GB2312') }
+ assert_undefined_in("\xA2\xF0", 'GB2312')
check_both_ways("\u2160", "\xA2\xF1", 'GB2312') # â… 
check_both_ways("\uFF01", "\xA3\xA1", 'GB2312') # ï¼
check_both_ways("\uFFE3", "\xA3\xFE", 'GB2312') # ï¿£
@@ -1705,9 +1695,9 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u0410", "\xA7\xA1", 'GB2312') # Ð
check_both_ways("\u0430", "\xA7\xD1", 'GB2312') # а
check_both_ways("\u0101", "\xA8\xA1", 'GB2312') # Ä
- assert_raise(Encoding::UndefinedConversionError) { "\xA8\xC4".encode("utf-8", 'GB2312') }
+ assert_undefined_in("\xA8\xC4", 'GB2312')
check_both_ways("\u3105", "\xA8\xC5", 'GB2312') # ã„…
- assert_raise(Encoding::UndefinedConversionError) { "\xA9\xA3".encode("utf-8", 'GB2312') }
+ assert_undefined_in("\xA9\xA3", 'GB2312')
check_both_ways("\u2500", "\xA9\xA4", 'GB2312') # ─
check_both_ways("\u554A", "\xB0\xA1", 'GB2312') # å•Š
check_both_ways("\u5265", "\xB0\xFE", 'GB2312') # 剥
@@ -1721,7 +1711,7 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u7384", "\xD0\xFE", 'GB2312') # 玄
check_both_ways("\u4F4F", "\xD7\xA1", 'GB2312') # ä½
check_both_ways("\u5EA7", "\xD7\xF9", 'GB2312') # 座
- assert_raise(Encoding::UndefinedConversionError) { "\xD7\xFA".encode("utf-8", 'GB2312') }
+ assert_undefined_in("\xD7\xFA", 'GB2312')
check_both_ways("\u647A", "\xDF\xA1", 'GB2312') # 摺
check_both_ways("\u553C", "\xDF\xFE", 'GB2312') # 唼
check_both_ways("\u5537", "\xE0\xA1", 'GB2312') # å”·
@@ -1759,48 +1749,48 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u3000", "\xA1\xA1", 'GBK') # full-width space
check_both_ways("\u3001", "\xA1\xA2", 'GBK') # ã€
check_both_ways("\u3013", "\xA1\xFE", 'GBK') # 〓
- assert_raise(Encoding::UndefinedConversionError) { "\xA2\xA0".encode("utf-8", 'GBK') }
+ assert_undefined_in("\xA2\xA0", 'GBK')
check_both_ways("\u2170", "\xA2\xA1", 'GBK') # â…°
- assert_raise(Encoding::UndefinedConversionError) { "\xA2\xB0".encode("utf-8", 'GBK') }
+ assert_undefined_in("\xA2\xB0", 'GBK')
check_both_ways("\u2488", "\xA2\xB1", 'GBK') # â’ˆ
- assert_raise(Encoding::UndefinedConversionError) { "\xA2\xE4".encode("utf-8", 'GBK') }
+ assert_undefined_in("\xA2\xE4", 'GBK')
check_both_ways("\u3220", "\xA2\xE5", 'GBK') # ㈠
- assert_raise(Encoding::UndefinedConversionError) { "\xA2\xF0".encode("utf-8", 'GBK') }
+ assert_undefined_in("\xA2\xF0", 'GBK')
check_both_ways("\u2160", "\xA2\xF1", 'GBK') # â… 
- assert_raise(Encoding::UndefinedConversionError) { "\xA3\xA0".encode("utf-8", 'GBK') }
+ assert_undefined_in("\xA3\xA0", 'GBK')
check_both_ways("\uFF01", "\xA3\xA1", 'GBK') # ï¼
check_both_ways("\uFFE3", "\xA3\xFE", 'GBK') # ï¿£
- assert_raise(Encoding::UndefinedConversionError) { "\xA4\xA0".encode("utf-8", 'GBK') }
+ assert_undefined_in("\xA4\xA0", 'GBK')
check_both_ways("\u3041", "\xA4\xA1", 'GBK') # ã
- assert_raise(Encoding::UndefinedConversionError) { "\xA5\xA0".encode("utf-8", 'GBK') }
+ assert_undefined_in("\xA5\xA0", 'GBK')
check_both_ways("\u30A1", "\xA5\xA1", 'GBK') # ã‚¡
check_both_ways("\u0391", "\xA6\xA1", 'GBK') # Α
check_both_ways("\u03B1", "\xA6\xC1", 'GBK') # α
- assert_raise(Encoding::UndefinedConversionError) { "\xA6\xED".encode("utf-8", 'GBK') }
+ assert_undefined_in("\xA6\xED", 'GBK')
check_both_ways("\uFE3B", "\xA6\xEE", 'GBK') # ︻
check_both_ways("\u0410", "\xA7\xA1", 'GBK') # Ð
check_both_ways("\u0430", "\xA7\xD1", 'GBK') # а
check_both_ways("\u02CA", "\xA8\x40", 'GBK') # ËŠ
check_both_ways("\u2587", "\xA8\x7E", 'GBK') # â–‡
- assert_raise(Encoding::UndefinedConversionError) { "\xA8\x96".encode("utf-8", 'GBK') }
+ assert_undefined_in("\xA8\x96", 'GBK')
check_both_ways("\u0101", "\xA8\xA1", 'GBK') # Ä
- assert_raise(Encoding::UndefinedConversionError) { "\xA8\xBC".encode("utf-8", 'GBK') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA8\xBF".encode("utf-8", 'GBK') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA8\xC4".encode("utf-8", 'GBK') }
+ assert_undefined_in("\xA8\xBC", 'GBK')
+ assert_undefined_in("\xA8\xBF", 'GBK')
+ assert_undefined_in("\xA8\xC4", 'GBK')
check_both_ways("\u3105", "\xA8\xC5", 'GBK') # ã„…
check_both_ways("\u3021", "\xA9\x40", 'GBK') # 〡
- assert_raise(Encoding::UndefinedConversionError) { "\xA9\x58".encode("utf-8", 'GBK') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA9\x5B".encode("utf-8", 'GBK') }
- assert_raise(Encoding::UndefinedConversionError) { "\xA9\x5D".encode("utf-8", 'GBK') }
+ assert_undefined_in("\xA9\x58", 'GBK')
+ assert_undefined_in("\xA9\x5B", 'GBK')
+ assert_undefined_in("\xA9\x5D", 'GBK')
check_both_ways("\u3007", "\xA9\x96", 'GBK') # 〇
- assert_raise(Encoding::UndefinedConversionError) { "\xA9\xA3".encode("utf-8", 'GBK') }
+ assert_undefined_in("\xA9\xA3", 'GBK')
check_both_ways("\u2500", "\xA9\xA4", 'GBK') # ─
- assert_raise(Encoding::UndefinedConversionError) { "\xA9\xF0".encode("utf-8", 'GBK') }
+ assert_undefined_in("\xA9\xF0", 'GBK')
check_both_ways("\u7588", "\xAF\x40", 'GBK') # ç–ˆ
check_both_ways("\u7607", "\xAF\x7E", 'GBK') # 瘇
check_both_ways("\u7608", "\xAF\x80", 'GBK') # 瘈
check_both_ways("\u7644", "\xAF\xA0", 'GBK') # 癄
- assert_raise(Encoding::UndefinedConversionError) { "\xAF\xA1".encode("utf-8", 'GBK') }
+ assert_undefined_in("\xAF\xA1", 'GBK')
check_both_ways("\u7645", "\xB0\x40", 'GBK') # ç™…
check_both_ways("\u769B", "\xB0\x7E", 'GBK') # çš›
check_both_ways("\u769C", "\xB0\x80", 'GBK') # 皜
@@ -1841,10 +1831,10 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u9F78", "\xFD\x7E", 'GBK') # 齸
check_both_ways("\u9F79", "\xFD\x80", 'GBK') # é½¹
check_both_ways("\uF9F1", "\xFD\xA0", 'GBK') # 隣
- assert_raise(Encoding::UndefinedConversionError) { "\xFD\xA1".encode("utf-8", 'GBK') }
+ assert_undefined_in("\xFD\xA1", 'GBK')
check_both_ways("\uFA0C", "\xFE\x40", 'GBK') # 兀
check_both_ways("\uFA29", "\xFE\x4F", 'GBK') # 﨩
- assert_raise(Encoding::UndefinedConversionError) { "\xFE\x50".encode("utf-8", 'GBK') }
+ assert_undefined_in("\xFE\x50", 'GBK')
check_both_ways("\u9752\u5C71\u5B66\u9662\u5927\u5B66", "\xC7\xE0\xC9\xBD\xD1\xA7\xD4\xBA\xB4\xF3\xD1\xA7", 'GBK') # é’山学院大学
check_both_ways("\u795E\u6797\u7FA9\u535A", "\xC9\xF1\xC1\xD6\xC1\x78\xB2\xA9", 'GBK') # 神林義åš
end
@@ -1880,48 +1870,48 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u3000", "\xA1\xA1", 'GB18030') # full-width space
check_both_ways("\u3001", "\xA1\xA2", 'GB18030') #
check_both_ways("\u3013", "\xA1\xFE", 'GB18030') #
- #assert_raise(Encoding::UndefinedConversionError) { "\xA2\xA0".encode("utf-8", 'GB18030') }
+ #assert_undefined_in("\xA2\xA0", 'GB18030')
check_both_ways("\u2170", "\xA2\xA1", 'GB18030') # â…°
- #assert_raise(Encoding::UndefinedConversionError) { "\xA2\xB0".encode("utf-8", 'GB18030') }
+ #assert_undefined_in("\xA2\xB0", 'GB18030')
check_both_ways("\u2488", "\xA2\xB1", 'GB18030') #
- #assert_raise(Encoding::UndefinedConversionError) { "\xA2\xE4".encode("utf-8", 'GB18030') }
+ #assert_undefined_in("\xA2\xE4", 'GB18030')
check_both_ways("\u3220", "\xA2\xE5", 'GB18030') # ㈠
- #assert_raise(Encoding::UndefinedConversionError) { "\xA2\xF0".encode("utf-8", 'GB18030') }
+ #assert_undefined_in("\xA2\xF0", 'GB18030')
check_both_ways("\u2160", "\xA2\xF1", 'GB18030') # â… 
- #assert_raise(Encoding::UndefinedConversionError) { "\xA3\xA0".encode("utf-8", 'GB18030') }
+ #assert_undefined_in("\xA3\xA0", 'GB18030')
check_both_ways("\uFF01", "\xA3\xA1", 'GB18030') # E
check_both_ways("\uFFE3", "\xA3\xFE", 'GB18030') # E
- #assert_raise(Encoding::UndefinedConversionError) { "\xA4\xA0".encode("utf-8", 'GB18030') }
+ #assert_undefined_in("\xA4\xA0", 'GB18030')
check_both_ways("\u3041", "\xA4\xA1", 'GB18030') #
- #assert_raise(Encoding::UndefinedConversionError) { "\xA5\xA0".encode("utf-8", 'GB18030') }
+ #assert_undefined_in("\xA5\xA0", 'GB18030')
check_both_ways("\u30A1", "\xA5\xA1", 'GB18030') # ã‚¡
check_both_ways("\u0391", "\xA6\xA1", 'GB18030') #
check_both_ways("\u03B1", "\xA6\xC1", 'GB18030') # α
- #assert_raise(Encoding::UndefinedConversionError) { "\xA6\xED".encode("utf-8", 'GB18030') }
+ #assert_undefined_in("\xA6\xED", 'GB18030')
check_both_ways("\uFE3B", "\xA6\xEE", 'GB18030') # E
check_both_ways("\u0410", "\xA7\xA1", 'GB18030') #
check_both_ways("\u0430", "\xA7\xD1", 'GB18030') # а
check_both_ways("\u02CA", "\xA8\x40", 'GB18030') #
check_both_ways("\u2587", "\xA8\x7E", 'GB18030') #
- #assert_raise(Encoding::UndefinedConversionError) { "\xA8\x96".encode("utf-8", 'GB18030') }
+ #assert_undefined_in("\xA8\x96", 'GB18030')
check_both_ways("\u0101", "\xA8\xA1", 'GB18030') #
- #assert_raise(Encoding::UndefinedConversionError) { "\xA8\xBC".encode("utf-8", 'GB18030') }
- #assert_raise(Encoding::UndefinedConversionError) { "\xA8\xBF".encode("utf-8", 'GB18030') }
- #assert_raise(Encoding::UndefinedConversionError) { "\xA8\xC4".encode("utf-8", 'GB18030') }
+ #assert_undefined_in("\xA8\xBC", 'GB18030')
+ #assert_undefined_in("\xA8\xBF", 'GB18030')
+ #assert_undefined_in("\xA8\xC4", 'GB18030')
check_both_ways("\u3105", "\xA8\xC5", 'GB18030') #
check_both_ways("\u3021", "\xA9\x40", 'GB18030') # 〡
- #assert_raise(Encoding::UndefinedConversionError) { "\xA9\x58".encode("utf-8", 'GB18030') }
- #assert_raise(Encoding::UndefinedConversionError) { "\xA9\x5B".encode("utf-8", 'GB18030') }
- #assert_raise(Encoding::UndefinedConversionError) { "\xA9\x5D".encode("utf-8", 'GB18030') }
+ #assert_undefined_in("\xA9\x58", 'GB18030')
+ #assert_undefined_in("\xA9\x5B", 'GB18030')
+ #assert_undefined_in("\xA9\x5D", 'GB18030')
check_both_ways("\u3007", "\xA9\x96", 'GB18030') #
- #assert_raise(Encoding::UndefinedConversionError) { "\xA9\xA3".encode("utf-8", 'GB18030') }
+ #assert_undefined_in("\xA9\xA3", 'GB18030')
check_both_ways("\u2500", "\xA9\xA4", 'GB18030') # ─
- #assert_raise(Encoding::UndefinedConversionError) { "\xA9\xF0".encode("utf-8", 'GB18030') }
+ #assert_undefined_in("\xA9\xF0", 'GB18030')
check_both_ways("\u7588", "\xAF\x40", 'GB18030') #
check_both_ways("\u7607", "\xAF\x7E", 'GB18030') #
check_both_ways("\u7608", "\xAF\x80", 'GB18030') #
check_both_ways("\u7644", "\xAF\xA0", 'GB18030') #
- #assert_raise(Encoding::UndefinedConversionError) { "\xAF\xA1".encode("utf-8", 'GB18030') }
+ #assert_undefined_in("\xAF\xA1", 'GB18030')
check_both_ways("\u7645", "\xB0\x40", 'GB18030') #
check_both_ways("\u769B", "\xB0\x7E", 'GB18030') #
check_both_ways("\u769C", "\xB0\x80", 'GB18030') #
@@ -1962,10 +1952,10 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u9F78", "\xFD\x7E", 'GB18030') # 齸
check_both_ways("\u9F79", "\xFD\x80", 'GB18030') # é½¹
check_both_ways("\uF9F1", "\xFD\xA0", 'GB18030') # E
- #assert_raise(Encoding::UndefinedConversionError) { "\xFD\xA1".encode("utf-8", 'GB18030') }
+ #assert_undefined_in("\xFD\xA1", 'GB18030')
check_both_ways("\uFA0C", "\xFE\x40", 'GB18030') # E
check_both_ways("\uFA29", "\xFE\x4F", 'GB18030') # E
- #assert_raise(Encoding::UndefinedConversionError) { "\xFE\x50".encode("utf-8", 'GB18030') }
+ #assert_undefined_in("\xFE\x50", 'GB18030')
check_both_ways("\u9752\u5C71\u5B66\u9662\u5927\u5B66", "\xC7\xE0\xC9\xBD\xD1\xA7\xD4\xBA\xB4\xF3\xD1\xA7", 'GB18030') # é’山学院大学
check_both_ways("\u795E\u6797\u7FA9\u535A", "\xC9\xF1\xC1\xD6\xC1\x78\xB2\xA9", 'GB18030') # 神林義
@@ -2020,7 +2010,7 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u310F", "\xA3\x7E", 'Big5') # ã„
check_both_ways("\u3110", "\xA3\xA1", 'Big5') # ã„
check_both_ways("\u02CB", "\xA3\xBF", 'Big5') # Ë‹
- assert_raise(Encoding::UndefinedConversionError) { "\xA3\xC0".encode("utf-8", 'Big5') }
+ assert_undefined_in("\xA3\xC0", 'Big5')
check_both_ways("\u6D6C", "\xAF\x40", 'Big5') # 浬
check_both_ways("\u7837", "\xAF\x7E", 'Big5') # ç ·
check_both_ways("\u7825", "\xAF\xA1", 'Big5') # ç ¥
@@ -2039,9 +2029,9 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u77AC", "\xC0\xFE", 'Big5') # 瞬
check_both_ways("\u8B96", "\xC6\x40", 'Big5') # è®–
check_both_ways("\u7C72", "\xC6\x7E", 'Big5') # ç±²
- #assert_raise(Encoding::UndefinedConversionError) { "\xC6\xA1".encode("utf-8", 'Big5') }
- #assert_raise(Encoding::UndefinedConversionError) { "\xC7\x40".encode("utf-8", 'Big5') }
- #assert_raise(Encoding::UndefinedConversionError) { "\xC8\x40".encode("utf-8", 'Big5') }
+ #assert_undefined_in("\xC6\xA1", 'Big5')
+ #assert_undefined_in("\xC7\x40", 'Big5')
+ #assert_undefined_in("\xC8\x40", 'Big5')
check_both_ways("\u4E42", "\xC9\x40", 'Big5') # 乂
check_both_ways("\u6C15", "\xC9\x7E", 'Big5') # æ°•
check_both_ways("\u6C36", "\xC9\xA1", 'Big5') # æ°¶
@@ -2074,7 +2064,7 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u9F0A", "\xF9\x7E", 'Big5') # 鼊
check_both_ways("\u9FA4", "\xF9\xA1", 'Big5') # 龤
check_both_ways("\u9F98", "\xF9\xD5", 'Big5') # 龘
- #assert_raise(Encoding::UndefinedConversionError) { "\xF9\xD6".encode("utf-8", 'Big5') }
+ #assert_undefined_in("\xF9\xD6", 'Big5')
check_both_ways("\u795E\u6797\u7FA9\u535A", "\xAF\xAB\xAA\x4C\xB8\x71\xB3\xD5", 'Big5') # 神林義åš
end
@@ -2087,7 +2077,7 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u310F", "\xA3\x7E", 'Big5-HKSCS') # ã„
check_both_ways("\u3110", "\xA3\xA1", 'Big5-HKSCS') # ã„
check_both_ways("\u02CB", "\xA3\xBF", 'Big5-HKSCS') # Ë‹
- #assert_raise(Encoding::UndefinedConversionError) { "\xA3\xC0".encode("utf-8", 'Big5-HKSCS') }
+ #assert_undefined_in("\xA3\xC0", 'Big5-HKSCS')
check_both_ways("\u6D6C", "\xAF\x40", 'Big5-HKSCS') # 浬
check_both_ways("\u7837", "\xAF\x7E", 'Big5-HKSCS') # ç ·
check_both_ways("\u7825", "\xAF\xA1", 'Big5-HKSCS') # ç ¥
@@ -2106,9 +2096,9 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u77AC", "\xC0\xFE", 'Big5-HKSCS') # 瞬
check_both_ways("\u8B96", "\xC6\x40", 'Big5-HKSCS') # è®–
check_both_ways("\u7C72", "\xC6\x7E", 'Big5-HKSCS') # ç±²
- #assert_raise(Encoding::UndefinedConversionError) { "\xC6\xA1".encode("utf-8", 'Big5-HKSCS') }
- #assert_raise(Encoding::UndefinedConversionError) { "\xC7\x40".encode("utf-8", 'Big5-HKSCS') }
- #assert_raise(Encoding::UndefinedConversionError) { "\xC8\x40".encode("utf-8", 'Big5-HKSCS') }
+ #assert_undefined_in("\xC6\xA1", 'Big5-HKSCS')
+ #assert_undefined_in("\xC7\x40", 'Big5-HKSCS')
+ #assert_undefined_in("\xC8\x40", 'Big5-HKSCS')
check_both_ways("\u4E42", "\xC9\x40", 'Big5-HKSCS') # 乂
check_both_ways("\u6C15", "\xC9\x7E", 'Big5-HKSCS') # æ°•
check_both_ways("\u6C36", "\xC9\xA1", 'Big5-HKSCS') # æ°¶
@@ -2142,7 +2132,7 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u9FA4", "\xF9\xA1", 'Big5-HKSCS') # 龤
check_both_ways("\u9F98", "\xF9\xD5", 'Big5-HKSCS') # 龘
#check_both_ways("\u{23ED7}", "\x8E\x40", 'Big5-HKSCS') # 𣻗
- #assert_raise(Encoding::UndefinedConversionError) { "\xF9\xD6".encode("utf-8", 'Big5-HKSCS') }
+ #assert_undefined_in("\xF9\xD6", 'Big5-HKSCS')
check_both_ways("\u795E\u6797\u7FA9\u535A", "\xAF\xAB\xAA\x4C\xB8\x71\xB3\xD5", 'Big5-HKSCS') # 神林義åš
end
@@ -2275,7 +2265,7 @@ class TestTranscode < Test::Unit::TestCase
result = th.map(&:value)
end
end
- expected = "\xa4\xa2".force_encoding(Encoding::EUC_JP)
+ expected = "\xa4\xa2".dup.force_encoding(Encoding::EUC_JP)
assert_equal([expected]*num, result, bug11277)
end;
end
@@ -2308,4 +2298,34 @@ class TestTranscode < Test::Unit::TestCase
assert_equal("A\nB\nC", s.encode(usascii, lf_newline: true))
assert_equal("A\nB\nC", s.encode(usascii, newline: :lf))
end
+
+ private
+
+ def assert_conversion_both_ways_utf8(utf8, raw, encoding)
+ assert_conversion_both_ways(utf8, 'utf-8', raw, encoding)
+ end
+ alias check_both_ways assert_conversion_both_ways_utf8
+
+ def assert_conversion_both_ways(str1, enc1, str2, enc2)
+ message = str1.dump+str2.dump
+ assert_equal(str1.force_encoding(enc1), str2.encode(enc1, enc2), message)
+ assert_equal(str2.force_encoding(enc2), str1.encode(enc2, enc1), message)
+ end
+ alias check_both_ways2 assert_conversion_both_ways
+
+ def assert_undefined_conversion(str, to, from = nil)
+ assert_raise(Encoding::UndefinedConversionError) { str.encode(to, from) }
+ end
+
+ def assert_undefined_in(str, encoding)
+ assert_undefined_conversion(str, 'utf-8', encoding)
+ end
+
+ def assert_invalid_byte_sequence(str, to, from = nil)
+ assert_raise(Encoding::InvalidByteSequenceError) { str.encode(to, from) }
+ end
+
+ def assert_invalid_in(str, encoding)
+ assert_invalid_byte_sequence(str, 'utf-8', encoding)
+ end
end
diff --git a/test/ruby/test_variable.rb b/test/ruby/test_variable.rb
index e50b681ce8..86f2e4bb84 100644
--- a/test/ruby/test_variable.rb
+++ b/test/ruby/test_variable.rb
@@ -413,6 +413,18 @@ class TestVariable < Test::Unit::TestCase
assert_equal(%i(v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11), v, bug11674)
end
+ def test_many_instance_variables
+ objects = [Object.new, Hash.new, Module.new]
+ objects.each do |obj|
+ 1000.times do |i|
+ obj.instance_variable_set("@var#{i}", i)
+ end
+ 1000.times do |i|
+ assert_equal(i, obj.instance_variable_get("@var#{i}"))
+ end
+ end
+ end
+
private
def with_kwargs_11(v1:, v2:, v3:, v4:, v5:, v6:, v7:, v8:, v9:, v10:, v11:)
local_variables
diff --git a/test/ruby/test_vm_dump.rb b/test/ruby/test_vm_dump.rb
index 9c06ec14fb..c718f69316 100644
--- a/test/ruby/test_vm_dump.rb
+++ b/test/ruby/test_vm_dump.rb
@@ -1,14 +1,15 @@
# frozen_string_literal: true
require 'test/unit'
+return unless /darwin/ =~ RUBY_PLATFORM
+
class TestVMDump < Test::Unit::TestCase
def assert_darwin_vm_dump_works(args)
- omit if RUBY_PLATFORM !~ /darwin/
assert_in_out_err(args, "", [], /^\[IMPORTANT\]/)
end
def test_darwin_invalid_call
- assert_darwin_vm_dump_works(['-rfiddle', '-eFiddle::Function.new(Fiddle::Pointer.new(1), [], Fiddle::TYPE_VOID).call'])
+ assert_darwin_vm_dump_works(['-r-test-/fatal', '-eBug.invalid_call(1)'])
end
def test_darwin_segv_in_syscall
@@ -16,6 +17,6 @@ class TestVMDump < Test::Unit::TestCase
end
def test_darwin_invalid_access
- assert_darwin_vm_dump_works(['-rfiddle', '-eFiddle.dlunwrap(100).inspect'])
+ assert_darwin_vm_dump_works(['-r-test-/fatal', '-eBug.invalid_access(100)'])
end
end
diff --git a/test/ruby/test_weakkeymap.rb b/test/ruby/test_weakkeymap.rb
index 5df53749ca..6b3ffbb81f 100644
--- a/test/ruby/test_weakkeymap.rb
+++ b/test/ruby/test_weakkeymap.rb
@@ -87,7 +87,7 @@ class TestWeakKeyMap < Test::Unit::TestCase
assert_nothing_raised(FrozenError) {@wm['foo'] = o}
end
- def test_inconsistent_hash_key
+ def test_inconsistent_hash_key_memory_leak
assert_no_memory_leak [], '', <<~RUBY
class BadHash
def initialize
@@ -108,6 +108,26 @@ class TestWeakKeyMap < Test::Unit::TestCase
RUBY
end
+ def test_compaction
+ omit "compaction is not supported on this platform" unless GC.respond_to?(:compact)
+
+ assert_separately(%w(-robjspace), <<-'end;')
+ wm = ObjectSpace::WeakKeyMap.new
+ key = Object.new
+ val = Object.new
+ wm[key] = val
+
+ GC.verify_compaction_references(expand_heap: true, toward: :empty)
+
+ assert_equal(val, wm[key])
+ end;
+ end
+
+ def test_gc_compact_stress
+ omit "compaction doesn't work well on s390x" if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077
+ EnvUtil.under_gc_compact_stress { ObjectSpace::WeakKeyMap.new }
+ end
+
private
def assert_weak_include(m, k, n = 100)
diff --git a/test/ruby/test_weakmap.rb b/test/ruby/test_weakmap.rb
index f9358e11f3..97d7197dbb 100644
--- a/test/ruby/test_weakmap.rb
+++ b/test/ruby/test_weakmap.rb
@@ -193,16 +193,17 @@ class TestWeakMap < Test::Unit::TestCase
end;
end
- def test_compaction_bug_19529
+ def test_compaction
omit "compaction is not supported on this platform" unless GC.respond_to?(:compact)
+ # [Bug #19529]
obj = Object.new
100.times do |i|
GC.compact
@wm[i] = obj
end
- assert_separately(%w(--disable-gems), <<-'end;')
+ assert_separately([], <<-'end;')
wm = ObjectSpace::WeakMap.new
obj = Object.new
100.times do
@@ -211,6 +212,34 @@ class TestWeakMap < Test::Unit::TestCase
end
GC.compact
end;
+
+ assert_separately(%w(-robjspace), <<-'end;')
+ wm = ObjectSpace::WeakMap.new
+ key = Object.new
+ val = Object.new
+ wm[key] = val
+
+ GC.verify_compaction_references(expand_heap: true, toward: :empty)
+
+ assert_equal(val, wm[key])
+ end;
+
+ assert_separately(["-W0"], <<-'end;')
+ wm = ObjectSpace::WeakMap.new
+
+ ary = 10_000.times.map do
+ o = Object.new
+ wm[o] = 1
+ o
+ end
+
+ GC.verify_compaction_references(expand_heap: true, toward: :empty)
+ end;
+ end
+
+ def test_gc_compact_stress
+ omit "compaction doesn't work well on s390x" if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077
+ EnvUtil.under_gc_compact_stress { ObjectSpace::WeakMap.new }
end
def test_replaced_values_bug_19531
diff --git a/test/ruby/test_whileuntil.rb b/test/ruby/test_whileuntil.rb
index 121c44817d..ff6d29ac4a 100644
--- a/test/ruby/test_whileuntil.rb
+++ b/test/ruby/test_whileuntil.rb
@@ -73,6 +73,24 @@ class TestWhileuntil < Test::Unit::TestCase
}
end
+ def test_begin_while
+ i = 0
+ sum = 0
+ begin
+ i += 1
+ sum += i
+ end while i < 10
+ assert_equal([10, 55], [i, sum])
+
+ i = 0
+ sum = 0
+ (
+ i += 1
+ sum += i
+ ) while false
+ assert_equal([0, 0], [i, sum])
+ end
+
def test_until
i = 0
until i>4
diff --git a/test/ruby/test_yjit.rb b/test/ruby/test_yjit.rb
index 6700e58176..796787e355 100644
--- a/test/ruby/test_yjit.rb
+++ b/test/ruby/test_yjit.rb
@@ -10,6 +10,8 @@ require_relative '../lib/jit_support'
return unless JITSupport.yjit_supported?
+require 'stringio'
+
# Tests for YJIT with assertions on compilation and side exits
# insipired by the RJIT tests in test/ruby/test_rjit.rb
class TestYJIT < Test::Unit::TestCase
@@ -51,31 +53,85 @@ class TestYJIT < Test::Unit::TestCase
#assert_in_out_err('--yjit-call-threshold=', '', [], /--yjit-call-threshold needs an argument/)
end
- def test_starting_paused
- program = <<~RUBY
+ def test_yjit_enable
+ args = []
+ args << "--disable=yjit" if RubyVM::YJIT.enabled?
+ assert_separately(args, <<~RUBY)
+ assert_false RubyVM::YJIT.enabled?
+ assert_false RUBY_DESCRIPTION.include?("+YJIT")
+
+ RubyVM::YJIT.enable
+
+ assert_true RubyVM::YJIT.enabled?
+ assert_true RUBY_DESCRIPTION.include?("+YJIT")
+ RUBY
+ end
+
+ def test_yjit_enable_stats_false
+ assert_separately(["--yjit-disable", "--yjit-stats"], <<~RUBY, ignore_stderr: true)
+ assert_false RubyVM::YJIT.enabled?
+ assert_nil RubyVM::YJIT.runtime_stats
+
+ RubyVM::YJIT.enable
+
+ assert_true RubyVM::YJIT.enabled?
+ assert_true RubyVM::YJIT.runtime_stats[:all_stats]
+ RUBY
+ end
+
+ def test_yjit_enable_stats_true
+ args = []
+ args << "--disable=yjit" if RubyVM::YJIT.enabled?
+ assert_separately(args, <<~RUBY, ignore_stderr: true)
+ assert_false RubyVM::YJIT.enabled?
+ assert_nil RubyVM::YJIT.runtime_stats
+
+ RubyVM::YJIT.enable(stats: true)
+
+ assert_true RubyVM::YJIT.enabled?
+ assert_true RubyVM::YJIT.runtime_stats[:all_stats]
+ RUBY
+ end
+
+ def test_yjit_enable_stats_quiet
+ assert_in_out_err(['--yjit-disable', '-e', 'RubyVM::YJIT.enable(stats: true)']) do |_stdout, stderr, _status|
+ assert_not_empty stderr
+ end
+ assert_in_out_err(['--yjit-disable', '-e', 'RubyVM::YJIT.enable(stats: :quiet)']) do |_stdout, stderr, _status|
+ assert_empty stderr
+ end
+ end
+
+ def test_yjit_enable_with_call_threshold
+ assert_separately(%w[--yjit-disable --yjit-call-threshold=1], <<~RUBY)
def not_compiled = nil
def will_compile = nil
- def compiled_counts = RubyVM::YJIT.runtime_stats[:compiled_iseq_count]
- counts = []
+ def compiled_counts = RubyVM::YJIT.runtime_stats&.dig(:compiled_iseq_count)
+
not_compiled
- counts << compiled_counts
+ assert_nil compiled_counts
+ assert_false RubyVM::YJIT.enabled?
- RubyVM::YJIT.resume
+ RubyVM::YJIT.enable
will_compile
- counts << compiled_counts
+ assert compiled_counts > 0
+ assert_true RubyVM::YJIT.enabled?
+ RUBY
+ end
- if counts[0] == 0 && counts[1] > 0
- p :ok
- end
+ def test_yjit_enable_with_monkey_patch
+ assert_separately(%w[--yjit-disable], <<~RUBY)
+ # This lets rb_method_entry_at(rb_mKernel, ...) return NULL
+ Kernel.prepend(Module.new)
+
+ # This must not crash with "undefined optimized method!"
+ RubyVM::YJIT.enable
RUBY
- assert_in_out_err(%w[--yjit-pause --yjit-stats --yjit-call-threshold=1], program, success: true) do |stdout, stderr|
- assert_equal([":ok"], stdout)
- end
end
def test_yjit_stats_and_v_no_error
- _stdout, stderr, _status = EnvUtil.invoke_ruby(%w(-v --yjit-stats), '', true, true)
+ _stdout, stderr, _status = invoke_ruby(%w(-v --yjit-stats), '', true, true)
refute_includes(stderr, "NoMethodError")
end
@@ -264,10 +320,10 @@ class TestYJIT < Test::Unit::TestCase
end
def test_compile_opt_aset
- assert_compiles('[1,2,3][2] = 4', insns: %i[opt_aset])
- assert_compiles('{}[:foo] = :bar', insns: %i[opt_aset])
- assert_compiles('[1,2,3][0..-1] = []', insns: %i[opt_aset])
- assert_compiles('"foo"[3] = "d"', insns: %i[opt_aset])
+ assert_compiles('[1,2,3][2] = 4', insns: %i[opt_aset], frozen_string_literal: false)
+ assert_compiles('{}[:foo] = :bar', insns: %i[opt_aset], frozen_string_literal: false)
+ assert_compiles('[1,2,3][0..-1] = []', insns: %i[opt_aset], frozen_string_literal: false)
+ assert_compiles('"foo"[3] = "d"', insns: %i[opt_aset], frozen_string_literal: false)
end
def test_compile_attr_set
@@ -476,6 +532,32 @@ class TestYJIT < Test::Unit::TestCase
RUBY
end
+ def test_opt_getconstant_path_general
+ assert_compiles(<<~RUBY, result: [1, 1])
+ module Base
+ Const = 1
+ end
+
+ class Sub
+ def const
+ _const = nil # make a non-entry block for opt_getconstant_path
+ Const
+ end
+
+ def self.const_missing(n)
+ Base.const_get(n)
+ end
+ end
+
+
+ sub = Sub.new
+ result = []
+ result << sub.const # generate the general case
+ result << sub.const # const_missing does not invalidate the block
+ result
+ RUBY
+ end
+
def test_string_interpolation
assert_compiles(<<~'RUBY', insns: %i[objtostring anytostring concatstrings], result: "foobar", call_threshold: 2)
def make_str(foo, bar)
@@ -547,8 +629,7 @@ class TestYJIT < Test::Unit::TestCase
end
def test_getblockparamproxy
- # Currently two side exits as OPTIMIZED_METHOD_TYPE_CALL is unimplemented
- assert_compiles(<<~'RUBY', insns: [:getblockparamproxy], exits: { opt_send_without_block: 2 })
+ assert_compiles(<<~'RUBY', insns: [:getblockparamproxy], exits: {})
def foo &blk
p blk.call
p blk.call
@@ -559,6 +640,24 @@ class TestYJIT < Test::Unit::TestCase
RUBY
end
+ def test_ifunc_getblockparamproxy
+ assert_compiles(<<~'RUBY', insns: [:getblockparamproxy], exits: {})
+ class Foo
+ include Enumerable
+
+ def each(&block)
+ block.call 1
+ block.call 2
+ block.call 3
+ end
+ end
+
+ foo = Foo.new
+ foo.map { _1 * 2 }
+ foo.map { _1 * 2 }
+ RUBY
+ end
+
def test_send_blockarg
assert_compiles(<<~'RUBY', insns: [:getblockparamproxy, :send], exits: {})
def bar
@@ -607,7 +706,7 @@ class TestYJIT < Test::Unit::TestCase
def test_send_kwargs
# For now, this side-exits when calls include keyword args
- assert_compiles(<<~'RUBY', result: "2#a:1,b:2/A", exits: {opt_send_without_block: 1})
+ assert_compiles(<<~'RUBY', result: "2#a:1,b:2/A")
def internal_method(**kw)
"#{kw.size}##{kw.keys.map { |k| "#{k}:#{kw[k]}" }.join(",")}"
end
@@ -647,7 +746,7 @@ class TestYJIT < Test::Unit::TestCase
def test_send_kwargs_splat
# For now, this side-exits when calling with a splat
- assert_compiles(<<~'RUBY', result: "2#a:1,b:2/B", exits: {opt_send_without_block: 1})
+ assert_compiles(<<~'RUBY', result: "2#a:1,b:2/B")
def internal_method(**kw)
"#{kw.size}##{kw.keys.map { |k| "#{k}:#{kw[k]}" }.join(",")}"
end
@@ -661,7 +760,7 @@ class TestYJIT < Test::Unit::TestCase
def test_send_block
# Setlocal_wc_0 sometimes side-exits on write barrier
- assert_compiles(<<~'RUBY', result: "b:n/b:y/b:y/b:n", exits: { :setlocal_WC_0 => 0..1 })
+ assert_compiles(<<~'RUBY', result: "b:n/b:y/b:y/b:n")
def internal_method(&b)
"b:#{block_given? ? "y" : "n"}"
end
@@ -994,8 +1093,55 @@ class TestYJIT < Test::Unit::TestCase
RUBY
end
+ def test_disable_code_gc_with_many_iseqs
+ assert_compiles(code_gc_helpers + <<~'RUBY', exits: :any, result: :ok, mem_size: 1, code_gc: false)
+ fiber = Fiber.new {
+ # Loop to call the same basic block again after Fiber.yield
+ while true
+ Fiber.yield(nil.to_i)
+ end
+ }
+
+ return :not_paged1 unless add_pages(250) # use some pages
+ return :broken_resume1 if fiber.resume != 0 # leave an on-stack code as well
+
+ add_pages(2000) # use a whole lot of pages to run out of 1MiB
+ return :broken_resume2 if fiber.resume != 0 # on-stack code should be callable
+
+ code_gc_count = RubyVM::YJIT.runtime_stats[:code_gc_count]
+ return :"code_gc_#{code_gc_count}" if code_gc_count != 0
+
+ :ok
+ RUBY
+ end
+
def test_code_gc_with_many_iseqs
- assert_compiles(code_gc_helpers + <<~'RUBY', exits: :any, result: :ok, mem_size: 1)
+ assert_compiles(code_gc_helpers + <<~'RUBY', exits: :any, result: :ok, mem_size: 1, code_gc: true)
+ fiber = Fiber.new {
+ # Loop to call the same basic block again after Fiber.yield
+ while true
+ Fiber.yield(nil.to_i)
+ end
+ }
+
+ return :not_paged1 unless add_pages(250) # use some pages
+ return :broken_resume1 if fiber.resume != 0 # leave an on-stack code as well
+
+ add_pages(2000) # use a whole lot of pages to run out of 1MiB
+ return :broken_resume2 if fiber.resume != 0 # on-stack code should be callable
+
+ code_gc_count = RubyVM::YJIT.runtime_stats[:code_gc_count]
+ return :"code_gc_#{code_gc_count}" if code_gc_count == 0
+
+ :ok
+ RUBY
+ end
+
+ def test_code_gc_with_auto_compact
+ assert_compiles((code_gc_helpers + <<~'RUBY'), exits: :any, result: :ok, mem_size: 1, code_gc: true)
+ # Test ISEQ moves in the middle of code GC
+ GC.auto_compact = true
+
fiber = Fiber.new {
# Loop to call the same basic block again after Fiber.yield
while true
@@ -1106,7 +1252,7 @@ class TestYJIT < Test::Unit::TestCase
def test_bug_19316
n = 2 ** 64
# foo's extra param and the splats are relevant
- assert_compiles(<<~'RUBY', result: [[n, -n], [n, -n]])
+ assert_compiles(<<~'RUBY', result: [[n, -n], [n, -n]], exits: :any)
def foo(_, a, b, c)
[a & b, ~c]
end
@@ -1136,7 +1282,7 @@ class TestYJIT < Test::Unit::TestCase
end
def test_invalidate_cyclic_branch
- assert_compiles(<<~'RUBY', result: 2)
+ assert_compiles(<<~'RUBY', result: 2, exits: { opt_plus: 1 })
def foo
i = 0
while i < 2
@@ -1154,7 +1300,7 @@ class TestYJIT < Test::Unit::TestCase
end
def test_tracing_str_uplus
- assert_compiles(<<~RUBY, frozen_string_literal: true, result: :ok)
+ assert_compiles(<<~RUBY, frozen_string_literal: true, result: :ok, exits: { putspecialobject: 1, definemethod: 1 })
def str_uplus
_ = 1
_ = 2
@@ -1199,7 +1345,7 @@ class TestYJIT < Test::Unit::TestCase
def test_return_to_invalidated_block
# [Bug #19463]
- assert_compiles(<<~RUBY, result: [1, 1, :ugokanai])
+ assert_compiles(<<~RUBY, result: [1, 1, :ugokanai], exits: { definesmethod: 1, getlocal_WC_0: 1 })
klass = Class.new do
def self.lookup(hash, key) = hash[key]
@@ -1238,9 +1384,62 @@ class TestYJIT < Test::Unit::TestCase
RUBY
end
+ def test_return_to_invalidated_frame
+ assert_compiles(code_gc_helpers + <<~RUBY, exits: :any, result: :ok)
+ def jump
+ [] # something not inlined
+ end
+
+ def entry(code_gc)
+ jit_exception(code_gc)
+ jump # faulty jump after code GC. #jit_exception should not come back.
+ end
+
+ def jit_exception(code_gc)
+ if code_gc
+ tap do
+ RubyVM::YJIT.code_gc
+ break # jit_exec_exception catches TAG_BREAK and re-enters JIT code
+ end
+ end
+ end
+
+ add_pages(100)
+ jump # Compile #jump in a non-first page
+ add_pages(100)
+ entry(false) # Compile #entry and its call to #jump in another page
+ entry(true) # Free #jump but not #entry
+
+ :ok
+ RUBY
+ end
+
+ def test_setivar_on_class
+ # Bug in https://github.com/ruby/ruby/pull/8152
+ assert_compiles(<<~RUBY, result: :ok)
+ class Base
+ def self.or_equal
+ @or_equal ||= Object.new
+ end
+ end
+
+ Base.or_equal # ensure compiled
+
+ class Child < Base
+ end
+
+ 200.times do |iv| # Need to be more than MAX_IVAR
+ Child.instance_variable_set("@_iv_\#{iv}", Object.new)
+ end
+
+ Child.or_equal
+ :ok
+ RUBY
+ end
+
def test_nested_send
#[Bug #19464]
- assert_compiles(<<~RUBY, result: [:ok, :ok])
+ assert_compiles(<<~RUBY, result: [:ok, :ok], exits: { defineclass: 1 })
klass = Class.new do
class << self
alias_method :my_send, :send
@@ -1259,7 +1458,7 @@ class TestYJIT < Test::Unit::TestCase
end
def test_str_concat_encoding_mismatch
- assert_compiles(<<~'RUBY', result: "incompatible character encodings: ASCII-8BIT and EUC-JP")
+ assert_compiles(<<~'RUBY', result: "incompatible character encodings: BINARY (ASCII-8BIT) and EUC-JP")
def bar(a, b)
a << b
rescue => e
@@ -1272,8 +1471,145 @@ class TestYJIT < Test::Unit::TestCase
end
h = Hash.new { nil }
- foo("\x80".b, "\xA1A1".force_encoding("EUC-JP"), h)
- foo("\x80".b, "\xA1A1".force_encoding("EUC-JP"), h)
+ foo("\x80".b, "\xA1A1".dup.force_encoding("EUC-JP"), h)
+ foo("\x80".b, "\xA1A1".dup.force_encoding("EUC-JP"), h)
+ RUBY
+ end
+
+ def test_io_reopen_clobbering_singleton_class
+ assert_compiles(<<~RUBY, result: [:ok, :ok], exits: { definesmethod: 1, opt_eq: 2 })
+ def $stderr.to_i = :i
+
+ def test = $stderr.to_i
+
+ [test, test]
+ $stderr.reopen($stderr.dup)
+ [test, test].map { :ok unless _1 == :i }
+ RUBY
+ end
+
+ def test_opt_aref_with
+ assert_compiles(<<~RUBY, insns: %i[opt_aref_with], result: "bar", frozen_string_literal: false)
+ h = {"foo" => "bar"}
+
+ h["foo"]
+ RUBY
+ end
+
+ def test_proc_block_arg
+ assert_compiles(<<~RUBY, result: [:proc, :no_block])
+ def yield_if_given = block_given? ? yield : :no_block
+
+ def call(block_arg = nil) = yield_if_given(&block_arg)
+
+ [call(-> { :proc }), call]
+ RUBY
+ end
+
+ def test_opt_mult_overflow
+ assert_no_exits('0xfff_ffff_ffff_ffff * 0x10')
+ end
+
+ def test_disable_stats
+ assert_in_out_err(%w[--yjit-stats --yjit-disable])
+ end
+
+ def test_odd_calls_to_attr_reader
+ # Use of delegate from ActiveSupport use these kind of calls to getter methods.
+ assert_compiles(<<~RUBY, result: [1, 1, 1], no_send_fallbacks: true)
+ class One
+ attr_reader :one
+ def initialize
+ @one = 1
+ end
+ end
+
+ def calls(obj, empty, &)
+ [obj.one(*empty), obj.one(&), obj.one(*empty, &)]
+ end
+
+ calls(One.new, [])
+ RUBY
+ end
+
+ def test_kwrest
+ assert_compiles(<<~RUBY, result: true, no_send_fallbacks: true)
+ def req_rest(r1:, **kwrest) = [r1, kwrest]
+ def opt_rest(r1: 1.succ, **kwrest) = [r1, kwrest]
+ def kwrest(**kwrest) = kwrest
+
+ def calls
+ [
+ [1, {}] == req_rest(r1: 1),
+ [1, {:r2=>2, :r3=>3}] == req_rest(r1: 1, r2: 2, r3: 3),
+ [1, {:r2=>2, :r3=>3}] == req_rest(r2: 2, r1:1, r3: 3),
+ [1, {:r2=>2, :r3=>3}] == req_rest(r2: 2, r3: 3, r1: 1),
+
+ [2, {}] == opt_rest,
+ [2, { r2: 2, r3: 3 }] == opt_rest(r2: 2, r3: 3),
+ [0, { r2: 2, r3: 3 }] == opt_rest(r1: 0, r3: 3, r2: 2),
+ [0, { r2: 2, r3: 3 }] == opt_rest(r2: 2, r1: 0, r3: 3),
+ [1, { r2: 2, r3: 3 }] == opt_rest(r2: 2, r3: 3, r1: 1),
+
+ {} == kwrest,
+ { r0: 88, r1: 99 } == kwrest(r0: 88, r1: 99),
+ ]
+ end
+
+ calls.all?
+ RUBY
+ end
+
+ def test_send_polymorphic_method_name
+ assert_compiles(<<~'RUBY', result: %i[ok ok], no_send_fallbacks: true)
+ mid = "dynamic_mid_#{rand(100..200)}"
+ mid_dsym = mid.to_sym
+
+ define_method(mid) { :ok }
+
+ define_method(:send_site) { send(_1) }
+
+ [send_site(mid), send_site(mid_dsym)]
+ RUBY
+ end
+
+ def test_kw_splat_nil
+ assert_compiles(<<~'RUBY', result: %i[ok ok ok], no_send_fallbacks: true)
+ def id(x) = x
+ def kw_fw(arg, **) = id(arg, **)
+ def fw(...) = id(...)
+ def use = [fw(:ok), kw_fw(:ok), :ok.itself(**nil)]
+
+ use
+ RUBY
+ end
+
+ def test_empty_splat
+ assert_compiles(<<~'RUBY', result: %i[ok ok], no_send_fallbacks: true)
+ def foo = :ok
+ def fw(...) = foo(...)
+ def use(empty) = [foo(*empty), fw]
+
+ use([])
+ RUBY
+ end
+
+ def test_byteslice_sp_invalidation
+ assert_compiles(<<~'RUBY', result: 'ok', no_send_fallbacks: true)
+ "okng".itself.byteslice(0, 2)
+ RUBY
+ end
+
+ def test_leaf_builtin
+ assert_compiles(code_gc_helpers + <<~'RUBY', exits: :any, result: 1)
+ before = RubyVM::YJIT.runtime_stats[:num_send_iseq_leaf]
+ return 1 if before.nil?
+
+ def entry = self.class
+ entry
+
+ after = RubyVM::YJIT.runtime_stats[:num_send_iseq_leaf]
+ after - before
RUBY
end
@@ -1288,9 +1624,9 @@ class TestYJIT < Test::Unit::TestCase
end
def add_pages(num_jits)
- pages = RubyVM::YJIT.runtime_stats[:compiled_page_count]
+ pages = RubyVM::YJIT.runtime_stats[:live_page_count]
num_jits.times { return false unless eval('compiles { nil.to_i }') }
- pages.nil? || pages < RubyVM::YJIT.runtime_stats[:compiled_page_count]
+ pages.nil? || pages < RubyVM::YJIT.runtime_stats[:live_page_count]
end
RUBY
end
@@ -1300,7 +1636,17 @@ class TestYJIT < Test::Unit::TestCase
end
ANY = Object.new
- def assert_compiles(test_script, insns: [], call_threshold: 1, stdout: nil, exits: {}, result: ANY, frozen_string_literal: nil, mem_size: nil)
+ def assert_compiles(
+ test_script, insns: [],
+ call_threshold: 1,
+ stdout: nil,
+ exits: {},
+ result: ANY,
+ frozen_string_literal: nil,
+ mem_size: nil,
+ code_gc: false,
+ no_send_fallbacks: false
+ )
reset_stats = <<~RUBY
RubyVM::YJIT.runtime_stats
RubyVM::YJIT.reset_stats!
@@ -1325,7 +1671,7 @@ class TestYJIT < Test::Unit::TestCase
RUBY
script = <<~RUBY
- #{"# frozen_string_literal: true" if frozen_string_literal}
+ #{"# frozen_string_literal: " + frozen_string_literal.to_s unless frozen_string_literal.nil?}
_test_proc = -> {
#{test_script}
}
@@ -1334,7 +1680,7 @@ class TestYJIT < Test::Unit::TestCase
#{write_results}
RUBY
- status, out, err, stats = eval_with_jit(script, call_threshold:, mem_size:)
+ status, out, err, stats = eval_with_jit(script, call_threshold:, mem_size:, code_gc:)
assert status.success?, "exited with status #{status.to_i}, stderr:\n#{err}"
@@ -1360,12 +1706,23 @@ class TestYJIT < Test::Unit::TestCase
# barriers, cache misses.)
if exits != :any &&
exits != recorded_exits &&
- !exits.all? { |k, v| v === recorded_exits[k] } # triple-equal checks range membership or integer equality
- flunk "Expected #{exits.empty? ? "no" : exits.inspect} exits" \
- ", but got\n#{recorded_exits.inspect}"
+ (exits.keys != recorded_exits.keys || !exits.all? { |k, v| v === recorded_exits[k] }) # triple-equal checks range membership or integer equality
+ stats_reasons = StringIO.new
+ ::RubyVM::YJIT.send(:_print_stats_reasons, runtime_stats, stats_reasons)
+ stats_reasons = stats_reasons.string
+ flunk <<~EOM
+ Expected #{exits.empty? ? "no" : exits.inspect} exits, but got:
+ #{recorded_exits.inspect}
+ Reasons:
+ #{stats_reasons}
+ EOM
end
end
+ if no_send_fallbacks
+ assert_equal(0, runtime_stats[:num_send_dynamic], "Expected no use of fallback implementation")
+ end
+
# Only available when --enable-yjit=dev
if runtime_stats[:all_stats]
missed_insns = insns.dup
@@ -1388,13 +1745,14 @@ class TestYJIT < Test::Unit::TestCase
s.chars.map { |c| c.ascii_only? ? c : "\\u%x" % c.codepoints[0] }.join
end
- def eval_with_jit(script, call_threshold: 1, timeout: 1000, mem_size: nil)
+ def eval_with_jit(script, call_threshold: 1, timeout: 1000, mem_size: nil, code_gc: false)
args = [
"--disable-gems",
"--yjit-call-threshold=#{call_threshold}",
- "--yjit-stats"
+ "--yjit-stats=quiet"
]
args << "--yjit-exec-mem-size=#{mem_size}" if mem_size
+ args << "--yjit-code-gc" if code_gc
args << "-e" << script_shell_encode(script)
stats_r, stats_w = IO.pipe
# Separate thread so we don't deadlock when
@@ -1404,9 +1762,7 @@ class TestYJIT < Test::Unit::TestCase
stats = stats_r.read
stats_r.close
end
- out, err, status = EnvUtil.invoke_ruby(args,
- '', true, true, timeout: timeout, ios: {3 => stats_w}
- )
+ out, err, status = invoke_ruby(args, '', true, true, timeout: timeout, ios: { 3 => stats_w })
stats_w.close
stats_reader.join(timeout)
stats = Marshal.load(stats) if !stats.empty?
@@ -1417,4 +1773,10 @@ class TestYJIT < Test::Unit::TestCase
stats_r&.close
stats_w&.close
end
+
+ # A wrapper of EnvUtil.invoke_ruby that uses RbConfig.ruby instead of EnvUtil.ruby
+ # that might use a wrong Ruby depending on your environment.
+ def invoke_ruby(*args, **kwargs)
+ EnvUtil.invoke_ruby(*args, rubybin: RbConfig.ruby, **kwargs)
+ end
end
diff --git a/test/ruby/test_yjit_exit_locations.rb b/test/ruby/test_yjit_exit_locations.rb
index 851a582470..816ab457ce 100644
--- a/test/ruby/test_yjit_exit_locations.rb
+++ b/test/ruby/test_yjit_exit_locations.rb
@@ -18,22 +18,8 @@ class TestYJITExitLocations < Test::Unit::TestCase
refute_includes(stderr, "NoMethodError")
end
- def test_trace_exits_setclassvariable
- script = 'class Foo; def self.foo; @@foo = 1; end; end; Foo.foo'
- assert_exit_locations(script)
- end
-
- def test_trace_exits_putobject
- assert_exit_locations('true')
- assert_exit_locations('123')
- assert_exit_locations(':foo')
- end
-
- def test_trace_exits_opt_not
- assert_exit_locations('!false')
- assert_exit_locations('!nil')
- assert_exit_locations('!true')
- assert_exit_locations('![]')
+ def test_trace_exits_expandarray_splat
+ assert_exit_locations('*arr = []')
end
private
diff --git a/test/rubygems/bundler_test_gem.rb b/test/rubygems/bundler_test_gem.rb
index 78adfc94c5..ca2980e04b 100644
--- a/test/rubygems/bundler_test_gem.rb
+++ b/test/rubygems/bundler_test_gem.rb
@@ -3,7 +3,7 @@
require_relative "helper"
class TestBundlerGem < Gem::TestCase
- PROJECT_DIR = File.expand_path("../..", __dir__).tap(&Gem::UNTAINT)
+ PROJECT_DIR = File.expand_path("../..", __dir__)
def test_self_use_gemdeps
with_local_bundler_at(Gem.dir) do
@@ -131,9 +131,9 @@ class TestBundlerGem < Gem::TestCase
install_specs a, b, c
- install_gem a, :install_dir => path
- install_gem b, :install_dir => path
- install_gem c, :install_dir => path
+ install_gem a, install_dir: path
+ install_gem b, install_dir: path
+ install_gem c, install_dir: path
ENV["GEM_PATH"] = path
@@ -183,9 +183,9 @@ class TestBundlerGem < Gem::TestCase
install_specs a, b, c
- install_gem a, :install_dir => path
- install_gem b, :install_dir => path
- install_gem c, :install_dir => path
+ install_gem a, install_dir: path
+ install_gem b, install_dir: path
+ install_gem c, install_dir: path
ENV["GEM_PATH"] = path
@@ -201,7 +201,7 @@ class TestBundlerGem < Gem::TestCase
f.puts "gem 'a'"
end
out0 = with_path_and_rubyopt(new_path, new_rubyopt) do
- IO.popen("foo", :chdir => "sub1", &:read).split(/\n/)
+ IO.popen("foo", chdir: "sub1", &:read).split(/\n/)
end
File.open path, "a" do |f|
@@ -209,7 +209,7 @@ class TestBundlerGem < Gem::TestCase
f.puts "gem 'c'"
end
out = with_path_and_rubyopt(new_path, new_rubyopt) do
- IO.popen("foo", :chdir => "sub1", &:read).split(/\n/)
+ IO.popen("foo", chdir: "sub1", &:read).split(/\n/)
end
Dir.rmdir "sub1"
@@ -221,7 +221,7 @@ class TestBundlerGem < Gem::TestCase
def test_use_gemdeps
with_local_bundler_at(Gem.dir) do
- gem_deps_file = "gem.deps.rb".tap(&Gem::UNTAINT)
+ gem_deps_file = "gem.deps.rb"
spec = util_spec "a", 1
install_specs spec
diff --git a/test/rubygems/helper.rb b/test/rubygems/helper.rb
index a9c1438272..f97306717d 100644
--- a/test/rubygems/helper.rb
+++ b/test/rubygems/helper.rb
@@ -17,7 +17,7 @@ require "pp"
require "rubygems/package"
require "shellwords"
require "tmpdir"
-require "uri"
+require "rubygems/vendor/uri/lib/uri"
require "zlib"
require "benchmark" # stdlib
require_relative "mock_gem_ui"
@@ -76,6 +76,8 @@ class Gem::TestCase < Test::Unit::TestCase
attr_accessor :uri # :nodoc:
+ @@tempdirs = []
+
def assert_activate(expected, *specs)
specs.each do |spec|
case spec
@@ -105,39 +107,32 @@ class Gem::TestCase < Test::Unit::TestCase
refute File.directory?(path), msg
end
- # https://github.com/seattlerb/minitest/blob/21d9e804b63c619f602f3f4ece6c71b48974707a/lib/minitest/assertions.rb#L188
- def _synchronize
- yield
- end
-
- # https://github.com/seattlerb/minitest/blob/21d9e804b63c619f602f3f4ece6c71b48974707a/lib/minitest/assertions.rb#L546
+ # Originally copied from minitest/assertions.rb
def capture_subprocess_io
- _synchronize do
- require "tempfile"
+ require "tempfile"
- captured_stdout = Tempfile.new("out")
- captured_stderr = Tempfile.new("err")
+ captured_stdout = Tempfile.new("out")
+ captured_stderr = Tempfile.new("err")
- orig_stdout = $stdout.dup
- orig_stderr = $stderr.dup
- $stdout.reopen captured_stdout
- $stderr.reopen captured_stderr
+ orig_stdout = $stdout.dup
+ orig_stderr = $stderr.dup
+ $stdout.reopen captured_stdout
+ $stderr.reopen captured_stderr
- yield
+ yield
- $stdout.rewind
- $stderr.rewind
+ $stdout.rewind
+ $stderr.rewind
- return captured_stdout.read, captured_stderr.read
- ensure
- $stdout.reopen orig_stdout
- $stderr.reopen orig_stderr
+ [captured_stdout.read, captured_stderr.read]
+ ensure
+ $stdout.reopen orig_stdout
+ $stderr.reopen orig_stderr
- orig_stdout.close
- orig_stderr.close
- captured_stdout.close!
- captured_stderr.close!
- end
+ orig_stdout.close
+ orig_stderr.close
+ captured_stdout.close!
+ captured_stderr.close!
end
##
@@ -158,6 +153,14 @@ class Gem::TestCase < Test::Unit::TestCase
end
##
+ # Overrides the Gem.install_extension_in_lib function and restores the
+ # original when the block ends
+ #
+ def extension_in_lib(value = true) # :nodoc:
+ Gem.stub(:install_extension_in_lib, value) { yield }
+ end
+
+ ##
# Sets the vendordir entry in RbConfig::CONFIG to +value+ and restores the
# original value when the block ends
#
@@ -282,12 +285,14 @@ class Gem::TestCase < Test::Unit::TestCase
def setup
@orig_hooks = {}
@orig_env = ENV.to_hash
- @tmp = File.expand_path("tmp")
- FileUtils.mkdir_p @tmp
+ top_srcdir = __dir__ + "/../.."
+ @tmp = File.expand_path(ENV.fetch("GEM_TEST_TMPDIR", "tmp"), top_srcdir)
+
+ FileUtils.mkdir_p(@tmp, mode: 0o700) # =rwx
+ @tmp = File.realpath(@tmp)
@tempdir = Dir.mktmpdir("test_rubygems_", @tmp)
- @tempdir.tap(&Gem::UNTAINT)
ENV["GEM_VENDOR"] = nil
ENV["GEMRC"] = nil
@@ -337,7 +342,6 @@ class Gem::TestCase < Test::Unit::TestCase
File.expand_path(s)
end
if expand_path != s
- expand_path.tap(&Gem::UNTAINT)
if s.instance_variable_defined?(:@gem_prelude_index)
expand_path.instance_variable_set(:@gem_prelude_index, expand_path)
end
@@ -350,11 +354,16 @@ class Gem::TestCase < Test::Unit::TestCase
Dir.chdir @tempdir
ENV["HOME"] = @userhome
+ # Remove "RUBY_CODESIGN", which is used by mkmf-generated Makefile to
+ # sign extension bundles on macOS, to avoid trying to find the specified key
+ # from the fake $HOME/Library/Keychains directory.
+ ENV.delete "RUBY_CODESIGN"
Gem.instance_variable_set :@config_file, nil
Gem.instance_variable_set :@user_home, nil
Gem.instance_variable_set :@config_home, nil
Gem.instance_variable_set :@data_home, nil
Gem.instance_variable_set :@state_home, @statehome
+ Gem.instance_variable_set :@state_file, nil
Gem.instance_variable_set :@gemdeps, nil
Gem.instance_variable_set :@env_requirements_by_name, nil
Gem.send :remove_instance_variable, :@ruby_version if
@@ -364,7 +373,7 @@ class Gem::TestCase < Test::Unit::TestCase
ENV["GEM_PRIVATE_KEY_PASSPHRASE"] = PRIVATE_KEY_PASSPHRASE
- Gem.instance_variable_set(:@default_specifications_dir, nil)
+ Gem.instance_variable_set(:@default_specifications_dir, File.join(@gemhome, "specifications", "default"))
if Gem.java_platform?
@orig_default_gem_home = RbConfig::CONFIG["default_gem_home"]
RbConfig::CONFIG["default_gem_home"] = @gemhome
@@ -396,7 +405,7 @@ class Gem::TestCase < Test::Unit::TestCase
Gem::RemoteFetcher.fetcher = Gem::FakeFetcher.new
@gem_repo = "http://gems.example.com/"
- @uri = URI.parse @gem_repo
+ @uri = Gem::URI.parse @gem_repo
Gem.sources.replace [@gem_repo]
Gem.searcher = nil
@@ -471,6 +480,13 @@ class Gem::TestCase < Test::Unit::TestCase
end
@back_ui.close
+
+ refute_directory_exists @tempdir, "may be still in use"
+ ghosts = @@tempdirs.filter_map do |test_name, tempdir|
+ test_name if File.exist?(tempdir)
+ end
+ @@tempdirs << [method_name, @tempdir]
+ assert_empty ghosts
end
def credential_setup
@@ -576,7 +592,7 @@ class Gem::TestCase < Test::Unit::TestCase
end
def in_path?(executable) # :nodoc:
- return true if %r{\A([A-Z]:|/)} =~ executable && File.exist?(executable)
+ return true if %r{\A([A-Z]:|/)}.match?(executable) && File.exist?(executable)
ENV["PATH"].split(File::PATH_SEPARATOR).any? do |directory|
File.exist? File.join directory, executable
@@ -598,17 +614,17 @@ class Gem::TestCase < Test::Unit::TestCase
end
end
- gem = File.join(@tempdir, File.basename(gem)).tap(&Gem::UNTAINT)
+ gem = File.join(@tempdir, File.basename(gem))
end
- Gem::Installer.at(gem, options.merge({ :wrappers => true })).install
+ Gem::Installer.at(gem, options.merge({ wrappers: true })).install
end
##
# Builds and installs the Gem::Specification +spec+ into the user dir
def install_gem_user(spec)
- install_gem spec, :user_install => true
+ install_gem spec, user_install: true
end
##
@@ -620,7 +636,7 @@ class Gem::TestCase < Test::Unit::TestCase
def ask_if_ok(spec)
true
end
- end.new(spec.name, :executables => true, :user_install => true).uninstall
+ end.new(spec.name, executables: true, user_install: true).uninstall
end
##
@@ -637,7 +653,7 @@ class Gem::TestCase < Test::Unit::TestCase
# Reads a Marshal file at +path+
def read_cache(path)
- File.open path.dup.tap(&Gem::UNTAINT), "rb" do |io|
+ File.open path.dup, "rb" do |io|
Marshal.load io.read
end
end
@@ -772,7 +788,7 @@ class Gem::TestCase < Test::Unit::TestCase
def install_specs(*specs)
specs.each do |spec|
- Gem::Installer.for_spec(spec, :force => true).install
+ Gem::Installer.for_spec(spec, force: true).install
end
Gem.searcher = nil
@@ -783,7 +799,7 @@ class Gem::TestCase < Test::Unit::TestCase
def install_default_gems(*specs)
specs.each do |spec|
- installer = Gem::Installer.for_spec(spec, :install_as_default => true)
+ installer = Gem::Installer.for_spec(spec, install_as_default: true)
installer.install
Gem.register_default_spec(spec)
end
@@ -1354,12 +1370,12 @@ Also, a list:
#
# Yields the +specification+ to the block, if given
- def vendor_gem(name = "a", version = 1)
+ def vendor_gem(name = "a", version = 1, &block)
directory = File.join "vendor", name
FileUtils.mkdir_p directory
- save_gemspec name, version, directory
+ save_gemspec name, version, directory, &block
end
##
diff --git a/test/rubygems/installer_test_case.rb b/test/rubygems/installer_test_case.rb
index 705de6804e..abddcbe848 100644
--- a/test/rubygems/installer_test_case.rb
+++ b/test/rubygems/installer_test_case.rb
@@ -111,7 +111,7 @@ class Gem::InstallerTestCase < Gem::TestCase
def setup_base_installer(force = true)
@gem = setup_base_gem
- util_installer @spec, @gemhome, false, force
+ util_installer @spec, @gemhome, force
end
##
@@ -163,7 +163,7 @@ class Gem::InstallerTestCase < Gem::TestCase
@user_gem = @user_spec.cache_file
- util_installer @user_spec, Gem.user_dir, :user
+ Gem::Installer.at @user_gem, user_install: true
end
##
@@ -215,18 +215,16 @@ class Gem::InstallerTestCase < Gem::TestCase
end
end
- Gem::Installer.at @gem, :force => force
+ Gem::Installer.at @gem, force: force
end
##
- # Creates an installer for +spec+ that will install into +gem_home+. If
- # +user+ is true a user-install will be performed.
+ # Creates an installer for +spec+ that will install into +gem_home+.
- def util_installer(spec, gem_home, user=false, force=true)
+ def util_installer(spec, gem_home, force=true)
Gem::Installer.at(spec.cache_file,
- :install_dir => gem_home,
- :user_install => user,
- :force => force)
+ install_dir: gem_home,
+ force: force)
end
@@symlink_supported = nil
diff --git a/test/rubygems/multifactor_auth_utilities.rb b/test/rubygems/multifactor_auth_utilities.rb
new file mode 100644
index 0000000000..1133131a76
--- /dev/null
+++ b/test/rubygems/multifactor_auth_utilities.rb
@@ -0,0 +1,111 @@
+# frozen_string_literal: true
+
+##
+# A MultifactorAuthFetcher is a FakeFetcher that adds paths to data for requests related to
+# multi-factor authentication.
+#
+
+require_relative "utilities"
+require "json"
+
+class Gem::MultifactorAuthFetcher < Gem::FakeFetcher
+ attr_reader :host, :webauthn_url
+
+ # GET /api/v1/webauthn_verification defaults to user does not have any security devices
+ def initialize(host: nil)
+ super()
+ @host = host || Gem.host
+ @path_token = "odow34b93t6aPCdY"
+ @webauthn_url = "#{@host}/webauthn_verification/#{@path_token}"
+ @data["#{@host}/api/v1/webauthn_verification"] = Gem::HTTPResponseFactory.create(
+ body: "You don't have any security devices",
+ code: 422,
+ msg: "Unprocessable Entity"
+ )
+ end
+
+ # given a url, return a response that requires multifactor authentication
+ def respond_with_require_otp(url, success_body)
+ response_fail = "You have enabled multifactor authentication"
+
+ @data[url] = proc do
+ @call_count ||= 0
+ if (@call_count += 1).odd?
+ Gem::HTTPResponseFactory.create(body: response_fail, code: 401, msg: "Unauthorized")
+ else
+ Gem::HTTPResponseFactory.create(body: success_body, code: 200, msg: "OK")
+ end
+ end
+ end
+
+ # GET /api/v1/webauthn_verification returns a webauthn url
+ # GET /api/v1/webauthn_verification/:token/status.json (polling url) returns pending status
+ def respond_with_webauthn_url
+ @data["#{@host}/api/v1/webauthn_verification"] = Gem::HTTPResponseFactory.create(body: @webauthn_url, code: 200, msg: "OK")
+ @data["#{@host}/api/v1/webauthn_verification/#{@path_token}/status.json"] = Gem::HTTPResponseFactory.create(
+ body: { status: "pending", message: "Security device authentication is still pending." }.to_json,
+ code: 200,
+ msg: "OK"
+ )
+ end
+
+ # GET /api/v1/webauthn_verification/:token/status.json returns success status with OTP code
+ def respond_with_webauthn_polling(code)
+ @data["#{@host}/api/v1/webauthn_verification/#{@path_token}/status.json"] = Gem::HTTPResponseFactory.create(
+ body: { status: "success", code: code }.to_json,
+ code: 200,
+ msg: "OK"
+ )
+ end
+
+ # GET /api/v1/webauthn_verification/:token/status.json returns expired status
+ def respond_with_webauthn_polling_failure
+ @data["#{@host}/api/v1/webauthn_verification/#{@path_token}/status.json"] = Gem::HTTPResponseFactory.create(
+ body: {
+ status: "expired",
+ message: "The token in the link you used has either expired or been used already.",
+ }.to_json,
+ code: 200,
+ msg: "OK"
+ )
+ end
+
+ def webauthn_url_with_port(port)
+ "#{@webauthn_url}?port=#{port}"
+ end
+end
+
+##
+# The MockTCPServer for use in tests or to avoid real TCPServer instances to be created
+# when testing code related to the WebAuthn listener.
+#
+# Example:
+#
+# server = Gem::MockTCPServer
+# port = server.addr[1].to_s
+#
+# # this mocks waiting for a request by calling sleep
+# server.accept
+#
+# # this mocks the server closing
+# server.close
+
+class Gem::MockTCPServer
+ attr_reader :port
+
+ def initialize(port = 5678)
+ @port = port
+ end
+
+ def close
+ true
+ end
+
+ def addr
+ ["AF_INET6", @port, "::", "::"]
+ end
+
+ def accept
+ sleep
+ end
+end
diff --git a/test/rubygems/specifications/rubyforge-0.0.1.gemspec b/test/rubygems/specifications/rubyforge-0.0.1.gemspec
index 49619bf569..0421b675a0 100644
--- a/test/rubygems/specifications/rubyforge-0.0.1.gemspec
+++ b/test/rubygems/specifications/rubyforge-0.0.1.gemspec
@@ -1,14 +1,15 @@
# frozen_string_literal: true
Gem::Specification.new do |s|
- s.name = "rubyforge"
- s.version = "0.0.1"
- s.platform = "ruby"
- s.require_paths = ["lib"]
- s.summary = "A very bar gem"
- s.authors = ["unknown"]
- s.license = "MIT"
- s.homepage = "http://example.com"
- s.files = ["README.md"]
- s.rubyforge_project = "abc"
+ s.name = "rubyforge"
+ s.version = "0.0.1"
+ s.platform = "ruby"
+ s.require_paths = ["lib"]
+ s.summary = "A very bar gem"
+ s.authors = ["unknown"]
+ s.license = "MIT"
+ s.homepage = "http://example.com"
+ s.files = ["README.md"]
+ s.rubyforge_project = "abc"
+ s.required_ruby_version = ">= 1.9.3"
end
diff --git a/test/rubygems/test_bundled_ca.rb b/test/rubygems/test_bundled_ca.rb
index 47c1d34df5..50e621f22b 100644
--- a/test/rubygems/test_bundled_ca.rb
+++ b/test/rubygems/test_bundled_ca.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require_relative "helper"
-require "net/http"
+require "rubygems/vendored_net_http"
require "rubygems/openssl"
unless Gem::HAVE_OPENSSL
@@ -28,12 +28,12 @@ class TestGemBundledCA < Gem::TestCase
def assert_https(host)
assert true
- http = Net::HTTP.new(host, 443)
+ http = Gem::Net::HTTP.new(host, 443)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.cert_store = bundled_certificate_store
http.get("/")
- rescue Errno::ENOENT, Errno::ETIMEDOUT, SocketError
+ rescue Errno::ENOENT, Errno::ETIMEDOUT, SocketError, Net::OpenTimeout
pend "#{host} seems offline, I can't tell whether ssl would work."
rescue OpenSSL::SSL::SSLError => e
# Only fail for certificate verification errors
diff --git a/test/rubygems/test_gem.rb b/test/rubygems/test_gem.rb
index 6d36a8bb62..244b7749a5 100644
--- a/test/rubygems/test_gem.rb
+++ b/test/rubygems/test_gem.rb
@@ -11,7 +11,7 @@ require "rbconfig"
class TestGem < Gem::TestCase
PLUGINS_LOADED = [] # rubocop:disable Style/MutableConstant
- PROJECT_DIR = File.expand_path("../..", __dir__).tap(&Gem::UNTAINT)
+ PROJECT_DIR = File.expand_path("../..", __dir__)
def setup
super
@@ -96,7 +96,7 @@ class TestGem < Gem::TestCase
gemhome2 = "#{@gemhome}2"
- installed = Gem.install "a", "= 1", :install_dir => gemhome2
+ installed = Gem.install "a", "= 1", install_dir: gemhome2
assert_equal %w[a-1], installed.map(&:full_name)
@@ -115,7 +115,7 @@ class TestGem < Gem::TestCase
begin
raise "Error"
rescue StandardError
- Gem.install "a", "= 1", :install_dir => gemhome2
+ Gem.install "a", "= 1", install_dir: gemhome2
end
assert_equal %w[a-1], installed.map(&:full_name)
end
@@ -133,7 +133,7 @@ class TestGem < Gem::TestCase
def test_self_install_permissions_umask_077
umask = File.umask(0o077)
- assert_self_install_permissions
+ assert_self_install_permissions(data_mode: 0o600)
ensure
File.umask(umask)
end
@@ -151,14 +151,14 @@ class TestGem < Gem::TestCase
Gem::Installer.exec_format = nil
end
- def assert_self_install_permissions(format_executable: false)
+ def assert_self_install_permissions(format_executable: false, data_mode: 0o640)
mask = Gem.win_platform? ? 0o700 : 0o777
options = {
- :dir_mode => 0o500,
- :prog_mode => Gem.win_platform? ? 0o410 : 0o510,
- :data_mode => 0o640,
- :wrappers => true,
- :format_executable => format_executable,
+ dir_mode: 0o500,
+ prog_mode: Gem.win_platform? ? 0o410 : 0o510,
+ data_mode: data_mode,
+ wrappers: true,
+ format_executable: format_executable,
}
Dir.chdir @tempdir do
Dir.mkdir "bin"
@@ -201,7 +201,7 @@ class TestGem < Gem::TestCase
end
assert_equal(expected, result)
ensure
- File.chmod(0o755, *Dir.glob(@gemhome + "/gems/**/").map {|path| path.tap(&Gem::UNTAINT) })
+ File.chmod(0o755, *Dir.glob(@gemhome + "/gems/**/"))
end
def test_require_missing
@@ -1254,8 +1254,8 @@ class TestGem < Gem::TestCase
Gem.try_activate "a_file"
end
- assert_match(/Could not find 'b' /, e.message)
- assert_match(/at: #{a.spec_file}/, e.message)
+ assert_include(e.message, "Could not find 'b' ")
+ assert_include(e.message, "at: #{a.spec_file}")
end
def test_self_try_activate_missing_prerelease
@@ -1552,9 +1552,9 @@ class TestGem < Gem::TestCase
g = util_spec "g", "1", nil, "lib/g.rb"
m = util_spec "m", "1", nil, "lib/m.rb"
- install_gem g, :install_dir => Gem.dir
- m0 = install_gem m, :install_dir => Gem.dir
- m1 = install_gem m, :install_dir => Gem.user_dir
+ install_gem g, install_dir: Gem.dir
+ m0 = install_gem m, install_dir: Gem.dir
+ m1 = install_gem m, install_dir: Gem.user_dir
assert_equal m0.gem_dir, File.join(Gem.dir, "gems", "m-1")
assert_equal m1.gem_dir, File.join(Gem.user_dir, "gems", "m-1")
@@ -1608,9 +1608,9 @@ class TestGem < Gem::TestCase
g = util_spec "g", "1", nil, "lib/g.rb"
m = util_spec "m", "1", nil, "lib/m.rb"
- install_gem g, :install_dir => Gem.dir
- install_gem m, :install_dir => Gem.dir
- install_gem m, :install_dir => Gem.user_dir
+ install_gem g, install_dir: Gem.dir
+ install_gem m, install_dir: Gem.dir
+ install_gem m, install_dir: Gem.user_dir
Gem.use_paths Gem.dir, [Gem.dir, Gem.user_dir]
diff --git a/test/rubygems/test_gem_ci_detector.rb b/test/rubygems/test_gem_ci_detector.rb
new file mode 100644
index 0000000000..3caefce97d
--- /dev/null
+++ b/test/rubygems/test_gem_ci_detector.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require_relative "helper"
+require "rubygems"
+
+class TestCiDetector < Test::Unit::TestCase
+ def test_ci?
+ with_env("FOO" => "bar") { assert_equal(false, Gem::CIDetector.ci?) }
+ with_env("CI" => "true") { assert_equal(true, Gem::CIDetector.ci?) }
+ with_env("CONTINUOUS_INTEGRATION" => "1") { assert_equal(true, Gem::CIDetector.ci?) }
+ with_env("RUN_ID" => "0", "TASKCLUSTER_ROOT_URL" => "2") do
+ assert_equal(true, Gem::CIDetector.ci?)
+ end
+ end
+
+ def test_ci_strings
+ with_env("FOO" => "bar") { assert_empty(Gem::CIDetector.ci_strings) }
+ with_env("TRAVIS" => "true") { assert_equal(["travis"], Gem::CIDetector.ci_strings) }
+ with_env("CI" => "true", "CIRCLECI" => "true", "GITHUB_ACTIONS" => "true") do
+ assert_equal(["ci", "circle", "github"], Gem::CIDetector.ci_strings)
+ end
+ with_env("CI" => "true", "CI_NAME" => "MYCI") do
+ assert_equal(["ci", "myci"], Gem::CIDetector.ci_strings)
+ end
+ with_env("GITHUB_ACTIONS" => "true", "CI_NAME" => "github") do
+ assert_equal(["github"], Gem::CIDetector.ci_strings)
+ end
+ with_env("TASKCLUSTER_ROOT_URL" => "https://foo.bar", "DSARI" => "1", "CI_NAME" => "") do
+ assert_equal(["dsari", "taskcluster"], Gem::CIDetector.ci_strings)
+ end
+ end
+
+ private
+
+ def with_env(overrides, &block)
+ @orig_env = ENV.to_h
+ ENV.replace(overrides)
+ begin
+ block.call
+ ensure
+ ENV.replace(@orig_env)
+ end
+ end
+end
diff --git a/test/rubygems/test_gem_command.rb b/test/rubygems/test_gem_command.rb
index 3d0f04830e..3695f9488f 100644
--- a/test/rubygems/test_gem_command.rb
+++ b/test/rubygems/test_gem_command.rb
@@ -92,7 +92,7 @@ class TestGemCommand < Gem::TestCase
options[:help] = value
end
- @cmd.defaults = { :help => true }
+ @cmd.defaults = { help: true }
@cmd.when_invoked do |options|
assert options[:help], "Help options should default true"
diff --git a/test/rubygems/test_gem_command_manager.rb b/test/rubygems/test_gem_command_manager.rb
index 6e27e849bc..f04ec0cafa 100644
--- a/test/rubygems/test_gem_command_manager.rb
+++ b/test/rubygems/test_gem_command_manager.rb
@@ -4,7 +4,7 @@ require_relative "helper"
require "rubygems/command_manager"
class TestGemCommandManager < Gem::TestCase
- PROJECT_DIR = File.expand_path("../..", __dir__).tap(&Gem::UNTAINT)
+ PROJECT_DIR = File.expand_path("../..", __dir__)
def setup
super
@@ -150,7 +150,7 @@ class TestGemCommandManager < Gem::TestCase
end
end
- assert_match(/install isn't a directory./i, @ui.error)
+ assert_match(/install isn't a directory\./i, @ui.error)
end
def test_process_args_with_c_flag_path_not_found
@@ -164,7 +164,7 @@ class TestGemCommandManager < Gem::TestCase
end
end
- assert_match(/#{custom_start_point} isn't a directory./i, @ui.error)
+ assert_match(/#{Regexp.quote(custom_start_point)} isn't a directory\./i, @ui.error)
end
def test_process_args_bad_arg
@@ -368,7 +368,7 @@ class TestGemCommandManager < Gem::TestCase
end
assert_equal "pew pew!\n", @ui.output
- assert_match(/WARNING: foo command is deprecated. It will be removed in Rubygems [0-9]+/, @ui.error)
+ assert_match(/WARNING: foo command is deprecated\. It will be removed in Rubygems [0-9]+/, @ui.error)
ensure
Gem::Commands.send(:remove_const, :FooCommand)
end
@@ -393,7 +393,7 @@ class TestGemCommandManager < Gem::TestCase
end
assert_equal "pew pew!\n", @ui.output
- assert_match(/WARNING: foo command is deprecated. It will be removed in Rubygems 9.9.9/, @ui.error)
+ assert_match(/WARNING: foo command is deprecated\. It will be removed in Rubygems 9\.9\.9/, @ui.error)
ensure
Gem::Commands.send(:remove_const, :FooCommand)
end
diff --git a/test/rubygems/test_gem_commands_build_command.rb b/test/rubygems/test_gem_commands_build_command.rb
index 42f09003fc..d44126d204 100644
--- a/test/rubygems/test_gem_commands_build_command.rb
+++ b/test/rubygems/test_gem_commands_build_command.rb
@@ -26,8 +26,9 @@ class TestGemCommandsBuildCommand < Gem::TestCase
end
@gem = util_spec "some_gem" do |s|
- s.license = "AGPL-3.0"
+ s.license = "AGPL-3.0-only"
s.files = ["README.md"]
+ s.required_ruby_version = "2.3.0"
end
@cmd = Gem::Commands::BuildCommand.new
@@ -178,6 +179,7 @@ class TestGemCommandsBuildCommand < Gem::TestCase
def test_execute_strict_with_warnings
bad_gem = util_spec "some_bad_gem" do |s|
s.files = ["README.md"]
+ s.required_ruby_version = ">= 1.9.3"
end
gemspec_file = File.join(@tempdir, bad_gem.spec_name)
@@ -198,8 +200,9 @@ class TestGemCommandsBuildCommand < Gem::TestCase
end
error = @ui.error.split "\n"
- assert_equal "WARNING: licenses is empty, but is recommended. Use a license identifier from", error.shift
- assert_equal "http://spdx.org/licenses or 'Nonstandard' for a nonstandard license.", error.shift
+ assert_equal "WARNING: licenses is empty, but is recommended. Use an license identifier from", error.shift
+ assert_equal "https://spdx.org/licenses or 'Nonstandard' for a nonstandard license,", error.shift
+ assert_equal "or set it to nil if you don't want to specify a license.", error.shift
assert_equal "WARNING: See https://guides.rubygems.org/specification-reference/ for help", error.shift
assert_equal [], error
diff --git a/test/rubygems/test_gem_commands_cert_command.rb b/test/rubygems/test_gem_commands_cert_command.rb
index 5249b25c8c..c173467935 100644
--- a/test/rubygems/test_gem_commands_cert_command.rb
+++ b/test/rubygems/test_gem_commands_cert_command.rb
@@ -677,8 +677,9 @@ ERROR: --private-key not specified and ~/.gem/gem-private_key.pem does not exis
expected_path = File.join(gem_path, "#{File.basename(tmp_expired_cert_file)}.expired")
+ assert_include(@ui.output, "INFO: Your certificate #{tmp_expired_cert_file} has been re-signed\n")
assert_match(
- /INFO: Your certificate #{tmp_expired_cert_file} has been re-signed\nINFO: Your expired certificate will be located at: #{expected_path}\.[0-9]+/,
+ /INFO: Your expired certificate will be located at: #{Regexp.quote(expected_path)}\.[0-9]+/,
@ui.output
)
assert_equal "", @ui.error
diff --git a/test/rubygems/test_gem_commands_cleanup_command.rb b/test/rubygems/test_gem_commands_cleanup_command.rb
index 732b3288a5..bcb8871b57 100644
--- a/test/rubygems/test_gem_commands_cleanup_command.rb
+++ b/test/rubygems/test_gem_commands_cleanup_command.rb
@@ -169,7 +169,7 @@ class TestGemCommandsCleanupCommand < Gem::TestCase
FileUtils.chmod 0o555, @gemhome
@a_1_1, = util_gem "a", "1.1"
- @a_1_1 = install_gem @a_1_1, :user_install => true # pick up user install path
+ @a_1_1 = install_gem @a_1_1, user_install: true # pick up user install path
Gem::Specification.dirs = [Gem.dir, Gem.user_dir]
@@ -243,11 +243,11 @@ class TestGemCommandsCleanupCommand < Gem::TestCase
e_1, = util_gem "e", "1"
e_2, = util_gem "e", "2"
- c_1 = install_gem c_1, :user_install => true # pick up user install path
+ c_1 = install_gem c_1, user_install: true # pick up user install path
c_2 = install_gem c_2
d_1 = install_gem d_1
- d_2 = install_gem d_2, :user_install => true # pick up user install path
+ d_2 = install_gem d_2, user_install: true # pick up user install path
e_1 = install_gem e_1
e_2 = install_gem e_2
@@ -270,8 +270,8 @@ class TestGemCommandsCleanupCommand < Gem::TestCase
d_1, = util_gem "d", "1.0"
d_2, = util_gem "d", "1.1"
- c_1 = install_gem c_1, :user_install => true # pick up user install path
- c_2 = install_gem c_2, :user_install => true # pick up user install path
+ c_1 = install_gem c_1, user_install: true # pick up user install path
+ c_2 = install_gem c_2, user_install: true # pick up user install path
d_1 = install_gem d_1
d_2 = install_gem d_2
diff --git a/test/rubygems/test_gem_commands_environment_command.rb b/test/rubygems/test_gem_commands_environment_command.rb
index f527574c07..48252d84d4 100644
--- a/test/rubygems/test_gem_commands_environment_command.rb
+++ b/test/rubygems/test_gem_commands_environment_command.rb
@@ -30,7 +30,7 @@ class TestGemCommandsEnvironmentCommand < Gem::TestCase
assert_match(/USER INSTALLATION DIRECTORY: #{Regexp.escape Gem.user_dir}/,
@ui.output)
assert_match(/RUBYGEMS PREFIX: /, @ui.output)
- assert_match(/RUBY EXECUTABLE:.*#{RbConfig::CONFIG['ruby_install_name']}/,
+ assert_match(/RUBY EXECUTABLE:.*#{RbConfig::CONFIG["ruby_install_name"]}/,
@ui.output)
assert_match(/GIT EXECUTABLE: #{@cmd.send(:git_path)}/, @ui.output)
assert_match(/SYSTEM CONFIGURATION DIRECTORY:/, @ui.output)
diff --git a/test/rubygems/test_gem_commands_exec_command.rb b/test/rubygems/test_gem_commands_exec_command.rb
index e8bd354bc3..e52fe247a2 100644
--- a/test/rubygems/test_gem_commands_exec_command.rb
+++ b/test/rubygems/test_gem_commands_exec_command.rb
@@ -71,10 +71,10 @@ class TestGemCommandsExecCommand < Gem::TestCase
assert_equal options, {
args: ["install", "--no-color", "--help", "--verbose"],
executable: "pod",
- :explicit_prerelease => false,
+ explicit_prerelease: false,
gem_name: "cocoapods",
prerelease: false,
- :version => Gem::Requirement.new(["> 1", "< 1.3"]),
+ version: Gem::Requirement.new(["> 1", "< 1.3"]),
build_args: nil,
}
end
@@ -87,7 +87,7 @@ class TestGemCommandsExecCommand < Gem::TestCase
args: [],
executable: "rails",
gem_name: "rails",
- :version => Gem::Requirement.new([">= 0"]),
+ version: Gem::Requirement.new([">= 0"]),
build_args: nil,
}
end
@@ -100,7 +100,7 @@ class TestGemCommandsExecCommand < Gem::TestCase
args: [],
executable: "rails",
gem_name: "rails",
- :version => Gem::Requirement.new(["= 7.1"]),
+ version: Gem::Requirement.new(["= 7.1"]),
build_args: nil,
}
end
@@ -736,6 +736,8 @@ class TestGemCommandsExecCommand < Gem::TestCase
rescue StandardError
nil
end
+
+ assert_empty @ui.error
refute_includes @ui.output, "running gem exec with"
assert_includes @ui.output, "Successfully uninstalled a-2\n"
diff --git a/test/rubygems/test_gem_commands_generate_index_command.rb b/test/rubygems/test_gem_commands_generate_index_command.rb
deleted file mode 100644
index 7e719a5f60..0000000000
--- a/test/rubygems/test_gem_commands_generate_index_command.rb
+++ /dev/null
@@ -1,81 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "helper"
-require "rubygems/indexer"
-require "rubygems/commands/generate_index_command"
-
-class TestGemCommandsGenerateIndexCommand < Gem::TestCase
- def setup
- super
-
- @cmd = Gem::Commands::GenerateIndexCommand.new
- @cmd.options[:directory] = @gemhome
- end
-
- def test_execute
- use_ui @ui do
- @cmd.execute
- end
-
- specs = File.join @gemhome, "specs.4.8.gz"
-
- assert File.exist?(specs), specs
- end
-
- def test_execute_no_modern
- @cmd.options[:modern] = false
-
- use_ui @ui do
- @cmd.execute
- end
-
- specs = File.join @gemhome, "specs.4.8.gz"
-
- assert File.exist?(specs), specs
- end
-
- def test_handle_options_directory
- return if Gem.win_platform?
- refute_equal "/nonexistent", @cmd.options[:directory]
-
- @cmd.handle_options %w[--directory /nonexistent]
-
- assert_equal "/nonexistent", @cmd.options[:directory]
- end
-
- def test_handle_options_directory_windows
- return unless Gem.win_platform?
-
- refute_equal "/nonexistent", @cmd.options[:directory]
-
- @cmd.handle_options %w[--directory C:/nonexistent]
-
- assert_equal "C:/nonexistent", @cmd.options[:directory]
- end
-
- def test_handle_options_update
- @cmd.handle_options %w[--update]
-
- assert @cmd.options[:update]
- end
-
- def test_handle_options_modern
- use_ui @ui do
- @cmd.handle_options %w[--modern]
- end
-
- assert_equal \
- "WARNING: The \"--modern\" option has been deprecated and will be removed in Rubygems 4.0. Modern indexes (specs, latest_specs, and prerelease_specs) are always generated, so this option is not needed.\n",
- @ui.error
- end
-
- def test_handle_options_no_modern
- use_ui @ui do
- @cmd.handle_options %w[--no-modern]
- end
-
- assert_equal \
- "WARNING: The \"--no-modern\" option has been deprecated and will be removed in Rubygems 4.0. The `--no-modern` option is currently ignored. Modern indexes (specs, latest_specs, and prerelease_specs) are always generated.\n",
- @ui.error
- end
-end
diff --git a/test/rubygems/test_gem_commands_info_command.rb b/test/rubygems/test_gem_commands_info_command.rb
index 4e2a8f3622..83e4c8a896 100644
--- a/test/rubygems/test_gem_commands_info_command.rb
+++ b/test/rubygems/test_gem_commands_info_command.rb
@@ -33,12 +33,12 @@ class TestGemCommandsInfoCommand < Gem::TestCase
@cmd.execute
end
- assert_match %r{#{@gem.name} \(#{@gem.version}\)\n}, @ui.output
- assert_match(/Authors: #{@gem.authors.join(', ')}\n/, @ui.output)
- assert_match(/Homepage: #{@gem.homepage}\n/, @ui.output)
- assert_match(/License: #{@gem.license}\n/, @ui.output)
- assert_match(/Installed at: #{@gem.base_dir}\n/, @ui.output)
- assert_match(/#{@gem.summary}\n/, @ui.output)
+ assert_include(@ui.output, "#{@gem.name} (#{@gem.version})\n")
+ assert_include(@ui.output, "Authors: #{@gem.authors.join(", ")}\n")
+ assert_include(@ui.output, "Homepage: #{@gem.homepage}\n")
+ assert_include(@ui.output, "License: #{@gem.license}\n")
+ assert_include(@ui.output, "Installed at: #{@gem.base_dir}\n")
+ assert_include(@ui.output, "#{@gem.summary}\n")
assert_match "", @ui.error
end
diff --git a/test/rubygems/test_gem_commands_install_command.rb b/test/rubygems/test_gem_commands_install_command.rb
index 90c030951e..5b09512ac4 100644
--- a/test/rubygems/test_gem_commands_install_command.rb
+++ b/test/rubygems/test_gem_commands_install_command.rb
@@ -192,31 +192,26 @@ ERROR: Could not find a valid gem 'bar' (= 0.5) (required by 'foo' (>= 0)) in a
pend "skipped on MS Windows (chmod has no effect)" if Gem.win_platform?
pend "skipped in root privilege" if Process.uid.zero?
- specs = spec_fetcher do |fetcher|
- fetcher.gem "a", 2
+ spec_fetcher do |fetcher|
+ fetcher.download "a", 2
end
@cmd.options[:user_install] = false
- FileUtils.mv specs["a-2"].cache_file, @tempdir
-
@cmd.options[:args] = %w[a]
use_ui @ui do
- orig_dir = Dir.pwd
- begin
- FileUtils.chmod 0o755, @userhome
- FileUtils.chmod 0o555, @gemhome
+ FileUtils.chmod 0o755, @userhome
+ FileUtils.chmod 0o555, @gemhome
- Dir.chdir @tempdir
- assert_raise Gem::FilePermissionError do
- @cmd.execute
- end
- ensure
- Dir.chdir orig_dir
- FileUtils.chmod 0o755, @gemhome
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
end
+ ensure
+ FileUtils.chmod 0o755, @gemhome
end
+
+ assert_equal %w[a-2], @cmd.installed_specs.map(&:full_name).sort
end
def test_execute_local_missing
@@ -436,21 +431,6 @@ ERROR: Possible alternatives: non_existent_with_hint
assert_equal expected, output
end
- def test_execute_conflicting_install_options
- @cmd.options[:user_install] = true
- @cmd.options[:install_dir] = "whatever"
-
- use_ui @ui do
- assert_raise Gem::MockGemUi::TermError do
- @cmd.execute
- end
- end
-
- expected = "ERROR: Use --install-dir or --user-install but not both\n"
-
- assert_equal expected, @ui.error
- end
-
def test_execute_prerelease_skipped_when_no_flag_set
spec_fetcher do |fetcher|
fetcher.gem "a", 1
@@ -1357,7 +1337,7 @@ ERROR: Possible alternatives: non_existent_with_hint
fetcher.gem "r", "2.0", "q" => nil
end
- i = Gem::Installer.at specs["q-1.0"].cache_file, :install_dir => "gf-path"
+ i = Gem::Installer.at specs["q-1.0"].cache_file, install_dir: "gf-path"
i.install
assert File.file?("gf-path/specifications/q-1.0.gemspec"), "not installed"
@@ -1554,7 +1534,7 @@ ERROR: Possible alternatives: non_existent_with_hint
end
def test_suggest_update_if_enabled
- TestUpdateSuggestion.with_eglible_environment(cmd: @cmd) do
+ TestUpdateSuggestion.with_eligible_environment(cmd: @cmd) do
spec_fetcher do |fetcher|
fetcher.gem "a", 2
end
diff --git a/test/rubygems/test_gem_commands_open_command.rb b/test/rubygems/test_gem_commands_open_command.rb
index 2519a20883..d9e518048c 100644
--- a/test/rubygems/test_gem_commands_open_command.rb
+++ b/test/rubygems/test_gem_commands_open_command.rb
@@ -22,20 +22,23 @@ class TestGemCommandsOpenCommand < Gem::TestCase
def test_execute
@cmd.options[:args] = %w[foo]
- @cmd.options[:editor] = "#{ruby_with_rubygems_in_load_path} -eexit --"
+ @cmd.options[:editor] = (ruby_with_rubygems_in_load_path + ["-e", "puts(ARGV,Dir.pwd)", "--"]).join(" ")
gem "foo", "1.0.0"
spec = gem "foo", "1.0.1"
assert_nothing_raised Gem::MockGemUi::TermError do
- Dir.stub(:chdir, spec.full_gem_path) do
+ stdout, stderr = capture_subprocess_io do
use_ui @ui do
@cmd.execute
end
end
+ assert_equal [spec.full_gem_path, spec.full_gem_path], stdout.split("\n")
+ assert_equal "", stderr
end
assert_equal "", @ui.error
+ assert_equal "", @ui.output
end
def test_wrong_version
diff --git a/test/rubygems/test_gem_commands_owner_command.rb b/test/rubygems/test_gem_commands_owner_command.rb
index 091335ab4b..eddd8afaf5 100644
--- a/test/rubygems/test_gem_commands_owner_command.rb
+++ b/test/rubygems/test_gem_commands_owner_command.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require_relative "helper"
+require_relative "multifactor_auth_utilities"
require "rubygems/commands/owner_command"
class TestGemCommandsOwnerCommand < Gem::TestCase
@@ -11,7 +12,7 @@ class TestGemCommandsOwnerCommand < Gem::TestCase
ENV["RUBYGEMS_HOST"] = nil
@stub_ui = Gem::MockGemUi.new
- @stub_fetcher = Gem::FakeFetcher.new
+ @stub_fetcher = Gem::MultifactorAuthFetcher.new
Gem::RemoteFetcher.fetcher = @stub_fetcher
Gem.configuration = nil
Gem.configuration.rubygems_api_key = "ed244fbf2b1a52e012da8616c512fa47f9aa5250"
@@ -43,7 +44,7 @@ EOF
@cmd.show_owners("freewill")
end
- assert_equal Net::HTTP::Get, @stub_fetcher.last_request.class
+ assert_equal Gem::Net::HTTP::Get, @stub_fetcher.last_request.class
assert_equal Gem.configuration.rubygems_api_key, @stub_fetcher.last_request["Authorization"]
assert_match(/Owners for gem: freewill/, @stub_ui.output)
@@ -164,7 +165,7 @@ EOF
@cmd.add_owners("freewill", ["user-new1@example.com"])
end
- assert_equal Net::HTTP::Post, @stub_fetcher.last_request.class
+ assert_equal Gem::Net::HTTP::Post, @stub_fetcher.last_request.class
assert_equal Gem.configuration.rubygems_api_key, @stub_fetcher.last_request["Authorization"]
assert_equal "email=user-new1%40example.com", @stub_fetcher.last_request.body
@@ -243,7 +244,7 @@ EOF
@cmd.remove_owners("freewill", ["user-remove1@example.com"])
end
- assert_equal Net::HTTP::Delete, @stub_fetcher.last_request.class
+ assert_equal Gem::Net::HTTP::Delete, @stub_fetcher.last_request.class
assert_equal Gem.configuration.rubygems_api_key, @stub_fetcher.last_request["Authorization"]
assert_equal "email=user-remove1%40example.com", @stub_fetcher.last_request.body
@@ -324,15 +325,8 @@ EOF
end
def test_otp_verified_success
- response_fail = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry."
response_success = "Owner added successfully."
-
- @stub_fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [
- HTTPResponseFactory.create(body: response_fail, code: 401, msg: "Unauthorized"),
- HTTPResponseFactory.create(body: response_success, code: 200, msg: "OK"),
- ]
- @stub_fetcher.data["#{Gem.host}/api/v1/webauthn_verification"] =
- HTTPResponseFactory.create(body: "You don't have any security devices", code: 422, msg: "Unprocessable Entity")
+ @stub_fetcher.respond_with_require_otp("#{Gem.host}/api/v1/gems/freewill/owners", response_success)
@otp_ui = Gem::MockGemUi.new "111111\n"
use_ui @otp_ui do
@@ -363,68 +357,102 @@ EOF
end
def test_with_webauthn_enabled_success
- webauthn_verification_url = "rubygems.org/api/v1/webauthn_verification/odow34b93t6aPCdY"
- response_fail = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry."
response_success = "Owner added successfully."
- port = 5678
- server = TCPServer.new(port)
+ server = Gem::MockTCPServer.new
- @stub_fetcher.data["#{Gem.host}/api/v1/webauthn_verification"] = HTTPResponseFactory.create(body: webauthn_verification_url, code: 200, msg: "OK")
- @stub_fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [
- HTTPResponseFactory.create(body: response_fail, code: 401, msg: "Unauthorized"),
- HTTPResponseFactory.create(body: response_success, code: 200, msg: "OK"),
- ]
+ @stub_fetcher.respond_with_require_otp("#{Gem.host}/api/v1/gems/freewill/owners", response_success)
+ @stub_fetcher.respond_with_webauthn_url
TCPServer.stub(:new, server) do
- Gem::WebauthnListener.stub(:wait_for_otp_code, "Uvh6T57tkWuUnWYo") do
+ Gem::GemcutterUtilities::WebauthnListener.stub(:listener_thread, Thread.new { Thread.current[:otp] = "Uvh6T57tkWuUnWYo" }) do
use_ui @stub_ui do
@cmd.add_owners("freewill", ["user-new1@example.com"])
end
end
- ensure
- server.close
end
- url_with_port = "#{webauthn_verification_url}?port=#{port}"
- assert_match "You have enabled multi-factor authentication. Please visit #{url_with_port} to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin command with the `--otp [your_code]` option.", @stub_ui.output
+ assert_match "You have enabled multi-factor authentication. Please visit #{@stub_fetcher.webauthn_url_with_port(server.port)} " \
+ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
+ "you can re-run the gem signin command with the `--otp [your_code]` option.", @stub_ui.output
assert_match "You are verified with a security device. You may close the browser window.", @stub_ui.output
assert_equal "Uvh6T57tkWuUnWYo", @stub_fetcher.last_request["OTP"]
assert_match response_success, @stub_ui.output
end
def test_with_webauthn_enabled_failure
- webauthn_verification_url = "rubygems.org/api/v1/webauthn_verification/odow34b93t6aPCdY"
- response_fail = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry."
response_success = "Owner added successfully."
- port = 5678
- server = TCPServer.new(port)
- raise_error = ->(*_args) { raise Gem::WebauthnVerificationError, "Something went wrong" }
+ server = Gem::MockTCPServer.new
+ error = Gem::WebauthnVerificationError.new("Something went wrong")
- @stub_fetcher.data["#{Gem.host}/api/v1/webauthn_verification"] = HTTPResponseFactory.create(body: webauthn_verification_url, code: 200, msg: "OK")
- @stub_fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [
- HTTPResponseFactory.create(body: response_fail, code: 401, msg: "Unauthorized"),
- HTTPResponseFactory.create(body: response_success, code: 200, msg: "OK"),
- ]
+ @stub_fetcher.respond_with_require_otp("#{Gem.host}/api/v1/gems/freewill/owners", response_success)
+ @stub_fetcher.respond_with_webauthn_url
TCPServer.stub(:new, server) do
- Gem::WebauthnListener.stub(:wait_for_otp_code, raise_error) do
+ Gem::GemcutterUtilities::WebauthnListener.stub(:listener_thread, Thread.new { Thread.current[:error] = error }) do
use_ui @stub_ui do
@cmd.add_owners("freewill", ["user-new1@example.com"])
end
end
- ensure
- server.close
end
- url_with_port = "#{webauthn_verification_url}?port=#{port}"
-
assert_match @stub_fetcher.last_request["Authorization"], Gem.configuration.rubygems_api_key
- assert_match "You have enabled multi-factor authentication. Please visit #{url_with_port} to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin command with the `--otp [your_code]` option.", @stub_ui.output
+ assert_match "You have enabled multi-factor authentication. Please visit #{@stub_fetcher.webauthn_url_with_port(server.port)} " \
+ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
+ "you can re-run the gem signin command with the `--otp [your_code]` option.", @stub_ui.output
assert_match "ERROR: Security device verification failed: Something went wrong", @stub_ui.error
refute_match "You are verified with a security device. You may close the browser window.", @stub_ui.output
refute_match response_success, @stub_ui.output
end
+ def test_with_webauthn_enabled_success_with_polling
+ response_success = "Owner added successfully."
+ server = Gem::MockTCPServer.new
+
+ @stub_fetcher.respond_with_require_otp("#{Gem.host}/api/v1/gems/freewill/owners", response_success)
+ @stub_fetcher.respond_with_webauthn_url
+ @stub_fetcher.respond_with_webauthn_polling("Uvh6T57tkWuUnWYo")
+
+ TCPServer.stub(:new, server) do
+ use_ui @stub_ui do
+ @cmd.add_owners("freewill", ["user-new1@example.com"])
+ end
+ end
+
+ assert_match "You have enabled multi-factor authentication. Please visit #{@stub_fetcher.webauthn_url_with_port(server.port)} " \
+ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin " \
+ "command with the `--otp [your_code]` option.", @stub_ui.output
+ assert_match "You are verified with a security device. You may close the browser window.", @stub_ui.output
+ assert_equal "Uvh6T57tkWuUnWYo", @stub_fetcher.last_request["OTP"]
+ assert_match response_success, @stub_ui.output
+ end
+
+ def test_with_webauthn_enabled_failure_with_polling
+ response_success = "Owner added successfully."
+ server = Gem::MockTCPServer.new
+
+ @stub_fetcher.respond_with_require_otp(
+ "#{Gem.host}/api/v1/gems/freewill/owners",
+ response_success
+ )
+ @stub_fetcher.respond_with_webauthn_url
+ @stub_fetcher.respond_with_webauthn_polling_failure
+
+ TCPServer.stub(:new, server) do
+ use_ui @stub_ui do
+ @cmd.add_owners("freewill", ["user-new1@example.com"])
+ end
+ end
+
+ assert_match @stub_fetcher.last_request["Authorization"], Gem.configuration.rubygems_api_key
+ assert_match "You have enabled multi-factor authentication. Please visit #{@stub_fetcher.webauthn_url_with_port(server.port)} " \
+ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin " \
+ "command with the `--otp [your_code]` option.", @stub_ui.output
+ assert_match "ERROR: Security device verification failed: The token in the link you used has either expired " \
+ "or been used already.", @stub_ui.error
+ refute_match "You are verified with a security device. You may close the browser window.", @stub_ui.output
+ refute_match response_success, @stub_ui.output
+ end
+
def test_remove_owners_unathorized_api_key
response_forbidden = "The API key doesn't have access"
response_success = "Owner removed successfully."
@@ -443,7 +471,7 @@ EOF
access_notice = "The existing key doesn't have access of remove_owner on RubyGems.org. Please sign in to update access."
assert_match access_notice, @stub_ui.output
- assert_match "Email:", @stub_ui.output
+ assert_match "Username/email:", @stub_ui.output
assert_match "Password:", @stub_ui.output
assert_match "Added remove_owner scope to the existing API key", @stub_ui.output
assert_match response_success, @stub_ui.output
@@ -467,7 +495,7 @@ EOF
access_notice = "The existing key doesn't have access of add_owner on RubyGems.org. Please sign in to update access."
assert_match access_notice, @stub_ui.output
- assert_match "Email:", @stub_ui.output
+ assert_match "Username/email:", @stub_ui.output
assert_match "Password:", @stub_ui.output
assert_match "Added add_owner scope to the existing API key", @stub_ui.output
assert_match response_success, @stub_ui.output
diff --git a/test/rubygems/test_gem_commands_pristine_command.rb b/test/rubygems/test_gem_commands_pristine_command.rb
index ff5e6f166d..a17d7837c9 100644
--- a/test/rubygems/test_gem_commands_pristine_command.rb
+++ b/test/rubygems/test_gem_commands_pristine_command.rb
@@ -296,7 +296,7 @@ class TestGemCommandsPristineCommand < Gem::TestCase
build_args = %w[--with-awesome=true --sweet]
- install_gem a, :build_args => build_args
+ install_gem a, build_args: build_args
@cmd.options[:args] = %w[a]
@@ -392,6 +392,9 @@ class TestGemCommandsPristineCommand < Gem::TestCase
b = util_spec "b"
install_gem b
+ assert_path_exist File.join(gemhome2, "gems", "b-2")
+ assert_path_not_exist File.join(@gemhome, "gems", "b-2")
+
@cmd.options[:args] = %w[a b]
use_ui @ui do
diff --git a/test/rubygems/test_gem_commands_push_command.rb b/test/rubygems/test_gem_commands_push_command.rb
index baaf8e85c2..a7a18ff4ab 100644
--- a/test/rubygems/test_gem_commands_push_command.rb
+++ b/test/rubygems/test_gem_commands_push_command.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require_relative "helper"
+require_relative "multifactor_auth_utilities"
require "rubygems/commands/push_command"
require "rubygems/config_file"
@@ -26,7 +27,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
@host = "https://rubygems.example"
@api_key = Gem.configuration.rubygems_api_key
- @fetcher = Gem::FakeFetcher.new
+ @fetcher = Gem::MultifactorAuthFetcher.new
Gem::RemoteFetcher.fetcher = @fetcher
@cmd = Gem::Commands::PushCommand.new
@@ -59,7 +60,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
assert_match(/Pushing gem to #{@host}.../, @ui.output)
- assert_equal Net::HTTP::Post, @fetcher.last_request.class
+ assert_equal Gem::Net::HTTP::Post, @fetcher.last_request.class
assert_equal Gem.read_binary(@path), @fetcher.last_request.body
assert_equal File.size(@path), @fetcher.last_request["Content-Length"].to_i
assert_equal "application/octet-stream", @fetcher.last_request["Content-Type"]
@@ -76,7 +77,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
@cmd.execute
- assert_equal Net::HTTP::Post, @fetcher.last_request.class
+ assert_equal Gem::Net::HTTP::Post, @fetcher.last_request.class
assert_equal Gem.read_binary(@path), @fetcher.last_request.body
assert_equal "application/octet-stream",
@fetcher.last_request["Content-Type"]
@@ -95,7 +96,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
@cmd.execute
- assert_equal Net::HTTP::Post, @fetcher.last_request.class
+ assert_equal Gem::Net::HTTP::Post, @fetcher.last_request.class
assert_equal Gem.read_binary(@path), @fetcher.last_request.body
assert_equal "application/octet-stream",
@fetcher.last_request["Content-Type"]
@@ -115,7 +116,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
@cmd.execute
- assert_equal Net::HTTP::Post, @fetcher.last_request.class
+ assert_equal Gem::Net::HTTP::Post, @fetcher.last_request.class
assert_equal Gem.read_binary(@path), @fetcher.last_request.body
assert_equal "application/octet-stream",
@fetcher.last_request["Content-Type"]
@@ -229,7 +230,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
@api_key = "DOESNTMATTER"
keys = {
- :rubygems_api_key => @api_key,
+ rubygems_api_key: @api_key,
}
File.open Gem.configuration.credentials_path, "w" do |f|
@@ -318,7 +319,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
assert_match(/Pushing gem to #{host}.../, @ui.output)
- assert_equal Net::HTTP::Post, @fetcher.last_request.class
+ assert_equal Gem::Net::HTTP::Post, @fetcher.last_request.class
assert_equal Gem.read_binary(@path), @fetcher.last_request.body
assert_equal File.size(@path), @fetcher.last_request["Content-Length"].to_i
assert_equal "application/octet-stream", @fetcher.last_request["Content-Type"]
@@ -386,15 +387,9 @@ class TestGemCommandsPushCommand < Gem::TestCase
end
def test_otp_verified_success
- response_fail = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry."
response_success = "Successfully registered gem: freewill (1.0.0)"
- @fetcher.data["#{Gem.host}/api/v1/gems"] = [
- HTTPResponseFactory.create(body: response_fail, code: 401, msg: "Unauthorized"),
- HTTPResponseFactory.create(body: response_success, code: 200, msg: "OK"),
- ]
- @fetcher.data["#{Gem.host}/api/v1/webauthn_verification"] =
- HTTPResponseFactory.create(body: "You don't have any security devices", code: 422, msg: "Unprocessable Entity")
+ @fetcher.respond_with_require_otp("#{Gem.host}/api/v1/gems", response_success)
@otp_ui = Gem::MockGemUi.new "111111\n"
use_ui @otp_ui do
@@ -427,70 +422,105 @@ class TestGemCommandsPushCommand < Gem::TestCase
end
def test_with_webauthn_enabled_success
- webauthn_verification_url = "rubygems.org/api/v1/webauthn_verification/odow34b93t6aPCdY"
- response_fail = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry."
response_success = "Successfully registered gem: freewill (1.0.0)"
- port = 5678
- server = TCPServer.new(port)
+ server = Gem::MockTCPServer.new
- @fetcher.data["#{Gem.host}/api/v1/gems"] = [
- HTTPResponseFactory.create(body: response_fail, code: 401, msg: "Unauthorized"),
- HTTPResponseFactory.create(body: response_success, code: 200, msg: "OK"),
- ]
- @fetcher.data["#{Gem.host}/api/v1/webauthn_verification"] = HTTPResponseFactory.create(body: webauthn_verification_url, code: 200, msg: "OK")
+ @fetcher.respond_with_require_otp("#{Gem.host}/api/v1/gems", response_success)
+ @fetcher.respond_with_webauthn_url
TCPServer.stub(:new, server) do
- Gem::WebauthnListener.stub(:wait_for_otp_code, "Uvh6T57tkWuUnWYo") do
+ Gem::GemcutterUtilities::WebauthnListener.stub(:listener_thread, Thread.new { Thread.current[:otp] = "Uvh6T57tkWuUnWYo" }) do
use_ui @ui do
@cmd.send_gem(@path)
end
end
- ensure
- server.close
end
- url_with_port = "#{webauthn_verification_url}?port=#{port}"
- assert_match "You have enabled multi-factor authentication. Please visit #{url_with_port} to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output
+ assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \
+ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
+ "you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output
assert_match "You are verified with a security device. You may close the browser window.", @ui.output
assert_equal "Uvh6T57tkWuUnWYo", @fetcher.last_request["OTP"]
assert_match response_success, @ui.output
end
def test_with_webauthn_enabled_failure
- webauthn_verification_url = "rubygems.org/api/v1/webauthn_verification/odow34b93t6aPCdY"
- response_fail = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry."
response_success = "Successfully registered gem: freewill (1.0.0)"
- port = 5678
- server = TCPServer.new(port)
- raise_error = ->(*_args) { raise Gem::WebauthnVerificationError, "Something went wrong" }
+ server = Gem::MockTCPServer.new
+ error = Gem::WebauthnVerificationError.new("Something went wrong")
- @fetcher.data["#{Gem.host}/api/v1/gems"] = [
- HTTPResponseFactory.create(body: response_fail, code: 401, msg: "Unauthorized"),
- HTTPResponseFactory.create(body: response_success, code: 200, msg: "OK"),
- ]
- @fetcher.data["#{Gem.host}/api/v1/webauthn_verification"] = HTTPResponseFactory.create(body: webauthn_verification_url, code: 200, msg: "OK")
+ @fetcher.respond_with_require_otp("#{Gem.host}/api/v1/gems", response_success)
+ @fetcher.respond_with_webauthn_url
error = assert_raise Gem::MockGemUi::TermError do
TCPServer.stub(:new, server) do
- Gem::WebauthnListener.stub(:wait_for_otp_code, raise_error) do
+ Gem::GemcutterUtilities::WebauthnListener.stub(:listener_thread, Thread.new { Thread.current[:error] = error }) do
use_ui @ui do
@cmd.send_gem(@path)
end
end
- ensure
- server.close
end
end
assert_equal 1, error.exit_code
assert_match @fetcher.last_request["Authorization"], Gem.configuration.rubygems_api_key
- url_with_port = "#{webauthn_verification_url}?port=#{port}"
- assert_match "You have enabled multi-factor authentication. Please visit #{url_with_port} to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output
+ assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \
+ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
+ "you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output
assert_match "ERROR: Security device verification failed: Something went wrong", @ui.error
refute_match "You are verified with a security device. You may close the browser window.", @ui.output
refute_match response_success, @ui.output
end
+ def test_with_webauthn_enabled_success_with_polling
+ response_success = "Successfully registered gem: freewill (1.0.0)"
+ server = Gem::MockTCPServer.new
+
+ @fetcher.respond_with_require_otp("#{Gem.host}/api/v1/gems", response_success)
+ @fetcher.respond_with_webauthn_url
+ @fetcher.respond_with_webauthn_polling("Uvh6T57tkWuUnWYo")
+
+ TCPServer.stub(:new, server) do
+ use_ui @ui do
+ @cmd.send_gem(@path)
+ end
+ end
+
+ assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \
+ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
+ "you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output
+ assert_match "You are verified with a security device. You may close the browser window.", @ui.output
+ assert_equal "Uvh6T57tkWuUnWYo", @fetcher.last_request["OTP"]
+ assert_match response_success, @ui.output
+ end
+
+ def test_with_webauthn_enabled_failure_with_polling
+ response_success = "Successfully registered gem: freewill (1.0.0)"
+ server = Gem::MockTCPServer.new
+
+ @fetcher.respond_with_require_otp("#{Gem.host}/api/v1/gems", response_success)
+ @fetcher.respond_with_webauthn_url
+ @fetcher.respond_with_webauthn_polling_failure
+
+ error = assert_raise Gem::MockGemUi::TermError do
+ TCPServer.stub(:new, server) do
+ use_ui @ui do
+ @cmd.send_gem(@path)
+ end
+ end
+ end
+ assert_equal 1, error.exit_code
+
+ assert_match @fetcher.last_request["Authorization"], Gem.configuration.rubygems_api_key
+ assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \
+ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin " \
+ "command with the `--otp [your_code]` option.", @ui.output
+ assert_match "ERROR: Security device verification failed: The token in the link you used has either expired " \
+ "or been used already.", @ui.error
+ refute_match "You are verified with a security device. You may close the browser window.", @ui.output
+ refute_match response_success, @ui.output
+ end
+
def test_sending_gem_unathorized_api_key_with_mfa_enabled
response_mfa_enabled = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry."
response_forbidden = "The API key doesn't have access"
@@ -517,7 +547,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
access_notice = "The existing key doesn't have access of push_rubygem on https://rubygems.example. Please sign in to update access."
assert_match mfa_notice, @ui.output
assert_match access_notice, @ui.output
- assert_match "Email:", @ui.output
+ assert_match "Username/email:", @ui.output
assert_match "Password:", @ui.output
assert_match "Added push_rubygem scope to the existing API key", @ui.output
assert_match response_success, @ui.output
@@ -558,7 +588,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
mfa_notice = "You have enabled multi-factor authentication. Please enter OTP code."
assert_match mfa_notice, @ui.output
assert_match "Enter your https://rubygems.example credentials.", @ui.output
- assert_match "Email:", @ui.output
+ assert_match "Username/email:", @ui.output
assert_match "Password:", @ui.output
assert_match "Signed in with API key:", @ui.output
assert_match response_success, @ui.output
diff --git a/test/rubygems/test_gem_commands_rebuild_command.rb b/test/rubygems/test_gem_commands_rebuild_command.rb
new file mode 100644
index 0000000000..5e8c797e2d
--- /dev/null
+++ b/test/rubygems/test_gem_commands_rebuild_command.rb
@@ -0,0 +1,145 @@
+# frozen_string_literal: true
+
+require_relative "helper"
+require "rubygems/commands/build_command"
+require "rubygems/commands/rebuild_command"
+require "rubygems/package"
+
+class TestGemCommandsRebuildCommand < Gem::TestCase
+ def setup
+ super
+
+ readme_file = File.join(@tempdir, "README.md")
+
+ begin
+ umask_orig = File.umask(2)
+ File.open readme_file, "w" do |f|
+ f.write "My awesome gem"
+ end
+ ensure
+ File.umask(umask_orig)
+ end
+
+ @gem_name = "rebuild_test_gem"
+ @gem_version = "1.0.0"
+ @gem = util_spec @gem_name do |s|
+ s.version = @gem_version
+ s.license = "AGPL-3.0"
+ s.files = ["README.md"]
+ end
+ end
+
+ def util_test_build_gem(gem, args)
+ @ui = Gem::MockGemUi.new
+
+ cmd = Gem::Commands::BuildCommand.new
+
+ cmd.options[:args] = args
+ cmd.options[:build_path] = @tempdir
+ use_ui @ui do
+ cmd.execute
+ end
+ gem_file = "#{@gem_name}-#{@gem_version}.gem"
+ output = @ui.output.split "\n"
+ assert_equal " Successfully built RubyGem", output.shift
+ assert_equal " Name: #{@gem_name}", output.shift
+ assert_equal " Version: #{@gem_version}", output.shift
+ assert_equal " File: #{gem_file}", output.shift
+ assert_equal [], output
+
+ gem_file = File.join(@tempdir, gem_file)
+ assert File.exist?(gem_file)
+
+ spec = Gem::Package.new(gem_file).spec
+
+ assert_equal @gem_name, spec.name
+ assert_equal "this is a summary", spec.summary
+ gem_file
+ end
+
+ def util_test_rebuild_gem(gem, args, original_gem_file, gemspec_file, timestamp)
+ @ui = Gem::MockGemUi.new
+
+ cmd = Gem::Commands::RebuildCommand.new
+
+ cmd.options[:args] = args
+ cmd.options[:original_gem_file] = original_gem_file
+ cmd.options[:build_path] = @tempdir
+ cmd.options[:gemspec_file] = gemspec_file
+ use_ui @ui do
+ cmd.execute
+ end
+ gem_file = "#{@gem_name}-#{@gem_version}.gem"
+ output = @ui.output.split "\n"
+
+ assert_equal " Successfully built RubyGem", output.shift
+ assert_equal " Name: #{@gem_name}", output.shift
+ assert_equal " Version: #{@gem_version}", output.shift
+ assert_equal " File: #{gem_file}", output.shift
+ assert_empty output.shift
+ assert_match(/^Built at: .+ \(#{timestamp}\)/, output.shift)
+ original_line = output.shift
+ original = original_line.split(" ")[-1]
+ assert_match(/^Original build saved to: /, original_line)
+ reproduced_line = output.shift
+ reproduced = reproduced_line.split(" ")[-1]
+ assert_match(/^Reproduced build saved to: /, reproduced_line)
+ assert_equal "Working directory: #{@tempdir}", output.shift
+ assert_equal "", output.shift
+ assert_equal "Hash comparison:", output.shift
+ output.shift # " #{old_hash}\t#{old_file}"
+ output.shift # " #{new_hash}\t#{new_file}"
+ assert_empty output.shift
+ assert_equal "SUCCESS - original and rebuild hashes matched", output.shift
+ assert_equal [], output
+
+ assert File.exist?(original)
+ assert File.exist?(reproduced)
+
+ old_spec = Gem::Package.new(original).spec
+ new_spec = Gem::Package.new(reproduced).spec
+
+ assert_equal @gem_name, old_spec.name
+ assert_equal "this is a summary", old_spec.summary
+
+ assert_equal old_spec.name, new_spec.name
+ assert_equal old_spec.summary, new_spec.summary
+
+ reproduced
+ end
+
+ def test_build_is_reproducible
+ # Back up SOURCE_DATE_EPOCH to restore later.
+ epoch = ENV["SOURCE_DATE_EPOCH"]
+
+ gemspec_file = File.join(@tempdir, @gem.spec_name)
+
+ # Initial Build
+
+ # Set SOURCE_DATE_EPOCH to 2001-02-03 04:05:06 -0500.
+ ENV["SOURCE_DATE_EPOCH"] = timestamp = Time.new(2001, 2, 3, 4, 5, 6).to_i.to_s
+ File.write(gemspec_file, @gem.to_ruby)
+ gem_file = util_test_build_gem @gem, [gemspec_file]
+
+ build_contents = File.read(gem_file)
+
+ gem_file_dir = File.dirname(gem_file)
+ gem_file_name = File.basename(gem_file)
+ original_gem_file = File.join(gem_file_dir, "original-" + gem_file_name)
+ File.rename(gem_file, original_gem_file)
+
+ # Rebuild
+
+ # Set SOURCE_DATE_EPOCH to a different value, meaning we are
+ # also testing that `gem rebuild` overrides the value.
+ ENV["SOURCE_DATE_EPOCH"] = Time.new(2007, 8, 9, 10, 11, 12).to_s
+
+ rebuild_gem_file = util_test_rebuild_gem(@gem, [@gem_name, @gem_version], original_gem_file, gemspec_file, timestamp)
+
+ rebuild_contents = File.read(rebuild_gem_file)
+
+ assert_equal build_contents, rebuild_contents
+ ensure
+ ENV["SOURCE_DATE_EPOCH"] = epoch
+ end
+end
diff --git a/test/rubygems/test_gem_commands_setup_command.rb b/test/rubygems/test_gem_commands_setup_command.rb
index 6859d7a5cb..43f695f147 100644
--- a/test/rubygems/test_gem_commands_setup_command.rb
+++ b/test/rubygems/test_gem_commands_setup_command.rb
@@ -31,7 +31,6 @@ class TestGemCommandsSetupCommand < Gem::TestCase
bundler/lib/bundler/man/gemfile.5
bundler/lib/bundler/man/gemfile.5.ronn
bundler/lib/bundler/templates/.circleci/config.yml
- bundler/lib/bundler/templates/.travis.yml
]
create_dummy_files(filelist)
@@ -178,7 +177,6 @@ class TestGemCommandsSetupCommand < Gem::TestCase
assert_path_exist File.join(dir, "bundler/b.rb")
assert_path_exist File.join(dir, "bundler/templates/.circleci/config.yml")
- assert_path_exist File.join(dir, "bundler/templates/.travis.yml")
end
end
diff --git a/test/rubygems/test_gem_commands_signin_command.rb b/test/rubygems/test_gem_commands_signin_command.rb
index fd4ffb414a..29e5edceb7 100644
--- a/test/rubygems/test_gem_commands_signin_command.rb
+++ b/test/rubygems/test_gem_commands_signin_command.rb
@@ -85,7 +85,7 @@ class TestGemCommandsSigninCommand < Gem::TestCase
headers: { "location" => redirected_uri }
)
Gem::RemoteFetcher.fetcher = fetcher
- ui = Gem::MockGemUi.new("you@example.com\nsecret\n\n\n\n\n\n\n\n\n")
+ ui = Gem::MockGemUi.new("you@example.com\nsecret\n\n\n")
assert_raise Gem::MockGemUi::TermError do
use_ui ui do
@@ -106,51 +106,98 @@ class TestGemCommandsSigninCommand < Gem::TestCase
assert_equal api_key, credentials[:rubygems_api_key]
end
- def test_execute_with_key_name_and_scope
+ def test_execute_with_key_name_default_scope
email = "you@example.com"
password = "secret"
api_key = "1234abcd"
fetcher = Gem::RemoteFetcher.fetcher
- key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\n\ny\n\n\n\n\n\n"
+ key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\n\n"
util_capture(key_name_ui, nil, api_key, fetcher) { @cmd.execute }
user = ENV["USER"] || ENV["USERNAME"]
assert_match "API Key name [#{Socket.gethostname}-#{user}", key_name_ui.output
+ assert_match "The default access scope is:", key_name_ui.output
+ assert_match "index_rubygems: y", key_name_ui.output
+ assert_match "Do you want to customise scopes? [yN]", key_name_ui.output
+ assert_equal "name=test-key&index_rubygems=true", fetcher.last_request.body
+
+ credentials = load_yaml_file Gem.configuration.credentials_path
+ assert_equal api_key, credentials[:rubygems_api_key]
+ end
+
+ def test_execute_with_key_name_and_custom_scope
+ email = "you@example.com"
+ password = "secret"
+ api_key = "1234abcd"
+ fetcher = Gem::RemoteFetcher.fetcher
+
+ key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\ny\n\n\ny\n\n\n\n\n\n\n"
+ util_capture(key_name_ui, nil, api_key, fetcher) { @cmd.execute }
+
+ user = ENV["USER"] || ENV["USERNAME"]
+
+ assert_match "API Key name [#{Socket.gethostname}-#{user}", key_name_ui.output
+ assert_match "The default access scope is:", key_name_ui.output
+ assert_match "Do you want to customise scopes? [yN]", key_name_ui.output
+ assert_match "show_dashboard (exclusive scope, answering yes will not prompt for other scopes) [yN]", key_name_ui.output
assert_match "index_rubygems [yN]", key_name_ui.output
assert_match "push_rubygem [yN]", key_name_ui.output
assert_match "yank_rubygem [yN]", key_name_ui.output
assert_match "add_owner [yN]", key_name_ui.output
assert_match "remove_owner [yN]", key_name_ui.output
assert_match "access_webhooks [yN]", key_name_ui.output
- assert_match "show_dashboard [yN]", key_name_ui.output
assert_equal "name=test-key&push_rubygem=true", fetcher.last_request.body
credentials = load_yaml_file Gem.configuration.credentials_path
assert_equal api_key, credentials[:rubygems_api_key]
end
- def test_execute_with_key_name_scope_and_mfa_level_of_ui_only
+ def test_execute_with_key_name_and_exclusive_scope
+ email = "you@example.com"
+ password = "secret"
+ api_key = "1234abcd"
+ fetcher = Gem::RemoteFetcher.fetcher
+
+ key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\ny\ny\n"
+ util_capture(key_name_ui, nil, api_key, fetcher) { @cmd.execute }
+
+ user = ENV["USER"] || ENV["USERNAME"]
+
+ assert_match "API Key name [#{Socket.gethostname}-#{user}", key_name_ui.output
+ assert_match "The default access scope is:", key_name_ui.output
+ assert_match "index_rubygems: y", key_name_ui.output
+ assert_match "Do you want to customise scopes? [yN]", key_name_ui.output
+ assert_match "show_dashboard (exclusive scope, answering yes will not prompt for other scopes) [yN]", key_name_ui.output
+ assert_equal "name=test-key&show_dashboard=true", fetcher.last_request.body
+
+ credentials = load_yaml_file Gem.configuration.credentials_path
+ assert_equal api_key, credentials[:rubygems_api_key]
+ end
+
+ def test_execute_with_key_name_custom_scope_and_mfa_level_of_ui_only
email = "you@example.com"
password = "secret"
api_key = "1234abcd"
fetcher = Gem::RemoteFetcher.fetcher
mfa_level = "ui_only"
- key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\n\ny\n\n\n\n\n\ny"
+ key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\ny\n\n\ny\n\n\n\n\n\n\ny"
util_capture(key_name_ui, nil, api_key, fetcher, mfa_level) { @cmd.execute }
user = ENV["USER"] || ENV["USERNAME"]
assert_match "API Key name [#{Socket.gethostname}-#{user}", key_name_ui.output
+ assert_match "The default access scope is:", key_name_ui.output
+ assert_match "Do you want to customise scopes? [yN]", key_name_ui.output
+ assert_match "show_dashboard (exclusive scope, answering yes will not prompt for other scopes) [yN]", key_name_ui.output
assert_match "index_rubygems [yN]", key_name_ui.output
assert_match "push_rubygem [yN]", key_name_ui.output
assert_match "yank_rubygem [yN]", key_name_ui.output
assert_match "add_owner [yN]", key_name_ui.output
assert_match "remove_owner [yN]", key_name_ui.output
assert_match "access_webhooks [yN]", key_name_ui.output
- assert_match "show_dashboard [yN]", key_name_ui.output
assert_match "Would you like to enable MFA for this key? (strongly recommended) [yn]", key_name_ui.output
assert_equal "name=test-key&push_rubygem=true&mfa=true", fetcher.last_request.body
@@ -158,26 +205,28 @@ class TestGemCommandsSigninCommand < Gem::TestCase
assert_equal api_key, credentials[:rubygems_api_key]
end
- def test_execute_with_key_name_scope_and_mfa_level_of_gem_signin
+ def test_execute_with_key_name_custom_scope_and_mfa_level_of_gem_signin
email = "you@example.com"
password = "secret"
api_key = "1234abcd"
fetcher = Gem::RemoteFetcher.fetcher
mfa_level = "ui_and_gem_signin"
- key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\n\ny\n\n\n\n\n\ny"
+ key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\ny\n\n\ny\n\n\n\n\n\n\ny"
util_capture(key_name_ui, nil, api_key, fetcher, mfa_level) { @cmd.execute }
user = ENV["USER"] || ENV["USERNAME"]
assert_match "API Key name [#{Socket.gethostname}-#{user}", key_name_ui.output
+ assert_match "The default access scope is:", key_name_ui.output
+ assert_match "Do you want to customise scopes? [yN]", key_name_ui.output
+ assert_match "show_dashboard (exclusive scope, answering yes will not prompt for other scopes) [yN]", key_name_ui.output
assert_match "index_rubygems [yN]", key_name_ui.output
assert_match "push_rubygem [yN]", key_name_ui.output
assert_match "yank_rubygem [yN]", key_name_ui.output
assert_match "add_owner [yN]", key_name_ui.output
assert_match "remove_owner [yN]", key_name_ui.output
assert_match "access_webhooks [yN]", key_name_ui.output
- assert_match "show_dashboard [yN]", key_name_ui.output
assert_match "Would you like to enable MFA for this key? (strongly recommended) [yn]", key_name_ui.output
assert_equal "name=test-key&push_rubygem=true&mfa=true", fetcher.last_request.body
@@ -207,7 +256,7 @@ class TestGemCommandsSigninCommand < Gem::TestCase
api_key = "1234abcd"
fetcher = Gem::RemoteFetcher.fetcher
- key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\n\ny\n\n\n\n\n\ny"
+ key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\ny\n\n\ny\n\n\n\n\n\n\ny"
# Set the expected response for the Web-API supplied
ENV["RUBYGEMS_HOST"] = host
@@ -221,13 +270,13 @@ class TestGemCommandsSigninCommand < Gem::TestCase
user = ENV["USER"] || ENV["USERNAME"]
assert_match "API Key name [#{Socket.gethostname}-#{user}", key_name_ui.output
+ assert_match "show_dashboard (exclusive scope, answering yes will not prompt for other scopes) [yN]", key_name_ui.output
assert_match "index_rubygems [yN]", key_name_ui.output
assert_match "push_rubygem [yN]", key_name_ui.output
assert_match "yank_rubygem [yN]", key_name_ui.output
assert_match "add_owner [yN]", key_name_ui.output
assert_match "remove_owner [yN]", key_name_ui.output
assert_match "access_webhooks [yN]", key_name_ui.output
- assert_match "show_dashboard [yN]", key_name_ui.output
assert_equal "name=test-key&push_rubygem=true", fetcher.last_request.body
end
@@ -248,7 +297,7 @@ class TestGemCommandsSigninCommand < Gem::TestCase
fetcher.data[profile] = profile_response
Gem::RemoteFetcher.fetcher = fetcher
- sign_in_ui = ui_stub || Gem::MockGemUi.new("#{email}\n#{password}\n\n\n\n\n\n\n\n\n")
+ sign_in_ui = ui_stub || Gem::MockGemUi.new("#{email}\n#{password}\n\n\n")
use_ui sign_in_ui do
yield
diff --git a/test/rubygems/test_gem_commands_stale_command.rb b/test/rubygems/test_gem_commands_stale_command.rb
index 1c224d0785..ea7493b418 100644
--- a/test/rubygems/test_gem_commands_stale_command.rb
+++ b/test/rubygems/test_gem_commands_stale_command.rb
@@ -25,11 +25,11 @@ class TestGemCommandsStaleCommand < Gem::TestCase
files.each do |file|
filename = File.join(bar_baz.full_gem_path, file)
FileUtils.mkdir_p File.dirname filename
- FileUtils.touch(filename, :mtime => Time.now)
+ FileUtils.touch(filename, mtime: Time.now)
filename = File.join(foo_bar.full_gem_path, file)
FileUtils.mkdir_p File.dirname filename
- FileUtils.touch(filename, :mtime => Time.now - 86_400)
+ FileUtils.touch(filename, mtime: Time.now - 86_400)
end
use_ui @stub_ui do
diff --git a/test/rubygems/test_gem_commands_uninstall_command.rb b/test/rubygems/test_gem_commands_uninstall_command.rb
index 48c5cadaed..4daa61cb0c 100644
--- a/test/rubygems/test_gem_commands_uninstall_command.rb
+++ b/test/rubygems/test_gem_commands_uninstall_command.rb
@@ -21,7 +21,7 @@ class TestGemCommandsUninstallCommand < Gem::InstallerTestCase
gemhome2 = "#{@gemhome}2"
a_4, = util_gem "a", 4
- install_gem a_4, :install_dir => gemhome2
+ install_gem a_4, install_dir: gemhome2
assert_gems_presence "a-1", "a-4", "b-2", "default-1", dirs: [@gemhome, gemhome2]
@@ -229,6 +229,26 @@ class TestGemCommandsUninstallCommand < Gem::InstallerTestCase
assert File.exist? File.join(@gemhome, "bin", "executable")
end
+ def test_execute_with_multiple_version_specified_as_colon
+ initial_install
+
+ ui = Gem::MockGemUi.new "y\n"
+
+ util_make_gems
+
+ assert_equal 3, Gem::Specification.find_all_by_name("a").length
+
+ @cmd.options[:force] = true
+ @cmd.options[:args] = ["a:1", "a:2"]
+
+ use_ui ui do
+ @cmd.execute
+ end
+
+ assert_equal 1, Gem::Specification.find_all_by_name("a").length
+ assert_equal Gem::Version.new("3.a"), Gem::Specification.find_by_name("a").version
+ end
+
def test_uninstall_selection
ui = Gem::MockGemUi.new "1\n"
@@ -361,7 +381,7 @@ class TestGemCommandsUninstallCommand < Gem::InstallerTestCase
gemhome2 = "#{@gemhome}2"
a_4, = util_gem "a", 4
- install_gem a_4, :install_dir => gemhome2
+ install_gem a_4, install_dir: gemhome2
assert_gems_presence "a-4", dirs: [@gemhome, gemhome2]
@@ -380,7 +400,7 @@ class TestGemCommandsUninstallCommand < Gem::InstallerTestCase
gemhome2 = "#{@gemhome}2"
a_4, = util_gem "a", 4
- install_gem a_4, :install_dir => gemhome2
+ install_gem a_4, install_dir: gemhome2
assert_gems_presence "a-4", dirs: [@gemhome, gemhome2]
diff --git a/test/rubygems/test_gem_commands_update_command.rb b/test/rubygems/test_gem_commands_update_command.rb
index 02dd57f660..2683840f2e 100644
--- a/test/rubygems/test_gem_commands_update_command.rb
+++ b/test/rubygems/test_gem_commands_update_command.rb
@@ -79,7 +79,6 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
end
out = @ui.output.split "\n"
- assert_equal "Updating rubygems-update", out.shift
assert_equal "Installing RubyGems 9", out.shift
assert_equal "RubyGems system software updated", out.shift
@@ -123,7 +122,6 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
end
out = @ui.output.split "\n"
- assert_equal "Updating rubygems-update", out.shift
assert_empty out
err = @ui.error.split "\n"
@@ -132,6 +130,34 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
assert_empty err
end
+ def test_execute_system_when_latest_does_not_support_your_ruby_but_previous_one_does
+ spec_fetcher do |fetcher|
+ fetcher.download "rubygems-update", 9 do |s|
+ s.files = %w[setup.rb]
+ s.required_ruby_version = "> 9"
+ end
+
+ fetcher.download "rubygems-update", 8 do |s|
+ s.files = %w[setup.rb]
+ end
+ end
+
+ @cmd.options[:args] = []
+ @cmd.options[:system] = true
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ err = @ui.error.split "\n"
+ assert_empty err
+
+ out = @ui.output.split "\n"
+ assert_equal "Installing RubyGems 8", out.shift
+ assert_equal "RubyGems system software updated", out.shift
+ assert_empty out
+ end
+
def test_execute_system_multiple
spec_fetcher do |fetcher|
fetcher.download "rubygems-update", 8 do |s|
@@ -151,7 +177,6 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
end
out = @ui.output.split "\n"
- assert_equal "Updating rubygems-update", out.shift
assert_equal "Installing RubyGems 9", out.shift
assert_equal "RubyGems system software updated", out.shift
@@ -185,7 +210,6 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
end
out = @ui.output.split "\n"
- assert_equal "Updating rubygems-update", out.shift
assert_equal "Installing RubyGems 9", out.shift
assert_equal "RubyGems system software updated", out.shift
@@ -205,7 +229,7 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
gemhome2 = "#{@gemhome}2"
- Gem::Installer.at(rubygems_update_package, :install_dir => gemhome2).install
+ Gem::Installer.at(rubygems_update_package, install_dir: gemhome2).install
Gem.use_paths @gemhome, [gemhome2, @gemhome]
@@ -242,7 +266,6 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
end
out = @ui.output.split "\n"
- assert_equal "Updating rubygems-update", out.shift
assert_equal "Installing RubyGems 8", out.shift
assert_equal "RubyGems system software updated", out.shift
@@ -353,7 +376,6 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
end
out = @ui.output.split "\n"
- assert_equal "Updating rubygems-update", out.shift
assert_equal "Installing RubyGems 9", out.shift
assert_equal "RubyGems system software updated", out.shift
@@ -670,10 +692,10 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
@cmd.handle_options %w[--system]
expected = {
- :args => [],
- :document => %w[ri],
- :force => false,
- :system => true,
+ args: [],
+ document: %w[ri],
+ force: false,
+ system: true,
}
assert_equal expected, @cmd.options
@@ -689,10 +711,10 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
@cmd.handle_options %w[--system 1.3.7]
expected = {
- :args => [],
- :document => %w[ri],
- :force => false,
- :system => "1.3.7",
+ args: [],
+ document: %w[ri],
+ force: false,
+ system: "1.3.7",
}
assert_equal expected, @cmd.options
diff --git a/test/rubygems/test_gem_commands_yank_command.rb b/test/rubygems/test_gem_commands_yank_command.rb
index e5e234e0f8..eb78e3a542 100644
--- a/test/rubygems/test_gem_commands_yank_command.rb
+++ b/test/rubygems/test_gem_commands_yank_command.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require_relative "helper"
+require_relative "multifactor_auth_utilities"
require "rubygems/commands/yank_command"
class TestGemCommandsYankCommand < Gem::TestCase
@@ -12,7 +13,8 @@ class TestGemCommandsYankCommand < Gem::TestCase
@cmd = Gem::Commands::YankCommand.new
@cmd.options[:host] = "http://example"
- @fetcher = Gem::RemoteFetcher.fetcher
+ @fetcher = Gem::MultifactorAuthFetcher.new(host: "http://example")
+ Gem::RemoteFetcher.fetcher = @fetcher
Gem.configuration.rubygems_api_key = "key"
Gem.configuration.api_keys[:KEY] = "other"
@@ -73,9 +75,6 @@ class TestGemCommandsYankCommand < Gem::TestCase
HTTPResponseFactory.create(body: response_fail, code: 401, msg: "Unauthorized"),
HTTPResponseFactory.create(body: "Successfully yanked", code: 200, msg: "OK"),
]
- webauthn_uri = "http://example/api/v1/webauthn_verification"
- @fetcher.data[webauthn_uri] =
- HTTPResponseFactory.create(body: "You don't have any security devices", code: 422, msg: "Unprocessable Entity")
@cmd.options[:args] = %w[a]
@cmd.options[:added_platform] = true
@@ -97,9 +96,6 @@ class TestGemCommandsYankCommand < Gem::TestCase
response = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry."
yank_uri = "http://example/api/v1/gems/yank"
@fetcher.data[yank_uri] = HTTPResponseFactory.create(body: response, code: 401, msg: "Unauthorized")
- webauthn_uri = "http://example/api/v1/webauthn_verification"
- @fetcher.data[webauthn_uri] =
- HTTPResponseFactory.create(body: "You don't have any security devices", code: 422, msg: "Unprocessable Entity")
@cmd.options[:args] = %w[a]
@cmd.options[:added_platform] = true
@@ -117,55 +113,38 @@ class TestGemCommandsYankCommand < Gem::TestCase
end
def test_with_webauthn_enabled_success
- webauthn_verification_url = "http://example/api/v1/webauthn_verification/odow34b93t6aPCdY"
- response_fail = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry."
- yank_uri = "http://example/api/v1/gems/yank"
- webauthn_uri = "http://example/api/v1/webauthn_verification"
- port = 5678
- server = TCPServer.new(port)
+ server = Gem::MockTCPServer.new
- @fetcher.data[webauthn_uri] = HTTPResponseFactory.create(body: webauthn_verification_url, code: 200, msg: "OK")
- @fetcher.data[yank_uri] = [
- HTTPResponseFactory.create(body: response_fail, code: 401, msg: "Unauthorized"),
- HTTPResponseFactory.create(body: "Successfully yanked", code: 200, msg: "OK"),
- ]
+ @fetcher.respond_with_require_otp("http://example/api/v1/gems/yank", "Successfully yanked")
+ @fetcher.respond_with_webauthn_url
@cmd.options[:args] = %w[a]
@cmd.options[:added_platform] = true
@cmd.options[:version] = req("= 1.0")
TCPServer.stub(:new, server) do
- Gem::WebauthnListener.stub(:wait_for_otp_code, "Uvh6T57tkWuUnWYo") do
+ Gem::GemcutterUtilities::WebauthnListener.stub(:listener_thread, Thread.new { Thread.current[:otp] = "Uvh6T57tkWuUnWYo" }) do
use_ui @ui do
@cmd.execute
end
end
- ensure
- server.close
end
- url_with_port = "#{webauthn_verification_url}?port=#{port}"
assert_match %r{Yanking gem from http://example}, @ui.output
- assert_match "You have enabled multi-factor authentication. Please visit #{url_with_port} to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output
+ assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \
+ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
+ "you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output
assert_match "You are verified with a security device. You may close the browser window.", @ui.output
assert_equal "Uvh6T57tkWuUnWYo", @fetcher.last_request["OTP"]
assert_match "Successfully yanked", @ui.output
end
def test_with_webauthn_enabled_failure
- webauthn_verification_url = "http://example/api/v1/webauthn_verification/odow34b93t6aPCdY"
- response_fail = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry."
- yank_uri = "http://example/api/v1/gems/yank"
- webauthn_uri = "http://example/api/v1/webauthn_verification"
- port = 5678
- server = TCPServer.new(port)
- raise_error = ->(*_args) { raise Gem::WebauthnVerificationError, "Something went wrong" }
+ server = Gem::MockTCPServer.new
+ error = Gem::WebauthnVerificationError.new("Something went wrong")
- @fetcher.data[webauthn_uri] = HTTPResponseFactory.create(body: webauthn_verification_url, code: 200, msg: "OK")
- @fetcher.data[yank_uri] = [
- HTTPResponseFactory.create(body: response_fail, code: 401, msg: "Unauthorized"),
- HTTPResponseFactory.create(body: "Successfully yanked", code: 200, msg: "OK"),
- ]
+ @fetcher.respond_with_require_otp("http://example/api/v1/gems/yank", "Successfully yanked")
+ @fetcher.respond_with_webauthn_url
@cmd.options[:args] = %w[a]
@cmd.options[:added_platform] = true
@@ -173,27 +152,82 @@ class TestGemCommandsYankCommand < Gem::TestCase
error = assert_raise Gem::MockGemUi::TermError do
TCPServer.stub(:new, server) do
- Gem::WebauthnListener.stub(:wait_for_otp_code, raise_error) do
+ Gem::GemcutterUtilities::WebauthnListener.stub(:listener_thread, Thread.new { Thread.current[:error] = error }) do
use_ui @ui do
@cmd.execute
end
end
- ensure
- server.close
end
end
assert_equal 1, error.exit_code
- url_with_port = "#{webauthn_verification_url}?port=#{port}"
-
assert_match @fetcher.last_request["Authorization"], Gem.configuration.rubygems_api_key
assert_match %r{Yanking gem from http://example}, @ui.output
- assert_match "You have enabled multi-factor authentication. Please visit #{url_with_port} to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output
+ assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \
+ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
+ "you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output
assert_match "ERROR: Security device verification failed: Something went wrong", @ui.error
refute_match "You are verified with a security device. You may close the browser window.", @ui.output
refute_match "Successfully yanked", @ui.output
end
+ def test_with_webauthn_enabled_success_with_polling
+ server = Gem::MockTCPServer.new
+
+ @fetcher.respond_with_require_otp("http://example/api/v1/gems/yank", "Successfully yanked")
+ @fetcher.respond_with_webauthn_url
+ @fetcher.respond_with_webauthn_polling("Uvh6T57tkWuUnWYo")
+
+ @cmd.options[:args] = %w[a]
+ @cmd.options[:added_platform] = true
+ @cmd.options[:version] = req("= 1.0")
+
+ TCPServer.stub(:new, server) do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_match %r{Yanking gem from http://example}, @ui.output
+ assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \
+ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
+ "you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output
+ assert_match "You are verified with a security device. You may close the browser window.", @ui.output
+ assert_equal "Uvh6T57tkWuUnWYo", @fetcher.last_request["OTP"]
+ assert_match "Successfully yanked", @ui.output
+ end
+
+ def test_with_webauthn_enabled_failure_with_polling
+ server = Gem::MockTCPServer.new
+
+ @fetcher.respond_with_require_otp("http://example/api/v1/gems/yank", "Successfully yanked")
+ @fetcher.respond_with_webauthn_url
+ @fetcher.respond_with_webauthn_polling_failure
+
+ @cmd.options[:args] = %w[a]
+ @cmd.options[:added_platform] = true
+ @cmd.options[:version] = req("= 1.0")
+
+ error = assert_raise Gem::MockGemUi::TermError do
+ TCPServer.stub(:new, server) do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+ end
+ assert_equal 1, error.exit_code
+
+ assert_match @fetcher.last_request["Authorization"], Gem.configuration.rubygems_api_key
+ assert_match %r{Yanking gem from http://example}, @ui.output
+ assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \
+ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
+ "you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output
+ assert_match "ERROR: Security device verification failed: The token in the link you used has either expired " \
+ "or been used already.", @ui.error
+ refute_match "You are verified with a security device. You may close the browser window.", @ui.output
+ refute_match "Successfully yanked", @ui.output
+ end
+
def test_execute_key
yank_uri = "http://example/api/v1/gems/yank"
@fetcher.data[yank_uri] = HTTPResponseFactory.create(body: "Successfully yanked", code: 200, msg: "OK")
@@ -257,7 +291,7 @@ class TestGemCommandsYankCommand < Gem::TestCase
access_notice = "The existing key doesn't have access of yank_rubygem on http://example. Please sign in to update access."
assert_match access_notice, @ui.output
- assert_match "Email:", @ui.output
+ assert_match "Username/email:", @ui.output
assert_match "Password:", @ui.output
assert_match "Added yank_rubygem scope to the existing API key", @ui.output
assert_match response_success, @ui.output
diff --git a/test/rubygems/test_gem_config_file.rb b/test/rubygems/test_gem_config_file.rb
index f53125cc0c..a055f248be 100644
--- a/test/rubygems/test_gem_config_file.rb
+++ b/test/rubygems/test_gem_config_file.rb
@@ -58,6 +58,7 @@ class TestGemConfigFile < Gem::TestCase
fp.puts ":ssl_verify_mode: 0"
fp.puts ":ssl_ca_cert: /etc/ssl/certs"
fp.puts ":cert_expiration_length_days: 28"
+ fp.puts ":install_extension_in_lib: true"
fp.puts ":ipv4_fallback_enabled: true"
end
@@ -73,6 +74,7 @@ class TestGemConfigFile < Gem::TestCase
assert_equal 0, @cfg.ssl_verify_mode
assert_equal "/etc/ssl/certs", @cfg.ssl_ca_cert
assert_equal 28, @cfg.cert_expiration_length_days
+ assert_equal true, @cfg.install_extension_in_lib
assert_equal true, @cfg.ipv4_fallback_enabled
end
@@ -191,7 +193,7 @@ class TestGemConfigFile < Gem::TestCase
util_config_file
- assert_equal({ :rubygems => "701229f217cdf23b1344c7b4b54ca97" },
+ assert_equal({ rubygems: "701229f217cdf23b1344c7b4b54ca97" },
@cfg.api_keys)
end
@@ -369,7 +371,7 @@ if you believe they were disclosed to a third party.
assert_equal "x", @cfg.rubygems_api_key
expected = {
- :rubygems_api_key => "x",
+ rubygems_api_key: "x",
}
assert_equal expected, load_yaml_file(@cfg.credentials_path)
@@ -393,7 +395,7 @@ if you believe they were disclosed to a third party.
end
expected = {
- :rubygems_api_key => "x",
+ rubygems_api_key: "x",
}
assert_equal expected, load_yaml_file(@cfg.credentials_path)
@@ -485,6 +487,16 @@ if you believe they were disclosed to a third party.
end
end
+ def test_accept_string_key
+ File.open @temp_conf, "w" do |fp|
+ fp.puts "verbose: false"
+ end
+
+ util_config_file
+
+ assert_equal false, @cfg.verbose
+ end
+
def test_load_ssl_verify_mode_from_config
File.open @temp_conf, "w" do |fp|
fp.puts ":ssl_verify_mode: 1"
@@ -509,8 +521,12 @@ if you believe they were disclosed to a third party.
assert_equal("/home/me/mine.pem", @cfg.ssl_client_cert)
end
- def util_config_file(args = @cfg_args)
- @cfg = Gem::ConfigFile.new args
+ def test_load_install_extension_in_lib_from_config
+ File.open @temp_conf, "w" do |fp|
+ fp.puts ":install_extension_in_lib: false"
+ end
+ util_config_file
+ assert_equal(false, @cfg.install_extension_in_lib)
end
def test_disable_default_gem_server
@@ -542,10 +558,24 @@ if you believe they were disclosed to a third party.
end
def test_dump_with_rubygems_yaml
- symbol_key_hash = { :foo => "bar" }
+ symbol_key_hash = { foo: "bar" }
actual = Gem::ConfigFile.dump_with_rubygems_yaml(symbol_key_hash)
assert_equal("---\n:foo: \"bar\"\n", actual)
end
+
+ def test_handle_comment
+ yaml = <<~YAML
+ ---
+ :foo: bar # buzz
+ YAML
+
+ actual = Gem::ConfigFile.load_with_rubygems_config_hash(yaml)
+ assert_equal("bar", actual[:foo])
+ end
+
+ def util_config_file(args = @cfg_args)
+ @cfg = Gem::ConfigFile.new args
+ end
end
diff --git a/test/rubygems/test_gem_console_ui.rb b/test/rubygems/test_gem_console_ui.rb
new file mode 100644
index 0000000000..b8a619625f
--- /dev/null
+++ b/test/rubygems/test_gem_console_ui.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require_relative "helper"
+require "rubygems/user_interaction"
+
+class TestGemConsoleUI < Gem::TestCase
+ def test_output_can_be_captured_by_test_unit
+ output = capture_output do
+ ui = Gem::ConsoleUI.new
+
+ ui.alert_error "test error"
+ ui.alert_warning "test warning"
+ ui.alert "test alert"
+ end
+
+ assert_equal "INFO: test alert\n", output.first
+ assert_equal "ERROR: test error\n" + "WARNING: test warning\n", output.last
+ end
+end
diff --git a/test/rubygems/test_gem_dependency.rb b/test/rubygems/test_gem_dependency.rb
index 6ac03fc0e2..2a989a5551 100644
--- a/test/rubygems/test_gem_dependency.rb
+++ b/test/rubygems/test_gem_dependency.rb
@@ -394,6 +394,16 @@ class TestGemDependency < Gem::TestCase
assert_match "Could not find 'b' (= 2.0) among 1 total gem(s)", e.message
end
+ def test_to_spec_with_only_prereleases
+ a_2_a_1 = util_spec "a", "2.a1"
+ a_2_a_2 = util_spec "a", "2.a2"
+ install_specs a_2_a_1, a_2_a_2
+
+ a_dep = dep "a", ">= 1"
+
+ assert_equal a_2_a_2, a_dep.to_spec
+ end
+
def test_identity
assert_equal dep("a", "= 1").identity, :released
assert_equal dep("a", "= 1.a").identity, :complete
diff --git a/test/rubygems/test_gem_dependency_installer.rb b/test/rubygems/test_gem_dependency_installer.rb
index f0044ea1fe..8999723ba1 100644
--- a/test/rubygems/test_gem_dependency_installer.rb
+++ b/test/rubygems/test_gem_dependency_installer.rb
@@ -75,7 +75,7 @@ class TestGemDependencyInstaller < Gem::TestCase
@fetcher.data["http://gems.example.com/gems/a-10.a.gem"] = p1a_data
dep = Gem::Dependency.new "a"
- inst = Gem::DependencyInstaller.new :prerelease => true
+ inst = Gem::DependencyInstaller.new prerelease: true
inst.install dep
assert_equal %w[a-10.a], Gem::Specification.map(&:full_name)
@@ -97,7 +97,7 @@ class TestGemDependencyInstaller < Gem::TestCase
dep = Gem::Dependency.new "a"
- inst = Gem::DependencyInstaller.new :prerelease => true
+ inst = Gem::DependencyInstaller.new prerelease: true
inst.install dep
assert_equal %w[a-1.b b-1.b c-1.1.b], Gem::Specification.map(&:full_name)
@@ -132,7 +132,7 @@ class TestGemDependencyInstaller < Gem::TestCase
@fetcher.data["http://gems.example.com/gems/a-1.gem"] = p1a_data
dep = Gem::Dependency.new "a"
- inst = Gem::DependencyInstaller.new :prerelease => true
+ inst = Gem::DependencyInstaller.new prerelease: true
inst.install dep
assert_equal %w[a-1], Gem::Specification.map(&:full_name)
@@ -153,7 +153,7 @@ class TestGemDependencyInstaller < Gem::TestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new :ignore_dependencies => true
+ inst = Gem::DependencyInstaller.new ignore_dependencies: true
inst.install "b"
end
@@ -178,7 +178,7 @@ class TestGemDependencyInstaller < Gem::TestCase
inst = nil
Dir.chdir dir do
- inst = Gem::DependencyInstaller.new :cache_dir => @tempdir
+ inst = Gem::DependencyInstaller.new cache_dir: @tempdir
inst.install "b"
end
@@ -198,7 +198,7 @@ class TestGemDependencyInstaller < Gem::TestCase
Gem::Specification.reset
FileUtils.mv @a1_gem, @tempdir
- FileUtils.mv a2_gem, @tempdir # not in index
+ FileUtils.mv a2_gem, @tempdir # not in index
FileUtils.mv @b1_gem, @tempdir
inst = nil
@@ -237,7 +237,7 @@ class TestGemDependencyInstaller < Gem::TestCase
Gem::Specification.reset
FileUtils.mv @a1_gem, @tempdir
- FileUtils.mv a2_gem, @tempdir # not in index
+ FileUtils.mv a2_gem, @tempdir # not in index
FileUtils.mv @b1_gem, @tempdir
FileUtils.mv a3_gem, @tempdir
@@ -274,7 +274,7 @@ class TestGemDependencyInstaller < Gem::TestCase
FileUtils.mv @b1_gem, @tempdir
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new(:build_docs_in_background => false)
+ inst = Gem::DependencyInstaller.new(build_docs_in_background: false)
inst.install "b"
end
@@ -294,7 +294,7 @@ class TestGemDependencyInstaller < Gem::TestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new(:development => true)
+ inst = Gem::DependencyInstaller.new(development: true)
inst.install "b"
end
@@ -314,7 +314,7 @@ class TestGemDependencyInstaller < Gem::TestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new(:development => true)
+ inst = Gem::DependencyInstaller.new(development: true)
inst.install "d"
end
@@ -334,7 +334,7 @@ class TestGemDependencyInstaller < Gem::TestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new(:development => true, :dev_shallow => true)
+ inst = Gem::DependencyInstaller.new(development: true, dev_shallow: true)
inst.install "d"
end
@@ -421,7 +421,7 @@ class TestGemDependencyInstaller < Gem::TestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new :domain => :local
+ inst = Gem::DependencyInstaller.new domain: :local
inst.install "a-1.gem"
end
@@ -435,7 +435,7 @@ class TestGemDependencyInstaller < Gem::TestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new :domain => :local
+ inst = Gem::DependencyInstaller.new domain: :local
inst.install "a-1.a.gem"
end
@@ -451,7 +451,7 @@ class TestGemDependencyInstaller < Gem::TestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new :domain => :local
+ inst = Gem::DependencyInstaller.new domain: :local
inst.install "b-1.gem"
end
@@ -469,20 +469,54 @@ class TestGemDependencyInstaller < Gem::TestCase
Dir.chdir @tempdir do
Gem::Installer.at("a-1.gem").install
- inst = Gem::DependencyInstaller.new :domain => :local
+ inst = Gem::DependencyInstaller.new domain: :local
inst.install "b-1.gem"
end
assert_equal %w[b-1], inst.installed_gems.map(&:full_name)
end
+ def test_install_local_dependency_no_network_for_target_gem
+ a1, a1_gem = util_gem "a", "1"
+ _, b1_gem = util_gem "b", "1" do |s|
+ s.add_dependency "a"
+ end
+
+ util_setup_spec_fetcher(a1)
+
+ a1_data = Gem.read_binary(a1_gem)
+ @fetcher.data["http://gems.example.com/gems/a-1.gem"] = a1_data
+
+ # compact index is available
+ compact_index_response = Gem::Net::HTTPResponse.new "1.1", 200, "OK"
+ compact_index_response.uri = Gem::URI("http://gems.example.com")
+ @fetcher.data["http://gems.example.com/"] = compact_index_response
+
+ # but private local gem not present there
+ @fetcher.data["http://gems.example.com/info/b"] =
+ proc do
+ raise "should not happen"
+ end
+
+ FileUtils.mv b1_gem, @tempdir
+
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new
+ inst.install "b-1.gem"
+ end
+
+ assert_equal %w[a-1 b-1], inst.installed_gems.map(&:full_name)
+ end
+
def test_install_local_subdir
util_setup_gems
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new :domain => :local
+ inst = Gem::DependencyInstaller.new domain: :local
inst.install "gems/a-1.gem"
end
@@ -508,7 +542,7 @@ class TestGemDependencyInstaller < Gem::TestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new :ignore_dependencies => true
+ inst = Gem::DependencyInstaller.new ignore_dependencies: true
inst.install "b", req("= 1")
end
@@ -516,7 +550,7 @@ class TestGemDependencyInstaller < Gem::TestCase
"sanity check"
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new :minimal_deps => true
+ inst = Gem::DependencyInstaller.new minimal_deps: true
inst.install "e"
end
@@ -542,7 +576,7 @@ class TestGemDependencyInstaller < Gem::TestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new :ignore_dependencies => true
+ inst = Gem::DependencyInstaller.new ignore_dependencies: true
inst.install "b", req("= 1")
end
@@ -550,7 +584,7 @@ class TestGemDependencyInstaller < Gem::TestCase
"sanity check"
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new :minimal_deps => false
+ inst = Gem::DependencyInstaller.new minimal_deps: false
inst.install "e"
end
@@ -567,7 +601,7 @@ class TestGemDependencyInstaller < Gem::TestCase
assert_empty dep_installer.document
end
- inst = Gem::DependencyInstaller.new :domain => :local, :document => []
+ inst = Gem::DependencyInstaller.new domain: :local, document: []
inst.install @a1_gem
@@ -581,13 +615,13 @@ class TestGemDependencyInstaller < Gem::TestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new :env_shebang => true, :wrappers => true, :format_executable => false
+ inst = Gem::DependencyInstaller.new env_shebang: true, wrappers: true, format_executable: false
inst.install "a"
end
env = "/\\S+/env" unless Gem.win_platform?
- assert_match(/\A#!#{env} #{RbConfig::CONFIG['ruby_install_name']}\n/,
+ assert_match(/\A#!#{env} #{RbConfig::CONFIG["ruby_install_name"]}\n/,
File.read(File.join(@gemhome, "bin", "a_bin")))
end
@@ -600,7 +634,7 @@ class TestGemDependencyInstaller < Gem::TestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new :force => true
+ inst = Gem::DependencyInstaller.new force: true
inst.install "b"
end
@@ -615,7 +649,7 @@ class TestGemDependencyInstaller < Gem::TestCase
build_args = %w[--a --b="c"]
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new(:build_args => build_args)
+ inst = Gem::DependencyInstaller.new(build_args: build_args)
inst.install "a"
end
@@ -629,7 +663,7 @@ class TestGemDependencyInstaller < Gem::TestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new :ignore_dependencies => true
+ inst = Gem::DependencyInstaller.new ignore_dependencies: true
inst.install "b"
end
@@ -650,7 +684,7 @@ class TestGemDependencyInstaller < Gem::TestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new :install_dir => gemhome2
+ inst = Gem::DependencyInstaller.new install_dir: gemhome2
inst.install "b"
end
@@ -674,7 +708,7 @@ class TestGemDependencyInstaller < Gem::TestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new :domain => :both
+ inst = Gem::DependencyInstaller.new domain: :both
inst.install "b"
end
@@ -698,7 +732,7 @@ class TestGemDependencyInstaller < Gem::TestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new :domain => :both
+ inst = Gem::DependencyInstaller.new domain: :both
inst.install "b"
end
@@ -713,7 +747,7 @@ class TestGemDependencyInstaller < Gem::TestCase
Dir.chdir @tempdir do
e = assert_raise Gem::UnsatisfiableDependencyError do
- inst = Gem::DependencyInstaller.new :domain => :local
+ inst = Gem::DependencyInstaller.new domain: :local
inst.install "b"
end
@@ -734,7 +768,7 @@ class TestGemDependencyInstaller < Gem::TestCase
@fetcher.data["http://gems.example.com/gems/a-1.gem"] = a1_data
- inst = Gem::DependencyInstaller.new :domain => :remote
+ inst = Gem::DependencyInstaller.new domain: :remote
inst.install "a"
assert_equal %w[a-1], inst.installed_gems.map(&:full_name)
@@ -750,7 +784,7 @@ class TestGemDependencyInstaller < Gem::TestCase
gemhome2 = "#{@gemhome}2"
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new :install_dir => gemhome2
+ inst = Gem::DependencyInstaller.new install_dir: gemhome2
inst.install "a"
end
@@ -777,7 +811,7 @@ class TestGemDependencyInstaller < Gem::TestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new :force => true
+ inst = Gem::DependencyInstaller.new force: true
inst.install "a"
end
@@ -851,7 +885,7 @@ class TestGemDependencyInstaller < Gem::TestCase
@fetcher.data["http://gems.example.com/gems/#{a2_o.file_name}"] =
a2_o_data
- inst = Gem::DependencyInstaller.new :domain => :remote
+ inst = Gem::DependencyInstaller.new domain: :remote
inst.install "a"
assert_equal %w[a-1], inst.installed_gems.map(&:full_name)
@@ -862,7 +896,7 @@ class TestGemDependencyInstaller < Gem::TestCase
s.platform = Gem::Platform.new %w[cpu other_platform 1]
end
- inst = Gem::DependencyInstaller.new :domain => :local
+ inst = Gem::DependencyInstaller.new domain: :local
inst.install a_gem
assert_equal %w[a-1-cpu-other_platform-1], inst.installed_gems.map(&:full_name)
@@ -881,7 +915,7 @@ class TestGemDependencyInstaller < Gem::TestCase
@fetcher.data["http://gems.example.com/gems/b-1.gem"] = data
policy = Gem::Security::HighSecurity
- inst = Gem::DependencyInstaller.new :security_policy => policy
+ inst = Gem::DependencyInstaller.new security_policy: policy
e = assert_raise Gem::Security::Exception do
inst.install "b"
@@ -901,7 +935,7 @@ class TestGemDependencyInstaller < Gem::TestCase
@fetcher.data["http://gems.example.com/gems/a-1.gem"] = read_binary(@a1_gem)
- inst = Gem::DependencyInstaller.new :wrappers => false, :format_executable => false
+ inst = Gem::DependencyInstaller.new wrappers: false, format_executable: false
inst.install "a"
refute_match(/This file was generated by RubyGems./,
@@ -1121,7 +1155,7 @@ class TestGemDependencyInstaller < Gem::TestCase
FileUtils.mv @a1_gem, @tempdir
FileUtils.mv @b1_gem, @tempdir
- inst = Gem::DependencyInstaller.new :ignore_dependencies => true
+ inst = Gem::DependencyInstaller.new ignore_dependencies: true
request_set = inst.resolve_dependencies "b", req(">= 0")
requests = request_set.sorted_requests.map(&:full_name)
diff --git a/test/rubygems/test_gem_ext_builder.rb b/test/rubygems/test_gem_ext_builder.rb
index 14b470de97..d5812da2aa 100644
--- a/test/rubygems/test_gem_ext_builder.rb
+++ b/test/rubygems/test_gem_ext_builder.rb
@@ -48,11 +48,11 @@ install:
results = results.join("\n").b
- assert_match(/DESTDIR\\=#{ENV['DESTDIR']} clean$/, results)
- assert_match(/DESTDIR\\=#{ENV['DESTDIR']}$/, results)
- assert_match(/DESTDIR\\=#{ENV['DESTDIR']} install$/, results)
+ assert_match(/DESTDIR\\=#{ENV["DESTDIR"]} clean$/, results)
+ assert_match(/DESTDIR\\=#{ENV["DESTDIR"]}$/, results)
+ assert_match(/DESTDIR\\=#{ENV["DESTDIR"]} install$/, results)
- unless /nmake/.match?(results)
+ unless results.include?("nmake")
assert_match(/^clean: destination$/, results)
assert_match(/^all: destination$/, results)
assert_match(/^install: destination$/, results)
@@ -77,12 +77,14 @@ install:
results = results.join("\n").b
- assert_match(/DESTDIR\\=#{ENV['DESTDIR']} clean$/, results)
- assert_match(/DESTDIR\\=#{ENV['DESTDIR']}$/, results)
- assert_match(/DESTDIR\\=#{ENV['DESTDIR']} install$/, results)
+ assert_match(/DESTDIR\\=#{ENV["DESTDIR"]} clean$/, results)
+ assert_match(/DESTDIR\\=#{ENV["DESTDIR"]}$/, results)
+ assert_match(/DESTDIR\\=#{ENV["DESTDIR"]} install$/, results)
end
def test_custom_make_with_options
+ pend "native windows platform only provides nmake" if vc_windows?
+
ENV["make"] = "make V=1"
results = []
File.open File.join(@ext, "Makefile"), "w" do |io|
@@ -106,38 +108,41 @@ install:
def test_build_extensions
pend "terminates on mswin" if vc_windows? && ruby_repo?
- @spec.extensions << "ext/extconf.rb"
- ext_dir = File.join @spec.gem_dir, "ext"
+ extension_in_lib do
+ @spec.extensions << "ext/extconf.rb"
- FileUtils.mkdir_p ext_dir
+ ext_dir = File.join @spec.gem_dir, "ext"
- extconf_rb = File.join ext_dir, "extconf.rb"
+ FileUtils.mkdir_p ext_dir
- File.open extconf_rb, "w" do |f|
- f.write <<-'RUBY'
- require 'mkmf'
+ extconf_rb = File.join ext_dir, "extconf.rb"
- create_makefile 'a'
- RUBY
- end
+ File.open extconf_rb, "w" do |f|
+ f.write <<-'RUBY'
+ require 'mkmf'
- ext_lib_dir = File.join ext_dir, "lib"
- FileUtils.mkdir ext_lib_dir
- FileUtils.touch File.join ext_lib_dir, "a.rb"
- FileUtils.mkdir File.join ext_lib_dir, "a"
- FileUtils.touch File.join ext_lib_dir, "a", "b.rb"
+ create_makefile 'a'
+ RUBY
+ end
- use_ui @ui do
- @builder.build_extensions
- end
+ ext_lib_dir = File.join ext_dir, "lib"
+ FileUtils.mkdir ext_lib_dir
+ FileUtils.touch File.join ext_lib_dir, "a.rb"
+ FileUtils.mkdir File.join ext_lib_dir, "a"
+ FileUtils.touch File.join ext_lib_dir, "a", "b.rb"
- assert_path_exist @spec.extension_dir
- assert_path_exist @spec.gem_build_complete_path
- assert_path_exist File.join @spec.extension_dir, "gem_make.out"
- assert_path_exist File.join @spec.extension_dir, "a.rb"
- assert_path_exist File.join @spec.gem_dir, "lib", "a.rb"
- assert_path_exist File.join @spec.gem_dir, "lib", "a", "b.rb"
+ use_ui @ui do
+ @builder.build_extensions
+ end
+
+ assert_path_exist @spec.extension_dir
+ assert_path_exist @spec.gem_build_complete_path
+ assert_path_exist File.join @spec.extension_dir, "gem_make.out"
+ assert_path_exist File.join @spec.extension_dir, "a.rb"
+ assert_path_exist File.join @spec.gem_dir, "lib", "a.rb"
+ assert_path_exist File.join @spec.gem_dir, "lib", "a", "b.rb"
+ end
end
def test_build_extensions_with_gemhome_with_space
@@ -153,55 +158,47 @@ install:
end
def test_build_extensions_install_ext_only
- class << Gem
- alias_method :orig_install_extension_in_lib, :install_extension_in_lib
-
- remove_method :install_extension_in_lib
-
- def Gem.install_extension_in_lib
- false
- end
- end
pend "terminates on mswin" if vc_windows? && ruby_repo?
- @spec.extensions << "ext/extconf.rb"
+ extension_in_lib(false) do
+ @orig_install_extension_in_lib = Gem.configuration.install_extension_in_lib
+ Gem.configuration.install_extension_in_lib = false
- ext_dir = File.join @spec.gem_dir, "ext"
+ @spec.extensions << "ext/extconf.rb"
- FileUtils.mkdir_p ext_dir
+ ext_dir = File.join @spec.gem_dir, "ext"
- extconf_rb = File.join ext_dir, "extconf.rb"
+ FileUtils.mkdir_p ext_dir
- File.open extconf_rb, "w" do |f|
- f.write <<-'RUBY'
- require 'mkmf'
+ extconf_rb = File.join ext_dir, "extconf.rb"
- create_makefile 'a'
- RUBY
- end
+ File.open extconf_rb, "w" do |f|
+ f.write <<-'RUBY'
+ require 'mkmf'
- ext_lib_dir = File.join ext_dir, "lib"
- FileUtils.mkdir ext_lib_dir
- FileUtils.touch File.join ext_lib_dir, "a.rb"
- FileUtils.mkdir File.join ext_lib_dir, "a"
- FileUtils.touch File.join ext_lib_dir, "a", "b.rb"
+ create_makefile 'a'
+ RUBY
+ end
- use_ui @ui do
- @builder.build_extensions
- end
+ ext_lib_dir = File.join ext_dir, "lib"
+ FileUtils.mkdir ext_lib_dir
+ FileUtils.touch File.join ext_lib_dir, "a.rb"
+ FileUtils.mkdir File.join ext_lib_dir, "a"
+ FileUtils.touch File.join ext_lib_dir, "a", "b.rb"
- assert_path_exist @spec.extension_dir
- assert_path_exist @spec.gem_build_complete_path
- assert_path_exist File.join @spec.extension_dir, "gem_make.out"
- assert_path_exist File.join @spec.extension_dir, "a.rb"
- assert_path_not_exist File.join @spec.gem_dir, "lib", "a.rb"
- assert_path_not_exist File.join @spec.gem_dir, "lib", "a", "b.rb"
- ensure
- class << Gem
- remove_method :install_extension_in_lib
+ use_ui @ui do
+ @builder.build_extensions
+ end
- alias_method :install_extension_in_lib, :orig_install_extension_in_lib
+ assert_path_exist @spec.extension_dir
+ assert_path_exist @spec.gem_build_complete_path
+ assert_path_exist File.join @spec.extension_dir, "gem_make.out"
+ assert_path_exist File.join @spec.extension_dir, "a.rb"
+ assert_path_not_exist File.join @spec.gem_dir, "lib", "a.rb"
+ assert_path_not_exist File.join @spec.gem_dir, "lib", "a", "b.rb"
end
+ ensure
+ Gem.configuration.install_extension_in_lib = @orig_install_extension_in_lib
end
def test_build_extensions_none
diff --git a/test/rubygems/test_gem_ext_cargo_builder.rb b/test/rubygems/test_gem_ext_cargo_builder.rb
index 0d893f5424..5faf3e2480 100644
--- a/test/rubygems/test_gem_ext_cargo_builder.rb
+++ b/test/rubygems/test_gem_ext_cargo_builder.rb
@@ -152,12 +152,16 @@ class TestGemExtCargoBuilder < Gem::TestCase
require "fiddle"
dylib_handle = Fiddle.dlopen bundle
assert_nothing_raised { dylib_handle[name] }
+ ensure
+ dylib_handle&.close
end
def refute_ffi_handle(bundle, name)
require "fiddle"
dylib_handle = Fiddle.dlopen bundle
assert_raise { dylib_handle[name] }
+ ensure
+ dylib_handle&.close
end
def replace_in_rust_file(name, from, to)
diff --git a/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock b/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock
index 25905d8cf6..abd1e0ae33 100644
--- a/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock
+++ b/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock
@@ -13,9 +13,9 @@ dependencies = [
[[package]]
name = "bindgen"
-version = "0.60.1"
+version = "0.69.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6"
+checksum = "9ffcebc3849946a7170a05992aac39da343a90676ab392c51a4280981d6379c2"
dependencies = [
"bitflags",
"cexpr",
@@ -28,13 +28,14 @@ dependencies = [
"regex",
"rustc-hash",
"shlex",
+ "syn",
]
[[package]]
name = "bitflags"
-version = "1.3.2"
+version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
[[package]]
name = "cexpr"
@@ -133,40 +134,44 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "proc-macro2"
-version = "1.0.47"
+version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
+checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.21"
+version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rb-sys"
-version = "0.9.58"
+version = "0.9.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0158f5115e1ad04a2ee231f597e86306af96f36a8b93ac0c01f8852d0ba89278"
+checksum = "47d30bcad206b51f2f66121190ca678dce1fdf3a2eae0ac5d838d1818b19bdf5"
dependencies = [
"rb-sys-build",
]
[[package]]
name = "rb-sys-build"
-version = "0.9.58"
+version = "0.9.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c27b779db4a2863db74ddad0011f0d0c55c528e9601126d4613ad688063bc05"
+checksum = "3cbd92f281615f3c2dcb9dcb0f0576624752afbf9a7f99173b37c4b55b62dd8a"
dependencies = [
"bindgen",
+ "lazy_static",
+ "proc-macro2",
+ "quote",
"regex",
"shell-words",
+ "syn",
]
[[package]]
@@ -200,9 +205,20 @@ checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
[[package]]
name = "shlex"
-version = "1.1.0"
+version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "syn"
+version = "2.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
[[package]]
name = "unicode-ident"
diff --git a/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml b/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml
index 0777be730e..ad3e7f9b76 100644
--- a/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml
+++ b/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml
@@ -7,4 +7,4 @@ edition = "2021"
crate-type = ["cdylib"]
[dependencies]
-rb-sys = "0.9.58"
+rb-sys = "0.9.97"
diff --git a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock
index a6739b0a1c..1d174f569e 100644
--- a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock
+++ b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock
@@ -13,9 +13,9 @@ dependencies = [
[[package]]
name = "bindgen"
-version = "0.62.0"
+version = "0.69.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6720a8b7b2d39dd533285ed438d458f65b31b5c257e6ac7bb3d7e82844dd722"
+checksum = "9ffcebc3849946a7170a05992aac39da343a90676ab392c51a4280981d6379c2"
dependencies = [
"bitflags",
"cexpr",
@@ -33,9 +33,9 @@ dependencies = [
[[package]]
name = "bitflags"
-version = "1.3.2"
+version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
[[package]]
name = "cexpr"
@@ -127,36 +127,36 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "proc-macro2"
-version = "1.0.56"
+version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
+checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.23"
+version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
+checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rb-sys"
-version = "0.9.78"
+version = "0.9.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91447d8cbb45afb5c915bad4dd44bd4b4e9be37648122409ceca75302cb81683"
+checksum = "47d30bcad206b51f2f66121190ca678dce1fdf3a2eae0ac5d838d1818b19bdf5"
dependencies = [
"rb-sys-build",
]
[[package]]
name = "rb-sys-build"
-version = "0.9.78"
+version = "0.9.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20673c1cfbd57b2db6c066b796352f07d241c45b210fd15b269dec54fa240380"
+checksum = "3cbd92f281615f3c2dcb9dcb0f0576624752afbf9a7f99173b37c4b55b62dd8a"
dependencies = [
"bindgen",
"lazy_static",
@@ -205,15 +205,15 @@ checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
[[package]]
name = "shlex"
-version = "1.1.0"
+version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "syn"
-version = "1.0.107"
+version = "2.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
+checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567"
dependencies = [
"proc-macro2",
"quote",
diff --git a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml
index d89721a3cd..60cf49ce03 100644
--- a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml
+++ b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml
@@ -7,4 +7,4 @@ edition = "2021"
crate-type = ["cdylib"]
[dependencies]
-rb-sys = "0.9.78"
+rb-sys = "0.9.97"
diff --git a/test/rubygems/test_gem_ext_ext_conf_builder.rb b/test/rubygems/test_gem_ext_ext_conf_builder.rb
index 018251ac06..218c6f3d5e 100644
--- a/test/rubygems/test_gem_ext_ext_conf_builder.rb
+++ b/test/rubygems/test_gem_ext_ext_conf_builder.rb
@@ -34,7 +34,7 @@ class TestGemExtExtConfBuilder < Gem::TestCase
assert_same result, output
assert_match(/^current directory:/, output[0])
- assert_match(/^#{Gem.ruby}.* extconf.rb/, output[1])
+ assert_match(/^#{Regexp.quote(Gem.ruby)}.* extconf.rb/, output[1])
assert_equal "creating Makefile\n", output[2]
assert_match(/^current directory:/, output[3])
assert_contains_make_command "clean", output[4]
@@ -114,7 +114,7 @@ class TestGemExtExtConfBuilder < Gem::TestCase
assert_equal "extconf failed, exit code 1", error.message
- assert_match(/^#{Gem.ruby}.* extconf.rb/, output[1])
+ assert_match(/^#{Regexp.quote(Gem.ruby)}.* extconf.rb/, output[1])
assert_match(File.join(@dest_path, "mkmf.log"), output[4])
assert_includes(output, "To see why this extension failed to compile, please check the mkmf.log which can be found here:\n")
diff --git a/test/rubygems/test_gem_gemcutter_utilities.rb b/test/rubygems/test_gem_gemcutter_utilities.rb
index d266bc7f92..a3236e6276 100644
--- a/test/rubygems/test_gem_gemcutter_utilities.rb
+++ b/test/rubygems/test_gem_gemcutter_utilities.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require_relative "helper"
+require_relative "multifactor_auth_utilities"
require "rubygems"
require "rubygems/command"
require "rubygems/gemcutter_utilities"
@@ -11,6 +12,7 @@ class TestGemGemcutterUtilities < Gem::TestCase
super
credential_setup
+ @fetcher = SignInFetcher.new
# below needed for random testing, class property
Gem.configuration.disable_default_gem_server = nil
@@ -51,7 +53,7 @@ class TestGemGemcutterUtilities < Gem::TestCase
end
def test_api_key
- keys = { :rubygems_api_key => "KEY" }
+ keys = { rubygems_api_key: "KEY" }
File.open Gem.configuration.credentials_path, "w" do |f|
f.write Gem::ConfigFile.dump_with_rubygems_yaml(keys)
@@ -63,7 +65,7 @@ class TestGemGemcutterUtilities < Gem::TestCase
end
def test_api_key_override
- keys = { :rubygems_api_key => "KEY", :other => "OTHER" }
+ keys = { rubygems_api_key: "KEY", other: "OTHER" }
File.open Gem.configuration.credentials_path, "w" do |f|
f.write Gem::ConfigFile.dump_with_rubygems_yaml(keys)
@@ -94,21 +96,19 @@ class TestGemGemcutterUtilities < Gem::TestCase
end
def test_sign_in
- api_key = "a5fdbb6ba150cbb83aad2bb2fede64cf040453903"
- util_sign_in HTTPResponseFactory.create(body: api_key, code: 200, msg: "OK")
+ util_sign_in
assert_match(/Enter your RubyGems.org credentials./, @sign_in_ui.output)
assert @fetcher.last_request["authorization"]
assert_match(/Signed in./, @sign_in_ui.output)
credentials = load_yaml_file Gem.configuration.credentials_path
- assert_equal api_key, credentials[:rubygems_api_key]
+ assert_equal @fetcher.api_key, credentials[:rubygems_api_key]
end
def test_sign_in_with_host
- api_key = "a5fdbb6ba150cbb83aad2bb2fede64cf040453903"
-
- util_sign_in HTTPResponseFactory.create(body: api_key, code: 200, msg: "OK"), "http://example.com", ["http://example.com"]
+ @fetcher = SignInFetcher.new(host: "http://example.com")
+ util_sign_in
assert_match "Enter your http://example.com credentials.",
@sign_in_ui.output
@@ -116,13 +116,12 @@ class TestGemGemcutterUtilities < Gem::TestCase
assert_match(/Signed in./, @sign_in_ui.output)
credentials = load_yaml_file Gem.configuration.credentials_path
- assert_equal api_key, credentials["http://example.com"]
+ assert_equal @fetcher.api_key, credentials["http://example.com"]
end
def test_sign_in_with_host_nil
- api_key = "a5fdbb6ba150cbb83aad2bb2fede64cf040453903"
-
- util_sign_in HTTPResponseFactory.create(body: api_key, code: 200, msg: "OK"), nil, [nil]
+ @fetcher = SignInFetcher.new(host: nil)
+ util_sign_in(args: [nil])
assert_match "Enter your RubyGems.org credentials.",
@sign_in_ui.output
@@ -130,12 +129,12 @@ class TestGemGemcutterUtilities < Gem::TestCase
assert_match(/Signed in./, @sign_in_ui.output)
credentials = load_yaml_file Gem.configuration.credentials_path
- assert_equal api_key, credentials[:rubygems_api_key]
+ assert_equal @fetcher.api_key, credentials[:rubygems_api_key]
end
def test_sign_in_with_host_ENV
- api_key = "a5fdbb6ba150cbb83aad2bb2fede64cf040453903"
- util_sign_in HTTPResponseFactory.create(body: api_key, code: 200, msg: "OK"), "http://example.com"
+ @fetcher = SignInFetcher.new(host: "http://example.com")
+ util_sign_in
assert_match "Enter your http://example.com credentials.",
@sign_in_ui.output
@@ -143,29 +142,26 @@ class TestGemGemcutterUtilities < Gem::TestCase
assert_match(/Signed in./, @sign_in_ui.output)
credentials = load_yaml_file Gem.configuration.credentials_path
- assert_equal api_key, credentials["http://example.com"]
+ assert_equal @fetcher.api_key, credentials["http://example.com"]
end
def test_sign_in_skips_with_existing_credentials
- api_key = "a5fdbb6ba150cbb83aad2bb2fede64cf040453903"
- Gem.configuration.rubygems_api_key = api_key
+ Gem.configuration.rubygems_api_key = @fetcher.api_key
- util_sign_in HTTPResponseFactory.create(body: api_key, code: 200, msg: "OK")
+ util_sign_in
assert_equal "", @sign_in_ui.output
end
def test_sign_in_skips_with_key_override
- api_key = "a5fdbb6ba150cbb83aad2bb2fede64cf040453903"
Gem.configuration.api_keys[:KEY] = "other"
@cmd.options[:key] = :KEY
- util_sign_in HTTPResponseFactory.create(body: api_key, code: 200, msg: "OK")
+ util_sign_in
assert_equal "", @sign_in_ui.output
end
def test_sign_in_with_other_credentials_doesnt_overwrite_other_keys
- api_key = "a5fdbb6ba150cbb83aad2bb2fede64cf040453903"
other_api_key = "f46dbb18bb6a9c97cdc61b5b85c186a17403cdcbf"
config = Hash[:other_api_key, other_api_key]
@@ -173,19 +169,20 @@ class TestGemGemcutterUtilities < Gem::TestCase
File.open Gem.configuration.credentials_path, "w" do |f|
f.write Gem::ConfigFile.dump_with_rubygems_yaml(config)
end
- util_sign_in HTTPResponseFactory.create(body: api_key, code: 200, msg: "OK")
+ util_sign_in
assert_match(/Enter your RubyGems.org credentials./, @sign_in_ui.output)
assert_match(/Signed in./, @sign_in_ui.output)
credentials = load_yaml_file Gem.configuration.credentials_path
- assert_equal api_key, credentials[:rubygems_api_key]
+ assert_equal @fetcher.api_key, credentials[:rubygems_api_key]
assert_equal other_api_key, credentials[:other_api_key]
end
def test_sign_in_with_bad_credentials
+ @fetcher.respond_with_forbidden_api_key_response
assert_raise Gem::MockGemUi::TermError do
- util_sign_in HTTPResponseFactory.create(body: "Access Denied.", code: 403, msg: "Forbidden")
+ util_sign_in
end
assert_match(/Enter your RubyGems.org credentials./, @sign_in_ui.output)
@@ -194,26 +191,16 @@ class TestGemGemcutterUtilities < Gem::TestCase
def test_signin_with_env_otp_code
ENV["GEM_HOST_OTP_CODE"] = "111111"
- api_key = "a5fdbb6ba150cbb83aad2bb2fede64cf040453903"
- util_sign_in HTTPResponseFactory.create(body: api_key, code: 200, msg: "OK")
+ util_sign_in
assert_match "Signed in with API key:", @sign_in_ui.output
assert_equal "111111", @fetcher.last_request["OTP"]
end
def test_sign_in_with_correct_otp_code
- api_key = "a5fdbb6ba150cbb83aad2bb2fede64cf040453903"
- response_fail = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry."
-
- util_sign_in(proc do
- @call_count ||= 0
- if (@call_count += 1).odd?
- HTTPResponseFactory.create(body: response_fail, code: 401, msg: "Unauthorized")
- else
- HTTPResponseFactory.create(body: api_key, code: 200, msg: "OK")
- end
- end, nil, [], "111111\n")
+ @fetcher.respond_with_require_otp
+ util_sign_in(extra_input: "111111\n")
assert_match "You have enabled multi-factor authentication. Please enter OTP code.", @sign_in_ui.output
assert_match "Code: ", @sign_in_ui.output
@@ -224,8 +211,9 @@ class TestGemGemcutterUtilities < Gem::TestCase
def test_sign_in_with_incorrect_otp_code
response = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry."
+ @fetcher.respond_with_unauthorized_api_key_response
assert_raise Gem::MockGemUi::TermError do
- util_sign_in HTTPResponseFactory.create(body: response, code: 401, msg: "Unauthorized"), nil, [], "111111\n"
+ util_sign_in(extra_input: "111111\n")
end
assert_match "You have enabled multi-factor authentication. Please enter OTP code.", @sign_in_ui.output
@@ -235,90 +223,90 @@ class TestGemGemcutterUtilities < Gem::TestCase
end
def test_sign_in_with_webauthn_enabled
- webauthn_verification_url = "rubygems.org/api/v1/webauthn_verification/odow34b93t6aPCdY"
- response_fail = "You have enabled multifactor authentication"
- api_key = "a5fdbb6ba150cbb83aad2bb2fede64cf040453903"
- port = 5678
- server = TCPServer.new(port)
+ server = Gem::MockTCPServer.new
+ @fetcher.respond_with_require_otp
+ @fetcher.respond_with_webauthn_url
TCPServer.stub(:new, server) do
- Gem::WebauthnListener.stub(:wait_for_otp_code, "Uvh6T57tkWuUnWYo") do
- util_sign_in(proc do
- @call_count ||= 0
- if (@call_count += 1).odd?
- HTTPResponseFactory.create(body: response_fail, code: 401, msg: "Unauthorized")
- else
- HTTPResponseFactory.create(body: api_key, code: 200, msg: "OK")
- end
- end, nil, [], "", webauthn_verification_url)
+ Gem::GemcutterUtilities::WebauthnListener.stub(:listener_thread, Thread.new { Thread.current[:otp] = "Uvh6T57tkWuUnWYo" }) do
+ util_sign_in
end
- ensure
- server.close
end
- url_with_port = "#{webauthn_verification_url}?port=#{port}"
- assert_match "You have enabled multi-factor authentication. Please visit #{url_with_port} to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin command with the `--otp [your_code]` option.", @sign_in_ui.output
+ assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \
+ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
+ "you can re-run the gem signin command with the `--otp [your_code]` option.", @sign_in_ui.output
assert_match "You are verified with a security device. You may close the browser window.", @sign_in_ui.output
assert_equal "Uvh6T57tkWuUnWYo", @fetcher.last_request["OTP"]
end
def test_sign_in_with_webauthn_enabled_with_error
- webauthn_verification_url = "rubygems.org/api/v1/webauthn_verification/odow34b93t6aPCdY"
- response_fail = "You have enabled multifactor authentication"
- api_key = "a5fdbb6ba150cbb83aad2bb2fede64cf040453903"
- port = 5678
- server = TCPServer.new(port)
- raise_error = ->(*_args) { raise Gem::WebauthnVerificationError, "Something went wrong" }
+ server = Gem::MockTCPServer.new
+ error = Gem::WebauthnVerificationError.new("Something went wrong")
+ @fetcher.respond_with_require_otp
+ @fetcher.respond_with_webauthn_url
error = assert_raise Gem::MockGemUi::TermError do
TCPServer.stub(:new, server) do
- Gem::WebauthnListener.stub(:wait_for_otp_code, raise_error) do
- util_sign_in(proc do
- @call_count ||= 0
- if (@call_count += 1).odd?
- HTTPResponseFactory.create(body: response_fail, code: 401, msg: "Unauthorized")
- else
- HTTPResponseFactory.create(body: api_key, code: 200, msg: "OK")
- end
- end, nil, [], "", webauthn_verification_url)
+ Gem::GemcutterUtilities::WebauthnListener.stub(:listener_thread, Thread.new { Thread.current[:error] = error }) do
+ util_sign_in
end
- ensure
- server.close
end
end
assert_equal 1, error.exit_code
- url_with_port = "#{webauthn_verification_url}?port=#{port}"
- assert_match "You have enabled multi-factor authentication. Please visit #{url_with_port} to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin command with the `--otp [your_code]` option.", @sign_in_ui.output
+ assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \
+ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
+ "you can re-run the gem signin command with the `--otp [your_code]` option.", @sign_in_ui.output
assert_match "ERROR: Security device verification failed: Something went wrong", @sign_in_ui.error
refute_match "You are verified with a security device. You may close the browser window.", @sign_in_ui.output
refute_match "Signed in with API key:", @sign_in_ui.output
end
- def util_sign_in(response, host = nil, args = [], extra_input = "", webauthn_url = nil)
- email = "you@example.com"
- password = "secret"
- profile_response = HTTPResponseFactory.create(body: "mfa: disabled\n", code: 200, msg: "OK")
- webauthn_response =
- if webauthn_url
- HTTPResponseFactory.create(body: webauthn_url, code: 200, msg: "OK")
- else
- HTTPResponseFactory.create(body: "You don't have any security devices", code: 422, msg: "Unprocessable Entity")
- end
+ def test_sign_in_with_webauthn_enabled_with_polling
+ server = Gem::MockTCPServer.new
+ @fetcher.respond_with_require_otp
+ @fetcher.respond_with_webauthn_url
+ @fetcher.respond_with_webauthn_polling("Uvh6T57tkWuUnWYo")
- if host
- ENV["RUBYGEMS_HOST"] = host
- else
- host = Gem.host
+ TCPServer.stub(:new, server) do
+ util_sign_in
+ end
+
+ assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \
+ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
+ "you can re-run the gem signin command with the `--otp [your_code]` option.", @sign_in_ui.output
+ assert_match "You are verified with a security device. You may close the browser window.", @sign_in_ui.output
+ assert_equal "Uvh6T57tkWuUnWYo", @fetcher.last_request["OTP"]
+ end
+
+ def test_sign_in_with_webauthn_enabled_with_polling_failure
+ server = Gem::MockTCPServer.new
+ @fetcher.respond_with_require_otp
+ @fetcher.respond_with_webauthn_url
+ @fetcher.respond_with_webauthn_polling_failure
+
+ assert_raise Gem::MockGemUi::TermError do
+ TCPServer.stub(:new, server) do
+ util_sign_in
+ end
end
- @fetcher = Gem::FakeFetcher.new
- @fetcher.data["#{host}/api/v1/api_key"] = response
- @fetcher.data["#{host}/api/v1/profile/me.yaml"] = profile_response
- @fetcher.data["#{host}/api/v1/webauthn_verification"] = webauthn_response
+ assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \
+ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
+ "you can re-run the gem signin command with the `--otp [your_code]` option.", @sign_in_ui.output
+ assert_match "ERROR: Security device verification failed: " \
+ "The token in the link you used has either expired or been used already.", @sign_in_ui.error
+ end
+
+ def util_sign_in(args: [], extra_input: "")
+ email = "you@example.com"
+ password = "secret"
+
+ ENV["RUBYGEMS_HOST"] = @fetcher.host
Gem::RemoteFetcher.fetcher = @fetcher
- @sign_in_ui = Gem::MockGemUi.new("#{email}\n#{password}\n\n\n\n\n\n\n\n\n" + extra_input)
+ @sign_in_ui = Gem::MockGemUi.new("#{email}\n#{password}\n\n\n" + extra_input)
use_ui @sign_in_ui do
if args.length > 0
@@ -330,7 +318,7 @@ class TestGemGemcutterUtilities < Gem::TestCase
end
def test_verify_api_key
- keys = { :other => "a5fdbb6ba150cbb83aad2bb2fede64cf040453903" }
+ keys = { other: "a5fdbb6ba150cbb83aad2bb2fede64cf040453903" }
File.open Gem.configuration.credentials_path, "w" do |f|
f.write Gem::ConfigFile.dump_with_rubygems_yaml(keys)
end
@@ -345,4 +333,29 @@ class TestGemGemcutterUtilities < Gem::TestCase
@cmd.verify_api_key :missing
end
end
+
+ class SignInFetcher < Gem::MultifactorAuthFetcher
+ attr_reader :api_key
+
+ def initialize(host: nil)
+ super(host: host)
+ @api_key = "a5fdbb6ba150cbb83aad2bb2fede64cf040453903"
+ @data["#{@host}/api/v1/api_key"] = Gem::HTTPResponseFactory.create(body: @api_key, code: 200, msg: "OK")
+ @data["#{@host}/api/v1/profile/me.yaml"] = Gem::HTTPResponseFactory.create(body: "mfa: disabled\n", code: 200, msg: "OK")
+ end
+
+ def respond_with_require_otp
+ super("#{host}/api/v1/api_key", @api_key)
+ end
+
+ def respond_with_forbidden_api_key_response
+ @data["#{host}/api/v1/api_key"] = Gem::HTTPResponseFactory.create(body: "Access Denied.", code: 403, msg: "Forbidden")
+ end
+
+ def respond_with_unauthorized_api_key_response
+ response = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry."
+
+ @data["#{host}/api/v1/api_key"] = Gem::HTTPResponseFactory.create(body: response, code: 401, msg: "Unauthorized")
+ end
+ end
end
diff --git a/test/rubygems/test_gem_indexer.rb b/test/rubygems/test_gem_indexer.rb
deleted file mode 100644
index 56bf3d9264..0000000000
--- a/test/rubygems/test_gem_indexer.rb
+++ /dev/null
@@ -1,380 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "helper"
-require "rubygems/indexer"
-
-class TestGemIndexer < Gem::TestCase
- def setup
- super
-
- util_make_gems
-
- @d2_0 = util_spec "d", "2.0" do |s|
- s.date = Gem::Specification::TODAY - 86_400 * 3
- end
- util_build_gem @d2_0
-
- @d2_0_a = util_spec "d", "2.0.a"
- util_build_gem @d2_0_a
-
- @d2_0_b = util_spec "d", "2.0.b"
- util_build_gem @d2_0_b
-
- @default = new_default_spec "default", 2
- install_default_gems @default
-
- @indexerdir = File.join(@tempdir, "indexer")
-
- gems = File.join(@indexerdir, "gems")
- FileUtils.mkdir_p gems
- FileUtils.mv Dir[File.join(@gemhome, "cache", "*.gem")], gems
-
- @indexer = Gem::Indexer.new(@indexerdir)
- end
-
- def teardown
- FileUtils.rm_rf(@indexer.directory)
- ensure
- super
- end
-
- def with_indexer(dir, **opts)
- indexer = Gem::Indexer.new(dir, **opts)
- build_directory = indexer.directory
- yield indexer
- ensure
- FileUtils.rm_rf(build_directory) if build_directory
- end
-
- def test_initialize
- assert_equal @indexerdir, @indexer.dest_directory
- Dir.mktmpdir("gem_generate_index") do |tmpdir|
- assert_match(%r{#{tmpdir.match(/.*-/)}}, @indexer.directory) # rubocop:disable Style/RegexpLiteral
- end
-
- with_indexer(@indexerdir) do |indexer|
- assert_predicate indexer, :build_modern
- end
-
- with_indexer(@indexerdir, :build_modern => true) do |indexer|
- assert_predicate indexer, :build_modern
- end
- end
-
- def test_build_indices
- @indexer.make_temp_directories
-
- use_ui @ui do
- @indexer.build_indices
- end
-
- specs_path = File.join @indexer.directory, "specs.#{@marshal_version}"
- specs_dump = Gem.read_binary specs_path
- specs = Marshal.load specs_dump
-
- expected = [["a", Gem::Version.new("1"), "ruby"],
- ["a", Gem::Version.new("2"), "ruby"],
- ["a_evil", Gem::Version.new("9"), "ruby"],
- ["b", Gem::Version.new("2"), "ruby"],
- ["c", Gem::Version.new("1.2"), "ruby"],
- ["d", Gem::Version.new("2.0"), "ruby"],
- ["dep_x", Gem::Version.new("1"), "ruby"],
- ["pl", Gem::Version.new("1"), "i386-linux"],
- ["x", Gem::Version.new("1"), "ruby"]]
-
- assert_equal expected, specs
-
- latest_specs_path = File.join(@indexer.directory,
- "latest_specs.#{@marshal_version}")
- latest_specs_dump = Gem.read_binary latest_specs_path
- latest_specs = Marshal.load latest_specs_dump
-
- expected = [["a", Gem::Version.new("2"), "ruby"],
- ["a_evil", Gem::Version.new("9"), "ruby"],
- ["b", Gem::Version.new("2"), "ruby"],
- ["c", Gem::Version.new("1.2"), "ruby"],
- ["d", Gem::Version.new("2.0"), "ruby"],
- ["dep_x", Gem::Version.new("1"), "ruby"],
- ["pl", Gem::Version.new("1"), "i386-linux"],
- ["x", Gem::Version.new("1"), "ruby"]]
-
- assert_equal expected, latest_specs, "latest_specs"
- end
-
- def test_generate_index
- use_ui @ui do
- @indexer.generate_index
- end
-
- quickdir = File.join @indexerdir, "quick"
- marshal_quickdir = File.join quickdir, "Marshal.#{@marshal_version}"
-
- assert_directory_exists quickdir
- assert_directory_exists marshal_quickdir
-
- assert_indexed marshal_quickdir, "#{File.basename(@a1.spec_file)}.rz"
- assert_indexed marshal_quickdir, "#{File.basename(@a2.spec_file)}.rz"
-
- refute_indexed marshal_quickdir, File.basename(@c1_2.spec_file)
-
- assert_indexed @indexerdir, "specs.#{@marshal_version}"
- assert_indexed @indexerdir, "specs.#{@marshal_version}.gz"
-
- assert_indexed @indexerdir, "latest_specs.#{@marshal_version}"
- assert_indexed @indexerdir, "latest_specs.#{@marshal_version}.gz"
-
- refute_directory_exists @indexer.directory
- end
-
- def test_generate_index_modern
- @indexer.build_modern = true
-
- use_ui @ui do
- @indexer.generate_index
- end
-
- refute_indexed @indexerdir, "yaml"
- refute_indexed @indexerdir, "yaml.Z"
- refute_indexed @indexerdir, "Marshal.#{@marshal_version}"
- refute_indexed @indexerdir, "Marshal.#{@marshal_version}.Z"
-
- quickdir = File.join @indexerdir, "quick"
- marshal_quickdir = File.join quickdir, "Marshal.#{@marshal_version}"
-
- assert_directory_exists quickdir, "quickdir should be directory"
- assert_directory_exists marshal_quickdir
-
- refute_indexed quickdir, "index"
- refute_indexed quickdir, "index.rz"
-
- refute_indexed quickdir, "latest_index"
- refute_indexed quickdir, "latest_index.rz"
-
- refute_indexed quickdir, "#{File.basename(@a1.spec_file)}.rz"
- refute_indexed quickdir, "#{File.basename(@a2.spec_file)}.rz"
- refute_indexed quickdir, "#{File.basename(@b2.spec_file)}.rz"
- refute_indexed quickdir, "#{File.basename(@c1_2.spec_file)}.rz"
-
- refute_indexed quickdir, "#{@pl1.original_name}.gemspec.rz"
- refute_indexed quickdir, "#{File.basename(@pl1.spec_file)}.rz"
-
- assert_indexed marshal_quickdir, "#{File.basename(@a1.spec_file)}.rz"
- assert_indexed marshal_quickdir, "#{File.basename(@a2.spec_file)}.rz"
-
- refute_indexed quickdir, File.basename(@c1_2.spec_file).to_s
- refute_indexed marshal_quickdir, File.basename(@c1_2.spec_file).to_s
-
- assert_indexed @indexerdir, "specs.#{@marshal_version}"
- assert_indexed @indexerdir, "specs.#{@marshal_version}.gz"
-
- assert_indexed @indexerdir, "latest_specs.#{@marshal_version}"
- assert_indexed @indexerdir, "latest_specs.#{@marshal_version}.gz"
- end
-
- def test_generate_index_modern_back_to_back
- @indexer.build_modern = true
-
- use_ui @ui do
- @indexer.generate_index
- end
-
- with_indexer @indexerdir do |indexer|
- indexer.build_modern = true
-
- use_ui @ui do
- indexer.generate_index
- end
- quickdir = File.join @indexerdir, "quick"
- marshal_quickdir = File.join quickdir, "Marshal.#{@marshal_version}"
-
- assert_directory_exists quickdir
- assert_directory_exists marshal_quickdir
-
- assert_indexed marshal_quickdir, "#{File.basename(@a1.spec_file)}.rz"
- assert_indexed marshal_quickdir, "#{File.basename(@a2.spec_file)}.rz"
-
- assert_indexed @indexerdir, "specs.#{@marshal_version}"
- assert_indexed @indexerdir, "specs.#{@marshal_version}.gz"
-
- assert_indexed @indexerdir, "latest_specs.#{@marshal_version}"
- assert_indexed @indexerdir, "latest_specs.#{@marshal_version}.gz"
- end
- end
-
- def test_generate_index_ui
- use_ui @ui do
- @indexer.generate_index
- end
-
- assert_match(/^\.\.\.\.\.\.\.\.\.\.\.\.$/, @ui.output)
- assert_match(/^Generating Marshal quick index gemspecs for 12 gems$/, @ui.output)
- assert_match(/^Complete$/, @ui.output)
- assert_match(/^Generating specs index$/, @ui.output)
- assert_match(/^Generating latest specs index$/, @ui.output)
- assert_match(/^Generating prerelease specs index$/, @ui.output)
- assert_match(/^Complete$/, @ui.output)
- assert_match(/^Compressing indices$/, @ui.output)
-
- assert_equal "", @ui.error
- end
-
- def test_generate_index_specs
- use_ui @ui do
- @indexer.generate_index
- end
-
- specs_path = File.join @indexerdir, "specs.#{@marshal_version}"
-
- specs_dump = Gem.read_binary specs_path
- specs = Marshal.load specs_dump
-
- expected = [
- ["a", Gem::Version.new(1), "ruby"],
- ["a", Gem::Version.new(2), "ruby"],
- ["a_evil", Gem::Version.new(9), "ruby"],
- ["b", Gem::Version.new(2), "ruby"],
- ["c", Gem::Version.new("1.2"), "ruby"],
- ["d", Gem::Version.new("2.0"), "ruby"],
- ["dep_x", Gem::Version.new(1), "ruby"],
- ["pl", Gem::Version.new(1), "i386-linux"],
- ["x", Gem::Version.new(1), "ruby"],
- ]
-
- assert_equal expected, specs
-
- assert_same specs[0].first, specs[1].first,
- "identical names not identical"
-
- assert_same specs[0][1], specs[-1][1],
- "identical versions not identical"
-
- assert_same specs[0].last, specs[1].last,
- "identical platforms not identical"
-
- refute_same specs[1][1], specs[5][1],
- "different versions not different"
- end
-
- def test_generate_index_latest_specs
- use_ui @ui do
- @indexer.generate_index
- end
-
- latest_specs_path = File.join @indexerdir, "latest_specs.#{@marshal_version}"
-
- latest_specs_dump = Gem.read_binary latest_specs_path
- latest_specs = Marshal.load latest_specs_dump
-
- expected = [
- ["a", Gem::Version.new(2), "ruby"],
- ["a_evil", Gem::Version.new(9), "ruby"],
- ["b", Gem::Version.new(2), "ruby"],
- ["c", Gem::Version.new("1.2"), "ruby"],
- ["d", Gem::Version.new("2.0"), "ruby"],
- ["dep_x", Gem::Version.new(1), "ruby"],
- ["pl", Gem::Version.new(1), "i386-linux"],
- ["x", Gem::Version.new(1), "ruby"],
- ]
-
- assert_equal expected, latest_specs
-
- assert_same latest_specs[0][1], latest_specs[2][1],
- "identical versions not identical"
-
- assert_same latest_specs[0].last, latest_specs[1].last,
- "identical platforms not identical"
- end
-
- def test_generate_index_prerelease_specs
- use_ui @ui do
- @indexer.generate_index
- end
-
- prerelease_specs_path = File.join @indexerdir, "prerelease_specs.#{@marshal_version}"
-
- prerelease_specs_dump = Gem.read_binary prerelease_specs_path
- prerelease_specs = Marshal.load prerelease_specs_dump
-
- assert_equal [["a", Gem::Version.new("3.a"), "ruby"],
- ["d", Gem::Version.new("2.0.a"), "ruby"],
- ["d", Gem::Version.new("2.0.b"), "ruby"]],
- prerelease_specs
- end
-
- ##
- # Emulate the starting state of Gem::Specification in a live environment,
- # where it will carry the list of system gems
- def with_system_gems
- Gem::Specification.reset
-
- sys_gem = util_spec "systemgem", "1.0"
- util_build_gem sys_gem
- install_default_gems sys_gem
- yield
- util_remove_gem sys_gem
- end
-
- def test_update_index
- use_ui @ui do
- @indexer.generate_index
- end
-
- quickdir = File.join @indexerdir, "quick"
- marshal_quickdir = File.join quickdir, "Marshal.#{@marshal_version}"
-
- assert_directory_exists quickdir
- assert_directory_exists marshal_quickdir
-
- @d2_1 = util_spec "d", "2.1"
- util_build_gem @d2_1
- @d2_1_tuple = [@d2_1.name, @d2_1.version, @d2_1.original_platform]
-
- @d2_1_a = util_spec "d", "2.2.a"
- util_build_gem @d2_1_a
- @d2_1_a_tuple = [@d2_1_a.name, @d2_1_a.version, @d2_1_a.original_platform]
-
- gems = File.join @indexerdir, "gems"
-
- FileUtils.mv @d2_1.cache_file, gems
- FileUtils.mv @d2_1_a.cache_file, gems
-
- with_system_gems do
- use_ui @ui do
- @indexer.update_index
- end
-
- assert_indexed marshal_quickdir, "#{File.basename(@d2_1.spec_file)}.rz"
-
- specs_index = Marshal.load Gem.read_binary(@indexer.dest_specs_index)
-
- assert_includes specs_index, @d2_1_tuple
- refute_includes specs_index, @d2_1_a_tuple
-
- latest_specs_index = Marshal.load \
- Gem.read_binary(@indexer.dest_latest_specs_index)
-
- assert_includes latest_specs_index, @d2_1_tuple
- assert_includes latest_specs_index,
- [@d2_0.name, @d2_0.version, @d2_0.original_platform]
- refute_includes latest_specs_index, @d2_1_a_tuple
-
- pre_specs_index = Marshal.load \
- Gem.read_binary(@indexer.dest_prerelease_specs_index)
-
- assert_includes pre_specs_index, @d2_1_a_tuple
- refute_includes pre_specs_index, @d2_1_tuple
-
- refute_directory_exists @indexer.directory
- end
- end
-
- def assert_indexed(dir, name)
- file = File.join dir, name
- assert File.exist?(file), "#{file} does not exist"
- end
-
- def refute_indexed(dir, name)
- file = File.join dir, name
- refute File.exist?(file), "#{file} exists"
- end
-end
diff --git a/test/rubygems/test_gem_install_update_options.rb b/test/rubygems/test_gem_install_update_options.rb
index f3bd1c6d5e..8fd5d9c543 100644
--- a/test/rubygems/test_gem_install_update_options.rb
+++ b/test/rubygems/test_gem_install_update_options.rb
@@ -130,6 +130,9 @@ class TestGemInstallUpdateOptions < Gem::InstallerTestCase
end
def test_user_install_disabled_read_only
+ pend "skipped on MS Windows (chmod has no effect)" if Gem.win_platform?
+ pend "skipped in root privilege" if Process.uid.zero?
+
@spec = quick_gem "a" do |spec|
util_make_exec spec
end
@@ -137,23 +140,17 @@ class TestGemInstallUpdateOptions < Gem::InstallerTestCase
util_build_gem @spec
@gem = @spec.cache_file
- if Gem.win_platform?
- pend("test_user_install_disabled_read_only test skipped on MS Windows")
- elsif Process.uid.zero?
- pend("test_user_install_disabled_read_only test skipped in root privilege")
- else
- @cmd.handle_options %w[--no-user-install]
+ @cmd.handle_options %w[--no-user-install]
- refute @cmd.options[:user_install]
+ refute @cmd.options[:user_install]
- FileUtils.chmod 0o755, @userhome
- FileUtils.chmod 0o000, @gemhome
+ FileUtils.chmod 0o755, @userhome
+ FileUtils.chmod 0o000, @gemhome
- Gem.use_paths @gemhome, @userhome
+ Gem.use_paths @gemhome, @userhome
- assert_raise(Gem::FilePermissionError) do
- Gem::Installer.at(@gem, @cmd.options).install
- end
+ assert_raise(Gem::FilePermissionError) do
+ Gem::Installer.at(@gem, @cmd.options).install
end
ensure
FileUtils.chmod 0o755, @gemhome
diff --git a/test/rubygems/test_gem_installer.rb b/test/rubygems/test_gem_installer.rb
index 3a4566ce8c..61609a26c9 100644
--- a/test/rubygems/test_gem_installer.rb
+++ b/test/rubygems/test_gem_installer.rb
@@ -299,7 +299,7 @@ gem 'other', version
end
policy = Gem::Security::HighSecurity
- installer = Gem::Installer.at a_gem, :security_policy => policy
+ installer = Gem::Installer.at a_gem, security_policy: policy
assert_raise Gem::Security::Exception do
installer.ensure_loadable_spec
@@ -352,8 +352,8 @@ gem 'other', version
ENV["PATH"] = [ENV["PATH"], bin_dir].compact.join(File::PATH_SEPARATOR)
options = {
- :bin_dir => bin_dir,
- :install_dir => "/non/existent",
+ bin_dir: bin_dir,
+ install_dir: "/non/existent",
}
inst = Gem::Installer.at "", options
@@ -738,9 +738,9 @@ gem 'other', version
installer.generate_bin
default_shebang = Gem.ruby
- shebang_line = File.open("#{@gemhome}/bin/executable") {|f| f.readlines.first }
+ shebang_line = File.open("#{@gemhome}/bin/executable", &:gets)
assert_match(/\A#!/, shebang_line)
- assert_match(/#{default_shebang}/, shebang_line)
+ assert_include(shebang_line, default_shebang)
end
end
@@ -749,8 +749,8 @@ gem 'other', version
installer = Gem::Installer.at(
gem_with_dangling_symlink,
- :user_install => false,
- :force => true
+ user_install: false,
+ force: true
)
build_rake_in do
@@ -821,7 +821,7 @@ gem 'other', version
File.chmod(0o555, Gem.plugindir)
system_path = File.join(Gem.plugindir, "a_plugin.rb")
user_path = File.join(Gem.plugindir(Gem.user_dir), "a_plugin.rb")
- installer = util_installer spec, Gem.dir, :user
+ installer = Gem::Installer.at spec.cache_file, user_install: true, force: true
assert_equal spec, installer.install
@@ -846,7 +846,7 @@ gem 'other', version
build_root = File.join(@tempdir, "build_root")
build_root_path = File.join(build_root, Gem.plugindir.gsub(/^[a-zA-Z]:/, ""), "a_plugin.rb")
- installer = Gem::Installer.at spec.cache_file, :build_root => build_root
+ installer = Gem::Installer.at spec.cache_file, build_root: build_root
assert_equal spec, installer.install
@@ -953,7 +953,7 @@ end
path = Gem::Package.build @spec
- installer = Gem::Installer.at path, :install_dir => "#{@gemhome}3"
+ installer = Gem::Installer.at path, install_dir: "#{@gemhome}3"
assert_equal @spec, installer.install
end
@@ -977,7 +977,7 @@ end
def test_initialize_user_install
@gem = setup_base_gem
- installer = Gem::Installer.at @gem, :user_install => true
+ installer = Gem::Installer.at @gem, user_install: true
assert_equal File.join(Gem.user_dir, "gems", @spec.full_name),
installer.gem_dir
@@ -988,13 +988,26 @@ end
@gem = setup_base_gem
installer =
- Gem::Installer.at @gem, :user_install => true, :bin_dir => @tempdir
+ Gem::Installer.at @gem, user_install: true, bin_dir: @tempdir
assert_equal File.join(Gem.user_dir, "gems", @spec.full_name),
installer.gem_dir
assert_equal @tempdir, installer.bin_dir
end
+ def test_install_dir_takes_precedence_to_user_install
+ gemhome2 = "#{@gemhome}2"
+
+ @gem = setup_base_gem
+
+ installer =
+ Gem::Installer.at @gem, install_dir: gemhome2, user_install: true
+ installer.install
+
+ assert_path_exist File.join(gemhome2, "gems", @spec.full_name)
+ assert_path_not_exist File.join(Gem.user_dir, "gems", @spec.full_name)
+ end
+
def test_install
installer = util_setup_installer
@@ -1237,7 +1250,7 @@ end
Gem::Package.build @spec
end
end
- installer = Gem::Installer.at @gem, :force => true
+ installer = Gem::Installer.at @gem, force: true
build_rake_in do
use_ui @ui do
assert_equal @spec, installer.install
@@ -1255,7 +1268,7 @@ end
end
use_ui @ui do
- installer = Gem::Installer.at missing_dep_gem, :force => true
+ installer = Gem::Installer.at missing_dep_gem, force: true
installer.install
end
@@ -1267,9 +1280,29 @@ end
build_root = File.join(@tempdir, "build_root")
@gem = setup_base_gem
- installer = Gem::Installer.at @gem, :build_root => build_root
+ installer = Gem::Installer.at @gem, build_root: build_root
+
+ assert_equal @spec, installer.install
+ end
+
+ def test_install_build_root_when_gem_home_not_writable_does_not_fallback_to_user_install_inside_build_root
+ build_root = File.join(@tempdir, "build_root")
+
+ orig_gem_home = ENV.delete("GEM_HOME")
+
+ @gem = setup_base_gem
+
+ FileUtils.chmod "-w", @gemhome
+
+ installer = Gem::Installer.at @gem, build_root: build_root
assert_equal @spec, installer.install
+
+ build_root_path = File.join(build_root, @gemhome.gsub(/^[a-zA-Z]:/, ""))
+ assert File.exist?(build_root_path), "gem not written to build_root"
+ ensure
+ FileUtils.chmod "+w", @gemhome
+ ENV["GEM_HOME"] = orig_gem_home
end
def test_install_missing_dirs
@@ -1396,7 +1429,7 @@ end
use_ui @ui do
path = Gem::Package.build @spec
- installer = Gem::Installer.at path, :post_install_message => false
+ installer = Gem::Installer.at path, post_install_message: false
installer.install
end
@@ -1420,7 +1453,7 @@ end
use_ui @ui do
path = Gem::Package.build @spec
- installer = Gem::Installer.at path, :install_dir => gemhome2
+ installer = Gem::Installer.at path, install_dir: gemhome2
installer.install
end
@@ -1459,7 +1492,7 @@ end
# reinstall the gem, this is also the same as pristine
use_ui @ui do
- installer = Gem::Installer.at path, :force => true
+ installer = Gem::Installer.at path, force: true
installer.install
end
@@ -1485,7 +1518,7 @@ end
use_ui @ui do
path = Gem::Package.build @spec
- installer = Gem::Installer.at path, :user_install => true
+ installer = Gem::Installer.at path, user_install: true
installer.install
end
@@ -1619,19 +1652,6 @@ end
installer.install
end
assert_path_exist so
- rescue StandardError
- puts "-" * 78
- puts File.read File.join(@gemhome, "gems", "a-2", "Makefile")
- puts "-" * 78
-
- path = File.join(@gemhome, "gems", "a-2", "gem_make.out")
-
- if File.exist?(path)
- puts File.read(path)
- puts "-" * 78
- end
-
- raise
end
end
@@ -1737,7 +1757,7 @@ end
# that it work everything out on it's own.
Gem::Specification.reset
- installer = Gem::Installer.at gem, :install_dir => gemhome2
+ installer = Gem::Installer.at gem, install_dir: gemhome2
build_rake_in do
use_ui @ui do
@@ -1885,9 +1905,9 @@ end
installer = Gem::Installer.at(
gem_with_ill_formated_platform,
- :install_dir => @gemhome,
- :user_install => false,
- :force => true
+ install_dir: @gemhome,
+ user_install: false,
+ force: true
)
use_ui @ui do
@@ -1927,7 +1947,7 @@ end
plugins_dir = File.join(build_root, @gemhome.gsub(/^[a-zA-Z]:/, ""), "plugins")
@gem = setup_base_gem
- installer = use_ui(@ui) { Gem::Installer.at @gem, :build_root => build_root }
+ installer = use_ui(@ui) { Gem::Installer.at @gem, build_root: build_root }
assert_equal build_root, installer.build_root
assert_equal bin_dir, installer.bin_dir
@@ -1942,6 +1962,48 @@ end
assert_equal " Plugins dir: #{plugins_dir}", errors.shift
end
+ def test_process_options_fallback_to_user_install_when_gem_home_not_writable
+ if Process.uid.zero?
+ pend("skipped in root privilege")
+ return
+ end
+
+ orig_gem_home = ENV.delete("GEM_HOME")
+
+ @gem = setup_base_gem
+
+ FileUtils.chmod 0o000, @gemhome
+
+ installer = use_ui(@ui) { Gem::Installer.at @gem }
+
+ assert_equal Gem.user_dir, installer.gem_home
+ assert_equal "Defaulting to user installation because default installation directory (#{@gemhome}) is not writable.", @ui.output.strip
+ ensure
+ FileUtils.chmod 0o755, @gemhome
+ ENV["GEM_HOME"] = orig_gem_home
+ end
+
+ def test_process_options_does_not_fallback_to_user_install_when_gem_home_not_writable_and_no_user_install
+ if Process.uid.zero?
+ pend("skipped in root privilege")
+ return
+ end
+
+ orig_gem_home = ENV.delete("GEM_HOME")
+
+ @gem = setup_base_gem
+
+ FileUtils.chmod 0o000, @gemhome
+
+ installer = use_ui(@ui) { Gem::Installer.at @gem, user_install: false }
+
+ assert_equal @gemhome, installer.gem_home
+ assert_empty @ui.output.strip
+ ensure
+ FileUtils.chmod 0o755, @gemhome
+ ENV["GEM_HOME"] = orig_gem_home
+ end
+
def test_shebang_arguments
load_relative "no" do
installer = setup_base_installer
@@ -2248,7 +2310,7 @@ end
def test_write_build_info_file_install_dir
@gem = setup_base_gem
- installer = Gem::Installer.at @gem, :install_dir => "#{@gemhome}2"
+ installer = Gem::Installer.at @gem, install_dir: "#{@gemhome}2"
installer.build_args = %w[
--with-libyaml-dir /usr/local/Cellar/libyaml/0.1.4
@@ -2340,7 +2402,7 @@ end
ensure
File.class_eval do
remove_method :write
- alias_method :write, :original_write # rubocop:disable Lint/DuplicateMethods
+ alias_method :write, :original_write
remove_method :original_write
end
end
@@ -2353,7 +2415,7 @@ end
def test_default_gem_loaded_from
spec = util_spec "a"
- installer = Gem::Installer.for_spec spec, :install_as_default => true
+ installer = Gem::Installer.for_spec spec, install_as_default: true
installer.install
assert_predicate spec, :default_gem?
end
diff --git a/test/rubygems/test_gem_local_remote_options.rb b/test/rubygems/test_gem_local_remote_options.rb
index b84e70e8b8..cea9cde82b 100644
--- a/test/rubygems/test_gem_local_remote_options.rb
+++ b/test/rubygems/test_gem_local_remote_options.rb
@@ -34,7 +34,7 @@ class TestGemLocalRemoteOptions < Gem::TestCase
def test_clear_sources_option
@cmd.add_local_remote_options
- s = URI.parse "http://only-gems.example.com/"
+ s = Gem::URI.parse "http://only-gems.example.com/"
@cmd.handle_options %W[--clear-sources --source #{s}]
assert_equal [s.to_s], Gem.sources
@@ -76,10 +76,10 @@ class TestGemLocalRemoteOptions < Gem::TestCase
def test_source_option
@cmd.add_source_option
- s1 = URI.parse "http://more-gems.example.com/"
- s2 = URI.parse "http://even-more-gems.example.com/"
- s3 = URI.parse "http://other-gems.example.com/some_subdir"
- s4 = URI.parse "http://more-gems.example.com/" # Intentional duplicate
+ s1 = Gem::URI.parse "http://more-gems.example.com/"
+ s2 = Gem::URI.parse "http://even-more-gems.example.com/"
+ s3 = Gem::URI.parse "http://other-gems.example.com/some_subdir"
+ s4 = Gem::URI.parse "http://more-gems.example.com/" # Intentional duplicate
original_sources = Gem.sources.dup
@@ -97,7 +97,7 @@ class TestGemLocalRemoteOptions < Gem::TestCase
original_sources = Gem.sources.dup
- source = URI.parse "http://more-gems.example.com/"
+ source = Gem::URI.parse "http://more-gems.example.com/"
@cmd.handle_options %W[-s #{source}]
original_sources << source
diff --git a/test/rubygems/test_gem_name_tuple.rb b/test/rubygems/test_gem_name_tuple.rb
index 75c0784d14..bdb8181ce8 100644
--- a/test/rubygems/test_gem_name_tuple.rb
+++ b/test/rubygems/test_gem_name_tuple.rb
@@ -19,14 +19,31 @@ class TestGemNameTuple < Gem::TestCase
end
def test_platform_normalization
- n = Gem::NameTuple.new "a", Gem::Version.new(0), "ruby"
- assert_equal "ruby", n.platform
-
- n = Gem::NameTuple.new "a", Gem::Version.new(0), nil
- assert_equal "ruby", n.platform
-
- n = Gem::NameTuple.new "a", Gem::Version.new(0), ""
- assert_equal "ruby", n.platform
+ a = Gem::NameTuple.new "a", Gem::Version.new(0), "ruby"
+ b = Gem::NameTuple.new "a", Gem::Version.new(0), Gem::Platform::RUBY
+ assert_equal a, b
+ assert_equal a.hash, b.hash
+
+ a = Gem::NameTuple.new "a", Gem::Version.new(0), nil
+ b = Gem::NameTuple.new "a", Gem::Version.new(0), Gem::Platform.new("ruby")
+ assert_equal a, b
+ assert_equal a.hash, b.hash
+
+ a = Gem::NameTuple.new "a", Gem::Version.new(0), ""
+ b = Gem::NameTuple.new "a", Gem::Version.new(0), Gem::Platform.new("ruby")
+ assert_equal a, b
+ assert_equal a.hash, b.hash
+
+ a = Gem::NameTuple.new "a", Gem::Version.new(0), "universal-darwin-23"
+ b = Gem::NameTuple.new "a", Gem::Version.new(0), Gem::Platform.new("universal-darwin-23")
+ assert_equal a, b
+ assert_equal a.hash, b.hash
+
+ # Gem::Platform does normalization so that these are equal (note the missing dash before 21)
+ a = Gem::NameTuple.new "a", Gem::Version.new(0), "universal-darwin-21"
+ b = Gem::NameTuple.new "a", Gem::Version.new(0), Gem::Platform.new("universal-darwin21")
+ assert_equal a, b
+ assert_equal a.hash, b.hash
end
def test_spec_name
diff --git a/test/rubygems/test_gem_package.rb b/test/rubygems/test_gem_package.rb
index 1adb780158..2065864107 100644
--- a/test/rubygems/test_gem_package.rb
+++ b/test/rubygems/test_gem_package.rb
@@ -574,6 +574,32 @@ class TestGemPackage < Gem::Package::TarTestCase
File.read(extracted)
end
+ def test_extract_symlink_into_symlink_dir
+ package = Gem::Package.new @gem
+ tgz_io = util_tar_gz do |tar|
+ tar.mkdir "lib", 0o755
+ tar.add_symlink "lib/link", "./inside.rb", 0o644
+ tar.add_file "lib/inside.rb", 0o644 do |io|
+ io.write "hi"
+ end
+ end
+
+ destination_subdir = File.join @destination, "subdir"
+ FileUtils.mkdir_p destination_subdir
+
+ destination_linkdir = File.join @destination, "linkdir"
+ File.symlink(destination_subdir, destination_linkdir)
+
+ package.extract_tar_gz tgz_io, destination_linkdir
+
+ extracted = File.join destination_subdir, "lib/link"
+ assert_path_exist extracted
+ assert_equal "./inside.rb",
+ File.readlink(extracted)
+ assert_equal "hi",
+ File.read(extracted)
+ end
+
def test_extract_tar_gz_symlink_broken_relative_path
package = Gem::Package.new @gem
package.verify
@@ -734,12 +760,10 @@ class TestGemPackage < Gem::Package::TarTestCase
package = Gem::Package.new @gem
file = "file.rb".dup
- file.taint if RUBY_VERSION < "2.7"
destination = package.install_location file, @destination
assert_equal File.join(@destination, "file.rb"), destination
- refute destination.tainted? if RUBY_VERSION < "2.7"
end
def test_install_location_absolute
@@ -773,12 +797,10 @@ class TestGemPackage < Gem::Package::TarTestCase
package = Gem::Package.new @gem
file = "foo//file.rb".dup
- file.taint if RUBY_VERSION < "2.7"
destination = package.install_location file, @destination
assert_equal File.join(@destination, "foo", "file.rb"), destination
- refute destination.tainted? if RUBY_VERSION < "2.7"
end
def test_install_location_relative
@@ -946,6 +968,95 @@ class TestGemPackage < Gem::Package::TarTestCase
tf.close!
end
+ def test_verify_corrupt_tar_metadata_entry
+ gem = tar_file_header("metadata.gz", "", 0, 999, Time.now)
+
+ File.open "corrupt.gem", "wb" do |io|
+ io.write gem
+ end
+
+ package = Gem::Package.new "corrupt.gem"
+
+ e = nil
+ out_err = capture_output do
+ e = assert_raise Gem::Package::FormatError do
+ package.verify
+ end
+ end
+
+ assert_match(/(EOFError|end of file reached) in corrupt.gem/i, e.message)
+ assert_equal(["", "Exception while verifying corrupt.gem\n"], out_err)
+ end
+
+ def test_verify_corrupt_tar_checksums_entry
+ gem = tar_file_header("checksums.yaml.gz", "", 0, 100, Time.now)
+
+ File.open "corrupt.gem", "wb" do |io|
+ io.write gem
+ end
+
+ package = Gem::Package.new "corrupt.gem"
+
+ e = assert_raise Gem::Package::FormatError do
+ package.verify
+ end
+
+ assert_equal "not in gzip format in corrupt.gem", e.message
+ end
+
+ def test_verify_corrupt_tar_data_entry
+ gem = tar_file_header("data.tar.gz", "", 0, 100, Time.now)
+
+ File.open "corrupt.gem", "wb" do |io|
+ io.write gem
+ end
+
+ package = Gem::Package.new "corrupt.gem"
+
+ e = nil
+ out_err = capture_output do
+ e = assert_raise Gem::Package::FormatError do
+ package.verify
+ end
+ end
+
+ assert_match(/(EOFError|end of file reached) in corrupt.gem/i, e.message)
+ assert_equal(["", "Exception while verifying corrupt.gem\n"], out_err)
+ end
+
+ def test_corrupt_data_tar_gz
+ data_tgz = util_gzip tar_file_header("lib/code.rb", "", 0, 100, Time.now)
+ metadata_gz = util_gzip @spec.to_yaml
+
+ gem = util_tar do |tar|
+ tar.add_file "data.tar.gz", 0o444 do |io|
+ io.write data_tgz
+ end
+
+ tar.add_file "metadata.gz", 0o644 do |io|
+ io.write metadata_gz
+ end
+ end
+
+ File.open "corrupt.gem", "wb" do |io|
+ io.write gem.string
+ end
+
+ package = Gem::Package.new "corrupt.gem"
+
+ e = assert_raise Gem::Package::FormatError do
+ package.contents
+ end
+
+ assert_match(/(EOFError|end of file reached) in corrupt.gem/i, e.message)
+
+ e = assert_raise Gem::Package::FormatError do
+ package.extract_files @destination
+ end
+
+ assert_match(/(EOFError|end of file reached) in corrupt.gem/i, e.message)
+ end
+
def test_verify_empty
FileUtils.touch "empty.gem"
diff --git a/test/rubygems/test_gem_package_tar_header.rb b/test/rubygems/test_gem_package_tar_header.rb
index bbdf20c23c..4469750f9a 100644
--- a/test/rubygems/test_gem_package_tar_header.rb
+++ b/test/rubygems/test_gem_package_tar_header.rb
@@ -8,19 +8,19 @@ class TestGemPackageTarHeader < Gem::Package::TarTestCase
super
header = {
- :name => "x",
- :mode => 0o644,
- :uid => 1000,
- :gid => 10_000,
- :size => 100,
- :mtime => 12_345,
- :typeflag => "0",
- :linkname => "link",
- :uname => "user",
- :gname => "group",
- :devmajor => 1,
- :devminor => 2,
- :prefix => "y",
+ name: "x",
+ mode: 0o644,
+ uid: 1000,
+ gid: 10_000,
+ size: 100,
+ mtime: 12_345,
+ typeflag: "0",
+ linkname: "link",
+ uname: "user",
+ gname: "group",
+ devmajor: 1,
+ devminor: 2,
+ prefix: "y",
}
@tar_header = Gem::Package::TarHeader.new header
@@ -59,29 +59,29 @@ class TestGemPackageTarHeader < Gem::Package::TarTestCase
def test_initialize_bad
assert_raise ArgumentError do
- Gem::Package::TarHeader.new :name => "", :size => "", :mode => ""
+ Gem::Package::TarHeader.new name: "", size: "", mode: ""
end
assert_raise ArgumentError do
- Gem::Package::TarHeader.new :name => "", :size => "", :prefix => ""
+ Gem::Package::TarHeader.new name: "", size: "", prefix: ""
end
assert_raise ArgumentError do
- Gem::Package::TarHeader.new :name => "", :prefix => "", :mode => ""
+ Gem::Package::TarHeader.new name: "", prefix: "", mode: ""
end
assert_raise ArgumentError do
- Gem::Package::TarHeader.new :prefix => "", :size => "", :mode => ""
+ Gem::Package::TarHeader.new prefix: "", size: "", mode: ""
end
end
def test_initialize_typeflag
header = {
- :mode => "",
- :name => "",
- :prefix => "",
- :size => "",
- :typeflag => "",
+ mode: "",
+ name: "",
+ prefix: "",
+ size: "",
+ typeflag: "",
}
tar_header = Gem::Package::TarHeader.new header
@@ -92,9 +92,9 @@ class TestGemPackageTarHeader < Gem::Package::TarTestCase
def test_empty_eh
refute_empty @tar_header
- @tar_header = Gem::Package::TarHeader.new :name => "x", :prefix => "",
- :mode => 0, :size => 0,
- :empty => true
+ @tar_header = Gem::Package::TarHeader.new name: "x", prefix: "",
+ mode: 0, size: 0,
+ empty: true
assert_empty @tar_header
end
diff --git a/test/rubygems/test_gem_package_tar_reader.rb b/test/rubygems/test_gem_package_tar_reader.rb
index 178e29c3d0..b2f7cc2d5c 100644
--- a/test/rubygems/test_gem_package_tar_reader.rb
+++ b/test/rubygems/test_gem_package_tar_reader.rb
@@ -25,6 +25,21 @@ class TestGemPackageTarReader < Gem::Package::TarTestCase
io.close!
end
+ def test_each_with_not_a_tar
+ text = "Hello, world!!?\n" * 256 # 4 KiB
+ io = TempIO.new text
+
+ Gem::Package::TarReader.new io do |tar|
+ assert_raise Gem::Package::TarInvalidError do
+ tar.each do
+ flunk "TarInvalidError was expected to occur, but an entry was found"
+ end
+ end
+ end
+ ensure
+ io.close!
+ end
+
def test_rewind
content = ("a".."z").to_a.join(" ")
diff --git a/test/rubygems/test_gem_package_tar_reader_entry.rb b/test/rubygems/test_gem_package_tar_reader_entry.rb
index d8c9ba5665..67ab7922b5 100644
--- a/test/rubygems/test_gem_package_tar_reader_entry.rb
+++ b/test/rubygems/test_gem_package_tar_reader_entry.rb
@@ -181,10 +181,15 @@ class TestGemPackageTarReaderEntry < Gem::Package::TarTestCase
assert_equal @contents[100..-1], @entry.read
end
- def test_read_partial
+ def test_readpartial
assert_equal @contents[0...100], @entry.readpartial(100)
end
+ def test_readpartial_to_eof
+ assert_equal @contents, @entry.readpartial(4096)
+ assert @entry.eof?
+ end
+
def test_read_partial_buffer
buffer = "".b
@entry.readpartial(100, buffer)
@@ -193,11 +198,42 @@ class TestGemPackageTarReaderEntry < Gem::Package::TarTestCase
def test_readpartial_past_eof
@entry.readpartial(@contents.size)
+ assert @entry.eof?
assert_raise(EOFError) do
@entry.readpartial(1)
end
end
+ def test_read_corrupted_tar
+ corrupt_tar = String.new
+ corrupt_tar << tar_file_header("lib/foo", "", 0, 100, Time.now)
+ corrupt_tar << tar_file_contents("")
+ corrupt_entry = util_entry corrupt_tar
+
+ assert_equal "", corrupt_entry.read(0)
+ assert_equal "", corrupt_entry.read, "IO.read without len should return empty string (even though it's at an unpexpected EOF)"
+
+ corrupt_entry.rewind
+
+ assert_nil corrupt_entry.read(100), "IO.read with len should return nil as per IO.read docs"
+ ensure
+ close_util_entry(corrupt_entry) if corrupt_entry
+ end
+
+ def test_readpartial_corrupted_tar
+ corrupt_tar = String.new
+ corrupt_tar << tar_file_header("lib/foo", "", 0, 100, Time.now)
+ corrupt_tar << tar_file_contents("")
+
+ corrupt_entry = util_entry corrupt_tar
+
+ assert_raise EOFError do
+ corrupt_entry.readpartial(100)
+ end
+ ensure
+ close_util_entry(corrupt_entry) if corrupt_entry
+ end
+
def test_rewind
char = @entry.getc
@@ -303,4 +339,20 @@ class TestGemPackageTarReaderEntry < Gem::Package::TarTestCase
assert_equal contents2.size, entry.pos
end
end
+
+ def test_seek_in_gzip_io_corrupted
+ @tar << tar_file_header("lib/bar", "", 0, 100, Time.now)
+ @tar << tar_file_contents("")
+
+ tgz = util_gzip(@tar)
+
+ Zlib::GzipReader.wrap StringIO.new(tgz) do |gzio|
+ util_entry(gzio).close # skip the first entry so io.pos is not 0
+ entry = util_entry(gzio)
+
+ assert_raise EOFError do
+ entry.seek(50)
+ end
+ end
+ end
end
diff --git a/test/rubygems/test_gem_package_task.rb b/test/rubygems/test_gem_package_task.rb
index f0aaa3f082..6f322ad61e 100644
--- a/test/rubygems/test_gem_package_task.rb
+++ b/test/rubygems/test_gem_package_task.rb
@@ -9,10 +9,6 @@ rescue LoadError => e
raise unless e.path == "rake/packagetask"
end
-unless defined?(Rake::PackageTask)
- warn "Skipping Gem::PackageTask tests. rake not found."
-end
-
class TestGemPackageTask < Gem::TestCase
def test_gem_package
original_rake_fileutils_verbosity = RakeFileUtils.verbose_flag
diff --git a/test/rubygems/test_gem_platform.rb b/test/rubygems/test_gem_platform.rb
index 4d1e0298ea..e4bf882317 100644
--- a/test/rubygems/test_gem_platform.rb
+++ b/test/rubygems/test_gem_platform.rb
@@ -71,7 +71,7 @@ class TestGemPlatform < Gem::TestCase
Gem.platforms = platforms
class << Gem::Platform
remove_method :match_gem?
- alias_method :match_gem?, :original_match_gem? # rubocop:disable Lint/DuplicateMethods
+ alias_method :match_gem?, :original_match_gem?
remove_method :original_match_gem?
end
end
@@ -483,8 +483,10 @@ class TestGemPlatform < Gem::TestCase
def test_gem_platform_match_with_string_argument
util_set_arch "x86_64-linux-musl"
- assert(Gem::Platform.match(Gem::Platform.new("x86_64-linux")), "should match Gem::Platform")
- assert(Gem::Platform.match("x86_64-linux"), "should match String platform")
+ Gem::Deprecate.skip_during do
+ assert(Gem::Platform.match(Gem::Platform.new("x86_64-linux")), "should match Gem::Platform")
+ assert(Gem::Platform.match("x86_64-linux"), "should match String platform")
+ end
end
def assert_local_match(name)
diff --git a/test/rubygems/test_gem_remote_fetcher.rb b/test/rubygems/test_gem_remote_fetcher.rb
index 47633175f6..e71b2f5ff6 100644
--- a/test/rubygems/test_gem_remote_fetcher.rb
+++ b/test/rubygems/test_gem_remote_fetcher.rb
@@ -162,7 +162,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
end
def test_cache_update_path
- uri = URI "http://example/file"
+ uri = Gem::URI "http://example/file"
path = File.join @tempdir, "file"
fetcher = util_fuck_with_fetcher "hello"
@@ -176,7 +176,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def test_cache_update_path_with_utf8_internal_encoding
with_internal_encoding("UTF-8") do
- uri = URI "http://example/file"
+ uri = Gem::URI "http://example/file"
path = File.join @tempdir, "file"
data = String.new("\xC8").force_encoding(Encoding::BINARY)
@@ -190,7 +190,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
end
def test_cache_update_path_no_update
- uri = URI "http://example/file"
+ uri = Gem::URI "http://example/file"
path = File.join @tempdir, "file"
fetcher = util_fuck_with_fetcher "hello"
@@ -311,6 +311,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
end
def test_download_local
+ omit "doesn't work if tempdir has +" if @tempdir.include?("+")
FileUtils.mv @a1_gem, @tempdir
local_path = File.join @tempdir, @a1.file_name
inst = nil
@@ -323,6 +324,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
end
def test_download_local_space
+ omit "doesn't work if tempdir has +" if @tempdir.include?("+")
space_path = File.join @tempdir, "space path"
FileUtils.mkdir space_path
FileUtils.mv @a1_gem, space_path
@@ -356,6 +358,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
unless Gem.win_platform? || Process.uid.zero? # File.chmod doesn't work
def test_download_local_read_only
+ omit "doesn't work if tempdir has +" if @tempdir.include?("+")
FileUtils.mv @a1_gem, @tempdir
local_path = File.join @tempdir, @a1.file_name
inst = nil
@@ -374,8 +377,10 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
assert_equal(File.join(@tempdir, @a1.file_name),
inst.download(@a1, local_path))
ensure
- FileUtils.chmod 0o755, File.join(Gem.user_dir, "cache")
- FileUtils.chmod 0o755, @a1.cache_dir
+ if local_path
+ FileUtils.chmod 0o755, File.join(Gem.user_dir, "cache")
+ FileUtils.chmod 0o755, @a1.cache_dir
+ end
end
def test_download_read_only
@@ -418,6 +423,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
end
def test_download_same_file
+ omit "doesn't work if tempdir has +" if @tempdir.include?("+")
FileUtils.mv @a1_gem, @tempdir
local_path = File.join @tempdir, @a1.file_name
inst = nil
@@ -548,7 +554,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
@fetcher = fetcher
def fetcher.fetch_http(uri, mtime = nil, head = nil)
- raise Timeout::Error, "timed out"
+ raise Gem::Timeout::Error, "timed out"
end
url = "http://example.com/uri"
@@ -557,7 +563,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
fetcher.fetch_path url
end
- assert_match(/Timeout::Error: timed out \(#{Regexp.escape url}\)\z/,
+ assert_match(/Gem::Timeout::Error: timed out \(#{Regexp.escape url}\)\z/,
e.message)
assert_equal url, e.uri
end
@@ -607,7 +613,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
nil
end
- assert_nil fetcher.fetch_path(URI.parse(@gem_repo), Time.at(0))
+ assert_nil fetcher.fetch_path(Gem::URI.parse(@gem_repo), Time.at(0))
end
def test_implicit_no_proxy
@@ -653,19 +659,19 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def fetcher.request(uri, request_class, last_modified = nil)
url = "http://gems.example.com/redirect"
if defined? @requested
- res = Net::HTTPOK.new nil, 200, nil
+ res = Gem::Net::HTTPOK.new nil, 200, nil
def res.body
"real_path"
end
else
@requested = true
- res = Net::HTTPMovedPermanently.new nil, 301, nil
+ res = Gem::Net::HTTPMovedPermanently.new nil, 301, nil
res.add_field "Location", url
end
res
end
- data = fetcher.fetch_http URI.parse(url)
+ data = fetcher.fetch_http Gem::URI.parse(url)
assert_equal "real_path", data
end
@@ -677,13 +683,13 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def fetcher.request(uri, request_class, last_modified = nil)
url = "http://gems.example.com/redirect"
- res = Net::HTTPMovedPermanently.new nil, 301, nil
+ res = Gem::Net::HTTPMovedPermanently.new nil, 301, nil
res.add_field "Location", url
res
end
e = assert_raise Gem::RemoteFetcher::FetchError do
- fetcher.fetch_http URI.parse(url)
+ fetcher.fetch_http Gem::URI.parse(url)
end
assert_equal "too many redirects (#{url})", e.message
@@ -695,12 +701,12 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
url = "http://gems.example.com/redirect"
def fetcher.request(uri, request_class, last_modified = nil)
- res = Net::HTTPMovedPermanently.new nil, 301, nil
+ res = Gem::Net::HTTPMovedPermanently.new nil, 301, nil
res
end
e = assert_raise Gem::RemoteFetcher::FetchError do
- fetcher.fetch_http URI.parse(url)
+ fetcher.fetch_http Gem::URI.parse(url)
end
assert_equal "redirecting but no redirect location was given (#{url})", e.message
@@ -708,7 +714,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def test_fetch_http_with_additional_headers
ENV["http_proxy"] = @proxy_uri
- ENV["no_proxy"] = URI.parse(@server_uri).host
+ ENV["no_proxy"] = Gem::URI.parse(@server_uri).host
fetcher = Gem::RemoteFetcher.new nil, nil, { "X-Captain" => "murphy" }
@fetcher = fetcher
assert_equal "murphy", fetcher.fetch_path(@server_uri)
@@ -722,7 +728,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def fetcher.request(uri, request_class, last_modified = nil)
$fetched_uri = uri
- res = Net::HTTPOK.new nil, 200, nil
+ res = Gem::Net::HTTPOK.new nil, 200, nil
def res.body
"success"
end
@@ -741,7 +747,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
s3_uri_signer
end
- data = fetcher.fetch_s3 URI.parse(url)
+ data = fetcher.fetch_s3 Gem::URI.parse(url)
assert_equal "https://my-bucket.s3.#{region}.amazonaws.com/gems/specs.4.8.gz?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=testuser%2F20190624%2F#{region}%2Fs3%2Faws4_request&X-Amz-Date=20190624T050641Z&X-Amz-Expires=86400#{token ? "&X-Amz-Security-Token=" + token : ""}&X-Amz-SignedHeaders=host&X-Amz-Signature=#{signature}", $fetched_uri.to_s
assert_equal "success", data
@@ -751,7 +757,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def test_fetch_s3_config_creds
Gem.configuration[:s3_source] = {
- "my-bucket" => { :id => "testuser", :secret => "testpass" },
+ "my-bucket" => { id: "testuser", secret: "testpass" },
}
url = "s3://my-bucket/gems/specs.4.8.gz"
Time.stub :now, Time.at(1_561_353_581) do
@@ -763,7 +769,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def test_fetch_s3_config_creds_with_region
Gem.configuration[:s3_source] = {
- "my-bucket" => { :id => "testuser", :secret => "testpass", :region => "us-west-2" },
+ "my-bucket" => { id: "testuser", secret: "testpass", region: "us-west-2" },
}
url = "s3://my-bucket/gems/specs.4.8.gz"
Time.stub :now, Time.at(1_561_353_581) do
@@ -775,7 +781,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def test_fetch_s3_config_creds_with_token
Gem.configuration[:s3_source] = {
- "my-bucket" => { :id => "testuser", :secret => "testpass", :security_token => "testtoken" },
+ "my-bucket" => { id: "testuser", secret: "testpass", security_token: "testtoken" },
}
url = "s3://my-bucket/gems/specs.4.8.gz"
Time.stub :now, Time.at(1_561_353_581) do
@@ -790,7 +796,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
ENV["AWS_SECRET_ACCESS_KEY"] = "testpass"
ENV["AWS_SESSION_TOKEN"] = nil
Gem.configuration[:s3_source] = {
- "my-bucket" => { :provider => "env" },
+ "my-bucket" => { provider: "env" },
}
url = "s3://my-bucket/gems/specs.4.8.gz"
Time.stub :now, Time.at(1_561_353_581) do
@@ -806,7 +812,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
ENV["AWS_SECRET_ACCESS_KEY"] = "testpass"
ENV["AWS_SESSION_TOKEN"] = nil
Gem.configuration[:s3_source] = {
- "my-bucket" => { :provider => "env", :region => "us-west-2" },
+ "my-bucket" => { provider: "env", region: "us-west-2" },
}
url = "s3://my-bucket/gems/specs.4.8.gz"
Time.stub :now, Time.at(1_561_353_581) do
@@ -822,7 +828,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
ENV["AWS_SECRET_ACCESS_KEY"] = "testpass"
ENV["AWS_SESSION_TOKEN"] = "testtoken"
Gem.configuration[:s3_source] = {
- "my-bucket" => { :provider => "env" },
+ "my-bucket" => { provider: "env" },
}
url = "s3://my-bucket/gems/specs.4.8.gz"
Time.stub :now, Time.at(1_561_353_581) do
@@ -842,7 +848,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def test_fetch_s3_instance_profile_creds
Gem.configuration[:s3_source] = {
- "my-bucket" => { :provider => "instance_profile" },
+ "my-bucket" => { provider: "instance_profile" },
}
url = "s3://my-bucket/gems/specs.4.8.gz"
@@ -856,7 +862,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def test_fetch_s3_instance_profile_creds_with_region
Gem.configuration[:s3_source] = {
- "my-bucket" => { :provider => "instance_profile", :region => "us-west-2" },
+ "my-bucket" => { provider: "instance_profile", region: "us-west-2" },
}
url = "s3://my-bucket/gems/specs.4.8.gz"
@@ -870,7 +876,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def test_fetch_s3_instance_profile_creds_with_token
Gem.configuration[:s3_source] = {
- "my-bucket" => { :provider => "instance_profile" },
+ "my-bucket" => { provider: "instance_profile" },
}
url = "s3://my-bucket/gems/specs.4.8.gz"
@@ -887,7 +893,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
@fetcher = fetcher
e = assert_raise Gem::RemoteFetcher::FetchError do
- fetcher.fetch_s3 URI.parse(url)
+ fetcher.fetch_s3 Gem::URI.parse(url)
end
assert_match expected_message, e.message
@@ -900,7 +906,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def test_fetch_s3_no_host
Gem.configuration[:s3_source] = {
- "my-bucket" => { :id => "testuser", :secret => "testpass" },
+ "my-bucket" => { id: "testuser", secret: "testpass" },
}
url = "s3://other-bucket/gems/specs.4.8.gz"
@@ -910,7 +916,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
end
def test_fetch_s3_no_id
- Gem.configuration[:s3_source] = { "my-bucket" => { :secret => "testpass" } }
+ Gem.configuration[:s3_source] = { "my-bucket" => { secret: "testpass" } }
url = "s3://my-bucket/gems/specs.4.8.gz"
refute_fetch_s3 url, "s3_source for my-bucket missing id or secret"
@@ -919,7 +925,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
end
def test_fetch_s3_no_secret
- Gem.configuration[:s3_source] = { "my-bucket" => { :id => "testuser" } }
+ Gem.configuration[:s3_source] = { "my-bucket" => { id: "testuser" } }
url = "s3://my-bucket/gems/specs.4.8.gz"
refute_fetch_s3 url, "s3_source for my-bucket missing id or secret"
@@ -930,7 +936,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def test_observe_no_proxy_env_single_host
use_ui @stub_ui do
ENV["http_proxy"] = @proxy_uri
- ENV["no_proxy"] = URI.parse(@server_uri).host
+ ENV["no_proxy"] = Gem::URI.parse(@server_uri).host
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
assert_data_from_server fetcher.fetch_path(@server_uri)
@@ -940,7 +946,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def test_observe_no_proxy_env_list
use_ui @stub_ui do
ENV["http_proxy"] = @proxy_uri
- ENV["no_proxy"] = "fakeurl.com, #{URI.parse(@server_uri).host}"
+ ENV["no_proxy"] = "fakeurl.com, #{Gem::URI.parse(@server_uri).host}"
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
assert_data_from_server fetcher.fetch_path(@server_uri)
@@ -952,8 +958,8 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
@fetcher = fetcher
assert_throws :block_called do
- fetcher.request URI("http://example"), Net::HTTP::Get do |req|
- assert_kind_of Net::HTTPGenericRequest, req
+ fetcher.request Gem::URI("http://example"), Gem::Net::HTTP::Get do |req|
+ assert_kind_of Gem::Net::HTTPGenericRequest, req
throw :block_called
end
end
@@ -978,7 +984,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def test_ssl_client_cert_auth_connection
ssl_server = start_ssl_server(
- { :SSLVerifyClient => OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT }
+ { SSLVerifyClient: OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT }
)
temp_ca_cert = File.join(__dir__, "ca_cert.pem")
@@ -994,7 +1000,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def test_do_not_allow_invalid_client_cert_auth_connection
ssl_server = start_ssl_server(
- { :SSLVerifyClient => OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT }
+ { SSLVerifyClient: OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT }
)
temp_ca_cert = File.join(__dir__, "ca_cert.pem")
@@ -1123,8 +1129,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
@ssl_server_thread.kill.join
@ssl_server_thread = nil
end
- utils = WEBrick::Utils # TimeoutHandler is since 1.9
- utils::TimeoutHandler.terminate if defined?(utils::TimeoutHandler.terminate)
+ WEBrick::Utils::TimeoutHandler.terminate
end
def normal_server_port
@@ -1140,15 +1145,15 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
null_logger = NilLog.new
server = WEBrick::HTTPServer.new({
- :Port => 0,
- :Logger => null_logger,
- :AccessLog => [],
- :SSLEnable => true,
- :SSLCACertificateFile => File.join(__dir__, "ca_cert.pem"),
- :SSLCertificate => cert("ssl_cert.pem"),
- :SSLPrivateKey => key("ssl_key.pem"),
- :SSLVerifyClient => nil,
- :SSLCertName => nil,
+ Port: 0,
+ Logger: null_logger,
+ AccessLog: [],
+ SSLEnable: true,
+ SSLCACertificateFile: File.join(__dir__, "ca_cert.pem"),
+ SSLCertificate: cert("ssl_cert.pem"),
+ SSLPrivateKey: key("ssl_key.pem"),
+ SSLVerifyClient: nil,
+ SSLCertName: nil,
}.merge(config))
server.mount_proc("/yaml") do |_req, res|
res.body = "--- true\n"
@@ -1180,10 +1185,10 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def start_server(data)
null_logger = NilLog.new
s = WEBrick::HTTPServer.new(
- :Port => 0,
- :DocumentRoot => nil,
- :Logger => null_logger,
- :AccessLog => null_logger
+ Port: 0,
+ DocumentRoot: nil,
+ Logger: null_logger,
+ AccessLog: null_logger
)
s.mount_proc("/kill") {|_req, _res| s.shutdown }
s.mount_proc("/yaml") do |req, res|
diff --git a/test/rubygems/test_gem_request.rb b/test/rubygems/test_gem_request.rb
index b75b25b37b..5e9b264dac 100644
--- a/test/rubygems/test_gem_request.rb
+++ b/test/rubygems/test_gem_request.rb
@@ -3,7 +3,6 @@
require_relative "helper"
require "rubygems/request"
require "ostruct"
-require "base64"
unless Gem::HAVE_OPENSSL
warn "Skipping Gem::Request tests. openssl not found."
@@ -21,6 +20,12 @@ class TestGemRequest < Gem::TestCase
Gem::Request.create_with_proxy uri, request_class, last_modified, proxy
end
+ # This method is same code as Base64.encode64
+ # We should not use Base64.encode64 because we need to avoid gem activation.
+ def base64_encode64(bin)
+ [bin].pack("m")
+ end
+
def setup
@proxies = %w[http_proxy https_proxy HTTP_PROXY http_proxy_user HTTP_PROXY_USER http_proxy_pass HTTP_PROXY_PASS no_proxy NO_PROXY]
@old_proxies = @proxies.map {|k| ENV[k] }
@@ -29,7 +34,7 @@ class TestGemRequest < Gem::TestCase
super
@proxy_uri = "http://localhost:1234"
- @uri = URI("http://example")
+ @uri = Gem::URI("http://example")
@request = make_request @uri, nil, nil, nil
end
@@ -51,7 +56,7 @@ class TestGemRequest < Gem::TestCase
def test_initialize_proxy_URI
proxy_uri = "http://proxy.example.com"
- request = make_request @uri, nil, nil, URI(proxy_uri)
+ request = make_request @uri, nil, nil, Gem::URI(proxy_uri)
assert_equal proxy_uri, request.proxy_uri.to_s
end
@@ -72,18 +77,18 @@ class TestGemRequest < Gem::TestCase
def test_initialize_proxy_ENV_https
ENV["https_proxy"] = @proxy_uri
- request = make_request URI("https://example"), nil, nil, nil
+ request = make_request Gem::URI("https://example"), nil, nil, nil
proxy = request.proxy_uri
- assert_equal URI(@proxy_uri), proxy
+ assert_equal Gem::URI(@proxy_uri), proxy
end
def test_proxy_ENV
ENV["http_proxy"] = "http://proxy"
ENV["https_proxy"] = ""
- request = make_request URI("https://example"), nil, nil, nil
+ request = make_request Gem::URI("https://example"), nil, nil, nil
proxy = request.proxy_uri
@@ -91,13 +96,13 @@ class TestGemRequest < Gem::TestCase
end
def test_configure_connection_for_https
- connection = Net::HTTP.new "localhost", 443
+ connection = Gem::Net::HTTP.new "localhost", 443
request = Class.new(Gem::Request) do
def self.get_cert_files
[TestGemRequest::PUBLIC_CERT_FILE]
end
- end.create_with_proxy URI("https://example"), nil, nil, nil
+ end.create_with_proxy Gem::URI("https://example"), nil, nil, nil
Gem::Request.configure_connection_for_https connection, request.cert_files
@@ -110,13 +115,13 @@ class TestGemRequest < Gem::TestCase
ssl_ca_cert = Gem.configuration.ssl_ca_cert
Gem.configuration.ssl_ca_cert = CA_CERT_FILE
- connection = Net::HTTP.new "localhost", 443
+ connection = Gem::Net::HTTP.new "localhost", 443
request = Class.new(Gem::Request) do
def self.get_cert_files
[TestGemRequest::PUBLIC_CERT_FILE]
end
- end.create_with_proxy URI("https://example"), nil, nil, nil
+ end.create_with_proxy Gem::URI("https://example"), nil, nil, nil
Gem::Request.configure_connection_for_https connection, request.cert_files
@@ -133,17 +138,17 @@ class TestGemRequest < Gem::TestCase
request = make_request @uri, nil, nil, nil
proxy = request.proxy_uri
- assert_equal URI(@proxy_uri), proxy
+ assert_equal Gem::URI(@proxy_uri), proxy
end
def test_get_proxy_from_env_https
ENV["https_proxy"] = @proxy_uri
- uri = URI("https://example")
+ uri = Gem::URI("https://example")
request = make_request uri, nil, nil, nil
proxy = request.proxy_uri
- assert_equal URI(@proxy_uri), proxy
+ assert_equal Gem::URI(@proxy_uri), proxy
end
def test_get_proxy_from_env_domain
@@ -186,9 +191,9 @@ class TestGemRequest < Gem::TestCase
end
def test_fetch
- uri = Gem::Uri.new(URI.parse("#{@gem_repo}/specs.#{Gem.marshal_version}"))
- response = util_stub_net_http(:body => :junk, :code => 200) do
- @request = make_request(uri, Net::HTTP::Get, nil, nil)
+ uri = Gem::Uri.new(Gem::URI.parse("#{@gem_repo}/specs.#{Gem.marshal_version}"))
+ response = util_stub_net_http(body: :junk, code: 200) do
+ @request = make_request(uri, Gem::Net::HTTP::Get, nil, nil)
@request.fetch
end
@@ -199,58 +204,58 @@ class TestGemRequest < Gem::TestCase
def test_fetch_basic_auth
Gem.configuration.verbose = :really
- uri = Gem::Uri.new(URI.parse("https://user:pass@example.rubygems/specs.#{Gem.marshal_version}"))
- conn = util_stub_net_http(:body => :junk, :code => 200) do |c|
+ uri = Gem::Uri.new(Gem::URI.parse("https://user:pass@example.rubygems/specs.#{Gem.marshal_version}"))
+ conn = util_stub_net_http(body: :junk, code: 200) do |c|
use_ui @ui do
- @request = make_request(uri, Net::HTTP::Get, nil, nil)
+ @request = make_request(uri, Gem::Net::HTTP::Get, nil, nil)
@request.fetch
end
c
end
auth_header = conn.payload["Authorization"]
- assert_equal "Basic #{Base64.encode64("user:pass")}".strip, auth_header
+ assert_equal "Basic #{base64_encode64("user:pass")}".strip, auth_header
assert_includes @ui.output, "GET https://user:REDACTED@example.rubygems/specs.#{Gem.marshal_version}"
end
def test_fetch_basic_auth_encoded
Gem.configuration.verbose = :really
- uri = Gem::Uri.new(URI.parse("https://user:%7BDEScede%7Dpass@example.rubygems/specs.#{Gem.marshal_version}"))
+ uri = Gem::Uri.new(Gem::URI.parse("https://user:%7BDEScede%7Dpass@example.rubygems/specs.#{Gem.marshal_version}"))
- conn = util_stub_net_http(:body => :junk, :code => 200) do |c|
+ conn = util_stub_net_http(body: :junk, code: 200) do |c|
use_ui @ui do
- @request = make_request(uri, Net::HTTP::Get, nil, nil)
+ @request = make_request(uri, Gem::Net::HTTP::Get, nil, nil)
@request.fetch
end
c
end
auth_header = conn.payload["Authorization"]
- assert_equal "Basic #{Base64.encode64("user:{DEScede}pass")}".strip, auth_header
+ assert_equal "Basic #{base64_encode64("user:{DEScede}pass")}".strip, auth_header
assert_includes @ui.output, "GET https://user:REDACTED@example.rubygems/specs.#{Gem.marshal_version}"
end
def test_fetch_basic_oauth_encoded
Gem.configuration.verbose = :really
- uri = Gem::Uri.new(URI.parse("https://%7BDEScede%7Dpass:x-oauth-basic@example.rubygems/specs.#{Gem.marshal_version}"))
+ uri = Gem::Uri.new(Gem::URI.parse("https://%7BDEScede%7Dpass:x-oauth-basic@example.rubygems/specs.#{Gem.marshal_version}"))
- conn = util_stub_net_http(:body => :junk, :code => 200) do |c|
+ conn = util_stub_net_http(body: :junk, code: 200) do |c|
use_ui @ui do
- @request = make_request(uri, Net::HTTP::Get, nil, nil)
+ @request = make_request(uri, Gem::Net::HTTP::Get, nil, nil)
@request.fetch
end
c
end
auth_header = conn.payload["Authorization"]
- assert_equal "Basic #{Base64.encode64("{DEScede}pass:x-oauth-basic")}".strip, auth_header
+ assert_equal "Basic #{base64_encode64("{DEScede}pass:x-oauth-basic")}".strip, auth_header
assert_includes @ui.output, "GET https://REDACTED:x-oauth-basic@example.rubygems/specs.#{Gem.marshal_version}"
end
def test_fetch_head
- uri = Gem::Uri.new(URI.parse("#{@gem_repo}/specs.#{Gem.marshal_version}"))
- response = util_stub_net_http(:body => "", :code => 200) do |_conn|
- @request = make_request(uri, Net::HTTP::Get, nil, nil)
+ uri = Gem::Uri.new(Gem::URI.parse("#{@gem_repo}/specs.#{Gem.marshal_version}"))
+ response = util_stub_net_http(body: "", code: 200) do |_conn|
+ @request = make_request(uri, Gem::Net::HTTP::Get, nil, nil)
@request.fetch
end
@@ -259,10 +264,10 @@ class TestGemRequest < Gem::TestCase
end
def test_fetch_unmodified
- uri = Gem::Uri.new(URI.parse("#{@gem_repo}/specs.#{Gem.marshal_version}"))
+ uri = Gem::Uri.new(Gem::URI.parse("#{@gem_repo}/specs.#{Gem.marshal_version}"))
t = Time.utc(2013, 1, 2, 3, 4, 5)
- conn, response = util_stub_net_http(:body => "", :code => 304) do |c|
- @request = make_request(uri, Net::HTTP::Get, t, nil)
+ conn, response = util_stub_net_http(body: "", code: 304) do |c|
+ @request = make_request(uri, Gem::Net::HTTP::Get, t, nil)
[c, @request.fetch]
end
diff --git a/test/rubygems/test_gem_request_connection_pools.rb b/test/rubygems/test_gem_request_connection_pools.rb
index a3d9b79f35..966447bff6 100644
--- a/test/rubygems/test_gem_request_connection_pools.rb
+++ b/test/rubygems/test_gem_request_connection_pools.rb
@@ -2,7 +2,7 @@
require_relative "helper"
require "rubygems/request"
-require "timeout"
+require "rubygems/vendored_timeout"
class TestGemRequestConnectionPool < Gem::TestCase
class FakeHttp
@@ -18,7 +18,7 @@ class TestGemRequestConnectionPool < Gem::TestCase
@old_client = Gem::Request::ConnectionPools.client
Gem::Request::ConnectionPools.client = FakeHttp
- @proxy = URI "http://proxy.example"
+ @proxy = Gem::URI "http://proxy.example"
end
def teardown
@@ -49,7 +49,7 @@ class TestGemRequestConnectionPool < Gem::TestCase
end
def test_checkout_same_connection
- uri = URI.parse("http://example/some_endpoint")
+ uri = Gem::URI.parse("http://example/some_endpoint")
pools = Gem::Request::ConnectionPools.new nil, []
pool = pools.pool_for uri
@@ -99,7 +99,7 @@ class TestGemRequestConnectionPool < Gem::TestCase
def test_net_http_args
pools = Gem::Request::ConnectionPools.new nil, []
- net_http_args = pools.send :net_http_args, URI("http://example"), nil
+ net_http_args = pools.send :net_http_args, Gem::URI("http://example"), nil
assert_equal ["example", 80], net_http_args
end
@@ -107,7 +107,7 @@ class TestGemRequestConnectionPool < Gem::TestCase
def test_net_http_args_ipv6
pools = Gem::Request::ConnectionPools.new nil, []
- net_http_args = pools.send :net_http_args, URI("http://[::1]"), nil
+ net_http_args = pools.send :net_http_args, Gem::URI("http://[::1]"), nil
assert_equal ["::1", 80], net_http_args
end
@@ -115,7 +115,7 @@ class TestGemRequestConnectionPool < Gem::TestCase
def test_net_http_args_proxy
pools = Gem::Request::ConnectionPools.new nil, []
- net_http_args = pools.send :net_http_args, URI("http://example"), @proxy
+ net_http_args = pools.send :net_http_args, Gem::URI("http://example"), @proxy
assert_equal ["example", 80, "proxy.example", 80, nil, nil], net_http_args
end
@@ -126,7 +126,7 @@ class TestGemRequestConnectionPool < Gem::TestCase
pools = Gem::Request::ConnectionPools.new nil, []
- net_http_args = pools.send :net_http_args, URI("http://example"), @proxy
+ net_http_args = pools.send :net_http_args, Gem::URI("http://example"), @proxy
assert_equal ["example", 80, nil, nil], net_http_args
ensure
@@ -134,15 +134,15 @@ class TestGemRequestConnectionPool < Gem::TestCase
end
def test_thread_waits_for_connection
- uri = URI.parse("http://example/some_endpoint")
+ uri = Gem::URI.parse("http://example/some_endpoint")
pools = Gem::Request::ConnectionPools.new nil, []
pool = pools.pool_for uri
pool.checkout
Thread.new do
- assert_raise(Timeout::Error) do
- Timeout.timeout(1) do
+ assert_raise(Gem::Timeout::Error) do
+ Gem::Timeout.timeout(1) do
pool.checkout
end
end
diff --git a/test/rubygems/test_gem_request_set.rb b/test/rubygems/test_gem_request_set.rb
index c8197676da..9aa244892c 100644
--- a/test/rubygems/test_gem_request_set.rb
+++ b/test/rubygems/test_gem_request_set.rb
@@ -55,7 +55,7 @@ class TestGemRequestSet < Gem::TestCase
io.puts 'gem "a"'
io.flush
- result = rs.install_from_gemdeps :gemdeps => io.path do |req, _installer|
+ result = rs.install_from_gemdeps gemdeps: io.path do |req, _installer|
installed << req.full_name
end
@@ -87,7 +87,7 @@ Gems to install:
EXPECTED
actual, _ = capture_output do
- rs.install_from_gemdeps :gemdeps => io.path, :explain => true
+ rs.install_from_gemdeps gemdeps: io.path, explain: true
end
assert_equal(expected, actual)
end
@@ -109,8 +109,8 @@ Gems to install:
end
options = {
- :gemdeps => "gem.deps.rb",
- :install_dir => "#{@gemhome}2",
+ gemdeps: "gem.deps.rb",
+ install_dir: "#{@gemhome}2",
}
rs.install_from_gemdeps options do |req, _installer|
@@ -133,7 +133,7 @@ Gems to install:
io.flush
assert_raise Gem::UnsatisfiableDependencyError do
- rs.install_from_gemdeps :gemdeps => io.path, :domain => :local
+ rs.install_from_gemdeps gemdeps: io.path, domain: :local
end
end
@@ -171,7 +171,7 @@ DEPENDENCIES
io.puts 'gem "b"'
end
- rs.install_from_gemdeps :gemdeps => "gem.deps.rb" do |req, _installer|
+ rs.install_from_gemdeps gemdeps: "gem.deps.rb" do |req, _installer|
installed << req.full_name
end
@@ -225,7 +225,7 @@ end
io.puts("gemspec")
end
- rs.install_from_gemdeps :gemdeps => "Gemfile" do |req, _installer|
+ rs.install_from_gemdeps gemdeps: "Gemfile" do |req, _installer|
installed << req.full_name
end
@@ -250,7 +250,7 @@ ruby "0"
io.flush
- rs.install_from_gemdeps :gemdeps => io.path do |req, _installer|
+ rs.install_from_gemdeps gemdeps: io.path do |req, _installer|
installed << req.full_name
end
end
@@ -574,8 +574,8 @@ ruby "0"
rs.resolve
options = {
- :development => true,
- :development_shallow => true,
+ development: true,
+ development_shallow: true,
}
installed = rs.install_into @tempdir, true, options do
diff --git a/test/rubygems/test_gem_request_set_gem_dependency_api.rb b/test/rubygems/test_gem_request_set_gem_dependency_api.rb
index 876d695c4e..af32005a16 100644
--- a/test/rubygems/test_gem_request_set_gem_dependency_api.rb
+++ b/test/rubygems/test_gem_request_set_gem_dependency_api.rb
@@ -90,7 +90,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
end
def test_gem_git
- @gda.gem "a", :git => "git/a"
+ @gda.gem "a", git: "git/a"
assert_equal [dep("a")], @set.dependencies
@@ -102,7 +102,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
end
def test_gem_bitbucket
- @gda.gem "a", :bitbucket => "example/repository"
+ @gda.gem "a", bitbucket: "example/repository"
assert_equal [dep("a")], @set.dependencies
@@ -115,7 +115,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
end
def test_gem_bitbucket_expand_path
- @gda.gem "a", :bitbucket => "example"
+ @gda.gem "a", bitbucket: "example"
assert_equal [dep("a")], @set.dependencies
@@ -129,7 +129,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
def test_gem_git_branch
_, err = capture_output do
- @gda.gem "a", :git => "git/a", :branch => "other", :tag => "v1"
+ @gda.gem "a", git: "git/a", branch: "other", tag: "v1"
end
expected = "Gem dependencies file gem.deps.rb includes git reference for both ref/branch and tag but only ref/branch is used."
assert_match expected, err
@@ -140,7 +140,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
end
def test_gem_git_gist
- @gda.gem "a", :gist => "a"
+ @gda.gem "a", gist: "a"
assert_equal [dep("a")], @set.dependencies
@@ -150,7 +150,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
def test_gem_git_ref
_, err = capture_output do
- @gda.gem "a", :git => "git/a", :ref => "abcd123", :branch => "other"
+ @gda.gem "a", git: "git/a", ref: "abcd123", branch: "other"
end
expected = "Gem dependencies file gem.deps.rb includes git reference for both ref and branch but only ref is used."
assert_match expected, err
@@ -161,7 +161,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
end
def test_gem_git_submodules
- @gda.gem "a", :git => "git/a", :submodules => true
+ @gda.gem "a", git: "git/a", submodules: true
assert_equal [dep("a")], @set.dependencies
@@ -170,7 +170,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
end
def test_gem_git_tag
- @gda.gem "a", :git => "git/a", :tag => "v1"
+ @gda.gem "a", git: "git/a", tag: "v1"
assert_equal [dep("a")], @set.dependencies
@@ -178,7 +178,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
end
def test_gem_github
- @gda.gem "a", :github => "example/repository"
+ @gda.gem "a", github: "example/repository"
assert_equal [dep("a")], @set.dependencies
@@ -191,7 +191,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
end
def test_gem_github_expand_path
- @gda.gem "a", :github => "example"
+ @gda.gem "a", github: "example"
assert_equal [dep("a")], @set.dependencies
@@ -204,7 +204,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
end
def test_gem_group
- @gda.gem "a", :group => :test
+ @gda.gem "a", group: :test
assert_equal [dep("a")], @set.dependencies
end
@@ -212,7 +212,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
def test_gem_group_without
@gda.without_groups << :test
- @gda.gem "a", :group => :test
+ @gda.gem "a", group: :test
assert_empty @set.dependencies
@@ -222,7 +222,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
end
def test_gem_groups
- @gda.gem "a", :groups => [:test, :development]
+ @gda.gem "a", groups: [:test, :development]
assert_equal [dep("a")], @set.dependencies
end
@@ -230,7 +230,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
def test_gem_path
name, version, directory = vendor_gem
- @gda.gem name, :path => directory
+ @gda.gem name, path: directory
assert_equal [dep(name)], @set.dependencies
@@ -248,7 +248,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
Gem.win_platform = false
with_engine_version "ruby", "2.0.0" do
- @gda.gem "a", :platforms => :ruby
+ @gda.gem "a", platforms: :ruby
refute_empty @set.dependencies
end
@@ -263,7 +263,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
with_engine_version "ruby", "2.0.0" do
set = Gem::RequestSet.new
gda = Gem::RequestSet::GemDependencyAPI.new set, "gem.deps.rb"
- gda.gem "a", :platforms => :ruby
+ gda.gem "a", platforms: :ruby
refute_empty set.dependencies
end
@@ -271,7 +271,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
with_engine_version "rbx", "2.0.0" do
set = Gem::RequestSet.new
gda = Gem::RequestSet::GemDependencyAPI.new set, "gem.deps.rb"
- gda.gem "a", :platforms => :ruby
+ gda.gem "a", platforms: :ruby
refute_empty set.dependencies
end
@@ -279,7 +279,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
with_engine_version "truffleruby", "2.0.0" do
set = Gem::RequestSet.new
gda = Gem::RequestSet::GemDependencyAPI.new set, "gem.deps.rb"
- gda.gem "a", :platforms => :ruby
+ gda.gem "a", platforms: :ruby
refute_empty set.dependencies
end
@@ -287,7 +287,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
with_engine_version "jruby", "1.7.6" do
set = Gem::RequestSet.new
gda = Gem::RequestSet::GemDependencyAPI.new set, "gem.deps.rb"
- gda.gem "a", :platforms => :ruby
+ gda.gem "a", platforms: :ruby
assert_empty set.dependencies
end
@@ -297,7 +297,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
with_engine_version "ruby", "2.0.0" do
set = Gem::RequestSet.new
gda = Gem::RequestSet::GemDependencyAPI.new set, "gem.deps.rb"
- gda.gem "a", :platforms => :ruby
+ gda.gem "a", platforms: :ruby
assert_empty set.dependencies
end
@@ -307,13 +307,13 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
def test_gem_platforms_engine
with_engine_version "jruby", "1.7.6" do
- @gda.gem "a", :platforms => :mri
+ @gda.gem "a", platforms: :mri
assert_empty @set.dependencies
end
with_engine_version "truffleruby", "1.2.3" do
- @gda.gem "a", :platforms => :mri
+ @gda.gem "a", platforms: :mri
assert_empty @set.dependencies
end
@@ -326,13 +326,13 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
with_engine_version "maglev", "1.0.0" do
set = Gem::RequestSet.new
gda = Gem::RequestSet::GemDependencyAPI.new set, "gem.deps.rb"
- gda.gem "a", :platforms => :ruby
+ gda.gem "a", platforms: :ruby
refute_empty set.dependencies
set = Gem::RequestSet.new
gda = Gem::RequestSet::GemDependencyAPI.new set, "gem.deps.rb"
- gda.gem "a", :platforms => :maglev
+ gda.gem "a", platforms: :maglev
refute_empty set.dependencies
end
@@ -344,13 +344,13 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
with_engine_version "truffleruby", "1.0.0" do
set = Gem::RequestSet.new
gda = Gem::RequestSet::GemDependencyAPI.new set, "gem.deps.rb"
- gda.gem "a", :platforms => :truffleruby
+ gda.gem "a", platforms: :truffleruby
refute_empty set.dependencies
set = Gem::RequestSet.new
gda = Gem::RequestSet::GemDependencyAPI.new set, "gem.deps.rb"
- gda.gem "a", :platforms => :maglev
+ gda.gem "a", platforms: :maglev
assert_empty set.dependencies
end
@@ -361,7 +361,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
Gem.win_platform = false
with_engine_version "ruby", "2.0.0" do
- @gda.gem "a", :platforms => [:mswin, :jruby]
+ @gda.gem "a", platforms: [:mswin, :jruby]
assert_empty @set.dependencies
end
@@ -374,7 +374,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
Gem.win_platform = false
with_engine_version "ruby", "2.0.0" do
- @gda.gem "a", :platforms => :jruby, :platform => :ruby
+ @gda.gem "a", platforms: :jruby, platform: :ruby
refute_empty @set.dependencies
end
@@ -384,7 +384,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
def test_gem_platforms_version
with_engine_version "ruby", "2.0.0" do
- @gda.gem "a", :platforms => :ruby_18
+ @gda.gem "a", platforms: :ruby_18
assert_empty @set.dependencies
end
@@ -392,15 +392,15 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
def test_gem_platforms_unknown
e = assert_raise ArgumentError do
- @gda.gem "a", :platforms => :unknown
+ @gda.gem "a", platforms: :unknown
end
assert_equal "unknown platform :unknown", e.message
end
def test_gem_requires
- @gda.gem "a", :require => %w[b c]
- @gda.gem "d", :require => "e"
+ @gda.gem "a", require: %w[b c]
+ @gda.gem "d", require: "e"
assert_equal [dep("a"), dep("d")], @set.dependencies
@@ -409,7 +409,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
end
def test_gem_requires_false
- @gda.gem "a", :require => false
+ @gda.gem "a", require: false
assert_equal [dep("a")], @set.dependencies
@@ -419,7 +419,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
def test_gem_requires_without_group
@gda.without_groups << :test
- @gda.gem "a", :group => :test
+ @gda.gem "a", group: :test
assert_empty @set.dependencies
@@ -447,7 +447,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
end
def test_gem_requirements_options
- @gda.gem "c", :git => "https://example/c.git"
+ @gda.gem "c", git: "https://example/c.git"
assert_equal [dep("c")], @set.dependencies
end
@@ -459,7 +459,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
gda.gem name
e = assert_raise ArgumentError do
- gda.gem name, :path => directory
+ gda.gem name, path: directory
end
assert_equal "duplicate source path: #{directory} for gem #{name}",
@@ -467,7 +467,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
gda = Gem::RequestSet::GemDependencyAPI.new @set, nil
gda.instance_variable_set :@vendor_set, @vendor_set
- gda.gem name, :path => directory
+ gda.gem name, path: directory
e = assert_raise ArgumentError do
gda.gem name
@@ -489,7 +489,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
groups = []
@gda.group :a do
- groups = @gda.send :gem_group, "a", :group => :b, :groups => [:c, :d]
+ groups = @gda.send :gem_group, "a", group: :b, groups: [:c, :d]
end
assert_equal [:a, :b, :c, :d], groups.sort_by(&:to_s)
@@ -537,7 +537,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
@gda.without_groups << :other
- @gda.gemspec :development_group => :other
+ @gda.gemspec development_group: :other
assert_equal [dep("a", "= 1"), dep("b", "= 2")], @set.dependencies
@@ -569,7 +569,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
s.add_dependency "c", 3
end
- @gda.gemspec :name => "b"
+ @gda.gemspec name: "b"
assert_equal [dep("b", "= 2"), dep("c", "= 3")], @set.dependencies
end
@@ -599,7 +599,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
s.add_dependency "b", 2
end
- @gda.gemspec :path => "other"
+ @gda.gemspec path: "other"
assert_equal [dep("a", "= 1"), dep("b", "= 2")], @set.dependencies
end
@@ -621,7 +621,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
"git://example/#{repo_name}.git"
end
- @gda.gem "a", :example => "repo"
+ @gda.gem "a", example: "repo"
assert_equal ["git://example/repo.git", nil], @git_set.repositories["a"]
end
@@ -762,19 +762,19 @@ end
def test_ruby_engine
with_engine_version "jruby", "1.7.6" do
assert @gda.ruby RUBY_VERSION,
- :engine => "jruby", :engine_version => "1.7.6"
+ engine: "jruby", engine_version: "1.7.6"
end
with_engine_version "truffleruby", "1.0.0-rc11" do
assert @gda.ruby RUBY_VERSION,
- :engine => "truffleruby", :engine_version => "1.0.0-rc11"
+ engine: "truffleruby", engine_version: "1.0.0-rc11"
end
end
def test_ruby_engine_mismatch_engine
with_engine_version "ruby", "2.0.0" do
e = assert_raise Gem::RubyVersionMismatch do
- @gda.ruby RUBY_VERSION, :engine => "jruby", :engine_version => "1.7.4"
+ @gda.ruby RUBY_VERSION, engine: "jruby", engine_version: "1.7.4"
end
assert_equal "Your Ruby engine is ruby, but your gem.deps.rb requires jruby",
@@ -785,7 +785,7 @@ end
def test_ruby_engine_mismatch_version
with_engine_version "jruby", "1.7.6" do
e = assert_raise Gem::RubyVersionMismatch do
- @gda.ruby RUBY_VERSION, :engine => "jruby", :engine_version => "1.7.4"
+ @gda.ruby RUBY_VERSION, engine: "jruby", engine_version: "1.7.4"
end
assert_equal "Your Ruby engine version is jruby 1.7.6, but your gem.deps.rb requires jruby 1.7.4",
@@ -795,7 +795,7 @@ end
def test_ruby_engine_no_engine_version
e = assert_raise ArgumentError do
- @gda.ruby RUBY_VERSION, :engine => "jruby"
+ @gda.ruby RUBY_VERSION, engine: "jruby"
end
assert_equal "You must specify engine_version along with the Ruby engine",
diff --git a/test/rubygems/test_gem_requirement.rb b/test/rubygems/test_gem_requirement.rb
index 05931d651d..de0d11ec00 100644
--- a/test/rubygems/test_gem_requirement.rb
+++ b/test/rubygems/test_gem_requirement.rb
@@ -12,6 +12,14 @@ class TestGemRequirement < Gem::TestCase
assert_equal [[">=", v(1)], ["<", v(2)]], r.requirements
end
+ def test_initialize_copy
+ r = req("= 1.2")
+ r2 = r.dup
+
+ assert_equal r.requirements, r2.requirements
+ refute_same r.requirements, r2.requirements
+ end
+
def test_equals2
r = req "= 1.2"
assert_equal r, r.dup
@@ -437,19 +445,19 @@ class TestGemRequirement < Gem::TestCase
end
def test_marshal_load_attack
- wa = Net::WriteAdapter.allocate
+ wa = Gem::Net::WriteAdapter.allocate
wa.instance_variable_set(:@socket, self.class)
wa.instance_variable_set(:@method_id, :exploit)
request_set = Gem::RequestSet.allocate
request_set.instance_variable_set(:@git_set, "id")
request_set.instance_variable_set(:@sets, wa)
- wa = Net::WriteAdapter.allocate
+ wa = Gem::Net::WriteAdapter.allocate
wa.instance_variable_set(:@socket, request_set)
wa.instance_variable_set(:@method_id, :resolve)
ent = Gem::Package::TarReader::Entry.allocate
ent.instance_variable_set(:@read, 0)
ent.instance_variable_set(:@header, "aaa")
- io = Net::BufferedIO.allocate
+ io = Gem::Net::BufferedIO.allocate
io.instance_variable_set(:@io, ent)
io.instance_variable_set(:@debug_output, wa)
reader = Gem::Package::TarReader.allocate
diff --git a/test/rubygems/test_gem_resolver.rb b/test/rubygems/test_gem_resolver.rb
index c2bdc5332c..b7dadda708 100644
--- a/test/rubygems/test_gem_resolver.rb
+++ b/test/rubygems/test_gem_resolver.rb
@@ -8,7 +8,7 @@ class TestGemResolver < Gem::TestCase
end
def set(*specs)
- source = Gem::Source.new URI @gem_repo
+ source = Gem::Source.new Gem::URI @gem_repo
specs = specs.map do |spec|
Gem::Resolver::SpecSpecification.new nil, spec, source
diff --git a/test/rubygems/test_gem_resolver_api_set.rb b/test/rubygems/test_gem_resolver_api_set.rb
index b82f00a190..5781cf37d2 100644
--- a/test/rubygems/test_gem_resolver_api_set.rb
+++ b/test/rubygems/test_gem_resolver_api_set.rb
@@ -6,40 +6,40 @@ class TestGemResolverAPISet < Gem::TestCase
def setup
super
- @dep_uri = URI "#{@gem_repo}info/"
+ @dep_uri = Gem::URI "#{@gem_repo}info/"
end
def test_initialize
set = Gem::Resolver::APISet.new
- assert_equal URI("https://index.rubygems.org/info/"), set.dep_uri
- assert_equal URI("https://index.rubygems.org/"), set.uri
- assert_equal Gem::Source.new(URI("https://index.rubygems.org")), set.source
+ assert_equal Gem::URI("https://index.rubygems.org/info/"), set.dep_uri
+ assert_equal Gem::URI("https://index.rubygems.org/"), set.uri
+ assert_equal Gem::Source.new(Gem::URI("https://index.rubygems.org")), set.source
end
def test_initialize_deeper_uri
set = Gem::Resolver::APISet.new "https://rubygemsserver.com/mygems/info"
- assert_equal URI("https://rubygemsserver.com/mygems/info"), set.dep_uri
- assert_equal URI("https://rubygemsserver.com/"), set.uri
- assert_equal Gem::Source.new(URI("https://rubygemsserver.com/")), set.source
+ assert_equal Gem::URI("https://rubygemsserver.com/mygems/info"), set.dep_uri
+ assert_equal Gem::URI("https://rubygemsserver.com/"), set.uri
+ assert_equal Gem::Source.new(Gem::URI("https://rubygemsserver.com/")), set.source
end
def test_initialize_uri
set = Gem::Resolver::APISet.new @dep_uri
- assert_equal URI("#{@gem_repo}info/"), set.dep_uri
- assert_equal URI(@gem_repo.to_s), set.uri
+ assert_equal Gem::URI("#{@gem_repo}info/"), set.dep_uri
+ assert_equal Gem::URI(@gem_repo.to_s), set.uri
end
def test_find_all
spec_fetcher
data = [
- { :name => "a",
- :number => "1",
- :platform => "ruby",
- :dependencies => [] },
+ { name: "a",
+ number: "1",
+ platform: "ruby",
+ dependencies: [] },
]
@fetcher.data["#{@dep_uri}a"] = "---\n1 "
@@ -59,14 +59,14 @@ class TestGemResolverAPISet < Gem::TestCase
spec_fetcher
data = [
- { :name => "a",
- :number => "1",
- :platform => "ruby",
- :dependencies => [] },
- { :name => "a",
- :number => "2.a",
- :platform => "ruby",
- :dependencies => [] },
+ { name: "a",
+ number: "1",
+ platform: "ruby",
+ dependencies: [] },
+ { name: "a",
+ number: "2.a",
+ platform: "ruby",
+ dependencies: [] },
]
@fetcher.data["#{@dep_uri}a"] = "---\n1\n2.a"
@@ -88,10 +88,10 @@ class TestGemResolverAPISet < Gem::TestCase
spec_fetcher
data = [
- { :name => "a",
- :number => "1",
- :platform => "ruby",
- :dependencies => [] },
+ { name: "a",
+ number: "1",
+ platform: "ruby",
+ dependencies: [] },
]
@fetcher.data["#{@dep_uri}a"] = "---\n1 "
diff --git a/test/rubygems/test_gem_resolver_api_specification.rb b/test/rubygems/test_gem_resolver_api_specification.rb
index 6ec0aa48fa..2119d73478 100644
--- a/test/rubygems/test_gem_resolver_api_specification.rb
+++ b/test/rubygems/test_gem_resolver_api_specification.rb
@@ -6,10 +6,10 @@ class TestGemResolverAPISpecification < Gem::TestCase
def test_initialize
set = Gem::Resolver::APISet.new
data = {
- :name => "rails",
- :number => "3.0.3",
- :platform => Gem::Platform.local.to_s,
- :dependencies => [
+ name: "rails",
+ number: "3.0.3",
+ platform: Gem::Platform.local.to_s,
+ dependencies: [
["bundler", "~> 1.0"],
["railties", "= 3.0.3"],
],
@@ -45,10 +45,10 @@ class TestGemResolverAPISpecification < Gem::TestCase
set = Gem::Resolver::APISet.new repo
data = {
- :name => "rails",
- :number => "3.0.3",
- :platform => "ruby",
- :dependencies => [
+ name: "rails",
+ number: "3.0.3",
+ platform: "ruby",
+ dependencies: [
["bundler", "~> 1.0"],
["railties", "= 3.0.3"],
],
@@ -72,10 +72,10 @@ class TestGemResolverAPISpecification < Gem::TestCase
def test_installable_platform_eh
set = Gem::Resolver::APISet.new
data = {
- :name => "a",
- :number => "1",
- :platform => "ruby",
- :dependencies => [],
+ name: "a",
+ number: "1",
+ platform: "ruby",
+ dependencies: [],
}
a_spec = Gem::Resolver::APISpecification.new set, data
@@ -83,10 +83,10 @@ class TestGemResolverAPISpecification < Gem::TestCase
assert a_spec.installable_platform?
data = {
- :name => "b",
- :number => "1",
- :platform => "cpu-other_platform-1",
- :dependencies => [],
+ name: "b",
+ number: "1",
+ platform: "cpu-other_platform-1",
+ dependencies: [],
}
b_spec = Gem::Resolver::APISpecification.new set, data
@@ -94,10 +94,10 @@ class TestGemResolverAPISpecification < Gem::TestCase
refute b_spec.installable_platform?
data = {
- :name => "c",
- :number => "1",
- :platform => Gem::Platform.local.to_s,
- :dependencies => [],
+ name: "c",
+ number: "1",
+ platform: Gem::Platform.local.to_s,
+ dependencies: [],
}
c_spec = Gem::Resolver::APISpecification.new set, data
@@ -108,10 +108,10 @@ class TestGemResolverAPISpecification < Gem::TestCase
def test_source
set = Gem::Resolver::APISet.new
data = {
- :name => "a",
- :number => "1",
- :platform => "ruby",
- :dependencies => [],
+ name: "a",
+ number: "1",
+ platform: "ruby",
+ dependencies: [],
}
api_spec = Gem::Resolver::APISpecification.new set, data
@@ -124,13 +124,13 @@ class TestGemResolverAPISpecification < Gem::TestCase
fetcher.spec "a", 1
end
- dep_uri = URI(@gem_repo) + "info"
+ dep_uri = Gem::URI(@gem_repo) + "info"
set = Gem::Resolver::APISet.new dep_uri
data = {
- :name => "a",
- :number => "1",
- :platform => "ruby",
- :dependencies => [],
+ name: "a",
+ number: "1",
+ platform: "ruby",
+ dependencies: [],
}
api_spec = Gem::Resolver::APISpecification.new set, data
@@ -148,13 +148,13 @@ class TestGemResolverAPISpecification < Gem::TestCase
end
end
- dep_uri = URI(@gem_repo) + "info"
+ dep_uri = Gem::URI(@gem_repo) + "info"
set = Gem::Resolver::APISet.new dep_uri
data = {
- :name => "j",
- :number => "1",
- :platform => "jruby",
- :dependencies => [],
+ name: "j",
+ number: "1",
+ platform: "jruby",
+ dependencies: [],
}
api_spec = Gem::Resolver::APISpecification.new set, data
diff --git a/test/rubygems/test_gem_resolver_best_set.rb b/test/rubygems/test_gem_resolver_best_set.rb
index 80aa883364..8a750cdf8f 100644
--- a/test/rubygems/test_gem_resolver_best_set.rb
+++ b/test/rubygems/test_gem_resolver_best_set.rb
@@ -34,7 +34,7 @@ class TestGemResolverBestSet < Gem::TestCase
set = Gem::Resolver::BestSet.new
- api_uri = URI(@gem_repo)
+ api_uri = Gem::URI(@gem_repo)
set.sets << Gem::Resolver::APISet.new(api_uri)
@@ -94,7 +94,7 @@ class TestGemResolverBestSet < Gem::TestCase
def test_replace_failed_api_set
set = Gem::Resolver::BestSet.new
- api_uri = URI(@gem_repo) + "./info/"
+ api_uri = Gem::URI(@gem_repo) + "./info/"
api_set = Gem::Resolver::APISet.new api_uri
set.sets << api_set
@@ -131,7 +131,7 @@ class TestGemResolverBestSet < Gem::TestCase
def test_replace_failed_api_set_uri_with_credentials
set = Gem::Resolver::BestSet.new
- api_uri = URI(@gem_repo) + "./info/"
+ api_uri = Gem::URI(@gem_repo) + "./info/"
api_uri.user = "user"
api_uri.password = "pass"
api_set = Gem::Resolver::APISet.new api_uri
diff --git a/test/rubygems/test_gem_resolver_specification.rb b/test/rubygems/test_gem_resolver_specification.rb
index db7093e423..e2bbce0c0c 100644
--- a/test/rubygems/test_gem_resolver_specification.rb
+++ b/test/rubygems/test_gem_resolver_specification.rb
@@ -25,7 +25,7 @@ class TestGemResolverSpecification < Gem::TestCase
a_spec = TestSpec.new a
a_spec.source = Gem::Source.new @gem_repo
- a_spec.install :install_dir => gemhome
+ a_spec.install install_dir: gemhome
assert_path_exist File.join gemhome, "gems", a.full_name
diff --git a/test/rubygems/test_gem_safe_marshal.rb b/test/rubygems/test_gem_safe_marshal.rb
new file mode 100644
index 0000000000..ebb000a9ef
--- /dev/null
+++ b/test/rubygems/test_gem_safe_marshal.rb
@@ -0,0 +1,404 @@
+# frozen_string_literal: true
+
+require_relative "helper"
+
+require "date"
+require "rubygems/safe_marshal"
+
+class TestGemSafeMarshal < Gem::TestCase
+ define_method("test_safe_load_marshal Date #<Date: 1994-12-09 ((2449696j,0s,0n),+0s,2299161j)>") { assert_safe_load_marshal "\x04\bU:\tDate[\vi\x00i\x03 a%i\x00i\x00i\x00f\f2299161" }
+ define_method("test_safe_load_marshal Float 0.0") { assert_safe_load_marshal "\x04\bf\x060" }
+ define_method("test_safe_load_marshal Float -0.0") { assert_safe_load_marshal "\x04\bf\a-0" }
+ define_method("test_safe_load_marshal Float Infinity") { assert_safe_load_marshal "\x04\bf\binf" }
+ define_method("test_safe_load_marshal Float -Infinity") { assert_safe_load_marshal "\x04\bf\t-inf" }
+ define_method("test_safe_load_marshal Float NaN") { assert_safe_load_marshal "\x04\bf\bnan", equality: false }
+ define_method("test_safe_load_marshal Float 1.1") { assert_safe_load_marshal "\x04\bf\b1.1" }
+ define_method("test_safe_load_marshal Float -1.1") { assert_safe_load_marshal "\x04\bf\t-1.1" }
+ define_method("test_safe_load_marshal Float 30000000.0") { assert_safe_load_marshal "\x04\bf\b3e7" }
+ define_method("test_safe_load_marshal Float -30000000.0") { assert_safe_load_marshal "\x04\bf\t-3e7" }
+ define_method("test_safe_load_marshal Gem::Version #<Gem::Version \"1.abc\">") { assert_safe_load_marshal "\x04\bU:\x11Gem::Version[\x06I\"\n1.abc\x06:\x06ET" }
+ define_method("test_safe_load_marshal Hash {} default value") { assert_safe_load_marshal "\x04\b}\x00[\x00", additional_methods: [:default] }
+ define_method("test_safe_load_marshal Hash {}") { assert_safe_load_marshal "\x04\b{\x00" }
+ define_method("test_safe_load_marshal Array {}") { assert_safe_load_marshal "\x04\b[\x00" }
+ define_method("test_safe_load_marshal Hash {:runtime=>:development}") { assert_safe_load_marshal "\x04\bI{\x06:\fruntime:\x10development\x06:\n@type[\x00", permitted_ivars: { "Hash" => %w[@type] } }
+ define_method("test_safe_load_marshal Integer -1") { assert_safe_load_marshal "\x04\bi\xFA" }
+ define_method("test_safe_load_marshal Integer -1048575") { assert_safe_load_marshal "\x04\bi\xFD\x01\x00\xF0" }
+ define_method("test_safe_load_marshal Integer -122") { assert_safe_load_marshal "\x04\bi\x81" }
+ define_method("test_safe_load_marshal Integer -123") { assert_safe_load_marshal "\x04\bi\x80" }
+ define_method("test_safe_load_marshal Integer -124") { assert_safe_load_marshal "\x04\bi\xFF\x84" }
+ define_method("test_safe_load_marshal Integer -127") { assert_safe_load_marshal "\x04\bi\xFF\x81" }
+ define_method("test_safe_load_marshal Integer -128") { assert_safe_load_marshal "\x04\bi\xFF\x80" }
+ define_method("test_safe_load_marshal Integer -2") { assert_safe_load_marshal "\x04\bi\xF9" }
+ define_method("test_safe_load_marshal Integer -255") { assert_safe_load_marshal "\x04\bi\xFF\x01" }
+ define_method("test_safe_load_marshal Integer -256") { assert_safe_load_marshal "\x04\bi\xFF\x00" }
+ define_method("test_safe_load_marshal Integer -257") { assert_safe_load_marshal "\x04\bi\xFE\xFF\xFE" }
+ define_method("test_safe_load_marshal Integer -268435455") { assert_safe_load_marshal "\x04\bi\xFC\x01\x00\x00\xF0" }
+ define_method("test_safe_load_marshal Integer -268435456") { assert_safe_load_marshal "\x04\bi\xFC\x00\x00\x00\xF0" }
+ define_method("test_safe_load_marshal Integer -3") { assert_safe_load_marshal "\x04\bi\xF8" }
+ define_method("test_safe_load_marshal Integer -4") { assert_safe_load_marshal "\x04\bi\xF7" }
+ define_method("test_safe_load_marshal Integer -4294967295") { assert_safe_load_marshal "\x04\bl-\a\xFF\xFF\xFF\xFF" }
+ define_method("test_safe_load_marshal Integer -4294967296") { assert_safe_load_marshal "\x04\bl-\b\x00\x00\x00\x00\x01\x00" }
+ define_method("test_safe_load_marshal Integer -5") { assert_safe_load_marshal "\x04\bi\xF6" }
+ define_method("test_safe_load_marshal Integer -6") { assert_safe_load_marshal "\x04\bi\xF5" }
+ define_method("test_safe_load_marshal Integer -65535") { assert_safe_load_marshal "\x04\bi\xFE\x01\x00" }
+ define_method("test_safe_load_marshal Integer -65536") { assert_safe_load_marshal "\x04\bi\xFE\x00\x00" }
+ define_method("test_safe_load_marshal Integer -9223372036854775807") { assert_safe_load_marshal "\x04\bl-\t\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x7F" }
+ define_method("test_safe_load_marshal Integer -9223372036854775808") { assert_safe_load_marshal "\x04\bl-\t\x00\x00\x00\x00\x00\x00\x00\x80" }
+ define_method("test_safe_load_marshal Integer 0") { assert_safe_load_marshal "\x04\bi\x00" }
+ define_method("test_safe_load_marshal Integer 1") { assert_safe_load_marshal "\x04\bi\x06" }
+ define_method("test_safe_load_marshal Integer 1048574") { assert_safe_load_marshal "\x04\bi\x03\xFE\xFF\x0F" }
+ define_method("test_safe_load_marshal Integer 1048575") { assert_safe_load_marshal "\x04\bi\x03\xFF\xFF\x0F" }
+ define_method("test_safe_load_marshal Integer 1048576") { assert_safe_load_marshal "\x04\bi\x03\x00\x00\x10" }
+ define_method("test_safe_load_marshal Integer 121") { assert_safe_load_marshal "\x04\bi~" }
+ define_method("test_safe_load_marshal Integer 122") { assert_safe_load_marshal "\x04\bi\x7F" }
+ define_method("test_safe_load_marshal Integer 123") { assert_safe_load_marshal "\x04\bi\x01{" }
+ define_method("test_safe_load_marshal Integer 124") { assert_safe_load_marshal "\x04\bi\x01|" }
+ define_method("test_safe_load_marshal Integer 125") { assert_safe_load_marshal "\x04\bi\x01}" }
+ define_method("test_safe_load_marshal Integer 126") { assert_safe_load_marshal "\x04\bi\x01~" }
+ define_method("test_safe_load_marshal Integer 127") { assert_safe_load_marshal "\x04\bi\x01\x7F" }
+ define_method("test_safe_load_marshal Integer 128") { assert_safe_load_marshal "\x04\bi\x01\x80" }
+ define_method("test_safe_load_marshal Integer 129") { assert_safe_load_marshal "\x04\bi\x01\x81" }
+ define_method("test_safe_load_marshal Integer 2") { assert_safe_load_marshal "\x04\bi\a" }
+ define_method("test_safe_load_marshal Integer 254") { assert_safe_load_marshal "\x04\bi\x01\xFE" }
+ define_method("test_safe_load_marshal Integer 255") { assert_safe_load_marshal "\x04\bi\x01\xFF" }
+ define_method("test_safe_load_marshal Integer 256") { assert_safe_load_marshal "\x04\bi\x02\x00\x01" }
+ define_method("test_safe_load_marshal Integer 257") { assert_safe_load_marshal "\x04\bi\x02\x01\x01" }
+ define_method("test_safe_load_marshal Integer 258") { assert_safe_load_marshal "\x04\bi\x02\x02\x01" }
+ define_method("test_safe_load_marshal Integer 268435454") { assert_safe_load_marshal "\x04\bi\x04\xFE\xFF\xFF\x0F" }
+ define_method("test_safe_load_marshal Integer 268435455") { assert_safe_load_marshal "\x04\bi\x04\xFF\xFF\xFF\x0F" }
+ define_method("test_safe_load_marshal Integer 268435456") { assert_safe_load_marshal "\x04\bi\x04\x00\x00\x00\x10" }
+ define_method("test_safe_load_marshal Integer 268435457") { assert_safe_load_marshal "\x04\bi\x04\x01\x00\x00\x10" }
+ define_method("test_safe_load_marshal Integer 3") { assert_safe_load_marshal "\x04\bi\b" }
+ define_method("test_safe_load_marshal Integer 4") { assert_safe_load_marshal "\x04\bi\t" }
+ define_method("test_safe_load_marshal Integer 4294967294") { assert_safe_load_marshal "\x04\bl+\a\xFE\xFF\xFF\xFF" }
+ define_method("test_safe_load_marshal Integer 4294967295") { assert_safe_load_marshal "\x04\bl+\a\xFF\xFF\xFF\xFF" }
+ define_method("test_safe_load_marshal Integer 4294967296") { assert_safe_load_marshal "\x04\bl+\b\x00\x00\x00\x00\x01\x00" }
+ define_method("test_safe_load_marshal Integer 4294967297") { assert_safe_load_marshal "\x04\bl+\b\x01\x00\x00\x00\x01\x00" }
+ define_method("test_safe_load_marshal Integer 5") { assert_safe_load_marshal "\x04\bi\n" }
+ define_method("test_safe_load_marshal Integer 6") { assert_safe_load_marshal "\x04\bi\v" }
+ define_method("test_safe_load_marshal Integer 65534") { assert_safe_load_marshal "\x04\bi\x02\xFE\xFF" }
+ define_method("test_safe_load_marshal Integer 65535") { assert_safe_load_marshal "\x04\bi\x02\xFF\xFF" }
+ define_method("test_safe_load_marshal Integer 65536") { assert_safe_load_marshal "\x04\bi\x03\x00\x00\x01" }
+ define_method("test_safe_load_marshal Integer 65537") { assert_safe_load_marshal "\x04\bi\x03\x01\x00\x01" }
+ define_method("test_safe_load_marshal Integer 7") { assert_safe_load_marshal "\x04\bi\f" }
+ define_method("test_safe_load_marshal Integer 9223372036854775806") { assert_safe_load_marshal "\x04\bl+\t\xFE\xFF\xFF\xFF\xFF\xFF\xFF\x7F" }
+ define_method("test_safe_load_marshal Integer 9223372036854775807") { assert_safe_load_marshal "\x04\bl+\t\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x7F" }
+ define_method("test_safe_load_marshal Integer 9223372036854775808") { assert_safe_load_marshal "\x04\bl+\t\x00\x00\x00\x00\x00\x00\x00\x80" }
+ define_method("test_safe_load_marshal Integer 9223372036854775809") { assert_safe_load_marshal "\x04\bl+\t\x01\x00\x00\x00\x00\x00\x00\x80" }
+ define_method("test_safe_load_marshal Rational (1/3)") { assert_safe_load_marshal "\x04\bU:\rRational[\ai\x06i\b" }
+ define_method("test_safe_load_marshal Array [[...]]") { assert_safe_load_marshal "\x04\b[\x06@\x00" }
+ define_method("test_safe_load_marshal String \"hello\" ivar") { assert_safe_load_marshal "\x04\bI\"\nhello\a:\x06ET:\n@type@\x00", additional_methods: [:instance_variables], permitted_ivars: { "String" => %w[@type E] } }
+ define_method("test_safe_load_marshal Array [\"hello\", [\"hello\"], \"hello\", [\"hello\"]]") { assert_safe_load_marshal "\x04\b[\tI\"\nhello\x06:\x06ET[\x06@\x06@\x06@\a" }
+ define_method("test_safe_load_marshal Array [\"hello\", \"hello\"]") { assert_safe_load_marshal "\x04\b[\aI\"\nhello\x06:\x06ET@\x06" }
+ define_method("test_safe_load_marshal Array [:development, :development]") { assert_safe_load_marshal "\x04\b[\a:\x10development;\x00" }
+ define_method("test_safe_load_marshal String \"abc\" ascii") { assert_safe_load_marshal "\x04\bI\"\babc\x06:\x06EF", additional_methods: [:encoding] }
+ define_method("test_safe_load_marshal Array [\"abc\", \"abc\"] ascii") { assert_safe_load_marshal "\x04\b[\aI\"\babc\x06:\x06EF@\x06", additional_methods: [->(x) { x.map(&:encoding) }] }
+ define_method("test_safe_load_marshal String \"abc\" utf8") { assert_safe_load_marshal "\x04\bI\"\babc\x06:\x06ET", additional_methods: [:encoding] }
+ define_method("test_safe_load_marshal Array [\"abc\", \"abc\"] utf8") { assert_safe_load_marshal "\x04\b[\aI\"\babc\x06:\x06ET@\x06", additional_methods: [->(x) { x.map(&:encoding) }] }
+ define_method("test_safe_load_marshal String \"abc\" Windows-1256") { assert_safe_load_marshal "\x04\bI\"\babc\x06:\rencoding\"\x11Windows-1256", additional_methods: [:encoding] }
+ define_method("test_safe_load_marshal Array [\"abc\", \"abc\"] Windows-1256") { assert_safe_load_marshal "\x04\b[\aI\"\babc\x06:\rencoding\"\x11Windows-1256@\x06", additional_methods: [->(x) { x.map(&:encoding) }] }
+ define_method("test_safe_load_marshal String \"abc\" binary") { assert_safe_load_marshal "\x04\b\"\babc", additional_methods: [:encoding] }
+ define_method("test_safe_load_marshal Array [\"abc\", \"abc\"] binary") { assert_safe_load_marshal "\x04\b[\a\"\babc@\x06", additional_methods: [->(x) { x.map(&:encoding) }] }
+ define_method("test_safe_load_marshal String \"\\x61\\x62\\x63\" utf32") { assert_safe_load_marshal "\x04\bI\"\babc\x06:\rencoding\"\vUTF-32", additional_methods: [:encoding] }
+ define_method("test_safe_load_marshal Array [\"\\x61\\x62\\x63\", \"\\x61\\x62\\x63\"] utf32") { assert_safe_load_marshal "\x04\b[\aI\"\babc\x06:\rencoding\"\vUTF-32@\x06", additional_methods: [->(x) { x.map(&:encoding) }] }
+ define_method("test_safe_load_marshal String \"abc\" ivar") { assert_safe_load_marshal "\x04\bI\"\babc\a:\x06ET:\n@typeI\"\ttype\x06;\x00T", permitted_ivars: { "String" => %w[@type E] } }
+ define_method("test_safe_load_marshal String \"\"") { assert_safe_load_marshal "\x04\bI\"\babc\x06:\x06ET" }
+ define_method("test_safe_load_marshal Time 2000-12-31 20:07:59 -1152") { assert_safe_load_marshal "\x04\bIu:\tTime\r'@\x19\x80\x00\x00\xB0\xEF\a:\voffseti\xFE Y:\tzone0", additional_methods: [:ctime, :to_f, :to_r, :to_i, :zone, :subsec, :instance_variables, :dst?, :to_a] }
+ define_method("test_safe_load_marshal Time 2000-12-31 23:59:59 -0800") { assert_safe_load_marshal "\x04\bIu:\tTime\r'@\x19\x80\x00\x00\xB0\xEF\a:\voffseti\xFE\x80\x8F:\tzoneI\"\bPST\x06:\x06EF", additional_methods: [:ctime, :to_f, :to_r, :to_i, :zone, :subsec, :instance_variables, :dst?, :to_a] }
+ define_method("test_safe_load_marshal Time 2000-12-31 23:59:59 2254051613498933/2251799813685248000000000 -0800") { assert_safe_load_marshal "\x04\bIu:\tTime\r'@\x19\x80\x00\x00\xB0\xEF\n:\rnano_numl+\t5^\xBAI\f\x02\b\x00:\rnano_denl+\t\x00\x00\x00\x00\x00\x00\b\x00:\rsubmicro\"\a\x00\x10:\voffseti\xFE\x80\x8F:\tzoneI\"\bPST\x06:\x06EF", additional_methods: [:ctime, :to_f, :to_r, :to_i, :zone, :subsec, :instance_variables, :dst?, :to_a] }
+ define_method("test_safe_load_marshal Time 2000-12-31 23:59:59 2476979795053773/2251799813685248000 -0800") { assert_safe_load_marshal "\x04\bIu:\tTime\r'@\x19\x80L\x04\xB0\xEF\t:\rnano_numi\x025\f:\rnano_denl+\b\x00\x00\x00\x00\x00 :\voffseti\xFE\x80\x8F:\tzoneI\"\bPST\x06:\x06EF", additional_methods: [:ctime, :to_f, :to_r, :to_i, :zone, :subsec, :instance_variables, :dst?, :to_a] }
+ define_method("test_safe_load_marshal Time 2000-12-31 23:59:59 2476979795053773/2251799813685248000000 -0800") { assert_safe_load_marshal "\x04\bIu:\tTime\r'@\x19\x80\x01\x00\xB0\xEF\n:\rnano_numl+\t\x19\x00\x00\x00\x00\x00d\x00:\rnano_denl+\t\x00\x00\x00\x00\x00\x00\x01\x00:\rsubmicro\"\x06\x10:\voffseti\xFE\x80\x8F:\tzoneI\"\bPST\x06:\x06EF", additional_methods: [:ctime, :to_f, :to_r, :to_i, :zone, :subsec, :instance_variables, :dst?, :to_a] }
+ define_method("test_safe_load_marshal Time 2000-12-31 23:59:59 2476979795053773/2251799813685248000000000 -0800") { assert_safe_load_marshal "\x04\bIu:\tTime\r'@\x19\x80\x00\x00\xB0\xEF\n:\rnano_numl+\t\xCD\xCC\xCC\xCC\xCC\xCC\b\x00:\rnano_denl+\t\x00\x00\x00\x00\x00\x00\b\x00:\rsubmicro\"\a\x00\x10:\voffseti\xFE\x80\x8F:\tzoneI\"\bPST\x06:\x06EF", additional_methods: [:ctime, :to_f, :to_r, :to_i, :zone, :subsec, :instance_variables, :dst?, :to_a] }
+ define_method("test_safe_load_marshal Time 2000-12-31 23:59:59 450364466336677/450359962737049600000000 -0800") { assert_safe_load_marshal "\x04\bIu:\tTime\r'@\x19\x80\x00\x00\xB0\xEF\n:\rnano_numl+\t9b->\x05\x00\b\x00:\rnano_denl+\t\x00\x00\x00\x00\x00\x00\b\x00:\rsubmicro\"\a\x00\x10:\voffseti\xFE\x80\x8F:\tzoneI\"\bPST\x06:\x06EF", additional_methods: [:ctime, :to_f, :to_r, :to_i, :zone, :subsec, :instance_variables, :dst?, :to_a] }
+ define_method("test_safe_load_marshal Time 2000-12-31 23:59:59 4548635623644201/4503599627370496000 -0800") { assert_safe_load_marshal "\x04\bIu:\tTime\r'@\x19\x80\xF2\x03\xB0\xEF\t:\rnano_numi\x02q\x02:\rnano_denl+\b\x00\x00\x00\x00\x00@:\voffseti\xFE\x80\x8F:\tzoneI\"\bPST\x06:\x06EF", additional_methods: [:ctime, :to_f, :to_r, :to_i, :zone, :subsec, :instance_variables, :dst?, :to_a] }
+ define_method("test_safe_load_marshal Time 2000-12-31 23:59:59 4548635623644201/4503599627370496000000 -0800") { assert_safe_load_marshal "\x04\bIu:\tTime\r'@\x19\x80\x01\x00\xB0\xEF\n:\rnano_numl+\t\x05\x00\x00\x00\x00\x00\x14\x00:\rnano_denl+\t\x00\x00\x00\x00\x00\x00\x02\x00:\rsubmicro\"\x06\x01:\voffseti\xFE\x80\x8F:\tzoneI\"\bPST\x06:\x06EF", additional_methods: [:ctime, :to_f, :to_r, :to_i, :zone, :subsec, :instance_variables, :dst?, :to_a] }
+ define_method("test_safe_load_marshal Time 2000-12-31 23:59:59 4548635623644201/4503599627370496000000000 -0800") { assert_safe_load_marshal "\x04\bIu:\tTime\r'@\x19\x80\x00\x00\xB0\xEF\n:\rnano_numl+\t)\\\x8F\xC2\xF5(\x10\x00:\rnano_denl+\t\x00\x00\x00\x00\x00\x00\x10\x00:\rsubmicro\"\a\x00\x10:\voffseti\xFE\x80\x8F:\tzoneI\"\bPST\x06:\x06EF", additional_methods: [:ctime, :to_f, :to_r, :to_i, :zone, :subsec, :instance_variables, :dst?, :to_a] }
+ define_method("test_safe_load_marshal Time 2000-12-31 23:59:59.000000001 -0800") { assert_safe_load_marshal "\x04\bIu:\tTime\r'@\x19\x80\x00\x00\xB0\xEF\n:\rnano_numi\x06:\rnano_deni\x06:\rsubmicro\"\a\x00\x10:\voffseti\xFE\x80\x8F:\tzoneI\"\bPST\x06:\x06EF", additional_methods: [:ctime, :to_f, :to_r, :to_i, :zone, :subsec, :instance_variables, :dst?, :to_a] }
+ define_method("test_safe_load_marshal Time 2000-12-31 23:59:59.000001 -0800") { assert_safe_load_marshal "\x04\bIu:\tTime\r'@\x19\x80\x01\x00\xB0\xEF\a:\voffseti\xFE\x80\x8F:\tzoneI\"\bPST\x06:\x06EF", additional_methods: [:ctime, :to_f, :to_r, :to_i, :zone, :subsec, :instance_variables, :dst?, :to_a] }
+ define_method("test_safe_load_marshal Time 2000-12-31 23:59:59.001 -0800") { assert_safe_load_marshal "\x04\bIu:\tTime\r'@\x19\x80\xE8\x03\xB0\xEF\a:\voffseti\xFE\x80\x8F:\tzoneI\"\bPST\x06:\x06EF", additional_methods: [:ctime, :to_f, :to_r, :to_i, :zone, :subsec, :instance_variables, :dst?, :to_a] }
+ define_method("test_safe_load_marshal Time 2001-01-01 07:59:59 +0000") { assert_safe_load_marshal "\x04\bIu:\tTime\r'@\x19\x80\x00\x00\xB0\xEF\a:\voffseti\x00:\tzone0", additional_methods: [:ctime, :to_f, :to_r, :to_i, :zone, :subsec, :instance_variables, :dst?, :to_a] }
+ define_method("test_safe_load_marshal Time 2001-01-01 07:59:59 UTC") { assert_safe_load_marshal "\x04\bIu:\tTime\r'@\x19\xC0\x00\x00\xB0\xEF\x06:\tzoneI\"\bUTC\x06:\x06EF", additional_methods: [:ctime, :to_f, :to_r, :to_i, :zone, :subsec, :instance_variables, :dst?, :to_a] }
+ define_method("test_safe_load_marshal Time 2001-01-01 11:59:59 +0400") { assert_safe_load_marshal "\x04\bIu:\tTime\r'@\x19\x80\x00\x00\xB0\xEF\a:\voffseti\x02@8:\tzone0", additional_methods: [:ctime, :to_f, :to_r, :to_i, :zone, :subsec, :instance_variables, :dst?, :to_a] }
+ define_method("test_safe_load_marshal Time 2023-08-24 10:10:39.09565 -0700") { assert_safe_load_marshal "\x04\bIu:\tTime\r\x11\xDF\x1E\x80\xA2uq*\a:\voffseti\xFE\x90\x9D:\tzoneI\"\bPDT\x06:\x06EF" }
+ define_method("test_safe_load_marshal Time 2023-08-24 10:10:39.098453 -0700") { assert_safe_load_marshal "\x04\bIu:\tTime\r\x11\xDF\x1E\x80\x95\x80q*\b:\n@typeI\"\fruntime\x06:\x06ET:\voffseti\xFE\x90\x9D:\tzoneI\"\bPDT\x06;\aF", permitted_ivars: { "Time" => %w[@type offset zone], "String" => %w[E @debug_created_info] }, marshal_dump_equality: true }
+
+ def test_repeated_symbol
+ assert_safe_load_as [:development, :development]
+ end
+
+ def test_length_one_symbols
+ with_const(Gem::SafeMarshal, :PERMITTED_SYMBOLS, %w[E A b 0] << "") do
+ assert_safe_load_as [:A, :E, :E, :A, "".to_sym, "".to_sym], additional_methods: [:instance_variables]
+ end
+ end
+
+ def test_repeated_string
+ s = "hello"
+ a = [s]
+ assert_safe_load_as [s, a, s, a]
+ assert_safe_load_as [s, s]
+ end
+
+ def test_recursive_string
+ s = String.new("hello")
+ s.instance_variable_set(:@type, s)
+ with_const(Gem::SafeMarshal, :PERMITTED_IVARS, { "String" => %w[@type E] }) do
+ assert_safe_load_as s, additional_methods: [:instance_variables]
+ end
+ end
+
+ def test_recursive_array
+ a = []
+ a << a
+ assert_safe_load_as a
+ end
+
+ def test_time_loads
+ assert_safe_load_as Time.new
+ end
+
+ def test_string_with_encoding
+ [
+ String.new("abc", encoding: "US-ASCII"),
+ String.new("abc", encoding: "UTF-8"),
+ String.new("abc", encoding: "Windows-1256"),
+ String.new("abc", encoding: Encoding::BINARY),
+ String.new("abc", encoding: "UTF-32"),
+
+ String.new("", encoding: "US-ASCII"),
+ String.new("", encoding: "UTF-8"),
+ String.new("", encoding: "Windows-1256"),
+ String.new("", encoding: Encoding::BINARY),
+ String.new("", encoding: "UTF-32"),
+ ].each do |s|
+ assert_safe_load_as s, additional_methods: [:encoding]
+ assert_safe_load_as [s, s], additional_methods: [->(a) { a.map(&:encoding) }]
+ end
+ end
+
+ def test_string_with_ivar
+ str = String.new("abc")
+ str.instance_variable_set :@type, "type"
+ with_const(Gem::SafeMarshal, :PERMITTED_IVARS, { "String" => %w[@type E @debug_created_info] }) do
+ assert_safe_load_as str
+ end
+ end
+
+ def test_time_with_ivar
+ pend "Marshal.load of Time with ivars is broken on jruby, see https://github.com/jruby/jruby/issues/7902" if RUBY_ENGINE == "jruby"
+
+ with_const(Gem::SafeMarshal, :PERMITTED_IVARS, { "Time" => %w[@type offset zone nano_num nano_den submicro], "String" => %w[E @debug_created_info] }) do
+ assert_safe_load_as Time.new.tap {|t| t.instance_variable_set :@type, "runtime" }, marshal_dump_equality: true
+ end
+ end
+
+ secs = Time.new(2000, 12, 31, 23, 59, 59).to_i
+ [
+ Time.at(secs),
+ Time.at(secs, in: "+04:00"),
+ Time.at(secs, in: "-11:52"),
+ Time.at(secs, in: "+00:00"),
+ Time.at(secs, in: "-00:00"),
+ Time.at(secs, 1, :millisecond),
+ Time.at(secs, 1.1, :millisecond),
+ Time.at(secs, 1.01, :millisecond),
+ Time.at(secs, 1, :microsecond),
+ Time.at(secs, 1.1, :microsecond),
+ Time.at(secs, 1.01, :microsecond),
+ Time.at(secs, 1, :nanosecond),
+ Time.at(secs, 1.1, :nanosecond),
+ Time.at(secs, 1.01, :nanosecond),
+ Time.at(secs, 1.001, :nanosecond),
+ Time.at(secs, 1.00001, :nanosecond),
+ Time.at(secs, 1.00001, :nanosecond),
+ Time.at(secs, in: "UTC"),
+ Time.at(secs, in: "Z"),
+ ].each_with_index do |t, i|
+ define_method("test_time_#{i} #{t.inspect}") do
+ additional_methods = [:ctime, :to_f, :to_r, :to_i, :zone, :subsec, :instance_variables, :dst?, :to_a]
+ assert_safe_load_as t, additional_methods: additional_methods
+ end
+ end
+
+ def test_floats
+ [0.0, Float::INFINITY, Float::NAN, 1.1, 3e7].each do |f|
+ assert_safe_load_as f
+ assert_safe_load_as(-f)
+ end
+ end
+
+ def test_hash_with_ivar
+ h = { runtime: :development }
+ h.instance_variable_set :@type, []
+ with_const(Gem::SafeMarshal, :PERMITTED_IVARS, { "Hash" => %w[@type] }) do
+ assert_safe_load_as(h)
+ end
+ end
+
+ def test_hash_with_default_value
+ assert_safe_load_as Hash.new([])
+ end
+
+ def test_hash_with_compare_by_identity
+ with_const(Gem::SafeMarshal, :PERMITTED_CLASSES, %w[Hash]) do
+ assert_safe_load_as Hash.new.compare_by_identity.tap {|h|
+ h[+"a"] = 1
+ h[+"a"] = 2 }, additional_methods: [:compare_by_identity?], equality: false
+ assert_safe_load_as Hash.new.compare_by_identity, additional_methods: [:compare_by_identity?]
+ assert_safe_load_as Hash.new(0).compare_by_identity.tap {|h|
+ h[+"a"] = 1
+ h[+"a"] = 2 }, additional_methods: [:compare_by_identity?, :default], equality: false
+ end
+ end
+
+ class StringSubclass < ::String
+ end
+
+ def test_string_subclass
+ with_const(Gem::SafeMarshal, :PERMITTED_CLASSES, [StringSubclass.name]) do
+ with_const(Gem::SafeMarshal, :PERMITTED_IVARS, { StringSubclass.name => %w[E] }) do
+ e = assert_raise(Gem::SafeMarshal::Visitors::ToRuby::UnsupportedError) do
+ Gem::SafeMarshal.safe_load Marshal.dump StringSubclass.new("abc")
+ end
+ assert_equal "Unsupported user class #{StringSubclass.name} in marshal stream @ root.object", e.message
+ end
+ end
+ end
+
+ class ArraySubclass < ::Array
+ end
+
+ def test_array_subclass
+ with_const(Gem::SafeMarshal, :PERMITTED_CLASSES, [ArraySubclass.name]) do
+ e = assert_raise(Gem::SafeMarshal::Visitors::ToRuby::UnsupportedError) do
+ Gem::SafeMarshal.safe_load(Marshal.dump(ArraySubclass.new << "abc"))
+ end
+ assert_equal "Unsupported user class #{ArraySubclass.name} in marshal stream @ root", e.message
+ end
+ end
+
+ def test_frozen_object
+ assert_safe_load_as Gem::Version.new("1.abc").freeze
+ end
+
+ def test_date
+ assert_safe_load_as Date.new(1994, 12, 9)
+ end
+
+ def test_rational
+ assert_safe_load_as Rational(1, 3)
+ end
+
+ [
+ 0, 1, 2, 3, 4, 5, 6, 122, 123, 124, 127, 128, 255, 256, 257,
+ 2**16, 2**16 - 1, 2**20 - 1,
+ 2**28, 2**28 - 1,
+ 2**32, 2**32 - 1,
+ 2**63, 2**63 - 1
+ ].
+ each do |i|
+ define_method("test_int_ #{i}") do
+ assert_safe_load_as i
+ assert_safe_load_as(-i)
+ assert_safe_load_as(i + 1)
+ assert_safe_load_as(i - 1)
+ end
+ end
+
+ def test_gem_spec_disallowed_symbol
+ e = assert_raise(Gem::SafeMarshal::Visitors::ToRuby::UnpermittedSymbolError) do
+ spec = Gem::Specification.new do |s|
+ s.name = "hi"
+ s.version = "1.2.3"
+
+ s.dependencies << Gem::Dependency.new("rspec", Gem::Requirement.new([">= 1.2.3"]), :runtime).tap {|d| d.instance_variable_set(:@name, :rspec) }
+ end
+ Gem::SafeMarshal.safe_load(Marshal.dump(spec))
+ end
+
+ assert_equal e.message, "Attempting to load unpermitted symbol \"rspec\" @ root.[9].[0].@name"
+ end
+
+ def test_gem_spec_disallowed_ivar
+ e = assert_raise(Gem::SafeMarshal::Visitors::ToRuby::UnpermittedIvarError) do
+ spec = Gem::Specification.new do |s|
+ s.name = "hi"
+ s.version = "1.2.3"
+
+ s.metadata.instance_variable_set(:@foobar, "rspec")
+ end
+ Gem::SafeMarshal.safe_load(Marshal.dump(spec))
+ end
+
+ assert_equal e.message, "Attempting to set unpermitted ivar \"@foobar\" on object of class Hash @ root.[18].ivar_0"
+ end
+
+ def test_unexpected_eof
+ e = assert_raise(Gem::SafeMarshal::Reader::EOFError) do
+ Gem::SafeMarshal.safe_load("\x04\x08")
+ end
+ assert_equal e.message, "Unexpected EOF"
+
+ e = assert_raise(Gem::SafeMarshal::Reader::EOFError) do
+ Gem::SafeMarshal.safe_load("\x04\x08[")
+ end
+ assert_equal e.message, "Unexpected EOF"
+
+ e = assert_raise(Gem::SafeMarshal::Reader::EOFError) do
+ Gem::SafeMarshal.safe_load("\x04\x08[\x06")
+ end
+ assert_equal e.message, "Unexpected EOF"
+ end
+
+ def assert_safe_load_marshal(dumped, additional_methods: [], permitted_ivars: nil, equality: true, marshal_dump_equality: true)
+ loaded = Marshal.load(dumped)
+ safe_loaded =
+ if permitted_ivars
+ with_const(Gem::SafeMarshal, :PERMITTED_IVARS, permitted_ivars) do
+ Gem::SafeMarshal.safe_load(dumped)
+ end
+ else
+ Gem::SafeMarshal.safe_load(dumped)
+ end
+
+ # NaN != NaN, for example
+ if equality
+ assert_equal loaded, safe_loaded, "should equal what Marshal.load returns"
+ end
+
+ assert_equal loaded.to_s, safe_loaded.to_s, "should have equal to_s"
+ assert_equal loaded.inspect, safe_loaded.inspect, "should have equal inspect"
+ additional_methods.each do |m|
+ if m.is_a?(Proc)
+ call = m
+ else
+ call = ->(obj) { obj.__send__(m) }
+ end
+
+ assert_equal call[loaded], call[safe_loaded], "should have equal #{m}"
+ end
+ if marshal_dump_equality
+ assert_equal Marshal.dump(loaded).dump, Marshal.dump(safe_loaded).dump, "should Marshal.dump the same"
+ end
+ end
+
+ def assert_safe_load_as(x, **kwargs)
+ dumped = Marshal.dump(x)
+ equality = x == x # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
+ assert_safe_load_marshal(dumped, equality: equality, **kwargs)
+ end
+
+ def with_const(mod, name, new_value, &block)
+ orig = mod.const_get(name)
+ mod.send :remove_const, name
+ mod.const_set name, new_value
+
+ begin
+ yield
+ ensure
+ mod.send :remove_const, name
+ mod.const_set name, orig
+ mod.send :private_constant, name
+ end
+ end
+end
diff --git a/test/rubygems/test_gem_safe_yaml.rb b/test/rubygems/test_gem_safe_yaml.rb
new file mode 100644
index 0000000000..02df9f97da
--- /dev/null
+++ b/test/rubygems/test_gem_safe_yaml.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require_relative "helper"
+
+Gem.load_yaml
+
+class TestGemSafeYAML < Gem::TestCase
+ def test_aliases_enabled_by_default
+ assert_predicate Gem::SafeYAML, :aliases_enabled?
+ assert_equal({ "a" => "a", "b" => "a" }, Gem::SafeYAML.safe_load("a: &a a\nb: *a\n"))
+ end
+
+ def test_aliases_disabled
+ aliases_enabled = Gem::SafeYAML.aliases_enabled?
+ Gem::SafeYAML.aliases_enabled = false
+ refute_predicate Gem::SafeYAML, :aliases_enabled?
+ expected_error = defined?(Psych::AliasesNotEnabled) ? Psych::AliasesNotEnabled : Psych::BadAlias
+ assert_raise expected_error do
+ Gem::SafeYAML.safe_load("a: &a\nb: *a\n")
+ end
+ ensure
+ Gem::SafeYAML.aliases_enabled = aliases_enabled
+ end
+end
diff --git a/test/rubygems/test_gem_security_policy.rb b/test/rubygems/test_gem_security_policy.rb
index 0c5c621485..2f4fb1ce28 100644
--- a/test/rubygems/test_gem_security_policy.rb
+++ b/test/rubygems/test_gem_security_policy.rb
@@ -43,22 +43,22 @@ class TestGemSecurityPolicy < Gem::TestCase
@chain = Gem::Security::Policy.new(
"Chain",
- :verify_data => true,
- :verify_signer => true,
- :verify_chain => true,
- :verify_root => false,
- :only_trusted => false,
- :only_signed => false
+ verify_data: true,
+ verify_signer: true,
+ verify_chain: true,
+ verify_root: false,
+ only_trusted: false,
+ only_signed: false
)
@root = Gem::Security::Policy.new(
"Root",
- :verify_data => true,
- :verify_signer => true,
- :verify_chain => true,
- :verify_root => true,
- :only_trusted => false,
- :only_signed => false
+ verify_data: true,
+ verify_signer: true,
+ verify_chain: true,
+ verify_root: true,
+ only_trusted: false,
+ only_signed: false
)
end
diff --git a/test/rubygems/test_gem_security_signer.rb b/test/rubygems/test_gem_security_signer.rb
index 5265678d29..f4799cbd46 100644
--- a/test/rubygems/test_gem_security_signer.rb
+++ b/test/rubygems/test_gem_security_signer.rb
@@ -144,7 +144,7 @@ B8khkB8hDKC6moCzebmUxCBmTmXD0Wjzon+bf4MOriVE3a0ySGRvpr1mKR2+
def test_sign_expired_auto_update
pend if Gem.java_platform?
- FileUtils.mkdir_p File.join(Gem.user_home, ".gem"), :mode => 0o700
+ FileUtils.mkdir_p File.join(Gem.user_home, ".gem"), mode: 0o700
private_key_path = File.join(Gem.user_home, ".gem", "gem-private_key.pem")
Gem::Security.write PRIVATE_KEY, private_key_path
@@ -171,7 +171,7 @@ B8khkB8hDKC6moCzebmUxCBmTmXD0Wjzon+bf4MOriVE3a0ySGRvpr1mKR2+
end
def test_sign_expired_auto_update_exists
- FileUtils.mkdir_p File.join(Gem.user_home, ".gem"), :mode => 0o700
+ FileUtils.mkdir_p File.join(Gem.user_home, ".gem"), mode: 0o700
expiry = EXPIRED_CERT.not_after.strftime "%Y%m%d%H%M%S"
expired_path =
diff --git a/test/rubygems/test_gem_security_trust_dir.rb b/test/rubygems/test_gem_security_trust_dir.rb
index b7c9a6feb0..cfde8e9d48 100644
--- a/test/rubygems/test_gem_security_trust_dir.rb
+++ b/test/rubygems/test_gem_security_trust_dir.rb
@@ -87,7 +87,7 @@ class TestGemSecurityTrustDir < Gem::TestCase
end
def test_verify_wrong_permissions
- FileUtils.mkdir_p @dest_dir, :mode => 0o777
+ FileUtils.mkdir_p @dest_dir, mode: 0o777
@trust_dir.verify
diff --git a/test/rubygems/test_gem_silent_ui.rb b/test/rubygems/test_gem_silent_ui.rb
index 94411f30b3..001a73eb51 100644
--- a/test/rubygems/test_gem_silent_ui.rb
+++ b/test/rubygems/test_gem_silent_ui.rb
@@ -2,7 +2,6 @@
require_relative "helper"
require "rubygems/user_interaction"
-require "timeout"
class TestGemSilentUI < Gem::TestCase
def setup
diff --git a/test/rubygems/test_gem_source.rb b/test/rubygems/test_gem_source.rb
index 0372c6253c..6baa203dcb 100644
--- a/test/rubygems/test_gem_source.rb
+++ b/test/rubygems/test_gem_source.rb
@@ -2,7 +2,6 @@
require_relative "helper"
require "rubygems/source"
-require "rubygems/indexer"
class TestGemSource < Gem::TestCase
def tuple(*args)
@@ -23,7 +22,7 @@ class TestGemSource < Gem::TestCase
end
def test_initialize_invalid_uri
- assert_raise URI::InvalidURIError do
+ assert_raise Gem::URI::InvalidURIError do
Gem::Source.new "git@example:a.git"
end
end
@@ -37,15 +36,15 @@ class TestGemSource < Gem::TestCase
end
def test_cache_dir_escapes_windows_paths
- uri = URI.parse("file:///C:/WINDOWS/Temp/gem_repo")
+ uri = Gem::URI.parse("file:///C:/WINDOWS/Temp/gem_repo")
root = Gem.spec_cache_dir
cache_dir = @source.cache_dir(uri).gsub(root, "")
- assert cache_dir !~ /:/, "#{cache_dir} should not contain a :"
+ assert !cache_dir.include?(":"), "#{cache_dir} should not contain a :"
end
def test_dependency_resolver_set_bundler_api
- response = Net::HTTPResponse.new "1.1", 200, "OK"
- response.uri = URI("http://example")
+ response = Gem::Net::HTTPResponse.new "1.1", 200, "OK"
+ response.uri = Gem::URI("http://example")
@fetcher.data[@gem_repo] = response
@@ -55,7 +54,9 @@ class TestGemSource < Gem::TestCase
end
def test_dependency_resolver_set_file_uri
- Gem::Indexer.new(@tempdir).generate_index
+ empty_dump = Gem::Util.gzip("\x04\x08[\x05".b)
+ File.binwrite(File.join(@tempdir, "prerelease_specs.4.8.gz"), empty_dump)
+ File.binwrite(File.join(@tempdir, "specs.4.8.gz"), empty_dump)
source = Gem::Source.new "file://#{@tempdir}/"
@@ -78,7 +79,7 @@ class TestGemSource < Gem::TestCase
spec = @source.fetch_spec tuple("a", Gem::Version.new(1), "ruby")
assert_equal a1.full_name, spec.full_name
- cache_dir = @source.cache_dir URI.parse(spec_uri)
+ cache_dir = @source.cache_dir Gem::URI.parse(spec_uri)
cache_file = File.join cache_dir, a1.spec_name
@@ -91,7 +92,7 @@ class TestGemSource < Gem::TestCase
spec_uri = "#{@gem_repo}/#{Gem::MARSHAL_SPEC_DIR}#{a1.spec_name}"
@fetcher.data["#{spec_uri}.rz"] = nil
- cache_dir = @source.cache_dir URI.parse(spec_uri)
+ cache_dir = @source.cache_dir Gem::URI.parse(spec_uri)
FileUtils.mkdir_p cache_dir
cache_file = File.join cache_dir, a1.spec_name
diff --git a/test/rubygems/test_gem_source_git.rb b/test/rubygems/test_gem_source_git.rb
index 18265bd814..20e750a0d4 100644
--- a/test/rubygems/test_gem_source_git.rb
+++ b/test/rubygems/test_gem_source_git.rb
@@ -289,7 +289,7 @@ class TestGemSourceGit < Gem::TestCase
end
def test_uri
- assert_equal URI(@repository), @source.uri
+ assert_equal Gem::URI(@repository), @source.uri
end
def test_uri_hash
diff --git a/test/rubygems/test_gem_source_list.rb b/test/rubygems/test_gem_source_list.rb
index fc084830ba..64353f8f90 100644
--- a/test/rubygems/test_gem_source_list.rb
+++ b/test/rubygems/test_gem_source_list.rb
@@ -37,7 +37,7 @@ class TestGemSourceList < Gem::TestCase
assert_kind_of Gem::Source, source
- assert_kind_of URI, source.uri
+ assert_kind_of Gem::URI, source.uri
assert_equal source.uri.to_s, @uri
assert_equal [source], sl.sources
@@ -99,7 +99,7 @@ class TestGemSourceList < Gem::TestCase
def test_include_eh
assert @sl.include?(@uri), "string comparison not working"
- assert @sl.include?(URI.parse(@uri)), "uri comparison not working"
+ assert @sl.include?(Gem::URI.parse(@uri)), "uri comparison not working"
end
def test_include_matches_a_source
diff --git a/test/rubygems/test_gem_source_lock.rb b/test/rubygems/test_gem_source_lock.rb
index ece55581ec..91ffee68f2 100644
--- a/test/rubygems/test_gem_source_lock.rb
+++ b/test/rubygems/test_gem_source_lock.rb
@@ -110,6 +110,6 @@ class TestGemSourceLock < Gem::TestCase
remote = Gem::Source.new @gem_repo
lock = Gem::Source::Lock.new remote
- assert_equal URI(@gem_repo), lock.uri
+ assert_equal Gem::URI(@gem_repo), lock.uri
end
end
diff --git a/test/rubygems/test_gem_source_subpath_problem.rb b/test/rubygems/test_gem_source_subpath_problem.rb
index 0aa2085b3c..a451a81a25 100644
--- a/test/rubygems/test_gem_source_subpath_problem.rb
+++ b/test/rubygems/test_gem_source_subpath_problem.rb
@@ -21,8 +21,8 @@ class TestGemSourceSubpathProblem < Gem::TestCase
end
def test_dependency_resolver_set
- response = Net::HTTPResponse.new "1.1", 200, "OK"
- response.uri = URI("http://example")
+ response = Gem::Net::HTTPResponse.new "1.1", 200, "OK"
+ response.uri = Gem::URI("http://example")
@fetcher.data["#{@gem_repo}/"] = response
diff --git a/test/rubygems/test_gem_spec_fetcher.rb b/test/rubygems/test_gem_spec_fetcher.rb
index 0fca9f0c48..cb4a4f7204 100644
--- a/test/rubygems/test_gem_spec_fetcher.rb
+++ b/test/rubygems/test_gem_spec_fetcher.rb
@@ -11,7 +11,7 @@ class TestGemSpecFetcher < Gem::TestCase
def setup
super
- @uri = URI.parse @gem_repo
+ @uri = Gem::URI.parse @gem_repo
@source = Gem::Source.new(@uri)
@sf = Gem::SpecFetcher.new
diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb
index e4e908c98f..9e05649f7c 100644
--- a/test/rubygems/test_gem_specification.rb
+++ b/test/rubygems/test_gem_specification.rb
@@ -90,6 +90,7 @@ end
Gem.instance_variable_set(:'@default_source_date_epoch', nil)
@a1 = util_spec "a", "1" do |s|
+ s.required_ruby_version = ">= 2.3.0"
s.executable = "exec"
s.test_file = "test/suite.rb"
s.requirements << "A working computer"
@@ -797,28 +798,6 @@ dependencies: []
assert_equal File.join(@tempdir, "a-2.gemspec"), spec.loaded_from
end
- if RUBY_ENGINE == "ruby" && RUBY_VERSION < "2.7"
- def test_self_load_tainted
- full_path = @a2.spec_file
- write_file full_path do |io|
- io.write @a2.to_ruby_for_cache
- end
-
- full_path.taint
- loader = Thread.new do
- $SAFE = 1
- Gem::Specification.load full_path
- end
- spec = loader.value
-
- @a2.files.clear
-
- assert_equal @a2, spec
- ensure
- $SAFE = 0
- end
- end
-
def test_self_load_escape_curly
@a2.name = 'a};raise "improper escaping";%q{'
@@ -1071,19 +1050,46 @@ dependencies: []
end
def test_handles_private_null_type
+ yaml_defined = Object.const_defined?("YAML")
+
path = File.expand_path "data/pry-0.4.7.gemspec.rz", __dir__
data = Marshal.load Gem::Util.inflate(Gem.read_binary(path))
assert_instance_of Gem::Specification, data
+
+ assert_equal(yaml_defined, Object.const_defined?("YAML"))
end
def test_handles_dependencies_with_syck_requirements_bug
+ yaml_defined = Object.const_defined?("YAML")
+
path = File.expand_path "data/excon-0.7.7.gemspec.rz", __dir__
data = Marshal.load Gem::Util.inflate(Gem.read_binary(path))
assert_instance_of Gem::Specification, data
+
+ assert_equal(yaml_defined, Object.const_defined?("YAML"))
+ end
+
+ def test_handles_dependencies_with_other_syck_requirements_argument_error
+ yaml_defined = Object.const_defined?("YAML")
+
+ data = Marshal.dump(Gem::Specification.new do |s|
+ v = Gem::Version.allocate
+ v.instance_variable_set :@version, "YAML::Syck::DefaultKey"
+ s.instance_variable_set :@version, v
+ end)
+
+ assert_raise(ArgumentError) { Marshal.load(data) }
+ out, err = capture_output do
+ assert_raise(ArgumentError) { Marshal.load(data) }
+ end
+ assert_empty out
+ assert_empty err
+
+ assert_equal(yaml_defined, Object.const_defined?("YAML"))
end
def test_initialize
@@ -1189,10 +1195,13 @@ dependencies: []
assert_same spec.bindir, dup_spec.bindir
assert_equal ">= 0", spec.required_ruby_version.to_s
- assert_same spec.required_ruby_version, dup_spec.required_ruby_version
+ assert_equal spec.required_ruby_version, dup_spec.required_ruby_version
+ refute_same spec.required_ruby_version, dup_spec.required_ruby_version
assert_equal ">= 0", spec.required_rubygems_version.to_s
- assert_same spec.required_rubygems_version,
+ assert_equal spec.required_rubygems_version,
+ dup_spec.required_rubygems_version
+ refute_same spec.required_rubygems_version,
dup_spec.required_rubygems_version
end
@@ -1556,6 +1565,17 @@ dependencies: []
assert_empty err
end
+ def test_contains_requirable_file_extension_soext
+ ext_spec
+ dlext = RbConfig::CONFIG["DLEXT"]
+ @ext.files += ["lib/ext.#{dlext}"]
+
+ FileUtils.mkdir_p @ext.extension_dir
+ FileUtils.touch File.join(@ext.extension_dir, "ext.#{dlext}")
+ FileUtils.touch File.join(@ext.extension_dir, "gem.build_complete")
+ assert @ext.contains_requirable_file? "ext.so"
+ end
+
def test_date
assert_date @a1.date
end
@@ -2000,12 +2020,6 @@ dependencies: []
assert_equal Gem::Platform.new("ppc-darwin"), @a1.platform
end
- def test_prerelease_spec_adds_required_rubygems_version
- @prerelease = util_spec("tardis", "2.2.0.a")
- refute @prerelease.required_rubygems_version.satisfied_by?(Gem::Version.new("1.3.1"))
- assert @prerelease.required_rubygems_version.satisfied_by?(Gem::Version.new("1.4.0"))
- end
-
def test_require_paths
enable_shared "no" do
ext_spec
@@ -2264,7 +2278,7 @@ dependencies: []
Gem::Specification.new do |s|
s.name = "a".freeze
- s.version = "2"
+ s.version = "2".freeze
s.required_rubygems_version = Gem::Requirement.new(\"> 0\".freeze) if s.respond_to? :required_rubygems_version=
s.require_paths = ["lib".freeze, "other".freeze]
@@ -2279,7 +2293,7 @@ Gem::Specification.new do |s|
s.specification_version = #{Gem::Specification::CURRENT_SPECIFICATION_VERSION}
- s.add_runtime_dependency(%q<b>.freeze, [\"= 1\"])
+ s.add_runtime_dependency(%q<b>.freeze, [\"= 1\".freeze])
end
SPEC
@@ -2304,7 +2318,7 @@ end
Gem::Specification.new do |s|
s.name = "a".freeze
- s.version = "2"
+ s.version = "2".freeze
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
s.require_paths = ["lib".freeze]
@@ -2337,7 +2351,7 @@ end
Gem::Specification.new do |s|
s.name = "a".freeze
- s.version = "2"
+ s.version = "2".freeze
s.required_rubygems_version = Gem::Requirement.new(\"> 0\".freeze) if s.respond_to? :required_rubygems_version=
s.require_paths = ["lib".freeze]
@@ -2349,11 +2363,11 @@ Gem::Specification.new do |s|
s.rubygems_version = "#{Gem::VERSION}".freeze
s.summary = "this is a summary".freeze
- s.installed_by_version = "#{Gem::VERSION}" if s.respond_to? :installed_by_version
+ s.installed_by_version = "#{Gem::VERSION}".freeze if s.respond_to? :installed_by_version
s.specification_version = #{Gem::Specification::CURRENT_SPECIFICATION_VERSION}
- s.add_runtime_dependency(%q<b>.freeze, [\"= 1\"])
+ s.add_runtime_dependency(%q<b>.freeze, ["= 1".freeze])
end
SPEC
@@ -2373,7 +2387,7 @@ end
ruby_code = @c1.to_ruby
local = Gem::Platform.local
- expected_platform = "[#{local.cpu.inspect}, #{local.os.inspect}, #{local.version.inspect}]"
+ expected_platform = "[#{local.cpu.inspect}.freeze, #{local.os.inspect}.freeze, #{local.version.inspect}.freeze]"
stub_require_paths =
@c1.instance_variable_get(:@require_paths).join "\u0000"
extensions = @c1.extensions.join "\u0000"
@@ -2385,7 +2399,7 @@ end
Gem::Specification.new do |s|
s.name = "a".freeze
- s.version = "1"
+ s.version = "1".freeze
s.platform = Gem::Platform.new(#{expected_platform})
s.required_rubygems_version = Gem::Requirement.new(\">= 0\".freeze) if s.respond_to? :required_rubygems_version=
@@ -2406,9 +2420,9 @@ Gem::Specification.new do |s|
s.specification_version = 4
- s.add_runtime_dependency(%q<rake>.freeze, [\"> 0.4\"])
- s.add_runtime_dependency(%q<jabber4r>.freeze, [\"> 0.0.0\"])
- s.add_runtime_dependency(%q<pqa>.freeze, [\"> 0.4\", \"<= 0.6\"])
+ s.add_runtime_dependency(%q<rake>.freeze, [\"> 0.4\".freeze])
+ s.add_runtime_dependency(%q<jabber4r>.freeze, [\"> 0.0.0\".freeze])
+ s.add_runtime_dependency(%q<pqa>.freeze, [\"> 0.4\".freeze, \"<= 0.6\".freeze])
end
SPEC
@@ -2424,7 +2438,7 @@ end
s.add_dependency "b", ["~> 1.0", ">= 1.0.0"]
end
- assert_includes spec.to_ruby, '"~> 1.0", ">= 1.0.0"'
+ assert_includes spec.to_ruby, '"~> 1.0".freeze, ">= 1.0.0".freeze'
end
def test_to_ruby_legacy
@@ -2504,6 +2518,26 @@ end
assert_match(/^platform: ruby$/, @a1.to_yaml)
end
+ def test_to_yaml_no_autorequire
+ yaml_str = @a1.to_yaml
+
+ refute_match(/^autorequire:/, yaml_str)
+ end
+
+ def test_to_yaml_no_signing_key
+ @a1.signing_key = nil
+ yaml_str = @a1.to_yaml
+
+ refute_match(/^signing_key:/, yaml_str)
+ end
+
+ def test_to_yaml_no_post_install_message
+ @a1.post_install_message = nil
+ yaml_str = @a1.to_yaml
+
+ refute_match(/^post_install_message:/, yaml_str)
+ end
+
def test_validate
util_setup_validate
@@ -2674,6 +2708,53 @@ duplicate dependency on c (>= 1.2.3, development), (~> 1.2) use:
end
end
+ def test_validate_no_required_ruby_versions
+ util_setup_validate
+
+ Dir.chdir @tempdir do
+ use_ui @ui do
+ @a1.required_ruby_version = nil # reset
+ @a1.validate
+ end
+
+ assert_equal <<-EXPECTED, @ui.error
+#{w}: make sure you specify the oldest ruby version constraint (like \">= 3.0\") that you want your gem to support by setting the `required_ruby_version` gemspec attribute
+#{w}: See https://guides.rubygems.org/specification-reference/ for help
+ EXPECTED
+ end
+ end
+
+ def test_validate_open_required_ruby_versions
+ util_setup_validate
+
+ Dir.chdir @tempdir do
+ @a1.required_ruby_version = ">= 0"
+
+ use_ui @ui do
+ @a1.validate
+ end
+
+ assert_equal <<-EXPECTED, @ui.error
+#{w}: make sure you specify the oldest ruby version constraint (like \">= 3.0\") that you want your gem to support by setting the `required_ruby_version` gemspec attribute
+#{w}: See https://guides.rubygems.org/specification-reference/ for help
+ EXPECTED
+ end
+ end
+
+ def test_validate_valid_required_ruby_versions
+ util_setup_validate
+
+ Dir.chdir @tempdir do
+ @a1.required_ruby_version = ">= 2.3.0"
+
+ use_ui @ui do
+ @a1.validate
+ end
+
+ assert_equal "", @ui.error, "warning"
+ end
+ end
+
def test_validate_prerelease_dependencies_with_prerelease_version
util_setup_validate
@@ -2717,7 +2798,7 @@ duplicate dependency on c (>= 1.2.3, development), (~> 1.2) use:
@a1.validate
end
- assert_match(/add rake as a dependency/, @ui.error)
+ assert_match(/add rake as a runtime dependency/, @ui.error)
end
end
@@ -2733,7 +2814,7 @@ duplicate dependency on c (>= 1.2.3, development), (~> 1.2) use:
@a1.validate
end
- refute_match(/add rake as a dependency/, @ui.error)
+ refute_match(/add rake as a runtime dependency/, @ui.error)
end
end
@@ -3077,11 +3158,23 @@ Please report a bug if this causes problems.
end
assert_match <<-WARNING, @ui.error
-WARNING: licenses is empty, but is recommended. Use a license identifier from
-http://spdx.org/licenses or 'Nonstandard' for a nonstandard license.
+WARNING: licenses is empty, but is recommended. Use an license identifier from
+https://spdx.org/licenses or 'Nonstandard' for a nonstandard license,
+or set it to nil if you don't want to specify a license.
WARNING
end
+ def test_validate_nil_license
+ util_setup_validate
+
+ use_ui @ui do
+ @a1.license = nil
+ @a1.validate
+ end
+
+ assert_empty @ui.error
+ end
+
def test_validate_license_in_a_non_packaging_context
util_setup_validate
@@ -3117,8 +3210,9 @@ http://spdx.org/licenses or 'Nonstandard' for a nonstandard license.
end
assert_match <<-WARNING, @ui.error
-WARNING: license value 'BSD' is invalid. Use a license identifier from
-http://spdx.org/licenses or 'Nonstandard' for a nonstandard license.
+WARNING: License identifier 'BSD' is invalid. Use an identifier from
+https://spdx.org/licenses or 'Nonstandard' for a nonstandard license,
+or set it to nil if you don't want to specify a license.
WARNING
end
@@ -3133,7 +3227,7 @@ http://spdx.org/licenses or 'Nonstandard' for a nonstandard license.
assert_empty @ui.error
end
- def test_validate_license_values_plus
+ def test_validate_deprecated_license_values_plus
util_setup_validate
use_ui @ui do
@@ -3141,7 +3235,11 @@ http://spdx.org/licenses or 'Nonstandard' for a nonstandard license.
@a1.validate
end
- assert_empty @ui.error
+ assert_match <<-WARNING, @ui.error
+WARNING: License identifier 'GPL-2.0+' is deprecated. Use an identifier from
+https://spdx.org/licenses or 'Nonstandard' for a nonstandard license,
+or set it to nil if you don't want to specify a license.
+ WARNING
end
def test_validate_license_values_or_later
@@ -3159,7 +3257,7 @@ http://spdx.org/licenses or 'Nonstandard' for a nonstandard license.
util_setup_validate
use_ui @ui do
- @a1.licenses = ["GPL-2.0+ WITH Autoconf-exception-2.0"]
+ @a1.licenses = ["GPL-2.0-or-later WITH Autoconf-exception-2.0"]
@a1.validate
end
@@ -3175,12 +3273,14 @@ http://spdx.org/licenses or 'Nonstandard' for a nonstandard license.
end
assert_match <<-WARNING, @ui.error
-WARNING: license value 'GPL-2.0+ FOO' is invalid. Use a license identifier from
-http://spdx.org/licenses or 'Nonstandard' for a nonstandard license.
+WARNING: License identifier 'GPL-2.0+ FOO' is invalid. Use an identifier from
+https://spdx.org/licenses or 'Nonstandard' for a nonstandard license,
+or set it to nil if you don't want to specify a license.
WARNING
assert_match <<-WARNING, @ui.error
-WARNING: license value 'GPL-2.0 FOO' is invalid. Use a license identifier from
-http://spdx.org/licenses or 'Nonstandard' for a nonstandard license.
+WARNING: License identifier 'GPL-2.0+ FOO' is invalid. Use an identifier from
+https://spdx.org/licenses or 'Nonstandard' for a nonstandard license,
+or set it to nil if you don't want to specify a license.
WARNING
end
@@ -3188,13 +3288,29 @@ http://spdx.org/licenses or 'Nonstandard' for a nonstandard license.
util_setup_validate
use_ui @ui do
- @a1.licenses = ["GPL-2.0+ WITH Autocofn-exception-2.0"]
+ @a1.licenses = ["GPL-2.0-only WITH Autocofn-exception-2.0"]
@a1.validate
end
assert_match <<-WARNING, @ui.error
-WARNING: license value 'GPL-2.0+ WITH Autocofn-exception-2.0' is invalid. Use a license identifier from
-http://spdx.org/licenses or 'Nonstandard' for a nonstandard license.
+WARNING: License identifier 'GPL-2.0-only WITH Autocofn-exception-2.0' is invalid. Use an identifier from
+https://spdx.org/licenses or 'Nonstandard' for a nonstandard license,
+or set it to nil if you don't want to specify a license.
+ WARNING
+ end
+
+ def test_validate_license_with_deprecated_exception
+ util_setup_validate
+
+ use_ui @ui do
+ @a1.licenses = ["GPL-2.0-only WITH Nokia-Qt-exception-1.1"]
+ @a1.validate
+ end
+
+ assert_match <<-WARNING, @ui.error
+WARNING: Exception identifier at 'GPL-2.0-only WITH Nokia-Qt-exception-1.1' is deprecated. Use an identifier from
+https://spdx.org/licenses or 'Nonstandard' for a nonstandard license,
+or set it to nil if you don't want to specify a license.
WARNING
end
@@ -3207,8 +3323,9 @@ http://spdx.org/licenses or 'Nonstandard' for a nonstandard license.
end
assert_match <<-WARNING, @ui.error
-WARNING: license value 'ruby' is invalid. Use a license identifier from
-http://spdx.org/licenses or 'Nonstandard' for a nonstandard license.
+WARNING: License identifier 'ruby' is invalid. Use an identifier from
+https://spdx.org/licenses or 'Nonstandard' for a nonstandard license,
+or set it to nil if you don't want to specify a license.
Did you mean 'Ruby'?
WARNING
end
@@ -3609,6 +3726,39 @@ Did you mean 'Ruby'?
end
end
+ def test_metadata_link_validation_warns_for_duplicates
+ util_setup_validate
+
+ Dir.chdir @tempdir do
+ @m2 = quick_gem "m", "2" do |s|
+ s.required_ruby_version = ">= 2.3.0"
+ s.files = %w[lib/code.rb]
+ s.licenses = "BSD-2-Clause"
+ s.metadata = {
+ "source_code_uri" => "http://example.com",
+ "homepage_uri" => "http://example.com",
+ "changelog_uri" => "http://example.com/changelog",
+ }
+ end
+
+ use_ui @ui do
+ @m2.validate
+ end
+
+ expected = <<~EXPECTED
+ #{w}: You have specified the uri:
+ http://example.com
+ for all of the following keys:
+ homepage_uri
+ source_code_uri
+ Only the first one will be shown on rubygems.org
+ #{w}: See https://guides.rubygems.org/specification-reference/ for help
+ EXPECTED
+
+ assert_equal expected, @ui.error, "warning"
+ end
+ end
+
def test_metadata_specs
@m1 = quick_gem "m", "1" do |s|
s.files = %w[lib/code.rb]
@@ -3621,7 +3771,7 @@ Did you mean 'Ruby'?
Gem::Specification.new do |s|
s.name = "m".freeze
- s.version = "1"
+ s.version = "1".freeze
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
s.metadata = { "one" => "two", "two" => "three" } if s.respond_to? :metadata=
@@ -3720,6 +3870,13 @@ end
assert Gem::Specification.find_by_name "q"
end
+ def test_find_by_name_with_only_prereleases_with_requirements
+ q = util_spec "q", "2.a"
+ install_specs q
+
+ assert Gem::Specification.find_by_name "q", ">= 1"
+ end
+
def test_find_by_name_prerelease
b = util_spec "b", "2.a"
diff --git a/test/rubygems/test_gem_stream_ui.rb b/test/rubygems/test_gem_stream_ui.rb
index 24a9a4ba19..b1fcb3bc26 100644
--- a/test/rubygems/test_gem_stream_ui.rb
+++ b/test/rubygems/test_gem_stream_ui.rb
@@ -2,7 +2,7 @@
require_relative "helper"
require "rubygems/user_interaction"
-require "timeout"
+require "rubygems/vendored_timeout"
class TestGemStreamUI < Gem::TestCase
# increase timeout with RJIT for --jit-wait testing
@@ -40,7 +40,7 @@ class TestGemStreamUI < Gem::TestCase
end
def test_ask
- Timeout.timeout(5) do
+ Gem::Timeout.timeout(5) do
expected_answer = "Arthur, King of the Britons"
@in.string = "#{expected_answer}\n"
actual_answer = @sui.ask("What is your name?")
@@ -51,14 +51,14 @@ class TestGemStreamUI < Gem::TestCase
def test_ask_no_tty
@in.tty = false
- Timeout.timeout(SHORT_TIMEOUT) do
+ Gem::Timeout.timeout(SHORT_TIMEOUT) do
answer = @sui.ask("what is your favorite color?")
assert_nil answer
end
end
def test_ask_for_password
- Timeout.timeout(5) do
+ Gem::Timeout.timeout(5) do
expected_answer = "Arthur, King of the Britons"
@in.string = "#{expected_answer}\n"
actual_answer = @sui.ask_for_password("What is your name?")
@@ -69,7 +69,7 @@ class TestGemStreamUI < Gem::TestCase
def test_ask_for_password_no_tty
@in.tty = false
- Timeout.timeout(SHORT_TIMEOUT) do
+ Gem::Timeout.timeout(SHORT_TIMEOUT) do
answer = @sui.ask_for_password("what is the airspeed velocity of an unladen swallow?")
assert_nil answer
end
@@ -78,7 +78,7 @@ class TestGemStreamUI < Gem::TestCase
def test_ask_yes_no_no_tty_with_default
@in.tty = false
- Timeout.timeout(SHORT_TIMEOUT) do
+ Gem::Timeout.timeout(SHORT_TIMEOUT) do
answer = @sui.ask_yes_no("do coconuts migrate?", false)
assert_equal false, answer
@@ -90,7 +90,7 @@ class TestGemStreamUI < Gem::TestCase
def test_ask_yes_no_no_tty_without_default
@in.tty = false
- Timeout.timeout(SHORT_TIMEOUT) do
+ Gem::Timeout.timeout(SHORT_TIMEOUT) do
assert_raise(Gem::OperationNotSupportedError) do
@sui.ask_yes_no("do coconuts migrate?")
end
@@ -114,6 +114,36 @@ class TestGemStreamUI < Gem::TestCase
assert_equal "which one?\n 1. foo\n 2. bar\n> ", @out.string
end
+ def test_choose_from_list_0
+ @in.puts "0"
+ @in.rewind
+
+ result = @sui.choose_from_list "which one?", %w[foo bar]
+
+ assert_equal [nil, nil], result
+ assert_equal "which one?\n 1. foo\n 2. bar\n> ", @out.string
+ end
+
+ def test_choose_from_list_over
+ @in.puts "3"
+ @in.rewind
+
+ result = @sui.choose_from_list "which one?", %w[foo bar]
+
+ assert_equal [nil, nil], result
+ assert_equal "which one?\n 1. foo\n 2. bar\n> ", @out.string
+ end
+
+ def test_choose_from_list_negative
+ @in.puts "-1"
+ @in.rewind
+
+ result = @sui.choose_from_list "which one?", %w[foo bar]
+
+ assert_equal [nil, nil], result
+ assert_equal "which one?\n 1. foo\n 2. bar\n> ", @out.string
+ end
+
def test_progress_reporter_silent_nil
@cfg.verbose = nil
reporter = @sui.progress_reporter 10, "hi"
diff --git a/test/rubygems/test_gem_uninstaller.rb b/test/rubygems/test_gem_uninstaller.rb
index dfa01768bc..9e0c1aa3d8 100644
--- a/test/rubygems/test_gem_uninstaller.rb
+++ b/test/rubygems/test_gem_uninstaller.rb
@@ -25,7 +25,7 @@ class TestGemUninstaller < Gem::InstallerTestCase
def test_initialize_expand_path
FileUtils.mkdir_p "foo/bar"
- uninstaller = Gem::Uninstaller.new nil, :install_dir => "foo//bar"
+ uninstaller = Gem::Uninstaller.new nil, install_dir: "foo//bar"
assert_match %r{foo/bar$}, uninstaller.instance_variable_get(:@gem_home)
end
@@ -59,7 +59,7 @@ class TestGemUninstaller < Gem::InstallerTestCase
end
def test_remove_executables_force_keep
- uninstaller = Gem::Uninstaller.new nil, :executables => false
+ uninstaller = Gem::Uninstaller.new nil, executables: false
executable = File.join Gem.bindir(@user_spec.base_dir), "executable"
assert File.exist?(executable), "executable not written"
@@ -74,7 +74,7 @@ class TestGemUninstaller < Gem::InstallerTestCase
end
def test_remove_executables_force_remove
- uninstaller = Gem::Uninstaller.new nil, :executables => true
+ uninstaller = Gem::Uninstaller.new nil, executables: true
executable = File.join Gem.bindir(@user_spec.base_dir), "executable"
assert File.exist?(executable), "executable not written"
@@ -89,7 +89,7 @@ class TestGemUninstaller < Gem::InstallerTestCase
end
def test_remove_executables_user
- uninstaller = Gem::Uninstaller.new nil, :executables => true
+ uninstaller = Gem::Uninstaller.new nil, executables: true
use_ui @ui do
uninstaller.remove_executables @user_spec
@@ -104,7 +104,7 @@ class TestGemUninstaller < Gem::InstallerTestCase
def test_remove_executables_user_format
Gem::Installer.exec_format = "foo-%s-bar"
- uninstaller = Gem::Uninstaller.new nil, :executables => true, :format_executable => true
+ uninstaller = Gem::Uninstaller.new nil, executables: true, format_executable: true
use_ui @ui do
uninstaller.remove_executables @user_spec
@@ -121,7 +121,7 @@ class TestGemUninstaller < Gem::InstallerTestCase
def test_remove_executables_user_format_disabled
Gem::Installer.exec_format = "foo-%s-bar"
- uninstaller = Gem::Uninstaller.new nil, :executables => true
+ uninstaller = Gem::Uninstaller.new nil, executables: true
use_ui @ui do
uninstaller.remove_executables @user_spec
@@ -137,7 +137,7 @@ class TestGemUninstaller < Gem::InstallerTestCase
def test_remove_not_in_home
Dir.mkdir "#{@gemhome}2"
- uninstaller = Gem::Uninstaller.new nil, :install_dir => "#{@gemhome}2"
+ uninstaller = Gem::Uninstaller.new nil, install_dir: "#{@gemhome}2"
e = assert_raise Gem::GemNotInHomeException do
use_ui ui do
@@ -161,7 +161,7 @@ class TestGemUninstaller < Gem::InstallerTestCase
FileUtils.ln_s(@gemhome, dir)
- uninstaller = Gem::Uninstaller.new nil, :install_dir => symlinked_gem_home
+ uninstaller = Gem::Uninstaller.new nil, install_dir: symlinked_gem_home
use_ui ui do
uninstaller.remove @spec
@@ -178,7 +178,7 @@ class TestGemUninstaller < Gem::InstallerTestCase
@spec.files += %w[lib/rubygems_plugin.rb]
- Gem::Installer.at(Gem::Package.build(@spec), :force => true).install
+ Gem::Installer.at(Gem::Package.build(@spec), force: true).install
plugin_path = File.join Gem.plugindir, "a_plugin.rb"
assert File.exist?(plugin_path), "plugin not written"
@@ -195,13 +195,13 @@ class TestGemUninstaller < Gem::InstallerTestCase
@spec.files += %w[lib/rubygems_plugin.rb]
- Gem::Installer.at(Gem::Package.build(@spec), :force => true).install
+ Gem::Installer.at(Gem::Package.build(@spec), force: true).install
plugin_path = File.join Gem.plugindir, "a_plugin.rb"
assert File.exist?(plugin_path), "plugin not written"
Dir.mkdir "#{@gemhome}2"
- Gem::Uninstaller.new(nil, :install_dir => "#{@gemhome}2").remove_plugins @spec
+ Gem::Uninstaller.new(nil, install_dir: "#{@gemhome}2").remove_plugins @spec
assert File.exist?(plugin_path), "plugin unintentionally removed"
end
@@ -213,7 +213,7 @@ class TestGemUninstaller < Gem::InstallerTestCase
@spec.files += %w[lib/rubygems_plugin.rb]
- Gem::Installer.at(Gem::Package.build(@spec), :force => true).install
+ Gem::Installer.at(Gem::Package.build(@spec), force: true).install
plugin_path = File.join Gem.plugindir, "a_plugin.rb"
assert File.exist?(plugin_path), "plugin not written"
@@ -247,7 +247,7 @@ class TestGemUninstaller < Gem::InstallerTestCase
end
def test_uninstall
- uninstaller = Gem::Uninstaller.new @spec.name, :executables => true
+ uninstaller = Gem::Uninstaller.new @spec.name, executables: true
gem_dir = File.join @gemhome, "gems", @spec.full_name
@@ -274,7 +274,7 @@ class TestGemUninstaller < Gem::InstallerTestCase
install_default_gems spec
- uninstaller = Gem::Uninstaller.new spec.name, :executables => true
+ uninstaller = Gem::Uninstaller.new spec.name, executables: true
use_ui @ui do
uninstaller.uninstall
@@ -294,7 +294,7 @@ class TestGemUninstaller < Gem::InstallerTestCase
Gem::Specification.reset
- uninstaller = Gem::Uninstaller.new spec.name, :executables => true
+ uninstaller = Gem::Uninstaller.new spec.name, executables: true
ui = Gem::MockGemUi.new "1\ny\n"
use_ui ui do
@@ -322,20 +322,20 @@ create_makefile '#{@spec.name}'
use_ui @ui do
path = Gem::Package.build @spec
- installer = Gem::Installer.at path, :force => true
+ installer = Gem::Installer.at path, force: true
installer.install
end
assert_path_exist @spec.extension_dir, "sanity check"
- uninstaller = Gem::Uninstaller.new @spec.name, :executables => true
+ uninstaller = Gem::Uninstaller.new @spec.name, executables: true
uninstaller.uninstall
assert_path_not_exist @spec.extension_dir
end
def test_uninstall_nonexistent
- uninstaller = Gem::Uninstaller.new "bogus", :executables => true
+ uninstaller = Gem::Uninstaller.new "bogus", executables: true
e = assert_raise Gem::InstallError do
uninstaller.uninstall
@@ -373,8 +373,8 @@ create_makefile '#{@spec.name}'
@user_spec = Gem::Specification.find_by_name "b"
uninstaller = Gem::Uninstaller.new(@user_spec.name,
- :executables => true,
- :user_install => true)
+ executables: true,
+ user_install: true)
gem_dir = File.join @user_spec.gem_dir
@@ -398,7 +398,7 @@ create_makefile '#{@spec.name}'
Dir.mkdir "#{@gemhome}2"
Gem.use_paths "#{@gemhome}2", [@gemhome]
- uninstaller = Gem::Uninstaller.new @spec.name, :executables => true
+ uninstaller = Gem::Uninstaller.new @spec.name, executables: true
e = assert_raise Gem::InstallError do
uninstaller.uninstall
@@ -487,7 +487,7 @@ create_makefile '#{@spec.name}'
quick_gem "q", "1.0"
quick_gem "q", "1.1"
- un = Gem::Uninstaller.new("q", :version => "1.0")
+ un = Gem::Uninstaller.new("q", version: "1.0")
ui = Gem::MockGemUi.new("y\n")
use_ui ui do
@@ -513,7 +513,7 @@ create_makefile '#{@spec.name}'
quick_gem "q", "1.0"
quick_gem "q", "1.1"
- un = Gem::Uninstaller.new("q", :version => "1.0")
+ un = Gem::Uninstaller.new("q", version: "1.0")
ui = Gem::MockGemUi.new("y\n")
use_ui ui do
@@ -532,7 +532,7 @@ create_makefile '#{@spec.name}'
quick_gem "q", "1.0"
- un = Gem::Uninstaller.new("q", :version => "1.0")
+ un = Gem::Uninstaller.new("q", version: "1.0")
ui = Gem::MockGemUi.new("y\n")
use_ui ui do
@@ -551,7 +551,7 @@ create_makefile '#{@spec.name}'
quick_gem "q", "1"
- un = Gem::Uninstaller.new("q", :abort_on_dependent => true)
+ un = Gem::Uninstaller.new("q", abort_on_dependent: true)
ui = Gem::MockGemUi.new("y\n")
assert_raise Gem::DependencyRemovalException do
@@ -568,7 +568,7 @@ create_makefile '#{@spec.name}'
quick_gem "q", "1"
- un = Gem::Uninstaller.new("q", :check_dev => true)
+ un = Gem::Uninstaller.new("q", check_dev: true)
ui = Gem::MockGemUi.new("y\n")
use_ui ui do
@@ -597,7 +597,7 @@ create_makefile '#{@spec.name}'
quick_gem "q", "1"
- un = Gem::Uninstaller.new("q", :check_dev => false)
+ un = Gem::Uninstaller.new("q", check_dev: false)
ui = Gem::MockGemUi.new("y\n")
use_ui ui do
@@ -616,7 +616,7 @@ create_makefile '#{@spec.name}'
end
def test_uninstall_no_permission
- uninstaller = Gem::Uninstaller.new @spec.name, :executables => true
+ uninstaller = Gem::Uninstaller.new @spec.name, executables: true
stub_rm_r = lambda do |*args|
_path = args.shift
@@ -641,34 +641,34 @@ create_makefile '#{@spec.name}'
plugin_path = File.join Gem.plugindir, "a_plugin.rb"
@spec.version = "1"
- Gem::Installer.at(Gem::Package.build(@spec), :force => true).install
+ Gem::Installer.at(Gem::Package.build(@spec), force: true).install
refute File.exist?(plugin_path), "version without plugin installed, but plugin written"
@spec.files += %w[lib/rubygems_plugin.rb]
@spec.version = "2"
- Gem::Installer.at(Gem::Package.build(@spec), :force => true).install
+ Gem::Installer.at(Gem::Package.build(@spec), force: true).install
assert File.exist?(plugin_path), "version with plugin installed, but plugin not written"
assert_match %r{\Arequire.*a-2/lib/rubygems_plugin\.rb}, File.read(plugin_path), "written plugin has incorrect content"
@spec.version = "3"
- Gem::Installer.at(Gem::Package.build(@spec), :force => true).install
+ Gem::Installer.at(Gem::Package.build(@spec), force: true).install
assert File.exist?(plugin_path), "version with plugin installed, but plugin removed"
assert_match %r{\Arequire.*a-3/lib/rubygems_plugin\.rb}, File.read(plugin_path), "old version installed, but plugin updated"
- Gem::Uninstaller.new("a", :version => "1", :executables => true).uninstall
+ Gem::Uninstaller.new("a", version: "1", executables: true).uninstall
assert File.exist?(plugin_path), "plugin removed when old version uninstalled"
assert_match %r{\Arequire.*a-3/lib/rubygems_plugin\.rb}, File.read(plugin_path), "old version uninstalled, but plugin updated"
- Gem::Uninstaller.new("a", version: "3", :executables => true).uninstall
+ Gem::Uninstaller.new("a", version: "3", executables: true).uninstall
assert File.exist?(plugin_path), "plugin removed when old version uninstalled and another version with plugin still present"
assert_match %r{\Arequire.*a-2/lib/rubygems_plugin\.rb}, File.read(plugin_path), "latest version uninstalled, but plugin not updated to previous version"
- Gem::Uninstaller.new("a", version: "2", :executables => true).uninstall
+ Gem::Uninstaller.new("a", version: "2", executables: true).uninstall
refute File.exist?(plugin_path), "last version uninstalled, but plugin still present"
end
diff --git a/test/rubygems/test_gem_update_suggestion.rb b/test/rubygems/test_gem_update_suggestion.rb
index f74349fe24..8cb8ee57ff 100644
--- a/test/rubygems/test_gem_update_suggestion.rb
+++ b/test/rubygems/test_gem_update_suggestion.rb
@@ -15,13 +15,13 @@ class TestUpdateSuggestion < Gem::TestCase
@week = 7 * 24 * @minute
end
- def with_eglible_environment(**params)
- self.class.with_eglible_environment(**params) do
+ def with_eligible_environment(**params)
+ self.class.with_eligible_environment(**params) do
yield
end
end
- def self.with_eglible_environment(
+ def self.with_eligible_environment(
tty: true,
rubygems_version: Gem::Version.new("1.2.3"),
latest_rubygems_version: Gem::Version.new("2.0.0"),
@@ -40,7 +40,7 @@ class TestUpdateSuggestion < Gem::TestCase
Gem.ui.stub :tty?, tty do
Gem.stub :rubygems_version, rubygems_version do
Gem.stub :latest_rubygems_version, latest_rubygems_version do
- cmd.stub :ci?, ci do
+ Gem::CIDetector.stub :ci?, ci do
yield
end
end
@@ -65,145 +65,145 @@ class TestUpdateSuggestion < Gem::TestCase
end
end
- def test_eglible_for_update
- with_eglible_environment(cmd: @cmd) do
+ def test_eligible_for_update
+ with_eligible_environment(cmd: @cmd) do
Time.stub :now, 123_456_789 do
- assert @cmd.eglible_for_update?
- assert_equal Gem.configuration.last_update_check, 123_456_789
+ assert_predicate @cmd, :eligible_for_update?
+ assert_equal 123_456_789, Gem.configuration.last_update_check
# test last check is written to config file
- assert File.read(Gem.configuration.state_file_name).match("123456789")
+ assert_include File.read(Gem.configuration.state_file_name), "123456789"
end
end
end
- def test_eglible_for_update_is_not_annoying_when_new_version_is_released
+ def test_eligible_for_update_is_not_annoying_when_new_version_is_released
current_version = Gem::Version.new("1.2.0")
latest_version = current_version
- # checking for first time, it is not eglible since new version
+ # checking for first time, it is not eligible since new version
# is not released yet and stored
- with_eglible_environment(cmd: @cmd, rubygems_version: current_version, latest_rubygems_version: latest_version) do
+ with_eligible_environment(cmd: @cmd, rubygems_version: current_version, latest_rubygems_version: latest_version) do
Time.stub :now, @start_time do
- refute @cmd.eglible_for_update?
- assert_equal Gem.configuration.last_update_check, @start_time
+ refute_predicate @cmd, :eligible_for_update?
+ assert_equal @start_time, Gem.configuration.last_update_check
end
end
- # checking next week, it is not eglible since new version
+ # checking next week, it is not eligible since new version
# is not released yet and timestamp is stored
- with_eglible_environment(
+ with_eligible_environment(
cmd: @cmd,
rubygems_version: current_version,
latest_rubygems_version: latest_version,
reset_last_update_check: false
) do
Time.stub :now, @start_time + @week do
- refute @cmd.eglible_for_update?
- assert_equal Gem.configuration.last_update_check, @start_time + @week
+ refute_predicate @cmd, :eligible_for_update?
+ assert_equal @start_time + @week, Gem.configuration.last_update_check
end
end
# pretend new version is released
latest_version = Gem::Version.new("1.3.0")
- # checking later same next week, it is not eglible even new version
+ # checking later same next week, it is not eligible even new version
# is released and timestamp is not stored
- with_eglible_environment(
+ with_eligible_environment(
cmd: @cmd,
rubygems_version: current_version,
latest_rubygems_version: latest_version,
reset_last_update_check: false
) do
Time.stub :now, @start_time + @week + @minute do
- refute @cmd.eglible_for_update?
- assert_equal Gem.configuration.last_update_check, @start_time + @week
+ refute_predicate @cmd, :eligible_for_update?
+ assert_equal @start_time + @week, Gem.configuration.last_update_check
end
end
end
- def test_eglible_for_update_is_not_annoying_when_not_upgraded
- with_eglible_environment(cmd: @cmd) do
- # checking for first time, it is eglible and stored
+ def test_eligible_for_update_is_not_annoying_when_not_upgraded
+ with_eligible_environment(cmd: @cmd) do
+ # checking for first time, it is eligible and stored
Time.stub :now, @start_time do
- assert @cmd.eglible_for_update?
- assert_equal Gem.configuration.last_update_check, @start_time
+ assert_predicate @cmd, :eligible_for_update?
+ assert_equal @start_time, Gem.configuration.last_update_check
end
- # checking minute later is not eglible and not stored
+ # checking minute later is not eligible and not stored
Time.stub :now, @start_time + @minute do
- refute @cmd.eglible_for_update?
- assert_equal Gem.configuration.last_update_check, @start_time
+ refute_predicate @cmd, :eligible_for_update?
+ assert_equal @start_time, Gem.configuration.last_update_check
end
- # checking week later is eglible again and stored
+ # checking week later is eligible again and stored
Time.stub :now, @start_time + @week do
- assert @cmd.eglible_for_update?
- assert_equal Gem.configuration.last_update_check, @start_time + @week
+ assert_predicate @cmd, :eligible_for_update?
+ assert_equal @start_time + @week, Gem.configuration.last_update_check
end
end
end
- def test_eglible_for_update_prevent_config
- with_eglible_environment(cmd: @cmd) do
+ def test_eligible_for_update_prevent_config
+ with_eligible_environment(cmd: @cmd) do
original_config = Gem.configuration[:prevent_update_suggestion]
Gem.configuration[:prevent_update_suggestion] = true
- refute @cmd.eglible_for_update?
+ refute_predicate @cmd, :eligible_for_update?
ensure
Gem.configuration[:prevent_update_suggestion] = original_config
end
end
- def test_eglible_for_update_prevent_env
- with_eglible_environment(cmd: @cmd) do
+ def test_eligible_for_update_prevent_env
+ with_eligible_environment(cmd: @cmd) do
original_env = ENV["RUBYGEMS_PREVENT_UPDATE_SUGGESTION"]
ENV["RUBYGEMS_PREVENT_UPDATE_SUGGESTION"] = "yes"
- refute @cmd.eglible_for_update?
+ refute_predicate @cmd, :eligible_for_update?
ensure
ENV["RUBYGEMS_PREVENT_UPDATE_SUGGESTION"] = original_env
end
end
- def test_eglible_for_update_non_tty
- with_eglible_environment(tty: false, cmd: @cmd) do
- refute @cmd.eglible_for_update?
+ def test_eligible_for_update_non_tty
+ with_eligible_environment(tty: false, cmd: @cmd) do
+ refute_predicate @cmd, :eligible_for_update?
end
end
- def test_eglible_for_update_for_prerelease
- with_eglible_environment(rubygems_version: Gem::Version.new("1.0.0-rc1"), cmd: @cmd) do
- refute @cmd.eglible_for_update?
+ def test_eligible_for_update_for_prerelease
+ with_eligible_environment(rubygems_version: Gem::Version.new("1.0.0-rc1"), cmd: @cmd) do
+ refute_predicate @cmd, :eligible_for_update?
end
end
- def test_eglible_for_update_disabled_update
- with_eglible_environment(cmd: @cmd) do
+ def test_eligible_for_update_disabled_update
+ with_eligible_environment(cmd: @cmd) do
original_disable = Gem.disable_system_update_message
Gem.disable_system_update_message = "disabled"
- refute @cmd.eglible_for_update?
+ refute_predicate @cmd, :eligible_for_update?
ensure
Gem.disable_system_update_message = original_disable
end
end
- def test_eglible_for_update_on_ci
- with_eglible_environment(ci: true, cmd: @cmd) do
- refute @cmd.eglible_for_update?
+ def test_eligible_for_update_on_ci
+ with_eligible_environment(ci: true, cmd: @cmd) do
+ refute_predicate @cmd, :eligible_for_update?
end
end
- def test_eglible_for_update_unwrittable_config
- with_eglible_environment(cmd: @cmd) do
+ def test_eligible_for_update_unwrittable_config
+ with_eligible_environment(cmd: @cmd) do
Gem.configuration.stub :state_file_writable?, false do
- refute @cmd.eglible_for_update?
+ refute_predicate @cmd, :eligible_for_update?
end
end
end
- def test_eglible_for_update_notification_delay
- with_eglible_environment(cmd: @cmd) do
+ def test_eligible_for_update_notification_delay
+ with_eligible_environment(cmd: @cmd) do
Gem.configuration.last_update_check = Time.now.to_i
- refute @cmd.eglible_for_update?
+ refute_predicate @cmd, :eligible_for_update?
end
end
end
diff --git a/test/rubygems/test_gem_version_option.rb b/test/rubygems/test_gem_version_option.rb
index b837b794db..8b6e14fc42 100644
--- a/test/rubygems/test_gem_version_option.rb
+++ b/test/rubygems/test_gem_version_option.rb
@@ -82,10 +82,10 @@ class TestGemVersionOption < Gem::TestCase
@cmd.handle_options %w[--version >1]
expected = {
- :args => [],
- :explicit_prerelease => false,
- :prerelease => false,
- :version => Gem::Requirement.new("> 1"),
+ args: [],
+ explicit_prerelease: false,
+ prerelease: false,
+ version: Gem::Requirement.new("> 1"),
}
assert_equal expected, @cmd.options
@@ -97,10 +97,10 @@ class TestGemVersionOption < Gem::TestCase
@cmd.handle_options ["--version", "< 1, > 0.9"]
expected = {
- :args => [],
- :explicit_prerelease => false,
- :prerelease => false,
- :version => Gem::Requirement.new("< 1", "> 0.9"),
+ args: [],
+ explicit_prerelease: false,
+ prerelease: false,
+ version: Gem::Requirement.new("< 1", "> 0.9"),
}
assert_equal expected, @cmd.options
@@ -112,10 +112,10 @@ class TestGemVersionOption < Gem::TestCase
@cmd.handle_options ["--version", "< 1", "--version", "> 0.9"]
expected = {
- :args => [],
- :explicit_prerelease => false,
- :prerelease => false,
- :version => Gem::Requirement.new("< 1", "> 0.9"),
+ args: [],
+ explicit_prerelease: false,
+ prerelease: false,
+ version: Gem::Requirement.new("< 1", "> 0.9"),
}
assert_equal expected, @cmd.options
@@ -128,10 +128,10 @@ class TestGemVersionOption < Gem::TestCase
@cmd.handle_options %w[--pre --version >1]
expected = {
- :args => [],
- :explicit_prerelease => true,
- :prerelease => true,
- :version => Gem::Requirement.new("> 1"),
+ args: [],
+ explicit_prerelease: true,
+ prerelease: true,
+ version: Gem::Requirement.new("> 1"),
}
assert_equal expected, @cmd.options
@@ -143,10 +143,10 @@ class TestGemVersionOption < Gem::TestCase
@cmd.handle_options %w[--version >1.a]
expected = {
- :args => [],
- :explicit_prerelease => false,
- :prerelease => true,
- :version => Gem::Requirement.new("> 1.a"),
+ args: [],
+ explicit_prerelease: false,
+ prerelease: true,
+ version: Gem::Requirement.new("> 1.a"),
}
assert_equal expected, @cmd.options
@@ -154,10 +154,10 @@ class TestGemVersionOption < Gem::TestCase
@cmd.handle_options %w[--version >1]
expected = {
- :args => [],
- :explicit_prerelease => false,
- :prerelease => false,
- :version => Gem::Requirement.new("> 1"),
+ args: [],
+ explicit_prerelease: false,
+ prerelease: false,
+ version: Gem::Requirement.new("> 1"),
}
assert_equal expected, @cmd.options
diff --git a/test/rubygems/test_kernel.rb b/test/rubygems/test_kernel.rb
index 4c3d2d20c6..d862b26fe9 100644
--- a/test/rubygems/test_kernel.rb
+++ b/test/rubygems/test_kernel.rb
@@ -52,10 +52,20 @@ class TestGemKernel < Gem::TestCase
assert_equal 1, $:.count {|p| p.include?("a-1/lib") }
end
- def test_gem_prerelease
+ def test_gem_prerelease_is_the_only_available
quick_gem "d", "1.1.a"
- refute gem("d", ">= 1"), "release requirement must not load prerelease"
- assert gem("d", ">= 1.a"), "prerelease requirement may load prerelease"
+
+ assert gem("d", ">= 1"), "release requirement may load prerelease when sole option"
+ assert $:.one? {|p| p.include?("/d-1.1.a/lib") }
+ end
+
+ def test_release_favored_over_prerelease
+ quick_gem "d", "1.1.a"
+ quick_gem "d", "1.2"
+ gem("d", ">= 1")
+
+ refute $:.any? {|p| p.include?("/d-1.1.a/lib") }
+ assert $:.one? {|p| p.include?("/d-1.2/lib") }
end
def test_gem_env_req
diff --git a/test/rubygems/test_require.rb b/test/rubygems/test_require.rb
index 95bae66e21..30a4a477f9 100644
--- a/test/rubygems/test_require.rb
+++ b/test/rubygems/test_require.rb
@@ -471,11 +471,11 @@ class TestGemRequire < Gem::TestCase
File.write(path, code)
output = Gem::Util.popen({ "GEM_HOME" => @gemhome }, *ruby_with_rubygems_in_load_path, path).strip
- assert $?.success?
refute_empty output
assert_equal "999.99.9", output.lines[0].chomp
# Make sure only files from the newer json gem are loaded, and no files from the default json gem
assert_equal ["#{@gemhome}/gems/json-999.99.9/lib/json.rb"], output.lines.grep(%r{/gems/json-}).map(&:chomp)
+ assert $?.success?
end
def test_default_gem_and_normal_gem
@@ -489,6 +489,17 @@ class TestGemRequire < Gem::TestCase
assert_equal %w[default-3.0], loaded_spec_names
end
+ def test_normal_gem_does_not_shadow_default_gem
+ default_gem_spec = new_default_spec("foo", "2.0", nil, "foo.rb")
+ install_default_gems(default_gem_spec)
+
+ normal_gem_spec = util_spec("fake-foo", "3.0", nil, "lib/foo.rb")
+ install_specs(normal_gem_spec)
+
+ assert_require "foo"
+ assert_equal %w[foo-2.0], loaded_spec_names
+ end
+
def test_normal_gems_with_overridden_load_error_message
normal_gem_spec = util_spec("normal", "3.0", nil, "lib/normal/gem.rb")
@@ -529,6 +540,65 @@ class TestGemRequire < Gem::TestCase
assert_equal %w[default-3.0.0.rc2], loaded_spec_names
end
+ def test_default_gem_with_unresolved_gems_depending_on_it
+ my_http_old = util_spec "my-http", "0.1.1", nil, "lib/my/http.rb"
+ install_gem my_http_old
+
+ my_http_default = new_default_spec "my-http", "0.3.0", nil, "my/http.rb"
+ install_default_gems my_http_default
+
+ faraday_1 = util_spec "faraday", "1", { "my-http" => ">= 0" }
+ install_gem faraday_1
+
+ faraday_2 = util_spec "faraday", "2", { "my-http" => ">= 0" }
+ install_gem faraday_2
+
+ chef = util_spec "chef", "1", { "faraday" => [">= 1", "< 3"] }, "lib/chef.rb"
+ install_gem chef
+
+ assert_require "chef"
+ assert_require "my/http"
+ end
+
+ def test_default_gem_required_circulary_with_unresolved_gems_depending_on_it
+ my_http_old = util_spec "my-http", "0.1.1", nil, "lib/my/http.rb"
+ install_gem my_http_old
+
+ my_http_default = new_default_spec "my-http", "0.3.0", nil, "my/http.rb"
+ my_http_default_path = File.join(@tempdir, "default_gems", "lib", "my/http.rb")
+ install_default_gems my_http_default
+ File.write(my_http_default_path, 'require "my/http"')
+
+ faraday_1 = util_spec "faraday", "1", { "my-http" => ">= 0" }
+ install_gem faraday_1
+
+ faraday_2 = util_spec "faraday", "2", { "my-http" => ">= 0" }
+ install_gem faraday_2
+
+ chef = util_spec "chef", "1", { "faraday" => [">= 1", "< 3"] }, "lib/chef.rb"
+ install_gem chef
+
+ assert_require "chef"
+
+ out, err = capture_output do
+ assert_require "my/http"
+ end
+
+ assert_empty out
+
+ circular_require_warning = false
+
+ err_lines = err.split("\n").reject do |line|
+ if line.include?("circular require")
+ circular_require_warning = true
+ elsif circular_require_warning # ignore backtrace lines for circular require warning
+ circular_require_warning = line.start_with?(/[\s]/)
+ end
+ end
+
+ assert_empty err_lines
+ end
+
def loaded_spec_names
Gem.loaded_specs.values.map(&:full_name).sort
end
diff --git a/test/rubygems/test_rubygems.rb b/test/rubygems/test_rubygems.rb
index ae8959c4ed..ec195b65cd 100644
--- a/test/rubygems/test_rubygems.rb
+++ b/test/rubygems/test_rubygems.rb
@@ -4,7 +4,7 @@ require_relative "helper"
class GemTest < Gem::TestCase
def test_rubygems_normal_behaviour
- _ = Gem::Util.popen(*ruby_with_rubygems_in_load_path, "-e", "'require \"rubygems\"'", { :err => [:child, :out] }).strip
+ _ = Gem::Util.popen(*ruby_with_rubygems_in_load_path, "-e", "'require \"rubygems\"'", { err: [:child, :out] }).strip
assert $?.success?
end
@@ -15,9 +15,9 @@ class GemTest < Gem::TestCase
intentionally_not_implemented_method
RUBY
- output = Gem::Util.popen(*ruby_with_rubygems_and_fake_operating_system_in_load_path(path), "-e", "'require \"rubygems\"'", { :err => [:child, :out] }).strip
+ output = Gem::Util.popen(*ruby_with_rubygems_and_fake_operating_system_in_load_path(path), "-e", "'require \"rubygems\"'", { err: [:child, :out] }).strip
assert !$?.success?
- assert_includes output, "undefined local variable or method `intentionally_not_implemented_method'"
+ assert_match(/undefined local variable or method [`']intentionally_not_implemented_method'/, output)
assert_includes output, "Loading the #{operating_system_rb_at(path)} file caused an error. " \
"This file is owned by your OS, not by rubygems upstream. " \
"Please find out which OS package this file belongs to and follow the guidelines from your OS to report " \
@@ -42,7 +42,7 @@ class GemTest < Gem::TestCase
*ruby_with_rubygems_and_fake_operating_system_in_load_path(path),
"-e",
"require \"rubygems\"; puts Gem::Specification.stubs.map(&:full_name)",
- { :err => [:child, :out] }
+ { err: [:child, :out] }
).strip
begin
assert_empty output
diff --git a/test/rubygems/test_webauthn_listener.rb b/test/rubygems/test_webauthn_listener.rb
index 81cfd2fc61..08edabceb2 100644
--- a/test/rubygems/test_webauthn_listener.rb
+++ b/test/rubygems/test_webauthn_listener.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
require_relative "helper"
-require "rubygems/webauthn_listener"
+require "rubygems/gemcutter_utilities/webauthn_listener"
+require "rubygems/gemcutter_utilities"
class WebauthnListenerTest < Gem::TestCase
def setup
@@ -16,17 +17,33 @@ class WebauthnListenerTest < Gem::TestCase
super
end
+ def test_listener_thread_retreives_otp_code
+ thread = Gem::GemcutterUtilities::WebauthnListener.listener_thread(Gem.host, @server)
+ Gem::MockBrowser.get Gem::URI("http://localhost:#{@port}?code=xyz")
+
+ thread.join
+ assert_equal "xyz", thread[:otp]
+ end
+
+ def test_listener_thread_sets_error
+ thread = Gem::GemcutterUtilities::WebauthnListener.listener_thread(Gem.host, @server)
+ Gem::MockBrowser.post Gem::URI("http://localhost:#{@port}?code=xyz")
+
+ thread.join
+ assert_equal "Security device verification failed: Invalid HTTP method POST received.", thread[:error].message
+ end
+
def test_wait_for_otp_code_get_follows_options
wait_for_otp_code
- assert Gem::MockBrowser.options(URI("http://localhost:#{@port}?code=xyz")).is_a? Net::HTTPNoContent
- assert Gem::MockBrowser.get(URI("http://localhost:#{@port}?code=xyz")).is_a? Net::HTTPOK
+ assert Gem::MockBrowser.options(Gem::URI("http://localhost:#{@port}?code=xyz")).is_a? Gem::Net::HTTPNoContent
+ assert Gem::MockBrowser.get(Gem::URI("http://localhost:#{@port}?code=xyz")).is_a? Gem::Net::HTTPOK
end
def test_wait_for_otp_code_options_request
wait_for_otp_code
- response = Gem::MockBrowser.options URI("http://localhost:#{@port}?code=xyz")
+ response = Gem::MockBrowser.options Gem::URI("http://localhost:#{@port}?code=xyz")
- assert response.is_a? Net::HTTPNoContent
+ assert response.is_a? Gem::Net::HTTPNoContent
assert_equal Gem.host, response["access-control-allow-origin"]
assert_equal "POST", response["access-control-allow-methods"]
assert_equal "Content-Type, Authorization, x-csrf-token", response["access-control-allow-headers"]
@@ -35,10 +52,10 @@ class WebauthnListenerTest < Gem::TestCase
def test_wait_for_otp_code_get_request
wait_for_otp_code
- response = Gem::MockBrowser.get URI("http://localhost:#{@port}?code=xyz")
+ response = Gem::MockBrowser.get Gem::URI("http://localhost:#{@port}?code=xyz")
- assert response.is_a? Net::HTTPOK
- assert_equal "text/plain", response["Content-Type"]
+ assert response.is_a? Gem::Net::HTTPOK
+ assert_equal "text/plain; charset=utf-8", response["Content-Type"]
assert_equal "7", response["Content-Length"]
assert_equal Gem.host, response["access-control-allow-origin"]
assert_equal "POST", response["access-control-allow-methods"]
@@ -52,10 +69,10 @@ class WebauthnListenerTest < Gem::TestCase
def test_wait_for_otp_code_invalid_post_req_method
wait_for_otp_code_expect_error_with_message("Security device verification failed: Invalid HTTP method POST received.")
- response = Gem::MockBrowser.post URI("http://localhost:#{@port}?code=xyz")
+ response = Gem::MockBrowser.post Gem::URI("http://localhost:#{@port}?code=xyz")
assert response
- assert response.is_a? Net::HTTPMethodNotAllowed
+ assert response.is_a? Gem::Net::HTTPMethodNotAllowed
assert_equal "GET, OPTIONS", response["allow"]
assert_equal "close", response["Connection"]
@@ -65,9 +82,9 @@ class WebauthnListenerTest < Gem::TestCase
def test_wait_for_otp_code_incorrect_path
wait_for_otp_code_expect_error_with_message("Security device verification failed: Page at /path not found.")
- response = Gem::MockBrowser.post URI("http://localhost:#{@port}/path?code=xyz")
+ response = Gem::MockBrowser.post Gem::URI("http://localhost:#{@port}/path?code=xyz")
- assert response.is_a? Net::HTTPNotFound
+ assert response.is_a? Gem::Net::HTTPNotFound
assert_equal "close", response["Connection"]
@thread.join
@@ -76,10 +93,10 @@ class WebauthnListenerTest < Gem::TestCase
def test_wait_for_otp_code_no_params_response
wait_for_otp_code_expect_error_with_message("Security device verification failed: Did not receive OTP from https://rubygems.org.")
- response = Gem::MockBrowser.get URI("http://localhost:#{@port}")
+ response = Gem::MockBrowser.get Gem::URI("http://localhost:#{@port}")
- assert response.is_a? Net::HTTPBadRequest
- assert_equal "text/plain", response["Content-Type"]
+ assert response.is_a? Gem::Net::HTTPBadRequest
+ assert_equal "text/plain; charset=utf-8", response["Content-Type"]
assert_equal "22", response["Content-Length"]
assert_equal "close", response["Connection"]
assert_equal "missing code parameter", response.body
@@ -90,10 +107,10 @@ class WebauthnListenerTest < Gem::TestCase
def test_wait_for_otp_code_incorrect_params
wait_for_otp_code_expect_error_with_message("Security device verification failed: Did not receive OTP from https://rubygems.org.")
- response = Gem::MockBrowser.get URI("http://localhost:#{@port}?param=xyz")
+ response = Gem::MockBrowser.get Gem::URI("http://localhost:#{@port}?param=xyz")
- assert response.is_a? Net::HTTPBadRequest
- assert_equal "text/plain", response["Content-Type"]
+ assert response.is_a? Gem::Net::HTTPBadRequest
+ assert_equal "text/plain; charset=utf-8", response["Content-Type"]
assert_equal "22", response["Content-Length"]
assert_equal "close", response["Connection"]
assert_equal "missing code parameter", response.body
@@ -106,7 +123,7 @@ class WebauthnListenerTest < Gem::TestCase
def wait_for_otp_code
@thread = Thread.new do
- Thread.current[:otp] = Gem::WebauthnListener.wait_for_otp_code(Gem.host, @server)
+ Thread.current[:otp] = Gem::GemcutterUtilities::WebauthnListener.new(Gem.host).wait_for_otp_code(@server)
end
@thread.abort_on_exception = true
@thread.report_on_exception = false
@@ -115,7 +132,7 @@ class WebauthnListenerTest < Gem::TestCase
def wait_for_otp_code_expect_error_with_message(message)
@thread = Thread.new do
error = assert_raise Gem::WebauthnVerificationError do
- Thread.current[:otp] = Gem::WebauthnListener.wait_for_otp_code(Gem.host, @server)
+ Thread.current[:otp] = Gem::GemcutterUtilities::WebauthnListener.new(Gem.host).wait_for_otp_code(@server)
end
assert_equal message, error.message
diff --git a/test/rubygems/test_webauthn_listener_response.rb b/test/rubygems/test_webauthn_listener_response.rb
index 79e88f1f02..377e5bfe5a 100644
--- a/test/rubygems/test_webauthn_listener_response.rb
+++ b/test/rubygems/test_webauthn_listener_response.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require_relative "helper"
-require "rubygems/webauthn_listener/response"
+require "rubygems/gemcutter_utilities/webauthn_listener/response"
class WebauthnListenerResponseTest < Gem::TestCase
def setup
@@ -10,7 +10,7 @@ class WebauthnListenerResponseTest < Gem::TestCase
end
def test_ok_response_to_s
- to_s = Gem::WebauthnListener::OkResponse.new(@host).to_s
+ to_s = Gem::GemcutterUtilities::WebauthnListener::OkResponse.new(@host).to_s
expected_to_s = <<~RESPONSE
HTTP/1.1 200 OK\r
@@ -18,7 +18,7 @@ class WebauthnListenerResponseTest < Gem::TestCase
access-control-allow-origin: rubygems.example\r
access-control-allow-methods: POST\r
access-control-allow-headers: Content-Type, Authorization, x-csrf-token\r
- content-type: text/plain\r
+ content-type: text/plain; charset=utf-8\r
content-length: 7\r
\r
success
@@ -28,7 +28,7 @@ class WebauthnListenerResponseTest < Gem::TestCase
end
def test_no_to_s_response_to_s
- to_s = Gem::WebauthnListener::NoContentResponse.new(@host).to_s
+ to_s = Gem::GemcutterUtilities::WebauthnListener::NoContentResponse.new(@host).to_s
expected_to_s = <<~RESPONSE
HTTP/1.1 204 No Content\r
@@ -43,7 +43,7 @@ class WebauthnListenerResponseTest < Gem::TestCase
end
def test_method_not_allowed_response_to_s
- to_s = Gem::WebauthnListener::MethodNotAllowedResponse.new(@host).to_s
+ to_s = Gem::GemcutterUtilities::WebauthnListener::MethodNotAllowedResponse.new(@host).to_s
expected_to_s = <<~RESPONSE
HTTP/1.1 405 Method Not Allowed\r
@@ -59,7 +59,7 @@ class WebauthnListenerResponseTest < Gem::TestCase
end
def test_method_not_found_response_to_s
- to_s = Gem::WebauthnListener::NotFoundResponse.new(@host).to_s
+ to_s = Gem::GemcutterUtilities::WebauthnListener::NotFoundResponse.new(@host).to_s
expected_to_s = <<~RESPONSE
HTTP/1.1 404 Not Found\r
@@ -74,7 +74,7 @@ class WebauthnListenerResponseTest < Gem::TestCase
end
def test_bad_request_response_to_s
- to_s = Gem::WebauthnListener::BadRequestResponse.new(@host).to_s
+ to_s = Gem::GemcutterUtilities::WebauthnListener::BadRequestResponse.new(@host).to_s
expected_to_s = <<~RESPONSE
HTTP/1.1 400 Bad Request\r
@@ -82,7 +82,7 @@ class WebauthnListenerResponseTest < Gem::TestCase
access-control-allow-origin: rubygems.example\r
access-control-allow-methods: POST\r
access-control-allow-headers: Content-Type, Authorization, x-csrf-token\r
- content-type: text/plain\r
+ content-type: text/plain; charset=utf-8\r
content-length: 22\r
\r
missing code parameter
diff --git a/test/rubygems/test_webauthn_poller.rb b/test/rubygems/test_webauthn_poller.rb
new file mode 100644
index 0000000000..23290d8ea1
--- /dev/null
+++ b/test/rubygems/test_webauthn_poller.rb
@@ -0,0 +1,124 @@
+# frozen_string_literal: true
+
+require_relative "helper"
+require "rubygems/gemcutter_utilities/webauthn_poller"
+require "rubygems/gemcutter_utilities"
+
+class WebauthnPollerTest < Gem::TestCase
+ def setup
+ super
+
+ @host = Gem.host
+ @webauthn_url = "#{@host}/api/v1/webauthn_verification/odow34b93t6aPCdY"
+ @fetcher = Gem::FakeFetcher.new
+ Gem::RemoteFetcher.fetcher = @fetcher
+ @credentials = {
+ email: "email@example.com",
+ password: "password",
+ }
+ end
+
+ def test_poll_thread_success
+ @fetcher.data["#{@webauthn_url}/status.json"] = Gem::HTTPResponseFactory.create(
+ body: "{\"status\":\"success\",\"code\":\"Uvh6T57tkWuUnWYo\"}",
+ code: 200,
+ msg: "OK"
+ )
+
+ thread = Gem::GemcutterUtilities::WebauthnPoller.poll_thread({}, @host, @webauthn_url, @credentials)
+ thread.join
+
+ assert_equal thread[:otp], "Uvh6T57tkWuUnWYo"
+ end
+
+ def test_poll_thread_webauthn_verification_error
+ @fetcher.data["#{@webauthn_url}/status.json"] = Gem::HTTPResponseFactory.create(
+ body: "HTTP Basic: Access denied.",
+ code: 401,
+ msg: "Unauthorized"
+ )
+
+ thread = Gem::GemcutterUtilities::WebauthnPoller.poll_thread({}, @host, @webauthn_url, @credentials)
+ thread.join
+
+ assert_equal thread[:error].message, "Security device verification failed: Unauthorized"
+ end
+
+ def test_poll_thread_timeout_error
+ raise_error = ->(*_args) { raise Gem::Timeout::Error, "execution expired" }
+ Gem::Timeout.stub(:timeout, raise_error) do
+ thread = Gem::GemcutterUtilities::WebauthnPoller.poll_thread({}, @host, @webauthn_url, @credentials)
+ thread.join
+ assert_equal thread[:error].message, "execution expired"
+ end
+ end
+
+ def test_poll_for_otp_success
+ @fetcher.data["#{@webauthn_url}/status.json"] = Gem::HTTPResponseFactory.create(
+ body: "{\"status\":\"success\",\"code\":\"Uvh6T57tkWuUnWYo\"}",
+ code: 200,
+ msg: "OK"
+ )
+
+ otp = Gem::GemcutterUtilities::WebauthnPoller.new({}, @host).poll_for_otp(@webauthn_url, @credentials)
+
+ assert_equal otp, "Uvh6T57tkWuUnWYo"
+ end
+
+ def test_poll_for_otp_pending_sleeps
+ @fetcher.data["#{@webauthn_url}/status.json"] = Gem::HTTPResponseFactory.create(
+ body: "{\"status\":\"pending\",\"message\":\"Security device authentication is still pending.\"}",
+ code: 200,
+ msg: "OK"
+ )
+
+ assert_raise Gem::Timeout::Error do
+ Gem::Timeout.timeout(0.1) do
+ Gem::GemcutterUtilities::WebauthnPoller.new({}, @host).poll_for_otp(@webauthn_url, @credentials)
+ end
+ end
+ end
+
+ def test_poll_for_otp_not_http_success
+ @fetcher.data["#{@webauthn_url}/status.json"] = Gem::HTTPResponseFactory.create(
+ body: "HTTP Basic: Access denied.",
+ code: 401,
+ msg: "Unauthorized"
+ )
+
+ error = assert_raise Gem::WebauthnVerificationError do
+ Gem::GemcutterUtilities::WebauthnPoller.new({}, @host).poll_for_otp(@webauthn_url, @credentials)
+ end
+
+ assert_equal error.message, "Security device verification failed: Unauthorized"
+ end
+
+ def test_poll_for_otp_invalid_format
+ @fetcher.data["#{@webauthn_url}/status.json"] = Gem::HTTPResponseFactory.create(
+ body: "{}",
+ code: 200,
+ msg: "OK"
+ )
+
+ error = assert_raise Gem::WebauthnVerificationError do
+ Gem::GemcutterUtilities::WebauthnPoller.new({}, @host).poll_for_otp(@webauthn_url, @credentials)
+ end
+
+ assert_equal error.message, "Security device verification failed: Invalid response from server"
+ end
+
+ def test_poll_for_otp_invalid_status
+ @fetcher.data["#{@webauthn_url}/status.json"] = Gem::HTTPResponseFactory.create(
+ body: "{\"status\":\"expired\",\"message\":\"The token in the link you used has either expired or been used already.\"}",
+ code: 200,
+ msg: "OK"
+ )
+
+ error = assert_raise Gem::WebauthnVerificationError do
+ Gem::GemcutterUtilities::WebauthnPoller.new({}, @host).poll_for_otp(@webauthn_url, @credentials)
+ end
+
+ assert_equal error.message,
+ "Security device verification failed: The token in the link you used has either expired or been used already."
+ end
+end
diff --git a/test/rubygems/utilities.rb b/test/rubygems/utilities.rb
index 6f5958c07c..357379f88d 100644
--- a/test/rubygems/utilities.rb
+++ b/test/rubygems/utilities.rb
@@ -40,16 +40,16 @@ class Gem::FakeFetcher
end
def find_data(path)
- return Gem.read_binary path.path if URI === path && path.scheme == "file"
+ return Gem.read_binary path.path if Gem::URI === path && path.scheme == "file"
- if URI === path && "URI::#{path.scheme.upcase}" != path.class.name
+ if Gem::URI === path && "Gem::URI::#{path.scheme.upcase}" != path.class.name
raise ArgumentError,
"mismatch for scheme #{path.scheme} and class #{path.class}"
end
path = path.to_s
@paths << path
- raise ArgumentError, "need full URI" unless path.start_with?("https://", "http://")
+ raise ArgumentError, "need full Gem::URI" unless path.start_with?("https://", "http://")
unless @data.key? path
raise Gem::RemoteFetcher::FetchError.new("no data for #{path}", path)
@@ -65,7 +65,7 @@ class Gem::FakeFetcher
def create_response(uri)
data = find_data(uri)
response = data.respond_to?(:call) ? data.call : data
- raise TypeError, "#{response.class} is not a type of Net::HTTPResponse" unless response.is_a?(Net::HTTPResponse)
+ raise TypeError, "#{response.class} is not a type of Gem::Net::HTTPResponse" unless response.is_a?(Gem::Net::HTTPResponse)
response
end
@@ -164,7 +164,7 @@ class Gem::FakeFetcher
end
##
-# The HTTPResponseFactory allows easy creation of Net::HTTPResponse instances in RubyGems tests:
+# The HTTPResponseFactory allows easy creation of Gem::Net::HTTPResponse instances in RubyGems tests:
#
# Example:
#
@@ -178,7 +178,7 @@ end
class Gem::HTTPResponseFactory
def self.create(body:, code:, msg:, headers: {})
- response = Net::HTTPResponse.send(:response_class, code.to_s).new("1.0", code.to_s, msg)
+ response = Gem::Net::HTTPResponse.send(:response_class, code.to_s).new("1.0", code.to_s, msg)
response.instance_variable_set(:@body, body)
response.instance_variable_set(:@read, true)
headers.each {|name, value| response[name] = value }
@@ -194,29 +194,30 @@ end
# Example:
#
# # Sends a get request to http://localhost:5678
-# Gem::MockBrowser.get URI("http://localhost:5678")
+# Gem::MockBrowser.get Gem::URI("http://localhost:5678")
#
# See RubyGems' tests for more examples of MockBrowser.
#
class Gem::MockBrowser
def self.options(uri)
- options = Net::HTTP::Options.new(uri)
- Net::HTTP.start(uri.hostname, uri.port) do |http|
+ options = Gem::Net::HTTP::Options.new(uri)
+ Gem::Net::HTTP.start(uri.hostname, uri.port) do |http|
http.request(options)
end
end
def self.get(uri)
- get = Net::HTTP::Get.new(uri)
- Net::HTTP.start(uri.hostname, uri.port) do |http|
+ get = Gem::Net::HTTP::Get.new(uri)
+ Gem::Net::HTTP.start(uri.hostname, uri.port) do |http|
http.request(get)
end
end
- def self.post(uri)
- post = Net::HTTP::Post.new(uri)
- Net::HTTP.start(uri.hostname, uri.port) do |http|
+ def self.post(uri, content_type: "application/x-www-form-urlencoded")
+ headers = { "content-type" => content_type } if content_type
+ post = Gem::Net::HTTP::Post.new(uri, headers)
+ Gem::Net::HTTP.start(uri.hostname, uri.port) do |http|
http.request(post)
end
end
@@ -367,12 +368,12 @@ class Gem::TestCase::SpecFetcherSetup
begin
gem_repo = @test.gem_repo
@test.gem_repo = @repository
- @test.uri = URI @repository
+ @test.uri = Gem::URI @repository
@test.util_setup_spec_fetcher(*@downloaded)
ensure
@test.gem_repo = gem_repo
- @test.uri = URI gem_repo
+ @test.uri = Gem::URI gem_repo
end
@gems.each do |spec, gem|
diff --git a/test/runner.rb b/test/runner.rb
index 2f3267f6c3..c34517cbf2 100644
--- a/test/runner.rb
+++ b/test/runner.rb
@@ -1,16 +1,5 @@
# frozen_string_literal: true
-# Should be done in rubygems test files?
-ENV["GEM_SKIP"] = "".freeze
-ENV.delete("RUBY_CODESIGN")
-
-Warning[:experimental] = false
-
-gem_path = [
- File.realdirpath(".bundle"),
- File.realdirpath("../.bundle", __dir__),
-]
-ENV["GEM_PATH"] = gem_path.join(File::PATH_SEPARATOR)
-ENV["GEM_HOME"] = gem_path.first
+# NOTE: Do not add any settings here for test-all. Instead, please add it to ../tool/test/init.rb.
require_relative '../tool/test/runner'
diff --git a/test/fixtures/fake_sorted_set_gem/sorted_set.rb b/test/set/fixtures/fake_sorted_set_gem/sorted_set.rb
index f45a766303..f45a766303 100644
--- a/test/fixtures/fake_sorted_set_gem/sorted_set.rb
+++ b/test/set/fixtures/fake_sorted_set_gem/sorted_set.rb
diff --git a/test/test_set.rb b/test/set/test_set.rb
index 49dc58ef7b..49dc58ef7b 100644
--- a/test/test_set.rb
+++ b/test/set/test_set.rb
diff --git a/test/test_sorted_set.rb b/test/set/test_sorted_set.rb
index f7ad7af299..f7ad7af299 100644
--- a/test/test_sorted_set.rb
+++ b/test/set/test_sorted_set.rb
diff --git a/test/socket/test_addrinfo.rb b/test/socket/test_addrinfo.rb
index 832a1f8d05..c61764d76d 100644
--- a/test/socket/test_addrinfo.rb
+++ b/test/socket/test_addrinfo.rb
@@ -103,7 +103,7 @@ class TestSocketAddrinfo < Test::Unit::TestCase
end
def test_error_message
- e = assert_raise_with_message(SocketError, /getaddrinfo/) do
+ e = assert_raise_with_message(Socket::ResolutionError, /getaddrinfo/) do
Addrinfo.ip("...")
end
m = e.message
@@ -357,7 +357,7 @@ class TestSocketAddrinfo < Test::Unit::TestCase
ai = Addrinfo.unix("/testdir/sock").family_addrinfo("/testdir/sock2")
assert_equal("/testdir/sock2", ai.unix_path)
assert_equal(Socket::SOCK_STREAM, ai.socktype)
- assert_raise(SocketError) { Addrinfo.tcp("0.0.0.0", 4649).family_addrinfo("::1", 80) }
+ assert_raise(Socket::ResolutionError) { Addrinfo.tcp("0.0.0.0", 4649).family_addrinfo("::1", 80) }
end
def random_port
diff --git a/test/socket/test_socket.rb b/test/socket/test_socket.rb
index ab4563dce2..6a057e866f 100644
--- a/test/socket/test_socket.rb
+++ b/test/socket/test_socket.rb
@@ -91,20 +91,20 @@ class TestSocket < Test::Unit::TestCase
def test_getaddrinfo
# This should not send a DNS query because AF_UNIX.
- assert_raise(SocketError) { Socket.getaddrinfo("www.kame.net", 80, "AF_UNIX") }
+ assert_raise(Socket::ResolutionError) { Socket.getaddrinfo("www.kame.net", 80, "AF_UNIX") }
end
def test_getaddrinfo_raises_no_errors_on_port_argument_of_0 # [ruby-core:29427]
assert_nothing_raised('[ruby-core:29427]'){ Socket.getaddrinfo('localhost', 0, Socket::AF_INET, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME) }
assert_nothing_raised('[ruby-core:29427]'){ Socket.getaddrinfo('localhost', '0', Socket::AF_INET, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME) }
assert_nothing_raised('[ruby-core:29427]'){ Socket.getaddrinfo('localhost', '00', Socket::AF_INET, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME) }
- assert_raise(SocketError, '[ruby-core:29427]'){ Socket.getaddrinfo(nil, nil, Socket::AF_INET, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME) }
+ assert_raise(Socket::ResolutionError, '[ruby-core:29427]'){ Socket.getaddrinfo(nil, nil, Socket::AF_INET, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME) }
assert_nothing_raised('[ruby-core:29427]'){ TCPServer.open('localhost', 0) {} }
end
def test_getnameinfo
- assert_raise(SocketError) { Socket.getnameinfo(["AF_UNIX", 80, "0.0.0.0"]) }
+ assert_raise(Socket::ResolutionError) { Socket.getnameinfo(["AF_UNIX", 80, "0.0.0.0"]) }
assert_raise(ArgumentError) {Socket.getnameinfo(["AF_INET", "http\0", "example.net"])}
assert_raise(ArgumentError) {Socket.getnameinfo(["AF_INET", "http", "example.net\0"])}
end
@@ -447,13 +447,12 @@ class TestSocket < Test::Unit::TestCase
omit "UDP server is no response: #{$!}"
ensure
if th
- if skipped
- Thread.kill th unless th.join(10)
- else
+ unless skipped
Addrinfo.udp("127.0.0.1", port).connect {|s| s.sendmsg "exit" }
- unless th.join(10)
- Thread.kill th
- th.join(10)
+ end
+ unless th.join(10)
+ th.kill.join(10)
+ unless skipped
raise "thread killed"
end
end
@@ -497,6 +496,7 @@ class TestSocket < Test::Unit::TestCase
assert(stamp.cmsg_is?(:SOCKET, type))
w.close # stop th
n = th.value
+ th = nil
n > 1 and
warn "UDP packet loss for #{type} over loopback, #{n} tries needed"
t2 = Time.now.strftime("%Y-%m-%d")
@@ -505,6 +505,10 @@ class TestSocket < Test::Unit::TestCase
t = stamp.timestamp
assert_match(pat, t.strftime("%Y-%m-%d"))
stamp
+ ensure
+ if th and !th.join(10)
+ th.kill.join(10)
+ end
end
end
@@ -766,4 +770,251 @@ class TestSocket < Test::Unit::TestCase
s2.close
end
+ def test_resolurion_error_error_code
+ begin
+ Socket.getaddrinfo("example.com", 80, "AF_UNIX")
+ rescue => e
+ assert_include([Socket::EAI_FAMILY, Socket::EAI_FAIL], e.error_code)
+ end
+ end
+
+ def test_tcp_socket_v6_hostname_resolved_earlier
+ opts = %w[-rsocket -W1]
+ assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}"
+
+ begin;
+ begin
+ server = TCPServer.new("::1", 0)
+ rescue Errno::EADDRNOTAVAIL # IPv6 is not supported
+ exit
+ end
+
+ server_thread = Thread.new { server.accept }
+ port = server.addr[1]
+
+ Addrinfo.define_singleton_method(:getaddrinfo) do |_, _, family, *_|
+ case family
+ when Socket::AF_INET6 then [Addrinfo.tcp("::1", port)]
+ when Socket::AF_INET then sleep(10); [Addrinfo.tcp("127.0.0.1", port)]
+ end
+ end
+
+ socket = Socket.tcp("localhost", port)
+ assert_true(socket.remote_address.ipv6?)
+ server_thread.value.close
+ server.close
+ socket.close if socket && !socket.closed?
+ end;
+ end
+
+ def test_tcp_socket_v4_hostname_resolved_earlier
+ opts = %w[-rsocket -W1]
+ assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}"
+
+ begin;
+ server = TCPServer.new("127.0.0.1", 0)
+ port = server.addr[1]
+
+ Addrinfo.define_singleton_method(:getaddrinfo) do |_, _, family, *_|
+ case family
+ when Socket::AF_INET6 then sleep(10); [Addrinfo.tcp("::1", port)]
+ when Socket::AF_INET then [Addrinfo.tcp("127.0.0.1", port)]
+ end
+ end
+
+ server_thread = Thread.new { server.accept }
+ socket = Socket.tcp("localhost", port)
+ assert_true(socket.remote_address.ipv4?)
+ server_thread.value.close
+ server.close
+ socket.close if socket && !socket.closed?
+ end;
+ end
+
+ def test_tcp_socket_v6_hostname_resolved_in_resolution_delay
+ opts = %w[-rsocket -W1]
+ assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}"
+
+ begin;
+ begin
+ server = TCPServer.new("::1", 0)
+ rescue Errno::EADDRNOTAVAIL # IPv6 is not supported
+ exit
+ end
+
+ port = server.addr[1]
+ delay_time = 0.025 # Socket::RESOLUTION_DELAY (private) is 0.05
+
+ Addrinfo.define_singleton_method(:getaddrinfo) do |_, _, family, *_|
+ case family
+ when Socket::AF_INET6 then sleep(delay_time); [Addrinfo.tcp("::1", port)]
+ when Socket::AF_INET then [Addrinfo.tcp("127.0.0.1", port)]
+ end
+ end
+
+ server_thread = Thread.new { server.accept }
+ socket = Socket.tcp("localhost", port)
+ assert_true(socket.remote_address.ipv6?)
+ server_thread.value.close
+ server.close
+ socket.close if socket && !socket.closed?
+ end;
+ end
+
+ def test_tcp_socket_v6_hostname_resolved_earlier_and_v6_server_is_not_listening
+ opts = %w[-rsocket -W1]
+ assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}"
+
+ begin;
+ ipv4_address = "127.0.0.1"
+ ipv4_server = Socket.new(Socket::AF_INET, :STREAM)
+ ipv4_server.bind(Socket.pack_sockaddr_in(0, ipv4_address))
+ port = ipv4_server.connect_address.ip_port
+
+ Addrinfo.define_singleton_method(:getaddrinfo) do |_, _, family, *_|
+ case family
+ when Socket::AF_INET6 then [Addrinfo.tcp("::1", port)]
+ when Socket::AF_INET then sleep(0.001); [Addrinfo.tcp(ipv4_address, port)]
+ end
+ end
+
+ ipv4_server_thread = Thread.new { ipv4_server.listen(1); ipv4_server.accept }
+ socket = Socket.tcp("localhost", port)
+ assert_equal(ipv4_address, socket.remote_address.ip_address)
+
+ accepted, _ = ipv4_server_thread.value
+ accepted.close
+ ipv4_server.close
+ socket.close if socket && !socket.closed?
+ end;
+ end
+
+ def test_tcp_socket_resolv_timeout
+ opts = %w[-rsocket -W1]
+ assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}"
+
+ begin;
+ Addrinfo.define_singleton_method(:getaddrinfo) { |*_| sleep }
+ port = TCPServer.new("localhost", 0).addr[1]
+
+ assert_raise(Errno::ETIMEDOUT) do
+ Socket.tcp("localhost", port, resolv_timeout: 0.01)
+ end
+ end;
+ end
+
+ def test_tcp_socket_resolv_timeout_with_connection_failure
+ opts = %w[-rsocket -W1]
+ assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}"
+
+ begin;
+ server = TCPServer.new("127.0.0.1", 12345)
+ _, port, = server.addr
+
+ Addrinfo.define_singleton_method(:getaddrinfo) do |_, _, family, *_|
+ if family == Socket::AF_INET6
+ sleep
+ else
+ [Addrinfo.tcp("127.0.0.1", port)]
+ end
+ end
+
+ server.close
+
+ assert_raise(Errno::ETIMEDOUT) do
+ Socket.tcp("localhost", port, resolv_timeout: 0.01)
+ end
+ end;
+ end
+
+ def test_tcp_socket_one_hostname_resolution_succeeded_at_least
+ opts = %w[-rsocket -W1]
+ assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}"
+
+ begin;
+ begin
+ server = TCPServer.new("::1", 0)
+ rescue Errno::EADDRNOTAVAIL # IPv6 is not supported
+ exit
+ end
+
+ port = server.addr[1]
+
+ Addrinfo.define_singleton_method(:getaddrinfo) do |_, _, family, *_|
+ case family
+ when Socket::AF_INET6 then [Addrinfo.tcp("::1", port)]
+ when Socket::AF_INET then sleep(0.001); raise SocketError
+ end
+ end
+
+ server_thread = Thread.new { server.accept }
+ socket = nil
+
+ assert_nothing_raised do
+ socket = Socket.tcp("localhost", port)
+ end
+
+ server_thread.value.close
+ server.close
+ socket.close if socket && !socket.closed?
+ end;
+ end
+
+ def test_tcp_socket_all_hostname_resolution_failed
+ opts = %w[-rsocket -W1]
+ assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}"
+
+ begin;
+ Addrinfo.define_singleton_method(:getaddrinfo) do |_, _, family, *_|
+ case family
+ when Socket::AF_INET6 then raise SocketError
+ when Socket::AF_INET then sleep(0.001); raise SocketError, "Last hostname resolution error"
+ end
+ end
+ port = TCPServer.new("localhost", 0).addr[1]
+
+ assert_raise_with_message(SocketError, "Last hostname resolution error") do
+ Socket.tcp("localhost", port)
+ end
+ end;
+ end
+
+ def test_tcp_socket_v6_address_passed
+ opts = %w[-rsocket -W1]
+ assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}"
+
+ begin;
+ begin
+ server = TCPServer.new("::1", 0)
+ rescue Errno::EADDRNOTAVAIL # IPv6 is not supported
+ exit
+ end
+
+ _, port, = server.addr
+
+ Addrinfo.define_singleton_method(:getaddrinfo) do |*_|
+ [Addrinfo.tcp("::1", port)]
+ end
+
+ server_thread = Thread.new { server.accept }
+ socket = Socket.tcp("::1", port)
+
+ assert_true(socket.remote_address.ipv6?)
+ server_thread.value.close
+ server.close
+ socket.close if socket && !socket.closed?
+ end;
+ end
+
+ def test_tcp_socket_fast_fallback_is_false
+ server = TCPServer.new("127.0.0.1", 0)
+ _, port, = server.addr
+ server_thread = Thread.new { server.accept }
+ socket = Socket.tcp("127.0.0.1", port, fast_fallback: false)
+
+ assert_true(socket.remote_address.ipv4?)
+ server_thread.value.close
+ server.close
+ socket.close if socket && !socket.closed?
+ end
end if defined?(Socket)
diff --git a/test/socket/test_tcp.rb b/test/socket/test_tcp.rb
index 83ebea1b7a..7f9dc53cae 100644
--- a/test/socket/test_tcp.rb
+++ b/test/socket/test_tcp.rb
@@ -70,7 +70,7 @@ class TestSocket_TCPSocket < Test::Unit::TestCase
end
def test_initialize_connect_timeout
- assert_raise(IO::TimeoutError) do
+ assert_raise(IO::TimeoutError, Errno::ENETUNREACH) do
TCPSocket.new("192.0.2.1", 80, connect_timeout: 0)
end
end
diff --git a/test/socket/test_unix.rb b/test/socket/test_unix.rb
index e206339db0..3e7d85befc 100644
--- a/test/socket/test_unix.rb
+++ b/test/socket/test_unix.rb
@@ -152,7 +152,7 @@ class TestSocket_UNIXSocket < Test::Unit::TestCase
lock = Thread::Mutex.new
nr = 0
x = 2
- y = 1000
+ y = 400
begin
s1.send_io(nil)
rescue NotImplementedError
@@ -420,9 +420,10 @@ class TestSocket_UNIXSocket < Test::Unit::TestCase
s1.recv_nonblock(10)
fail
rescue => e
- assert(IO::EAGAINWaitReadable === e)
- assert(IO::WaitReadable === e)
+ assert_kind_of(IO::EWOULDBLOCKWaitReadable, e)
+ assert_kind_of(IO::WaitReadable, e)
end
+
s2.send("", 0)
s2.send("haha", 0)
s2.send("", 0)
@@ -446,6 +447,67 @@ class TestSocket_UNIXSocket < Test::Unit::TestCase
s2.close if s2
end
+ def test_stream_pair
+ s1, s2 = UNIXSocket.pair(Socket::SOCK_STREAM)
+ begin
+ s1.recv_nonblock(10)
+ fail
+ rescue => e
+ assert_kind_of(IO::EWOULDBLOCKWaitReadable, e)
+ assert_kind_of(IO::WaitReadable, e)
+ end
+
+ s2.send("", 0)
+ s2.send("haha", 0)
+ assert_equal("haha", s1.recv(10))
+ assert_raise(IO::EWOULDBLOCKWaitReadable) { s1.recv_nonblock(10) }
+
+ buf = "".dup
+ s2.send("BBBBBB", 0)
+ IO.select([s1])
+ rv = s1.recv(100, 0, buf)
+ assert_equal buf.object_id, rv.object_id
+ assert_equal "BBBBBB", rv
+
+ s2.close
+ assert_nil(s1.recv(10))
+ rescue Errno::EPROTOTYPE => error
+ omit error.message
+ ensure
+ s1.close if s1
+ s2.close if s2
+ end
+
+ def test_seqpacket_pair
+ s1, s2 = UNIXSocket.pair(Socket::SOCK_SEQPACKET)
+ begin
+ s1.recv_nonblock(10)
+ fail
+ rescue => e
+ assert_kind_of(IO::EWOULDBLOCKWaitReadable, e)
+ assert_kind_of(IO::WaitReadable, e)
+ end
+
+ s2.send("haha", 0)
+ assert_equal("haha", s1.recv(10))
+ assert_raise(IO::EWOULDBLOCKWaitReadable) { s1.recv_nonblock(10) }
+
+ buf = "".dup
+ s2.send("BBBBBB", 0)
+ IO.select([s1])
+ rv = s1.recv(100, 0, buf)
+ assert_equal buf.object_id, rv.object_id
+ assert_equal "BBBBBB", rv
+
+ s2.close
+ assert_nil(s1.recv(10))
+ rescue Errno::EPROTOTYPE, Errno::EPROTONOSUPPORT => error
+ omit error.message
+ ensure
+ s1.close if s1
+ s2.close if s2
+ end
+
def test_dgram_pair_sendrecvmsg_errno_set
if /mswin|mingw/ =~ RUBY_PLATFORM
omit("AF_UNIX + SOCK_DGRAM is not supported on windows")
diff --git a/test/stringio/test_ractor.rb b/test/stringio/test_ractor.rb
index 1c334e2c3f..4a2033bc1f 100644
--- a/test/stringio/test_ractor.rb
+++ b/test/stringio/test_ractor.rb
@@ -11,7 +11,7 @@ class TestStringIOInRactor < Test::Unit::TestCase
require "stringio"
$VERBOSE = nil
r = Ractor.new do
- io = StringIO.new("")
+ io = StringIO.new(+"")
io.puts "abc"
io.truncate(0)
io.puts "def"
diff --git a/test/stringio/test_stringio.rb b/test/stringio/test_stringio.rb
index fd9974caeb..9031650581 100644
--- a/test/stringio/test_stringio.rb
+++ b/test/stringio/test_stringio.rb
@@ -14,14 +14,19 @@ class TestStringIO < Test::Unit::TestCase
include TestEOF::Seek
+ def test_version
+ assert_kind_of(String, StringIO::VERSION)
+ end
+
def test_initialize
assert_kind_of StringIO, StringIO.new
assert_kind_of StringIO, StringIO.new('str')
assert_kind_of StringIO, StringIO.new('str', 'r+')
+ assert_kind_of StringIO, StringIO.new(nil)
assert_raise(ArgumentError) { StringIO.new('', 'x') }
assert_raise(ArgumentError) { StringIO.new('', 'rx') }
assert_raise(ArgumentError) { StringIO.new('', 'rbt') }
- assert_raise(TypeError) { StringIO.new(nil) }
+ assert_raise(TypeError) { StringIO.new(Object) }
o = Object.new
def o.to_str
@@ -36,6 +41,13 @@ class TestStringIO < Test::Unit::TestCase
assert_kind_of StringIO, StringIO.new(o)
end
+ def test_null
+ io = StringIO.new(nil)
+ assert_nil io.gets
+ io.puts "abc"
+ assert_nil io.string
+ end
+
def test_truncate
io = StringIO.new("")
io.puts "abc"
@@ -84,6 +96,14 @@ class TestStringIO < Test::Unit::TestCase
assert_string("", Encoding::UTF_8, StringIO.new("foo").gets(0))
end
+ def test_gets_utf_16
+ stringio = StringIO.new("line1\nline2\nline3\n".encode("utf-16le"))
+ assert_equal("line1\n".encode("utf-16le"), stringio.gets)
+ assert_equal("line2\n".encode("utf-16le"), stringio.gets)
+ assert_equal("line3\n".encode("utf-16le"), stringio.gets)
+ assert_nil(stringio.gets)
+ end
+
def test_gets_chomp
assert_equal(nil, StringIO.new("").gets(chomp: true))
assert_equal("", StringIO.new("\n").gets(chomp: true))
@@ -225,7 +245,7 @@ class TestStringIO < Test::Unit::TestCase
def test_write_integer_overflow
f = StringIO.new
- f.pos = RbConfig::LIMITS["LONG_MAX"]
+ f.pos = StringIO::MAX_LENGTH
assert_raise(ArgumentError) {
f.write("pos + len overflows")
}
@@ -680,6 +700,18 @@ class TestStringIO < Test::Unit::TestCase
s.force_encoding(Encoding::US_ASCII)
assert_same(s, f.read(nil, s))
assert_string("", Encoding::UTF_8, s, bug13806)
+
+ bug20418 = '[Bug #20418] ™€®'.b
+ f = StringIO.new(bug20418)
+ s = ""
+ assert_equal(Encoding::UTF_8, s.encoding, bug20418)
+ f.read(4, s)
+ assert_equal(Encoding::UTF_8, s.encoding, bug20418)
+
+ f.rewind
+ s = ""
+ f.read(nil, s)
+ assert_equal(Encoding::ASCII_8BIT, s.encoding, bug20418)
end
def test_readpartial
@@ -691,8 +723,8 @@ class TestStringIO < Test::Unit::TestCase
assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.readpartial(f.size))
f.rewind
# not empty buffer
- s = '0123456789'
- assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.readpartial(f.size, s))
+ s = '0123456789'.b
+ assert_equal("\u3042\u3044".b, f.readpartial(f.size, s))
end
def test_read_nonblock
@@ -716,8 +748,8 @@ class TestStringIO < Test::Unit::TestCase
assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.read_nonblock(f.size))
f.rewind
# not empty buffer
- s = '0123456789'
- assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.read_nonblock(f.size, s))
+ s = '0123456789'.b
+ assert_equal("\u3042\u3044".b, f.read_nonblock(f.size, s))
end
def test_sysread
@@ -729,6 +761,32 @@ class TestStringIO < Test::Unit::TestCase
assert_equal Encoding::ASCII_8BIT, f.sysread(3).encoding
end
+ def test_pread
+ f = StringIO.new("pread")
+ f.read
+
+ assert_equal "pre".b, f.pread(3, 0)
+ assert_equal "read".b, f.pread(4, 1)
+ assert_equal Encoding::ASCII_8BIT, f.pread(4, 1).encoding
+
+ buf = "".b
+ f.pread(3, 0, buf)
+ assert_equal "pre".b, buf
+ f.pread(4, 1, buf)
+ assert_equal "read".b, buf
+
+ assert_raise(EOFError) { f.pread(1, 5) }
+ assert_raise(ArgumentError) { f.pread(-1, 0) }
+ assert_raise(Errno::EINVAL) { f.pread(3, -1) }
+
+ assert_equal "".b, StringIO.new("").pread(0, 0)
+ assert_equal "".b, StringIO.new("").pread(0, -10)
+
+ buf = "stale".b
+ assert_equal "stale".b, StringIO.new("").pread(0, 0, buf)
+ assert_equal "stale".b, buf
+ end
+
def test_size
f = StringIO.new("1234")
assert_equal(4, f.size)
@@ -861,8 +919,9 @@ class TestStringIO < Test::Unit::TestCase
end
def test_overflow
- return if RbConfig::SIZEOF["void*"] > RbConfig::SIZEOF["long"]
- limit = RbConfig::LIMITS["INTPTR_MAX"] - 0x10
+ intptr_max = RbConfig::LIMITS["INTPTR_MAX"]
+ return if intptr_max > StringIO::MAX_LENGTH
+ limit = intptr_max - 0x10
assert_separately(%w[-rstringio], "#{<<-"begin;"}\n#{<<-"end;"}")
begin;
limit = #{limit}
@@ -915,6 +974,43 @@ class TestStringIO < Test::Unit::TestCase
$VERBOSE = verbose
end
+ def test_coderange_after_overwrite
+ s = StringIO.new("".b)
+
+ s.write("a=b&c=d")
+ s.rewind
+ assert_predicate(s.string, :ascii_only?)
+ s.write "\u{431 43e 433 443 441}"
+ assert_not_predicate(s.string, :ascii_only?)
+
+ s = StringIO.new("\u{3042}")
+ s.rewind
+ assert_not_predicate(s.string, :ascii_only?)
+ s.write('aaaa')
+ assert_predicate(s.string, :ascii_only?)
+ end
+
+ if eval(%{ "test".frozen? && !"test".equal?("test") }) # Ruby 3.4+ chilled strings
+ def test_chilled_string
+ chilled_string = eval(%{""})
+ io = StringIO.new(chilled_string)
+ assert_warning(/literal string will be frozen/) { io << "test" }
+ assert_equal("test", io.string)
+ assert_same(chilled_string, io.string)
+ end
+
+ def test_chilled_string_string_set
+ io = StringIO.new
+ chilled_string = eval(%{""})
+ io.string = chilled_string
+ assert_warning(/literal string will be frozen/) { io << "test" }
+ assert_equal("test", io.string)
+ assert_same(chilled_string, io.string)
+ end
+ end
+
+ private
+
def assert_string(content, encoding, str, mesg = nil)
assert_equal([content, encoding], [str, str.encoding], mesg)
end
diff --git a/test/strscan/test_stringscanner.rb b/test/strscan/test_stringscanner.rb
index b7ffac816d..143cf7197d 100644
--- a/test/strscan/test_stringscanner.rb
+++ b/test/strscan/test_stringscanner.rb
@@ -8,6 +8,31 @@ require 'strscan'
require 'test/unit'
module StringScannerTests
+ def test_peek_byte
+ omit("not implemented on TruffleRuby") if RUBY_ENGINE == "truffleruby"
+ s = create_string_scanner('ab')
+ assert_equal 97, s.peek_byte
+ assert_equal 97, s.scan_byte
+ assert_equal 98, s.peek_byte
+ assert_equal 98, s.scan_byte
+ assert_nil s.peek_byte
+ assert_nil s.scan_byte
+ end
+
+ def test_scan_byte
+ omit("not implemented on TruffleRuby") if RUBY_ENGINE == "truffleruby"
+ s = create_string_scanner('ab')
+ assert_equal 97, s.scan_byte
+ assert_equal 98, s.scan_byte
+ assert_nil s.scan_byte
+
+ str = "\244\242".dup.force_encoding("euc-jp")
+ s = StringScanner.new(str)
+ assert_equal str.getbyte(s.pos), s.scan_byte
+ assert_equal str.getbyte(s.pos), s.scan_byte
+ assert_nil s.scan_byte
+ end
+
def test_s_new
s = create_string_scanner('test string')
assert_instance_of StringScanner, s
@@ -558,6 +583,16 @@ module StringScannerTests
assert_nil s.matched_size
end
+ def test_empty_encoding_utf8
+ ss = create_string_scanner('')
+ assert_equal(Encoding::UTF_8, ss.rest.encoding)
+ end
+
+ def test_empty_encoding_ascii_8bit
+ ss = create_string_scanner(''.dup.force_encoding("ASCII-8BIT"))
+ assert_equal(Encoding::ASCII_8BIT, ss.rest.encoding)
+ end
+
def test_encoding
ss = create_string_scanner("\xA1\xA2".dup.force_encoding("euc-jp"))
assert_equal(Encoding::EUC_JP, ss.scan(/./e).encoding)
@@ -737,8 +772,8 @@ module StringScannerTests
def test_captures
s = create_string_scanner("Timestamp: Fri Dec 12 1975 14:39")
s.scan("Timestamp: ")
- s.scan(/(\w+) (\w+) (\d+) /)
- assert_equal(["Fri", "Dec", "12"], s.captures)
+ s.scan(/(\w+) (\w+) (\d+) (1980)?/)
+ assert_equal(["Fri", "Dec", "12", nil], s.captures)
s.scan(/(\w+) (\w+) (\d+) /)
assert_nil(s.captures)
end
@@ -775,6 +810,7 @@ module StringScannerTests
def test_named_captures
omit("not implemented on TruffleRuby") if ["truffleruby"].include?(RUBY_ENGINE)
scan = StringScanner.new("foobarbaz")
+ assert_equal({}, scan.named_captures)
assert_equal(9, scan.match?(/(?<f>foo)(?<r>bar)(?<z>baz)/))
assert_equal({"f" => "foo", "r" => "bar", "z" => "baz"}, scan.named_captures)
end
@@ -830,4 +866,12 @@ class TestStringScannerFixedAnchor < Test::Unit::TestCase
assert_equal 1, s.skip(/a/)
assert_nil s.skip(/^b/)
end
+
+ # ruby/strscan#86
+ def test_scan_shared_string
+ s = "hellohello"[5..-1]
+ ss = StringScanner.new(s).scan(/hello/)
+
+ assert_equal "hello", ss
+ end
end
diff --git a/test/syslog/test_syslog_logger.rb b/test/syslog/test_syslog_logger.rb
deleted file mode 100644
index d9ffae3901..0000000000
--- a/test/syslog/test_syslog_logger.rb
+++ /dev/null
@@ -1,588 +0,0 @@
-# coding: US-ASCII
-# frozen_string_literal: false
-require 'test/unit'
-require 'tempfile'
-begin
- require 'syslog/logger'
-rescue LoadError
- # skip. see the bottom of this file.
-end
-
-# These tests ensure Syslog::Logger works like Logger
-
-class TestSyslogRootLogger < Test::Unit::TestCase
-
- module MockSyslog
-
- PRIMASK = Syslog::Level.constants.inject(0) { |mask, name| mask | Syslog::Level.const_get(name) }
-
- LEVEL_LABEL_MAP = {
- Syslog::LOG_ALERT => 'ALERT',
- Syslog::LOG_ERR => 'ERR',
- Syslog::LOG_WARNING => 'WARNING',
- Syslog::LOG_NOTICE => 'NOTICE',
- Syslog::LOG_INFO => 'INFO',
- Syslog::LOG_DEBUG => 'DEBUG'
- }
-
- @facility = Syslog::LOG_USER
-
- class << self
-
- attr_reader :facility
- attr_reader :line
- attr_reader :program_name
-
- def log(priority, format, *args)
- level = priority & PRIMASK
- @line = "<#{priority}> #{LEVEL_LABEL_MAP[level]} - #{format % args}"
- end
-
- def open(program_name)
- @program_name = program_name
- end
-
- def reset
- @line = ''
- end
-
- end
- end
-
- Syslog::Logger.syslog = MockSyslog
-
- LEVEL_LABEL_MAP = {
- Logger::DEBUG => 'DEBUG',
- Logger::INFO => 'INFO',
- Logger::WARN => 'WARN',
- Logger::ERROR => 'ERROR',
- Logger::FATAL => 'FATAL',
- Logger::UNKNOWN => 'ANY',
- }
-
- def setup
- @logger = Logger.new(nil)
- end
-
- class Log
- attr_reader :line, :label, :datetime, :pid, :severity, :progname, :msg
- def initialize(line)
- @line = line
- /\A(\w+), \[([^#]*)#(\d+)\]\s+(\w+) -- (\w*): ([\x0-\xff]*)/ =~ @line
- @label, @datetime, @pid, @severity, @progname, @msg = $1, $2, $3, $4, $5, $6
- end
- end
-
- def log_add(severity, msg, progname = nil, &block)
- log(:add, severity, msg, progname, &block)
- end
-
- def log(msg_id, *arg, &block)
- Log.new(log_raw(msg_id, *arg, &block))
- end
-
- def log_raw(msg_id, *arg, &block)
- Tempfile.create(File.basename(__FILE__) + '.log') {|logdev|
- @logger.instance_eval { @logdev = Logger::LogDevice.new(logdev) }
- assert_equal true, @logger.__send__(msg_id, *arg, &block)
- logdev.rewind
- logdev.read
- }
- end
-
- def test_initialize
- assert_equal Logger::DEBUG, @logger.level
- end
-
- def test_custom_formatter
- @logger.formatter = Class.new {
- def call severity, time, progname, msg
- "hi mom!"
- end
- }.new
-
- assert_match(/hi mom!/, log_raw(:fatal, 'fatal level message'))
- end
-
- def test_add
- msg = log_add nil, 'unknown level message' # nil == unknown
- assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
-
- msg = log_add Logger::FATAL, 'fatal level message'
- assert_equal LEVEL_LABEL_MAP[Logger::FATAL], msg.severity
-
- msg = log_add Logger::ERROR, 'error level message'
- assert_equal LEVEL_LABEL_MAP[Logger::ERROR], msg.severity
-
- msg = log_add Logger::WARN, 'warn level message'
- assert_equal LEVEL_LABEL_MAP[Logger::WARN], msg.severity
-
- msg = log_add Logger::INFO, 'info level message'
- assert_equal LEVEL_LABEL_MAP[Logger::INFO], msg.severity
-
- msg = log_add Logger::DEBUG, 'debug level message'
- assert_equal LEVEL_LABEL_MAP[Logger::DEBUG], msg.severity
- end
-
- def test_add_level_unknown
- @logger.level = Logger::UNKNOWN
-
- msg = log_add nil, 'unknown level message' # nil == unknown
- assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
-
- msg = log_add Logger::FATAL, 'fatal level message'
- assert_equal '', msg.line
-
- msg = log_add Logger::ERROR, 'error level message'
- assert_equal '', msg.line
-
- msg = log_add Logger::WARN, 'warn level message'
- assert_equal '', msg.line
-
- msg = log_add Logger::INFO, 'info level message'
- assert_equal '', msg.line
-
- msg = log_add Logger::DEBUG, 'debug level message'
- assert_equal '', msg.line
- end
-
- def test_add_level_fatal
- @logger.level = Logger::FATAL
-
- msg = log_add nil, 'unknown level message' # nil == unknown
- assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
-
- msg = log_add Logger::FATAL, 'fatal level message'
- assert_equal LEVEL_LABEL_MAP[Logger::FATAL], msg.severity
-
- msg = log_add Logger::ERROR, 'error level message'
- assert_equal '', msg.line
-
- msg = log_add Logger::WARN, 'warn level message'
- assert_equal '', msg.line
-
- msg = log_add Logger::INFO, 'info level message'
- assert_equal '', msg.line
-
- msg = log_add Logger::DEBUG, 'debug level message'
- assert_equal '', msg.line
- end
-
- def test_add_level_error
- @logger.level = Logger::ERROR
-
- msg = log_add nil, 'unknown level message' # nil == unknown
- assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
-
- msg = log_add Logger::FATAL, 'fatal level message'
- assert_equal LEVEL_LABEL_MAP[Logger::FATAL], msg.severity
-
- msg = log_add Logger::ERROR, 'error level message'
- assert_equal LEVEL_LABEL_MAP[Logger::ERROR], msg.severity
-
- msg = log_add Logger::WARN, 'warn level message'
- assert_equal '', msg.line
-
- msg = log_add Logger::INFO, 'info level message'
- assert_equal '', msg.line
-
- msg = log_add Logger::DEBUG, 'debug level message'
- assert_equal '', msg.line
- end
-
- def test_add_level_warn
- @logger.level = Logger::WARN
-
- msg = log_add nil, 'unknown level message' # nil == unknown
- assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
-
- msg = log_add Logger::FATAL, 'fatal level message'
- assert_equal LEVEL_LABEL_MAP[Logger::FATAL], msg.severity
-
- msg = log_add Logger::ERROR, 'error level message'
- assert_equal LEVEL_LABEL_MAP[Logger::ERROR], msg.severity
-
- msg = log_add Logger::WARN, 'warn level message'
- assert_equal LEVEL_LABEL_MAP[Logger::WARN], msg.severity
-
- msg = log_add Logger::INFO, 'info level message'
- assert_equal '', msg.line
-
- msg = log_add Logger::DEBUG, 'debug level message'
- assert_equal '', msg.line
- end
-
- def test_add_level_info
- @logger.level = Logger::INFO
-
- msg = log_add nil, 'unknown level message' # nil == unknown
- assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
-
- msg = log_add Logger::FATAL, 'fatal level message'
- assert_equal LEVEL_LABEL_MAP[Logger::FATAL], msg.severity
-
- msg = log_add Logger::ERROR, 'error level message'
- assert_equal LEVEL_LABEL_MAP[Logger::ERROR], msg.severity
-
- msg = log_add Logger::WARN, 'warn level message'
- assert_equal LEVEL_LABEL_MAP[Logger::WARN], msg.severity
-
- msg = log_add Logger::INFO, 'info level message'
- assert_equal LEVEL_LABEL_MAP[Logger::INFO], msg.severity
-
- msg = log_add Logger::DEBUG, 'debug level message'
- assert_equal '', msg.line
- end
-
- def test_add_level_debug
- @logger.level = Logger::DEBUG
-
- msg = log_add nil, 'unknown level message' # nil == unknown
- assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
-
- msg = log_add Logger::FATAL, 'fatal level message'
- assert_equal LEVEL_LABEL_MAP[Logger::FATAL], msg.severity
-
- msg = log_add Logger::ERROR, 'error level message'
- assert_equal LEVEL_LABEL_MAP[Logger::ERROR], msg.severity
-
- msg = log_add Logger::WARN, 'warn level message'
- assert_equal LEVEL_LABEL_MAP[Logger::WARN], msg.severity
-
- msg = log_add Logger::INFO, 'info level message'
- assert_equal LEVEL_LABEL_MAP[Logger::INFO], msg.severity
-
- msg = log_add Logger::DEBUG, 'debug level message'
- assert_equal LEVEL_LABEL_MAP[Logger::DEBUG], msg.severity
- end
-
- def test_unknown
- msg = log :unknown, 'unknown level message'
- assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
-
- @logger.level = Logger::UNKNOWN
- msg = log :unknown, 'unknown level message'
- assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
-
- @logger.level = Logger::FATAL
- msg = log :unknown, 'unknown level message'
- assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
-
- @logger.level = Logger::ERROR
- msg = log :unknown, 'unknown level message'
- assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
-
- @logger.level = Logger::WARN
- msg = log :unknown, 'unknown level message'
- assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
-
- @logger.level = Logger::INFO
- msg = log :unknown, 'unknown level message'
- assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
-
- @logger.level = Logger::DEBUG
- msg = log :unknown, 'unknown level message'
- assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
- end
-
- def test_fatal
- msg = log :fatal, 'fatal level message'
- assert_equal LEVEL_LABEL_MAP[Logger::FATAL], msg.severity
-
- @logger.level = Logger::UNKNOWN
- msg = log :fatal, 'fatal level message'
- assert_equal '', msg.line
-
- @logger.level = Logger::FATAL
- msg = log :fatal, 'fatal level message'
- assert_equal LEVEL_LABEL_MAP[Logger::FATAL], msg.severity
-
- @logger.level = Logger::ERROR
- msg = log :fatal, 'fatal level message'
- assert_equal LEVEL_LABEL_MAP[Logger::FATAL], msg.severity
-
- @logger.level = Logger::WARN
- msg = log :fatal, 'fatal level message'
- assert_equal LEVEL_LABEL_MAP[Logger::FATAL], msg.severity
-
- @logger.level = Logger::INFO
- msg = log :fatal, 'fatal level message'
- assert_equal LEVEL_LABEL_MAP[Logger::FATAL], msg.severity
-
- @logger.level = Logger::DEBUG
- msg = log :fatal, 'fatal level message'
- assert_equal LEVEL_LABEL_MAP[Logger::FATAL], msg.severity
- end
-
- def test_fatal_eh
- @logger.level = Logger::FATAL
- assert_equal true, @logger.fatal?
-
- @logger.level = Logger::UNKNOWN
- assert_equal false, @logger.fatal?
- end
-
- def test_error
- msg = log :error, 'error level message'
- assert_equal LEVEL_LABEL_MAP[Logger::ERROR], msg.severity
-
- @logger.level = Logger::UNKNOWN
- msg = log :error, 'error level message'
- assert_equal '', msg.line
-
- @logger.level = Logger::FATAL
- msg = log :error, 'error level message'
- assert_equal '', msg.line
-
- @logger.level = Logger::ERROR
- msg = log :error, 'error level message'
- assert_equal LEVEL_LABEL_MAP[Logger::ERROR], msg.severity
-
- @logger.level = Logger::WARN
- msg = log :error, 'error level message'
- assert_equal LEVEL_LABEL_MAP[Logger::ERROR], msg.severity
-
- @logger.level = Logger::INFO
- msg = log :error, 'error level message'
- assert_equal LEVEL_LABEL_MAP[Logger::ERROR], msg.severity
-
- @logger.level = Logger::DEBUG
- msg = log :error, 'error level message'
- assert_equal LEVEL_LABEL_MAP[Logger::ERROR], msg.severity
- end
-
- def test_error_eh
- @logger.level = Logger::ERROR
- assert_equal true, @logger.error?
-
- @logger.level = Logger::FATAL
- assert_equal false, @logger.error?
- end
-
- def test_warn
- msg = log :warn, 'warn level message'
- assert_equal LEVEL_LABEL_MAP[Logger::WARN], msg.severity
-
- @logger.level = Logger::UNKNOWN
- msg = log :warn, 'warn level message'
- assert_equal '', msg.line
-
- @logger.level = Logger::FATAL
- msg = log :warn, 'warn level message'
- assert_equal '', msg.line
-
- @logger.level = Logger::ERROR
- msg = log :warn, 'warn level message'
- assert_equal '', msg.line
-
- @logger.level = Logger::WARN
- msg = log :warn, 'warn level message'
- assert_equal LEVEL_LABEL_MAP[Logger::WARN], msg.severity
-
- @logger.level = Logger::INFO
- msg = log :warn, 'warn level message'
- assert_equal LEVEL_LABEL_MAP[Logger::WARN], msg.severity
-
- @logger.level = Logger::DEBUG
- msg = log :warn, 'warn level message'
- assert_equal LEVEL_LABEL_MAP[Logger::WARN], msg.severity
- end
-
- def test_warn_eh
- @logger.level = Logger::WARN
- assert_equal true, @logger.warn?
-
- @logger.level = Logger::ERROR
- assert_equal false, @logger.warn?
- end
-
- def test_info
- msg = log :info, 'info level message'
- assert_equal LEVEL_LABEL_MAP[Logger::INFO], msg.severity
-
- @logger.level = Logger::UNKNOWN
- msg = log :info, 'info level message'
- assert_equal '', msg.line
-
- @logger.level = Logger::FATAL
- msg = log :info, 'info level message'
- assert_equal '', msg.line
-
- @logger.level = Logger::ERROR
- msg = log :info, 'info level message'
- assert_equal '', msg.line
-
- @logger.level = Logger::WARN
- msg = log :info, 'info level message'
- assert_equal '', msg.line
-
- @logger.level = Logger::INFO
- msg = log :info, 'info level message'
- assert_equal LEVEL_LABEL_MAP[Logger::INFO], msg.severity
-
- @logger.level = Logger::DEBUG
- msg = log :info, 'info level message'
- assert_equal LEVEL_LABEL_MAP[Logger::INFO], msg.severity
- end
-
- def test_info_eh
- @logger.level = Logger::INFO
- assert_equal true, @logger.info?
-
- @logger.level = Logger::WARN
- assert_equal false, @logger.info?
- end
-
- def test_debug
- msg = log :debug, 'debug level message'
- assert_equal LEVEL_LABEL_MAP[Logger::DEBUG], msg.severity
-
- @logger.level = Logger::UNKNOWN
- msg = log :debug, 'debug level message'
- assert_equal '', msg.line
-
- @logger.level = Logger::FATAL
- msg = log :debug, 'debug level message'
- assert_equal '', msg.line
-
- @logger.level = Logger::ERROR
- msg = log :debug, 'debug level message'
- assert_equal '', msg.line
-
- @logger.level = Logger::WARN
- msg = log :debug, 'debug level message'
- assert_equal '', msg.line
-
- @logger.level = Logger::INFO
- msg = log :debug, 'debug level message'
- assert_equal '', msg.line
-
- @logger.level = Logger::DEBUG
- msg = log :debug, 'debug level message'
- assert_equal LEVEL_LABEL_MAP[Logger::DEBUG], msg.severity
- end
-
- def test_debug_eh
- @logger.level = Logger::DEBUG
- assert_equal true, @logger.debug?
-
- @logger.level = Logger::INFO
- assert_equal false, @logger.debug?
- end
-
-end if defined?(Syslog)
-
-class TestSyslogLogger < TestSyslogRootLogger
-
- @facility = Syslog::LOG_USER
-
- def facility
- self.class.instance_variable_get("@facility")
- end
-
- def setup
- super
- @logger = Syslog::Logger.new
- end
-
- SEVERITY_MAP = {}.tap { |map|
- level2severity = Syslog::Logger::LEVEL_MAP.invert
-
- MockSyslog::LEVEL_LABEL_MAP.each { |level, name|
- map[name] = TestSyslogRootLogger::LEVEL_LABEL_MAP[level2severity[level]]
- }
- }
-
- class Log
- attr_reader :line, :label, :datetime, :pid, :severity, :progname, :msg, :priority
- def initialize(line)
- @line = line
- return unless /\A<(\d+)> (\w+) - (.*)\Z/ =~ @line
- priority, severity, @msg = $1, $2, $3
- @severity = SEVERITY_MAP[severity]
- @priority = priority.to_i
- end
- end
-
- def log_add(severity, msg, progname = nil, &block)
- log(:add, severity, msg, progname, &block)
- end
-
- def log(msg_id, *arg, &block)
- Log.new(log_raw(msg_id, *arg, &block))
- end
-
- def log_raw(msg_id, *arg, &block)
- assert_equal true, @logger.__send__(msg_id, *arg, &block)
- msg = MockSyslog.line
- MockSyslog.reset
- return msg
- end
-
- def test_unknown_eh
- @logger.level = Logger::UNKNOWN
- assert_equal true, @logger.unknown?
-
- @logger.level = Logger::UNKNOWN + 1
- assert_equal false, @logger.unknown?
- end
-
- def test_facility
- assert_equal facility, @logger.facility
- end
-
- def test_priority
- msg = log_add nil, 'unknown level message' # nil == unknown
- assert_equal facility|Syslog::LOG_ALERT, msg.priority
-
- msg = log_add Logger::FATAL, 'fatal level message'
- assert_equal facility|Syslog::LOG_ERR, msg.priority
-
- msg = log_add Logger::ERROR, 'error level message'
- assert_equal facility|Syslog::LOG_WARNING, msg.priority
-
- msg = log_add Logger::WARN, 'warn level message'
- assert_equal facility|Syslog::LOG_NOTICE, msg.priority
-
- msg = log_add Logger::INFO, 'info level message'
- assert_equal facility|Syslog::LOG_INFO, msg.priority
-
- msg = log_add Logger::DEBUG, 'debug level message'
- assert_equal facility|Syslog::LOG_DEBUG, msg.priority
- end
-
- class CustomSyslogLogger < Syslog::Logger
- def level
- Logger::INFO
- end
- end
-
- def test_overriding_level
- @logger = CustomSyslogLogger.new
- log = log_add Logger::INFO, 'msg'
- assert_equal 'msg', log.msg
-
- log = log_add Logger::DEBUG, 'msg'
- assert_nil log.msg
- end
-
-end if defined?(Syslog)
-
-
-# Create test class for each available facility
-
-Syslog::Facility.constants.each do |facility_symb|
-
- test_syslog_class = Class.new(TestSyslogLogger) do
-
- @facility = Syslog.const_get(facility_symb)
-
- def setup
- super
- @logger.facility = facility
- end
-
- end
- Object.const_set("TestSyslogLogger_#{facility_symb}", test_syslog_class)
-
-end if defined?(Syslog)
diff --git a/test/test_abbrev.rb b/test/test_abbrev.rb
deleted file mode 100644
index 67287138aa..0000000000
--- a/test/test_abbrev.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-# frozen_string_literal: true
-require 'test/unit'
-require 'abbrev'
-
-class TestAbbrev < Test::Unit::TestCase
- def test_abbrev
- words = %w[summer winter win ruby rules]
-
- assert_equal({
- "rub" => "ruby",
- "ruby" => "ruby",
- "rul" => "rules",
- "rule" => "rules",
- "rules" => "rules",
- "s" => "summer",
- "su" => "summer",
- "sum" => "summer",
- "summ" => "summer",
- "summe" => "summer",
- "summer" => "summer",
- "win" => "win",
- "wint" => "winter",
- "winte" => "winter",
- "winter" => "winter",
- }, words.abbrev)
-
- assert_equal({
- "rub" => "ruby",
- "ruby" => "ruby",
- "rul" => "rules",
- "rule" => "rules",
- "rules" => "rules",
- }, words.abbrev('ru'))
-
- assert_equal words.abbrev, Abbrev.abbrev(words)
- assert_equal words.abbrev('ru'), Abbrev.abbrev(words, 'ru')
- end
-
- def test_abbrev_lf
- words = ["abc", "abc\nd", "de"]
-
- assert_equal({
- "abc" => "abc",
- "abc\n" => "abc\nd",
- "abc\nd" => "abc\nd",
- "d" => "de",
- "de" => "de",
- }, words.abbrev)
-
- assert_equal({
- "d" => "de",
- "de" => "de",
- }, words.abbrev('d'))
- end
-end
diff --git a/test/test_delegate.rb b/test/test_delegate.rb
index 57480b18ea..f7bedf37fb 100644
--- a/test/test_delegate.rb
+++ b/test/test_delegate.rb
@@ -3,14 +3,6 @@ require 'test/unit'
require 'delegate'
class TestDelegateClass < Test::Unit::TestCase
- module PP
- def mu_pp(obj)
- str = super
- str = "#<#{obj.class}: #{str}>" if Delegator === obj
- str
- end
- end
-
module M
attr_reader :m
end
@@ -215,7 +207,6 @@ class TestDelegateClass < Test::Unit::TestCase
end
def test_eql?
- extend PP
s0 = SimpleDelegator.new("foo")
s1 = SimpleDelegator.new("bar")
s2 = SimpleDelegator.new("foo")
diff --git a/test/test_extlibs.rb b/test/test_extlibs.rb
index fb67e8453a..9b6676416c 100644
--- a/test/test_extlibs.rb
+++ b/test/test_extlibs.rb
@@ -34,14 +34,13 @@ class TestExtLibs < Test::Unit::TestCase
end.flatten.compact
excluded << '+' if excluded.empty?
if windows?
- excluded.map! {|i| i == '+' ? ['pty', 'syslog'] : i}
+ excluded.map! {|i| i == '+' ? ['pty'] : i}
excluded.flatten!
else
excluded.map! {|i| i == '+' ? '*win32*' : i}
end
@excluded = excluded
- check_existence "bigdecimal"
check_existence "continuation"
check_existence "coverage"
check_existence "date"
@@ -59,19 +58,16 @@ class TestExtLibs < Test::Unit::TestCase
check_existence "io/nonblock"
check_existence "io/wait"
check_existence "json"
- check_existence "nkf"
check_existence "objspace"
check_existence "openssl", "this may be false positive, but should assert because rubygems requires this"
check_existence "pathname"
check_existence "psych"
check_existence "pty"
- check_existence "racc/cparse"
check_existence "rbconfig/sizeof"
check_existence "ripper"
check_existence "socket"
check_existence "stringio"
check_existence "strscan"
- check_existence "syslog"
check_existence "thread"
check_existence "win32ole"
check_existence "zlib", "this may be false positive, but should assert because rubygems requires this"
diff --git a/test/test_forwardable.rb b/test/test_forwardable.rb
index deb0b5d5cf..9ed330058a 100644
--- a/test/test_forwardable.rb
+++ b/test/test_forwardable.rb
@@ -306,7 +306,7 @@ class TestForwardable < Test::Unit::TestCase
def test_basicobject_subclass
bug11616 = '[ruby-core:71176] [Bug #11616]'
- assert_raise_with_message(NameError, /`bar'/, bug11616) {
+ assert_raise_with_message(NameError, /[`']bar'/, bug11616) {
Foo2.new.baz
}
end
diff --git a/test/test_getoptlong.rb b/test/test_getoptlong.rb
deleted file mode 100644
index 0cd370b6c7..0000000000
--- a/test/test_getoptlong.rb
+++ /dev/null
@@ -1,163 +0,0 @@
-require 'test/unit'
-require 'getoptlong'
-
-class TestGetoptLong < Test::Unit::TestCase
-
- def verify(test_argv, expected_remaining_argv, expected_options)
- # Save ARGV and replace it with a test ARGV.
- argv_saved = ARGV.dup
- ARGV.replace(test_argv)
- # Define options.
- opts = GetoptLong.new(
- ['--xxx', '-x', '--aaa', '-a', GetoptLong::REQUIRED_ARGUMENT],
- ['--yyy', '-y', '--bbb', '-b', GetoptLong::OPTIONAL_ARGUMENT],
- ['--zzz', '-z', '--ccc', '-c', GetoptLong::NO_ARGUMENT]
- )
- opts.quiet = true
- # Gather options.
- actual_options = []
- opts.each do |opt, arg|
- actual_options << "#{opt}: #{arg}"
- end
- # Save remaining test ARGV and restore original ARGV.
- actual_remaining_argv = ARGV.dup
- ARGV.replace(argv_saved)
- # Assert.
- assert_equal(expected_remaining_argv, actual_remaining_argv, 'ARGV')
- assert_equal(expected_options, actual_options, 'Options')
- end
-
- def test_no_options
- expected_options = []
- expected_argv = %w[foo bar]
- argv = %w[foo bar]
- verify(argv, expected_argv, expected_options)
- end
-
- def test_required_argument
- expected_options = [
- '--xxx: arg'
- ]
- expected_argv = %w[foo bar]
- options = %w[--xxx --xx --x -x --aaa --aa --a -a]
- options.each do |option|
- argv = ['foo', option, 'arg', 'bar']
- verify(argv, expected_argv, expected_options)
- end
- end
-
- def test_required_argument_missing
- options = %w[--xxx --xx --x -x --aaa --aa --a -a]
- options.each do |option|
- argv = [option]
- e = assert_raise(GetoptLong::MissingArgument) do
- verify(argv, [], [])
- end
- assert_match('requires an argument', e.message)
- end
- end
-
- def test_optional_argument
- expected_options = [
- '--yyy: arg'
- ]
- expected_argv = %w[foo bar]
- options = %w[--yyy --y --y -y --bbb --bb --b -b]
- options.each do |option|
- argv = ['foo', 'bar', option, 'arg']
- verify(argv, expected_argv, expected_options)
- end
- end
-
- def test_optional_argument_missing
- expected_options = [
- '--yyy: '
- ]
- expected_argv = %w[foo bar]
- options = %w[--yyy --y --y -y --bbb --bb --b -b]
- options.each do |option|
- argv = ['foo', 'bar', option]
- verify(argv, expected_argv, expected_options)
- end
- end
-
- def test_no_argument
- expected_options = [
- '--zzz: '
- ]
- expected_argv = %w[foo bar]
- options = %w[--zzz --zz --z -z --ccc --cc --c -c]
- options.each do |option|
- argv = ['foo', option, 'bar']
- verify(argv, expected_argv, expected_options)
- end
- end
-
- def test_new_with_empty_array
- e = assert_raise(ArgumentError) do
- GetoptLong.new([])
- end
- assert_match(/no argument-flag/, e.message)
- end
-
- def test_new_with_bad_array
- e = assert_raise(ArgumentError) do
- GetoptLong.new('foo')
- end
- assert_match(/option list contains non-Array argument/, e.message)
- end
-
- def test_new_with_empty_subarray
- e = assert_raise(ArgumentError) do
- GetoptLong.new([[]])
- end
- assert_match(/no argument-flag/, e.message)
- end
-
- def test_new_with_bad_subarray
- e = assert_raise(ArgumentError) do
- GetoptLong.new([1])
- end
- assert_match(/no option name/, e.message)
- end
-
- def test_new_with_invalid_option
- invalid_options = %w[verbose -verbose -- +]
- invalid_options.each do |invalid_option|
- e = assert_raise(ArgumentError, invalid_option.to_s) do
- arguments = [
- [invalid_option, '-v', GetoptLong::NO_ARGUMENT]
- ]
- GetoptLong.new(*arguments)
- end
- assert_match(/invalid option/, e.message)
- end
- end
-
- def test_new_with_invalid_alias
- invalid_aliases = %w[v - -- +]
- invalid_aliases.each do |invalid_alias|
- e = assert_raise(ArgumentError, invalid_alias.to_s) do
- arguments = [
- ['--verbose', invalid_alias, GetoptLong::NO_ARGUMENT]
- ]
- GetoptLong.new(*arguments)
- end
- assert_match(/invalid option/, e.message)
- end
- end
-
- def test_new_with_invalid_flag
- invalid_flags = ['foo']
- invalid_flags.each do |invalid_flag|
- e = assert_raise(ArgumentError, invalid_flag.to_s) do
- arguments = [
- ['--verbose', '-v', invalid_flag]
- ]
- GetoptLong.new(*arguments)
- end
- assert_match(/no argument-flag/, e.message)
- end
- end
-
-end
diff --git a/test/test_ipaddr.rb b/test/test_ipaddr.rb
index c07ee2a8ee..3e5c3d2aa4 100644
--- a/test/test_ipaddr.rb
+++ b/test/test_ipaddr.rb
@@ -20,18 +20,21 @@ class TC_IPAddr < Test::Unit::TestCase
a = IPAddr.new
assert_equal("::", a.to_s)
assert_equal("0000:0000:0000:0000:0000:0000:0000:0000", a.to_string)
+ assert_equal("::/128", a.cidr)
assert_equal(Socket::AF_INET6, a.family)
assert_equal(128, a.prefix)
a = IPAddr.new("0123:4567:89ab:cdef:0ABC:DEF0:1234:5678")
assert_equal("123:4567:89ab:cdef:abc:def0:1234:5678", a.to_s)
assert_equal("0123:4567:89ab:cdef:0abc:def0:1234:5678", a.to_string)
+ assert_equal("123:4567:89ab:cdef:abc:def0:1234:5678/128", a.cidr)
assert_equal(Socket::AF_INET6, a.family)
assert_equal(128, a.prefix)
a = IPAddr.new("3ffe:505:2::/48")
assert_equal("3ffe:505:2::", a.to_s)
assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string)
+ assert_equal("3ffe:505:2::/48", a.cidr)
assert_equal(Socket::AF_INET6, a.family)
assert_equal(false, a.ipv4?)
assert_equal(true, a.ipv6?)
@@ -41,6 +44,7 @@ class TC_IPAddr < Test::Unit::TestCase
a = IPAddr.new("3ffe:505:2::/ffff:ffff:ffff::")
assert_equal("3ffe:505:2::", a.to_s)
assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string)
+ assert_equal("3ffe:505:2::/48", a.cidr)
assert_equal(Socket::AF_INET6, a.family)
assert_equal(48, a.prefix)
assert_nil(a.zone_id)
@@ -58,12 +62,14 @@ class TC_IPAddr < Test::Unit::TestCase
a = IPAddr.new("0.0.0.0")
assert_equal("0.0.0.0", a.to_s)
assert_equal("0.0.0.0", a.to_string)
+ assert_equal("0.0.0.0/32", a.cidr)
assert_equal(Socket::AF_INET, a.family)
assert_equal(32, a.prefix)
a = IPAddr.new("192.168.1.2")
assert_equal("192.168.1.2", a.to_s)
assert_equal("192.168.1.2", a.to_string)
+ assert_equal("192.168.1.2/32", a.cidr)
assert_equal(Socket::AF_INET, a.family)
assert_equal(true, a.ipv4?)
assert_equal(false, a.ipv6?)
@@ -72,6 +78,7 @@ class TC_IPAddr < Test::Unit::TestCase
a = IPAddr.new("192.168.1.2/26")
assert_equal("192.168.1.0", a.to_s)
assert_equal("192.168.1.0", a.to_string)
+ assert_equal("192.168.1.0/26", a.cidr)
assert_equal(Socket::AF_INET, a.family)
assert_equal("#<IPAddr: IPv4:192.168.1.0/255.255.255.192>", a.inspect)
assert_equal(26, a.prefix)
@@ -79,6 +86,7 @@ class TC_IPAddr < Test::Unit::TestCase
a = IPAddr.new("192.168.1.2/255.255.255.0")
assert_equal("192.168.1.0", a.to_s)
assert_equal("192.168.1.0", a.to_string)
+ assert_equal("192.168.1.0/24", a.cidr)
assert_equal(Socket::AF_INET, a.family)
assert_equal(24, a.prefix)
@@ -133,18 +141,37 @@ class TC_IPAddr < Test::Unit::TestCase
def test_ntop
# IPv4
- assert_equal("192.168.1.1", IPAddr.ntop("\xC0\xA8\x01\x01"))
+ assert_equal("192.168.1.1", IPAddr.ntop("\xC0\xA8\x01\x01".b))
+ assert_equal("10.231.140.171", IPAddr.ntop("\x0A\xE7\x8C\xAB".b))
# IPv6
assert_equal("0000:0000:0000:0000:0000:0000:0000:0001",
- IPAddr.ntop("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"))
+ IPAddr.ntop("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01".b))
+ assert_equal("fe80:0000:0000:0000:f09f:9985:f09f:9986",
+ IPAddr.ntop("\xFE\x80\x00\x00\x00\x00\x00\x00\xF0\x9F\x99\x85\xF0\x9F\x99\x86".b))
# Invalid parameters
+ ## wrong length
assert_raise(IPAddr::AddressFamilyError) {
- IPAddr.ntop("192.168.1.1")
+ IPAddr.ntop("192.168.1.1".b)
}
-
assert_raise(IPAddr::AddressFamilyError) {
- IPAddr.ntop("\xC0\xA8\x01\xFF1")
+ IPAddr.ntop("\xC0\xA8\x01\xFF1".b)
+ }
+ ## UTF-8
+ assert_raise(IPAddr::InvalidAddressError) {
+ IPAddr.ntop("192.168.1.1")
+ }
+ assert_raise(IPAddr::InvalidAddressError) {
+ IPAddr.ntop("\x0A\x0A\x0A\x0A")
+ }
+ assert_raise(IPAddr::InvalidAddressError) {
+ IPAddr.ntop("\x0A\xE7\x8C\xAB")
+ }
+ assert_raise(IPAddr::InvalidAddressError) {
+ IPAddr.ntop("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01")
+ }
+ assert_raise(IPAddr::InvalidAddressError) {
+ IPAddr.ntop("\xFE\x80\x00\x00\x00\x00\x00\x00\xF0\x9F\x99\x85\xF0\x9F\x99\x86")
}
end
@@ -244,6 +271,29 @@ class TC_IPAddr < Test::Unit::TestCase
assert_equal(a.netmask, "255.255.255.0")
end
+ def test_wildcard_mask
+ a = IPAddr.new("192.168.1.2/1")
+ assert_equal(a.wildcard_mask, "127.255.255.255")
+
+ a = IPAddr.new("192.168.1.2/8")
+ assert_equal(a.wildcard_mask, "0.255.255.255")
+
+ a = IPAddr.new("192.168.1.2/16")
+ assert_equal(a.wildcard_mask, "0.0.255.255")
+
+ a = IPAddr.new("192.168.1.2/24")
+ assert_equal(a.wildcard_mask, "0.0.0.255")
+
+ a = IPAddr.new("192.168.1.2/32")
+ assert_equal(a.wildcard_mask, "0.0.0.0")
+
+ a = IPAddr.new("3ffe:505:2::/48")
+ assert_equal(a.wildcard_mask, "0000:0000:0000:ffff:ffff:ffff:ffff:ffff")
+
+ a = IPAddr.new("3ffe:505:2::/128")
+ assert_equal(a.wildcard_mask, "0000:0000:0000:0000:0000:0000:0000:0000")
+ end
+
def test_zone_id
a = IPAddr.new("192.168.1.2")
assert_raise(IPAddr::InvalidAddressError) { a.zone_id = '%ab0' }
@@ -396,6 +446,12 @@ class TC_Operator < Test::Unit::TestCase
assert_equal(true, IPAddr.new('::1').loopback?)
assert_equal(false, IPAddr.new('::').loopback?)
assert_equal(false, IPAddr.new('3ffe:505:2::1').loopback?)
+
+ assert_equal(true, IPAddr.new('::ffff:127.0.0.1').loopback?)
+ assert_equal(true, IPAddr.new('::ffff:127.127.1.1').loopback?)
+ assert_equal(false, IPAddr.new('::ffff:0.0.0.0').loopback?)
+ assert_equal(false, IPAddr.new('::ffff:192.168.2.0').loopback?)
+ assert_equal(false, IPAddr.new('::ffff:255.0.0.0').loopback?)
end
def test_private?
@@ -426,6 +482,26 @@ class TC_Operator < Test::Unit::TestCase
assert_equal(true, IPAddr.new('fc84:8bf7:e905::1').private?)
assert_equal(true, IPAddr.new('fd84:8bf7:e905::1').private?)
assert_equal(false, IPAddr.new('fe84:8bf7:e905::1').private?)
+
+ assert_equal(false, IPAddr.new('::ffff:0.0.0.0').private?)
+ assert_equal(false, IPAddr.new('::ffff:127.0.0.1').private?)
+
+ assert_equal(false, IPAddr.new('::ffff:8.8.8.8').private?)
+ assert_equal(true, IPAddr.new('::ffff:10.0.0.0').private?)
+ assert_equal(true, IPAddr.new('::ffff:10.255.255.255').private?)
+ assert_equal(false, IPAddr.new('::ffff:11.255.1.1').private?)
+
+ assert_equal(false, IPAddr.new('::ffff:172.15.255.255').private?)
+ assert_equal(true, IPAddr.new('::ffff:172.16.0.0').private?)
+ assert_equal(true, IPAddr.new('::ffff:172.31.255.255').private?)
+ assert_equal(false, IPAddr.new('::ffff:172.32.0.0').private?)
+
+ assert_equal(false, IPAddr.new('::ffff:190.168.0.0').private?)
+ assert_equal(true, IPAddr.new('::ffff:192.168.0.0').private?)
+ assert_equal(true, IPAddr.new('::ffff:192.168.255.255').private?)
+ assert_equal(false, IPAddr.new('::ffff:192.169.0.0').private?)
+
+ assert_equal(false, IPAddr.new('::ffff:169.254.0.1').private?)
end
def test_link_local?
@@ -443,6 +519,15 @@ class TC_Operator < Test::Unit::TestCase
assert_equal(false, IPAddr.new('fb84:8bf7:e905::1').link_local?)
assert_equal(true, IPAddr.new('fe80::dead:beef:cafe:1234').link_local?)
+
+ assert_equal(false, IPAddr.new('::ffff:0.0.0.0').link_local?)
+ assert_equal(false, IPAddr.new('::ffff:127.0.0.1').link_local?)
+ assert_equal(false, IPAddr.new('::ffff:10.0.0.0').link_local?)
+ assert_equal(false, IPAddr.new('::ffff:172.16.0.0').link_local?)
+ assert_equal(false, IPAddr.new('::ffff:192.168.0.0').link_local?)
+
+ assert_equal(true, IPAddr.new('::ffff:169.254.1.1').link_local?)
+ assert_equal(true, IPAddr.new('::ffff:169.254.254.255').link_local?)
end
def test_hash
diff --git a/test/test_mutex_m.rb b/test/test_mutex_m.rb
deleted file mode 100644
index f938e71729..0000000000
--- a/test/test_mutex_m.rb
+++ /dev/null
@@ -1,79 +0,0 @@
-# frozen_string_literal: false
-require 'test/unit'
-require 'mutex_m'
-
-class TestMutexM < Test::Unit::TestCase
- def test_cv_wait
- o = Object.new
- o.instance_variable_set(:@foo, nil)
- o.extend(Mutex_m)
- c = Thread::ConditionVariable.new
- t = Thread.start {
- o.synchronize do
- until foo = o.instance_variable_get(:@foo)
- c.wait(o)
- end
- foo
- end
- }
- sleep(0.0001)
- o.synchronize do
- o.instance_variable_set(:@foo, "abc")
- end
- c.signal
- assert_equal "abc", t.value
- end
-
- class KeywordInitializeParent
- def initialize(x:)
- end
- end
-
- class KeywordInitializeChild < KeywordInitializeParent
- include Mutex_m
- def initialize
- super(x: 1)
- end
- end
-
- def test_initialize_with_keyword_arg
- assert KeywordInitializeChild.new
- end
-
- class NoArgInitializeParent
- def initialize
- end
- end
-
- class NoArgInitializeChild < NoArgInitializeParent
- include Mutex_m
- def initialize
- super()
- end
- end
-
- def test_initialize_no_args
- assert NoArgInitializeChild.new
- end
-
- def test_alias_extended_object
- object = Object.new
- object.extend(Mutex_m)
-
- assert object.respond_to?(:locked?)
- assert object.respond_to?(:lock)
- assert object.respond_to?(:unlock)
- assert object.respond_to?(:try_lock)
- assert object.respond_to?(:synchronize)
- end
-
- def test_alias_included_class
- object = NoArgInitializeChild.new
-
- assert object.respond_to?(:locked?)
- assert object.respond_to?(:lock)
- assert object.respond_to?(:unlock)
- assert object.respond_to?(:try_lock)
- assert object.respond_to?(:synchronize)
- end
-end
diff --git a/test/test_observer.rb b/test/test_observer.rb
deleted file mode 100644
index 8f8f24b3c5..0000000000
--- a/test/test_observer.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-# frozen_string_literal: true
-require 'test/unit'
-require 'observer'
-
-class TestObserver < Test::Unit::TestCase
- class TestObservable
- include Observable
-
- def notify(*args)
- changed
- notify_observers(*args)
- end
- end
-
- class TestWatcher
- def initialize(observable)
- @notifications = []
- observable.add_observer(self)
- end
-
- attr_reader :notifications
-
- def update(*args)
- @notifications << args
- end
- end
-
- def test_observers
- observable = TestObservable.new
-
- assert_equal(0, observable.count_observers)
-
- watcher1 = TestWatcher.new(observable)
-
- assert_equal(1, observable.count_observers)
-
- observable.notify("test", 123)
-
- watcher2 = TestWatcher.new(observable)
-
- assert_equal(2, observable.count_observers)
-
- observable.notify(42)
-
- assert_equal([["test", 123], [42]], watcher1.notifications)
- assert_equal([[42]], watcher2.notifications)
-
- observable.delete_observer(watcher1)
-
- assert_equal(1, observable.count_observers)
-
- observable.notify(:cats)
-
- assert_equal([["test", 123], [42]], watcher1.notifications)
- assert_equal([[42], [:cats]], watcher2.notifications)
-
- observable.delete_observers
-
- assert_equal(0, observable.count_observers)
-
- observable.notify("nope")
-
- assert_equal([["test", 123], [42]], watcher1.notifications)
- assert_equal([[42], [:cats]], watcher2.notifications)
- end
-end
diff --git a/test/test_pp.rb b/test/test_pp.rb
index bb2299a3fa..2fdd5df114 100644
--- a/test/test_pp.rb
+++ b/test/test_pp.rb
@@ -28,6 +28,13 @@ class PPTest < Test::Unit::TestCase
end
assert_equal(%(""\n), PP.pp(o, "".dup))
end
+
+ def test_range
+ assert_equal("0..1\n", PP.pp(0..1, "".dup))
+ assert_equal("0...1\n", PP.pp(0...1, "".dup))
+ assert_equal("0...\n", PP.pp(0..., "".dup))
+ assert_equal("...1\n", PP.pp(...1, "".dup))
+ end
end
class HasInspect
@@ -140,15 +147,18 @@ class PPCycleTest < Test::Unit::TestCase
a = S.new(1,2)
a.b = a
assert_equal("#<struct Struct::S a=1, b=#<struct Struct::S:...>>\n", PP.pp(a, ''.dup))
- assert_equal("#{a.inspect}\n", PP.pp(a, ''.dup))
+ assert_equal("#{a.inspect}\n", PP.pp(a, ''.dup)) unless RUBY_ENGINE == "truffleruby"
end
- if "3.2" <= RUBY_VERSION
+ if defined?(Data.define)
D = Data.define(:aaa, :bbb)
def test_data
a = D.new("aaa", "bbb")
assert_equal("#<data PPTestModule::PPCycleTest::D\n aaa=\"aaa\",\n bbb=\"bbb\">\n", PP.pp(a, ''.dup, 20))
assert_equal("#{a.inspect}\n", PP.pp(a, ''.dup))
+
+ b = Data.define(:a).new(42)
+ assert_equal("#{b.inspect}\n", PP.pp(b, ''.dup))
end
end
@@ -164,7 +174,7 @@ class PPCycleTest < Test::Unit::TestCase
end
def test_withinspect
- omit if RUBY_ENGINE == "jruby"
+ omit if RUBY_ENGINE == "jruby" or RUBY_ENGINE == "truffleruby"
a = []
a << HasInspect.new(a)
assert_equal("[<inspect:[...]>]\n", PP.pp(a, ''.dup))
@@ -228,10 +238,36 @@ if defined?(RubyVM)
AST = RubyVM::AbstractSyntaxTree
def test_lasgn_literal
ast = AST.parse("_=1")
- expected = "(SCOPE@1:0-1:3 tbl: [:_] args: nil body: (LASGN@1:0-1:3 :_ (LIT@1:2-1:3 1)))"
+ integer = RUBY_VERSION >= "3.4." ? "INTEGER" : "LIT"
+ expected = "(SCOPE@1:0-1:3 tbl: [:_] args: nil body: (LASGN@1:0-1:3 :_ (#{integer}@1:2-1:3 1)))"
assert_equal(expected, PP.singleline_pp(ast, ''.dup), ast)
end
end
end
+class PPInheritedTest < Test::Unit::TestCase
+ class PPSymbolHash < PP
+ def pp_hash_pair(k, v)
+ case k
+ when Symbol
+ text k.inspect.delete_prefix(":")
+ text ":"
+ group(1) {
+ breakable
+ pp v
+ }
+ else
+ super
+ end
+ end
+ end
+
+ def test_hash_override
+ obj = {k: 1, "": :null, "0": :zero, 100 => :ten}
+ assert_equal <<~EXPECT, PPSymbolHash.pp(obj, "".dup)
+ {k: 1, "": :null, "0": :zero, 100=>:ten}
+ EXPECT
+ end
+end
+
end
diff --git a/test/test_syslog.rb b/test/test_syslog.rb
deleted file mode 100644
index 842ae8df49..0000000000
--- a/test/test_syslog.rb
+++ /dev/null
@@ -1,193 +0,0 @@
-# frozen_string_literal: false
-# Please only run this test on machines reasonable for testing.
-# If in doubt, ask your admin.
-
-require 'test/unit'
-
-begin
- require 'syslog'
-rescue LoadError
- # suppress error messages.
-end
-
-class TestSyslog < Test::Unit::TestCase
- def test_new
- assert_raise(NoMethodError) {
- Syslog.new
- }
- end
-
- def test_instance
- sl1 = Syslog.instance
- sl2 = Syslog.open
- sl3 = Syslog.instance
-
- assert_equal(Syslog, sl1)
- assert_equal(Syslog, sl2)
- assert_equal(Syslog, sl3)
- ensure
- Syslog.close if Syslog.opened?
- end
-
- def test_open
- # default parameters
- Syslog.open
-
- assert_equal($0, Syslog.ident)
- assert_equal(Syslog::LOG_PID | Syslog::LOG_CONS, Syslog.options)
- assert_equal(Syslog::LOG_USER, Syslog.facility)
-
- # open without close
- assert_raise(RuntimeError) {
- Syslog.open
- }
-
- Syslog.close
-
- # given parameters
- options = Syslog::LOG_NDELAY | Syslog::LOG_PID
- Syslog.open("foo", options, Syslog::LOG_DAEMON)
-
- assert_equal('foo', Syslog.ident)
- assert_equal(options, Syslog.options)
- assert_equal(Syslog::LOG_DAEMON, Syslog.facility)
-
- Syslog.close
-
- # default parameters again (after close)
- Syslog.open
- Syslog.close
-
- assert_equal(nil, Syslog.ident)
- assert_equal(nil, Syslog.options)
- assert_equal(nil, Syslog.facility)
-
- # block
- param = nil
- Syslog.open { |syslog|
- param = syslog
- }
- assert_equal(Syslog, param)
- ensure
- Syslog.close if Syslog.opened?
- end
-
- def test_opened?
- assert_equal(false, Syslog.opened?)
-
- Syslog.open
- assert_equal(true, Syslog.opened?)
-
- Syslog.close
- assert_equal(false, Syslog.opened?)
-
- Syslog.open {
- assert_equal(true, Syslog.opened?)
- }
-
- assert_equal(false, Syslog.opened?)
- end
-
- def test_close
- assert_raise(RuntimeError) {
- Syslog.close
- }
- end
-
- def test_mask
- assert_equal(nil, Syslog.mask)
-
- Syslog.open
-
- orig = Syslog.mask
-
- Syslog.mask = Syslog.LOG_UPTO(Syslog::LOG_ERR)
- assert_equal(Syslog.LOG_UPTO(Syslog::LOG_ERR), Syslog.mask)
-
- Syslog.mask = Syslog.LOG_MASK(Syslog::LOG_CRIT)
- assert_equal(Syslog.LOG_MASK(Syslog::LOG_CRIT), Syslog.mask)
-
- Syslog.mask = orig
- ensure
- Syslog.close if Syslog.opened?
- end
-
- def syslog_line_regex(ident, message)
- /(?:^| )#{Regexp.quote(ident)}(?:\[([1-9][0-9]*)\])?(?: | ([1-9][0-9]*) - - ||[: ].* )#{Regexp.quote(message)}$/
- end
-
- def test_log
- IO.pipe {|stderr|
- pid = fork {
- stderr[0].close
- STDERR.reopen(stderr[1])
- stderr[1].close
-
- options = Syslog::LOG_PERROR | Syslog::LOG_NDELAY
-
- Syslog.open("syslog_test", options) { |sl|
- sl.log(Syslog::LOG_NOTICE, "test1 - hello, %s!", "world")
- sl.notice("test1 - hello, %s!", "world")
- }
-
- Syslog.open("syslog_test", options | Syslog::LOG_PID) { |sl|
- sl.log(Syslog::LOG_CRIT, "test2 - pid")
- sl.crit("test2 - pid")
- }
- exit!
- }
-
- stderr[1].close
- Process.waitpid(pid)
-
- # LOG_PERROR is not implemented on Cygwin or Solaris. Only test
- # these on systems that define it.
- return unless Syslog.const_defined?(:LOG_PERROR)
- # LOG_PERROR is defined but not supported yet on Android.
- return if RUBY_PLATFORM =~ /android/
-
- 2.times {
- re = syslog_line_regex("syslog_test", "test1 - hello, world!")
- line = stderr[0].gets
- # In AIX, each LOG_PERROR output line has an appended empty line.
- if /aix/ =~ RUBY_PLATFORM && line =~ /^$/
- line = stderr[0].gets
- end
- m = re.match(line)
- assert_not_nil(m)
- if m[1]
- # pid is written regardless of LOG_PID on OS X 10.7+
- assert_equal(pid, m[1].to_i)
- end
- }
-
- 2.times {
- re = syslog_line_regex("syslog_test", "test2 - pid")
- line = stderr[0].gets
- # In AIX, each LOG_PERROR output line has an appended empty line.
- if /aix/ =~ RUBY_PLATFORM && line =~ /^$/
- line = stderr[0].gets
- end
- m = re.match(line)
- assert_not_nil(m)
- output_pid = m[1] || m[2]
- assert_not_nil(output_pid)
- assert_equal(pid, output_pid.to_i)
- }
- }
- end
-
- def test_inspect
- Syslog.open { |sl|
- assert_equal(format('<#%s: opened=true, ident="%s", options=%d, facility=%d, mask=%d>',
- Syslog,
- sl.ident,
- sl.options,
- sl.facility,
- sl.mask),
- sl.inspect)
- }
-
- assert_equal(format('<#%s: opened=false>', Syslog), Syslog.inspect)
- end
-end if defined?(Syslog)
diff --git a/test/test_tempfile.rb b/test/test_tempfile.rb
index 6b087f9207..eddbac5d75 100644
--- a/test/test_tempfile.rb
+++ b/test/test_tempfile.rb
@@ -63,6 +63,22 @@ class TestTempfile < Test::Unit::TestCase
assert_match(/\.txt$/, File.basename(t.path))
end
+ def test_dup
+ t = tempfile
+ t2 = t.dup
+ t2.close
+ assert_equal true, t2.closed?
+ assert_equal false, t.closed?
+ end
+
+ def test_clone
+ t = tempfile
+ t2 = t.clone
+ t2.close
+ assert_equal true, t2.closed?
+ assert_equal false, t.closed?
+ end
+
def test_unlink
t = tempfile("foo")
path = t.path
@@ -209,8 +225,7 @@ puts Tempfile.new('foo').path
def test_tempfile_finalizer_does_not_run_if_unlinked
bug8768 = '[ruby-core:56521] [Bug #8768]'
- args = %w(--disable-gems -rtempfile)
- assert_in_out_err(args, <<-'EOS') do |(filename), (error)|
+ assert_in_out_err(%w(-rtempfile), <<-'EOS') do |(filename), (error)|
tmp = Tempfile.new('foo')
puts tmp.path
tmp.close
@@ -363,6 +378,14 @@ puts Tempfile.new('foo').path
assert_file.not_exist?(path)
end
+ def test_open
+ Tempfile.open {|f|
+ file = f.open
+ assert_kind_of File, file
+ assert_equal f.to_i, file.to_i
+ }
+ end
+
def test_open_traversal_dir
assert_mktmpdir_traversal do |traversal_path|
t = Tempfile.open([traversal_path, 'foo'])
diff --git a/test/test_timeout.rb b/test/test_timeout.rb
index c3349d081b..e900b10cd7 100644
--- a/test/test_timeout.rb
+++ b/test/test_timeout.rb
@@ -4,12 +4,33 @@ require 'timeout'
class TestTimeout < Test::Unit::TestCase
+ def test_work_is_done_in_same_thread_as_caller
+ assert_equal Thread.current, Timeout.timeout(10){ Thread.current }
+ end
+
+ def test_work_is_done_in_same_fiber_as_caller
+ require 'fiber' # needed for ruby 3.0 and lower
+ assert_equal Fiber.current, Timeout.timeout(10){ Fiber.current }
+ end
+
def test_non_timing_out_code_is_successful
assert_nothing_raised do
assert_equal :ok, Timeout.timeout(1){ :ok }
end
end
+ def test_allows_zero_seconds
+ assert_nothing_raised do
+ assert_equal :ok, Timeout.timeout(0){:ok}
+ end
+ end
+
+ def test_allows_nil_seconds
+ assert_nothing_raised do
+ assert_equal :ok, Timeout.timeout(nil){:ok}
+ end
+ end
+
def test_included
c = Class.new do
include Timeout
@@ -41,25 +62,87 @@ class TestTimeout < Test::Unit::TestCase
end
end
+ def test_nested_timeout
+ a = nil
+ assert_raise(Timeout::Error) do
+ Timeout.timeout(0.1) {
+ Timeout.timeout(1) {
+ nil while true
+ }
+ a = 1
+ }
+ end
+ assert_nil a
+ end
+
+ class MyNewErrorOuter < StandardError; end
+ class MyNewErrorInner < StandardError; end
+
+ # DOES NOT fail with
+ # - raise new(message) if exc.equal?(e)
+ # + raise new(message) if exc.class == e.class
+ def test_nested_timeout_error_identity
+ begin
+ Timeout.timeout(0.1, MyNewErrorOuter) {
+ Timeout.timeout(1, MyNewErrorInner) {
+ nil while true
+ }
+ }
+ rescue => e
+ assert e.class == MyNewErrorOuter
+ end
+ end
+
+ # DOES fail with
+ # - raise new(message) if exc.equal?(e)
+ # + raise new(message) if exc.class == e.class
+ def test_nested_timeout_which_error_bubbles_up
+ raised_exception = nil
+ begin
+ Timeout.timeout(0.1) {
+ Timeout.timeout(1) {
+ raise Timeout::ExitException.new("inner message")
+ }
+ }
+ rescue Exception => e
+ raised_exception = e
+ end
+
+ assert_equal 'inner message', raised_exception.message
+ end
+
def test_cannot_convert_into_time_interval
bug3168 = '[ruby-dev:41010]'
def (n = Object.new).zero?; false; end
assert_raise(TypeError, bug3168) {Timeout.timeout(n) { sleep 0.1 }}
end
- def test_skip_rescue
- bug8730 = '[Bug #8730]'
+ def test_skip_rescue_standarderror
e = nil
- assert_raise_with_message(Timeout::Error, /execution expired/, bug8730) do
+ assert_raise_with_message(Timeout::Error, /execution expired/) do
Timeout.timeout 0.01 do
begin
sleep 3
- rescue Exception => e
+ rescue => e
flunk "should not see any exception but saw #{e.inspect}"
end
end
end
- assert_nil(e, bug8730)
+ end
+
+ def test_raises_exception_internally
+ e = nil
+ assert_raise_with_message(Timeout::Error, /execution expired/) do
+ Timeout.timeout 0.01 do
+ begin
+ sleep 3
+ rescue Exception => exc
+ e = exc
+ raise
+ end
+ end
+ end
+ assert_equal Timeout::ExitException, e.class
end
def test_rescue_exit
@@ -127,11 +210,11 @@ class TestTimeout < Test::Unit::TestCase
bug11344 = '[ruby-dev:49179] [Bug #11344]'
ok = false
assert_raise(Timeout::Error) {
- Thread.handle_interrupt(Timeout::Error => :never) {
+ Thread.handle_interrupt(Timeout::ExitException => :never) {
Timeout.timeout(0.01) {
sleep 0.2
ok = true
- Thread.handle_interrupt(Timeout::Error => :on_blocking) {
+ Thread.handle_interrupt(Timeout::ExitException => :on_blocking) {
sleep 0.2
}
}
diff --git a/test/test_tmpdir.rb b/test/test_tmpdir.rb
index eada416f6f..054ca15d7a 100644
--- a/test/test_tmpdir.rb
+++ b/test/test_tmpdir.rb
@@ -115,4 +115,20 @@ class TestTmpdir < Test::Unit::TestCase
end
end
end
+
+ def test_ractor
+ assert_ractor(<<~'end;', require: "tmpdir")
+ r = Ractor.new do
+ Dir.mktmpdir() do |d|
+ Ractor.yield d
+ Ractor.receive
+ end
+ end
+ dir = r.take
+ assert_file.directory? dir
+ r.send true
+ r.take
+ assert_file.not_exist? dir
+ end;
+ end
end
diff --git a/test/test_trick.rb b/test/test_trick.rb
index 201dd50b9b..c5c19d079e 100644
--- a/test/test_trick.rb
+++ b/test/test_trick.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: false
+
require "test/unit"
require "ripper"
require "envutil"
@@ -12,24 +14,24 @@ class TestTRICK2013 < Test::Unit::TestCase
def test_kinaba
src = File.join(__dir__, "../sample/trick2013/kinaba/entry.rb")
expected = [*" ".."~"].join("") # all ASCII printables
- assert_in_out_err(["-W0", src], "", [expected])
+ assert_in_out_err(["-W0", "--disable-frozen-string-literal", src], "", [expected])
assert_equal(expected, File.read(src).chomp.chars.sort.join)
end
def test_mame
src = File.join(__dir__, "../sample/trick2013/mame/entry.rb")
ignore_dsp = "def open(_file, _mode); s = ''; def s.flush; self;end; yield s; end;"
- assert_in_out_err(["-W0"], ignore_dsp + File.read(src), File.read(src).lines(chomp: true), timeout: 60)
+ assert_in_out_err(["-W0", "--disable-frozen-string-literal"], ignore_dsp + File.read(src), File.read(src).lines(chomp: true), timeout: 60)
end
def test_shinh
src = File.join(__dir__, "../sample/trick2013/shinh/entry.rb")
- assert_in_out_err(["-W0", src], "", [])
+ assert_in_out_err(["-W0", "--disable-frozen-string-literal", src], "", [])
end
def test_yhara
src = File.join(__dir__, "../sample/trick2013/yhara/entry.rb")
- assert_in_out_err(["-W0", src], "", ["JUST ANOTHER RUBY HACKER"])
+ assert_in_out_err(["-W0", "--disable-frozen-string-literal", src], "", ["JUST ANOTHER RUBY HACKER"])
end
end
@@ -45,7 +47,7 @@ class TestTRICK2015 < Test::Unit::TestCase
end
pi = "3#{ a - b }"
- assert_in_out_err(["-W0", src], "", [pi], timeout: 60)
+ assert_in_out_err(["-W0", "--disable-frozen-string-literal", src], "", [pi], timeout: 60)
assert_equal(pi[0, 242], Ripper.tokenize(File.read(src)).grep(/\S/).map{|t|t.size%10}.join)
end
@@ -60,7 +62,7 @@ class TestTRICK2015 < Test::Unit::TestCase
s << n.to_s
end
- assert_in_out_err(["-W0", src, "27"], "", s)
+ assert_in_out_err(["-W0", "--disable-frozen-string-literal", src, "27"], "", s)
end
def test_monae
@@ -78,13 +80,13 @@ class TestTRICK2015 < Test::Unit::TestCase
end
expected = /\A#{ expected.map {|s| "#{ Regexp.quote(s) }\s*\n" }.join }\z/
- assert_in_out_err(["-W0", src], "", expected)
+ assert_in_out_err(["-W0", "--disable-frozen-string-literal", src], "", expected)
end
def test_eregon
src = File.join(__dir__, "../sample/trick2015/eregon/entry.rb")
- assert_in_out_err(["-W0", src], "", <<END.lines(chomp: true))
+ assert_in_out_err(["-W0", "--disable-frozen-string-literal", src], "", <<END.lines(chomp: true))
1 9 4 2 3 8 7 6 5
3 7 2 6 5 1 4 8 9
8 5 6 7 4 9 2 3 1
@@ -123,7 +125,7 @@ p cnf 3 5
1 3 0
END
- assert_in_out_err(["-W0", src], inp, ["s SATISFIABLE", "v 1 2 -3"])
+ assert_in_out_err(["-W0", "--disable-frozen-string-literal", src], inp, ["s SATISFIABLE", "v 1 2 -3"])
end
end
@@ -131,17 +133,17 @@ class TestTRICK2018 < Test::Unit::TestCase
def test_01_kinaba
src = File.join(__dir__, "../sample/trick2018/01-kinaba/entry.rb")
- assert_in_out_err(["-W0", src], "", [])
+ assert_in_out_err(["-W0", "--disable-frozen-string-literal", src], "", [])
end
def test_02_mame
src = File.join(__dir__, "../sample/trick2018/02-mame/entry.rb")
ignore_sleep = "def sleep(_); end;"
- assert_in_out_err(["-W0"], ignore_sleep + File.read(src)) do |stdout, _stderr, _status|
+ assert_in_out_err(["-W0", "--disable-frozen-string-literal"], ignore_sleep + File.read(src)) do |stdout, _stderr, _status|
code = stdout.join("\n") + "\n"
expected = code.lines(chomp: true)
- assert_in_out_err(["-W0"], ignore_sleep + code, expected)
+ assert_in_out_err(["-W0", "--disable-frozen-string-literal"], ignore_sleep + code, expected)
end
end
@@ -149,7 +151,7 @@ class TestTRICK2018 < Test::Unit::TestCase
src = File.join(__dir__, "../sample/trick2018/03-tompng/entry.rb")
# only syntax check because it requires chunky_png
- assert_in_out_err(["-W0", "-c", src], "", ["Syntax OK"])
+ assert_in_out_err(["-W0", "--disable-frozen-string-literal", "-c", src], "", ["Syntax OK"])
end
def test_04_colin
@@ -172,7 +174,7 @@ class TestTRICK2018 < Test::Unit::TestCase
end
end
END
- assert_in_out_err(["-W0"], code, <<END.lines(chomp: true), encoding: "UTF-8")
+ assert_in_out_err(["-W0", "--disable-frozen-string-literal"], code, <<END.lines(chomp: true), encoding: "UTF-8")
Math
Addition
One plus one equals two.
@@ -187,7 +189,7 @@ END
src = File.join(__dir__, "../sample/trick2018/05-tompng/entry.rb")
# only syntax check because it generates 3D model data
- assert_in_out_err(["-W0", "-c", src], "", ["Syntax OK"])
+ assert_in_out_err(["-W0", "--disable-frozen-string-literal", "-c", src], "", ["Syntax OK"])
end
end
@@ -196,21 +198,45 @@ class TestTRICK2022 < Test::Unit::TestCase
src = File.join(__dir__, "../sample/trick2022/01-tompng/entry.rb")
# only syntax check because it requires matrix
- assert_in_out_err(["-W0", "-c", src], "", ["Syntax OK"])
+ assert_in_out_err(["-W0", "--disable-frozen-string-literal", "-c", src], "", ["Syntax OK"])
end
def test_02_tompng
src = File.join(__dir__, "../sample/trick2022/02-tompng/entry.rb")
# only syntax check because it works as a web server
- assert_in_out_err(["-W0", "-c", src], "", ["Syntax OK"])
+ assert_in_out_err(["-W0", "--disable-frozen-string-literal", "-c", src], "", ["Syntax OK"])
end
def test_03_mame
src = File.join(__dir__, "../sample/trick2022/03-mame/entry.rb")
# TODO
- assert_in_out_err(["-W0", "-c", src], "", ["Syntax OK"])
+ assert_in_out_err(["-W0", "--disable-frozen-string-literal", "-c", src], "", ["Syntax OK"])
+ end
+end
+
+class TestRubyKaigi2023🥢 < Test::Unit::TestCase
+ CHOPSTICKS = [<<~'0', <<~'1'] # by mame
+ BEGIN{q=:Ruby};p||=:Enjoy;END{puts p,q||2023}
+ 0
+ q=print(q||"/:|}\n")||p&&:@Matsumoto;p=:Kaigi
+ 1
+
+ def test_chopsticks_0
+ assert_in_out_err(%w[-W0], CHOPSTICKS[0], %w[Enjoy Ruby])
+ end
+
+ def test_chopsticks_1
+ assert_in_out_err(%w[-W0], CHOPSTICKS[1], %w[/:|}])
+ end
+
+ def test_chopsticks_0_1
+ assert_in_out_err(%w[-W0], "#{CHOPSTICKS[0]}\n#{CHOPSTICKS[1]}", %w[RubyKaigi @Matsumoto])
+ end
+
+ def test_chopsticks_1_0
+ assert_in_out_err(%w[-W0], "#{CHOPSTICKS[1]}\n#{CHOPSTICKS[0]}", %w[RubyKaigi 2023])
end
end
@@ -219,6 +245,8 @@ class TestAllRubyQuine < Test::Unit::TestCase
def test_all_ruby_quine
stdout_bak = $stdout
$stdout = StringIO.new
+ verbose_bak = $VERBOSE
+ $VERBOSE = nil
src = File.read(File.join(__dir__, "../sample/all-ruby-quine.rb"))
eval(src)
@@ -242,5 +270,6 @@ class TestAllRubyQuine < Test::Unit::TestCase
assert_equal(RUBY_VERSION, out)
ensure
$stdout = stdout_bak
+ $VERBOSE = verbose_bak
end
end
diff --git a/test/uri/test_generic.rb b/test/uri/test_generic.rb
index 3897c3d6ee..8209363b82 100644
--- a/test/uri/test_generic.rb
+++ b/test/uri/test_generic.rb
@@ -26,6 +26,17 @@ class URI::TestGeneric < Test::Unit::TestCase
assert_equal "postgres:///foo", URI("postgres:///foo").to_s
assert_equal "http:///foo", URI("http:///foo").to_s
assert_equal "http:/foo", URI("http:/foo").to_s
+
+ uri = URI('rel_path')
+ assert_equal "rel_path", uri.to_s
+ uri.scheme = 'http'
+ assert_equal "http:rel_path", uri.to_s
+ uri.host = 'h'
+ assert_equal "http://h/rel_path", uri.to_s
+ uri.port = 8080
+ assert_equal "http://h:8080/rel_path", uri.to_s
+ uri.host = nil
+ assert_equal "http::8080/rel_path", uri.to_s
end
def test_parse
@@ -977,6 +988,10 @@ class URI::TestGeneric < Test::Unit::TestCase
end
end
+ def test_split
+ assert_equal [nil, nil, nil, nil, nil, "", nil, nil, nil], URI.split("//")
+ end
+
class CaseInsensitiveEnv
def initialize(h={})
@h = {}
diff --git a/test/uri/test_parser.rb b/test/uri/test_parser.rb
index 72fb5901d9..75c02fe65b 100644
--- a/test/uri/test_parser.rb
+++ b/test/uri/test_parser.rb
@@ -78,5 +78,35 @@ class URI::TestParser < Test::Unit::TestCase
assert_equal(["http", nil, "[0::0]", nil, nil, "", nil, nil, nil], URI.split("http://[0::0]"))
assert_equal([nil, nil, "example.com", nil, nil, "", nil, nil, nil], URI.split("//example.com"))
assert_equal([nil, nil, "[0::0]", nil, nil, "", nil, nil, nil], URI.split("//[0::0]"))
+
+ assert_equal(["a", nil, nil, nil, nil, "", nil, nil, nil], URI.split("a:"))
+ assert_raise(URI::InvalidURIError) do
+ URI.parse("::")
+ end
+ assert_raise(URI::InvalidURIError) do
+ URI.parse("foo@example:foo")
+ end
+ end
+
+ def test_rfc2822_parse_relative_uri
+ pre = ->(length) {
+ " " * length + "\0"
+ }
+ parser = URI::RFC2396_Parser.new
+ assert_linear_performance((1..5).map {|i| 10**i}, pre: pre) do |uri|
+ assert_raise(URI::InvalidURIError) do
+ parser.split(uri)
+ end
+ end
+ end
+
+ def test_rfc3986_port_check
+ pre = ->(length) {"\t" * length + "a"}
+ uri = URI.parse("http://my.example.com")
+ assert_linear_performance((1..5).map {|i| 10**i}, pre: pre) do |port|
+ assert_raise(URI::InvalidComponentError) do
+ uri.port = port
+ end
+ end
end
end
diff --git a/test/win32ole/available_ole.rb b/test/win32ole/available_ole.rb
index ebc9baae66..af397e00b5 100644
--- a/test/win32ole/available_ole.rb
+++ b/test/win32ole/available_ole.rb
@@ -8,7 +8,7 @@ if defined?(WIN32OLE)
module_function
def sysmon_available?
- WIN32OLE_TYPE.new('System Monitor Control', 'SystemMonitor')
+ WIN32OLE::Type.new('System Monitor Control', 'SystemMonitor')
true
rescue
false
@@ -22,18 +22,18 @@ if defined?(WIN32OLE)
end
def msxml_available?
- !WIN32OLE_TYPELIB.typelibs.find { |t| t.name.start_with?('Microsoft XML') }.nil?
+ !WIN32OLE::TypeLib.typelibs.find { |t| t.name.start_with?('Microsoft XML') }.nil?
end
def event_param
method = if msxml_available?
- typelib = WIN32OLE_TYPELIB.typelibs.find { |t| t.name.start_with?('Microsoft XML') }
- ole_type = WIN32OLE_TYPE.new(typelib.name, 'IVBSAXContentHandler')
- WIN32OLE_METHOD.new(ole_type, 'startElement')
+ typelib = WIN32OLE::TypeLib.typelibs.find { |t| t.name.start_with?('Microsoft XML') }
+ ole_type = WIN32OLE::Type.new(typelib.name, 'IVBSAXContentHandler')
+ WIN32OLE::Method.new(ole_type, 'startElement')
elsif ado_available?
typelib = WIN32OLE.new('ADODB.Connection').ole_typelib
- ole_type = WIN32OLE_TYPE.new(typelib.name, 'Connection')
- WIN32OLE_METHOD.new(ole_type, 'WillConnect')
+ ole_type = WIN32OLE::Type.new(typelib.name, 'Connection')
+ WIN32OLE::Method.new(ole_type, 'WillConnect')
end
method && method.params[0]
end
diff --git a/test/win32ole/err_in_callback.rb b/test/win32ole/err_in_callback.rb
index aa6c9c7e3a..baecf780b6 100644
--- a/test/win32ole/err_in_callback.rb
+++ b/test/win32ole/err_in_callback.rb
@@ -2,9 +2,9 @@
require 'win32ole'
db = WIN32OLE.new('ADODB.Connection')
db.connectionString = "Driver={Microsoft Text Driver (*.txt; *.csv)};DefaultDir=.;"
-ev = WIN32OLE_EVENT.new(db)
+ev = WIN32OLE::Event.new(db)
ev.on_event('WillConnect') {|*args|
foo
}
db.open
-WIN32OLE_EVENT.message_loop
+WIN32OLE::Event.message_loop
diff --git a/test/win32ole/test_err_in_callback.rb b/test/win32ole/test_err_in_callback.rb
index 9ffaf9125f..bea5781bc9 100644
--- a/test/win32ole/test_err_in_callback.rb
+++ b/test/win32ole/test_err_in_callback.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
#
# test Win32OLE avoids cfp consistency error when the exception raised
-# in WIN32OLE_EVENT handler block. [ruby-dev:35450]
+# in WIN32OLE::Event handler block. [ruby-dev:35450]
#
begin
@@ -31,7 +31,7 @@ if defined?(WIN32OLE)
def available_adodb?
begin
WIN32OLE.new('ADODB.Connection')
- rescue WIN32OLERuntimeError
+ rescue WIN32OLE::RuntimeError
return false
end
return true
diff --git a/test/win32ole/test_folderitem2_invokeverb.rb b/test/win32ole/test_folderitem2_invokeverb.rb
index e11503ca2a..a1c2b2f472 100644
--- a/test/win32ole/test_folderitem2_invokeverb.rb
+++ b/test/win32ole/test_folderitem2_invokeverb.rb
@@ -44,7 +44,7 @@ if defined?(WIN32OLE)
assert_equal(0, links.size)
# Now create shortcut to @dummy_path
- arg = WIN32OLE_VARIANT.new("Link")
+ arg = WIN32OLE::Variant.new("Link")
@fi2.InvokeVerb(arg)
# Now search shortcut to @dummy_path
diff --git a/test/win32ole/test_nil2vtempty.rb b/test/win32ole/test_nil2vtempty.rb
index 49757d61b3..bd86403676 100644
--- a/test/win32ole/test_nil2vtempty.rb
+++ b/test/win32ole/test_nil2vtempty.rb
@@ -28,7 +28,7 @@ if defined?(WIN32OLE)
assert(rs)
assert_equal("_Recordset", rs.ole_type.name)
- rs = con.openSchema(4, [WIN32OLE_VARIANT::Empty, WIN32OLE_VARIANT::Empty, "DUMMY", "TABLE"])
+ rs = con.openSchema(4, [WIN32OLE::Variant::Empty, WIN32OLE::Variant::Empty, "DUMMY", "TABLE"])
assert(rs)
assert_equal("_Recordset", rs.ole_type.name)
end
diff --git a/test/win32ole/test_propertyputref.rb b/test/win32ole/test_propertyputref.rb
index 93edb50835..83418140c2 100644
--- a/test/win32ole/test_propertyputref.rb
+++ b/test/win32ole/test_propertyputref.rb
@@ -11,7 +11,7 @@ if defined?(WIN32OLE)
begin
@sapi = WIN32OLE.new('SAPI.SpVoice')
@sv = @sapi.voice
- rescue WIN32OLERuntimeError
+ rescue WIN32OLE::RuntimeError
@sapi = nil
end
end
diff --git a/test/win32ole/test_thread.rb b/test/win32ole/test_thread.rb
index cb34693064..b30b2349c5 100644
--- a/test/win32ole/test_thread.rb
+++ b/test/win32ole/test_thread.rb
@@ -14,7 +14,7 @@ if defined?(WIN32OLE)
t = Thread.__send__(meth) {
WIN32OLE.new('Scripting.Dictionary')
}
- assert_nothing_raised(WIN32OLERuntimeError, "[Bug #2618] Thread.#{meth}") {
+ assert_nothing_raised(WIN32OLE::RuntimeError, "[Bug #2618] Thread.#{meth}") {
t.join
}
end
diff --git a/test/win32ole/test_win32ole.rb b/test/win32ole/test_win32ole.rb
index 594830b8fa..e6347e89b4 100644
--- a/test/win32ole/test_win32ole.rb
+++ b/test/win32ole/test_win32ole.rb
@@ -34,23 +34,23 @@ if defined?(WIN32OLE)
assert_equal(1, @dict2.item("one"))
end
def test_non_exist_property
- assert_raise(WIN32OLERuntimeError) {
+ assert_raise(WIN32OLE::RuntimeError) {
@dict1.unknown_property = 1
}
end
def test_raise_message
- exc = assert_raise(WIN32OLERuntimeError) {
+ exc = assert_raise(WIN32OLE::RuntimeError) {
@dict1.add
}
assert_match(/^\(in OLE method `add': \)/, exc.message) #`
- exc = assert_raise(WIN32OLERuntimeError) {
+ exc = assert_raise(WIN32OLE::RuntimeError) {
@dict1._invoke(1, [], [])
}
assert_match(/^\(in OLE method `<dispatch id:1>': \)/, exc.message) #`
- exc = assert_raise(WIN32OLERuntimeError) {
+ exc = assert_raise(WIN32OLE::RuntimeError) {
@dict1.compareMode = -1
}
assert_match(/^\(in setting property `compareMode': \)/, exc.message) #`
@@ -167,6 +167,11 @@ if defined?(WIN32OLE)
assert_instance_of(WIN32OLE, @dict2)
end
+ def test_toplevel_constants_backward_compatibility
+ assert_equal(WIN32OLE::RuntimeError, ::WIN32OLERuntimeError)
+ assert_equal(WIN32OLE::QueryInterfaceError, ::WIN32OLEQueryInterfaceError)
+ end
+
def test_s_new_exc
assert_raise(TypeError) {
WIN32OLE.new(1)
@@ -184,7 +189,7 @@ if defined?(WIN32OLE)
def test_s_new_from_clsid
shell = WIN32OLE.new("{13709620-C279-11CE-A49E-444553540000}")
assert_instance_of(WIN32OLE, shell)
- exc = assert_raise(WIN32OLERuntimeError) {
+ exc = assert_raise(WIN32OLE::RuntimeError) {
WIN32OLE.new("{000}")
}
assert_match(/unknown OLE server: `\{000\}'/, exc.message) #`
@@ -341,13 +346,13 @@ if defined?(WIN32OLE)
fso = WIN32OLE.new("Scripting.FileSystemObject")
fname = fso.getTempName
begin
- obj = WIN32OLE_VARIANT.new([0x3042].pack("U*").force_encoding("UTF-8"))
+ obj = WIN32OLE::Variant.new([0x3042].pack("U*").force_encoding("UTF-8"))
WIN32OLE.codepage = WIN32OLE::CP_UTF8
assert_equal("\xE3\x81\x82".force_encoding("CP65001"), obj.value)
begin
WIN32OLE.codepage = 932 # Windows-31J
- rescue WIN32OLERuntimeError
+ rescue WIN32OLE::RuntimeError
end
if (WIN32OLE.codepage == 932)
assert_equal("\x82\xA0".force_encoding("CP932"), obj.value)
@@ -355,7 +360,7 @@ if defined?(WIN32OLE)
begin
WIN32OLE.codepage = 20932 # MS EUC-JP
- rescue WIN32OLERuntimeError
+ rescue WIN32OLE::RuntimeError
end
if (WIN32OLE.codepage == 20932)
assert_equal("\xA4\xA2".force_encoding("CP20932"), obj.value)
@@ -378,7 +383,7 @@ if defined?(WIN32OLE)
# This test fail if codepage 20932 (euc) is not installed.
begin
WIN32OLE.codepage = 20932
- rescue WIN32OLERuntimeError
+ rescue WIN32OLE::RuntimeError
end
if (WIN32OLE.codepage == 20932)
WIN32OLE.codepage = cp
@@ -405,7 +410,7 @@ if defined?(WIN32OLE)
def test_cp51932
cp = WIN32OLE.codepage
begin
- obj = WIN32OLE_VARIANT.new([0x3042].pack("U*").force_encoding("UTF-8"))
+ obj = WIN32OLE::Variant.new([0x3042].pack("U*").force_encoding("UTF-8"))
begin
WIN32OLE.codepage = 51932
rescue
@@ -426,13 +431,13 @@ if defined?(WIN32OLE)
begin
begin
WIN32OLE.locale = 1041
- rescue WIN32OLERuntimeError
+ rescue WIN32OLE::RuntimeError
STDERR.puts("\n#{__FILE__}:#{__LINE__}:#{self.class.name}.test_s_locale_set is skipped(Japanese locale is not installed)")
return
end
assert_equal(1041, WIN32OLE.locale)
WIN32OLE.locale = WIN32OLE::LOCALE_SYSTEM_DEFAULT
- assert_raise(WIN32OLERuntimeError) {
+ assert_raise(WIN32OLE::RuntimeError) {
WIN32OLE.locale = 111
}
assert_equal(WIN32OLE::LOCALE_SYSTEM_DEFAULT, WIN32OLE.locale)
@@ -445,13 +450,13 @@ if defined?(WIN32OLE)
begin
begin
WIN32OLE.locale = 0x0411
- rescue WIN32OLERuntimeError
+ rescue WIN32OLE::RuntimeError
end
if WIN32OLE.locale == 0x0411
- obj = WIN32OLE_VARIANT.new("\\100,000", WIN32OLE::VARIANT::VT_CY)
+ obj = WIN32OLE::Variant.new("\\100,000", WIN32OLE::VARIANT::VT_CY)
assert_equal("100000", obj.value)
- assert_raise(WIN32OLERuntimeError) {
- obj = WIN32OLE_VARIANT.new("$100.000", WIN32OLE::VARIANT::VT_CY)
+ assert_raise(WIN32OLE::RuntimeError) {
+ obj = WIN32OLE::Variant.new("$100.000", WIN32OLE::VARIANT::VT_CY)
}
else
STDERR.puts("\n#{__FILE__}:#{__LINE__}:#{self.class.name}.test_s_locale_change is skipped(Japanese locale is not installed)")
@@ -459,10 +464,10 @@ if defined?(WIN32OLE)
begin
WIN32OLE.locale = 1033
- rescue WIN32OLERuntimeError
+ rescue WIN32OLE::RuntimeError
end
if WIN32OLE.locale == 1033
- obj = WIN32OLE_VARIANT.new("$100,000", WIN32OLE::VARIANT::VT_CY)
+ obj = WIN32OLE::Variant.new("$100,000", WIN32OLE::VARIANT::VT_CY)
assert_equal("100000", obj.value)
else
STDERR.puts("\n#{__FILE__}:#{__LINE__}:#{self.class.name}.test_s_locale_change is skipped(US English locale is not installed)")
diff --git a/test/win32ole/test_win32ole_event.rb b/test/win32ole/test_win32ole_event.rb
index f02df53be7..d52f8cf9b3 100644
--- a/test/win32ole/test_win32ole_event.rb
+++ b/test/win32ole/test_win32ole_event.rb
@@ -28,17 +28,21 @@ swbemsink_available =
end
end
-if defined?(WIN32OLE_EVENT)
+if defined?(WIN32OLE::Event)
class TestWIN32OLE_EVENT < Test::Unit::TestCase
+ def test_toplevel_constants_backward_compatibility
+ assert_equal(WIN32OLE::Event, ::WIN32OLE_EVENT)
+ end
+
def test_s_new_exception
assert_raise(TypeError) {
- WIN32OLE_EVENT.new("A")
+ WIN32OLE::Event.new("A")
}
end
def test_s_new_non_exist_event
dict = WIN32OLE.new('Scripting.Dictionary')
assert_raise(RuntimeError) {
- WIN32OLE_EVENT.new(dict)
+ WIN32OLE::Event.new(dict)
}
end
end
@@ -58,7 +62,7 @@ if defined?(WIN32OLE_EVENT)
end
2.times do
- WIN32OLE_EVENT.message_loop
+ WIN32OLE::Event.message_loop
sleep 1
end
@@ -68,7 +72,7 @@ if defined?(WIN32OLE_EVENT)
seconds = EnvUtil.apply_timeout_scale(1)
while tries < 5 && instance_variable_get(watch_ivar) == orig_ivar
$stderr.puts "test_win32ole_event.rb: retrying and sleeping #{seconds}s until #{watch_ivar} is changed from #{orig_ivar.inspect}..."
- WIN32OLE_EVENT.message_loop
+ WIN32OLE::Event.message_loop
sleep(seconds)
tries += 1
seconds *= 2 # sleep at most 31s in total
@@ -86,24 +90,24 @@ if defined?(WIN32OLE_EVENT)
def test_s_new_non_exist_event
assert_raise(RuntimeError) {
- WIN32OLE_EVENT.new(@sws, 'XXXXX')
+ WIN32OLE::Event.new(@sws, 'XXXXX')
}
end
def test_s_new
- obj = WIN32OLE_EVENT.new(@sws, 'ISWbemSinkEvents')
- assert_instance_of(WIN32OLE_EVENT, obj)
- obj = WIN32OLE_EVENT.new(@sws)
- assert_instance_of(WIN32OLE_EVENT, obj)
+ obj = WIN32OLE::Event.new(@sws, 'ISWbemSinkEvents')
+ assert_instance_of(WIN32OLE::Event, obj)
+ obj = WIN32OLE::Event.new(@sws)
+ assert_instance_of(WIN32OLE::Event, obj)
end
def test_s_new_loop
exec_notification_query_async
- ev = WIN32OLE_EVENT.new(@sws)
+ ev = WIN32OLE::Event.new(@sws)
ev.on_event {|*args| default_handler(*args)}
message_loop
10.times do |i|
- WIN32OLE_EVENT.new(@sws)
+ WIN32OLE::Event.new(@sws)
message_loop
GC.start
end
@@ -115,7 +119,7 @@ if defined?(WIN32OLE_EVENT)
def test_on_event
exec_notification_query_async
- ev = WIN32OLE_EVENT.new(@sws, 'ISWbemSinkEvents')
+ ev = WIN32OLE::Event.new(@sws, 'ISWbemSinkEvents')
ev.on_event {|*args| default_handler(*args)}
message_loop(:@event)
assert_match(/OnObjectReady/, @event)
@@ -123,7 +127,7 @@ if defined?(WIN32OLE_EVENT)
def test_on_event_symbol
exec_notification_query_async
- ev = WIN32OLE_EVENT.new(@sws)
+ ev = WIN32OLE::Event.new(@sws)
ev.on_event(:OnObjectReady) {|*args|
handler1
}
@@ -163,7 +167,7 @@ if defined?(WIN32OLE_EVENT)
module ADO
end
def message_loop
- WIN32OLE_EVENT.message_loop
+ WIN32OLE::Event.message_loop
end
def default_handler(event, *args)
@@ -182,7 +186,7 @@ if defined?(WIN32OLE_EVENT)
end
def test_on_event2
- ev = WIN32OLE_EVENT.new(@db, 'ConnectionEvents')
+ ev = WIN32OLE::Event.new(@db, 'ConnectionEvents')
ev.on_event('WillConnect') {|*args| handler1}
ev.on_event('WillConnect') {|*args| handler2}
@db.open
@@ -191,7 +195,7 @@ if defined?(WIN32OLE_EVENT)
end
def test_on_event4
- ev = WIN32OLE_EVENT.new(@db, 'ConnectionEvents')
+ ev = WIN32OLE::Event.new(@db, 'ConnectionEvents')
ev.on_event{|*args| handler1}
ev.on_event{|*args| handler2}
ev.on_event('WillConnect'){|*args| handler3(*args)}
@@ -202,7 +206,7 @@ if defined?(WIN32OLE_EVENT)
end
def test_on_event5
- ev = WIN32OLE_EVENT.new(@db, 'ConnectionEvents')
+ ev = WIN32OLE::Event.new(@db, 'ConnectionEvents')
ev.on_event {|*args| default_handler(*args)}
ev.on_event('WillConnect'){|*args| handler3(*args)}
@db.open
@@ -213,7 +217,7 @@ if defined?(WIN32OLE_EVENT)
end
def test_unadvise
- ev = WIN32OLE_EVENT.new(@db, 'ConnectionEvents')
+ ev = WIN32OLE::Event.new(@db, 'ConnectionEvents')
ev.on_event {|*args| default_handler(*args)}
@db.open
message_loop
@@ -224,16 +228,16 @@ if defined?(WIN32OLE_EVENT)
@db.open
message_loop
assert_equal("", @event);
- assert_raise(WIN32OLERuntimeError) {
+ assert_raise(WIN32OLE::RuntimeError) {
ev.on_event {|*args| default_handler(*args)}
}
end
def test_on_event_with_outargs
- ev = WIN32OLE_EVENT.new(@db)
+ ev = WIN32OLE::Event.new(@db)
@db.connectionString = 'XXX' # set illegal connection string
- assert_raise(WIN32OLERuntimeError) {
+ assert_raise(WIN32OLE::RuntimeError) {
@db.open
}
ev.on_event_with_outargs('WillConnect'){|*args|
@@ -245,7 +249,7 @@ if defined?(WIN32OLE_EVENT)
end
def test_on_event_hash_return
- ev = WIN32OLE_EVENT.new(@db)
+ ev = WIN32OLE::Event.new(@db)
ev.on_event('WillConnect'){|*args|
{:return => 1, :ConnectionString => CONNSTR}
}
@@ -255,7 +259,7 @@ if defined?(WIN32OLE_EVENT)
end
def test_on_event_hash_return2
- ev = WIN32OLE_EVENT.new(@db)
+ ev = WIN32OLE::Event.new(@db)
ev.on_event('WillConnect'){|*args|
{:ConnectionString => CONNSTR}
}
@@ -265,7 +269,7 @@ if defined?(WIN32OLE_EVENT)
end
def test_on_event_hash_return3
- ev = WIN32OLE_EVENT.new(@db)
+ ev = WIN32OLE::Event.new(@db)
ev.on_event('WillConnect'){|*args|
{'ConnectionString' => CONNSTR}
}
@@ -275,7 +279,7 @@ if defined?(WIN32OLE_EVENT)
end
def test_on_event_hash_return4
- ev = WIN32OLE_EVENT.new(@db)
+ ev = WIN32OLE::Event.new(@db)
ev.on_event('WillConnect'){|*args|
{'return' => 1, 'ConnectionString' => CONNSTR}
}
@@ -285,7 +289,7 @@ if defined?(WIN32OLE_EVENT)
end
def test_on_event_hash_return5
- ev = WIN32OLE_EVENT.new(@db)
+ ev = WIN32OLE::Event.new(@db)
ev.on_event('WillConnect'){|*args|
{0 => CONNSTR}
}
@@ -295,7 +299,7 @@ if defined?(WIN32OLE_EVENT)
end
def test_off_event
- ev = WIN32OLE_EVENT.new(@db)
+ ev = WIN32OLE::Event.new(@db)
ev.on_event{handler1}
ev.off_event
@db.open
@@ -304,7 +308,7 @@ if defined?(WIN32OLE_EVENT)
end
def test_off_event_arg
- ev = WIN32OLE_EVENT.new(@db)
+ ev = WIN32OLE::Event.new(@db)
ev.on_event('WillConnect'){handler1}
ev.off_event('WillConnect')
@db.open
@@ -313,7 +317,7 @@ if defined?(WIN32OLE_EVENT)
end
def test_off_event_arg2
- ev = WIN32OLE_EVENT.new(@db)
+ ev = WIN32OLE::Event.new(@db)
ev.on_event('WillConnect'){handler1}
ev.on_event('ConnectComplete'){handler1}
ev.off_event('WillConnect')
@@ -323,7 +327,7 @@ if defined?(WIN32OLE_EVENT)
end
def test_off_event_sym_arg
- ev = WIN32OLE_EVENT.new(@db)
+ ev = WIN32OLE::Event.new(@db)
ev.on_event('WillConnect'){handler1}
ev.off_event(:WillConnect)
@db.open
@@ -382,7 +386,7 @@ if defined?(WIN32OLE_EVENT)
end
def test_handler1
- ev = WIN32OLE_EVENT.new(@db)
+ ev = WIN32OLE::Event.new(@db)
h1 = Handler1.new
ev.handler = h1
@db.open
@@ -395,7 +399,7 @@ if defined?(WIN32OLE_EVENT)
end
def test_handler2
- ev = WIN32OLE_EVENT.new(@db)
+ ev = WIN32OLE::Event.new(@db)
h2 = Handler2.new
ev.handler = h2
@db.open
diff --git a/test/win32ole/test_win32ole_method.rb b/test/win32ole/test_win32ole_method.rb
index a0e113e7f0..c84c4027ff 100644
--- a/test/win32ole/test_win32ole_method.rb
+++ b/test/win32ole/test_win32ole_method.rb
@@ -5,42 +5,46 @@ rescue LoadError
end
require "test/unit"
-if defined?(WIN32OLE_METHOD)
+if defined?(WIN32OLE::Method)
class TestWIN32OLE_METHOD < Test::Unit::TestCase
def setup
- ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
- @m_open = WIN32OLE_METHOD.new(ole_type, "open")
- @m_namespace = WIN32OLE_METHOD.new(ole_type, "namespace")
- @m_parent = WIN32OLE_METHOD.new(ole_type, "parent")
- @m_invoke = WIN32OLE_METHOD.new(ole_type, "invoke")
- @m_browse_for_folder = WIN32OLE_METHOD.new(ole_type, "BrowseForFolder")
+ ole_type = WIN32OLE::Type.new("Microsoft Shell Controls And Automation", "Shell")
+ @m_open = WIN32OLE::Method.new(ole_type, "open")
+ @m_namespace = WIN32OLE::Method.new(ole_type, "namespace")
+ @m_parent = WIN32OLE::Method.new(ole_type, "parent")
+ @m_invoke = WIN32OLE::Method.new(ole_type, "invoke")
+ @m_browse_for_folder = WIN32OLE::Method.new(ole_type, "BrowseForFolder")
- ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "File")
- @m_file_name = WIN32OLE_METHOD.new(ole_type, "name")
+ ole_type = WIN32OLE::Type.new("Microsoft Scripting Runtime", "File")
+ @m_file_name = WIN32OLE::Method.new(ole_type, "name")
+ end
+
+ def test_toplevel_constants_backward_compatibility
+ assert_equal(WIN32OLE::Method, ::WIN32OLE_METHOD)
end
def test_initialize
- ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ ole_type = WIN32OLE::Type.new("Microsoft Shell Controls And Automation", "Shell")
assert_raise(TypeError) {
- WIN32OLE_METHOD.new(1, 2)
+ WIN32OLE::Method.new(1, 2)
}
assert_raise(ArgumentError) {
- WIN32OLE_METHOD.new("foo")
+ WIN32OLE::Method.new("foo")
}
assert_raise(ArgumentError) {
- WIN32OLE_METHOD.new(ole_type)
+ WIN32OLE::Method.new(ole_type)
}
- assert_raise(WIN32OLERuntimeError) {
- WIN32OLE_METHOD.new(ole_type, "NonExistMethod")
+ assert_raise(WIN32OLE::RuntimeError) {
+ WIN32OLE::Method.new(ole_type, "NonExistMethod")
}
assert_raise(TypeError) {
- WIN32OLE_METHOD.new(ole_type, 1)
+ WIN32OLE::Method.new(ole_type, 1)
}
- method = WIN32OLE_METHOD.new(ole_type, "Open")
- assert_instance_of(WIN32OLE_METHOD, method)
- method = WIN32OLE_METHOD.new(ole_type, "open")
- assert_instance_of(WIN32OLE_METHOD, method)
+ method = WIN32OLE::Method.new(ole_type, "Open")
+ assert_instance_of(WIN32OLE::Method, method)
+ method = WIN32OLE::Method.new(ole_type, "open")
+ assert_instance_of(WIN32OLE::Method, method)
end
def test_name
@@ -119,7 +123,7 @@ if defined?(WIN32OLE_METHOD)
params = @m_browse_for_folder.params
assert_instance_of(Array, params)
assert_equal(4, params.size)
- assert_instance_of(WIN32OLE_PARAM, params[0])
+ assert_instance_of(WIN32OLE::Param, params[0])
end
def test_to_s
@@ -127,7 +131,7 @@ if defined?(WIN32OLE_METHOD)
end
def test_inspect
- assert_equal("#<WIN32OLE_METHOD:NameSpace>", @m_namespace.inspect)
+ assert_equal("#<WIN32OLE::Method:NameSpace>", @m_namespace.inspect)
end
end
diff --git a/test/win32ole/test_win32ole_method_event.rb b/test/win32ole/test_win32ole_method_event.rb
index 7758168872..ab409a1f2d 100644
--- a/test/win32ole/test_win32ole_method_event.rb
+++ b/test/win32ole/test_win32ole_method_event.rb
@@ -5,7 +5,7 @@ end
require 'test/unit'
-if defined?(WIN32OLE_METHOD)
+if defined?(WIN32OLE::Method)
require_relative 'available_ole'
class TestWIN32OLE_METHOD_EVENT < Test::Unit::TestCase
unless AvailableOLE.sysmon_available?
@@ -14,10 +14,10 @@ if defined?(WIN32OLE_METHOD)
end
else
def setup
- ole_type = WIN32OLE_TYPE.new('System Monitor Control', 'SystemMonitor')
- @on_dbl_click = WIN32OLE_METHOD.new(ole_type, 'OnDblClick')
- ole_type = WIN32OLE_TYPE.new('Microsoft Shell Controls And Automation', 'Shell')
- @namespace = WIN32OLE_METHOD.new(ole_type, 'namespace')
+ ole_type = WIN32OLE::Type.new('System Monitor Control', 'SystemMonitor')
+ @on_dbl_click = WIN32OLE::Method.new(ole_type, 'OnDblClick')
+ ole_type = WIN32OLE::Type.new('Microsoft Shell Controls And Automation', 'Shell')
+ @namespace = WIN32OLE::Method.new(ole_type, 'namespace')
end
def test_event?
diff --git a/test/win32ole/test_win32ole_param.rb b/test/win32ole/test_win32ole_param.rb
index 09452d1927..13ca9d0cfb 100644
--- a/test/win32ole/test_win32ole_param.rb
+++ b/test/win32ole/test_win32ole_param.rb
@@ -5,45 +5,49 @@ rescue LoadError
end
require "test/unit"
-if defined?(WIN32OLE_PARAM)
+if defined?(WIN32OLE::Param)
class TestWIN32OLE_PARAM < Test::Unit::TestCase
def setup
- ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "ShellLinkObject")
- m_geticonlocation = WIN32OLE_METHOD.new(ole_type, "GetIconLocation")
+ ole_type = WIN32OLE::Type.new("Microsoft Shell Controls And Automation", "ShellLinkObject")
+ m_geticonlocation = WIN32OLE::Method.new(ole_type, "GetIconLocation")
@param_pbs = m_geticonlocation.params[0]
- ole_type = WIN32OLE_TYPE.new("Microsoft HTML Object Library", "FontNames")
- m_count = WIN32OLE_METHOD.new(ole_type, "Count")
+ ole_type = WIN32OLE::Type.new("Microsoft HTML Object Library", "FontNames")
+ m_count = WIN32OLE::Method.new(ole_type, "Count")
@param_p = m_count.params[0]
- ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "FileSystemObject")
- m_copyfile = WIN32OLE_METHOD.new(ole_type, "CopyFile")
+ ole_type = WIN32OLE::Type.new("Microsoft Scripting Runtime", "FileSystemObject")
+ m_copyfile = WIN32OLE::Method.new(ole_type, "CopyFile")
@param_source = m_copyfile.params[0]
@param_overwritefiles = m_copyfile.params[2]
- ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "Dictionary")
- m_add = WIN32OLE_METHOD.new(ole_type, "Add")
+ ole_type = WIN32OLE::Type.new("Microsoft Scripting Runtime", "Dictionary")
+ m_add = WIN32OLE::Method.new(ole_type, "Add")
@param_key = m_add.params[0]
end
+ def test_constants_backward_compatibility
+ assert_equal(WIN32OLE::Param, ::WIN32OLE_PARAM)
+ end
+
def test_s_new
assert_raise(ArgumentError) {
- WIN32OLE_PARAM.new("hoge")
+ WIN32OLE::Param.new("hoge")
}
- ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "FileSystemObject")
- m_copyfile = WIN32OLE_METHOD.new(ole_type, "CopyFile")
+ ole_type = WIN32OLE::Type.new("Microsoft Scripting Runtime", "FileSystemObject")
+ m_copyfile = WIN32OLE::Method.new(ole_type, "CopyFile")
assert_raise(IndexError) {
- WIN32OLE_PARAM.new(m_copyfile, 4);
+ WIN32OLE::Param.new(m_copyfile, 4);
}
assert_raise(IndexError) {
- WIN32OLE_PARAM.new(m_copyfile, 0);
+ WIN32OLE::Param.new(m_copyfile, 0);
}
- param = WIN32OLE_PARAM.new(m_copyfile, 3)
+ param = WIN32OLE::Param.new(m_copyfile, 3)
assert_equal("OverWriteFiles", param.name)
- assert_equal(WIN32OLE_PARAM, param.class)
+ assert_equal(WIN32OLE::Param, param.class)
assert_equal(true, param.default)
- assert_equal("#<WIN32OLE_PARAM:OverWriteFiles=true>", param.inspect)
+ assert_equal("#<WIN32OLE::Param:OverWriteFiles=true>", param.inspect)
end
def test_name
@@ -91,8 +95,8 @@ if defined?(WIN32OLE_PARAM)
end
def test_inspect
- assert_equal("#<WIN32OLE_PARAM:Source>", @param_source.inspect)
- assert_equal("#<WIN32OLE_PARAM:OverWriteFiles=true>", @param_overwritefiles.inspect)
+ assert_equal("#<WIN32OLE::Param:Source>", @param_source.inspect)
+ assert_equal("#<WIN32OLE::Param:OverWriteFiles=true>", @param_overwritefiles.inspect)
end
end
end
diff --git a/test/win32ole/test_win32ole_param_event.rb b/test/win32ole/test_win32ole_param_event.rb
index a659a6d0f3..f5a16ead76 100644
--- a/test/win32ole/test_win32ole_param_event.rb
+++ b/test/win32ole/test_win32ole_param_event.rb
@@ -5,7 +5,7 @@ end
require 'test/unit'
-if defined?(WIN32OLE_PARAM)
+if defined?(WIN32OLE::Param)
require_relative 'available_ole'
class TestWIN32OLE_PARAM_EVENT < Test::Unit::TestCase
diff --git a/test/win32ole/test_win32ole_record.rb b/test/win32ole/test_win32ole_record.rb
index 65fd5f6a3c..49205ce53d 100644
--- a/test/win32ole/test_win32ole_record.rb
+++ b/test/win32ole/test_win32ole_record.rb
@@ -65,18 +65,24 @@ End Class
=end
-if defined?(WIN32OLE_RECORD)
+if defined?(WIN32OLE::Record)
+ class TestWIN32OLE_RECORD < Test::Unit::TestCase
+ def test_toplevel_constants_backward_compatibility
+ assert_equal(WIN32OLE::Record, ::WIN32OLE_RECORD)
+ end
+ end
+
def rbcomtest_exist?
WIN32OLE.new(PROGID_RBCOMTEST)
true
- rescue WIN32OLERuntimeError
+ rescue WIN32OLE::RuntimeError
false
end
class TestWIN32OLE_RECORD_BY_RBCOMTEST < Test::Unit::TestCase
unless rbcomtest_exist?
def test_dummy_for_skip_message
- omit "#{PROGID_RBCOMTEST} for WIN32OLE_RECORD test is not installed"
+ omit "#{PROGID_RBCOMTEST} for WIN32OLE::Record test is not installed"
end
else
def setup
@@ -84,42 +90,42 @@ if defined?(WIN32OLE_RECORD)
end
def test_s_new_from_win32ole
- rec = WIN32OLE_RECORD.new('Book', @obj)
+ rec = WIN32OLE::Record.new('Book', @obj)
assert(rec)
- assert_instance_of(WIN32OLE_RECORD, rec)
+ assert_instance_of(WIN32OLE::Record, rec)
end
def test_s_new_from_win32ole_typelib
tlib = @obj.ole_typelib
- rec = WIN32OLE_RECORD.new('Book', tlib)
+ rec = WIN32OLE::Record.new('Book', tlib)
assert(rec)
- assert_instance_of(WIN32OLE_RECORD, rec)
+ assert_instance_of(WIN32OLE::Record, rec)
end
def test_s_new_raise
- assert_raise(WIN32OLERuntimeError) {
- WIN32OLE_RECORD.new('NonExistRecordName', @obj)
+ assert_raise(WIN32OLE::RuntimeError) {
+ WIN32OLE::Record.new('NonExistRecordName', @obj)
}
assert_raise(ArgumentError) {
- WIN32OLE_RECORD.new
+ WIN32OLE::Record.new
}
assert_raise(ArgumentError) {
- WIN32OLE_RECORD.new('NonExistRecordName')
+ WIN32OLE::Record.new('NonExistRecordName')
}
end
def test_to_h
- rec = WIN32OLE_RECORD.new('Book', @obj)
+ rec = WIN32OLE::Record.new('Book', @obj)
assert_equal({'title'=>nil, 'cost'=>nil}, rec.to_h)
end
def test_typename
- rec = WIN32OLE_RECORD.new('Book', @obj)
+ rec = WIN32OLE::Record.new('Book', @obj)
assert_equal('Book', rec.typename)
end
def test_method_missing_getter
- rec = WIN32OLE_RECORD.new('Book', @obj)
+ rec = WIN32OLE::Record.new('Book', @obj)
assert_equal(nil, rec.title)
assert_raise(KeyError) {
rec.non_exist_name
@@ -127,14 +133,14 @@ if defined?(WIN32OLE_RECORD)
end
def test_method_missing_setter
- rec = WIN32OLE_RECORD.new('Book', @obj)
+ rec = WIN32OLE::Record.new('Book', @obj)
rec.title = "Ruby Book"
assert_equal("Ruby Book", rec.title)
end
def test_get_record_from_comserver
rec = @obj.getBook
- assert_instance_of(WIN32OLE_RECORD, rec)
+ assert_instance_of(WIN32OLE::Record, rec)
assert_equal("The Ruby Book", rec.title)
assert_equal(20, rec.cost)
end
@@ -143,16 +149,16 @@ if defined?(WIN32OLE_RECORD)
rec = @obj.getBooks
assert_instance_of(Array, rec)
assert_equal(2, rec.size)
- assert_instance_of(WIN32OLE_RECORD, rec[0])
+ assert_instance_of(WIN32OLE::Record, rec[0])
assert_equal("The CRuby Book", rec[0].title)
assert_equal(30, rec[0].cost)
- assert_instance_of(WIN32OLE_RECORD, rec[1])
+ assert_instance_of(WIN32OLE::Record, rec[1])
assert_equal("The JRuby Book", rec[1].title)
assert_equal(40, rec[1].cost)
end
def test_pass_record_parameter
- rec = WIN32OLE_RECORD.new('Book', @obj)
+ rec = WIN32OLE::Record.new('Book', @obj)
rec.title = "Ruby Book"
rec.cost = 60
book = @obj.getVer2BookByValBook(rec)
@@ -161,23 +167,23 @@ if defined?(WIN32OLE_RECORD)
end
def test_pass_variant_parameter_byref
- obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_VARIANT|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new(nil, WIN32OLE::VARIANT::VT_VARIANT|WIN32OLE::VARIANT::VT_BYREF)
@obj.getBookByRefBook(obj)
- assert_instance_of(WIN32OLE_RECORD, obj.value)
+ assert_instance_of(WIN32OLE::Record, obj.value)
book = obj.value
assert_equal("The Ruby Reference Book2", book.title)
assert_equal(44, book.cost)
end
def test_pass_record_parameter_byref
- book = WIN32OLE_RECORD.new('Book', @obj)
+ book = WIN32OLE::Record.new('Book', @obj)
@obj.getBookByRefBook(book)
assert_equal("The Ruby Reference Book2", book.title)
assert_equal(44, book.cost)
end
def test_pass_and_get_record_parameter_byref
- book = WIN32OLE_RECORD.new('Book', @obj)
+ book = WIN32OLE::Record.new('Book', @obj)
book.title = "Ruby Book"
book.cost = 60
@obj.getVer3BookByRefBook(book)
@@ -186,13 +192,13 @@ if defined?(WIN32OLE_RECORD)
end
def test_ole_instance_variable_get
- obj = WIN32OLE_RECORD.new('Book', @obj)
+ obj = WIN32OLE::Record.new('Book', @obj)
assert_equal(nil, obj.ole_instance_variable_get(:title))
assert_equal(nil, obj.ole_instance_variable_get('title'))
end
def test_ole_instance_variable_set
- book = WIN32OLE_RECORD.new('Book', @obj)
+ book = WIN32OLE::Record.new('Book', @obj)
book.ole_instance_variable_set(:title, "Ruby Book")
assert_equal("Ruby Book", book.title)
book.ole_instance_variable_set('title', "Ruby Book2")
@@ -200,8 +206,8 @@ if defined?(WIN32OLE_RECORD)
end
def test_inspect
- book = WIN32OLE_RECORD.new('Book', @obj)
- assert_equal(%q[#<WIN32OLE_RECORD(Book) {"title"=>nil, "cost"=>nil}>], book.inspect)
+ book = WIN32OLE::Record.new('Book', @obj)
+ assert_equal(%q[#<WIN32OLE::Record(Book) {"title"=>nil, "cost"=>nil}>], book.inspect)
end
end
end
diff --git a/test/win32ole/test_win32ole_type.rb b/test/win32ole/test_win32ole_type.rb
index 485b390d5c..9abcd68c9b 100644
--- a/test/win32ole/test_win32ole_type.rb
+++ b/test/win32ole/test_win32ole_type.rb
@@ -5,11 +5,14 @@ rescue LoadError
end
require "test/unit"
-if defined?(WIN32OLE_TYPE)
+if defined?(WIN32OLE::Type)
class TestWIN32OLE_TYPE < Test::Unit::TestCase
+ def test_toplevel_constants_backward_compatibility
+ assert_equal(WIN32OLE::Type, ::WIN32OLE_TYPE)
+ end
def test_s_progids
- progids = WIN32OLE_TYPE.progids
+ progids = WIN32OLE::Type.progids
assert_instance_of(Array, progids)
assert(progids.size > 0)
assert_instance_of(String, progids[0])
@@ -18,25 +21,25 @@ if defined?(WIN32OLE_TYPE)
def test_initialize
assert_raise(ArgumentError) {
- WIN32OLE_TYPE.new
+ WIN32OLE::Type.new
}
assert_raise(ArgumentError) {
- WIN32OLE_TYPE.new("foo")
+ WIN32OLE::Type.new("foo")
}
assert_raise(TypeError) {
- WIN32OLE_TYPE.new(1, 2)
+ WIN32OLE::Type.new(1, 2)
}
assert_raise(TypeError) {
- WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", 1)
+ WIN32OLE::Type.new("Microsoft Shell Controls And Automation", 1)
}
- assert_raise(WIN32OLERuntimeError) {
- WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "foo")
+ assert_raise(WIN32OLE::RuntimeError) {
+ WIN32OLE::Type.new("Microsoft Shell Controls And Automation", "foo")
}
- assert_raise(WIN32OLERuntimeError) {
- WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Application")
+ assert_raise(WIN32OLE::RuntimeError) {
+ WIN32OLE::Type.new("Microsoft Shell Controls And Automation", "Application")
}
- ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
- assert_instance_of(WIN32OLE_TYPE, ole_type)
+ ole_type = WIN32OLE::Type.new("Microsoft Shell Controls And Automation", "Shell")
+ assert_instance_of(WIN32OLE::Type, ole_type)
assert_equal("Shell", ole_type.name)
assert_equal("Class", ole_type.ole_type)
assert_equal("{13709620-C279-11CE-A49E-444553540000}", ole_type.guid)
@@ -53,8 +56,8 @@ if defined?(WIN32OLE_TYPE)
assert_equal([], ole_type.variables)
assert(ole_type.ole_methods.select{|m|/NameSpace/i =~ m.name}.size > 0)
- ole_type2 = WIN32OLE_TYPE.new("{13709620-C279-11CE-A49E-444553540000}", "Shell")
- assert_instance_of(WIN32OLE_TYPE, ole_type)
+ ole_type2 = WIN32OLE::Type.new("{13709620-C279-11CE-A49E-444553540000}", "Shell")
+ assert_instance_of(WIN32OLE::Type, ole_type)
assert_equal(ole_type.name, ole_type2.name)
assert_equal(ole_type.ole_type, ole_type2.ole_type)
assert_equal(ole_type.guid, ole_type2.guid)
@@ -76,7 +79,7 @@ if defined?(WIN32OLE_TYPE)
end
def setup
- @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ @ole_type = WIN32OLE::Type.new("Microsoft Shell Controls And Automation", "Shell")
end
def test_name
@@ -97,7 +100,7 @@ if defined?(WIN32OLE_TYPE)
def test_visible?
assert(@ole_type.visible?)
- ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "IShellDispatch")
+ ole_type = WIN32OLE::Type.new("Microsoft Shell Controls And Automation", "IShellDispatch")
assert(!ole_type.visible?)
end
@@ -107,13 +110,13 @@ if defined?(WIN32OLE_TYPE)
def test_major_version
assert_equal(0, @ole_type.major_version)
- # ole_type = WIN32OLE_TYPE.new("Microsoft Word 11.0 Object Library", "Documents")
+ # ole_type = WIN32OLE::Type.new("Microsoft Word 11.0 Object Library", "Documents")
# assert_equal(8, ole_type.major_version)
end
def test_minor_version
assert_equal(0, @ole_type.minor_version)
- # ole_type = WIN32OLE_TYPE.new("Microsoft Word 11.0 Object Library", "Documents")
+ # ole_type = WIN32OLE::Type.new("Microsoft Word 11.0 Object Library", "Documents")
# assert_equal(3, ole_type.minor_version)
end
@@ -126,20 +129,20 @@ if defined?(WIN32OLE_TYPE)
end
def test_src_type
- ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "DriveTypeConst")
+ ole_type = WIN32OLE::Type.new("Microsoft Scripting Runtime", "DriveTypeConst")
assert_match(/__MIDL___MIDL_itf_scrrun_/, ole_type.src_type)
assert_equal(nil, @ole_type.src_type)
end
def test_helpfile
assert_equal("", @ole_type.helpfile)
- ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "Folders")
+ ole_type = WIN32OLE::Type.new("Microsoft Scripting Runtime", "Folders")
assert_match(/VBENLR98\.CHM$/i, ole_type.helpfile)
end
def test_helpcontext
assert_equal(0, @ole_type.helpcontext)
- ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "Folders")
+ ole_type = WIN32OLE::Type.new("Microsoft Scripting Runtime", "Folders")
assert_equal(2181929, ole_type.helpcontext)
end
@@ -148,25 +151,25 @@ if defined?(WIN32OLE_TYPE)
assert_instance_of(Array, variables)
assert(variables.size == 0)
- ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "ShellSpecialFolderConstants")
+ ole_type = WIN32OLE::Type.new("Microsoft Shell Controls And Automation", "ShellSpecialFolderConstants")
variables = ole_type.variables
assert_instance_of(Array, variables)
assert(variables.size > 0)
- assert_instance_of(WIN32OLE_VARIABLE, variables[0])
+ assert_instance_of(WIN32OLE::Variable, variables[0])
end
def test_ole_methods
methods = @ole_type.ole_methods
assert_instance_of(Array, methods)
assert(methods.size > 0)
- assert_instance_of(WIN32OLE_METHOD, methods[0]);
+ assert_instance_of(WIN32OLE::Method, methods[0]);
assert(methods.collect{|m| m.name}.include?("Application"))
end
def test_ole_typelib
tlib = @ole_type.ole_typelib
- assert_instance_of(WIN32OLE_TYPELIB, tlib)
+ assert_instance_of(WIN32OLE::TypeLib, tlib)
assert_equal("Microsoft Shell Controls And Automation", tlib.name)
end
@@ -178,20 +181,20 @@ if defined?(WIN32OLE_TYPE)
end
def test_inspect
- assert_equal("#<WIN32OLE_TYPE:Shell>", @ole_type.inspect)
+ assert_equal("#<WIN32OLE::Type:Shell>", @ole_type.inspect)
end
- # WIN32OLE_TYPE.typelibs will be obsoleted.
+ # WIN32OLE::Type.typelibs will be obsoleted.
def test_s_typelibs
- tlibs = WIN32OLE_TYPE.typelibs.sort
- tlibs2 = WIN32OLE_TYPELIB.typelibs.collect{|t|t.name}.sort
+ tlibs = WIN32OLE::Type.typelibs.sort
+ tlibs2 = WIN32OLE::TypeLib.typelibs.collect{|t|t.name}.sort
assert_equal(tlibs2, tlibs)
end
- # WIN32OLE_TYPE.ole_classes will be obsoleted.
+ # WIN32OLE::Type.ole_classes will be obsoleted.
def test_s_ole_classes
- ots1 = WIN32OLE_TYPE.ole_classes("Microsoft Shell Controls And Automation")
- ots2 = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation").ole_types
+ ots1 = WIN32OLE::Type.ole_classes("Microsoft Shell Controls And Automation")
+ ots2 = WIN32OLE::TypeLib.new("Microsoft Shell Controls And Automation").ole_types
otns1 = ots1.collect{|t| t.name}.sort
otns2 = ots2.collect{|t| t.name}.sort
assert_equal(otns2, otns1)
diff --git a/test/win32ole/test_win32ole_type_event.rb b/test/win32ole/test_win32ole_type_event.rb
index ec46245cae..5d1c9fc44c 100644
--- a/test/win32ole/test_win32ole_type_event.rb
+++ b/test/win32ole/test_win32ole_type_event.rb
@@ -6,7 +6,7 @@ end
require 'test/unit'
-if defined?(WIN32OLE_TYPE)
+if defined?(WIN32OLE::Type)
require_relative 'available_ole'
class TestWIN32OLE_TYPE_EVENT < Test::Unit::TestCase
@@ -17,7 +17,7 @@ if defined?(WIN32OLE_TYPE)
else
def setup
- @ole_type = WIN32OLE_TYPE.new('System Monitor Control', 'SystemMonitor')
+ @ole_type = WIN32OLE::Type.new('System Monitor Control', 'SystemMonitor')
end
def test_implemented_ole_types
diff --git a/test/win32ole/test_win32ole_typelib.rb b/test/win32ole/test_win32ole_typelib.rb
index 321c019e53..31d6122756 100644
--- a/test/win32ole/test_win32ole_typelib.rb
+++ b/test/win32ole/test_win32ole_typelib.rb
@@ -5,10 +5,14 @@ rescue LoadError
end
require "test/unit"
-if defined?(WIN32OLE_TYPELIB)
+if defined?(WIN32OLE::TypeLib)
class TestWIN32OLE_TYPELIB < Test::Unit::TestCase
+ def test_toplevel_constants_backward_compatibility
+ assert_equal(WIN32OLE::TypeLib, ::WIN32OLE_TYPELIB)
+ end
+
def test_s_typelibs
- tlibs = WIN32OLE_TYPELIB.typelibs
+ tlibs = WIN32OLE::TypeLib.typelibs
assert_instance_of(Array, tlibs)
assert(tlibs.size > 0)
tlib = tlibs.find {|t| t.name == "Microsoft Shell Controls And Automation"}
@@ -17,100 +21,100 @@ if defined?(WIN32OLE_TYPELIB)
def test_initialize
assert_raise(ArgumentError) {
- WIN32OLE_TYPELIB.new(1,2,3,4)
+ WIN32OLE::TypeLib.new(1,2,3,4)
}
assert_raise(TypeError) {
- WIN32OLE_TYPELIB.new(100)
+ WIN32OLE::TypeLib.new(100)
}
- tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation")
- assert_instance_of(WIN32OLE_TYPELIB, tlib)
+ tlib = WIN32OLE::TypeLib.new("Microsoft Shell Controls And Automation")
+ assert_instance_of(WIN32OLE::TypeLib, tlib)
- tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation", 1.0)
- assert_instance_of(WIN32OLE_TYPELIB, tlib)
+ tlib = WIN32OLE::TypeLib.new("Microsoft Shell Controls And Automation", 1.0)
+ assert_instance_of(WIN32OLE::TypeLib, tlib)
- tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation", 1, 0)
- assert_instance_of(WIN32OLE_TYPELIB, tlib)
+ tlib = WIN32OLE::TypeLib.new("Microsoft Shell Controls And Automation", 1, 0)
+ assert_instance_of(WIN32OLE::TypeLib, tlib)
guid = tlib.guid
- tlib_by_guid = WIN32OLE_TYPELIB.new(guid, 1, 0)
- assert_instance_of(WIN32OLE_TYPELIB, tlib_by_guid)
+ tlib_by_guid = WIN32OLE::TypeLib.new(guid, 1, 0)
+ assert_instance_of(WIN32OLE::TypeLib, tlib_by_guid)
assert_equal("Microsoft Shell Controls And Automation" , tlib_by_guid.name)
path = tlib.path
- tlib_by_path = WIN32OLE_TYPELIB.new(path)
+ tlib_by_path = WIN32OLE::TypeLib.new(path)
assert_equal("Microsoft Shell Controls And Automation" , tlib_by_path.name)
- assert_raise(WIN32OLERuntimeError) {
- WIN32OLE_TYPELIB.new("Non Exist Type Library")
+ assert_raise(WIN32OLE::RuntimeError) {
+ WIN32OLE::TypeLib.new("Non Exist Type Library")
}
end
# #Bug:3907 [ruby-dev:42338]
def test_initialize_with_REG_EXPAND_SZ
- tlib = WIN32OLE_TYPELIB.new("Disk Management Snap-In Object Library")
- assert_instance_of(WIN32OLE_TYPELIB, tlib)
+ tlib = WIN32OLE::TypeLib.new("Disk Management Snap-In Object Library")
+ assert_instance_of(WIN32OLE::TypeLib, tlib)
end
def test_guid
- tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation")
+ tlib = WIN32OLE::TypeLib.new("Microsoft Shell Controls And Automation")
assert_equal("{50A7E9B0-70EF-11D1-B75A-00A0C90564FE}", tlib.guid)
end
def test_name
- tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation")
+ tlib = WIN32OLE::TypeLib.new("Microsoft Shell Controls And Automation")
assert_equal("Microsoft Shell Controls And Automation", tlib.name)
- tlib = WIN32OLE_TYPELIB.new("{50A7E9B0-70EF-11D1-B75A-00A0C90564FE}")
+ tlib = WIN32OLE::TypeLib.new("{50A7E9B0-70EF-11D1-B75A-00A0C90564FE}")
assert_equal("Microsoft Shell Controls And Automation", tlib.name)
end
def test_version
- tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation")
+ tlib = WIN32OLE::TypeLib.new("Microsoft Shell Controls And Automation")
assert_equal("1.0", tlib.version)
end
def test_major_version
- tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation")
+ tlib = WIN32OLE::TypeLib.new("Microsoft Shell Controls And Automation")
assert_equal(1, tlib.major_version)
end
def test_minor_version
- tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation")
+ tlib = WIN32OLE::TypeLib.new("Microsoft Shell Controls And Automation")
assert_equal(0, tlib.minor_version)
end
def test_path
- tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation")
+ tlib = WIN32OLE::TypeLib.new("Microsoft Shell Controls And Automation")
assert_match(/shell32\.dll$/i, tlib.path)
end
def test_visible?
- tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation")
+ tlib = WIN32OLE::TypeLib.new("Microsoft Shell Controls And Automation")
assert(tlib.visible?)
end
def test_library_name
- tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation")
+ tlib = WIN32OLE::TypeLib.new("Microsoft Shell Controls And Automation")
assert_equal("Shell32", tlib.library_name)
end
def test_to_s
- tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation")
+ tlib = WIN32OLE::TypeLib.new("Microsoft Shell Controls And Automation")
assert_equal("Microsoft Shell Controls And Automation", tlib.to_s)
end
def test_ole_types
- tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation")
+ tlib = WIN32OLE::TypeLib.new("Microsoft Shell Controls And Automation")
ole_types = tlib.ole_types
assert_instance_of(Array, ole_types)
assert(ole_types.size > 0)
- assert_instance_of(WIN32OLE_TYPE, ole_types[0])
+ assert_instance_of(WIN32OLE::Type, ole_types[0])
end
def test_inspect
- tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation")
- assert_equal("#<WIN32OLE_TYPELIB:Microsoft Shell Controls And Automation>", tlib.inspect)
+ tlib = WIN32OLE::TypeLib.new("Microsoft Shell Controls And Automation")
+ assert_equal("#<WIN32OLE::TypeLib:Microsoft Shell Controls And Automation>", tlib.inspect)
end
end
diff --git a/test/win32ole/test_win32ole_variable.rb b/test/win32ole/test_win32ole_variable.rb
index 8af3f987a8..646cf68915 100644
--- a/test/win32ole/test_win32ole_variable.rb
+++ b/test/win32ole/test_win32ole_variable.rb
@@ -5,19 +5,23 @@ rescue LoadError
end
require "test/unit"
-if defined?(WIN32OLE_VARIABLE)
+if defined?(WIN32OLE::Variable)
class TestWIN32OLE_VARIABLE < Test::Unit::TestCase
def setup
- ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "ShellSpecialFolderConstants")
+ ole_type = WIN32OLE::Type.new("Microsoft Shell Controls And Automation", "ShellSpecialFolderConstants")
@var1 = ole_type.variables.find {|v| v.name == 'ssfDESKTOP'}
- variables = WIN32OLE_TYPE.new("Microsoft Windows Installer Object Library", "Installer").variables
+ variables = WIN32OLE::Type.new("Microsoft Windows Installer Object Library", "Installer").variables
@var2 = variables.find {|v| v.name == 'UILevel'}
end
+ def test_toplevel_constants_backward_compatibility
+ assert_equal(WIN32OLE::Variable, ::WIN32OLE_VARIABLE)
+ end
+
def test_initialize
- assert_raise(TypeError) {WIN32OLE_VARIABLE.new}
+ assert_raise(TypeError) {WIN32OLE::Variable.new}
end
def test_name
@@ -58,8 +62,8 @@ if defined?(WIN32OLE_VARIABLE)
end
def test_inspect
- assert_equal("#<WIN32OLE_VARIABLE:ssfDESKTOP=0>", @var1.inspect)
- assert_equal("#<WIN32OLE_VARIABLE:UILevel=nil>", @var2.inspect)
+ assert_equal("#<WIN32OLE::Variable:ssfDESKTOP=0>", @var1.inspect)
+ assert_equal("#<WIN32OLE::Variable:UILevel=nil>", @var2.inspect)
end
end
diff --git a/test/win32ole/test_win32ole_variant.rb b/test/win32ole/test_win32ole_variant.rb
index 1cedf55ef3..13b9a229a5 100644
--- a/test/win32ole/test_win32ole_variant.rb
+++ b/test/win32ole/test_win32ole_variant.rb
@@ -5,7 +5,7 @@ rescue LoadError
end
require "test/unit"
-if defined?(WIN32OLE_VARIANT)
+if defined?(WIN32OLE::Variant)
class TestWIN32OLE_VARIANT < Test::Unit::TestCase
def setup
@@ -17,36 +17,40 @@ if defined?(WIN32OLE_VARIANT)
WIN32OLE.locale = @orglocale
end
+ def test_toplevel_constants_backward_compatibility
+ assert_equal(WIN32OLE::Variant, ::WIN32OLE_VARIANT)
+ end
+
def test_s_new
- obj = WIN32OLE_VARIANT.new('foo')
- assert_instance_of(WIN32OLE_VARIANT, obj)
+ obj = WIN32OLE::Variant.new('foo')
+ assert_instance_of(WIN32OLE::Variant, obj)
end
def test_s_new_exc
assert_raise(TypeError) {
- WIN32OLE_VARIANT.new(/foo/)
+ WIN32OLE::Variant.new(/foo/)
}
end
def test_s_new_ary
- obj = WIN32OLE_VARIANT.new([1])
- assert_instance_of(WIN32OLE_VARIANT, obj)
+ obj = WIN32OLE::Variant.new([1])
+ assert_instance_of(WIN32OLE::Variant, obj)
assert_raise(TypeError) {
- WIN32OLE_VARIANT.new([/foo/])
+ WIN32OLE::Variant.new([/foo/])
}
end
def test_s_new_no_argument
pat = /wrong number of arguments \(.*\b0\b.* 1\.\.3\)/
assert_raise_with_message(ArgumentError, pat) do
- WIN32OLE_VARIANT.new
+ WIN32OLE::Variant.new
end
end
def test_s_new_one_argument
ex = nil
begin
- WIN32OLE_VARIANT.new('foo')
+ WIN32OLE::Variant.new('foo')
rescue
ex = $!
end
@@ -54,247 +58,247 @@ if defined?(WIN32OLE_VARIANT)
end
def test_s_new_with_nil
- obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_I2)
+ obj = WIN32OLE::Variant.new(nil, WIN32OLE::VARIANT::VT_I2)
assert_equal(0, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_I2, obj.vartype)
- obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_I4)
+ obj = WIN32OLE::Variant.new(nil, WIN32OLE::VARIANT::VT_I4)
assert_equal(0, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_I4, obj.vartype)
- obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_R4)
+ obj = WIN32OLE::Variant.new(nil, WIN32OLE::VARIANT::VT_R4)
assert_equal(0, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_R4, obj.vartype)
- obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_R8)
+ obj = WIN32OLE::Variant.new(nil, WIN32OLE::VARIANT::VT_R8)
assert_equal(0, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_R8, obj.vartype)
- obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_CY)
+ obj = WIN32OLE::Variant.new(nil, WIN32OLE::VARIANT::VT_CY)
assert_equal("0", obj.value)
assert_equal(WIN32OLE::VARIANT::VT_CY, obj.vartype)
- obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_DATE)
+ obj = WIN32OLE::Variant.new(nil, WIN32OLE::VARIANT::VT_DATE)
assert_equal(Time.new(1899,12,30), obj.value)
assert_equal(WIN32OLE::VARIANT::VT_DATE, obj.vartype)
- obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_BSTR)
+ obj = WIN32OLE::Variant.new(nil, WIN32OLE::VARIANT::VT_BSTR)
assert_equal("", obj.value)
assert_equal(WIN32OLE::VARIANT::VT_BSTR, obj.vartype)
- obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_DISPATCH)
+ obj = WIN32OLE::Variant.new(nil, WIN32OLE::VARIANT::VT_DISPATCH)
assert_nil(obj.value)
assert_equal(WIN32OLE::VARIANT::VT_DISPATCH, obj.vartype)
- obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_BOOL)
+ obj = WIN32OLE::Variant.new(nil, WIN32OLE::VARIANT::VT_BOOL)
assert_equal(false, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_BOOL, obj.vartype)
- obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_VARIANT)
+ obj = WIN32OLE::Variant.new(nil, WIN32OLE::VARIANT::VT_VARIANT)
assert_nil(obj.value)
assert_equal(WIN32OLE::VARIANT::VT_VARIANT, obj.vartype)
- obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_I1)
+ obj = WIN32OLE::Variant.new(nil, WIN32OLE::VARIANT::VT_I1)
assert_equal(0, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_I1, obj.vartype)
- obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_UI1)
+ obj = WIN32OLE::Variant.new(nil, WIN32OLE::VARIANT::VT_UI1)
assert_equal(0, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_UI1, obj.vartype)
- obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_UI2)
+ obj = WIN32OLE::Variant.new(nil, WIN32OLE::VARIANT::VT_UI2)
assert_equal(0, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_UI2, obj.vartype)
- obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_UI4)
+ obj = WIN32OLE::Variant.new(nil, WIN32OLE::VARIANT::VT_UI4)
assert_equal(0, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_UI4, obj.vartype)
if defined?(WIN32OLE::VARIANT::VT_I8)
- obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_I8)
+ obj = WIN32OLE::Variant.new(nil, WIN32OLE::VARIANT::VT_I8)
assert_equal(0, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_I8, obj.vartype)
end
if defined?(WIN32OLE::VARIANT::VT_UI8)
- obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_UI8)
+ obj = WIN32OLE::Variant.new(nil, WIN32OLE::VARIANT::VT_UI8)
assert_equal(0, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_UI8, obj.vartype)
end
- obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_INT)
+ obj = WIN32OLE::Variant.new(nil, WIN32OLE::VARIANT::VT_INT)
assert_equal(0, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_INT, obj.vartype)
- obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_UINT)
+ obj = WIN32OLE::Variant.new(nil, WIN32OLE::VARIANT::VT_UINT)
assert_equal(0, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_UINT, obj.vartype)
end
def test_s_new_with_non_nil
- obj = WIN32OLE_VARIANT.new(2, WIN32OLE::VARIANT::VT_I2)
+ obj = WIN32OLE::Variant.new(2, WIN32OLE::VARIANT::VT_I2)
assert_equal(2, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_I2, obj.vartype)
- obj = WIN32OLE_VARIANT.new(3, WIN32OLE::VARIANT::VT_I4)
+ obj = WIN32OLE::Variant.new(3, WIN32OLE::VARIANT::VT_I4)
assert_equal(3, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_I4, obj.vartype)
- obj = WIN32OLE_VARIANT.new(4.5, WIN32OLE::VARIANT::VT_R4)
+ obj = WIN32OLE::Variant.new(4.5, WIN32OLE::VARIANT::VT_R4)
assert_equal(4.5, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_R4, obj.vartype)
- obj = WIN32OLE_VARIANT.new(5.5, WIN32OLE::VARIANT::VT_R8)
+ obj = WIN32OLE::Variant.new(5.5, WIN32OLE::VARIANT::VT_R8)
assert_equal(5.5, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_R8, obj.vartype)
- obj = WIN32OLE_VARIANT.new(600, WIN32OLE::VARIANT::VT_CY)
+ obj = WIN32OLE::Variant.new(600, WIN32OLE::VARIANT::VT_CY)
assert_equal("600", obj.value)
assert_equal(WIN32OLE::VARIANT::VT_CY, obj.vartype)
- obj = WIN32OLE_VARIANT.new("2001-06-15 12:17:34", WIN32OLE::VARIANT::VT_DATE)
+ obj = WIN32OLE::Variant.new("2001-06-15 12:17:34", WIN32OLE::VARIANT::VT_DATE)
assert_equal(Time.new(2001,06,15,12,17,34), obj.value)
assert_equal(WIN32OLE::VARIANT::VT_DATE, obj.vartype)
- obj = WIN32OLE_VARIANT.new("foo", WIN32OLE::VARIANT::VT_BSTR)
+ obj = WIN32OLE::Variant.new("foo", WIN32OLE::VARIANT::VT_BSTR)
assert_equal("foo", obj.value)
assert_equal(WIN32OLE::VARIANT::VT_BSTR, obj.vartype)
- obj = WIN32OLE_VARIANT.new(true, WIN32OLE::VARIANT::VT_BOOL)
+ obj = WIN32OLE::Variant.new(true, WIN32OLE::VARIANT::VT_BOOL)
assert_equal(true, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_BOOL, obj.vartype)
- obj = WIN32OLE_VARIANT.new(2, WIN32OLE::VARIANT::VT_I1)
+ obj = WIN32OLE::Variant.new(2, WIN32OLE::VARIANT::VT_I1)
assert_equal(2, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_I1, obj.vartype)
- obj = WIN32OLE_VARIANT.new(3, WIN32OLE::VARIANT::VT_UI1)
+ obj = WIN32OLE::Variant.new(3, WIN32OLE::VARIANT::VT_UI1)
assert_equal(3, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_UI1, obj.vartype)
- obj = WIN32OLE_VARIANT.new(4, WIN32OLE::VARIANT::VT_UI2)
+ obj = WIN32OLE::Variant.new(4, WIN32OLE::VARIANT::VT_UI2)
assert_equal(4, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_UI2, obj.vartype)
- obj = WIN32OLE_VARIANT.new(5, WIN32OLE::VARIANT::VT_UI4)
+ obj = WIN32OLE::Variant.new(5, WIN32OLE::VARIANT::VT_UI4)
assert_equal(5, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_UI4, obj.vartype)
if defined?(WIN32OLE::VARIANT::VT_I8)
- obj = WIN32OLE_VARIANT.new(-123456789012345, WIN32OLE::VARIANT::VT_I8)
+ obj = WIN32OLE::Variant.new(-123456789012345, WIN32OLE::VARIANT::VT_I8)
assert_equal(-123456789012345, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_I8, obj.vartype)
end
if defined?(WIN32OLE::VARIANT::VT_UI8)
- obj = WIN32OLE_VARIANT.new(123456789012345, WIN32OLE::VARIANT::VT_UI8)
+ obj = WIN32OLE::Variant.new(123456789012345, WIN32OLE::VARIANT::VT_UI8)
assert_equal(123456789012345, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_UI8, obj.vartype)
end
- obj = WIN32OLE_VARIANT.new(4, WIN32OLE::VARIANT::VT_INT)
+ obj = WIN32OLE::Variant.new(4, WIN32OLE::VARIANT::VT_INT)
assert_equal(4, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_INT, obj.vartype)
- obj = WIN32OLE_VARIANT.new(5, WIN32OLE::VARIANT::VT_UINT)
+ obj = WIN32OLE::Variant.new(5, WIN32OLE::VARIANT::VT_UINT)
assert_equal(5, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_UINT, obj.vartype)
end
def test_s_new_with_non_nil_byref
- obj = WIN32OLE_VARIANT.new(2, WIN32OLE::VARIANT::VT_I2|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new(2, WIN32OLE::VARIANT::VT_I2|WIN32OLE::VARIANT::VT_BYREF)
assert_equal(2, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_I2|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
- obj = WIN32OLE_VARIANT.new(3, WIN32OLE::VARIANT::VT_I4|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new(3, WIN32OLE::VARIANT::VT_I4|WIN32OLE::VARIANT::VT_BYREF)
assert_equal(3, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_I4|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
- obj = WIN32OLE_VARIANT.new(4.5, WIN32OLE::VARIANT::VT_R4|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new(4.5, WIN32OLE::VARIANT::VT_R4|WIN32OLE::VARIANT::VT_BYREF)
assert_equal(4.5, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_R4|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
- obj = WIN32OLE_VARIANT.new(5.5, WIN32OLE::VARIANT::VT_R8|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new(5.5, WIN32OLE::VARIANT::VT_R8|WIN32OLE::VARIANT::VT_BYREF)
assert_equal(5.5, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_R8|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
- obj = WIN32OLE_VARIANT.new(600, WIN32OLE::VARIANT::VT_CY|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new(600, WIN32OLE::VARIANT::VT_CY|WIN32OLE::VARIANT::VT_BYREF)
assert_equal("600", obj.value)
assert_equal(WIN32OLE::VARIANT::VT_CY|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
- obj = WIN32OLE_VARIANT.new("2001-06-15 12:17:34", WIN32OLE::VARIANT::VT_DATE|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new("2001-06-15 12:17:34", WIN32OLE::VARIANT::VT_DATE|WIN32OLE::VARIANT::VT_BYREF)
assert_equal(Time.new(2001,06,15,12,17,34), obj.value)
assert_equal(WIN32OLE::VARIANT::VT_DATE|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
- obj = WIN32OLE_VARIANT.new("foo", WIN32OLE::VARIANT::VT_BSTR|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new("foo", WIN32OLE::VARIANT::VT_BSTR|WIN32OLE::VARIANT::VT_BYREF)
assert_equal("foo", obj.value)
assert_equal(WIN32OLE::VARIANT::VT_BSTR|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
- obj = WIN32OLE_VARIANT.new(true, WIN32OLE::VARIANT::VT_BOOL|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new(true, WIN32OLE::VARIANT::VT_BOOL|WIN32OLE::VARIANT::VT_BYREF)
assert_equal(true, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_BOOL|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
- obj = WIN32OLE_VARIANT.new(2, WIN32OLE::VARIANT::VT_I1|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new(2, WIN32OLE::VARIANT::VT_I1|WIN32OLE::VARIANT::VT_BYREF)
assert_equal(2, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_I1|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
- obj = WIN32OLE_VARIANT.new(3, WIN32OLE::VARIANT::VT_UI1|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new(3, WIN32OLE::VARIANT::VT_UI1|WIN32OLE::VARIANT::VT_BYREF)
assert_equal(3, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_UI1|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
- obj = WIN32OLE_VARIANT.new(4, WIN32OLE::VARIANT::VT_UI2|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new(4, WIN32OLE::VARIANT::VT_UI2|WIN32OLE::VARIANT::VT_BYREF)
assert_equal(4, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_UI2|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
- obj = WIN32OLE_VARIANT.new(5, WIN32OLE::VARIANT::VT_UI4|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new(5, WIN32OLE::VARIANT::VT_UI4|WIN32OLE::VARIANT::VT_BYREF)
assert_equal(5, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_UI4|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
- obj = WIN32OLE_VARIANT.new(4, WIN32OLE::VARIANT::VT_INT|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new(4, WIN32OLE::VARIANT::VT_INT|WIN32OLE::VARIANT::VT_BYREF)
assert_equal(4, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_INT|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
- obj = WIN32OLE_VARIANT.new(5, WIN32OLE::VARIANT::VT_UINT|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new(5, WIN32OLE::VARIANT::VT_UINT|WIN32OLE::VARIANT::VT_BYREF)
assert_equal(5, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_UINT|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
end
def test_s_new_with_i8_byref
- obj = WIN32OLE_VARIANT.new(-123456789012345, WIN32OLE::VARIANT::VT_I8|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new(-123456789012345, WIN32OLE::VARIANT::VT_I8|WIN32OLE::VARIANT::VT_BYREF)
assert_equal(-123456789012345, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_I8|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
end
def test_s_new_with_ui8_byref
- obj = WIN32OLE_VARIANT.new(123456789012345, WIN32OLE::VARIANT::VT_UI8|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new(123456789012345, WIN32OLE::VARIANT::VT_UI8|WIN32OLE::VARIANT::VT_BYREF)
assert_equal(123456789012345, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_UI8|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
end
def test_value
- obj = WIN32OLE_VARIANT.new('foo')
+ obj = WIN32OLE::Variant.new('foo')
assert_equal('foo', obj.value)
end
def test_s_new_2_argument
- obj = WIN32OLE_VARIANT.new('foo', WIN32OLE::VARIANT::VT_BSTR|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new('foo', WIN32OLE::VARIANT::VT_BSTR|WIN32OLE::VARIANT::VT_BYREF)
assert_equal('foo', obj.value);
end
def test_s_new_2_argument2
- obj = WIN32OLE_VARIANT.new('foo', WIN32OLE::VARIANT::VT_BSTR)
+ obj = WIN32OLE::Variant.new('foo', WIN32OLE::VARIANT::VT_BSTR)
assert_equal('foo', obj.value);
end
def test_s_new_dispatch_array
vt = WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_DISPATCH
- obj = WIN32OLE_VARIANT.new(nil, vt)
+ obj = WIN32OLE::Variant.new(nil, vt)
assert_equal(vt, obj.vartype)
assert_nil(obj.value)
vt = WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_DISPATCH|WIN32OLE::VARIANT::VT_BYREF
- obj = WIN32OLE_VARIANT.new(nil, vt)
+ obj = WIN32OLE::Variant.new(nil, vt)
assert_equal(vt, obj.vartype)
assert_nil(obj.value)
end
@@ -302,29 +306,29 @@ if defined?(WIN32OLE_VARIANT)
def test_s_new_array
# should not occur stack over flow
ar = (1..500000).to_a.map{|i| [i]}
- ar2 = WIN32OLE_VARIANT.new(ar)
+ ar2 = WIN32OLE::Variant.new(ar)
assert_equal(ar, ar2.value)
end
def test_s_new_vt_record_exc
- # VT_RECORD (= 36) should not be allowed in WIN32OLE_VARIANT#new
+ # VT_RECORD (= 36) should not be allowed in WIN32OLE::Variant#new
assert_raise(ArgumentError) {
- WIN32OLE_VARIANT.new(nil, 36)
+ WIN32OLE::Variant.new(nil, 36)
}
end
def test_s_array
- obj = WIN32OLE_VARIANT.array([2,3], WIN32OLE::VARIANT::VT_I4)
- assert_instance_of(WIN32OLE_VARIANT, obj)
+ obj = WIN32OLE::Variant.array([2,3], WIN32OLE::VARIANT::VT_I4)
+ assert_instance_of(WIN32OLE::Variant, obj)
assert_equal(WIN32OLE::VARIANT::VT_I4|WIN32OLE::VARIANT::VT_ARRAY, obj.vartype)
assert_equal([[0, 0, 0],[0, 0, 0]], obj.value)
- obj = WIN32OLE_VARIANT.array([2,3], WIN32OLE::VARIANT::VT_I4|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.array([2,3], WIN32OLE::VARIANT::VT_I4|WIN32OLE::VARIANT::VT_BYREF)
assert_equal(WIN32OLE::VARIANT::VT_I4|WIN32OLE::VARIANT::VT_BYREF|WIN32OLE::VARIANT::VT_ARRAY, obj.vartype)
assert_equal([[0, 0, 0],[0, 0, 0]], obj.value)
- obj = WIN32OLE_VARIANT.array([2,3], WIN32OLE::VARIANT::VT_I4|WIN32OLE::VARIANT::VT_ARRAY)
- assert_instance_of(WIN32OLE_VARIANT, obj)
+ obj = WIN32OLE::Variant.array([2,3], WIN32OLE::VARIANT::VT_I4|WIN32OLE::VARIANT::VT_ARRAY)
+ assert_instance_of(WIN32OLE::Variant, obj)
assert_equal(WIN32OLE::VARIANT::VT_I4|WIN32OLE::VARIANT::VT_ARRAY, obj.vartype)
assert_equal([[0, 0, 0],[0, 0, 0]], obj.value)
@@ -334,60 +338,60 @@ if defined?(WIN32OLE_VARIANT)
obj[0,1] = "13.2"
assert_equal([[10, 13, 0],[0, 0, 0]], obj.value)
- obj = WIN32OLE_VARIANT.array([3, 2], WIN32OLE::VARIANT::VT_VARIANT)
+ obj = WIN32OLE::Variant.array([3, 2], WIN32OLE::VARIANT::VT_VARIANT)
obj[0,0] = 10
obj[0,1] = "string"
obj[1,0] = 12.735
assert_equal([[10, "string"],[12.735, nil],[nil,nil]], obj.value)
- obj = WIN32OLE_VARIANT.array([2,3], WIN32OLE::VARIANT::VT_DISPATCH)
+ obj = WIN32OLE::Variant.array([2,3], WIN32OLE::VARIANT::VT_DISPATCH)
assert_equal([[nil, nil, nil],[nil,nil,nil]], obj.value)
end
def test_s_array_exc
assert_raise(TypeError) {
- WIN32OLE_VARIANT.array(2, WIN32OLE::VARIANT::VT_I4)
+ WIN32OLE::Variant.array(2, WIN32OLE::VARIANT::VT_I4)
}
end
def test_conversion_num2str
- obj = WIN32OLE_VARIANT.new(124, WIN32OLE::VARIANT::VT_BSTR)
+ obj = WIN32OLE::Variant.new(124, WIN32OLE::VARIANT::VT_BSTR)
assert_equal("124", obj.value);
end
def test_conversion_float2int
- obj = WIN32OLE_VARIANT.new(12.345, WIN32OLE::VARIANT::VT_I4)
+ obj = WIN32OLE::Variant.new(12.345, WIN32OLE::VARIANT::VT_I4)
assert_equal(12, obj.value)
- obj = WIN32OLE_VARIANT.new(12.345, WIN32OLE::VARIANT::VT_I4|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new(12.345, WIN32OLE::VARIANT::VT_I4|WIN32OLE::VARIANT::VT_BYREF)
assert_equal(12, obj.value)
end
def test_conversion_str2num
- obj = WIN32OLE_VARIANT.new("12.345", WIN32OLE::VARIANT::VT_R8)
+ obj = WIN32OLE::Variant.new("12.345", WIN32OLE::VARIANT::VT_R8)
assert_equal(12.345, obj.value)
end
def test_conversion_ole_variant2ole_variant
- obj = WIN32OLE_VARIANT.new("12.345", WIN32OLE::VARIANT::VT_R4)
- obj = WIN32OLE_VARIANT.new(obj, WIN32OLE::VARIANT::VT_I4)
+ obj = WIN32OLE::Variant.new("12.345", WIN32OLE::VARIANT::VT_R4)
+ obj = WIN32OLE::Variant.new(obj, WIN32OLE::VARIANT::VT_I4)
assert_equal(12, obj.value)
end
def test_conversion_str2date
- obj = WIN32OLE_VARIANT.new("2004-12-24 12:24:45", WIN32OLE::VARIANT::VT_DATE)
+ obj = WIN32OLE::Variant.new("2004-12-24 12:24:45", WIN32OLE::VARIANT::VT_DATE)
assert_equal(Time.new(2004,12,24,12,24,45), obj.value)
end
def test_conversion_time2date
dt = Time.mktime(2004, 12, 24, 12, 24, 45)
- obj = WIN32OLE_VARIANT.new(dt, WIN32OLE::VARIANT::VT_DATE)
+ obj = WIN32OLE::Variant.new(dt, WIN32OLE::VARIANT::VT_DATE)
assert_equal(dt, obj.value)
end
def test_conversion_dbl2date_with_msec
# Date is "2014/8/27 12:34:56.789"
- obj = WIN32OLE_VARIANT.new(41878.524268391200167, WIN32OLE::VARIANT::VT_DATE)
+ obj = WIN32OLE::Variant.new(41878.524268391200167, WIN32OLE::VARIANT::VT_DATE)
t = obj.value
assert_equal("2014-08-27 12:34:56", t.strftime('%Y-%m-%d %H:%M:%S'))
assert_in_delta(0.789, t.nsec / 1000000000.0, 0.001)
@@ -396,7 +400,7 @@ if defined?(WIN32OLE_VARIANT)
def test_conversion_time2date_with_msec
t0 = Time.new(2014, 8, 27, 12, 34, 56)
t0 += 0.789
- t1 = WIN32OLE_VARIANT.new(t0).value
+ t1 = WIN32OLE::Variant.new(t0).value
# The t0.nsec is 789000000 and t1.nsec is 789000465
# because of error range by conversion Time between VT_DATE Variant.
@@ -406,7 +410,7 @@ if defined?(WIN32OLE_VARIANT)
t0 = Time.new(2014, 8, 27, 12, 34, 56)
t0 += 0.999999999
- t1 = WIN32OLE_VARIANT.new(t0).value
+ t1 = WIN32OLE::Variant.new(t0).value
msg = "Expected:#{t0.strftime('%Y-%m-%dT%H:%M:%S.%N')} but was:#{t1.strftime('%Y-%m-%dT%H:%M:%S.%N')}"
# The t0 is "2014/08/27 12:34.56.999999999" and
@@ -414,7 +418,7 @@ if defined?(WIN32OLE_VARIANT)
assert_in_delta(t0, t1, 0.001, msg)
t0 = Time.now
- t1 = WIN32OLE_VARIANT.new(t0).value
+ t1 = WIN32OLE::Variant.new(t0).value
msg = "Expected:#{t0.strftime('%Y-%m-%dT%H:%M:%S.%N')} but was:#{t1.strftime('%Y-%m-%dT%H:%M:%S.%N')}"
assert_in_delta(t0, t1, 0.001, msg)
end
@@ -426,110 +430,110 @@ if defined?(WIN32OLE_VARIANT)
# def test_conversion_time_nsec2date
# dt = Time.new(2004, 12,24, 12, 24, 45)
# dt += 0.1
- # obj = WIN32OLE_VARIANT.new(dt, WIN32OLE::VARIANT::VT_DATE)
+ # obj = WIN32OLE::Variant.new(dt, WIN32OLE::VARIANT::VT_DATE)
# assert_equal(dt, obj.value)
# end
def test_conversion_str2cy
begin
WIN32OLE.locale = 0x0411 # set locale Japanese
- rescue WIN32OLERuntimeError
+ rescue WIN32OLE::RuntimeError
omit("Japanese locale is not installed")
end
if WIN32OLE.locale == 0x0411
- obj = WIN32OLE_VARIANT.new("\\10,000", WIN32OLE::VARIANT::VT_CY)
+ obj = WIN32OLE::Variant.new("\\10,000", WIN32OLE::VARIANT::VT_CY)
assert_equal("10000", obj.value)
end
end
def test_create_vt_array
- obj = WIN32OLE_VARIANT.new([1.2, 2.3], WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_R8)
+ obj = WIN32OLE::Variant.new([1.2, 2.3], WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_R8)
assert_equal([1.2, 2.3], obj.value)
assert_equal(WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_R8, obj.vartype)
- obj = WIN32OLE_VARIANT.new([1.2, 2.3], WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_R8|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new([1.2, 2.3], WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_R8|WIN32OLE::VARIANT::VT_BYREF)
assert_equal([1.2, 2.3], obj.value)
assert_equal(WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_R8|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
end
def test_create_vt_array2
- obj = WIN32OLE_VARIANT.new([1.2, "a"], WIN32OLE::VARIANT::VT_ARRAY)
+ obj = WIN32OLE::Variant.new([1.2, "a"], WIN32OLE::VARIANT::VT_ARRAY)
assert_equal([1.2, "a"], obj.value)
assert_equal(WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_VARIANT, obj.vartype)
- obj = WIN32OLE_VARIANT.new([1.2, "a"])
+ obj = WIN32OLE::Variant.new([1.2, "a"])
assert_equal([1.2, "a"], obj.value)
assert_equal(WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_VARIANT, obj.vartype)
end
def test_create_vt_nested_array
- obj = WIN32OLE_VARIANT.new([[1.2, "a", "b"], [3.4, "C", "D"]], WIN32OLE::VARIANT::VT_ARRAY)
+ obj = WIN32OLE::Variant.new([[1.2, "a", "b"], [3.4, "C", "D"]], WIN32OLE::VARIANT::VT_ARRAY)
assert_equal([[1.2, "a", "b"], [3.4, "C", "D"]], obj.value)
- obj = WIN32OLE_VARIANT.new([[1.2, "a", "b"], [3.4, "C", "D"]])
+ obj = WIN32OLE::Variant.new([[1.2, "a", "b"], [3.4, "C", "D"]])
assert_equal([[1.2, "a", "b"], [3.4, "C", "D"]], obj.value)
- obj = WIN32OLE_VARIANT.new([[1.2, "a", "b"], [3.4, "C", "D"], [5.6, "E", "F"]])
+ obj = WIN32OLE::Variant.new([[1.2, "a", "b"], [3.4, "C", "D"], [5.6, "E", "F"]])
assert_equal([[1.2, "a", "b"], [3.4, "C", "D"], [5.6, "E", "F"]], obj.value)
- obj = WIN32OLE_VARIANT.new([[[1.2], [3.4]], [[5.6], [7.8]], [[9.1],[9.2]]])
+ obj = WIN32OLE::Variant.new([[[1.2], [3.4]], [[5.6], [7.8]], [[9.1],[9.2]]])
assert_equal([[[1.2], [3.4]], [[5.6], [7.8]], [[9.1],[9.2]]], obj.value)
end
def test_create_vt_array3
- obj = WIN32OLE_VARIANT.new([])
+ obj = WIN32OLE::Variant.new([])
assert_equal([], obj.value)
- obj = WIN32OLE_VARIANT.new([[]])
+ obj = WIN32OLE::Variant.new([[]])
assert_equal([[]], obj.value)
- obj = WIN32OLE_VARIANT.new([[],[]])
+ obj = WIN32OLE::Variant.new([[],[]])
assert_equal([[],[]], obj.value)
- obj = WIN32OLE_VARIANT.new([], WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new([], WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_BYREF)
assert_equal([], obj.value)
- obj = WIN32OLE_VARIANT.new([[]], WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new([[]], WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_BYREF)
assert_equal([[]], obj.value)
- obj = WIN32OLE_VARIANT.new([[],[]], WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new([[],[]], WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_BYREF)
assert_equal([[],[]], obj.value)
end
def test_create_vt_array_nil
vartype = WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_DISPATCH|WIN32OLE::VARIANT::VT_BYREF
- obj = WIN32OLE_VARIANT.new(nil, vartype)
+ obj = WIN32OLE::Variant.new(nil, vartype)
assert_nil(obj.value)
assert_equal(vartype, obj.vartype)
vartype = WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_DISPATCH
- obj = WIN32OLE_VARIANT.new(nil, vartype)
+ obj = WIN32OLE::Variant.new(nil, vartype)
assert_nil(obj.value)
assert_equal(vartype, obj.vartype)
end
def test_create_vt_array_str
vartype = WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_BSTR
- obj = WIN32OLE_VARIANT.new(["abc", "123"], vartype)
+ obj = WIN32OLE::Variant.new(["abc", "123"], vartype)
assert_equal(vartype, obj.vartype)
assert_equal(["abc", "123"], obj.value)
vartype = WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_BYREF|WIN32OLE::VARIANT::VT_BSTR
- obj = WIN32OLE_VARIANT.new(["abc", "123"], vartype)
+ obj = WIN32OLE::Variant.new(["abc", "123"], vartype)
assert_equal(vartype, obj.vartype)
assert_equal(["abc", "123"], obj.value)
end
def test_create_vt_array_exc
exc = assert_raise(TypeError) {
- WIN32OLE_VARIANT.new("", WIN32OLE::VARIANT::VT_ARRAY)
+ WIN32OLE::Variant.new("", WIN32OLE::VARIANT::VT_ARRAY)
}
assert_match(/wrong argument type String \(expected Array\)/, exc.message)
end
def test_create_vt_array_str2ui1array
- obj = WIN32OLE_VARIANT.new("ABC", WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_UI1)
+ obj = WIN32OLE::Variant.new("ABC", WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_UI1)
assert_equal("ABC", obj.value)
obj.value = "DEF"
@@ -537,10 +541,10 @@ if defined?(WIN32OLE_VARIANT)
obj[0] = 71
assert_equal("GEF", obj.value)
- obj = WIN32OLE_VARIANT.new([65, 0].pack("C*"), WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_UI1)
+ obj = WIN32OLE::Variant.new([65, 0].pack("C*"), WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_UI1)
assert_equal([65, 0].pack("C*"), obj.value)
- obj = WIN32OLE_VARIANT.new("abc", WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_UI1|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new("abc", WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_UI1|WIN32OLE::VARIANT::VT_BYREF)
assert_equal("abc", obj.value)
obj.value = "DEF"
assert_equal("DEF", obj.value)
@@ -551,19 +555,19 @@ if defined?(WIN32OLE_VARIANT)
end
def test_create_vt_array_int
- obj = WIN32OLE_VARIANT.new([65, 0], WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_UI1)
+ obj = WIN32OLE::Variant.new([65, 0], WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_UI1)
assert_equal([65, 0].pack("C*"), obj.value)
- obj = WIN32OLE_VARIANT.new([65, 0])
+ obj = WIN32OLE::Variant.new([65, 0])
assert_equal([65, 0], obj.value)
- obj = WIN32OLE_VARIANT.new([65, 0], WIN32OLE::VARIANT::VT_I2|WIN32OLE::VARIANT::VT_ARRAY)
+ obj = WIN32OLE::Variant.new([65, 0], WIN32OLE::VARIANT::VT_I2|WIN32OLE::VARIANT::VT_ARRAY)
assert_equal([65, 0], obj.value)
end
def test_vt_array_bracket
- obj = WIN32OLE_VARIANT.new([[1,2,3],[4,5,6]])
+ obj = WIN32OLE::Variant.new([[1,2,3],[4,5,6]])
assert_equal(1, obj[0,0])
assert_equal(2, obj[0,1])
assert_equal(3, obj[0,2])
@@ -571,10 +575,10 @@ if defined?(WIN32OLE_VARIANT)
assert_equal(5, obj[1,1])
assert_equal(6, obj[1,2])
- assert_raise(WIN32OLERuntimeError) {
+ assert_raise(WIN32OLE::RuntimeError) {
obj[0,4]
}
- assert_raise(WIN32OLERuntimeError) {
+ assert_raise(WIN32OLE::RuntimeError) {
obj[0,-1]
}
assert_raise(ArgumentError) {
@@ -585,10 +589,10 @@ if defined?(WIN32OLE_VARIANT)
obj[1,2] = 8
assert_equal([[7,2,3], [4,5,8]], obj.value)
- assert_raise(WIN32OLERuntimeError) {
+ assert_raise(WIN32OLE::RuntimeError) {
obj[0,4] = 9
}
- assert_raise(WIN32OLERuntimeError) {
+ assert_raise(WIN32OLE::RuntimeError) {
obj[0,-1] = 10
}
assert_raise(ArgumentError) {
@@ -597,60 +601,60 @@ if defined?(WIN32OLE_VARIANT)
end
def test_conversion_vt_date
- obj = WIN32OLE_VARIANT.new(-657434, WIN32OLE::VARIANT::VT_DATE)
+ obj = WIN32OLE::Variant.new(-657434, WIN32OLE::VARIANT::VT_DATE)
assert_equal(Time.new(100,1,1), obj.value)
- obj = WIN32OLE_VARIANT.new("1500/12/29 23:59:59", WIN32OLE::VARIANT::VT_DATE)
+ obj = WIN32OLE::Variant.new("1500/12/29 23:59:59", WIN32OLE::VARIANT::VT_DATE)
assert_equal(Time.new(1500,12,29,23,59,59), obj.value)
- obj = WIN32OLE_VARIANT.new("1500/12/30 00:00:00", WIN32OLE::VARIANT::VT_DATE)
+ obj = WIN32OLE::Variant.new("1500/12/30 00:00:00", WIN32OLE::VARIANT::VT_DATE)
assert_equal(Time.new(1500,12,30), obj.value)
- obj = WIN32OLE_VARIANT.new("1500/12/30 00:00:01", WIN32OLE::VARIANT::VT_DATE)
+ obj = WIN32OLE::Variant.new("1500/12/30 00:00:01", WIN32OLE::VARIANT::VT_DATE)
assert_equal(Time.new(1500,12,30,0,0,1), obj.value)
- obj = WIN32OLE_VARIANT.new("1899/12/29 23:59:59", WIN32OLE::VARIANT::VT_DATE)
+ obj = WIN32OLE::Variant.new("1899/12/29 23:59:59", WIN32OLE::VARIANT::VT_DATE)
assert_equal(Time.new(1899,12,29,23,59,59), obj.value)
- obj = WIN32OLE_VARIANT.new("1899/12/30 00:00:00", WIN32OLE::VARIANT::VT_DATE)
+ obj = WIN32OLE::Variant.new("1899/12/30 00:00:00", WIN32OLE::VARIANT::VT_DATE)
assert_equal(Time.new(1899,12,30), obj.value)
- obj = WIN32OLE_VARIANT.new("1899/12/30 00:00:01", WIN32OLE::VARIANT::VT_DATE)
+ obj = WIN32OLE::Variant.new("1899/12/30 00:00:01", WIN32OLE::VARIANT::VT_DATE)
assert_equal(Time.new(1899,12,30,0,0,1), obj.value)
- obj = WIN32OLE_VARIANT.new(0, WIN32OLE::VARIANT::VT_DATE)
+ obj = WIN32OLE::Variant.new(0, WIN32OLE::VARIANT::VT_DATE)
assert_equal(Time.new(1899,12,30), obj.value)
- obj = WIN32OLE_VARIANT.new("2008/12/29 23:59:59", WIN32OLE::VARIANT::VT_DATE)
+ obj = WIN32OLE::Variant.new("2008/12/29 23:59:59", WIN32OLE::VARIANT::VT_DATE)
assert_equal(Time.new(2008,12,29,23,59,59), obj.value)
- obj = WIN32OLE_VARIANT.new("2008/12/30 00:00:00", WIN32OLE::VARIANT::VT_DATE)
+ obj = WIN32OLE::Variant.new("2008/12/30 00:00:00", WIN32OLE::VARIANT::VT_DATE)
assert_equal(Time.new(2008,12,30,0,0,0), obj.value)
- obj = WIN32OLE_VARIANT.new("2008/12/30 00:00:01", WIN32OLE::VARIANT::VT_DATE)
+ obj = WIN32OLE::Variant.new("2008/12/30 00:00:01", WIN32OLE::VARIANT::VT_DATE)
assert_equal(Time.new(2008,12,30,0,0,1), obj.value)
- obj = WIN32OLE_VARIANT.new("9999/12/31 23:59:59", WIN32OLE::VARIANT::VT_DATE)
+ obj = WIN32OLE::Variant.new("9999/12/31 23:59:59", WIN32OLE::VARIANT::VT_DATE)
assert_equal(Time.new(9999,12,31,23,59,59), obj.value)
end
def test_create_nil_dispatch
- var = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_DISPATCH)
+ var = WIN32OLE::Variant.new(nil, WIN32OLE::VARIANT::VT_DISPATCH)
assert_nil(var.value)
end
def test_create_variant_byref
- obj = WIN32OLE_VARIANT.new("Str", WIN32OLE::VARIANT::VT_VARIANT|WIN32OLE::VARIANT::VT_BYREF);
+ obj = WIN32OLE::Variant.new("Str", WIN32OLE::VARIANT::VT_VARIANT|WIN32OLE::VARIANT::VT_BYREF);
assert_equal("Str", obj.value);
end
def test_vartype
- obj = WIN32OLE_VARIANT.new("Str")
+ obj = WIN32OLE::Variant.new("Str")
assert_equal(WIN32OLE::VARIANT::VT_BSTR, obj.vartype)
end
def test_set_value
- obj = WIN32OLE_VARIANT.new(10)
+ obj = WIN32OLE::Variant.new(10)
obj.value = 12
assert_equal(12, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_I4, obj.vartype)
@@ -661,57 +665,57 @@ if defined?(WIN32OLE_VARIANT)
assert_equal(11, obj.value)
assert_equal(WIN32OLE::VARIANT::VT_I4, obj.vartype)
- obj = WIN32OLE_VARIANT.new([1,2])
- assert_raise(WIN32OLERuntimeError) {
+ obj = WIN32OLE::Variant.new([1,2])
+ assert_raise(WIN32OLE::RuntimeError) {
obj.value = [3,4]
}
- obj = WIN32OLE_VARIANT.new("2007/01/01", WIN32OLE::VARIANT::VT_DATE)
- assert_raise(WIN32OLERuntimeError) {
+ obj = WIN32OLE::Variant.new("2007/01/01", WIN32OLE::VARIANT::VT_DATE)
+ assert_raise(WIN32OLE::RuntimeError) {
obj.value = "hogehoge"
}
assert_equal(Time.new(2007,1,1), obj.value)
- obj2 = WIN32OLE_VARIANT.new("2006/01/01", WIN32OLE::VARIANT::VT_DATE)
+ obj2 = WIN32OLE::Variant.new("2006/01/01", WIN32OLE::VARIANT::VT_DATE)
obj.value = obj2
assert_equal(Time.new(2006,01,01), obj.value)
end
def test_c_nothing
- assert_nil(WIN32OLE_VARIANT::Nothing.value)
+ assert_nil(WIN32OLE::Variant::Nothing.value)
end
def test_c_empty
- assert_nil(WIN32OLE_VARIANT::Empty.value)
+ assert_nil(WIN32OLE::Variant::Empty.value)
end
def test_c_null
- assert_nil(WIN32OLE_VARIANT::Null.value)
+ assert_nil(WIN32OLE::Variant::Null.value)
end
def test_c_noparam
# DISP_E_PARAMNOTFOUND
- assert_equal(-2147352572, WIN32OLE_VARIANT::NoParam.value)
+ assert_equal(-2147352572, WIN32OLE::Variant::NoParam.value)
end
def test_vt_error_noparam
- v = WIN32OLE_VARIANT.new(-1, WIN32OLE::VARIANT::VT_ERROR)
+ v = WIN32OLE::Variant.new(-1, WIN32OLE::VARIANT::VT_ERROR)
assert_equal(-1, v.value)
fso = WIN32OLE.new("Scripting.FileSystemObject")
- exc = assert_raise(WIN32OLERuntimeError) {
+ exc = assert_raise(WIN32OLE::RuntimeError) {
fso.openTextFile("NonExistingFile", v, false)
}
assert_match(/Type mismatch/i, exc.message)
- exc = assert_raise(WIN32OLERuntimeError) {
- fso.openTextFile("NonExistingFile", WIN32OLE_VARIANT::NoParam, false)
+ exc = assert_raise(WIN32OLE::RuntimeError) {
+ fso.openTextFile("NonExistingFile", WIN32OLE::Variant::NoParam, false)
}
# 800A0035 is 'file not found' error.
assert_match(/800A0035/, exc.message)
# -2147352572 is DISP_E_PARAMNOTFOUND
- v = WIN32OLE_VARIANT.new(-2147352572, WIN32OLE::VARIANT::VT_ERROR)
- exc = assert_raise(WIN32OLERuntimeError) {
- fso.openTextFile("NonExistingFile", WIN32OLE_VARIANT::NoParam, false)
+ v = WIN32OLE::Variant.new(-2147352572, WIN32OLE::VARIANT::VT_ERROR)
+ exc = assert_raise(WIN32OLE::RuntimeError) {
+ fso.openTextFile("NonExistingFile", WIN32OLE::Variant::NoParam, false)
}
# 800A0035 is 'file not found' error code.
assert_match(/800A0035/, exc.message)
diff --git a/test/win32ole/test_win32ole_variant_m.rb b/test/win32ole/test_win32ole_variant_m.rb
index 25ad56cc21..3c2884644c 100644
--- a/test/win32ole/test_win32ole_variant_m.rb
+++ b/test/win32ole/test_win32ole_variant_m.rb
@@ -8,6 +8,11 @@ require "test/unit"
if defined?(WIN32OLE::VARIANT)
class TestWin32OLE_VARIANT_MODULE < Test::Unit::TestCase
include WIN32OLE::VARIANT
+
+ def test_toplevel_constants_backward_compatibility
+ assert_equal(WIN32OLE::VariantType, WIN32OLE::VARIANT)
+ end
+
def test_variant
assert_equal(0, VT_EMPTY)
assert_equal(1, VT_NULL)
diff --git a/test/win32ole/test_win32ole_variant_outarg.rb b/test/win32ole/test_win32ole_variant_outarg.rb
index f50b04aaf5..9301a76aaa 100644
--- a/test/win32ole/test_win32ole_variant_outarg.rb
+++ b/test/win32ole/test_win32ole_variant_outarg.rb
@@ -23,7 +23,7 @@ def ado_csv_installed?
installed
end
-if defined?(WIN32OLE_VARIANT)
+if defined?(WIN32OLE::Variant)
class TestWIN32OLE_VARIANT_OUTARG < Test::Unit::TestCase
module ADO
end
@@ -48,11 +48,11 @@ if defined?(WIN32OLE_VARIANT)
@db.execute(sql, -1)
c = WIN32OLE::ARGV[1]
assert_equal(1, c)
- obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_VARIANT|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new(nil, WIN32OLE::VARIANT::VT_VARIANT|WIN32OLE::VARIANT::VT_BYREF)
assert_equal(nil, obj.value)
@db.execute(sql , obj)
assert_equal(1, obj.value)
- obj = WIN32OLE_VARIANT.new(-100, WIN32OLE::VARIANT::VT_VARIANT|WIN32OLE::VARIANT::VT_BYREF)
+ obj = WIN32OLE::Variant.new(-100, WIN32OLE::VARIANT::VT_VARIANT|WIN32OLE::VARIANT::VT_BYREF)
assert_equal(-100, obj.value)
@db.execute(sql, obj)
assert_equal(1, obj.value)
diff --git a/test/win32ole/test_word.rb b/test/win32ole/test_word.rb
index a23757f620..34cfbbc2a4 100644
--- a/test/win32ole/test_word.rb
+++ b/test/win32ole/test_word.rb
@@ -41,7 +41,7 @@ if defined?(WIN32OLE)
def setup
begin
@obj = WIN32OLE.new('Word.Application')
- rescue WIN32OLERuntimeError
+ rescue WIN32OLE::RuntimeError
@obj = nil
end
end
diff --git a/test/yaml/test_store.rb b/test/yaml/test_store.rb
index 74557db964..d389530271 100644
--- a/test/yaml/test_store.rb
+++ b/test/yaml/test_store.rb
@@ -177,4 +177,4 @@ class YAMLStoreTest < Test::Unit::TestCase
end
assert_equal(indentation_3_yaml, File.read(@yaml_store_file), bug12800)
end
-end
+end if defined?(::YAML::Store)
diff --git a/test/zlib/test_zlib.rb b/test/zlib/test_zlib.rb
index ccb8b3834e..ae4adc21fe 100644
--- a/test/zlib/test_zlib.rb
+++ b/test/zlib/test_zlib.rb
@@ -506,6 +506,7 @@ if defined? Zlib
end
def test_multithread_deflate
+ pend 'hangs' if RUBY_ENGINE == 'truffleruby'
zd = Zlib::Deflate.new
s = "x" * 10000
@@ -522,6 +523,7 @@ if defined? Zlib
end
def test_multithread_inflate
+ pend 'hangs' if RUBY_ENGINE == 'truffleruby'
zi = Zlib::Inflate.new
s = Zlib.deflate("x" * 10000)
@@ -792,14 +794,15 @@ if defined? Zlib
}
end
- if defined? File::TMPFILE
+ if defined?(File::TMPFILE) and RUBY_ENGINE != 'truffleruby'
def test_path_tmpfile
sio = StringIO.new("".dup, 'w')
gz = Zlib::GzipWriter.new(sio)
gz.write "hi"
gz.close
- File.open(Dir.mktmpdir, File::RDWR | File::TMPFILE) do |io|
+ tmpdir = Dir.mktmpdir("zlib_file_tmpfile")
+ File.open(tmpdir, File::RDWR | File::TMPFILE) do |io|
io.write sio.string
io.rewind
@@ -823,6 +826,8 @@ if defined? Zlib
omit 'O_TMPFILE not supported (EISDIR)'
rescue Errno::EOPNOTSUPP
omit 'O_TMPFILE not supported (EOPNOTSUPP)'
+ ensure
+ Dir.rmdir(tmpdir) if tmpdir
end
end
end
@@ -1203,6 +1208,38 @@ if defined? Zlib
}
end
+ # Various methods of Zlib::GzipReader failed when to reading files
+ # just a few bytes larger than GZFILE_READ_SIZE.
+ def test_gzfile_read_size_boundary
+ Tempfile.create("test_zlib_gzip_read_size_boundary") {|t|
+ t.close
+ # NO_COMPRESSION helps with recreating the error condition.
+ # The error happens on compressed files too, but it's harder to reproduce.
+ # For example, ~12750 bytes are needed to trigger the error using __FILE__.
+ # We avoid this because the test file will change over time.
+ Zlib::GzipWriter.open(t.path, Zlib::NO_COMPRESSION) do |gz|
+ gz.print("\n" * 2024) # range from 2024 to 2033 triggers the error
+ gz.flush
+ end
+
+ Zlib::GzipReader.open(t.path) do |f|
+ f.readpartial(1024) until f.eof?
+ assert_raise(EOFError) { f.readpartial(1) }
+ end
+
+ Zlib::GzipReader.open(t.path) do |f|
+ f.readline until f.eof?
+ assert_raise(EOFError) { f.readline }
+ end
+
+ Zlib::GzipReader.open(t.path) do |f|
+ b = f.readbyte until f.eof?
+ f.ungetbyte(b)
+ f.readbyte
+ assert_raise(EOFError) { f.readbyte }
+ end
+ }
+ end
end
class TestZlibGzipWriter < Test::Unit::TestCase
@@ -1457,6 +1494,13 @@ if defined? Zlib
assert_raise(Zlib::GzipFile::Error){ Zlib.gunzip(src) }
end
+ # Zlib.gunzip input is always considered a binary string, regardless of its String#encoding.
+ def test_gunzip_encoding
+ # vvvvvvvv = mtime, but valid UTF-8 string of U+0080
+ src = %w[1f8b0800c28000000003cb48cdc9c9070086a6103605000000].pack("H*").force_encoding('UTF-8')
+ assert_equal 'hello', Zlib.gunzip(src.freeze)
+ end
+
def test_gunzip_no_memory_leak
assert_no_memory_leak(%[-rzlib], "#{<<~"{#"}", "#{<<~'};'}")
d = Zlib.gzip("data")
diff --git a/thread.c b/thread.c
index 5dc0c269f2..b8ba61e188 100644
--- a/thread.c
+++ b/thread.c
@@ -146,10 +146,10 @@ static int rb_threadptr_pending_interrupt_empty_p(const rb_thread_t *th);
static const char *thread_status_name(rb_thread_t *th, int detail);
static int hrtime_update_expire(rb_hrtime_t *, const rb_hrtime_t);
NORETURN(static void async_bug_fd(const char *mesg, int errno_arg, int fd));
-static int consume_communication_pipe(int fd);
-static int check_signals_nogvl(rb_thread_t *, int sigwait_fd);
+MAYBE_UNUSED(static int consume_communication_pipe(int fd));
static volatile int system_working = 1;
+static rb_internal_thread_specific_key_t specific_key_count;
struct waiting_fd {
struct ccan_list_node wfd_node; /* <=> vm.waiting_fds */
@@ -259,12 +259,8 @@ timeout_prepare(rb_hrtime_t **to, rb_hrtime_t *rel, rb_hrtime_t *end,
}
MAYBE_UNUSED(NOINLINE(static int thread_start_func_2(rb_thread_t *th, VALUE *stack_start)));
-
-static void
-ubf_sigwait(void *ignore)
-{
- rb_thread_wakeup_timer_thread(0);
-}
+MAYBE_UNUSED(static bool th_has_dedicated_nt(const rb_thread_t *th));
+MAYBE_UNUSED(static int waitfd_to_waiting_flag(int wfd_event));
#include THREAD_IMPL_SRC
@@ -416,12 +412,12 @@ rb_threadptr_join_list_wakeup(rb_thread_t *thread)
rb_threadptr_interrupt(target_thread);
switch (target_thread->status) {
- case THREAD_STOPPED:
- case THREAD_STOPPED_FOREVER:
- target_thread->status = THREAD_RUNNABLE;
- break;
- default:
- break;
+ case THREAD_STOPPED:
+ case THREAD_STOPPED_FOREVER:
+ target_thread->status = THREAD_RUNNABLE;
+ break;
+ default:
+ break;
}
}
}
@@ -515,25 +511,22 @@ thread_cleanup_func(void *th_ptr, int atfork)
* Unfortunately, we can't release native threading resource at fork
* because libc may have unstable locking state therefore touching
* a threading resource may cause a deadlock.
- *
- * FIXME: Skipping native_mutex_destroy(pthread_mutex_destroy) is safe
- * with NPTL, but native_thread_destroy calls pthread_cond_destroy
- * which calls free(3), so there is a small memory leak atfork, here.
*/
- if (atfork)
+ if (atfork) {
+ th->nt = NULL;
return;
+ }
rb_native_mutex_destroy(&th->interrupt_lock);
- native_thread_destroy(th);
}
static VALUE rb_threadptr_raise(rb_thread_t *, int, VALUE *);
static VALUE rb_thread_to_s(VALUE thread);
void
-ruby_thread_init_stack(rb_thread_t *th)
+ruby_thread_init_stack(rb_thread_t *th, void *local_in_parent_frame)
{
- native_thread_init_stack(th);
+ native_thread_init_stack(th, local_in_parent_frame);
}
const VALUE *
@@ -589,7 +582,7 @@ thread_do_start_proc(rb_thread_t *th)
if (args_len < 8) {
/* free proc.args if the length is enough small */
args_ptr = ALLOCA_N(VALUE, args_len);
- MEMCPY((VALUE *)args_ptr, RARRAY_CONST_PTR_TRANSIENT(args), VALUE, args_len);
+ MEMCPY((VALUE *)args_ptr, RARRAY_CONST_PTR(args), VALUE, args_len);
th->invoke_arg.proc.args = Qnil;
}
else {
@@ -607,14 +600,12 @@ thread_do_start_proc(rb_thread_t *th)
}
}
-static void
+static VALUE
thread_do_start(rb_thread_t *th)
{
native_set_thread_name(th);
VALUE result = Qundef;
- EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_BEGIN, th->self, 0, 0, 0, Qundef);
-
switch (th->invoke_type) {
case thread_invoke_type_proc:
result = thread_do_start_proc(th);
@@ -633,11 +624,7 @@ thread_do_start(rb_thread_t *th)
rb_bug("unreachable");
}
- rb_fiber_scheduler_set(Qnil);
-
- th->value = result;
-
- EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_END, th->self, 0, 0, 0, Qundef);
+ return result;
}
void rb_ec_clear_current_thread_trace_func(const rb_execution_context_t *ec);
@@ -645,21 +632,12 @@ void rb_ec_clear_current_thread_trace_func(const rb_execution_context_t *ec);
static int
thread_start_func_2(rb_thread_t *th, VALUE *stack_start)
{
- STACK_GROW_DIR_DETECTION;
+ RUBY_DEBUG_LOG("th:%u", rb_th_serial(th));
+ VM_ASSERT(th != th->vm->ractor.main_thread);
+
enum ruby_tag_type state;
VALUE errinfo = Qnil;
- size_t size = th->vm->default_params.thread_vm_stack_size / sizeof(VALUE);
rb_thread_t *ractor_main_th = th->ractor->threads.main;
- VALUE * vm_stack = NULL;
-
- VM_ASSERT(th != th->vm->ractor.main_thread);
- RUBY_DEBUG_LOG("th:%u", rb_th_serial(th));
-
- // setup native thread
- thread_sched_to_running(TH_SCHED(th), th);
- ruby_thread_set_native(th);
-
- RUBY_DEBUG_LOG("got lock. th:%u", rb_th_serial(th));
// setup ractor
if (rb_ractor_status_p(th->ractor, ractor_blocking)) {
@@ -674,26 +652,34 @@ thread_start_func_2(rb_thread_t *th, VALUE *stack_start)
RB_VM_UNLOCK();
}
- // This assertion is not passed on win32 env. Check it later.
- // VM_ASSERT((size * sizeof(VALUE)) <= th->ec->machine.stack_maxsize);
-
- // setup VM and machine stack
- vm_stack = alloca(size * sizeof(VALUE));
- VM_ASSERT(vm_stack);
-
- rb_ec_initialize_vm_stack(th->ec, vm_stack, size);
- th->ec->machine.stack_start = STACK_DIR_UPPER(vm_stack + size, vm_stack);
- th->ec->machine.stack_maxsize -= size * sizeof(VALUE);
-
// Ensure that we are not joinable.
VM_ASSERT(UNDEF_P(th->value));
+ int fiber_scheduler_closed = 0, event_thread_end_hooked = 0;
+ VALUE result = Qundef;
+
EC_PUSH_TAG(th->ec);
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
- SAVE_ROOT_JMPBUF(th, thread_do_start(th));
+ EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_BEGIN, th->self, 0, 0, 0, Qundef);
+
+ result = thread_do_start(th);
}
- else {
+
+ if (!fiber_scheduler_closed) {
+ fiber_scheduler_closed = 1;
+ rb_fiber_scheduler_set(Qnil);
+ }
+
+ if (!event_thread_end_hooked) {
+ event_thread_end_hooked = 1;
+ EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_END, th->self, 0, 0, 0, Qundef);
+ }
+
+ if (state == TAG_NONE) {
+ // This must be set AFTER doing all user-level code. At this point, the thread is effectively finished and calls to `Thread#join` will succeed.
+ th->value = result;
+ } else {
errinfo = th->ec->errinfo;
VALUE exc = rb_vm_make_jump_tag_but_local_jump(state, Qundef);
@@ -806,6 +792,8 @@ struct thread_create_params {
VALUE (*fn)(void *);
};
+static void thread_specific_storage_alloc(rb_thread_t *th);
+
static VALUE
thread_create_core(VALUE thval, struct thread_create_params *params)
{
@@ -813,6 +801,8 @@ thread_create_core(VALUE thval, struct thread_create_params *params)
rb_thread_t *th = rb_thread_ptr(thval), *current_th = rb_ec_thread_ptr(ec);
int err;
+ thread_specific_storage_alloc(th);
+
if (OBJ_FROZEN(current_th->thgroup)) {
rb_raise(rb_eThreadError,
"can't start a new thread (frozen ThreadGroup)");
@@ -910,7 +900,7 @@ thread_s_new(int argc, VALUE *argv, VALUE klass)
rb_obj_call_init_kw(thread, argc, argv, RB_PASS_CALLED_KEYWORDS);
th = rb_thread_ptr(thread);
if (!threadptr_initialized(th)) {
- rb_raise(rb_eThreadError, "uninitialized thread - check `%"PRIsVALUE"#initialize'",
+ rb_raise(rb_eThreadError, "uninitialized thread - check '%"PRIsVALUE"#initialize'",
klass);
}
return thread;
@@ -990,11 +980,11 @@ rb_thread_create(VALUE (*fn)(void *), void *arg)
}
VALUE
-rb_thread_create_ractor(rb_ractor_t *g, VALUE args, VALUE proc)
+rb_thread_create_ractor(rb_ractor_t *r, VALUE args, VALUE proc)
{
struct thread_create_params params = {
.type = thread_invoke_type_ractor_proc,
- .g = g,
+ .g = r,
.args = args,
.proc = proc,
};
@@ -1375,14 +1365,14 @@ sleep_forever(rb_thread_t *th, unsigned int fl)
void
rb_thread_sleep_forever(void)
{
- RUBY_DEBUG_LOG("");
+ RUBY_DEBUG_LOG("forever");
sleep_forever(GET_THREAD(), SLEEP_SPURIOUS_CHECK);
}
void
rb_thread_sleep_deadly(void)
{
- RUBY_DEBUG_LOG("");
+ RUBY_DEBUG_LOG("deadly");
sleep_forever(GET_THREAD(), SLEEP_DEADLOCKABLE|SLEEP_SPURIOUS_CHECK);
}
@@ -1394,7 +1384,7 @@ rb_thread_sleep_deadly_allow_spurious_wakeup(VALUE blocker, VALUE timeout, rb_hr
rb_fiber_scheduler_block(scheduler, blocker, timeout);
}
else {
- RUBY_DEBUG_LOG("");
+ RUBY_DEBUG_LOG("...");
if (end) {
sleep_hrtime_until(GET_THREAD(), end, SLEEP_SPURIOUS_CHECK);
}
@@ -1491,7 +1481,7 @@ blocking_region_begin(rb_thread_t *th, struct rb_blocking_region_buffer *region,
th->status = THREAD_STOPPED;
rb_ractor_blocking_threads_inc(th->ractor, __FILE__, __LINE__);
- RUBY_DEBUG_LOG("");
+ RUBY_DEBUG_LOG("thread_id:%p", (void *)th->nt->thread_id);
RB_VM_SAVE_MACHINE_CONTEXT(th);
thread_sched_to_waiting(TH_SCHED(th), th);
@@ -1519,8 +1509,12 @@ blocking_region_end(rb_thread_t *th, struct rb_blocking_region_buffer *region)
th->status = region->prev_status;
}
- RUBY_DEBUG_LOG("");
+ RUBY_DEBUG_LOG("end");
+
+#ifndef _WIN32
+ // GET_THREAD() clears WSAGetLastError()
VM_ASSERT(th == GET_THREAD());
+#endif
}
void *
@@ -1544,14 +1538,11 @@ rb_nogvl(void *(*func)(void *), void *data1,
if (flags & RB_NOGVL_UBF_ASYNC_SAFE) {
vm->ubf_async_safe = 1;
}
- else {
- ubf_th = rb_thread_start_unblock_thread();
- }
}
BLOCKING_REGION(th, {
val = func(data1);
- saved_errno = errno;
+ saved_errno = rb_errno();
}, ubf, data2, flags & RB_NOGVL_INTR_FAIL);
if (is_main_thread) vm->ubf_async_safe = 0;
@@ -1564,7 +1555,7 @@ rb_nogvl(void *(*func)(void *), void *data1,
thread_value(rb_thread_kill(ubf_th));
}
- errno = saved_errno;
+ rb_errno_set(saved_errno);
return val;
}
@@ -1668,8 +1659,28 @@ rb_thread_call_without_gvl(void *(*func)(void *data), void *data1,
return rb_nogvl(func, data1, ubf, data2, 0);
}
+static int
+waitfd_to_waiting_flag(int wfd_event)
+{
+ return wfd_event << 1;
+}
+
+static void
+thread_io_setup_wfd(rb_thread_t *th, int fd, struct waiting_fd *wfd)
+{
+ wfd->fd = fd;
+ wfd->th = th;
+ wfd->busy = NULL;
+
+ RB_VM_LOCK_ENTER();
+ {
+ ccan_list_add(&th->vm->waiting_fds, &wfd->wfd_node);
+ }
+ RB_VM_LOCK_LEAVE();
+}
+
static void
-rb_thread_io_wake_pending_closer(struct waiting_fd *wfd)
+thread_io_wake_pending_closer(struct waiting_fd *wfd)
{
bool has_waiter = wfd->busy && RB_TEST(wfd->busy->wakeup_mutex);
if (has_waiter) {
@@ -1689,19 +1700,84 @@ rb_thread_io_wake_pending_closer(struct waiting_fd *wfd)
}
}
+static bool
+thread_io_mn_schedulable(rb_thread_t *th, int events, const struct timeval *timeout)
+{
+#if defined(USE_MN_THREADS) && USE_MN_THREADS
+ return !th_has_dedicated_nt(th) && (events || timeout) && th->blocking;
+#else
+ return false;
+#endif
+}
+
+// true if need retry
+static bool
+thread_io_wait_events(rb_thread_t *th, int fd, int events, const struct timeval *timeout)
+{
+#if defined(USE_MN_THREADS) && USE_MN_THREADS
+ if (thread_io_mn_schedulable(th, events, timeout)) {
+ rb_hrtime_t rel, *prel;
+
+ if (timeout) {
+ rel = rb_timeval2hrtime(timeout);
+ prel = &rel;
+ }
+ else {
+ prel = NULL;
+ }
+
+ VM_ASSERT(prel || (events & (RB_WAITFD_IN | RB_WAITFD_OUT)));
+
+ if (thread_sched_wait_events(TH_SCHED(th), th, fd, waitfd_to_waiting_flag(events), prel)) {
+ // timeout
+ return false;
+ }
+ else {
+ return true;
+ }
+ }
+#endif // defined(USE_MN_THREADS) && USE_MN_THREADS
+ return false;
+}
+
+// assume read/write
+static bool
+blocking_call_retryable_p(int r, int eno)
+{
+ if (r != -1) return false;
+
+ switch (eno) {
+ case EAGAIN:
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool
+rb_thread_mn_schedulable(VALUE thval)
+{
+ rb_thread_t *th = rb_thread_ptr(thval);
+ return th->mn_schedulable;
+}
+
VALUE
-rb_thread_io_blocking_region(rb_blocking_function_t *func, void *data1, int fd)
+rb_thread_io_blocking_call(rb_blocking_function_t *func, void *data1, int fd, int events)
{
- volatile VALUE val = Qundef; /* shouldn't be used */
rb_execution_context_t * volatile ec = GET_EC();
+ rb_thread_t *th = rb_ec_thread_ptr(ec);
+
+ RUBY_DEBUG_LOG("th:%u fd:%d ev:%d", rb_th_serial(th), fd, events);
+
+ struct waiting_fd waiting_fd;
+ volatile VALUE val = Qundef; /* shouldn't be used */
volatile int saved_errno = 0;
enum ruby_tag_type state;
-
- struct waiting_fd waiting_fd = {
- .fd = fd,
- .th = rb_ec_thread_ptr(ec),
- .busy = NULL,
- };
+ bool prev_mn_schedulable = th->mn_schedulable;
+ th->mn_schedulable = thread_io_mn_schedulable(th, events, NULL);
// `errno` is only valid when there is an actual error - but we can't
// extract that from the return value of `func` alone, so we clear any
@@ -1709,26 +1785,32 @@ rb_thread_io_blocking_region(rb_blocking_function_t *func, void *data1, int fd)
// `func` or not (as opposed to some previously set value).
errno = 0;
- RB_VM_LOCK_ENTER();
+ thread_io_setup_wfd(th, fd, &waiting_fd);
{
- ccan_list_add(&rb_ec_vm_ptr(ec)->waiting_fds, &waiting_fd.wfd_node);
- }
- RB_VM_LOCK_LEAVE();
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ retry:
+ BLOCKING_REGION(waiting_fd.th, {
+ val = func(data1);
+ saved_errno = errno;
+ }, ubf_select, waiting_fd.th, FALSE);
+
+ if (events &&
+ blocking_call_retryable_p((int)val, saved_errno) &&
+ thread_io_wait_events(th, fd, events, NULL)) {
+ RUBY_VM_CHECK_INTS_BLOCKING(ec);
+ goto retry;
+ }
+ }
+ EC_POP_TAG();
- EC_PUSH_TAG(ec);
- if ((state = EC_EXEC_TAG()) == TAG_NONE) {
- BLOCKING_REGION(waiting_fd.th, {
- val = func(data1);
- saved_errno = errno;
- }, ubf_select, waiting_fd.th, FALSE);
+ th->mn_schedulable = prev_mn_schedulable;
}
- EC_POP_TAG();
-
/*
* must be deleted before jump
* this will delete either from waiting_fds or on-stack struct rb_io_close_wait_list
*/
- rb_thread_io_wake_pending_closer(&waiting_fd);
+ thread_io_wake_pending_closer(&waiting_fd);
if (state) {
EC_JUMP_TAG(ec, state);
@@ -1746,6 +1828,12 @@ rb_thread_io_blocking_region(rb_blocking_function_t *func, void *data1, int fd)
return val;
}
+VALUE
+rb_thread_io_blocking_region(rb_blocking_function_t *func, void *data1, int fd)
+{
+ return rb_thread_io_blocking_call(func, data1, fd, 0);
+}
+
/*
* rb_thread_call_with_gvl - re-enter the Ruby world after GVL release.
*
@@ -1892,6 +1980,23 @@ enum handle_interrupt_timing {
};
static enum handle_interrupt_timing
+rb_threadptr_pending_interrupt_from_symbol(rb_thread_t *th, VALUE sym)
+{
+ if (sym == sym_immediate) {
+ return INTERRUPT_IMMEDIATE;
+ }
+ else if (sym == sym_on_blocking) {
+ return INTERRUPT_ON_BLOCKING;
+ }
+ else if (sym == sym_never) {
+ return INTERRUPT_NEVER;
+ }
+ else {
+ rb_raise(rb_eThreadError, "unknown mask signature");
+ }
+}
+
+static enum handle_interrupt_timing
rb_threadptr_pending_interrupt_check_mask(rb_thread_t *th, VALUE err)
{
VALUE mask;
@@ -1903,6 +2008,16 @@ rb_threadptr_pending_interrupt_check_mask(rb_thread_t *th, VALUE err)
for (i=0; i<mask_stack_len; i++) {
mask = mask_stack[mask_stack_len-(i+1)];
+ if (SYMBOL_P(mask)) {
+ /* do not match RUBY_FATAL_THREAD_KILLED etc */
+ if (err != rb_cInteger) {
+ return rb_threadptr_pending_interrupt_from_symbol(th, mask);
+ }
+ else {
+ continue;
+ }
+ }
+
for (mod = err; mod; mod = RCLASS_SUPER(mod)) {
VALUE klass = mod;
VALUE sym;
@@ -1915,18 +2030,7 @@ rb_threadptr_pending_interrupt_check_mask(rb_thread_t *th, VALUE err)
}
if ((sym = rb_hash_aref(mask, klass)) != Qnil) {
- if (sym == sym_immediate) {
- return INTERRUPT_IMMEDIATE;
- }
- else if (sym == sym_on_blocking) {
- return INTERRUPT_ON_BLOCKING;
- }
- else if (sym == sym_never) {
- return INTERRUPT_NEVER;
- }
- else {
- rb_raise(rb_eThreadError, "unknown mask signature");
- }
+ return rb_threadptr_pending_interrupt_from_symbol(th, sym);
}
}
/* try to next mask */
@@ -2018,10 +2122,24 @@ handle_interrupt_arg_check_i(VALUE key, VALUE val, VALUE args)
rb_raise(rb_eArgError, "unknown mask signature");
}
- if (!*maskp) {
- *maskp = rb_ident_hash_new();
+ if (key == rb_eException && (UNDEF_P(*maskp) || NIL_P(*maskp))) {
+ *maskp = val;
+ return ST_CONTINUE;
+ }
+
+ if (RTEST(*maskp)) {
+ if (!RB_TYPE_P(*maskp, T_HASH)) {
+ VALUE prev = *maskp;
+ *maskp = rb_ident_hash_new();
+ if (SYMBOL_P(prev)) {
+ rb_hash_aset(*maskp, rb_eException, prev);
+ }
+ }
+ rb_hash_aset(*maskp, key, val);
+ }
+ else {
+ *maskp = Qfalse;
}
- rb_hash_aset(*maskp, key, val);
return ST_CONTINUE;
}
@@ -2137,7 +2255,7 @@ handle_interrupt_arg_check_i(VALUE key, VALUE val, VALUE args)
static VALUE
rb_thread_s_handle_interrupt(VALUE self, VALUE mask_arg)
{
- VALUE mask;
+ VALUE mask = Qundef;
rb_execution_context_t * volatile ec = GET_EC();
rb_thread_t * volatile th = rb_ec_thread_ptr(ec);
volatile VALUE r = Qnil;
@@ -2147,13 +2265,25 @@ rb_thread_s_handle_interrupt(VALUE self, VALUE mask_arg)
rb_raise(rb_eArgError, "block is needed.");
}
- mask = 0;
mask_arg = rb_to_hash_type(mask_arg);
+
+ if (OBJ_FROZEN(mask_arg) && rb_hash_compare_by_id_p(mask_arg)) {
+ mask = Qnil;
+ }
+
rb_hash_foreach(mask_arg, handle_interrupt_arg_check_i, (VALUE)&mask);
- if (!mask) {
+
+ if (UNDEF_P(mask)) {
return rb_yield(Qnil);
}
- OBJ_FREEZE_RAW(mask);
+
+ if (!RTEST(mask)) {
+ mask = mask_arg;
+ }
+ else if (RB_TYPE_P(mask, T_HASH)) {
+ OBJ_FREEZE(mask);
+ }
+
rb_ary_push(th->pending_interrupt_mask_stack, mask);
if (!rb_threadptr_pending_interrupt_empty_p(th)) {
th->pending_interrupt_queue_checked = 0;
@@ -2337,15 +2467,12 @@ rb_threadptr_execute_interrupts(rb_thread_t *th, int blocking_timing)
/* signal handling */
if (trap_interrupt && (th == th->vm->ractor.main_thread)) {
enum rb_thread_status prev_status = th->status;
- int sigwait_fd = rb_sigwait_fd_get(th);
- if (sigwait_fd >= 0) {
- (void)consume_communication_pipe(sigwait_fd);
- rb_sigwait_fd_put(th, sigwait_fd);
- }
th->status = THREAD_RUNNABLE;
- while ((sig = rb_get_next_signal()) != 0) {
- ret |= rb_signal_exec(th, sig);
+ {
+ while ((sig = rb_get_next_signal()) != 0) {
+ ret |= rb_signal_exec(th, sig);
+ }
}
th->status = prev_status;
}
@@ -2390,7 +2517,7 @@ rb_threadptr_execute_interrupts(rb_thread_t *th, int blocking_timing)
limits_us >>= -th->priority;
if (th->status == THREAD_RUNNABLE)
- th->running_time_us += TIME_QUANTUM_USEC;
+ th->running_time_us += 10 * 1000; // 10ms = 10_000us // TODO: use macro
VM_ASSERT(th->ec->cfp);
EXEC_EVENT_HOOK(th->ec, RUBY_INTERNAL_EVENT_SWITCH, th->ec->cfp->self,
@@ -3320,7 +3447,7 @@ rb_thread_setname(VALUE thread, VALUE name)
name = rb_str_new_frozen(name);
}
target_th->name = name;
- if (threadptr_initialized(target_th)) {
+ if (threadptr_initialized(target_th) && target_th->has_dedicated_nt) {
native_set_another_thread_name(target_th->nt->thread_id, name);
}
return name;
@@ -3911,7 +4038,7 @@ rb_fd_init_copy(rb_fdset_t *dst, rb_fdset_t *src)
void
rb_fd_term(rb_fdset_t *fds)
{
- if (fds->fdset) xfree(fds->fdset);
+ xfree(fds->fdset);
fds->maxfd = 0;
fds->fdset = 0;
}
@@ -4106,7 +4233,6 @@ wait_retryable(int *result, int errnum, rb_hrtime_t *rel, rb_hrtime_t end)
struct select_set {
int max;
- int sigwait_fd;
rb_thread_t *th;
rb_fdset_t *rset;
rb_fdset_t *wset;
@@ -4122,10 +4248,6 @@ select_set_free(VALUE p)
{
struct select_set *set = (struct select_set *)p;
- if (set->sigwait_fd >= 0) {
- rb_sigwait_fd_put(set->th, set->sigwait_fd);
- }
-
rb_fd_term(&set->orig_rset);
rb_fd_term(&set->orig_wset);
rb_fd_term(&set->orig_eset);
@@ -4133,24 +4255,6 @@ select_set_free(VALUE p)
return Qfalse;
}
-static const rb_hrtime_t *
-sigwait_timeout(rb_thread_t *th, int sigwait_fd, const rb_hrtime_t *orig,
- int *drained_p)
-{
- static const rb_hrtime_t quantum = TIME_QUANTUM_USEC * 1000;
-
- if (sigwait_fd >= 0 && (!ubf_threads_empty() || BUSY_WAIT_SIGNALS)) {
- *drained_p = check_signals_nogvl(th, sigwait_fd);
- if (!orig || *orig > quantum)
- return &quantum;
- }
-
- return orig;
-}
-
-#define sigwait_signals_fd(result, cond, sigwait_fd) \
- (result > 0 && (cond) ? (result--, (sigwait_fd)) : -1)
-
static VALUE
do_select(VALUE p)
{
@@ -4169,28 +4273,18 @@ do_select(VALUE p)
TRUE)
do {
- int drained;
lerrno = 0;
BLOCKING_REGION(set->th, {
- const rb_hrtime_t *sto;
struct timeval tv;
- sto = sigwait_timeout(set->th, set->sigwait_fd, to, &drained);
if (!RUBY_VM_INTERRUPTED(set->th->ec)) {
- result = native_fd_select(set->max, set->rset, set->wset,
- set->eset,
- rb_hrtime2timeval(&tv, sto), set->th);
+ result = native_fd_select(set->max,
+ set->rset, set->wset, set->eset,
+ rb_hrtime2timeval(&tv, to), set->th);
if (result < 0) lerrno = errno;
}
- }, set->sigwait_fd >= 0 ? ubf_sigwait : ubf_select, set->th, TRUE);
-
- if (set->sigwait_fd >= 0) {
- int fd = sigwait_signals_fd(result,
- rb_fd_isset(set->sigwait_fd, set->rset),
- set->sigwait_fd);
- (void)check_signals_nogvl(set->th, fd);
- }
+ }, ubf_select, set->th, TRUE);
RUBY_VM_CHECK_INTS_BLOCKING(set->th->ec); /* may raise */
} while (wait_retryable(&result, lerrno, to, end) && do_select_update());
@@ -4202,18 +4296,6 @@ do_select(VALUE p)
return (VALUE)result;
}
-static rb_fdset_t *
-init_set_fd(int fd, rb_fdset_t *fds)
-{
- if (fd < 0) {
- return 0;
- }
- rb_fd_init(fds);
- rb_fd_set(fd, fds);
-
- return fds;
-}
-
int
rb_thread_fd_select(int max, rb_fdset_t * read, rb_fdset_t * write, rb_fdset_t * except,
struct timeval *timeout)
@@ -4237,16 +4319,6 @@ rb_thread_fd_select(int max, rb_fdset_t * read, rb_fdset_t * write, rb_fdset_t *
return 0;
}
- set.sigwait_fd = rb_sigwait_fd_get(set.th);
- if (set.sigwait_fd >= 0) {
- if (set.rset)
- rb_fd_set(set.sigwait_fd, set.rset);
- else
- set.rset = init_set_fd(set.sigwait_fd, &set.orig_rset);
- if (set.sigwait_fd >= set.max) {
- set.max = set.sigwait_fd + 1;
- }
- }
#define fd_init_copy(f) do { \
if (set.f) { \
rb_fd_resize(set.max - 1, set.f); \
@@ -4283,70 +4355,54 @@ rb_thread_fd_select(int max, rb_fdset_t * read, rb_fdset_t * write, rb_fdset_t *
int
rb_thread_wait_for_single_fd(int fd, int events, struct timeval *timeout)
{
- struct pollfd fds[2];
+ struct pollfd fds[1] = {{
+ .fd = fd,
+ .events = (short)events,
+ .revents = 0,
+ }};
int result = 0;
- int drained;
nfds_t nfds;
- rb_unblock_function_t *ubf;
struct waiting_fd wfd;
- int state;
+ enum ruby_tag_type state;
volatile int lerrno;
- wfd.th = GET_THREAD();
- wfd.fd = fd;
- wfd.busy = NULL;
+ rb_execution_context_t *ec = GET_EC();
+ rb_thread_t *th = rb_ec_thread_ptr(ec);
- RB_VM_LOCK_ENTER();
- {
- ccan_list_add(&wfd.th->vm->waiting_fds, &wfd.wfd_node);
- }
- RB_VM_LOCK_LEAVE();
+ thread_io_setup_wfd(th, fd, &wfd);
- EC_PUSH_TAG(wfd.th->ec);
- if ((state = EC_EXEC_TAG()) == TAG_NONE) {
- rb_hrtime_t *to, rel, end = 0;
- RUBY_VM_CHECK_INTS_BLOCKING(wfd.th->ec);
- timeout_prepare(&to, &rel, &end, timeout);
- fds[0].fd = fd;
- fds[0].events = (short)events;
- fds[0].revents = 0;
- do {
- fds[1].fd = rb_sigwait_fd_get(wfd.th);
-
- if (fds[1].fd >= 0) {
- fds[1].events = POLLIN;
- fds[1].revents = 0;
- nfds = 2;
- ubf = ubf_sigwait;
- }
- else {
+ if (timeout == NULL && thread_io_wait_events(th, fd, events, NULL)) {
+ // fd is readable
+ state = 0;
+ fds[0].revents = events;
+ errno = 0;
+ }
+ else {
+ EC_PUSH_TAG(wfd.th->ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ rb_hrtime_t *to, rel, end = 0;
+ RUBY_VM_CHECK_INTS_BLOCKING(wfd.th->ec);
+ timeout_prepare(&to, &rel, &end, timeout);
+ do {
nfds = 1;
- ubf = ubf_select;
- }
- lerrno = 0;
- BLOCKING_REGION(wfd.th, {
- const rb_hrtime_t *sto;
- struct timespec ts;
+ lerrno = 0;
+ BLOCKING_REGION(wfd.th, {
+ struct timespec ts;
- sto = sigwait_timeout(wfd.th, fds[1].fd, to, &drained);
- if (!RUBY_VM_INTERRUPTED(wfd.th->ec)) {
- result = ppoll(fds, nfds, rb_hrtime2timespec(&ts, sto), 0);
- if (result < 0) lerrno = errno;
- }
- }, ubf, wfd.th, TRUE);
+ if (!RUBY_VM_INTERRUPTED(wfd.th->ec)) {
+ result = ppoll(fds, nfds, rb_hrtime2timespec(&ts, to), 0);
+ if (result < 0) lerrno = errno;
+ }
+ }, ubf_select, wfd.th, TRUE);
- if (fds[1].fd >= 0) {
- int fd1 = sigwait_signals_fd(result, fds[1].revents, fds[1].fd);
- (void)check_signals_nogvl(wfd.th, fd1);
- rb_sigwait_fd_put(wfd.th, fds[1].fd);
- }
- RUBY_VM_CHECK_INTS_BLOCKING(wfd.th->ec);
- } while (wait_retryable(&result, lerrno, to, end));
+ RUBY_VM_CHECK_INTS_BLOCKING(wfd.th->ec);
+ } while (wait_retryable(&result, lerrno, to, end));
+ }
+ EC_POP_TAG();
}
- EC_POP_TAG();
- rb_thread_io_wake_pending_closer(&wfd);
+ thread_io_wake_pending_closer(&wfd);
if (state) {
EC_JUMP_TAG(wfd.th->ec, state);
@@ -4420,7 +4476,7 @@ select_single_cleanup(VALUE ptr)
{
struct select_args *args = (struct select_args *)ptr;
- rb_thread_io_wake_pending_closer(&args->wfd);
+ thread_io_wake_pending_closer(&args->wfd);
if (args->read) rb_fd_term(args->read);
if (args->write) rb_fd_term(args->write);
if (args->except) rb_fd_term(args->except);
@@ -4428,6 +4484,18 @@ select_single_cleanup(VALUE ptr)
return (VALUE)-1;
}
+static rb_fdset_t *
+init_set_fd(int fd, rb_fdset_t *fds)
+{
+ if (fd < 0) {
+ return 0;
+ }
+ rb_fd_init(fds);
+ rb_fd_set(fd, fds);
+
+ return fds;
+}
+
int
rb_thread_wait_for_single_fd(int fd, int events, struct timeval *timeout)
{
@@ -4435,21 +4503,15 @@ rb_thread_wait_for_single_fd(int fd, int events, struct timeval *timeout)
struct select_args args;
int r;
VALUE ptr = (VALUE)&args;
+ rb_execution_context_t *ec = GET_EC();
+ rb_thread_t *th = rb_ec_thread_ptr(ec);
args.as.fd = fd;
args.read = (events & RB_WAITFD_IN) ? init_set_fd(fd, &rfds) : NULL;
args.write = (events & RB_WAITFD_OUT) ? init_set_fd(fd, &wfds) : NULL;
args.except = (events & RB_WAITFD_PRI) ? init_set_fd(fd, &efds) : NULL;
args.tv = timeout;
- args.wfd.fd = fd;
- args.wfd.th = GET_THREAD();
- args.wfd.busy = NULL;
-
- RB_VM_LOCK_ENTER();
- {
- ccan_list_add(&args.wfd.th->vm->waiting_fds, &args.wfd.wfd_node);
- }
- RB_VM_LOCK_LEAVE();
+ thread_io_setup_wfd(th, fd, &args.wfd);
r = (int)rb_ensure(select_single, ptr, select_single_cleanup, ptr);
if (r == -1)
@@ -4510,16 +4572,13 @@ consume_communication_pipe(int fd)
ssize_t result;
int ret = FALSE; /* for rb_sigwait_sleep */
- /*
- * disarm UBF_TIMER before we read, because it can become
- * re-armed at any time via sighandler and the pipe will refill
- * We can disarm it because this thread is now processing signals
- * and we do not want unnecessary SIGVTALRM
- */
- ubf_timer_disarm();
-
while (1) {
result = read(fd, buff, sizeof(buff));
+#if USE_EVENTFD
+ RUBY_DEBUG_LOG("resultf:%d buff:%lu", (int)result, (unsigned long)buff[0]);
+#else
+ RUBY_DEBUG_LOG("result:%d", (int)result);
+#endif
if (result > 0) {
ret = TRUE;
if (USE_EVENTFD || result < (ssize_t)sizeof(buff)) {
@@ -4546,24 +4605,6 @@ consume_communication_pipe(int fd)
}
}
-static int
-check_signals_nogvl(rb_thread_t *th, int sigwait_fd)
-{
- rb_vm_t *vm = GET_VM(); /* th may be 0 */
- int ret = sigwait_fd >= 0 ? consume_communication_pipe(sigwait_fd) : FALSE;
- ubf_wakeup_all_threads();
- if (rb_signal_buff_size()) {
- if (th == vm->ractor.main_thread) {
- /* no need to lock + wakeup if already in main thread */
- RUBY_VM_SET_TRAP_INTERRUPT(th->ec);
- }
- else {
- threadptr_trap_interrupt(vm->ractor.main_thread);
- }
- }
- return ret;
-}
-
void
rb_thread_stop_timer_thread(void)
{
@@ -4649,6 +4690,7 @@ rb_thread_atfork_internal(rb_thread_t *th, void (*atfork)(rb_thread_t *, const r
rb_vm_living_threads_init(vm);
rb_ractor_atfork(vm, th);
+ rb_vm_postponed_job_atfork();
/* may be held by RJIT threads in parent */
rb_native_mutex_initialize(&vm->workqueue_lock);
@@ -4660,6 +4702,10 @@ rb_thread_atfork_internal(rb_thread_t *th, void (*atfork)(rb_thread_t *, const r
rb_ractor_sleeper_threads_clear(th->ractor);
rb_clear_coverages();
+ // restart timer thread (timer threads access to `vm->waitpid_lock` and so on.
+ rb_thread_reset_timer_thread();
+ rb_thread_start_timer_thread();
+
VM_ASSERT(vm->ractor.blocking_cnt == 0);
VM_ASSERT(vm->ractor.cnt == 1);
}
@@ -4679,6 +4725,7 @@ void
rb_thread_atfork(void)
{
rb_thread_t *th = GET_THREAD();
+ rb_threadptr_pending_interrupt_clear(th);
rb_thread_atfork_internal(th, terminate_atfork_i);
th->join_list = NULL;
rb_fiber_atfork(th);
@@ -4717,16 +4764,14 @@ struct thgroup {
int enclosed;
};
-static size_t
-thgroup_memsize(const void *ptr)
-{
- return sizeof(struct thgroup);
-}
-
static const rb_data_type_t thgroup_data_type = {
"thgroup",
- {0, RUBY_TYPED_DEFAULT_FREE, thgroup_memsize,},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+ {
+ 0,
+ RUBY_TYPED_DEFAULT_FREE,
+ NULL, // No external memory to report
+ },
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
};
/*
@@ -5054,12 +5099,12 @@ recursive_list_access(VALUE sym)
}
/*
- * Returns Qtrue if and only if obj (or the pair <obj, paired_obj>) is already
+ * Returns true if and only if obj (or the pair <obj, paired_obj>) is already
* in the recursion list.
* Assumes the recursion list is valid.
*/
-static VALUE
+static bool
recursive_check(VALUE list, VALUE obj, VALUE paired_obj_id)
{
#if SIZEOF_LONG == SIZEOF_VOIDP
@@ -5071,18 +5116,18 @@ recursive_check(VALUE list, VALUE obj, VALUE paired_obj_id)
VALUE pair_list = rb_hash_lookup2(list, obj, Qundef);
if (UNDEF_P(pair_list))
- return Qfalse;
+ return false;
if (paired_obj_id) {
if (!RB_TYPE_P(pair_list, T_HASH)) {
if (!OBJ_ID_EQL(paired_obj_id, pair_list))
- return Qfalse;
+ return false;
}
else {
if (NIL_P(rb_hash_lookup(pair_list, paired_obj_id)))
- return Qfalse;
+ return false;
}
}
- return Qtrue;
+ return true;
}
/*
@@ -5162,7 +5207,7 @@ exec_recursive_i(RB_BLOCK_CALL_FUNC_ARGLIST(tag, data))
* Calls func(obj, arg, recursive), where recursive is non-zero if the
* current method is called recursively on obj, or on the pair <obj, pairid>
* If outer is 0, then the innermost func will be called with recursive set
- * to Qtrue, otherwise the outermost func will be called. In the latter case,
+ * to true, otherwise the outermost func will be called. In the latter case,
* all inner func are short-circuited by throw.
* Implementation details: the value thrown is the recursive list which is
* proper to the current method and unlikely to be caught anywhere else.
@@ -5253,7 +5298,7 @@ rb_exec_recursive_paired(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE pai
/*
* If recursion is detected on the current method and obj, the outermost
- * func will be called with (obj, arg, Qtrue). All inner func will be
+ * func will be called with (obj, arg, true). All inner func will be
* short-circuited using throw.
*/
@@ -5271,7 +5316,7 @@ rb_exec_recursive_outer_mid(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE
/*
* If recursion is detected on the current method, obj and paired_obj,
- * the outermost func will be called with (obj, arg, Qtrue). All inner
+ * the outermost func will be called with (obj, arg, true). All inner
* func will be short-circuited using throw.
*/
@@ -5425,8 +5470,12 @@ Init_Thread(void)
/* main thread setting */
{
/* acquire global vm lock */
- struct rb_thread_sched *sched = TH_SCHED(th);
- thread_sched_to_running(sched, th);
+#ifdef HAVE_PTHREAD_NP_H
+ VM_ASSERT(TH_SCHED(th)->running == th);
+#endif
+ // thread_sched_to_running() should not be called because
+ // it assumes blocked by thread_sched_to_waiting().
+ // thread_sched_to_running(sched, th);
th->pending_interrupt_queue = rb_ary_hidden_new(0);
th->pending_interrupt_queue_checked = 0;
@@ -5437,6 +5486,9 @@ Init_Thread(void)
rb_thread_create_timer_thread();
Init_thread_sync();
+
+ // TODO: Suppress unused function warning for now
+ // if (0) rb_thread_sched_destroy(NULL);
}
int
@@ -5466,7 +5518,7 @@ debug_deadlock_check(rb_ractor_t *r, VALUE msg)
ccan_list_for_each(&r->threads.set, th, lt_node) {
rb_str_catf(msg, "* %+"PRIsVALUE"\n rb_thread_t:%p "
"native:%p int:%u",
- th->self, (void *)th, thread_id_str(th), th->ec->interrupt_flag);
+ th->self, (void *)th, th->nt ? thread_id_str(th) : "N/A", th->ec->interrupt_flag);
if (th->locking_mutex) {
rb_mutex_t *mutex = mutex_ptr(th->locking_mutex);
@@ -5492,14 +5544,18 @@ rb_check_deadlock(rb_ractor_t *r)
{
if (GET_THREAD()->vm->thread_ignore_deadlock) return;
- int found = 0;
- rb_thread_t *th = NULL;
+#ifdef RUBY_THREAD_PTHREAD_H
+ if (r->threads.sched.readyq_cnt > 0) return;
+#endif
+
int sleeper_num = rb_ractor_sleeper_thread_num(r);
int ltnum = rb_ractor_living_thread_num(r);
if (ltnum > sleeper_num) return;
if (ltnum < sleeper_num) rb_bug("sleeper must not be more than vm_living_thread_num(vm)");
- if (patrol_thread && patrol_thread != GET_THREAD()) return;
+
+ int found = 0;
+ rb_thread_t *th = NULL;
ccan_list_for_each(&r->threads.set, th, lt_node) {
if (th->status != THREAD_STOPPED_FOREVER || RUBY_VM_INTERRUPTED(th->ec)) {
@@ -5790,7 +5846,7 @@ rb_uninterruptible(VALUE (*b_proc)(VALUE), VALUE data)
rb_thread_t *cur_th = GET_THREAD();
rb_hash_aset(interrupt_mask, rb_cObject, sym_never);
- OBJ_FREEZE_RAW(interrupt_mask);
+ OBJ_FREEZE(interrupt_mask);
rb_ary_push(cur_th->pending_interrupt_mask_stack, interrupt_mask);
VALUE ret = rb_ensure(b_proc, data, uninterruptible_exit, Qnil);
@@ -5798,3 +5854,66 @@ rb_uninterruptible(VALUE (*b_proc)(VALUE), VALUE data)
RUBY_VM_CHECK_INTS(cur_th->ec);
return ret;
}
+
+static void
+thread_specific_storage_alloc(rb_thread_t *th)
+{
+ VM_ASSERT(th->specific_storage == NULL);
+
+ if (UNLIKELY(specific_key_count > 0)) {
+ th->specific_storage = ZALLOC_N(void *, RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
+ }
+}
+
+rb_internal_thread_specific_key_t
+rb_internal_thread_specific_key_create(void)
+{
+ rb_vm_t *vm = GET_VM();
+
+ if (specific_key_count == 0 && vm->ractor.cnt > 1) {
+ rb_raise(rb_eThreadError, "The first rb_internal_thread_specific_key_create() is called with multiple ractors");
+ }
+ else if (specific_key_count > RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX) {
+ rb_raise(rb_eThreadError, "rb_internal_thread_specific_key_create() is called more than %d times", RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
+ }
+ else {
+ rb_internal_thread_specific_key_t key = specific_key_count++;
+
+ if (key == 0) {
+ // allocate
+ rb_ractor_t *cr = GET_RACTOR();
+ rb_thread_t *th;
+
+ ccan_list_for_each(&cr->threads.set, th, lt_node) {
+ thread_specific_storage_alloc(th);
+ }
+ }
+ return key;
+ }
+}
+
+// async and native thread safe.
+void *
+rb_internal_thread_specific_get(VALUE thread_val, rb_internal_thread_specific_key_t key)
+{
+ rb_thread_t *th = DATA_PTR(thread_val);
+
+ VM_ASSERT(rb_thread_ptr(thread_val) == th);
+ VM_ASSERT(key < RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
+ VM_ASSERT(th->specific_storage);
+
+ return th->specific_storage[key];
+}
+
+// async and native thread safe.
+void
+rb_internal_thread_specific_set(VALUE thread_val, rb_internal_thread_specific_key_t key, void *data)
+{
+ rb_thread_t *th = DATA_PTR(thread_val);
+
+ VM_ASSERT(rb_thread_ptr(thread_val) == th);
+ VM_ASSERT(key < RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
+ VM_ASSERT(th->specific_storage);
+
+ th->specific_storage[key] = data;
+}
diff --git a/thread_none.c b/thread_none.c
index 1e151cccb2..38730df7ba 100644
--- a/thread_none.c
+++ b/thread_none.c
@@ -42,7 +42,7 @@ thread_sched_yield(struct rb_thread_sched *sched, rb_thread_t *th)
}
void
-rb_thread_sched_init(struct rb_thread_sched *sched)
+rb_thread_sched_init(struct rb_thread_sched *sched, bool atfork)
{
}
@@ -136,18 +136,13 @@ Init_native_thread(rb_thread_t *main_th)
ruby_thread_set_native(main_th);
}
-static void
-native_thread_destroy(rb_thread_t *th)
-{
-}
-
void
-ruby_init_stack(volatile VALUE *addr)
+ruby_mn_threads_params(void)
{
}
static int
-native_thread_init_stack(rb_thread_t *th)
+native_thread_init_stack(rb_thread_t *th, void *local_in_parent_frame)
{
#if defined(__wasm__) && !defined(__EMSCRIPTEN__)
th->ec->machine.stack_start = (VALUE *)rb_wasm_stack_get_base();
@@ -278,9 +273,57 @@ native_fd_select(int n, rb_fdset_t *readfds, rb_fdset_t *writefds, rb_fdset_t *e
return rb_fd_select(n, readfds, writefds, exceptfds, timeout);
}
-static VALUE
-rb_thread_start_unblock_thread(void)
+static bool
+th_has_dedicated_nt(const rb_thread_t *th)
+{
+ return true;
+}
+
+void
+rb_add_running_thread(rb_thread_t *th){
+ // do nothing
+}
+
+void
+rb_del_running_thread(rb_thread_t *th)
{
- return Qfalse;
+ // do nothing
}
+
+void
+rb_threadptr_sched_free(rb_thread_t *th)
+{
+ // do nothing
+}
+
+void
+rb_ractor_sched_barrier_start(rb_vm_t *vm, rb_ractor_t *cr)
+{
+ // do nothing
+}
+
+void
+rb_ractor_sched_barrier_join(rb_vm_t *vm, rb_ractor_t *cr)
+{
+ // do nothing
+}
+
+void
+rb_threadptr_remove(rb_thread_t *th)
+{
+ // do nothing
+}
+
+void
+rb_thread_sched_mark_zombies(rb_vm_t *vm)
+{
+ // do nothing
+}
+
+bool
+rb_thread_lock_native_thread(void)
+{
+ return false;
+}
+
#endif /* THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION */
diff --git a/thread_none.h b/thread_none.h
index 89f64667f0..ac47e52bda 100644
--- a/thread_none.h
+++ b/thread_none.h
@@ -16,5 +16,6 @@ struct rb_thread_sched_item {};
struct rb_thread_sched {};
RUBY_EXTERN struct rb_execution_context_struct *ruby_current_ec;
+NOINLINE(struct rb_execution_context_struct *rb_current_ec_noinline(void)); // for assertions
#endif /* RUBY_THREAD_NONE_H */
diff --git a/thread_pthread.c b/thread_pthread.c
index 1f8bf7a1d5..82b5e362cc 100644
--- a/thread_pthread.c
+++ b/thread_pthread.c
@@ -12,6 +12,7 @@
#ifdef THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION
#include "internal/gc.h"
+#include "internal/sanitizers.h"
#include "rjit.h"
#ifdef HAVE_SYS_RESOURCE_H
@@ -60,6 +61,36 @@ static pthread_condattr_t *condattr_monotonic = &condattr_mono;
static const void *const condattr_monotonic = NULL;
#endif
+#include COROUTINE_H
+
+#ifndef HAVE_SYS_EVENT_H
+#define HAVE_SYS_EVENT_H 0
+#endif
+
+#ifndef HAVE_SYS_EPOLL_H
+#define HAVE_SYS_EPOLL_H 0
+#else
+// force setting for debug
+// #undef HAVE_SYS_EPOLL_H
+// #define HAVE_SYS_EPOLL_H 0
+#endif
+
+#ifndef USE_MN_THREADS
+ #if defined(__EMSCRIPTEN__) || defined(COROUTINE_PTHREAD_CONTEXT)
+ // on __EMSCRIPTEN__ provides epoll* declarations, but no implementations.
+ // on COROUTINE_PTHREAD_CONTEXT, it doesn't worth to use it.
+ #define USE_MN_THREADS 0
+ #elif HAVE_SYS_EPOLL_H
+ #include <sys/epoll.h>
+ #define USE_MN_THREADS 1
+ #elif HAVE_SYS_EVENT_H
+ #include <sys/event.h>
+ #define USE_MN_THREADS 1
+ #else
+ #define USE_MN_THREADS 0
+ #endif
+#endif
+
// native thread wrappers
#define NATIVE_MUTEX_LOCK_DEBUG 0
@@ -242,379 +273,1237 @@ rb_native_cond_timedwait(rb_nativethread_cond_t *cond, pthread_mutex_t *mutex, u
// thread scheduling
static rb_internal_thread_event_hook_t *rb_internal_thread_event_hooks = NULL;
-static void rb_thread_execute_hooks(rb_event_flag_t event);
-#define RB_INTERNAL_THREAD_HOOK(event) if (rb_internal_thread_event_hooks) { rb_thread_execute_hooks(event); }
+static void rb_thread_execute_hooks(rb_event_flag_t event, rb_thread_t *th);
+
+#if 0
+static const char *
+event_name(rb_event_flag_t event)
+{
+ switch (event) {
+ case RUBY_INTERNAL_THREAD_EVENT_STARTED:
+ return "STARTED";
+ case RUBY_INTERNAL_THREAD_EVENT_READY:
+ return "READY";
+ case RUBY_INTERNAL_THREAD_EVENT_RESUMED:
+ return "RESUMED";
+ case RUBY_INTERNAL_THREAD_EVENT_SUSPENDED:
+ return "SUSPENDED";
+ case RUBY_INTERNAL_THREAD_EVENT_EXITED:
+ return "EXITED";
+ }
+ return "no-event";
+}
+
+#define RB_INTERNAL_THREAD_HOOK(event, th) \
+ if (UNLIKELY(rb_internal_thread_event_hooks)) { \
+ fprintf(stderr, "[thread=%"PRIxVALUE"] %s in %s (%s:%d)\n", th->self, event_name(event), __func__, __FILE__, __LINE__); \
+ rb_thread_execute_hooks(event, th); \
+ }
+#else
+#define RB_INTERNAL_THREAD_HOOK(event, th) if (UNLIKELY(rb_internal_thread_event_hooks)) { rb_thread_execute_hooks(event, th); }
+#endif
static rb_serial_t current_fork_gen = 1; /* We can't use GET_VM()->fork_gen */
-#if defined(SIGVTALRM) && !defined(__CYGWIN__) && !defined(__EMSCRIPTEN__)
+#if defined(SIGVTALRM) && !defined(__EMSCRIPTEN__)
# define USE_UBF_LIST 1
#endif
-/*
- * UBF_TIMER and ubf_list both use SIGVTALRM.
- *
- * UBF_TIMER has NOTHING to do with thread timeslices (TIMER_INTERRUPT_MASK)
- *
- * UBF_TIMER is to close TOCTTOU signal race on programs where we
- * cannot rely on GVL contention (vm->gvl.timer) to perform wakeups
- * while a thread is doing blocking I/O on sockets or pipes. With
- * rb_thread_call_without_gvl and similar functions:
- *
- * (1) Check interrupts.
- * (2) release GVL.
- * (2a) signal received
- * (3) call func with data1 (blocks for a long time without ubf_timer)
- * (4) acquire GVL.
- * Other Ruby threads can not run in parallel any more.
- * (5) Check interrupts.
- *
- * We need UBF_TIMER to break out of (3) if (2a) happens.
- *
- * ubf_list wakeups may be triggered on gvl_yield.
- *
- * If we have vm->gvl.timer (on GVL contention), we don't need UBF_TIMER
- * as it can perform the same tasks while doing timeslices.
+static void threadptr_trap_interrupt(rb_thread_t *);
+
+#ifdef HAVE_SCHED_YIELD
+#define native_thread_yield() (void)sched_yield()
+#else
+#define native_thread_yield() ((void)0)
+#endif
+
+/* 100ms. 10ms is too small for user level thread scheduling
+ * on recent Linux (tested on 2.6.35)
*/
-#define UBF_TIMER_NONE 0
-#define UBF_TIMER_POSIX 1
-#define UBF_TIMER_PTHREAD 2
-
-#ifndef UBF_TIMER
-# if defined(HAVE_TIMER_SETTIME) && defined(HAVE_TIMER_CREATE) && \
- defined(CLOCK_MONOTONIC) && defined(USE_UBF_LIST)
- /* preferred */
-# define UBF_TIMER UBF_TIMER_POSIX
-# elif defined(USE_UBF_LIST)
- /* safe, but inefficient */
-# define UBF_TIMER UBF_TIMER_PTHREAD
-# else
- /* we'll be racy without SIGVTALRM for ubf_list */
-# define UBF_TIMER UBF_TIMER_NONE
-# endif
-#endif
-
-enum rtimer_state {
- /* alive, after timer_create: */
- RTIMER_DISARM,
- RTIMER_ARMING,
- RTIMER_ARMED,
-
- RTIMER_DEAD
-};
+#define TIME_QUANTUM_MSEC (100)
+#define TIME_QUANTUM_USEC (TIME_QUANTUM_MSEC * 1000)
+#define TIME_QUANTUM_NSEC (TIME_QUANTUM_USEC * 1000)
-#if UBF_TIMER == UBF_TIMER_POSIX
-static const struct itimerspec zero;
-static struct {
- rb_atomic_t state_; /* rtimer_state */
- rb_serial_t fork_gen;
- timer_t timerid;
-} timer_posix = {
- /* .state = */ RTIMER_DEAD,
-};
+static void native_thread_dedicated_inc(rb_vm_t *vm, rb_ractor_t *cr, struct rb_native_thread *nt);
+static void native_thread_dedicated_dec(rb_vm_t *vm, rb_ractor_t *cr, struct rb_native_thread *nt);
+static void native_thread_assign(struct rb_native_thread *nt, rb_thread_t *th);
-#define TIMER_STATE_DEBUG 0
+static void ractor_sched_enq(rb_vm_t *vm, rb_ractor_t *r);
+static void timer_thread_wakeup(void);
+static void timer_thread_wakeup_locked(rb_vm_t *vm);
+static void timer_thread_wakeup_force(void);
+static void thread_sched_switch(rb_thread_t *cth, rb_thread_t *next_th);
+static void coroutine_transfer0(struct coroutine_context *transfer_from,
+ struct coroutine_context *transfer_to, bool to_dead);
-static const char *
-rtimer_state_name(enum rtimer_state state)
+#define thread_sched_dump(s) thread_sched_dump_(__FILE__, __LINE__, s)
+
+static bool
+th_has_dedicated_nt(const rb_thread_t *th)
{
- switch (state) {
- case RTIMER_DISARM: return "disarm";
- case RTIMER_ARMING: return "arming";
- case RTIMER_ARMED: return "armed";
- case RTIMER_DEAD: return "dead";
- default: rb_bug("unreachable");
- }
+ // TODO: th->has_dedicated_nt
+ return th->nt->dedicated > 0;
}
-static enum rtimer_state
-timer_state_exchange(enum rtimer_state state)
+RBIMPL_ATTR_MAYBE_UNUSED()
+static void
+thread_sched_dump_(const char *file, int line, struct rb_thread_sched *sched)
{
- enum rtimer_state prev = ATOMIC_EXCHANGE(timer_posix.state_, state);
- if (TIMER_STATE_DEBUG) fprintf(stderr, "state (exc): %s->%s\n", rtimer_state_name(prev), rtimer_state_name(state));
- return prev;
+ fprintf(stderr, "@%s:%d running:%d\n", file, line, sched->running ? (int)sched->running->serial : -1);
+ rb_thread_t *th;
+ int i = 0;
+ ccan_list_for_each(&sched->readyq, th, sched.node.readyq) {
+ i++; if (i>10) rb_bug("too many");
+ fprintf(stderr, " ready:%d (%sNT:%d)\n", th->serial,
+ th->nt ? (th->nt->dedicated ? "D" : "S") : "x",
+ th->nt ? (int)th->nt->serial : -1);
+ }
}
-static enum rtimer_state
-timer_state_cas(enum rtimer_state expected_prev, enum rtimer_state state)
+#define ractor_sched_dump(s) ractor_sched_dump_(__FILE__, __LINE__, s)
+
+RBIMPL_ATTR_MAYBE_UNUSED()
+static void
+ractor_sched_dump_(const char *file, int line, rb_vm_t *vm)
{
- enum rtimer_state prev = ATOMIC_CAS(timer_posix.state_, expected_prev, state);
+ rb_ractor_t *r;
- if (TIMER_STATE_DEBUG) {
- if (prev == expected_prev) {
- fprintf(stderr, "state (cas): %s->%s\n", rtimer_state_name(prev), rtimer_state_name(state));
- }
- else {
- fprintf(stderr, "state (cas): %s (expected:%s)\n", rtimer_state_name(prev), rtimer_state_name(expected_prev));
- }
+ fprintf(stderr, "ractor_sched_dump %s:%d\n", file, line);
+
+ int i = 0;
+ ccan_list_for_each(&vm->ractor.sched.grq, r, threads.sched.grq_node) {
+ i++;
+ if (i>10) rb_bug("!!");
+ fprintf(stderr, " %d ready:%d\n", i, rb_ractor_id(r));
}
+}
+
+#define thread_sched_lock(a, b) thread_sched_lock_(a, b, __FILE__, __LINE__)
+#define thread_sched_unlock(a, b) thread_sched_unlock_(a, b, __FILE__, __LINE__)
- return prev;
+static void
+thread_sched_lock_(struct rb_thread_sched *sched, rb_thread_t *th, const char *file, int line)
+{
+ rb_native_mutex_lock(&sched->lock_);
+
+#if VM_CHECK_MODE
+ RUBY_DEBUG_LOG2(file, line, "th:%u prev_owner:%u", rb_th_serial(th), rb_th_serial(sched->lock_owner));
+ VM_ASSERT(sched->lock_owner == NULL);
+ sched->lock_owner = th;
+#else
+ RUBY_DEBUG_LOG2(file, line, "th:%u", rb_th_serial(th));
+#endif
}
-#elif UBF_TIMER == UBF_TIMER_PTHREAD
-static void *timer_pthread_fn(void *);
-static struct {
- int low[2];
- rb_atomic_t armed; /* boolean */
- rb_serial_t fork_gen;
- pthread_t thid;
-} timer_pthread = {
- { -1, -1 },
-};
+static void
+thread_sched_unlock_(struct rb_thread_sched *sched, rb_thread_t *th, const char *file, int line)
+{
+ RUBY_DEBUG_LOG2(file, line, "th:%u", rb_th_serial(th));
+
+#if VM_CHECK_MODE
+ VM_ASSERT(sched->lock_owner == th);
+ sched->lock_owner = NULL;
#endif
-static const rb_hrtime_t *sigwait_timeout(rb_thread_t *, int sigwait_fd,
- const rb_hrtime_t *,
- int *drained_p);
-static void ubf_timer_disarm(void);
-static void threadptr_trap_interrupt(rb_thread_t *);
-static void ubf_wakeup_all_threads(void);
-static int ubf_threads_empty(void);
+ rb_native_mutex_unlock(&sched->lock_);
+}
-#define TIMER_THREAD_CREATED_P() (signal_self_pipe.fork_gen == current_fork_gen)
+static void
+thread_sched_set_lock_owner(struct rb_thread_sched *sched, rb_thread_t *th)
+{
+ RUBY_DEBUG_LOG("th:%u", rb_th_serial(th));
-/* for testing, and in case we come across a platform w/o pipes: */
-#define BUSY_WAIT_SIGNALS (0)
+#if VM_CHECK_MODE > 0
+ sched->lock_owner = th;
+#endif
+}
-/*
- * sigwait_th is the thread which owns sigwait_fd and sleeps on it
- * (using ppoll). RJIT worker can be sigwait_th==0, so we initialize
- * it to THREAD_INVALID at startup and fork time. It is the ONLY thread
- * allowed to read from sigwait_fd, otherwise starvation can occur.
- */
-#define THREAD_INVALID ((const rb_thread_t *)-1)
-static const rb_thread_t *sigwait_th;
+static void
+ASSERT_thread_sched_locked(struct rb_thread_sched *sched, rb_thread_t *th)
+{
+ VM_ASSERT(rb_native_mutex_trylock(&sched->lock_) == EBUSY);
-#ifdef HAVE_SCHED_YIELD
-#define native_thread_yield() (void)sched_yield()
+#if VM_CHECK_MODE
+ if (th) {
+ VM_ASSERT(sched->lock_owner == th);
+ }
+ else {
+ VM_ASSERT(sched->lock_owner != NULL);
+ }
+#endif
+}
+
+#define ractor_sched_lock(a, b) ractor_sched_lock_(a, b, __FILE__, __LINE__)
+#define ractor_sched_unlock(a, b) ractor_sched_unlock_(a, b, __FILE__, __LINE__)
+
+RBIMPL_ATTR_MAYBE_UNUSED()
+static unsigned int
+rb_ractor_serial(const rb_ractor_t *r) {
+ if (r) {
+ return rb_ractor_id(r);
+ }
+ else {
+ return 0;
+ }
+}
+
+static void
+ractor_sched_set_locked(rb_vm_t *vm, rb_ractor_t *cr)
+{
+#if VM_CHECK_MODE > 0
+ VM_ASSERT(vm->ractor.sched.lock_owner == NULL);
+ VM_ASSERT(vm->ractor.sched.locked == false);
+
+ vm->ractor.sched.lock_owner = cr;
+ vm->ractor.sched.locked = true;
+#endif
+}
+
+static void
+ractor_sched_set_unlocked(rb_vm_t *vm, rb_ractor_t *cr)
+{
+#if VM_CHECK_MODE > 0
+ VM_ASSERT(vm->ractor.sched.locked);
+ VM_ASSERT(vm->ractor.sched.lock_owner == cr);
+
+ vm->ractor.sched.locked = false;
+ vm->ractor.sched.lock_owner = NULL;
+#endif
+}
+
+static void
+ractor_sched_lock_(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line)
+{
+ rb_native_mutex_lock(&vm->ractor.sched.lock);
+
+#if VM_CHECK_MODE
+ RUBY_DEBUG_LOG2(file, line, "cr:%u prev_owner:%u", rb_ractor_serial(cr), rb_ractor_serial(vm->ractor.sched.lock_owner));
#else
-#define native_thread_yield() ((void)0)
+ RUBY_DEBUG_LOG2(file, line, "cr:%u", rb_ractor_serial(cr));
#endif
-/* 100ms. 10ms is too small for user level thread scheduling
- * on recent Linux (tested on 2.6.35)
- */
-#define TIME_QUANTUM_MSEC (100)
-#define TIME_QUANTUM_USEC (TIME_QUANTUM_MSEC * 1000)
-#define TIME_QUANTUM_NSEC (TIME_QUANTUM_USEC * 1000)
+ ractor_sched_set_locked(vm, cr);
+}
-/*
- * Designate the next sched.timer thread, favor the last thread in
- * the readyq since it will be in readyq longest
- */
-static int
-designate_timer_thread(struct rb_thread_sched *sched)
+static void
+ractor_sched_unlock_(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line)
+{
+ RUBY_DEBUG_LOG2(file, line, "cr:%u", rb_ractor_serial(cr));
+
+ ractor_sched_set_unlocked(vm, cr);
+ rb_native_mutex_unlock(&vm->ractor.sched.lock);
+}
+
+static void
+ASSERT_ractor_sched_locked(rb_vm_t *vm, rb_ractor_t *cr)
+{
+ VM_ASSERT(rb_native_mutex_trylock(&vm->ractor.sched.lock) == EBUSY);
+ VM_ASSERT(vm->ractor.sched.locked);
+ VM_ASSERT(cr == NULL || vm->ractor.sched.lock_owner == cr);
+}
+
+RBIMPL_ATTR_MAYBE_UNUSED()
+static bool
+ractor_sched_running_threads_contain_p(rb_vm_t *vm, rb_thread_t *th)
+{
+ rb_thread_t *rth;
+ ccan_list_for_each(&vm->ractor.sched.running_threads, rth, sched.node.running_threads) {
+ if (rth == th) return true;
+ }
+ return false;
+}
+
+RBIMPL_ATTR_MAYBE_UNUSED()
+static unsigned int
+ractor_sched_running_threads_size(rb_vm_t *vm)
+{
+ rb_thread_t *th;
+ unsigned int i = 0;
+ ccan_list_for_each(&vm->ractor.sched.running_threads, th, sched.node.running_threads) {
+ i++;
+ }
+ return i;
+}
+
+RBIMPL_ATTR_MAYBE_UNUSED()
+static unsigned int
+ractor_sched_timeslice_threads_size(rb_vm_t *vm)
+{
+ rb_thread_t *th;
+ unsigned int i = 0;
+ ccan_list_for_each(&vm->ractor.sched.timeslice_threads, th, sched.node.timeslice_threads) {
+ i++;
+ }
+ return i;
+}
+
+RBIMPL_ATTR_MAYBE_UNUSED()
+static bool
+ractor_sched_timeslice_threads_contain_p(rb_vm_t *vm, rb_thread_t *th)
{
- rb_thread_t *last;
+ rb_thread_t *rth;
+ ccan_list_for_each(&vm->ractor.sched.timeslice_threads, rth, sched.node.timeslice_threads) {
+ if (rth == th) return true;
+ }
+ return false;
+}
- last = ccan_list_tail(&sched->readyq, rb_thread_t, sched.node.readyq);
+static void ractor_sched_barrier_join_signal_locked(rb_vm_t *vm);
+static void ractor_sched_barrier_join_wait_locked(rb_vm_t *vm, rb_thread_t *th);
- if (last) {
- rb_native_cond_signal(&last->nt->cond.readyq);
- return TRUE;
+// setup timeslice signals by the timer thread.
+static void
+thread_sched_setup_running_threads(struct rb_thread_sched *sched, rb_ractor_t *cr, rb_vm_t *vm,
+ rb_thread_t *add_th, rb_thread_t *del_th, rb_thread_t *add_timeslice_th)
+{
+#if USE_RUBY_DEBUG_LOG
+ unsigned int prev_running_cnt = vm->ractor.sched.running_cnt;
+#endif
+
+ rb_thread_t *del_timeslice_th;
+
+ if (del_th && sched->is_running_timeslice) {
+ del_timeslice_th = del_th;
+ sched->is_running_timeslice = false;
}
else {
- return FALSE;
+ del_timeslice_th = NULL;
}
+
+ RUBY_DEBUG_LOG("+:%u -:%u +ts:%u -ts:%u",
+ rb_th_serial(add_th), rb_th_serial(del_th),
+ rb_th_serial(add_timeslice_th), rb_th_serial(del_timeslice_th));
+
+ ractor_sched_lock(vm, cr);
+ {
+ // update running_threads
+ if (del_th) {
+ VM_ASSERT(ractor_sched_running_threads_contain_p(vm, del_th));
+ VM_ASSERT(del_timeslice_th != NULL ||
+ !ractor_sched_timeslice_threads_contain_p(vm, del_th));
+
+ ccan_list_del_init(&del_th->sched.node.running_threads);
+ vm->ractor.sched.running_cnt--;
+
+ if (UNLIKELY(vm->ractor.sched.barrier_waiting)) {
+ ractor_sched_barrier_join_signal_locked(vm);
+ }
+ sched->is_running = false;
+ }
+
+ if (add_th) {
+ if (UNLIKELY(vm->ractor.sched.barrier_waiting)) {
+ RUBY_DEBUG_LOG("barrier-wait");
+
+ ractor_sched_barrier_join_signal_locked(vm);
+ ractor_sched_barrier_join_wait_locked(vm, add_th);
+ }
+
+ VM_ASSERT(!ractor_sched_running_threads_contain_p(vm, add_th));
+ VM_ASSERT(!ractor_sched_timeslice_threads_contain_p(vm, add_th));
+
+ ccan_list_add(&vm->ractor.sched.running_threads, &add_th->sched.node.running_threads);
+ vm->ractor.sched.running_cnt++;
+ sched->is_running = true;
+ }
+
+ if (add_timeslice_th) {
+ // update timeslice threads
+ int was_empty = ccan_list_empty(&vm->ractor.sched.timeslice_threads);
+ VM_ASSERT(!ractor_sched_timeslice_threads_contain_p(vm, add_timeslice_th));
+ ccan_list_add(&vm->ractor.sched.timeslice_threads, &add_timeslice_th->sched.node.timeslice_threads);
+ sched->is_running_timeslice = true;
+ if (was_empty) {
+ timer_thread_wakeup_locked(vm);
+ }
+ }
+
+ if (del_timeslice_th) {
+ VM_ASSERT(ractor_sched_timeslice_threads_contain_p(vm, del_timeslice_th));
+ ccan_list_del_init(&del_timeslice_th->sched.node.timeslice_threads);
+ }
+
+ VM_ASSERT(ractor_sched_running_threads_size(vm) == vm->ractor.sched.running_cnt);
+ VM_ASSERT(ractor_sched_timeslice_threads_size(vm) <= vm->ractor.sched.running_cnt);
+ }
+ ractor_sched_unlock(vm, cr);
+
+ if (add_th && !del_th && UNLIKELY(vm->ractor.sync.lock_owner != NULL)) {
+ // it can be after barrier synchronization by another ractor
+ rb_thread_t *lock_owner = NULL;
+#if VM_CHECK_MODE
+ lock_owner = sched->lock_owner;
+#endif
+ thread_sched_unlock(sched, lock_owner);
+ {
+ RB_VM_LOCK_ENTER();
+ RB_VM_LOCK_LEAVE();
+ }
+ thread_sched_lock(sched, lock_owner);
+ }
+
+ //RUBY_DEBUG_LOG("+:%u -:%u +ts:%u -ts:%u run:%u->%u",
+ // rb_th_serial(add_th), rb_th_serial(del_th),
+ // rb_th_serial(add_timeslice_th), rb_th_serial(del_timeslice_th),
+ RUBY_DEBUG_LOG("run:%u->%u", prev_running_cnt, vm->ractor.sched.running_cnt);
}
-/*
- * We become designated timer thread to kick vm->gvl.owner
- * periodically. Continue on old timeout if it expired.
- */
static void
-do_gvl_timer(struct rb_thread_sched *sched, rb_thread_t *th)
+thread_sched_add_running_thread(struct rb_thread_sched *sched, rb_thread_t *th)
{
- rb_vm_t *vm = GET_VM();
- static rb_hrtime_t abs;
+ ASSERT_thread_sched_locked(sched, th);
+ VM_ASSERT(sched->running == th);
- sched->timer = th;
+ rb_vm_t *vm = th->vm;
+ thread_sched_setup_running_threads(sched, th->ractor, vm, th, NULL, ccan_list_empty(&sched->readyq) ? NULL : th);
+}
- /* take over wakeups from UBF_TIMER */
- ubf_timer_disarm();
+static void
+thread_sched_del_running_thread(struct rb_thread_sched *sched, rb_thread_t *th)
+{
+ ASSERT_thread_sched_locked(sched, th);
+
+ rb_vm_t *vm = th->vm;
+ thread_sched_setup_running_threads(sched, th->ractor, vm, NULL, th, NULL);
+}
+
+void
+rb_add_running_thread(rb_thread_t *th)
+{
+ struct rb_thread_sched *sched = TH_SCHED(th);
- if (sched->timer_err == ETIMEDOUT) {
- abs = native_cond_timeout(&th->nt->cond.readyq, TIME_QUANTUM_NSEC);
+ thread_sched_lock(sched, th);
+ {
+ thread_sched_add_running_thread(sched, th);
}
- sched->timer_err = native_cond_timedwait(&th->nt->cond.readyq, &sched->lock, &abs);
+ thread_sched_unlock(sched, th);
+}
- ubf_wakeup_all_threads();
+void
+rb_del_running_thread(rb_thread_t *th)
+{
+ struct rb_thread_sched *sched = TH_SCHED(th);
- if (UNLIKELY(rb_signal_buff_size())) {
- if (th == vm->ractor.main_thread) {
- RUBY_VM_SET_TRAP_INTERRUPT(th->ec);
+ thread_sched_lock(sched, th);
+ {
+ thread_sched_del_running_thread(sched, th);
+ }
+ thread_sched_unlock(sched, th);
+}
+
+// setup current or next running thread
+// sched->running should be set only on this function.
+//
+// if th is NULL, there is no running threads.
+static void
+thread_sched_set_running(struct rb_thread_sched *sched, rb_thread_t *th)
+{
+ RUBY_DEBUG_LOG("th:%u->th:%u", rb_th_serial(sched->running), rb_th_serial(th));
+ VM_ASSERT(sched->running != th);
+
+ sched->running = th;
+}
+
+RBIMPL_ATTR_MAYBE_UNUSED()
+static bool
+thread_sched_readyq_contain_p(struct rb_thread_sched *sched, rb_thread_t *th)
+{
+ rb_thread_t *rth;
+ ccan_list_for_each(&sched->readyq, rth, sched.node.readyq) {
+ if (rth == th) return true;
+ }
+ return false;
+}
+
+// deque thread from the ready queue.
+// if the ready queue is empty, return NULL.
+//
+// return deque'ed running thread (or NULL).
+static rb_thread_t *
+thread_sched_deq(struct rb_thread_sched *sched)
+{
+ ASSERT_thread_sched_locked(sched, NULL);
+ rb_thread_t *next_th;
+
+ VM_ASSERT(sched->running != NULL);
+
+ if (ccan_list_empty(&sched->readyq)) {
+ next_th = NULL;
+ }
+ else {
+ next_th = ccan_list_pop(&sched->readyq, rb_thread_t, sched.node.readyq);
+
+ VM_ASSERT(sched->readyq_cnt > 0);
+ sched->readyq_cnt--;
+ ccan_list_node_init(&next_th->sched.node.readyq);
+ }
+
+ RUBY_DEBUG_LOG("next_th:%u readyq_cnt:%d", rb_th_serial(next_th), sched->readyq_cnt);
+
+ return next_th;
+}
+
+// enqueue ready thread to the ready queue.
+static void
+thread_sched_enq(struct rb_thread_sched *sched, rb_thread_t *ready_th)
+{
+ ASSERT_thread_sched_locked(sched, NULL);
+ RUBY_DEBUG_LOG("ready_th:%u readyq_cnt:%d", rb_th_serial(ready_th), sched->readyq_cnt);
+
+ VM_ASSERT(sched->running != NULL);
+ VM_ASSERT(!thread_sched_readyq_contain_p(sched, ready_th));
+
+ if (sched->is_running) {
+ if (ccan_list_empty(&sched->readyq)) {
+ // add sched->running to timeslice
+ thread_sched_setup_running_threads(sched, ready_th->ractor, ready_th->vm, NULL, NULL, sched->running);
+ }
+ }
+ else {
+ VM_ASSERT(!ractor_sched_timeslice_threads_contain_p(ready_th->vm, sched->running));
+ }
+
+ ccan_list_add_tail(&sched->readyq, &ready_th->sched.node.readyq);
+ sched->readyq_cnt++;
+}
+
+// DNT: kick condvar
+// SNT: TODO
+static void
+thread_sched_wakeup_running_thread(struct rb_thread_sched *sched, rb_thread_t *next_th, bool will_switch)
+{
+ ASSERT_thread_sched_locked(sched, NULL);
+ VM_ASSERT(sched->running == next_th);
+
+ if (next_th) {
+ if (next_th->nt) {
+ if (th_has_dedicated_nt(next_th)) {
+ RUBY_DEBUG_LOG("pinning th:%u", next_th->serial);
+ rb_native_cond_signal(&next_th->nt->cond.readyq);
+ }
+ else {
+ // TODO
+ RUBY_DEBUG_LOG("th:%u is already running.", next_th->serial);
+ }
}
else {
- threadptr_trap_interrupt(vm->ractor.main_thread);
+ if (will_switch) {
+ RUBY_DEBUG_LOG("th:%u (do nothing)", rb_th_serial(next_th));
+ }
+ else {
+ RUBY_DEBUG_LOG("th:%u (enq)", rb_th_serial(next_th));
+ ractor_sched_enq(next_th->vm, next_th->ractor);
+ }
}
}
+ else {
+ RUBY_DEBUG_LOG("no waiting threads%s", "");
+ }
+}
- /*
- * Timeslice. Warning: the process may fork while this
- * thread is contending for GVL:
- */
- const rb_thread_t *running;
- if ((running = sched->running) != 0) {
- // strictly speaking, accessing "running" is not thread-safe
- RUBY_VM_SET_TIMER_INTERRUPT(running->ec);
+// waiting -> ready (locked)
+static void
+thread_sched_to_ready_common(struct rb_thread_sched *sched, rb_thread_t *th, bool wakeup, bool will_switch)
+{
+ RUBY_DEBUG_LOG("th:%u running:%u redyq_cnt:%d", rb_th_serial(th), rb_th_serial(sched->running), sched->readyq_cnt);
+
+ VM_ASSERT(sched->running != th);
+ VM_ASSERT(!thread_sched_readyq_contain_p(sched, th));
+ RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_READY, th);
+
+ if (sched->running == NULL) {
+ thread_sched_set_running(sched, th);
+ if (wakeup) thread_sched_wakeup_running_thread(sched, th, will_switch);
+ }
+ else {
+ thread_sched_enq(sched, th);
}
- sched->timer = 0;
}
+// waiting -> ready
+//
+// `th` had became "waiting" state by `thread_sched_to_waiting`
+// and `thread_sched_to_ready` enqueue `th` to the thread ready queue.
+RBIMPL_ATTR_MAYBE_UNUSED()
static void
-thread_sched_to_ready_common(struct rb_thread_sched *sched, rb_thread_t *th)
+thread_sched_to_ready(struct rb_thread_sched *sched, rb_thread_t *th)
{
- ccan_list_add_tail(&sched->readyq, &th->sched.node.readyq);
+ RUBY_DEBUG_LOG("th:%u", rb_th_serial(th));
+
+ thread_sched_lock(sched, th);
+ {
+ thread_sched_to_ready_common(sched, th, true, false);
+ }
+ thread_sched_unlock(sched, th);
}
+// wait until sched->running is `th`.
static void
-thread_sched_to_running_common(struct rb_thread_sched *sched, rb_thread_t *th)
+thread_sched_wait_running_turn(struct rb_thread_sched *sched, rb_thread_t *th, bool can_direct_transfer)
{
- RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_READY);
- if (sched->running) {
- VM_ASSERT(th->unblock.func == 0 &&
- "we must not be in ubf_list and GVL readyq at the same time");
+ RUBY_DEBUG_LOG("th:%u", rb_th_serial(th));
- // waiting -> ready
- thread_sched_to_ready_common(sched, th);
+ ASSERT_thread_sched_locked(sched, th);
+ VM_ASSERT(th == GET_THREAD());
- // wait for running chance
- do {
- if (!sched->timer) {
- do_gvl_timer(sched, th);
+ if (th != sched->running) {
+ // already deleted from running threads
+ // VM_ASSERT(!ractor_sched_running_threads_contain_p(th->vm, th)); // need locking
+
+ // wait for execution right
+ rb_thread_t *next_th;
+ while((next_th = sched->running) != th) {
+ if (th_has_dedicated_nt(th)) {
+ RUBY_DEBUG_LOG("(nt) sleep th:%u running:%u", rb_th_serial(th), rb_th_serial(sched->running));
+
+ thread_sched_set_lock_owner(sched, NULL);
+ {
+ RUBY_DEBUG_LOG("nt:%d cond:%p", th->nt->serial, &th->nt->cond.readyq);
+ rb_native_cond_wait(&th->nt->cond.readyq, &sched->lock_);
+ }
+ thread_sched_set_lock_owner(sched, th);
+
+ RUBY_DEBUG_LOG("(nt) wakeup %s", sched->running == th ? "success" : "failed");
+ if (th == sched->running) {
+ rb_ractor_thread_switch(th->ractor, th);
+ }
}
else {
- rb_native_cond_wait(&th->nt->cond.readyq, &sched->lock);
+ // search another ready thread
+ if (can_direct_transfer &&
+ (next_th = sched->running) != NULL &&
+ !next_th->nt // next_th is running or has dedicated nt
+ ) {
+
+ RUBY_DEBUG_LOG("th:%u->%u (direct)", rb_th_serial(th), rb_th_serial(next_th));
+
+ thread_sched_set_lock_owner(sched, NULL);
+ {
+ rb_ractor_set_current_ec(th->ractor, NULL);
+ thread_sched_switch(th, next_th);
+ }
+ thread_sched_set_lock_owner(sched, th);
+ }
+ else {
+ // search another ready ractor
+ struct rb_native_thread *nt = th->nt;
+ native_thread_assign(NULL, th);
+
+ RUBY_DEBUG_LOG("th:%u->%u (ractor scheduling)", rb_th_serial(th), rb_th_serial(next_th));
+
+ thread_sched_set_lock_owner(sched, NULL);
+ {
+ rb_ractor_set_current_ec(th->ractor, NULL);
+ coroutine_transfer0(th->sched.context, nt->nt_context, false);
+ }
+ thread_sched_set_lock_owner(sched, th);
+ }
+
+ VM_ASSERT(GET_EC() == th->ec);
}
- } while (sched->running);
+ }
- ccan_list_del_init(&th->sched.node.readyq);
+ VM_ASSERT(th->nt != NULL);
+ VM_ASSERT(GET_EC() == th->ec);
+ VM_ASSERT(th->sched.waiting_reason.flags == thread_sched_waiting_none);
- if (sched->need_yield) {
- sched->need_yield = 0;
- rb_native_cond_signal(&sched->switch_cond);
- }
- }
- else { /* reset timer if uncontended */
- sched->timer_err = ETIMEDOUT;
+ // add th to running threads
+ thread_sched_add_running_thread(sched, th);
}
- // ready -> running
- sched->running = th;
+ // VM_ASSERT(ractor_sched_running_threads_contain_p(th->vm, th)); need locking
+ RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_RESUMED, th);
+}
- RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_RESUMED);
+// waiting -> ready -> running (locked)
+static void
+thread_sched_to_running_common(struct rb_thread_sched *sched, rb_thread_t *th)
+{
+ RUBY_DEBUG_LOG("th:%u dedicated:%d", rb_th_serial(th), th_has_dedicated_nt(th));
- if (!sched->timer) {
- /* Make sure that this thread is not currently the sigwait_thread before we
- decide to wake it up. Otherwise, we can end up in a loop of the following
- operations:
- * We are in native_sleep -> sigwait_sleep
- * A signal arives, kicking this thread out of rb_sigwait_sleep
- * We get here because of the call to THREAD_BLOCKING_END() in native_sleep
- * write into the sigwait_fd pipe here
- * re-loop around in native_sleep() because the desired sleep time has not
- actually yet expired
- * that calls rb_sigwait_sleep again
- * the ppoll() in rb_sigwait_sleep immediately returns because of the byte we
- wrote to the sigwait_fd here
- * that wakes the thread up again and we end up here again.
- Such a loop can only be broken by the main thread waking up and handling the
- signal, such that ubf_threads_empty() below becomes true again; however this
- loop can actually keep things so busy (and cause so much contention on the
- main thread's interrupt_lock) that the main thread doesn't deal with the
- signal for many seconds. This seems particuarly likely on FreeBSD 13.
- */
- if (!designate_timer_thread(sched) && !ubf_threads_empty() && th != sigwait_th) {
- rb_thread_wakeup_timer_thread(-1);
- }
+ VM_ASSERT(sched->running != th);
+ VM_ASSERT(th_has_dedicated_nt(th));
+ VM_ASSERT(GET_THREAD() == th);
+
+ native_thread_dedicated_dec(th->vm, th->ractor, th->nt);
+
+ // waiting -> ready
+ thread_sched_to_ready_common(sched, th, false, false);
+
+ if (sched->running == th) {
+ thread_sched_add_running_thread(sched, th);
}
+
+ // TODO: check SNT number
+ thread_sched_wait_running_turn(sched, th, false);
}
+// waiting -> ready -> running
+//
+// `th` had been waiting by `thread_sched_to_waiting()`
+// and run a dedicated task (like waitpid and so on).
+// After the dedicated task, this function is called
+// to join a normal thread-scheduling.
static void
thread_sched_to_running(struct rb_thread_sched *sched, rb_thread_t *th)
{
- rb_native_mutex_lock(&sched->lock);
- thread_sched_to_running_common(sched, th);
- rb_native_mutex_unlock(&sched->lock);
+ thread_sched_lock(sched, th);
+ {
+ thread_sched_to_running_common(sched, th);
+ }
+ thread_sched_unlock(sched, th);
+}
+
+// resume a next thread in the thread ready queue.
+//
+// deque next running thread from the ready thread queue and
+// resume this thread if available.
+//
+// If the next therad has a dedicated native thraed, simply signal to resume.
+// Otherwise, make the ractor ready and other nt will run the ractor and the thread.
+static void
+thread_sched_wakeup_next_thread(struct rb_thread_sched *sched, rb_thread_t *th, bool will_switch)
+{
+ ASSERT_thread_sched_locked(sched, th);
+
+ VM_ASSERT(sched->running == th);
+ VM_ASSERT(sched->running->nt != NULL);
+
+ rb_thread_t *next_th = thread_sched_deq(sched);
+
+ RUBY_DEBUG_LOG("next_th:%u", rb_th_serial(next_th));
+ VM_ASSERT(th != next_th);
+
+ thread_sched_set_running(sched, next_th);
+ VM_ASSERT(next_th == sched->running);
+ thread_sched_wakeup_running_thread(sched, next_th, will_switch);
+
+ if (th != next_th) {
+ thread_sched_del_running_thread(sched, th);
+ }
}
-static rb_thread_t *
-thread_sched_to_waiting_common(struct rb_thread_sched *sched, rb_thread_t *th)
+// running -> waiting
+//
+// to_dead: false
+// th will run dedicated task.
+// run another ready thread.
+// to_dead: true
+// th will be dead.
+// run another ready thread.
+static void
+thread_sched_to_waiting_common0(struct rb_thread_sched *sched, rb_thread_t *th, bool to_dead)
{
- rb_thread_t *next;
- sched->running = NULL;
- next = ccan_list_top(&sched->readyq, rb_thread_t, sched.node.readyq);
- if (next) rb_native_cond_signal(&next->nt->cond.readyq);
+ RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_SUSPENDED, th);
+
+ if (!to_dead) native_thread_dedicated_inc(th->vm, th->ractor, th->nt);
- return next;
+ RUBY_DEBUG_LOG("%sth:%u", to_dead ? "to_dead " : "", rb_th_serial(th));
+
+ bool can_switch = to_dead ? !th_has_dedicated_nt(th) : false;
+ thread_sched_wakeup_next_thread(sched, th, can_switch);
}
+// running -> dead (locked)
static void
-thread_sched_to_waiting(struct rb_thread_sched *sched, rb_thread_t *th)
+thread_sched_to_dead_common(struct rb_thread_sched *sched, rb_thread_t *th)
{
- RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_SUSPENDED);
- rb_native_mutex_lock(&sched->lock);
- thread_sched_to_waiting_common(sched, th);
- rb_native_mutex_unlock(&sched->lock);
+ RUBY_DEBUG_LOG("dedicated:%d", th->nt->dedicated);
+ thread_sched_to_waiting_common0(sched, th, true);
+ RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_EXITED, th);
}
+// running -> dead
static void
thread_sched_to_dead(struct rb_thread_sched *sched, rb_thread_t *th)
{
- RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_EXITED);
- thread_sched_to_waiting(sched, th);
+ thread_sched_lock(sched, th);
+ {
+ thread_sched_to_dead_common(sched, th);
+ }
+ thread_sched_unlock(sched, th);
}
+// running -> waiting (locked)
+//
+// This thread will run dedicated task (th->nt->dedicated++).
static void
-thread_sched_yield(struct rb_thread_sched *sched, rb_thread_t *th)
+thread_sched_to_waiting_common(struct rb_thread_sched *sched, rb_thread_t *th)
{
- rb_thread_t *next;
+ RUBY_DEBUG_LOG("dedicated:%d", th->nt->dedicated);
+ thread_sched_to_waiting_common0(sched, th, false);
+}
- /*
- * Perhaps other threads are stuck in blocking region w/o GVL, too,
- * (perhaps looping in io_close_fptr) so we kick them:
- */
- ubf_wakeup_all_threads();
- rb_native_mutex_lock(&sched->lock);
- next = thread_sched_to_waiting_common(sched, th);
-
- /* An another thread is processing GVL yield. */
- if (UNLIKELY(sched->wait_yield)) {
- while (sched->wait_yield)
- rb_native_cond_wait(&sched->switch_wait_cond, &sched->lock);
- }
- else if (next) {
- /* Wait until another thread task takes GVL. */
- sched->need_yield = 1;
- sched->wait_yield = 1;
- while (sched->need_yield)
- rb_native_cond_wait(&sched->switch_cond, &sched->lock);
- sched->wait_yield = 0;
- rb_native_cond_broadcast(&sched->switch_wait_cond);
+// running -> waiting
+//
+// This thread will run a dedicated task.
+static void
+thread_sched_to_waiting(struct rb_thread_sched *sched, rb_thread_t *th)
+{
+ thread_sched_lock(sched, th);
+ {
+ thread_sched_to_waiting_common(sched, th);
}
- else {
- rb_native_mutex_unlock(&sched->lock);
- native_thread_yield();
- rb_native_mutex_lock(&sched->lock);
- rb_native_cond_broadcast(&sched->switch_wait_cond);
+ thread_sched_unlock(sched, th);
+}
+
+// mini utility func
+static void
+setup_ubf(rb_thread_t *th, rb_unblock_function_t *func, void *arg)
+{
+ rb_native_mutex_lock(&th->interrupt_lock);
+ {
+ th->unblock.func = func;
+ th->unblock.arg = arg;
+ }
+ rb_native_mutex_unlock(&th->interrupt_lock);
+}
+
+static void
+ubf_waiting(void *ptr)
+{
+ rb_thread_t *th = (rb_thread_t *)ptr;
+ struct rb_thread_sched *sched = TH_SCHED(th);
+
+ // only once. it is safe because th->interrupt_lock is already acquired.
+ th->unblock.func = NULL;
+ th->unblock.arg = NULL;
+
+ RUBY_DEBUG_LOG("th:%u", rb_th_serial(th));
+
+ thread_sched_lock(sched, th);
+ {
+ if (sched->running == th) {
+ // not sleeping yet.
+ }
+ else {
+ thread_sched_to_ready_common(sched, th, true, false);
+ }
}
- thread_sched_to_running_common(sched, th);
- rb_native_mutex_unlock(&sched->lock);
+ thread_sched_unlock(sched, th);
+}
+
+// running -> waiting
+//
+// This thread will sleep until other thread wakeup the thread.
+static void
+thread_sched_to_waiting_until_wakeup(struct rb_thread_sched *sched, rb_thread_t *th)
+{
+ RUBY_DEBUG_LOG("th:%u", rb_th_serial(th));
+
+ RB_VM_SAVE_MACHINE_CONTEXT(th);
+ setup_ubf(th, ubf_waiting, (void *)th);
+
+ RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_SUSPENDED, th);
+
+ thread_sched_lock(sched, th);
+ {
+ if (!RUBY_VM_INTERRUPTED(th->ec)) {
+ bool can_direct_transfer = !th_has_dedicated_nt(th);
+ thread_sched_wakeup_next_thread(sched, th, can_direct_transfer);
+ thread_sched_wait_running_turn(sched, th, can_direct_transfer);
+ }
+ else {
+ RUBY_DEBUG_LOG("th:%u interrupted", rb_th_serial(th));
+ }
+ }
+ thread_sched_unlock(sched, th);
+
+ setup_ubf(th, NULL, NULL);
+}
+
+// run another thread in the ready queue.
+// continue to run if there are no ready threads.
+static void
+thread_sched_yield(struct rb_thread_sched *sched, rb_thread_t *th)
+{
+ RUBY_DEBUG_LOG("th:%d sched->readyq_cnt:%d", (int)th->serial, sched->readyq_cnt);
+
+ thread_sched_lock(sched, th);
+ {
+ if (!ccan_list_empty(&sched->readyq)) {
+ RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_SUSPENDED, th);
+ thread_sched_wakeup_next_thread(sched, th, !th_has_dedicated_nt(th));
+ bool can_direct_transfer = !th_has_dedicated_nt(th);
+ thread_sched_to_ready_common(sched, th, false, can_direct_transfer);
+ thread_sched_wait_running_turn(sched, th, can_direct_transfer);
+ }
+ else {
+ VM_ASSERT(sched->readyq_cnt == 0);
+ }
+ }
+ thread_sched_unlock(sched, th);
}
void
-rb_thread_sched_init(struct rb_thread_sched *sched)
+rb_thread_sched_init(struct rb_thread_sched *sched, bool atfork)
{
- rb_native_mutex_initialize(&sched->lock);
- rb_native_cond_initialize(&sched->switch_cond);
- rb_native_cond_initialize(&sched->switch_wait_cond);
+ rb_native_mutex_initialize(&sched->lock_);
+
+#if VM_CHECK_MODE
+ sched->lock_owner = NULL;
+#endif
+
ccan_list_head_init(&sched->readyq);
- sched->running = NULL;
- sched->timer = 0;
- sched->timer_err = ETIMEDOUT;
- sched->need_yield = 0;
- sched->wait_yield = 0;
+ sched->readyq_cnt = 0;
+
+#if USE_MN_THREADS
+ if (!atfork) sched->enable_mn_threads = true; // MN is enabled on Ractors
+#endif
+}
+
+static void
+coroutine_transfer0(struct coroutine_context *transfer_from, struct coroutine_context *transfer_to, bool to_dead)
+{
+#ifdef RUBY_ASAN_ENABLED
+ void **fake_stack = to_dead ? NULL : &transfer_from->fake_stack;
+ __sanitizer_start_switch_fiber(fake_stack, transfer_to->stack_base, transfer_to->stack_size);
+#endif
+
+ RBIMPL_ATTR_MAYBE_UNUSED()
+ struct coroutine_context *returning_from = coroutine_transfer(transfer_from, transfer_to);
+
+ /* if to_dead was passed, the caller is promising that this coroutine is finished and it should
+ * never be resumed! */
+ VM_ASSERT(!to_dead);
+#ifdef RUBY_ASAN_ENABLED
+ __sanitizer_finish_switch_fiber(transfer_from->fake_stack,
+ (const void**)&returning_from->stack_base, &returning_from->stack_size);
+#endif
+
+}
+
+static void
+thread_sched_switch0(struct coroutine_context *current_cont, rb_thread_t *next_th, struct rb_native_thread *nt, bool to_dead)
+{
+ VM_ASSERT(!nt->dedicated);
+ VM_ASSERT(next_th->nt == NULL);
+
+ RUBY_DEBUG_LOG("next_th:%u", rb_th_serial(next_th));
+
+ ruby_thread_set_native(next_th);
+ native_thread_assign(nt, next_th);
+
+ coroutine_transfer0(current_cont, next_th->sched.context, to_dead);
+}
+
+static void
+thread_sched_switch(rb_thread_t *cth, rb_thread_t *next_th)
+{
+ struct rb_native_thread *nt = cth->nt;
+ native_thread_assign(NULL, cth);
+ RUBY_DEBUG_LOG("th:%u->%u on nt:%d", rb_th_serial(cth), rb_th_serial(next_th), nt->serial);
+ thread_sched_switch0(cth->sched.context, next_th, nt, cth->status == THREAD_KILLED);
+}
+
+#if VM_CHECK_MODE > 0
+RBIMPL_ATTR_MAYBE_UNUSED()
+static unsigned int
+grq_size(rb_vm_t *vm, rb_ractor_t *cr)
+{
+ ASSERT_ractor_sched_locked(vm, cr);
+
+ rb_ractor_t *r, *prev_r = NULL;
+ unsigned int i = 0;
+
+ ccan_list_for_each(&vm->ractor.sched.grq, r, threads.sched.grq_node) {
+ i++;
+
+ VM_ASSERT(r != prev_r);
+ prev_r = r;
+ }
+ return i;
+}
+#endif
+
+static void
+ractor_sched_enq(rb_vm_t *vm, rb_ractor_t *r)
+{
+ struct rb_thread_sched *sched = &r->threads.sched;
+ rb_ractor_t *cr = NULL; // timer thread can call this function
+
+ VM_ASSERT(sched->running != NULL);
+ VM_ASSERT(sched->running->nt == NULL);
+
+ ractor_sched_lock(vm, cr);
+ {
+#if VM_CHECK_MODE > 0
+ // check if grq contains r
+ rb_ractor_t *tr;
+ ccan_list_for_each(&vm->ractor.sched.grq, tr, threads.sched.grq_node) {
+ VM_ASSERT(r != tr);
+ }
+#endif
+
+ ccan_list_add_tail(&vm->ractor.sched.grq, &sched->grq_node);
+ vm->ractor.sched.grq_cnt++;
+ VM_ASSERT(grq_size(vm, cr) == vm->ractor.sched.grq_cnt);
+
+ RUBY_DEBUG_LOG("r:%u th:%u grq_cnt:%u", rb_ractor_id(r), rb_th_serial(sched->running), vm->ractor.sched.grq_cnt);
+
+ rb_native_cond_signal(&vm->ractor.sched.cond);
+
+ // ractor_sched_dump(vm);
+ }
+ ractor_sched_unlock(vm, cr);
+}
+
+
+#ifndef SNT_KEEP_SECONDS
+#define SNT_KEEP_SECONDS 0
+#endif
+
+#ifndef MINIMUM_SNT
+// make at least MINIMUM_SNT snts for debug.
+#define MINIMUM_SNT 0
+#endif
+
+static rb_ractor_t *
+ractor_sched_deq(rb_vm_t *vm, rb_ractor_t *cr)
+{
+ rb_ractor_t *r;
+
+ ractor_sched_lock(vm, cr);
+ {
+ RUBY_DEBUG_LOG("empty? %d", ccan_list_empty(&vm->ractor.sched.grq));
+ // ractor_sched_dump(vm);
+
+ VM_ASSERT(rb_current_execution_context(false) == NULL);
+ VM_ASSERT(grq_size(vm, cr) == vm->ractor.sched.grq_cnt);
+
+ while ((r = ccan_list_pop(&vm->ractor.sched.grq, rb_ractor_t, threads.sched.grq_node)) == NULL) {
+ RUBY_DEBUG_LOG("wait grq_cnt:%d", (int)vm->ractor.sched.grq_cnt);
+
+#if SNT_KEEP_SECONDS > 0
+ rb_hrtime_t abs = rb_hrtime_add(rb_hrtime_now(), RB_HRTIME_PER_SEC * SNT_KEEP_SECONDS);
+ if (native_cond_timedwait(&vm->ractor.sched.cond, &vm->ractor.sched.lock, &abs) == ETIMEDOUT) {
+ RUBY_DEBUG_LOG("timeout, grq_cnt:%d", (int)vm->ractor.sched.grq_cnt);
+ VM_ASSERT(r == NULL);
+ vm->ractor.sched.snt_cnt--;
+ vm->ractor.sched.running_cnt--;
+ break;
+ }
+ else {
+ RUBY_DEBUG_LOG("wakeup grq_cnt:%d", (int)vm->ractor.sched.grq_cnt);
+ }
+#else
+ ractor_sched_set_unlocked(vm, cr);
+ rb_native_cond_wait(&vm->ractor.sched.cond, &vm->ractor.sched.lock);
+ ractor_sched_set_locked(vm, cr);
+
+ RUBY_DEBUG_LOG("wakeup grq_cnt:%d", (int)vm->ractor.sched.grq_cnt);
+#endif
+ }
+
+ VM_ASSERT(rb_current_execution_context(false) == NULL);
+
+ if (r) {
+ VM_ASSERT(vm->ractor.sched.grq_cnt > 0);
+ vm->ractor.sched.grq_cnt--;
+ RUBY_DEBUG_LOG("r:%d grq_cnt:%u", (int)rb_ractor_id(r), vm->ractor.sched.grq_cnt);
+ }
+ else {
+ VM_ASSERT(SNT_KEEP_SECONDS > 0);
+ // timeout
+ }
+ }
+ ractor_sched_unlock(vm, cr);
+
+ return r;
+}
+
+void rb_ractor_lock_self(rb_ractor_t *r);
+void rb_ractor_unlock_self(rb_ractor_t *r);
+
+void
+rb_ractor_sched_sleep(rb_execution_context_t *ec, rb_ractor_t *cr, rb_unblock_function_t *ubf)
+{
+ // ractor lock of cr is acquired
+ // r is sleeping statuss
+ rb_thread_t *th = rb_ec_thread_ptr(ec);
+ struct rb_thread_sched *sched = TH_SCHED(th);
+ cr->sync.wait.waiting_thread = th; // TODO: multi-thread
+
+ setup_ubf(th, ubf, (void *)cr);
+
+ thread_sched_lock(sched, th);
+ {
+ rb_ractor_unlock_self(cr);
+ {
+ if (RUBY_VM_INTERRUPTED(th->ec)) {
+ RUBY_DEBUG_LOG("interrupted");
+ }
+ else if (cr->sync.wait.wakeup_status != wakeup_none) {
+ RUBY_DEBUG_LOG("awaken:%d", (int)cr->sync.wait.wakeup_status);
+ }
+ else {
+ // sleep
+ RB_VM_SAVE_MACHINE_CONTEXT(th);
+ th->status = THREAD_STOPPED_FOREVER;
+
+ RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_SUSPENDED, th);
+
+ bool can_direct_transfer = !th_has_dedicated_nt(th);
+ thread_sched_wakeup_next_thread(sched, th, can_direct_transfer);
+ thread_sched_wait_running_turn(sched, th, can_direct_transfer);
+ th->status = THREAD_RUNNABLE;
+ // wakeup
+ }
+ }
+ }
+ thread_sched_unlock(sched, th);
+
+ setup_ubf(th, NULL, NULL);
+
+ rb_ractor_lock_self(cr);
+ cr->sync.wait.waiting_thread = NULL;
+}
+
+void
+rb_ractor_sched_wakeup(rb_ractor_t *r)
+{
+ rb_thread_t *r_th = r->sync.wait.waiting_thread;
+ // ractor lock of r is acquired
+ struct rb_thread_sched *sched = TH_SCHED(r_th);
+
+ VM_ASSERT(r->sync.wait.wakeup_status != 0);
+
+ thread_sched_lock(sched, r_th);
+ {
+ if (r_th->status == THREAD_STOPPED_FOREVER) {
+ thread_sched_to_ready_common(sched, r_th, true, false);
+ }
+ }
+ thread_sched_unlock(sched, r_th);
+}
+
+static bool
+ractor_sched_barrier_completed_p(rb_vm_t *vm)
+{
+ RUBY_DEBUG_LOG("run:%u wait:%u", vm->ractor.sched.running_cnt, vm->ractor.sched.barrier_waiting_cnt);
+ VM_ASSERT(vm->ractor.sched.running_cnt - 1 >= vm->ractor.sched.barrier_waiting_cnt);
+ return (vm->ractor.sched.running_cnt - vm->ractor.sched.barrier_waiting_cnt) == 1;
+}
+
+void
+rb_ractor_sched_barrier_start(rb_vm_t *vm, rb_ractor_t *cr)
+{
+ VM_ASSERT(cr == GET_RACTOR());
+ VM_ASSERT(vm->ractor.sync.lock_owner == cr); // VM is locked
+ VM_ASSERT(!vm->ractor.sched.barrier_waiting);
+ VM_ASSERT(vm->ractor.sched.barrier_waiting_cnt == 0);
+
+ RUBY_DEBUG_LOG("start serial:%u", vm->ractor.sched.barrier_serial);
+
+ unsigned int lock_rec;
+
+ ractor_sched_lock(vm, cr);
+ {
+ vm->ractor.sched.barrier_waiting = true;
+
+ // release VM lock
+ lock_rec = vm->ractor.sync.lock_rec;
+ vm->ractor.sync.lock_rec = 0;
+ vm->ractor.sync.lock_owner = NULL;
+ rb_native_mutex_unlock(&vm->ractor.sync.lock);
+ {
+ // interrupts all running threads
+ rb_thread_t *ith;
+ ccan_list_for_each(&vm->ractor.sched.running_threads, ith, sched.node.running_threads) {
+ if (ith->ractor != cr) {
+ RUBY_DEBUG_LOG("barrier int:%u", rb_th_serial(ith));
+ RUBY_VM_SET_VM_BARRIER_INTERRUPT(ith->ec);
+ }
+ }
+
+ // wait for other ractors
+ while (!ractor_sched_barrier_completed_p(vm)) {
+ ractor_sched_set_unlocked(vm, cr);
+ rb_native_cond_wait(&vm->ractor.sched.barrier_complete_cond, &vm->ractor.sched.lock);
+ ractor_sched_set_locked(vm, cr);
+ }
+ }
+ }
+ ractor_sched_unlock(vm, cr);
+
+ // acquire VM lock
+ rb_native_mutex_lock(&vm->ractor.sync.lock);
+ vm->ractor.sync.lock_rec = lock_rec;
+ vm->ractor.sync.lock_owner = cr;
+
+ RUBY_DEBUG_LOG("completed seirial:%u", vm->ractor.sched.barrier_serial);
+
+ ractor_sched_lock(vm, cr);
+ {
+ vm->ractor.sched.barrier_waiting = false;
+ vm->ractor.sched.barrier_serial++;
+ vm->ractor.sched.barrier_waiting_cnt = 0;
+ rb_native_cond_broadcast(&vm->ractor.sched.barrier_release_cond);
+ }
+ ractor_sched_unlock(vm, cr);
+}
+
+static void
+ractor_sched_barrier_join_signal_locked(rb_vm_t *vm)
+{
+ if (ractor_sched_barrier_completed_p(vm)) {
+ rb_native_cond_signal(&vm->ractor.sched.barrier_complete_cond);
+ }
+}
+
+static void
+ractor_sched_barrier_join_wait_locked(rb_vm_t *vm, rb_thread_t *th)
+{
+ VM_ASSERT(vm->ractor.sched.barrier_waiting);
+
+ unsigned int barrier_serial = vm->ractor.sched.barrier_serial;
+
+ while (vm->ractor.sched.barrier_serial == barrier_serial) {
+ RUBY_DEBUG_LOG("sleep serial:%u", barrier_serial);
+ RB_VM_SAVE_MACHINE_CONTEXT(th);
+
+ rb_ractor_t *cr = th->ractor;
+ ractor_sched_set_unlocked(vm, cr);
+ rb_native_cond_wait(&vm->ractor.sched.barrier_release_cond, &vm->ractor.sched.lock);
+ ractor_sched_set_locked(vm, cr);
+
+ RUBY_DEBUG_LOG("wakeup serial:%u", barrier_serial);
+ }
+}
+
+void
+rb_ractor_sched_barrier_join(rb_vm_t *vm, rb_ractor_t *cr)
+{
+ VM_ASSERT(cr->threads.sched.running != NULL); // running ractor
+ VM_ASSERT(cr == GET_RACTOR());
+ VM_ASSERT(vm->ractor.sync.lock_owner == NULL); // VM is locked, but owner == NULL
+ VM_ASSERT(vm->ractor.sched.barrier_waiting); // VM needs barrier sync
+
+#if USE_RUBY_DEBUG_LOG || VM_CHECK_MODE > 0
+ unsigned int barrier_serial = vm->ractor.sched.barrier_serial;
+#endif
+
+ RUBY_DEBUG_LOG("join");
+
+ rb_native_mutex_unlock(&vm->ractor.sync.lock);
+ {
+ VM_ASSERT(vm->ractor.sched.barrier_waiting); // VM needs barrier sync
+ VM_ASSERT(vm->ractor.sched.barrier_serial == barrier_serial);
+
+ ractor_sched_lock(vm, cr);
+ {
+ // running_cnt
+ vm->ractor.sched.barrier_waiting_cnt++;
+ RUBY_DEBUG_LOG("waiting_cnt:%u serial:%u", vm->ractor.sched.barrier_waiting_cnt, barrier_serial);
+
+ ractor_sched_barrier_join_signal_locked(vm);
+ ractor_sched_barrier_join_wait_locked(vm, cr->threads.sched.running);
+ }
+ ractor_sched_unlock(vm, cr);
+ }
+
+ rb_native_mutex_lock(&vm->ractor.sync.lock);
+ // VM locked here
}
#if 0
@@ -631,24 +1520,67 @@ rb_thread_sched_destroy(struct rb_thread_sched *sched)
* the end of thread_start_func_2
*/
if (0) {
- rb_native_cond_destroy(&sched->switch_wait_cond);
- rb_native_cond_destroy(&sched->switch_cond);
rb_native_mutex_destroy(&sched->lock);
}
clear_thread_cache_altstack();
}
#endif
+#ifdef RB_THREAD_T_HAS_NATIVE_ID
+static int
+get_native_thread_id(void)
+{
+#ifdef __linux__
+ return (int)syscall(SYS_gettid);
+#elif defined(__FreeBSD__)
+ return pthread_getthreadid_np();
+#endif
+}
+#endif
+
#if defined(HAVE_WORKING_FORK)
-static void thread_cache_reset(void);
static void
thread_sched_atfork(struct rb_thread_sched *sched)
{
current_fork_gen++;
- thread_cache_reset();
- rb_thread_sched_init(sched);
- thread_sched_to_running(sched, GET_THREAD());
+ rb_thread_sched_init(sched, true);
+ rb_thread_t *th = GET_THREAD();
+ rb_vm_t *vm = GET_VM();
+
+ if (th_has_dedicated_nt(th)) {
+ vm->ractor.sched.snt_cnt = 0;
+ }
+ else {
+ vm->ractor.sched.snt_cnt = 1;
+ }
+ vm->ractor.sched.running_cnt = 0;
+
+ // rb_native_cond_destroy(&vm->ractor.sched.cond);
+ rb_native_cond_initialize(&vm->ractor.sched.cond);
+ rb_native_cond_initialize(&vm->ractor.sched.barrier_complete_cond);
+ rb_native_cond_initialize(&vm->ractor.sched.barrier_release_cond);
+
+ ccan_list_head_init(&vm->ractor.sched.grq);
+ ccan_list_head_init(&vm->ractor.sched.timeslice_threads);
+ ccan_list_head_init(&vm->ractor.sched.running_threads);
+
+ VM_ASSERT(sched->is_running);
+ sched->is_running_timeslice = false;
+
+ if (sched->running != th) {
+ thread_sched_to_running(sched, th);
+ }
+ else {
+ thread_sched_setup_running_threads(sched, th->ractor, vm, th, NULL, NULL);
+ }
+
+#ifdef RB_THREAD_T_HAS_NATIVE_ID
+ if (th->nt) {
+ th->nt->tid = get_native_thread_id();
+ }
+#endif
}
+
#endif
#ifdef RB_THREAD_LOCAL_SPECIFIER
@@ -661,6 +1593,8 @@ static void
null_func(int i)
{
/* null */
+ // This function can be called from signal handler
+ // RUBY_DEBUG_LOG("i:%d", i);
}
rb_thread_t *
@@ -695,28 +1629,8 @@ ruby_thread_set_native(rb_thread_t *th)
#endif
}
-#ifdef RB_THREAD_T_HAS_NATIVE_ID
-static int
-get_native_thread_id(void)
-{
-#ifdef __linux__
- return (int)syscall(SYS_gettid);
-#elif defined(__FreeBSD__)
- return pthread_getthreadid_np();
-#endif
-}
-#endif
-
-static void
-native_thread_init(struct rb_native_thread *nt)
-{
-#ifdef RB_THREAD_T_HAS_NATIVE_ID
- nt->tid = get_native_thread_id();
-#endif
- rb_native_cond_initialize(&nt->cond.readyq);
- if (&nt->cond.readyq != &nt->cond.intr)
- rb_native_cond_initialize(&nt->cond.intr);
-}
+static void native_thread_setup(struct rb_native_thread *nt);
+static void native_thread_setup_on_thread(struct rb_native_thread *nt);
void
Init_native_thread(rb_thread_t *main_th)
@@ -739,40 +1653,148 @@ Init_native_thread(rb_thread_t *main_th)
rb_bug("pthread_key_create failed (ruby_current_ec_key)");
}
#endif
- posix_signal(SIGVTALRM, null_func);
+ ruby_posix_signal(SIGVTALRM, null_func);
+
+ // setup vm
+ rb_vm_t *vm = main_th->vm;
+ rb_native_mutex_initialize(&vm->ractor.sched.lock);
+ rb_native_cond_initialize(&vm->ractor.sched.cond);
+ rb_native_cond_initialize(&vm->ractor.sched.barrier_complete_cond);
+ rb_native_cond_initialize(&vm->ractor.sched.barrier_release_cond);
+
+ ccan_list_head_init(&vm->ractor.sched.grq);
+ ccan_list_head_init(&vm->ractor.sched.timeslice_threads);
+ ccan_list_head_init(&vm->ractor.sched.running_threads);
// setup main thread
main_th->nt->thread_id = pthread_self();
+ main_th->nt->serial = 1;
+#ifdef RUBY_NT_SERIAL
+ ruby_nt_serial = 1;
+#endif
ruby_thread_set_native(main_th);
- native_thread_init(main_th->nt);
+ native_thread_setup(main_th->nt);
+ native_thread_setup_on_thread(main_th->nt);
+
+ TH_SCHED(main_th)->running = main_th;
+ main_th->has_dedicated_nt = 1;
+
+ thread_sched_setup_running_threads(TH_SCHED(main_th), main_th->ractor, vm, main_th, NULL, NULL);
+
+ // setup main NT
+ main_th->nt->dedicated = 1;
+ main_th->nt->vm = vm;
+
+ // setup mn
+ vm->ractor.sched.dnt_cnt = 1;
}
-#ifndef USE_THREAD_CACHE
-#define USE_THREAD_CACHE 1
-#endif
+extern int ruby_mn_threads_enabled;
+
+void
+ruby_mn_threads_params(void)
+{
+ rb_vm_t *vm = GET_VM();
+ rb_ractor_t *main_ractor = GET_RACTOR();
+
+ const char *mn_threads_cstr = getenv("RUBY_MN_THREADS");
+ bool enable_mn_threads = false;
+
+ if (USE_MN_THREADS && mn_threads_cstr && (enable_mn_threads = atoi(mn_threads_cstr) > 0)) {
+ // enabled
+ ruby_mn_threads_enabled = 1;
+ }
+ main_ractor->threads.sched.enable_mn_threads = enable_mn_threads;
+
+ const char *max_cpu_cstr = getenv("RUBY_MAX_CPU");
+ const int default_max_cpu = 8; // TODO: CPU num?
+ int max_cpu = default_max_cpu;
+
+ if (USE_MN_THREADS && max_cpu_cstr) {
+ int given_max_cpu = atoi(max_cpu_cstr);
+ if (given_max_cpu > 0) {
+ max_cpu = given_max_cpu;
+ }
+ }
+
+ vm->ractor.sched.max_cpu = max_cpu;
+}
static void
-native_thread_destroy(rb_thread_t *th)
+native_thread_dedicated_inc(rb_vm_t *vm, rb_ractor_t *cr, struct rb_native_thread *nt)
{
- struct rb_native_thread *nt = th->nt;
+ RUBY_DEBUG_LOG("nt:%d %d->%d", nt->serial, nt->dedicated, nt->dedicated + 1);
- rb_native_cond_destroy(&nt->cond.readyq);
+ if (nt->dedicated == 0) {
+ ractor_sched_lock(vm, cr);
+ {
+ vm->ractor.sched.snt_cnt--;
+ vm->ractor.sched.dnt_cnt++;
+ }
+ ractor_sched_unlock(vm, cr);
+ }
- if (&nt->cond.readyq != &nt->cond.intr)
- rb_native_cond_destroy(&nt->cond.intr);
+ nt->dedicated++;
+}
- /*
- * prevent false positive from ruby_thread_has_gvl_p if that
- * gets called from an interposing function wrapper
- */
- if (USE_THREAD_CACHE)
- ruby_thread_set_native(0);
+static void
+native_thread_dedicated_dec(rb_vm_t *vm, rb_ractor_t *cr, struct rb_native_thread *nt)
+{
+ RUBY_DEBUG_LOG("nt:%d %d->%d", nt->serial, nt->dedicated, nt->dedicated - 1);
+ VM_ASSERT(nt->dedicated > 0);
+ nt->dedicated--;
+
+ if (nt->dedicated == 0) {
+ ractor_sched_lock(vm, cr);
+ {
+ nt->vm->ractor.sched.snt_cnt++;
+ nt->vm->ractor.sched.dnt_cnt--;
+ }
+ ractor_sched_unlock(vm, cr);
+ }
}
-#if USE_THREAD_CACHE
-static rb_thread_t *register_cached_thread_and_wait(void *);
+static void
+native_thread_assign(struct rb_native_thread *nt, rb_thread_t *th)
+{
+#if USE_RUBY_DEBUG_LOG
+ if (nt) {
+ if (th->nt) {
+ RUBY_DEBUG_LOG("th:%d nt:%d->%d", (int)th->serial, (int)th->nt->serial, (int)nt->serial);
+ }
+ else {
+ RUBY_DEBUG_LOG("th:%d nt:NULL->%d", (int)th->serial, (int)nt->serial);
+ }
+ }
+ else {
+ if (th->nt) {
+ RUBY_DEBUG_LOG("th:%d nt:%d->NULL", (int)th->serial, (int)th->nt->serial);
+ }
+ else {
+ RUBY_DEBUG_LOG("th:%d nt:NULL->NULL", (int)th->serial);
+ }
+ }
#endif
+ th->nt = nt;
+}
+
+static void
+native_thread_destroy(struct rb_native_thread *nt)
+{
+ if (nt) {
+ rb_native_cond_destroy(&nt->cond.readyq);
+
+ if (&nt->cond.readyq != &nt->cond.intr) {
+ rb_native_cond_destroy(&nt->cond.intr);
+ }
+
+ RB_ALTSTACK_FREE(nt->altstack);
+ ruby_xfree(nt->nt_context);
+ ruby_xfree(nt);
+ }
+}
+
#if defined HAVE_PTHREAD_GETATTR_NP || defined HAVE_PTHREAD_ATTR_GET_NP
#define STACKADDR_AVAILABLE 1
#elif defined HAVE_PTHREAD_GET_STACKADDR_NP && defined HAVE_PTHREAD_GET_STACKSIZE_NP
@@ -965,11 +1987,13 @@ reserve_stack(volatile char *limit, size_t size)
# define reserve_stack(limit, size) ((void)(limit), (void)(size))
#endif
-#undef ruby_init_stack
-void
-ruby_init_stack(volatile VALUE *addr)
+static void
+native_thread_init_main_thread_stack(void *addr)
{
native_main_thread.id = pthread_self();
+#ifdef RUBY_ASAN_ENABLED
+ addr = asan_get_real_stack_addr((void *)addr);
+#endif
#if MAINSTACKADDR_AVAILABLE
if (native_main_thread.stack_maxsize) return;
@@ -989,8 +2013,8 @@ ruby_init_stack(volatile VALUE *addr)
#else
if (!native_main_thread.stack_start ||
STACK_UPPER((VALUE *)(void *)&addr,
- native_main_thread.stack_start > addr,
- native_main_thread.stack_start < addr)) {
+ native_main_thread.stack_start > (VALUE *)addr,
+ native_main_thread.stack_start < (VALUE *)addr)) {
native_main_thread.stack_start = (VALUE *)addr;
}
#endif
@@ -1052,9 +2076,19 @@ ruby_init_stack(volatile VALUE *addr)
{int err = (expr); if (err) {rb_bug_errno(#expr, err);}}
static int
-native_thread_init_stack(rb_thread_t *th)
+native_thread_init_stack(rb_thread_t *th, void *local_in_parent_frame)
{
rb_nativethread_id_t curr = pthread_self();
+#ifdef RUBY_ASAN_ENABLED
+ local_in_parent_frame = asan_get_real_stack_addr(local_in_parent_frame);
+ th->ec->machine.asan_fake_stack_handle = asan_get_thread_fake_stack_handle();
+#endif
+
+ if (!native_main_thread.id) {
+ /* This thread is the first thread, must be the main thread -
+ * configure the native_main_thread object */
+ native_thread_init_main_thread_stack(local_in_parent_frame);
+ }
if (pthread_equal(curr, native_main_thread.id)) {
th->ec->machine.stack_start = native_main_thread.stack_start;
@@ -1062,13 +2096,15 @@ native_thread_init_stack(rb_thread_t *th)
}
else {
#ifdef STACKADDR_AVAILABLE
- void *start;
- size_t size;
-
- if (get_stack(&start, &size) == 0) {
- uintptr_t diff = (uintptr_t)start - (uintptr_t)&curr;
- th->ec->machine.stack_start = (VALUE *)&curr;
- th->ec->machine.stack_maxsize = size - diff;
+ if (th_has_dedicated_nt(th)) {
+ void *start;
+ size_t size;
+
+ if (get_stack(&start, &size) == 0) {
+ uintptr_t diff = (uintptr_t)start - (uintptr_t)local_in_parent_frame;
+ th->ec->machine.stack_start = local_in_parent_frame;
+ th->ec->machine.stack_maxsize = size - diff;
+ }
}
#else
rb_raise(rb_eNotImpError, "ruby engine can initialize only in the main thread");
@@ -1078,212 +2114,292 @@ native_thread_init_stack(rb_thread_t *th)
return 0;
}
-#ifndef __CYGWIN__
-#define USE_NATIVE_THREAD_INIT 1
-#endif
+struct nt_param {
+ rb_vm_t *vm;
+ struct rb_native_thread *nt;
+};
static void *
-thread_start_func_1(void *th_ptr)
+nt_start(void *ptr);
+
+static int
+native_thread_create0(struct rb_native_thread *nt)
{
- rb_thread_t *th = th_ptr;
+ int err = 0;
+ pthread_attr_t attr;
-#if USE_RUBY_DEBUG_LOG && defined(RUBY_NT_SERIAL)
- ruby_nt_serial = th->nt->serial;
-#endif
+ const size_t stack_size = nt->vm->default_params.thread_machine_stack_size;
+ const size_t space = space_size(stack_size);
- RB_ALTSTACK_INIT(void *altstack, th->nt->altstack);
-#if USE_THREAD_CACHE
- thread_start:
-#endif
- {
-#if !defined USE_NATIVE_THREAD_INIT
- VALUE stack_start;
+ nt->machine_stack_maxsize = stack_size - space;
+
+#ifdef USE_SIGALTSTACK
+ nt->altstack = rb_allocate_sigaltstack();
#endif
-#if defined USE_NATIVE_THREAD_INIT
- native_thread_init_stack(th);
+ CHECK_ERR(pthread_attr_init(&attr));
+
+# ifdef PTHREAD_STACK_MIN
+ RUBY_DEBUG_LOG("stack size: %lu", (unsigned long)stack_size);
+ CHECK_ERR(pthread_attr_setstacksize(&attr, stack_size));
+# endif
+
+# ifdef HAVE_PTHREAD_ATTR_SETINHERITSCHED
+ CHECK_ERR(pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED));
+# endif
+ CHECK_ERR(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
+
+ err = pthread_create(&nt->thread_id, &attr, nt_start, nt);
+
+ RUBY_DEBUG_LOG("nt:%d err:%d", (int)nt->serial, err);
+
+ CHECK_ERR(pthread_attr_destroy(&attr));
+
+ return err;
+}
+
+static void
+native_thread_setup(struct rb_native_thread *nt)
+{
+ // init cond
+ rb_native_cond_initialize(&nt->cond.readyq);
+
+ if (&nt->cond.readyq != &nt->cond.intr) {
+ rb_native_cond_initialize(&nt->cond.intr);
+ }
+}
+
+static void
+native_thread_setup_on_thread(struct rb_native_thread *nt)
+{
+ // init tid
+#ifdef RB_THREAD_T_HAS_NATIVE_ID
+ nt->tid = get_native_thread_id();
#endif
- native_thread_init(th->nt);
+ // init signal handler
+ RB_ALTSTACK_INIT(nt->altstack, nt->altstack);
+}
- RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_STARTED);
+static struct rb_native_thread *
+native_thread_alloc(void)
+{
+ struct rb_native_thread *nt = ZALLOC(struct rb_native_thread);
+ native_thread_setup(nt);
- /* run */
-#if defined USE_NATIVE_THREAD_INIT
- thread_start_func_2(th, th->ec->machine.stack_start);
-#else
- thread_start_func_2(th, &stack_start);
+#if USE_MN_THREADS
+ nt->nt_context = ruby_xmalloc(sizeof(struct coroutine_context));
#endif
- }
-#if USE_THREAD_CACHE
- /* cache thread */
- if ((th = register_cached_thread_and_wait(RB_ALTSTACK(altstack))) != 0) {
- goto thread_start;
- }
-#else
- RB_ALTSTACK_FREE(altstack);
+
+#if USE_RUBY_DEBUG_LOG
+ static rb_atomic_t nt_serial = 2;
+ nt->serial = RUBY_ATOMIC_FETCH_ADD(nt_serial, 1);
#endif
- return 0;
+ return nt;
}
-struct cached_thread_entry {
- rb_nativethread_cond_t cond;
- rb_nativethread_id_t thread_id;
- rb_thread_t *th;
- void *altstack;
- struct ccan_list_node node;
-};
+static int
+native_thread_create_dedicated(rb_thread_t *th)
+{
+ th->nt = native_thread_alloc();
+ th->nt->vm = th->vm;
+ th->nt->running_thread = th;
+ th->nt->dedicated = 1;
+
+ // vm stack
+ size_t vm_stack_word_size = th->vm->default_params.thread_vm_stack_size / sizeof(VALUE);
+ void *vm_stack = ruby_xmalloc(vm_stack_word_size * sizeof(VALUE));
+ th->sched.malloc_stack = true;
+ rb_ec_initialize_vm_stack(th->ec, vm_stack, vm_stack_word_size);
+ th->sched.context_stack = vm_stack;
-#if USE_THREAD_CACHE
-static rb_nativethread_lock_t thread_cache_lock = RB_NATIVETHREAD_LOCK_INIT;
-static CCAN_LIST_HEAD(cached_thread_head);
+ // setup
+ thread_sched_to_ready(TH_SCHED(th), th);
+
+ return native_thread_create0(th->nt);
+}
-# if defined(HAVE_WORKING_FORK)
static void
-thread_cache_reset(void)
+call_thread_start_func_2(rb_thread_t *th)
{
- rb_native_mutex_initialize(&thread_cache_lock);
- ccan_list_head_init(&cached_thread_head);
+ /* Capture the address of a local in this stack frame to mark the beginning of the
+ machine stack for this thread. This is required even if we can tell the real
+ stack beginning from the pthread API in native_thread_init_stack, because
+ glibc stores some of its own data on the stack before calling into user code
+ on a new thread, and replacing that data on fiber-switch would break it (see
+ bug #13887) */
+ VALUE stack_start = 0;
+ VALUE *stack_start_addr = asan_get_real_stack_addr(&stack_start);
+
+ native_thread_init_stack(th, stack_start_addr);
+ thread_start_func_2(th, th->ec->machine.stack_start);
}
-# endif
-/*
- * number of seconds to cache for, I think 1-5s is sufficient to obviate
- * the need for thread pool in many network programs (taking into account
- * worst case network latency across the globe) without wasting memory
- */
-#ifndef THREAD_CACHE_TIME
-# define THREAD_CACHE_TIME ((rb_hrtime_t)3 * RB_HRTIME_PER_SEC)
+static void *
+nt_start(void *ptr)
+{
+ struct rb_native_thread *nt = (struct rb_native_thread *)ptr;
+ rb_vm_t *vm = nt->vm;
+
+ native_thread_setup_on_thread(nt);
+
+ // init tid
+#ifdef RB_THREAD_T_HAS_NATIVE_ID
+ nt->tid = get_native_thread_id();
#endif
-static rb_thread_t *
-register_cached_thread_and_wait(void *altstack)
-{
- rb_hrtime_t end = THREAD_CACHE_TIME;
- struct cached_thread_entry entry;
+#if USE_RUBY_DEBUG_LOG && defined(RUBY_NT_SERIAL)
+ ruby_nt_serial = nt->serial;
+#endif
- rb_native_cond_initialize(&entry.cond);
- entry.altstack = altstack;
- entry.th = NULL;
- entry.thread_id = pthread_self();
- end = native_cond_timeout(&entry.cond, end);
+ RUBY_DEBUG_LOG("nt:%u", nt->serial);
- rb_native_mutex_lock(&thread_cache_lock);
- {
- ccan_list_add(&cached_thread_head, &entry.node);
+ if (!nt->dedicated) {
+ coroutine_initialize_main(nt->nt_context);
+ }
- native_cond_timedwait(&entry.cond, &thread_cache_lock, &end);
+ while (1) {
+ if (nt->dedicated) {
+ // wait running turn
+ rb_thread_t *th = nt->running_thread;
+ struct rb_thread_sched *sched = TH_SCHED(th);
- if (entry.th == NULL) { /* unused */
- ccan_list_del(&entry.node);
+ RUBY_DEBUG_LOG("on dedicated th:%u", rb_th_serial(th));
+ ruby_thread_set_native(th);
+
+ thread_sched_lock(sched, th);
+ {
+ if (sched->running == th) {
+ thread_sched_add_running_thread(sched, th);
+ }
+ thread_sched_wait_running_turn(sched, th, false);
+ }
+ thread_sched_unlock(sched, th);
+
+ // start threads
+ call_thread_start_func_2(th);
+ break; // TODO: allow to change to the SNT
}
- }
- rb_native_mutex_unlock(&thread_cache_lock);
+ else {
+ RUBY_DEBUG_LOG("check next");
+ rb_ractor_t *r = ractor_sched_deq(vm, NULL);
+
+ if (r) {
+ struct rb_thread_sched *sched = &r->threads.sched;
+
+ thread_sched_lock(sched, NULL);
+ {
+ rb_thread_t *next_th = sched->running;
+
+ if (next_th && next_th->nt == NULL) {
+ RUBY_DEBUG_LOG("nt:%d next_th:%d", (int)nt->serial, (int)next_th->serial);
+ thread_sched_switch0(nt->nt_context, next_th, nt, false);
+ }
+ else {
+ RUBY_DEBUG_LOG("no schedulable threads -- next_th:%p", next_th);
+ }
+ }
+ thread_sched_unlock(sched, NULL);
+ }
+ else {
+ // timeout -> deleted.
+ break;
+ }
- rb_native_cond_destroy(&entry.cond);
- if (!entry.th) {
- RB_ALTSTACK_FREE(entry.altstack);
+ if (nt->dedicated) {
+ // SNT becomes DNT while running
+ break;
+ }
+ }
}
- return entry.th;
+ return NULL;
}
-#else
-# if defined(HAVE_WORKING_FORK)
-static void thread_cache_reset(void) { }
-# endif
+
+static int native_thread_create_shared(rb_thread_t *th);
+
+#if USE_MN_THREADS
+static void nt_free_stack(void *mstack);
#endif
-static int
-use_cached_thread(rb_thread_t *th)
+void
+rb_threadptr_remove(rb_thread_t *th)
{
-#if USE_THREAD_CACHE
- struct cached_thread_entry *entry;
+#if USE_MN_THREADS
+ if (th->sched.malloc_stack) {
+ // dedicated
+ return;
+ }
+ else {
+ rb_vm_t *vm = th->vm;
+ th->sched.finished = false;
- rb_native_mutex_lock(&thread_cache_lock);
- entry = ccan_list_pop(&cached_thread_head, struct cached_thread_entry, node);
- if (entry) {
- entry->th = th;
- /* th->nt->thread_id must be set before signal for Thread#name= */
- th->nt->thread_id = entry->thread_id;
- rb_native_cond_signal(&entry->cond);
+ RB_VM_LOCK_ENTER();
+ {
+ ccan_list_add(&vm->ractor.sched.zombie_threads, &th->sched.node.zombie_threads);
+ }
+ RB_VM_LOCK_LEAVE();
}
- rb_native_mutex_unlock(&thread_cache_lock);
- return !!entry;
#endif
- return 0;
}
-#if 0
-// TODO
-static void
-clear_thread_cache_altstack(void)
+void
+rb_threadptr_sched_free(rb_thread_t *th)
{
-#if USE_THREAD_CACHE
- struct cached_thread_entry *entry;
-
- rb_native_mutex_lock(&thread_cache_lock);
- ccan_list_for_each(&cached_thread_head, entry, node) {
- void MAYBE_UNUSED(*altstack) = entry->altstack;
- entry->altstack = 0;
- RB_ALTSTACK_FREE(altstack);
+#if USE_MN_THREADS
+ if (th->sched.malloc_stack) {
+ // has dedicated
+ ruby_xfree(th->sched.context_stack);
+ native_thread_destroy(th->nt);
+ }
+ else {
+ nt_free_stack(th->sched.context_stack);
+ // TODO: how to free nt and nt->altstack?
}
- rb_native_mutex_unlock(&thread_cache_lock);
+
+ ruby_xfree(th->sched.context);
+ VM_ASSERT((th->sched.context = NULL) == NULL);
+#else
+ ruby_xfree(th->sched.context_stack);
+ native_thread_destroy(th->nt);
#endif
+
+ th->nt = NULL;
}
-#endif
-static struct rb_native_thread *
-native_thread_alloc(void)
+void
+rb_thread_sched_mark_zombies(rb_vm_t *vm)
{
- struct rb_native_thread *nt = ZALLOC(struct rb_native_thread);
-#if USE_RUBY_DEBUG_LOG
- static rb_atomic_t nt_serial = 1;
- nt->serial = RUBY_ATOMIC_FETCH_ADD(nt_serial, 1);
-#endif
- return nt;
+ if (!ccan_list_empty(&vm->ractor.sched.zombie_threads)) {
+ rb_thread_t *zombie_th, *next_zombie_th;
+ ccan_list_for_each_safe(&vm->ractor.sched.zombie_threads, zombie_th, next_zombie_th, sched.node.zombie_threads) {
+ if (zombie_th->sched.finished) {
+ ccan_list_del_init(&zombie_th->sched.node.zombie_threads);
+ }
+ else {
+ rb_gc_mark(zombie_th->self);
+ }
+ }
+ }
}
static int
native_thread_create(rb_thread_t *th)
{
- int err = 0;
-
VM_ASSERT(th->nt == 0);
- th->nt = native_thread_alloc();
+ RUBY_DEBUG_LOG("th:%d has_dnt:%d", th->serial, th->has_dedicated_nt);
+ RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_STARTED, th);
- if (use_cached_thread(th)) {
- RUBY_DEBUG_LOG("use cached nt. th:%u", rb_th_serial(th));
+ if (!th->ractor->threads.sched.enable_mn_threads) {
+ th->has_dedicated_nt = 1;
}
- else {
- pthread_attr_t attr;
- const size_t stack_size = th->vm->default_params.thread_machine_stack_size + th->vm->default_params.thread_vm_stack_size;
- const size_t space = space_size(stack_size);
-#ifdef USE_SIGALTSTACK
- th->nt->altstack = rb_allocate_sigaltstack();
-#endif
- th->ec->machine.stack_maxsize = stack_size - space;
-
- CHECK_ERR(pthread_attr_init(&attr));
-
-# ifdef PTHREAD_STACK_MIN
- RUBY_DEBUG_LOG("stack size: %lu", (unsigned long)stack_size);
- CHECK_ERR(pthread_attr_setstacksize(&attr, stack_size));
-# endif
-
-# ifdef HAVE_PTHREAD_ATTR_SETINHERITSCHED
- CHECK_ERR(pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED));
-# endif
- CHECK_ERR(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
-
- err = pthread_create(&th->nt->thread_id, &attr, thread_start_func_1, th);
-
- RUBY_DEBUG_LOG("th:%u err:%d", rb_th_serial(th), err);
-
- /* should be done in the created thread */
- CHECK_ERR(pthread_attr_destroy(&attr));
+ if (th->has_dedicated_nt) {
+ return native_thread_create_dedicated(th);
+ }
+ else {
+ return native_thread_create_shared(th);
}
- return err;
}
#if USE_NATIVE_THREAD_PRIORITY
@@ -1326,7 +2442,7 @@ static void
ubf_pthread_cond_signal(void *ptr)
{
rb_thread_t *th = (rb_thread_t *)ptr;
- RUBY_DEBUG_LOG("th:%u", rb_th_serial(th));
+ RUBY_DEBUG_LOG("th:%u on nt:%d", rb_th_serial(th), (int)th->nt->serial);
rb_native_cond_signal(&th->nt->cond.intr);
}
@@ -1391,33 +2507,54 @@ ubf_list_atfork(void)
rb_native_mutex_initialize(&ubf_list_lock);
}
+RBIMPL_ATTR_MAYBE_UNUSED()
+static bool
+ubf_list_contain_p(rb_thread_t *th)
+{
+ rb_thread_t *list_th;
+ ccan_list_for_each(&ubf_list_head, list_th, sched.node.ubf) {
+ if (list_th == th) return true;
+ }
+ return false;
+}
+
/* The thread 'th' is registered to be trying unblock. */
static void
register_ubf_list(rb_thread_t *th)
{
+ RUBY_DEBUG_LOG("th:%u", rb_th_serial(th));
struct ccan_list_node *node = &th->sched.node.ubf;
- if (ccan_list_empty((struct ccan_list_head*)node)) {
- rb_native_mutex_lock(&ubf_list_lock);
- ccan_list_add(&ubf_list_head, node);
- rb_native_mutex_unlock(&ubf_list_lock);
+ VM_ASSERT(th->unblock.func != NULL);
+
+ rb_native_mutex_lock(&ubf_list_lock);
+ {
+ // check not connected yet
+ if (ccan_list_empty((struct ccan_list_head*)node)) {
+ VM_ASSERT(!ubf_list_contain_p(th));
+ ccan_list_add(&ubf_list_head, node);
+ }
}
+ rb_native_mutex_unlock(&ubf_list_lock);
+
+ timer_thread_wakeup();
}
/* The thread 'th' is unblocked. It no longer need to be registered. */
static void
unregister_ubf_list(rb_thread_t *th)
{
+ RUBY_DEBUG_LOG("th:%u", rb_th_serial(th));
struct ccan_list_node *node = &th->sched.node.ubf;
/* we can't allow re-entry into ubf_list_head */
- VM_ASSERT(th->unblock.func == 0);
+ VM_ASSERT(th->unblock.func == NULL);
if (!ccan_list_empty((struct ccan_list_head*)node)) {
rb_native_mutex_lock(&ubf_list_lock);
- ccan_list_del_init(node);
- if (ccan_list_empty(&ubf_list_head) && !rb_signal_buff_size()) {
- ubf_timer_disarm();
+ {
+ VM_ASSERT(ubf_list_contain_p(th));
+ ccan_list_del_init(node);
}
rb_native_mutex_unlock(&ubf_list_lock);
}
@@ -1430,61 +2567,39 @@ unregister_ubf_list(rb_thread_t *th)
static void
ubf_wakeup_thread(rb_thread_t *th)
{
- RUBY_DEBUG_LOG("th:%u", rb_th_serial(th));
- pthread_kill(th->nt->thread_id, SIGVTALRM);
+ RUBY_DEBUG_LOG("th:%u thread_id:%p", rb_th_serial(th), (void *)th->nt->thread_id);
+
+ int r = pthread_kill(th->nt->thread_id, SIGVTALRM);
+ if (r != 0) {
+ rb_bug_errno("pthread_kill", r);
+ }
}
static void
ubf_select(void *ptr)
{
rb_thread_t *th = (rb_thread_t *)ptr;
- struct rb_thread_sched *sched = TH_SCHED(th);
- const rb_thread_t *cur = ruby_thread_from_native(); /* may be 0 */
-
- register_ubf_list(th);
-
- /*
- * ubf_wakeup_thread() doesn't guarantee to wake up a target thread.
- * Therefore, we repeatedly call ubf_wakeup_thread() until a target thread
- * exit from ubf function. We must have a timer to perform this operation.
- * We use double-checked locking here because this function may be called
- * while vm->gvl.lock is held in do_gvl_timer.
- * There is also no need to start a timer if we're the designated
- * sigwait_th thread, otherwise we can deadlock with a thread
- * in unblock_function_clear.
- */
- if (cur != sched->timer && cur != sigwait_th) {
- /*
- * Double-checked locking above was to prevent nested locking
- * by the SAME thread. We use trylock here to prevent deadlocks
- * between DIFFERENT threads
- */
- if (rb_native_mutex_trylock(&sched->lock) == 0) {
- if (!sched->timer) {
- rb_thread_wakeup_timer_thread(-1);
- }
- rb_native_mutex_unlock(&sched->lock);
- }
- }
-
+ RUBY_DEBUG_LOG("wakeup th:%u", rb_th_serial(th));
ubf_wakeup_thread(th);
+ register_ubf_list(th);
}
-static int
+static bool
ubf_threads_empty(void)
{
- return ccan_list_empty(&ubf_list_head);
+ return ccan_list_empty(&ubf_list_head) != 0;
}
static void
ubf_wakeup_all_threads(void)
{
if (!ubf_threads_empty()) {
- rb_native_mutex_lock(&ubf_list_lock);
rb_thread_t *th;
-
- ccan_list_for_each(&ubf_list_head, th, sched.node.ubf) {
- ubf_wakeup_thread(th);
+ rb_native_mutex_lock(&ubf_list_lock);
+ {
+ ccan_list_for_each(&ubf_list_head, th, sched.node.ubf) {
+ ubf_wakeup_thread(th);
+ }
}
rb_native_mutex_unlock(&ubf_list_lock);
}
@@ -1495,151 +2610,35 @@ ubf_wakeup_all_threads(void)
#define unregister_ubf_list(th) (void)(th)
#define ubf_select 0
static void ubf_wakeup_all_threads(void) { return; }
-static int ubf_threads_empty(void) { return 1; }
+static bool ubf_threads_empty(void) { return true; }
#define ubf_list_atfork() do {} while (0)
#endif /* USE_UBF_LIST */
#define TT_DEBUG 0
#define WRITE_CONST(fd, str) (void)(write((fd),(str),sizeof(str)-1)<0)
-static struct {
- /* pipes are closed in forked children when owner_process does not match */
- int normal[2]; /* [0] == sigwait_fd */
- int ub_main[2]; /* unblock main thread from native_ppoll_sleep */
-
- /* volatile for signal handler use: */
- volatile rb_serial_t fork_gen;
-} signal_self_pipe = {
- {-1, -1},
- {-1, -1},
-};
-
-/* only use signal-safe system calls here */
-static void
-rb_thread_wakeup_timer_thread_fd(int fd)
-{
-#if USE_EVENTFD
- const uint64_t buff = 1;
-#else
- const char buff = '!';
-#endif
- ssize_t result;
-
- /* already opened */
- if (fd >= 0) {
- retry:
- if ((result = write(fd, &buff, sizeof(buff))) <= 0) {
- int e = errno;
- switch (e) {
- case EINTR: goto retry;
- case EAGAIN:
-#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
- case EWOULDBLOCK:
-#endif
- break;
- default:
- async_bug_fd("rb_thread_wakeup_timer_thread: write", e, fd);
- }
- }
- if (TT_DEBUG) WRITE_CONST(2, "rb_thread_wakeup_timer_thread: write\n");
- }
- else {
- /* ignore wakeup */
- }
-}
-
-/*
- * This ensures we get a SIGVTALRM in TIME_QUANTUM_MSEC if our
- * process could not react to the original signal in time.
- */
-static void
-ubf_timer_arm(rb_serial_t fork_gen) /* async signal safe */
-{
-#if UBF_TIMER == UBF_TIMER_POSIX
- if ((!fork_gen || timer_posix.fork_gen == fork_gen) &&
- timer_state_cas(RTIMER_DISARM, RTIMER_ARMING) == RTIMER_DISARM) {
- struct itimerspec it;
-
- it.it_interval.tv_sec = it.it_value.tv_sec = 0;
- it.it_interval.tv_nsec = it.it_value.tv_nsec = TIME_QUANTUM_NSEC;
-
- if (timer_settime(timer_posix.timerid, 0, &it, 0))
- rb_async_bug_errno("timer_settime (arm)", errno);
-
- switch (timer_state_cas(RTIMER_ARMING, RTIMER_ARMED)) {
- case RTIMER_DISARM:
- /* somebody requested a disarm while we were arming */
- /* may race harmlessly with ubf_timer_destroy */
- (void)timer_settime(timer_posix.timerid, 0, &zero, 0);
-
- case RTIMER_ARMING: return; /* success */
- case RTIMER_ARMED:
- /*
- * it is possible to have another thread disarm, and
- * a third thread arm finish re-arming before we get
- * here, so we wasted a syscall with timer_settime but
- * probably unavoidable in a signal handler.
- */
- return;
- case RTIMER_DEAD:
- /* may race harmlessly with ubf_timer_destroy */
- (void)timer_settime(timer_posix.timerid, 0, &zero, 0);
- return;
- default:
- rb_async_bug_errno("UBF_TIMER_POSIX unknown state", ERANGE);
- }
- }
-#elif UBF_TIMER == UBF_TIMER_PTHREAD
- if (!fork_gen || fork_gen == timer_pthread.fork_gen) {
- if (ATOMIC_EXCHANGE(timer_pthread.armed, 1) == 0)
- rb_thread_wakeup_timer_thread_fd(timer_pthread.low[1]);
- }
-#endif
-}
-
void
rb_thread_wakeup_timer_thread(int sig)
{
- /* non-sighandler path */
- if (sig <= 0) {
- rb_thread_wakeup_timer_thread_fd(signal_self_pipe.normal[1]);
- if (sig < 0) {
- ubf_timer_arm(0);
- }
- return;
- }
+ // This function can be called from signal handlers so that
+ // pthread_mutex_lock() should not be used.
- /* must be safe inside sighandler, so no mutex */
- if (signal_self_pipe.fork_gen == current_fork_gen) {
- rb_thread_wakeup_timer_thread_fd(signal_self_pipe.normal[1]);
+ // wakeup timer thread
+ timer_thread_wakeup_force();
- /*
- * system_working check is required because vm and main_thread are
- * freed during shutdown
- */
- if (system_working > 0) {
- volatile rb_execution_context_t *ec;
- rb_vm_t *vm = GET_VM();
- rb_thread_t *mth;
+ // interrupt main thread if main thread is available
+ if (system_working) {
+ rb_vm_t *vm = GET_VM();
+ rb_thread_t *main_th = vm->ractor.main_thread;
- /*
- * FIXME: root VM and main_thread should be static and not
- * on heap for maximum safety (and startup/shutdown speed)
- */
- if (!vm) return;
- mth = vm->ractor.main_thread;
- if (!mth || system_working <= 0) return;
+ if (main_th) {
+ volatile rb_execution_context_t *main_th_ec = ACCESS_ONCE(rb_execution_context_t *, main_th->ec);
- /* this relies on GC for grace period before cont_free */
- ec = ACCESS_ONCE(rb_execution_context_t *, mth->ec);
+ if (main_th_ec) {
+ RUBY_VM_SET_TRAP_INTERRUPT(main_th_ec);
- if (ec) {
- RUBY_VM_SET_TRAP_INTERRUPT(ec);
- ubf_timer_arm(current_fork_gen);
-
- /* some ubfs can interrupt single-threaded process directly */
- if (vm->ubf_async_safe && mth->unblock.func) {
- (mth->unblock.func)(mth->unblock.arg);
+ if (vm->ubf_async_safe && main_th->unblock.func) {
+ (main_th->unblock.func)(main_th->unblock.arg);
}
}
}
@@ -1663,12 +2662,12 @@ static void
close_invalidate_pair(int fds[2], const char *msg)
{
if (USE_EVENTFD && fds[0] == fds[1]) {
+ fds[1] = -1; // disable write port first
close_invalidate(&fds[0], msg);
- fds[1] = -1;
}
else {
- close_invalidate(&fds[0], msg);
close_invalidate(&fds[1], msg);
+ close_invalidate(&fds[0], msg);
}
}
@@ -1688,15 +2687,15 @@ set_nonblock(int fd)
}
/* communication pipe with timer thread and signal handler */
-static int
+static void
setup_communication_pipe_internal(int pipes[2])
{
int err;
- if (pipes[0] >= 0 || pipes[1] >= 0) {
- VM_ASSERT(pipes[0] >= 0);
- VM_ASSERT(pipes[1] >= 0);
- return 0;
+ if (pipes[0] > 0 || pipes[1] > 0) {
+ VM_ASSERT(pipes[0] > 0);
+ VM_ASSERT(pipes[1] > 0);
+ return;
}
/*
@@ -1705,23 +2704,21 @@ setup_communication_pipe_internal(int pipes[2])
*/
#if USE_EVENTFD && defined(EFD_NONBLOCK) && defined(EFD_CLOEXEC)
pipes[0] = pipes[1] = eventfd(0, EFD_NONBLOCK|EFD_CLOEXEC);
+
if (pipes[0] >= 0) {
rb_update_max_fd(pipes[0]);
- return 0;
+ return;
}
#endif
err = rb_cloexec_pipe(pipes);
if (err != 0) {
- rb_warn("pipe creation failed for timer: %s, scheduling broken",
- strerror(errno));
- return -1;
+ rb_bug("can not create communication pipe");
}
rb_update_max_fd(pipes[0]);
rb_update_max_fd(pipes[1]);
set_nonblock(pipes[0]);
set_nonblock(pipes[1]);
- return 0;
}
#if !defined(SET_CURRENT_THREAD_NAME) && defined(__linux__) && defined(PR_SET_NAME)
@@ -1804,12 +2801,18 @@ native_set_another_thread_name(rb_nativethread_id_t thread_id, VALUE name)
static VALUE
native_thread_native_thread_id(rb_thread_t *target_th)
{
+ if (!target_th->nt) return Qnil;
+
#ifdef RB_THREAD_T_HAS_NATIVE_ID
int tid = target_th->nt->tid;
if (tid == 0) return Qnil;
return INT2FIX(tid);
#elif defined(__APPLE__)
uint64_t tid;
+/* The first condition is needed because MAC_OS_X_VERSION_10_6
+ is not defined on 10.5, and while __POWERPC__ takes care of ppc/ppc64,
+ i386 will be broken without this. Note, 10.5 is supported with GCC upstream,
+ so it has C++17 and everything needed to build modern Ruby. */
# if (!defined(MAC_OS_X_VERSION_10_6) || \
(MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6) || \
defined(__POWERPC__) /* never defined for PowerPC platforms */)
@@ -1840,173 +2843,322 @@ native_thread_native_thread_id(rb_thread_t *target_th)
# define USE_NATIVE_THREAD_NATIVE_THREAD_ID 0
#endif
-static void
-ubf_timer_invalidate(void)
+static struct {
+ rb_serial_t created_fork_gen;
+ pthread_t pthread_id;
+
+ int comm_fds[2]; // r, w
+
+#if (HAVE_SYS_EPOLL_H || HAVE_SYS_EVENT_H) && USE_MN_THREADS
+ int event_fd; // kernel event queue fd (epoll/kqueue)
+#endif
+#if HAVE_SYS_EPOLL_H && USE_MN_THREADS
+#define EPOLL_EVENTS_MAX 0x10
+ struct epoll_event finished_events[EPOLL_EVENTS_MAX];
+#elif HAVE_SYS_EVENT_H && USE_MN_THREADS
+#define KQUEUE_EVENTS_MAX 0x10
+ struct kevent finished_events[KQUEUE_EVENTS_MAX];
+#endif
+
+ // waiting threads list
+ struct ccan_list_head waiting; // waiting threads in ractors
+ pthread_mutex_t waiting_lock;
+} timer_th = {
+ .created_fork_gen = 0,
+};
+
+#define TIMER_THREAD_CREATED_P() (timer_th.created_fork_gen == current_fork_gen)
+
+static void timer_thread_check_timeslice(rb_vm_t *vm);
+static int timer_thread_set_timeout(rb_vm_t *vm);
+static void timer_thread_wakeup_thread(rb_thread_t *th);
+
+#include "thread_pthread_mn.c"
+
+static int
+timer_thread_set_timeout(rb_vm_t *vm)
{
-#if UBF_TIMER == UBF_TIMER_PTHREAD
- CLOSE_INVALIDATE_PAIR(timer_pthread.low);
+#if 0
+ return 10; // ms
+#else
+ int timeout = -1;
+
+ ractor_sched_lock(vm, NULL);
+ {
+ if ( !ccan_list_empty(&vm->ractor.sched.timeslice_threads) // (1-1) Provide time slice for active NTs
+ || !ubf_threads_empty() // (1-3) Periodic UBF
+ || vm->ractor.sched.grq_cnt > 0 // (1-4) Lazy GRQ deq start
+ ) {
+
+ RUBY_DEBUG_LOG("timeslice:%d ubf:%d grq:%d",
+ !ccan_list_empty(&vm->ractor.sched.timeslice_threads),
+ !ubf_threads_empty(),
+ (vm->ractor.sched.grq_cnt > 0));
+
+ timeout = 10; // ms
+ vm->ractor.sched.timeslice_wait_inf = false;
+ }
+ else {
+ vm->ractor.sched.timeslice_wait_inf = true;
+ }
+ }
+ ractor_sched_unlock(vm, NULL);
+
+ if (vm->ractor.sched.timeslice_wait_inf) {
+ rb_native_mutex_lock(&timer_th.waiting_lock);
+ {
+ rb_thread_t *th = ccan_list_top(&timer_th.waiting, rb_thread_t, sched.waiting_reason.node);
+ if (th && (th->sched.waiting_reason.flags & thread_sched_waiting_timeout)) {
+ rb_hrtime_t now = rb_hrtime_now();
+ rb_hrtime_t hrrel = rb_hrtime_sub(th->sched.waiting_reason.data.timeout, now);
+
+ RUBY_DEBUG_LOG("th:%u now:%lu rel:%lu", rb_th_serial(th), (unsigned long)now, (unsigned long)hrrel);
+
+ // TODO: overflow?
+ timeout = (int)((hrrel + RB_HRTIME_PER_MSEC - 1) / RB_HRTIME_PER_MSEC); // ms
+ }
+ }
+ rb_native_mutex_unlock(&timer_th.waiting_lock);
+ }
+
+ RUBY_DEBUG_LOG("timeout:%d inf:%d", timeout, (int)vm->ractor.sched.timeslice_wait_inf);
+
+ // fprintf(stderr, "timeout:%d\n", timeout);
+ return timeout;
#endif
}
static void
-ubf_timer_pthread_create(rb_serial_t fork_gen)
+timer_thread_check_signal(rb_vm_t *vm)
{
-#if UBF_TIMER == UBF_TIMER_PTHREAD
- int err;
- if (timer_pthread.fork_gen == fork_gen)
- return;
+ // ruby_sigchld_handler(vm); TODO
- if (setup_communication_pipe_internal(timer_pthread.low) < 0)
- return;
+ int signum = rb_signal_buff_size();
+ if (UNLIKELY(signum > 0) && vm->ractor.main_thread) {
+ RUBY_DEBUG_LOG("signum:%d", signum);
+ threadptr_trap_interrupt(vm->ractor.main_thread);
+ }
+}
- err = pthread_create(&timer_pthread.thid, 0, timer_pthread_fn, GET_VM());
- if (!err)
- timer_pthread.fork_gen = fork_gen;
- else
- rb_warn("pthread_create failed for timer: %s, signals racy",
- strerror(err));
-#endif
+static bool
+timer_thread_check_exceed(rb_hrtime_t abs, rb_hrtime_t now)
+{
+ if (abs < now) {
+ return true;
+ }
+ else if (abs - now < RB_HRTIME_PER_MSEC) {
+ return true; // too short time
+ }
+ else {
+ return false;
+ }
}
-static void
-ubf_timer_create(rb_serial_t fork_gen)
+static rb_thread_t *
+timer_thread_deq_wakeup(rb_vm_t *vm, rb_hrtime_t now)
{
-#if UBF_TIMER == UBF_TIMER_POSIX
-# if defined(__sun)
-# define UBF_TIMER_CLOCK CLOCK_REALTIME
-# else /* Tested Linux and FreeBSD: */
-# define UBF_TIMER_CLOCK CLOCK_MONOTONIC
-# endif
+ rb_thread_t *th = ccan_list_top(&timer_th.waiting, rb_thread_t, sched.waiting_reason.node);
- struct sigevent sev;
+ if (th != NULL &&
+ (th->sched.waiting_reason.flags & thread_sched_waiting_timeout) &&
+ timer_thread_check_exceed(th->sched.waiting_reason.data.timeout, now)) {
- sev.sigev_notify = SIGEV_SIGNAL;
- sev.sigev_signo = SIGVTALRM;
- sev.sigev_value.sival_ptr = &timer_posix;
+ RUBY_DEBUG_LOG("wakeup th:%u", rb_th_serial(th));
- if (!timer_create(UBF_TIMER_CLOCK, &sev, &timer_posix.timerid)) {
- rb_atomic_t prev = timer_state_exchange(RTIMER_DISARM);
+ // delete from waiting list
+ ccan_list_del_init(&th->sched.waiting_reason.node);
- if (prev != RTIMER_DEAD) {
- rb_bug("timer_posix was not dead: %u", (unsigned)prev);
- }
- timer_posix.fork_gen = fork_gen;
- }
- else {
- rb_warn("timer_create failed: %s, signals racy", strerror(errno));
+ // setup result
+ th->sched.waiting_reason.flags = thread_sched_waiting_none;
+ th->sched.waiting_reason.data.result = 0;
+
+ return th;
}
-#endif
- if (UBF_TIMER == UBF_TIMER_PTHREAD)
- ubf_timer_pthread_create(fork_gen);
+
+ return NULL;
}
static void
-rb_thread_create_timer_thread(void)
+timer_thread_wakeup_thread(rb_thread_t *th)
{
- /* we only create the pipe, and lazy-spawn */
- rb_serial_t fork_gen = signal_self_pipe.fork_gen;
+ RUBY_DEBUG_LOG("th:%u", rb_th_serial(th));
+ struct rb_thread_sched *sched = TH_SCHED(th);
- if (fork_gen && fork_gen != current_fork_gen) {
- CLOSE_INVALIDATE_PAIR(signal_self_pipe.normal);
- CLOSE_INVALIDATE_PAIR(signal_self_pipe.ub_main);
- ubf_timer_invalidate();
+ thread_sched_lock(sched, th);
+ {
+ if (sched->running != th) {
+ thread_sched_to_ready_common(sched, th, true, false);
+ }
+ else {
+ // will be release the execution right
+ }
}
+ thread_sched_unlock(sched, th);
+}
- if (setup_communication_pipe_internal(signal_self_pipe.normal) < 0) return;
- if (setup_communication_pipe_internal(signal_self_pipe.ub_main) < 0) return;
+static void
+timer_thread_check_timeout(rb_vm_t *vm)
+{
+ rb_hrtime_t now = rb_hrtime_now();
+ rb_thread_t *th;
- ubf_timer_create(current_fork_gen);
- if (fork_gen != current_fork_gen) {
- /* validate pipe on this process */
- sigwait_th = THREAD_INVALID;
- signal_self_pipe.fork_gen = current_fork_gen;
+ rb_native_mutex_lock(&timer_th.waiting_lock);
+ {
+ while ((th = timer_thread_deq_wakeup(vm, now)) != NULL) {
+ timer_thread_wakeup_thread(th);
+ }
}
+ rb_native_mutex_unlock(&timer_th.waiting_lock);
}
static void
-ubf_timer_disarm(void)
+timer_thread_check_timeslice(rb_vm_t *vm)
{
-#if UBF_TIMER == UBF_TIMER_POSIX
- rb_atomic_t prev;
+ // TODO: check time
+ rb_thread_t *th;
+ ccan_list_for_each(&vm->ractor.sched.timeslice_threads, th, sched.node.timeslice_threads) {
+ RUBY_DEBUG_LOG("timeslice th:%u", rb_th_serial(th));
+ RUBY_VM_SET_TIMER_INTERRUPT(th->ec);
+ }
+}
- if (timer_posix.fork_gen && timer_posix.fork_gen != current_fork_gen) return;
- prev = timer_state_cas(RTIMER_ARMED, RTIMER_DISARM);
- switch (prev) {
- case RTIMER_DISARM: return; /* likely */
- case RTIMER_ARMING: return; /* ubf_timer_arm will disarm itself */
- case RTIMER_ARMED:
- if (timer_settime(timer_posix.timerid, 0, &zero, 0)) {
- int err = errno;
+void
+rb_assert_sig(void)
+{
+ sigset_t oldmask;
+ pthread_sigmask(0, NULL, &oldmask);
+ if (sigismember(&oldmask, SIGVTALRM)) {
+ rb_bug("!!!");
+ }
+ else {
+ RUBY_DEBUG_LOG("ok");
+ }
+}
- if (err == EINVAL) {
- prev = timer_state_cas(RTIMER_DISARM, RTIMER_DISARM);
+static void *
+timer_thread_func(void *ptr)
+{
+ rb_vm_t *vm = (rb_vm_t *)ptr;
+#if defined(RUBY_NT_SERIAL)
+ ruby_nt_serial = (rb_atomic_t)-1;
+#endif
- /* main thread may have killed the timer */
- if (prev == RTIMER_DEAD) return;
+ RUBY_DEBUG_LOG("started%s", "");
- rb_bug_errno("timer_settime (disarm)", err);
- }
- }
- return;
- case RTIMER_DEAD: return; /* stay dead */
- default:
- rb_bug("UBF_TIMER_POSIX bad state: %u", (unsigned)prev);
+ while (system_working) {
+ timer_thread_check_signal(vm);
+ timer_thread_check_timeout(vm);
+ ubf_wakeup_all_threads();
+
+ RUBY_DEBUG_LOG("system_working:%d", system_working);
+ timer_thread_polling(vm);
}
-#elif UBF_TIMER == UBF_TIMER_PTHREAD
- ATOMIC_SET(timer_pthread.armed, 0);
-#endif
+ RUBY_DEBUG_LOG("terminated");
+ return NULL;
}
+/* only use signal-safe system calls here */
static void
-ubf_timer_destroy(void)
+signal_communication_pipe(int fd)
{
-#if UBF_TIMER == UBF_TIMER_POSIX
- if (timer_posix.fork_gen == current_fork_gen) {
- rb_atomic_t expect = RTIMER_DISARM;
- size_t i, max = 10000000;
+#if USE_EVENTFD
+ const uint64_t buff = 1;
+#else
+ const char buff = '!';
+#endif
+ ssize_t result;
- /* prevent signal handler from arming: */
- for (i = 0; i < max; i++) {
- switch (timer_state_cas(expect, RTIMER_DEAD)) {
- case RTIMER_DISARM:
- if (expect == RTIMER_DISARM) goto done;
- expect = RTIMER_DISARM;
- break;
- case RTIMER_ARMING:
- native_thread_yield(); /* let another thread finish arming */
- expect = RTIMER_ARMED;
- break;
- case RTIMER_ARMED:
- if (expect == RTIMER_ARMED) {
- if (timer_settime(timer_posix.timerid, 0, &zero, 0))
- rb_bug_errno("timer_settime (destroy)", errno);
- goto done;
- }
- expect = RTIMER_ARMED;
+ /* already opened */
+ if (fd >= 0) {
+ retry:
+ if ((result = write(fd, &buff, sizeof(buff))) <= 0) {
+ int e = errno;
+ switch (e) {
+ case EINTR: goto retry;
+ case EAGAIN:
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
break;
- case RTIMER_DEAD:
- rb_bug("RTIMER_DEAD unexpected");
+ default:
+ async_bug_fd("rb_thread_wakeup_timer_thread: write", e, fd);
}
}
- rb_bug("timed out waiting for timer to arm");
-done:
- if (timer_delete(timer_posix.timerid) < 0)
- rb_sys_fail("timer_delete");
+ if (TT_DEBUG) WRITE_CONST(2, "rb_thread_wakeup_timer_thread: write\n");
+ }
+ else {
+ // ignore wakeup
+ }
+}
- VM_ASSERT(timer_state_exchange(RTIMER_DEAD) == RTIMER_DEAD);
+static void
+timer_thread_wakeup_force(void)
+{
+ // should not use RUBY_DEBUG_LOG() because it can be called within signal handlers.
+ signal_communication_pipe(timer_th.comm_fds[1]);
+}
+
+static void
+timer_thread_wakeup_locked(rb_vm_t *vm)
+{
+ // should be locked before.
+ ASSERT_ractor_sched_locked(vm, NULL);
+
+ if (timer_th.created_fork_gen == current_fork_gen) {
+ if (vm->ractor.sched.timeslice_wait_inf) {
+ RUBY_DEBUG_LOG("wakeup with fd:%d", timer_th.comm_fds[1]);
+ timer_thread_wakeup_force();
+ }
+ else {
+ RUBY_DEBUG_LOG("will be wakeup...");
+ }
}
-#elif UBF_TIMER == UBF_TIMER_PTHREAD
- int err;
+}
- timer_pthread.fork_gen = 0;
- ubf_timer_disarm();
- rb_thread_wakeup_timer_thread_fd(timer_pthread.low[1]);
- err = pthread_join(timer_pthread.thid, 0);
- if (err) {
- rb_raise(rb_eThreadError, "native_thread_join() failed (%d)", err);
+static void
+timer_thread_wakeup(void)
+{
+ rb_vm_t *vm = GET_VM();
+
+ ractor_sched_lock(vm, NULL);
+ {
+ timer_thread_wakeup_locked(vm);
}
+ ractor_sched_unlock(vm, NULL);
+}
+
+static void
+rb_thread_create_timer_thread(void)
+{
+ rb_serial_t created_fork_gen = timer_th.created_fork_gen;
+
+ RUBY_DEBUG_LOG("fork_gen create:%d current:%d", (int)created_fork_gen, (int)current_fork_gen);
+
+ timer_th.created_fork_gen = current_fork_gen;
+
+ if (created_fork_gen != current_fork_gen) {
+ if (created_fork_gen != 0) {
+ RUBY_DEBUG_LOG("forked child process");
+
+ CLOSE_INVALIDATE_PAIR(timer_th.comm_fds);
+#if HAVE_SYS_EPOLL_H && USE_MN_THREADS
+ close_invalidate(&timer_th.event_fd, "close event_fd");
#endif
+ rb_native_mutex_destroy(&timer_th.waiting_lock);
+ }
+
+ ccan_list_head_init(&timer_th.waiting);
+ rb_native_mutex_initialize(&timer_th.waiting_lock);
+
+ // open communication channel
+ setup_communication_pipe_internal(timer_th.comm_fds);
+
+ // open event fd
+ timer_thread_setup_mn();
+ }
+
+ pthread_create(&timer_th.pthread_id, NULL, timer_thread_func, GET_VM());
}
static int
@@ -2014,8 +3166,13 @@ native_stop_timer_thread(void)
{
int stopped;
stopped = --system_working <= 0;
- if (stopped)
- ubf_timer_destroy();
+
+ if (stopped) {
+ RUBY_DEBUG_LOG("wakeup send %d", timer_th.comm_fds[1]);
+ timer_thread_wakeup_force();
+ RUBY_DEBUG_LOG("wakeup sent");
+ pthread_join(timer_th.pthread_id, NULL);
+ }
if (TT_DEBUG) fprintf(stderr, "stop timer thread\n");
return stopped;
@@ -2024,7 +3181,7 @@ native_stop_timer_thread(void)
static void
native_reset_timer_thread(void)
{
- if (TT_DEBUG) fprintf(stderr, "reset timer thread\n");
+ //
}
#ifdef HAVE_SIGALTSTACK
@@ -2075,22 +3232,26 @@ int
rb_reserved_fd_p(int fd)
{
/* no false-positive if out-of-FD at startup */
- if (fd < 0)
- return 0;
+ if (fd < 0) return 0;
-#if UBF_TIMER == UBF_TIMER_PTHREAD
- if (fd == timer_pthread.low[0] || fd == timer_pthread.low[1])
- goto check_fork_gen;
+ if (fd == timer_th.comm_fds[0] ||
+ fd == timer_th.comm_fds[1]
+#if (HAVE_SYS_EPOLL_H || HAVE_SYS_EVENT_H) && USE_MN_THREADS
+ || fd == timer_th.event_fd
#endif
- if (fd == signal_self_pipe.normal[0] || fd == signal_self_pipe.normal[1])
- goto check_fork_gen;
- if (fd == signal_self_pipe.ub_main[0] || fd == signal_self_pipe.ub_main[1])
+ ) {
goto check_fork_gen;
+ }
return 0;
-check_fork_gen:
- if (signal_self_pipe.fork_gen == current_fork_gen) /* async-signal-safe */
+
+ check_fork_gen:
+ if (timer_th.created_fork_gen == current_fork_gen) {
+ /* async-signal-safe */
return 1;
- return 0;
+ }
+ else {
+ return 0;
+ }
}
rb_nativethread_id_t
@@ -2099,35 +3260,7 @@ rb_nativethread_self(void)
return pthread_self();
}
-int
-rb_sigwait_fd_get(const rb_thread_t *th)
-{
- if (signal_self_pipe.normal[0] >= 0) {
- VM_ASSERT(signal_self_pipe.fork_gen == current_fork_gen);
- /*
- * no need to keep firing the timer if any thread is sleeping
- * on the signal self-pipe
- */
- ubf_timer_disarm();
-
- if (ATOMIC_PTR_CAS(sigwait_th, THREAD_INVALID, th) == THREAD_INVALID) {
- return signal_self_pipe.normal[0];
- }
- }
- return -1; /* avoid thundering herd and work stealing/starvation */
-}
-
-void
-rb_sigwait_fd_put(const rb_thread_t *th, int fd)
-{
- const rb_thread_t *old;
-
- VM_ASSERT(signal_self_pipe.normal[0] == fd);
- old = ATOMIC_PTR_EXCHANGE(sigwait_th, THREAD_INVALID);
- if (old != th) assert(old == th);
-}
-
-#ifndef HAVE_PPOLL
+#if defined(USE_POLL) && !defined(HAVE_PPOLL)
/* TODO: don't ignore sigmask */
static int
ruby_ppoll(struct pollfd *fds, nfds_t nfds,
@@ -2158,61 +3291,6 @@ ruby_ppoll(struct pollfd *fds, nfds_t nfds,
# define ppoll(fds,nfds,ts,sigmask) ruby_ppoll((fds),(nfds),(ts),(sigmask))
#endif
-void
-rb_sigwait_sleep(rb_thread_t *th, int sigwait_fd, const rb_hrtime_t *rel)
-{
- struct pollfd pfd;
- struct timespec ts;
-
- pfd.fd = sigwait_fd;
- pfd.events = POLLIN;
-
- if (!BUSY_WAIT_SIGNALS && ubf_threads_empty()) {
- (void)ppoll(&pfd, 1, rb_hrtime2timespec(&ts, rel), 0);
- check_signals_nogvl(th, sigwait_fd);
- }
- else {
- rb_hrtime_t to = RB_HRTIME_MAX, end = 0;
- int n = 0;
-
- if (rel) {
- to = *rel;
- end = rb_hrtime_add(rb_hrtime_now(), to);
- }
- /*
- * tricky: this needs to return on spurious wakeup (no auto-retry).
- * But we also need to distinguish between periodic quantum
- * wakeups, so we care about the result of consume_communication_pipe
- *
- * We want to avoid spurious wakeup for Mutex#sleep compatibility
- * [ruby-core:88102]
- */
- for (;;) {
- const rb_hrtime_t *sto = sigwait_timeout(th, sigwait_fd, &to, &n);
-
- if (n) return;
- n = ppoll(&pfd, 1, rb_hrtime2timespec(&ts, sto), 0);
- if (check_signals_nogvl(th, sigwait_fd))
- return;
- if (n || (th && RUBY_VM_INTERRUPTED(th->ec)))
- return;
- if (rel && hrtime_update_expire(&to, end))
- return;
- }
- }
-}
-
-/*
- * we need to guarantee wakeups from native_ppoll_sleep because
- * ubf_select may not be going through ubf_list if other threads
- * are all sleeping.
- */
-static void
-ubf_ppoll_sleep(void *ignore)
-{
- rb_thread_wakeup_timer_thread_fd(signal_self_pipe.ub_main[1]);
-}
-
/*
* Single CPU setups benefit from explicit sched_yield() before ppoll(),
* since threads may be too starved to enter the GVL waitqueue for
@@ -2224,153 +3302,36 @@ ubf_ppoll_sleep(void *ignore)
* [ruby-core:90417] [Bug #15398]
*/
#define THREAD_BLOCKING_YIELD(th) do { \
- const rb_thread_t *next; \
+ const rb_thread_t *next_th; \
struct rb_thread_sched *sched = TH_SCHED(th); \
RB_VM_SAVE_MACHINE_CONTEXT(th); \
- rb_native_mutex_lock(&sched->lock); \
- next = thread_sched_to_waiting_common((sched), (th)); \
- rb_native_mutex_unlock(&sched->lock); \
- if (!next && rb_ractor_living_thread_num(th->ractor) > 1) { \
+ thread_sched_to_waiting(sched, (th)); \
+ next_th = sched->running; \
+ rb_native_mutex_unlock(&sched->lock_); \
+ native_thread_yield(); /* TODO: needed? */ \
+ if (!next_th && rb_ractor_living_thread_num(th->ractor) > 1) { \
native_thread_yield(); \
}
-/*
- * This function does not exclusively acquire sigwait_fd, so it
- * cannot safely read from it. However, it can be woken up in
- * 4 ways:
- *
- * 1) ubf_ppoll_sleep (from another thread)
- * 2) rb_thread_wakeup_timer_thread (from signal handler)
- * 3) any unmasked signal hitting the process
- * 4) periodic ubf timer wakeups (after 3)
- */
-static void
-native_ppoll_sleep(rb_thread_t *th, rb_hrtime_t *rel)
-{
- rb_native_mutex_lock(&th->interrupt_lock);
- th->unblock.func = ubf_ppoll_sleep;
- rb_native_mutex_unlock(&th->interrupt_lock);
-
- THREAD_BLOCKING_YIELD(th);
- {
- if (!RUBY_VM_INTERRUPTED(th->ec)) {
- struct pollfd pfd[2];
- struct timespec ts;
-
- pfd[0].fd = signal_self_pipe.normal[0]; /* sigwait_fd */
- pfd[1].fd = signal_self_pipe.ub_main[0];
- pfd[0].events = pfd[1].events = POLLIN;
- if (ppoll(pfd, 2, rb_hrtime2timespec(&ts, rel), 0) > 0) {
- if (pfd[1].revents & POLLIN) {
- (void)consume_communication_pipe(pfd[1].fd);
- }
- }
- /*
- * do not read the sigwait_fd, here, let uplevel callers
- * or other threads that, otherwise we may steal and starve
- * other threads
- */
- }
- unblock_function_clear(th);
- }
- THREAD_BLOCKING_END(th);
-}
-
static void
native_sleep(rb_thread_t *th, rb_hrtime_t *rel)
{
- int sigwait_fd = rb_sigwait_fd_get(th);
- rb_ractor_blocking_threads_inc(th->ractor, __FILE__, __LINE__);
-
- RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_SUSPENDED);
-
- if (sigwait_fd >= 0) {
- rb_native_mutex_lock(&th->interrupt_lock);
- th->unblock.func = ubf_sigwait;
- rb_native_mutex_unlock(&th->interrupt_lock);
+ struct rb_thread_sched *sched = TH_SCHED(th);
- THREAD_BLOCKING_YIELD(th);
- {
- if (!RUBY_VM_INTERRUPTED(th->ec)) {
- rb_sigwait_sleep(th, sigwait_fd, rel);
- }
- else {
- check_signals_nogvl(th, sigwait_fd);
- }
- unblock_function_clear(th);
+ RUBY_DEBUG_LOG("rel:%d", rel ? (int)*rel : 0);
+ if (rel) {
+ if (th_has_dedicated_nt(th)) {
+ native_cond_sleep(th, rel);
+ }
+ else {
+ thread_sched_wait_events(sched, th, -1, thread_sched_waiting_timeout, rel);
}
- THREAD_BLOCKING_END(th);
-
- rb_sigwait_fd_put(th, sigwait_fd);
- }
- else if (th == th->vm->ractor.main_thread) { /* always able to handle signals */
- native_ppoll_sleep(th, rel);
}
else {
- native_cond_sleep(th, rel);
+ thread_sched_to_waiting_until_wakeup(sched, th);
}
- rb_ractor_blocking_threads_dec(th->ractor, __FILE__, __LINE__);
-}
-
-#if UBF_TIMER == UBF_TIMER_PTHREAD
-static void *
-timer_pthread_fn(void *p)
-{
- rb_vm_t *vm = p;
- pthread_t main_thread_id = vm->ractor.main_thread->nt->thread_id;
- struct pollfd pfd;
- int timeout = -1;
- int ccp;
-
- pfd.fd = timer_pthread.low[0];
- pfd.events = POLLIN;
-
- while (system_working > 0) {
- (void)poll(&pfd, 1, timeout);
- ccp = consume_communication_pipe(pfd.fd);
-
- if (system_working > 0) {
- if (ATOMIC_CAS(timer_pthread.armed, 1, 1)) {
- pthread_kill(main_thread_id, SIGVTALRM);
-
- if (rb_signal_buff_size() || !ubf_threads_empty()) {
- timeout = TIME_QUANTUM_MSEC;
- }
- else {
- ATOMIC_SET(timer_pthread.armed, 0);
- timeout = -1;
- }
- }
- else if (ccp) {
- pthread_kill(main_thread_id, SIGVTALRM);
- ATOMIC_SET(timer_pthread.armed, 0);
- timeout = -1;
- }
- }
- }
-
- return 0;
-}
-#endif /* UBF_TIMER_PTHREAD */
-
-static VALUE
-ubf_caller(void *ignore)
-{
- rb_thread_sleep_forever();
-
- return Qfalse;
-}
-
-/*
- * Called if and only if one thread is running, and
- * the unblock function is NOT async-signal-safe
- * This assumes USE_THREAD_CACHE is true for performance reasons
- */
-static VALUE
-rb_thread_start_unblock_thread(void)
-{
- return rb_thread_create(ubf_caller, 0);
+ RUBY_DEBUG_LOG("wakeup");
}
// thread internal event hooks (only for pthread)
@@ -2444,7 +3405,7 @@ rb_internal_thread_remove_event_hook(rb_internal_thread_event_hook_t * hook)
}
static void
-rb_thread_execute_hooks(rb_event_flag_t event)
+rb_thread_execute_hooks(rb_event_flag_t event, rb_thread_t *th)
{
int r;
if ((r = pthread_rwlock_rdlock(&rb_internal_thread_event_hooks_rw_lock))) {
@@ -2455,7 +3416,10 @@ rb_thread_execute_hooks(rb_event_flag_t event)
rb_internal_thread_event_hook_t *h = rb_internal_thread_event_hooks;
do {
if (h->event & event) {
- (*h->callback)(event, NULL, h->user_data);
+ rb_internal_thread_event_data_t event_data = {
+ .thread = th->self,
+ };
+ (*h->callback)(event, &event_data, h->user_data);
}
} while((h = h->next));
}
@@ -2464,4 +3428,16 @@ rb_thread_execute_hooks(rb_event_flag_t event)
}
}
+// return true if the current thread acquires DNT.
+// return false if the current thread already acquires DNT.
+bool
+rb_thread_lock_native_thread(void)
+{
+ rb_thread_t *th = GET_THREAD();
+ bool is_snt = th->nt->dedicated == 0;
+ native_thread_dedicated_inc(th->vm, th->ractor, th->nt);
+
+ return is_snt;
+}
+
#endif /* THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION */
diff --git a/thread_pthread.h b/thread_pthread.h
index bf97c7a0ee..20c4b9f9a8 100644
--- a/thread_pthread.h
+++ b/thread_pthread.h
@@ -19,14 +19,59 @@
// per-Thead scheduler helper data
struct rb_thread_sched_item {
- union {
+ struct {
struct ccan_list_node ubf;
- struct ccan_list_node readyq; // protected by sched->lock
+
+ // connected to ractor->threads.sched.reqdyq
+ // locked by ractor->threads.sched.lock
+ struct ccan_list_node readyq;
+
+ // connected to vm->ractor.sched.timeslice_threads
+ // locked by vm->ractor.sched.lock
+ struct ccan_list_node timeslice_threads;
+
+ // connected to vm->ractor.sched.running_threads
+ // locked by vm->ractor.sched.lock
+ struct ccan_list_node running_threads;
+
+ // connected to vm->ractor.sched.zombie_threads
+ struct ccan_list_node zombie_threads;
} node;
+
+ // this data should be protected by timer_th.waiting_lock
+ struct {
+ enum thread_sched_waiting_flag {
+ thread_sched_waiting_none = 0x00,
+ thread_sched_waiting_timeout = 0x01,
+ thread_sched_waiting_io_read = 0x02,
+ thread_sched_waiting_io_write = 0x08,
+ thread_sched_waiting_io_force = 0x40, // ignore readable
+ } flags;
+
+ struct {
+ // should be compat with hrtime.h
+#ifdef MY_RUBY_BUILD_MAY_TIME_TRAVEL
+ int128_t timeout;
+#else
+ uint64_t timeout;
+#endif
+ int fd; // -1 for timeout only
+ int result;
+ } data;
+
+ // connected to timer_th.waiting
+ struct ccan_list_node node;
+ } waiting_reason;
+
+ bool finished;
+ bool malloc_stack;
+ void *context_stack;
+ struct coroutine_context *context;
};
struct rb_native_thread {
rb_atomic_t serial;
+ struct rb_vm_struct *vm;
rb_nativethread_id_t thread_id;
@@ -54,6 +99,11 @@ struct rb_native_thread {
#ifdef USE_SIGALTSTACK
void *altstack;
#endif
+
+ struct coroutine_context *nt_context;
+ int dedicated;
+
+ size_t machine_stack_maxsize;
};
#undef except
@@ -63,39 +113,28 @@ struct rb_native_thread {
// per-Ractor
struct rb_thread_sched {
- /* fast path */
-
- const struct rb_thread_struct *running; // running thread or NULL
- rb_nativethread_lock_t lock;
+ rb_nativethread_lock_t lock_;
+#if VM_CHECK_MODE
+ struct rb_thread_struct *lock_owner;
+#endif
+ struct rb_thread_struct *running; // running thread or NULL
+ bool is_running;
+ bool is_running_timeslice;
+ bool enable_mn_threads;
- /*
- * slow path, protected by ractor->thread_sched->lock
- * - @readyq - FIFO queue of threads waiting for running
- * - @timer - it handles timeslices for @current. It is any one thread
- * in @waitq, there is no @timer if @waitq is empty, but always
- * a @timer if @waitq has entries
- * - @timer_err tracks timeslice limit, the timeslice only resets
- * when pthread_cond_timedwait returns ETIMEDOUT, so frequent
- * switching between contended/uncontended GVL won't reset the
- * timer.
- */
struct ccan_list_head readyq;
- const struct rb_thread_struct *timer;
- int timer_err;
-
- /* yield */
- rb_nativethread_cond_t switch_cond;
- rb_nativethread_cond_t switch_wait_cond;
- int need_yield;
- int wait_yield;
+ int readyq_cnt;
+ // ractor scheduling
+ struct ccan_list_node grq_node;
};
-RUBY_SYMBOL_EXPORT_BEGIN
#ifdef RB_THREAD_LOCAL_SPECIFIER
+ NOINLINE(void rb_current_ec_set(struct rb_execution_context_struct *));
+ NOINLINE(struct rb_execution_context_struct *rb_current_ec_noinline(void));
+
# ifdef __APPLE__
// on Darwin, TLS can not be accessed across .so
- struct rb_execution_context_struct *rb_current_ec(void);
- void rb_current_ec_set(struct rb_execution_context_struct *);
+ NOINLINE(struct rb_execution_context_struct *rb_current_ec(void));
# else
RUBY_EXTERN RB_THREAD_LOCAL_SPECIFIER struct rb_execution_context_struct *ruby_current_ec;
@@ -123,6 +162,5 @@ native_tls_set(native_tls_key_t key, void *ptr)
RUBY_EXTERN native_tls_key_t ruby_current_ec_key;
#endif
-RUBY_SYMBOL_EXPORT_END
#endif /* RUBY_THREAD_PTHREAD_H */
diff --git a/thread_pthread_mn.c b/thread_pthread_mn.c
new file mode 100644
index 0000000000..b605d6a751
--- /dev/null
+++ b/thread_pthread_mn.c
@@ -0,0 +1,1061 @@
+// included by "thread_pthread.c"
+
+#if USE_MN_THREADS
+
+static void timer_thread_unregister_waiting(rb_thread_t *th, int fd, enum thread_sched_waiting_flag flags);
+
+static bool
+timer_thread_cancel_waiting(rb_thread_t *th)
+{
+ bool canceled = false;
+
+ if (th->sched.waiting_reason.flags) {
+ rb_native_mutex_lock(&timer_th.waiting_lock);
+ {
+ if (th->sched.waiting_reason.flags) {
+ canceled = true;
+ ccan_list_del_init(&th->sched.waiting_reason.node);
+ if (th->sched.waiting_reason.flags & (thread_sched_waiting_io_read | thread_sched_waiting_io_write)) {
+ timer_thread_unregister_waiting(th, th->sched.waiting_reason.data.fd, th->sched.waiting_reason.flags);
+ }
+ th->sched.waiting_reason.flags = thread_sched_waiting_none;
+ }
+ }
+ rb_native_mutex_unlock(&timer_th.waiting_lock);
+ }
+
+ return canceled;
+}
+
+static void
+ubf_event_waiting(void *ptr)
+{
+ rb_thread_t *th = (rb_thread_t *)ptr;
+ struct rb_thread_sched *sched = TH_SCHED(th);
+
+ RUBY_DEBUG_LOG("th:%u", rb_th_serial(th));
+
+ VM_ASSERT(th->nt == NULL || !th_has_dedicated_nt(th));
+
+ // only once. it is safe because th->interrupt_lock is already acquired.
+ th->unblock.func = NULL;
+ th->unblock.arg = NULL;
+
+ bool canceled = timer_thread_cancel_waiting(th);
+
+ thread_sched_lock(sched, th);
+ {
+ if (sched->running == th) {
+ RUBY_DEBUG_LOG("not waiting yet");
+ }
+ else if (canceled) {
+ thread_sched_to_ready_common(sched, th, true, false);
+ }
+ else {
+ RUBY_DEBUG_LOG("already not waiting");
+ }
+ }
+ thread_sched_unlock(sched, th);
+}
+
+static bool timer_thread_register_waiting(rb_thread_t *th, int fd, enum thread_sched_waiting_flag flags, rb_hrtime_t *rel);
+
+// return true if timed out
+static bool
+thread_sched_wait_events(struct rb_thread_sched *sched, rb_thread_t *th, int fd, enum thread_sched_waiting_flag events, rb_hrtime_t *rel)
+{
+ VM_ASSERT(!th_has_dedicated_nt(th)); // on SNT
+
+ volatile bool timedout = false, need_cancel = false;
+
+ if (timer_thread_register_waiting(th, fd, events, rel)) {
+ RUBY_DEBUG_LOG("wait fd:%d", fd);
+
+ RB_VM_SAVE_MACHINE_CONTEXT(th);
+ setup_ubf(th, ubf_event_waiting, (void *)th);
+
+ RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_SUSPENDED, th);
+
+ thread_sched_lock(sched, th);
+ {
+ if (th->sched.waiting_reason.flags == thread_sched_waiting_none) {
+ // already awaken
+ }
+ else if (RUBY_VM_INTERRUPTED(th->ec)) {
+ need_cancel = true;
+ }
+ else {
+ RUBY_DEBUG_LOG("sleep");
+
+ th->status = THREAD_STOPPED_FOREVER;
+ thread_sched_wakeup_next_thread(sched, th, true);
+ thread_sched_wait_running_turn(sched, th, true);
+
+ RUBY_DEBUG_LOG("wakeup");
+ }
+
+ timedout = th->sched.waiting_reason.data.result == 0;
+ }
+ thread_sched_unlock(sched, th);
+
+ if (need_cancel) {
+ timer_thread_cancel_waiting(th);
+ }
+
+ setup_ubf(th, NULL, NULL); // TODO: maybe it is already NULL?
+
+ th->status = THREAD_RUNNABLE;
+ }
+ else {
+ RUBY_DEBUG_LOG("can not wait fd:%d", fd);
+ return false;
+ }
+
+ VM_ASSERT(sched->running == th);
+
+ return timedout;
+}
+
+/// stack management
+
+static int
+get_sysconf_page_size(void)
+{
+ static long page_size = 0;
+
+ if (UNLIKELY(page_size == 0)) {
+ page_size = sysconf(_SC_PAGESIZE);
+ VM_ASSERT(page_size < INT_MAX);
+ }
+ return (int)page_size;
+}
+
+#define MSTACK_CHUNK_SIZE (512 * 1024 * 1024) // 512MB
+#define MSTACK_PAGE_SIZE get_sysconf_page_size()
+#define MSTACK_CHUNK_PAGE_NUM (MSTACK_CHUNK_SIZE / MSTACK_PAGE_SIZE - 1) // 1 is start redzone
+
+// 512MB chunk
+// 131,072 pages (> 65,536)
+// 0th page is Redzone. Start from 1st page.
+
+/*
+ * <--> machine stack + vm stack
+ * ----------------------------------
+ * |HD...|RZ| ... |RZ| ... ... |RZ|
+ * <------------- 512MB ------------->
+ */
+
+static struct nt_stack_chunk_header {
+ struct nt_stack_chunk_header *prev_chunk;
+ struct nt_stack_chunk_header *prev_free_chunk;
+
+ uint16_t start_page;
+ uint16_t stack_count;
+ uint16_t uninitialized_stack_count;
+
+ uint16_t free_stack_pos;
+ uint16_t free_stack[];
+} *nt_stack_chunks = NULL,
+ *nt_free_stack_chunks = NULL;
+
+struct nt_machine_stack_footer {
+ struct nt_stack_chunk_header *ch;
+ size_t index;
+};
+
+static rb_nativethread_lock_t nt_machine_stack_lock = RB_NATIVETHREAD_LOCK_INIT;
+
+#include <sys/mman.h>
+
+// vm_stack_size + machine_stack_size + 1 * (guard page size)
+static inline size_t
+nt_thread_stack_size(void)
+{
+ static size_t msz;
+ if (LIKELY(msz > 0)) return msz;
+
+ rb_vm_t *vm = GET_VM();
+ int sz = (int)(vm->default_params.thread_vm_stack_size + vm->default_params.thread_machine_stack_size + MSTACK_PAGE_SIZE);
+ int page_num = roomof(sz, MSTACK_PAGE_SIZE);
+ msz = (size_t)page_num * MSTACK_PAGE_SIZE;
+ return msz;
+}
+
+static struct nt_stack_chunk_header *
+nt_alloc_thread_stack_chunk(void)
+{
+ int mmap_flags = MAP_ANONYMOUS | MAP_PRIVATE;
+#if defined(MAP_STACK) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__)
+ mmap_flags |= MAP_STACK;
+#endif
+
+ const char *m = (void *)mmap(NULL, MSTACK_CHUNK_SIZE, PROT_READ | PROT_WRITE, mmap_flags, -1, 0);
+ if (m == MAP_FAILED) {
+ return NULL;
+ }
+
+ size_t msz = nt_thread_stack_size();
+ int header_page_cnt = 1;
+ int stack_count = ((MSTACK_CHUNK_PAGE_NUM - header_page_cnt) * MSTACK_PAGE_SIZE) / msz;
+ int ch_size = sizeof(struct nt_stack_chunk_header) + sizeof(uint16_t) * stack_count;
+
+ if (ch_size > MSTACK_PAGE_SIZE * header_page_cnt) {
+ header_page_cnt = (ch_size + MSTACK_PAGE_SIZE - 1) / MSTACK_PAGE_SIZE;
+ stack_count = ((MSTACK_CHUNK_PAGE_NUM - header_page_cnt) * MSTACK_PAGE_SIZE) / msz;
+ }
+
+ VM_ASSERT(stack_count <= UINT16_MAX);
+
+ struct nt_stack_chunk_header *ch = (struct nt_stack_chunk_header *)m;
+
+ ch->start_page = header_page_cnt;
+ ch->prev_chunk = nt_stack_chunks;
+ ch->prev_free_chunk = nt_free_stack_chunks;
+ ch->uninitialized_stack_count = ch->stack_count = (uint16_t)stack_count;
+ ch->free_stack_pos = 0;
+
+ RUBY_DEBUG_LOG("ch:%p start_page:%d stack_cnt:%d stack_size:%d", ch, (int)ch->start_page, (int)ch->stack_count, (int)msz);
+
+ return ch;
+}
+
+static void *
+nt_stack_chunk_get_stack_start(struct nt_stack_chunk_header *ch, size_t idx)
+{
+ const char *m = (char *)ch;
+ return (void *)(m + ch->start_page * MSTACK_PAGE_SIZE + idx * nt_thread_stack_size());
+}
+
+static struct nt_machine_stack_footer *
+nt_stack_chunk_get_msf(const rb_vm_t *vm, const char *mstack)
+{
+ // TODO: stack direction
+ const size_t msz = vm->default_params.thread_machine_stack_size;
+ return (struct nt_machine_stack_footer *)&mstack[msz - sizeof(struct nt_machine_stack_footer)];
+}
+
+static void *
+nt_stack_chunk_get_stack(const rb_vm_t *vm, struct nt_stack_chunk_header *ch, size_t idx, void **vm_stack, void **machine_stack)
+{
+ // TODO: only support stack going down
+ // [VM ... <GUARD> machine stack ...]
+
+ const char *vstack, *mstack;
+ const char *guard_page;
+ vstack = nt_stack_chunk_get_stack_start(ch, idx);
+ guard_page = vstack + vm->default_params.thread_vm_stack_size;
+ mstack = guard_page + MSTACK_PAGE_SIZE;
+
+ struct nt_machine_stack_footer *msf = nt_stack_chunk_get_msf(vm, mstack);
+ msf->ch = ch;
+ msf->index = idx;
+
+#if 0
+ RUBY_DEBUG_LOG("msf:%p vstack:%p-%p guard_page:%p-%p mstack:%p-%p", msf,
+ vstack, (void *)(guard_page-1),
+ guard_page, (void *)(mstack-1),
+ mstack, (void *)(msf));
+#endif
+
+ *vm_stack = (void *)vstack;
+ *machine_stack = (void *)mstack;
+
+ return (void *)guard_page;
+}
+
+RBIMPL_ATTR_MAYBE_UNUSED()
+static void
+nt_stack_chunk_dump(void)
+{
+ struct nt_stack_chunk_header *ch;
+ int i;
+
+ fprintf(stderr, "** nt_stack_chunks\n");
+ ch = nt_stack_chunks;
+ for (i=0; ch; i++, ch = ch->prev_chunk) {
+ fprintf(stderr, "%d %p free_pos:%d\n", i, (void *)ch, (int)ch->free_stack_pos);
+ }
+
+ fprintf(stderr, "** nt_free_stack_chunks\n");
+ ch = nt_free_stack_chunks;
+ for (i=0; ch; i++, ch = ch->prev_free_chunk) {
+ fprintf(stderr, "%d %p free_pos:%d\n", i, (void *)ch, (int)ch->free_stack_pos);
+ }
+}
+
+static int
+nt_guard_page(const char *p, size_t len)
+{
+ if (mprotect((void *)p, len, PROT_NONE) != -1) {
+ return 0;
+ }
+ else {
+ return errno;
+ }
+}
+
+static int
+nt_alloc_stack(rb_vm_t *vm, void **vm_stack, void **machine_stack)
+{
+ int err = 0;
+
+ rb_native_mutex_lock(&nt_machine_stack_lock);
+ {
+ retry:
+ if (nt_free_stack_chunks) {
+ struct nt_stack_chunk_header *ch = nt_free_stack_chunks;
+ if (ch->free_stack_pos > 0) {
+ RUBY_DEBUG_LOG("free_stack_pos:%d", ch->free_stack_pos);
+ nt_stack_chunk_get_stack(vm, ch, ch->free_stack[--ch->free_stack_pos], vm_stack, machine_stack);
+ }
+ else if (ch->uninitialized_stack_count > 0) {
+ RUBY_DEBUG_LOG("uninitialized_stack_count:%d", ch->uninitialized_stack_count);
+
+ size_t idx = ch->stack_count - ch->uninitialized_stack_count--;
+ void *guard_page = nt_stack_chunk_get_stack(vm, ch, idx, vm_stack, machine_stack);
+ err = nt_guard_page(guard_page, MSTACK_PAGE_SIZE);
+ }
+ else {
+ nt_free_stack_chunks = ch->prev_free_chunk;
+ ch->prev_free_chunk = NULL;
+ goto retry;
+ }
+ }
+ else {
+ struct nt_stack_chunk_header *p = nt_alloc_thread_stack_chunk();
+ if (p == NULL) {
+ err = errno;
+ }
+ else {
+ nt_free_stack_chunks = nt_stack_chunks = p;
+ goto retry;
+ }
+ }
+ }
+ rb_native_mutex_unlock(&nt_machine_stack_lock);
+
+ return err;
+}
+
+static void
+nt_free_stack(void *mstack)
+{
+ if (!mstack) return;
+
+ rb_native_mutex_lock(&nt_machine_stack_lock);
+ {
+ struct nt_machine_stack_footer *msf = nt_stack_chunk_get_msf(GET_VM(), mstack);
+ struct nt_stack_chunk_header *ch = msf->ch;
+ int idx = (int)msf->index;
+ void *stack = nt_stack_chunk_get_stack_start(ch, idx);
+
+ RUBY_DEBUG_LOG("stack:%p mstack:%p ch:%p index:%d", stack, mstack, ch, idx);
+
+ if (ch->prev_free_chunk == NULL) {
+ ch->prev_free_chunk = nt_free_stack_chunks;
+ nt_free_stack_chunks = ch;
+ }
+ ch->free_stack[ch->free_stack_pos++] = idx;
+
+ // clear the stack pages
+#if defined(MADV_FREE)
+ int r = madvise(stack, nt_thread_stack_size(), MADV_FREE);
+#elif defined(MADV_DONTNEED)
+ int r = madvise(stack, nt_thread_stack_size(), MADV_DONTNEED);
+#else
+ int r = 0;
+#endif
+
+ if (r != 0) rb_bug("madvise errno:%d", errno);
+ }
+ rb_native_mutex_unlock(&nt_machine_stack_lock);
+}
+
+static int
+native_thread_check_and_create_shared(rb_vm_t *vm)
+{
+ bool need_to_make = false;
+
+ rb_native_mutex_lock(&vm->ractor.sched.lock);
+ {
+ unsigned int snt_cnt = vm->ractor.sched.snt_cnt;
+ if (!vm->ractor.main_ractor->threads.sched.enable_mn_threads) snt_cnt++; // do not need snt for main ractor
+
+ if (((int)snt_cnt < MINIMUM_SNT) ||
+ (snt_cnt < vm->ractor.cnt &&
+ snt_cnt < vm->ractor.sched.max_cpu)) {
+
+ RUBY_DEBUG_LOG("added snt:%u dnt:%u ractor_cnt:%u grq_cnt:%u",
+ vm->ractor.sched.snt_cnt,
+ vm->ractor.sched.dnt_cnt,
+ vm->ractor.cnt,
+ vm->ractor.sched.grq_cnt);
+
+ vm->ractor.sched.snt_cnt++;
+ need_to_make = true;
+ }
+ else {
+ RUBY_DEBUG_LOG("snt:%d ractor_cnt:%d", (int)vm->ractor.sched.snt_cnt, (int)vm->ractor.cnt);
+ }
+ }
+ rb_native_mutex_unlock(&vm->ractor.sched.lock);
+
+ if (need_to_make) {
+ struct rb_native_thread *nt = native_thread_alloc();
+ nt->vm = vm;
+ return native_thread_create0(nt);
+ }
+ else {
+ return 0;
+ }
+}
+
+static COROUTINE
+co_start(struct coroutine_context *from, struct coroutine_context *self)
+{
+#ifdef RUBY_ASAN_ENABLED
+ __sanitizer_finish_switch_fiber(self->fake_stack,
+ (const void**)&from->stack_base, &from->stack_size);
+#endif
+
+ rb_thread_t *th = (rb_thread_t *)self->argument;
+ struct rb_thread_sched *sched = TH_SCHED(th);
+ VM_ASSERT(th->nt != NULL);
+ VM_ASSERT(th == sched->running);
+ VM_ASSERT(sched->lock_owner == NULL);
+
+ // RUBY_DEBUG_LOG("th:%u", rb_th_serial(th));
+
+ thread_sched_set_lock_owner(sched, th);
+ thread_sched_add_running_thread(TH_SCHED(th), th);
+ thread_sched_unlock(sched, th);
+ {
+ RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_RESUMED, th);
+ call_thread_start_func_2(th);
+ }
+ thread_sched_lock(sched, NULL);
+
+ RUBY_DEBUG_LOG("terminated th:%d", (int)th->serial);
+
+ // Thread is terminated
+
+ struct rb_native_thread *nt = th->nt;
+ bool is_dnt = th_has_dedicated_nt(th);
+ native_thread_assign(NULL, th);
+ rb_ractor_set_current_ec(th->ractor, NULL);
+
+ if (is_dnt) {
+ // SNT became DNT while running. Just return to the nt_context
+
+ th->sched.finished = true;
+ coroutine_transfer0(self, nt->nt_context, true);
+ }
+ else {
+ rb_vm_t *vm = th->vm;
+ bool has_ready_ractor = vm->ractor.sched.grq_cnt > 0; // at least this ractor is not queued
+ rb_thread_t *next_th = sched->running;
+
+ if (!has_ready_ractor && next_th && !next_th->nt) {
+ // switch to the next thread
+ thread_sched_set_lock_owner(sched, NULL);
+ thread_sched_switch0(th->sched.context, next_th, nt, true);
+ th->sched.finished = true;
+ }
+ else {
+ // switch to the next Ractor
+ th->sched.finished = true;
+ coroutine_transfer0(self, nt->nt_context, true);
+ }
+ }
+
+ rb_bug("unreachable");
+}
+
+static int
+native_thread_create_shared(rb_thread_t *th)
+{
+ // setup coroutine
+ rb_vm_t *vm = th->vm;
+ void *vm_stack = NULL, *machine_stack = NULL;
+ int err = nt_alloc_stack(vm, &vm_stack, &machine_stack);
+ if (err) return err;
+
+ VM_ASSERT(vm_stack < machine_stack);
+
+ // setup vm stack
+ size_t vm_stack_words = th->vm->default_params.thread_vm_stack_size/sizeof(VALUE);
+ rb_ec_initialize_vm_stack(th->ec, vm_stack, vm_stack_words);
+
+ // setup machine stack
+ size_t machine_stack_size = vm->default_params.thread_machine_stack_size - sizeof(struct nt_machine_stack_footer);
+ th->ec->machine.stack_start = (void *)((uintptr_t)machine_stack + machine_stack_size);
+ th->ec->machine.stack_maxsize = machine_stack_size; // TODO
+ th->sched.context_stack = machine_stack;
+
+ th->sched.context = ruby_xmalloc(sizeof(struct coroutine_context));
+ coroutine_initialize(th->sched.context, co_start, machine_stack, machine_stack_size);
+ th->sched.context->argument = th;
+
+ RUBY_DEBUG_LOG("th:%u vm_stack:%p machine_stack:%p", rb_th_serial(th), vm_stack, machine_stack);
+ thread_sched_to_ready(TH_SCHED(th), th);
+
+ // setup nt
+ return native_thread_check_and_create_shared(th->vm);
+}
+
+#else // USE_MN_THREADS
+
+static int
+native_thread_create_shared(rb_thread_t *th)
+{
+ rb_bug("unreachable");
+}
+
+static bool
+thread_sched_wait_events(struct rb_thread_sched *sched, rb_thread_t *th, int fd, enum thread_sched_waiting_flag events, rb_hrtime_t *rel)
+{
+ rb_bug("unreachable");
+}
+
+#endif // USE_MN_THREADS
+
+/// EPOLL/KQUEUE specific code
+#if (HAVE_SYS_EPOLL_H || HAVE_SYS_EVENT_H) && USE_MN_THREADS
+
+static bool
+fd_readable_nonblock(int fd)
+{
+ struct pollfd pfd = {
+ .fd = fd,
+ .events = POLLIN,
+ };
+ return poll(&pfd, 1, 0) != 0;
+}
+
+static bool
+fd_writable_nonblock(int fd)
+{
+ struct pollfd pfd = {
+ .fd = fd,
+ .events = POLLOUT,
+ };
+ return poll(&pfd, 1, 0) != 0;
+}
+
+static void
+verify_waiting_list(void)
+{
+#if VM_CHECK_MODE > 0
+ rb_thread_t *wth, *prev_wth = NULL;
+ ccan_list_for_each(&timer_th.waiting, wth, sched.waiting_reason.node) {
+ // fprintf(stderr, "verify_waiting_list th:%u abs:%lu\n", rb_th_serial(wth), (unsigned long)wth->sched.waiting_reason.data.timeout);
+ if (prev_wth) {
+ rb_hrtime_t timeout = wth->sched.waiting_reason.data.timeout;
+ rb_hrtime_t prev_timeout = prev_wth->sched.waiting_reason.data.timeout;
+ VM_ASSERT(timeout == 0 || prev_timeout <= timeout);
+ }
+ prev_wth = wth;
+ }
+#endif
+}
+
+#if HAVE_SYS_EVENT_H // kqueue helpers
+
+static enum thread_sched_waiting_flag
+kqueue_translate_filter_to_flags(int16_t filter)
+{
+ switch (filter) {
+ case EVFILT_READ:
+ return thread_sched_waiting_io_read;
+ case EVFILT_WRITE:
+ return thread_sched_waiting_io_write;
+ case EVFILT_TIMER:
+ return thread_sched_waiting_timeout;
+ default:
+ rb_bug("kevent filter:%d not supported", filter);
+ }
+}
+
+static int
+kqueue_wait(rb_vm_t *vm)
+{
+ struct timespec calculated_timeout;
+ struct timespec *timeout = NULL;
+ int timeout_ms = timer_thread_set_timeout(vm);
+
+ if (timeout_ms >= 0) {
+ calculated_timeout.tv_sec = timeout_ms / 1000;
+ calculated_timeout.tv_nsec = (timeout_ms % 1000) * 1000000;
+ timeout = &calculated_timeout;
+ }
+
+ return kevent(timer_th.event_fd, NULL, 0, timer_th.finished_events, KQUEUE_EVENTS_MAX, timeout);
+}
+
+static void
+kqueue_create(void)
+{
+ if ((timer_th.event_fd = kqueue()) == -1) rb_bug("kqueue creation failed (errno:%d)", errno);
+ int flags = fcntl(timer_th.event_fd, F_GETFD);
+ if (flags == -1) {
+ rb_bug("kqueue GETFD failed (errno:%d)", errno);
+ }
+
+ flags |= FD_CLOEXEC;
+ if (fcntl(timer_th.event_fd, F_SETFD, flags) == -1) {
+ rb_bug("kqueue SETFD failed (errno:%d)", errno);
+ }
+}
+
+static void
+kqueue_unregister_waiting(int fd, enum thread_sched_waiting_flag flags)
+{
+ if (flags) {
+ struct kevent ke[2];
+ int num_events = 0;
+
+ if (flags & thread_sched_waiting_io_read) {
+ EV_SET(&ke[num_events], fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
+ num_events++;
+ }
+ if (flags & thread_sched_waiting_io_write) {
+ EV_SET(&ke[num_events], fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
+ num_events++;
+ }
+ if (kevent(timer_th.event_fd, ke, num_events, NULL, 0, NULL) == -1) {
+ perror("kevent");
+ rb_bug("unregister/kevent fails. errno:%d", errno);
+ }
+ }
+}
+
+static bool
+kqueue_already_registered(int fd)
+{
+ rb_thread_t *wth, *found_wth = NULL;
+ ccan_list_for_each(&timer_th.waiting, wth, sched.waiting_reason.node) {
+ // Similar to EEXIST in epoll_ctl, but more strict because it checks fd rather than flags
+ // for simplicity
+ if (wth->sched.waiting_reason.flags && wth->sched.waiting_reason.data.fd == fd) {
+ found_wth = wth;
+ break;
+ }
+ }
+ return found_wth != NULL;
+}
+
+#endif // HAVE_SYS_EVENT_H
+
+// return false if the fd is not waitable or not need to wait.
+static bool
+timer_thread_register_waiting(rb_thread_t *th, int fd, enum thread_sched_waiting_flag flags, rb_hrtime_t *rel)
+{
+ RUBY_DEBUG_LOG("th:%u fd:%d flag:%d rel:%lu", rb_th_serial(th), fd, flags, rel ? (unsigned long)*rel : 0);
+
+ VM_ASSERT(th == NULL || TH_SCHED(th)->running == th);
+ VM_ASSERT(flags != 0);
+
+ rb_hrtime_t abs = 0; // 0 means no timeout
+
+ if (rel) {
+ if (*rel > 0) {
+ flags |= thread_sched_waiting_timeout;
+ }
+ else {
+ return false;
+ }
+ }
+
+ if (rel && *rel > 0) {
+ flags |= thread_sched_waiting_timeout;
+ }
+
+#if HAVE_SYS_EVENT_H
+ struct kevent ke[2];
+ int num_events = 0;
+#else
+ uint32_t epoll_events = 0;
+#endif
+ if (flags & thread_sched_waiting_timeout) {
+ VM_ASSERT(rel != NULL);
+ abs = rb_hrtime_add(rb_hrtime_now(), *rel);
+ }
+
+ if (flags & thread_sched_waiting_io_read) {
+ if (!(flags & thread_sched_waiting_io_force) && fd_readable_nonblock(fd)) {
+ RUBY_DEBUG_LOG("fd_readable_nonblock");
+ return false;
+ }
+ else {
+ VM_ASSERT(fd >= 0);
+#if HAVE_SYS_EVENT_H
+ EV_SET(&ke[num_events], fd, EVFILT_READ, EV_ADD, 0, 0, (void *)th);
+ num_events++;
+#else
+ epoll_events |= EPOLLIN;
+#endif
+ }
+ }
+
+ if (flags & thread_sched_waiting_io_write) {
+ if (!(flags & thread_sched_waiting_io_force) && fd_writable_nonblock(fd)) {
+ RUBY_DEBUG_LOG("fd_writable_nonblock");
+ return false;
+ }
+ else {
+ VM_ASSERT(fd >= 0);
+#if HAVE_SYS_EVENT_H
+ EV_SET(&ke[num_events], fd, EVFILT_WRITE, EV_ADD, 0, 0, (void *)th);
+ num_events++;
+#else
+ epoll_events |= EPOLLOUT;
+#endif
+ }
+ }
+
+ rb_native_mutex_lock(&timer_th.waiting_lock);
+ {
+#if HAVE_SYS_EVENT_H
+ if (num_events > 0) {
+ if (kqueue_already_registered(fd)) {
+ rb_native_mutex_unlock(&timer_th.waiting_lock);
+ return false;
+ }
+
+ if (kevent(timer_th.event_fd, ke, num_events, NULL, 0, NULL) == -1) {
+ RUBY_DEBUG_LOG("failed (%d)", errno);
+
+ switch (errno) {
+ case EBADF:
+ // the fd is closed?
+ case EINTR:
+ // signal received? is there a sensible way to handle this?
+ default:
+ perror("kevent");
+ rb_bug("register/kevent failed(fd:%d, errno:%d)", fd, errno);
+ }
+ }
+ RUBY_DEBUG_LOG("kevent(add, fd:%d) success", fd);
+ }
+#else
+ if (epoll_events) {
+ struct epoll_event event = {
+ .events = epoll_events,
+ .data = {
+ .ptr = (void *)th,
+ },
+ };
+ if (epoll_ctl(timer_th.event_fd, EPOLL_CTL_ADD, fd, &event) == -1) {
+ RUBY_DEBUG_LOG("failed (%d)", errno);
+
+ switch (errno) {
+ case EBADF:
+ // the fd is closed?
+ case EPERM:
+ // the fd doesn't support epoll
+ case EEXIST:
+ // the fd is already registered by another thread
+ rb_native_mutex_unlock(&timer_th.waiting_lock);
+ return false;
+ default:
+ perror("epoll_ctl");
+ rb_bug("register/epoll_ctl failed(fd:%d, errno:%d)", fd, errno);
+ }
+ }
+ RUBY_DEBUG_LOG("epoll_ctl(add, fd:%d, events:%d) success", fd, epoll_events);
+ }
+#endif
+
+ if (th) {
+ VM_ASSERT(th->sched.waiting_reason.flags == thread_sched_waiting_none);
+
+ // setup waiting information
+ {
+ th->sched.waiting_reason.flags = flags;
+ th->sched.waiting_reason.data.timeout = abs;
+ th->sched.waiting_reason.data.fd = fd;
+ th->sched.waiting_reason.data.result = 0;
+ }
+
+ if (abs == 0) { // no timeout
+ VM_ASSERT(!(flags & thread_sched_waiting_timeout));
+ ccan_list_add_tail(&timer_th.waiting, &th->sched.waiting_reason.node);
+ }
+ else {
+ RUBY_DEBUG_LOG("abs:%lu", (unsigned long)abs);
+ VM_ASSERT(flags & thread_sched_waiting_timeout);
+
+ // insert th to sorted list (TODO: O(n))
+ rb_thread_t *wth, *prev_wth = NULL;
+
+ ccan_list_for_each(&timer_th.waiting, wth, sched.waiting_reason.node) {
+ if ((wth->sched.waiting_reason.flags & thread_sched_waiting_timeout) &&
+ wth->sched.waiting_reason.data.timeout < abs) {
+ prev_wth = wth;
+ }
+ else {
+ break;
+ }
+ }
+
+ if (prev_wth) {
+ ccan_list_add_after(&timer_th.waiting, &prev_wth->sched.waiting_reason.node, &th->sched.waiting_reason.node);
+ }
+ else {
+ ccan_list_add(&timer_th.waiting, &th->sched.waiting_reason.node);
+ }
+
+ verify_waiting_list();
+
+ // update timeout seconds
+ timer_thread_wakeup();
+ }
+ }
+ else {
+ VM_ASSERT(abs == 0);
+ }
+ }
+ rb_native_mutex_unlock(&timer_th.waiting_lock);
+
+ return true;
+}
+
+static void
+timer_thread_unregister_waiting(rb_thread_t *th, int fd, enum thread_sched_waiting_flag flags)
+{
+ RUBY_DEBUG_LOG("th:%u fd:%d", rb_th_serial(th), fd);
+#if HAVE_SYS_EVENT_H
+ kqueue_unregister_waiting(fd, flags);
+#else
+ // Linux 2.6.9 or later is needed to pass NULL as data.
+ if (epoll_ctl(timer_th.event_fd, EPOLL_CTL_DEL, fd, NULL) == -1) {
+ switch (errno) {
+ case EBADF:
+ // just ignore. maybe fd is closed.
+ break;
+ default:
+ perror("epoll_ctl");
+ rb_bug("unregister/epoll_ctl fails. errno:%d", errno);
+ }
+ }
+#endif
+}
+
+static void
+timer_thread_setup_mn(void)
+{
+#if HAVE_SYS_EVENT_H
+ kqueue_create();
+ RUBY_DEBUG_LOG("kqueue_fd:%d", timer_th.event_fd);
+#else
+ if ((timer_th.event_fd = epoll_create1(EPOLL_CLOEXEC)) == -1) rb_bug("epoll_create (errno:%d)", errno);
+ RUBY_DEBUG_LOG("epoll_fd:%d", timer_th.event_fd);
+#endif
+ RUBY_DEBUG_LOG("comm_fds:%d/%d", timer_th.comm_fds[0], timer_th.comm_fds[1]);
+
+ timer_thread_register_waiting(NULL, timer_th.comm_fds[0], thread_sched_waiting_io_read | thread_sched_waiting_io_force, NULL);
+}
+
+static int
+event_wait(rb_vm_t *vm)
+{
+#if HAVE_SYS_EVENT_H
+ int r = kqueue_wait(vm);
+#else
+ int r = epoll_wait(timer_th.event_fd, timer_th.finished_events, EPOLL_EVENTS_MAX, timer_thread_set_timeout(vm));
+#endif
+ return r;
+}
+
+/*
+ * The purpose of the timer thread:
+ *
+ * (1) Periodic checking
+ * (1-1) Provide time slice for active NTs
+ * (1-2) Check NT shortage
+ * (1-3) Periodic UBF (global)
+ * (1-4) Lazy GRQ deq start
+ * (2) Receive notification
+ * (2-1) async I/O termination
+ * (2-2) timeout
+ * (2-2-1) sleep(n)
+ * (2-2-2) timeout(n), I/O, ...
+ */
+static void
+timer_thread_polling(rb_vm_t *vm)
+{
+ int r = event_wait(vm);
+
+ RUBY_DEBUG_LOG("r:%d errno:%d", r, errno);
+
+ switch (r) {
+ case 0: // timeout
+ RUBY_DEBUG_LOG("timeout%s", "");
+
+ ractor_sched_lock(vm, NULL);
+ {
+ // (1-1) timeslice
+ timer_thread_check_timeslice(vm);
+
+ // (1-4) lazy grq deq
+ if (vm->ractor.sched.grq_cnt > 0) {
+ RUBY_DEBUG_LOG("GRQ cnt: %u", vm->ractor.sched.grq_cnt);
+ rb_native_cond_signal(&vm->ractor.sched.cond);
+ }
+ }
+ ractor_sched_unlock(vm, NULL);
+
+ // (1-2)
+ native_thread_check_and_create_shared(vm);
+
+ break;
+
+ case -1:
+ switch (errno) {
+ case EINTR:
+ // simply retry
+ break;
+ default:
+ perror("event_wait");
+ rb_bug("event_wait errno:%d", errno);
+ }
+ break;
+
+ default:
+ RUBY_DEBUG_LOG("%d event(s)", r);
+
+#if HAVE_SYS_EVENT_H
+ for (int i=0; i<r; i++) {
+ rb_thread_t *th = (rb_thread_t *)timer_th.finished_events[i].udata;
+ int fd = (int)timer_th.finished_events[i].ident;
+ int16_t filter = timer_th.finished_events[i].filter;
+
+ if (th == NULL) {
+ // wakeup timerthread
+ RUBY_DEBUG_LOG("comm from fd:%d", timer_th.comm_fds[1]);
+ consume_communication_pipe(timer_th.comm_fds[0]);
+ }
+ else {
+ // wakeup specific thread by IO
+ RUBY_DEBUG_LOG("io event. wakeup_th:%u event:%s%s",
+ rb_th_serial(th),
+ (filter == EVFILT_READ) ? "read/" : "",
+ (filter == EVFILT_WRITE) ? "write/" : "");
+
+ rb_native_mutex_lock(&timer_th.waiting_lock);
+ {
+ if (th->sched.waiting_reason.flags) {
+ // delete from chain
+ ccan_list_del_init(&th->sched.waiting_reason.node);
+ timer_thread_unregister_waiting(th, fd, kqueue_translate_filter_to_flags(filter));
+
+ th->sched.waiting_reason.flags = thread_sched_waiting_none;
+ th->sched.waiting_reason.data.fd = -1;
+ th->sched.waiting_reason.data.result = filter;
+
+ timer_thread_wakeup_thread(th);
+ }
+ else {
+ // already released
+ }
+ }
+ rb_native_mutex_unlock(&timer_th.waiting_lock);
+ }
+ }
+#else
+ for (int i=0; i<r; i++) {
+ rb_thread_t *th = (rb_thread_t *)timer_th.finished_events[i].data.ptr;
+
+ if (th == NULL) {
+ // wakeup timerthread
+ RUBY_DEBUG_LOG("comm from fd:%d", timer_th.comm_fds[1]);
+ consume_communication_pipe(timer_th.comm_fds[0]);
+ }
+ else {
+ // wakeup specific thread by IO
+ uint32_t events = timer_th.finished_events[i].events;
+
+ RUBY_DEBUG_LOG("io event. wakeup_th:%u event:%s%s%s%s%s%s",
+ rb_th_serial(th),
+ (events & EPOLLIN) ? "in/" : "",
+ (events & EPOLLOUT) ? "out/" : "",
+ (events & EPOLLRDHUP) ? "RDHUP/" : "",
+ (events & EPOLLPRI) ? "pri/" : "",
+ (events & EPOLLERR) ? "err/" : "",
+ (events & EPOLLHUP) ? "hup/" : "");
+
+ rb_native_mutex_lock(&timer_th.waiting_lock);
+ {
+ if (th->sched.waiting_reason.flags) {
+ // delete from chain
+ ccan_list_del_init(&th->sched.waiting_reason.node);
+ timer_thread_unregister_waiting(th, th->sched.waiting_reason.data.fd, th->sched.waiting_reason.flags);
+
+ th->sched.waiting_reason.flags = thread_sched_waiting_none;
+ th->sched.waiting_reason.data.fd = -1;
+ th->sched.waiting_reason.data.result = (int)events;
+
+ timer_thread_wakeup_thread(th);
+ }
+ else {
+ // already released
+ }
+ }
+ rb_native_mutex_unlock(&timer_th.waiting_lock);
+ }
+ }
+#endif
+ }
+}
+
+#else // HAVE_SYS_EPOLL_H || HAVE_SYS_EVENT_H
+
+static void
+timer_thread_setup_mn(void)
+{
+ // do nothing
+}
+
+static void
+timer_thread_polling(rb_vm_t *vm)
+{
+ int timeout = timer_thread_set_timeout(vm);
+
+ struct pollfd pfd = {
+ .fd = timer_th.comm_fds[0],
+ .events = POLLIN,
+ };
+
+ int r = poll(&pfd, 1, timeout);
+
+ switch (r) {
+ case 0: // timeout
+ rb_native_mutex_lock(&vm->ractor.sched.lock);
+ {
+ // (1-1) timeslice
+ timer_thread_check_timeslice(vm);
+ }
+ rb_native_mutex_unlock(&vm->ractor.sched.lock);
+ break;
+
+ case -1: // error
+ switch (errno) {
+ case EINTR:
+ // simply retry
+ break;
+ default:
+ perror("poll");
+ rb_bug("poll errno:%d", errno);
+ break;
+ }
+
+ case 1:
+ consume_communication_pipe(timer_th.comm_fds[0]);
+ break;
+
+ default:
+ rb_bug("unreachbale");
+ }
+}
+
+#endif // HAVE_SYS_EPOLL_H || HAVE_SYS_EVENT_H
diff --git a/thread_sync.c b/thread_sync.c
index cbcb7c2eaf..ae69cb4a6e 100644
--- a/thread_sync.c
+++ b/thread_sync.c
@@ -41,6 +41,8 @@ struct queue_sleep_arg {
static void
sync_wakeup(struct ccan_list_head *head, long max)
{
+ RUBY_DEBUG_LOG("max:%ld", max);
+
struct sync_waiter *cur = 0, *next;
ccan_list_for_each_safe(head, cur, next, node) {
@@ -51,6 +53,7 @@ sync_wakeup(struct ccan_list_head *head, long max)
rb_fiber_scheduler_unblock(cur->th->scheduler, cur->self, rb_fiberptr_self(cur->fiber));
}
else {
+ RUBY_DEBUG_LOG("target_th:%u", rb_th_serial(cur->th));
rb_threadptr_interrupt(cur->th);
cur->th->status = THREAD_RUNNABLE;
}
@@ -251,6 +254,8 @@ rb_mutex_trylock(VALUE self)
rb_mutex_t *mutex = mutex_ptr(self);
if (mutex->fiber == 0) {
+ RUBY_DEBUG_LOG("%p ok", mutex);
+
rb_fiber_t *fiber = GET_EC()->fiber_ptr;
rb_thread_t *th = GET_THREAD();
mutex->fiber = fiber;
@@ -258,17 +263,12 @@ rb_mutex_trylock(VALUE self)
mutex_locked(th, self);
return Qtrue;
}
-
- return Qfalse;
+ else {
+ RUBY_DEBUG_LOG("%p ng", mutex);
+ return Qfalse;
+ }
}
-/*
- * At maximum, only one thread can use cond_timedwait and watch deadlock
- * periodically. Multiple polling thread (i.e. concurrent deadlock check)
- * introduces new race conditions. [Bug #6278] [ruby-core:44275]
- */
-static const rb_thread_t *patrol_thread = NULL;
-
static VALUE
mutex_owned_p(rb_fiber_t *fiber, rb_mutex_t *mutex)
{
@@ -290,6 +290,8 @@ delete_from_waitq(VALUE value)
return Qnil;
}
+static inline rb_atomic_t threadptr_get_interrupts(rb_thread_t *th);
+
static VALUE
do_mutex_lock(VALUE self, int interruptible_p)
{
@@ -297,6 +299,7 @@ do_mutex_lock(VALUE self, int interruptible_p)
rb_thread_t *th = ec->thread_ptr;
rb_fiber_t *fiber = ec->fiber_ptr;
rb_mutex_t *mutex = mutex_ptr(self);
+ rb_atomic_t saved_ints = 0;
/* When running trap handler */
if (!FL_TEST_RAW(self, MUTEX_ALLOW_TRAP) &&
@@ -310,6 +313,8 @@ do_mutex_lock(VALUE self, int interruptible_p)
}
while (mutex->fiber != fiber) {
+ VM_ASSERT(mutex->fiber != NULL);
+
VALUE scheduler = rb_fiber_scheduler_current();
if (scheduler != Qnil) {
struct sync_waiter sync_waiter = {
@@ -331,51 +336,47 @@ do_mutex_lock(VALUE self, int interruptible_p)
rb_raise(rb_eThreadError, "deadlock; lock already owned by another fiber belonging to the same thread");
}
- enum rb_thread_status prev_status = th->status;
- rb_hrtime_t *timeout = 0;
- rb_hrtime_t rel = rb_msec2hrtime(100);
-
- th->status = THREAD_STOPPED_FOREVER;
- th->locking_mutex = self;
- rb_ractor_sleeper_threads_inc(th->ractor);
- /*
- * Carefully! while some contended threads are in native_sleep(),
- * ractor->sleeper is unstable value. we have to avoid both deadlock
- * and busy loop.
- */
- if ((rb_ractor_living_thread_num(th->ractor) == rb_ractor_sleeper_thread_num(th->ractor)) &&
- !patrol_thread) {
- timeout = &rel;
- patrol_thread = th;
- }
-
struct sync_waiter sync_waiter = {
.self = self,
.th = th,
- .fiber = nonblocking_fiber(fiber)
+ .fiber = nonblocking_fiber(fiber),
};
- ccan_list_add_tail(&mutex->waitq, &sync_waiter.node);
+ RUBY_DEBUG_LOG("%p wait", mutex);
+
+ // similar code with `sleep_forever`, but
+ // sleep_forever(SLEEP_DEADLOCKABLE) raises an exception.
+ // Ensure clause is needed like but `rb_ensure` a bit slow.
+ //
+ // begin
+ // sleep_forever(th, SLEEP_DEADLOCKABLE);
+ // ensure
+ // ccan_list_del(&sync_waiter.node);
+ // end
+ enum rb_thread_status prev_status = th->status;
+ th->status = THREAD_STOPPED_FOREVER;
+ rb_ractor_sleeper_threads_inc(th->ractor);
+ rb_check_deadlock(th->ractor);
- native_sleep(th, timeout); /* release GVL */
+ th->locking_mutex = self;
+ ccan_list_add_tail(&mutex->waitq, &sync_waiter.node);
+ {
+ native_sleep(th, NULL);
+ }
ccan_list_del(&sync_waiter.node);
+ // unlocked by another thread while sleeping
if (!mutex->fiber) {
mutex->fiber = fiber;
}
- if (patrol_thread == th)
- patrol_thread = NULL;
-
- th->locking_mutex = Qfalse;
- if (mutex->fiber && timeout && !RUBY_VM_INTERRUPTED(th->ec)) {
- rb_check_deadlock(th->ractor);
- }
- if (th->status == THREAD_STOPPED_FOREVER) {
- th->status = prev_status;
- }
rb_ractor_sleeper_threads_dec(th->ractor);
+ th->status = prev_status;
+ th->locking_mutex = Qfalse;
+ th->locking_mutex = Qfalse;
+
+ RUBY_DEBUG_LOG("%p wakeup", mutex);
}
if (interruptible_p) {
@@ -387,11 +388,27 @@ do_mutex_lock(VALUE self, int interruptible_p)
mutex->fiber = fiber;
}
}
+ else {
+ // clear interrupt information
+ if (RUBY_VM_INTERRUPTED(th->ec)) {
+ // reset interrupts
+ if (saved_ints == 0) {
+ saved_ints = threadptr_get_interrupts(th);
+ }
+ else {
+ // ignore additional interrupts
+ threadptr_get_interrupts(th);
+ }
+ }
+ }
}
+ if (saved_ints) th->ec->interrupt_flag = saved_ints;
if (mutex->fiber == fiber) mutex_locked(th, self);
}
+ RUBY_DEBUG_LOG("%p locked", mutex);
+
// assertion
if (mutex_owned_p(fiber, mutex) == Qfalse) rb_bug("do_mutex_lock: mutex is not owned.");
@@ -435,6 +452,8 @@ rb_mutex_owned_p(VALUE self)
static const char *
rb_mutex_unlock_th(rb_mutex_t *mutex, rb_thread_t *th, rb_fiber_t *fiber)
{
+ RUBY_DEBUG_LOG("%p", mutex);
+
if (mutex->fiber == 0) {
return "Attempt to unlock a mutex which is not locked";
}
@@ -456,13 +475,14 @@ rb_mutex_unlock_th(rb_mutex_t *mutex, rb_thread_t *th, rb_fiber_t *fiber)
}
else {
switch (cur->th->status) {
- case THREAD_RUNNABLE: /* from someone else calling Thread#run */
- case THREAD_STOPPED_FOREVER: /* likely (rb_mutex_lock) */
+ case THREAD_RUNNABLE: /* from someone else calling Thread#run */
+ case THREAD_STOPPED_FOREVER: /* likely (rb_mutex_lock) */
+ RUBY_DEBUG_LOG("wakeup th:%u", rb_th_serial(cur->th));
rb_threadptr_interrupt(cur->th);
return NULL;
- case THREAD_STOPPED: /* probably impossible */
+ case THREAD_STOPPED: /* probably impossible */
rb_bug("unexpected THREAD_STOPPED");
- case THREAD_KILLED:
+ case THREAD_KILLED:
/* not sure about this, possible in exit GC? */
rb_bug("unexpected THREAD_KILLED");
continue;
@@ -834,7 +854,7 @@ raise_closed_queue_error(VALUE self)
static VALUE
queue_closed_result(VALUE self, struct rb_queue *q)
{
- assert(queue_length(self, q) == 0);
+ RUBY_ASSERT(queue_length(self, q) == 0);
return Qnil;
}
@@ -846,8 +866,8 @@ queue_closed_result(VALUE self, struct rb_queue *q)
* information must be exchanged safely between multiple threads. The
* Thread::Queue class implements all the required locking semantics.
*
- * The class implements FIFO type of queue. In a FIFO queue, the first
- * tasks added are the first retrieved.
+ * The class implements FIFO (first in, first out) type of queue.
+ * In a FIFO queue, the first tasks added are the first retrieved.
*
* Example:
*
@@ -855,17 +875,17 @@ queue_closed_result(VALUE self, struct rb_queue *q)
*
* producer = Thread.new do
* 5.times do |i|
- * sleep rand(i) # simulate expense
- * queue << i
- * puts "#{i} produced"
+ * sleep rand(i) # simulate expense
+ * queue << i
+ * puts "#{i} produced"
* end
* end
*
* consumer = Thread.new do
* 5.times do |i|
- * value = queue.pop
- * sleep rand(i/2) # simulate expense
- * puts "consumed #{value}"
+ * value = queue.pop
+ * sleep rand(i/2) # simulate expense
+ * puts "consumed #{value}"
* end
* end
*
@@ -1061,8 +1081,8 @@ queue_do_pop(VALUE self, struct rb_queue *q, int should_block, VALUE timeout)
else {
rb_execution_context_t *ec = GET_EC();
- assert(RARRAY_LEN(q->que) == 0);
- assert(queue_closed_p(self) == 0);
+ RUBY_ASSERT(RARRAY_LEN(q->que) == 0);
+ RUBY_ASSERT(queue_closed_p(self) == 0);
struct queue_waiter queue_waiter = {
.w = {.self = self, .th = ec->thread_ptr, .fiber = nonblocking_fiber(ec->fiber_ptr)},
@@ -1138,6 +1158,22 @@ rb_queue_length(VALUE self)
return LONG2NUM(queue_length(self, queue_ptr(self)));
}
+NORETURN(static VALUE rb_queue_freeze(VALUE self));
+/*
+ * call-seq:
+ * freeze
+ *
+ * The queue can't be frozen, so this method raises an exception:
+ * Thread::Queue.new.freeze # Raises TypeError (cannot freeze #<Thread::Queue:0x...>)
+ *
+ */
+static VALUE
+rb_queue_freeze(VALUE self)
+{
+ rb_raise(rb_eTypeError, "cannot freeze " "%+"PRIsVALUE, self);
+ UNREACHABLE_RETURN(self);
+}
+
/*
* Document-method: Thread::Queue#num_waiting
*
@@ -1599,6 +1635,7 @@ Init_thread_sync(void)
rb_define_method(rb_cQueue, "clear", rb_queue_clear, 0);
rb_define_method(rb_cQueue, "length", rb_queue_length, 0);
rb_define_method(rb_cQueue, "num_waiting", rb_queue_num_waiting, 0);
+ rb_define_method(rb_cQueue, "freeze", rb_queue_freeze, 0);
rb_define_alias(rb_cQueue, "enq", "push");
rb_define_alias(rb_cQueue, "<<", "push");
diff --git a/thread_win32.c b/thread_win32.c
index b1aab910ef..74015b463b 100644
--- a/thread_win32.c
+++ b/thread_win32.c
@@ -11,6 +11,7 @@
#ifdef THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION
+#include "internal/sanitizers.h"
#include <process.h>
#define TIME_QUANTUM_USEC (10 * 1000)
@@ -148,13 +149,14 @@ thread_sched_yield(struct rb_thread_sched *sched, rb_thread_t *th)
}
void
-rb_thread_sched_init(struct rb_thread_sched *sched)
+rb_thread_sched_init(struct rb_thread_sched *sched, bool atfork)
{
if (GVL_DEBUG) fprintf(stderr, "sched init\n");
sched->lock = w32_mutex_create();
}
#if 0
+// per-ractor
void
rb_thread_sched_destroy(struct rb_thread_sched *sched)
{
@@ -204,6 +206,11 @@ Init_native_thread(rb_thread_t *main_th)
main_th->nt->interrupt_event);
}
+void
+ruby_mn_threads_params(void)
+{
+}
+
static int
w32_wait_events(HANDLE *events, int count, DWORD timeout, rb_thread_t *th)
{
@@ -577,10 +584,6 @@ rb_native_cond_destroy(rb_nativethread_cond_t *cond)
/* */
}
-void
-ruby_init_stack(volatile VALUE *addr)
-{
-}
#define CHECK_ERR(expr) \
{if (!(expr)) {rb_bug("err: %lu - %s", GetLastError(), #expr);}}
@@ -590,20 +593,20 @@ COMPILER_WARNING_PUSH
COMPILER_WARNING_IGNORED(-Wmaybe-uninitialized)
#endif
static inline SIZE_T
-query_memory_basic_info(PMEMORY_BASIC_INFORMATION mi)
+query_memory_basic_info(PMEMORY_BASIC_INFORMATION mi, void *local_in_parent_frame)
{
- return VirtualQuery(mi, mi, sizeof(*mi));
+ return VirtualQuery(asan_get_real_stack_addr(local_in_parent_frame), mi, sizeof(*mi));
}
COMPILER_WARNING_POP
static void
-native_thread_init_stack(rb_thread_t *th)
+native_thread_init_stack(rb_thread_t *th, void *local_in_parent_frame)
{
MEMORY_BASIC_INFORMATION mi;
char *base, *end;
DWORD size, space;
- CHECK_ERR(query_memory_basic_info(&mi));
+ CHECK_ERR(query_memory_basic_info(&mi, local_in_parent_frame));
base = mi.AllocationBase;
end = mi.BaseAddress;
end += mi.RegionSize;
@@ -619,11 +622,13 @@ native_thread_init_stack(rb_thread_t *th)
(void *)InterlockedExchange((long *)(t), (long)(v))
#endif
static void
-native_thread_destroy(rb_thread_t *th)
+native_thread_destroy(struct rb_native_thread *nt)
{
- HANDLE intr = InterlockedExchangePointer(&th->nt->interrupt_event, 0);
- RUBY_DEBUG_LOG("close handle intr:%p, thid:%p\n", intr, th->nt->thread_id);
- w32_close_handle(intr);
+ if (nt) {
+ HANDLE intr = InterlockedExchangePointer(&nt->interrupt_event, 0);
+ RUBY_DEBUG_LOG("close handle intr:%p, thid:%p\n", intr, nt->thread_id);
+ w32_close_handle(intr);
+ }
}
static unsigned long __stdcall
@@ -632,27 +637,39 @@ thread_start_func_1(void *th_ptr)
rb_thread_t *th = th_ptr;
volatile HANDLE thread_id = th->nt->thread_id;
- native_thread_init_stack(th);
+ native_thread_init_stack(th, &th);
th->nt->interrupt_event = CreateEvent(0, TRUE, FALSE, 0);
/* run */
RUBY_DEBUG_LOG("thread created th:%u, thid: %p, event: %p",
rb_th_serial(th), th->nt->thread_id, th->nt->interrupt_event);
+ thread_sched_to_running(TH_SCHED(th), th);
+ ruby_thread_set_native(th);
+
+ // kick threads
thread_start_func_2(th, th->ec->machine.stack_start);
w32_close_handle(thread_id);
RUBY_DEBUG_LOG("thread deleted th:%u", rb_th_serial(th));
+
return 0;
}
static int
native_thread_create(rb_thread_t *th)
{
- const size_t stack_size = th->vm->default_params.thread_machine_stack_size + th->vm->default_params.thread_vm_stack_size;
+ // setup nt
+ const size_t stack_size = th->vm->default_params.thread_machine_stack_size;
th->nt = ZALLOC(struct rb_native_thread);
th->nt->thread_id = w32_create_thread(stack_size, thread_start_func_1, th);
+ // setup vm stack
+ size_t vm_stack_word_size = th->vm->default_params.thread_vm_stack_size / sizeof(VALUE);
+ void *vm_stack = ruby_xmalloc(vm_stack_word_size * sizeof(VALUE));
+ th->sched.vm_stack = vm_stack;
+ rb_ec_initialize_vm_stack(th->ec, vm_stack, vm_stack_word_size);
+
if ((th->nt->thread_id) == 0) {
return thread_errno;
}
@@ -765,12 +782,6 @@ rb_thread_wakeup_timer_thread(int sig)
/* do nothing */
}
-static VALUE
-rb_thread_start_unblock_thread(void)
-{
- return Qfalse; /* no-op */
-}
-
static void
rb_thread_create_timer_thread(void)
{
@@ -843,26 +854,6 @@ rb_reserved_fd_p(int fd)
return 0;
}
-int
-rb_sigwait_fd_get(rb_thread_t *th)
-{
- return -1; /* TODO */
-}
-
-NORETURN(void rb_sigwait_fd_put(rb_thread_t *, int));
-void
-rb_sigwait_fd_put(rb_thread_t *th, int fd)
-{
- rb_bug("not implemented, should not be called");
-}
-
-NORETURN(void rb_sigwait_sleep(const rb_thread_t *, int, const rb_hrtime_t *));
-void
-rb_sigwait_sleep(const rb_thread_t *th, int fd, const rb_hrtime_t *rel)
-{
- rb_bug("not implemented, should not be called");
-}
-
rb_nativethread_id_t
rb_nativethread_self(void)
{
@@ -883,4 +874,141 @@ native_thread_native_thread_id(rb_thread_t *th)
}
#define USE_NATIVE_THREAD_NATIVE_THREAD_ID 1
+void
+rb_add_running_thread(rb_thread_t *th){
+ // do nothing
+}
+
+void
+rb_del_running_thread(rb_thread_t *th)
+{
+ // do nothing
+}
+
+static bool
+th_has_dedicated_nt(const rb_thread_t *th)
+{
+ return true;
+}
+
+void
+rb_threadptr_sched_free(rb_thread_t *th)
+{
+ native_thread_destroy(th->nt);
+ ruby_xfree(th->nt);
+ ruby_xfree(th->sched.vm_stack);
+}
+
+void
+rb_threadptr_remove(rb_thread_t *th)
+{
+ // do nothing
+}
+
+void
+rb_thread_sched_mark_zombies(rb_vm_t *vm)
+{
+ // do nothing
+}
+
+static bool
+vm_barrier_finish_p(rb_vm_t *vm)
+{
+ RUBY_DEBUG_LOG("cnt:%u living:%u blocking:%u",
+ vm->ractor.blocking_cnt == vm->ractor.cnt,
+ vm->ractor.sync.barrier_cnt,
+ vm->ractor.cnt,
+ vm->ractor.blocking_cnt);
+
+ VM_ASSERT(vm->ractor.blocking_cnt <= vm->ractor.cnt);
+ return vm->ractor.blocking_cnt == vm->ractor.cnt;
+}
+
+void
+rb_ractor_sched_barrier_start(rb_vm_t *vm, rb_ractor_t *cr)
+{
+ vm->ractor.sync.barrier_waiting = true;
+
+ RUBY_DEBUG_LOG("barrier start. cnt:%u living:%u blocking:%u",
+ vm->ractor.sync.barrier_cnt,
+ vm->ractor.cnt,
+ vm->ractor.blocking_cnt);
+
+ rb_vm_ractor_blocking_cnt_inc(vm, cr, __FILE__, __LINE__);
+
+ // send signal
+ rb_ractor_t *r = 0;
+ ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
+ if (r != cr) {
+ rb_ractor_vm_barrier_interrupt_running_thread(r);
+ }
+ }
+
+ // wait
+ while (!vm_barrier_finish_p(vm)) {
+ rb_vm_cond_wait(vm, &vm->ractor.sync.barrier_cond);
+ }
+
+ RUBY_DEBUG_LOG("cnt:%u barrier success", vm->ractor.sync.barrier_cnt);
+
+ rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
+
+ vm->ractor.sync.barrier_waiting = false;
+ vm->ractor.sync.barrier_cnt++;
+
+ ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
+ rb_native_cond_signal(&r->barrier_wait_cond);
+ }
+}
+
+void
+rb_ractor_sched_barrier_join(rb_vm_t *vm, rb_ractor_t *cr)
+{
+ vm->ractor.sync.lock_owner = cr;
+ unsigned int barrier_cnt = vm->ractor.sync.barrier_cnt;
+ rb_thread_t *th = GET_THREAD();
+ bool running;
+
+ RB_VM_SAVE_MACHINE_CONTEXT(th);
+
+ if (rb_ractor_status_p(cr, ractor_running)) {
+ rb_vm_ractor_blocking_cnt_inc(vm, cr, __FILE__, __LINE__);
+ running = true;
+ }
+ else {
+ running = false;
+ }
+ VM_ASSERT(rb_ractor_status_p(cr, ractor_blocking));
+
+ if (vm_barrier_finish_p(vm)) {
+ RUBY_DEBUG_LOG("wakeup barrier owner");
+ rb_native_cond_signal(&vm->ractor.sync.barrier_cond);
+ }
+ else {
+ RUBY_DEBUG_LOG("wait for barrier finish");
+ }
+
+ // wait for restart
+ while (barrier_cnt == vm->ractor.sync.barrier_cnt) {
+ vm->ractor.sync.lock_owner = NULL;
+ rb_native_cond_wait(&cr->barrier_wait_cond, &vm->ractor.sync.lock);
+ VM_ASSERT(vm->ractor.sync.lock_owner == NULL);
+ vm->ractor.sync.lock_owner = cr;
+ }
+
+ RUBY_DEBUG_LOG("barrier is released. Acquire vm_lock");
+
+ if (running) {
+ rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
+ }
+
+ vm->ractor.sync.lock_owner = NULL;
+}
+
+bool
+rb_thread_lock_native_thread(void)
+{
+ return false;
+}
+
#endif /* THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION */
diff --git a/thread_win32.h b/thread_win32.h
index 0dfe9d46de..23cd71fcfe 100644
--- a/thread_win32.h
+++ b/thread_win32.h
@@ -27,7 +27,7 @@ struct rb_native_thread {
};
struct rb_thread_sched_item {
- char dmy;
+ void *vm_stack;
};
struct rb_thread_sched {
diff --git a/time.c b/time.c
index 4dbdd3cf7d..3304b2f4f4 100644
--- a/time.c
+++ b/time.c
@@ -650,13 +650,13 @@ wv2timet(wideval_t w)
wideint_t wi = FIXWV2WINT(w);
if (TIMET_MIN == 0) {
if (wi < 0)
- rb_raise(rb_eRangeError, "negative value to convert into `time_t'");
+ rb_raise(rb_eRangeError, "negative value to convert into 'time_t'");
if (TIMET_MAX < (uwideint_t)wi)
- rb_raise(rb_eRangeError, "too big to convert into `time_t'");
+ rb_raise(rb_eRangeError, "too big to convert into 'time_t'");
}
else {
if (wi < TIMET_MIN || TIMET_MAX < wi)
- rb_raise(rb_eRangeError, "too big to convert into `time_t'");
+ rb_raise(rb_eRangeError, "too big to convert into 'time_t'");
}
return (time_t)wi;
}
@@ -1501,7 +1501,7 @@ guess_local_offset(struct vtm *vtm_utc, int *isdst_ret, VALUE *zone_ret)
localtime_with_gmtoff_zone(&now, &tm, &now_gmtoff, &zone);
now_isdst = tm.tm_isdst;
zone = rb_fstring(zone);
- rb_gc_register_mark_object(zone);
+ rb_vm_register_global_object(zone);
now_zone = zone;
}
if (isdst_ret)
@@ -1841,17 +1841,15 @@ time_mark(void *ptr)
rb_gc_mark(tobj->vtm.zone);
}
-static size_t
-time_memsize(const void *tobj)
-{
- return sizeof(struct time_object);
-}
-
static const rb_data_type_t time_data_type = {
"time",
- {time_mark, RUBY_TYPED_DEFAULT_FREE, time_memsize,},
+ {
+ time_mark,
+ RUBY_TYPED_DEFAULT_FREE,
+ NULL, // No external memory to report,
+ },
0, 0,
- (RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_WB_PROTECTED),
+ (RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE),
};
static VALUE
@@ -1960,6 +1958,10 @@ rb_timespec_now(struct timespec *ts)
#endif
}
+/*
+ * Sets the current time information into _time_.
+ * Returns _time_.
+ */
static VALUE
time_init_now(rb_execution_context_t *ec, VALUE time, VALUE zone)
{
@@ -2250,10 +2252,12 @@ extract_time(VALUE time)
} while (0)
if (rb_typeddata_is_kind_of(time, &time_data_type)) {
- struct time_object *tobj = DATA_PTR(time);
+ struct time_object *tobj = RTYPEDDATA_GET_DATA(time);
time_gmtime(time); /* ensure tm got */
t = rb_time_unmagnify(tobj->timew);
+
+ RB_GC_GUARD(time);
}
else if (RB_TYPE_P(time, T_STRUCT)) {
#define AREF(x) rb_struct_aref(time, ID2SYM(id_##x))
@@ -2291,13 +2295,15 @@ extract_vtm(VALUE time, VALUE orig_time, struct time_object *orig_tobj, VALUE su
} while (0)
if (rb_typeddata_is_kind_of(time, &time_data_type)) {
- struct time_object *tobj = DATA_PTR(time);
+ struct time_object *tobj = RTYPEDDATA_GET_DATA(time);
time_get_tm(time, tobj);
time_set_vtm(orig_time, orig_tobj, tobj->vtm);
t = rb_time_unmagnify(tobj->timew);
if (TZMODE_FIXOFF_P(tobj) && vtm->utc_offset != INT2FIX(0))
t = wadd(t, v2w(vtm->utc_offset));
+
+ RB_GC_GUARD(time);
}
else if (RB_TYPE_P(time, T_STRUCT)) {
#define AREF(x) rb_struct_aref(time, ID2SYM(id_##x))
@@ -2337,10 +2343,10 @@ static int
zone_timelocal(VALUE zone, VALUE time)
{
VALUE utc, tm;
- struct time_object *tobj = DATA_PTR(time);
+ struct time_object *tobj = RTYPEDDATA_GET_DATA(time);
wideval_t t, s;
- t = rb_time_unmagnify(tobj->timew);
+ wdivmod(tobj->timew, WINT2FIXWV(TIME_SCALE), &t, &s);
tm = tm_from_time(rb_cTimeTM, time);
utc = rb_check_funcall(zone, id_local_to_utc, 1, &tm);
if (UNDEF_P(utc)) return 0;
@@ -2354,6 +2360,9 @@ zone_timelocal(VALUE zone, VALUE time)
time_set_timew(time, tobj, s);
zone_set_dst(zone, tobj, tm);
+
+ RB_GC_GUARD(time);
+
return 1;
}
@@ -2361,7 +2370,7 @@ static int
zone_localtime(VALUE zone, VALUE time)
{
VALUE local, tm, subsecx;
- struct time_object *tobj = DATA_PTR(time);
+ struct time_object *tobj = RTYPEDDATA_GET_DATA(time);
wideval_t t, s;
split_second(tobj->timew, &t, &subsecx);
@@ -2374,6 +2383,9 @@ zone_localtime(VALUE zone, VALUE time)
tobj->vtm.tm_got = 1;
zone_set_offset(zone, tobj, s, t);
zone_set_dst(zone, tobj, tm);
+
+ RB_GC_GUARD(time);
+
return 1;
}
@@ -2400,6 +2412,10 @@ vtm_day_wraparound(struct vtm *vtm)
static VALUE time_init_vtm(VALUE time, struct vtm vtm, VALUE zone);
+/*
+ * Sets the broken-out time information into _time_.
+ * Returns _time_.
+ */
static VALUE
time_init_args(rb_execution_context_t *ec, VALUE time, VALUE year, VALUE mon, VALUE mday,
VALUE hour, VALUE min, VALUE sec, VALUE zone)
@@ -2512,7 +2528,7 @@ two_digits(const char *ptr, const char *end, const char **endp, const char *name
((len > 2) && ISDIGIT(ptr[2]))) {
VALUE mesg = rb_sprintf("two digits %s is expected", name);
if (ptr[-1] == '-' || ptr[-1] == ':') {
- rb_str_catf(mesg, " after `%c'", ptr[-1]);
+ rb_str_catf(mesg, " after '%c'", ptr[-1]);
}
rb_str_catf(mesg, ": %.*s", ((len > 10) ? 10 : (int)(end - ptr)) + 1, ptr - 1);
rb_exc_raise(rb_exc_new_str(rb_eArgError, mesg));
@@ -2529,8 +2545,12 @@ parse_int(const char *ptr, const char *end, const char **endp, size_t *ndigits,
return rb_int_parse_cstr(ptr, len, (char **)endp, ndigits, 10, flags);
}
+/*
+ * Parses _str_ and sets the broken-out time information into _time_.
+ * If _str_ is not a String, returns +nil+, otherwise returns _time_.
+ */
static VALUE
-time_init_parse(rb_execution_context_t *ec, VALUE klass, VALUE str, VALUE zone, VALUE precision)
+time_init_parse(rb_execution_context_t *ec, VALUE time, VALUE str, VALUE zone, VALUE precision)
{
if (NIL_P(str = rb_check_string_type(str))) return Qnil;
if (!rb_enc_str_asciicompat_p(str)) {
@@ -2545,7 +2565,9 @@ time_init_parse(rb_execution_context_t *ec, VALUE klass, VALUE str, VALUE zone,
size_t ndigits;
size_t prec = NIL_P(precision) ? SIZE_MAX : NUM2SIZET(precision);
- while ((ptr < end) && ISSPACE(*ptr)) ptr++;
+ if ((ptr < end) && (ISSPACE(*ptr) || ISSPACE(*(end-1)))) {
+ rb_raise(rb_eArgError, "can't parse: %+"PRIsVALUE, str);
+ }
year = parse_int(ptr, end, &ptr, &ndigits, true);
if (NIL_P(year)) {
rb_raise(rb_eArgError, "can't parse: %+"PRIsVALUE, str);
@@ -2553,6 +2575,9 @@ time_init_parse(rb_execution_context_t *ec, VALUE klass, VALUE str, VALUE zone,
else if (ndigits < 4) {
rb_raise(rb_eArgError, "year must be 4 or more digits: %.*s", (int)ndigits, ptr - ndigits);
}
+ else if (ptr == end) {
+ goto only_year;
+ }
do {
#define peekable_p(n) ((ptrdiff_t)(n) < (end - ptr))
#define peek_n(c, n) (peekable_p(n) && ((unsigned char)ptr[n] == (c)))
@@ -2613,6 +2638,9 @@ time_init_parse(rb_execution_context_t *ec, VALUE klass, VALUE str, VALUE zone,
if (zend > zstr) {
zone = rb_str_subseq(str, zstr - begin, zend - zstr);
}
+ else if (hour == -1) {
+ rb_raise(rb_eArgError, "no time information");
+ }
if (!NIL_P(subsec)) {
/* subseconds is the last using ndigits */
static const size_t TIME_SCALE_NUMDIGITS =
@@ -2629,6 +2657,9 @@ time_init_parse(rb_execution_context_t *ec, VALUE klass, VALUE str, VALUE zone,
}
}
+only_year:
+ ;
+
struct vtm vtm = {
.wday = VTM_WDAY_INITVAL,
.yday = 0,
@@ -2641,7 +2672,7 @@ time_init_parse(rb_execution_context_t *ec, VALUE klass, VALUE str, VALUE zone,
.sec = (sec < 0) ? 0 : sec,
.subsecx = NIL_P(subsec) ? INT2FIX(0) : subsec,
};
- return time_init_vtm(klass, vtm, zone);
+ return time_init_vtm(time, vtm, zone);
}
static void
@@ -2691,7 +2722,7 @@ time_new_timew(VALUE klass, wideval_t timew)
VALUE time = time_s_alloc(klass);
struct time_object *tobj;
- tobj = DATA_PTR(time); /* skip type check */
+ tobj = RTYPEDDATA_GET_DATA(time); /* skip type check */
TZMODE_SET_LOCALTIME(tobj);
time_set_timew(time, tobj, timew);
@@ -3565,7 +3596,7 @@ tmcmp(struct tm *a, struct tm *b)
* Time.utc(year, month = 1, mday = 1, hour = 0, min = 0, sec = 0, usec = 0) -> new_time
* Time.utc(sec, min, hour, mday, month, year, dummy, dummy, dummy, dummy) -> new_time
*
- * Returns a new \Time object based the on given arguments,
+ * Returns a new +Time+ object based the on given arguments,
* in the UTC timezone.
*
* With one to seven arguments given,
@@ -3626,7 +3657,7 @@ tmcmp(struct tm *a, struct tm *b)
* Time.utc(Float(0.0), Rational(1, 1), 1.0, 0.0, 0.0, 0.0, 0.0)
* # => 0000-01-01 00:00:00 UTC
*
- * - \String integers:
+ * - String integers:
*
* a = %w[0 1 1 0 0 0 0 0]
* # => ["0", "1", "1", "0", "0", "0", "0", "0"]
@@ -3643,7 +3674,7 @@ tmcmp(struct tm *a, struct tm *b)
* # => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
* Time.utc(*a) # => 0005-04-03 02:01:00 UTC
*
- * This form is useful for creating a \Time object from a 10-element
+ * This form is useful for creating a +Time+ object from a 10-element
* array returned by Time.to_a:
*
* t = Time.new(2000, 1, 2, 3, 4, 5, 6) # => 2000-01-02 03:04:05 +000006
@@ -3674,7 +3705,7 @@ time_s_mkutc(int argc, VALUE *argv, VALUE klass)
* Time.local(year, month = 1, mday = 1, hour = 0, min = 0, sec = 0, usec = 0) -> new_time
* Time.local(sec, min, hour, mday, month, year, dummy, dummy, dummy, dummy) -> new_time
*
- * Like Time.utc, except that the returned \Time object
+ * Like Time.utc, except that the returned +Time+ object
* has the local timezone, not the UTC timezone:
*
* # With seven arguments.
@@ -3903,7 +3934,7 @@ time_cmp(VALUE time1, VALUE time2)
* eql?(other_time)
*
* Returns +true+ if +self+ and +other_time+ are
- * both \Time objects with the exact same time value.
+ * both +Time+ objects with the exact same time value.
*/
static VALUE
@@ -4043,20 +4074,20 @@ time_zonelocal(VALUE time, VALUE off)
* With no argument given:
*
* - Returns +self+ if +self+ is a local time.
- * - Otherwise returns a new \Time in the user's local timezone:
+ * - Otherwise returns a new +Time+ in the user's local timezone:
*
* t = Time.utc(2000, 1, 1, 20, 15, 1) # => 2000-01-01 20:15:01 UTC
* t.localtime # => 2000-01-01 14:15:01 -0600
*
* With argument +zone+ given,
- * returns the new \Time object created by converting
+ * returns the new +Time+ object created by converting
* +self+ to the given time zone:
*
* t = Time.utc(2000, 1, 1, 20, 15, 1) # => 2000-01-01 20:15:01 UTC
* t.localtime("-09:00") # => 2000-01-01 11:15:01 -0900
*
* For forms of argument +zone+, see
- * {Timezone Specifiers}[rdoc-ref:timezones.rdoc].
+ * {Timezone Specifiers}[rdoc-ref:Time@Timezone+Specifiers].
*
*/
@@ -4083,7 +4114,7 @@ time_localtime_m(int argc, VALUE *argv, VALUE time)
* t.utc # => 2000-01-01 06:00:00 UTC
* t.utc? # => true
*
- * Related: Time#getutc (returns a new converted \Time object).
+ * Related: Time#getutc (returns a new converted +Time+ object).
*/
static VALUE
@@ -4148,7 +4179,7 @@ time_fixoff(VALUE time)
* call-seq:
* getlocal(zone = nil) -> new_time
*
- * Returns a new \Time object representing the value of +self+
+ * Returns a new +Time+ object representing the value of +self+
* converted to a given timezone;
* if +zone+ is +nil+, the local timezone is used:
*
@@ -4157,7 +4188,7 @@ time_fixoff(VALUE time)
* t.getlocal('+12:00') # => 2000-01-01 12:00:00 +1200
*
* For forms of argument +zone+, see
- * {Timezone Specifiers}[rdoc-ref:timezones.rdoc].
+ * {Timezone Specifiers}[rdoc-ref:Time@Timezone+Specifiers].
*
*/
@@ -4197,7 +4228,7 @@ time_getlocaltime(int argc, VALUE *argv, VALUE time)
* call-seq:
* getutc -> new_time
*
- * Returns a new \Time object representing the value of +self+
+ * Returns a new +Time+ object representing the value of +self+
* converted to the UTC timezone:
*
* local = Time.local(2000) # => 2000-01-01 00:00:00 -0600
@@ -4362,7 +4393,7 @@ time_add(const struct time_object *tobj, VALUE torig, VALUE offset, int sign)
* call-seq:
* self + numeric -> new_time
*
- * Returns a new \Time object whose value is the sum of the numeric value
+ * Returns a new +Time+ object whose value is the sum of the numeric value
* of +self+ and the given +numeric+:
*
* t = Time.new(2000) # => 2000-01-01 00:00:00 -0600
@@ -4390,7 +4421,7 @@ time_plus(VALUE time1, VALUE time2)
* self - other_time -> float
*
* When +numeric+ is given,
- * returns a new \Time object whose value is the difference
+ * returns a new +Time+ object whose value is the difference
* of the numeric value of +self+ and +numeric+:
*
* t = Time.new(2000) # => 2000-01-01 00:00:00 -0600
@@ -4399,7 +4430,7 @@ time_plus(VALUE time1, VALUE time2)
*
* When +other_time+ is given,
* returns a Float whose value is the difference
- * of the numeric values of +self+ and +other_time+:
+ * of the numeric values of +self+ and +other_time+ in seconds:
*
* t - t # => 0.0
*
@@ -4440,7 +4471,7 @@ ndigits_denominator(VALUE ndigits)
* call-seq:
* round(ndigits = 0) -> new_time
*
- * Returns a new \Time object whose numeric value is that of +self+,
+ * Returns a new +Time+ object whose numeric value is that of +self+,
* with its seconds value rounded to precision +ndigits+:
*
* t = Time.utc(2010, 3, 30, 5, 43, 25.123456789r)
@@ -4489,7 +4520,7 @@ time_round(int argc, VALUE *argv, VALUE time)
* call-seq:
* floor(ndigits = 0) -> new_time
*
- * Returns a new \Time object whose numerical value
+ * Returns a new +Time+ object whose numerical value
* is less than or equal to +self+ with its seconds
* truncated to precision +ndigits+:
*
@@ -4534,7 +4565,7 @@ time_floor(int argc, VALUE *argv, VALUE time)
* call-seq:
* ceil(ndigits = 0) -> new_time
*
- * Returns a new \Time object whose numerical value
+ * Returns a new +Time+ object whose numerical value
* is greater than or equal to +self+ with its seconds
* truncated to precision +ndigits+:
*
@@ -4996,7 +5027,7 @@ rb_time_utc_offset(VALUE time)
* # [sec, min, hour, day, mon, year, wday, yday, dst?, zone]
*
* The returned array is suitable for use as an argument to Time.utc or Time.local
- * to create a new \Time object.
+ * to create a new +Time+ object.
*
*/
@@ -5550,7 +5581,7 @@ tm_from_time(VALUE klass, VALUE time)
GetTimeval(time, tobj);
tm = time_s_alloc(klass);
- ttm = DATA_PTR(tm);
+ ttm = RTYPEDDATA_GET_DATA(tm);
v = &vtm;
GMTIMEW(ttm->timew = tobj->timew, v);
ttm->timew = wsub(ttm->timew, v->subsecx);
@@ -5580,7 +5611,7 @@ tm_initialize(int argc, VALUE *argv, VALUE time)
if (rb_check_arity(argc, 1, 7) > 6) argc = 6;
time_arg(argc, argv, &vtm);
t = timegmw(&vtm);
- struct time_object *tobj = DATA_PTR(time);
+ struct time_object *tobj = RTYPEDDATA_GET_DATA(time);
TZMODE_SET_UTC(tobj);
time_set_timew(time, tobj, t);
time_set_vtm(time, tobj, vtm);
@@ -5600,7 +5631,7 @@ tm_to_time(VALUE tm)
{
struct time_object *torig = get_timeval(tm);
VALUE dup = time_s_alloc(rb_cTime);
- struct time_object *tobj = DATA_PTR(dup);
+ struct time_object *tobj = RTYPEDDATA_GET_DATA(dup);
*tobj = *torig;
return dup;
}
@@ -5742,9 +5773,9 @@ Init_Time(void)
sym_zone = ID2SYM(rb_intern_const("zone"));
str_utc = rb_fstring_lit("UTC");
- rb_gc_register_mark_object(str_utc);
+ rb_vm_register_global_object(str_utc);
str_empty = rb_fstring_lit("");
- rb_gc_register_mark_object(str_empty);
+ rb_vm_register_global_object(str_empty);
rb_cTime = rb_define_class("Time", rb_cObject);
VALUE scTime = rb_singleton_class(rb_cTime);
@@ -5828,7 +5859,7 @@ Init_Time(void)
if (debug_find_time_numguess) {
rb_define_hooked_variable("$find_time_numguess", (VALUE *)&find_time_numguess,
- find_time_numguess_getter, NULL);
+ find_time_numguess_getter, 0);
}
rb_cTimeTM = Init_tm(rb_cTime, "tm");
diff --git a/timev.rb b/timev.rb
index 76d3ab7058..22f46b9729 100644
--- a/timev.rb
+++ b/timev.rb
@@ -1,4 +1,4 @@
-# A \Time object represents a date and time:
+# A +Time+ object represents a date and time:
#
# Time.new(2000, 1, 1, 0, 0, 0) # => 2000-01-01 00:00:00 -0600
#
@@ -41,7 +41,7 @@
#
# == \Time Resolution
#
-# A \Time object derived from the system clock
+# A +Time+ object derived from the system clock
# (for example, by method Time.now)
# has the resolution supported by the system.
#
@@ -49,7 +49,7 @@
#
# All of these examples were done using the EST timezone which is GMT-5.
#
-# === Creating a New \Time Instance
+# === Creating a New +Time+ Instance
#
# You can create a new instance of Time with Time.new. This will use the
# current system time. Time.now is an alias for this. You can also
@@ -66,7 +66,7 @@
#
# Time.new(2002, 10, 31, 2, 2, 2, "+02:00") #=> 2002-10-31 02:02:02 +0200
#
-# Or a timezone object:
+# Or {a timezone object}[rdoc-ref:Time@Timezone+Objects]:
#
# zone = timezone("Europe/Athens") # Eastern European Time, UTC+2
# Time.new(2002, 10, 31, 2, 2, 2, zone) #=> 2002-10-31 02:02:02 +0200
@@ -81,7 +81,7 @@
#
# Time.at(628232400) #=> 1989-11-28 00:00:00 -0500
#
-# === Working with an Instance of \Time
+# === Working with an Instance of +Time+
#
# Once you have an instance of Time there is a multitude of things you can
# do with it. Below are some examples. For all of the following examples, we
@@ -123,19 +123,19 @@
#
# == What's Here
#
-# First, what's elsewhere. \Class \Time:
+# First, what's elsewhere. \Class +Time+:
#
# - Inherits from {class Object}[rdoc-ref:Object@What-27s+Here].
# - Includes {module Comparable}[rdoc-ref:Comparable@What-27s+Here].
#
-# Here, class \Time provides methods that are useful for:
+# Here, class +Time+ provides methods that are useful for:
#
-# - {Creating \Time objects}[rdoc-ref:Time@Methods+for+Creating].
-# - {Fetching \Time values}[rdoc-ref:Time@Methods+for+Fetching].
-# - {Querying a \Time object}[rdoc-ref:Time@Methods+for+Querying].
-# - {Comparing \Time objects}[rdoc-ref:Time@Methods+for+Comparing].
-# - {Converting a \Time object}[rdoc-ref:Time@Methods+for+Converting].
-# - {Rounding a \Time}[rdoc-ref:Time@Methods+for+Rounding].
+# - {Creating +Time+ objects}[rdoc-ref:Time@Methods+for+Creating].
+# - {Fetching +Time+ values}[rdoc-ref:Time@Methods+for+Fetching].
+# - {Querying a +Time+ object}[rdoc-ref:Time@Methods+for+Querying].
+# - {Comparing +Time+ objects}[rdoc-ref:Time@Methods+for+Comparing].
+# - {Converting a +Time+ object}[rdoc-ref:Time@Methods+for+Converting].
+# - {Rounding a +Time+}[rdoc-ref:Time@Methods+for+Rounding].
#
# === Methods for Creating
#
@@ -210,25 +210,27 @@
# - #floor: Returns a new time with subseconds lowered to a floor.
#
# For the forms of argument +zone+, see
-# {Timezone Specifiers}[rdoc-ref:timezones.rdoc].
+# {Timezone Specifiers}[rdoc-ref:Time@Timezone+Specifiers].
+#
+# :include: doc/_timezones.rdoc
class Time
- # Creates a new \Time object from the current system time.
+ # Creates a new +Time+ object from the current system time.
# This is the same as Time.new without arguments.
#
# Time.now # => 2009-06-24 12:39:54 +0900
# Time.now(in: '+04:00') # => 2009-06-24 07:39:54 +0400
#
# For forms of argument +zone+, see
- # {Timezone Specifiers}[rdoc-ref:timezones.rdoc].
+ # {Timezone Specifiers}[rdoc-ref:Time@Timezone+Specifiers].
def self.now(in: nil)
Primitive.time_s_now(Primitive.arg!(:in))
end
- # Returns a new \Time object based on the given arguments.
+ # Returns a new +Time+ object based on the given arguments.
#
# Required argument +time+ may be either of:
#
- # - A \Time object, whose value is the basis for the returned time;
+ # - A +Time+ object, whose value is the basis for the returned time;
# also influenced by optional keyword argument +in:+ (see below).
# - A numeric number of
# {Epoch seconds}[rdoc-ref:Time@Epoch+Seconds]
@@ -270,14 +272,14 @@ class Time
# Time.at(secs, -1000000000, :nanosecond) # => 2000-12-31 23:59:58 -0600
#
#
- # Optional keyword argument <tt>+in: zone</tt> specifies the timezone
+ # Optional keyword argument <tt>in: zone</tt> specifies the timezone
# for the returned time:
#
# Time.at(secs, in: '+12:00') # => 2001-01-01 17:59:59 +1200
# Time.at(secs, in: '-12:00') # => 2000-12-31 17:59:59 -1200
#
# For the forms of argument +zone+, see
- # {Timezone Specifiers}[rdoc-ref:timezones.rdoc].
+ # {Timezone Specifiers}[rdoc-ref:Time@Timezone+Specifiers].
#
def self.at(time, subsec = false, unit = :microsecond, in: nil)
if Primitive.mandatory_only?
@@ -287,7 +289,10 @@ class Time
end
end
- # Returns a new \Time object based on the given arguments,
+ # call-seq:
+ # Time.new(year = nil, mon = nil, mday = nil, hour = nil, min = nil, sec = nil, zone = nil, in: nil, precision: 9)
+ #
+ # Returns a new +Time+ object based on the given arguments,
# by default in the local timezone.
#
# With no positional arguments, returns the value of Time.now:
@@ -295,7 +300,7 @@ class Time
# Time.new # => 2021-04-24 17:27:46.0512465 -0500
#
# With one string argument that represents a time, returns a new
- # \Time object based on the given argument, in the local timezone.
+ # +Time+ object based on the given argument, in the local timezone.
#
# Time.new('2000-12-31 23:59:59.5') # => 2000-12-31 23:59:59.5 -0600
# Time.new('2000-12-31 23:59:59.5 +0900') # => 2000-12-31 23:59:59.5 +0900
@@ -303,7 +308,7 @@ class Time
# Time.new('2000-12-31 23:59:59.5') # => 2000-12-31 23:59:59.5 -0600
# Time.new('2000-12-31 23:59:59.56789', precision: 3) # => 2000-12-31 23:59:59.567 -0600
#
- # With one to six arguments, returns a new \Time object
+ # With one to six arguments, returns a new +Time+ object
# based on the given arguments, in the local timezone.
#
# Time.new(2000, 1, 2, 3, 4, 5) # => 2000-01-02 03:04:05 -0600
@@ -359,16 +364,16 @@ class Time
# Time.new(Float(0.0), Rational(1, 1), 1.0, 0.0, 0.0, 0.0)
# # => 0000-01-01 00:00:00 -0600
#
- # - \String integers:
+ # - String integers:
#
# a = %w[0 1 1 0 0 0]
# # => ["0", "1", "1", "0", "0", "0"]
# Time.new(*a) # => 0000-01-01 00:00:00 -0600
#
# When positional argument +zone+ or keyword argument +in:+ is given,
- # the new \Time object is in the specified timezone.
+ # the new +Time+ object is in the specified timezone.
# For the forms of argument +zone+, see
- # {Timezone Specifiers}[rdoc-ref:timezones.rdoc]:
+ # {Timezone Specifiers}[rdoc-ref:Time@Timezone+Specifiers]:
#
# Time.new(2000, 1, 1, 0, 0, 0, '+12:00')
# # => 2000-01-01 00:00:00 +1200
@@ -377,8 +382,14 @@ class Time
# Time.new(in: '-12:00')
# # => 2022-08-23 08:49:26.1941467 -1200
#
+ # Since +in:+ keyword argument just provides the default, so if the
+ # first argument in single string form contains time zone information,
+ # this keyword argument will be silently ignored.
+ #
+ # Time.new('2000-01-01 00:00:00 +0100', in: '-0500').utc_offset # => 3600
+ #
# - +precision+: maximum effective digits in sub-second part, default is 9.
- # More digits will be truncated, as other operations of \Time.
+ # More digits will be truncated, as other operations of +Time+.
# Ignored unless the first argument is a string.
#
def initialize(year = (now = true), mon = (str = year; nil), mday = nil, hour = nil, min = nil, sec = nil, zone = nil,
diff --git a/tool/bundler/dev_gems.rb b/tool/bundler/dev_gems.rb
index 856256a246..1422cfc7a5 100644
--- a/tool/bundler/dev_gems.rb
+++ b/tool/bundler/dev_gems.rb
@@ -3,18 +3,18 @@
source "https://rubygems.org"
gem "test-unit", "~> 3.0"
-gem "rake", "~> 13.0"
+gem "rake", "~> 13.1"
gem "rb_sys"
gem "webrick", "~> 1.6"
-gem "turbo_tests", "~> 2.1"
+gem "turbo_tests", "~> 2.2.3"
gem "parallel_tests", "< 3.9.0"
gem "parallel", "~> 1.19"
gem "rspec-core", "~> 3.12"
gem "rspec-expectations", "~> 3.12"
gem "rspec-mocks", "~> 3.12"
-gem "uri", "~> 0.12.0"
+gem "uri", "~> 0.13.0"
group :doc do
- gem "ronn", "~> 0.7.3", :platform => :ruby
+ gem "nronn", "~> 0.11.1", platform: :ruby
end
diff --git a/tool/bundler/dev_gems.rb.lock b/tool/bundler/dev_gems.rb.lock
deleted file mode 100644
index c7c323ea54..0000000000
--- a/tool/bundler/dev_gems.rb.lock
+++ /dev/null
@@ -1,66 +0,0 @@
-GEM
- remote: https://rubygems.org/
- specs:
- diff-lcs (1.5.0)
- hpricot (0.8.6)
- hpricot (0.8.6-java)
- mustache (1.1.1)
- parallel (1.23.0)
- parallel_tests (3.8.1)
- parallel
- power_assert (2.0.3)
- rake (13.0.6)
- rb_sys (0.9.78)
- rdiscount (2.2.7)
- ronn (0.7.3)
- hpricot (>= 0.8.2)
- mustache (>= 0.7.0)
- rdiscount (>= 1.5.8)
- rspec (3.12.0)
- rspec-core (~> 3.12.0)
- rspec-expectations (~> 3.12.0)
- rspec-mocks (~> 3.12.0)
- rspec-core (3.12.2)
- rspec-support (~> 3.12.0)
- rspec-expectations (3.12.3)
- diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.12.0)
- rspec-mocks (3.12.5)
- diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.12.0)
- rspec-support (3.12.0)
- test-unit (3.5.9)
- power_assert
- turbo_tests (2.1.0)
- bundler (>= 2.1)
- parallel_tests (>= 3.3.0, < 5)
- rspec (>= 3.10)
- uri (0.12.1)
- webrick (1.8.1)
-
-PLATFORMS
- arm64-darwin-22
- java
- universal-java-11
- universal-java-18
- x64-mingw-ucrt
- x64-mingw32
- x86_64-darwin-20
- x86_64-linux
-
-DEPENDENCIES
- parallel (~> 1.19)
- parallel_tests (< 3.9.0)
- rake (~> 13.0)
- rb_sys
- ronn (~> 0.7.3)
- rspec-core (~> 3.12)
- rspec-expectations (~> 3.12)
- rspec-mocks (~> 3.12)
- test-unit (~> 3.0)
- turbo_tests (~> 2.1)
- uri (~> 0.12.0)
- webrick (~> 1.6)
-
-BUNDLED WITH
- 2.5.0.dev
diff --git a/tool/bundler/rubocop_gems.rb b/tool/bundler/rubocop_gems.rb
index 9cb740cd15..4d0b21060a 100644
--- a/tool/bundler/rubocop_gems.rb
+++ b/tool/bundler/rubocop_gems.rb
@@ -2,7 +2,7 @@
source "https://rubygems.org"
-gem "rubocop", "~> 1.7"
+gem "rubocop", ">= 1.52.1", "< 2"
gem "minitest"
gem "rake"
diff --git a/tool/bundler/rubocop_gems.rb.lock b/tool/bundler/rubocop_gems.rb.lock
deleted file mode 100644
index 7f43e38dcb..0000000000
--- a/tool/bundler/rubocop_gems.rb.lock
+++ /dev/null
@@ -1,73 +0,0 @@
-GEM
- remote: https://rubygems.org/
- specs:
- ast (2.4.2)
- diff-lcs (1.5.0)
- json (2.6.3)
- json (2.6.3-java)
- minitest (5.16.3)
- parallel (1.22.1)
- parser (3.1.3.0)
- ast (~> 2.4.1)
- power_assert (2.0.2)
- rainbow (3.1.1)
- rake (13.0.6)
- rake-compiler (1.2.0)
- rake
- rb_sys (0.9.63)
- regexp_parser (2.6.1)
- rexml (3.2.5)
- rspec (3.12.0)
- rspec-core (~> 3.12.0)
- rspec-expectations (~> 3.12.0)
- rspec-mocks (~> 3.12.0)
- rspec-core (3.12.2)
- rspec-support (~> 3.12.0)
- rspec-expectations (3.12.3)
- diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.12.0)
- rspec-mocks (3.12.5)
- diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.12.0)
- rspec-support (3.12.0)
- rubocop (1.40.0)
- json (~> 2.3)
- parallel (~> 1.10)
- parser (>= 3.1.2.1)
- rainbow (>= 2.2.2, < 4.0)
- regexp_parser (>= 1.8, < 3.0)
- rexml (>= 3.2.5, < 4.0)
- rubocop-ast (>= 1.23.0, < 2.0)
- ruby-progressbar (~> 1.7)
- unicode-display_width (>= 1.4.0, < 3.0)
- rubocop-ast (1.24.0)
- parser (>= 3.1.1.0)
- ruby-progressbar (1.11.0)
- test-unit (3.5.5)
- power_assert
- unicode-display_width (2.3.0)
-
-PLATFORMS
- aarch64-linux
- arm64-darwin-20
- arm64-darwin-21
- arm64-darwin-22
- universal-java-11
- universal-java-18
- x64-mingw-ucrt
- x86_64-darwin-19
- x86_64-darwin-20
- x86_64-darwin-21
- x86_64-linux
-
-DEPENDENCIES
- minitest
- rake
- rake-compiler
- rb_sys
- rspec
- rubocop (~> 1.7)
- test-unit
-
-BUNDLED WITH
- 2.5.0.dev
diff --git a/tool/bundler/standard_gems.rb.lock b/tool/bundler/standard_gems.rb.lock
deleted file mode 100644
index d0387b438c..0000000000
--- a/tool/bundler/standard_gems.rb.lock
+++ /dev/null
@@ -1,81 +0,0 @@
-GEM
- remote: https://rubygems.org/
- specs:
- ast (2.4.2)
- diff-lcs (1.5.0)
- json (2.6.3)
- json (2.6.3-java)
- language_server-protocol (3.17.0.2)
- minitest (5.16.3)
- parallel (1.22.1)
- parser (3.1.3.0)
- ast (~> 2.4.1)
- power_assert (2.0.2)
- rainbow (3.1.1)
- rake (13.0.6)
- rake-compiler (1.2.0)
- rake
- rb_sys (0.9.63)
- regexp_parser (2.6.1)
- rexml (3.2.5)
- rspec (3.12.0)
- rspec-core (~> 3.12.0)
- rspec-expectations (~> 3.12.0)
- rspec-mocks (~> 3.12.0)
- rspec-core (3.12.2)
- rspec-support (~> 3.12.0)
- rspec-expectations (3.12.3)
- diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.12.0)
- rspec-mocks (3.12.5)
- diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.12.0)
- rspec-support (3.12.0)
- rubocop (1.39.0)
- json (~> 2.3)
- parallel (~> 1.10)
- parser (>= 3.1.2.1)
- rainbow (>= 2.2.2, < 4.0)
- regexp_parser (>= 1.8, < 3.0)
- rexml (>= 3.2.5, < 4.0)
- rubocop-ast (>= 1.23.0, < 2.0)
- ruby-progressbar (~> 1.7)
- unicode-display_width (>= 1.4.0, < 3.0)
- rubocop-ast (1.24.0)
- parser (>= 3.1.1.0)
- rubocop-performance (1.15.1)
- rubocop (>= 1.7.0, < 2.0)
- rubocop-ast (>= 0.4.0)
- ruby-progressbar (1.11.0)
- standard (1.19.1)
- language_server-protocol (~> 3.17.0.2)
- rubocop (= 1.39.0)
- rubocop-performance (= 1.15.1)
- test-unit (3.5.5)
- power_assert
- unicode-display_width (2.3.0)
-
-PLATFORMS
- aarch64-linux
- arm64-darwin-20
- arm64-darwin-21
- arm64-darwin-22
- universal-java-11
- universal-java-18
- x64-mingw-ucrt
- x86_64-darwin-19
- x86_64-darwin-20
- x86_64-darwin-21
- x86_64-linux
-
-DEPENDENCIES
- minitest
- rake
- rake-compiler
- rb_sys
- rspec
- standard (~> 1.0)
- test-unit
-
-BUNDLED WITH
- 2.5.0.dev
diff --git a/tool/bundler/test_gems.rb b/tool/bundler/test_gems.rb
index 9ba5763a3b..32cb6b34ee 100644
--- a/tool/bundler/test_gems.rb
+++ b/tool/bundler/test_gems.rb
@@ -2,11 +2,13 @@
source "https://rubygems.org"
-gem "rack", "2.0.8"
+gem "rack", "~> 2.0"
+gem "base64"
gem "webrick", "1.7.0"
gem "rack-test", "~> 1.1"
-gem "compact_index", "~> 0.13.0"
-gem "sinatra", "~> 2.0"
-gem "rake", "13.0.1"
+gem "compact_index", "~> 0.15.0"
+gem "sinatra", "~> 3.0"
+gem "rake", "~> 13.1"
gem "builder", "~> 3.2"
gem "rb_sys"
+gem "rubygems-generate_index", "~> 1.1"
diff --git a/tool/bundler/test_gems.rb.lock b/tool/bundler/test_gems.rb.lock
deleted file mode 100644
index 8b4bb4ddfb..0000000000
--- a/tool/bundler/test_gems.rb.lock
+++ /dev/null
@@ -1,45 +0,0 @@
-GEM
- remote: https://rubygems.org/
- specs:
- builder (3.2.4)
- compact_index (0.13.0)
- mustermann (1.1.2)
- ruby2_keywords (~> 0.0.1)
- rack (2.0.8)
- rack-protection (2.0.8.1)
- rack
- rack-test (1.1.0)
- rack (>= 1.0, < 3)
- rake (13.0.1)
- rb_sys (0.9.63)
- ruby2_keywords (0.0.5)
- sinatra (2.0.8.1)
- mustermann (~> 1.0)
- rack (~> 2.0)
- rack-protection (= 2.0.8.1)
- tilt (~> 2.0)
- tilt (2.0.11)
- webrick (1.7.0)
-
-PLATFORMS
- java
- ruby
- universal-java-11
- universal-java-18
- x64-mingw-ucrt
- x64-mingw32
- x86_64-darwin-20
- x86_64-linux
-
-DEPENDENCIES
- builder (~> 3.2)
- compact_index (~> 0.13.0)
- rack (= 2.0.8)
- rack-test (~> 1.1)
- rake (= 13.0.1)
- rb_sys
- sinatra (~> 2.0)
- webrick (= 1.7.0)
-
-BUNDLED WITH
- 2.5.0.dev
diff --git a/tool/bundler/vendor_gems.rb b/tool/bundler/vendor_gems.rb
new file mode 100644
index 0000000000..f02d02656d
--- /dev/null
+++ b/tool/bundler/vendor_gems.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+source "https://rubygems.org"
+
+gem "fileutils", "1.7.2"
+gem "molinillo", github: "cocoapods/molinillo"
+gem "net-http", "0.4.0"
+gem "net-http-persistent", "4.0.2"
+gem "net-protocol", "0.2.2"
+gem "optparse", "0.4.0"
+gem "pub_grub", github: "jhawthorn/pub_grub"
+gem "resolv", "0.4.0"
+gem "timeout", "0.4.1"
+gem "thor", "1.3.0"
+gem "tsort", "0.2.0"
diff --git a/tool/ci_functions.sh b/tool/ci_functions.sh
deleted file mode 100644
index 7066bbe4ec..0000000000
--- a/tool/ci_functions.sh
+++ /dev/null
@@ -1,29 +0,0 @@
-# -*- BASH -*-
-# Manage functions used on a CI.
-# Run `. tool/ci_functions.sh` to use it.
-
-# Create options with patterns `-n !/name1/ -n !/name2/ ..` to exclude the test
-# method names by the method names `name1 name2 ..`.
-# See `ruby tool/test/runner.rb --help` `-n` option.
-function ci_to_excluded_test_opts {
- local tests_str="${1}"
- # Use the backward matching `!/name$/`, as the perfect matching doesn't work.
- # https://bugs.ruby-lang.org/issues/16936
- ruby <<EOF
- opts = "${tests_str}".split.map { |test| "-n \!/#{test}\$$/" }
- puts opts.join(' ')
-EOF
- return 0
-}
-
-# Create options with patterns `-n name1 -n name2 ..` to include the test
-# method names by the method names `name1 name2 ..`.
-# See `ruby tool/test/runner.rb --help` `-n` option.
-function ci_to_included_test_opts {
- local tests_str="${1}"
- ruby <<EOF
- opts = "${tests_str}".split.map { |test| "-n #{test}" }
- puts opts.join(' ')
-EOF
- return 0
-}
diff --git a/tool/darwin-cc b/tool/darwin-cc
index 6eee96e435..42637022a4 100755
--- a/tool/darwin-cc
+++ b/tool/darwin-cc
@@ -2,5 +2,8 @@
exec 2> >(exec grep -v \
-e '^ld: warning: The [a-z0-9_][a-z0-9_]* architecture is deprecated for macOS' \
-e '^ld: warning: text-based stub file /System/Library/Frameworks/' \
+ -e '^ld: warning: ignoring duplicate libraries:' \
+ -e "warning: '\.debug_macinfo' is not currently supported:" \
+ -e "note: while processing" \
>&2)
exec "$@"
diff --git a/tool/downloader.rb b/tool/downloader.rb
index 0bcd8f31c3..3a91ea0b93 100644
--- a/tool/downloader.rb
+++ b/tool/downloader.rb
@@ -79,6 +79,9 @@ class Downloader
require 'rubygems'
options = options.dup
options[:ssl_ca_cert] = Dir.glob(File.expand_path("../lib/rubygems/ssl_certs/**/*.pem", File.dirname(__FILE__)))
+ if Gem::Version.new(name[/-\K[^-]*(?=\.gem\z)/]).prerelease?
+ options[:ignore_http_client_errors] = true
+ end
super("https://rubygems.org/downloads/#{name}", name, dir, since, options)
end
end
@@ -237,6 +240,7 @@ class Downloader
$stdout.flush
end
mtime = nil
+ ignore_http_client_errors = options.delete(:ignore_http_client_errors)
options = options.merge(http_options(file, since.nil? ? true : since))
begin
data = with_retry(10) do
@@ -247,12 +251,18 @@ class Downloader
data
end
rescue OpenURI::HTTPError => http_error
- if http_error.message =~ /^304 / # 304 Not Modified
+ case http_error.message
+ when /^304 / # 304 Not Modified
if $VERBOSE
$stdout.puts "#{name} not modified"
$stdout.flush
end
return file.to_path
+ when /^40/ # Net::HTTPClientError: 403 Forbidden, 404 Not Found
+ if ignore_http_client_errors
+ puts "Ignore #{url}: #{http_error.message}"
+ return file.to_path
+ end
end
raise
rescue Timeout::Error
@@ -270,6 +280,7 @@ class Downloader
end
dest = (cache_save && cache && !cache.exist? ? cache : file)
dest.parent.mkpath
+ dest.unlink if dest.symlink? && !dest.exist?
dest.open("wb", 0600) do |f|
f.write(data)
f.chmod(mode_for(data))
@@ -406,26 +417,42 @@ if $0 == __FILE__
case ARGV[0]
when '-d', '--destdir'
+ ## -d, --destdir DIRECTORY Download into the directory
destdir = ARGV[1]
ARGV.shift
when '-p', '--prefix'
- # strip directory names from the name to download, and add the
- # prefix instead.
+ ## -p, --prefix Strip directory names from the name to download,
+ ## and add the prefix instead.
prefix = ARGV[1]
ARGV.shift
when '-e', '--exist', '--non-existent-only'
+ ## -e, --exist, --non-existent-only Skip already existent files.
since = nil
when '-a', '--always'
+ ## -a, --always Download all files.
since = false
when '-u', '--update', '--if-modified'
+ ## -u, --update, --if-modified Download newer files only.
since = true
- when '-n', '--dryrun'
+ when '-n', '--dry-run', '--dryrun'
+ ## -n, --dry-run Do not download actually.
options[:dryrun] = true
when '--cache-dir'
+ ## --cache-dir DIRECTORY Cache downloaded files in the directory.
options[:cache_dir] = ARGV[1]
ARGV.shift
when /\A--cache-dir=(.*)/m
options[:cache_dir] = $1
+ when /\A--help\z/
+ ## --help Print this message
+ puts "Usage: #$0 [options] relative-url..."
+ File.foreach(__FILE__) do |line|
+ line.sub!(/^ *## /, "") or next
+ break if line.chomp!.empty?
+ opt, desc = line.split(/ {2,}/, 2)
+ printf " %-28s %s\n", opt, desc
+ end
+ exit
when /\A-/
abort "#{$0}: unknown option #{ARGV[0]}"
else
diff --git a/tool/dummy-rake-compiler/rake/extensiontask.rb b/tool/dummy-rake-compiler/rake/extensiontask.rb
deleted file mode 100644
index 62b7ff8018..0000000000
--- a/tool/dummy-rake-compiler/rake/extensiontask.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-module Rake
- class ExtensionTask < TaskLib
- def initialize(...)
- task :compile do
- puts "Dummy `compile` task defined in #{__FILE__}"
- end
- end
- end
-end
diff --git a/tool/enc-unicode.rb b/tool/enc-unicode.rb
index 3fdbe71634..9d49f427bb 100755
--- a/tool/enc-unicode.rb
+++ b/tool/enc-unicode.rb
@@ -166,7 +166,7 @@ def parse_scripts(data, categories)
categories[current] = file[:title]
(names[file[:title]] ||= []) << current
cps = []
- elsif /^([0-9a-fA-F]+)(?:\.\.([0-9a-fA-F]+))?\s*;\s*(\w+)/ =~ line
+ elsif /^(\h+)(?:\.\.(\h+))?\s*;\s*(\w+)/ =~ line
current = $3
$2 ? cps.concat(($1.to_i(16)..$2.to_i(16)).to_a) : cps.push($1.to_i(16))
end
@@ -221,7 +221,7 @@ def parse_age(data)
ages << current
last_constname = constname
cps = []
- elsif /^([0-9a-fA-F]+)(?:\.\.([0-9a-fA-F]+))?\s*;\s*(\d+\.\d+)/ =~ line
+ elsif /^(\h+)(?:\.\.(\h+))?\s*;\s*(\d+\.\d+)/ =~ line
current = $3
$2 ? cps.concat(($1.to_i(16)..$2.to_i(16)).to_a) : cps.push($1.to_i(16))
end
@@ -240,7 +240,7 @@ def parse_GraphemeBreakProperty(data)
make_const(constname, cps, "Grapheme_Cluster_Break=#{current}")
ages << current
cps = []
- elsif /^([0-9a-fA-F]+)(?:\.\.([0-9a-fA-F]+))?\s*;\s*(\w+)/ =~ line
+ elsif /^(\h+)(?:\.\.(\h+))?\s*;\s*(\w+)/ =~ line
current = $3
$2 ? cps.concat(($1.to_i(16)..$2.to_i(16)).to_a) : cps.push($1.to_i(16))
end
@@ -252,7 +252,7 @@ def parse_block(data)
cps = []
blocks = []
data_foreach('Blocks.txt') do |line|
- if /^([0-9a-fA-F]+)\.\.([0-9a-fA-F]+);\s*(.*)/ =~ line
+ if /^(\h+)\.\.(\h+);\s*(.*)/ =~ line
cps = ($1.to_i(16)..$2.to_i(16)).to_a
constname = constantize_blockname($3)
data[constname] = cps
@@ -269,23 +269,12 @@ def parse_block(data)
blocks << constname
end
-# shim for Ruby 1.8
-unless {}.respond_to?(:key)
- class Hash
- alias key index
- end
-end
-
$const_cache = {}
# make_const(property, pairs, name): Prints a 'static const' structure for a
# given property, group of paired codepoints, and a human-friendly name for
# the group
def make_const(prop, data, name)
- if name.empty?
- puts "\n/* '#{prop}' */"
- else
- puts "\n/* '#{prop}': #{name} */"
- end
+ puts "\n/* '#{prop}': #{name} */" # comment used to generate documentation
if origprop = $const_cache.key(data)
puts "#define CR_#{prop} CR_#{origprop}"
else
@@ -437,8 +426,6 @@ define_posix_props(data)
POSIX_NAMES.each do |name|
if name == 'XPosixPunct'
make_const(name, data[name], "[[:Punct:]]")
- elsif name == 'Punct'
- make_const(name, data[name], "")
else
make_const(name, data[name], "[[:#{name}:]]")
end
@@ -610,7 +597,6 @@ if header
IO.popen([*NAME2CTYPE, out: tmp], "w") {|f| output.show(f, *syms)}
end while syms.pop
fds.each(&:close)
- ff = nil
IO.popen(ifdef["USE_UNICODE_AGE_PROPERTIES", fds[1].path, fds[0].path], "r") {|age|
IO.popen(ifdef["USE_UNICODE_PROPERTIES", fds[2].path, "-"], "r", in: age) {|f|
ansi = false
@@ -621,7 +607,7 @@ if header
line.sub!(/\/\*ANSI\*\//, '1') if ansi
line.gsub!(/\(int\)\((?:long|size_t)\)&\(\(struct uniname2ctype_pool_t \*\)0\)->uniname2ctype_pool_(str\d+),\s+/,
'uniname2ctype_offset(\1), ')
- if ff = (!ff ? /^(uniname2ctype_hash) /=~line : /^\}/!~line) # no line can match both, exclusive flip-flop
+ if line.start_with?("uniname2ctype_hash\s") ... line.start_with?("}")
line.sub!(/^( *(?:register\s+)?(.*\S)\s+hval\s*=\s*)(?=len;)/, '\1(\2)')
end
puts line
diff --git a/tool/fake.rb b/tool/fake.rb
index 91dfb041c4..0366144531 100644
--- a/tool/fake.rb
+++ b/tool/fake.rb
@@ -9,6 +9,15 @@ class File
end
end
+[[libpathenv, "."], [preloadenv, libruby_so]].each do |env, path|
+ env or next
+ e = ENV[env] or next
+ e = e.split(File::PATH_SEPARATOR)
+ path = File.realpath(path, builddir) rescue next
+ e.delete(path) or next
+ ENV[env] = (e.join(File::PATH_SEPARATOR) unless e.empty?)
+end
+
static = !!(defined?($static) && $static)
$:.unshift(builddir)
posthook = proc do
diff --git a/tool/fetch-bundled_gems.rb b/tool/fetch-bundled_gems.rb
index f790c92ba9..f0d3c3cb89 100755
--- a/tool/fetch-bundled_gems.rb
+++ b/tool/fetch-bundled_gems.rb
@@ -1,6 +1,9 @@
#!ruby -an
BEGIN {
require 'fileutils'
+ require_relative 'lib/colorize'
+
+ color = Colorize.new
dir = ARGV.shift
ARGF.eof?
@@ -14,21 +17,23 @@ next unless n
next if n =~ /^#/
if File.directory?(n)
- puts "updating #{n} ..."
- system("git", "fetch", chdir: n) or abort
+ puts "updating #{color.notice(n)} ..."
+ system("git", "fetch", "--all", chdir: n) or abort
else
- puts "retrieving #{n} ..."
+ puts "retrieving #{color.notice(n)} ..."
system(*%W"git clone #{u} #{n}") or abort
end
if r
- puts "fetching #{r} ..."
+ puts "fetching #{color.notice(r)} ..."
system("git", "fetch", "origin", r, chdir: n) or abort
end
c = r || "v#{v}"
checkout = %w"git -c advice.detachedHead=false checkout"
-puts "checking out #{c} (v=#{v}, r=#{r}) ..."
+print %[checking out #{color.notice(c)} (v=#{color.info(v)}]
+print %[, r=#{color.info(r)}] if r
+puts ") ..."
unless system(*checkout, c, "--", chdir: n)
abort if r or !system(*checkout, v, "--", chdir: n)
end
diff --git a/tool/format-release b/tool/format-release
index 7b0f8e78e7..737148e0ce 100755
--- a/tool/format-release
+++ b/tool/format-release
@@ -235,11 +235,11 @@ eom
diff.each_with_index do |line, index|
case index
when 0
- line.sub!(/\A--- (.*)\t(\d+-\d+-\d+ [0-9:.]+ [\-+]\d+)\Z/) do
+ line.sub!(/\A--- (.*)\t(\d+-\d+-\d+ [0-9:.]+(?: [\-+]\d+)?)\Z/) do
"--- a/#{filename}\t#{$2}"
end
when 1
- line.sub!(/\A\+\+\+ (.*)\t(\d+-\d+-\d+ [0-9:.]+ [\-+]\d+)\Z/) do
+ line.sub!(/\A\+\+\+ (.*)\t(\d+-\d+-\d+ [0-9:.]+(?: [\-+]\d+)?)\Z/) do
"+++ b/#{filename}\t#{$2}"
end
end
diff --git a/tool/gen-github-release.rb b/tool/gen-github-release.rb
index 4d8c2910f4..cdb66080d9 100755
--- a/tool/gen-github-release.rb
+++ b/tool/gen-github-release.rb
@@ -27,10 +27,12 @@ client = Octokit::Client.new
note = "## What's Changed\n\n"
+notes = []
+
diff = client.compare("ruby/ruby", ARGV[0], ARGV[1])
diff[:commits].each do |c|
- if c[:commit][:message] =~ /\[Backport #(\d*)\]/
- url = "https://bugs.ruby-lang.org/issues/#{$1}"
+ if c[:commit][:message] =~ /\[(Backport|Feature|Bug) #(\d*)\]/
+ url = "https://bugs.ruby-lang.org/issues/#{$2}"
title = Nokogiri::HTML(URI.open(url)).title
title.gsub!(/ - Ruby master - Ruby Issue Tracking System/, "")
elsif c[:commit][:message] =~ /\(#(\d*)\)/
@@ -40,19 +42,24 @@ diff[:commits].each do |c|
else
next
end
- note << "* [#{title}](#{url})\n"
+ notes << "* [#{title}](#{url})"
rescue OpenURI::HTTPError
puts "Error: #{url}"
end
-note << "\n"
+notes.uniq!
+
+note << notes.join("\n")
+
+note << "\n\n"
note << "Note: This list is automatically generated by tool/gen-github-release.rb. Because of this, some commits may be missing.\n\n"
note << "## Full Changelog\n\n"
note << "https://github.com/ruby/ruby/compare/#{ARGV[0]}...#{ARGV[1]}\n\n"
if ARGV[2] == "--no-dry-run"
- name = ARGV[1].gsub(/v/, "").gsub(/_/, ".")
- client.create_release("ruby/ruby", ARGV[1], name: name, body: note)
+ name = ARGV[1].gsub(/^v/, "").gsub(/_/, ".")
+ prerelease = ARGV[1].match?(/rc|preview/) ? true : false
+ client.create_release("ruby/ruby", ARGV[1], name: name, body: note, make_latest: "false", prerelease: prerelease)
puts "Created a release: https://github.com/ruby/ruby/releases/tag/#{ARGV[1]}"
else
puts note
diff --git a/tool/generic_erb.rb b/tool/generic_erb.rb
index 6607d5c256..9326309ff6 100644
--- a/tool/generic_erb.rb
+++ b/tool/generic_erb.rb
@@ -27,11 +27,7 @@ vpath = out.vpath
output, vpath = output, vpath
result = templates.map do |template|
- if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
- erb = ERB.new(File.read(template), trim_mode: '%-')
- else
- erb = ERB.new(File.read(template), nil, '%-')
- end
+ erb = ERB.new(File.read(template), trim_mode: '%-')
erb.filename = template
source ? erb.src : proc{erb.result(binding)}.call
end
diff --git a/tool/leaked-globals b/tool/leaked-globals
index e079b0efc5..4aa2aff996 100755
--- a/tool/leaked-globals
+++ b/tool/leaked-globals
@@ -1,19 +1,28 @@
#!/usr/bin/ruby
require_relative 'lib/colorize'
+require 'shellwords'
until ARGV.empty?
case ARGV[0]
- when /\ASYMBOL_PREFIX=(.*)/
+ when /\A SYMBOL_PREFIX=(.*)/x
SYMBOL_PREFIX = $1
- when /\ANM=(.*)/ # may be multiple words
- NM = $1
- when /\APLATFORM=(.+)?/
+ when /\A NM=(.*)/x # may be multiple words
+ NM = $1.shellsplit
+ when /\A PLATFORM=(.+)?/x
platform = $1
+ when /\A SOEXT=(.+)?/x
+ soext = $1
+ when /\A SYMBOLS_IN_EMPTYLIB=(.*)/x
+ SYMBOLS_IN_EMPTYLIB = $1.split(" ")
+ when /\A EXTSTATIC=(.+)?/x
+ EXTSTATIC = true
else
break
end
ARGV.shift
end
+SYMBOLS_IN_EMPTYLIB ||= nil
+EXTSTATIC ||= false
config = ARGV.shift
count = 0
@@ -39,23 +48,57 @@ if platform and !platform.empty?
end
missing = File.dirname(config) + "/missing/"
ARGV.reject! do |n|
- unless (src = Dir.glob(missing + File.basename(n, ".*") + ".[cS]")).empty?
+ base = File.basename(n, ".*")
+ next true if REPLACE.include?(base)
+ unless (src = Dir.glob(missing + base + ".[cS]")).empty?
puts "Ignore #{col.skip(n)} because of #{src.map {|s| File.basename(s)}.join(', ')} under missing"
true
end
end
+
# darwin's ld64 seems to require exception handling personality functions to be
# extern, so we allow the Rust one.
REPLACE.push("rust_eh_personality") if RUBY_PLATFORM.include?("darwin")
print "Checking leaked global symbols..."
STDOUT.flush
-IO.foreach("|#{NM} #{ARGV.join(' ')}") do |line|
+if soext
+ soext = /\.#{soext}(?:$|\.)/
+ if EXTSTATIC
+ ARGV.delete_if {|n| soext =~ n}
+ elsif ARGV.size == 1
+ so = soext =~ ARGV.first
+ end
+end
+
+Pipe = Struct.new(:command) do
+ def open(&block) IO.popen(command, &block) end
+ def each(&block) open {|f| f.each(&block)} end
+end
+
+Pipe.new(NM + ARGV).each do |line|
+ line.chomp!
+ next so = nil if line.empty?
+ if so.nil? and line.chomp!(":")
+ so = soext =~ line || false
+ next
+ end
n, t, = line.split
next unless /[A-TV-Z]/ =~ t
next unless n.sub!(/^#{SYMBOL_PREFIX}/o, "")
next if n.include?(".")
- next if /\A(?:Init_|InitVM_|RUBY_|ruby_|rb_|[Oo]nig|dln_|coroutine_)/ =~ n
+ next if !so and n.start_with?("___asan_")
+ next if !so and n.start_with?("__odr_asan_")
+ case n
+ when /\A(?:Init_|InitVM_|pm_|[Oo]nig|dln_|coroutine_)/
+ next
+ when /\Aruby_static_id_/
+ next unless so
+ when /\A(?:RUBY_|ruby_|rb_)/
+ next unless so and /_(threadptr|ec)_/ =~ n
+ when *SYMBOLS_IN_EMPTYLIB
+ next
+ end
next if REPLACE.include?(n)
puts col.fail("leaked") if count.zero?
count += 1
diff --git a/tool/lib/_tmpdir.rb b/tool/lib/_tmpdir.rb
new file mode 100644
index 0000000000..fd429dab37
--- /dev/null
+++ b/tool/lib/_tmpdir.rb
@@ -0,0 +1,100 @@
+template = "rubytest."
+
+# This path is only for tests.
+# Assume the directory by these environment variables are safe.
+base = [ENV["TMPDIR"], ENV["TMP"], "/tmp"].find do |tmp|
+ next unless tmp and tmp.size <= 50 and File.directory?(tmp)
+ # On macOS, the default TMPDIR is very long, inspite of UNIX socket
+ # path length is limited.
+ #
+ # Also Rubygems creates its own temporary directory per tests, and
+ # some tests copy the full path of gemhome there. In that caes, the
+ # path contains both temporary names twice, and can exceed path name
+ # limit very easily.
+ tmp
+end
+begin
+ tmpdir = File.join(base, template + Random.new_seed.to_s(36)[-6..-1])
+ Dir.mkdir(tmpdir, 0o700)
+rescue Errno::EEXIST
+ retry
+end
+# warn "tmpdir(#{tmpdir.size}) = #{tmpdir}"
+
+pid = $$
+END {
+ if pid == $$
+ begin
+ Dir.rmdir(tmpdir)
+ rescue Errno::ENOENT
+ rescue Errno::ENOTEMPTY
+ require_relative "colorize"
+ colorize = Colorize.new
+ ls = Struct.new(:colorize) do
+ def mode_inspect(m, s)
+ [
+ (m & 0o4 == 0 ? ?- : ?r),
+ (m & 0o2 == 0 ? ?- : ?w),
+ (m & 0o1 == 0 ? (s ? s.upcase : ?-) : (s || ?x)),
+ ]
+ end
+ def decorate_path(path, st)
+ case
+ when st.directory?
+ color = "bold;blue"
+ type = "/"
+ when st.symlink?
+ color = "bold;cyan"
+ # type = "@"
+ when st.executable?
+ color = "bold;green"
+ type = "*"
+ when path.end_with?(".gem")
+ color = "green"
+ end
+ colorize.decorate(path, color) + (type || "")
+ end
+ def list_tree(parent, indent = "", &block)
+ children = Dir.children(parent).map do |child|
+ [child, path = File.join(parent, child), File.lstat(path)]
+ end
+ nlink_width = children.map {|child, path, st| st.nlink}.max.to_s.size
+ size_width = children.map {|child, path, st| st.size}.max.to_s.size
+
+ children.each do |child, path, st|
+ m = st.mode
+ m = [
+ (st.file? ? ?- : st.ftype[0]),
+ mode_inspect(m >> 6, (?s unless m & 04000 == 0)),
+ mode_inspect(m >> 3, (?s unless m & 02000 == 0)),
+ mode_inspect(m, (?t unless m & 01000 == 0)),
+ ].join("")
+ warn sprintf("%s* %s %*d %*d %s % s%s",
+ indent, m, nlink_width, st.nlink, size_width, st.size,
+ st.mtime.to_s, decorate_path(child, st),
+ (" -> " + decorate_path(File.readlink(path), File.stat(path)) if
+ st.symlink?))
+ if st.directory?
+ list_tree(File.join(parent, child), indent + " ", &block)
+ end
+ yield path, st if block
+ end
+ end
+ end.new(colorize)
+ warn colorize.notice("Children under ")+colorize.fail(tmpdir)+":"
+ Dir.chdir(tmpdir) do
+ ls.list_tree(".") do |path, st|
+ if st.directory?
+ Dir.rmdir(path)
+ else
+ File.unlink(path)
+ end
+ end
+ end
+ require "fileutils"
+ FileUtils.rm_rf(tmpdir)
+ end
+ end
+}
+
+ENV["TMPDIR"] = ENV["SPEC_TEMP_DIR"] = ENV["GEM_TEST_TMPDIR"] = tmpdir
diff --git a/tool/lib/bundled_gem.rb b/tool/lib/bundled_gem.rb
index bfc0be1c81..3ba27f6d64 100644
--- a/tool/lib/bundled_gem.rb
+++ b/tool/lib/bundled_gem.rb
@@ -6,6 +6,14 @@ require 'rubygems/package'
# unpack bundled gem files.
module BundledGem
+ DEFAULT_GEMS_DEPENDENCIES = [
+ "net-protocol", # net-ftp
+ "time", # net-ftp
+ "singleton", # prime
+ "ipaddr", # rinda
+ "forwardable" # prime, rinda
+ ]
+
module_function
def unpack(file, *rest)
@@ -55,6 +63,9 @@ module BundledGem
gem_dir = File.join(dir, "gems", target)
yield gem_dir
spec_dir = spec.extensions.empty? ? "specifications" : File.join("gems", target)
+ if spec.extensions.empty?
+ spec.dependencies.reject! {|dep| DEFAULT_GEMS_DEPENDENCIES.include?(dep.name)}
+ end
File.binwrite(File.join(dir, spec_dir, "#{target}.gemspec"), spec.to_ruby)
unless spec.extensions.empty?
spec.dependencies.clear
diff --git a/tool/lib/colorize.rb b/tool/lib/colorize.rb
index 8fb90e1833..0904312119 100644
--- a/tool/lib/colorize.rb
+++ b/tool/lib/colorize.rb
@@ -7,7 +7,7 @@ class Colorize
def initialize(color = nil, opts = ((_, color = color, nil)[0] if Hash === color))
@colors = @reset = nil
@color = opts && opts[:color] || color
- if color or (color == nil && STDOUT.tty? && (ENV["NO_COLOR"] || "").empty?)
+ if color or (color == nil && coloring?)
if (%w[smso so].any? {|attr| /\A\e\[.*m\z/ =~ IO.popen("tput #{attr}", "r", :err => IO::NULL, &:read)} rescue nil)
@beg = "\e["
colors = (colors = ENV['TEST_COLORS']) ? Hash[colors.scan(/(\w+)=([^:\n]*)/)] : {}
@@ -27,23 +27,48 @@ class Colorize
end
DEFAULTS = {
- "pass"=>"32", "fail"=>"31;1", "skip"=>"33;1",
+ # color names
"black"=>"30", "red"=>"31", "green"=>"32", "yellow"=>"33",
"blue"=>"34", "magenta"=>"35", "cyan"=>"36", "white"=>"37",
"bold"=>"1", "underline"=>"4", "reverse"=>"7",
+ "bright_black"=>"90", "bright_red"=>"91", "bright_green"=>"92", "bright_yellow"=>"93",
+ "bright_blue"=>"94", "bright_magenta"=>"95", "bright_cyan"=>"96", "bright_white"=>"97",
+
+ # abstract decorations
+ "pass"=>"green", "fail"=>"red;bold", "skip"=>"yellow;bold",
+ "note"=>"bright_yellow", "notice"=>"bright_yellow", "info"=>"bright_magenta",
}
- NO_COLOR = (nc = ENV['NO_COLOR']) && !nc.empty?
+ def coloring?
+ STDOUT.tty? && (!(nc = ENV['NO_COLOR']) || nc.empty?)
+ end
# colorize.decorate(str, name = color_name)
def decorate(str, name = @color)
- if !NO_COLOR and @colors and color = (@colors[name] || DEFAULTS[name])
+ if coloring? and color = resolve_color(name)
"#{@beg}#{color}m#{str}#{@reset}"
else
str
end
end
+ def resolve_color(color = @color, seen = {}, colors = nil)
+ return unless @colors
+ color.to_s.gsub(/\b[a-z][\w ]+/) do |n|
+ n.gsub!(/\W+/, "_")
+ n.downcase!
+ c = seen[n] and next c
+ if colors
+ c = colors[n]
+ elsif (c = (tbl = @colors)[n] || (tbl = DEFAULTS)[n])
+ colors = tbl
+ else
+ next n
+ end
+ seen[n] = resolve_color(c, seen, colors)
+ end
+ end
+
DEFAULTS.each_key do |name|
define_method(name) {|str|
decorate(str, name)
diff --git a/tool/lib/core_assertions.rb b/tool/lib/core_assertions.rb
index 4887d944c5..b456a55b34 100644
--- a/tool/lib/core_assertions.rb
+++ b/tool/lib/core_assertions.rb
@@ -1,6 +1,43 @@
# frozen_string_literal: true
module Test
+
+ class << self
+ ##
+ # Filter object for backtraces.
+
+ attr_accessor :backtrace_filter
+ end
+
+ class BacktraceFilter # :nodoc:
+ def filter bt
+ return ["No backtrace"] unless bt
+
+ new_bt = []
+ pattern = %r[/(?:lib\/test/|core_assertions\.rb:)]
+
+ unless $DEBUG then
+ bt.each do |line|
+ break if pattern.match?(line)
+ new_bt << line
+ end
+
+ new_bt = bt.reject { |line| pattern.match?(line) } if new_bt.empty?
+ new_bt = bt.dup if new_bt.empty?
+ else
+ new_bt = bt.dup
+ end
+
+ new_bt
+ end
+ end
+
+ self.backtrace_filter = BacktraceFilter.new
+
+ def self.filter_backtrace bt # :nodoc:
+ backtrace_filter.filter bt
+ end
+
module Unit
module Assertions
def assert_raises(*exp, &b)
@@ -37,6 +74,11 @@ module Test
module CoreAssertions
require_relative 'envutil'
require 'pp'
+ begin
+ require '-test-/asan'
+ rescue LoadError
+ end
+
nil.pretty_inspect
def mu_pp(obj) #:nodoc:
@@ -115,6 +157,9 @@ module Test
pend 'assert_no_memory_leak may consider RJIT memory usage as leak' if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?
# For previous versions which implemented MJIT
pend 'assert_no_memory_leak may consider MJIT memory usage as leak' if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled?
+ # ASAN has the same problem - its shadow memory greatly increases memory usage
+ # (plus asan has better ways to detect memory leaks than this assertion)
+ pend 'assert_no_memory_leak may consider ASAN memory usage as leak' if defined?(Test::ASAN) && Test::ASAN.enabled?
require_relative 'memory_status'
raise Test::Unit::PendedError, "unsupported platform" unless defined?(Memory::Status)
@@ -741,14 +786,16 @@ eom
%w[
CLOCK_THREAD_CPUTIME_ID CLOCK_PROCESS_CPUTIME_ID
CLOCK_MONOTONIC
- ].find do |clk|
- if Process.const_defined?(clk)
- [clk.to_sym, Process.const_get(clk)].find do |clk|
- Process.clock_gettime(clk)
- rescue
- # Constants may be defined but not implemented, e.g., mingw.
- else
- PERFORMANCE_CLOCK = clk
+ ].find do |c|
+ if Process.const_defined?(c)
+ [c.to_sym, Process.const_get(c)].find do |clk|
+ begin
+ Process.clock_gettime(clk)
+ rescue
+ # Constants may be defined but not implemented, e.g., mingw.
+ else
+ PERFORMANCE_CLOCK = clk
+ end
end
end
end
diff --git a/tool/lib/envutil.rb b/tool/lib/envutil.rb
index 728ca7059b..642965047f 100644
--- a/tool/lib/envutil.rb
+++ b/tool/lib/envutil.rb
@@ -15,23 +15,22 @@ end
module EnvUtil
def rubybin
if ruby = ENV["RUBY"]
- return ruby
- end
- ruby = "ruby"
- exeext = RbConfig::CONFIG["EXEEXT"]
- rubyexe = (ruby + exeext if exeext and !exeext.empty?)
- 3.times do
- if File.exist? ruby and File.executable? ruby and !File.directory? ruby
- return File.expand_path(ruby)
- end
- if rubyexe and File.exist? rubyexe and File.executable? rubyexe
- return File.expand_path(rubyexe)
- end
- ruby = File.join("..", ruby)
- end
- if defined?(RbConfig.ruby)
+ ruby
+ elsif defined?(RbConfig.ruby)
RbConfig.ruby
else
+ ruby = "ruby"
+ exeext = RbConfig::CONFIG["EXEEXT"]
+ rubyexe = (ruby + exeext if exeext and !exeext.empty?)
+ 3.times do
+ if File.exist? ruby and File.executable? ruby and !File.directory? ruby
+ return File.expand_path(ruby)
+ end
+ if rubyexe and File.exist? rubyexe and File.executable? rubyexe
+ return File.expand_path(rubyexe)
+ end
+ ruby = File.join("..", ruby)
+ end
"ruby"
end
end
@@ -53,7 +52,14 @@ module EnvUtil
@original_internal_encoding = Encoding.default_internal
@original_external_encoding = Encoding.default_external
@original_verbose = $VERBOSE
- @original_warning = defined?(Warning.[]) ? %i[deprecated experimental].to_h {|i| [i, Warning[i]]} : nil
+ @original_warning =
+ if defined?(Warning.categories)
+ Warning.categories.to_h {|i| [i, Warning[i]]}
+ elsif defined?(Warning.[]) # 2.7+
+ %i[deprecated experimental performance].to_h do |i|
+ [i, begin Warning[i]; rescue ArgumentError; end]
+ end.compact
+ end
end
end
@@ -155,7 +161,7 @@ module EnvUtil
# remain env
%w(ASAN_OPTIONS RUBY_ON_BUG).each{|name|
- child_env[name] = ENV[name] if ENV[name]
+ child_env[name] = ENV[name] if !child_env.key?(name) and ENV.key?(name)
}
args = [args] if args.kind_of?(String)
@@ -246,6 +252,24 @@ module EnvUtil
end
module_function :under_gc_stress
+ def under_gc_compact_stress(val = :empty, &block)
+ raise "compaction doesn't work well on s390x. Omit the test in the caller." if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077
+ auto_compact = GC.auto_compact
+ GC.auto_compact = val
+ under_gc_stress(&block)
+ ensure
+ GC.auto_compact = auto_compact
+ end
+ module_function :under_gc_compact_stress
+
+ def without_gc
+ prev_disabled = GC.disable
+ yield
+ ensure
+ GC.enable unless prev_disabled
+ end
+ module_function :without_gc
+
def with_default_external(enc)
suppress_warning { Encoding.default_external = enc }
yield
diff --git a/tool/lib/helper.rb b/tool/lib/helper.rb
deleted file mode 100644
index 909f8f98af..0000000000
--- a/tool/lib/helper.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-require "test/unit"
-require_relative "core_assertions"
-
-Test::Unit::TestCase.include Test::Unit::CoreAssertions
diff --git a/tool/lib/iseq_loader_checker.rb b/tool/lib/iseq_loader_checker.rb
index 3f07b3a999..73784f8450 100644
--- a/tool/lib/iseq_loader_checker.rb
+++ b/tool/lib/iseq_loader_checker.rb
@@ -76,6 +76,15 @@ class RubyVM::InstructionSequence
# return value
i2_bin if CHECK_TO_BINARY
end if CHECK_TO_A || CHECK_TO_BINARY
+
+ if opt == "prism"
+ # If RUBY_ISEQ_DUMP_DEBUG is "prism", we'll set up
+ # InstructionSequence.load_iseq to intercept loading filepaths to compile
+ # using prism.
+ def self.load_iseq(filepath)
+ RubyVM::InstructionSequence.compile_file_prism(filepath)
+ end
+ end
end
#require_relative 'x'; exit(1)
diff --git a/tool/lib/output.rb b/tool/lib/output.rb
index 51e3d32010..8cb426ae4a 100644
--- a/tool/lib/output.rb
+++ b/tool/lib/output.rb
@@ -4,21 +4,34 @@ require_relative 'colorize'
class Output
attr_reader :path, :vpath
- def initialize
- @path = @timestamp = @ifchange = @color = nil
- @vpath = VPath.new
+ def initialize(path: nil, timestamp: nil, ifchange: nil, color: nil,
+ overwrite: false, create_only: false, vpath: VPath.new)
+ @path = path
+ @timestamp = timestamp
+ @ifchange = ifchange
+ @color = color
+ @overwrite = overwrite
+ @create_only = create_only
+ @vpath = vpath
end
+ COLOR_WHEN = {
+ 'always' => true, 'auto' => nil, 'never' => false,
+ nil => true, false => false,
+ }
+
def def_options(opt)
opt.separator(" Output common options:")
opt.on('-o', '--output=PATH') {|v| @path = v}
opt.on('-t', '--timestamp[=PATH]') {|v| @timestamp = v || true}
opt.on('-c', '--[no-]if-change') {|v| @ifchange = v}
- opt.on('--color') {@color = true}
+ opt.on('--[no-]color=[WHEN]', COLOR_WHEN.keys) {|v| @color = COLOR_WHEN[v]}
+ opt.on('--[no-]create-only') {|v| @create_only = v}
+ opt.on('--[no-]overwrite') {|v| @overwrite = v}
@vpath.def_options(opt)
end
- def write(data, overwrite: false, create_only: false)
+ def write(data, overwrite: @overwrite, create_only: @create_only)
unless @path
$stdout.print data
return true
diff --git a/tool/lib/path.rb b/tool/lib/path.rb
new file mode 100644
index 0000000000..5582b2851e
--- /dev/null
+++ b/tool/lib/path.rb
@@ -0,0 +1,101 @@
+module Path
+ module_function
+
+ def clean(path)
+ path = "#{path}/".gsub(/(\A|\/)(?:\.\/)+/, '\1').tr_s('/', '/')
+ nil while path.sub!(/[^\/]+\/\.\.\//, '')
+ path
+ end
+
+ def relative(path, base)
+ path = clean(path)
+ base = clean(base)
+ path, base = [path, base].map{|s|s.split("/")}
+ until path.empty? or base.empty? or path[0] != base[0]
+ path.shift
+ base.shift
+ end
+ path, base = [path, base].map{|s|s.join("/")}
+ if base.empty?
+ path
+ elsif base.start_with?("../") or File.absolute_path?(base)
+ File.expand_path(path)
+ else
+ base.gsub!(/[^\/]+/, '..')
+ File.join(base, path)
+ end
+ end
+
+ def clean_link(src, dest)
+ begin
+ link = File.readlink(dest)
+ rescue
+ else
+ return if link == src
+ File.unlink(dest)
+ end
+ yield src, dest
+ end
+
+ # Extensions to FileUtils
+
+ module Mswin
+ def ln_safe(src, dest, *opt)
+ cmd = ["mklink", dest.tr("/", "\\"), src.tr("/", "\\")]
+ cmd[1, 0] = opt
+ return if system("cmd", "/c", *cmd)
+ # TODO: use RUNAS or something
+ puts cmd.join(" ")
+ end
+
+ def ln_dir_safe(src, dest)
+ ln_safe(src, dest, "/d")
+ end
+ end
+
+ module HardlinkExcutable
+ def ln_exe(src, dest)
+ ln(src, dest, force: true)
+ end
+ end
+
+ def ln_safe(src, dest)
+ ln_sf(src, dest)
+ rescue Errno::ENOENT
+ # Windows disallows to create broken symboic links, probably because
+ # it is a kind of reparse points.
+ raise if File.exist?(src)
+ end
+
+ alias ln_dir_safe ln_safe
+ alias ln_exe ln_safe
+
+ def ln_relative(src, dest, executable = false)
+ return if File.identical?(src, dest)
+ parent = File.dirname(dest)
+ File.directory?(parent) or mkdir_p(parent)
+ if executable
+ return (ln_exe(src, dest) if File.exist?(src))
+ end
+ clean_link(relative(src, parent), dest) {|s, d| ln_safe(s, d)}
+ end
+
+ def ln_dir_relative(src, dest)
+ return if File.identical?(src, dest)
+ parent = File.dirname(dest)
+ File.directory?(parent) or mkdir_p(parent)
+ clean_link(relative(src, parent), dest) {|s, d| ln_dir_safe(s, d)}
+ end
+
+ case (CROSS_COMPILING || RUBY_PLATFORM)
+ when /linux|darwin|solaris/
+ prepend HardlinkExcutable
+ extend HardlinkExcutable
+ when /mingw|mswin/
+ unless File.respond_to?(:symlink)
+ prepend Mswin
+ extend Mswin
+ end
+ else
+ end
+end
diff --git a/tool/lib/test/unit.rb b/tool/lib/test/unit.rb
index 9ce3c8b30a..d758b5fb02 100644
--- a/tool/lib/test/unit.rb
+++ b/tool/lib/test/unit.rb
@@ -24,42 +24,6 @@ require 'optparse'
# See Test::Unit
module Test
- class << self
- ##
- # Filter object for backtraces.
-
- attr_accessor :backtrace_filter
- end
-
- class BacktraceFilter # :nodoc:
- def filter bt
- return ["No backtrace"] unless bt
-
- new_bt = []
- pattern = %r[/(?:lib\/test/|core_assertions\.rb:)]
-
- unless $DEBUG then
- bt.each do |line|
- break if pattern.match?(line)
- new_bt << line
- end
-
- new_bt = bt.reject { |line| pattern.match?(line) } if new_bt.empty?
- new_bt = bt.dup if new_bt.empty?
- else
- new_bt = bt.dup
- end
-
- new_bt
- end
- end
-
- self.backtrace_filter = BacktraceFilter.new
-
- def self.filter_backtrace bt # :nodoc:
- backtrace_filter.filter bt
- end
-
##
# Test::Unit is an implementation of the xUnit testing framework for Ruby.
module Unit
@@ -89,17 +53,7 @@ module Test
end
end
- module RJITFirst
- def group(list)
- # RJIT first
- rjit, others = list.partition {|e| /test_rjit/ =~ e}
- rjit + others
- end
- end
-
class Alpha < NoSort
- include RJITFirst
-
def sort_by_name(list)
list.sort_by(&:name)
end
@@ -112,8 +66,6 @@ module Test
# shuffle test suites based on CRC32 of their names
Shuffle = Struct.new(:seed, :salt) do
- include RJITFirst
-
def initialize(seed)
self.class::CRC_TBL ||= (0..255).map {|i|
(0..7).inject(i) {|c,| (c & 1 == 1) ? (0xEDB88320 ^ (c >> 1)) : (c >> 1) }
@@ -131,6 +83,10 @@ module Test
list.sort_by {|e| randomize_key(e)}
end
+ def group(list)
+ list
+ end
+
private
def crc32(str, crc32 = 0xffffffff)
@@ -303,7 +259,7 @@ module Test
r.close_on_exec = true
w.close_on_exec = true
@jobserver = [r, w]
- options[:parallel] ||= 1
+ options[:parallel] ||= 256 # number of tokens to acquire first
end
end
@worker_timeout = EnvUtil.apply_timeout_scale(options[:worker_timeout] || 180)
@@ -346,7 +302,8 @@ module Test
options[:retry] = false
end
- opts.on '--ruby VAL', "Path to ruby which is used at -j option" do |a|
+ opts.on '--ruby VAL', "Path to ruby which is used at -j option",
+ "Also used as EnvUtil.rubybin by some assertion methods" do |a|
options[:ruby] = a.split(/ /).reject(&:empty?)
end
@@ -696,10 +653,18 @@ module Test
@ios = [] # Array of worker IOs
@job_tokens = String.new(encoding: Encoding::ASCII_8BIT) if @jobserver
begin
- [@tasks.size, @options[:parallel]].min.times {launch_worker}
-
while true
- timeout = [(@workers.filter_map {|w| w.response_at}.min&.-(Time.now) || 0) + @worker_timeout, 1].max
+ newjobs = [@tasks.size, @options[:parallel]].min - @workers.size
+ if newjobs > 0
+ if @jobserver
+ t = @jobserver[0].read_nonblock(newjobs, exception: false)
+ @job_tokens << t if String === t
+ newjobs = @job_tokens.size + 1 - @workers.size
+ end
+ newjobs.times {launch_worker}
+ end
+
+ timeout = [(@workers.filter_map {|w| w.response_at}.min&.-(Time.now) || 0), 0].max + @worker_timeout
if !(_io = IO.select(@ios, nil, nil, timeout))
timeout = Time.now - @worker_timeout
@@ -713,15 +678,9 @@ module Test
}
break
end
- break if @tasks.empty? and @workers.empty?
- if @jobserver and @job_tokens and !@tasks.empty? and
- ((newjobs = [@tasks.size, @options[:parallel]].min) > @workers.size or
- !@workers.any? {|x| x.status == :ready})
- t = @jobserver[0].read_nonblock(newjobs, exception: false)
- if String === t
- @job_tokens << t
- t.size.times {launch_worker}
- end
+ if @tasks.empty?
+ break if @workers.empty?
+ next # wait for all workers to finish
end
end
rescue Interrupt => ex
@@ -757,7 +716,15 @@ module Test
del_status_line or puts
error, suites = suites.partition {|r| r[:error]}
unless suites.empty?
- puts "\n""Retrying..."
+ puts "\n"
+ @failed_output.puts "Failed tests:"
+ suites.each {|r|
+ r[:report].each {|c, m, e|
+ @failed_output.puts "#{c}##{m}: #{e&.class}: #{e&.message&.slice(/\A.*/)}"
+ }
+ }
+ @failed_output.puts "\n"
+ puts "Retrying..."
@verbose = options[:verbose]
suites.map! {|r| ::Object.const_get(r[:testcase])}
_run_suites(suites, type)
@@ -883,7 +850,7 @@ module Test
end
end
- def record(suite, method, assertions, time, error)
+ def record(suite, method, assertions, time, error, source_location = nil)
if @options.values_at(:longest, :most_asserted).any?
@tops ||= {}
rec = [suite.name, method, assertions, time, error]
@@ -981,7 +948,7 @@ module Test
end
def _prepare_run(suites, type)
- options[:job_status] ||= :replace if @tty && !@verbose
+ options[:job_status] ||= @tty ? :replace : :normal unless @verbose
case options[:color]
when :always
color = true
@@ -997,11 +964,14 @@ module Test
@output = Output.new(self) unless @options[:testing]
filter = options[:filter]
type = "#{type}_methods"
- total = if filter
- suites.inject(0) {|n, suite| n + suite.send(type).grep(filter).size}
- else
- suites.inject(0) {|n, suite| n + suite.send(type).size}
- end
+ total = suites.sum {|suite|
+ methods = suite.send(type)
+ if filter
+ methods.count {|method| filter === "#{suite}##{method}"}
+ else
+ methods.size
+ end
+ }
@test_count = 0
@total_tests = total.to_s(10)
end
@@ -1095,7 +1065,7 @@ module Test
runner.add_status(" = #$1")
when /\A\.+\z/
runner.succeed
- when /\A\.*[EFS][EFS.]*\z/
+ when /\A\.*[EFST][EFST.]*\z/
runner.failed(s)
else
$stdout.print(s)
@@ -1391,6 +1361,182 @@ module Test
end
end
+ module LaunchableOption
+ module Nothing
+ private
+ def setup_options(opts, options)
+ super
+ opts.define_tail 'Launchable options:'
+ # This is expected to be called by Test::Unit::Worker.
+ opts.on_tail '--launchable-test-reports=PATH', String, 'Do nothing'
+ end
+ end
+
+ def record(suite, method, assertions, time, error, source_location = nil)
+ if writer = @options[:launchable_test_reports]
+ if loc = (source_location || suite.instance_method(method).source_location)
+ path, lineno = loc
+ # Launchable JSON schema is defined at
+ # https://github.com/search?q=repo%3Alaunchableinc%2Fcli+https%3A%2F%2Flaunchableinc.com%2Fschema%2FRecordTestInput&type=code.
+ e = case error
+ when nil
+ status = 'TEST_PASSED'
+ nil
+ when Test::Unit::PendedError
+ status = 'TEST_SKIPPED'
+ "Skipped:\n#{suite.name}##{method} [#{location error}]:\n#{error.message}\n"
+ when Test::Unit::AssertionFailedError
+ status = 'TEST_FAILED'
+ "Failure:\n#{suite.name}##{method} [#{location error}]:\n#{error.message}\n"
+ when Timeout::Error
+ status = 'TEST_FAILED'
+ "Timeout:\n#{suite.name}##{method}\n"
+ else
+ status = 'TEST_FAILED'
+ bt = Test::filter_backtrace(error.backtrace).join "\n "
+ "Error:\n#{suite.name}##{method}:\n#{error.class}: #{error.message.b}\n #{bt}\n"
+ end
+ repo_path = File.expand_path("#{__dir__}/../../../")
+ relative_path = path.delete_prefix("#{repo_path}/")
+ # The test path is a URL-encoded representation.
+ # https://github.com/launchableinc/cli/blob/v1.81.0/launchable/testpath.py#L18
+ test_path = {file: relative_path, class: suite.name, testcase: method}.map{|key, val|
+ "#{encode_test_path_component(key)}=#{encode_test_path_component(val)}"
+ }.join('#')
+ end
+ end
+ super
+ ensure
+ if writer && test_path && status
+ # Occasionally, the file writing operation may be paused, especially when `--repeat-count` is specified.
+ # In such cases, we proceed to execute the operation here.
+ writer.write_object(
+ {
+ testPath: test_path,
+ status: status,
+ duration: time,
+ createdAt: Time.now.to_s,
+ stderr: e,
+ stdout: nil,
+ data: {
+ lineNumber: lineno
+ }
+ }
+ )
+ end
+ end
+
+ private
+ def setup_options(opts, options)
+ super
+ opts.on_tail '--launchable-test-reports=PATH', String, 'Report test results in Launchable JSON format' do |path|
+ require 'json'
+ require 'uri'
+ options[:launchable_test_reports] = writer = JsonStreamWriter.new(path)
+ writer.write_array('testCases')
+ main_pid = Process.pid
+ at_exit {
+ # This block is executed when the fork block in a test is completed.
+ # Therefore, we need to verify whether all tests have been completed.
+ stack = caller
+ if stack.size == 0 && main_pid == Process.pid && $!.is_a?(SystemExit)
+ writer.close
+ end
+ }
+ end
+
+ def encode_test_path_component component
+ component.to_s.gsub('%', '%25').gsub('=', '%3D').gsub('#', '%23').gsub('&', '%26')
+ end
+ end
+
+ ##
+ # JsonStreamWriter writes a JSON file using a stream.
+ # By utilizing a stream, we can minimize memory usage, especially for large files.
+ class JsonStreamWriter
+ def initialize(path)
+ @file = File.open(path, "w")
+ @file.write("{")
+ @indent_level = 0
+ @is_first_key_val = true
+ @is_first_obj = true
+ write_new_line
+ end
+
+ def write_object obj
+ if @is_first_obj
+ @is_first_obj = false
+ else
+ write_comma
+ write_new_line
+ end
+ @indent_level += 1
+ @file.write(to_json_str(obj))
+ @indent_level -= 1
+ @is_first_key_val = true
+ # Occasionally, invalid JSON will be created as shown below, especially when `--repeat-count` is specified.
+ # {
+ # "testPath": "file=test%2Ftest_timeout.rb&class=TestTimeout&testcase=test_allows_zero_seconds",
+ # "status": "TEST_PASSED",
+ # "duration": 2.7e-05,
+ # "createdAt": "2024-02-09 12:21:07 +0000",
+ # "stderr": null,
+ # "stdout": null
+ # }: null <- here
+ # },
+ # To prevent this, IO#flush is called here.
+ @file.flush
+ end
+
+ def write_array(key)
+ @indent_level += 1
+ @file.write(to_json_str(key))
+ write_colon
+ @file.write(" ", "[")
+ write_new_line
+ end
+
+ def close
+ return if @file.closed?
+ close_array
+ @indent_level -= 1
+ write_new_line
+ @file.write("}", "\n")
+ @file.flush
+ @file.close
+ end
+
+ private
+ def to_json_str(obj)
+ json = JSON.pretty_generate(obj)
+ json.gsub(/^/, ' ' * (2 * @indent_level))
+ end
+
+ def write_indent
+ @file.write(" " * 2 * @indent_level)
+ end
+
+ def write_new_line
+ @file.write("\n")
+ end
+
+ def write_comma
+ @file.write(',')
+ end
+
+ def write_colon
+ @file.write(":")
+ end
+
+ def close_array
+ write_new_line
+ write_indent
+ @file.write("]")
+ @indent_level -= 1
+ end
+ end
+ end
+
class Runner # :nodoc: all
attr_accessor :report, :failures, :errors, :skips # :nodoc:
@@ -1580,7 +1726,7 @@ module Test
_start_method(inst)
inst._assertions = 0
- print "#{suite}##{method} = " if @verbose
+ print "#{suite}##{method.inspect.sub(/\A:/, '')} = " if @verbose
start_time = Time.now if @verbose
result =
@@ -1628,16 +1774,16 @@ module Test
# failure or error in teardown, it will be sent again with the
# error or failure.
- def record suite, method, assertions, time, error
+ def record suite, method, assertions, time, error, source_location = nil
end
def location e # :nodoc:
last_before_assertion = ""
- return '<empty>' unless e.backtrace # SystemStackError can return nil.
+ return '<empty>' unless e&.backtrace # SystemStackError can return nil.
e.backtrace.reverse_each do |s|
- break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
+ break if s =~ /in .(?:Test::Unit::(?:Core)?Assertions#)?(assert|refute|flunk|pass|fail|raise|must|wont)/
last_before_assertion = s
end
last_before_assertion.sub(/:in .*$/, '')
@@ -1719,6 +1865,7 @@ module Test
prepend Test::Unit::ExcludesOption
prepend Test::Unit::TimeoutOption
prepend Test::Unit::RunCount
+ prepend Test::Unit::LaunchableOption::Nothing
##
# Begins the full test run. Delegates to +runner+'s #_run method.
@@ -1775,6 +1922,7 @@ module Test
class AutoRunner # :nodoc: all
class Runner < Test::Unit::Runner
include Test::Unit::RequireFiles
+ include Test::Unit::LaunchableOption
end
attr_accessor :to_run, :options
diff --git a/tool/lib/test/unit/parallel.rb b/tool/lib/test/unit/parallel.rb
index 407e3fa1a2..ac297d4a0e 100644
--- a/tool/lib/test/unit/parallel.rb
+++ b/tool/lib/test/unit/parallel.rb
@@ -1,12 +1,6 @@
# frozen_string_literal: true
-$LOAD_PATH.unshift "#{__dir__}/../.."
-require_relative '../../test/unit'
-require_relative '../../profile_test_all' if ENV.key?('RUBY_TEST_ALL_PROFILE')
-require_relative '../../tracepointchecker'
-require_relative '../../zombie_hunter'
-require_relative '../../iseq_loader_checker'
-require_relative '../../gc_checker'
+require_relative "../../../test/init"
module Test
module Unit
@@ -186,7 +180,7 @@ module Test
else
error = ProxyError.new(error)
end
- _report "record", Marshal.dump([suite.name, method, assertions, time, error])
+ _report "record", Marshal.dump([suite.name, method, assertions, time, error, suite.instance_method(method).source_location])
super
end
end
diff --git a/tool/lib/test/unit/testcase.rb b/tool/lib/test/unit/testcase.rb
index 44d9ba7fdb..51ffff37eb 100644
--- a/tool/lib/test/unit/testcase.rb
+++ b/tool/lib/test/unit/testcase.rb
@@ -137,6 +137,9 @@ module Test
attr_reader :__name__ # :nodoc:
+ # Method name of this test.
+ alias method_name __name__
+
PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException,
Interrupt, SystemExit] # :nodoc:
@@ -144,8 +147,7 @@ module Test
# Runs the tests reporting the status to +runner+
def run runner
- @options = runner.options
-
+ @__runner_options__ = runner.options
trap "INFO" do
runner.report.each_with_index do |msg, i|
warn "\n%3d) %s" % [i + 1, msg]
@@ -161,7 +163,7 @@ module Test
result = ""
begin
- @passed = nil
+ @__passed__ = nil
self.before_setup
self.setup
self.after_setup
@@ -169,11 +171,11 @@ module Test
result = "." unless io?
time = Time.now - start_time
runner.record self.class, self.__name__, self._assertions, time, nil
- @passed = true
+ @__passed__ = true
rescue *PASSTHROUGH_EXCEPTIONS
raise
rescue Exception => e
- @passed = Test::Unit::PendedError === e
+ @__passed__ = Test::Unit::PendedError === e
time = Time.now - start_time
runner.record self.class, self.__name__, self._assertions, time, e
result = runner.puke self.class, self.__name__, e
@@ -184,7 +186,7 @@ module Test
rescue *PASSTHROUGH_EXCEPTIONS
raise
rescue Exception => e
- @passed = false
+ @__passed__ = false
runner.record self.class, self.__name__, self._assertions, time, e
result = runner.puke self.class, self.__name__, e
end
@@ -206,12 +208,12 @@ module Test
def initialize name # :nodoc:
@__name__ = name
@__io__ = nil
- @passed = nil
- @@current = self # FIX: make thread local
+ @__passed__ = nil
+ @@__current__ = self # FIX: make thread local
end
def self.current # :nodoc:
- @@current # FIX: make thread local
+ @@__current__ # FIX: make thread local
end
##
@@ -263,7 +265,7 @@ module Test
# Returns true if the test passed.
def passed?
- @passed
+ @__passed__
end
##
diff --git a/tool/lib/vcs.rb b/tool/lib/vcs.rb
index 650f275250..3894f9c8e8 100644
--- a/tool/lib/vcs.rb
+++ b/tool/lib/vcs.rb
@@ -154,8 +154,7 @@ class VCS
alias dryrun? dryrun
alias debug? debug
- NullDevice = defined?(IO::NULL) ? IO::NULL :
- %w[/dev/null NUL NIL: NL:].find {|dev| File.exist?(dev)}
+ NullDevice = IO::NULL
# returns
# * the last revision of the current branch
@@ -227,6 +226,7 @@ class VCS
def after_export(dir)
FileUtils.rm_rf(Dir.glob("#{dir}/.git*"))
+ FileUtils.rm_rf(Dir.glob("#{dir}/.mailmap"))
end
def revision_handler(rev)
@@ -681,7 +681,10 @@ class VCS
def format_changelog(path, arg, base_url = nil)
env = {'TZ' => 'JST-9', 'LANG' => 'C', 'LC_ALL' => 'C'}
- cmd = %W"#{COMMAND} log --format=fuller --notes=commits --notes=log-fix --topo-order --no-merges"
+ cmd = %W[#{COMMAND} log
+ --format=fuller --notes=commits --notes=log-fix --topo-order --no-merges
+ --fixed-strings --invert-grep --grep=[ci\ skip] --grep=[skip\ ci]
+ ]
date = "--date=iso-local"
unless system(env, *cmd, date, "-1", chdir: @srcdir, out: NullDevice, exception: false)
date = "--date=iso"
@@ -694,20 +697,49 @@ class VCS
cmd_pipe(env, cmd, chdir: @srcdir) do |r|
while s = r.gets("\ncommit ")
h, s = s.split(/^$/, 2)
+
+ next if /^Author: *dependabot\[bot\]/ =~ h
+
h.gsub!(/^(?:(?:Author|Commit)(?:Date)?|Date): /, ' \&')
if s.sub!(/\nNotes \(log-fix\):\n((?: +.*\n)+)/, '')
fix = $1
s = s.lines
fix.each_line do |x|
+ next unless x.sub!(/^(\s+)(?:(\d+)|\$(?:-\d+)?)/, '')
+ b = ($2&.to_i || (s.size - 1 + $3.to_i))
+ sp = $1
+ if x.sub!(/^,(?:(\d+)|\$(?:-\d+)?)/, '')
+ range = b..($1&.to_i || (s.size - 1 + $2.to_i))
+ else
+ range = b..b
+ end
case x
- when %r[^ +(\d+)s([#{LOG_FIX_REGEXP_SEPARATORS}])(.+)\2(.*)\2]o
- n = $1.to_i
- wrong = $3
- correct = $4
- begin
- s[n][wrong] = correct
- rescue IndexError
- message = ["format_changelog failed to replace #{wrong.dump} with #{correct.dump} at #$1\n"]
+ when %r[^s([#{LOG_FIX_REGEXP_SEPARATORS}])(.+)\1(.*)\1([gr]+)?]o
+ wrong = $2
+ correct = $3
+ if opt = $4 and opt.include?("r") # regexp
+ wrong = Regexp.new(wrong)
+ correct.gsub!(/(?<!\\)(?:\\\\)*\K(?:\\n)+/) {"\n" * ($&.size / 2)}
+ sub = opt.include?("g") ? :gsub! : :sub!
+ else
+ sub = false
+ end
+ range.each do |n|
+ if sub
+ ss = s[n].sub(/^#{sp}/, "") # un-indent for /^/
+ if ss.__send__(sub, wrong, correct)
+ s[n, 1] = ss.lines.map {|l| "#{sp}#{l}"}
+ next
+ end
+ else
+ begin
+ s[n][wrong] = correct
+ rescue IndexError
+ else
+ next
+ end
+ end
+ message = ["format_changelog failed to replace #{wrong.dump} with #{correct.dump} at #{n}\n"]
from = [1, n-2].max
to = [s.size-1, n+2].min
s.each_with_index do |e, i|
@@ -717,12 +749,13 @@ class VCS
end
raise message.join('')
end
- when %r[^( +)(\d+)i([#{LOG_FIX_REGEXP_SEPARATORS}])(.*)\3]o
- s[$2.to_i, 0] = "#{$1}#{$4}\n"
- when %r[^ +(\d+)(?:,(\d+))?d]
- n = $1.to_i
- e = $2
- s[n..(e ? e.to_i : n)] = []
+ when %r[^i([#{LOG_FIX_REGEXP_SEPARATORS}])(.*)\1]o
+ insert = "#{sp}#{$2}\n"
+ range.reverse_each do |n|
+ s[n, 0] = insert
+ end
+ when %r[^d]
+ s[range] = []
end
end
s = s.join('')
diff --git a/tool/lib/webrick/httprequest.rb b/tool/lib/webrick/httprequest.rb
index d34eac7ecf..258ee37a38 100644
--- a/tool/lib/webrick/httprequest.rb
+++ b/tool/lib/webrick/httprequest.rb
@@ -402,7 +402,7 @@ module WEBrick
# This method provides the metavariables defined by the revision 3
# of "The WWW Common Gateway Interface Version 1.1"
# To browse the current document of CGI Version 1.1, see below:
- # http://tools.ietf.org/html/rfc3875
+ # https://www.rfc-editor.org/rfc/rfc3875
def meta_vars
meta = Hash.new
diff --git a/tool/ln_sr.rb b/tool/ln_sr.rb
index 2aa8391e17..e1b5b6f76b 100755
--- a/tool/ln_sr.rb
+++ b/tool/ln_sr.rb
@@ -96,7 +96,7 @@ unless respond_to?(:ln_sr)
while c = comp.shift
if c == ".." and clean.last != ".." and !(fu_have_symlink? && File.symlink?(path))
clean.pop
- path.chomp!(%r((?<=\A|/)[^/]+/\z), "")
+ path.sub!(%r((?<=\A|/)[^/]+/\z), "")
else
clean << c
path << c << "/"
diff --git a/tool/lrama/LEGAL.md b/tool/lrama/LEGAL.md
new file mode 100644
index 0000000000..a3ef848514
--- /dev/null
+++ b/tool/lrama/LEGAL.md
@@ -0,0 +1,12 @@
+# LEGAL NOTICE INFORMATION
+
+All the files in this distribution are covered under the MIT License except some files
+mentioned below.
+
+## GNU General Public License version 3
+
+These files are licensed under the GNU General Public License version 3 or later. See these files for more information.
+
+* template/bison/_yacc.h
+* template/bison/yacc.c
+* template/bison/yacc.h
diff --git a/tool/lrama/MIT b/tool/lrama/MIT
new file mode 100644
index 0000000000..b23d5210d5
--- /dev/null
+++ b/tool/lrama/MIT
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2023 Yuichiro Kaneko
+
+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.
diff --git a/tool/lrama/NEWS.md b/tool/lrama/NEWS.md
new file mode 100644
index 0000000000..96aaaf94f5
--- /dev/null
+++ b/tool/lrama/NEWS.md
@@ -0,0 +1,382 @@
+# NEWS for Lrama
+
+## Lrama 0.6.5 (2024-03-25)
+
+### Typed Midrule Actions
+
+User can specify the type of mid rule action by tag (`<bar>`) instead of specifying it with in an action.
+
+```
+primary: k_case expr_value terms?
+ {
+ $<val>$ = p->case_labels;
+ p->case_labels = Qnil;
+ }
+ case_body
+ k_end
+ {
+ ...
+ }
+```
+
+can be written as
+
+```
+primary: k_case expr_value terms?
+ {
+ $$ = p->case_labels;
+ p->case_labels = Qnil;
+ }<val>
+ case_body
+ k_end
+ {
+ ...
+ }
+```
+
+`%destructor` for midrule action is invoked only when tag is specified by Typed Midrule Actions.
+
+Difference from Bison's Typed Midrule Actions is that tag is postposed in Lrama however it's preposed in Bison.
+
+Bison supports this feature from 3.1.
+
+## Lrama 0.6.4 (2024-03-22)
+
+### Parameterizing rules (preceded, terminated, delimited)
+
+Support `preceded`, `terminated` and `delimited` rules.
+
+```
+program: preceded(opening, X)
+
+// Expanded to
+
+program: preceded_opening_X
+preceded_opening_X: opening X
+```
+
+```
+program: terminated(X, closing)
+
+// Expanded to
+
+program: terminated_X_closing
+terminated_X_closing: X closing
+```
+
+```
+program: delimited(opening, X, closing)
+
+// Expanded to
+
+program: delimited_opening_X_closing
+delimited_opening_X_closing: opening X closing
+```
+
+https://github.com/ruby/lrama/pull/382
+
+### Support `%destructor` declaration
+
+User can set codes for freeing semantic value resources by using `%destructor`.
+In general, these resources are freed by actions or after parsing.
+However if syntax error happens in parsing, these codes may not be executed.
+Codes associated to `%destructor` are executed when semantic value is popped from the stack by an error.
+
+```
+%token <val1> NUM
+%type <val2> expr2
+%type <val3> expr
+
+%destructor {
+ printf("destructor for val1: %d\n", $$);
+} <val1> // printer for TAG
+
+%destructor {
+ printf("destructor for val2: %d\n", $$);
+} <val2>
+
+%destructor {
+ printf("destructor for expr: %d\n", $$);
+} expr // printer for symbol
+```
+
+Bison supports this feature from 1.75b.
+
+https://github.com/ruby/lrama/pull/385
+
+## Lrama 0.6.3 (2024-02-15)
+
+### Bring Your Own Stack
+
+Provide functionalities for Bring Your Own Stack.
+
+Ruby’s Ripper library requires their own semantic value stack to manage Ruby Objects returned by user defined callback method. Currently Ripper uses semantic value stack (`yyvsa`) which is used by parser to manage Node. This hack introduces some limitation on Ripper. For example, Ripper can not execute semantic analysis depending on Node structure.
+
+Lrama introduces two features to support another semantic value stack by parser generator users.
+
+1. Callback entry points
+
+User can emulate semantic value stack by these callbacks.
+Lrama provides these five callbacks. Registered functions are called when each event happen. For example %after-shift function is called when shift happens on original semantic value stack.
+
+* `%after-shift` function_name
+* `%before-reduce` function_name
+* `%after-reduce` function_name
+* `%after-shift-error-token` function_name
+* `%after-pop-stack` function_name
+
+2. `$:n` variable to access index of each grammar symbols
+
+User also needs to access semantic value of their stack in grammar action. `$:n` provides the way to access to it. `$:n` is translated to the minus index from the top of the stack.
+For example
+
+```
+primary: k_if expr_value then compstmt if_tail k_end
+ {
+ /*% ripper: if!($:2, $:4, $:5) %*/
+ /* $:2 = -5, $:4 = -3, $:5 = -2. */
+ }
+```
+
+https://github.com/ruby/lrama/pull/367
+
+## Lrama 0.6.2 (2024-01-27)
+
+### %no-stdlib directive
+
+If `%no-stdlib` directive is set, Lrama doesn't load Lrama standard library for
+parameterizing rules, stdlib.y.
+
+https://github.com/ruby/lrama/pull/344
+
+## Lrama 0.6.1 (2024-01-13)
+
+### Nested parameterizing rules
+
+Allow to pass an instantiated rule to other parameterizing rules.
+
+```
+%rule constant(X) : X
+ ;
+
+%rule option(Y) : /* empty */
+ | Y
+ ;
+
+%%
+
+program : option(constant(number)) // Nested rule
+ ;
+%%
+```
+
+Allow to use nested parameterizing rules when define parameterizing rules.
+
+```
+%rule option(x) : /* empty */
+ | X
+ ;
+
+%rule double(Y) : Y Y
+ ;
+
+%rule double_opt(A) : option(double(A)) // Nested rule
+ ;
+
+%%
+
+program : double_opt(number)
+ ;
+
+%%
+```
+
+https://github.com/ruby/lrama/pull/337
+
+## Lrama 0.6.0 (2023-12-25)
+
+### User defined parameterizing rules
+
+Allow to define parameterizing rule by `%rule` directive.
+
+```
+%rule pair(X, Y): X Y { $$ = $1 + $2; }
+ ;
+
+%%
+
+program: stmt
+ ;
+
+stmt: pair(ODD, EVEN) <num>
+ | pair(EVEN, ODD) <num>
+ ;
+```
+
+https://github.com/ruby/lrama/pull/285
+
+## Lrama 0.5.11 (2023-12-02)
+
+### Type specification of parameterizing rules
+
+Allow to specify type of rules by specifying tag, `<i>` in below example.
+Tag is post-modification style.
+
+```
+%union {
+ int i;
+}
+
+%%
+
+program : option(number) <i>
+ | number_alias? <i>
+ ;
+```
+
+https://github.com/ruby/lrama/pull/272
+
+
+## Lrama 0.5.10 (2023-11-18)
+
+### Parameterizing rules (option, nonempty_list, list)
+
+Support function call style parameterizing rules for `option`, `nonempty_list` and `list`.
+
+https://github.com/ruby/lrama/pull/197
+
+### Parameterizing rules (separated_list)
+
+Support `separated_list` and `separated_nonempty_list` parameterizing rules.
+
+```
+program: separated_list(',', number)
+
+// Expanded to
+
+program: separated_list_number
+separated_list_number: ε
+separated_list_number: separated_nonempty_list_number
+separated_nonempty_list_number: number
+separated_nonempty_list_number: separated_nonempty_list_number ',' number
+```
+
+```
+program: separated_nonempty_list(',', number)
+
+// Expanded to
+
+program: separated_nonempty_list_number
+separated_nonempty_list_number: number
+separated_nonempty_list_number: separated_nonempty_list_number ',' number
+```
+
+https://github.com/ruby/lrama/pull/204
+
+## Lrama 0.5.9 (2023-11-05)
+
+### Parameterizing rules (suffix)
+
+Parameterizing rules are template of rules.
+It's very common pattern to write "list" grammar rule like:
+
+```
+opt_args: /* none */
+ | args
+ ;
+
+args: arg
+ | args arg
+```
+
+Lrama supports these suffixes:
+
+* `?`: option
+* `+`: nonempty list
+* `*`: list
+
+Idea of Parameterizing rules comes from Menhir LR(1) parser generator (https://gallium.inria.fr/~fpottier/menhir/manual.html#sec32).
+
+https://github.com/ruby/lrama/pull/181
+
+## Lrama 0.5.7 (2023-10-23)
+
+### Racc parser
+
+Replace Lrama's parser from hand written parser to LR parser generated by Racc.
+Lrama uses `--embedded` option to generate LR parser because Racc is changed from default gem to bundled gem by Ruby 3.3 (https://github.com/ruby/lrama/pull/132).
+
+https://github.com/ruby/lrama/pull/62
+
+## Lrama 0.5.4 (2023-08-17)
+
+### Runtime configuration for error recovery
+
+Meke error recovery function configurable on runtime by two new macros.
+
+* `YYMAXREPAIR`: Expected to return max length of repair operations. `%parse-param` is passed to this function.
+* `YYERROR_RECOVERY_ENABLED`: Expected to return bool value to determine error recovery is enabled or not. `%parse-param` is passed to this function.
+
+https://github.com/ruby/lrama/pull/74
+
+## Lrama 0.5.3 (2023-08-05)
+
+### Error Recovery
+
+Support token insert base Error Recovery.
+`-e` option is needed to generate parser with error recovery functions.
+
+https://github.com/ruby/lrama/pull/44
+
+## Lrama 0.5.2 (2023-06-14)
+
+### Named References
+
+Instead of positional references like `$1` or `$$`,
+named references allow to access to symbol by name.
+
+```
+primary: k_class cpath superclass bodystmt k_end
+ {
+ $primary = new_class($cpath, $bodystmt, $superclass);
+ }
+```
+
+Alias name can be declared.
+
+```
+expr[result]: expr[ex-left] '+' expr[ex.right]
+ {
+ $result = $[ex-left] + $[ex.right];
+ }
+```
+
+Bison supports this feature from 2.5.
+
+### Add parse params to some macros and functions
+
+`%parse-param` are added to these macros and functions to remove ytab.sed hack from Ruby.
+
+* `YY_LOCATION_PRINT`
+* `YY_SYMBOL_PRINT`
+* `yy_stack_print`
+* `YY_STACK_PRINT`
+* `YY_REDUCE_PRINT`
+* `yysyntax_error`
+
+https://github.com/ruby/lrama/pull/40
+
+See also: https://github.com/ruby/ruby/pull/7807
+
+## Lrama 0.5.0 (2023-05-17)
+
+### stdin mode
+
+When `-` is given as grammar file name, reads the grammar source from STDIN, and takes the next argument as the input file name. This mode helps pre-process a grammar source.
+
+https://github.com/ruby/lrama/pull/8
+
+## Lrama 0.4.0 (2023-05-13)
+
+This is the first version migrated to Ruby.
+This version generates "parse.c" compatible with Bison 3.8.2.
diff --git a/tool/lrama/exe/lrama b/tool/lrama/exe/lrama
index 5c61e1a684..ba5fb06c82 100755
--- a/tool/lrama/exe/lrama
+++ b/tool/lrama/exe/lrama
@@ -1,6 +1,5 @@
#!/usr/bin/env ruby
-
$LOAD_PATH << File.join(__dir__, "../lib")
require "lrama"
diff --git a/tool/lrama/lib/lrama.rb b/tool/lrama/lib/lrama.rb
index 19f579c330..9e517b0d71 100644
--- a/tool/lrama/lib/lrama.rb
+++ b/tool/lrama/lib/lrama.rb
@@ -1,9 +1,12 @@
require "lrama/bitmap"
require "lrama/command"
require "lrama/context"
+require "lrama/counterexamples"
require "lrama/digraph"
require "lrama/grammar"
require "lrama/lexer"
+require "lrama/option_parser"
+require "lrama/options"
require "lrama/output"
require "lrama/parser"
require "lrama/report"
diff --git a/tool/lrama/lib/lrama/command.rb b/tool/lrama/lib/lrama/command.rb
index b1485b73c5..12fc4fc7ec 100644
--- a/tool/lrama/lib/lrama/command.rb
+++ b/tool/lrama/lib/lrama/command.rb
@@ -1,148 +1,73 @@
-require 'optparse'
-
module Lrama
class Command
- def run(argv)
- opt = OptionParser.new
-
- # opt.on('-h') {|v| p v }
- opt.on('-V', '--version') {|v| puts Lrama::VERSION ; exit 0 }
-
- # Tuning the Parser
- skeleton = "bison/yacc.c"
-
- opt.on('-S', '--skeleton=FILE') {|v| skeleton = v }
- opt.on('-t') { } # Do nothing
-
- # Output Files:
- header = false
- header_file = nil
- report = []
- report_file = nil
- outfile = "y.tab.c"
-
- opt.on('-h', '--header=[FILE]') {|v| header = true; header_file = v }
- opt.on('-d') { header = true }
- opt.on('-r', '--report=THINGS') {|v| report = v.split(',') }
- opt.on('--report-file=FILE') {|v| report_file = v }
- opt.on('-v') { } # Do nothing
- opt.on('-o', '--output=FILE') {|v| outfile = v }
-
- # Hidden
- trace = []
- opt.on('--trace=THINGS') {|v| trace = v.split(',') }
-
- # Error Recovery
- error_recovery = false
- opt.on('-e') {|v| error_recovery = true }
-
- opt.parse!(argv)
+ LRAMA_LIB = File.realpath(File.join(File.dirname(__FILE__)))
+ STDLIB_FILE_PATH = File.join(LRAMA_LIB, 'grammar', 'stdlib.y')
- trace_opts = validate_trace(trace)
- report_opts = validate_report(report)
-
- grammar_file = argv.shift
-
- if !report.empty? && report_file.nil? && grammar_file
- report_file = File.dirname(grammar_file) + "/" + File.basename(grammar_file, ".*") + ".output"
- end
-
- if !header_file && header
- case
- when outfile
- header_file = File.dirname(outfile) + "/" + File.basename(outfile, ".*") + ".h"
- when grammar_file
- header_file = File.dirname(grammar_file) + "/" + File.basename(grammar_file, ".*") + ".h"
- end
- end
-
- if !grammar_file
- abort "File should be specified\n"
+ def run(argv)
+ begin
+ options = OptionParser.new.parse(argv)
+ rescue => e
+ message = e.message
+ message = message.gsub(/.+/, "\e[1m\\&\e[m") if Exception.to_tty?
+ abort message
end
- Report::Duration.enable if trace_opts[:time]
+ Report::Duration.enable if options.trace_opts[:time]
warning = Lrama::Warning.new
- if grammar_file == '-'
- grammar_file = argv.shift or abort "File name for STDIN should be specified\n"
- y = STDIN.read
- else
- y = File.read(grammar_file)
+ text = options.y.read
+ options.y.close if options.y != STDIN
+ begin
+ grammar = Lrama::Parser.new(text, options.grammar_file, options.debug).parse
+ unless grammar.no_stdlib
+ stdlib_grammar = Lrama::Parser.new(File.read(STDLIB_FILE_PATH), STDLIB_FILE_PATH, options.debug).parse
+ grammar.insert_before_parameterizing_rules(stdlib_grammar.parameterizing_rules)
+ end
+ grammar.prepare
+ grammar.validate!
+ rescue => e
+ raise e if options.debug
+ message = e.message
+ message = message.gsub(/.+/, "\e[1m\\&\e[m") if Exception.to_tty?
+ abort message
end
- grammar = Lrama::Parser.new(y).parse
- states = Lrama::States.new(grammar, warning, trace_state: (trace_opts[:automaton] || trace_opts[:closure]))
+ states = Lrama::States.new(grammar, warning, trace_state: (options.trace_opts[:automaton] || options.trace_opts[:closure]))
states.compute
context = Lrama::Context.new(states)
- if report_file
+ if options.report_file
reporter = Lrama::StatesReporter.new(states)
- File.open(report_file, "w+") do |f|
- reporter.report(f, **report_opts)
+ File.open(options.report_file, "w+") do |f|
+ reporter.report(f, **options.report_opts)
end
end
- File.open(outfile, "w+") do |f|
+ if options.trace_opts && options.trace_opts[:rules]
+ puts "Grammar rules:"
+ puts grammar.rules
+ end
+
+ if options.trace_opts && options.trace_opts[:actions]
+ puts "Grammar rules with actions:"
+ grammar.rules.each { |rule| puts rule.with_actions }
+ end
+
+ File.open(options.outfile, "w+") do |f|
Lrama::Output.new(
out: f,
- output_file_path: outfile,
- template_name: skeleton,
- grammar_file_path: grammar_file,
- header_file_path: header_file,
+ output_file_path: options.outfile,
+ template_name: options.skeleton,
+ grammar_file_path: options.grammar_file,
+ header_file_path: options.header_file,
context: context,
grammar: grammar,
+ error_recovery: options.error_recovery,
).render
end
if warning.has_error?
- exit 1
- end
- end
-
- private
-
- def validate_report(report)
- bison_list = %w[states itemsets lookaheads solved counterexamples cex all none]
- others = %w[verbose]
- list = bison_list + others
- not_supported = %w[counterexamples cex none]
- h = { grammar: true }
-
- report.each do |r|
- if list.include?(r) && !not_supported.include?(r)
- h[r.to_sym] = true
- else
- raise "Invalid report option \"#{r}\"."
- end
- end
-
- if h[:all]
- (bison_list - not_supported).each do |r|
- h[r.to_sym] = true
- end
-
- h.delete(:all)
- end
-
- return h
- end
-
- def validate_trace(trace)
- list = %w[
- none locations scan parse automaton bitsets
- closure grammar resource sets muscles tools
- m4-early m4 skeleton time ielr cex all
- ]
- h = {}
-
- trace.each do |t|
- if list.include?(t)
- h[t.to_sym] = true
- else
- raise "Invalid trace option \"#{t}\"."
- end
+ exit false
end
-
- return h
end
end
end
diff --git a/tool/lrama/lib/lrama/context.rb b/tool/lrama/lib/lrama/context.rb
index 9086470011..32017e65fc 100644
--- a/tool/lrama/lib/lrama/context.rb
+++ b/tool/lrama/lib/lrama/context.rb
@@ -1,4 +1,4 @@
-require "lrama/report"
+require "lrama/report/duration"
module Lrama
# This is passed to a template
@@ -9,7 +9,7 @@ module Lrama
BaseMin = -Float::INFINITY
# TODO: It might be better to pass `states` to Output directly?
- attr_reader :states
+ attr_reader :states, :yylast, :yypact_ninf, :yytable_ninf, :yydefact, :yydefgoto
def initialize(states)
@states = states
@@ -41,15 +41,11 @@ module Lrama
def yyfinal
@states.states.find do |state|
state.items.find do |item|
- item.rule.lhs.id.s_value == "$accept" && item.end_of_rule?
+ item.lhs.accept_symbol? && item.end_of_rule?
end
end.id
end
- def yylast
- @yylast
- end
-
# Number of terms
def yyntokens
@states.terms.count
@@ -89,6 +85,16 @@ module Lrama
return a
end
+ def yytranslate_inverted
+ a = Array.new(@states.symbols.count, @states.undef_symbol.token_id)
+
+ @states.terms.each do |term|
+ a[term.number] = term.token_id
+ end
+
+ return a
+ end
+
# Mapping from rule number to line number of the rule is defined.
# Dummy rule is appended as the first element whose value is 0
# because 0 means error in yydefact.
@@ -109,30 +115,14 @@ module Lrama
end
end
- def yypact_ninf
- @yypact_ninf
- end
-
- def yytable_ninf
- @yytable_ninf
- end
-
def yypact
@base[0...yynstates]
end
- def yydefact
- @yydefact
- end
-
def yypgoto
@base[yynstates..-1]
end
- def yydefgoto
- @yydefgoto
- end
-
def yytable
@table
end
@@ -160,7 +150,7 @@ module Lrama
return a
end
- # Mapping from rule number to lenght of RHS.
+ # Mapping from rule number to length of RHS.
# Dummy rule is appended as the first element whose value is 0
# because 0 means error in yydefact.
def yyr2
@@ -204,7 +194,7 @@ module Lrama
(rule_id + 1) * -1
end
- # Symbol number is assinged to term first then nterm.
+ # Symbol number is assigned to term first then nterm.
# This method calculates sequence_number for nterm.
def nterm_number_to_sequence_number(nterm_number)
nterm_number - @states.terms.count
@@ -231,7 +221,7 @@ module Lrama
if state.reduces.map(&:selected_look_ahead).any? {|la| !la.empty? }
# Iterate reduces with reverse order so that first rule is used.
- state.reduces.reverse.each do |reduce|
+ state.reduces.reverse_each do |reduce|
reduce.look_ahead.each do |term|
actions[term.number] = rule_id_to_action_number(reduce.rule.id)
end
@@ -249,7 +239,7 @@ module Lrama
actions[conflict.symbol.number] = ErrorActionNumber
end
- # If default_reduction_rule, replase default_reduction_rule in
+ # If default_reduction_rule, replace default_reduction_rule in
# actions with zero.
if state.default_reduction_rule
actions.map! do |e|
@@ -262,7 +252,7 @@ module Lrama
end
# If no default_reduction_rule, default behavior is an
- # error then replase ErrorActionNumber with zero.
+ # error then replace ErrorActionNumber with zero.
if !state.default_reduction_rule
actions.map! do |e|
if e == ErrorActionNumber
@@ -275,9 +265,9 @@ module Lrama
s = actions.each_with_index.map do |n, i|
[i, n]
- end.select do |i, n|
+ end.reject do |i, n|
# Remove default_reduction_rule entries
- n != 0
+ n == 0
end
if s.count != 0
@@ -299,10 +289,8 @@ module Lrama
# Index is sequence number of nterm, value is state id
# of a default nterm transition destination.
@yydefgoto = Array.new(@states.nterms.count, 0)
- h = {}
# Mapping from nterm to next_states
nterm_to_next_states = {}
- terms_count = @states.terms.count
@states.states.each do |state|
state.nterm_transitions.each do |shift, next_state|
@@ -359,7 +347,7 @@ module Lrama
end
j = @sorted_actions.count - 1
- state_id, froms_and_tos, count, width = action
+ _state_id, _froms_and_tos, count, width = action
while (j >= 0) do
case
@@ -391,7 +379,6 @@ module Lrama
end
print sprintf("]\n\n")
-
print sprintf("width [\n")
vectors_count.times do |i|
print sprintf("%d, ", ary[i] ? ary[i][3] : 0)
@@ -399,7 +386,6 @@ module Lrama
end
print sprintf("]\n\n")
-
print sprintf("tally [\n")
vectors_count.times do |i|
print sprintf("%d, ", ary[i] ? ary[i][2] : 0)
@@ -476,7 +462,7 @@ module Lrama
@yylast = high
# replace_ninf
- @yypact_ninf = (@base.select {|i| i != BaseMin } + [0]).min - 1
+ @yypact_ninf = (@base.reject {|i| i == BaseMin } + [0]).min - 1
@base.map! do |i|
case i
when BaseMin
@@ -486,7 +472,7 @@ module Lrama
end
end
- @yytable_ninf = (@table.compact.select {|i| i != ErrorActionNumber } + [0]).min - 1
+ @yytable_ninf = (@table.compact.reject {|i| i == ErrorActionNumber } + [0]).min - 1
@table.map! do |i|
case i
when nil
diff --git a/tool/lrama/lib/lrama/counterexamples.rb b/tool/lrama/lib/lrama/counterexamples.rb
new file mode 100644
index 0000000000..046265da59
--- /dev/null
+++ b/tool/lrama/lib/lrama/counterexamples.rb
@@ -0,0 +1,286 @@
+require "set"
+
+require "lrama/counterexamples/derivation"
+require "lrama/counterexamples/example"
+require "lrama/counterexamples/path"
+require "lrama/counterexamples/production_path"
+require "lrama/counterexamples/start_path"
+require "lrama/counterexamples/state_item"
+require "lrama/counterexamples/transition_path"
+require "lrama/counterexamples/triple"
+
+module Lrama
+ # See: https://www.cs.cornell.edu/andru/papers/cupex/cupex.pdf
+ # 4. Constructing Nonunifying Counterexamples
+ class Counterexamples
+ attr_reader :transitions, :productions
+
+ def initialize(states)
+ @states = states
+ setup_transitions
+ setup_productions
+ end
+
+ def to_s
+ "#<Counterexamples>"
+ end
+ alias :inspect :to_s
+
+ def compute(conflict_state)
+ conflict_state.conflicts.flat_map do |conflict|
+ case conflict.type
+ when :shift_reduce
+ shift_reduce_example(conflict_state, conflict)
+ when :reduce_reduce
+ reduce_reduce_examples(conflict_state, conflict)
+ end
+ end.compact
+ end
+
+ private
+
+ def setup_transitions
+ # Hash [StateItem, Symbol] => StateItem
+ @transitions = {}
+ # Hash [StateItem, Symbol] => Set(StateItem)
+ @reverse_transitions = {}
+
+ @states.states.each do |src_state|
+ trans = {}
+
+ src_state.transitions.each do |shift, next_state|
+ trans[shift.next_sym] = next_state
+ end
+
+ src_state.items.each do |src_item|
+ next if src_item.end_of_rule?
+ sym = src_item.next_sym
+ dest_state = trans[sym]
+
+ dest_state.kernels.each do |dest_item|
+ next unless (src_item.rule == dest_item.rule) && (src_item.position + 1 == dest_item.position)
+ src_state_item = StateItem.new(src_state, src_item)
+ dest_state_item = StateItem.new(dest_state, dest_item)
+
+ @transitions[[src_state_item, sym]] = dest_state_item
+
+ key = [dest_state_item, sym]
+ @reverse_transitions[key] ||= Set.new
+ @reverse_transitions[key] << src_state_item
+ end
+ end
+ end
+ end
+
+ def setup_productions
+ # Hash [StateItem] => Set(Item)
+ @productions = {}
+ # Hash [State, Symbol] => Set(Item). Symbol is nterm
+ @reverse_productions = {}
+
+ @states.states.each do |state|
+ # LHS => Set(Item)
+ h = {}
+
+ state.closure.each do |item|
+ sym = item.lhs
+
+ h[sym] ||= Set.new
+ h[sym] << item
+ end
+
+ state.items.each do |item|
+ next if item.end_of_rule?
+ next if item.next_sym.term?
+
+ sym = item.next_sym
+ state_item = StateItem.new(state, item)
+ key = [state, sym]
+
+ @productions[state_item] = h[sym]
+
+ @reverse_productions[key] ||= Set.new
+ @reverse_productions[key] << item
+ end
+ end
+ end
+
+ def shift_reduce_example(conflict_state, conflict)
+ conflict_symbol = conflict.symbols.first
+ shift_conflict_item = conflict_state.items.find { |item| item.next_sym == conflict_symbol }
+ path2 = shortest_path(conflict_state, conflict.reduce.item, conflict_symbol)
+ path1 = find_shift_conflict_shortest_path(path2, conflict_state, shift_conflict_item)
+
+ Example.new(path1, path2, conflict, conflict_symbol, self)
+ end
+
+ def reduce_reduce_examples(conflict_state, conflict)
+ conflict_symbol = conflict.symbols.first
+ path1 = shortest_path(conflict_state, conflict.reduce1.item, conflict_symbol)
+ path2 = shortest_path(conflict_state, conflict.reduce2.item, conflict_symbol)
+
+ Example.new(path1, path2, conflict, conflict_symbol, self)
+ end
+
+ def find_shift_conflict_shortest_path(reduce_path, conflict_state, conflict_item)
+ state_items = find_shift_conflict_shortest_state_items(reduce_path, conflict_state, conflict_item)
+ build_paths_from_state_items(state_items)
+ end
+
+ def find_shift_conflict_shortest_state_items(reduce_path, conflict_state, conflict_item)
+ target_state_item = StateItem.new(conflict_state, conflict_item)
+ result = [target_state_item]
+ reversed_reduce_path = reduce_path.to_a.reverse
+ # Index for state_item
+ i = 0
+
+ while (path = reversed_reduce_path[i])
+ # Index for prev_state_item
+ j = i + 1
+ _j = j
+
+ while (prev_path = reversed_reduce_path[j])
+ if prev_path.production?
+ j += 1
+ else
+ break
+ end
+ end
+
+ state_item = path.to
+ prev_state_item = prev_path&.to
+
+ if target_state_item == state_item || target_state_item.item.start_item?
+ result.concat(reversed_reduce_path[_j..-1].map(&:to))
+ break
+ end
+
+ if target_state_item.item.beginning_of_rule?
+ queue = []
+ queue << [target_state_item]
+
+ # Find reverse production
+ while (sis = queue.shift)
+ si = sis.last
+
+ # Reach to start state
+ if si.item.start_item?
+ sis.shift
+ result.concat(sis)
+ target_state_item = si
+ break
+ end
+
+ if !si.item.beginning_of_rule?
+ key = [si, si.item.previous_sym]
+ @reverse_transitions[key].each do |prev_target_state_item|
+ next if prev_target_state_item.state != prev_state_item.state
+ sis.shift
+ result.concat(sis)
+ result << prev_target_state_item
+ target_state_item = prev_target_state_item
+ i = j
+ queue.clear
+ break
+ end
+ else
+ key = [si.state, si.item.lhs]
+ @reverse_productions[key].each do |item|
+ state_item = StateItem.new(si.state, item)
+ queue << (sis + [state_item])
+ end
+ end
+ end
+ else
+ # Find reverse transition
+ key = [target_state_item, target_state_item.item.previous_sym]
+ @reverse_transitions[key].each do |prev_target_state_item|
+ next if prev_target_state_item.state != prev_state_item.state
+ result << prev_target_state_item
+ target_state_item = prev_target_state_item
+ i = j
+ break
+ end
+ end
+ end
+
+ result.reverse
+ end
+
+ def build_paths_from_state_items(state_items)
+ state_items.zip([nil] + state_items).map do |si, prev_si|
+ case
+ when prev_si.nil?
+ StartPath.new(si)
+ when si.item.beginning_of_rule?
+ ProductionPath.new(prev_si, si)
+ else
+ TransitionPath.new(prev_si, si)
+ end
+ end
+ end
+
+ def shortest_path(conflict_state, conflict_reduce_item, conflict_term)
+ # queue: is an array of [Triple, [Path]]
+ queue = []
+ visited = {}
+ start_state = @states.states.first
+ raise "BUG: Start state should be just one kernel." if start_state.kernels.count != 1
+
+ start = Triple.new(start_state, start_state.kernels.first, Set.new([@states.eof_symbol]))
+
+ queue << [start, [StartPath.new(start.state_item)]]
+
+ while true
+ triple, paths = queue.shift
+
+ next if visited[triple]
+ visited[triple] = true
+
+ # Found
+ if triple.state == conflict_state && triple.item == conflict_reduce_item && triple.l.include?(conflict_term)
+ return paths
+ end
+
+ # transition
+ triple.state.transitions.each do |shift, next_state|
+ next unless triple.item.next_sym && triple.item.next_sym == shift.next_sym
+ next_state.kernels.each do |kernel|
+ next if kernel.rule != triple.item.rule
+ t = Triple.new(next_state, kernel, triple.l)
+ queue << [t, paths + [TransitionPath.new(triple.state_item, t.state_item)]]
+ end
+ end
+
+ # production step
+ triple.state.closure.each do |item|
+ next unless triple.item.next_sym && triple.item.next_sym == item.lhs
+ l = follow_l(triple.item, triple.l)
+ t = Triple.new(triple.state, item, l)
+ queue << [t, paths + [ProductionPath.new(triple.state_item, t.state_item)]]
+ end
+
+ break if queue.empty?
+ end
+
+ return nil
+ end
+
+ def follow_l(item, current_l)
+ # 1. follow_L (A -> X1 ... Xn-1 • Xn) = L
+ # 2. follow_L (A -> X1 ... Xk • Xk+1 Xk+2 ... Xn) = {Xk+2} if Xk+2 is a terminal
+ # 3. follow_L (A -> X1 ... Xk • Xk+1 Xk+2 ... Xn) = FIRST(Xk+2) if Xk+2 is a nonnullable nonterminal
+ # 4. follow_L (A -> X1 ... Xk • Xk+1 Xk+2 ... Xn) = FIRST(Xk+2) + follow_L (A -> X1 ... Xk+1 • Xk+2 ... Xn) if Xk+2 is a nullable nonterminal
+ case
+ when item.number_of_rest_symbols == 1
+ current_l
+ when item.next_next_sym.term?
+ Set.new([item.next_next_sym])
+ when !item.next_next_sym.nullable
+ item.next_next_sym.first_set
+ else
+ item.next_next_sym.first_set + follow_l(item.new_by_next_position, current_l)
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/counterexamples/derivation.rb b/tool/lrama/lib/lrama/counterexamples/derivation.rb
new file mode 100644
index 0000000000..691e935356
--- /dev/null
+++ b/tool/lrama/lib/lrama/counterexamples/derivation.rb
@@ -0,0 +1,63 @@
+module Lrama
+ class Counterexamples
+ class Derivation
+ attr_reader :item, :left, :right
+ attr_writer :right
+
+ def initialize(item, left, right = nil)
+ @item = item
+ @left = left
+ @right = right
+ end
+
+ def to_s
+ "#<Derivation(#{item.display_name})>"
+ end
+ alias :inspect :to_s
+
+ def render_strings_for_report
+ result = []
+ _render_for_report(self, 0, result, 0)
+ result.map(&:rstrip)
+ end
+
+ def render_for_report
+ render_strings_for_report.join("\n")
+ end
+
+ private
+
+ def _render_for_report(derivation, offset, strings, index)
+ item = derivation.item
+ if strings[index]
+ strings[index] << " " * (offset - strings[index].length)
+ else
+ strings[index] = " " * offset
+ end
+ str = strings[index]
+ str << "#{item.rule_id}: #{item.symbols_before_dot.map(&:display_name).join(" ")} "
+
+ if derivation.left
+ len = str.length
+ str << "#{item.next_sym.display_name}"
+ length = _render_for_report(derivation.left, len, strings, index + 1)
+ # I want String#ljust!
+ str << " " * (length - str.length)
+ else
+ str << " • #{item.symbols_after_dot.map(&:display_name).join(" ")} "
+ return str.length
+ end
+
+ if derivation.right&.left
+ length = _render_for_report(derivation.right.left, str.length, strings, index + 1)
+ str << "#{item.symbols_after_dot[1..-1].map(&:display_name).join(" ")} "
+ str << " " * (length - str.length) if length > str.length
+ elsif item.next_next_sym
+ str << "#{item.symbols_after_dot[1..-1].map(&:display_name).join(" ")} "
+ end
+
+ return str.length
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/counterexamples/example.rb b/tool/lrama/lib/lrama/counterexamples/example.rb
new file mode 100644
index 0000000000..62244a77e0
--- /dev/null
+++ b/tool/lrama/lib/lrama/counterexamples/example.rb
@@ -0,0 +1,124 @@
+module Lrama
+ class Counterexamples
+ class Example
+ attr_reader :path1, :path2, :conflict, :conflict_symbol
+
+ # path1 is shift conflict when S/R conflict
+ # path2 is always reduce conflict
+ def initialize(path1, path2, conflict, conflict_symbol, counterexamples)
+ @path1 = path1
+ @path2 = path2
+ @conflict = conflict
+ @conflict_symbol = conflict_symbol
+ @counterexamples = counterexamples
+ end
+
+ def type
+ @conflict.type
+ end
+
+ def path1_item
+ @path1.last.to.item
+ end
+
+ def path2_item
+ @path2.last.to.item
+ end
+
+ def derivations1
+ @derivations1 ||= _derivations(path1)
+ end
+
+ def derivations2
+ @derivations2 ||= _derivations(path2)
+ end
+
+ private
+
+ def _derivations(paths)
+ derivation = nil
+ current = :production
+ lookahead_sym = paths.last.to.item.end_of_rule? ? @conflict_symbol : nil
+
+ paths.reverse_each do |path|
+ item = path.to.item
+
+ case current
+ when :production
+ case path
+ when StartPath
+ derivation = Derivation.new(item, derivation)
+ current = :start
+ when TransitionPath
+ derivation = Derivation.new(item, derivation)
+ current = :transition
+ when ProductionPath
+ derivation = Derivation.new(item, derivation)
+ current = :production
+ end
+
+ if lookahead_sym && item.next_next_sym && item.next_next_sym.first_set.include?(lookahead_sym)
+ state_item = @counterexamples.transitions[[path.to, item.next_sym]]
+ derivation2 = find_derivation_for_symbol(state_item, lookahead_sym)
+ derivation.right = derivation2
+ lookahead_sym = nil
+ end
+
+ when :transition
+ case path
+ when StartPath
+ derivation = Derivation.new(item, derivation)
+ current = :start
+ when TransitionPath
+ # ignore
+ current = :transition
+ when ProductionPath
+ # ignore
+ current = :production
+ end
+ else
+ raise "BUG: Unknown #{current}"
+ end
+
+ break if current == :start
+ end
+
+ derivation
+ end
+
+ def find_derivation_for_symbol(state_item, sym)
+ queue = []
+ queue << [state_item]
+
+ while (sis = queue.shift)
+ si = sis.last
+ next_sym = si.item.next_sym
+
+ if next_sym == sym
+ derivation = nil
+
+ sis.reverse_each do |si|
+ derivation = Derivation.new(si.item, derivation)
+ end
+
+ return derivation
+ end
+
+ if next_sym.nterm? && next_sym.first_set.include?(sym)
+ @counterexamples.productions[si].each do |next_item|
+ next if next_item.empty_rule?
+ next_si = StateItem.new(si.state, next_item)
+ next if sis.include?(next_si)
+ queue << (sis + [next_si])
+ end
+
+ if next_sym.nullable
+ next_si = @counterexamples.transitions[[si, next_sym]]
+ queue << (sis + [next_si])
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/counterexamples/path.rb b/tool/lrama/lib/lrama/counterexamples/path.rb
new file mode 100644
index 0000000000..edba67a3b6
--- /dev/null
+++ b/tool/lrama/lib/lrama/counterexamples/path.rb
@@ -0,0 +1,23 @@
+module Lrama
+ class Counterexamples
+ class Path
+ def initialize(from_state_item, to_state_item)
+ @from_state_item = from_state_item
+ @to_state_item = to_state_item
+ end
+
+ def from
+ @from_state_item
+ end
+
+ def to
+ @to_state_item
+ end
+
+ def to_s
+ "#<Path(#{type})>"
+ end
+ alias :inspect :to_s
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/counterexamples/production_path.rb b/tool/lrama/lib/lrama/counterexamples/production_path.rb
new file mode 100644
index 0000000000..d7db688518
--- /dev/null
+++ b/tool/lrama/lib/lrama/counterexamples/production_path.rb
@@ -0,0 +1,17 @@
+module Lrama
+ class Counterexamples
+ class ProductionPath < Path
+ def type
+ :production
+ end
+
+ def transition?
+ false
+ end
+
+ def production?
+ true
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/counterexamples/start_path.rb b/tool/lrama/lib/lrama/counterexamples/start_path.rb
new file mode 100644
index 0000000000..4a6821cd0f
--- /dev/null
+++ b/tool/lrama/lib/lrama/counterexamples/start_path.rb
@@ -0,0 +1,21 @@
+module Lrama
+ class Counterexamples
+ class StartPath < Path
+ def initialize(to_state_item)
+ super nil, to_state_item
+ end
+
+ def type
+ :start
+ end
+
+ def transition?
+ false
+ end
+
+ def production?
+ false
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/counterexamples/state_item.rb b/tool/lrama/lib/lrama/counterexamples/state_item.rb
new file mode 100644
index 0000000000..930ff4a5f8
--- /dev/null
+++ b/tool/lrama/lib/lrama/counterexamples/state_item.rb
@@ -0,0 +1,6 @@
+module Lrama
+ class Counterexamples
+ class StateItem < Struct.new(:state, :item)
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/counterexamples/transition_path.rb b/tool/lrama/lib/lrama/counterexamples/transition_path.rb
new file mode 100644
index 0000000000..96e611612a
--- /dev/null
+++ b/tool/lrama/lib/lrama/counterexamples/transition_path.rb
@@ -0,0 +1,17 @@
+module Lrama
+ class Counterexamples
+ class TransitionPath < Path
+ def type
+ :transition
+ end
+
+ def transition?
+ true
+ end
+
+ def production?
+ false
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/counterexamples/triple.rb b/tool/lrama/lib/lrama/counterexamples/triple.rb
new file mode 100644
index 0000000000..e802beccf4
--- /dev/null
+++ b/tool/lrama/lib/lrama/counterexamples/triple.rb
@@ -0,0 +1,21 @@
+module Lrama
+ class Counterexamples
+ # s: state
+ # itm: item within s
+ # l: precise lookahead set
+ class Triple < Struct.new(:s, :itm, :l)
+ alias :state :s
+ alias :item :itm
+ alias :precise_lookahead_set :l
+
+ def state_item
+ StateItem.new(state, item)
+ end
+
+ def inspect
+ "#{state.inspect}. #{item.display_name}. #{l.map(&:id).map(&:s_value)}"
+ end
+ alias :to_s :inspect
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/digraph.rb b/tool/lrama/lib/lrama/digraph.rb
index 28f26781b1..bbaa86019f 100644
--- a/tool/lrama/lib/lrama/digraph.rb
+++ b/tool/lrama/lib/lrama/digraph.rb
@@ -33,19 +33,17 @@ module Lrama
@h[x] = d
@result[x] = @base_function[x] # F x = F' x
- @relation[x] && @relation[x].each do |y|
+ @relation[x]&.each do |y|
traverse(y) if @h[y] == 0
@h[x] = [@h[x], @h[y]].min
@result[x] |= @result[y] # F x = F x + F y
end
if @h[x] == d
- while true do
- z = @stack.pop
+ while (z = @stack.pop) do
@h[z] = Float::INFINITY
- @result[z] = @result[x] # F (Top of S) = F x
-
break if z == x
+ @result[z] = @result[x] # F (Top of S) = F x
end
end
end
diff --git a/tool/lrama/lib/lrama/grammar.rb b/tool/lrama/lib/lrama/grammar.rb
index 1daec4446b..a816b8261b 100644
--- a/tool/lrama/lib/lrama/grammar.rb
+++ b/tool/lrama/lib/lrama/grammar.rb
@@ -1,355 +1,82 @@
require "forwardable"
+require "lrama/grammar/auxiliary"
+require "lrama/grammar/binding"
+require "lrama/grammar/code"
+require "lrama/grammar/counter"
+require "lrama/grammar/destructor"
+require "lrama/grammar/error_token"
+require "lrama/grammar/parameterizing_rule"
+require "lrama/grammar/percent_code"
+require "lrama/grammar/precedence"
+require "lrama/grammar/printer"
+require "lrama/grammar/reference"
+require "lrama/grammar/rule"
+require "lrama/grammar/rule_builder"
+require "lrama/grammar/symbol"
+require "lrama/grammar/symbols"
+require "lrama/grammar/type"
+require "lrama/grammar/union"
require "lrama/lexer"
module Lrama
- Rule = Struct.new(:id, :lhs, :rhs, :code, :nullable, :precedence_sym, :lineno, keyword_init: true) do
- # TODO: Change this to display_name
- def to_s
- l = lhs.id.s_value
- r = rhs.empty? ? "ε" : rhs.map {|r| r.id.s_value }.join(", ")
-
- "#{l} -> #{r}"
- end
-
- # Used by #user_actions
- def as_comment
- l = lhs.id.s_value
- r = rhs.empty? ? "%empty" : rhs.map {|r| r.display_name }.join(" ")
-
- "#{l}: #{r}"
- end
-
- def precedence
- precedence_sym && precedence_sym.precedence
- end
-
- def initial_rule?
- id == 0
- end
-
- def translated_code
- if code
- code.translated_code
- else
- nil
- end
- end
- end
-
- # Symbol is both of nterm and term
- # `number` is both for nterm and term
- # `token_id` is tokentype for term, internal sequence number for nterm
- #
- # TODO: Add validation for ASCII code range for Token::Char
- Symbol = Struct.new(:id, :alias_name, :number, :tag, :term, :token_id, :nullable, :precedence, :printer, keyword_init: true) do
- attr_writer :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol
-
- def term?
- term
- end
-
- def nterm?
- !term
- end
-
- def eof_symbol?
- !!@eof_symbol
- end
-
- def error_symbol?
- !!@error_symbol
- end
-
- def undef_symbol?
- !!@undef_symbol
- end
-
- def accept_symbol?
- !!@accept_symbol
- end
-
- def display_name
- if alias_name
- alias_name
- else
- id.s_value
- end
- end
-
- # name for yysymbol_kind_t
- #
- # See: b4_symbol_kind_base
- def enum_name
- case
- when accept_symbol?
- name = "YYACCEPT"
- when eof_symbol?
- name = "YYEOF"
- when term? && id.type == Token::Char
- if alias_name
- name = number.to_s + alias_name
- else
- name = number.to_s + id.s_value
- end
- when term? && id.type == Token::Ident
- name = id.s_value
- when nterm? && (id.s_value.include?("$") || id.s_value.include?("@"))
- name = number.to_s + id.s_value
- when nterm?
- name = id.s_value
- else
- raise "Unexpected #{self}"
- end
-
- "YYSYMBOL_" + name.gsub(/[^a-zA-Z_0-9]+/, "_")
- end
-
- # comment for yysymbol_kind_t
- def comment
- case
- when accept_symbol?
- # YYSYMBOL_YYACCEPT
- id.s_value
- when eof_symbol?
- # YYEOF
- alias_name
- when (term? && 0 < token_id && token_id < 128)
- # YYSYMBOL_3_backslash_, YYSYMBOL_14_
- alias_name || id.s_value
- when id.s_value.include?("$") || id.s_value.include?("@")
- # YYSYMBOL_21_1
- id.s_value
- else
- # YYSYMBOL_keyword_class, YYSYMBOL_strings_1
- alias_name || id.s_value
- end
- end
- end
-
- Type = Struct.new(:id, :tag, keyword_init: true)
-
- Code = Struct.new(:type, :token_code, keyword_init: true) do
- extend Forwardable
-
- def_delegators "token_code", :s_value, :line, :column, :references
-
- # $$, $n, @$, @n is translated to C code
- def translated_code
- case type
- when :user_code
- translated_user_code
- when :initial_action
- translated_initial_action_code
- end
- end
-
- # * ($1) error
- # * ($$) *yyvaluep
- # * (@1) error
- # * (@$) *yylocationp
- def translated_printer_code(tag)
- t_code = s_value.dup
-
- references.reverse.each do |ref|
- first_column = ref.first_column
- last_column = ref.last_column
-
- case
- when ref.number == "$" && ref.type == :dollar # $$
- # Omit "<>"
- member = tag.s_value[1..-2]
- str = "((*yyvaluep).#{member})"
- when ref.number == "$" && ref.type == :at # @$
- str = "(*yylocationp)"
- when ref.type == :dollar # $n
- raise "$#{ref.number} can not be used in %printer."
- when ref.type == :at # @n
- raise "@#{ref.number} can not be used in %printer."
- else
- raise "Unexpected. #{self}, #{ref}"
- end
-
- t_code[first_column..last_column] = str
- end
-
- return t_code
- end
-
-
- private
-
- # * ($1) yyvsp[i]
- # * ($$) yyval
- # * (@1) yylsp[i]
- # * (@$) yyloc
- def translated_user_code
- t_code = s_value.dup
-
- references.reverse.each do |ref|
- first_column = ref.first_column
- last_column = ref.last_column
-
- case
- when ref.number == "$" && ref.type == :dollar # $$
- # Omit "<>"
- member = ref.tag.s_value[1..-2]
- str = "(yyval.#{member})"
- when ref.number == "$" && ref.type == :at # @$
- str = "(yyloc)"
- when ref.type == :dollar # $n
- i = -ref.position_in_rhs + ref.number
- # Omit "<>"
- member = ref.tag.s_value[1..-2]
- str = "(yyvsp[#{i}].#{member})"
- when ref.type == :at # @n
- i = -ref.position_in_rhs + ref.number
- str = "(yylsp[#{i}])"
- else
- raise "Unexpected. #{self}, #{ref}"
- end
-
- t_code[first_column..last_column] = str
- end
-
- return t_code
- end
-
- # * ($1) error
- # * ($$) yylval
- # * (@1) error
- # * (@$) yylloc
- def translated_initial_action_code
- t_code = s_value.dup
-
- references.reverse.each do |ref|
- first_column = ref.first_column
- last_column = ref.last_column
-
- case
- when ref.number == "$" && ref.type == :dollar # $$
- str = "yylval"
- when ref.number == "$" && ref.type == :at # @$
- str = "yylloc"
- when ref.type == :dollar # $n
- raise "$#{ref.number} can not be used in initial_action."
- when ref.type == :at # @n
- raise "@#{ref.number} can not be used in initial_action."
- else
- raise "Unexpected. #{self}, #{ref}"
- end
-
- t_code[first_column..last_column] = str
- end
-
- return t_code
- end
- end
-
- # type: :dollar or :at
- # ex_tag: "$<tag>1" (Optional)
- Reference = Struct.new(:type, :number, :ex_tag, :first_column, :last_column, :referring_symbol, :position_in_rhs, keyword_init: true) do
- def tag
- if ex_tag
- ex_tag
- else
- referring_symbol.tag
- end
- end
- end
-
- Precedence = Struct.new(:type, :precedence, keyword_init: true) do
- include Comparable
-
- def <=>(other)
- self.precedence <=> other.precedence
- end
- end
-
- Printer = Struct.new(:ident_or_tags, :code, :lineno, keyword_init: true) do
- def translated_code(member)
- code.translated_printer_code(member)
- end
- end
-
- Union = Struct.new(:code, :lineno, keyword_init: true) do
- def braces_less_code
- # Remove braces
- code.s_value[1..-2]
- end
- end
-
- Token = Lrama::Lexer::Token
-
# Grammar is the result of parsing an input grammar file
class Grammar
- # Grammar file information not used by States but by Output
- Aux = Struct.new(:prologue_first_lineno, :prologue, :epilogue_first_lineno, :epilogue, keyword_init: true)
+ extend Forwardable
- attr_reader :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol, :aux
+ attr_reader :percent_codes, :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol, :aux
attr_accessor :union, :expect,
- :printers,
+ :printers, :error_tokens,
:lex_param, :parse_param, :initial_action,
- :symbols, :types,
- :rules, :_rules,
- :sym_to_rules
+ :after_shift, :before_reduce, :after_reduce, :after_shift_error_token, :after_pop_stack,
+ :symbols_resolver, :types,
+ :rules, :rule_builders,
+ :sym_to_rules, :no_stdlib
+
+ def_delegators "@symbols_resolver", :symbols, :nterms, :terms, :add_nterm, :add_term,
+ :find_symbol_by_number!, :find_symbol_by_id!, :token_to_symbol,
+ :find_symbol_by_s_value!, :fill_symbol_number, :fill_nterm_type,
+ :fill_printer, :fill_destructor, :fill_error_token, :sort_by_number!
- def initialize
+
+ def initialize(rule_counter)
+ @rule_counter = rule_counter
+
+ # Code defined by "%code"
+ @percent_codes = []
@printers = []
- @symbols = []
+ @destructors = []
+ @error_tokens = []
+ @symbols_resolver = Grammar::Symbols::Resolver.new
@types = []
- @_rules = []
+ @rule_builders = []
@rules = []
@sym_to_rules = {}
+ @parameterizing_rule_resolver = ParameterizingRule::Resolver.new
@empty_symbol = nil
@eof_symbol = nil
@error_symbol = nil
@undef_symbol = nil
@accept_symbol = nil
- @aux = Aux.new
+ @aux = Auxiliary.new
+ @no_stdlib = false
append_special_symbols
end
- def add_printer(ident_or_tags:, code:, lineno:)
- @printers << Printer.new(ident_or_tags: ident_or_tags, code: code, lineno: lineno)
+ def add_percent_code(id:, code:)
+ @percent_codes << PercentCode.new(id.s_value, code.s_value)
end
- def add_term(id:, alias_name: nil, tag: nil, token_id: nil, replace: false)
- if token_id && (sym = @symbols.find {|s| s.token_id == token_id })
- if replace
- sym.id = id
- sym.alias_name = alias_name
- sym.tag = tag
- end
-
- return sym
- end
-
- if sym = @symbols.find {|s| s.id == id }
- return sym
- end
-
- sym = Symbol.new(
- id: id, alias_name: alias_name, number: nil, tag: tag,
- term: true, token_id: token_id, nullable: false
- )
- @symbols << sym
- @terms = nil
-
- return sym
+ def add_destructor(ident_or_tags:, token_code:, lineno:)
+ @destructors << Destructor.new(ident_or_tags: ident_or_tags, token_code: token_code, lineno: lineno)
end
- def add_nterm(id:, alias_name: nil, tag: nil)
- return if @symbols.find {|s| s.id == id }
-
- sym = Symbol.new(
- id: id, alias_name: alias_name, number: nil, tag: tag,
- term: false, token_id: nil, nullable: nil,
- )
- @symbols << sym
- @nterms = nil
+ def add_printer(ident_or_tags:, token_code:, lineno:)
+ @printers << Printer.new(ident_or_tags: ident_or_tags, token_code: token_code, lineno: lineno)
+ end
- return sym
+ def add_error_token(ident_or_tags:, token_code:, lineno:)
+ @error_tokens << ErrorToken.new(ident_or_tags: ident_or_tags, token_code: token_code, lineno: lineno)
end
def add_type(id:, tag:)
@@ -368,6 +95,10 @@ module Lrama
set_precedence(sym, Precedence.new(type: :right, precedence: precedence))
end
+ def add_precedence(sym, precedence)
+ set_precedence(sym, Precedence.new(type: :precedence, precedence: precedence))
+ end
+
def set_precedence(sym, precedence)
raise "" if sym.nterm?
sym.precedence = precedence
@@ -377,21 +108,20 @@ module Lrama
@union = Union.new(code: code, lineno: lineno)
end
- def add_rule(lhs:, rhs:, lineno:)
- @_rules << [lhs, rhs, lineno]
+ def add_rule_builder(builder)
+ @rule_builders << builder
end
- def build_references(token_code)
- token_code.references.map! do |type, number, tag, first_column, last_column|
- Reference.new(type: type, number: number, ex_tag: tag, first_column: first_column, last_column: last_column)
- end
+ def add_parameterizing_rule(rule)
+ @parameterizing_rule_resolver.add_parameterizing_rule(rule)
+ end
- token_code
+ def parameterizing_rules
+ @parameterizing_rule_resolver.rules
end
- def build_code(type, token_code)
- build_references(token_code)
- Code.new(type: type, token_code: token_code)
+ def insert_before_parameterizing_rules(rules)
+ @parameterizing_rule_resolver.rules = rules + @parameterizing_rule_resolver.rules
end
def prologue_first_lineno=(prologue_first_lineno)
@@ -413,24 +143,36 @@ module Lrama
def prepare
normalize_rules
collect_symbols
- replace_token_with_symbol
- fill_symbol_number
+ set_lhs_and_rhs
fill_default_precedence
+ fill_symbols
fill_sym_to_rules
- fill_nterm_type
- fill_symbol_printer
- @symbols.sort_by!(&:number)
+ compute_nullable
+ compute_first_set
end
# TODO: More validation methods
+ #
+ # * Validation for no_declared_type_reference
def validate!
- validate_symbol_number_uniqueness!
+ @symbols_resolver.validate!
+ validate_rule_lhs_is_nterm!
end
+ def find_rules_by_symbol!(sym)
+ find_rules_by_symbol(sym) || (raise "Rules for #{sym} not found")
+ end
+
+ def find_rules_by_symbol(sym)
+ @sym_to_rules[sym.number]
+ end
+
+ private
+
def compute_nullable
@rules.each do |rule|
case
- when rule.rhs.empty?
+ when rule.empty_rule?
rule.nullable = true
when rule.rhs.any?(&:term)
rule.nullable = false
@@ -471,74 +213,52 @@ module Lrama
rule.nullable = false
end
- nterms.select {|r| r.nullable.nil? }.each do |nterm|
+ nterms.select {|e| e.nullable.nil? }.each do |nterm|
nterm.nullable = false
end
end
- def find_symbol_by_s_value(s_value)
- @symbols.find do |sym|
- sym.id.s_value == s_value
+ def compute_first_set
+ terms.each do |term|
+ term.first_set = Set.new([term]).freeze
+ term.first_set_bitmap = Lrama::Bitmap.from_array([term.number])
end
- end
-
- def find_symbol_by_s_value!(s_value)
- find_symbol_by_s_value(s_value) || (raise "Symbol not found: #{s_value}")
- end
- def find_symbol_by_id(id)
- @symbols.find do |sym|
- # TODO: validate uniqueness of Token#s_value and Symbol#alias_name
- sym.id == id || sym.alias_name == id.s_value
+ nterms.each do |nterm|
+ nterm.first_set = Set.new([]).freeze
+ nterm.first_set_bitmap = Lrama::Bitmap.from_array([])
end
- end
-
- def find_symbol_by_id!(id)
- find_symbol_by_id(id) || (raise "Symbol not found: #{id}")
- end
- def find_symbol_by_number!(number)
- sym = @symbols[number]
-
- raise "Symbol not found: #{number}" unless sym
- raise "[BUG] Symbol number mismatch. #{number}, #{sym}" if sym.number != number
-
- sym
- end
-
- def find_rules_by_symbol!(sym)
- find_rules_by_symbol(sym) || (raise "Rules for #{sym} not found")
- end
-
- def find_rules_by_symbol(sym)
- @sym_to_rules[sym.number]
- end
+ while true do
+ changed = false
- def terms_count
- terms.count
- end
+ @rules.each do |rule|
+ rule.rhs.each do |r|
+ if rule.lhs.first_set_bitmap | r.first_set_bitmap != rule.lhs.first_set_bitmap
+ changed = true
+ rule.lhs.first_set_bitmap = rule.lhs.first_set_bitmap | r.first_set_bitmap
+ end
- def terms
- @terms ||= @symbols.select(&:term?)
- end
+ break unless r.nullable
+ end
+ end
- def nterms_count
- nterms.count
- end
+ break unless changed
+ end
- def nterms
- @nterms ||= @symbols.select(&:nterm?)
+ nterms.each do |nterm|
+ nterm.first_set = Lrama::Bitmap.to_array(nterm.first_set_bitmap).map do |number|
+ find_symbol_by_number!(number)
+ end.to_set
+ end
end
- private
-
- def find_nterm_by_id!(id)
- nterms.find do |nterm|
- nterm.id == id
- end || (raise "Nterm not found: #{id}")
+ def setup_rules
+ @rule_builders.each do |builder|
+ builder.setup_rules(@parameterizing_rule_resolver)
+ end
end
-
def append_special_symbols
# YYEMPTY (token_id: -2, number: -2) is added when a template is evaluated
# term = add_term(id: Token.new(Token::Ident, "YYEMPTY"), token_id: -2)
@@ -546,138 +266,53 @@ module Lrama
# @empty_symbol = term
# YYEOF
- term = add_term(id: Token.new(type: Token::Ident, s_value: "YYEOF"), alias_name: "\"end of file\"", token_id: 0)
+ term = add_term(id: Lrama::Lexer::Token::Ident.new(s_value: "YYEOF"), alias_name: "\"end of file\"", token_id: 0)
term.number = 0
term.eof_symbol = true
@eof_symbol = term
# YYerror
- term = add_term(id: Token.new(type: Token::Ident, s_value: "YYerror"), alias_name: "error")
+ term = add_term(id: Lrama::Lexer::Token::Ident.new(s_value: "YYerror"), alias_name: "error")
term.number = 1
term.error_symbol = true
@error_symbol = term
# YYUNDEF
- term = add_term(id: Token.new(type: Token::Ident, s_value: "YYUNDEF"), alias_name: "\"invalid token\"")
+ term = add_term(id: Lrama::Lexer::Token::Ident.new(s_value: "YYUNDEF"), alias_name: "\"invalid token\"")
term.number = 2
term.undef_symbol = true
@undef_symbol = term
# $accept
- term = add_nterm(id: Token.new(type: Token::Ident, s_value: "$accept"))
+ term = add_nterm(id: Lrama::Lexer::Token::Ident.new(s_value: "$accept"))
term.accept_symbol = true
@accept_symbol = term
end
- # 1. Add $accept rule to the top of rules
- # 2. Extract precedence and last action
- # 3. Extract action in the middle of RHS into new Empty rule
- # 4. Append id and extract action then create Rule
- #
- # Bison 3.8.2 uses different orders for symbol number and rule number
- # when a rule has actions in the middle of a rule.
- #
- # For example,
- #
- # `program: $@1 top_compstmt`
- #
- # Rules are ordered like below,
- #
- # 1 $@1: ε
- # 2 program: $@1 top_compstmt
- #
- # Symbols are ordered like below,
- #
- # 164 program
- # 165 $@1
- #
def normalize_rules
- # 1. Add $accept rule to the top of rules
- accept = find_symbol_by_s_value!("$accept")
- eof = find_symbol_by_number!(0)
- lineno = @_rules.first ? @_rules.first[2] : 0
- @rules << Rule.new(id: @rules.count, lhs: accept, rhs: [@_rules.first[0], eof], code: nil, lineno: lineno)
-
- extracted_action_number = 1 # @n as nterm
-
- @_rules.each do |lhs, rhs, lineno|
- a = []
- rhs1 = []
- code = nil
- precedence_sym = nil
-
- # 2. Extract precedence and last action
- rhs.reverse.each do |r|
- case
- when r.is_a?(Symbol) # precedence_sym
- precedence_sym = r
- when (r.type == Token::User_code) && precedence_sym.nil? && code.nil? && rhs1.empty?
- code = r
- else
- rhs1 << r
- end
- end
- rhs1.reverse!
-
- # Bison n'th component is 1-origin
- (rhs1 + [code]).compact.each.with_index(1) do |token, i|
- if token.type == Token::User_code
- token.references.each do |ref|
- # Need to keep position_in_rhs for actions in the middle of RHS
- ref.position_in_rhs = i - 1
- next if ref.type == :at
- # $$, $n, @$, @n can be used in any actions
- number = ref.number
-
- if number == "$"
- # TODO: Should be postponed after middle actions are extracted?
- ref.referring_symbol = lhs
- else
- raise "Can not refer following component. #{number} >= #{i}. #{token}" if number >= i
- rhs1[number - 1].referred = true
- ref.referring_symbol = rhs1[number - 1]
- end
- end
- end
- end
-
- rhs2 = rhs1.map do |token|
- if token.type == Token::User_code
- prefix = token.referred ? "@" : "$@"
- new_token = Token.new(type: Token::Ident, s_value: prefix + extracted_action_number.to_s)
- extracted_action_number += 1
- a << [new_token, token]
- new_token
- else
- token
- end
- end
+ # Add $accept rule to the top of rules
+ lineno = @rule_builders.first ? @rule_builders.first.line : 0
+ @rules << Rule.new(id: @rule_counter.increment, _lhs: @accept_symbol.id, _rhs: [@rule_builders.first.lhs, @eof_symbol.id], token_code: nil, lineno: lineno)
- # Extract actions in the middle of RHS
- # into new rules.
- a.each do |new_token, code|
- @rules << Rule.new(id: @rules.count, lhs: new_token, rhs: [], code: Code.new(type: :user_code, token_code: code), lineno: code.line)
- end
-
- c = code ? Code.new(type: :user_code, token_code: code) : nil
- @rules << Rule.new(id: @rules.count, lhs: lhs, rhs: rhs2, code: c, precedence_sym: precedence_sym, lineno: lineno)
+ setup_rules
- add_nterm(id: lhs)
- a.each do |new_token, _|
- add_nterm(id: new_token)
+ @rule_builders.each do |builder|
+ builder.rules.each do |rule|
+ add_nterm(id: rule._lhs, tag: rule.lhs_tag)
+ @rules << rule
end
end
+
+ @rules.sort_by!(&:id)
end
# Collect symbols from rules
def collect_symbols
- @rules.flat_map(&:rhs).each do |s|
+ @rules.flat_map(&:_rhs).each do |s|
case s
- when Token
- if s.type == Token::Char
- add_term(id: s)
- end
- when Symbol
+ when Lrama::Lexer::Token::Char
+ add_term(id: s)
+ when Lrama::Lexer::Token
# skip
else
raise "Unknown class: #{s}"
@@ -685,105 +320,13 @@ module Lrama
end
end
- # Fill #number and #token_id
- def fill_symbol_number
- # TODO: why start from 256
- token_id = 256
-
- # YYEMPTY = -2
- # YYEOF = 0
- # YYerror = 1
- # YYUNDEF = 2
- number = 3
-
- nterm_token_id = 0
- used_numbers = {}
-
- @symbols.map(&:number).each do |n|
- used_numbers[n] = true
- end
-
- (@symbols.select(&:term?) + @symbols.select(&:nterm?)).each do |sym|
- while used_numbers[number] do
- number += 1
- end
-
- if sym.number.nil?
- sym.number = number
- number += 1
- end
-
- # If id is Token::Char, it uses ASCII code
- if sym.term? && sym.token_id.nil?
- if sym.id.type == Token::Char
- # Ignore ' on the both sides
- case sym.id.s_value[1..-2]
- when "\\b"
- sym.token_id = 8
- when "\\f"
- sym.token_id = 12
- when "\\n"
- sym.token_id = 10
- when "\\r"
- sym.token_id = 13
- when "\\t"
- sym.token_id = 9
- when "\\v"
- sym.token_id = 11
- when "\""
- sym.token_id = 34
- when "\'"
- sym.token_id = 39
- when "\\\\"
- sym.token_id = 92
- when /\A\\(\d+)\z/
- sym.token_id = Integer($1, 8)
- when /\A(.)\z/
- sym.token_id = $1.bytes.first
- else
- raise "Unknown Char s_value #{sym}"
- end
- else
- sym.token_id = token_id
- token_id += 1
- end
- end
-
- if sym.nterm? && sym.token_id.nil?
- sym.token_id = nterm_token_id
- nterm_token_id += 1
- end
- end
- end
-
- def replace_token_with_symbol
+ def set_lhs_and_rhs
@rules.each do |rule|
- rule.lhs = token_to_symbol(rule.lhs)
+ rule.lhs = token_to_symbol(rule._lhs) if rule._lhs
- rule.rhs.map! do |t|
+ rule.rhs = rule._rhs.map do |t|
token_to_symbol(t)
end
-
- if rule.code
- rule.code.references.each do |ref|
- next if ref.type == :at
-
- if ref.referring_symbol.type != Token::User_code
- ref.referring_symbol = token_to_symbol(ref.referring_symbol)
- end
- end
- end
- end
- end
-
- def token_to_symbol(token)
- case token
- when Token
- find_symbol_by_id!(token)
- when Symbol
- token
- else
- raise "Unknown class: #{token}"
end
end
@@ -804,6 +347,15 @@ module Lrama
end
end
+ def fill_symbols
+ fill_symbol_number
+ fill_nterm_type(@types)
+ fill_printer(@printers)
+ fill_destructor(@destructors)
+ fill_error_token(@error_tokens)
+ sort_by_number!
+ end
+
def fill_sym_to_rules
@rules.each do |rule|
key = rule.lhs.number
@@ -812,39 +364,18 @@ module Lrama
end
end
- # Fill nterm's tag defined by %type decl
- def fill_nterm_type
- @types.each do |type|
- nterm = find_nterm_by_id!(type.id)
- nterm.tag = type.tag
- end
- end
+ def validate_rule_lhs_is_nterm!
+ errors = []
- def fill_symbol_printer
- @symbols.each do |sym|
- @printers.each do |printer|
- printer.ident_or_tags.each do |ident_or_tag|
- case ident_or_tag.type
- when Token::Ident
- sym.printer = printer if sym.id == ident_or_tag
- when Token::Tag
- sym.printer = printer if sym.tag == ident_or_tag
- else
- raise "Unknown token type. #{printer}"
- end
- end
- end
- end
- end
+ rules.each do |rule|
+ next if rule.lhs.nterm?
- def validate_symbol_number_uniqueness!
- invalid = @symbols.group_by(&:number).select do |number, syms|
- syms.count > 1
+ errors << "[BUG] LHS of #{rule} (line: #{rule.lineno}) is term. It should be nterm."
end
- return if invalid.empty?
+ return if errors.empty?
- raise "Symbol number is duplicated. #{invalid}"
+ raise errors.join("\n")
end
end
end
diff --git a/tool/lrama/lib/lrama/grammar/auxiliary.rb b/tool/lrama/lib/lrama/grammar/auxiliary.rb
new file mode 100644
index 0000000000..933574b0f6
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/auxiliary.rb
@@ -0,0 +1,7 @@
+module Lrama
+ class Grammar
+ # Grammar file information not used by States but by Output
+ class Auxiliary < Struct.new(:prologue_first_lineno, :prologue, :epilogue_first_lineno, :epilogue, keyword_init: true)
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/grammar/binding.rb b/tool/lrama/lib/lrama/grammar/binding.rb
new file mode 100644
index 0000000000..e5ea3fb037
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/binding.rb
@@ -0,0 +1,24 @@
+module Lrama
+ class Grammar
+ class Binding
+ attr_reader :actual_args, :count
+
+ def initialize(parameterizing_rule, actual_args)
+ @parameters = parameterizing_rule.parameters
+ @actual_args = actual_args
+ @parameter_to_arg = @parameters.zip(actual_args).map do |param, arg|
+ [param.s_value, arg]
+ end.to_h
+ end
+
+ def resolve_symbol(symbol)
+ if symbol.is_a?(Lexer::Token::InstantiateRule)
+ resolved_args = symbol.args.map { |arg| resolve_symbol(arg) }
+ Lrama::Lexer::Token::InstantiateRule.new(s_value: symbol.s_value, location: symbol.location, args: resolved_args, lhs_tag: symbol.lhs_tag)
+ else
+ @parameter_to_arg[symbol.s_value] || symbol
+ end
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/grammar/code.rb b/tool/lrama/lib/lrama/grammar/code.rb
new file mode 100644
index 0000000000..3bad599dae
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/code.rb
@@ -0,0 +1,51 @@
+require "forwardable"
+require "lrama/grammar/code/destructor_code"
+require "lrama/grammar/code/initial_action_code"
+require "lrama/grammar/code/no_reference_code"
+require "lrama/grammar/code/printer_code"
+require "lrama/grammar/code/rule_action"
+
+module Lrama
+ class Grammar
+ class Code
+ extend Forwardable
+
+ def_delegators "token_code", :s_value, :line, :column, :references
+
+ attr_reader :type, :token_code
+
+ def initialize(type:, token_code:)
+ @type = type
+ @token_code = token_code
+ end
+
+ def ==(other)
+ self.class == other.class &&
+ self.type == other.type &&
+ self.token_code == other.token_code
+ end
+
+ # $$, $n, @$, @n are translated to C code
+ def translated_code
+ t_code = s_value.dup
+
+ references.reverse_each do |ref|
+ first_column = ref.first_column
+ last_column = ref.last_column
+
+ str = reference_to_c(ref)
+
+ t_code[first_column...last_column] = str
+ end
+
+ return t_code
+ end
+
+ private
+
+ def reference_to_c(ref)
+ raise NotImplementedError.new("#reference_to_c is not implemented")
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/grammar/code/destructor_code.rb b/tool/lrama/lib/lrama/grammar/code/destructor_code.rb
new file mode 100644
index 0000000000..70360eb90f
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/code/destructor_code.rb
@@ -0,0 +1,40 @@
+module Lrama
+ class Grammar
+ class Code
+ class DestructorCode < Code
+ def initialize(type:, token_code:, tag:)
+ super(type: type, token_code: token_code)
+ @tag = tag
+ end
+
+ private
+
+ # * ($$) *yyvaluep
+ # * (@$) *yylocationp
+ # * ($:$) error
+ # * ($1) error
+ # * (@1) error
+ # * ($:1) error
+ def reference_to_c(ref)
+ case
+ when ref.type == :dollar && ref.name == "$" # $$
+ member = @tag.member
+ "((*yyvaluep).#{member})"
+ when ref.type == :at && ref.name == "$" # @$
+ "(*yylocationp)"
+ when ref.type == :index && ref.name == "$" # $:$
+ raise "$:#{ref.value} can not be used in #{type}."
+ when ref.type == :dollar # $n
+ raise "$#{ref.value} can not be used in #{type}."
+ when ref.type == :at # @n
+ raise "@#{ref.value} can not be used in #{type}."
+ when ref.type == :index # $:n
+ raise "$:#{ref.value} can not be used in #{type}."
+ else
+ raise "Unexpected. #{self}, #{ref}"
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/grammar/code/initial_action_code.rb b/tool/lrama/lib/lrama/grammar/code/initial_action_code.rb
new file mode 100644
index 0000000000..a694f193cb
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/code/initial_action_code.rb
@@ -0,0 +1,34 @@
+module Lrama
+ class Grammar
+ class Code
+ class InitialActionCode < Code
+ private
+
+ # * ($$) yylval
+ # * (@$) yylloc
+ # * ($:$) error
+ # * ($1) error
+ # * (@1) error
+ # * ($:1) error
+ def reference_to_c(ref)
+ case
+ when ref.type == :dollar && ref.name == "$" # $$
+ "yylval"
+ when ref.type == :at && ref.name == "$" # @$
+ "yylloc"
+ when ref.type == :index && ref.name == "$" # $:$
+ raise "$:#{ref.value} can not be used in initial_action."
+ when ref.type == :dollar # $n
+ raise "$#{ref.value} can not be used in initial_action."
+ when ref.type == :at # @n
+ raise "@#{ref.value} can not be used in initial_action."
+ when ref.type == :index # $:n
+ raise "$:#{ref.value} can not be used in initial_action."
+ else
+ raise "Unexpected. #{self}, #{ref}"
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/grammar/code/no_reference_code.rb b/tool/lrama/lib/lrama/grammar/code/no_reference_code.rb
new file mode 100644
index 0000000000..6e614cc64a
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/code/no_reference_code.rb
@@ -0,0 +1,28 @@
+module Lrama
+ class Grammar
+ class Code
+ class NoReferenceCode < Code
+ private
+
+ # * ($$) error
+ # * (@$) error
+ # * ($:$) error
+ # * ($1) error
+ # * (@1) error
+ # * ($:1) error
+ def reference_to_c(ref)
+ case
+ when ref.type == :dollar # $$, $n
+ raise "$#{ref.value} can not be used in #{type}."
+ when ref.type == :at # @$, @n
+ raise "@#{ref.value} can not be used in #{type}."
+ when ref.type == :index # $:$, $:n
+ raise "$:#{ref.value} can not be used in #{type}."
+ else
+ raise "Unexpected. #{self}, #{ref}"
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/grammar/code/printer_code.rb b/tool/lrama/lib/lrama/grammar/code/printer_code.rb
new file mode 100644
index 0000000000..ffccd89395
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/code/printer_code.rb
@@ -0,0 +1,40 @@
+module Lrama
+ class Grammar
+ class Code
+ class PrinterCode < Code
+ def initialize(type:, token_code:, tag:)
+ super(type: type, token_code: token_code)
+ @tag = tag
+ end
+
+ private
+
+ # * ($$) *yyvaluep
+ # * (@$) *yylocationp
+ # * ($:$) error
+ # * ($1) error
+ # * (@1) error
+ # * ($:1) error
+ def reference_to_c(ref)
+ case
+ when ref.type == :dollar && ref.name == "$" # $$
+ member = @tag.member
+ "((*yyvaluep).#{member})"
+ when ref.type == :at && ref.name == "$" # @$
+ "(*yylocationp)"
+ when ref.type == :index && ref.name == "$" # $:$
+ raise "$:#{ref.value} can not be used in #{type}."
+ when ref.type == :dollar # $n
+ raise "$#{ref.value} can not be used in #{type}."
+ when ref.type == :at # @n
+ raise "@#{ref.value} can not be used in #{type}."
+ when ref.type == :index # $:n
+ raise "$:#{ref.value} can not be used in #{type}."
+ else
+ raise "Unexpected. #{self}, #{ref}"
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/grammar/code/rule_action.rb b/tool/lrama/lib/lrama/grammar/code/rule_action.rb
new file mode 100644
index 0000000000..d3c0eab64a
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/code/rule_action.rb
@@ -0,0 +1,88 @@
+module Lrama
+ class Grammar
+ class Code
+ class RuleAction < Code
+ def initialize(type:, token_code:, rule:)
+ super(type: type, token_code: token_code)
+ @rule = rule
+ end
+
+ private
+
+ # * ($$) yyval
+ # * (@$) yyloc
+ # * ($:$) error
+ # * ($1) yyvsp[i]
+ # * (@1) yylsp[i]
+ # * ($:1) i - 1
+ #
+ #
+ # Consider a rule like
+ #
+ # class: keyword_class { $1 } tSTRING { $2 + $3 } keyword_end { $class = $1 + $keyword_end }
+ #
+ # For the semantic action of original rule:
+ #
+ # "Rule" class: keyword_class { $1 } tSTRING { $2 + $3 } keyword_end { $class = $1 + $keyword_end }
+ # "Position in grammar" $1 $2 $3 $4 $5
+ # "Index for yyvsp" -4 -3 -2 -1 0
+ # "$:n" $:1 $:2 $:3 $:4 $:5
+ # "index of $:n" -5 -4 -3 -2 -1
+ #
+ #
+ # For the first midrule action:
+ #
+ # "Rule" class: keyword_class { $1 } tSTRING { $2 + $3 } keyword_end { $class = $1 + $keyword_end }
+ # "Position in grammar" $1
+ # "Index for yyvsp" 0
+ # "$:n" $:1
+ def reference_to_c(ref)
+ case
+ when ref.type == :dollar && ref.name == "$" # $$
+ tag = ref.ex_tag || lhs.tag
+ raise_tag_not_found_error(ref) unless tag
+ "(yyval.#{tag.member})"
+ when ref.type == :at && ref.name == "$" # @$
+ "(yyloc)"
+ when ref.type == :index && ref.name == "$" # $:$
+ raise "$:$ is not supported"
+ when ref.type == :dollar # $n
+ i = -position_in_rhs + ref.index
+ tag = ref.ex_tag || rhs[ref.index - 1].tag
+ raise_tag_not_found_error(ref) unless tag
+ "(yyvsp[#{i}].#{tag.member})"
+ when ref.type == :at # @n
+ i = -position_in_rhs + ref.index
+ "(yylsp[#{i}])"
+ when ref.type == :index # $:n
+ i = -position_in_rhs + ref.index
+ "(#{i} - 1)"
+ else
+ raise "Unexpected. #{self}, #{ref}"
+ end
+ end
+
+ def position_in_rhs
+ # If rule is not derived rule, User Code is only action at
+ # the end of rule RHS. In such case, the action is located on
+ # `@rule.rhs.count`.
+ @rule.position_in_original_rule_rhs || @rule.rhs.count
+ end
+
+ # If this is midrule action, RHS is a RHS of the original rule.
+ def rhs
+ (@rule.original_rule || @rule).rhs
+ end
+
+ # Unlike `rhs`, LHS is always a LHS of the rule.
+ def lhs
+ @rule.lhs
+ end
+
+ def raise_tag_not_found_error(ref)
+ raise "Tag is not specified for '$#{ref.value}' in '#{@rule}'"
+ end
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/grammar/counter.rb b/tool/lrama/lib/lrama/grammar/counter.rb
new file mode 100644
index 0000000000..c13f4ec3e3
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/counter.rb
@@ -0,0 +1,15 @@
+module Lrama
+ class Grammar
+ class Counter
+ def initialize(number)
+ @number = number
+ end
+
+ def increment
+ n = @number
+ @number += 1
+ n
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/grammar/destructor.rb b/tool/lrama/lib/lrama/grammar/destructor.rb
new file mode 100644
index 0000000000..4b7059e923
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/destructor.rb
@@ -0,0 +1,9 @@
+module Lrama
+ class Grammar
+ class Destructor < Struct.new(:ident_or_tags, :token_code, :lineno, keyword_init: true)
+ def translated_code(tag)
+ Code::DestructorCode.new(type: :destructor, token_code: token_code, tag: tag).translated_code
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/grammar/error_token.rb b/tool/lrama/lib/lrama/grammar/error_token.rb
new file mode 100644
index 0000000000..8efde7df33
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/error_token.rb
@@ -0,0 +1,9 @@
+module Lrama
+ class Grammar
+ class ErrorToken < Struct.new(:ident_or_tags, :token_code, :lineno, keyword_init: true)
+ def translated_code(tag)
+ Code::PrinterCode.new(type: :error_token, token_code: token_code, tag: tag).translated_code
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/grammar/parameterizing_rule.rb b/tool/lrama/lib/lrama/grammar/parameterizing_rule.rb
new file mode 100644
index 0000000000..d371805f4b
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/parameterizing_rule.rb
@@ -0,0 +1,3 @@
+require_relative 'parameterizing_rule/resolver'
+require_relative 'parameterizing_rule/rhs'
+require_relative 'parameterizing_rule/rule'
diff --git a/tool/lrama/lib/lrama/grammar/parameterizing_rule/resolver.rb b/tool/lrama/lib/lrama/grammar/parameterizing_rule/resolver.rb
new file mode 100644
index 0000000000..d8f3ae7897
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/parameterizing_rule/resolver.rb
@@ -0,0 +1,56 @@
+module Lrama
+ class Grammar
+ class ParameterizingRule
+ class Resolver
+ attr_accessor :rules, :created_lhs_list
+
+ def initialize
+ @rules = []
+ @created_lhs_list = []
+ end
+
+ def add_parameterizing_rule(rule)
+ @rules << rule
+ end
+
+ def find_rule(token)
+ select_rules(@rules, token).last
+ end
+
+ def find_inline(token)
+ @rules.select { |rule| rule.name == token.s_value && rule.is_inline }.last
+ end
+
+ def created_lhs(lhs_s_value)
+ @created_lhs_list.reverse.find { |created_lhs| created_lhs.s_value == lhs_s_value }
+ end
+
+ private
+
+ def select_rules(rules, token)
+ rules = select_not_inline_rules(rules)
+ rules = select_rules_by_name(rules, token.rule_name)
+ rules = rules.select { |rule| rule.required_parameters_count == token.args_count }
+ if rules.empty?
+ raise "Invalid number of arguments. `#{token.rule_name}`"
+ else
+ rules
+ end
+ end
+
+ def select_not_inline_rules(rules)
+ rules.select { |rule| !rule.is_inline }
+ end
+
+ def select_rules_by_name(rules, rule_name)
+ rules = rules.select { |rule| rule.name == rule_name }
+ if rules.empty?
+ raise "Parameterizing rule does not exist. `#{rule_name}`"
+ else
+ rules
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/grammar/parameterizing_rule/rhs.rb b/tool/lrama/lib/lrama/grammar/parameterizing_rule/rhs.rb
new file mode 100644
index 0000000000..7f50be873c
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/parameterizing_rule/rhs.rb
@@ -0,0 +1,15 @@
+module Lrama
+ class Grammar
+ class ParameterizingRule
+ class Rhs
+ attr_accessor :symbols, :user_code, :precedence_sym
+
+ def initialize
+ @symbols = []
+ @user_code = nil
+ @precedence_sym = nil
+ end
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/grammar/parameterizing_rule/rule.rb b/tool/lrama/lib/lrama/grammar/parameterizing_rule/rule.rb
new file mode 100644
index 0000000000..eb119c005e
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/parameterizing_rule/rule.rb
@@ -0,0 +1,17 @@
+module Lrama
+ class Grammar
+ class ParameterizingRule
+ class Rule
+ attr_reader :name, :parameters, :rhs_list, :required_parameters_count, :is_inline
+
+ def initialize(name, parameters, rhs_list, is_inline: false)
+ @name = name
+ @parameters = parameters
+ @rhs_list = rhs_list
+ @is_inline = is_inline
+ @required_parameters_count = parameters.count
+ end
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/grammar/percent_code.rb b/tool/lrama/lib/lrama/grammar/percent_code.rb
new file mode 100644
index 0000000000..8cbc5aef2c
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/percent_code.rb
@@ -0,0 +1,12 @@
+module Lrama
+ class Grammar
+ class PercentCode
+ attr_reader :name, :code
+
+ def initialize(name, code)
+ @name = name
+ @code = code
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/grammar/precedence.rb b/tool/lrama/lib/lrama/grammar/precedence.rb
new file mode 100644
index 0000000000..fed739b3c0
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/precedence.rb
@@ -0,0 +1,11 @@
+module Lrama
+ class Grammar
+ class Precedence < Struct.new(:type, :precedence, keyword_init: true)
+ include Comparable
+
+ def <=>(other)
+ self.precedence <=> other.precedence
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/grammar/printer.rb b/tool/lrama/lib/lrama/grammar/printer.rb
new file mode 100644
index 0000000000..8984a96e1a
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/printer.rb
@@ -0,0 +1,9 @@
+module Lrama
+ class Grammar
+ class Printer < Struct.new(:ident_or_tags, :token_code, :lineno, keyword_init: true)
+ def translated_code(tag)
+ Code::PrinterCode.new(type: :printer, token_code: token_code, tag: tag).translated_code
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/grammar/reference.rb b/tool/lrama/lib/lrama/grammar/reference.rb
new file mode 100644
index 0000000000..c56e7673a6
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/reference.rb
@@ -0,0 +1,14 @@
+module Lrama
+ class Grammar
+ # type: :dollar or :at
+ # name: String (e.g. $$, $foo, $expr.right)
+ # number: Integer (e.g. $1)
+ # index: Integer
+ # ex_tag: "$<tag>1" (Optional)
+ class Reference < Struct.new(:type, :name, :number, :index, :ex_tag, :first_column, :last_column, keyword_init: true)
+ def value
+ name || number
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/grammar/rule.rb b/tool/lrama/lib/lrama/grammar/rule.rb
new file mode 100644
index 0000000000..0e06edc80d
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/rule.rb
@@ -0,0 +1,60 @@
+module Lrama
+ class Grammar
+ # _rhs holds original RHS element. Use rhs to refer to Symbol.
+ class Rule < Struct.new(:id, :_lhs, :lhs, :lhs_tag, :_rhs, :rhs, :token_code, :position_in_original_rule_rhs, :nullable, :precedence_sym, :lineno, keyword_init: true)
+ attr_accessor :original_rule
+
+ def ==(other)
+ self.class == other.class &&
+ self.lhs == other.lhs &&
+ self.lhs_tag == other.lhs_tag &&
+ self.rhs == other.rhs &&
+ self.token_code == other.token_code &&
+ self.position_in_original_rule_rhs == other.position_in_original_rule_rhs &&
+ self.nullable == other.nullable &&
+ self.precedence_sym == other.precedence_sym &&
+ self.lineno == other.lineno
+ end
+
+ # TODO: Change this to display_name
+ def to_s
+ l = lhs.id.s_value
+ r = empty_rule? ? "ε" : rhs.map {|r| r.id.s_value }.join(" ")
+
+ "#{l} -> #{r}"
+ end
+
+ # Used by #user_actions
+ def as_comment
+ l = lhs.id.s_value
+ r = empty_rule? ? "%empty" : rhs.map(&:display_name).join(" ")
+
+ "#{l}: #{r}"
+ end
+
+ def with_actions
+ "#{to_s} {#{token_code&.s_value}}"
+ end
+
+ # opt_nl: ε <-- empty_rule
+ # | '\n' <-- not empty_rule
+ def empty_rule?
+ rhs.empty?
+ end
+
+ def precedence
+ precedence_sym&.precedence
+ end
+
+ def initial_rule?
+ id == 0
+ end
+
+ def translated_code
+ return nil unless token_code
+
+ Code::RuleAction.new(type: :rule_action, token_code: token_code, rule: self).translated_code
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/grammar/rule_builder.rb b/tool/lrama/lib/lrama/grammar/rule_builder.rb
new file mode 100644
index 0000000000..4ba92bc804
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/rule_builder.rb
@@ -0,0 +1,268 @@
+module Lrama
+ class Grammar
+ class RuleBuilder
+ attr_accessor :lhs, :line
+ attr_reader :lhs_tag, :rhs, :user_code, :precedence_sym
+
+ def initialize(rule_counter, midrule_action_counter, position_in_original_rule_rhs = nil, lhs_tag: nil, skip_preprocess_references: false)
+ @rule_counter = rule_counter
+ @midrule_action_counter = midrule_action_counter
+ @position_in_original_rule_rhs = position_in_original_rule_rhs
+ @skip_preprocess_references = skip_preprocess_references
+
+ @lhs = nil
+ @lhs_tag = lhs_tag
+ @rhs = []
+ @user_code = nil
+ @precedence_sym = nil
+ @line = nil
+ @rules = []
+ @rule_builders_for_parameterizing_rules = []
+ @rule_builders_for_derived_rules = []
+ @rule_builders_for_inline_rules = []
+ @parameterizing_rules = []
+ @inline_rules = []
+ @midrule_action_rules = []
+ end
+
+ def add_rhs(rhs)
+ if !@line
+ @line = rhs.line
+ end
+
+ flush_user_code
+
+ @rhs << rhs
+ end
+
+ def user_code=(user_code)
+ if !@line
+ @line = user_code&.line
+ end
+
+ flush_user_code
+
+ @user_code = user_code
+ end
+
+ def precedence_sym=(precedence_sym)
+ flush_user_code
+
+ @precedence_sym = precedence_sym
+ end
+
+ def complete_input
+ freeze_rhs
+ end
+
+ def setup_rules(parameterizing_rule_resolver)
+ preprocess_references unless @skip_preprocess_references
+ if rhs.any? { |token| parameterizing_rule_resolver.find_inline(token) }
+ resolve_inline(parameterizing_rule_resolver)
+ else
+ process_rhs(parameterizing_rule_resolver)
+ end
+ build_rules
+ end
+
+ def rules
+ @parameterizing_rules + @inline_rules + @midrule_action_rules + @rules
+ end
+
+ private
+
+ def freeze_rhs
+ @rhs.freeze
+ end
+
+ def preprocess_references
+ numberize_references
+ end
+
+ def build_rules
+ tokens = @replaced_rhs
+
+ if tokens
+ rule = Rule.new(
+ id: @rule_counter.increment, _lhs: lhs, _rhs: tokens, lhs_tag: lhs_tag, token_code: user_code,
+ position_in_original_rule_rhs: @position_in_original_rule_rhs, precedence_sym: precedence_sym, lineno: line
+ )
+ @rules = [rule]
+ @parameterizing_rules = @rule_builders_for_parameterizing_rules.map do |rule_builder|
+ rule_builder.rules
+ end.flatten
+ @midrule_action_rules = @rule_builders_for_derived_rules.map do |rule_builder|
+ rule_builder.rules
+ end.flatten
+ @midrule_action_rules.each do |r|
+ r.original_rule = rule
+ end
+ else
+ @inline_rules = @rule_builders_for_inline_rules.map do |rule_builder|
+ rule_builder.rules
+ end.flatten
+ end
+ end
+
+ # rhs is a mixture of variety type of tokens like `Ident`, `InstantiateRule`, `UserCode` and so on.
+ # `#process_rhs` replaces some kind of tokens to `Ident` so that all `@replaced_rhs` are `Ident` or `Char`.
+ def process_rhs(parameterizing_rule_resolver)
+ return if @replaced_rhs
+
+ @replaced_rhs = []
+
+ rhs.each_with_index do |token, i|
+ case token
+ when Lrama::Lexer::Token::Char
+ @replaced_rhs << token
+ when Lrama::Lexer::Token::Ident
+ @replaced_rhs << token
+ when Lrama::Lexer::Token::InstantiateRule
+ parameterizing_rule = parameterizing_rule_resolver.find_rule(token)
+ raise "Unexpected token. #{token}" unless parameterizing_rule
+
+ bindings = Binding.new(parameterizing_rule, token.args)
+ lhs_s_value = lhs_s_value(token, bindings)
+ if (created_lhs = parameterizing_rule_resolver.created_lhs(lhs_s_value))
+ @replaced_rhs << created_lhs
+ else
+ lhs_token = Lrama::Lexer::Token::Ident.new(s_value: lhs_s_value, location: token.location)
+ @replaced_rhs << lhs_token
+ parameterizing_rule_resolver.created_lhs_list << lhs_token
+ parameterizing_rule.rhs_list.each do |r|
+ rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, lhs_tag: token.lhs_tag)
+ rule_builder.lhs = lhs_token
+ r.symbols.each { |sym| rule_builder.add_rhs(bindings.resolve_symbol(sym)) }
+ rule_builder.line = line
+ rule_builder.precedence_sym = r.precedence_sym
+ rule_builder.user_code = r.user_code
+ rule_builder.complete_input
+ rule_builder.setup_rules(parameterizing_rule_resolver)
+ @rule_builders_for_parameterizing_rules << rule_builder
+ end
+ end
+ when Lrama::Lexer::Token::UserCode
+ prefix = token.referred ? "@" : "$@"
+ tag = token.tag || lhs_tag
+ new_token = Lrama::Lexer::Token::Ident.new(s_value: prefix + @midrule_action_counter.increment.to_s)
+ @replaced_rhs << new_token
+
+ rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, i, lhs_tag: tag, skip_preprocess_references: true)
+ rule_builder.lhs = new_token
+ rule_builder.user_code = token
+ rule_builder.complete_input
+ rule_builder.setup_rules(parameterizing_rule_resolver)
+
+ @rule_builders_for_derived_rules << rule_builder
+ else
+ raise "Unexpected token. #{token}"
+ end
+ end
+ end
+
+ def lhs_s_value(token, bindings)
+ s_values = token.args.map do |arg|
+ resolved = bindings.resolve_symbol(arg)
+ if resolved.is_a?(Lexer::Token::InstantiateRule)
+ [resolved.s_value, resolved.args.map(&:s_value)]
+ else
+ resolved.s_value
+ end
+ end
+ "#{token.rule_name}_#{s_values.join('_')}"
+ end
+
+ def resolve_inline(parameterizing_rule_resolver)
+ rhs.each_with_index do |token, i|
+ if inline_rule = parameterizing_rule_resolver.find_inline(token)
+ inline_rule.rhs_list.each_with_index do |inline_rhs|
+ rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, lhs_tag: lhs_tag, skip_preprocess_references: true)
+ resolve_inline_rhs(rule_builder, inline_rhs, i)
+ rule_builder.lhs = lhs
+ rule_builder.line = line
+ rule_builder.user_code = replace_inline_user_code(inline_rhs, i)
+ rule_builder.complete_input
+ rule_builder.setup_rules(parameterizing_rule_resolver)
+ @rule_builders_for_inline_rules << rule_builder
+ end
+ end
+ end
+ end
+
+ def resolve_inline_rhs(rule_builder, inline_rhs, index)
+ rhs.each_with_index do |token, i|
+ if index == i
+ inline_rhs.symbols.each { |sym| rule_builder.add_rhs(sym) }
+ else
+ rule_builder.add_rhs(token)
+ end
+ end
+ end
+
+ def replace_inline_user_code(inline_rhs, index)
+ return user_code if inline_rhs.user_code.nil?
+ return user_code if user_code.nil?
+
+ code = user_code.s_value.gsub(/\$#{index + 1}/, inline_rhs.user_code.s_value)
+ Lrama::Lexer::Token::UserCode.new(s_value: code, location: user_code.location)
+ end
+
+ def numberize_references
+ # Bison n'th component is 1-origin
+ (rhs + [user_code]).compact.each.with_index(1) do |token, i|
+ next unless token.is_a?(Lrama::Lexer::Token::UserCode)
+
+ token.references.each do |ref|
+ ref_name = ref.name
+
+ if ref_name
+ if ref_name == '$'
+ ref.name = '$'
+ else
+ candidates = ([lhs] + rhs).each_with_index.select {|token, _i| token.referred_by?(ref_name) }
+
+ if candidates.size >= 2
+ token.invalid_ref(ref, "Referring symbol `#{ref_name}` is duplicated.")
+ end
+
+ unless (referring_symbol = candidates.first)
+ token.invalid_ref(ref, "Referring symbol `#{ref_name}` is not found.")
+ end
+
+ if referring_symbol[1] == 0 # Refers to LHS
+ ref.name = '$'
+ else
+ ref.number = referring_symbol[1]
+ end
+ end
+ end
+
+ if ref.number
+ # TODO: When Inlining is implemented, for example, if `$1` is expanded to multiple RHS tokens,
+ # `$2` needs to access `$2 + n` to actually access it. So, after the Inlining implementation,
+ # it needs resolves from number to index.
+ ref.index = ref.number
+ end
+
+ # TODO: Need to check index of @ too?
+ next if ref.type == :at
+
+ if ref.index
+ # TODO: Prohibit $0 even so Bison allows it?
+ # See: https://www.gnu.org/software/bison/manual/html_node/Actions.html
+ token.invalid_ref(ref, "Can not refer following component. #{ref.index} >= #{i}.") if ref.index >= i
+ rhs[ref.index - 1].referred = true
+ end
+ end
+ end
+ end
+
+ def flush_user_code
+ if (c = @user_code)
+ @rhs << c
+ @user_code = nil
+ end
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/grammar/stdlib.y b/tool/lrama/lib/lrama/grammar/stdlib.y
new file mode 100644
index 0000000000..d6e89c908c
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/stdlib.y
@@ -0,0 +1,122 @@
+/**********************************************************************
+
+ stdlib.y
+
+ This is lrama's standard library. It provides a number of
+ parameterizing rule definitions, such as options and lists,
+ that should be useful in a number of situations.
+
+**********************************************************************/
+
+// -------------------------------------------------------------------
+// Options
+
+/*
+ * program: option(number)
+ *
+ * =>
+ *
+ * program: option_number
+ * option_number: %empty
+ * option_number: number
+ */
+%rule option(X): /* empty */
+ | X
+ ;
+
+// -------------------------------------------------------------------
+// Sequences
+
+/*
+ * program: preceded(opening, X)
+ *
+ * =>
+ *
+ * program: preceded_opening_X
+ * preceded_opening_X: opening X
+ */
+%rule preceded(opening, X): opening X { $$ = $2; }
+ ;
+
+/*
+ * program: terminated(X, closing)
+ *
+ * =>
+ *
+ * program: terminated_X_closing
+ * terminated_X_closing: X closing
+ */
+%rule terminated(X, closing): X closing { $$ = $1; }
+ ;
+
+/*
+ * program: delimited(opening, X, closing)
+ *
+ * =>
+ *
+ * program: delimited_opening_X_closing
+ * delimited_opening_X_closing: opening X closing
+ */
+%rule delimited(opening, X, closing): opening X closing { $$ = $2; }
+ ;
+
+// -------------------------------------------------------------------
+// Lists
+
+/*
+ * program: list(number)
+ *
+ * =>
+ *
+ * program: list_number
+ * list_number: %empty
+ * list_number: list_number number
+ */
+%rule list(X): /* empty */
+ | list(X) X
+ ;
+
+/*
+ * program: nonempty_list(number)
+ *
+ * =>
+ *
+ * program: nonempty_list_number
+ * nonempty_list_number: number
+ * nonempty_list_number: nonempty_list_number number
+ */
+%rule nonempty_list(X): X
+ | nonempty_list(X) X
+ ;
+
+/*
+ * program: separated_nonempty_list(comma, number)
+ *
+ * =>
+ *
+ * program: separated_nonempty_list_comma_number
+ * separated_nonempty_list_comma_number: number
+ * separated_nonempty_list_comma_number: separated_nonempty_list_comma_number comma number
+ */
+%rule separated_nonempty_list(separator, X): X
+ | separated_nonempty_list(separator, X) separator X
+ ;
+
+/*
+ * program: separated_list(comma, number)
+ *
+ * =>
+ *
+ * program: separated_list_comma_number
+ * separated_list_comma_number: option_separated_nonempty_list_comma_number
+ * option_separated_nonempty_list_comma_number: %empty
+ * option_separated_nonempty_list_comma_number: separated_nonempty_list_comma_number
+ * separated_nonempty_list_comma_number: number
+ * separated_nonempty_list_comma_number: comma separated_nonempty_list_comma_number number
+ */
+%rule separated_list(separator, X): option(separated_nonempty_list(separator, X))
+ ;
+
+%%
+
+%union{};
diff --git a/tool/lrama/lib/lrama/grammar/symbol.rb b/tool/lrama/lib/lrama/grammar/symbol.rb
new file mode 100644
index 0000000000..deb67ad9a8
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/symbol.rb
@@ -0,0 +1,103 @@
+# Symbol is both of nterm and term
+# `number` is both for nterm and term
+# `token_id` is tokentype for term, internal sequence number for nterm
+#
+# TODO: Add validation for ASCII code range for Token::Char
+
+module Lrama
+ class Grammar
+ class Symbol
+ attr_accessor :id, :alias_name, :tag, :number, :token_id, :nullable, :precedence,
+ :printer, :destructor, :error_token, :first_set, :first_set_bitmap
+ attr_reader :term
+ attr_writer :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol
+
+ def initialize(id:, term:, alias_name: nil, number: nil, tag: nil, token_id: nil, nullable: nil, precedence: nil, printer: nil, destructor: nil)
+ @id = id
+ @alias_name = alias_name
+ @number = number
+ @tag = tag
+ @term = term
+ @token_id = token_id
+ @nullable = nullable
+ @precedence = precedence
+ @printer = printer
+ @destructor = destructor
+ end
+
+ def term?
+ term
+ end
+
+ def nterm?
+ !term
+ end
+
+ def eof_symbol?
+ !!@eof_symbol
+ end
+
+ def error_symbol?
+ !!@error_symbol
+ end
+
+ def undef_symbol?
+ !!@undef_symbol
+ end
+
+ def accept_symbol?
+ !!@accept_symbol
+ end
+
+ def display_name
+ alias_name || id.s_value
+ end
+
+ # name for yysymbol_kind_t
+ #
+ # See: b4_symbol_kind_base
+ # @type var name: String
+ def enum_name
+ case
+ when accept_symbol?
+ name = "YYACCEPT"
+ when eof_symbol?
+ name = "YYEOF"
+ when term? && id.is_a?(Lrama::Lexer::Token::Char)
+ name = number.to_s + display_name
+ when term? && id.is_a?(Lrama::Lexer::Token::Ident)
+ name = id.s_value
+ when nterm? && (id.s_value.include?("$") || id.s_value.include?("@"))
+ name = number.to_s + id.s_value
+ when nterm?
+ name = id.s_value
+ else
+ raise "Unexpected #{self}"
+ end
+
+ "YYSYMBOL_" + name.gsub(/\W+/, "_")
+ end
+
+ # comment for yysymbol_kind_t
+ def comment
+ case
+ when accept_symbol?
+ # YYSYMBOL_YYACCEPT
+ id.s_value
+ when eof_symbol?
+ # YYEOF
+ alias_name
+ when (term? && 0 < token_id && token_id < 128)
+ # YYSYMBOL_3_backslash_, YYSYMBOL_14_
+ alias_name || id.s_value
+ when id.s_value.include?("$") || id.s_value.include?("@")
+ # YYSYMBOL_21_1
+ id.s_value
+ else
+ # YYSYMBOL_keyword_class, YYSYMBOL_strings_1
+ alias_name || id.s_value
+ end
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/grammar/symbols.rb b/tool/lrama/lib/lrama/grammar/symbols.rb
new file mode 100644
index 0000000000..cc9b4ec559
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/symbols.rb
@@ -0,0 +1 @@
+require_relative "symbols/resolver"
diff --git a/tool/lrama/lib/lrama/grammar/symbols/resolver.rb b/tool/lrama/lib/lrama/grammar/symbols/resolver.rb
new file mode 100644
index 0000000000..1788ed63fa
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/symbols/resolver.rb
@@ -0,0 +1,293 @@
+module Lrama
+ class Grammar
+ class Symbols
+ class Resolver
+ attr_reader :terms, :nterms
+
+ def initialize
+ @terms = []
+ @nterms = []
+ end
+
+ def symbols
+ @symbols ||= (@terms + @nterms)
+ end
+
+ def sort_by_number!
+ symbols.sort_by!(&:number)
+ end
+
+ def add_term(id:, alias_name: nil, tag: nil, token_id: nil, replace: false)
+ if token_id && (sym = find_symbol_by_token_id(token_id))
+ if replace
+ sym.id = id
+ sym.alias_name = alias_name
+ sym.tag = tag
+ end
+
+ return sym
+ end
+
+ if (sym = find_symbol_by_id(id))
+ return sym
+ end
+
+ @symbols = nil
+ term = Symbol.new(
+ id: id, alias_name: alias_name, number: nil, tag: tag,
+ term: true, token_id: token_id, nullable: false
+ )
+ @terms << term
+ term
+ end
+
+ def add_nterm(id:, alias_name: nil, tag: nil)
+ return if find_symbol_by_id(id)
+
+ @symbols = nil
+ nterm = Symbol.new(
+ id: id, alias_name: alias_name, number: nil, tag: tag,
+ term: false, token_id: nil, nullable: nil,
+ )
+ @nterms << nterm
+ nterm
+ end
+
+ def find_symbol_by_s_value(s_value)
+ symbols.find { |s| s.id.s_value == s_value }
+ end
+
+ def find_symbol_by_s_value!(s_value)
+ find_symbol_by_s_value(s_value) || (raise "Symbol not found. value: `#{s_value}`")
+ end
+
+ def find_symbol_by_id(id)
+ symbols.find do |s|
+ s.id == id || s.alias_name == id.s_value
+ end
+ end
+
+ def find_symbol_by_id!(id)
+ find_symbol_by_id(id) || (raise "Symbol not found. #{id}")
+ end
+
+ def find_symbol_by_token_id(token_id)
+ symbols.find {|s| s.token_id == token_id }
+ end
+
+ def find_symbol_by_number!(number)
+ sym = symbols[number]
+
+ raise "Symbol not found. number: `#{number}`" unless sym
+ raise "[BUG] Symbol number mismatch. #{number}, #{sym}" if sym.number != number
+
+ sym
+ end
+
+ def fill_symbol_number
+ # YYEMPTY = -2
+ # YYEOF = 0
+ # YYerror = 1
+ # YYUNDEF = 2
+ @number = 3
+ fill_terms_number
+ fill_nterms_number
+ end
+
+ def fill_nterm_type(types)
+ types.each do |type|
+ nterm = find_nterm_by_id!(type.id)
+ nterm.tag = type.tag
+ end
+ end
+
+ def fill_printer(printers)
+ symbols.each do |sym|
+ printers.each do |printer|
+ printer.ident_or_tags.each do |ident_or_tag|
+ case ident_or_tag
+ when Lrama::Lexer::Token::Ident
+ sym.printer = printer if sym.id == ident_or_tag
+ when Lrama::Lexer::Token::Tag
+ sym.printer = printer if sym.tag == ident_or_tag
+ else
+ raise "Unknown token type. #{printer}"
+ end
+ end
+ end
+ end
+ end
+
+ def fill_destructor(destructors)
+ symbols.each do |sym|
+ destructors.each do |destructor|
+ destructor.ident_or_tags.each do |ident_or_tag|
+ case ident_or_tag
+ when Lrama::Lexer::Token::Ident
+ sym.destructor = destructor if sym.id == ident_or_tag
+ when Lrama::Lexer::Token::Tag
+ sym.destructor = destructor if sym.tag == ident_or_tag
+ else
+ raise "Unknown token type. #{destructor}"
+ end
+ end
+ end
+ end
+ end
+
+ def fill_error_token(error_tokens)
+ symbols.each do |sym|
+ error_tokens.each do |token|
+ token.ident_or_tags.each do |ident_or_tag|
+ case ident_or_tag
+ when Lrama::Lexer::Token::Ident
+ sym.error_token = token if sym.id == ident_or_tag
+ when Lrama::Lexer::Token::Tag
+ sym.error_token = token if sym.tag == ident_or_tag
+ else
+ raise "Unknown token type. #{token}"
+ end
+ end
+ end
+ end
+ end
+
+ def token_to_symbol(token)
+ case token
+ when Lrama::Lexer::Token
+ find_symbol_by_id!(token)
+ else
+ raise "Unknown class: #{token}"
+ end
+ end
+
+ def validate!
+ validate_number_uniqueness!
+ validate_alias_name_uniqueness!
+ end
+
+ private
+
+ def find_nterm_by_id!(id)
+ @nterms.find do |s|
+ s.id == id
+ end || (raise "Symbol not found. #{id}")
+ end
+
+ def fill_terms_number
+ # Character literal in grammar file has
+ # token id corresponding to ASCII code by default,
+ # so start token_id from 256.
+ token_id = 256
+
+ @terms.each do |sym|
+ while used_numbers[@number] do
+ @number += 1
+ end
+
+ if sym.number.nil?
+ sym.number = @number
+ used_numbers[@number] = true
+ @number += 1
+ end
+
+ # If id is Token::Char, it uses ASCII code
+ if sym.token_id.nil?
+ if sym.id.is_a?(Lrama::Lexer::Token::Char)
+ # Ignore ' on the both sides
+ case sym.id.s_value[1..-2]
+ when "\\b"
+ sym.token_id = 8
+ when "\\f"
+ sym.token_id = 12
+ when "\\n"
+ sym.token_id = 10
+ when "\\r"
+ sym.token_id = 13
+ when "\\t"
+ sym.token_id = 9
+ when "\\v"
+ sym.token_id = 11
+ when "\""
+ sym.token_id = 34
+ when "'"
+ sym.token_id = 39
+ when "\\\\"
+ sym.token_id = 92
+ when /\A\\(\d+)\z/
+ unless (id = Integer($1, 8)).nil?
+ sym.token_id = id
+ else
+ raise "Unknown Char s_value #{sym}"
+ end
+ when /\A(.)\z/
+ unless (id = $1&.bytes&.first).nil?
+ sym.token_id = id
+ else
+ raise "Unknown Char s_value #{sym}"
+ end
+ else
+ raise "Unknown Char s_value #{sym}"
+ end
+ else
+ sym.token_id = token_id
+ token_id += 1
+ end
+ end
+ end
+ end
+
+ def fill_nterms_number
+ token_id = 0
+
+ @nterms.each do |sym|
+ while used_numbers[@number] do
+ @number += 1
+ end
+
+ if sym.number.nil?
+ sym.number = @number
+ used_numbers[@number] = true
+ @number += 1
+ end
+
+ if sym.token_id.nil?
+ sym.token_id = token_id
+ token_id += 1
+ end
+ end
+ end
+
+ def used_numbers
+ return @used_numbers if defined?(@used_numbers)
+
+ @used_numbers = {}
+ symbols.map(&:number).each do |n|
+ @used_numbers[n] = true
+ end
+ @used_numbers
+ end
+
+ def validate_number_uniqueness!
+ invalid = symbols.group_by(&:number).select do |number, syms|
+ syms.count > 1
+ end
+
+ return if invalid.empty?
+
+ raise "Symbol number is duplicated. #{invalid}"
+ end
+
+ def validate_alias_name_uniqueness!
+ invalid = symbols.select(&:alias_name).group_by(&:alias_name).select do |alias_name, syms|
+ syms.count > 1
+ end
+
+ return if invalid.empty?
+
+ raise "Symbol alias name is duplicated. #{invalid}"
+ end
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/grammar/type.rb b/tool/lrama/lib/lrama/grammar/type.rb
new file mode 100644
index 0000000000..6b4b0961a1
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/type.rb
@@ -0,0 +1,18 @@
+module Lrama
+ class Grammar
+ class Type
+ attr_reader :id, :tag
+
+ def initialize(id:, tag:)
+ @id = id
+ @tag = tag
+ end
+
+ def ==(other)
+ self.class == other.class &&
+ self.id == other.id &&
+ self.tag == other.tag
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/grammar/union.rb b/tool/lrama/lib/lrama/grammar/union.rb
new file mode 100644
index 0000000000..854bffb5c1
--- /dev/null
+++ b/tool/lrama/lib/lrama/grammar/union.rb
@@ -0,0 +1,10 @@
+module Lrama
+ class Grammar
+ class Union < Struct.new(:code, :lineno, keyword_init: true)
+ def braces_less_code
+ # Braces is already removed by lexer
+ code.s_value
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/lexer.rb b/tool/lrama/lib/lrama/lexer.rb
index 6c1139b416..40622a51b4 100644
--- a/tool/lrama/lib/lrama/lexer.rb
+++ b/tool/lrama/lib/lrama/lexer.rb
@@ -1,369 +1,188 @@
require "strscan"
-require "lrama/report"
+
+require "lrama/lexer/grammar_file"
+require "lrama/lexer/location"
+require "lrama/lexer/token"
module Lrama
- # Lexer for parse.y
class Lexer
- include Lrama::Report::Duration
-
- # s_value is semantic value
- Token = Struct.new(:type, :s_value, keyword_init: true) do
- Type = Struct.new(:id, :name, keyword_init: true)
-
- attr_accessor :line, :column, :referred
- # For User_code
- attr_accessor :references
-
- def to_s
- "#{super} line: #{line}, column: #{column}"
- end
-
- @i = 0
- @types = []
-
- def self.define_type(name)
- type = Type.new(id: @i, name: name.to_s)
- const_set(name, type)
- @types << type
- @i += 1
- end
-
- # Token types
- define_type(:P_expect) # %expect
- define_type(:P_define) # %define
- define_type(:P_printer) # %printer
- define_type(:P_lex_param) # %lex-param
- define_type(:P_parse_param) # %parse-param
- define_type(:P_initial_action) # %initial-action
- define_type(:P_union) # %union
- define_type(:P_token) # %token
- define_type(:P_type) # %type
- define_type(:P_nonassoc) # %nonassoc
- define_type(:P_left) # %left
- define_type(:P_right) # %right
- define_type(:P_prec) # %prec
- define_type(:User_code) # { ... }
- define_type(:Tag) # <int>
- define_type(:Number) # 0
- define_type(:Ident_Colon) # k_if:, k_if : (spaces can be there)
- define_type(:Ident) # api.pure, tNUMBER
- define_type(:Semicolon) # ;
- define_type(:Bar) # |
- define_type(:String) # "str"
- define_type(:Char) # '+'
+ attr_reader :head_line, :head_column, :line
+ attr_accessor :status, :end_symbol
+
+ SYMBOLS = ['%{', '%}', '%%', '{', '}', '\[', '\]', '\(', '\)', '\,', ':', '\|', ';']
+ PERCENT_TOKENS = %w(
+ %union
+ %token
+ %type
+ %left
+ %right
+ %nonassoc
+ %expect
+ %define
+ %require
+ %printer
+ %destructor
+ %lex-param
+ %parse-param
+ %initial-action
+ %precedence
+ %prec
+ %error-token
+ %before-reduce
+ %after-reduce
+ %after-shift-error-token
+ %after-shift
+ %after-pop-stack
+ %empty
+ %code
+ %rule
+ %no-stdlib
+ %inline
+ )
+
+ def initialize(grammar_file)
+ @grammar_file = grammar_file
+ @scanner = StringScanner.new(grammar_file.text)
+ @head_column = @head = @scanner.pos
+ @head_line = @line = 1
+ @status = :initial
+ @end_symbol = nil
end
- # States
- #
- # See: https://www.gnu.org/software/bison/manual/html_node/Grammar-Outline.html
- Initial = 0
- Prologue = 1
- BisonDeclarations = 2
- GrammarRules = 3
- Epilogue = 4
-
- # Token types
-
- attr_reader :prologue, :bison_declarations, :grammar_rules, :epilogue,
- :bison_declarations_tokens, :grammar_rules_tokens
-
- def initialize(text)
- @text = text
- @state = Initial
- # Array of texts
- @prologue = []
- @bison_declarations = []
- @grammar_rules = []
- @epilogue = []
-
- #
- @bison_declarations_tokens = []
- @grammar_rules_tokens = []
-
- @debug = false
-
- report_duration(:lex) do
- lex_text
- lex_bison_declarations_tokens
- lex_grammar_rules_tokens
+ def next_token
+ case @status
+ when :initial
+ lex_token
+ when :c_declaration
+ lex_c_code
end
end
- private
-
- def create_token(type, s_value, line, column)
- t = Token.new(type: type, s_value: s_value)
- t.line = line
- t.column = column
-
- return t
+ def column
+ @scanner.pos - @head
end
- # TODO: Remove this
- def lex_text
- @text.each_line.with_index(1) do |string, lineno|
- case @state
- when Initial
- # Skip until "%{"
- if string == "%{\n"
- @state = Prologue
- @prologue << ["", lineno]
- next
- end
- when Prologue
- # Between "%{" and "%}"
- if string == "%}\n"
- @state = BisonDeclarations
- @prologue << ["", lineno]
- next
- end
-
- @prologue << [string, lineno]
- when BisonDeclarations
- if string == "%%\n"
- @state = GrammarRules
- next
- end
-
- @bison_declarations << [string, lineno]
- when GrammarRules
- # Between "%%" and "%%"
- if string == "%%\n"
- @state = Epilogue
- next
- end
-
- @grammar_rules << [string, lineno]
- when Epilogue
- @epilogue << [string, lineno]
- else
- raise "Unknown state: #{@state}"
- end
- end
+ def location
+ Location.new(
+ grammar_file: @grammar_file,
+ first_line: @head_line, first_column: @head_column,
+ last_line: line, last_column: column
+ )
end
- # See:
- # * https://www.gnu.org/software/bison/manual/html_node/Decl-Summary.html
- # * https://www.gnu.org/software/bison/manual/html_node/Symbol-Decls.html
- # * https://www.gnu.org/software/bison/manual/html_node/Empty-Rules.html
- def lex_common(lines, tokens)
- line = lines.first[1]
- column = 0
- ss = StringScanner.new(lines.map(&:first).join)
-
- while !ss.eos? do
+ def lex_token
+ while !@scanner.eos? do
case
- when ss.scan(/\n/)
- line += 1
- column = ss.pos
- when ss.scan(/\s+/)
- # skip
- when ss.scan(/;/)
- tokens << create_token(Token::Semicolon, ss[0], line, ss.pos - column)
- when ss.scan(/\|/)
- tokens << create_token(Token::Bar, ss[0], line, ss.pos - column)
- when ss.scan(/(\d+)/)
- tokens << create_token(Token::Number, Integer(ss[0]), line, ss.pos - column)
- when ss.scan(/(<[a-zA-Z0-9_]+>)/)
- tokens << create_token(Token::Tag, ss[0], line, ss.pos - column)
- when ss.scan(/([a-zA-Z_.][-a-zA-Z0-9_.]*)\s*:/)
- tokens << create_token(Token::Ident_Colon, ss[1], line, ss.pos - column)
- when ss.scan(/([a-zA-Z_.][-a-zA-Z0-9_.]*)/)
- tokens << create_token(Token::Ident, ss[0], line, ss.pos - column)
- when ss.scan(/%expect/)
- tokens << create_token(Token::P_expect, ss[0], line, ss.pos - column)
- when ss.scan(/%define/)
- tokens << create_token(Token::P_define, ss[0], line, ss.pos - column)
- when ss.scan(/%printer/)
- tokens << create_token(Token::P_printer, ss[0], line, ss.pos - column)
- when ss.scan(/%lex-param/)
- tokens << create_token(Token::P_lex_param, ss[0], line, ss.pos - column)
- when ss.scan(/%parse-param/)
- tokens << create_token(Token::P_parse_param, ss[0], line, ss.pos - column)
- when ss.scan(/%initial-action/)
- tokens << create_token(Token::P_initial_action, ss[0], line, ss.pos - column)
- when ss.scan(/%union/)
- tokens << create_token(Token::P_union, ss[0], line, ss.pos - column)
- when ss.scan(/%token/)
- tokens << create_token(Token::P_token, ss[0], line, ss.pos - column)
- when ss.scan(/%type/)
- tokens << create_token(Token::P_type, ss[0], line, ss.pos - column)
- when ss.scan(/%nonassoc/)
- tokens << create_token(Token::P_nonassoc, ss[0], line, ss.pos - column)
- when ss.scan(/%left/)
- tokens << create_token(Token::P_left, ss[0], line, ss.pos - column)
- when ss.scan(/%right/)
- tokens << create_token(Token::P_right, ss[0], line, ss.pos - column)
- when ss.scan(/%prec/)
- tokens << create_token(Token::P_prec, ss[0], line, ss.pos - column)
- when ss.scan(/{/)
- token, line = lex_user_code(ss, line, ss.pos - column, lines)
- tokens << token
- when ss.scan(/"/)
- string, line = lex_string(ss, "\"", line, lines)
- token = create_token(Token::String, string, line, ss.pos - column)
- tokens << token
- when ss.scan(/\/\*/)
- # TODO: Need to keep comment?
- line = lex_comment(ss, line, lines, "")
- when ss.scan(/\/\//)
- line = lex_line_comment(ss, line, "")
- when ss.scan(/'(.)'/)
- tokens << create_token(Token::Char, ss[0], line, ss.pos - column)
- when ss.scan(/'\\(.)'/) # '\\', '\t'
- tokens << create_token(Token::Char, ss[0], line, ss.pos - column)
- when ss.scan(/'\\(\d+)'/) # '\13'
- tokens << create_token(Token::Char, ss[0], line, ss.pos - column)
- when ss.scan(/%empty/)
- # skip
+ when @scanner.scan(/\n/)
+ newline
+ when @scanner.scan(/\s+/)
+ # noop
+ when @scanner.scan(/\/\*/)
+ lex_comment
+ when @scanner.scan(/\/\/.*(?<newline>\n)?/)
+ newline if @scanner[:newline]
else
- l = line - lines.first[1]
- split = ss.string.split("\n")
- col = ss.pos - split[0...l].join("\n").length
- raise "Parse error (unknown token): #{split[l]} \"#{ss.string[ss.pos]}\" (#{line}: #{col})"
+ break
end
end
- end
-
- def lex_bison_declarations_tokens
- lex_common(@bison_declarations, @bison_declarations_tokens)
- end
-
- def lex_user_code(ss, line, column, lines)
- first_line = line
- first_column = column
- debug("Enter lex_user_code: #{line}")
- brace_count = 1
- str = "{"
- # Array of [type, $n, tag, first column, last column]
- # TODO: Is it better to keep string, like "$$", and use gsub?
- references = []
-
- while !ss.eos? do
- case
- when ss.scan(/\n/)
- line += 1
- when ss.scan(/"/)
- string, line = lex_string(ss, "\"", line, lines)
- str << string
- next
- when ss.scan(/'/)
- string, line = lex_string(ss, "'", line, lines)
- str << string
- next
- when ss.scan(/\$(<[a-zA-Z0-9_]+>)?\$/) # $$, $<long>$
- tag = ss[1] ? create_token(Token::Tag, ss[1], line, str.length) : nil
- references << [:dollar, "$", tag, str.length, str.length + ss[0].length - 1]
- when ss.scan(/\$(<[a-zA-Z0-9_]+>)?(\d+)/) # $1, $2, $<long>1
- tag = ss[1] ? create_token(Token::Tag, ss[1], line, str.length) : nil
- references << [:dollar, Integer(ss[2]), tag, str.length, str.length + ss[0].length - 1]
- when ss.scan(/@\$/) # @$
- references << [:at, "$", nil, str.length, str.length + ss[0].length - 1]
- when ss.scan(/@(\d)+/) # @1
- references << [:at, Integer(ss[1]), nil, str.length, str.length + ss[0].length - 1]
- when ss.scan(/{/)
- brace_count += 1
- when ss.scan(/}/)
- brace_count -= 1
- debug("Return lex_user_code: #{line}")
- if brace_count == 0
- str << ss[0]
- user_code = Token.new(type: Token::User_code, s_value: str.freeze)
- user_code.line = first_line
- user_code.column = first_column
- user_code.references = references
- return [user_code, line]
+ reset_first_position
+
+ case
+ when @scanner.eos?
+ return
+ when @scanner.scan(/#{SYMBOLS.join('|')}/)
+ return [@scanner.matched, @scanner.matched]
+ when @scanner.scan(/#{PERCENT_TOKENS.join('|')}/)
+ return [@scanner.matched, @scanner.matched]
+ when @scanner.scan(/[\?\+\*]/)
+ return [@scanner.matched, @scanner.matched]
+ when @scanner.scan(/<\w+>/)
+ return [:TAG, Lrama::Lexer::Token::Tag.new(s_value: @scanner.matched, location: location)]
+ when @scanner.scan(/'.'/)
+ return [:CHARACTER, Lrama::Lexer::Token::Char.new(s_value: @scanner.matched, location: location)]
+ when @scanner.scan(/'\\\\'|'\\b'|'\\t'|'\\f'|'\\r'|'\\n'|'\\v'|'\\13'/)
+ return [:CHARACTER, Lrama::Lexer::Token::Char.new(s_value: @scanner.matched, location: location)]
+ when @scanner.scan(/".*?"/)
+ return [:STRING, %Q(#{@scanner.matched})]
+ when @scanner.scan(/\d+/)
+ return [:INTEGER, Integer(@scanner.matched)]
+ when @scanner.scan(/([a-zA-Z_.][-a-zA-Z0-9_.]*)/)
+ token = Lrama::Lexer::Token::Ident.new(s_value: @scanner.matched, location: location)
+ type =
+ if @scanner.check(/\s*(\[\s*[a-zA-Z_.][-a-zA-Z0-9_.]*\s*\])?\s*:/)
+ :IDENT_COLON
+ else
+ :IDENTIFIER
end
- when ss.scan(/\/\*/)
- str << ss[0]
- line = lex_comment(ss, line, lines, str)
- when ss.scan(/\/\//)
- str << ss[0]
- line = lex_line_comment(ss, line, str)
- else
- # noop, just consume char
- str << ss.getch
- next
- end
-
- str << ss[0]
+ return [type, token]
+ else
+ raise ParseError, "Unexpected token: #{@scanner.peek(10).chomp}."
end
-
- # Reach to end of input but brace does not match
- l = line - lines.first[1]
- raise "Parse error (brace mismatch): #{ss.string.split("\n")[l]} \"#{ss.string[ss.pos]}\" (#{line}: #{ss.pos})"
end
- def lex_string(ss, terminator, line, lines)
- debug("Enter lex_string: #{line}")
-
- str = terminator.dup
-
- while (c = ss.getch) do
- str << c
+ def lex_c_code
+ nested = 0
+ code = ''
+ reset_first_position
- case c
- when "\n"
- line += 1
- when terminator
- debug("Return lex_string: #{line}")
- return [str, line]
- else
- # noop
- end
- end
-
- # Reach to end of input but quote does not match
- l = line - lines.first[1]
- raise "Parse error (quote mismatch): #{ss.string.split("\n")[l]} \"#{ss.string[ss.pos]}\" (#{line}: #{ss.pos})"
- end
-
- # /* */ style comment
- def lex_comment(ss, line, lines, str)
- while !ss.eos? do
+ while !@scanner.eos? do
case
- when ss.scan(/\n/)
- line += 1
- when ss.scan(/\*\//)
- return line
+ when @scanner.scan(/{/)
+ code += @scanner.matched
+ nested += 1
+ when @scanner.scan(/}/)
+ if nested == 0 && @end_symbol == '}'
+ @scanner.unscan
+ return [:C_DECLARATION, Lrama::Lexer::Token::UserCode.new(s_value: code, location: location)]
+ else
+ code += @scanner.matched
+ nested -= 1
+ end
+ when @scanner.check(/#{@end_symbol}/)
+ return [:C_DECLARATION, Lrama::Lexer::Token::UserCode.new(s_value: code, location: location)]
+ when @scanner.scan(/\n/)
+ code += @scanner.matched
+ newline
+ when @scanner.scan(/".*?"/)
+ code += %Q(#{@scanner.matched})
+ @line += @scanner.matched.count("\n")
+ when @scanner.scan(/'.*?'/)
+ code += %Q(#{@scanner.matched})
+ when @scanner.scan(/[^\"'\{\}\n]+/)
+ code += @scanner.matched
+ when @scanner.scan(/#{Regexp.escape(@end_symbol)}/)
+ code += @scanner.matched
else
- str << ss.getch
- next
+ code += @scanner.getch
end
-
- str << ss[0]
end
-
- # Reach to end of input but quote does not match
- l = line - lines.first[1]
- raise "Parse error (comment mismatch): #{ss.string.split("\n")[l]} \"#{ss.string[ss.pos]}\" (#{line}: #{ss.pos})"
+ raise ParseError, "Unexpected code: #{code}."
end
- # // style comment
- def lex_line_comment(ss, line, str)
- while !ss.eos? do
+ private
+
+ def lex_comment
+ while !@scanner.eos? do
case
- when ss.scan(/\n/)
- return line + 1
+ when @scanner.scan(/\n/)
+ newline
+ when @scanner.scan(/\*\//)
+ return
else
- str << ss.getch
- next
+ @scanner.getch
end
-
- str << ss[0]
end
-
- line # Reach to end of input
end
- def lex_grammar_rules_tokens
- lex_common(@grammar_rules, @grammar_rules_tokens)
+ def reset_first_position
+ @head_line = line
+ @head_column = column
end
- def debug(msg)
- return unless @debug
- puts "#{msg}\n"
+ def newline
+ @line += 1
+ @head = @scanner.pos
end
end
end
diff --git a/tool/lrama/lib/lrama/lexer/grammar_file.rb b/tool/lrama/lib/lrama/lexer/grammar_file.rb
new file mode 100644
index 0000000000..3d3368625d
--- /dev/null
+++ b/tool/lrama/lib/lrama/lexer/grammar_file.rb
@@ -0,0 +1,31 @@
+module Lrama
+ class Lexer
+ class GrammarFile
+ class Text < String
+ def inspect
+ length <= 50 ? super : "#{self[0..47]}...".inspect
+ end
+ end
+
+ attr_reader :path, :text
+
+ def initialize(path, text)
+ @path = path
+ @text = Text.new(text).freeze
+ end
+
+ def inspect
+ "<#{self.class}: @path=#{path}, @text=#{text.inspect}>"
+ end
+
+ def ==(other)
+ self.class == other.class &&
+ self.path == other.path
+ end
+
+ def lines
+ @lines ||= text.split("\n")
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/lexer/location.rb b/tool/lrama/lib/lrama/lexer/location.rb
new file mode 100644
index 0000000000..aefce3e16b
--- /dev/null
+++ b/tool/lrama/lib/lrama/lexer/location.rb
@@ -0,0 +1,97 @@
+module Lrama
+ class Lexer
+ class Location
+ attr_reader :grammar_file, :first_line, :first_column, :last_line, :last_column
+
+ def initialize(grammar_file:, first_line:, first_column:, last_line:, last_column:)
+ @grammar_file = grammar_file
+ @first_line = first_line
+ @first_column = first_column
+ @last_line = last_line
+ @last_column = last_column
+ end
+
+ def ==(other)
+ self.class == other.class &&
+ self.grammar_file == other.grammar_file &&
+ self.first_line == other.first_line &&
+ self.first_column == other.first_column &&
+ self.last_line == other.last_line &&
+ self.last_column == other.last_column
+ end
+
+ def partial_location(left, right)
+ offset = -first_column
+ new_first_line = -1
+ new_first_column = -1
+ new_last_line = -1
+ new_last_column = -1
+
+ _text.each.with_index do |line, index|
+ new_offset = offset + line.length + 1
+
+ if offset <= left && left <= new_offset
+ new_first_line = first_line + index
+ new_first_column = left - offset
+ end
+
+ if offset <= right && right <= new_offset
+ new_last_line = first_line + index
+ new_last_column = right - offset
+ end
+
+ offset = new_offset
+ end
+
+ Location.new(
+ grammar_file: grammar_file,
+ first_line: new_first_line, first_column: new_first_column,
+ last_line: new_last_line, last_column: new_last_column
+ )
+ end
+
+ def to_s
+ "#{path} (#{first_line},#{first_column})-(#{last_line},#{last_column})"
+ end
+
+ def generate_error_message(error_message)
+ <<~ERROR.chomp
+ #{path}:#{first_line}:#{first_column}: #{error_message}
+ #{line_with_carets}
+ ERROR
+ end
+
+ def line_with_carets
+ <<~TEXT
+ #{text}
+ #{carets}
+ TEXT
+ end
+
+ private
+
+ def path
+ grammar_file.path
+ end
+
+ def blanks
+ (text[0...first_column] or raise "#{first_column} is invalid").gsub(/[^\t]/, ' ')
+ end
+
+ def carets
+ blanks + '^' * (last_column - first_column)
+ end
+
+ def text
+ @text ||= _text.join("\n")
+ end
+
+ def _text
+ @_text ||=begin
+ range = (first_line - 1)...last_line
+ grammar_file.lines[range] or raise "#{range} is invalid"
+ end
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/lexer/token.rb b/tool/lrama/lib/lrama/lexer/token.rb
new file mode 100644
index 0000000000..59b49d5fba
--- /dev/null
+++ b/tool/lrama/lib/lrama/lexer/token.rb
@@ -0,0 +1,56 @@
+require 'lrama/lexer/token/char'
+require 'lrama/lexer/token/ident'
+require 'lrama/lexer/token/instantiate_rule'
+require 'lrama/lexer/token/tag'
+require 'lrama/lexer/token/user_code'
+
+module Lrama
+ class Lexer
+ class Token
+ attr_reader :s_value, :location
+ attr_accessor :alias_name, :referred
+
+ def initialize(s_value:, alias_name: nil, location: nil)
+ s_value.freeze
+ @s_value = s_value
+ @alias_name = alias_name
+ @location = location
+ end
+
+ def to_s
+ "value: `#{s_value}`, location: #{location}"
+ end
+
+ def referred_by?(string)
+ [self.s_value, self.alias_name].compact.include?(string)
+ end
+
+ def ==(other)
+ self.class == other.class && self.s_value == other.s_value
+ end
+
+ def first_line
+ location.first_line
+ end
+ alias :line :first_line
+
+ def first_column
+ location.first_column
+ end
+ alias :column :first_column
+
+ def last_line
+ location.last_line
+ end
+
+ def last_column
+ location.last_column
+ end
+
+ def invalid_ref(ref, message)
+ location = self.location.partial_location(ref.first_column, ref.last_column)
+ raise location.generate_error_message(message)
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/lexer/token/char.rb b/tool/lrama/lib/lrama/lexer/token/char.rb
new file mode 100644
index 0000000000..ec3560ca09
--- /dev/null
+++ b/tool/lrama/lib/lrama/lexer/token/char.rb
@@ -0,0 +1,8 @@
+module Lrama
+ class Lexer
+ class Token
+ class Char < Token
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/lexer/token/ident.rb b/tool/lrama/lib/lrama/lexer/token/ident.rb
new file mode 100644
index 0000000000..e576eaeccd
--- /dev/null
+++ b/tool/lrama/lib/lrama/lexer/token/ident.rb
@@ -0,0 +1,8 @@
+module Lrama
+ class Lexer
+ class Token
+ class Ident < Token
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/lexer/token/instantiate_rule.rb b/tool/lrama/lib/lrama/lexer/token/instantiate_rule.rb
new file mode 100644
index 0000000000..1c4d1095c8
--- /dev/null
+++ b/tool/lrama/lib/lrama/lexer/token/instantiate_rule.rb
@@ -0,0 +1,23 @@
+module Lrama
+ class Lexer
+ class Token
+ class InstantiateRule < Token
+ attr_reader :args, :lhs_tag
+
+ def initialize(s_value:, alias_name: nil, location: nil, args: [], lhs_tag: nil)
+ super s_value: s_value, alias_name: alias_name, location: location
+ @args = args
+ @lhs_tag = lhs_tag
+ end
+
+ def rule_name
+ s_value
+ end
+
+ def args_count
+ args.count
+ end
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/lexer/token/tag.rb b/tool/lrama/lib/lrama/lexer/token/tag.rb
new file mode 100644
index 0000000000..e54d773915
--- /dev/null
+++ b/tool/lrama/lib/lrama/lexer/token/tag.rb
@@ -0,0 +1,12 @@
+module Lrama
+ class Lexer
+ class Token
+ class Tag < Token
+ # Omit "<>"
+ def member
+ s_value[1..-2] or raise "Unexpected Tag format (#{s_value})"
+ end
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/lexer/token/user_code.rb b/tool/lrama/lib/lrama/lexer/token/user_code.rb
new file mode 100644
index 0000000000..4d487bf01c
--- /dev/null
+++ b/tool/lrama/lib/lrama/lexer/token/user_code.rb
@@ -0,0 +1,77 @@
+require "strscan"
+
+module Lrama
+ class Lexer
+ class Token
+ class UserCode < Token
+ attr_accessor :tag
+
+ def references
+ @references ||= _references
+ end
+
+ private
+
+ def _references
+ scanner = StringScanner.new(s_value)
+ references = []
+
+ while !scanner.eos? do
+ case
+ when reference = scan_reference(scanner)
+ references << reference
+ when scanner.scan(/\/\*/)
+ scanner.scan_until(/\*\//)
+ else
+ scanner.getch
+ end
+ end
+
+ references
+ end
+
+ def scan_reference(scanner)
+ start = scanner.pos
+ case
+ # $ references
+ # It need to wrap an identifier with brackets to use ".-" for identifiers
+ when scanner.scan(/\$(<[a-zA-Z0-9_]+>)?\$/) # $$, $<long>$
+ tag = scanner[1] ? Lrama::Lexer::Token::Tag.new(s_value: scanner[1]) : nil
+ return Lrama::Grammar::Reference.new(type: :dollar, name: "$", ex_tag: tag, first_column: start, last_column: scanner.pos)
+ when scanner.scan(/\$(<[a-zA-Z0-9_]+>)?(\d+)/) # $1, $2, $<long>1
+ tag = scanner[1] ? Lrama::Lexer::Token::Tag.new(s_value: scanner[1]) : nil
+ return Lrama::Grammar::Reference.new(type: :dollar, number: Integer(scanner[2]), index: Integer(scanner[2]), ex_tag: tag, first_column: start, last_column: scanner.pos)
+ when scanner.scan(/\$(<[a-zA-Z0-9_]+>)?([a-zA-Z_][a-zA-Z0-9_]*)/) # $foo, $expr, $<long>program (named reference without brackets)
+ tag = scanner[1] ? Lrama::Lexer::Token::Tag.new(s_value: scanner[1]) : nil
+ return Lrama::Grammar::Reference.new(type: :dollar, name: scanner[2], ex_tag: tag, first_column: start, last_column: scanner.pos)
+ when scanner.scan(/\$(<[a-zA-Z0-9_]+>)?\[([a-zA-Z_.][-a-zA-Z0-9_.]*)\]/) # $[expr.right], $[expr-right], $<long>[expr.right] (named reference with brackets)
+ tag = scanner[1] ? Lrama::Lexer::Token::Tag.new(s_value: scanner[1]) : nil
+ return Lrama::Grammar::Reference.new(type: :dollar, name: scanner[2], ex_tag: tag, first_column: start, last_column: scanner.pos)
+
+ # @ references
+ # It need to wrap an identifier with brackets to use ".-" for identifiers
+ when scanner.scan(/@\$/) # @$
+ return Lrama::Grammar::Reference.new(type: :at, name: "$", first_column: start, last_column: scanner.pos)
+ when scanner.scan(/@(\d+)/) # @1
+ return Lrama::Grammar::Reference.new(type: :at, number: Integer(scanner[1]), index: Integer(scanner[1]), first_column: start, last_column: scanner.pos)
+ when scanner.scan(/@([a-zA-Z][a-zA-Z0-9_]*)/) # @foo, @expr (named reference without brackets)
+ return Lrama::Grammar::Reference.new(type: :at, name: scanner[1], first_column: start, last_column: scanner.pos)
+ when scanner.scan(/@\[([a-zA-Z_.][-a-zA-Z0-9_.]*)\]/) # @[expr.right], @[expr-right] (named reference with brackets)
+ return Lrama::Grammar::Reference.new(type: :at, name: scanner[1], first_column: start, last_column: scanner.pos)
+
+ # $: references
+ when scanner.scan(/\$:\$/) # $:$
+ return Lrama::Grammar::Reference.new(type: :index, name: "$", first_column: start, last_column: scanner.pos)
+ when scanner.scan(/\$:(\d+)/) # $:1
+ return Lrama::Grammar::Reference.new(type: :index, number: Integer(scanner[1]), first_column: start, last_column: scanner.pos)
+ when scanner.scan(/\$:([a-zA-Z_][a-zA-Z0-9_]*)/) # $:foo, $:expr (named reference without brackets)
+ return Lrama::Grammar::Reference.new(type: :index, name: scanner[1], first_column: start, last_column: scanner.pos)
+ when scanner.scan(/\$:\[([a-zA-Z_.][-a-zA-Z0-9_.]*)\]/) # $:[expr.right], $:[expr-right] (named reference with brackets)
+ return Lrama::Grammar::Reference.new(type: :index, name: scanner[1], first_column: start, last_column: scanner.pos)
+
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/option_parser.rb b/tool/lrama/lib/lrama/option_parser.rb
new file mode 100644
index 0000000000..1e4d448fd1
--- /dev/null
+++ b/tool/lrama/lib/lrama/option_parser.rb
@@ -0,0 +1,142 @@
+require 'optparse'
+
+module Lrama
+ # Handle option parsing for the command line interface.
+ class OptionParser
+ def initialize
+ @options = Options.new
+ @trace = []
+ @report = []
+ end
+
+ def parse(argv)
+ parse_by_option_parser(argv)
+
+ @options.trace_opts = validate_trace(@trace)
+ @options.report_opts = validate_report(@report)
+ @options.grammar_file = argv.shift
+
+ if !@options.grammar_file
+ abort "File should be specified\n"
+ end
+
+ if @options.grammar_file == '-'
+ @options.grammar_file = argv.shift or abort "File name for STDIN should be specified\n"
+ else
+ @options.y = File.open(@options.grammar_file, 'r')
+ end
+
+ if !@report.empty? && @options.report_file.nil? && @options.grammar_file
+ @options.report_file = File.dirname(@options.grammar_file) + "/" + File.basename(@options.grammar_file, ".*") + ".output"
+ end
+
+ if !@options.header_file && @options.header
+ case
+ when @options.outfile
+ @options.header_file = File.dirname(@options.outfile) + "/" + File.basename(@options.outfile, ".*") + ".h"
+ when @options.grammar_file
+ @options.header_file = File.dirname(@options.grammar_file) + "/" + File.basename(@options.grammar_file, ".*") + ".h"
+ end
+ end
+
+ @options
+ end
+
+ private
+
+ def parse_by_option_parser(argv)
+ ::OptionParser.new do |o|
+ o.banner = <<~BANNER
+ Lrama is LALR (1) parser generator written by Ruby.
+
+ Usage: lrama [options] FILE
+ BANNER
+ o.separator ''
+ o.separator 'STDIN mode:'
+ o.separator 'lrama [options] - FILE read grammar from STDIN'
+ o.separator ''
+ o.separator 'Tuning the Parser:'
+ o.on('-S', '--skeleton=FILE', 'specify the skeleton to use') {|v| @options.skeleton = v }
+ o.on('-t', 'reserved, do nothing') { }
+ o.on('--debug', 'display debugging outputs of internal parser') {|v| @options.debug = true }
+ o.separator ''
+ o.separator 'Output:'
+ o.on('-H', '--header=[FILE]', 'also produce a header file named FILE') {|v| @options.header = true; @options.header_file = v }
+ o.on('-d', 'also produce a header file') { @options.header = true }
+ o.on('-r', '--report=THINGS', Array, 'also produce details on the automaton') {|v| @report = v }
+ o.on_tail ''
+ o.on_tail 'Valid Reports:'
+ o.on_tail " #{VALID_REPORTS.join(' ')}"
+
+ o.on('--report-file=FILE', 'also produce details on the automaton output to a file named FILE') {|v| @options.report_file = v }
+ o.on('-o', '--output=FILE', 'leave output to FILE') {|v| @options.outfile = v }
+
+ o.on('--trace=THINGS', Array, 'also output trace logs at runtime') {|v| @trace = v }
+ o.on_tail ''
+ o.on_tail 'Valid Traces:'
+ o.on_tail " #{VALID_TRACES.join(' ')}"
+
+ o.on('-v', 'reserved, do nothing') { }
+ o.separator ''
+ o.separator 'Error Recovery:'
+ o.on('-e', 'enable error recovery') {|v| @options.error_recovery = true }
+ o.separator ''
+ o.separator 'Other options:'
+ o.on('-V', '--version', "output version information and exit") {|v| puts "lrama #{Lrama::VERSION}"; exit 0 }
+ o.on('-h', '--help', "display this help and exit") {|v| puts o; exit 0 }
+ o.on_tail
+ o.parse!(argv)
+ end
+ end
+
+ BISON_REPORTS = %w[states itemsets lookaheads solved counterexamples cex all none]
+ OTHER_REPORTS = %w[verbose]
+ NOT_SUPPORTED_REPORTS = %w[cex none]
+ VALID_REPORTS = BISON_REPORTS + OTHER_REPORTS - NOT_SUPPORTED_REPORTS
+
+ def validate_report(report)
+ list = VALID_REPORTS
+ h = { grammar: true }
+
+ report.each do |r|
+ if list.include?(r)
+ h[r.to_sym] = true
+ else
+ raise "Invalid report option \"#{r}\"."
+ end
+ end
+
+ if h[:all]
+ (BISON_REPORTS - NOT_SUPPORTED_REPORTS).each do |r|
+ h[r.to_sym] = true
+ end
+
+ h.delete(:all)
+ end
+
+ return h
+ end
+
+ VALID_TRACES = %w[
+ none locations scan parse automaton bitsets
+ closure grammar rules actions resource
+ sets muscles tools m4-early m4 skeleton time
+ ielr cex all
+ ]
+
+ def validate_trace(trace)
+ list = VALID_TRACES
+ h = {}
+
+ trace.each do |t|
+ if list.include?(t)
+ h[t.to_sym] = true
+ else
+ raise "Invalid trace option \"#{t}\"."
+ end
+ end
+
+ return h
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/options.rb b/tool/lrama/lib/lrama/options.rb
new file mode 100644
index 0000000000..739ca16f55
--- /dev/null
+++ b/tool/lrama/lib/lrama/options.rb
@@ -0,0 +1,24 @@
+module Lrama
+ # Command line options.
+ class Options
+ attr_accessor :skeleton, :header, :header_file,
+ :report_file, :outfile,
+ :error_recovery, :grammar_file,
+ :trace_opts, :report_opts, :y,
+ :debug
+
+ def initialize
+ @skeleton = "bison/yacc.c"
+ @header = false
+ @header_file = nil
+ @report_file = nil
+ @outfile = "y.tab.c"
+ @error_recovery = false
+ @grammar_file = nil
+ @trace_opts = nil
+ @report_opts = nil
+ @y = STDIN
+ @debug = false
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/output.rb b/tool/lrama/lib/lrama/output.rb
index 696aa79feb..642c8b4708 100644
--- a/tool/lrama/lib/lrama/output.rb
+++ b/tool/lrama/lib/lrama/output.rb
@@ -1,20 +1,23 @@
require "erb"
require "forwardable"
-require "lrama/report"
+require "lrama/report/duration"
module Lrama
class Output
extend Forwardable
include Report::Duration
- attr_reader :grammar_file_path, :context, :grammar
+ attr_reader :grammar_file_path, :context, :grammar, :error_recovery, :include_header
def_delegators "@context", :yyfinal, :yylast, :yyntokens, :yynnts, :yynrules, :yynstates,
:yymaxutok, :yypact_ninf, :yytable_ninf
def_delegators "@grammar", :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol
- def initialize(out:, output_file_path:, template_name:, grammar_file_path:, header_out: nil, header_file_path: nil, context:, grammar:)
+ def initialize(
+ out:, output_file_path:, template_name:, grammar_file_path:,
+ context:, grammar:, header_out: nil, header_file_path: nil, error_recovery: false
+ )
@out = out
@output_file_path = output_file_path
@template_name = template_name
@@ -23,6 +26,8 @@ module Lrama
@header_file_path = header_file_path
@context = context
@grammar = grammar
+ @error_recovery = error_recovery
+ @include_header = header_file_path ? header_file_path.sub("./", "") : nil
end
if ERB.instance_method(:initialize).parameters.last.first == :key
@@ -35,11 +40,8 @@ module Lrama
end
end
- def eval_template(file, path)
- erb = self.class.erb(File.read(file))
- erb.filename = file
- tmp = erb.result_with_hash(context: @context, output: self)
- replace_special_variables(tmp, path)
+ def render_partial(file)
+ render_template(partial_file(file))
end
def render
@@ -98,6 +100,10 @@ module Lrama
int_array_to_string(@context.yytranslate)
end
+ def yytranslate_inverted
+ int_array_to_string(@context.yytranslate_inverted)
+ end
+
def yyrline
int_array_to_string(@context.yyrline)
end
@@ -134,7 +140,26 @@ module Lrama
str << <<-STR
case #{sym.enum_name}: /* #{sym.comment} */
#line #{sym.printer.lineno} "#{@grammar_file_path}"
- #{sym.printer.translated_code(sym.tag)}
+ {#{sym.printer.translated_code(sym.tag)}}
+#line [@oline@] [@ofile@]
+ break;
+
+ STR
+ end
+
+ str
+ end
+
+ def symbol_actions_for_destructor
+ str = ""
+
+ @grammar.symbols.each do |sym|
+ next unless sym.destructor
+
+ str << <<-STR
+ case #{sym.enum_name}: /* #{sym.comment} */
+#line #{sym.destructor.lineno} "#{@grammar_file_path}"
+ {#{sym.destructor.translated_code(sym.tag)}}
#line [@oline@] [@ofile@]
break;
@@ -151,25 +176,98 @@ module Lrama
<<-STR
#{comment}
#line #{@grammar.initial_action.line} "#{@grammar_file_path}"
- #{@grammar.initial_action.translated_code}
+ {#{@grammar.initial_action.translated_code}}
+ STR
+ end
+
+ def after_shift_function(comment = "")
+ return "" unless @grammar.after_shift
+
+ <<-STR
+ #{comment}
+#line #{@grammar.after_shift.line} "#{@grammar_file_path}"
+ {#{@grammar.after_shift.s_value}(#{parse_param_name});}
+#line [@oline@] [@ofile@]
+ STR
+ end
+
+ def before_reduce_function(comment = "")
+ return "" unless @grammar.before_reduce
+
+ <<-STR
+ #{comment}
+#line #{@grammar.before_reduce.line} "#{@grammar_file_path}"
+ {#{@grammar.before_reduce.s_value}(yylen#{user_args});}
+#line [@oline@] [@ofile@]
+ STR
+ end
+
+ def after_reduce_function(comment = "")
+ return "" unless @grammar.after_reduce
+
+ <<-STR
+ #{comment}
+#line #{@grammar.after_reduce.line} "#{@grammar_file_path}"
+ {#{@grammar.after_reduce.s_value}(yylen#{user_args});}
+#line [@oline@] [@ofile@]
STR
end
+ def after_shift_error_token_function(comment = "")
+ return "" unless @grammar.after_shift_error_token
+
+ <<-STR
+ #{comment}
+#line #{@grammar.after_shift_error_token.line} "#{@grammar_file_path}"
+ {#{@grammar.after_shift_error_token.s_value}(#{parse_param_name});}
+#line [@oline@] [@ofile@]
+ STR
+ end
+
+ def after_pop_stack_function(len, comment = "")
+ return "" unless @grammar.after_pop_stack
+
+ <<-STR
+ #{comment}
+#line #{@grammar.after_pop_stack.line} "#{@grammar_file_path}"
+ {#{@grammar.after_pop_stack.s_value}(#{len}#{user_args});}
+#line [@oline@] [@ofile@]
+ STR
+ end
+
+ def symbol_actions_for_error_token
+ str = ""
+
+ @grammar.symbols.each do |sym|
+ next unless sym.error_token
+
+ str << <<-STR
+ case #{sym.enum_name}: /* #{sym.comment} */
+#line #{sym.error_token.lineno} "#{@grammar_file_path}"
+ {#{sym.error_token.translated_code(sym.tag)}}
+#line [@oline@] [@ofile@]
+ break;
+
+ STR
+ end
+
+ str
+ end
+
# b4_user_actions
def user_actions
str = ""
@context.states.rules.each do |rule|
- next unless rule.code
+ next unless rule.token_code
- rule = rule
- code = rule.code
+ code = rule.token_code
spaces = " " * (code.column - 1)
str << <<-STR
case #{rule.id + 1}: /* #{rule.as_comment} */
#line #{code.line} "#{@grammar_file_path}"
-#{spaces}#{rule.translated_code}
+#{spaces}{#{rule.translated_code}}
#line [@oline@] [@ofile@]
break;
@@ -184,14 +282,14 @@ module Lrama
str
end
- def omit_braces_and_blanks(param)
- param[1..-2].strip
+ def omit_blanks(param)
+ param.strip
end
# b4_parse_param
def parse_param
if @grammar.parse_param
- omit_braces_and_blanks(@grammar.parse_param)
+ omit_blanks(@grammar.parse_param)
else
""
end
@@ -199,7 +297,7 @@ module Lrama
def lex_param
if @grammar.lex_param
- omit_braces_and_blanks(@grammar.lex_param)
+ omit_blanks(@grammar.lex_param)
else
""
end
@@ -224,7 +322,7 @@ module Lrama
end
def extract_param_name(param)
- /\A(.)+([a-zA-Z0-9_]+)\z/.match(param)[2]
+ param[/\b([a-zA-Z0-9_]+)(?=\s*\z)/]
end
def parse_param_name
@@ -324,8 +422,28 @@ module Lrama
end
end
+ # b4_percent_code_get
+ def percent_code(name)
+ @grammar.percent_codes.select do |percent_code|
+ percent_code.name == name
+ end.map do |percent_code|
+ percent_code.code
+ end.join
+ end
+
private
+ def eval_template(file, path)
+ tmp = render_template(file)
+ replace_special_variables(tmp, path)
+ end
+
+ def render_template(file)
+ erb = self.class.erb(File.read(file))
+ erb.filename = file
+ erb.result_with_hash(context: @context, output: self)
+ end
+
def template_file
File.join(template_dir, @template_name)
end
@@ -334,6 +452,10 @@ module Lrama
File.join(template_dir, "bison/yacc.h")
end
+ def partial_file(file)
+ File.join(template_dir, file)
+ end
+
def template_dir
File.expand_path("../../../template", __FILE__)
end
diff --git a/tool/lrama/lib/lrama/parser.rb b/tool/lrama/lib/lrama/parser.rb
index a2d3b9e0d4..ee4f034621 100644
--- a/tool/lrama/lib/lrama/parser.rb
+++ b/tool/lrama/lib/lrama/parser.rb
@@ -1,270 +1,2234 @@
-require "lrama/report"
-require "lrama/parser/token_scanner"
+#
+# DO NOT MODIFY!!!!
+# This file is automatically generated by Racc 1.7.3
+# from Racc grammar file "parser.y".
+#
+
+###### racc/parser.rb begin
+unless $".find {|p| p.end_with?('/racc/parser.rb')}
+$".push "#{__dir__}/racc/parser.rb"
+self.class.module_eval(<<'...end racc/parser.rb/module_eval...', 'racc/parser.rb', 1)
+#--
+# Copyright (c) 1999-2006 Minero Aoki
+#
+# This program is free software.
+# You can distribute/modify this program under the same terms of ruby.
+#
+# As a special exception, when this code is copied by Racc
+# into a Racc output file, you may use that output file
+# without restriction.
+#++
+
+unless $".find {|p| p.end_with?('/racc/info.rb')}
+$".push "#{__dir__}/racc/info.rb"
+
+module Racc
+ VERSION = '1.7.3'
+ Version = VERSION
+ Copyright = 'Copyright (c) 1999-2006 Minero Aoki'
+end
+
+end
+
+
+unless defined?(NotImplementedError)
+ NotImplementedError = NotImplementError # :nodoc:
+end
+
+module Racc
+ class ParseError < StandardError; end
+end
+unless defined?(::ParseError)
+ ParseError = Racc::ParseError # :nodoc:
+end
+
+# Racc is a LALR(1) parser generator.
+# It is written in Ruby itself, and generates Ruby programs.
+#
+# == Command-line Reference
+#
+# racc [-o<var>filename</var>] [--output-file=<var>filename</var>]
+# [-e<var>rubypath</var>] [--executable=<var>rubypath</var>]
+# [-v] [--verbose]
+# [-O<var>filename</var>] [--log-file=<var>filename</var>]
+# [-g] [--debug]
+# [-E] [--embedded]
+# [-l] [--no-line-convert]
+# [-c] [--line-convert-all]
+# [-a] [--no-omit-actions]
+# [-C] [--check-only]
+# [-S] [--output-status]
+# [--version] [--copyright] [--help] <var>grammarfile</var>
+#
+# [+grammarfile+]
+# Racc grammar file. Any extension is permitted.
+# [-o+outfile+, --output-file=+outfile+]
+# A filename for output. default is <+filename+>.tab.rb
+# [-O+filename+, --log-file=+filename+]
+# Place logging output in file +filename+.
+# Default log file name is <+filename+>.output.
+# [-e+rubypath+, --executable=+rubypath+]
+# output executable file(mode 755). where +path+ is the Ruby interpreter.
+# [-v, --verbose]
+# verbose mode. create +filename+.output file, like yacc's y.output file.
+# [-g, --debug]
+# add debug code to parser class. To display debugging information,
+# use this '-g' option and set @yydebug true in parser class.
+# [-E, --embedded]
+# Output parser which doesn't need runtime files (racc/parser.rb).
+# [-F, --frozen]
+# Output parser which declares frozen_string_literals: true
+# [-C, --check-only]
+# Check syntax of racc grammar file and quit.
+# [-S, --output-status]
+# Print messages time to time while compiling.
+# [-l, --no-line-convert]
+# turns off line number converting.
+# [-c, --line-convert-all]
+# Convert line number of actions, inner, header and footer.
+# [-a, --no-omit-actions]
+# Call all actions, even if an action is empty.
+# [--version]
+# print Racc version and quit.
+# [--copyright]
+# Print copyright and quit.
+# [--help]
+# Print usage and quit.
+#
+# == Generating Parser Using Racc
+#
+# To compile Racc grammar file, simply type:
+#
+# $ racc parse.y
+#
+# This creates Ruby script file "parse.tab.y". The -o option can change the output filename.
+#
+# == Writing A Racc Grammar File
+#
+# If you want your own parser, you have to write a grammar file.
+# A grammar file contains the name of your parser class, grammar for the parser,
+# user code, and anything else.
+# When writing a grammar file, yacc's knowledge is helpful.
+# If you have not used yacc before, Racc is not too difficult.
+#
+# Here's an example Racc grammar file.
+#
+# class Calcparser
+# rule
+# target: exp { print val[0] }
+#
+# exp: exp '+' exp
+# | exp '*' exp
+# | '(' exp ')'
+# | NUMBER
+# end
+#
+# Racc grammar files resemble yacc files.
+# But (of course), this is Ruby code.
+# yacc's $$ is the 'result', $0, $1... is
+# an array called 'val', and $-1, $-2... is an array called '_values'.
+#
+# See the {Grammar File Reference}[rdoc-ref:lib/racc/rdoc/grammar.en.rdoc] for
+# more information on grammar files.
+#
+# == Parser
+#
+# Then you must prepare the parse entry method. There are two types of
+# parse methods in Racc, Racc::Parser#do_parse and Racc::Parser#yyparse
+#
+# Racc::Parser#do_parse is simple.
+#
+# It's yyparse() of yacc, and Racc::Parser#next_token is yylex().
+# This method must returns an array like [TOKENSYMBOL, ITS_VALUE].
+# EOF is [false, false].
+# (TOKENSYMBOL is a Ruby symbol (taken from String#intern) by default.
+# If you want to change this, see the grammar reference.
+#
+# Racc::Parser#yyparse is little complicated, but useful.
+# It does not use Racc::Parser#next_token, instead it gets tokens from any iterator.
+#
+# For example, <code>yyparse(obj, :scan)</code> causes
+# calling +obj#scan+, and you can return tokens by yielding them from +obj#scan+.
+#
+# == Debugging
+#
+# When debugging, "-v" or/and the "-g" option is helpful.
+#
+# "-v" creates verbose log file (.output).
+# "-g" creates a "Verbose Parser".
+# Verbose Parser prints the internal status when parsing.
+# But it's _not_ automatic.
+# You must use -g option and set +@yydebug+ to +true+ in order to get output.
+# -g option only creates the verbose parser.
+#
+# === Racc reported syntax error.
+#
+# Isn't there too many "end"?
+# grammar of racc file is changed in v0.10.
+#
+# Racc does not use '%' mark, while yacc uses huge number of '%' marks..
+#
+# === Racc reported "XXXX conflicts".
+#
+# Try "racc -v xxxx.y".
+# It causes producing racc's internal log file, xxxx.output.
+#
+# === Generated parsers does not work correctly
+#
+# Try "racc -g xxxx.y".
+# This command let racc generate "debugging parser".
+# Then set @yydebug=true in your parser.
+# It produces a working log of your parser.
+#
+# == Re-distributing Racc runtime
+#
+# A parser, which is created by Racc, requires the Racc runtime module;
+# racc/parser.rb.
+#
+# Ruby 1.8.x comes with Racc runtime module,
+# you need NOT distribute Racc runtime files.
+#
+# If you want to include the Racc runtime module with your parser.
+# This can be done by using '-E' option:
+#
+# $ racc -E -omyparser.rb myparser.y
+#
+# This command creates myparser.rb which `includes' Racc runtime.
+# Only you must do is to distribute your parser file (myparser.rb).
+#
+# Note: parser.rb is ruby license, but your parser is not.
+# Your own parser is completely yours.
+module Racc
+
+ unless defined?(Racc_No_Extensions)
+ Racc_No_Extensions = false # :nodoc:
+ end
-module Lrama
- # Parser for parse.y, generates a grammar
class Parser
- include Lrama::Report::Duration
- T = Lrama::Lexer::Token
+ Racc_Runtime_Version = ::Racc::VERSION
+ Racc_Runtime_Core_Version_R = ::Racc::VERSION
+
+ begin
+ if Object.const_defined?(:RUBY_ENGINE) and RUBY_ENGINE == 'jruby'
+ require 'jruby'
+ require 'racc/cparse-jruby.jar'
+ com.headius.racc.Cparse.new.load(JRuby.runtime, false)
+ else
+ require 'racc/cparse'
+ end
+
+ unless new.respond_to?(:_racc_do_parse_c, true)
+ raise LoadError, 'old cparse.so'
+ end
+ if Racc_No_Extensions
+ raise LoadError, 'selecting ruby version of racc runtime core'
+ end
+
+ Racc_Main_Parsing_Routine = :_racc_do_parse_c # :nodoc:
+ Racc_YY_Parse_Method = :_racc_yyparse_c # :nodoc:
+ Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_C # :nodoc:
+ Racc_Runtime_Type = 'c' # :nodoc:
+ rescue LoadError
+ Racc_Main_Parsing_Routine = :_racc_do_parse_rb
+ Racc_YY_Parse_Method = :_racc_yyparse_rb
+ Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_R
+ Racc_Runtime_Type = 'ruby'
+ end
- def initialize(text)
- @text = text
+ def Parser.racc_runtime_type # :nodoc:
+ Racc_Runtime_Type
end
- def parse
- report_duration(:parse) do
- lexer = Lexer.new(@text)
- grammar = Grammar.new
- process_prologue(grammar, lexer)
- parse_bison_declarations(TokenScanner.new(lexer.bison_declarations_tokens), grammar)
- parse_grammar_rules(TokenScanner.new(lexer.grammar_rules_tokens), grammar)
- process_epilogue(grammar, lexer)
- grammar.prepare
- grammar.compute_nullable
- grammar.validate!
-
- grammar
+ def _racc_setup
+ @yydebug = false unless self.class::Racc_debug_parser
+ @yydebug = false unless defined?(@yydebug)
+ if @yydebug
+ @racc_debug_out = $stderr unless defined?(@racc_debug_out)
+ @racc_debug_out ||= $stderr
end
+ arg = self.class::Racc_arg
+ arg[13] = true if arg.size < 14
+ arg
end
- private
+ def _racc_init_sysvars
+ @racc_state = [0]
+ @racc_tstack = []
+ @racc_vstack = []
- def process_prologue(grammar, lexer)
- grammar.prologue_first_lineno = lexer.prologue.first[1] if lexer.prologue.first
- grammar.prologue = lexer.prologue.map(&:first).join
+ @racc_t = nil
+ @racc_val = nil
+
+ @racc_read_next = true
+
+ @racc_user_yyerror = false
+ @racc_error_status = 0
end
- def process_epilogue(grammar, lexer)
- grammar.epilogue_first_lineno = lexer.epilogue.first[1] if lexer.epilogue.first
- grammar.epilogue = lexer.epilogue.map(&:first).join
+ # The entry point of the parser. This method is used with #next_token.
+ # If Racc wants to get token (and its value), calls next_token.
+ #
+ # Example:
+ # def parse
+ # @q = [[1,1],
+ # [2,2],
+ # [3,3],
+ # [false, '$']]
+ # do_parse
+ # end
+ #
+ # def next_token
+ # @q.shift
+ # end
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
+ def do_parse
+ #{Racc_Main_Parsing_Routine}(_racc_setup(), false)
end
+ RUBY
+
+ # The method to fetch next token.
+ # If you use #do_parse method, you must implement #next_token.
+ #
+ # The format of return value is [TOKEN_SYMBOL, VALUE].
+ # +token-symbol+ is represented by Ruby's symbol by default, e.g. :IDENT
+ # for 'IDENT'. ";" (String) for ';'.
+ #
+ # The final symbol (End of file) must be false.
+ def next_token
+ raise NotImplementedError, "#{self.class}\#next_token is not defined"
+ end
+
+ def _racc_do_parse_rb(arg, in_debug)
+ action_table, action_check, action_default, action_pointer,
+ _, _, _, _,
+ _, _, token_table, * = arg
+
+ _racc_init_sysvars
+ tok = act = i = nil
+
+ catch(:racc_end_parse) {
+ while true
+ if i = action_pointer[@racc_state[-1]]
+ if @racc_read_next
+ if @racc_t != 0 # not EOF
+ tok, @racc_val = next_token()
+ unless tok # EOF
+ @racc_t = 0
+ else
+ @racc_t = (token_table[tok] or 1) # error token
+ end
+ racc_read_token(@racc_t, tok, @racc_val) if @yydebug
+ @racc_read_next = false
+ end
+ end
+ i += @racc_t
+ unless i >= 0 and
+ act = action_table[i] and
+ action_check[i] == @racc_state[-1]
+ act = action_default[@racc_state[-1]]
+ end
+ else
+ act = action_default[@racc_state[-1]]
+ end
+ while act = _racc_evalact(act, arg)
+ ;
+ end
+ end
+ }
+ end
+
+ # Another entry point for the parser.
+ # If you use this method, you must implement RECEIVER#METHOD_ID method.
+ #
+ # RECEIVER#METHOD_ID is a method to get next token.
+ # It must 'yield' the token, which format is [TOKEN-SYMBOL, VALUE].
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
+ def yyparse(recv, mid)
+ #{Racc_YY_Parse_Method}(recv, mid, _racc_setup(), false)
+ end
+ RUBY
+
+ def _racc_yyparse_rb(recv, mid, arg, c_debug)
+ action_table, action_check, action_default, action_pointer,
+ _, _, _, _,
+ _, _, token_table, * = arg
+
+ _racc_init_sysvars
+
+ catch(:racc_end_parse) {
+ until i = action_pointer[@racc_state[-1]]
+ while act = _racc_evalact(action_default[@racc_state[-1]], arg)
+ ;
+ end
+ end
+ recv.__send__(mid) do |tok, val|
+ unless tok
+ @racc_t = 0
+ else
+ @racc_t = (token_table[tok] or 1) # error token
+ end
+ @racc_val = val
+ @racc_read_next = false
+
+ i += @racc_t
+ unless i >= 0 and
+ act = action_table[i] and
+ action_check[i] == @racc_state[-1]
+ act = action_default[@racc_state[-1]]
+ end
+ while act = _racc_evalact(act, arg)
+ ;
+ end
+
+ while !(i = action_pointer[@racc_state[-1]]) ||
+ ! @racc_read_next ||
+ @racc_t == 0 # $
+ unless i and i += @racc_t and
+ i >= 0 and
+ act = action_table[i] and
+ action_check[i] == @racc_state[-1]
+ act = action_default[@racc_state[-1]]
+ end
+ while act = _racc_evalact(act, arg)
+ ;
+ end
+ end
+ end
+ }
+ end
+
+ ###
+ ### common
+ ###
+
+ def _racc_evalact(act, arg)
+ action_table, action_check, _, action_pointer,
+ _, _, _, _,
+ _, _, _, shift_n,
+ reduce_n, * = arg
+ nerr = 0 # tmp
+
+ if act > 0 and act < shift_n
+ #
+ # shift
+ #
+ if @racc_error_status > 0
+ @racc_error_status -= 1 unless @racc_t <= 1 # error token or EOF
+ end
+ @racc_vstack.push @racc_val
+ @racc_state.push act
+ @racc_read_next = true
+ if @yydebug
+ @racc_tstack.push @racc_t
+ racc_shift @racc_t, @racc_tstack, @racc_vstack
+ end
- def parse_bison_declarations(ts, grammar)
- precedence_number = 0
-
- while !ts.eots? do
- case ts.current_type
- when T::P_expect
- ts.next
- grammar.expect = ts.consume!(T::Number).s_value
- when T::P_define
- ts.next
- # Ignore
- ts.consume_multi(T::Ident)
- when T::P_printer
- lineno = ts.current_token.line
- ts.next
- code = ts.consume!(T::User_code)
- code = grammar.build_code(:printer, code)
- ident_or_tags = ts.consume_multi(T::Ident, T::Tag)
- grammar.add_printer(ident_or_tags: ident_or_tags, code: code, lineno: lineno)
- when T::P_lex_param
- ts.next
- code = ts.consume!(T::User_code)
- code = grammar.build_code(:lex_param, code)
- grammar.lex_param = code.token_code.s_value
- when T::P_parse_param
- ts.next
- code = ts.consume!(T::User_code)
- code = grammar.build_code(:parse_param, code)
- grammar.parse_param = code.token_code.s_value
- when T::P_initial_action
- ts.next
- code = ts.consume!(T::User_code)
- code = grammar.build_code(:initial_action, code)
- ts.consume(T::Semicolon)
- grammar.initial_action = code
- when T::P_union
- lineno = ts.current_token.line
- ts.next
- code = ts.consume!(T::User_code)
- code = grammar.build_code(:union, code)
- ts.consume(T::Semicolon)
- grammar.set_union(code, lineno)
- when T::P_token
- # %token tag? (ident number? string?)+
- #
- # * ident can be char, e.g. '\\', '\t', '\13'
- # * number is a token_id for term
- #
- # These are valid token declaration (from CRuby parse.y)
- #
- # %token END_OF_INPUT 0 "end-of-input"
- # %token <id> '\\' "backslash"
- # %token tSP "escaped space"
- # %token tUPLUS 132 "unary+"
- # %token tCOLON3 ":: at EXPR_BEG"
- # %token tSTRING_DBEG tSTRING_DVAR tLAMBEG tLABEL_END
- #
- #
- # See: https://www.gnu.org/software/bison/manual/html_node/Symbol-Decls.html
- ts.next
- opt_tag = ts.consume(T::Tag)
-
- while (id = ts.consume(T::Ident, T::Char)) do
- opt_number = ts.consume(T::Number)
- opt_string = ts.consume(T::String)
- # Can replace 0 (EOF)
- grammar.add_term(
- id: id,
- alias_name: opt_string && opt_string.s_value,
- token_id: opt_number && opt_number.s_value,
- tag: opt_tag,
- replace: true,
- )
+ elsif act < 0 and act > -reduce_n
+ #
+ # reduce
+ #
+ code = catch(:racc_jump) {
+ @racc_state.push _racc_do_reduce(arg, act)
+ false
+ }
+ if code
+ case code
+ when 1 # yyerror
+ @racc_user_yyerror = true # user_yyerror
+ return -reduce_n
+ when 2 # yyaccept
+ return shift_n
+ else
+ raise '[Racc Bug] unknown jump code'
end
- when T::P_type
- # %type tag? (ident|char|string)+
- #
- # See: https://www.gnu.org/software/bison/manual/html_node/Symbol-Decls.html
- ts.next
- opt_tag = ts.consume(T::Tag)
-
- while (id = ts.consume(T::Ident, T::Char, T::String)) do
- grammar.add_type(
- id: id,
- tag: opt_tag
- )
+ end
+
+ elsif act == shift_n
+ #
+ # accept
+ #
+ racc_accept if @yydebug
+ throw :racc_end_parse, @racc_vstack[0]
+
+ elsif act == -reduce_n
+ #
+ # error
+ #
+ case @racc_error_status
+ when 0
+ unless arg[21] # user_yyerror
+ nerr += 1
+ on_error @racc_t, @racc_val, @racc_vstack
end
- when T::P_nonassoc
- # %nonassoc (ident|char|string)+
- ts.next
- while (id = ts.consume(T::Ident, T::Char, T::String)) do
- sym = grammar.add_term(id: id)
- grammar.add_nonassoc(sym, precedence_number)
+ when 3
+ if @racc_t == 0 # is $
+ # We're at EOF, and another error occurred immediately after
+ # attempting auto-recovery
+ throw :racc_end_parse, nil
end
- precedence_number += 1
- when T::P_left
- # %left (ident|char|string)+
- ts.next
- while (id = ts.consume(T::Ident, T::Char, T::String)) do
- sym = grammar.add_term(id: id)
- grammar.add_left(sym, precedence_number)
+ @racc_read_next = true
+ end
+ @racc_user_yyerror = false
+ @racc_error_status = 3
+ while true
+ if i = action_pointer[@racc_state[-1]]
+ i += 1 # error token
+ if i >= 0 and
+ (act = action_table[i]) and
+ action_check[i] == @racc_state[-1]
+ break
+ end
end
- precedence_number += 1
- when T::P_right
- # %right (ident|char|string)+
- ts.next
- while (id = ts.consume(T::Ident, T::Char, T::String)) do
- sym = grammar.add_term(id: id)
- grammar.add_right(sym, precedence_number)
+ throw :racc_end_parse, nil if @racc_state.size <= 1
+ @racc_state.pop
+ @racc_vstack.pop
+ if @yydebug
+ @racc_tstack.pop
+ racc_e_pop @racc_state, @racc_tstack, @racc_vstack
end
- precedence_number += 1
- when nil
- # end of input
- raise "Reach to end of input within declarations"
- else
- raise "Unexpected token: #{ts.current_token}"
end
+ return act
+
+ else
+ raise "[Racc Bug] unknown action #{act.inspect}"
end
+
+ racc_next_state(@racc_state[-1], @racc_state) if @yydebug
+
+ nil
end
- def parse_grammar_rules(ts, grammar)
- while !ts.eots? do
- parse_grammar_rule(ts, grammar)
+ def _racc_do_reduce(arg, act)
+ _, _, _, _,
+ goto_table, goto_check, goto_default, goto_pointer,
+ nt_base, reduce_table, _, _,
+ _, use_result, * = arg
+
+ state = @racc_state
+ vstack = @racc_vstack
+ tstack = @racc_tstack
+
+ i = act * -3
+ len = reduce_table[i]
+ reduce_to = reduce_table[i+1]
+ method_id = reduce_table[i+2]
+ void_array = []
+
+ tmp_t = tstack[-len, len] if @yydebug
+ tmp_v = vstack[-len, len]
+ tstack[-len, len] = void_array if @yydebug
+ vstack[-len, len] = void_array
+ state[-len, len] = void_array
+
+ # tstack must be updated AFTER method call
+ if use_result
+ vstack.push __send__(method_id, tmp_v, vstack, tmp_v[0])
+ else
+ vstack.push __send__(method_id, tmp_v, vstack)
+ end
+ tstack.push reduce_to
+
+ racc_reduce(tmp_t, reduce_to, tstack, vstack) if @yydebug
+
+ k1 = reduce_to - nt_base
+ if i = goto_pointer[k1]
+ i += state[-1]
+ if i >= 0 and (curstate = goto_table[i]) and goto_check[i] == k1
+ return curstate
+ end
end
+ goto_default[k1]
end
- # TODO: Take care of %prec of rule.
- # If %prec exists, user code before %prec
- # is NOT an action. For example "{ code 3 }" is NOT an action.
+ # This method is called when a parse error is found.
#
- # keyword_class { code 2 } tSTRING '!' keyword_end { code 3 } %prec "="
- def parse_grammar_rule(ts, grammar)
- # LHS
- lhs = ts.consume!(T::Ident_Colon) # class:
- lhs.type = T::Ident
-
- rhs = parse_grammar_rule_rhs(ts, grammar)
-
- grammar.add_rule(lhs: lhs, rhs: rhs, lineno: rhs.first ? rhs.first.line : lhs.line)
-
- while true do
- case ts.current_type
- when T::Bar
- # |
- bar_lineno = ts.current_token.line
- ts.next
- rhs = parse_grammar_rule_rhs(ts, grammar)
- grammar.add_rule(lhs: lhs, rhs: rhs, lineno: rhs.first ? rhs.first.line : bar_lineno)
- when T::Semicolon
- # ;
- ts.next
- break
- when T::Ident_Colon
- # Next lhs can be here because ";" is optional.
- # Do not consume next token.
- break
- when nil
- # end of input can be here when ";" is omitted
- break
- else
- raise "Unexpected token: #{ts.current_token}"
- end
+ # ERROR_TOKEN_ID is an internal ID of token which caused error.
+ # You can get string representation of this ID by calling
+ # #token_to_str.
+ #
+ # ERROR_VALUE is a value of error token.
+ #
+ # value_stack is a stack of symbol values.
+ # DO NOT MODIFY this object.
+ #
+ # This method raises ParseError by default.
+ #
+ # If this method returns, parsers enter "error recovering mode".
+ def on_error(t, val, vstack)
+ raise ParseError, sprintf("parse error on value %s (%s)",
+ val.inspect, token_to_str(t) || '?')
+ end
+
+ # Enter error recovering mode.
+ # This method does not call #on_error.
+ def yyerror
+ throw :racc_jump, 1
+ end
+
+ # Exit parser.
+ # Return value is +Symbol_Value_Stack[0]+.
+ def yyaccept
+ throw :racc_jump, 2
+ end
+
+ # Leave error recovering mode.
+ def yyerrok
+ @racc_error_status = 0
+ end
+
+ # For debugging output
+ def racc_read_token(t, tok, val)
+ @racc_debug_out.print 'read '
+ @racc_debug_out.print tok.inspect, '(', racc_token2str(t), ') '
+ @racc_debug_out.puts val.inspect
+ @racc_debug_out.puts
+ end
+
+ def racc_shift(tok, tstack, vstack)
+ @racc_debug_out.puts "shift #{racc_token2str tok}"
+ racc_print_stacks tstack, vstack
+ @racc_debug_out.puts
+ end
+
+ def racc_reduce(toks, sim, tstack, vstack)
+ out = @racc_debug_out
+ out.print 'reduce '
+ if toks.empty?
+ out.print ' <none>'
+ else
+ toks.each {|t| out.print ' ', racc_token2str(t) }
end
+ out.puts " --> #{racc_token2str(sim)}"
+ racc_print_stacks tstack, vstack
+ @racc_debug_out.puts
end
- def parse_grammar_rule_rhs(ts, grammar)
- a = []
- prec_seen = false
- code_after_prec = false
-
- while true do
- # TODO: Srting can be here
- case ts.current_type
- when T::Ident
- # keyword_class
-
- raise "Ident after %prec" if prec_seen
- a << ts.current_token
- ts.next
- when T::Char
- # '!'
-
- raise "Char after %prec" if prec_seen
- a << ts.current_token
- ts.next
- when T::P_prec
- # %prec tPLUS
- #
- # See: https://www.gnu.org/software/bison/manual/html_node/Contextual-Precedence.html
-
- ts.next
- prec_seen = true
- precedence_id = ts.consume!(T::Ident, T::String, T::Char)
- precedence_sym = grammar.find_symbol_by_id!(precedence_id)
- a << precedence_sym
- when T::User_code
- # { code } in the middle of rhs
-
- if prec_seen
- raise "Multiple User_code after %prec" if code_after_prec
- code_after_prec = true
- end
+ def racc_accept
+ @racc_debug_out.puts 'accept'
+ @racc_debug_out.puts
+ end
- code = ts.current_token
- grammar.build_references(code)
- a << code
- ts.next
- when T::Bar
- # |
- break
- when T::Semicolon
- # ;
- break
- when T::Ident_Colon
- # Next lhs can be here because ";" is optional.
- break
- when nil
- # end of input can be here when ";" is omitted
- break
- else
- raise "Unexpected token: #{ts.current_token}"
- end
+ def racc_e_pop(state, tstack, vstack)
+ @racc_debug_out.puts 'error recovering mode: pop token'
+ racc_print_states state
+ racc_print_stacks tstack, vstack
+ @racc_debug_out.puts
+ end
+
+ def racc_next_state(curstate, state)
+ @racc_debug_out.puts "goto #{curstate}"
+ racc_print_states state
+ @racc_debug_out.puts
+ end
+
+ def racc_print_stacks(t, v)
+ out = @racc_debug_out
+ out.print ' ['
+ t.each_index do |i|
+ out.print ' (', racc_token2str(t[i]), ' ', v[i].inspect, ')'
end
+ out.puts ' ]'
+ end
+
+ def racc_print_states(s)
+ out = @racc_debug_out
+ out.print ' ['
+ s.each {|st| out.print ' ', st }
+ out.puts ' ]'
+ end
- return a
+ def racc_token2str(tok)
+ self.class::Racc_token_to_s_table[tok] or
+ raise "[Racc Bug] can't convert token #{tok} to string"
end
+
+ # Convert internal ID of token symbol to the string.
+ def token_to_str(t)
+ self.class::Racc_token_to_s_table[t]
+ end
+
+ end
+
+end
+
+...end racc/parser.rb/module_eval...
+end
+###### racc/parser.rb end
+module Lrama
+ class Parser < Racc::Parser
+
+module_eval(<<'...end parser.y/module_eval...', 'parser.y', 536)
+
+include Lrama::Report::Duration
+
+def initialize(text, path, debug = false)
+ @grammar_file = Lrama::Lexer::GrammarFile.new(path, text)
+ @yydebug = debug
+ @rule_counter = Lrama::Grammar::Counter.new(0)
+ @midrule_action_counter = Lrama::Grammar::Counter.new(1)
+end
+
+def parse
+ report_duration(:parse) do
+ @lexer = Lrama::Lexer.new(@grammar_file)
+ @grammar = Lrama::Grammar.new(@rule_counter)
+ @precedence_number = 0
+ reset_precs
+ do_parse
+ @grammar
end
end
+
+def next_token
+ @lexer.next_token
+end
+
+def on_error(error_token_id, error_value, value_stack)
+ if error_value.is_a?(Lrama::Lexer::Token)
+ location = error_value.location
+ value = "'#{error_value.s_value}'"
+ else
+ location = @lexer.location
+ value = error_value.inspect
+ end
+
+ error_message = "parse error on value #{value} (#{token_to_str(error_token_id) || '?'})"
+
+ raise_parse_error(error_message, location)
+end
+
+def on_action_error(error_message, error_value)
+ if error_value.is_a?(Lrama::Lexer::Token)
+ location = error_value.location
+ else
+ location = @lexer.location
+ end
+
+ raise_parse_error(error_message, location)
+end
+
+private
+
+def reset_precs
+ @prec_seen = false
+ @code_after_prec = false
+end
+
+def begin_c_declaration(end_symbol)
+ @lexer.status = :c_declaration
+ @lexer.end_symbol = end_symbol
+end
+
+def end_c_declaration
+ @lexer.status = :initial
+ @lexer.end_symbol = nil
+end
+
+def raise_parse_error(error_message, location)
+ raise ParseError, location.generate_error_message(error_message)
+end
+...end parser.y/module_eval...
+##### State transition tables begin ###
+
+racc_action_table = [
+ 98, 51, 99, 163, 88, 79, 51, 51, 179, 163,
+ 79, 79, 51, 162, 179, 156, 79, 165, 157, 51,
+ 3, 50, 180, 165, 70, 51, 8, 50, 180, 79,
+ 75, 51, 6, 50, 7, 161, 82, 47, 51, 51,
+ 50, 50, 89, 82, 82, 166, 41, 51, 100, 50,
+ 181, 166, 82, 51, 48, 50, 181, 23, 25, 26,
+ 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+ 37, 38, 47, 51, 51, 50, 50, 93, 79, 196,
+ 51, 51, 50, 50, 79, 196, 51, 51, 50, 50,
+ 79, 196, 23, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 9, 51, 54,
+ 50, 14, 15, 16, 17, 18, 19, 54, 54, 20,
+ 21, 22, 23, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 51, 51,
+ 50, 50, 79, 196, 51, 51, 50, 50, 79, 196,
+ 51, 51, 50, 50, 79, 196, 51, 51, 50, 50,
+ 79, 79, 51, 51, 50, 50, 79, 79, 51, 51,
+ 50, 50, 79, 79, 51, 51, 50, 206, 79, 79,
+ 51, 51, 206, 206, 79, 79, 51, 51, 50, 50,
+ 79, 186, 187, 188, 96, 186, 187, 188, 96, 216,
+ 220, 228, 217, 217, 217, 51, 51, 50, 50, 186,
+ 187, 188, 57, 58, 59, 60, 61, 62, 63, 64,
+ 65, 66, 67, 90, 94, 96, 101, 101, 101, 103,
+ 109, 113, 114, 117, 117, 117, 117, 120, 47, 124,
+ 125, 127, 129, 130, 131, 132, 133, 136, 140, 141,
+ 142, 143, 146, 147, 148, 150, 160, 168, 170, 171,
+ 172, 173, 174, 175, 176, 177, 146, 183, 191, 192,
+ 160, 160, 203, 210, 211, 177, 214, 215, 210, 225,
+ 210, 227, 96, 96, 210 ]
+
+racc_action_check = [
+ 49, 145, 49, 145, 39, 145, 159, 182, 159, 182,
+ 159, 182, 200, 144, 200, 139, 200, 145, 139, 33,
+ 1, 33, 159, 182, 33, 34, 3, 34, 200, 34,
+ 34, 35, 2, 35, 2, 144, 35, 9, 36, 37,
+ 36, 37, 39, 36, 37, 145, 7, 38, 49, 38,
+ 159, 182, 38, 15, 14, 15, 200, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 42, 69, 172, 69, 172, 42, 172, 172,
+ 173, 70, 173, 70, 173, 173, 174, 81, 174, 81,
+ 174, 174, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 4, 82, 16,
+ 82, 4, 4, 4, 4, 4, 4, 17, 18, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 193, 109,
+ 193, 109, 193, 193, 197, 111, 197, 111, 197, 197,
+ 198, 117, 198, 117, 198, 198, 74, 75, 74, 75,
+ 74, 75, 114, 116, 114, 116, 114, 116, 137, 166,
+ 137, 166, 137, 166, 181, 183, 181, 183, 181, 183,
+ 203, 215, 203, 215, 203, 215, 217, 119, 217, 119,
+ 217, 164, 164, 164, 164, 178, 178, 178, 178, 207,
+ 213, 222, 207, 213, 222, 134, 138, 134, 138, 208,
+ 208, 208, 19, 20, 23, 25, 26, 27, 28, 29,
+ 30, 31, 32, 40, 45, 46, 53, 55, 56, 57,
+ 68, 72, 73, 80, 85, 86, 87, 88, 89, 95,
+ 96, 102, 104, 105, 106, 107, 108, 112, 120, 121,
+ 122, 123, 124, 125, 126, 128, 141, 149, 151, 152,
+ 153, 154, 155, 156, 157, 158, 161, 163, 167, 169,
+ 175, 177, 179, 185, 189, 199, 204, 206, 216, 219,
+ 220, 221, 225, 227, 229 ]
+
+racc_action_pointer = [
+ nil, 20, 22, 26, 98, nil, nil, 39, nil, 33,
+ nil, nil, nil, nil, 48, 50, 90, 98, 99, 207,
+ 194, nil, nil, 195, nil, 196, 197, 198, 213, 214,
+ 215, 216, 217, 16, 22, 28, 35, 36, 44, -1,
+ 221, nil, 68, nil, nil, 201, 174, nil, nil, -5,
+ nil, nil, nil, 207, nil, 208, 209, 210, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, 222, 70,
+ 78, nil, 225, 224, 153, 154, nil, nil, nil, nil,
+ 225, 84, 105, nil, nil, 226, 227, 228, 197, 234,
+ nil, nil, nil, nil, nil, 197, 235, nil, nil, nil,
+ nil, nil, 239, nil, 240, 241, 242, 243, 244, 136,
+ nil, 142, 240, nil, 159, nil, 160, 148, nil, 184,
+ 243, 207, 239, 249, 206, 201, 252, nil, 253, nil,
+ nil, nil, nil, nil, 202, nil, nil, 165, 203, -26,
+ nil, 210, nil, nil, -10, -2, nil, nil, nil, 237,
+ nil, 238, 239, 240, 241, 242, 221, 259, 220, 3,
+ nil, 220, nil, 227, 143, nil, 166, 248, nil, 249,
+ nil, nil, 71, 77, 83, 224, nil, 225, 147, 232,
+ nil, 171, 4, 172, nil, 265, nil, nil, nil, 272,
+ nil, nil, nil, 135, nil, nil, nil, 141, 147, 230,
+ 9, nil, nil, 177, 274, nil, 237, 158, 161, nil,
+ nil, nil, nil, 159, nil, 178, 270, 183, nil, 259,
+ 272, 261, 160, nil, nil, 231, nil, 232, nil, 276,
+ nil, nil ]
+
+racc_action_default = [
+ -2, -138, -8, -138, -138, -3, -4, -138, 232, -138,
+ -9, -10, -11, -12, -138, -138, -138, -138, -138, -138,
+ -138, -24, -25, -138, -29, -138, -138, -138, -138, -138,
+ -138, -138, -138, -138, -138, -138, -138, -138, -138, -138,
+ -138, -7, -123, -96, -98, -138, -120, -122, -13, -127,
+ -94, -95, -126, -15, -85, -16, -17, -138, -21, -26,
+ -30, -33, -36, -39, -40, -41, -42, -43, -44, -50,
+ -138, -53, -71, -45, -75, -138, -78, -80, -81, -135,
+ -46, -88, -138, -91, -93, -47, -48, -49, -138, -138,
+ -5, -1, -97, -124, -99, -138, -138, -14, -128, -129,
+ -130, -82, -138, -18, -138, -138, -138, -138, -138, -138,
+ -54, -51, -73, -72, -138, -79, -76, -138, -92, -89,
+ -138, -138, -138, -138, -104, -138, -138, -86, -138, -22,
+ -27, -31, -34, -37, -52, -55, -74, -77, -90, -138,
+ -58, -62, -6, -125, -100, -101, -105, -121, -83, -138,
+ -19, -138, -138, -138, -138, -138, -138, -138, -57, -60,
+ -63, -104, -103, -94, -120, -109, -138, -138, -87, -138,
+ -23, -28, -138, -138, -138, -62, -59, -62, -120, -94,
+ -67, -138, -102, -138, -106, -136, -113, -114, -115, -138,
+ -112, -84, -20, -32, -131, -133, -134, -35, -38, -56,
+ -61, -64, -65, -138, -138, -70, -94, -138, -116, -107,
+ -137, -110, -132, -138, -68, -138, -136, -138, -118, -138,
+ -136, -138, -138, -108, -117, -120, -66, -120, -119, -136,
+ -69, -111 ]
+
+racc_goto_table = [
+ 76, 95, 69, 52, 74, 118, 110, 209, 185, 145,
+ 158, 1, 119, 212, 2, 4, 43, 212, 212, 42,
+ 91, 72, 202, 84, 84, 84, 84, 80, 85, 86,
+ 87, 5, 40, 207, 53, 55, 56, 122, 223, 111,
+ 115, 76, 226, 118, 199, 116, 182, 138, 110, 92,
+ 10, 231, 218, 213, 193, 197, 198, 72, 72, 11,
+ 12, 13, 118, 49, 97, 222, 128, 169, 104, 84,
+ 84, 110, 151, 105, 152, 106, 153, 107, 134, 154,
+ 76, 108, 115, 155, 137, 68, 73, 112, 135, 139,
+ 121, 200, 204, 221, 126, 167, 102, 72, 149, 72,
+ 144, 189, 219, 115, 123, 84, nil, 84, nil, nil,
+ nil, 164, nil, nil, nil, nil, nil, nil, nil, 184,
+ nil, nil, 72, nil, nil, 178, 84, nil, nil, nil,
+ nil, nil, 190, 201, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, 205, 164, 208,
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, 178, nil, nil, 208,
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ 229, 208, 230, 224 ]
+
+racc_goto_check = [
+ 42, 43, 33, 35, 49, 56, 34, 46, 44, 60,
+ 39, 1, 55, 64, 2, 3, 57, 64, 64, 4,
+ 5, 35, 44, 35, 35, 35, 35, 32, 32, 32,
+ 32, 6, 7, 45, 15, 15, 15, 8, 46, 33,
+ 42, 42, 46, 56, 39, 49, 60, 55, 34, 57,
+ 9, 46, 44, 45, 21, 21, 21, 35, 35, 10,
+ 11, 12, 56, 13, 14, 45, 16, 17, 18, 35,
+ 35, 34, 19, 22, 23, 24, 25, 26, 33, 27,
+ 42, 28, 42, 29, 49, 30, 31, 36, 37, 38,
+ 40, 41, 47, 48, 51, 52, 53, 35, 54, 35,
+ 59, 61, 62, 42, 63, 35, nil, 35, nil, nil,
+ nil, 42, nil, nil, nil, nil, nil, nil, nil, 43,
+ nil, nil, 35, nil, nil, 42, 35, nil, nil, nil,
+ nil, nil, 42, 43, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, 42, 42, 42,
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, 42, nil, nil, 42,
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ 43, 42, 43, 42 ]
+
+racc_goto_pointer = [
+ nil, 11, 14, 13, 10, -22, 29, 26, -53, 46,
+ 55, 56, 57, 48, 15, 18, -37, -83, 10, -57,
+ nil, -118, 14, -56, 15, -55, 16, -53, 19, -50,
+ 52, 52, -8, -31, -63, -12, 15, -24, -31, -131,
+ 1, -86, -34, -45, -156, -150, -178, -88, -121, -30,
+ nil, -7, -53, 42, -29, -70, -76, 7, nil, -24,
+ -115, -64, -109, 11, -180 ]
+
+racc_goto_default = [
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ 45, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ 24, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, 71, 77, nil, nil, nil, nil,
+ 46, 159, 195, nil, nil, nil, nil, nil, nil, nil,
+ 78, nil, nil, nil, nil, 81, 83, nil, 44, nil,
+ nil, nil, nil, nil, 194 ]
+
+racc_reduce_table = [
+ 0, 0, :racc_error,
+ 5, 55, :_reduce_none,
+ 0, 56, :_reduce_none,
+ 2, 56, :_reduce_none,
+ 0, 61, :_reduce_4,
+ 0, 62, :_reduce_5,
+ 5, 60, :_reduce_6,
+ 2, 60, :_reduce_none,
+ 0, 57, :_reduce_8,
+ 2, 57, :_reduce_none,
+ 1, 63, :_reduce_none,
+ 1, 63, :_reduce_none,
+ 1, 63, :_reduce_none,
+ 2, 63, :_reduce_13,
+ 3, 63, :_reduce_none,
+ 2, 63, :_reduce_none,
+ 2, 63, :_reduce_16,
+ 2, 63, :_reduce_17,
+ 0, 70, :_reduce_18,
+ 0, 71, :_reduce_19,
+ 7, 63, :_reduce_20,
+ 0, 72, :_reduce_21,
+ 0, 73, :_reduce_22,
+ 6, 63, :_reduce_23,
+ 1, 63, :_reduce_24,
+ 1, 63, :_reduce_none,
+ 0, 76, :_reduce_26,
+ 0, 77, :_reduce_27,
+ 6, 64, :_reduce_28,
+ 1, 64, :_reduce_none,
+ 0, 78, :_reduce_30,
+ 0, 79, :_reduce_31,
+ 7, 64, :_reduce_32,
+ 0, 80, :_reduce_33,
+ 0, 81, :_reduce_34,
+ 7, 64, :_reduce_35,
+ 0, 82, :_reduce_36,
+ 0, 83, :_reduce_37,
+ 7, 64, :_reduce_38,
+ 2, 64, :_reduce_39,
+ 2, 64, :_reduce_40,
+ 2, 64, :_reduce_41,
+ 2, 64, :_reduce_42,
+ 2, 64, :_reduce_43,
+ 2, 74, :_reduce_none,
+ 2, 74, :_reduce_45,
+ 2, 74, :_reduce_46,
+ 2, 74, :_reduce_47,
+ 2, 74, :_reduce_48,
+ 2, 74, :_reduce_49,
+ 1, 84, :_reduce_50,
+ 2, 84, :_reduce_51,
+ 3, 84, :_reduce_52,
+ 1, 87, :_reduce_53,
+ 2, 87, :_reduce_54,
+ 3, 88, :_reduce_55,
+ 7, 65, :_reduce_56,
+ 5, 66, :_reduce_57,
+ 1, 92, :_reduce_58,
+ 3, 92, :_reduce_59,
+ 1, 93, :_reduce_60,
+ 3, 93, :_reduce_61,
+ 0, 95, :_reduce_62,
+ 1, 95, :_reduce_63,
+ 3, 95, :_reduce_64,
+ 3, 95, :_reduce_65,
+ 6, 95, :_reduce_66,
+ 0, 101, :_reduce_67,
+ 0, 102, :_reduce_68,
+ 7, 95, :_reduce_69,
+ 3, 95, :_reduce_70,
+ 0, 90, :_reduce_none,
+ 1, 90, :_reduce_none,
+ 0, 91, :_reduce_none,
+ 1, 91, :_reduce_none,
+ 1, 85, :_reduce_75,
+ 2, 85, :_reduce_76,
+ 3, 85, :_reduce_77,
+ 1, 103, :_reduce_78,
+ 2, 103, :_reduce_79,
+ 1, 96, :_reduce_none,
+ 1, 96, :_reduce_none,
+ 0, 105, :_reduce_82,
+ 0, 106, :_reduce_83,
+ 6, 69, :_reduce_84,
+ 0, 107, :_reduce_85,
+ 0, 108, :_reduce_86,
+ 5, 69, :_reduce_87,
+ 1, 86, :_reduce_88,
+ 2, 86, :_reduce_89,
+ 3, 86, :_reduce_90,
+ 1, 109, :_reduce_91,
+ 2, 109, :_reduce_92,
+ 1, 110, :_reduce_none,
+ 1, 89, :_reduce_94,
+ 1, 89, :_reduce_95,
+ 1, 58, :_reduce_none,
+ 2, 58, :_reduce_none,
+ 1, 111, :_reduce_none,
+ 2, 111, :_reduce_none,
+ 4, 112, :_reduce_100,
+ 1, 113, :_reduce_101,
+ 3, 113, :_reduce_102,
+ 2, 113, :_reduce_none,
+ 0, 114, :_reduce_104,
+ 1, 114, :_reduce_105,
+ 3, 114, :_reduce_106,
+ 4, 114, :_reduce_107,
+ 6, 114, :_reduce_108,
+ 0, 115, :_reduce_109,
+ 0, 116, :_reduce_110,
+ 8, 114, :_reduce_111,
+ 3, 114, :_reduce_112,
+ 1, 98, :_reduce_113,
+ 1, 98, :_reduce_114,
+ 1, 98, :_reduce_115,
+ 1, 99, :_reduce_116,
+ 3, 99, :_reduce_117,
+ 2, 99, :_reduce_118,
+ 4, 99, :_reduce_119,
+ 0, 97, :_reduce_none,
+ 3, 97, :_reduce_121,
+ 1, 94, :_reduce_none,
+ 0, 59, :_reduce_none,
+ 0, 117, :_reduce_124,
+ 3, 59, :_reduce_125,
+ 1, 67, :_reduce_none,
+ 0, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 75, :_reduce_131,
+ 2, 75, :_reduce_132,
+ 1, 118, :_reduce_none,
+ 1, 118, :_reduce_none,
+ 1, 104, :_reduce_135,
+ 0, 100, :_reduce_none,
+ 1, 100, :_reduce_none ]
+
+racc_reduce_n = 138
+
+racc_shift_n = 232
+
+racc_token_table = {
+ false => 0,
+ :error => 1,
+ :C_DECLARATION => 2,
+ :CHARACTER => 3,
+ :IDENT_COLON => 4,
+ :IDENTIFIER => 5,
+ :INTEGER => 6,
+ :STRING => 7,
+ :TAG => 8,
+ "%%" => 9,
+ "%{" => 10,
+ "%}" => 11,
+ "%require" => 12,
+ "%expect" => 13,
+ "%define" => 14,
+ "%param" => 15,
+ "%lex-param" => 16,
+ "%parse-param" => 17,
+ "%code" => 18,
+ "{" => 19,
+ "}" => 20,
+ "%initial-action" => 21,
+ "%no-stdlib" => 22,
+ ";" => 23,
+ "%union" => 24,
+ "%destructor" => 25,
+ "%printer" => 26,
+ "%error-token" => 27,
+ "%after-shift" => 28,
+ "%before-reduce" => 29,
+ "%after-reduce" => 30,
+ "%after-shift-error-token" => 31,
+ "%after-pop-stack" => 32,
+ "%token" => 33,
+ "%type" => 34,
+ "%left" => 35,
+ "%right" => 36,
+ "%precedence" => 37,
+ "%nonassoc" => 38,
+ "%rule" => 39,
+ "(" => 40,
+ ")" => 41,
+ ":" => 42,
+ "%inline" => 43,
+ "," => 44,
+ "|" => 45,
+ "%empty" => 46,
+ "%prec" => 47,
+ "?" => 48,
+ "+" => 49,
+ "*" => 50,
+ "[" => 51,
+ "]" => 52,
+ "{...}" => 53 }
+
+racc_nt_base = 54
+
+racc_use_result_var = true
+
+Racc_arg = [
+ racc_action_table,
+ racc_action_check,
+ racc_action_default,
+ racc_action_pointer,
+ racc_goto_table,
+ racc_goto_check,
+ racc_goto_default,
+ racc_goto_pointer,
+ racc_nt_base,
+ racc_reduce_table,
+ racc_token_table,
+ racc_shift_n,
+ racc_reduce_n,
+ racc_use_result_var ]
+Ractor.make_shareable(Racc_arg) if defined?(Ractor)
+
+Racc_token_to_s_table = [
+ "$end",
+ "error",
+ "C_DECLARATION",
+ "CHARACTER",
+ "IDENT_COLON",
+ "IDENTIFIER",
+ "INTEGER",
+ "STRING",
+ "TAG",
+ "\"%%\"",
+ "\"%{\"",
+ "\"%}\"",
+ "\"%require\"",
+ "\"%expect\"",
+ "\"%define\"",
+ "\"%param\"",
+ "\"%lex-param\"",
+ "\"%parse-param\"",
+ "\"%code\"",
+ "\"{\"",
+ "\"}\"",
+ "\"%initial-action\"",
+ "\"%no-stdlib\"",
+ "\";\"",
+ "\"%union\"",
+ "\"%destructor\"",
+ "\"%printer\"",
+ "\"%error-token\"",
+ "\"%after-shift\"",
+ "\"%before-reduce\"",
+ "\"%after-reduce\"",
+ "\"%after-shift-error-token\"",
+ "\"%after-pop-stack\"",
+ "\"%token\"",
+ "\"%type\"",
+ "\"%left\"",
+ "\"%right\"",
+ "\"%precedence\"",
+ "\"%nonassoc\"",
+ "\"%rule\"",
+ "\"(\"",
+ "\")\"",
+ "\":\"",
+ "\"%inline\"",
+ "\",\"",
+ "\"|\"",
+ "\"%empty\"",
+ "\"%prec\"",
+ "\"?\"",
+ "\"+\"",
+ "\"*\"",
+ "\"[\"",
+ "\"]\"",
+ "\"{...}\"",
+ "$start",
+ "input",
+ "prologue_declarations",
+ "bison_declarations",
+ "grammar",
+ "epilogue_opt",
+ "prologue_declaration",
+ "@1",
+ "@2",
+ "bison_declaration",
+ "grammar_declaration",
+ "rule_declaration",
+ "inline_declaration",
+ "variable",
+ "value",
+ "params",
+ "@3",
+ "@4",
+ "@5",
+ "@6",
+ "symbol_declaration",
+ "generic_symlist",
+ "@7",
+ "@8",
+ "@9",
+ "@10",
+ "@11",
+ "@12",
+ "@13",
+ "@14",
+ "token_declarations",
+ "symbol_declarations",
+ "token_declarations_for_precedence",
+ "token_declaration_list",
+ "token_declaration",
+ "id",
+ "int_opt",
+ "alias",
+ "rule_args",
+ "rule_rhs_list",
+ "id_colon",
+ "rule_rhs",
+ "symbol",
+ "named_ref_opt",
+ "parameterizing_suffix",
+ "parameterizing_args",
+ "tag_opt",
+ "@15",
+ "@16",
+ "symbol_declaration_list",
+ "string_as_id",
+ "@17",
+ "@18",
+ "@19",
+ "@20",
+ "token_declaration_list_for_precedence",
+ "token_declaration_for_precedence",
+ "rules_or_grammar_declaration",
+ "rules",
+ "rhs_list",
+ "rhs",
+ "@21",
+ "@22",
+ "@23",
+ "generic_symlist_item" ]
+Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)
+
+Racc_debug_parser = true
+
+##### State transition tables end #####
+
+# reduce 0 omitted
+
+# reduce 1 omitted
+
+# reduce 2 omitted
+
+# reduce 3 omitted
+
+module_eval(<<'.,.,', 'parser.y', 14)
+ def _reduce_4(val, _values, result)
+ begin_c_declaration("%}")
+ @grammar.prologue_first_lineno = @lexer.line
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 19)
+ def _reduce_5(val, _values, result)
+ end_c_declaration
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 23)
+ def _reduce_6(val, _values, result)
+ @grammar.prologue = val[2].s_value
+
+ result
+ end
+.,.,
+
+# reduce 7 omitted
+
+module_eval(<<'.,.,', 'parser.y', 27)
+ def _reduce_8(val, _values, result)
+ result = ""
+ result
+ end
+.,.,
+
+# reduce 9 omitted
+
+# reduce 10 omitted
+
+# reduce 11 omitted
+
+# reduce 12 omitted
+
+module_eval(<<'.,.,', 'parser.y', 33)
+ def _reduce_13(val, _values, result)
+ @grammar.expect = val[1]
+ result
+ end
+.,.,
+
+# reduce 14 omitted
+
+# reduce 15 omitted
+
+module_eval(<<'.,.,', 'parser.y', 38)
+ def _reduce_16(val, _values, result)
+ val[1].each {|token|
+ @grammar.lex_param = Grammar::Code::NoReferenceCode.new(type: :lex_param, token_code: token).token_code.s_value
+ }
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 44)
+ def _reduce_17(val, _values, result)
+ val[1].each {|token|
+ @grammar.parse_param = Grammar::Code::NoReferenceCode.new(type: :parse_param, token_code: token).token_code.s_value
+ }
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 50)
+ def _reduce_18(val, _values, result)
+ begin_c_declaration("}")
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 54)
+ def _reduce_19(val, _values, result)
+ end_c_declaration
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 58)
+ def _reduce_20(val, _values, result)
+ @grammar.add_percent_code(id: val[1], code: val[4])
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 62)
+ def _reduce_21(val, _values, result)
+ begin_c_declaration("}")
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 66)
+ def _reduce_22(val, _values, result)
+ end_c_declaration
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 70)
+ def _reduce_23(val, _values, result)
+ @grammar.initial_action = Grammar::Code::InitialActionCode.new(type: :initial_action, token_code: val[3])
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 72)
+ def _reduce_24(val, _values, result)
+ @grammar.no_stdlib = true
+ result
+ end
+.,.,
+
+# reduce 25 omitted
+
+module_eval(<<'.,.,', 'parser.y', 77)
+ def _reduce_26(val, _values, result)
+ begin_c_declaration("}")
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 81)
+ def _reduce_27(val, _values, result)
+ end_c_declaration
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 85)
+ def _reduce_28(val, _values, result)
+ @grammar.set_union(
+ Grammar::Code::NoReferenceCode.new(type: :union, token_code: val[3]),
+ val[3].line
+ )
+
+ result
+ end
+.,.,
+
+# reduce 29 omitted
+
+module_eval(<<'.,.,', 'parser.y', 93)
+ def _reduce_30(val, _values, result)
+ begin_c_declaration("}")
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 97)
+ def _reduce_31(val, _values, result)
+ end_c_declaration
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 101)
+ def _reduce_32(val, _values, result)
+ @grammar.add_destructor(
+ ident_or_tags: val[6],
+ token_code: val[3],
+ lineno: val[3].line
+ )
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 109)
+ def _reduce_33(val, _values, result)
+ begin_c_declaration("}")
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 113)
+ def _reduce_34(val, _values, result)
+ end_c_declaration
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 117)
+ def _reduce_35(val, _values, result)
+ @grammar.add_printer(
+ ident_or_tags: val[6],
+ token_code: val[3],
+ lineno: val[3].line
+ )
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 125)
+ def _reduce_36(val, _values, result)
+ begin_c_declaration("}")
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 129)
+ def _reduce_37(val, _values, result)
+ end_c_declaration
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 133)
+ def _reduce_38(val, _values, result)
+ @grammar.add_error_token(
+ ident_or_tags: val[6],
+ token_code: val[3],
+ lineno: val[3].line
+ )
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 141)
+ def _reduce_39(val, _values, result)
+ @grammar.after_shift = val[1]
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 145)
+ def _reduce_40(val, _values, result)
+ @grammar.before_reduce = val[1]
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 149)
+ def _reduce_41(val, _values, result)
+ @grammar.after_reduce = val[1]
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 153)
+ def _reduce_42(val, _values, result)
+ @grammar.after_shift_error_token = val[1]
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 157)
+ def _reduce_43(val, _values, result)
+ @grammar.after_pop_stack = val[1]
+
+ result
+ end
+.,.,
+
+# reduce 44 omitted
+
+module_eval(<<'.,.,', 'parser.y', 163)
+ def _reduce_45(val, _values, result)
+ val[1].each {|hash|
+ hash[:tokens].each {|id|
+ @grammar.add_type(id: id, tag: hash[:tag])
+ }
+ }
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 171)
+ def _reduce_46(val, _values, result)
+ val[1].each {|hash|
+ hash[:tokens].each {|id|
+ sym = @grammar.add_term(id: id)
+ @grammar.add_left(sym, @precedence_number)
+ }
+ }
+ @precedence_number += 1
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 181)
+ def _reduce_47(val, _values, result)
+ val[1].each {|hash|
+ hash[:tokens].each {|id|
+ sym = @grammar.add_term(id: id)
+ @grammar.add_right(sym, @precedence_number)
+ }
+ }
+ @precedence_number += 1
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 191)
+ def _reduce_48(val, _values, result)
+ val[1].each {|hash|
+ hash[:tokens].each {|id|
+ sym = @grammar.add_term(id: id)
+ @grammar.add_precedence(sym, @precedence_number)
+ }
+ }
+ @precedence_number += 1
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 201)
+ def _reduce_49(val, _values, result)
+ val[1].each {|hash|
+ hash[:tokens].each {|id|
+ sym = @grammar.add_term(id: id)
+ @grammar.add_nonassoc(sym, @precedence_number)
+ }
+ }
+ @precedence_number += 1
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 212)
+ def _reduce_50(val, _values, result)
+ val[0].each {|token_declaration|
+ @grammar.add_term(id: token_declaration[0], alias_name: token_declaration[2], token_id: token_declaration[1], tag: nil, replace: true)
+ }
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 218)
+ def _reduce_51(val, _values, result)
+ val[1].each {|token_declaration|
+ @grammar.add_term(id: token_declaration[0], alias_name: token_declaration[2], token_id: token_declaration[1], tag: val[0], replace: true)
+ }
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 224)
+ def _reduce_52(val, _values, result)
+ val[2].each {|token_declaration|
+ @grammar.add_term(id: token_declaration[0], alias_name: token_declaration[2], token_id: token_declaration[1], tag: val[1], replace: true)
+ }
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 229)
+ def _reduce_53(val, _values, result)
+ result = [val[0]]
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 230)
+ def _reduce_54(val, _values, result)
+ result = val[0].append(val[1])
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 232)
+ def _reduce_55(val, _values, result)
+ result = val
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 236)
+ def _reduce_56(val, _values, result)
+ rule = Grammar::ParameterizingRule::Rule.new(val[1].s_value, val[3], val[6])
+ @grammar.add_parameterizing_rule(rule)
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 242)
+ def _reduce_57(val, _values, result)
+ rule = Grammar::ParameterizingRule::Rule.new(val[2].s_value, [], val[4], is_inline: true)
+ @grammar.add_parameterizing_rule(rule)
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 246)
+ def _reduce_58(val, _values, result)
+ result = [val[0]]
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 247)
+ def _reduce_59(val, _values, result)
+ result = val[0].append(val[2])
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 251)
+ def _reduce_60(val, _values, result)
+ builder = val[0]
+ result = [builder]
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 256)
+ def _reduce_61(val, _values, result)
+ builder = val[2]
+ result = val[0].append(builder)
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 262)
+ def _reduce_62(val, _values, result)
+ reset_precs
+ result = Grammar::ParameterizingRule::Rhs.new
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 267)
+ def _reduce_63(val, _values, result)
+ reset_precs
+ result = Grammar::ParameterizingRule::Rhs.new
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 272)
+ def _reduce_64(val, _values, result)
+ token = val[1]
+ token.alias_name = val[2]
+ builder = val[0]
+ builder.symbols << token
+ result = builder
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 280)
+ def _reduce_65(val, _values, result)
+ builder = val[0]
+ builder.symbols << Lrama::Lexer::Token::InstantiateRule.new(s_value: val[2], location: @lexer.location, args: [val[1]])
+ result = builder
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 286)
+ def _reduce_66(val, _values, result)
+ builder = val[0]
+ builder.symbols << Lrama::Lexer::Token::InstantiateRule.new(s_value: val[1].s_value, location: @lexer.location, args: val[3], lhs_tag: val[5])
+ result = builder
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 292)
+ def _reduce_67(val, _values, result)
+ if @prec_seen
+ on_action_error("multiple User_code after %prec", val[0]) if @code_after_prec
+ @code_after_prec = true
+ end
+ begin_c_declaration("}")
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 300)
+ def _reduce_68(val, _values, result)
+ end_c_declaration
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 304)
+ def _reduce_69(val, _values, result)
+ user_code = val[3]
+ user_code.alias_name = val[6]
+ builder = val[0]
+ builder.user_code = user_code
+ result = builder
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 312)
+ def _reduce_70(val, _values, result)
+ sym = @grammar.find_symbol_by_id!(val[2])
+ @prec_seen = true
+ builder = val[0]
+ builder.precedence_sym = sym
+ result = builder
+
+ result
+ end
+.,.,
+
+# reduce 71 omitted
+
+# reduce 72 omitted
+
+# reduce 73 omitted
+
+# reduce 74 omitted
+
+module_eval(<<'.,.,', 'parser.y', 327)
+ def _reduce_75(val, _values, result)
+ result = [{tag: nil, tokens: val[0]}]
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 331)
+ def _reduce_76(val, _values, result)
+ result = [{tag: val[0], tokens: val[1]}]
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 335)
+ def _reduce_77(val, _values, result)
+ result = val[0].append({tag: val[1], tokens: val[2]})
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 338)
+ def _reduce_78(val, _values, result)
+ result = [val[0]]
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 339)
+ def _reduce_79(val, _values, result)
+ result = val[0].append(val[1])
+ result
+ end
+.,.,
+
+# reduce 80 omitted
+
+# reduce 81 omitted
+
+module_eval(<<'.,.,', 'parser.y', 346)
+ def _reduce_82(val, _values, result)
+ begin_c_declaration("}")
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 350)
+ def _reduce_83(val, _values, result)
+ end_c_declaration
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 354)
+ def _reduce_84(val, _values, result)
+ result = val[0].append(val[3])
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 358)
+ def _reduce_85(val, _values, result)
+ begin_c_declaration("}")
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 362)
+ def _reduce_86(val, _values, result)
+ end_c_declaration
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 366)
+ def _reduce_87(val, _values, result)
+ result = [val[2]]
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 371)
+ def _reduce_88(val, _values, result)
+ result = [{tag: nil, tokens: val[0]}]
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 375)
+ def _reduce_89(val, _values, result)
+ result = [{tag: val[0], tokens: val[1]}]
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 379)
+ def _reduce_90(val, _values, result)
+ result = val[0].append({tag: val[1], tokens: val[2]})
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 382)
+ def _reduce_91(val, _values, result)
+ result = [val[0]]
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 383)
+ def _reduce_92(val, _values, result)
+ result = val[0].append(val[1])
+ result
+ end
+.,.,
+
+# reduce 93 omitted
+
+module_eval(<<'.,.,', 'parser.y', 387)
+ def _reduce_94(val, _values, result)
+ on_action_error("ident after %prec", val[0]) if @prec_seen
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 388)
+ def _reduce_95(val, _values, result)
+ on_action_error("char after %prec", val[0]) if @prec_seen
+ result
+ end
+.,.,
+
+# reduce 96 omitted
+
+# reduce 97 omitted
+
+# reduce 98 omitted
+
+# reduce 99 omitted
+
+module_eval(<<'.,.,', 'parser.y', 398)
+ def _reduce_100(val, _values, result)
+ lhs = val[0]
+ lhs.alias_name = val[1]
+ val[3].each do |builder|
+ builder.lhs = lhs
+ builder.complete_input
+ @grammar.add_rule_builder(builder)
+ end
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 409)
+ def _reduce_101(val, _values, result)
+ builder = val[0]
+ if !builder.line
+ builder.line = @lexer.line - 1
+ end
+ result = [builder]
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 417)
+ def _reduce_102(val, _values, result)
+ builder = val[2]
+ if !builder.line
+ builder.line = @lexer.line - 1
+ end
+ result = val[0].append(builder)
+
+ result
+ end
+.,.,
+
+# reduce 103 omitted
+
+module_eval(<<'.,.,', 'parser.y', 427)
+ def _reduce_104(val, _values, result)
+ reset_precs
+ result = Grammar::RuleBuilder.new(@rule_counter, @midrule_action_counter)
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 432)
+ def _reduce_105(val, _values, result)
+ reset_precs
+ result = Grammar::RuleBuilder.new(@rule_counter, @midrule_action_counter)
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 437)
+ def _reduce_106(val, _values, result)
+ token = val[1]
+ token.alias_name = val[2]
+ builder = val[0]
+ builder.add_rhs(token)
+ result = builder
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 445)
+ def _reduce_107(val, _values, result)
+ token = Lrama::Lexer::Token::InstantiateRule.new(s_value: val[2], location: @lexer.location, args: [val[1]], lhs_tag: val[3])
+ builder = val[0]
+ builder.add_rhs(token)
+ builder.line = val[1].first_line
+ result = builder
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 453)
+ def _reduce_108(val, _values, result)
+ token = Lrama::Lexer::Token::InstantiateRule.new(s_value: val[1].s_value, location: @lexer.location, args: val[3], lhs_tag: val[5])
+ builder = val[0]
+ builder.add_rhs(token)
+ builder.line = val[1].first_line
+ result = builder
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 461)
+ def _reduce_109(val, _values, result)
+ if @prec_seen
+ on_action_error("multiple User_code after %prec", val[0]) if @code_after_prec
+ @code_after_prec = true
+ end
+ begin_c_declaration("}")
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 469)
+ def _reduce_110(val, _values, result)
+ end_c_declaration
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 473)
+ def _reduce_111(val, _values, result)
+ user_code = val[3]
+ user_code.alias_name = val[6]
+ user_code.tag = val[7]
+ builder = val[0]
+ builder.user_code = user_code
+ result = builder
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 482)
+ def _reduce_112(val, _values, result)
+ sym = @grammar.find_symbol_by_id!(val[2])
+ @prec_seen = true
+ builder = val[0]
+ builder.precedence_sym = sym
+ result = builder
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 489)
+ def _reduce_113(val, _values, result)
+ result = "option"
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 490)
+ def _reduce_114(val, _values, result)
+ result = "nonempty_list"
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 491)
+ def _reduce_115(val, _values, result)
+ result = "list"
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 493)
+ def _reduce_116(val, _values, result)
+ result = [val[0]]
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 494)
+ def _reduce_117(val, _values, result)
+ result = val[0].append(val[2])
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 495)
+ def _reduce_118(val, _values, result)
+ result = [Lrama::Lexer::Token::InstantiateRule.new(s_value: val[1].s_value, location: @lexer.location, args: val[0])]
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 496)
+ def _reduce_119(val, _values, result)
+ result = [Lrama::Lexer::Token::InstantiateRule.new(s_value: val[0].s_value, location: @lexer.location, args: val[2])]
+ result
+ end
+.,.,
+
+# reduce 120 omitted
+
+module_eval(<<'.,.,', 'parser.y', 499)
+ def _reduce_121(val, _values, result)
+ result = val[1].s_value
+ result
+ end
+.,.,
+
+# reduce 122 omitted
+
+# reduce 123 omitted
+
+module_eval(<<'.,.,', 'parser.y', 506)
+ def _reduce_124(val, _values, result)
+ begin_c_declaration('\Z')
+ @grammar.epilogue_first_lineno = @lexer.line + 1
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 511)
+ def _reduce_125(val, _values, result)
+ end_c_declaration
+ @grammar.epilogue = val[2].s_value
+
+ result
+ end
+.,.,
+
+# reduce 126 omitted
+
+# reduce 127 omitted
+
+# reduce 128 omitted
+
+# reduce 129 omitted
+
+# reduce 130 omitted
+
+module_eval(<<'.,.,', 'parser.y', 522)
+ def _reduce_131(val, _values, result)
+ result = [val[0]]
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 523)
+ def _reduce_132(val, _values, result)
+ result = val[0].append(val[1])
+ result
+ end
+.,.,
+
+# reduce 133 omitted
+
+# reduce 134 omitted
+
+module_eval(<<'.,.,', 'parser.y', 528)
+ def _reduce_135(val, _values, result)
+ result = Lrama::Lexer::Token::Ident.new(s_value: val[0])
+ result
+ end
+.,.,
+
+# reduce 136 omitted
+
+# reduce 137 omitted
+
+def _reduce_none(val, _values, result)
+ val[0]
+end
+
+ end # class Parser
+end # module Lrama
diff --git a/tool/lrama/lib/lrama/parser/token_scanner.rb b/tool/lrama/lib/lrama/parser/token_scanner.rb
deleted file mode 100644
index b9c1522aff..0000000000
--- a/tool/lrama/lib/lrama/parser/token_scanner.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-module Lrama
- class Parser
- class TokenScanner
- def initialize(tokens)
- @tokens = tokens
- @index = 0
- end
-
- def current_token
- @tokens[@index]
- end
-
- def current_type
- current_token && current_token.type
- end
-
- def next
- token = current_token
- @index += 1
- return token
- end
-
- def consume(*token_types)
- if token_types.include?(current_type)
- token = current_token
- self.next
- return token
- end
-
- return nil
- end
-
- def consume!(*token_types)
- consume(*token_types) || (raise "#{token_types} is expected but #{current_type}. #{current_token}")
- end
-
- def consume_multi(*token_types)
- a = []
-
- while token_types.include?(current_type)
- a << current_token
- self.next
- end
-
- raise "No token is consumed. #{token_types}" if a.empty?
-
- return a
- end
-
- def eots?
- current_token.nil?
- end
- end
- end
-end
diff --git a/tool/lrama/lib/lrama/report.rb b/tool/lrama/lib/lrama/report.rb
index 7016a45171..650ac09d52 100644
--- a/tool/lrama/lib/lrama/report.rb
+++ b/tool/lrama/lib/lrama/report.rb
@@ -1,47 +1,2 @@
-module Lrama
- class Report
- module Profile
- # 1. Wrap target method with Profile.report_profile like below:
- #
- # Lrama::Report::Profile.report_profile { method }
- #
- # 2. Run lrama command, for example
- #
- # $ ./exe/lrama --trace=time spec/fixtures/integration/ruby_3_2_0/parse.tmp.y
- #
- # 3. Generate html file
- #
- # $ stackprof --d3-flamegraph tmp/stackprof-cpu-myapp.dump > tmp/flamegraph.html
- #
- def self.report_profile
- require "stackprof"
-
- StackProf.run(mode: :cpu, raw: true, out: 'tmp/stackprof-cpu-myapp.dump') do
- yield
- end
- end
- end
-
- module Duration
- def self.enable
- @_report_duration_enabled = true
- end
-
- def self.enabled?
- !!@_report_duration_enabled
- end
-
- def report_duration(method_name)
- time1 = Time.now.to_f
- result = yield
- time2 = Time.now.to_f
-
- if Duration.enabled?
- puts sprintf("%s %10.5f s", method_name, time2 - time1)
- end
-
- return result
- end
- end
- end
-end
+require 'lrama/report/duration'
+require 'lrama/report/profile'
diff --git a/tool/lrama/lib/lrama/report/duration.rb b/tool/lrama/lib/lrama/report/duration.rb
new file mode 100644
index 0000000000..7afe284f1a
--- /dev/null
+++ b/tool/lrama/lib/lrama/report/duration.rb
@@ -0,0 +1,25 @@
+module Lrama
+ class Report
+ module Duration
+ def self.enable
+ @_report_duration_enabled = true
+ end
+
+ def self.enabled?
+ !!@_report_duration_enabled
+ end
+
+ def report_duration(method_name)
+ time1 = Time.now.to_f
+ result = yield
+ time2 = Time.now.to_f
+
+ if Duration.enabled?
+ puts sprintf("%s %10.5f s", method_name, time2 - time1)
+ end
+
+ return result
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/report/profile.rb b/tool/lrama/lib/lrama/report/profile.rb
new file mode 100644
index 0000000000..36156800a4
--- /dev/null
+++ b/tool/lrama/lib/lrama/report/profile.rb
@@ -0,0 +1,14 @@
+module Lrama
+ class Report
+ module Profile
+ # See "Profiling Lrama" in README.md for how to use.
+ def self.report_profile
+ require "stackprof"
+
+ StackProf.run(mode: :cpu, raw: true, out: 'tmp/stackprof-cpu-myapp.dump') do
+ yield
+ end
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/state.rb b/tool/lrama/lib/lrama/state.rb
index 65ca3bcb46..ceb74d856a 100644
--- a/tool/lrama/lib/lrama/state.rb
+++ b/tool/lrama/lib/lrama/state.rb
@@ -1,36 +1,11 @@
require "lrama/state/reduce"
+require "lrama/state/reduce_reduce_conflict"
+require "lrama/state/resolved_conflict"
require "lrama/state/shift"
+require "lrama/state/shift_reduce_conflict"
module Lrama
class State
- # * symbol: A symbol under discussion
- # * reduce: A reduce under discussion
- # * which: For which a conflict is resolved. :shift, :reduce or :error (for nonassociative)
- ResolvedConflict = Struct.new(:symbol, :reduce, :which, :same_prec, keyword_init: true) do
- def report_message
- s = symbol.display_name
- r = reduce.rule.precedence_sym.display_name
- case
- when which == :shift && same_prec
- msg = "resolved as #{which} (%right #{s})"
- when which == :shift
- msg = "resolved as #{which} (#{r} < #{s})"
- when which == :reduce && same_prec
- msg = "resolved as #{which} (%left #{s})"
- when which == :reduce
- msg = "resolved as #{which} (#{s} < #{r})"
- when which == :error
- msg = "resolved as an #{which} (%nonassoc #{s})"
- else
- raise "Unknown direction. #{self}"
- end
-
- "Conflict between rule #{reduce.rule.id} and token #{s} #{msg}."
- end
- end
-
- Conflict = Struct.new(:symbols, :reduce, :type, keyword_init: true)
-
attr_reader :id, :accessing_symbol, :kernels, :conflicts, :resolved_conflicts,
:default_reduction_rule, :closure, :items
attr_accessor :shifts, :reduces
@@ -54,8 +29,8 @@ module Lrama
end
def non_default_reduces
- reduces.select do |reduce|
- reduce.rule != @default_reduction_rule
+ reduces.reject do |reduce|
+ reduce.rule == @default_reduction_rule
end
end
@@ -87,7 +62,6 @@ module Lrama
@items_to_state[items] = next_state
end
- #
def set_look_ahead(rule, look_ahead)
reduce = reduces.find do |r|
r.rule == rule
@@ -96,39 +70,21 @@ module Lrama
reduce.look_ahead = look_ahead
end
- # Returns array of [nterm, next_state]
def nterm_transitions
- return @nterm_transitions if @nterm_transitions
-
- @nterm_transitions = []
-
- shifts.each do |shift|
- next if shift.next_sym.term?
-
- @nterm_transitions << [shift, @items_to_state[shift.next_items]]
- end
-
- @nterm_transitions
+ @nterm_transitions ||= transitions.select {|shift, _| shift.next_sym.nterm? }
end
- # Returns array of [term, next_state]
def term_transitions
- return @term_transitions if @term_transitions
-
- @term_transitions = []
-
- shifts.each do |shift|
- next if shift.next_sym.nterm?
-
- @term_transitions << [shift, @items_to_state[shift.next_items]]
- end
+ @term_transitions ||= transitions.select {|shift, _| shift.next_sym.term? }
+ end
- @term_transitions
+ def transitions
+ @transitions ||= shifts.map {|shift| [shift, @items_to_state[shift.next_items]] }
end
def selected_term_transitions
- term_transitions.select do |shift, next_state|
- !shift.not_selected
+ term_transitions.reject do |shift, next_state|
+ shift.not_selected
end
end
@@ -169,6 +125,10 @@ module Lrama
end
end
+ def has_conflicts?
+ !@conflicts.empty?
+ end
+
def sr_conflicts
@conflicts.select do |conflict|
conflict.type == :shift_reduce
diff --git a/tool/lrama/lib/lrama/state/reduce_reduce_conflict.rb b/tool/lrama/lib/lrama/state/reduce_reduce_conflict.rb
new file mode 100644
index 0000000000..0a0e4dc20a
--- /dev/null
+++ b/tool/lrama/lib/lrama/state/reduce_reduce_conflict.rb
@@ -0,0 +1,9 @@
+module Lrama
+ class State
+ class ReduceReduceConflict < Struct.new(:symbols, :reduce1, :reduce2, keyword_init: true)
+ def type
+ :reduce_reduce
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/state/resolved_conflict.rb b/tool/lrama/lib/lrama/state/resolved_conflict.rb
new file mode 100644
index 0000000000..02ea892147
--- /dev/null
+++ b/tool/lrama/lib/lrama/state/resolved_conflict.rb
@@ -0,0 +1,29 @@
+module Lrama
+ class State
+ # * symbol: A symbol under discussion
+ # * reduce: A reduce under discussion
+ # * which: For which a conflict is resolved. :shift, :reduce or :error (for nonassociative)
+ class ResolvedConflict < Struct.new(:symbol, :reduce, :which, :same_prec, keyword_init: true)
+ def report_message
+ s = symbol.display_name
+ r = reduce.rule.precedence_sym.display_name
+ case
+ when which == :shift && same_prec
+ msg = "resolved as #{which} (%right #{s})"
+ when which == :shift
+ msg = "resolved as #{which} (#{r} < #{s})"
+ when which == :reduce && same_prec
+ msg = "resolved as #{which} (%left #{s})"
+ when which == :reduce
+ msg = "resolved as #{which} (#{s} < #{r})"
+ when which == :error
+ msg = "resolved as an #{which} (%nonassoc #{s})"
+ else
+ raise "Unknown direction. #{self}"
+ end
+
+ "Conflict between rule #{reduce.rule.id} and token #{s} #{msg}."
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/state/shift_reduce_conflict.rb b/tool/lrama/lib/lrama/state/shift_reduce_conflict.rb
new file mode 100644
index 0000000000..f80bd5f352
--- /dev/null
+++ b/tool/lrama/lib/lrama/state/shift_reduce_conflict.rb
@@ -0,0 +1,9 @@
+module Lrama
+ class State
+ class ShiftReduceConflict < Struct.new(:symbols, :shift, :reduce, keyword_init: true)
+ def type
+ :shift_reduce
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/states.rb b/tool/lrama/lib/lrama/states.rb
index 64be781df6..290e996b82 100644
--- a/tool/lrama/lib/lrama/states.rb
+++ b/tool/lrama/lib/lrama/states.rb
@@ -1,5 +1,6 @@
require "forwardable"
-require "lrama/report"
+require "lrama/report/duration"
+require "lrama/states/item"
module Lrama
# States is passed to a template file
@@ -11,46 +12,7 @@ module Lrama
include Lrama::Report::Duration
def_delegators "@grammar", :symbols, :terms, :nterms, :rules,
- :accept_symbol, :eof_symbol, :find_symbol_by_s_value!
-
- # TODO: Validate position is not over rule rhs
- Item = Struct.new(:rule, :position, keyword_init: true) do
- # Optimization for States#setup_state
- def hash
- [rule.id, position].hash
- end
-
- def rule_id
- rule.id
- end
-
- def next_sym
- rule.rhs[position]
- end
-
- def end_of_rule?
- rule.rhs.count == position
- end
-
- def new_by_next_position
- Item.new(rule: rule, position: position + 1)
- end
-
- def previous_sym
- rule.rhs[position - 1]
- end
-
- def display_name
- r = rule.rhs.map(&:display_name).insert(position, "•").join(" ")
- "#{r} (rule #{rule.id})"
- end
-
- # Right after position
- def display_rest
- r = rule.rhs[position..-1].map(&:display_name).join(" ")
- ". #{r} (rule #{rule.id})"
- end
- end
+ :accept_symbol, :eof_symbol, :undef_symbol, :find_symbol_by_s_value!
attr_reader :states, :reads_relation, :includes_relation, :lookback_relation
@@ -140,43 +102,27 @@ module Lrama
end
def direct_read_sets
- h = {}
-
- @direct_read_sets.each do |k, v|
- h[k] = bitmap_to_terms(v)
+ @direct_read_sets.transform_values do |v|
+ bitmap_to_terms(v)
end
-
- return h
end
def read_sets
- h = {}
-
- @read_sets.each do |k, v|
- h[k] = bitmap_to_terms(v)
+ @read_sets.transform_values do |v|
+ bitmap_to_terms(v)
end
-
- return h
end
def follow_sets
- h = {}
-
- @follow_sets.each do |k, v|
- h[k] = bitmap_to_terms(v)
+ @follow_sets.transform_values do |v|
+ bitmap_to_terms(v)
end
-
- return h
end
def la
- h = {}
-
- @la.each do |k, v|
- h[k] = bitmap_to_terms(v)
+ @la.transform_values do |v|
+ bitmap_to_terms(v)
end
-
- return h
end
private
@@ -490,7 +436,7 @@ module Lrama
# Can resolve only when both have prec
unless shift_prec && reduce_prec
- state.conflicts << State::Conflict.new(symbols: [sym], reduce: reduce, type: :shift_reduce)
+ state.conflicts << State::ShiftReduceConflict.new(symbols: [sym], shift: shift, reduce: reduce)
next
end
@@ -509,6 +455,11 @@ module Lrama
# shift_prec == reduce_prec, then check associativity
case sym.precedence.type
+ when :precedence
+ # %precedence only specifies precedence and not specify associativity
+ # then a conflict is unresolved if precedence is same.
+ state.conflicts << State::ShiftReduceConflict.new(symbols: [sym], shift: shift, reduce: reduce)
+ next
when :right
# Shift is selected
state.resolved_conflicts << State::ResolvedConflict.new(symbol: sym, reduce: reduce, which: :shift, same_prec: true)
@@ -539,16 +490,21 @@ module Lrama
def compute_reduce_reduce_conflicts
states.each do |state|
- a = []
+ count = state.reduces.count
+
+ for i in 0...count do
+ reduce1 = state.reduces[i]
+ next if reduce1.look_ahead.nil?
- state.reduces.each do |reduce|
- next if reduce.look_ahead.nil?
+ for j in (i+1)...count do
+ reduce2 = state.reduces[j]
+ next if reduce2.look_ahead.nil?
- intersection = a & reduce.look_ahead
- a += reduce.look_ahead
+ intersection = reduce1.look_ahead & reduce2.look_ahead
- if !intersection.empty?
- state.conflicts << State::Conflict.new(symbols: intersection.dup, reduce: reduce, type: :reduce_reduce)
+ if !intersection.empty?
+ state.conflicts << State::ReduceReduceConflict.new(symbols: intersection, reduce1: reduce1, reduce2: reduce2)
+ end
end
end
end
@@ -564,9 +520,9 @@ module Lrama
state.default_reduction_rule = state.reduces.map do |r|
[r.rule, r.rule.id, (r.look_ahead || []).count]
- end.sort_by do |rule, rule_id, count|
+ end.min_by do |rule, rule_id, count|
[-count, rule_id]
- end.first.first
+ end.first
end
end
diff --git a/tool/lrama/lib/lrama/states/item.rb b/tool/lrama/lib/lrama/states/item.rb
new file mode 100644
index 0000000000..31b74b9d34
--- /dev/null
+++ b/tool/lrama/lib/lrama/states/item.rb
@@ -0,0 +1,81 @@
+# TODO: Validate position is not over rule rhs
+
+require "forwardable"
+
+module Lrama
+ class States
+ class Item < Struct.new(:rule, :position, keyword_init: true)
+ extend Forwardable
+
+ def_delegators "rule", :lhs, :rhs
+
+ # Optimization for States#setup_state
+ def hash
+ [rule_id, position].hash
+ end
+
+ def rule_id
+ rule.id
+ end
+
+ def empty_rule?
+ rule.empty_rule?
+ end
+
+ def number_of_rest_symbols
+ rhs.count - position
+ end
+
+ def next_sym
+ rhs[position]
+ end
+
+ def next_next_sym
+ rhs[position + 1]
+ end
+
+ def previous_sym
+ rhs[position - 1]
+ end
+
+ def end_of_rule?
+ rhs.count == position
+ end
+
+ def beginning_of_rule?
+ position == 0
+ end
+
+ def start_item?
+ rule.initial_rule? && beginning_of_rule?
+ end
+
+ def new_by_next_position
+ Item.new(rule: rule, position: position + 1)
+ end
+
+ def symbols_before_dot
+ rhs[0...position]
+ end
+
+ def symbols_after_dot
+ rhs[position..-1]
+ end
+
+ def to_s
+ "#{lhs.id.s_value}: #{display_name}"
+ end
+
+ def display_name
+ r = rhs.map(&:display_name).insert(position, "•").join(" ")
+ "#{r} (rule #{rule_id})"
+ end
+
+ # Right after position
+ def display_rest
+ r = rhs[position..-1].map(&:display_name).join(" ")
+ ". #{r} (rule #{rule_id})"
+ end
+ end
+ end
+end
diff --git a/tool/lrama/lib/lrama/states_reporter.rb b/tool/lrama/lib/lrama/states_reporter.rb
index d3dbe6b1b4..6f96cc6f65 100644
--- a/tool/lrama/lib/lrama/states_reporter.rb
+++ b/tool/lrama/lib/lrama/states_reporter.rb
@@ -14,13 +14,13 @@ module Lrama
private
- def _report(io, grammar: false, states: false, itemsets: false, lookaheads: false, solved: false, verbose: false)
+ def _report(io, grammar: false, states: false, itemsets: false, lookaheads: false, solved: false, counterexamples: false, verbose: false)
# TODO: Unused terms
# TODO: Unused rules
report_conflicts(io)
report_grammar(io) if grammar
- report_states(io, itemsets, lookaheads, solved, verbose)
+ report_states(io, itemsets, lookaheads, solved, counterexamples, verbose)
end
def report_conflicts(io)
@@ -53,7 +53,7 @@ module Lrama
last_lhs = nil
@states.rules.each do |rule|
- if rule.rhs.empty?
+ if rule.empty_rule?
r = "ε"
else
r = rule.rhs.map(&:display_name).join(" ")
@@ -71,7 +71,11 @@ module Lrama
io << "\n\n"
end
- def report_states(io, itemsets, lookaheads, solved, verbose)
+ def report_states(io, itemsets, lookaheads, solved, counterexamples, verbose)
+ if counterexamples
+ cex = Counterexamples.new(@states)
+ end
+
@states.states.each do |state|
# Report State
io << "State #{state.id}\n\n"
@@ -80,17 +84,15 @@ module Lrama
last_lhs = nil
list = itemsets ? state.items : state.kernels
list.sort_by {|i| [i.rule_id, i.position] }.each do |item|
- rule = item.rule
- position = item.position
- if rule.rhs.empty?
+ if item.empty_rule?
r = "ε •"
else
- r = rule.rhs.map(&:display_name).insert(position, "•").join(" ")
+ r = item.rhs.map(&:display_name).insert(item.position, "•").join(" ")
end
- if rule.lhs == last_lhs
- l = " " * rule.lhs.id.s_value.length + "|"
+ if item.lhs == last_lhs
+ l = " " * item.lhs.id.s_value.length + "|"
else
- l = rule.lhs.id.s_value + ":"
+ l = item.lhs.id.s_value + ":"
end
la = ""
if lookaheads && item.end_of_rule?
@@ -100,16 +102,15 @@ module Lrama
la = " [#{look_ahead.map(&:display_name).join(", ")}]"
end
end
- last_lhs = rule.lhs
+ last_lhs = item.lhs
- io << sprintf("%5i %s %s%s\n", rule.id, l, r, la)
+ io << sprintf("%5i %s %s%s\n", item.rule_id, l, r, la)
end
io << "\n"
-
# Report shifts
- tmp = state.term_transitions.select do |shift, _|
- !shift.not_selected
+ tmp = state.term_transitions.reject do |shift, _|
+ shift.not_selected
end.map do |shift, next_state|
[shift.next_sym, next_state.id]
end
@@ -119,7 +120,6 @@ module Lrama
end
io << "\n" if !tmp.empty?
-
# Report error caused by %nonassoc
nl = false
tmp = state.resolved_conflicts.select do |resolved|
@@ -134,7 +134,6 @@ module Lrama
end
io << "\n" if !tmp.empty?
-
# Report reduces
nl = false
max_len = state.non_default_reduces.flat_map(&:look_ahead).compact.map(&:display_name).map(&:length).max || 0
@@ -155,7 +154,7 @@ module Lrama
nl = true
end
- if r = state.default_reduction_rule
+ if (r = state.default_reduction_rule)
nl = true
s = "$default".ljust(max_len)
@@ -167,7 +166,6 @@ module Lrama
end
io << "\n" if nl
-
# Report nonterminal transitions
tmp = []
max_len = 0
@@ -185,7 +183,6 @@ module Lrama
end
io << "\n" if !tmp.empty?
-
if solved
# Report conflict resolutions
state.resolved_conflicts.each do |resolved|
@@ -194,6 +191,27 @@ module Lrama
io << "\n" if !state.resolved_conflicts.empty?
end
+ if counterexamples && state.has_conflicts?
+ # Report counterexamples
+ examples = cex.compute(state)
+ examples.each do |example|
+ label0 = example.type == :shift_reduce ? "shift/reduce" : "reduce/reduce"
+ label1 = example.type == :shift_reduce ? "Shift derivation" : "First Reduce derivation"
+ label2 = example.type == :shift_reduce ? "Reduce derivation" : "Second Reduce derivation"
+
+ io << " #{label0} conflict on token #{example.conflict_symbol.id.s_value}:\n"
+ io << " #{example.path1_item}\n"
+ io << " #{example.path2_item}\n"
+ io << " #{label1}\n"
+ example.derivations1.render_strings_for_report.each do |str|
+ io << " #{str}\n"
+ end
+ io << " #{label2}\n"
+ example.derivations2.render_strings_for_report.each do |str|
+ io << " #{str}\n"
+ end
+ end
+ end
if verbose
# Report direct_read_sets
@@ -209,7 +227,6 @@ module Lrama
end
io << "\n"
-
# Report reads_relation
io << " [Reads Relation]\n"
@states.nterms.each do |nterm|
@@ -223,7 +240,6 @@ module Lrama
end
io << "\n"
-
# Report read_sets
io << " [Read sets]\n"
read_sets = @states.read_sets
@@ -238,7 +254,6 @@ module Lrama
end
io << "\n"
-
# Report includes_relation
io << " [Includes Relation]\n"
@states.nterms.each do |nterm|
@@ -252,7 +267,6 @@ module Lrama
end
io << "\n"
-
# Report lookback_relation
io << " [Lookback Relation]\n"
@states.rules.each do |rule|
@@ -261,12 +275,11 @@ module Lrama
a.each do |state_id2, nterm_id2|
n = @states.nterms.find {|n| n.token_id == nterm_id2 }
- io << " (Rule: #{rule.to_s}) -> (State #{state_id2}, #{n.id.s_value})\n"
+ io << " (Rule: #{rule}) -> (State #{state_id2}, #{n.id.s_value})\n"
end
end
io << "\n"
-
# Report follow_sets
io << " [Follow sets]\n"
follow_sets = @states.follow_sets
@@ -281,7 +294,6 @@ module Lrama
end
io << "\n"
-
# Report LA
io << " [Look-Ahead Sets]\n"
tmp = []
@@ -301,7 +313,6 @@ module Lrama
io << "\n" if !tmp.empty?
end
-
# End of Report State
io << "\n"
end
diff --git a/tool/lrama/lib/lrama/version.rb b/tool/lrama/lib/lrama/version.rb
index 0054f3d0bd..5493b9ee1f 100644
--- a/tool/lrama/lib/lrama/version.rb
+++ b/tool/lrama/lib/lrama/version.rb
@@ -1,3 +1,3 @@
module Lrama
- VERSION = "0.5.1".freeze
+ VERSION = "0.6.8".freeze
end
diff --git a/tool/lrama/template/bison/_yacc.h b/tool/lrama/template/bison/_yacc.h
new file mode 100644
index 0000000000..34ed6d81f5
--- /dev/null
+++ b/tool/lrama/template/bison/_yacc.h
@@ -0,0 +1,71 @@
+<%# b4_shared_declarations -%>
+ <%-# b4_cpp_guard_open([b4_spec_mapped_header_file]) -%>
+ <%- if output.spec_mapped_header_file -%>
+#ifndef <%= output.b4_cpp_guard__b4_spec_mapped_header_file %>
+# define <%= output.b4_cpp_guard__b4_spec_mapped_header_file %>
+ <%- end -%>
+ <%-# b4_declare_yydebug & b4_YYDEBUG_define -%>
+/* Debug traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG && !defined(yydebug)
+extern int yydebug;
+#endif
+<%= output.percent_code("requires") %>
+
+ <%-# b4_token_enums_defines -%>
+/* Token kinds. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ enum yytokentype
+ {
+<%= output.token_enums -%>
+ };
+ typedef enum yytokentype yytoken_kind_t;
+#endif
+
+ <%-# b4_declare_yylstype -%>
+ <%-# b4_value_type_define -%>
+/* Value type. */
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+union YYSTYPE
+{
+#line <%= output.grammar.union.lineno %> "<%= output.grammar_file_path %>"
+<%= output.grammar.union.braces_less_code %>
+#line [@oline@] [@ofile@]
+
+};
+typedef union YYSTYPE YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+ <%-# b4_location_type_define -%>
+/* Location type. */
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE YYLTYPE;
+struct YYLTYPE
+{
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+};
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+
+ <%-# b4_declare_yyerror_and_yylex. Not supported -%>
+ <%-# b4_declare_yyparse -%>
+int yyparse (<%= output.parse_param %>);
+
+
+<%= output.percent_code("provides") %>
+ <%-# b4_cpp_guard_close([b4_spec_mapped_header_file]) -%>
+ <%- if output.spec_mapped_header_file -%>
+#endif /* !<%= output.b4_cpp_guard__b4_spec_mapped_header_file %> */
+ <%- end -%>
diff --git a/tool/lrama/template/bison/yacc.c b/tool/lrama/template/bison/yacc.c
index 857afb26c0..2d6753c282 100644
--- a/tool/lrama/template/bison/yacc.c
+++ b/tool/lrama/template/bison/yacc.c
@@ -68,14 +68,13 @@
#define YYPULL 1
-
-
<%# b4_user_pre_prologue -%>
+<%- if output.aux.prologue -%>
/* First part of user prologue. */
#line <%= output.aux.prologue_first_lineno %> "<%= output.grammar_file_path %>"
-
<%= output.aux.prologue %>
#line [@oline@] [@ofile@]
+<%- end -%>
<%# b4_cast_define -%>
# ifndef YY_CAST
@@ -101,79 +100,13 @@
# endif
<%# b4_header_include_if -%>
+<%- if output.include_header -%>
+#include "<%= output.include_header %>"
+<%- else -%>
/* Use api.header.include to #include this header
instead of duplicating it here. */
-<%# b4_shared_declarations -%>
- <%-# b4_cpp_guard_open([b4_spec_mapped_header_file]) -%>
- <%- if output.spec_mapped_header_file -%>
-#ifndef <%= output.b4_cpp_guard__b4_spec_mapped_header_file %>
-# define <%= output.b4_cpp_guard__b4_spec_mapped_header_file %>
- <%- end -%>
- <%-# b4_declare_yydebug & b4_YYDEBUG_define -%>
-/* Debug traces. */
-#ifndef YYDEBUG
-# define YYDEBUG 0
-#endif
-#if YYDEBUG && !defined(yydebug)
-extern int yydebug;
-#endif
- <%-# b4_percent_code_get([[requires]]). %code is not supported -%>
-
- <%-# b4_token_enums_defines -%>
-/* Token kinds. */
-#ifndef YYTOKENTYPE
-# define YYTOKENTYPE
- enum yytokentype
- {
-<%= output.token_enums -%>
- };
- typedef enum yytokentype yytoken_kind_t;
-#endif
-
- <%-# b4_declare_yylstype -%>
- <%-# b4_value_type_define -%>
-/* Value type. */
-#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
-union YYSTYPE
-{
-#line <%= output.grammar.union.lineno %> "<%= output.grammar_file_path %>"
-<%= output.grammar.union.braces_less_code %>
-#line [@oline@] [@ofile@]
-
-};
-typedef union YYSTYPE YYSTYPE;
-# define YYSTYPE_IS_TRIVIAL 1
-# define YYSTYPE_IS_DECLARED 1
-#endif
-
- <%-# b4_location_type_define -%>
-/* Location type. */
-#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
-typedef struct YYLTYPE YYLTYPE;
-struct YYLTYPE
-{
- int first_line;
- int first_column;
- int last_line;
- int last_column;
-};
-# define YYLTYPE_IS_DECLARED 1
-# define YYLTYPE_IS_TRIVIAL 1
-#endif
-
-
-
-
- <%-# b4_declare_yyerror_and_yylex. Not supported -%>
- <%-# b4_declare_yyparse -%>
-int yyparse (<%= output.parse_param %>);
-
-
- <%-# b4_percent_code_get([[provides]]). %code is not supported -%>
- <%-# b4_cpp_guard_close([b4_spec_mapped_header_file]) -%>
- <%- if output.spec_mapped_header_file -%>
-#endif /* !<%= output.b4_cpp_guard__b4_spec_mapped_header_file %> */
- <%- end -%>
+<%= output.render_partial("bison/_yacc.h") %>
+<%- end -%>
<%# b4_declare_symbol_enum -%>
/* Symbol kind. */
enum yysymbol_kind_t
@@ -542,6 +475,13 @@ static const <%= output.int_type_for(output.context.yytranslate) %> yytranslate[
<%= output.yytranslate %>
};
+<%- if output.error_recovery -%>
+/* YYTRANSLATE_INVERTED[SYMBOL-NUM] -- Token number corresponding to SYMBOL-NUM */
+static const <%= output.int_type_for(output.context.yytranslate_inverted) %> yytranslate_inverted[] =
+{
+<%= output.yytranslate_inverted %>
+};
+<%- end -%>
#if YYDEBUG
/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */
static const <%= output.int_type_for(output.context.yyrline) %> yyrline[] =
@@ -1205,12 +1145,318 @@ yydestruct (const char *yymsg,
YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp<%= output.user_args %>);
YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
- YY_USE (yykind);
+ switch (yykind)
+ {
+<%= output.symbol_actions_for_destructor -%>
+ default:
+ break;
+ }
YY_IGNORE_MAYBE_UNINITIALIZED_END
}
+<%- if output.error_recovery -%>
+#ifndef YYMAXREPAIR
+# define YYMAXREPAIR(<%= output.parse_param_name %>) (3)
+#endif
+
+#ifndef YYERROR_RECOVERY_ENABLED
+# define YYERROR_RECOVERY_ENABLED(<%= output.parse_param_name %>) (1)
+#endif
+
+enum yy_repair_type {
+ insert,
+ delete,
+ shift,
+};
+
+struct yy_repair {
+ enum yy_repair_type type;
+ yysymbol_kind_t term;
+};
+typedef struct yy_repair yy_repair;
+
+struct yy_repairs {
+ /* For debug */
+ int id;
+ /* For breadth-first traversing */
+ struct yy_repairs *next;
+ YYPTRDIFF_T stack_length;
+ /* Bottom of states */
+ yy_state_t *states;
+ /* Top of states */
+ yy_state_t *state;
+ /* repair length */
+ int repair_length;
+ /* */
+ struct yy_repairs *prev_repair;
+ struct yy_repair repair;
+};
+typedef struct yy_repairs yy_repairs;
+
+struct yy_term {
+ yysymbol_kind_t kind;
+ YYSTYPE value;
+ YYLTYPE location;
+};
+typedef struct yy_term yy_term;
+
+struct yy_repair_terms {
+ int id;
+ int length;
+ yy_term terms[];
+};
+typedef struct yy_repair_terms yy_repair_terms;
+
+static void
+yy_error_token_initialize (yysymbol_kind_t yykind, YYSTYPE * const yyvaluep, YYLTYPE * const yylocationp<%= output.user_formals %>)
+{
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+switch (yykind)
+ {
+<%= output.symbol_actions_for_error_token -%>
+ default:
+ break;
+ }
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
+}
+
+static yy_repair_terms *
+yy_create_repair_terms(yy_repairs *reps<%= output.user_formals %>)
+{
+ yy_repairs *r = reps;
+ yy_repair_terms *rep_terms;
+ int count = 0;
+
+ while (r->prev_repair)
+ {
+ count++;
+ r = r->prev_repair;
+ }
+
+ rep_terms = (yy_repair_terms *) YYMALLOC (sizeof (yy_repair_terms) + sizeof (yy_term) * count);
+ rep_terms->id = reps->id;
+ rep_terms->length = count;
+
+ r = reps;
+ while (r->prev_repair)
+ {
+ rep_terms->terms[count-1].kind = r->repair.term;
+ count--;
+ r = r->prev_repair;
+ }
+
+ return rep_terms;
+}
+
+static void
+yy_print_repairs(yy_repairs *reps<%= output.user_formals %>)
+{
+ yy_repairs *r = reps;
+
+ YYDPRINTF ((stderr,
+ "id: %d, repair_length: %d, repair_state: %d, prev_repair_id: %d\n",
+ reps->id, reps->repair_length, *reps->state, reps->prev_repair->id));
+
+ while (r->prev_repair)
+ {
+ YYDPRINTF ((stderr, "%s ", yysymbol_name (r->repair.term)));
+ r = r->prev_repair;
+ }
+
+ YYDPRINTF ((stderr, "\n"));
+}
+
+static void
+yy_print_repair_terms(yy_repair_terms *rep_terms<%= output.user_formals %>)
+{
+ for (int i = 0; i < rep_terms->length; i++)
+ YYDPRINTF ((stderr, "%s ", yysymbol_name (rep_terms->terms[i].kind)));
+
+ YYDPRINTF ((stderr, "\n"));
+}
+
+static void
+yy_free_repairs(yy_repairs *reps<%= output.user_formals %>)
+{
+ while (reps)
+ {
+ yy_repairs *r = reps;
+ reps = reps->next;
+ YYFREE (r->states);
+ YYFREE (r);
+ }
+}
+
+static int
+yy_process_repairs(yy_repairs *reps, yysymbol_kind_t token)
+{
+ int yyn;
+ int yystate = *reps->state;
+ int yylen = 0;
+ yysymbol_kind_t yytoken = token;
+
+ goto yyrecover_backup;
+
+yyrecover_newstate:
+ // TODO: check reps->stack_length
+ reps->state += 1;
+ *reps->state = (yy_state_t) yystate;
+
+
+yyrecover_backup:
+ yyn = yypact[yystate];
+ if (yypact_value_is_default (yyn))
+ goto yyrecover_default;
+
+ /* "Reading a token" */
+ if (yytoken == YYSYMBOL_YYEMPTY)
+ return 1;
+
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yyrecover_default;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yytable_value_is_error (yyn))
+ goto yyrecover_errlab;
+ yyn = -yyn;
+ goto yyrecover_reduce;
+ }
+
+ /* shift */
+ yystate = yyn;
+ yytoken = YYSYMBOL_YYEMPTY;
+ goto yyrecover_newstate;
+
+
+yyrecover_default:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyrecover_errlab;
+ goto yyrecover_reduce;
+
+
+yyrecover_reduce:
+ yylen = yyr2[yyn];
+ /* YYPOPSTACK */
+ reps->state -= yylen;
+ yylen = 0;
+
+ {
+ const int yylhs = yyr1[yyn] - YYNTOKENS;
+ const int yyi = yypgoto[yylhs] + *reps->state;
+ yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *reps->state
+ ? yytable[yyi]
+ : yydefgoto[yylhs]);
+ }
+
+ goto yyrecover_newstate;
+
+yyrecover_errlab:
+ return 0;
+}
+
+static yy_repair_terms *
+yyrecover(yy_state_t *yyss, yy_state_t *yyssp, int yychar<%= output.user_formals %>)
+{
+ yysymbol_kind_t yytoken = YYTRANSLATE (yychar);
+ yy_repair_terms *rep_terms = YY_NULLPTR;
+ int count = 0;
+
+ yy_repairs *head = (yy_repairs *) YYMALLOC (sizeof (yy_repairs));
+ yy_repairs *current = head;
+ yy_repairs *tail = head;
+ YYPTRDIFF_T stack_length = yyssp - yyss + 1;
+
+ head->id = count;
+ head->next = 0;
+ head->stack_length = stack_length;
+ head->states = (yy_state_t *) YYMALLOC (sizeof (yy_state_t) * (stack_length));
+ head->state = head->states + (yyssp - yyss);
+ YYCOPY (head->states, yyss, stack_length);
+ head->repair_length = 0;
+ head->prev_repair = 0;
+
+ stack_length = (stack_length * 2 > 100) ? (stack_length * 2) : 100;
+ count++;
+
+ while (current)
+ {
+ int yystate = *current->state;
+ int yyn = yypact[yystate];
+ /* See also: yypcontext_expected_tokens */
+ if (!yypact_value_is_default (yyn))
+ {
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yyx;
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ {
+ if (yyx != YYSYMBOL_YYerror)
+ {
+ if (current->repair_length + 1 > YYMAXREPAIR(<%= output.parse_param_name %>))
+ continue;
+
+ yy_repairs *new = (yy_repairs *) YYMALLOC (sizeof (yy_repairs));
+ new->id = count;
+ new->next = 0;
+ new->stack_length = stack_length;
+ new->states = (yy_state_t *) YYMALLOC (sizeof (yy_state_t) * (stack_length));
+ new->state = new->states + (current->state - current->states);
+ YYCOPY (new->states, current->states, current->state - current->states + 1);
+ new->repair_length = current->repair_length + 1;
+ new->prev_repair = current;
+ new->repair.type = insert;
+ new->repair.term = (yysymbol_kind_t) yyx;
+
+ /* Process PDA assuming next token is yyx */
+ if (! yy_process_repairs (new, yyx))
+ {
+ YYFREE (new);
+ continue;
+ }
+
+ tail->next = new;
+ tail = new;
+ count++;
+
+ if (yyx == yytoken)
+ {
+ rep_terms = yy_create_repair_terms (current<%= output.user_args %>);
+ YYDPRINTF ((stderr, "repair_terms found. id: %d, length: %d\n", rep_terms->id, rep_terms->length));
+ yy_print_repairs (current<%= output.user_args %>);
+ yy_print_repair_terms (rep_terms<%= output.user_args %>);
+
+ goto done;
+ }
+
+ YYDPRINTF ((stderr,
+ "New repairs is enqueued. count: %d, yystate: %d, yyx: %d\n",
+ count, yystate, yyx));
+ yy_print_repairs (new<%= output.user_args %>);
+ }
+ }
+ }
+
+ current = current->next;
+ }
+
+done:
+
+ yy_free_repairs(head<%= output.user_args %>);
+
+ if (!rep_terms)
+ {
+ YYDPRINTF ((stderr, "repair_terms not found\n"));
+ }
+
+ return rep_terms;
+}
+<%- end -%>
@@ -1243,6 +1489,7 @@ YYLTYPE yylloc = yyloc_default;
<%# b4_declare_parser_state_variables -%>
/* Number of syntax errors so far. */
int yynerrs = 0;
+ YY_USE (yynerrs); /* Silence compiler warning. */
yy_state_fast_t yystate = 0;
/* Number of tokens to shift before error messages enabled. */
@@ -1281,6 +1528,12 @@ YYLTYPE yylloc = yyloc_default;
/* The locations where the error started and ended. */
YYLTYPE yyerror_range[3];
+<%- if output.error_recovery -%>
+ yy_repair_terms *rep_terms = 0;
+ yy_term term_backup;
+ int rep_terms_index;
+ int yychar_backup;
+<%- end -%>
/* Buffer for error messages, and its allocated size. */
char yymsgbuf[128];
@@ -1415,6 +1668,39 @@ yybackup:
/* Not known => get a lookahead token if don't already have one. */
+<%- if output.error_recovery -%>
+ if (YYERROR_RECOVERY_ENABLED(<%= output.parse_param_name %>))
+ {
+ if (yychar == YYEMPTY && rep_terms)
+ {
+
+ if (rep_terms_index < rep_terms->length)
+ {
+ YYDPRINTF ((stderr, "An error recovery token is used\n"));
+ yy_term term = rep_terms->terms[rep_terms_index];
+ yytoken = term.kind;
+ yylval = term.value;
+ yylloc = term.location;
+ yychar = yytranslate_inverted[yytoken];
+ YY_SYMBOL_PRINT ("Next error recovery token is", yytoken, &yylval, &yylloc<%= output.user_args %>);
+ rep_terms_index++;
+ }
+ else
+ {
+ YYDPRINTF ((stderr, "Error recovery is completed\n"));
+ yytoken = term_backup.kind;
+ yylval = term_backup.value;
+ yylloc = term_backup.location;
+ yychar = yychar_backup;
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc<%= output.user_args %>);
+
+ YYFREE (rep_terms);
+ rep_terms = 0;
+ yychar_backup = 0;
+ }
+ }
+ }
+<%- end -%>
/* YYCHAR is either empty, or end-of-input, or a valid lookahead. */
if (yychar == YYEMPTY)
{
@@ -1471,6 +1757,7 @@ yybackup:
*++yyvsp = yylval;
YY_IGNORE_MAYBE_UNINITIALIZED_END
*++yylsp = yylloc;
+<%= output.after_shift_function("/* %after-shift code. */") %>
/* Discard the shifted token. */
yychar = YYEMPTY;
@@ -1503,6 +1790,7 @@ yyreduce:
unconditionally makes the parser a bit smaller, and it avoids a
GCC warning that YYVAL may be used uninitialized. */
yyval = yyvsp[1-yylen];
+<%= output.before_reduce_function("/* %before-reduce function. */") %>
/* Default location. */
YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen);
@@ -1528,6 +1816,7 @@ yyreduce:
YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc<%= output.user_args %>);
YYPOPSTACK (yylen);
+<%= output.after_reduce_function("/* %after-reduce function. */") %>
yylen = 0;
*++yyvsp = yyval;
@@ -1629,6 +1918,7 @@ yyerrorlab:
/* Do not reclaim the symbols of the rule whose action triggered
this YYERROR. */
YYPOPSTACK (yylen);
+<%= output.after_pop_stack_function("yylen", "/* %after-pop-stack function. */") %>
yylen = 0;
YY_STACK_PRINT (yyss, yyssp<%= output.user_args %>);
yystate = *yyssp;
@@ -1639,6 +1929,30 @@ yyerrorlab:
| yyerrlab1 -- common code for both syntax error and YYERROR. |
`-------------------------------------------------------------*/
yyerrlab1:
+<%- if output.error_recovery -%>
+ if (YYERROR_RECOVERY_ENABLED(<%= output.parse_param_name %>))
+ {
+ rep_terms = yyrecover (yyss, yyssp, yychar<%= output.user_args %>);
+ if (rep_terms)
+ {
+ for (int i = 0; i < rep_terms->length; i++)
+ {
+ yy_term *term = &rep_terms->terms[i];
+ yy_error_token_initialize (term->kind, &term->value, &term->location<%= output.user_args %>);
+ }
+
+ yychar_backup = yychar;
+ /* Can be packed into (the tail of) rep_terms? */
+ term_backup.kind = yytoken;
+ term_backup.value = yylval;
+ term_backup.location = yylloc;
+ rep_terms_index = 0;
+ yychar = YYEMPTY;
+
+ goto yybackup;
+ }
+ }
+<%- end -%>
yyerrstatus = 3; /* Each real token shifted decrements this. */
/* Pop stack until we find a state that shifts the error token. */
@@ -1664,6 +1978,7 @@ yyerrlab1:
yydestruct ("Error: popping",
YY_ACCESSING_SYMBOL (yystate), yyvsp, yylsp<%= output.user_args %>);
YYPOPSTACK (1);
+<%= output.after_pop_stack_function(1, "/* %after-pop-stack function. */") %>
yystate = *yyssp;
YY_STACK_PRINT (yyss, yyssp<%= output.user_args %>);
}
@@ -1678,6 +1993,7 @@ yyerrlab1:
/* Shift the error token. */
YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp<%= output.user_args %>);
+<%= output.after_shift_error_token_function("/* %after-shift-error-token code. */") %>
yystate = yyn;
goto yynewstate;
@@ -1740,6 +2056,8 @@ yyreturnlab:
}
<%# b4_percent_code_get([[epilogue]]) -%>
+<%- if output.aux.epilogue -%>
#line <%= output.aux.epilogue_first_lineno - 1 %> "<%= output.grammar_file_path %>"
-
<%= output.aux.epilogue -%>
+<%- end -%>
+
diff --git a/tool/lrama/template/bison/yacc.h b/tool/lrama/template/bison/yacc.h
index 932d6409db..848dbf5961 100644
--- a/tool/lrama/template/bison/yacc.h
+++ b/tool/lrama/template/bison/yacc.h
@@ -37,76 +37,4 @@
/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual,
especially those whose name start with YY_ or yy_. They are
private implementation details that can be changed or removed. */
-
-<%# b4_shared_declarations -%>
-<%# b4_shared_declarations -%>
- <%-# b4_cpp_guard_open([b4_spec_mapped_header_file]) -%>
- <%- if output.spec_mapped_header_file -%>
-#ifndef <%= output.b4_cpp_guard__b4_spec_mapped_header_file %>
-# define <%= output.b4_cpp_guard__b4_spec_mapped_header_file %>
- <%- end -%>
- <%-# b4_declare_yydebug & b4_YYDEBUG_define -%>
-/* Debug traces. */
-#ifndef YYDEBUG
-# define YYDEBUG 0
-#endif
-#if YYDEBUG
-extern int yydebug;
-#endif
- <%-# b4_percent_code_get([[requires]]). %code is not supported -%>
-
- <%-# b4_token_enums_defines -%>
-/* Token kinds. */
-#ifndef YYTOKENTYPE
-# define YYTOKENTYPE
- enum yytokentype
- {
-<%= output.token_enums -%>
- };
- typedef enum yytokentype yytoken_kind_t;
-#endif
-
- <%-# b4_declare_yylstype -%>
- <%-# b4_value_type_define -%>
-/* Value type. */
-#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
-union YYSTYPE
-{
-#line <%= output.grammar.union.lineno %> "<%= output.grammar_file_path %>"
-<%= output.grammar.union.braces_less_code %>
-#line [@oline@] [@ofile@]
-
-};
-typedef union YYSTYPE YYSTYPE;
-# define YYSTYPE_IS_TRIVIAL 1
-# define YYSTYPE_IS_DECLARED 1
-#endif
-
- <%-# b4_location_type_define -%>
-/* Location type. */
-#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
-typedef struct YYLTYPE YYLTYPE;
-struct YYLTYPE
-{
- int first_line;
- int first_column;
- int last_line;
- int last_column;
-};
-# define YYLTYPE_IS_DECLARED 1
-# define YYLTYPE_IS_TRIVIAL 1
-#endif
-
-
-
-
- <%-# b4_declare_yyerror_and_yylex. Not supported -%>
- <%-# b4_declare_yyparse -%>
-int yyparse (<%= output.parse_param %>);
-
-
- <%-# b4_percent_code_get([[provides]]). %code is not supported -%>
- <%-# b4_cpp_guard_close([b4_spec_mapped_header_file]) -%>
- <%- if output.spec_mapped_header_file -%>
-#endif /* !<%= output.b4_cpp_guard__b4_spec_mapped_header_file %> */
- <%- end -%>
+<%= output.render_partial("bison/_yacc.h") %>
diff --git a/tool/m4/ruby_default_arch.m4 b/tool/m4/ruby_default_arch.m4
index 35eb8112f6..2f25ba81ee 100644
--- a/tool/m4/ruby_default_arch.m4
+++ b/tool/m4/ruby_default_arch.m4
@@ -1,12 +1,21 @@
dnl -*- Autoconf -*-
AC_DEFUN([RUBY_DEFAULT_ARCH], [
+# Set ARCH_FLAG for different width but family CPU
AC_MSG_CHECKING([arch option])
-AS_CASE([$1],
- [arm64], [],
- [*64], [ARCH_FLAG=-m64],
- [[i[3-6]86]], [ARCH_FLAG=-m32],
- [ppc], [ARCH_FLAG=-m32],
- [AC_MSG_ERROR(unknown target architecture: $target_archs)]
- )
-AC_MSG_RESULT([$ARCH_FLAG])
+AS_CASE([$1:"$host_cpu"],
+ [arm64:arm*], [ARCH_FLAG=-m64],
+ [arm*:arm*], [ARCH_FLAG=-m32],
+ [x86_64:[i[3-6]86]], [ARCH_FLAG=-m64],
+ [x64:x86_64], [],
+ [[i[3-6]86]:x86_64], [ARCH_FLAG=-m32],
+ [ppc64:ppc*], [ARCH_FLAG=-m64],
+ [ppc*:ppc64], [ARCH_FLAG=-m32],
+ [
+ ARCH_FLAG=
+ for flag in "-arch "$1 -march=$1; do
+ _RUBY_TRY_CFLAGS([$]flag, [ARCH_FLAG="[$]flag"])
+ test x"$ARCH_FLAG" = x || break
+ done]
+)
+AC_MSG_RESULT([${ARCH_FLAG:-'(none)'}])
])dnl
diff --git a/tool/m4/ruby_shared_gc.m4 b/tool/m4/ruby_shared_gc.m4
new file mode 100644
index 0000000000..a27b9b8505
--- /dev/null
+++ b/tool/m4/ruby_shared_gc.m4
@@ -0,0 +1,19 @@
+dnl -*- Autoconf -*-
+AC_DEFUN([RUBY_SHARED_GC],[
+AC_ARG_WITH(shared-gc,
+ AS_HELP_STRING([--with-shared-gc],
+ [Enable replacement of Ruby's GC from a shared library.]),
+ [with_shared_gc=$withval], [unset with_shared_gc]
+)
+
+AC_SUBST([with_shared_gc])
+AC_MSG_CHECKING([if Ruby is build with shared GC support])
+AS_IF([test "$with_shared_gc" = "yes"], [
+ AC_MSG_RESULT([yes])
+ AC_DEFINE([USE_SHARED_GC], [1])
+], [
+ AC_MSG_RESULT([no])
+ with_shared_gc="no"
+ AC_DEFINE([USE_SHARED_GC], [0])
+])
+])dnl
diff --git a/tool/m4/ruby_try_cflags.m4 b/tool/m4/ruby_try_cflags.m4
index 672f4f8e51..b74718fe5e 100644
--- a/tool/m4/ruby_try_cflags.m4
+++ b/tool/m4/ruby_try_cflags.m4
@@ -6,14 +6,19 @@ m4_version_prereq([2.70], [], [
m4_defun([AC_LANG_PROGRAM(C)], m4_bpatsubst(m4_defn([AC_LANG_PROGRAM(C)]), [main ()], [main (void)]))
])dnl
dnl
-AC_DEFUN([RUBY_TRY_CFLAGS], [
- AC_MSG_CHECKING([whether ]$1[ is accepted as CFLAGS])
+AC_DEFUN([_RUBY_TRY_CFLAGS], [
RUBY_WERROR_FLAG([
CFLAGS="[$]CFLAGS $1"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[$4]], [[$5]])],
+ [$2], [$3])
+ ])dnl
+])dnl
+AC_DEFUN([RUBY_TRY_CFLAGS], [
+ AC_MSG_CHECKING([whether ]$1[ is accepted as CFLAGS])dnl
+ _RUBY_TRY_CFLAGS([$1],
[$2
AC_MSG_RESULT(yes)],
[$3
- AC_MSG_RESULT(no)])
- ])
+ AC_MSG_RESULT(no)],
+ [$4], [$5])
])dnl
diff --git a/tool/m4/ruby_universal_arch.m4 b/tool/m4/ruby_universal_arch.m4
index c8914c88d9..d3e0dd0b47 100644
--- a/tool/m4/ruby_universal_arch.m4
+++ b/tool/m4/ruby_universal_arch.m4
@@ -17,7 +17,7 @@ AS_IF([test ${target_archs+set}], [
cpu=$archs
cpu=`echo $cpu | sed 's/-.*-.*//'`
universal_binary="${universal_binary+$universal_binary,}$cpu"
- universal_archnames="${universal_archnames} ${archs}=${cpu}"
+ universal_archnames="${universal_archnames:+$universal_archnames }${archs}=${cpu}"
ARCH_FLAG="${ARCH_FLAG+$ARCH_FLAG }-arch $archs"
])
done
@@ -40,7 +40,7 @@ AS_IF([test ${target_archs+set}], [
AS_IF([$CC $CFLAGS $ARCH_FLAG -o conftest conftest.c > /dev/null 2>&1], [
rm -fr conftest.*
], [test -z "$ARCH_FLAG"], [
- RUBY_DEFAULT_ARCH("$target_archs")
+ RUBY_DEFAULT_ARCH($target_archs)
])
])
target_cpu=${target_archs}
@@ -73,7 +73,7 @@ EOF
sed -n 's/^"processor-name=\(.*\)"/\1/p'`
target="$target_cpu${target}"
AC_MSG_RESULT([$target_cpu])
- ])
+ ])
])
target_archs="$target_cpu"
])
diff --git a/tool/m4/ruby_wasm_tools.m4 b/tool/m4/ruby_wasm_tools.m4
index a6d8c34ebc..efc017e771 100644
--- a/tool/m4/ruby_wasm_tools.m4
+++ b/tool/m4/ruby_wasm_tools.m4
@@ -9,7 +9,7 @@ AC_DEFUN([RUBY_WASM_TOOLS],
AC_SUBST(wasmoptflags)
: ${wasmoptflags=-O3}
- AC_MSG_CHECKING([wheather \$WASI_SDK_PATH is set])
+ AC_MSG_CHECKING([whether \$WASI_SDK_PATH is set])
AS_IF([test x"${WASI_SDK_PATH}" = x], [
AC_MSG_RESULT([no])
AC_MSG_ERROR([WASI_SDK_PATH environment variable is required])
@@ -19,6 +19,7 @@ AC_DEFUN([RUBY_WASM_TOOLS],
LD="${LD:-${WASI_SDK_PATH}/bin/clang}"
AR="${AR:-${WASI_SDK_PATH}/bin/llvm-ar}"
RANLIB="${RANLIB:-${WASI_SDK_PATH}/bin/llvm-ranlib}"
+ OBJCOPY="${OBJCOPY:-${WASI_SDK_PATH}/bin/llvm-objcopy}"
])
])
])dnl
diff --git a/tool/make-snapshot b/tool/make-snapshot
index 3e5689e873..7446f18578 100755
--- a/tool/make-snapshot
+++ b/tool/make-snapshot
@@ -364,7 +364,7 @@ def package(vcs, rev, destdir, tmp = nil)
end
elsif prerelease
versionhdr ||= IO.read("#{v}/version.h")
- versionhdr.sub!(/^\#define\s+RUBY_PATCHLEVEL_STR\s+"\K.+?(?=")/, tag)
+ versionhdr.sub!(/^\#\s*define\s+RUBY_PATCHLEVEL_STR\s+"\K.+?(?=")/, tag) or raise "no match of RUBY_PATCHLEVEL_STR to replace"
IO.write("#{v}/version.h", versionhdr)
else
tag ||= vcs.revision_name(revision)
@@ -384,8 +384,21 @@ def package(vcs, rev, destdir, tmp = nil)
puts $colorize.fail("patching failed")
return
end
- def (clean = []).add(n) push(n); n end
- def clean.create(file, content = "") File.binwrite(add(file), content) end
+
+ class << (clean = [])
+ def add(n) push(n)
+ n
+ end
+ def create(file, content = "", &block)
+ add(file)
+ if block
+ File.open(file, "wb", &block)
+ else
+ File.binwrite(file, content)
+ end
+ end
+ end
+
Dir.chdir(v) do
unless File.exist?("ChangeLog")
vcs.export_changelog(url, nil, revision, "ChangeLog")
@@ -406,7 +419,7 @@ def package(vcs, rev, destdir, tmp = nil)
puts
end
- File.open(clean.add("cross.rb"), "w") do |f|
+ clean.create("cross.rb") do |f|
f.puts "Object.__send__(:remove_const, :CROSS_COMPILING) if defined?(CROSS_COMPILING)"
f.puts "CROSS_COMPILING=true"
f.puts "Object.__send__(:remove_const, :RUBY_PLATFORM)"
@@ -505,8 +518,7 @@ touch-unicode-files:
end
vcs.after_export(".") if exported
clean.concat(Dir.glob("ext/**/autom4te.cache"))
- FileUtils.rm_rf(clean) unless $keep_temp
- FileUtils.rm_rf(".downloaded-cache")
+ clean.add(".downloaded-cache")
if File.exist?("gems/bundled_gems")
gems = Dir.glob("gems/*.gem")
gems -= File.readlines("gems/bundled_gems").map {|line|
@@ -514,10 +526,11 @@ touch-unicode-files:
name, version, _ = line.split(' ')
"gems/#{name}-#{version}.gem"
}
- FileUtils.rm_f(gems)
+ clean.concat(gems)
else
- FileUtils.rm_rf("gems")
+ clean.add("gems")
end
+ FileUtils.rm_rf(clean)
if modified
touch_all(modified, "**/*/", 0) do |name, stat|
stat.mtime > modified
diff --git a/tool/merger.rb b/tool/merger.rb
index 7b378787ce..0d9957074f 100755
--- a/tool/merger.rb
+++ b/tool/merger.rb
@@ -57,11 +57,11 @@ class << Merger
yield if block_given?
STDERR.puts "\e[1;33m#{str} ([y]es|[a]bort|[r]etry#{'|[e]dit' if editfile})\e[0m"
case STDIN.gets
- when /\Aa/i then exit
+ when /\Aa/i then exit 1
when /\Ar/i then redo
when /\Ay/i then break
when /\Ae/i then system(ENV['EDITOR'], editfile)
- else exit
+ else exit 1
end
end
end
@@ -295,7 +295,8 @@ else
tickets = ''
end
- revstr = ARGV[0].delete('^, :\-0-9a-fA-F')
+ revstr = ARGV[0].gsub(%r!https://github\.com/ruby/ruby/commit/|https://bugs\.ruby-lang\.org/projects/ruby-master/repository/git/revisions/!, '')
+ revstr = revstr.delete('^, :\-0-9a-fA-F')
revs = revstr.split(/[,\s]+/)
commit_message = ''
@@ -323,7 +324,10 @@ else
end
patch = resp.body.sub(/^diff --git a\/version\.h b\/version\.h\nindex .*\n--- a\/version\.h\n\+\+\+ b\/version\.h\n@@ .* @@\n(?:[-\+ ].*\n|\n)+/, '')
- message = "\n\n#{(patch[/^Subject: (.*)\n\ndiff --git/m, 1] || "Message not found for revision: #{git_rev}\n")}"
+ message = "#{(patch[/^Subject: (.*)\n---\n /m, 1] || "Message not found for revision: #{git_rev}\n")}"
+ message.gsub!(/\G(.*)\n( .*)/, "\\1\\2")
+ message = "\n\n#{message}"
+
puts '+ git apply'
IO.popen(['git', 'apply', '--3way'], 'wb') { |f| f.write(patch) }
else
diff --git a/tool/missing-baseruby.bat b/tool/missing-baseruby.bat
new file mode 100755
index 0000000000..87a9857e06
--- /dev/null
+++ b/tool/missing-baseruby.bat
@@ -0,0 +1,19 @@
+:"" == "
+@echo off || (
+ :warn
+ echo>&2.%~1
+ goto :eof
+ :abort
+ exit /b 1
+)||(
+:)"||(
+ s = %^#
+)
+: ; call() { local call=${1#:}; shift; $call "$@"; }
+: ; warn() { echo "$1" >&2; }
+: ; abort () { exit 1; }
+
+call :warn "executable host ruby is required. use --with-baseruby option."
+call :warn "Note that BASERUBY must be Ruby 3.0.0 or later."
+call :abort
+: || (:^; abort if RUBY_VERSION < s[%r"warn .*Ruby ([\d.]+)(?:\.0)?",1])
diff --git a/tool/mk_builtin_loader.rb b/tool/mk_builtin_loader.rb
index 5ab427ca9b..4abd497f0e 100644
--- a/tool/mk_builtin_loader.rb
+++ b/tool/mk_builtin_loader.rb
@@ -6,7 +6,7 @@ require_relative 'ruby_vm/helpers/c_escape'
SUBLIBS = {}
REQUIRED = {}
-BUILTIN_ATTRS = %w[leaf no_gc]
+BUILTIN_ATTRS = %w[leaf inline_block use_block]
def string_literal(lit, str = [])
while lit
@@ -166,7 +166,7 @@ def collect_builtin base, tree, name, bs, inlines, locals = nil
when 'cstmt'
text = inline_text argc, args.first
- func_name = "_bi#{inlines.size}"
+ func_name = "_bi#{lineno}"
cfunc_name = make_cfunc_name(inlines, name, lineno)
inlines[cfunc_name] = [lineno, text, locals, func_name]
argc -= 1
@@ -174,7 +174,7 @@ def collect_builtin base, tree, name, bs, inlines, locals = nil
text = inline_text argc, args.first
code = "return #{text};"
- func_name = "_bi#{inlines.size}"
+ func_name = "_bi#{lineno}"
cfunc_name = make_cfunc_name(inlines, name, lineno)
locals = [] if $1 == 'cconst'
@@ -263,12 +263,19 @@ end
def generate_cexpr(ofile, lineno, line_file, body_lineno, text, locals, func_name)
f = StringIO.new
+
+ # Avoid generating fetches of lvars we don't need. This is imperfect as it
+ # will match text inside strings or other false positives.
+ local_candidates = text.scan(/[a-zA-Z_][a-zA-Z0-9_]*/)
+
f.puts '{'
lineno += 1
# locals is nil outside methods
locals&.reverse_each&.with_index{|param, i|
next unless Symbol === param
- f.puts "MAYBE_UNUSED(const VALUE) #{param} = rb_vm_lvar(ec, #{-3 - i});"
+ next unless local_candidates.include?(param.to_s)
+ f.puts "VALUE *const #{param}__ptr = (VALUE *)&ec->cfp->ep[#{-3 - i}];"
+ f.puts "MAYBE_UNUSED(const VALUE) #{param} = *#{param}__ptr;"
lineno += 1
}
f.puts "#line #{body_lineno} \"#{line_file}\""
diff --git a/tool/mkrunnable.rb b/tool/mkrunnable.rb
index 8bfb4fe6a4..3a62fea80f 100755
--- a/tool/mkrunnable.rb
+++ b/tool/mkrunnable.rb
@@ -6,6 +6,7 @@
require './rbconfig'
require 'fileutils'
+require_relative 'lib/path'
case ARGV[0]
when "-n"
@@ -18,93 +19,7 @@ else
include FileUtils
end
-module Mswin
- def ln_safe(src, dest, *opt)
- cmd = ["mklink", dest.tr("/", "\\"), src.tr("/", "\\")]
- cmd[1, 0] = opt
- return if system("cmd", "/c", *cmd)
- # TODO: use RUNAS or something
- puts cmd.join(" ")
- end
-
- def ln_dir_safe(src, dest)
- ln_safe(src, dest, "/d")
- end
-end
-
-def clean_link(src, dest)
- begin
- link = File.readlink(dest)
- rescue
- else
- return if link == src
- File.unlink(dest)
- end
- yield src, dest
-end
-
-def ln_safe(src, dest)
- ln_sf(src, dest)
-rescue Errno::ENOENT
- # Windows disallows to create broken symboic links, probably because
- # it is a kind of reparse points.
- raise if File.exist?(src)
-end
-
-alias ln_dir_safe ln_safe
-
-case RUBY_PLATFORM
-when /linux|darwin|solaris/
- def ln_exe(src, dest)
- ln(src, dest, force: true)
- end
-else
- alias ln_exe ln_safe
-end
-
-if !File.respond_to?(:symlink) && /mingw|mswin/ =~ (CROSS_COMPILING || RUBY_PLATFORM)
- extend Mswin
-end
-
-def clean_path(path)
- path = "#{path}/".gsub(/(\A|\/)(?:\.\/)+/, '\1').tr_s('/', '/')
- nil while path.sub!(/[^\/]+\/\.\.\//, '')
- path
-end
-
-def relative_path_from(path, base)
- path = clean_path(path)
- base = clean_path(base)
- path, base = [path, base].map{|s|s.split("/")}
- until path.empty? or base.empty? or path[0] != base[0]
- path.shift
- base.shift
- end
- path, base = [path, base].map{|s|s.join("/")}
- if /(\A|\/)\.\.\// =~ base
- File.expand_path(path)
- else
- base.gsub!(/[^\/]+/, '..')
- File.join(base, path)
- end
-end
-
-def ln_relative(src, dest, executable = false)
- return if File.identical?(src, dest)
- parent = File.dirname(dest)
- File.directory?(parent) or mkdir_p(parent)
- if executable
- return (ln_exe(src, dest) if File.exist?(src))
- end
- clean_link(relative_path_from(src, parent), dest) {|s, d| ln_safe(s, d)}
-end
-
-def ln_dir_relative(src, dest)
- return if File.identical?(src, dest)
- parent = File.dirname(dest)
- File.directory?(parent) or mkdir_p(parent)
- clean_link(relative_path_from(src, parent), dest) {|s, d| ln_dir_safe(s, d)}
-end
+include Path
config = RbConfig::MAKEFILE_CONFIG.merge("prefix" => ".", "exec_prefix" => ".")
config.each_value {|s| RbConfig.expand(s, config)}
@@ -119,18 +34,22 @@ vendordir = config["vendordir"]
rubylibdir = config["rubylibdir"]
rubyarchdir = config["rubyarchdir"]
archdir = "#{extout}/#{arch}"
-[bindir, libdir, archdir].uniq.each do |dir|
+exedir = libdirname == "archlibdir" ? "#{config["libexecdir"]}/#{arch}/bin" : bindir
+[exedir, libdir, archdir].uniq.each do |dir|
File.directory?(dir) or mkdir_p(dir)
end
+unless exedir == bindir
+ ln_dir_relative(exedir, bindir)
+end
exeext = config["EXEEXT"]
ruby_install_name = config["ruby_install_name"]
rubyw_install_name = config["rubyw_install_name"]
goruby_install_name = "go" + ruby_install_name
-[ruby_install_name, rubyw_install_name, goruby_install_name].map do |ruby|
+[ruby_install_name, rubyw_install_name, goruby_install_name].each do |ruby|
if ruby and !ruby.empty?
ruby += exeext
- ln_relative(ruby, "#{bindir}/#{ruby}", true)
+ ln_relative(ruby, "#{exedir}/#{ruby}", true)
end
end
so = config["LIBRUBY_SO"]
diff --git a/tool/outdate-bundled-gems.rb b/tool/outdate-bundled-gems.rb
index bf6b268700..c82d31d743 100755
--- a/tool/outdate-bundled-gems.rb
+++ b/tool/outdate-bundled-gems.rb
@@ -3,26 +3,60 @@ require 'fileutils'
require 'rubygems'
fu = FileUtils::Verbose
+
until ARGV.empty?
case ARGV.first
when '--'
ARGV.shift
break
- when '-n', '--dryrun'
+ when '-n', '--dry-run', '--dryrun'
+ ## -n, --dry-run Don't remove
fu = FileUtils::DryRun
when /\A--make=/
# just to run when `make -n`
when /\A--mflags=(.*)/
fu = FileUtils::DryRun if /\A-\S*n/ =~ $1
+ when /\A--gem[-_]platform=(.*)/im
+ ## --gem-platform=PLATFORM Platform in RubyGems style
+ gem_platform = $1
+ ruby_platform = nil
+ when /\A--ruby[-_]platform=(.*)/im
+ ## --ruby-platform=PLATFORM Platform in Ruby style
+ ruby_platform = $1
+ gem_platform = nil
+ when /\A--ruby[-_]version=(.*)/im
+ ## --ruby-version=VERSION Ruby version to keep
+ ruby_version = $1
+ when /\A--only=(?:(curdir|srcdir)|all)\z/im
+ ## --only=(curdir|srcdir|all) Specify directory to remove gems from
+ only = $1&.downcase
+ when /\A--all\z/im
+ ## --all Remove all gems not only bundled gems
+ all = true
+ when /\A--help\z/im
+ ## --help Print this message
+ puts "Usage: #$0 [options] [srcdir]"
+ File.foreach(__FILE__) do |line|
+ line.sub!(/^ *## /, "") or next
+ break if line.chomp!.empty?
+ opt, desc = line.split(/ {2,}/, 2)
+ printf " %-28s %s\n", opt, desc
+ end
+ exit
when /\A-/
raise "#{$0}: unknown option: #{ARGV.first}"
else
break
end
+ ##
ARGV.shift
end
+gem_platform ||= Gem::Platform.new(ruby_platform).to_s if ruby_platform
+
class Removal
+ attr_reader :base
+
def initialize(base = nil)
@base = (File.join(base, "/") if base)
@remove = {}
@@ -64,24 +98,39 @@ class Removal
}
end
+ def sorted
+ @remove.sort_by {|k, | [-k.count("/"), k]}
+ end
+
def each_file
- @remove.each {|k, v| yield prefixed(k) if v == :rm_f}
+ sorted.each {|k, v| yield prefixed(k) if v == :rm_f}
end
def each_directory
- @remove.each {|k, v| yield prefixed(k) if v == :rm_rf}
+ sorted.each {|k, v| yield prefixed(k) if v == :rm_rf}
end
end
srcdir = Removal.new(ARGV.shift)
-curdir = Removal.new
+curdir = !srcdir.base || File.identical?(srcdir.base, ".") ? srcdir : Removal.new
+
+bundled = File.readlines("#{srcdir.base}gems/bundled_gems").
+ grep(/^(\w\S+)\s+\S+(?:\s+\S+\s+(\S+))?/) {$~.captures}.to_h rescue nil
srcdir.glob(".bundle/gems/*/") do |dir|
- unless srcdir.exist?("gems/#{File.basename(dir)}.gem")
+ base = File.basename(dir)
+ next if !all && bundled && !bundled.key?(base[/\A.+(?=-)/])
+ unless srcdir.exist?("gems/#{base}.gem")
srcdir.rmdir(dir)
end
end
+srcdir.glob(".bundle/.timestamp/*.revision") do |file|
+ unless bundled&.fetch(File.basename(file, ".revision"), nil)
+ srcdir.unlink(file)
+ end
+end
+
srcdir.glob(".bundle/specifications/*.gemspec") do |spec|
unless srcdir.directory?(".bundle/gems/#{File.basename(spec, '.gemspec')}/")
srcdir.unlink(spec)
@@ -102,34 +151,40 @@ curdir.glob(".bundle/gems/*/") do |dir|
end
end
-platform = Gem::Platform.local.to_s
curdir.glob(".bundle/{extensions,.timestamp}/*/") do |dir|
- unless File.basename(dir) == platform
+ unless gem_platform and File.fnmatch?(gem_platform, File.basename(dir))
curdir.rmdir(dir)
end
end
-baseruby_version = RbConfig::CONFIG['ruby_version'] # This may not have "-static"
-curdir.glob(".bundle/{extensions,.timestamp}/#{platform}/*/") do |dir|
- version = File.basename(dir).split('-', 2).first # Remove "-static" if exists
- unless version == baseruby_version
- curdir.rmdir(dir)
+if gem_platform
+ curdir.glob(".bundle/{extensions,.timestamp}/#{gem_platform}/*/") do |dir|
+ unless ruby_version and File.fnmatch?(ruby_version, File.basename(dir, '-static'))
+ curdir.rmdir(dir)
+ end
end
end
-curdir.glob(".bundle/extensions/#{platform}/#{baseruby_version}/*/") do |dir|
- unless curdir.exist?(".bundle/specifications/#{File.basename(dir)}.gemspec")
- curdir.rmdir(dir)
+if ruby_version
+ curdir.glob(".bundle/extensions/#{gem_platform || '*'}/#{ruby_version}/*/") do |dir|
+ unless curdir.exist?(".bundle/specifications/#{File.basename(dir)}.gemspec")
+ curdir.rmdir(dir)
+ end
end
-end
-curdir.glob(".bundle/.timestamp/#{platform}/#{baseruby_version}/.*.time") do |stamp|
- unless curdir.directory?(File.join(".bundle", stamp[%r[/\.([^/]+)\.time\z], 1].gsub('.-.', '/')))
- curdir.unlink(stamp)
+ curdir.glob(".bundle/.timestamp/#{gem_platform || '*'}/#{ruby_version}/.*.time") do |stamp|
+ dir = stamp[%r[/\.([^/]+)\.time\z], 1].gsub('.-.', '/')[%r[\A[^/]+/[^/]+]]
+ unless curdir.directory?(File.join(".bundle", dir))
+ curdir.unlink(stamp)
+ end
end
end
-srcdir.each_file {|f| fu.rm_f(f)}
-srcdir.each_directory {|d| fu.rm_rf(d)}
-curdir.each_file {|f| fu.rm_f(f)}
-curdir.each_directory {|d| fu.rm_rf(d)}
+unless only == "curdir"
+ srcdir.each_file {|f| fu.rm_f(f)}
+ srcdir.each_directory {|d| fu.rm_rf(d)}
+end
+unless only == "srcdir" or curdir.equal?(srcdir)
+ curdir.each_file {|f| fu.rm_f(f)}
+ curdir.each_directory {|d| fu.rm_rf(d)}
+end
diff --git a/tool/rakelib/sync_tool.rake b/tool/rakelib/sync_tool.rake
deleted file mode 100644
index 8ea8cb0ad2..0000000000
--- a/tool/rakelib/sync_tool.rake
+++ /dev/null
@@ -1,17 +0,0 @@
-task :sync_tool, [:from] do |t, from: nil|
- from ||= (File.identical?(__dir__, "rakelib") ? "../ruby/tool" : File.dirname(__dir__))
-
- require 'fileutils'
-
- {
- "rakelib/sync_tool.rake" => "rakelib",
- "lib/core_assertions.rb" => "test/lib",
- "lib/envutil.rb" => "test/lib",
- "lib/find_executable.rb" => "test/lib",
- "lib/helper.rb" => "test/lib",
- }.each do |src, dest|
- FileUtils.mkpath(dest)
- FileUtils.cp "#{from}/#{src}", dest
- rescue Errno::ENOENT
- end
-end
diff --git a/tool/rbinstall.rb b/tool/rbinstall.rb
index 90697bfe92..63f4beb943 100755
--- a/tool/rbinstall.rb
+++ b/tool/rbinstall.rb
@@ -28,6 +28,7 @@ begin
rescue LoadError
$" << "zlib.rb"
end
+require_relative 'lib/path'
INDENT = " "*36
STDOUT.sync = true
@@ -96,6 +97,20 @@ def parse_args(argv = ARGV)
opt.on('--gnumake') {gnumake = true}
opt.on('--debug-symbols=SUFFIX', /\w+/) {|name| $debug_symbols = ".#{name}"}
+ unless $install_procs.empty?
+ w = (w = ENV["COLUMNS"] and (w = w.to_i) > 80) ? w - 30 : 50
+ opt.on("\n""Types for --install and --exclude:")
+ mesg = +" "
+ $install_procs.each_key do |t|
+ if mesg.size + t.size > w
+ opt.on(mesg)
+ mesg = +" "
+ end
+ mesg << " " << t.to_s
+ end
+ opt.on(mesg)
+ end
+
opt.order!(argv) do |v|
case v
when /\AINSTALL[-_]([-\w]+)=(.*)/
@@ -205,15 +220,20 @@ def ln_sf(src, dest)
end
$made_dirs = {}
+
+def dir_creating(dir)
+ $made_dirs.fetch(dir) do
+ $made_dirs[dir] = true
+ $installed_list.puts(File.join(dir, "")) if $installed_list
+ yield if defined?(yield)
+ end
+end
+
def makedirs(dirs)
dirs = fu_list(dirs)
dirs.collect! do |dir|
realdir = with_destdir(dir)
- realdir unless $made_dirs.fetch(dir) do
- $made_dirs[dir] = true
- $installed_list.puts(File.join(dir, "")) if $installed_list
- File.directory?(realdir)
- end
+ realdir unless dir_creating(dir) {File.directory?(realdir)}
end.compact!
super(dirs, :mode => $dir_mode) unless dirs.empty?
end
@@ -346,6 +366,13 @@ rubyw_install_name = CONFIG["rubyw_install_name"]
goruby_install_name = "go" + ruby_install_name
bindir = CONFIG["bindir", true]
+if CONFIG["libdirname"] == "archlibdir"
+ libexecdir = MAKEFILE_CONFIG["archlibdir"].dup
+ unless libexecdir.sub!(/\$\(lib\K(?=dir\))/) {"exec"}
+ libexecdir = "$(libexecdir)/$(arch)"
+ end
+ archbindir = RbConfig.expand(libexecdir) + "/bin"
+end
libdir = CONFIG[CONFIG.fetch("libdirname", "libdir"), true]
rubyhdrdir = CONFIG["rubyhdrdir", true]
archhdrdir = CONFIG["rubyarchhdrdir"] || (rubyhdrdir + "/" + CONFIG['arch'])
@@ -369,106 +396,6 @@ load_relative = CONFIG["LIBRUBY_RELATIVE"] == 'yes'
rdoc_noinst = %w[created.rid]
-install?(:local, :arch, :bin, :'bin-arch') do
- prepare "binary commands", bindir
-
- install ruby_install_name+exeext, bindir, :mode => $prog_mode, :strip => $strip
- if rubyw_install_name and !rubyw_install_name.empty?
- install rubyw_install_name+exeext, bindir, :mode => $prog_mode, :strip => $strip
- end
- # emcc produces ruby and ruby.wasm, the first is a JavaScript file of runtime support
- # to load and execute the second .wasm file. Both are required to execute ruby
- if RUBY_PLATFORM =~ /emscripten/ and File.exist? ruby_install_name+".wasm"
- install ruby_install_name+".wasm", bindir, :mode => $prog_mode, :strip => $strip
- end
- if File.exist? goruby_install_name+exeext
- install goruby_install_name+exeext, bindir, :mode => $prog_mode, :strip => $strip
- end
- if enable_shared and dll != lib
- install dll, bindir, :mode => $prog_mode, :strip => $strip
- end
-end
-
-install?(:local, :arch, :lib, :'lib-arch') do
- prepare "base libraries", libdir
-
- install lib, libdir, :mode => $prog_mode, :strip => $strip unless lib == arc
- install arc, libdir, :mode => $data_mode unless CONFIG["INSTALL_STATIC_LIBRARY"] == "no"
- if dll == lib and dll != arc
- for link in CONFIG["LIBRUBY_ALIASES"].split - [File.basename(dll)]
- ln_sf(dll, File.join(libdir, link))
- end
- end
-
- prepare "arch files", archlibdir
- install "rbconfig.rb", archlibdir, :mode => $data_mode
- if CONFIG["ARCHFILE"]
- for file in CONFIG["ARCHFILE"].split
- install file, archlibdir, :mode => $data_mode
- end
- end
-end
-
-install?(:local, :arch, :data) do
- pc = CONFIG["ruby_pc"]
- if pc and File.file?(pc) and File.size?(pc)
- prepare "pkgconfig data", pkgconfigdir = File.join(libdir, "pkgconfig")
- install pc, pkgconfigdir, :mode => $data_mode
- end
-end
-
-install?(:ext, :arch, :'ext-arch') do
- prepare "extension objects", archlibdir
- noinst = %w[-* -*/] | (CONFIG["no_install_files"] || "").split
- install_recursive("#{$extout}/#{CONFIG['arch']}", archlibdir, :no_install => noinst, :mode => $prog_mode, :strip => $strip)
- prepare "extension objects", sitearchlibdir
- prepare "extension objects", vendorarchlibdir
- if extso = File.read("exts.mk")[/^EXTSO[ \t]*=[ \t]*((?:.*\\\n)*.*)/, 1] and
- !(extso = extso.gsub(/\\\n/, '').split).empty?
- libpathenv = CONFIG["LIBPATHENV"]
- dest = CONFIG[!libpathenv || libpathenv == "PATH" ? "bindir" : "libdir"]
- prepare "external libraries", dest
- for file in extso
- install file, dest, :mode => $prog_mode
- end
- end
-end
-install?(:ext, :arch, :hdr, :'arch-hdr', :'hdr-arch') do
- prepare "extension headers", archhdrdir
- install_recursive("#{$extout}/include/#{CONFIG['arch']}", archhdrdir, :glob => "*.h", :mode => $data_mode)
- install_recursive("#{$extout}/include/#{CONFIG['arch']}", archhdrdir, :glob => "rb_rjit_header-*.obj", :mode => $data_mode)
- install_recursive("#{$extout}/include/#{CONFIG['arch']}", archhdrdir, :glob => "rb_rjit_header-*.pch", :mode => $data_mode)
-end
-install?(:ext, :comm, :'ext-comm') do
- prepare "extension scripts", rubylibdir
- install_recursive("#{$extout}/common", rubylibdir, :mode => $data_mode)
- prepare "extension scripts", sitelibdir
- prepare "extension scripts", vendorlibdir
-end
-install?(:ext, :comm, :hdr, :'comm-hdr', :'hdr-comm') do
- hdrdir = rubyhdrdir + "/ruby"
- prepare "extension headers", hdrdir
- install_recursive("#{$extout}/include/ruby", hdrdir, :glob => "*.h", :mode => $data_mode)
-end
-
-install?(:doc, :rdoc) do
- if $rdocdir
- ridatadir = File.join(CONFIG['ridir'], CONFIG['ruby_version'], "system")
- prepare "rdoc", ridatadir
- install_recursive($rdocdir, ridatadir, :no_install => rdoc_noinst, :mode => $data_mode)
- end
-end
-install?(:doc, :html) do
- if $htmldir
- prepare "html-docs", docdir
- install_recursive($htmldir, docdir+"/html", :no_install => rdoc_noinst, :mode => $data_mode)
- end
-end
-install?(:doc, :capi) do
- prepare "capi-docs", docdir
- install_recursive "doc/capi", docdir+"/capi", :mode => $data_mode
-end
-
prolog_script = <<EOS
bindir="#{load_relative ? '${0%/*}' : bindir.gsub(/\"/, '\\\\"')}"
EOS
@@ -584,129 +511,6 @@ $script_installer = Class.new(installer) do
break new(ruby_shebang, ruby_bin, ruby_install_name, nil, trans)
end
-install?(:local, :comm, :bin, :'bin-comm') do
- prepare "command scripts", bindir
-
- install_recursive(File.join(srcdir, "bin"), bindir, :maxdepth => 1) do |src, cmd|
- $script_installer.install(src, cmd)
- end
-end
-
-install?(:local, :comm, :lib) do
- prepare "library scripts", rubylibdir
- noinst = %w[*.txt *.rdoc *.gemspec]
- install_recursive(File.join(srcdir, "lib"), rubylibdir, :no_install => noinst, :mode => $data_mode)
-end
-
-install?(:local, :comm, :hdr, :'comm-hdr') do
- prepare "common headers", rubyhdrdir
-
- noinst = []
- unless RUBY_PLATFORM =~ /mswin|mingw|bccwin/
- noinst << "win32.h"
- end
- noinst = nil if noinst.empty?
- install_recursive(File.join(srcdir, "include"), rubyhdrdir, :no_install => noinst, :glob => "*.{h,hpp}", :mode => $data_mode)
-end
-
-install?(:local, :comm, :man) do
- mdocs = Dir["#{srcdir}/man/*.[1-9]"]
- prepare "manpages", mandir, ([] | mdocs.collect {|mdoc| mdoc[/\d+$/]}).sort.collect {|sec| "man#{sec}"}
-
- case $mantype
- when /\.(?:(gz)|bz2)\z/
- compress = $1 ? "gzip" : "bzip2"
- suffix = $&
- end
- mandir = File.join(mandir, "man")
- has_goruby = File.exist?(goruby_install_name+exeext)
- require File.join(srcdir, "tool/mdoc2man.rb") if /\Adoc\b/ !~ $mantype
- mdocs.each do |mdoc|
- next unless File.file?(mdoc) and File.read(mdoc, 1) == '.'
- base = File.basename(mdoc)
- if base == "goruby.1"
- next unless has_goruby
- end
-
- destdir = mandir + (section = mdoc[/\d+$/])
- destname = ruby_install_name.sub(/ruby/, base.chomp(".#{section}"))
- destfile = File.join(destdir, "#{destname}.#{section}")
-
- if /\Adoc\b/ =~ $mantype
- if compress
- begin
- w = IO.popen(compress, "rb", in: mdoc, &:read)
- rescue
- else
- destfile << suffix
- end
- end
- if w
- open_for_install(destfile, $data_mode) {w}
- else
- install mdoc, destfile, :mode => $data_mode
- end
- else
- class << (w = [])
- alias print push
- end
- if File.basename(mdoc).start_with?('bundle') ||
- File.basename(mdoc).start_with?('gemfile')
- w = File.read(mdoc)
- else
- File.open(mdoc) {|r| Mdoc2Man.mdoc2man(r, w)}
- w = w.join("")
- end
- if compress
- begin
- w = IO.popen(compress, "r+b") do |f|
- Thread.start {f.write w; f.close_write}
- f.read
- end
- rescue
- else
- destfile << suffix
- end
- end
- open_for_install(destfile, $data_mode) {w}
- end
- end
-end
-
-install?(:dbg, :nodefault) do
- prepare "debugger commands", bindir
- prepare "debugger scripts", rubylibdir
- conf = RbConfig::MAKEFILE_CONFIG.merge({"prefix"=>"${prefix#/}"})
- Dir.glob(File.join(srcdir, "template/ruby-*db.in")) do |src|
- cmd = $script_installer.transform(File.basename(src, ".in"))
- open_for_install(File.join(bindir, cmd), $script_mode) {
- RbConfig.expand(File.read(src), conf)
- }
- end
- Dir.glob(File.join(srcdir, "misc/lldb_*")) do |src|
- if File.directory?(src)
- install_recursive src, File.join(rubylibdir, File.basename(src))
- else
- install src, rubylibdir
- end
- end
- install File.join(srcdir, ".gdbinit"), File.join(rubylibdir, "gdbinit")
- if $debug_symbols
- {
- ruby_install_name => bindir,
- rubyw_install_name => bindir,
- goruby_install_name => bindir,
- dll => libdir,
- }.each do |src, dest|
- next if src.empty?
- src += $debug_symbols
- if File.directory?(src)
- install_recursive src, File.join(dest, src)
- end
- end
- end
-end
-
module RbInstall
def self.no_write(options = nil)
u = File.umask(0022)
@@ -745,47 +549,108 @@ module RbInstall
end
def collect
- ruby_libraries.sort
+ requirable_features.sort
+ end
+
+ private
+
+ def features_from_makefile(makefile_path)
+ makefile = File.read(makefile_path)
+
+ name = makefile[/^TARGET[ \t]*=[ \t]*((?:.*\\\n)*.*)/, 1]
+ return [] if name.nil? || name.empty?
+
+ feature = makefile[/^DLLIB[ \t]*=[ \t]*((?:.*\\\n)*.*)/, 1]
+ feature = feature.sub("$(TARGET)", name)
+
+ target_prefix = makefile[/^target_prefix[ \t]*=[ \t]*((?:.*\\\n)*.*)/, 1]
+ feature = File.join(target_prefix.delete_prefix("/"), feature) unless target_prefix.empty?
+
+ Array(feature)
end
class Ext < self
- def skip_install?(files)
+ def requirable_features
# install ext only when it's configured
- !File.exist?("#{$ext_build_dir}/#{relative_base}/Makefile")
+ return [] unless File.exist?(makefile_path)
+
+ ruby_features + ext_features
+ end
+
+ private
+
+ def ruby_features
+ Dir.glob("**/*.rb", base: "#{makefile_dir}/lib")
end
- def ruby_libraries
- Dir.glob("lib/**/*.rb", base: "#{srcdir}/ext/#{relative_base}")
+ def ext_features
+ features_from_makefile(makefile_path)
+ end
+
+ def makefile_path
+ if File.exist?("#{makefile_dir}/Makefile")
+ "#{makefile_dir}/Makefile"
+ else
+ # for out-of-place build
+ "#{$ext_build_dir}/#{relative_base}/Makefile"
+ end
+ end
+
+ def makefile_dir
+ "#{root}/#{relative_base}"
+ end
+
+ def root
+ File.expand_path($ext_build_dir, srcdir)
end
end
class Lib < self
- def skip_install?(files)
- files.empty?
+ def requirable_features
+ ruby_features + ext_features
end
- def ruby_libraries
+ private
+
+ def ruby_features
gemname = File.basename(gemspec, ".gemspec")
base = relative_base || gemname
# for lib/net/net-smtp.gemspec
if m = /.*(?=-(.*)\z)/.match(gemname)
base = File.join(base, *m.to_a.select {|n| !base.include?(n)})
end
- files = Dir.glob("lib/#{base}{.rb,/**/*.rb}", base: srcdir)
+ files = Dir.glob("#{base}{.rb,/**/*.rb}", base: root)
if !relative_base and files.empty? # no files at the toplevel
# pseudo gem like ruby2_keywords
- files << "lib/#{gemname}.rb"
+ files << "#{gemname}.rb"
end
case gemname
when "net-http"
- files << "lib/net/https.rb"
+ files << "net/https.rb"
when "optparse"
- files << "lib/optionparser.rb"
+ files << "optionparser.rb"
end
files
end
+
+ def ext_features
+ loaded_gemspec = load_gemspec("#{root}/#{gemspec}")
+ extension = loaded_gemspec.extensions.first
+ return [] unless extension
+
+ extconf = File.expand_path(extension, srcdir)
+ ext_build_dir = File.dirname(extconf)
+ makefile_path = "#{ext_build_dir}/Makefile"
+ return [] unless File.exist?(makefile_path)
+
+ features_from_makefile(makefile_path)
+ end
+
+ def root
+ "#{srcdir}/lib"
+ end
end
end
end
@@ -823,10 +688,7 @@ module RbInstall
end
end
- class GemInstaller < Gem::Installer
- end
-
- class UnpackedInstaller < GemInstaller
+ class UnpackedInstaller < Gem::Installer
def write_cache_file
end
@@ -850,11 +712,6 @@ module RbInstall
super
end
- def generate_bin_script(filename, bindir)
- return if same_bin_script?(filename, bindir)
- super
- end
-
def same_bin_script?(filename, bindir)
path = File.join(bindir, formatted_program_filename(filename))
begin
@@ -873,11 +730,10 @@ module RbInstall
super unless $dryrun
$installed_list.puts(without_destdir(default_spec_file)) if $installed_list
end
- end
- class GemInstaller
def install
spec.post_install_message = nil
+ dir_creating(without_destdir(gem_dir))
RbInstall.no_write(options) {super}
end
@@ -886,6 +742,7 @@ module RbInstall
end
def generate_bin_script(filename, bindir)
+ return if same_bin_script?(filename, bindir)
name = formatted_program_filename(filename)
unless $dryrun
super
@@ -907,29 +764,23 @@ module RbInstall
end
end
-# :startdoc:
-
-install?(:ext, :comm, :gem, :'default-gems', :'default-gems-comm') do
- install_default_gem('lib', srcdir, bindir)
-end
-install?(:ext, :arch, :gem, :'default-gems', :'default-gems-arch') do
- install_default_gem('ext', srcdir, bindir)
-end
-
def load_gemspec(file, base = nil)
file = File.realpath(file)
code = File.read(file, encoding: "utf-8:-")
+
+ files = []
+ Dir.glob("**/*", File::FNM_DOTMATCH, base: base) do |n|
+ case File.basename(n); when ".", ".."; next; end
+ next if File.directory?(File.join(base, n))
+ files << n.dump
+ end if base
code.gsub!(/(?:`git[^\`]*`|%x\[git[^\]]*\])\.split\([^\)]*\)/m) do
- files = []
- if base
- Dir.glob("**/*", File::FNM_DOTMATCH, base: base) do |n|
- case File.basename(n); when ".", ".."; next; end
- next if File.directory?(File.join(base, n))
- files << n.dump
- end
- end
"[" + files.join(", ") + "]"
end
+ code.gsub!(/IO\.popen\(.*git.*?\)/) do
+ "[" + files.join(", ") + "] || itself"
+ end
+
spec = eval(code, binding, file)
unless Gem::Specification === spec
raise TypeError, "[#{file}] isn't a Gem::Specification (#{spec.class} instead)."
@@ -967,7 +818,7 @@ def install_default_gem(dir, srcdir, bindir)
spec = load_gemspec("#{base}/#{src}")
file_collector = RbInstall::Specs::FileCollector.for(srcdir, dir, src)
files = file_collector.collect
- if file_collector.skip_install?(files)
+ if files.empty?
next
end
spec.files = files
@@ -990,6 +841,261 @@ def install_default_gem(dir, srcdir, bindir)
end
end
+# :startdoc:
+
+install?(:local, :arch, :bin, :'bin-arch') do
+ prepare "binary commands", (dest = archbindir || bindir)
+
+ def (bins = []).add(name)
+ push(name)
+ name
+ end
+
+ install bins.add(ruby_install_name+exeext), dest, :mode => $prog_mode, :strip => $strip
+ if rubyw_install_name and !rubyw_install_name.empty?
+ install bins.add(rubyw_install_name+exeext), dest, :mode => $prog_mode, :strip => $strip
+ end
+ # emcc produces ruby and ruby.wasm, the first is a JavaScript file of runtime support
+ # to load and execute the second .wasm file. Both are required to execute ruby
+ if RUBY_PLATFORM =~ /emscripten/ and File.exist? ruby_install_name+".wasm"
+ install bins.add(ruby_install_name+".wasm"), dest, :mode => $prog_mode, :strip => $strip
+ end
+ if File.exist? goruby_install_name+exeext
+ install bins.add(goruby_install_name+exeext), dest, :mode => $prog_mode, :strip => $strip
+ end
+ if enable_shared and dll != lib
+ install bins.add(dll), dest, :mode => $prog_mode, :strip => $strip
+ end
+ if archbindir
+ prepare "binary command links", bindir
+ relpath = Path.relative(archbindir, bindir)
+ bins.each do |f|
+ ln_sf(File.join(relpath, f), File.join(bindir, f))
+ end
+ end
+end
+
+install?(:local, :arch, :lib, :'lib-arch') do
+ prepare "base libraries", libdir
+
+ install lib, libdir, :mode => $prog_mode, :strip => $strip unless lib == arc
+ install arc, libdir, :mode => $data_mode unless CONFIG["INSTALL_STATIC_LIBRARY"] == "no"
+ if dll == lib and dll != arc
+ for link in CONFIG["LIBRUBY_ALIASES"].split - [File.basename(dll)]
+ ln_sf(dll, File.join(libdir, link))
+ end
+ end
+
+ prepare "arch files", archlibdir
+ install "rbconfig.rb", archlibdir, :mode => $data_mode
+ if CONFIG["ARCHFILE"]
+ for file in CONFIG["ARCHFILE"].split
+ install file, archlibdir, :mode => $data_mode
+ end
+ end
+end
+
+install?(:local, :arch, :data) do
+ pc = CONFIG["ruby_pc"]
+ if pc and File.file?(pc) and File.size?(pc)
+ prepare "pkgconfig data", pkgconfigdir = File.join(libdir, "pkgconfig")
+ install pc, pkgconfigdir, :mode => $data_mode
+ if (pkgconfig_base = CONFIG["libdir", true]) != libdir
+ prepare "pkgconfig data link", File.join(pkgconfig_base, "pkgconfig")
+ ln_sf(File.join("..", Path.relative(pkgconfigdir, pkgconfig_base), pc),
+ File.join(pkgconfig_base, "pkgconfig", pc))
+ end
+ end
+end
+
+install?(:ext, :arch, :'ext-arch') do
+ prepare "extension objects", archlibdir
+ noinst = %w[-* -*/] | (CONFIG["no_install_files"] || "").split
+ install_recursive("#{$extout}/#{CONFIG['arch']}", archlibdir, :no_install => noinst, :mode => $prog_mode, :strip => $strip)
+ prepare "extension objects", sitearchlibdir
+ prepare "extension objects", vendorarchlibdir
+ if extso = File.read("exts.mk")[/^EXTSO[ \t]*=[ \t]*((?:.*\\\n)*.*)/, 1] and
+ !(extso = extso.gsub(/\\\n/, '').split).empty?
+ libpathenv = CONFIG["LIBPATHENV"]
+ dest = CONFIG[!libpathenv || libpathenv == "PATH" ? "bindir" : "libdir"]
+ prepare "external libraries", dest
+ for file in extso
+ install file, dest, :mode => $prog_mode
+ end
+ end
+end
+
+install?(:ext, :arch, :hdr, :'arch-hdr', :'hdr-arch') do
+ prepare "extension headers", archhdrdir
+ install_recursive("#{$extout}/include/#{CONFIG['arch']}", archhdrdir, :glob => "*.h", :mode => $data_mode)
+ install_recursive("#{$extout}/include/#{CONFIG['arch']}", archhdrdir, :glob => "rb_rjit_header-*.obj", :mode => $data_mode)
+ install_recursive("#{$extout}/include/#{CONFIG['arch']}", archhdrdir, :glob => "rb_rjit_header-*.pch", :mode => $data_mode)
+end
+
+install?(:ext, :comm, :'ext-comm') do
+ prepare "extension scripts", rubylibdir
+ install_recursive("#{$extout}/common", rubylibdir, :mode => $data_mode)
+ prepare "extension scripts", sitelibdir
+ prepare "extension scripts", vendorlibdir
+end
+
+install?(:ext, :comm, :hdr, :'comm-hdr', :'hdr-comm') do
+ hdrdir = rubyhdrdir + "/ruby"
+ prepare "extension headers", hdrdir
+ install_recursive("#{$extout}/include/ruby", hdrdir, :glob => "*.h", :mode => $data_mode)
+end
+
+install?(:doc, :rdoc) do
+ if $rdocdir
+ ridatadir = File.join(CONFIG['ridir'], CONFIG['ruby_version'], "system")
+ prepare "rdoc", ridatadir
+ install_recursive($rdocdir, ridatadir, :no_install => rdoc_noinst, :mode => $data_mode)
+ end
+end
+
+install?(:doc, :html) do
+ if $htmldir
+ prepare "html-docs", docdir
+ install_recursive($htmldir, docdir+"/html", :no_install => rdoc_noinst, :mode => $data_mode)
+ end
+end
+
+install?(:doc, :capi) do
+ prepare "capi-docs", docdir
+ install_recursive "doc/capi", docdir+"/capi", :mode => $data_mode
+end
+
+install?(:local, :comm, :bin, :'bin-comm') do
+ prepare "command scripts", bindir
+
+ install_recursive(File.join(srcdir, "bin"), bindir, :maxdepth => 1) do |src, cmd|
+ $script_installer.install(src, cmd)
+ end
+end
+
+install?(:local, :comm, :lib) do
+ prepare "library scripts", rubylibdir
+ noinst = %w[*.txt *.rdoc *.gemspec]
+ install_recursive(File.join(srcdir, "lib"), rubylibdir, :no_install => noinst, :mode => $data_mode)
+end
+
+install?(:local, :comm, :hdr, :'comm-hdr') do
+ prepare "common headers", rubyhdrdir
+
+ noinst = []
+ unless RUBY_PLATFORM =~ /mswin|mingw|bccwin/
+ noinst << "win32.h"
+ end
+ noinst = nil if noinst.empty?
+ install_recursive(File.join(srcdir, "include"), rubyhdrdir, :no_install => noinst, :glob => "*.{h,hpp}", :mode => $data_mode)
+end
+
+install?(:local, :comm, :man) do
+ mdocs = Dir["#{srcdir}/man/*.[1-9]"]
+ prepare "manpages", mandir, ([] | mdocs.collect {|mdoc| mdoc[/\d+$/]}).sort.collect {|sec| "man#{sec}"}
+
+ case $mantype
+ when /\.(?:(gz)|bz2)\z/
+ compress = $1 ? "gzip" : "bzip2"
+ suffix = $&
+ end
+ mandir = File.join(mandir, "man")
+ has_goruby = File.exist?(goruby_install_name+exeext)
+ require File.join(srcdir, "tool/mdoc2man.rb") if /\Adoc\b/ !~ $mantype
+ mdocs.each do |mdoc|
+ next unless File.file?(mdoc) and File.read(mdoc, 1) == '.'
+ base = File.basename(mdoc)
+ if base == "goruby.1"
+ next unless has_goruby
+ end
+
+ destdir = mandir + (section = mdoc[/\d+$/])
+ destname = ruby_install_name.sub(/ruby/, base.chomp(".#{section}"))
+ destfile = File.join(destdir, "#{destname}.#{section}")
+
+ if /\Adoc\b/ =~ $mantype
+ if compress
+ begin
+ w = IO.popen(compress, "rb", in: mdoc, &:read)
+ rescue
+ else
+ destfile << suffix
+ end
+ end
+ if w
+ open_for_install(destfile, $data_mode) {w}
+ else
+ install mdoc, destfile, :mode => $data_mode
+ end
+ else
+ class << (w = [])
+ alias print push
+ end
+ if File.basename(mdoc).start_with?('bundle') ||
+ File.basename(mdoc).start_with?('gemfile')
+ w = File.read(mdoc)
+ else
+ File.open(mdoc) {|r| Mdoc2Man.mdoc2man(r, w)}
+ w = w.join("")
+ end
+ if compress
+ begin
+ w = IO.popen(compress, "r+b") do |f|
+ Thread.start {f.write w; f.close_write}
+ f.read
+ end
+ rescue
+ else
+ destfile << suffix
+ end
+ end
+ open_for_install(destfile, $data_mode) {w}
+ end
+ end
+end
+
+install?(:dbg, :nodefault) do
+ prepare "debugger commands", bindir
+ prepare "debugger scripts", rubylibdir
+ conf = MAKEFILE_CONFIG.merge({"prefix"=>"${prefix#/}"})
+ Dir.glob(File.join(srcdir, "template/ruby-*db.in")) do |src|
+ cmd = $script_installer.transform(File.basename(src, ".in"))
+ open_for_install(File.join(bindir, cmd), $script_mode) {
+ RbConfig.expand(File.read(src), conf)
+ }
+ end
+ Dir.glob(File.join(srcdir, "misc/lldb_*")) do |src|
+ if File.directory?(src)
+ install_recursive src, File.join(rubylibdir, File.basename(src))
+ else
+ install src, rubylibdir
+ end
+ end
+ install File.join(srcdir, ".gdbinit"), File.join(rubylibdir, "gdbinit")
+ if $debug_symbols
+ {
+ ruby_install_name => archbindir || bindir,
+ rubyw_install_name => archbindir || bindir,
+ goruby_install_name => archbindir || bindir,
+ dll => libdir,
+ }.each do |src, dest|
+ next if src.empty?
+ src += $debug_symbols
+ if File.directory?(src)
+ install_recursive src, File.join(dest, src)
+ end
+ end
+ end
+end
+
+install?(:ext, :comm, :gem, :'default-gems', :'default-gems-comm') do
+ install_default_gem('lib', srcdir, bindir)
+end
+
+install?(:ext, :arch, :gem, :'default-gems', :'default-gems-arch') do
+ install_default_gem('ext', srcdir, bindir)
+end
+
install?(:ext, :comm, :gem, :'bundled-gems') do
gem_dir = Gem.default_dir
install_dir = with_destdir(gem_dir)
@@ -1026,16 +1132,22 @@ install?(:ext, :comm, :gem, :'bundled-gems') do
File.foreach("#{srcdir}/gems/bundled_gems") do |name|
next if /^\s*(?:#|$)/ =~ name
next unless /^(\S+)\s+(\S+).*/ =~ name
+ gem = $1
gem_name = "#$1-#$2"
- # Try to find the gemspec file for C ext gems
- # ex .bundle/gems/debug-1.7.1/debug-1.7.1.gemspec
- # This gemspec keep the original dependencies
- path = "#{srcdir}/.bundle/gems/#{gem_name}/#{gem_name}.gemspec"
+ # Try to find the original gemspec file
+ path = "#{srcdir}/.bundle/gems/#{gem_name}/#{gem}.gemspec"
unless File.exist?(path)
- path = "#{srcdir}/.bundle/specifications/#{gem_name}.gemspec"
+ # Try to find the gemspec file for C ext gems
+ # ex .bundle/gems/debug-1.7.1/debug-1.7.1.gemspec
+ # This gemspec keep the original dependencies
+ path = "#{srcdir}/.bundle/gems/#{gem_name}/#{gem_name}.gemspec"
unless File.exist?(path)
- skipped[gem_name] = "gemspec not found"
- next
+ # Try to find the gemspec file for gems that hasn't own gemspec
+ path = "#{srcdir}/.bundle/specifications/#{gem_name}.gemspec"
+ unless File.exist?(path)
+ skipped[gem_name] = "gemspec not found"
+ next
+ end
end
end
spec = load_gemspec(path, "#{srcdir}/.bundle/gems/#{gem_name}")
@@ -1047,6 +1159,11 @@ install?(:ext, :comm, :gem, :'bundled-gems') do
skipped[gem_name] = "full name unmatch #{spec.full_name}"
next
end
+ # Skip install C ext bundled gem if it is build failed or not found
+ if !spec.extensions.empty? && !File.exist?("#{build_dir}/#{gem_name}/gem.build_complete")
+ skipped[gem_name] = "extensions not found or build failed #{spec.full_name}"
+ next
+ end
spec.extension_dir = "#{extensions_dir}/#{spec.full_name}"
package = RbInstall::DirPackage.new spec
ins = RbInstall::UnpackedInstaller.new(package, options)
diff --git a/tool/rbs_skip_tests b/tool/rbs_skip_tests
index 6adef0059b..56a864d193 100644
--- a/tool/rbs_skip_tests
+++ b/tool/rbs_skip_tests
@@ -21,9 +21,38 @@ test_collection_install(RBS::CliTest) running tests without Bundler
test_collection_install_frozen(RBS::CliTest) running tests without Bundler
test_collection_install_gemspec(RBS::CliTest) running tests without Bundler
test_collection_update(RBS::CliTest) running tests without Bundler
-test_subtract(RBS::CliTest) running tests without Bundler
-test_subtract_several_subtrahends(RBS::CliTest) running tests without Bundler
-test_subtract_write(RBS::CliTest) running tests without Bundler
+
+test_loading_from_rbs_collection__gem_version_mismatch(RBS::EnvironmentLoaderTest) running test without rbs-amber testing gem
+
+test_defs(RBS::RbPrototypeTest) Numeric Nodes are added
+test_defs_return_type(RBS::RbPrototypeTest) Numeric Nodes are added
+test_defs_return_type_with_block(RBS::RbPrototypeTest) Numeric Nodes are added
+test_defs_return_type_with_if(RBS::RbPrototypeTest) Numeric Nodes are added
+test_endless_method_definition(RBS::RbPrototypeTest) Numeric Nodes are added
+test_literal_to_type(RBS::RbPrototypeTest) Numeric Nodes are added
+test_literal_types(RBS::RbPrototypeTest) Numeric Nodes are added
+test_accessibility(RBS::RbPrototypeTest) Symbol Node is added
+test_aliases(RBS::RbPrototypeTest) Symbol Node is added
+test_comments(RBS::RbPrototypeTest) Symbol Node is added
+test_const(RBS::RbPrototypeTest) Symbol Node is added
+test_meta_programming(RBS::RbPrototypeTest) Symbol Node is added
+test_module_function(RBS::RbPrototypeTest) Symbol Node is added
+test_all(RBS::RbiPrototypeTest) Symbol Node is added
+test_block_args(RBS::RbiPrototypeTest) Symbol Node is added
+test_implicit_block(RBS::RbiPrototypeTest) Symbol Node is added
+test_non_parameter_type_member(RBS::RbiPrototypeTest) Symbol Node is added
+test_noreturn(RBS::RbiPrototypeTest) Symbol Node is added
+test_optional_block(RBS::RbiPrototypeTest) Symbol Node is added
+test_overloading(RBS::RbiPrototypeTest) Symbol Node is added
+test_parameter(RBS::RbiPrototypeTest) Symbol Node is added
+test_parameter_type_member_variance(RBS::RbiPrototypeTest) Symbol Node is added
+test_tuple(RBS::RbiPrototypeTest) Symbol Node is added
+test_untyped_block(RBS::RbiPrototypeTest) Symbol Node is added
+test_argument_forwarding(RBS::RbPrototypeTest) `...` args handling is changed
+
+test_TOPDIR(RbConfigSingletonTest) `TOPDIR` is `nil` during CI while RBS type is declared as `String`
+
+test_aref(FiberSingletonTest) the method should not accept String keys
NetSingletonTest depending on external resources
NetInstanceTest depending on external resources
diff --git a/tool/rbuninstall.rb b/tool/rbuninstall.rb
index f0c286012c..60f5241a4f 100755
--- a/tool/rbuninstall.rb
+++ b/tool/rbuninstall.rb
@@ -21,15 +21,33 @@ BEGIN {
end
$dirs = []
$files = []
+ COLUMNS = $tty && (ENV["COLUMNS"]&.to_i || begin require 'io/console/size'; rescue; else IO.console_size&.at(1); end)&.then do |n|
+ n-1 if n > 1
+ end
+ if COLUMNS
+ $column = 0
+ def message(str = nil)
+ $stdout.print "\b \b" * $column
+ if str
+ if str.size > COLUMNS
+ str = "..." + str[(-COLUMNS+3)..-1]
+ end
+ $stdout.print str
+ end
+ $stdout.flush
+ $column = str&.size || 0
+ end
+ else
+ alias message puts
+ end
}
list = ($_.chomp!('/') ? $dirs : $files)
list << $_
END {
status = true
$\ = nil
- ors = (!$dryrun and $tty) ? "\e[K\r" : "\n"
$files.each do |file|
- print "rm #{file}#{ors}"
+ message "rm #{file}"
unless $dryrun
file = File.join($destdir, file) if $destdir
begin
@@ -45,9 +63,10 @@ END {
$dirs.each do |dir|
unlink[dir] = true
end
+ nonempty = {}
while dir = $dirs.pop
dir = File.dirname(dir) while File.basename(dir) == '.'
- print "rmdir #{dir}#{ors}"
+ message "rmdir #{dir}"
unless $dryrun
realdir = $destdir ? File.join($destdir, dir) : dir
begin
@@ -58,16 +77,23 @@ END {
raise unless File.symlink?(realdir)
File.unlink(realdir)
end
- rescue Errno::ENOENT, Errno::ENOTEMPTY
+ rescue Errno::ENOTEMPTY
+ nonempty[dir] = true
+ rescue Errno::ENOENT
rescue
status = false
puts $!
else
+ nonempty.delete(dir)
parent = File.dirname(dir)
$dirs.push(parent) unless parent == dir or unlink[parent]
end
end
end
- print ors.chomp
+ message
+ unless nonempty.empty?
+ puts "Non empty director#{nonempty.size == 1 ? 'y' : 'ies'}:"
+ nonempty.each_key {|dir| print " #{dir}\n"}
+ end
exit(status)
}
diff --git a/tool/rdoc-srcdir b/tool/rdoc-srcdir
new file mode 100755
index 0000000000..10c63caf9e
--- /dev/null
+++ b/tool/rdoc-srcdir
@@ -0,0 +1,20 @@
+#!ruby
+
+require 'rdoc/rdoc'
+
+# Make only the output directory relative to the invoked directory.
+invoked = Dir.pwd
+
+# Load options and parse files from srcdir.
+Dir.chdir(File.dirname(__dir__))
+
+options = RDoc::Options.load_options
+options.parse ARGV
+
+options.singleton_class.define_method(:finish) do
+ super()
+ @op_dir = File.expand_path(@op_dir, invoked)
+end
+
+# Do not hide errors when generating documents of Ruby itself.
+RDoc::RDoc.new.document options
diff --git a/tool/rjit/bindgen.rb b/tool/rjit/bindgen.rb
index 1dd3b78bd5..fb6653ed9c 100755
--- a/tool/rjit/bindgen.rb
+++ b/tool/rjit/bindgen.rb
@@ -141,12 +141,10 @@ class BindingGenerator
# Define variables
@values.each do |type, values|
values.each do |value|
- println " def C.#{value}"
- println " Primitive.cexpr! %q{ #{type}2NUM(#{value}) }"
- println " end"
- println
+ println " def C.#{value} = Primitive.cexpr!(%q{ #{type}2NUM(#{value}) })"
end
end
+ println
# Define function pointers
@funcs.each do |func|
@@ -182,8 +180,12 @@ class BindingGenerator
unless generate_node(nodes_index[type])&.start_with?('CType::Immediate')
raise "Non-immediate type is given to dynamic_types: #{type}"
end
+ # Only one Primitive.cexpr! is allowed for each line: https://github.com/ruby/ruby/pull/9612
println " def C.#{type}"
- println " @#{type} ||= CType::Immediate.find(Primitive.cexpr!(\"SIZEOF(#{type})\"), Primitive.cexpr!(\"SIGNED_TYPE_P(#{type})\"))"
+ println " @#{type} ||= CType::Immediate.find("
+ println " Primitive.cexpr!(\"SIZEOF(#{type})\"),"
+ println " Primitive.cexpr!(\"SIGNED_TYPE_P(#{type})\"),"
+ println " )"
println " end"
println
end
@@ -244,7 +246,6 @@ class BindingGenerator
to_ruby = @ruby_fields.fetch(node.spelling, []).include?(field)
if child.bitwidth > 0
if bit_fields_end <= i # give up offsetof calculation for non-leading bit fields
- binding.irb
raise "non-leading bit fields are not supported. consider including '#{field}' in skip_fields."
end
offsetof = node.offsetof.fetch(field)
@@ -297,7 +298,7 @@ class BindingGenerator
# @param type [String]
def generate_type(type)
if type.match?(/\[\d+\]\z/)
- return "CType::Pointer.new { #{generate_type(type.sub!(/\[\d+\]\z/, ''))} }"
+ return "CType::Array.new { #{generate_type(type.sub!(/\[\d+\]\z/, ''))} }"
end
type = type.delete_suffix('const')
if type.end_with?('*')
@@ -397,7 +398,6 @@ generator = BindingGenerator.new(
BOP_OR
BOP_PLUS
BUILTIN_ATTR_LEAF
- BUILTIN_ATTR_NO_GC
HASH_REDEFINED_OP_FLAG
INTEGER_REDEFINED_OP_FLAG
INVALID_SHAPE_ID
@@ -423,7 +423,6 @@ generator = BindingGenerator.new(
RUBY_FIXNUM_FLAG
RUBY_FLONUM_FLAG
RUBY_FLONUM_MASK
- RUBY_FL_SINGLETON
RUBY_IMMEDIATE_MASK
RUBY_SPECIAL_SHIFT
RUBY_SYMBOL_FLAG
@@ -436,11 +435,9 @@ generator = BindingGenerator.new(
RUBY_T_STRING
RUBY_T_SYMBOL
RUBY_T_OBJECT
- SHAPE_CAPACITY_CHANGE
SHAPE_FLAG_SHIFT
SHAPE_FROZEN
SHAPE_ID_NUM_BITS
- SHAPE_INITIAL_CAPACITY
SHAPE_IVAR
SHAPE_MASK
SHAPE_ROOT
@@ -504,6 +501,7 @@ generator = BindingGenerator.new(
rb_cTrueClass
rb_rjit_global_events
rb_mRubyVMFrozenCore
+ rb_vm_insns_count
idRespond_to_missing
],
},
@@ -526,6 +524,7 @@ generator = BindingGenerator.new(
rb_hash_bulk_insert
rb_hash_new
rb_hash_new_with_size
+ rb_hash_resurrect
rb_ivar_get
rb_obj_as_string_result
rb_obj_is_kind_of
@@ -602,6 +601,7 @@ generator = BindingGenerator.new(
rb_callcache
rb_callinfo
rb_captured_block
+ rb_cfunc_t
rb_control_frame_t
rb_cref_t
rb_execution_context_struct
@@ -637,7 +637,7 @@ generator = BindingGenerator.new(
skip_fields: {
'rb_execution_context_struct.machine': %w[regs], # differs between macOS and Linux
rb_execution_context_struct: %w[method_missing_reason], # non-leading bit fields not supported
- rb_iseq_constant_body: %w[yjit_payload], # conditionally defined
+ rb_iseq_constant_body: %w[jit_exception jit_exception_calls yjit_payload yjit_calls_at_interv], # conditionally defined
rb_thread_struct: %w[status has_dedicated_nt to_kill abort_on_exception report_on_exception pending_interrupt_queue_checked],
:'' => %w[is_from_method is_lambda is_isolated], # rb_proc_t
},
diff --git a/tool/ruby_vm/helpers/c_escape.rb b/tool/ruby_vm/helpers/c_escape.rb
index 34fafd1e34..2a99e408da 100644
--- a/tool/ruby_vm/helpers/c_escape.rb
+++ b/tool/ruby_vm/helpers/c_escape.rb
@@ -17,7 +17,10 @@ module RubyVM::CEscape
# generate comment, with escaps.
def commentify str
- return "/* #{str.b.gsub('*/', '*\\/').gsub('/*', '/\\*')} */"
+ unless str = str.dump[/\A"\K.*(?="\z)/]
+ raise Encoding::CompatibilityError, "must be ASCII-compatible (#{str.encoding})"
+ end
+ return "/* #{str.gsub('*/', '*\\/').gsub('/*', '/\\*')} */"
end
# Mimic gensym of CL.
diff --git a/tool/ruby_vm/helpers/dumper.rb b/tool/ruby_vm/helpers/dumper.rb
index c083dffa7a..8a04041da9 100644
--- a/tool/ruby_vm/helpers/dumper.rb
+++ b/tool/ruby_vm/helpers/dumper.rb
@@ -33,11 +33,7 @@ class RubyVM::Dumper
rescue Errno::ENOENT
raise "don't know how to generate #{path}"
else
- if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
- erb = ERB.new(src, trim_mode: '%-')
- else
- erb = ERB.new(src, nil, '%-')
- end
+ erb = ERB.new(src, trim_mode: '%-')
erb.filename = path.to_path
return erb
end
diff --git a/tool/ruby_vm/views/_insn_entry.erb b/tool/ruby_vm/views/_insn_entry.erb
index 32070c5f34..6ec33461c4 100644
--- a/tool/ruby_vm/views/_insn_entry.erb
+++ b/tool/ruby_vm/views/_insn_entry.erb
@@ -24,7 +24,7 @@ INSN_ENTRY(<%= insn.name %>)
<%= ope[:decl] %> = (<%= ope[:type] %>)GET_OPERAND(<%= i + 1 %>);
% end
# define INSN_ATTR(x) <%= insn.call_attribute(' ## x ## ') %>
- const bool leaf = INSN_ATTR(leaf);
+ const bool MAYBE_UNUSED(leaf) = INSN_ATTR(leaf);
% insn.pops.reverse_each.with_index.reverse_each do |pop, i|
<%= pop[:decl] %> = <%= insn.cast_from_VALUE pop, "TOPN(#{i})"%>;
% end
@@ -35,7 +35,7 @@ INSN_ENTRY(<%= insn.name %>)
% end
/* ### Instruction preambles. ### */
- if (! leaf) ADD_PC(INSN_ATTR(width));
+ ADD_PC(INSN_ATTR(width));
% if insn.handles_sp?
POPN(INSN_ATTR(popn));
% end
@@ -68,7 +68,6 @@ INSN_ENTRY(<%= insn.name %>)
VM_ASSERT(!RB_TYPE_P(TOPN(<%= i %>), T_MOVED));
% end
% end
- if (leaf) ADD_PC(INSN_ATTR(width));
# undef INSN_ATTR
/* ### Leave the instruction. ### */
diff --git a/tool/ruby_vm/views/_leaf_helpers.erb b/tool/ruby_vm/views/_leaf_helpers.erb
index ac35df64f4..f740107a0a 100644
--- a/tool/ruby_vm/views/_leaf_helpers.erb
+++ b/tool/ruby_vm/views/_leaf_helpers.erb
@@ -10,31 +10,6 @@
#include "iseq.h"
-extern const bool rb_vm_insn_leaf_p[];
-
-#ifdef RUBY_VM_INSNS_INFO
-const bool rb_vm_insn_leaf_p[] = {
-% RubyVM::Instructions.each_slice(20) do |insns|
- <%= insns.map do |insn|
- if insn.is_a?(RubyVM::BareInstructions)
- insn.always_leaf? ? '1' : '0'
- else
- '0'
- end
- end.join(', ')
- %>,
-% end
-};
-#endif
-
-CONSTFUNC(MAYBE_UNUSED(static bool insn_leaf_p(VALUE insn)));
-
-bool
-insn_leaf_p(VALUE insn)
-{
- return rb_vm_insn_leaf_p[insn];
-}
-
// This is used to tell RJIT that this insn would be leaf if CHECK_INTS didn't exist.
// It should be used only when RUBY_VM_CHECK_INTS is directly written in insns.def.
static bool leafness_of_check_ints = false;
diff --git a/tool/ruby_vm/views/opt_sc.inc.erb b/tool/ruby_vm/views/opt_sc.inc.erb
deleted file mode 100644
index e58c81989f..0000000000
--- a/tool/ruby_vm/views/opt_sc.inc.erb
+++ /dev/null
@@ -1,40 +0,0 @@
-/* -*- C -*- */
-
-%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
-%#
-%# This file is a part of the programming language Ruby. Permission is hereby
-%# granted, to either redistribute and/or modify this file, provided that the
-%# conditions mentioned in the file COPYING are met. Consult the file for
-%# details.
-% raise ':FIXME:TBW' if RubyVM::VmOptsH['STACK_CACHING']
-<%= render 'copyright' %>
-<%= render 'notice', locals: {
- this_file: 'is for threaded code',
- edit: __FILE__,
-} -%>
-
-#define SC_STATE_SIZE 6
-
-#define SCS_XX 1
-#define SCS_AX 2
-#define SCS_BX 3
-#define SCS_AB 4
-#define SCS_BA 5
-
-#define SC_ERROR 0xffffffff
-
-static const VALUE sc_insn_info[][SC_STATE_SIZE] = {
-#define NO_SC { SC_ERROR, SC_ERROR, SC_ERROR, SC_ERROR, SC_ERROR, SC_ERROR }
-% RubyVM::Instructions.each_slice 8 do |a|
- <%= a.map{|i| 'NO_SC' }.join(', ') %>,
-% end
-#undef NO_SC
-};
-
-static const VALUE sc_insn_next[] = {
-% RubyVM::Instructions.each_slice 8 do |a|
- <%= a.map{|i| 'SCS_XX' }.join(', ') %>,
-% end
-};
-
-ASSERT_VM_INSTRUCTION_SIZE(sc_insn_next);
diff --git a/tool/rubyspec_temp.rb b/tool/rubyspec_temp.rb
deleted file mode 100644
index 339bfce211..0000000000
--- a/tool/rubyspec_temp.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require "tmpdir"
-require "fileutils"
-
-if (tmpdir = Dir.mktmpdir("rubyspec_temp.")).size > 80
- # On macOS, the default TMPDIR is very long, inspite of UNIX socket
- # path length is limited.
- Dir.rmdir(tmpdir)
- tmpdir = Dir.mktmpdir("rubyspec_temp.", "/tmp")
-end
-# warn "tmpdir(#{tmpdir.size}) = #{tmpdir}"
-END {FileUtils.rm_rf(tmpdir)}
-
-ENV["TMPDIR"] = ENV["SPEC_TEMP_DIR"] = tmpdir
diff --git a/tool/runruby.rb b/tool/runruby.rb
index 1efe38fd13..ec63d1008a 100755
--- a/tool/runruby.rb
+++ b/tool/runruby.rb
@@ -11,6 +11,8 @@ when ENV['RUNRUBY_USE_GDB'] == 'true'
debugger = :gdb
when ENV['RUNRUBY_USE_LLDB'] == 'true'
debugger = :lldb
+when ENV['RUNRUBY_USE_RR'] == 'true'
+ debugger = :rr
when ENV['RUNRUBY_YJIT_STATS']
use_yjit_stat = true
end
@@ -134,12 +136,15 @@ if File.file?(libruby_so)
env[e] = [abs_archdir, ENV[e]].compact.join(File::PATH_SEPARATOR)
end
end
+# Work around a bug in FreeBSD 13.2 which can cause fork(2) to hang
+# See: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=271490
+env['LD_BIND_NOW'] = 'yes' if /freebsd/ =~ RUBY_PLATFORM
ENV.update env
if debugger
case debugger
- when :gdb, nil
+ when :gdb
debugger = %W'gdb -x #{srcdir}/.gdbinit'
if File.exist?(gdb = 'run.gdb') or
File.exist?(gdb = File.join(abs_archdir, 'run.gdb'))
@@ -153,6 +158,8 @@ if debugger
debugger.push('-s', lldb)
end
debugger << '--'
+ when :rr
+ debugger = ['rr', 'record']
end
if idx = precommand.index(:debugger)
diff --git a/tool/sync_default_gems.rb b/tool/sync_default_gems.rb
index 482e138cf8..0307028eb7 100755
--- a/tool/sync_default_gems.rb
+++ b/tool/sync_default_gems.rb
@@ -3,6 +3,7 @@
# See `tool/sync_default_gems.rb --help` for how to use this.
require 'fileutils'
+require "rbconfig"
module SyncDefaultGems
include FileUtils
@@ -11,83 +12,71 @@ module SyncDefaultGems
module_function
REPOSITORIES = {
- rubygems: 'rubygems/rubygems',
- rdoc: 'ruby/rdoc',
- reline: 'ruby/reline',
- json: 'flori/json',
- psych: 'ruby/psych',
- fileutils: 'ruby/fileutils',
- fiddle: 'ruby/fiddle',
- stringio: 'ruby/stringio',
"io-console": 'ruby/io-console',
"io-nonblock": 'ruby/io-nonblock',
"io-wait": 'ruby/io-wait',
- csv: 'ruby/csv',
- etc: 'ruby/etc',
+ "net-http": "ruby/net-http",
+ "net-protocol": "ruby/net-protocol",
+ "open-uri": "ruby/open-uri",
+ English: "ruby/English",
+ benchmark: "ruby/benchmark",
+ cgi: "ruby/cgi",
date: 'ruby/date',
- zlib: 'ruby/zlib',
+ delegate: "ruby/delegate",
+ did_you_mean: "ruby/did_you_mean",
+ digest: "ruby/digest",
+ erb: "ruby/erb",
+ error_highlight: "ruby/error_highlight",
+ etc: 'ruby/etc',
fcntl: 'ruby/fcntl',
- strscan: 'ruby/strscan',
+ fiddle: 'ruby/fiddle',
+ fileutils: 'ruby/fileutils',
+ find: "ruby/find",
+ forwardable: "ruby/forwardable",
ipaddr: 'ruby/ipaddr',
- logger: 'ruby/logger',
- ostruct: 'ruby/ostruct',
irb: 'ruby/irb',
- forwardable: "ruby/forwardable",
- mutex_m: "ruby/mutex_m",
- racc: "ruby/racc",
- singleton: "ruby/singleton",
+ json: 'flori/json',
+ logger: 'ruby/logger',
open3: "ruby/open3",
- getoptlong: "ruby/getoptlong",
- pstore: "ruby/pstore",
- delegate: "ruby/delegate",
- benchmark: "ruby/benchmark",
- cgi: "ruby/cgi",
- readline: "ruby/readline",
- observer: "ruby/observer",
- timeout: "ruby/timeout",
- yaml: "ruby/yaml",
- uri: "ruby/uri",
openssl: "ruby/openssl",
- did_you_mean: "ruby/did_you_mean",
- weakref: "ruby/weakref",
- tempfile: "ruby/tempfile",
- tmpdir: "ruby/tmpdir",
- English: "ruby/English",
- "net-protocol": "ruby/net-protocol",
- "net-http": "ruby/net-http",
- bigdecimal: "ruby/bigdecimal",
optparse: "ruby/optparse",
- set: "ruby/set",
- find: "ruby/find",
- rinda: "ruby/rinda",
- erb: "ruby/erb",
- nkf: "ruby/nkf",
- tsort: "ruby/tsort",
- abbrev: "ruby/abbrev",
- shellwords: "ruby/shellwords",
- base64: "ruby/base64",
- syslog: "ruby/syslog",
- "open-uri": "ruby/open-uri",
- securerandom: "ruby/securerandom",
- resolv: "ruby/resolv",
- "resolv-replace": "ruby/resolv-replace",
- time: "ruby/time",
+ ostruct: 'ruby/ostruct',
+ pathname: "ruby/pathname",
pp: "ruby/pp",
prettyprint: "ruby/prettyprint",
- drb: "ruby/drb",
- pathname: "ruby/pathname",
- digest: "ruby/digest",
- error_highlight: "ruby/error_highlight",
+ prism: ["ruby/prism", "main"],
+ pstore: "ruby/pstore",
+ psych: 'ruby/psych',
+ rdoc: 'ruby/rdoc',
+ readline: "ruby/readline",
+ reline: 'ruby/reline',
+ resolv: "ruby/resolv",
+ rubygems: 'rubygems/rubygems',
+ securerandom: "ruby/securerandom",
+ set: "ruby/set",
+ shellwords: "ruby/shellwords",
+ singleton: "ruby/singleton",
+ stringio: 'ruby/stringio',
+ strscan: 'ruby/strscan',
syntax_suggest: ["ruby/syntax_suggest", "main"],
+ tempfile: "ruby/tempfile",
+ time: "ruby/time",
+ timeout: "ruby/timeout",
+ tmpdir: "ruby/tmpdir",
+ tsort: "ruby/tsort",
un: "ruby/un",
+ uri: "ruby/uri",
+ weakref: "ruby/weakref",
win32ole: "ruby/win32ole",
- }
+ yaml: "ruby/yaml",
+ zlib: 'ruby/zlib',
+ }.transform_keys(&:to_s)
CLASSICAL_DEFAULT_BRANCH = "master"
class << REPOSITORIES
def [](gem)
- repo, branch = super
+ repo, branch = super(gem)
return repo, branch || CLASSICAL_DEFAULT_BRANCH
end
@@ -122,16 +111,17 @@ module SyncDefaultGems
end
def replace_rdoc_ref_all
- result = pipe_readlines(%W"git status porcelain -z -- *.c *.rb *.rdoc")
+ result = pipe_readlines(%W"git status --porcelain -z -- *.c *.rb *.rdoc")
result.map! {|line| line[/\A.M (.*)/, 1]}
result.compact!
+ return if result.empty?
result = pipe_readlines(%W"git grep -z -l -F [https://docs.ruby-lang.org/en/master/ --" + result)
result.inject(false) {|changed, file| changed | replace_rdoc_ref(file)}
end
# We usually don't use this. Please consider using #sync_default_gems_with_commits instead.
def sync_default_gems(gem)
- repo, = REPOSITORIES[gem.to_sym]
+ repo, = REPOSITORIES[gem]
puts "Sync #{repo}"
upstream = File.join("..", "..", repo)
@@ -148,15 +138,14 @@ module SyncDefaultGems
gemspec_content = File.readlines("#{upstream}/bundler/bundler.gemspec").map do |line|
next if line =~ /LICENSE\.md/
- line.gsub("bundler.gemspec", "lib/bundler/bundler.gemspec").gsub('"exe"', '"libexec"')
+ line.gsub("bundler.gemspec", "lib/bundler/bundler.gemspec")
end.compact.join
File.write("lib/bundler/bundler.gemspec", gemspec_content)
cp_r("#{upstream}/bundler/spec", "spec/bundler")
- cp_r(Dir.glob("#{upstream}/bundler/tool/bundler/dev_gems*"), "tool/bundler")
- cp_r(Dir.glob("#{upstream}/bundler/tool/bundler/test_gems*"), "tool/bundler")
- cp_r(Dir.glob("#{upstream}/bundler/tool/bundler/rubocop_gems*"), "tool/bundler")
- cp_r(Dir.glob("#{upstream}/bundler/tool/bundler/standard_gems*"), "tool/bundler")
+ %w[dev_gems test_gems rubocop_gems standard_gems].each do |gemfile|
+ cp_r("#{upstream}/tool/bundler/#{gemfile}.rb", "tool/bundler")
+ end
rm_rf Dir.glob("spec/bundler/support/artifice/{vcr_cassettes,used_cassettes.txt}")
rm_rf Dir.glob("lib/{bundler,rubygems}/**/{COPYING,LICENSE,README}{,.{md,txt,rdoc}}")
when "rdoc"
@@ -206,7 +195,6 @@ module SyncDefaultGems
rm_rf("test/json/lib")
cp_r("#{upstream}/lib", "ext/json")
cp_r("#{upstream}/json.gemspec", "ext/json")
- cp_r("#{upstream}/VERSION", "ext/json")
rm_rf(%w[ext/json/lib/json/ext ext/json/lib/json/pure.rb ext/json/lib/json/pure])
`git checkout ext/json/extconf.rb ext/json/parser/prereq.mk ext/json/generator/depend ext/json/parser/depend ext/json/depend`
when "psych"
@@ -287,25 +275,6 @@ module SyncDefaultGems
cp_r("#{upstream}/strscan.gemspec", "ext/strscan")
rm_rf(%w["ext/strscan/regenc.h ext/strscan/regint.h"])
`git checkout ext/strscan/depend`
- when "racc"
- rm_rf(%w[lib/racc lib/racc.rb ext/racc test/racc])
- parser_files = %w[
- lib/racc/parser-text.rb
- ]
- Dir.chdir(upstream) do
- `bundle install`
- parser_files.each do |file|
- `bundle exec rake #{file}`
- end
- end
- cp_r(Dir.glob("#{upstream}/lib/racc*"), "lib")
- mkdir_p("ext/racc/cparse")
- cp_r(Dir.glob("#{upstream}/ext/racc/cparse/*"), "ext/racc/cparse")
- cp_r("#{upstream}/test", "test/racc")
- cp_r("#{upstream}/racc.gemspec", "lib/racc")
- rm_rf("test/racc/lib")
- rm_rf("lib/racc/cparse-jruby.jar")
- `git checkout ext/racc/cparse/README ext/racc/cparse/depend`
when "cgi"
rm_rf(%w[lib/cgi.rb lib/cgi ext/cgi test/cgi])
cp_r("#{upstream}/ext/cgi", "ext")
@@ -347,29 +316,6 @@ module SyncDefaultGems
cp_r("#{upstream}/test/erb", "test")
cp_r("#{upstream}/erb.gemspec", "lib")
cp_r("#{upstream}/libexec/erb", "libexec")
- when "nkf"
- rm_rf(%w[ext/nkf test/nkf])
- cp_r("#{upstream}/ext/nkf", "ext")
- cp_r("#{upstream}/lib", "ext/nkf")
- cp_r("#{upstream}/test/nkf", "test")
- cp_r("#{upstream}/nkf.gemspec", "ext/nkf")
- `git checkout ext/nkf/depend`
- when "syslog"
- rm_rf(%w[ext/syslog test/syslog test/test_syslog.rb])
- cp_r("#{upstream}/ext/syslog", "ext")
- cp_r("#{upstream}/lib", "ext/syslog")
- cp_r("#{upstream}/test/syslog", "test")
- cp_r("#{upstream}/test/test_syslog.rb", "test")
- cp_r("#{upstream}/syslog.gemspec", "ext/syslog")
- `git checkout ext/syslog/depend`
- when "bigdecimal"
- rm_rf(%w[ext/bigdecimal test/bigdecimal])
- cp_r("#{upstream}/ext/bigdecimal", "ext")
- cp_r("#{upstream}/sample", "ext/bigdecimal")
- cp_r("#{upstream}/lib", "ext/bigdecimal")
- cp_r("#{upstream}/test/bigdecimal", "test")
- cp_r("#{upstream}/bigdecimal.gemspec", "ext/bigdecimal")
- `git checkout ext/bigdecimal/depend`
when "pathname"
rm_rf(%w[ext/pathname test/pathname])
cp_r("#{upstream}/ext/pathname", "ext")
@@ -391,7 +337,7 @@ module SyncDefaultGems
`git checkout ext/digest/depend ext/digest/*/depend`
when "set"
sync_lib gem, upstream
- cp_r("#{upstream}/test", ".")
+ cp_r(Dir.glob("#{upstream}/test/*"), "test/set")
when "optparse"
sync_lib gem, upstream
rm_rf(%w[doc/optparse])
@@ -416,27 +362,69 @@ module SyncDefaultGems
rm_rf(%w[spec/syntax_suggest libexec/syntax_suggest])
cp_r("#{upstream}/spec", "spec/syntax_suggest")
cp_r("#{upstream}/exe/syntax_suggest", "libexec/syntax_suggest")
+ when "prism"
+ rm_rf(%w[test/prism prism])
+
+ cp_r("#{upstream}/ext/prism", "prism")
+ cp_r("#{upstream}/lib/.", "lib")
+ cp_r("#{upstream}/test/prism", "test")
+ cp_r("#{upstream}/src/.", "prism")
+
+ cp_r("#{upstream}/prism.gemspec", "lib/prism")
+ cp_r("#{upstream}/include/prism/.", "prism")
+ cp_r("#{upstream}/include/prism.h", "prism")
+
+ cp_r("#{upstream}/config.yml", "prism/")
+ cp_r("#{upstream}/templates", "prism/")
+ rm_rf("prism/templates/javascript")
+ rm_rf("prism/templates/java")
+ rm_rf("prism/templates/rbi")
+ rm_rf("prism/templates/sig")
+
+ rm("prism/extconf.rb")
else
sync_lib gem, upstream
end
+
+ # Architecture-dependent files must not pollute libdir.
+ rm_rf(Dir["lib/**/*.#{RbConfig::CONFIG['DLEXT']}"])
replace_rdoc_ref_all
end
- IGNORE_FILE_PATTERN =
- /\A(?:[A-Z]\w*\.(?:md|txt)
- |[^\/]+\.yml
- |\.git.*
- |[A-Z]\w+file
- |COPYING
- |\Arakelib\/.*
- |\Atest\/lib\/.*
- )\z/mx
+ def ignore_file_pattern_for(gem)
+ patterns = []
+
+ # Common patterns
+ patterns << %r[\A(?:
+ [^/]+ # top-level entries
+ |\.git.*
+ |bin/.*
+ |ext/.*\.java
+ |rakelib/.*
+ |test/(?:lib|fixtures)/.*
+ |tool/(?!bundler/).*
+ )\z]mx
+
+ # Gem-specific patterns
+ case gem
+ when nil
+ end&.tap do |pattern|
+ patterns << pattern
+ end
+
+ Regexp.union(*patterns)
+ end
def message_filter(repo, sha, input: ARGF)
log = input.read
log.delete!("\r")
+ log << "\n" if !log.end_with?("\n")
repo_url = "https://github.com/#{repo}"
- subject, log = log.split(/\n(?:[ \t]*(?:\n|\z))/, 2)
+
+ # Split the subject from the log message according to git conventions.
+ # SPECIAL TREAT: when the first line ends with a dot `.` (which is not
+ # obeying the conventions too), takes only that line.
+ subject, log = log.split(/\A.+\.\K\n(?=\S)|\n(?:[ \t]*(?:\n|\z))/, 2)
conv = proc do |s|
mod = true if s.gsub!(/\b(?:(?i:fix(?:e[sd])?|close[sd]?|resolve[sd]?) +)\K#(?=\d+\b)|\bGH-#?(?=\d+\b)|\(\K#(?=\d+\))/) {
"#{repo_url}/pull/"
@@ -466,41 +454,210 @@ module SyncDefaultGems
puts subject, "\n", log
end
+ # Returns commit list as array of [commit_hash, subject].
+ def commits_in_ranges(gem, repo, default_branch, ranges)
+ # If -a is given, discover all commits since the last picked commit
+ if ranges == true
+ # \r? needed in the regex in case the commit has windows-style line endings (because e.g. we're running
+ # tests on Windows)
+ pattern = "https://github\.com/#{Regexp.quote(repo)}/commit/([0-9a-f]+)\r?$"
+ log = IO.popen(%W"git log -E --grep=#{pattern} -n1 --format=%B", "rb", &:read)
+ ranges = ["#{log[%r[#{pattern}\n\s*(?i:co-authored-by:.*)*\s*\Z], 1]}..#{gem}/#{default_branch}"]
+ end
+
+ # Parse a given range with git log
+ ranges.flat_map do |range|
+ unless range.include?("..")
+ range = "#{range}~1..#{range}"
+ end
+
+ IO.popen(%W"git log --format=%H,%s #{range} --", "rb") do |f|
+ f.read.split("\n").reverse.map{|commit| commit.split(',', 2)}
+ end
+ end
+ end
+
+ #--
+ # Following methods used by sync_default_gems_with_commits return
+ # true: success
+ # false: skipped
+ # nil: failed
+ #++
+
+ def resolve_conflicts(gem, sha, edit)
+ # Skip this commit if everything has been removed as `ignored_paths`.
+ changes = pipe_readlines(%W"git status --porcelain -z")
+ if changes.empty?
+ puts "Skip empty commit #{sha}"
+ return false
+ end
+
+ # We want to skip DD: deleted by both.
+ deleted = changes.grep(/^DD /) {$'}
+ system(*%W"git rm -f --", *deleted) unless deleted.empty?
+
+ # Import UA: added by them
+ added = changes.grep(/^UA /) {$'}
+ system(*%W"git add --", *added) unless added.empty?
+
+ # Discover unmerged files
+ # AU: unmerged, added by us
+ # DU: unmerged, deleted by us
+ # UU: unmerged, both modified
+ # AA: unmerged, both added
+ conflict = changes.grep(/\A(?:.U|AA) /) {$'}
+ # If -e option is given, open each conflicted file with an editor
+ unless conflict.empty?
+ if edit
+ case
+ when (editor = ENV["GIT_EDITOR"] and !editor.empty?)
+ when (editor = `git config core.editor` and (editor.chomp!; !editor.empty?))
+ end
+ if editor
+ system([editor, conflict].join(' '))
+ conflict.delete_if {|f| !File.exist?(f)}
+ return true if conflict.empty?
+ return system(*%w"git add --", *conflict)
+ end
+ end
+ return false
+ end
+
+ return true
+ end
+
+ def preexisting?(base, file)
+ system(*%w"git cat-file -e", "#{base}:#{file}", err: File::NULL)
+ end
+
+ def filter_pickup_files(changed, ignore_file_pattern, base)
+ toplevels = {}
+ remove = []
+ ignore = []
+ changed = changed.reject do |f|
+ case
+ when toplevels.fetch(top = f[%r[\A[^/]+(?=/|\z)]m]) {
+ remove << top if toplevels[top] = !preexisting?(base, top)
+ }
+ # Remove any new top-level directories.
+ true
+ when ignore_file_pattern.match?(f)
+ # Forcibly reset any changes matching ignore_file_pattern.
+ (preexisting?(base, f) ? ignore : remove) << f
+ end
+ end
+ return changed, remove, ignore
+ end
+
+ def pickup_files(gem, changed, picked)
+ # Forcibly remove any files that we don't want to copy to this
+ # repository.
+
+ ignore_file_pattern = ignore_file_pattern_for(gem)
+
+ base = picked ? "HEAD~" : "HEAD"
+ changed, remove, ignore = filter_pickup_files(changed, ignore_file_pattern, base)
+
+ unless remove.empty?
+ puts "Remove added files: #{remove.join(', ')}"
+ system(*%w"git rm -fr --", *remove)
+ if picked
+ system(*%w"git commit --amend --no-edit --", *remove, %i[out err] => File::NULL)
+ end
+ end
+
+ unless ignore.empty?
+ puts "Reset ignored files: #{ignore.join(', ')}"
+ system(*%W"git rm -r --", *ignore)
+ ignore.each {|f| system(*%W"git checkout -f", base, "--", f)}
+ end
+
+ if changed.empty?
+ return nil
+ end
+
+ return changed
+ end
+
+ def pickup_commit(gem, sha, edit)
+ # Attempt to cherry-pick a commit
+ result = IO.popen(%W"git cherry-pick #{sha}", "rb", &:read)
+ picked = $?.success?
+ if result =~ /nothing\ to\ commit/
+ `git reset`
+ puts "Skip empty commit #{sha}"
+ return false
+ end
+
+ # Skip empty commits
+ if result.empty?
+ return false
+ end
+
+ if picked
+ changed = pipe_readlines(%w"git diff-tree --name-only -r -z HEAD~..HEAD --")
+ else
+ changed = pipe_readlines(%w"git diff --name-only -r -z HEAD --")
+ end
+
+ # Pick up files to merge.
+ unless changed = pickup_files(gem, changed, picked)
+ puts "Skip commit #{sha} only for tools or toplevel"
+ if picked
+ `git reset --hard HEAD~`
+ else
+ `git cherry-pick --abort`
+ end
+ return false
+ end
+
+ # If the cherry-pick attempt failed, try to resolve conflicts.
+ # Skip the commit, if it contains unresolved conflicts or no files to pick up.
+ unless picked or resolve_conflicts(gem, sha, edit)
+ `git reset` && `git checkout .` && `git clean -fd`
+ return picked || nil # Fail unless cherry-picked
+ end
+
+ # Commit cherry-picked commit
+ if picked
+ system(*%w"git commit --amend --no-edit")
+ else
+ system(*%w"git cherry-pick --continue --no-edit")
+ end or return nil
+
+ # Amend the commit if RDoc references need to be replaced
+ head = `git log --format=%H -1 HEAD`.chomp
+ system(*%w"git reset --quiet HEAD~ --")
+ amend = replace_rdoc_ref_all
+ system(*%W"git reset --quiet #{head} --")
+ if amend
+ `git commit --amend --no-edit --all`
+ end
+
+ return true
+ end
+
# NOTE: This method is also used by GitHub ruby/git.ruby-lang.org's bin/update-default-gem.sh
# @param gem [String] A gem name, also used as a git remote name. REPOSITORIES converts it to the appropriate GitHub repository.
# @param ranges [Array<String>] "before..after". Note that it will NOT sync "before" (but commits after that).
# @param edit [TrueClass] Set true if you want to resolve conflicts. Obviously, update-default-gem.sh doesn't use this.
def sync_default_gems_with_commits(gem, ranges, edit: nil)
- repo, default_branch = REPOSITORIES[gem.to_sym]
+ repo, default_branch = REPOSITORIES[gem]
puts "Sync #{repo} with commit history."
+ # Fetch the repository to be synchronized
IO.popen(%W"git remote") do |f|
unless f.read.split.include?(gem)
- `git remote add #{gem} git@github.com:#{repo}.git`
+ `git remote add #{gem} https://github.com/#{repo}.git`
end
end
system(*%W"git fetch --no-tags #{gem}")
- if ranges == true
- pattern = "https://github\.com/#{Regexp.quote(repo)}/commit/([0-9a-f]+)$"
- log = IO.popen(%W"git log -E --grep=#{pattern} -n1 --format=%B", &:read)
- ranges = ["#{log[%r[#{pattern}\n\s*(?i:co-authored-by:.*)*\s*\Z], 1]}..#{gem}/#{default_branch}"]
- end
-
- commits = ranges.flat_map do |range|
- unless range.include?("..")
- range = "#{range}~1..#{range}"
- end
+ commits = commits_in_ranges(gem, repo, default_branch, ranges)
- IO.popen(%W"git log --format=%H,%s #{range} --") do |f|
- f.read.split("\n").reverse.map{|commit| commit.split(',', 2)}
- end
- end
-
- # Ignore Merge commit and insufficiency commit for ruby core repository.
+ # Ignore Merge commits and already-merged commits.
commits.delete_if do |sha, subject|
- files = pipe_readlines(%W"git diff-tree -z --no-commit-id --name-only -r #{sha}")
- subject.start_with?("Merge", "Auto Merge") or files.all?(IGNORE_FILE_PATTERN)
+ subject.start_with?("Merge", "Auto Merge")
end
if commits.empty?
@@ -522,88 +679,17 @@ module SyncDefaultGems
]
commits.each do |sha, subject|
puts "Pick #{sha} from #{repo}."
-
- skipped = false
- result = IO.popen(%W"git cherry-pick #{sha}", &:read)
- if result =~ /nothing\ to\ commit/
- `git reset`
- skipped = true
- puts "Skip empty commit #{sha}"
- end
- next if skipped
-
- case gem
- when "rubygems"
- %w[bundler/spec/support/artifice/vcr_cassettes].each do |rem|
- if File.exist?(rem)
- system("git", "reset", rem)
- rm_rf(rem)
- end
- end
- system(*%w[git add spec/bundler])
- end
-
- if result.empty?
- skipped = true
- elsif /^CONFLICT/ =~ result
- result = pipe_readlines(%W"git status --porcelain -z")
- result.map! {|line| line[/\A(?:.U|[UA]A) (.*)/, 1]}
- result.compact!
- ignore, conflict = result.partition {|name| IGNORE_FILE_PATTERN =~ name}
- unless ignore.empty?
- system(*%W"git reset HEAD --", *ignore)
- File.unlink(*ignore)
- ignore = pipe_readlines(%W"git status --porcelain -z" + ignore).map! {|line| line[/\A.. (.*)/, 1]}
- system(*%W"git checkout HEAD --", *ignore) unless ignore.empty?
- end
- unless conflict.empty?
- if edit
- case
- when (editor = ENV["GIT_EDITOR"] and !editor.empty?)
- when (editor = `git config core.editor` and (editor.chomp!; !editor.empty?))
- end
- if editor
- system([editor, conflict].join(' '))
- end
- end
- end
- skipped = !system({"GIT_EDITOR"=>"true"}, *%W"git cherry-pick --no-edit --continue")
- end
-
- if skipped
+ case pickup_commit(gem, sha, edit)
+ when false
+ next
+ when nil
failed_commits << sha
- `git reset` && `git checkout .` && `git clean -fd`
- puts "Failed to pick #{sha}"
next
end
- tools = pipe_readlines(%W"git diff --name-only -z HEAD~..HEAD -- test/lib/ tool/ rakelib/")
- unless tools.empty?
- system(*%W"git rm --", *tools)
- system(*%W"git checkout HEAD~ --", *tools)
- if system(*%W"git diff --quiet HEAD~")
- `git reset HEAD~ --` && `git checkout .` && `git clean -fd`
- puts "Skip commit #{sha} only for tools"
- next
- end
- unless system(*%W"git commit --amend --no-edit --", *tools)
- failed_commits << sha
- `git reset HEAD~ --` && `git checkout .` && `git clean -fd`
- puts "Failed to pick #{sha}"
- next
- end
- end
-
- head = `git log --format=%H -1 HEAD`.chomp
- system(*%w"git reset --quiet HEAD~ --")
- amend = replace_rdoc_ref_all
- system(*%W"git reset --quiet #{head} --")
- if amend
- `git commit --amend --no-edit --all`
- end
-
puts "Update commit message: #{sha}"
+ # Run this script itself (tool/sync_default_gems.rb --message-filter) as a message filter
IO.popen({"FILTER_BRANCH_SQUELCH_WARNING" => "1"},
%W[git filter-branch -f --msg-filter #{[filter, repo, sha].join(' ')} -- HEAD~1..HEAD],
&:read)
@@ -643,7 +729,7 @@ module SyncDefaultGems
def update_default_gems(gem, release: false)
- repository, default_branch = REPOSITORIES[gem.to_sym]
+ repository, default_branch = REPOSITORIES[gem]
author, repository = repository.split('/')
puts "Update #{author}/#{repository}"
@@ -681,16 +767,16 @@ module SyncDefaultGems
if ARGV[1]
update_default_gems(ARGV[1])
else
- REPOSITORIES.each_key {|gem| update_default_gems(gem.to_s)}
+ REPOSITORIES.each_key {|gem| update_default_gems(gem)}
end
when "all"
if ARGV[1] == "release"
REPOSITORIES.each_key do |gem|
- update_default_gems(gem.to_s, release: true)
- sync_default_gems(gem.to_s)
+ update_default_gems(gem, release: true)
+ sync_default_gems(gem)
end
else
- REPOSITORIES.each_key {|gem| sync_default_gems(gem.to_s)}
+ REPOSITORIES.each_key {|gem| sync_default_gems(gem)}
end
when "list"
ARGV.shift
@@ -730,6 +816,9 @@ module SyncDefaultGems
\e[1mPick a commit range from the upstream repository\e[0m
ruby #$0 rubygems 97e9768612..9e53702832
+\e[1mPick all commits since the last picked commit\e[0m
+ ruby #$0 -a rubygems
+
\e[1mList known libraries\e[0m
ruby #$0 list
diff --git a/tool/sync_test_lib.rb b/tool/sync_test_lib.rb
deleted file mode 100755
index 23ca4a1154..0000000000
--- a/tool/sync_test_lib.rb
+++ /dev/null
@@ -1,101 +0,0 @@
-#!/usr/bin/env ruby
-
-require "fileutils"
-
-test_lib_files = [
- ["lib/core_assertions.rb", "test/lib"],
- ["lib/find_executable.rb", "test/lib"],
- ["lib/envutil.rb", "test/lib"],
- ["lib/helper.rb", "test/lib"],
- ["rakelib/sync_tool.rake", "rakelib"],
-].map do |file, dest|
- [file, dest, File.read("#{__dir__}/#{file}")]
-end
-
-repos = %w[
- bigdecimal cgi cmath date delegate did_you_mean digest drb erb etc
- fileutils find forwardable io-console io-nonblock io-wait ipaddr
- irb logger net-http net-protocol open-uri open3 openssl optparse
- ostruct pathname pstore psych racc resolv stringio strscan tempfile
- time timeout tmpdir uri weakref win32ole yaml zlib
-]
-
-branch_name = "update-test-lib-#{Time.now.strftime("%Y%m%d")}"
-title = "Update test libraries from ruby/ruby #{Time.now.strftime("%Y-%m-%d")}"
-commit = `git rev-parse HEAD`.chomp
-message = "From https://github.com/ruby/ruby/commit/#{commit}"
-
-update = true
-keep = nil
-topdir = nil
-while arg = ARGV.shift
- case arg
- when '--dry-run'
- update = false
- keep = true
- when '--update'
- update = true
- keep = false if keep.nil?
- when '--no-update'
- update = false
- keep = true if keep.nil?
- when '--keep'
- keep = true
- when '--no-keep'
- keep = false
- when '--'
- break
- else
- if topdir
- ARGV.unshift(arg)
- else
- topdir = arg
- end
- break
- end
-end
-topdir ||= '..'
-repos = ARGV unless ARGV.empty?
-
-repos.each do |repo|
- puts "#{repo}: start"
-
- Dir.chdir("#{topdir}/#{repo}") do
- if `git branch --list #{branch_name}`.empty?
- system(*%W"git switch master")
- system(*%W"git switch -c #{branch_name}")
- else
- puts "#{repo}: skip"
- next
- end
-
- test_lib_files.each do |file, dest, code|
- FileUtils.mkdir_p(dest)
- file = "#{dest}/#{File.basename(file)}"
- File.binwrite(file, code)
- system "git add #{file}"
- end
-
- rf = File.binread("Rakefile")
- if rf.sub!(/(?>\A|(\n)\n*)task +:sync_tool +do\n(?>(?> .*)?\n)*end\n(?=\z|(\n))/) {$1&&$2}
- File.binwrite("Rakefile", rf)
- system "git add Rakefile"
- end
-
- if IO.popen(%W"git commit -m #{title}\n\n#{message}", &:read).chomp =~ /nothing to commit/
- puts "#{repo}: nothing to update"
- elsif update
- system(*%W"git push")
- system(*%W"gh repo set-default ruby/#{repo}")
- system(*%W"gh pr create --base master --head ruby:#{branch_name} --title #{title} --body #{message}")
- puts "#{repo}: updated"
- end
-
- unless keep
- system "git switch master"
- system "git branch -D #{branch_name}"
- end
- end
-rescue StandardError => e
- puts e
-end
diff --git a/tool/test-bundled-gems.rb b/tool/test-bundled-gems.rb
index 5ec8cc5cc7..eeec1d7aed 100644
--- a/tool/test-bundled-gems.rb
+++ b/tool/test-bundled-gems.rb
@@ -15,8 +15,7 @@ ENV["GEM_PATH"] = [File.realpath('.bundle'), File.realpath('../.bundle', __dir__
colorize = Colorize.new
rake = File.realpath("../../.bundle/bin/rake", __FILE__)
gem_dir = File.realpath('../../gems', __FILE__)
-dummy_rake_compiler_dir = File.realpath('../dummy-rake-compiler', __FILE__)
-rubylib = [File.expand_path(dummy_rake_compiler_dir), ENV["RUBYLIB"]].compact.join(File::PATH_SEPARATOR)
+rubylib = [gem_dir+'/lib', ENV["RUBYLIB"]].compact.join(File::PATH_SEPARATOR)
exit_code = 0
ruby = ENV['RUBY'] || RbConfig.ruby
failed = []
@@ -24,25 +23,41 @@ File.foreach("#{gem_dir}/bundled_gems") do |line|
next if /^\s*(?:#|$)/ =~ line
gem = line.split.first
next if ARGV.any? {|pat| !File.fnmatch?(pat, gem)}
- puts "#{github_actions ? "##[group]" : "\n"}Testing the #{gem} gem"
+ # 93(bright yellow) is copied from .github/workflows/mingw.yml
+ puts "#{github_actions ? "::group::\e\[93m" : "\n"}Testing the #{gem} gem#{github_actions ? "\e\[m" : ""}"
test_command = "#{ruby} -C #{gem_dir}/src/#{gem} #{rake} test"
first_timeout = 600 # 10min
toplib = gem
case gem
+ when "resolv-replace"
+ # Skip test suite
+ next
when "typeprof"
when "rbs"
+ # TODO: We should skip test file instead of test class/methods
+ skip_test_files = %w[
+ ]
+
+ skip_test_files.each do |file|
+ path = "#{gem_dir}/src/#{gem}/#{file}"
+ File.unlink(path) if File.exist?(path)
+ end
+
test_command << " stdlib_test validate RBS_SKIP_TESTS=#{__dir__}/rbs_skip_tests SKIP_RBS_VALIDATION=true"
first_timeout *= 3
when "debug"
# Since debug gem requires debug.so in child processes without
- # acitvating the gem, we preset necessary paths in RUBYLIB
+ # activating the gem, we preset necessary paths in RUBYLIB
# environment variable.
load_path = true
+ when "test-unit"
+ test_command = "#{ruby} -C #{gem_dir}/src/#{gem} test/run-test.rb"
+
when /\Anet-/
toplib = gem.tr("-", "/")
@@ -76,7 +91,7 @@ File.foreach("#{gem_dir}/bundled_gems") do |line|
break
end
- print "##[endgroup]\n" if github_actions
+ print "::endgroup::\n" if github_actions
unless $?.success?
mesg = "Tests failed " +
diff --git a/tool/test/init.rb b/tool/test/init.rb
new file mode 100644
index 0000000000..3a1143d01d
--- /dev/null
+++ b/tool/test/init.rb
@@ -0,0 +1,18 @@
+# This file includes the settings for "make test-all".
+# Note that this file is loaded not only by test/runner.rb but also by tool/lib/test/unit/parallel.rb.
+
+ENV["GEM_SKIP"] = ENV["GEM_HOME"] = ENV["GEM_PATH"] = "".freeze
+ENV.delete("RUBY_CODESIGN")
+
+Warning[:experimental] = false
+
+$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
+
+require 'test/unit'
+
+require "profile_test_all" if ENV.key?('RUBY_TEST_ALL_PROFILE')
+require "tracepointchecker"
+require "zombie_hunter"
+require "iseq_loader_checker"
+require "gc_checker"
+require_relative "../test-coverage.rb" if ENV.key?('COVERAGE')
diff --git a/tool/test/runner.rb b/tool/test/runner.rb
index 335fe65fd4..9001fc2d06 100644
--- a/tool/test/runner.rb
+++ b/tool/test/runner.rb
@@ -1,16 +1,7 @@
# frozen_string_literal: true
require 'rbconfig'
-$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
-
-require 'test/unit'
-
-require "profile_test_all" if ENV.key?('RUBY_TEST_ALL_PROFILE')
-require "tracepointchecker"
-require "zombie_hunter"
-require "iseq_loader_checker"
-require "gc_checker"
-require_relative "../test-coverage.rb" if ENV.key?('COVERAGE')
+require_relative "init"
case $0
when __FILE__
diff --git a/tool/test/test_sync_default_gems.rb b/tool/test/test_sync_default_gems.rb
index 8e667fc4d2..e64c6c6fda 100755
--- a/tool/test/test_sync_default_gems.rb
+++ b/tool/test/test_sync_default_gems.rb
@@ -1,6 +1,7 @@
#!/usr/bin/ruby
require 'test/unit'
require 'stringio'
+require 'tmpdir'
require_relative '../sync_default_gems'
module Test_SyncDefaultGems
@@ -72,6 +73,15 @@ module Test_SyncDefaultGems
]
assert_message_filter(expected, trailers, [expected, "", trailers].join("\n"))
end
+
+ def test_dot_ending_subject
+ expected = [
+ "subject with a dot.",
+ "",
+ "- next body line",
+ ]
+ assert_message_filter(expected, nil, [expected[0], expected[2], ""].join("\n"))
+ end
end
class TestSyncWithCommits < Test::Unit::TestCase
@@ -80,38 +90,55 @@ module Test_SyncDefaultGems
@target = nil
pend "No git" unless system("git --version", out: IO::NULL)
@testdir = Dir.mktmpdir("sync")
- @git_config = ENV["GIT_CONFIG_GLOBAL"]
+ @git_config = %W"HOME GIT_CONFIG_GLOBAL".each_with_object({}) {|k, c| c[k] = ENV[k]}
+ ENV["HOME"] = @testdir
ENV["GIT_CONFIG_GLOBAL"] = @testdir + "/gitconfig"
- system(*%W"git config --global user.email test@ruby-lang.org")
- system(*%W"git config --global user.name", "Ruby")
- system(*%W"git config --global init.defaultBranch default")
+ git(*%W"config --global user.email test@ruby-lang.org")
+ git(*%W"config --global user.name", "Ruby")
+ git(*%W"config --global init.defaultBranch default")
@target = "sync-test"
- SyncDefaultGems::REPOSITORIES[@target.to_sym] = ["ruby/#{@target}", "default"]
+ SyncDefaultGems::REPOSITORIES[@target] = ["ruby/#{@target}", "default"]
@sha = {}
@origdir = Dir.pwd
Dir.chdir(@testdir)
["src", @target].each do |dir|
- system(*%W"git init -q #{dir}", exception: true)
+ git(*%W"init -q #{dir}")
+ File.write("#{dir}/.gitignore", "*~\n")
+ Dir.mkdir("#{dir}/lib")
+ File.write("#{dir}/lib/common.rb", ":ok\n")
+ Dir.mkdir("#{dir}/.github")
+ Dir.mkdir("#{dir}/.github/workflows")
+ File.write("#{dir}/.github/workflows/default.yml", "default:\n")
+ git(*%W"add .gitignore lib/common.rb .github", chdir: dir)
+ git(*%W"commit -q -m", "Initialize", chdir: dir)
+ if dir == "src"
+ File.write("#{dir}/lib/fine.rb", "return\n")
+ Dir.mkdir("#{dir}/test")
+ File.write("#{dir}/test/test_fine.rb", "return\n")
+ git(*%W"add lib/fine.rb test/test_fine.rb", chdir: dir)
+ git(*%W"commit -q -m", "Looks fine", chdir: dir)
+ end
Dir.mkdir("#{dir}/tool")
File.write("#{dir}/tool/ok", "#!/bin/sh\n""echo ok\n")
- system(*%W"git add tool/ok", exception: true, chdir: dir)
- system(*%W"git commit -q -m", "Add tool #{dir}", exception: true, chdir: dir)
- @sha[dir] = IO.popen(%W[git log --format=%H -1], chdir: dir, &:read).chomp
+ git(*%W"add tool/ok", chdir: dir)
+ git(*%W"commit -q -m", "Add tool #{dir}", chdir: dir)
+ @sha[dir] = top_commit(dir)
end
- system(*%W"git remote add #{@target} ../#{@target}", exception: true, chdir: "src")
+ git(*%W"remote add #{@target} ../#{@target}", chdir: "src")
end
def teardown
if @target
Dir.chdir(@origdir)
- SyncDefaultGems::REPOSITORIES.delete(@target.to_sym)
- ENV["GIT_CONFIG_GLOBAL"] = @git_config
+ SyncDefaultGems::REPOSITORIES.delete(@target)
+ ENV.update(@git_config)
FileUtils.rm_rf(@testdir)
end
super
end
def capture_process_output_to(outputs)
+ return yield unless outputs&.empty? == false
IO.pipe do |r, w|
orig = outputs.map {|out| out.dup}
outputs.each {|out| out.reopen(w)}
@@ -136,15 +163,135 @@ module Test_SyncDefaultGems
return out, err
end
- def test_skip_tool
- system(*%W"git rm -q tool/ok", exception: true, chdir: @target)
- system(*%W"git commit -q -m", "Remove tool", exception: true, chdir: @target)
+ def git(*commands, **opts)
+ system("git", *commands, exception: true, **opts)
+ end
+
+ def top_commit(dir, format: "%H")
+ IO.popen(%W[git log --format=#{format} -1], chdir: dir, &:read)&.chomp
+ end
+
+ def assert_sync(commits = true, success: true, editor: nil)
+ result = nil
out = capture_process_output_to([STDOUT, STDERR]) do
Dir.chdir("src") do
- SyncDefaultGems.sync_default_gems_with_commits(@target, true)
+ orig_editor = ENV["GIT_EDITOR"]
+ ENV["GIT_EDITOR"] = editor || 'false'
+ edit = true if editor
+
+ result = SyncDefaultGems.sync_default_gems_with_commits(@target, commits, edit: edit)
+ ensure
+ ENV["GIT_EDITOR"] = orig_editor
end
end
- assert_equal(@sha["src"], IO.popen(%W[git log --format=%H -1], chdir: "src", &:read).chomp, out)
+ assert_equal(success, result, out)
+ out
+ end
+
+ def test_sync
+ File.write("#@target/lib/common.rb", "# OK!\n")
+ git(*%W"commit -q -m", "OK", "lib/common.rb", chdir: @target)
+ out = assert_sync()
+ assert_not_equal(@sha["src"], top_commit("src"), out)
+ assert_equal("# OK!\n", File.read("src/lib/common.rb"))
+ log = top_commit("src", format: "%B").lines
+ assert_equal("[ruby/#@target] OK\n", log.first, out)
+ assert_match(%r[/ruby/#{@target}/commit/\h+$], log.last, out)
+ assert_operator(top_commit(@target), :start_with?, log.last[/\h+$/], out)
+ end
+
+ def test_skip_tool
+ git(*%W"rm -q tool/ok", chdir: @target)
+ git(*%W"commit -q -m", "Remove tool", chdir: @target)
+ out = assert_sync()
+ assert_equal(@sha["src"], top_commit("src"), out)
+ end
+
+ def test_skip_test_fixtures
+ Dir.mkdir("#@target/test")
+ Dir.mkdir("#@target/test/fixtures")
+ File.write("#@target/test/fixtures/fixme.rb", "")
+ git(*%W"add test/fixtures/fixme.rb", chdir: @target)
+ git(*%W"commit -q -m", "Add fixtures", chdir: @target)
+ out = assert_sync(["#{@sha[@target]}..#{@target}/default"])
+ assert_equal(@sha["src"], top_commit("src"), out)
+ end
+
+ def test_skip_toplevel
+ Dir.mkdir("#@target/docs")
+ File.write("#@target/docs/NEWS.md", "= NEWS!!!\n")
+ git(*%W"add --", "docs/NEWS.md", chdir: @target)
+ File.write("#@target/docs/hello.md", "Hello\n")
+ git(*%W"add --", "docs/hello.md", chdir: @target)
+ git(*%W"commit -q -m", "It's a news", chdir: @target)
+ out = assert_sync()
+ assert_equal(@sha["src"], top_commit("src"), out)
+ end
+
+ def test_adding_toplevel
+ Dir.mkdir("#@target/docs")
+ File.write("#@target/docs/NEWS.md", "= New library\n")
+ File.write("#@target/lib/news.rb", "return\n")
+ git(*%W"add --", "docs/NEWS.md", "lib/news.rb", chdir: @target)
+ git(*%W"commit -q -m", "New lib", chdir: @target)
+ out = assert_sync()
+ assert_not_equal(@sha["src"], top_commit("src"), out)
+ assert_equal "return\n", File.read("src/lib/news.rb")
+ assert_include top_commit("src", format: "oneline"), "[ruby/#{@target}] New lib"
+ assert_not_operator File, :exist?, "src/docs"
+ end
+
+ def test_gitignore
+ File.write("#@target/.gitignore", "*.bak\n", mode: "a")
+ File.write("#@target/lib/common.rb", "Should.be_merged\n", mode: "a")
+ File.write("#@target/.github/workflows/main.yml", "# Should not merge\n", mode: "a")
+ git(*%W"add .github", chdir: @target)
+ git(*%W"commit -q -m", "Should be common.rb only",
+ *%W".gitignore lib/common.rb .github", chdir: @target)
+ out = assert_sync()
+ assert_not_equal(@sha["src"], top_commit("src"), out)
+ assert_equal("*~\n", File.read("src/.gitignore"), out)
+ assert_equal("#!/bin/sh\n""echo ok\n", File.read("src/tool/ok"), out)
+ assert_equal(":ok\n""Should.be_merged\n", File.read("src/lib/common.rb"), out)
+ assert_not_operator(File, :exist?, "src/.github/workflows/main.yml", out)
+ end
+
+ def test_gitignore_after_conflict
+ File.write("src/Gemfile", "# main\n")
+ git(*%W"add Gemfile", chdir: "src")
+ git(*%W"commit -q -m", "Add Gemfile", chdir: "src")
+ File.write("#@target/Gemfile", "# conflict\n", mode: "a")
+ File.write("#@target/lib/common.rb", "Should.be_merged\n", mode: "a")
+ File.write("#@target/.github/workflows/main.yml", "# Should not merge\n", mode: "a")
+ git(*%W"add Gemfile .github lib/common.rb", chdir: @target)
+ git(*%W"commit -q -m", "Should be common.rb only", chdir: @target)
+ out = assert_sync()
+ assert_not_equal(@sha["src"], top_commit("src"), out)
+ assert_equal("# main\n", File.read("src/Gemfile"), out)
+ assert_equal(":ok\n""Should.be_merged\n", File.read("src/lib/common.rb"), out)
+ assert_not_operator(File, :exist?, "src/.github/workflows/main.yml", out)
+ end
+
+ def test_delete_after_conflict
+ File.write("#@target/lib/bad.rb", "raise\n")
+ git(*%W"add lib/bad.rb", chdir: @target)
+ git(*%W"commit -q -m", "Add bad.rb", chdir: @target)
+ out = assert_sync
+ assert_equal("raise\n", File.read("src/lib/bad.rb"))
+
+ git(*%W"rm lib/bad.rb", chdir: "src", out: IO::NULL)
+ git(*%W"commit -q -m", "Remove bad.rb", chdir: "src")
+
+ File.write("#@target/lib/bad.rb", "raise 'bar'\n")
+ File.write("#@target/lib/common.rb", "Should.be_merged\n", mode: "a")
+ git(*%W"add lib/bad.rb lib/common.rb", chdir: @target)
+ git(*%W"commit -q -m", "Add conflict", chdir: @target)
+
+ head = top_commit("src")
+ out = assert_sync(editor: "git rm -f lib/bad.rb")
+ assert_not_equal(head, top_commit("src"))
+ assert_equal(":ok\n""Should.be_merged\n", File.read("src/lib/common.rb"), out)
+ assert_not_operator(File, :exist?, "src/lib/bad.rb", out)
end
end
end
diff --git a/tool/test/testunit/test4test_timeout.rb b/tool/test/testunit/test4test_timeout.rb
new file mode 100644
index 0000000000..3225f66398
--- /dev/null
+++ b/tool/test/testunit/test4test_timeout.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../lib"
+
+require 'test/unit'
+require 'timeout'
+
+class TestForTestTimeout < Test::Unit::TestCase
+ 10.times do |i|
+ define_method("test_timeout_#{i}") do
+ Timeout.timeout(0.001) do
+ sleep
+ end
+ end
+ end
+end
diff --git a/tool/test/testunit/test_hideskip.rb b/tool/test/testunit/test_hideskip.rb
index 0cf8f4e4b0..0c4c9b40f2 100644
--- a/tool/test/testunit/test_hideskip.rb
+++ b/tool/test/testunit/test_hideskip.rb
@@ -13,7 +13,7 @@ class TestHideSkip < Test::Unit::TestCase
private
def hideskip(*args)
- IO.popen([*@options[:ruby], "#{File.dirname(__FILE__)}/test4test_hideskip.rb",
+ IO.popen([*@__runner_options__[:ruby], "#{File.dirname(__FILE__)}/test4test_hideskip.rb",
"--verbose", *args], err: [:child, :out]) {|f|
f.read
}
diff --git a/tool/test/testunit/test_launchable.rb b/tool/test/testunit/test_launchable.rb
new file mode 100644
index 0000000000..70c371e212
--- /dev/null
+++ b/tool/test/testunit/test_launchable.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: false
+require 'test/unit'
+require 'tempfile'
+require 'json'
+
+class TestLaunchable < Test::Unit::TestCase
+ def test_json_stream_writer
+ Tempfile.create(['launchable-test-', '.json']) do |f|
+ json_stream_writer = Test::Unit::LaunchableOption::JsonStreamWriter.new(f.path)
+ json_stream_writer.write_array('testCases')
+ json_stream_writer.write_object(
+ {
+ testPath: "file=test/test_a.rb#class=class1#testcase=testcase899",
+ duration: 42,
+ status: "TEST_FAILED",
+ stdout: nil,
+ stderr: nil,
+ createdAt: "2021-10-05T12:34:00",
+ data: {
+ lineNumber: 1
+ }
+ }
+ )
+ json_stream_writer.write_object(
+ {
+ testPath: "file=test/test_a.rb#class=class1#testcase=testcase899",
+ duration: 45,
+ status: "TEST_PASSED",
+ stdout: "This is stdout",
+ stderr: "This is stderr",
+ createdAt: "2021-10-05T12:36:00",
+ data: {
+ lineNumber: 10
+ }
+ }
+ )
+ json_stream_writer.close()
+ expected = <<JSON
+{
+ "testCases": [
+ {
+ "testPath": "file=test/test_a.rb#class=class1#testcase=testcase899",
+ "duration": 42,
+ "status": "TEST_FAILED",
+ "stdout": null,
+ "stderr": null,
+ "createdAt": "2021-10-05T12:34:00",
+ "data": {
+ "lineNumber": 1
+ }
+ },
+ {
+ "testPath": "file=test/test_a.rb#class=class1#testcase=testcase899",
+ "duration": 45,
+ "status": "TEST_PASSED",
+ "stdout": "This is stdout",
+ "stderr": "This is stderr",
+ "createdAt": "2021-10-05T12:36:00",
+ "data": {
+ "lineNumber": 10
+ }
+ }
+ ]
+}
+JSON
+ assert_equal(expected, f.read)
+ end
+ end
+end
diff --git a/tool/test/testunit/test_load_failure.rb b/tool/test/testunit/test_load_failure.rb
index 63db33fd2c..8defa9e39a 100644
--- a/tool/test/testunit/test_load_failure.rb
+++ b/tool/test/testunit/test_load_failure.rb
@@ -13,7 +13,7 @@ class TestLoadFailure < Test::Unit::TestCase
private
def load_failure(*args)
- IO.popen([*@options[:ruby], "#{__dir__}/../runner.rb",
+ IO.popen([*@__runner_options__[:ruby], "#{__dir__}/../runner.rb",
"#{__dir__}/test4test_load_failure.rb",
"--verbose", *args], err: [:child, :out]) {|f|
assert_include(f.read, "test4test_load_failure.rb")
diff --git a/tool/test/testunit/test_parallel.rb b/tool/test/testunit/test_parallel.rb
index 29176483b5..6882fd6c5f 100644
--- a/tool/test/testunit/test_parallel.rb
+++ b/tool/test/testunit/test_parallel.rb
@@ -12,8 +12,8 @@ module TestParallel
def setup
i, @worker_in = IO.pipe
@worker_out, o = IO.pipe
- @worker_pid = spawn(*@options[:ruby], PARALLEL_RB,
- "--ruby", @options[:ruby].join(" "),
+ @worker_pid = spawn(*@__runner_options__[:ruby], PARALLEL_RB,
+ "--ruby", @__runner_options__[:ruby].join(" "),
"-j", "t1", "-v", out: o, in: i)
[i,o].each(&:close)
end
@@ -119,7 +119,7 @@ module TestParallel
result = Marshal.load($1.chomp.unpack1("m"))
assert_equal(5, result[0])
- pend "TODO: result[1] returns 17. We should investigate it" do
+ pend "TODO: result[1] returns 17. We should investigate it" do # TODO: misusage of pend (pend doens't use given block)
assert_equal(12, result[1])
end
assert_kind_of(Array,result[2])
@@ -143,11 +143,11 @@ module TestParallel
end
class TestParallel < Test::Unit::TestCase
- def spawn_runner(*opt_args)
+ def spawn_runner(*opt_args, jobs: "t1")
@test_out, o = IO.pipe
- @test_pid = spawn(*@options[:ruby], TESTS+"/runner.rb",
- "--ruby", @options[:ruby].join(" "),
- "-j","t1",*opt_args, out: o, err: o)
+ @test_pid = spawn(*@__runner_options__[:ruby], TESTS+"/runner.rb",
+ "--ruby", @__runner_options__[:ruby].join(" "),
+ "-j", jobs, *opt_args, out: o, err: o)
o.close
end
@@ -166,11 +166,7 @@ module TestParallel
end
def test_ignore_jzero
- @test_out, o = IO.pipe
- @test_pid = spawn(*@options[:ruby], TESTS+"/runner.rb",
- "--ruby", @options[:ruby].join(" "),
- "-j","0", out: File::NULL, err: o)
- o.close
+ spawn_runner(jobs: "0")
Timeout.timeout(TIMEOUT) {
assert_match(/Error: parameter of -j option should be greater than 0/,@test_out.read)
}
@@ -215,5 +211,12 @@ module TestParallel
assert_match(/^Retrying hung up testcases\.+$/, buf)
assert_match(/^2 tests,.* 0 failures,/, buf)
end
+
+ def test_retry_workers
+ spawn_runner "--worker-timeout=1", "test4test_slow_0.rb", "test4test_slow_1.rb", jobs: "2"
+ buf = Timeout.timeout(TIMEOUT) {@test_out.read}
+ assert_match(/^Retrying hung up testcases\.+$/, buf)
+ assert_match(/^2 tests,.* 0 failures,/, buf)
+ end
end
end
diff --git a/tool/test/testunit/test_sorting.rb b/tool/test/testunit/test_sorting.rb
index 7678249ec2..3e5d7bfdcc 100644
--- a/tool/test/testunit/test_sorting.rb
+++ b/tool/test/testunit/test_sorting.rb
@@ -10,7 +10,7 @@ class TestTestUnitSorting < Test::Unit::TestCase
end
def sorting(*args)
- IO.popen([*@options[:ruby], "#{File.dirname(__FILE__)}/test4test_sorting.rb",
+ IO.popen([*@__runner_options__[:ruby], "#{File.dirname(__FILE__)}/test4test_sorting.rb",
"--verbose", *args], err: [:child, :out]) {|f|
f.read
}
diff --git a/tool/test/testunit/test_timeout.rb b/tool/test/testunit/test_timeout.rb
new file mode 100644
index 0000000000..452f5e1a7e
--- /dev/null
+++ b/tool/test/testunit/test_timeout.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: false
+require 'test/unit'
+
+class TestTiemout < Test::Unit::TestCase
+ def test_timeout
+ cmd = [*@__runner_options__[:ruby], "#{File.dirname(__FILE__)}/test4test_timeout.rb"]
+ result = IO.popen(cmd, err: [:child, :out], &:read)
+ assert_not_match(/^T{10}$/, result)
+ end
+end
diff --git a/tool/test/testunit/tests_for_parallel/slow_helper.rb b/tool/test/testunit/tests_for_parallel/slow_helper.rb
new file mode 100644
index 0000000000..38067c1f47
--- /dev/null
+++ b/tool/test/testunit/tests_for_parallel/slow_helper.rb
@@ -0,0 +1,8 @@
+require 'test/unit'
+
+module TestSlowTimeout
+ def test_slow
+ sleep_for = EnvUtil.apply_timeout_scale((ENV['sec'] || 3).to_i)
+ sleep sleep_for if on_parallel_worker?
+ end
+end
diff --git a/tool/test/testunit/tests_for_parallel/test4test_slow_0.rb b/tool/test/testunit/tests_for_parallel/test4test_slow_0.rb
new file mode 100644
index 0000000000..a749b0e1d3
--- /dev/null
+++ b/tool/test/testunit/tests_for_parallel/test4test_slow_0.rb
@@ -0,0 +1,5 @@
+require_relative 'slow_helper'
+
+class TestSlowV0 < Test::Unit::TestCase
+ include TestSlowTimeout
+end
diff --git a/tool/test/testunit/tests_for_parallel/test4test_slow_1.rb b/tool/test/testunit/tests_for_parallel/test4test_slow_1.rb
new file mode 100644
index 0000000000..924a3b11fa
--- /dev/null
+++ b/tool/test/testunit/tests_for_parallel/test4test_slow_1.rb
@@ -0,0 +1,5 @@
+require_relative 'slow_helper'
+
+class TestSlowV1 < Test::Unit::TestCase
+ include TestSlowTimeout
+end
diff --git a/tool/test/webrick/test_cgi.rb b/tool/test/webrick/test_cgi.rb
index 7a75cf565e..a9be8f353d 100644
--- a/tool/test/webrick/test_cgi.rb
+++ b/tool/test/webrick/test_cgi.rb
@@ -12,30 +12,8 @@ class TestWEBrickCGI < Test::Unit::TestCase
super
end
- def start_cgi_server(log_tester=TestWEBrick::DefaultLogTester, &block)
- config = {
- :CGIInterpreter => TestWEBrick::RubyBin,
- :DocumentRoot => File.dirname(__FILE__),
- :DirectoryIndex => ["webrick.cgi"],
- :RequestCallback => Proc.new{|req, res|
- def req.meta_vars
- meta = super
- meta["RUBYLIB"] = $:.join(File::PATH_SEPARATOR)
- meta[RbConfig::CONFIG['LIBPATHENV']] = ENV[RbConfig::CONFIG['LIBPATHENV']] if RbConfig::CONFIG['LIBPATHENV']
- return meta
- end
- },
- }
- if RUBY_PLATFORM =~ /mswin|mingw|cygwin|bccwin32/
- config[:CGIPathEnv] = ENV['PATH'] # runtime dll may not be in system dir.
- end
- TestWEBrick.start_httpserver(config, log_tester){|server, addr, port, log|
- block.call(server, addr, port, log)
- }
- end
-
def test_cgi
- start_cgi_server{|server, addr, port, log|
+ TestWEBrick.start_cgi_server{|server, addr, port, log|
http = Net::HTTP.new(addr, port)
req = Net::HTTP::Get.new("/webrick.cgi")
http.request(req){|res| assert_equal("/webrick.cgi", res.body, log.call)}
@@ -98,7 +76,7 @@ class TestWEBrickCGI < Test::Unit::TestCase
log_tester = lambda {|log, access_log|
assert_match(/BadRequest/, log.join)
}
- start_cgi_server(log_tester) {|server, addr, port, log|
+ TestWEBrick.start_cgi_server({}, log_tester) {|server, addr, port, log|
sock = TCPSocket.new(addr, port)
begin
sock << "POST /webrick.cgi HTTP/1.0" << CRLF
@@ -115,7 +93,7 @@ class TestWEBrickCGI < Test::Unit::TestCase
end
def test_cgi_env
- start_cgi_server do |server, addr, port, log|
+ TestWEBrick.start_cgi_server do |server, addr, port, log|
http = Net::HTTP.new(addr, port)
req = Net::HTTP::Get.new("/webrick.cgi/dumpenv")
req['proxy'] = 'http://example.com/'
@@ -137,7 +115,7 @@ class TestWEBrickCGI < Test::Unit::TestCase
assert_equal(1, log.length)
assert_match(/ERROR bad URI/, log[0])
}
- start_cgi_server(log_tester) {|server, addr, port, log|
+ TestWEBrick.start_cgi_server({}, log_tester) {|server, addr, port, log|
res = TCPSocket.open(addr, port) {|sock|
sock << "GET /#{CtrlSeq}#{CRLF}#{CRLF}"
sock.close_write
@@ -155,7 +133,7 @@ class TestWEBrickCGI < Test::Unit::TestCase
assert_equal(1, log.length)
assert_match(/ERROR bad header/, log[0])
}
- start_cgi_server(log_tester) {|server, addr, port, log|
+ TestWEBrick.start_cgi_server({}, log_tester) {|server, addr, port, log|
res = TCPSocket.open(addr, port) {|sock|
sock << "GET / HTTP/1.0#{CRLF}#{CtrlSeq}#{CRLF}#{CRLF}"
sock.close_write
diff --git a/tool/test/webrick/test_filehandler.rb b/tool/test/webrick/test_filehandler.rb
index 9c5b83e300..452667d4f4 100644
--- a/tool/test/webrick/test_filehandler.rb
+++ b/tool/test/webrick/test_filehandler.rb
@@ -247,22 +247,16 @@ class WEBrick::TestFileHandler < Test::Unit::TestCase
def test_short_filename
return if File.executable?(__FILE__) # skip on strange file system
- return if /mswin/ =~ RUBY_PLATFORM && ENV.key?('GITHUB_ACTIONS') # not working from the beginning
- config = {
- :CGIInterpreter => TestWEBrick::RubyBin,
- :DocumentRoot => File.dirname(__FILE__),
- :CGIPathEnv => ENV['PATH'],
- }
log_tester = lambda {|log, access_log|
log = log.reject {|s| /ERROR `.*\' not found\./ =~ s }
log = log.reject {|s| /WARN the request refers nondisclosure name/ =~ s }
assert_equal([], log)
}
- TestWEBrick.start_httpserver(config, log_tester) do |server, addr, port, log|
+ TestWEBrick.start_cgi_server({}, log_tester) do |server, addr, port, log|
http = Net::HTTP.new(addr, port)
if windows?
- root = config[:DocumentRoot].tr("/", "\\")
+ root = File.dirname(__FILE__).tr("/", "\\")
fname = IO.popen(%W[dir /x #{root}\\webrick_long_filename.cgi], encoding: "binary", &:read)
fname.sub!(/\A.*$^$.*$^$/m, '')
if fname
diff --git a/tool/test/webrick/utils.rb b/tool/test/webrick/utils.rb
index a8568d0a43..c8e84c37f1 100644
--- a/tool/test/webrick/utils.rb
+++ b/tool/test/webrick/utils.rb
@@ -81,4 +81,24 @@ module TestWEBrick
def start_httpproxy(config={}, log_tester=DefaultLogTester, &block)
start_server(WEBrick::HTTPProxyServer, config, log_tester, &block)
end
+
+ def start_cgi_server(config={}, log_tester=TestWEBrick::DefaultLogTester, &block)
+ config = {
+ :CGIInterpreter => TestWEBrick::RubyBin,
+ :DocumentRoot => File.dirname(__FILE__),
+ :DirectoryIndex => ["webrick.cgi"],
+ :RequestCallback => Proc.new{|req, res|
+ def req.meta_vars
+ meta = super
+ meta["RUBYLIB"] = $:.join(File::PATH_SEPARATOR)
+ meta[RbConfig::CONFIG['LIBPATHENV']] = ENV[RbConfig::CONFIG['LIBPATHENV']] if RbConfig::CONFIG['LIBPATHENV']
+ return meta
+ end
+ },
+ }.merge(config)
+ if RUBY_PLATFORM =~ /mswin|mingw|cygwin|bccwin32/
+ config[:CGIPathEnv] = ENV['PATH'] # runtime dll may not be in system dir.
+ end
+ start_server(WEBrick::HTTPServer, config, log_tester, &block)
+ end
end
diff --git a/tool/test/webrick/webrick.cgi b/tool/test/webrick/webrick.cgi
index a294fa72f9..45594b7a7b 100644..100755
--- a/tool/test/webrick/webrick.cgi
+++ b/tool/test/webrick/webrick.cgi
@@ -15,11 +15,11 @@ class TestApp < WEBrick::CGI
}.join(", ")
}.join(", ")
elsif %r{/$} =~ req.request_uri.to_s
- res.body = ""
+ res.body = +""
res.body << req.request_uri.to_s << "\n"
res.body << req.script_name
elsif !req.cookies.empty?
- res.body = req.cookies.inject(""){|result, cookie|
+ res.body = req.cookies.inject(+""){|result, cookie|
result << "%s=%s\n" % [cookie.name, cookie.value]
}
res.cookies << WEBrick::Cookie.new("Customer", "WILE_E_COYOTE")
diff --git a/tool/test_for_warn_bundled_gems/.gitignore b/tool/test_for_warn_bundled_gems/.gitignore
new file mode 100644
index 0000000000..a9a5aecf42
--- /dev/null
+++ b/tool/test_for_warn_bundled_gems/.gitignore
@@ -0,0 +1 @@
+tmp
diff --git a/tool/test_for_warn_bundled_gems/Gemfile b/tool/test_for_warn_bundled_gems/Gemfile
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tool/test_for_warn_bundled_gems/Gemfile
diff --git a/tool/test_for_warn_bundled_gems/Gemfile.lock b/tool/test_for_warn_bundled_gems/Gemfile.lock
new file mode 100644
index 0000000000..003cb81444
--- /dev/null
+++ b/tool/test_for_warn_bundled_gems/Gemfile.lock
@@ -0,0 +1,11 @@
+GEM
+ specs:
+
+PLATFORMS
+ arm64-darwin-22
+ ruby
+
+DEPENDENCIES
+
+BUNDLED WITH
+ 2.5.0.dev
diff --git a/tool/test_for_warn_bundled_gems/README.md b/tool/test_for_warn_bundled_gems/README.md
new file mode 100644
index 0000000000..dc2d2a6cb9
--- /dev/null
+++ b/tool/test_for_warn_bundled_gems/README.md
@@ -0,0 +1,3 @@
+This directory contains tests for the bundled gems warning under the Bundler.
+
+see [test.sh](./test.sh) for details.
diff --git a/tool/test_for_warn_bundled_gems/test.sh b/tool/test_for_warn_bundled_gems/test.sh
new file mode 100755
index 0000000000..a14d5bcedc
--- /dev/null
+++ b/tool/test_for_warn_bundled_gems/test.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+echo "* Show warning require and LoadError"
+ruby test_warn_bundled_gems.rb
+echo
+
+echo "* Show warning when bundled gems called as dependency"
+ruby test_warn_dependency.rb
+echo
+
+echo "* Show warning sub-feature like bigdecimal/util"
+ruby test_warn_sub_feature.rb
+echo
+
+echo "* Show warning dash gem like net/smtp"
+ruby test_warn_dash_gem.rb
+echo
+
+echo "* Show warning when bundle exec with ruby and script"
+bundle exec ruby test_warn_bundle_exec.rb
+echo
+
+echo "* Show warning when bundle exec with shebang's script"
+bundle exec ./test_warn_bundle_exec_shebang.rb
+echo
+
+echo "* Show warning when bundle exec with -r option"
+bundle exec ruby -rostruct -e ''
+echo
+
+echo "* Show warning with bootsnap"
+ruby test_warn_bootsnap.rb
+echo
+
+echo "* Show warning with bootsnap for gem with native extension"
+ruby test_warn_bootsnap_rubyarchdir_gem.rb
+echo
+
+echo "* Show warning with zeitwerk"
+ruby test_warn_zeitwerk.rb
+echo
+
+echo "* Don't show warning bundled gems on Gemfile"
+ruby test_no_warn_dependency.rb
+echo
+
+echo "* Don't show warning with net/smtp when net-smtp on Gemfile"
+ruby test_no_warn_dash_gem.rb
+echo
+
+echo "* Don't show warning bigdecimal/util when bigdecimal on Gemfile"
+ruby test_no_warn_sub_feature.rb
+echo
diff --git a/tool/test_for_warn_bundled_gems/test_no_warn_dash_gem.rb b/tool/test_for_warn_bundled_gems/test_no_warn_dash_gem.rb
new file mode 100644
index 0000000000..72ae23b040
--- /dev/null
+++ b/tool/test_for_warn_bundled_gems/test_no_warn_dash_gem.rb
@@ -0,0 +1,8 @@
+require "bundler/inline"
+
+gemfile do
+ source "https://rubygems.org"
+ gem "net-smtp"
+end
+
+require "net/smtp"
diff --git a/tool/test_for_warn_bundled_gems/test_no_warn_dependency.rb b/tool/test_for_warn_bundled_gems/test_no_warn_dependency.rb
new file mode 100644
index 0000000000..94a32a9108
--- /dev/null
+++ b/tool/test_for_warn_bundled_gems/test_no_warn_dependency.rb
@@ -0,0 +1,10 @@
+require "bundler/inline"
+
+gemfile do
+ source "https://rubygems.org"
+ gem "activesupport", "7.0.7.2"
+ gem "bigdecimal"
+ gem "mutex_m"
+end
+
+require "active_support/all"
diff --git a/tool/test_for_warn_bundled_gems/test_no_warn_sub_feature.rb b/tool/test_for_warn_bundled_gems/test_no_warn_sub_feature.rb
new file mode 100644
index 0000000000..7d62a2f9d0
--- /dev/null
+++ b/tool/test_for_warn_bundled_gems/test_no_warn_sub_feature.rb
@@ -0,0 +1,8 @@
+require "bundler/inline"
+
+gemfile do
+ source "https://rubygems.org"
+ gem "bigdecimal"
+end
+
+require "bigdecimal/util"
diff --git a/tool/test_for_warn_bundled_gems/test_warn_bootsnap.rb b/tool/test_for_warn_bundled_gems/test_warn_bootsnap.rb
new file mode 100644
index 0000000000..eac58de974
--- /dev/null
+++ b/tool/test_for_warn_bundled_gems/test_warn_bootsnap.rb
@@ -0,0 +1,11 @@
+require "bundler/inline"
+
+gemfile do
+ source "https://rubygems.org"
+ gem "bootsnap", require: false
+end
+
+require 'bootsnap'
+Bootsnap.setup(cache_dir: 'tmp/cache')
+
+require 'csv'
diff --git a/tool/test_for_warn_bundled_gems/test_warn_bootsnap_rubyarchdir_gem.rb b/tool/test_for_warn_bundled_gems/test_warn_bootsnap_rubyarchdir_gem.rb
new file mode 100644
index 0000000000..477933f6f2
--- /dev/null
+++ b/tool/test_for_warn_bundled_gems/test_warn_bootsnap_rubyarchdir_gem.rb
@@ -0,0 +1,11 @@
+require "bundler/inline"
+
+gemfile do
+ source "https://rubygems.org"
+ gem "bootsnap", require: false
+end
+
+require 'bootsnap'
+Bootsnap.setup(cache_dir: 'tmp/cache')
+
+require 'syslog'
diff --git a/tool/test_for_warn_bundled_gems/test_warn_bundle_exec.rb b/tool/test_for_warn_bundled_gems/test_warn_bundle_exec.rb
new file mode 100644
index 0000000000..30db47ce61
--- /dev/null
+++ b/tool/test_for_warn_bundled_gems/test_warn_bundle_exec.rb
@@ -0,0 +1 @@
+require "base64"
diff --git a/tool/test_for_warn_bundled_gems/test_warn_bundle_exec_shebang.rb b/tool/test_for_warn_bundled_gems/test_warn_bundle_exec_shebang.rb
new file mode 100755
index 0000000000..0338928e1e
--- /dev/null
+++ b/tool/test_for_warn_bundled_gems/test_warn_bundle_exec_shebang.rb
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+
+require "base64"
diff --git a/tool/test_for_warn_bundled_gems/test_warn_bundled_gems.rb b/tool/test_for_warn_bundled_gems/test_warn_bundled_gems.rb
new file mode 100644
index 0000000000..13168292e3
--- /dev/null
+++ b/tool/test_for_warn_bundled_gems/test_warn_bundled_gems.rb
@@ -0,0 +1,8 @@
+require "bundler/inline"
+
+gemfile do
+ source "https://rubygems.org"
+end
+
+require "mutex_m"
+require "rss"
diff --git a/tool/test_for_warn_bundled_gems/test_warn_dash_gem.rb b/tool/test_for_warn_bundled_gems/test_warn_dash_gem.rb
new file mode 100644
index 0000000000..04ef2a52c0
--- /dev/null
+++ b/tool/test_for_warn_bundled_gems/test_warn_dash_gem.rb
@@ -0,0 +1,7 @@
+require "bundler/inline"
+
+gemfile do
+ source "https://rubygems.org"
+end
+
+require "net/smtp"
diff --git a/tool/test_for_warn_bundled_gems/test_warn_dependency.rb b/tool/test_for_warn_bundled_gems/test_warn_dependency.rb
new file mode 100644
index 0000000000..9be3a2f6d9
--- /dev/null
+++ b/tool/test_for_warn_bundled_gems/test_warn_dependency.rb
@@ -0,0 +1,8 @@
+require "bundler/inline"
+
+gemfile do
+ source "https://rubygems.org"
+ gem "activesupport", "7.0.7.2"
+end
+
+require "active_support/all"
diff --git a/tool/test_for_warn_bundled_gems/test_warn_sub_feature.rb b/tool/test_for_warn_bundled_gems/test_warn_sub_feature.rb
new file mode 100644
index 0000000000..bf7eb3572d
--- /dev/null
+++ b/tool/test_for_warn_bundled_gems/test_warn_sub_feature.rb
@@ -0,0 +1,7 @@
+require "bundler/inline"
+
+gemfile do
+ source "https://rubygems.org"
+end
+
+require "bigdecimal/util"
diff --git a/tool/test_for_warn_bundled_gems/test_warn_zeitwerk.rb b/tool/test_for_warn_bundled_gems/test_warn_zeitwerk.rb
new file mode 100644
index 0000000000..d554a0e675
--- /dev/null
+++ b/tool/test_for_warn_bundled_gems/test_warn_zeitwerk.rb
@@ -0,0 +1,12 @@
+require "bundler/inline"
+
+gemfile do
+ source "https://rubygems.org"
+ gem "zeitwerk", require: false
+end
+
+require "zeitwerk"
+loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
+loader.setup
+
+require 'csv'
diff --git a/tool/transcode-tblgen.rb b/tool/transcode-tblgen.rb
index b19f68bac4..1257a92d38 100644
--- a/tool/transcode-tblgen.rb
+++ b/tool/transcode-tblgen.rb
@@ -1078,11 +1078,7 @@ if __FILE__ == $0
end
libs1 = $".dup
- if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
- erb = ERB.new(src, trim_mode: '%')
- else
- erb = ERB.new(src, nil, '%')
- end
+ erb = ERB.new(src, trim_mode: '%')
erb.filename = arg
erb_result = erb.result(binding)
libs2 = $".dup
diff --git a/tool/update-NEWS-gemlist.rb b/tool/update-NEWS-gemlist.rb
index aa766b8846..8e4d39046b 100755
--- a/tool/update-NEWS-gemlist.rb
+++ b/tool/update-NEWS-gemlist.rb
@@ -1,4 +1,4 @@
-#!/usr/bin/ruby
+#!/usr/bin/env ruby
require 'json'
news = File.read("NEWS.md")
prev = news[/since the \*+(\d+\.\d+\.\d+)\*+/, 1]
@@ -28,12 +28,14 @@ ARGV.each do |type|
next unless v
[g, v] unless last[g] == v
end
- if type == 'bundled'
- changed, added = changed.partition {|g, _| last[g]}
- end
+ changed, added = changed.partition {|g, _| last[g]}
update[changed, type] or next
if added and !added.empty?
- update[added, 'default', 'now bundled'] or next
+ if type == 'bundled'
+ update[added, type, 'promoted from default gems'] or next
+ else
+ update[added, type, 'added'] or next
+ end
end
File.write("NEWS.md", news)
end
diff --git a/tool/update-NEWS-refs.rb b/tool/update-NEWS-refs.rb
index 2b19f0fdaa..f48cac5ee1 100644
--- a/tool/update-NEWS-refs.rb
+++ b/tool/update-NEWS-refs.rb
@@ -13,8 +13,9 @@ if links.empty? || lines.last != ""
raise "NEWS.md must end with a sequence of links"
end
-labels = links.keys.select {|k| !(k.start_with?("Feature") || k.start_with?("Bug"))}
-new_src = lines.join("\n").gsub(/\[?\[((?:Feature|Bug)\s+#(\d+))\]\]?/) do
+trackers = ["Feature", "Bug", "Misc"]
+labels = links.keys.reject {|k| k.start_with?(*trackers)}
+new_src = lines.join("\n").gsub(/\[?\[(#{Regexp.union(trackers)}\s+#(\d+))\]\]?/) do
links[$1] ||= "https://bugs.ruby-lang.org/issues/#$2"
"[[#$1]]"
end.gsub(/\[\[#{Regexp.union(labels)}\]\]?/) do
@@ -22,7 +23,7 @@ end.gsub(/\[\[#{Regexp.union(labels)}\]\]?/) do
end.chomp + "\n\n"
label_width = links.max_by {|k, _| k.size}.first.size + 4
-redmine_links, non_redmine_links = links.partition {|k,| k =~ /\A(Feature|Bug)\s+#\d+\z/ }
+redmine_links, non_redmine_links = links.partition {|k,| k =~ /\A#{Regexp.union(trackers)}\s+#\d+\z/ }
(redmine_links.sort_by {|k,| k[/\d+/].to_i } + non_redmine_links.reverse).each do |k, v|
new_src << "[#{k}]:".ljust(label_width) << v << "\n"
diff --git a/tool/update-bundled_gems.rb b/tool/update-bundled_gems.rb
index 688ca34086..2842516cac 100755
--- a/tool/update-bundled_gems.rb
+++ b/tool/update-bundled_gems.rb
@@ -1,27 +1,38 @@
#!ruby -pla
BEGIN {
require 'rubygems'
+ date = nil
+ # STDOUT is not usable in inplace edit mode
+ output = $-i ? STDOUT : STDERR
+}
+output = STDERR if ARGF.file == STDIN
+END {
+ output.print date.strftime("latest_date=%F") if date
}
unless /^[^#]/ !~ (gem = $F[0])
+ ver = Gem::Version.new($F[1])
(gem, src), = Gem::SpecFetcher.fetcher.detect(:latest) {|s|
s.platform == "ruby" && s.name == gem
}
- gem = src.fetch_spec(gem)
- if ENV["UPDATE_BUNDLED_GEMS_ALL"]
- uri = gem.metadata["source_code_uri"] || gem.homepage
- uri = uri.sub(%r[\Ahttps://github\.com/[^/]+/[^/]+\K/tree/.*], "").chomp(".git")
- else
- uri = $F[2]
- end
- if $F[3]
- if $F[3].include?($F[1])
- $F[3][$F[1]] = gem.version.to_s
- elsif Gem::Version.new($F[1]) != gem.version and /\A\h+\z/ =~ $F[3]
- $F[3..-1] = []
+ if gem.version > ver
+ gem = src.fetch_spec(gem)
+ if ENV["UPDATE_BUNDLED_GEMS_ALL"]
+ uri = gem.metadata["source_code_uri"] || gem.homepage
+ uri = uri.sub(%r[\Ahttps://github\.com/[^/]+/[^/]+\K/tree/.*], "").chomp(".git")
+ else
+ uri = $F[2]
+ end
+ date = gem.date if !date or gem.date && gem.date > date
+ if $F[3]
+ if $F[3].include?($F[1])
+ $F[3][$F[1]] = gem.version.to_s
+ elsif Gem::Version.new($F[1]) != gem.version and /\A\h+\z/ =~ $F[3]
+ $F[3..-1] = []
+ end
end
+ f = [gem.name, gem.version.to_s, uri, *$F[3..-1]]
+ $_.gsub!(/\S+\s*(?=\s|$)/) {|s| (f.shift || "").ljust(s.size)}
+ $_ = [$_, *f].join(" ") unless f.empty?
+ $_.rstrip!
end
- f = [gem.name, gem.version.to_s, uri, *$F[3..-1]]
- $_.gsub!(/\S+\s*/) {|s| (f.shift || "").ljust(s.size)}
- $_ = [$_, *f].join(" ") unless f.empty?
- $_.rstrip!
end
diff --git a/tool/update-deps b/tool/update-deps
index 6b8bb67236..0b90876cd2 100755
--- a/tool/update-deps
+++ b/tool/update-deps
@@ -111,6 +111,7 @@ FILES_NEED_VPATH = %w[
ext/ripper/eventids1.c
ext/ripper/eventids2table.c
ext/ripper/ripper.c
+ ext/ripper/ripper_init.c
golf_prelude.c
id.c
id.h
@@ -121,7 +122,6 @@ FILES_NEED_VPATH = %w[
miniprelude.c
newline.c
node_name.inc
- opt_sc.inc
optinsn.inc
optunifs.inc
parse.c
@@ -149,6 +149,16 @@ FILES_NEED_VPATH = %w[
enc/trans/single_byte.c
enc/trans/utf8_mac.c
enc/trans/utf_16_32.c
+
+ prism/api_node.c
+ prism/ast.h
+ prism/diagnostic.c
+ prism/diagnostic.h
+ prism/node.c
+ prism/prettyprint.c
+ prism/serialize.c
+ prism/token_type.c
+ prism/version.h
]
# Multiple files with same filename.
@@ -166,13 +176,17 @@ FILES_SAME_NAME_TOP = %w[
version.h
]
+# Files that may or may not exist on CI for some reason.
+# Windows build generally seems to have missing dependencies.
+UNSTABLE_FILES = %r{\Awin32/[^/]+\.o\z}
+
# Other source files exist in the source directory.
def in_makefile(target, source)
target = target.to_s
source = source.to_s
case target
- when %r{\A[^/]*\z}, %r{\Acoroutine/}
+ when %r{\A[^/]*\z}, %r{\Acoroutine/}, %r{\Aprism/}
target2 = "#{target.sub(/\.o\z/, '.$(OBJEXT)')}"
case source
when *FILES_IN_SOURCE_DIRECTORY then source2 = "$(top_srcdir)/#{source}"
@@ -229,6 +243,10 @@ def in_makefile(target, source)
else source2 = "$(top_srcdir)/#{source}"
end
["#{File.dirname(target)}/depend", target2, source2]
+ # Files that may or may not exist on CI for some reason.
+ # Windows build generally seems to have missing dependencies.
+ when UNSTABLE_FILES
+ warn "warning: ignoring: #{target}"
else
raise "unexpected target: #{target}"
end
@@ -471,7 +489,7 @@ def compare_deps(make_deps, cc_deps, out=$stdout)
}
}
- makefiles.keys.sort.each {|makefile|
+ makefiles.keys.compact.sort.each {|makefile|
cc_lines = cc_lines_hash[makefile] || Hash.new(false)
make_lines = make_lines_hash[makefile] || Hash.new(false)
content = begin
diff --git a/trace_point.rb b/trace_point.rb
index ac4086589c..b136e9b399 100644
--- a/trace_point.rb
+++ b/trace_point.rb
@@ -1,7 +1,5 @@
# loaded from vm_trace.c
-# Document-class: TracePoint
-#
# A class that provides the functionality of Kernel#set_trace_func in a
# nice Object-Oriented API.
#
@@ -39,6 +37,7 @@
# +:c_call+:: call a C-language routine
# +:c_return+:: return from a C-language routine
# +:raise+:: raise an exception
+# +:rescue+:: rescue an exception
# +:b_call+:: event hook at block entry
# +:b_return+:: event hook at block ending
# +:a_call+:: event hook at all calls (+call+, +b_call+, and +c_call+)
@@ -95,6 +94,7 @@ class TracePoint
# Access from other threads is also forbidden.
#
def self.new(*events)
+ Primitive.attr! :use_block
Primitive.tracepoint_new_s(events)
end
@@ -132,6 +132,7 @@ class TracePoint
# trace.enabled? #=> true
#
def self.trace(*events)
+ Primitive.attr! :use_block
Primitive.tracepoint_trace_s(events)
end
@@ -197,6 +198,7 @@ class TracePoint
# out calls by itself from :line handler, otherwise it will call itself infinitely).
#
def self.allow_reentry
+ Primitive.attr! :use_block
Primitive.tracepoint_allow_reentry
end
@@ -259,6 +261,7 @@ class TracePoint
# #=> RuntimeError: access from outside
#
def enable(target: nil, target_line: nil, target_thread: :default)
+ Primitive.attr! :use_block
Primitive.tracepoint_enable_m(target, target_line, target_thread)
end
@@ -295,6 +298,7 @@ class TracePoint
# trace.disable { p tp.lineno }
# #=> RuntimeError: access from outside
def disable
+ Primitive.attr! :use_block
Primitive.tracepoint_disable_m
end
@@ -377,7 +381,7 @@ class TracePoint
# Return the generated binding object from event.
#
- # Note that for +c_call+ and +c_return+ events, the method will return
+ # Note that for +:c_call+ and +:c_return+ events, the method will return
# +nil+, since C methods themselves do not have bindings.
def binding
Primitive.tracepoint_attr_binding
@@ -386,19 +390,19 @@ class TracePoint
# Return the trace object during event
#
# Same as the following, except it returns the correct object (the method
- # receiver) for +c_call+ and +c_return+ events:
+ # receiver) for +:c_call+ and +:c_return+ events:
#
# trace.binding.eval('self')
def self
Primitive.tracepoint_attr_self
end
- # Return value from +:return+, +c_return+, and +b_return+ event
+ # Return value from +:return+, +:c_return+, and +:b_return+ event
def return_value
Primitive.tracepoint_attr_return_value
end
- # Value from exception raised on the +:raise+ event
+ # Value from exception raised on the +:raise+ event, or rescued on the +:rescue+ event.
def raised_exception
Primitive.tracepoint_attr_raised_exception
end
diff --git a/transcode.c b/transcode.c
index e6c4f85f83..892814ee3b 100644
--- a/transcode.c
+++ b/transcode.c
@@ -181,6 +181,28 @@ typedef struct {
static st_table *transcoder_table;
+static int
+free_inner_transcode_i(st_data_t key, st_data_t val, st_data_t arg)
+{
+ xfree((void *)val);
+ return ST_DELETE;
+}
+
+static int
+free_transcode_i(st_data_t key, st_data_t val, st_data_t arg)
+{
+ st_foreach((void *)val, free_inner_transcode_i, 0);
+ st_free_table((void *)val);
+ return ST_DELETE;
+}
+
+void
+rb_free_transcoder_table(void)
+{
+ st_foreach(transcoder_table, free_transcode_i, 0);
+ st_free_table(transcoder_table);
+}
+
static transcoder_entry_t *
make_transcoder_entry(const char *sname, const char *dname)
{
@@ -675,7 +697,7 @@ transcode_restartable0(const unsigned char **in_pos, unsigned char **out_pos,
}
break;
}
- case FUNsio:
+ case FUNsio:
{
const unsigned char *char_start;
size_t char_len;
@@ -1715,8 +1737,7 @@ rb_econv_close(rb_econv_t *ec)
}
for (i = 0; i < ec->num_trans; i++) {
rb_transcoding_close(ec->elems[i].tc);
- if (ec->elems[i].out_buf_start)
- xfree(ec->elems[i].out_buf_start);
+ xfree(ec->elems[i].out_buf_start);
}
xfree(ec->in_buf_start);
xfree(ec->elems);
diff --git a/transient_heap.c b/transient_heap.c
deleted file mode 100644
index 6859926f93..0000000000
--- a/transient_heap.c
+++ /dev/null
@@ -1,976 +0,0 @@
-/**********************************************************************
-
- transient_heap.c - implement transient_heap.
-
- Copyright (C) 2018 Koichi Sasada
-
-**********************************************************************/
-
-#include "debug_counter.h"
-#include "internal.h"
-#include "internal/array.h"
-#include "internal/gc.h"
-#include "internal/sanitizers.h"
-#include "internal/static_assert.h"
-#include "internal/struct.h"
-#include "internal/variable.h"
-#include "ruby/debug.h"
-#include "ruby/ruby.h"
-#include "ruby_assert.h"
-#include "transient_heap.h"
-#include "vm_debug.h"
-#include "vm_sync.h"
-
-#if USE_TRANSIENT_HEAP /* USE_TRANSIENT_HEAP */
-/*
- * 1: enable assertions
- * 2: enable verify all transient heaps
- */
-#ifndef TRANSIENT_HEAP_CHECK_MODE
-#define TRANSIENT_HEAP_CHECK_MODE 0
-#endif
-#define TH_ASSERT(expr) RUBY_ASSERT_MESG_WHEN(TRANSIENT_HEAP_CHECK_MODE > 0, expr, #expr)
-
-/*
- * 1: show events
- * 2: show dump at events
- * 3: show all operations
- */
-#define TRANSIENT_HEAP_DEBUG 0
-
-/* For Debug: Provide blocks infinitely.
- * This mode generates blocks unlimitedly
- * and prohibit access free'ed blocks to check invalid access.
- */
-#define TRANSIENT_HEAP_DEBUG_INFINITE_BLOCK 0
-
-#if TRANSIENT_HEAP_DEBUG_INFINITE_BLOCK
-#include <sys/mman.h>
-#include <errno.h>
-#endif
-
-/* For Debug: Prohibit promoting to malloc space.
- */
-#define TRANSIENT_HEAP_DEBUG_DONT_PROMOTE 0
-
-/* size configuration */
-#define TRANSIENT_HEAP_PROMOTED_DEFAULT_SIZE 1024
-
- /* K M */
-#define TRANSIENT_HEAP_BLOCK_SIZE (1024 * 32 ) /* 32KB int16_t */
-#ifndef TRANSIENT_HEAP_TOTAL_SIZE
-#define TRANSIENT_HEAP_TOTAL_SIZE (1024 * 1024 * 32) /* 32 MB */
-#endif
-#define TRANSIENT_HEAP_ALLOC_MAX (1024 * 2 ) /* 2 KB */
-#define TRANSIENT_HEAP_BLOCK_NUM (TRANSIENT_HEAP_TOTAL_SIZE / TRANSIENT_HEAP_BLOCK_SIZE)
-#define TRANSIENT_HEAP_USABLE_SIZE (TRANSIENT_HEAP_BLOCK_SIZE - sizeof(struct transient_heap_block_header))
-
-#define TRANSIENT_HEAP_ALLOC_MAGIC 0xfeab
-#define TRANSIENT_HEAP_ALLOC_ALIGN RUBY_ALIGNOF(void *)
-
-#define TRANSIENT_HEAP_ALLOC_MARKING_LAST -1
-#define TRANSIENT_HEAP_ALLOC_MARKING_FREE -2
-
-enum transient_heap_status {
- transient_heap_none,
- transient_heap_marking,
- transient_heap_escaping
-};
-
-struct transient_heap_block {
- struct transient_heap_block_header {
- int16_t index;
- int16_t last_marked_index;
- int16_t objects;
- struct transient_heap_block *next_block;
- } info;
- char buff[TRANSIENT_HEAP_USABLE_SIZE];
-};
-
-struct transient_heap {
- struct transient_heap_block *using_blocks;
- struct transient_heap_block *marked_blocks;
- struct transient_heap_block *free_blocks;
- int total_objects;
- int total_marked_objects;
- int total_blocks;
- enum transient_heap_status status;
-
- VALUE *promoted_objects;
- int promoted_objects_size;
- int promoted_objects_index;
-
- struct transient_heap_block *arena;
- int arena_index; /* increment only */
-};
-
-struct transient_alloc_header {
- uint16_t magic;
- uint16_t size;
- int16_t next_marked_index;
- int16_t dummy;
- VALUE obj;
-};
-
-static struct transient_heap global_transient_heap;
-
-static void transient_heap_promote_add(struct transient_heap* theap, VALUE obj);
-static const void *transient_heap_ptr(VALUE obj, int error);
-static int transient_header_managed_ptr_p(struct transient_heap* theap, const void *ptr);
-
-#define ROUND_UP(v, a) (((size_t)(v) + (a) - 1) & ~((a) - 1))
-
-static void
-transient_heap_block_dump(struct transient_heap* theap, struct transient_heap_block *block)
-{
- int i=0, n=0;
-
- while (i<block->info.index) {
- void *ptr = &block->buff[i];
- struct transient_alloc_header *header = ptr;
- fprintf(stderr, "%4d %8d %p size:%4d next:%4d %s\n", n, i, ptr, header->size, header->next_marked_index, rb_obj_info(header->obj));
- i += header->size;
- n++;
- }
-}
-
-static void
-transient_heap_blocks_dump(struct transient_heap* theap, struct transient_heap_block *block, const char *type_str)
-{
- while (block) {
- fprintf(stderr, "- transient_heap_dump: %s:%p index:%d objects:%d last_marked_index:%d next:%p\n",
- type_str, (void *)block, block->info.index, block->info.objects, block->info.last_marked_index, (void *)block->info.next_block);
-
- transient_heap_block_dump(theap, block);
- block = block->info.next_block;
- }
-}
-
-static void
-transient_heap_dump(struct transient_heap* theap)
-{
- fprintf(stderr, "transient_heap_dump objects:%d marked_objects:%d blocks:%d\n", theap->total_objects, theap->total_marked_objects, theap->total_blocks);
- transient_heap_blocks_dump(theap, theap->using_blocks, "using_blocks");
- transient_heap_blocks_dump(theap, theap->marked_blocks, "marked_blocks");
- transient_heap_blocks_dump(theap, theap->free_blocks, "free_blocks");
-}
-
-/* Debug: dump all transient_heap blocks */
-void
-rb_transient_heap_dump(void)
-{
- transient_heap_dump(&global_transient_heap);
-}
-
-#if TRANSIENT_HEAP_CHECK_MODE >= 2
-ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(static void transient_heap_ptr_check(struct transient_heap *theap, VALUE obj));
-static void
-transient_heap_ptr_check(struct transient_heap *theap, VALUE obj)
-{
- if (!UNDEF_P(obj)) {
- const void *ptr = transient_heap_ptr(obj, FALSE);
- TH_ASSERT(ptr == NULL || transient_header_managed_ptr_p(theap, ptr));
- }
-}
-
-ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(static int transient_heap_block_verify(struct transient_heap *theap, struct transient_heap_block *block));
-static int
-transient_heap_block_verify(struct transient_heap *theap, struct transient_heap_block *block)
-{
- int i=0, n=0;
- struct transient_alloc_header *header;
-
- while (i<block->info.index) {
- header = (void *)&block->buff[i];
- TH_ASSERT(header->magic == TRANSIENT_HEAP_ALLOC_MAGIC);
- transient_heap_ptr_check(theap, header->obj);
- n ++;
- i += header->size;
- }
- TH_ASSERT(block->info.objects == n);
-
- return n;
-}
-
-static int
-transient_heap_blocks_verify(struct transient_heap *theap, struct transient_heap_block *blocks, int *block_num_ptr)
-{
- int n = 0;
- struct transient_heap_block *block = blocks;
- while (block) {
- n += transient_heap_block_verify(theap, block);
- *block_num_ptr += 1;
- block = block->info.next_block;
- }
-
- return n;
-}
-#endif
-
-static void
-transient_heap_verify(struct transient_heap *theap)
-{
-#if TRANSIENT_HEAP_CHECK_MODE >= 2
- int n=0, block_num=0;
-
- n += transient_heap_blocks_verify(theap, theap->using_blocks, &block_num);
- n += transient_heap_blocks_verify(theap, theap->marked_blocks, &block_num);
-
- TH_ASSERT(n == theap->total_objects);
- TH_ASSERT(n >= theap->total_marked_objects);
- TH_ASSERT(block_num == theap->total_blocks);
-#endif
-}
-
-/* Debug: check assertions for all transient_heap blocks */
-void
-rb_transient_heap_verify(void)
-{
- transient_heap_verify(&global_transient_heap);
-}
-
-static struct transient_heap*
-transient_heap_get(void)
-{
- struct transient_heap* theap = &global_transient_heap;
- transient_heap_verify(theap);
- return theap;
-}
-
-static void
-reset_block(struct transient_heap_block *block)
-{
- __msan_allocated_memory(block, sizeof block);
- block->info.index = 0;
- block->info.objects = 0;
- block->info.last_marked_index = TRANSIENT_HEAP_ALLOC_MARKING_LAST;
- block->info.next_block = NULL;
- __asan_poison_memory_region(&block->buff, sizeof block->buff);
-}
-
-static void
-connect_to_free_blocks(struct transient_heap *theap, struct transient_heap_block *block)
-{
- block->info.next_block = theap->free_blocks;
- theap->free_blocks = block;
-}
-
-static void
-connect_to_using_blocks(struct transient_heap *theap, struct transient_heap_block *block)
-{
- block->info.next_block = theap->using_blocks;
- theap->using_blocks = block;
-}
-
-#if 0
-static void
-connect_to_marked_blocks(struct transient_heap *theap, struct transient_heap_block *block)
-{
- block->info.next_block = theap->marked_blocks;
- theap->marked_blocks = block;
-}
-#endif
-
-static void
-append_to_marked_blocks(struct transient_heap *theap, struct transient_heap_block *append_blocks)
-{
- if (theap->marked_blocks) {
- struct transient_heap_block *block = theap->marked_blocks, *last_block = NULL;
- while (block) {
- last_block = block;
- block = block->info.next_block;
- }
-
- TH_ASSERT(last_block->info.next_block == NULL);
- last_block->info.next_block = append_blocks;
- }
- else {
- theap->marked_blocks = append_blocks;
- }
-}
-
-static struct transient_heap_block *
-transient_heap_block_alloc(struct transient_heap* theap)
-{
- struct transient_heap_block *block;
-#if TRANSIENT_HEAP_DEBUG_INFINITE_BLOCK
- block = mmap(NULL, TRANSIENT_HEAP_BLOCK_SIZE, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS,
- -1, 0);
- if (block == MAP_FAILED) rb_bug("transient_heap_block_alloc: err:%d", errno);
-#else
- if (theap->arena == NULL) {
- theap->arena = rb_aligned_malloc(TRANSIENT_HEAP_BLOCK_SIZE, TRANSIENT_HEAP_TOTAL_SIZE);
- if (theap->arena == NULL) {
- rb_bug("transient_heap_block_alloc: failed");
- }
- }
-
- TH_ASSERT(theap->arena_index < TRANSIENT_HEAP_BLOCK_NUM);
- block = &theap->arena[theap->arena_index++];
- TH_ASSERT(((intptr_t)block & (TRANSIENT_HEAP_BLOCK_SIZE - 1)) == 0);
-#endif
- reset_block(block);
-
- TH_ASSERT(((intptr_t)block->buff & (TRANSIENT_HEAP_ALLOC_ALIGN-1)) == 0);
- if (0) fprintf(stderr, "transient_heap_block_alloc: %4d %p\n", theap->total_blocks, (void *)block);
- return block;
-}
-
-
-static struct transient_heap_block *
-transient_heap_allocatable_block(struct transient_heap* theap)
-{
- struct transient_heap_block *block;
-
-#if TRANSIENT_HEAP_DEBUG_INFINITE_BLOCK
- block = transient_heap_block_alloc(theap);
- theap->total_blocks++;
-#else
- /* get one block from free_blocks */
- block = theap->free_blocks;
- if (block) {
- theap->free_blocks = block->info.next_block;
- block->info.next_block = NULL;
- theap->total_blocks++;
- }
-#endif
-
- return block;
-}
-
-static struct transient_alloc_header *
-transient_heap_allocatable_header(struct transient_heap* theap, size_t size)
-{
- struct transient_heap_block *block = theap->using_blocks;
-
- while (block) {
- TH_ASSERT(block->info.index <= (int16_t)TRANSIENT_HEAP_USABLE_SIZE);
-
- if (TRANSIENT_HEAP_USABLE_SIZE - block->info.index >= size) {
- struct transient_alloc_header *header = (void *)&block->buff[block->info.index];
- block->info.index += size;
- block->info.objects++;
- return header;
- }
- else {
- block = transient_heap_allocatable_block(theap);
- if (block) connect_to_using_blocks(theap, block);
- }
- }
-
- return NULL;
-}
-
-void *
-rb_transient_heap_alloc(VALUE obj, size_t req_size)
-{
- // only on single main ractor
- if (ruby_single_main_ractor == NULL) return NULL;
-
- void *ret;
- struct transient_heap* theap = transient_heap_get();
- size_t size = ROUND_UP(req_size + sizeof(struct transient_alloc_header), TRANSIENT_HEAP_ALLOC_ALIGN);
-
- TH_ASSERT(RB_TYPE_P(obj, T_ARRAY) ||
- RB_TYPE_P(obj, T_OBJECT) ||
- RB_TYPE_P(obj, T_STRUCT)); /* supported types */
-
- if (size > TRANSIENT_HEAP_ALLOC_MAX) {
- if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_alloc: [too big: %ld] %s\n", (long)size, rb_obj_info(obj));
- ret = NULL;
- }
-#if TRANSIENT_HEAP_DEBUG_DONT_PROMOTE == 0
- else if (RB_OBJ_PROMOTED_RAW(obj)) {
- if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_alloc: [promoted object] %s\n", rb_obj_info(obj));
- ret = NULL;
- }
-#else
- else if (RBASIC_CLASS(obj) == 0) {
- if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_alloc: [hidden object] %s\n", rb_obj_info(obj));
- ret = NULL;
- }
-#endif
- else {
- struct transient_alloc_header *header = transient_heap_allocatable_header(theap, size);
- if (header) {
- void *ptr;
-
- /* header is poisoned to prevent buffer overflow, should
- * unpoison first... */
- asan_unpoison_memory_region(header, sizeof *header, true);
-
- header->size = size;
- header->magic = TRANSIENT_HEAP_ALLOC_MAGIC;
- header->next_marked_index = TRANSIENT_HEAP_ALLOC_MARKING_FREE;
- header->obj = obj; /* TODO: can we eliminate it? */
-
- /* header is fixed; shall poison again */
- asan_poison_memory_region(header, sizeof *header);
- ptr = header + 1;
-
- theap->total_objects++; /* statistics */
-
-#if TRANSIENT_HEAP_DEBUG_DONT_PROMOTE
- if (RB_OBJ_PROMOTED_RAW(obj)) {
- transient_heap_promote_add(theap, obj);
- }
-#endif
- if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_alloc: header:%p ptr:%p size:%d obj:%s\n", (void *)header, ptr, (int)size, rb_obj_info(obj));
-
- RB_DEBUG_COUNTER_INC(theap_alloc);
-
- /* ptr is set up; OK to unpoison. */
- asan_unpoison_memory_region(ptr, size - sizeof *header, true);
- ret = ptr;
- }
- else {
- if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_alloc: [no enough space: %ld] %s\n", (long)size, rb_obj_info(obj));
- RB_DEBUG_COUNTER_INC(theap_alloc_fail);
- ret = NULL;
- }
- }
-
- return ret;
-}
-
-void
-Init_TransientHeap(void)
-{
- int i, block_num;
- struct transient_heap* theap = transient_heap_get();
-
-#if TRANSIENT_HEAP_DEBUG_INFINITE_BLOCK
- block_num = 0;
-#else
- TH_ASSERT(TRANSIENT_HEAP_BLOCK_SIZE * TRANSIENT_HEAP_BLOCK_NUM == TRANSIENT_HEAP_TOTAL_SIZE);
- block_num = TRANSIENT_HEAP_BLOCK_NUM;
-#endif
- for (i=0; i<block_num; i++) {
- connect_to_free_blocks(theap, transient_heap_block_alloc(theap));
- }
- theap->using_blocks = transient_heap_allocatable_block(theap);
-
- theap->promoted_objects_size = TRANSIENT_HEAP_PROMOTED_DEFAULT_SIZE;
- theap->promoted_objects_index = 0;
- /* should not use ALLOC_N to be free from GC */
- theap->promoted_objects = malloc(sizeof(VALUE) * theap->promoted_objects_size);
- STATIC_ASSERT(
- integer_overflow,
- sizeof(VALUE) <= SIZE_MAX / TRANSIENT_HEAP_PROMOTED_DEFAULT_SIZE);
- if (theap->promoted_objects == NULL) rb_bug("Init_TransientHeap: malloc failed.");
-}
-
-static struct transient_heap_block *
-blocks_alloc_header_to_block(struct transient_heap *theap, struct transient_heap_block *blocks, struct transient_alloc_header *header)
-{
- struct transient_heap_block *block = blocks;
-
- while (block) {
- if (block->buff <= (char *)header && (char *)header < block->buff + TRANSIENT_HEAP_USABLE_SIZE) {
- return block;
- }
- block = block->info.next_block;
- }
-
- return NULL;
-}
-
-static struct transient_heap_block *
-alloc_header_to_block_verbose(struct transient_heap *theap, struct transient_alloc_header *header)
-{
- struct transient_heap_block *block;
-
- if ((block = blocks_alloc_header_to_block(theap, theap->marked_blocks, header)) != NULL) {
- if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "alloc_header_to_block: found in marked_blocks\n");
- return block;
- }
- else if ((block = blocks_alloc_header_to_block(theap, theap->using_blocks, header)) != NULL) {
- if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "alloc_header_to_block: found in using_blocks\n");
- return block;
- }
- else {
- return NULL;
- }
-}
-
-static struct transient_alloc_header *
-ptr_to_alloc_header(const void *ptr)
-{
- struct transient_alloc_header *header = (void *)ptr;
- header -= 1;
- return header;
-}
-
-static int
-transient_header_managed_ptr_p(struct transient_heap* theap, const void *ptr)
-{
- if (alloc_header_to_block_verbose(theap, ptr_to_alloc_header(ptr))) {
- return TRUE;
- }
- else {
- return FALSE;
- }
-}
-
-
-int
-rb_transient_heap_managed_ptr_p(const void *ptr)
-{
- return transient_header_managed_ptr_p(transient_heap_get(), ptr);
-}
-
-static struct transient_heap_block *
-alloc_header_to_block(struct transient_heap *theap, struct transient_alloc_header *header)
-{
- struct transient_heap_block *block;
-#if TRANSIENT_HEAP_DEBUG_INFINITE_BLOCK
- block = alloc_header_to_block_verbose(theap, header);
- if (block == NULL) {
- transient_heap_dump(theap);
- rb_bug("alloc_header_to_block: not found in mark_blocks (%p)", header);
- }
-#else
- block = (void *)((intptr_t)header & ~(TRANSIENT_HEAP_BLOCK_SIZE-1));
- TH_ASSERT(block == alloc_header_to_block_verbose(theap, header));
-#endif
- return block;
-}
-
-void
-rb_transient_heap_mark(VALUE obj, const void *ptr)
-{
- ASSERT_vm_locking();
-
- struct transient_alloc_header *header = ptr_to_alloc_header(ptr);
- asan_unpoison_memory_region(header, sizeof *header, false);
- if (header->magic != TRANSIENT_HEAP_ALLOC_MAGIC) rb_bug("rb_transient_heap_mark: wrong header, %s (%p)", rb_obj_info(obj), ptr);
- if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_mark: %s (%p)\n", rb_obj_info(obj), ptr);
-
-#if TRANSIENT_HEAP_CHECK_MODE > 0
- {
- struct transient_heap* theap = transient_heap_get();
- TH_ASSERT(theap->status == transient_heap_marking);
- TH_ASSERT(transient_header_managed_ptr_p(theap, ptr));
-
- if (header->magic != TRANSIENT_HEAP_ALLOC_MAGIC) {
- transient_heap_dump(theap);
- rb_bug("rb_transient_heap_mark: magic is broken");
- }
- else if (header->obj != obj) {
- // transient_heap_dump(theap);
- rb_bug("rb_transient_heap_mark: unmatch (%s is stored, but %s is given)",
- rb_obj_info(header->obj), rb_obj_info(obj));
- }
- }
-#endif
-
- if (header->next_marked_index != TRANSIENT_HEAP_ALLOC_MARKING_FREE) {
- /* already marked */
- return;
- }
- else {
- struct transient_heap* theap = transient_heap_get();
- struct transient_heap_block *block = alloc_header_to_block(theap, header);
- __asan_unpoison_memory_region(&block->info, sizeof block->info);
- header->next_marked_index = block->info.last_marked_index;
- block->info.last_marked_index = (int)((char *)header - block->buff);
- theap->total_marked_objects++;
-
- transient_heap_verify(theap);
- }
-}
-
-ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(static const void *transient_heap_ptr(VALUE obj, int error));
-static const void *
-transient_heap_ptr(VALUE obj, int error)
-{
- const void *ptr = NULL;
-
- switch (BUILTIN_TYPE(obj)) {
- case T_ARRAY:
- if (RARRAY_TRANSIENT_P(obj)) {
- TH_ASSERT(!ARY_EMBED_P(obj));
- ptr = RARRAY(obj)->as.heap.ptr;
- }
- break;
- case T_OBJECT:
- if (ROBJ_TRANSIENT_P(obj)) {
- RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
- ptr = ROBJECT_IVPTR(obj);
- }
- break;
- case T_STRUCT:
- if (RSTRUCT_TRANSIENT_P(obj)) {
- ptr = rb_struct_const_heap_ptr(obj);
- }
- break;
- default:
- if (error) {
- rb_bug("transient_heap_ptr: unknown obj %s", rb_obj_info(obj));
- }
- }
-
- return ptr;
-}
-
-static void
-transient_heap_promote_add(struct transient_heap* theap, VALUE obj)
-{
- if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_promote: %s\n", rb_obj_info(obj));
-
- if (TRANSIENT_HEAP_DEBUG_DONT_PROMOTE) {
- /* duplicate check */
- int i;
- for (i=0; i<theap->promoted_objects_index; i++) {
- if (theap->promoted_objects[i] == obj) return;
- }
- }
-
- if (theap->promoted_objects_size <= theap->promoted_objects_index) {
- theap->promoted_objects_size *= 2;
- if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr, "rb_transient_heap_promote: expand table to %d\n", theap->promoted_objects_size);
- if (UNLIKELY((size_t)theap->promoted_objects_size > SIZE_MAX / sizeof(VALUE))) {
- /* realloc failure due to integer overflow */
- theap->promoted_objects = NULL;
- }
- else {
- theap->promoted_objects = realloc(theap->promoted_objects, theap->promoted_objects_size * sizeof(VALUE));
- }
- if (theap->promoted_objects == NULL) rb_bug("rb_transient_heap_promote: realloc failed");
- }
- theap->promoted_objects[theap->promoted_objects_index++] = obj;
-}
-
-void
-rb_transient_heap_promote(VALUE obj)
-{
- ASSERT_vm_locking();
-
- if (transient_heap_ptr(obj, FALSE)) {
- struct transient_heap* theap = transient_heap_get();
- transient_heap_promote_add(theap, obj);
- }
- else {
- /* ignore */
- }
-}
-
-static struct transient_alloc_header *
-alloc_header(struct transient_heap_block* block, int index)
-{
- return (void *)&block->buff[index];
-}
-
-static void
-transient_heap_reset(void)
-{
- ASSERT_vm_locking();
-
- struct transient_heap* theap = transient_heap_get();
- struct transient_heap_block* block;
-
- if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr, "!! transient_heap_reset\n");
-
- block = theap->marked_blocks;
- while (block) {
- struct transient_heap_block *next_block = block->info.next_block;
- theap->total_objects -= block->info.objects;
-#if TRANSIENT_HEAP_DEBUG_INFINITE_BLOCK
- if (madvise(block, TRANSIENT_HEAP_BLOCK_SIZE, MADV_DONTNEED) != 0) {
- rb_bug("madvise err:%d", errno);
- }
- if (mprotect(block, TRANSIENT_HEAP_BLOCK_SIZE, PROT_NONE) != 0) {
- rb_bug("mprotect err:%d", errno);
- }
-#else
- reset_block(block);
- connect_to_free_blocks(theap, block);
-#endif
- theap->total_blocks--;
- block = next_block;
- }
-
- if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr, "!! transient_heap_reset block_num:%d\n", theap->total_blocks);
-
- theap->marked_blocks = NULL;
- theap->total_marked_objects = 0;
-}
-
-static void
-transient_heap_block_evacuate(struct transient_heap* theap, struct transient_heap_block* block)
-{
- int marked_index = block->info.last_marked_index;
- block->info.last_marked_index = TRANSIENT_HEAP_ALLOC_MARKING_LAST;
-
- while (marked_index >= 0) {
- struct transient_alloc_header *header = alloc_header(block, marked_index);
- asan_unpoison_memory_region(header, sizeof *header, true);
- VALUE obj = header->obj;
- TH_ASSERT(header->magic == TRANSIENT_HEAP_ALLOC_MAGIC);
- if (header->magic != TRANSIENT_HEAP_ALLOC_MAGIC) rb_bug("transient_heap_block_evacuate: wrong header %p %s", (void *)header, rb_obj_info(obj));
-
- if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, " * transient_heap_block_evacuate %p %s\n", (void *)header, rb_obj_info(obj));
-
- if (obj != Qnil) {
- RB_DEBUG_COUNTER_INC(theap_evacuate);
-
- switch (BUILTIN_TYPE(obj)) {
- case T_ARRAY:
- rb_ary_transient_heap_evacuate(obj, !TRANSIENT_HEAP_DEBUG_DONT_PROMOTE);
- break;
- case T_OBJECT:
- rb_obj_transient_heap_evacuate(obj, !TRANSIENT_HEAP_DEBUG_DONT_PROMOTE);
- break;
- case T_STRUCT:
- rb_struct_transient_heap_evacuate(obj, !TRANSIENT_HEAP_DEBUG_DONT_PROMOTE);
- break;
- default:
- rb_bug("unsupported: %s", rb_obj_info(obj));
- }
- header->obj = Qundef; /* for debug */
- }
- marked_index = header->next_marked_index;
- asan_poison_memory_region(header, sizeof *header);
- }
-}
-
-#if USE_RUBY_DEBUG_LOG
-static const char *
-transient_heap_status_cstr(enum transient_heap_status status)
-{
- switch (status) {
- case transient_heap_none: return "none";
- case transient_heap_marking: return "marking";
- case transient_heap_escaping: return "escaping";
- }
- UNREACHABLE_RETURN(NULL);
-}
-#endif
-
-static void
-transient_heap_update_status(struct transient_heap* theap, enum transient_heap_status status)
-{
- RUBY_DEBUG_LOG("%s -> %s",
- transient_heap_status_cstr(theap->status),
- transient_heap_status_cstr(status));
-
- TH_ASSERT(theap->status != status);
- theap->status = status;
-}
-
-static void
-transient_heap_evacuate(void *dmy)
-{
- struct transient_heap* theap = transient_heap_get();
-
- if (theap->total_marked_objects == 0) return;
- if (ruby_single_main_ractor == NULL) rb_bug("not single ractor mode");
- if (theap->status == transient_heap_marking) {
- if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr, "!! transient_heap_evacuate: skip while transient_heap_marking\n");
- }
- else {
- VALUE gc_disabled = rb_gc_disable_no_rest();
- {
- struct transient_heap_block* block;
-
- RUBY_DEBUG_LOG("start gc_disabled:%d", RTEST(gc_disabled));
-
- if (TRANSIENT_HEAP_DEBUG >= 1) {
- int i;
- fprintf(stderr, "!! transient_heap_evacuate start total_blocks:%d\n", theap->total_blocks);
- if (TRANSIENT_HEAP_DEBUG >= 4) {
- for (i=0; i<theap->promoted_objects_index; i++) fprintf(stderr, "%4d %s\n", i, rb_obj_info(theap->promoted_objects[i]));
- }
- }
- if (TRANSIENT_HEAP_DEBUG >= 2) transient_heap_dump(theap);
-
- TH_ASSERT(theap->status == transient_heap_none);
- transient_heap_update_status(theap, transient_heap_escaping);
-
- /* evacuate from marked blocks */
- block = theap->marked_blocks;
- while (block) {
- transient_heap_block_evacuate(theap, block);
- block = block->info.next_block;
- }
-
- /* evacuate from using blocks
- only affect incremental marking */
- block = theap->using_blocks;
- while (block) {
- transient_heap_block_evacuate(theap, block);
- block = block->info.next_block;
- }
-
- /* all objects in marked_objects are escaped. */
- transient_heap_reset();
-
- if (TRANSIENT_HEAP_DEBUG > 0) {
- fprintf(stderr, "!! transient_heap_evacuate end total_blocks:%d\n", theap->total_blocks);
- }
-
- transient_heap_verify(theap);
- transient_heap_update_status(theap, transient_heap_none);
- }
- if (gc_disabled != Qtrue) rb_gc_enable();
- RUBY_DEBUG_LOG("finish");
- }
-}
-
-void
-rb_transient_heap_evacuate(void)
-{
- transient_heap_evacuate(NULL);
-}
-
-static void
-clear_marked_index(struct transient_heap_block* block)
-{
- int marked_index = block->info.last_marked_index;
-
- while (marked_index != TRANSIENT_HEAP_ALLOC_MARKING_LAST) {
- struct transient_alloc_header *header = alloc_header(block, marked_index);
- /* header is poisoned to prevent buffer overflow, should
- * unpoison first... */
- asan_unpoison_memory_region(header, sizeof *header, false);
- TH_ASSERT(marked_index != TRANSIENT_HEAP_ALLOC_MARKING_FREE);
- if (0) fprintf(stderr, "clear_marked_index - block:%p mark_index:%d\n", (void *)block, marked_index);
-
- marked_index = header->next_marked_index;
- header->next_marked_index = TRANSIENT_HEAP_ALLOC_MARKING_FREE;
- }
-
- block->info.last_marked_index = TRANSIENT_HEAP_ALLOC_MARKING_LAST;
-}
-
-static void
-blocks_clear_marked_index(struct transient_heap_block* block)
-{
- while (block) {
- clear_marked_index(block);
- block = block->info.next_block;
- }
-}
-
-static void
-transient_heap_block_update_refs(struct transient_heap* theap, struct transient_heap_block* block)
-{
- int marked_index = block->info.last_marked_index;
-
- while (marked_index >= 0) {
- struct transient_alloc_header *header = alloc_header(block, marked_index);
-
- asan_unpoison_memory_region(header, sizeof *header, false);
-
- header->obj = rb_gc_location(header->obj);
-
- marked_index = header->next_marked_index;
- asan_poison_memory_region(header, sizeof *header);
- }
-}
-
-static void
-transient_heap_blocks_update_refs(struct transient_heap* theap, struct transient_heap_block *block, const char *type_str)
-{
- while (block) {
- transient_heap_block_update_refs(theap, block);
- block = block->info.next_block;
- }
-}
-
-void
-rb_transient_heap_update_references(void)
-{
- ASSERT_vm_locking();
-
- struct transient_heap* theap = transient_heap_get();
- int i;
-
- transient_heap_blocks_update_refs(theap, theap->using_blocks, "using_blocks");
- transient_heap_blocks_update_refs(theap, theap->marked_blocks, "marked_blocks");
-
- for (i=0; i<theap->promoted_objects_index; i++) {
- VALUE obj = theap->promoted_objects[i];
- theap->promoted_objects[i] = rb_gc_location(obj);
- }
-}
-
-void
-rb_transient_heap_start_marking(int full_marking)
-{
- ASSERT_vm_locking();
- RUBY_DEBUG_LOG("full?:%d", full_marking);
-
- struct transient_heap* theap = transient_heap_get();
-
- if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr, "!! rb_transient_heap_start_marking objects:%d blocks:%d promoted:%d full_marking:%d\n",
- theap->total_objects, theap->total_blocks, theap->promoted_objects_index, full_marking);
- if (TRANSIENT_HEAP_DEBUG >= 2) transient_heap_dump(theap);
-
- blocks_clear_marked_index(theap->marked_blocks);
- blocks_clear_marked_index(theap->using_blocks);
-
- if (theap->using_blocks) {
- if (theap->using_blocks->info.objects > 0) {
- append_to_marked_blocks(theap, theap->using_blocks);
- theap->using_blocks = NULL;
- }
- else {
- append_to_marked_blocks(theap, theap->using_blocks->info.next_block);
- theap->using_blocks->info.next_block = NULL;
- }
- }
-
- if (theap->using_blocks == NULL) {
- theap->using_blocks = transient_heap_allocatable_block(theap);
- }
-
- TH_ASSERT(theap->status == transient_heap_none);
- transient_heap_update_status(theap, transient_heap_marking);
- theap->total_marked_objects = 0;
-
- if (full_marking) {
- theap->promoted_objects_index = 0;
- }
- else { /* mark promoted objects */
- int i;
- for (i=0; i<theap->promoted_objects_index; i++) {
- VALUE obj = theap->promoted_objects[i];
- const void *ptr = transient_heap_ptr(obj, TRUE);
- if (ptr) {
- rb_transient_heap_mark(obj, ptr);
- }
- }
- }
-
- transient_heap_verify(theap);
-}
-
-void
-rb_transient_heap_finish_marking(void)
-{
- ASSERT_vm_locking();
- struct transient_heap* theap = transient_heap_get();
-
- RUBY_DEBUG_LOG("objects:%d, marked:%d",
- theap->total_objects,
- theap->total_marked_objects);
- if (TRANSIENT_HEAP_DEBUG >= 2) transient_heap_dump(theap);
-
- TH_ASSERT(theap->total_objects >= theap->total_marked_objects);
-
- TH_ASSERT(theap->status == transient_heap_marking);
- transient_heap_update_status(theap, transient_heap_none);
-
- if (theap->total_marked_objects > 0) {
- if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr, "-> rb_transient_heap_finish_marking register escape func.\n");
- rb_postponed_job_register_one(0, transient_heap_evacuate, NULL);
- }
- else {
- transient_heap_reset();
- }
-
- transient_heap_verify(theap);
-}
-#endif /* USE_TRANSIENT_HEAP */
diff --git a/transient_heap.h b/transient_heap.h
deleted file mode 100644
index 6c6141f71a..0000000000
--- a/transient_heap.h
+++ /dev/null
@@ -1,63 +0,0 @@
-#ifndef RUBY_TRANSIENT_HEAP_H
-#define RUBY_TRANSIENT_HEAP_H
-/**********************************************************************
-
- transient_heap.h - declarations of transient_heap related APIs.
-
- Copyright (C) 2018 Koichi Sasada
-
-**********************************************************************/
-
-#include "internal.h"
-
-#if USE_TRANSIENT_HEAP
-
-/* public API */
-
-/* Allocate req_size bytes from transient_heap.
- Allocated memories are free-ed when next GC
- if this memory is not marked by `rb_transient_heap_mark()`.
- */
-void *rb_transient_heap_alloc(VALUE obj, size_t req_size);
-
-/* If `obj` uses a memory pointed by `ptr` from transient_heap,
- you need to call `rb_transient_heap_mark(obj, ptr)`
- to assert liveness of `obj` (and ptr). */
-void rb_transient_heap_mark(VALUE obj, const void *ptr);
-
-/* used by gc.c */
-void rb_transient_heap_promote(VALUE obj);
-void rb_transient_heap_start_marking(int full_marking);
-void rb_transient_heap_finish_marking(void);
-void rb_transient_heap_update_references(void);
-
-/* used by ractor.c */
-void rb_transient_heap_evacuate(void);
-
-/* for debug API */
-void rb_transient_heap_dump(void);
-void rb_transient_heap_verify(void);
-int rb_transient_heap_managed_ptr_p(const void *ptr);
-
-/* evacuate functions for each type */
-void rb_ary_transient_heap_evacuate(VALUE ary, int promote);
-void rb_obj_transient_heap_evacuate(VALUE obj, int promote);
-void rb_struct_transient_heap_evacuate(VALUE st, int promote);
-
-#else /* USE_TRANSIENT_HEAP */
-
-#define rb_transient_heap_alloc(o, s) NULL
-#define rb_transient_heap_verify() ((void)0)
-#define rb_transient_heap_promote(obj) ((void)0)
-#define rb_transient_heap_start_marking(full_marking) ((void)0)
-#define rb_transient_heap_update_references() ((void)0)
-#define rb_transient_heap_evacuate() ((void)0)
-#define rb_transient_heap_finish_marking() ((void)0)
-#define rb_transient_heap_mark(obj, ptr) ((void)0)
-
-#define rb_ary_transient_heap_evacuate(x, y) ((void)0)
-#define rb_obj_transient_heap_evacuate(x, y) ((void)0)
-#define rb_struct_transient_heap_evacuate(x, y) ((void)0)
-
-#endif /* USE_TRANSIENT_HEAP */
-#endif
diff --git a/universal_parser.c b/universal_parser.c
new file mode 100644
index 0000000000..05445587ba
--- /dev/null
+++ b/universal_parser.c
@@ -0,0 +1,233 @@
+#include <alloca.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+/* Dependency */
+#include "internal/parse.h"
+#include "node.h"
+#include "id.h"
+
+#include "internal/compilers.h"
+#include "ruby/backward/2/inttypes.h"
+#include "probes.h"
+
+#define LIKELY(x) RB_LIKELY(x)
+#define UNLIKELY(x) RB_UNLIKELY(x)
+#ifndef TRUE
+# define TRUE 1
+#endif
+
+#ifndef FALSE
+# define FALSE 0
+#endif
+#define numberof(array) ((int)(sizeof(array) / sizeof((array)[0])))
+#define rb_strlen_lit(str) (sizeof(str "") - 1)
+#undef FIXNUM_MAX
+#define FIXNUM_MAX (LONG_MAX / 2)
+#undef RSTRING_GETMEM
+#define RSTRING_GETMEM(str, ptrvar, lenvar) \
+ ((ptrvar) = RSTRING_PTR(str), \
+ (lenvar) = RSTRING_LEN(str))
+
+/* parser_st */
+#define st_table parser_st_table
+#define st_data_t parser_st_data_t
+#define st_hash_type parser_st_hash_type
+#define ST_CONTINUE ST2_CONTINUE
+#define ST_STOP ST2_STOP
+#define ST_DELETE ST2_DELETE
+#define ST_CHECK ST2_CHECK
+#define ST_REPLACE ST2_REPLACE
+#undef st_init_numtable
+#define st_init_numtable rb_parser_st_init_numtable
+#undef st_free_table
+#define st_free_table rb_parser_st_free_table
+#undef st_init_table_with_size
+#define st_init_table_with_size rb_parser_st_init_table_with_size
+#undef st_insert
+#define st_insert rb_parser_st_insert
+#undef st_foreach
+#define st_foreach rb_parser_st_foreach
+#undef st_delete
+#define st_delete rb_parser_st_delete
+#undef st_is_member
+#define st_is_member parser_st_is_member
+#undef st_init_table
+#define st_init_table rb_parser_st_init_table
+#undef st_lookup
+#define st_lookup rb_parser_st_lookup
+
+#define rb_encoding void
+
+#undef xmalloc
+#define xmalloc p->config->malloc
+#undef xcalloc
+#define xcalloc p->config->calloc
+#undef xrealloc
+#define xrealloc p->config->realloc
+#undef ALLOC_N
+#define ALLOC_N(type,n) ((type *)p->config->alloc_n((n), sizeof(type)))
+#undef ALLOC
+#define ALLOC(type) ((type *)p->config->alloc(sizeof(type)))
+#undef xfree
+#define xfree p->config->free
+#undef ALLOCA_N
+// alloca(rbimpl_size_mul_or_raise(x, y));
+#define ALLOCA_N(type,n) ((type *)alloca(sizeof(type) * (n)))
+#undef REALLOC_N
+#define REALLOC_N(var,type,n) ((var) = (type *)p->config->realloc_n((void *)var, n, sizeof(type)))
+#undef ZALLOC
+#define ZALLOC(type) ((type *)p->config->zalloc(sizeof(type)))
+#undef MEMMOVE
+#define MEMMOVE(p1,p2,type,n) (p->config->rb_memmove((p1), (p2), sizeof(type), (n)))
+#undef MEMCPY
+#define MEMCPY(p1,p2,type,n) (p->config->nonempty_memcpy((p1), (p2), sizeof(type), (n)))
+
+#define compile_callback p->config->compile_callback
+#define reg_named_capture_assign p->config->reg_named_capture_assign
+
+#define rb_attr_get p->config->attr_get
+
+#define rb_ary_new p->config->ary_new
+#define rb_ary_push p->config->ary_push
+#undef rb_ary_new_from_args
+#define rb_ary_new_from_args p->config->ary_new_from_args
+#define rb_ary_unshift p->config->ary_unshift
+
+#define rb_make_temporary_id p->config->make_temporary_id
+#define is_local_id p->config->is_local_id
+#define is_attrset_id p->config->is_attrset_id
+#define is_global_name_punct p->config->is_global_name_punct
+#define id_type p->config->id_type
+#define rb_id_attrset p->config->id_attrset
+#undef rb_intern
+#define rb_intern p->config->intern
+#define rb_intern2 p->config->intern2
+#define rb_intern3 p->config->intern3
+#define rb_intern_str p->config->intern_str
+#define is_notop_id p->config->is_notop_id
+#define rb_enc_symname_type p->config->enc_symname_type
+#define rb_id2name p->config->id2name
+#define rb_id2str p->config->id2str
+#undef ID2SYM
+#define ID2SYM p->config->id2sym
+#undef SYM2ID
+#define SYM2ID p->config->sym2id
+
+#define rb_str_catf p->config->str_catf
+#undef rb_str_cat_cstr
+#define rb_str_cat_cstr p->config->str_cat_cstr
+#define rb_str_modify p->config->str_modify
+#define rb_str_set_len p->config->str_set_len
+#define rb_str_cat p->config->str_cat
+#define rb_str_resize p->config->str_resize
+#undef rb_str_new
+#define rb_str_new p->config->str_new
+#undef rb_str_new_cstr
+#define rb_str_new_cstr p->config->str_new_cstr
+#define rb_str_to_interned_str p->config->str_to_interned_str
+#define is_ascii_string p->config->is_ascii_string
+#define rb_enc_str_new p->config->enc_str_new
+#define rb_str_vcatf p->config->str_vcatf
+#undef StringValueCStr
+#define StringValueCStr(v) p->config->string_value_cstr(&(v))
+#define rb_sprintf p->config->rb_sprintf
+#undef RSTRING_PTR
+#define RSTRING_PTR p->config->rstring_ptr
+#undef RSTRING_END
+#define RSTRING_END p->config->rstring_end
+#undef RSTRING_LEN
+#define RSTRING_LEN p->config->rstring_len
+#define rb_obj_as_string p->config->obj_as_string
+
+#undef INT2NUM
+#define INT2NUM p->config->int2num
+
+#define rb_stderr_tty_p p->config->stderr_tty_p
+#define rb_write_error_str p->config->write_error_str
+#define rb_io_write p->config->io_write
+#define rb_io_flush p->config->io_flush
+#define rb_io_puts p->config->io_puts
+
+#define rb_ractor_stdout p->config->debug_output_stdout
+#define rb_ractor_stderr p->config->debug_output_stderr
+
+#define rb_is_usascii_enc p->config->is_usascii_enc
+#define rb_enc_isalnum p->config->enc_isalnum
+#define rb_enc_precise_mbclen p->config->enc_precise_mbclen
+#define MBCLEN_CHARFOUND_P p->config->mbclen_charfound_p
+#define MBCLEN_CHARFOUND_LEN p->config->mbclen_charfound_len
+#define rb_enc_name p->config->enc_name
+#define rb_enc_prev_char p->config->enc_prev_char
+#define rb_enc_get p->config->enc_get
+#define rb_enc_asciicompat p->config->enc_asciicompat
+#define rb_utf8_encoding p->config->utf8_encoding
+#define rb_enc_associate p->config->enc_associate
+#define rb_ascii8bit_encoding p->config->ascii8bit_encoding
+#define rb_enc_codelen p->config->enc_codelen
+#define rb_enc_mbcput p->config->enc_mbcput
+#define rb_enc_mbclen p->config->enc_mbclen
+#define rb_enc_find_index p->config->enc_find_index
+#define rb_enc_from_index p->config->enc_from_index
+#define rb_enc_isspace p->config->enc_isspace
+#define ENC_CODERANGE_7BIT p->config->enc_coderange_7bit
+#define ENC_CODERANGE_UNKNOWN p->config->enc_coderange_unknown
+#define rb_usascii_encoding p->config->usascii_encoding
+
+#define rb_local_defined p->config->local_defined
+#define rb_dvar_defined p->config->dvar_defined
+
+#define rb_syntax_error_append p->config->syntax_error_append
+#define rb_raise p->config->raise
+#define syntax_error_new p->config->syntax_error_new
+
+#define rb_errinfo p->config->errinfo
+#define rb_set_errinfo p->config->set_errinfo
+#define rb_exc_raise p->config->exc_raise
+#define rb_make_exception p->config->make_exception
+
+#define ruby_sized_xfree p->config->sized_xfree
+#define SIZED_REALLOC_N(v, T, m, n) ((v) = (T *)p->config->sized_realloc_n((void *)(v), (m), sizeof(T), (n)))
+#undef RB_GC_GUARD
+#define RB_GC_GUARD p->config->gc_guard
+#define rb_gc_mark p->config->gc_mark
+
+#define rb_reg_compile p->config->reg_compile
+#define rb_reg_check_preprocess p->config->reg_check_preprocess
+#define rb_memcicmp p->config->memcicmp
+
+#define rb_compile_warn p->config->compile_warn
+#define rb_compile_warning p->config->compile_warning
+#define rb_bug p->config->bug
+#define rb_fatal p->config->fatal
+#undef ruby_verbose
+#define ruby_verbose p->config->verbose()
+#undef errno
+#define errno (*p->config->errno_ptr())
+
+#define rb_make_backtrace p->config->make_backtrace
+
+#define ruby_scan_hex p->config->scan_hex
+#define ruby_scan_oct p->config->scan_oct
+#define ruby_scan_digits p->config->scan_digits
+#define strtod p->config->strtod
+
+#undef RTEST
+#define RTEST p->config->rtest
+#undef NIL_P
+#define NIL_P p->config->nil_p
+#undef Qnil
+#define Qnil p->config->qnil
+#undef Qfalse
+#define Qfalse p->config->qfalse
+#define rb_eArgError p->config->eArgError()
+#undef rb_long2int
+#define rb_long2int p->config->long2int
+#define rb_enc_mbminlen p->config->enc_mbminlen
+#define rb_enc_isascii p->config->enc_isascii
+#define rb_enc_mbc_to_codepoint p->config->enc_mbc_to_codepoint
+
+#define rb_ast_new() \
+ rb_ast_new(p->config)
diff --git a/util.c b/util.c
index 74f2d11b94..3c08879ce5 100644
--- a/util.c
+++ b/util.c
@@ -13,6 +13,10 @@
# define MINGW_HAS_SECURE_API 1
#endif
+#ifndef __STDC_WANT_LIB_EXT1__
+#define __STDC_WANT_LIB_EXT1__ 1 /* for qsort_s() */
+#endif
+
#include "ruby/internal/config.h"
#include <ctype.h>
@@ -203,22 +207,15 @@ ruby_strtoul(const char *str, char **endptr, int base)
}
}
+#if !defined HAVE_GNU_QSORT_R
#include <sys/types.h>
-#include <sys/stat.h>
+#include <stdint.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
-#if defined(HAVE_FCNTL_H)
-#include <fcntl.h>
-#endif
-
-#ifndef S_ISDIR
-# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
-#endif
typedef int (cmpfunc_t)(const void*, const void*, void*);
-#if !defined HAVE_GNU_QSORT_R
#if defined HAVE_QSORT_S && defined RUBY_MSVCRT_VERSION
/* In contrast to its name, Visual Studio qsort_s is incompatible with
* C11 in the order of the comparison function's arguments, and same
diff --git a/variable.c b/variable.c
index 0491562d22..fcde8a603e 100644
--- a/variable.c
+++ b/variable.c
@@ -33,8 +33,8 @@
#include "ruby/encoding.h"
#include "ruby/st.h"
#include "ruby/util.h"
-#include "transient_heap.h"
#include "shape.h"
+#include "symbol.h"
#include "variable.h"
#include "vm_core.h"
#include "ractor_core.h"
@@ -63,15 +63,6 @@ static void setup_const_entry(rb_const_entry_t *, VALUE, VALUE, rb_const_flag_t)
static VALUE rb_const_search(VALUE klass, ID id, int exclude, int recurse, int visibility);
static st_table *generic_iv_tbl_;
-struct ivar_update {
- struct gen_ivtbl *ivtbl;
- uint32_t iv_index;
- uint32_t max_index;
-#if !SHAPE_IN_BASIC_FLAGS
- rb_shape_t *shape;
-#endif
-};
-
void
Init_var_tables(void)
{
@@ -81,11 +72,11 @@ Init_var_tables(void)
autoload_mutex = rb_mutex_new();
rb_obj_hide(autoload_mutex);
- rb_gc_register_mark_object(autoload_mutex);
+ rb_vm_register_global_object(autoload_mutex);
autoload_features = rb_ident_hash_new();
rb_obj_hide(autoload_features);
- rb_gc_register_mark_object(autoload_features);
+ rb_vm_register_global_object(autoload_features);
}
static inline bool
@@ -120,11 +111,17 @@ classname(VALUE klass, bool *permanent)
return classpath;
}
+VALUE
+rb_mod_name0(VALUE klass, bool *permanent)
+{
+ return classname(klass, permanent);
+}
+
/*
* call-seq:
- * mod.name -> string
+ * mod.name -> string or nil
*
- * Returns the name of the module <i>mod</i>. Returns nil for anonymous modules.
+ * Returns the name of the module <i>mod</i>. Returns +nil+ for anonymous modules.
*/
VALUE
@@ -134,6 +131,117 @@ rb_mod_name(VALUE mod)
return classname(mod, &permanent);
}
+// Similar to logic in rb_mod_const_get().
+static bool
+is_constant_path(VALUE name)
+{
+ const char *path = RSTRING_PTR(name);
+ const char *pend = RSTRING_END(name);
+ rb_encoding *enc = rb_enc_get(name);
+
+ const char *p = path;
+
+ if (p >= pend || !*p) {
+ return false;
+ }
+
+ while (p < pend) {
+ if (p + 2 <= pend && p[0] == ':' && p[1] == ':') {
+ p += 2;
+ }
+
+ const char *pbeg = p;
+ while (p < pend && *p != ':') p++;
+
+ if (pbeg == p) return false;
+
+ if (rb_enc_symname_type(pbeg, p - pbeg, enc, 0) != ID_CONST) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * call-seq:
+ * mod.set_temporary_name(string) -> self
+ * mod.set_temporary_name(nil) -> self
+ *
+ * Sets the temporary name of the module. This name is reflected in
+ * introspection of the module and the values that are related to it, such
+ * as instances, constants, and methods.
+ *
+ * The name should be +nil+ or a non-empty string that is not a valid constant
+ * path (to avoid confusing between permanent and temporary names).
+ *
+ * The method can be useful to distinguish dynamically generated classes and
+ * modules without assigning them to constants.
+ *
+ * If the module is given a permanent name by assigning it to a constant,
+ * the temporary name is discarded. A temporary name can't be assigned to
+ * modules that have a permanent name.
+ *
+ * If the given name is +nil+, the module becomes anonymous again.
+ *
+ * Example:
+ *
+ * m = Module.new # => #<Module:0x0000000102c68f38>
+ * m.name #=> nil
+ *
+ * m.set_temporary_name("fake_name") # => fake_name
+ * m.name #=> "fake_name"
+ *
+ * m.set_temporary_name(nil) # => #<Module:0x0000000102c68f38>
+ * m.name #=> nil
+ *
+ * c = Class.new
+ * c.set_temporary_name("MyClass(with description)")
+ *
+ * c.new # => #<MyClass(with description):0x0....>
+ *
+ * c::M = m
+ * c::M.name #=> "MyClass(with description)::M"
+ *
+ * # Assigning to a constant replaces the name with a permanent one
+ * C = c
+ *
+ * C.name #=> "C"
+ * C::M.name #=> "C::M"
+ * c.new # => #<C:0x0....>
+ */
+
+VALUE
+rb_mod_set_temporary_name(VALUE mod, VALUE name)
+{
+ // We don't allow setting the name if the classpath is already permanent:
+ if (RCLASS_EXT(mod)->permanent_classpath) {
+ rb_raise(rb_eRuntimeError, "can't change permanent name");
+ }
+
+ if (NIL_P(name)) {
+ // Set the temporary classpath to NULL (anonymous):
+ RCLASS_SET_CLASSPATH(mod, 0, FALSE);
+ }
+ else {
+ // Ensure the name is a string:
+ StringValue(name);
+
+ if (RSTRING_LEN(name) == 0) {
+ rb_raise(rb_eArgError, "empty class/module name");
+ }
+
+ if (is_constant_path(name)) {
+ rb_raise(rb_eArgError, "the temporary name must not be a constant path to avoid confusion");
+ }
+
+ // Set the temporary classpath to the given name:
+ RCLASS_SET_CLASSPATH(mod, name, FALSE);
+ }
+
+ return mod;
+}
+
static VALUE
make_temporary_path(VALUE obj, VALUE klass)
{
@@ -163,19 +271,19 @@ rb_tmp_class_path(VALUE klass, bool *permanent, fallback_func fallback)
if (!NIL_P(path)) {
return path;
}
- else {
- if (RB_TYPE_P(klass, T_MODULE)) {
- if (rb_obj_class(klass) == rb_cModule) {
- path = Qfalse;
- }
- else {
- bool perm;
- path = rb_tmp_class_path(RBASIC(klass)->klass, &perm, fallback);
- }
+
+ if (RB_TYPE_P(klass, T_MODULE)) {
+ if (rb_obj_class(klass) == rb_cModule) {
+ path = Qfalse;
+ }
+ else {
+ bool perm;
+ path = rb_tmp_class_path(RBASIC(klass)->klass, &perm, fallback);
}
- *permanent = false;
- return fallback(klass, path);
}
+
+ *permanent = false;
+ return fallback(klass, path);
}
VALUE
@@ -342,6 +450,33 @@ struct rb_global_entry {
bool ractor_local;
};
+static enum rb_id_table_iterator_result
+free_global_entry_i(ID key, VALUE val, void *arg)
+{
+ struct rb_global_entry *entry = (struct rb_global_entry *)val;
+ if (entry->var->counter == 1) {
+ ruby_xfree(entry->var);
+ }
+ else {
+ entry->var->counter--;
+ }
+ ruby_xfree(entry);
+ return ID_TABLE_DELETE;
+}
+
+void
+rb_free_rb_global_tbl(void)
+{
+ rb_id_table_foreach(rb_global_tbl, free_global_entry_i, 0);
+ rb_id_table_free(rb_global_tbl);
+}
+
+void
+rb_free_generic_iv_tbl_(void)
+{
+ st_free_table(generic_iv_tbl_);
+}
+
static struct rb_global_entry*
rb_find_global_entry(ID id)
{
@@ -403,7 +538,7 @@ rb_global_entry(ID id)
VALUE
rb_gvar_undef_getter(ID id, VALUE *_)
{
- rb_warning("global variable `%"PRIsVALUE"' not initialized", QUOTE_ID(id));
+ rb_warning("global variable '%"PRIsVALUE"' not initialized", QUOTE_ID(id));
return Qnil;
}
@@ -777,7 +912,7 @@ rb_gv_get(const char *name)
ID id = find_global_id(name);
if (!id) {
- rb_warning("global variable `%s' not initialized", name);
+ rb_warning("global variable '%s' not initialized", name);
return Qnil;
}
@@ -829,7 +964,7 @@ rb_f_global_variables(void)
int i, nmatch = rb_match_count(backref);
buf[0] = '$';
for (i = 1; i <= nmatch; ++i) {
- if (!rb_match_nth_defined(i, backref)) continue;
+ if (!RTEST(rb_reg_nth_defined(i, backref))) continue;
if (i < 10) {
/* probably reused, make static ID */
buf[1] = (char)(i + '0');
@@ -921,19 +1056,6 @@ generic_ivtbl_no_ractor_check(VALUE obj)
return generic_ivtbl(obj, 0, false);
}
-static int
-gen_ivtbl_get_unlocked(VALUE obj, ID id, struct gen_ivtbl **ivtbl)
-{
- st_data_t data;
-
- if (st_lookup(generic_ivtbl(obj, id, false), (st_data_t)obj, &data)) {
- *ivtbl = (struct gen_ivtbl *)data;
- return 1;
- }
-
- return 0;
-}
-
int
rb_gen_ivtbl_get(VALUE obj, ID id, struct gen_ivtbl **ivtbl)
{
@@ -963,7 +1085,7 @@ rb_ivar_generic_ivtbl_lookup(VALUE obj, struct gen_ivtbl **ivtbl)
static size_t
gen_ivtbl_bytes(size_t n)
{
- return offsetof(struct gen_ivtbl, ivptr) + n * sizeof(VALUE);
+ return offsetof(struct gen_ivtbl, as.shape.ivptr) + n * sizeof(VALUE);
}
static struct gen_ivtbl *
@@ -971,73 +1093,48 @@ gen_ivtbl_resize(struct gen_ivtbl *old, uint32_t n)
{
RUBY_ASSERT(n > 0);
- uint32_t len = old ? old->numiv : 0;
+ uint32_t len = old ? old->as.shape.numiv : 0;
struct gen_ivtbl *ivtbl = xrealloc(old, gen_ivtbl_bytes(n));
- ivtbl->numiv = n;
+ ivtbl->as.shape.numiv = n;
for (; len < n; len++) {
- ivtbl->ivptr[len] = Qundef;
+ ivtbl->as.shape.ivptr[len] = Qundef;
}
return ivtbl;
}
-#if 0
-static struct gen_ivtbl *
-gen_ivtbl_dup(const struct gen_ivtbl *orig)
-{
- size_t s = gen_ivtbl_bytes(orig->numiv);
- struct gen_ivtbl *ivtbl = xmalloc(s);
-
- memcpy(ivtbl, orig, s);
-
- return ivtbl;
-}
-#endif
-
-static int
-generic_ivar_update(st_data_t *k, st_data_t *v, st_data_t u, int existing)
+void
+rb_mark_generic_ivar(VALUE obj)
{
- ASSERT_vm_locking();
-
- struct ivar_update *ivup = (struct ivar_update *)u;
- struct gen_ivtbl *ivtbl = 0;
+ struct gen_ivtbl *ivtbl;
- if (existing) {
- ivtbl = (struct gen_ivtbl *)*v;
- if (ivup->iv_index < ivtbl->numiv) {
- ivup->ivtbl = ivtbl;
- return ST_STOP;
+ if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) {
+ if (rb_shape_obj_too_complex(obj)) {
+ rb_mark_tbl_no_pin(ivtbl->as.complex.table);
+ }
+ else {
+ for (uint32_t i = 0; i < ivtbl->as.shape.numiv; i++) {
+ rb_gc_mark_movable(ivtbl->as.shape.ivptr[i]);
+ }
}
- }
- FL_SET((VALUE)*k, FL_EXIVAR);
- ivtbl = gen_ivtbl_resize(ivtbl, ivup->max_index);
- // Reinsert in to the hash table because ivtbl might be a newly resized chunk of memory
- *v = (st_data_t)ivtbl;
- ivup->ivtbl = ivtbl;
-#if !SHAPE_IN_BASIC_FLAGS
- ivtbl->shape_id = rb_shape_id(ivup->shape);
-#endif
- return ST_CONTINUE;
-}
-
-static void
-gen_ivtbl_mark_and_update(struct gen_ivtbl *ivtbl)
-{
- uint32_t i;
-
- for (i = 0; i < ivtbl->numiv; i++) {
- rb_gc_mark_and_move(&ivtbl->ivptr[i]);
}
}
void
-rb_mark_and_update_generic_ivar(VALUE obj)
+rb_ref_update_generic_ivar(VALUE obj)
{
struct gen_ivtbl *ivtbl;
if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) {
- gen_ivtbl_mark_and_update(ivtbl);
+ if (rb_shape_obj_too_complex(obj)) {
+ rb_gc_ref_update_table_values_only(ivtbl->as.complex.table);
+ }
+ else {
+ for (uint32_t i = 0; i < ivtbl->as.shape.numiv; i++) {
+ ivtbl->as.shape.ivptr[i] = rb_gc_location(ivtbl->as.shape.ivptr[i]);
+ }
+ }
}
}
@@ -1054,19 +1151,34 @@ rb_mv_generic_ivar(VALUE rsrc, VALUE dst)
void
rb_free_generic_ivar(VALUE obj)
{
- st_data_t key = (st_data_t)obj, ivtbl;
+ st_data_t key = (st_data_t)obj, value;
+
+ bool too_complex = rb_shape_obj_too_complex(obj);
+
+ if (st_delete(generic_ivtbl_no_ractor_check(obj), &key, &value)) {
+ struct gen_ivtbl *ivtbl = (struct gen_ivtbl *)value;
+
+ if (UNLIKELY(too_complex)) {
+ st_free_table(ivtbl->as.complex.table);
+ }
- if (st_delete(generic_ivtbl_no_ractor_check(obj), &key, &ivtbl))
- xfree((struct gen_ivtbl *)ivtbl);
+ xfree(ivtbl);
+ }
}
-RUBY_FUNC_EXPORTED size_t
+size_t
rb_generic_ivar_memsize(VALUE obj)
{
struct gen_ivtbl *ivtbl;
- if (rb_gen_ivtbl_get(obj, 0, &ivtbl))
- return gen_ivtbl_bytes(ivtbl->numiv);
+ if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) {
+ if (rb_shape_obj_too_complex(obj)) {
+ return sizeof(struct gen_ivtbl) + st_memsize(ivtbl->as.complex.table);
+ }
+ else {
+ return gen_ivtbl_bytes(ivtbl->as.shape.numiv);
+ }
+ }
return 0;
}
@@ -1095,14 +1207,19 @@ rb_generic_shape_id(VALUE obj)
#endif
static size_t
-gen_ivtbl_count(const struct gen_ivtbl *ivtbl)
+gen_ivtbl_count(VALUE obj, const struct gen_ivtbl *ivtbl)
{
uint32_t i;
size_t n = 0;
- for (i = 0; i < ivtbl->numiv; i++) {
- if (!UNDEF_P(ivtbl->ivptr[i])) {
- n++;
+ if (rb_shape_obj_too_complex(obj)) {
+ n = st_table_size(ivtbl->as.complex.table);
+ }
+ else {
+ for (i = 0; i < ivtbl->as.shape.numiv; i++) {
+ if (!UNDEF_P(ivtbl->as.shape.ivptr[i])) {
+ n++;
+ }
}
}
@@ -1126,7 +1243,7 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef)
case T_CLASS:
case T_MODULE:
{
- bool found;
+ bool found = false;
VALUE val;
RB_VM_LOCK_ENTER();
@@ -1135,18 +1252,29 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef)
shape_id = RCLASS_SHAPE_ID(obj);
#endif
- attr_index_t index = 0;
- shape = rb_shape_get_shape_by_id(shape_id);
- found = rb_shape_get_iv_index(shape, id, &index);
-
- if (found) {
- ivar_list = RCLASS_IVPTR(obj);
- RUBY_ASSERT(ivar_list);
-
- val = ivar_list[index];
+ if (rb_shape_obj_too_complex(obj)) {
+ st_table * iv_table = RCLASS_IV_HASH(obj);
+ if (rb_st_lookup(iv_table, (st_data_t)id, (st_data_t *)&val)) {
+ found = true;
+ }
+ else {
+ val = undef;
+ }
}
else {
- val = undef;
+ attr_index_t index = 0;
+ shape = rb_shape_get_shape_by_id(shape_id);
+ found = rb_shape_get_iv_index(shape, id, &index);
+
+ if (found) {
+ ivar_list = RCLASS_IVPTR(obj);
+ RUBY_ASSERT(ivar_list);
+
+ val = ivar_list[index];
+ }
+ else {
+ val = undef;
+ }
}
}
RB_VM_LOCK_LEAVE();
@@ -1184,10 +1312,21 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef)
if (FL_TEST_RAW(obj, FL_EXIVAR)) {
struct gen_ivtbl *ivtbl;
rb_gen_ivtbl_get(obj, id, &ivtbl);
+
+ if (rb_shape_obj_too_complex(obj)) {
+ VALUE val;
+ if (rb_st_lookup(ivtbl->as.complex.table, (st_data_t)id, (st_data_t *)&val)) {
+ return val;
+ }
+ else {
+ return undef;
+ }
+ }
+
#if !SHAPE_IN_BASIC_FLAGS
shape_id = ivtbl->shape_id;
#endif
- ivar_list = ivtbl->ivptr;
+ ivar_list = ivtbl->as.shape.ivptr;
}
else {
return undef;
@@ -1224,25 +1363,42 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef)
rb_check_frozen(obj);
VALUE val = undef;
- rb_shape_t * shape = rb_shape_get_shape(obj);
+ rb_shape_t *shape = rb_shape_get_shape(obj);
- switch (BUILTIN_TYPE(obj)) {
- case T_CLASS:
- case T_MODULE:
+ if (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) {
IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id);
+ }
- RB_VM_LOCK_ENTER();
- {
- rb_shape_transition_shape_remove_ivar(obj, id, shape, &val);
+ if (!rb_shape_transition_shape_remove_ivar(obj, id, shape, &val)) {
+ if (!rb_shape_obj_too_complex(obj)) {
+ rb_evict_ivars_to_hash(obj);
}
- RB_VM_LOCK_LEAVE();
- break;
- default: {
- rb_shape_transition_shape_remove_ivar(obj, id, shape, &val);
+ st_table *table = NULL;
+ switch (BUILTIN_TYPE(obj)) {
+ case T_CLASS:
+ case T_MODULE:
+ table = RCLASS_IV_HASH(obj);
+ break;
- break;
- }
+ case T_OBJECT:
+ table = ROBJECT_IV_HASH(obj);
+ break;
+
+ default: {
+ struct gen_ivtbl *ivtbl;
+ if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) {
+ table = ivtbl->as.complex.table;
+ }
+ break;
+ }
+ }
+
+ if (table) {
+ if (!st_delete(table, (st_data_t *)&id, (st_data_t *)&val)) {
+ val = undef;
+ }
+ }
}
return val;
@@ -1254,243 +1410,348 @@ rb_attr_delete(VALUE obj, ID id)
return rb_ivar_delete(obj, id, Qnil);
}
-static void
-generic_ivar_set(VALUE obj, ID id, VALUE val)
+void
+rb_obj_convert_to_too_complex(VALUE obj, st_table *table)
{
- struct ivar_update ivup;
-
- attr_index_t index;
- // The returned shape will have `id` in its iv_table
- rb_shape_t *shape = rb_shape_get_shape(obj);
- bool found = rb_shape_get_iv_index(shape, id, &index);
- if (!found) {
- index = shape->next_iv_index;
- shape = rb_shape_get_next(shape, obj, id);
- RUBY_ASSERT(index == (shape->next_iv_index - 1));
- }
+ RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
- ivup.max_index = shape->next_iv_index;
-#if !SHAPE_IN_BASIC_FLAGS
- ivup.shape = shape;
-#endif
+ VALUE *old_ivptr = NULL;
- RB_VM_LOCK_ENTER();
- {
- ivup.iv_index = (uint32_t)index;
+ switch (BUILTIN_TYPE(obj)) {
+ case T_OBJECT:
+ if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) {
+ old_ivptr = ROBJECT_IVPTR(obj);
+ }
+ rb_shape_set_shape_id(obj, OBJ_TOO_COMPLEX_SHAPE_ID);
+ ROBJECT_SET_IV_HASH(obj, table);
+ break;
+ case T_CLASS:
+ case T_MODULE:
+ old_ivptr = RCLASS_IVPTR(obj);
+ rb_shape_set_shape_id(obj, OBJ_TOO_COMPLEX_SHAPE_ID);
+ RCLASS_SET_IV_HASH(obj, table);
+ break;
+ default:
+ RB_VM_LOCK_ENTER();
+ {
+ struct st_table *gen_ivs = generic_ivtbl_no_ractor_check(obj);
- st_update(generic_ivtbl(obj, id, false), (st_data_t)obj, generic_ivar_update, (st_data_t)&ivup);
- }
- RB_VM_LOCK_LEAVE();
+ struct gen_ivtbl *old_ivtbl = NULL;
+ st_lookup(gen_ivs, (st_data_t)obj, (st_data_t *)&old_ivtbl);
- ivup.ivtbl->ivptr[ivup.iv_index] = val;
- RB_OBJ_WRITTEN(obj, Qundef, val);
+ if (old_ivtbl) {
+ /* We need to modify old_ivtbl to have the too complex shape
+ * and hold the table because the xmalloc could trigger a GC
+ * compaction. We want the table to be updated rather than
+ * the original ivptr. */
+#if SHAPE_IN_BASIC_FLAGS
+ rb_shape_set_shape_id(obj, OBJ_TOO_COMPLEX_SHAPE_ID);
+#else
+ old_ivtbl->shape_id = OBJ_TOO_COMPLEX_SHAPE_ID;
+#endif
+ old_ivtbl->as.complex.table = table;
+ old_ivptr = (VALUE *)old_ivtbl;
+ }
- if (!found) {
- rb_shape_set_shape(obj, shape);
+ struct gen_ivtbl *ivtbl = xmalloc(sizeof(struct gen_ivtbl));
+ ivtbl->as.complex.table = table;
+ st_insert(gen_ivs, (st_data_t)obj, (st_data_t)ivtbl);
+#if SHAPE_IN_BASIC_FLAGS
+ rb_shape_set_shape_id(obj, OBJ_TOO_COMPLEX_SHAPE_ID);
+#else
+ ivtbl->shape_id = OBJ_TOO_COMPLEX_SHAPE_ID;
+#endif
+ }
+ RB_VM_LOCK_LEAVE();
}
+
+ xfree(old_ivptr);
}
-static VALUE *
-obj_ivar_heap_alloc(VALUE obj, size_t newsize)
+void
+rb_evict_ivars_to_hash(VALUE obj)
{
- VALUE *newptr = rb_transient_heap_alloc(obj, sizeof(VALUE) * newsize);
+ RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
- if (newptr != NULL) {
- ROBJ_TRANSIENT_SET(obj);
- }
- else {
- ROBJ_TRANSIENT_UNSET(obj);
- newptr = ALLOC_N(VALUE, newsize);
- }
- return newptr;
+ st_table *table = st_init_numtable_with_size(rb_ivar_count(obj));
+
+ // Evacuate all previous values from shape into id_table
+ rb_obj_copy_ivs_to_hash_table(obj, table);
+ rb_obj_convert_to_too_complex(obj, table);
+
+ RUBY_ASSERT(rb_shape_obj_too_complex(obj));
}
-static VALUE *
-obj_ivar_heap_realloc(VALUE obj, int32_t len, size_t newsize)
-{
- VALUE *newptr;
- int i;
+struct general_ivar_set_result {
+ attr_index_t index;
+ bool existing;
+};
- if (ROBJ_TRANSIENT_P(obj)) {
- const VALUE *orig_ptr = ROBJECT(obj)->as.heap.ivptr;
- newptr = obj_ivar_heap_alloc(obj, newsize);
+static struct general_ivar_set_result
+general_ivar_set(VALUE obj, ID id, VALUE val, void *data,
+ VALUE *(*shape_ivptr_func)(VALUE, void *),
+ void (*shape_resize_ivptr_func)(VALUE, attr_index_t, attr_index_t, void *),
+ void (*set_shape_func)(VALUE, rb_shape_t *, void *),
+ void (*transition_too_complex_func)(VALUE, void *),
+ st_table *(*too_complex_table_func)(VALUE, void *))
+{
+ struct general_ivar_set_result result = {
+ .index = 0,
+ .existing = true
+ };
- assert(newptr);
- ROBJECT(obj)->as.heap.ivptr = newptr;
- for (i=0; i<(int)len; i++) {
- newptr[i] = orig_ptr[i];
- }
+ rb_shape_t *current_shape = rb_shape_get_shape(obj);
+
+ if (UNLIKELY(current_shape->type == SHAPE_OBJ_TOO_COMPLEX)) {
+ goto too_complex;
}
- else {
- REALLOC_N(ROBJECT(obj)->as.heap.ivptr, VALUE, newsize);
- newptr = ROBJECT(obj)->as.heap.ivptr;
+
+ attr_index_t index;
+ if (!rb_shape_get_iv_index(current_shape, id, &index)) {
+ result.existing = false;
+
+ index = current_shape->next_iv_index;
+ if (index >= MAX_IVARS) {
+ rb_raise(rb_eArgError, "too many instance variables");
+ }
+
+ rb_shape_t *next_shape = rb_shape_get_next(current_shape, obj, id);
+ if (UNLIKELY(next_shape->type == SHAPE_OBJ_TOO_COMPLEX)) {
+ transition_too_complex_func(obj, data);
+ goto too_complex;
+ }
+ else if (UNLIKELY(next_shape->capacity != current_shape->capacity)) {
+ RUBY_ASSERT(next_shape->capacity > current_shape->capacity);
+ shape_resize_ivptr_func(obj, current_shape->capacity, next_shape->capacity, data);
+ }
+
+ RUBY_ASSERT(next_shape->type == SHAPE_IVAR);
+ RUBY_ASSERT(index == (next_shape->next_iv_index - 1));
+ set_shape_func(obj, next_shape, data);
}
- return newptr;
+ VALUE *table = shape_ivptr_func(obj, data);
+ RB_OBJ_WRITE(obj, &table[index], val);
+
+ result.index = index;
+ return result;
+
+too_complex:
+ {
+ RUBY_ASSERT(rb_shape_obj_too_complex(obj));
+
+ st_table *table = too_complex_table_func(obj, data);
+ result.existing = st_insert(table, (st_data_t)id, (st_data_t)val);
+ result.index = 0;
+ RB_OBJ_WRITTEN(obj, Qundef, val);
+ }
+ return result;
}
-#if USE_TRANSIENT_HEAP
-void
-rb_obj_transient_heap_evacuate(VALUE obj, int promote)
+struct gen_ivar_lookup_ensure_size {
+ VALUE obj;
+ ID id;
+ struct gen_ivtbl *ivtbl;
+ rb_shape_t *shape;
+ bool resize;
+};
+
+static int
+generic_ivar_lookup_ensure_size(st_data_t *k, st_data_t *v, st_data_t u, int existing)
{
- if (ROBJ_TRANSIENT_P(obj)) {
- assert(!RB_FL_TEST_RAW(obj, ROBJECT_EMBED));
+ ASSERT_vm_locking();
- uint32_t len = ROBJECT_IV_CAPACITY(obj);
- RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
- const VALUE *old_ptr = ROBJECT_IVPTR(obj);
- VALUE *new_ptr;
+ struct gen_ivar_lookup_ensure_size *ivar_lookup = (struct gen_ivar_lookup_ensure_size *)u;
+ struct gen_ivtbl *ivtbl = existing ? (struct gen_ivtbl *)*v : NULL;
- if (promote) {
- new_ptr = ALLOC_N(VALUE, len);
- ROBJ_TRANSIENT_UNSET(obj);
+ if (!existing || ivar_lookup->resize) {
+ if (existing) {
+ RUBY_ASSERT(ivar_lookup->shape->type == SHAPE_IVAR);
+ RUBY_ASSERT(rb_shape_get_shape_by_id(ivar_lookup->shape->parent_id)->capacity < ivar_lookup->shape->capacity);
}
else {
- new_ptr = obj_ivar_heap_alloc(obj, len);
+ FL_SET_RAW((VALUE)*k, FL_EXIVAR);
}
- MEMCPY(new_ptr, old_ptr, VALUE, len);
- ROBJECT(obj)->as.heap.ivptr = new_ptr;
+
+ ivtbl = gen_ivtbl_resize(ivtbl, ivar_lookup->shape->capacity);
+ *v = (st_data_t)ivtbl;
}
-}
-#endif
-void
-rb_ensure_iv_list_size(VALUE obj, uint32_t current_capacity, uint32_t new_capacity)
-{
- RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
- VALUE *ptr = ROBJECT_IVPTR(obj);
- VALUE *newptr;
+ RUBY_ASSERT(FL_TEST((VALUE)*k, FL_EXIVAR));
- if (RBASIC(obj)->flags & ROBJECT_EMBED) {
- newptr = obj_ivar_heap_alloc(obj, new_capacity);
- MEMCPY(newptr, ptr, VALUE, current_capacity);
- RB_FL_UNSET_RAW(obj, ROBJECT_EMBED);
- ROBJECT(obj)->as.heap.ivptr = newptr;
- }
- else {
- newptr = obj_ivar_heap_realloc(obj, current_capacity, new_capacity);
+ ivar_lookup->ivtbl = ivtbl;
+ if (ivar_lookup->shape) {
+#if SHAPE_IN_BASIC_FLAGS
+ rb_shape_set_shape(ivar_lookup->obj, ivar_lookup->shape);
+#else
+ ivtbl->shape_id = rb_shape_id(ivar_lookup->shape);
+#endif
}
+
+ return ST_CONTINUE;
}
-struct gen_ivtbl *
-rb_ensure_generic_iv_list_size(VALUE obj, rb_shape_t *shape, uint32_t newsize)
+static VALUE *
+generic_ivar_set_shape_ivptr(VALUE obj, void *data)
{
- struct gen_ivtbl * ivtbl = 0;
+ RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
+
+ struct gen_ivar_lookup_ensure_size *ivar_lookup = data;
RB_VM_LOCK_ENTER();
{
- if (UNLIKELY(!gen_ivtbl_get_unlocked(obj, 0, &ivtbl) || newsize > ivtbl->numiv)) {
- struct ivar_update ivup = {
- .iv_index = newsize - 1,
- .max_index = newsize,
-#if !SHAPE_IN_BASIC_FLAGS
- .shape = shape
-#endif
- };
- st_update(generic_ivtbl_no_ractor_check(obj), (st_data_t)obj, generic_ivar_update, (st_data_t)&ivup);
- ivtbl = ivup.ivtbl;
- FL_SET_RAW(obj, FL_EXIVAR);
- }
+ st_update(generic_ivtbl(obj, ivar_lookup->id, false), (st_data_t)obj, generic_ivar_lookup_ensure_size, (st_data_t)ivar_lookup);
}
RB_VM_LOCK_LEAVE();
- RUBY_ASSERT(ivtbl);
+ FL_SET_RAW(obj, FL_EXIVAR);
- return ivtbl;
+ return ivar_lookup->ivtbl->as.shape.ivptr;
}
-// @note May raise when there are too many instance variables.
-rb_shape_t *
-rb_grow_iv_list(VALUE obj)
+static void
+generic_ivar_set_shape_resize_ivptr(VALUE obj, attr_index_t _old_capa, attr_index_t new_capa, void *data)
{
- rb_shape_t * initial_shape = rb_shape_get_shape(obj);
- uint32_t len = initial_shape->capacity;
- RUBY_ASSERT(len > 0);
- uint32_t newsize = (uint32_t)(len * 2);
-
- rb_shape_t * res = rb_shape_transition_shape_capa(initial_shape, newsize);
+ struct gen_ivar_lookup_ensure_size *ivar_lookup = data;
- rb_ensure_iv_list_size(obj, len, newsize);
+ ivar_lookup->resize = true;
+}
- rb_shape_set_shape(obj, res);
+static void
+generic_ivar_set_set_shape(VALUE obj, rb_shape_t *shape, void *data)
+{
+ struct gen_ivar_lookup_ensure_size *ivar_lookup = data;
- return res;
+ ivar_lookup->shape = shape;
}
-int
-rb_obj_evacuate_ivs_to_hash_table(ID key, VALUE val, st_data_t arg)
+static void
+generic_ivar_set_transition_too_complex(VALUE obj, void *_data)
{
- st_insert((st_table *)arg, (st_data_t)key, (st_data_t)val);
- return ST_CONTINUE;
+ rb_evict_ivars_to_hash(obj);
+ FL_SET_RAW(obj, FL_EXIVAR);
}
-attr_index_t
-rb_obj_ivar_set(VALUE obj, ID id, VALUE val)
+static st_table *
+generic_ivar_set_too_complex_table(VALUE obj, void *data)
{
- attr_index_t index;
+ struct gen_ivar_lookup_ensure_size *ivar_lookup = data;
- rb_shape_t *shape = rb_shape_get_shape(obj);
- uint32_t num_iv = shape->capacity;
+ struct gen_ivtbl *ivtbl;
+ if (!rb_gen_ivtbl_get(obj, 0, &ivtbl)) {
+ ivtbl = xmalloc(sizeof(struct gen_ivtbl));
+#if !SHAPE_IN_BASIC_FLAGS
+ ivtbl->shape_id = SHAPE_OBJ_TOO_COMPLEX;
+#endif
+ ivtbl->as.complex.table = st_init_numtable_with_size(1);
- if (rb_shape_obj_too_complex(obj)) {
- st_table * table = ROBJECT_IV_HASH(obj);
- st_insert(table, (st_data_t)id, (st_data_t)val);
- RB_OBJ_WRITTEN(obj, Qundef, val);
- return 0;
+ RB_VM_LOCK_ENTER();
+ {
+ st_insert(generic_ivtbl(obj, ivar_lookup->id, false), (st_data_t)obj, (st_data_t)ivtbl);
+ }
+ RB_VM_LOCK_LEAVE();
+
+ FL_SET_RAW(obj, FL_EXIVAR);
}
- if (!rb_shape_get_iv_index(shape, id, &index)) {
- index = shape->next_iv_index;
- if (index >= MAX_IVARS) {
- rb_raise(rb_eArgError, "too many instance variables");
- }
+ RUBY_ASSERT(rb_shape_obj_too_complex(obj));
- RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
+ return ivtbl->as.complex.table;
+}
- if (UNLIKELY(shape->next_iv_index >= num_iv)) {
- RUBY_ASSERT(shape->next_iv_index == num_iv);
+static void
+generic_ivar_set(VALUE obj, ID id, VALUE val)
+{
+ struct gen_ivar_lookup_ensure_size ivar_lookup = {
+ .obj = obj,
+ .id = id,
+ .resize = false,
+ .shape = NULL,
+ };
- shape = rb_grow_iv_list(obj);
- RUBY_ASSERT(shape->type == SHAPE_CAPACITY_CHANGE);
- }
+ general_ivar_set(obj, id, val, &ivar_lookup,
+ generic_ivar_set_shape_ivptr,
+ generic_ivar_set_shape_resize_ivptr,
+ generic_ivar_set_set_shape,
+ generic_ivar_set_transition_too_complex,
+ generic_ivar_set_too_complex_table);
+}
- rb_shape_t *next_shape = rb_shape_get_next(shape, obj, id);
+void
+rb_ensure_iv_list_size(VALUE obj, uint32_t current_capacity, uint32_t new_capacity)
+{
+ RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
+
+ if (RBASIC(obj)->flags & ROBJECT_EMBED) {
+ VALUE *ptr = ROBJECT_IVPTR(obj);
+ VALUE *newptr = ALLOC_N(VALUE, new_capacity);
+ MEMCPY(newptr, ptr, VALUE, current_capacity);
+ RB_FL_UNSET_RAW(obj, ROBJECT_EMBED);
+ ROBJECT(obj)->as.heap.ivptr = newptr;
+ }
+ else {
+ REALLOC_N(ROBJECT(obj)->as.heap.ivptr, VALUE, new_capacity);
+ }
+}
+
+static int
+rb_obj_copy_ivs_to_hash_table_i(ID key, VALUE val, st_data_t arg)
+{
+ RUBY_ASSERT(!st_lookup((st_table *)arg, (st_data_t)key, NULL));
+
+ st_add_direct((st_table *)arg, (st_data_t)key, (st_data_t)val);
+ return ST_CONTINUE;
+}
- if (next_shape->type == SHAPE_OBJ_TOO_COMPLEX) {
- st_table * table = st_init_numtable_with_size(shape->next_iv_index);
+void
+rb_obj_copy_ivs_to_hash_table(VALUE obj, st_table *table)
+{
+ rb_ivar_foreach(obj, rb_obj_copy_ivs_to_hash_table_i, (st_data_t)table);
+}
- // Evacuate all previous values from shape into id_table
- rb_ivar_foreach(obj, rb_obj_evacuate_ivs_to_hash_table, (st_data_t)table);
+static VALUE *
+obj_ivar_set_shape_ivptr(VALUE obj, void *_data)
+{
+ RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
- // Insert new value too
- st_insert(table, (st_data_t)id, (st_data_t)val);
- RB_OBJ_WRITTEN(obj, Qundef, val);
+ return ROBJECT_IVPTR(obj);
+}
- rb_shape_set_too_complex(obj);
- RUBY_ASSERT(rb_shape_obj_too_complex(obj));
+static void
+obj_ivar_set_shape_resize_ivptr(VALUE obj, attr_index_t old_capa, attr_index_t new_capa, void *_data)
+{
+ rb_ensure_iv_list_size(obj, old_capa, new_capa);
+}
- if (ROBJ_TRANSIENT_P(obj)) {
- ROBJ_TRANSIENT_UNSET(obj);
- }
- else if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) {
- xfree(ROBJECT(obj)->as.heap.ivptr);
- }
+static void
+obj_ivar_set_set_shape(VALUE obj, rb_shape_t *shape, void *_data)
+{
+ rb_shape_set_shape(obj, shape);
+}
- ROBJECT(obj)->as.heap.ivptr = (VALUE *)table;
+static void
+obj_ivar_set_transition_too_complex(VALUE obj, void *_data)
+{
+ rb_evict_ivars_to_hash(obj);
+}
- return 0;
- }
- else {
- rb_shape_set_shape(obj, next_shape);
- RUBY_ASSERT(next_shape->type == SHAPE_IVAR);
- RUBY_ASSERT(index == (next_shape->next_iv_index - 1));
- }
- }
+static st_table *
+obj_ivar_set_too_complex_table(VALUE obj, void *_data)
+{
+ RUBY_ASSERT(rb_shape_obj_too_complex(obj));
- RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
- RB_OBJ_WRITE(obj, &ROBJECT_IVPTR(obj)[index], val);
+ return ROBJECT_IV_HASH(obj);
+}
- return index;
+attr_index_t
+rb_obj_ivar_set(VALUE obj, ID id, VALUE val)
+{
+ return general_ivar_set(obj, id, val, NULL,
+ obj_ivar_set_shape_ivptr,
+ obj_ivar_set_shape_resize_ivptr,
+ obj_ivar_set_set_shape,
+ obj_ivar_set_transition_too_complex,
+ obj_ivar_set_too_complex_table).index;
}
/* Set the instance variable +val+ on object +obj+ at ivar name +id+.
@@ -1554,11 +1815,21 @@ rb_shape_set_shape_id(VALUE obj, shape_id_t shape_id)
void rb_obj_freeze_inline(VALUE x)
{
if (RB_FL_ABLE(x)) {
- RB_OBJ_FREEZE_RAW(x);
+ RB_FL_SET_RAW(x, RUBY_FL_FREEZE);
+ if (TYPE(x) == T_STRING) {
+ RB_FL_UNSET_RAW(x, FL_USER3); // STR_CHILLED
+ }
+
+ rb_shape_t * next_shape = rb_shape_transition_shape_frozen(x);
- rb_shape_transition_shape_frozen(x);
+ // If we're transitioning from "not complex" to "too complex"
+ // then evict ivars. This can happen if we run out of shapes
+ if (!rb_shape_obj_too_complex(x) && next_shape->type == SHAPE_OBJ_TOO_COMPLEX) {
+ rb_evict_ivars_to_hash(x);
+ }
+ rb_shape_set_shape(x, next_shape);
- if (RBASIC_CLASS(x) && !(RBASIC(x)->flags & RUBY_FL_SINGLETON)) {
+ if (RBASIC_CLASS(x)) {
rb_freeze_singleton_class(x);
}
}
@@ -1572,8 +1843,8 @@ ivar_set(VALUE obj, ID id, VALUE val)
switch (BUILTIN_TYPE(obj)) {
case T_OBJECT:
{
- rb_obj_ivar_set(obj, id, val);
- break;
+ rb_obj_ivar_set(obj, id, val);
+ break;
}
case T_CLASS:
case T_MODULE:
@@ -1612,7 +1883,27 @@ rb_ivar_defined(VALUE obj, ID id)
if (SPECIAL_CONST_P(obj)) return Qfalse;
if (rb_shape_obj_too_complex(obj)) {
VALUE idx;
- if (!rb_st_lookup(ROBJECT_IV_HASH(obj), id, &idx)) {
+ st_table *table = NULL;
+ switch (BUILTIN_TYPE(obj)) {
+ case T_CLASS:
+ case T_MODULE:
+ table = (st_table *)RCLASS_IVPTR(obj);
+ break;
+
+ case T_OBJECT:
+ table = ROBJECT_IV_HASH(obj);
+ break;
+
+ default: {
+ struct gen_ivtbl *ivtbl;
+ if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) {
+ table = ivtbl->as.complex.table;
+ }
+ break;
+ }
+ }
+
+ if (!table || !rb_st_lookup(table, id, &idx)) {
return Qfalse;
}
@@ -1633,14 +1924,20 @@ struct iv_itr_data {
rb_ivar_foreach_callback_func *func;
};
-static void
+/*
+ * Returns a flag to stop iterating depending on the result of +callback+.
+ */
+static bool
iterate_over_shapes_with_callback(rb_shape_t *shape, rb_ivar_foreach_callback_func *callback, struct iv_itr_data * itr_data)
{
switch ((enum shape_type)shape->type) {
case SHAPE_ROOT:
- return;
+ case SHAPE_T_OBJECT:
+ return false;
case SHAPE_IVAR:
- iterate_over_shapes_with_callback(rb_shape_get_parent(shape), callback, itr_data);
+ ASSUME(callback);
+ if (iterate_over_shapes_with_callback(rb_shape_get_parent(shape), callback, itr_data))
+ return true;
VALUE * iv_list;
switch (BUILTIN_TYPE(itr_data->obj)) {
case T_OBJECT:
@@ -1652,21 +1949,26 @@ iterate_over_shapes_with_callback(rb_shape_t *shape, rb_ivar_foreach_callback_fu
iv_list = RCLASS_IVPTR(itr_data->obj);
break;
default:
- iv_list = itr_data->ivtbl->ivptr;
+ iv_list = itr_data->ivtbl->as.shape.ivptr;
break;
}
VALUE val = iv_list[shape->next_iv_index - 1];
if (!UNDEF_P(val)) {
- callback(shape->edge_name, val, itr_data->arg);
+ switch (callback(shape->edge_name, val, itr_data->arg)) {
+ case ST_CHECK:
+ case ST_CONTINUE:
+ break;
+ case ST_STOP:
+ return true;
+ default:
+ rb_bug("unreachable");
+ }
}
- return;
- case SHAPE_INITIAL_CAPACITY:
- case SHAPE_CAPACITY_CHANGE:
+ return false;
case SHAPE_FROZEN:
- case SHAPE_T_OBJECT:
- iterate_over_shapes_with_callback(rb_shape_get_parent(shape), callback, itr_data);
- return;
+ return iterate_over_shapes_with_callback(rb_shape_get_parent(shape), callback, itr_data);
case SHAPE_OBJ_TOO_COMPLEX:
+ default:
rb_bug("Unreachable");
}
}
@@ -1706,7 +2008,13 @@ gen_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg)
itr_data.obj = obj;
itr_data.ivtbl = ivtbl;
itr_data.arg = arg;
- iterate_over_shapes_with_callback(shape, func, &itr_data);
+ itr_data.func = func;
+ if (rb_shape_obj_too_complex(obj)) {
+ rb_st_foreach(ivtbl->as.complex.table, each_hash_iv, (st_data_t)&itr_data);
+ }
+ else {
+ iterate_over_shapes_with_callback(shape, func, &itr_data);
+ }
}
static void
@@ -1718,7 +2026,13 @@ class_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg)
struct iv_itr_data itr_data;
itr_data.obj = obj;
itr_data.arg = arg;
- iterate_over_shapes_with_callback(shape, func, &itr_data);
+ itr_data.func = func;
+ if (rb_shape_obj_too_complex(obj)) {
+ rb_st_foreach(RCLASS_IV_HASH(obj), each_hash_iv, (st_data_t)&itr_data);
+ }
+ else {
+ iterate_over_shapes_with_callback(shape, func, &itr_data);
+ }
}
void
@@ -1734,15 +2048,24 @@ rb_copy_generic_ivar(VALUE clone, VALUE obj)
}
if (rb_gen_ivtbl_get(obj, 0, &obj_ivtbl)) {
- if (gen_ivtbl_count(obj_ivtbl) == 0)
+ if (gen_ivtbl_count(obj, obj_ivtbl) == 0)
goto clear;
- new_ivtbl = gen_ivtbl_resize(0, obj_ivtbl->numiv);
FL_SET(clone, FL_EXIVAR);
- for (uint32_t i=0; i<obj_ivtbl->numiv; i++) {
- new_ivtbl->ivptr[i] = obj_ivtbl->ivptr[i];
- RB_OBJ_WRITTEN(clone, Qundef, &new_ivtbl[i]);
+ if (rb_shape_obj_too_complex(obj)) {
+ new_ivtbl = xmalloc(sizeof(struct gen_ivtbl));
+#if !SHAPE_IN_BASIC_FLAGS
+ new_ivtbl->shape_id = SHAPE_OBJ_TOO_COMPLEX;
+#endif
+ new_ivtbl->as.complex.table = st_copy(obj_ivtbl->as.complex.table);
+ }
+ else {
+ new_ivtbl = gen_ivtbl_resize(0, obj_ivtbl->as.shape.numiv);
+
+ for (uint32_t i=0; i<obj_ivtbl->as.shape.numiv; i++) {
+ RB_OBJ_WRITE(clone, &new_ivtbl->as.shape.ivptr[i], obj_ivtbl->as.shape.ivptr[i]);
+ }
}
/*
@@ -1826,47 +2149,16 @@ rb_ivar_count(VALUE obj)
switch (BUILTIN_TYPE(obj)) {
case T_OBJECT:
- if (rb_shape_obj_too_complex(obj)) {
- return ROBJECT_IV_COUNT(obj);
- }
-
- if (rb_shape_get_shape(obj)->next_iv_index > 0) {
- st_index_t i, count, num = ROBJECT_IV_COUNT(obj);
- const VALUE *const ivptr = ROBJECT_IVPTR(obj);
- for (i = count = 0; i < num; ++i) {
- if (!UNDEF_P(ivptr[i])) {
- count++;
- }
- }
- return count;
- }
- break;
+ return ROBJECT_IV_COUNT(obj);
case T_CLASS:
case T_MODULE:
- if (rb_shape_get_shape(obj)->next_iv_index > 0) {
- st_index_t count = 0;
-
- RB_VM_LOCK_ENTER();
- {
- st_index_t i, num = rb_shape_get_shape(obj)->next_iv_index;
- const VALUE *const ivptr = RCLASS_IVPTR(obj);
- for (i = count = 0; i < num; ++i) {
- if (!UNDEF_P(ivptr[i])) {
- count++;
- }
- }
- }
- RB_VM_LOCK_LEAVE();
-
- return count;
- }
- break;
+ return RCLASS_IV_COUNT(obj);
default:
if (FL_TEST(obj, FL_EXIVAR)) {
struct gen_ivtbl *ivtbl;
if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) {
- return gen_ivtbl_count(ivtbl);
+ return gen_ivtbl_count(obj, ivtbl);
}
}
break;
@@ -1875,9 +2167,8 @@ rb_ivar_count(VALUE obj)
}
static int
-ivar_i(st_data_t k, st_data_t v, st_data_t a)
+ivar_i(ID key, VALUE v, st_data_t a)
{
- ID key = (ID)k;
VALUE ary = (VALUE)a;
if (rb_is_instance_id(key)) {
@@ -1916,7 +2207,7 @@ rb_obj_instance_variables(VALUE obj)
#define rb_is_constant_id rb_is_const_id
#define rb_is_constant_name rb_is_const_name
#define id_for_var(obj, name, part, type) \
- id_for_var_message(obj, name, type, "`%1$s' is not allowed as "#part" "#type" variable name")
+ id_for_var_message(obj, name, type, "'%1$s' is not allowed as "#part" "#type" variable name")
#define id_for_var_message(obj, name, type, message) \
check_id_type(obj, &(name), rb_is_##type##_id, rb_is_##type##_name, message, strlen(message))
static ID
@@ -1940,8 +2231,7 @@ check_id_type(VALUE obj, VALUE *pname,
* obj.remove_instance_variable(string) -> obj
*
* Removes the named instance variable from <i>obj</i>, returning that
- * variable's value.
- * String arguments are converted to symbols.
+ * variable's value. The name can be passed as a symbol or as a string.
*
* class Dummy
* attr_reader :var
@@ -1961,47 +2251,18 @@ check_id_type(VALUE obj, VALUE *pname,
VALUE
rb_obj_remove_instance_variable(VALUE obj, VALUE name)
{
- VALUE val = Qundef;
const ID id = id_for_var(obj, name, an, instance);
// Frozen check comes here because it's expected that we raise a
// NameError (from the id_for_var check) before we raise a FrozenError
rb_check_frozen(obj);
- if (!id) {
- goto not_defined;
- }
-
- rb_shape_t * shape = rb_shape_get_shape(obj);
+ if (id) {
+ VALUE val = rb_ivar_delete(obj, id, Qundef);
- switch (BUILTIN_TYPE(obj)) {
- case T_CLASS:
- case T_MODULE:
- IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id);
- rb_shape_transition_shape_remove_ivar(obj, id, shape, &val);
- break;
- case T_OBJECT: {
- if (rb_shape_obj_too_complex(obj)) {
- if (rb_st_lookup(ROBJECT_IV_HASH(obj), (st_data_t)id, (st_data_t *)&val)) {
- rb_st_delete(ROBJECT_IV_HASH(obj), (st_data_t *)&id, 0);
- }
- }
- else {
- rb_shape_transition_shape_remove_ivar(obj, id, shape, &val);
- }
- break;
- }
- default: {
- rb_shape_transition_shape_remove_ivar(obj, id, shape, &val);
- break;
- }
- }
-
- if (val != Qundef) {
- return val;
+ if (!UNDEF_P(val)) return val;
}
- not_defined:
rb_name_err_raise("instance variable %1$s not defined",
obj, name);
UNREACHABLE_RETURN(Qnil);
@@ -2034,8 +2295,7 @@ rb_const_missing(VALUE klass, VALUE name)
*
* Invoked when a reference is made to an undefined constant in
* <i>mod</i>. It is passed a symbol for the undefined constant, and
- * returns a value to be used for that constant. The
- * following code is an example of the same:
+ * returns a value to be used for that constant. For example, consider:
*
* def Foo.const_missing(name)
* name # return the constant name as Symbol
@@ -2043,23 +2303,28 @@ rb_const_missing(VALUE klass, VALUE name)
*
* Foo::UNDEFINED_CONST #=> :UNDEFINED_CONST: symbol returned
*
- * In the next example when a reference is made to an undefined constant,
- * it attempts to load a file whose name is the lowercase version of the
- * constant (thus class <code>Fred</code> is assumed to be in file
- * <code>fred.rb</code>). If found, it returns the loaded class. It
- * therefore implements an autoload feature similar to Kernel#autoload and
- * Module#autoload.
+ * As the example above shows, +const_missing+ is not required to create the
+ * missing constant in <i>mod</i>, though that is often a side-effect. The
+ * caller gets its return value when triggered. If the constant is also defined,
+ * further lookups won't hit +const_missing+ and will return the value stored in
+ * the constant as usual. Otherwise, +const_missing+ will be invoked again.
+ *
+ * In the next example, when a reference is made to an undefined constant,
+ * +const_missing+ attempts to load a file whose path is the lowercase version
+ * of the constant name (thus class <code>Fred</code> is assumed to be in file
+ * <code>fred.rb</code>). If defined as a side-effect of loading the file, the
+ * method returns the value stored in the constant. This implements an autoload
+ * feature similar to Kernel#autoload and Module#autoload, though it differs in
+ * important ways.
*
* def Object.const_missing(name)
* @looked_for ||= {}
* str_name = name.to_s
- * raise "Class not found: #{name}" if @looked_for[str_name]
+ * raise "Constant not found: #{name}" if @looked_for[str_name]
* @looked_for[str_name] = 1
* file = str_name.downcase
* require file
- * klass = const_get(name)
- * return klass if klass
- * raise "Class not found: #{name}"
+ * const_get(name, false)
* end
*
*/
@@ -2205,10 +2470,12 @@ autoload_data_free(void *ptr)
{
struct autoload_data *p = ptr;
- // We may leak some memory at VM shutdown time, no big deal...?
- if (ccan_list_empty(&p->constants)) {
- ruby_xfree(p);
+ struct autoload_const *autoload_const, *next;
+ ccan_list_for_each_safe(&p->constants, autoload_const, next, cnode) {
+ ccan_list_del_init(&autoload_const->cnode);
}
+
+ ruby_xfree(p);
}
static size_t
@@ -2285,7 +2552,7 @@ get_autoload_data(VALUE autoload_const_value, struct autoload_const **autoload_c
return autoload_data;
}
-RUBY_FUNC_EXPORTED void
+void
rb_autoload(VALUE module, ID name, const char *feature)
{
if (!feature || !*feature) {
@@ -2928,6 +3195,19 @@ rb_const_location_from(VALUE klass, ID id, int exclude, int recurse, int visibil
if (exclude && klass == rb_cObject) {
goto not_found;
}
+
+ if (UNDEF_P(ce->value)) { // autoload
+ VALUE autoload_const_value = autoload_data(klass, id);
+ if (RTEST(autoload_const_value)) {
+ struct autoload_const *autoload_const;
+ struct autoload_data *autoload_data = get_autoload_data(autoload_const_value, &autoload_const);
+
+ if (!UNDEF_P(autoload_const->value) && RTEST(rb_mutex_owned_p(autoload_data->mutex))) {
+ return rb_assoc_new(autoload_const->file, INT2NUM(autoload_const->line));
+ }
+ }
+ }
+
if (NIL_P(ce->file)) return rb_ary_new();
return rb_assoc_new(ce->file, INT2NUM(ce->line));
}
@@ -3438,9 +3718,11 @@ rb_define_const(VALUE klass, const char *name, VALUE val)
ID id = rb_intern(name);
if (!rb_is_const_id(id)) {
- rb_warn("rb_define_const: invalid name `%s' for constant", name);
+ rb_warn("rb_define_const: invalid name '%s' for constant", name);
+ }
+ if (!RB_SPECIAL_CONST_P(val)) {
+ rb_vm_register_global_object(val);
}
- rb_gc_register_mark_object(val);
rb_const_set(klass, id, val);
}
@@ -3594,7 +3876,7 @@ cvar_lookup_at(VALUE klass, ID id, st_data_t *v)
static VALUE
cvar_front_klass(VALUE klass)
{
- if (FL_TEST(klass, FL_SINGLETON)) {
+ if (RCLASS_SINGLETON_P(klass)) {
VALUE obj = RCLASS_ATTACHED_OBJECT(klass);
if (rb_namespace_p(obj)) {
return obj;
@@ -3776,9 +4058,8 @@ rb_define_class_variable(VALUE klass, const char *name, VALUE val)
}
static int
-cv_i(st_data_t k, st_data_t v, st_data_t a)
+cv_i(ID key, VALUE v, st_data_t a)
{
- ID key = (ID)k;
st_table *tbl = (st_table *)a;
if (rb_is_class_id(key)) {
@@ -3804,7 +4085,7 @@ static void*
mod_cvar_of(VALUE mod, void *data)
{
VALUE tmp = mod;
- if (FL_TEST(mod, FL_SINGLETON)) {
+ if (RCLASS_SINGLETON_P(mod)) {
if (rb_namespace_p(RCLASS_ATTACHED_OBJECT(mod))) {
data = mod_cvar_at(tmp, data);
tmp = cvar_front_klass(tmp);
@@ -3937,56 +4218,65 @@ rb_iv_set(VALUE obj, const char *name, VALUE val)
return rb_ivar_set(obj, id, val);
}
-/* tbl = xx(obj); tbl[key] = value; */
-int
-rb_class_ivar_set(VALUE obj, ID key, VALUE value)
+static VALUE *
+class_ivar_set_shape_ivptr(VALUE obj, void *_data)
{
- RUBY_ASSERT(RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE));
- int found;
- rb_check_frozen(obj);
+ RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
- RB_VM_LOCK_ENTER();
- {
- rb_shape_t * shape = rb_shape_get_shape(obj);
- attr_index_t idx;
- found = rb_shape_get_iv_index(shape, key, &idx);
+ return RCLASS_IVPTR(obj);
+}
- if (found) {
- // Changing an existing instance variable
- RUBY_ASSERT(RCLASS_IVPTR(obj));
+static void
+class_ivar_set_shape_resize_ivptr(VALUE obj, attr_index_t _old_capa, attr_index_t new_capa, void *_data)
+{
+ REALLOC_N(RCLASS_IVPTR(obj), VALUE, new_capa);
+}
- RCLASS_IVPTR(obj)[idx] = value;
- RB_OBJ_WRITTEN(obj, Qundef, value);
- }
- else {
- // Creating and setting a new instance variable
+static void
+class_ivar_set_set_shape(VALUE obj, rb_shape_t *shape, void *_data)
+{
+ rb_shape_set_shape(obj, shape);
+}
- // Move to a shape which fits the new ivar
- idx = shape->next_iv_index;
- shape = rb_shape_get_next(shape, obj, key);
+static void
+class_ivar_set_transition_too_complex(VALUE obj, void *_data)
+{
+ rb_evict_ivars_to_hash(obj);
+}
- // We always allocate a power of two sized IV array. This way we
- // only need to realloc when we expand into a new power of two size
- if ((idx & (idx - 1)) == 0) {
- size_t newsize = idx ? idx * 2 : 1;
- REALLOC_N(RCLASS_IVPTR(obj), VALUE, newsize);
- }
+static st_table *
+class_ivar_set_too_complex_table(VALUE obj, void *_data)
+{
+ RUBY_ASSERT(rb_shape_obj_too_complex(obj));
- RUBY_ASSERT(RCLASS_IVPTR(obj));
+ return RCLASS_IV_HASH(obj);
+}
- RB_OBJ_WRITE(obj, &RCLASS_IVPTR(obj)[idx], value);
- rb_shape_set_shape(obj, shape);
- }
+int
+rb_class_ivar_set(VALUE obj, ID id, VALUE val)
+{
+ RUBY_ASSERT(RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE));
+ bool existing = false;
+ rb_check_frozen(obj);
+
+ RB_VM_LOCK_ENTER();
+ {
+ existing = general_ivar_set(obj, id, val, NULL,
+ class_ivar_set_shape_ivptr,
+ class_ivar_set_shape_resize_ivptr,
+ class_ivar_set_set_shape,
+ class_ivar_set_transition_too_complex,
+ class_ivar_set_too_complex_table).existing;
}
RB_VM_LOCK_LEAVE();
- return found;
+ return existing;
}
static int
-tbl_copy_i(st_data_t key, st_data_t val, st_data_t dest)
+tbl_copy_i(ID key, VALUE val, st_data_t dest)
{
- rb_class_ivar_set(dest, key, val);
+ rb_class_ivar_set((VALUE)dest, key, val);
return ST_CONTINUE;
}
@@ -3997,7 +4287,7 @@ rb_iv_tbl_copy(VALUE dst, VALUE src)
RUBY_ASSERT(rb_type(dst) == rb_type(src));
RUBY_ASSERT(RB_TYPE_P(dst, T_CLASS) || RB_TYPE_P(dst, T_MODULE));
- RUBY_ASSERT(RCLASS_SHAPE_ID(dst) == ROOT_SHAPE_ID || rb_shape_get_shape_by_id(RCLASS_SHAPE_ID(dst))->type == SHAPE_INITIAL_CAPACITY);
+ RUBY_ASSERT(rb_shape_get_shape(dst)->type == SHAPE_ROOT);
RUBY_ASSERT(!RCLASS_IVPTR(dst));
rb_ivar_foreach(src, tbl_copy_i, dst);
diff --git a/variable.h b/variable.h
index 5c0366de32..1abc89ed99 100644
--- a/variable.h
+++ b/variable.h
@@ -16,8 +16,15 @@ struct gen_ivtbl {
#if !SHAPE_IN_BASIC_FLAGS
uint16_t shape_id;
#endif
- uint32_t numiv;
- VALUE ivptr[FLEX_ARY_LEN];
+ union {
+ struct {
+ uint32_t numiv;
+ VALUE ivptr[1];
+ } shape;
+ struct {
+ st_table *table;
+ } complex;
+ } as;
};
int rb_ivar_generic_ivtbl_lookup(VALUE obj, struct gen_ivtbl **);
@@ -26,5 +33,7 @@ int rb_ivar_generic_ivtbl_lookup(VALUE obj, struct gen_ivtbl **);
shape_id_t rb_generic_shape_id(VALUE obj);
#endif
+void rb_free_rb_global_tbl(void);
+void rb_free_generic_iv_tbl_(void);
#endif /* RUBY_TOPLEVEL_VARIABLE_H */
diff --git a/vcpkg.json b/vcpkg.json
new file mode 100644
index 0000000000..6d2ee3a6bc
--- /dev/null
+++ b/vcpkg.json
@@ -0,0 +1,11 @@
+{
+ "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json",
+ "dependencies": [
+ "gmp",
+ "libffi",
+ "libyaml",
+ "openssl",
+ "zlib"
+ ],
+ "builtin-baseline": "53bef8994c541b6561884a8395ea35715ece75db"
+}
diff --git a/version.c b/version.c
index c4fad96fe8..e719a59da2 100644
--- a/version.c
+++ b/version.c
@@ -39,11 +39,6 @@
# define RUBY_RELEASE_DATETIME RUBY_RELEASE_DATE
#endif
-# define RUBY_DESCRIPTION_WITH(opt) \
- "ruby " RUBY_VERSION RUBY_PATCHLEVEL_STR " " \
- "(" RUBY_RELEASE_DATETIME RUBY_REVISION_STR ")" opt " " \
- "[" RUBY_PLATFORM "]"
-
#define PRINT(type) puts(ruby_##type)
#define MKSTR(type) rb_obj_freeze(rb_usascii_str_new_static(ruby_##type, sizeof(ruby_##type)-1))
#define MKINT(name) INT2FIX(ruby_##name)
@@ -70,9 +65,13 @@ const char ruby_revision[] = RUBY_FULL_REVISION;
const char ruby_release_date[] = RUBY_RELEASE_DATE;
const char ruby_platform[] = RUBY_PLATFORM;
const int ruby_patchlevel = RUBY_PATCHLEVEL;
-const char ruby_description[] = RUBY_DESCRIPTION_WITH("");
-static const char ruby_description_with_rjit[] = RUBY_DESCRIPTION_WITH(" +RJIT");
-static const char ruby_description_with_yjit[] = RUBY_DESCRIPTION_WITH(YJIT_DESCRIPTION);
+const char ruby_description[] =
+ "ruby " RUBY_VERSION RUBY_PATCHLEVEL_STR " "
+ "(" RUBY_RELEASE_DATETIME RUBY_REVISION_STR ") "
+ "[" RUBY_PLATFORM "]";
+static const int ruby_description_opt_point =
+ (int)(sizeof(ruby_description) - sizeof(" [" RUBY_PLATFORM "]"));
+
const char ruby_copyright[] = "ruby - Copyright (C) "
RUBY_BIRTH_YEAR_STR "-" RUBY_RELEASE_YEAR_STR " "
RUBY_AUTHOR;
@@ -86,12 +85,14 @@ void
Init_version(void)
{
enum {ruby_patchlevel = RUBY_PATCHLEVEL};
- VALUE version;
- VALUE ruby_engine_name;
+ VALUE version = MKSTR(version);
+ VALUE ruby_engine_name = MKSTR(engine);
+ // MKSTR macro is a marker for fake.rb
+
/*
* The running version of ruby
*/
- rb_define_global_const("RUBY_VERSION", (version = MKSTR(version)));
+ rb_define_global_const("RUBY_VERSION", /* MKSTR(version) */ version);
/*
* The date this ruby was released
*/
@@ -116,12 +117,12 @@ Init_version(void)
/*
* The engine or interpreter this ruby uses.
*/
- rb_define_global_const("RUBY_ENGINE", ruby_engine_name = MKSTR(engine));
+ rb_define_global_const("RUBY_ENGINE", /* MKSTR(engine) */ ruby_engine_name);
ruby_set_script_name(ruby_engine_name);
/*
* The version of the engine or interpreter this ruby uses.
*/
- rb_define_global_const("RUBY_ENGINE_VERSION", (1 ? version : MKSTR(version)));
+ rb_define_global_const("RUBY_ENGINE_VERSION", /* MKSTR(version) */ version);
rb_provide("ruby2_keywords.rb");
}
@@ -138,22 +139,36 @@ Init_version(void)
#define YJIT_OPTS_ON 0
#endif
-void
-Init_ruby_description(ruby_cmdline_options_t *opt)
+int ruby_mn_threads_enabled;
+
+bool * rb_ruby_prism_ptr(void);
+
+static void
+define_ruby_description(const char *const jit_opt)
{
- VALUE description;
-
- if (RJIT_OPTS_ON) {
- rb_dynamic_description = ruby_description_with_rjit;
- description = MKSTR(description_with_rjit);
- }
- else if (YJIT_OPTS_ON) {
- rb_dynamic_description = ruby_description_with_yjit;
- description = MKSTR(description_with_yjit);
- }
- else {
- description = MKSTR(description);
- }
+ static char desc[
+ sizeof(ruby_description)
+ + rb_strlen_lit(YJIT_DESCRIPTION)
+ + rb_strlen_lit(" +MN")
+ ];
+
+ const char *const threads_opt = ruby_mn_threads_enabled ? " +MN" : "";
+ const char *const parser_opt = (*rb_ruby_prism_ptr()) ? " +PRISM" : "";
+
+ int n = snprintf(desc, sizeof(desc),
+ "%.*s"
+ "%s" // jit_opt
+ "%s" // threads_opts
+ "%s" // parser_opt
+ "%s",
+ ruby_description_opt_point, ruby_description,
+ jit_opt,
+ threads_opt,
+ parser_opt,
+ ruby_description + ruby_description_opt_point);
+
+ VALUE description = rb_obj_freeze(rb_usascii_str_new_static(desc, n));
+ rb_dynamic_description = desc;
/*
* The full ruby version string, like <tt>ruby -v</tt> prints
@@ -162,6 +177,23 @@ Init_ruby_description(ruby_cmdline_options_t *opt)
}
void
+Init_ruby_description(ruby_cmdline_options_t *opt)
+{
+ const char *const jit_opt =
+ RJIT_OPTS_ON ? " +RJIT" :
+ YJIT_OPTS_ON ? YJIT_DESCRIPTION :
+ "";
+ define_ruby_description(jit_opt);
+}
+
+void
+ruby_set_yjit_description(void)
+{
+ rb_const_remove(rb_cObject, rb_intern("RUBY_DESCRIPTION"));
+ define_ruby_description(YJIT_DESCRIPTION);
+}
+
+void
ruby_show_version(void)
{
puts(rb_dynamic_description);
diff --git a/version.h b/version.h
index 46f14e3f14..eaba1c1985 100644
--- a/version.h
+++ b/version.h
@@ -55,11 +55,15 @@
#endif
#if RUBY_PATCHLEVEL == -1
-#define RUBY_PATCHLEVEL_STR "dev"
+# ifdef RUBY_PATCHLEVEL_NAME
+# define RUBY_PATCHLEVEL_STR STRINGIZE(RUBY_PATCHLEVEL_NAME)
+# else
+# define RUBY_PATCHLEVEL_STR "dev"
+# endif
#elif defined RUBY_ABI_VERSION
-#error RUBY_ABI_VERSION is defined in non-development branch
+# error RUBY_ABI_VERSION is defined in non-development branch
#else
-#define RUBY_PATCHLEVEL_STR ""
+# define RUBY_PATCHLEVEL_STR ""
#endif
#endif /* RUBY_TOPLEVEL_VERSION_H */
diff --git a/vm.c b/vm.c
index b6b8ca80f5..dbee303851 100644
--- a/vm.c
+++ b/vm.c
@@ -16,15 +16,18 @@
#include "internal/compile.h"
#include "internal/cont.h"
#include "internal/error.h"
+#include "internal/encoding.h"
#include "internal/eval.h"
#include "internal/gc.h"
#include "internal/inits.h"
+#include "internal/missing.h"
#include "internal/object.h"
-#include "internal/parse.h"
#include "internal/proc.h"
#include "internal/re.h"
+#include "internal/ruby_parser.h"
#include "internal/symbol.h"
#include "internal/thread.h"
+#include "internal/transcode.h"
#include "internal/vm.h"
#include "internal/sanitizers.h"
#include "internal/variable.h"
@@ -51,6 +54,8 @@
int ruby_assert_critical_section_entered = 0;
#endif
+static void *native_main_thread_stack_top;
+
VALUE rb_str_concat_literals(size_t, const VALUE*);
VALUE vm_exec(rb_execution_context_t *);
@@ -198,7 +203,7 @@ VM_CAPTURED_BLOCK_TO_CFP(const struct rb_captured_block *captured)
{
rb_control_frame_t *cfp = ((rb_control_frame_t *)((VALUE *)(captured) - 3));
VM_ASSERT(!VM_CFP_IN_HEAP_P(GET_EC(), cfp));
- VM_ASSERT(sizeof(rb_control_frame_t)/sizeof(VALUE) == 8 + VM_DEBUG_BP_CHECK ? 1 : 0);
+ VM_ASSERT(sizeof(rb_control_frame_t)/sizeof(VALUE) == 7 + VM_DEBUG_BP_CHECK ? 1 : 0);
return cfp;
}
@@ -223,7 +228,6 @@ vm_cref_new0(VALUE klass, rb_method_visibility_t visi, int module_func, rb_cref_
{
VALUE refinements = Qnil;
int omod_shared = FALSE;
- rb_cref_t *cref;
/* scope */
union {
@@ -246,7 +250,10 @@ vm_cref_new0(VALUE klass, rb_method_visibility_t visi, int module_func, rb_cref_
VM_ASSERT(singleton || klass);
- cref = (rb_cref_t *)rb_imemo_new(imemo_cref, klass, (VALUE)(use_prev_prev ? CREF_NEXT(prev_cref) : prev_cref), scope_visi.value, refinements);
+ rb_cref_t *cref = IMEMO_NEW(rb_cref_t, imemo_cref, refinements);
+ cref->klass_or_self = klass;
+ cref->next = use_prev_prev ? CREF_NEXT(prev_cref) : prev_cref;
+ *((rb_scope_visibility_t *)&cref->scope_visi) = scope_visi.visi;
if (pushed_by_eval) CREF_PUSHED_BY_EVAL_SET(cref);
if (omod_shared) CREF_OMOD_SHARED_SET(cref);
@@ -369,40 +376,84 @@ extern VALUE rb_vm_invoke_bmethod(rb_execution_context_t *ec, rb_proc_t *proc, V
const rb_callable_method_entry_t *me);
static VALUE vm_invoke_proc(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self, int argc, const VALUE *argv, int kw_splat, VALUE block_handler);
+#if USE_YJIT
+// Counter to serve as a proxy for execution time, total number of calls
+static uint64_t yjit_total_entry_hits = 0;
+
+// Number of calls used to estimate how hot an ISEQ is
+#define YJIT_CALL_COUNT_INTERV 20u
+
+/// Test whether we are ready to compile an ISEQ or not
+static inline bool
+rb_yjit_threshold_hit(const rb_iseq_t *iseq, uint64_t entry_calls)
+{
+ yjit_total_entry_hits += 1;
+
+ // Record the number of calls at the beginning of the interval
+ if (entry_calls + YJIT_CALL_COUNT_INTERV == rb_yjit_call_threshold) {
+ iseq->body->yjit_calls_at_interv = yjit_total_entry_hits;
+ }
+
+ // Try to estimate the total time taken (total number of calls) to reach 20 calls to this ISEQ
+ // This give us a ratio of how hot/cold this ISEQ is
+ if (entry_calls == rb_yjit_call_threshold) {
+ // We expect threshold 1 to compile everything immediately
+ if (rb_yjit_call_threshold < YJIT_CALL_COUNT_INTERV) {
+ return true;
+ }
+
+ uint64_t num_calls = yjit_total_entry_hits - iseq->body->yjit_calls_at_interv;
+
+ // Reject ISEQs that don't get called often enough
+ if (num_calls > rb_yjit_cold_threshold) {
+ rb_yjit_incr_counter("cold_iseq_entry");
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+#else
+#define rb_yjit_threshold_hit(iseq, entry_calls) false
+#endif
+
#if USE_RJIT || USE_YJIT
-// Try to compile the current ISeq in ec. Return 0 if not compiled.
+// Generate JIT code that supports the following kinds of ISEQ entries:
+// * The first ISEQ on vm_exec (e.g. <main>, or Ruby methods/blocks
+// called by a C method). The current frame has VM_FRAME_FLAG_FINISH.
+// The current vm_exec stops if JIT code returns a non-Qundef value.
+// * ISEQs called by the interpreter on vm_sendish (e.g. Ruby methods or
+// blocks called by a Ruby frame that isn't compiled or side-exited).
+// The current frame doesn't have VM_FRAME_FLAG_FINISH. The current
+// vm_exec does NOT stop whether JIT code returns Qundef or not.
static inline rb_jit_func_t
jit_compile(rb_execution_context_t *ec)
{
- // Increment the ISEQ's call counter
const rb_iseq_t *iseq = ec->cfp->iseq;
struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
- bool yjit_enabled = rb_yjit_compile_new_iseqs();
- if (yjit_enabled || rb_rjit_call_p) {
- body->total_calls++;
- }
- else {
- return 0;
+ bool yjit_enabled = rb_yjit_enabled_p;
+ if (!(yjit_enabled || rb_rjit_call_p)) {
+ return NULL;
}
- // Trigger JIT compilation as needed
- if (yjit_enabled) {
- if (body->total_calls == rb_yjit_call_threshold()) {
- rb_yjit_compile_iseq(iseq, ec);
+ // Increment the ISEQ's call counter and trigger JIT compilation if not compiled
+ if (body->jit_entry == NULL) {
+ body->jit_entry_calls++;
+ if (yjit_enabled) {
+ if (rb_yjit_threshold_hit(iseq, body->jit_entry_calls)) {
+ rb_yjit_compile_iseq(iseq, ec, false);
+ }
}
- }
- else { // rb_rjit_call_p
- if (body->total_calls == rb_rjit_call_threshold()) {
+ else if (body->jit_entry_calls == rb_rjit_call_threshold()) {
rb_rjit_compile(iseq);
}
}
-
- return body->jit_func;
+ return body->jit_entry;
}
-// Try to execute the current iseq in ec. Use JIT code if it is ready.
-// If it is not, add ISEQ to the compilation queue and return Qundef for RJIT.
-// YJIT compiles on the thread running the iseq.
+// Execute JIT code compiled by jit_compile()
static inline VALUE
jit_exec(rb_execution_context_t *ec)
{
@@ -416,10 +467,57 @@ jit_exec(rb_execution_context_t *ec)
}
}
#else
-static inline rb_jit_func_t jit_compile(rb_execution_context_t *ec) { return 0; }
-static inline VALUE jit_exec(rb_execution_context_t *ec) { return Qundef; }
+# define jit_compile(ec) ((rb_jit_func_t)0)
+# define jit_exec(ec) Qundef
+#endif
+
+#if USE_YJIT
+// Generate JIT code that supports the following kind of ISEQ entry:
+// * The first ISEQ pushed by vm_exec_handle_exception. The frame would
+// point to a location specified by a catch table, and it doesn't have
+// VM_FRAME_FLAG_FINISH. The current vm_exec stops if JIT code returns
+// a non-Qundef value. So you should not return a non-Qundef value
+// until ec->cfp is changed to a frame with VM_FRAME_FLAG_FINISH.
+static inline rb_jit_func_t
+jit_compile_exception(rb_execution_context_t *ec)
+{
+ const rb_iseq_t *iseq = ec->cfp->iseq;
+ struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
+ if (!rb_yjit_enabled_p) {
+ return NULL;
+ }
+
+ // Increment the ISEQ's call counter and trigger JIT compilation if not compiled
+ if (body->jit_exception == NULL) {
+ body->jit_exception_calls++;
+ if (body->jit_exception_calls == rb_yjit_call_threshold) {
+ rb_yjit_compile_iseq(iseq, ec, true);
+ }
+ }
+
+ return body->jit_exception;
+}
+
+// Execute JIT code compiled by jit_compile_exception()
+static inline VALUE
+jit_exec_exception(rb_execution_context_t *ec)
+{
+ rb_jit_func_t func = jit_compile_exception(ec);
+ if (func) {
+ // Call the JIT code
+ return func(ec, ec->cfp);
+ }
+ else {
+ return Qundef;
+ }
+}
+#else
+# define jit_compile_exception(ec) ((rb_jit_func_t)0)
+# define jit_exec_exception(ec) Qundef
#endif
+static void add_opt_method_entry(const rb_method_entry_t *me);
+
#include "vm_insnhelper.c"
#include "vm_exec.c"
@@ -441,23 +539,33 @@ bool ruby_vm_keep_script_lines;
#ifdef RB_THREAD_LOCAL_SPECIFIER
RB_THREAD_LOCAL_SPECIFIER rb_execution_context_t *ruby_current_ec;
+
#ifdef RUBY_NT_SERIAL
RB_THREAD_LOCAL_SPECIFIER rb_atomic_t ruby_nt_serial;
#endif
+// no-inline decl on thread_pthread.h
+rb_execution_context_t *
+rb_current_ec_noinline(void)
+{
+ return ruby_current_ec;
+}
+
+void
+rb_current_ec_set(rb_execution_context_t *ec)
+{
+ ruby_current_ec = ec;
+}
+
+
#ifdef __APPLE__
- rb_execution_context_t *
- rb_current_ec(void)
- {
- return ruby_current_ec;
- }
- void
- rb_current_ec_set(rb_execution_context_t *ec)
- {
- ruby_current_ec = ec;
- }
-#endif
+rb_execution_context_t *
+rb_current_ec(void)
+{
+ return ruby_current_ec;
+}
+#endif
#else
native_tls_key_t ruby_current_ec_key;
#endif
@@ -511,7 +619,7 @@ rb_dtrace_setup(rb_execution_context_t *ec, VALUE klass, ID id,
if (RB_TYPE_P(klass, T_ICLASS)) {
klass = RBASIC(klass)->klass;
}
- else if (FL_TEST(klass, FL_SINGLETON)) {
+ else if (RCLASS_SINGLETON_P(klass)) {
klass = RCLASS_ATTACHED_OBJECT(klass);
if (NIL_P(klass)) return FALSE;
}
@@ -534,6 +642,8 @@ rb_dtrace_setup(rb_execution_context_t *ec, VALUE klass, ID id,
return FALSE;
}
+extern unsigned int redblack_buffer_size;
+
/*
* call-seq:
* RubyVM.stat -> Hash
@@ -561,6 +671,7 @@ static VALUE
vm_stat(int argc, VALUE *argv, VALUE self)
{
static VALUE sym_constant_cache_invalidations, sym_constant_cache_misses, sym_global_cvar_state, sym_next_shape_id;
+ static VALUE sym_shape_cache_size;
VALUE arg = Qnil;
VALUE hash = Qnil, key = Qnil;
@@ -582,6 +693,7 @@ vm_stat(int argc, VALUE *argv, VALUE self)
S(constant_cache_misses);
S(global_cvar_state);
S(next_shape_id);
+ S(shape_cache_size);
#undef S
#define SET(name, attr) \
@@ -594,6 +706,7 @@ vm_stat(int argc, VALUE *argv, VALUE self)
SET(constant_cache_misses, ruby_vm_constant_cache_misses);
SET(global_cvar_state, ruby_vm_global_cvar_state);
SET(next_shape_id, (rb_serial_t)GET_SHAPE_TREE()->next_shape_id);
+ SET(shape_cache_size, (rb_serial_t)GET_SHAPE_TREE()->cache_size);
#undef SET
#if USE_DEBUG_COUNTER
@@ -800,7 +913,7 @@ vm_block_handler_escape(const rb_execution_context_t *ec, VALUE block_handler)
switch (vm_block_handler_type(block_handler)) {
case block_handler_type_ifunc:
case block_handler_type_iseq:
- return rb_vm_make_proc(ec, VM_BH_TO_CAPT_BLOCK(block_handler), rb_cProc);
+ return rb_vm_make_proc(ec, VM_BH_TO_CAPT_BLOCK(block_handler), rb_cProc);
case block_handler_type_symbol:
case block_handler_type_proc:
@@ -814,8 +927,6 @@ static VALUE
vm_make_env_each(const rb_execution_context_t * const ec, rb_control_frame_t *const cfp)
{
const VALUE * const ep = cfp->ep;
- const rb_env_t *env;
- const rb_iseq_t *env_iseq;
VALUE *env_body, *env_ep;
int local_size, env_size;
@@ -867,9 +978,26 @@ vm_make_env_each(const rb_execution_context_t * const ec, rb_control_frame_t *co
env_size = local_size +
1 /* envval */;
+
+ // Careful with order in the following sequence. Each allocation can move objects.
env_body = ALLOC_N(VALUE, env_size);
+ rb_env_t *env = IMEMO_NEW(rb_env_t, imemo_env, 0);
+
+ // Set up env without WB since it's brand new (similar to newobj_init(), newobj_fill())
MEMCPY(env_body, ep - (local_size - 1 /* specval */), VALUE, local_size);
+ env_ep = &env_body[local_size - 1 /* specval */];
+ env_ep[VM_ENV_DATA_INDEX_ENV] = (VALUE)env;
+
+ env->iseq = (rb_iseq_t *)(VM_FRAME_RUBYFRAME_P(cfp) ? cfp->iseq : NULL);
+ env->ep = env_ep;
+ env->env = env_body;
+ env->env_size = env_size;
+
+ cfp->ep = env_ep;
+ VM_ENV_FLAGS_SET(env_ep, VM_ENV_FLAG_ESCAPED | VM_ENV_FLAG_WB_REQUIRED);
+ VM_STACK_ENV_WRITE(ep, 0, (VALUE)env); /* GC mark */
+
#if 0
for (i = 0; i < local_size; i++) {
if (VM_FRAME_RUBYFRAME_P(cfp)) {
@@ -879,14 +1007,11 @@ vm_make_env_each(const rb_execution_context_t * const ec, rb_control_frame_t *co
}
#endif
- env_iseq = VM_FRAME_RUBYFRAME_P(cfp) ? cfp->iseq : NULL;
- env_ep = &env_body[local_size - 1 /* specval */];
-
- env = vm_env_new(env_ep, env_body, env_size, env_iseq);
+ // Invalidate JIT code that assumes cfp->ep == vm_base_ptr(cfp).
+ if (env->iseq) {
+ rb_yjit_invalidate_ep_is_bp(env->iseq);
+ }
- cfp->ep = env_ep;
- VM_ENV_FLAGS_SET(env_ep, VM_ENV_FLAG_ESCAPED | VM_ENV_FLAG_WB_REQUIRED);
- VM_STACK_ENV_WRITE(ep, 0, (VALUE)env); /* GC mark */
return (VALUE)env;
}
@@ -1107,26 +1232,33 @@ env_copy(const VALUE *src_ep, VALUE read_only_variables)
VALUE *env_body = ZALLOC_N(VALUE, src_env->env_size); // fill with Qfalse
VALUE *ep = &env_body[src_env->env_size - 2];
- volatile VALUE prev_env = Qnil;
+ const rb_env_t *copied_env = vm_env_new(ep, env_body, src_env->env_size, src_env->iseq);
+
+ // Copy after allocations above, since they can move objects in src_ep.
+ RB_OBJ_WRITE(copied_env, &ep[VM_ENV_DATA_INDEX_ME_CREF], src_ep[VM_ENV_DATA_INDEX_ME_CREF]);
+ ep[VM_ENV_DATA_INDEX_FLAGS] = src_ep[VM_ENV_DATA_INDEX_FLAGS] | VM_ENV_FLAG_ISOLATED;
+ if (!VM_ENV_LOCAL_P(src_ep)) {
+ VM_ENV_FLAGS_SET(ep, VM_ENV_FLAG_LOCAL);
+ }
if (read_only_variables) {
for (int i=RARRAY_LENINT(read_only_variables)-1; i>=0; i--) {
ID id = NUM2ID(RARRAY_AREF(read_only_variables, i));
for (unsigned int j=0; j<ISEQ_BODY(src_env->iseq)->local_table_size; j++) {
- if (id == ISEQ_BODY(src_env->iseq)->local_table[j]) {
+ if (id == ISEQ_BODY(src_env->iseq)->local_table[j]) {
VALUE v = src_env->env[j];
if (!rb_ractor_shareable_p(v)) {
VALUE name = rb_id2str(id);
VALUE msg = rb_sprintf("can not make shareable Proc because it can refer"
" unshareable object %+" PRIsVALUE " from ", v);
if (name)
- rb_str_catf(msg, "variable `%" PRIsVALUE "'", name);
+ rb_str_catf(msg, "variable '%" PRIsVALUE "'", name);
else
rb_str_cat_cstr(msg, "a hidden variable");
rb_exc_raise(rb_exc_new_str(rb_eRactorIsolationError, msg));
}
- env_body[j] = v;
+ RB_OBJ_WRITE((VALUE)copied_env, &env_body[j], v);
rb_ary_delete_at(read_only_variables, i);
break;
}
@@ -1134,21 +1266,17 @@ env_copy(const VALUE *src_ep, VALUE read_only_variables)
}
}
- ep[VM_ENV_DATA_INDEX_ME_CREF] = src_ep[VM_ENV_DATA_INDEX_ME_CREF];
- ep[VM_ENV_DATA_INDEX_FLAGS] = src_ep[VM_ENV_DATA_INDEX_FLAGS] | VM_ENV_FLAG_ISOLATED;
-
if (!VM_ENV_LOCAL_P(src_ep)) {
const VALUE *prev_ep = VM_ENV_PREV_EP(src_env->ep);
const rb_env_t *new_prev_env = env_copy(prev_ep, read_only_variables);
- prev_env = (VALUE)new_prev_env;
ep[VM_ENV_DATA_INDEX_SPECVAL] = VM_GUARDED_PREV_EP(new_prev_env->ep);
+ RB_OBJ_WRITTEN(copied_env, Qundef, new_prev_env);
+ VM_ENV_FLAGS_UNSET(ep, VM_ENV_FLAG_LOCAL);
}
else {
ep[VM_ENV_DATA_INDEX_SPECVAL] = VM_BLOCK_HANDLER_NONE;
}
- const rb_env_t *copied_env = vm_env_new(ep, env_body, src_env->env_size, src_env->iseq);
- RB_GC_GUARD(prev_env);
return copied_env;
}
@@ -1184,11 +1312,11 @@ proc_shared_outer_variables(struct rb_id_table *outer_variables, bool isolate, c
rb_str_append(str, name);
}
if (*sep == ',') rb_str_cat_cstr(str, ")");
- rb_str_cat_cstr(str, data.yield ? " and uses `yield'." : ".");
+ rb_str_cat_cstr(str, data.yield ? " and uses 'yield'." : ".");
rb_exc_raise(rb_exc_new_str(rb_eArgError, str));
}
else if (data.yield) {
- rb_raise(rb_eArgError, "can not %s because it uses `yield'.", message);
+ rb_raise(rb_eArgError, "can not %s because it uses 'yield'.", message);
}
return data.read_only;
@@ -1271,7 +1399,7 @@ rb_vm_make_proc_lambda(const rb_execution_context_t *ec, const struct rb_capture
code_type == imemo_iseq ? block_type_iseq : block_type_ifunc,
FALSE, is_lambda);
- if (code_type == imemo_ifunc) {
+ if (code_type == imemo_ifunc) {
struct vm_ifunc *ifunc = (struct vm_ifunc *)captured->code.val;
if (ifunc->svar_lep) {
VALUE ep0 = ifunc->svar_lep[0];
@@ -1335,8 +1463,7 @@ rb_binding_add_dynavars(VALUE bindval, rb_binding_t *bind, int dyncount, const I
const rb_env_t *env;
rb_execution_context_t *ec = GET_EC();
const rb_iseq_t *base_iseq, *iseq;
- rb_ast_body_t ast;
- NODE tmp_node;
+ rb_node_scope_t tmp_node;
if (dyncount < 0) return 0;
@@ -1348,17 +1475,19 @@ rb_binding_add_dynavars(VALUE bindval, rb_binding_t *bind, int dyncount, const I
dyns->size = dyncount;
MEMCPY(dyns->ids, dynvars, ID, dyncount);
- rb_node_init(&tmp_node, NODE_SCOPE, (VALUE)dyns, 0, 0);
- ast.root = &tmp_node;
- ast.compile_option = 0;
- ast.script_lines = INT2FIX(-1);
+ rb_node_init(RNODE(&tmp_node), NODE_SCOPE);
+ tmp_node.nd_tbl = dyns;
+ tmp_node.nd_body = 0;
+ tmp_node.nd_args = 0;
+
+ VALUE vast = rb_ruby_ast_new(RNODE(&tmp_node));
if (base_iseq) {
- iseq = rb_iseq_new(&ast, ISEQ_BODY(base_iseq)->location.label, path, realpath, base_iseq, ISEQ_TYPE_EVAL);
+ iseq = rb_iseq_new(vast, ISEQ_BODY(base_iseq)->location.label, path, realpath, base_iseq, ISEQ_TYPE_EVAL);
}
else {
VALUE tempstr = rb_fstring_lit("<temp>");
- iseq = rb_iseq_new_top(&ast, tempstr, tempstr, tempstr, NULL);
+ iseq = rb_iseq_new_top(vast, tempstr, tempstr, tempstr, NULL);
}
tmp_node.nd_tbl = 0; /* reset table */
ALLOCV_END(idtmp);
@@ -1391,7 +1520,7 @@ invoke_block(rb_execution_context_t *ec, const rb_iseq_t *iseq, VALUE self, cons
static VALUE
invoke_bmethod(rb_execution_context_t *ec, const rb_iseq_t *iseq, VALUE self, const struct rb_captured_block *captured, const rb_callable_method_entry_t *me, VALUE type, int opt_pc)
{
- /* bmethod */
+ /* bmethod call from outside the VM */
int arg_size = ISEQ_BODY(iseq)->param.size;
VALUE ret;
@@ -1401,7 +1530,7 @@ invoke_bmethod(rb_execution_context_t *ec, const rb_iseq_t *iseq, VALUE self, co
VM_GUARDED_PREV_EP(captured->ep),
(VALUE)me,
ISEQ_BODY(iseq)->iseq_encoded + opt_pc,
- ec->cfp->sp + arg_size,
+ ec->cfp->sp + 1 /* self */ + arg_size,
ISEQ_BODY(iseq)->local_table_size - arg_size,
ISEQ_BODY(iseq)->stack_max);
@@ -1422,7 +1551,7 @@ invoke_iseq_block_from_c(rb_execution_context_t *ec, const struct rb_captured_bl
const rb_cref_t *cref, int is_lambda, const rb_callable_method_entry_t *me)
{
const rb_iseq_t *iseq = rb_iseq_check(captured->code.iseq);
- int i, opt_pc;
+ int opt_pc;
VALUE type = VM_FRAME_MAGIC_BLOCK | (is_lambda ? VM_FRAME_FLAG_LAMBDA : 0);
rb_control_frame_t *cfp = ec->cfp;
VALUE *sp = cfp->sp;
@@ -1432,23 +1561,25 @@ invoke_iseq_block_from_c(rb_execution_context_t *ec, const struct rb_captured_bl
stack_check(ec);
-#if VM_ARGC_STACK_MAX < 1
- /* Skip ruby array for potential autosplat case */
- if (UNLIKELY(argc > VM_ARGC_STACK_MAX && (argc != 1 || is_lambda))) {
-#else
- if (UNLIKELY(argc > VM_ARGC_STACK_MAX)) {
-#endif
+ if (UNLIKELY(argc > VM_ARGC_STACK_MAX) &&
+ (VM_ARGC_STACK_MAX >= 1 ||
+ /* Skip ruby array for potential autosplat case */
+ (argc != 1 || is_lambda))) {
use_argv = vm_argv_ruby_array(av, argv, &flags, &argc, kw_splat);
}
- CHECK_VM_STACK_OVERFLOW(cfp, argc);
+ CHECK_VM_STACK_OVERFLOW(cfp, argc + 1);
vm_check_canary(ec, sp);
- cfp->sp = sp + argc;
- for (i=0; i<argc; i++) {
- sp[i] = use_argv[i];
+
+ VALUE *stack_argv = sp;
+ if (me) {
+ *sp = self; // bemthods need `self` on the VM stack
+ stack_argv++;
}
+ cfp->sp = stack_argv + argc;
+ MEMCPY(stack_argv, use_argv, VALUE, argc); // restrict: new stack space
- opt_pc = vm_yield_setup_args(ec, iseq, argc, sp, flags, passed_block_handler,
+ opt_pc = vm_yield_setup_args(ec, iseq, argc, stack_argv, flags, passed_block_handler,
(is_lambda ? arg_setup_method : arg_setup_block));
cfp->sp = sp;
@@ -1626,7 +1757,7 @@ VALUE *
rb_vm_svar_lep(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
{
while (cfp->pc == 0 || cfp->iseq == 0) {
- if (VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_IFUNC) {
+ if (VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_IFUNC) {
struct vm_ifunc *ifunc = (struct vm_ifunc *)cfp->iseq;
return ifunc->svar_lep;
}
@@ -1639,12 +1770,7 @@ rb_vm_svar_lep(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
}
}
- if (cfp) {
- return (VALUE *)VM_CF_LEP(cfp);
- }
- else {
- return NULL;
- }
+ return (VALUE *)VM_CF_LEP(cfp);
}
static VALUE
@@ -1695,6 +1821,17 @@ rb_lastline_set(VALUE val)
vm_svar_set(GET_EC(), VM_SVAR_LASTLINE, val);
}
+void
+rb_lastline_set_up(VALUE val, unsigned int up)
+{
+ rb_control_frame_t * cfp = GET_EC()->cfp;
+
+ for(unsigned int i = 0; i < up; i++) {
+ cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
+ }
+ vm_cfp_svar_set(GET_EC(), cfp, VM_SVAR_LASTLINE, val);
+}
+
/* misc */
const char *
@@ -1845,7 +1982,7 @@ rb_vm_localjump_error(const char *mesg, VALUE value, int reason)
}
VALUE
-rb_vm_make_jump_tag_but_local_jump(int state, VALUE val)
+rb_vm_make_jump_tag_but_local_jump(enum ruby_tag_type state, VALUE val)
{
const char *mesg;
@@ -1877,7 +2014,7 @@ rb_vm_make_jump_tag_but_local_jump(int state, VALUE val)
}
void
-rb_vm_jump_tag_but_local_jump(int state)
+rb_vm_jump_tag_but_local_jump(enum ruby_tag_type state)
{
VALUE exc = rb_vm_make_jump_tag_but_local_jump(state, Qundef);
if (!NIL_P(exc)) rb_exc_raise(exc);
@@ -1928,6 +2065,13 @@ short ruby_vm_redefined_flag[BOP_LAST_];
static st_table *vm_opt_method_def_table = 0;
static st_table *vm_opt_mid_table = 0;
+void
+rb_free_vm_opt_tables(void)
+{
+ st_free_table(vm_opt_method_def_table);
+ st_free_table(vm_opt_mid_table);
+}
+
static int
vm_redefinition_check_flag(VALUE klass)
{
@@ -1965,6 +2109,8 @@ vm_redefinition_check_method_type(const rb_method_entry_t *me)
return FALSE;
}
+ if (METHOD_ENTRY_BASIC(me)) return TRUE;
+
const rb_method_definition_t *def = me->def;
switch (def->type) {
case VM_METHOD_TYPE_CFUNC:
@@ -1987,6 +2133,12 @@ rb_vm_check_redefinition_opt_method(const rb_method_entry_t *me, VALUE klass)
if (st_lookup(vm_opt_method_def_table, (st_data_t)me->def, &bop)) {
int flag = vm_redefinition_check_flag(klass);
if (flag != 0) {
+ rb_category_warn(
+ RB_WARN_CATEGORY_PERFORMANCE,
+ "Redefining '%s#%s' disables interpreter and JIT optimizations",
+ rb_class2name(me->owner),
+ rb_id2name(me->called_id)
+ );
rb_yjit_bop_redefined(flag, (enum ruby_basic_operators)bop);
rb_rjit_bop_redefined(flag, (enum ruby_basic_operators)bop);
ruby_vm_redefined_flag[bop] |= flag;
@@ -2015,27 +2167,44 @@ rb_vm_check_redefinition_by_prepend(VALUE klass)
}
static void
-add_opt_method(VALUE klass, ID mid, VALUE bop)
+add_opt_method_entry_bop(const rb_method_entry_t *me, ID mid, enum ruby_basic_operators bop)
+{
+ st_insert(vm_opt_method_def_table, (st_data_t)me->def, (st_data_t)bop);
+ st_insert(vm_opt_mid_table, (st_data_t)mid, (st_data_t)Qtrue);
+}
+
+static void
+add_opt_method(VALUE klass, ID mid, enum ruby_basic_operators bop)
{
const rb_method_entry_t *me = rb_method_entry_at(klass, mid);
if (me && vm_redefinition_check_method_type(me)) {
- st_insert(vm_opt_method_def_table, (st_data_t)me->def, (st_data_t)bop);
- st_insert(vm_opt_mid_table, (st_data_t)mid, (st_data_t)Qtrue);
+ add_opt_method_entry_bop(me, mid, bop);
}
else {
rb_bug("undefined optimized method: %s", rb_id2name(mid));
}
}
+static enum ruby_basic_operators vm_redefinition_bop_for_id(ID mid);
+
+static void
+add_opt_method_entry(const rb_method_entry_t *me)
+{
+ if (me && vm_redefinition_check_method_type(me)) {
+ ID mid = me->called_id;
+ enum ruby_basic_operators bop = vm_redefinition_bop_for_id(mid);
+ if ((int)bop >= 0) {
+ add_opt_method_entry_bop(me, mid, bop);
+ }
+ }
+}
+
static void
vm_init_redefined_flag(void)
{
ID mid;
- VALUE bop;
-
- vm_opt_method_def_table = st_init_numtable();
- vm_opt_mid_table = st_init_numtable();
+ enum ruby_basic_operators bop;
#define OP(mid_, bop_) (mid = id##mid_, bop = BOP_##bop_, ruby_vm_redefined_flag[bop] = 0)
#define C(k) add_opt_method(rb_c##k, mid, bop)
@@ -2074,6 +2243,46 @@ vm_init_redefined_flag(void)
#undef OP
}
+static enum ruby_basic_operators
+vm_redefinition_bop_for_id(ID mid)
+{
+ switch (mid) {
+#define OP(mid_, bop_) case id##mid_: return BOP_##bop_
+ OP(PLUS, PLUS);
+ OP(MINUS, MINUS);
+ OP(MULT, MULT);
+ OP(DIV, DIV);
+ OP(MOD, MOD);
+ OP(Eq, EQ);
+ OP(Eqq, EQQ);
+ OP(LT, LT);
+ OP(LE, LE);
+ OP(GT, GT);
+ OP(GE, GE);
+ OP(LTLT, LTLT);
+ OP(AREF, AREF);
+ OP(ASET, ASET);
+ OP(Length, LENGTH);
+ OP(Size, SIZE);
+ OP(EmptyP, EMPTY_P);
+ OP(Succ, SUCC);
+ OP(EqTilde, MATCH);
+ OP(Freeze, FREEZE);
+ OP(UMinus, UMINUS);
+ OP(Max, MAX);
+ OP(Min, MIN);
+ OP(Hash, HASH);
+ OP(Call, CALL);
+ OP(And, AND);
+ OP(Or, OR);
+ OP(NilP, NIL_P);
+ OP(Cmp, CMP);
+ OP(Default, DEFAULT);
+#undef OP
+ }
+ return -1;
+}
+
/* for vm development */
#if VMDEBUG
@@ -2134,14 +2343,13 @@ frame_name(const rb_control_frame_t *cfp)
// cfp_returning_with_value:
// Whether cfp is the last frame in the unwinding process for a non-local return.
static void
-hook_before_rewind(rb_execution_context_t *ec, const rb_control_frame_t *cfp,
- bool cfp_returning_with_value, int state, struct vm_throw_data *err)
+hook_before_rewind(rb_execution_context_t *ec, bool cfp_returning_with_value, int state, struct vm_throw_data *err)
{
if (state == TAG_RAISE && RBASIC(err)->klass == rb_eSysStackError) {
return;
}
else {
- const rb_iseq_t *iseq = cfp->iseq;
+ const rb_iseq_t *iseq = ec->cfp->iseq;
rb_hook_list_t *local_hooks = iseq->aux.exec.local_hooks;
switch (VM_FRAME_TYPE(ec->cfp)) {
@@ -2288,124 +2496,107 @@ hook_before_rewind(rb_execution_context_t *ec, const rb_control_frame_t *cfp,
*/
static inline VALUE
-vm_exec_handle_exception(rb_execution_context_t *ec, enum ruby_tag_type state,
- VALUE errinfo, VALUE *initial);
+vm_exec_handle_exception(rb_execution_context_t *ec, enum ruby_tag_type state, VALUE errinfo);
+static inline VALUE
+vm_exec_loop(rb_execution_context_t *ec, enum ruby_tag_type state, struct rb_vm_tag *tag, VALUE result);
// for non-Emscripten Wasm build, use vm_exec with optimized setjmp for runtime performance
#if defined(__wasm__) && !defined(__EMSCRIPTEN__)
struct rb_vm_exec_context {
- rb_execution_context_t *ec;
- struct rb_vm_tag *tag;
- VALUE initial;
+ rb_execution_context_t *const ec;
+ struct rb_vm_tag *const tag;
+
VALUE result;
- enum ruby_tag_type state;
};
static void
-vm_exec_enter_vm_loop(rb_execution_context_t *ec, struct rb_vm_exec_context *ctx,
- struct rb_vm_tag *_tag, bool skip_first_ex_handle)
-{
- if (skip_first_ex_handle) {
- goto vm_loop_start;
- }
-
- ctx->result = ec->errinfo;
- rb_ec_raised_reset(ec, RAISED_STACKOVERFLOW | RAISED_NOMEMORY);
- while (UNDEF_P(ctx->result = vm_exec_handle_exception(ec, ctx->state, ctx->result, &ctx->initial))) {
- /* caught a jump, exec the handler */
- ctx->result = vm_exec_core(ec, ctx->initial);
- vm_loop_start:
- VM_ASSERT(ec->tag == _tag);
- /* when caught `throw`, `tag.state` is set. */
- if ((ctx->state = _tag->state) == TAG_NONE) break;
- _tag->state = TAG_NONE;
- }
-}
-
-static void
vm_exec_bottom_main(void *context)
{
- struct rb_vm_exec_context *ctx = (struct rb_vm_exec_context *)context;
+ struct rb_vm_exec_context *ctx = context;
+ rb_execution_context_t *ec = ctx->ec;
- ctx->state = TAG_NONE;
- if (UNDEF_P(ctx->result = jit_exec(ctx->ec))) {
- ctx->result = vm_exec_core(ctx->ec, ctx->initial);
- }
- vm_exec_enter_vm_loop(ctx->ec, ctx, ctx->tag, true);
+ ctx->result = vm_exec_loop(ec, TAG_NONE, ctx->tag, vm_exec_core(ec));
}
static void
vm_exec_bottom_rescue(void *context)
{
- struct rb_vm_exec_context *ctx = (struct rb_vm_exec_context *)context;
- ctx->state = rb_ec_tag_state(ctx->ec);
- vm_exec_enter_vm_loop(ctx->ec, ctx, ctx->tag, false);
+ struct rb_vm_exec_context *ctx = context;
+ rb_execution_context_t *ec = ctx->ec;
+
+ ctx->result = vm_exec_loop(ec, rb_ec_tag_state(ec), ctx->tag, ec->errinfo);
}
+#endif
VALUE
vm_exec(rb_execution_context_t *ec)
{
- struct rb_vm_exec_context ctx = {
- .ec = ec,
- .initial = 0, .result = Qundef,
- };
- struct rb_wasm_try_catch try_catch;
+ VALUE result = Qundef;
EC_PUSH_TAG(ec);
_tag.retval = Qnil;
- ctx.tag = &_tag;
+
+#if defined(__wasm__) && !defined(__EMSCRIPTEN__)
+ struct rb_vm_exec_context ctx = {
+ .ec = ec,
+ .tag = &_tag,
+ };
+ struct rb_wasm_try_catch try_catch;
EC_REPUSH_TAG();
rb_wasm_try_catch_init(&try_catch, vm_exec_bottom_main, vm_exec_bottom_rescue, &ctx);
- rb_wasm_try_catch_loop_run(&try_catch, &_tag.buf);
-
- EC_POP_TAG();
- return ctx.result;
-}
+ rb_wasm_try_catch_loop_run(&try_catch, &RB_VM_TAG_JMPBUF_GET(_tag.buf));
+ result = ctx.result;
#else
-
-VALUE
-vm_exec(rb_execution_context_t *ec)
-{
enum ruby_tag_type state;
- VALUE result = Qundef;
- VALUE initial = 0;
-
- EC_PUSH_TAG(ec);
-
- _tag.retval = Qnil;
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
if (UNDEF_P(result = jit_exec(ec))) {
- result = vm_exec_core(ec, initial);
+ result = vm_exec_core(ec);
}
- goto vm_loop_start; /* fallback to the VM */
+ /* fallback to the VM */
+ result = vm_exec_loop(ec, TAG_NONE, &_tag, result);
}
else {
- result = ec->errinfo;
- rb_ec_raised_reset(ec, RAISED_STACKOVERFLOW | RAISED_NOMEMORY);
- while (UNDEF_P(result = vm_exec_handle_exception(ec, state, result, &initial))) {
- /* caught a jump, exec the handler */
- result = vm_exec_core(ec, initial);
- vm_loop_start:
- VM_ASSERT(ec->tag == &_tag);
- /* when caught `throw`, `tag.state` is set. */
- if ((state = _tag.state) == TAG_NONE) break;
- _tag.state = TAG_NONE;
- }
+ result = vm_exec_loop(ec, state, &_tag, ec->errinfo);
}
+#endif
+
EC_POP_TAG();
return result;
}
-#endif
static inline VALUE
-vm_exec_handle_exception(rb_execution_context_t *ec, enum ruby_tag_type state,
- VALUE errinfo, VALUE *initial)
+vm_exec_loop(rb_execution_context_t *ec, enum ruby_tag_type state,
+ struct rb_vm_tag *tag, VALUE result)
+{
+ if (state == TAG_NONE) { /* no jumps, result is discarded */
+ goto vm_loop_start;
+ }
+
+ rb_ec_raised_reset(ec, RAISED_STACKOVERFLOW | RAISED_NOMEMORY);
+ while (UNDEF_P(result = vm_exec_handle_exception(ec, state, result))) {
+ // caught a jump, exec the handler. JIT code in jit_exec_exception()
+ // may return Qundef to run remaining frames with vm_exec_core().
+ if (UNDEF_P(result = jit_exec_exception(ec))) {
+ result = vm_exec_core(ec);
+ }
+ vm_loop_start:
+ VM_ASSERT(ec->tag == tag);
+ /* when caught `throw`, `tag.state` is set. */
+ if ((state = tag->state) == TAG_NONE) break;
+ tag->state = TAG_NONE;
+ }
+
+ return result;
+}
+
+static inline VALUE
+vm_exec_handle_exception(rb_execution_context_t *ec, enum ruby_tag_type state, VALUE errinfo)
{
struct vm_throw_data *err = (struct vm_throw_data *)errinfo;
@@ -2415,7 +2606,6 @@ vm_exec_handle_exception(rb_execution_context_t *ec, enum ruby_tag_type state,
const struct iseq_catch_table *ct;
unsigned long epc, cont_pc, cont_sp;
const rb_iseq_t *catch_iseq;
- rb_control_frame_t *cfp;
VALUE type;
const rb_control_frame_t *escape_cfp;
@@ -2435,7 +2625,7 @@ vm_exec_handle_exception(rb_execution_context_t *ec, enum ruby_tag_type state,
rb_vm_pop_frame(ec);
}
- cfp = ec->cfp;
+ rb_control_frame_t *const cfp = ec->cfp;
epc = cfp->pc - ISEQ_BODY(cfp->iseq)->iseq_encoded;
escape_cfp = NULL;
@@ -2465,7 +2655,7 @@ vm_exec_handle_exception(rb_execution_context_t *ec, enum ruby_tag_type state,
ec->errinfo = Qnil;
THROW_DATA_CATCH_FRAME_SET(err, cfp + 1);
// cfp == escape_cfp here so calling with cfp_returning_with_value = true
- hook_before_rewind(ec, ec->cfp, true, state, err);
+ hook_before_rewind(ec, true, state, err);
rb_vm_pop_frame(ec);
return THROW_DATA_VAL(err);
}
@@ -2474,11 +2664,7 @@ vm_exec_handle_exception(rb_execution_context_t *ec, enum ruby_tag_type state,
}
else {
/* TAG_BREAK */
-#if OPT_STACK_CACHING
- *initial = THROW_DATA_VAL(err);
-#else
- *ec->cfp->sp++ = THROW_DATA_VAL(err);
-#endif
+ *cfp->sp++ = THROW_DATA_VAL(err);
ec->errinfo = Qnil;
return Qundef;
}
@@ -2551,11 +2737,7 @@ vm_exec_handle_exception(rb_execution_context_t *ec, enum ruby_tag_type state,
cfp->sp = vm_base_ptr(cfp) + entry->sp;
if (state != TAG_REDO) {
-#if OPT_STACK_CACHING
- *initial = THROW_DATA_VAL(err);
-#else
- *ec->cfp->sp++ = THROW_DATA_VAL(err);
-#endif
+ *cfp->sp++ = THROW_DATA_VAL(err);
}
ec->errinfo = Qnil;
VM_ASSERT(ec->tag->state == TAG_NONE);
@@ -2606,7 +2788,7 @@ vm_exec_handle_exception(rb_execution_context_t *ec, enum ruby_tag_type state,
return Qundef;
}
else {
- hook_before_rewind(ec, ec->cfp, (cfp == escape_cfp), state, err);
+ hook_before_rewind(ec, (cfp == escape_cfp), state, err);
if (VM_FRAME_FINISHED_P(ec->cfp)) {
rb_vm_pop_frame(ec);
@@ -2678,7 +2860,7 @@ rb_vm_call_cfunc(VALUE recv, VALUE (*func)(VALUE), VALUE arg,
{
rb_execution_context_t *ec = GET_EC();
const rb_control_frame_t *reg_cfp = ec->cfp;
- const rb_iseq_t *iseq = rb_iseq_new(0, filename, filename, Qnil, 0, ISEQ_TYPE_TOP);
+ const rb_iseq_t *iseq = rb_iseq_new(Qnil, filename, filename, Qnil, 0, ISEQ_TYPE_TOP);
VALUE val;
vm_push_frame(ec, iseq, VM_FRAME_MAGIC_TOP | VM_ENV_FLAG_LOCAL | VM_FRAME_FLAG_FINISH,
@@ -2700,6 +2882,7 @@ rb_vm_update_references(void *ptr)
if (ptr) {
rb_vm_t *vm = ptr;
+ rb_gc_update_tbl_refs(vm->ci_table);
rb_gc_update_tbl_refs(vm->frozen_strings);
vm->mark_object_ary = rb_gc_location(vm->mark_object_ary);
vm->load_path = rb_gc_location(vm->load_path);
@@ -2719,6 +2902,8 @@ rb_vm_update_references(void *ptr)
rb_gc_update_tbl_refs(vm->overloaded_cme_table);
+ rb_gc_update_values(RUBY_NSIG, vm->trap_list.cmd);
+
if (vm->coverages) {
vm->coverages = rb_gc_location(vm->coverages);
vm->me2counter = rb_gc_location(vm->me2counter);
@@ -2744,7 +2929,7 @@ rb_vm_each_stack_value(void *ptr, void (*cb)(VALUE, void*), void *ctx)
VALUE *p = ec->vm_stack;
VALUE *sp = ec->cfp->sp;
while (p < sp) {
- if (!rb_special_const_p(*p)) {
+ if (!RB_SPECIAL_CONST_P(*p)) {
cb(*p, ctx);
}
p++;
@@ -2763,6 +2948,8 @@ vm_mark_negative_cme(VALUE val, void *dmy)
return ID_TABLE_CONTINUE;
}
+void rb_thread_sched_mark_zombies(rb_vm_t *vm);
+
void
rb_vm_mark(void *ptr)
{
@@ -2771,8 +2958,7 @@ rb_vm_mark(void *ptr)
if (ptr) {
rb_vm_t *vm = ptr;
rb_ractor_t *r = 0;
- long i, len;
- const VALUE *obj_ary;
+ long i;
ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
// ractor.set only contains blocking or running ractors
@@ -2781,26 +2967,14 @@ rb_vm_mark(void *ptr)
rb_gc_mark(rb_ractor_self(r));
}
- rb_gc_mark_movable(vm->mark_object_ary);
-
- len = RARRAY_LEN(vm->mark_object_ary);
- obj_ary = RARRAY_CONST_PTR(vm->mark_object_ary);
- for (i=0; i < len; i++) {
- const VALUE *ptr;
- long j, jlen;
-
- rb_gc_mark(*obj_ary);
- jlen = RARRAY_LEN(*obj_ary);
- ptr = RARRAY_CONST_PTR(*obj_ary);
- for (j=0; j < jlen; j++) {
- rb_gc_mark(*ptr++);
- }
- obj_ary++;
+ for (struct global_object_list *list = vm->global_object_list; list; list = list->next) {
+ rb_gc_mark_maybe(*list->varptr);
}
+ rb_gc_mark_movable(vm->mark_object_ary);
rb_gc_mark_movable(vm->load_path);
rb_gc_mark_movable(vm->load_path_snapshot);
- RUBY_MARK_MOVABLE_UNLESS_NULL(vm->load_path_check_cache);
+ rb_gc_mark_movable(vm->load_path_check_cache);
rb_gc_mark_movable(vm->expanded_load_path);
rb_gc_mark_movable(vm->loaded_features);
rb_gc_mark_movable(vm->loaded_features_snapshot);
@@ -2808,10 +2982,8 @@ rb_vm_mark(void *ptr)
rb_gc_mark_movable(vm->loaded_features_realpath_map);
rb_gc_mark_movable(vm->top_self);
rb_gc_mark_movable(vm->orig_progname);
- RUBY_MARK_MOVABLE_UNLESS_NULL(vm->coverages);
- RUBY_MARK_MOVABLE_UNLESS_NULL(vm->me2counter);
- /* Prevent classes from moving */
- rb_mark_tbl(vm->defined_module_hash);
+ rb_gc_mark_movable(vm->coverages);
+ rb_gc_mark_movable(vm->me2counter);
if (vm->loading_table) {
rb_mark_tbl(vm->loading_table);
@@ -2834,6 +3006,7 @@ rb_vm_mark(void *ptr)
}
}
+ rb_thread_sched_mark_zombies(vm);
rb_rjit_mark();
}
@@ -2848,17 +3021,7 @@ rb_vm_register_special_exception_str(enum ruby_special_exceptions sp, VALUE cls,
VALUE exc = rb_exc_new3(cls, rb_obj_freeze(mesg));
OBJ_FREEZE(exc);
((VALUE *)vm->special_exceptions)[sp] = exc;
- rb_gc_register_mark_object(exc);
-}
-
-int
-rb_vm_add_root_module(VALUE module)
-{
- rb_vm_t *vm = GET_VM();
-
- st_insert(vm->defined_module_hash, (st_data_t)module, (st_data_t)module);
-
- return TRUE;
+ rb_vm_register_global_object(exc);
}
static int
@@ -2868,6 +3031,9 @@ free_loading_table_entry(st_data_t key, st_data_t value, st_data_t arg)
return ST_DELETE;
}
+void rb_free_loaded_features_index(rb_vm_t *vm);
+void rb_objspace_free_objects(void *objspace);
+
int
ruby_vm_destruct(rb_vm_t *vm)
{
@@ -2875,13 +3041,62 @@ ruby_vm_destruct(rb_vm_t *vm)
if (vm) {
rb_thread_t *th = vm->ractor.main_thread;
- struct rb_objspace *objspace = vm->objspace;
- vm->ractor.main_thread = NULL;
+ VALUE *stack = th->ec->vm_stack;
+ if (rb_free_at_exit) {
+ rb_free_encoded_insn_data();
+ rb_free_global_enc_table();
+ rb_free_loaded_builtin_table();
+
+ rb_free_shared_fiber_pool();
+ rb_free_static_symid_str();
+ rb_free_transcoder_table();
+ rb_free_vm_opt_tables();
+ rb_free_warning();
+ rb_free_rb_global_tbl();
+ rb_free_loaded_features_index(vm);
+
+ rb_id_table_free(vm->negative_cme_table);
+ st_free_table(vm->overloaded_cme_table);
+
+ rb_id_table_free(RCLASS(rb_mRubyVMFrozenCore)->m_tbl);
+
+ rb_shape_t *cursor = rb_shape_get_root_shape();
+ rb_shape_t *end = rb_shape_get_shape_by_id(GET_SHAPE_TREE()->next_shape_id);
+ while (cursor < end) {
+ // 0x1 == SINGLE_CHILD_P
+ if (cursor->edges && !(((uintptr_t)cursor->edges) & 0x1))
+ rb_id_table_free(cursor->edges);
+ cursor += 1;
+ }
+
+ xfree(GET_SHAPE_TREE());
+
+ st_free_table(vm->static_ext_inits);
+ st_free_table(vm->ensure_rollback_table);
+
+ rb_vm_postponed_job_free();
- if (th) {
- rb_fiber_reset_root_local_storage(th);
- thread_free(th);
+ rb_id_table_free(vm->constant_cache);
+ st_free_table(vm->unused_block_warning_table);
+
+ if (th) {
+ xfree(th->nt);
+ th->nt = NULL;
+ }
+
+#ifndef HAVE_SETPROCTITLE
+ ruby_free_proctitle();
+#endif
}
+ else {
+ if (th) {
+ rb_fiber_reset_root_local_storage(th);
+ thread_free(th);
+ }
+ }
+
+ struct rb_objspace *objspace = vm->objspace;
+
rb_vm_living_threads_init(vm);
ruby_vm_run_at_exit_hooks(vm);
if (vm->loading_table) {
@@ -2889,12 +3104,33 @@ ruby_vm_destruct(rb_vm_t *vm)
st_free_table(vm->loading_table);
vm->loading_table = 0;
}
+ if (vm->ci_table) {
+ st_free_table(vm->ci_table);
+ vm->ci_table = NULL;
+ }
if (vm->frozen_strings) {
st_free_table(vm->frozen_strings);
vm->frozen_strings = 0;
}
RB_ALTSTACK_FREE(vm->main_altstack);
+
+ struct global_object_list *next;
+ for (struct global_object_list *list = vm->global_object_list; list; list = next) {
+ next = list->next;
+ xfree(list);
+ }
+
if (objspace) {
+ if (rb_free_at_exit) {
+ rb_objspace_free_objects(objspace);
+ rb_free_generic_iv_tbl_();
+ rb_free_default_rand_key();
+ if (th && vm->fork_gen == 0) {
+ /* If we have forked, main_thread may not be the initial thread */
+ xfree(stack);
+ ruby_mimfree(th);
+ }
+ }
rb_objspace_free(objspace);
}
rb_native_mutex_destroy(&vm->workqueue_lock);
@@ -2907,7 +3143,6 @@ ruby_vm_destruct(rb_vm_t *vm)
}
size_t rb_vm_memsize_waiting_fds(struct ccan_list_head *waiting_fds); // thread.c
-size_t rb_vm_memsize_postponed_job_buffer(void); // vm_trace.c
size_t rb_vm_memsize_workqueue(struct ccan_list_head *workqueue); // vm_trace.c
// Used for VM memsize reporting. Returns the size of the at_exit list by
@@ -2966,15 +3201,16 @@ vm_memsize(const void *ptr)
rb_st_memsize(vm->loaded_features_index) +
rb_st_memsize(vm->loading_table) +
rb_st_memsize(vm->ensure_rollback_table) +
- rb_vm_memsize_postponed_job_buffer() +
+ rb_vm_memsize_postponed_job_queue() +
rb_vm_memsize_workqueue(&vm->workqueue) +
- rb_st_memsize(vm->defined_module_hash) +
vm_memsize_at_exit_list(vm->at_exit) +
+ rb_st_memsize(vm->ci_table) +
rb_st_memsize(vm->frozen_strings) +
vm_memsize_builtin_function_table(vm->builtin_function_table) +
rb_id_table_memsize(vm->negative_cme_table) +
rb_st_memsize(vm->overloaded_cme_table) +
- vm_memsize_constant_cache()
+ vm_memsize_constant_cache() +
+ GET_SHAPE_TREE()->cache_size * sizeof(redblack_node_t)
);
// TODO
@@ -3067,7 +3303,6 @@ vm_default_params_setup(rb_vm_t *vm)
static void
vm_init2(rb_vm_t *vm)
{
- MEMZERO(vm, rb_vm_t, 1);
rb_vm_living_threads_init(vm);
vm->thread_report_on_exception = 1;
vm->src_encoding_index = -1;
@@ -3171,22 +3406,19 @@ rb_execution_context_mark(const rb_execution_context_t *ec)
if (ec->machine.stack_start && ec->machine.stack_end &&
ec != GET_EC() /* marked for current ec at the first stage of marking */
) {
- rb_gc_mark_machine_stack(ec);
- rb_gc_mark_locations((VALUE *)&ec->machine.regs,
- (VALUE *)(&ec->machine.regs) +
- sizeof(ec->machine.regs) / (sizeof(VALUE)));
+ rb_gc_mark_machine_context(ec);
}
- RUBY_MARK_UNLESS_NULL(ec->errinfo);
- RUBY_MARK_UNLESS_NULL(ec->root_svar);
+ rb_gc_mark(ec->errinfo);
+ rb_gc_mark(ec->root_svar);
if (ec->local_storage) {
rb_id_table_foreach_values(ec->local_storage, mark_local_storage_i, NULL);
}
- RUBY_MARK_UNLESS_NULL(ec->local_storage_recursive_hash);
- RUBY_MARK_UNLESS_NULL(ec->local_storage_recursive_hash_for_trace);
- RUBY_MARK_UNLESS_NULL(ec->private_const_reference);
+ rb_gc_mark(ec->local_storage_recursive_hash);
+ rb_gc_mark(ec->local_storage_recursive_hash_for_trace);
+ rb_gc_mark(ec->private_const_reference);
- RUBY_MARK_MOVABLE_UNLESS_NULL(ec->storage);
+ rb_gc_mark_movable(ec->storage);
}
void rb_fiber_mark_self(rb_fiber_t *fib);
@@ -3217,8 +3449,8 @@ thread_mark(void *ptr)
switch (th->invoke_type) {
case thread_invoke_type_proc:
case thread_invoke_type_ractor_proc:
- RUBY_MARK_UNLESS_NULL(th->invoke_arg.proc.proc);
- RUBY_MARK_UNLESS_NULL(th->invoke_arg.proc.args);
+ rb_gc_mark(th->invoke_arg.proc.proc);
+ rb_gc_mark(th->invoke_arg.proc.args);
break;
case thread_invoke_type_func:
rb_gc_mark_maybe((VALUE)th->invoke_arg.func.arg);
@@ -3228,31 +3460,35 @@ thread_mark(void *ptr)
}
rb_gc_mark(rb_ractor_self(th->ractor));
- RUBY_MARK_UNLESS_NULL(th->thgroup);
- RUBY_MARK_UNLESS_NULL(th->value);
- RUBY_MARK_UNLESS_NULL(th->pending_interrupt_queue);
- RUBY_MARK_UNLESS_NULL(th->pending_interrupt_mask_stack);
- RUBY_MARK_UNLESS_NULL(th->top_self);
- RUBY_MARK_UNLESS_NULL(th->top_wrapper);
+ rb_gc_mark(th->thgroup);
+ rb_gc_mark(th->value);
+ rb_gc_mark(th->pending_interrupt_queue);
+ rb_gc_mark(th->pending_interrupt_mask_stack);
+ rb_gc_mark(th->top_self);
+ rb_gc_mark(th->top_wrapper);
if (th->root_fiber) rb_fiber_mark_self(th->root_fiber);
RUBY_ASSERT(th->ec == rb_fiberptr_get_ec(th->ec->fiber_ptr));
- RUBY_MARK_UNLESS_NULL(th->stat_insn_usage);
- RUBY_MARK_UNLESS_NULL(th->last_status);
- RUBY_MARK_UNLESS_NULL(th->locking_mutex);
- RUBY_MARK_UNLESS_NULL(th->name);
+ rb_gc_mark(th->stat_insn_usage);
+ rb_gc_mark(th->last_status);
+ rb_gc_mark(th->locking_mutex);
+ rb_gc_mark(th->name);
- RUBY_MARK_UNLESS_NULL(th->scheduler);
+ rb_gc_mark(th->scheduler);
RUBY_MARK_LEAVE("thread");
}
+void rb_threadptr_sched_free(rb_thread_t *th); // thread_*.c
+
static void
thread_free(void *ptr)
{
rb_thread_t *th = ptr;
RUBY_FREE_ENTER("thread");
+ rb_threadptr_sched_free(th);
+
if (th->locking_mutex != Qfalse) {
rb_bug("thread_free: locking_mutex must be NULL (%p:%p)", (void *)th, (void *)th->locking_mutex);
}
@@ -3260,13 +3496,16 @@ thread_free(void *ptr)
rb_bug("thread_free: keeping_mutexes must be NULL (%p:%p)", (void *)th, (void *)th->keeping_mutexes);
}
+ ruby_xfree(th->specific_storage);
+
rb_threadptr_root_fiber_release(th);
if (th->vm && th->vm->ractor.main_thread == th) {
RUBY_GC_INFO("MRI main thread\n");
}
else {
- ruby_xfree(th->nt); // TODO
+ // ruby_xfree(th->nt);
+ // TODO: MN system collect nt, but without MN system it should be freed here.
ruby_xfree(th);
}
@@ -3325,6 +3564,10 @@ rb_ec_initialize_vm_stack(rb_execution_context_t *ec, VALUE *stack, size_t size)
{
rb_ec_set_vm_stack(ec, stack, size);
+#if VM_CHECK_MODE > 0
+ MEMZERO(stack, VALUE, size); // malloc memory could have the VM canary in it
+#endif
+
ec->cfp = (void *)(ec->vm_stack + ec->vm_stack_size);
vm_push_frame(ec,
@@ -3387,8 +3630,10 @@ th_init(rb_thread_t *th, VALUE self, rb_vm_t *vm)
th->ext_config.ractor_safe = true;
#if USE_RUBY_DEBUG_LOG
- static rb_atomic_t thread_serial = 0;
+ static rb_atomic_t thread_serial = 1;
th->serial = RUBY_ATOMIC_FETCH_ADD(thread_serial, 1);
+
+ RUBY_DEBUG_LOG("th:%u", th->serial);
#endif
}
@@ -3478,7 +3723,9 @@ kwmerge_i(VALUE key, VALUE value, VALUE hash)
static VALUE
m_core_hash_merge_kwd(VALUE recv, VALUE hash, VALUE kw)
{
- REWIND_CFP(hash = core_hash_merge_kwd(hash, kw));
+ if (!NIL_P(kw)) {
+ REWIND_CFP(hash = core_hash_merge_kwd(hash, kw));
+ }
return hash;
}
@@ -3516,7 +3763,7 @@ extern size_t rb_gc_stack_maxsize;
static VALUE
sdr(VALUE self)
{
- rb_vm_bugreport(NULL);
+ rb_vm_bugreport(NULL, stderr);
return Qnil;
}
@@ -3584,6 +3831,7 @@ f_sprintf(int c, const VALUE *v, VALUE _)
return rb_f_sprintf(c, v);
}
+/* :nodoc: */
static VALUE
vm_mtbl(VALUE self, VALUE obj, VALUE sym)
{
@@ -3591,6 +3839,7 @@ vm_mtbl(VALUE self, VALUE obj, VALUE sym)
return Qnil;
}
+/* :nodoc: */
static VALUE
vm_mtbl2(VALUE self, VALUE obj, VALUE sym)
{
@@ -3666,6 +3915,7 @@ Init_VM(void)
/* FrozenCore (hidden) */
fcore = rb_class_new(rb_cBasicObject);
rb_set_class_path(fcore, rb_cRubyVM, "FrozenCore");
+ rb_vm_register_global_object(rb_class_path_cached(fcore));
RBASIC(fcore)->flags = T_ICLASS;
klass = rb_singleton_class(fcore);
rb_define_method_id(klass, id_core_set_method_alias, m_core_set_method_alias, 3);
@@ -3684,7 +3934,7 @@ Init_VM(void)
rb_obj_freeze(fcore);
RBASIC_CLEAR_CLASS(klass);
rb_obj_freeze(klass);
- rb_gc_register_mark_object(fcore);
+ rb_vm_register_global_object(fcore);
rb_mRubyVMFrozenCore = fcore;
/*
@@ -3884,9 +4134,6 @@ Init_VM(void)
rb_ary_push(opts, rb_str_new2("call threaded code"));
#endif
-#if OPT_STACK_CACHING
- rb_ary_push(opts, rb_str_new2("stack caching"));
-#endif
#if OPT_OPERANDS_UNIFICATION
rb_ary_push(opts, rb_str_new2("operands unification"));
#endif
@@ -3929,7 +4176,7 @@ Init_VM(void)
rb_vm_t *vm = ruby_current_vm_ptr;
rb_thread_t *th = GET_THREAD();
VALUE filename = rb_fstring_lit("<main>");
- const rb_iseq_t *iseq = rb_iseq_new(0, filename, filename, Qnil, 0, ISEQ_TYPE_TOP);
+ const rb_iseq_t *iseq = rb_iseq_new(Qnil, filename, filename, Qnil, 0, ISEQ_TYPE_TOP);
// Ractor setup
rb_ractor_main_setup(vm, th->ractor, th);
@@ -3945,7 +4192,7 @@ Init_VM(void)
th->top_wrapper = 0;
th->top_self = rb_vm_top_self();
- rb_gc_register_mark_object((VALUE)iseq);
+ rb_vm_register_global_object((VALUE)iseq);
th->ec->cfp->iseq = iseq;
th->ec->cfp->pc = ISEQ_BODY(iseq)->iseq_encoded;
th->ec->cfp->self = th->top_self;
@@ -3958,7 +4205,9 @@ Init_VM(void)
*/
rb_define_global_const("TOPLEVEL_BINDING", rb_binding_new());
+#ifdef _WIN32
rb_objspace_gc_enable(vm->objspace);
+#endif
}
vm_init_redefined_flag();
@@ -3966,7 +4215,7 @@ Init_VM(void)
rb_add_method_optimized(rb_singleton_class(rb_block_param_proxy), idCall,
OPTIMIZED_METHOD_TYPE_BLOCK_CALL, 0, METHOD_VISI_PUBLIC);
rb_obj_freeze(rb_block_param_proxy);
- rb_gc_register_mark_object(rb_block_param_proxy);
+ rb_vm_register_global_object(rb_block_param_proxy);
/* vm_backtrace.c */
Init_vm_backtrace();
@@ -3989,37 +4238,58 @@ void
Init_BareVM(void)
{
/* VM bootstrap: phase 1 */
- rb_vm_t * vm = ruby_mimmalloc(sizeof(*vm));
- rb_thread_t * th = ruby_mimmalloc(sizeof(*th));
+ rb_vm_t *vm = ruby_mimcalloc(1, sizeof(*vm));
+ rb_thread_t *th = ruby_mimcalloc(1, sizeof(*th));
if (!vm || !th) {
fputs("[FATAL] failed to allocate memory\n", stderr);
exit(EXIT_FAILURE);
}
// setup the VM
- MEMZERO(th, rb_thread_t, 1);
vm_init2(vm);
- vm->objspace = rb_objspace_alloc();
+ rb_vm_postponed_job_queue_init(vm);
ruby_current_vm_ptr = vm;
+ rb_objspace_alloc();
vm->negative_cme_table = rb_id_table_create(16);
vm->overloaded_cme_table = st_init_numtable();
vm->constant_cache = rb_id_table_create(0);
+ vm->unused_block_warning_table = st_init_numtable();
+
+ // TODO: remove before Ruby 3.4.0 release
+ const char *s = getenv("RUBY_TRY_UNUSED_BLOCK_WARNING_STRICT");
+ if (s && strcmp(s, "1") == 0) {
+ vm->unused_block_warning_strict = true;
+ }
// setup main thread
th->nt = ZALLOC(struct rb_native_thread);
th->vm = vm;
th->ractor = vm->ractor.main_ractor = rb_ractor_main_alloc();
Init_native_thread(th);
+ rb_jit_cont_init();
th_init(th, 0, vm);
rb_ractor_set_current_ec(th->ractor, th->ec);
- ruby_thread_init_stack(th);
+ /* n.b. native_main_thread_stack_top is set by the INIT_STACK macro */
+ ruby_thread_init_stack(th, native_main_thread_stack_top);
// setup ractor system
rb_native_mutex_initialize(&vm->ractor.sync.lock);
- rb_native_cond_initialize(&vm->ractor.sync.barrier_cond);
rb_native_cond_initialize(&vm->ractor.sync.terminate_cond);
+
+ vm_opt_method_def_table = st_init_numtable();
+ vm_opt_mid_table = st_init_numtable();
+
+#ifdef RUBY_THREAD_WIN32_H
+ rb_native_cond_initialize(&vm->ractor.sync.barrier_cond);
+#endif
+}
+
+void
+ruby_init_stack(void *addr)
+{
+ native_main_thread_stack_top = addr;
}
#ifndef _WIN32
@@ -4027,16 +4297,123 @@ Init_BareVM(void)
#include <sys/mman.h>
#endif
+
+#ifndef MARK_OBJECT_ARY_BUCKET_SIZE
+#define MARK_OBJECT_ARY_BUCKET_SIZE 1024
+#endif
+
+struct pin_array_list {
+ VALUE next;
+ long len;
+ VALUE *array;
+};
+
+static void
+pin_array_list_mark(void *data)
+{
+ struct pin_array_list *array = (struct pin_array_list *)data;
+ rb_gc_mark_movable(array->next);
+
+ rb_gc_mark_vm_stack_values(array->len, array->array);
+}
+
+static void
+pin_array_list_free(void *data)
+{
+ struct pin_array_list *array = (struct pin_array_list *)data;
+ xfree(array->array);
+}
+
+static size_t
+pin_array_list_memsize(const void *data)
+{
+ return sizeof(struct pin_array_list) + (MARK_OBJECT_ARY_BUCKET_SIZE * sizeof(VALUE));
+}
+
+static void
+pin_array_list_update_references(void *data)
+{
+ struct pin_array_list *array = (struct pin_array_list *)data;
+ array->next = rb_gc_location(array->next);
+}
+
+static const rb_data_type_t pin_array_list_type = {
+ .wrap_struct_name = "VM/pin_array_list",
+ .function = {
+ .dmark = pin_array_list_mark,
+ .dfree = pin_array_list_free,
+ .dsize = pin_array_list_memsize,
+ .dcompact = pin_array_list_update_references,
+ },
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE,
+};
+
+static VALUE
+pin_array_list_new(VALUE next)
+{
+ struct pin_array_list *array_list;
+ VALUE obj = TypedData_Make_Struct(0, struct pin_array_list, &pin_array_list_type, array_list);
+ RB_OBJ_WRITE(obj, &array_list->next, next);
+ array_list->array = ALLOC_N(VALUE, MARK_OBJECT_ARY_BUCKET_SIZE);
+ return obj;
+}
+
+static VALUE
+pin_array_list_append(VALUE obj, VALUE item)
+{
+ struct pin_array_list *array_list;
+ TypedData_Get_Struct(obj, struct pin_array_list, &pin_array_list_type, array_list);
+
+ if (array_list->len >= MARK_OBJECT_ARY_BUCKET_SIZE) {
+ obj = pin_array_list_new(obj);
+ TypedData_Get_Struct(obj, struct pin_array_list, &pin_array_list_type, array_list);
+ }
+
+ RB_OBJ_WRITE(obj, &array_list->array[array_list->len], item);
+ array_list->len++;
+ return obj;
+}
+
+void
+rb_vm_register_global_object(VALUE obj)
+{
+ RUBY_ASSERT(!RB_SPECIAL_CONST_P(obj));
+ if (RB_SPECIAL_CONST_P(obj)) {
+ return;
+ }
+
+ switch (RB_BUILTIN_TYPE(obj)) {
+ case T_CLASS:
+ case T_MODULE:
+ if (FL_TEST(obj, RCLASS_IS_ROOT)) {
+ return;
+ }
+ FL_SET(obj, RCLASS_IS_ROOT);
+ break;
+ default:
+ break;
+ }
+ RB_VM_LOCK_ENTER();
+ {
+ VALUE list = GET_VM()->mark_object_ary;
+ VALUE head = pin_array_list_append(list, obj);
+ if (head != list) {
+ GET_VM()->mark_object_ary = head;
+ }
+ RB_GC_GUARD(obj);
+ }
+ RB_VM_LOCK_LEAVE();
+}
+
void
Init_vm_objects(void)
{
rb_vm_t *vm = GET_VM();
- vm->defined_module_hash = st_init_numtable();
-
/* initialize mark object array, hash */
- vm->mark_object_ary = rb_ary_hidden_new(128);
+ vm->mark_object_ary = pin_array_list_new(Qnil);
vm->loading_table = st_init_strtable();
+ vm->ci_table = st_init_table(&vm_ci_hashtype);
vm->frozen_strings = st_init_table_with_size(&rb_fstring_hash_type, 10000);
}
@@ -4076,6 +4453,14 @@ rb_ruby_verbose_ptr(void)
return &cr->verbose;
}
+static bool prism;
+
+bool *
+rb_ruby_prism_ptr(void)
+{
+ return &prism;
+}
+
VALUE *
rb_ruby_debug_ptr(void)
{
@@ -4083,6 +4468,14 @@ rb_ruby_debug_ptr(void)
return &cr->debug;
}
+bool rb_free_at_exit = false;
+
+bool
+ruby_free_at_exit_p(void)
+{
+ return rb_free_at_exit;
+}
+
/* iseq.c */
VALUE rb_insn_operand_intern(const rb_iseq_t *iseq,
VALUE insn, int op_no, VALUE op,
@@ -4285,21 +4678,21 @@ usage_analysis_register_stop(VALUE self)
static VALUE
usage_analysis_insn_running(VALUE self)
{
- return RBOOL(ruby_vm_collect_usage_func_insn != 0);
+ return RBOOL(ruby_vm_collect_usage_func_insn != 0);
}
/* :nodoc: */
static VALUE
usage_analysis_operand_running(VALUE self)
{
- return RBOOL(ruby_vm_collect_usage_func_operand != 0);
+ return RBOOL(ruby_vm_collect_usage_func_operand != 0);
}
/* :nodoc: */
static VALUE
usage_analysis_register_running(VALUE self)
{
- return RBOOL(ruby_vm_collect_usage_func_register != 0);
+ return RBOOL(ruby_vm_collect_usage_func_register != 0);
}
static VALUE
diff --git a/vm_args.c b/vm_args.c
index cc7f1e29b5..1a78e96776 100644
--- a/vm_args.c
+++ b/vm_args.c
@@ -165,7 +165,7 @@ args_copy(struct args_info *args)
static inline const VALUE *
args_rest_argv(struct args_info *args)
{
- return RARRAY_CONST_PTR_TRANSIENT(args->rest) + args->rest_index;
+ return RARRAY_CONST_PTR(args->rest) + args->rest_index;
}
static inline VALUE
@@ -230,7 +230,7 @@ args_setup_post_parameters(struct args_info *args, int argc, VALUE *locals)
{
long len;
len = RARRAY_LEN(args->rest);
- MEMCPY(locals, RARRAY_CONST_PTR_TRANSIENT(args->rest) + len - argc, VALUE, argc);
+ MEMCPY(locals, RARRAY_CONST_PTR(args->rest) + len - argc, VALUE, argc);
rb_ary_resize(args->rest, len - argc);
}
@@ -251,7 +251,7 @@ args_setup_opt_parameters(struct args_info *args, int opt_max, VALUE *locals)
if (args->rest) {
int len = RARRAY_LENINT(args->rest);
- const VALUE *argv = RARRAY_CONST_PTR_TRANSIENT(args->rest);
+ const VALUE *argv = RARRAY_CONST_PTR(args->rest);
for (; i<opt_max && args->rest_index < len; i++, args->rest_index++) {
locals[i] = argv[args->rest_index];
@@ -396,11 +396,116 @@ args_setup_kw_parameters(rb_execution_context_t *const ec, const rb_iseq_t *cons
locals[key_num] = unspecified_bits_value;
}
+static void
+args_setup_kw_parameters_from_kwsplat(rb_execution_context_t *const ec, const rb_iseq_t *const iseq,
+ VALUE keyword_hash, VALUE *const locals, bool remove_hash_value)
+{
+ const ID *acceptable_keywords = ISEQ_BODY(iseq)->param.keyword->table;
+ const int req_key_num = ISEQ_BODY(iseq)->param.keyword->required_num;
+ const int key_num = ISEQ_BODY(iseq)->param.keyword->num;
+ const VALUE * const default_values = ISEQ_BODY(iseq)->param.keyword->default_values;
+ VALUE missing = 0;
+ int i, di;
+ int unspecified_bits = 0;
+ size_t keyword_size = RHASH_SIZE(keyword_hash);
+ VALUE unspecified_bits_value = Qnil;
+
+ for (i=0; i<req_key_num; i++) {
+ VALUE key = ID2SYM(acceptable_keywords[i]);
+ VALUE value;
+ if (remove_hash_value) {
+ value = rb_hash_delete_entry(keyword_hash, key);
+ }
+ else {
+ value = rb_hash_lookup2(keyword_hash, key, Qundef);
+ }
+
+ if (!UNDEF_P(value)) {
+ keyword_size--;
+ locals[i] = value;
+ }
+ else {
+ if (!missing) missing = rb_ary_hidden_new(1);
+ rb_ary_push(missing, key);
+ }
+ }
+
+ if (missing) argument_kw_error(ec, iseq, "missing", missing);
+
+ for (di=0; i<key_num; i++, di++) {
+ VALUE key = ID2SYM(acceptable_keywords[i]);
+ VALUE value;
+ if (remove_hash_value) {
+ value = rb_hash_delete_entry(keyword_hash, key);
+ }
+ else {
+ value = rb_hash_lookup2(keyword_hash, key, Qundef);
+ }
+
+ if (!UNDEF_P(value)) {
+ keyword_size--;
+ locals[i] = value;
+ }
+ else {
+ if (UNDEF_P(default_values[di])) {
+ locals[i] = Qnil;
+
+ if (LIKELY(i < KW_SPECIFIED_BITS_MAX)) {
+ unspecified_bits |= 0x01 << di;
+ }
+ else {
+ if (NIL_P(unspecified_bits_value)) {
+ /* fixnum -> hash */
+ int j;
+ unspecified_bits_value = rb_hash_new();
+
+ for (j=0; j<KW_SPECIFIED_BITS_MAX; j++) {
+ if (unspecified_bits & (0x01 << j)) {
+ rb_hash_aset(unspecified_bits_value, INT2FIX(j), Qtrue);
+ }
+ }
+ }
+ rb_hash_aset(unspecified_bits_value, INT2FIX(di), Qtrue);
+ }
+ }
+ else {
+ locals[i] = default_values[di];
+ }
+ }
+ }
+
+ if (ISEQ_BODY(iseq)->param.flags.has_kwrest) {
+ const int rest_hash_index = key_num + 1;
+ locals[rest_hash_index] = keyword_hash;
+ }
+ else {
+ if (!remove_hash_value) {
+ if (keyword_size != 0) {
+ /* Recurse with duplicated keyword hash in remove mode.
+ * This is simpler than writing code to check which entries in the hash do not match.
+ * This will raise an exception, so the additional performance impact shouldn't be material.
+ */
+ args_setup_kw_parameters_from_kwsplat(ec, iseq, rb_hash_dup(keyword_hash), locals, true);
+ }
+ }
+ else if (!RHASH_EMPTY_P(keyword_hash)) {
+ argument_kw_error(ec, iseq, "unknown", rb_hash_keys(keyword_hash));
+ }
+ }
+
+ if (NIL_P(unspecified_bits_value)) {
+ unspecified_bits_value = INT2FIX(unspecified_bits);
+ }
+ locals[key_num] = unspecified_bits_value;
+}
+
static inline void
-args_setup_kw_rest_parameter(VALUE keyword_hash, VALUE *locals, int kw_flag)
+args_setup_kw_rest_parameter(VALUE keyword_hash, VALUE *locals, int kw_flag, bool anon_kwrest)
{
if (NIL_P(keyword_hash)) {
- keyword_hash = rb_hash_new();
+ if (!anon_kwrest) {
+ keyword_hash = rb_hash_new();
+ }
}
else if (!(kw_flag & VM_CALL_KW_SPLAT_MUT)) {
keyword_hash = rb_hash_dup(keyword_hash);
@@ -415,28 +520,21 @@ args_setup_block_parameter(const rb_execution_context_t *ec, struct rb_calling_i
*locals = rb_vm_bh_to_procval(ec, block_handler);
}
-struct fill_values_arg {
- VALUE *keys;
- VALUE *vals;
- int argc;
-};
-
-static int
-fill_keys_values(st_data_t key, st_data_t val, st_data_t ptr)
-{
- struct fill_values_arg *arg = (struct fill_values_arg *)ptr;
- int i = arg->argc++;
- arg->keys[i] = (VALUE)key;
- arg->vals[i] = (VALUE)val;
- return ST_CONTINUE;
-}
-
static inline int
ignore_keyword_hash_p(VALUE keyword_hash, const rb_iseq_t * const iseq, unsigned int * kw_flag, VALUE * converted_keyword_hash)
{
+ if (keyword_hash == Qnil) {
+ return 1;
+ }
+
if (!RB_TYPE_P(keyword_hash, T_HASH)) {
keyword_hash = rb_to_hash_type(keyword_hash);
}
+ else if (UNLIKELY(ISEQ_BODY(iseq)->param.flags.anon_kwrest)) {
+ if (!ISEQ_BODY(iseq)->param.flags.has_kw) {
+ *kw_flag |= VM_CALL_KW_SPLAT_MUT;
+ }
+ }
if (!(*kw_flag & VM_CALL_KW_SPLAT_MUT) &&
(ISEQ_BODY(iseq)->param.flags.has_kwrest ||
@@ -471,7 +569,8 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
const int min_argc = ISEQ_BODY(iseq)->param.lead_num + ISEQ_BODY(iseq)->param.post_num;
const int max_argc = (ISEQ_BODY(iseq)->param.flags.has_rest == FALSE) ? min_argc + ISEQ_BODY(iseq)->param.opt_num : UNLIMITED_ARGUMENTS;
int given_argc;
- unsigned int kw_flag = vm_ci_flag(ci) & (VM_CALL_KWARG | VM_CALL_KW_SPLAT | VM_CALL_KW_SPLAT_MUT);
+ unsigned int ci_flag = vm_ci_flag(ci);
+ unsigned int kw_flag = ci_flag & (VM_CALL_KWARG | VM_CALL_KW_SPLAT | VM_CALL_KW_SPLAT_MUT);
int opt_pc = 0, allow_autosplat = !kw_flag;
struct args_info args_body, *args;
VALUE keyword_hash = Qnil;
@@ -506,7 +605,21 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
args = &args_body;
given_argc = args->argc = calling->argc;
args->argv = locals;
- args->rest_dupped = FALSE;
+ args->rest_dupped = ci_flag & VM_CALL_ARGS_SPLAT_MUT;
+
+ if (UNLIKELY(ISEQ_BODY(iseq)->param.flags.anon_rest)) {
+ if ((ci_flag & VM_CALL_ARGS_SPLAT) &&
+ given_argc == ISEQ_BODY(iseq)->param.lead_num + (kw_flag ? 2 : 1) &&
+ !ISEQ_BODY(iseq)->param.flags.has_opt &&
+ !ISEQ_BODY(iseq)->param.flags.has_post &&
+ !ISEQ_BODY(iseq)->param.flags.ruby2_keywords &&
+ (!kw_flag ||
+ !ISEQ_BODY(iseq)->param.flags.has_kw ||
+ !ISEQ_BODY(iseq)->param.flags.has_kwrest ||
+ !ISEQ_BODY(iseq)->param.flags.accepts_no_kwarg)) {
+ args->rest_dupped = true;
+ }
+ }
if (kw_flag & VM_CALL_KWARG) {
args->kw_arg = vm_ci_kwarg(ci);
@@ -530,7 +643,7 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
args->kw_argv = NULL;
}
- if ((vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT) && (vm_ci_flag(ci) & VM_CALL_KW_SPLAT)) {
+ if ((ci_flag & VM_CALL_ARGS_SPLAT) && (ci_flag & VM_CALL_KW_SPLAT)) {
// f(*a, **kw)
args->rest_index = 0;
keyword_hash = locals[--args->argc];
@@ -542,34 +655,41 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
else if (UNLIKELY(ISEQ_BODY(iseq)->param.flags.ruby2_keywords)) {
converted_keyword_hash = check_kwrestarg(converted_keyword_hash, &kw_flag);
flag_keyword_hash = converted_keyword_hash;
+ arg_rest_dup(args);
rb_ary_push(args->rest, converted_keyword_hash);
keyword_hash = Qnil;
}
else if (!ISEQ_BODY(iseq)->param.flags.has_kwrest && !ISEQ_BODY(iseq)->param.flags.has_kw) {
converted_keyword_hash = check_kwrestarg(converted_keyword_hash, &kw_flag);
+ arg_rest_dup(args);
rb_ary_push(args->rest, converted_keyword_hash);
keyword_hash = Qnil;
}
+ else {
+ keyword_hash = converted_keyword_hash;
+ }
int len = RARRAY_LENINT(args->rest);
given_argc += len - 2;
}
- else if (vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT) {
+ else if (ci_flag & VM_CALL_ARGS_SPLAT) {
// f(*a)
args->rest_index = 0;
args->rest = locals[--args->argc];
int len = RARRAY_LENINT(args->rest);
given_argc += len - 1;
- rest_last = RARRAY_AREF(args->rest, len - 1);
if (!kw_flag && len > 0) {
- if (RB_TYPE_P(rest_last, T_HASH) &&
- (((struct RHash *)rest_last)->basic.flags & RHASH_PASS_AS_KEYWORDS)) {
+ rest_last = RARRAY_AREF(args->rest, len - 1);
+ if (RB_TYPE_P(rest_last, T_HASH) && FL_TEST_RAW(rest_last, RHASH_PASS_AS_KEYWORDS)) {
// def f(**kw); a = [..., kw]; g(*a)
splat_flagged_keyword_hash = rest_last;
rest_last = rb_hash_dup(rest_last);
kw_flag |= VM_CALL_KW_SPLAT | VM_CALL_KW_SPLAT_MUT;
+ // Unset rest_dupped set by anon_rest as we may need to modify splat in this case
+ args->rest_dupped = false;
+
if (ignore_keyword_hash_p(rest_last, iseq, &kw_flag, &converted_keyword_hash)) {
arg_rest_dup(args);
rb_ary_pop(args->rest);
@@ -595,9 +715,6 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
}
}
}
- else {
- rest_last = 0;
- }
}
else {
args->rest = Qfalse;
@@ -611,6 +728,11 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
kw_flag &= ~(VM_CALL_KW_SPLAT | VM_CALL_KW_SPLAT_MUT);
}
else {
+ if (!(kw_flag & VM_CALL_KW_SPLAT_MUT) && !ISEQ_BODY(iseq)->param.flags.has_kw) {
+ converted_keyword_hash = rb_hash_dup(converted_keyword_hash);
+ kw_flag |= VM_CALL_KW_SPLAT_MUT;
+ }
+
if (last_arg != converted_keyword_hash) {
last_arg = converted_keyword_hash;
args->argv[args->argc-1] = last_arg;
@@ -628,8 +750,8 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
}
}
- if (flag_keyword_hash && RB_TYPE_P(flag_keyword_hash, T_HASH)) {
- ((struct RHash *)flag_keyword_hash)->basic.flags |= RHASH_PASS_AS_KEYWORDS;
+ if (flag_keyword_hash) {
+ FL_SET_RAW(flag_keyword_hash, RHASH_PASS_AS_KEYWORDS);
}
if (kw_flag && ISEQ_BODY(iseq)->param.flags.accepts_no_kwarg) {
@@ -640,8 +762,9 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
case arg_setup_method:
break; /* do nothing special */
case arg_setup_block:
- if (given_argc == (NIL_P(keyword_hash) ? 1 : 2) &&
+ if (given_argc == 1 &&
allow_autosplat &&
+ !splat_flagged_keyword_hash &&
(min_argc > 0 || ISEQ_BODY(iseq)->param.opt_num > 1) &&
!ISEQ_BODY(iseq)->param.flags.ambiguous_param0 &&
!((ISEQ_BODY(iseq)->param.flags.has_kw ||
@@ -715,15 +838,12 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
args_setup_kw_parameters(ec, iseq, args->kw_argv, kw_arg->keyword_len, kw_arg->keywords, klocals);
}
else if (!NIL_P(keyword_hash)) {
- int kw_len = rb_long2int(RHASH_SIZE(keyword_hash));
- struct fill_values_arg arg;
- /* copy kw_argv */
- arg.keys = args->kw_argv = ALLOCA_N(VALUE, kw_len * 2);
- arg.vals = arg.keys + kw_len;
- arg.argc = 0;
- rb_hash_foreach(keyword_hash, fill_keys_values, (VALUE)&arg);
- VM_ASSERT(arg.argc == kw_len);
- args_setup_kw_parameters(ec, iseq, arg.vals, kw_len, arg.keys, klocals);
+ bool remove_hash_value = false;
+ if (ISEQ_BODY(iseq)->param.flags.has_kwrest) {
+ keyword_hash = check_kwrestarg(keyword_hash, &kw_flag);
+ remove_hash_value = true;
+ }
+ args_setup_kw_parameters_from_kwsplat(ec, iseq, keyword_hash, klocals, remove_hash_value);
}
else {
VM_ASSERT(args_argc(args) == 0);
@@ -731,7 +851,8 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
}
}
else if (ISEQ_BODY(iseq)->param.flags.has_kwrest) {
- args_setup_kw_rest_parameter(keyword_hash, locals + ISEQ_BODY(iseq)->param.keyword->rest_start, kw_flag);
+ args_setup_kw_rest_parameter(keyword_hash, locals + ISEQ_BODY(iseq)->param.keyword->rest_start,
+ kw_flag, ISEQ_BODY(iseq)->param.flags.anon_kwrest);
}
else if (!NIL_P(keyword_hash) && RHASH_SIZE(keyword_hash) > 0 && arg_setup_type == arg_setup_method) {
argument_kw_error(ec, iseq, "unknown", rb_hash_keys(keyword_hash));
@@ -888,10 +1009,7 @@ vm_caller_setup_arg_block(const rb_execution_context_t *ec, rb_control_frame_t *
return VM_BLOCK_HANDLER_NONE;
}
else if (block_code == rb_block_param_proxy) {
- VM_ASSERT(!VM_CFP_IN_HEAP_P(GET_EC(), reg_cfp));
- VALUE handler = VM_CF_BLOCK_HANDLER(reg_cfp);
- reg_cfp->block_code = (const void *) handler;
- return handler;
+ return VM_CF_BLOCK_HANDLER(reg_cfp);
}
else if (SYMBOL_P(block_code) && rb_method_basic_definition_p(rb_cSymbol, idTo_proc)) {
const rb_cref_t *cref = vm_env_cref(reg_cfp->ep);
@@ -903,7 +1021,7 @@ vm_caller_setup_arg_block(const rb_execution_context_t *ec, rb_control_frame_t *
VALUE callback_arg = rb_ary_hidden_new(2);
rb_ary_push(callback_arg, block_code);
rb_ary_push(callback_arg, ref);
- OBJ_FREEZE_RAW(callback_arg);
+ OBJ_FREEZE(callback_arg);
func = rb_func_lambda_new(refine_sym_proc_call, callback_arg, 1, UNLIMITED_ARGUMENTS);
rb_hash_aset(ref, block_code, func);
}
diff --git a/vm_backtrace.c b/vm_backtrace.c
index 9a041c4c2c..22b28368d7 100644
--- a/vm_backtrace.c
+++ b/vm_backtrace.c
@@ -71,7 +71,7 @@ calc_pos(const rb_iseq_t *iseq, const VALUE *pc, int *lineno, int *node_id)
#if VMDEBUG && defined(HAVE_BUILTIN___BUILTIN_TRAP)
else {
/* SDR() is not possible; that causes infinite loop. */
- rb_print_backtrace();
+ rb_print_backtrace(stderr);
__builtin_trap();
}
#endif
@@ -120,14 +120,9 @@ rb_vm_get_sourceline(const rb_control_frame_t *cfp)
}
typedef struct rb_backtrace_location_struct {
- enum LOCATION_TYPE {
- LOCATION_TYPE_ISEQ = 1,
- LOCATION_TYPE_CFUNC,
- } type;
-
+ const rb_callable_method_entry_t *cme;
const rb_iseq_t *iseq;
const VALUE *pc;
- ID mid;
} rb_backtrace_location_t;
struct valued_frame_info {
@@ -139,37 +134,32 @@ static void
location_mark(void *ptr)
{
struct valued_frame_info *vfi = (struct valued_frame_info *)ptr;
- rb_gc_mark(vfi->btobj);
+ rb_gc_mark_movable(vfi->btobj);
}
static void
-location_mark_entry(rb_backtrace_location_t *fi)
+location_ref_update(void *ptr)
{
- switch (fi->type) {
- case LOCATION_TYPE_ISEQ:
- rb_gc_mark_movable((VALUE)fi->iseq);
- break;
- case LOCATION_TYPE_CFUNC:
- if (fi->iseq) {
- rb_gc_mark_movable((VALUE)fi->iseq);
- }
- break;
- default:
- break;
- }
+ struct valued_frame_info *vfi = ptr;
+ vfi->btobj = rb_gc_location(vfi->btobj);
}
-static size_t
-location_memsize(const void *ptr)
+static void
+location_mark_entry(rb_backtrace_location_t *fi)
{
- /* rb_backtrace_location_t *fi = (rb_backtrace_location_t *)ptr; */
- return sizeof(rb_backtrace_location_t);
+ rb_gc_mark((VALUE)fi->cme);
+ if (fi->iseq) rb_gc_mark_movable((VALUE)fi->iseq);
}
static const rb_data_type_t location_data_type = {
"frame_info",
- {location_mark, RUBY_TYPED_DEFAULT_FREE, location_memsize,},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
+ {
+ location_mark,
+ RUBY_TYPED_DEFAULT_FREE,
+ NULL, // No external memory to report,
+ location_ref_update,
+ },
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
};
int
@@ -182,25 +172,17 @@ static inline rb_backtrace_location_t *
location_ptr(VALUE locobj)
{
struct valued_frame_info *vloc;
- GetCoreDataFromValue(locobj, struct valued_frame_info, vloc);
+ TypedData_Get_Struct(locobj, struct valued_frame_info, &location_data_type, vloc);
return vloc->loc;
}
static int
location_lineno(rb_backtrace_location_t *loc)
{
- switch (loc->type) {
- case LOCATION_TYPE_ISEQ:
+ if (loc->iseq) {
return calc_lineno(loc->iseq, loc->pc);
- case LOCATION_TYPE_CFUNC:
- if (loc->iseq && loc->pc) {
- return calc_lineno(loc->iseq, loc->pc);
- }
- return 0;
- default:
- rb_bug("location_lineno: unreachable");
- UNREACHABLE;
}
+ return 0;
}
/*
@@ -217,20 +199,86 @@ location_lineno_m(VALUE self)
return INT2FIX(location_lineno(location_ptr(self)));
}
+VALUE rb_mod_name0(VALUE klass, bool *permanent);
+
+VALUE
+rb_gen_method_name(VALUE owner, VALUE name)
+{
+ bool permanent;
+ if (RB_TYPE_P(owner, T_CLASS) || RB_TYPE_P(owner, T_MODULE)) {
+ if (RCLASS_SINGLETON_P(owner)) {
+ VALUE v = RCLASS_ATTACHED_OBJECT(owner);
+ if (RB_TYPE_P(v, T_CLASS) || RB_TYPE_P(v, T_MODULE)) {
+ v = rb_mod_name0(v, &permanent);
+ if (permanent && !NIL_P(v)) {
+ return rb_sprintf("%"PRIsVALUE".%"PRIsVALUE, v, name);
+ }
+ }
+ }
+ else {
+ owner = rb_mod_name0(owner, &permanent);
+ if (permanent && !NIL_P(owner)) {
+ return rb_sprintf("%"PRIsVALUE"#%"PRIsVALUE, owner, name);
+ }
+ }
+ }
+ return name;
+}
+
static VALUE
-location_label(rb_backtrace_location_t *loc)
-{
- switch (loc->type) {
- case LOCATION_TYPE_ISEQ:
- return ISEQ_BODY(loc->iseq)->location.label;
- case LOCATION_TYPE_CFUNC:
- return rb_id2str(loc->mid);
+calculate_iseq_label(VALUE owner, const rb_iseq_t *iseq)
+{
+retry:
+ switch (ISEQ_BODY(iseq)->type) {
+ case ISEQ_TYPE_TOP:
+ case ISEQ_TYPE_CLASS:
+ case ISEQ_TYPE_MAIN:
+ return ISEQ_BODY(iseq)->location.label;
+ case ISEQ_TYPE_METHOD:
+ return rb_gen_method_name(owner, ISEQ_BODY(iseq)->location.label);
+ case ISEQ_TYPE_BLOCK:
+ case ISEQ_TYPE_PLAIN: {
+ int level = 0;
+ const rb_iseq_t *orig_iseq = iseq;
+ if (ISEQ_BODY(orig_iseq)->parent_iseq != 0) {
+ while (ISEQ_BODY(orig_iseq)->local_iseq != iseq) {
+ if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_BLOCK) {
+ level++;
+ }
+ iseq = ISEQ_BODY(iseq)->parent_iseq;
+ }
+ }
+ if (level <= 1) {
+ return rb_sprintf("block in %"PRIsVALUE, calculate_iseq_label(owner, iseq));
+ }
+ else {
+ return rb_sprintf("block (%d levels) in %"PRIsVALUE, level, calculate_iseq_label(owner, iseq));
+ }
+ }
+ case ISEQ_TYPE_RESCUE:
+ case ISEQ_TYPE_ENSURE:
+ case ISEQ_TYPE_EVAL:
+ iseq = ISEQ_BODY(iseq)->parent_iseq;
+ goto retry;
default:
- rb_bug("location_label: unreachable");
- UNREACHABLE;
+ rb_bug("calculate_iseq_label: unreachable");
}
}
+static VALUE
+location_label(rb_backtrace_location_t *loc)
+{
+ if (loc->cme && loc->cme->def->type == VM_METHOD_TYPE_CFUNC) {
+ return rb_gen_method_name(loc->cme->owner, rb_id2str(loc->cme->def->original_id));
+ }
+ else {
+ VALUE owner = Qnil;
+ if (loc->cme) {
+ owner = loc->cme->owner;
+ }
+ return calculate_iseq_label(owner, loc->iseq);
+ }
+}
/*
* Returns the label of this frame.
*
@@ -247,15 +295,14 @@ location_label(rb_backtrace_location_t *loc)
* 1.times do
* puts caller_locations(0).first.label
* end
- *
* end
* end
*
* The result of calling +foo+ is this:
*
- * label: foo
- * label: block in foo
- * label: block (2 levels) in foo
+ * foo
+ * block in foo
+ * block (2 levels) in foo
*
*/
static VALUE
@@ -267,21 +314,36 @@ location_label_m(VALUE self)
static VALUE
location_base_label(rb_backtrace_location_t *loc)
{
- switch (loc->type) {
- case LOCATION_TYPE_ISEQ:
- return ISEQ_BODY(loc->iseq)->location.base_label;
- case LOCATION_TYPE_CFUNC:
- return rb_id2str(loc->mid);
- default:
- rb_bug("location_base_label: unreachable");
- UNREACHABLE;
+ if (loc->cme && loc->cme->def->type == VM_METHOD_TYPE_CFUNC) {
+ return rb_id2str(loc->cme->def->original_id);
}
+
+ return ISEQ_BODY(loc->iseq)->location.base_label;
}
/*
- * Returns the base label of this frame.
+ * Returns the base label of this frame, which is usually equal to the label,
+ * without decoration.
*
- * Usually same as #label, without decoration.
+ * Consider the following example:
+ *
+ * def foo
+ * puts caller_locations(0).first.base_label
+ *
+ * 1.times do
+ * puts caller_locations(0).first.base_label
+ *
+ * 1.times do
+ * puts caller_locations(0).first.base_label
+ * end
+ * end
+ * end
+ *
+ * The result of calling +foo+ is this:
+ *
+ * foo
+ * foo
+ * foo
*/
static VALUE
location_base_label_m(VALUE self)
@@ -292,15 +354,7 @@ location_base_label_m(VALUE self)
static const rb_iseq_t *
location_iseq(rb_backtrace_location_t *loc)
{
- switch (loc->type) {
- case LOCATION_TYPE_ISEQ:
- return loc->iseq;
- case LOCATION_TYPE_CFUNC:
- return loc->iseq;
- default:
- rb_bug("location_iseq: unreachable");
- UNREACHABLE;
- }
+ return loc->iseq;
}
/*
@@ -324,18 +378,10 @@ location_path_m(VALUE self)
static int
location_node_id(rb_backtrace_location_t *loc)
{
- switch (loc->type) {
- case LOCATION_TYPE_ISEQ:
+ if (loc->iseq && loc->pc) {
return calc_node_id(loc->iseq, loc->pc);
- case LOCATION_TYPE_CFUNC:
- if (loc->iseq && loc->pc) {
- return calc_node_id(loc->iseq, loc->pc);
- }
- return -1;
- default:
- rb_bug("location_node_id: unreachable");
- UNREACHABLE;
}
+ return -1;
}
#endif
@@ -361,18 +407,10 @@ rb_get_iseq_from_frame_info(VALUE obj)
static VALUE
location_realpath(rb_backtrace_location_t *loc)
{
- switch (loc->type) {
- case LOCATION_TYPE_ISEQ:
+ if (loc->iseq) {
return rb_iseq_realpath(loc->iseq);
- case LOCATION_TYPE_CFUNC:
- if (loc->iseq) {
- return rb_iseq_realpath(loc->iseq);
- }
- return Qnil;
- default:
- rb_bug("location_realpath: unreachable");
- UNREACHABLE;
}
+ return Qnil;
}
/*
@@ -399,7 +437,7 @@ location_format(VALUE file, int lineno, VALUE name)
rb_str_cat_cstr(s, "unknown method");
}
else {
- rb_str_catf(s, "`%s'", RSTRING_PTR(name));
+ rb_str_catf(s, "'%s'", RSTRING_PTR(name));
}
return s;
}
@@ -407,17 +445,10 @@ location_format(VALUE file, int lineno, VALUE name)
static VALUE
location_to_str(rb_backtrace_location_t *loc)
{
- VALUE file, name;
+ VALUE file, owner = Qnil, name;
int lineno;
- switch (loc->type) {
- case LOCATION_TYPE_ISEQ:
- file = rb_iseq_path(loc->iseq);
- name = ISEQ_BODY(loc->iseq)->location.label;
-
- lineno = calc_lineno(loc->iseq, loc->pc);
- break;
- case LOCATION_TYPE_CFUNC:
+ if (loc->cme && loc->cme->def->type == VM_METHOD_TYPE_CFUNC) {
if (loc->iseq && loc->pc) {
file = rb_iseq_path(loc->iseq);
lineno = calc_lineno(loc->iseq, loc->pc);
@@ -426,10 +457,15 @@ location_to_str(rb_backtrace_location_t *loc)
file = GET_VM()->progname;
lineno = 0;
}
- name = rb_id2str(loc->mid);
- break;
- default:
- rb_bug("location_to_str: unreachable");
+ name = rb_gen_method_name(loc->cme->owner, rb_id2str(loc->cme->def->original_id));
+ }
+ else {
+ file = rb_iseq_path(loc->iseq);
+ lineno = calc_lineno(loc->iseq, loc->pc);
+ if (loc->cme) {
+ owner = loc->cme->owner;
+ }
+ name = calculate_iseq_label(owner, loc->iseq);
}
return location_format(file, lineno, name);
@@ -455,10 +491,10 @@ location_inspect_m(VALUE self)
}
typedef struct rb_backtrace_struct {
- rb_backtrace_location_t *backtrace;
int backtrace_size;
VALUE strary;
VALUE locary;
+ rb_backtrace_location_t backtrace[1];
} rb_backtrace_t;
static void
@@ -475,27 +511,11 @@ backtrace_mark(void *ptr)
}
static void
-backtrace_free(void *ptr)
-{
- rb_backtrace_t *bt = (rb_backtrace_t *)ptr;
- if (bt->backtrace) ruby_xfree(bt->backtrace);
- ruby_xfree(bt);
-}
-
-static void
location_update_entry(rb_backtrace_location_t *fi)
{
- switch (fi->type) {
- case LOCATION_TYPE_ISEQ:
- fi->iseq = (rb_iseq_t*)rb_gc_location((VALUE)fi->iseq);
- break;
- case LOCATION_TYPE_CFUNC:
- if (fi->iseq) {
- fi->iseq = (rb_iseq_t*)rb_gc_location((VALUE)fi->iseq);
- }
- break;
- default:
- break;
+ fi->cme = (rb_callable_method_entry_t *)rb_gc_location((VALUE)fi->cme);
+ if (fi->iseq) {
+ fi->iseq = (rb_iseq_t *)rb_gc_location((VALUE)fi->iseq);
}
}
@@ -512,16 +532,17 @@ backtrace_update(void *ptr)
bt->locary = rb_gc_location(bt->locary);
}
-static size_t
-backtrace_memsize(const void *ptr)
-{
- rb_backtrace_t *bt = (rb_backtrace_t *)ptr;
- return sizeof(rb_backtrace_t) + sizeof(rb_backtrace_location_t) * bt->backtrace_size;
-}
-
static const rb_data_type_t backtrace_data_type = {
"backtrace",
- {backtrace_mark, backtrace_free, backtrace_memsize, backtrace_update},
+ {
+ backtrace_mark,
+ RUBY_DEFAULT_FREE,
+ NULL, // No external memory to report,
+ backtrace_update,
+ },
+ /* Cannot set the RUBY_TYPED_EMBEDDABLE flag because the loc of frame_info
+ * points elements in the backtrace array. This can cause the loc to become
+ * incorrect if this backtrace object is moved by compaction. */
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
};
@@ -539,6 +560,16 @@ backtrace_alloc(VALUE klass)
return obj;
}
+static VALUE
+backtrace_alloc_capa(long num_frames, rb_backtrace_t **backtrace)
+{
+ size_t memsize = offsetof(rb_backtrace_t, backtrace) + num_frames * sizeof(rb_backtrace_location_t);
+ VALUE btobj = rb_data_typed_object_zalloc(rb_cBacktrace, memsize, &backtrace_data_type);
+ TypedData_Get_Struct(btobj, rb_backtrace_t, &backtrace_data_type, *backtrace);
+ return btobj;
+}
+
+
static long
backtrace_size(const rb_execution_context_t *ec)
{
@@ -569,6 +600,13 @@ is_internal_location(const rb_control_frame_t *cfp)
return strncmp(prefix, RSTRING_PTR(file), prefix_len) == 0;
}
+static bool
+is_rescue_or_ensure_frame(const rb_control_frame_t *cfp)
+{
+ enum rb_iseq_type type = ISEQ_BODY(cfp->iseq)->type;
+ return type == ISEQ_TYPE_RESCUE || type == ISEQ_TYPE_ENSURE;
+}
+
static void
bt_update_cfunc_loc(unsigned long cfunc_counter, rb_backtrace_location_t *cfunc_loc, const rb_iseq_t *iseq, const VALUE *pc)
{
@@ -594,11 +632,11 @@ rb_ec_partial_backtrace_object(const rb_execution_context_t *ec, long start_fram
const rb_control_frame_t *cfp = ec->cfp;
const rb_control_frame_t *end_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
ptrdiff_t size;
- rb_backtrace_t *bt;
- VALUE btobj = backtrace_alloc(rb_cBacktrace);
+ rb_backtrace_t *bt = NULL;
+ VALUE btobj = Qnil;
rb_backtrace_location_t *loc = NULL;
unsigned long cfunc_counter = 0;
- GetCoreDataFromValue(btobj, rb_backtrace_t, bt);
+ bool skip_next_frame = FALSE;
// In the case the thread vm_stack or cfp is not initialized, there is no backtrace.
if (end_cfp == NULL) {
@@ -626,7 +664,8 @@ rb_ec_partial_backtrace_object(const rb_execution_context_t *ec, long start_fram
}
}
- bt->backtrace = ZALLOC_N(rb_backtrace_location_t, num_frames);
+ btobj = backtrace_alloc_capa(num_frames, &bt);
+
bt->backtrace_size = 0;
if (num_frames == 0) {
if (start_too_large) *start_too_large = 0;
@@ -639,18 +678,21 @@ rb_ec_partial_backtrace_object(const rb_execution_context_t *ec, long start_fram
if (start_frame > 0) {
start_frame--;
}
- else if (!skip_internal || !is_internal_location(cfp)) {
- const rb_iseq_t *iseq = cfp->iseq;
- const VALUE *pc = cfp->pc;
- loc = &bt->backtrace[bt->backtrace_size++];
- loc->type = LOCATION_TYPE_ISEQ;
- RB_OBJ_WRITE(btobj, &loc->iseq, iseq);
- loc->pc = pc;
- bt_update_cfunc_loc(cfunc_counter, loc-1, iseq, pc);
- if (do_yield) {
- bt_yield_loc(loc - cfunc_counter, cfunc_counter+1, btobj);
+ else if (!(skip_internal && is_internal_location(cfp))) {
+ if (!skip_next_frame) {
+ const rb_iseq_t *iseq = cfp->iseq;
+ const VALUE *pc = cfp->pc;
+ loc = &bt->backtrace[bt->backtrace_size++];
+ RB_OBJ_WRITE(btobj, &loc->cme, rb_vm_frame_method_entry(cfp));
+ RB_OBJ_WRITE(btobj, &loc->iseq, iseq);
+ loc->pc = pc;
+ bt_update_cfunc_loc(cfunc_counter, loc-1, iseq, pc);
+ if (do_yield) {
+ bt_yield_loc(loc - cfunc_counter, cfunc_counter+1, btobj);
+ }
+ cfunc_counter = 0;
}
- cfunc_counter = 0;
+ skip_next_frame = is_rescue_or_ensure_frame(cfp);
}
}
}
@@ -661,18 +703,20 @@ rb_ec_partial_backtrace_object(const rb_execution_context_t *ec, long start_fram
}
else {
loc = &bt->backtrace[bt->backtrace_size++];
- loc->type = LOCATION_TYPE_CFUNC;
+ RB_OBJ_WRITE(btobj, &loc->cme, rb_vm_frame_method_entry(cfp));
loc->iseq = NULL;
loc->pc = NULL;
- loc->mid = rb_vm_frame_method_entry(cfp)->def->original_id;
cfunc_counter++;
}
}
}
+ // When a backtrace entry corresponds to a method defined in C (e.g. rb_define_method), the reported file:line
+ // is the one of the caller Ruby frame, so if the last entry is a C frame we find the caller Ruby frame here.
if (cfunc_counter > 0) {
for (; cfp != end_cfp; cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)) {
- if (cfp->iseq && cfp->pc && (!skip_internal || !is_internal_location(cfp))) {
+ if (cfp->iseq && cfp->pc && !(skip_internal && is_internal_location(cfp))) {
+ VM_ASSERT(!skip_next_frame); // ISEQ_TYPE_RESCUE/ISEQ_TYPE_ENSURE should have a caller Ruby ISEQ, not a cfunc
bt_update_cfunc_loc(cfunc_counter, loc, cfp->iseq, cfp->pc);
RB_OBJ_WRITTEN(btobj, Qundef, cfp->iseq);
if (do_yield) {
@@ -720,7 +764,7 @@ backtrace_to_str_ary(VALUE self)
{
VALUE r;
rb_backtrace_t *bt;
- GetCoreDataFromValue(self, rb_backtrace_t, bt);
+ TypedData_Get_Struct(self, rb_backtrace_t, &backtrace_data_type, bt);
r = backtrace_collect(bt, location_to_str_dmyarg, 0);
RB_GC_GUARD(self);
return r;
@@ -730,7 +774,7 @@ VALUE
rb_backtrace_to_str_ary(VALUE self)
{
rb_backtrace_t *bt;
- GetCoreDataFromValue(self, rb_backtrace_t, bt);
+ TypedData_Get_Struct(self, rb_backtrace_t, &backtrace_data_type, bt);
if (!bt->strary) {
RB_OBJ_WRITE(self, &bt->strary, backtrace_to_str_ary(self));
@@ -741,15 +785,15 @@ rb_backtrace_to_str_ary(VALUE self)
void
rb_backtrace_use_iseq_first_lineno_for_last_location(VALUE self)
{
- const rb_backtrace_t *bt;
+ rb_backtrace_t *bt;
rb_backtrace_location_t *loc;
- GetCoreDataFromValue(self, rb_backtrace_t, bt);
+ TypedData_Get_Struct(self, rb_backtrace_t, &backtrace_data_type, bt);
VM_ASSERT(bt->backtrace_size > 0);
loc = &bt->backtrace[0];
- VM_ASSERT(loc->type == LOCATION_TYPE_ISEQ);
+ VM_ASSERT(!loc->cme || loc->cme->def->type == VM_METHOD_TYPE_ISEQ);
loc->pc = NULL; // means location.first_lineno
}
@@ -772,7 +816,7 @@ backtrace_to_location_ary(VALUE self)
{
VALUE r;
rb_backtrace_t *bt;
- GetCoreDataFromValue(self, rb_backtrace_t, bt);
+ TypedData_Get_Struct(self, rb_backtrace_t, &backtrace_data_type, bt);
r = backtrace_collect(bt, location_create, (void *)self);
RB_GC_GUARD(self);
return r;
@@ -782,7 +826,7 @@ VALUE
rb_backtrace_to_location_ary(VALUE self)
{
rb_backtrace_t *bt;
- GetCoreDataFromValue(self, rb_backtrace_t, bt);
+ TypedData_Get_Struct(self, rb_backtrace_t, &backtrace_data_type, bt);
if (!bt->locary) {
RB_OBJ_WRITE(self, &bt->locary, backtrace_to_location_ary(self));
@@ -790,6 +834,40 @@ rb_backtrace_to_location_ary(VALUE self)
return bt->locary;
}
+VALUE
+rb_location_ary_to_backtrace(VALUE ary)
+{
+ if (!RB_TYPE_P(ary, T_ARRAY) || !rb_frame_info_p(RARRAY_AREF(ary, 0))) {
+ return Qfalse;
+ }
+
+ rb_backtrace_t *new_backtrace;
+ long num_frames = RARRAY_LEN(ary);
+ VALUE btobj = backtrace_alloc_capa(num_frames, &new_backtrace);
+
+ for (long index = 0; index < RARRAY_LEN(ary); index++) {
+ VALUE locobj = RARRAY_AREF(ary, index);
+
+ if (!rb_frame_info_p(locobj)) {
+ return Qfalse;
+ }
+
+ struct valued_frame_info *src_vloc;
+ TypedData_Get_Struct(locobj, struct valued_frame_info, &location_data_type, src_vloc);
+
+ rb_backtrace_location_t *dst_location = &new_backtrace->backtrace[index];
+ RB_OBJ_WRITE(btobj, &dst_location->cme, src_vloc->loc->cme);
+ RB_OBJ_WRITE(btobj, &dst_location->iseq, src_vloc->loc->iseq);
+ dst_location->pc = src_vloc->loc->pc;
+
+ new_backtrace->backtrace_size++;
+
+ RB_GC_GUARD(locobj);
+ }
+
+ return btobj;
+}
+
static VALUE
backtrace_dump_data(VALUE self)
{
@@ -801,7 +879,7 @@ static VALUE
backtrace_load_data(VALUE self, VALUE str)
{
rb_backtrace_t *bt;
- GetCoreDataFromValue(self, rb_backtrace_t, bt);
+ TypedData_Get_Struct(self, rb_backtrace_t, &backtrace_data_type, bt);
RB_OBJ_WRITE(self, &bt->strary, str);
return self;
}
@@ -984,7 +1062,7 @@ oldbt_print(void *data, VALUE file, int lineno, VALUE name)
RSTRING_PTR(file), lineno);
}
else {
- fprintf(fp, "\tfrom %s:%d:in `%s'\n",
+ fprintf(fp, "\tfrom %s:%d:in '%s'\n",
RSTRING_PTR(file), lineno, RSTRING_PTR(name));
}
}
@@ -1003,31 +1081,38 @@ vm_backtrace_print(FILE *fp)
&arg);
}
+struct oldbt_bugreport_arg {
+ FILE *fp;
+ int count;
+};
+
static void
oldbt_bugreport(void *arg, VALUE file, int line, VALUE method)
{
+ struct oldbt_bugreport_arg *p = arg;
+ FILE *fp = p->fp;
const char *filename = NIL_P(file) ? "ruby" : RSTRING_PTR(file);
- if (!*(int *)arg) {
- fprintf(stderr, "-- Ruby level backtrace information "
+ if (!p->count) {
+ fprintf(fp, "-- Ruby level backtrace information "
"----------------------------------------\n");
- *(int *)arg = 1;
+ p->count = 1;
}
if (NIL_P(method)) {
- fprintf(stderr, "%s:%d:in unknown method\n", filename, line);
+ fprintf(fp, "%s:%d:in unknown method\n", filename, line);
}
else {
- fprintf(stderr, "%s:%d:in `%s'\n", filename, line, RSTRING_PTR(method));
+ fprintf(fp, "%s:%d:in '%s'\n", filename, line, RSTRING_PTR(method));
}
}
void
-rb_backtrace_print_as_bugreport(void)
+rb_backtrace_print_as_bugreport(FILE *fp)
{
struct oldbt_arg arg;
- int i = 0;
+ struct oldbt_bugreport_arg barg = {fp, 0};
arg.func = oldbt_bugreport;
- arg.data = (int *)&i;
+ arg.data = &barg;
backtrace_each(GET_EC(),
oldbt_init,
@@ -1057,7 +1142,7 @@ oldbt_print_to(void *data, VALUE file, int lineno, VALUE name)
rb_str_cat2(str, "unknown method\n");
}
else {
- rb_str_catf(str, " `%"PRIsVALUE"'\n", name);
+ rb_str_catf(str, " '%"PRIsVALUE"'\n", name);
}
(*arg->iter)(arg->output, str);
}
@@ -1085,17 +1170,17 @@ rb_make_backtrace(void)
return rb_ec_backtrace_str_ary(GET_EC(), BACKTRACE_START, ALL_BACKTRACE_LINES);
}
-static VALUE
-ec_backtrace_to_ary(const rb_execution_context_t *ec, int argc, const VALUE *argv, int lev_default, int lev_plus, int to_str)
+static long
+ec_backtrace_range(const rb_execution_context_t *ec, int argc, const VALUE *argv, int lev_default, int lev_plus, long *len_ptr)
{
- VALUE level, vn;
+ VALUE level, vn, opts;
long lev, n;
- VALUE btval;
- VALUE r;
- int too_large;
- rb_scan_args(argc, argv, "02", &level, &vn);
+ rb_scan_args(argc, argv, "02:", &level, &vn, &opts);
+ if (!NIL_P(opts)) {
+ rb_get_kwargs(opts, (ID []){0}, 0, 0, NULL);
+ }
if (argc == 2 && NIL_P(vn)) argc--;
switch (argc) {
@@ -1116,7 +1201,7 @@ ec_backtrace_to_ary(const rb_execution_context_t *ec, int argc, const VALUE *arg
n = ALL_BACKTRACE_LINES;
break;
case Qnil:
- return Qnil;
+ return -1;
default:
lev = beg + lev_plus;
n = len;
@@ -1140,6 +1225,20 @@ ec_backtrace_to_ary(const rb_execution_context_t *ec, int argc, const VALUE *arg
break;
}
+ *len_ptr = n;
+ return lev;
+}
+
+static VALUE
+ec_backtrace_to_ary(const rb_execution_context_t *ec, int argc, const VALUE *argv, int lev_default, int lev_plus, int to_str)
+{
+ long lev, n;
+ VALUE btval, r;
+ int too_large;
+
+ lev = ec_backtrace_range(ec, argc, argv, lev_default, lev_plus, &n);
+ if (lev < 0) return Qnil;
+
if (n == 0) {
return rb_ary_new();
}
@@ -1269,15 +1368,19 @@ rb_f_caller_locations(int argc, VALUE *argv, VALUE _)
/*
* call-seq:
- * Thread.each_caller_location{ |loc| ... } -> nil
+ * Thread.each_caller_location(...) { |loc| ... } -> nil
*
* Yields each frame of the current execution stack as a
* backtrace location object.
*/
static VALUE
-each_caller_location(VALUE unused)
+each_caller_location(int argc, VALUE *argv, VALUE _)
{
- rb_ec_partial_backtrace_object(GET_EC(), 2, ALL_BACKTRACE_LINES, NULL, FALSE, TRUE);
+ rb_execution_context_t *ec = GET_EC();
+ long n, lev = ec_backtrace_range(ec, argc, argv, 1, 1, &n);
+ if (lev >= 0 && n != 0) {
+ rb_ec_partial_backtrace_object(ec, lev, n, NULL, FALSE, TRUE);
+ }
return Qnil;
}
@@ -1357,7 +1460,7 @@ Init_vm_backtrace(void)
rb_define_global_function("caller", rb_f_caller, -1);
rb_define_global_function("caller_locations", rb_f_caller_locations, -1);
- rb_define_singleton_method(rb_cThread, "each_caller_location", each_caller_location, 0);
+ rb_define_singleton_method(rb_cThread, "each_caller_location", each_caller_location, -1);
}
/* debugger API */
@@ -1577,12 +1680,12 @@ rb_debug_inspector_backtrace_locations(const rb_debug_inspector_t *dc)
return dc->backtrace;
}
-int
-rb_profile_frames(int start, int limit, VALUE *buff, int *lines)
+static int
+thread_profile_frames(rb_execution_context_t *ec, int start, int limit, VALUE *buff, int *lines)
{
int i;
- const rb_execution_context_t *ec = GET_EC();
const rb_control_frame_t *cfp = ec->cfp, *end_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
+ const rb_control_frame_t *top = cfp;
const rb_callable_method_entry_t *cme;
// If this function is called inside a thread after thread creation, but
@@ -1597,7 +1700,7 @@ rb_profile_frames(int start, int limit, VALUE *buff, int *lines)
// Skip dummy frame; see `rb_ec_partial_backtrace_object` for details
end_cfp = RUBY_VM_NEXT_CONTROL_FRAME(end_cfp);
- for (i=0; i<limit && cfp != end_cfp;) {
+ for (i=0; i<limit && cfp != end_cfp; cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)) {
if (VM_FRAME_RUBYFRAME_P(cfp) && cfp->pc != 0) {
if (start > 0) {
start--;
@@ -1613,24 +1716,59 @@ rb_profile_frames(int start, int limit, VALUE *buff, int *lines)
buff[i] = (VALUE)cfp->iseq;
}
- if (lines) lines[i] = calc_lineno(cfp->iseq, cfp->pc);
+ if (lines) {
+ // The topmost frame may not have an updated PC because the JIT
+ // may not have set one. The JIT compiler will update the PC
+ // before entering a new function (so that `caller` will work),
+ // so only the topmost frame could possibly have an out of date PC
+ if (cfp == top && cfp->jit_return) {
+ lines[i] = 0;
+ }
+ else {
+ lines[i] = calc_lineno(cfp->iseq, cfp->pc);
+ }
+ }
i++;
}
else {
cme = rb_vm_frame_method_entry(cfp);
if (cme && cme->def->type == VM_METHOD_TYPE_CFUNC) {
+ if (start > 0) {
+ start--;
+ continue;
+ }
buff[i] = (VALUE)cme;
if (lines) lines[i] = 0;
i++;
}
}
- cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
}
return i;
}
+int
+rb_profile_frames(int start, int limit, VALUE *buff, int *lines)
+{
+ rb_execution_context_t *ec = rb_current_execution_context(false);
+
+ // If there is no EC, we may be attempting to profile a non-Ruby thread or a
+ // M:N shared native thread which has no active Ruby thread.
+ if (!ec) {
+ return 0;
+ }
+
+ return thread_profile_frames(ec, start, limit, buff, lines);
+}
+
+int
+rb_profile_thread_frames(VALUE thread, int start, int limit, VALUE *buff, int *lines)
+{
+ rb_thread_t *th = rb_thread_ptr(thread);
+ return thread_profile_frames(th->ec, start, limit, buff, lines);
+}
+
static const rb_iseq_t *
frame2iseq(VALUE frame)
{
@@ -1696,7 +1834,7 @@ rb_profile_frame_absolute_path(VALUE frame)
static VALUE cfunc_str = Qfalse;
if (!cfunc_str) {
cfunc_str = rb_str_new_literal("<cfunc>");
- rb_gc_register_mark_object(cfunc_str);
+ rb_vm_register_global_object(cfunc_str);
}
return cfunc_str;
}
@@ -1749,7 +1887,7 @@ rb_profile_frame_classpath(VALUE frame)
if (RB_TYPE_P(klass, T_ICLASS)) {
klass = RBASIC(klass)->klass;
}
- else if (FL_TEST(klass, FL_SINGLETON)) {
+ else if (RCLASS_SINGLETON_P(klass)) {
klass = RCLASS_ATTACHED_OBJECT(klass);
if (!RB_TYPE_P(klass, T_CLASS) && !RB_TYPE_P(klass, T_MODULE))
return rb_sprintf("#<%s:%p>", rb_class2name(rb_obj_class(klass)), (void*)klass);
@@ -1766,7 +1904,7 @@ rb_profile_frame_singleton_method_p(VALUE frame)
{
VALUE klass = frame2klass(frame);
- return RBOOL(klass && !NIL_P(klass) && FL_TEST(klass, FL_SINGLETON));
+ return RBOOL(klass && !NIL_P(klass) && RCLASS_SINGLETON_P(klass));
}
VALUE
diff --git a/vm_callinfo.h b/vm_callinfo.h
index ccdf720cfc..71ab9fe3fa 100644
--- a/vm_callinfo.h
+++ b/vm_callinfo.h
@@ -25,6 +25,7 @@ enum vm_call_flag_bits {
VM_CALL_ZSUPER_bit, // zsuper
VM_CALL_OPT_SEND_bit, // internal flag
VM_CALL_KW_SPLAT_MUT_bit, // kw splat hash can be modified (to avoid allocating a new one)
+ VM_CALL_ARGS_SPLAT_MUT_bit, // args splat can be modified (to avoid allocating a new one)
VM_CALL__END
};
@@ -40,9 +41,11 @@ enum vm_call_flag_bits {
#define VM_CALL_ZSUPER (0x01 << VM_CALL_ZSUPER_bit)
#define VM_CALL_OPT_SEND (0x01 << VM_CALL_OPT_SEND_bit)
#define VM_CALL_KW_SPLAT_MUT (0x01 << VM_CALL_KW_SPLAT_MUT_bit)
+#define VM_CALL_ARGS_SPLAT_MUT (0x01 << VM_CALL_ARGS_SPLAT_MUT_bit)
struct rb_callinfo_kwarg {
int keyword_len;
+ int references;
VALUE keywords[];
};
@@ -65,8 +68,12 @@ struct rb_callinfo {
VALUE argc;
};
-#ifndef USE_EMBED_CI
+#if !defined(USE_EMBED_CI) || (USE_EMBED_CI+0)
+#undef USE_EMBED_CI
#define USE_EMBED_CI 1
+#else
+#undef USE_EMBED_CI
+#define USE_EMBED_CI 0
#endif
#if SIZEOF_VALUE == 8
@@ -96,7 +103,9 @@ struct rb_callinfo {
static inline bool
vm_ci_packed_p(const struct rb_callinfo *ci)
{
-#if USE_EMBED_CI
+ if (!USE_EMBED_CI) {
+ return 0;
+ }
if (LIKELY(((VALUE)ci) & 0x01)) {
return 1;
}
@@ -104,9 +113,6 @@ vm_ci_packed_p(const struct rb_callinfo *ci)
VM_ASSERT(IMEMO_TYPE_P(ci, imemo_callinfo));
return 0;
}
-#else
- return 0;
-#endif
}
static inline bool
@@ -193,26 +199,23 @@ vm_ci_dump(const struct rb_callinfo *ci)
(((VALUE)(argc)) << CI_EMBED_ARGC_SHFT) | \
RUBY_FIXNUM_FLAG))
+// vm_method.c
+const struct rb_callinfo *rb_vm_ci_lookup(ID mid, unsigned int flag, unsigned int argc, const struct rb_callinfo_kwarg *kwarg);
+void rb_vm_ci_free(const struct rb_callinfo *);
+
static inline const struct rb_callinfo *
vm_ci_new_(ID mid, unsigned int flag, unsigned int argc, const struct rb_callinfo_kwarg *kwarg, const char *file, int line)
{
-#if USE_EMBED_CI
- if (VM_CI_EMBEDDABLE_P(mid, flag, argc, kwarg)) {
+ if (USE_EMBED_CI && VM_CI_EMBEDDABLE_P(mid, flag, argc, kwarg)) {
RB_DEBUG_COUNTER_INC(ci_packed);
return vm_ci_new_id(mid, flag, argc, kwarg);
}
-#endif
const bool debug = 0;
if (debug) ruby_debug_printf("%s:%d ", file, line);
- // TODO: dedup
- const struct rb_callinfo *ci = (const struct rb_callinfo *)
- rb_imemo_new(imemo_callinfo,
- (VALUE)mid,
- (VALUE)flag,
- (VALUE)argc,
- (VALUE)kwarg);
+ const struct rb_callinfo *ci = rb_vm_ci_lookup(mid, flag, argc, kwarg);
+
if (debug) rp(ci);
if (kwarg) {
RB_DEBUG_COUNTER_INC(ci_kw);
@@ -237,21 +240,6 @@ vm_ci_new_runtime_(ID mid, unsigned int flag, unsigned int argc, const struct rb
#define VM_CALLINFO_NOT_UNDER_GC IMEMO_FL_USER0
-static inline bool
-vm_ci_markable(const struct rb_callinfo *ci)
-{
- if (! ci) {
- return false; /* or true? This is Qfalse... */
- }
- else if (vm_ci_packed_p(ci)) {
- return true;
- }
- else {
- VM_ASSERT(IMEMO_TYPE_P(ci, imemo_callinfo));
- return ! FL_ANY_RAW((VALUE)ci, VM_CALLINFO_NOT_UNDER_GC);
- }
-}
-
#define VM_CI_ON_STACK(mid_, flags_, argc_, kwarg_) \
(struct rb_callinfo) { \
.flags = T_IMEMO | \
@@ -295,6 +283,18 @@ struct rb_callcache {
#define VM_CALLCACHE_UNMARKABLE FL_FREEZE
#define VM_CALLCACHE_ON_STACK FL_EXIVAR
+/* VM_CALLCACHE_IVAR used for IVAR/ATTRSET/STRUCT_AREF/STRUCT_ASET methods */
+#define VM_CALLCACHE_IVAR IMEMO_FL_USER0
+#define VM_CALLCACHE_BF IMEMO_FL_USER1
+#define VM_CALLCACHE_SUPER IMEMO_FL_USER2
+#define VM_CALLCACHE_REFINEMENT IMEMO_FL_USER3
+
+enum vm_cc_type {
+ cc_type_normal, // chained from ccs
+ cc_type_super,
+ cc_type_refinement,
+};
+
extern const struct rb_callcache *rb_vm_empty_cc(void);
extern const struct rb_callcache *rb_vm_empty_cc_for_super(void);
@@ -311,14 +311,41 @@ vm_cc_attr_index_initialize(const struct rb_callcache *cc, shape_id_t shape_id)
static inline const struct rb_callcache *
vm_cc_new(VALUE klass,
const struct rb_callable_method_entry_struct *cme,
- vm_call_handler call)
-{
- const struct rb_callcache *cc = (const struct rb_callcache *)rb_imemo_new(imemo_callcache, (VALUE)cme, (VALUE)call, 0, klass);
+ vm_call_handler call,
+ enum vm_cc_type type)
+{
+ struct rb_callcache *cc = IMEMO_NEW(struct rb_callcache, imemo_callcache, klass);
+ *((struct rb_callable_method_entry_struct **)&cc->cme_) = (struct rb_callable_method_entry_struct *)cme;
+ *((vm_call_handler *)&cc->call_) = call;
+
+ switch (type) {
+ case cc_type_normal:
+ break;
+ case cc_type_super:
+ *(VALUE *)&cc->flags |= VM_CALLCACHE_SUPER;
+ break;
+ case cc_type_refinement:
+ *(VALUE *)&cc->flags |= VM_CALLCACHE_REFINEMENT;
+ break;
+ }
+
vm_cc_attr_index_initialize(cc, INVALID_SHAPE_ID);
RB_DEBUG_COUNTER_INC(cc_new);
return cc;
}
+static inline bool
+vm_cc_super_p(const struct rb_callcache *cc)
+{
+ return (cc->flags & VM_CALLCACHE_SUPER) != 0;
+}
+
+static inline bool
+vm_cc_refinement_p(const struct rb_callcache *cc)
+{
+ return (cc->flags & VM_CALLCACHE_REFINEMENT) != 0;
+}
+
#define VM_CC_ON_STACK(clazz, call, aux, cme) \
(struct rb_callcache) { \
.flags = T_IMEMO | \
@@ -438,9 +465,6 @@ vm_cc_valid_p(const struct rb_callcache *cc, const rb_callable_method_entry_t *c
/* callcache: mutate */
-#define VM_CALLCACH_IVAR IMEMO_FL_USER0
-#define VM_CALLCACH_BF IMEMO_FL_USER1
-
static inline void
vm_cc_call_set(const struct rb_callcache *cc, vm_call_handler call)
{
@@ -450,6 +474,12 @@ vm_cc_call_set(const struct rb_callcache *cc, vm_call_handler call)
}
static inline void
+set_vm_cc_ivar(const struct rb_callcache *cc)
+{
+ *(VALUE *)&cc->flags |= VM_CALLCACHE_IVAR;
+}
+
+static inline void
vm_cc_attr_index_set(const struct rb_callcache *cc, attr_index_t index, shape_id_t dest_shape_id)
{
uintptr_t *attr_value = (uintptr_t *)&cc->aux_.attr.value;
@@ -460,13 +490,13 @@ vm_cc_attr_index_set(const struct rb_callcache *cc, attr_index_t index, shape_id
VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache));
VM_ASSERT(cc != vm_cc_empty());
*attr_value = (attr_index_t)(index + 1) | ((uintptr_t)(dest_shape_id) << SHAPE_FLAG_SHIFT);
- *(VALUE *)&cc->flags |= VM_CALLCACH_IVAR;
+ set_vm_cc_ivar(cc);
}
static inline bool
vm_cc_ivar_p(const struct rb_callcache *cc)
{
- return (cc->flags & VM_CALLCACH_IVAR) != 0;
+ return (cc->flags & VM_CALLCACHE_IVAR) != 0;
}
static inline void
@@ -495,13 +525,13 @@ vm_cc_bf_set(const struct rb_callcache *cc, const struct rb_builtin_function *bf
VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache));
VM_ASSERT(cc != vm_cc_empty());
*(const struct rb_builtin_function **)&cc->aux_.bf = bf;
- *(VALUE *)&cc->flags |= VM_CALLCACH_BF;
+ *(VALUE *)&cc->flags |= VM_CALLCACHE_BF;
}
static inline bool
vm_cc_bf_p(const struct rb_callcache *cc)
{
- return (cc->flags & VM_CALLCACH_BF) != 0;
+ return (cc->flags & VM_CALLCACHE_BF) != 0;
}
static inline void
@@ -530,7 +560,8 @@ struct rb_class_cc_entries {
int len;
const struct rb_callable_method_entry_struct *cme;
struct rb_class_cc_entries_entry {
- const struct rb_callinfo *ci;
+ unsigned int argc;
+ unsigned int flag;
const struct rb_callcache *cc;
} *entries;
};
diff --git a/vm_core.h b/vm_core.h
index f0f393e497..69b7899ec4 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -56,12 +56,12 @@
#define RVALUE_SIZE (sizeof(struct RBasic) + sizeof(VALUE[RBIMPL_RVALUE_EMBED_LEN_MAX]))
#if VM_CHECK_MODE > 0
-#define VM_ASSERT(expr) RUBY_ASSERT_MESG_WHEN(VM_CHECK_MODE > 0, expr, #expr)
+#define VM_ASSERT(/*expr, */...) RUBY_ASSERT_WHEN(VM_CHECK_MODE > 0, __VA_ARGS__)
#define VM_UNREACHABLE(func) rb_bug(#func ": unreachable")
#define RUBY_ASSERT_CRITICAL_SECTION
#define RUBY_DEBUG_THREAD_SCHEDULE() rb_thread_schedule()
#else
-#define VM_ASSERT(expr) ((void)0)
+#define VM_ASSERT(/*expr, */...) ((void)0)
#define VM_UNREACHABLE(func) UNREACHABLE
#define RUBY_DEBUG_THREAD_SCHEDULE()
#endif
@@ -94,6 +94,7 @@ extern int ruby_assert_critical_section_entered;
#include "internal.h"
#include "internal/array.h"
#include "internal/basic_operators.h"
+#include "internal/sanitizers.h"
#include "internal/serial.h"
#include "internal/vm.h"
#include "method.h"
@@ -105,6 +106,14 @@ extern int ruby_assert_critical_section_entered;
#include "ruby/thread_native.h"
+#if USE_SHARED_GC
+typedef struct gc_function_map {
+ void *(*objspace_alloc)(void);
+} rb_gc_function_map_t;
+
+#define rb_gc_functions (&GET_VM()->gc_functions_map)
+#endif
+
/*
* implementation selector of get_insn_info algorithm
* 0: linear search
@@ -182,9 +191,6 @@ void *rb_register_sigaltstack(void *);
#if OPT_DIRECT_THREADED_CODE
#undef OPT_DIRECT_THREADED_CODE
#endif /* OPT_DIRECT_THREADED_CODE */
-#if OPT_STACK_CACHING
-#undef OPT_STACK_CACHING
-#endif /* OPT_STACK_CACHING */
#endif /* OPT_CALL_THREADED_CODE */
void rb_vm_encoded_insn_data_table_init(void);
@@ -281,7 +287,7 @@ union iseq_inline_storage_entry {
};
struct rb_calling_info {
- const struct rb_callinfo *ci;
+ const struct rb_call_data *cd;
const struct rb_callcache *cc;
VALUE block_handler;
VALUE recv;
@@ -368,10 +374,10 @@ enum rb_iseq_type {
enum rb_builtin_attr {
// The iseq does not call methods.
BUILTIN_ATTR_LEAF = 0x01,
- // The iseq does not allocate objects.
- BUILTIN_ATTR_NO_GC = 0x02,
// This iseq only contains single `opt_invokebuiltin_delegate_leave` instruction with 0 arguments.
- BUILTIN_ATTR_SINGLE_NOARG_INLINE = 0x04,
+ BUILTIN_ATTR_SINGLE_NOARG_LEAF = 0x02,
+ // This attribute signals JIT to duplicate the iseq for each block iseq so that its `yield` will be monomorphic.
+ BUILTIN_ATTR_INLINE_BLOCK = 0x04,
};
typedef VALUE (*rb_jit_func_t)(struct rb_execution_context_struct *, struct rb_control_frame_struct *);
@@ -418,6 +424,9 @@ struct rb_iseq_constant_body {
unsigned int ambiguous_param0 : 1; /* {|a|} */
unsigned int accepts_no_kwarg : 1;
unsigned int ruby2_keywords: 1;
+ unsigned int anon_rest: 1;
+ unsigned int anon_kwrest: 1;
+ unsigned int use_block: 1;
} flags;
unsigned int size;
@@ -496,6 +505,8 @@ struct rb_iseq_constant_body {
unsigned int builtin_attrs; // Union of rb_builtin_attr
+ bool prism; // ISEQ was generated from prism compiler
+
union {
iseq_bits_t * list; /* Find references for GC */
iseq_bits_t single;
@@ -506,10 +517,17 @@ struct rb_iseq_constant_body {
const rb_iseq_t *mandatory_only_iseq;
#if USE_RJIT || USE_YJIT
- // Function pointer for JIT code
- rb_jit_func_t jit_func;
- // Number of total calls with jit_exec()
- long unsigned total_calls;
+ // Function pointer for JIT code on jit_exec()
+ rb_jit_func_t jit_entry;
+ // Number of calls on jit_exec()
+ long unsigned jit_entry_calls;
+#endif
+
+#if USE_YJIT
+ // Function pointer for JIT code on jit_exec_exception()
+ rb_jit_func_t jit_exception;
+ // Number of calls on jit_exec_exception()
+ long unsigned jit_exception_calls;
#endif
#if USE_RJIT
@@ -520,6 +538,8 @@ struct rb_iseq_constant_body {
#if USE_YJIT
// YJIT stores some data on each iseq.
void *yjit_payload;
+ // Used to estimate how frequently this ISEQ gets called
+ uint64_t yjit_calls_at_interv;
#endif
};
@@ -548,22 +568,21 @@ struct rb_iseq_struct {
#define ISEQ_BODY(iseq) ((iseq)->body)
-#ifndef USE_LAZY_LOAD
+#if !defined(USE_LAZY_LOAD) || !(USE_LAZY_LOAD+0)
#define USE_LAZY_LOAD 0
#endif
-#if USE_LAZY_LOAD
-const rb_iseq_t *rb_iseq_complete(const rb_iseq_t *iseq);
+#if !USE_LAZY_LOAD
+static inline const rb_iseq_t *rb_iseq_complete(const rb_iseq_t *iseq) {return 0;}
#endif
+const rb_iseq_t *rb_iseq_complete(const rb_iseq_t *iseq);
static inline const rb_iseq_t *
rb_iseq_check(const rb_iseq_t *iseq)
{
-#if USE_LAZY_LOAD
- if (ISEQ_BODY(iseq) == NULL) {
+ if (USE_LAZY_LOAD && ISEQ_BODY(iseq) == NULL) {
rb_iseq_complete((rb_iseq_t *)iseq);
}
-#endif
return iseq;
}
@@ -614,6 +633,11 @@ typedef struct rb_hook_list_struct {
// see builtin.h for definition
typedef const struct rb_builtin_function *RB_BUILTIN;
+struct global_object_list {
+ VALUE *varptr;
+ struct global_object_list *next;
+};
+
typedef struct rb_vm_struct {
VALUE self;
@@ -631,15 +655,51 @@ typedef struct rb_vm_struct {
struct rb_ractor_struct *lock_owner;
unsigned int lock_rec;
- // barrier
- bool barrier_waiting;
- unsigned int barrier_cnt;
- rb_nativethread_cond_t barrier_cond;
-
// join at exit
rb_nativethread_cond_t terminate_cond;
bool terminate_waiting;
+
+#ifndef RUBY_THREAD_PTHREAD_H
+ bool barrier_waiting;
+ unsigned int barrier_cnt;
+ rb_nativethread_cond_t barrier_cond;
+#endif
} sync;
+
+ // ractor scheduling
+ struct {
+ rb_nativethread_lock_t lock;
+ struct rb_ractor_struct *lock_owner;
+ bool locked;
+
+ rb_nativethread_cond_t cond; // GRQ
+ unsigned int snt_cnt; // count of shared NTs
+ unsigned int dnt_cnt; // count of dedicated NTs
+
+ unsigned int running_cnt;
+
+ unsigned int max_cpu;
+ struct ccan_list_head grq; // // Global Ready Queue
+ unsigned int grq_cnt;
+
+ // running threads
+ struct ccan_list_head running_threads;
+
+ // threads which switch context by timeslice
+ struct ccan_list_head timeslice_threads;
+
+ struct ccan_list_head zombie_threads;
+
+ // true if timeslice timer is not enable
+ bool timeslice_wait_inf;
+
+ // barrier
+ rb_nativethread_cond_t barrier_complete_cond;
+ rb_nativethread_cond_t barrier_release_cond;
+ bool barrier_waiting;
+ unsigned int barrier_waiting_cnt;
+ unsigned int barrier_serial;
+ } sched;
} ractor;
#ifdef USE_SIGALTSTACK
@@ -659,6 +719,7 @@ typedef struct rb_vm_struct {
/* object management */
VALUE mark_object_ary;
+ struct global_object_list *global_object_list;
const VALUE special_exceptions[ruby_special_error_count];
/* load */
@@ -685,9 +746,8 @@ typedef struct rb_vm_struct {
/* relation table of ensure - rollback for callcc */
struct st_table *ensure_rollback_table;
- /* postponed_job (async-signal-safe, NOT thread-safe) */
- struct rb_postponed_job_struct *postponed_job_buffer;
- rb_atomic_t postponed_job_index;
+ /* postponed_job (async-signal-safe, and thread-safe) */
+ struct rb_postponed_job_queue *postponed_job_queue;
int src_encoding_index;
@@ -699,19 +759,22 @@ typedef struct rb_vm_struct {
VALUE coverages, me2counter;
int coverage_mode;
- st_table * defined_module_hash;
-
struct rb_objspace *objspace;
+#if USE_SHARED_GC
+ rb_gc_function_map_t gc_functions_map;
+#endif
rb_at_exit_list *at_exit;
st_table *frozen_strings;
const struct rb_builtin_function *builtin_function_table;
- int builtin_inline_index;
+ st_table *ci_table;
struct rb_id_table *negative_cme_table;
st_table *overloaded_cme_table; // cme -> overloaded_cme
+ st_table *unused_block_warning_table;
+ bool unused_block_warning_strict;
// This id table contains a mapping from ID to ICs. It does this with ID
// keys and nested st_tables as values. The nested tables have ICs as keys
@@ -810,19 +873,16 @@ struct rb_block {
};
typedef struct rb_control_frame_struct {
- const VALUE *pc; /* cfp[0] */
- VALUE *sp; /* cfp[1] */
- const rb_iseq_t *iseq; /* cfp[2] */
- VALUE self; /* cfp[3] / block[0] */
- const VALUE *ep; /* cfp[4] / block[1] */
- const void *block_code; /* cfp[5] / block[2] */ /* iseq or ifunc or forwarded block handler */
- VALUE *__bp__; /* cfp[6] */ /* outside vm_push_frame, use vm_base_ptr instead. */
-
+ const VALUE *pc; // cfp[0]
+ VALUE *sp; // cfp[1]
+ const rb_iseq_t *iseq; // cfp[2]
+ VALUE self; // cfp[3] / block[0]
+ const VALUE *ep; // cfp[4] / block[1]
+ const void *block_code; // cfp[5] / block[2] -- iseq, ifunc, or forwarded block handler
+ void *jit_return; // cfp[6] -- return address for JIT code
#if VM_DEBUG_BP_CHECK
- VALUE *bp_check; /* cfp[7] */
+ VALUE *bp_check; // cfp[7]
#endif
- // Return address for YJIT code
- void *jit_return;
} rb_control_frame_t;
extern const rb_data_type_t ruby_threadptr_data_type;
@@ -847,13 +907,68 @@ typedef void *rb_jmpbuf_t[5];
#endif
/*
+ `rb_vm_tag_jmpbuf_t` type represents a buffer used to
+ long jump to a C frame associated with `rb_vm_tag`.
+
+ Use-site of `rb_vm_tag_jmpbuf_t` is responsible for calling the
+ following functions:
+ - `rb_vm_tag_jmpbuf_init` once `rb_vm_tag_jmpbuf_t` is allocated.
+ - `rb_vm_tag_jmpbuf_deinit` once `rb_vm_tag_jmpbuf_t` is no longer necessary.
+
+ `RB_VM_TAG_JMPBUF_GET` transforms a `rb_vm_tag_jmpbuf_t` into a
+ `rb_jmpbuf_t` to be passed to `rb_setjmp/rb_longjmp`.
+*/
+#if defined(__wasm__) && !defined(__EMSCRIPTEN__)
+/*
+ WebAssembly target with Asyncify-based SJLJ needs
+ to capture the execution context by unwind/rewind-ing
+ call frames into a jump buffer. The buffer space tends
+ to be considerably large unlike other architectures'
+ register-based buffers.
+ Therefore, we allocates the buffer on the heap on such
+ environments.
+*/
+typedef rb_jmpbuf_t *rb_vm_tag_jmpbuf_t;
+
+#define RB_VM_TAG_JMPBUF_GET(buf) (*buf)
+
+static inline void
+rb_vm_tag_jmpbuf_init(rb_vm_tag_jmpbuf_t *jmpbuf)
+{
+ *jmpbuf = ruby_xmalloc(sizeof(rb_jmpbuf_t));
+}
+
+static inline void
+rb_vm_tag_jmpbuf_deinit(const rb_vm_tag_jmpbuf_t *jmpbuf)
+{
+ ruby_xfree(*jmpbuf);
+}
+#else
+typedef rb_jmpbuf_t rb_vm_tag_jmpbuf_t;
+
+#define RB_VM_TAG_JMPBUF_GET(buf) (buf)
+
+static inline void
+rb_vm_tag_jmpbuf_init(rb_vm_tag_jmpbuf_t *jmpbuf)
+{
+ // no-op
+}
+
+static inline void
+rb_vm_tag_jmpbuf_deinit(const rb_vm_tag_jmpbuf_t *jmpbuf)
+{
+ // no-op
+}
+#endif
+
+/*
the members which are written in EC_PUSH_TAG() should be placed at
the beginning and the end, so that entire region is accessible.
*/
struct rb_vm_tag {
VALUE tag;
VALUE retval;
- rb_jmpbuf_t buf;
+ rb_vm_tag_jmpbuf_t buf;
struct rb_vm_tag *prev;
enum ruby_tag_type state;
unsigned int lock_rec;
@@ -861,7 +976,7 @@ struct rb_vm_tag {
STATIC_ASSERT(rb_vm_tag_buf_offset, offsetof(struct rb_vm_tag, buf) > 0);
STATIC_ASSERT(rb_vm_tag_buf_end,
- offsetof(struct rb_vm_tag, buf) + sizeof(rb_jmpbuf_t) <
+ offsetof(struct rb_vm_tag, buf) + sizeof(rb_vm_tag_jmpbuf_t) <
sizeof(struct rb_vm_tag));
struct rb_unblock_callback {
@@ -943,6 +1058,10 @@ struct rb_execution_context_struct {
VALUE *stack_end;
size_t stack_maxsize;
RUBY_ALIGNAS(SIZEOF_VALUE) jmp_buf regs;
+
+#ifdef RUBY_ASAN_ENABLED
+ void *asan_fake_stack_handle;
+#endif
} machine;
};
@@ -984,6 +1103,7 @@ typedef struct rb_thread_struct {
rb_execution_context_t *ec;
struct rb_thread_sched_item sched;
+ bool mn_schedulable;
rb_atomic_t serial; // only for RUBY_DEBUG_LOG()
VALUE last_status; /* $? */
@@ -1059,6 +1179,7 @@ typedef struct rb_thread_struct {
/* misc */
VALUE name;
+ void **specific_storage;
struct rb_ext_config ext_config;
} rb_thread_t;
@@ -1088,12 +1209,13 @@ typedef enum {
RUBY_SYMBOL_EXPORT_BEGIN
/* node -> iseq */
-rb_iseq_t *rb_iseq_new (const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent, enum rb_iseq_type);
-rb_iseq_t *rb_iseq_new_top (const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent);
-rb_iseq_t *rb_iseq_new_main (const rb_ast_body_t *ast, VALUE path, VALUE realpath, const rb_iseq_t *parent, int opt);
-rb_iseq_t *rb_iseq_new_eval (const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth);
-rb_iseq_t *rb_iseq_new_with_opt(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth,
- enum rb_iseq_type, const rb_compile_option_t*);
+rb_iseq_t *rb_iseq_new (const VALUE vast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent, enum rb_iseq_type);
+rb_iseq_t *rb_iseq_new_top (const VALUE vast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent);
+rb_iseq_t *rb_iseq_new_main (const VALUE vast, VALUE path, VALUE realpath, const rb_iseq_t *parent, int opt);
+rb_iseq_t *rb_iseq_new_eval (const VALUE vast, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth);
+rb_iseq_t *rb_iseq_new_with_opt(const VALUE vast, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth,
+ enum rb_iseq_type, const rb_compile_option_t*,
+ VALUE script_lines);
struct iseq_link_anchor;
struct rb_iseq_new_with_callback_callback_func {
@@ -1106,8 +1228,12 @@ static inline struct rb_iseq_new_with_callback_callback_func *
rb_iseq_new_with_callback_new_callback(
void (*func)(rb_iseq_t *, struct iseq_link_anchor *, const void *), const void *ptr)
{
- VALUE memo = rb_imemo_new(imemo_ifunc, (VALUE)func, (VALUE)ptr, Qundef, Qfalse);
- return (struct rb_iseq_new_with_callback_callback_func *)memo;
+ struct rb_iseq_new_with_callback_callback_func *memo =
+ IMEMO_NEW(struct rb_iseq_new_with_callback_callback_func, imemo_ifunc, Qfalse);
+ memo->func = func;
+ memo->data = ptr;
+
+ return memo;
}
rb_iseq_t *rb_iseq_new_with_callback(const struct rb_iseq_new_with_callback_callback_func * ifunc,
VALUE name, VALUE path, VALUE realpath, int first_lineno,
@@ -1404,7 +1530,9 @@ VM_ENV_ENVVAL_PTR(const VALUE *ep)
static inline const rb_env_t *
vm_env_new(VALUE *env_ep, VALUE *env_body, unsigned int env_size, const rb_iseq_t *iseq)
{
- rb_env_t *env = (rb_env_t *)rb_imemo_new(imemo_env, (VALUE)env_ep, (VALUE)env_body, 0, (VALUE)iseq);
+ rb_env_t *env = IMEMO_NEW(rb_env_t, imemo_env, (VALUE)iseq);
+ env->ep = env_ep;
+ env->env = env_body;
env->env_size = env_size;
env_ep[VM_ENV_DATA_INDEX_ENV] = (VALUE)env;
return env;
@@ -1550,12 +1678,6 @@ vm_block_handler_verify(MAYBE_UNUSED(VALUE block_handler))
(vm_block_handler_type(block_handler), 1));
}
-static inline int
-vm_cfp_forwarded_bh_p(const rb_control_frame_t *cfp, VALUE block_handler)
-{
- return ((VALUE) cfp->block_code) == block_handler;
-}
-
static inline enum rb_block_type
vm_block_type(const struct rb_block *block)
{
@@ -1684,17 +1806,13 @@ VALUE rb_proc_alloc(VALUE klass);
VALUE rb_proc_dup(VALUE self);
/* for debug */
-extern void rb_vmdebug_stack_dump_raw(const rb_execution_context_t *ec, const rb_control_frame_t *cfp);
-extern void rb_vmdebug_debug_print_pre(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, const VALUE *_pc);
-extern void rb_vmdebug_debug_print_post(const rb_execution_context_t *ec, const rb_control_frame_t *cfp
-#if OPT_STACK_CACHING
- , VALUE reg_a, VALUE reg_b
-#endif
-);
+extern bool rb_vmdebug_stack_dump_raw(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, FILE *);
+extern bool rb_vmdebug_debug_print_pre(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, const VALUE *_pc, FILE *);
+extern bool rb_vmdebug_debug_print_post(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, FILE *);
-#define SDR() rb_vmdebug_stack_dump_raw(GET_EC(), GET_EC()->cfp)
-#define SDR2(cfp) rb_vmdebug_stack_dump_raw(GET_EC(), (cfp))
-void rb_vm_bugreport(const void *);
+#define SDR() rb_vmdebug_stack_dump_raw(GET_EC(), GET_EC()->cfp, stderr)
+#define SDR2(cfp) rb_vmdebug_stack_dump_raw(GET_EC(), (cfp), stderr)
+bool rb_vm_bugreport(const void *, FILE *);
typedef void (*ruby_sighandler_t)(int);
RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 4, 5)
NORETURN(void rb_bug_for_fatal_signal(ruby_sighandler_t default_sighandler, int sig, const void *, const char *fmt, ...));
@@ -1749,6 +1867,7 @@ rb_vm_living_threads_init(rb_vm_t *vm)
ccan_list_head_init(&vm->waiting_fds);
ccan_list_head_init(&vm->workqueue);
ccan_list_head_init(&vm->ractor.set);
+ ccan_list_head_init(&vm->ractor.sched.zombie_threads);
}
typedef int rb_backtrace_iter_func(void *, VALUE, int, VALUE);
@@ -1757,11 +1876,12 @@ rb_control_frame_t *rb_vm_get_binding_creatable_next_cfp(const rb_execution_cont
VALUE *rb_vm_svar_lep(const rb_execution_context_t *ec, const rb_control_frame_t *cfp);
int rb_vm_get_sourceline(const rb_control_frame_t *);
void rb_vm_stack_to_heap(rb_execution_context_t *ec);
-void ruby_thread_init_stack(rb_thread_t *th);
+void ruby_thread_init_stack(rb_thread_t *th, void *local_in_parent_frame);
rb_thread_t * ruby_thread_from_native(void);
int ruby_thread_set_native(rb_thread_t *th);
int rb_vm_control_frame_id_and_class(const rb_control_frame_t *cfp, ID *idp, ID *called_idp, VALUE *klassp);
void rb_vm_rewind_cfp(rb_execution_context_t *ec, rb_control_frame_t *cfp);
+void rb_vm_env_write(const VALUE *ep, int index, VALUE v);
VALUE rb_vm_bh_to_procval(const rb_execution_context_t *ec, VALUE block_handler);
void rb_vm_register_special_exception_str(enum ruby_special_exceptions sp, VALUE exception_class, VALUE mesg);
@@ -1769,7 +1889,7 @@ void rb_vm_register_special_exception_str(enum ruby_special_exceptions sp, VALUE
#define rb_vm_register_special_exception(sp, e, m) \
rb_vm_register_special_exception_str(sp, e, rb_usascii_str_new_static((m), (long)rb_strlen_lit(m)))
-void rb_gc_mark_machine_stack(const rb_execution_context_t *ec);
+void rb_gc_mark_machine_context(const rb_execution_context_t *ec);
void rb_vm_rewrite_cref(rb_cref_t *node, VALUE old_klass, VALUE new_klass, rb_cref_t **new_cref_ptr);
@@ -1848,6 +1968,20 @@ rb_current_execution_context(bool expect_ec)
#else
rb_execution_context_t *ec = ruby_current_ec;
#endif
+
+ /* On the shared objects, `__tls_get_addr()` is used to access the TLS
+ * and the address of the `ruby_current_ec` can be stored on a function
+ * frame. However, this address can be mis-used after native thread
+ * migration of a coroutine.
+ * 1) Get `ptr =&ruby_current_ec` op NT1 and store it on the frame.
+ * 2) Context switch and resume it on the NT2.
+ * 3) `ptr` is used on NT2 but it accesses to the TLS on NT1.
+ * This assertion checks such misusage.
+ *
+ * To avoid accidents, `GET_EC()` should be called once on the frame.
+ * Note that inlining can produce the problem.
+ */
+ VM_ASSERT(ec == rb_current_ec_noinline());
#else
rb_execution_context_t *ec = native_tls_get(ruby_current_ec_key);
#endif
@@ -2071,6 +2205,10 @@ rb_exec_event_hook_script_compiled(rb_execution_context_t *ec, const rb_iseq_t *
}
void rb_vm_trap_exit(rb_vm_t *vm);
+void rb_vm_postponed_job_atfork(void); /* vm_trace.c */
+void rb_vm_postponed_job_free(void); /* vm_trace.c */
+size_t rb_vm_memsize_postponed_job_queue(void); /* vm_trace.c */
+void rb_vm_postponed_job_queue_init(rb_vm_t *vm); /* vm_trace.c */
RUBY_SYMBOL_EXPORT_BEGIN
diff --git a/vm_debug.h b/vm_debug.h
index 9c7fc65f7c..d0bc81574a 100644
--- a/vm_debug.h
+++ b/vm_debug.h
@@ -23,8 +23,10 @@ RUBY_SYMBOL_EXPORT_BEGIN
struct RNode;
VALUE ruby_debug_print_value(int level, int debug_level, const char *header, VALUE v);
+void ruby_debug_print_v(VALUE v);
ID ruby_debug_print_id(int level, int debug_level, const char *header, ID id);
struct RNode *ruby_debug_print_node(int level, int debug_level, const char *header, const struct RNode *node);
+void ruby_debug_print_n(const struct RNode *node);
int ruby_debug_print_indent(int level, int debug_level, int indent_level);
void ruby_debug_gc_check_func(void);
void ruby_set_debug_option(const char *str);
diff --git a/vm_dump.c b/vm_dump.c
index fae34d72aa..444be4a4f3 100644
--- a/vm_dump.c
+++ b/vm_dump.c
@@ -46,8 +46,11 @@
const char *rb_method_type_name(rb_method_type_t type);
int ruby_on_ci;
-static void
-control_frame_dump(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
+#define kprintf(...) if (fprintf(errout, __VA_ARGS__) < 0) goto error
+#define kputs(s) if (fputs(s, errout) < 0) goto error
+
+static bool
+control_frame_dump(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, FILE *errout)
{
ptrdiff_t pc = -1;
ptrdiff_t ep = cfp->ep - ec->vm_stack;
@@ -140,30 +143,30 @@ control_frame_dump(const rb_execution_context_t *ec, const rb_control_frame_t *c
line = -1;
}
- fprintf(stderr, "c:%04"PRIdPTRDIFF" ",
+ kprintf("c:%04"PRIdPTRDIFF" ",
((rb_control_frame_t *)(ec->vm_stack + ec->vm_stack_size) - cfp));
if (pc == -1) {
- fprintf(stderr, "p:---- ");
+ kprintf("p:---- ");
}
else {
- fprintf(stderr, "p:%04"PRIdPTRDIFF" ", pc);
+ kprintf("p:%04"PRIdPTRDIFF" ", pc);
}
- fprintf(stderr, "s:%04"PRIdPTRDIFF" ", cfp->sp - ec->vm_stack);
- fprintf(stderr, ep_in_heap == ' ' ? "e:%06"PRIdPTRDIFF" " : "E:%06"PRIxPTRDIFF" ", ep % 10000);
- fprintf(stderr, "%-6s", magic);
+ kprintf("s:%04"PRIdPTRDIFF" ", cfp->sp - ec->vm_stack);
+ kprintf(ep_in_heap == ' ' ? "e:%06"PRIdPTRDIFF" " : "E:%06"PRIxPTRDIFF" ", ep % 10000);
+ kprintf("%-6s", magic);
if (line) {
- fprintf(stderr, " %s", posbuf);
+ kprintf(" %s", posbuf);
}
if (VM_FRAME_FINISHED_P(cfp)) {
- fprintf(stderr, " [FINISH]");
+ kprintf(" [FINISH]");
}
if (0) {
- fprintf(stderr, " \t");
- fprintf(stderr, "iseq: %-24s ", iseq_name);
- fprintf(stderr, "self: %-24s ", selfstr);
- fprintf(stderr, "%-1s ", biseq_name);
+ kprintf(" \t");
+ kprintf("iseq: %-24s ", iseq_name);
+ kprintf("self: %-24s ", selfstr);
+ kprintf("%-1s ", biseq_name);
}
- fprintf(stderr, "\n");
+ kprintf("\n");
// additional information for CI machines
if (ruby_on_ci) {
@@ -171,131 +174,146 @@ control_frame_dump(const rb_execution_context_t *ec, const rb_control_frame_t *c
if (me) {
if (IMEMO_TYPE_P(me, imemo_ment)) {
- fprintf(stderr, " me:\n");
- fprintf(stderr, " called_id: %s, type: %s\n", rb_id2name(me->called_id), rb_method_type_name(me->def->type));
- fprintf(stderr, " owner class: %s\n", rb_raw_obj_info(buff, 0x100, me->owner));
+ kprintf(" me:\n");
+ kprintf(" called_id: %s, type: %s\n", rb_id2name(me->called_id), rb_method_type_name(me->def->type));
+ kprintf(" owner class: %s\n", rb_raw_obj_info(buff, 0x100, me->owner));
if (me->owner != me->defined_class) {
- fprintf(stderr, " defined_class: %s\n", rb_raw_obj_info(buff, 0x100, me->defined_class));
+ kprintf(" defined_class: %s\n", rb_raw_obj_info(buff, 0x100, me->defined_class));
}
}
else {
- fprintf(stderr, " me is corrupted (%s)\n", rb_raw_obj_info(buff, 0x100, (VALUE)me));
+ kprintf(" me is corrupted (%s)\n", rb_raw_obj_info(buff, 0x100, (VALUE)me));
}
}
- fprintf(stderr, " self: %s\n", rb_raw_obj_info(buff, 0x100, cfp->self));
+ kprintf(" self: %s\n", rb_raw_obj_info(buff, 0x100, cfp->self));
if (iseq) {
if (ISEQ_BODY(iseq)->local_table_size > 0) {
- fprintf(stderr, " lvars:\n");
+ kprintf(" lvars:\n");
for (unsigned int i=0; i<ISEQ_BODY(iseq)->local_table_size; i++) {
const VALUE *argv = cfp->ep - ISEQ_BODY(cfp->iseq)->local_table_size - VM_ENV_DATA_SIZE + 1;
- fprintf(stderr, " %s: %s\n",
+ kprintf(" %s: %s\n",
rb_id2name(ISEQ_BODY(iseq)->local_table[i]),
rb_raw_obj_info(buff, 0x100, argv[i]));
}
}
}
}
+ return true;
+ error:
+ return false;
}
-void
-rb_vmdebug_stack_dump_raw(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
+bool
+rb_vmdebug_stack_dump_raw(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, FILE *errout)
{
#if 0
VALUE *sp = cfp->sp;
const VALUE *ep = cfp->ep;
VALUE *p, *st, *t;
- fprintf(stderr, "-- stack frame ------------\n");
+ kprintf("-- stack frame ------------\n");
for (p = st = ec->vm_stack; p < sp; p++) {
- fprintf(stderr, "%04ld (%p): %08"PRIxVALUE, (long)(p - st), p, *p);
+ kprintf("%04ld (%p): %08"PRIxVALUE, (long)(p - st), p, *p);
t = (VALUE *)*p;
if (ec->vm_stack <= t && t < sp) {
- fprintf(stderr, " (= %ld)", (long)((VALUE *)GC_GUARDED_PTR_REF((VALUE)t) - ec->vm_stack));
+ kprintf(" (= %ld)", (long)((VALUE *)GC_GUARDED_PTR_REF((VALUE)t) - ec->vm_stack));
}
if (p == ep)
- fprintf(stderr, " <- ep");
+ kprintf(" <- ep");
- fprintf(stderr, "\n");
+ kprintf("\n");
}
#endif
- fprintf(stderr, "-- Control frame information "
+ kprintf("-- Control frame information "
"-----------------------------------------------\n");
while ((void *)cfp < (void *)(ec->vm_stack + ec->vm_stack_size)) {
- control_frame_dump(ec, cfp);
+ control_frame_dump(ec, cfp, errout);
cfp++;
}
- fprintf(stderr, "\n");
+ kprintf("\n");
+ return true;
+
+ error:
+ return false;
}
-void
+bool
rb_vmdebug_stack_dump_raw_current(void)
{
const rb_execution_context_t *ec = GET_EC();
- rb_vmdebug_stack_dump_raw(ec, ec->cfp);
+ return rb_vmdebug_stack_dump_raw(ec, ec->cfp, stderr);
}
-void
-rb_vmdebug_env_dump_raw(const rb_env_t *env, const VALUE *ep)
+bool
+rb_vmdebug_env_dump_raw(const rb_env_t *env, const VALUE *ep, FILE *errout)
{
unsigned int i;
- fprintf(stderr, "-- env --------------------\n");
+ kprintf("-- env --------------------\n");
while (env) {
- fprintf(stderr, "--\n");
+ kprintf("--\n");
for (i = 0; i < env->env_size; i++) {
- fprintf(stderr, "%04d: %08"PRIxVALUE" (%p)", i, env->env[i], (void *)&env->env[i]);
- if (&env->env[i] == ep) fprintf(stderr, " <- ep");
- fprintf(stderr, "\n");
+ kprintf("%04d: %08"PRIxVALUE" (%p)", i, env->env[i], (void *)&env->env[i]);
+ if (&env->env[i] == ep) kprintf(" <- ep");
+ kprintf("\n");
}
env = rb_vm_env_prev_env(env);
}
- fprintf(stderr, "---------------------------\n");
+ kprintf("---------------------------\n");
+ return true;
+
+ error:
+ return false;
}
-void
-rb_vmdebug_proc_dump_raw(rb_proc_t *proc)
+bool
+rb_vmdebug_proc_dump_raw(rb_proc_t *proc, FILE *errout)
{
const rb_env_t *env;
char *selfstr;
VALUE val = rb_inspect(vm_block_self(&proc->block));
selfstr = StringValueCStr(val);
- fprintf(stderr, "-- proc -------------------\n");
- fprintf(stderr, "self: %s\n", selfstr);
+ kprintf("-- proc -------------------\n");
+ kprintf("self: %s\n", selfstr);
env = VM_ENV_ENVVAL_PTR(vm_block_ep(&proc->block));
- rb_vmdebug_env_dump_raw(env, vm_block_ep(&proc->block));
+ rb_vmdebug_env_dump_raw(env, vm_block_ep(&proc->block), errout);
+ return true;
+
+ error:
+ return false;
}
-void
-rb_vmdebug_stack_dump_th(VALUE thval)
+bool
+rb_vmdebug_stack_dump_th(VALUE thval, FILE *errout)
{
rb_thread_t *target_th = rb_thread_ptr(thval);
- rb_vmdebug_stack_dump_raw(target_th->ec, target_th->ec->cfp);
+ return rb_vmdebug_stack_dump_raw(target_th->ec, target_th->ec->cfp, errout);
}
#if VMDEBUG > 2
-/* copy from vm.c */
+/* copy from vm_insnhelper.c */
static const VALUE *
vm_base_ptr(const rb_control_frame_t *cfp)
{
const rb_control_frame_t *prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
const VALUE *bp = prev_cfp->sp + ISEQ_BODY(cfp->iseq)->local_table_size + VM_ENV_DATA_SIZE;
- if (ISEQ_BODY(cfp->iseq)->type == ISEQ_TYPE_METHOD) {
+ if (ISEQ_BODY(cfp->iseq)->type == ISEQ_TYPE_METHOD || VM_FRAME_BMETHOD_P(cfp)) {
bp += 1;
}
return bp;
}
static void
-vm_stack_dump_each(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
+vm_stack_dump_each(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, FILE *errout)
{
int i, argc = 0, local_table_size = 0;
VALUE rstr;
@@ -321,17 +339,17 @@ vm_stack_dump_each(const rb_execution_context_t *ec, const rb_control_frame_t *c
{
const VALUE *ptr = ep - local_table_size;
- control_frame_dump(ec, cfp);
+ control_frame_dump(ec, cfp, errout);
for (i = 0; i < argc; i++) {
rstr = rb_inspect(*ptr);
- fprintf(stderr, " arg %2d: %8s (%p)\n", i, StringValueCStr(rstr),
- (void *)ptr++);
+ kprintf(" arg %2d: %8s (%p)\n", i, StringValueCStr(rstr),
+ (void *)ptr++);
}
for (; i < local_table_size - 1; i++) {
rstr = rb_inspect(*ptr);
- fprintf(stderr, " local %2d: %8s (%p)\n", i, StringValueCStr(rstr),
- (void *)ptr++);
+ kprintf(" local %2d: %8s (%p)\n", i, StringValueCStr(rstr),
+ (void *)ptr++);
}
ptr = vm_base_ptr(cfp);
@@ -347,13 +365,13 @@ vm_stack_dump_each(const rb_execution_context_t *ec, const rb_control_frame_t *c
rstr = rb_inspect(*ptr);
break;
}
- fprintf(stderr, " stack %2d: %8s (%"PRIdPTRDIFF")\n", i, StringValueCStr(rstr),
+ kprintf(" stack %2d: %8s (%"PRIdPTRDIFF")\n", i, StringValueCStr(rstr),
(ptr - ec->vm_stack));
}
}
else if (VM_FRAME_FINISHED_P(cfp)) {
if (ec->vm_stack + ec->vm_stack_size > (VALUE *)(cfp + 1)) {
- vm_stack_dump_each(ec, cfp + 1);
+ vm_stack_dump_each(ec, cfp + 1, errout);
}
else {
/* SDR(); */
@@ -365,8 +383,8 @@ vm_stack_dump_each(const rb_execution_context_t *ec, const rb_control_frame_t *c
}
#endif
-void
-rb_vmdebug_debug_print_register(const rb_execution_context_t *ec)
+bool
+rb_vmdebug_debug_print_register(const rb_execution_context_t *ec, FILE *errout)
{
rb_control_frame_t *cfp = ec->cfp;
ptrdiff_t pc = -1;
@@ -382,18 +400,22 @@ rb_vmdebug_debug_print_register(const rb_execution_context_t *ec)
}
cfpi = ((rb_control_frame_t *)(ec->vm_stack + ec->vm_stack_size)) - cfp;
- fprintf(stderr, " [PC] %04"PRIdPTRDIFF", [SP] %04"PRIdPTRDIFF", [EP] %04"PRIdPTRDIFF", [CFP] %04"PRIdPTRDIFF"\n",
+ kprintf(" [PC] %04"PRIdPTRDIFF", [SP] %04"PRIdPTRDIFF", [EP] %04"PRIdPTRDIFF", [CFP] %04"PRIdPTRDIFF"\n",
pc, (cfp->sp - ec->vm_stack), ep, cfpi);
+ return true;
+
+ error:
+ return false;
}
-void
-rb_vmdebug_thread_dump_regs(VALUE thval)
+bool
+rb_vmdebug_thread_dump_regs(VALUE thval, FILE *errout)
{
- rb_vmdebug_debug_print_register(rb_thread_ptr(thval)->ec);
+ return rb_vmdebug_debug_print_register(rb_thread_ptr(thval)->ec, errout);
}
-void
-rb_vmdebug_debug_print_pre(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, const VALUE *_pc)
+bool
+rb_vmdebug_debug_print_pre(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, const VALUE *_pc, FILE *errout)
{
const rb_iseq_t *iseq = cfp->iseq;
@@ -402,10 +424,10 @@ rb_vmdebug_debug_print_pre(const rb_execution_context_t *ec, const rb_control_fr
int i;
for (i=0; i<(int)VM_CFP_CNT(ec, cfp); i++) {
- printf(" ");
+ kprintf(" ");
}
- printf("| ");
- if(0)printf("[%03ld] ", (long)(cfp->sp - ec->vm_stack));
+ kprintf("| ");
+ if(0) kprintf("[%03ld] ", (long)(cfp->sp - ec->vm_stack));
/* printf("%3"PRIdPTRDIFF" ", VM_CFP_CNT(ec, cfp)); */
if (pc >= 0) {
@@ -416,56 +438,54 @@ rb_vmdebug_debug_print_pre(const rb_execution_context_t *ec, const rb_control_fr
}
#if VMDEBUG > 3
- fprintf(stderr, " (1)");
- rb_vmdebug_debug_print_register(ec);
+ kprintf(" (1)");
+ rb_vmdebug_debug_print_register(errout, ec);
#endif
+ return true;
+
+ error:
+ return false;
}
-void
-rb_vmdebug_debug_print_post(const rb_execution_context_t *ec, const rb_control_frame_t *cfp
-#if OPT_STACK_CACHING
- , VALUE reg_a, VALUE reg_b
-#endif
- )
+bool
+rb_vmdebug_debug_print_post(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, FILE *errout)
{
#if VMDEBUG > 9
- SDR2(cfp);
+ if (!rb_vmdebug_stack_dump_raw(ec, cfp, errout)) goto errout;
#endif
#if VMDEBUG > 3
- fprintf(stderr, " (2)");
- rb_vmdebug_debug_print_register(ec);
+ kprintf(" (2)");
+ rb_vmdebug_debug_print_register(errout, ec);
#endif
/* stack_dump_raw(ec, cfp); */
#if VMDEBUG > 2
/* stack_dump_thobj(ec); */
- vm_stack_dump_each(ec, ec->cfp);
+ vm_stack_dump_each(ec, ec->cfp, errout);
-#if OPT_STACK_CACHING
- {
- VALUE rstr;
- rstr = rb_inspect(reg_a);
- fprintf(stderr, " sc reg A: %s\n", StringValueCStr(rstr));
- rstr = rb_inspect(reg_b);
- fprintf(stderr, " sc reg B: %s\n", StringValueCStr(rstr));
- }
-#endif
- printf
+ kprintf
("--------------------------------------------------------------\n");
#endif
+ return true;
+
+#if VMDEBUG > 2
+ error:
+ return false;
+#endif
}
VALUE
-rb_vmdebug_thread_dump_state(VALUE self)
+rb_vmdebug_thread_dump_state(FILE *errout, VALUE self)
{
rb_thread_t *th = rb_thread_ptr(self);
rb_control_frame_t *cfp = th->ec->cfp;
- fprintf(stderr, "Thread state dump:\n");
- fprintf(stderr, "pc : %p, sp : %p\n", (void *)cfp->pc, (void *)cfp->sp);
- fprintf(stderr, "cfp: %p, ep : %p\n", (void *)cfp, (void *)cfp->ep);
+ kprintf("Thread state dump:\n");
+ kprintf("pc : %p, sp : %p\n", (void *)cfp->pc, (void *)cfp->sp);
+ kprintf("cfp: %p, ep : %p\n", (void *)cfp, (void *)cfp->ep);
+ error:
return Qnil;
}
@@ -677,6 +697,11 @@ typedef void *PGET_MODULE_BASE_ROUTINE64;
typedef void *PTRANSLATE_ADDRESS_ROUTINE64;
# endif
+struct dump_thead_arg {
+ DWORD tid;
+ FILE *errout;
+};
+
static void
dump_thread(void *arg)
{
@@ -688,7 +713,8 @@ dump_thread(void *arg)
BOOL (WINAPI *pSymFromAddr)(HANDLE, DWORD64, DWORD64 *, SYMBOL_INFO *);
BOOL (WINAPI *pSymGetLineFromAddr64)(HANDLE, DWORD64, DWORD *, IMAGEHLP_LINE64 *);
HANDLE (WINAPI *pOpenThread)(DWORD, BOOL, DWORD);
- DWORD tid = *(DWORD *)arg;
+ DWORD tid = ((struct dump_thead_arg *)arg)->tid;
+ FILE *errout = ((struct dump_thead_arg *)arg)->errout;
HANDLE ph;
HANDLE th;
@@ -727,6 +753,14 @@ dump_thread(void *arg)
frame.AddrFrame.Offset = context.Rbp;
frame.AddrStack.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Rsp;
+#elif defined(__aarch64__)
+ mac = IMAGE_FILE_MACHINE_ARM64;
+ frame.AddrPC.Mode = AddrModeFlat;
+ frame.AddrPC.Offset = context.Pc;
+ frame.AddrFrame.Mode = AddrModeFlat;
+ frame.AddrFrame.Offset = context.Fp;
+ frame.AddrStack.Mode = AddrModeFlat;
+ frame.AddrStack.Offset = context.Sp;
#else /* i386 */
mac = IMAGE_FILE_MACHINE_I386;
frame.AddrPC.Mode = AddrModeFlat;
@@ -753,19 +787,20 @@ dump_thread(void *arg)
info->MaxNameLen = MAX_SYM_NAME;
if (pSymFromAddr(ph, addr, &displacement, info)) {
if (GetModuleFileName((HANDLE)(uintptr_t)pSymGetModuleBase64(ph, addr), libpath, sizeof(libpath)))
- fprintf(stderr, "%s", libpath);
- fprintf(stderr, "(%s+0x%"PRI_64_PREFIX"x)",
+ kprintf("%s", libpath);
+ kprintf("(%s+0x%"PRI_64_PREFIX"x)",
info->Name, displacement);
}
- fprintf(stderr, " [0x%p]", (void *)(VALUE)addr);
+ kprintf(" [0x%p]", (void *)(VALUE)addr);
memset(&line, 0, sizeof(line));
line.SizeOfStruct = sizeof(line);
if (pSymGetLineFromAddr64(ph, addr, &tmp, &line))
- fprintf(stderr, " %s:%lu", line.FileName, line.LineNumber);
- fprintf(stderr, "\n");
+ kprintf(" %s:%lu", line.FileName, line.LineNumber);
+ kprintf("\n");
}
}
+ error:
ResumeThread(th);
}
CloseHandle(th);
@@ -777,53 +812,61 @@ dump_thread(void *arg)
#endif
void
-rb_print_backtrace(void)
+rb_print_backtrace(FILE *errout)
{
#if USE_BACKTRACE
#define MAX_NATIVE_TRACE 1024
static void *trace[MAX_NATIVE_TRACE];
int n = (int)backtrace(trace, MAX_NATIVE_TRACE);
#if (defined(USE_ELF) || defined(HAVE_MACH_O_LOADER_H)) && defined(HAVE_DLADDR) && !defined(__sparc)
- rb_dump_backtrace_with_lines(n, trace);
+ rb_dump_backtrace_with_lines(n, trace, errout);
#else
char **syms = backtrace_symbols(trace, n);
if (syms) {
int i;
for (i=0; i<n; i++) {
- fprintf(stderr, "%s\n", syms[i]);
+ kprintf("%s\n", syms[i]);
}
free(syms);
}
+ error:
+ /* ignore errors at writing */;
#endif
#elif defined(_WIN32)
- DWORD tid = GetCurrentThreadId();
- HANDLE th = (HANDLE)_beginthread(dump_thread, 0, &tid);
+ struct dump_thead_arg arg = {
+ .tid = GetCurrentThreadId(),
+ .errout = errout,
+ };
+ HANDLE th = (HANDLE)_beginthread(dump_thread, 0, &arg);
if (th != (HANDLE)-1)
WaitForSingleObject(th, INFINITE);
#endif
}
#ifdef HAVE_LIBPROCSTAT
+struct procstat;
+struct kinfo_proc;
+static void procstat_vm(struct procstat *, struct kinfo_proc *, FILE *);
#include "missing/procstat_vm.c"
#endif
#if defined __linux__
# if defined(__x86_64__) || defined(__i386__)
-# define dump_machine_register(reg) (col_count = print_machine_register(mctx->gregs[REG_##reg], #reg, col_count, 80))
+# define dump_machine_register(reg) (col_count = print_machine_register(errout, mctx->gregs[REG_##reg], #reg, col_count, 80))
# elif defined(__aarch64__) || defined(__arm__) || defined(__riscv) || defined(__loongarch64)
-# define dump_machine_register(reg, regstr) (col_count = print_machine_register(reg, regstr, col_count, 80))
+# define dump_machine_register(reg, regstr) (col_count = print_machine_register(errout, reg, regstr, col_count, 80))
# endif
#elif defined __APPLE__
# if defined(__aarch64__)
-# define dump_machine_register(reg, regstr) (col_count = print_machine_register(mctx->MCTX_SS_REG(reg), regstr, col_count, 80))
+# define dump_machine_register(reg, regstr) (col_count = print_machine_register(errout, mctx->MCTX_SS_REG(reg), regstr, col_count, 80))
# else
-# define dump_machine_register(reg) (col_count = print_machine_register(mctx->MCTX_SS_REG(reg), #reg, col_count, 80))
+# define dump_machine_register(reg) (col_count = print_machine_register(errout, mctx->MCTX_SS_REG(reg), #reg, col_count, 80))
# endif
#endif
#ifdef dump_machine_register
static int
-print_machine_register(size_t reg, const char *reg_name, int col_count, int max_col)
+print_machine_register(FILE *errout, size_t reg, const char *reg_name, int col_count, int max_col)
{
int ret;
char buf[64];
@@ -831,21 +874,24 @@ print_machine_register(size_t reg, const char *reg_name, int col_count, int max_
ret = snprintf(buf, sizeof(buf), " %3.3s: 0x%.*" PRIxSIZE, reg_name, size_width, reg);
if (col_count + ret > max_col) {
- fputs("\n", stderr);
+ kputs("\n");
col_count = 0;
}
col_count += ret;
- fputs(buf, stderr);
+ kputs(buf);
return col_count;
+
+ error:
+ return -1;
}
-static void
-rb_dump_machine_register(const ucontext_t *ctx)
+static bool
+rb_dump_machine_register(FILE *errout, const ucontext_t *ctx)
{
int col_count = 0;
- if (!ctx) return;
+ if (!ctx) return true;
- fprintf(stderr, "-- Machine register context "
+ kprintf("-- Machine register context "
"------------------------------------------------\n");
# if defined __linux__
@@ -1038,14 +1084,18 @@ rb_dump_machine_register(const ucontext_t *ctx)
# endif
}
# endif
- fprintf(stderr, "\n\n");
+ kprintf("\n\n");
+ return true;
+
+ error:
+ return false;
}
#else
-# define rb_dump_machine_register(ctx) ((void)0)
+# define rb_dump_machine_register(errout, ctx) ((void)0)
#endif /* dump_machine_register */
-void
-rb_vm_bugreport(const void *ctx)
+bool
+rb_vm_bugreport(const void *ctx, FILE *errout)
{
const char *cmd = getenv("RUBY_ON_BUG");
if (cmd) {
@@ -1062,8 +1112,8 @@ rb_vm_bugreport(const void *ctx)
{
static bool crashing = false;
if (crashing) {
- fprintf(stderr, "Crashed while printing bug report\n");
- return;
+ kprintf("Crashed while printing bug report\n");
+ return true;
}
crashing = true;
}
@@ -1080,32 +1130,32 @@ rb_vm_bugreport(const void *ctx)
const rb_execution_context_t *ec = rb_current_execution_context(false);
if (vm && ec) {
- SDR();
- rb_backtrace_print_as_bugreport();
- fputs("\n", stderr);
+ rb_vmdebug_stack_dump_raw(ec, ec->cfp, errout);
+ rb_backtrace_print_as_bugreport(errout);
+ kputs("\n");
// If we get here, hopefully things are intact enough that
// we can read these two numbers. It is an estimate because
// we are reading without synchronization.
- fprintf(stderr, "-- Threading information "
+ kprintf("-- Threading information "
"---------------------------------------------------\n");
- fprintf(stderr, "Total ractor count: %u\n", vm->ractor.cnt);
- fprintf(stderr, "Ruby thread count for this ractor: %u\n", rb_ec_ractor_ptr(ec)->threads.cnt);
- fputs("\n", stderr);
+ kprintf("Total ractor count: %u\n", vm->ractor.cnt);
+ kprintf("Ruby thread count for this ractor: %u\n", rb_ec_ractor_ptr(ec)->threads.cnt);
+ kputs("\n");
}
- rb_dump_machine_register(ctx);
+ rb_dump_machine_register(errout, ctx);
#if USE_BACKTRACE || defined(_WIN32)
- fprintf(stderr, "-- C level backtrace information "
+ kprintf("-- C level backtrace information "
"-------------------------------------------\n");
- rb_print_backtrace();
+ rb_print_backtrace(errout);
- fprintf(stderr, "\n");
+ kprintf("\n");
#endif /* USE_BACKTRACE */
if (other_runtime_info || vm) {
- fprintf(stderr, "-- Other runtime information "
+ kprintf("-- Other runtime information "
"-----------------------------------------------\n\n");
}
if (vm && !rb_during_gc()) {
@@ -1118,16 +1168,16 @@ rb_vm_bugreport(const void *ctx)
name = vm->progname;
if (name) {
- fprintf(stderr, "* Loaded script: %.*s\n",
+ kprintf("* Loaded script: %.*s\n",
LIMITED_NAME_LENGTH(name), RSTRING_PTR(name));
- fprintf(stderr, "\n");
+ kprintf("\n");
}
if (vm->loaded_features) {
- fprintf(stderr, "* Loaded features:\n\n");
+ kprintf("* Loaded features:\n\n");
for (i=0; i<RARRAY_LEN(vm->loaded_features); i++) {
name = RARRAY_AREF(vm->loaded_features, i);
if (RB_TYPE_P(name, T_STRING)) {
- fprintf(stderr, " %4d %.*s\n", i,
+ kprintf(" %4d %.*s\n", i,
LIMITED_NAME_LENGTH(name), RSTRING_PTR(name));
}
else if (RB_TYPE_P(name, T_CLASS) || RB_TYPE_P(name, T_MODULE)) {
@@ -1135,26 +1185,26 @@ rb_vm_bugreport(const void *ctx)
"class" : "module";
name = rb_search_class_path(rb_class_real(name));
if (!RB_TYPE_P(name, T_STRING)) {
- fprintf(stderr, " %4d %s:<unnamed>\n", i, type);
+ kprintf(" %4d %s:<unnamed>\n", i, type);
continue;
}
- fprintf(stderr, " %4d %s:%.*s\n", i, type,
+ kprintf(" %4d %s:%.*s\n", i, type,
LIMITED_NAME_LENGTH(name), RSTRING_PTR(name));
}
else {
VALUE klass = rb_search_class_path(rb_obj_class(name));
if (!RB_TYPE_P(klass, T_STRING)) {
- fprintf(stderr, " %4d #<%p:%p>\n", i,
+ kprintf(" %4d #<%p:%p>\n", i,
(void *)CLASS_OF(name), (void *)name);
continue;
}
- fprintf(stderr, " %4d #<%.*s:%p>\n", i,
+ kprintf(" %4d #<%.*s:%p>\n", i,
LIMITED_NAME_LENGTH(klass), RSTRING_PTR(klass),
(void *)name);
}
}
}
- fprintf(stderr, "\n");
+ kprintf("\n");
}
{
@@ -1162,17 +1212,17 @@ rb_vm_bugreport(const void *ctx)
{
FILE *fp = fopen(PROC_MAPS_NAME, "r");
if (fp) {
- fprintf(stderr, "* Process memory map:\n\n");
+ kprintf("* Process memory map:\n\n");
while (!feof(fp)) {
char buff[0x100];
size_t rn = fread(buff, 1, 0x100, fp);
- if (fwrite(buff, 1, rn, stderr) != rn)
+ if (fwrite(buff, 1, rn, errout) != rn)
break;
}
fclose(fp);
- fprintf(stderr, "\n\n");
+ kprintf("\n\n");
}
}
#endif /* __linux__ */
@@ -1186,14 +1236,14 @@ rb_vm_bugreport(const void *ctx)
mib[2] = KERN_PROC_PID;
mib[3] = getpid();
if (sysctl(mib, MIB_KERN_PROC_PID_LEN, &kp, &len, NULL, 0) == -1) {
- perror("sysctl");
+ kprintf("sysctl: %s\n", strerror(errno));
}
else {
struct procstat *prstat = procstat_open_sysctl();
- fprintf(stderr, "* Process memory map:\n\n");
- procstat_vm(prstat, &kp);
+ kprintf("* Process memory map:\n\n");
+ procstat_vm(prstat, &kp, errout);
procstat_close(prstat);
- fprintf(stderr, "\n");
+ kprintf("\n");
}
#endif /* __FreeBSD__ */
#ifdef __APPLE__
@@ -1203,7 +1253,7 @@ rb_vm_bugreport(const void *ctx)
mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT;
natural_t depth = 0;
- fprintf(stderr, "* Process memory map:\n\n");
+ kprintf("* Process memory map:\n\n");
while (1) {
if (vm_region_recurse(mach_task_self(), &addr, &size, &depth,
(vm_region_recurse_info_t)&map, &count) != KERN_SUCCESS) {
@@ -1215,17 +1265,17 @@ rb_vm_bugreport(const void *ctx)
depth++;
}
else {
- fprintf(stderr, "%lx-%lx %s%s%s", addr, (addr+size),
+ kprintf("%lx-%lx %s%s%s", addr, (addr+size),
((map.protection & VM_PROT_READ) != 0 ? "r" : "-"),
((map.protection & VM_PROT_WRITE) != 0 ? "w" : "-"),
((map.protection & VM_PROT_EXECUTE) != 0 ? "x" : "-"));
#ifdef HAVE_LIBPROC_H
char buff[PATH_MAX];
if (proc_regionfilename(getpid(), addr, buff, sizeof(buff)) > 0) {
- fprintf(stderr, " %s", buff);
+ kprintf(" %s", buff);
}
#endif
- fprintf(stderr, "\n");
+ kprintf("\n");
}
addr += size;
@@ -1233,21 +1283,30 @@ rb_vm_bugreport(const void *ctx)
}
#endif
}
+ return true;
+
+ error:
+ return false;
}
-void
+bool
rb_vmdebug_stack_dump_all_threads(void)
{
rb_thread_t *th = NULL;
rb_ractor_t *r = GET_RACTOR();
+ FILE *errout = stderr;
// TODO: now it only shows current ractor
ccan_list_for_each(&r->threads.set, th, lt_node) {
#ifdef NON_SCALAR_THREAD_ID
- fprintf(stderr, "th: %p, native_id: N/A\n", th);
+ kprintf("th: %p, native_id: N/A\n", th);
#else
- fprintf(stderr, "th: %p, native_id: %p\n", (void *)th, (void *)(uintptr_t)th->nt->thread_id);
+ kprintf("th: %p, native_id: %p\n", (void *)th, (void *)(uintptr_t)th->nt->thread_id);
#endif
- rb_vmdebug_stack_dump_raw(th->ec, th->ec->cfp);
+ if (!rb_vmdebug_stack_dump_raw(th->ec, th->ec->cfp, errout)) goto error;
}
+ return true;
+
+ error:
+ return false;
}
diff --git a/vm_eval.c b/vm_eval.c
index d5fbcf5f56..4dfff02f6b 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -29,15 +29,6 @@ static VALUE rb_eUncaughtThrow;
static ID id_result, id_tag, id_value;
#define id_mesg idMesg
-typedef enum call_type {
- CALL_PUBLIC,
- CALL_FCALL,
- CALL_VCALL,
- CALL_PUBLIC_KW,
- CALL_FCALL_KW,
- CALL_TYPE_MAX
-} call_type;
-
static VALUE send_internal(int argc, const VALUE *argv, VALUE recv, call_type scope);
static VALUE vm_call0_body(rb_execution_context_t* ec, struct rb_calling_info *calling, const VALUE *argv);
@@ -96,7 +87,10 @@ vm_call0_cc(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE
}
struct rb_calling_info calling = {
- .ci = &VM_CI_ON_STACK(id, flags, argc, NULL),
+ .cd = &(struct rb_call_data) {
+ .ci = &VM_CI_ON_STACK(id, flags, argc, NULL),
+ .cc = NULL,
+ },
.cc = cc,
.block_handler = vm_passed_block_handler(ec),
.recv = recv,
@@ -117,7 +111,7 @@ vm_call0_cme(rb_execution_context_t *ec, struct rb_calling_info *calling, const
static VALUE
vm_call0_super(rb_execution_context_t *ec, struct rb_calling_info *calling, const VALUE *argv, VALUE klass, enum method_missing_reason ex)
{
- ID mid = vm_ci_mid(calling->ci);
+ ID mid = vm_ci_mid(calling->cd->ci);
klass = RCLASS_SUPER(klass);
if (klass) {
@@ -136,7 +130,7 @@ vm_call0_super(rb_execution_context_t *ec, struct rb_calling_info *calling, cons
static VALUE
vm_call0_cfunc_with_frame(rb_execution_context_t* ec, struct rb_calling_info *calling, const VALUE *argv)
{
- const struct rb_callinfo *ci = calling->ci;
+ const struct rb_callinfo *ci = calling->cd->ci;
VALUE val;
const rb_callable_method_entry_t *me = vm_cc_cme(calling->cc);
const rb_method_cfunc_t *cfunc = UNALIGNED_MEMBER_PTR(me->def, body.cfunc);
@@ -201,7 +195,7 @@ vm_call_check_arity(struct rb_calling_info *calling, int argc, const VALUE *argv
static VALUE
vm_call0_body(rb_execution_context_t *ec, struct rb_calling_info *calling, const VALUE *argv)
{
- const struct rb_callinfo *ci = calling->ci;
+ const struct rb_callinfo *ci = calling->cd->ci;
const struct rb_callcache *cc = calling->cc;
VALUE ret;
@@ -296,11 +290,15 @@ vm_call0_body(rb_execution_context_t *ec, struct rb_calling_info *calling, const
}
case OPTIMIZED_METHOD_TYPE_STRUCT_AREF:
vm_call_check_arity(calling, 0, argv);
- ret = vm_call_opt_struct_aref0(ec, calling);
+ VM_CALL_METHOD_ATTR(ret,
+ vm_call_opt_struct_aref0(ec, calling),
+ (void)0);
goto success;
case OPTIMIZED_METHOD_TYPE_STRUCT_ASET:
vm_call_check_arity(calling, 1, argv);
- ret = vm_call_opt_struct_aset0(ec, calling, argv[0]);
+ VM_CALL_METHOD_ATTR(ret,
+ vm_call_opt_struct_aset0(ec, calling, argv[0]),
+ (void)0);
goto success;
default:
rb_bug("vm_call0: unsupported optimized method type (%d)", vm_cc_cme(cc)->def->body.optimized.type);
@@ -396,71 +394,53 @@ NORETURN(static void uncallable_object(VALUE recv, ID mid));
static inline const rb_callable_method_entry_t *rb_search_method_entry(VALUE recv, ID mid);
static inline enum method_missing_reason rb_method_call_status(rb_execution_context_t *ec, const rb_callable_method_entry_t *me, call_type scope, VALUE self);
-static const struct rb_callcache *
-cc_new(VALUE klass, ID mid, int argc, const rb_callable_method_entry_t *cme)
-{
- const struct rb_callcache *cc = NULL;
-
- RB_VM_LOCK_ENTER();
- {
- struct rb_class_cc_entries *ccs;
- struct rb_id_table *cc_tbl = RCLASS_CC_TBL(klass);
- VALUE ccs_data;
-
- if (rb_id_table_lookup(cc_tbl, mid, &ccs_data)) {
- // ok
- ccs = (struct rb_class_cc_entries *)ccs_data;
- }
- else {
- ccs = vm_ccs_create(klass, cc_tbl, mid, cme);
- }
-
- for (int i=0; i<ccs->len; i++) {
- cc = ccs->entries[i].cc;
- if (vm_cc_cme(cc) == cme) {
- break;
- }
- cc = NULL;
- }
-
- if (cc == NULL) {
- const struct rb_callinfo *ci = vm_ci_new(mid, 0, argc, NULL); // TODO: proper ci
- cc = vm_cc_new(klass, cme, vm_call_general);
- METHOD_ENTRY_CACHED_SET((struct rb_callable_method_entry_struct *)cme);
- vm_ccs_push(klass, ccs, ci, cc);
- }
- }
- RB_VM_LOCK_LEAVE();
-
- return cc;
-}
-
static VALUE
gccct_hash(VALUE klass, ID mid)
{
return (klass >> 3) ^ (VALUE)mid;
}
-NOINLINE(static const struct rb_callcache *gccct_method_search_slowpath(rb_vm_t *vm, VALUE klass, ID mid, int argc, unsigned int index));
+NOINLINE(static const struct rb_callcache *gccct_method_search_slowpath(rb_vm_t *vm, VALUE klass, unsigned int index, const struct rb_callinfo * ci));
static const struct rb_callcache *
-gccct_method_search_slowpath(rb_vm_t *vm, VALUE klass, ID mid, int argc, unsigned int index)
+gccct_method_search_slowpath(rb_vm_t *vm, VALUE klass, unsigned int index, const struct rb_callinfo *ci)
{
- const rb_callable_method_entry_t *cme = rb_callable_method_entry(klass, mid);
- const struct rb_callcache *cc;
+ struct rb_call_data cd = {
+ .ci = ci,
+ .cc = NULL
+ };
- if (cme != NULL) {
- cc = cc_new(klass, mid, argc, cme);
- }
- else {
- cc = NULL;
- }
+ vm_search_method_slowpath0(vm->self, &cd, klass);
- return vm->global_cc_cache_table[index] = cc;
+ return vm->global_cc_cache_table[index] = cd.cc;
+}
+
+static void
+scope_to_ci(call_type scope, ID mid, int argc, struct rb_callinfo *ci)
+{
+ int flags = 0;
+
+ switch(scope) {
+ case CALL_PUBLIC:
+ break;
+ case CALL_FCALL:
+ flags |= VM_CALL_FCALL;
+ break;
+ case CALL_VCALL:
+ flags |= VM_CALL_VCALL;
+ break;
+ case CALL_PUBLIC_KW:
+ flags |= VM_CALL_KWARG;
+ break;
+ case CALL_FCALL_KW:
+ flags |= (VM_CALL_KWARG | VM_CALL_FCALL);
+ break;
+ }
+ *ci = VM_CI_ON_STACK(mid, flags, argc, NULL);
}
static inline const struct rb_callcache *
-gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, int argc)
+gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, const struct rb_callinfo *ci)
{
VALUE klass;
@@ -495,24 +475,24 @@ gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, int argc)
}
RB_DEBUG_COUNTER_INC(gccct_miss);
- return gccct_method_search_slowpath(vm, klass, mid, argc, index);
+ return gccct_method_search_slowpath(vm, klass, index, ci);
}
-/*!
- * \internal
+/**
+ * @internal
* calls the specified method.
*
* This function is called by functions in rb_call* family.
- * \param ec current execution context
- * \param recv receiver of the method
- * \param mid an ID that represents the name of the method
- * \param argc the number of method arguments
- * \param argv a pointer to an array of method arguments
- * \param scope
- * \param self self in the caller. Qundef means no self is considered and
+ * @param ec current execution context
+ * @param recv receiver of the method
+ * @param mid an ID that represents the name of the method
+ * @param argc the number of method arguments
+ * @param argv a pointer to an array of method arguments
+ * @param scope
+ * @param self self in the caller. Qundef means no self is considered and
* protected methods cannot be called
*
- * \note \a self is used in order to controlling access to protected methods.
+ * @note `self` is used in order to controlling access to protected methods.
*/
static inline VALUE
rb_call0(rb_execution_context_t *ec,
@@ -536,13 +516,16 @@ rb_call0(rb_execution_context_t *ec,
break;
}
- const struct rb_callcache *cc = gccct_method_search(ec, recv, mid, argc);
+ struct rb_callinfo ci;
+ scope_to_ci(scope, mid, argc, &ci);
+
+ const struct rb_callcache *cc = gccct_method_search(ec, recv, mid, &ci);
if (scope == CALL_PUBLIC) {
RB_DEBUG_COUNTER_INC(call0_public);
const rb_callable_method_entry_t *cc_cme = cc ? vm_cc_cme(cc) : NULL;
- const rb_callable_method_entry_t *cme = callable_method_entry_refeinements0(CLASS_OF(recv), mid, NULL, true, cc_cme);
+ const rb_callable_method_entry_t *cme = callable_method_entry_refinements0(CLASS_OF(recv), mid, NULL, true, cc_cme);
call_status = rb_method_call_status(ec, cme, scope, self);
if (UNLIKELY(call_status != MISSING_NONE)) {
@@ -793,29 +776,29 @@ uncallable_object(VALUE recv, ID mid)
if (SPECIAL_CONST_P(recv)) {
rb_raise(rb_eNotImpError,
- "method `%"PRIsVALUE"' called on unexpected immediate object (%p)",
+ "method '%"PRIsVALUE"' called on unexpected immediate object (%p)",
mname, (void *)recv);
}
else if ((flags = RBASIC(recv)->flags) == 0) {
rb_raise(rb_eNotImpError,
- "method `%"PRIsVALUE"' called on terminated object (%p)",
+ "method '%"PRIsVALUE"' called on terminated object (%p)",
mname, (void *)recv);
}
else if (!(typestr = rb_type_str(type = BUILTIN_TYPE(recv)))) {
rb_raise(rb_eNotImpError,
- "method `%"PRIsVALUE"' called on broken T_?""?""?(0x%02x) object"
+ "method '%"PRIsVALUE"' called on broken T_?""?""?(0x%02x) object"
" (%p flags=0x%"PRIxVALUE")",
mname, type, (void *)recv, flags);
}
else if (T_OBJECT <= type && type < T_NIL) {
rb_raise(rb_eNotImpError,
- "method `%"PRIsVALUE"' called on hidden %s object"
+ "method '%"PRIsVALUE"' called on hidden %s object"
" (%p flags=0x%"PRIxVALUE")",
mname, typestr, (void *)recv, flags);
}
else {
rb_raise(rb_eNotImpError,
- "method `%"PRIsVALUE"' called on unexpected %s object"
+ "method '%"PRIsVALUE"' called on unexpected %s object"
" (%p flags=0x%"PRIxVALUE")",
mname, typestr, (void *)recv, flags);
}
@@ -874,16 +857,16 @@ rb_method_call_status(rb_execution_context_t *ec, const rb_callable_method_entry
}
-/*!
- * \internal
+/**
+ * @internal
* calls the specified method.
*
* This function is called by functions in rb_call* family.
- * \param recv receiver
- * \param mid an ID that represents the name of the method
- * \param argc the number of method arguments
- * \param argv a pointer to an array of method arguments
- * \param scope
+ * @param recv receiver
+ * @param mid an ID that represents the name of the method
+ * @param argc the number of method arguments
+ * @param argv a pointer to an array of method arguments
+ * @param scope
*/
static inline VALUE
rb_call(VALUE recv, ID mid, int argc, const VALUE *argv, call_type scope)
@@ -949,7 +932,7 @@ rb_make_no_method_exception(VALUE exc, VALUE format, VALUE obj,
VALUE name = argv[0];
if (!format) {
- format = rb_fstring_lit("undefined method `%1$s' for %3$s%4$s");
+ format = rb_fstring_lit("undefined method '%1$s' for %3$s%4$s");
}
if (exc == rb_eNoMethodError) {
VALUE args = rb_ary_new4(argc - 1, argv + 1);
@@ -979,17 +962,17 @@ raise_method_missing(rb_execution_context_t *ec, int argc, const VALUE *argv, VA
stack_check(ec);
if (last_call_status & MISSING_PRIVATE) {
- format = rb_fstring_lit("private method `%1$s' called for %3$s%4$s");
+ format = rb_fstring_lit("private method '%1$s' called for %3$s%4$s");
}
else if (last_call_status & MISSING_PROTECTED) {
- format = rb_fstring_lit("protected method `%1$s' called for %3$s%4$s");
+ format = rb_fstring_lit("protected method '%1$s' called for %3$s%4$s");
}
else if (last_call_status & MISSING_VCALL) {
- format = rb_fstring_lit("undefined local variable or method `%1$s' for %3$s%4$s");
+ format = rb_fstring_lit("undefined local variable or method '%1$s' for %3$s%4$s");
exc = rb_eNameError;
}
else if (last_call_status & MISSING_SUPER) {
- format = rb_fstring_lit("super: no superclass method `%1$s' for %3$s%4$s");
+ format = rb_fstring_lit("super: no superclass method '%1$s' for %3$s%4$s");
}
{
@@ -1053,7 +1036,11 @@ static inline VALUE
rb_funcallv_scope(VALUE recv, ID mid, int argc, const VALUE *argv, call_type scope)
{
rb_execution_context_t *ec = GET_EC();
- const struct rb_callcache *cc = gccct_method_search(ec, recv, mid, argc);
+
+ struct rb_callinfo ci;
+ scope_to_ci(scope, mid, argc, &ci);
+
+ const struct rb_callcache *cc = gccct_method_search(ec, recv, mid, &ci);
VALUE self = ec->cfp->self;
if (LIKELY(cc) &&
@@ -1101,7 +1088,7 @@ rb_apply(VALUE recv, ID mid, VALUE args)
return ret;
}
argv = ALLOCA_N(VALUE, argc);
- MEMCPY(argv, RARRAY_CONST_PTR_TRANSIENT(args), VALUE, argc);
+ MEMCPY(argv, RARRAY_CONST_PTR(args), VALUE, argc);
return rb_funcallv(recv, mid, argc, argv);
}
@@ -1134,15 +1121,15 @@ rb_funcall(VALUE recv, ID mid, int n, ...)
return rb_funcallv(recv, mid, n, argv);
}
-/*!
+/**
* Calls a method only if it is the basic method of `ancestor`
* otherwise returns Qundef;
- * \param recv receiver of the method
- * \param mid an ID that represents the name of the method
- * \param ancestor the Class that defined the basic method
- * \param argc the number of arguments
- * \param argv pointer to an array of method arguments
- * \param kw_splat bool
+ * @param recv receiver of the method
+ * @param mid an ID that represents the name of the method
+ * @param ancestor the Class that defined the basic method
+ * @param argc the number of arguments
+ * @param argv pointer to an array of method arguments
+ * @param kw_splat bool
*/
VALUE
rb_check_funcall_basic_kw(VALUE recv, ID mid, VALUE ancestor, int argc, const VALUE *argv, int kw_splat)
@@ -1613,20 +1600,165 @@ rb_each(VALUE obj)
return rb_call(obj, idEach, 0, 0, CALL_FCALL);
}
-static VALUE eval_default_path;
+static VALUE eval_default_path = Qfalse;
+
+#define EVAL_LOCATION_MARK "eval at "
+#define EVAL_LOCATION_MARK_LEN (int)rb_strlen_lit(EVAL_LOCATION_MARK)
+
+static VALUE
+get_eval_default_path(void)
+{
+ int location_lineno;
+ VALUE location_path = rb_source_location(&location_lineno);
+ if (!NIL_P(location_path)) {
+ return rb_fstring(rb_sprintf("("EVAL_LOCATION_MARK"%"PRIsVALUE":%d)",
+ location_path, location_lineno));
+ }
+
+ if (!eval_default_path) {
+ eval_default_path = rb_fstring_lit("(eval)");
+ rb_vm_register_global_object(eval_default_path);
+ }
+ return eval_default_path;
+}
+
+static const rb_iseq_t *
+pm_eval_make_iseq(VALUE src, VALUE fname, int line,
+ const struct rb_block *base_block)
+{
+ const rb_iseq_t *const parent = vm_block_iseq(base_block);
+ const rb_iseq_t *iseq = parent;
+ VALUE name = rb_fstring_lit("<compiled>");
+ if (!fname) {
+ fname = rb_source_location(&line);
+ }
+
+ if (!UNDEF_P(fname)) {
+ if (!NIL_P(fname)) fname = rb_fstring(fname);
+ }
+ else {
+ fname = get_eval_default_path();
+ }
+
+ pm_parse_result_t result = { 0 };
+ pm_options_line_set(&result.options, line);
+
+ // Cout scopes, one for each parent iseq, plus one for our local scope
+ int scopes_count = 0;
+ do {
+ scopes_count++;
+ } while ((iseq = ISEQ_BODY(iseq)->parent_iseq) && (ISEQ_BODY(iseq)->type != ISEQ_TYPE_TOP));
+ pm_options_scopes_init(&result.options, scopes_count + 1);
+
+ // Walk over the scope tree, adding known locals at the correct depths. The
+ // scope array should be deepest -> shallowest. so lower indexes in the
+ // scopes array refer to root nodes on the tree, and higher indexes are the
+ // leaf nodes.
+ iseq = parent;
+ for (int scopes_index = 0; scopes_index < scopes_count; scopes_index++) {
+ int locals_count = ISEQ_BODY(iseq)->local_table_size;
+ pm_options_scope_t *options_scope = &result.options.scopes[scopes_count - scopes_index - 1];
+ pm_options_scope_init(options_scope, locals_count);
+
+ for (int local_index = 0; local_index < locals_count; local_index++) {
+ pm_string_t *scope_local = &options_scope->locals[local_index];
+ ID local = ISEQ_BODY(iseq)->local_table[local_index];
+
+ if (rb_is_local_id(local)) {
+ const char *name = rb_id2name(local);
+ size_t length = strlen(name);
+
+ // Explicitly skip numbered parameters. These should not be sent
+ // into the eval.
+ if (length == 2 && name[0] == '_' && name[1] >= '1' && name[1] <= '9') {
+ continue;
+ }
+
+ pm_string_constant_init(scope_local, name, strlen(name));
+ }
+ }
+
+ iseq = ISEQ_BODY(iseq)->parent_iseq;
+ }
+
+ // Add our empty local scope at the very end of the array for our eval
+ // scope's locals.
+ pm_options_scope_init(&result.options.scopes[scopes_count], 0);
+ VALUE error = pm_parse_string(&result, src, fname);
+
+ // If the parse failed, clean up and raise.
+ if (error != Qnil) {
+ pm_parse_result_free(&result);
+ rb_exc_raise(error);
+ }
+
+ // Create one scope node for each scope passed in, initialize the local
+ // lookup table with all the local variable information attached to the
+ // scope used by the parser.
+ pm_scope_node_t *node = &result.node;
+ iseq = parent;
+
+ for (int scopes_index = 0; scopes_index < scopes_count; scopes_index++) {
+ pm_scope_node_t *parent_scope = ruby_xcalloc(1, sizeof(pm_scope_node_t));
+ RUBY_ASSERT(parent_scope != NULL);
+
+ pm_options_scope_t *options_scope = &result.options.scopes[scopes_count - scopes_index - 1];
+ parent_scope->parser = &result.parser;
+ parent_scope->index_lookup_table = st_init_numtable();
+
+ int locals_count = ISEQ_BODY(iseq)->local_table_size;
+ parent_scope->local_table_for_iseq_size = locals_count;
+ pm_constant_id_list_init(&parent_scope->locals);
+
+ for (int local_index = 0; local_index < locals_count; local_index++) {
+ const pm_string_t *scope_local = &options_scope->locals[local_index];
+
+ pm_constant_id_t constant_id = 0;
+ if (pm_string_length(scope_local) > 0) {
+ constant_id = pm_constant_pool_insert_constant(
+ &result.parser.constant_pool, pm_string_source(scope_local),
+ pm_string_length(scope_local));
+ st_insert(parent_scope->index_lookup_table, (st_data_t)constant_id, (st_data_t)local_index);
+ }
+ pm_constant_id_list_append(&parent_scope->locals, constant_id);
+ }
+
+ node->previous = parent_scope;
+ node = parent_scope;
+ iseq = ISEQ_BODY(iseq)->parent_iseq;
+ }
+
+ iseq = pm_iseq_new_eval(&result.node, name, fname, Qnil, line, parent, 0);
+
+ pm_scope_node_t *prev = result.node.previous;
+ while (prev) {
+ pm_scope_node_t *next = prev->previous;
+ ruby_xfree(prev);
+ prev = next;
+ }
+
+ pm_parse_result_free(&result);
+ rb_exec_event_hook_script_compiled(GET_EC(), iseq, src);
+
+ return iseq;
+}
static const rb_iseq_t *
-eval_make_iseq(VALUE src, VALUE fname, int line, const rb_binding_t *bind,
+eval_make_iseq(VALUE src, VALUE fname, int line,
const struct rb_block *base_block)
{
+ if (*rb_ruby_prism_ptr()) {
+ return pm_eval_make_iseq(src, fname, line, base_block);
+ }
const VALUE parser = rb_parser_new();
const rb_iseq_t *const parent = vm_block_iseq(base_block);
rb_iseq_t *iseq = NULL;
+ VALUE vast;
rb_ast_t *ast;
int isolated_depth = 0;
// Conditionally enable coverage depending on the current mode:
- VALUE coverage_enabled = RBOOL(rb_get_coverage_mode() & COVERAGE_TARGET_EVAL);
+ int coverage_enabled = (rb_get_coverage_mode() & COVERAGE_TARGET_EVAL) != 0;
{
int depth = 1;
@@ -1653,24 +1785,19 @@ eval_make_iseq(VALUE src, VALUE fname, int line, const rb_binding_t *bind,
if (!NIL_P(fname)) fname = rb_fstring(fname);
}
else {
- fname = rb_fstring_lit("(eval)");
- if (!eval_default_path) {
- eval_default_path = rb_fstring_lit("(eval)");
- rb_gc_register_mark_object(eval_default_path);
- }
- fname = eval_default_path;
- coverage_enabled = Qfalse;
+ fname = get_eval_default_path();
+ coverage_enabled = FALSE;
}
rb_parser_set_context(parser, parent, FALSE);
- ast = rb_parser_compile_string_path(parser, fname, src, line);
- if (ast->body.root) {
- if (ast->body.compile_option == Qnil) {
- ast->body.compile_option = rb_obj_hide(rb_ident_hash_new());
- }
- rb_hash_aset(ast->body.compile_option, rb_sym_intern_ascii_cstr("coverage_enabled"), coverage_enabled);
+ if (ruby_vm_keep_script_lines) rb_parser_set_script_lines(parser);
+ vast = rb_parser_compile_string_path(parser, fname, src, line);
- iseq = rb_iseq_new_eval(&ast->body,
+ ast = rb_ruby_ast_data_get(vast);
+
+ if (ast->body.root) {
+ ast->body.coverage_enabled = coverage_enabled;
+ iseq = rb_iseq_new_eval(vast,
ISEQ_BODY(parent)->location.label,
fname, Qnil, line,
parent, isolated_depth);
@@ -1705,7 +1832,7 @@ eval_string_with_cref(VALUE self, VALUE src, rb_cref_t *cref, VALUE file, int li
block.as.captured.code.iseq = cfp->iseq;
block.type = block_type_iseq;
- iseq = eval_make_iseq(src, file, line, NULL, &block);
+ iseq = eval_make_iseq(src, file, line, &block);
if (!iseq) {
rb_exc_raise(ec->errinfo);
}
@@ -1726,7 +1853,7 @@ eval_string_with_scope(VALUE scope, VALUE src, VALUE file, int line)
{
rb_execution_context_t *ec = GET_EC();
rb_binding_t *bind = Check_TypedStruct(scope, &ruby_binding_data_type);
- const rb_iseq_t *iseq = eval_make_iseq(src, file, line, bind, &bind->block);
+ const rb_iseq_t *iseq = eval_make_iseq(src, file, line, &bind->block);
if (!iseq) {
rb_exc_raise(ec->errinfo);
}
@@ -1768,7 +1895,7 @@ rb_f_eval(int argc, const VALUE *argv, VALUE self)
int line = 1;
rb_scan_args(argc, argv, "13", &src, &scope, &vfile, &vline);
- SafeStringValue(src);
+ StringValue(src);
if (argc >= 3) {
StringValue(vfile);
}
@@ -1946,13 +2073,14 @@ rb_yield_refine_block(VALUE refinement, VALUE refinements)
else {
const struct rb_captured_block *captured = VM_BH_TO_ISEQ_BLOCK(block_handler);
struct rb_captured_block new_captured = *captured;
+ const VALUE *const argv = &new_captured.self; /* dummy to suppress nonnull warning from gcc */
VALUE new_block_handler = VM_BH_FROM_ISEQ_BLOCK(&new_captured);
const VALUE *ep = captured->ep;
rb_cref_t *cref = vm_cref_push(ec, refinement, ep, TRUE, FALSE);
CREF_REFINEMENTS_SET(cref, refinements);
VM_FORCE_WRITE_SPECIAL_CONST(&VM_CF_LEP(ec->cfp)[VM_ENV_DATA_INDEX_SPECVAL], new_block_handler);
new_captured.self = refinement;
- return vm_yield_with_cref(ec, 0, NULL, RB_NO_KEYWORDS, cref, FALSE);
+ return vm_yield_with_cref(ec, 0, argv, RB_NO_KEYWORDS, cref, FALSE);
}
}
@@ -1961,7 +2089,7 @@ static VALUE
eval_under(VALUE self, int singleton, VALUE src, VALUE file, int line)
{
rb_cref_t *cref = vm_cref_push(GET_EC(), self, NULL, FALSE, singleton);
- SafeStringValue(src);
+ StringValue(src);
return eval_string_with_cref(self, src, cref, file, line);
}
@@ -1974,19 +2102,24 @@ specific_eval(int argc, const VALUE *argv, VALUE self, int singleton, int kw_spl
return yield_under(self, singleton, 1, &self, kw_splat);
}
else {
- VALUE file = Qundef;
+ VALUE file = Qnil;
int line = 1;
VALUE code;
rb_check_arity(argc, 1, 3);
code = argv[0];
- SafeStringValue(code);
+ StringValue(code);
if (argc > 2)
line = NUM2INT(argv[2]);
if (argc > 1) {
file = argv[1];
if (!NIL_P(file)) StringValue(file);
}
+
+ if (NIL_P(file)) {
+ file = get_eval_default_path();
+ }
+
return eval_under(self, singleton, code, file, line);
}
}
@@ -2513,9 +2646,18 @@ rb_current_realfilepath(void)
if (path == eval_default_path) {
return Qnil;
}
- else {
- return path;
+
+ // [Feature #19755] implicit eval location is "(eval at #{__FILE__}:#{__LINE__})"
+ const long len = RSTRING_LEN(path);
+ if (len > EVAL_LOCATION_MARK_LEN+1) {
+ const char *const ptr = RSTRING_PTR(path);
+ if (ptr[len - 1] == ')' &&
+ memcmp(ptr, "("EVAL_LOCATION_MARK, EVAL_LOCATION_MARK_LEN+1) == 0) {
+ return Qnil;
+ }
}
+
+ return path;
}
return Qnil;
}
diff --git a/vm_exec.c b/vm_exec.c
index 36bacfd5e8..44d92e2225 100644
--- a/vm_exec.c
+++ b/vm_exec.c
@@ -11,39 +11,13 @@
#include <math.h>
-#if VM_COLLECT_USAGE_DETAILS
-static void vm_analysis_insn(int insn);
+#if USE_YJIT || USE_RJIT
+// The number of instructions executed on vm_exec_core. --yjit-stats uses this.
+uint64_t rb_vm_insns_count = 0;
#endif
-MAYBE_UNUSED(static void vm_insns_counter_count_insn(int insn));
-#if USE_INSNS_COUNTER
-static size_t rb_insns_counter[VM_INSTRUCTION_SIZE];
-
-static void
-vm_insns_counter_count_insn(int insn)
-{
- rb_insns_counter[insn]++;
-}
-
-__attribute__((destructor))
-static void
-vm_insns_counter_show_results_at_exit(void)
-{
- int insn_end = (ruby_vm_event_enabled_global_flags & ISEQ_TRACE_EVENTS)
- ? VM_INSTRUCTION_SIZE : VM_INSTRUCTION_SIZE / 2;
-
- size_t total = 0;
- for (int insn = 0; insn < insn_end; insn++)
- total += rb_insns_counter[insn];
-
- for (int insn = 0; insn < insn_end; insn++) {
- fprintf(stderr, "[RUBY_INSNS_COUNTER]\t%-32s%'12"PRIuSIZE" (%4.1f%%)\n",
- insn_name(insn), rb_insns_counter[insn],
- 100.0 * rb_insns_counter[insn] / total);
- }
-}
-#else
-static void vm_insns_counter_count_insn(int insn) {}
+#if VM_COLLECT_USAGE_DETAILS
+static void vm_analysis_insn(int insn);
#endif
#if VMDEBUG > 0
@@ -68,20 +42,8 @@ static void vm_insns_counter_count_insn(int insn) {}
#if !OPT_CALL_THREADED_CODE
static VALUE
-vm_exec_core(rb_execution_context_t *ec, VALUE initial)
+vm_exec_core(rb_execution_context_t *ec)
{
-
-#if OPT_STACK_CACHING
-#if 0
-#elif __GNUC__ && __x86_64__
- DECL_SC_REG(VALUE, a, "12");
- DECL_SC_REG(VALUE, b, "13");
-#else
- register VALUE reg_a;
- register VALUE reg_b;
-#endif
-#endif
-
#if defined(__GNUC__) && defined(__i386__)
DECL_SC_REG(const VALUE *, pc, "di");
DECL_SC_REG(rb_control_frame_t *, cfp, "si");
@@ -135,11 +97,6 @@ vm_exec_core(rb_execution_context_t *ec, VALUE initial)
reg_cfp = ec->cfp;
reg_pc = reg_cfp->pc;
-#if OPT_STACK_CACHING
- reg_a = initial;
- reg_b = 0;
-#endif
-
first:
INSN_DISPATCH();
/*****************/
@@ -155,7 +112,7 @@ vm_exec_core(rb_execution_context_t *ec, VALUE initial)
const void **
rb_vm_get_insns_address_table(void)
{
- return (const void **)vm_exec_core(0, 0);
+ return (const void **)vm_exec_core(0);
}
#else /* OPT_CALL_THREADED_CODE */
@@ -170,7 +127,7 @@ rb_vm_get_insns_address_table(void)
}
static VALUE
-vm_exec_core(rb_execution_context_t *ec, VALUE initial)
+vm_exec_core(rb_execution_context_t *ec)
{
register rb_control_frame_t *reg_cfp = ec->cfp;
rb_thread_t *th;
diff --git a/vm_exec.h b/vm_exec.h
index a00bd5027c..c3b7d4e488 100644
--- a/vm_exec.h
+++ b/vm_exec.h
@@ -21,11 +21,7 @@ typedef rb_iseq_t *ISEQ;
#define DEBUG_ENTER_INSN(insn) \
rb_vmdebug_debug_print_pre(ec, GET_CFP(), GET_PC());
-#if OPT_STACK_CACHING
-#define SC_REGS() , reg_a, reg_b
-#else
#define SC_REGS()
-#endif
#define DEBUG_END_INSN() \
rb_vmdebug_debug_print_post(ec, GET_CFP() SC_REGS());
@@ -40,10 +36,6 @@ typedef rb_iseq_t *ISEQ;
#define throwdebug if(0)ruby_debug_printf
/* #define throwdebug ruby_debug_printf */
-#ifndef USE_INSNS_COUNTER
-#define USE_INSNS_COUNTER 0
-#endif
-
/************************************************/
#if defined(DISPATCH_XXX)
error !
@@ -80,8 +72,7 @@ error !
(reg_cfp->pc - ISEQ_BODY(reg_cfp->iseq)->iseq_encoded), \
RSTRING_PTR(rb_iseq_path(reg_cfp->iseq)), \
rb_iseq_line_no(reg_cfp->iseq, reg_pc - ISEQ_BODY(reg_cfp->iseq)->iseq_encoded)); \
- } \
- if (USE_INSNS_COUNTER) vm_insns_counter_count_insn(BIN(insn));
+ }
#define INSN_DISPATCH_SIG(insn)
@@ -174,9 +165,19 @@ default: \
#define THROW_EXCEPTION(exc) return (VALUE)(exc)
#endif
+// Run the interpreter from the JIT
+#define VM_EXEC(ec, val) do { \
+ if (UNDEF_P(val)) { \
+ VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH); \
+ val = vm_exec(ec); \
+ } \
+} while (0)
+
+// Run the JIT from the interpreter
#define JIT_EXEC(ec, val) do { \
rb_jit_func_t func; \
- if (val == Qundef && (func = jit_compile(ec))) { \
+ /* don't run tailcalls since that breaks FINISH */ \
+ if (UNDEF_P(val) && GET_CFP() != ec->cfp && (func = jit_compile(ec))) { \
val = func(ec, ec->cfp); \
if (ec->tag->state) THROW_EXCEPTION(val); \
} \
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index cbcd51cce9..a1893b1ba2 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -93,6 +93,7 @@ rb_ec_stack_overflow(rb_execution_context_t *ec, int crit)
#endif
}
+static inline void stack_check(rb_execution_context_t *ec);
#if VM_CHECK_MODE > 0
static int
@@ -232,6 +233,23 @@ vm_check_frame(VALUE type,
static VALUE vm_stack_canary; /* Initialized later */
static bool vm_stack_canary_was_born = false;
+// Return the index of the instruction right before the given PC.
+// This is needed because insn_entry advances PC before the insn body.
+static unsigned int
+previous_insn_index(const rb_iseq_t *iseq, const VALUE *pc)
+{
+ unsigned int pos = 0;
+ while (pos < ISEQ_BODY(iseq)->iseq_size) {
+ int opcode = rb_vm_insn_addr2opcode((void *)ISEQ_BODY(iseq)->iseq_encoded[pos]);
+ unsigned int next_pos = pos + insn_len(opcode);
+ if (ISEQ_BODY(iseq)->iseq_encoded + next_pos == pc) {
+ return pos;
+ }
+ pos = next_pos;
+ }
+ rb_bug("failed to find the previous insn");
+}
+
void
rb_vm_check_canary(const rb_execution_context_t *ec, VALUE *sp)
{
@@ -258,15 +276,14 @@ rb_vm_check_canary(const rb_execution_context_t *ec, VALUE *sp)
}
const VALUE *orig = rb_iseq_original_iseq(iseq);
- const VALUE *encoded = ISEQ_BODY(iseq)->iseq_encoded;
- const ptrdiff_t pos = GET_PC() - encoded;
- const enum ruby_vminsn_type insn = (enum ruby_vminsn_type)orig[pos];
- const char *name = insn_name(insn);
const VALUE iseqw = rb_iseqw_new(iseq);
const VALUE inspection = rb_inspect(iseqw);
const char *stri = rb_str_to_cstr(inspection);
const VALUE disasm = rb_iseq_disasm(iseq);
const char *strd = rb_str_to_cstr(disasm);
+ const ptrdiff_t pos = previous_insn_index(iseq, GET_PC());
+ const enum ruby_vminsn_type insn = (enum ruby_vminsn_type)orig[pos];
+ const char *name = insn_name(insn);
/* rb_bug() is not capable of outputting this large contents. It
is designed to run form a SIGSEGV handler, which tends to be
@@ -336,6 +353,17 @@ vm_push_frame_debug_counter_inc(
#define vm_push_frame_debug_counter_inc(ec, cfp, t) /* void */
#endif
+// Return a poison value to be set above the stack top to verify leafness.
+VALUE
+rb_vm_stack_canary(void)
+{
+#if VM_CHECK_MODE > 0
+ return vm_stack_canary;
+#else
+ return 0;
+#endif
+}
+
STATIC_ASSERT(VM_ENV_DATA_INDEX_ME_CREF, VM_ENV_DATA_INDEX_ME_CREF == -2);
STATIC_ASSERT(VM_ENV_DATA_INDEX_SPECVAL, VM_ENV_DATA_INDEX_SPECVAL == -1);
STATIC_ASSERT(VM_ENV_DATA_INDEX_FLAGS, VM_ENV_DATA_INDEX_FLAGS == -0);
@@ -381,7 +409,6 @@ vm_push_frame(rb_execution_context_t *ec,
.self = self,
.ep = sp - 1,
.block_code = NULL,
- .__bp__ = sp, /* Store initial value of ep as bp to skip calculation cost of bp on JIT cancellation. */
#if VM_DEBUG_BP_CHECK
.bp_check = sp,
#endif
@@ -494,6 +521,7 @@ vm_env_write_slowpath(const VALUE *ep, int index, VALUE v)
RB_DEBUG_COUNTER_INC(lvar_set_slowpath);
}
+// YJIT assumes this function never runs GC
static inline void
vm_env_write(const VALUE *ep, int index, VALUE v)
{
@@ -506,6 +534,12 @@ vm_env_write(const VALUE *ep, int index, VALUE v)
}
}
+void
+rb_vm_env_write(const VALUE *ep, int index, VALUE v)
+{
+ vm_env_write(ep, index, v);
+}
+
VALUE
rb_vm_bh_to_procval(const rb_execution_context_t *ec, VALUE block_handler)
{
@@ -606,7 +640,12 @@ lep_svar_get(const rb_execution_context_t *ec, const VALUE *lep, rb_num_t key)
static struct vm_svar *
svar_new(VALUE obj)
{
- return (struct vm_svar *)rb_imemo_new(imemo_svar, Qnil, Qnil, Qnil, obj);
+ struct vm_svar *svar = IMEMO_NEW(struct vm_svar, imemo_svar, obj);
+ *((VALUE *)&svar->lastline) = Qnil;
+ *((VALUE *)&svar->backref) = Qnil;
+ *((VALUE *)&svar->others) = Qnil;
+
+ return svar;
}
static void
@@ -672,6 +711,30 @@ vm_getspecial(const rb_execution_context_t *ec, const VALUE *lep, rb_num_t key,
return val;
}
+static inline VALUE
+vm_backref_defined(const rb_execution_context_t *ec, const VALUE *lep, rb_num_t type)
+{
+ VALUE backref = lep_svar_get(ec, lep, VM_SVAR_BACKREF);
+ int nth = 0;
+
+ if (type & 0x01) {
+ switch (type >> 1) {
+ case '&':
+ case '`':
+ case '\'':
+ break;
+ case '+':
+ return rb_reg_last_defined(backref);
+ default:
+ rb_bug("unexpected back-ref");
+ }
+ }
+ else {
+ nth = (int)(type >> 1);
+ }
+ return rb_reg_nth_defined(nth, backref);
+}
+
PUREFUNC(static rb_callable_method_entry_t *check_method_entry(VALUE obj, int can_be_svar));
static rb_callable_method_entry_t *
check_method_entry(VALUE obj, int can_be_svar)
@@ -894,7 +957,7 @@ vm_get_const_key_cref(const VALUE *ep)
const rb_cref_t *key_cref = cref;
while (cref) {
- if (FL_TEST(CREF_CLASS(cref), FL_SINGLETON) ||
+ if (RCLASS_SINGLETON_P(CREF_CLASS(cref)) ||
RCLASS_EXT(CREF_CLASS(cref))->cloned) {
return key_cref;
}
@@ -1108,7 +1171,7 @@ vm_get_cvar_base(const rb_cref_t *cref, const rb_control_frame_t *cfp, int top_l
}
while (CREF_NEXT(cref) &&
- (NIL_P(CREF_CLASS(cref)) || FL_TEST(CREF_CLASS(cref), FL_SINGLETON) ||
+ (NIL_P(CREF_CLASS(cref)) || RCLASS_SINGLETON_P(CREF_CLASS(cref)) ||
CREF_PUSHED_BY_EVAL(cref) || CREF_SINGLETON(cref))) {
cref = CREF_NEXT(cref);
}
@@ -1197,7 +1260,7 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call
#if !SHAPE_IN_BASIC_FLAGS
shape_id = ivtbl->shape_id;
#endif
- ivar_list = ivtbl->ivptr;
+ ivar_list = ivtbl->as.shape.ivptr;
}
else {
return default_value;
@@ -1256,22 +1319,48 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call
}
#endif
- rb_shape_t *shape = rb_shape_get_shape_by_id(shape_id);
-
if (shape_id == OBJ_TOO_COMPLEX_SHAPE_ID) {
- if (!st_lookup(ROBJECT_IV_HASH(obj), id, &val)) {
+ st_table *table = NULL;
+ switch (BUILTIN_TYPE(obj)) {
+ case T_CLASS:
+ case T_MODULE:
+ table = (st_table *)RCLASS_IVPTR(obj);
+ break;
+
+ case T_OBJECT:
+ table = ROBJECT_IV_HASH(obj);
+ break;
+
+ default: {
+ struct gen_ivtbl *ivtbl;
+ if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) {
+ table = ivtbl->as.complex.table;
+ }
+ break;
+ }
+ }
+
+ if (!table || !st_lookup(table, id, &val)) {
val = default_value;
}
}
else {
- if (rb_shape_get_iv_index(shape, id, &index)) {
+ shape_id_t previous_cached_id = cached_id;
+ if (rb_shape_get_iv_index_with_hint(shape_id, id, &index, &cached_id)) {
// This fills in the cache with the shared cache object.
// "ent" is the shared cache object
- fill_ivar_cache(iseq, ic, cc, is_attr, index, shape_id);
+ if (cached_id != previous_cached_id) {
+ fill_ivar_cache(iseq, ic, cc, is_attr, index, cached_id);
+ }
- // We fetched the ivar list above
- val = ivar_list[index];
- RUBY_ASSERT(!UNDEF_P(val));
+ if (index == ATTR_INDEX_NOT_SET) {
+ val = default_value;
+ }
+ else {
+ // We fetched the ivar list above
+ val = ivar_list[index];
+ RUBY_ASSERT(!UNDEF_P(val));
+ }
}
else {
if (is_attr) {
@@ -1287,7 +1376,7 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call
}
- if (default_value != Qundef) {
+ if (!UNDEF_P(default_value)) {
RUBY_ASSERT(!UNDEF_P(val));
}
@@ -1329,45 +1418,19 @@ vm_setivar_slowpath(VALUE obj, ID id, VALUE val, const rb_iseq_t *iseq, IVC ic,
#if OPT_IC_FOR_IVAR
RB_DEBUG_COUNTER_INC(ivar_set_ic_miss);
- switch (BUILTIN_TYPE(obj)) {
- case T_OBJECT:
- {
- rb_check_frozen_internal(obj);
+ if (BUILTIN_TYPE(obj) == T_OBJECT) {
+ rb_check_frozen_internal(obj);
- attr_index_t index = rb_obj_ivar_set(obj, id, val);
+ attr_index_t index = rb_obj_ivar_set(obj, id, val);
- shape_id_t next_shape_id = ROBJECT_SHAPE_ID(obj);
-
- if (next_shape_id != OBJ_TOO_COMPLEX_SHAPE_ID) {
- populate_cache(index, next_shape_id, id, iseq, ic, cc, is_attr);
- }
+ shape_id_t next_shape_id = ROBJECT_SHAPE_ID(obj);
- RB_DEBUG_COUNTER_INC(ivar_set_obj_miss);
- return val;
+ if (next_shape_id != OBJ_TOO_COMPLEX_SHAPE_ID) {
+ populate_cache(index, next_shape_id, id, iseq, ic, cc, is_attr);
}
- case T_CLASS:
- case T_MODULE:
- break;
- default:
- {
- rb_ivar_set(obj, id, val);
- shape_id_t next_shape_id = rb_shape_get_shape_id(obj);
- rb_shape_t *next_shape = rb_shape_get_shape_by_id(next_shape_id);
- attr_index_t index;
-
- if (rb_shape_get_iv_index(next_shape, id, &index)) { // based off the hash stored in the transition tree
- if (index >= MAX_IVARS) {
- rb_raise(rb_eArgError, "too many instance variables");
- }
-
- populate_cache(index, next_shape_id, id, iseq, ic, cc, is_attr);
- }
- else {
- rb_bug("didn't find the id");
- }
- return val;
- }
+ RB_DEBUG_COUNTER_INC(ivar_set_obj_miss);
+ return val;
}
#endif
return rb_ivar_set(obj, id, val);
@@ -1400,21 +1463,13 @@ vm_setivar_default(VALUE obj, ID id, VALUE val, shape_id_t dest_shape_id, attr_i
// Cache hit case
if (shape_id == dest_shape_id) {
RUBY_ASSERT(dest_shape_id != INVALID_SHAPE_ID && shape_id != INVALID_SHAPE_ID);
-
- // Just get the IV table
- rb_gen_ivtbl_get(obj, 0, &ivtbl);
}
else if (dest_shape_id != INVALID_SHAPE_ID) {
- rb_shape_t * dest_shape = rb_shape_get_shape_by_id(dest_shape_id);
- shape_id_t source_shape_id = dest_shape->parent_id;
+ rb_shape_t *shape = rb_shape_get_shape_by_id(shape_id);
+ rb_shape_t *dest_shape = rb_shape_get_shape_by_id(dest_shape_id);
- if (shape_id == source_shape_id && dest_shape->edge_name == id && dest_shape->type == SHAPE_IVAR) {
- ivtbl = rb_ensure_generic_iv_list_size(obj, dest_shape, index + 1);
-#if SHAPE_IN_BASIC_FLAGS
- RBASIC_SET_SHAPE_ID(obj, dest_shape_id);
-#else
- RUBY_ASSERT(ivtbl->shape_id == dest_shape_id);
-#endif
+ if (shape_id == dest_shape->parent_id && dest_shape->edge_name == id && shape->capacity == dest_shape->capacity) {
+ RUBY_ASSERT(index < dest_shape->capacity);
}
else {
return Qundef;
@@ -1424,9 +1479,17 @@ vm_setivar_default(VALUE obj, ID id, VALUE val, shape_id_t dest_shape_id, attr_i
return Qundef;
}
- VALUE *ptr = ivtbl->ivptr;
+ rb_gen_ivtbl_get(obj, 0, &ivtbl);
- RB_OBJ_WRITE(obj, &ptr[index], val);
+ if (shape_id != dest_shape_id) {
+#if SHAPE_IN_BASIC_FLAGS
+ RBASIC_SET_SHAPE_ID(obj, dest_shape_id);
+#else
+ ivtbl->shape_id = dest_shape_id;
+#endif
+ }
+
+ RB_OBJ_WRITE(obj, &ivtbl->as.shape.ivptr[index], val);
RB_DEBUG_COUNTER_INC(ivar_set_ic_hit);
@@ -1450,10 +1513,11 @@ vm_setivar(VALUE obj, ID id, VALUE val, shape_id_t dest_shape_id, attr_index_t i
VM_ASSERT(!rb_ractor_shareable_p(obj));
}
else if (dest_shape_id != INVALID_SHAPE_ID) {
+ rb_shape_t *shape = rb_shape_get_shape_by_id(shape_id);
rb_shape_t *dest_shape = rb_shape_get_shape_by_id(dest_shape_id);
shape_id_t source_shape_id = dest_shape->parent_id;
- if (shape_id == source_shape_id && dest_shape->edge_name == id) {
+ if (shape_id == source_shape_id && dest_shape->edge_name == id && shape->capacity == dest_shape->capacity) {
RUBY_ASSERT(dest_shape_id != INVALID_SHAPE_ID && shape_id != INVALID_SHAPE_ID);
ROBJECT_SET_SHAPE_ID(obj, dest_shape_id);
@@ -1778,7 +1842,16 @@ vm_throw_start(const rb_execution_context_t *ec, rb_control_frame_t *const reg_c
}
}
break;
- case ISEQ_TYPE_EVAL:
+ case ISEQ_TYPE_EVAL: {
+ const rb_iseq_t *is = escape_cfp->iseq;
+ enum rb_iseq_type t = ISEQ_BODY(is)->type;
+ while (t == ISEQ_TYPE_RESCUE || t == ISEQ_TYPE_ENSURE || t == ISEQ_TYPE_EVAL) {
+ if (!(is = ISEQ_BODY(is)->parent_iseq)) break;
+ t = ISEQ_BODY(is)->type;
+ }
+ toplevel = t == ISEQ_TYPE_TOP || t == ISEQ_TYPE_MAIN;
+ break;
+ }
case ISEQ_TYPE_CLASS:
toplevel = 0;
break;
@@ -1835,11 +1908,9 @@ rb_vm_throw(const rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, rb_nu
}
static inline void
-vm_expandarray(VALUE *sp, VALUE ary, rb_num_t num, int flag)
+vm_expandarray(struct rb_control_frame_struct *cfp, VALUE ary, rb_num_t num, int flag)
{
int is_splat = flag & 0x01;
- rb_num_t space_size = num + is_splat;
- VALUE *base = sp - 1;
const VALUE *ptr;
rb_num_t len;
const VALUE obj = ary;
@@ -1850,11 +1921,11 @@ vm_expandarray(VALUE *sp, VALUE ary, rb_num_t num, int flag)
len = 1;
}
else {
- ptr = RARRAY_CONST_PTR_TRANSIENT(ary);
+ ptr = RARRAY_CONST_PTR(ary);
len = (rb_num_t)RARRAY_LEN(ary);
}
- if (space_size == 0) {
+ if (num + is_splat == 0) {
/* no space left on stack */
}
else if (flag & 0x02) {
@@ -1862,41 +1933,48 @@ vm_expandarray(VALUE *sp, VALUE ary, rb_num_t num, int flag)
rb_num_t i = 0, j;
if (len < num) {
- for (i=0; i<num-len; i++) {
- *base++ = Qnil;
+ for (i = 0; i < num - len; i++) {
+ *cfp->sp++ = Qnil;
}
}
- for (j=0; i<num; i++, j++) {
+
+ for (j = 0; i < num; i++, j++) {
VALUE v = ptr[len - j - 1];
- *base++ = v;
+ *cfp->sp++ = v;
}
+
if (is_splat) {
- *base = rb_ary_new4(len - j, ptr);
+ *cfp->sp++ = rb_ary_new4(len - j, ptr);
}
}
else {
/* normal: ary[num..-1], ary[num-2], ary[num-3], ..., ary[0] # top */
- rb_num_t i;
- VALUE *bptr = &base[space_size - 1];
-
- for (i=0; i<num; i++) {
- if (len <= i) {
- for (; i<num; i++) {
- *bptr-- = Qnil;
- }
- break;
- }
- *bptr-- = ptr[i];
- }
if (is_splat) {
if (num > len) {
- *bptr = rb_ary_new();
+ *cfp->sp++ = rb_ary_new();
}
else {
- *bptr = rb_ary_new4(len - num, ptr + num);
+ *cfp->sp++ = rb_ary_new4(len - num, ptr + num);
+ }
+ }
+
+ if (num > len) {
+ rb_num_t i = 0;
+ for (; i < num - len; i++) {
+ *cfp->sp++ = Qnil;
+ }
+
+ for (rb_num_t j = 0; i < num; i++, j++) {
+ *cfp->sp++ = ptr[len - j - 1];
+ }
+ }
+ else {
+ for (rb_num_t j = 0; j < num; j++) {
+ *cfp->sp++ = ptr[num - j - 1];
}
}
}
+
RB_GC_GUARD(ary);
}
@@ -1928,9 +2006,6 @@ vm_ccs_push(VALUE klass, struct rb_class_cc_entries *ccs, const struct rb_callin
if (! vm_cc_markable(cc)) {
return;
}
- else if (! vm_ci_markable(ci)) {
- return;
- }
if (UNLIKELY(ccs->len == ccs->capa)) {
if (ccs->capa == 0) {
@@ -1945,7 +2020,8 @@ vm_ccs_push(VALUE klass, struct rb_class_cc_entries *ccs, const struct rb_callin
VM_ASSERT(ccs->len < ccs->capa);
const int pos = ccs->len++;
- RB_OBJ_WRITE(klass, &ccs->entries[pos].ci, ci);
+ ccs->entries[pos].argc = vm_ci_argc(ci);
+ ccs->entries[pos].flag = vm_ci_flag(ci);
RB_OBJ_WRITE(klass, &ccs->entries[pos].cc, cc);
if (RB_DEBUG_COUNTER_SETMAX(ccs_maxlen, ccs->len)) {
@@ -1960,7 +2036,9 @@ rb_vm_ccs_dump(struct rb_class_cc_entries *ccs)
{
ruby_debug_printf("ccs:%p (%d,%d)\n", (void *)ccs, ccs->len, ccs->capa);
for (int i=0; i<ccs->len; i++) {
- vm_ci_dump(ccs->entries[i].ci);
+ ruby_debug_printf("CCS CI ID:flag:%x argc:%u\n",
+ ccs->entries[i].flag,
+ ccs->entries[i].argc);
rp(ccs->entries[i].cc);
}
}
@@ -1972,20 +2050,19 @@ vm_ccs_verify(struct rb_class_cc_entries *ccs, ID mid, VALUE klass)
VM_ASSERT(ccs->len <= ccs->capa);
for (int i=0; i<ccs->len; i++) {
- const struct rb_callinfo *ci = ccs->entries[i].ci;
const struct rb_callcache *cc = ccs->entries[i].cc;
- VM_ASSERT(vm_ci_p(ci));
- VM_ASSERT(vm_ci_mid(ci) == mid);
VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache));
VM_ASSERT(vm_cc_class_check(cc, klass));
VM_ASSERT(vm_cc_check_cme(cc, ccs->cme));
+ VM_ASSERT(!vm_cc_super_p(cc));
+ VM_ASSERT(!vm_cc_refinement_p(cc));
}
return TRUE;
}
#endif
-static const rb_callable_method_entry_t *check_overloaded_cme(const rb_callable_method_entry_t *cme, const struct rb_callinfo * const ci);
+const rb_callable_method_entry_t *rb_check_overloaded_cme(const rb_callable_method_entry_t *cme, const struct rb_callinfo * const ci);
static const struct rb_callcache *
vm_search_cc(const VALUE klass, const struct rb_callinfo * const ci)
@@ -1996,6 +2073,8 @@ vm_search_cc(const VALUE klass, const struct rb_callinfo * const ci)
VALUE ccs_data;
if (cc_tbl) {
+ // CCS data is keyed on method id, so we don't need the method id
+ // for doing comparisons in the `for` loop below.
if (rb_id_table_lookup(cc_tbl, mid, &ccs_data)) {
ccs = (struct rb_class_cc_entries *)ccs_data;
const int ccs_len = ccs->len;
@@ -2008,14 +2087,20 @@ vm_search_cc(const VALUE klass, const struct rb_callinfo * const ci)
else {
VM_ASSERT(vm_ccs_verify(ccs, mid, klass));
+ // We already know the method id is correct because we had
+ // to look up the ccs_data by method id. All we need to
+ // compare is argc and flag
+ unsigned int argc = vm_ci_argc(ci);
+ unsigned int flag = vm_ci_flag(ci);
+
for (int i=0; i<ccs_len; i++) {
- const struct rb_callinfo *ccs_ci = ccs->entries[i].ci;
+ unsigned int ccs_ci_argc = ccs->entries[i].argc;
+ unsigned int ccs_ci_flag = ccs->entries[i].flag;
const struct rb_callcache *ccs_cc = ccs->entries[i].cc;
- VM_ASSERT(vm_ci_p(ccs_ci));
VM_ASSERT(IMEMO_TYPE_P(ccs_cc, imemo_callcache));
- if (ccs_ci == ci) { // TODO: equality
+ if (ccs_ci_argc == argc && ccs_ci_flag == flag) {
RB_DEBUG_COUNTER_INC(cc_found_in_ccs);
VM_ASSERT(vm_cc_cme(ccs_cc)->called_id == mid);
@@ -2071,9 +2156,9 @@ vm_search_cc(const VALUE klass, const struct rb_callinfo * const ci)
}
}
- cme = check_overloaded_cme(cme, ci);
+ cme = rb_check_overloaded_cme(cme, ci);
- const struct rb_callcache *cc = vm_cc_new(klass, cme, vm_call_general);
+ const struct rb_callcache *cc = vm_cc_new(klass, cme, vm_call_general, cc_type_normal);
vm_ccs_push(klass, ccs, ci, cc);
VM_ASSERT(vm_cc_cme(cc) != NULL);
@@ -2119,7 +2204,9 @@ vm_search_method_slowpath0(VALUE cd_owner, struct rb_call_data *cd, VALUE klass)
cd->cc = cc;
const struct rb_callcache *empty_cc = &vm_empty_cc;
- if (cd_owner && cc != empty_cc) RB_OBJ_WRITTEN(cd_owner, Qundef, cc);
+ if (cd_owner && cc != empty_cc) {
+ RB_OBJ_WRITTEN(cd_owner, Qundef, cc);
+ }
#if USE_DEBUG_COUNTER
if (old_cc == empty_cc) {
@@ -2334,13 +2421,13 @@ opt_equality(const rb_iseq_t *cd_owner, VALUE recv, VALUE obj, CALL_DATA cd)
#undef EQ_UNREDEFINED_P
-static inline const struct rb_callcache *gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, int argc); // vm_eval.c
+static inline const struct rb_callcache *gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, const struct rb_callinfo *ci); // vm_eval.c
NOINLINE(static VALUE opt_equality_by_mid_slowpath(VALUE recv, VALUE obj, ID mid));
static VALUE
opt_equality_by_mid_slowpath(VALUE recv, VALUE obj, ID mid)
{
- const struct rb_callcache *cc = gccct_method_search(GET_EC(), recv, mid, 1);
+ const struct rb_callcache *cc = gccct_method_search(GET_EC(), recv, mid, &VM_CI_ON_STACK(mid, 0, 1, NULL));
if (cc && check_cfunc(vm_cc_cme(cc), rb_obj_equal)) {
return RBOOL(recv == obj);
@@ -2431,15 +2518,15 @@ double_cmp_ge(double a, double b)
return RBOOL(a >= b);
}
+// Copied by vm_dump.c
static inline VALUE *
vm_base_ptr(const rb_control_frame_t *cfp)
{
-#if 0 // we may optimize and use this once we confirm it does not spoil performance on JIT.
const rb_control_frame_t *prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
if (cfp->iseq && VM_FRAME_RUBYFRAME_P(cfp)) {
VALUE *bp = prev_cfp->sp + ISEQ_BODY(cfp->iseq)->local_table_size + VM_ENV_DATA_SIZE;
- if (ISEQ_BODY(cfp->iseq)->type == ISEQ_TYPE_METHOD) {
+ if (ISEQ_BODY(cfp->iseq)->type == ISEQ_TYPE_METHOD || VM_FRAME_BMETHOD_P(cfp)) {
/* adjust `self' */
bp += 1;
}
@@ -2456,9 +2543,12 @@ vm_base_ptr(const rb_control_frame_t *cfp)
else {
return NULL;
}
-#else
- return cfp->__bp__;
-#endif
+}
+
+VALUE *
+rb_vm_base_ptr(const rb_control_frame_t *cfp)
+{
+ return vm_base_ptr(cfp);
}
/* method call processes with call_info */
@@ -2540,7 +2630,7 @@ vm_caller_setup_arg_splat(rb_control_frame_t *cfp, struct rb_calling_info *calli
bool ret = false;
if (!NIL_P(ary)) {
- const VALUE *ptr = RARRAY_CONST_PTR_TRANSIENT(ary);
+ const VALUE *ptr = RARRAY_CONST_PTR(ary);
long len = RARRAY_LEN(ary);
int argc = calling->argc;
@@ -2616,8 +2706,10 @@ static inline VALUE
vm_caller_setup_keyword_hash(const struct rb_callinfo *ci, VALUE keyword_hash)
{
if (UNLIKELY(!RB_TYPE_P(keyword_hash, T_HASH))) {
- /* Convert a non-hash keyword splat to a new hash */
- keyword_hash = rb_hash_dup(rb_to_hash_type(keyword_hash));
+ if (keyword_hash != Qnil) {
+ /* Convert a non-hash keyword splat to a new hash */
+ keyword_hash = rb_hash_dup(rb_to_hash_type(keyword_hash));
+ }
}
else if (!IS_ARGS_KW_SPLAT_MUT(ci)) {
/* Convert a hash keyword splat to a new hash unless
@@ -2647,7 +2739,7 @@ CALLER_SETUP_ARG(struct rb_control_frame_struct *restrict cfp,
if (vm_caller_setup_arg_splat(cfp, calling, ary, max_args)) return;
// put kw
- if (!RHASH_EMPTY_P(kwh)) {
+ if (kwh != Qnil && !RHASH_EMPTY_P(kwh)) {
if (UNLIKELY(calling->heap_argv)) {
rb_ary_push(calling->heap_argv, kwh);
((struct RHash *)kwh)->basic.flags |= RHASH_PASS_AS_KEYWORDS;
@@ -2718,7 +2810,7 @@ check_keyword:
VM_ASSERT(calling->kw_splat == 1);
VALUE kwh = vm_caller_setup_keyword_hash(ci, cfp->sp[-1]);
- if (RHASH_EMPTY_P(kwh)) {
+ if (kwh == Qnil || RHASH_EMPTY_P(kwh)) {
cfp->sp--;
calling->argc--;
calling->kw_splat = 0;
@@ -2816,7 +2908,7 @@ static VALUE
vm_call_iseq_setup_kwparm_kwarg(rb_execution_context_t *ec, rb_control_frame_t *cfp,
struct rb_calling_info *calling)
{
- const struct rb_callinfo *ci = calling->ci;
+ const struct rb_callinfo *ci = calling->cd->ci;
const struct rb_callcache *cc = calling->cc;
VM_ASSERT(vm_ci_flag(ci) & VM_CALL_KWARG);
@@ -2843,7 +2935,7 @@ static VALUE
vm_call_iseq_setup_kwparm_nokwarg(rb_execution_context_t *ec, rb_control_frame_t *cfp,
struct rb_calling_info *calling)
{
- const struct rb_callinfo *MAYBE_UNUSED(ci) = calling->ci;
+ const struct rb_callinfo *MAYBE_UNUSED(ci) = calling->cd->ci;
const struct rb_callcache *cc = calling->cc;
VM_ASSERT((vm_ci_flag(ci) & VM_CALL_KWARG) == 0);
@@ -2871,7 +2963,7 @@ vm_call_iseq_setup_kwparm_nokwarg(rb_execution_context_t *ec, rb_control_frame_t
static VALUE builtin_invoker0(rb_execution_context_t *ec, VALUE self, const VALUE *argv, rb_insn_func_t funcptr);
static VALUE
-vm_call_single_noarg_inline_builtin(rb_execution_context_t *ec, rb_control_frame_t *cfp,
+vm_call_single_noarg_leaf_builtin(rb_execution_context_t *ec, rb_control_frame_t *cfp,
struct rb_calling_info *calling)
{
const struct rb_builtin_function *bf = calling->cc->aux_.bf;
@@ -2879,13 +2971,77 @@ vm_call_single_noarg_inline_builtin(rb_execution_context_t *ec, rb_control_frame
return builtin_invoker0(ec, calling->recv, NULL, (rb_insn_func_t)bf->func_ptr);
}
+VALUE rb_gen_method_name(VALUE owner, VALUE name); // in vm_backtrace.c
+
+static void
+warn_unused_block(const rb_callable_method_entry_t *cme, const rb_iseq_t *iseq, void *pc)
+{
+ rb_vm_t *vm = GET_VM();
+ st_table *dup_check_table = vm->unused_block_warning_table;
+ st_data_t key;
+
+ union {
+ VALUE v;
+ unsigned char b[SIZEOF_VALUE];
+ } k1 = {
+ .v = (VALUE)pc,
+ }, k2 = {
+ .v = (VALUE)cme->def,
+ };
+
+ // relax check
+ if (!vm->unused_block_warning_strict) {
+ key = (st_data_t)cme->def->original_id;
+
+ if (st_lookup(dup_check_table, key, NULL)) {
+ return;
+ }
+ }
+
+ // strict check
+ // make unique key from pc and me->def pointer
+ key = 0;
+ for (int i=0; i<SIZEOF_VALUE; i++) {
+ // fprintf(stderr, "k1:%3d k2:%3d\n", k1.b[i], k2.b[SIZEOF_VALUE-1-i]);
+ key |= (st_data_t)(k1.b[i] ^ k2.b[SIZEOF_VALUE-1-i]) << (8 * i);
+ }
+
+ if (0) {
+ fprintf(stderr, "SIZEOF_VALUE:%d\n", SIZEOF_VALUE);
+ fprintf(stderr, "pc:%p def:%p\n", pc, cme->def);
+ fprintf(stderr, "key:%p\n", (void *)key);
+ }
+
+ // duplication check
+ if (st_insert(dup_check_table, key, 1)) {
+ // already shown
+ }
+ else {
+ VALUE m_loc = rb_method_entry_location((const rb_method_entry_t *)cme);
+ VALUE name = rb_gen_method_name(cme->defined_class, ISEQ_BODY(iseq)->location.base_label);
+
+ if (!NIL_P(m_loc)) {
+ rb_warning("the block passed to '%"PRIsVALUE"' defined at %"PRIsVALUE":%"PRIsVALUE" may be ignored",
+ name, RARRAY_AREF(m_loc, 0), RARRAY_AREF(m_loc, 1));
+ }
+ else {
+ rb_warning("the block may be ignored because '%"PRIsVALUE"' does not use a block", name);
+ }
+ }
+}
+
static inline int
vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling,
const rb_iseq_t *iseq, VALUE *argv, int param_size, int local_size)
{
- const struct rb_callinfo *ci = calling->ci;
+ const struct rb_callinfo *ci = calling->cd->ci;
const struct rb_callcache *cc = calling->cc;
- bool cacheable_ci = vm_ci_markable(ci);
+
+ if (UNLIKELY(!ISEQ_BODY(iseq)->param.flags.use_block &&
+ calling->block_handler != VM_BLOCK_HANDLER_NONE &&
+ !(vm_ci_flag(calling->cd->ci) & VM_CALL_SUPER))) {
+ warn_unused_block(vm_cc_cme(cc), iseq, (void *)ec->cfp->pc);
+ }
if (LIKELY(!(vm_ci_flag(ci) & VM_CALL_KW_SPLAT))) {
if (LIKELY(rb_simple_iseq_p(iseq))) {
@@ -2897,15 +3053,15 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling,
argument_arity_error(ec, iseq, calling->argc, lead_num, lead_num);
}
- VM_ASSERT(ci == calling->ci);
+ VM_ASSERT(ci == calling->cd->ci);
VM_ASSERT(cc == calling->cc);
- if (cacheable_ci && vm_call_iseq_optimizable_p(ci, cc)) {
- if ((iseq->body->builtin_attrs & BUILTIN_ATTR_SINGLE_NOARG_INLINE) &&
+ if (vm_call_iseq_optimizable_p(ci, cc)) {
+ if ((iseq->body->builtin_attrs & BUILTIN_ATTR_SINGLE_NOARG_LEAF) &&
!(ruby_vm_event_flags & (RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN))) {
VM_ASSERT(iseq->body->builtin_attrs & BUILTIN_ATTR_LEAF);
vm_cc_bf_set(cc, (void *)iseq->body->iseq_encoded[1]);
- CC_SET_FASTPATH(cc, vm_call_single_noarg_inline_builtin, true);
+ CC_SET_FASTPATH(cc, vm_call_single_noarg_leaf_builtin, true);
}
else {
CC_SET_FASTPATH(cc, vm_call_iseq_setup_func(ci, param_size, local_size), true);
@@ -2930,12 +3086,12 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling,
if (LIKELY(!(vm_ci_flag(ci) & VM_CALL_TAILCALL))) {
CC_SET_FASTPATH(cc, vm_call_iseq_setup_normal_opt_start,
!IS_ARGS_SPLAT(ci) && !IS_ARGS_KEYWORD(ci) &&
- cacheable_ci && vm_call_cacheable(ci, cc));
+ vm_call_cacheable(ci, cc));
}
else {
CC_SET_FASTPATH(cc, vm_call_iseq_setup_tailcall_opt_start,
!IS_ARGS_SPLAT(ci) && !IS_ARGS_KEYWORD(ci) &&
- cacheable_ci && vm_call_cacheable(ci, cc));
+ vm_call_cacheable(ci, cc));
}
/* initialize opt vars for self-references */
@@ -2963,7 +3119,7 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling,
args_setup_kw_parameters(ec, iseq, ci_kws, ci_kw_len, ci_keywords, klocals);
CC_SET_FASTPATH(cc, vm_call_iseq_setup_kwparm_kwarg,
- cacheable_ci && vm_call_cacheable(ci, cc));
+ vm_call_cacheable(ci, cc));
return 0;
}
@@ -2976,7 +3132,7 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling,
if (klocals[kw_param->num] == INT2FIX(0)) {
/* copy from default_values */
CC_SET_FASTPATH(cc, vm_call_iseq_setup_kwparm_nokwarg,
- cacheable_ci && vm_call_cacheable(ci, cc));
+ vm_call_cacheable(ci, cc));
}
return 0;
@@ -3004,7 +3160,7 @@ static inline VALUE
vm_call_iseq_setup_2(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling,
int opt_pc, int param_size, int local_size)
{
- const struct rb_callinfo *ci = calling->ci;
+ const struct rb_callinfo *ci = calling->cd->ci;
const struct rb_callcache *cc = calling->cc;
if (LIKELY(!(vm_ci_flag(ci) & VM_CALL_TAILCALL))) {
@@ -3408,7 +3564,7 @@ vm_call_cfunc_with_frame_(rb_execution_context_t *ec, rb_control_frame_t *reg_cf
int argc, VALUE *argv, VALUE *stack_bottom)
{
RB_DEBUG_COUNTER_INC(ccf_cfunc_with_frame);
- const struct rb_callinfo *ci = calling->ci;
+ const struct rb_callinfo *ci = calling->cd->ci;
const struct rb_callcache *cc = calling->cc;
VALUE val;
const rb_callable_method_entry_t *me = vm_cc_cme(cc);
@@ -3449,6 +3605,24 @@ vm_call_cfunc_with_frame_(rb_execution_context_t *ec, rb_control_frame_t *reg_cf
return val;
}
+// Push a C method frame for a given cme. This is called when JIT code skipped
+// pushing a frame but the C method reached a point where a frame is needed.
+void
+rb_vm_push_cfunc_frame(const rb_callable_method_entry_t *cme, int recv_idx)
+{
+ VM_ASSERT(cme->def->type == VM_METHOD_TYPE_CFUNC);
+ rb_execution_context_t *ec = GET_EC();
+ VALUE *sp = ec->cfp->sp;
+ VALUE recv = *(sp - recv_idx - 1);
+ VALUE frame_type = VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL;
+ VALUE block_handler = VM_BLOCK_HANDLER_NONE;
+#if VM_CHECK_MODE > 0
+ // Clean up the stack canary since we're about to satisfy the "leaf or lazy push" assumption
+ *(GET_EC()->cfp->sp) = Qfalse;
+#endif
+ vm_push_frame(ec, NULL, frame_type, recv, block_handler, (VALUE)cme, 0, ec->cfp->sp, 0, 0);
+}
+
// If true, cc->call needs to include `CALLER_SETUP_ARG` (i.e. can't be skipped in fastpath)
bool
rb_splat_or_kwargs_p(const struct rb_callinfo *restrict ci)
@@ -3469,7 +3643,7 @@ vm_call_cfunc_with_frame(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp
static VALUE
vm_call_cfunc_other(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling)
{
- const struct rb_callinfo *ci = calling->ci;
+ const struct rb_callinfo *ci = calling->cd->ci;
RB_DEBUG_COUNTER_INC(ccf_cfunc_other);
CALLER_SETUP_ARG(reg_cfp, calling, ci, ALLOW_HEAP_ARGV_KEEP_KWSPLAT);
@@ -3544,7 +3718,7 @@ vm_call_cfunc_only_splat_kw(rb_execution_context_t *ec, rb_control_frame_t *reg_
RB_DEBUG_COUNTER_INC(ccf_cfunc_only_splat_kw);
VALUE keyword_hash = reg_cfp->sp[-1];
- if (RB_TYPE_P(keyword_hash, T_HASH) && RHASH_EMPTY_P(keyword_hash)) {
+ if (keyword_hash == Qnil || (RB_TYPE_P(keyword_hash, T_HASH) && RHASH_EMPTY_P(keyword_hash))) {
return vm_call_cfunc_array_argv(ec, reg_cfp, calling, 1, 0);
}
@@ -3554,7 +3728,7 @@ vm_call_cfunc_only_splat_kw(rb_execution_context_t *ec, rb_control_frame_t *reg_
static VALUE
vm_call_cfunc(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling)
{
- const struct rb_callinfo *ci = calling->ci;
+ const struct rb_callinfo *ci = calling->cd->ci;
RB_DEBUG_COUNTER_INC(ccf_cfunc);
if (IS_ARGS_SPLAT(ci)) {
@@ -3669,23 +3843,30 @@ vm_call_iseq_bmethod(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct
const struct rb_captured_block *captured = &block->as.captured;
const rb_iseq_t *iseq = rb_iseq_check(captured->code.iseq);
- int i, opt_pc;
-
- VALUE *sp = cfp->sp - calling->argc - 1;
- for (i = 0; i < calling->argc; i++) {
- sp[i] = sp[i+1];
- }
+ VALUE * const argv = cfp->sp - calling->argc;
+ const int arg_size = ISEQ_BODY(iseq)->param.size;
- if (vm_ci_flag(calling->ci) & VM_CALL_ARGS_SIMPLE) {
- opt_pc = vm_callee_setup_block_arg(ec, calling, calling->ci, iseq, sp, arg_setup_method);
+ int opt_pc;
+ if (vm_ci_flag(calling->cd->ci) & VM_CALL_ARGS_SIMPLE) {
+ opt_pc = vm_callee_setup_block_arg(ec, calling, calling->cd->ci, iseq, argv, arg_setup_method);
}
else {
- opt_pc = setup_parameters_complex(ec, iseq, calling, calling->ci, sp, arg_setup_method);
+ opt_pc = setup_parameters_complex(ec, iseq, calling, calling->cd->ci, argv, arg_setup_method);
}
- cfp->sp = sp;
- return invoke_bmethod(ec, iseq, calling->recv, captured, cme,
- VM_FRAME_MAGIC_BLOCK | VM_FRAME_FLAG_LAMBDA, opt_pc);
+ cfp->sp = argv - 1; // -1 for the receiver
+
+ vm_push_frame(ec, iseq,
+ VM_FRAME_MAGIC_BLOCK | VM_FRAME_FLAG_BMETHOD | VM_FRAME_FLAG_LAMBDA,
+ calling->recv,
+ VM_GUARDED_PREV_EP(captured->ep),
+ (VALUE)cme,
+ ISEQ_BODY(iseq)->iseq_encoded + opt_pc,
+ argv + arg_size,
+ ISEQ_BODY(iseq)->local_table_size - arg_size,
+ ISEQ_BODY(iseq)->stack_max);
+
+ return Qundef;
}
static VALUE
@@ -3695,7 +3876,7 @@ vm_call_noniseq_bmethod(rb_execution_context_t *ec, rb_control_frame_t *cfp, str
VALUE *argv;
int argc;
- CALLER_SETUP_ARG(cfp, calling, calling->ci, ALLOW_HEAP_ARGV);
+ CALLER_SETUP_ARG(cfp, calling, calling->cd->ci, ALLOW_HEAP_ARGV);
if (UNLIKELY(calling->heap_argv)) {
argv = RARRAY_PTR(calling->heap_argv);
cfp->sp -= 2;
@@ -3767,7 +3948,7 @@ aliased_callable_method_entry(const rb_callable_method_entry_t *me)
VM_ASSERT(RB_TYPE_P(orig_me->owner, T_MODULE));
cme = rb_method_entry_complement_defined_class(orig_me, me->called_id, defined_class);
- if (me->def->alias_count + me->def->complemented_count == 0) {
+ if (me->def->reference_count == 1) {
RB_OBJ_WRITE(me, &me->def->body.alias.original_me, cme);
}
else {
@@ -3881,7 +4062,10 @@ vm_call_symbol(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
}
}
- calling->ci = &VM_CI_ON_STACK(mid, flags, argc, vm_ci_kwarg(ci));
+ calling->cd = &(struct rb_call_data) {
+ .ci = &VM_CI_ON_STACK(mid, flags, argc, vm_ci_kwarg(ci)),
+ .cc = NULL,
+ };
calling->cc = &VM_CC_ON_STACK(klass,
vm_call_general,
{ .method_missing_reason = missing_reason },
@@ -3916,7 +4100,7 @@ vm_call_symbol(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
static VALUE
vm_call_opt_send0(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, int flags)
{
- const struct rb_callinfo *ci = calling->ci;
+ const struct rb_callinfo *ci = calling->cd->ci;
int i;
VALUE sym;
@@ -3954,7 +4138,7 @@ static VALUE
vm_call_opt_send_complex(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling)
{
RB_DEBUG_COUNTER_INC(ccf_opt_send_complex);
- const struct rb_callinfo *ci = calling->ci;
+ const struct rb_callinfo *ci = calling->cd->ci;
int flags = VM_CALL_FCALL;
VALUE sym;
@@ -3979,7 +4163,7 @@ static VALUE
vm_call_opt_send_simple(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling)
{
RB_DEBUG_COUNTER_INC(ccf_opt_send_simple);
- return vm_call_opt_send0(ec, reg_cfp, calling, vm_ci_flag(calling->ci) | VM_CALL_FCALL);
+ return vm_call_opt_send0(ec, reg_cfp, calling, vm_ci_flag(calling->cd->ci) | VM_CALL_FCALL);
}
static VALUE
@@ -3987,7 +4171,7 @@ vm_call_opt_send(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct
{
RB_DEBUG_COUNTER_INC(ccf_opt_send);
- const struct rb_callinfo *ci = calling->ci;
+ const struct rb_callinfo *ci = calling->cd->ci;
int flags = vm_ci_flag(ci);
if (UNLIKELY(!(flags & VM_CALL_ARGS_SIMPLE) &&
@@ -4024,7 +4208,10 @@ vm_call_method_missing_body(rb_execution_context_t *ec, rb_control_frame_t *reg_
INC_SP(1);
ec->method_missing_reason = reason;
- calling->ci = &VM_CI_ON_STACK(idMethodMissing, flag, argc, vm_ci_kwarg(orig_ci));
+ calling->cd = &(struct rb_call_data) {
+ .ci = &VM_CI_ON_STACK(idMethodMissing, flag, argc, vm_ci_kwarg(orig_ci)),
+ .cc = NULL,
+ };
calling->cc = &VM_CC_ON_STACK(Qundef, vm_call_general, {{ 0 }},
rb_callable_method_entry_without_refinements(CLASS_OF(calling->recv), idMethodMissing, NULL));
return vm_call_method(ec, reg_cfp, calling);
@@ -4033,7 +4220,7 @@ vm_call_method_missing_body(rb_execution_context_t *ec, rb_control_frame_t *reg_
static VALUE
vm_call_method_missing(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling)
{
- return vm_call_method_missing_body(ec, reg_cfp, calling, calling->ci, vm_cc_cmethod_missing_reason(calling->cc));
+ return vm_call_method_missing_body(ec, reg_cfp, calling, calling->cd->ci, vm_cc_cmethod_missing_reason(calling->cc));
}
static const rb_callable_method_entry_t *refined_method_callable_without_refinement(const rb_callable_method_entry_t *me);
@@ -4042,7 +4229,7 @@ vm_call_zsuper(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_ca
{
klass = RCLASS_SUPER(klass);
- const rb_callable_method_entry_t *cme = klass ? rb_callable_method_entry(klass, vm_ci_mid(calling->ci)) : NULL;
+ const rb_callable_method_entry_t *cme = klass ? rb_callable_method_entry(klass, vm_ci_mid(calling->cd->ci)) : NULL;
if (cme == NULL) {
return vm_call_method_nome(ec, cfp, calling);
}
@@ -4111,7 +4298,7 @@ refined_method_callable_without_refinement(const rb_callable_method_entry_t *me)
static const rb_callable_method_entry_t *
search_refined_method(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling)
{
- ID mid = vm_ci_mid(calling->ci);
+ ID mid = vm_ci_mid(calling->cd->ci);
const rb_cref_t *cref = vm_get_cref(cfp->ep);
const struct rb_callcache * const cc = calling->cc;
const rb_callable_method_entry_t *cme = vm_cc_cme(cc);
@@ -4158,12 +4345,19 @@ search_refined_method(rb_execution_context_t *ec, rb_control_frame_t *cfp, struc
static VALUE
vm_call_refined(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling)
{
- struct rb_callcache *ref_cc = &VM_CC_ON_STACK(Qundef, vm_call_general, {{ 0 }},
- search_refined_method(ec, cfp, calling));
+ const rb_callable_method_entry_t *ref_cme = search_refined_method(ec, cfp, calling);
- if (vm_cc_cme(ref_cc)) {
- calling->cc= ref_cc;
- return vm_call_method(ec, cfp, calling);
+ if (ref_cme) {
+ if (calling->cd->cc) {
+ const struct rb_callcache *cc = calling->cc = vm_cc_new(vm_cc_cme(calling->cc)->defined_class, ref_cme, vm_call_general, cc_type_refinement);
+ RB_OBJ_WRITE(cfp->iseq, &calling->cd->cc, cc);
+ return vm_call_method(ec, cfp, calling);
+ }
+ else {
+ struct rb_callcache *ref_cc = &VM_CC_ON_STACK(Qundef, vm_call_general, {{ 0 }}, ref_cme);
+ calling->cc= ref_cc;
+ return vm_call_method(ec, cfp, calling);
+ }
}
else {
return vm_call_method_nome(ec, cfp, calling);
@@ -4194,7 +4388,7 @@ vm_call_opt_call(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct
{
RB_DEBUG_COUNTER_INC(ccf_opt_call);
- const struct rb_callinfo *ci = calling->ci;
+ const struct rb_callinfo *ci = calling->cd->ci;
VALUE procval = calling->recv;
return vm_invoke_block_opt_call(ec, reg_cfp, calling, ci, VM_BH_FROM_PROC(procval));
}
@@ -4205,7 +4399,7 @@ vm_call_opt_block_call(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
RB_DEBUG_COUNTER_INC(ccf_opt_block_call);
VALUE block_handler = VM_ENV_BLOCK_HANDLER(VM_CF_LEP(reg_cfp));
- const struct rb_callinfo *ci = calling->ci;
+ const struct rb_callinfo *ci = calling->cd->ci;
if (BASIC_OP_UNREDEFINED_P(BOP_CALL, PROC_REDEFINED_OP_FLAG)) {
return vm_invoke_block_opt_call(ec, reg_cfp, calling, ci, block_handler);
@@ -4270,6 +4464,19 @@ vm_call_opt_struct_aset(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
NOINLINE(static VALUE vm_call_optimized(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling,
const struct rb_callinfo *ci, const struct rb_callcache *cc));
+#define VM_CALL_METHOD_ATTR(var, func, nohook) \
+ if (UNLIKELY(ruby_vm_event_flags & (RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN))) { \
+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_CALL, calling->recv, vm_cc_cme(cc)->def->original_id, \
+ vm_ci_mid(ci), vm_cc_cme(cc)->owner, Qundef); \
+ var = func; \
+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_RETURN, calling->recv, vm_cc_cme(cc)->def->original_id, \
+ vm_ci_mid(ci), vm_cc_cme(cc)->owner, (var)); \
+ } \
+ else { \
+ nohook; \
+ var = func; \
+ }
+
static VALUE
vm_call_optimized(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling,
const struct rb_callinfo *ci, const struct rb_callcache *cc)
@@ -4284,43 +4491,43 @@ vm_call_optimized(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb
case OPTIMIZED_METHOD_TYPE_BLOCK_CALL:
CC_SET_FASTPATH(cc, vm_call_opt_block_call, TRUE);
return vm_call_opt_block_call(ec, cfp, calling);
- case OPTIMIZED_METHOD_TYPE_STRUCT_AREF:
+ case OPTIMIZED_METHOD_TYPE_STRUCT_AREF: {
CALLER_SETUP_ARG(cfp, calling, ci, 0);
rb_check_arity(calling->argc, 0, 0);
- CC_SET_FASTPATH(cc, vm_call_opt_struct_aref, (vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE));
- return vm_call_opt_struct_aref(ec, cfp, calling);
- case OPTIMIZED_METHOD_TYPE_STRUCT_ASET:
+ VALUE v;
+ VM_CALL_METHOD_ATTR(v,
+ vm_call_opt_struct_aref(ec, cfp, calling),
+ set_vm_cc_ivar(cc); \
+ CC_SET_FASTPATH(cc, vm_call_opt_struct_aref, (vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE)))
+ return v;
+ }
+ case OPTIMIZED_METHOD_TYPE_STRUCT_ASET: {
CALLER_SETUP_ARG(cfp, calling, ci, 1);
rb_check_arity(calling->argc, 1, 1);
- CC_SET_FASTPATH(cc, vm_call_opt_struct_aset, (vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE));
- return vm_call_opt_struct_aset(ec, cfp, calling);
+
+ VALUE v;
+ VM_CALL_METHOD_ATTR(v,
+ vm_call_opt_struct_aset(ec, cfp, calling),
+ set_vm_cc_ivar(cc); \
+ CC_SET_FASTPATH(cc, vm_call_opt_struct_aset, (vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE)))
+ return v;
+ }
default:
rb_bug("vm_call_method: unsupported optimized method type (%d)", vm_cc_cme(cc)->def->body.optimized.type);
}
}
-#define VM_CALL_METHOD_ATTR(var, func, nohook) \
- if (UNLIKELY(ruby_vm_event_flags & (RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN))) { \
- EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_CALL, calling->recv, vm_cc_cme(cc)->def->original_id, \
- vm_ci_mid(ci), vm_cc_cme(cc)->owner, Qundef); \
- var = func; \
- EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_RETURN, calling->recv, vm_cc_cme(cc)->def->original_id, \
- vm_ci_mid(ci), vm_cc_cme(cc)->owner, (var)); \
- } \
- else { \
- nohook; \
- var = func; \
- }
-
static VALUE
vm_call_method_each_type(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling)
{
- const struct rb_callinfo *ci = calling->ci;
+ const struct rb_callinfo *ci = calling->cd->ci;
const struct rb_callcache *cc = calling->cc;
const rb_callable_method_entry_t *cme = vm_cc_cme(cc);
VALUE v;
+ VM_ASSERT(! METHOD_ENTRY_INVALIDATED(cme));
+
switch (cme->def->type) {
case VM_METHOD_TYPE_ISEQ:
CC_SET_FASTPATH(cc, vm_call_iseq_setup, TRUE);
@@ -4413,7 +4620,7 @@ static VALUE
vm_call_method_nome(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling)
{
/* method missing */
- const struct rb_callinfo *ci = calling->ci;
+ const struct rb_callinfo *ci = calling->cd->ci;
const int stat = ci_missing_reason(ci);
if (vm_ci_mid(ci) == idMethodMissing) {
@@ -4447,7 +4654,7 @@ vm_defined_class_for_protected_call(const rb_callable_method_entry_t *me)
static inline VALUE
vm_call_method(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling)
{
- const struct rb_callinfo *ci = calling->ci;
+ const struct rb_callinfo *ci = calling->cd->ci;
const struct rb_callcache *cc = calling->cc;
VM_ASSERT(callable_method_entry_p(vm_cc_cme(cc)));
@@ -4603,7 +4810,7 @@ vm_search_super_method(const rb_control_frame_t *reg_cfp, struct rb_call_data *c
if (!klass) {
/* bound instance method of module */
- cc = vm_cc_new(klass, NULL, vm_call_method_missing);
+ cc = vm_cc_new(klass, NULL, vm_call_method_missing, cc_type_super);
RB_OBJ_WRITE(reg_cfp->iseq, &cd->cc, cc);
}
else {
@@ -4618,7 +4825,7 @@ vm_search_super_method(const rb_control_frame_t *reg_cfp, struct rb_call_data *c
else if (cached_cme->called_id != mid) {
const rb_callable_method_entry_t *cme = rb_callable_method_entry(klass, mid);
if (cme) {
- cc = vm_cc_new(klass, cme, vm_call_super_method);
+ cc = vm_cc_new(klass, cme, vm_call_super_method, cc_type_super);
RB_OBJ_WRITE(reg_cfp->iseq, &cd->cc, cc);
}
else {
@@ -5064,10 +5271,8 @@ vm_defined(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, rb_num_t op_
}
}
break;
- case DEFINED_REF:{
- return vm_getspecial(ec, GET_LEP(), Qfalse, FIX2INT(obj)) != Qnil;
- break;
- }
+ case DEFINED_REF:
+ return RTEST(vm_backref_defined(ec, GET_LEP(), FIX2INT(obj)));
default:
rb_bug("unimplemented defined? type (VM)");
break;
@@ -5119,15 +5324,29 @@ vm_concat_array(VALUE ary1, VALUE ary2st)
if (NIL_P(tmp1)) {
tmp1 = rb_ary_new3(1, ary1);
}
+ if (tmp1 == ary1) {
+ tmp1 = rb_ary_dup(ary1);
+ }
if (NIL_P(tmp2)) {
- tmp2 = rb_ary_new3(1, ary2);
+ return rb_ary_push(tmp1, ary2);
+ } else {
+ return rb_ary_concat(tmp1, tmp2);
}
+}
- if (tmp1 == ary1) {
- tmp1 = rb_ary_dup(ary1);
+static VALUE
+vm_concat_to_array(VALUE ary1, VALUE ary2st)
+{
+ /* ary1 must be a newly created array */
+ const VALUE ary2 = ary2st;
+ VALUE tmp2 = rb_check_to_array(ary2);
+
+ if (NIL_P(tmp2)) {
+ return rb_ary_push(ary1, ary2);
+ } else {
+ return rb_ary_concat(ary1, tmp2);
}
- return rb_ary_concat(tmp1, tmp2);
}
// YJIT implementation is using the C function
@@ -5138,6 +5357,12 @@ rb_vm_concat_array(VALUE ary1, VALUE ary2st)
return vm_concat_array(ary1, ary2st);
}
+VALUE
+rb_vm_concat_to_array(VALUE ary1, VALUE ary2st)
+{
+ return vm_concat_to_array(ary1, ary2st);
+}
+
static VALUE
vm_splat_array(VALUE flag, VALUE ary)
{
@@ -5185,6 +5410,12 @@ vm_check_match(rb_execution_context_t *ec, VALUE target, VALUE pattern, rb_num_t
}
}
+VALUE
+rb_vm_check_match(rb_execution_context_t *ec, VALUE target, VALUE pattern, rb_num_t flag)
+{
+ return vm_check_match(ec, target, pattern, flag);
+}
+
static VALUE
vm_check_keyword(lindex_t bits, lindex_t idx, const VALUE *ep)
{
@@ -5432,7 +5663,7 @@ vm_define_method(const rb_execution_context_t *ec, VALUE obj, ID id, VALUE iseqv
rb_add_method_iseq(klass, id, (const rb_iseq_t *)iseqval, cref, visi);
// Set max_iv_count on klasses based on number of ivar sets that are in the initialize method
- if (id == rb_intern("initialize") && klass != rb_cObject && RB_TYPE_P(klass, T_CLASS) && (rb_get_alloc_func(klass) == rb_class_allocate_instance)) {
+ if (id == idInitialize && klass != rb_cObject && RB_TYPE_P(klass, T_CLASS) && (rb_get_alloc_func(klass) == rb_class_allocate_instance)) {
RCLASS_EXT(klass)->max_iv_count = rb_estimate_iv_count(klass, (const rb_iseq_t *)iseqval);
}
@@ -5448,7 +5679,7 @@ vm_invokeblock_i(struct rb_execution_context_struct *ec,
struct rb_control_frame_struct *reg_cfp,
struct rb_calling_info *calling)
{
- const struct rb_callinfo *ci = calling->ci;
+ const struct rb_callinfo *ci = calling->cd->ci;
VALUE block_handler = VM_CF_BLOCK_HANDLER(GET_CFP());
if (block_handler == VM_BLOCK_HANDLER_NONE) {
@@ -5483,7 +5714,7 @@ vm_sendish(
.kw_splat = IS_ARGS_KW_SPLAT(ci) > 0,
.recv = recv,
.argc = argc,
- .ci = ci,
+ .cd = cd,
};
switch (method_explorer) {
@@ -5493,7 +5724,6 @@ vm_sendish(
break;
case mexp_search_super:
calling.cc = cc = vm_search_super_method(reg_cfp, cd, recv);
- calling.ci = cd->ci; // TODO: does it safe?
val = vm_cc_call(cc)(ec, GET_CFP(), &calling);
break;
case mexp_search_invokeblock:
@@ -5503,6 +5733,46 @@ vm_sendish(
return val;
}
+VALUE
+rb_vm_send(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, CALL_DATA cd, ISEQ blockiseq)
+{
+ stack_check(ec);
+ VALUE bh = vm_caller_setup_arg_block(ec, GET_CFP(), cd->ci, blockiseq, false);
+ VALUE val = vm_sendish(ec, GET_CFP(), cd, bh, mexp_search_method);
+ VM_EXEC(ec, val);
+ return val;
+}
+
+VALUE
+rb_vm_opt_send_without_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, CALL_DATA cd)
+{
+ stack_check(ec);
+ VALUE bh = VM_BLOCK_HANDLER_NONE;
+ VALUE val = vm_sendish(ec, GET_CFP(), cd, bh, mexp_search_method);
+ VM_EXEC(ec, val);
+ return val;
+}
+
+VALUE
+rb_vm_invokesuper(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, CALL_DATA cd, ISEQ blockiseq)
+{
+ stack_check(ec);
+ VALUE bh = vm_caller_setup_arg_block(ec, GET_CFP(), cd->ci, blockiseq, true);
+ VALUE val = vm_sendish(ec, GET_CFP(), cd, bh, mexp_search_super);
+ VM_EXEC(ec, val);
+ return val;
+}
+
+VALUE
+rb_vm_invokeblock(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, CALL_DATA cd)
+{
+ stack_check(ec);
+ VALUE bh = VM_BLOCK_HANDLER_NONE;
+ VALUE val = vm_sendish(ec, GET_CFP(), cd, bh, mexp_search_invokeblock);
+ VM_EXEC(ec, val);
+ return val;
+}
+
/* object.c */
VALUE rb_nil_to_s(VALUE);
VALUE rb_true_to_s(VALUE);
@@ -5734,7 +6004,7 @@ vm_ic_update(const rb_iseq_t *iseq, IC ic, VALUE val, const VALUE *reg_ep, const
return;
}
- struct iseq_inline_constant_cache_entry *ice = (struct iseq_inline_constant_cache_entry *)rb_imemo_new(imemo_constcache, 0, 0, 0, 0);
+ struct iseq_inline_constant_cache_entry *ice = IMEMO_NEW(struct iseq_inline_constant_cache_entry, imemo_constcache, 0);
RB_OBJ_WRITE(ice, &ice->value, val);
ice->ic_cref = vm_get_const_key_cref(reg_ep);
if (rb_ractor_shareable_p(val)) ice->flags |= IMEMO_CONST_CACHE_SHAREABLE;
@@ -5746,6 +6016,28 @@ vm_ic_update(const rb_iseq_t *iseq, IC ic, VALUE val, const VALUE *reg_ep, const
rb_rjit_constant_ic_update(iseq, ic, pos);
}
+VALUE
+rb_vm_opt_getconstant_path(rb_execution_context_t *ec, rb_control_frame_t *const reg_cfp, IC ic)
+{
+ VALUE val;
+ const ID *segments = ic->segments;
+ struct iseq_inline_constant_cache_entry *ice = ic->entry;
+ if (ice && vm_ic_hit_p(ice, GET_EP())) {
+ val = ice->value;
+
+ VM_ASSERT(val == vm_get_ev_const_chain(ec, segments));
+ }
+ else {
+ ruby_vm_constant_cache_misses++;
+ val = vm_get_ev_const_chain(ec, segments);
+ vm_ic_track_const_chain(GET_CFP(), ic, segments);
+ // Undo the PC increment to get the address to this instruction
+ // INSN_ATTR(width) == 2
+ vm_ic_update(GET_ISEQ(), ic, val, GET_EP(), GET_PC() - 2);
+ }
+ return val;
+}
+
static VALUE
vm_once_dispatch(rb_execution_context_t *ec, ISEQ iseq, ISE is)
{
@@ -6198,6 +6490,12 @@ vm_opt_aref_with(VALUE recv, VALUE key)
}
}
+VALUE
+rb_vm_opt_aref_with(VALUE recv, VALUE key)
+{
+ return vm_opt_aref_with(recv, key);
+}
+
static VALUE
vm_opt_aset_with(VALUE recv, VALUE key, VALUE val)
{
@@ -6378,28 +6676,20 @@ vm_trace_hook(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, const VAL
}
}
-// Return true if given cc has cfunc which is NOT handled by opt_send_without_block.
-bool
-rb_vm_opt_cfunc_p(CALL_CACHE cc, int insn)
-{
- switch (insn) {
- case BIN(opt_eq):
- return check_cfunc(vm_cc_cme(cc), rb_obj_equal);
- case BIN(opt_nil_p):
- return check_cfunc(vm_cc_cme(cc), rb_false);
- case BIN(opt_not):
- return check_cfunc(vm_cc_cme(cc), rb_obj_not);
- default:
- return false;
- }
-}
-
#define VM_TRACE_HOOK(target_event, val) do { \
if ((pc_events & (target_event)) & enabled_flags) { \
vm_trace_hook(ec, reg_cfp, pc, pc_events, (target_event), global_hooks, local_hooks_ptr, (val)); \
} \
} while (0)
+static VALUE
+rescue_errinfo(rb_execution_context_t *ec, rb_control_frame_t *cfp)
+{
+ VM_ASSERT(VM_FRAME_RUBYFRAME_P(cfp));
+ VM_ASSERT(ISEQ_BODY(cfp->iseq)->type == ISEQ_TYPE_RESCUE);
+ return cfp->ep[VM_ENV_INDEX_LAST_LVAR];
+}
+
static void
vm_trace(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp)
{
@@ -6476,6 +6766,7 @@ vm_trace(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp)
vm_trace_hook(ec, reg_cfp, pc, RUBY_EVENT_CALL, RUBY_EVENT_CALL, global_hooks, bmethod_local_hooks_ptr, Qundef);
}
VM_TRACE_HOOK(RUBY_EVENT_CLASS | RUBY_EVENT_CALL | RUBY_EVENT_B_CALL, Qundef);
+ VM_TRACE_HOOK(RUBY_EVENT_RESCUE, rescue_errinfo(ec, reg_cfp));
VM_TRACE_HOOK(RUBY_EVENT_LINE, Qundef);
VM_TRACE_HOOK(RUBY_EVENT_COVERAGE_LINE, Qundef);
VM_TRACE_HOOK(RUBY_EVENT_COVERAGE_BRANCH, Qundef);
diff --git a/vm_insnhelper.h b/vm_insnhelper.h
index ac60c40ef5..926700b90a 100644
--- a/vm_insnhelper.h
+++ b/vm_insnhelper.h
@@ -16,34 +16,23 @@ RUBY_EXTERN rb_serial_t ruby_vm_constant_cache_invalidations;
RUBY_EXTERN rb_serial_t ruby_vm_constant_cache_misses;
RUBY_EXTERN rb_serial_t ruby_vm_global_cvar_state;
-#ifndef RJIT_STATS
-# define RJIT_STATS RUBY_DEBUG
+#if USE_YJIT || USE_RJIT // We want vm_insns_count on any JIT-enabled build.
+// Increment vm_insns_count for --yjit-stats. We increment this even when
+// --yjit or --yjit-stats is not used because branching to skip it is slower.
+// We also don't use ATOMIC_INC for performance, allowing inaccuracy on Ractors.
+#define JIT_COLLECT_USAGE_INSN(insn) rb_vm_insns_count++
+#else
+#define JIT_COLLECT_USAGE_INSN(insn) // none
#endif
#if VM_COLLECT_USAGE_DETAILS
#define COLLECT_USAGE_INSN(insn) vm_collect_usage_insn(insn)
#define COLLECT_USAGE_OPERAND(insn, n, op) vm_collect_usage_operand((insn), (n), ((VALUE)(op)))
-
#define COLLECT_USAGE_REGISTER(reg, s) vm_collect_usage_register((reg), (s))
-#elif RJIT_STATS && YJIT_STATS
-// Both flags could be enabled at the same time. You need to call both in that case.
-#define COLLECT_USAGE_INSN(insn) rb_rjit_collect_vm_usage_insn(insn); rb_yjit_collect_vm_usage_insn(insn)
-#define COLLECT_USAGE_OPERAND(insn, n, op) /* none */
-#define COLLECT_USAGE_REGISTER(reg, s) /* none */
-#elif RJIT_STATS
-// for --rjit-stats
-#define COLLECT_USAGE_INSN(insn) rb_rjit_collect_vm_usage_insn(insn)
-#define COLLECT_USAGE_OPERAND(insn, n, op) /* none */
-#define COLLECT_USAGE_REGISTER(reg, s) /* none */
-#elif YJIT_STATS
-/* for --yjit-stats */
-#define COLLECT_USAGE_INSN(insn) rb_yjit_collect_vm_usage_insn(insn)
-#define COLLECT_USAGE_OPERAND(insn, n, op) /* none */
-#define COLLECT_USAGE_REGISTER(reg, s) /* none */
#else
-#define COLLECT_USAGE_INSN(insn) /* none */
-#define COLLECT_USAGE_OPERAND(insn, n, op) /* none */
-#define COLLECT_USAGE_REGISTER(reg, s) /* none */
+#define COLLECT_USAGE_INSN(insn) JIT_COLLECT_USAGE_INSN(insn)
+#define COLLECT_USAGE_OPERAND(insn, n, op) // none
+#define COLLECT_USAGE_REGISTER(reg, s) // none
#endif
/**********************************************************/
@@ -69,6 +58,14 @@ RUBY_EXTERN rb_serial_t ruby_vm_global_cvar_state;
VM_REG_CFP = ec->cfp; \
} while (0)
+typedef enum call_type {
+ CALL_PUBLIC,
+ CALL_FCALL,
+ CALL_VCALL,
+ CALL_PUBLIC_KW,
+ CALL_FCALL_KW
+} call_type;
+
#if VM_COLLECT_USAGE_DETAILS
enum vm_regan_regtype {
VM_REGAN_PC = 0,
@@ -182,10 +179,8 @@ CC_SET_FASTPATH(const struct rb_callcache *cc, vm_call_handler func, bool enable
/**********************************************************/
#define CALL_SIMPLE_METHOD() do { \
- rb_snum_t x = leaf ? INSN_ATTR(width) : 0; \
- rb_snum_t y = attr_width_opt_send_without_block(0); \
- rb_snum_t z = x - y; \
- ADD_PC(z); \
+ rb_snum_t insn_width = attr_width_opt_send_without_block(0); \
+ ADD_PC(-insn_width); \
DISPATCH_ORIGINAL_INSN(opt_send_without_block); \
} while (0)
@@ -195,8 +190,11 @@ CC_SET_FASTPATH(const struct rb_callcache *cc, vm_call_handler func, bool enable
static inline struct vm_throw_data *
THROW_DATA_NEW(VALUE val, const rb_control_frame_t *cf, int st)
{
- struct vm_throw_data *obj = (struct vm_throw_data *)rb_imemo_new(imemo_throw_data, val, (VALUE)cf, 0, 0);
+ struct vm_throw_data *obj = IMEMO_NEW(struct vm_throw_data, imemo_throw_data, 0);
+ *((VALUE *)&obj->throw_obj) = val;
+ *((struct rb_control_frame_struct **)&obj->catch_frame) = (struct rb_control_frame_struct *)cf;
obj->throw_state = st;
+
return obj;
}
diff --git a/vm_method.c b/vm_method.c
index d925e4f2fb..3cacc010b8 100644
--- a/vm_method.c
+++ b/vm_method.c
@@ -31,7 +31,6 @@ vm_ccs_dump_i(ID mid, VALUE val, void *data)
rp(ccs->cme);
for (int i=0; i<ccs->len; i++) {
- fprintf(stderr, " | [%d]\t", i); vm_ci_dump(ccs->entries[i].ci);
rp_m( " | \t", ccs->entries[i].cc);
}
@@ -118,7 +117,7 @@ rb_vm_mtbl_dump(const char *msg, VALUE klass, ID target_mid)
static inline void
vm_cme_invalidate(rb_callable_method_entry_t *cme)
{
- VM_ASSERT(IMEMO_TYPE_P(cme, imemo_ment));
+ VM_ASSERT(IMEMO_TYPE_P(cme, imemo_ment), "cme: %d", imemo_type((VALUE)cme));
VM_ASSERT(callable_method_entry_p(cme));
METHOD_ENTRY_INVALIDATED_SET(cme);
RB_DEBUG_COUNTER_INC(cc_cme_invalidate);
@@ -166,7 +165,6 @@ invalidate_negative_cache(ID mid)
}
}
-static rb_method_entry_t *rb_method_entry_alloc(ID called_id, VALUE owner, VALUE defined_class, const rb_method_definition_t *def);
const rb_method_entry_t * rb_method_entry_clone(const rb_method_entry_t *src_me);
static const rb_callable_method_entry_t *complemented_callable_method_entry(VALUE klass, ID id);
static const rb_callable_method_entry_t *lookup_overloaded_cme(const rb_callable_method_entry_t *cme);
@@ -201,7 +199,7 @@ clear_method_cache_by_id_in_class(VALUE klass, ID mid)
struct rb_id_table *cm_tbl;
if ((cm_tbl = RCLASS_CALLABLE_M_TBL(klass)) != NULL) {
VALUE cme;
- if (rb_yjit_enabled_p() && rb_id_table_lookup(cm_tbl, mid, &cme)) {
+ if (rb_yjit_enabled_p && rb_id_table_lookup(cm_tbl, mid, &cme)) {
rb_yjit_cme_invalidate((rb_callable_method_entry_t *)cme);
}
if (rb_rjit_enabled && rb_id_table_lookup(cm_tbl, mid, &cme)) {
@@ -241,6 +239,13 @@ clear_method_cache_by_id_in_class(VALUE klass, ID mid)
vm_cme_invalidate((rb_callable_method_entry_t *)cme);
RB_DEBUG_COUNTER_INC(cc_invalidate_tree_cme);
+ // In case of refinement ME, also invalidate the wrapped ME that
+ // could be cached at some callsite and is unreachable from any
+ // RCLASS_CC_TBL.
+ if (cme->def->type == VM_METHOD_TYPE_REFINED && cme->def->body.refined.orig_me) {
+ vm_cme_invalidate((rb_callable_method_entry_t *)cme->def->body.refined.orig_me);
+ }
+
if (cme->def->iseq_overload) {
rb_callable_method_entry_t *monly_cme = (rb_callable_method_entry_t *)lookup_overloaded_cme(cme);
if (monly_cme) {
@@ -295,6 +300,7 @@ rb_clear_method_cache(VALUE klass_or_module, ID mid)
VALUE refined_class = rb_refinement_module_get_refined_class(module);
rb_clear_method_cache(refined_class, mid);
rb_class_foreach_subclass(refined_class, clear_iclass_method_cache_by_id_for_refinements, mid);
+ rb_clear_all_refinement_method_cache();
}
rb_class_foreach_subclass(module, clear_iclass_method_cache_by_id, mid);
}
@@ -303,25 +309,23 @@ rb_clear_method_cache(VALUE klass_or_module, ID mid)
}
}
-// gc.c
-void rb_cc_table_free(VALUE klass);
-
static int
-invalidate_all_cc(void *vstart, void *vend, size_t stride, void *data)
+invalidate_all_refinement_cc(void *vstart, void *vend, size_t stride, void *data)
{
VALUE v = (VALUE)vstart;
for (; v != (VALUE)vend; v += stride) {
void *ptr = asan_poisoned_object_p(v);
asan_unpoison_object(v, false);
+
if (RBASIC(v)->flags) { // liveness check
- if (RB_TYPE_P(v, T_CLASS) ||
- RB_TYPE_P(v, T_ICLASS)) {
- if (RCLASS_CC_TBL(v)) {
- rb_cc_table_free(v);
+ if (imemo_type_p(v, imemo_callcache)) {
+ const struct rb_callcache *cc = (const struct rb_callcache *)v;
+ if (vm_cc_refinement_p(cc) && cc->klass) {
+ vm_cc_invalidate(cc);
}
- RCLASS_CC_TBL(v) = NULL;
}
}
+
if (ptr) {
asan_poison_object(v);
}
@@ -329,11 +333,121 @@ invalidate_all_cc(void *vstart, void *vend, size_t stride, void *data)
return 0; // continue to iteration
}
+static st_index_t
+vm_ci_hash(VALUE v)
+{
+ const struct rb_callinfo *ci = (const struct rb_callinfo *)v;
+ st_index_t h;
+ h = rb_hash_start(ci->mid);
+ h = rb_hash_uint(h, ci->flag);
+ h = rb_hash_uint(h, ci->argc);
+ if (ci->kwarg) {
+ for (int i = 0; i < ci->kwarg->keyword_len; i++) {
+ h = rb_hash_uint(h, ci->kwarg->keywords[i]);
+ }
+ }
+ return h;
+}
+
+static int
+vm_ci_hash_cmp(VALUE v1, VALUE v2)
+{
+ const struct rb_callinfo *ci1 = (const struct rb_callinfo *)v1;
+ const struct rb_callinfo *ci2 = (const struct rb_callinfo *)v2;
+ if (ci1->mid != ci2->mid) return 1;
+ if (ci1->flag != ci2->flag) return 1;
+ if (ci1->argc != ci2->argc) return 1;
+ if (ci1->kwarg != NULL) {
+ VM_ASSERT(ci2->kwarg != NULL); // implied by matching flags
+
+ if (ci1->kwarg->keyword_len != ci2->kwarg->keyword_len)
+ return 1;
+
+ for (int i = 0; i < ci1->kwarg->keyword_len; i++) {
+ if (ci1->kwarg->keywords[i] != ci2->kwarg->keywords[i]) {
+ return 1;
+ }
+ }
+ } else {
+ VM_ASSERT(ci2->kwarg == NULL); // implied by matching flags
+ }
+ return 0;
+}
+
+static const struct st_hash_type vm_ci_hashtype = {
+ vm_ci_hash_cmp,
+ vm_ci_hash
+};
+
+static int
+ci_lookup_i(st_data_t *key, st_data_t *value, st_data_t data, int existing)
+{
+ const struct rb_callinfo *ci = (const struct rb_callinfo *)*key;
+ st_data_t *ret = (st_data_t *)data;
+
+ if (existing) {
+ if (rb_objspace_garbage_object_p((VALUE)ci)) {
+ *ret = (st_data_t)NULL;
+ return ST_DELETE;
+ } else {
+ *ret = *key;
+ return ST_STOP;
+ }
+ }
+ else {
+ *key = *value = *ret = (st_data_t)ci;
+ return ST_CONTINUE;
+ }
+}
+
+const struct rb_callinfo *
+rb_vm_ci_lookup(ID mid, unsigned int flag, unsigned int argc, const struct rb_callinfo_kwarg *kwarg)
+{
+ rb_vm_t *vm = GET_VM();
+ const struct rb_callinfo *ci = NULL;
+
+ if (kwarg) {
+ ((struct rb_callinfo_kwarg *)kwarg)->references++;
+ }
+
+ struct rb_callinfo *new_ci = IMEMO_NEW(struct rb_callinfo, imemo_callinfo, (VALUE)kwarg);
+ new_ci->mid = mid;
+ new_ci->flag = flag;
+ new_ci->argc = argc;
+
+ RB_VM_LOCK_ENTER();
+ {
+ st_table *ci_table = vm->ci_table;
+ VM_ASSERT(ci_table);
+
+ do {
+ st_update(ci_table, (st_data_t)new_ci, ci_lookup_i, (st_data_t)&ci);
+ } while (ci == NULL);
+ }
+ RB_VM_LOCK_LEAVE();
+
+ VM_ASSERT(ci);
+
+ return ci;
+}
+
void
-rb_clear_method_cache_all(void)
+rb_vm_ci_free(const struct rb_callinfo *ci)
{
- rb_objspace_each_objects(invalidate_all_cc, NULL);
+ rb_vm_t *vm = GET_VM();
+ RB_VM_LOCK_ENTER();
+ {
+ st_data_t key = (st_data_t)ci;
+ st_delete(vm->ci_table, &key, NULL);
+ }
+ RB_VM_LOCK_LEAVE();
+}
+
+void
+rb_clear_all_refinement_method_cache(void)
+{
+ rb_objspace_each_objects(invalidate_all_refinement_cc, NULL);
rb_yjit_invalidate_all_method_lookup_assumptions();
}
@@ -403,33 +517,25 @@ rb_add_method_optimized(VALUE klass, ID mid, enum method_optimized_type opt_type
}
static void
-rb_method_definition_release(rb_method_definition_t *def, int complemented)
+rb_method_definition_release(rb_method_definition_t *def)
{
if (def != NULL) {
- const int alias_count = def->alias_count;
- const int complemented_count = def->complemented_count;
- VM_ASSERT(alias_count >= 0);
- VM_ASSERT(complemented_count >= 0);
-
- if (alias_count + complemented_count == 0) {
- if (METHOD_DEBUG) fprintf(stderr, "-%p-%s:%d,%d (remove)\n", (void *)def,
- rb_id2name(def->original_id), alias_count, complemented_count);
+ const int reference_count = def->reference_count;
+ def->reference_count--;
+
+ VM_ASSERT(reference_count >= 0);
+
+ if (def->reference_count == 0) {
+ if (METHOD_DEBUG) fprintf(stderr, "-%p-%s:%d (remove)\n", (void *)def,
+ rb_id2name(def->original_id), def->reference_count);
if (def->type == VM_METHOD_TYPE_BMETHOD && def->body.bmethod.hooks) {
xfree(def->body.bmethod.hooks);
}
xfree(def);
}
else {
- if (complemented) {
- VM_ASSERT(def->complemented_count > 0);
- def->complemented_count--;
- }
- else if (def->alias_count > 0) {
- def->alias_count--;
- }
-
- if (METHOD_DEBUG) fprintf(stderr, "-%p-%s:%d->%d,%d->%d (dec)\n", (void *)def, rb_id2name(def->original_id),
- alias_count, def->alias_count, complemented_count, def->complemented_count);
+ if (METHOD_DEBUG) fprintf(stderr, "-%p-%s:%d->%d (dec)\n", (void *)def, rb_id2name(def->original_id),
+ reference_count, def->reference_count);
}
}
}
@@ -442,7 +548,7 @@ rb_free_method_entry(const rb_method_entry_t *me)
if (me->def && me->def->iseq_overload) {
delete_overloaded_cme((const rb_callable_method_entry_t *)me);
}
- rb_method_definition_release(me->def, METHOD_ENTRY_COMPLEMENTED(me));
+ rb_method_definition_release(me->def);
}
static inline rb_method_entry_t *search_method(VALUE klass, ID id, VALUE *defined_class_ptr);
@@ -509,10 +615,22 @@ setup_method_cfunc_struct(rb_method_cfunc_t *cfunc, VALUE (*func)(ANYARGS), int
cfunc->invoker = call_cfunc_invoker_func(argc);
}
+static rb_method_definition_t *
+method_definition_addref(rb_method_definition_t *def, bool complemented)
+{
+ if (!complemented && def->reference_count > 0) def->aliased = true;
+ def->reference_count++;
+ if (METHOD_DEBUG) fprintf(stderr, "+%p-%s:%d\n", (void *)def, rb_id2name(def->original_id), def->reference_count);
+ return def;
+}
+
void
rb_method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *def, void *opts)
{
- *(rb_method_definition_t **)&me->def = def;
+ rb_method_definition_release(me->def);
+ *(rb_method_definition_t **)&me->def = method_definition_addref(def, METHOD_ENTRY_COMPLEMENTED(me));
+
+ if (!ruby_running) add_opt_method_entry(me);
if (opts != NULL) {
switch (def->type) {
@@ -577,9 +695,7 @@ rb_method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *de
return;
case VM_METHOD_TYPE_REFINED:
{
- const rb_method_refined_t *refined = (rb_method_refined_t *)opts;
- RB_OBJ_WRITE(me, &def->body.refined.orig_me, refined->orig_me);
- RB_OBJ_WRITE(me, &def->body.refined.owner, refined->owner);
+ RB_OBJ_WRITE(me, &def->body.refined.orig_me, (rb_method_entry_t *)opts);
return;
}
case VM_METHOD_TYPE_ALIAS:
@@ -615,7 +731,6 @@ method_definition_reset(const rb_method_entry_t *me)
break;
case VM_METHOD_TYPE_REFINED:
RB_OBJ_WRITTEN(me, Qundef, def->body.refined.orig_me);
- RB_OBJ_WRITTEN(me, Qundef, def->body.refined.owner);
break;
case VM_METHOD_TYPE_ALIAS:
RB_OBJ_WRITTEN(me, Qundef, def->body.alias.original_me);
@@ -642,26 +757,15 @@ rb_method_definition_create(rb_method_type_t type, ID mid)
return def;
}
-static rb_method_definition_t *
-method_definition_addref(rb_method_definition_t *def)
-{
- def->alias_count++;
- if (METHOD_DEBUG) fprintf(stderr, "+%p-%s:%d\n", (void *)def, rb_id2name(def->original_id), def->alias_count);
- return def;
-}
-
-static rb_method_definition_t *
-method_definition_addref_complement(rb_method_definition_t *def)
-{
- def->complemented_count++;
- if (METHOD_DEBUG) fprintf(stderr, "+%p-%s:%d\n", (void *)def, rb_id2name(def->original_id), def->complemented_count);
- return def;
-}
-
static rb_method_entry_t *
-rb_method_entry_alloc(ID called_id, VALUE owner, VALUE defined_class, const rb_method_definition_t *def)
+rb_method_entry_alloc(ID called_id, VALUE owner, VALUE defined_class, rb_method_definition_t *def, bool complement)
{
- rb_method_entry_t *me = (rb_method_entry_t *)rb_imemo_new(imemo_ment, (VALUE)def, (VALUE)called_id, owner, defined_class);
+ if (def) method_definition_addref(def, complement);
+ rb_method_entry_t *me = IMEMO_NEW(rb_method_entry_t, imemo_ment, defined_class);
+ *((rb_method_definition_t **)&me->def) = def;
+ me->called_id = called_id;
+ me->owner = owner;
+
return me;
}
@@ -682,24 +786,39 @@ filter_defined_class(VALUE klass)
}
rb_method_entry_t *
-rb_method_entry_create(ID called_id, VALUE klass, rb_method_visibility_t visi, const rb_method_definition_t *def)
+rb_method_entry_create(ID called_id, VALUE klass, rb_method_visibility_t visi, rb_method_definition_t *def)
{
- rb_method_entry_t *me = rb_method_entry_alloc(called_id, klass, filter_defined_class(klass), def);
+ rb_method_entry_t *me = rb_method_entry_alloc(called_id, klass, filter_defined_class(klass), def, false);
METHOD_ENTRY_FLAGS_SET(me, visi, ruby_running ? FALSE : TRUE);
if (def != NULL) method_definition_reset(me);
return me;
}
+// Return a cloned ME that's not invalidated (MEs are disposable for caching).
const rb_method_entry_t *
rb_method_entry_clone(const rb_method_entry_t *src_me)
{
- rb_method_entry_t *me = rb_method_entry_alloc(src_me->called_id, src_me->owner, src_me->defined_class,
- method_definition_addref(src_me->def));
- if (METHOD_ENTRY_COMPLEMENTED(src_me)) {
- method_definition_addref_complement(src_me->def);
- }
+ rb_method_entry_t *me = rb_method_entry_alloc(src_me->called_id, src_me->owner, src_me->defined_class, src_me->def, METHOD_ENTRY_COMPLEMENTED(src_me));
METHOD_ENTRY_FLAGS_COPY(me, src_me);
+
+ // Also clone inner ME in case of refinement ME
+ if (src_me->def &&
+ src_me->def->type == VM_METHOD_TYPE_REFINED &&
+ src_me->def->body.refined.orig_me) {
+ const rb_method_entry_t *orig_me = src_me->def->body.refined.orig_me;
+ VM_ASSERT(orig_me->def->type != VM_METHOD_TYPE_REFINED);
+
+ rb_method_entry_t *orig_clone = rb_method_entry_alloc(orig_me->called_id,
+ orig_me->owner, orig_me->defined_class, orig_me->def, METHOD_ENTRY_COMPLEMENTED(orig_me));
+ METHOD_ENTRY_FLAGS_COPY(orig_clone, orig_me);
+
+ // Clone definition, since writing a VALUE to a shared definition
+ // can create reference edges we can't run WBs for.
+ rb_method_definition_t *clone_def =
+ rb_method_definition_create(VM_METHOD_TYPE_REFINED, src_me->called_id);
+ rb_method_definition_set(me, clone_def, orig_clone);
+ }
return me;
}
@@ -708,10 +827,7 @@ rb_method_entry_complement_defined_class(const rb_method_entry_t *src_me, ID cal
{
rb_method_definition_t *def = src_me->def;
rb_method_entry_t *me;
- struct {
- const struct rb_method_entry_struct *orig_me;
- VALUE owner;
- } refined = {0};
+ const rb_method_entry_t *refined_orig_me = NULL;
if (!src_me->defined_class &&
def->type == VM_METHOD_TYPE_REFINED &&
@@ -719,19 +835,16 @@ rb_method_entry_complement_defined_class(const rb_method_entry_t *src_me, ID cal
const rb_method_entry_t *orig_me =
rb_method_entry_clone(def->body.refined.orig_me);
RB_OBJ_WRITE((VALUE)orig_me, &orig_me->defined_class, defined_class);
- refined.orig_me = orig_me;
- refined.owner = orig_me->owner;
+ refined_orig_me = orig_me;
def = NULL;
}
- else {
- def = method_definition_addref_complement(def);
- }
- me = rb_method_entry_alloc(called_id, src_me->owner, defined_class, def);
+
+ me = rb_method_entry_alloc(called_id, src_me->owner, defined_class, def, true);
METHOD_ENTRY_FLAGS_COPY(me, src_me);
METHOD_ENTRY_COMPLEMENTED_SET(me);
if (!def) {
def = rb_method_definition_create(VM_METHOD_TYPE_REFINED, called_id);
- rb_method_definition_set(me, def, &refined);
+ rb_method_definition_set(me, def, (void *)refined_orig_me);
}
VM_ASSERT(RB_TYPE_P(me->owner, T_MODULE));
@@ -742,7 +855,8 @@ rb_method_entry_complement_defined_class(const rb_method_entry_t *src_me, ID cal
void
rb_method_entry_copy(rb_method_entry_t *dst, const rb_method_entry_t *src)
{
- *(rb_method_definition_t **)&dst->def = method_definition_addref(src->def);
+ rb_method_definition_release(dst->def);
+ *(rb_method_definition_t **)&dst->def = method_definition_addref(src->def, METHOD_ENTRY_COMPLEMENTED(src));
method_definition_reset(dst);
dst->called_id = src->called_id;
RB_OBJ_WRITE((VALUE)dst, &dst->owner, src->owner);
@@ -757,24 +871,19 @@ make_method_entry_refined(VALUE owner, rb_method_entry_t *me)
return;
}
else {
- struct {
- struct rb_method_entry_struct *orig_me;
- VALUE owner;
- } refined;
rb_method_definition_t *def;
rb_vm_check_redefinition_opt_method(me, me->owner);
- refined.orig_me =
+ struct rb_method_entry_struct *orig_me =
rb_method_entry_alloc(me->called_id, me->owner,
- me->defined_class ?
- me->defined_class : owner,
- method_definition_addref(me->def));
- METHOD_ENTRY_FLAGS_COPY(refined.orig_me, me);
- refined.owner = owner;
+ me->defined_class ? me->defined_class : owner,
+ me->def,
+ true);
+ METHOD_ENTRY_FLAGS_COPY(orig_me, me);
def = rb_method_definition_create(VM_METHOD_TYPE_REFINED, me->called_id);
- rb_method_definition_set(me, def, (void *)&refined);
+ rb_method_definition_set(me, def, orig_me);
METHOD_ENTRY_VISI_SET(me, METHOD_VISI_PUBLIC);
}
}
@@ -852,7 +961,7 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil
}
orig_klass = klass;
- if (!FL_TEST(klass, FL_SINGLETON) &&
+ if (!RCLASS_SINGLETON_P(klass) &&
type != VM_METHOD_TYPE_NOTIMPLEMENTED &&
type != VM_METHOD_TYPE_ZSUPER) {
switch (mid) {
@@ -897,7 +1006,7 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil
if (RTEST(ruby_verbose) &&
type != VM_METHOD_TYPE_UNDEF &&
- (old_def->alias_count == 0) &&
+ (old_def->aliased == false) &&
(!old_def->no_redef_warning) &&
!make_refined &&
old_def->type != VM_METHOD_TYPE_UNDEF &&
@@ -905,7 +1014,6 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil
old_def->type != VM_METHOD_TYPE_ALIAS) {
const rb_iseq_t *iseq = 0;
- rb_warning("method redefined; discarding old %"PRIsVALUE, rb_id2str(mid));
switch (old_def->type) {
case VM_METHOD_TYPE_ISEQ:
iseq = def_iseq_ptr(old_def);
@@ -917,17 +1025,25 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil
break;
}
if (iseq) {
- rb_compile_warning(RSTRING_PTR(rb_iseq_path(iseq)),
- ISEQ_BODY(iseq)->location.first_lineno,
- "previous definition of %"PRIsVALUE" was here",
- rb_id2str(old_def->original_id));
+ rb_warning(
+ "method redefined; discarding old %"PRIsVALUE"\n%s:%d: warning: previous definition of %"PRIsVALUE" was here",
+ rb_id2str(mid),
+ RSTRING_PTR(rb_iseq_path(iseq)),
+ ISEQ_BODY(iseq)->location.first_lineno,
+ rb_id2str(old_def->original_id)
+ );
+ }
+ else {
+ rb_warning("method redefined; discarding old %"PRIsVALUE, rb_id2str(mid));
}
}
}
/* create method entry */
me = rb_method_entry_create(mid, defined_class, visi, NULL);
- if (def == NULL) def = rb_method_definition_create(type, original_id);
+ if (def == NULL) {
+ def = rb_method_definition_create(type, original_id);
+ }
rb_method_definition_set(me, def, opts);
rb_clear_method_cache(klass, mid);
@@ -945,7 +1061,7 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil
/* check mid */
if (mid == object_id || mid == id__send__) {
if (type == VM_METHOD_TYPE_ISEQ && search_method(klass, mid, 0)) {
- rb_warn("redefining `%s' may cause serious problems", rb_id2name(mid));
+ rb_warn("redefining '%s' may cause serious problems", rb_id2name(mid));
}
}
@@ -965,7 +1081,7 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil
return me;
}
-static rb_method_entry_t *rb_method_entry_alloc(ID called_id, VALUE owner, VALUE defined_class, const rb_method_definition_t *def);
+static rb_method_entry_t *rb_method_entry_alloc(ID called_id, VALUE owner, VALUE defined_class, rb_method_definition_t *def, bool refined);
static st_table *
overloaded_cme_table(void)
@@ -1049,13 +1165,14 @@ get_overloaded_cme(const rb_callable_method_entry_t *cme)
else {
// create
rb_method_definition_t *def = rb_method_definition_create(VM_METHOD_TYPE_ISEQ, cme->def->original_id);
- def->body.iseq.cref = cme->def->body.iseq.cref;
- def->body.iseq.iseqptr = ISEQ_BODY(cme->def->body.iseq.iseqptr)->mandatory_only_iseq;
-
rb_method_entry_t *me = rb_method_entry_alloc(cme->called_id,
cme->owner,
cme->defined_class,
- def);
+ def,
+ false);
+
+ RB_OBJ_WRITE(me, &def->body.iseq.cref, cme->def->body.iseq.cref);
+ RB_OBJ_WRITE(me, &def->body.iseq.iseqptr, ISEQ_BODY(cme->def->body.iseq.iseqptr)->mandatory_only_iseq);
ASSERT_vm_locking();
st_insert(overloaded_cme_table(), (st_data_t)cme, (st_data_t)me);
@@ -1065,8 +1182,8 @@ get_overloaded_cme(const rb_callable_method_entry_t *cme)
}
}
-static const rb_callable_method_entry_t *
-check_overloaded_cme(const rb_callable_method_entry_t *cme, const struct rb_callinfo * const ci)
+const rb_callable_method_entry_t *
+rb_check_overloaded_cme(const rb_callable_method_entry_t *cme, const struct rb_callinfo * const ci)
{
if (UNLIKELY(cme->def->iseq_overload) &&
(vm_ci_flag(ci) & (VM_CALL_ARGS_SIMPLE)) &&
@@ -1086,7 +1203,7 @@ check_overloaded_cme(const rb_callable_method_entry_t *cme, const struct rb_call
const VALUE arg = ID2SYM(mid); \
VALUE recv_class = (klass); \
ID hook_id = (hook); \
- if (FL_TEST((klass), FL_SINGLETON)) { \
+ if (RCLASS_SINGLETON_P((klass))) { \
recv_class = RCLASS_ATTACHED_OBJECT((klass)); \
hook_id = singleton_##hook; \
} \
@@ -1134,9 +1251,7 @@ method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *me,
if (newme == me) {
me->def->no_redef_warning = TRUE;
}
- else {
- method_definition_addref(me->def);
- }
+
method_added(klass, mid);
return newme;
}
@@ -1153,7 +1268,7 @@ void
rb_define_alloc_func(VALUE klass, VALUE (*func)(VALUE))
{
Check_Type(klass, T_CLASS);
- if (FL_TEST_RAW(klass, FL_SINGLETON)) {
+ if (RCLASS_SINGLETON_P(klass)) {
rb_raise(rb_eTypeError, "can't define an allocator for a singleton class");
}
RCLASS_SET_ALLOCATOR(klass, func);
@@ -1350,7 +1465,7 @@ negative_cme(ID mid)
cme = (rb_callable_method_entry_t *)cme_data;
}
else {
- cme = (rb_callable_method_entry_t *)rb_method_entry_alloc(mid, Qnil, Qnil, NULL);
+ cme = (rb_callable_method_entry_t *)rb_method_entry_alloc(mid, Qnil, Qnil, NULL, false);
rb_id_table_insert(vm->negative_cme_table, mid, (VALUE)cme);
}
@@ -1445,7 +1560,7 @@ rb_method_entry_with_refinements(VALUE klass, ID id, VALUE *defined_class_ptr)
}
static const rb_callable_method_entry_t *
-callable_method_entry_refeinements0(VALUE klass, ID id, VALUE *defined_class_ptr, bool with_refinements,
+callable_method_entry_refinements0(VALUE klass, ID id, VALUE *defined_class_ptr, bool with_refinements,
const rb_callable_method_entry_t *cme)
{
if (cme == NULL || LIKELY(cme->def->type != VM_METHOD_TYPE_REFINED)) {
@@ -1462,7 +1577,7 @@ static const rb_callable_method_entry_t *
callable_method_entry_refinements(VALUE klass, ID id, VALUE *defined_class_ptr, bool with_refinements)
{
const rb_callable_method_entry_t *cme = callable_method_entry(klass, id, defined_class_ptr);
- return callable_method_entry_refeinements0(klass, id, defined_class_ptr, with_refinements, cme);
+ return callable_method_entry_refinements0(klass, id, defined_class_ptr, with_refinements, cme);
}
const rb_callable_method_entry_t *
@@ -1554,14 +1669,14 @@ remove_method(VALUE klass, ID mid)
rb_class_modify_check(klass);
klass = RCLASS_ORIGIN(klass);
if (mid == object_id || mid == id__send__ || mid == idInitialize) {
- rb_warn("removing `%s' may cause serious problems", rb_id2name(mid));
+ rb_warn("removing '%s' may cause serious problems", rb_id2name(mid));
}
if (!rb_id_table_lookup(RCLASS_M_TBL(klass), mid, &data) ||
!(me = (rb_method_entry_t *)data) ||
(!me->def || me->def->type == VM_METHOD_TYPE_UNDEF) ||
UNDEFINED_REFINED_METHOD_P(me->def)) {
- rb_name_err_raise("method `%1$s' not defined in %2$s",
+ rb_name_err_raise("method '%1$s' not defined in %2$s",
klass, ID2SYM(mid));
}
@@ -1611,7 +1726,7 @@ rb_mod_remove_method(int argc, VALUE *argv, VALUE mod)
VALUE v = argv[i];
ID id = rb_check_id(&v);
if (!id) {
- rb_name_err_raise("method `%1$s' not defined in %2$s",
+ rb_name_err_raise("method '%1$s' not defined in %2$s",
mod, v);
}
remove_method(mod, id);
@@ -1784,7 +1899,7 @@ rb_undef(VALUE klass, ID id)
}
rb_class_modify_check(klass);
if (id == object_id || id == id__send__ || id == idInitialize) {
- rb_warn("undefining `%s' may cause serious problems", rb_id2name(id));
+ rb_warn("undefining '%s' may cause serious problems", rb_id2name(id));
}
me = search_method(klass, id, 0);
@@ -1844,7 +1959,7 @@ rb_undef(VALUE klass, ID id)
*
* In child
* In parent
- * prog.rb:23: undefined method `hello' for #<Child:0x401b3bb4> (NoMethodError)
+ * prog.rb:23: undefined method 'hello' for #<Child:0x401b3bb4> (NoMethodError)
*/
static VALUE
@@ -2573,7 +2688,7 @@ rb_mod_private_method(int argc, VALUE *argv, VALUE obj)
static VALUE
top_public(int argc, VALUE *argv, VALUE _)
{
- return rb_mod_public(argc, argv, rb_cObject);
+ return rb_mod_public(argc, argv, rb_top_main_class("public"));
}
/*
@@ -2593,7 +2708,7 @@ top_public(int argc, VALUE *argv, VALUE _)
static VALUE
top_private(int argc, VALUE *argv, VALUE _)
{
- return rb_mod_private(argc, argv, rb_cObject);
+ return rb_mod_private(argc, argv, rb_top_main_class("private"));
}
/*
@@ -2606,7 +2721,7 @@ top_private(int argc, VALUE *argv, VALUE _)
static VALUE
top_ruby2_keywords(int argc, VALUE *argv, VALUE module)
{
- return rb_mod_ruby2_keywords(argc, argv, rb_cObject);
+ return rb_mod_ruby2_keywords(argc, argv, rb_top_main_class("ruby2_keywords"));
}
/*
@@ -2787,8 +2902,8 @@ vm_respond_to(rb_execution_context_t *ec, VALUE klass, VALUE obj, ID id, int pri
rb_category_warn(RB_WARN_CATEGORY_DEPRECATED,
"%"PRIsVALUE"%c""respond_to?(:%"PRIsVALUE") uses"
" the deprecated method signature, which takes one parameter",
- (FL_TEST(klass, FL_SINGLETON) ? obj : klass),
- (FL_TEST(klass, FL_SINGLETON) ? '.' : '#'),
+ (RCLASS_SINGLETON_P(klass) ? obj : klass),
+ (RCLASS_SINGLETON_P(klass) ? '.' : '#'),
QUOTE_ID(id));
if (!NIL_P(location)) {
VALUE path = RARRAY_AREF(location, 0);
@@ -2888,12 +3003,6 @@ obj_respond_to_missing(VALUE obj, VALUE mid, VALUE priv)
}
void
-Init_Method(void)
-{
- //
-}
-
-void
Init_eval_method(void)
{
rb_define_method(rb_mKernel, "respond_to?", obj_respond_to, -1);
diff --git a/vm_opts.h b/vm_opts.h
index 86616649db..ce47745b11 100644
--- a/vm_opts.h
+++ b/vm_opts.h
@@ -19,7 +19,7 @@
#define OPT_PEEPHOLE_OPTIMIZATION 1
#define OPT_SPECIALISED_INSTRUCTION 1
#define OPT_INLINE_CONST_CACHE 1
-#define OPT_FROZEN_STRING_LITERAL 0
+#define OPT_FROZEN_STRING_LITERAL -1
#define OPT_DEBUG_FROZEN_STRING_LITERAL 0
/* Build Options.
@@ -54,17 +54,12 @@
#define OPT_OPERANDS_UNIFICATION 1
#define OPT_INSTRUCTIONS_UNIFICATION 0
#define OPT_UNIFY_ALL_COMBINATION 0
-#define OPT_STACK_CACHING 0
/* misc */
#ifndef OPT_SUPPORT_JOKE
#define OPT_SUPPORT_JOKE 0
#endif
-#ifndef OPT_SUPPORT_CALL_C_FUNCTION
-#define OPT_SUPPORT_CALL_C_FUNCTION 0
-#endif
-
#ifndef VM_COLLECT_USAGE_DETAILS
#define VM_COLLECT_USAGE_DETAILS 0
#endif
diff --git a/vm_sync.c b/vm_sync.c
index 01c8505344..4bef232f20 100644
--- a/vm_sync.c
+++ b/vm_sync.c
@@ -5,7 +5,8 @@
#include "ractor_core.h"
#include "vm_debug.h"
-static bool vm_barrier_finish_p(rb_vm_t *vm);
+void rb_ractor_sched_barrier_start(rb_vm_t *vm, rb_ractor_t *cr);
+void rb_ractor_sched_barrier_join(rb_vm_t *vm, rb_ractor_t *cr);
static bool
vm_locked(rb_vm_t *vm)
@@ -52,56 +53,32 @@ vm_lock_enter(rb_ractor_t *cr, rb_vm_t *vm, bool locked, bool no_barrier, unsign
// locking ractor and acquire VM lock will cause deadlock
VM_ASSERT(cr->sync.locked_by != rb_ractor_self(cr));
#endif
-
// lock
rb_native_mutex_lock(&vm->ractor.sync.lock);
VM_ASSERT(vm->ractor.sync.lock_owner == NULL);
- vm->ractor.sync.lock_owner = cr;
+ VM_ASSERT(vm->ractor.sync.lock_rec == 0);
+
+#ifdef RUBY_THREAD_PTHREAD_H
+ if (!no_barrier &&
+ cr->threads.sched.running != NULL // ractor has running threads.
+ ) {
+ while (vm->ractor.sched.barrier_waiting) {
+ RUBY_DEBUG_LOG("barrier serial:%u", vm->ractor.sched.barrier_serial);
+ rb_ractor_sched_barrier_join(vm, cr);
+ }
+ }
+#else
if (!no_barrier) {
- // barrier
while (vm->ractor.sync.barrier_waiting) {
- unsigned int barrier_cnt = vm->ractor.sync.barrier_cnt;
- rb_thread_t *th = GET_THREAD();
- bool running;
-
- RB_VM_SAVE_MACHINE_CONTEXT(th);
-
- if (rb_ractor_status_p(cr, ractor_running)) {
- rb_vm_ractor_blocking_cnt_inc(vm, cr, __FILE__, __LINE__);
- running = true;
- }
- else {
- running = false;
- }
- VM_ASSERT(rb_ractor_status_p(cr, ractor_blocking));
-
- if (vm_barrier_finish_p(vm)) {
- RUBY_DEBUG_LOG("wakeup barrier owner");
- rb_native_cond_signal(&vm->ractor.sync.barrier_cond);
- }
- else {
- RUBY_DEBUG_LOG("wait for barrier finish");
- }
-
- // wait for restart
- while (barrier_cnt == vm->ractor.sync.barrier_cnt) {
- vm->ractor.sync.lock_owner = NULL;
- rb_native_cond_wait(&cr->barrier_wait_cond, &vm->ractor.sync.lock);
- VM_ASSERT(vm->ractor.sync.lock_owner == NULL);
- vm->ractor.sync.lock_owner = cr;
- }
-
- RUBY_DEBUG_LOG("barrier is released. Acquire vm_lock");
-
- if (running) {
- rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
- }
+ rb_ractor_sched_barrier_join(vm, cr);
}
}
+#endif
VM_ASSERT(vm->ractor.sync.lock_rec == 0);
- VM_ASSERT(vm->ractor.sync.lock_owner == cr);
+ VM_ASSERT(vm->ractor.sync.lock_owner == NULL);
+ vm->ractor.sync.lock_owner = cr;
}
vm->ractor.sync.lock_rec++;
@@ -114,8 +91,9 @@ vm_lock_enter(rb_ractor_t *cr, rb_vm_t *vm, bool locked, bool no_barrier, unsign
static void
vm_lock_leave(rb_vm_t *vm, unsigned int *lev APPEND_LOCATION_ARGS)
{
- RUBY_DEBUG_LOG2(file, line, "rec:%u owner:%u", vm->ractor.sync.lock_rec,
- (unsigned int)rb_ractor_id(vm->ractor.sync.lock_owner));
+ RUBY_DEBUG_LOG2(file, line, "rec:%u owner:%u%s", vm->ractor.sync.lock_rec,
+ (unsigned int)rb_ractor_id(vm->ractor.sync.lock_owner),
+ vm->ractor.sync.lock_rec == 1 ? " (leave)" : "");
ASSERT_vm_locking();
VM_ASSERT(vm->ractor.sync.lock_rec > 0);
@@ -216,18 +194,6 @@ rb_vm_cond_timedwait(rb_vm_t *vm, rb_nativethread_cond_t *cond, unsigned long ms
vm_cond_wait(vm, cond, msec);
}
-static bool
-vm_barrier_finish_p(rb_vm_t *vm)
-{
- RUBY_DEBUG_LOG("cnt:%u living:%u blocking:%u",
- vm->ractor.sync.barrier_cnt,
- vm->ractor.cnt,
- vm->ractor.blocking_cnt);
-
- VM_ASSERT(vm->ractor.blocking_cnt <= vm->ractor.cnt);
- return vm->ractor.blocking_cnt == vm->ractor.cnt;
-}
-
void
rb_vm_barrier(void)
{
@@ -239,45 +205,13 @@ rb_vm_barrier(void)
}
else {
rb_vm_t *vm = GET_VM();
- VM_ASSERT(vm->ractor.sync.barrier_waiting == false);
+ VM_ASSERT(!vm->ractor.sched.barrier_waiting);
ASSERT_vm_locking();
-
rb_ractor_t *cr = vm->ractor.sync.lock_owner;
VM_ASSERT(cr == GET_RACTOR());
VM_ASSERT(rb_ractor_status_p(cr, ractor_running));
- vm->ractor.sync.barrier_waiting = true;
-
- RUBY_DEBUG_LOG("barrier start. cnt:%u living:%u blocking:%u",
- vm->ractor.sync.barrier_cnt,
- vm->ractor.cnt,
- vm->ractor.blocking_cnt);
-
- rb_vm_ractor_blocking_cnt_inc(vm, cr, __FILE__, __LINE__);
-
- // send signal
- rb_ractor_t *r = 0;
- ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
- if (r != cr) {
- rb_ractor_vm_barrier_interrupt_running_thread(r);
- }
- }
-
- // wait
- while (!vm_barrier_finish_p(vm)) {
- rb_vm_cond_wait(vm, &vm->ractor.sync.barrier_cond);
- }
-
- RUBY_DEBUG_LOG("cnt:%u barrier success", vm->ractor.sync.barrier_cnt);
-
- rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
-
- vm->ractor.sync.barrier_waiting = false;
- vm->ractor.sync.barrier_cnt++;
-
- ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
- rb_native_cond_signal(&r->barrier_wait_cond);
- }
+ rb_ractor_sched_barrier_start(vm, cr);
}
}
diff --git a/vm_trace.c b/vm_trace.c
index e4d0f25112..0f99e34e7b 100644
--- a/vm_trace.c
+++ b/vm_trace.c
@@ -23,11 +23,15 @@
#include "eval_intern.h"
#include "internal.h"
+#include "internal/bits.h"
#include "internal/class.h"
+#include "internal/gc.h"
#include "internal/hash.h"
#include "internal/symbol.h"
+#include "internal/thread.h"
#include "iseq.h"
#include "rjit.h"
+#include "ruby/atomic.h"
#include "ruby/debug.h"
#include "vm_core.h"
#include "ruby/ractor.h"
@@ -78,7 +82,7 @@ rb_hook_list_mark_and_update(rb_hook_list_t *hooks)
}
}
-static void clean_hooks(const rb_execution_context_t *ec, rb_hook_list_t *list);
+static void clean_hooks(rb_hook_list_t *list);
void
rb_hook_list_free(rb_hook_list_t *hooks)
@@ -86,7 +90,7 @@ rb_hook_list_free(rb_hook_list_t *hooks)
hooks->need_clean = true;
if (hooks->running == 0) {
- clean_hooks(GET_EC(), hooks);
+ clean_hooks(hooks);
}
}
@@ -219,7 +223,7 @@ rb_add_event_hook2(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data
}
static void
-clean_hooks(const rb_execution_context_t *ec, rb_hook_list_t *list)
+clean_hooks(rb_hook_list_t *list)
{
rb_event_hook_t *hook, **nextp = &list->hooks;
rb_event_flag_t prev_events = list->events;
@@ -253,11 +257,11 @@ clean_hooks(const rb_execution_context_t *ec, rb_hook_list_t *list)
}
static void
-clean_hooks_check(const rb_execution_context_t *ec, rb_hook_list_t *list)
+clean_hooks_check(rb_hook_list_t *list)
{
if (UNLIKELY(list->need_clean)) {
if (list->running == 0) {
- clean_hooks(ec, list);
+ clean_hooks(list);
}
}
}
@@ -285,7 +289,7 @@ remove_event_hook(const rb_execution_context_t *ec, const rb_thread_t *filter_th
hook = hook->next;
}
- clean_hooks_check(ec, list);
+ clean_hooks_check(list);
return ret;
}
@@ -369,7 +373,7 @@ static void
exec_hooks_postcheck(const rb_execution_context_t *ec, rb_hook_list_t *list)
{
list->running--;
- clean_hooks_check(ec, list);
+ clean_hooks_check(list);
}
static void
@@ -431,7 +435,7 @@ rb_exec_event_hooks(rb_trace_arg_t *trace_arg, rb_hook_list_t *hooks, int pop_p)
trace_arg->self != rb_mRubyVMFrozenCore /* skip special methods. TODO: remove it. */) {
const VALUE errinfo = ec->errinfo;
const VALUE old_recursive = ec->local_storage_recursive_hash;
- int state = 0;
+ enum ruby_tag_type state = 0;
/* setup */
ec->local_storage_recursive_hash = ec->local_storage_recursive_hash_for_trace;
@@ -510,66 +514,65 @@ static void call_trace_func(rb_event_flag_t, VALUE data, VALUE self, ID id, VALU
/* (2-1) set_trace_func (old API) */
/*
- * call-seq:
- * set_trace_func(proc) -> proc
- * set_trace_func(nil) -> nil
+ * call-seq:
+ * set_trace_func(proc) -> proc
+ * set_trace_func(nil) -> nil
*
- * Establishes _proc_ as the handler for tracing, or disables
- * tracing if the parameter is +nil+.
+ * Establishes _proc_ as the handler for tracing, or disables
+ * tracing if the parameter is +nil+.
*
- * *Note:* this method is obsolete, please use TracePoint instead.
+ * *Note:* this method is obsolete, please use TracePoint instead.
*
- * _proc_ takes up to six parameters:
+ * _proc_ takes up to six parameters:
*
- * * an event name
- * * a filename
- * * a line number
- * * an object id
- * * a binding
- * * the name of a class
+ * * an event name string
+ * * a filename string
+ * * a line number
+ * * a method name symbol, or nil
+ * * a binding, or nil
+ * * the class, module, or nil
*
- * _proc_ is invoked whenever an event occurs.
+ * _proc_ is invoked whenever an event occurs.
*
- * Events are:
+ * Events are:
*
- * +c-call+:: call a C-language routine
- * +c-return+:: return from a C-language routine
- * +call+:: call a Ruby method
- * +class+:: start a class or module definition
- * +end+:: finish a class or module definition
- * +line+:: execute code on a new line
- * +raise+:: raise an exception
- * +return+:: return from a Ruby method
+ * <code>"c-call"</code>:: call a C-language routine
+ * <code>"c-return"</code>:: return from a C-language routine
+ * <code>"call"</code>:: call a Ruby method
+ * <code>"class"</code>:: start a class or module definition
+ * <code>"end"</code>:: finish a class or module definition
+ * <code>"line"</code>:: execute code on a new line
+ * <code>"raise"</code>:: raise an exception
+ * <code>"return"</code>:: return from a Ruby method
*
- * Tracing is disabled within the context of _proc_.
+ * Tracing is disabled within the context of _proc_.
*
- * class Test
- * def test
- * a = 1
- * b = 2
- * end
- * end
+ * class Test
+ * def test
+ * a = 1
+ * b = 2
+ * end
+ * end
*
- * set_trace_func proc { |event, file, line, id, binding, classname|
- * printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
- * }
- * t = Test.new
- * t.test
+ * set_trace_func proc { |event, file, line, id, binding, class_or_module|
+ * printf "%8s %s:%-2d %16p %14p\n", event, file, line, id, class_or_module
+ * }
+ * t = Test.new
+ * t.test
*
- * line prog.rb:11 false
- * c-call prog.rb:11 new Class
- * c-call prog.rb:11 initialize Object
- * c-return prog.rb:11 initialize Object
- * c-return prog.rb:11 new Class
- * line prog.rb:12 false
- * call prog.rb:2 test Test
- * line prog.rb:3 test Test
- * line prog.rb:4 test Test
- * return prog.rb:4 test Test
+ * Produces:
*
- * Note that for +c-call+ and +c-return+ events, the binding returned is the
- * binding of the nearest Ruby method calling the C method, since C methods
- * themselves do not have bindings.
+ * c-return prog.rb:8 :set_trace_func Kernel
+ * line prog.rb:11 nil nil
+ * c-call prog.rb:11 :new Class
+ * c-call prog.rb:11 :initialize BasicObject
+ * c-return prog.rb:11 :initialize BasicObject
+ * c-return prog.rb:11 :new Class
+ * line prog.rb:12 nil nil
+ * call prog.rb:2 :test Test
+ * line prog.rb:3 :test Test
+ * line prog.rb:4 :test Test
+ * return prog.rb:5 :test Test
*/
static VALUE
@@ -681,6 +684,7 @@ get_event_id(rb_event_flag_t event)
C(thread_end, THREAD_END);
C(fiber_switch, FIBER_SWITCH);
C(script_compiled, SCRIPT_COMPILED);
+ C(rescue, RESCUE);
#undef C
default:
return 0;
@@ -697,8 +701,8 @@ get_path_and_lineno(const rb_execution_context_t *ec, const rb_control_frame_t *
*pathp = rb_iseq_path(iseq);
if (event & (RUBY_EVENT_CLASS |
- RUBY_EVENT_CALL |
- RUBY_EVENT_B_CALL)) {
+ RUBY_EVENT_CALL |
+ RUBY_EVENT_B_CALL)) {
*linep = FIX2INT(rb_iseq_first_lineno(iseq));
}
else {
@@ -730,7 +734,7 @@ call_trace_func(rb_event_flag_t event, VALUE proc, VALUE self, ID id, VALUE klas
if (RB_TYPE_P(klass, T_ICLASS)) {
klass = RBASIC(klass)->klass;
}
- else if (FL_TEST(klass, FL_SINGLETON)) {
+ else if (RCLASS_SINGLETON_P(klass)) {
klass = RCLASS_ATTACHED_OBJECT(klass);
}
}
@@ -779,16 +783,14 @@ tp_mark(void *ptr)
if (tp->target_th) rb_gc_mark(tp->target_th->self);
}
-static size_t
-tp_memsize(const void *ptr)
-{
- return sizeof(rb_tp_t);
-}
-
static const rb_data_type_t tp_data_type = {
"tracepoint",
- {tp_mark, RUBY_TYPED_DEFAULT_FREE, tp_memsize,},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+ {
+ tp_mark,
+ RUBY_TYPED_DEFAULT_FREE,
+ NULL, // Nothing allocated externally, so don't need a memsize function
+ },
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
};
static VALUE
@@ -823,6 +825,7 @@ symbol2event_flag(VALUE v)
C(thread_end, THREAD_END);
C(fiber_switch, FIBER_SWITCH);
C(script_compiled, SCRIPT_COMPILED);
+ C(rescue, RESCUE);
/* joke */
C(a_call, A_CALL);
@@ -943,6 +946,7 @@ rb_tracearg_parameters(rb_trace_arg_t *trace_arg)
case RUBY_EVENT_CLASS:
case RUBY_EVENT_END:
case RUBY_EVENT_SCRIPT_COMPILED:
+ case RUBY_EVENT_RESCUE:
rb_raise(rb_eRuntimeError, "not supported by this event");
break;
}
@@ -1013,7 +1017,7 @@ rb_tracearg_return_value(rb_trace_arg_t *trace_arg)
VALUE
rb_tracearg_raised_exception(rb_trace_arg_t *trace_arg)
{
- if (trace_arg->event & (RUBY_EVENT_RAISE)) {
+ if (trace_arg->event & (RUBY_EVENT_RAISE | RUBY_EVENT_RESCUE)) {
/* ok */
}
else {
@@ -1193,6 +1197,10 @@ rb_tracepoint_enable(VALUE tpval)
rb_raise(rb_eArgError, "can't nest-enable a targeting TracePoint");
}
+ if (tp->tracing) {
+ return Qundef;
+ }
+
if (tp->target_th) {
rb_thread_add_event_hook2(tp->target_th->self, (rb_event_hook_func_t)tp_call_trace, tp->events, tpval,
RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG);
@@ -1242,7 +1250,7 @@ rb_tracepoint_enable_for_target(VALUE tpval, VALUE target, VALUE target_line)
}
VM_ASSERT(tp->local_target_set == Qfalse);
- tp->local_target_set = rb_obj_hide(rb_ident_hash_new());
+ RB_OBJ_WRITE(tpval, &tp->local_target_set, rb_obj_hide(rb_ident_hash_new()));
/* bmethod */
if (rb_obj_is_method(target)) {
@@ -1251,6 +1259,7 @@ rb_tracepoint_enable_for_target(VALUE tpval, VALUE target, VALUE target_line)
(tp->events & (RUBY_EVENT_CALL | RUBY_EVENT_RETURN))) {
if (def->body.bmethod.hooks == NULL) {
def->body.bmethod.hooks = ZALLOC(rb_hook_list_t);
+ def->body.bmethod.hooks->is_local = true;
}
rb_hook_list_connect_tracepoint(target, def->body.bmethod.hooks, tpval, 0);
rb_hash_aset(tp->local_target_set, target, Qfalse);
@@ -1265,7 +1274,7 @@ rb_tracepoint_enable_for_target(VALUE tpval, VALUE target, VALUE target_line)
rb_hash_aset(tp->local_target_set, (VALUE)iseq, Qtrue);
if ((tp->events & (RUBY_EVENT_CALL | RUBY_EVENT_RETURN)) &&
- iseq->body->builtin_attrs & BUILTIN_ATTR_SINGLE_NOARG_INLINE) {
+ iseq->body->builtin_attrs & BUILTIN_ATTR_SINGLE_NOARG_LEAF) {
rb_clear_bf_ccs();
}
@@ -1313,7 +1322,7 @@ rb_tracepoint_disable(VALUE tpval)
if (tp->local_target_set) {
rb_hash_foreach(tp->local_target_set, disable_local_event_iseq_i, tpval);
- tp->local_target_set = Qfalse;
+ RB_OBJ_WRITE(tpval, &tp->local_target_set, Qfalse);
ruby_vm_event_local_num--;
}
else {
@@ -1380,6 +1389,9 @@ tracepoint_enable_m(rb_execution_context_t *ec, VALUE tpval, VALUE target, VALUE
rb_raise(rb_eArgError, "can not override target_thread filter");
}
tp->target_th = rb_thread_ptr(target_thread);
+
+ RUBY_ASSERT(tp->target_th->self == target_thread);
+ RB_OBJ_WRITTEN(tpval, Qundef, target_thread);
}
else {
tp->target_th = NULL;
@@ -1447,7 +1459,7 @@ tracepoint_new(VALUE klass, rb_thread_t *target_th, rb_event_flag_t events, void
rb_tp_t *tp;
TypedData_Get_Struct(tpval, rb_tp_t, &tp_data_type, tp);
- tp->proc = proc;
+ RB_OBJ_WRITE(tpval, &tp->proc, proc);
tp->ractor = rb_ractor_shareable_p(proc) ? NULL : GET_RACTOR();
tp->func = func;
tp->data = data;
@@ -1515,7 +1527,7 @@ tracepoint_inspect(rb_execution_context_t *ec, VALUE self)
VALUE sym = rb_tracearg_method_id(trace_arg);
if (NIL_P(sym))
break;
- return rb_sprintf("#<TracePoint:%"PRIsVALUE" %"PRIsVALUE":%d in `%"PRIsVALUE"'>",
+ return rb_sprintf("#<TracePoint:%"PRIsVALUE" %"PRIsVALUE":%d in '%"PRIsVALUE"'>",
rb_tracearg_event(trace_arg),
rb_tracearg_path(trace_arg),
FIX2INT(rb_tracearg_lineno(trace_arg)),
@@ -1525,7 +1537,7 @@ tracepoint_inspect(rb_execution_context_t *ec, VALUE self)
case RUBY_EVENT_C_CALL:
case RUBY_EVENT_RETURN:
case RUBY_EVENT_C_RETURN:
- return rb_sprintf("#<TracePoint:%"PRIsVALUE" `%"PRIsVALUE"' %"PRIsVALUE":%d>",
+ return rb_sprintf("#<TracePoint:%"PRIsVALUE" '%"PRIsVALUE"' %"PRIsVALUE":%d>",
rb_tracearg_event(trace_arg),
rb_tracearg_method_id(trace_arg),
rb_tracearg_path(trace_arg),
@@ -1614,17 +1626,22 @@ Init_vm_trace(void)
rb_undef_alloc_func(rb_cTracePoint);
}
-typedef struct rb_postponed_job_struct {
- rb_postponed_job_func_t func;
- void *data;
-} rb_postponed_job_t;
-
-#define MAX_POSTPONED_JOB 1000
-#define MAX_POSTPONED_JOB_SPECIAL_ADDITION 24
+/*
+ * Ruby actually has two separate mechanisms for enqueueing work from contexts
+ * where it is not safe to run Ruby code, to run later on when it is safe. One
+ * is async-signal-safe but more limited, and accessed through the
+ * `rb_postponed_job_preregister` and `rb_postponed_job_trigger` functions. The
+ * other is more flexible but cannot be used in signal handlers, and is accessed
+ * through the `rb_workqueue_register` function.
+ *
+ * The postponed job functions form part of Ruby's extension API, but the
+ * workqueue functions are for internal use only.
+ */
struct rb_workqueue_job {
struct ccan_list_node jnode; /* <=> vm->workqueue */
- rb_postponed_job_t job;
+ rb_postponed_job_func_t func;
+ void *data;
};
// Used for VM memsize reporting. Returns the size of a list of rb_workqueue_job
@@ -1642,52 +1659,51 @@ rb_vm_memsize_workqueue(struct ccan_list_head *workqueue)
return size;
}
-// Used for VM memsize reporting. Returns the total size of the postponed job
-// buffer that was allocated at initialization.
-size_t
-rb_vm_memsize_postponed_job_buffer(void)
-{
- return sizeof(rb_postponed_job_t) * MAX_POSTPONED_JOB;
-}
-
-void
-Init_vm_postponed_job(void)
+/*
+ * thread-safe and called from non-Ruby thread
+ * returns FALSE on failure (ENOMEM), TRUE otherwise
+ */
+int
+rb_workqueue_register(unsigned flags, rb_postponed_job_func_t func, void *data)
{
+ struct rb_workqueue_job *wq_job = malloc(sizeof(*wq_job));
rb_vm_t *vm = GET_VM();
- vm->postponed_job_buffer = ALLOC_N(rb_postponed_job_t, MAX_POSTPONED_JOB);
- vm->postponed_job_index = 0;
- /* workqueue is initialized when VM locks are initialized */
-}
-enum postponed_job_register_result {
- PJRR_SUCCESS = 0,
- PJRR_FULL = 1,
- PJRR_INTERRUPTED = 2
-};
-
-/* Async-signal-safe */
-static enum postponed_job_register_result
-postponed_job_register(rb_execution_context_t *ec, rb_vm_t *vm,
- unsigned int flags, rb_postponed_job_func_t func, void *data, rb_atomic_t max, rb_atomic_t expected_index)
-{
- rb_postponed_job_t *pjob;
+ if (!wq_job) return FALSE;
+ wq_job->func = func;
+ wq_job->data = data;
- if (expected_index >= max) return PJRR_FULL; /* failed */
+ rb_nativethread_lock_lock(&vm->workqueue_lock);
+ ccan_list_add_tail(&vm->workqueue, &wq_job->jnode);
+ rb_nativethread_lock_unlock(&vm->workqueue_lock);
- if (ATOMIC_CAS(vm->postponed_job_index, expected_index, expected_index+1) == expected_index) {
- pjob = &vm->postponed_job_buffer[expected_index];
- }
- else {
- return PJRR_INTERRUPTED;
- }
+ // TODO: current implementation affects only main ractor
+ RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(rb_vm_main_ractor_ec(vm));
- /* unused: pjob->flags = flags; */
- pjob->func = func;
- pjob->data = data;
+ return TRUE;
+}
- RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(ec);
+#define PJOB_TABLE_SIZE (sizeof(rb_atomic_t) * CHAR_BIT)
+/* pre-registered jobs table, for async-safe jobs */
+typedef struct rb_postponed_job_queue {
+ struct {
+ rb_postponed_job_func_t func;
+ void *data;
+ } table[PJOB_TABLE_SIZE];
+ /* Bits in this are set when the corresponding entry in prereg_table has non-zero
+ * triggered_count; i.e. somebody called rb_postponed_job_trigger */
+ rb_atomic_t triggered_bitset;
+} rb_postponed_job_queues_t;
- return PJRR_SUCCESS;
+void
+rb_vm_postponed_job_queue_init(rb_vm_t *vm)
+{
+ /* use mimmalloc; postponed job registration is a dependency of objspace, so this gets
+ * called _VERY_ early inside Init_BareVM */
+ rb_postponed_job_queues_t *pjq = ruby_mimmalloc(sizeof(rb_postponed_job_queues_t));
+ pjq->triggered_bitset = 0;
+ memset(pjq->table, 0, sizeof(pjq->table));
+ vm->postponed_job_queue = pjq;
}
static rb_execution_context_t *
@@ -1698,83 +1714,114 @@ get_valid_ec(rb_vm_t *vm)
return ec;
}
-/*
- * return 0 if job buffer is full
- * Async-signal-safe
- */
-int
-rb_postponed_job_register(unsigned int flags, rb_postponed_job_func_t func, void *data)
+void
+rb_vm_postponed_job_atfork(void)
{
rb_vm_t *vm = GET_VM();
- rb_execution_context_t *ec = get_valid_ec(vm);
-
- begin:
- switch (postponed_job_register(ec, vm, flags, func, data, MAX_POSTPONED_JOB, vm->postponed_job_index)) {
- case PJRR_SUCCESS : return 1;
- case PJRR_FULL : return 0;
- case PJRR_INTERRUPTED: goto begin;
- default: rb_bug("unreachable");
+ rb_postponed_job_queues_t *pjq = vm->postponed_job_queue;
+ /* make sure we set the interrupt flag on _this_ thread if we carried any pjobs over
+ * from the other side of the fork */
+ if (pjq->triggered_bitset) {
+ RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(get_valid_ec(vm));
}
+
}
-/*
- * return 0 if job buffer is full
- * Async-signal-safe
- */
-int
-rb_postponed_job_register_one(unsigned int flags, rb_postponed_job_func_t func, void *data)
+/* Frees the memory managed by the postponed job infrastructure at shutdown */
+void
+rb_vm_postponed_job_free(void)
{
rb_vm_t *vm = GET_VM();
- rb_execution_context_t *ec = get_valid_ec(vm);
- rb_postponed_job_t *pjob;
- rb_atomic_t i, index;
-
- begin:
- index = vm->postponed_job_index;
- for (i=0; i<index; i++) {
- pjob = &vm->postponed_job_buffer[i];
- if (pjob->func == func) {
- RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(ec);
- return 2;
+ ruby_xfree(vm->postponed_job_queue);
+ vm->postponed_job_queue = NULL;
+}
+
+// Used for VM memsize reporting. Returns the total size of the postponed job
+// queue infrastructure.
+size_t
+rb_vm_memsize_postponed_job_queue(void)
+{
+ return sizeof(rb_postponed_job_queues_t);
+}
+
+
+rb_postponed_job_handle_t
+rb_postponed_job_preregister(unsigned int flags, rb_postponed_job_func_t func, void *data)
+{
+ /* The doc comments say that this function should be called under the GVL, because
+ * that is actually required to get the guarantee that "if a given (func, data) pair
+ * was already pre-registered, this method will return the same handle instance".
+ *
+ * However, the actual implementation here is called without the GVL, from inside
+ * rb_postponed_job_register, to support that legacy interface. In the presence
+ * of concurrent calls to both _preregister and _register functions on the same
+ * func, however, the data may get mixed up between them. */
+
+ rb_postponed_job_queues_t *pjq = GET_VM()->postponed_job_queue;
+ for (unsigned int i = 0; i < PJOB_TABLE_SIZE; i++) {
+ /* Try and set this slot to equal `func` */
+ rb_postponed_job_func_t existing_func = (rb_postponed_job_func_t)RUBY_ATOMIC_PTR_CAS(pjq->table[i], NULL, (void *)func);
+ if (existing_func == NULL || existing_func == func) {
+ /* Either this slot was NULL, and we set it to func, or, this slot was already equal to func.
+ * In either case, clobber the data with our data. Note that concurrent calls to
+ * rb_postponed_job_register with the same func & different data will result in either of the
+ * datas being written */
+ RUBY_ATOMIC_PTR_EXCHANGE(pjq->table[i].data, data);
+ return (rb_postponed_job_handle_t)i;
+ }
+ else {
+ /* Try the next slot if this one already has a func in it */
+ continue;
}
}
- switch (postponed_job_register(ec, vm, flags, func, data, MAX_POSTPONED_JOB + MAX_POSTPONED_JOB_SPECIAL_ADDITION, index)) {
- case PJRR_SUCCESS : return 1;
- case PJRR_FULL : return 0;
- case PJRR_INTERRUPTED: goto begin;
- default: rb_bug("unreachable");
- }
+
+ /* full */
+ return POSTPONED_JOB_HANDLE_INVALID;
}
-/*
- * thread-safe and called from non-Ruby thread
- * returns FALSE on failure (ENOMEM), TRUE otherwise
- */
-int
-rb_workqueue_register(unsigned flags, rb_postponed_job_func_t func, void *data)
+void
+rb_postponed_job_trigger(rb_postponed_job_handle_t h)
{
- struct rb_workqueue_job *wq_job = malloc(sizeof(*wq_job));
rb_vm_t *vm = GET_VM();
+ rb_postponed_job_queues_t *pjq = vm->postponed_job_queue;
- if (!wq_job) return FALSE;
- wq_job->job.func = func;
- wq_job->job.data = data;
+ RUBY_ATOMIC_OR(pjq->triggered_bitset, (((rb_atomic_t)1UL) << h));
+ RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(get_valid_ec(vm));
+}
- rb_nativethread_lock_lock(&vm->workqueue_lock);
- ccan_list_add_tail(&vm->workqueue, &wq_job->jnode);
- rb_nativethread_lock_unlock(&vm->workqueue_lock);
- // TODO: current implementation affects only main ractor
- RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(rb_vm_main_ractor_ec(vm));
+static int
+pjob_register_legacy_impl(unsigned int flags, rb_postponed_job_func_t func, void *data)
+{
+ /* We _know_ calling preregister from a signal handler like this is racy; what is
+ * and is not promised is very exhaustively documented in debug.h */
+ rb_postponed_job_handle_t h = rb_postponed_job_preregister(0, func, data);
+ if (h == POSTPONED_JOB_HANDLE_INVALID) {
+ return 0;
+ }
+ rb_postponed_job_trigger(h);
+ return 1;
+}
- return TRUE;
+int
+rb_postponed_job_register(unsigned int flags, rb_postponed_job_func_t func, void *data)
+{
+ return pjob_register_legacy_impl(flags, func, data);
+}
+
+int
+rb_postponed_job_register_one(unsigned int flags, rb_postponed_job_func_t func, void *data)
+{
+ return pjob_register_legacy_impl(flags, func, data);
}
+
void
rb_postponed_job_flush(rb_vm_t *vm)
{
+ rb_postponed_job_queues_t *pjq = GET_VM()->postponed_job_queue;
rb_execution_context_t *ec = GET_EC();
- const rb_atomic_t block_mask = POSTPONED_JOB_INTERRUPT_MASK|TRAP_INTERRUPT_MASK;
+ const rb_atomic_t block_mask = POSTPONED_JOB_INTERRUPT_MASK | TRAP_INTERRUPT_MASK;
volatile rb_atomic_t saved_mask = ec->interrupt_mask & block_mask;
VALUE volatile saved_errno = ec->errinfo;
struct ccan_list_head tmp;
@@ -1785,26 +1832,31 @@ rb_postponed_job_flush(rb_vm_t *vm)
ccan_list_append_list(&tmp, &vm->workqueue);
rb_nativethread_lock_unlock(&vm->workqueue_lock);
+ rb_atomic_t triggered_bits = RUBY_ATOMIC_EXCHANGE(pjq->triggered_bitset, 0);
+
ec->errinfo = Qnil;
/* mask POSTPONED_JOB dispatch */
ec->interrupt_mask |= block_mask;
{
EC_PUSH_TAG(ec);
if (EC_EXEC_TAG() == TAG_NONE) {
- rb_atomic_t index;
- struct rb_workqueue_job *wq_job;
-
- while ((index = vm->postponed_job_index) > 0) {
- if (ATOMIC_CAS(vm->postponed_job_index, index, index-1) == index) {
- rb_postponed_job_t *pjob = &vm->postponed_job_buffer[index-1];
- (*pjob->func)(pjob->data);
- }
+ /* execute postponed jobs */
+ while (triggered_bits) {
+ unsigned int i = bit_length(triggered_bits) - 1;
+ triggered_bits ^= ((1UL) << i); /* toggle ith bit off */
+ rb_postponed_job_func_t func = pjq->table[i].func;
+ void *data = pjq->table[i].data;
+ (func)(data);
}
+
+ /* execute workqueue jobs */
+ struct rb_workqueue_job *wq_job;
while ((wq_job = ccan_list_pop(&tmp, struct rb_workqueue_job, jnode))) {
- rb_postponed_job_t pjob = wq_job->job;
+ rb_postponed_job_func_t func = wq_job->func;
+ void *data = wq_job->data;
free(wq_job);
- (pjob.func)(pjob.data);
+ (func)(data);
}
}
EC_POP_TAG();
@@ -1813,7 +1865,8 @@ rb_postponed_job_flush(rb_vm_t *vm)
ec->interrupt_mask &= ~(saved_mask ^ block_mask);
ec->errinfo = saved_errno;
- /* don't leak memory if a job threw an exception */
+ /* If we threw an exception, there might be leftover workqueue items; carry them over
+ * to a subsequent execution of flush */
if (!ccan_list_empty(&tmp)) {
rb_nativethread_lock_lock(&vm->workqueue_lock);
ccan_list_prepend_list(&vm->workqueue, &tmp);
@@ -1821,4 +1874,10 @@ rb_postponed_job_flush(rb_vm_t *vm)
RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(GET_EC());
}
+ /* likewise with any remaining-to-be-executed bits of the preregistered postponed
+ * job table */
+ if (triggered_bits) {
+ RUBY_ATOMIC_OR(pjq->triggered_bitset, triggered_bits);
+ RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(GET_EC());
+ }
}
diff --git a/vsnprintf.c b/vsnprintf.c
index 212bb06c5e..ecd5573dd5 100644
--- a/vsnprintf.c
+++ b/vsnprintf.c
@@ -1255,8 +1255,8 @@ cvt(double value, int ndigits, int flags, char *sign, int *decpt, int ch, int *l
}
buf[0] = 0; /* rve - digits may be 0 */
memcpy(buf, digits, rve - digits);
- xfree(digits);
rve = buf + (rve - digits);
+ free(digits);
digits = buf;
if (flags & ALT) { /* Print trailing zeros */
bp = digits + ndigits;
diff --git a/warning.rb b/warning.rb
index 4e34c63833..aab5e7c2c6 100644
--- a/warning.rb
+++ b/warning.rb
@@ -40,7 +40,7 @@ module Kernel
# baz.rb:6: warning: invalid call to foo
#
# If <code>category</code> keyword argument is given, passes the category
- # to <code>Warning.warn</code>. The category given must be be one of the
+ # to <code>Warning.warn</code>. The category given must be one of the
# following categories:
#
# :deprecated :: Used for warning for deprecated functionality that may
diff --git a/wasm/runtime.c b/wasm/runtime.c
index b5b0a1a966..89b06be6ad 100644
--- a/wasm/runtime.c
+++ b/wasm/runtime.c
@@ -19,6 +19,13 @@ int rb_wasm_rt_start(int (main)(int argc, char **argv), int argc, char **argv) {
result = main(argc, argv);
}
+ extern void *rb_asyncify_unwind_buf;
+ // Exit Asyncify loop if there is no unwound buffer, which
+ // means that main function has returned normally.
+ if (rb_asyncify_unwind_buf == NULL) {
+ break;
+ }
+
// NOTE: it's important to call 'asyncify_stop_unwind' here instead in rb_wasm_handle_jmp_unwind
// because unless that, Asyncify inserts another unwind check here and it unwinds to the root frame.
asyncify_stop_unwind();
diff --git a/wasm/setjmp.c b/wasm/setjmp.c
index c782987454..ebbf8949c1 100644
--- a/wasm/setjmp.c
+++ b/wasm/setjmp.c
@@ -101,7 +101,6 @@ _rb_wasm_setjmp_internal(rb_wasm_jmp_buf *env)
asyncify_stop_rewind();
RB_WASM_DEBUG_LOG(" JMP_BUF_STATE_RETURNING");
env->state = JMP_BUF_STATE_CAPTURED;
- free(env->longjmp_buf_ptr);
_rb_wasm_active_jmpbuf = NULL;
return env->payload;
}
@@ -119,7 +118,10 @@ _rb_wasm_longjmp(rb_wasm_jmp_buf* env, int value)
assert(value != 0);
env->state = JMP_BUF_STATE_RETURNING;
env->payload = value;
- env->longjmp_buf_ptr = malloc(sizeof(struct __rb_wasm_asyncify_jmp_buf));
+ // Asyncify buffer built during unwinding for longjmp will not
+ // be used to rewind, so re-use static-variable.
+ static struct __rb_wasm_asyncify_jmp_buf tmp_longjmp_buf;
+ env->longjmp_buf_ptr = &tmp_longjmp_buf;
_rb_wasm_active_jmpbuf = env;
async_buf_init(env->longjmp_buf_ptr);
asyncify_start_unwind(env->longjmp_buf_ptr);
@@ -153,23 +155,21 @@ rb_wasm_try_catch_loop_run(struct rb_wasm_try_catch *try_catch, rb_wasm_jmp_buf
target->state = JMP_BUF_STATE_CAPTURED;
switch ((enum try_catch_phase)try_catch->state) {
- case TRY_CATCH_PHASE_MAIN: {
+ case TRY_CATCH_PHASE_MAIN:
// may unwind
try_catch->try_f(try_catch->context);
break;
- }
- case TRY_CATCH_PHASE_RESCUE: {
+ case TRY_CATCH_PHASE_RESCUE:
if (try_catch->catch_f) {
// may unwind
try_catch->catch_f(try_catch->context);
}
break;
}
- }
- while (1) {
+ {
// catch longjmp with target jmp_buf
- if (rb_asyncify_unwind_buf && _rb_wasm_active_jmpbuf == target) {
+ while (rb_asyncify_unwind_buf && _rb_wasm_active_jmpbuf == target) {
// do similar steps setjmp does when JMP_BUF_STATE_RETURNING
// stop unwinding
@@ -184,14 +184,9 @@ rb_wasm_try_catch_loop_run(struct rb_wasm_try_catch *try_catch, rb_wasm_jmp_buf
if (try_catch->catch_f) {
try_catch->catch_f(try_catch->context);
}
- continue;
- } else if (rb_asyncify_unwind_buf /* unrelated unwind */) {
- return;
}
- // no unwind, then exit
- break;
+ // no unwind or unrelated unwind, then exit
}
- return;
}
void *
@@ -203,18 +198,16 @@ rb_wasm_handle_jmp_unwind(void)
}
switch (_rb_wasm_active_jmpbuf->state) {
- case JMP_BUF_STATE_CAPTURING: {
+ case JMP_BUF_STATE_CAPTURING:
RB_WASM_DEBUG_LOG(" JMP_BUF_STATE_CAPTURING");
// save the captured Asyncify stack top
_rb_wasm_active_jmpbuf->dst_buf_top = _rb_wasm_active_jmpbuf->setjmp_buf.top;
break;
- }
- case JMP_BUF_STATE_RETURNING: {
+ case JMP_BUF_STATE_RETURNING:
RB_WASM_DEBUG_LOG(" JMP_BUF_STATE_RETURNING");
// restore the saved Asyncify stack top
_rb_wasm_active_jmpbuf->setjmp_buf.top = _rb_wasm_active_jmpbuf->dst_buf_top;
break;
- }
default:
assert(0 && "unexpected state");
}
diff --git a/weakmap.c b/weakmap.c
index 17883f05a4..86920952f3 100644
--- a/weakmap.c
+++ b/weakmap.c
@@ -4,70 +4,79 @@
#include "internal/proc.h"
#include "internal/sanitizers.h"
#include "ruby/st.h"
-#include "ruby/st.h"
+
+/* ===== WeakMap =====
+ *
+ * WeakMap contains one ST table which contains a pointer to the object as the
+ * key and a pointer to the object as the value. This means that the key and
+ * value of the table are both of the type `VALUE *`.
+ *
+ * The objects are not directly stored as keys and values in the table because
+ * `rb_gc_mark_weak` requires a pointer to the memory location to overwrite
+ * when the object is reclaimed. Using a pointer into the ST table entry is not
+ * safe because the pointer can change when the ST table is resized.
+ *
+ * WeakMap hashes and compares using the pointer address of the object.
+ *
+ * For performance and memory efficiency reasons, the key and value
+ * are allocated at the same time and adjacent to each other.
+ *
+ * During GC and while iterating, reclaimed entries (i.e. either the key or
+ * value points to `Qundef`) are removed from the ST table.
+ */
struct weakmap {
- st_table *obj2wmap; /* obj -> [ref,...] */
- st_table *wmap2obj; /* ref -> obj */
- VALUE final;
+ st_table *table;
};
-static int
-wmap_replace_ref(st_data_t *key, st_data_t *value, st_data_t _argp, int existing)
+static bool
+wmap_live_p(VALUE obj)
{
- *key = rb_gc_location((VALUE)*key);
-
- VALUE *values = (VALUE *)*value;
- VALUE size = values[0];
+ return !UNDEF_P(obj);
+}
- for (VALUE index = 1; index <= size; index++) {
- values[index] = rb_gc_location(values[index]);
- }
+static void
+wmap_free_entry(VALUE *key, VALUE *val)
+{
+ RUBY_ASSERT(key + 1 == val);
- return ST_CONTINUE;
+ /* We only need to free key because val is allocated beside key on in the
+ * same malloc call. */
+ ruby_sized_xfree(key, sizeof(VALUE) * 2);
}
static int
-wmap_foreach_replace(st_data_t key, st_data_t value, st_data_t _argp, int error)
+wmap_mark_weak_table_i(st_data_t key, st_data_t val, st_data_t _)
{
- if (rb_gc_location((VALUE)key) != (VALUE)key) {
- return ST_REPLACE;
- }
+ VALUE key_obj = *(VALUE *)key;
+ VALUE val_obj = *(VALUE *)val;
- VALUE *values = (VALUE *)value;
- VALUE size = values[0];
+ if (wmap_live_p(key_obj) && wmap_live_p(val_obj)) {
+ rb_gc_mark_weak((VALUE *)key);
+ rb_gc_mark_weak((VALUE *)val);
- for (VALUE index = 1; index <= size; index++) {
- VALUE val = values[index];
- if (rb_gc_location(val) != val) {
- return ST_REPLACE;
- }
+ return ST_CONTINUE;
}
+ else {
+ wmap_free_entry((VALUE *)key, (VALUE *)val);
- return ST_CONTINUE;
-}
-
-static void
-wmap_compact(void *ptr)
-{
- struct weakmap *w = ptr;
- if (w->wmap2obj) rb_gc_update_tbl_refs(w->wmap2obj);
- if (w->obj2wmap) st_foreach_with_replace(w->obj2wmap, wmap_foreach_replace, wmap_replace_ref, (st_data_t)NULL);
- w->final = rb_gc_location(w->final);
+ return ST_DELETE;
+ }
}
static void
wmap_mark(void *ptr)
{
struct weakmap *w = ptr;
- rb_gc_mark_movable(w->final);
+ if (w->table) {
+ st_foreach(w->table, wmap_mark_weak_table_i, (st_data_t)0);
+ }
}
static int
-wmap_free_map(st_data_t key, st_data_t val, st_data_t arg)
+wmap_free_table_i(st_data_t key, st_data_t val, st_data_t arg)
{
- VALUE *ptr = (VALUE *)val;
- ruby_sized_xfree(ptr, (ptr[0] + 1) * sizeof(VALUE));
+ wmap_free_entry((VALUE *)key, (VALUE *)val);
return ST_CONTINUE;
}
@@ -75,32 +84,70 @@ static void
wmap_free(void *ptr)
{
struct weakmap *w = ptr;
- st_foreach(w->obj2wmap, wmap_free_map, 0);
- st_free_table(w->obj2wmap);
- st_free_table(w->wmap2obj);
- xfree(w);
-}
-static int
-wmap_memsize_map(st_data_t key, st_data_t val, st_data_t arg)
-{
- VALUE *ptr = (VALUE *)val;
- *(size_t *)arg += (ptr[0] + 1) * sizeof(VALUE);
- return ST_CONTINUE;
+ st_foreach(w->table, wmap_free_table_i, 0);
+ st_free_table(w->table);
}
static size_t
wmap_memsize(const void *ptr)
{
- size_t size;
const struct weakmap *w = ptr;
- size = sizeof(*w);
- size += st_memsize(w->obj2wmap);
- size += st_memsize(w->wmap2obj);
- st_foreach(w->obj2wmap, wmap_memsize_map, (st_data_t)&size);
+
+ size_t size = 0;
+ size += st_memsize(w->table);
+ /* The key and value of the table each take sizeof(VALUE) in size. */
+ size += st_table_size(w->table) * (2 * sizeof(VALUE));
+
return size;
}
+static int
+wmap_compact_table_i(st_data_t key, st_data_t val, st_data_t data)
+{
+ st_table *table = (st_table *)data;
+
+ VALUE key_obj = *(VALUE *)key;
+ VALUE val_obj = *(VALUE *)val;
+
+ if (wmap_live_p(key_obj) && wmap_live_p(val_obj)) {
+ VALUE new_key_obj = rb_gc_location(key_obj);
+
+ *(VALUE *)val = rb_gc_location(val_obj);
+
+ /* If the key object moves, then we must reinsert because the hash is
+ * based on the pointer rather than the object itself. */
+ if (key_obj != new_key_obj) {
+ *(VALUE *)key = new_key_obj;
+
+ DURING_GC_COULD_MALLOC_REGION_START();
+ {
+ st_insert(table, key, val);
+ }
+ DURING_GC_COULD_MALLOC_REGION_END();
+
+ return ST_DELETE;
+ }
+ }
+ else {
+ wmap_free_entry((VALUE *)key, (VALUE *)val);
+
+ return ST_DELETE;
+ }
+
+ return ST_CONTINUE;
+}
+
+static void
+wmap_compact(void *ptr)
+{
+ struct weakmap *w = ptr;
+
+ if (w->table) {
+ st_foreach(w->table, wmap_compact_table_i, (st_data_t)w->table);
+ }
+}
+
static const rb_data_type_t weakmap_type = {
"weakmap",
{
@@ -109,114 +156,71 @@ static const rb_data_type_t weakmap_type = {
wmap_memsize,
wmap_compact,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
};
-static VALUE wmap_finalize(RB_BLOCK_CALL_FUNC_ARGLIST(objid, self));
+static int
+wmap_cmp(st_data_t x, st_data_t y)
+{
+ return *(VALUE *)x != *(VALUE *)y;
+}
+
+static st_index_t
+wmap_hash(st_data_t n)
+{
+ return st_numhash(*(VALUE *)n);
+}
+
+static const struct st_hash_type wmap_hash_type = {
+ wmap_cmp,
+ wmap_hash,
+};
static VALUE
wmap_allocate(VALUE klass)
{
struct weakmap *w;
VALUE obj = TypedData_Make_Struct(klass, struct weakmap, &weakmap_type, w);
- w->obj2wmap = rb_init_identtable();
- w->wmap2obj = rb_init_identtable();
- RB_OBJ_WRITE(obj, &w->final, rb_func_lambda_new(wmap_finalize, obj, 1, 1));
+ w->table = st_init_table(&wmap_hash_type);
return obj;
}
-static int
-wmap_live_p(VALUE obj)
-{
- if (SPECIAL_CONST_P(obj)) return TRUE;
- /* If rb_gc_is_ptr_to_obj returns false, the page could be in the tomb heap
- * or have already been freed. */
- if (!rb_gc_is_ptr_to_obj((void *)obj)) return FALSE;
-
- void *poisoned = asan_poisoned_object_p(obj);
- asan_unpoison_object(obj, false);
-
- enum ruby_value_type t = BUILTIN_TYPE(obj);
- int ret = (!(t == T_NONE || t >= T_FIXNUM || t == T_ICLASS) &&
- !rb_objspace_garbage_object_p(obj));
-
- if (poisoned) {
- asan_poison_object(obj);
- }
-
- return ret;
-}
+struct wmap_foreach_data {
+ struct weakmap *w;
+ void (*func)(VALUE, VALUE, st_data_t);
+ st_data_t arg;
+};
static int
-wmap_remove_inverse_ref(st_data_t *key, st_data_t *val, st_data_t arg, int existing)
+wmap_foreach_i(st_data_t key, st_data_t val, st_data_t arg)
{
- if (!existing) return ST_STOP;
-
- VALUE old_ref = (VALUE)arg;
-
- VALUE *values = (VALUE *)*val;
- VALUE size = values[0];
+ struct wmap_foreach_data *data = (struct wmap_foreach_data *)arg;
- if (size == 1) {
- // fast path, we only had one backref
- RUBY_ASSERT(values[1] == old_ref);
- ruby_sized_xfree(values, 2 * sizeof(VALUE));
- return ST_DELETE;
- }
+ VALUE key_obj = *(VALUE *)key;
+ VALUE val_obj = *(VALUE *)val;
- bool found = false;
- VALUE index = 1;
- for (; index <= size; index++) {
- if (values[index] == old_ref) {
- found = true;
- break;
- }
+ if (wmap_live_p(key_obj) && wmap_live_p(val_obj)) {
+ data->func(key_obj, val_obj, data->arg);
}
- if (!found) return ST_STOP;
+ else {
+ wmap_free_entry((VALUE *)key, (VALUE *)val);
- if (size > index) {
- MEMMOVE(&values[index], &values[index + 1], VALUE, size - index);
+ return ST_DELETE;
}
- size -= 1;
- values[0] = size;
- SIZED_REALLOC_N(values, VALUE, size + 1, size + 2);
- *val = (st_data_t)values;
return ST_CONTINUE;
}
-/* :nodoc: */
-static VALUE
-wmap_finalize(RB_BLOCK_CALL_FUNC_ARGLIST(objid, self))
+static void
+wmap_foreach(struct weakmap *w, void (*func)(VALUE, VALUE, st_data_t), st_data_t arg)
{
- st_data_t orig, wmap, data;
- VALUE obj, *rids, i, size;
- struct weakmap *w;
-
- TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
- /* Get reference from object id. */
- if (UNDEF_P(obj = rb_gc_id2ref_obj_tbl(objid))) {
- rb_bug("wmap_finalize: objid is not found.");
- }
-
- /* obj is original referenced object and/or weak reference. */
- orig = (st_data_t)obj;
- if (st_delete(w->obj2wmap, &orig, &data)) {
- rids = (VALUE *)data;
- size = *rids++;
- for (i = 0; i < size; ++i) {
- wmap = (st_data_t)rids[i];
- st_delete(w->wmap2obj, &wmap, NULL);
- }
- ruby_sized_xfree((VALUE *)data, (size + 1) * sizeof(VALUE));
- }
+ struct wmap_foreach_data foreach_data = {
+ .w = w,
+ .func = func,
+ .arg = arg,
+ };
- wmap = (st_data_t)obj;
- if (st_delete(w->wmap2obj, &wmap, &orig)) {
- wmap = (st_data_t)obj;
- st_update(w->obj2wmap, orig, wmap_remove_inverse_ref, wmap);
- }
- return self;
+ st_foreach(w->table, wmap_foreach_i, (st_data_t)&foreach_data);
}
static VALUE
@@ -225,19 +229,15 @@ wmap_inspect_append(VALUE str, VALUE obj)
if (SPECIAL_CONST_P(obj)) {
return rb_str_append(str, rb_inspect(obj));
}
- else if (wmap_live_p(obj)) {
- return rb_str_append(str, rb_any_to_s(obj));
- }
else {
- return rb_str_catf(str, "#<collected:%p>", (void*)obj);
+ return rb_str_append(str, rb_any_to_s(obj));
}
}
-static int
-wmap_inspect_i(st_data_t key, st_data_t val, st_data_t arg)
+static void
+wmap_inspect_i(VALUE key, VALUE val, st_data_t data)
{
- VALUE str = (VALUE)arg;
- VALUE k = (VALUE)key, v = (VALUE)val;
+ VALUE str = (VALUE)data;
if (RSTRING_PTR(str)[0] == '#') {
rb_str_cat2(str, ", ");
@@ -246,11 +246,10 @@ wmap_inspect_i(st_data_t key, st_data_t val, st_data_t arg)
rb_str_cat2(str, ": ");
RSTRING_PTR(str)[0] = '#';
}
- wmap_inspect_append(str, k);
- rb_str_cat2(str, " => ");
- wmap_inspect_append(str, v);
- return ST_CONTINUE;
+ wmap_inspect_append(str, key);
+ rb_str_cat2(str, " => ");
+ wmap_inspect_append(str, val);
}
static VALUE
@@ -261,104 +260,105 @@ wmap_inspect(VALUE self)
TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
VALUE str = rb_sprintf("-<%"PRIsVALUE":%p", c, (void *)self);
- if (w->wmap2obj) {
- st_foreach(w->wmap2obj, wmap_inspect_i, (st_data_t)str);
- }
+
+ wmap_foreach(w, wmap_inspect_i, (st_data_t)str);
+
RSTRING_PTR(str)[0] = '#';
rb_str_cat2(str, ">");
+
return str;
}
-static inline bool
-wmap_live_entry_p(st_data_t key, st_data_t val)
+static void
+wmap_each_i(VALUE key, VALUE val, st_data_t _)
{
- return wmap_live_p((VALUE)key) && wmap_live_p((VALUE)val);
+ rb_yield_values(2, key, val);
}
-static int
-wmap_each_i(st_data_t key, st_data_t val, st_data_t _)
-{
- if (wmap_live_entry_p(key, val)) {
- rb_yield_values(2, (VALUE)key, (VALUE)val);
- return ST_CONTINUE;
- }
- else {
- return ST_DELETE;
- }
-}
-
-/* Iterates over keys and objects in a weakly referenced object */
+/*
+ * call-seq:
+ * map.each {|key, val| ... } -> self
+ *
+ * Iterates over keys and values. Note that unlike other collections,
+ * +each+ without block isn't supported.
+ *
+ */
static VALUE
wmap_each(VALUE self)
{
struct weakmap *w;
-
TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
- st_foreach(w->wmap2obj, wmap_each_i, (st_data_t)0);
+
+ wmap_foreach(w, wmap_each_i, (st_data_t)0);
+
return self;
}
-static int
-wmap_each_key_i(st_data_t key, st_data_t val, st_data_t arg)
+static void
+wmap_each_key_i(VALUE key, VALUE _val, st_data_t _data)
{
- if (wmap_live_entry_p(key, val)) {
- rb_yield((VALUE)key);
- return ST_CONTINUE;
- }
- else {
- return ST_DELETE;
- }
+ rb_yield(key);
}
-/* Iterates over keys and objects in a weakly referenced object */
+/*
+ * call-seq:
+ * map.each_key {|key| ... } -> self
+ *
+ * Iterates over keys. Note that unlike other collections,
+ * +each_key+ without block isn't supported.
+ *
+ */
static VALUE
wmap_each_key(VALUE self)
{
struct weakmap *w;
-
TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
- st_foreach(w->wmap2obj, wmap_each_key_i, (st_data_t)0);
+
+ wmap_foreach(w, wmap_each_key_i, (st_data_t)0);
+
return self;
}
-static int
-wmap_each_value_i(st_data_t key, st_data_t val, st_data_t arg)
+static void
+wmap_each_value_i(VALUE _key, VALUE val, st_data_t _data)
{
- if (wmap_live_entry_p(key, val)) {
- rb_yield((VALUE)val);
- return ST_CONTINUE;
- }
- else {
- return ST_DELETE;
- }
+ rb_yield(val);
}
-/* Iterates over keys and objects in a weakly referenced object */
+/*
+ * call-seq:
+ * map.each_value {|val| ... } -> self
+ *
+ * Iterates over values. Note that unlike other collections,
+ * +each_value+ without block isn't supported.
+ *
+ */
static VALUE
wmap_each_value(VALUE self)
{
struct weakmap *w;
-
TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
- st_foreach(w->wmap2obj, wmap_each_value_i, (st_data_t)0);
+
+ wmap_foreach(w, wmap_each_value_i, (st_data_t)0);
+
return self;
}
-static int
-wmap_keys_i(st_data_t key, st_data_t val, st_data_t arg)
+static void
+wmap_keys_i(st_data_t key, st_data_t _, st_data_t arg)
{
VALUE ary = (VALUE)arg;
- if (wmap_live_entry_p(key, val)) {
- rb_ary_push(ary, (VALUE)key);
- return ST_CONTINUE;
- }
- else {
- return ST_DELETE;
- }
+ rb_ary_push(ary, key);
}
-/* Iterates over keys and objects in a weakly referenced object */
+/*
+ * call-seq:
+ * map.keys -> new_array
+ *
+ * Returns a new Array containing all keys in the map.
+ *
+ */
static VALUE
wmap_keys(VALUE self)
{
@@ -366,25 +366,26 @@ wmap_keys(VALUE self)
TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
VALUE ary = rb_ary_new();
- st_foreach(w->wmap2obj, wmap_keys_i, (st_data_t)ary);
+ wmap_foreach(w, wmap_keys_i, (st_data_t)ary);
+
return ary;
}
-static int
+static void
wmap_values_i(st_data_t key, st_data_t val, st_data_t arg)
{
VALUE ary = (VALUE)arg;
- if (wmap_live_entry_p(key, val)) {
- rb_ary_push(ary, (VALUE)val);
- return ST_CONTINUE;
- }
- else {
- return ST_DELETE;
- }
+ rb_ary_push(ary, (VALUE)val);
}
-/* Iterates over values and objects in a weakly referenced object */
+/*
+ * call-seq:
+ * map.values -> new_array
+ *
+ * Returns a new Array containing all values in the map.
+ *
+ */
static VALUE
wmap_values(VALUE self)
{
@@ -392,37 +393,9 @@ wmap_values(VALUE self)
TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
VALUE ary = rb_ary_new();
- st_foreach(w->wmap2obj, wmap_values_i, (st_data_t)ary);
- return ary;
-}
-
-static int
-wmap_aset_update(st_data_t *key, st_data_t *val, st_data_t arg, int existing)
-{
- VALUE size, *ptr, *optr;
- if (existing) {
- size = (ptr = optr = (VALUE *)*val)[0];
+ wmap_foreach(w, wmap_values_i, (st_data_t)ary);
- for (VALUE index = 1; index <= size; index++) {
- if (ptr[index] == (VALUE)arg) {
- // The reference was already registered.
- return ST_STOP;
- }
- }
-
- ++size;
- SIZED_REALLOC_N(ptr, VALUE, size + 1, size);
- }
- else {
- optr = 0;
- size = 1;
- ptr = ruby_xmalloc(2 * sizeof(VALUE));
- }
- ptr[0] = size;
- ptr[size] = (VALUE)arg;
- if (ptr == optr) return ST_STOP;
- *val = (st_data_t)ptr;
- return ST_CONTINUE;
+ return ary;
}
static VALUE
@@ -437,77 +410,78 @@ nonspecial_obj_id(VALUE obj)
#endif
}
-struct wmap_aset_replace_args {
- VALUE new_value;
- VALUE old_value;
-};
-
static int
-wmap_aset_replace_value(st_data_t *key, st_data_t *val, st_data_t _args, int existing)
+wmap_aset_replace(st_data_t *key, st_data_t *val, st_data_t new_key_ptr, int existing)
{
- struct wmap_aset_replace_args *args = (struct wmap_aset_replace_args *)_args;
+ VALUE new_key = *(VALUE *)new_key_ptr;
+ VALUE new_val = *(((VALUE *)new_key_ptr) + 1);
if (existing) {
- args->old_value = *val;
+ RUBY_ASSERT(*(VALUE *)*key == new_key);
+ }
+ else {
+ VALUE *pair = xmalloc(sizeof(VALUE) * 2);
+
+ *key = (st_data_t)pair;
+ *val = (st_data_t)(pair + 1);
}
- *val = (st_data_t)args->new_value;
+
+ *(VALUE *)*key = new_key;
+ *(VALUE *)*val = new_val;
+
return ST_CONTINUE;
}
-/* Creates a weak reference from the given key to the given value */
+/*
+ * call-seq:
+ * map[key] = value -> value
+ *
+ * Associates the given +value+ with the given +key+.
+ *
+ * If the given +key+ exists, replaces its value with the given +value+;
+ * the ordering is not affected.
+ */
static VALUE
-wmap_aset(VALUE self, VALUE key, VALUE value)
+wmap_aset(VALUE self, VALUE key, VALUE val)
{
struct weakmap *w;
-
TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
- if (FL_ABLE(value)) {
- rb_define_finalizer_no_check(value, w->final);
- }
- if (FL_ABLE(key)) {
- rb_define_finalizer_no_check(key, w->final);
- }
- struct wmap_aset_replace_args aset_args = {
- .new_value = value,
- .old_value = Qundef,
- };
- st_update(w->wmap2obj, (st_data_t)key, wmap_aset_replace_value, (st_data_t)&aset_args);
+ VALUE pair[2] = { key, val };
- // If the value is unchanged, we have nothing to do.
- if (value != aset_args.old_value) {
- if (!UNDEF_P(aset_args.old_value) && FL_ABLE(aset_args.old_value)) {
- // That key existed and had an inverse reference, we need to clear the outdated inverse reference.
- st_update(w->obj2wmap, (st_data_t)aset_args.old_value, wmap_remove_inverse_ref, key);
- }
+ st_update(w->table, (st_data_t)pair, wmap_aset_replace, (st_data_t)pair);
- if (FL_ABLE(value)) {
- // If the value has no finalizer, we don't need to keep the inverse reference
- st_update(w->obj2wmap, (st_data_t)value, wmap_aset_update, key);
- }
- }
+ RB_OBJ_WRITTEN(self, Qundef, key);
+ RB_OBJ_WRITTEN(self, Qundef, val);
- return nonspecial_obj_id(value);
+ return nonspecial_obj_id(val);
}
/* Retrieves a weakly referenced object with the given key */
static VALUE
wmap_lookup(VALUE self, VALUE key)
{
- assert(wmap_live_p(key));
+ RUBY_ASSERT(wmap_live_p(key));
struct weakmap *w;
TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
st_data_t data;
- if (!st_lookup(w->wmap2obj, (st_data_t)key, &data)) return Qundef;
+ if (!st_lookup(w->table, (st_data_t)&key, &data)) return Qundef;
- VALUE obj = (VALUE)data;
- if (!wmap_live_p(obj)) return Qundef;
- return obj;
+ if (!wmap_live_p(*(VALUE *)data)) return Qundef;
+
+ return *(VALUE *)data;
}
-/* Retrieves a weakly referenced object with the given key */
+/*
+ * call-seq:
+ * map[key] -> value
+ *
+ * Returns the value associated with the given +key+ if found.
+ *
+ * If +key+ is not found, returns +nil+.
+ */
static VALUE
wmap_aref(VALUE self, VALUE key)
{
@@ -515,24 +489,57 @@ wmap_aref(VALUE self, VALUE key)
return !UNDEF_P(obj) ? obj : Qnil;
}
-/* Delete the given key from the map */
+/*
+ * call-seq:
+ * map.delete(key) -> value or nil
+ * map.delete(key) {|key| ... } -> object
+ *
+ * Deletes the entry for the given +key+ and returns its associated value.
+ *
+ * If no block is given and +key+ is found, deletes the entry and returns the associated value:
+ * m = ObjectSpace::WeakMap.new
+ * key = "foo"
+ * m[key] = 1
+ * m.delete(key) # => 1
+ * m[key] # => nil
+ *
+ * If no block is given and +key+ is not found, returns +nil+.
+ *
+ * If a block is given and +key+ is found, ignores the block,
+ * deletes the entry, and returns the associated value:
+ * m = ObjectSpace::WeakMap.new
+ * key = "foo"
+ * m[key] = 2
+ * m.delete(key) { |key| raise 'Will never happen'} # => 2
+ *
+ * If a block is given and +key+ is not found,
+ * yields the +key+ to the block and returns the block's return value:
+ * m = ObjectSpace::WeakMap.new
+ * m.delete("nosuch") { |key| "Key #{key} not found" } # => "Key nosuch not found"
+ */
static VALUE
wmap_delete(VALUE self, VALUE key)
{
- assert(wmap_live_p(key));
-
struct weakmap *w;
TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
- VALUE old_value = Qnil;
- if (st_delete(w->wmap2obj, (st_data_t *)&key, (st_data_t *)&old_value)) {
- if (FL_ABLE(old_value)) {
- // That key existed and had an inverse reference, we need to clear the outdated inverse reference.
- st_update(w->obj2wmap, (st_data_t)old_value, wmap_remove_inverse_ref, key);
+ VALUE orig_key = key;
+ st_data_t orig_key_data = (st_data_t)&orig_key;
+ st_data_t orig_val_data;
+ if (st_delete(w->table, &orig_key_data, &orig_val_data)) {
+ VALUE orig_val = *(VALUE *)orig_val_data;
+
+ rb_gc_remove_weak(self, (VALUE *)orig_key_data);
+ rb_gc_remove_weak(self, (VALUE *)orig_val_data);
+
+ wmap_free_entry((VALUE *)orig_key_data, (VALUE *)orig_val_data);
+
+ if (wmap_live_p(orig_val)) {
+ return orig_val;
}
- return old_value;
}
- else if (rb_block_given_p()) {
+
+ if (rb_block_given_p()) {
return rb_yield(key);
}
else {
@@ -540,22 +547,32 @@ wmap_delete(VALUE self, VALUE key)
}
}
-/* Returns +true+ if +key+ is registered */
+/*
+ * call-seq:
+ * map.key?(key) -> true or false
+ *
+ * Returns +true+ if +key+ is a key in +self+, otherwise +false+.
+ */
static VALUE
wmap_has_key(VALUE self, VALUE key)
{
return RBOOL(!UNDEF_P(wmap_lookup(self, key)));
}
-/* Returns the number of referenced objects */
+/*
+ * call-seq:
+ * map.size -> number
+ *
+ * Returns the number of referenced objects
+ */
static VALUE
wmap_size(VALUE self)
{
struct weakmap *w;
- st_index_t n;
-
TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
- n = w->wmap2obj->num_entries;
+
+ st_index_t n = st_table_size(w->table);
+
#if SIZEOF_ST_INDEX_T <= SIZEOF_LONG
return ULONG2NUM(n);
#else
@@ -563,72 +580,122 @@ wmap_size(VALUE self)
#endif
}
-typedef struct weakkeymap_entry {
- VALUE obj;
- st_index_t hash;
-} weakkeymap_entry_t;
+/* ===== WeakKeyMap =====
+ *
+ * WeakKeyMap contains one ST table which contains a pointer to the object as
+ * the key and the object as the value. This means that the key is of the type
+ * `VALUE *` while the value is of the type `VALUE`.
+ *
+ * The object is not directly stored as keys in the table because
+ * `rb_gc_mark_weak` requires a pointer to the memory location to overwrite
+ * when the object is reclaimed. Using a pointer into the ST table entry is not
+ * safe because the pointer can change when the ST table is resized.
+ *
+ * WeakKeyMap hashes and compares using the `#hash` and `#==` methods of the
+ * object, respectively.
+ *
+ * During GC and while iterating, reclaimed entries (i.e. the key points to
+ * `Qundef`) are removed from the ST table.
+ */
struct weakkeymap {
- st_table *map;
- st_table *obj2hash;
- VALUE final;
+ st_table *table;
};
static int
-weakkeymap_cmp_entry(st_data_t a, st_data_t b)
+wkmap_mark_table_i(st_data_t key, st_data_t val_obj, st_data_t _)
{
- struct weakkeymap_entry *entry_a = (struct weakkeymap_entry *)a;
- struct weakkeymap_entry *entry_b = (struct weakkeymap_entry *)b;
- if (entry_a == entry_b) {
- return 0;
+ VALUE key_obj = *(VALUE *)key;
+
+ if (wmap_live_p(key_obj)) {
+ rb_gc_mark_weak((VALUE *)key);
+ rb_gc_mark_movable((VALUE)val_obj);
+
+ return ST_CONTINUE;
}
else {
- return rb_any_cmp(entry_a->obj, entry_b->obj);
- }
-}
+ ruby_sized_xfree((VALUE *)key, sizeof(VALUE));
-static st_index_t
-weakkeymap_hash_entry(st_data_t a)
-{
- struct weakkeymap_entry *entry_a = (struct weakkeymap_entry *)a;
- return entry_a->hash;
+ return ST_DELETE;
+ }
}
-static const struct st_hash_type weakkeymap_hash = {
- weakkeymap_cmp_entry,
- weakkeymap_hash_entry,
-};
-
static void
-wkmap_compact(void *ptr)
+wkmap_mark(void *ptr)
{
struct weakkeymap *w = ptr;
- if (w->map) rb_gc_update_tbl_refs(w->map);
- w->final = rb_gc_location(w->final);
+ if (w->table) {
+ st_foreach(w->table, wkmap_mark_table_i, (st_data_t)0);
+ }
}
-static void
-wkmap_mark(void *ptr)
+static int
+wkmap_free_table_i(st_data_t key, st_data_t _val, st_data_t _arg)
{
- struct weakkeymap *w = ptr;
- rb_mark_tbl_no_pin(w->map);
- rb_gc_mark_movable(w->final);
+ ruby_sized_xfree((VALUE *)key, sizeof(VALUE));
+ return ST_CONTINUE;
}
static void
wkmap_free(void *ptr)
{
struct weakkeymap *w = ptr;
- st_free_table(w->map);
- st_free_table(w->obj2hash);
- xfree(w);
+
+ st_foreach(w->table, wkmap_free_table_i, 0);
+ st_free_table(w->table);
}
static size_t
wkmap_memsize(const void *ptr)
{
const struct weakkeymap *w = ptr;
- return sizeof(struct weakkeymap) + st_memsize(w->map) + st_memsize(w->obj2hash);
+
+ size_t size = 0;
+ size += st_memsize(w->table);
+ /* Each key of the table takes sizeof(VALUE) in size. */
+ size += st_table_size(w->table) * sizeof(VALUE);
+
+ return size;
+}
+
+static int
+wkmap_compact_table_i(st_data_t key, st_data_t val_obj, st_data_t _data, int _error)
+{
+ VALUE key_obj = *(VALUE *)key;
+
+ if (wmap_live_p(key_obj)) {
+ if (key_obj != rb_gc_location(key_obj) || val_obj != rb_gc_location(val_obj)) {
+ return ST_REPLACE;
+ }
+ }
+ else {
+ ruby_sized_xfree((VALUE *)key, sizeof(VALUE));
+
+ return ST_DELETE;
+ }
+
+ return ST_CONTINUE;
+}
+
+static int
+wkmap_compact_table_replace(st_data_t *key_ptr, st_data_t *val_ptr, st_data_t _data, int existing)
+{
+ RUBY_ASSERT(existing);
+
+ *(VALUE *)*key_ptr = rb_gc_location(*(VALUE *)*key_ptr);
+ *val_ptr = (st_data_t)rb_gc_location((VALUE)*val_ptr);
+
+ return ST_CONTINUE;
+}
+
+static void
+wkmap_compact(void *ptr)
+{
+ struct weakkeymap *w = ptr;
+
+ if (w->table) {
+ st_foreach_with_replace(w->table, wkmap_compact_table_i, wkmap_compact_table_replace, (st_data_t)0);
+ }
}
static const rb_data_type_t weakkeymap_type = {
@@ -639,84 +706,57 @@ static const rb_data_type_t weakkeymap_type = {
wkmap_memsize,
wkmap_compact,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
};
-static VALUE
-wkmap_finalize(RB_BLOCK_CALL_FUNC_ARGLIST(objid, self))
+static int
+wkmap_cmp(st_data_t x, st_data_t y)
{
- struct weakkeymap *w;
- VALUE key;
-
- TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
+ VALUE x_obj = *(VALUE *)x;
+ VALUE y_obj = *(VALUE *)y;
- /* Get reference from object id. */
- if ((key = rb_gc_id2ref_obj_tbl(objid)) == Qundef) {
- rb_bug("wkmap_finalize: objid is not found.");
+ if (wmap_live_p(x_obj) && wmap_live_p(y_obj)) {
+ return rb_any_cmp(x_obj, y_obj);
}
-
- st_index_t hash;
- if (st_delete(w->obj2hash, (st_data_t *)key, &hash)) {
- weakkeymap_entry_t lookup_entry = {key, hash};
- weakkeymap_entry_t *deleted_entry = NULL;
- if (st_get_key(w->map, (st_data_t)&lookup_entry, (st_data_t *)deleted_entry)) {
- st_data_t deleted_value;
- st_delete(w->map, (st_data_t *)deleted_entry, &deleted_value);
- xfree(deleted_entry);
- }
+ else {
+ /* If one of the objects is dead, then they cannot be the same. */
+ return 1;
}
+}
- return self;
+static st_index_t
+wkmap_hash(st_data_t n)
+{
+ VALUE obj = *(VALUE *)n;
+ RUBY_ASSERT(wmap_live_p(obj));
+
+ return rb_any_hash(obj);
}
+static const struct st_hash_type wkmap_hash_type = {
+ wkmap_cmp,
+ wkmap_hash,
+};
+
static VALUE
wkmap_allocate(VALUE klass)
{
struct weakkeymap *w;
VALUE obj = TypedData_Make_Struct(klass, struct weakkeymap, &weakkeymap_type, w);
- w->map = st_init_table(&weakkeymap_hash);
- w->obj2hash = rb_init_identtable();
- RB_OBJ_WRITE(obj, &w->final, rb_func_lambda_new(wkmap_finalize, obj, 1, 1));
+ w->table = st_init_table(&wkmap_hash_type);
return obj;
}
-static st_index_t
-wkmap_lookup_hash(struct weakkeymap *w, VALUE key)
-{
- st_index_t hash;
- if (!st_lookup(w->obj2hash, (st_data_t)key, &hash)) {
- hash = rb_any_hash(key);
- }
- return hash;
-}
-
-static weakkeymap_entry_t*
-wkmap_lookup_entry(struct weakkeymap *w, VALUE key, st_index_t hash)
-{
- st_data_t data;
- weakkeymap_entry_t lookup_entry = {key, hash};
-
- if (st_get_key(w->map, (st_data_t)&lookup_entry, &data)) {
- return (weakkeymap_entry_t *)data;
- }
-
- return NULL;
-}
-
static VALUE
wkmap_lookup(VALUE self, VALUE key)
{
- st_data_t data;
struct weakkeymap *w;
TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
- st_index_t hash = rb_any_hash(key);
- weakkeymap_entry_t lookup_entry = {key, hash};
+ st_data_t data;
+ if (!st_lookup(w->table, (st_data_t)&key, &data)) return Qundef;
- if (st_lookup(w->map, (st_data_t)&lookup_entry, &data)) {
- return (VALUE)data;
- }
- return Qundef;
+ return (VALUE)data;
}
/*
@@ -731,14 +771,34 @@ static VALUE
wkmap_aref(VALUE self, VALUE key)
{
VALUE obj = wkmap_lookup(self, key);
- return obj != Qundef ? obj : Qnil;
+ return !UNDEF_P(obj) ? obj : Qnil;
+}
+
+struct wkmap_aset_args {
+ VALUE new_key;
+ VALUE new_val;
+};
+
+static int
+wkmap_aset_replace(st_data_t *key, st_data_t *val, st_data_t data_args, int existing)
+{
+ struct wkmap_aset_args *args = (struct wkmap_aset_args *)data_args;
+
+ if (!existing) {
+ *key = (st_data_t)xmalloc(sizeof(VALUE));
+ }
+
+ *(VALUE *)*key = args->new_key;
+ *val = (st_data_t)args->new_val;
+
+ return ST_CONTINUE;
}
/*
* call-seq:
* map[key] = value -> value
*
- * Associates the given +value+ with the given +key+; returns +value+.
+ * Associates the given +value+ with the given +key+
*
* The reference to +key+ is weak, so when there is no other reference
* to +key+ it may be garbage collected.
@@ -747,7 +807,7 @@ wkmap_aref(VALUE self, VALUE key)
* the ordering is not affected
*/
static VALUE
-wkmap_aset(VALUE self, VALUE key, VALUE value)
+wkmap_aset(VALUE self, VALUE key, VALUE val)
{
struct weakkeymap *w;
TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
@@ -757,23 +817,17 @@ wkmap_aset(VALUE self, VALUE key, VALUE value)
UNREACHABLE_RETURN(Qnil);
}
- st_index_t hash = wkmap_lookup_hash(w, key);
- weakkeymap_entry_t *key_entry = wkmap_lookup_entry(w, key, hash);
-
- if (!key_entry) {
- key_entry = ALLOC(weakkeymap_entry_t);
- key_entry->obj = key;
- key_entry->hash = hash;
- }
+ struct wkmap_aset_args args = {
+ .new_key = key,
+ .new_val = val,
+ };
- if (!st_insert(w->map, (st_data_t)key_entry, (st_data_t)value)) {
- st_insert(w->obj2hash, (st_data_t)key, (st_data_t)hash);
- rb_define_finalizer_no_check(key, w->final);
- }
+ st_update(w->table, (st_data_t)&key, wkmap_aset_replace, (st_data_t)&args);
- RB_OBJ_WRITTEN(self, Qundef, value);
+ RB_OBJ_WRITTEN(self, Qundef, key);
+ RB_OBJ_WRITTEN(self, Qundef, val);
- return value;
+ return val;
}
/*
@@ -785,7 +839,8 @@ wkmap_aset(VALUE self, VALUE key, VALUE value)
*
* If no block is given and +key+ is found, deletes the entry and returns the associated value:
* m = ObjectSpace::WeakKeyMap.new
- * m["foo"] = 1
+ * key = "foo" # to hold reference to the key
+ * m[key] = 1
* m.delete("foo") # => 1
* m["foo"] # => nil
*
@@ -794,13 +849,14 @@ wkmap_aset(VALUE self, VALUE key, VALUE value)
* If a block is given and +key+ is found, ignores the block,
* deletes the entry, and returns the associated value:
* m = ObjectSpace::WeakKeyMap.new
- * m["foo"] = 2
- * h.delete("foo") { |key| raise 'Will never happen'} # => 2
+ * key = "foo" # to hold reference to the key
+ * m[key] = 2
+ * m.delete("foo") { |key| raise 'Will never happen'} # => 2
*
* If a block is given and +key+ is not found,
- * calls the block and returns the block's return value:
+ * yields the +key+ to the block and returns the block's return value:
* m = ObjectSpace::WeakKeyMap.new
- * h.delete("nosuch") { |key| "Key #{key} not found" } # => "Key nosuch not found"
+ * m.delete("nosuch") { |key| "Key #{key} not found" } # => "Key nosuch not found"
*/
static VALUE
@@ -809,21 +865,20 @@ wkmap_delete(VALUE self, VALUE key)
struct weakkeymap *w;
TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
- st_index_t hash = rb_any_hash(key);
- weakkeymap_entry_t lookup_entry = {key, hash};
- weakkeymap_entry_t *deleted_entry = NULL;
- if (st_get_key(w->map, (st_data_t)&lookup_entry, (st_data_t *)&deleted_entry)) {
- st_data_t deleted_value;
- if (st_delete(w->map, (st_data_t *)&deleted_entry, &deleted_value)) {
- xfree(deleted_entry);
- st_delete(w->obj2hash, (st_data_t *)key, &hash);
- return (VALUE)deleted_value;
- }
- else {
- rb_bug("WeakKeyMap: miss on delete, corrupted memory?");
- }
+ VALUE orig_key = key;
+ st_data_t orig_key_data = (st_data_t)&orig_key;
+ st_data_t orig_val_data;
+ if (st_delete(w->table, &orig_key_data, &orig_val_data)) {
+ VALUE orig_val = (VALUE)orig_val_data;
+
+ rb_gc_remove_weak(self, (VALUE *)orig_key_data);
+
+ ruby_sized_xfree((VALUE *)orig_key_data, sizeof(VALUE));
+
+ return orig_val;
}
- else if (rb_block_given_p()) {
+
+ if (rb_block_given_p()) {
return rb_yield(key);
}
else {
@@ -836,6 +891,19 @@ wkmap_delete(VALUE self, VALUE key)
* map.getkey(key) -> existing_key or nil
*
* Returns the existing equal key if it exists, otherwise returns +nil+.
+ *
+ * This might be useful for implementing caches, so that only one copy of
+ * some object would be used everywhere in the program:
+ *
+ * value = {amount: 1, currency: 'USD'}
+ *
+ * # Now if we put this object in a cache:
+ * cache = ObjectSpace::WeakKeyMap.new
+ * cache[value] = true
+ *
+ * # ...we can always extract from there and use the same object:
+ * copy = cache.getkey({amount: 1, currency: 'USD'})
+ * copy.object_id == value.object_id #=> true
*/
static VALUE
wkmap_getkey(VALUE self, VALUE key)
@@ -843,31 +911,22 @@ wkmap_getkey(VALUE self, VALUE key)
struct weakkeymap *w;
TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
- st_index_t hash = rb_any_hash(key);
- weakkeymap_entry_t lookup_entry = {key, hash};
-
- weakkeymap_entry_t *key_entry = NULL;
- if (st_get_key(w->map, (st_data_t)&lookup_entry, (st_data_t *)&key_entry)) {
- assert(key_entry != NULL);
+ st_data_t orig_key;
+ if (!st_get_key(w->table, (st_data_t)&key, &orig_key)) return Qnil;
- VALUE obj = key_entry->obj;
- if (wmap_live_p(obj)) {
- return obj;
- }
- }
- return Qnil;
+ return *(VALUE *)orig_key;
}
/*
* call-seq:
- * hash.key?(key) -> true or false
+ * map.key?(key) -> true or false
*
* Returns +true+ if +key+ is a key in +self+, otherwise +false+.
*/
static VALUE
wkmap_has_key(VALUE self, VALUE key)
{
- return RBOOL(wkmap_lookup(self, key) != Qundef);
+ return RBOOL(!UNDEF_P(wkmap_lookup(self, key)));
}
/*
@@ -881,12 +940,10 @@ wkmap_clear(VALUE self)
{
struct weakkeymap *w;
TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
- if (w->map) {
- st_clear(w->map);
- }
- if (w->obj2hash) {
- st_clear(w->obj2hash);
- }
+
+ st_foreach(w->table, wkmap_free_table_i, 0);
+ st_clear(w->table);
+
return self;
}
@@ -894,8 +951,8 @@ wkmap_clear(VALUE self)
* call-seq:
* map.inspect -> new_string
*
- * Returns a new \String containing informations about the map:
-
+ * Returns a new String containing informations about the map:
+ *
* m = ObjectSpace::WeakKeyMap.new
* m[key] = value
* m.inspect # => "#<ObjectSpace::WeakKeyMap:0x00000001028dcba8 size=1>"
@@ -907,10 +964,7 @@ wkmap_inspect(VALUE self)
struct weakkeymap *w;
TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
- st_index_t n = 0;
- if (w->map) {
- n = w->map->num_entries;
- }
+ st_index_t n = st_table_size(w->table);
#if SIZEOF_ST_INDEX_T <= SIZEOF_LONG
const char * format = "#<%"PRIsVALUE":%p size=%lu>";
@@ -925,20 +979,101 @@ wkmap_inspect(VALUE self)
/*
* Document-class: ObjectSpace::WeakMap
*
- * An ObjectSpace::WeakMap object holds references to
- * any objects, but those objects can get garbage collected.
+ * An ObjectSpace::WeakMap is a key-value map that holds weak references
+ * to its keys and values, so they can be garbage-collected when there are
+ * no more references left.
+ *
+ * Keys in the map are compared by identity.
+ *
+ * m = ObjectSpace::WeekMap.new
+ * key1 = "foo"
+ * val1 = Object.new
+ * m[key1] = val1
+ *
+ * key2 = "foo"
+ * val2 = Object.new
+ * m[key2] = val2
+ *
+ * m[key1] #=> #<Object:0x0...>
+ * m[key2] #=> #<Object:0x0...>
+ *
+ * val1 = nil # remove the other reference to value
+ * GC.start
+ *
+ * m[key1] #=> nil
+ * m.keys #=> ["bar"]
+ *
+ * key2 = nil # remove the other reference to key
+ * GC.start
+ *
+ * m[key2] #=> nil
+ * m.keys #=> []
*
- * This class is mostly used internally by WeakRef, please use
- * +lib/weakref.rb+ for the public interface.
+ * (Note that GC.start is used here only for demonstrational purposes and might
+ * not always lead to demonstrated results.)
+ *
+ *
+ * See also ObjectSpace::WeakKeyMap map class, which compares keys by value,
+ * and holds weak references only to the keys.
*/
/*
* Document-class: ObjectSpace::WeakKeyMap
*
- * An ObjectSpace::WeakKeyMap object holds references to
- * any objects, but objects uses as keys can be garbage collected.
+ * An ObjectSpace::WeakKeyMap is a key-value map that holds weak references
+ * to its keys, so they can be garbage collected when there is no more references.
+ *
+ * Unlike ObjectSpace::WeakMap:
+ *
+ * * references to values are _strong_, so they aren't garbage collected while
+ * they are in the map;
+ * * keys are compared by value (using Object#eql?), not by identity;
+ * * only garbage-collectable objects can be used as keys.
+ *
+ * map = ObjectSpace::WeakKeyMap.new
+ * val = Time.new(2023, 12, 7)
+ * key = "name"
+ * map[key] = val
+ *
+ * # Value is fetched by equality: the instance of string "name" is
+ * # different here, but it is equal to the key
+ * map["name"] #=> 2023-12-07 00:00:00 +0200
+ *
+ * val = nil
+ * GC.start
+ * # There is no more references to `val`, yet the pair isn't
+ * # garbage-collected.
+ * map["name"] #=> 2023-12-07 00:00:00 +0200
+ *
+ * key = nil
+ * GC.start
+ * # There is no more references to `key`, key and value are
+ * # garbage-collected.
+ * map["name"] #=> nil
+ *
+ * (Note that GC.start is used here only for demonstrational purposes and might
+ * not always lead to demonstrated results.)
+ *
+ * The collection is especially useful for implementing caches of lightweight value
+ * objects, so that only one copy of each value representation would be stored in
+ * memory, but the copies that aren't used would be garbage-collected.
+ *
+ * CACHE = ObjectSpace::WeakKeyMap
+ *
+ * def make_value(**)
+ * val = ValueObject.new(**)
+ * if (existing = @cache.getkey(val))
+ * # if the object with this value exists, we return it
+ * existing
+ * else
+ * # otherwise, put it in the cache
+ * @cache[val] = true
+ * val
+ * end
+ * end
*
- * Objects used as values can't be garbage collected until the key is.
+ * This will result in +make_value+ returning the same object for same set of attributes
+ * always, but the values that aren't needed anymore woudn't be sitting in the cache forever.
*/
void
diff --git a/win32/Makefile.sub b/win32/Makefile.sub
index 7b56e43511..7dbb020eee 100644
--- a/win32/Makefile.sub
+++ b/win32/Makefile.sub
@@ -11,13 +11,15 @@ PATH_SEPARATOR = ;
TZ = # skip timezone tests
PWD = $(MAKEDIR)
empty =
+tooldir = $(srcdir)/tool
!ifndef MFLAGS
MFLAGS=-l
!endif
!if "$(BASERUBY)" == ""
-! if [for %I in (ruby.exe) do @echo BASERUBY = %~s$$PATH:I > baseruby.mk]
+! if [ruby $(tooldir)/missing-baseruby.bat 2> nul]
+! else if [for %I in (ruby.exe) do @echo BASERUBY = %~s$$PATH:I > baseruby.mk]
! else
! include baseruby.mk
! endif
@@ -27,7 +29,7 @@ MFLAGS=-l
BASERUBY =
!endif
!if "$(BASERUBY)" == ""
-BASERUBY = echo executable host ruby is required. use --with-baseruby option.^& exit 1
+BASERUBY = $(tooldir:/=\)\missing-baseruby.bat
HAVE_BASERUBY = no
!else
HAVE_BASERUBY = yes
@@ -330,8 +332,7 @@ COROUTINE_SRC = $(COROUTINE_OBJ:.obj=.asm)
COROUTINE_OBJ = coroutine/win32/Context.obj
COROUTINE_SRC = $(COROUTINE_OBJ:.obj=.asm)
!else
-COROUTINE_OBJ = coroutine/copy/Context.obj
-COROUTINE_SRC = $(COROUTINE_OBJ:.obj=.c)
+!error copy coroutine has been replaced with pthread implementation at 42130a64f02294dc8025af3a51bda518c67ab33d
!endif
COROUTINE_H = $(COROUTINE_OBJ:.obj=.h)
@@ -339,7 +340,9 @@ ARFLAGS = -machine:$(MACHINE) -out:
LD = $(CC)
LDSHARED = $(LD) -LD
XCFLAGS = -DRUBY_EXPORT $(INCFLAGS) $(XCFLAGS)
-!if $(MSC_VER) >= 1400
+!if $(MSC_VER) >= 1800
+LDFLAGS = $(LDFLAGS) -manifest:embed,ID=2
+!elseif $(MSC_VER) >= 1400
# Prevents VC++ 2005 (cl ver 14) warnings
MANIFESTTOOL = mt -nologo
LDSHARED_0 = @if exist $(@).manifest $(MINIRUBY) -run -e wait_writable -- -n 10 $@
@@ -433,8 +436,10 @@ THREAD_IMPL_SRC = thread_$(THREAD_MODEL).c
!if "$(CROSS_COMPILING)" == "yes"
PREP = $(arch)-fake.rb
+BUILTIN_BINARY = no
!else
PREP = miniruby$(EXEEXT)
+BUILTIN_BINARY = yes
!endif
!if !defined(EXTSTATIC)
@@ -482,7 +487,6 @@ EXTOBJS = dmyext.$(OBJEXT)
arch_hdrdir = $(EXTOUT)/include/$(arch)
top_srcdir = $(srcdir)
hdrdir = $(srcdir)/include
-tooldir = $(srcdir)/tool
VPATH = $(arch_hdrdir)/ruby;$(hdrdir)/ruby;$(srcdir);$(srcdir)/missing;$(win_srcdir)
!ifndef GIT
@@ -545,8 +549,9 @@ GOLF_PRELUDE_C = golf_prelude.c
RBCONFIG = ./.rbconfig.time
!if "$(GITHUB_ACTIONS)" == "true"
-ACTIONS_GROUP = @echo ^#^#[group]$(@:yes-=)
-ACTIONS_ENDGROUP = @echo ^#^#[endgroup]
+# 93(bright yellow) is copied from .github/workflows/mingw.yml
+ACTIONS_GROUP = @echo ::group::$(@:yes-=)
+ACTIONS_ENDGROUP = @echo ::endgroup::
!else
ACTIONS_GROUP = @:: $(empty)
ACTIONS_ENDGROUP = @::
@@ -678,7 +683,7 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub
#define _INTEGRAL_MAX_BITS 64
#endif
#define SIZEOF_OFF_T 8
-!if "$(ARCH)" == "x64"
+!if "$(TARGET_OS)" == "mswin64"
#define SIZEOF_VOIDP 8
!else
#define SIZEOF_VOIDP 4
@@ -700,7 +705,7 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub
#define NUM2CLOCKID(v) NUM2INT(v)
#define SIZEOF_CLOCK_T 4
#define SIZEOF_RLIM_T 0
-!if "$(ARCH)" == "x64"
+!if "$(TARGET_OS)" == "mswin64"
#define SIZEOF_SIZE_T 8
#define SIZEOF_PTRDIFF_T 8
#define SIZEOF_INTPTR_T 8
@@ -757,6 +762,7 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub
#define HAVE_STRUCT_TIMESPEC
!endif
!if $(MSC_VER) >= 1600
+#define HAVE_INTTYPES_H 1
#define HAVE_STDINT_H 1
!else
#define int8_t signed char
@@ -799,7 +805,7 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub
#define HAVE_INTPTR_T 1
#define HAVE_UINTPTR_T 1
#define HAVE_SSIZE_T 1
-!if "$(ARCH)" == "x64"
+!if "$(TARGET_OS)" == "mswin64"
#define ssize_t __int64
#define PRI_PTR_PREFIX "I64"
!else
@@ -1071,7 +1077,7 @@ s,@LIBPATHFLAG@, -libpath:%s,;t t
s,@RPATHFLAG@,,;t t
s,@LIBARG@,%s.lib,;t t
s,@LINK_SO@,$$(LDSHARED) -Fe$$(@) $$(OBJS) $$(LIBS) $$(LOCAL_LIBS) -link $$(DLDFLAGS) -implib:$$(*F:.so=)-$$(arch).lib -pdb:$$(*F:.so=)-$$(arch).pdb -def:$$(DEFFILE),;t t
-!if $(MSC_VER) >= 1400
+!if $(MSC_VER) >= 1400 && $(MSC_VER) < 1800
s,@LINK_SO@,@if exist $$(@).manifest $$(RUBY) -run -e wait_writable -- -n 10 $$(@),;t t
s,@LINK_SO@,@if exist $$(@).manifest $(MANIFESTTOOL) -manifest $$(@).manifest -outputresource:$$(@);2,;t t
s,@LINK_SO@,@if exist $$(@).manifest $$(RM) $$(@:/=\).manifest,;t t
@@ -1151,9 +1157,11 @@ $(PROGRAM): $(MAINOBJ) $(LIBRUBY_SO) $(RUBY_INSTALL_NAME).res
$(ECHO) linking $(@:\=/)
$(Q) $(PURIFY) $(CC) $(MAINOBJ) $(EXTOBJS) $(RUBY_INSTALL_NAME).res \
$(OUTFLAG)$@ $(LIBRUBYARG) -link $(LDFLAGS) $(XLDFLAGS)
+! if defined(LDSHARED_0)
$(Q) $(LDSHARED_0)
$(Q) $(LDSHARED_1)
$(Q) $(LDSHARED_2)
+! endif
!endif
!if "$(WPROGRAM)" != ""
@@ -1162,9 +1170,11 @@ $(WPROGRAM): $(MAINOBJ) $(WINMAINOBJ) $(LIBRUBY_SO) $(RUBYW_INSTALL_NAME).res
$(Q) $(PURIFY) $(CC) $(MAINOBJ) $(WINMAINOBJ) \
$(RUBYW_INSTALL_NAME).res $(OUTFLAG)$@ $(LIBRUBYARG) \
-link $(LDFLAGS) $(XLDFLAGS) -subsystem:Windows
+! if defined(LDSHARED_0)
$(Q) $(LDSHARED_0)
$(Q) $(LDSHARED_1)
$(Q) $(LDSHARED_2)
+! endif
!endif
!if "$(STUBPROGRAM)" != ""
@@ -1172,9 +1182,11 @@ $(STUBPROGRAM): rubystub.$(OBJEXT) $(LIBRUBY) $(LIBRUBY_SO) $(RUBY_INSTALL_NAME)
$(ECHO) linking $(@:\=/)
$(Q) $(PURIFY) $(CC) rubystub.$(OBJEXT) $(RUBY_INSTALL_NAME).res \
$(OUTFLAG)$@ $(LIBRUBYARG) -link $(LDFLAGS) $(XLDFLAGS)
+! if defined(LDSHARED_0)
$(Q) $(LDSHARED_0)
$(Q) $(LDSHARED_1)
$(Q) $(LDSHARED_2)
+! endif
!endif
!if "$(LIBRUBY_SO_UPDATE)" == ""
@@ -1204,10 +1216,12 @@ $(LIBRUBY_SO): $(LIBRUBY_A) $(DLDOBJS) $(RUBYDEF) $(RUBY_SO_NAME).res
$(RUBY_SO_NAME).res $(SOLIBS) $(EXTSOLIBS) $(LIBS) -Fe$@ -link $(LDFLAGS) \
$(LIBRUBY_DLDFLAGS)
@$(RM) dummy.lib dummy.exp
+!if defined(LDSHARED_0)
$(Q) $(LDSHARED_0)
$(Q) $(LDSHARED_1)
$(Q) $(LDSHARED_2)
# | findstr -v -c:LNK4049 -c:LNK4217
+!endif
$(RUBYDEF): $(LIBRUBY_A) $(RBCONFIG)
$(ECHO) generating $(@:\=/)
@@ -1278,6 +1292,15 @@ $(ruby_pc): $(RBCONFIG)
$(ECHO) assembling $(<:\=/)
$(Q) $(AS) $(ASFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c $(<:\=/)
+{$(srcdir)/prism}.c.obj:
+ $(ECHO) compiling $(<:\=/)
+ $(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$(<:\=/)
+{$(srcdir)/prism/enc}.c.obj:
+ $(ECHO) compiling $(<:\=/)
+ $(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$(<:\=/)
+{$(srcdir)/prism/util}.c.obj:
+ $(ECHO) compiling $(<:\=/)
+ $(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$(<:\=/)
{$(srcdir)/enc/trans}.c.obj:
$(ECHO) compiling $(<:\=/)
$(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$(<:\=/)
@@ -1354,7 +1377,7 @@ probes.h: {$(VPATH)}probes.dmyh
#include "$(*F).dmyh"
<<KEEP
-INSNS = opt_sc.inc optinsn.inc optunifs.inc insns.inc insns_info.inc \
+INSNS = optinsn.inc optunifs.inc insns.inc insns_info.inc \
vmtc.inc vm.inc
!if [exit > insns_rules.mk]
@@ -1404,8 +1427,10 @@ rubyspec-capiext: $(RUBYSPEC_CAPIEXT_EXTS)
$(Q)echo> $*.def EXPORTS
$(Q)echo>> $*.def Init_$(*F)
$(Q)$(LDSHARED) -Fe$(@) $(INCFLAGS) $(CFLAGS) $(CPPFLAGS) $< $(LIBRUBYARG) -link $(DLDFLAGS) $(LIBS) $(LOCAL_LIBS) -implib:$*.lib -pdb:$*.pdb -def:$*.def
+!if defined(LDSHARED_0)
$(Q)$(LDSHARED_0)
$(Q)$(LDSHARED_1)
$(Q)$(LDSHARED_2)
+!endif
exts: rubyspec-capiext
diff --git a/win32/file.c b/win32/file.c
index 31cc1aff6e..5f5590d3c8 100644
--- a/win32/file.c
+++ b/win32/file.c
@@ -263,7 +263,7 @@ rb_default_home_dir(VALUE result)
{
WCHAR *dir = rb_w32_home_dir();
if (!dir) {
- rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'");
+ rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding '~'");
}
append_wstr(result, dir, -1,
CP_UTF8, rb_utf8_encoding());
@@ -326,7 +326,7 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na
whome = rb_w32_home_dir();
if (whome == NULL) {
free(wpath);
- rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'");
+ rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding '~'");
}
whome_len = wcslen(whome);
@@ -372,8 +372,7 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na
result = append_wstr(result, wpath_pos + 1, user_length_in_path(wpath_pos + 1, wpath_len - 1),
path_cp, path_encoding);
- if (wpath)
- free(wpath);
+ free(wpath);
rb_exc_raise(rb_exc_new_str(rb_eArgError, result));
}
@@ -390,7 +389,7 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na
const long dir_len = RSTRING_LEN(dir);
#if SIZEOF_INT < SIZEOF_LONG
if ((long)(int)dir_len != dir_len) {
- if (wpath) free(wpath);
+ free(wpath);
rb_raise(rb_eRangeError, "base directory (%ld bytes) is too long",
dir_len);
}
@@ -405,7 +404,7 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na
if (whome == NULL) {
free(wpath);
free(wdir);
- rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'");
+ rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding '~'");
}
whome_len = wcslen(whome);
@@ -452,11 +451,8 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na
result = rb_str_new_cstr("can't find user ");
result = append_wstr(result, wdir_pos + 1, user_length_in_path(wdir_pos + 1, wdir_len - 1),
path_cp, path_encoding);
- if (wpath)
- free(wpath);
-
- if (wdir)
- free(wdir);
+ free(wpath);
+ free(wdir);
rb_exc_raise(rb_exc_new_str(rb_eArgError, result));
}
@@ -573,17 +569,10 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na
result = append_wstr(result, wfullpath, size, path_cp, path_encoding);
/* TODO: better cleanup */
- if (buffer)
- xfree(buffer);
-
- if (wpath)
- free(wpath);
-
- if (wdir)
- free(wdir);
-
- if (whome)
- xfree(whome);
+ xfree(buffer);
+ free(wpath);
+ free(wdir);
+ xfree(whome);
if (wfullpath != wfullpath_buffer)
xfree(wfullpath);
diff --git a/win32/file.h b/win32/file.h
index 4f1f36a75c..7bf9868e7d 100644
--- a/win32/file.h
+++ b/win32/file.h
@@ -47,4 +47,7 @@ int fchmod(int fd, int mode);
UINT rb_w32_filecp(void);
WCHAR *rb_w32_home_dir(void);
+rb_pid_t rb_w32_uspawn_process(int mode, const char *prog, char *const *argv,
+ int in_fd, int out_fd, int err_fd, DWORD flags);
+
#endif /* RUBY_WIN32_FILE_H */
diff --git a/win32/mkexports.rb b/win32/mkexports.rb
index 2889908942..dd0fbf6313 100755
--- a/win32/mkexports.rb
+++ b/win32/mkexports.rb
@@ -151,7 +151,7 @@ class Exports::Cygwin < Exports
end
def each_line(objs, &block)
- IO.foreach("|#{self.class.nm} --extern --defined #{objs.join(' ')}", &block)
+ IO.foreach("|#{self.class.nm} --extern-only --defined-only #{objs.join(' ')}", &block)
end
def each_export(objs)
diff --git a/win32/setup.mak b/win32/setup.mak
index c0074bf963..8c27994821 100644
--- a/win32/setup.mak
+++ b/win32/setup.mak
@@ -66,6 +66,7 @@ RJIT_SUPPORT = $(RJIT_SUPPORT)
# TOOLS
<<
!if defined(BASERUBY)
+ $(BASERUBY:/=\) "$(srcdir)/tool/missing-baseruby.bat"
@echo BASERUBY = $(BASERUBY:/=\)>> $(MAKEFILE)
!endif
!if "$(RUBY_DEVEL)" == "yes"
@@ -129,6 +130,7 @@ vs2022-fp-bug:
/* compile with -O2 */
#include <math.h>
#include <float.h>
+#include <stdio.h>
#define value_finite(d) 'f'
#define value_infinity() 'i'
diff --git a/win32/win32.c b/win32/win32.c
index c2d12a79f4..c51d53595f 100644
--- a/win32/win32.c
+++ b/win32/win32.c
@@ -622,21 +622,24 @@ init_env(void)
if (!GetEnvironmentVariableW(L"HOME", env, numberof(env))) {
f = FALSE;
- if (GetEnvironmentVariableW(L"HOMEDRIVE", env, numberof(env)))
- len = lstrlenW(env);
- else
- len = 0;
- if (GetEnvironmentVariableW(L"HOMEPATH", env + len, numberof(env) - len) || len) {
- f = TRUE;
- }
- else if (GetEnvironmentVariableW(L"USERPROFILE", env, numberof(env))) {
- f = TRUE;
- }
- else if (get_special_folder(CSIDL_PROFILE, env, numberof(env))) {
+ if (GetEnvironmentVariableW(L"USERPROFILE", env, numberof(env))) {
f = TRUE;
}
- else if (get_special_folder(CSIDL_PERSONAL, env, numberof(env))) {
- f = TRUE;
+ else {
+ if (GetEnvironmentVariableW(L"HOMEDRIVE", env, numberof(env)))
+ len = lstrlenW(env);
+ else
+ len = 0;
+
+ if (GetEnvironmentVariableW(L"HOMEPATH", env + len, numberof(env) - len) || len) {
+ f = TRUE;
+ }
+ else if (get_special_folder(CSIDL_PROFILE, env, numberof(env))) {
+ f = TRUE;
+ }
+ else if (get_special_folder(CSIDL_PERSONAL, env, numberof(env))) {
+ f = TRUE;
+ }
}
if (f) {
regulate_path(env);
@@ -1537,7 +1540,8 @@ rb_w32_uspawn(int mode, const char *cmd, const char *prog)
/* License: Artistic or GPL */
static rb_pid_t
-w32_aspawn_flags(int mode, const char *prog, char *const *argv, DWORD flags, UINT cp)
+w32_spawn_process(int mode, const char *prog, char *const *argv,
+ int in_fd, int out_fd, int err_fd, DWORD flags, UINT cp)
{
int c_switch = 0;
size_t len;
@@ -1548,9 +1552,20 @@ w32_aspawn_flags(int mode, const char *prog, char *const *argv, DWORD flags, UIN
int e = 0;
rb_pid_t ret = -1;
VALUE v = 0;
+ HANDLE in_handle = NULL, out_handle = NULL, err_handle = NULL;
if (check_spawn_mode(mode)) return -1;
+ if (in_fd >= 0) {
+ in_handle = (HANDLE)rb_w32_get_osfhandle(in_fd);
+ }
+ if (out_fd >= 0) {
+ out_handle = (HANDLE)rb_w32_get_osfhandle(out_fd);
+ }
+ if (err_fd >= 0) {
+ err_handle = (HANDLE)rb_w32_get_osfhandle(err_fd);
+ }
+
if (!prog) prog = argv[0];
if ((shell = w32_getenv("COMSPEC", cp)) &&
internal_cmd_match(prog, tmpnt = !is_command_com(shell))) {
@@ -1598,7 +1613,7 @@ w32_aspawn_flags(int mode, const char *prog, char *const *argv, DWORD flags, UIN
if (!e) {
struct ChildRecord *child = FindFreeChildSlot();
- if (CreateChild(child, wcmd, wprog, NULL, NULL, NULL, flags)) {
+ if (CreateChild(child, wcmd, wprog, in_handle, out_handle, err_handle, flags)) {
ret = child_result(child, mode);
}
}
@@ -1613,21 +1628,21 @@ rb_pid_t
rb_w32_aspawn_flags(int mode, const char *prog, char *const *argv, DWORD flags)
{
/* assume ACP */
- return w32_aspawn_flags(mode, prog, argv, flags, filecp());
+ return w32_spawn_process(mode, prog, argv, -1, -1, -1, flags, filecp());
}
/* License: Ruby's */
rb_pid_t
rb_w32_uaspawn_flags(int mode, const char *prog, char *const *argv, DWORD flags)
{
- return w32_aspawn_flags(mode, prog, argv, flags, CP_UTF8);
+ return w32_spawn_process(mode, prog, argv, -1, -1, -1, flags, CP_UTF8);
}
/* License: Ruby's */
rb_pid_t
rb_w32_aspawn(int mode, const char *prog, char *const *argv)
{
- return w32_aspawn_flags(mode, prog, argv, 0, filecp());
+ return w32_spawn_process(mode, prog, argv, -1, -1, -1, 0, filecp());
}
/* License: Ruby's */
@@ -1637,6 +1652,15 @@ rb_w32_uaspawn(int mode, const char *prog, char *const *argv)
return rb_w32_uaspawn_flags(mode, prog, argv, 0);
}
+/* License: Ruby's */
+rb_pid_t
+rb_w32_uspawn_process(int mode, const char *prog, char *const *argv,
+ int in_fd, int out_fd, int err_fd, DWORD flags)
+{
+ return w32_spawn_process(mode, prog, argv, in_fd, out_fd, err_fd,
+ flags, CP_UTF8);
+}
+
/* License: Artistic or GPL */
typedef struct _NtCmdLineElement {
struct _NtCmdLineElement *next;
@@ -2369,10 +2393,8 @@ readdir_internal(DIR *dirp, BOOL (*conv)(const WCHAR *, const WCHAR *, struct di
//
// first set up the structure to return
//
- if (dirp->dirstr.d_name)
- free(dirp->dirstr.d_name);
- if (dirp->dirstr.d_altname)
- free(dirp->dirstr.d_altname);
+ free(dirp->dirstr.d_name);
+ free(dirp->dirstr.d_altname);
dirp->dirstr.d_altname = 0;
dirp->dirstr.d_altlen = 0;
conv(dirp->curr, dirp->curr + lstrlenW(dirp->curr) + 1, &dirp->dirstr, enc);
@@ -2478,14 +2500,10 @@ void
rb_w32_closedir(DIR *dirp)
{
if (dirp) {
- if (dirp->dirstr.d_name)
- free(dirp->dirstr.d_name);
- if (dirp->dirstr.d_altname)
- free(dirp->dirstr.d_altname);
- if (dirp->start)
- free(dirp->start);
- if (dirp->bits)
- free(dirp->bits);
+ free(dirp->dirstr.d_name);
+ free(dirp->dirstr.d_altname);
+ free(dirp->start);
+ free(dirp->bits);
free(dirp);
}
}
@@ -2597,9 +2615,73 @@ set_pioinfo_extra(void)
* * https://bugs.ruby-lang.org/issues/18605
*/
char *p = (char*)get_proc_address(UCRTBASE, "_isatty", NULL);
- char *pend = p;
/* _osfile(fh) & FDEV */
+#ifdef _M_ARM64
+#define IS_INSN(pc, name) ((*(pc) & name##_mask) == name##_id)
+ const int max_num_inst = 500;
+ uint32_t *start = (uint32_t*)p;
+ uint32_t *end_limit = (start + max_num_inst);
+ uint32_t *pc = start;
+
+ if (!p) {
+ fprintf(stderr, "_isatty proc not found in " UCRTBASE "\n");
+ _exit(1);
+ }
+
+ /* end of function */
+ const uint32_t ret_id = 0xd65f0000;
+ const uint32_t ret_mask = 0xfffffc1f;
+ for(; pc < end_limit; pc++) {
+ if (IS_INSN(pc, ret)) {
+ break;
+ }
+ }
+ if (pc == end_limit) {
+ fprintf(stderr, "end of _isatty not found in " UCRTBASE "\n");
+ _exit(1);
+ }
+
+ /* pioinfo instruction mark */
+ const uint32_t adrp_id = 0x90000000;
+ const uint32_t adrp_mask = 0x9f000000;
+ const uint32_t add_id = 0x11000000;
+ const uint32_t add_mask = 0x3fc00000;
+ for(; pc > start; pc--) {
+ if (IS_INSN(pc, adrp) && IS_INSN(pc + 1, add)) {
+ break;
+ }
+ }
+ if(pc == start) {
+ fprintf(stderr, "pioinfo mark not found in " UCRTBASE "\n");
+ _exit(1);
+ }
+
+ /* We now point to instructions that load address of __pioinfo:
+ * adrp x8, 0x1801d8000
+ * add x8, x8, #0xdb0
+ * https://devblogs.microsoft.com/oldnewthing/20220809-00/?p=106955
+ * The last adrp/add sequence before ret is what we are looking for.
+ */
+ const uint32_t adrp_insn = *pc;
+ const uint32_t adrp_immhi = (adrp_insn & 0x00ffffe0) >> 5;
+ const uint32_t adrp_immlo = (adrp_insn & 0x60000000) >> (5 + 19 + 5);
+ /* imm = immhi:immlo:Zeros(12), 64 */
+ const uint64_t adrp_imm = ((adrp_immhi << 2) | adrp_immlo) << 12;
+ /* base = PC64<63:12>:Zeros(12) */
+ const uint64_t adrp_base = (uint64_t)pc & 0xfffffffffffff000;
+
+ const uint32_t add_insn = *(pc + 1);
+ const uint32_t add_sh = (add_insn & 0x400000) >> (12 + 5 + 5);
+ /* case sh of
+ when '0' imm = ZeroExtend(imm12, datasize);
+ when '1' imm = ZeroExtend(imm12:Zeros(12), datasize); */
+ const uint64_t add_imm = ((add_insn & 0x3ffc00) >> (5 + 5)) << (add_sh ? 12 : 0);
+
+ __pioinfo = (ioinfo**)(adrp_base + adrp_imm + add_imm);
+#else /* _M_ARM64 */
+ char *pend = p;
+
# ifdef _WIN64
int32_t rel;
char *rip;
@@ -2649,7 +2731,8 @@ set_pioinfo_extra(void)
#else
__pioinfo = *(ioinfo***)(p);
#endif
-#endif
+#endif /* _M_ARM64 */
+#endif /* RUBY_MSVCRT_VERSION */
int fd;
fd = _open("NUL", O_RDONLY);
@@ -4378,8 +4461,8 @@ freeifaddrs(struct ifaddrs *ifp)
{
while (ifp) {
struct ifaddrs *next = ifp->ifa_next;
- if (ifp->ifa_addr) ruby_xfree(ifp->ifa_addr);
- if (ifp->ifa_name) ruby_xfree(ifp->ifa_name);
+ ruby_xfree(ifp->ifa_addr);
+ ruby_xfree(ifp->ifa_name);
ruby_xfree(ifp);
ifp = next;
}
@@ -7593,7 +7676,7 @@ rb_w32_write_console(uintptr_t strarg, int fd)
}
}
RB_GC_GUARD(str);
- if (wbuffer) free(wbuffer);
+ free(wbuffer);
return (long)reslen;
}
diff --git a/yjit.c b/yjit.c
index 2f8abd4567..d40ac81fb5 100644
--- a/yjit.c
+++ b/yjit.c
@@ -38,10 +38,16 @@
#include <errno.h>
+// Field offsets for the RObject struct
+enum robject_offsets {
+ ROBJECT_OFFSET_AS_HEAP_IVPTR = offsetof(struct RObject, as.heap.ivptr),
+ ROBJECT_OFFSET_AS_HEAP_IV_INDEX_TBL = offsetof(struct RObject, as.heap.iv_index_tbl),
+ ROBJECT_OFFSET_AS_ARY = offsetof(struct RObject, as.ary),
+};
+
// Field offsets for the RString struct
enum rstring_offsets {
- RUBY_OFFSET_RSTRING_AS_HEAP_LEN = offsetof(struct RString, as.heap.len),
- RUBY_OFFSET_RSTRING_EMBED_LEN = offsetof(struct RString, as.embed.len),
+ RUBY_OFFSET_RSTRING_LEN = offsetof(struct RString, len)
};
// We need size_t to have a known size to simplify code generation and FFI.
@@ -416,10 +422,12 @@ void
rb_iseq_reset_jit_func(const rb_iseq_t *iseq)
{
RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(iseq, imemo_iseq));
- iseq->body->jit_func = NULL;
+ iseq->body->jit_entry = NULL;
+ iseq->body->jit_exception = NULL;
// Enable re-compiling this ISEQ. Event when it's invalidated for TracePoint,
// we'd like to re-compile ISEQs that haven't been converted to trace_* insns.
- iseq->body->total_calls = 0;
+ iseq->body->jit_entry_calls = 0;
+ iseq->body->jit_exception_calls = 0;
}
// Get the PC for a given index in an iseq
@@ -621,6 +629,12 @@ rb_get_iseq_body_stack_max(const rb_iseq_t *iseq)
return iseq->body->stack_max;
}
+enum rb_iseq_type
+rb_get_iseq_body_type(const rb_iseq_t *iseq)
+{
+ return iseq->body->type;
+}
+
bool
rb_get_iseq_flags_has_lead(const rb_iseq_t *iseq)
{
@@ -652,6 +666,12 @@ rb_get_iseq_flags_has_kwrest(const rb_iseq_t *iseq)
}
bool
+rb_get_iseq_flags_anon_kwrest(const rb_iseq_t *iseq)
+{
+ return iseq->body->param.flags.anon_kwrest;
+}
+
+bool
rb_get_iseq_flags_has_rest(const rb_iseq_t *iseq)
{
return iseq->body->param.flags.has_rest;
@@ -725,15 +745,17 @@ rb_yjit_iseq_builtin_attrs(const rb_iseq_t *iseq)
return iseq->body->builtin_attrs;
}
-// If true, the iseq has only opt_invokebuiltin_delegate_leave and leave insns.
+// If true, the iseq has only opt_invokebuiltin_delegate(_leave) and leave insns.
static bool
invokebuiltin_delegate_leave_p(const rb_iseq_t *iseq)
{
- unsigned int invokebuiltin_len = insn_len(BIN(opt_invokebuiltin_delegate_leave));
- unsigned int leave_len = insn_len(BIN(leave));
- return iseq->body->iseq_size == (invokebuiltin_len + leave_len) &&
- rb_vm_insn_addr2opcode((void *)iseq->body->iseq_encoded[0]) == BIN(opt_invokebuiltin_delegate_leave) &&
- rb_vm_insn_addr2opcode((void *)iseq->body->iseq_encoded[invokebuiltin_len]) == BIN(leave);
+ int insn1 = rb_vm_insn_addr2opcode((void *)iseq->body->iseq_encoded[0]);
+ if ((int)iseq->body->iseq_size != insn_len(insn1) + insn_len(BIN(leave))) {
+ return false;
+ }
+ int insn2 = rb_vm_insn_addr2opcode((void *)iseq->body->iseq_encoded[insn_len(insn1)]);
+ return (insn1 == BIN(opt_invokebuiltin_delegate) || insn1 == BIN(opt_invokebuiltin_delegate_leave)) &&
+ insn2 == BIN(leave);
}
// Return an rb_builtin_function if the iseq contains only that builtin function.
@@ -790,13 +812,6 @@ rb_set_cfp_sp(struct rb_control_frame_struct *cfp, VALUE *sp)
cfp->sp = sp;
}
-rb_iseq_t *
-rb_cfp_get_iseq(struct rb_control_frame_struct *cfp)
-{
- // TODO(alan) could assert frame type here to make sure that it's a ruby frame with an iseq.
- return (rb_iseq_t*)cfp->iseq;
-}
-
VALUE
rb_get_cfp_self(struct rb_control_frame_struct *cfp)
{
@@ -820,6 +835,8 @@ rb_get_cfp_ep_level(struct rb_control_frame_struct *cfp, uint32_t lv)
return ep;
}
+extern VALUE *rb_vm_base_ptr(struct rb_control_frame_struct *cfp);
+
VALUE
rb_yarv_class_of(VALUE obj)
{
@@ -868,10 +885,60 @@ rb_yjit_fix_mod_fix(VALUE recv, VALUE obj)
return rb_fix_mod_fix(recv, obj);
}
+// Return non-zero when `obj` is an array and its last item is a
+// `ruby2_keywords` hash. We don't support this kind of splat.
+size_t
+rb_yjit_ruby2_keywords_splat_p(VALUE obj)
+{
+ if (!RB_TYPE_P(obj, T_ARRAY)) return 0;
+ long len = RARRAY_LEN(obj);
+ if (len == 0) return 0;
+ VALUE last = RARRAY_AREF(obj, len - 1);
+ if (!RB_TYPE_P(last, T_HASH)) return 0;
+ return FL_TEST_RAW(last, RHASH_PASS_AS_KEYWORDS);
+}
+
+// Checks to establish preconditions for rb_yjit_splat_varg_cfunc()
VALUE
-rb_yjit_fix_mul_fix(VALUE recv, VALUE obj)
+rb_yjit_splat_varg_checks(VALUE *sp, VALUE splat_array, rb_control_frame_t *cfp)
+{
+ // We inserted a T_ARRAY guard before this call
+ long len = RARRAY_LEN(splat_array);
+
+ // Large splat arrays need a separate allocation
+ if (len < 0 || len > VM_ARGC_STACK_MAX) return Qfalse;
+
+ // Would we overflow if we put the contents of the array onto the stack?
+ if (sp + len > (VALUE *)(cfp - 2)) return Qfalse;
+
+ // Reject keywords hash since that requires duping it sometimes
+ if (len > 0) {
+ VALUE last_hash = RARRAY_AREF(splat_array, len - 1);
+ if (RB_TYPE_P(last_hash, T_HASH) &&
+ FL_TEST_RAW(last_hash, RHASH_PASS_AS_KEYWORDS)) {
+ return Qfalse;
+ }
+ }
+
+ return Qtrue;
+}
+
+// Push array elements to the stack for a C method that has a variable number
+// of parameters. Returns the number of arguments the splat array contributes.
+int
+rb_yjit_splat_varg_cfunc(VALUE *stack_splat_array)
{
- return rb_fix_mul_fix(recv, obj);
+ VALUE splat_array = *stack_splat_array;
+ int len;
+
+ // We already checked that length fits in `int`
+ RUBY_ASSERT(RB_TYPE_P(splat_array, T_ARRAY));
+ len = (int)RARRAY_LEN(splat_array);
+
+ // Push the contents of the array onto the stack
+ MEMCPY(stack_splat_array, RARRAY_CONST_PTR(splat_array), VALUE, len);
+
+ return len;
}
// Print the Ruby source location of some ISEQ for debugging purposes
@@ -885,6 +952,30 @@ rb_yjit_dump_iseq_loc(const rb_iseq_t *iseq, uint32_t insn_idx)
fprintf(stderr, "%s %.*s:%u\n", __func__, (int)len, ptr, rb_iseq_line_no(iseq, insn_idx));
}
+// Get the number of digits required to print an integer
+static int
+num_digits(int integer)
+{
+ int num = 1;
+ while (integer /= 10) {
+ num++;
+ }
+ return num;
+}
+
+// Allocate a C string that formats an ISEQ label like iseq_inspect()
+char *
+rb_yjit_iseq_inspect(const rb_iseq_t *iseq)
+{
+ const char *label = RSTRING_PTR(iseq->body->location.label);
+ const char *path = RSTRING_PTR(rb_iseq_path(iseq));
+ int lineno = iseq->body->location.code_location.beg_pos.lineno;
+
+ char *buf = ZALLOC_N(char, strlen(label) + strlen(path) + num_digits(lineno) + 3);
+ sprintf(buf, "%s@%s:%d", label, path, lineno);
+ return buf;
+}
+
// The FL_TEST() macro
VALUE
rb_FL_TEST(VALUE obj, VALUE flags)
@@ -956,7 +1047,6 @@ rb_yjit_multi_ractor_p(void)
void
rb_assert_iseq_handle(VALUE handle)
{
- RUBY_ASSERT_ALWAYS(rb_objspace_markable_object_p(handle));
RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(handle, imemo_iseq));
}
@@ -1035,27 +1125,24 @@ rb_yjit_vm_unlock(unsigned int *recursive_lock_level, const char *file, int line
rb_vm_lock_leave(recursive_lock_level, file, line);
}
-bool
-rb_yjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec)
+void
+rb_yjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception)
{
- bool success = true;
RB_VM_LOCK_ENTER();
rb_vm_barrier();
- // Compile a block version starting at the first instruction
- uint8_t *rb_yjit_iseq_gen_entry_point(const rb_iseq_t *iseq, rb_execution_context_t *ec); // defined in Rust
- uint8_t *code_ptr = rb_yjit_iseq_gen_entry_point(iseq, ec);
+ // Compile a block version starting at the current instruction
+ uint8_t *rb_yjit_iseq_gen_entry_point(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception); // defined in Rust
+ uint8_t *code_ptr = rb_yjit_iseq_gen_entry_point(iseq, ec, jit_exception);
- if (code_ptr) {
- iseq->body->jit_func = (rb_jit_func_t)code_ptr;
+ if (jit_exception) {
+ iseq->body->jit_exception = (rb_jit_func_t)code_ptr;
}
else {
- iseq->body->jit_func = 0;
- success = false;
+ iseq->body->jit_entry = (rb_jit_func_t)code_ptr;
}
RB_VM_LOCK_LEAVE();
- return success;
}
// GC root for interacting with the GC
@@ -1076,20 +1163,14 @@ yjit_root_memsize(const void *ptr)
return 0; // TODO: more accurate accounting
}
-// GC callback during compaction
-static void
-yjit_root_update_references(void *ptr)
-{
- // Do nothing since we use rb_gc_mark(), which pins.
-}
-
void rb_yjit_root_mark(void *ptr); // in Rust
+void rb_yjit_root_update_references(void *ptr); // in Rust
// Custom type for interacting with the GC
// TODO: make this write barrier protected
static const rb_data_type_t yjit_root_type = {
"yjit_root",
- {rb_yjit_root_mark, yjit_root_free, yjit_root_memsize, yjit_root_update_references},
+ {rb_yjit_root_mark, yjit_root_free, yjit_root_memsize, rb_yjit_root_update_references},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
};
@@ -1117,8 +1198,52 @@ rb_yjit_assert_holding_vm_lock(void)
ASSERT_vm_locking();
}
+// The number of stack slots that vm_sendish() pops for send and invokesuper.
+size_t
+rb_yjit_sendish_sp_pops(const struct rb_callinfo *ci)
+{
+ return 1 - sp_inc_of_sendish(ci); // + 1 to ignore return value push
+}
+
+// The number of stack slots that vm_sendish() pops for invokeblock.
+size_t
+rb_yjit_invokeblock_sp_pops(const struct rb_callinfo *ci)
+{
+ return 1 - sp_inc_of_invokeblock(ci); // + 1 to ignore return value push
+}
+
+// Setup jit_return to avoid returning a non-Qundef value on a non-FINISH frame.
+// See [jit_compile_exception] for details.
+void
+rb_yjit_set_exception_return(rb_control_frame_t *cfp, void *leave_exit, void *leave_exception)
+{
+ if (VM_FRAME_FINISHED_P(cfp)) {
+ // If it's a FINISH frame, just normally exit with a non-Qundef value.
+ cfp->jit_return = leave_exit;
+ }
+ else if (cfp->jit_return) {
+ while (!VM_FRAME_FINISHED_P(cfp)) {
+ if (cfp->jit_return == leave_exit) {
+ // Unlike jit_exec(), leave_exit is not safe on a non-FINISH frame on
+ // jit_exec_exception(). See [jit_exec] and [jit_exec_exception] for
+ // details. Exit to the interpreter with Qundef to let it keep executing
+ // other Ruby frames.
+ cfp->jit_return = leave_exception;
+ return;
+ }
+ cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
+ }
+ }
+ else {
+ // If the caller was not JIT code, exit to the interpreter with Qundef
+ // to keep executing Ruby frames with the interpreter.
+ cfp->jit_return = leave_exception;
+ }
+}
+
// Primitives used by yjit.rb
VALUE rb_yjit_stats_enabled_p(rb_execution_context_t *ec, VALUE self);
+VALUE rb_yjit_print_stats_p(rb_execution_context_t *ec, VALUE self);
VALUE rb_yjit_trace_exit_locations_enabled_p(rb_execution_context_t *ec, VALUE self);
VALUE rb_yjit_get_stats(rb_execution_context_t *ec, VALUE self, VALUE context);
VALUE rb_yjit_reset_stats_bang(rb_execution_context_t *ec, VALUE self);
@@ -1127,21 +1252,16 @@ VALUE rb_yjit_insns_compiled(rb_execution_context_t *ec, VALUE self, VALUE iseq)
VALUE rb_yjit_code_gc(rb_execution_context_t *ec, VALUE self);
VALUE rb_yjit_simulate_oom_bang(rb_execution_context_t *ec, VALUE self);
VALUE rb_yjit_get_exit_locations(rb_execution_context_t *ec, VALUE self);
-VALUE rb_yjit_resume(rb_execution_context_t *ec, VALUE self);
+VALUE rb_yjit_enable(rb_execution_context_t *ec, VALUE self, VALUE gen_stats, VALUE print_stats);
// Preprocessed yjit.rb generated during build
#include "yjit.rbinc"
-// Can raise RuntimeError
+// Initialize the GC hooks
void
-rb_yjit_init(void)
+rb_yjit_init_gc_hooks(void)
{
- // Call the Rust initialization code
- void rb_yjit_init_rust(void);
- rb_yjit_init_rust();
-
- // Initialize the GC hooks. Do this second as some code depend on Rust initialization.
struct yjit_root_struct *root;
VALUE yjit_root = TypedData_Make_Struct(0, struct yjit_root_struct, &yjit_root_type, root);
- rb_gc_register_mark_object(yjit_root);
+ rb_vm_register_global_object(yjit_root);
}
diff --git a/yjit.h b/yjit.h
index a640d5982a..5d1de2df90 100644
--- a/yjit.h
+++ b/yjit.h
@@ -25,47 +25,54 @@
#endif
// Expose these as declarations since we are building YJIT.
-bool rb_yjit_enabled_p(void);
-bool rb_yjit_compile_new_iseqs(void);
-unsigned rb_yjit_call_threshold(void);
+extern uint64_t rb_yjit_call_threshold;
+extern uint64_t rb_yjit_cold_threshold;
+extern uint64_t rb_yjit_live_iseq_count;
+extern uint64_t rb_yjit_iseq_alloc_count;
+extern bool rb_yjit_enabled_p;
+void rb_yjit_incr_counter(const char *counter_name);
void rb_yjit_invalidate_all_method_lookup_assumptions(void);
void rb_yjit_cme_invalidate(rb_callable_method_entry_t *cme);
-void rb_yjit_collect_vm_usage_insn(int insn);
void rb_yjit_collect_binding_alloc(void);
void rb_yjit_collect_binding_set(void);
-bool rb_yjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec);
-void rb_yjit_init(void);
+void rb_yjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception);
+void rb_yjit_init(bool yjit_enabled);
void rb_yjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop);
void rb_yjit_constant_state_changed(ID id);
void rb_yjit_iseq_mark(void *payload);
-void rb_yjit_iseq_update_references(void *payload);
-void rb_yjit_iseq_free(void *payload);
+void rb_yjit_iseq_update_references(const rb_iseq_t *iseq);
+void rb_yjit_iseq_free(const rb_iseq_t *iseq);
void rb_yjit_before_ractor_spawn(void);
void rb_yjit_constant_ic_update(const rb_iseq_t *const iseq, IC ic, unsigned insn_idx);
void rb_yjit_tracing_invalidate_all(void);
+void rb_yjit_show_usage(int help, int highlight, unsigned int width, int columns);
+void rb_yjit_lazy_push_frame(const VALUE *pc);
+void rb_yjit_invalidate_no_singleton_class(VALUE klass);
+void rb_yjit_invalidate_ep_is_bp(const rb_iseq_t *iseq);
#else
// !USE_YJIT
// In these builds, YJIT could never be turned on. Provide dummy implementations.
-static inline bool rb_yjit_enabled_p(void) { return false; }
-static inline bool rb_yjit_compile_new_iseqs(void) { return false; }
-static inline unsigned rb_yjit_call_threshold(void) { return UINT_MAX; }
+#define rb_yjit_enabled_p false
+static inline void rb_yjit_incr_counter(const char *counter_name) {}
static inline void rb_yjit_invalidate_all_method_lookup_assumptions(void) {}
static inline void rb_yjit_cme_invalidate(rb_callable_method_entry_t *cme) {}
-static inline void rb_yjit_collect_vm_usage_insn(int insn) {}
static inline void rb_yjit_collect_binding_alloc(void) {}
static inline void rb_yjit_collect_binding_set(void) {}
-static inline bool rb_yjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec) { return false; }
-static inline void rb_yjit_init(void) {}
+static inline void rb_yjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception) {}
+static inline void rb_yjit_init(bool yjit_enabled) {}
static inline void rb_yjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop) {}
static inline void rb_yjit_constant_state_changed(ID id) {}
static inline void rb_yjit_iseq_mark(void *payload) {}
-static inline void rb_yjit_iseq_update_references(void *payload) {}
-static inline void rb_yjit_iseq_free(void *payload) {}
+static inline void rb_yjit_iseq_update_references(const rb_iseq_t *iseq) {}
+static inline void rb_yjit_iseq_free(const rb_iseq_t *iseq) {}
static inline void rb_yjit_before_ractor_spawn(void) {}
static inline void rb_yjit_constant_ic_update(const rb_iseq_t *const iseq, IC ic, unsigned insn_idx) {}
static inline void rb_yjit_tracing_invalidate_all(void) {}
+static inline void rb_yjit_lazy_push_frame(const VALUE *pc) {}
+static inline void rb_yjit_invalidate_no_singleton_class(VALUE klass) {}
+static inline void rb_yjit_invalidate_ep_is_bp(const rb_iseq_t *iseq) {}
#endif // #if USE_YJIT
diff --git a/yjit.rb b/yjit.rb
index 0ac81f1881..6612d8bd82 100644
--- a/yjit.rb
+++ b/yjit.rb
@@ -1,44 +1,49 @@
# frozen_string_literal: true
+# :markup: markdown
-# This module allows for introspection of YJIT, CRuby's in-process
-# just-in-time compiler. This module exists only to help develop YJIT, as such,
-# everything in the module is highly implementation specific and comes with no
-# API stability guarantee whatsoever.
+# This module allows for introspection of \YJIT, CRuby's just-in-time compiler.
+# Everything in the module is highly implementation specific and the API might
+# be less stable compared to the standard library.
#
-# This module may not exist if YJIT does not support the particular platform
-# for which CRuby is built. There is also no API stability guarantee as to in
-# what situations this module is defined.
+# This module may not exist if \YJIT does not support the particular platform
+# for which CRuby is built.
module RubyVM::YJIT
- # Check if YJIT is enabled
+ # Check if \YJIT is enabled.
def self.enabled?
- Primitive.cexpr! 'RBOOL(rb_yjit_enabled_p())'
+ Primitive.cexpr! 'RBOOL(rb_yjit_enabled_p)'
end
- # Check if --yjit-stats is used.
+ # Check if `--yjit-stats` is used.
def self.stats_enabled?
Primitive.rb_yjit_stats_enabled_p
end
# Check if rb_yjit_trace_exit_locations_enabled_p is enabled.
- def self.trace_exit_locations_enabled?
+ def self.trace_exit_locations_enabled? # :nodoc:
Primitive.rb_yjit_trace_exit_locations_enabled_p
end
- # Discard statistics collected for --yjit-stats.
+ # Discard statistics collected for `--yjit-stats`.
def self.reset_stats!
Primitive.rb_yjit_reset_stats_bang
end
- # Resume YJIT compilation after paused on startup with --yjit-pause
- def self.resume
- Primitive.rb_yjit_resume
+ # Enable \YJIT compilation. `stats` option decides whether to enable \YJIT stats or not.
+ #
+ # * `false`: Disable stats.
+ # * `true`: Enable stats. Print stats at exit.
+ # * `:quiet`: Enable stats. Do not print stats at exit.
+ def self.enable(stats: false)
+ return false if enabled?
+ at_exit { print_and_dump_stats } if stats
+ Primitive.rb_yjit_enable(stats, stats != :quiet)
end
# If --yjit-trace-exits is enabled parse the hashes from
# Primitive.rb_yjit_get_exit_locations into a format readable
# by Stackprof. This will allow us to find the exact location of a
# side exit in YJIT based on the instruction that is exiting.
- def self.exit_locations
+ def self.exit_locations # :nodoc:
return unless trace_exit_locations_enabled?
results = Primitive.rb_yjit_get_exit_locations
@@ -130,13 +135,13 @@ module RubyVM::YJIT
#
# In a script call:
#
- # at_exit do
- # RubyVM::YJIT.dump_exit_locations("my_file.dump")
- # end
+ # at_exit do
+ # RubyVM::YJIT.dump_exit_locations("my_file.dump")
+ # end
#
# Then run the file with the following options:
#
- # ruby --yjit --yjit-trace-exits test.rb
+ # ruby --yjit --yjit-trace-exits test.rb
#
# Once the code is done running, use Stackprof to read the dump file.
# See Stackprof documentation for options.
@@ -148,8 +153,8 @@ module RubyVM::YJIT
File.binwrite(filename, Marshal.dump(RubyVM::YJIT.exit_locations))
end
- # Return a hash for statistics generated for the --yjit-stats command line option.
- # Return nil when option is not passed or unavailable.
+ # Return a hash for statistics generated for the `--yjit-stats` command line option.
+ # Return `nil` when option is not passed or unavailable.
def self.runtime_stats(context: false)
stats = Primitive.rb_yjit_get_stats(context)
return stats if stats.nil?
@@ -162,19 +167,16 @@ module RubyVM::YJIT
# Number of instructions that finish executing in YJIT.
# See :count-placement: about the subtraction.
- retired_in_yjit = stats[:exec_instruction] - side_exits
+ retired_in_yjit = stats[:yjit_insns_count] - side_exits
# Average length of instruction sequences executed by YJIT
avg_len_in_yjit = total_exits > 0 ? retired_in_yjit.to_f / total_exits : 0
- # This only available on yjit stats builds
- if stats.key?(:vm_insns_count)
- # Proportion of instructions that retire in YJIT
- total_insns_count = retired_in_yjit + stats[:vm_insns_count]
- yjit_ratio_pct = 100.0 * retired_in_yjit.to_f / total_insns_count
- stats[:total_insns_count] = total_insns_count
- stats[:ratio_in_yjit] = yjit_ratio_pct
- end
+ # Proportion of instructions that retire in YJIT
+ total_insns_count = retired_in_yjit + stats[:vm_insns_count]
+ yjit_ratio_pct = 100.0 * retired_in_yjit.to_f / total_insns_count
+ stats[:total_insns_count] = total_insns_count
+ stats[:ratio_in_yjit] = yjit_ratio_pct
# Make those stats available in RubyVM::YJIT.runtime_stats as well
stats[:side_exit_count] = side_exits
@@ -185,7 +187,7 @@ module RubyVM::YJIT
end
# Format and print out counters as a String. This returns a non-empty
- # content only when --yjit-stats is enabled.
+ # content only when `--yjit-stats` is enabled.
def self.stats_string
# Lazily require StringIO to avoid breaking miniruby
require 'stringio'
@@ -194,22 +196,36 @@ module RubyVM::YJIT
strio.string
end
- # Produce disassembly for an iseq
- def self.disasm(iseq)
+ # Produce disassembly for an iseq. This requires a `--enable-yjit=dev` build.
+ def self.disasm(iseq) # :nodoc:
# If a method or proc is passed in, get its iseq
iseq = RubyVM::InstructionSequence.of(iseq)
- if self.enabled?
- # Produce the disassembly string
- # Include the YARV iseq disasm in the string for additional context
- iseq.disasm + "\n" + Primitive.rb_yjit_disasm_iseq(iseq)
- else
- iseq.disasm
+ if !self.enabled?
+ warn(
+ "YJIT needs to be enabled to produce disasm output, e.g.\n" +
+ "ruby --yjit-call-threshold=1 my_script.rb (see doc/yjit/yjit.md)"
+ )
+ return nil
+ end
+
+ disasm_str = Primitive.rb_yjit_disasm_iseq(iseq)
+
+ if !disasm_str
+ warn(
+ "YJIT disasm is only available when YJIT is built in dev mode, i.e.\n" +
+ "./configure --enable-yjit=dev (see doc/yjit/yjit.md)\n"
+ )
+ return nil
end
+
+ # Produce the disassembly string
+ # Include the YARV iseq disasm in the string for additional context
+ iseq.disasm + "\n" + disasm_str
end
# Produce a list of instructions compiled by YJIT for an iseq
- def self.insns_compiled(iseq)
+ def self.insns_compiled(iseq) # :nodoc:
return nil unless self.enabled?
# If a method or proc is passed in, get its iseq
@@ -217,7 +233,8 @@ module RubyVM::YJIT
Primitive.rb_yjit_insns_compiled(iseq)
end
- # Free and recompile all existing JIT code
+ # Discard existing compiled code to reclaim memory
+ # and allow for recompilations in the future.
def self.code_gc
Primitive.rb_yjit_code_gc
end
@@ -226,17 +243,23 @@ module RubyVM::YJIT
Primitive.rb_yjit_simulate_oom_bang
end
- # Avoid calling a method here to not interfere with compilation tests
+ # Avoid calling a Ruby method here to not interfere with compilation tests
if Primitive.rb_yjit_stats_enabled_p
- at_exit do
- _print_stats
- _dump_locations
- end
+ at_exit { print_and_dump_stats }
end
class << self
+ # :stopdoc:
private
+ # Print stats and dump exit locations
+ def print_and_dump_stats # :nodoc:
+ if Primitive.rb_yjit_print_stats_p
+ _print_stats
+ end
+ _dump_locations
+ end
+
def _dump_locations # :nodoc:
return unless trace_exit_locations_enabled?
@@ -246,6 +269,48 @@ module RubyVM::YJIT
$stderr.puts("YJIT exit locations dumped to `#{filename}`.")
end
+ # Print a summary of reasons for adverse performance events (e.g. exits)
+ def _print_stats_reasons(stats, out) # :nodoc:
+ print_counters(stats, out: out, prefix: 'send_', prompt: 'method call fallback reasons: ')
+ print_counters(stats, out: out, prefix: 'invokeblock_', prompt: 'invokeblock fallback reasons: ')
+ print_counters(stats, out: out, prefix: 'invokesuper_', prompt: 'invokesuper fallback reasons: ')
+ print_counters(stats, out: out, prefix: 'guard_send_', prompt: 'method call exit reasons: ')
+ print_counters(stats, out: out, prefix: 'guard_invokeblock_', prompt: 'invokeblock exit reasons: ')
+ print_counters(stats, out: out, prefix: 'guard_invokesuper_', prompt: 'invokesuper exit reasons: ')
+ print_counters(stats, out: out, prefix: 'gbpp_', prompt: 'getblockparamproxy exit reasons: ')
+ print_counters(stats, out: out, prefix: 'getivar_', prompt: 'getinstancevariable exit reasons:')
+ print_counters(stats, out: out, prefix: 'setivar_', prompt: 'setinstancevariable exit reasons:')
+ %w[
+ branchif
+ branchnil
+ branchunless
+ definedivar
+ expandarray
+ invokebuiltin
+ jump
+ leave
+ objtostring
+ opt_aref
+ opt_aref_with
+ opt_aset
+ opt_case_dispatch
+ opt_div
+ opt_getconstant_path
+ opt_minus
+ opt_mod
+ opt_mult
+ opt_plus
+ opt_succ
+ setlocal
+ splatkw
+ ].each do |insn|
+ print_counters(stats, out: out, prefix: "#{insn}_", prompt: "#{insn} exit reasons:", optional: true)
+ end
+ print_counters(stats, out: out, prefix: 'lshift_', prompt: 'left shift (opt_ltlt) exit reasons: ')
+ print_counters(stats, out: out, prefix: 'rshift_', prompt: 'right shift (>>) exit reasons: ')
+ print_counters(stats, out: out, prefix: 'invalidate_', prompt: 'invalidation reasons: ')
+ end
+
# Format and print out counters
def _print_stats(out: $stderr) # :nodoc:
stats = runtime_stats(context: true)
@@ -253,29 +318,38 @@ module RubyVM::YJIT
out.puts("***YJIT: Printing YJIT statistics on exit***")
- print_counters(stats, out: out, prefix: 'send_', prompt: 'method call exit reasons: ')
- print_counters(stats, out: out, prefix: 'invokeblock_', prompt: 'invokeblock exit reasons: ')
- print_counters(stats, out: out, prefix: 'invokesuper_', prompt: 'invokesuper exit reasons: ')
- print_counters(stats, out: out, prefix: 'leave_', prompt: 'leave exit reasons: ')
- print_counters(stats, out: out, prefix: 'gbpp_', prompt: 'getblockparamproxy exit reasons: ')
- print_counters(stats, out: out, prefix: 'getivar_', prompt: 'getinstancevariable exit reasons:')
- print_counters(stats, out: out, prefix: 'setivar_', prompt: 'setinstancevariable exit reasons:')
- print_counters(stats, out: out, prefix: 'definedivar_', prompt: 'definedivar exit reasons:')
- print_counters(stats, out: out, prefix: 'opt_aref_', prompt: 'opt_aref exit reasons: ')
- print_counters(stats, out: out, prefix: 'expandarray_', prompt: 'expandarray exit reasons: ')
- print_counters(stats, out: out, prefix: 'opt_getinlinecache_', prompt: 'opt_getinlinecache exit reasons: ')
- print_counters(stats, out: out, prefix: 'invalidate_', prompt: 'invalidation reasons: ')
+ _print_stats_reasons(stats, out)
# Number of failed compiler invocations
compilation_failure = stats[:compilation_failure]
+ code_region_overhead = stats[:code_region_size] - (stats[:inline_code_size] + stats[:outlined_code_size])
+
out.puts "num_send: " + format_number(13, stats[:num_send])
out.puts "num_send_known_class: " + format_number_pct(13, stats[:num_send_known_class], stats[:num_send])
out.puts "num_send_polymorphic: " + format_number_pct(13, stats[:num_send_polymorphic], stats[:num_send])
+ out.puts "num_send_megamorphic: " + format_number_pct(13, stats[:send_megamorphic], stats[:num_send])
+ out.puts "num_send_dynamic: " + format_number_pct(13, stats[:num_send_dynamic], stats[:num_send])
+ out.puts "num_send_cfunc: " + format_number_pct(13, stats[:num_send_cfunc], stats[:num_send])
+ out.puts "num_send_cfunc_inline: " + format_number_pct(13, stats[:num_send_cfunc_inline], stats[:num_send_cfunc])
+ out.puts "num_send_iseq: " + format_number_pct(13, stats[:num_send_iseq], stats[:num_send])
+ out.puts "num_send_iseq_leaf: " + format_number_pct(13, stats[:num_send_iseq_leaf], stats[:num_send_iseq])
+ out.puts "num_send_iseq_inline: " + format_number_pct(13, stats[:num_send_iseq_inline], stats[:num_send_iseq])
if stats[:num_send_x86_rel32] != 0 || stats[:num_send_x86_reg] != 0
out.puts "num_send_x86_rel32: " + format_number(13, stats[:num_send_x86_rel32])
out.puts "num_send_x86_reg: " + format_number(13, stats[:num_send_x86_reg])
end
+ out.puts "num_getivar_megamorphic: " + format_number(11, stats[:num_getivar_megamorphic])
+ out.puts "num_setivar_megamorphic: " + format_number(11, stats[:num_setivar_megamorphic])
+ out.puts "num_opt_case_megamorphic: " + format_number(10, stats[:num_opt_case_dispatch_megamorphic])
+ out.puts "num_throw: " + format_number(13, stats[:num_throw])
+ out.puts "num_throw_break: " + format_number_pct(13, stats[:num_throw_break], stats[:num_throw])
+ out.puts "num_throw_retry: " + format_number_pct(13, stats[:num_throw_retry], stats[:num_throw])
+ out.puts "num_throw_return: " + format_number_pct(13, stats[:num_throw_return], stats[:num_throw])
+ out.puts "num_lazy_frame_check: " + format_number(13, stats[:num_lazy_frame_check])
+ out.puts "num_lazy_frame_push: " + format_number_pct(13, stats[:num_lazy_frame_push], stats[:num_lazy_frame_check])
+ out.puts "lazy_frame_count: " + format_number(13, stats[:lazy_frame_count])
+ out.puts "lazy_frame_failure: " + format_number(13, stats[:lazy_frame_failure])
out.puts "iseq_stack_too_large: " + format_number(13, stats[:iseq_stack_too_large])
out.puts "iseq_too_long: " + format_number(13, stats[:iseq_too_long])
@@ -285,13 +359,19 @@ module RubyVM::YJIT
out.puts "bindings_allocations: " + format_number(13, stats[:binding_allocations])
out.puts "bindings_set: " + format_number(13, stats[:binding_set])
out.puts "compilation_failure: " + format_number(13, compilation_failure) if compilation_failure != 0
+ out.puts "live_iseq_count: " + format_number(13, stats[:live_iseq_count])
+ out.puts "iseq_alloc_count: " + format_number(13, stats[:iseq_alloc_count])
+ out.puts "compiled_iseq_entry: " + format_number(13, stats[:compiled_iseq_entry])
+ out.puts "cold_iseq_entry: " + format_number_pct(13, stats[:cold_iseq_entry], stats[:compiled_iseq_entry] + stats[:cold_iseq_entry])
out.puts "compiled_iseq_count: " + format_number(13, stats[:compiled_iseq_count])
out.puts "compiled_blockid_count:" + format_number(13, stats[:compiled_blockid_count])
out.puts "compiled_block_count: " + format_number(13, stats[:compiled_block_count])
if stats[:compiled_blockid_count] != 0
out.puts "versions_per_block: " + format_number(13, "%4.3f" % (stats[:compiled_block_count].fdiv(stats[:compiled_blockid_count])))
end
+ out.puts "max_inline_versions: " + format_number(13, stats[:max_inline_versions])
out.puts "compiled_branch_count: " + format_number(13, stats[:compiled_branch_count])
+ out.puts "compile_time_ms: " + format_number(13, stats[:compile_time_ns] / (1000 * 1000))
out.puts "block_next_count: " + format_number(13, stats[:block_next_count])
out.puts "defer_count: " + format_number(13, stats[:defer_count])
out.puts "defer_empty_count: " + format_number(13, stats[:defer_empty_count])
@@ -301,11 +381,11 @@ module RubyVM::YJIT
out.puts "freed_iseq_count: " + format_number(13, stats[:freed_iseq_count])
out.puts "invalidation_count: " + format_number(13, stats[:invalidation_count])
- out.puts "constant_state_bumps: " + format_number(13, stats[:constant_state_bumps])
- out.puts "get_ivar_max_depth: " + format_number(13, stats[:get_ivar_max_depth])
out.puts "inline_code_size: " + format_number(13, stats[:inline_code_size])
out.puts "outlined_code_size: " + format_number(13, stats[:outlined_code_size])
out.puts "code_region_size: " + format_number(13, stats[:code_region_size])
+ out.puts "code_region_overhead: " + format_number_pct(13, code_region_overhead, stats[:code_region_size])
+
out.puts "freed_code_size: " + format_number(13, stats[:freed_code_size])
out.puts "yjit_alloc_size: " + format_number(13, stats[:yjit_alloc_size]) if stats.key?(:yjit_alloc_size)
out.puts "live_context_size: " + format_number(13, stats[:live_context_size])
@@ -317,17 +397,36 @@ module RubyVM::YJIT
out.puts "object_shape_count: " + format_number(13, stats[:object_shape_count])
out.puts "side_exit_count: " + format_number(13, stats[:side_exit_count])
out.puts "total_exit_count: " + format_number(13, stats[:total_exit_count])
- out.puts "total_insns_count: " + format_number(13, stats[:total_insns_count]) if stats.key?(:total_insns_count)
- if stats.key?(:vm_insns_count)
- out.puts "vm_insns_count: " + format_number(13, stats[:vm_insns_count])
- end
- out.puts "yjit_insns_count: " + format_number(13, stats[:exec_instruction])
- if stats.key?(:ratio_in_yjit)
- out.puts "ratio_in_yjit: " + ("%12.1f" % stats[:ratio_in_yjit]) + "%"
- end
+ out.puts "total_insns_count: " + format_number(13, stats[:total_insns_count])
+ out.puts "vm_insns_count: " + format_number(13, stats[:vm_insns_count])
+ out.puts "yjit_insns_count: " + format_number(13, stats[:yjit_insns_count])
+ out.puts "ratio_in_yjit: " + ("%12.1f" % stats[:ratio_in_yjit]) + "%"
out.puts "avg_len_in_yjit: " + ("%13.1f" % stats[:avg_len_in_yjit])
print_sorted_exit_counts(stats, out: out, prefix: "exit_")
+
+ print_sorted_method_calls(stats[:cfunc_calls], stats[:num_send_cfunc], out: out, type: 'C')
+ print_sorted_method_calls(stats[:iseq_calls], stats[:num_send_iseq], out: out, type: 'ISEQ')
+ end
+
+ def print_sorted_method_calls(calls, num_calls, out:, type:, how_many: 20, left_pad: 4) # :nodoc:
+ return if calls.empty?
+
+ # Sort calls by decreasing frequency and keep the top N
+ pairs = calls.map { |k,v| [k, v] }
+ pairs.sort_by! {|pair| -pair[1] }
+ pairs = pairs[0...how_many]
+
+ top_n_total = pairs.sum { |name, count| count }
+ top_n_pct = 100.0 * top_n_total / num_calls
+
+ out.puts "Top-#{pairs.size} most frequent #{type} calls (#{"%.1f" % top_n_pct}% of #{type} calls):"
+
+ count_width = format_number(0, pairs[0][1]).length
+ pairs.each do |name, count|
+ padded_count = format_number_pct(count_width, count, num_calls)
+ out.puts(" #{padded_count}: #{name}")
+ end
end
def print_sorted_exit_counts(stats, out:, prefix:, how_many: 20, left_pad: 4) # :nodoc:
@@ -348,12 +447,10 @@ module RubyVM::YJIT
out.puts "Top-#{exits.size} most frequent exit ops (#{"%.1f" % top_n_exit_pct}% of exits):"
- longest_insn_name_len = exits.max_by { |name, count| name.length }.first.length
+ count_width = format_number(0, exits[0][1]).length
exits.each do |name, count|
- padding = longest_insn_name_len + left_pad
- padded_name = "%#{padding}s" % name
- padded_count = format_number_pct(10, count, total_exits)
- out.puts("#{padded_name}: #{padded_count}")
+ padded_count = format_number_pct(count_width, count, total_exits)
+ out.puts(" #{padded_count}: #{name}")
end
else
out.puts "total_exits: " + format_number(10, total_exits)
@@ -368,15 +465,19 @@ module RubyVM::YJIT
total
end
- def print_counters(counters, out:, prefix:, prompt:) # :nodoc:
- out.puts(prompt)
+ def print_counters(counters, out:, prefix:, prompt:, optional: false) # :nodoc:
counters = counters.filter { |key, _| key.start_with?(prefix) }
counters.filter! { |_, value| value != 0 }
counters.transform_keys! { |key| key.to_s.delete_prefix(prefix) }
if counters.empty?
- out.puts(" (all relevant counters are zero)")
+ unless optional
+ out.puts(prompt)
+ out.puts(" (all relevant counters are zero)")
+ end
return
+ else
+ out.puts(prompt)
end
counters = counters.to_a
@@ -392,20 +493,21 @@ module RubyVM::YJIT
end
# Format large numbers with comma separators for readability
- def format_number(pad, number)
- integer, decimal = number.to_s.split(".")
- d_groups = integer.chars.to_a.reverse.each_slice(3)
- with_commas = d_groups.map(&:join).join(',').reverse
- formatted = [with_commas, decimal].compact.join(".")
- formatted.rjust(pad, ' ')
+ def format_number(pad, number) # :nodoc:
+ s = number.to_s
+ i = s.index('.') || s.size
+ s.insert(i -= 3, ',') while i > 3
+ s.rjust(pad, ' ')
end
# Format a number along with a percentage over a total value
- def format_number_pct(pad, number, total)
+ def format_number_pct(pad, number, total) # :nodoc:
padded_count = format_number(pad, number)
percentage = number.fdiv(total) * 100
formatted_pct = "%4.1f%%" % percentage
"#{padded_count} (#{formatted_pct})"
end
+
+ # :startdoc:
end
end
diff --git a/yjit/Cargo.lock b/yjit/Cargo.lock
index 08ae1bd487..5245a61aff 100644
--- a/yjit/Cargo.lock
+++ b/yjit/Cargo.lock
@@ -4,9 +4,9 @@ version = 3
[[package]]
name = "capstone"
-version = "0.10.0"
+version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "66b5d1f14c3539b6ff22fcb602fea5f1c4416148c8b7965a2e74860aa169b7b5"
+checksum = "b08ca438d9585a2b216b0c2e88ea51e096286c5f197f7be2526bb515ef775b6c"
dependencies = [
"capstone-sys",
"libc",
@@ -14,9 +14,9 @@ dependencies = [
[[package]]
name = "capstone-sys"
-version = "0.14.0"
+version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df653a22d0ad34b0d91cc92a6289d96e44aac1c9a96250a094c9aeec4a91084f"
+checksum = "fe7183271711ffb7c63a6480e4baf480e0140da59eeba9b18fcc8bf3478950e3"
dependencies = [
"cc",
"libc",
@@ -35,15 +35,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50"
[[package]]
-name = "stats_alloc"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c0e04424e733e69714ca1bbb9204c1a57f09f5493439520f9f68c132ad25eec"
-
-[[package]]
name = "yjit"
version = "0.1.0"
dependencies = [
"capstone",
- "stats_alloc",
]
diff --git a/yjit/Cargo.toml b/yjit/Cargo.toml
index 8cd593e4ee..0b2e286cc9 100644
--- a/yjit/Cargo.toml
+++ b/yjit/Cargo.toml
@@ -15,14 +15,13 @@ crate-type = ["staticlib"]
[dependencies]
# No required dependencies to simplify build process. TODO: Link to yet to be
# written rationale. Optional For development and testing purposes
-capstone = { version = "0.10.0", optional = true }
-stats_alloc = { version = "0.1.10", optional = true }
+capstone = { version = "0.12.0", optional = true }
[features]
# NOTE: Development builds select a set of these via configure.ac
# For debugging, `make V=1` shows exact cargo invocation.
disasm = ["capstone"]
-stats = ["stats_alloc"]
+stats = []
[profile.dev]
opt-level = 0
diff --git a/yjit/bindgen/Cargo.lock b/yjit/bindgen/Cargo.lock
index 817caf548a..6338d9fea8 100644
--- a/yjit/bindgen/Cargo.lock
+++ b/yjit/bindgen/Cargo.lock
@@ -12,23 +12,12 @@ dependencies = [
]
[[package]]
-name = "atty"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
-dependencies = [
- "hermit-abi",
- "libc",
- "winapi",
-]
-
-[[package]]
name = "bindgen"
version = "0.63.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36d860121800b2a9a94f9b5604b332d5cffb234ce17609ea479d723dbc9d3885"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"cexpr",
"clang-sys",
"lazy_static",
@@ -51,6 +40,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
+name = "bitflags"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+
+[[package]]
+name = "cc"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+
+[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -84,18 +85,39 @@ checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]]
name = "env_logger"
-version = "0.9.3"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7"
+checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
dependencies = [
- "atty",
"humantime",
+ "is-terminal",
"log",
"regex",
"termcolor",
]
[[package]]
+name = "errno"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -103,12 +125,9 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "hermit-abi"
-version = "0.1.19"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
-dependencies = [
- "libc",
-]
+checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "humantime"
@@ -117,6 +136,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
+name = "is-terminal"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24fddda5af7e54bf7da53067d6e802dbcc381d0a8eef629df528e3ebf68755cb"
+dependencies = [
+ "hermit-abi",
+ "rustix",
+ "windows-sys",
+]
+
+[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -130,9 +160,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
-version = "0.2.138"
+version = "0.2.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
+checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
[[package]]
name = "libloading"
@@ -145,6 +175,12 @@ dependencies = [
]
[[package]]
+name = "linux-raw-sys"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
+
+[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -229,10 +265,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
+name = "rustix"
+version = "0.38.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed"
+dependencies = [
+ "bitflags 2.4.1",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
name = "shlex"
-version = "1.1.0"
+version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "syn"
@@ -303,6 +352,72 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+
+[[package]]
name = "yjit-bindgen"
version = "0.1.0"
dependencies = [
diff --git a/yjit/bindgen/Cargo.toml b/yjit/bindgen/Cargo.toml
index 34dfc27ba9..a85c04cf0e 100644
--- a/yjit/bindgen/Cargo.toml
+++ b/yjit/bindgen/Cargo.toml
@@ -7,4 +7,4 @@ edition = "2021"
[dependencies]
bindgen = "0.63.0"
-env_logger = "0.9.0"
+env_logger = "0.10.0"
diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs
index 4fb02cfdf0..a7473c1bf6 100644
--- a/yjit/bindgen/src/main.rs
+++ b/yjit/bindgen/src/main.rs
@@ -38,6 +38,7 @@ fn main() {
.clang_args(filtered_clang_args)
.header("encindex.h")
.header("internal.h")
+ .header("internal/object.h")
.header("internal/re.h")
.header("include/ruby/ruby.h")
.header("shape.h")
@@ -84,7 +85,7 @@ fn main() {
// From include/ruby/internal/core/rbasic.h
.allowlist_type("RBasic")
- .allowlist_type("rstring_offsets")
+ // From include/ruby/internal/core/rstring.h
.allowlist_type("ruby_rstring_flags")
// From internal.h
@@ -101,7 +102,6 @@ fn main() {
.allowlist_function("rb_shape_get_iv_index")
.allowlist_function("rb_shape_get_next")
.allowlist_function("rb_shape_id")
- .allowlist_function("rb_shape_transition_shape_capa")
.allowlist_function("rb_shape_obj_too_complex")
.allowlist_var("SHAPE_ID_NUM_BITS")
.allowlist_var("OBJ_TOO_COMPLEX_SHAPE_ID")
@@ -120,6 +120,7 @@ fn main() {
.allowlist_function("rb_hash_new_with_size")
.allowlist_function("rb_hash_resurrect")
.allowlist_function("rb_hash_stlike_foreach")
+ .allowlist_function("rb_to_hash_type")
// From include/ruby/st.h
.allowlist_type("st_retval")
@@ -134,6 +135,7 @@ fn main() {
.allowlist_function("rb_ary_new_capa")
.allowlist_function("rb_ary_store")
.allowlist_function("rb_ary_resurrect")
+ .allowlist_function("rb_ary_cat")
.allowlist_function("rb_ary_clear")
.allowlist_function("rb_ary_dup")
.allowlist_function("rb_ary_push")
@@ -165,12 +167,14 @@ fn main() {
.allowlist_var("rb_cTrueClass")
.allowlist_var("rb_cFalseClass")
.allowlist_var("rb_cInteger")
+ .allowlist_var("rb_cIO")
.allowlist_var("rb_cSymbol")
.allowlist_var("rb_cFloat")
.allowlist_var("rb_cString")
.allowlist_var("rb_cThread")
.allowlist_var("rb_cArray")
.allowlist_var("rb_cHash")
+ .allowlist_var("rb_cClass")
// From include/ruby/internal/fl_type.h
.allowlist_type("ruby_fl_type")
@@ -178,7 +182,6 @@ fn main() {
// From include/ruby/internal/core/robject.h
.allowlist_type("ruby_robject_flags")
- .allowlist_var("ROBJECT_OFFSET_.*")
// From include/ruby/internal/core/rarray.h
.allowlist_type("ruby_rarray_flags")
@@ -195,8 +198,6 @@ fn main() {
.allowlist_type("rb_call_data")
.blocklist_type("rb_callcache.*") // Not used yet - opaque to make it easy to import rb_call_data
.opaque_type("rb_callcache.*")
- .blocklist_type("rb_callinfo_kwarg") // Contains a VALUE[] array of undefined size, so we don't import
- .opaque_type("rb_callinfo_kwarg")
.allowlist_type("rb_callinfo")
// From vm_insnhelper.h
@@ -208,6 +209,7 @@ fn main() {
// From include/ruby/internal/symbol.h
.allowlist_function("rb_intern")
+ .allowlist_function("rb_intern2")
.allowlist_function("rb_id2sym")
.allowlist_function("rb_id2name")
.allowlist_function("rb_sym2id")
@@ -215,11 +217,16 @@ fn main() {
// From internal/numeric.h
.allowlist_function("rb_fix_aref")
+ .allowlist_function("rb_float_plus")
+ .allowlist_function("rb_float_minus")
+ .allowlist_function("rb_float_mul")
+ .allowlist_function("rb_float_div")
// From internal/string.h
.allowlist_function("rb_ec_str_resurrect")
.allowlist_function("rb_str_concat_literals")
.allowlist_function("rb_obj_as_string_result")
+ .allowlist_function("rb_str_byte_substr")
// From include/ruby/internal/intern/parse.h
.allowlist_function("rb_backref_get")
@@ -244,9 +251,6 @@ fn main() {
// From include/ruby/internal/hash.h
.allowlist_type("ruby_rhash_flags") // really old C extension API
- // Autogenerated into id.h
- .allowlist_type("ruby_method_ids")
-
// From method.h
.allowlist_type("rb_method_visibility_t")
.allowlist_type("rb_method_type_t")
@@ -264,6 +268,7 @@ fn main() {
.allowlist_var("VM_BLOCK_HANDLER_NONE")
.allowlist_type("vm_frame_env_flags")
.allowlist_type("rb_seq_param_keyword_struct")
+ .allowlist_type("rb_callinfo_kwarg")
.allowlist_type("ruby_basic_operators")
.allowlist_var(".*_REDEFINED_OP_FLAG")
.allowlist_type("rb_num_t")
@@ -283,6 +288,7 @@ fn main() {
.blocklist_type("rb_control_frame_struct")
.opaque_type("rb_control_frame_struct")
.allowlist_function("rb_vm_bh_to_procval")
+ .allowlist_function("rb_vm_env_write")
.allowlist_function("rb_vm_ep_local_ep")
.allowlist_type("vm_special_object_type")
.allowlist_var("VM_ENV_DATA_INDEX_SPECVAL")
@@ -290,6 +296,10 @@ fn main() {
.allowlist_var("VM_ENV_DATA_SIZE")
.allowlist_function("rb_iseq_path")
.allowlist_type("rb_builtin_attr")
+ .allowlist_type("ruby_tag_type")
+ .allowlist_type("ruby_vm_throw_flags")
+ .allowlist_type("vm_check_match_type")
+ .allowlist_type("rb_iseq_type")
// From yjit.c
.allowlist_function("rb_iseq_(get|set)_yjit_payload")
@@ -301,9 +311,9 @@ fn main() {
.allowlist_function("rb_yjit_mark_unused")
.allowlist_function("rb_yjit_get_page_size")
.allowlist_function("rb_yjit_iseq_builtin_attrs")
+ .allowlist_function("rb_yjit_iseq_inspect")
.allowlist_function("rb_yjit_builtin_function")
.allowlist_function("rb_set_cfp_(pc|sp)")
- .allowlist_function("rb_cfp_get_iseq")
.allowlist_function("rb_yjit_multi_ractor_p")
.allowlist_function("rb_c_method_tracing_currently_enabled")
.allowlist_function("rb_full_cfunc_return")
@@ -324,6 +334,11 @@ fn main() {
.allowlist_function("rb_yjit_icache_invalidate")
.allowlist_function("rb_optimized_call")
.allowlist_function("rb_yjit_assert_holding_vm_lock")
+ .allowlist_function("rb_yjit_sendish_sp_pops")
+ .allowlist_function("rb_yjit_invokeblock_sp_pops")
+ .allowlist_function("rb_yjit_set_exception_return")
+ .allowlist_type("robject_offsets")
+ .allowlist_type("rstring_offsets")
// from vm_sync.h
.allowlist_function("rb_vm_barrier")
@@ -343,6 +358,7 @@ fn main() {
.allowlist_function("rb_iseqw_to_iseq")
.allowlist_function("rb_iseq_label")
.allowlist_function("rb_iseq_line_no")
+ .allowlist_type("defined_type")
// From builtin.h
.allowlist_type("rb_builtin_function.*")
@@ -356,12 +372,19 @@ fn main() {
.allowlist_function("rb_ivar_defined")
.allowlist_function("rb_ivar_get")
+ // From internal/vm.h
+ .allowlist_var("rb_vm_insns_count")
+
// From include/ruby/internal/intern/vm.h
.allowlist_function("rb_get_alloc_func")
- // From gc.h and internal/gc.h
+ // From internal/object.h
.allowlist_function("rb_class_allocate_instance")
+ .allowlist_function("rb_obj_equal")
+
+ // From gc.h and internal/gc.h
.allowlist_function("rb_obj_info")
+ .allowlist_function("ruby_xfree")
// From include/ruby/debug.h
.allowlist_function("rb_profile_frames")
@@ -390,16 +413,19 @@ fn main() {
.allowlist_function("rb_get_def_iseq_ptr")
.allowlist_function("rb_get_def_bmethod_proc")
.allowlist_function("rb_iseq_encoded_size")
+ .allowlist_function("rb_get_iseq_body_total_calls")
.allowlist_function("rb_get_iseq_body_local_iseq")
.allowlist_function("rb_get_iseq_body_parent_iseq")
.allowlist_function("rb_get_iseq_body_iseq_encoded")
.allowlist_function("rb_get_iseq_body_stack_max")
+ .allowlist_function("rb_get_iseq_body_type")
.allowlist_function("rb_get_iseq_flags_has_lead")
.allowlist_function("rb_get_iseq_flags_has_opt")
.allowlist_function("rb_get_iseq_flags_has_kw")
.allowlist_function("rb_get_iseq_flags_has_rest")
.allowlist_function("rb_get_iseq_flags_has_post")
.allowlist_function("rb_get_iseq_flags_has_kwrest")
+ .allowlist_function("rb_get_iseq_flags_anon_kwrest")
.allowlist_function("rb_get_iseq_flags_has_block")
.allowlist_function("rb_get_iseq_flags_ambiguous_param0")
.allowlist_function("rb_get_iseq_flags_accepts_no_kwarg")
@@ -416,9 +442,9 @@ fn main() {
.allowlist_function("rb_yarv_str_eql_internal")
.allowlist_function("rb_str_neq_internal")
.allowlist_function("rb_yarv_ary_entry_internal")
+ .allowlist_function("rb_yjit_ruby2_keywords_splat_p")
.allowlist_function("rb_yjit_fix_div_fix")
.allowlist_function("rb_yjit_fix_mod_fix")
- .allowlist_function("rb_yjit_fix_mul_fix")
.allowlist_function("rb_FL_TEST")
.allowlist_function("rb_FL_TEST_RAW")
.allowlist_function("rb_RB_TYPE_P")
@@ -434,6 +460,12 @@ fn main() {
.allowlist_function("rb_method_basic_definition_p")
.allowlist_function("rb_yjit_array_len")
.allowlist_function("rb_obj_class")
+ .allowlist_function("rb_obj_is_proc")
+ .allowlist_function("rb_vm_base_ptr")
+ .allowlist_function("rb_ec_stack_check")
+ .allowlist_function("rb_vm_top_self")
+ .allowlist_function("rb_yjit_splat_varg_checks")
+ .allowlist_function("rb_yjit_splat_varg_cfunc")
// We define VALUE manually, don't import it
.blocklist_type("VALUE")
diff --git a/yjit/src/asm/arm64/arg/bitmask_imm.rs b/yjit/src/asm/arm64/arg/bitmask_imm.rs
index ff9b2c8a2d..6b71a73d2c 100644
--- a/yjit/src/asm/arm64/arg/bitmask_imm.rs
+++ b/yjit/src/asm/arm64/arg/bitmask_imm.rs
@@ -106,7 +106,7 @@ mod tests {
#[test]
fn test_failures() {
- vec![5, 9, 10, 11, 13, 17, 18, 19].iter().for_each(|&imm| {
+ [5, 9, 10, 11, 13, 17, 18, 19].iter().for_each(|&imm| {
assert!(BitmaskImmediate::try_from(imm).is_err());
});
}
diff --git a/yjit/src/asm/arm64/inst/madd.rs b/yjit/src/asm/arm64/inst/madd.rs
new file mode 100644
index 0000000000..683e643189
--- /dev/null
+++ b/yjit/src/asm/arm64/inst/madd.rs
@@ -0,0 +1,73 @@
+use super::super::arg::Sf;
+
+/// The struct that represents an A64 multiply-add instruction that can be
+/// encoded.
+///
+/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
+/// | 31 30 29 28 | 27 26 25 24 | 23 22 21 20 | 19 18 17 16 | 15 14 13 12 | 11 10 09 08 | 07 06 05 04 | 03 02 01 00 |
+/// | 0 0 1 1 0 1 1 0 0 0 0 |
+/// | sf rm.............. ra.............. rn.............. rd.............. |
+/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
+///
+pub struct MAdd {
+ /// The number of the general-purpose destination register.
+ rd: u8,
+
+ /// The number of the first general-purpose source register.
+ rn: u8,
+
+ /// The number of the third general-purpose source register.
+ ra: u8,
+
+ /// The number of the second general-purpose source register.
+ rm: u8,
+
+ /// The size of the registers of this instruction.
+ sf: Sf
+}
+
+impl MAdd {
+ /// MUL
+ /// https://developer.arm.com/documentation/ddi0602/2023-06/Base-Instructions/MUL--Multiply--an-alias-of-MADD-
+ pub fn mul(rd: u8, rn: u8, rm: u8, num_bits: u8) -> Self {
+ Self { rd, rn, ra: 0b11111, rm, sf: num_bits.into() }
+ }
+}
+
+impl From<MAdd> for u32 {
+ /// Convert an instruction into a 32-bit value.
+ fn from(inst: MAdd) -> Self {
+ 0
+ | ((inst.sf as u32) << 31)
+ | (0b11011 << 24)
+ | ((inst.rm as u32) << 16)
+ | ((inst.ra as u32) << 10)
+ | ((inst.rn as u32) << 5)
+ | (inst.rd as u32)
+ }
+}
+
+impl From<MAdd> for [u8; 4] {
+ /// Convert an instruction into a 4 byte array.
+ fn from(inst: MAdd) -> [u8; 4] {
+ let result: u32 = inst.into();
+ result.to_le_bytes()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_mul_32() {
+ let result: u32 = MAdd::mul(0, 1, 2, 32).into();
+ assert_eq!(0x1B027C20, result);
+ }
+
+ #[test]
+ fn test_mul_64() {
+ let result: u32 = MAdd::mul(0, 1, 2, 64).into();
+ assert_eq!(0x9B027C20, result);
+ }
+}
diff --git a/yjit/src/asm/arm64/inst/mod.rs b/yjit/src/asm/arm64/inst/mod.rs
index 9821e6a334..bfffd914ef 100644
--- a/yjit/src/asm/arm64/inst/mod.rs
+++ b/yjit/src/asm/arm64/inst/mod.rs
@@ -16,6 +16,8 @@ mod load_store;
mod load_store_exclusive;
mod logical_imm;
mod logical_reg;
+mod madd;
+mod smulh;
mod mov;
mod nop;
mod pc_rel;
@@ -40,6 +42,8 @@ pub use load_store::LoadStore;
pub use load_store_exclusive::LoadStoreExclusive;
pub use logical_imm::LogicalImm;
pub use logical_reg::LogicalReg;
+pub use madd::MAdd;
+pub use smulh::SMulH;
pub use mov::Mov;
pub use nop::Nop;
pub use pc_rel::PCRelative;
diff --git a/yjit/src/asm/arm64/inst/smulh.rs b/yjit/src/asm/arm64/inst/smulh.rs
new file mode 100644
index 0000000000..5e9b231fde
--- /dev/null
+++ b/yjit/src/asm/arm64/inst/smulh.rs
@@ -0,0 +1,60 @@
+/// The struct that represents an A64 signed multiply high instruction
+///
+/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
+/// | 31 30 29 28 | 27 26 25 24 | 23 22 21 20 | 19 18 17 16 | 15 14 13 12 | 11 10 09 08 | 07 06 05 04 | 03 02 01 00 |
+/// | 1 0 0 1 1 0 1 1 0 1 0 0 |
+/// | rm.............. ra.............. rn.............. rd.............. |
+/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
+///
+pub struct SMulH {
+ /// The number of the general-purpose destination register.
+ rd: u8,
+
+ /// The number of the first general-purpose source register.
+ rn: u8,
+
+ /// The number of the third general-purpose source register.
+ ra: u8,
+
+ /// The number of the second general-purpose source register.
+ rm: u8,
+}
+
+impl SMulH {
+ /// SMULH
+ /// https://developer.arm.com/documentation/ddi0602/2023-06/Base-Instructions/SMULH--Signed-Multiply-High-
+ pub fn smulh(rd: u8, rn: u8, rm: u8) -> Self {
+ Self { rd, rn, ra: 0b11111, rm }
+ }
+}
+
+impl From<SMulH> for u32 {
+ /// Convert an instruction into a 32-bit value.
+ fn from(inst: SMulH) -> Self {
+ 0
+ | (0b10011011010 << 21)
+ | ((inst.rm as u32) << 16)
+ | ((inst.ra as u32) << 10)
+ | ((inst.rn as u32) << 5)
+ | (inst.rd as u32)
+ }
+}
+
+impl From<SMulH> for [u8; 4] {
+ /// Convert an instruction into a 4 byte array.
+ fn from(inst: SMulH) -> [u8; 4] {
+ let result: u32 = inst.into();
+ result.to_le_bytes()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_smulh() {
+ let result: u32 = SMulH::smulh(0, 1, 2).into();
+ assert_eq!(0x9b427c20, result);
+ }
+}
diff --git a/yjit/src/asm/arm64/mod.rs b/yjit/src/asm/arm64/mod.rs
index 9d4c4d639b..a94d435b7c 100644
--- a/yjit/src/asm/arm64/mod.rs
+++ b/yjit/src/asm/arm64/mod.rs
@@ -186,7 +186,7 @@ pub fn asr(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, shift: A64Opnd) {
SBFM::asr(rd.reg_no, rn.reg_no, shift.try_into().unwrap(), rd.num_bits).into()
},
- _ => panic!("Invalid operand combination to asr instruction."),
+ _ => panic!("Invalid operand combination to asr instruction: asr {:?}, {:?}, {:?}", rd, rn, shift),
};
cb.write_bytes(&bytes);
@@ -215,6 +215,9 @@ pub const fn bcond_offset_fits_bits(offset: i64) -> bool {
imm_fits_bits(offset, 19)
}
+/// CBZ and CBNZ also have a limit of 19 bits for the branch offset.
+pub use bcond_offset_fits_bits as cmp_branch_offset_fits_bits;
+
/// B.cond - branch to target if condition is true
pub fn bcond(cb: &mut CodeBlock, cond: u8, offset: InstructionOffset) {
assert!(bcond_offset_fits_bits(offset.into()), "The offset must be 19 bits or less.");
@@ -276,6 +279,9 @@ pub fn cmp(cb: &mut CodeBlock, rn: A64Opnd, rm: A64Opnd) {
DataReg::cmp(rn.reg_no, rm.reg_no, rn.num_bits).into()
},
+ (A64Opnd::Reg(rn), A64Opnd::Imm(imm12)) => {
+ DataImm::cmp(rn.reg_no, (imm12 as u64).try_into().unwrap(), rn.num_bits).into()
+ },
(A64Opnd::Reg(rn), A64Opnd::UImm(imm12)) => {
DataImm::cmp(rn.reg_no, imm12.try_into().unwrap(), rn.num_bits).into()
},
@@ -699,6 +705,35 @@ pub fn msr(cb: &mut CodeBlock, systemregister: SystemRegister, rt: A64Opnd) {
cb.write_bytes(&bytes);
}
+/// MUL - multiply two registers, put the result in a third register
+pub fn mul(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, rm: A64Opnd) {
+ let bytes: [u8; 4] = match (rd, rn, rm) {
+ (A64Opnd::Reg(rd), A64Opnd::Reg(rn), A64Opnd::Reg(rm)) => {
+ assert!(rd.num_bits == rn.num_bits && rn.num_bits == rm.num_bits, "Expected registers to be the same size");
+
+ MAdd::mul(rd.reg_no, rn.reg_no, rm.reg_no, rd.num_bits).into()
+ },
+ _ => panic!("Invalid operand combination to mul instruction")
+ };
+
+ cb.write_bytes(&bytes);
+}
+
+/// SMULH - multiply two 64-bit registers to produce a 128-bit result, put the high 64-bits of the result into rd
+pub fn smulh(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, rm: A64Opnd) {
+ let bytes: [u8; 4] = match (rd, rn, rm) {
+ (A64Opnd::Reg(rd), A64Opnd::Reg(rn), A64Opnd::Reg(rm)) => {
+ assert!(rd.num_bits == rn.num_bits && rn.num_bits == rm.num_bits, "Expected registers to be the same size");
+ assert!(rd.num_bits == 64, "smulh only applicable to 64-bit registers");
+
+ SMulH::smulh(rd.reg_no, rn.reg_no, rm.reg_no).into()
+ },
+ _ => panic!("Invalid operand combination to mul instruction")
+ };
+
+ cb.write_bytes(&bytes);
+}
+
/// MVN - move a value in a register to another register, negating it
pub fn mvn(cb: &mut CodeBlock, rd: A64Opnd, rm: A64Opnd) {
let bytes: [u8; 4] = match (rd, rm) {
@@ -1064,6 +1099,48 @@ pub fn tst(cb: &mut CodeBlock, rn: A64Opnd, rm: A64Opnd) {
cb.write_bytes(&bytes);
}
+/// CBZ - branch if a register is zero
+pub fn cbz(cb: &mut CodeBlock, rt: A64Opnd, offset: InstructionOffset) {
+ assert!(imm_fits_bits(offset.into(), 19), "jump offset for cbz must fit in 19 bits");
+ let bytes: [u8; 4] = if let A64Opnd::Reg(rt) = rt {
+ cbz_cbnz(rt.num_bits, false, offset, rt.reg_no)
+ } else {
+ panic!("Invalid operand combination to cbz instruction.")
+ };
+
+ cb.write_bytes(&bytes);
+}
+
+/// CBNZ - branch if a register is non-zero
+pub fn cbnz(cb: &mut CodeBlock, rt: A64Opnd, offset: InstructionOffset) {
+ assert!(imm_fits_bits(offset.into(), 19), "jump offset for cbz must fit in 19 bits");
+ let bytes: [u8; 4] = if let A64Opnd::Reg(rt) = rt {
+ cbz_cbnz(rt.num_bits, true, offset, rt.reg_no)
+ } else {
+ panic!("Invalid operand combination to cbnz instruction.")
+ };
+
+ cb.write_bytes(&bytes);
+}
+
+/// Encode Compare and Branch on Zero (CBZ) with `op=0` or Compare and Branch on Nonzero (CBNZ)
+/// with `op=1`.
+///
+/// <https://developer.arm.com/documentation/ddi0602/2024-03/Base-Instructions/CBZ--Compare-and-Branch-on-Zero->
+///
+/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
+/// | 31 30 29 28 | 27 26 25 24 | 23 22 21 20 | 19 18 17 16 | 15 14 13 12 | 11 10 09 08 | 07 06 05 04 | 03 02 01 00 |
+/// | sf 0 1 1 0 1 0 op |
+/// | imm19........................................................... Rt.............. |
+/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
+fn cbz_cbnz(num_bits: u8, op: bool, offset: InstructionOffset, rt: u8) -> [u8; 4] {
+ ((Sf::from(num_bits) as u32) << 31 |
+ 0b11010 << 25 |
+ u32::from(op) << 24 |
+ truncate_imm::<_, 19>(offset) << 5 |
+ rt as u32).to_le_bytes()
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -1239,6 +1316,24 @@ mod tests {
}
#[test]
+ fn test_cbz() {
+ let offset = InstructionOffset::from_insns(-1);
+ check_bytes("e0ffffb4e0ffff34", |cb| {
+ cbz(cb, X0, offset);
+ cbz(cb, W0, offset);
+ });
+ }
+
+ #[test]
+ fn test_cbnz() {
+ let offset = InstructionOffset::from_insns(2);
+ check_bytes("540000b554000035", |cb| {
+ cbnz(cb, X20, offset);
+ cbnz(cb, W20, offset);
+ });
+ }
+
+ #[test]
fn test_brk_none() {
check_bytes("000020d4", |cb| brk(cb, A64Opnd::None));
}
@@ -1414,6 +1509,11 @@ mod tests {
}
#[test]
+ fn test_mul() {
+ check_bytes("6a7d0c9b", |cb| mul(cb, X10, X11, X12));
+ }
+
+ #[test]
fn test_mvn() {
check_bytes("ea032baa", |cb| mvn(cb, X10, X11));
}
diff --git a/yjit/src/asm/mod.rs b/yjit/src/asm/mod.rs
index 3c6c6e8e80..524d6341f5 100644
--- a/yjit/src/asm/mod.rs
+++ b/yjit/src/asm/mod.rs
@@ -6,6 +6,7 @@ use crate::core::IseqPayload;
use crate::core::for_each_off_stack_iseq_payload;
use crate::core::for_each_on_stack_iseq_payload;
use crate::invariants::rb_yjit_tracing_invalidate_all;
+use crate::stats::incr_counter;
use crate::virtualmem::WriteError;
#[cfg(feature = "disasm")]
@@ -58,6 +59,9 @@ pub struct CodeBlock {
// Current writing position
write_pos: usize,
+ // The index of the last page with written bytes
+ last_page_idx: usize,
+
// Total number of bytes written to past pages
past_page_bytes: usize,
@@ -118,6 +122,7 @@ impl CodeBlock {
mem_size,
page_size,
write_pos: 0,
+ last_page_idx: 0,
past_page_bytes: 0,
page_end_reserve: 0,
label_addrs: Vec::new(),
@@ -131,6 +136,10 @@ impl CodeBlock {
};
cb.page_end_reserve = cb.jmp_ptr_bytes();
cb.write_pos = cb.page_start();
+
+ #[cfg(not(test))]
+ assert_eq!(0, mem_size % page_size, "partially in-bounds code pages should be impossible");
+
cb
}
@@ -184,29 +193,32 @@ impl CodeBlock {
// but you need to waste some space for keeping write_pos for every single page.
// It doesn't seem necessary for performance either. So we're currently not doing it.
let dst_pos = self.get_page_pos(page_idx);
- if self.page_size * page_idx < self.mem_size && self.write_pos < dst_pos {
+ if self.write_pos < dst_pos {
+ // Fail if next page is out of bounds
+ if dst_pos >= self.mem_size {
+ return false;
+ }
+
// Reset dropped_bytes
self.dropped_bytes = false;
- // Convert dst_pos to dst_ptr
- let src_pos = self.write_pos;
- self.write_pos = dst_pos;
- let dst_ptr = self.get_write_ptr();
- self.write_pos = src_pos;
- self.without_page_end_reserve(|cb| assert!(cb.has_capacity(cb.jmp_ptr_bytes())));
-
// Generate jmp_ptr from src_pos to dst_pos
+ let dst_ptr = self.get_ptr(dst_pos);
self.without_page_end_reserve(|cb| {
+ assert!(cb.has_capacity(cb.jmp_ptr_bytes()));
cb.add_comment("jump to next page");
jmp_ptr(cb, dst_ptr);
- assert!(!cb.has_dropped_bytes());
});
- // Update past_page_bytes for code_size()
- self.past_page_bytes += self.current_page_bytes();
+ // Update past_page_bytes for code_size() if this is a new page
+ if self.last_page_idx < page_idx {
+ self.past_page_bytes += self.current_page_bytes();
+ }
// Start the next code from dst_pos
self.write_pos = dst_pos;
+ // Update the last_page_idx if page_idx points to the furthest page
+ self.last_page_idx = usize::max(self.last_page_idx, page_idx);
}
!self.dropped_bytes
}
@@ -314,13 +326,12 @@ impl CodeBlock {
}
/// Return the address ranges of a given address range that this CodeBlock can write.
- #[cfg(any(feature = "disasm", target_arch = "aarch64"))]
#[allow(dead_code)]
pub fn writable_addrs(&self, start_ptr: CodePtr, end_ptr: CodePtr) -> Vec<(usize, usize)> {
- let region_start = self.get_ptr(0).into_usize();
- let region_end = self.get_ptr(self.get_mem_size()).into_usize();
- let mut start = start_ptr.into_usize();
- let end = std::cmp::min(end_ptr.into_usize(), region_end);
+ let region_start = self.get_ptr(0).raw_addr(self);
+ let region_end = self.get_ptr(self.get_mem_size()).raw_addr(self);
+ let mut start = start_ptr.raw_addr(self);
+ let end = std::cmp::min(end_ptr.raw_addr(self), region_end);
let freed_pages = self.freed_pages.as_ref().as_ref();
let mut addrs = vec![];
@@ -358,7 +369,7 @@ impl CodeBlock {
/// If not, this becomes an inline no-op.
#[cfg(feature = "disasm")]
pub fn add_comment(&mut self, comment: &str) {
- let cur_ptr = self.get_write_ptr().into_usize();
+ let cur_ptr = self.get_write_ptr().raw_addr(self);
// If there's no current list of comments for this line number, add one.
let this_line_comments = self.asm_comments.entry(cur_ptr).or_default();
@@ -380,7 +391,7 @@ impl CodeBlock {
#[allow(unused_variables)]
#[cfg(feature = "disasm")]
pub fn remove_comments(&mut self, start_addr: CodePtr, end_addr: CodePtr) {
- for addr in start_addr.into_usize()..end_addr.into_usize() {
+ for addr in start_addr.raw_addr(self)..end_addr.raw_addr(self) {
self.asm_comments.remove(&addr);
}
}
@@ -416,8 +427,8 @@ impl CodeBlock {
// Set the current write position from a pointer
pub fn set_write_ptr(&mut self, code_ptr: CodePtr) {
- let pos = code_ptr.into_usize() - self.mem_block.borrow().start_ptr().into_usize();
- self.set_pos(pos);
+ let pos = code_ptr.as_offset() - self.mem_block.borrow().start_ptr().as_offset();
+ self.set_pos(pos.try_into().unwrap());
}
/// Get a (possibly dangling) direct pointer into the executable memory block
@@ -427,19 +438,19 @@ impl CodeBlock {
/// Convert an address range to memory page indexes against a num_pages()-sized array.
pub fn addrs_to_pages(&self, start_addr: CodePtr, end_addr: CodePtr) -> Vec<usize> {
- let mem_start = self.mem_block.borrow().start_ptr().into_usize();
- let mem_end = self.mem_block.borrow().mapped_end_ptr().into_usize();
- assert!(mem_start <= start_addr.into_usize());
- assert!(start_addr.into_usize() <= end_addr.into_usize());
- assert!(end_addr.into_usize() <= mem_end);
+ let mem_start = self.mem_block.borrow().start_ptr().raw_addr(self);
+ let mem_end = self.mem_block.borrow().mapped_end_ptr().raw_addr(self);
+ assert!(mem_start <= start_addr.raw_addr(self));
+ assert!(start_addr.raw_addr(self) <= end_addr.raw_addr(self));
+ assert!(end_addr.raw_addr(self) <= mem_end);
// Ignore empty code ranges
if start_addr == end_addr {
return vec![];
}
- let start_page = (start_addr.into_usize() - mem_start) / self.page_size;
- let end_page = (end_addr.into_usize() - mem_start - 1) / self.page_size;
+ let start_page = (start_addr.raw_addr(self) - mem_start) / self.page_size;
+ let end_page = (end_addr.raw_addr(self) - mem_start - 1) / self.page_size;
(start_page..=end_page).collect() // TODO: consider returning an iterator
}
@@ -652,7 +663,7 @@ impl CodeBlock {
ocb.unwrap().freed_pages = new_freed_pages;
assert_eq!(1, Rc::strong_count(&old_freed_pages)); // will deallocate
- CodegenGlobals::incr_code_gc_count();
+ incr_counter!(code_gc_count);
}
pub fn inline(&self) -> bool {
@@ -708,13 +719,20 @@ impl CodeBlock {
impl fmt::LowerHex for CodeBlock {
fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
for pos in 0..self.write_pos {
- let byte = unsafe { self.mem_block.borrow().start_ptr().raw_ptr().add(pos).read() };
+ let mem_block = &*self.mem_block.borrow();
+ let byte = unsafe { mem_block.start_ptr().raw_ptr(mem_block).add(pos).read() };
fmtr.write_fmt(format_args!("{:02x}", byte))?;
}
Ok(())
}
}
+impl crate::virtualmem::CodePtrBase for CodeBlock {
+ fn base_ptr(&self) -> std::ptr::NonNull<u8> {
+ self.mem_block.borrow().base_ptr()
+ }
+}
+
/// Wrapper struct so we can use the type system to distinguish
/// Between the inlined and outlined code blocks
pub struct OutlinedCb {
@@ -804,6 +822,7 @@ mod tests
#[test]
fn test_code_size() {
+ // Write 4 bytes in the first page
let mut cb = CodeBlock::new_dummy(CodeBlock::PREFERRED_CODE_PAGE_SIZE * 2);
cb.write_bytes(&[0, 0, 0, 0]);
assert_eq!(cb.code_size(), 4);
@@ -812,7 +831,18 @@ mod tests
cb.next_page(cb.get_write_ptr(), |_, _| {});
assert_eq!(cb.code_size(), 4);
+ // Write 4 bytes in the second page
cb.write_bytes(&[0, 0, 0, 0]);
assert_eq!(cb.code_size(), 8);
+
+ // Rewrite 4 bytes in the first page
+ let old_write_pos = cb.get_write_pos();
+ cb.set_pos(0);
+ cb.write_bytes(&[1, 1, 1, 1]);
+
+ // Moving from an old page to the next page should not increase code_size
+ cb.next_page(cb.get_write_ptr(), |_, _| {});
+ cb.set_pos(old_write_pos);
+ assert_eq!(cb.code_size(), 8);
}
}
diff --git a/yjit/src/asm/x86_64/mod.rs b/yjit/src/asm/x86_64/mod.rs
index 0bb1a6381f..fbbfa714d8 100644
--- a/yjit/src/asm/x86_64/mod.rs
+++ b/yjit/src/asm/x86_64/mod.rs
@@ -362,11 +362,6 @@ pub fn const_ptr_opnd(ptr: *const u8) -> X86Opnd
uimm_opnd(ptr as u64)
}
-pub fn code_ptr_opnd(code_ptr: CodePtr) -> X86Opnd
-{
- uimm_opnd(code_ptr.raw_ptr() as u64)
-}
-
/// Write the REX byte
fn write_rex(cb: &mut CodeBlock, w_flag: bool, reg_no: u8, idx_reg_no: u8, rm_reg_no: u8) {
// 0 1 0 0 w r x b
@@ -635,7 +630,7 @@ fn write_rm_multi(cb: &mut CodeBlock, op_mem_reg8: u8, op_mem_reg_pref: u8, op_r
panic!("immediate value too large (num_bits={}, num={uimm:?})", num_bits);
}
},
- _ => unreachable!()
+ _ => panic!("unknown encoding combo: {opnd0:?} {opnd1:?}")
};
}
@@ -696,7 +691,7 @@ pub fn call_ptr(cb: &mut CodeBlock, scratch_opnd: X86Opnd, dst_ptr: *const u8) {
let end_ptr = cb.get_ptr(cb.write_pos + 5);
// Compute the jump offset
- let rel64: i64 = dst_ptr as i64 - end_ptr.into_i64();
+ let rel64: i64 = dst_ptr as i64 - end_ptr.raw_ptr(cb) as i64;
// If the offset fits in 32-bit
if rel64 >= i32::MIN.into() && rel64 <= i32::MAX.into() {
@@ -805,6 +800,31 @@ pub fn cqo(cb: &mut CodeBlock) {
cb.write_bytes(&[0x48, 0x99]);
}
+/// imul - signed integer multiply
+pub fn imul(cb: &mut CodeBlock, opnd0: X86Opnd, opnd1: X86Opnd) {
+ assert!(opnd0.num_bits() == 64);
+ assert!(opnd1.num_bits() == 64);
+ assert!(matches!(opnd0, X86Opnd::Reg(_) | X86Opnd::Mem(_)));
+ assert!(matches!(opnd1, X86Opnd::Reg(_) | X86Opnd::Mem(_)));
+
+ match (opnd0, opnd1) {
+ (X86Opnd::Reg(_), X86Opnd::Reg(_) | X86Opnd::Mem(_)) => {
+ //REX.W + 0F AF /rIMUL r64, r/m64
+ // Quadword register := Quadword register * r/m64.
+ write_rm(cb, false, true, opnd0, opnd1, None, &[0x0F, 0xAF]);
+ }
+
+ // Flip the operands to handle this case. This instruction has weird encoding restrictions.
+ (X86Opnd::Mem(_), X86Opnd::Reg(_)) => {
+ //REX.W + 0F AF /rIMUL r64, r/m64
+ // Quadword register := Quadword register * r/m64.
+ write_rm(cb, false, true, opnd1, opnd0, None, &[0x0F, 0xAF]);
+ }
+
+ _ => unreachable!()
+ }
+}
+
/// Interrupt 3 - trap to debugger
pub fn int3(cb: &mut CodeBlock) {
cb.write_byte(0xcc);
@@ -872,7 +892,7 @@ fn write_jcc_ptr(cb: &mut CodeBlock, op0: u8, op1: u8, dst_ptr: CodePtr) {
let end_ptr = cb.get_ptr(cb.write_pos + 4);
// Compute the jump offset
- let rel64 = dst_ptr.into_i64() - end_ptr.into_i64();
+ let rel64 = dst_ptr.as_offset() - end_ptr.as_offset();
if rel64 >= i32::MIN.into() && rel64 <= i32::MAX.into() {
// Write the relative 32-bit jump offset
@@ -932,6 +952,7 @@ pub fn jmp32(cb: &mut CodeBlock, offset: i32) {
pub fn lea(cb: &mut CodeBlock, dst: X86Opnd, src: X86Opnd) {
if let X86Opnd::Reg(reg) = dst {
assert!(reg.num_bits == 64);
+ assert!(matches!(src, X86Opnd::Mem(_) | X86Opnd::IPRel(_)));
write_rm(cb, false, true, dst, src, None, &[0x8d]);
} else {
unreachable!();
@@ -1223,8 +1244,8 @@ pub fn ret(cb: &mut CodeBlock) {
cb.write_byte(0xC3);
}
-// Encode a single-operand shift instruction
-fn write_shift(cb: &mut CodeBlock, op_mem_one_pref: u8, _op_mem_cl_pref: u8, op_mem_imm_pref: u8, op_ext: Option<u8>, opnd0: X86Opnd, opnd1: X86Opnd) {
+// Encode a bitwise shift instruction
+fn write_shift(cb: &mut CodeBlock, op_mem_one_pref: u8, op_mem_cl_pref: u8, op_mem_imm_pref: u8, op_ext: u8, opnd0: X86Opnd, opnd1: X86Opnd) {
assert!(matches!(opnd0, X86Opnd::Reg(_) | X86Opnd::Mem(_)));
// Check the size of opnd0
@@ -1234,16 +1255,26 @@ fn write_shift(cb: &mut CodeBlock, op_mem_one_pref: u8, _op_mem_cl_pref: u8, op_
let sz_pref = opnd_size == 16;
let rex_w = opnd_size == 64;
- if let X86Opnd::UImm(imm) = opnd1 {
- if imm.value == 1 {
- write_rm(cb, sz_pref, rex_w, X86Opnd::None, opnd0, op_ext, &[op_mem_one_pref]);
- } else {
- assert!(imm.num_bits <= 8);
- write_rm(cb, sz_pref, rex_w, X86Opnd::None, opnd0, op_ext, &[op_mem_imm_pref]);
- cb.write_byte(imm.value as u8);
+ match opnd1 {
+ X86Opnd::UImm(imm) => {
+ if imm.value == 1 {
+ write_rm(cb, sz_pref, rex_w, X86Opnd::None, opnd0, Some(op_ext), &[op_mem_one_pref]);
+ } else {
+ assert!(imm.num_bits <= 8);
+ write_rm(cb, sz_pref, rex_w, X86Opnd::None, opnd0, Some(op_ext), &[op_mem_imm_pref]);
+ cb.write_byte(imm.value as u8);
+ }
+ }
+
+ X86Opnd::Reg(reg) => {
+ // We can only use CL/RCX as the shift amount
+ assert!(reg.reg_no == RCX_REG.reg_no);
+ write_rm(cb, sz_pref, rex_w, X86Opnd::None, opnd0, Some(op_ext), &[op_mem_cl_pref]);
+ }
+
+ _ => {
+ unreachable!("unsupported operands: {:?}, {:?}", opnd0, opnd1);
}
- } else {
- unreachable!();
}
}
@@ -1254,7 +1285,7 @@ pub fn sal(cb: &mut CodeBlock, opnd0: X86Opnd, opnd1: X86Opnd) {
0xD1, // opMemOnePref,
0xD3, // opMemClPref,
0xC1, // opMemImmPref,
- Some(0x04),
+ 0x04,
opnd0,
opnd1
);
@@ -1267,7 +1298,7 @@ pub fn sar(cb: &mut CodeBlock, opnd0: X86Opnd, opnd1: X86Opnd) {
0xD1, // opMemOnePref,
0xD3, // opMemClPref,
0xC1, // opMemImmPref,
- Some(0x07),
+ 0x07,
opnd0,
opnd1
);
@@ -1280,7 +1311,7 @@ pub fn shl(cb: &mut CodeBlock, opnd0: X86Opnd, opnd1: X86Opnd) {
0xD1, // opMemOnePref,
0xD3, // opMemClPref,
0xC1, // opMemImmPref,
- Some(0x04),
+ 0x04,
opnd0,
opnd1
);
@@ -1293,7 +1324,7 @@ pub fn shr(cb: &mut CodeBlock, opnd0: X86Opnd, opnd1: X86Opnd) {
0xD1, // opMemOnePref,
0xD3, // opMemClPref,
0xC1, // opMemImmPref,
- Some(0x05),
+ 0x05,
opnd0,
opnd1
);
diff --git a/yjit/src/asm/x86_64/tests.rs b/yjit/src/asm/x86_64/tests.rs
index 2b1775452e..5ae983270f 100644
--- a/yjit/src/asm/x86_64/tests.rs
+++ b/yjit/src/asm/x86_64/tests.rs
@@ -68,7 +68,7 @@ fn test_call_ptr() {
// calling a lower address
check_bytes("e8fbffffff", |cb| {
let ptr = cb.get_write_ptr();
- call_ptr(cb, RAX, ptr.raw_ptr());
+ call_ptr(cb, RAX, ptr.raw_ptr(cb));
});
}
@@ -106,6 +106,15 @@ fn test_cqo() {
}
#[test]
+fn test_imul() {
+ check_bytes("480fafc3", |cb| imul(cb, RAX, RBX));
+ check_bytes("480faf10", |cb| imul(cb, RDX, mem_opnd(64, RAX, 0)));
+
+ // Operands flipped for encoding since multiplication is commutative
+ check_bytes("480faf10", |cb| imul(cb, mem_opnd(64, RAX, 0), RDX));
+}
+
+#[test]
fn test_jge_label() {
check_bytes("0f8dfaffffff", |cb| {
let label_idx = cb.new_label("loop".to_owned());
@@ -340,6 +349,7 @@ fn test_sal() {
check_bytes("d1e1", |cb| sal(cb, ECX, uimm_opnd(1)));
check_bytes("c1e505", |cb| sal(cb, EBP, uimm_opnd(5)));
check_bytes("d1642444", |cb| sal(cb, mem_opnd(32, RSP, 68), uimm_opnd(1)));
+ check_bytes("48d3e1", |cb| sal(cb, RCX, CL));
}
#[test]
@@ -432,15 +442,15 @@ fn basic_capstone_usage() -> std::result::Result<(), capstone::Error> {
fn block_comments() {
let mut cb = super::CodeBlock::new_dummy(4096);
- let first_write_ptr = cb.get_write_ptr().into_usize();
+ let first_write_ptr = cb.get_write_ptr().raw_addr(&cb);
cb.add_comment("Beginning");
xor(&mut cb, EAX, EAX); // 2 bytes long
- let second_write_ptr = cb.get_write_ptr().into_usize();
+ let second_write_ptr = cb.get_write_ptr().raw_addr(&cb);
cb.add_comment("Two bytes in");
cb.add_comment("Still two bytes in");
cb.add_comment("Still two bytes in"); // Duplicate, should be ignored
test(&mut cb, mem_opnd(64, RSI, 64), imm_opnd(!0x08)); // 8 bytes long
- let third_write_ptr = cb.get_write_ptr().into_usize();
+ let third_write_ptr = cb.get_write_ptr().raw_addr(&cb);
cb.add_comment("Ten bytes in");
assert_eq!(&vec!( "Beginning".to_string() ), cb.comments_at(first_write_ptr).unwrap());
diff --git a/yjit/src/backend/arm64/mod.rs b/yjit/src/backend/arm64/mod.rs
index d3a386d42e..3bf949ba7d 100644
--- a/yjit/src/backend/arm64/mod.rs
+++ b/yjit/src/backend/arm64/mod.rs
@@ -1,18 +1,11 @@
-#![allow(dead_code)]
-#![allow(unused_variables)]
-#![allow(unused_imports)]
-
use std::mem::take;
-use crate::asm::x86_64::jmp_ptr;
use crate::asm::{CodeBlock, OutlinedCb};
use crate::asm::arm64::*;
-use crate::codegen::{JITState, CodegenGlobals};
-use crate::core::Context;
use crate::cruby::*;
use crate::backend::ir::*;
use crate::virtualmem::CodePtr;
-use crate::options::*;
+use crate::utils::*;
// Use the arm64 register type for this platform
pub type Reg = A64Reg;
@@ -74,6 +67,7 @@ impl From<Opnd> for A64Opnd {
Opnd::Mem(Mem { base: MemBase::InsnOut(_), .. }) => {
panic!("attempted to lower an Opnd::Mem with a MemBase::InsnOut base")
},
+ Opnd::CArg(_) => panic!("attempted to lower an Opnd::CArg"),
Opnd::InsnOut { .. } => panic!("attempted to lower an Opnd::InsnOut"),
Opnd::Value(_) => panic!("attempted to lower an Opnd::Value"),
Opnd::Stack { .. } => panic!("attempted to lower an Opnd::Stack"),
@@ -104,14 +98,13 @@ fn emit_jmp_ptr_with_invalidation(cb: &mut CodeBlock, dst_ptr: CodePtr) {
#[cfg(not(test))]
{
let end = cb.get_write_ptr();
- use crate::cruby::rb_yjit_icache_invalidate;
- unsafe { rb_yjit_icache_invalidate(start.raw_ptr() as _, end.raw_ptr() as _) };
+ unsafe { rb_yjit_icache_invalidate(start.raw_ptr(cb) as _, end.raw_ptr(cb) as _) };
}
}
fn emit_jmp_ptr(cb: &mut CodeBlock, dst_ptr: CodePtr, padding: bool) {
- let src_addr = cb.get_write_ptr().into_i64();
- let dst_addr = dst_ptr.into_i64();
+ let src_addr = cb.get_write_ptr().as_offset();
+ let dst_addr = dst_ptr.as_offset();
// If the offset is short enough, then we'll use the
// branch instruction. Otherwise, we'll move the
@@ -183,17 +176,24 @@ fn emit_load_value(cb: &mut CodeBlock, rd: A64Opnd, value: u64) -> usize {
}
}
+/// List of registers that can be used for stack temps.
+/// These are caller-saved registers.
+pub static TEMP_REGS: [Reg; 5] = [X1_REG, X9_REG, X10_REG, X14_REG, X15_REG];
+
+#[derive(Debug, PartialEq)]
+enum EmitError {
+ RetryOnNextPage,
+ OutOfMemory,
+}
+
impl Assembler
{
- // A special scratch register for intermediate processing.
+ // Special scratch registers for intermediate processing.
// This register is caller-saved (so we don't have to save it before using it)
- const SCRATCH0: A64Opnd = A64Opnd::Reg(X16_REG);
+ pub const SCRATCH_REG: Reg = X16_REG;
+ const SCRATCH0: A64Opnd = A64Opnd::Reg(Assembler::SCRATCH_REG);
const SCRATCH1: A64Opnd = A64Opnd::Reg(X17_REG);
- /// List of registers that can be used for stack temps.
- /// These are caller-saved registers.
- pub const TEMP_REGS: [Reg; 5] = [X1_REG, X9_REG, X10_REG, X14_REG, X15_REG];
-
/// Get the list of registers from which we will allocate on this platform
/// These are caller-saved registers
/// Note: we intentionally exclude C_RET_REG (X0) from this list
@@ -280,7 +280,7 @@ impl Assembler
/// do follow that encoding, and if they don't then we load them first.
fn split_bitmask_immediate(asm: &mut Assembler, opnd: Opnd, dest_num_bits: u8) -> Opnd {
match opnd {
- Opnd::Reg(_) | Opnd::InsnOut { .. } | Opnd::Stack { .. } => opnd,
+ Opnd::Reg(_) | Opnd::CArg(_) | Opnd::InsnOut { .. } | Opnd::Stack { .. } => opnd,
Opnd::Mem(_) => split_load_operand(asm, opnd),
Opnd::Imm(imm) => {
if imm == 0 {
@@ -313,9 +313,13 @@ impl Assembler
/// a certain size. If they don't then we need to load them first.
fn split_shifted_immediate(asm: &mut Assembler, opnd: Opnd) -> Opnd {
match opnd {
- Opnd::Reg(_) | Opnd::InsnOut { .. } => opnd,
+ Opnd::Reg(_) | Opnd::CArg(_) | Opnd::InsnOut { .. } => opnd,
Opnd::Mem(_) => split_load_operand(asm, opnd),
- Opnd::Imm(_) => asm.load(opnd),
+ Opnd::Imm(imm) => if ShiftedImmediate::try_from(imm as u64).is_ok() {
+ opnd
+ } else {
+ asm.load(opnd)
+ }
Opnd::UImm(uimm) => {
if ShiftedImmediate::try_from(uimm).is_ok() {
opnd
@@ -427,14 +431,62 @@ impl Assembler
}
}
},
- Insn::And { left, right, .. } |
- Insn::Or { left, right, .. } |
- Insn::Xor { left, right, .. } => {
+ Insn::And { left, right, out } |
+ Insn::Or { left, right, out } |
+ Insn::Xor { left, right, out } => {
let (opnd0, opnd1) = split_boolean_operands(asm, *left, *right);
*left = opnd0;
*right = opnd1;
+
+ // Since these instructions are lowered to an instruction that have 2 input
+ // registers and an output register, look to merge with an `Insn::Mov` that
+ // follows which puts the output in another register. For example:
+ // `Add a, b => out` followed by `Mov c, out` becomes `Add a, b => c`.
+ if let (Opnd::Reg(_), Opnd::Reg(_), Some(Insn::Mov { dest, src })) = (left, right, iterator.peek()) {
+ if live_ranges[index] == index + 1 {
+ // Check after potentially lowering a stack operand to a register operand
+ let lowered_dest = if let Opnd::Stack { .. } = dest {
+ asm.lower_stack_opnd(dest)
+ } else {
+ *dest
+ };
+ if out == src && matches!(lowered_dest, Opnd::Reg(_)) {
+ *out = lowered_dest;
+ iterator.map_insn_index(asm);
+ iterator.next_unmapped(); // Pop merged Insn::Mov
+ }
+ }
+ }
+
asm.push_insn(insn);
- },
+ }
+ // Lower to Joz and Jonz for generating CBZ/CBNZ for compare-with-0-and-branch.
+ ref insn @ Insn::Cmp { ref left, right: ref right @ (Opnd::UImm(0) | Opnd::Imm(0)) } |
+ ref insn @ Insn::Test { ref left, right: ref right @ (Opnd::InsnOut { .. } | Opnd::Reg(_)) } if {
+ let same_opnd_if_test = if let Insn::Test { .. } = insn {
+ left == right
+ } else {
+ true
+ };
+
+ same_opnd_if_test && if let Some(
+ Insn::Jz(target) | Insn::Je(target) | Insn::Jnz(target) | Insn::Jne(target)
+ ) = iterator.peek() {
+ matches!(target, Target::SideExit { .. })
+ } else {
+ false
+ }
+ } => {
+ let reg = split_load_operand(asm, *left);
+ match iterator.peek() {
+ Some(Insn::Jz(target) | Insn::Je(target)) => asm.push_insn(Insn::Joz(reg, *target)),
+ Some(Insn::Jnz(target) | Insn::Jne(target)) => asm.push_insn(Insn::Jonz(reg, *target)),
+ _ => ()
+ }
+
+ iterator.map_insn_index(asm);
+ iterator.next_unmapped(); // Pop merged jump instruction
+ }
Insn::CCall { opnds, fptr, .. } => {
assert!(opnds.len() <= C_ARG_OPNDS.len());
@@ -452,7 +504,7 @@ impl Assembler
_ => *opnd
};
- asm.load_into(C_ARG_OPNDS[idx], value);
+ asm.load_into(Opnd::c_arg(C_ARG_OPNDS[idx]), value);
}
// Now we push the CCall without any arguments so that it
@@ -564,6 +616,7 @@ impl Assembler
// If we're attempting to load into a memory operand, then
// we'll switch over to the store instruction.
(Opnd::Mem(_), _) => {
+ let opnd0 = split_memory_address(asm, *dest);
let value = match *src {
// If the first operand is zero, then we can just use
// the zero register.
@@ -579,7 +632,6 @@ impl Assembler
_ => split_bitmask_immediate(asm, *src, dest.rm_num_bits())
};
- let opnd0 = split_memory_address(asm, *dest);
asm.store(opnd0, value);
},
// If we're loading a memory operand into a register, then
@@ -610,6 +662,19 @@ impl Assembler
asm.not(opnd0);
},
+ Insn::LShift { opnd, .. } |
+ Insn::RShift { opnd, .. } |
+ Insn::URShift { opnd, .. } => {
+ // The operand must be in a register, so
+ // if we get anything else we need to load it first.
+ let opnd0 = match opnd {
+ Opnd::Mem(_) => split_load_operand(asm, *opnd),
+ _ => *opnd
+ };
+
+ *opnd = opnd0;
+ asm.push_insn(insn);
+ },
Insn::Store { dest, src } => {
// The value being stored must be in a register, so if it's
// not already one we'll load it first.
@@ -640,6 +705,11 @@ impl Assembler
let opnd1 = split_shifted_immediate(asm, *right);
asm.sub(opnd0, opnd1);
},
+ Insn::Mul { left, right, .. } => {
+ let opnd0 = split_load_operand(asm, *left);
+ let opnd1 = split_load_operand(asm, *right);
+ asm.mul(opnd0, opnd1);
+ },
Insn::Test { left, right } => {
// The value being tested must be in a register, so if it's
// not already one we'll load it first.
@@ -673,7 +743,7 @@ impl Assembler
/// Emit platform-specific machine code
/// Returns a list of GC offsets. Can return failure to signal caller to retry.
- fn arm64_emit(&mut self, cb: &mut CodeBlock, ocb: &mut Option<&mut OutlinedCb>) -> Result<Vec<u32>, ()> {
+ fn arm64_emit(&mut self, cb: &mut CodeBlock, ocb: &mut Option<&mut OutlinedCb>) -> Result<Vec<u32>, EmitError> {
/// Determine how many instructions it will take to represent moving
/// this value into a register. Note that the return value of this
/// function must correspond to how many instructions are used to
@@ -699,8 +769,8 @@ impl Assembler
fn emit_conditional_jump<const CONDITION: u8>(cb: &mut CodeBlock, target: Target) {
match target {
Target::CodePtr(dst_ptr) | Target::SideExitPtr(dst_ptr) => {
- let dst_addr = dst_ptr.into_i64();
- let src_addr = cb.get_write_ptr().into_i64();
+ let dst_addr = dst_ptr.as_offset();
+ let src_addr = cb.get_write_ptr().as_offset();
let num_insns = if bcond_offset_fits_bits((dst_addr - src_addr) / 4) {
// If the jump offset fits into the conditional jump as
@@ -729,7 +799,7 @@ impl Assembler
} else {
// Otherwise, we need to load the address into a
// register and use the branch register instruction.
- let dst_addr = dst_ptr.into_u64();
+ let dst_addr = (dst_ptr.raw_ptr(cb) as usize).as_u64();
let load_insns: i32 = emit_load_size(dst_addr).into();
// We're going to write out the inverse condition so
@@ -769,6 +839,45 @@ impl Assembler
};
}
+ /// Emit a CBZ or CBNZ which branches when a register is zero or non-zero
+ fn emit_cmp_zero_jump(cb: &mut CodeBlock, reg: A64Opnd, branch_if_zero: bool, target: Target) {
+ if let Target::SideExitPtr(dst_ptr) = target {
+ let dst_addr = dst_ptr.as_offset();
+ let src_addr = cb.get_write_ptr().as_offset();
+
+ if cmp_branch_offset_fits_bits((dst_addr - src_addr) / 4) {
+ // If the offset fits in one instruction, generate cbz or cbnz
+ let bytes = (dst_addr - src_addr) as i32;
+ if branch_if_zero {
+ cbz(cb, reg, InstructionOffset::from_bytes(bytes));
+ } else {
+ cbnz(cb, reg, InstructionOffset::from_bytes(bytes));
+ }
+ } else {
+ // Otherwise, we load the address into a register and
+ // use the branch register instruction. Note that because
+ // side exits should always be close, this form should be
+ // rare or impossible to see.
+ let dst_addr = dst_ptr.raw_addr(cb) as u64;
+ let load_insns: i32 = emit_load_size(dst_addr).into();
+
+ // Write out the inverse condition so that if
+ // it doesn't match it will skip over the
+ // instructions used for branching.
+ if branch_if_zero {
+ cbnz(cb, reg, InstructionOffset::from_insns(load_insns + 2));
+ } else {
+ cbz(cb, reg, InstructionOffset::from_insns(load_insns + 2));
+ }
+ emit_load_value(cb, Assembler::SCRATCH0, dst_addr);
+ br(cb, Assembler::SCRATCH0);
+
+ }
+ } else {
+ unreachable!("We should only generate Joz/Jonz with side-exit targets");
+ }
+ }
+
/// Emit a push instruction for the given operand by adding to the stack
/// pointer and then storing the given value.
fn emit_push(cb: &mut CodeBlock, opnd: A64Opnd) {
@@ -786,12 +895,13 @@ impl Assembler
target: Target,
asm: &mut Assembler,
ocb: &mut Option<&mut OutlinedCb>,
- ) -> Target {
+ ) -> Result<Target, EmitError> {
if let Target::SideExit { counter, context } = target {
- let side_exit = asm.get_side_exit(&context.unwrap(), Some(counter), ocb.as_mut().unwrap());
- Target::SideExitPtr(side_exit)
+ let side_exit = asm.get_side_exit(&context.unwrap(), Some(counter), ocb.as_mut().unwrap())
+ .ok_or(EmitError::OutOfMemory)?;
+ Ok(Target::SideExitPtr(side_exit))
} else {
- target
+ Ok(target)
}
}
@@ -800,6 +910,9 @@ impl Assembler
// List of GC offsets
let mut gc_offsets: Vec<u32> = Vec::new();
+ // Buffered list of PosMarker callbacks to fire if codegen is successful
+ let mut pos_markers: Vec<(usize, CodePtr)> = vec![];
+
// For each instruction
let start_write_pos = cb.get_write_pos();
let mut insn_idx: usize = 0;
@@ -819,8 +932,8 @@ impl Assembler
cb.write_label(target.unwrap_label_idx());
},
// Report back the current position in the generated code
- Insn::PosMarker(pos_marker) => {
- pos_marker(cb.get_write_ptr());
+ Insn::PosMarker(..) => {
+ pos_markers.push((insn_idx, cb.get_write_ptr()))
}
Insn::BakeString(text) => {
for byte in text.as_bytes() {
@@ -837,9 +950,6 @@ impl Assembler
cb.write_byte(0);
}
},
- Insn::Add { left, right, out } => {
- adds(cb, out.into(), left.into(), right.into());
- },
Insn::FrameSetup => {
stp_pre(cb, X29, X30, A64Opnd::new_mem(128, C_SP_REG, -16));
@@ -852,9 +962,39 @@ impl Assembler
ldp_post(cb, X29, X30, A64Opnd::new_mem(128, C_SP_REG, 16));
},
+ Insn::Add { left, right, out } => {
+ adds(cb, out.into(), left.into(), right.into());
+ },
Insn::Sub { left, right, out } => {
subs(cb, out.into(), left.into(), right.into());
},
+ Insn::Mul { left, right, out } => {
+ // If the next instruction is jo (jump on overflow)
+ match (self.insns.get(insn_idx + 1), self.insns.get(insn_idx + 2)) {
+ (Some(Insn::JoMul(_)), _) |
+ (Some(Insn::PosMarker(_)), Some(Insn::JoMul(_))) => {
+ // Compute the high 64 bits
+ smulh(cb, Self::SCRATCH0, left.into(), right.into());
+
+ // Compute the low 64 bits
+ // This may clobber one of the input registers,
+ // so we do it after smulh
+ mul(cb, out.into(), left.into(), right.into());
+
+ // Produce a register that is all zeros or all ones
+ // Based on the sign bit of the 64-bit mul result
+ asr(cb, Self::SCRATCH1, out.into(), A64Opnd::UImm(63));
+
+ // If the high 64-bits are not all zeros or all ones,
+ // matching the sign bit, then we have an overflow
+ cmp(cb, Self::SCRATCH0, Self::SCRATCH1);
+ // Insn::JoMul will emit_conditional_jump::<{Condition::NE}>
+ }
+ _ => {
+ mul(cb, out.into(), left.into(), right.into());
+ }
+ }
+ },
Insn::And { left, right, out } => {
and(cb, out.into(), left.into(), right.into());
},
@@ -924,6 +1064,9 @@ impl Assembler
let ptr_offset: u32 = (cb.get_write_pos() as u32) - (SIZEOF_VALUE as u32);
insn_gc_offsets.push(ptr_offset);
},
+ Opnd::CArg { .. } => {
+ unreachable!("C argument operand was not lowered before arm64_emit");
+ }
Opnd::Stack { .. } => {
unreachable!("Stack operand was not lowered before arm64_emit");
}
@@ -975,14 +1118,20 @@ impl Assembler
}
};
},
- Insn::LeaLabel { out, target, .. } => {
- let label_idx = target.unwrap_label_idx();
-
- cb.label_ref(label_idx, 4, |cb, end_addr, dst_addr| {
- adr(cb, Self::SCRATCH0, A64Opnd::new_imm(dst_addr - (end_addr - 4)));
- });
+ Insn::LeaJumpTarget { out, target, .. } => {
+ if let Target::Label(label_idx) = target {
+ // Set output to the raw address of the label
+ cb.label_ref(*label_idx, 4, |cb, end_addr, dst_addr| {
+ adr(cb, Self::SCRATCH0, A64Opnd::new_imm(dst_addr - (end_addr - 4)));
+ });
- mov(cb, out.into(), Self::SCRATCH0);
+ mov(cb, out.into(), Self::SCRATCH0);
+ } else {
+ // Set output to the jump target's raw address
+ let target_code = target.unwrap_code_ptr();
+ let target_addr = target_code.raw_addr(cb).as_u64();
+ emit_load_value(cb, out.into(), target_addr);
+ }
},
Insn::CPush(opnd) => {
emit_push(cb, opnd.into());
@@ -1017,7 +1166,7 @@ impl Assembler
},
Insn::CCall { fptr, .. } => {
// The offset to the call target in bytes
- let src_addr = cb.get_write_ptr().into_i64();
+ let src_addr = cb.get_write_ptr().raw_ptr(cb) as i64;
let dst_addr = *fptr as i64;
// Use BL if the offset is short enough to encode as an immediate.
@@ -1042,7 +1191,7 @@ impl Assembler
br(cb, opnd.into());
},
Insn::Jmp(target) => {
- match compile_side_exit(*target, self, ocb) {
+ match compile_side_exit(*target, self, ocb)? {
Target::CodePtr(dst_ptr) => {
emit_jmp_ptr(cb, dst_ptr, true);
},
@@ -1066,19 +1215,34 @@ impl Assembler
};
},
Insn::Je(target) | Insn::Jz(target) => {
- emit_conditional_jump::<{Condition::EQ}>(cb, compile_side_exit(*target, self, ocb));
+ emit_conditional_jump::<{Condition::EQ}>(cb, compile_side_exit(*target, self, ocb)?);
},
- Insn::Jne(target) | Insn::Jnz(target) => {
- emit_conditional_jump::<{Condition::NE}>(cb, compile_side_exit(*target, self, ocb));
+ Insn::Jne(target) | Insn::Jnz(target) | Insn::JoMul(target) => {
+ emit_conditional_jump::<{Condition::NE}>(cb, compile_side_exit(*target, self, ocb)?);
},
Insn::Jl(target) => {
- emit_conditional_jump::<{Condition::LT}>(cb, compile_side_exit(*target, self, ocb));
+ emit_conditional_jump::<{Condition::LT}>(cb, compile_side_exit(*target, self, ocb)?);
+ },
+ Insn::Jg(target) => {
+ emit_conditional_jump::<{Condition::GT}>(cb, compile_side_exit(*target, self, ocb)?);
+ },
+ Insn::Jge(target) => {
+ emit_conditional_jump::<{Condition::GE}>(cb, compile_side_exit(*target, self, ocb)?);
},
Insn::Jbe(target) => {
- emit_conditional_jump::<{Condition::LS}>(cb, compile_side_exit(*target, self, ocb));
+ emit_conditional_jump::<{Condition::LS}>(cb, compile_side_exit(*target, self, ocb)?);
+ },
+ Insn::Jb(target) => {
+ emit_conditional_jump::<{Condition::CC}>(cb, compile_side_exit(*target, self, ocb)?);
},
Insn::Jo(target) => {
- emit_conditional_jump::<{Condition::VS}>(cb, compile_side_exit(*target, self, ocb));
+ emit_conditional_jump::<{Condition::VS}>(cb, compile_side_exit(*target, self, ocb)?);
+ },
+ Insn::Joz(opnd, target) => {
+ emit_cmp_zero_jump(cb, opnd.into(), true, compile_side_exit(*target, self, ocb)?);
+ },
+ Insn::Jonz(opnd, target) => {
+ emit_cmp_zero_jump(cb, opnd.into(), false, compile_side_exit(*target, self, ocb)?);
},
Insn::IncrCounter { mem, value } => {
let label = cb.new_label("incr_counter_loop".to_string());
@@ -1136,7 +1300,7 @@ impl Assembler
// We don't want label references to cross page boundaries. Signal caller for
// retry.
if !self.label_names.is_empty() {
- return Err(());
+ return Err(EmitError::RetryOnNextPage);
}
} else {
insn_idx += 1;
@@ -1144,11 +1308,25 @@ impl Assembler
}
}
- Ok(gc_offsets)
+ // Error if we couldn't write out everything
+ if cb.has_dropped_bytes() {
+ return Err(EmitError::OutOfMemory)
+ } else {
+ // No bytes dropped, so the pos markers point to valid code
+ for (insn_idx, pos) in pos_markers {
+ if let Insn::PosMarker(callback) = self.insns.get(insn_idx).unwrap() {
+ callback(pos, &cb);
+ } else {
+ panic!("non-PosMarker in pos_markers insn_idx={insn_idx} {self:?}");
+ }
+ }
+
+ return Ok(gc_offsets)
+ }
}
/// Optimize and compile the stored instructions
- pub fn compile_with_regs(self, cb: &mut CodeBlock, ocb: Option<&mut OutlinedCb>, regs: Vec<Reg>) -> Vec<u32> {
+ pub fn compile_with_regs(self, cb: &mut CodeBlock, ocb: Option<&mut OutlinedCb>, regs: Vec<Reg>) -> Option<(CodePtr, Vec<u32>)> {
let asm = self.arm64_split();
let mut asm = asm.alloc_regs(regs);
@@ -1161,19 +1339,25 @@ impl Assembler
let start_ptr = cb.get_write_ptr();
let starting_label_state = cb.get_label_state();
let mut ocb = ocb; // for &mut
- let gc_offsets = asm.arm64_emit(cb, &mut ocb)
- .unwrap_or_else(|_err| {
+ let emit_result = match asm.arm64_emit(cb, &mut ocb) {
+ Err(EmitError::RetryOnNextPage) => {
// we want to lower jumps to labels to b.cond instructions, which have a 1 MiB
// range limit. We can easily exceed the limit in case the jump straddles two pages.
// In this case, we retry with a fresh page.
cb.set_label_state(starting_label_state);
cb.next_page(start_ptr, emit_jmp_ptr_with_invalidation);
- asm.arm64_emit(cb, &mut ocb).expect("should not fail when writing to a fresh code page")
- });
+ let result = asm.arm64_emit(cb, &mut ocb);
+ assert_ne!(
+ Err(EmitError::RetryOnNextPage),
+ result,
+ "should not fail when writing to a fresh code page"
+ );
+ result
+ }
+ result => result
+ };
- if cb.has_dropped_bytes() {
- cb.clear_labels();
- } else {
+ if let (Ok(gc_offsets), false) = (emit_result, cb.has_dropped_bytes()) {
cb.link_labels();
// Invalidate icache for newly written out region so we don't run stale code.
@@ -1185,9 +1369,13 @@ impl Assembler
unsafe { rb_yjit_icache_invalidate(start as _, end as _) };
}
});
- }
- gc_offsets
+ Some((start_ptr, gc_offsets))
+ } else {
+ cb.clear_labels();
+
+ None
+ }
}
}
@@ -1253,8 +1441,7 @@ mod tests {
fn test_emit_je_fits_into_bcond() {
let (mut asm, mut cb) = setup_asm();
- let offset = 80;
- let target: CodePtr = ((cb.get_write_ptr().into_u64() + offset) as *mut u8).into();
+ let target: CodePtr = cb.get_write_ptr().add_bytes(80);
asm.je(Target::CodePtr(target));
asm.compile_with_num_regs(&mut cb, 0);
@@ -1265,7 +1452,7 @@ mod tests {
let (mut asm, mut cb) = setup_asm();
let offset = 1 << 21;
- let target: CodePtr = ((cb.get_write_ptr().into_u64() + offset) as *mut u8).into();
+ let target: CodePtr = cb.get_write_ptr().add_bytes(offset);
asm.je(Target::CodePtr(target));
asm.compile_with_num_regs(&mut cb, 0);
@@ -1276,7 +1463,7 @@ mod tests {
let (mut asm, mut cb) = setup_asm();
let label = asm.new_label("label");
- let opnd = asm.lea_label(label);
+ let opnd = asm.lea_jump_target(label);
asm.write_label(label);
asm.bake_string("Hello, world!");
@@ -1526,7 +1713,7 @@ mod tests {
assert!(gap > 0b1111111111111111111);
let instruction_at_starting_pos: [u8; 4] = unsafe {
- std::slice::from_raw_parts(cb.get_ptr(starting_pos).raw_ptr(), 4)
+ std::slice::from_raw_parts(cb.get_ptr(starting_pos).raw_ptr(&cb), 4)
}.try_into().unwrap();
assert_eq!(
0b000101 << 26_u32,
@@ -1578,7 +1765,7 @@ mod tests {
fn test_replace_mov_with_ldur() {
let (mut asm, mut cb) = setup_asm();
- asm.mov(Opnd::Reg(Assembler::TEMP_REGS[0]), Opnd::mem(64, CFP, 8));
+ asm.mov(Opnd::Reg(TEMP_REGS[0]), Opnd::mem(64, CFP, 8));
asm.compile_with_num_regs(&mut cb, 1);
assert_disasm!(cb, "618240f8", {"
@@ -1590,8 +1777,8 @@ mod tests {
fn test_not_split_mov() {
let (mut asm, mut cb) = setup_asm();
- asm.mov(Opnd::Reg(Assembler::TEMP_REGS[0]), Opnd::UImm(0xffff));
- asm.mov(Opnd::Reg(Assembler::TEMP_REGS[0]), Opnd::UImm(0x10000));
+ asm.mov(Opnd::Reg(TEMP_REGS[0]), Opnd::UImm(0xffff));
+ asm.mov(Opnd::Reg(TEMP_REGS[0]), Opnd::UImm(0x10000));
asm.compile_with_num_regs(&mut cb, 1);
assert_disasm!(cb, "e1ff9fd2e10370b2", {"
@@ -1605,7 +1792,7 @@ mod tests {
let (mut asm, mut cb) = setup_asm();
let out = asm.csel_l(Qtrue.into(), Qfalse.into());
- asm.mov(Opnd::Reg(Assembler::TEMP_REGS[0]), out);
+ asm.mov(Opnd::Reg(TEMP_REGS[0]), out);
asm.compile_with_num_regs(&mut cb, 2);
assert_disasm!(cb, "8b0280d20c0080d261b18c9a", {"
@@ -1614,4 +1801,35 @@ mod tests {
0x8: csel x1, x11, x12, lt
"});
}
+
+ #[test]
+ fn test_add_with_immediate() {
+ let (mut asm, mut cb) = setup_asm();
+
+ let out = asm.add(Opnd::Reg(TEMP_REGS[1]), 1.into());
+ let out = asm.add(out, 1_usize.into());
+ asm.mov(Opnd::Reg(TEMP_REGS[0]), out);
+ asm.compile_with_num_regs(&mut cb, 2);
+
+ assert_disasm!(cb, "2b0500b16b0500b1e1030baa", {"
+ 0x0: adds x11, x9, #1
+ 0x4: adds x11, x11, #1
+ 0x8: mov x1, x11
+ "});
+ }
+
+ #[test]
+ fn test_mul_with_immediate() {
+ let (mut asm, mut cb) = setup_asm();
+
+ let out = asm.mul(Opnd::Reg(TEMP_REGS[1]), 3.into());
+ asm.mov(Opnd::Reg(TEMP_REGS[0]), out);
+ asm.compile_with_num_regs(&mut cb, 2);
+
+ assert_disasm!(cb, "6b0080d22b7d0b9be1030baa", {"
+ 0x0: mov x11, #3
+ 0x4: mul x11, x9, x11
+ 0x8: mov x1, x11
+ "});
+ }
}
diff --git a/yjit/src/backend/ir.rs b/yjit/src/backend/ir.rs
index 8c4a6c1b6d..edc0eaf390 100644
--- a/yjit/src/backend/ir.rs
+++ b/yjit/src/backend/ir.rs
@@ -1,26 +1,16 @@
-#![allow(dead_code)]
-#![allow(unused_variables)]
-#![allow(unused_imports)]
-
-use std::cell::Cell;
use std::collections::HashMap;
use std::fmt;
use std::convert::From;
-use std::io::Write;
use std::mem::take;
use crate::codegen::{gen_outlined_exit, gen_counted_exit};
-use crate::cruby::{VALUE, SIZEOF_VALUE_I32};
-use crate::virtualmem::{CodePtr};
-use crate::asm::{CodeBlock, uimm_num_bits, imm_num_bits, OutlinedCb};
-use crate::core::{Context, Type, TempMapping, RegTemps, MAX_REG_TEMPS, MAX_TEMP_TYPES};
+use crate::cruby::{vm_stack_canary, SIZEOF_VALUE_I32, VALUE};
+use crate::virtualmem::CodePtr;
+use crate::asm::{CodeBlock, OutlinedCb};
+use crate::core::{Context, RegTemps, MAX_REG_TEMPS};
use crate::options::*;
use crate::stats::*;
-#[cfg(target_arch = "x86_64")]
-use crate::backend::x86_64::*;
-
-#[cfg(target_arch = "aarch64")]
-use crate::backend::arm64::*;
+use crate::backend::current::*;
pub const EC: Opnd = _EC;
pub const CFP: Opnd = _CFP;
@@ -28,6 +18,7 @@ pub const SP: Opnd = _SP;
pub const C_ARG_OPNDS: [Opnd; 6] = _C_ARG_OPNDS;
pub const C_RET_OPND: Opnd = _C_RET_OPND;
+pub use crate::backend::current::{Reg, C_RET_REG};
// Memory operand base
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
@@ -72,6 +63,9 @@ pub enum Opnd
// Immediate Ruby value, may be GC'd, movable
Value(VALUE),
+ /// C argument register. The alloc_regs resolves its register dependencies.
+ CArg(Reg),
+
// Output of a preceding instruction in this block
InsnOut{ idx: usize, num_bits: u8 },
@@ -102,6 +96,7 @@ impl fmt::Debug for Opnd {
match self {
Self::None => write!(fmt, "None"),
Value(val) => write!(fmt, "Value({val:?})"),
+ CArg(reg) => write!(fmt, "CArg({reg:?})"),
Stack { idx, sp_offset, .. } => write!(fmt, "SP[{}]", *sp_offset as i32 - idx - 1),
InsnOut { idx, num_bits } => write!(fmt, "Out{num_bits}({idx})"),
Imm(signed) => write!(fmt, "{signed:x}_i64"),
@@ -145,10 +140,11 @@ impl Opnd
Opnd::UImm(ptr as u64)
}
- pub fn is_some(&self) -> bool {
- match *self {
- Opnd::None => false,
- _ => true,
+ /// Constructor for a C argument operand
+ pub fn c_arg(reg_opnd: Opnd) -> Self {
+ match reg_opnd {
+ Opnd::Reg(reg) => Opnd::CArg(reg),
+ _ => unreachable!(),
}
}
@@ -233,11 +229,16 @@ impl Opnd
/// Calculate Opnd::Stack's index from the stack bottom.
pub fn stack_idx(&self) -> u8 {
+ self.get_stack_idx().unwrap()
+ }
+
+ /// Calculate Opnd::Stack's index from the stack bottom if it's Opnd::Stack.
+ pub fn get_stack_idx(&self) -> Option<u8> {
match self {
Opnd::Stack { idx, stack_size, .. } => {
- (*stack_size as isize - *idx as isize - 1) as u8
+ Some((*stack_size as isize - *idx as isize - 1) as u8)
},
- _ => unreachable!(),
+ _ => None
}
}
@@ -331,7 +332,7 @@ impl From<CodePtr> for Target {
}
}
-type PosMarkerFn = Box<dyn Fn(CodePtr)>;
+type PosMarkerFn = Box<dyn Fn(CodePtr, &CodeBlock)>;
/// YJIT IR instruction
pub enum Insn {
@@ -346,6 +347,7 @@ pub enum Insn {
BakeString(String),
// Trigger a debugger breakpoint
+ #[allow(dead_code)]
Breakpoint,
/// Add a comment into the IR at the point that this instruction is added.
@@ -411,15 +413,24 @@ pub enum Insn {
// Produces no output
IncrCounter { mem: Opnd, value: Opnd },
- /// Jump if below or equal
+ /// Jump if below or equal (unsigned)
Jbe(Target),
+ /// Jump if below (unsigned)
+ Jb(Target),
+
/// Jump if equal
Je(Target),
/// Jump if lower
Jl(Target),
+ /// Jump if greater
+ Jg(Target),
+
+ /// Jump if greater or equal
+ Jge(Target),
+
// Unconditional jump to a branch target
Jmp(Target),
@@ -435,15 +446,23 @@ pub enum Insn {
/// Jump if overflow
Jo(Target),
+ /// Jump if overflow in multiplication
+ JoMul(Target),
+
/// Jump if zero
Jz(Target),
+ /// Jump if operand is zero (only used during lowering at the moment)
+ Joz(Opnd, Target),
+
+ /// Jump if operand is non-zero (only used during lowering at the moment)
+ Jonz(Opnd, Target),
+
// Add a label into the IR at the point that this instruction is added.
Label(Target),
- // Load effective address relative to the current instruction pointer. It
- // accepts a single signed immediate operand.
- LeaLabel { target: Target, out: Opnd },
+ /// Get the code address of a jump target
+ LeaJumpTarget { target: Target, out: Opnd },
// Load effective address
Lea { opnd: Opnd, out: Opnd },
@@ -489,9 +508,12 @@ pub enum Insn {
// Low-level instruction to store a value to memory.
Store { dest: Opnd, src: Opnd },
- // This is the same as the OP_ADD instruction, except for subtraction.
+ // This is the same as the add instruction, except for subtraction.
Sub { left: Opnd, right: Opnd, out: Opnd },
+ // Integer multiplication
+ Mul { left: Opnd, right: Opnd, out: Opnd },
+
// Bitwise AND test instruction
Test { left: Opnd, right: Opnd },
@@ -520,15 +542,21 @@ impl Insn {
pub(super) fn target_mut(&mut self) -> Option<&mut Target> {
match self {
Insn::Jbe(target) |
+ Insn::Jb(target) |
Insn::Je(target) |
Insn::Jl(target) |
+ Insn::Jg(target) |
+ Insn::Jge(target) |
Insn::Jmp(target) |
Insn::Jne(target) |
Insn::Jnz(target) |
Insn::Jo(target) |
Insn::Jz(target) |
Insn::Label(target) |
- Insn::LeaLabel { target, .. } => {
+ Insn::JoMul(target) |
+ Insn::Joz(_, target) |
+ Insn::Jonz(_, target) |
+ Insn::LeaJumpTarget { target, .. } => {
Some(target)
}
_ => None,
@@ -564,16 +592,22 @@ impl Insn {
Insn::FrameTeardown => "FrameTeardown",
Insn::IncrCounter { .. } => "IncrCounter",
Insn::Jbe(_) => "Jbe",
+ Insn::Jb(_) => "Jb",
Insn::Je(_) => "Je",
Insn::Jl(_) => "Jl",
+ Insn::Jg(_) => "Jg",
+ Insn::Jge(_) => "Jge",
Insn::Jmp(_) => "Jmp",
Insn::JmpOpnd(_) => "JmpOpnd",
Insn::Jne(_) => "Jne",
Insn::Jnz(_) => "Jnz",
Insn::Jo(_) => "Jo",
+ Insn::JoMul(_) => "JoMul",
Insn::Jz(_) => "Jz",
+ Insn::Joz(..) => "Joz",
+ Insn::Jonz(..) => "Jonz",
Insn::Label(_) => "Label",
- Insn::LeaLabel { .. } => "LeaLabel",
+ Insn::LeaJumpTarget { .. } => "LeaJumpTarget",
Insn::Lea { .. } => "Lea",
Insn::LiveReg { .. } => "LiveReg",
Insn::Load { .. } => "Load",
@@ -588,6 +622,7 @@ impl Insn {
Insn::RShift { .. } => "RShift",
Insn::Store { .. } => "Store",
Insn::Sub { .. } => "Sub",
+ Insn::Mul { .. } => "Mul",
Insn::Test { .. } => "Test",
Insn::URShift { .. } => "URShift",
Insn::Xor { .. } => "Xor"
@@ -611,7 +646,7 @@ impl Insn {
Insn::CSelNZ { out, .. } |
Insn::CSelZ { out, .. } |
Insn::Lea { out, .. } |
- Insn::LeaLabel { out, .. } |
+ Insn::LeaJumpTarget { out, .. } |
Insn::LiveReg { out, .. } |
Insn::Load { out, .. } |
Insn::LoadSExt { out, .. } |
@@ -620,6 +655,7 @@ impl Insn {
Insn::Or { out, .. } |
Insn::RShift { out, .. } |
Insn::Sub { out, .. } |
+ Insn::Mul { out, .. } |
Insn::URShift { out, .. } |
Insn::Xor { out, .. } => Some(out),
_ => None
@@ -643,7 +679,7 @@ impl Insn {
Insn::CSelNZ { out, .. } |
Insn::CSelZ { out, .. } |
Insn::Lea { out, .. } |
- Insn::LeaLabel { out, .. } |
+ Insn::LeaJumpTarget { out, .. } |
Insn::LiveReg { out, .. } |
Insn::Load { out, .. } |
Insn::LoadSExt { out, .. } |
@@ -652,6 +688,7 @@ impl Insn {
Insn::Or { out, .. } |
Insn::RShift { out, .. } |
Insn::Sub { out, .. } |
+ Insn::Mul { out, .. } |
Insn::URShift { out, .. } |
Insn::Xor { out, .. } => Some(out),
_ => None
@@ -662,14 +699,17 @@ impl Insn {
pub fn target(&self) -> Option<&Target> {
match self {
Insn::Jbe(target) |
+ Insn::Jb(target) |
Insn::Je(target) |
Insn::Jl(target) |
+ Insn::Jg(target) |
+ Insn::Jge(target) |
Insn::Jmp(target) |
Insn::Jne(target) |
Insn::Jnz(target) |
Insn::Jo(target) |
Insn::Jz(target) |
- Insn::LeaLabel { target, .. } => Some(target),
+ Insn::LeaJumpTarget { target, .. } => Some(target),
_ => None
}
}
@@ -711,17 +751,22 @@ impl<'a> Iterator for InsnOpndIterator<'a> {
Insn::FrameSetup |
Insn::FrameTeardown |
Insn::Jbe(_) |
+ Insn::Jb(_) |
Insn::Je(_) |
Insn::Jl(_) |
+ Insn::Jg(_) |
+ Insn::Jge(_) |
Insn::Jmp(_) |
Insn::Jne(_) |
Insn::Jnz(_) |
Insn::Jo(_) |
+ Insn::JoMul(_) |
Insn::Jz(_) |
Insn::Label(_) |
- Insn::LeaLabel { .. } |
+ Insn::LeaJumpTarget { .. } |
Insn::PadInvalPatch |
Insn::PosMarker(_) => None,
+
Insn::CPopInto(opnd) |
Insn::CPush(opnd) |
Insn::CRet(opnd) |
@@ -730,6 +775,8 @@ impl<'a> Iterator for InsnOpndIterator<'a> {
Insn::LiveReg { opnd, .. } |
Insn::Load { opnd, .. } |
Insn::LoadSExt { opnd, .. } |
+ Insn::Joz(opnd, _) |
+ Insn::Jonz(opnd, _) |
Insn::Not { opnd, .. } => {
match self.idx {
0 => {
@@ -758,6 +805,7 @@ impl<'a> Iterator for InsnOpndIterator<'a> {
Insn::RShift { opnd: opnd0, shift: opnd1, .. } |
Insn::Store { dest: opnd0, src: opnd1 } |
Insn::Sub { left: opnd0, right: opnd1, .. } |
+ Insn::Mul { left: opnd0, right: opnd1, .. } |
Insn::Test { left: opnd0, right: opnd1 } |
Insn::URShift { opnd: opnd0, shift: opnd1, .. } |
Insn::Xor { left: opnd0, right: opnd1, .. } => {
@@ -808,17 +856,22 @@ impl<'a> InsnOpndMutIterator<'a> {
Insn::FrameSetup |
Insn::FrameTeardown |
Insn::Jbe(_) |
+ Insn::Jb(_) |
Insn::Je(_) |
Insn::Jl(_) |
+ Insn::Jg(_) |
+ Insn::Jge(_) |
Insn::Jmp(_) |
Insn::Jne(_) |
Insn::Jnz(_) |
Insn::Jo(_) |
+ Insn::JoMul(_) |
Insn::Jz(_) |
Insn::Label(_) |
- Insn::LeaLabel { .. } |
+ Insn::LeaJumpTarget { .. } |
Insn::PadInvalPatch |
Insn::PosMarker(_) => None,
+
Insn::CPopInto(opnd) |
Insn::CPush(opnd) |
Insn::CRet(opnd) |
@@ -827,6 +880,8 @@ impl<'a> InsnOpndMutIterator<'a> {
Insn::LiveReg { opnd, .. } |
Insn::Load { opnd, .. } |
Insn::LoadSExt { opnd, .. } |
+ Insn::Joz(opnd, _) |
+ Insn::Jonz(opnd, _) |
Insn::Not { opnd, .. } => {
match self.idx {
0 => {
@@ -855,6 +910,7 @@ impl<'a> InsnOpndMutIterator<'a> {
Insn::RShift { opnd: opnd0, shift: opnd1, .. } |
Insn::Store { dest: opnd0, src: opnd1 } |
Insn::Sub { left: opnd0, right: opnd1, .. } |
+ Insn::Mul { left: opnd0, right: opnd1, .. } |
Insn::Test { left: opnd0, right: opnd1 } |
Insn::URShift { opnd: opnd0, shift: opnd1, .. } |
Insn::Xor { left: opnd0, right: opnd1, .. } => {
@@ -915,10 +971,51 @@ pub struct SideExitContext {
/// PC of the instruction being compiled
pub pc: *mut VALUE,
- /// Context when it started to compile the instruction
- pub ctx: Context,
+ /// Context fields used by get_generic_ctx()
+ pub stack_size: u8,
+ pub sp_offset: i8,
+ pub reg_temps: RegTemps,
+ pub is_return_landing: bool,
+ pub is_deferred: bool,
+}
+
+impl SideExitContext {
+ /// Convert PC and Context into SideExitContext
+ pub fn new(pc: *mut VALUE, ctx: Context) -> Self {
+ let exit_ctx = SideExitContext {
+ pc,
+ stack_size: ctx.get_stack_size(),
+ sp_offset: ctx.get_sp_offset(),
+ reg_temps: ctx.get_reg_temps(),
+ is_return_landing: ctx.is_return_landing(),
+ is_deferred: ctx.is_deferred(),
+ };
+ if cfg!(debug_assertions) {
+ // Assert that we're not losing any mandatory metadata
+ assert_eq!(exit_ctx.get_ctx(), ctx.get_generic_ctx());
+ }
+ exit_ctx
+ }
+
+ /// Convert SideExitContext to Context
+ fn get_ctx(&self) -> Context {
+ let mut ctx = Context::default();
+ ctx.set_stack_size(self.stack_size);
+ ctx.set_sp_offset(self.sp_offset);
+ ctx.set_reg_temps(self.reg_temps);
+ if self.is_return_landing {
+ ctx.set_as_return_landing();
+ }
+ if self.is_deferred {
+ ctx.mark_as_deferred();
+ }
+ ctx
+ }
}
+/// Initial capacity for asm.insns vector
+const ASSEMBLER_INSNS_CAPACITY: usize = 256;
+
/// Object into which we assemble instructions to be
/// optimized and lowered
pub struct Assembler {
@@ -942,6 +1039,9 @@ pub struct Assembler {
/// Stack size for Target::SideExit
side_exit_stack_size: Option<u8>,
+
+ /// If true, the next ccall() should verify its leafness
+ leaf_ccall: bool,
}
impl Assembler
@@ -952,21 +1052,21 @@ impl Assembler
pub fn new_with_label_names(label_names: Vec<String>, side_exits: HashMap<SideExitContext, CodePtr>) -> Self {
Self {
- insns: Vec::default(),
- live_ranges: Vec::default(),
+ insns: Vec::with_capacity(ASSEMBLER_INSNS_CAPACITY),
+ live_ranges: Vec::with_capacity(ASSEMBLER_INSNS_CAPACITY),
label_names,
ctx: Context::default(),
side_exits,
side_exit_pc: None,
side_exit_stack_size: None,
+ leaf_ccall: false,
}
}
/// Get the list of registers that can be used for stack temps.
- pub fn get_temp_regs() -> Vec<Reg> {
+ pub fn get_temp_regs() -> &'static [Reg] {
let num_regs = get_option!(num_temp_regs);
- let mut regs = Self::TEMP_REGS.to_vec();
- regs.drain(0..num_regs).collect()
+ &TEMP_REGS[0..num_regs]
}
/// Set a context for generating side exits
@@ -984,11 +1084,10 @@ impl Assembler
/// Append an instruction onto the current list of instructions and update
/// the live ranges of any instructions whose outputs are being used as
/// operands to this instruction.
- pub(super) fn push_insn(&mut self, insn: Insn) {
+ pub fn push_insn(&mut self, mut insn: Insn) {
// Index of this instruction
let insn_idx = self.insns.len();
- let mut insn = insn;
let mut opnd_iter = insn.opnd_iter_mut();
while let Some(opnd) = opnd_iter.next() {
match opnd {
@@ -1004,6 +1103,12 @@ impl Assembler
}
// Set current ctx.reg_temps to Opnd::Stack.
Opnd::Stack { idx, num_bits, stack_size, sp_offset, reg_temps: None } => {
+ assert_eq!(
+ self.ctx.get_stack_size() as i16 - self.ctx.get_sp_offset() as i16,
+ *stack_size as i16 - *sp_offset as i16,
+ "Opnd::Stack (stack_size: {}, sp_offset: {}) expects a different SP position from asm.ctx (stack_size: {}, sp_offset: {})",
+ *stack_size, *sp_offset, self.ctx.get_stack_size(), self.ctx.get_sp_offset(),
+ );
*opnd = Opnd::Stack {
idx: *idx,
num_bits: *num_bits,
@@ -1017,14 +1122,13 @@ impl Assembler
}
// Set a side exit context to Target::SideExit
- let mut insn = insn;
if let Some(Target::SideExit { context, .. }) = insn.target_mut() {
// We should skip this when this instruction is being copied from another Assembler.
if context.is_none() {
- *context = Some(SideExitContext {
- pc: self.side_exit_pc.unwrap(),
- ctx: self.ctx.with_stack_size(self.side_exit_stack_size.unwrap()),
- });
+ *context = Some(SideExitContext::new(
+ self.side_exit_pc.unwrap(),
+ self.ctx.with_stack_size(self.side_exit_stack_size.unwrap()),
+ ));
}
}
@@ -1033,23 +1137,19 @@ impl Assembler
}
/// Get a cached side exit, wrapping a counter if specified
- pub fn get_side_exit(&mut self, side_exit_context: &SideExitContext, counter: Option<Counter>, ocb: &mut OutlinedCb) -> CodePtr {
- // Drop type information from a cache key
- let mut side_exit_context = side_exit_context.clone();
- side_exit_context.ctx = side_exit_context.ctx.get_generic_ctx();
-
+ pub fn get_side_exit(&mut self, side_exit_context: &SideExitContext, counter: Option<Counter>, ocb: &mut OutlinedCb) -> Option<CodePtr> {
// Get a cached side exit
let side_exit = match self.side_exits.get(&side_exit_context) {
None => {
- let exit_code = gen_outlined_exit(side_exit_context.pc, &side_exit_context.ctx, ocb);
- self.side_exits.insert(side_exit_context.clone(), exit_code);
+ let exit_code = gen_outlined_exit(side_exit_context.pc, &side_exit_context.get_ctx(), ocb)?;
+ self.side_exits.insert(*side_exit_context, exit_code);
exit_code
}
Some(code_ptr) => *code_ptr,
};
// Wrap a counter if needed
- gen_counted_exit(side_exit, ocb, counter)
+ gen_counted_exit(side_exit_context.pc, side_exit, ocb, counter)
}
/// Create a new label instance that we can jump to
@@ -1086,7 +1186,7 @@ impl Assembler
}
match opnd {
- Opnd::Stack { idx, num_bits, stack_size, sp_offset, reg_temps } => {
+ Opnd::Stack { reg_temps, .. } => {
if opnd.stack_idx() < MAX_REG_TEMPS && reg_temps.unwrap().get(opnd.stack_idx()) {
reg_opnd(opnd)
} else {
@@ -1113,6 +1213,13 @@ impl Assembler
}
}
+ /// Erase local variable type information
+ /// eg: because of a call we can't track
+ pub fn clear_local_types(&mut self) {
+ asm_comment!(self, "clear local variable types");
+ self.ctx.clear_local_types();
+ }
+
/// Spill all live stack temps from registers to the stack
pub fn spill_temps(&mut self) {
// Forget registers above the stack top
@@ -1124,7 +1231,7 @@ impl Assembler
// Spill live stack temps
if self.ctx.get_reg_temps() != RegTemps::default() {
- self.comment(&format!("spill_temps: {:08b} -> {:08b}", self.ctx.get_reg_temps().as_u8(), RegTemps::default().as_u8()));
+ asm_comment!(self, "spill_temps: {:08b} -> {:08b}", self.ctx.get_reg_temps().as_u8(), RegTemps::default().as_u8());
for stack_idx in 0..u8::min(MAX_REG_TEMPS, self.ctx.get_stack_size()) {
if self.ctx.get_reg_temps().get(stack_idx) {
let idx = self.ctx.get_stack_size() - 1 - stack_idx;
@@ -1164,7 +1271,7 @@ impl Assembler
/// Update which stack temps are in a register
pub fn set_reg_temps(&mut self, reg_temps: RegTemps) {
if self.ctx.get_reg_temps() != reg_temps {
- self.comment(&format!("reg_temps: {:08b} -> {:08b}", self.ctx.get_reg_temps().as_u8(), reg_temps.as_u8()));
+ asm_comment!(self, "reg_temps: {:08b} -> {:08b}", self.ctx.get_reg_temps().as_u8(), reg_temps.as_u8());
self.ctx.set_reg_temps(reg_temps);
self.verify_reg_temps();
}
@@ -1224,6 +1331,55 @@ impl Assembler
}
}
+ // Reorder C argument moves, sometimes adding extra moves using SCRATCH_REG,
+ // so that they will not rewrite each other before they are used.
+ fn reorder_c_args(c_args: &Vec<(Reg, Opnd)>) -> Vec<(Reg, Opnd)> {
+ // Return the index of a move whose destination is not used as a source if any.
+ fn find_safe_arg(c_args: &Vec<(Reg, Opnd)>) -> Option<usize> {
+ c_args.iter().enumerate().find(|(_, &(dest_reg, _))| {
+ c_args.iter().all(|&(_, src_opnd)| src_opnd != Opnd::Reg(dest_reg))
+ }).map(|(index, _)| index)
+ }
+
+ // Remove moves whose source and destination are the same
+ let mut c_args: Vec<(Reg, Opnd)> = c_args.clone().into_iter()
+ .filter(|&(reg, opnd)| Opnd::Reg(reg) != opnd).collect();
+
+ let mut moves = vec![];
+ while c_args.len() > 0 {
+ // Keep taking safe moves
+ while let Some(index) = find_safe_arg(&c_args) {
+ moves.push(c_args.remove(index));
+ }
+
+ // No safe move. Load the source of one move into SCRATCH_REG, and
+ // then load SCRATCH_REG into the destination when it's safe.
+ if c_args.len() > 0 {
+ // Make sure it's safe to use SCRATCH_REG
+ assert!(c_args.iter().all(|&(_, opnd)| opnd != Opnd::Reg(Assembler::SCRATCH_REG)));
+
+ // Move SCRATCH <- opnd, and delay reg <- SCRATCH
+ let (reg, opnd) = c_args.remove(0);
+ moves.push((Assembler::SCRATCH_REG, opnd));
+ c_args.push((reg, Opnd::Reg(Assembler::SCRATCH_REG)));
+ }
+ }
+ moves
+ }
+
+ // Adjust the number of entries in live_ranges so that it can be indexed by mapped indexes.
+ fn shift_live_ranges(live_ranges: &mut Vec<usize>, start_index: usize, shift_offset: isize) {
+ if shift_offset >= 0 {
+ for index in 0..(shift_offset as usize) {
+ live_ranges.insert(start_index + index, start_index + index);
+ }
+ } else {
+ for _ in 0..-shift_offset {
+ live_ranges.remove(start_index);
+ }
+ }
+ }
+
// Dump live registers for register spill debugging.
fn dump_live_regs(insns: Vec<Insn>, live_ranges: Vec<usize>, num_regs: usize, spill_index: usize) {
// Convert live_ranges to live_regs: the number of live registers at each index
@@ -1247,11 +1403,18 @@ impl Assembler
}
}
+ // We may need to reorder LoadInto instructions with a C argument operand.
+ // This buffers the operands of such instructions to process them in batches.
+ let mut c_args: Vec<(Reg, Opnd)> = vec![];
+
+ // live_ranges is indexed by original `index` given by the iterator.
let live_ranges: Vec<usize> = take(&mut self.live_ranges);
+ // shifted_live_ranges is indexed by mapped indexes in insn operands.
+ let mut shifted_live_ranges: Vec<usize> = live_ranges.clone();
let mut asm = Assembler::new_with_label_names(take(&mut self.label_names), take(&mut self.side_exits));
let mut iterator = self.into_draining_iter();
- while let Some((index, mut insn)) = iterator.next_unmapped() {
+ while let Some((index, mut insn)) = iterator.next_mapped() {
// Check if this is the last instruction that uses an operand that
// spans more than one instruction. In that case, return the
// allocated register to the pool.
@@ -1262,12 +1425,11 @@ impl Assembler
// Since we have an InsnOut, we know it spans more that one
// instruction.
let start_index = *idx;
- assert!(start_index < index);
// We're going to check if this is the last instruction that
// uses this operand. If it is, we can return the allocated
// register to the pool.
- if live_ranges[start_index] == index {
+ if shifted_live_ranges[start_index] == index {
if let Some(Opnd::Reg(reg)) = asm.insns[start_index].out_opnd() {
dealloc_reg(&mut pool, &regs, reg);
} else {
@@ -1314,7 +1476,7 @@ impl Assembler
let mut opnd_iter = insn.opnd_iter();
if let Some(Opnd::InsnOut{ idx, .. }) = opnd_iter.next() {
- if live_ranges[*idx] == index {
+ if shifted_live_ranges[*idx] == index {
if let Some(Opnd::Reg(reg)) = asm.insns[*idx].out_opnd() {
out_reg = Some(take_reg(&mut pool, &regs, reg));
}
@@ -1371,23 +1533,43 @@ impl Assembler
}
}
- asm.push_insn(insn);
+ // Push instruction(s). Batch and reorder C argument operations if needed.
+ if let Insn::LoadInto { dest: Opnd::CArg(reg), opnd } = insn {
+ // Buffer C arguments
+ c_args.push((reg, opnd));
+ } else {
+ // C arguments are buffered until CCall
+ if c_args.len() > 0 {
+ // Resolve C argument dependencies
+ let c_args_len = c_args.len() as isize;
+ let moves = reorder_c_args(&c_args.drain(..).into_iter().collect());
+ shift_live_ranges(&mut shifted_live_ranges, asm.insns.len(), moves.len() as isize - c_args_len);
+
+ // Push batched C arguments
+ for (reg, opnd) in moves {
+ asm.load_into(Opnd::Reg(reg), opnd);
+ }
+ }
+ // Other instructions are pushed as is
+ asm.push_insn(insn);
+ }
+ iterator.map_insn_index(&mut asm);
}
assert_eq!(pool, 0, "Expected all registers to be returned to the pool");
asm
}
- /// Compile the instructions down to machine code
- /// NOTE: should compile return a list of block labels to enable
- /// compiling multiple blocks at a time?
- pub fn compile(self, cb: &mut CodeBlock, ocb: Option<&mut OutlinedCb>) -> Vec<u32>
+ /// Compile the instructions down to machine code.
+ /// Can fail due to lack of code memory and inopportune code placement, among other reasons.
+ #[must_use]
+ pub fn compile(self, cb: &mut CodeBlock, ocb: Option<&mut OutlinedCb>) -> Option<(CodePtr, Vec<u32>)>
{
#[cfg(feature = "disasm")]
let start_addr = cb.get_write_ptr();
let alloc_regs = Self::get_alloc_regs();
- let gc_offsets = self.compile_with_regs(cb, ocb, alloc_regs);
+ let ret = self.compile_with_regs(cb, ocb, alloc_regs);
#[cfg(feature = "disasm")]
if let Some(dump_disasm) = get_option_ref!(dump_disasm) {
@@ -1395,15 +1577,16 @@ impl Assembler
let end_addr = cb.get_write_ptr();
dump_disasm_addr_range(cb, start_addr, end_addr, dump_disasm)
}
- gc_offsets
+ ret
}
/// Compile with a limited number of registers. Used only for unit tests.
- pub fn compile_with_num_regs(self, cb: &mut CodeBlock, num_regs: usize) -> Vec<u32>
+ #[cfg(test)]
+ pub fn compile_with_num_regs(self, cb: &mut CodeBlock, num_regs: usize) -> (CodePtr, Vec<u32>)
{
let mut alloc_regs = Self::get_alloc_regs();
let alloc_regs = alloc_regs.drain(0..num_regs).collect();
- self.compile_with_regs(cb, None, alloc_regs)
+ self.compile_with_regs(cb, None, alloc_regs).unwrap()
}
/// Consume the assembler by creating a new draining iterator.
@@ -1411,9 +1594,14 @@ impl Assembler
AssemblerDrainingIterator::new(self)
}
- /// Consume the assembler by creating a new lookback iterator.
- pub fn into_lookback_iter(self) -> AssemblerLookbackIterator {
- AssemblerLookbackIterator::new(self)
+ /// Return true if the next ccall() is expected to be leaf.
+ pub fn get_leaf_ccall(&mut self) -> bool {
+ self.leaf_ccall
+ }
+
+ /// Assert that the next ccall() is going to be leaf.
+ pub fn expect_leaf_ccall(&mut self) {
+ self.leaf_ccall = true;
}
}
@@ -1430,7 +1618,7 @@ impl AssemblerDrainingIterator {
Self {
insns: asm.insns.into_iter().peekable(),
index: 0,
- indices: Vec::default()
+ indices: Vec::with_capacity(ASSEMBLER_INSNS_CAPACITY),
}
}
@@ -1442,10 +1630,11 @@ impl AssemblerDrainingIterator {
/// end of the current list of instructions in order to maintain that
/// alignment.
pub fn map_insn_index(&mut self, asm: &mut Assembler) {
- self.indices.push(asm.insns.len() - 1);
+ self.indices.push(asm.insns.len().saturating_sub(1));
}
/// Map an operand by using this iterator's list of mapped indices.
+ #[cfg(target_arch = "x86_64")]
pub fn map_opnd(&self, opnd: Opnd) -> Opnd {
opnd.map_index(&self.indices)
}
@@ -1477,52 +1666,6 @@ impl AssemblerDrainingIterator {
}
}
-/// A struct that allows iterating through references to an assembler's
-/// instructions without consuming them.
-pub struct AssemblerLookbackIterator {
- asm: Assembler,
- index: Cell<usize>
-}
-
-impl AssemblerLookbackIterator {
- fn new(asm: Assembler) -> Self {
- Self { asm, index: Cell::new(0) }
- }
-
- /// Fetches a reference to an instruction at a specific index.
- pub fn get(&self, index: usize) -> Option<&Insn> {
- self.asm.insns.get(index)
- }
-
- /// Fetches a reference to an instruction in the list relative to the
- /// current cursor location of this iterator.
- pub fn get_relative(&self, difference: i32) -> Option<&Insn> {
- let index: Result<i32, _> = self.index.get().try_into();
- let relative: Result<usize, _> = index.and_then(|value| (value + difference).try_into());
- relative.ok().and_then(|value| self.asm.insns.get(value))
- }
-
- /// Fetches the previous instruction relative to the current cursor location
- /// of this iterator.
- pub fn get_previous(&self) -> Option<&Insn> {
- self.get_relative(-1)
- }
-
- /// Fetches the next instruction relative to the current cursor location of
- /// this iterator.
- pub fn get_next(&self) -> Option<&Insn> {
- self.get_relative(1)
- }
-
- /// Returns the next instruction in the list with the indices corresponding
- /// to the previous list of instructions.
- pub fn next_unmapped(&self) -> Option<(usize, &Insn)> {
- let index = self.index.get();
- self.index.set(index + 1);
- self.asm.insns.get(index).map(|insn| (index, insn))
- }
-}
-
impl fmt::Debug for Assembler {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
writeln!(fmt, "Assembler")?;
@@ -1554,23 +1697,67 @@ impl Assembler {
self.push_insn(Insn::BakeString(text.to_string()));
}
+ #[allow(dead_code)]
pub fn breakpoint(&mut self) {
self.push_insn(Insn::Breakpoint);
}
pub fn ccall(&mut self, fptr: *const u8, opnds: Vec<Opnd>) -> Opnd {
- assert_eq!(self.ctx.get_reg_temps(), RegTemps::default(), "temps must be spilled before ccall");
+ // Let vm_check_canary() assert this ccall's leafness if leaf_ccall is set
+ let canary_opnd = self.set_stack_canary(&opnds);
+
+ let old_temps = self.ctx.get_reg_temps(); // with registers
+ // Spill stack temp registers since they are caller-saved registers.
+ // Note that this doesn't spill stack temps that are already popped
+ // but may still be used in the C arguments.
+ self.spill_temps();
+ let new_temps = self.ctx.get_reg_temps(); // all spilled
+
+ // Temporarily manipulate RegTemps so that we can use registers
+ // to pass stack operands that are already spilled above.
+ self.ctx.set_reg_temps(old_temps);
+
+ // Call a C function
let out = self.next_opnd_out(Opnd::match_num_bits(&opnds));
self.push_insn(Insn::CCall { fptr, opnds, out });
+
+ // Registers in old_temps may be clobbered by the above C call,
+ // so rollback the manipulated RegTemps to a spilled version.
+ self.ctx.set_reg_temps(new_temps);
+
+ // Clear the canary after use
+ if let Some(canary_opnd) = canary_opnd {
+ self.mov(canary_opnd, 0.into());
+ }
+
out
}
- pub fn cmp(&mut self, left: Opnd, right: Opnd) {
- self.push_insn(Insn::Cmp { left, right });
+ /// Let vm_check_canary() assert the leafness of this ccall if leaf_ccall is set
+ fn set_stack_canary(&mut self, opnds: &Vec<Opnd>) -> Option<Opnd> {
+ // Use the slot right above the stack top for verifying leafness.
+ let canary_opnd = self.stack_opnd(-1);
+
+ // If the slot is already used, which is a valid optimization to avoid spills,
+ // give up the verification.
+ let canary_opnd = if cfg!(debug_assertions) && self.leaf_ccall && opnds.iter().all(|opnd|
+ opnd.get_stack_idx() != canary_opnd.get_stack_idx()
+ ) {
+ asm_comment!(self, "set stack canary");
+ self.mov(canary_opnd, vm_stack_canary().into());
+ Some(canary_opnd)
+ } else {
+ None
+ };
+
+ // Avoid carrying the flag to the next instruction whether we verified it or not.
+ self.leaf_ccall = false;
+
+ canary_opnd
}
- pub fn comment(&mut self, text: &str) {
- self.push_insn(Insn::Comment(text.to_string()));
+ pub fn cmp(&mut self, left: Opnd, right: Opnd) {
+ self.push_insn(Insn::Cmp { left, right });
}
#[must_use]
@@ -1682,6 +1869,10 @@ impl Assembler {
self.push_insn(Insn::Jbe(target));
}
+ pub fn jb(&mut self, target: Target) {
+ self.push_insn(Insn::Jb(target));
+ }
+
pub fn je(&mut self, target: Target) {
self.push_insn(Insn::Je(target));
}
@@ -1690,6 +1881,16 @@ impl Assembler {
self.push_insn(Insn::Jl(target));
}
+ #[allow(dead_code)]
+ pub fn jg(&mut self, target: Target) {
+ self.push_insn(Insn::Jg(target));
+ }
+
+ #[allow(dead_code)]
+ pub fn jge(&mut self, target: Target) {
+ self.push_insn(Insn::Jge(target));
+ }
+
pub fn jmp(&mut self, target: Target) {
self.push_insn(Insn::Jmp(target));
}
@@ -1710,6 +1911,10 @@ impl Assembler {
self.push_insn(Insn::Jo(target));
}
+ pub fn jo_mul(&mut self, target: Target) {
+ self.push_insn(Insn::JoMul(target));
+ }
+
pub fn jz(&mut self, target: Target) {
self.push_insn(Insn::Jz(target));
}
@@ -1722,9 +1927,9 @@ impl Assembler {
}
#[must_use]
- pub fn lea_label(&mut self, target: Target) -> Opnd {
+ pub fn lea_jump_target(&mut self, target: Target) -> Opnd {
let out = self.next_opnd_out(Opnd::DEFAULT_NUM_BITS);
- self.push_insn(Insn::LeaLabel { target, out });
+ self.push_insn(Insn::LeaJumpTarget { target, out });
out
}
@@ -1786,7 +1991,7 @@ impl Assembler {
}
//pub fn pos_marker<F: FnMut(CodePtr)>(&mut self, marker_fn: F)
- pub fn pos_marker(&mut self, marker_fn: impl Fn(CodePtr) + 'static) {
+ pub fn pos_marker(&mut self, marker_fn: impl Fn(CodePtr, &CodeBlock) + 'static) {
self.push_insn(Insn::PosMarker(Box::new(marker_fn)));
}
@@ -1808,17 +2013,35 @@ impl Assembler {
out
}
+ #[must_use]
+ pub fn mul(&mut self, left: Opnd, right: Opnd) -> Opnd {
+ let out = self.next_opnd_out(Opnd::match_num_bits(&[left, right]));
+ self.push_insn(Insn::Mul { left, right, out });
+ out
+ }
+
pub fn test(&mut self, left: Opnd, right: Opnd) {
self.push_insn(Insn::Test { left, right });
}
#[must_use]
+ #[allow(dead_code)]
pub fn urshift(&mut self, opnd: Opnd, shift: Opnd) -> Opnd {
let out = self.next_opnd_out(Opnd::match_num_bits(&[opnd, shift]));
self.push_insn(Insn::URShift { opnd, shift, out });
out
}
+ /// Verify the leafness of the given block
+ pub fn with_leaf_ccall<F, R>(&mut self, mut block: F) -> R
+ where F: FnMut(&mut Self) -> R {
+ let old_leaf_ccall = self.leaf_ccall;
+ self.leaf_ccall = true;
+ let ret = block(self);
+ self.leaf_ccall = old_leaf_ccall;
+ ret
+ }
+
/// Add a label at the current position
pub fn write_label(&mut self, target: Target) {
assert!(target.unwrap_label_idx() < self.label_names.len());
@@ -1833,6 +2056,17 @@ impl Assembler {
}
}
+/// Macro to use format! for Insn::Comment, which skips a format! call
+/// when disasm is not supported.
+macro_rules! asm_comment {
+ ($asm:expr, $($fmt:tt)*) => {
+ if cfg!(feature = "disasm") {
+ $asm.push_insn(Insn::Comment(format!($($fmt)*)));
+ }
+ };
+}
+pub(crate) use asm_comment;
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/yjit/src/backend/mod.rs b/yjit/src/backend/mod.rs
index 4794695094..6921244c72 100644
--- a/yjit/src/backend/mod.rs
+++ b/yjit/src/backend/mod.rs
@@ -4,5 +4,11 @@ pub mod x86_64;
#[cfg(target_arch = "aarch64")]
pub mod arm64;
+#[cfg(target_arch = "x86_64")]
+pub use x86_64 as current;
+
+#[cfg(target_arch = "aarch64")]
+pub use arm64 as current;
+
pub mod ir;
mod tests;
diff --git a/yjit/src/backend/tests.rs b/yjit/src/backend/tests.rs
index 8ba9f61d25..01e87fe26c 100644
--- a/yjit/src/backend/tests.rs
+++ b/yjit/src/backend/tests.rs
@@ -87,7 +87,7 @@ fn test_mov_mem2mem()
{
let (mut asm, mut cb) = setup_asm();
- asm.comment("check that comments work too");
+ asm_comment!(asm, "check that comments work too");
asm.mov(Opnd::mem(64, SP, 0), Opnd::mem(64, SP, 8));
asm.compile_with_num_regs(&mut cb, 1);
@@ -231,7 +231,7 @@ fn test_jcc_ptr()
{
let (mut asm, mut cb) = setup_asm();
- let side_exit = Target::CodePtr(((cb.get_write_ptr().raw_ptr() as usize + 4) as *mut u8).into());
+ let side_exit = Target::CodePtr(cb.get_write_ptr().add_bytes(4));
let not_mask = asm.not(Opnd::mem(32, EC, RUBY_OFFSET_EC_INTERRUPT_MASK));
asm.test(
Opnd::mem(32, EC, RUBY_OFFSET_EC_INTERRUPT_FLAG),
@@ -248,7 +248,7 @@ fn test_jmp_ptr()
{
let (mut asm, mut cb) = setup_asm();
- let stub = Target::CodePtr(((cb.get_write_ptr().raw_ptr() as usize + 4) as *mut u8).into());
+ let stub = Target::CodePtr(cb.get_write_ptr().add_bytes(4));
asm.jmp(stub);
asm.compile_with_num_regs(&mut cb, 0);
@@ -259,7 +259,7 @@ fn test_jo()
{
let (mut asm, mut cb) = setup_asm();
- let side_exit = Target::CodePtr(((cb.get_write_ptr().raw_ptr() as usize + 4) as *mut u8).into());
+ let side_exit = Target::CodePtr(cb.get_write_ptr().add_bytes(4));
let arg1 = Opnd::mem(64, SP, 0);
let arg0 = Opnd::mem(64, SP, 8);
@@ -303,25 +303,6 @@ fn test_draining_iterator() {
}
#[test]
-fn test_lookback_iterator() {
- let mut asm = Assembler::new();
-
- let _ = asm.load(Opnd::None);
- asm.store(Opnd::None, Opnd::None);
- asm.store(Opnd::None, Opnd::None);
-
- let iter = asm.into_lookback_iter();
-
- while let Some((index, insn)) = iter.next_unmapped() {
- if index > 0 {
- let opnd_iter = iter.get_previous().unwrap().opnd_iter();
- assert_eq!(opnd_iter.take(1).next(), Some(&Opnd::None));
- assert!(matches!(insn, Insn::Store { .. }));
- }
- }
-}
-
-#[test]
fn test_cmp_8_bit() {
let (mut asm, mut cb) = setup_asm();
let reg = Assembler::get_alloc_regs()[0];
@@ -329,3 +310,21 @@ fn test_cmp_8_bit() {
asm.compile_with_num_regs(&mut cb, 1);
}
+
+#[test]
+fn test_no_pos_marker_callback_when_compile_fails() {
+ // When compilation fails (e.g. when out of memory), the code written out is malformed.
+ // We don't want to invoke the pos_marker callbacks with positions of malformed code.
+ let mut asm = Assembler::new();
+
+ // Markers around code to exhaust memory limit
+ let fail_if_called = |_code_ptr, _cb: &_| panic!("pos_marker callback should not be called");
+ asm.pos_marker(fail_if_called);
+ let zero = asm.load(0.into());
+ let sum = asm.add(zero, 500.into());
+ asm.store(Opnd::mem(64, SP, 8), sum);
+ asm.pos_marker(fail_if_called);
+
+ let cb = &mut CodeBlock::new_dummy(8);
+ assert!(asm.compile(cb, None).is_none(), "should fail due to tiny size limit");
+}
diff --git a/yjit/src/backend/x86_64/mod.rs b/yjit/src/backend/x86_64/mod.rs
index a2ee94cf66..4ca5e9be9c 100644
--- a/yjit/src/backend/x86_64/mod.rs
+++ b/yjit/src/backend/x86_64/mod.rs
@@ -1,17 +1,12 @@
-#![allow(dead_code)]
-#![allow(unused_variables)]
-#![allow(unused_imports)]
-
use std::mem::take;
use crate::asm::*;
use crate::asm::x86_64::*;
-use crate::codegen::{JITState};
-use crate::core::Context;
+use crate::codegen::CodePtr;
use crate::cruby::*;
use crate::backend::ir::*;
-use crate::codegen::CodegenGlobals;
use crate::options::*;
+use crate::utils::*;
// Use the x86 register type for this platform
pub type Reg = X86Reg;
@@ -37,7 +32,7 @@ pub const _C_RET_OPND: Opnd = Opnd::Reg(RAX_REG);
impl CodeBlock {
// The number of bytes that are generated by jmp_ptr
- pub fn jmp_ptr_bytes(&self) -> usize { 6 }
+ pub fn jmp_ptr_bytes(&self) -> usize { 5 }
}
/// Map Opnd to X86Opnd
@@ -84,15 +79,16 @@ impl From<&Opnd> for X86Opnd {
}
}
+/// List of registers that can be used for stack temps.
+pub static TEMP_REGS: [Reg; 5] = [RSI_REG, RDI_REG, R8_REG, R9_REG, R10_REG];
+
impl Assembler
{
// A special scratch register for intermediate processing.
- // Note: right now this is only used by LeaLabel because label_ref accepts
- // a closure and we don't want it to have to capture anything.
- const SCRATCH0: X86Opnd = X86Opnd::Reg(R11_REG);
+ // This register is caller-saved (so we don't have to save it before using it)
+ pub const SCRATCH_REG: Reg = R11_REG;
+ const SCRATCH0: X86Opnd = X86Opnd::Reg(Assembler::SCRATCH_REG);
- /// List of registers that can be used for stack temps.
- pub const TEMP_REGS: [Reg; 5] = [RSI_REG, RDI_REG, R8_REG, R9_REG, R10_REG];
/// Get the list of registers from which we can allocate on this platform
pub fn get_alloc_regs() -> Vec<Reg>
@@ -139,7 +135,7 @@ impl Assembler
// Opnd::Value operands into registers here because:
//
// - Most instructions can't be encoded with 64-bit immediates.
- // - We look for Op::Load specifically when emiting to keep GC'ed
+ // - We look for Op::Load specifically when emitting to keep GC'ed
// VALUEs alive. This is a sort of canonicalization.
let mut unmapped_opnds: Vec<Opnd> = vec![];
@@ -172,6 +168,7 @@ impl Assembler
match &mut insn {
Insn::Add { left, right, out } |
Insn::Sub { left, right, out } |
+ Insn::Mul { left, right, out } |
Insn::And { left, right, out } |
Insn::Or { left, right, out } |
Insn::Xor { left, right, out } => {
@@ -184,6 +181,23 @@ impl Assembler
iterator.map_insn_index(&mut asm);
iterator.next_unmapped(); // Pop merged Insn::Mov
}
+ (Opnd::Reg(_), Opnd::Reg(_), Some(Insn::Mov { dest, src }))
+ if out == src && live_ranges[index] == index + 1 && {
+ // We want to do `dest == left`, but `left` has already gone
+ // through lower_stack_opnd() while `dest` has not. So we
+ // lower `dest` before comparing.
+ let lowered_dest = if let Opnd::Stack { .. } = dest {
+ asm.lower_stack_opnd(dest)
+ } else {
+ *dest
+ };
+ lowered_dest == *left
+ } => {
+ *out = *dest;
+ asm.push_insn(insn);
+ iterator.map_insn_index(&mut asm);
+ iterator.next_unmapped(); // Pop merged Insn::Mov
+ }
_ => {
match (unmapped_opnds[0], unmapped_opnds[1]) {
(Opnd::Mem(_), Opnd::Mem(_)) => {
@@ -274,7 +288,11 @@ impl Assembler
*truthy = asm.load(*truthy);
}
},
- Opnd::UImm(_) | Opnd::Imm(_) | Opnd::Value(_) => {
+ Opnd::UImm(_) | Opnd::Imm(_) => {
+ *truthy = asm.load(*truthy);
+ },
+ // Opnd::Value could have already been split
+ Opnd::Value(_) if !matches!(truthy, Opnd::InsnOut { .. }) => {
*truthy = asm.load(*truthy);
},
_ => {}
@@ -290,7 +308,7 @@ impl Assembler
*out = asm.next_opnd_out(Opnd::match_num_bits(&[*truthy, *falsy]));
asm.push_insn(insn);
},
- Insn::Mov { dest, src } => {
+ Insn::Mov { dest, src } | Insn::Store { dest, src } => {
match (&dest, &src) {
(Opnd::Mem(_), Opnd::Mem(_)) => {
// We load opnd1 because for mov, opnd0 is the output
@@ -347,7 +365,7 @@ impl Assembler
// Load each operand into the corresponding argument
// register.
for (idx, opnd) in opnds.into_iter().enumerate() {
- asm.load_into(C_ARG_OPNDS[idx], *opnd);
+ asm.load_into(Opnd::c_arg(C_ARG_OPNDS[idx]), *opnd);
}
// Now we push the CCall without any arguments so that it
@@ -384,7 +402,7 @@ impl Assembler
}
/// Emit platform-specific machine code
- pub fn x86_emit(&mut self, cb: &mut CodeBlock, ocb: &mut Option<&mut OutlinedCb>) -> Vec<u32>
+ pub fn x86_emit(&mut self, cb: &mut CodeBlock, ocb: &mut Option<&mut OutlinedCb>) -> Option<Vec<u32>>
{
/// For some instructions, we want to be able to lower a 64-bit operand
/// without requiring more registers to be available in the register
@@ -419,20 +437,40 @@ impl Assembler
target: Target,
asm: &mut Assembler,
ocb: &mut Option<&mut OutlinedCb>,
- ) -> Target {
+ ) -> Option<Target> {
if let Target::SideExit { counter, context } = target {
let side_exit = asm.get_side_exit(&context.unwrap(), Some(counter), ocb.as_mut().unwrap());
- Target::SideExitPtr(side_exit)
+ Some(Target::SideExitPtr(side_exit?))
} else {
- target
+ Some(target)
}
}
- fn emit_csel(cb: &mut CodeBlock, truthy: Opnd, falsy: Opnd, out: Opnd, cmov_fn: fn(&mut CodeBlock, X86Opnd, X86Opnd)) {
- if out != truthy {
- mov(cb, out.into(), truthy.into());
+ fn emit_csel(
+ cb: &mut CodeBlock,
+ truthy: Opnd,
+ falsy: Opnd,
+ out: Opnd,
+ cmov_fn: fn(&mut CodeBlock, X86Opnd, X86Opnd),
+ cmov_neg: fn(&mut CodeBlock, X86Opnd, X86Opnd)){
+
+ // Assert that output is a register
+ out.unwrap_reg();
+
+ // If the truthy value is a memory operand
+ if let Opnd::Mem(_) = truthy {
+ if out != falsy {
+ mov(cb, out.into(), falsy.into());
+ }
+
+ cmov_fn(cb, out.into(), truthy.into());
+ } else {
+ if out != truthy {
+ mov(cb, out.into(), truthy.into());
+ }
+
+ cmov_neg(cb, out.into(), falsy.into());
}
- cmov_fn(cb, out.into(), falsy.into());
}
//dbg!(&self.insns);
@@ -440,6 +478,9 @@ impl Assembler
// List of GC offsets
let mut gc_offsets: Vec<u32> = Vec::new();
+ // Buffered list of PosMarker callbacks to fire if codegen is successful
+ let mut pos_markers: Vec<(usize, CodePtr)> = vec![];
+
// For each instruction
let start_write_pos = cb.get_write_pos();
let mut insn_idx: usize = 0;
@@ -462,8 +503,8 @@ impl Assembler
},
// Report back the current position in the generated code
- Insn::PosMarker(pos_marker) => {
- pos_marker(cb.get_write_ptr());
+ Insn::PosMarker(..) => {
+ pos_markers.push((insn_idx, cb.get_write_ptr()));
},
Insn::BakeString(text) => {
@@ -476,19 +517,37 @@ impl Assembler
cb.write_byte(0);
},
+ // Set up RBP to work with frame pointer unwinding
+ // (e.g. with Linux `perf record --call-graph fp`)
+ Insn::FrameSetup => {
+ if get_option!(frame_pointer) {
+ push(cb, RBP);
+ mov(cb, RBP, RSP);
+ push(cb, RBP);
+ }
+ },
+ Insn::FrameTeardown => {
+ if get_option!(frame_pointer) {
+ pop(cb, RBP);
+ pop(cb, RBP);
+ }
+ },
+
Insn::Add { left, right, .. } => {
let opnd1 = emit_64bit_immediate(cb, right);
add(cb, left.into(), opnd1);
},
- Insn::FrameSetup => {},
- Insn::FrameTeardown => {},
-
Insn::Sub { left, right, .. } => {
let opnd1 = emit_64bit_immediate(cb, right);
sub(cb, left.into(), opnd1);
},
+ Insn::Mul { left, right, .. } => {
+ let opnd1 = emit_64bit_immediate(cb, right);
+ imul(cb, left.into(), opnd1);
+ },
+
Insn::And { left, right, .. } => {
let opnd1 = emit_64bit_immediate(cb, right);
and(cb, left.into(), opnd1);
@@ -552,16 +611,23 @@ impl Assembler
lea(cb, out.into(), opnd.into());
},
- // Load relative address
- Insn::LeaLabel { target, out } => {
- let label_idx = target.unwrap_label_idx();
-
- cb.label_ref(label_idx, 7, |cb, src_addr, dst_addr| {
- let disp = dst_addr - src_addr;
- lea(cb, Self::SCRATCH0, mem_opnd(8, RIP, disp.try_into().unwrap()));
- });
+ // Load address of jump target
+ Insn::LeaJumpTarget { target, out } => {
+ if let Target::Label(label_idx) = target {
+ // Set output to the raw address of the label
+ cb.label_ref(*label_idx, 7, |cb, src_addr, dst_addr| {
+ let disp = dst_addr - src_addr;
+ lea(cb, Self::SCRATCH0, mem_opnd(8, RIP, disp.try_into().unwrap()));
+ });
- mov(cb, out.into(), Self::SCRATCH0);
+ mov(cb, out.into(), Self::SCRATCH0);
+ } else {
+ // Set output to the jump target's raw address
+ let target_code = target.unwrap_code_ptr();
+ let target_addr = target_code.raw_addr(cb).as_u64();
+ // Constant encoded length important for patching
+ movabs(cb, out.into(), target_addr);
+ }
},
// Push and pop to/from the C stack
@@ -642,7 +708,7 @@ impl Assembler
// Conditional jump to a label
Insn::Jmp(target) => {
- match compile_side_exit(*target, self, ocb) {
+ match compile_side_exit(*target, self, ocb)? {
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jmp_ptr(cb, code_ptr),
Target::Label(label_idx) => jmp_label(cb, label_idx),
Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"),
@@ -650,7 +716,7 @@ impl Assembler
}
Insn::Je(target) => {
- match compile_side_exit(*target, self, ocb) {
+ match compile_side_exit(*target, self, ocb)? {
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => je_ptr(cb, code_ptr),
Target::Label(label_idx) => je_label(cb, label_idx),
Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"),
@@ -658,7 +724,7 @@ impl Assembler
}
Insn::Jne(target) => {
- match compile_side_exit(*target, self, ocb) {
+ match compile_side_exit(*target, self, ocb)? {
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jne_ptr(cb, code_ptr),
Target::Label(label_idx) => jne_label(cb, label_idx),
Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"),
@@ -666,23 +732,47 @@ impl Assembler
}
Insn::Jl(target) => {
- match compile_side_exit(*target, self, ocb) {
+ match compile_side_exit(*target, self, ocb)? {
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jl_ptr(cb, code_ptr),
Target::Label(label_idx) => jl_label(cb, label_idx),
Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"),
}
},
+ Insn::Jg(target) => {
+ match compile_side_exit(*target, self, ocb)? {
+ Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jg_ptr(cb, code_ptr),
+ Target::Label(label_idx) => jg_label(cb, label_idx),
+ Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"),
+ }
+ },
+
+ Insn::Jge(target) => {
+ match compile_side_exit(*target, self, ocb)? {
+ Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jge_ptr(cb, code_ptr),
+ Target::Label(label_idx) => jge_label(cb, label_idx),
+ Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"),
+ }
+ },
+
Insn::Jbe(target) => {
- match compile_side_exit(*target, self, ocb) {
+ match compile_side_exit(*target, self, ocb)? {
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jbe_ptr(cb, code_ptr),
Target::Label(label_idx) => jbe_label(cb, label_idx),
Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"),
}
},
+ Insn::Jb(target) => {
+ match compile_side_exit(*target, self, ocb)? {
+ Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jb_ptr(cb, code_ptr),
+ Target::Label(label_idx) => jb_label(cb, label_idx),
+ Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"),
+ }
+ },
+
Insn::Jz(target) => {
- match compile_side_exit(*target, self, ocb) {
+ match compile_side_exit(*target, self, ocb)? {
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jz_ptr(cb, code_ptr),
Target::Label(label_idx) => jz_label(cb, label_idx),
Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"),
@@ -690,21 +780,24 @@ impl Assembler
}
Insn::Jnz(target) => {
- match compile_side_exit(*target, self, ocb) {
+ match compile_side_exit(*target, self, ocb)? {
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jnz_ptr(cb, code_ptr),
Target::Label(label_idx) => jnz_label(cb, label_idx),
Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"),
}
}
- Insn::Jo(target) => {
- match compile_side_exit(*target, self, ocb) {
+ Insn::Jo(target) |
+ Insn::JoMul(target) => {
+ match compile_side_exit(*target, self, ocb)? {
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jo_ptr(cb, code_ptr),
Target::Label(label_idx) => jo_label(cb, label_idx),
Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"),
}
}
+ Insn::Joz(..) | Insn::Jonz(..) => unreachable!("Joz/Jonz should be unused for now"),
+
// Atomically increment a counter at a given memory location
Insn::IncrCounter { mem, value } => {
assert!(matches!(mem, Opnd::Mem(_)));
@@ -716,28 +809,28 @@ impl Assembler
Insn::Breakpoint => int3(cb),
Insn::CSelZ { truthy, falsy, out } => {
- emit_csel(cb, *truthy, *falsy, *out, cmovnz);
+ emit_csel(cb, *truthy, *falsy, *out, cmovz, cmovnz);
},
Insn::CSelNZ { truthy, falsy, out } => {
- emit_csel(cb, *truthy, *falsy, *out, cmovz);
+ emit_csel(cb, *truthy, *falsy, *out, cmovnz, cmovz);
},
Insn::CSelE { truthy, falsy, out } => {
- emit_csel(cb, *truthy, *falsy, *out, cmovne);
+ emit_csel(cb, *truthy, *falsy, *out, cmove, cmovne);
},
Insn::CSelNE { truthy, falsy, out } => {
- emit_csel(cb, *truthy, *falsy, *out, cmove);
+ emit_csel(cb, *truthy, *falsy, *out, cmovne, cmove);
},
Insn::CSelL { truthy, falsy, out } => {
- emit_csel(cb, *truthy, *falsy, *out, cmovge);
+ emit_csel(cb, *truthy, *falsy, *out, cmovl, cmovge);
},
Insn::CSelLE { truthy, falsy, out } => {
- emit_csel(cb, *truthy, *falsy, *out, cmovg);
+ emit_csel(cb, *truthy, *falsy, *out, cmovle, cmovg);
},
Insn::CSelG { truthy, falsy, out } => {
- emit_csel(cb, *truthy, *falsy, *out, cmovle);
+ emit_csel(cb, *truthy, *falsy, *out, cmovg, cmovle);
},
Insn::CSelGE { truthy, falsy, out } => {
- emit_csel(cb, *truthy, *falsy, *out, cmovl);
+ emit_csel(cb, *truthy, *falsy, *out, cmovge, cmovl);
}
Insn::LiveReg { .. } => (), // just a reg alloc signal, no code
Insn::PadInvalPatch => {
@@ -758,11 +851,25 @@ impl Assembler
}
}
- gc_offsets
+ // Error if we couldn't write out everything
+ if cb.has_dropped_bytes() {
+ return None
+ } else {
+ // No bytes dropped, so the pos markers point to valid code
+ for (insn_idx, pos) in pos_markers {
+ if let Insn::PosMarker(callback) = self.insns.get(insn_idx).unwrap() {
+ callback(pos, &cb);
+ } else {
+ panic!("non-PosMarker in pos_markers insn_idx={insn_idx} {self:?}");
+ }
+ }
+
+ return Some(gc_offsets)
+ }
}
/// Optimize and compile the stored instructions
- pub fn compile_with_regs(self, cb: &mut CodeBlock, ocb: Option<&mut OutlinedCb>, regs: Vec<Reg>) -> Vec<u32> {
+ pub fn compile_with_regs(self, cb: &mut CodeBlock, ocb: Option<&mut OutlinedCb>, regs: Vec<Reg>) -> Option<(CodePtr, Vec<u32>)> {
let asm = self.x86_split();
let mut asm = asm.alloc_regs(regs);
@@ -773,15 +880,18 @@ impl Assembler
}
let mut ocb = ocb; // for &mut
+ let start_ptr = cb.get_write_ptr();
let gc_offsets = asm.x86_emit(cb, &mut ocb);
- if cb.has_dropped_bytes() {
- cb.clear_labels();
- } else {
+ if let (Some(gc_offsets), false) = (gc_offsets, cb.has_dropped_bytes()) {
cb.link_labels();
- }
- gc_offsets
+ Some((start_ptr, gc_offsets))
+ } else {
+ cb.clear_labels();
+
+ None
+ }
}
}
@@ -1055,4 +1165,158 @@ mod tests {
assert_eq!(format!("{:x}", cb), "4983f540");
}
+
+ #[test]
+ fn test_reorder_c_args_no_cycle() {
+ let (mut asm, mut cb) = setup_asm();
+
+ asm.ccall(0 as _, vec![
+ C_ARG_OPNDS[0], // mov rdi, rdi (optimized away)
+ C_ARG_OPNDS[1], // mov rsi, rsi (optimized away)
+ ]);
+ asm.compile_with_num_regs(&mut cb, 0);
+
+ assert_disasm!(cb, "b800000000ffd0", {"
+ 0x0: mov eax, 0
+ 0x5: call rax
+ "});
+ }
+
+ #[test]
+ fn test_reorder_c_args_single_cycle() {
+ let (mut asm, mut cb) = setup_asm();
+
+ // rdi and rsi form a cycle
+ asm.ccall(0 as _, vec![
+ C_ARG_OPNDS[1], // mov rdi, rsi
+ C_ARG_OPNDS[0], // mov rsi, rdi
+ C_ARG_OPNDS[2], // mov rdx, rdx (optimized away)
+ ]);
+ asm.compile_with_num_regs(&mut cb, 0);
+
+ assert_disasm!(cb, "4989f34889fe4c89dfb800000000ffd0", {"
+ 0x0: mov r11, rsi
+ 0x3: mov rsi, rdi
+ 0x6: mov rdi, r11
+ 0x9: mov eax, 0
+ 0xe: call rax
+ "});
+ }
+
+ #[test]
+ fn test_reorder_c_args_two_cycles() {
+ let (mut asm, mut cb) = setup_asm();
+
+ // rdi and rsi form a cycle, and rdx and rcx form another cycle
+ asm.ccall(0 as _, vec![
+ C_ARG_OPNDS[1], // mov rdi, rsi
+ C_ARG_OPNDS[0], // mov rsi, rdi
+ C_ARG_OPNDS[3], // mov rdx, rcx
+ C_ARG_OPNDS[2], // mov rcx, rdx
+ ]);
+ asm.compile_with_num_regs(&mut cb, 0);
+
+ assert_disasm!(cb, "4989f34889fe4c89df4989cb4889d14c89dab800000000ffd0", {"
+ 0x0: mov r11, rsi
+ 0x3: mov rsi, rdi
+ 0x6: mov rdi, r11
+ 0x9: mov r11, rcx
+ 0xc: mov rcx, rdx
+ 0xf: mov rdx, r11
+ 0x12: mov eax, 0
+ 0x17: call rax
+ "});
+ }
+
+ #[test]
+ fn test_reorder_c_args_large_cycle() {
+ let (mut asm, mut cb) = setup_asm();
+
+ // rdi, rsi, and rdx form a cycle
+ asm.ccall(0 as _, vec![
+ C_ARG_OPNDS[1], // mov rdi, rsi
+ C_ARG_OPNDS[2], // mov rsi, rdx
+ C_ARG_OPNDS[0], // mov rdx, rdi
+ ]);
+ asm.compile_with_num_regs(&mut cb, 0);
+
+ assert_disasm!(cb, "4989f34889d64889fa4c89dfb800000000ffd0", {"
+ 0x0: mov r11, rsi
+ 0x3: mov rsi, rdx
+ 0x6: mov rdx, rdi
+ 0x9: mov rdi, r11
+ 0xc: mov eax, 0
+ 0x11: call rax
+ "});
+ }
+
+ #[test]
+ fn test_reorder_c_args_with_insn_out() {
+ let (mut asm, mut cb) = setup_asm();
+
+ let rax = asm.load(Opnd::UImm(1));
+ let rcx = asm.load(Opnd::UImm(2));
+ let rdx = asm.load(Opnd::UImm(3));
+ // rcx and rdx form a cycle
+ asm.ccall(0 as _, vec![
+ rax, // mov rdi, rax
+ rcx, // mov rsi, rcx
+ rcx, // mov rdx, rcx
+ rdx, // mov rcx, rdx
+ ]);
+ asm.compile_with_num_regs(&mut cb, 3);
+
+ assert_disasm!(cb, "b801000000b902000000ba030000004889c74889ce4989cb4889d14c89dab800000000ffd0", {"
+ 0x0: mov eax, 1
+ 0x5: mov ecx, 2
+ 0xa: mov edx, 3
+ 0xf: mov rdi, rax
+ 0x12: mov rsi, rcx
+ 0x15: mov r11, rcx
+ 0x18: mov rcx, rdx
+ 0x1b: mov rdx, r11
+ 0x1e: mov eax, 0
+ 0x23: call rax
+ "});
+ }
+
+ #[test]
+ fn test_cmov_mem() {
+ let (mut asm, mut cb) = setup_asm();
+
+ let top = Opnd::mem(64, SP, 0);
+ let ary_opnd = SP;
+ let array_len_opnd = Opnd::mem(64, SP, 16);
+
+ asm.cmp(array_len_opnd, 1.into());
+ let elem_opnd = asm.csel_g(Opnd::mem(64, ary_opnd, 0), Qnil.into());
+ asm.mov(top, elem_opnd);
+
+ asm.compile_with_num_regs(&mut cb, 1);
+
+ assert_disasm!(cb, "48837b1001b804000000480f4f03488903", {"
+ 0x0: cmp qword ptr [rbx + 0x10], 1
+ 0x5: mov eax, 4
+ 0xa: cmovg rax, qword ptr [rbx]
+ 0xe: mov qword ptr [rbx], rax
+ "});
+ }
+
+ #[test]
+ fn test_csel_split() {
+ let (mut asm, mut cb) = setup_asm();
+
+ let stack_top = Opnd::mem(64, SP, 0);
+ let elem_opnd = asm.csel_ne(VALUE(0x7f22c88d1930).into(), Qnil.into());
+ asm.mov(stack_top, elem_opnd);
+
+ asm.compile_with_num_regs(&mut cb, 3);
+
+ assert_disasm!(cb, "48b830198dc8227f0000b904000000480f44c1488903", {"
+ 0x0: movabs rax, 0x7f22c88d1930
+ 0xa: mov ecx, 4
+ 0xf: cmove rax, rcx
+ 0x13: mov qword ptr [rbx], rax
+ "});
+ }
}
diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs
index 6bee890f4e..ab5f00db1b 100644
--- a/yjit/src/codegen.rs
+++ b/yjit/src/codegen.rs
@@ -1,4 +1,4 @@
-// We use the YARV bytecode constants which have a CRuby-style name
+// We use the YARV bytecode constants which have a CRuby-style name
#![allow(non_upper_case_globals)]
use crate::asm::*;
@@ -16,11 +16,13 @@ use std::cell::Cell;
use std::cmp;
use std::cmp::min;
use std::collections::HashMap;
+use std::ffi::c_void;
use std::ffi::CStr;
use std::mem;
-use std::os::raw::{c_int};
+use std::os::raw::c_int;
use std::ptr;
use std::rc::Rc;
+use std::cell::RefCell;
use std::slice;
pub use crate::virtualmem::CodePtr;
@@ -28,6 +30,7 @@ pub use crate::virtualmem::CodePtr;
/// Status returned by code generation functions
#[derive(PartialEq, Debug)]
enum CodegenStatus {
+ SkipNextInsn,
KeepCompiling,
EndBlock,
}
@@ -43,7 +46,7 @@ type InsnGenFn = fn(
/// Represents a [core::Block] while we build it.
pub struct JITState {
/// Instruction sequence for the compiling block
- iseq: IseqPtr,
+ pub iseq: IseqPtr,
/// The iseq index of the first instruction in the block
starting_insn_idx: IseqIdx,
@@ -95,8 +98,20 @@ pub struct JITState {
/// not been written to for the block to be valid.
pub stable_constant_names_assumption: Option<*const ID>,
+ /// A list of classes that are not supposed to have a singleton class.
+ pub no_singleton_class_assumptions: Vec<VALUE>,
+
+ /// When true, the block is valid only when base pointer is equal to environment pointer.
+ pub no_ep_escape: bool,
+
/// When true, the block is valid only when there is a total of one ractor running
pub block_assumes_single_ractor: bool,
+
+ /// Address range for Linux perf's [JIT interface](https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/jit-interface.txt)
+ perf_map: Rc::<RefCell::<Vec<(CodePtr, Option<CodePtr>, String)>>>,
+
+ /// Stack of symbol names for --yjit-perf
+ perf_stack: Vec<String>,
}
impl JITState {
@@ -104,7 +119,7 @@ impl JITState {
JITState {
iseq: blockid.iseq,
starting_insn_idx: blockid.idx,
- starting_ctx: starting_ctx.clone(),
+ starting_ctx,
output_ptr,
insn_idx: 0,
opcode: 0,
@@ -117,7 +132,11 @@ impl JITState {
method_lookup_assumptions: vec![],
bop_assumptions: vec![],
stable_constant_names_assumption: None,
+ no_singleton_class_assumptions: vec![],
+ no_ep_escape: false,
block_assumes_single_ractor: false,
+ perf_map: Rc::default(),
+ perf_stack: vec![],
}
}
@@ -146,7 +165,7 @@ impl JITState {
}
pub fn get_starting_ctx(&self) -> Context {
- self.starting_ctx.clone()
+ self.starting_ctx
}
pub fn get_arg(&self, arg_idx: isize) -> VALUE {
@@ -156,6 +175,23 @@ impl JITState {
unsafe { *(self.pc.offset(arg_idx + 1)) }
}
+ /// Return true if the current ISEQ could escape an environment.
+ ///
+ /// As of vm_push_frame(), EP is always equal to BP. However, after pushing
+ /// a frame, some ISEQ setups call vm_bind_update_env(), which redirects EP.
+ /// Also, some method calls escape the environment to the heap.
+ fn escapes_ep(&self) -> bool {
+ match unsafe { get_iseq_body_type(self.iseq) } {
+ // <main> frame is always associated to TOPLEVEL_BINDING.
+ ISEQ_TYPE_MAIN |
+ // Kernel#eval uses a heap EP when a Binding argument is not nil.
+ ISEQ_TYPE_EVAL => true,
+ // If this ISEQ has previously escaped EP, give up the optimization.
+ _ if iseq_escapes_ep(self.iseq) => true,
+ _ => false,
+ }
+ }
+
// Get the index of the next instruction
fn next_insn_idx(&self) -> u16 {
self.insn_idx + insn_len(self.get_opcode()) as u16
@@ -214,23 +250,180 @@ impl JITState {
}
}
- pub fn assume_method_lookup_stable(&mut self, asm: &mut Assembler, ocb: &mut OutlinedCb, cme: CmePtr) {
- jit_ensure_block_entry_exit(self, asm, ocb);
+ pub fn assume_expected_cfunc(
+ &mut self,
+ asm: &mut Assembler,
+ ocb: &mut OutlinedCb,
+ class: VALUE,
+ method: ID,
+ cfunc: *mut c_void,
+ ) -> bool {
+ let cme = unsafe { rb_callable_method_entry(class, method) };
+
+ if cme.is_null() {
+ return false;
+ }
+
+ let def_type = unsafe { get_cme_def_type(cme) };
+ if def_type != VM_METHOD_TYPE_CFUNC {
+ return false;
+ }
+ if unsafe { get_mct_func(get_cme_def_body_cfunc(cme)) } != cfunc {
+ return false;
+ }
+
+ self.assume_method_lookup_stable(asm, ocb, cme);
+
+ true
+ }
+
+ pub fn assume_method_lookup_stable(&mut self, asm: &mut Assembler, ocb: &mut OutlinedCb, cme: CmePtr) -> Option<()> {
+ jit_ensure_block_entry_exit(self, asm, ocb)?;
self.method_lookup_assumptions.push(cme);
+
+ Some(())
+ }
+
+ /// Assume that objects of a given class will have no singleton class.
+ /// Return true if there has been no such singleton class since boot
+ /// and we can safely invalidate it.
+ pub fn assume_no_singleton_class(&mut self, asm: &mut Assembler, ocb: &mut OutlinedCb, klass: VALUE) -> bool {
+ if jit_ensure_block_entry_exit(self, asm, ocb).is_none() {
+ return false; // out of space, give up
+ }
+ if has_singleton_class_of(klass) {
+ return false; // we've seen a singleton class. disable the optimization to avoid an invalidation loop.
+ }
+ self.no_singleton_class_assumptions.push(klass);
+ true
+ }
+
+ /// Assume that base pointer is equal to environment pointer in the current ISEQ.
+ /// Return true if it's safe to assume so.
+ fn assume_no_ep_escape(&mut self, asm: &mut Assembler, ocb: &mut OutlinedCb) -> bool {
+ if jit_ensure_block_entry_exit(self, asm, ocb).is_none() {
+ return false; // out of space, give up
+ }
+ if self.escapes_ep() {
+ return false; // EP has been escaped in this ISEQ. disable the optimization to avoid an invalidation loop.
+ }
+ self.no_ep_escape = true;
+ true
}
fn get_cfp(&self) -> *mut rb_control_frame_struct {
unsafe { get_ec_cfp(self.ec) }
}
- pub fn assume_stable_constant_names(&mut self, asm: &mut Assembler, ocb: &mut OutlinedCb, id: *const ID) {
- jit_ensure_block_entry_exit(self, asm, ocb);
+ pub fn assume_stable_constant_names(&mut self, asm: &mut Assembler, ocb: &mut OutlinedCb, id: *const ID) -> Option<()> {
+ jit_ensure_block_entry_exit(self, asm, ocb)?;
self.stable_constant_names_assumption = Some(id);
+
+ Some(())
}
pub fn queue_outgoing_branch(&mut self, branch: PendingBranchRef) {
self.pending_outgoing.push(branch)
}
+
+ /// Push a symbol for --yjit-perf
+ fn perf_symbol_push(&mut self, asm: &mut Assembler, symbol_name: &str) {
+ if !self.perf_stack.is_empty() {
+ self.perf_symbol_range_end(asm);
+ }
+ self.perf_stack.push(symbol_name.to_string());
+ self.perf_symbol_range_start(asm, symbol_name);
+ }
+
+ /// Pop the stack-top symbol for --yjit-perf
+ fn perf_symbol_pop(&mut self, asm: &mut Assembler) {
+ self.perf_symbol_range_end(asm);
+ self.perf_stack.pop();
+ if let Some(symbol_name) = self.perf_stack.get(0) {
+ self.perf_symbol_range_start(asm, symbol_name);
+ }
+ }
+
+ /// Mark the start address of a symbol to be reported to perf
+ fn perf_symbol_range_start(&self, asm: &mut Assembler, symbol_name: &str) {
+ let symbol_name = format!("[JIT] {}", symbol_name);
+ let syms = self.perf_map.clone();
+ asm.pos_marker(move |start, _| syms.borrow_mut().push((start, None, symbol_name.clone())));
+ }
+
+ /// Mark the end address of a symbol to be reported to perf
+ fn perf_symbol_range_end(&self, asm: &mut Assembler) {
+ let syms = self.perf_map.clone();
+ asm.pos_marker(move |end, _| {
+ if let Some((_, ref mut end_store, _)) = syms.borrow_mut().last_mut() {
+ assert_eq!(None, *end_store);
+ *end_store = Some(end);
+ }
+ });
+ }
+
+ /// Flush addresses and symbols to /tmp/perf-{pid}.map
+ fn flush_perf_symbols(&self, cb: &CodeBlock) {
+ assert_eq!(0, self.perf_stack.len());
+ let path = format!("/tmp/perf-{}.map", std::process::id());
+ let mut f = std::fs::File::options().create(true).append(true).open(path).unwrap();
+ for sym in self.perf_map.borrow().iter() {
+ if let (start, Some(end), name) = sym {
+ // In case the code straddles two pages, part of it belongs to the symbol.
+ for (inline_start, inline_end) in cb.writable_addrs(*start, *end) {
+ use std::io::Write;
+ let code_size = inline_end - inline_start;
+ writeln!(f, "{inline_start:x} {code_size:x} {name}").unwrap();
+ }
+ }
+ }
+ }
+
+ /// Return true if we're compiling a send-like instruction, not an opt_* instruction.
+ pub fn is_sendish(&self) -> bool {
+ match unsafe { rb_iseq_opcode_at_pc(self.iseq, self.pc) } as u32 {
+ YARVINSN_send |
+ YARVINSN_opt_send_without_block |
+ YARVINSN_invokesuper => true,
+ _ => false,
+ }
+ }
+}
+
+/// Macro to call jit.perf_symbol_push() without evaluating arguments when
+/// the option is turned off, which is useful for avoiding string allocation.
+macro_rules! jit_perf_symbol_push {
+ ($jit:expr, $asm:expr, $symbol_name:expr, $perf_map:expr) => {
+ if get_option!(perf_map) == Some($perf_map) {
+ $jit.perf_symbol_push($asm, $symbol_name);
+ }
+ };
+}
+
+/// Macro to call jit.perf_symbol_pop(), for consistency with jit_perf_symbol_push!().
+macro_rules! jit_perf_symbol_pop {
+ ($jit:expr, $asm:expr, $perf_map:expr) => {
+ if get_option!(perf_map) == Some($perf_map) {
+ $jit.perf_symbol_pop($asm);
+ }
+ };
+}
+
+/// Macro to push and pop a perf symbol around a function call.
+macro_rules! perf_call {
+ // perf_call!("prefix: ", func(...)) uses "prefix: func" as a symbol.
+ ($prefix:expr, $func_name:ident($jit:expr, $asm:expr$(, $arg:expr)*$(,)?) ) => {
+ {
+ jit_perf_symbol_push!($jit, $asm, &format!("{}{}", $prefix, stringify!($func_name)), PerfMap::Codegen);
+ let ret = $func_name($jit, $asm, $($arg),*);
+ jit_perf_symbol_pop!($jit, $asm, PerfMap::Codegen);
+ ret
+ }
+ };
+ // perf_call! { func(...) } uses "func" as a symbol.
+ { $func_name:ident($jit:expr, $asm:expr$(, $arg:expr)*$(,)?) } => {
+ perf_call!("", $func_name($jit, $asm, $($arg),*))
+ };
}
use crate::codegen::JCCKinds::*;
@@ -241,14 +434,20 @@ pub enum JCCKinds {
JCC_JNZ,
JCC_JZ,
JCC_JE,
+ JCC_JB,
JCC_JBE,
JCC_JNA,
+ JCC_JNAE,
+ JCC_JO_MUL,
}
#[inline(always)]
fn gen_counter_incr(asm: &mut Assembler, counter: Counter) {
+ // Assert that default counters are not incremented by generated code as this would impact performance
+ assert!(!DEFAULT_COUNTERS.contains(&counter), "gen_counter_incr incremented {:?}", counter);
+
if get_option!(gen_stats) {
- asm.comment(&format!("increment counter {}", counter.get_name()));
+ asm_comment!(asm, "increment counter {}", counter.get_name());
let ptr = get_counter_ptr(&counter.get_name());
let ptr_reg = asm.load(Opnd::const_ptr(ptr as *const u8));
let counter_opnd = Opnd::mem(64, ptr_reg, 0);
@@ -267,7 +466,7 @@ fn jit_save_pc(jit: &JITState, asm: &mut Assembler) {
pc.offset(cur_insn_len)
};
- asm.comment("save PC to CFP");
+ asm_comment!(asm, "save PC to CFP");
asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_PC), Opnd::const_ptr(ptr as *const u8));
}
@@ -276,42 +475,115 @@ fn jit_save_pc(jit: &JITState, asm: &mut Assembler) {
/// Note: this will change the current value of REG_SP,
/// which could invalidate memory operands
fn gen_save_sp(asm: &mut Assembler) {
- asm.spill_temps();
- if asm.ctx.get_sp_offset() != 0 {
- asm.comment("save SP to CFP");
- let stack_pointer = asm.ctx.sp_opnd(0);
+ gen_save_sp_with_offset(asm, 0);
+}
+
+/// Save the current SP + offset on the CFP
+fn gen_save_sp_with_offset(asm: &mut Assembler, offset: i8) {
+ if asm.ctx.get_sp_offset() != -offset {
+ asm_comment!(asm, "save SP to CFP");
+ let stack_pointer = asm.ctx.sp_opnd(offset as i32);
let sp_addr = asm.lea(stack_pointer);
asm.mov(SP, sp_addr);
let cfp_sp_opnd = Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SP);
asm.mov(cfp_sp_opnd, SP);
- asm.ctx.set_sp_offset(0);
+ asm.ctx.set_sp_offset(-offset);
+ }
+}
+
+/// Basically jit_prepare_non_leaf_call(), but this registers the current PC
+/// to lazily push a C method frame when it's necessary.
+fn jit_prepare_lazy_frame_call(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ cme: *const rb_callable_method_entry_t,
+ recv_opnd: YARVOpnd,
+) -> bool {
+ // We can use this only when the receiver is on stack.
+ let recv_idx = match recv_opnd {
+ StackOpnd(recv_idx) => recv_idx,
+ _ => unreachable!("recv_opnd must be on stack, but got: {:?}", recv_opnd),
+ };
+
+ // Get the next PC. jit_save_pc() saves that PC.
+ let pc: *mut VALUE = unsafe {
+ let cur_insn_len = insn_len(jit.get_opcode()) as isize;
+ jit.get_pc().offset(cur_insn_len)
+ };
+
+ let pc_to_cfunc = CodegenGlobals::get_pc_to_cfunc();
+ match pc_to_cfunc.get(&pc) {
+ Some(&(other_cme, _)) if other_cme != cme => {
+ // Bail out if it's not the only cme on this callsite.
+ incr_counter!(lazy_frame_failure);
+ return false;
+ }
+ _ => {
+ // Let rb_yjit_lazy_push_frame() lazily push a C frame on this PC.
+ incr_counter!(lazy_frame_count);
+ pc_to_cfunc.insert(pc, (cme, recv_idx));
+ }
+ }
+
+ // Save the PC to trigger a lazy frame push, and save the SP to get the receiver.
+ // The C func may call a method that doesn't raise, so prepare for invalidation too.
+ jit_prepare_non_leaf_call(jit, asm);
+
+ // Make sure we're ready for calling rb_vm_push_cfunc_frame().
+ let cfunc_argc = unsafe { get_mct_argc(get_cme_def_body_cfunc(cme)) };
+ if cfunc_argc != -1 {
+ assert_eq!(recv_idx as i32, cfunc_argc); // verify the receiver index if possible
}
+ assert!(asm.get_leaf_ccall()); // It checks the stack canary we set for known_cfunc_codegen.
+
+ true
}
-/// jit_save_pc() + gen_save_sp(). Should be used before calling a routine that
-/// could:
+/// jit_save_pc() + gen_save_sp(). Should be used before calling a routine that could:
/// - Perform GC allocation
/// - Take the VM lock through RB_VM_LOCK_ENTER()
/// - Perform Ruby method call
-fn jit_prepare_routine_call(
+///
+/// If the routine doesn't call arbitrary methods, use jit_prepare_call_with_gc() instead.
+fn jit_prepare_non_leaf_call(
jit: &mut JITState,
asm: &mut Assembler
) {
- jit.record_boundary_patch_point = true;
- jit_save_pc(jit, asm);
- gen_save_sp(asm);
+ // Prepare for GC. Setting PC also prepares for showing a backtrace.
+ jit.record_boundary_patch_point = true; // VM lock could trigger invalidation
+ jit_save_pc(jit, asm); // for allocation tracing
+ gen_save_sp(asm); // protect objects from GC
// In case the routine calls Ruby methods, it can set local variables
- // through Kernel#binding and other means.
- asm.ctx.clear_local_types();
+ // through Kernel#binding, rb_debug_inspector API, and other means.
+ asm.clear_local_types();
+}
+
+/// jit_save_pc() + gen_save_sp(). Should be used before calling a routine that could:
+/// - Perform GC allocation
+/// - Take the VM lock through RB_VM_LOCK_ENTER()
+fn jit_prepare_call_with_gc(
+ jit: &mut JITState,
+ asm: &mut Assembler
+) {
+ jit.record_boundary_patch_point = true; // VM lock could trigger invalidation
+ jit_save_pc(jit, asm); // for allocation tracing
+ gen_save_sp(asm); // protect objects from GC
+
+ // Expect a leaf ccall(). You should use jit_prepare_non_leaf_call() if otherwise.
+ asm.expect_leaf_ccall();
}
/// Record the current codeblock write position for rewriting into a jump into
/// the outlined block later. Used to implement global code invalidation.
fn record_global_inval_patch(asm: &mut Assembler, outline_block_target_pos: CodePtr) {
+ // We add a padding before pos_marker so that the previous patch will not overlap this.
+ // jump_to_next_insn() puts a patch point at the end of the block in fallthrough cases.
+ // In the fallthrough case, the next block should start with the same Context, so the
+ // patch is fine, but it should not overlap another patch.
asm.pad_inval_patch();
- asm.pos_marker(move |code_ptr| {
- CodegenGlobals::push_global_inval_patch(code_ptr, outline_block_target_pos);
+ asm.pos_marker(move |code_ptr, cb| {
+ CodegenGlobals::push_global_inval_patch(code_ptr, outline_block_target_pos, cb);
});
}
@@ -322,14 +594,36 @@ fn verify_ctx(jit: &JITState, ctx: &Context) {
unsafe { CStr::from_ptr(rb_obj_info(val)).to_str().unwrap() }
}
+ // Some types such as CString only assert the class field of the object
+ // when there has never been a singleton class created for objects of that class.
+ // Once there is a singleton class created they become their weaker
+ // `T*` variant, and we more objects should pass the verification.
+ fn relax_type_with_singleton_class_assumption(ty: Type) -> Type {
+ if let Type::CString | Type::CArray | Type::CHash = ty {
+ if has_singleton_class_of(ty.known_class().unwrap()) {
+ match ty {
+ Type::CString => return Type::TString,
+ Type::CArray => return Type::TArray,
+ Type::CHash => return Type::THash,
+ _ => (),
+ }
+ }
+ }
+
+ ty
+ }
+
// Only able to check types when at current insn
assert!(jit.at_current_insn());
let self_val = jit.peek_at_self();
let self_val_type = Type::from(self_val);
+ let learned_self_type = ctx.get_opnd_type(SelfOpnd);
+ let learned_self_type = relax_type_with_singleton_class_assumption(learned_self_type);
+
// Verify self operand type
- if self_val_type.diff(ctx.get_opnd_type(SelfOpnd)) == TypeDiff::Incompatible {
+ if self_val_type.diff(learned_self_type) == TypeDiff::Incompatible {
panic!(
"verify_ctx: ctx self type ({:?}) incompatible with actual value of self {}",
ctx.get_opnd_type(SelfOpnd),
@@ -340,12 +634,15 @@ fn verify_ctx(jit: &JITState, ctx: &Context) {
// Verify stack operand types
let top_idx = cmp::min(ctx.get_stack_size(), MAX_TEMP_TYPES as u8);
for i in 0..top_idx {
- let (learned_mapping, learned_type) = ctx.get_opnd_mapping(StackOpnd(i));
+ let learned_mapping = ctx.get_opnd_mapping(StackOpnd(i));
+ let learned_type = ctx.get_opnd_type(StackOpnd(i));
+ let learned_type = relax_type_with_singleton_class_assumption(learned_type);
+
let stack_val = jit.peek_at_stack(ctx, i as isize);
let val_type = Type::from(stack_val);
- match learned_mapping {
- TempMapping::MapToSelf => {
+ match learned_mapping.get_kind() {
+ TempMappingKind::MapToSelf => {
if self_val != stack_val {
panic!(
"verify_ctx: stack value was mapped to self, but values did not match!\n stack: {}\n self: {}",
@@ -354,8 +651,8 @@ fn verify_ctx(jit: &JITState, ctx: &Context) {
);
}
}
- TempMapping::MapToLocal(local_idx) => {
- let local_idx: u8 = local_idx.into();
+ TempMappingKind::MapToLocal => {
+ let local_idx: u8 = learned_mapping.get_local_idx();
let local_val = jit.peek_at_local(local_idx.into());
if local_val != stack_val {
panic!(
@@ -366,7 +663,7 @@ fn verify_ctx(jit: &JITState, ctx: &Context) {
);
}
}
- TempMapping::MapToStack => {}
+ TempMappingKind::MapToStack => {}
}
// If the actual type differs from the learned type
@@ -385,6 +682,7 @@ fn verify_ctx(jit: &JITState, ctx: &Context) {
let top_idx: usize = cmp::min(local_table_size as usize, MAX_TEMP_TYPES);
for i in 0..top_idx {
let learned_type = ctx.get_local_type(i);
+ let learned_type = relax_type_with_singleton_class_assumption(learned_type);
let local_val = jit.peek_at_local(i as i32);
let local_type = Type::from(local_val);
@@ -403,14 +701,13 @@ fn verify_ctx(jit: &JITState, ctx: &Context) {
// to the interpreter when it cannot service a stub by generating new code.
// Before coming here, branch_stub_hit() takes care of fully reconstructing
// interpreter state.
-fn gen_code_for_exit_from_stub(ocb: &mut OutlinedCb) -> CodePtr {
+fn gen_stub_exit(ocb: &mut OutlinedCb) -> Option<CodePtr> {
let ocb = ocb.unwrap();
- let code_ptr = ocb.get_write_ptr();
let mut asm = Assembler::new();
gen_counter_incr(&mut asm, Counter::exit_from_branch_stub);
- asm.comment("exit from branch stub");
+ asm_comment!(asm, "exit from branch stub");
asm.cpop_into(SP);
asm.cpop_into(EC);
asm.cpop_into(CFP);
@@ -419,9 +716,7 @@ fn gen_code_for_exit_from_stub(ocb: &mut OutlinedCb) -> CodePtr {
asm.cret(Qundef.into());
- asm.compile(ocb, None);
-
- code_ptr
+ asm.compile(ocb, None).map(|(code_ptr, _)| code_ptr)
}
/// Generate an exit to return to the interpreter
@@ -429,7 +724,13 @@ fn gen_exit(exit_pc: *mut VALUE, asm: &mut Assembler) {
#[cfg(all(feature = "disasm", not(test)))]
{
let opcode = unsafe { rb_vm_insn_addr2opcode((*exit_pc).as_ptr()) };
- asm.comment(&format!("exit to interpreter on {}", insn_name(opcode as usize)));
+ asm_comment!(asm, "exit to interpreter on {}", insn_name(opcode as usize));
+ }
+
+ if asm.ctx.is_return_landing() {
+ asm.mov(SP, Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SP));
+ let top = asm.stack_push(Type::Unknown);
+ asm.mov(top, C_RET_OPND);
}
// Spill stack temps before returning to the interpreter
@@ -458,9 +759,9 @@ fn gen_exit(exit_pc: *mut VALUE, asm: &mut Assembler) {
vec![Opnd::const_ptr(exit_pc as *const u8)]
);
- // If --yjit-trace-exits option is enabled, record the exit stack
- // while recording the side exits.
- if get_option!(gen_trace_exits) {
+ // If --yjit-trace-exits is enabled, record the exit stack while recording
+ // the side exits. TraceExits::Counter is handled by gen_counted_exit().
+ if get_option!(trace_exits) == Some(TraceExits::All) {
asm.ccall(
rb_yjit_record_exit_stack as *const u8,
vec![Opnd::const_ptr(exit_pc as *const u8)]
@@ -489,56 +790,66 @@ fn gen_exit(exit_pc: *mut VALUE, asm: &mut Assembler) {
/// moment, so there is one unique side exit for each context. Note that
/// it's incorrect to jump to the side exit after any ctx stack push operations
/// since they change the logic required for reconstructing interpreter state.
-pub fn gen_outlined_exit(exit_pc: *mut VALUE, ctx: &Context, ocb: &mut OutlinedCb) -> CodePtr {
+pub fn gen_outlined_exit(exit_pc: *mut VALUE, ctx: &Context, ocb: &mut OutlinedCb) -> Option<CodePtr> {
let mut cb = ocb.unwrap();
- let exit_code = cb.get_write_ptr();
let mut asm = Assembler::new();
- asm.ctx = ctx.clone();
+ asm.ctx = *ctx;
asm.set_reg_temps(ctx.get_reg_temps());
gen_exit(exit_pc, &mut asm);
- asm.compile(&mut cb, None);
-
- exit_code
+ asm.compile(&mut cb, None).map(|(code_ptr, _)| code_ptr)
}
/// Get a side exit. Increment a counter in it if --yjit-stats is enabled.
-pub fn gen_counted_exit(side_exit: CodePtr, ocb: &mut OutlinedCb, counter: Option<Counter>) -> CodePtr {
+pub fn gen_counted_exit(exit_pc: *mut VALUE, side_exit: CodePtr, ocb: &mut OutlinedCb, counter: Option<Counter>) -> Option<CodePtr> {
// The counter is only incremented when stats are enabled
if !get_option!(gen_stats) {
- return side_exit;
+ return Some(side_exit);
}
let counter = match counter {
Some(counter) => counter,
- None => return side_exit,
+ None => return Some(side_exit),
};
- let ocb = ocb.unwrap();
- let code_ptr = ocb.get_write_ptr();
-
let mut asm = Assembler::new();
- // Load the pointer into a register
- asm.comment(&format!("increment counter {}", counter.get_name()));
- let ptr_reg = asm.load(Opnd::const_ptr(get_counter_ptr(&counter.get_name()) as *const u8));
- let counter_opnd = Opnd::mem(64, ptr_reg, 0);
+ // Increment a counter
+ gen_counter_incr(&mut asm, counter);
- // Increment and store the updated value
- asm.incr_counter(counter_opnd, Opnd::UImm(1));
+ // Trace a counted exit if --yjit-trace-exits=counter is given.
+ // TraceExits::All is handled by gen_exit().
+ if get_option!(trace_exits) == Some(TraceExits::CountedExit(counter)) {
+ with_caller_saved_temp_regs(&mut asm, |asm| {
+ asm.ccall(rb_yjit_record_exit_stack as *const u8, vec![Opnd::const_ptr(exit_pc as *const u8)]);
+ });
+ }
// Jump to the existing side exit
asm.jmp(Target::CodePtr(side_exit));
- asm.compile(ocb, None);
- code_ptr
+ let ocb = ocb.unwrap();
+ asm.compile(ocb, None).map(|(code_ptr, _)| code_ptr)
+}
+
+/// Preserve caller-saved stack temp registers during the call of a given block
+fn with_caller_saved_temp_regs<F, R>(asm: &mut Assembler, block: F) -> R where F: FnOnce(&mut Assembler) -> R {
+ for &reg in caller_saved_temp_regs() {
+ asm.cpush(Opnd::Reg(reg)); // save stack temps
+ }
+ let ret = block(asm);
+ for &reg in caller_saved_temp_regs().rev() {
+ asm.cpop_into(Opnd::Reg(reg)); // restore stack temps
+ }
+ ret
}
// Ensure that there is an exit for the start of the block being compiled.
// Block invalidation uses this exit.
-pub fn jit_ensure_block_entry_exit(jit: &mut JITState, asm: &mut Assembler, ocb: &mut OutlinedCb) {
+#[must_use]
+pub fn jit_ensure_block_entry_exit(jit: &mut JITState, asm: &mut Assembler, ocb: &mut OutlinedCb) -> Option<()> {
if jit.block_entry_exit.is_some() {
- return;
+ return Some(());
}
let block_starting_context = &jit.get_starting_ctx();
@@ -546,25 +857,26 @@ pub fn jit_ensure_block_entry_exit(jit: &mut JITState, asm: &mut Assembler, ocb:
// If we're compiling the first instruction in the block.
if jit.insn_idx == jit.starting_insn_idx {
// Generate the exit with the cache in Assembler.
- let side_exit_context = SideExitContext { pc: jit.pc, ctx: block_starting_context.clone() };
+ let side_exit_context = SideExitContext::new(jit.pc, *block_starting_context);
let entry_exit = asm.get_side_exit(&side_exit_context, None, ocb);
- jit.block_entry_exit = Some(entry_exit);
+ jit.block_entry_exit = Some(entry_exit?);
} else {
let block_entry_pc = unsafe { rb_iseq_pc_at_idx(jit.iseq, jit.starting_insn_idx.into()) };
- jit.block_entry_exit = Some(gen_outlined_exit(block_entry_pc, block_starting_context, ocb));
+ jit.block_entry_exit = Some(gen_outlined_exit(block_entry_pc, block_starting_context, ocb)?);
}
+
+ Some(())
}
// Landing code for when c_return tracing is enabled. See full_cfunc_return().
-fn gen_full_cfunc_return(ocb: &mut OutlinedCb) -> CodePtr {
+fn gen_full_cfunc_return(ocb: &mut OutlinedCb) -> Option<CodePtr> {
let ocb = ocb.unwrap();
- let code_ptr = ocb.get_write_ptr();
let mut asm = Assembler::new();
// This chunk of code expects REG_EC to be filled properly and
// RAX to contain the return value of the C method.
- asm.comment("full cfunc return");
+ asm_comment!(asm, "full cfunc return");
asm.ccall(
rb_full_cfunc_return as *const u8,
vec![EC, C_RET_OPND]
@@ -582,16 +894,13 @@ fn gen_full_cfunc_return(ocb: &mut OutlinedCb) -> CodePtr {
asm.cret(Qundef.into());
- asm.compile(ocb, None);
-
- return code_ptr;
+ asm.compile(ocb, None).map(|(code_ptr, _)| code_ptr)
}
/// Generate a continuation for leave that exits to the interpreter at REG_CFP->pc.
/// This is used by gen_leave() and gen_entry_prologue()
-fn gen_leave_exit(ocb: &mut OutlinedCb) -> CodePtr {
+fn gen_leave_exit(ocb: &mut OutlinedCb) -> Option<CodePtr> {
let ocb = ocb.unwrap();
- let code_ptr = ocb.get_write_ptr();
let mut asm = Assembler::new();
// gen_leave() fully reconstructs interpreter state and leaves the
@@ -601,7 +910,7 @@ fn gen_leave_exit(ocb: &mut OutlinedCb) -> CodePtr {
// Every exit to the interpreter should be counted
gen_counter_incr(&mut asm, Counter::leave_interp_return);
- asm.comment("exit from leave");
+ asm_comment!(asm, "exit from leave");
asm.cpop_into(SP);
asm.cpop_into(EC);
asm.cpop_into(CFP);
@@ -610,9 +919,41 @@ fn gen_leave_exit(ocb: &mut OutlinedCb) -> CodePtr {
asm.cret(ret_opnd);
- asm.compile(ocb, None);
+ asm.compile(ocb, None).map(|(code_ptr, _)| code_ptr)
+}
+
+// Increment SP and transfer the execution to the interpreter after jit_exec_exception().
+// On jit_exec_exception(), you need to return Qundef to keep executing caller non-FINISH
+// frames on the interpreter. You also need to increment SP to push the return value to
+// the caller's stack, which is different from gen_stub_exit().
+fn gen_leave_exception(ocb: &mut OutlinedCb) -> Option<CodePtr> {
+ let ocb = ocb.unwrap();
+ let mut asm = Assembler::new();
+
+ // gen_leave() leaves the return value in C_RET_OPND before coming here.
+ let ruby_ret_val = asm.live_reg_opnd(C_RET_OPND);
+
+ // Every exit to the interpreter should be counted
+ gen_counter_incr(&mut asm, Counter::leave_interp_return);
+
+ asm_comment!(asm, "push return value through cfp->sp");
+ let cfp_sp = Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SP);
+ let sp = asm.load(cfp_sp);
+ asm.mov(Opnd::mem(64, sp, 0), ruby_ret_val);
+ let new_sp = asm.add(sp, SIZEOF_VALUE.into());
+ asm.mov(cfp_sp, new_sp);
+
+ asm_comment!(asm, "exit from exception");
+ asm.cpop_into(SP);
+ asm.cpop_into(EC);
+ asm.cpop_into(CFP);
+
+ asm.frame_teardown();
- return code_ptr;
+ // Execute vm_exec_core
+ asm.cret(Qundef.into());
+
+ asm.compile(ocb, None).map(|(code_ptr, _)| code_ptr)
}
// Generate a runtime guard that ensures the PC is at the expected
@@ -634,7 +975,7 @@ pub fn gen_entry_chain_guard(
let expected_pc = unsafe { rb_iseq_pc_at_idx(iseq, insn_idx.into()) };
let expected_pc_opnd = Opnd::const_ptr(expected_pc as *const u8);
- asm.comment("guard expected PC");
+ asm_comment!(asm, "guard expected PC");
asm.cmp(pc_opnd, expected_pc_opnd);
asm.mark_entry_start(&entry);
@@ -645,14 +986,22 @@ pub fn gen_entry_chain_guard(
/// Compile an interpreter entry block to be inserted into an iseq
/// Returns None if compilation fails.
-pub fn gen_entry_prologue(cb: &mut CodeBlock, ocb: &mut OutlinedCb, iseq: IseqPtr, insn_idx: u16) -> Option<CodePtr> {
+/// If jit_exception is true, compile JIT code for handling exceptions.
+/// See [jit_compile_exception] for details.
+pub fn gen_entry_prologue(
+ cb: &mut CodeBlock,
+ ocb: &mut OutlinedCb,
+ iseq: IseqPtr,
+ insn_idx: u16,
+ jit_exception: bool,
+) -> Option<CodePtr> {
let code_ptr = cb.get_write_ptr();
let mut asm = Assembler::new();
if get_option_ref!(dump_disasm).is_some() {
- asm.comment(&format!("YJIT entry point: {}", iseq_get_location(iseq, 0)));
+ asm_comment!(asm, "YJIT entry point: {}", iseq_get_location(iseq, 0));
} else {
- asm.comment("YJIT entry");
+ asm_comment!(asm, "YJIT entry");
}
asm.frame_setup();
@@ -670,25 +1019,42 @@ pub fn gen_entry_prologue(cb: &mut CodeBlock, ocb: &mut OutlinedCb, iseq: IseqPt
asm.mov(SP, Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SP));
// Setup cfp->jit_return
- asm.mov(
- Opnd::mem(64, CFP, RUBY_OFFSET_CFP_JIT_RETURN),
- Opnd::const_ptr(CodegenGlobals::get_leave_exit_code().raw_ptr()),
- );
+ // If this is an exception handler entry point
+ if jit_exception {
+ // On jit_exec_exception(), it's NOT safe to return a non-Qundef value
+ // from a non-FINISH frame. This function fixes that problem.
+ // See [jit_compile_exception] for details.
+ asm.ccall(
+ rb_yjit_set_exception_return as *mut u8,
+ vec![
+ CFP,
+ Opnd::const_ptr(CodegenGlobals::get_leave_exit_code().raw_ptr(cb)),
+ Opnd::const_ptr(CodegenGlobals::get_leave_exception_code().raw_ptr(cb)),
+ ],
+ );
+ } else {
+ // On jit_exec() or JIT_EXEC(), it's safe to return a non-Qundef value
+ // on the entry frame. See [jit_compile] for details.
+ asm.mov(
+ Opnd::mem(64, CFP, RUBY_OFFSET_CFP_JIT_RETURN),
+ Opnd::const_ptr(CodegenGlobals::get_leave_exit_code().raw_ptr(cb)),
+ );
+ }
- // We're compiling iseqs that we *expect* to start at `insn_idx`. But in
- // the case of optional parameters, the interpreter can set the pc to a
- // different location depending on the optional parameters. If an iseq
- // has optional parameters, we'll add a runtime check that the PC we've
+ // We're compiling iseqs that we *expect* to start at `insn_idx`.
+ // But in the case of optional parameters or when handling exceptions,
+ // the interpreter can set the pc to a different location. For
+ // such scenarios, we'll add a runtime check that the PC we've
// compiled for is the same PC that the interpreter wants us to run with.
// If they don't match, then we'll jump to an entry stub and generate
// another PC check and entry there.
- let pending_entry = if unsafe { get_iseq_flags_has_opt(iseq) } {
+ let pending_entry = if unsafe { get_iseq_flags_has_opt(iseq) } || jit_exception {
Some(gen_entry_chain_guard(&mut asm, ocb, iseq, insn_idx)?)
} else {
None
};
- asm.compile(cb, Some(ocb));
+ asm.compile(cb, Some(ocb))?;
if cb.has_dropped_bytes() {
None
@@ -716,7 +1082,7 @@ fn gen_check_ints(
) {
// Check for interrupts
// see RUBY_VM_CHECK_INTS(ec) macro
- asm.comment("RUBY_VM_CHECK_INTS(ec)");
+ asm_comment!(asm, "RUBY_VM_CHECK_INTS(ec)");
// Not checking interrupt_mask since it's zero outside finalize_deferred_heap_pages,
// signal_exec, or rb_postponed_job_flush.
@@ -732,11 +1098,11 @@ fn jump_to_next_insn(
jit: &mut JITState,
asm: &mut Assembler,
ocb: &mut OutlinedCb,
-) {
- // Reset the depth since in current usages we only ever jump to to
+) -> Option<()> {
+ // Reset the depth since in current usages we only ever jump to
// chain_depth > 0 from the same instruction.
- let mut reset_depth = asm.ctx.clone();
- reset_depth.reset_chain_depth();
+ let mut reset_depth = asm.ctx;
+ reset_depth.reset_chain_depth_and_defer();
let jump_block = BlockId {
iseq: jit.iseq,
@@ -745,14 +1111,15 @@ fn jump_to_next_insn(
// We are at the end of the current instruction. Record the boundary.
if jit.record_boundary_patch_point {
+ jit.record_boundary_patch_point = false;
let exit_pc = unsafe { jit.pc.offset(insn_len(jit.opcode).try_into().unwrap()) };
let exit_pos = gen_outlined_exit(exit_pc, &reset_depth, ocb);
- record_global_inval_patch(asm, exit_pos);
- jit.record_boundary_patch_point = false;
+ record_global_inval_patch(asm, exit_pos?);
}
// Generate the jump instruction
gen_direct_jump(jit, &reset_depth, jump_block, asm);
+ Some(())
}
// Compile a sequence of bytecode instructions for a given basic block version.
@@ -788,7 +1155,7 @@ pub fn gen_single_block(
let mut insn_idx: IseqIdx = blockid.idx;
// Initialize a JIT state object
- let mut jit = JITState::new(blockid, ctx.clone(), cb.get_write_ptr(), ec);
+ let mut jit = JITState::new(blockid, ctx, cb.get_write_ptr(), ec);
jit.iseq = blockid.iseq;
// Create a backend assembler instance
@@ -799,8 +1166,23 @@ pub fn gen_single_block(
if get_option_ref!(dump_disasm).is_some() {
let blockid_idx = blockid.idx;
let chain_depth = if asm.ctx.get_chain_depth() > 0 { format!("(chain_depth: {})", asm.ctx.get_chain_depth()) } else { "".to_string() };
- asm.comment(&format!("Block: {} {}", iseq_get_location(blockid.iseq, blockid_idx), chain_depth));
- asm.comment(&format!("reg_temps: {:08b}", ctx.get_reg_temps().as_u8()));
+ asm_comment!(asm, "Block: {} {}", iseq_get_location(blockid.iseq, blockid_idx), chain_depth);
+ asm_comment!(asm, "reg_temps: {:08b}", asm.ctx.get_reg_temps().as_u8());
+ }
+
+ // Mark the start of an ISEQ for --yjit-perf
+ jit_perf_symbol_push!(jit, &mut asm, &get_iseq_name(iseq), PerfMap::ISEQ);
+
+ if asm.ctx.is_return_landing() {
+ // Continuation of the end of gen_leave().
+ // Reload REG_SP for the current frame and transfer the return value
+ // to the stack top.
+ asm.mov(SP, Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SP));
+
+ let top = asm.stack_push(Type::Unknown);
+ asm.mov(top, C_RET_OPND);
+
+ asm.ctx.clear_return_landing();
}
// For each instruction to compile
@@ -837,7 +1219,7 @@ pub fn gen_single_block(
// If previous instruction requested to record the boundary
if jit.record_boundary_patch_point {
// Generate an exit to this instruction and record it
- let exit_pos = gen_outlined_exit(jit.pc, &asm.ctx, ocb);
+ let exit_pos = gen_outlined_exit(jit.pc, &asm.ctx, ocb).ok_or(())?;
record_global_inval_patch(&mut asm, exit_pos);
jit.record_boundary_patch_point = false;
}
@@ -847,16 +1229,16 @@ pub fn gen_single_block(
verify_ctx(&jit, &asm.ctx);
}
+ // :count-placement:
+ // Count bytecode instructions that execute in generated code.
+ // Note that the increment happens even when the output takes side exit.
+ gen_counter_incr(&mut asm, Counter::yjit_insns_count);
+
// Lookup the codegen function for this instruction
let mut status = None;
if let Some(gen_fn) = get_gen_fn(VALUE(opcode)) {
- // :count-placement:
- // Count bytecode instructions that execute in generated code.
- // Note that the increment happens even when the output takes side exit.
- gen_counter_incr(&mut asm, Counter::exec_instruction);
-
// Add a comment for the name of the YARV instruction
- asm.comment(&format!("Insn: {:04} {} (stack_size: {})", insn_idx, insn_name(opcode), asm.ctx.get_stack_size()));
+ asm_comment!(asm, "Insn: {:04} {} (stack_size: {})", insn_idx, insn_name(opcode), asm.ctx.get_stack_size());
// If requested, dump instructions for debugging
if get_option!(dump_insns) {
@@ -865,7 +1247,12 @@ pub fn gen_single_block(
}
// Call the code generation function
+ jit_perf_symbol_push!(jit, &mut asm, &insn_name(opcode), PerfMap::Codegen);
status = gen_fn(&mut jit, &mut asm, ocb);
+ jit_perf_symbol_pop!(jit, &mut asm, PerfMap::Codegen);
+
+ #[cfg(debug_assertions)]
+ assert!(!asm.get_leaf_ccall(), "ccall() wasn't used after leaf_ccall was set in {}", insn_name(opcode));
}
// If we can't compile this instruction
@@ -891,11 +1278,18 @@ pub fn gen_single_block(
// For now, reset the chain depth after each instruction as only the
// first instruction in the block can concern itself with the depth.
- asm.ctx.reset_chain_depth();
+ asm.ctx.reset_chain_depth_and_defer();
// Move to the next instruction to compile
insn_idx += insn_len(opcode) as u16;
+ // Move past next instruction when instructed
+ if status == Some(SkipNextInsn) {
+ let next_pc = unsafe { rb_iseq_pc_at_idx(iseq, insn_idx.into()) };
+ let next_opcode: usize = unsafe { rb_iseq_opcode_at_pc(iseq, next_pc) }.try_into().unwrap();
+ insn_idx += insn_len(next_opcode) as u16;
+ }
+
// If the instruction terminates this block
if status == Some(EndBlock) {
break;
@@ -912,10 +1306,18 @@ pub fn gen_single_block(
asm.pad_inval_patch();
}
+ // Mark the end of an ISEQ for --yjit-perf
+ jit_perf_symbol_pop!(jit, &mut asm, PerfMap::ISEQ);
+
// Compile code into the code block
- let gc_offsets = asm.compile(cb, Some(ocb));
+ let (_, gc_offsets) = asm.compile(cb, Some(ocb)).ok_or(())?;
let end_addr = cb.get_write_ptr();
+ // Flush perf symbols after asm.compile() writes addresses
+ if get_option!(perf_map).is_some() {
+ jit.flush_perf_symbols(cb);
+ }
+
// If code for the block doesn't fit, fail
if cb.has_dropped_bytes() || ocb.unwrap().has_dropped_bytes() {
return Err(());
@@ -950,9 +1352,9 @@ fn gen_dup(
_ocb: &mut OutlinedCb,
) -> Option<CodegenStatus> {
let dup_val = asm.stack_opnd(0);
- let (mapping, tmp_type) = asm.ctx.get_opnd_mapping(dup_val.into());
+ let mapping = asm.ctx.get_opnd_mapping(dup_val.into());
- let loc0 = asm.stack_push_mapping((mapping, tmp_type));
+ let loc0 = asm.stack_push_mapping(mapping);
asm.mov(loc0, dup_val);
Some(KeepCompiling)
@@ -998,11 +1400,11 @@ fn gen_swap(
fn stack_swap(
asm: &mut Assembler,
- offset0: u16,
- offset1: u16,
+ offset0: i32,
+ offset1: i32,
) {
- let stack0_mem = asm.stack_opnd(offset0 as i32);
- let stack1_mem = asm.stack_opnd(offset1 as i32);
+ let stack0_mem = asm.stack_opnd(offset0);
+ let stack1_mem = asm.stack_opnd(offset1);
let mapping0 = asm.ctx.get_opnd_mapping(stack0_mem.into());
let mapping1 = asm.ctx.get_opnd_mapping(stack1_mem.into());
@@ -1034,7 +1436,7 @@ fn jit_putobject(asm: &mut Assembler, arg: VALUE) {
fn gen_putobject_int2fix(
jit: &mut JITState,
asm: &mut Assembler,
- _ocb: &mut OutlinedCb,
+ ocb: &mut OutlinedCb,
) -> Option<CodegenStatus> {
let opcode = jit.opcode;
let cst_val: usize = if opcode == YARVINSN_putobject_INT2FIX_0_.as_usize() {
@@ -1042,22 +1444,86 @@ fn gen_putobject_int2fix(
} else {
1
};
+ let cst_val = VALUE::fixnum_from_usize(cst_val);
+
+ if let Some(result) = fuse_putobject_opt_ltlt(jit, asm, cst_val, ocb) {
+ return Some(result);
+ }
- jit_putobject(asm, VALUE::fixnum_from_usize(cst_val));
+ jit_putobject(asm, cst_val);
Some(KeepCompiling)
}
fn gen_putobject(
jit: &mut JITState,
asm: &mut Assembler,
- _ocb: &mut OutlinedCb,
+ ocb: &mut OutlinedCb,
) -> Option<CodegenStatus> {
let arg: VALUE = jit.get_arg(0);
+ if let Some(result) = fuse_putobject_opt_ltlt(jit, asm, arg, ocb) {
+ return Some(result);
+ }
+
jit_putobject(asm, arg);
Some(KeepCompiling)
}
+/// Combine `putobject` and `opt_ltlt` together if profitable, for example when
+/// left shifting an integer by a constant amount.
+fn fuse_putobject_opt_ltlt(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ constant_object: VALUE,
+ ocb: &mut OutlinedCb,
+) -> Option<CodegenStatus> {
+ let next_opcode = unsafe { rb_vm_insn_addr2opcode(jit.pc.add(insn_len(jit.opcode).as_usize()).read().as_ptr()) };
+ if next_opcode == YARVINSN_opt_ltlt as i32 && constant_object.fixnum_p() {
+ // Untag the fixnum shift amount
+ let shift_amt = constant_object.as_isize() >> 1;
+ if shift_amt > 63 || shift_amt < 0 {
+ return None;
+ }
+ if !jit.at_current_insn() {
+ defer_compilation(jit, asm, ocb);
+ return Some(EndBlock);
+ }
+
+ let lhs = jit.peek_at_stack(&asm.ctx, 0);
+ if !lhs.fixnum_p() {
+ return None;
+ }
+
+ if !assume_bop_not_redefined(jit, asm, ocb, INTEGER_REDEFINED_OP_FLAG, BOP_LTLT) {
+ return None;
+ }
+
+ asm_comment!(asm, "integer left shift with rhs={shift_amt}");
+ let lhs = asm.stack_opnd(0);
+
+ // Guard that lhs is a fixnum if necessary
+ let lhs_type = asm.ctx.get_opnd_type(lhs.into());
+ if lhs_type != Type::Fixnum {
+ asm_comment!(asm, "guard arg0 fixnum");
+ asm.test(lhs, Opnd::UImm(RUBY_FIXNUM_FLAG as u64));
+
+ jit_chain_guard(
+ JCC_JZ,
+ jit,
+ asm,
+ ocb,
+ SEND_MAX_DEPTH,
+ Counter::guard_send_not_fixnums,
+ );
+ }
+
+ asm.stack_pop(1);
+ fixnum_left_shift_body(asm, lhs, shift_amt as u64);
+ return Some(SkipNextInsn);
+ }
+ return None;
+}
+
fn gen_putself(
_jit: &mut JITState,
asm: &mut Assembler,
@@ -1190,15 +1656,14 @@ fn gen_newarray(
let n = jit.get_arg(0).as_u32();
// Save the PC and SP because we are allocating
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_call_with_gc(jit, asm);
// If n is 0, then elts is never going to be read, so we can just pass null
let values_ptr = if n == 0 {
Opnd::UImm(0)
} else {
- asm.comment("load pointer to array elements");
- let offset_magnitude = (SIZEOF_VALUE as u32) * n;
- let values_opnd = asm.ctx.sp_opnd(-(offset_magnitude as isize));
+ asm_comment!(asm, "load pointer to array elements");
+ let values_opnd = asm.ctx.sp_opnd(-(n as i32));
asm.lea(values_opnd)
};
@@ -1228,7 +1693,7 @@ fn gen_duparray(
let ary = jit.get_arg(0);
// Save the PC and SP because we are allocating
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_call_with_gc(jit, asm);
// call rb_ary_resurrect(VALUE ary);
let new_ary = asm.ccall(
@@ -1251,12 +1716,12 @@ fn gen_duphash(
let hash = jit.get_arg(0);
// Save the PC and SP because we are allocating
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_call_with_gc(jit, asm);
// call rb_hash_resurrect(VALUE hash);
let hash = asm.ccall(rb_hash_resurrect as *const u8, vec![hash.into()]);
- let stack_ret = asm.stack_push(Type::Hash);
+ let stack_ret = asm.stack_push(Type::CHash);
asm.mov(stack_ret, hash);
Some(KeepCompiling)
@@ -1270,15 +1735,16 @@ fn gen_splatarray(
) -> Option<CodegenStatus> {
let flag = jit.get_arg(0).as_usize();
- // Save the PC and SP because the callee may allocate
+ // Save the PC and SP because the callee may call #to_a
// Note that this modifies REG_SP, which is why we do it first
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_non_leaf_call(jit, asm);
// Get the operands from the stack
- let ary_opnd = asm.stack_pop(1);
+ let ary_opnd = asm.stack_opnd(0);
// Call rb_vm_splat_array(flag, ary)
let ary = asm.ccall(rb_vm_splat_array as *const u8, vec![flag.into(), ary_opnd]);
+ asm.stack_pop(1); // Keep it on stack during ccall for GC
let stack_ret = asm.stack_push(Type::TArray);
asm.mov(stack_ret, ary);
@@ -1286,22 +1752,135 @@ fn gen_splatarray(
Some(KeepCompiling)
}
+// call to_hash on hash to keyword splat before converting block
+// e.g. foo(**object, &block)
+fn gen_splatkw(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ ocb: &mut OutlinedCb,
+) -> Option<CodegenStatus> {
+ // Defer compilation so we can specialize on a runtime hash operand
+ if !jit.at_current_insn() {
+ defer_compilation(jit, asm, ocb);
+ return Some(EndBlock);
+ }
+
+ let comptime_hash = jit.peek_at_stack(&asm.ctx, 1);
+ if comptime_hash.hash_p() {
+ // If a compile-time hash operand is T_HASH, just guard that it's T_HASH.
+ let hash_opnd = asm.stack_opnd(1);
+ guard_object_is_hash(asm, hash_opnd, hash_opnd.into(), Counter::splatkw_not_hash);
+ } else if comptime_hash.nil_p() {
+ // Speculate we'll see nil if compile-time hash operand is nil
+ let hash_opnd = asm.stack_opnd(1);
+ let hash_opnd_type = asm.ctx.get_opnd_type(hash_opnd.into());
+
+ if hash_opnd_type != Type::Nil {
+ asm.cmp(hash_opnd, Qnil.into());
+ asm.jne(Target::side_exit(Counter::splatkw_not_nil));
+
+ if Type::Nil.diff(hash_opnd_type) != TypeDiff::Incompatible {
+ asm.ctx.upgrade_opnd_type(hash_opnd.into(), Type::Nil);
+ }
+ }
+ } else {
+ // Otherwise, call #to_hash on the operand if it's not nil.
+
+ // Save the PC and SP because the callee may call #to_hash
+ jit_prepare_non_leaf_call(jit, asm);
+
+ // Get the operands from the stack
+ let block_opnd = asm.stack_opnd(0);
+ let block_type = asm.ctx.get_opnd_type(block_opnd.into());
+ let hash_opnd = asm.stack_opnd(1);
+
+ c_callable! {
+ fn to_hash_if_not_nil(mut obj: VALUE) -> VALUE {
+ if obj != Qnil {
+ obj = unsafe { rb_to_hash_type(obj) };
+ }
+ obj
+ }
+ }
+
+ let hash = asm.ccall(to_hash_if_not_nil as _, vec![hash_opnd]);
+ asm.stack_pop(2); // Keep it on stack during ccall for GC
+
+ let stack_ret = asm.stack_push(Type::Unknown);
+ asm.mov(stack_ret, hash);
+ asm.stack_push(block_type);
+ // Leave block_opnd spilled by ccall as is
+ asm.ctx.dealloc_temp_reg(asm.ctx.get_stack_size() - 1);
+ }
+
+ Some(KeepCompiling)
+}
+
// concat two arrays
fn gen_concatarray(
jit: &mut JITState,
asm: &mut Assembler,
_ocb: &mut OutlinedCb,
) -> Option<CodegenStatus> {
- // Save the PC and SP because the callee may allocate
+ // Save the PC and SP because the callee may call #to_a
// Note that this modifies REG_SP, which is why we do it first
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_non_leaf_call(jit, asm);
// Get the operands from the stack
- let ary2st_opnd = asm.stack_pop(1);
- let ary1_opnd = asm.stack_pop(1);
+ let ary2st_opnd = asm.stack_opnd(0);
+ let ary1_opnd = asm.stack_opnd(1);
// Call rb_vm_concat_array(ary1, ary2st)
let ary = asm.ccall(rb_vm_concat_array as *const u8, vec![ary1_opnd, ary2st_opnd]);
+ asm.stack_pop(2); // Keep them on stack during ccall for GC
+
+ let stack_ret = asm.stack_push(Type::TArray);
+ asm.mov(stack_ret, ary);
+
+ Some(KeepCompiling)
+}
+
+// concat second array to first array.
+// first argument must already be an array.
+// attempts to convert second object to array using to_a.
+fn gen_concattoarray(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ _ocb: &mut OutlinedCb,
+) -> Option<CodegenStatus> {
+ // Save the PC and SP because the callee may call #to_a
+ jit_prepare_non_leaf_call(jit, asm);
+
+ // Get the operands from the stack
+ let ary2_opnd = asm.stack_opnd(0);
+ let ary1_opnd = asm.stack_opnd(1);
+
+ let ary = asm.ccall(rb_vm_concat_to_array as *const u8, vec![ary1_opnd, ary2_opnd]);
+ asm.stack_pop(2); // Keep them on stack during ccall for GC
+
+ let stack_ret = asm.stack_push(Type::TArray);
+ asm.mov(stack_ret, ary);
+
+ Some(KeepCompiling)
+}
+
+// push given number of objects to array directly before.
+fn gen_pushtoarray(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ _ocb: &mut OutlinedCb,
+) -> Option<CodegenStatus> {
+ let num = jit.get_arg(0).as_u64();
+
+ // Save the PC and SP because the callee may allocate
+ jit_prepare_call_with_gc(jit, asm);
+
+ // Get the operands from the stack
+ let ary_opnd = asm.stack_opnd(num as i32);
+ let objp_opnd = asm.lea(asm.ctx.sp_opnd(-(num as i32)));
+
+ let ary = asm.ccall(rb_ary_cat as *const u8, vec![ary_opnd, objp_opnd, num.into()]);
+ asm.stack_pop(num as usize + 1); // Keep it on stack during ccall for GC
let stack_ret = asm.stack_push(Type::TArray);
asm.mov(stack_ret, ary);
@@ -1318,7 +1897,7 @@ fn gen_newrange(
let flag = jit.get_arg(0).as_usize();
// rb_range_new() allocates and can raise
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_non_leaf_call(jit, asm);
// val = rb_range_new(low, high, (int)flag);
let range_opnd = asm.ccall(
@@ -1348,7 +1927,7 @@ fn guard_object_is_heap(
return;
}
- asm.comment("guard object is heap");
+ asm_comment!(asm, "guard object is heap");
// Test that the object is not an immediate
asm.test(object, (RUBY_IMMEDIATE_MASK as u64).into());
@@ -1358,7 +1937,7 @@ fn guard_object_is_heap(
asm.cmp(object, Qfalse.into());
asm.je(Target::side_exit(counter));
- if object_type.diff(Type::UnknownHeap) != TypeDiff::Incompatible {
+ if Type::UnknownHeap.diff(object_type) != TypeDiff::Incompatible {
asm.ctx.upgrade_opnd_type(object_opnd, Type::UnknownHeap);
}
}
@@ -1375,12 +1954,12 @@ fn guard_object_is_array(
}
let object_reg = match object {
- Opnd::Reg(_) => object,
+ Opnd::InsnOut { .. } => object,
_ => asm.load(object),
};
guard_object_is_heap(asm, object_reg, object_opnd, counter);
- asm.comment("guard object is array");
+ asm_comment!(asm, "guard object is array");
// Pull out the type mask
let flags_opnd = Opnd::mem(VALUE_BITS, object_reg, RUBY_OFFSET_RBASIC_FLAGS);
@@ -1390,11 +1969,43 @@ fn guard_object_is_array(
asm.cmp(flags_opnd, (RUBY_T_ARRAY as u64).into());
asm.jne(Target::side_exit(counter));
- if object_type.diff(Type::TArray) != TypeDiff::Incompatible {
+ if Type::TArray.diff(object_type) != TypeDiff::Incompatible {
asm.ctx.upgrade_opnd_type(object_opnd, Type::TArray);
}
}
+fn guard_object_is_hash(
+ asm: &mut Assembler,
+ object: Opnd,
+ object_opnd: YARVOpnd,
+ counter: Counter,
+) {
+ let object_type = asm.ctx.get_opnd_type(object_opnd);
+ if object_type.is_hash() {
+ return;
+ }
+
+ let object_reg = match object {
+ Opnd::InsnOut { .. } => object,
+ _ => asm.load(object),
+ };
+ guard_object_is_heap(asm, object_reg, object_opnd, counter);
+
+ asm_comment!(asm, "guard object is hash");
+
+ // Pull out the type mask
+ let flags_opnd = Opnd::mem(VALUE_BITS, object_reg, RUBY_OFFSET_RBASIC_FLAGS);
+ let flags_opnd = asm.and(flags_opnd, (RUBY_T_MASK as u64).into());
+
+ // Compare the result with T_HASH
+ asm.cmp(flags_opnd, (RUBY_T_HASH as u64).into());
+ asm.jne(Target::side_exit(counter));
+
+ if Type::THash.diff(object_type) != TypeDiff::Incompatible {
+ asm.ctx.upgrade_opnd_type(object_opnd, Type::THash);
+ }
+}
+
fn guard_object_is_string(
asm: &mut Assembler,
object: Opnd,
@@ -1407,12 +2018,12 @@ fn guard_object_is_string(
}
let object_reg = match object {
- Opnd::Reg(_) => object,
+ Opnd::InsnOut { .. } => object,
_ => asm.load(object),
};
guard_object_is_heap(asm, object_reg, object_opnd, counter);
- asm.comment("guard object is string");
+ asm_comment!(asm, "guard object is string");
// Pull out the type mask
let flags_reg = asm.load(Opnd::mem(VALUE_BITS, object_reg, RUBY_OFFSET_RBASIC_FLAGS));
@@ -1422,7 +2033,7 @@ fn guard_object_is_string(
asm.cmp(flags_reg, Opnd::UImm(RUBY_T_STRING as u64));
asm.jne(Target::side_exit(counter));
- if object_type.diff(Type::TString) != TypeDiff::Incompatible {
+ if Type::TString.diff(object_type) != TypeDiff::Incompatible {
asm.ctx.upgrade_opnd_type(object_opnd, Type::TString);
}
}
@@ -1436,7 +2047,7 @@ fn guard_object_is_not_ruby2_keyword_hash(
object_opnd: Opnd,
counter: Counter,
) {
- asm.comment("guard object is not ruby2 keyword hash");
+ asm_comment!(asm, "guard object is not ruby2 keyword hash");
let not_ruby2_keyword = asm.new_label("not_ruby2_keyword");
asm.test(object_opnd, (RUBY_IMMEDIATE_MASK as u64).into());
@@ -1467,10 +2078,10 @@ fn guard_object_is_not_ruby2_keyword_hash(
fn gen_expandarray(
jit: &mut JITState,
asm: &mut Assembler,
- _ocb: &mut OutlinedCb,
+ ocb: &mut OutlinedCb,
) -> Option<CodegenStatus> {
// Both arguments are rb_num_t which is unsigned
- let num = jit.get_arg(0).as_usize();
+ let num = jit.get_arg(0).as_u32();
let flag = jit.get_arg(1).as_usize();
// If this instruction has the splat flag, then bail out.
@@ -1487,19 +2098,65 @@ fn gen_expandarray(
let array_opnd = asm.stack_opnd(0);
- // num is the number of requested values. If there aren't enough in the
- // array then we're going to push on nils.
- if asm.ctx.get_opnd_type(array_opnd.into()) == Type::Nil {
- asm.stack_pop(1); // pop after using the type info
- // special case for a, b = nil pattern
- // push N nils onto the stack
- for _ in 0..num {
+ // Defer compilation so we can specialize on a runtime `self`
+ if !jit.at_current_insn() {
+ defer_compilation(jit, asm, ocb);
+ return Some(EndBlock);
+ }
+
+ let comptime_recv = jit.peek_at_stack(&asm.ctx, 0);
+
+ // If the comptime receiver is not an array
+ if !unsafe { RB_TYPE_P(comptime_recv, RUBY_T_ARRAY) } {
+ // at compile time, ensure to_ary is not defined
+ let target_cme = unsafe { rb_callable_method_entry_or_negative(comptime_recv.class_of(), ID!(to_ary)) };
+ let cme_def_type = unsafe { get_cme_def_type(target_cme) };
+
+ // if to_ary is defined, return can't compile so to_ary can be called
+ if cme_def_type != VM_METHOD_TYPE_UNDEF {
+ gen_counter_incr(asm, Counter::expandarray_to_ary);
+ return None;
+ }
+
+ // invalidate compile block if to_ary is later defined
+ jit.assume_method_lookup_stable(asm, ocb, target_cme);
+
+ jit_guard_known_klass(
+ jit,
+ asm,
+ ocb,
+ comptime_recv.class_of(),
+ array_opnd,
+ array_opnd.into(),
+ comptime_recv,
+ SEND_MAX_DEPTH,
+ Counter::expandarray_not_array,
+ );
+
+ let opnd = asm.stack_pop(1); // pop after using the type info
+
+ // If we don't actually want any values, then just keep going
+ if num == 0 {
+ return Some(KeepCompiling);
+ }
+
+ // load opnd to avoid a race because we are also pushing onto the stack
+ let opnd = asm.load(opnd);
+
+ for _ in 1..num {
let push_opnd = asm.stack_push(Type::Nil);
asm.mov(push_opnd, Qnil.into());
}
+
+ let push_opnd = asm.stack_push(Type::Unknown);
+ asm.mov(push_opnd, opnd);
+
return Some(KeepCompiling);
}
+ // Get the compile-time array length
+ let comptime_len = unsafe { rb_yjit_array_len(comptime_recv) as u32 };
+
// Move the array from the stack and check that it's an array.
guard_object_is_array(
asm,
@@ -1507,42 +2164,62 @@ fn gen_expandarray(
array_opnd.into(),
Counter::expandarray_not_array,
);
- let array_opnd = asm.stack_pop(1); // pop after using the type info
// If we don't actually want any values, then just return.
if num == 0 {
+ asm.stack_pop(1); // pop the array
return Some(KeepCompiling);
}
+ let array_opnd = asm.stack_opnd(0);
let array_reg = asm.load(array_opnd);
let array_len_opnd = get_array_len(asm, array_reg);
- // Only handle the case where the number of values in the array is greater
- // than or equal to the number of values requested.
- asm.cmp(array_len_opnd, num.into());
- asm.jl(Target::side_exit(Counter::expandarray_rhs_too_small));
+ // Guard on the comptime/expected array length
+ if comptime_len >= num {
+ asm_comment!(asm, "guard array length >= {}", num);
+ asm.cmp(array_len_opnd, num.into());
+ jit_chain_guard(
+ JCC_JB,
+ jit,
+ asm,
+ ocb,
+ EXPANDARRAY_MAX_CHAIN_DEPTH,
+ Counter::expandarray_chain_max_depth,
+ );
- // Load the address of the embedded array into REG1.
- // (struct RArray *)(obj)->as.ary
- let array_reg = asm.load(array_opnd);
- let ary_opnd = asm.lea(Opnd::mem(VALUE_BITS, array_reg, RUBY_OFFSET_RARRAY_AS_ARY));
+ } else {
+ asm_comment!(asm, "guard array length == {}", comptime_len);
+ asm.cmp(array_len_opnd, comptime_len.into());
+ jit_chain_guard(
+ JCC_JNE,
+ jit,
+ asm,
+ ocb,
+ EXPANDARRAY_MAX_CHAIN_DEPTH,
+ Counter::expandarray_chain_max_depth,
+ );
+ }
- // Conditionally load the address of the heap array into REG1.
- // (struct RArray *)(obj)->as.heap.ptr
- let flags_opnd = Opnd::mem(VALUE_BITS, array_reg, RUBY_OFFSET_RBASIC_FLAGS);
- asm.test(flags_opnd, Opnd::UImm(RARRAY_EMBED_FLAG as u64));
- let heap_ptr_opnd = Opnd::mem(
- usize::BITS as u8,
- asm.load(array_opnd),
- RUBY_OFFSET_RARRAY_AS_HEAP_PTR,
- );
- let ary_opnd = asm.csel_nz(ary_opnd, heap_ptr_opnd);
+ let array_opnd = asm.stack_pop(1); // pop after using the type info
+
+ // Load the pointer to the embedded or heap array
+ let ary_opnd = if comptime_len > 0 {
+ let array_reg = asm.load(array_opnd);
+ Some(get_array_ptr(asm, array_reg))
+ } else {
+ None
+ };
// Loop backward through the array and push each element onto the stack.
for i in (0..num).rev() {
- let top = asm.stack_push(Type::Unknown);
- let offset = i32::try_from(i * SIZEOF_VALUE).unwrap();
- asm.mov(top, Opnd::mem(64, ary_opnd, offset));
+ let top = asm.stack_push(if i < comptime_len { Type::Unknown } else { Type::Nil });
+ let offset = i32::try_from(i * (SIZEOF_VALUE as u32)).unwrap();
+
+ // Missing elements are Qnil
+ asm_comment!(asm, "load array[{}]", i);
+ let elem_opnd = if i < comptime_len { Opnd::mem(64, ary_opnd.unwrap(), offset) } else { Qnil.into() };
+ asm.mov(top, elem_opnd);
}
Some(KeepCompiling)
@@ -1594,7 +2271,7 @@ fn gen_get_ep(asm: &mut Assembler, level: u32) -> Opnd {
// Gets the EP of the ISeq of the containing method, or "local level".
// Equivalent of GET_LEP() macro.
-fn gen_get_lep(jit: &mut JITState, asm: &mut Assembler) -> Opnd {
+fn gen_get_lep(jit: &JITState, asm: &mut Assembler) -> Opnd {
// Equivalent of get_lvar_level() in compile.c
fn get_lvar_level(iseq: IseqPtr) -> u32 {
if iseq == unsafe { rb_get_iseq_body_local_iseq(iseq) } {
@@ -1611,16 +2288,22 @@ fn gen_get_lep(jit: &mut JITState, asm: &mut Assembler) -> Opnd {
fn gen_getlocal_generic(
jit: &mut JITState,
asm: &mut Assembler,
+ ocb: &mut OutlinedCb,
ep_offset: u32,
level: u32,
) -> Option<CodegenStatus> {
- // Load environment pointer EP (level 0) from CFP
- let ep_opnd = gen_get_ep(asm, level);
+ let local_opnd = if level == 0 && jit.assume_no_ep_escape(asm, ocb) {
+ // Load the local using SP register
+ asm.ctx.ep_opnd(-(ep_offset as i32))
+ } else {
+ // Load environment pointer EP (level 0) from CFP
+ let ep_opnd = gen_get_ep(asm, level);
- // Load the local from the block
- // val = *(vm_get_ep(GET_EP(), level) - idx);
- let offs = -(SIZEOF_VALUE_I32 * ep_offset as i32);
- let local_opnd = Opnd::mem(64, ep_opnd, offs);
+ // Load the local from the block
+ // val = *(vm_get_ep(GET_EP(), level) - idx);
+ let offs = -(SIZEOF_VALUE_I32 * ep_offset as i32);
+ Opnd::mem(64, ep_opnd, offs)
+ };
// Write the local at SP
let stack_top = if level == 0 {
@@ -1638,56 +2321,95 @@ fn gen_getlocal_generic(
fn gen_getlocal(
jit: &mut JITState,
asm: &mut Assembler,
- _ocb: &mut OutlinedCb,
+ ocb: &mut OutlinedCb,
) -> Option<CodegenStatus> {
let idx = jit.get_arg(0).as_u32();
let level = jit.get_arg(1).as_u32();
- gen_getlocal_generic(jit, asm, idx, level)
+ gen_getlocal_generic(jit, asm, ocb, idx, level)
}
fn gen_getlocal_wc0(
jit: &mut JITState,
asm: &mut Assembler,
- _ocb: &mut OutlinedCb,
+ ocb: &mut OutlinedCb,
) -> Option<CodegenStatus> {
let idx = jit.get_arg(0).as_u32();
- gen_getlocal_generic(jit, asm, idx, 0)
+ gen_getlocal_generic(jit, asm, ocb, idx, 0)
}
fn gen_getlocal_wc1(
jit: &mut JITState,
asm: &mut Assembler,
- _ocb: &mut OutlinedCb,
+ ocb: &mut OutlinedCb,
) -> Option<CodegenStatus> {
let idx = jit.get_arg(0).as_u32();
- gen_getlocal_generic(jit, asm, idx, 1)
+ gen_getlocal_generic(jit, asm, ocb, idx, 1)
}
fn gen_setlocal_generic(
jit: &mut JITState,
asm: &mut Assembler,
+ ocb: &mut OutlinedCb,
ep_offset: u32,
level: u32,
) -> Option<CodegenStatus> {
let value_type = asm.ctx.get_opnd_type(StackOpnd(0));
- // Load environment pointer EP at level
- let ep_opnd = gen_get_ep(asm, level);
+ // Fallback because of write barrier
+ if asm.ctx.get_chain_depth() > 0 {
+ // Load environment pointer EP at level
+ let ep_opnd = gen_get_ep(asm, level);
- // Write barriers may be required when VM_ENV_FLAG_WB_REQUIRED is set, however write barriers
- // only affect heap objects being written. If we know an immediate value is being written we
- // can skip this check.
- if !value_type.is_imm() {
- // flags & VM_ENV_FLAG_WB_REQUIRED
+ // This function should not yield to the GC.
+ // void rb_vm_env_write(const VALUE *ep, int index, VALUE v)
+ let index = -(ep_offset as i64);
+ let value_opnd = asm.stack_opnd(0);
+ asm.ccall(
+ rb_vm_env_write as *const u8,
+ vec![
+ ep_opnd,
+ index.into(),
+ value_opnd,
+ ]
+ );
+ asm.stack_pop(1);
+
+ return Some(KeepCompiling);
+ }
+
+ let (flags_opnd, local_opnd) = if level == 0 && jit.assume_no_ep_escape(asm, ocb) {
+ // Load flags and the local using SP register
+ let local_opnd = asm.ctx.ep_opnd(-(ep_offset as i32));
+ let flags_opnd = asm.ctx.ep_opnd(VM_ENV_DATA_INDEX_FLAGS as i32);
+ (flags_opnd, local_opnd)
+ } else {
+ // Load flags and the local for the level
+ let ep_opnd = gen_get_ep(asm, level);
let flags_opnd = Opnd::mem(
64,
ep_opnd,
SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_FLAGS as i32,
);
+ (flags_opnd, Opnd::mem(64, ep_opnd, -SIZEOF_VALUE_I32 * ep_offset as i32))
+ };
+
+ // Write barriers may be required when VM_ENV_FLAG_WB_REQUIRED is set, however write barriers
+ // only affect heap objects being written. If we know an immediate value is being written we
+ // can skip this check.
+ if !value_type.is_imm() {
+ // flags & VM_ENV_FLAG_WB_REQUIRED
asm.test(flags_opnd, VM_ENV_FLAG_WB_REQUIRED.into());
// if (flags & VM_ENV_FLAG_WB_REQUIRED) != 0
- asm.jnz(Target::side_exit(Counter::setlocal_wb_required));
+ assert!(asm.ctx.get_chain_depth() == 0);
+ jit_chain_guard(
+ JCC_JNZ,
+ jit,
+ asm,
+ ocb,
+ 1,
+ Counter::setlocal_wb_required,
+ );
}
if level == 0 {
@@ -1699,8 +2421,7 @@ fn gen_setlocal_generic(
let stack_top = asm.stack_pop(1);
// Write the value at the environment pointer
- let offs = -(SIZEOF_VALUE_I32 * ep_offset as i32);
- asm.mov(Opnd::mem(64, ep_opnd, offs), stack_top);
+ asm.mov(local_opnd, stack_top);
Some(KeepCompiling)
}
@@ -1708,29 +2429,29 @@ fn gen_setlocal_generic(
fn gen_setlocal(
jit: &mut JITState,
asm: &mut Assembler,
- _ocb: &mut OutlinedCb,
+ ocb: &mut OutlinedCb,
) -> Option<CodegenStatus> {
let idx = jit.get_arg(0).as_u32();
let level = jit.get_arg(1).as_u32();
- gen_setlocal_generic(jit, asm, idx, level)
+ gen_setlocal_generic(jit, asm, ocb, idx, level)
}
fn gen_setlocal_wc0(
jit: &mut JITState,
asm: &mut Assembler,
- _ocb: &mut OutlinedCb,
+ ocb: &mut OutlinedCb,
) -> Option<CodegenStatus> {
let idx = jit.get_arg(0).as_u32();
- gen_setlocal_generic(jit, asm, idx, 0)
+ gen_setlocal_generic(jit, asm, ocb, idx, 0)
}
fn gen_setlocal_wc1(
jit: &mut JITState,
asm: &mut Assembler,
- _ocb: &mut OutlinedCb,
+ ocb: &mut OutlinedCb,
) -> Option<CodegenStatus> {
let idx = jit.get_arg(0).as_u32();
- gen_setlocal_generic(jit, asm, idx, 1)
+ gen_setlocal_generic(jit, asm, ocb, idx, 1)
}
// new hash initialized from top N values
@@ -1742,7 +2463,7 @@ fn gen_newhash(
let num: u64 = jit.get_arg(0).as_u64();
// Save the PC and SP because we are allocating
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_call_with_gc(jit, asm);
if num != 0 {
// val = rb_hash_new_with_size(num / 2);
@@ -1772,12 +2493,12 @@ fn gen_newhash(
asm.cpop_into(new_hash); // x86 alignment
asm.stack_pop(num.try_into().unwrap());
- let stack_ret = asm.stack_push(Type::Hash);
+ let stack_ret = asm.stack_push(Type::CHash);
asm.mov(stack_ret, new_hash);
} else {
// val = rb_hash_new();
let new_hash = asm.ccall(rb_hash_new as *const u8, vec![]);
- let stack_ret = asm.stack_push(Type::Hash);
+ let stack_ret = asm.stack_push(Type::CHash);
asm.mov(stack_ret, new_hash);
}
@@ -1792,11 +2513,32 @@ fn gen_putstring(
let put_val = jit.get_arg(0);
// Save the PC and SP because the callee will allocate
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_call_with_gc(jit, asm);
+
+ let str_opnd = asm.ccall(
+ rb_ec_str_resurrect as *const u8,
+ vec![EC, put_val.into(), 0.into()]
+ );
+
+ let stack_top = asm.stack_push(Type::CString);
+ asm.mov(stack_top, str_opnd);
+
+ Some(KeepCompiling)
+}
+
+fn gen_putchilledstring(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ _ocb: &mut OutlinedCb,
+) -> Option<CodegenStatus> {
+ let put_val = jit.get_arg(0);
+
+ // Save the PC and SP because the callee will allocate
+ jit_prepare_call_with_gc(jit, asm);
let str_opnd = asm.ccall(
rb_ec_str_resurrect as *const u8,
- vec![EC, put_val.into()]
+ vec![EC, put_val.into(), 1.into()]
);
let stack_top = asm.stack_push(Type::CString);
@@ -1805,6 +2547,34 @@ fn gen_putstring(
Some(KeepCompiling)
}
+fn gen_checkmatch(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ _ocb: &mut OutlinedCb,
+) -> Option<CodegenStatus> {
+ let flag = jit.get_arg(0).as_u32();
+
+ // rb_vm_check_match is not leaf unless flag is VM_CHECKMATCH_TYPE_WHEN.
+ // See also: leafness_of_checkmatch() and check_match()
+ if flag != VM_CHECKMATCH_TYPE_WHEN {
+ jit_prepare_non_leaf_call(jit, asm);
+ }
+
+ let pattern = asm.stack_opnd(0);
+ let target = asm.stack_opnd(1);
+
+ extern "C" {
+ fn rb_vm_check_match(ec: EcPtr, target: VALUE, pattern: VALUE, num: u32) -> VALUE;
+ }
+ let result = asm.ccall(rb_vm_check_match as *const u8, vec![EC, target, pattern, flag.into()]);
+ asm.stack_pop(2); // Keep them on stack during ccall for GC
+
+ let stack_ret = asm.stack_push(Type::Unknown);
+ asm.mov(stack_ret, result);
+
+ Some(KeepCompiling)
+}
+
// Push Qtrue or Qfalse depending on whether the given keyword was supplied by
// the caller
fn gen_checkkeyword(
@@ -1851,17 +2621,20 @@ fn jit_chain_guard(
jit: &mut JITState,
asm: &mut Assembler,
ocb: &mut OutlinedCb,
- depth_limit: i32,
+ depth_limit: u8,
counter: Counter,
) {
let target0_gen_fn = match jcc {
JCC_JNE | JCC_JNZ => BranchGenFn::JNZToTarget0,
JCC_JZ | JCC_JE => BranchGenFn::JZToTarget0,
JCC_JBE | JCC_JNA => BranchGenFn::JBEToTarget0,
+ JCC_JB | JCC_JNAE => BranchGenFn::JBToTarget0,
+ JCC_JO_MUL => BranchGenFn::JOMulToTarget0,
};
- if (asm.ctx.get_chain_depth() as i32) < depth_limit {
- let mut deeper = asm.ctx.clone();
+ if asm.ctx.get_chain_depth() < depth_limit {
+ // Rewind Context to use the stack_size at the beginning of this instruction.
+ let mut deeper = asm.ctx.with_stack_size(jit.stack_size_for_pc);
deeper.increment_chain_depth();
let bid = BlockId {
iseq: jit.iseq,
@@ -1874,68 +2647,26 @@ fn jit_chain_guard(
}
}
-// up to 5 different classes, and embedded or not for each
-pub const GET_IVAR_MAX_DEPTH: i32 = 10;
+// up to 8 different shapes for each
+pub const GET_IVAR_MAX_DEPTH: u8 = 8;
-// up to 5 different classes, and embedded or not for each
-pub const SET_IVAR_MAX_DEPTH: i32 = 10;
+// up to 8 different shapes for each
+pub const SET_IVAR_MAX_DEPTH: u8 = 8;
// hashes and arrays
-pub const OPT_AREF_MAX_CHAIN_DEPTH: i32 = 2;
+pub const OPT_AREF_MAX_CHAIN_DEPTH: u8 = 2;
-// up to 10 different classes
-pub const SEND_MAX_DEPTH: i32 = 20;
+// expandarray
+pub const EXPANDARRAY_MAX_CHAIN_DEPTH: u8 = 4;
-// up to 20 different methods for send
-pub const SEND_MAX_CHAIN_DEPTH: i32 = 20;
+// up to 5 different methods for send
+pub const SEND_MAX_DEPTH: u8 = 5;
// up to 20 different offsets for case-when
-pub const CASE_WHEN_MAX_DEPTH: i32 = 20;
+pub const CASE_WHEN_MAX_DEPTH: u8 = 20;
pub const MAX_SPLAT_LENGTH: i32 = 127;
-// Codegen for setting an instance variable.
-// Preconditions:
-// - receiver is in REG0
-// - receiver has the same class as CLASS_OF(comptime_receiver)
-// - no stack push or pops to ctx since the entry to the codegen of the instruction being compiled
-fn gen_set_ivar(
- jit: &mut JITState,
- asm: &mut Assembler,
- ivar_name: ID,
- flags: u32,
- argc: i32,
-) -> Option<CodegenStatus> {
-
- // This is a .send call and we need to adjust the stack
- if flags & VM_CALL_OPT_SEND != 0 {
- handle_opt_send_shift_stack(asm, argc);
- }
-
- // Save the PC and SP because the callee may allocate
- // Note that this modifies REG_SP, which is why we do it first
- jit_prepare_routine_call(jit, asm);
-
- // Get the operands from the stack
- let val_opnd = asm.stack_pop(1);
- let recv_opnd = asm.stack_pop(1);
-
- // Call rb_vm_set_ivar_id with the receiver, the ivar name, and the value
- let val = asm.ccall(
- rb_vm_set_ivar_id as *const u8,
- vec![
- recv_opnd,
- Opnd::UImm(ivar_name),
- val_opnd,
- ],
- );
-
- let out_opnd = asm.stack_push(Type::Unknown);
- asm.mov(out_opnd, val);
-
- Some(KeepCompiling)
-}
-
// Codegen for getting an instance variable.
// Preconditions:
// - receiver has the same class as CLASS_OF(comptime_receiver)
@@ -1944,7 +2675,7 @@ fn gen_get_ivar(
jit: &mut JITState,
asm: &mut Assembler,
ocb: &mut OutlinedCb,
- max_chain_depth: i32,
+ max_chain_depth: u8,
comptime_receiver: VALUE,
ivar_name: ID,
recv: Opnd,
@@ -1954,7 +2685,7 @@ fn gen_get_ivar(
// If recv isn't already a register, load it.
let recv = match recv {
- Opnd::Reg(_) => recv,
+ Opnd::InsnOut { .. } => recv,
_ => asm.load(recv),
};
@@ -1971,9 +2702,9 @@ fn gen_get_ivar(
// Check if the comptime receiver is a T_OBJECT
let receiver_t_object = unsafe { RB_TYPE_P(comptime_receiver, RUBY_T_OBJECT) };
// Use a general C call at the last chain to avoid exits on megamorphic shapes
- let last_chain = asm.ctx.get_chain_depth() as i32 == max_chain_depth - 1;
- if last_chain {
- gen_counter_incr(asm, Counter::get_ivar_max_depth);
+ let megamorphic = asm.ctx.get_chain_depth() >= max_chain_depth;
+ if megamorphic {
+ gen_counter_incr(asm, Counter::num_getivar_megamorphic);
}
// If the class uses the default allocator, instances should all be T_OBJECT
@@ -1981,13 +2712,13 @@ fn gen_get_ivar(
// Eventually, we can encode whether an object is T_OBJECT or not
// inside object shapes.
// too-complex shapes can't use index access, so we use rb_ivar_get for them too.
- if !receiver_t_object || uses_custom_allocator || comptime_receiver.shape_too_complex() || last_chain {
+ if !receiver_t_object || uses_custom_allocator || comptime_receiver.shape_too_complex() || megamorphic {
// General case. Call rb_ivar_get().
// VALUE rb_ivar_get(VALUE obj, ID id)
- asm.comment("call rb_ivar_get()");
+ asm_comment!(asm, "call rb_ivar_get()");
- // The function could raise exceptions.
- jit_prepare_routine_call(jit, asm);
+ // The function could raise RactorIsolationError.
+ jit_prepare_non_leaf_call(jit, asm);
let ivar_val = asm.ccall(rb_ivar_get as *const u8, vec![recv, Opnd::UImm(ivar_name)]);
@@ -2025,7 +2756,7 @@ fn gen_get_ivar(
let shape_id_offset = unsafe { rb_shape_id_offset() };
let shape_opnd = Opnd::mem(SHAPE_ID_NUM_BITS as u8, recv, shape_id_offset);
- asm.comment("guard shape");
+ asm_comment!(asm, "guard shape");
asm.cmp(shape_opnd, Opnd::UImm(expected_shape as u64));
jit_chain_guard(
JCC_JNE,
@@ -2054,7 +2785,7 @@ fn gen_get_ivar(
// See ROBJECT_IVPTR() from include/ruby/internal/core/robject.h
// Load the variable
- let offs = ROBJECT_OFFSET_AS_ARY + (ivar_index * SIZEOF_VALUE) as i32;
+ let offs = ROBJECT_OFFSET_AS_ARY as i32 + (ivar_index * SIZEOF_VALUE) as i32;
let ivar_opnd = Opnd::mem(64, recv, offs);
// Push the ivar on the stack
@@ -2064,7 +2795,7 @@ fn gen_get_ivar(
// Compile time value is *not* embedded.
// Get a pointer to the extended table
- let tbl_opnd = asm.load(Opnd::mem(64, recv, ROBJECT_OFFSET_AS_HEAP_IVPTR));
+ let tbl_opnd = asm.load(Opnd::mem(64, recv, ROBJECT_OFFSET_AS_HEAP_IVPTR as i32));
// Read the ivar from the extended table
let ivar_opnd = Opnd::mem(64, tbl_opnd, (SIZEOF_VALUE * ivar_index) as i32);
@@ -2126,22 +2857,22 @@ fn gen_write_iv(
if embed_test_result {
// Find the IV offset
- let offs = ROBJECT_OFFSET_AS_ARY + (ivar_index * SIZEOF_VALUE) as i32;
+ let offs = ROBJECT_OFFSET_AS_ARY as i32 + (ivar_index * SIZEOF_VALUE) as i32;
let ivar_opnd = Opnd::mem(64, recv, offs);
// Write the IV
- asm.comment("write IV");
+ asm_comment!(asm, "write IV");
asm.mov(ivar_opnd, set_value);
} else {
// Compile time value is *not* embedded.
// Get a pointer to the extended table
- let tbl_opnd = asm.load(Opnd::mem(64, recv, ROBJECT_OFFSET_AS_HEAP_IVPTR));
+ let tbl_opnd = asm.load(Opnd::mem(64, recv, ROBJECT_OFFSET_AS_HEAP_IVPTR as i32));
// Write the ivar in to the extended table
let ivar_opnd = Opnd::mem(64, tbl_opnd, (SIZEOF_VALUE * ivar_index) as i32);
- asm.comment("write IV");
+ asm_comment!(asm, "write IV");
asm.mov(ivar_opnd, set_value);
}
}
@@ -2158,7 +2889,32 @@ fn gen_setinstancevariable(
}
let ivar_name = jit.get_arg(0).as_u64();
+ let ic = jit.get_arg(1).as_ptr();
let comptime_receiver = jit.peek_at_self();
+ gen_set_ivar(
+ jit,
+ asm,
+ ocb,
+ comptime_receiver,
+ ivar_name,
+ SelfOpnd,
+ Some(ic),
+ )
+}
+
+/// Set an instance variable on setinstancevariable or attr_writer.
+/// It switches the behavior based on what recv_opnd is given.
+/// * SelfOpnd: setinstancevariable, which doesn't push a result onto the stack.
+/// * StackOpnd: attr_writer, which pushes a result onto the stack.
+fn gen_set_ivar(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ ocb: &mut OutlinedCb,
+ comptime_receiver: VALUE,
+ ivar_name: ID,
+ recv_opnd: YARVOpnd,
+ ic: Option<*const iseq_inline_iv_cache_entry>,
+) -> Option<CodegenStatus> {
let comptime_val_klass = comptime_receiver.class_of();
// If the comptime receiver is frozen, writing an IV will raise an exception
@@ -2168,7 +2924,7 @@ fn gen_setinstancevariable(
return None;
}
- let (_, stack_type) = asm.ctx.get_opnd_mapping(StackOpnd(0));
+ let stack_type = asm.ctx.get_opnd_type(StackOpnd(0));
// Check if the comptime class uses a custom allocator
let custom_allocator = unsafe { rb_get_alloc_func(comptime_val_klass) };
@@ -2182,50 +2938,101 @@ fn gen_setinstancevariable(
// Check if the comptime receiver is a T_OBJECT
let receiver_t_object = unsafe { RB_TYPE_P(comptime_receiver, RUBY_T_OBJECT) };
+ // Use a general C call at the last chain to avoid exits on megamorphic shapes
+ let megamorphic = asm.ctx.get_chain_depth() >= SET_IVAR_MAX_DEPTH;
+ if megamorphic {
+ gen_counter_incr(asm, Counter::num_setivar_megamorphic);
+ }
+
+ // Get the iv index
+ let shape_too_complex = comptime_receiver.shape_too_complex();
+ let ivar_index = if !shape_too_complex {
+ let shape_id = comptime_receiver.shape_id_of();
+ let shape = unsafe { rb_shape_get_shape_by_id(shape_id) };
+ let mut ivar_index: u32 = 0;
+ if unsafe { rb_shape_get_iv_index(shape, ivar_name, &mut ivar_index) } {
+ Some(ivar_index as usize)
+ } else {
+ None
+ }
+ } else {
+ None
+ };
+
+ // The current shape doesn't contain this iv, we need to transition to another shape.
+ let new_shape = if !shape_too_complex && receiver_t_object && ivar_index.is_none() {
+ let current_shape = comptime_receiver.shape_of();
+ let next_shape = unsafe { rb_shape_get_next(current_shape, comptime_receiver, ivar_name) };
+ let next_shape_id = unsafe { rb_shape_id(next_shape) };
+
+ // If the VM ran out of shapes, or this class generated too many leaf,
+ // it may be de-optimized into OBJ_TOO_COMPLEX_SHAPE (hash-table).
+ if next_shape_id == OBJ_TOO_COMPLEX_SHAPE_ID {
+ Some((next_shape_id, None, 0_usize))
+ } else {
+ let current_capacity = unsafe { (*current_shape).capacity };
+
+ // If the new shape has a different capacity, or is TOO_COMPLEX, we'll have to
+ // reallocate it.
+ let needs_extension = unsafe { (*current_shape).capacity != (*next_shape).capacity };
+
+ // We can write to the object, but we need to transition the shape
+ let ivar_index = unsafe { (*current_shape).next_iv_index } as usize;
+
+ let needs_extension = if needs_extension {
+ Some((current_capacity, unsafe { (*next_shape).capacity }))
+ } else {
+ None
+ };
+ Some((next_shape_id, needs_extension, ivar_index))
+ }
+ } else {
+ None
+ };
+ let new_shape_too_complex = matches!(new_shape, Some((OBJ_TOO_COMPLEX_SHAPE_ID, _, _)));
// If the receiver isn't a T_OBJECT, or uses a custom allocator,
// then just write out the IV write as a function call.
// too-complex shapes can't use index access, so we use rb_ivar_get for them too.
- if !receiver_t_object || uses_custom_allocator || comptime_receiver.shape_too_complex() || (asm.ctx.get_chain_depth() as i32) >= SET_IVAR_MAX_DEPTH {
- asm.comment("call rb_vm_setinstancevariable()");
-
- let ic = jit.get_arg(1).as_u64(); // type IVC
-
- // The function could raise exceptions.
+ if !receiver_t_object || uses_custom_allocator || shape_too_complex || new_shape_too_complex || megamorphic {
+ // The function could raise FrozenError.
// Note that this modifies REG_SP, which is why we do it first
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_non_leaf_call(jit, asm);
// Get the operands from the stack
- let val_opnd = asm.stack_pop(1);
+ let val_opnd = asm.stack_opnd(0);
- // Call rb_vm_setinstancevariable(iseq, obj, id, val, ic);
- asm.ccall(
- rb_vm_setinstancevariable as *const u8,
- vec![
- Opnd::const_ptr(jit.iseq as *const u8),
- Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF),
- ivar_name.into(),
- val_opnd,
- Opnd::const_ptr(ic as *const u8),
- ]
- );
+ if let StackOpnd(index) = recv_opnd { // attr_writer
+ let recv = asm.stack_opnd(index as i32);
+ asm_comment!(asm, "call rb_vm_set_ivar_id()");
+ asm.ccall(
+ rb_vm_set_ivar_id as *const u8,
+ vec![
+ recv,
+ Opnd::UImm(ivar_name),
+ val_opnd,
+ ],
+ );
+ } else { // setinstancevariable
+ asm_comment!(asm, "call rb_vm_setinstancevariable()");
+ asm.ccall(
+ rb_vm_setinstancevariable as *const u8,
+ vec![
+ Opnd::const_ptr(jit.iseq as *const u8),
+ Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF),
+ ivar_name.into(),
+ val_opnd,
+ Opnd::const_ptr(ic.unwrap() as *const u8),
+ ],
+ );
+ }
} else {
- // Get the iv index
- let ivar_index = unsafe {
- let shape_id = comptime_receiver.shape_id_of();
- let shape = rb_shape_get_shape_by_id(shape_id);
- let mut ivar_index: u32 = 0;
- if rb_shape_get_iv_index(shape, ivar_name, &mut ivar_index) {
- Some(ivar_index as usize)
- } else {
- None
- }
- };
-
// Get the receiver
- let mut recv = asm.load(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF));
-
- let recv_opnd = SelfOpnd;
+ let mut recv = asm.load(if let StackOpnd(index) = recv_opnd {
+ asm.stack_opnd(index as i32)
+ } else {
+ Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF)
+ });
// Upgrade type
guard_object_is_heap(asm, recv, recv_opnd, Counter::setivar_not_heap);
@@ -2234,7 +3041,7 @@ fn gen_setinstancevariable(
let shape_id_offset = unsafe { rb_shape_id_offset() };
let shape_opnd = Opnd::mem(SHAPE_ID_NUM_BITS as u8, recv, shape_id_offset);
- asm.comment("guard shape");
+ asm_comment!(asm, "guard shape");
asm.cmp(shape_opnd, Opnd::UImm(expected_shape as u64));
jit_chain_guard(
JCC_JNE,
@@ -2245,54 +3052,21 @@ fn gen_setinstancevariable(
Counter::setivar_megamorphic,
);
- asm.spill_temps(); // for ccall (must be done before write_val is popped)
let write_val;
match ivar_index {
// If we don't have an instance variable index, then we need to
// transition out of the current shape.
None => {
- let shape = comptime_receiver.shape_of();
-
- let current_capacity = unsafe { (*shape).capacity };
- let new_capacity = current_capacity * 2;
-
- // If the object doesn't have the capacity to store the IV,
- // then we'll need to allocate it.
- let needs_extension = unsafe { (*shape).next_iv_index >= current_capacity };
-
- // We can write to the object, but we need to transition the shape
- let ivar_index = unsafe { (*shape).next_iv_index } as usize;
-
- let capa_shape = if needs_extension {
- // We need to add an extended table to the object
- // First, create an outgoing transition that increases the
- // capacity
- Some(unsafe { rb_shape_transition_shape_capa(shape, new_capacity) })
- } else {
- None
- };
-
- let dest_shape = if let Some(capa_shape) = capa_shape {
- unsafe { rb_shape_get_next(capa_shape, comptime_receiver, ivar_name) }
- } else {
- unsafe { rb_shape_get_next(shape, comptime_receiver, ivar_name) }
- };
-
- let new_shape_id = unsafe { rb_shape_id(dest_shape) };
-
- if new_shape_id == OBJ_TOO_COMPLEX_SHAPE_ID {
- return None;
- }
-
- if needs_extension {
+ let (new_shape_id, needs_extension, ivar_index) = new_shape.unwrap();
+ if let Some((current_capacity, new_capacity)) = needs_extension {
// Generate the C call so that runtime code will increase
// the capacity and set the buffer.
- asm.comment("call rb_ensure_iv_list_size");
+ asm_comment!(asm, "call rb_ensure_iv_list_size");
// It allocates so can trigger GC, which takes the VM lock
// so could yield to a different ractor.
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_call_with_gc(jit, asm);
asm.ccall(rb_ensure_iv_list_size as *const u8,
vec![
recv,
@@ -2302,13 +3076,17 @@ fn gen_setinstancevariable(
);
// Load the receiver again after the function call
- recv = asm.load(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF))
+ recv = asm.load(if let StackOpnd(index) = recv_opnd {
+ asm.stack_opnd(index as i32)
+ } else {
+ Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF)
+ });
}
- write_val = asm.stack_pop(1);
- gen_write_iv(asm, comptime_receiver, recv, ivar_index, write_val, needs_extension);
+ write_val = asm.stack_opnd(0);
+ gen_write_iv(asm, comptime_receiver, recv, ivar_index, write_val, needs_extension.is_some());
- asm.comment("write shape");
+ asm_comment!(asm, "write shape");
let shape_id_offset = unsafe { rb_shape_id_offset() };
let shape_opnd = Opnd::mem(SHAPE_ID_NUM_BITS as u8, recv, shape_id_offset);
@@ -2323,7 +3101,7 @@ fn gen_setinstancevariable(
// the iv index by searching up the shape tree. If we've
// made the transition already, then there's no reason to
// update the shape on the object. Just set the IV.
- write_val = asm.stack_pop(1);
+ write_val = asm.stack_opnd(0);
gen_write_iv(asm, comptime_receiver, recv, ivar_index, write_val, false);
},
}
@@ -2331,6 +3109,7 @@ fn gen_setinstancevariable(
// If we know the stack value is an immediate, there's no need to
// generate WB code.
if !stack_type.is_imm() {
+ asm.spill_temps(); // for ccall (unconditionally spill them for RegTemps consistency)
let skip_wb = asm.new_label("skip_wb");
// If the value we're writing is an immediate, we don't need to WB
asm.test(write_val, (RUBY_IMMEDIATE_MASK as u64).into());
@@ -2340,7 +3119,7 @@ fn gen_setinstancevariable(
asm.cmp(write_val, Qnil.into());
asm.jbe(skip_wb);
- asm.comment("write barrier");
+ asm_comment!(asm, "write barrier");
asm.ccall(
rb_gc_writebarrier as *const u8,
vec![
@@ -2352,6 +3131,16 @@ fn gen_setinstancevariable(
asm.write_label(skip_wb);
}
}
+ let write_val = asm.stack_pop(1); // Keep write_val on stack during ccall for GC
+
+ // If it's attr_writer, i.e. recv_opnd is StackOpnd, we need to pop
+ // the receiver and push the written value onto the stack.
+ if let StackOpnd(_) = recv_opnd {
+ asm.stack_pop(1); // Pop receiver
+
+ let out_opnd = asm.stack_push(Type::Unknown); // Push a return value
+ asm.mov(out_opnd, write_val);
+ }
Some(KeepCompiling)
}
@@ -2365,30 +3154,41 @@ fn gen_defined(
let obj = jit.get_arg(1);
let pushval = jit.get_arg(2);
- // Save the PC and SP because the callee may allocate
- // Note that this modifies REG_SP, which is why we do it first
- jit_prepare_routine_call(jit, asm);
+ match op_type as u32 {
+ DEFINED_YIELD => {
+ asm.stack_pop(1); // v operand is not used
+ let out_opnd = asm.stack_push(Type::Unknown); // nil or "yield"
- // Get the operands from the stack
- let v_opnd = asm.stack_pop(1);
+ gen_block_given(jit, asm, out_opnd, pushval.into(), Qnil.into());
+ }
+ _ => {
+ // Save the PC and SP because the callee may allocate or call #respond_to?
+ // Note that this modifies REG_SP, which is why we do it first
+ jit_prepare_non_leaf_call(jit, asm);
- // Call vm_defined(ec, reg_cfp, op_type, obj, v)
- let def_result = asm.ccall(rb_vm_defined as *const u8, vec![EC, CFP, op_type.into(), obj.into(), v_opnd]);
+ // Get the operands from the stack
+ let v_opnd = asm.stack_opnd(0);
- // if (vm_defined(ec, GET_CFP(), op_type, obj, v)) {
- // val = pushval;
- // }
- asm.test(def_result, Opnd::UImm(255));
- let out_value = asm.csel_nz(pushval.into(), Qnil.into());
+ // Call vm_defined(ec, reg_cfp, op_type, obj, v)
+ let def_result = asm.ccall(rb_vm_defined as *const u8, vec![EC, CFP, op_type.into(), obj.into(), v_opnd]);
+ asm.stack_pop(1); // Keep it on stack during ccall for GC
- // Push the return value onto the stack
- let out_type = if pushval.special_const_p() {
- Type::UnknownImm
- } else {
- Type::Unknown
- };
- let stack_ret = asm.stack_push(out_type);
- asm.mov(stack_ret, out_value);
+ // if (vm_defined(ec, GET_CFP(), op_type, obj, v)) {
+ // val = pushval;
+ // }
+ asm.test(def_result, Opnd::UImm(255));
+ let out_value = asm.csel_nz(pushval.into(), Qnil.into());
+
+ // Push the return value onto the stack
+ let out_type = if pushval.special_const_p() {
+ Type::UnknownImm
+ } else {
+ Type::Unknown
+ };
+ let stack_ret = asm.stack_push(out_type);
+ asm.mov(stack_ret, out_value);
+ }
+ }
Some(KeepCompiling)
}
@@ -2415,15 +3215,15 @@ fn gen_definedivar(
// Specialize base on compile time values
let comptime_receiver = jit.peek_at_self();
- if comptime_receiver.shape_too_complex() || asm.ctx.get_chain_depth() as i32 >= GET_IVAR_MAX_DEPTH {
+ if comptime_receiver.shape_too_complex() || asm.ctx.get_chain_depth() >= GET_IVAR_MAX_DEPTH {
// Fall back to calling rb_ivar_defined
// Save the PC and SP because the callee may allocate
// Note that this modifies REG_SP, which is why we do it first
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_call_with_gc(jit, asm);
// Call rb_ivar_defined(recv, ivar_name)
- let def_result = asm.ccall(rb_ivar_defined as *const u8, vec![recv.into(), ivar_name.into()]);
+ let def_result = asm.ccall(rb_ivar_defined as *const u8, vec![recv, ivar_name.into()]);
// if (rb_ivar_defined(recv, ivar_name)) {
// val = pushval;
@@ -2452,7 +3252,7 @@ fn gen_definedivar(
let shape_id_offset = unsafe { rb_shape_id_offset() };
let shape_opnd = Opnd::mem(SHAPE_ID_NUM_BITS as u8, recv, shape_id_offset);
- asm.comment("guard shape");
+ asm_comment!(asm, "guard shape");
asm.cmp(shape_opnd, Opnd::UImm(shape_id as u64));
jit_chain_guard(
JCC_JNE,
@@ -2483,7 +3283,6 @@ fn gen_checktype(
if let RUBY_T_STRING | RUBY_T_ARRAY | RUBY_T_HASH = type_val {
let val_type = asm.ctx.get_opnd_type(StackOpnd(0));
let val = asm.stack_pop(1);
- let val = asm.load(val);
// Check if we know from type information
match val_type.known_value_type() {
@@ -2501,6 +3300,7 @@ fn gen_checktype(
let ret = asm.new_label("ret");
+ let val = asm.load(val);
if !val_type.is_heap() {
// if (SPECIAL_CONST_P(val)) {
// Return Qfalse via REG1 if not on heap
@@ -2534,10 +3334,10 @@ fn gen_concatstrings(
) -> Option<CodegenStatus> {
let n = jit.get_arg(0).as_usize();
- // Save the PC and SP because we are allocating
- jit_prepare_routine_call(jit, asm);
+ // rb_str_concat_literals may raise Encoding::CompatibilityError
+ jit_prepare_non_leaf_call(jit, asm);
- let values_ptr = asm.lea(asm.ctx.sp_opnd(-((SIZEOF_VALUE as isize) * n as isize)));
+ let values_ptr = asm.lea(asm.ctx.sp_opnd(-(n as i32)));
// call rb_str_concat_literals(size_t n, const VALUE *strings);
let return_value = asm.ccall(
@@ -2546,7 +3346,7 @@ fn gen_concatstrings(
);
asm.stack_pop(n);
- let stack_ret = asm.stack_push(Type::CString);
+ let stack_ret = asm.stack_push(Type::TString);
asm.mov(stack_ret, return_value);
Some(KeepCompiling)
@@ -2557,7 +3357,7 @@ fn guard_two_fixnums(
asm: &mut Assembler,
ocb: &mut OutlinedCb,
) {
- let counter = Counter::send_not_fixnums;
+ let counter = Counter::guard_send_not_fixnums;
// Get stack operands without popping them
let arg1 = asm.stack_opnd(0);
@@ -2568,19 +3368,19 @@ fn guard_two_fixnums(
let arg0_type = asm.ctx.get_opnd_type(arg0.into());
if arg0_type.is_heap() || arg1_type.is_heap() {
- asm.comment("arg is heap object");
+ asm_comment!(asm, "arg is heap object");
asm.jmp(Target::side_exit(counter));
return;
}
if arg0_type != Type::Fixnum && arg0_type.is_specific() {
- asm.comment("arg0 not fixnum");
+ asm_comment!(asm, "arg0 not fixnum");
asm.jmp(Target::side_exit(counter));
return;
}
if arg1_type != Type::Fixnum && arg1_type.is_specific() {
- asm.comment("arg1 not fixnum");
+ asm_comment!(asm, "arg1 not fixnum");
asm.jmp(Target::side_exit(counter));
return;
}
@@ -2592,7 +3392,7 @@ fn guard_two_fixnums(
// If not fixnums at run-time, fall back
if arg0_type != Type::Fixnum {
- asm.comment("guard arg0 fixnum");
+ asm_comment!(asm, "guard arg0 fixnum");
asm.test(arg0, Opnd::UImm(RUBY_FIXNUM_FLAG as u64));
jit_chain_guard(
@@ -2605,7 +3405,7 @@ fn guard_two_fixnums(
);
}
if arg1_type != Type::Fixnum {
- asm.comment("guard arg1 fixnum");
+ asm_comment!(asm, "guard arg1 fixnum");
asm.test(arg1, Opnd::UImm(RUBY_FIXNUM_FLAG as u64));
jit_chain_guard(
@@ -2762,7 +3562,7 @@ fn gen_equality_specialized(
a_opnd.into(),
comptime_a,
SEND_MAX_DEPTH,
- Counter::send_not_string,
+ Counter::guard_send_not_string,
);
let equal = asm.new_label("equal");
@@ -2789,7 +3589,7 @@ fn gen_equality_specialized(
b_opnd.into(),
comptime_b,
SEND_MAX_DEPTH,
- Counter::send_not_string,
+ Counter::guard_send_not_string,
);
}
@@ -2846,7 +3646,7 @@ fn gen_opt_neq(
// opt_neq is passed two rb_call_data as arguments:
// first for ==, second for !=
let cd = jit.get_arg(1).as_ptr();
- return gen_send_general(jit, asm, ocb, cd, None);
+ perf_call! { gen_send_general(jit, asm, ocb, cd, None) }
}
fn gen_opt_aref(
@@ -2904,13 +3704,12 @@ fn gen_opt_aref(
// Call VALUE rb_ary_entry_internal(VALUE ary, long offset).
// It never raises or allocates, so we don't need to write to cfp->pc.
{
- asm.spill_temps(); // for ccall
- let idx_reg = asm.rshift(idx_reg, Opnd::UImm(1)); // Convert fixnum to int
- let val = asm.ccall(rb_ary_entry_internal as *const u8, vec![recv_opnd, idx_reg]);
-
// Pop the argument and the receiver
asm.stack_pop(2);
+ let idx_reg = asm.rshift(idx_reg, Opnd::UImm(1)); // Convert fixnum to int
+ let val = asm.ccall(rb_ary_entry_internal as *const u8, vec![recv_opnd, idx_reg]);
+
// Push the return value onto the stack
let stack_ret = asm.stack_push(Type::Unknown);
asm.mov(stack_ret, val);
@@ -2940,7 +3739,7 @@ fn gen_opt_aref(
);
// Prepare to call rb_hash_aref(). It might call #hash on the key.
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_non_leaf_call(jit, asm);
// Call rb_hash_aref
let key_opnd = asm.stack_opnd(0);
@@ -3010,7 +3809,7 @@ fn gen_opt_aset(
);
// We might allocate or raise
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_non_leaf_call(jit, asm);
// Call rb_ary_store
let recv = asm.stack_opnd(2);
@@ -3045,7 +3844,7 @@ fn gen_opt_aset(
);
// We might allocate or raise
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_non_leaf_call(jit, asm);
// Call rb_hash_aset
let recv = asm.stack_opnd(2);
@@ -3065,6 +3864,39 @@ fn gen_opt_aset(
}
}
+fn gen_opt_aref_with(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ _ocb: &mut OutlinedCb,
+) -> Option<CodegenStatus>{
+ // We might allocate or raise
+ jit_prepare_non_leaf_call(jit, asm);
+
+ let key_opnd = Opnd::Value(jit.get_arg(0));
+ let recv_opnd = asm.stack_opnd(0);
+
+ extern "C" {
+ fn rb_vm_opt_aref_with(recv: VALUE, key: VALUE) -> VALUE;
+ }
+
+ let val_opnd = asm.ccall(
+ rb_vm_opt_aref_with as *const u8,
+ vec![
+ recv_opnd,
+ key_opnd
+ ],
+ );
+ asm.stack_pop(1); // Keep it on stack during GC
+
+ asm.cmp(val_opnd, Qundef.into());
+ asm.je(Target::side_exit(Counter::opt_aref_with_qundef));
+
+ let top = asm.stack_push(Type::Unknown);
+ asm.mov(top, val_opnd);
+
+ return Some(KeepCompiling);
+}
+
fn gen_opt_and(
jit: &mut JITState,
asm: &mut Assembler,
@@ -3096,7 +3928,7 @@ fn gen_opt_and(
// Push the output on the stack
let dst = asm.stack_push(Type::Fixnum);
- asm.store(dst, val);
+ asm.mov(dst, val);
Some(KeepCompiling)
} else {
@@ -3136,7 +3968,7 @@ fn gen_opt_or(
// Push the output on the stack
let dst = asm.stack_push(Type::Fixnum);
- asm.store(dst, val);
+ asm.mov(dst, val);
Some(KeepCompiling)
} else {
@@ -3178,7 +4010,7 @@ fn gen_opt_minus(
// Push the output on the stack
let dst = asm.stack_push(Type::Fixnum);
- asm.store(dst, val);
+ asm.mov(dst, val);
Some(KeepCompiling)
} else {
@@ -3192,8 +4024,43 @@ fn gen_opt_mult(
asm: &mut Assembler,
ocb: &mut OutlinedCb,
) -> Option<CodegenStatus> {
- // Delegate to send, call the method on the recv
- gen_opt_send_without_block(jit, asm, ocb)
+ let two_fixnums = match asm.ctx.two_fixnums_on_stack(jit) {
+ Some(two_fixnums) => two_fixnums,
+ None => {
+ defer_compilation(jit, asm, ocb);
+ return Some(EndBlock);
+ }
+ };
+
+ // Fallback to a method call if it overflows
+ if two_fixnums && asm.ctx.get_chain_depth() == 0 {
+ if !assume_bop_not_redefined(jit, asm, ocb, INTEGER_REDEFINED_OP_FLAG, BOP_MULT) {
+ return None;
+ }
+
+ // Check that both operands are fixnums
+ guard_two_fixnums(jit, asm, ocb);
+
+ // Get the operands from the stack
+ let arg1 = asm.stack_pop(1);
+ let arg0 = asm.stack_pop(1);
+
+ // Do some bitwise gymnastics to handle tag bits
+ // x * y is translated to (x >> 1) * (y - 1) + 1
+ let arg0_untag = asm.rshift(arg0, Opnd::UImm(1));
+ let arg1_untag = asm.sub(arg1, Opnd::UImm(1));
+ let out_val = asm.mul(arg0_untag, arg1_untag);
+ jit_chain_guard(JCC_JO_MUL, jit, asm, ocb, 1, Counter::opt_mult_overflow);
+ let out_val = asm.add(out_val, Opnd::UImm(1));
+
+ // Push the output on the stack
+ let dst = asm.stack_push(Type::Fixnum);
+ asm.mov(dst, out_val);
+
+ Some(KeepCompiling)
+ } else {
+ gen_opt_send_without_block(jit, asm, ocb)
+ }
}
fn gen_opt_div(
@@ -3228,7 +4095,6 @@ fn gen_opt_mod(
guard_two_fixnums(jit, asm, ocb);
// Get the operands and destination from the stack
- asm.spill_temps(); // for ccall (must be done before stack_pop)
let arg1 = asm.stack_pop(1);
let arg0 = asm.stack_pop(1);
@@ -3330,15 +4196,14 @@ fn gen_opt_newarray_max(
) -> Option<CodegenStatus> {
let num = jit.get_arg(0).as_u32();
- // Save the PC and SP because we may allocate
- jit_prepare_routine_call(jit, asm);
+ // Save the PC and SP because we may call #max
+ jit_prepare_non_leaf_call(jit, asm);
extern "C" {
fn rb_vm_opt_newarray_max(ec: EcPtr, num: u32, elts: *const VALUE) -> VALUE;
}
- let offset_magnitude = (SIZEOF_VALUE as u32) * num;
- let values_opnd = asm.ctx.sp_opnd(-(offset_magnitude as isize));
+ let values_opnd = asm.ctx.sp_opnd(-(num as i32));
let values_ptr = asm.lea(values_opnd);
let val_opnd = asm.ccall(
@@ -3362,13 +4227,13 @@ fn gen_opt_newarray_send(
asm: &mut Assembler,
_ocb: &mut OutlinedCb,
) -> Option<CodegenStatus> {
- let method = jit.get_arg(1).as_u32();
+ let method = jit.get_arg(1).as_u64();
- if method == idMin {
+ if method == ID!(min) {
gen_opt_newarray_min(jit, asm, _ocb)
- } else if method == idMax {
+ } else if method == ID!(max) {
gen_opt_newarray_max(jit, asm, _ocb)
- } else if method == idHash {
+ } else if method == ID!(hash) {
gen_opt_newarray_hash(jit, asm, _ocb)
} else {
None
@@ -3383,15 +4248,14 @@ fn gen_opt_newarray_hash(
let num = jit.get_arg(0).as_u32();
- // Save the PC and SP because we may allocate
- jit_prepare_routine_call(jit, asm);
+ // Save the PC and SP because we may call #hash
+ jit_prepare_non_leaf_call(jit, asm);
extern "C" {
fn rb_vm_opt_newarray_hash(ec: EcPtr, num: u32, elts: *const VALUE) -> VALUE;
}
- let offset_magnitude = (SIZEOF_VALUE as u32) * num;
- let values_opnd = asm.ctx.sp_opnd(-(offset_magnitude as isize));
+ let values_opnd = asm.ctx.sp_opnd(-(num as i32));
let values_ptr = asm.lea(values_opnd);
let val_opnd = asm.ccall(
@@ -3418,15 +4282,14 @@ fn gen_opt_newarray_min(
let num = jit.get_arg(0).as_u32();
- // Save the PC and SP because we may allocate
- jit_prepare_routine_call(jit, asm);
+ // Save the PC and SP because we may call #min
+ jit_prepare_non_leaf_call(jit, asm);
extern "C" {
fn rb_vm_opt_newarray_min(ec: EcPtr, num: u32, elts: *const VALUE) -> VALUE;
}
- let offset_magnitude = (SIZEOF_VALUE as u32) * num;
- let values_opnd = asm.ctx.sp_opnd(-(offset_magnitude as isize));
+ let values_opnd = asm.ctx.sp_opnd(-(num as i32));
let values_ptr = asm.lea(values_opnd);
let val_opnd = asm.ccall(
@@ -3521,7 +4384,13 @@ fn gen_opt_case_dispatch(
all_fixnum
}
- if comptime_key.fixnum_p() && comptime_key.0 <= u32::MAX.as_usize() && case_hash_all_fixnum_p(case_hash) {
+ // If megamorphic, fallback to compiling branch instructions after opt_case_dispatch
+ let megamorphic = asm.ctx.get_chain_depth() >= CASE_WHEN_MAX_DEPTH;
+ if megamorphic {
+ gen_counter_incr(asm, Counter::num_opt_case_dispatch_megamorphic);
+ }
+
+ if comptime_key.fixnum_p() && comptime_key.0 <= u32::MAX.as_usize() && case_hash_all_fixnum_p(case_hash) && !megamorphic {
if !assume_bop_not_redefined(jit, asm, ocb, INTEGER_REDEFINED_OP_FLAG, BOP_EQQ) {
return None;
}
@@ -3598,7 +4467,7 @@ fn gen_branchif(
asm.test(val_opnd, Opnd::Imm(!Qnil.as_i64()));
// Generate the branch instructions
- let ctx = asm.ctx.clone();
+ let ctx = asm.ctx;
gen_branch(
jit,
asm,
@@ -3654,7 +4523,7 @@ fn gen_branchunless(
asm.test(val_opnd, not_qnil.into());
// Generate the branch instructions
- let ctx = asm.ctx.clone();
+ let ctx = asm.ctx;
gen_branch(
jit,
asm,
@@ -3707,7 +4576,7 @@ fn gen_branchnil(
// Test if the value is Qnil
asm.cmp(val_opnd, Opnd::UImm(Qnil.into()));
// Generate the branch instructions
- let ctx = asm.ctx.clone();
+ let ctx = asm.ctx;
gen_branch(
jit,
asm,
@@ -3732,8 +4601,17 @@ fn gen_throw(
let throwobj = asm.stack_pop(1);
let throwobj = asm.load(throwobj);
+ // Gather some statistics about throw
+ gen_counter_incr(asm, Counter::num_throw);
+ match (throw_state & VM_THROW_STATE_MASK as u64) as u32 {
+ RUBY_TAG_BREAK => gen_counter_incr(asm, Counter::num_throw_break),
+ RUBY_TAG_RETRY => gen_counter_incr(asm, Counter::num_throw_retry),
+ RUBY_TAG_RETURN => gen_counter_incr(asm, Counter::num_throw_return),
+ _ => {},
+ }
+
// THROW_DATA_NEW allocates. Save SP for GC and PC for allocation tracing as
- // well as handling the catch table. However, not using jit_prepare_routine_call
+ // well as handling the catch table. However, not using jit_prepare_call_with_gc
// since we don't need a patch point for this implementation.
jit_save_pc(jit, asm);
gen_save_sp(asm);
@@ -3746,7 +4624,7 @@ fn gen_throw(
}
let val = asm.ccall(rb_vm_throw as *mut u8, vec![EC, CFP, throw_state.into(), throwobj]);
- asm.comment("exit from throw");
+ asm_comment!(asm, "exit from throw");
asm.cpop_into(SP);
asm.cpop_into(EC);
asm.cpop_into(CFP);
@@ -3796,21 +4674,31 @@ fn jit_guard_known_klass(
obj_opnd: Opnd,
insn_opnd: YARVOpnd,
sample_instance: VALUE,
- max_chain_depth: i32,
+ max_chain_depth: u8,
counter: Counter,
) {
let val_type = asm.ctx.get_opnd_type(insn_opnd);
if val_type.known_class() == Some(known_klass) {
- // We already know from type information that this is a match
- return;
+ // Unless frozen, Array, Hash, and String objects may change their RBASIC_CLASS
+ // when they get a singleton class. Those types need invalidations.
+ if unsafe { [rb_cArray, rb_cHash, rb_cString].contains(&known_klass) } {
+ if jit.assume_no_singleton_class(asm, ocb, known_klass) {
+ // Speculate that this object will not have a singleton class,
+ // and invalidate the block in case it does.
+ return;
+ }
+ } else {
+ // We already know from type information that this is a match
+ return;
+ }
}
if unsafe { known_klass == rb_cNilClass } {
assert!(!val_type.is_heap());
assert!(val_type.is_unknown());
- asm.comment("guard object is nil");
+ asm_comment!(asm, "guard object is nil");
asm.cmp(obj_opnd, Qnil.into());
jit_chain_guard(JCC_JNE, jit, asm, ocb, max_chain_depth, counter);
@@ -3819,7 +4707,7 @@ fn jit_guard_known_klass(
assert!(!val_type.is_heap());
assert!(val_type.is_unknown());
- asm.comment("guard object is true");
+ asm_comment!(asm, "guard object is true");
asm.cmp(obj_opnd, Qtrue.into());
jit_chain_guard(JCC_JNE, jit, asm, ocb, max_chain_depth, counter);
@@ -3828,7 +4716,7 @@ fn jit_guard_known_klass(
assert!(!val_type.is_heap());
assert!(val_type.is_unknown());
- asm.comment("guard object is false");
+ asm_comment!(asm, "guard object is false");
assert!(Qfalse.as_i32() == 0);
asm.test(obj_opnd, obj_opnd);
jit_chain_guard(JCC_JNZ, jit, asm, ocb, max_chain_depth, counter);
@@ -3839,7 +4727,7 @@ fn jit_guard_known_klass(
// BIGNUM can be handled by the general else case below
assert!(val_type.is_unknown());
- asm.comment("guard object is fixnum");
+ asm_comment!(asm, "guard object is fixnum");
asm.test(obj_opnd, Opnd::Imm(RUBY_FIXNUM_FLAG as i64));
jit_chain_guard(JCC_JZ, jit, asm, ocb, max_chain_depth, counter);
asm.ctx.upgrade_opnd_type(insn_opnd, Type::Fixnum);
@@ -3850,7 +4738,7 @@ fn jit_guard_known_klass(
if val_type != Type::ImmSymbol || !val_type.is_imm() {
assert!(val_type.is_unknown());
- asm.comment("guard object is static symbol");
+ asm_comment!(asm, "guard object is static symbol");
assert!(RUBY_SPECIAL_SHIFT == 8);
asm.cmp(obj_opnd.with_num_bits(8).unwrap(), Opnd::UImm(RUBY_SYMBOL_FLAG as u64));
jit_chain_guard(JCC_JNE, jit, asm, ocb, max_chain_depth, counter);
@@ -3862,7 +4750,7 @@ fn jit_guard_known_klass(
assert!(val_type.is_unknown());
// We will guard flonum vs heap float as though they were separate classes
- asm.comment("guard object is flonum");
+ asm_comment!(asm, "guard object is flonum");
let flag_bits = asm.and(obj_opnd, Opnd::UImm(RUBY_FLONUM_MASK as u64));
asm.cmp(flag_bits, Opnd::UImm(RUBY_FLONUM_FLAG as u64));
jit_chain_guard(JCC_JNE, jit, asm, ocb, max_chain_depth, counter);
@@ -3871,6 +4759,7 @@ fn jit_guard_known_klass(
} else if unsafe {
FL_TEST(known_klass, VALUE(RUBY_FL_SINGLETON as usize)) != VALUE(0)
&& sample_instance == rb_class_attached_object(known_klass)
+ && !rb_obj_is_kind_of(sample_instance, rb_cIO).test()
} {
// Singleton classes are attached to one specific object, so we can
// avoid one memory access (and potentially the is_heap check) by
@@ -3882,7 +4771,9 @@ fn jit_guard_known_klass(
// that its singleton class is empty, so we can't avoid the memory
// access. As an example, `Object.new.singleton_class` is an object in
// this situation.
- asm.comment("guard known object with singleton class");
+ // Also, guarding by identity is incorrect for IO objects because
+ // IO#reopen can be used to change the class and singleton class of IO objects!
+ asm_comment!(asm, "guard known object with singleton class");
asm.cmp(obj_opnd, sample_instance.into());
jit_chain_guard(JCC_JNE, jit, asm, ocb, max_chain_depth, counter);
} else if val_type == Type::CString && unsafe { known_klass == rb_cString } {
@@ -3896,7 +4787,7 @@ fn jit_guard_known_klass(
// Check that the receiver is a heap object
// Note: if we get here, the class doesn't have immediate instances.
if !val_type.is_heap() {
- asm.comment("guard not immediate");
+ asm_comment!(asm, "guard not immediate");
asm.test(obj_opnd, (RUBY_IMMEDIATE_MASK as u64).into());
jit_chain_guard(JCC_JNZ, jit, asm, ocb, max_chain_depth, counter);
asm.cmp(obj_opnd, Qfalse.into());
@@ -3907,14 +4798,14 @@ fn jit_guard_known_klass(
// If obj_opnd isn't already a register, load it.
let obj_opnd = match obj_opnd {
- Opnd::Reg(_) => obj_opnd,
+ Opnd::InsnOut { .. } => obj_opnd,
_ => asm.load(obj_opnd),
};
let klass_opnd = Opnd::mem(64, obj_opnd, RUBY_OFFSET_RBASIC_KLASS);
// Bail if receiver class is different from known_klass
// TODO: jit_mov_gc_ptr keeps a strong reference, which leaks the class.
- asm.comment("guard known class");
+ asm_comment!(asm, "guard known class");
asm.cmp(klass_opnd, known_klass.into());
jit_chain_guard(JCC_JNE, jit, asm, ocb, max_chain_depth, counter);
@@ -3922,6 +4813,8 @@ fn jit_guard_known_klass(
asm.ctx.upgrade_opnd_type(insn_opnd, Type::CString);
} else if known_klass == unsafe { rb_cArray } {
asm.ctx.upgrade_opnd_type(insn_opnd, Type::CArray);
+ } else if known_klass == unsafe { rb_cHash } {
+ asm.ctx.upgrade_opnd_type(insn_opnd, Type::CHash);
}
}
}
@@ -3937,7 +4830,6 @@ fn jit_protected_callee_ancestry_guard(
// Note: PC isn't written to current control frame as rb_is_kind_of() shouldn't raise.
// VALUE rb_obj_is_kind_of(VALUE obj, VALUE klass);
- asm.spill_temps(); // for ccall
let val = asm.ccall(
rb_obj_is_kind_of as *mut u8,
vec![
@@ -3946,7 +4838,7 @@ fn jit_protected_callee_ancestry_guard(
],
);
asm.test(val, val);
- asm.jz(Target::side_exit(Counter::send_se_protected_check_failed))
+ asm.jz(Target::side_exit(Counter::guard_send_se_protected_check_failed))
}
// Codegen for rb_obj_not().
@@ -3958,22 +4850,22 @@ fn jit_rb_obj_not(
_ocb: &mut OutlinedCb,
_ci: *const rb_callinfo,
_cme: *const rb_callable_method_entry_t,
- _block: Option<IseqPtr>,
+ _block: Option<BlockHandler>,
_argc: i32,
- _known_recv_class: *const VALUE,
+ _known_recv_class: Option<VALUE>,
) -> bool {
let recv_opnd = asm.ctx.get_opnd_type(StackOpnd(0));
match recv_opnd.known_truthy() {
Some(false) => {
- asm.comment("rb_obj_not(nil_or_false)");
+ asm_comment!(asm, "rb_obj_not(nil_or_false)");
asm.stack_pop(1);
let out_opnd = asm.stack_push(Type::True);
asm.mov(out_opnd, Qtrue.into());
},
Some(true) => {
// Note: recv_opnd != Type::Nil && recv_opnd != Type::False.
- asm.comment("rb_obj_not(truthy)");
+ asm_comment!(asm, "rb_obj_not(truthy)");
asm.stack_pop(1);
let out_opnd = asm.stack_push(Type::False);
asm.mov(out_opnd, Qfalse.into());
@@ -3993,11 +4885,11 @@ fn jit_rb_true(
_ocb: &mut OutlinedCb,
_ci: *const rb_callinfo,
_cme: *const rb_callable_method_entry_t,
- _block: Option<IseqPtr>,
+ _block: Option<BlockHandler>,
_argc: i32,
- _known_recv_class: *const VALUE,
+ _known_recv_class: Option<VALUE>,
) -> bool {
- asm.comment("nil? == true");
+ asm_comment!(asm, "nil? == true");
asm.stack_pop(1);
let stack_ret = asm.stack_push(Type::True);
asm.mov(stack_ret, Qtrue.into());
@@ -4011,11 +4903,11 @@ fn jit_rb_false(
_ocb: &mut OutlinedCb,
_ci: *const rb_callinfo,
_cme: *const rb_callable_method_entry_t,
- _block: Option<IseqPtr>,
+ _block: Option<BlockHandler>,
_argc: i32,
- _known_recv_class: *const VALUE,
+ _known_recv_class: Option<VALUE>,
) -> bool {
- asm.comment("nil? == false");
+ asm_comment!(asm, "nil? == false");
asm.stack_pop(1);
let stack_ret = asm.stack_push(Type::False);
asm.mov(stack_ret, Qfalse.into());
@@ -4029,16 +4921,16 @@ fn jit_rb_kernel_is_a(
_ocb: &mut OutlinedCb,
_ci: *const rb_callinfo,
_cme: *const rb_callable_method_entry_t,
- _block: Option<IseqPtr>,
+ _block: Option<BlockHandler>,
argc: i32,
- known_recv_class: *const VALUE,
+ known_recv_class: Option<VALUE>,
) -> bool {
if argc != 1 {
return false;
}
// If this is a super call we might not know the class
- if known_recv_class.is_null() {
+ if known_recv_class.is_none() {
return false;
}
@@ -4053,15 +4945,15 @@ fn jit_rb_kernel_is_a(
let sample_rhs = jit.peek_at_stack(&asm.ctx, 0);
let sample_lhs = jit.peek_at_stack(&asm.ctx, 1);
- // We are not allowing module here because the module hierachy can change at runtime.
+ // We are not allowing module here because the module hierarchy can change at runtime.
if !unsafe { RB_TYPE_P(sample_rhs, RUBY_T_CLASS) } {
return false;
}
let sample_is_a = unsafe { rb_obj_is_kind_of(sample_lhs, sample_rhs) == Qtrue };
- asm.comment("Kernel#is_a?");
+ asm_comment!(asm, "Kernel#is_a?");
asm.cmp(asm.stack_opnd(0), sample_rhs.into());
- asm.jne(Target::side_exit(Counter::send_is_a_class_mismatch));
+ asm.jne(Target::side_exit(Counter::guard_send_is_a_class_mismatch));
asm.stack_pop(2);
@@ -4079,19 +4971,19 @@ fn jit_rb_kernel_is_a(
fn jit_rb_kernel_instance_of(
jit: &mut JITState,
asm: &mut Assembler,
- _ocb: &mut OutlinedCb,
+ ocb: &mut OutlinedCb,
_ci: *const rb_callinfo,
_cme: *const rb_callable_method_entry_t,
- _block: Option<IseqPtr>,
+ _block: Option<BlockHandler>,
argc: i32,
- known_recv_class: *const VALUE,
+ known_recv_class: Option<VALUE>,
) -> bool {
if argc != 1 {
return false;
}
// If this is a super call we might not know the class
- if known_recv_class.is_null() {
+ if known_recv_class.is_none() {
return false;
}
@@ -4118,9 +5010,16 @@ fn jit_rb_kernel_instance_of(
let sample_instance_of = sample_lhs_real_class == sample_rhs;
- asm.comment("Kernel#instance_of?");
+ asm_comment!(asm, "Kernel#instance_of?");
asm.cmp(asm.stack_opnd(0), sample_rhs.into());
- asm.jne(Target::side_exit(Counter::send_instance_of_class_mismatch));
+ jit_chain_guard(
+ JCC_JNE,
+ jit,
+ asm,
+ ocb,
+ SEND_MAX_DEPTH,
+ Counter::guard_send_instance_of_class_mismatch,
+ );
asm.stack_pop(2);
@@ -4140,28 +5039,26 @@ fn jit_rb_mod_eqq(
_ocb: &mut OutlinedCb,
_ci: *const rb_callinfo,
_cme: *const rb_callable_method_entry_t,
- _block: Option<IseqPtr>,
+ _block: Option<BlockHandler>,
argc: i32,
- _known_recv_class: *const VALUE,
+ _known_recv_class: Option<VALUE>,
) -> bool {
if argc != 1 {
return false;
}
- asm.comment("Module#===");
- asm.spill_temps(); // for ccall
+ asm_comment!(asm, "Module#===");
// By being here, we know that the receiver is a T_MODULE or a T_CLASS, because Module#=== can
// only live on these objects. With that, we can call rb_obj_is_kind_of() without
- // jit_prepare_routine_call() or a control frame push because it can't raise, allocate, or call
+ // jit_prepare_non_leaf_call() or a control frame push because it can't raise, allocate, or call
// Ruby methods with these inputs.
// Note the difference in approach from Kernel#is_a? because we don't get a free guard for the
// right hand side.
- let lhs = asm.stack_opnd(1); // the module
- let rhs = asm.stack_opnd(0);
+ let rhs = asm.stack_pop(1);
+ let lhs = asm.stack_pop(1); // the module
let ret = asm.ccall(rb_obj_is_kind_of as *const u8, vec![rhs, lhs]);
// Return the result
- asm.stack_pop(2);
let stack_ret = asm.stack_push(Type::UnknownImm);
asm.mov(stack_ret, ret);
@@ -4176,11 +5073,11 @@ fn jit_rb_obj_equal(
_ocb: &mut OutlinedCb,
_ci: *const rb_callinfo,
_cme: *const rb_callable_method_entry_t,
- _block: Option<IseqPtr>,
+ _block: Option<BlockHandler>,
_argc: i32,
- _known_recv_class: *const VALUE,
+ _known_recv_class: Option<VALUE>,
) -> bool {
- asm.comment("equal?");
+ asm_comment!(asm, "equal?");
let obj1 = asm.stack_pop(1);
let obj2 = asm.stack_pop(1);
@@ -4200,9 +5097,9 @@ fn jit_rb_obj_not_equal(
ocb: &mut OutlinedCb,
_ci: *const rb_callinfo,
_cme: *const rb_callable_method_entry_t,
- _block: Option<IseqPtr>,
+ _block: Option<BlockHandler>,
_argc: i32,
- _known_recv_class: *const VALUE,
+ _known_recv_class: Option<VALUE>,
) -> bool {
gen_equality_specialized(jit, asm, ocb, false) == Some(true)
}
@@ -4214,15 +5111,15 @@ fn jit_rb_int_equal(
ocb: &mut OutlinedCb,
_ci: *const rb_callinfo,
_cme: *const rb_callable_method_entry_t,
- _block: Option<IseqPtr>,
+ _block: Option<BlockHandler>,
_argc: i32,
- _known_recv_class: *const VALUE,
+ _known_recv_class: Option<VALUE>,
) -> bool {
// Check that both operands are fixnums
guard_two_fixnums(jit, asm, ocb);
// Compare the arguments
- asm.comment("rb_int_equal");
+ asm_comment!(asm, "rb_int_equal");
let arg1 = asm.stack_pop(1);
let arg0 = asm.stack_pop(1);
asm.cmp(arg0, arg1);
@@ -4233,62 +5130,216 @@ fn jit_rb_int_equal(
true
}
-fn jit_rb_int_mul(
+fn jit_rb_int_succ(
+ _jit: &mut JITState,
+ asm: &mut Assembler,
+ _ocb: &mut OutlinedCb,
+ _ci: *const rb_callinfo,
+ _cme: *const rb_callable_method_entry_t,
+ _block: Option<BlockHandler>,
+ _argc: i32,
+ _known_recv_class: Option<VALUE>,
+) -> bool {
+ // Guard the receiver is fixnum
+ let recv_type = asm.ctx.get_opnd_type(StackOpnd(0));
+ let recv = asm.stack_pop(1);
+ if recv_type != Type::Fixnum {
+ asm_comment!(asm, "guard object is fixnum");
+ asm.test(recv, Opnd::Imm(RUBY_FIXNUM_FLAG as i64));
+ asm.jz(Target::side_exit(Counter::opt_succ_not_fixnum));
+ }
+
+ asm_comment!(asm, "Integer#succ");
+ let out_val = asm.add(recv, Opnd::Imm(2)); // 2 is untagged Fixnum 1
+ asm.jo(Target::side_exit(Counter::opt_succ_overflow));
+
+ // Push the output onto the stack
+ let dst = asm.stack_push(Type::Fixnum);
+ asm.mov(dst, out_val);
+
+ true
+}
+
+fn jit_rb_int_div(
jit: &mut JITState,
asm: &mut Assembler,
ocb: &mut OutlinedCb,
_ci: *const rb_callinfo,
_cme: *const rb_callable_method_entry_t,
- _block: Option<IseqPtr>,
+ _block: Option<BlockHandler>,
_argc: i32,
- _known_recv_class: *const VALUE,
+ _known_recv_class: Option<VALUE>,
) -> bool {
if asm.ctx.two_fixnums_on_stack(jit) != Some(true) {
return false;
}
guard_two_fixnums(jit, asm, ocb);
- // rb_fix_mul_fix may allocate memory for Bignum
- jit_prepare_routine_call(jit, asm);
+ // rb_fix_div_fix may GC-allocate for Bignum
+ jit_prepare_call_with_gc(jit, asm);
- asm.comment("Integer#*");
- let obj = asm.stack_pop(1);
- let recv = asm.stack_pop(1);
- let ret = asm.ccall(rb_fix_mul_fix as *const u8, vec![recv, obj]);
+ asm_comment!(asm, "Integer#/");
+ let obj = asm.stack_opnd(0);
+ let recv = asm.stack_opnd(1);
+
+ // Check for arg0 % 0
+ asm.cmp(obj, VALUE::fixnum_from_usize(0).as_i64().into());
+ asm.je(Target::side_exit(Counter::opt_div_zero));
+
+ let ret = asm.ccall(rb_fix_div_fix as *const u8, vec![recv, obj]);
+ asm.stack_pop(2); // Keep them during ccall for GC
let ret_opnd = asm.stack_push(Type::Unknown);
asm.mov(ret_opnd, ret);
true
}
-fn jit_rb_int_div(
+fn jit_rb_int_lshift(
jit: &mut JITState,
asm: &mut Assembler,
ocb: &mut OutlinedCb,
_ci: *const rb_callinfo,
_cme: *const rb_callable_method_entry_t,
- _block: Option<IseqPtr>,
+ _block: Option<BlockHandler>,
_argc: i32,
- _known_recv_class: *const VALUE,
+ _known_recv_class: Option<VALUE>,
) -> bool {
if asm.ctx.two_fixnums_on_stack(jit) != Some(true) {
return false;
}
guard_two_fixnums(jit, asm, ocb);
- asm.comment("Integer#/");
- asm.spill_temps(); // for ccall (must be done before stack_pop)
- let obj = asm.stack_pop(1);
- let recv = asm.stack_pop(1);
+ let comptime_shift = jit.peek_at_stack(&asm.ctx, 0);
- // Check for arg0 % 0
- asm.cmp(obj, VALUE::fixnum_from_usize(0).as_i64().into());
- asm.je(Target::side_exit(Counter::opt_div_zero));
+ if !comptime_shift.fixnum_p() {
+ return false;
+ }
- let ret = asm.ccall(rb_fix_div_fix as *const u8, vec![recv, obj]);
+ // Untag the fixnum shift amount
+ let shift_amt = comptime_shift.as_isize() >> 1;
+ if shift_amt > 63 || shift_amt < 0 {
+ return false;
+ }
+
+ // Fallback to a C call if the shift amount varies
+ // This check is needed because the chain guard will side-exit
+ // if its max depth is reached
+ if asm.ctx.get_chain_depth() > 0 {
+ return false;
+ }
+
+ let rhs = asm.stack_pop(1);
+ let lhs = asm.stack_pop(1);
+
+ // Guard on the shift amount we speculated on
+ asm.cmp(rhs, comptime_shift.into());
+ jit_chain_guard(
+ JCC_JNE,
+ jit,
+ asm,
+ ocb,
+ 1,
+ Counter::lshift_amount_changed,
+ );
+
+ fixnum_left_shift_body(asm, lhs, shift_amt as u64);
+ true
+}
+
+fn fixnum_left_shift_body(asm: &mut Assembler, lhs: Opnd, shift_amt: u64) {
+ let in_val = asm.sub(lhs, 1.into());
+ let shift_opnd = Opnd::UImm(shift_amt);
+ let out_val = asm.lshift(in_val, shift_opnd);
+ let unshifted = asm.rshift(out_val, shift_opnd);
+
+ // Guard that we did not overflow
+ asm.cmp(unshifted, in_val);
+ asm.jne(Target::side_exit(Counter::lshift_overflow));
+
+ // Re-tag the output value
+ let out_val = asm.add(out_val, 1.into());
let ret_opnd = asm.stack_push(Type::Fixnum);
- asm.mov(ret_opnd, ret);
+ asm.mov(ret_opnd, out_val);
+}
+
+fn jit_rb_int_rshift(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ ocb: &mut OutlinedCb,
+ _ci: *const rb_callinfo,
+ _cme: *const rb_callable_method_entry_t,
+ _block: Option<BlockHandler>,
+ _argc: i32,
+ _known_recv_class: Option<VALUE>,
+) -> bool {
+ if asm.ctx.two_fixnums_on_stack(jit) != Some(true) {
+ return false;
+ }
+ guard_two_fixnums(jit, asm, ocb);
+
+ let comptime_shift = jit.peek_at_stack(&asm.ctx, 0);
+
+ // Untag the fixnum shift amount
+ let shift_amt = comptime_shift.as_isize() >> 1;
+ if shift_amt > 63 || shift_amt < 0 {
+ return false;
+ }
+
+ // Fallback to a C call if the shift amount varies
+ // This check is needed because the chain guard will side-exit
+ // if its max depth is reached
+ if asm.ctx.get_chain_depth() > 0 {
+ return false;
+ }
+
+ let rhs = asm.stack_pop(1);
+ let lhs = asm.stack_pop(1);
+
+ // Guard on the shift amount we speculated on
+ asm.cmp(rhs, comptime_shift.into());
+ jit_chain_guard(
+ JCC_JNE,
+ jit,
+ asm,
+ ocb,
+ 1,
+ Counter::rshift_amount_changed,
+ );
+
+ let shift_opnd = Opnd::UImm(shift_amt as u64);
+ let out_val = asm.rshift(lhs, shift_opnd);
+ let out_val = asm.or(out_val, 1.into());
+
+ let ret_opnd = asm.stack_push(Type::Fixnum);
+ asm.mov(ret_opnd, out_val);
+ true
+}
+
+fn jit_rb_int_xor(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ ocb: &mut OutlinedCb,
+ _ci: *const rb_callinfo,
+ _cme: *const rb_callable_method_entry_t,
+ _block: Option<BlockHandler>,
+ _argc: i32,
+ _known_recv_class: Option<VALUE>,
+) -> bool {
+ if asm.ctx.two_fixnums_on_stack(jit) != Some(true) {
+ return false;
+ }
+ guard_two_fixnums(jit, asm, ocb);
+
+ let rhs = asm.stack_pop(1);
+ let lhs = asm.stack_pop(1);
+
+ // XOR and then re-tag the resulting fixnum
+ let out_val = asm.xor(lhs, rhs);
+ let out_val = asm.or(out_val, 1.into());
+
+ let ret_opnd = asm.stack_push(Type::Fixnum);
+ asm.mov(ret_opnd, out_val);
true
}
@@ -4298,9 +5349,9 @@ fn jit_rb_int_aref(
ocb: &mut OutlinedCb,
_ci: *const rb_callinfo,
_cme: *const rb_callable_method_entry_t,
- _block: Option<IseqPtr>,
+ _block: Option<BlockHandler>,
argc: i32,
- _known_recv_class: *const VALUE,
+ _known_recv_class: Option<VALUE>,
) -> bool {
if argc != 1 {
return false;
@@ -4310,8 +5361,7 @@ fn jit_rb_int_aref(
}
guard_two_fixnums(jit, asm, ocb);
- asm.comment("Integer#[]");
- asm.spill_temps(); // for ccall (must be done before stack_pop)
+ asm_comment!(asm, "Integer#[]");
let obj = asm.stack_pop(1);
let recv = asm.stack_pop(1);
@@ -4322,6 +5372,182 @@ fn jit_rb_int_aref(
true
}
+fn jit_rb_float_plus(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ ocb: &mut OutlinedCb,
+ _ci: *const rb_callinfo,
+ _cme: *const rb_callable_method_entry_t,
+ _block: Option<BlockHandler>,
+ _argc: i32,
+ _known_recv_class: Option<VALUE>,
+) -> bool {
+ // Guard obj is Fixnum or Flonum to avoid rb_funcall on rb_num_coerce_bin
+ let comptime_obj = jit.peek_at_stack(&asm.ctx, 0);
+ if comptime_obj.fixnum_p() || comptime_obj.flonum_p() {
+ let obj = asm.stack_opnd(0);
+ jit_guard_known_klass(
+ jit,
+ asm,
+ ocb,
+ comptime_obj.class_of(),
+ obj,
+ obj.into(),
+ comptime_obj,
+ SEND_MAX_DEPTH,
+ Counter::guard_send_not_fixnum_or_flonum,
+ );
+ } else {
+ return false;
+ }
+
+ // Save the PC and SP because the callee may allocate Float on heap
+ jit_prepare_call_with_gc(jit, asm);
+
+ asm_comment!(asm, "Float#+");
+ let obj = asm.stack_opnd(0);
+ let recv = asm.stack_opnd(1);
+
+ let ret = asm.ccall(rb_float_plus as *const u8, vec![recv, obj]);
+ asm.stack_pop(2); // Keep recv during ccall for GC
+
+ let ret_opnd = asm.stack_push(Type::Unknown); // Flonum or heap Float
+ asm.mov(ret_opnd, ret);
+ true
+}
+
+fn jit_rb_float_minus(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ ocb: &mut OutlinedCb,
+ _ci: *const rb_callinfo,
+ _cme: *const rb_callable_method_entry_t,
+ _block: Option<BlockHandler>,
+ _argc: i32,
+ _known_recv_class: Option<VALUE>,
+) -> bool {
+ // Guard obj is Fixnum or Flonum to avoid rb_funcall on rb_num_coerce_bin
+ let comptime_obj = jit.peek_at_stack(&asm.ctx, 0);
+ if comptime_obj.fixnum_p() || comptime_obj.flonum_p() {
+ let obj = asm.stack_opnd(0);
+ jit_guard_known_klass(
+ jit,
+ asm,
+ ocb,
+ comptime_obj.class_of(),
+ obj,
+ obj.into(),
+ comptime_obj,
+ SEND_MAX_DEPTH,
+ Counter::guard_send_not_fixnum_or_flonum,
+ );
+ } else {
+ return false;
+ }
+
+ // Save the PC and SP because the callee may allocate Float on heap
+ jit_prepare_call_with_gc(jit, asm);
+
+ asm_comment!(asm, "Float#-");
+ let obj = asm.stack_opnd(0);
+ let recv = asm.stack_opnd(1);
+
+ let ret = asm.ccall(rb_float_minus as *const u8, vec![recv, obj]);
+ asm.stack_pop(2); // Keep recv during ccall for GC
+
+ let ret_opnd = asm.stack_push(Type::Unknown); // Flonum or heap Float
+ asm.mov(ret_opnd, ret);
+ true
+}
+
+fn jit_rb_float_mul(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ ocb: &mut OutlinedCb,
+ _ci: *const rb_callinfo,
+ _cme: *const rb_callable_method_entry_t,
+ _block: Option<BlockHandler>,
+ _argc: i32,
+ _known_recv_class: Option<VALUE>,
+) -> bool {
+ // Guard obj is Fixnum or Flonum to avoid rb_funcall on rb_num_coerce_bin
+ let comptime_obj = jit.peek_at_stack(&asm.ctx, 0);
+ if comptime_obj.fixnum_p() || comptime_obj.flonum_p() {
+ let obj = asm.stack_opnd(0);
+ jit_guard_known_klass(
+ jit,
+ asm,
+ ocb,
+ comptime_obj.class_of(),
+ obj,
+ obj.into(),
+ comptime_obj,
+ SEND_MAX_DEPTH,
+ Counter::guard_send_not_fixnum_or_flonum,
+ );
+ } else {
+ return false;
+ }
+
+ // Save the PC and SP because the callee may allocate Float on heap
+ jit_prepare_call_with_gc(jit, asm);
+
+ asm_comment!(asm, "Float#*");
+ let obj = asm.stack_opnd(0);
+ let recv = asm.stack_opnd(1);
+
+ let ret = asm.ccall(rb_float_mul as *const u8, vec![recv, obj]);
+ asm.stack_pop(2); // Keep recv during ccall for GC
+
+ let ret_opnd = asm.stack_push(Type::Unknown); // Flonum or heap Float
+ asm.mov(ret_opnd, ret);
+ true
+}
+
+fn jit_rb_float_div(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ ocb: &mut OutlinedCb,
+ _ci: *const rb_callinfo,
+ _cme: *const rb_callable_method_entry_t,
+ _block: Option<BlockHandler>,
+ _argc: i32,
+ _known_recv_class: Option<VALUE>,
+) -> bool {
+ // Guard obj is Fixnum or Flonum to avoid rb_funcall on rb_num_coerce_bin
+ let comptime_obj = jit.peek_at_stack(&asm.ctx, 0);
+ if comptime_obj.fixnum_p() || comptime_obj.flonum_p() {
+ let obj = asm.stack_opnd(0);
+ jit_guard_known_klass(
+ jit,
+ asm,
+ ocb,
+ comptime_obj.class_of(),
+ obj,
+ obj.into(),
+ comptime_obj,
+ SEND_MAX_DEPTH,
+ Counter::guard_send_not_fixnum_or_flonum,
+ );
+ } else {
+ return false;
+ }
+
+ // Save the PC and SP because the callee may allocate Float on heap
+ jit_prepare_call_with_gc(jit, asm);
+
+ asm_comment!(asm, "Float#/");
+ let obj = asm.stack_opnd(0);
+ let recv = asm.stack_opnd(1);
+
+ let ret = asm.ccall(rb_float_div as *const u8, vec![recv, obj]);
+ asm.stack_pop(2); // Keep recv during ccall for GC
+
+ let ret_opnd = asm.stack_push(Type::Unknown); // Flonum or heap Float
+ asm.mov(ret_opnd, ret);
+ true
+}
+
/// If string is frozen, duplicate it to get a non-frozen string. Otherwise, return it.
fn jit_rb_str_uplus(
jit: &mut JITState,
@@ -4329,9 +5555,9 @@ fn jit_rb_str_uplus(
_ocb: &mut OutlinedCb,
_ci: *const rb_callinfo,
_cme: *const rb_callable_method_entry_t,
- _block: Option<IseqPtr>,
+ _block: Option<BlockHandler>,
argc: i32,
- _known_recv_class: *const VALUE,
+ _known_recv_class: Option<VALUE>,
) -> bool
{
if argc != 0 {
@@ -4339,9 +5565,10 @@ fn jit_rb_str_uplus(
}
// We allocate when we dup the string
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_call_with_gc(jit, asm);
+ asm.spill_temps(); // For ccall. Unconditionally spill them for RegTemps consistency.
- asm.comment("Unary plus on string");
+ asm_comment!(asm, "Unary plus on string");
let recv_opnd = asm.stack_pop(1);
let recv_opnd = asm.load(recv_opnd);
let flags_opnd = asm.load(Opnd::mem(64, recv_opnd, RUBY_OFFSET_RBASIC_FLAGS));
@@ -4366,23 +5593,203 @@ fn jit_rb_str_uplus(
true
}
+fn jit_rb_str_length(
+ _jit: &mut JITState,
+ asm: &mut Assembler,
+ _ocb: &mut OutlinedCb,
+ _ci: *const rb_callinfo,
+ _cme: *const rb_callable_method_entry_t,
+ _block: Option<BlockHandler>,
+ _argc: i32,
+ _known_recv_class: Option<VALUE>,
+) -> bool {
+ asm_comment!(asm, "String#length");
+ extern "C" {
+ fn rb_str_length(str: VALUE) -> VALUE;
+ }
+
+ // This function cannot allocate or raise an exceptions
+ let recv = asm.stack_opnd(0);
+ let ret_opnd = asm.ccall(rb_str_length as *const u8, vec![recv]);
+ asm.stack_pop(1); // Keep recv on stack during ccall for GC
+
+ // Should be guaranteed to be a fixnum on 64-bit systems
+ let out_opnd = asm.stack_push(Type::Fixnum);
+ asm.mov(out_opnd, ret_opnd);
+
+ true
+}
+
fn jit_rb_str_bytesize(
_jit: &mut JITState,
asm: &mut Assembler,
_ocb: &mut OutlinedCb,
_ci: *const rb_callinfo,
_cme: *const rb_callable_method_entry_t,
- _block: Option<IseqPtr>,
+ _block: Option<BlockHandler>,
_argc: i32,
- _known_recv_class: *const VALUE,
+ _known_recv_class: Option<VALUE>,
) -> bool {
- asm.comment("String#bytesize");
+ asm_comment!(asm, "String#bytesize");
- asm.spill_temps(); // for ccall (must be done before stack_pop)
let recv = asm.stack_pop(1);
- let ret_opnd = asm.ccall(rb_str_bytesize as *const u8, vec![recv]);
+ asm_comment!(asm, "get string length");
+ let str_len_opnd = Opnd::mem(
+ std::os::raw::c_long::BITS as u8,
+ asm.load(recv),
+ RUBY_OFFSET_RSTRING_LEN as i32,
+ );
+
+ let len = asm.load(str_len_opnd);
+ let shifted_val = asm.lshift(len, Opnd::UImm(1));
+ let out_val = asm.or(shifted_val, Opnd::UImm(RUBY_FIXNUM_FLAG as u64));
+
+ let out_opnd = asm.stack_push(Type::Fixnum);
+
+ asm.mov(out_opnd, out_val);
+
+ true
+}
+
+fn jit_rb_str_byteslice(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ _ocb: &mut OutlinedCb,
+ _ci: *const rb_callinfo,
+ cme: *const rb_callable_method_entry_t,
+ _block: Option<BlockHandler>,
+ argc: i32,
+ _known_recv_class: Option<VALUE>,
+) -> bool {
+ if argc != 2 {
+ return false
+ }
+
+ // rb_str_byte_substr should be leaf if indexes are fixnums
+ match (asm.ctx.get_opnd_type(StackOpnd(0)), asm.ctx.get_opnd_type(StackOpnd(1))) {
+ (Type::Fixnum, Type::Fixnum) => {},
+ // Raises when non-integers are passed in, which requires the method frame
+ // to be pushed for the backtrace
+ _ => if !jit_prepare_lazy_frame_call(jit, asm, cme, StackOpnd(2)) {
+ return false;
+ }
+ }
+ asm_comment!(asm, "String#byteslice");
+
+ // rb_str_byte_substr allocates a substring
+ jit_prepare_call_with_gc(jit, asm);
+
+ // Get stack operands after potential SP change
+ let len = asm.stack_opnd(0);
+ let beg = asm.stack_opnd(1);
+ let recv = asm.stack_opnd(2);
+
+ let ret_opnd = asm.ccall(rb_str_byte_substr as *const u8, vec![recv, beg, len]);
+ asm.stack_pop(3);
+
+ let out_opnd = asm.stack_push(Type::Unknown);
+ asm.mov(out_opnd, ret_opnd);
+
+ true
+}
+
+fn jit_rb_str_getbyte(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ ocb: &mut OutlinedCb,
+ _ci: *const rb_callinfo,
+ _cme: *const rb_callable_method_entry_t,
+ _block: Option<BlockHandler>,
+ _argc: i32,
+ _known_recv_class: Option<VALUE>,
+) -> bool {
+ asm_comment!(asm, "String#getbyte");
+
+ // Don't pop since we may bail
+ let idx = asm.stack_opnd(0);
+ let recv = asm.stack_opnd(1);
+
+ let comptime_idx = jit.peek_at_stack(&asm.ctx, 0);
+ if comptime_idx.fixnum_p(){
+ jit_guard_known_klass(
+ jit,
+ asm,
+ ocb,
+ comptime_idx.class_of(),
+ idx,
+ idx.into(),
+ comptime_idx,
+ SEND_MAX_DEPTH,
+ Counter::getbyte_idx_not_fixnum,
+ );
+ } else {
+ return false;
+ }
+
+ // Untag the index
+ let idx = asm.rshift(idx, Opnd::UImm(1));
+
+ // If index is negative, exit
+ asm.cmp(idx, Opnd::UImm(0));
+ asm.jl(Target::side_exit(Counter::getbyte_idx_negative));
+
+ asm_comment!(asm, "get string length");
+ let recv = asm.load(recv);
+ let str_len_opnd = Opnd::mem(
+ std::os::raw::c_long::BITS as u8,
+ asm.load(recv),
+ RUBY_OFFSET_RSTRING_LEN as i32,
+ );
+
+ // Exit if the indes is out of bounds
+ asm.cmp(idx, str_len_opnd);
+ asm.jge(Target::side_exit(Counter::getbyte_idx_out_of_bounds));
+
+ let str_ptr = get_string_ptr(asm, recv);
+ // FIXME: could use SIB indexing here with proper support in backend
+ let str_ptr = asm.add(str_ptr, idx);
+ let byte = asm.load(Opnd::mem(8, str_ptr, 0));
+
+ // Zero-extend the byte to 64 bits
+ let byte = byte.with_num_bits(64).unwrap();
+ let byte = asm.and(byte, 0xFF.into());
+
+ // Tag the byte
+ let byte = asm.lshift(byte, Opnd::UImm(1));
+ let byte = asm.or(byte, Opnd::UImm(1));
+
+ asm.stack_pop(2); // Keep them on stack during ccall for GC
let out_opnd = asm.stack_push(Type::Fixnum);
+ asm.mov(out_opnd, byte);
+
+ true
+}
+
+fn jit_rb_str_setbyte(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ _ocb: &mut OutlinedCb,
+ _ci: *const rb_callinfo,
+ cme: *const rb_callable_method_entry_t,
+ _block: Option<BlockHandler>,
+ _argc: i32,
+ _known_recv_class: Option<VALUE>,
+) -> bool {
+ // Raises when index is out of range. Lazily push a frame in that case.
+ if !jit_prepare_lazy_frame_call(jit, asm, cme, StackOpnd(2)) {
+ return false;
+ }
+ asm_comment!(asm, "String#setbyte");
+
+ let value = asm.stack_opnd(0);
+ let index = asm.stack_opnd(1);
+ let recv = asm.stack_opnd(2);
+
+ let ret_opnd = asm.ccall(rb_str_setbyte as *const u8, vec![recv, index, value]);
+ asm.stack_pop(3); // Keep them on stack during ccall for GC
+
+ let out_opnd = asm.stack_push(Type::UnknownImm);
asm.mov(out_opnd, ret_opnd);
true
@@ -4398,12 +5805,12 @@ fn jit_rb_str_to_s(
_ocb: &mut OutlinedCb,
_ci: *const rb_callinfo,
_cme: *const rb_callable_method_entry_t,
- _block: Option<IseqPtr>,
+ _block: Option<BlockHandler>,
_argc: i32,
- known_recv_class: *const VALUE,
+ known_recv_class: Option<VALUE>,
) -> bool {
- if !known_recv_class.is_null() && unsafe { *known_recv_class == rb_cString } {
- asm.comment("to_s on plain string");
+ if unsafe { known_recv_class == Some(rb_cString) } {
+ asm_comment!(asm, "to_s on plain string");
// The method returns the receiver, which is already on the stack.
// No stack movement.
return true;
@@ -4418,22 +5825,17 @@ fn jit_rb_str_empty_p(
_ocb: &mut OutlinedCb,
_ci: *const rb_callinfo,
_cme: *const rb_callable_method_entry_t,
- _block: Option<IseqPtr>,
+ _block: Option<BlockHandler>,
_argc: i32,
- _known_recv_class: *const VALUE,
+ _known_recv_class: Option<VALUE>,
) -> bool {
- const _: () = assert!(
- RUBY_OFFSET_RSTRING_AS_HEAP_LEN == RUBY_OFFSET_RSTRING_EMBED_LEN,
- "same offset to len embedded or not so we can use one code path to read the length",
- );
-
let recv_opnd = asm.stack_pop(1);
- asm.comment("get string length");
+ asm_comment!(asm, "get string length");
let str_len_opnd = Opnd::mem(
std::os::raw::c_long::BITS as u8,
asm.load(recv_opnd),
- RUBY_OFFSET_RSTRING_AS_HEAP_LEN as i32,
+ RUBY_OFFSET_RSTRING_LEN as i32,
);
asm.cmp(str_len_opnd, Opnd::UImm(0));
@@ -4453,9 +5855,9 @@ fn jit_rb_str_concat(
_ocb: &mut OutlinedCb,
_ci: *const rb_callinfo,
_cme: *const rb_callable_method_entry_t,
- _block: Option<IseqPtr>,
+ _block: Option<BlockHandler>,
_argc: i32,
- _known_recv_class: *const VALUE,
+ _known_recv_class: Option<VALUE>,
) -> bool {
// The << operator can accept integer codepoints for characters
// as the argument. We only specially optimise string arguments.
@@ -4467,19 +5869,20 @@ fn jit_rb_str_concat(
}
// Guard that the concat argument is a string
- guard_object_is_string(asm, asm.stack_opnd(0), StackOpnd(0), Counter::send_not_string);
+ guard_object_is_string(asm, asm.stack_opnd(0), StackOpnd(0), Counter::guard_send_not_string);
- // Guard buffers from GC since rb_str_buf_append may allocate. During the VM lock on GC,
- // other Ractors may trigger global invalidation, so we need ctx.clear_local_types().
- // PC is used on errors like Encoding::CompatibilityError raised by rb_str_buf_append.
- jit_prepare_routine_call(jit, asm);
+ // Guard buffers from GC since rb_str_buf_append may allocate.
+ // rb_str_buf_append may raise Encoding::CompatibilityError, but we accept compromised
+ // backtraces on this method since the interpreter does the same thing on opt_ltlt.
+ jit_prepare_non_leaf_call(jit, asm);
+ asm.spill_temps(); // For ccall. Unconditionally spill them for RegTemps consistency.
let concat_arg = asm.stack_pop(1);
let recv = asm.stack_pop(1);
// Test if string encodings differ. If different, use rb_str_append. If the same,
// use rb_yjit_str_simple_append, which calls rb_str_cat.
- asm.comment("<< on strings");
+ asm_comment!(asm, "<< on strings");
// Take receiver's object flags XOR arg's flags. If any
// string-encoding flags are different between the two,
@@ -4496,19 +5899,18 @@ fn jit_rb_str_concat(
asm.jnz(enc_mismatch);
// If encodings match, call the simple append function and jump to return
- asm.spill_temps(); // for ccall
let ret_opnd = asm.ccall(rb_yjit_str_simple_append as *const u8, vec![recv, concat_arg]);
let ret_label = asm.new_label("func_return");
- let stack_ret = asm.stack_push(Type::CString);
+ let stack_ret = asm.stack_push(Type::TString);
asm.mov(stack_ret, ret_opnd);
asm.stack_pop(1); // forget stack_ret to re-push after ccall
asm.jmp(ret_label);
// If encodings are different, use a slower encoding-aware concatenate
asm.write_label(enc_mismatch);
- asm.spill_temps(); // for ccall
+ asm.spill_temps(); // Ignore the register for the other local branch
let ret_opnd = asm.ccall(rb_str_buf_append as *const u8, vec![recv, concat_arg]);
- let stack_ret = asm.stack_push(Type::CString);
+ let stack_ret = asm.stack_push(Type::TString);
asm.mov(stack_ret, ret_opnd);
// Drop through to return
@@ -4524,9 +5926,9 @@ fn jit_rb_ary_empty_p(
_ocb: &mut OutlinedCb,
_ci: *const rb_callinfo,
_cme: *const rb_callable_method_entry_t,
- _block: Option<IseqPtr>,
+ _block: Option<BlockHandler>,
_argc: i32,
- _known_recv_class: *const VALUE,
+ _known_recv_class: Option<VALUE>,
) -> bool {
let array_opnd = asm.stack_pop(1);
let array_reg = asm.load(array_opnd);
@@ -4541,50 +5943,98 @@ fn jit_rb_ary_empty_p(
return true;
}
+// Codegen for rb_ary_length()
+fn jit_rb_ary_length(
+ _jit: &mut JITState,
+ asm: &mut Assembler,
+ _ocb: &mut OutlinedCb,
+ _ci: *const rb_callinfo,
+ _cme: *const rb_callable_method_entry_t,
+ _block: Option<BlockHandler>,
+ _argc: i32,
+ _known_recv_class: Option<VALUE>,
+) -> bool {
+ let array_opnd = asm.stack_pop(1);
+ let array_reg = asm.load(array_opnd);
+ let len_opnd = get_array_len(asm, array_reg);
+
+ // Convert the length to a fixnum
+ let shifted_val = asm.lshift(len_opnd, Opnd::UImm(1));
+ let out_val = asm.or(shifted_val, Opnd::UImm(RUBY_FIXNUM_FLAG as u64));
+
+ let out_opnd = asm.stack_push(Type::Fixnum);
+ asm.store(out_opnd, out_val);
+
+ return true;
+}
+
fn jit_rb_ary_push(
jit: &mut JITState,
asm: &mut Assembler,
_ocb: &mut OutlinedCb,
_ci: *const rb_callinfo,
_cme: *const rb_callable_method_entry_t,
- _block: Option<IseqPtr>,
+ _block: Option<BlockHandler>,
_argc: i32,
- _known_recv_class: *const VALUE,
+ _known_recv_class: Option<VALUE>,
) -> bool {
- asm.comment("Array#<<");
+ asm_comment!(asm, "Array#<<");
- // rb_ary_push allocates memory for buffer extension
- jit_prepare_routine_call(jit, asm);
+ // rb_ary_push allocates memory for buffer extension and can raise FrozenError
+ // Not using a lazy frame here since the interpreter also has a truncated
+ // stack trace from opt_ltlt.
+ jit_prepare_non_leaf_call(jit, asm);
- let item_opnd = asm.stack_pop(1);
- let ary_opnd = asm.stack_pop(1);
+ let item_opnd = asm.stack_opnd(0);
+ let ary_opnd = asm.stack_opnd(1);
let ret = asm.ccall(rb_ary_push as *const u8, vec![ary_opnd, item_opnd]);
+ asm.stack_pop(2); // Keep them on stack during ccall for GC
let ret_opnd = asm.stack_push(Type::TArray);
asm.mov(ret_opnd, ret);
true
}
+// Just a leaf method, but not using `Primitive.attr! :leaf` since BOP methods can't use it.
+fn jit_rb_hash_empty_p(
+ _jit: &mut JITState,
+ asm: &mut Assembler,
+ _ocb: &mut OutlinedCb,
+ _ci: *const rb_callinfo,
+ _cme: *const rb_callable_method_entry_t,
+ _block: Option<BlockHandler>,
+ _argc: i32,
+ _known_recv_class: Option<VALUE>,
+) -> bool {
+ asm_comment!(asm, "Hash#empty?");
+
+ let hash_opnd = asm.stack_pop(1);
+ let ret = asm.ccall(rb_hash_empty_p as *const u8, vec![hash_opnd]);
+
+ let ret_opnd = asm.stack_push(Type::UnknownImm);
+ asm.mov(ret_opnd, ret);
+ true
+}
+
fn jit_obj_respond_to(
jit: &mut JITState,
asm: &mut Assembler,
ocb: &mut OutlinedCb,
_ci: *const rb_callinfo,
_cme: *const rb_callable_method_entry_t,
- _block: Option<IseqPtr>,
+ _block: Option<BlockHandler>,
argc: i32,
- known_recv_class: *const VALUE,
+ known_recv_class: Option<VALUE>,
) -> bool {
// respond_to(:sym) or respond_to(:sym, true)
if argc != 1 && argc != 2 {
return false;
}
- if known_recv_class.is_null() {
- return false;
- }
-
- let recv_class = unsafe { *known_recv_class };
+ let recv_class = match known_recv_class {
+ Some(class) => class,
+ None => return false,
+ };
// Get the method_id from compile time. We will later add a guard against it.
let mid_sym = jit.peek_at_stack(&asm.ctx, (argc - 1) as isize);
@@ -4620,20 +6070,28 @@ fn jit_obj_respond_to(
};
let result = match (visibility, allow_priv) {
- (METHOD_VISI_UNDEF, _) => Qfalse, // No method => false
- (METHOD_VISI_PUBLIC, _) => Qtrue, // Public method => true regardless of include_all
- (_, Some(true)) => Qtrue, // include_all => always true
+ (METHOD_VISI_UNDEF, _) => {
+ // No method, we can return false given respond_to_missing? hasn't been overridden.
+ // In the future, we might want to jit the call to respond_to_missing?
+ if !assume_method_basic_definition(jit, asm, ocb, recv_class, ID!(respond_to_missing)) {
+ return false;
+ }
+ Qfalse
+ }
+ (METHOD_VISI_PUBLIC, _) | // Public method => fine regardless of include_all
+ (_, Some(true)) => { // include_all => all visibility are acceptable
+ // Method exists and has acceptable visibility
+ if cme_def_type == VM_METHOD_TYPE_NOTIMPLEMENTED {
+ // C method with rb_f_notimplement(). `respond_to?` returns false
+ // without consulting `respond_to_missing?`. See also: rb_add_method_cfunc()
+ Qfalse
+ } else {
+ Qtrue
+ }
+ }
(_, _) => return false // not public and include_all not known, can't compile
};
- if result != Qtrue {
- // Only if respond_to_missing? hasn't been overridden
- // In the future, we might want to jit the call to respond_to_missing?
- if !assume_method_basic_definition(jit, asm, ocb, recv_class, idRespond_to_missing.into()) {
- return false;
- }
- }
-
// Invalidate this block if method lookup changes for the method being queried. This works
// both for the case where a method does or does not exist, as for the latter we asked for a
// "negative CME" earlier.
@@ -4648,9 +6106,16 @@ fn jit_obj_respond_to(
let _recv_opnd = asm.stack_pop(1);
// This is necessary because we have no guarantee that sym_opnd is a constant
- asm.comment("guard known mid");
+ asm_comment!(asm, "guard known mid");
asm.cmp(sym_opnd, mid_sym.into());
- asm.jne(Target::side_exit(Counter::send_mid_mismatch));
+ jit_chain_guard(
+ JCC_JNE,
+ jit,
+ asm,
+ ocb,
+ SEND_MAX_DEPTH,
+ Counter::guard_send_respond_to_mid_mismatch,
+ );
jit_putobject(asm, result);
@@ -4663,11 +6128,26 @@ fn jit_rb_f_block_given_p(
_ocb: &mut OutlinedCb,
_ci: *const rb_callinfo,
_cme: *const rb_callable_method_entry_t,
- _block: Option<IseqPtr>,
+ _block: Option<BlockHandler>,
_argc: i32,
- _known_recv_class: *const VALUE,
+ _known_recv_class: Option<VALUE>,
) -> bool {
- asm.comment("block_given?");
+ asm.stack_pop(1);
+ let out_opnd = asm.stack_push(Type::UnknownImm);
+
+ gen_block_given(jit, asm, out_opnd, Qtrue.into(), Qfalse.into());
+
+ true
+}
+
+fn gen_block_given(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ out_opnd: Opnd,
+ true_opnd: Opnd,
+ false_opnd: Opnd,
+) {
+ asm_comment!(asm, "block_given?");
// Same as rb_vm_frame_block_handler
let ep_opnd = gen_get_lep(jit, asm);
@@ -4675,13 +6155,66 @@ fn jit_rb_f_block_given_p(
Opnd::mem(64, ep_opnd, SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_SPECVAL)
);
- asm.stack_pop(1);
- let out_opnd = asm.stack_push(Type::UnknownImm);
-
// Return `block_handler != VM_BLOCK_HANDLER_NONE`
asm.cmp(block_handler, VM_BLOCK_HANDLER_NONE.into());
- let block_given = asm.csel_ne(Qtrue.into(), Qfalse.into());
+ let block_given = asm.csel_ne(true_opnd, false_opnd);
asm.mov(out_opnd, block_given);
+}
+
+// Codegen for rb_class_superclass()
+fn jit_rb_class_superclass(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ _ocb: &mut OutlinedCb,
+ _ci: *const rb_callinfo,
+ cme: *const rb_callable_method_entry_t,
+ _block: Option<crate::codegen::BlockHandler>,
+ _argc: i32,
+ _known_recv_class: Option<VALUE>,
+) -> bool {
+ extern "C" {
+ fn rb_class_superclass(klass: VALUE) -> VALUE;
+ }
+
+ if !jit_prepare_lazy_frame_call(jit, asm, cme, StackOpnd(0)) {
+ return false;
+ }
+
+ asm_comment!(asm, "Class#superclass");
+ let recv_opnd = asm.stack_opnd(0);
+ let ret = asm.ccall(rb_class_superclass as *const u8, vec![recv_opnd]);
+
+ asm.stack_pop(1);
+ let ret_opnd = asm.stack_push(Type::Unknown);
+ asm.mov(ret_opnd, ret);
+
+ true
+}
+
+fn jit_rb_case_equal(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ ocb: &mut OutlinedCb,
+ _ci: *const rb_callinfo,
+ _cme: *const rb_callable_method_entry_t,
+ _block: Option<BlockHandler>,
+ _argc: i32,
+ known_recv_class: Option<VALUE>,
+) -> bool {
+ if !jit.assume_expected_cfunc( asm, ocb, known_recv_class.unwrap(), ID!(eq), rb_obj_equal as _) {
+ return false;
+ }
+
+ asm_comment!(asm, "case_equal: {}#===", get_class_name(known_recv_class));
+
+ // Compare the arguments
+ let arg1 = asm.stack_pop(1);
+ let arg0 = asm.stack_pop(1);
+ asm.cmp(arg0, arg1);
+ let ret_opnd = asm.csel_e(Qtrue.into(), Qfalse.into());
+
+ let stack_ret = asm.stack_push(Type::UnknownImm);
+ asm.mov(stack_ret, ret_opnd);
true
}
@@ -4692,11 +6225,11 @@ fn jit_thread_s_current(
_ocb: &mut OutlinedCb,
_ci: *const rb_callinfo,
_cme: *const rb_callable_method_entry_t,
- _block: Option<IseqPtr>,
+ _block: Option<BlockHandler>,
_argc: i32,
- _known_recv_class: *const VALUE,
+ _known_recv_class: Option<VALUE>,
) -> bool {
- asm.comment("Thread.current");
+ asm_comment!(asm, "Thread.current");
asm.stack_pop(1);
// ec->thread_ptr
@@ -4713,8 +6246,13 @@ fn jit_thread_s_current(
// Check if we know how to codegen for a particular cfunc method
fn lookup_cfunc_codegen(def: *const rb_method_definition_t) -> Option<MethodGenFn> {
let method_serial = unsafe { get_def_method_serial(def) };
+ let table = unsafe { METHOD_CODEGEN_TABLE.as_ref().unwrap() };
- CodegenGlobals::look_up_codegen_method(method_serial)
+ let option_ref = table.get(&method_serial);
+ match option_ref {
+ None => None,
+ Some(&mgf) => Some(mgf), // Deref
+ }
}
// Is anyone listening for :c_call and :c_return event currently?
@@ -4746,13 +6284,25 @@ unsafe extern "C" fn build_kwhash(ci: *const rb_callinfo, sp: *const VALUE) -> V
// at sp[-2]. Depending on the frame type, it can serve different purposes,
// which are covered here by enum variants.
enum SpecVal {
- None,
- BlockISeq(IseqPtr),
- BlockParamProxy,
+ BlockHandler(Option<BlockHandler>),
PrevEP(*const VALUE),
PrevEPOpnd(Opnd),
}
+// Each variant represents a branch in vm_caller_setup_arg_block.
+#[derive(Clone, Copy)]
+pub enum BlockHandler {
+ // send, invokesuper: blockiseq operand
+ BlockISeq(IseqPtr),
+ // invokesuper: GET_BLOCK_HANDLER() (GET_LEP()[VM_ENV_DATA_INDEX_SPECVAL])
+ LEPSpecVal,
+ // part of the allocate-free block forwarding scheme
+ BlockParamProxy,
+ // To avoid holding the block arg (e.g. proc and symbol) across C calls,
+ // we might need to set the block handler early in the call sequence
+ AlreadySet,
+}
+
struct ControlFrame {
recv: Opnd,
sp: Opnd,
@@ -4761,7 +6311,6 @@ struct ControlFrame {
frame_type: u32,
specval: SpecVal,
cme: *const rb_callable_method_entry_t,
- local_size: i32
}
// Codegen performing a similar (but not identical) function to vm_push_frame
@@ -4776,21 +6325,17 @@ struct ControlFrame {
// * Provided sp should point to the new frame's sp, immediately following locals and the environment
// * At entry, CFP points to the caller (not callee) frame
// * At exit, ec->cfp is updated to the pushed CFP
-// * CFP and SP registers are updated only if set_sp_cfp is set
+// * SP register is updated only if frame.iseq is set
// * Stack overflow is not checked (should be done by the caller)
// * Interrupts are not checked (should be done by the caller)
fn gen_push_frame(
jit: &mut JITState,
asm: &mut Assembler,
- set_sp_cfp: bool, // if true CFP and SP will be switched to the callee
frame: ControlFrame,
- rest_arg: Option<(i32, Opnd)>,
) {
- assert!(frame.local_size >= 0);
-
let sp = frame.sp;
- asm.comment("push cme, specval, frame type");
+ asm_comment!(asm, "push cme, specval, frame type");
// Write method entry at sp[-3]
// sp[-3] = me;
@@ -4802,27 +6347,31 @@ fn gen_push_frame(
// the outer environment depending on the frame type.
// sp[-2] = specval;
let specval: Opnd = match frame.specval {
- SpecVal::None => {
- VM_BLOCK_HANDLER_NONE.into()
- }
- SpecVal::BlockISeq(block_iseq) => {
- // Change cfp->block_code in the current frame. See vm_caller_setup_arg_block().
- // VM_CFP_TO_CAPTURED_BLOCK does &cfp->self, rb_captured_block->code.iseq aliases
- // with cfp->block_code.
- asm.store(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_BLOCK_CODE), VALUE::from(block_iseq).into());
-
- let cfp_self = asm.lea(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF));
- asm.or(cfp_self, Opnd::Imm(1))
- }
- SpecVal::BlockParamProxy => {
- let ep_opnd = gen_get_lep(jit, asm);
- let block_handler = asm.load(
- Opnd::mem(64, ep_opnd, SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_SPECVAL)
- );
-
- asm.store(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_BLOCK_CODE), block_handler);
-
- block_handler
+ SpecVal::BlockHandler(None) => VM_BLOCK_HANDLER_NONE.into(),
+ SpecVal::BlockHandler(Some(block_handler)) => {
+ match block_handler {
+ BlockHandler::BlockISeq(block_iseq) => {
+ // Change cfp->block_code in the current frame. See vm_caller_setup_arg_block().
+ // VM_CFP_TO_CAPTURED_BLOCK does &cfp->self, rb_captured_block->code.iseq aliases
+ // with cfp->block_code.
+ asm.store(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_BLOCK_CODE), VALUE::from(block_iseq).into());
+
+ let cfp_self = asm.lea(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF));
+ asm.or(cfp_self, Opnd::Imm(1))
+ }
+ BlockHandler::LEPSpecVal => {
+ let lep_opnd = gen_get_lep(jit, asm);
+ asm.load(Opnd::mem(64, lep_opnd, SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_SPECVAL))
+ }
+ BlockHandler::BlockParamProxy => {
+ let ep_opnd = gen_get_lep(jit, asm);
+ let block_handler = asm.load(
+ Opnd::mem(64, ep_opnd, SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_SPECVAL)
+ );
+ block_handler
+ }
+ BlockHandler::AlreadySet => 0.into(), // unused
+ }
}
SpecVal::PrevEP(prev_ep) => {
let tagged_prev_ep = (prev_ep as usize) | 1;
@@ -4830,9 +6379,13 @@ fn gen_push_frame(
}
SpecVal::PrevEPOpnd(ep_opnd) => {
asm.or(ep_opnd, 1.into())
- },
+ }
};
- asm.store(Opnd::mem(64, sp, SIZEOF_VALUE_I32 * -2), specval);
+ if let SpecVal::BlockHandler(Some(BlockHandler::AlreadySet)) = frame.specval {
+ asm_comment!(asm, "specval should have been set");
+ } else {
+ asm.store(Opnd::mem(64, sp, SIZEOF_VALUE_I32 * -2), specval);
+ }
// Write env flags at sp[-1]
// sp[-1] = frame_type;
@@ -4851,16 +6404,14 @@ fn gen_push_frame(
// .self = recv,
// .ep = <sp - 1>,
// .block_code = 0,
- // .__bp__ = sp,
// };
- asm.comment("push callee control frame");
+ asm_comment!(asm, "push callee control frame");
// For an iseq call PC may be None, in which case we will not set PC and will allow jitted code
// to set it as necessary.
if let Some(pc) = frame.pc {
asm.mov(cfp_opnd(RUBY_OFFSET_CFP_PC), pc.into());
};
- asm.mov(cfp_opnd(RUBY_OFFSET_CFP_BP), sp);
asm.mov(cfp_opnd(RUBY_OFFSET_CFP_SP), sp);
let iseq: Opnd = if let Some(iseq) = frame.iseq {
VALUE::from(iseq).into()
@@ -4871,46 +6422,16 @@ fn gen_push_frame(
asm.mov(cfp_opnd(RUBY_OFFSET_CFP_SELF), frame.recv);
asm.mov(cfp_opnd(RUBY_OFFSET_CFP_BLOCK_CODE), 0.into());
- // This Qnil fill snippet potentially requires 2 more registers on Arm, one for Qnil and
- // another for calculating the address in case there are a lot of local variables. So doing
- // this after releasing the register for specval and the receiver to avoid register spill.
- let num_locals = frame.local_size;
- if num_locals > 0 {
- asm.comment("initialize locals");
-
- // Initialize local variables to Qnil
- for i in 0..num_locals {
- let offs = SIZEOF_VALUE_I32 * (i - num_locals - 3);
- asm.store(Opnd::mem(64, sp, offs), Qnil.into());
- }
- }
-
- if let Some((opts_missing, rest_arg)) = rest_arg {
- // We want to set the rest_param just after the optional arguments
- let index = opts_missing - num_locals - 3;
- let offset = SIZEOF_VALUE_I32 * index;
- asm.store(Opnd::mem(64, sp, offset), rest_arg);
- }
-
- // Spill stack temps to let the callee use them (must be done before changing SP)
- asm.spill_temps();
+ if frame.iseq.is_some() {
+ // Spill stack temps to let the callee use them (must be done before changing the SP register)
+ asm.spill_temps();
- if set_sp_cfp {
// Saving SP before calculating ep avoids a dependency on a register
// However this must be done after referencing frame.recv, which may be SP-relative
asm.mov(SP, sp);
}
let ep = asm.sub(sp, SIZEOF_VALUE.into());
asm.mov(cfp_opnd(RUBY_OFFSET_CFP_EP), ep);
-
- asm.comment("switch to new CFP");
- let new_cfp = asm.lea(cfp_opnd(0));
- if set_sp_cfp {
- asm.mov(CFP, new_cfp);
- asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), CFP);
- } else {
- asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), new_cfp);
- }
}
fn gen_send_cfunc(
@@ -4919,8 +6440,8 @@ fn gen_send_cfunc(
ocb: &mut OutlinedCb,
ci: *const rb_callinfo,
cme: *const rb_callable_method_entry_t,
- block: Option<IseqPtr>,
- recv_known_klass: *const VALUE,
+ block: Option<BlockHandler>,
+ recv_known_class: Option<VALUE>,
flags: u32,
argc: i32,
) -> Option<CodegenStatus> {
@@ -4928,38 +6449,18 @@ fn gen_send_cfunc(
let cfunc_argc = unsafe { get_mct_argc(cfunc) };
let mut argc = argc;
- // If the function expects a Ruby array of arguments
- if cfunc_argc < 0 && cfunc_argc != -1 {
- gen_counter_incr(asm, Counter::send_cfunc_ruby_array_varg);
- return None;
- }
-
- // We aren't handling a vararg cfuncs with splat currently.
- if flags & VM_CALL_ARGS_SPLAT != 0 && cfunc_argc == -1 {
- gen_counter_incr(asm, Counter::send_args_splat_cfunc_var_args);
- return None;
- }
+ // Splat call to a C method that takes `VALUE *` and `len`
+ let variable_splat = flags & VM_CALL_ARGS_SPLAT != 0 && cfunc_argc == -1;
+ let block_arg = flags & VM_CALL_ARGS_BLOCKARG != 0;
- if flags & VM_CALL_ARGS_SPLAT != 0 && flags & VM_CALL_ZSUPER != 0 {
- // zsuper methods are super calls without any arguments.
- // They are also marked as splat, but don't actually have an array
- // they pull arguments from, instead we need to change to call
- // a different method with the current stack.
- gen_counter_incr(asm, Counter::send_args_splat_cfunc_zuper);
+ // If it's a splat and the method expects a Ruby array of arguments
+ if cfunc_argc == -2 && flags & VM_CALL_ARGS_SPLAT != 0 {
+ gen_counter_incr(asm, Counter::send_cfunc_splat_neg2);
return None;
}
- // In order to handle backwards compatibility between ruby 3 and 2
- // ruby2_keywords was introduced. It is called only on methods
- // with splat and changes they way they handle them.
- // We are just going to not compile these.
- // https://docs.ruby-lang.org/en/3.2/Module.html#method-i-ruby2_keywords
- if unsafe {
- get_iseq_flags_ruby2_keywords(jit.iseq) && flags & VM_CALL_ARGS_SPLAT != 0
- } {
- gen_counter_incr(asm, Counter::send_args_splat_cfunc_ruby2_keywords);
- return None;
- }
+ exit_if_kwsplat_non_nil(asm, flags, Counter::send_cfunc_kw_splat_non_nil)?;
+ let kw_splat = flags & VM_CALL_KW_SPLAT != 0;
let kw_arg = unsafe { vm_ci_kwarg(ci) };
let kw_arg_num = if kw_arg.is_null() {
@@ -4979,13 +6480,31 @@ fn gen_send_cfunc(
return None;
}
+ // Increment total cfunc send count
+ gen_counter_incr(asm, Counter::num_send_cfunc);
+
// Delegate to codegen for C methods if we have it.
- if kw_arg.is_null() && flags & VM_CALL_OPT_SEND == 0 && flags & VM_CALL_ARGS_SPLAT == 0 && (cfunc_argc == -1 || argc == cfunc_argc) {
- let codegen_p = lookup_cfunc_codegen(unsafe { (*cme).def });
+ if kw_arg.is_null() &&
+ !kw_splat &&
+ flags & VM_CALL_OPT_SEND == 0 &&
+ flags & VM_CALL_ARGS_SPLAT == 0 &&
+ (cfunc_argc == -1 || argc == cfunc_argc) {
let expected_stack_after = asm.ctx.get_stack_size() as i32 - argc;
- if let Some(known_cfunc_codegen) = codegen_p {
- if known_cfunc_codegen(jit, asm, ocb, ci, cme, block, argc, recv_known_klass) {
+ if let Some(known_cfunc_codegen) = lookup_cfunc_codegen(unsafe { (*cme).def }) {
+ // We don't push a frame for specialized cfunc codegen, so the generated code must be leaf.
+ // However, the interpreter doesn't push a frame on opt_* instruction either, so we allow
+ // non-sendish instructions to break this rule as an exception.
+ let cfunc_codegen = if jit.is_sendish() {
+ asm.with_leaf_ccall(|asm|
+ perf_call!("gen_send_cfunc: ", known_cfunc_codegen(jit, asm, ocb, ci, cme, block, argc, recv_known_class))
+ )
+ } else {
+ perf_call!("gen_send_cfunc: ", known_cfunc_codegen(jit, asm, ocb, ci, cme, block, argc, recv_known_class))
+ };
+
+ if cfunc_codegen {
assert_eq!(expected_stack_after, asm.ctx.get_stack_size() as i32);
+ gen_counter_incr(asm, Counter::num_send_cfunc_inline);
// cfunc codegen generated code. Terminate the block so
// there isn't multiple calls in the same block.
jump_to_next_insn(jit, asm, ocb);
@@ -4995,15 +6514,35 @@ fn gen_send_cfunc(
}
// Check for interrupts
- gen_check_ints(asm, Counter::send_interrupted);
+ gen_check_ints(asm, Counter::guard_send_interrupted);
// Stack overflow check
// #define CHECK_VM_STACK_OVERFLOW0(cfp, sp, margin)
// REG_CFP <= REG_SP + 4 * SIZEOF_VALUE + sizeof(rb_control_frame_t)
- asm.comment("stack overflow check");
- let stack_limit = asm.lea(asm.ctx.sp_opnd((SIZEOF_VALUE * 4 + 2 * RUBY_SIZEOF_CONTROL_FRAME) as isize));
+ asm_comment!(asm, "stack overflow check");
+ const _: () = assert!(RUBY_SIZEOF_CONTROL_FRAME % SIZEOF_VALUE == 0, "sizeof(rb_control_frame_t) is a multiple of sizeof(VALUE)");
+ let stack_limit = asm.lea(asm.ctx.sp_opnd((4 + 2 * (RUBY_SIZEOF_CONTROL_FRAME / SIZEOF_VALUE)) as i32));
asm.cmp(CFP, stack_limit);
- asm.jbe(Target::side_exit(Counter::send_se_cf_overflow));
+ asm.jbe(Target::side_exit(Counter::guard_send_se_cf_overflow));
+
+ // Guard for variable length splat call before any modifications to the stack
+ if variable_splat {
+ let splat_array_idx = i32::from(kw_splat) + i32::from(block_arg);
+ let comptime_splat_array = jit.peek_at_stack(&asm.ctx, splat_array_idx as isize);
+ if unsafe { rb_yjit_ruby2_keywords_splat_p(comptime_splat_array) } != 0 {
+ gen_counter_incr(asm, Counter::send_cfunc_splat_varg_ruby2_keywords);
+ return None;
+ }
+
+ let splat_array = asm.stack_opnd(splat_array_idx);
+ guard_object_is_array(asm, splat_array, splat_array.into(), Counter::guard_send_splat_not_array);
+
+ asm_comment!(asm, "guard variable length splat call servicable");
+ let sp = asm.ctx.sp_opnd(0);
+ let proceed = asm.ccall(rb_yjit_splat_varg_checks as _, vec![sp, splat_array, CFP]);
+ asm.cmp(proceed, Qfalse.into());
+ asm.je(Target::side_exit(Counter::guard_send_cfunc_bad_splat_vargs));
+ }
// Number of args which will be passed through to the callee
// This is adjusted by the kwargs being combined into a hash.
@@ -5013,6 +6552,10 @@ fn gen_send_cfunc(
argc - kw_arg_num + 1
};
+ // Exclude the kw_splat hash from arity check
+ if kw_splat {
+ passed_argc -= 1;
+ }
// If the argument count doesn't match
if cfunc_argc >= 0 && cfunc_argc != passed_argc && flags & VM_CALL_ARGS_SPLAT == 0 {
@@ -5026,7 +6569,6 @@ fn gen_send_cfunc(
return None;
}
- let block_arg = flags & VM_CALL_ARGS_BLOCKARG != 0;
let block_arg_type = if block_arg {
Some(asm.ctx.get_opnd_type(StackOpnd(0)))
} else {
@@ -5041,7 +6583,7 @@ fn gen_send_cfunc(
// Nothing to do
}
_ => {
- gen_counter_incr(asm, Counter::send_block_arg);
+ gen_counter_incr(asm, Counter::send_cfunc_block_arg);
return None;
}
}
@@ -5063,9 +6605,17 @@ fn gen_send_cfunc(
}
}
- // push_splat_args does stack manipulation so we can no longer side exit
- if flags & VM_CALL_ARGS_SPLAT != 0 {
- assert!(cfunc_argc >= 0);
+ // Pop the empty kw_splat hash
+ if kw_splat {
+ // Only `**nil` is supported right now. Checked in exit_if_kwsplat_non_nil()
+ assert_eq!(Type::Nil, asm.ctx.get_opnd_type(StackOpnd(0)));
+ asm.stack_pop(1);
+ argc -= 1;
+ }
+
+ // Splat handling when C method takes a static number of arguments.
+ // push_splat_args() does stack manipulation so we can no longer side exit
+ if flags & VM_CALL_ARGS_SPLAT != 0 && cfunc_argc >= 0 {
let required_args : u32 = (cfunc_argc as u32).saturating_sub(argc as u32 - 1);
// + 1 because we pass self
if required_args + 1 >= C_ARG_OPNDS.len() as u32 {
@@ -5088,22 +6638,38 @@ fn gen_send_cfunc(
handle_opt_send_shift_stack(asm, argc);
}
+ // Push a dynamic number of items from the splat array to the stack when calling a vargs method
+ let dynamic_splat_size = if variable_splat {
+ asm_comment!(asm, "variable length splat");
+ let stack_splat_array = asm.lea(asm.stack_opnd(0));
+ Some(asm.ccall(rb_yjit_splat_varg_cfunc as _, vec![stack_splat_array]))
+ } else {
+ None
+ };
+
// Points to the receiver operand on the stack
let recv = asm.stack_opnd(argc);
// Store incremented PC into current control frame in case callee raises.
jit_save_pc(jit, asm);
- // Increment the stack pointer by 3 (in the callee)
- // sp += 3
- let sp = asm.lea(asm.ctx.sp_opnd((SIZEOF_VALUE as isize) * 3));
+ // Find callee's SP with space for metadata.
+ // Usually sp+3.
+ let sp = if let Some(splat_size) = dynamic_splat_size {
+ // Compute the callee's SP at runtime in case we accept a variable size for the splat array
+ const _: () = assert!(SIZEOF_VALUE == 8, "opting for a shift since mul on A64 takes no immediates");
+ let splat_size_bytes = asm.lshift(splat_size, 3usize.into());
+ // 3 items for method metadata, minus one to remove the splat array
+ let static_stack_top = asm.lea(asm.ctx.sp_opnd(2));
+ asm.add(static_stack_top, splat_size_bytes)
+ } else {
+ asm.lea(asm.ctx.sp_opnd(3))
+ };
let specval = if block_arg_type == Some(Type::BlockParamProxy) {
- SpecVal::BlockParamProxy
- } else if let Some(block_iseq) = block {
- SpecVal::BlockISeq(block_iseq)
+ SpecVal::BlockHandler(Some(BlockHandler::BlockParamProxy))
} else {
- SpecVal::None
+ SpecVal::BlockHandler(block)
};
let mut frame_type = VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL;
@@ -5111,7 +6677,7 @@ fn gen_send_cfunc(
frame_type |= VM_FRAME_FLAG_CFRAME_KW
}
- gen_push_frame(jit, asm, false, ControlFrame {
+ perf_call!("gen_send_cfunc: ", gen_push_frame(jit, asm, ControlFrame {
frame_type,
specval,
cme,
@@ -5123,12 +6689,15 @@ fn gen_send_cfunc(
None // Leave PC uninitialized as cfuncs shouldn't read it
},
iseq: None,
- local_size: 0,
- }, None);
+ }));
+
+ asm_comment!(asm, "set ec->cfp");
+ let new_cfp = asm.lea(Opnd::mem(64, CFP, -(RUBY_SIZEOF_CONTROL_FRAME as i32)));
+ asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), new_cfp);
if !kw_arg.is_null() {
// Build a hash from all kwargs passed
- asm.comment("build_kwhash");
+ asm_comment!(asm, "build_kwhash");
let imemo_ci = VALUE(ci as usize);
assert_ne!(0, unsafe { rb_IMEMO_TYPE_P(imemo_ci, imemo_callinfo) },
"we assume all callinfos with kwargs are on the GC heap");
@@ -5140,35 +6709,51 @@ fn gen_send_cfunc(
asm.mov(stack_opnd, kwargs);
}
- // Copy SP because REG_SP will get overwritten
- let sp = asm.lea(asm.ctx.sp_opnd(0));
-
- // Pop the C function arguments from the stack (in the caller)
- asm.stack_pop((argc + 1).try_into().unwrap());
-
// Write interpreter SP into CFP.
- // Needed in case the callee yields to the block.
- gen_save_sp(asm);
+ // We don't pop arguments yet to use registers for passing them, but we
+ // have to set cfp->sp below them for full_cfunc_return() invalidation.
+ gen_save_sp_with_offset(asm, -(argc + 1) as i8);
// Non-variadic method
let args = if cfunc_argc >= 0 {
// Copy the arguments from the stack to the C argument registers
// self is the 0th argument and is at index argc from the stack top
(0..=passed_argc).map(|i|
- Opnd::mem(64, sp, -(argc + 1 - i) * SIZEOF_VALUE_I32)
+ asm.stack_opnd(argc - i)
).collect()
}
// Variadic method
else if cfunc_argc == -1 {
// The method gets a pointer to the first argument
// rb_f_puts(int argc, VALUE *argv, VALUE recv)
+
+ let passed_argc_opnd = if let Some(splat_size) = dynamic_splat_size {
+ // The final argc is the size of the splat, minus one for the splat array itself
+ asm.add(splat_size, (passed_argc - 1).into())
+ } else {
+ // Without a splat, passed_argc is static
+ Opnd::Imm(passed_argc.into())
+ };
+
vec![
- Opnd::Imm(passed_argc.into()),
- asm.lea(Opnd::mem(64, sp, -(argc) * SIZEOF_VALUE_I32)),
- Opnd::mem(64, sp, -(argc + 1) * SIZEOF_VALUE_I32),
+ passed_argc_opnd,
+ asm.lea(asm.ctx.sp_opnd(-argc)),
+ asm.stack_opnd(argc),
]
}
- else {
+ // Variadic method taking a Ruby array
+ else if cfunc_argc == -2 {
+ // Slurp up all the arguments into an array
+ let stack_args = asm.lea(asm.ctx.sp_opnd(-argc));
+ let args_array = asm.ccall(
+ rb_ec_ary_new_from_values as _,
+ vec![EC, passed_argc.into(), stack_args]
+ );
+
+ // Example signature:
+ // VALUE neg2_method(VALUE self, VALUE argv)
+ vec![asm.stack_opnd(argc), args_array]
+ } else {
panic!("unexpected cfunc_args: {}", cfunc_argc)
};
@@ -5176,8 +6761,9 @@ fn gen_send_cfunc(
// VALUE ret = (cfunc->func)(recv, argv[0], argv[1]);
// cfunc comes from compile-time cme->def, which we assume to be stable.
// Invalidation logic is in yjit_method_lookup_change()
- asm.comment("call C function");
+ asm_comment!(asm, "call C function");
let ret = asm.ccall(unsafe { get_mct_func(cfunc) }.cast(), args);
+ asm.stack_pop((argc + 1).try_into().unwrap()); // Pop arguments after ccall to use registers for passing them.
// Record code position for TracePoint patching. See full_cfunc_return().
record_global_inval_patch(asm, CodegenGlobals::get_outline_full_cfunc_return_pos());
@@ -5186,6 +6772,20 @@ fn gen_send_cfunc(
let stack_ret = asm.stack_push(Type::Unknown);
asm.mov(stack_ret, ret);
+ // Log the name of the method we're calling to. We intentionally don't do this for inlined cfuncs.
+ // We also do this after the C call to minimize the impact of spill_temps() on asm.ccall().
+ if get_option!(gen_stats) {
+ // Assemble the method name string
+ let mid = unsafe { vm_ci_mid(ci) };
+ let name_str = get_method_name(recv_known_class, mid);
+
+ // Get an index for this cfunc name
+ let cfunc_idx = get_cfunc_idx(&name_str);
+
+ // Increment the counter for this cfunc
+ asm.ccall(incr_cfunc_counter as *const u8, vec![cfunc_idx.into()]);
+ }
+
// Pop the stack frame (ec->cfp++)
// Instead of recalculating, we can reuse the previous CFP, which is stored in a callee-saved
// register
@@ -5193,7 +6793,7 @@ fn gen_send_cfunc(
asm.store(ec_cfp_opnd, CFP);
// cfunc calls may corrupt types
- asm.ctx.clear_local_types();
+ asm.clear_local_types();
// Note: the return block of gen_send_iseq() has ctx->sp_offset == 1
// which allows for sharing the same successor.
@@ -5207,11 +6807,11 @@ fn gen_send_cfunc(
// Generate RARRAY_LEN. For array_opnd, use Opnd::Reg to reduce memory access,
// and use Opnd::Mem to save registers.
fn get_array_len(asm: &mut Assembler, array_opnd: Opnd) -> Opnd {
- asm.comment("get array length for embedded or heap");
+ asm_comment!(asm, "get array length for embedded or heap");
// Pull out the embed flag to check if it's an embedded array.
let array_reg = match array_opnd {
- Opnd::Reg(_) => array_opnd,
+ Opnd::InsnOut { .. } => array_opnd,
_ => asm.load(array_opnd),
};
let flags_opnd = Opnd::mem(VALUE_BITS, array_reg, RUBY_OFFSET_RBASIC_FLAGS);
@@ -5225,7 +6825,7 @@ fn get_array_len(asm: &mut Assembler, array_opnd: Opnd) -> Opnd {
asm.test(flags_opnd, (RARRAY_EMBED_FLAG as u64).into());
let array_reg = match array_opnd {
- Opnd::Reg(_) => array_opnd,
+ Opnd::InsnOut { .. } => array_opnd,
_ => asm.load(array_opnd),
};
let array_len_opnd = Opnd::mem(
@@ -5238,9 +6838,9 @@ fn get_array_len(asm: &mut Assembler, array_opnd: Opnd) -> Opnd {
asm.csel_nz(emb_len_opnd, array_len_opnd)
}
-// Generate RARRAY_CONST_PTR_TRANSIENT (part of RARRAY_AREF)
+// Generate RARRAY_CONST_PTR (part of RARRAY_AREF)
fn get_array_ptr(asm: &mut Assembler, array_reg: Opnd) -> Opnd {
- asm.comment("get array pointer for embedded or heap");
+ asm_comment!(asm, "get array pointer for embedded or heap");
let flags_opnd = Opnd::mem(VALUE_BITS, array_reg, RUBY_OFFSET_RBASIC_FLAGS);
asm.test(flags_opnd, (RARRAY_EMBED_FLAG as u64).into());
@@ -5249,43 +6849,45 @@ fn get_array_ptr(asm: &mut Assembler, array_reg: Opnd) -> Opnd {
array_reg,
RUBY_OFFSET_RARRAY_AS_HEAP_PTR,
);
+
// Load the address of the embedded array
// (struct RArray *)(obj)->as.ary
let ary_opnd = asm.lea(Opnd::mem(VALUE_BITS, array_reg, RUBY_OFFSET_RARRAY_AS_ARY));
asm.csel_nz(ary_opnd, heap_ptr_opnd)
}
-/// Pushes arguments from an array to the stack. Differs from push splat because
-/// the array can have items left over.
-fn move_rest_args_to_stack(array: Opnd, num_args: u32, asm: &mut Assembler) {
- asm.comment("move_rest_args_to_stack");
+// Generate RSTRING_PTR
+fn get_string_ptr(asm: &mut Assembler, string_reg: Opnd) -> Opnd {
+ asm_comment!(asm, "get string pointer for embedded or heap");
- let array_len_opnd = get_array_len(asm, array);
+ let flags_opnd = Opnd::mem(VALUE_BITS, string_reg, RUBY_OFFSET_RBASIC_FLAGS);
+ asm.test(flags_opnd, (RSTRING_NOEMBED as u64).into());
+ let heap_ptr_opnd = asm.load(Opnd::mem(
+ usize::BITS as u8,
+ string_reg,
+ RUBY_OFFSET_RSTRING_AS_HEAP_PTR,
+ ));
- asm.comment("Side exit if length is less than required");
- asm.cmp(array_len_opnd, num_args.into());
- asm.jl(Target::side_exit(Counter::send_iseq_has_rest_and_splat_not_equal));
+ // Load the address of the embedded array
+ // (struct RString *)(obj)->as.ary
+ let ary_opnd = asm.lea(Opnd::mem(VALUE_BITS, string_reg, RUBY_OFFSET_RSTRING_AS_ARY));
+ asm.csel_nz(heap_ptr_opnd, ary_opnd)
+}
- asm.comment("Push arguments from array");
+/// Pushes arguments from an array to the stack. Differs from push splat because
+/// the array can have items left over. Array is assumed to be T_ARRAY without guards.
+fn copy_splat_args_for_rest_callee(array: Opnd, num_args: u32, asm: &mut Assembler) {
+ asm_comment!(asm, "copy_splat_args_for_rest_callee");
- // Load the address of the embedded array
- // (struct RArray *)(obj)->as.ary
- let array_reg = asm.load(array);
+ // Unused operands cause the backend to panic
+ if num_args == 0 {
+ return;
+ }
- // Conditionally load the address of the heap array
- // (struct RArray *)(obj)->as.heap.ptr
- let flags_opnd = Opnd::mem(VALUE_BITS, array_reg, RUBY_OFFSET_RBASIC_FLAGS);
- asm.test(flags_opnd, Opnd::UImm(RARRAY_EMBED_FLAG as u64));
- let heap_ptr_opnd = Opnd::mem(
- usize::BITS as u8,
- array_reg,
- RUBY_OFFSET_RARRAY_AS_HEAP_PTR,
- );
- // Load the address of the embedded array
- // (struct RArray *)(obj)->as.ary
- let ary_opnd = asm.lea(Opnd::mem(VALUE_BITS, array_reg, RUBY_OFFSET_RARRAY_AS_ARY));
- let ary_opnd = asm.csel_nz(ary_opnd, heap_ptr_opnd);
+ asm_comment!(asm, "Push arguments from array");
+ let array_reg = asm.load(array);
+ let ary_opnd = get_array_ptr(asm, array_reg);
for i in 0..num_args {
let top = asm.stack_push(Type::Unknown);
asm.mov(top, Opnd::mem(64, ary_opnd, i as i32 * SIZEOF_VALUE_I32));
@@ -5296,47 +6898,23 @@ fn move_rest_args_to_stack(array: Opnd, num_args: u32, asm: &mut Assembler) {
/// It optimistically compiles to a static size that is the exact number of arguments
/// needed for the function.
fn push_splat_args(required_args: u32, asm: &mut Assembler) {
- asm.comment("push_splat_args");
+ asm_comment!(asm, "push_splat_args");
let array_opnd = asm.stack_opnd(0);
- let array_reg = asm.load(array_opnd);
-
guard_object_is_array(
asm,
- array_reg,
+ array_opnd,
array_opnd.into(),
- Counter::send_splat_not_array,
+ Counter::guard_send_splat_not_array,
);
- asm.comment("Get array length for embedded or heap");
-
- // Pull out the embed flag to check if it's an embedded array.
- let flags_opnd = Opnd::mem(VALUE_BITS, array_reg, RUBY_OFFSET_RBASIC_FLAGS);
-
- // Get the length of the array
- let emb_len_opnd = asm.and(flags_opnd, (RARRAY_EMBED_LEN_MASK as u64).into());
- let emb_len_opnd = asm.rshift(emb_len_opnd, (RARRAY_EMBED_LEN_SHIFT as u64).into());
-
- // Conditionally move the length of the heap array
- let flags_opnd = Opnd::mem(VALUE_BITS, array_reg, RUBY_OFFSET_RBASIC_FLAGS);
- asm.test(flags_opnd, (RARRAY_EMBED_FLAG as u64).into());
-
- // Need to repeat this here to deal with register allocation
- let array_opnd = asm.stack_opnd(0);
- let array_reg = asm.load(array_opnd);
+ let array_len_opnd = get_array_len(asm, array_opnd);
- let array_len_opnd = Opnd::mem(
- std::os::raw::c_long::BITS as u8,
- array_reg,
- RUBY_OFFSET_RARRAY_AS_HEAP_LEN,
- );
- let array_len_opnd = asm.csel_nz(emb_len_opnd, array_len_opnd);
-
- asm.comment("Side exit if length doesn't not equal remaining args");
+ asm_comment!(asm, "Guard for expected splat length");
asm.cmp(array_len_opnd, required_args.into());
- asm.jne(Target::side_exit(Counter::send_splatarray_length_not_equal));
+ asm.jne(Target::side_exit(Counter::guard_send_splatarray_length_not_equal));
- asm.comment("Check last argument is not ruby2keyword hash");
+ asm_comment!(asm, "Check last argument is not ruby2keyword hash");
// Need to repeat this here to deal with register allocation
let array_reg = asm.load(asm.stack_opnd(0));
@@ -5348,37 +6926,22 @@ fn push_splat_args(required_args: u32, asm: &mut Assembler) {
guard_object_is_not_ruby2_keyword_hash(
asm,
last_array_value,
- Counter::send_splatarray_last_ruby_2_keywords,
+ Counter::guard_send_splatarray_last_ruby2_keywords,
);
- asm.comment("Push arguments from array");
+ asm_comment!(asm, "Push arguments from array");
let array_opnd = asm.stack_pop(1);
if required_args > 0 {
- // Load the address of the embedded array
- // (struct RArray *)(obj)->as.ary
let array_reg = asm.load(array_opnd);
-
- // Conditionally load the address of the heap array
- // (struct RArray *)(obj)->as.heap.ptr
- let flags_opnd = Opnd::mem(VALUE_BITS, array_reg, RUBY_OFFSET_RBASIC_FLAGS);
- asm.test(flags_opnd, Opnd::UImm(RARRAY_EMBED_FLAG as u64));
- let heap_ptr_opnd = Opnd::mem(
- usize::BITS as u8,
- array_reg,
- RUBY_OFFSET_RARRAY_AS_HEAP_PTR,
- );
- // Load the address of the embedded array
- // (struct RArray *)(obj)->as.ary
- let ary_opnd = asm.lea(Opnd::mem(VALUE_BITS, array_reg, RUBY_OFFSET_RARRAY_AS_ARY));
- let ary_opnd = asm.csel_nz(ary_opnd, heap_ptr_opnd);
+ let ary_opnd = get_array_ptr(asm, array_reg);
for i in 0..required_args {
let top = asm.stack_push(Type::Unknown);
asm.mov(top, Opnd::mem(64, ary_opnd, i as i32 * SIZEOF_VALUE_I32));
}
- asm.comment("end push_each");
+ asm_comment!(asm, "end push_each");
}
}
@@ -5388,7 +6951,7 @@ fn gen_send_bmethod(
ocb: &mut OutlinedCb,
ci: *const rb_callinfo,
cme: *const rb_callable_method_entry_t,
- block: Option<IseqPtr>,
+ block: Option<BlockHandler>,
flags: u32,
argc: i32,
) -> Option<CodegenStatus> {
@@ -5419,7 +6982,56 @@ fn gen_send_bmethod(
}
let frame_type = VM_FRAME_MAGIC_BLOCK | VM_FRAME_FLAG_BMETHOD | VM_FRAME_FLAG_LAMBDA;
- gen_send_iseq(jit, asm, ocb, iseq, ci, frame_type, Some(capture.ep), cme, block, flags, argc, None)
+ perf_call! { gen_send_iseq(jit, asm, ocb, iseq, ci, frame_type, Some(capture.ep), cme, block, flags, argc, None) }
+}
+
+/// The kind of a value an ISEQ returns
+enum IseqReturn {
+ Value(VALUE),
+ LocalVariable(u32),
+ Receiver,
+}
+
+extern {
+ fn rb_simple_iseq_p(iseq: IseqPtr) -> bool;
+}
+
+/// Return the ISEQ's return value if it consists of one simple instruction and leave.
+fn iseq_get_return_value(iseq: IseqPtr, captured_opnd: Option<Opnd>, ci_flags: u32) -> Option<IseqReturn> {
+ // Expect only two instructions and one possible operand
+ let iseq_size = unsafe { get_iseq_encoded_size(iseq) };
+ if !(2..=3).contains(&iseq_size) {
+ return None;
+ }
+
+ // Get the first two instructions
+ let first_insn = iseq_opcode_at_idx(iseq, 0);
+ let second_insn = iseq_opcode_at_idx(iseq, insn_len(first_insn as usize));
+
+ // Extract the return value if known
+ if second_insn != YARVINSN_leave {
+ return None;
+ }
+ match first_insn {
+ YARVINSN_getlocal_WC_0 => {
+ // Only accept simple positional only cases for both the caller and the callee.
+ // Reject block ISEQs to avoid autosplat and other block parameter complications.
+ if captured_opnd.is_none() && unsafe { rb_simple_iseq_p(iseq) } && ci_flags & VM_CALL_ARGS_SIMPLE != 0 {
+ let ep_offset = unsafe { *rb_iseq_pc_at_idx(iseq, 1) }.as_u32();
+ let local_idx = ep_offset_to_local_idx(iseq, ep_offset);
+ Some(IseqReturn::LocalVariable(local_idx))
+ } else {
+ None
+ }
+ }
+ YARVINSN_putnil => Some(IseqReturn::Value(Qnil)),
+ YARVINSN_putobject => Some(IseqReturn::Value(unsafe { *rb_iseq_pc_at_idx(iseq, 1) })),
+ YARVINSN_putobject_INT2FIX_0_ => Some(IseqReturn::Value(VALUE::fixnum_from_usize(0))),
+ YARVINSN_putobject_INT2FIX_1_ => Some(IseqReturn::Value(VALUE::fixnum_from_usize(1))),
+ // We don't support invokeblock for now. Such ISEQs are likely not used by blocks anyway.
+ YARVINSN_putself if captured_opnd.is_none() => Some(IseqReturn::Receiver),
+ _ => None,
+ }
}
fn gen_send_iseq(
@@ -5431,24 +7043,37 @@ fn gen_send_iseq(
frame_type: u32,
prev_ep: Option<*const VALUE>,
cme: *const rb_callable_method_entry_t,
- block: Option<IseqPtr>,
+ block: Option<BlockHandler>,
flags: u32,
argc: i32,
captured_opnd: Option<Opnd>,
) -> Option<CodegenStatus> {
+ // Argument count. We will change this as we gather values from
+ // sources to satisfy the callee's parameters. To help make sense
+ // of changes, note that:
+ // - Parameters syntactically on the left have lower addresses.
+ // For example, all the lead (required) and optional parameters
+ // have lower addresses than the rest parameter array.
+ // - The larger the index one passes to Assembler::stack_opnd(),
+ // the *lower* the address.
let mut argc = argc;
- // When you have keyword arguments, there is an extra object that gets
- // placed on the stack the represents a bitmap of the keywords that were not
- // specified at the call site. We need to keep track of the fact that this
- // value is present on the stack in order to properly set up the callee's
- // stack pointer.
- let doing_kw_call = unsafe { get_iseq_flags_has_kw(iseq) };
+ // Iseqs with keyword parameters have a hidden, unnamed parameter local
+ // that the callee could use to know which keywords are unspecified
+ // (see the `checkkeyword` instruction and check `ruby --dump=insn -e 'def foo(k:itself)=k'`).
+ // We always need to set up this local if the call goes through.
+ let has_kwrest = unsafe { get_iseq_flags_has_kwrest(iseq) };
+ let doing_kw_call = unsafe { get_iseq_flags_has_kw(iseq) } || has_kwrest;
let supplying_kws = unsafe { vm_ci_flag(ci) & VM_CALL_KWARG } != 0;
let iseq_has_rest = unsafe { get_iseq_flags_has_rest(iseq) };
+ let iseq_has_block_param = unsafe { get_iseq_flags_has_block(iseq) };
+ let arg_setup_block = captured_opnd.is_some(); // arg_setup_type: arg_setup_block (invokeblock)
+ let kw_splat = flags & VM_CALL_KW_SPLAT != 0;
+ let splat_call = flags & VM_CALL_ARGS_SPLAT != 0;
- // For computing number of locals to set up for the callee
- let mut num_params = unsafe { get_iseq_body_param_size(iseq) };
+ // For computing offsets to callee locals
+ let num_params = unsafe { get_iseq_body_param_size(iseq) as i32 };
+ let num_locals = unsafe { get_iseq_body_local_table_size(iseq) as i32 };
let mut start_pc_offset: u16 = 0;
let required_num = unsafe { get_iseq_body_param_lead_num(iseq) };
@@ -5462,46 +7087,47 @@ fn gen_send_iseq(
unsafe { get_cikw_keyword_len(kw_arg) }
};
- // Arity handling and optional parameter setup
- let mut opts_filled = argc - required_num - kw_arg_num;
+ // Arity handling and optional parameter setup for positional arguments.
+ // Splats are handled later.
+ let mut opts_filled = argc - required_num - kw_arg_num - i32::from(kw_splat) - i32::from(splat_call);
let opt_num = unsafe { get_iseq_body_param_opt_num(iseq) };
- // We have a rest argument so there could be more args
- // than are required + optional. Those will go in rest.
+ // With a rest parameter or a yield to a block,
+ // callers can pass more than required + optional.
// So we cap ops_filled at opt_num.
- if iseq_has_rest {
+ if iseq_has_rest || arg_setup_block {
opts_filled = min(opts_filled, opt_num);
}
let mut opts_missing: i32 = opt_num - opts_filled;
-
let block_arg = flags & VM_CALL_ARGS_BLOCKARG != 0;
- let block_arg_type = if block_arg {
- Some(asm.ctx.get_opnd_type(StackOpnd(0)))
- } else {
- None
- };
+ // Stack index of the splat array
+ let splat_pos = i32::from(block_arg) + i32::from(kw_splat) + kw_arg_num;
exit_if_stack_too_large(iseq)?;
exit_if_tail_call(asm, ci)?;
exit_if_has_post(asm, iseq)?;
- exit_if_has_kwrest(asm, iseq)?;
- exit_if_splat_and_ruby2_keywords(asm, jit, flags)?;
+ exit_if_kwsplat_non_nil(asm, flags, Counter::send_iseq_kw_splat_non_nil)?;
exit_if_has_rest_and_captured(asm, iseq_has_rest, captured_opnd)?;
- exit_if_has_rest_and_send(asm, iseq_has_rest, flags)?;
- exit_if_has_rest_and_supplying_kws(asm, iseq_has_rest, iseq, supplying_kws)?;
- exit_if_supplying_kw_and_has_no_kw(asm, supplying_kws, iseq)?;
+ exit_if_has_kwrest_and_captured(asm, has_kwrest, captured_opnd)?;
+ exit_if_has_rest_and_supplying_kws(asm, iseq_has_rest, supplying_kws)?;
+ exit_if_supplying_kw_and_has_no_kw(asm, supplying_kws, doing_kw_call)?;
exit_if_supplying_kws_and_accept_no_kwargs(asm, supplying_kws, iseq)?;
- exit_if_splat_and_zsuper(asm, flags)?;
exit_if_doing_kw_and_splat(asm, doing_kw_call, flags)?;
- exit_if_wrong_number_arguments(asm, opts_filled, flags, opt_num, iseq_has_rest)?;
+ exit_if_wrong_number_arguments(asm, arg_setup_block, opts_filled, flags, opt_num, iseq_has_rest)?;
exit_if_doing_kw_and_opts_missing(asm, doing_kw_call, opts_missing)?;
exit_if_has_rest_and_optional_and_block(asm, iseq_has_rest, opt_num, iseq, block_arg)?;
- exit_if_unsupported_block_arg_type(asm, block_arg_type)?;
+ let block_arg_type = exit_if_unsupported_block_arg_type(jit, asm, block_arg)?;
+
+ // Bail if we can't drop extra arguments for a yield by just popping them
+ if supplying_kws && arg_setup_block && argc > (kw_arg_num + required_num + opt_num) {
+ gen_counter_incr(asm, Counter::send_iseq_complex_discard_extras);
+ return None;
+ }
// Block parameter handling. This mirrors setup_parameters_complex().
- if unsafe { get_iseq_flags_has_block(iseq) } {
+ if iseq_has_block_param {
if unsafe { get_iseq_body_local_iseq(iseq) == iseq } {
- num_params -= 1;
+ // Do nothing
} else {
// In this case (param.flags.has_block && local_iseq != iseq),
// the block argument is setup as a local variable and requires
@@ -5511,132 +7137,45 @@ fn gen_send_iseq(
}
}
- // We will handle splat case later
- if opt_num > 0 && flags & VM_CALL_ARGS_SPLAT == 0 {
- num_params -= opts_missing as u32;
- unsafe {
- let opt_table = get_iseq_body_param_opt_table(iseq);
- start_pc_offset = (*opt_table.offset(opts_filled as isize)).try_into().unwrap();
- }
- }
-
+ // Check that required keyword arguments are supplied and find any extras
+ // that should go into the keyword rest parameter (**kw_rest).
if doing_kw_call {
- // Here we're calling a method with keyword arguments and specifying
- // keyword arguments at this call site.
-
- // This struct represents the metadata about the callee-specified
- // keyword parameters.
- let keyword = unsafe { get_iseq_body_param_keyword(iseq) };
- let keyword_num: usize = unsafe { (*keyword).num }.try_into().unwrap();
- let keyword_required_num: usize = unsafe { (*keyword).required_num }.try_into().unwrap();
-
- let mut required_kwargs_filled = 0;
-
- if keyword_num > 30 {
- // We have so many keywords that (1 << num) encoded as a FIXNUM
- // (which shifts it left one more) no longer fits inside a 32-bit
- // immediate.
- gen_counter_incr(asm, Counter::send_iseq_too_many_kwargs);
- return None;
- }
-
- // Check that the kwargs being passed are valid
- if supplying_kws {
- // This is the list of keyword arguments that the callee specified
- // in its initial declaration.
- // SAFETY: see compile.c for sizing of this slice.
- let callee_kwargs = unsafe { slice::from_raw_parts((*keyword).table, keyword_num) };
-
- // Here we're going to build up a list of the IDs that correspond to
- // the caller-specified keyword arguments. If they're not in the
- // same order as the order specified in the callee declaration, then
- // we're going to need to generate some code to swap values around
- // on the stack.
- let kw_arg_keyword_len: usize =
- unsafe { get_cikw_keyword_len(kw_arg) }.try_into().unwrap();
- let mut caller_kwargs: Vec<ID> = vec![0; kw_arg_keyword_len];
- for kwarg_idx in 0..kw_arg_keyword_len {
- let sym = unsafe { get_cikw_keywords_idx(kw_arg, kwarg_idx.try_into().unwrap()) };
- caller_kwargs[kwarg_idx] = unsafe { rb_sym2id(sym) };
- }
-
- // First, we're going to be sure that the names of every
- // caller-specified keyword argument correspond to a name in the
- // list of callee-specified keyword parameters.
- for caller_kwarg in caller_kwargs {
- let search_result = callee_kwargs
- .iter()
- .enumerate() // inject element index
- .find(|(_, &kwarg)| kwarg == caller_kwarg);
-
- match search_result {
- None => {
- // If the keyword was never found, then we know we have a
- // mismatch in the names of the keyword arguments, so we need to
- // bail.
- gen_counter_incr(asm, Counter::send_iseq_kwargs_mismatch);
- return None;
- }
- Some((callee_idx, _)) if callee_idx < keyword_required_num => {
- // Keep a count to ensure all required kwargs are specified
- required_kwargs_filled += 1;
- }
- _ => (),
- }
- }
- }
- assert!(required_kwargs_filled <= keyword_required_num);
- if required_kwargs_filled != keyword_required_num {
- gen_counter_incr(asm, Counter::send_iseq_kwargs_mismatch);
- return None;
- }
- }
-
- // Check if we need the arg0 splat handling of vm_callee_setup_block_arg
- let arg_setup_block = captured_opnd.is_some(); // arg_setup_type: arg_setup_block (invokeblock)
- let block_arg0_splat = arg_setup_block && argc == 1 && unsafe {
- get_iseq_flags_has_lead(iseq) && !get_iseq_flags_ambiguous_param0(iseq)
- };
- if block_arg0_splat {
- // If block_arg0_splat, we still need side exits after splat, but
- // doing push_splat_args here disallows it. So bail out.
- if flags & VM_CALL_ARGS_SPLAT != 0 && !iseq_has_rest {
- gen_counter_incr(asm, Counter::invokeblock_iseq_arg0_args_splat);
- return None;
- }
- // The block_arg0_splat implementation is for the rb_simple_iseq_p case,
- // but doing_kw_call means it's not a simple ISEQ.
- if doing_kw_call {
- gen_counter_incr(asm, Counter::invokeblock_iseq_arg0_has_kw);
- return None;
- }
+ gen_iseq_kw_call_checks(asm, iseq, kw_arg, has_kwrest, kw_arg_num)?;
}
- let splat_array_length = if flags & VM_CALL_ARGS_SPLAT != 0 {
- let array = jit.peek_at_stack(&asm.ctx, if block_arg { 1 } else { 0 }) ;
+ let splat_array_length = if splat_call {
+ let array = jit.peek_at_stack(&asm.ctx, splat_pos as isize);
let array_length = if array == Qnil {
0
+ } else if unsafe { !RB_TYPE_P(array, RUBY_T_ARRAY) } {
+ gen_counter_incr(asm, Counter::send_iseq_splat_not_array);
+ return None;
} else {
unsafe { rb_yjit_array_len(array) as u32}
};
- if opt_num == 0 && required_num != array_length as i32 + argc - 1 && !iseq_has_rest {
- gen_counter_incr(asm, Counter::send_iseq_splat_arity_error);
- return None;
+ // Arity check accounting for size of the splat. When callee has rest parameters, we insert
+ // runtime guards later in copy_splat_args_for_rest_callee()
+ if !iseq_has_rest {
+ let supplying = argc - 1 - i32::from(kw_splat) + array_length as i32;
+ if (required_num..=required_num + opt_num).contains(&supplying) == false {
+ gen_counter_incr(asm, Counter::send_iseq_splat_arity_error);
+ return None;
+ }
}
if iseq_has_rest && opt_num > 0 {
- // If we have a rest and option arugments
+ // If we have a rest and option arguments
// we are going to set the pc_offset for where
// to jump in the called method.
// If the number of args change, that would need to
// change and we don't change that dynmically so we side exit.
// On a normal splat without rest and option args this is handled
// elsewhere depending on the case
- asm.comment("Side exit if length doesn't not equal compile time length");
- let array_len_opnd = get_array_len(asm, asm.stack_opnd(if block_arg { 1 } else { 0 }));
+ asm_comment!(asm, "Side exit if length doesn't not equal compile time length");
+ let array_len_opnd = get_array_len(asm, asm.stack_opnd(splat_pos));
asm.cmp(array_len_opnd, array_length.into());
- asm.jne(Target::side_exit(Counter::send_splatarray_length_not_equal));
+ asm.jne(Target::side_exit(Counter::guard_send_splatarray_length_not_equal));
}
Some(array_length)
@@ -5644,78 +7183,103 @@ fn gen_send_iseq(
None
};
- // If we have a rest, optional arguments, and a splat
- // some of those splatted args will end up filling the
- // optional arguments and some will potentially end up
- // in the rest. This calculates how many are filled
- // by the splat.
- let opts_filled_with_splat: Option<i32> = {
- if iseq_has_rest && opt_num > 0 {
- splat_array_length.map(|len| {
- let num_args = (argc - 1) + len as i32;
- if num_args >= required_num {
- min(num_args - required_num, opt_num)
- } else {
- 0
- }
- })
- } else {
- None
+ // Check if we need the arg0 splat handling of vm_callee_setup_block_arg()
+ // Also known as "autosplat" inside setup_parameters_complex().
+ // Autosplat checks argc == 1 after splat and kwsplat processing, so make
+ // sure to amend this if we start support kw_splat.
+ let block_arg0_splat = arg_setup_block
+ && (argc == 1 || (argc == 2 && splat_array_length == Some(0)))
+ && !supplying_kws && !doing_kw_call
+ && unsafe {
+ (get_iseq_flags_has_lead(iseq) || opt_num > 1)
+ && !get_iseq_flags_ambiguous_param0(iseq)
+ };
+ if block_arg0_splat {
+ // If block_arg0_splat, we still need side exits after splat, but
+ // the splat modifies the stack which breaks side exits. So bail out.
+ if splat_call {
+ gen_counter_incr(asm, Counter::invokeblock_iseq_arg0_args_splat);
+ return None;
}
- };
+ // The block_arg0_splat implementation cannot deal with optional parameters.
+ // This is a setup_parameters_complex() situation and interacts with the
+ // starting position of the callee.
+ if opt_num > 1 {
+ gen_counter_incr(asm, Counter::invokeblock_iseq_arg0_optional);
+ return None;
+ }
+ }
- // If we have optional arguments filled by the splat (see comment above)
- // we need to set a few variables concerning optional arguments
- // to their correct values, as well as set the pc_offset.
- if let Some(filled) = opts_filled_with_splat {
- opts_missing = opt_num - filled;
- opts_filled = filled;
- num_params -= opts_missing as u32;
+ // Adjust `opts_filled` and `opts_missing` taking
+ // into account the size of the splat expansion.
+ if let Some(len) = splat_array_length {
+ assert_eq!(kw_arg_num, 0); // Due to exit_if_doing_kw_and_splat().
+ // Simplifies calculation below.
+ let num_args = argc - 1 - i32::from(kw_splat) + len as i32;
- // We are going to jump to the correct offset based on how many optional
- // params are remaining.
- unsafe {
- let opt_table = get_iseq_body_param_opt_table(iseq);
- start_pc_offset = (*opt_table.offset(filled as isize)).try_into().unwrap();
+ opts_filled = if num_args >= required_num {
+ min(num_args - required_num, opt_num)
+ } else {
+ 0
};
+ opts_missing = opt_num - opts_filled;
}
- // We will not have None from here. You can use stack_pop / stack_pop.
+ assert_eq!(opts_missing + opts_filled, opt_num);
+ assert!(opts_filled >= 0);
- match block_arg_type {
- Some(Type::Nil) => {
- // We have a nil block arg, so let's pop it off the args
- asm.stack_pop(1);
- }
- Some(Type::BlockParamProxy) => {
- // We don't need the actual stack value
- asm.stack_pop(1);
- }
- None => {
- // Nothing to do
- }
- _ => {
- assert!(false);
+ // ISeq with optional parameters start at different
+ // locations depending on the number of optionals given.
+ if opt_num > 0 {
+ assert!(opts_filled >= 0);
+ unsafe {
+ let opt_table = get_iseq_body_param_opt_table(iseq);
+ start_pc_offset = opt_table.offset(opts_filled as isize).read().try_into().unwrap();
}
}
+ // Increment total ISEQ send count
+ gen_counter_incr(asm, Counter::num_send_iseq);
+
+ // Shortcut for special `Primitive.attr! :leaf` builtins
let builtin_attrs = unsafe { rb_yjit_iseq_builtin_attrs(iseq) };
let builtin_func_raw = unsafe { rb_yjit_builtin_function(iseq) };
let builtin_func = if builtin_func_raw.is_null() { None } else { Some(builtin_func_raw) };
let opt_send_call = flags & VM_CALL_OPT_SEND != 0; // .send call is not currently supported for builtins
- if let (None, Some(builtin_info), true, false) = (block, builtin_func, builtin_attrs & BUILTIN_ATTR_LEAF != 0, opt_send_call) {
+ if let (None, Some(builtin_info), true, false, None | Some(0)) =
+ (block, builtin_func, builtin_attrs & BUILTIN_ATTR_LEAF != 0, opt_send_call, splat_array_length) {
let builtin_argc = unsafe { (*builtin_info).argc };
if builtin_argc + 1 < (C_ARG_OPNDS.len() as i32) {
- asm.comment("inlined leaf builtin");
-
- // Skip this if it doesn't trigger GC
- if builtin_attrs & BUILTIN_ATTR_NO_GC == 0 {
- // The callee may allocate, e.g. Integer#abs on a Bignum.
- // Save SP for GC, save PC for allocation tracing, and prepare
- // for global invalidation after GC's VM lock contention.
- jit_prepare_routine_call(jit, asm);
+ // We pop the block arg without using it because:
+ // - the builtin is leaf, so it promises to not `yield`.
+ // - no leaf builtins have block param at the time of writing, and
+ // adding one requires interpreter changes to support.
+ if block_arg_type.is_some() {
+ if iseq_has_block_param {
+ gen_counter_incr(asm, Counter::send_iseq_leaf_builtin_block_arg_block_param);
+ return None;
+ }
+ asm.stack_pop(1);
}
+ // Pop empty kw_splat hash which passes nothing (exit_if_kwsplat_non_nil())
+ if kw_splat {
+ asm.stack_pop(1);
+ }
+
+ // Pop empty splat array which passes nothing
+ if let Some(0) = splat_array_length {
+ asm.stack_pop(1);
+ }
+
+ asm_comment!(asm, "inlined leaf builtin");
+ gen_counter_incr(asm, Counter::num_send_iseq_leaf);
+
+ // The callee may allocate, e.g. Integer#abs on a Bignum.
+ // Save SP for GC, save PC for allocation tracing, and prepare
+ // for global invalidation after GC's VM lock contention.
+ jit_prepare_call_with_gc(jit, asm);
+
// Call the builtin func (ec, recv, arg1, arg2, ...)
let mut args = vec![EC];
@@ -5724,8 +7288,8 @@ fn gen_send_iseq(
let stack_opnd = asm.stack_opnd(builtin_argc - i);
args.push(stack_opnd);
}
- asm.stack_pop((builtin_argc + 1).try_into().unwrap());
let val = asm.ccall(unsafe { (*builtin_info).func_ptr as *const u8 }, args);
+ asm.stack_pop((builtin_argc + 1).try_into().unwrap()); // Keep them on stack during ccall for GC
// Push the return value
let stack_ret = asm.stack_push(Type::Unknown);
@@ -5734,58 +7298,158 @@ fn gen_send_iseq(
// Note: assuming that the leaf builtin doesn't change local variables here.
// Seems like a safe assumption.
- return Some(KeepCompiling);
+ // Let guard chains share the same successor
+ jump_to_next_insn(jit, asm, ocb);
+ return Some(EndBlock);
}
}
- // Number of locals that are not parameters
- let num_locals = unsafe { get_iseq_body_local_table_size(iseq) as i32 } - (num_params as i32) + if iseq_has_rest && opt_num != 0 { 1 } else { 0 };
+ // Inline simple ISEQs whose return value is known at compile time
+ if let (Some(value), None, false) = (iseq_get_return_value(iseq, captured_opnd, flags), block_arg_type, opt_send_call) {
+ asm_comment!(asm, "inlined simple ISEQ");
+ gen_counter_incr(asm, Counter::num_send_iseq_inline);
+
+ match value {
+ IseqReturn::LocalVariable(local_idx) => {
+ // Put the local variable at the return slot
+ let stack_local = asm.stack_opnd(argc - 1 - local_idx as i32);
+ let stack_return = asm.stack_opnd(argc);
+ asm.mov(stack_return, stack_local);
+
+ // Update the mapping for the return value
+ let mapping = asm.ctx.get_opnd_mapping(stack_local.into());
+ asm.ctx.set_opnd_mapping(stack_return.into(), mapping);
+
+ // Pop everything but the return value
+ asm.stack_pop(argc as usize);
+ }
+ IseqReturn::Value(value) => {
+ // Pop receiver and arguments
+ asm.stack_pop(argc as usize + if captured_opnd.is_some() { 0 } else { 1 });
+
+ // Push the return value
+ let stack_ret = asm.stack_push(Type::from(value));
+ asm.mov(stack_ret, value.into());
+ },
+ IseqReturn::Receiver => {
+ // Just pop arguments and leave the receiver on stack
+ asm.stack_pop(argc as usize);
+ }
+ }
+
+ // Let guard chains share the same successor
+ jump_to_next_insn(jit, asm, ocb);
+ return Some(EndBlock);
+ }
// Stack overflow check
// Note that vm_push_frame checks it against a decremented cfp, hence the multiply by 2.
// #define CHECK_VM_STACK_OVERFLOW0(cfp, sp, margin)
- asm.comment("stack overflow check");
+ asm_comment!(asm, "stack overflow check");
+ const _: () = assert!(RUBY_SIZEOF_CONTROL_FRAME % SIZEOF_VALUE == 0, "sizeof(rb_control_frame_t) is a multiple of sizeof(VALUE)");
let stack_max: i32 = unsafe { get_iseq_body_stack_max(iseq) }.try_into().unwrap();
- let locals_offs =
- SIZEOF_VALUE_I32 * (num_locals + stack_max) + 2 * (RUBY_SIZEOF_CONTROL_FRAME as i32);
- let stack_limit = asm.lea(asm.ctx.sp_opnd(locals_offs as isize));
+ let locals_offs = (num_locals + stack_max) + 2 * (RUBY_SIZEOF_CONTROL_FRAME / SIZEOF_VALUE) as i32;
+ let stack_limit = asm.lea(asm.ctx.sp_opnd(locals_offs));
asm.cmp(CFP, stack_limit);
- asm.jbe(Target::side_exit(Counter::send_se_cf_overflow));
+ asm.jbe(Target::side_exit(Counter::guard_send_se_cf_overflow));
+
+ if iseq_has_rest && splat_call {
+ // Insert length guard for a call to copy_splat_args_for_rest_callee()
+ // that will come later. We will have made changes to
+ // the stack by spilling or handling __send__ shifting
+ // by the time we get to that code, so we need the
+ // guard here where we can still side exit.
+ let non_rest_arg_count = argc - i32::from(kw_splat) - 1;
+ if non_rest_arg_count < required_num + opt_num {
+ let take_count: u32 = (required_num - non_rest_arg_count + opts_filled)
+ .try_into().unwrap();
+
+ if take_count > 0 {
+ asm_comment!(asm, "guard splat_array_length >= {take_count}");
+
+ let splat_array = asm.stack_opnd(splat_pos);
+ let array_len_opnd = get_array_len(asm, splat_array);
+ asm.cmp(array_len_opnd, take_count.into());
+ asm.jl(Target::side_exit(Counter::guard_send_iseq_has_rest_and_splat_too_few));
+ }
+ }
- // push_splat_args does stack manipulation so we can no longer side exit
- if let Some(array_length) = splat_array_length {
- let remaining_opt = (opt_num as u32 + required_num as u32).saturating_sub(array_length + (argc as u32 - 1));
+ // All splats need to guard for ruby2_keywords hash. Check with a function call when
+ // splatting into a rest param since the index for the last item in the array is dynamic.
+ asm_comment!(asm, "guard no ruby2_keywords hash in splat");
+ let bad_splat = asm.ccall(rb_yjit_ruby2_keywords_splat_p as _, vec![asm.stack_opnd(splat_pos)]);
+ asm.cmp(bad_splat, 0.into());
+ asm.jnz(Target::side_exit(Counter::guard_send_splatarray_last_ruby2_keywords));
+ }
- if !iseq_has_rest {
- if opt_num > 0 {
- // We are going to jump to the correct offset based on how many optional
- // params are remaining.
- unsafe {
- let opt_table = get_iseq_body_param_opt_table(iseq);
- let offset = (opt_num - remaining_opt as i32) as isize;
- start_pc_offset = (*opt_table.offset(offset)).try_into().unwrap();
- };
+ match block_arg_type {
+ Some(BlockArg::Nil) => {
+ // We have a nil block arg, so let's pop it off the args
+ asm.stack_pop(1);
+ }
+ Some(BlockArg::BlockParamProxy) => {
+ // We don't need the actual stack value
+ asm.stack_pop(1);
+ }
+ Some(BlockArg::TProc) => {
+ // Place the proc as the block handler. We do this early because
+ // the block arg being at the top of the stack gets in the way of
+ // rest param handling later. Also, since there are C calls that
+ // come later, we can't hold this value in a register and place it
+ // near the end when we push a new control frame.
+ asm_comment!(asm, "guard block arg is a proc");
+ // Simple predicate, no need for jit_prepare_non_leaf_call().
+ let is_proc = asm.ccall(rb_obj_is_proc as _, vec![asm.stack_opnd(0)]);
+ asm.cmp(is_proc, Qfalse.into());
+ jit_chain_guard(
+ JCC_JE,
+ jit,
+ asm,
+ ocb,
+ SEND_MAX_DEPTH,
+ Counter::guard_send_block_arg_type,
+ );
+
+ let callee_ep = -argc + num_locals + VM_ENV_DATA_SIZE as i32 - 1;
+ let callee_specval = callee_ep + VM_ENV_DATA_INDEX_SPECVAL;
+ if callee_specval < 0 {
+ // Can't write to sp[-n] since that's where the arguments are
+ gen_counter_incr(asm, Counter::send_iseq_clobbering_block_arg);
+ return None;
}
+ let proc = asm.stack_pop(1); // Pop first, as argc doesn't account for the block arg
+ let callee_specval = asm.ctx.sp_opnd(callee_specval);
+ asm.store(callee_specval, proc);
+ }
+ None => {
+ // Nothing to do
+ }
+ }
+
+ if kw_splat {
+ // Only `**nil` is supported right now. Checked in exit_if_kwsplat_non_nil()
+ assert_eq!(Type::Nil, asm.ctx.get_opnd_type(StackOpnd(0)));
+ asm.stack_pop(1);
+ argc -= 1;
+ }
- // We are going to assume that the splat fills
- // all the remaining arguments. In the generated code
- // we test if this is true and if not side exit.
- argc = argc - 1 + array_length as i32 + remaining_opt as i32;
+ // push_splat_args does stack manipulation so we can no longer side exit
+ if let Some(array_length) = splat_array_length {
+ if !iseq_has_rest {
+ // Speculate that future splats will be done with
+ // an array that has the same length. We will insert guards.
+ argc = argc - 1 + array_length as i32;
if argc + asm.ctx.get_stack_size() as i32 > MAX_SPLAT_LENGTH {
gen_counter_incr(asm, Counter::send_splat_too_long);
return None;
}
push_splat_args(array_length, asm);
-
- for _ in 0..remaining_opt {
- // We need to push nil for the optional arguments
- let stack_ret = asm.stack_push(Type::Unknown);
- asm.mov(stack_ret, Qnil.into());
- }
}
}
// This is a .send call and we need to adjust the stack
+ // TODO: This can be more efficient if we do it before
+ // extracting from the splat array above.
if flags & VM_CALL_OPT_SEND != 0 {
handle_opt_send_shift_stack(asm, argc);
}
@@ -5795,73 +7459,73 @@ fn gen_send_iseq(
jit_save_pc(jit, asm);
gen_save_sp(asm);
- if flags & VM_CALL_ARGS_SPLAT != 0 {
+ let rest_param_array = if splat_call {
let non_rest_arg_count = argc - 1;
// We start by dupping the array because someone else might have
- // a reference to it.
- let array = asm.stack_pop(1);
+ // a reference to it. This also normalizes to an ::Array instance.
+ let array = asm.stack_opnd(0);
let array = asm.ccall(
rb_ary_dup as *const u8,
vec![array],
);
- if non_rest_arg_count > required_num + opt_num {
+ asm.stack_pop(1); // Pop array after ccall to use a register for passing it.
+ // This is the end stack state of all `non_rest_arg_count` situations below
+ argc = required_num + opts_filled;
+
+ if non_rest_arg_count > required_num + opt_num {
// If we have more arguments than required, we need to prepend
// the items from the stack onto the array.
- let diff = (non_rest_arg_count - (required_num + opts_filled_with_splat.unwrap_or(0))) as u32;
+ let diff: u32 = (non_rest_arg_count - (required_num + opt_num))
+ .try_into().unwrap();
// diff is >0 so no need to worry about null pointer
- asm.comment("load pointer to array elements");
- let offset_magnitude = SIZEOF_VALUE as u32 * diff;
- let values_opnd = asm.ctx.sp_opnd(-(offset_magnitude as isize));
+ asm_comment!(asm, "load pointer to array elements");
+ let values_opnd = asm.ctx.sp_opnd(-(diff as i32));
let values_ptr = asm.lea(values_opnd);
- asm.comment("prepend stack values to rest array");
+ asm_comment!(asm, "prepend stack values to rest array");
let array = asm.ccall(
rb_ary_unshift_m as *const u8,
vec![Opnd::UImm(diff as u64), values_ptr, array],
);
asm.stack_pop(diff as usize);
- let stack_ret = asm.stack_push(Type::TArray);
- asm.mov(stack_ret, array);
- // We now should have the required arguments
- // and an array of all the rest arguments
- argc = required_num + opts_filled_with_splat.unwrap_or(0) + 1;
+ array
} else if non_rest_arg_count < required_num + opt_num {
// If we have fewer arguments than required, we need to take some
// from the array and move them to the stack.
+ asm_comment!(asm, "take items from splat array");
- let diff = (required_num - non_rest_arg_count + opts_filled_with_splat.unwrap_or(0)) as u32;
- // This moves the arguments onto the stack. But it doesn't modify the array.
- move_rest_args_to_stack(array, diff, asm);
+ let take_count: u32 = (required_num - non_rest_arg_count + opts_filled)
+ .try_into().unwrap();
+
+ // Copy required arguments to the stack without modifying the array
+ copy_splat_args_for_rest_callee(array, take_count, asm);
// We will now slice the array to give us a new array of the correct size
- asm.spill_temps(); // for ccall
- let ret = asm.ccall(rb_yjit_rb_ary_subseq_length as *const u8, vec![array, Opnd::UImm(diff as u64)]);
- let stack_ret = asm.stack_push(Type::TArray);
- asm.mov(stack_ret, ret);
-
- // We now should have the required arguments
- // and an array of all the rest arguments
- argc = required_num + opts_filled_with_splat.unwrap_or(0) + 1;
+ let sliced = asm.ccall(rb_yjit_rb_ary_subseq_length as *const u8, vec![array, Opnd::UImm(take_count.into())]);
+
+ sliced
} else {
// The arguments are equal so we can just push to the stack
+ asm_comment!(asm, "same length for splat array and rest param");
assert!(non_rest_arg_count == required_num + opt_num);
- let stack_ret = asm.stack_push(Type::TArray);
- asm.mov(stack_ret, array);
+
+ array
}
} else {
+ asm_comment!(asm, "rest parameter without splat");
+
assert!(argc >= required_num);
let n = (argc - required_num - opts_filled) as u32;
- argc = required_num + opts_filled + 1;
+ argc = required_num + opts_filled;
// If n is 0, then elts is never going to be read, so we can just pass null
let values_ptr = if n == 0 {
Opnd::UImm(0)
} else {
- asm.comment("load pointer to array elements");
- let offset_magnitude = SIZEOF_VALUE as u32 * n;
- let values_opnd = asm.ctx.sp_opnd(-(offset_magnitude as isize));
+ asm_comment!(asm, "load pointer to array elements");
+ let values_opnd = asm.ctx.sp_opnd(-(n as i32));
asm.lea(values_opnd)
};
@@ -5874,147 +7538,49 @@ fn gen_send_iseq(
]
);
asm.stack_pop(n.as_usize());
- let stack_ret = asm.stack_push(Type::CArray);
- asm.mov(stack_ret, new_ary);
- }
- }
-
- if doing_kw_call {
- // Here we're calling a method with keyword arguments and specifying
- // keyword arguments at this call site.
- // Number of positional arguments the callee expects before the first
- // keyword argument
- let args_before_kw = required_num + opt_num;
+ new_ary
+ };
- // This struct represents the metadata about the caller-specified
- // keyword arguments.
- let ci_kwarg = unsafe { vm_ci_kwarg(ci) };
- let caller_keyword_len: usize = if ci_kwarg.is_null() {
- 0
+ // Find where to put the rest parameter array
+ let rest_param = if opts_missing == 0 {
+ // All optionals are filled, the rest param goes at the top of the stack
+ argc += 1;
+ asm.stack_push(Type::TArray)
} else {
- unsafe { get_cikw_keyword_len(ci_kwarg) }
- .try_into()
- .unwrap()
+ // The top of the stack will be a missing optional, but the rest
+ // parameter needs to be placed after all the missing optionals.
+ // Place it using a stack operand with a negative stack index.
+ // (Higher magnitude negative stack index have higher address.)
+ assert!(opts_missing > 0);
+ // The argument deepest in the stack will be the 0th local in the callee.
+ let callee_locals_base = argc - 1;
+ let rest_param_stack_idx = callee_locals_base - required_num - opt_num;
+ assert!(rest_param_stack_idx < 0);
+ asm.stack_opnd(rest_param_stack_idx)
};
-
- // This struct represents the metadata about the callee-specified
- // keyword parameters.
- let keyword = unsafe { get_iseq_body_param_keyword(iseq) };
-
- asm.comment("keyword args");
-
- // This is the list of keyword arguments that the callee specified
- // in its initial declaration.
- let callee_kwargs = unsafe { (*keyword).table };
- let total_kwargs: usize = unsafe { (*keyword).num }.try_into().unwrap();
-
- // Here we're going to build up a list of the IDs that correspond to
- // the caller-specified keyword arguments. If they're not in the
- // same order as the order specified in the callee declaration, then
- // we're going to need to generate some code to swap values around
- // on the stack.
- let mut caller_kwargs: Vec<ID> = vec![0; total_kwargs];
-
- for kwarg_idx in 0..caller_keyword_len {
- let sym = unsafe { get_cikw_keywords_idx(ci_kwarg, kwarg_idx.try_into().unwrap()) };
- caller_kwargs[kwarg_idx] = unsafe { rb_sym2id(sym) };
- }
- let mut kwarg_idx = caller_keyword_len;
-
- let mut unspecified_bits = 0;
-
- let keyword_required_num: usize = unsafe { (*keyword).required_num }.try_into().unwrap();
- for callee_idx in keyword_required_num..total_kwargs {
- let mut already_passed = false;
- let callee_kwarg = unsafe { *(callee_kwargs.offset(callee_idx.try_into().unwrap())) };
-
- for caller_idx in 0..caller_keyword_len {
- if caller_kwargs[caller_idx] == callee_kwarg {
- already_passed = true;
- break;
- }
- }
-
- if !already_passed {
- // Reserve space on the stack for each default value we'll be
- // filling in (which is done in the next loop). Also increments
- // argc so that the callee's SP is recorded correctly.
- argc += 1;
- let default_arg = asm.stack_push(Type::Unknown);
-
- // callee_idx - keyword->required_num is used in a couple of places below.
- let req_num: isize = unsafe { (*keyword).required_num }.try_into().unwrap();
- let callee_idx_isize: isize = callee_idx.try_into().unwrap();
- let extra_args = callee_idx_isize - req_num;
-
- //VALUE default_value = keyword->default_values[callee_idx - keyword->required_num];
- let mut default_value = unsafe { *((*keyword).default_values.offset(extra_args)) };
-
- if default_value == Qundef {
- // Qundef means that this value is not constant and must be
- // recalculated at runtime, so we record it in unspecified_bits
- // (Qnil is then used as a placeholder instead of Qundef).
- unspecified_bits |= 0x01 << extra_args;
- default_value = Qnil;
- }
-
- asm.mov(default_arg, default_value.into());
-
- caller_kwargs[kwarg_idx] = callee_kwarg;
- kwarg_idx += 1;
- }
- }
-
- assert!(kwarg_idx == total_kwargs);
-
- // Next, we're going to loop through every keyword that was
- // specified by the caller and make sure that it's in the correct
- // place. If it's not we're going to swap it around with another one.
- for kwarg_idx in 0..total_kwargs {
- let kwarg_idx_isize: isize = kwarg_idx.try_into().unwrap();
- let callee_kwarg = unsafe { *(callee_kwargs.offset(kwarg_idx_isize)) };
-
- // If the argument is already in the right order, then we don't
- // need to generate any code since the expected value is already
- // in the right place on the stack.
- if callee_kwarg == caller_kwargs[kwarg_idx] {
- continue;
- }
-
- // In this case the argument is not in the right place, so we
- // need to find its position where it _should_ be and swap with
- // that location.
- for swap_idx in (kwarg_idx + 1)..total_kwargs {
- if callee_kwarg == caller_kwargs[swap_idx] {
- // First we're going to generate the code that is going
- // to perform the actual swapping at runtime.
- let swap_idx_i32: i32 = swap_idx.try_into().unwrap();
- let kwarg_idx_i32: i32 = kwarg_idx.try_into().unwrap();
- let offset0: u16 = (argc - 1 - swap_idx_i32 - args_before_kw)
- .try_into()
- .unwrap();
- let offset1: u16 = (argc - 1 - kwarg_idx_i32 - args_before_kw)
- .try_into()
- .unwrap();
- stack_swap(asm, offset0, offset1);
-
- // Next we're going to do some bookkeeping on our end so
- // that we know the order that the arguments are
- // actually in now.
- caller_kwargs.swap(kwarg_idx, swap_idx);
-
- break;
- }
- }
+ // Store rest param to memory to avoid register shuffle as
+ // we won't be reading it for the remainder of the block.
+ asm.ctx.dealloc_temp_reg(rest_param.stack_idx());
+ asm.store(rest_param, rest_param_array);
+ }
+
+ // Pop surplus positional arguments when yielding
+ if arg_setup_block {
+ let extras = argc - required_num - opt_num;
+ if extras > 0 {
+ // Checked earlier. If there are keyword args, then
+ // the positional arguments are not at the stack top.
+ assert_eq!(0, kw_arg_num);
+
+ asm.stack_pop(extras as usize);
+ argc = required_num + opt_num;
}
+ }
- // Keyword arguments cause a special extra local variable to be
- // pushed onto the stack that represents the parameters that weren't
- // explicitly given a value and have a non-constant default.
- let unspec_opnd = VALUE::fixnum_from_usize(unspecified_bits).as_u64();
- asm.ctx.dealloc_temp_reg(asm.stack_opnd(-1).stack_idx()); // avoid using a register for unspecified_bits
- asm.mov(asm.stack_opnd(-1), unspec_opnd.into());
+ // Keyword argument passing
+ if doing_kw_call {
+ argc = gen_iseq_kw_call(jit, asm, kw_arg, iseq, argc, has_kwrest);
}
// Same as vm_callee_setup_block_arg_arg0_check and vm_callee_setup_block_arg_arg0_splat
@@ -6037,7 +7603,7 @@ fn gen_send_iseq(
let arg0_reg = asm.load(arg0_opnd);
let array_opnd = get_array_ptr(asm, arg0_reg);
- asm.comment("push splat arg0 onto the stack");
+ asm_comment!(asm, "push splat arg0 onto the stack");
asm.stack_pop(argc.try_into().unwrap());
for i in 0..lead_num {
let stack_opnd = asm.stack_push(Type::Unknown);
@@ -6046,16 +7612,45 @@ fn gen_send_iseq(
argc = lead_num;
}
- // If we have a rest param and optional parameters,
- // we don't actually pass the rest parameter as an argument,
- // instead we set its value in the callee's locals
- let rest_param = if iseq_has_rest && opt_num != 0 {
- argc -= 1;
- let top = asm.stack_pop(1);
- Some((opts_missing as i32, asm.load(top)))
- } else {
- None
- };
+ fn nil_fill(comment: &'static str, fill_range: std::ops::Range<i32>, asm: &mut Assembler) {
+ if fill_range.is_empty() {
+ return;
+ }
+
+ asm_comment!(asm, "{}", comment);
+ for i in fill_range {
+ let value_slot = asm.ctx.sp_opnd(i);
+ asm.store(value_slot, Qnil.into());
+ }
+ }
+
+ // Nil-initialize missing optional parameters
+ nil_fill(
+ "nil-initialize missing optionals",
+ {
+ let begin = -argc + required_num + opts_filled;
+ let end = -argc + required_num + opt_num;
+
+ begin..end
+ },
+ asm
+ );
+ // Nil-initialize the block parameter. It's the last parameter local
+ if iseq_has_block_param {
+ let block_param = asm.ctx.sp_opnd(-argc + num_params - 1);
+ asm.store(block_param, Qnil.into());
+ }
+ // Nil-initialize non-parameter locals
+ nil_fill(
+ "nil-initialize locals",
+ {
+ let begin = -argc + num_params;
+ let end = -argc + num_locals;
+
+ begin..end
+ },
+ asm
+ );
// Points to the receiver operand on the stack unless a captured environment is used
let recv = match captured_opnd {
@@ -6063,20 +7658,18 @@ fn gen_send_iseq(
_ => asm.stack_opnd(argc),
};
let captured_self = captured_opnd.is_some();
- let sp_offset = (argc as isize) + if captured_self { 0 } else { 1 };
+ let sp_offset = argc + if captured_self { 0 } else { 1 };
// Store the updated SP on the current frame (pop arguments and receiver)
- asm.comment("store caller sp");
- let caller_sp = asm.lea(asm.ctx.sp_opnd((SIZEOF_VALUE as isize) * -sp_offset));
+ asm_comment!(asm, "store caller sp");
+ let caller_sp = asm.lea(asm.ctx.sp_opnd(-sp_offset));
asm.store(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SP), caller_sp);
// Store the next PC in the current frame
jit_save_pc(jit, asm);
// Adjust the callee's stack pointer
- let offs =
- (SIZEOF_VALUE as isize) * (3 + (num_locals as isize) + if doing_kw_call { 1 } else { 0 });
- let callee_sp = asm.lea(asm.ctx.sp_opnd(offs));
+ let callee_sp = asm.lea(asm.ctx.sp_opnd(-argc + num_locals + VM_ENV_DATA_SIZE as i32));
let specval = if let Some(prev_ep) = prev_ep {
// We've already side-exited if the callee expects a block, so we
@@ -6085,16 +7678,16 @@ fn gen_send_iseq(
} else if let Some(captured_opnd) = captured_opnd {
let ep_opnd = asm.load(Opnd::mem(64, captured_opnd, SIZEOF_VALUE_I32)); // captured->ep
SpecVal::PrevEPOpnd(ep_opnd)
- } else if block_arg_type == Some(Type::BlockParamProxy) {
- SpecVal::BlockParamProxy
- } else if let Some(block_val) = block {
- SpecVal::BlockISeq(block_val)
+ } else if let Some(BlockArg::TProc) = block_arg_type {
+ SpecVal::BlockHandler(Some(BlockHandler::AlreadySet))
+ } else if let Some(BlockArg::BlockParamProxy) = block_arg_type {
+ SpecVal::BlockHandler(Some(BlockHandler::BlockParamProxy))
} else {
- SpecVal::None
+ SpecVal::BlockHandler(block)
};
// Setup the new frame
- gen_push_frame(jit, asm, true, ControlFrame {
+ perf_call!("gen_send_iseq: ", gen_push_frame(jit, asm, ControlFrame {
frame_type,
specval,
cme,
@@ -6102,8 +7695,20 @@ fn gen_send_iseq(
sp: callee_sp,
iseq: Some(iseq),
pc: None, // We are calling into jitted code, which will set the PC as necessary
- local_size: num_locals
- }, rest_param);
+ }));
+
+ // Log the name of the method we're calling to. We intentionally don't do this for inlined ISEQs.
+ // We also do this after gen_push_frame() to minimize the impact of spill_temps() on asm.ccall().
+ if get_option!(gen_stats) {
+ // Assemble the ISEQ name string
+ let name_str = get_iseq_name(iseq);
+
+ // Get an index for this ISEQ name
+ let iseq_idx = get_iseq_idx(&name_str);
+
+ // Increment the counter for this cfunc
+ asm.ccall(incr_iseq_counter as *const u8, vec![iseq_idx.into()]);
+ }
// No need to set cfp->pc since the callee sets it whenever calling into routines
// that could look at it through jit_save_pc().
@@ -6119,6 +7724,12 @@ fn gen_send_iseq(
// Create a context for the callee
let mut callee_ctx = Context::default();
+ // If the callee has :inline_block annotation and the callsite has a block ISEQ,
+ // duplicate a callee block for each block ISEQ to make its `yield` monomorphic.
+ if let (Some(BlockHandler::BlockISeq(iseq)), true) = (block, builtin_attrs & BUILTIN_ATTR_INLINE_BLOCK != 0) {
+ callee_ctx.set_inline_block(iseq);
+ }
+
// Set the argument types in the callee's context
for arg_idx in 0..argc {
let stack_offs: u8 = (argc - arg_idx - 1).try_into().unwrap();
@@ -6134,19 +7745,16 @@ fn gen_send_iseq(
callee_ctx.upgrade_opnd_type(SelfOpnd, recv_type);
// The callee might change locals through Kernel#binding and other means.
- asm.ctx.clear_local_types();
+ asm.clear_local_types();
- // Pop arguments and receiver in return context, push the return value
- // After the return, sp_offset will be 1. The codegen for leave writes
- // the return value in case of JIT-to-JIT return.
+ // Pop arguments and receiver in return context and
+ // mark it as a continuation of gen_leave()
let mut return_asm = Assembler::new();
- return_asm.ctx = asm.ctx.clone();
+ return_asm.ctx = asm.ctx;
return_asm.stack_pop(sp_offset.try_into().unwrap());
- let return_val = return_asm.stack_push(Type::Unknown);
- // The callee writes a return value on stack. Update reg_temps accordingly.
- return_asm.ctx.dealloc_temp_reg(return_val.stack_idx());
- return_asm.ctx.set_sp_offset(1);
- return_asm.ctx.reset_chain_depth();
+ return_asm.ctx.set_sp_offset(0); // We set SP on the caller's frame above
+ return_asm.ctx.reset_chain_depth_and_defer();
+ return_asm.ctx.set_as_return_landing();
// Write the JIT return address on the callee frame
gen_branch(
@@ -6160,6 +7768,12 @@ fn gen_send_iseq(
BranchGenFn::JITReturn,
);
+ // ec->cfp is updated after cfp->jit_return for rb_profile_frames() safety
+ asm_comment!(asm, "switch to new CFP");
+ let new_cfp = asm.sub(CFP, RUBY_SIZEOF_CONTROL_FRAME.into());
+ asm.mov(CFP, new_cfp);
+ asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), CFP);
+
// Directly jump to the entry point of the callee
gen_direct_jump(
jit,
@@ -6174,6 +7788,326 @@ fn gen_send_iseq(
Some(EndBlock)
}
+// Check if we can handle a keyword call
+fn gen_iseq_kw_call_checks(
+ asm: &mut Assembler,
+ iseq: *const rb_iseq_t,
+ kw_arg: *const rb_callinfo_kwarg,
+ has_kwrest: bool,
+ caller_kw_num: i32
+) -> Option<()> {
+ // This struct represents the metadata about the callee-specified
+ // keyword parameters.
+ let keyword = unsafe { get_iseq_body_param_keyword(iseq) };
+ let keyword_num: usize = unsafe { (*keyword).num }.try_into().unwrap();
+ let keyword_required_num: usize = unsafe { (*keyword).required_num }.try_into().unwrap();
+
+ let mut required_kwargs_filled = 0;
+
+ if keyword_num > 30 || caller_kw_num > 64 {
+ // We have so many keywords that (1 << num) encoded as a FIXNUM
+ // (which shifts it left one more) no longer fits inside a 32-bit
+ // immediate. Similarly, we use a u64 in case of keyword rest parameter.
+ gen_counter_incr(asm, Counter::send_iseq_too_many_kwargs);
+ return None;
+ }
+
+ // Check that the kwargs being passed are valid
+ if caller_kw_num > 0 {
+ // This is the list of keyword arguments that the callee specified
+ // in its initial declaration.
+ // SAFETY: see compile.c for sizing of this slice.
+ let callee_kwargs = if keyword_num == 0 {
+ &[]
+ } else {
+ unsafe { slice::from_raw_parts((*keyword).table, keyword_num) }
+ };
+
+ // Here we're going to build up a list of the IDs that correspond to
+ // the caller-specified keyword arguments. If they're not in the
+ // same order as the order specified in the callee declaration, then
+ // we're going to need to generate some code to swap values around
+ // on the stack.
+ let kw_arg_keyword_len = caller_kw_num as usize;
+ let mut caller_kwargs: Vec<ID> = vec![0; kw_arg_keyword_len];
+ for kwarg_idx in 0..kw_arg_keyword_len {
+ let sym = unsafe { get_cikw_keywords_idx(kw_arg, kwarg_idx.try_into().unwrap()) };
+ caller_kwargs[kwarg_idx] = unsafe { rb_sym2id(sym) };
+ }
+
+ // First, we're going to be sure that the names of every
+ // caller-specified keyword argument correspond to a name in the
+ // list of callee-specified keyword parameters.
+ for caller_kwarg in caller_kwargs {
+ let search_result = callee_kwargs
+ .iter()
+ .enumerate() // inject element index
+ .find(|(_, &kwarg)| kwarg == caller_kwarg);
+
+ match search_result {
+ None if !has_kwrest => {
+ // If the keyword was never found, then we know we have a
+ // mismatch in the names of the keyword arguments, so we need to
+ // bail.
+ gen_counter_incr(asm, Counter::send_iseq_kwargs_mismatch);
+ return None;
+ }
+ Some((callee_idx, _)) if callee_idx < keyword_required_num => {
+ // Keep a count to ensure all required kwargs are specified
+ required_kwargs_filled += 1;
+ }
+ _ => (),
+ }
+ }
+ }
+ assert!(required_kwargs_filled <= keyword_required_num);
+ if required_kwargs_filled != keyword_required_num {
+ gen_counter_incr(asm, Counter::send_iseq_kwargs_mismatch);
+ return None;
+ }
+
+ Some(())
+}
+
+// Codegen for keyword argument handling. Essentially private to gen_send_iseq() since
+// there are a lot of preconditions to check before reaching this code.
+fn gen_iseq_kw_call(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ ci_kwarg: *const rb_callinfo_kwarg,
+ iseq: *const rb_iseq_t,
+ mut argc: i32,
+ has_kwrest: bool,
+) -> i32 {
+ let caller_keyword_len_i32: i32 = if ci_kwarg.is_null() {
+ 0
+ } else {
+ unsafe { get_cikw_keyword_len(ci_kwarg) }
+ };
+ let caller_keyword_len: usize = caller_keyword_len_i32.try_into().unwrap();
+ let anon_kwrest = unsafe { rb_get_iseq_flags_anon_kwrest(iseq) && !get_iseq_flags_has_kw(iseq) };
+
+ // This struct represents the metadata about the callee-specified
+ // keyword parameters.
+ let keyword = unsafe { get_iseq_body_param_keyword(iseq) };
+
+ asm_comment!(asm, "keyword args");
+
+ // This is the list of keyword arguments that the callee specified
+ // in its initial declaration.
+ let callee_kwargs = unsafe { (*keyword).table };
+ let callee_kw_count_i32: i32 = unsafe { (*keyword).num };
+ let callee_kw_count: usize = callee_kw_count_i32.try_into().unwrap();
+ let keyword_required_num: usize = unsafe { (*keyword).required_num }.try_into().unwrap();
+
+ // Here we're going to build up a list of the IDs that correspond to
+ // the caller-specified keyword arguments. If they're not in the
+ // same order as the order specified in the callee declaration, then
+ // we're going to need to generate some code to swap values around
+ // on the stack.
+ let mut kwargs_order: Vec<ID> = vec![0; cmp::max(caller_keyword_len, callee_kw_count)];
+ for kwarg_idx in 0..caller_keyword_len {
+ let sym = unsafe { get_cikw_keywords_idx(ci_kwarg, kwarg_idx.try_into().unwrap()) };
+ kwargs_order[kwarg_idx] = unsafe { rb_sym2id(sym) };
+ }
+
+ let mut unspecified_bits = 0;
+
+ // The stack_opnd() index to the 0th keyword argument.
+ let kwargs_stack_base = caller_keyword_len_i32 - 1;
+
+ // Build the keyword rest parameter hash before we make any changes to the order of
+ // the supplied keyword arguments
+ let kwrest_type = if has_kwrest {
+ c_callable! {
+ fn build_kw_rest(rest_mask: u64, stack_kwargs: *const VALUE, keywords: *const rb_callinfo_kwarg) -> VALUE {
+ if keywords.is_null() {
+ return unsafe { rb_hash_new() };
+ }
+
+ // Use the total number of supplied keywords as a size upper bound
+ let keyword_len = unsafe { (*keywords).keyword_len } as usize;
+ let hash = unsafe { rb_hash_new_with_size(keyword_len as u64) };
+
+ // Put pairs into the kwrest hash as the mask describes
+ for kwarg_idx in 0..keyword_len {
+ if (rest_mask & (1 << kwarg_idx)) != 0 {
+ unsafe {
+ let keyword_symbol = (*keywords).keywords.as_ptr().add(kwarg_idx).read();
+ let keyword_value = stack_kwargs.add(kwarg_idx).read();
+ rb_hash_aset(hash, keyword_symbol, keyword_value);
+ }
+ }
+ }
+ return hash;
+ }
+ }
+
+ asm_comment!(asm, "build kwrest hash");
+
+ // Make a bit mask describing which keywords should go into kwrest.
+ let mut rest_mask: u64 = 0;
+ // Index for one argument that will go into kwrest.
+ let mut rest_collected_idx = None;
+ for (supplied_kw_idx, &supplied_kw) in kwargs_order.iter().take(caller_keyword_len).enumerate() {
+ let mut found = false;
+ for callee_idx in 0..callee_kw_count {
+ let callee_kw = unsafe { callee_kwargs.add(callee_idx).read() };
+ if callee_kw == supplied_kw {
+ found = true;
+ break;
+ }
+ }
+ if !found {
+ rest_mask |= 1 << supplied_kw_idx;
+ if rest_collected_idx.is_none() {
+ rest_collected_idx = Some(supplied_kw_idx as i32);
+ }
+ }
+ }
+
+ let (kwrest, kwrest_type) = if rest_mask == 0 && anon_kwrest {
+ // In case the kwrest hash should be empty and is anonymous in the callee,
+ // we can pass nil instead of allocating. Anonymous kwrest can only be
+ // delegated, and nil is the same as an empty hash when delegating.
+ (Qnil.into(), Type::Nil)
+ } else {
+ // Save PC and SP before allocating
+ jit_save_pc(jit, asm);
+ gen_save_sp(asm);
+
+ // Build the kwrest hash. `struct rb_callinfo_kwarg` is malloc'd, so no GC concerns.
+ let kwargs_start = asm.lea(asm.ctx.sp_opnd(-caller_keyword_len_i32));
+ let hash = asm.ccall(
+ build_kw_rest as _,
+ vec![rest_mask.into(), kwargs_start, Opnd::const_ptr(ci_kwarg.cast())]
+ );
+ (hash, Type::THash)
+ };
+
+ // The kwrest parameter sits after `unspecified_bits` if the callee specifies any
+ // keywords.
+ let stack_kwrest_idx = kwargs_stack_base - callee_kw_count_i32 - i32::from(callee_kw_count > 0);
+ let stack_kwrest = asm.stack_opnd(stack_kwrest_idx);
+ // If `stack_kwrest` already has another argument there, we need to stow it elsewhere
+ // first before putting kwrest there. Use `rest_collected_idx` because that value went
+ // into kwrest so the slot is now free.
+ let kwrest_idx = callee_kw_count + usize::from(callee_kw_count > 0);
+ if let (Some(rest_collected_idx), true) = (rest_collected_idx, kwrest_idx < caller_keyword_len) {
+ let rest_collected = asm.stack_opnd(kwargs_stack_base - rest_collected_idx);
+ let mapping = asm.ctx.get_opnd_mapping(stack_kwrest.into());
+ asm.mov(rest_collected, stack_kwrest);
+ asm.ctx.set_opnd_mapping(rest_collected.into(), mapping);
+ // Update our bookkeeping to inform the reordering step later.
+ kwargs_order[rest_collected_idx as usize] = kwargs_order[kwrest_idx];
+ kwargs_order[kwrest_idx] = 0;
+ }
+ // Put kwrest straight into memory, since we might pop it later
+ asm.ctx.dealloc_temp_reg(stack_kwrest.stack_idx());
+ asm.mov(stack_kwrest, kwrest);
+ if stack_kwrest_idx >= 0 {
+ asm.ctx.set_opnd_mapping(stack_kwrest.into(), TempMapping::map_to_stack(kwrest_type));
+ }
+
+ Some(kwrest_type)
+ } else {
+ None
+ };
+
+ // Ensure the stack is large enough for the callee
+ for _ in caller_keyword_len..callee_kw_count {
+ argc += 1;
+ asm.stack_push(Type::Unknown);
+ }
+ // Now this is the stack_opnd() index to the 0th keyword argument.
+ let kwargs_stack_base = kwargs_order.len() as i32 - 1;
+
+ // Next, we're going to loop through every keyword that was
+ // specified by the caller and make sure that it's in the correct
+ // place. If it's not we're going to swap it around with another one.
+ for kwarg_idx in 0..callee_kw_count {
+ let callee_kwarg = unsafe { callee_kwargs.add(kwarg_idx).read() };
+
+ // If the argument is already in the right order, then we don't
+ // need to generate any code since the expected value is already
+ // in the right place on the stack.
+ if callee_kwarg == kwargs_order[kwarg_idx] {
+ continue;
+ }
+
+ // In this case the argument is not in the right place, so we
+ // need to find its position where it _should_ be and swap with
+ // that location.
+ for swap_idx in 0..kwargs_order.len() {
+ if callee_kwarg == kwargs_order[swap_idx] {
+ // First we're going to generate the code that is going
+ // to perform the actual swapping at runtime.
+ let swap_idx_i32: i32 = swap_idx.try_into().unwrap();
+ let kwarg_idx_i32: i32 = kwarg_idx.try_into().unwrap();
+ let offset0 = kwargs_stack_base - swap_idx_i32;
+ let offset1 = kwargs_stack_base - kwarg_idx_i32;
+ stack_swap(asm, offset0, offset1);
+
+ // Next we're going to do some bookkeeping on our end so
+ // that we know the order that the arguments are
+ // actually in now.
+ kwargs_order.swap(kwarg_idx, swap_idx);
+
+ break;
+ }
+ }
+ }
+
+ // Now that every caller specified kwarg is in the right place, filling
+ // in unspecified default paramters won't overwrite anything.
+ for kwarg_idx in keyword_required_num..callee_kw_count {
+ if kwargs_order[kwarg_idx] != unsafe { callee_kwargs.add(kwarg_idx).read() } {
+ let default_param_idx = kwarg_idx - keyword_required_num;
+ let mut default_value = unsafe { (*keyword).default_values.add(default_param_idx).read() };
+
+ if default_value == Qundef {
+ // Qundef means that this value is not constant and must be
+ // recalculated at runtime, so we record it in unspecified_bits
+ // (Qnil is then used as a placeholder instead of Qundef).
+ unspecified_bits |= 0x01 << default_param_idx;
+ default_value = Qnil;
+ }
+
+ let default_param = asm.stack_opnd(kwargs_stack_base - kwarg_idx as i32);
+ let param_type = Type::from(default_value);
+ asm.mov(default_param, default_value.into());
+ asm.ctx.set_opnd_mapping(default_param.into(), TempMapping::map_to_stack(param_type));
+ }
+ }
+
+ // Pop extra arguments that went into kwrest now that they're at stack top
+ if has_kwrest && caller_keyword_len > callee_kw_count {
+ let extra_kwarg_count = caller_keyword_len - callee_kw_count;
+ asm.stack_pop(extra_kwarg_count);
+ argc = argc - extra_kwarg_count as i32;
+ }
+
+ // Keyword arguments cause a special extra local variable to be
+ // pushed onto the stack that represents the parameters that weren't
+ // explicitly given a value and have a non-constant default.
+ if callee_kw_count > 0 {
+ let unspec_opnd = VALUE::fixnum_from_usize(unspecified_bits).as_u64();
+ let top = asm.stack_push(Type::Fixnum);
+ asm.mov(top, unspec_opnd.into());
+ argc += 1;
+ }
+
+ // The kwrest parameter sits after `unspecified_bits`
+ if let Some(kwrest_type) = kwrest_type {
+ let kwrest = asm.stack_push(kwrest_type);
+ // We put the kwrest parameter in memory earlier
+ asm.ctx.dealloc_temp_reg(kwrest.stack_idx());
+ argc += 1;
+ }
+
+ argc
+}
+
/// This is a helper function to allow us to exit early
/// during code generation if a predicate is true.
/// We return Option<()> here because we will be able to
@@ -6199,22 +8133,10 @@ fn exit_if_has_post(asm: &mut Assembler, iseq: *const rb_iseq_t) -> Option<()> {
}
#[must_use]
-fn exit_if_has_kwrest(asm: &mut Assembler, iseq: *const rb_iseq_t) -> Option<()> {
- exit_if(asm, unsafe { get_iseq_flags_has_kwrest(iseq) }, Counter::send_iseq_has_kwrest)
-}
-
-#[must_use]
-fn exit_if_splat_and_ruby2_keywords(asm: &mut Assembler, jit: &mut JITState, flags: u32) -> Option<()> {
- // In order to handle backwards compatibility between ruby 3 and 2
- // ruby2_keywords was introduced. It is called only on methods
- // with splat and changes they way they handle them.
- // We are just going to not compile these.
- // https://www.rubydoc.info/stdlib/core/Proc:ruby2_keywords
- exit_if(
- asm,
- unsafe { get_iseq_flags_ruby2_keywords(jit.iseq) } && flags & VM_CALL_ARGS_SPLAT != 0,
- Counter::send_iseq_ruby2_keywords,
- )
+fn exit_if_kwsplat_non_nil(asm: &mut Assembler, flags: u32, counter: Counter) -> Option<()> {
+ let kw_splat = flags & VM_CALL_KW_SPLAT != 0;
+ let kw_splat_stack = StackOpnd((flags & VM_CALL_ARGS_BLOCKARG != 0).into());
+ exit_if(asm, kw_splat && asm.ctx.get_opnd_type(kw_splat_stack) != Type::Nil, counter)
}
#[must_use]
@@ -6223,27 +8145,31 @@ fn exit_if_has_rest_and_captured(asm: &mut Assembler, iseq_has_rest: bool, captu
}
#[must_use]
-fn exit_if_has_rest_and_send( asm: &mut Assembler, iseq_has_rest: bool, flags: u32) -> Option<()> {
- exit_if(asm, iseq_has_rest && flags & VM_CALL_ARGS_SPLAT != 0, Counter::send_iseq_has_rest_and_send)
+fn exit_if_has_kwrest_and_captured(asm: &mut Assembler, iseq_has_kwrest: bool, captured_opnd: Option<Opnd>) -> Option<()> {
+ // We need to call a C function to allocate the kwrest hash, but also need to hold the captred
+ // block across the call, which we can't do.
+ exit_if(asm, iseq_has_kwrest && captured_opnd.is_some(), Counter::send_iseq_has_kwrest_and_captured)
}
#[must_use]
-fn exit_if_has_rest_and_supplying_kws(asm: &mut Assembler, iseq_has_rest: bool, iseq: *const rb_iseq_t, supplying_kws: bool) -> Option<()> {
+fn exit_if_has_rest_and_supplying_kws(asm: &mut Assembler, iseq_has_rest: bool, supplying_kws: bool) -> Option<()> {
+ // There can be a gap between the rest parameter array and the supplied keywords, or
+ // no space to put the rest array (e.g. `def foo(*arr, k:) = arr; foo(k: 1)` 1 is
+ // sitting where the rest array should be).
exit_if(
asm,
- iseq_has_rest && unsafe { get_iseq_flags_has_kw(iseq) } && supplying_kws,
+ iseq_has_rest && supplying_kws,
Counter::send_iseq_has_rest_and_kw_supplied,
)
}
#[must_use]
-fn exit_if_supplying_kw_and_has_no_kw(asm: &mut Assembler, supplying_kws: bool, iseq: *const rb_iseq_t) -> Option<()> {
- // If we have keyword arguments being passed to a callee that only takes
- // positionals, then we need to allocate a hash. For now we're going to
- // call that too complex and bail.
+fn exit_if_supplying_kw_and_has_no_kw(asm: &mut Assembler, supplying_kws: bool, callee_kws: bool) -> Option<()> {
+ // Passing keyword arguments to a callee means allocating a hash and treating
+ // that as a positional argument. Bail for now.
exit_if(
asm,
- supplying_kws && !unsafe { get_iseq_flags_has_kw(iseq) },
+ supplying_kws && !callee_kws,
Counter::send_iseq_has_no_kw,
)
}
@@ -6260,25 +8186,23 @@ fn exit_if_supplying_kws_and_accept_no_kwargs(asm: &mut Assembler, supplying_kws
}
#[must_use]
-fn exit_if_splat_and_zsuper(asm: &mut Assembler, flags: u32) -> Option<()> {
- // zsuper methods are super calls without any arguments.
- // They are also marked as splat, but don't actually have an array
- // they pull arguments from, instead we need to change to call
- // a different method with the current stack.
- exit_if(asm, flags & VM_CALL_ARGS_SPLAT != 0 && flags & VM_CALL_ZSUPER != 0, Counter::send_iseq_zsuper)
-}
-
-#[must_use]
fn exit_if_doing_kw_and_splat(asm: &mut Assembler, doing_kw_call: bool, flags: u32) -> Option<()> {
exit_if(asm, doing_kw_call && flags & VM_CALL_ARGS_SPLAT != 0, Counter::send_iseq_splat_with_kw)
}
#[must_use]
-fn exit_if_wrong_number_arguments(asm: &mut Assembler, opts_filled: i32, flags: u32, opt_num: i32, iseq_has_rest: bool) -> Option<()> {
+fn exit_if_wrong_number_arguments(
+ asm: &mut Assembler,
+ args_setup_block: bool,
+ opts_filled: i32,
+ flags: u32,
+ opt_num: i32,
+ iseq_has_rest: bool,
+) -> Option<()> {
// Too few arguments and no splat to make up for it
let too_few = opts_filled < 0 && flags & VM_CALL_ARGS_SPLAT == 0;
- // Too many arguments and no place to put them (i.e. rest arg)
- let too_many = opts_filled > opt_num && !iseq_has_rest;
+ // Too many arguments and no sink that take them
+ let too_many = opts_filled > opt_num && !(iseq_has_rest || args_setup_block);
exit_if(asm, too_few || too_many, Counter::send_iseq_arity_error)
}
@@ -6300,21 +8224,45 @@ fn exit_if_has_rest_and_optional_and_block(asm: &mut Assembler, iseq_has_rest: b
)
}
+#[derive(Clone, Copy)]
+enum BlockArg {
+ Nil,
+ /// A special sentinel value indicating the block parameter should be read from
+ /// the current surrounding cfp
+ BlockParamProxy,
+ /// A proc object. Could be an instance of a subclass of ::rb_cProc
+ TProc,
+}
+
#[must_use]
-fn exit_if_unsupported_block_arg_type(asm: &mut Assembler, block_arg_type: Option<Type>) -> Option<()> {
+fn exit_if_unsupported_block_arg_type(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ supplying_block_arg: bool
+) -> Option<Option<BlockArg>> {
+ let block_arg_type = if supplying_block_arg {
+ asm.ctx.get_opnd_type(StackOpnd(0))
+ } else {
+ // Passing no block argument
+ return Some(None);
+ };
+
match block_arg_type {
- Some(Type::Nil | Type::BlockParamProxy) => {
- // We'll handle this later
- }
- None => {
- // Nothing to do
+ // We'll handle Nil and BlockParamProxy later
+ Type::Nil => Some(Some(BlockArg::Nil)),
+ Type::BlockParamProxy => Some(Some(BlockArg::BlockParamProxy)),
+ _ if {
+ let sample_block_arg = jit.peek_at_stack(&asm.ctx, 0);
+ unsafe { rb_obj_is_proc(sample_block_arg) }.test()
+ } => {
+ // Speculate that we'll have a proc as the block arg
+ Some(Some(BlockArg::TProc))
}
_ => {
- gen_counter_incr(asm, Counter::send_block_arg);
- return None
+ gen_counter_incr(asm, Counter::send_iseq_block_arg_type);
+ None
}
}
- Some(())
}
#[must_use]
@@ -6372,7 +8320,7 @@ fn gen_struct_aref(
// true of the converse.
let embedded = unsafe { FL_TEST_RAW(comptime_recv, VALUE(RSTRUCT_EMBED_LEN_MASK)) };
- asm.comment("struct aref");
+ asm_comment!(asm, "struct aref");
let recv = asm.stack_pop(1);
let recv = asm.load(recv);
@@ -6418,9 +8366,8 @@ fn gen_struct_aset(
assert!(unsafe { RB_TYPE_P(comptime_recv, RUBY_T_STRUCT) });
assert!((off as i64) < unsafe { RSTRUCT_LEN(comptime_recv) });
- asm.comment("struct aset");
+ asm_comment!(asm, "struct aset");
- asm.spill_temps(); // for ccall (must be done before stack_pop)
let val = asm.stack_pop(1);
let recv = asm.stack_pop(1);
@@ -6433,12 +8380,62 @@ fn gen_struct_aset(
Some(EndBlock)
}
+// Generate code that calls a method with dynamic dispatch
+fn gen_send_dynamic<F: Fn(&mut Assembler) -> Opnd>(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ ocb: &mut OutlinedCb,
+ cd: *const rb_call_data,
+ sp_pops: usize,
+ vm_sendish: F,
+) -> Option<CodegenStatus> {
+ // Our frame handling is not compatible with tailcall
+ if unsafe { vm_ci_flag((*cd).ci) } & VM_CALL_TAILCALL != 0 {
+ return None;
+ }
+ jit_perf_symbol_push!(jit, asm, "gen_send_dynamic", PerfMap::Codegen);
+
+ // Rewind stack_size using ctx.with_stack_size to allow stack_size changes
+ // before you return None.
+ asm.ctx = asm.ctx.with_stack_size(jit.stack_size_for_pc);
+
+ // Save PC and SP to prepare for dynamic dispatch
+ jit_prepare_non_leaf_call(jit, asm);
+
+ // Squash stack canary that might be left over from elsewhere
+ assert_eq!(false, asm.get_leaf_ccall());
+ if cfg!(debug_assertions) {
+ asm.store(asm.ctx.sp_opnd(0), 0.into());
+ }
+
+ // Dispatch a method
+ let ret = vm_sendish(asm);
+
+ // Pop arguments and a receiver
+ asm.stack_pop(sp_pops);
+
+ // Push the return value
+ let stack_ret = asm.stack_push(Type::Unknown);
+ asm.mov(stack_ret, ret);
+
+ // Fix the interpreter SP deviated by vm_sendish
+ asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SP), SP);
+
+ gen_counter_incr(asm, Counter::num_send_dynamic);
+
+ jit_perf_symbol_pop!(jit, asm, PerfMap::Codegen);
+
+ // End the current block for invalidationg and sharing the same successor
+ jump_to_next_insn(jit, asm, ocb);
+ Some(EndBlock)
+}
+
fn gen_send_general(
jit: &mut JITState,
asm: &mut Assembler,
ocb: &mut OutlinedCb,
cd: *const rb_call_data,
- block: Option<IseqPtr>,
+ block: Option<BlockHandler>,
) -> Option<CodegenStatus> {
// Relevant definitions:
// rb_execution_context_t : vm_core.h
@@ -6455,12 +8452,6 @@ fn gen_send_general(
let mut mid = unsafe { vm_ci_mid(ci) };
let mut flags = unsafe { vm_ci_flag(ci) };
- // Don't JIT calls with keyword splat
- if flags & VM_CALL_KW_SPLAT != 0 {
- gen_counter_incr(asm, Counter::send_kw_splat);
- return None;
- }
-
// Defer compilation so we can specialize on class of receiver
if !jit.at_current_insn() {
defer_compilation(jit, asm, ocb);
@@ -6470,6 +8461,19 @@ fn gen_send_general(
let recv_idx = argc + if flags & VM_CALL_ARGS_BLOCKARG != 0 { 1 } else { 0 };
let comptime_recv = jit.peek_at_stack(&asm.ctx, recv_idx as isize);
let comptime_recv_klass = comptime_recv.class_of();
+ assert_eq!(RUBY_T_CLASS, comptime_recv_klass.builtin_type(),
+ "objects visible to ruby code should have a T_CLASS in their klass field");
+
+ // Don't compile calls through singleton classes to avoid retaining the receiver.
+ // Make an exception for class methods since classes tend to be retained anyways.
+ // Also compile calls on top_self to help tests.
+ if VALUE(0) != unsafe { FL_TEST(comptime_recv_klass, VALUE(RUBY_FL_SINGLETON as usize)) }
+ && comptime_recv != unsafe { rb_vm_top_self() }
+ && !unsafe { RB_TYPE_P(comptime_recv, RUBY_T_CLASS) }
+ && !unsafe { RB_TYPE_P(comptime_recv, RUBY_T_MODULE) } {
+ gen_counter_incr(asm, Counter::send_singleton_class);
+ return None;
+ }
// Points to the receiver operand on the stack
let recv = asm.stack_opnd(recv_idx);
@@ -6477,16 +8481,7 @@ fn gen_send_general(
// Log the name of the method we're calling to
#[cfg(feature = "disasm")]
- {
- let class_name = unsafe { cstr_to_rust_string(rb_class2name(comptime_recv_klass)) };
- let method_name = unsafe { cstr_to_rust_string(rb_id2name(mid)) };
- match (class_name, method_name) {
- (Some(class_name), Some(method_name)) => {
- asm.comment(&format!("call to {}#{}", class_name, method_name))
- }
- _ => {}
- }
- }
+ asm_comment!(asm, "call to {}", get_method_name(Some(comptime_recv_klass), mid));
// Gather some statistics about sends
gen_counter_incr(asm, Counter::num_send);
@@ -6496,8 +8491,13 @@ fn gen_send_general(
if asm.ctx.get_chain_depth() > 1 {
gen_counter_incr(asm, Counter::num_send_polymorphic);
}
+ // If megamorphic, let the caller fallback to dynamic dispatch
+ if asm.ctx.get_chain_depth() >= SEND_MAX_DEPTH {
+ gen_counter_incr(asm, Counter::send_megamorphic);
+ return None;
+ }
- jit_guard_known_klass(
+ perf_call!("gen_send_general: ", jit_guard_known_klass(
jit,
asm,
ocb,
@@ -6506,16 +8506,20 @@ fn gen_send_general(
recv_opnd,
comptime_recv,
SEND_MAX_DEPTH,
- Counter::send_klass_megamorphic,
- );
+ Counter::guard_send_klass_megamorphic,
+ ));
// Do method lookup
let mut cme = unsafe { rb_callable_method_entry(comptime_recv_klass, mid) };
if cme.is_null() {
- // TODO: counter
+ gen_counter_incr(asm, Counter::send_cme_not_found);
return None;
}
+ // Load an overloaded cme if applicable. See vm_search_cc().
+ // It allows you to use a faster ISEQ if possible.
+ cme = unsafe { rb_check_overloaded_cme(cme, ci) };
+
let visi = unsafe { METHOD_ENTRY_VISI(cme) };
match visi {
METHOD_VISI_PUBLIC => {
@@ -6525,6 +8529,7 @@ fn gen_send_general(
if flags & VM_CALL_FCALL == 0 {
// Can only call private methods with FCALL callsites.
// (at the moment they are callsites without a receiver or an explicit `self` receiver)
+ gen_counter_incr(asm, Counter::send_private_not_fcall);
return None;
}
}
@@ -6553,37 +8558,59 @@ fn gen_send_general(
VM_METHOD_TYPE_ISEQ => {
let iseq = unsafe { get_def_iseq_ptr((*cme).def) };
let frame_type = VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL;
- return gen_send_iseq(jit, asm, ocb, iseq, ci, frame_type, None, cme, block, flags, argc, None);
+ return perf_call! { gen_send_iseq(jit, asm, ocb, iseq, ci, frame_type, None, cme, block, flags, argc, None) };
}
VM_METHOD_TYPE_CFUNC => {
- return gen_send_cfunc(
+ return perf_call! { gen_send_cfunc(
jit,
asm,
ocb,
ci,
cme,
block,
- &comptime_recv_klass,
+ Some(comptime_recv_klass),
flags,
argc,
- );
+ ) };
}
VM_METHOD_TYPE_IVAR => {
- if flags & VM_CALL_ARGS_SPLAT != 0 {
- gen_counter_incr(asm, Counter::send_args_splat_ivar);
+ // This is a .send call not supported right now for attr_reader
+ if flags & VM_CALL_OPT_SEND != 0 {
+ gen_counter_incr(asm, Counter::send_send_attr_reader);
return None;
}
- if argc != 0 {
- // Argument count mismatch. Getters take no arguments.
- gen_counter_incr(asm, Counter::send_getter_arity);
- return None;
+ if flags & VM_CALL_ARGS_BLOCKARG != 0 {
+ match asm.ctx.get_opnd_type(StackOpnd(0)) {
+ Type::Nil | Type::BlockParamProxy => {
+ // Getters ignore the block arg, and these types of block args can be
+ // passed without side-effect (never any `to_proc` call).
+ asm.stack_pop(1);
+ }
+ _ => {
+ gen_counter_incr(asm, Counter::send_getter_block_arg);
+ return None;
+ }
+ }
}
- // This is a .send call not supported right now for getters
- if flags & VM_CALL_OPT_SEND != 0 {
- gen_counter_incr(asm, Counter::send_send_getter);
- return None;
+ if argc != 0 {
+ // Guard for simple splat of empty array
+ if VM_CALL_ARGS_SPLAT == flags & (VM_CALL_ARGS_SPLAT | VM_CALL_KWARG | VM_CALL_KW_SPLAT)
+ && argc == 1 {
+ // Not using chain guards since on failure these likely end up just raising
+ // ArgumentError
+ let splat = asm.stack_opnd(0);
+ guard_object_is_array(asm, splat, splat.into(), Counter::guard_send_getter_splat_non_empty);
+ let splat_len = get_array_len(asm, splat);
+ asm.cmp(splat_len, 0.into());
+ asm.jne(Target::side_exit(Counter::guard_send_getter_splat_non_empty));
+ asm.stack_pop(1);
+ } else {
+ // Argument count mismatch. Getters take no arguments.
+ gen_counter_incr(asm, Counter::send_getter_arity);
+ return None;
+ }
}
if c_method_tracing_currently_enabled(jit) {
@@ -6600,13 +8627,9 @@ fn gen_send_general(
return None;
}
+ let recv = asm.stack_opnd(0); // the receiver should now be the stack top
let ivar_name = unsafe { get_cme_def_body_attr_id(cme) };
- if flags & VM_CALL_ARGS_BLOCKARG != 0 {
- gen_counter_incr(asm, Counter::send_block_arg);
- return None;
- }
-
return gen_get_ivar(
jit,
asm,
@@ -6615,10 +8638,15 @@ fn gen_send_general(
comptime_recv,
ivar_name,
recv,
- recv_opnd,
+ recv.into(),
);
}
VM_METHOD_TYPE_ATTRSET => {
+ // This is a .send call not supported right now for attr_writer
+ if flags & VM_CALL_OPT_SEND != 0 {
+ gen_counter_incr(asm, Counter::send_send_attr_writer);
+ return None;
+ }
if flags & VM_CALL_ARGS_SPLAT != 0 {
gen_counter_incr(asm, Counter::send_args_splat_attrset);
return None;
@@ -6635,11 +8663,11 @@ fn gen_send_general(
gen_counter_incr(asm, Counter::send_cfunc_tracing);
return None;
} else if flags & VM_CALL_ARGS_BLOCKARG != 0 {
- gen_counter_incr(asm, Counter::send_block_arg);
+ gen_counter_incr(asm, Counter::send_attrset_block_arg);
return None;
} else {
let ivar_name = unsafe { get_cme_def_body_attr_id(cme) };
- return gen_set_ivar(jit, asm, ivar_name, flags, argc);
+ return gen_set_ivar(jit, asm, ocb, comptime_recv, ivar_name, StackOpnd(1), None);
}
}
// Block method, e.g. define_method(:foo) { :my_block }
@@ -6658,7 +8686,7 @@ fn gen_send_general(
// Send family of methods, e.g. call/apply
VM_METHOD_TYPE_OPTIMIZED => {
if flags & VM_CALL_ARGS_BLOCKARG != 0 {
- gen_counter_incr(asm, Counter::send_block_arg);
+ gen_counter_incr(asm, Counter::send_optimized_block_arg);
return None;
}
@@ -6689,13 +8717,9 @@ fn gen_send_general(
let compile_time_name = jit.peek_at_stack(&asm.ctx, argc as isize);
- if !compile_time_name.string_p() && !compile_time_name.static_sym_p() {
- gen_counter_incr(asm, Counter::send_send_chain_not_string_or_sym);
- return None;
- }
-
mid = unsafe { rb_get_symbol_id(compile_time_name) };
if mid == 0 {
+ // This also rejects method names that need conversion
gen_counter_incr(asm, Counter::send_send_null_mid);
return None;
}
@@ -6710,49 +8734,23 @@ fn gen_send_general(
jit.assume_method_lookup_stable(asm, ocb, cme);
- let (known_class, type_mismatch_counter) = {
- if compile_time_name.string_p() {
- (
- unsafe { rb_cString },
- Counter::send_send_chain_not_string,
- )
- } else {
- (
- unsafe { rb_cSymbol },
- Counter::send_send_chain_not_sym,
- )
- }
- };
-
- let name_opnd = asm.stack_opnd(argc);
- jit_guard_known_klass(
- jit,
+ asm_comment!(
asm,
- ocb,
- known_class,
- name_opnd,
- name_opnd.into(),
- compile_time_name,
- 2, // We have string or symbol, so max depth is 2
- type_mismatch_counter
+ "guard sending method name \'{}\'",
+ unsafe { cstr_to_rust_string(rb_id2name(mid)) }.unwrap_or_else(|| "<unknown>".to_owned()),
);
- // Need to do this here so we don't have too many live
- // values for the register allocator.
- let name_opnd = asm.load(name_opnd);
-
- asm.spill_temps(); // for ccall
+ let name_opnd = asm.stack_opnd(argc);
let symbol_id_opnd = asm.ccall(rb_get_symbol_id as *const u8, vec![name_opnd]);
- asm.comment("chain_guard_send");
asm.cmp(symbol_id_opnd, mid.into());
jit_chain_guard(
JCC_JNE,
jit,
asm,
ocb,
- SEND_MAX_CHAIN_DEPTH,
- Counter::send_send_chain,
+ SEND_MAX_DEPTH,
+ Counter::guard_send_send_name_chain,
);
// We have changed the argc, flags, mid, and cme, so we need to re-enter the match
@@ -6795,7 +8793,7 @@ fn gen_send_general(
let sp = asm.lea(asm.ctx.sp_opnd(0));
// Save the PC and SP because the callee can make Ruby calls
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_non_leaf_call(jit, asm);
let kw_splat = flags & VM_CALL_KW_SPLAT;
let stack_argument_pointer = asm.lea(Opnd::mem(64, sp, -(argc) * SIZEOF_VALUE_I32));
@@ -6884,6 +8882,32 @@ fn gen_send_general(
}
}
+/// Get class name from a class pointer.
+fn get_class_name(class: Option<VALUE>) -> String {
+ class.and_then(|class| unsafe {
+ cstr_to_rust_string(rb_class2name(class))
+ }).unwrap_or_else(|| "Unknown".to_string())
+}
+
+/// Assemble "{class_name}#{method_name}" from a class pointer and a method ID
+fn get_method_name(class: Option<VALUE>, mid: u64) -> String {
+ let class_name = get_class_name(class);
+ let method_name = if mid != 0 {
+ unsafe { cstr_to_rust_string(rb_id2name(mid)) }
+ } else {
+ None
+ }.unwrap_or_else(|| "Unknown".to_string());
+ format!("{}#{}", class_name, method_name)
+}
+
+/// Assemble "{label}@{iseq_path}:{lineno}" (iseq_inspect() format) from an ISEQ
+fn get_iseq_name(iseq: IseqPtr) -> String {
+ let c_string = unsafe { rb_yjit_iseq_inspect(iseq) };
+ let string = unsafe { CStr::from_ptr(c_string) }.to_str()
+ .unwrap_or_else(|_| "not UTF-8").to_string();
+ unsafe { ruby_xfree(c_string as *mut c_void); }
+ string
+}
/// Shifts the stack for send in order to remove the name of the method
/// Comment below borrow from vm_call_opt_send in vm_insnhelper.c
@@ -6901,7 +8925,7 @@ fn gen_send_general(
///
/// We do this for our compiletime context and the actual stack
fn handle_opt_send_shift_stack(asm: &mut Assembler, argc: i32) {
- asm.comment("shift_stack");
+ asm_comment!(asm, "shift_stack");
for j in (0..argc).rev() {
let opnd = asm.stack_opnd(j);
let opnd2 = asm.stack_opnd(j + 1);
@@ -6915,9 +8939,22 @@ fn gen_opt_send_without_block(
asm: &mut Assembler,
ocb: &mut OutlinedCb,
) -> Option<CodegenStatus> {
+ // Generate specialized code if possible
let cd = jit.get_arg(0).as_ptr();
+ if let Some(status) = perf_call! { gen_send_general(jit, asm, ocb, cd, None) } {
+ return Some(status);
+ }
- gen_send_general(jit, asm, ocb, cd, None)
+ // Otherwise, fallback to dynamic dispatch using the interpreter's implementation of send
+ gen_send_dynamic(jit, asm, ocb, cd, unsafe { rb_yjit_sendish_sp_pops((*cd).ci) }, |asm| {
+ extern "C" {
+ fn rb_vm_opt_send_without_block(ec: EcPtr, cfp: CfpPtr, cd: VALUE) -> VALUE;
+ }
+ asm.ccall(
+ rb_vm_opt_send_without_block as *const u8,
+ vec![EC, CFP, (cd as usize).into()],
+ )
+ })
}
fn gen_send(
@@ -6925,9 +8962,24 @@ fn gen_send(
asm: &mut Assembler,
ocb: &mut OutlinedCb,
) -> Option<CodegenStatus> {
+ // Generate specialized code if possible
let cd = jit.get_arg(0).as_ptr();
- let block = jit.get_arg(1).as_optional_ptr();
- return gen_send_general(jit, asm, ocb, cd, block);
+ let block = jit.get_arg(1).as_optional_ptr().map(|iseq| BlockHandler::BlockISeq(iseq));
+ if let Some(status) = perf_call! { gen_send_general(jit, asm, ocb, cd, block) } {
+ return Some(status);
+ }
+
+ // Otherwise, fallback to dynamic dispatch using the interpreter's implementation of send
+ let blockiseq = jit.get_arg(1).as_iseq();
+ gen_send_dynamic(jit, asm, ocb, cd, unsafe { rb_yjit_sendish_sp_pops((*cd).ci) }, |asm| {
+ extern "C" {
+ fn rb_vm_send(ec: EcPtr, cfp: CfpPtr, cd: VALUE, blockiseq: IseqPtr) -> VALUE;
+ }
+ asm.ccall(
+ rb_vm_send as *const u8,
+ vec![EC, CFP, (cd as usize).into(), VALUE(blockiseq as usize).into()],
+ )
+ })
}
fn gen_invokeblock(
@@ -6935,13 +8987,42 @@ fn gen_invokeblock(
asm: &mut Assembler,
ocb: &mut OutlinedCb,
) -> Option<CodegenStatus> {
+ // Generate specialized code if possible
+ let cd = jit.get_arg(0).as_ptr();
+ if let Some(status) = gen_invokeblock_specialized(jit, asm, ocb, cd) {
+ return Some(status);
+ }
+
+ // Otherwise, fallback to dynamic dispatch using the interpreter's implementation of send
+ gen_send_dynamic(jit, asm, ocb, cd, unsafe { rb_yjit_invokeblock_sp_pops((*cd).ci) }, |asm| {
+ extern "C" {
+ fn rb_vm_invokeblock(ec: EcPtr, cfp: CfpPtr, cd: VALUE) -> VALUE;
+ }
+ asm.ccall(
+ rb_vm_invokeblock as *const u8,
+ vec![EC, CFP, (cd as usize).into()],
+ )
+ })
+}
+
+fn gen_invokeblock_specialized(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ ocb: &mut OutlinedCb,
+ cd: *const rb_call_data,
+) -> Option<CodegenStatus> {
if !jit.at_current_insn() {
defer_compilation(jit, asm, ocb);
return Some(EndBlock);
}
+ // Fallback to dynamic dispatch if this callsite is megamorphic
+ if asm.ctx.get_chain_depth() >= SEND_MAX_DEPTH {
+ gen_counter_incr(asm, Counter::invokeblock_megamorphic);
+ return None;
+ }
+
// Get call info
- let cd = jit.get_arg(0).as_ptr();
let ci = unsafe { get_call_data_ci(cd) };
let argc: i32 = unsafe { vm_ci_argc(ci) }.try_into().unwrap();
let flags = unsafe { vm_ci_flag(ci) };
@@ -6956,13 +9037,13 @@ fn gen_invokeblock(
gen_counter_incr(asm, Counter::invokeblock_none);
None
} else if comptime_handler.0 & 0x3 == 0x1 { // VM_BH_ISEQ_BLOCK_P
- asm.comment("get local EP");
+ asm_comment!(asm, "get local EP");
let ep_opnd = gen_get_lep(jit, asm);
let block_handler_opnd = asm.load(
Opnd::mem(64, ep_opnd, SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_SPECVAL)
);
- asm.comment("guard block_handler type");
+ asm_comment!(asm, "guard block_handler type");
let tag_opnd = asm.and(block_handler_opnd, 0x3.into()); // block_handler is a tagged pointer
asm.cmp(tag_opnd, 0x1.into()); // VM_BH_ISEQ_BLOCK_P
jit_chain_guard(
@@ -6970,40 +9051,34 @@ fn gen_invokeblock(
jit,
asm,
ocb,
- SEND_MAX_CHAIN_DEPTH,
- Counter::invokeblock_tag_changed,
+ SEND_MAX_DEPTH,
+ Counter::guard_invokeblock_tag_changed,
);
+ // If the current ISEQ is annotated to be inlined but it's not being inlined here,
+ // generate a dynamic dispatch to avoid making this yield megamorphic.
+ if unsafe { rb_yjit_iseq_builtin_attrs(jit.iseq) } & BUILTIN_ATTR_INLINE_BLOCK != 0 && !asm.ctx.inline() {
+ gen_counter_incr(asm, Counter::invokeblock_iseq_not_inlined);
+ return None;
+ }
+
let comptime_captured = unsafe { ((comptime_handler.0 & !0x3) as *const rb_captured_block).as_ref().unwrap() };
let comptime_iseq = unsafe { *comptime_captured.code.iseq.as_ref() };
- asm.comment("guard known ISEQ");
+ asm_comment!(asm, "guard known ISEQ");
let captured_opnd = asm.and(block_handler_opnd, Opnd::Imm(!0x3));
let iseq_opnd = asm.load(Opnd::mem(64, captured_opnd, SIZEOF_VALUE_I32 * 2));
- asm.cmp(iseq_opnd, (comptime_iseq as usize).into());
+ asm.cmp(iseq_opnd, VALUE::from(comptime_iseq).into());
jit_chain_guard(
JCC_JNE,
jit,
asm,
ocb,
- SEND_MAX_CHAIN_DEPTH,
- Counter::invokeblock_iseq_block_changed,
+ SEND_MAX_DEPTH,
+ Counter::guard_invokeblock_iseq_block_changed,
);
- gen_send_iseq(
- jit,
- asm,
- ocb,
- comptime_iseq,
- ci,
- VM_FRAME_MAGIC_BLOCK,
- None,
- 0 as _,
- None,
- flags,
- argc,
- Some(captured_opnd),
- )
+ perf_call! { gen_send_iseq(jit, asm, ocb, comptime_iseq, ci, VM_FRAME_MAGIC_BLOCK, None, 0 as _, None, flags, argc, Some(captured_opnd)) }
} else if comptime_handler.0 & 0x3 == 0x3 { // VM_BH_IFUNC_P
// We aren't handling CALLER_SETUP_ARG and CALLER_REMOVE_EMPTY_KW_SPLAT yet.
if flags & VM_CALL_ARGS_SPLAT != 0 {
@@ -7015,13 +9090,13 @@ fn gen_invokeblock(
return None;
}
- asm.comment("get local EP");
+ asm_comment!(asm, "get local EP");
let ep_opnd = gen_get_lep(jit, asm);
let block_handler_opnd = asm.load(
Opnd::mem(64, ep_opnd, SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_SPECVAL)
);
- asm.comment("guard block_handler type");
+ asm_comment!(asm, "guard block_handler type");
let tag_opnd = asm.and(block_handler_opnd, 0x3.into()); // block_handler is a tagged pointer
asm.cmp(tag_opnd, 0x3.into()); // VM_BH_IFUNC_P
jit_chain_guard(
@@ -7029,19 +9104,19 @@ fn gen_invokeblock(
jit,
asm,
ocb,
- SEND_MAX_CHAIN_DEPTH,
- Counter::invokeblock_tag_changed,
+ SEND_MAX_DEPTH,
+ Counter::guard_invokeblock_tag_changed,
);
// The cfunc may not be leaf
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_non_leaf_call(jit, asm);
extern "C" {
fn rb_vm_yield_with_cfunc(ec: EcPtr, captured: *const rb_captured_block, argc: c_int, argv: *const VALUE) -> VALUE;
}
- asm.comment("call ifunc");
+ asm_comment!(asm, "call ifunc");
let captured_opnd = asm.and(block_handler_opnd, Opnd::Imm(!0x3));
- let argv = asm.lea(asm.ctx.sp_opnd((-argc * SIZEOF_VALUE_I32) as isize));
+ let argv = asm.lea(asm.ctx.sp_opnd(-argc));
let ret = asm.ccall(
rb_vm_yield_with_cfunc as *const u8,
vec![EC, captured_opnd, argc.into(), argv],
@@ -7052,7 +9127,7 @@ fn gen_invokeblock(
asm.mov(stack_ret, ret);
// cfunc calls may corrupt types
- asm.ctx.clear_local_types();
+ asm.clear_local_types();
// Share the successor with other chains
jump_to_next_insn(jit, asm, ocb);
@@ -7071,17 +9146,53 @@ fn gen_invokesuper(
asm: &mut Assembler,
ocb: &mut OutlinedCb,
) -> Option<CodegenStatus> {
- let cd: *const rb_call_data = jit.get_arg(0).as_ptr();
- let block: Option<IseqPtr> = jit.get_arg(1).as_optional_ptr();
+ // Generate specialized code if possible
+ let cd = jit.get_arg(0).as_ptr();
+ if let Some(status) = gen_invokesuper_specialized(jit, asm, ocb, cd) {
+ return Some(status);
+ }
+ // Otherwise, fallback to dynamic dispatch using the interpreter's implementation of send
+ let blockiseq = jit.get_arg(1).as_iseq();
+ gen_send_dynamic(jit, asm, ocb, cd, unsafe { rb_yjit_sendish_sp_pops((*cd).ci) }, |asm| {
+ extern "C" {
+ fn rb_vm_invokesuper(ec: EcPtr, cfp: CfpPtr, cd: VALUE, blockiseq: IseqPtr) -> VALUE;
+ }
+ asm.ccall(
+ rb_vm_invokesuper as *const u8,
+ vec![EC, CFP, (cd as usize).into(), VALUE(blockiseq as usize).into()],
+ )
+ })
+}
+
+fn gen_invokesuper_specialized(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ ocb: &mut OutlinedCb,
+ cd: *const rb_call_data,
+) -> Option<CodegenStatus> {
// Defer compilation so we can specialize on class of receiver
if !jit.at_current_insn() {
defer_compilation(jit, asm, ocb);
return Some(EndBlock);
}
+ // Handle the last two branches of vm_caller_setup_arg_block
+ let block = if let Some(iseq) = jit.get_arg(1).as_optional_ptr() {
+ BlockHandler::BlockISeq(iseq)
+ } else {
+ BlockHandler::LEPSpecVal
+ };
+
+ // Fallback to dynamic dispatch if this callsite is megamorphic
+ if asm.ctx.get_chain_depth() >= SEND_MAX_DEPTH {
+ gen_counter_incr(asm, Counter::invokesuper_megamorphic);
+ return None;
+ }
+
let me = unsafe { rb_vm_frame_method_entry(jit.get_cfp()) };
if me.is_null() {
+ gen_counter_incr(asm, Counter::invokesuper_no_me);
return None;
}
@@ -7094,6 +9205,7 @@ fn gen_invokesuper(
if current_defined_class.builtin_type() == RUBY_T_ICLASS
&& unsafe { RB_TYPE_P((*rbasic_ptr).klass, RUBY_T_MODULE) && FL_TEST_RAW((*rbasic_ptr).klass, VALUE(RMODULE_IS_REFINEMENT.as_usize())) != VALUE(0) }
{
+ gen_counter_incr(asm, Counter::invokesuper_refinement);
return None;
}
let comptime_superclass =
@@ -7108,15 +9220,11 @@ fn gen_invokesuper(
// Note, not using VM_CALL_ARGS_SIMPLE because sometimes we pass a block.
if ci_flags & VM_CALL_KWARG != 0 {
- gen_counter_incr(asm, Counter::send_keywords);
+ gen_counter_incr(asm, Counter::invokesuper_kwarg);
return None;
}
if ci_flags & VM_CALL_KW_SPLAT != 0 {
- gen_counter_incr(asm, Counter::send_kw_splat);
- return None;
- }
- if ci_flags & VM_CALL_ARGS_BLOCKARG != 0 {
- gen_counter_incr(asm, Counter::send_block_arg);
+ gen_counter_incr(asm, Counter::invokesuper_kw_splat);
return None;
}
@@ -7127,13 +9235,20 @@ fn gen_invokesuper(
// check and side exit.
let comptime_recv = jit.peek_at_stack(&asm.ctx, argc as isize);
if unsafe { rb_obj_is_kind_of(comptime_recv, current_defined_class) } == VALUE(0) {
+ gen_counter_incr(asm, Counter::invokesuper_defined_class_mismatch);
+ return None;
+ }
+
+ // Don't compile `super` on objects with singleton class to avoid retaining the receiver.
+ if VALUE(0) != unsafe { FL_TEST(comptime_recv.class_of(), VALUE(RUBY_FL_SINGLETON as usize)) } {
+ gen_counter_incr(asm, Counter::invokesuper_singleton_class);
return None;
}
// Do method lookup
let cme = unsafe { rb_callable_method_entry(comptime_superclass, mid) };
-
if cme.is_null() {
+ gen_counter_incr(asm, Counter::invokesuper_no_cme);
return None;
}
@@ -7141,10 +9256,11 @@ fn gen_invokesuper(
let cme_def_type = unsafe { get_cme_def_type(cme) };
if cme_def_type != VM_METHOD_TYPE_ISEQ && cme_def_type != VM_METHOD_TYPE_CFUNC {
// others unimplemented
+ gen_counter_incr(asm, Counter::invokesuper_not_iseq_or_cfunc);
return None;
}
- asm.comment("guard known me");
+ asm_comment!(asm, "guard known me");
let lep_opnd = gen_get_lep(jit, asm);
let ep_me_opnd = Opnd::mem(
64,
@@ -7154,24 +9270,14 @@ fn gen_invokesuper(
let me_as_value = VALUE(me as usize);
asm.cmp(ep_me_opnd, me_as_value.into());
- asm.jne(Target::side_exit(Counter::invokesuper_me_changed));
-
- if block.is_none() {
- // Guard no block passed
- // rb_vm_frame_block_handler(GET_EC()->cfp) == VM_BLOCK_HANDLER_NONE
- // note, we assume VM_ASSERT(VM_ENV_LOCAL_P(ep))
- //
- // TODO: this could properly forward the current block handler, but
- // would require changes to gen_send_*
- asm.comment("guard no block given");
- let ep_specval_opnd = Opnd::mem(
- 64,
- lep_opnd,
- SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_SPECVAL,
- );
- asm.cmp(ep_specval_opnd, VM_BLOCK_HANDLER_NONE.into());
- asm.jne(Target::side_exit(Counter::invokesuper_block));
- }
+ jit_chain_guard(
+ JCC_JNE,
+ jit,
+ asm,
+ ocb,
+ SEND_MAX_DEPTH,
+ Counter::guard_invokesuper_me_changed,
+ );
// We need to assume that both our current method entry and the super
// method entry we invoke remain stable
@@ -7179,16 +9285,16 @@ fn gen_invokesuper(
jit.assume_method_lookup_stable(asm, ocb, cme);
// Method calls may corrupt types
- asm.ctx.clear_local_types();
+ asm.clear_local_types();
match cme_def_type {
VM_METHOD_TYPE_ISEQ => {
let iseq = unsafe { get_def_iseq_ptr((*cme).def) };
let frame_type = VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL;
- gen_send_iseq(jit, asm, ocb, iseq, ci, frame_type, None, cme, block, ci_flags, argc, None)
+ perf_call! { gen_send_iseq(jit, asm, ocb, iseq, ci, frame_type, None, cme, Some(block), ci_flags, argc, None) }
}
VM_METHOD_TYPE_CFUNC => {
- gen_send_cfunc(jit, asm, ocb, ci, cme, block, ptr::null(), ci_flags, argc)
+ perf_call! { gen_send_cfunc(jit, asm, ocb, ci, cme, Some(block), None, ci_flags, argc) }
}
_ => unreachable!(),
}
@@ -7197,20 +9303,17 @@ fn gen_invokesuper(
fn gen_leave(
_jit: &mut JITState,
asm: &mut Assembler,
- ocb: &mut OutlinedCb,
+ _ocb: &mut OutlinedCb,
) -> Option<CodegenStatus> {
// Only the return value should be on the stack
- assert_eq!(1, asm.ctx.get_stack_size());
-
- let ocb_asm = Assembler::new();
+ assert_eq!(1, asm.ctx.get_stack_size(), "leave instruction expects stack size 1, but was: {}", asm.ctx.get_stack_size());
// Check for interrupts
gen_check_ints(asm, Counter::leave_se_interrupt);
- ocb_asm.compile(ocb.unwrap(), None);
// Pop the current frame (ec->cfp++)
// Note: the return PC is already in the previous CFP
- asm.comment("pop stack frame");
+ asm_comment!(asm, "pop stack frame");
let incr_cfp = asm.add(CFP, RUBY_SIZEOF_CONTROL_FRAME.into());
asm.mov(CFP, incr_cfp);
asm.mov(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), CFP);
@@ -7218,15 +9321,15 @@ fn gen_leave(
// Load the return value
let retval_opnd = asm.stack_pop(1);
- // Move the return value into the C return register for gen_leave_exit()
+ // Move the return value into the C return register
asm.mov(C_RET_OPND, retval_opnd);
- // Reload REG_SP for the caller and write the return value.
- // Top of the stack is REG_SP[0] since the caller has sp_offset=1.
- asm.mov(SP, Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SP));
- asm.mov(Opnd::mem(64, SP, 0), C_RET_OPND);
-
- // Jump to the JIT return address on the frame that was just popped
+ // Jump to the JIT return address on the frame that was just popped.
+ // There are a few possible jump targets:
+ // - gen_leave_exit() and gen_leave_exception(), for C callers
+ // - Return context set up by gen_send_iseq()
+ // We don't write the return value to stack memory like the interpreter here.
+ // Each jump target do it as necessary.
let offset_to_jit_return =
-(RUBY_SIZEOF_CONTROL_FRAME as i32) + RUBY_OFFSET_CFP_JIT_RETURN;
asm.jmp_opnd(Opnd::mem(64, CFP, offset_to_jit_return));
@@ -7242,7 +9345,7 @@ fn gen_getglobal(
let gid = jit.get_arg(0).as_usize();
// Save the PC and SP because we might make a Ruby call for warning
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_non_leaf_call(jit, asm);
let val_opnd = asm.ccall(
rb_gvar_get as *const u8,
@@ -7264,9 +9367,9 @@ fn gen_setglobal(
// Save the PC and SP because we might make a Ruby call for
// Kernel#set_trace_var
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_non_leaf_call(jit, asm);
- let val = asm.stack_pop(1);
+ let val = asm.stack_opnd(0);
asm.ccall(
rb_gvar_set as *const u8,
vec![
@@ -7274,6 +9377,7 @@ fn gen_setglobal(
val,
],
);
+ asm.stack_pop(1); // Keep it during ccall for GC
Some(KeepCompiling)
}
@@ -7284,12 +9388,13 @@ fn gen_anytostring(
_ocb: &mut OutlinedCb,
) -> Option<CodegenStatus> {
// Save the PC and SP since we might call #to_s
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_non_leaf_call(jit, asm);
- let str = asm.stack_pop(1);
- let val = asm.stack_pop(1);
+ let str = asm.stack_opnd(0);
+ let val = asm.stack_opnd(1);
let val = asm.ccall(rb_obj_as_string_result as *const u8, vec![str, val]);
+ asm.stack_pop(2); // Keep them during ccall for GC
// Push the return value
let stack_ret = asm.stack_push(Type::TString);
@@ -7328,7 +9433,7 @@ fn gen_objtostring(
Some(KeepCompiling)
} else {
let cd = jit.get_arg(0).as_ptr();
- gen_send_general(jit, asm, ocb, cd, None)
+ perf_call! { gen_send_general(jit, asm, ocb, cd, None) }
}
}
@@ -7338,10 +9443,11 @@ fn gen_intern(
_ocb: &mut OutlinedCb,
) -> Option<CodegenStatus> {
// Save the PC and SP because we might allocate
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_call_with_gc(jit, asm);
- let str = asm.stack_pop(1);
+ let str = asm.stack_opnd(0);
let sym = asm.ccall(rb_str_intern as *const u8, vec![str]);
+ asm.stack_pop(1); // Keep it during ccall for GC
// Push the return value
let stack_ret = asm.stack_push(Type::Unknown);
@@ -7360,10 +9466,9 @@ fn gen_toregexp(
// Save the PC and SP because this allocates an object and could
// raise an exception.
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_non_leaf_call(jit, asm);
- let values_ptr = asm.lea(asm.ctx.sp_opnd(-((SIZEOF_VALUE as isize) * (cnt as isize))));
- asm.stack_pop(cnt);
+ let values_ptr = asm.lea(asm.ctx.sp_opnd(-(cnt as i32)));
let ary = asm.ccall(
rb_ary_tmp_new_from_values as *const u8,
@@ -7373,6 +9478,7 @@ fn gen_toregexp(
values_ptr,
]
);
+ asm.stack_pop(cnt); // Let ccall spill them
// Save the array so we can clear it later
asm.cpush(ary);
@@ -7396,7 +9502,6 @@ fn gen_toregexp(
asm.mov(stack_ret, val);
// Clear the temp array.
- asm.spill_temps(); // for ccall
asm.ccall(rb_ary_clear as *const u8, vec![ary]);
Some(KeepCompiling)
@@ -7420,28 +9525,28 @@ fn gen_getspecial(
// Fetch a "special" backref based on a char encoded by shifting by 1
// Can raise if matchdata uninitialized
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_non_leaf_call(jit, asm);
// call rb_backref_get()
- asm.comment("rb_backref_get");
+ asm_comment!(asm, "rb_backref_get");
let backref = asm.ccall(rb_backref_get as *const u8, vec![]);
let rt_u8: u8 = (rtype >> 1).try_into().unwrap();
let val = match rt_u8.into() {
'&' => {
- asm.comment("rb_reg_last_match");
+ asm_comment!(asm, "rb_reg_last_match");
asm.ccall(rb_reg_last_match as *const u8, vec![backref])
}
'`' => {
- asm.comment("rb_reg_match_pre");
+ asm_comment!(asm, "rb_reg_match_pre");
asm.ccall(rb_reg_match_pre as *const u8, vec![backref])
}
'\'' => {
- asm.comment("rb_reg_match_post");
+ asm_comment!(asm, "rb_reg_match_post");
asm.ccall(rb_reg_match_post as *const u8, vec![backref])
}
'+' => {
- asm.comment("rb_reg_match_last");
+ asm_comment!(asm, "rb_reg_match_last");
asm.ccall(rb_reg_match_last as *const u8, vec![backref])
}
_ => panic!("invalid back-ref"),
@@ -7455,14 +9560,14 @@ fn gen_getspecial(
// Fetch the N-th match from the last backref based on type shifted by 1
// Can raise if matchdata uninitialized
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_non_leaf_call(jit, asm);
// call rb_backref_get()
- asm.comment("rb_backref_get");
+ asm_comment!(asm, "rb_backref_get");
let backref = asm.ccall(rb_backref_get as *const u8, vec![]);
// rb_reg_nth_match((int)(type >> 1), backref);
- asm.comment("rb_reg_nth_match");
+ asm_comment!(asm, "rb_reg_nth_match");
let val = asm.ccall(
rb_reg_nth_match as *const u8,
vec![
@@ -7484,7 +9589,7 @@ fn gen_getclassvariable(
_ocb: &mut OutlinedCb,
) -> Option<CodegenStatus> {
// rb_vm_getclassvariable can raise exceptions.
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_non_leaf_call(jit, asm);
let val_opnd = asm.ccall(
rb_vm_getclassvariable as *const u8,
@@ -7508,9 +9613,9 @@ fn gen_setclassvariable(
_ocb: &mut OutlinedCb,
) -> Option<CodegenStatus> {
// rb_vm_setclassvariable can raise exceptions.
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_non_leaf_call(jit, asm);
- let val = asm.stack_pop(1);
+ let val = asm.stack_opnd(0);
asm.ccall(
rb_vm_setclassvariable as *const u8,
vec![
@@ -7521,6 +9626,7 @@ fn gen_setclassvariable(
Opnd::UImm(jit.get_arg(1).as_u64()),
],
);
+ asm.stack_pop(1); // Keep it during ccall for GC
Some(KeepCompiling)
}
@@ -7534,10 +9640,10 @@ fn gen_getconstant(
let id = jit.get_arg(0).as_usize();
// vm_get_ev_const can raise exceptions.
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_non_leaf_call(jit, asm);
- let allow_nil_opnd = asm.stack_pop(1);
- let klass_opnd = asm.stack_pop(1);
+ let allow_nil_opnd = asm.stack_opnd(0);
+ let klass_opnd = asm.stack_opnd(1);
extern "C" {
fn rb_vm_get_ev_const(ec: EcPtr, klass: VALUE, id: ID, allow_nil: VALUE) -> VALUE;
@@ -7552,6 +9658,7 @@ fn gen_getconstant(
allow_nil_opnd
],
);
+ asm.stack_pop(2); // Keep them during ccall for GC
let top = asm.stack_push(Type::Unknown);
asm.mov(top, val_opnd);
@@ -7568,24 +9675,38 @@ fn gen_opt_getconstant_path(
let ic: *const iseq_inline_constant_cache = const_cache_as_value.as_ptr();
let idlist: *const ID = unsafe { (*ic).segments };
+ // Make sure there is an exit for this block as the interpreter might want
+ // to invalidate this block from yjit_constant_ic_update().
+ jit_ensure_block_entry_exit(jit, asm, ocb)?;
+
// See vm_ic_hit_p(). The same conditions are checked in yjit_constant_ic_update().
+ // If a cache is not filled, fallback to the general C call.
let ice = unsafe { (*ic).entry };
if ice.is_null() {
- // In this case, leave a block that unconditionally side exits
- // for the interpreter to invalidate.
- return None;
- }
+ // Prepare for const_missing
+ jit_prepare_non_leaf_call(jit, asm);
- // Make sure there is an exit for this block as the interpreter might want
- // to invalidate this block from yjit_constant_ic_update().
- jit_ensure_block_entry_exit(jit, asm, ocb);
+ // If this does not trigger const_missing, vm_ic_update will invalidate this block.
+ extern "C" {
+ fn rb_vm_opt_getconstant_path(ec: EcPtr, cfp: CfpPtr, ic: *const u8) -> VALUE;
+ }
+ let val = asm.ccall(
+ rb_vm_opt_getconstant_path as *const u8,
+ vec![EC, CFP, Opnd::const_ptr(ic as *const u8)],
+ );
+
+ let stack_top = asm.stack_push(Type::Unknown);
+ asm.store(stack_top, val);
+
+ jump_to_next_insn(jit, asm, ocb);
+ return Some(EndBlock);
+ }
if !unsafe { (*ice).ic_cref }.is_null() {
// Cache is keyed on a certain lexical scope. Use the interpreter's cache.
let inline_cache = asm.load(Opnd::const_ptr(ic as *const u8));
// Call function to verify the cache. It doesn't allocate or call methods.
- asm.spill_temps(); // for ccall
let ret_val = asm.ccall(
rb_vm_ic_hit_p as *const u8,
vec![inline_cache, Opnd::mem(64, CFP, RUBY_OFFSET_CFP_EP)]
@@ -7594,7 +9715,7 @@ fn gen_opt_getconstant_path(
// Check the result. SysV only specifies one byte for _Bool return values,
// so it's important we only check one bit to ignore the higher bits in the register.
asm.test(ret_val, 1.into());
- asm.jz(Target::side_exit(Counter::opt_getinlinecache_miss));
+ asm.jz(Target::side_exit(Counter::opt_getconstant_path_ic_miss));
let inline_cache = asm.load(Opnd::const_ptr(ic as *const u8));
@@ -7616,6 +9737,7 @@ fn gen_opt_getconstant_path(
} else {
// Optimize for single ractor mode.
if !assume_single_ractor_mode(jit, asm, ocb) {
+ gen_counter_incr(asm, Counter::opt_getconstant_path_multi_ractor);
return None;
}
@@ -7643,18 +9765,20 @@ fn gen_getblockparamproxy(
return Some(EndBlock);
}
- // A mirror of the interpreter code. Checking for the case
- // where it's pushing rb_block_param_proxy.
-
// EP level
let level = jit.get_arg(1).as_u32();
// Peek at the block handler so we can check whether it's nil
let comptime_handler = jit.peek_at_block_handler(level);
- // When a block handler is present, it should always be a GC-guarded
- // pointer (VM_BH_ISEQ_BLOCK_P)
- if comptime_handler.as_u64() != 0 && comptime_handler.as_u64() & 0x3 != 0x1 {
+ // Filter for the 4 cases we currently handle
+ if !(comptime_handler.as_u64() == 0 || // no block given
+ comptime_handler.as_u64() & 0x3 == 0x1 || // iseq block (no associated GC managed object)
+ comptime_handler.as_u64() & 0x3 == 0x3 || // ifunc block (no associated GC managed object)
+ unsafe { rb_obj_is_proc(comptime_handler) }.test() // block is a Proc
+ ) {
+ // Missing the symbol case, where we basically need to call Symbol#to_proc at runtime
+ gen_counter_incr(asm, Counter::gbpp_unsupported_type);
return None;
}
@@ -7676,7 +9800,12 @@ fn gen_getblockparamproxy(
Opnd::mem(64, ep_opnd, SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_SPECVAL)
);
- // Specialize compilation for the case where no block handler is present
+ // Use block handler sample to guide specialization...
+ // NOTE: we use jit_chain_guard() in this decision tree, and since
+ // there are only a few cases, it should never reach the depth limit use
+ // the exit counter we pass to it.
+ //
+ // No block given
if comptime_handler.as_u64() == 0 {
// Bail if there is a block handler
asm.cmp(block_handler, Opnd::UImm(0));
@@ -7691,15 +9820,18 @@ fn gen_getblockparamproxy(
);
jit_putobject(asm, Qnil);
- } else {
- // Block handler is a tagged pointer. Look at the tag. 0x03 is from VM_BH_ISEQ_BLOCK_P().
- let block_handler = asm.and(block_handler, 0x3.into());
-
- // Bail unless VM_BH_ISEQ_BLOCK_P(bh). This also checks for null.
- asm.cmp(block_handler, 0x1.into());
-
+ } else if comptime_handler.as_u64() & 0x1 == 0x1 {
+ // This handles two cases which are nearly identical
+ // Block handler is a tagged pointer. Look at the tag.
+ // VM_BH_ISEQ_BLOCK_P(): block_handler & 0x03 == 0x01
+ // VM_BH_IFUNC_P(): block_handler & 0x03 == 0x03
+ // So to check for either of those cases we can use: val & 0x1 == 0x1
+ const _: () = assert!(RUBY_SYMBOL_FLAG & 1 == 0, "guard below rejects symbol block handlers");
+ // Procs are aligned heap pointers so testing the bit rejects them too.
+
+ asm.test(block_handler, 0x1.into());
jit_chain_guard(
- JCC_JNZ,
+ JCC_JZ,
jit,
asm,
ocb,
@@ -7712,6 +9844,39 @@ fn gen_getblockparamproxy(
let top = asm.stack_push(Type::BlockParamProxy);
asm.mov(top, Opnd::const_ptr(unsafe { rb_block_param_proxy }.as_ptr()));
+ } else if unsafe { rb_obj_is_proc(comptime_handler) }.test() {
+ // The block parameter is a Proc
+ c_callable! {
+ // We can't hold values across C calls due to a backend limitation,
+ // so we'll use this thin wrapper around rb_obj_is_proc().
+ fn is_proc(object: VALUE) -> VALUE {
+ if unsafe { rb_obj_is_proc(object) }.test() {
+ // VM_BH_TO_PROC() is the identify function.
+ object
+ } else {
+ Qfalse
+ }
+ }
+ }
+
+ // Simple predicate, no need to jit_prepare_non_leaf_call()
+ let proc_or_false = asm.ccall(is_proc as _, vec![block_handler]);
+
+ // Guard for proc
+ asm.cmp(proc_or_false, Qfalse.into());
+ jit_chain_guard(
+ JCC_JE,
+ jit,
+ asm,
+ ocb,
+ SEND_MAX_DEPTH,
+ Counter::gbpp_block_handler_not_proc,
+ );
+
+ let top = asm.stack_push(Type::Unknown);
+ asm.mov(top, proc_or_false);
+ } else {
+ unreachable!("absurd given initial filtering");
}
jump_to_next_insn(jit, asm, ocb);
@@ -7728,7 +9893,8 @@ fn gen_getblockparam(
let level = jit.get_arg(1).as_u32();
// Save the PC and SP because we might allocate
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_call_with_gc(jit, asm);
+ asm.spill_temps(); // For ccall. Unconditionally spill them for RegTemps consistency.
// A mirror of the interpreter code. Checking for the case
// where it's pushing rb_block_param_proxy.
@@ -7810,11 +9976,12 @@ fn gen_invokebuiltin(
// ec, self, and arguments
if bf_argc + 2 > C_ARG_OPNDS.len() {
+ incr_counter!(invokebuiltin_too_many_args);
return None;
}
// If the calls don't allocate, do they need up to date PC, SP?
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_non_leaf_call(jit, asm);
// Call the builtin func (ec, recv, arg1, arg2, ...)
let mut args = vec![EC, Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF)];
@@ -7849,11 +10016,12 @@ fn gen_opt_invokebuiltin_delegate(
// ec, self, and arguments
if bf_argc + 2 > (C_ARG_OPNDS.len() as i32) {
+ incr_counter!(invokebuiltin_too_many_args);
return None;
}
// If the calls don't allocate, do they need up to date PC, SP?
- jit_prepare_routine_call(jit, asm);
+ jit_prepare_non_leaf_call(jit, asm);
// Call the builtin func (ec, recv, arg1, arg2, ...)
let mut args = vec![EC, Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF)];
@@ -7925,12 +10093,17 @@ fn get_gen_fn(opcode: VALUE) -> Option<InsnGenFn> {
YARVINSN_opt_str_uminus => Some(gen_opt_str_uminus),
YARVINSN_opt_newarray_send => Some(gen_opt_newarray_send),
YARVINSN_splatarray => Some(gen_splatarray),
+ YARVINSN_splatkw => Some(gen_splatkw),
YARVINSN_concatarray => Some(gen_concatarray),
+ YARVINSN_concattoarray => Some(gen_concattoarray),
+ YARVINSN_pushtoarray => Some(gen_pushtoarray),
YARVINSN_newrange => Some(gen_newrange),
YARVINSN_putstring => Some(gen_putstring),
+ YARVINSN_putchilledstring => Some(gen_putchilledstring),
YARVINSN_expandarray => Some(gen_expandarray),
YARVINSN_defined => Some(gen_defined),
YARVINSN_definedivar => Some(gen_definedivar),
+ YARVINSN_checkmatch => Some(gen_checkmatch),
YARVINSN_checkkeyword => Some(gen_checkkeyword),
YARVINSN_concatstrings => Some(gen_concatstrings),
YARVINSN_getinstancevariable => Some(gen_getinstancevariable),
@@ -7940,6 +10113,7 @@ fn get_gen_fn(opcode: VALUE) -> Option<InsnGenFn> {
YARVINSN_opt_neq => Some(gen_opt_neq),
YARVINSN_opt_aref => Some(gen_opt_aref),
YARVINSN_opt_aset => Some(gen_opt_aset),
+ YARVINSN_opt_aref_with => Some(gen_opt_aref_with),
YARVINSN_opt_mult => Some(gen_opt_mult),
YARVINSN_opt_div => Some(gen_opt_div),
YARVINSN_opt_ltlt => Some(gen_opt_ltlt),
@@ -7986,7 +10160,7 @@ fn get_gen_fn(opcode: VALUE) -> Option<InsnGenFn> {
}
// Return true when the codegen function generates code.
-// known_recv_klass is non-NULL when the caller has used jit_guard_known_klass().
+// known_recv_class has Some value when the caller has used jit_guard_known_klass().
// See yjit_reg_method().
type MethodGenFn = fn(
jit: &mut JITState,
@@ -7994,11 +10168,110 @@ type MethodGenFn = fn(
ocb: &mut OutlinedCb,
ci: *const rb_callinfo,
cme: *const rb_callable_method_entry_t,
- block: Option<IseqPtr>,
+ block: Option<BlockHandler>,
argc: i32,
- known_recv_class: *const VALUE,
+ known_recv_class: Option<VALUE>,
) -> bool;
+/// Methods for generating code for hardcoded (usually C) methods
+static mut METHOD_CODEGEN_TABLE: Option<HashMap<usize, MethodGenFn>> = None;
+
+/// Register codegen functions for some Ruby core methods
+pub fn yjit_reg_method_codegen_fns() {
+ unsafe {
+ assert!(METHOD_CODEGEN_TABLE.is_none());
+ METHOD_CODEGEN_TABLE = Some(HashMap::default());
+
+ // Specialization for C methods. See yjit_reg_method() for details.
+ yjit_reg_method(rb_cBasicObject, "!", jit_rb_obj_not);
+
+ yjit_reg_method(rb_cNilClass, "nil?", jit_rb_true);
+ yjit_reg_method(rb_mKernel, "nil?", jit_rb_false);
+ yjit_reg_method(rb_mKernel, "is_a?", jit_rb_kernel_is_a);
+ yjit_reg_method(rb_mKernel, "kind_of?", jit_rb_kernel_is_a);
+ yjit_reg_method(rb_mKernel, "instance_of?", jit_rb_kernel_instance_of);
+
+ yjit_reg_method(rb_cBasicObject, "==", jit_rb_obj_equal);
+ yjit_reg_method(rb_cBasicObject, "equal?", jit_rb_obj_equal);
+ yjit_reg_method(rb_cBasicObject, "!=", jit_rb_obj_not_equal);
+ yjit_reg_method(rb_mKernel, "eql?", jit_rb_obj_equal);
+ yjit_reg_method(rb_cModule, "==", jit_rb_obj_equal);
+ yjit_reg_method(rb_cModule, "===", jit_rb_mod_eqq);
+ yjit_reg_method(rb_cSymbol, "==", jit_rb_obj_equal);
+ yjit_reg_method(rb_cSymbol, "===", jit_rb_obj_equal);
+ yjit_reg_method(rb_cInteger, "==", jit_rb_int_equal);
+ yjit_reg_method(rb_cInteger, "===", jit_rb_int_equal);
+
+ yjit_reg_method(rb_cInteger, "succ", jit_rb_int_succ);
+ yjit_reg_method(rb_cInteger, "/", jit_rb_int_div);
+ yjit_reg_method(rb_cInteger, "<<", jit_rb_int_lshift);
+ yjit_reg_method(rb_cInteger, ">>", jit_rb_int_rshift);
+ yjit_reg_method(rb_cInteger, "^", jit_rb_int_xor);
+ yjit_reg_method(rb_cInteger, "[]", jit_rb_int_aref);
+
+ yjit_reg_method(rb_cFloat, "+", jit_rb_float_plus);
+ yjit_reg_method(rb_cFloat, "-", jit_rb_float_minus);
+ yjit_reg_method(rb_cFloat, "*", jit_rb_float_mul);
+ yjit_reg_method(rb_cFloat, "/", jit_rb_float_div);
+
+ yjit_reg_method(rb_cString, "empty?", jit_rb_str_empty_p);
+ yjit_reg_method(rb_cString, "to_s", jit_rb_str_to_s);
+ yjit_reg_method(rb_cString, "to_str", jit_rb_str_to_s);
+ yjit_reg_method(rb_cString, "length", jit_rb_str_length);
+ yjit_reg_method(rb_cString, "size", jit_rb_str_length);
+ yjit_reg_method(rb_cString, "bytesize", jit_rb_str_bytesize);
+ yjit_reg_method(rb_cString, "getbyte", jit_rb_str_getbyte);
+ yjit_reg_method(rb_cString, "setbyte", jit_rb_str_setbyte);
+ yjit_reg_method(rb_cString, "byteslice", jit_rb_str_byteslice);
+ yjit_reg_method(rb_cString, "<<", jit_rb_str_concat);
+ yjit_reg_method(rb_cString, "+@", jit_rb_str_uplus);
+
+ yjit_reg_method(rb_cNilClass, "===", jit_rb_case_equal);
+ yjit_reg_method(rb_cTrueClass, "===", jit_rb_case_equal);
+ yjit_reg_method(rb_cFalseClass, "===", jit_rb_case_equal);
+
+ yjit_reg_method(rb_cArray, "empty?", jit_rb_ary_empty_p);
+ yjit_reg_method(rb_cArray, "length", jit_rb_ary_length);
+ yjit_reg_method(rb_cArray, "size", jit_rb_ary_length);
+ yjit_reg_method(rb_cArray, "<<", jit_rb_ary_push);
+
+ yjit_reg_method(rb_cHash, "empty?", jit_rb_hash_empty_p);
+
+ yjit_reg_method(rb_mKernel, "respond_to?", jit_obj_respond_to);
+ yjit_reg_method(rb_mKernel, "block_given?", jit_rb_f_block_given_p);
+
+ yjit_reg_method(rb_cClass, "superclass", jit_rb_class_superclass);
+
+ yjit_reg_method(rb_singleton_class(rb_cThread), "current", jit_thread_s_current);
+ }
+}
+
+// Register a specialized codegen function for a particular method. Note that
+// the if the function returns true, the code it generates runs without a
+// control frame and without interrupt checks. To avoid creating observable
+// behavior changes, the codegen function should only target simple code paths
+// that do not allocate and do not make method calls.
+fn yjit_reg_method(klass: VALUE, mid_str: &str, gen_fn: MethodGenFn) {
+ let id_string = std::ffi::CString::new(mid_str).expect("couldn't convert to CString!");
+ let mid = unsafe { rb_intern(id_string.as_ptr()) };
+ let me = unsafe { rb_method_entry_at(klass, mid) };
+
+ if me.is_null() {
+ panic!("undefined optimized method!: {mid_str}");
+ }
+
+ // For now, only cfuncs are supported
+ //RUBY_ASSERT(me && me->def);
+ //RUBY_ASSERT(me->def->type == VM_METHOD_TYPE_CFUNC);
+
+ let method_serial = unsafe {
+ let def = (*me).def;
+ get_def_method_serial(def)
+ };
+
+ unsafe { METHOD_CODEGEN_TABLE.as_mut().unwrap().insert(method_serial, gen_fn); }
+}
+
/// Global state needed for code generation
pub struct CodegenGlobals {
/// Inline code block (fast path)
@@ -8010,8 +10283,11 @@ pub struct CodegenGlobals {
/// Code for exiting back to the interpreter from the leave instruction
leave_exit_code: CodePtr,
+ /// Code for exiting back to the interpreter after handling an exception
+ leave_exception_code: CodePtr,
+
// For exiting from YJIT frame from branch_stub_hit().
- // Filled by gen_code_for_exit_from_stub().
+ // Filled by gen_stub_exit().
stub_exit_code: CodePtr,
// For servicing branch stubs
@@ -8026,14 +10302,12 @@ pub struct CodegenGlobals {
/// For implementing global code invalidation
global_inval_patches: Vec<CodepagePatch>,
- // Methods for generating code for hardcoded (usually C) methods
- method_codegen_table: HashMap<usize, MethodGenFn>,
-
/// Page indexes for outlined code that are not associated to any ISEQ.
ocb_pages: Vec<usize>,
- /// How many times code GC has been executed.
- code_gc_count: usize,
+ /// Map of cfunc YARV PCs to CMEs and receiver indexes, used to lazily push
+ /// a frame when rb_yjit_lazy_push_frame() is called with a PC in this HashMap.
+ pc_to_cfunc: HashMap<*mut VALUE, (*const rb_callable_method_entry_t, u8)>,
}
/// For implementing global code invalidation. A position in the inline
@@ -8053,11 +10327,8 @@ impl CodegenGlobals {
// Executable memory and code page size in bytes
let mem_size = get_option!(exec_mem_size);
-
#[cfg(not(test))]
let (mut cb, mut ocb) = {
- use std::cell::RefCell;
-
let virt_block: *mut u8 = unsafe { rb_yjit_reserve_addr_space(mem_size as u32) };
// Memory protection syscalls need page-aligned addresses, so check it here. Assuming
@@ -8099,15 +10370,16 @@ impl CodegenGlobals {
let mut ocb = OutlinedCb::wrap(CodeBlock::new_dummy(mem_size / 2));
let ocb_start_addr = ocb.unwrap().get_write_ptr();
- let leave_exit_code = gen_leave_exit(&mut ocb);
+ let leave_exit_code = gen_leave_exit(&mut ocb).unwrap();
+ let leave_exception_code = gen_leave_exception(&mut ocb).unwrap();
- let stub_exit_code = gen_code_for_exit_from_stub(&mut ocb);
+ let stub_exit_code = gen_stub_exit(&mut ocb).unwrap();
- let branch_stub_hit_trampoline = gen_branch_stub_hit_trampoline(&mut ocb);
- let entry_stub_hit_trampoline = gen_entry_stub_hit_trampoline(&mut ocb);
+ let branch_stub_hit_trampoline = gen_branch_stub_hit_trampoline(&mut ocb).unwrap();
+ let entry_stub_hit_trampoline = gen_entry_stub_hit_trampoline(&mut ocb).unwrap();
// Generate full exit code for C func
- let cfunc_exit_code = gen_full_cfunc_return(&mut ocb);
+ let cfunc_exit_code = gen_full_cfunc_return(&mut ocb).unwrap();
let ocb_end_addr = ocb.unwrap().get_write_ptr();
let ocb_pages = ocb.unwrap().addrs_to_pages(ocb_start_addr, ocb_end_addr);
@@ -8116,106 +10388,26 @@ impl CodegenGlobals {
cb.mark_all_executable();
ocb.unwrap().mark_all_executable();
- let mut codegen_globals = CodegenGlobals {
+ let codegen_globals = CodegenGlobals {
inline_cb: cb,
outlined_cb: ocb,
leave_exit_code,
- stub_exit_code: stub_exit_code,
+ leave_exception_code,
+ stub_exit_code,
outline_full_cfunc_return_pos: cfunc_exit_code,
branch_stub_hit_trampoline,
entry_stub_hit_trampoline,
global_inval_patches: Vec::new(),
- method_codegen_table: HashMap::new(),
ocb_pages,
- code_gc_count: 0,
+ pc_to_cfunc: HashMap::new(),
};
- // Register the method codegen functions
- codegen_globals.reg_method_codegen_fns();
-
// Initialize the codegen globals instance
unsafe {
CODEGEN_GLOBALS = Some(codegen_globals);
}
}
- // Register a specialized codegen function for a particular method. Note that
- // the if the function returns true, the code it generates runs without a
- // control frame and without interrupt checks. To avoid creating observable
- // behavior changes, the codegen function should only target simple code paths
- // that do not allocate and do not make method calls.
- fn yjit_reg_method(&mut self, klass: VALUE, mid_str: &str, gen_fn: MethodGenFn) {
- let id_string = std::ffi::CString::new(mid_str).expect("couldn't convert to CString!");
- let mid = unsafe { rb_intern(id_string.as_ptr()) };
- let me = unsafe { rb_method_entry_at(klass, mid) };
-
- if me.is_null() {
- panic!("undefined optimized method!");
- }
-
- // For now, only cfuncs are supported
- //RUBY_ASSERT(me && me->def);
- //RUBY_ASSERT(me->def->type == VM_METHOD_TYPE_CFUNC);
-
- let method_serial = unsafe {
- let def = (*me).def;
- get_def_method_serial(def)
- };
-
- self.method_codegen_table.insert(method_serial, gen_fn);
- }
-
- /// Register codegen functions for some Ruby core methods
- fn reg_method_codegen_fns(&mut self) {
- unsafe {
- // Specialization for C methods. See yjit_reg_method() for details.
- self.yjit_reg_method(rb_cBasicObject, "!", jit_rb_obj_not);
-
- self.yjit_reg_method(rb_cNilClass, "nil?", jit_rb_true);
- self.yjit_reg_method(rb_mKernel, "nil?", jit_rb_false);
- self.yjit_reg_method(rb_mKernel, "is_a?", jit_rb_kernel_is_a);
- self.yjit_reg_method(rb_mKernel, "kind_of?", jit_rb_kernel_is_a);
- self.yjit_reg_method(rb_mKernel, "instance_of?", jit_rb_kernel_instance_of);
-
- self.yjit_reg_method(rb_cBasicObject, "==", jit_rb_obj_equal);
- self.yjit_reg_method(rb_cBasicObject, "equal?", jit_rb_obj_equal);
- self.yjit_reg_method(rb_cBasicObject, "!=", jit_rb_obj_not_equal);
- self.yjit_reg_method(rb_mKernel, "eql?", jit_rb_obj_equal);
- self.yjit_reg_method(rb_cModule, "==", jit_rb_obj_equal);
- self.yjit_reg_method(rb_cModule, "===", jit_rb_mod_eqq);
- self.yjit_reg_method(rb_cSymbol, "==", jit_rb_obj_equal);
- self.yjit_reg_method(rb_cSymbol, "===", jit_rb_obj_equal);
- self.yjit_reg_method(rb_cInteger, "==", jit_rb_int_equal);
- self.yjit_reg_method(rb_cInteger, "===", jit_rb_int_equal);
-
- self.yjit_reg_method(rb_cInteger, "*", jit_rb_int_mul);
- self.yjit_reg_method(rb_cInteger, "/", jit_rb_int_div);
- self.yjit_reg_method(rb_cInteger, "[]", jit_rb_int_aref);
-
- // rb_str_to_s() methods in string.c
- self.yjit_reg_method(rb_cString, "empty?", jit_rb_str_empty_p);
- self.yjit_reg_method(rb_cString, "to_s", jit_rb_str_to_s);
- self.yjit_reg_method(rb_cString, "to_str", jit_rb_str_to_s);
- self.yjit_reg_method(rb_cString, "bytesize", jit_rb_str_bytesize);
- self.yjit_reg_method(rb_cString, "<<", jit_rb_str_concat);
- self.yjit_reg_method(rb_cString, "+@", jit_rb_str_uplus);
-
- // rb_ary_empty_p() method in array.c
- self.yjit_reg_method(rb_cArray, "empty?", jit_rb_ary_empty_p);
- self.yjit_reg_method(rb_cArray, "<<", jit_rb_ary_push);
-
- self.yjit_reg_method(rb_mKernel, "respond_to?", jit_obj_respond_to);
- self.yjit_reg_method(rb_mKernel, "block_given?", jit_rb_f_block_given_p);
-
- // Thread.current
- self.yjit_reg_method(
- rb_singleton_class(rb_cThread),
- "current",
- jit_thread_s_current,
- );
- }
- }
-
/// Get a mutable reference to the codegen globals instance
pub fn get_instance() -> &'static mut CodegenGlobals {
unsafe { CODEGEN_GLOBALS.as_mut().unwrap() }
@@ -8239,14 +10431,26 @@ impl CodegenGlobals {
CodegenGlobals::get_instance().leave_exit_code
}
+ pub fn get_leave_exception_code() -> CodePtr {
+ CodegenGlobals::get_instance().leave_exception_code
+ }
+
pub fn get_stub_exit_code() -> CodePtr {
CodegenGlobals::get_instance().stub_exit_code
}
- pub fn push_global_inval_patch(i_pos: CodePtr, o_pos: CodePtr) {
+ pub fn push_global_inval_patch(inline_pos: CodePtr, outlined_pos: CodePtr, cb: &CodeBlock) {
+ if let Some(last_patch) = CodegenGlobals::get_instance().global_inval_patches.last() {
+ let patch_offset = inline_pos.as_offset() - last_patch.inline_patch_pos.as_offset();
+ assert!(
+ patch_offset < 0 || cb.jmp_ptr_bytes() as i64 <= patch_offset,
+ "patches should not overlap (patch_offset: {patch_offset})",
+ );
+ }
+
let patch = CodepagePatch {
- inline_patch_pos: i_pos,
- outlined_target_pos: o_pos,
+ inline_patch_pos: inline_pos,
+ outlined_target_pos: outlined_pos,
};
CodegenGlobals::get_instance()
.global_inval_patches
@@ -8271,26 +10475,12 @@ impl CodegenGlobals {
CodegenGlobals::get_instance().entry_stub_hit_trampoline
}
- pub fn look_up_codegen_method(method_serial: usize) -> Option<MethodGenFn> {
- let table = &CodegenGlobals::get_instance().method_codegen_table;
-
- let option_ref = table.get(&method_serial);
- match option_ref {
- None => None,
- Some(&mgf) => Some(mgf), // Deref
- }
- }
-
pub fn get_ocb_pages() -> &'static Vec<usize> {
&CodegenGlobals::get_instance().ocb_pages
}
- pub fn incr_code_gc_count() {
- CodegenGlobals::get_instance().code_gc_count += 1;
- }
-
- pub fn get_code_gc_count() -> usize {
- CodegenGlobals::get_instance().code_gc_count
+ pub fn get_pc_to_cfunc() -> &'static mut HashMap<*mut VALUE, (*const rb_callable_method_entry_t, u8)> {
+ &mut CodegenGlobals::get_instance().pc_to_cfunc
}
}
@@ -8326,14 +10516,14 @@ mod tests {
fn test_gen_exit() {
let (_, _ctx, mut asm, mut cb, _) = setup_codegen();
gen_exit(0 as *mut VALUE, &mut asm);
- asm.compile(&mut cb, None);
+ asm.compile(&mut cb, None).unwrap();
assert!(cb.get_write_pos() > 0);
}
#[test]
fn test_get_side_exit() {
let (_jit, ctx, mut asm, _, mut ocb) = setup_codegen();
- let side_exit_context = SideExitContext { pc: 0 as _, ctx };
+ let side_exit_context = SideExitContext::new(0 as _, ctx);
asm.get_side_exit(&side_exit_context, None, &mut ocb);
assert!(ocb.unwrap().get_write_pos() > 0);
}
@@ -8342,14 +10532,14 @@ mod tests {
fn test_gen_check_ints() {
let (_jit, _ctx, mut asm, _cb, _ocb) = setup_codegen();
asm.set_side_exit_context(0 as _, 0);
- gen_check_ints(&mut asm, Counter::send_interrupted);
+ gen_check_ints(&mut asm, Counter::guard_send_interrupted);
}
#[test]
fn test_gen_nop() {
let (mut jit, context, mut asm, mut cb, mut ocb) = setup_codegen();
let status = gen_nop(&mut jit, &mut asm, &mut ocb);
- asm.compile(&mut cb, None);
+ asm.compile(&mut cb, None).unwrap();
assert_eq!(status, Some(KeepCompiling));
assert_eq!(context.diff(&Context::default()), TypeDiff::Compatible(0));
@@ -8381,7 +10571,7 @@ mod tests {
assert_eq!(Type::Fixnum, asm.ctx.get_opnd_type(StackOpnd(0)));
assert_eq!(Type::Fixnum, asm.ctx.get_opnd_type(StackOpnd(1)));
- asm.compile(&mut cb, None);
+ asm.compile(&mut cb, None).unwrap();
assert!(cb.get_write_pos() > 0); // Write some movs
}
@@ -8405,7 +10595,7 @@ mod tests {
assert_eq!(Type::Flonum, asm.ctx.get_opnd_type(StackOpnd(0)));
// TODO: this is writing zero bytes on x86. Why?
- asm.compile(&mut cb, None);
+ asm.compile(&mut cb, None).unwrap();
assert!(cb.get_write_pos() > 0); // Write some movs
}
@@ -8417,8 +10607,8 @@ mod tests {
let status = gen_swap(&mut jit, &mut asm, &mut ocb);
- let (_, tmp_type_top) = asm.ctx.get_opnd_mapping(StackOpnd(0));
- let (_, tmp_type_next) = asm.ctx.get_opnd_mapping(StackOpnd(1));
+ let tmp_type_top = asm.ctx.get_opnd_type(StackOpnd(0));
+ let tmp_type_next = asm.ctx.get_opnd_type(StackOpnd(1));
assert_eq!(status, Some(KeepCompiling));
assert_eq!(tmp_type_top, Type::Fixnum);
@@ -8430,65 +10620,14 @@ mod tests {
let (mut jit, _context, mut asm, mut cb, mut ocb) = setup_codegen();
let status = gen_putnil(&mut jit, &mut asm, &mut ocb);
- let (_, tmp_type_top) = asm.ctx.get_opnd_mapping(StackOpnd(0));
+ let tmp_type_top = asm.ctx.get_opnd_type(StackOpnd(0));
assert_eq!(status, Some(KeepCompiling));
assert_eq!(tmp_type_top, Type::Nil);
- asm.compile(&mut cb, None);
- assert!(cb.get_write_pos() > 0);
- }
-
- #[test]
- fn test_putobject_qtrue() {
- // Test gen_putobject with Qtrue
- let (mut jit, _context, mut asm, mut cb, mut ocb) = setup_codegen();
-
- let mut value_array: [u64; 2] = [0, Qtrue.into()];
- let pc: *mut VALUE = &mut value_array as *mut u64 as *mut VALUE;
- jit.pc = pc;
-
- let status = gen_putobject(&mut jit, &mut asm, &mut ocb);
-
- let (_, tmp_type_top) = asm.ctx.get_opnd_mapping(StackOpnd(0));
-
- assert_eq!(status, Some(KeepCompiling));
- assert_eq!(tmp_type_top, Type::True);
- asm.compile(&mut cb, None);
- assert!(cb.get_write_pos() > 0);
- }
-
- #[test]
- fn test_putobject_fixnum() {
- // Test gen_putobject with a Fixnum to test another conditional branch
- let (mut jit, _context, mut asm, mut cb, mut ocb) = setup_codegen();
-
- // The Fixnum 7 is encoded as 7 * 2 + 1, or 15
- let mut value_array: [u64; 2] = [0, 15];
- let pc: *mut VALUE = &mut value_array as *mut u64 as *mut VALUE;
- jit.pc = pc;
-
- let status = gen_putobject(&mut jit, &mut asm, &mut ocb);
-
- let (_, tmp_type_top) = asm.ctx.get_opnd_mapping(StackOpnd(0));
-
- assert_eq!(status, Some(KeepCompiling));
- assert_eq!(tmp_type_top, Type::Fixnum);
- asm.compile(&mut cb, None);
+ asm.compile(&mut cb, None).unwrap();
assert!(cb.get_write_pos() > 0);
}
- #[test]
- fn test_int2fix() {
- let (mut jit, _context, mut asm, _cb, mut ocb) = setup_codegen();
- jit.opcode = YARVINSN_putobject_INT2FIX_0_.as_usize();
- let status = gen_putobject_int2fix(&mut jit, &mut asm, &mut ocb);
-
- let (_, tmp_type_top) = asm.ctx.get_opnd_mapping(StackOpnd(0));
-
- // Right now we're not testing the generated machine code to make sure a literal 1 or 0 was pushed. I've checked locally.
- assert_eq!(status, Some(KeepCompiling));
- assert_eq!(tmp_type_top, Type::Fixnum);
- }
#[test]
fn test_putself() {
@@ -8496,7 +10635,7 @@ mod tests {
let status = gen_putself(&mut jit, &mut asm, &mut ocb);
assert_eq!(status, Some(KeepCompiling));
- asm.compile(&mut cb, None);
+ asm.compile(&mut cb, None).unwrap();
assert!(cb.get_write_pos() > 0);
}
@@ -8519,7 +10658,7 @@ mod tests {
assert_eq!(Type::Flonum, asm.ctx.get_opnd_type(StackOpnd(1)));
assert_eq!(Type::CString, asm.ctx.get_opnd_type(StackOpnd(0)));
- asm.compile(&mut cb, None);
+ asm.compile(&mut cb, None).unwrap();
assert!(cb.get_write_pos() > 0);
}
@@ -8541,7 +10680,7 @@ mod tests {
assert_eq!(Type::CString, asm.ctx.get_opnd_type(StackOpnd(1)));
assert_eq!(Type::Flonum, asm.ctx.get_opnd_type(StackOpnd(0)));
- asm.compile(&mut cb, None);
+ asm.compile(&mut cb, None).unwrap();
assert!(cb.get_write_pos() > 0); // Write some movs
}
@@ -8562,7 +10701,7 @@ mod tests {
assert_eq!(Type::Flonum, asm.ctx.get_opnd_type(StackOpnd(0)));
- asm.compile(&mut cb, None);
+ asm.compile(&mut cb, None).unwrap();
assert!(cb.get_write_pos() == 0); // No instructions written
}
diff --git a/yjit/src/core.rs b/yjit/src/core.rs
index 4dd0a387d5..cd6e649aa0 100644
--- a/yjit/src/core.rs
+++ b/yjit/src/core.rs
@@ -18,13 +18,14 @@ use std::cell::*;
use std::collections::HashSet;
use std::fmt;
use std::mem;
+use std::mem::transmute;
use std::ops::Range;
use std::rc::Rc;
use mem::MaybeUninit;
use std::ptr;
use ptr::NonNull;
use YARVOpnd::*;
-use TempMapping::*;
+use TempMappingKind::*;
use crate::invariants::*;
// Maximum number of temp value types we keep track of
@@ -39,8 +40,9 @@ pub type IseqIdx = u16;
// Represent the type of a value (local/stack/self) in YJIT
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
+#[repr(u8)]
pub enum Type {
- Unknown,
+ Unknown = 0,
UnknownImm,
UnknownHeap,
Nil,
@@ -48,19 +50,20 @@ pub enum Type {
False,
Fixnum,
Flonum,
- Hash,
ImmSymbol,
- #[allow(unused)]
- HeapSymbol,
-
TString, // An object with the T_STRING flag set, possibly an rb_cString
- CString, // An un-subclassed string of type rb_cString (can have instance vars in some cases)
+ CString, // An object that at one point had its class field equal rb_cString (creating a singleton class changes it)
TArray, // An object with the T_ARRAY flag set, possibly an rb_cArray
- CArray, // An un-subclassed string of type rb_cArray (can have instance vars in some cases)
+ CArray, // An object that at one point had its class field equal rb_cArray (creating a singleton class changes it)
+ THash, // An object with the T_HASH flag set, possibly an rb_cHash
+ CHash, // An object that at one point had its class field equal rb_cHash (creating a singleton class changes it)
BlockParamProxy, // A special sentinel value indicating the block parameter should be read from
// the current surrounding cfp
+
+ // The context currently relies on types taking at most 4 bits (max value 15)
+ // to encode, so if we add any more, we will need to refactor the context.
}
// Default initialization
@@ -93,12 +96,11 @@ impl Type {
// Core.rs can't reference rb_cString because it's linked by Rust-only tests.
// But CString vs TString is only an optimisation and shouldn't affect correctness.
#[cfg(not(test))]
- if val.class_of() == unsafe { rb_cString } {
- return Type::CString;
- }
- #[cfg(not(test))]
- if val.class_of() == unsafe { rb_cArray } {
- return Type::CArray;
+ match val.class_of() {
+ class if class == unsafe { rb_cArray } => return Type::CArray,
+ class if class == unsafe { rb_cHash } => return Type::CHash,
+ class if class == unsafe { rb_cString } => return Type::CString,
+ _ => {}
}
// We likewise can't reference rb_block_param_proxy, but it's again an optimisation;
// we can just treat it as a normal Object.
@@ -108,7 +110,7 @@ impl Type {
}
match val.builtin_type() {
RUBY_T_ARRAY => Type::TArray,
- RUBY_T_HASH => Type::Hash,
+ RUBY_T_HASH => Type::THash,
RUBY_T_STRING => Type::TString,
_ => Type::UnknownHeap,
}
@@ -150,8 +152,8 @@ impl Type {
Type::UnknownHeap => true,
Type::TArray => true,
Type::CArray => true,
- Type::Hash => true,
- Type::HeapSymbol => true,
+ Type::THash => true,
+ Type::CHash => true,
Type::TString => true,
Type::CString => true,
Type::BlockParamProxy => true,
@@ -161,20 +163,17 @@ impl Type {
/// Check if it's a T_ARRAY object (both TArray and CArray are T_ARRAY)
pub fn is_array(&self) -> bool {
- match self {
- Type::TArray => true,
- Type::CArray => true,
- _ => false,
- }
+ matches!(self, Type::TArray | Type::CArray)
+ }
+
+ /// Check if it's a T_HASH object (both THash and CHash are T_HASH)
+ pub fn is_hash(&self) -> bool {
+ matches!(self, Type::THash | Type::CHash)
}
/// Check if it's a T_STRING object (both TString and CString are T_STRING)
pub fn is_string(&self) -> bool {
- match self {
- Type::TString => true,
- Type::CString => true,
- _ => false,
- }
+ matches!(self, Type::TString | Type::CString)
}
/// Returns an Option with the T_ value type if it is known, otherwise None
@@ -186,8 +185,8 @@ impl Type {
Type::Fixnum => Some(RUBY_T_FIXNUM),
Type::Flonum => Some(RUBY_T_FLOAT),
Type::TArray | Type::CArray => Some(RUBY_T_ARRAY),
- Type::Hash => Some(RUBY_T_HASH),
- Type::ImmSymbol | Type::HeapSymbol => Some(RUBY_T_SYMBOL),
+ Type::THash | Type::CHash => Some(RUBY_T_HASH),
+ Type::ImmSymbol => Some(RUBY_T_SYMBOL),
Type::TString | Type::CString => Some(RUBY_T_STRING),
Type::Unknown | Type::UnknownImm | Type::UnknownHeap => None,
Type::BlockParamProxy => None,
@@ -203,9 +202,10 @@ impl Type {
Type::False => Some(rb_cFalseClass),
Type::Fixnum => Some(rb_cInteger),
Type::Flonum => Some(rb_cFloat),
- Type::ImmSymbol | Type::HeapSymbol => Some(rb_cSymbol),
- Type::CString => Some(rb_cString),
+ Type::ImmSymbol => Some(rb_cSymbol),
Type::CArray => Some(rb_cArray),
+ Type::CHash => Some(rb_cHash),
+ Type::CString => Some(rb_cString),
_ => None,
}
}
@@ -255,13 +255,18 @@ impl Type {
return TypeDiff::Compatible(1);
}
- // A CString is also a TString.
- if self == Type::CString && dst == Type::TString {
+ // A CArray is also a TArray.
+ if self == Type::CArray && dst == Type::TArray {
return TypeDiff::Compatible(1);
}
- // A CArray is also a TArray.
- if self == Type::CArray && dst == Type::TArray {
+ // A CHash is also a THash.
+ if self == Type::CHash && dst == Type::THash {
+ return TypeDiff::Compatible(1);
+ }
+
+ // A CString is also a TString.
+ if self == Type::CString && dst == Type::TString {
return TypeDiff::Compatible(1);
}
@@ -296,63 +301,92 @@ pub enum TypeDiff {
Incompatible,
}
-// Potential mapping of a value on the temporary stack to
-// self, a local variable or constant so that we can track its type
#[derive(Copy, Clone, Eq, Hash, PartialEq, Debug)]
-pub enum TempMapping {
- MapToStack, // Normal stack value
- MapToSelf, // Temp maps to the self operand
- MapToLocal(LocalIndex), // Temp maps to a local variable with index
- //ConstMapping, // Small constant (0, 1, 2, Qnil, Qfalse, Qtrue)
+#[repr(u8)]
+pub enum TempMappingKind
+{
+ MapToStack = 0,
+ MapToSelf = 1,
+ MapToLocal = 2,
}
-// Index used by MapToLocal. Using this instead of u8 makes TempMapping 1 byte.
+// Potential mapping of a value on the temporary stack to
+// self, a local variable or constant so that we can track its type
+//
+// The highest two bits represent TempMappingKind, and the rest of
+// the bits are used differently across different kinds.
+// * MapToStack: The lowest 5 bits are used for mapping Type.
+// * MapToSelf: The remaining bits are not used; the type is stored in self_type.
+// * MapToLocal: The lowest 3 bits store the index of a local variable.
#[derive(Copy, Clone, Eq, Hash, PartialEq, Debug)]
-pub enum LocalIndex {
- Local0,
- Local1,
- Local2,
- Local3,
- Local4,
- Local5,
- Local6,
- Local7,
-}
+pub struct TempMapping(u8);
-impl From<LocalIndex> for u8 {
- fn from(idx: LocalIndex) -> Self {
- match idx {
- LocalIndex::Local0 => 0,
- LocalIndex::Local1 => 1,
- LocalIndex::Local2 => 2,
- LocalIndex::Local3 => 3,
- LocalIndex::Local4 => 4,
- LocalIndex::Local5 => 5,
- LocalIndex::Local6 => 6,
- LocalIndex::Local7 => 7,
- }
+impl TempMapping {
+ pub fn map_to_stack(t: Type) -> TempMapping
+ {
+ let kind_bits = TempMappingKind::MapToStack as u8;
+ let type_bits = t as u8;
+ assert!(type_bits <= 0b11111);
+ let bits = (kind_bits << 6) | (type_bits & 0b11111);
+ TempMapping(bits)
+ }
+
+ pub fn map_to_self() -> TempMapping
+ {
+ let kind_bits = TempMappingKind::MapToSelf as u8;
+ let bits = kind_bits << 6;
+ TempMapping(bits)
}
-}
-impl From<u8> for LocalIndex {
- fn from(idx: u8) -> Self {
- match idx {
- 0 => LocalIndex::Local0,
- 1 => LocalIndex::Local1,
- 2 => LocalIndex::Local2,
- 3 => LocalIndex::Local3,
- 4 => LocalIndex::Local4,
- 5 => LocalIndex::Local5,
- 6 => LocalIndex::Local6,
- 7 => LocalIndex::Local7,
- _ => unreachable!("{idx} was larger than {MAX_LOCAL_TYPES}"),
+ pub fn map_to_local(local_idx: u8) -> TempMapping
+ {
+ let kind_bits = TempMappingKind::MapToLocal as u8;
+ assert!(local_idx <= 0b111);
+ let bits = (kind_bits << 6) | (local_idx & 0b111);
+ TempMapping(bits)
+ }
+
+ pub fn without_type(&self) -> TempMapping
+ {
+ if self.get_kind() != TempMappingKind::MapToStack {
+ return *self;
}
+
+ TempMapping::map_to_stack(Type::Unknown)
+ }
+
+ pub fn get_kind(&self) -> TempMappingKind
+ {
+ // Take the two highest bits
+ let TempMapping(bits) = self;
+ let kind_bits = bits >> 6;
+ assert!(kind_bits <= 2);
+ unsafe { transmute::<u8, TempMappingKind>(kind_bits) }
+ }
+
+ pub fn get_type(&self) -> Type
+ {
+ assert!(self.get_kind() == TempMappingKind::MapToStack);
+
+ // Take the 5 lowest bits
+ let TempMapping(bits) = self;
+ let type_bits = bits & 0b11111;
+ unsafe { transmute::<u8, Type>(type_bits) }
+ }
+
+ pub fn get_local_idx(&self) -> u8
+ {
+ assert!(self.get_kind() == TempMappingKind::MapToLocal);
+
+ // Take the 3 lowest bits
+ let TempMapping(bits) = self;
+ bits & 0b111
}
}
impl Default for TempMapping {
fn default() -> Self {
- MapToStack
+ TempMapping::map_to_stack(Type::Unknown)
}
}
@@ -403,21 +437,27 @@ impl RegTemps {
/// Return true if there's a register that conflicts with a given stack_idx.
pub fn conflicts_with(&self, stack_idx: u8) -> bool {
- let mut other_idx = stack_idx as isize - get_option!(num_temp_regs) as isize;
- while other_idx >= 0 {
- if self.get(other_idx as u8) {
+ let mut other_idx = stack_idx as usize % get_option!(num_temp_regs);
+ while other_idx < MAX_REG_TEMPS as usize {
+ if stack_idx as usize != other_idx && self.get(other_idx as u8) {
return true;
}
- other_idx -= get_option!(num_temp_regs) as isize;
+ other_idx += get_option!(num_temp_regs);
}
false
}
}
+/// Bits for chain_depth_return_landing_defer
+const RETURN_LANDING_BIT: u8 = 0b10000000;
+const DEFER_BIT: u8 = 0b01000000;
+const CHAIN_DEPTH_MASK: u8 = 0b00111111; // 63
+
/// Code generation context
/// Contains information we can use to specialize/optimize code
/// There are a lot of context objects so we try to keep the size small.
-#[derive(Clone, Copy, Default, Eq, Hash, PartialEq, Debug)]
+#[derive(Copy, Clone, Default, Eq, Hash, PartialEq, Debug)]
+#[repr(packed)]
pub struct Context {
// Number of values currently on the temporary stack
stack_size: u8,
@@ -429,20 +469,33 @@ pub struct Context {
/// Bitmap of which stack temps are in a register
reg_temps: RegTemps,
- // Depth of this block in the sidechain (eg: inline-cache chain)
- chain_depth: u8,
+ /// Fields packed into u8
+ /// - 1st bit from the left: Whether this code is the target of a JIT-to-JIT Ruby return ([Self::is_return_landing])
+ /// - 2nd bit from the left: Whether the compilation of this code has been deferred ([Self::is_deferred])
+ /// - Last 6 bits (max: 63): Depth of this block in the sidechain (eg: inline-cache chain)
+ chain_depth_and_flags: u8,
+
+ // Type we track for self
+ self_type: Type,
// Local variable types we keep track of
- local_types: [Type; MAX_LOCAL_TYPES],
+ // We store 8 local types, requiring 4 bits each, for a total of 32 bits
+ local_types: u32,
- // Temporary variable types we keep track of
- temp_types: [Type; MAX_TEMP_TYPES],
+ // Temp mapping kinds we track
+ // 8 temp mappings * 2 bits, total 16 bits
+ temp_mapping_kind: u16,
- // Type we track for self
- self_type: Type,
+ // Stack slot type/local_idx we track
+ // 8 temp types * 4 bits, total 32 bits
+ temp_payload: u32,
- // Mapping of temp stack entries to types we track
- temp_mapping: [TempMapping; MAX_TEMP_TYPES],
+ /// A pointer to a block ISEQ supplied by the caller. 0 if not inlined.
+ /// Not using IseqPtr to satisfy Default trait, and not using Option for #[repr(packed)]
+ /// TODO: This could be u16 if we have a global or per-ISEQ HashMap to convert IseqPtr
+ /// to serial indexes. We're thinking of overhauling Context structure in Ruby 3.4 which
+ /// could allow this to consume no bytes, so we're leaving this as is.
+ inline_block: u64,
}
/// Tuple of (iseq, idx) used to identify basic blocks
@@ -474,6 +527,8 @@ pub enum BranchGenFn {
JNZToTarget0,
JZToTarget0,
JBEToTarget0,
+ JBToTarget0,
+ JOMulToTarget0,
JITReturn,
}
@@ -485,8 +540,8 @@ impl BranchGenFn {
BranchShape::Next0 => asm.jz(target1.unwrap()),
BranchShape::Next1 => asm.jnz(target0),
BranchShape::Default => {
- asm.jnz(target0.into());
- asm.jmp(target1.unwrap().into());
+ asm.jnz(target0);
+ asm.jmp(target1.unwrap());
}
}
}
@@ -515,11 +570,11 @@ impl BranchGenFn {
panic!("Branch shape Next1 not allowed in JumpToTarget0!");
}
if shape.get() == BranchShape::Default {
- asm.jmp(target0.into());
+ asm.jmp(target0);
}
}
BranchGenFn::JNZToTarget0 => {
- asm.jnz(target0.into())
+ asm.jnz(target0)
}
BranchGenFn::JZToTarget0 => {
asm.jz(target0)
@@ -527,9 +582,17 @@ impl BranchGenFn {
BranchGenFn::JBEToTarget0 => {
asm.jbe(target0)
}
+ BranchGenFn::JBToTarget0 => {
+ asm.jb(target0)
+ }
+ BranchGenFn::JOMulToTarget0 => {
+ asm.jo_mul(target0)
+ }
BranchGenFn::JITReturn => {
- asm.comment("update cfp->jit_return");
- asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_JIT_RETURN), Opnd::const_ptr(target0.unwrap_code_ptr().raw_ptr()));
+ asm_comment!(asm, "update cfp->jit_return");
+ let jit_return = RUBY_OFFSET_CFP_JIT_RETURN - RUBY_SIZEOF_CONTROL_FRAME as i32;
+ let raw_ptr = asm.lea_jump_target(target0);
+ asm.mov(Opnd::mem(64, CFP, jit_return), raw_ptr);
}
}
}
@@ -543,6 +606,8 @@ impl BranchGenFn {
BranchGenFn::JNZToTarget0 |
BranchGenFn::JZToTarget0 |
BranchGenFn::JBEToTarget0 |
+ BranchGenFn::JBToTarget0 |
+ BranchGenFn::JOMulToTarget0 |
BranchGenFn::JITReturn => BranchShape::Default,
}
}
@@ -563,6 +628,8 @@ impl BranchGenFn {
BranchGenFn::JNZToTarget0 |
BranchGenFn::JZToTarget0 |
BranchGenFn::JBEToTarget0 |
+ BranchGenFn::JBToTarget0 |
+ BranchGenFn::JOMulToTarget0 |
BranchGenFn::JITReturn => {
assert_eq!(new_shape, BranchShape::Default);
}
@@ -594,8 +661,8 @@ impl BranchTarget {
fn get_ctx(&self) -> Context {
match self {
- BranchTarget::Stub(stub) => stub.ctx.clone(),
- BranchTarget::Block(blockref) => unsafe { blockref.as_ref() }.ctx.clone(),
+ BranchTarget::Stub(stub) => stub.ctx,
+ BranchTarget::Block(blockref) => unsafe { blockref.as_ref() }.ctx,
}
}
@@ -660,7 +727,7 @@ pub struct PendingBranch {
impl Branch {
// Compute the size of the branch code
fn code_size(&self) -> usize {
- (self.end_addr.get().raw_ptr() as usize) - (self.start_addr.raw_ptr() as usize)
+ (self.end_addr.get().as_offset() - self.start_addr.as_offset()) as usize
}
/// Get the address of one of the branch destination
@@ -752,7 +819,7 @@ impl PendingBranch {
address: Some(stub_addr),
iseq: Cell::new(target.iseq),
iseq_idx: target.idx,
- ctx: ctx.clone(),
+ ctx: *ctx,
})))));
}
@@ -937,7 +1004,6 @@ impl fmt::Debug for MutableBranchList {
}
}
-
/// This is all the data YJIT stores on an iseq
/// This will be dynamically allocated by C code
/// C code should pass an &mut IseqPayload to us
@@ -1050,23 +1116,34 @@ pub fn for_each_on_stack_iseq_payload<F: FnMut(&IseqPayload)>(mut callback: F) {
/// Iterate over all NOT on-stack ISEQ payloads
pub fn for_each_off_stack_iseq_payload<F: FnMut(&mut IseqPayload)>(mut callback: F) {
- let mut on_stack_iseqs: Vec<IseqPtr> = vec![];
- for_each_on_stack_iseq(|iseq| {
- on_stack_iseqs.push(iseq);
- });
- for_each_iseq(|iseq| {
+ // Get all ISEQs on the heap. Note that rb_objspace_each_objects() runs GC first,
+ // which could move ISEQ pointers when GC.auto_compact = true.
+ // So for_each_on_stack_iseq() must be called after this, which doesn't run GC.
+ let mut iseqs: Vec<IseqPtr> = vec![];
+ for_each_iseq(|iseq| iseqs.push(iseq));
+
+ // Get all ISEQs that are on a CFP of existing ECs.
+ let mut on_stack_iseqs: HashSet<IseqPtr> = HashSet::new();
+ for_each_on_stack_iseq(|iseq| { on_stack_iseqs.insert(iseq); });
+
+ // Invoke the callback for iseqs - on_stack_iseqs
+ for iseq in iseqs {
if !on_stack_iseqs.contains(&iseq) {
if let Some(iseq_payload) = get_iseq_payload(iseq) {
callback(iseq_payload);
}
}
- })
+ }
}
/// Free the per-iseq payload
#[no_mangle]
-pub extern "C" fn rb_yjit_iseq_free(payload: *mut c_void) {
+pub extern "C" fn rb_yjit_iseq_free(iseq: IseqPtr) {
+ // Free invariants for the ISEQ
+ iseq_free_invariants(iseq);
+
let payload = {
+ let payload = unsafe { rb_iseq_get_yjit_payload(iseq) };
if payload.is_null() {
// Nothing to free.
return;
@@ -1103,7 +1180,7 @@ pub extern "C" fn rb_yjit_iseq_free(payload: *mut c_void) {
incr_counter!(freed_iseq_count);
}
-/// GC callback for marking GC objects in the the per-iseq payload.
+/// GC callback for marking GC objects in the per-iseq payload.
#[no_mangle]
pub extern "C" fn rb_yjit_iseq_mark(payload: *mut c_void) {
let payload = if payload.is_null() {
@@ -1129,30 +1206,54 @@ pub extern "C" fn rb_yjit_iseq_mark(payload: *mut c_void) {
for block in versions {
// SAFETY: all blocks inside version_map are initialized.
let block = unsafe { block.as_ref() };
+ mark_block(block, cb, false);
+ }
+ }
+ // Mark dead blocks, since there could be stubs pointing at them
+ for blockref in &payload.dead_blocks {
+ // SAFETY: dead blocks come from version_map, which only have initialized blocks
+ let block = unsafe { blockref.as_ref() };
+ mark_block(block, cb, true);
+ }
+
+ return;
+
+ fn mark_block(block: &Block, cb: &CodeBlock, dead: bool) {
+ unsafe { rb_gc_mark_movable(block.iseq.get().into()) };
+
+ // Mark method entry dependencies
+ for cme_dep in block.cme_dependencies.iter() {
+ unsafe { rb_gc_mark_movable(cme_dep.get().into()) };
+ }
+
+ // Mark outgoing branch entries
+ for branch in block.outgoing.iter() {
+ let branch = unsafe { branch.as_ref() };
+ for target in branch.targets.iter() {
+ // SAFETY: no mutation inside unsafe
+ let target_iseq = unsafe {
+ target.ref_unchecked().as_ref().and_then(|target| {
+ // Avoid get_blockid() on blockref. Can be dangling on dead blocks,
+ // and the iseq housing the block already naturally handles it.
+ if target.get_block().is_some() {
+ None
+ } else {
+ Some(target.get_blockid().iseq)
+ }
+ })
+ };
- unsafe { rb_gc_mark_movable(block.iseq.get().into()) };
-
- // Mark method entry dependencies
- for cme_dep in block.cme_dependencies.iter() {
- unsafe { rb_gc_mark_movable(cme_dep.get().into()) };
- }
-
- // Mark outgoing branch entries
- for branch in block.outgoing.iter() {
- let branch = unsafe { branch.as_ref() };
- for target in branch.targets.iter() {
- // SAFETY: no mutation inside unsafe
- let target_iseq = unsafe { target.ref_unchecked().as_ref().map(|target| target.get_blockid().iseq) };
-
- if let Some(target_iseq) = target_iseq {
- unsafe { rb_gc_mark_movable(target_iseq.into()) };
- }
+ if let Some(target_iseq) = target_iseq {
+ unsafe { rb_gc_mark_movable(target_iseq.into()) };
}
}
+ }
- // Walk over references to objects in generated code.
+ // Mark references to objects in generated code.
+ // Skip for dead blocks since they shouldn't run.
+ if !dead {
for offset in block.gc_obj_offsets.iter() {
- let value_address: *const u8 = cb.get_ptr(offset.as_usize()).raw_ptr();
+ let value_address: *const u8 = cb.get_ptr(offset.as_usize()).raw_ptr(cb);
// Creating an unaligned pointer is well defined unlike in C.
let value_address = value_address as *const VALUE;
@@ -1166,10 +1267,11 @@ pub extern "C" fn rb_yjit_iseq_mark(payload: *mut c_void) {
}
}
-/// GC callback for updating GC objects in the the per-iseq payload.
+/// GC callback for updating GC objects in the per-iseq payload.
/// This is a mirror of [rb_yjit_iseq_mark].
#[no_mangle]
-pub extern "C" fn rb_yjit_iseq_update_references(payload: *mut c_void) {
+pub extern "C" fn rb_yjit_iseq_update_references(iseq: IseqPtr) {
+ let payload = unsafe { rb_iseq_get_yjit_payload(iseq) };
let payload = if payload.is_null() {
// Nothing to update.
return;
@@ -1196,21 +1298,70 @@ pub extern "C" fn rb_yjit_iseq_update_references(payload: *mut c_void) {
for version in versions {
// SAFETY: all blocks inside version_map are initialized
let block = unsafe { version.as_ref() };
+ block_update_references(block, cb, false);
+ }
+ }
+ // Update dead blocks, since there could be stubs pointing at them
+ for blockref in &payload.dead_blocks {
+ // SAFETY: dead blocks come from version_map, which only have initialized blocks
+ let block = unsafe { blockref.as_ref() };
+ block_update_references(block, cb, true);
+ }
- block.iseq.set(unsafe { rb_gc_location(block.iseq.get().into()) }.as_iseq());
+ // Note that we would have returned already if YJIT is off.
+ cb.mark_all_executable();
- // Update method entry dependencies
- for cme_dep in block.cme_dependencies.iter() {
- let cur_cme: VALUE = cme_dep.get().into();
- let new_cme = unsafe { rb_gc_location(cur_cme) }.as_cme();
- cme_dep.set(new_cme);
+ CodegenGlobals::get_outlined_cb()
+ .unwrap()
+ .mark_all_executable();
+
+ return;
+
+ fn block_update_references(block: &Block, cb: &mut CodeBlock, dead: bool) {
+ block.iseq.set(unsafe { rb_gc_location(block.iseq.get().into()) }.as_iseq());
+
+ // Update method entry dependencies
+ for cme_dep in block.cme_dependencies.iter() {
+ let cur_cme: VALUE = cme_dep.get().into();
+ let new_cme = unsafe { rb_gc_location(cur_cme) }.as_cme();
+ cme_dep.set(new_cme);
+ }
+
+ // Update outgoing branch entries
+ for branch in block.outgoing.iter() {
+ let branch = unsafe { branch.as_ref() };
+ for target in branch.targets.iter() {
+ // SAFETY: no mutation inside unsafe
+ let current_iseq = unsafe {
+ target.ref_unchecked().as_ref().and_then(|target| {
+ // Avoid get_blockid() on blockref. Can be dangling on dead blocks,
+ // and the iseq housing the block already naturally handles it.
+ if target.get_block().is_some() {
+ None
+ } else {
+ Some(target.get_blockid().iseq)
+ }
+ })
+ };
+
+ if let Some(current_iseq) = current_iseq {
+ let updated_iseq = unsafe { rb_gc_location(current_iseq.into()) }
+ .as_iseq();
+ // SAFETY: the Cell::set is not on the reference given out
+ // by ref_unchecked.
+ unsafe { target.ref_unchecked().as_ref().unwrap().set_iseq(updated_iseq) };
+ }
}
+ }
- // Walk over references to objects in generated code.
+ // Update references to objects in generated code.
+ // Skip for dead blocks since they shouldn't run and
+ // so there is no potential of writing over invalidation jumps
+ if !dead {
for offset in block.gc_obj_offsets.iter() {
let offset_to_value = offset.as_usize();
let value_code_ptr = cb.get_ptr(offset_to_value);
- let value_ptr: *const u8 = value_code_ptr.raw_ptr();
+ let value_ptr: *const u8 = value_code_ptr.raw_ptr(cb);
// Creating an unaligned pointer is well defined unlike in C.
let value_ptr = value_ptr as *mut VALUE;
@@ -1227,32 +1378,9 @@ pub extern "C" fn rb_yjit_iseq_update_references(payload: *mut c_void) {
}
}
}
-
- // Update outgoing branch entries
- for branch in block.outgoing.iter() {
- let branch = unsafe { branch.as_ref() };
- for target in branch.targets.iter() {
- // SAFETY: no mutation inside unsafe
- let current_iseq = unsafe { target.ref_unchecked().as_ref().map(|target| target.get_blockid().iseq) };
-
- if let Some(current_iseq) = current_iseq {
- let updated_iseq = unsafe { rb_gc_location(current_iseq.into()) }
- .as_iseq();
- // SAFETY: the Cell::set is not on the reference given out
- // by ref_unchecked.
- unsafe { target.ref_unchecked().as_ref().unwrap().set_iseq(updated_iseq) };
- }
- }
- }
}
- }
- // Note that we would have returned already if YJIT is off.
- cb.mark_all_executable();
-
- CodegenGlobals::get_outlined_cb()
- .unwrap()
- .mark_all_executable();
+ }
}
/// Get all blocks for a particular place in an iseq.
@@ -1293,14 +1421,19 @@ pub fn take_version_list(blockid: BlockId) -> VersionList {
}
/// Count the number of block versions matching a given blockid
-fn get_num_versions(blockid: BlockId) -> usize {
+/// `inlined: true` counts inlined versions, and `inlined: false` counts other versions.
+fn get_num_versions(blockid: BlockId, inlined: bool) -> usize {
let insn_idx = blockid.idx.as_usize();
match get_iseq_payload(blockid.iseq) {
Some(payload) => {
payload
.version_map
.get(insn_idx)
- .map(|versions| versions.len())
+ .map(|versions| {
+ versions.iter().filter(|&&version|
+ unsafe { version.as_ref() }.ctx.inline() == inlined
+ ).count()
+ })
.unwrap_or(0)
}
None => 0,
@@ -1355,41 +1488,54 @@ fn find_block_version(blockid: BlockId, ctx: &Context) -> Option<BlockRef> {
}
}
- // If greedy versioning is enabled
- if get_option!(greedy_versioning) {
- // If we're below the version limit, don't settle for an imperfect match
- if versions.len() + 1 < get_option!(max_versions) && best_diff > 0 {
- return None;
- }
- }
-
return best_version;
}
+/// Allow inlining a Block up to MAX_INLINE_VERSIONS times.
+const MAX_INLINE_VERSIONS: usize = 1000;
+
/// Produce a generic context when the block version limit is hit for a blockid
pub fn limit_block_versions(blockid: BlockId, ctx: &Context) -> Context {
// Guard chains implement limits separately, do nothing
- if ctx.chain_depth > 0 {
- return ctx.clone();
+ if ctx.get_chain_depth() > 0 {
+ return *ctx;
}
+ let next_versions = get_num_versions(blockid, ctx.inline()) + 1;
+ let max_versions = if ctx.inline() {
+ MAX_INLINE_VERSIONS
+ } else {
+ get_option!(max_versions)
+ };
+
// If this block version we're about to add will hit the version limit
- if get_num_versions(blockid) + 1 >= get_option!(max_versions) {
+ if next_versions >= max_versions {
// Produce a generic context that stores no type information,
// but still respects the stack_size and sp_offset constraints.
// This new context will then match all future requests.
let generic_ctx = ctx.get_generic_ctx();
- debug_assert_ne!(
- TypeDiff::Incompatible,
- ctx.diff(&generic_ctx),
- "should substitute a compatible context",
- );
+ if cfg!(debug_assertions) {
+ let mut ctx = ctx.clone();
+ if ctx.inline() {
+ // Suppress TypeDiff::Incompatible from ctx.diff(). We return TypeDiff::Incompatible
+ // to keep inlining blocks until we hit the limit, but it's safe to give up inlining.
+ ctx.inline_block = 0;
+ assert!(generic_ctx.inline_block == 0);
+ }
+
+ assert_ne!(
+ TypeDiff::Incompatible,
+ ctx.diff(&generic_ctx),
+ "should substitute a compatible context",
+ );
+ }
return generic_ctx;
}
+ incr_counter_to!(max_inline_versions, next_versions);
- return ctx.clone();
+ return *ctx;
}
/// Install a block version into its [IseqPayload], letting the GC track its
@@ -1436,7 +1582,7 @@ unsafe fn add_block_version(blockref: BlockRef, cb: &CodeBlock) {
// Run write barriers for all objects in generated code.
for offset in block.gc_obj_offsets.iter() {
- let value_address: *const u8 = cb.get_ptr(offset.as_usize()).raw_ptr();
+ let value_address: *const u8 = cb.get_ptr(offset.as_usize()).raw_ptr(cb);
// Creating an unaligned pointer is well defined unlike in C.
let value_address: *const VALUE = value_address.cast();
@@ -1513,6 +1659,12 @@ impl JITState {
if let Some(idlist) = self.stable_constant_names_assumption {
track_stable_constant_names_assumption(blockref, idlist);
}
+ for klass in self.no_singleton_class_assumptions {
+ track_no_singleton_class_assumption(blockref, klass);
+ }
+ if self.no_ep_escape {
+ track_no_ep_escape_assumption(blockref, self.iseq);
+ }
blockref
}
@@ -1558,7 +1710,7 @@ impl Block {
// Compute the size of the block code
pub fn code_size(&self) -> usize {
- (self.end_addr.get().into_usize()) - (self.start_addr.into_usize())
+ (self.end_addr.get().as_offset() - self.start_addr.as_offset()).try_into().unwrap()
}
}
@@ -1567,12 +1719,22 @@ impl Context {
self.stack_size
}
+ pub fn set_stack_size(&mut self, stack_size: u8) {
+ self.stack_size = stack_size;
+ }
+
/// Create a new Context that is compatible with self but doesn't have type information.
pub fn get_generic_ctx(&self) -> Context {
let mut generic_ctx = Context::default();
generic_ctx.stack_size = self.stack_size;
generic_ctx.sp_offset = self.sp_offset;
generic_ctx.reg_temps = self.reg_temps;
+ if self.is_return_landing() {
+ generic_ctx.set_as_return_landing();
+ }
+ if self.is_deferred() {
+ generic_ctx.mark_as_deferred();
+ }
generic_ctx
}
@@ -1580,7 +1742,7 @@ impl Context {
/// accordingly. This is useful when you want to virtually rewind a stack_size for
/// generating a side exit while considering past sp_offset changes on gen_save_sp.
pub fn with_stack_size(&self, stack_size: u8) -> Context {
- let mut ctx = self.clone();
+ let mut ctx = *self;
ctx.sp_offset -= (ctx.get_stack_size() as isize - stack_size as isize) as i8;
ctx.stack_size = stack_size;
ctx
@@ -1603,24 +1765,54 @@ impl Context {
}
pub fn get_chain_depth(&self) -> u8 {
- self.chain_depth
+ self.chain_depth_and_flags & CHAIN_DEPTH_MASK
}
- pub fn reset_chain_depth(&mut self) {
- self.chain_depth = 0;
+ pub fn reset_chain_depth_and_defer(&mut self) {
+ self.chain_depth_and_flags &= !CHAIN_DEPTH_MASK;
+ self.chain_depth_and_flags &= !DEFER_BIT;
}
pub fn increment_chain_depth(&mut self) {
- self.chain_depth += 1;
+ if self.get_chain_depth() == CHAIN_DEPTH_MASK {
+ panic!("max block version chain depth reached!");
+ }
+ self.chain_depth_and_flags += 1;
+ }
+
+ pub fn set_as_return_landing(&mut self) {
+ self.chain_depth_and_flags |= RETURN_LANDING_BIT;
+ }
+
+ pub fn clear_return_landing(&mut self) {
+ self.chain_depth_and_flags &= !RETURN_LANDING_BIT;
+ }
+
+ pub fn is_return_landing(&self) -> bool {
+ self.chain_depth_and_flags & RETURN_LANDING_BIT != 0
+ }
+
+ pub fn mark_as_deferred(&mut self) {
+ self.chain_depth_and_flags |= DEFER_BIT;
+ }
+
+ pub fn is_deferred(&self) -> bool {
+ self.chain_depth_and_flags & DEFER_BIT != 0
}
/// Get an operand for the adjusted stack pointer address
- pub fn sp_opnd(&self, offset_bytes: isize) -> Opnd {
- let offset = ((self.sp_offset as isize) * (SIZEOF_VALUE as isize)) + offset_bytes;
- let offset = offset as i32;
+ pub fn sp_opnd(&self, offset: i32) -> Opnd {
+ let offset = (self.sp_offset as i32 + offset) * SIZEOF_VALUE_I32;
return Opnd::mem(64, SP, offset);
}
+ /// Get an operand for the adjusted environment pointer address using SP register.
+ /// This is valid only when a Binding object hasn't been created for the frame.
+ pub fn ep_opnd(&self, offset: i32) -> Opnd {
+ let ep_offset = self.get_stack_size() as i32 + 1;
+ self.sp_opnd(-ep_offset + offset)
+ }
+
/// Stop using a register for a given stack temp.
/// This allows us to reuse the register for a value that we know is dead
/// and will no longer be used (e.g. popped stack temp).
@@ -1645,14 +1837,15 @@ impl Context {
return Type::Unknown;
}
- let mapping = self.temp_mapping[stack_idx];
+ let mapping = self.get_temp_mapping(stack_idx);
- match mapping {
+ match mapping.get_kind() {
MapToSelf => self.self_type,
- MapToStack => self.temp_types[(self.stack_size - 1 - idx) as usize],
- MapToLocal(idx) => {
+ MapToStack => mapping.get_type(),
+ MapToLocal => {
+ let idx = mapping.get_local_idx();
assert!((idx as usize) < MAX_LOCAL_TYPES);
- return self.local_types[idx as usize];
+ return self.get_local_type(idx.into());
}
}
}
@@ -1660,8 +1853,83 @@ impl Context {
}
/// Get the currently tracked type for a local variable
- pub fn get_local_type(&self, idx: usize) -> Type {
- *self.local_types.get(idx).unwrap_or(&Type::Unknown)
+ pub fn get_local_type(&self, local_idx: usize) -> Type {
+ if local_idx >= MAX_LOCAL_TYPES {
+ return Type::Unknown
+ } else {
+ // Each type is stored in 4 bits
+ let type_bits = (self.local_types >> (4 * local_idx)) & 0b1111;
+ unsafe { transmute::<u8, Type>(type_bits as u8) }
+ }
+ }
+
+ /// Get the current temp mapping for a given stack slot
+ fn get_temp_mapping(&self, temp_idx: usize) -> TempMapping {
+ assert!(temp_idx < MAX_TEMP_TYPES);
+
+ // Extract the temp mapping kind
+ let kind_bits = (self.temp_mapping_kind >> (2 * temp_idx)) & 0b11;
+ let temp_kind = unsafe { transmute::<u8, TempMappingKind>(kind_bits as u8) };
+
+ // Extract the payload bits (temp type or local idx)
+ let payload_bits = (self.temp_payload >> (4 * temp_idx)) & 0b1111;
+
+ match temp_kind {
+ MapToSelf => TempMapping::map_to_self(),
+
+ MapToStack => {
+ TempMapping::map_to_stack(
+ unsafe { transmute::<u8, Type>(payload_bits as u8) }
+ )
+ }
+
+ MapToLocal => {
+ TempMapping::map_to_local(
+ payload_bits as u8
+ )
+ }
+ }
+ }
+
+ /// Get the current temp mapping for a given stack slot
+ fn set_temp_mapping(&mut self, temp_idx: usize, mapping: TempMapping) {
+ assert!(temp_idx < MAX_TEMP_TYPES);
+
+ // Extract the kind bits
+ let mapping_kind = mapping.get_kind();
+ let kind_bits = unsafe { transmute::<TempMappingKind, u8>(mapping_kind) };
+ assert!(kind_bits <= 0b11);
+
+ // Extract the payload bits
+ let payload_bits = match mapping_kind {
+ MapToSelf => 0,
+
+ MapToStack => {
+ let t = mapping.get_type();
+ unsafe { transmute::<Type, u8>(t) }
+ }
+
+ MapToLocal => {
+ mapping.get_local_idx()
+ }
+ };
+ assert!(payload_bits <= 0b1111);
+
+ // Update the kind bits
+ {
+ let mask_bits = 0b11_u16 << (2 * temp_idx);
+ let shifted_bits = (kind_bits as u16) << (2 * temp_idx);
+ let all_kind_bits = self.temp_mapping_kind as u16;
+ self.temp_mapping_kind = (all_kind_bits & !mask_bits) | shifted_bits;
+ }
+
+ // Update the payload bits
+ {
+ let mask_bits = 0b1111_u32 << (4 * temp_idx);
+ let shifted_bits = (payload_bits as u32) << (4 * temp_idx);
+ let all_payload_bits = self.temp_payload as u32;
+ self.temp_payload = (all_payload_bits & !mask_bits) | shifted_bits;
+ }
}
/// Upgrade (or "learn") the type of an instruction operand
@@ -1685,15 +1953,24 @@ impl Context {
return;
}
- let mapping = self.temp_mapping[stack_idx];
+ let mapping = self.get_temp_mapping(stack_idx);
- match mapping {
+ match mapping.get_kind() {
MapToSelf => self.self_type.upgrade(opnd_type),
- MapToStack => self.temp_types[stack_idx].upgrade(opnd_type),
- MapToLocal(idx) => {
- let idx = idx as usize;
+ MapToStack => {
+ let mut temp_type = mapping.get_type();
+ temp_type.upgrade(opnd_type);
+ self.set_temp_mapping(stack_idx, TempMapping::map_to_stack(temp_type));
+ }
+ MapToLocal => {
+ let idx = mapping.get_local_idx() as usize;
assert!(idx < MAX_LOCAL_TYPES);
- self.local_types[idx].upgrade(opnd_type);
+ let mut new_type = self.get_local_type(idx);
+ new_type.upgrade(opnd_type);
+ self.set_local_type(idx, new_type);
+ // Re-attach MapToLocal for this StackOpnd(idx). set_local_type() detaches
+ // all MapToLocal mappings, including the one we're upgrading here.
+ self.set_opnd_mapping(opnd, mapping);
}
}
}
@@ -1705,29 +1982,29 @@ impl Context {
This is can be used with stack_push_mapping or set_opnd_mapping to copy
a stack value's type while maintaining the mapping.
*/
- pub fn get_opnd_mapping(&self, opnd: YARVOpnd) -> (TempMapping, Type) {
+ pub fn get_opnd_mapping(&self, opnd: YARVOpnd) -> TempMapping {
let opnd_type = self.get_opnd_type(opnd);
match opnd {
- SelfOpnd => (MapToSelf, opnd_type),
+ SelfOpnd => TempMapping::map_to_self(),
StackOpnd(idx) => {
assert!(idx < self.stack_size);
let stack_idx = (self.stack_size - 1 - idx) as usize;
if stack_idx < MAX_TEMP_TYPES {
- (self.temp_mapping[stack_idx], opnd_type)
+ self.get_temp_mapping(stack_idx)
} else {
// We can't know the source of this stack operand, so we assume it is
// a stack-only temporary. type will be UNKNOWN
assert!(opnd_type == Type::Unknown);
- (MapToStack, opnd_type)
+ TempMapping::map_to_stack(opnd_type)
}
}
}
}
/// Overwrite both the type and mapping of a stack operand.
- pub fn set_opnd_mapping(&mut self, opnd: YARVOpnd, (mapping, opnd_type): (TempMapping, Type)) {
+ pub fn set_opnd_mapping(&mut self, opnd: YARVOpnd, mapping: TempMapping) {
match opnd {
SelfOpnd => unreachable!("self always maps to self"),
StackOpnd(idx) => {
@@ -1744,44 +2021,47 @@ impl Context {
return;
}
- self.temp_mapping[stack_idx] = mapping;
-
- // Only used when mapping == MAP_STACK
- self.temp_types[stack_idx] = opnd_type;
+ self.set_temp_mapping(stack_idx, mapping);
}
}
}
/// Set the type of a local variable
pub fn set_local_type(&mut self, local_idx: usize, local_type: Type) {
- let ctx = self;
-
// If type propagation is disabled, store no types
if get_option!(no_type_prop) {
return;
}
if local_idx >= MAX_LOCAL_TYPES {
- return;
+ return
}
// If any values on the stack map to this local we must detach them
- for (i, mapping) in ctx.temp_mapping.iter_mut().enumerate() {
- *mapping = match *mapping {
- MapToStack => MapToStack,
- MapToSelf => MapToSelf,
- MapToLocal(idx) => {
+ for mapping_idx in 0..MAX_TEMP_TYPES {
+ let mapping = self.get_temp_mapping(mapping_idx);
+ let tm = match mapping.get_kind() {
+ MapToStack => mapping,
+ MapToSelf => mapping,
+ MapToLocal => {
+ let idx = mapping.get_local_idx();
if idx as usize == local_idx {
- ctx.temp_types[i] = ctx.local_types[idx as usize];
- MapToStack
+ let local_type = self.get_local_type(local_idx);
+ TempMapping::map_to_stack(local_type)
} else {
- MapToLocal(idx)
+ TempMapping::map_to_local(idx)
}
}
- }
+ };
+ self.set_temp_mapping(mapping_idx, tm);
}
- ctx.local_types[local_idx] = local_type;
+ // Update the type bits
+ let type_bits = local_type as u32;
+ assert!(type_bits <= 0b1111);
+ let mask_bits = 0b1111_u32 << (4 * local_idx);
+ let shifted_bits = type_bits << (4 * local_idx);
+ self.local_types = (self.local_types & !mask_bits) | shifted_bits;
}
/// Erase local variable type information
@@ -1789,19 +2069,27 @@ impl Context {
pub fn clear_local_types(&mut self) {
// When clearing local types we must detach any stack mappings to those
// locals. Even if local values may have changed, stack values will not.
- for (i, mapping) in self.temp_mapping.iter_mut().enumerate() {
- *mapping = match *mapping {
- MapToStack => MapToStack,
- MapToSelf => MapToSelf,
- MapToLocal(idx) => {
- self.temp_types[i] = self.local_types[idx as usize];
- MapToStack
- }
+
+ for mapping_idx in 0..MAX_TEMP_TYPES {
+ let mapping = self.get_temp_mapping(mapping_idx);
+ if mapping.get_kind() == MapToLocal {
+ let local_idx = mapping.get_local_idx() as usize;
+ self.set_temp_mapping(mapping_idx, TempMapping::map_to_stack(self.get_local_type(local_idx)));
}
}
// Clear the local types
- self.local_types = [Type::default(); MAX_LOCAL_TYPES];
+ self.local_types = 0;
+ }
+
+ /// Return true if the code is inlined by the caller
+ pub fn inline(&self) -> bool {
+ self.inline_block != 0
+ }
+
+ /// Set a block ISEQ given to the Block of this Context
+ pub fn set_inline_block(&mut self, iseq: IseqPtr) {
+ self.inline_block = iseq as u64
}
/// Compute a difference score for two context objects
@@ -1810,13 +2098,21 @@ impl Context {
let src = self;
// Can only lookup the first version in the chain
- if dst.chain_depth != 0 {
+ if dst.get_chain_depth() != 0 {
return TypeDiff::Incompatible;
}
// Blocks with depth > 0 always produce new versions
// Sidechains cannot overlap
- if src.chain_depth != 0 {
+ if src.get_chain_depth() != 0 {
+ return TypeDiff::Incompatible;
+ }
+
+ if src.is_return_landing() != dst.is_return_landing() {
+ return TypeDiff::Incompatible;
+ }
+
+ if src.is_deferred() != dst.is_deferred() {
return TypeDiff::Incompatible;
}
@@ -1841,10 +2137,17 @@ impl Context {
TypeDiff::Incompatible => return TypeDiff::Incompatible,
};
+ // Check the block to inline
+ if src.inline_block != dst.inline_block {
+ // find_block_version should not find existing blocks with different
+ // inline_block so that their yield will not be megamorphic.
+ return TypeDiff::Incompatible;
+ }
+
// For each local type we track
- for i in 0..src.local_types.len() {
- let t_src = src.local_types[i];
- let t_dst = dst.local_types[i];
+ for i in 0.. MAX_LOCAL_TYPES {
+ let t_src = src.get_local_type(i);
+ let t_dst = dst.get_local_type(i);
diff += match t_src.diff(t_dst) {
TypeDiff::Compatible(diff) => diff,
TypeDiff::Incompatible => return TypeDiff::Incompatible,
@@ -1853,12 +2156,12 @@ impl Context {
// For each value on the temp stack
for i in 0..src.stack_size {
- let (src_mapping, src_type) = src.get_opnd_mapping(StackOpnd(i));
- let (dst_mapping, dst_type) = dst.get_opnd_mapping(StackOpnd(i));
+ let src_mapping = src.get_opnd_mapping(StackOpnd(i));
+ let dst_mapping = dst.get_opnd_mapping(StackOpnd(i));
// If the two mappings aren't the same
if src_mapping != dst_mapping {
- if dst_mapping == MapToStack {
+ if dst_mapping.get_kind() == MapToStack {
// We can safely drop information about the source of the temp
// stack operand.
diff += 1;
@@ -1867,6 +2170,9 @@ impl Context {
}
}
+ let src_type = src.get_opnd_type(StackOpnd(i));
+ let dst_type = dst.get_opnd_type(StackOpnd(i));
+
diff += match src_type.diff(dst_type) {
TypeDiff::Compatible(diff) => diff,
TypeDiff::Incompatible => return TypeDiff::Incompatible,
@@ -1896,20 +2202,20 @@ impl Context {
impl Assembler {
/// Push one new value on the temp stack with an explicit mapping
/// Return a pointer to the new stack top
- pub fn stack_push_mapping(&mut self, (mapping, temp_type): (TempMapping, Type)) -> Opnd {
+ pub fn stack_push_mapping(&mut self, mapping: TempMapping) -> Opnd {
// If type propagation is disabled, store no types
if get_option!(no_type_prop) {
- return self.stack_push_mapping((mapping, Type::Unknown));
+ return self.stack_push_mapping(mapping.without_type());
}
let stack_size: usize = self.ctx.stack_size.into();
// Keep track of the type and mapping of the value
if stack_size < MAX_TEMP_TYPES {
- self.ctx.temp_mapping[stack_size] = mapping;
- self.ctx.temp_types[stack_size] = temp_type;
+ self.ctx.set_temp_mapping(stack_size, mapping);
- if let MapToLocal(idx) = mapping {
+ if mapping.get_kind() == MapToLocal {
+ let idx = mapping.get_local_idx();
assert!((idx as usize) < MAX_LOCAL_TYPES);
}
}
@@ -1928,12 +2234,12 @@ impl Assembler {
/// Push one new value on the temp stack
/// Return a pointer to the new stack top
pub fn stack_push(&mut self, val_type: Type) -> Opnd {
- return self.stack_push_mapping((MapToStack, val_type));
+ return self.stack_push_mapping(TempMapping::map_to_stack(val_type));
}
/// Push the self value on the stack
pub fn stack_push_self(&mut self) -> Opnd {
- return self.stack_push_mapping((MapToSelf, Type::Unknown));
+ return self.stack_push_mapping(TempMapping::map_to_self());
}
/// Push a local variable on the stack
@@ -1942,7 +2248,7 @@ impl Assembler {
return self.stack_push(Type::Unknown);
}
- return self.stack_push_mapping((MapToLocal((local_idx as u8).into()), Type::Unknown));
+ return self.stack_push_mapping(TempMapping::map_to_local(local_idx as u8));
}
// Pop N values off the stack
@@ -1957,8 +2263,7 @@ impl Assembler {
let idx: usize = (self.ctx.stack_size as usize) - i - 1;
if idx < MAX_TEMP_TYPES {
- self.ctx.temp_types[idx] = Type::Unknown;
- self.ctx.temp_mapping[idx] = MapToStack;
+ self.ctx.set_temp_mapping(idx, TempMapping::map_to_stack(Type::Unknown));
}
}
@@ -1972,12 +2277,16 @@ impl Assembler {
pub fn shift_stack(&mut self, argc: usize) {
assert!(argc < self.ctx.stack_size.into());
- let method_name_index = (self.ctx.stack_size as usize) - (argc as usize) - 1;
+ let method_name_index = (self.ctx.stack_size as usize) - argc - 1;
for i in method_name_index..(self.ctx.stack_size - 1) as usize {
- if i + 1 < MAX_TEMP_TYPES {
- self.ctx.temp_types[i] = self.ctx.temp_types[i + 1];
- self.ctx.temp_mapping[i] = self.ctx.temp_mapping[i + 1];
+ if i < MAX_TEMP_TYPES {
+ let next_arg_mapping = if i + 1 < MAX_TEMP_TYPES {
+ self.ctx.get_temp_mapping(i + 1)
+ } else {
+ TempMapping::map_to_stack(Type::Unknown)
+ };
+ self.ctx.set_temp_mapping(i, next_arg_mapping);
}
}
self.stack_pop(1);
@@ -2125,12 +2434,18 @@ fn gen_block_series_body(
/// Generate a block version that is an entry point inserted into an iseq
/// NOTE: this function assumes that the VM lock has been taken
-pub fn gen_entry_point(iseq: IseqPtr, ec: EcPtr) -> Option<CodePtr> {
+/// If jit_exception is true, compile JIT code for handling exceptions.
+/// See [jit_compile_exception] for details.
+pub fn gen_entry_point(iseq: IseqPtr, ec: EcPtr, jit_exception: bool) -> Option<*const u8> {
// Compute the current instruction index based on the current PC
+ let cfp = unsafe { get_ec_cfp(ec) };
let insn_idx: u16 = unsafe {
- let ec_pc = get_cfp_pc(get_ec_cfp(ec));
+ let ec_pc = get_cfp_pc(cfp);
iseq_pc_to_insn_idx(iseq, ec_pc)?
};
+ let stack_size: u8 = unsafe {
+ u8::try_from(get_cfp_sp(cfp).offset_from(get_cfp_bp(cfp))).ok()?
+ };
// The entry context makes no assumptions about types
let blockid = BlockId {
@@ -2143,10 +2458,12 @@ pub fn gen_entry_point(iseq: IseqPtr, ec: EcPtr) -> Option<CodePtr> {
let ocb = CodegenGlobals::get_outlined_cb();
// Write the interpreter entry prologue. Might be NULL when out of memory.
- let code_ptr = gen_entry_prologue(cb, ocb, iseq, insn_idx);
+ let code_ptr = gen_entry_prologue(cb, ocb, iseq, insn_idx, jit_exception);
// Try to generate code for the entry block
- let block = gen_block_series(blockid, &Context::default(), ec, cb, ocb);
+ let mut ctx = Context::default();
+ ctx.stack_size = stack_size;
+ let block = gen_block_series(blockid, &ctx, ec, cb, ocb);
cb.mark_all_executable();
ocb.unwrap().mark_all_executable();
@@ -2155,7 +2472,9 @@ pub fn gen_entry_point(iseq: IseqPtr, ec: EcPtr) -> Option<CodePtr> {
// Compilation failed
None => {
// Trigger code GC. This entry point will be recompiled later.
- cb.code_gc(ocb);
+ if get_option!(code_gc) {
+ cb.code_gc(ocb);
+ }
return None;
}
@@ -2168,14 +2487,17 @@ pub fn gen_entry_point(iseq: IseqPtr, ec: EcPtr) -> Option<CodePtr> {
}
}
+ // Count the number of entry points we compile
+ incr_counter!(compiled_iseq_entry);
+
// Compilation successful and block not empty
- return code_ptr;
+ code_ptr.map(|ptr| ptr.raw_ptr(cb))
}
// Change the entry's jump target from an entry stub to a next entry
pub fn regenerate_entry(cb: &mut CodeBlock, entryref: &EntryRef, next_entry: CodePtr) {
let mut asm = Assembler::new();
- asm.comment("regenerate_entry");
+ asm_comment!(asm, "regenerate_entry");
// gen_entry_guard generates cmp + jne. We're rewriting only jne.
asm.jne(next_entry.into());
@@ -2185,7 +2507,7 @@ pub fn regenerate_entry(cb: &mut CodeBlock, entryref: &EntryRef, next_entry: Cod
let old_dropped_bytes = cb.has_dropped_bytes();
cb.set_write_ptr(unsafe { entryref.as_ref() }.start_addr);
cb.set_dropped_bytes(false);
- asm.compile(cb, None);
+ asm.compile(cb, None).expect("can rewrite existing code");
// Rewind write_pos to the original one
assert_eq!(cb.get_write_ptr(), unsafe { entryref.as_ref() }.end_addr);
@@ -2209,78 +2531,88 @@ c_callable! {
/// Generated code calls this function with the SysV calling convention.
/// See [gen_call_entry_stub_hit].
fn entry_stub_hit(entry_ptr: *const c_void, ec: EcPtr) -> *const u8 {
- with_vm_lock(src_loc!(), || {
- match entry_stub_hit_body(entry_ptr, ec) {
- Some(addr) => addr,
- // Failed to service the stub by generating a new block so now we
- // need to exit to the interpreter at the stubbed location.
- None => return CodegenGlobals::get_stub_exit_code().raw_ptr(),
- }
+ with_compile_time(|| {
+ with_vm_lock(src_loc!(), || {
+ let cb = CodegenGlobals::get_inline_cb();
+ let ocb = CodegenGlobals::get_outlined_cb();
+
+ let addr = entry_stub_hit_body(entry_ptr, ec, cb, ocb)
+ .unwrap_or_else(|| {
+ // Trigger code GC (e.g. no space).
+ // This entry point will be recompiled later.
+ if get_option!(code_gc) {
+ cb.code_gc(ocb);
+ }
+ CodegenGlobals::get_stub_exit_code().raw_ptr(cb)
+ });
+
+ cb.mark_all_executable();
+ ocb.unwrap().mark_all_executable();
+
+ addr
+ })
})
}
}
/// Called by the generated code when an entry stub is executed
-fn entry_stub_hit_body(entry_ptr: *const c_void, ec: EcPtr) -> Option<*const u8> {
+fn entry_stub_hit_body(
+ entry_ptr: *const c_void,
+ ec: EcPtr,
+ cb: &mut CodeBlock,
+ ocb: &mut OutlinedCb
+) -> Option<*const u8> {
// Get ISEQ and insn_idx from the current ec->cfp
let cfp = unsafe { get_ec_cfp(ec) };
let iseq = unsafe { get_cfp_iseq(cfp) };
let insn_idx = iseq_pc_to_insn_idx(iseq, unsafe { get_cfp_pc(cfp) })?;
-
- let cb = CodegenGlobals::get_inline_cb();
- let ocb = CodegenGlobals::get_outlined_cb();
+ let stack_size: u8 = unsafe {
+ u8::try_from(get_cfp_sp(cfp).offset_from(get_cfp_bp(cfp))).ok()?
+ };
// Compile a new entry guard as a next entry
let next_entry = cb.get_write_ptr();
let mut asm = Assembler::new();
let pending_entry = gen_entry_chain_guard(&mut asm, ocb, iseq, insn_idx)?;
- asm.compile(cb, Some(ocb));
+ asm.compile(cb, Some(ocb))?;
- // Try to find an existing compiled version of this block
+ // Find or compile a block version
let blockid = BlockId { iseq, idx: insn_idx };
- let ctx = Context::default();
+ let mut ctx = Context::default();
+ ctx.stack_size = stack_size;
let blockref = match find_block_version(blockid, &ctx) {
// If an existing block is found, generate a jump to the block.
Some(blockref) => {
let mut asm = Assembler::new();
asm.jmp(unsafe { blockref.as_ref() }.start_addr.into());
- asm.compile(cb, Some(ocb));
- blockref
+ asm.compile(cb, Some(ocb))?;
+ Some(blockref)
}
// If this block hasn't yet been compiled, generate blocks after the entry guard.
- None => match gen_block_series(blockid, &ctx, ec, cb, ocb) {
- Some(blockref) => blockref,
- None => { // No space
- // Trigger code GC. This entry point will be recompiled later.
- cb.code_gc(ocb);
- return None;
- }
- }
+ None => gen_block_series(blockid, &ctx, ec, cb, ocb),
};
- // Regenerate the previous entry
- assert!(!entry_ptr.is_null());
- let entryref = NonNull::<Entry>::new(entry_ptr as *mut Entry).expect("Entry should not be null");
- regenerate_entry(cb, &entryref, next_entry);
-
- // Write an entry to the heap and push it to the ISEQ
- let pending_entry = Rc::try_unwrap(pending_entry).ok().expect("PendingEntry should be unique");
- get_or_create_iseq_payload(iseq).entries.push(pending_entry.into_entry());
+ // Commit or retry the entry
+ if blockref.is_some() {
+ // Regenerate the previous entry
+ let entryref = NonNull::<Entry>::new(entry_ptr as *mut Entry).expect("Entry should not be null");
+ regenerate_entry(cb, &entryref, next_entry);
- cb.mark_all_executable();
- ocb.unwrap().mark_all_executable();
+ // Write an entry to the heap and push it to the ISEQ
+ let pending_entry = Rc::try_unwrap(pending_entry).ok().expect("PendingEntry should be unique");
+ get_or_create_iseq_payload(iseq).entries.push(pending_entry.into_entry());
+ }
// Let the stub jump to the block
- Some(unsafe { blockref.as_ref() }.start_addr.raw_ptr())
+ blockref.map(|block| unsafe { block.as_ref() }.start_addr.raw_ptr(cb))
}
/// Generate a stub that calls entry_stub_hit
pub fn gen_entry_stub(entry_address: usize, ocb: &mut OutlinedCb) -> Option<CodePtr> {
let ocb = ocb.unwrap();
- let stub_addr = ocb.get_write_ptr();
let mut asm = Assembler::new();
- asm.comment("entry stub hit");
+ asm_comment!(asm, "entry stub hit");
asm.mov(C_ARG_OPNDS[0], entry_address.into());
@@ -2288,32 +2620,23 @@ pub fn gen_entry_stub(entry_address: usize, ocb: &mut OutlinedCb) -> Option<Code
// Not really a side exit, just don't need a padded jump here.
asm.jmp(CodegenGlobals::get_entry_stub_hit_trampoline().as_side_exit());
- asm.compile(ocb, None);
-
- if ocb.has_dropped_bytes() {
- return None; // No space
- } else {
- return Some(stub_addr);
- }
+ asm.compile(ocb, None).map(|(code_ptr, _)| code_ptr)
}
/// A trampoline used by gen_entry_stub. entry_stub_hit may issue Code GC, so
/// it's useful for Code GC to call entry_stub_hit from a globally shared code.
-pub fn gen_entry_stub_hit_trampoline(ocb: &mut OutlinedCb) -> CodePtr {
+pub fn gen_entry_stub_hit_trampoline(ocb: &mut OutlinedCb) -> Option<CodePtr> {
let ocb = ocb.unwrap();
- let code_ptr = ocb.get_write_ptr();
let mut asm = Assembler::new();
// See gen_entry_guard for how it's used.
- asm.comment("entry_stub_hit() trampoline");
+ asm_comment!(asm, "entry_stub_hit() trampoline");
let jump_addr = asm.ccall(entry_stub_hit as *mut u8, vec![C_ARG_OPNDS[0], EC]);
// Jump to the address returned by the entry_stub_hit() call
asm.jmp_opnd(jump_addr);
- asm.compile(ocb, None);
-
- code_ptr
+ asm.compile(ocb, None).map(|(code_ptr, _)| code_ptr)
}
/// Generate code for a branch, possibly rewriting and changing the size of it
@@ -2328,19 +2651,25 @@ fn regenerate_branch(cb: &mut CodeBlock, branch: &Branch) {
// Generate the branch
let mut asm = Assembler::new();
- asm.comment("regenerate_branch");
+ asm_comment!(asm, "regenerate_branch");
branch.gen_fn.call(
&mut asm,
Target::CodePtr(branch.get_target_address(0).unwrap()),
branch.get_target_address(1).map(|addr| Target::CodePtr(addr)),
);
+ // If the entire block is the branch and the block could be invalidated,
+ // we need to pad to ensure there is room for invalidation patching.
+ if branch.start_addr == block.start_addr && branch_terminates_block && block.entry_exit.is_some() {
+ asm.pad_inval_patch();
+ }
+
// Rewrite the branch
let old_write_pos = cb.get_write_pos();
let old_dropped_bytes = cb.has_dropped_bytes();
cb.set_write_ptr(branch.start_addr);
cb.set_dropped_bytes(false);
- asm.compile(cb, None);
+ asm.compile(cb, None).expect("can rewrite existing code");
let new_end_addr = cb.get_write_ptr();
branch.end_addr.set(new_end_addr);
@@ -2399,7 +2728,7 @@ c_callable! {
ec: EcPtr,
) -> *const u8 {
with_vm_lock(src_loc!(), || {
- branch_stub_hit_body(branch_ptr, target_idx, ec)
+ with_compile_time(|| { branch_stub_hit_body(branch_ptr, target_idx, ec) })
})
}
}
@@ -2427,6 +2756,9 @@ fn branch_stub_hit_body(branch_ptr: *const c_void, target_idx: u32, ec: EcPtr) -
_ => unreachable!("target_idx < 2 must always hold"),
};
+ let cb = CodegenGlobals::get_inline_cb();
+ let ocb = CodegenGlobals::get_outlined_cb();
+
let (target_blockid, target_ctx): (BlockId, Context) = unsafe {
// SAFETY: no mutation of the target's Cell. Just reading out data.
let target = branch.targets[target_idx].ref_unchecked().as_ref().unwrap();
@@ -2434,24 +2766,24 @@ fn branch_stub_hit_body(branch_ptr: *const c_void, target_idx: u32, ec: EcPtr) -
// If this branch has already been patched, return the dst address
// Note: recursion can cause the same stub to be hit multiple times
if let BranchTarget::Block(_) = target.as_ref() {
- return target.get_address().unwrap().raw_ptr();
+ return target.get_address().unwrap().raw_ptr(cb);
}
(target.get_blockid(), target.get_ctx())
};
- let cb = CodegenGlobals::get_inline_cb();
- let ocb = CodegenGlobals::get_outlined_cb();
-
let (cfp, original_interp_sp) = unsafe {
let cfp = get_ec_cfp(ec);
let original_interp_sp = get_cfp_sp(cfp);
- let running_iseq = rb_cfp_get_iseq(cfp);
+ let running_iseq = get_cfp_iseq(cfp);
+ assert_eq!(running_iseq, target_blockid.iseq as _, "each stub expects a particular iseq");
+
let reconned_pc = rb_iseq_pc_at_idx(running_iseq, target_blockid.idx.into());
let reconned_sp = original_interp_sp.offset(target_ctx.sp_offset.into());
-
- assert_eq!(running_iseq, target_blockid.iseq as _, "each stub expects a particular iseq");
+ // Unlike in the interpreter, our `leave` doesn't write to the caller's
+ // SP -- we do it in the returned-to code. Account for this difference.
+ let reconned_sp = reconned_sp.add(target_ctx.is_return_landing().into());
// Update the PC in the current CFP, because it may be out of sync in JITted code
rb_set_cfp_pc(cfp, reconned_pc);
@@ -2464,6 +2796,17 @@ fn branch_stub_hit_body(branch_ptr: *const c_void, target_idx: u32, ec: EcPtr) -
// So we do it here instead.
rb_set_cfp_sp(cfp, reconned_sp);
+ // Bail if code GC is disabled and we've already run out of spaces.
+ if !get_option!(code_gc) && (cb.has_dropped_bytes() || ocb.unwrap().has_dropped_bytes()) {
+ return CodegenGlobals::get_stub_exit_code().raw_ptr(cb);
+ }
+
+ // Bail if we're about to run out of native stack space.
+ // We've just reconstructed interpreter state.
+ if rb_ec_stack_check(ec as _) != 0 {
+ return CodegenGlobals::get_stub_exit_code().raw_ptr(cb);
+ }
+
(cfp, original_interp_sp)
};
@@ -2474,7 +2817,6 @@ fn branch_stub_hit_body(branch_ptr: *const c_void, target_idx: u32, ec: EcPtr) -
if block.is_none() {
let branch_old_shape = branch.gen_fn.get_shape();
-
// If the new block can be generated right after the branch (at cb->write_pos)
if cb.get_write_ptr() == branch.end_addr.get() {
// This branch should be terminating its block
@@ -2532,7 +2874,9 @@ fn branch_stub_hit_body(branch_ptr: *const c_void, target_idx: u32, ec: EcPtr) -
// because incomplete code could be used when cb.dropped_bytes is flipped
// by code GC. So this place, after all compilation, is the safest place
// to hook code GC on branch_stub_hit.
- cb.code_gc(ocb);
+ if get_option!(code_gc) {
+ cb.code_gc(ocb);
+ }
// Failed to service the stub by generating a new block so now we
// need to exit to the interpreter at the stubbed location. We are
@@ -2552,11 +2896,11 @@ fn branch_stub_hit_body(branch_ptr: *const c_void, target_idx: u32, ec: EcPtr) -
assert!(
new_branch_size <= branch_size_on_entry,
"branch stubs should never enlarge branches (start_addr: {:?}, old_size: {}, new_size: {})",
- branch.start_addr.raw_ptr(), branch_size_on_entry, new_branch_size,
+ branch.start_addr.raw_ptr(cb), branch_size_on_entry, new_branch_size,
);
// Return a pointer to the compiled block version
- dst_addr.raw_ptr()
+ dst_addr.raw_ptr(cb)
}
/// Generate a "stub", a piece of code that calls the compiler back when run.
@@ -2569,18 +2913,21 @@ fn gen_branch_stub(
) -> Option<CodePtr> {
let ocb = ocb.unwrap();
- // Generate an outlined stub that will call branch_stub_hit()
- let stub_addr = ocb.get_write_ptr();
-
let mut asm = Assembler::new();
- asm.ctx = ctx.clone();
+ asm.ctx = *ctx;
asm.set_reg_temps(ctx.reg_temps);
- asm.comment("branch stub hit");
+ asm_comment!(asm, "branch stub hit");
+
+ if asm.ctx.is_return_landing() {
+ asm.mov(SP, Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SP));
+ let top = asm.stack_push(Type::Unknown);
+ asm.mov(top, C_RET_OPND);
+ }
// Save caller-saved registers before C_ARG_OPNDS get clobbered.
// Spill all registers for consistency with the trampoline.
- for &reg in caller_saved_temp_regs().iter() {
- asm.cpush(reg);
+ for &reg in caller_saved_temp_regs() {
+ asm.cpush(Opnd::Reg(reg));
}
// Spill temps to the VM stack as well for jit.peek_at_stack()
@@ -2599,19 +2946,11 @@ fn gen_branch_stub(
// Not really a side exit, just don't need a padded jump here.
asm.jmp(CodegenGlobals::get_branch_stub_hit_trampoline().as_side_exit());
- asm.compile(ocb, None);
-
- if ocb.has_dropped_bytes() {
- // No space
- None
- } else {
- Some(stub_addr)
- }
+ asm.compile(ocb, None).map(|(code_ptr, _)| code_ptr)
}
-pub fn gen_branch_stub_hit_trampoline(ocb: &mut OutlinedCb) -> CodePtr {
+pub fn gen_branch_stub_hit_trampoline(ocb: &mut OutlinedCb) -> Option<CodePtr> {
let ocb = ocb.unwrap();
- let code_ptr = ocb.get_write_ptr();
let mut asm = Assembler::new();
// For `branch_stub_hit(branch_ptr, target_idx, ec)`,
@@ -2620,8 +2959,8 @@ pub fn gen_branch_stub_hit_trampoline(ocb: &mut OutlinedCb) -> CodePtr {
// is the unchanging part.
// Since this trampoline is static, it allows code GC inside
// branch_stub_hit() to free stubs without problems.
- asm.comment("branch_stub_hit() trampoline");
- let jump_addr = asm.ccall(
+ asm_comment!(asm, "branch_stub_hit() trampoline");
+ let stub_hit_ret = asm.ccall(
branch_stub_hit as *mut u8,
vec![
C_ARG_OPNDS[0],
@@ -2629,28 +2968,39 @@ pub fn gen_branch_stub_hit_trampoline(ocb: &mut OutlinedCb) -> CodePtr {
EC,
]
);
+ let jump_addr = asm.load(stub_hit_ret);
// Restore caller-saved registers for stack temps
- for &reg in caller_saved_temp_regs().iter().rev() {
- asm.cpop_into(reg);
+ for &reg in caller_saved_temp_regs().rev() {
+ asm.cpop_into(Opnd::Reg(reg));
}
// Jump to the address returned by the branch_stub_hit() call
asm.jmp_opnd(jump_addr);
- asm.compile(ocb, None);
+ // HACK: popping into C_RET_REG clobbers the return value of branch_stub_hit() we need to jump
+ // to, so we need a scratch register to preserve it. This extends the live range of the C
+ // return register so we get something else for the return value.
+ let _ = asm.live_reg_opnd(stub_hit_ret);
- code_ptr
+ asm.compile(ocb, None).map(|(code_ptr, _)| code_ptr)
}
/// Return registers to be pushed and popped on branch_stub_hit.
-/// The return value may include an extra register for x86 alignment.
-fn caller_saved_temp_regs() -> Vec<Opnd> {
- let mut regs = Assembler::get_temp_regs();
- if regs.len() % 2 == 1 {
- regs.push(*regs.last().unwrap()); // x86 alignment
+pub fn caller_saved_temp_regs() -> impl Iterator<Item = &'static Reg> + DoubleEndedIterator {
+ let temp_regs = Assembler::get_temp_regs().iter();
+ let len = temp_regs.len();
+ // The return value gen_leave() leaves in C_RET_REG
+ // needs to survive the branch_stub_hit() call.
+ let regs = temp_regs.chain(std::iter::once(&C_RET_REG));
+
+ // On x86_64, maintain 16-byte stack alignment
+ if cfg!(target_arch = "x86_64") && len % 2 == 0 {
+ static ONE_MORE: [Reg; 1] = [C_RET_REG];
+ regs.chain(ONE_MORE.iter())
+ } else {
+ regs.chain(&[])
}
- regs.iter().map(|&reg| Opnd::Reg(reg)).collect()
}
impl Assembler
@@ -2661,7 +3011,7 @@ impl Assembler
// so that we can move the closure below
let entryref = entryref.clone();
- self.pos_marker(move |code_ptr| {
+ self.pos_marker(move |code_ptr, _| {
entryref.start_addr.set(Some(code_ptr));
});
}
@@ -2672,7 +3022,7 @@ impl Assembler
// so that we can move the closure below
let entryref = entryref.clone();
- self.pos_marker(move |code_ptr| {
+ self.pos_marker(move |code_ptr, _| {
entryref.end_addr.set(Some(code_ptr));
});
}
@@ -2684,7 +3034,7 @@ impl Assembler
// so that we can move the closure below
let branchref = branchref.clone();
- self.pos_marker(move |code_ptr| {
+ self.pos_marker(move |code_ptr, _| {
branchref.start_addr.set(Some(code_ptr));
});
}
@@ -2696,7 +3046,7 @@ impl Assembler
// so that we can move the closure below
let branchref = branchref.clone();
- self.pos_marker(move |code_ptr| {
+ self.pos_marker(move |code_ptr, _| {
branchref.end_addr.set(Some(code_ptr));
});
}
@@ -2745,7 +3095,7 @@ pub fn gen_direct_jump(jit: &mut JITState, ctx: &Context, target0: BlockId, asm:
let block_addr = block.start_addr;
// Call the branch generation function
- asm.comment("gen_direct_jmp: existing block");
+ asm_comment!(asm, "gen_direct_jmp: existing block");
asm.mark_branch_start(&branch);
branch.gen_fn.call(asm, Target::CodePtr(block_addr), None);
asm.mark_branch_end(&branch);
@@ -2753,7 +3103,7 @@ pub fn gen_direct_jump(jit: &mut JITState, ctx: &Context, target0: BlockId, asm:
BranchTarget::Block(blockref)
} else {
// The branch is effectively empty (a noop)
- asm.comment("gen_direct_jmp: fallthrough");
+ asm_comment!(asm, "gen_direct_jmp: fallthrough");
asm.mark_branch_start(&branch);
asm.mark_branch_end(&branch);
branch.gen_fn.set_shape(BranchShape::Next0);
@@ -2762,7 +3112,7 @@ pub fn gen_direct_jump(jit: &mut JITState, ctx: &Context, target0: BlockId, asm:
// compile the target block right after this one (fallthrough).
BranchTarget::Stub(Box::new(BranchStub {
address: None,
- ctx: ctx.clone(),
+ ctx: *ctx,
iseq: Cell::new(target0.iseq),
iseq_idx: target0.idx,
}))
@@ -2777,16 +3127,13 @@ pub fn defer_compilation(
asm: &mut Assembler,
ocb: &mut OutlinedCb,
) {
- if asm.ctx.chain_depth != 0 {
+ if asm.ctx.is_deferred() {
panic!("Double defer!");
}
- let mut next_ctx = asm.ctx.clone();
+ let mut next_ctx = asm.ctx;
- if next_ctx.chain_depth == u8::MAX {
- panic!("max block version chain depth reached!");
- }
- next_ctx.chain_depth += 1;
+ next_ctx.mark_as_deferred();
let branch = new_pending_branch(jit, BranchGenFn::JumpToTarget0(Cell::new(BranchShape::Default)));
@@ -2798,8 +3145,14 @@ pub fn defer_compilation(
// Likely a stub due to the increased chain depth
let target0_address = branch.set_target(0, blockid, &next_ctx, ocb);
+ // Pad the block if it has the potential to be invalidated. This must be
+ // done before gen_fn() in case the jump is overwritten by a fallthrough.
+ if jit.block_entry_exit.is_some() {
+ asm.pad_inval_patch();
+ }
+
// Call the branch generation function
- asm.comment("defer_compilation");
+ asm_comment!(asm, "defer_compilation");
asm.mark_branch_start(&branch);
if let Some(dst_addr) = target0_address {
branch.gen_fn.call(asm, Target::CodePtr(dst_addr), None);
@@ -2951,7 +3304,7 @@ pub fn invalidate_block_version(blockref: &BlockRef) {
// Get a pointer to the generated code for this block
let block_start = block.start_addr;
- // Make the the start of the block do an exit. This handles OOM situations
+ // Make the start of the block do an exit. This handles OOM situations
// and some cases where we can't efficiently patch incoming branches.
// Do this first, since in case there is a fallthrough branch into this
// block, the patching loop below can overwrite the start of the block.
@@ -2977,13 +3330,14 @@ pub fn invalidate_block_version(blockref: &BlockRef) {
let mut asm = Assembler::new();
asm.jmp(block_entry_exit.as_side_exit());
cb.set_dropped_bytes(false);
- asm.compile(&mut cb, Some(ocb));
+ asm.compile(&mut cb, Some(ocb)).expect("can rewrite existing code");
assert!(
cb.get_write_ptr() <= block_end,
- "invalidation wrote past end of block (code_size: {:?}, new_size: {})",
+ "invalidation wrote past end of block (code_size: {:?}, new_size: {}, start_addr: {:?})",
block.code_size(),
- cb.get_write_ptr().into_i64() - block_start.into_i64(),
+ cb.get_write_ptr().as_offset() - block_start.as_offset(),
+ block.start_addr.raw_ptr(cb),
);
cb.set_write_ptr(cur_pos);
cb.set_dropped_bytes(cur_dropped_bytes);
@@ -3024,7 +3378,7 @@ pub fn invalidate_block_version(blockref: &BlockRef) {
address: Some(stub_addr),
iseq: block.iseq.clone(),
iseq_idx: block.iseq_range.start,
- ctx: block.ctx.clone(),
+ ctx: block.ctx,
})))));
// Check if the invalidated block immediately follows
@@ -3047,7 +3401,7 @@ pub fn invalidate_block_version(blockref: &BlockRef) {
if !target_next && branch.code_size() > old_branch_size {
panic!(
"invalidated branch grew in size (start_addr: {:?}, old_size: {}, new_size: {})",
- branch.start_addr.raw_ptr(), old_branch_size, branch.code_size()
+ branch.start_addr.raw_ptr(cb), old_branch_size, branch.code_size()
);
}
}
@@ -3089,9 +3443,9 @@ pub fn invalidate_block_version(blockref: &BlockRef) {
// invalidated branch pointers. Example:
// def foo(n)
// if n == 2
-// # 1.times{} to use a cfunc to avoid exiting from the
-// # frame which will use the retained return address
-// return 1.times { Object.define_method(:foo) {} }
+// # 1.times.each to create a cfunc frame to preserve the JIT frame
+// # which will return to a stub housed in an invalidated block
+// return 1.times.each { Object.define_method(:foo) {} }
// end
//
// foo(n + 1)
@@ -3139,6 +3493,65 @@ mod tests {
use crate::core::*;
#[test]
+ fn type_size() {
+ // Check that we can store types in 4 bits,
+ // and all local types in 32 bits
+ assert_eq!(mem::size_of::<Type>(), 1);
+ assert!(Type::BlockParamProxy as usize <= 0b1111);
+ assert!(MAX_LOCAL_TYPES * 4 <= 32);
+ }
+
+ #[test]
+ fn tempmapping_size() {
+ assert_eq!(mem::size_of::<TempMapping>(), 1);
+ }
+
+ #[test]
+ fn local_types() {
+ let mut ctx = Context::default();
+
+ for i in 0..MAX_LOCAL_TYPES {
+ ctx.set_local_type(i, Type::Fixnum);
+ assert_eq!(ctx.get_local_type(i), Type::Fixnum);
+ ctx.set_local_type(i, Type::BlockParamProxy);
+ assert_eq!(ctx.get_local_type(i), Type::BlockParamProxy);
+ }
+
+ ctx.set_local_type(0, Type::Fixnum);
+ ctx.clear_local_types();
+ assert!(ctx.get_local_type(0) == Type::Unknown);
+
+ // Make sure we don't accidentally set bits incorrectly
+ let mut ctx = Context::default();
+ ctx.set_local_type(0, Type::Fixnum);
+ assert_eq!(ctx.get_local_type(0), Type::Fixnum);
+ ctx.set_local_type(2, Type::Fixnum);
+ ctx.set_local_type(1, Type::BlockParamProxy);
+ assert_eq!(ctx.get_local_type(0), Type::Fixnum);
+ assert_eq!(ctx.get_local_type(2), Type::Fixnum);
+ }
+
+ #[test]
+ fn tempmapping() {
+ let t = TempMapping::map_to_stack(Type::Unknown);
+ assert_eq!(t.get_kind(), MapToStack);
+ assert_eq!(t.get_type(), Type::Unknown);
+
+ let t = TempMapping::map_to_stack(Type::TString);
+ assert_eq!(t.get_kind(), MapToStack);
+ assert_eq!(t.get_type(), Type::TString);
+
+ let t = TempMapping::map_to_local(7);
+ assert_eq!(t.get_kind(), MapToLocal);
+ assert_eq!(t.get_local_idx(), 7);
+ }
+
+ #[test]
+ fn context_size() {
+ assert_eq!(mem::size_of::<Context>(), 23);
+ }
+
+ #[test]
fn types() {
// Valid src => dst
assert_eq!(Type::Unknown.diff(Type::Unknown), TypeDiff::Compatible(0));
@@ -3162,7 +3575,7 @@ mod tests {
assert_eq!(reg_temps.get(stack_idx), false);
}
- // Set 0, 2, 7
+ // Set 0, 2, 7 (RegTemps: 10100001)
reg_temps.set(0, true);
reg_temps.set(2, true);
reg_temps.set(3, true);
@@ -3178,6 +3591,17 @@ mod tests {
assert_eq!(reg_temps.get(5), false);
assert_eq!(reg_temps.get(6), false);
assert_eq!(reg_temps.get(7), true);
+
+ // Test conflicts
+ assert_eq!(5, get_option!(num_temp_regs));
+ assert_eq!(reg_temps.conflicts_with(0), false); // already set, but no conflict
+ assert_eq!(reg_temps.conflicts_with(1), false);
+ assert_eq!(reg_temps.conflicts_with(2), true); // already set, and conflicts with 7
+ assert_eq!(reg_temps.conflicts_with(3), false);
+ assert_eq!(reg_temps.conflicts_with(4), false);
+ assert_eq!(reg_temps.conflicts_with(5), true); // not set, and will conflict with 0
+ assert_eq!(reg_temps.conflicts_with(6), false);
+ assert_eq!(reg_temps.conflicts_with(7), true); // already set, and conflicts with 2
}
#[test]
@@ -3195,6 +3619,60 @@ mod tests {
}
#[test]
+ fn context_upgrade_local() {
+ let mut asm = Assembler::new();
+ asm.stack_push_local(0);
+ asm.ctx.upgrade_opnd_type(StackOpnd(0), Type::Nil);
+ assert_eq!(Type::Nil, asm.ctx.get_opnd_type(StackOpnd(0)));
+ }
+
+ #[test]
+ fn context_chain_depth() {
+ let mut ctx = Context::default();
+ assert_eq!(ctx.get_chain_depth(), 0);
+ assert_eq!(ctx.is_return_landing(), false);
+ assert_eq!(ctx.is_deferred(), false);
+
+ for _ in 0..5 {
+ ctx.increment_chain_depth();
+ }
+ assert_eq!(ctx.get_chain_depth(), 5);
+
+ ctx.set_as_return_landing();
+ assert_eq!(ctx.is_return_landing(), true);
+
+ ctx.clear_return_landing();
+ assert_eq!(ctx.is_return_landing(), false);
+
+ ctx.mark_as_deferred();
+ assert_eq!(ctx.is_deferred(), true);
+
+ ctx.reset_chain_depth_and_defer();
+ assert_eq!(ctx.get_chain_depth(), 0);
+ assert_eq!(ctx.is_deferred(), false);
+ }
+
+ #[test]
+ fn shift_stack_for_send() {
+ let mut asm = Assembler::new();
+
+ // Push values to simulate send(:name, arg) with 6 items already on-stack
+ for _ in 0..6 {
+ asm.stack_push(Type::Fixnum);
+ }
+ asm.stack_push(Type::Unknown);
+ asm.stack_push(Type::ImmSymbol);
+ asm.stack_push(Type::Unknown);
+
+ // This method takes argc of the sendee, not argc of send
+ asm.shift_stack(1);
+
+ // The symbol should be gone
+ assert_eq!(Type::Unknown, asm.ctx.get_opnd_type(StackOpnd(0)));
+ assert_eq!(Type::Unknown, asm.ctx.get_opnd_type(StackOpnd(1)));
+ }
+
+ #[test]
fn test_miri_ref_unchecked() {
let blockid = BlockId {
iseq: ptr::null(),
diff --git a/yjit/src/cruby.rs b/yjit/src/cruby.rs
index 265748cd6d..68c0304b06 100644
--- a/yjit/src/cruby.rs
+++ b/yjit/src/cruby.rs
@@ -83,7 +83,7 @@
#![allow(non_upper_case_globals)]
use std::convert::From;
-use std::ffi::CString;
+use std::ffi::{CString, CStr};
use std::os::raw::{c_char, c_int, c_uint};
use std::panic::{catch_unwind, UnwindSafe};
@@ -96,7 +96,7 @@ pub type size_t = u64;
pub type RedefinitionFlag = u32;
#[allow(dead_code)]
-#[allow(clippy::useless_transmute)]
+#[allow(clippy::all)]
mod autogened {
use super::*;
// Textually include output from rust-bindgen as suggested by its user guide.
@@ -107,13 +107,20 @@ pub use autogened::*;
// TODO: For #defines that affect memory layout, we need to check for them
// on build and fail if they're wrong. e.g. USE_FLONUM *must* be true.
-// These are functions we expose from vm_insnhelper.c, not in any header.
+// These are functions we expose from C files, not in any header.
// Parsing it would result in a lot of duplicate definitions.
// Use bindgen for functions that are defined in headers or in yjit.c.
#[cfg_attr(test, allow(unused))] // We don't link against C code when testing
extern "C" {
+ pub fn rb_check_overloaded_cme(
+ me: *const rb_callable_method_entry_t,
+ ci: *const rb_callinfo,
+ ) -> *const rb_callable_method_entry_t;
+ pub fn rb_hash_empty_p(hash: VALUE) -> VALUE;
+ pub fn rb_str_setbyte(str: VALUE, index: VALUE, value: VALUE) -> VALUE;
pub fn rb_vm_splat_array(flag: VALUE, ary: VALUE) -> VALUE;
pub fn rb_vm_concat_array(ary1: VALUE, ary2st: VALUE) -> VALUE;
+ pub fn rb_vm_concat_to_array(ary1: VALUE, ary2st: VALUE) -> VALUE;
pub fn rb_vm_defined(
ec: EcPtr,
reg_cfp: CfpPtr,
@@ -135,13 +142,12 @@ extern "C" {
ic: ICVARC,
) -> VALUE;
pub fn rb_vm_ic_hit_p(ic: IC, reg_ep: *const VALUE) -> bool;
- pub fn rb_str_bytesize(str: VALUE) -> VALUE;
+ pub fn rb_vm_stack_canary() -> VALUE;
+ pub fn rb_vm_push_cfunc_frame(cme: *const rb_callable_method_entry_t, recv_idx: c_int);
}
// Renames
pub use rb_insn_name as raw_insn_name;
-pub use rb_insn_len as raw_insn_len;
-pub use rb_yarv_class_of as CLASS_OF;
pub use rb_get_ec_cfp as get_ec_cfp;
pub use rb_get_cfp_iseq as get_cfp_iseq;
pub use rb_get_cfp_pc as get_cfp_pc;
@@ -149,6 +155,7 @@ pub use rb_get_cfp_sp as get_cfp_sp;
pub use rb_get_cfp_self as get_cfp_self;
pub use rb_get_cfp_ep as get_cfp_ep;
pub use rb_get_cfp_ep_level as get_cfp_ep_level;
+pub use rb_vm_base_ptr as get_cfp_bp;
pub use rb_get_cme_def_type as get_cme_def_type;
pub use rb_get_cme_def_body_attr_id as get_cme_def_body_attr_id;
pub use rb_get_cme_def_body_optimized_type as get_cme_def_body_optimized_type;
@@ -163,11 +170,11 @@ pub use rb_iseq_encoded_size as get_iseq_encoded_size;
pub use rb_get_iseq_body_local_iseq as get_iseq_body_local_iseq;
pub use rb_get_iseq_body_iseq_encoded as get_iseq_body_iseq_encoded;
pub use rb_get_iseq_body_stack_max as get_iseq_body_stack_max;
+pub use rb_get_iseq_body_type as get_iseq_body_type;
pub use rb_get_iseq_flags_has_lead as get_iseq_flags_has_lead;
pub use rb_get_iseq_flags_has_opt as get_iseq_flags_has_opt;
pub use rb_get_iseq_flags_has_kw as get_iseq_flags_has_kw;
pub use rb_get_iseq_flags_has_rest as get_iseq_flags_has_rest;
-pub use rb_get_iseq_flags_ruby2_keywords as get_iseq_flags_ruby2_keywords;
pub use rb_get_iseq_flags_has_post as get_iseq_flags_has_post;
pub use rb_get_iseq_flags_has_kwrest as get_iseq_flags_has_kwrest;
pub use rb_get_iseq_flags_has_block as get_iseq_flags_has_block;
@@ -186,7 +193,6 @@ pub use rb_yarv_str_eql_internal as rb_str_eql_internal;
pub use rb_yarv_ary_entry_internal as rb_ary_entry_internal;
pub use rb_yjit_fix_div_fix as rb_fix_div_fix;
pub use rb_yjit_fix_mod_fix as rb_fix_mod_fix;
-pub use rb_yjit_fix_mul_fix as rb_fix_mul_fix;
pub use rb_FL_TEST as FL_TEST;
pub use rb_FL_TEST_RAW as FL_TEST_RAW;
pub use rb_RB_TYPE_P as RB_TYPE_P;
@@ -202,8 +208,6 @@ pub use rb_RCLASS_ORIGIN as RCLASS_ORIGIN;
/// Helper so we can get a Rust string for insn_name()
pub fn insn_name(opcode: usize) -> String {
- use std::ffi::CStr;
-
unsafe {
// Look up Ruby's NULL-terminated insn name string
let op_name = raw_insn_name(VALUE(opcode));
@@ -223,7 +227,7 @@ pub fn insn_len(opcode: usize) -> u32 {
#[cfg(not(test))]
unsafe {
- raw_insn_len(VALUE(opcode)).try_into().unwrap()
+ rb_insn_len(VALUE(opcode)).try_into().unwrap()
}
}
@@ -252,6 +256,24 @@ pub fn iseq_pc_to_insn_idx(iseq: IseqPtr, pc: *mut VALUE) -> Option<u16> {
unsafe { pc.offset_from(pc_zero) }.try_into().ok()
}
+/// Given an ISEQ pointer and an instruction index, return an opcode.
+pub fn iseq_opcode_at_idx(iseq: IseqPtr, insn_idx: u32) -> u32 {
+ let pc = unsafe { rb_iseq_pc_at_idx(iseq, insn_idx) };
+ unsafe { rb_iseq_opcode_at_pc(iseq, pc) as u32 }
+}
+
+/// Return a poison value to be set above the stack top to verify leafness.
+#[cfg(not(test))]
+pub fn vm_stack_canary() -> u64 {
+ unsafe { rb_vm_stack_canary() }.as_u64()
+}
+
+/// Avoid linking the C function in `cargo test`
+#[cfg(test)]
+pub fn vm_stack_canary() -> u64 {
+ 0
+}
+
/// Opaque execution-context type from vm_core.h
#[repr(C)]
pub struct rb_execution_context_struct {
@@ -286,13 +308,6 @@ pub struct rb_callcache {
_marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
}
-/// Opaque call-info type from vm_callinfo.h
-#[repr(C)]
-pub struct rb_callinfo_kwarg {
- _data: [u8; 0],
- _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
-}
-
/// Opaque control_frame (CFP) struct from vm_core.h
#[repr(C)]
pub struct rb_control_frame_struct {
@@ -376,6 +391,11 @@ impl VALUE {
}
}
+ /// Returns true if the value is T_HASH
+ pub fn hash_p(self) -> bool {
+ !self.special_const_p() && self.builtin_type() == RUBY_T_HASH
+ }
+
/// Returns true or false depending on whether the value is nil
pub fn nil_p(self) -> bool {
self == Qnil
@@ -400,7 +420,13 @@ impl VALUE {
}
pub fn class_of(self) -> VALUE {
- unsafe { CLASS_OF(self) }
+ if !self.special_const_p() {
+ let builtin_type = self.builtin_type();
+ assert_ne!(builtin_type, RUBY_T_NONE, "YJIT should only see live objects");
+ assert_ne!(builtin_type, RUBY_T_MOVED, "YJIT should only see live objects");
+ }
+
+ unsafe { rb_yarv_class_of(self) }
}
pub fn is_frozen(self) -> bool {
@@ -577,11 +603,9 @@ pub fn rust_str_to_sym(str: &str) -> VALUE {
}
/// Produce an owned Rust String from a C char pointer
-#[cfg(feature = "disasm")]
pub fn cstr_to_rust_string(c_char_ptr: *const c_char) -> Option<String> {
assert!(c_char_ptr != std::ptr::null());
- use std::ffi::CStr;
let c_str: &CStr = unsafe { CStr::from_ptr(c_char_ptr) };
match c_str.to_str() {
@@ -593,17 +617,20 @@ pub fn cstr_to_rust_string(c_char_ptr: *const c_char) -> Option<String> {
/// A location in Rust code for integrating with debugging facilities defined in C.
/// Use the [src_loc!] macro to crate an instance.
pub struct SourceLocation {
- pub file: CString,
+ pub file: &'static CStr,
pub line: c_int,
}
/// Make a [SourceLocation] at the current spot.
macro_rules! src_loc {
() => {
- // NOTE(alan): `CString::new` allocates so we might want to limit this to debug builds.
- $crate::cruby::SourceLocation {
- file: std::ffi::CString::new(file!()).unwrap(), // ASCII source file paths
- line: line!().try_into().unwrap(), // not that many lines
+ {
+ // Nul-terminated string with static lifetime, make a CStr out of it safely.
+ let file: &'static str = concat!(file!(), '\0');
+ $crate::cruby::SourceLocation {
+ file: unsafe { std::ffi::CStr::from_ptr(file.as_ptr().cast()) },
+ line: line!().try_into().unwrap(),
+ }
}
};
}
@@ -641,17 +668,16 @@ where
Err(_) => {
// Theoretically we can recover from some of these panics,
// but it's too late if the unwind reaches here.
- use std::{process, str};
let _ = catch_unwind(|| {
// IO functions can panic too.
eprintln!(
"YJIT panicked while holding VM lock acquired at {}:{}. Aborting...",
- str::from_utf8(loc.file.as_bytes()).unwrap_or("<not utf8>"),
+ loc.file.to_string_lossy(),
line,
);
});
- process::abort();
+ std::process::abort();
}
};
@@ -685,6 +711,7 @@ mod manual_defs {
pub const RUBY_FIXNUM_MAX: isize = RUBY_LONG_MAX / 2;
// From vm_callinfo.h - uses calculation that seems to confuse bindgen
+ pub const VM_CALL_ARGS_SIMPLE: u32 = 1 << VM_CALL_ARGS_SIMPLE_bit;
pub const VM_CALL_ARGS_SPLAT: u32 = 1 << VM_CALL_ARGS_SPLAT_bit;
pub const VM_CALL_ARGS_BLOCKARG: u32 = 1 << VM_CALL_ARGS_BLOCKARG_bit;
pub const VM_CALL_FCALL: u32 = 1 << VM_CALL_FCALL_bit;
@@ -711,6 +738,9 @@ mod manual_defs {
pub const RUBY_OFFSET_RSTRUCT_AS_HEAP_PTR: i32 = 24; // struct RStruct, subfield "as.heap.ptr"
pub const RUBY_OFFSET_RSTRUCT_AS_ARY: i32 = 16; // struct RStruct, subfield "as.ary"
+ pub const RUBY_OFFSET_RSTRING_AS_HEAP_PTR: i32 = 24; // struct RString, subfield "as.heap.ptr"
+ pub const RUBY_OFFSET_RSTRING_AS_ARY: i32 = 24; // struct RString, subfield "as.embed.ary"
+
// Constants from rb_control_frame_t vm_core.h
pub const RUBY_OFFSET_CFP_PC: i32 = 0;
pub const RUBY_OFFSET_CFP_SP: i32 = 8;
@@ -718,9 +748,8 @@ mod manual_defs {
pub const RUBY_OFFSET_CFP_SELF: i32 = 24;
pub const RUBY_OFFSET_CFP_EP: i32 = 32;
pub const RUBY_OFFSET_CFP_BLOCK_CODE: i32 = 40;
- pub const RUBY_OFFSET_CFP_BP: i32 = 48; // field __bp__
- pub const RUBY_OFFSET_CFP_JIT_RETURN: i32 = 56;
- pub const RUBY_SIZEOF_CONTROL_FRAME: usize = 64;
+ pub const RUBY_OFFSET_CFP_JIT_RETURN: i32 = 48;
+ pub const RUBY_SIZEOF_CONTROL_FRAME: usize = 56;
// Constants from rb_execution_context_t vm_core.h
pub const RUBY_OFFSET_EC_CFP: i32 = 16;
@@ -736,3 +765,53 @@ mod manual_defs {
pub const RUBY_OFFSET_ICE_VALUE: i32 = 8;
}
pub use manual_defs::*;
+
+/// Interned ID values for Ruby symbols and method names.
+/// See [crate::cruby::ID] and usages outside of YJIT.
+pub(crate) mod ids {
+ use std::sync::atomic::AtomicU64;
+ /// Globals to cache IDs on boot. Atomic to use with relaxed ordering
+ /// so reads can happen without `unsafe`. Initialization is done
+ /// single-threaded and release-acquire on [crate::yjit::YJIT_ENABLED]
+ /// makes sure we read the cached values after initialization is done.
+ macro_rules! def_ids {
+ ($(name: $ident:ident content: $str:literal)*) => {
+ $(
+ #[doc = concat!("[crate::cruby::ID] for `", stringify!($str), "`")]
+ pub static $ident: AtomicU64 = AtomicU64::new(0);
+ )*
+
+ pub(crate) fn init() {
+ $(
+ let content = &$str;
+ let ptr: *const u8 = content.as_ptr();
+
+ // Lookup and cache each ID
+ $ident.store(
+ unsafe { $crate::cruby::rb_intern2(ptr.cast(), content.len() as _) },
+ std::sync::atomic::Ordering::Relaxed
+ );
+ )*
+
+ }
+ }
+ }
+
+ def_ids! {
+ name: NULL content: b""
+ name: min content: b"min"
+ name: max content: b"max"
+ name: hash content: b"hash"
+ name: respond_to_missing content: b"respond_to_missing?"
+ name: to_ary content: b"to_ary"
+ name: eq content: b"=="
+ }
+}
+
+/// Get an CRuby `ID` to an interned string, e.g. a particular method name.
+macro_rules! ID {
+ ($id_name:ident) => {
+ $crate::cruby::ids::$id_name.load(std::sync::atomic::Ordering::Relaxed)
+ }
+}
+pub(crate) use ID;
diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs
index 16bb6e1feb..a03c2d0f00 100644
--- a/yjit/src/cruby_bindings.inc.rs
+++ b/yjit/src/cruby_bindings.inc.rs
@@ -81,6 +81,36 @@ where
}
}
#[repr(C)]
+#[derive(Default)]
+pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>, [T; 0]);
+impl<T> __IncompleteArrayField<T> {
+ #[inline]
+ pub const fn new() -> Self {
+ __IncompleteArrayField(::std::marker::PhantomData, [])
+ }
+ #[inline]
+ pub fn as_ptr(&self) -> *const T {
+ self as *const _ as *const T
+ }
+ #[inline]
+ pub fn as_mut_ptr(&mut self) -> *mut T {
+ self as *mut _ as *mut T
+ }
+ #[inline]
+ pub unsafe fn as_slice(&self, len: usize) -> &[T] {
+ ::std::slice::from_raw_parts(self.as_ptr(), len)
+ }
+ #[inline]
+ pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
+ ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
+ }
+}
+impl<T> ::std::fmt::Debug for __IncompleteArrayField<T> {
+ fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ fmt.write_str("__IncompleteArrayField")
+ }
+}
+#[repr(C)]
pub struct __BindgenUnionField<T>(::std::marker::PhantomData<T>);
impl<T> __BindgenUnionField<T> {
#[inline]
@@ -141,7 +171,7 @@ pub const VM_ENV_DATA_INDEX_SPECVAL: i32 = -1;
pub const VM_ENV_DATA_INDEX_FLAGS: u32 = 0;
pub const VM_BLOCK_HANDLER_NONE: u32 = 0;
pub const SHAPE_ID_NUM_BITS: u32 = 32;
-pub const OBJ_TOO_COMPLEX_SHAPE_ID: u32 = 11;
+pub const OBJ_TOO_COMPLEX_SHAPE_ID: u32 = 2;
pub type ID = ::std::os::raw::c_ulong;
pub type rb_alloc_func_t = ::std::option::Option<unsafe extern "C" fn(klass: VALUE) -> VALUE>;
pub const RUBY_Qfalse: ruby_special_consts = 0;
@@ -192,13 +222,12 @@ pub type ruby_value_type = u32;
pub const RUBY_FL_USHIFT: ruby_fl_ushift = 12;
pub type ruby_fl_ushift = u32;
pub const RUBY_FL_WB_PROTECTED: ruby_fl_type = 32;
-pub const RUBY_FL_PROMOTED0: ruby_fl_type = 32;
-pub const RUBY_FL_PROMOTED1: ruby_fl_type = 64;
-pub const RUBY_FL_PROMOTED: ruby_fl_type = 96;
+pub const RUBY_FL_PROMOTED: ruby_fl_type = 32;
+pub const RUBY_FL_UNUSED6: ruby_fl_type = 64;
pub const RUBY_FL_FINALIZE: ruby_fl_type = 128;
-pub const RUBY_FL_TAINT: ruby_fl_type = 256;
+pub const RUBY_FL_TAINT: ruby_fl_type = 0;
pub const RUBY_FL_SHAREABLE: ruby_fl_type = 256;
-pub const RUBY_FL_UNTRUSTED: ruby_fl_type = 256;
+pub const RUBY_FL_UNTRUSTED: ruby_fl_type = 0;
pub const RUBY_FL_SEEN_OBJ_ID: ruby_fl_type = 512;
pub const RUBY_FL_EXIVAR: ruby_fl_type = 1024;
pub const RUBY_FL_FREEZE: ruby_fl_type = 2048;
@@ -223,7 +252,7 @@ pub const RUBY_FL_USER17: ruby_fl_type = 536870912;
pub const RUBY_FL_USER18: ruby_fl_type = 1073741824;
pub const RUBY_FL_USER19: ruby_fl_type = -2147483648;
pub const RUBY_ELTS_SHARED: ruby_fl_type = 16384;
-pub const RUBY_FL_SINGLETON: ruby_fl_type = 4096;
+pub const RUBY_FL_SINGLETON: ruby_fl_type = 8192;
pub type ruby_fl_type = i32;
pub const RSTRING_NOEMBED: ruby_rstring_flags = 8192;
pub const RSTRING_FSTR: ruby_rstring_flags = 536870912;
@@ -245,7 +274,6 @@ pub type st_foreach_callback_func = ::std::option::Option<
>;
pub const RARRAY_EMBED_FLAG: ruby_rarray_flags = 8192;
pub const RARRAY_EMBED_LEN_MASK: ruby_rarray_flags = 4161536;
-pub const RARRAY_TRANSIENT_FLAG: ruby_rarray_flags = 33554432;
pub type ruby_rarray_flags = u32;
pub const RARRAY_EMBED_LEN_SHIFT: ruby_rarray_consts = 15;
pub type ruby_rarray_consts = u32;
@@ -253,9 +281,6 @@ pub const RMODULE_IS_REFINEMENT: ruby_rmodule_flags = 32768;
pub type ruby_rmodule_flags = u32;
pub const ROBJECT_EMBED: ruby_robject_flags = 8192;
pub type ruby_robject_flags = u32;
-pub const ROBJECT_OFFSET_AS_HEAP_IVPTR: i32 = 16;
-pub const ROBJECT_OFFSET_AS_HEAP_IV_INDEX_TBL: i32 = 24;
-pub const ROBJECT_OFFSET_AS_ARY: i32 = 16;
pub type rb_block_call_func = ::std::option::Option<
unsafe extern "C" fn(
yielded_arg: VALUE,
@@ -285,219 +310,6 @@ pub const RUBY_ENCINDEX_EUC_JP: ruby_preserved_encindex = 10;
pub const RUBY_ENCINDEX_Windows_31J: ruby_preserved_encindex = 11;
pub const RUBY_ENCINDEX_BUILTIN_MAX: ruby_preserved_encindex = 12;
pub type ruby_preserved_encindex = u32;
-pub const idDot2: ruby_method_ids = 128;
-pub const idDot3: ruby_method_ids = 129;
-pub const idUPlus: ruby_method_ids = 132;
-pub const idUMinus: ruby_method_ids = 133;
-pub const idPow: ruby_method_ids = 134;
-pub const idCmp: ruby_method_ids = 135;
-pub const idPLUS: ruby_method_ids = 43;
-pub const idMINUS: ruby_method_ids = 45;
-pub const idMULT: ruby_method_ids = 42;
-pub const idDIV: ruby_method_ids = 47;
-pub const idMOD: ruby_method_ids = 37;
-pub const idLTLT: ruby_method_ids = 136;
-pub const idGTGT: ruby_method_ids = 137;
-pub const idLT: ruby_method_ids = 60;
-pub const idLE: ruby_method_ids = 138;
-pub const idGT: ruby_method_ids = 62;
-pub const idGE: ruby_method_ids = 139;
-pub const idEq: ruby_method_ids = 140;
-pub const idEqq: ruby_method_ids = 141;
-pub const idNeq: ruby_method_ids = 142;
-pub const idNot: ruby_method_ids = 33;
-pub const idAnd: ruby_method_ids = 38;
-pub const idOr: ruby_method_ids = 124;
-pub const idBackquote: ruby_method_ids = 96;
-pub const idEqTilde: ruby_method_ids = 143;
-pub const idNeqTilde: ruby_method_ids = 144;
-pub const idAREF: ruby_method_ids = 145;
-pub const idASET: ruby_method_ids = 146;
-pub const idCOLON2: ruby_method_ids = 147;
-pub const idANDOP: ruby_method_ids = 148;
-pub const idOROP: ruby_method_ids = 149;
-pub const idANDDOT: ruby_method_ids = 150;
-pub const tPRESERVED_ID_BEGIN: ruby_method_ids = 150;
-pub const idNilP: ruby_method_ids = 151;
-pub const idNULL: ruby_method_ids = 152;
-pub const idEmptyP: ruby_method_ids = 153;
-pub const idEqlP: ruby_method_ids = 154;
-pub const idRespond_to: ruby_method_ids = 155;
-pub const idRespond_to_missing: ruby_method_ids = 156;
-pub const idIFUNC: ruby_method_ids = 157;
-pub const idCFUNC: ruby_method_ids = 158;
-pub const id_core_set_method_alias: ruby_method_ids = 159;
-pub const id_core_set_variable_alias: ruby_method_ids = 160;
-pub const id_core_undef_method: ruby_method_ids = 161;
-pub const id_core_define_method: ruby_method_ids = 162;
-pub const id_core_define_singleton_method: ruby_method_ids = 163;
-pub const id_core_set_postexe: ruby_method_ids = 164;
-pub const id_core_hash_merge_ptr: ruby_method_ids = 165;
-pub const id_core_hash_merge_kwd: ruby_method_ids = 166;
-pub const id_core_raise: ruby_method_ids = 167;
-pub const id_core_sprintf: ruby_method_ids = 168;
-pub const id_debug_created_info: ruby_method_ids = 169;
-pub const tPRESERVED_ID_END: ruby_method_ids = 170;
-pub const tTOKEN_LOCAL_BEGIN: ruby_method_ids = 169;
-pub const tMax: ruby_method_ids = 170;
-pub const tMin: ruby_method_ids = 171;
-pub const tHash: ruby_method_ids = 172;
-pub const tFreeze: ruby_method_ids = 173;
-pub const tInspect: ruby_method_ids = 174;
-pub const tIntern: ruby_method_ids = 175;
-pub const tObject_id: ruby_method_ids = 176;
-pub const tConst_added: ruby_method_ids = 177;
-pub const tConst_missing: ruby_method_ids = 178;
-pub const tMethodMissing: ruby_method_ids = 179;
-pub const tMethod_added: ruby_method_ids = 180;
-pub const tSingleton_method_added: ruby_method_ids = 181;
-pub const tMethod_removed: ruby_method_ids = 182;
-pub const tSingleton_method_removed: ruby_method_ids = 183;
-pub const tMethod_undefined: ruby_method_ids = 184;
-pub const tSingleton_method_undefined: ruby_method_ids = 185;
-pub const tLength: ruby_method_ids = 186;
-pub const tSize: ruby_method_ids = 187;
-pub const tGets: ruby_method_ids = 188;
-pub const tSucc: ruby_method_ids = 189;
-pub const tEach: ruby_method_ids = 190;
-pub const tProc: ruby_method_ids = 191;
-pub const tLambda: ruby_method_ids = 192;
-pub const tSend: ruby_method_ids = 193;
-pub const t__send__: ruby_method_ids = 194;
-pub const t__recursive_key__: ruby_method_ids = 195;
-pub const tInitialize: ruby_method_ids = 196;
-pub const tInitialize_copy: ruby_method_ids = 197;
-pub const tInitialize_clone: ruby_method_ids = 198;
-pub const tInitialize_dup: ruby_method_ids = 199;
-pub const tTo_int: ruby_method_ids = 200;
-pub const tTo_ary: ruby_method_ids = 201;
-pub const tTo_str: ruby_method_ids = 202;
-pub const tTo_sym: ruby_method_ids = 203;
-pub const tTo_hash: ruby_method_ids = 204;
-pub const tTo_proc: ruby_method_ids = 205;
-pub const tTo_io: ruby_method_ids = 206;
-pub const tTo_a: ruby_method_ids = 207;
-pub const tTo_s: ruby_method_ids = 208;
-pub const tTo_i: ruby_method_ids = 209;
-pub const tTo_f: ruby_method_ids = 210;
-pub const tTo_r: ruby_method_ids = 211;
-pub const tBt: ruby_method_ids = 212;
-pub const tBt_locations: ruby_method_ids = 213;
-pub const tCall: ruby_method_ids = 214;
-pub const tMesg: ruby_method_ids = 215;
-pub const tException: ruby_method_ids = 216;
-pub const tLocals: ruby_method_ids = 217;
-pub const tNOT: ruby_method_ids = 218;
-pub const tAND: ruby_method_ids = 219;
-pub const tOR: ruby_method_ids = 220;
-pub const tDiv: ruby_method_ids = 221;
-pub const tDivmod: ruby_method_ids = 222;
-pub const tFdiv: ruby_method_ids = 223;
-pub const tQuo: ruby_method_ids = 224;
-pub const tName: ruby_method_ids = 225;
-pub const tNil: ruby_method_ids = 226;
-pub const tPath: ruby_method_ids = 227;
-pub const tUScore: ruby_method_ids = 228;
-pub const tNUMPARAM_1: ruby_method_ids = 229;
-pub const tNUMPARAM_2: ruby_method_ids = 230;
-pub const tNUMPARAM_3: ruby_method_ids = 231;
-pub const tNUMPARAM_4: ruby_method_ids = 232;
-pub const tNUMPARAM_5: ruby_method_ids = 233;
-pub const tNUMPARAM_6: ruby_method_ids = 234;
-pub const tNUMPARAM_7: ruby_method_ids = 235;
-pub const tNUMPARAM_8: ruby_method_ids = 236;
-pub const tNUMPARAM_9: ruby_method_ids = 237;
-pub const tDefault: ruby_method_ids = 238;
-pub const tTOKEN_LOCAL_END: ruby_method_ids = 239;
-pub const tTOKEN_INSTANCE_BEGIN: ruby_method_ids = 238;
-pub const tTOKEN_INSTANCE_END: ruby_method_ids = 239;
-pub const tTOKEN_GLOBAL_BEGIN: ruby_method_ids = 238;
-pub const tLASTLINE: ruby_method_ids = 239;
-pub const tBACKREF: ruby_method_ids = 240;
-pub const tERROR_INFO: ruby_method_ids = 241;
-pub const tTOKEN_GLOBAL_END: ruby_method_ids = 242;
-pub const tTOKEN_CONST_BEGIN: ruby_method_ids = 241;
-pub const tTOKEN_CONST_END: ruby_method_ids = 242;
-pub const tTOKEN_CLASS_BEGIN: ruby_method_ids = 241;
-pub const tTOKEN_CLASS_END: ruby_method_ids = 242;
-pub const tTOKEN_ATTRSET_BEGIN: ruby_method_ids = 241;
-pub const tTOKEN_ATTRSET_END: ruby_method_ids = 242;
-pub const tNEXT_ID: ruby_method_ids = 242;
-pub const idMax: ruby_method_ids = 2721;
-pub const idMin: ruby_method_ids = 2737;
-pub const idHash: ruby_method_ids = 2753;
-pub const idFreeze: ruby_method_ids = 2769;
-pub const idInspect: ruby_method_ids = 2785;
-pub const idIntern: ruby_method_ids = 2801;
-pub const idObject_id: ruby_method_ids = 2817;
-pub const idConst_added: ruby_method_ids = 2833;
-pub const idConst_missing: ruby_method_ids = 2849;
-pub const idMethodMissing: ruby_method_ids = 2865;
-pub const idMethod_added: ruby_method_ids = 2881;
-pub const idSingleton_method_added: ruby_method_ids = 2897;
-pub const idMethod_removed: ruby_method_ids = 2913;
-pub const idSingleton_method_removed: ruby_method_ids = 2929;
-pub const idMethod_undefined: ruby_method_ids = 2945;
-pub const idSingleton_method_undefined: ruby_method_ids = 2961;
-pub const idLength: ruby_method_ids = 2977;
-pub const idSize: ruby_method_ids = 2993;
-pub const idGets: ruby_method_ids = 3009;
-pub const idSucc: ruby_method_ids = 3025;
-pub const idEach: ruby_method_ids = 3041;
-pub const idProc: ruby_method_ids = 3057;
-pub const idLambda: ruby_method_ids = 3073;
-pub const idSend: ruby_method_ids = 3089;
-pub const id__send__: ruby_method_ids = 3105;
-pub const id__recursive_key__: ruby_method_ids = 3121;
-pub const idInitialize: ruby_method_ids = 3137;
-pub const idInitialize_copy: ruby_method_ids = 3153;
-pub const idInitialize_clone: ruby_method_ids = 3169;
-pub const idInitialize_dup: ruby_method_ids = 3185;
-pub const idTo_int: ruby_method_ids = 3201;
-pub const idTo_ary: ruby_method_ids = 3217;
-pub const idTo_str: ruby_method_ids = 3233;
-pub const idTo_sym: ruby_method_ids = 3249;
-pub const idTo_hash: ruby_method_ids = 3265;
-pub const idTo_proc: ruby_method_ids = 3281;
-pub const idTo_io: ruby_method_ids = 3297;
-pub const idTo_a: ruby_method_ids = 3313;
-pub const idTo_s: ruby_method_ids = 3329;
-pub const idTo_i: ruby_method_ids = 3345;
-pub const idTo_f: ruby_method_ids = 3361;
-pub const idTo_r: ruby_method_ids = 3377;
-pub const idBt: ruby_method_ids = 3393;
-pub const idBt_locations: ruby_method_ids = 3409;
-pub const idCall: ruby_method_ids = 3425;
-pub const idMesg: ruby_method_ids = 3441;
-pub const idException: ruby_method_ids = 3457;
-pub const idLocals: ruby_method_ids = 3473;
-pub const idNOT: ruby_method_ids = 3489;
-pub const idAND: ruby_method_ids = 3505;
-pub const idOR: ruby_method_ids = 3521;
-pub const idDiv: ruby_method_ids = 3537;
-pub const idDivmod: ruby_method_ids = 3553;
-pub const idFdiv: ruby_method_ids = 3569;
-pub const idQuo: ruby_method_ids = 3585;
-pub const idName: ruby_method_ids = 3601;
-pub const idNil: ruby_method_ids = 3617;
-pub const idPath: ruby_method_ids = 3633;
-pub const idUScore: ruby_method_ids = 3649;
-pub const idNUMPARAM_1: ruby_method_ids = 3665;
-pub const idNUMPARAM_2: ruby_method_ids = 3681;
-pub const idNUMPARAM_3: ruby_method_ids = 3697;
-pub const idNUMPARAM_4: ruby_method_ids = 3713;
-pub const idNUMPARAM_5: ruby_method_ids = 3729;
-pub const idNUMPARAM_6: ruby_method_ids = 3745;
-pub const idNUMPARAM_7: ruby_method_ids = 3761;
-pub const idNUMPARAM_8: ruby_method_ids = 3777;
-pub const idNUMPARAM_9: ruby_method_ids = 3793;
-pub const idDefault: ruby_method_ids = 3809;
-pub const idLASTLINE: ruby_method_ids = 3831;
-pub const idBACKREF: ruby_method_ids = 3847;
-pub const idERROR_INFO: ruby_method_ids = 3863;
-pub const tLAST_OP_ID: ruby_method_ids = 169;
-pub const idLAST_OP_ID: ruby_method_ids = 10;
-pub type ruby_method_ids = u32;
pub const BOP_PLUS: ruby_basic_operators = 0;
pub const BOP_MINUS: ruby_basic_operators = 1;
pub const BOP_MULT: ruby_basic_operators = 2;
@@ -599,10 +411,11 @@ pub const VM_METHOD_TYPE_OPTIMIZED: rb_method_type_t = 9;
pub const VM_METHOD_TYPE_MISSING: rb_method_type_t = 10;
pub const VM_METHOD_TYPE_REFINED: rb_method_type_t = 11;
pub type rb_method_type_t = u32;
+pub type rb_cfunc_t = ::std::option::Option<unsafe extern "C" fn() -> VALUE>;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct rb_method_cfunc_struct {
- pub func: ::std::option::Option<unsafe extern "C" fn() -> VALUE>,
+ pub func: rb_cfunc_t,
pub invoker: ::std::option::Option<
unsafe extern "C" fn(
recv: VALUE,
@@ -626,6 +439,20 @@ pub struct rb_id_table {
_unused: [u8; 0],
}
pub type rb_num_t = ::std::os::raw::c_ulong;
+pub const RUBY_TAG_NONE: ruby_tag_type = 0;
+pub const RUBY_TAG_RETURN: ruby_tag_type = 1;
+pub const RUBY_TAG_BREAK: ruby_tag_type = 2;
+pub const RUBY_TAG_NEXT: ruby_tag_type = 3;
+pub const RUBY_TAG_RETRY: ruby_tag_type = 4;
+pub const RUBY_TAG_REDO: ruby_tag_type = 5;
+pub const RUBY_TAG_RAISE: ruby_tag_type = 6;
+pub const RUBY_TAG_THROW: ruby_tag_type = 7;
+pub const RUBY_TAG_FATAL: ruby_tag_type = 8;
+pub const RUBY_TAG_MASK: ruby_tag_type = 15;
+pub type ruby_tag_type = u32;
+pub const VM_THROW_NO_ESCAPE_FLAG: ruby_vm_throw_flags = 32768;
+pub const VM_THROW_STATE_MASK: ruby_vm_throw_flags = 255;
+pub type ruby_vm_throw_flags = u32;
#[repr(C)]
pub struct iseq_inline_constant_cache_entry {
pub flags: VALUE,
@@ -651,9 +478,19 @@ pub struct iseq_inline_iv_cache_entry {
pub struct iseq_inline_cvar_cache_entry {
pub entry: *mut rb_cvar_class_tbl_entry,
}
+pub const ISEQ_TYPE_TOP: rb_iseq_type = 0;
+pub const ISEQ_TYPE_METHOD: rb_iseq_type = 1;
+pub const ISEQ_TYPE_BLOCK: rb_iseq_type = 2;
+pub const ISEQ_TYPE_CLASS: rb_iseq_type = 3;
+pub const ISEQ_TYPE_RESCUE: rb_iseq_type = 4;
+pub const ISEQ_TYPE_ENSURE: rb_iseq_type = 5;
+pub const ISEQ_TYPE_EVAL: rb_iseq_type = 6;
+pub const ISEQ_TYPE_MAIN: rb_iseq_type = 7;
+pub const ISEQ_TYPE_PLAIN: rb_iseq_type = 8;
+pub type rb_iseq_type = u32;
pub const BUILTIN_ATTR_LEAF: rb_builtin_attr = 1;
-pub const BUILTIN_ATTR_NO_GC: rb_builtin_attr = 2;
-pub const BUILTIN_ATTR_SINGLE_NOARG_INLINE: rb_builtin_attr = 4;
+pub const BUILTIN_ATTR_SINGLE_NOARG_LEAF: rb_builtin_attr = 2;
+pub const BUILTIN_ATTR_INLINE_BLOCK: rb_builtin_attr = 4;
pub type rb_builtin_attr = u32;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
@@ -759,6 +596,10 @@ impl rb_proc_t {
__bindgen_bitfield_unit
}
}
+pub const VM_CHECKMATCH_TYPE_WHEN: vm_check_match_type = 1;
+pub const VM_CHECKMATCH_TYPE_CASE: vm_check_match_type = 2;
+pub const VM_CHECKMATCH_TYPE_RESCUE: vm_check_match_type = 3;
+pub type vm_check_match_type = u32;
pub const VM_SPECIAL_OBJECT_VMCORE: vm_special_object_type = 1;
pub const VM_SPECIAL_OBJECT_CBASE: vm_special_object_type = 2;
pub const VM_SPECIAL_OBJECT_CONST_BASE: vm_special_object_type = 3;
@@ -790,6 +631,8 @@ pub const VM_ENV_FLAG_ISOLATED: vm_frame_env_flags = 16;
pub type vm_frame_env_flags = u32;
pub type attr_index_t = u32;
pub type shape_id_t = u32;
+pub type redblack_id_t = u32;
+pub type redblack_node_t = redblack_node;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct rb_shape {
@@ -800,9 +643,18 @@ pub struct rb_shape {
pub type_: u8,
pub size_pool_index: u8,
pub parent_id: shape_id_t,
+ pub ancestor_index: *mut redblack_node_t,
}
pub type rb_shape_t = rb_shape;
#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct redblack_node {
+ pub key: ID,
+ pub value: *mut rb_shape_t,
+ pub l: redblack_id_t,
+ pub r: redblack_id_t,
+}
+#[repr(C)]
pub struct rb_cvar_class_tbl_entry {
pub index: u32,
pub global_cvar_state: rb_serial_t,
@@ -821,9 +673,16 @@ pub const VM_CALL_SUPER_bit: vm_call_flag_bits = 8;
pub const VM_CALL_ZSUPER_bit: vm_call_flag_bits = 9;
pub const VM_CALL_OPT_SEND_bit: vm_call_flag_bits = 10;
pub const VM_CALL_KW_SPLAT_MUT_bit: vm_call_flag_bits = 11;
-pub const VM_CALL__END: vm_call_flag_bits = 12;
+pub const VM_CALL_ARGS_SPLAT_MUT_bit: vm_call_flag_bits = 12;
+pub const VM_CALL__END: vm_call_flag_bits = 13;
pub type vm_call_flag_bits = u32;
#[repr(C)]
+pub struct rb_callinfo_kwarg {
+ pub keyword_len: ::std::os::raw::c_int,
+ pub references: ::std::os::raw::c_int,
+ pub keywords: __IncompleteArrayField<VALUE>,
+}
+#[repr(C)]
pub struct rb_callinfo {
pub flags: VALUE,
pub kwarg: *const rb_callinfo_kwarg,
@@ -844,7 +703,6 @@ pub const RHASH_AR_TABLE_SIZE_MASK: ruby_rhash_flags = 983040;
pub const RHASH_AR_TABLE_SIZE_SHIFT: ruby_rhash_flags = 16;
pub const RHASH_AR_TABLE_BOUND_MASK: ruby_rhash_flags = 15728640;
pub const RHASH_AR_TABLE_BOUND_SHIFT: ruby_rhash_flags = 20;
-pub const RHASH_TRANSIENT_FLAG: ruby_rhash_flags = 16777216;
pub const RHASH_LEV_SHIFT: ruby_rhash_flags = 25;
pub const RHASH_LEV_MAX: ruby_rhash_flags = 127;
pub type ruby_rhash_flags = u32;
@@ -878,196 +736,229 @@ pub const YARVINSN_putself: ruby_vminsn_type = 18;
pub const YARVINSN_putobject: ruby_vminsn_type = 19;
pub const YARVINSN_putspecialobject: ruby_vminsn_type = 20;
pub const YARVINSN_putstring: ruby_vminsn_type = 21;
-pub const YARVINSN_concatstrings: ruby_vminsn_type = 22;
-pub const YARVINSN_anytostring: ruby_vminsn_type = 23;
-pub const YARVINSN_toregexp: ruby_vminsn_type = 24;
-pub const YARVINSN_intern: ruby_vminsn_type = 25;
-pub const YARVINSN_newarray: ruby_vminsn_type = 26;
-pub const YARVINSN_newarraykwsplat: ruby_vminsn_type = 27;
-pub const YARVINSN_duparray: ruby_vminsn_type = 28;
-pub const YARVINSN_duphash: ruby_vminsn_type = 29;
-pub const YARVINSN_expandarray: ruby_vminsn_type = 30;
-pub const YARVINSN_concatarray: ruby_vminsn_type = 31;
-pub const YARVINSN_splatarray: ruby_vminsn_type = 32;
-pub const YARVINSN_newhash: ruby_vminsn_type = 33;
-pub const YARVINSN_newrange: ruby_vminsn_type = 34;
-pub const YARVINSN_pop: ruby_vminsn_type = 35;
-pub const YARVINSN_dup: ruby_vminsn_type = 36;
-pub const YARVINSN_dupn: ruby_vminsn_type = 37;
-pub const YARVINSN_swap: ruby_vminsn_type = 38;
-pub const YARVINSN_opt_reverse: ruby_vminsn_type = 39;
-pub const YARVINSN_topn: ruby_vminsn_type = 40;
-pub const YARVINSN_setn: ruby_vminsn_type = 41;
-pub const YARVINSN_adjuststack: ruby_vminsn_type = 42;
-pub const YARVINSN_defined: ruby_vminsn_type = 43;
-pub const YARVINSN_definedivar: ruby_vminsn_type = 44;
-pub const YARVINSN_checkmatch: ruby_vminsn_type = 45;
-pub const YARVINSN_checkkeyword: ruby_vminsn_type = 46;
-pub const YARVINSN_checktype: ruby_vminsn_type = 47;
-pub const YARVINSN_defineclass: ruby_vminsn_type = 48;
-pub const YARVINSN_definemethod: ruby_vminsn_type = 49;
-pub const YARVINSN_definesmethod: ruby_vminsn_type = 50;
-pub const YARVINSN_send: ruby_vminsn_type = 51;
-pub const YARVINSN_opt_send_without_block: ruby_vminsn_type = 52;
-pub const YARVINSN_objtostring: ruby_vminsn_type = 53;
-pub const YARVINSN_opt_str_freeze: ruby_vminsn_type = 54;
-pub const YARVINSN_opt_nil_p: ruby_vminsn_type = 55;
-pub const YARVINSN_opt_str_uminus: ruby_vminsn_type = 56;
-pub const YARVINSN_opt_newarray_send: ruby_vminsn_type = 57;
-pub const YARVINSN_invokesuper: ruby_vminsn_type = 58;
-pub const YARVINSN_invokeblock: ruby_vminsn_type = 59;
-pub const YARVINSN_leave: ruby_vminsn_type = 60;
-pub const YARVINSN_throw: ruby_vminsn_type = 61;
-pub const YARVINSN_jump: ruby_vminsn_type = 62;
-pub const YARVINSN_branchif: ruby_vminsn_type = 63;
-pub const YARVINSN_branchunless: ruby_vminsn_type = 64;
-pub const YARVINSN_branchnil: ruby_vminsn_type = 65;
-pub const YARVINSN_once: ruby_vminsn_type = 66;
-pub const YARVINSN_opt_case_dispatch: ruby_vminsn_type = 67;
-pub const YARVINSN_opt_plus: ruby_vminsn_type = 68;
-pub const YARVINSN_opt_minus: ruby_vminsn_type = 69;
-pub const YARVINSN_opt_mult: ruby_vminsn_type = 70;
-pub const YARVINSN_opt_div: ruby_vminsn_type = 71;
-pub const YARVINSN_opt_mod: ruby_vminsn_type = 72;
-pub const YARVINSN_opt_eq: ruby_vminsn_type = 73;
-pub const YARVINSN_opt_neq: ruby_vminsn_type = 74;
-pub const YARVINSN_opt_lt: ruby_vminsn_type = 75;
-pub const YARVINSN_opt_le: ruby_vminsn_type = 76;
-pub const YARVINSN_opt_gt: ruby_vminsn_type = 77;
-pub const YARVINSN_opt_ge: ruby_vminsn_type = 78;
-pub const YARVINSN_opt_ltlt: ruby_vminsn_type = 79;
-pub const YARVINSN_opt_and: ruby_vminsn_type = 80;
-pub const YARVINSN_opt_or: ruby_vminsn_type = 81;
-pub const YARVINSN_opt_aref: ruby_vminsn_type = 82;
-pub const YARVINSN_opt_aset: ruby_vminsn_type = 83;
-pub const YARVINSN_opt_aset_with: ruby_vminsn_type = 84;
-pub const YARVINSN_opt_aref_with: ruby_vminsn_type = 85;
-pub const YARVINSN_opt_length: ruby_vminsn_type = 86;
-pub const YARVINSN_opt_size: ruby_vminsn_type = 87;
-pub const YARVINSN_opt_empty_p: ruby_vminsn_type = 88;
-pub const YARVINSN_opt_succ: ruby_vminsn_type = 89;
-pub const YARVINSN_opt_not: ruby_vminsn_type = 90;
-pub const YARVINSN_opt_regexpmatch2: ruby_vminsn_type = 91;
-pub const YARVINSN_invokebuiltin: ruby_vminsn_type = 92;
-pub const YARVINSN_opt_invokebuiltin_delegate: ruby_vminsn_type = 93;
-pub const YARVINSN_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 94;
-pub const YARVINSN_getlocal_WC_0: ruby_vminsn_type = 95;
-pub const YARVINSN_getlocal_WC_1: ruby_vminsn_type = 96;
-pub const YARVINSN_setlocal_WC_0: ruby_vminsn_type = 97;
-pub const YARVINSN_setlocal_WC_1: ruby_vminsn_type = 98;
-pub const YARVINSN_putobject_INT2FIX_0_: ruby_vminsn_type = 99;
-pub const YARVINSN_putobject_INT2FIX_1_: ruby_vminsn_type = 100;
-pub const YARVINSN_trace_nop: ruby_vminsn_type = 101;
-pub const YARVINSN_trace_getlocal: ruby_vminsn_type = 102;
-pub const YARVINSN_trace_setlocal: ruby_vminsn_type = 103;
-pub const YARVINSN_trace_getblockparam: ruby_vminsn_type = 104;
-pub const YARVINSN_trace_setblockparam: ruby_vminsn_type = 105;
-pub const YARVINSN_trace_getblockparamproxy: ruby_vminsn_type = 106;
-pub const YARVINSN_trace_getspecial: ruby_vminsn_type = 107;
-pub const YARVINSN_trace_setspecial: ruby_vminsn_type = 108;
-pub const YARVINSN_trace_getinstancevariable: ruby_vminsn_type = 109;
-pub const YARVINSN_trace_setinstancevariable: ruby_vminsn_type = 110;
-pub const YARVINSN_trace_getclassvariable: ruby_vminsn_type = 111;
-pub const YARVINSN_trace_setclassvariable: ruby_vminsn_type = 112;
-pub const YARVINSN_trace_opt_getconstant_path: ruby_vminsn_type = 113;
-pub const YARVINSN_trace_getconstant: ruby_vminsn_type = 114;
-pub const YARVINSN_trace_setconstant: ruby_vminsn_type = 115;
-pub const YARVINSN_trace_getglobal: ruby_vminsn_type = 116;
-pub const YARVINSN_trace_setglobal: ruby_vminsn_type = 117;
-pub const YARVINSN_trace_putnil: ruby_vminsn_type = 118;
-pub const YARVINSN_trace_putself: ruby_vminsn_type = 119;
-pub const YARVINSN_trace_putobject: ruby_vminsn_type = 120;
-pub const YARVINSN_trace_putspecialobject: ruby_vminsn_type = 121;
-pub const YARVINSN_trace_putstring: ruby_vminsn_type = 122;
-pub const YARVINSN_trace_concatstrings: ruby_vminsn_type = 123;
-pub const YARVINSN_trace_anytostring: ruby_vminsn_type = 124;
-pub const YARVINSN_trace_toregexp: ruby_vminsn_type = 125;
-pub const YARVINSN_trace_intern: ruby_vminsn_type = 126;
-pub const YARVINSN_trace_newarray: ruby_vminsn_type = 127;
-pub const YARVINSN_trace_newarraykwsplat: ruby_vminsn_type = 128;
-pub const YARVINSN_trace_duparray: ruby_vminsn_type = 129;
-pub const YARVINSN_trace_duphash: ruby_vminsn_type = 130;
-pub const YARVINSN_trace_expandarray: ruby_vminsn_type = 131;
-pub const YARVINSN_trace_concatarray: ruby_vminsn_type = 132;
-pub const YARVINSN_trace_splatarray: ruby_vminsn_type = 133;
-pub const YARVINSN_trace_newhash: ruby_vminsn_type = 134;
-pub const YARVINSN_trace_newrange: ruby_vminsn_type = 135;
-pub const YARVINSN_trace_pop: ruby_vminsn_type = 136;
-pub const YARVINSN_trace_dup: ruby_vminsn_type = 137;
-pub const YARVINSN_trace_dupn: ruby_vminsn_type = 138;
-pub const YARVINSN_trace_swap: ruby_vminsn_type = 139;
-pub const YARVINSN_trace_opt_reverse: ruby_vminsn_type = 140;
-pub const YARVINSN_trace_topn: ruby_vminsn_type = 141;
-pub const YARVINSN_trace_setn: ruby_vminsn_type = 142;
-pub const YARVINSN_trace_adjuststack: ruby_vminsn_type = 143;
-pub const YARVINSN_trace_defined: ruby_vminsn_type = 144;
-pub const YARVINSN_trace_definedivar: ruby_vminsn_type = 145;
-pub const YARVINSN_trace_checkmatch: ruby_vminsn_type = 146;
-pub const YARVINSN_trace_checkkeyword: ruby_vminsn_type = 147;
-pub const YARVINSN_trace_checktype: ruby_vminsn_type = 148;
-pub const YARVINSN_trace_defineclass: ruby_vminsn_type = 149;
-pub const YARVINSN_trace_definemethod: ruby_vminsn_type = 150;
-pub const YARVINSN_trace_definesmethod: ruby_vminsn_type = 151;
-pub const YARVINSN_trace_send: ruby_vminsn_type = 152;
-pub const YARVINSN_trace_opt_send_without_block: ruby_vminsn_type = 153;
-pub const YARVINSN_trace_objtostring: ruby_vminsn_type = 154;
-pub const YARVINSN_trace_opt_str_freeze: ruby_vminsn_type = 155;
-pub const YARVINSN_trace_opt_nil_p: ruby_vminsn_type = 156;
-pub const YARVINSN_trace_opt_str_uminus: ruby_vminsn_type = 157;
-pub const YARVINSN_trace_opt_newarray_send: ruby_vminsn_type = 158;
-pub const YARVINSN_trace_invokesuper: ruby_vminsn_type = 159;
-pub const YARVINSN_trace_invokeblock: ruby_vminsn_type = 160;
-pub const YARVINSN_trace_leave: ruby_vminsn_type = 161;
-pub const YARVINSN_trace_throw: ruby_vminsn_type = 162;
-pub const YARVINSN_trace_jump: ruby_vminsn_type = 163;
-pub const YARVINSN_trace_branchif: ruby_vminsn_type = 164;
-pub const YARVINSN_trace_branchunless: ruby_vminsn_type = 165;
-pub const YARVINSN_trace_branchnil: ruby_vminsn_type = 166;
-pub const YARVINSN_trace_once: ruby_vminsn_type = 167;
-pub const YARVINSN_trace_opt_case_dispatch: ruby_vminsn_type = 168;
-pub const YARVINSN_trace_opt_plus: ruby_vminsn_type = 169;
-pub const YARVINSN_trace_opt_minus: ruby_vminsn_type = 170;
-pub const YARVINSN_trace_opt_mult: ruby_vminsn_type = 171;
-pub const YARVINSN_trace_opt_div: ruby_vminsn_type = 172;
-pub const YARVINSN_trace_opt_mod: ruby_vminsn_type = 173;
-pub const YARVINSN_trace_opt_eq: ruby_vminsn_type = 174;
-pub const YARVINSN_trace_opt_neq: ruby_vminsn_type = 175;
-pub const YARVINSN_trace_opt_lt: ruby_vminsn_type = 176;
-pub const YARVINSN_trace_opt_le: ruby_vminsn_type = 177;
-pub const YARVINSN_trace_opt_gt: ruby_vminsn_type = 178;
-pub const YARVINSN_trace_opt_ge: ruby_vminsn_type = 179;
-pub const YARVINSN_trace_opt_ltlt: ruby_vminsn_type = 180;
-pub const YARVINSN_trace_opt_and: ruby_vminsn_type = 181;
-pub const YARVINSN_trace_opt_or: ruby_vminsn_type = 182;
-pub const YARVINSN_trace_opt_aref: ruby_vminsn_type = 183;
-pub const YARVINSN_trace_opt_aset: ruby_vminsn_type = 184;
-pub const YARVINSN_trace_opt_aset_with: ruby_vminsn_type = 185;
-pub const YARVINSN_trace_opt_aref_with: ruby_vminsn_type = 186;
-pub const YARVINSN_trace_opt_length: ruby_vminsn_type = 187;
-pub const YARVINSN_trace_opt_size: ruby_vminsn_type = 188;
-pub const YARVINSN_trace_opt_empty_p: ruby_vminsn_type = 189;
-pub const YARVINSN_trace_opt_succ: ruby_vminsn_type = 190;
-pub const YARVINSN_trace_opt_not: ruby_vminsn_type = 191;
-pub const YARVINSN_trace_opt_regexpmatch2: ruby_vminsn_type = 192;
-pub const YARVINSN_trace_invokebuiltin: ruby_vminsn_type = 193;
-pub const YARVINSN_trace_opt_invokebuiltin_delegate: ruby_vminsn_type = 194;
-pub const YARVINSN_trace_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 195;
-pub const YARVINSN_trace_getlocal_WC_0: ruby_vminsn_type = 196;
-pub const YARVINSN_trace_getlocal_WC_1: ruby_vminsn_type = 197;
-pub const YARVINSN_trace_setlocal_WC_0: ruby_vminsn_type = 198;
-pub const YARVINSN_trace_setlocal_WC_1: ruby_vminsn_type = 199;
-pub const YARVINSN_trace_putobject_INT2FIX_0_: ruby_vminsn_type = 200;
-pub const YARVINSN_trace_putobject_INT2FIX_1_: ruby_vminsn_type = 201;
-pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 202;
+pub const YARVINSN_putchilledstring: ruby_vminsn_type = 22;
+pub const YARVINSN_concatstrings: ruby_vminsn_type = 23;
+pub const YARVINSN_anytostring: ruby_vminsn_type = 24;
+pub const YARVINSN_toregexp: ruby_vminsn_type = 25;
+pub const YARVINSN_intern: ruby_vminsn_type = 26;
+pub const YARVINSN_newarray: ruby_vminsn_type = 27;
+pub const YARVINSN_newarraykwsplat: ruby_vminsn_type = 28;
+pub const YARVINSN_pushtoarraykwsplat: ruby_vminsn_type = 29;
+pub const YARVINSN_duparray: ruby_vminsn_type = 30;
+pub const YARVINSN_duphash: ruby_vminsn_type = 31;
+pub const YARVINSN_expandarray: ruby_vminsn_type = 32;
+pub const YARVINSN_concatarray: ruby_vminsn_type = 33;
+pub const YARVINSN_concattoarray: ruby_vminsn_type = 34;
+pub const YARVINSN_pushtoarray: ruby_vminsn_type = 35;
+pub const YARVINSN_splatarray: ruby_vminsn_type = 36;
+pub const YARVINSN_splatkw: ruby_vminsn_type = 37;
+pub const YARVINSN_newhash: ruby_vminsn_type = 38;
+pub const YARVINSN_newrange: ruby_vminsn_type = 39;
+pub const YARVINSN_pop: ruby_vminsn_type = 40;
+pub const YARVINSN_dup: ruby_vminsn_type = 41;
+pub const YARVINSN_dupn: ruby_vminsn_type = 42;
+pub const YARVINSN_swap: ruby_vminsn_type = 43;
+pub const YARVINSN_opt_reverse: ruby_vminsn_type = 44;
+pub const YARVINSN_topn: ruby_vminsn_type = 45;
+pub const YARVINSN_setn: ruby_vminsn_type = 46;
+pub const YARVINSN_adjuststack: ruby_vminsn_type = 47;
+pub const YARVINSN_defined: ruby_vminsn_type = 48;
+pub const YARVINSN_definedivar: ruby_vminsn_type = 49;
+pub const YARVINSN_checkmatch: ruby_vminsn_type = 50;
+pub const YARVINSN_checkkeyword: ruby_vminsn_type = 51;
+pub const YARVINSN_checktype: ruby_vminsn_type = 52;
+pub const YARVINSN_defineclass: ruby_vminsn_type = 53;
+pub const YARVINSN_definemethod: ruby_vminsn_type = 54;
+pub const YARVINSN_definesmethod: ruby_vminsn_type = 55;
+pub const YARVINSN_send: ruby_vminsn_type = 56;
+pub const YARVINSN_opt_send_without_block: ruby_vminsn_type = 57;
+pub const YARVINSN_objtostring: ruby_vminsn_type = 58;
+pub const YARVINSN_opt_str_freeze: ruby_vminsn_type = 59;
+pub const YARVINSN_opt_nil_p: ruby_vminsn_type = 60;
+pub const YARVINSN_opt_str_uminus: ruby_vminsn_type = 61;
+pub const YARVINSN_opt_newarray_send: ruby_vminsn_type = 62;
+pub const YARVINSN_invokesuper: ruby_vminsn_type = 63;
+pub const YARVINSN_invokeblock: ruby_vminsn_type = 64;
+pub const YARVINSN_leave: ruby_vminsn_type = 65;
+pub const YARVINSN_throw: ruby_vminsn_type = 66;
+pub const YARVINSN_jump: ruby_vminsn_type = 67;
+pub const YARVINSN_branchif: ruby_vminsn_type = 68;
+pub const YARVINSN_branchunless: ruby_vminsn_type = 69;
+pub const YARVINSN_branchnil: ruby_vminsn_type = 70;
+pub const YARVINSN_once: ruby_vminsn_type = 71;
+pub const YARVINSN_opt_case_dispatch: ruby_vminsn_type = 72;
+pub const YARVINSN_opt_plus: ruby_vminsn_type = 73;
+pub const YARVINSN_opt_minus: ruby_vminsn_type = 74;
+pub const YARVINSN_opt_mult: ruby_vminsn_type = 75;
+pub const YARVINSN_opt_div: ruby_vminsn_type = 76;
+pub const YARVINSN_opt_mod: ruby_vminsn_type = 77;
+pub const YARVINSN_opt_eq: ruby_vminsn_type = 78;
+pub const YARVINSN_opt_neq: ruby_vminsn_type = 79;
+pub const YARVINSN_opt_lt: ruby_vminsn_type = 80;
+pub const YARVINSN_opt_le: ruby_vminsn_type = 81;
+pub const YARVINSN_opt_gt: ruby_vminsn_type = 82;
+pub const YARVINSN_opt_ge: ruby_vminsn_type = 83;
+pub const YARVINSN_opt_ltlt: ruby_vminsn_type = 84;
+pub const YARVINSN_opt_and: ruby_vminsn_type = 85;
+pub const YARVINSN_opt_or: ruby_vminsn_type = 86;
+pub const YARVINSN_opt_aref: ruby_vminsn_type = 87;
+pub const YARVINSN_opt_aset: ruby_vminsn_type = 88;
+pub const YARVINSN_opt_aset_with: ruby_vminsn_type = 89;
+pub const YARVINSN_opt_aref_with: ruby_vminsn_type = 90;
+pub const YARVINSN_opt_length: ruby_vminsn_type = 91;
+pub const YARVINSN_opt_size: ruby_vminsn_type = 92;
+pub const YARVINSN_opt_empty_p: ruby_vminsn_type = 93;
+pub const YARVINSN_opt_succ: ruby_vminsn_type = 94;
+pub const YARVINSN_opt_not: ruby_vminsn_type = 95;
+pub const YARVINSN_opt_regexpmatch2: ruby_vminsn_type = 96;
+pub const YARVINSN_invokebuiltin: ruby_vminsn_type = 97;
+pub const YARVINSN_opt_invokebuiltin_delegate: ruby_vminsn_type = 98;
+pub const YARVINSN_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 99;
+pub const YARVINSN_getlocal_WC_0: ruby_vminsn_type = 100;
+pub const YARVINSN_getlocal_WC_1: ruby_vminsn_type = 101;
+pub const YARVINSN_setlocal_WC_0: ruby_vminsn_type = 102;
+pub const YARVINSN_setlocal_WC_1: ruby_vminsn_type = 103;
+pub const YARVINSN_putobject_INT2FIX_0_: ruby_vminsn_type = 104;
+pub const YARVINSN_putobject_INT2FIX_1_: ruby_vminsn_type = 105;
+pub const YARVINSN_trace_nop: ruby_vminsn_type = 106;
+pub const YARVINSN_trace_getlocal: ruby_vminsn_type = 107;
+pub const YARVINSN_trace_setlocal: ruby_vminsn_type = 108;
+pub const YARVINSN_trace_getblockparam: ruby_vminsn_type = 109;
+pub const YARVINSN_trace_setblockparam: ruby_vminsn_type = 110;
+pub const YARVINSN_trace_getblockparamproxy: ruby_vminsn_type = 111;
+pub const YARVINSN_trace_getspecial: ruby_vminsn_type = 112;
+pub const YARVINSN_trace_setspecial: ruby_vminsn_type = 113;
+pub const YARVINSN_trace_getinstancevariable: ruby_vminsn_type = 114;
+pub const YARVINSN_trace_setinstancevariable: ruby_vminsn_type = 115;
+pub const YARVINSN_trace_getclassvariable: ruby_vminsn_type = 116;
+pub const YARVINSN_trace_setclassvariable: ruby_vminsn_type = 117;
+pub const YARVINSN_trace_opt_getconstant_path: ruby_vminsn_type = 118;
+pub const YARVINSN_trace_getconstant: ruby_vminsn_type = 119;
+pub const YARVINSN_trace_setconstant: ruby_vminsn_type = 120;
+pub const YARVINSN_trace_getglobal: ruby_vminsn_type = 121;
+pub const YARVINSN_trace_setglobal: ruby_vminsn_type = 122;
+pub const YARVINSN_trace_putnil: ruby_vminsn_type = 123;
+pub const YARVINSN_trace_putself: ruby_vminsn_type = 124;
+pub const YARVINSN_trace_putobject: ruby_vminsn_type = 125;
+pub const YARVINSN_trace_putspecialobject: ruby_vminsn_type = 126;
+pub const YARVINSN_trace_putstring: ruby_vminsn_type = 127;
+pub const YARVINSN_trace_putchilledstring: ruby_vminsn_type = 128;
+pub const YARVINSN_trace_concatstrings: ruby_vminsn_type = 129;
+pub const YARVINSN_trace_anytostring: ruby_vminsn_type = 130;
+pub const YARVINSN_trace_toregexp: ruby_vminsn_type = 131;
+pub const YARVINSN_trace_intern: ruby_vminsn_type = 132;
+pub const YARVINSN_trace_newarray: ruby_vminsn_type = 133;
+pub const YARVINSN_trace_newarraykwsplat: ruby_vminsn_type = 134;
+pub const YARVINSN_trace_pushtoarraykwsplat: ruby_vminsn_type = 135;
+pub const YARVINSN_trace_duparray: ruby_vminsn_type = 136;
+pub const YARVINSN_trace_duphash: ruby_vminsn_type = 137;
+pub const YARVINSN_trace_expandarray: ruby_vminsn_type = 138;
+pub const YARVINSN_trace_concatarray: ruby_vminsn_type = 139;
+pub const YARVINSN_trace_concattoarray: ruby_vminsn_type = 140;
+pub const YARVINSN_trace_pushtoarray: ruby_vminsn_type = 141;
+pub const YARVINSN_trace_splatarray: ruby_vminsn_type = 142;
+pub const YARVINSN_trace_splatkw: ruby_vminsn_type = 143;
+pub const YARVINSN_trace_newhash: ruby_vminsn_type = 144;
+pub const YARVINSN_trace_newrange: ruby_vminsn_type = 145;
+pub const YARVINSN_trace_pop: ruby_vminsn_type = 146;
+pub const YARVINSN_trace_dup: ruby_vminsn_type = 147;
+pub const YARVINSN_trace_dupn: ruby_vminsn_type = 148;
+pub const YARVINSN_trace_swap: ruby_vminsn_type = 149;
+pub const YARVINSN_trace_opt_reverse: ruby_vminsn_type = 150;
+pub const YARVINSN_trace_topn: ruby_vminsn_type = 151;
+pub const YARVINSN_trace_setn: ruby_vminsn_type = 152;
+pub const YARVINSN_trace_adjuststack: ruby_vminsn_type = 153;
+pub const YARVINSN_trace_defined: ruby_vminsn_type = 154;
+pub const YARVINSN_trace_definedivar: ruby_vminsn_type = 155;
+pub const YARVINSN_trace_checkmatch: ruby_vminsn_type = 156;
+pub const YARVINSN_trace_checkkeyword: ruby_vminsn_type = 157;
+pub const YARVINSN_trace_checktype: ruby_vminsn_type = 158;
+pub const YARVINSN_trace_defineclass: ruby_vminsn_type = 159;
+pub const YARVINSN_trace_definemethod: ruby_vminsn_type = 160;
+pub const YARVINSN_trace_definesmethod: ruby_vminsn_type = 161;
+pub const YARVINSN_trace_send: ruby_vminsn_type = 162;
+pub const YARVINSN_trace_opt_send_without_block: ruby_vminsn_type = 163;
+pub const YARVINSN_trace_objtostring: ruby_vminsn_type = 164;
+pub const YARVINSN_trace_opt_str_freeze: ruby_vminsn_type = 165;
+pub const YARVINSN_trace_opt_nil_p: ruby_vminsn_type = 166;
+pub const YARVINSN_trace_opt_str_uminus: ruby_vminsn_type = 167;
+pub const YARVINSN_trace_opt_newarray_send: ruby_vminsn_type = 168;
+pub const YARVINSN_trace_invokesuper: ruby_vminsn_type = 169;
+pub const YARVINSN_trace_invokeblock: ruby_vminsn_type = 170;
+pub const YARVINSN_trace_leave: ruby_vminsn_type = 171;
+pub const YARVINSN_trace_throw: ruby_vminsn_type = 172;
+pub const YARVINSN_trace_jump: ruby_vminsn_type = 173;
+pub const YARVINSN_trace_branchif: ruby_vminsn_type = 174;
+pub const YARVINSN_trace_branchunless: ruby_vminsn_type = 175;
+pub const YARVINSN_trace_branchnil: ruby_vminsn_type = 176;
+pub const YARVINSN_trace_once: ruby_vminsn_type = 177;
+pub const YARVINSN_trace_opt_case_dispatch: ruby_vminsn_type = 178;
+pub const YARVINSN_trace_opt_plus: ruby_vminsn_type = 179;
+pub const YARVINSN_trace_opt_minus: ruby_vminsn_type = 180;
+pub const YARVINSN_trace_opt_mult: ruby_vminsn_type = 181;
+pub const YARVINSN_trace_opt_div: ruby_vminsn_type = 182;
+pub const YARVINSN_trace_opt_mod: ruby_vminsn_type = 183;
+pub const YARVINSN_trace_opt_eq: ruby_vminsn_type = 184;
+pub const YARVINSN_trace_opt_neq: ruby_vminsn_type = 185;
+pub const YARVINSN_trace_opt_lt: ruby_vminsn_type = 186;
+pub const YARVINSN_trace_opt_le: ruby_vminsn_type = 187;
+pub const YARVINSN_trace_opt_gt: ruby_vminsn_type = 188;
+pub const YARVINSN_trace_opt_ge: ruby_vminsn_type = 189;
+pub const YARVINSN_trace_opt_ltlt: ruby_vminsn_type = 190;
+pub const YARVINSN_trace_opt_and: ruby_vminsn_type = 191;
+pub const YARVINSN_trace_opt_or: ruby_vminsn_type = 192;
+pub const YARVINSN_trace_opt_aref: ruby_vminsn_type = 193;
+pub const YARVINSN_trace_opt_aset: ruby_vminsn_type = 194;
+pub const YARVINSN_trace_opt_aset_with: ruby_vminsn_type = 195;
+pub const YARVINSN_trace_opt_aref_with: ruby_vminsn_type = 196;
+pub const YARVINSN_trace_opt_length: ruby_vminsn_type = 197;
+pub const YARVINSN_trace_opt_size: ruby_vminsn_type = 198;
+pub const YARVINSN_trace_opt_empty_p: ruby_vminsn_type = 199;
+pub const YARVINSN_trace_opt_succ: ruby_vminsn_type = 200;
+pub const YARVINSN_trace_opt_not: ruby_vminsn_type = 201;
+pub const YARVINSN_trace_opt_regexpmatch2: ruby_vminsn_type = 202;
+pub const YARVINSN_trace_invokebuiltin: ruby_vminsn_type = 203;
+pub const YARVINSN_trace_opt_invokebuiltin_delegate: ruby_vminsn_type = 204;
+pub const YARVINSN_trace_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 205;
+pub const YARVINSN_trace_getlocal_WC_0: ruby_vminsn_type = 206;
+pub const YARVINSN_trace_getlocal_WC_1: ruby_vminsn_type = 207;
+pub const YARVINSN_trace_setlocal_WC_0: ruby_vminsn_type = 208;
+pub const YARVINSN_trace_setlocal_WC_1: ruby_vminsn_type = 209;
+pub const YARVINSN_trace_putobject_INT2FIX_0_: ruby_vminsn_type = 210;
+pub const YARVINSN_trace_putobject_INT2FIX_1_: ruby_vminsn_type = 211;
+pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 212;
pub type ruby_vminsn_type = u32;
pub type rb_iseq_callback = ::std::option::Option<
unsafe extern "C" fn(arg1: *const rb_iseq_t, arg2: *mut ::std::os::raw::c_void),
>;
-pub const RUBY_OFFSET_RSTRING_AS_HEAP_LEN: rstring_offsets = 16;
-pub const RUBY_OFFSET_RSTRING_EMBED_LEN: rstring_offsets = 16;
+pub const DEFINED_NOT_DEFINED: defined_type = 0;
+pub const DEFINED_NIL: defined_type = 1;
+pub const DEFINED_IVAR: defined_type = 2;
+pub const DEFINED_LVAR: defined_type = 3;
+pub const DEFINED_GVAR: defined_type = 4;
+pub const DEFINED_CVAR: defined_type = 5;
+pub const DEFINED_CONST: defined_type = 6;
+pub const DEFINED_METHOD: defined_type = 7;
+pub const DEFINED_YIELD: defined_type = 8;
+pub const DEFINED_ZSUPER: defined_type = 9;
+pub const DEFINED_SELF: defined_type = 10;
+pub const DEFINED_TRUE: defined_type = 11;
+pub const DEFINED_FALSE: defined_type = 12;
+pub const DEFINED_ASGN: defined_type = 13;
+pub const DEFINED_EXPR: defined_type = 14;
+pub const DEFINED_REF: defined_type = 15;
+pub const DEFINED_FUNC: defined_type = 16;
+pub const DEFINED_CONST_FROM: defined_type = 17;
+pub type defined_type = u32;
+pub const ROBJECT_OFFSET_AS_HEAP_IVPTR: robject_offsets = 16;
+pub const ROBJECT_OFFSET_AS_HEAP_IV_INDEX_TBL: robject_offsets = 24;
+pub const ROBJECT_OFFSET_AS_ARY: robject_offsets = 16;
+pub type robject_offsets = u32;
+pub const RUBY_OFFSET_RSTRING_LEN: rstring_offsets = 16;
pub type rstring_offsets = u32;
pub type rb_seq_param_keyword_struct = rb_iseq_constant_body__bindgen_ty_1_rb_iseq_param_keyword;
extern "C" {
+ pub fn ruby_xfree(ptr: *mut ::std::os::raw::c_void);
pub fn rb_class_attached_object(klass: VALUE) -> VALUE;
pub fn rb_singleton_class(obj: VALUE) -> VALUE;
pub fn rb_get_alloc_func(klass: VALUE) -> rb_alloc_func_t;
@@ -1081,9 +972,11 @@ extern "C" {
pub static mut rb_mKernel: VALUE;
pub static mut rb_cBasicObject: VALUE;
pub static mut rb_cArray: VALUE;
+ pub static mut rb_cClass: VALUE;
pub static mut rb_cFalseClass: VALUE;
pub static mut rb_cFloat: VALUE;
pub static mut rb_cHash: VALUE;
+ pub static mut rb_cIO: VALUE;
pub static mut rb_cInteger: VALUE;
pub static mut rb_cModule: VALUE;
pub static mut rb_cNilClass: VALUE;
@@ -1096,15 +989,18 @@ extern "C" {
pub fn rb_ary_store(ary: VALUE, key: ::std::os::raw::c_long, val: VALUE);
pub fn rb_ary_dup(ary: VALUE) -> VALUE;
pub fn rb_ary_resurrect(ary: VALUE) -> VALUE;
+ pub fn rb_ary_cat(ary: VALUE, train: *const VALUE, len: ::std::os::raw::c_long) -> VALUE;
pub fn rb_ary_push(ary: VALUE, elem: VALUE) -> VALUE;
pub fn rb_ary_clear(ary: VALUE) -> VALUE;
pub fn rb_hash_new() -> VALUE;
pub fn rb_hash_aref(hash: VALUE, key: VALUE) -> VALUE;
pub fn rb_hash_aset(hash: VALUE, key: VALUE, val: VALUE) -> VALUE;
pub fn rb_hash_bulk_insert(argc: ::std::os::raw::c_long, argv: *const VALUE, hash: VALUE);
+ pub fn rb_obj_is_proc(recv: VALUE) -> VALUE;
pub fn rb_sym2id(obj: VALUE) -> ID;
pub fn rb_id2sym(id: ID) -> VALUE;
pub fn rb_intern(name: *const ::std::os::raw::c_char) -> ID;
+ pub fn rb_intern2(name: *const ::std::os::raw::c_char, len: ::std::os::raw::c_long) -> ID;
pub fn rb_id2name(id: ID) -> *const ::std::os::raw::c_char;
pub fn rb_class2name(klass: VALUE) -> *const ::std::os::raw::c_char;
pub fn rb_obj_is_kind_of(obj: VALUE, klass: VALUE) -> VALUE;
@@ -1127,6 +1023,8 @@ extern "C" {
pub fn rb_ivar_defined(obj: VALUE, name: ID) -> VALUE;
pub fn rb_attr_get(obj: VALUE, name: ID) -> VALUE;
pub fn rb_obj_info_dump(obj: VALUE);
+ pub fn rb_class_allocate_instance(klass: VALUE) -> VALUE;
+ pub fn rb_obj_equal(obj1: VALUE, obj2: VALUE) -> VALUE;
pub fn rb_reg_new_ary(ary: VALUE, options: ::std::os::raw::c_int) -> VALUE;
pub fn rb_ary_tmp_new_from_values(
arg1: VALUE,
@@ -1138,6 +1036,8 @@ extern "C" {
n: ::std::os::raw::c_long,
elts: *const VALUE,
) -> VALUE;
+ pub fn rb_vm_top_self() -> VALUE;
+ pub static mut rb_vm_insns_count: u64;
pub fn rb_method_entry_at(obj: VALUE, id: ID) -> *const rb_method_entry_t;
pub fn rb_callable_method_entry(klass: VALUE, id: ID) -> *const rb_callable_method_entry_t;
pub fn rb_callable_method_entry_or_negative(
@@ -1148,29 +1048,33 @@ extern "C" {
pub static mut rb_block_param_proxy: VALUE;
pub fn rb_vm_ep_local_ep(ep: *const VALUE) -> *const VALUE;
pub fn rb_iseq_path(iseq: *const rb_iseq_t) -> VALUE;
+ pub fn rb_vm_env_write(ep: *const VALUE, index: ::std::os::raw::c_int, v: VALUE);
pub fn rb_vm_bh_to_procval(ec: *const rb_execution_context_t, block_handler: VALUE) -> VALUE;
pub fn rb_vm_frame_method_entry(
cfp: *const rb_control_frame_t,
) -> *const rb_callable_method_entry_t;
pub fn rb_obj_info(obj: VALUE) -> *const ::std::os::raw::c_char;
- pub fn rb_class_allocate_instance(klass: VALUE) -> VALUE;
+ pub fn rb_ec_stack_check(ec: *mut rb_execution_context_struct) -> ::std::os::raw::c_int;
pub fn rb_shape_id_offset() -> i32;
pub fn rb_shape_get_shape_by_id(shape_id: shape_id_t) -> *mut rb_shape_t;
pub fn rb_shape_get_shape_id(obj: VALUE) -> shape_id_t;
pub fn rb_shape_get_iv_index(shape: *mut rb_shape_t, id: ID, value: *mut attr_index_t) -> bool;
pub fn rb_shape_obj_too_complex(obj: VALUE) -> bool;
- pub fn rb_shape_transition_shape_capa(
- shape: *mut rb_shape_t,
- new_capacity: u32,
- ) -> *mut rb_shape_t;
pub fn rb_shape_get_next(shape: *mut rb_shape_t, obj: VALUE, id: ID) -> *mut rb_shape_t;
pub fn rb_shape_id(shape: *mut rb_shape_t) -> shape_id_t;
pub fn rb_gvar_get(arg1: ID) -> VALUE;
pub fn rb_gvar_set(arg1: ID, arg2: VALUE) -> VALUE;
pub fn rb_ensure_iv_list_size(obj: VALUE, len: u32, newsize: u32);
+ pub fn rb_vm_barrier();
+ pub fn rb_str_byte_substr(str_: VALUE, beg: VALUE, len: VALUE) -> VALUE;
pub fn rb_obj_as_string_result(str_: VALUE, obj: VALUE) -> VALUE;
pub fn rb_str_concat_literals(num: usize, strary: *const VALUE) -> VALUE;
- pub fn rb_ec_str_resurrect(ec: *mut rb_execution_context_struct, str_: VALUE) -> VALUE;
+ pub fn rb_ec_str_resurrect(
+ ec: *mut rb_execution_context_struct,
+ str_: VALUE,
+ chilled: bool,
+ ) -> VALUE;
+ pub fn rb_to_hash_type(obj: VALUE) -> VALUE;
pub fn rb_hash_stlike_foreach(
hash: VALUE,
func: st_foreach_callback_func,
@@ -1185,12 +1089,15 @@ extern "C" {
) -> ::std::os::raw::c_int;
pub fn rb_insn_len(insn: VALUE) -> ::std::os::raw::c_int;
pub fn rb_vm_insn_decode(encoded: VALUE) -> ::std::os::raw::c_int;
+ pub fn rb_float_plus(x: VALUE, y: VALUE) -> VALUE;
+ pub fn rb_float_minus(x: VALUE, y: VALUE) -> VALUE;
+ pub fn rb_float_mul(x: VALUE, y: VALUE) -> VALUE;
+ pub fn rb_float_div(x: VALUE, y: VALUE) -> VALUE;
pub fn rb_fix_aref(fix: VALUE, idx: VALUE) -> VALUE;
pub fn rb_vm_insn_addr2opcode(addr: *const ::std::os::raw::c_void) -> ::std::os::raw::c_int;
pub fn rb_iseq_line_no(iseq: *const rb_iseq_t, pos: usize) -> ::std::os::raw::c_uint;
pub fn rb_iseqw_to_iseq(iseqw: VALUE) -> *const rb_iseq_t;
pub fn rb_iseq_label(iseq: *const rb_iseq_t) -> VALUE;
- pub fn rb_vm_barrier();
pub fn rb_profile_frames(
start: ::std::os::raw::c_int,
limit: ::std::os::raw::c_int,
@@ -1258,11 +1165,13 @@ extern "C" {
pub fn rb_get_iseq_body_local_table_size(iseq: *const rb_iseq_t) -> ::std::os::raw::c_uint;
pub fn rb_get_iseq_body_iseq_encoded(iseq: *const rb_iseq_t) -> *mut VALUE;
pub fn rb_get_iseq_body_stack_max(iseq: *const rb_iseq_t) -> ::std::os::raw::c_uint;
+ pub fn rb_get_iseq_body_type(iseq: *const rb_iseq_t) -> rb_iseq_type;
pub fn rb_get_iseq_flags_has_lead(iseq: *const rb_iseq_t) -> bool;
pub fn rb_get_iseq_flags_has_opt(iseq: *const rb_iseq_t) -> bool;
pub fn rb_get_iseq_flags_has_kw(iseq: *const rb_iseq_t) -> bool;
pub fn rb_get_iseq_flags_has_post(iseq: *const rb_iseq_t) -> bool;
pub fn rb_get_iseq_flags_has_kwrest(iseq: *const rb_iseq_t) -> bool;
+ pub fn rb_get_iseq_flags_anon_kwrest(iseq: *const rb_iseq_t) -> bool;
pub fn rb_get_iseq_flags_has_rest(iseq: *const rb_iseq_t) -> bool;
pub fn rb_get_iseq_flags_ruby2_keywords(iseq: *const rb_iseq_t) -> bool;
pub fn rb_get_iseq_flags_has_block(iseq: *const rb_iseq_t) -> bool;
@@ -1292,10 +1201,10 @@ extern "C" {
pub fn rb_get_cfp_sp(cfp: *mut rb_control_frame_struct) -> *mut VALUE;
pub fn rb_set_cfp_pc(cfp: *mut rb_control_frame_struct, pc: *const VALUE);
pub fn rb_set_cfp_sp(cfp: *mut rb_control_frame_struct, sp: *mut VALUE);
- pub fn rb_cfp_get_iseq(cfp: *mut rb_control_frame_struct) -> *mut rb_iseq_t;
pub fn rb_get_cfp_self(cfp: *mut rb_control_frame_struct) -> VALUE;
pub fn rb_get_cfp_ep(cfp: *mut rb_control_frame_struct) -> *mut VALUE;
pub fn rb_get_cfp_ep_level(cfp: *mut rb_control_frame_struct, lv: u32) -> *const VALUE;
+ pub fn rb_vm_base_ptr(cfp: *mut rb_control_frame_struct) -> *mut VALUE;
pub fn rb_yarv_class_of(obj: VALUE) -> VALUE;
pub fn rb_yarv_str_eql_internal(str1: VALUE, str2: VALUE) -> VALUE;
pub fn rb_str_neq_internal(str1: VALUE, str2: VALUE) -> VALUE;
@@ -1304,8 +1213,15 @@ extern "C" {
pub fn rb_yjit_rb_ary_subseq_length(ary: VALUE, beg: ::std::os::raw::c_long) -> VALUE;
pub fn rb_yjit_fix_div_fix(recv: VALUE, obj: VALUE) -> VALUE;
pub fn rb_yjit_fix_mod_fix(recv: VALUE, obj: VALUE) -> VALUE;
- pub fn rb_yjit_fix_mul_fix(recv: VALUE, obj: VALUE) -> VALUE;
+ pub fn rb_yjit_ruby2_keywords_splat_p(obj: VALUE) -> usize;
+ pub fn rb_yjit_splat_varg_checks(
+ sp: *mut VALUE,
+ splat_array: VALUE,
+ cfp: *mut rb_control_frame_t,
+ ) -> VALUE;
+ pub fn rb_yjit_splat_varg_cfunc(stack_splat_array: *mut VALUE) -> ::std::os::raw::c_int;
pub fn rb_yjit_dump_iseq_loc(iseq: *const rb_iseq_t, insn_idx: u32);
+ pub fn rb_yjit_iseq_inspect(iseq: *const rb_iseq_t) -> *mut ::std::os::raw::c_char;
pub fn rb_FL_TEST(obj: VALUE, flags: VALUE) -> VALUE;
pub fn rb_FL_TEST_RAW(obj: VALUE, flags: VALUE) -> VALUE;
pub fn rb_RB_TYPE_P(obj: VALUE, t: ruby_value_type) -> bool;
@@ -1337,4 +1253,11 @@ extern "C" {
line: ::std::os::raw::c_int,
);
pub fn rb_yjit_assert_holding_vm_lock();
+ pub fn rb_yjit_sendish_sp_pops(ci: *const rb_callinfo) -> usize;
+ pub fn rb_yjit_invokeblock_sp_pops(ci: *const rb_callinfo) -> usize;
+ pub fn rb_yjit_set_exception_return(
+ cfp: *mut rb_control_frame_t,
+ leave_exit: *mut ::std::os::raw::c_void,
+ leave_exception: *mut ::std::os::raw::c_void,
+ );
}
diff --git a/yjit/src/disasm.rs b/yjit/src/disasm.rs
index 57806ccc30..7875276815 100644
--- a/yjit/src/disasm.rs
+++ b/yjit/src/disasm.rs
@@ -59,21 +59,7 @@ pub fn disasm_iseq_insn_range(iseq: IseqPtr, start_idx: u16, end_idx: u16) -> St
let global_cb = crate::codegen::CodegenGlobals::get_inline_cb();
// Sort the blocks by increasing start addresses
- block_list.sort_by(|a, b| {
- use std::cmp::Ordering;
-
- // Get the start addresses for each block
- let addr_a = a.get_start_addr().raw_ptr();
- let addr_b = b.get_start_addr().raw_ptr();
-
- if addr_a < addr_b {
- Ordering::Less
- } else if addr_a == addr_b {
- Ordering::Equal
- } else {
- Ordering::Greater
- }
- });
+ block_list.sort_by_key(|block| block.get_start_addr().as_offset());
// Compute total code size in bytes for all blocks in the function
let mut total_code_size = 0;
@@ -116,7 +102,7 @@ pub fn disasm_iseq_insn_range(iseq: IseqPtr, start_idx: u16, end_idx: u16) -> St
// Compute the size of the gap between this block and the next
let next_block = block_list[block_idx + 1];
let next_start_addr = next_block.get_start_addr();
- let gap_size = next_start_addr.into_usize() - end_addr.into_usize();
+ let gap_size = next_start_addr.as_offset() - end_addr.as_offset();
// Log the size of the gap between the blocks if nonzero
if gap_size > 0 {
@@ -180,23 +166,24 @@ pub fn disasm_addr_range(cb: &CodeBlock, start_addr: usize, end_addr: usize) ->
let start_addr = 0;
let insns = cs.disasm_all(code_slice, start_addr as u64).unwrap();
- // Colorize outlined code in blue
- if cb.outlined {
- write!(&mut out, "\x1b[34m").unwrap();
- }
// For each instruction in this block
for insn in insns.as_ref() {
// Comments for this block
if let Some(comment_list) = cb.comments_at(insn.address() as usize) {
for comment in comment_list {
+ if cb.outlined {
+ write!(&mut out, "\x1b[34m").unwrap(); // Make outlined code blue
+ }
writeln!(&mut out, " \x1b[1m# {comment}\x1b[22m").unwrap(); // Make comments bold
}
}
+ if cb.outlined {
+ write!(&mut out, "\x1b[34m").unwrap(); // Make outlined code blue
+ }
writeln!(&mut out, " {insn}").unwrap();
- }
- // Disable blue color
- if cb.outlined {
- write!(&mut out, "\x1b[0m").unwrap();
+ if cb.outlined {
+ write!(&mut out, "\x1b[0m").unwrap(); // Disable blue
+ }
}
return out;
@@ -211,8 +198,8 @@ macro_rules! assert_disasm {
{
let disasm = disasm_addr_range(
&$cb,
- $cb.get_ptr(0).raw_ptr() as usize,
- $cb.get_write_ptr().raw_ptr() as usize,
+ $cb.get_ptr(0).raw_addr(&$cb),
+ $cb.get_write_ptr().raw_addr(&$cb),
);
assert_eq!(unindent(&disasm, false), unindent(&$disasm, true));
}
diff --git a/yjit/src/invariants.rs b/yjit/src/invariants.rs
index 854ef6cf14..695a37878f 100644
--- a/yjit/src/invariants.rs
+++ b/yjit/src/invariants.rs
@@ -11,6 +11,7 @@ use crate::utils::IntoUsize;
use crate::yjit::yjit_enabled_p;
use std::collections::{HashMap, HashSet};
+use std::os::raw::c_void;
use std::mem;
// Invariants to track:
@@ -52,6 +53,17 @@ pub struct Invariants {
/// A map from a block to a set of IDs that it is assuming have not been
/// redefined.
block_constant_states: HashMap<BlockRef, HashSet<ID>>,
+
+ /// A map from a class to a set of blocks that assume objects of the class
+ /// will have no singleton class. When the set is empty, it means that
+ /// there has been a singleton class for the class after boot, so you cannot
+ /// assume no singleton class going forward.
+ no_singleton_classes: HashMap<VALUE, HashSet<BlockRef>>,
+
+ /// A map from an ISEQ to a set of blocks that assume base pointer is equal
+ /// to environment pointer. When the set is empty, it means that EP has been
+ /// escaped in the ISEQ.
+ no_ep_escape_iseqs: HashMap<IseqPtr, HashSet<BlockRef>>,
}
/// Private singleton instance of the invariants global struct.
@@ -68,6 +80,8 @@ impl Invariants {
single_ractor: HashSet::new(),
constant_state_blocks: HashMap::new(),
block_constant_states: HashMap::new(),
+ no_singleton_classes: HashMap::new(),
+ no_ep_escape_iseqs: HashMap::new(),
});
}
}
@@ -89,7 +103,9 @@ pub fn assume_bop_not_redefined(
bop: ruby_basic_operators,
) -> bool {
if unsafe { BASIC_OP_UNREDEFINED_P(bop, klass) } {
- jit_ensure_block_entry_exit(jit, asm, ocb);
+ if jit_ensure_block_entry_exit(jit, asm, ocb).is_none() {
+ return false;
+ }
jit.bop_assumptions.push((klass, bop));
return true;
@@ -127,6 +143,48 @@ pub fn track_method_lookup_stability_assumption(
.insert(uninit_block);
}
+/// Track that a block will assume that `klass` objects will have no singleton class.
+pub fn track_no_singleton_class_assumption(uninit_block: BlockRef, klass: VALUE) {
+ Invariants::get_instance()
+ .no_singleton_classes
+ .entry(klass)
+ .or_default()
+ .insert(uninit_block);
+}
+
+/// Returns true if we've seen a singleton class of a given class since boot.
+pub fn has_singleton_class_of(klass: VALUE) -> bool {
+ Invariants::get_instance()
+ .no_singleton_classes
+ .get(&klass)
+ .map_or(false, |blocks| blocks.is_empty())
+}
+
+/// Track that a block will assume that base pointer is equal to environment pointer.
+pub fn track_no_ep_escape_assumption(uninit_block: BlockRef, iseq: IseqPtr) {
+ Invariants::get_instance()
+ .no_ep_escape_iseqs
+ .entry(iseq)
+ .or_default()
+ .insert(uninit_block);
+}
+
+/// Returns true if a given ISEQ has previously escaped an environment.
+pub fn iseq_escapes_ep(iseq: IseqPtr) -> bool {
+ Invariants::get_instance()
+ .no_ep_escape_iseqs
+ .get(&iseq)
+ .map_or(false, |blocks| blocks.is_empty())
+}
+
+/// Forget an ISEQ remembered in invariants
+pub fn iseq_free_invariants(iseq: IseqPtr) {
+ if unsafe { INVARIANTS.is_none() } {
+ return;
+ }
+ Invariants::get_instance().no_ep_escape_iseqs.remove(&iseq);
+}
+
// Checks rb_method_basic_definition_p and registers the current block for invalidation if method
// lookup changes.
// A "basic method" is one defined during VM boot, so we can use this to check assumptions based on
@@ -153,7 +211,9 @@ pub fn assume_single_ractor_mode(jit: &mut JITState, asm: &mut Assembler, ocb: &
if unsafe { rb_yjit_multi_ractor_p() } {
false
} else {
- jit_ensure_block_entry_exit(jit, asm, ocb);
+ if jit_ensure_block_entry_exit(jit, asm, ocb).is_none() {
+ return false;
+ }
jit.block_assumes_single_ractor = true;
true
@@ -174,7 +234,7 @@ pub fn track_stable_constant_names_assumption(uninit_block: BlockRef, idlist: *c
uninit_block: BlockRef,
id: ID,
) {
- if id == idNULL as u64 {
+ if id == ID!(NULL) {
// Used for :: prefix
return;
}
@@ -288,7 +348,7 @@ pub extern "C" fn rb_yjit_constant_state_changed(id: ID) {
/// Callback for marking GC objects inside [Invariants].
/// See `struct yjijt_root_struct` in C.
#[no_mangle]
-pub extern "C" fn rb_yjit_root_mark() {
+pub extern "C" fn rb_yjit_root_mark(_: *mut c_void) {
// Call rb_gc_mark on exit location's raw_samples to
// wrap frames in a GC allocated object. This needs to be called
// at the same time as root mark.
@@ -316,6 +376,23 @@ pub extern "C" fn rb_yjit_root_mark() {
}
}
+#[no_mangle]
+pub extern "C" fn rb_yjit_root_update_references(_: *mut c_void) {
+ if unsafe { INVARIANTS.is_none() } {
+ return;
+ }
+ let no_ep_escape_iseqs = &mut Invariants::get_instance().no_ep_escape_iseqs;
+
+ // Make a copy of the table with updated ISEQ keys
+ let mut updated_copy = HashMap::with_capacity(no_ep_escape_iseqs.len());
+ for (iseq, blocks) in mem::take(no_ep_escape_iseqs) {
+ let new_iseq = unsafe { rb_gc_location(iseq.into()) }.as_iseq();
+ updated_copy.insert(new_iseq, blocks);
+ }
+
+ *no_ep_escape_iseqs = updated_copy;
+}
+
/// Remove all invariant assumptions made by the block by removing the block as
/// as a key in all of the relevant tables.
/// For safety, the block has to be initialized and the vm lock must be held.
@@ -386,6 +463,15 @@ pub fn block_assumptions_free(blockref: BlockRef) {
if invariants.constant_state_blocks.is_empty() {
invariants.constant_state_blocks.shrink_to_fit();
}
+
+ // Remove tracking for blocks assuming no singleton class
+ for (_, blocks) in invariants.no_singleton_classes.iter_mut() {
+ blocks.remove(&blockref);
+ }
+ // Remove tracking for blocks assuming EP doesn't escape
+ for (_, blocks) in invariants.no_ep_escape_iseqs.iter_mut() {
+ blocks.remove(&blockref);
+ }
}
/// Callback from the opt_setinlinecache instruction in the interpreter.
@@ -402,7 +488,7 @@ pub extern "C" fn rb_yjit_constant_ic_update(iseq: *const rb_iseq_t, ic: IC, ins
let insn_idx: IseqIdx = if let Ok(idx) = insn_idx.try_into() {
idx
} else {
- // The index is too large, YJIT can't possibily have code for it,
+ // The index is too large, YJIT can't possibly have code for it,
// so there is nothing to invalidate.
return;
};
@@ -452,6 +538,67 @@ pub extern "C" fn rb_yjit_constant_ic_update(iseq: *const rb_iseq_t, ic: IC, ins
});
}
+/// Invalidate blocks that assume objects of a given class will have no singleton class.
+#[no_mangle]
+pub extern "C" fn rb_yjit_invalidate_no_singleton_class(klass: VALUE) {
+ // Skip tracking singleton classes during boot. Such objects already have a singleton class
+ // before entering JIT code, so they get rejected when they're checked for the first time.
+ if unsafe { INVARIANTS.is_none() } {
+ return;
+ }
+
+ // We apply this optimization only to Array, Hash, and String for now.
+ if unsafe { [rb_cArray, rb_cHash, rb_cString].contains(&klass) } {
+ with_vm_lock(src_loc!(), || {
+ let no_singleton_classes = &mut Invariants::get_instance().no_singleton_classes;
+ match no_singleton_classes.get_mut(&klass) {
+ Some(blocks) => {
+ // Invalidate existing blocks and let has_singleton_class_of()
+ // return true when they are compiled again
+ for block in mem::take(blocks) {
+ invalidate_block_version(&block);
+ incr_counter!(invalidate_no_singleton_class);
+ }
+ }
+ None => {
+ // Let has_singleton_class_of() return true for this class
+ no_singleton_classes.insert(klass, HashSet::new());
+ }
+ }
+ });
+ }
+}
+
+/// Invalidate blocks for a given ISEQ that assumes environment pointer is
+/// equal to base pointer.
+#[no_mangle]
+pub extern "C" fn rb_yjit_invalidate_ep_is_bp(iseq: IseqPtr) {
+ // Skip tracking EP escapes on boot. We don't need to invalidate anything during boot.
+ if unsafe { INVARIANTS.is_none() } {
+ return;
+ }
+
+ with_vm_lock(src_loc!(), || {
+ // If an EP escape for this ISEQ is detected for the first time, invalidate all blocks
+ // associated to the ISEQ.
+ let no_ep_escape_iseqs = &mut Invariants::get_instance().no_ep_escape_iseqs;
+ match no_ep_escape_iseqs.get_mut(&iseq) {
+ Some(blocks) => {
+ // Invalidate existing blocks and let jit.ep_is_bp()
+ // return true when they are compiled again
+ for block in mem::take(blocks) {
+ invalidate_block_version(&block);
+ incr_counter!(invalidate_no_singleton_class);
+ }
+ }
+ None => {
+ // Let jit.ep_is_bp() return false for this ISEQ
+ no_ep_escape_iseqs.insert(iseq, HashSet::new());
+ }
+ }
+ });
+}
+
// Invalidate all generated code and patch C method return code to contain
// logic for firing the c_return TracePoint event. Once rb_vm_barrier()
// returns, all other ractors are pausing inside RB_VM_LOCK_ENTER(), which
@@ -513,22 +660,40 @@ pub extern "C" fn rb_yjit_tracing_invalidate_all() {
let cb = CodegenGlobals::get_inline_cb();
+ // Prevent on-stack frames from jumping to the caller on jit_exec_exception
+ extern "C" {
+ fn rb_yjit_cancel_jit_return(leave_exit: *mut c_void, leave_exception: *mut c_void) -> VALUE;
+ }
+ unsafe {
+ rb_yjit_cancel_jit_return(
+ CodegenGlobals::get_leave_exit_code().raw_ptr(cb) as _,
+ CodegenGlobals::get_leave_exception_code().raw_ptr(cb) as _,
+ );
+ }
+
// Apply patches
let old_pos = cb.get_write_pos();
let old_dropped_bytes = cb.has_dropped_bytes();
let mut patches = CodegenGlobals::take_global_inval_patches();
- patches.sort_by_cached_key(|patch| patch.inline_patch_pos.raw_ptr());
+ patches.sort_by_cached_key(|patch| patch.inline_patch_pos.raw_ptr(cb));
let mut last_patch_end = std::ptr::null();
for patch in &patches {
- assert!(last_patch_end <= patch.inline_patch_pos.raw_ptr(), "patches should not overlap");
-
- let mut asm = crate::backend::ir::Assembler::new();
- asm.jmp(patch.outlined_target_pos.as_side_exit());
+ let patch_pos = patch.inline_patch_pos.raw_ptr(cb);
+ assert!(
+ last_patch_end <= patch_pos,
+ "patches should not overlap (last_patch_end: {last_patch_end:?}, patch_pos: {patch_pos:?})",
+ );
cb.set_write_ptr(patch.inline_patch_pos);
cb.set_dropped_bytes(false);
- asm.compile(cb, None);
- last_patch_end = cb.get_write_ptr().raw_ptr();
+ cb.without_page_end_reserve(|cb| {
+ let mut asm = crate::backend::ir::Assembler::new();
+ asm.jmp(patch.outlined_target_pos.as_side_exit());
+ if asm.compile(cb, None).is_none() {
+ panic!("Failed to apply patch at {:?}", patch.inline_patch_pos);
+ }
+ });
+ last_patch_end = cb.get_write_ptr().raw_ptr(cb);
}
cb.set_pos(old_pos);
cb.set_dropped_bytes(old_dropped_bytes);
diff --git a/yjit/src/lib.rs b/yjit/src/lib.rs
index ce87cc250a..3f3d24be4b 100644
--- a/yjit/src/lib.rs
+++ b/yjit/src/lib.rs
@@ -3,8 +3,7 @@
#![allow(clippy::too_many_arguments)] // :shrug:
#![allow(clippy::identity_op)] // Sometimes we do it for style
-
-mod asm;
+pub mod asm;
mod backend;
mod codegen;
mod core;
diff --git a/yjit/src/options.rs b/yjit/src/options.rs
index 78a507ce11..59ec864bf5 100644
--- a/yjit/src/options.rs
+++ b/yjit/src/options.rs
@@ -1,5 +1,27 @@
-use std::ffi::CStr;
-use crate::backend::ir::Assembler;
+use std::{ffi::{CStr, CString}, ptr::null, fs::File};
+use crate::{backend::current::TEMP_REGS, stats::Counter};
+use std::os::raw::{c_char, c_int, c_uint};
+
+// Call threshold for small deployments and command-line apps
+pub static SMALL_CALL_THRESHOLD: u64 = 30;
+
+// Call threshold for larger deployments and production-sized applications
+pub static LARGE_CALL_THRESHOLD: u64 = 120;
+
+// Number of live ISEQs after which we consider an app to be large
+pub static LARGE_ISEQ_COUNT: u64 = 40_000;
+
+// This option is exposed to the C side in a global variable for performance, see vm.c
+// Number of method calls after which to start generating code
+// Threshold==1 means compile on first execution
+#[no_mangle]
+pub static mut rb_yjit_call_threshold: u64 = SMALL_CALL_THRESHOLD;
+
+// This option is exposed to the C side in a global variable for performance, see vm.c
+// Number of execution requests after which a method is no longer
+// considered hot. Raising this results in more generated code.
+#[no_mangle]
+pub static mut rb_yjit_cold_threshold: u64 = 200_000;
// Command-line options
#[derive(Clone, PartialEq, Eq, Debug)]
@@ -9,13 +31,6 @@ pub struct Options {
// Note that the command line argument is expressed in MiB and not bytes
pub exec_mem_size: usize,
- // Number of method calls after which to start generating code
- // Threshold==1 means compile on first execution
- pub call_threshold: usize,
-
- // Generate versions greedily until the limit is hit
- pub greedy_versioning: bool,
-
// Disable the propagation of type information
pub no_type_prop: bool,
@@ -26,18 +41,21 @@ pub struct Options {
// The number of registers allocated for stack temps
pub num_temp_regs: usize,
- // Capture and print out stats
+ // Capture stats
pub gen_stats: bool,
+ // Print stats on exit (when gen_stats is also true)
+ pub print_stats: bool,
+
// Trace locations of exits
- pub gen_trace_exits: bool,
+ pub trace_exits: Option<TraceExits>,
// how often to sample exit trace data
pub trace_exits_sample_rate: usize,
- // Whether to start YJIT in paused state (initialize YJIT but don't
- // compile anything)
- pub pause: bool,
+ // Whether to enable YJIT at boot. This option prevents other
+ // YJIT tuning options from enabling YJIT at boot.
+ pub disable: bool,
/// Dump compiled and executed instructions for debugging
pub dump_insns: bool,
@@ -50,26 +68,58 @@ pub struct Options {
/// Verify context objects (debug mode only)
pub verify_ctx: bool,
+
+ /// Enable generating frame pointers (for x86. arm64 always does this)
+ pub frame_pointer: bool,
+
+ /// Run code GC when exec_mem_size is reached.
+ pub code_gc: bool,
+
+ /// Enable writing /tmp/perf-{pid}.map for Linux perf
+ pub perf_map: Option<PerfMap>,
}
// Initialize the options to default values
pub static mut OPTIONS: Options = Options {
- exec_mem_size: 64 * 1024 * 1024,
- call_threshold: 30,
- greedy_versioning: false,
+ exec_mem_size: 48 * 1024 * 1024,
no_type_prop: false,
max_versions: 4,
num_temp_regs: 5,
gen_stats: false,
- gen_trace_exits: false,
+ trace_exits: None,
+ print_stats: true,
trace_exits_sample_rate: 0,
- pause: false,
+ disable: false,
dump_insns: false,
dump_disasm: None,
verify_ctx: false,
dump_iseq_disasm: None,
+ frame_pointer: false,
+ code_gc: false,
+ perf_map: None,
};
+/// YJIT option descriptions for `ruby --help`.
+static YJIT_OPTIONS: [(&str, &str); 9] = [
+ ("--yjit-exec-mem-size=num", "Size of executable memory block in MiB (default: 48)."),
+ ("--yjit-call-threshold=num", "Number of calls to trigger JIT."),
+ ("--yjit-cold-threshold=num", "Global calls after which ISEQs not compiled (default: 200K)."),
+ ("--yjit-stats", "Enable collecting YJIT statistics."),
+ ("--yjit-disable", "Disable YJIT for lazily enabling it with RubyVM::YJIT.enable."),
+ ("--yjit-code-gc", "Run code GC when the code size reaches the limit."),
+ ("--yjit-perf", "Enable frame pointers and perf profiling."),
+ ("--yjit-trace-exits", "Record Ruby source location when exiting from generated code."),
+ ("--yjit-trace-exits-sample-rate=num", "Trace exit locations only every Nth occurrence."),
+];
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub enum TraceExits {
+ // Trace all exits
+ All,
+ // Trace a specific counted exit
+ CountedExit(Counter),
+}
+
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum DumpDisasm {
// Dump to stdout
@@ -78,12 +128,26 @@ pub enum DumpDisasm {
File(String),
}
+/// Type of symbols to dump into /tmp/perf-{pid}.map
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub enum PerfMap {
+ // Dump ISEQ symbols
+ ISEQ,
+ // Dump YJIT codegen symbols
+ Codegen,
+}
+
/// Macro to get an option value by name
macro_rules! get_option {
// Unsafe is ok here because options are initialized
// once before any Ruby code executes
($option_name:ident) => {
- unsafe { OPTIONS.$option_name }
+ {
+ // Make this a statement since attributes on expressions are experimental
+ #[allow(unused_unsafe)]
+ let ret = unsafe { OPTIONS.$option_name };
+ ret
+ }
};
}
pub(crate) use get_option;
@@ -133,7 +197,14 @@ pub fn parse_option(str_ptr: *const std::os::raw::c_char) -> Option<()> {
},
("call-threshold", _) => match opt_val.parse() {
- Ok(n) => unsafe { OPTIONS.call_threshold = n },
+ Ok(n) => unsafe { rb_yjit_call_threshold = n },
+ Err(_) => {
+ return None;
+ }
+ },
+
+ ("cold-threshold", _) => match opt_val.parse() {
+ Ok(n) => unsafe { rb_yjit_cold_threshold = n },
Err(_) => {
return None;
}
@@ -146,13 +217,13 @@ pub fn parse_option(str_ptr: *const std::os::raw::c_char) -> Option<()> {
}
},
- ("pause", "") => unsafe {
- OPTIONS.pause = true;
+ ("disable", "") => unsafe {
+ OPTIONS.disable = true;
},
("temp-regs", _) => match opt_val.parse() {
Ok(n) => {
- assert!(n <= Assembler::TEMP_REGS.len(), "--yjit-temp-regs must be <= {}", Assembler::TEMP_REGS.len());
+ assert!(n <= TEMP_REGS.len(), "--yjit-temp-regs must be <= {}", TEMP_REGS.len());
unsafe { OPTIONS.num_temp_regs = n }
}
Err(_) => {
@@ -160,25 +231,78 @@ pub fn parse_option(str_ptr: *const std::os::raw::c_char) -> Option<()> {
}
},
- ("dump-disasm", _) => match opt_val.to_string().as_str() {
- "" => unsafe { OPTIONS.dump_disasm = Some(DumpDisasm::Stdout) },
- directory => {
- let pid = std::process::id();
- let path = format!("{directory}/yjit_{pid}.log");
- println!("YJIT disasm dump: {path}");
- unsafe { OPTIONS.dump_disasm = Some(DumpDisasm::File(path)) }
- }
+ ("code-gc", _) => unsafe {
+ OPTIONS.code_gc = true;
+ },
+
+ ("perf", _) => match opt_val {
+ "" => unsafe {
+ OPTIONS.frame_pointer = true;
+ OPTIONS.perf_map = Some(PerfMap::ISEQ);
+ },
+ "fp" => unsafe { OPTIONS.frame_pointer = true },
+ "iseq" => unsafe { OPTIONS.perf_map = Some(PerfMap::ISEQ) },
+ // Accept --yjit-perf=map for backward compatibility
+ "codegen" | "map" => unsafe { OPTIONS.perf_map = Some(PerfMap::Codegen) },
+ _ => return None,
},
+ ("dump-disasm", _) => {
+ if !cfg!(feature = "disasm") {
+ eprintln!("WARNING: the {} option is only available when YJIT is built in dev mode, i.e. ./configure --enable-yjit=dev", opt_name);
+ }
+
+ match opt_val {
+ "" => unsafe { OPTIONS.dump_disasm = Some(DumpDisasm::Stdout) },
+ directory => {
+ let path = format!("{directory}/yjit_{}.log", std::process::id());
+ match File::options().create(true).append(true).open(&path) {
+ Ok(_) => {
+ eprintln!("YJIT disasm dump: {path}");
+ unsafe { OPTIONS.dump_disasm = Some(DumpDisasm::File(path)) }
+ }
+ Err(err) => eprintln!("Failed to create {path}: {err}"),
+ }
+ }
+ }
+ },
+
("dump-iseq-disasm", _) => unsafe {
+ if !cfg!(feature = "disasm") {
+ eprintln!("WARNING: the {} option is only available when YJIT is built in dev mode, i.e. ./configure --enable-yjit=dev", opt_name);
+ }
+
OPTIONS.dump_iseq_disasm = Some(opt_val.to_string());
},
- ("greedy-versioning", "") => unsafe { OPTIONS.greedy_versioning = true },
("no-type-prop", "") => unsafe { OPTIONS.no_type_prop = true },
- ("stats", "") => unsafe { OPTIONS.gen_stats = true },
- ("trace-exits", "") => unsafe { OPTIONS.gen_trace_exits = true; OPTIONS.gen_stats = true; OPTIONS.trace_exits_sample_rate = 0 },
- ("trace-exits-sample-rate", sample_rate) => unsafe { OPTIONS.gen_trace_exits = true; OPTIONS.gen_stats = true; OPTIONS.trace_exits_sample_rate = sample_rate.parse().unwrap(); },
+ ("stats", _) => match opt_val {
+ "" => unsafe { OPTIONS.gen_stats = true },
+ "quiet" => unsafe {
+ OPTIONS.gen_stats = true;
+ OPTIONS.print_stats = false;
+ },
+ _ => {
+ return None;
+ }
+ },
+ ("trace-exits", _) => unsafe {
+ OPTIONS.gen_stats = true;
+ OPTIONS.trace_exits = match opt_val {
+ "" => Some(TraceExits::All),
+ name => match Counter::get(name) {
+ Some(counter) => Some(TraceExits::CountedExit(counter)),
+ None => return None,
+ },
+ };
+ },
+ ("trace-exits-sample-rate", sample_rate) => unsafe {
+ OPTIONS.gen_stats = true;
+ if OPTIONS.trace_exits.is_none() {
+ OPTIONS.trace_exits = Some(TraceExits::All);
+ }
+ OPTIONS.trace_exits_sample_rate = sample_rate.parse().unwrap();
+ },
("dump-insns", "") => unsafe { OPTIONS.dump_insns = true },
("verify-ctx", "") => unsafe { OPTIONS.verify_ctx = true },
@@ -206,3 +330,18 @@ pub fn parse_option(str_ptr: *const std::os::raw::c_char) -> Option<()> {
// Option successfully parsed
return Some(());
}
+
+/// Print YJIT options for `ruby --help`. `width` is width of option parts, and
+/// `columns` is indent width of descriptions.
+#[no_mangle]
+pub extern "C" fn rb_yjit_show_usage(help: c_int, highlight: c_int, width: c_uint, columns: c_int) {
+ for &(name, description) in YJIT_OPTIONS.iter() {
+ extern "C" {
+ fn ruby_show_usage_line(name: *const c_char, secondary: *const c_char, description: *const c_char,
+ help: c_int, highlight: c_int, width: c_uint, columns: c_int);
+ }
+ let name = CString::new(name).unwrap();
+ let description = CString::new(description).unwrap();
+ unsafe { ruby_show_usage_line(name.as_ptr(), null(), description.as_ptr(), help, highlight, width, columns) }
+ }
+}
diff --git a/yjit/src/stats.rs b/yjit/src/stats.rs
index 91ecc8209b..0a63fab8b0 100644
--- a/yjit/src/stats.rs
+++ b/yjit/src/stats.rs
@@ -3,6 +3,12 @@
#![allow(dead_code)] // Counters are only used with the stats features
+use std::alloc::{GlobalAlloc, Layout, System};
+use std::ptr::addr_of_mut;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::time::Instant;
+use std::collections::HashMap;
+
use crate::codegen::CodegenGlobals;
use crate::core::Context;
use crate::core::for_each_iseq_payload;
@@ -10,13 +16,112 @@ use crate::cruby::*;
use crate::options::*;
use crate::yjit::yjit_enabled_p;
-// stats_alloc is a middleware to instrument global allocations in Rust.
-#[cfg(feature="stats")]
+/// Running total of how many ISeqs are in the system.
+#[no_mangle]
+pub static mut rb_yjit_live_iseq_count: u64 = 0;
+
+/// Monotonically increasing total of how many ISEQs were allocated
+#[no_mangle]
+pub static mut rb_yjit_iseq_alloc_count: u64 = 0;
+
+/// A middleware to count Rust-allocated bytes as yjit_alloc_size.
#[global_allocator]
-static GLOBAL_ALLOCATOR: &stats_alloc::StatsAlloc<std::alloc::System> = &stats_alloc::INSTRUMENTED_SYSTEM;
+static GLOBAL_ALLOCATOR: StatsAlloc = StatsAlloc { alloc_size: AtomicUsize::new(0) };
+
+pub struct StatsAlloc {
+ alloc_size: AtomicUsize,
+}
+
+unsafe impl GlobalAlloc for StatsAlloc {
+ unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+ self.alloc_size.fetch_add(layout.size(), Ordering::SeqCst);
+ System.alloc(layout)
+ }
+
+ unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
+ self.alloc_size.fetch_sub(layout.size(), Ordering::SeqCst);
+ System.dealloc(ptr, layout)
+ }
+
+ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
+ self.alloc_size.fetch_add(layout.size(), Ordering::SeqCst);
+ System.alloc_zeroed(layout)
+ }
+
+ unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+ if new_size > layout.size() {
+ self.alloc_size.fetch_add(new_size - layout.size(), Ordering::SeqCst);
+ } else if new_size < layout.size() {
+ self.alloc_size.fetch_sub(layout.size() - new_size, Ordering::SeqCst);
+ }
+ System.realloc(ptr, layout, new_size)
+ }
+}
+
+/// Mapping of C function / ISEQ name to integer indices
+/// This is accessed at compilation time only (protected by a lock)
+static mut CFUNC_NAME_TO_IDX: Option<HashMap<String, usize>> = None;
+static mut ISEQ_NAME_TO_IDX: Option<HashMap<String, usize>> = None;
+
+/// Vector of call counts for each C function / ISEQ index
+/// This is modified (but not resized) by JITted code
+static mut CFUNC_CALL_COUNT: Option<Vec<u64>> = None;
+static mut ISEQ_CALL_COUNT: Option<Vec<u64>> = None;
+
+/// Assign an index to a given cfunc name string
+pub fn get_cfunc_idx(name: &str) -> usize {
+ // SAFETY: We acquire a VM lock and don't create multiple &mut references to these static mut variables.
+ unsafe { get_method_idx(name, &mut *addr_of_mut!(CFUNC_NAME_TO_IDX), &mut *addr_of_mut!(CFUNC_CALL_COUNT)) }
+}
+
+/// Assign an index to a given ISEQ name string
+pub fn get_iseq_idx(name: &str) -> usize {
+ // SAFETY: We acquire a VM lock and don't create multiple &mut references to these static mut variables.
+ unsafe { get_method_idx(name, &mut *addr_of_mut!(ISEQ_NAME_TO_IDX), &mut *addr_of_mut!(ISEQ_CALL_COUNT)) }
+}
+
+fn get_method_idx(
+ name: &str,
+ method_name_to_idx: &mut Option<HashMap<String, usize>>,
+ method_call_count: &mut Option<Vec<u64>>,
+) -> usize {
+ //println!("{}", name);
+
+ let name_to_idx = method_name_to_idx.get_or_insert_with(HashMap::default);
+ let call_count = method_call_count.get_or_insert_with(Vec::default);
+
+ match name_to_idx.get(name) {
+ Some(idx) => *idx,
+ None => {
+ let idx = name_to_idx.len();
+ name_to_idx.insert(name.to_string(), idx);
+
+ // Resize the call count vector
+ if idx >= call_count.len() {
+ call_count.resize(idx + 1, 0);
+ }
+
+ idx
+ }
+ }
+}
+
+// Increment the counter for a C function
+pub extern "C" fn incr_cfunc_counter(idx: usize) {
+ let cfunc_call_count = unsafe { CFUNC_CALL_COUNT.as_mut().unwrap() };
+ assert!(idx < cfunc_call_count.len());
+ cfunc_call_count[idx] += 1;
+}
+
+// Increment the counter for an ISEQ
+pub extern "C" fn incr_iseq_counter(idx: usize) {
+ let iseq_call_count = unsafe { ISEQ_CALL_COUNT.as_mut().unwrap() };
+ assert!(idx < iseq_call_count.len());
+ iseq_call_count[idx] += 1;
+}
// YJIT exit counts for each instruction type
-const VM_INSTRUCTION_SIZE_USIZE:usize = VM_INSTRUCTION_SIZE as usize;
+const VM_INSTRUCTION_SIZE_USIZE: usize = VM_INSTRUCTION_SIZE as usize;
static mut EXIT_OP_COUNT: [u64; VM_INSTRUCTION_SIZE_USIZE] = [0; VM_INSTRUCTION_SIZE_USIZE];
/// Global state needed for collecting backtraces of exits
@@ -37,13 +142,8 @@ static mut YJIT_EXIT_LOCATIONS: Option<YjitExitLocations> = None;
impl YjitExitLocations {
/// Initialize the yjit exit locations
pub fn init() {
- // Return if the stats feature is disabled
- if !cfg!(feature = "stats") {
- return;
- }
-
// Return if --yjit-trace-exits isn't enabled
- if !get_option!(gen_trace_exits) {
+ if get_option!(trace_exits).is_none() {
return;
}
@@ -91,13 +191,8 @@ impl YjitExitLocations {
return;
}
- // Return if the stats feature is disabled
- if !cfg!(feature = "stats") {
- return;
- }
-
// Return if --yjit-trace-exits isn't enabled
- if !get_option!(gen_trace_exits) {
+ if get_option!(trace_exits).is_none() {
return;
}
@@ -139,6 +234,14 @@ macro_rules! make_counters {
pub enum Counter { $($counter_name),+ }
impl Counter {
+ /// Map a counter name string to a counter enum
+ pub fn get(name: &str) -> Option<Counter> {
+ match name {
+ $( stringify!($counter_name) => { Some(Counter::$counter_name) } ),+
+ _ => None,
+ }
+ }
+
/// Get a counter name string
pub fn get_name(&self) -> String {
match self {
@@ -163,6 +266,28 @@ macro_rules! make_counters {
}
}
+/// The list of counters that are available without --yjit-stats.
+/// They are incremented only by `incr_counter!` and don't use `gen_counter_incr`.
+pub const DEFAULT_COUNTERS: [Counter; 16] = [
+ Counter::code_gc_count,
+ Counter::compiled_iseq_entry,
+ Counter::cold_iseq_entry,
+ Counter::compiled_iseq_count,
+ Counter::compiled_blockid_count,
+ Counter::compiled_block_count,
+ Counter::compiled_branch_count,
+ Counter::compile_time_ns,
+ Counter::max_inline_versions,
+
+ Counter::invalidation_count,
+ Counter::invalidate_method_lookup,
+ Counter::invalidate_bop_redefined,
+ Counter::invalidate_ractor_spawn,
+ Counter::invalidate_constant_state_bump,
+ Counter::invalidate_constant_ic_fill,
+ Counter::invalidate_no_singleton_class,
+];
+
/// Macro to increase a counter by name and count
macro_rules! incr_counter_by {
// Unsafe is ok here because options are initialized
@@ -176,6 +301,24 @@ macro_rules! incr_counter_by {
}
pub(crate) use incr_counter_by;
+/// Macro to increase a counter if the given value is larger
+macro_rules! incr_counter_to {
+ // Unsafe is ok here because options are initialized
+ // once before any Ruby code executes
+ ($counter_name:ident, $count:expr) => {
+ #[allow(unused_unsafe)]
+ {
+ unsafe {
+ $crate::stats::COUNTERS.$counter_name = u64::max(
+ $crate::stats::COUNTERS.$counter_name,
+ $count as u64,
+ )
+ }
+ }
+ };
+}
+pub(crate) use incr_counter_to;
+
/// Macro to increment a counter by name
macro_rules! incr_counter {
// Unsafe is ok here because options are initialized
@@ -202,123 +345,128 @@ pub(crate) use ptr_to_counter;
// Declare all the counters we track
make_counters! {
- exec_instruction,
-
- send_keywords,
- send_klass_megamorphic,
- send_kw_splat,
- send_args_splat_super,
- send_iseq_zsuper,
- send_block_arg,
+ yjit_insns_count,
+
+ // Method calls that fallback to dynamic dispatch
+ send_singleton_class,
send_ivar_set_method,
send_zsuper_method,
send_undef_method,
- send_optimized_method,
- send_optimized_method_call,
send_optimized_method_block_call,
send_call_block,
send_call_kwarg,
send_call_multi_ractor,
+ send_cme_not_found,
+ send_megamorphic,
send_missing_method,
send_refined_method,
- send_cfunc_ruby_array_varg,
+ send_private_not_fcall,
+ send_cfunc_kw_splat_non_nil,
+ send_cfunc_splat_neg2,
send_cfunc_argc_mismatch,
+ send_cfunc_block_arg,
send_cfunc_toomany_args,
send_cfunc_tracing,
- send_cfunc_kwargs,
send_cfunc_splat_with_kw,
- send_cfunc_splat_send,
+ send_cfunc_splat_varg_ruby2_keywords,
send_attrset_kwargs,
+ send_attrset_block_arg,
send_iseq_tailcall,
send_iseq_arity_error,
- send_iseq_only_keywords,
- send_iseq_kwargs_req_and_opt_missing,
+ send_iseq_block_arg_type,
+ send_iseq_clobbering_block_arg,
+ send_iseq_complex_discard_extras,
+ send_iseq_leaf_builtin_block_arg_block_param,
+ send_iseq_kw_splat_non_nil,
send_iseq_kwargs_mismatch,
send_iseq_has_post,
- send_iseq_has_kwrest,
send_iseq_has_no_kw,
send_iseq_accepts_no_kwarg,
send_iseq_materialized_block,
- send_iseq_splat_with_opt,
+ send_iseq_splat_not_array,
send_iseq_splat_with_kw,
send_iseq_missing_optional_kw,
send_iseq_too_many_kwargs,
send_not_implemented_method,
send_getter_arity,
- send_se_cf_overflow,
- send_se_protected_check_failed,
- send_splatarray_length_not_equal,
- send_splatarray_last_ruby_2_keywords,
- send_splat_not_array,
- send_args_splat_non_iseq,
- send_args_splat_ivar,
+ send_getter_block_arg,
send_args_splat_attrset,
send_args_splat_bmethod,
send_args_splat_aref,
send_args_splat_aset,
send_args_splat_opt_call,
- send_args_splat_cfunc_var_args,
- send_args_splat_cfunc_zuper,
- send_args_splat_cfunc_ruby2_keywords,
send_iseq_splat_arity_error,
send_splat_too_long,
- send_iseq_ruby2_keywords,
- send_send_not_imm,
send_send_wrong_args,
send_send_null_mid,
send_send_null_cme,
send_send_nested,
- send_send_chain,
- send_send_chain_string,
- send_send_chain_not_string,
- send_send_chain_not_sym,
- send_send_chain_not_string_or_sym,
- send_send_getter,
- send_send_builtin,
+ send_send_attr_reader,
+ send_send_attr_writer,
send_iseq_has_rest_and_captured,
- send_iseq_has_rest_and_send,
+ send_iseq_has_kwrest_and_captured,
send_iseq_has_rest_and_kw_supplied,
send_iseq_has_rest_opt_and_block,
- send_iseq_has_rest_and_splat_not_equal,
- send_is_a_class_mismatch,
- send_instance_of_class_mismatch,
- send_interrupted,
- send_not_fixnums,
- send_not_string,
- send_mid_mismatch,
-
send_bmethod_ractor,
send_bmethod_block_arg,
-
- traced_cfunc_return,
-
- invokesuper_me_changed,
- invokesuper_block,
-
+ send_optimized_block_arg,
+
+ invokesuper_defined_class_mismatch,
+ invokesuper_kw_splat,
+ invokesuper_kwarg,
+ invokesuper_megamorphic,
+ invokesuper_no_cme,
+ invokesuper_no_me,
+ invokesuper_not_iseq_or_cfunc,
+ invokesuper_refinement,
+ invokesuper_singleton_class,
+
+ invokeblock_megamorphic,
invokeblock_none,
- invokeblock_iseq_arg0_has_kw,
+ invokeblock_iseq_arg0_optional,
invokeblock_iseq_arg0_args_splat,
invokeblock_iseq_arg0_not_array,
invokeblock_iseq_arg0_wrong_len,
- invokeblock_iseq_block_changed,
- invokeblock_tag_changed,
+ invokeblock_iseq_not_inlined,
invokeblock_ifunc_args_splat,
invokeblock_ifunc_kw_splat,
invokeblock_proc,
invokeblock_symbol,
+ // Method calls that exit to the interpreter
+ guard_send_block_arg_type,
+ guard_send_getter_splat_non_empty,
+ guard_send_klass_megamorphic,
+ guard_send_se_cf_overflow,
+ guard_send_se_protected_check_failed,
+ guard_send_splatarray_length_not_equal,
+ guard_send_splatarray_last_ruby2_keywords,
+ guard_send_splat_not_array,
+ guard_send_send_name_chain,
+ guard_send_iseq_has_rest_and_splat_too_few,
+ guard_send_is_a_class_mismatch,
+ guard_send_instance_of_class_mismatch,
+ guard_send_interrupted,
+ guard_send_not_fixnums,
+ guard_send_not_fixnum_or_flonum,
+ guard_send_not_string,
+ guard_send_respond_to_mid_mismatch,
+
+ guard_send_cfunc_bad_splat_vargs,
+
+ guard_invokesuper_me_changed,
+
+ guard_invokeblock_tag_changed,
+ guard_invokeblock_iseq_block_changed,
+
+ traced_cfunc_return,
+
leave_se_interrupt,
leave_interp_return,
- getivar_se_self_not_heap,
- getivar_idx_out_of_range,
getivar_megamorphic,
getivar_not_heap,
- setivar_se_self_not_heap,
- setivar_idx_out_of_range,
- setivar_val_heapobject,
- setivar_name_not_mapped,
setivar_not_heap,
setivar_frozen,
setivar_megamorphic,
@@ -328,12 +476,23 @@ make_counters! {
setlocal_wb_required,
+ invokebuiltin_too_many_args,
+
opt_plus_overflow,
opt_minus_overflow,
+ opt_mult_overflow,
+
+ opt_succ_not_fixnum,
+ opt_succ_overflow,
opt_mod_zero,
opt_div_zero,
+ lshift_amount_changed,
+ lshift_overflow,
+
+ rshift_amount_changed,
+
opt_aref_argc_not_one,
opt_aref_arg_not_fixnum,
opt_aref_not_array,
@@ -343,19 +502,28 @@ make_counters! {
opt_aset_not_fixnum,
opt_aset_not_hash,
+ opt_aref_with_qundef,
+
opt_case_dispatch_megamorphic,
- opt_getinlinecache_miss,
+ opt_getconstant_path_ic_miss,
+ opt_getconstant_path_multi_ractor,
expandarray_splat,
expandarray_postarg,
expandarray_not_array,
- expandarray_rhs_too_small,
+ expandarray_to_ary,
+ expandarray_chain_max_depth,
+ // getblockparam
gbp_wb_required,
+
+ // getblockparamproxy
+ gbpp_unsupported_type,
gbpp_block_param_modified,
gbpp_block_handler_not_none,
gbpp_block_handler_not_iseq,
+ gbpp_block_handler_not_proc,
branchif_interrupted,
branchunless_interrupted,
@@ -364,20 +532,30 @@ make_counters! {
objtostring_not_string,
+ getbyte_idx_not_fixnum,
+ getbyte_idx_negative,
+ getbyte_idx_out_of_bounds,
+
+ splatkw_not_hash,
+ splatkw_not_nil,
+
binding_allocations,
binding_set,
- vm_insns_count,
+ compiled_iseq_entry,
+ cold_iseq_entry,
compiled_iseq_count,
compiled_blockid_count,
compiled_block_count,
compiled_branch_count,
+ compile_time_ns,
compilation_failure,
block_next_count,
defer_count,
defer_empty_count,
branch_insn_count,
branch_known_count,
+ max_inline_versions,
freed_iseq_count,
@@ -389,16 +567,14 @@ make_counters! {
invalidate_ractor_spawn,
invalidate_constant_state_bump,
invalidate_constant_ic_fill,
-
- constant_state_bumps,
-
- // Not using "getivar_" to exclude this from exit reasons
- get_ivar_max_depth,
+ invalidate_no_singleton_class,
// Currently, it's out of the ordinary (might be impossible) for YJIT to leave gaps in
// executable memory, so this should be 0.
exec_mem_non_bump_alloc,
+ code_gc_count,
+
num_gc_obj_refs,
num_send,
@@ -406,6 +582,26 @@ make_counters! {
num_send_polymorphic,
num_send_x86_rel32,
num_send_x86_reg,
+ num_send_dynamic,
+ num_send_cfunc,
+ num_send_cfunc_inline,
+ num_send_iseq,
+ num_send_iseq_leaf,
+ num_send_iseq_inline,
+
+ num_getivar_megamorphic,
+ num_setivar_megamorphic,
+ num_opt_case_dispatch_megamorphic,
+
+ num_throw,
+ num_throw_break,
+ num_throw_retry,
+ num_throw_return,
+
+ num_lazy_frame_check,
+ num_lazy_frame_push,
+ lazy_frame_count,
+ lazy_frame_failure,
iseq_stack_too_large,
iseq_too_long,
@@ -429,6 +625,17 @@ pub extern "C" fn rb_yjit_stats_enabled_p(_ec: EcPtr, _ruby_self: VALUE) -> VALU
}
}
+/// Primitive called in yjit.rb
+/// Check if stats generation should print at exit
+#[no_mangle]
+pub extern "C" fn rb_yjit_print_stats_p(_ec: EcPtr, _ruby_self: VALUE) -> VALUE {
+ if yjit_enabled_p() && get_option!(print_stats) {
+ return Qtrue;
+ } else {
+ return Qfalse;
+ }
+}
+
/// Primitive called in yjit.rb.
/// Export all YJIT statistics as a Ruby hash.
#[no_mangle]
@@ -442,8 +649,7 @@ pub extern "C" fn rb_yjit_get_stats(_ec: EcPtr, _ruby_self: VALUE, context: VALU
/// to be enabled.
#[no_mangle]
pub extern "C" fn rb_yjit_trace_exit_locations_enabled_p(_ec: EcPtr, _ruby_self: VALUE) -> VALUE {
- #[cfg(feature = "stats")]
- if get_option!(gen_trace_exits) {
+ if get_option!(trace_exits).is_some() {
return Qtrue;
}
@@ -459,13 +665,8 @@ pub extern "C" fn rb_yjit_get_exit_locations(_ec: EcPtr, _ruby_self: VALUE) -> V
return Qnil;
}
- // Return if the stats feature is disabled
- if !cfg!(feature = "stats") {
- return Qnil;
- }
-
// Return if --yjit-trace-exits isn't enabled
- if !get_option!(gen_trace_exits) {
+ if get_option!(trace_exits).is_none() {
return Qnil;
}
@@ -487,6 +688,16 @@ pub extern "C" fn rb_yjit_get_exit_locations(_ec: EcPtr, _ruby_self: VALUE) -> V
}
}
+/// Increment a counter by name from the CRuby side
+/// Warning: this is not fast because it requires a hash lookup, so don't use in tight loops
+#[no_mangle]
+pub extern "C" fn rb_yjit_incr_counter(counter_name: *const std::os::raw::c_char) {
+ use std::ffi::CStr;
+ let counter_name = unsafe { CStr::from_ptr(counter_name).to_str().unwrap() };
+ let counter_ptr = get_counter_ptr(counter_name);
+ unsafe { *counter_ptr += 1 };
+}
+
/// Export all YJIT statistics as a Ruby hash.
fn rb_yjit_gen_stats_dict(context: bool) -> VALUE {
// If YJIT is not enabled, return Qnil
@@ -504,7 +715,6 @@ fn rb_yjit_gen_stats_dict(context: bool) -> VALUE {
let hash = unsafe { rb_hash_new() };
- // CodeBlock stats
unsafe {
// Get the inline and outlined code blocks
let cb = CodegenGlobals::get_inline_cb();
@@ -526,15 +736,11 @@ fn rb_yjit_gen_stats_dict(context: bool) -> VALUE {
// Live pages
hash_aset_usize!(hash, "live_page_count", cb.num_mapped_pages() - freed_page_count);
- // Code GC count
- hash_aset_usize!(hash, "code_gc_count", CodegenGlobals::get_code_gc_count());
-
// Size of memory region allocated for JIT code
hash_aset_usize!(hash, "code_region_size", cb.mapped_region_size());
// Rust global allocations in bytes
- #[cfg(feature="stats")]
- hash_aset_usize!(hash, "yjit_alloc_size", global_allocation_size());
+ hash_aset_usize!(hash, "yjit_alloc_size", GLOBAL_ALLOCATOR.alloc_size.load(Ordering::SeqCst));
// `context` is true at RubyVM::YJIT._print_stats for --yjit-stats. It's false by default
// for RubyVM::YJIT.runtime_stats because counting all Contexts could be expensive.
@@ -544,15 +750,30 @@ fn rb_yjit_gen_stats_dict(context: bool) -> VALUE {
hash_aset_usize!(hash, "live_context_count", live_context_count);
hash_aset_usize!(hash, "live_context_size", live_context_count * context_size);
}
+
+ // VM instructions count
+ hash_aset_usize!(hash, "vm_insns_count", rb_vm_insns_count as usize);
+
+ hash_aset_usize!(hash, "live_iseq_count", rb_yjit_live_iseq_count as usize);
+ hash_aset_usize!(hash, "iseq_alloc_count", rb_yjit_iseq_alloc_count as usize);
}
- // If we're not generating stats, the hash is done
+ // If we're not generating stats, put only default counters
if !get_option!(gen_stats) {
+ for counter in DEFAULT_COUNTERS {
+ // Get the counter value
+ let counter_ptr = get_counter_ptr(&counter.get_name());
+ let counter_val = unsafe { *counter_ptr };
+
+ // Put counter into hash
+ let key = rust_str_to_sym(&counter.get_name());
+ let value = VALUE::fixnum_from_usize(counter_val as usize);
+ unsafe { rb_hash_aset(hash, key, value); }
+ }
+
return hash;
}
- // If the stats feature is enabled
-
unsafe {
// Indicate that the complete set of stats is available
rb_hash_aset(hash, rust_str_to_sym("all_stats"), Qtrue);
@@ -563,13 +784,6 @@ fn rb_yjit_gen_stats_dict(context: bool) -> VALUE {
let counter_ptr = get_counter_ptr(counter_name);
let counter_val = *counter_ptr;
- #[cfg(not(feature = "stats"))]
- if counter_name == &"vm_insns_count" {
- // If the stats feature is disabled, we don't have vm_insns_count
- // so we are going to exclude the key
- continue;
- }
-
// Put counter into hash
let key = rust_str_to_sym(counter_name);
let value = VALUE::fixnum_from_usize(counter_val as usize);
@@ -585,6 +799,31 @@ fn rb_yjit_gen_stats_dict(context: bool) -> VALUE {
let value = VALUE::fixnum_from_usize(EXIT_OP_COUNT[op_idx] as usize);
rb_hash_aset(hash, key, value);
}
+
+ fn set_call_counts(
+ calls_hash: VALUE,
+ method_name_to_idx: &mut Option<HashMap<String, usize>>,
+ method_call_count: &mut Option<Vec<u64>>,
+ ) {
+ if let (Some(name_to_idx), Some(call_counts)) = (method_name_to_idx, method_call_count) {
+ for (name, idx) in name_to_idx {
+ let count = call_counts[*idx];
+ let key = rust_str_to_sym(name);
+ let value = VALUE::fixnum_from_usize(count as usize);
+ unsafe { rb_hash_aset(calls_hash, key, value); }
+ }
+ }
+ }
+
+ // Create a hash for the cfunc call counts
+ let cfunc_calls = rb_hash_new();
+ rb_hash_aset(hash, rust_str_to_sym("cfunc_calls"), cfunc_calls);
+ set_call_counts(cfunc_calls, &mut *addr_of_mut!(CFUNC_NAME_TO_IDX), &mut *addr_of_mut!(CFUNC_CALL_COUNT));
+
+ // Create a hash for the ISEQ call counts
+ let iseq_calls = rb_hash_new();
+ rb_hash_aset(hash, rust_str_to_sym("iseq_calls"), iseq_calls);
+ set_call_counts(iseq_calls, &mut *addr_of_mut!(ISEQ_NAME_TO_IDX), &mut *addr_of_mut!(ISEQ_CALL_COUNT));
}
hash
@@ -619,13 +858,8 @@ pub extern "C" fn rb_yjit_record_exit_stack(_exit_pc: *const VALUE)
return;
}
- // Return if the stats feature is disabled
- if !cfg!(feature = "stats") {
- return;
- }
-
// Return if --yjit-trace-exits isn't enabled
- if !get_option!(gen_trace_exits) {
+ if get_option!(trace_exits).is_none() {
return;
}
@@ -747,12 +981,6 @@ pub extern "C" fn rb_yjit_reset_stats_bang(_ec: EcPtr, _ruby_self: VALUE) -> VAL
return Qnil;
}
-/// Increment the number of instructions executed by the interpreter
-#[no_mangle]
-pub extern "C" fn rb_yjit_collect_vm_usage_insn() {
- incr_counter!(vm_insns_count);
-}
-
#[no_mangle]
pub extern "C" fn rb_yjit_collect_binding_alloc() {
incr_counter!(binding_allocations);
@@ -778,9 +1006,11 @@ pub extern "C" fn rb_yjit_count_side_exit_op(exit_pc: *const VALUE) -> *const VA
return exit_pc;
}
-// Get the size of global allocations in Rust.
-#[cfg(feature="stats")]
-fn global_allocation_size() -> usize {
- let stats = GLOBAL_ALLOCATOR.stats();
- stats.bytes_allocated.saturating_sub(stats.bytes_deallocated)
+/// Measure the time taken by func() and add that to yjit_compile_time.
+pub fn with_compile_time<F, R>(func: F) -> R where F: FnOnce() -> R {
+ let start = Instant::now();
+ let ret = func();
+ let nanos = Instant::now().duration_since(start).as_nanos();
+ incr_counter_by!(compile_time_ns, nanos);
+ ret
}
diff --git a/yjit/src/utils.rs b/yjit/src/utils.rs
index 396b330abf..6bc66ee33e 100644
--- a/yjit/src/utils.rs
+++ b/yjit/src/utils.rs
@@ -5,7 +5,7 @@ use crate::cruby::*;
use std::slice;
/// Trait for casting to [usize] that allows you to say `.as_usize()`.
-/// Implementation conditional on the the cast preserving the numeric value on
+/// Implementation conditional on the cast preserving the numeric value on
/// all inputs and being inexpensive.
///
/// [usize] is only guaranteed to be more than 16-bit wide, so we can't use
@@ -51,6 +51,20 @@ impl IntoUsize for u8 {
}
}
+/// The [Into<u64>] Rust does not provide.
+/// Convert to u64 with assurance that the value is preserved.
+/// Currently, `usize::BITS == 64` holds for all platforms we support.
+pub(crate) trait IntoU64 {
+ fn as_u64(self) -> u64;
+}
+
+#[cfg(target_pointer_width = "64")]
+impl IntoU64 for usize {
+ fn as_u64(self) -> u64 {
+ self as u64
+ }
+}
+
/// Compute an offset in bytes of a given struct field
#[allow(unused)]
macro_rules! offset_of {
@@ -73,7 +87,7 @@ pub(crate) use offset_of;
// Convert a CRuby UTF-8-encoded RSTRING into a Rust string.
// This should work fine on ASCII strings and anything else
// that is considered legal UTF-8, including embedded nulls.
-fn ruby_str_to_rust(v: VALUE) -> String {
+pub fn ruby_str_to_rust(v: VALUE) -> String {
let str_ptr = unsafe { rb_RSTRING_PTR(v) } as *mut u8;
let str_len: usize = unsafe { rb_RSTRING_LEN(v) }.try_into().unwrap();
let str_slice: &[u8] = unsafe { slice::from_raw_parts(str_ptr, str_len) };
@@ -219,7 +233,7 @@ pub fn print_str(asm: &mut Assembler, str: &str) {
asm.bake_string(str);
asm.write_label(after_string);
- let opnd = asm.lea_label(string_data);
+ let opnd = asm.lea_jump_target(string_data);
asm.ccall(print_str_cfun as *const u8, vec![opnd, Opnd::UImm(str.len() as u64)]);
asm.cpop_all();
@@ -263,7 +277,7 @@ mod tests {
let mut cb = CodeBlock::new_dummy(1024);
print_int(&mut asm, Opnd::Imm(42));
- asm.compile(&mut cb, None);
+ asm.compile(&mut cb, None).unwrap();
}
#[test]
@@ -272,6 +286,6 @@ mod tests {
let mut cb = CodeBlock::new_dummy(1024);
print_str(&mut asm, "Hello, world!");
- asm.compile(&mut cb, None);
+ asm.compile(&mut cb, None).unwrap();
}
}
diff --git a/yjit/src/virtualmem.rs b/yjit/src/virtualmem.rs
index d6e5089dac..f3c0ceefff 100644
--- a/yjit/src/virtualmem.rs
+++ b/yjit/src/virtualmem.rs
@@ -57,14 +57,39 @@ pub trait Allocator {
fn mark_unused(&mut self, ptr: *const u8, size: u32) -> bool;
}
-/// Pointer into a [VirtualMemory].
-/// We may later change this to wrap an u32.
-/// Note: there is no NULL constant for CodePtr. You should use Option<CodePtr> instead.
+/// Pointer into a [VirtualMemory] represented as an offset from the base.
+/// Note: there is no NULL constant for [CodePtr]. You should use `Option<CodePtr>` instead.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug)]
#[repr(C, packed)]
-pub struct CodePtr(NonNull<u8>);
+pub struct CodePtr(u32);
impl CodePtr {
+ /// Advance the CodePtr. Can return a dangling pointer.
+ pub fn add_bytes(self, bytes: usize) -> Self {
+ let CodePtr(raw) = self;
+ let bytes: u32 = bytes.try_into().unwrap();
+ CodePtr(raw + bytes)
+ }
+
+ /// Note that the raw pointer might be dangling if there hasn't
+ /// been any writes to it through the [VirtualMemory] yet.
+ pub fn raw_ptr(self, base: &impl CodePtrBase) -> *const u8 {
+ let CodePtr(offset) = self;
+ return base.base_ptr().as_ptr().wrapping_add(offset.as_usize())
+ }
+
+ /// Get the address of the code pointer.
+ pub fn raw_addr(self, base: &impl CodePtrBase) -> usize {
+ self.raw_ptr(base) as usize
+ }
+
+ /// Get the offset component for the code pointer. Useful finding the distance between two
+ /// code pointers that share the same [VirtualMem].
+ pub fn as_offset(self) -> i64 {
+ let CodePtr(offset) = self;
+ offset.into()
+ }
+
pub fn as_side_exit(self) -> Target {
Target::SideExitPtr(self)
}
@@ -98,7 +123,7 @@ impl<A: Allocator> VirtualMemory<A> {
/// Return the start of the region as a raw pointer. Note that it could be a dangling
/// pointer so be careful dereferencing it.
pub fn start_ptr(&self) -> CodePtr {
- CodePtr(self.region_start)
+ CodePtr(0)
}
pub fn mapped_end_ptr(&self) -> CodePtr {
@@ -128,7 +153,7 @@ impl<A: Allocator> VirtualMemory<A> {
/// Write a single byte. The first write to a page makes it readable.
pub fn write_byte(&mut self, write_ptr: CodePtr, byte: u8) -> Result<(), WriteError> {
let page_size = self.page_size_bytes;
- let raw: *mut u8 = write_ptr.raw_ptr() as *mut u8;
+ let raw: *mut u8 = write_ptr.raw_ptr(self) as *mut u8;
let page_addr = (raw as usize / page_size) * page_size;
if self.current_write_page == Some(page_addr) {
@@ -209,57 +234,30 @@ impl<A: Allocator> VirtualMemory<A> {
/// Free a range of bytes. start_ptr must be memory page-aligned.
pub fn free_bytes(&mut self, start_ptr: CodePtr, size: u32) {
- assert_eq!(start_ptr.into_usize() % self.page_size_bytes, 0);
+ assert_eq!(start_ptr.raw_ptr(self) as usize % self.page_size_bytes, 0);
// Bounds check the request. We should only free memory we manage.
- let mapped_region = self.start_ptr().raw_ptr()..self.mapped_end_ptr().raw_ptr();
- let virtual_region = self.start_ptr().raw_ptr()..self.virtual_end_ptr().raw_ptr();
- let last_byte_to_free = start_ptr.add_bytes(size.saturating_sub(1).as_usize()).raw_ptr();
- assert!(mapped_region.contains(&start_ptr.raw_ptr()));
+ let mapped_region = self.start_ptr().raw_ptr(self)..self.mapped_end_ptr().raw_ptr(self);
+ let virtual_region = self.start_ptr().raw_ptr(self)..self.virtual_end_ptr().raw_ptr(self);
+ let last_byte_to_free = start_ptr.add_bytes(size.saturating_sub(1).as_usize()).raw_ptr(self);
+ assert!(mapped_region.contains(&start_ptr.raw_ptr(self)));
// On platforms where code page size != memory page size (e.g. Linux), we often need
// to free code pages that contain unmapped memory pages. When it happens on the last
// code page, it's more appropriate to check the last byte against the virtual region.
assert!(virtual_region.contains(&last_byte_to_free));
- self.allocator.mark_unused(start_ptr.0.as_ptr(), size);
+ self.allocator.mark_unused(start_ptr.raw_ptr(self), size);
}
}
-impl CodePtr {
- /// Note that the raw pointer might be dangling if there hasn't
- /// been any writes to it through the [VirtualMemory] yet.
- pub fn raw_ptr(self) -> *const u8 {
- let CodePtr(ptr) = self;
- return ptr.as_ptr();
- }
-
- /// Advance the CodePtr. Can return a dangling pointer.
- pub fn add_bytes(self, bytes: usize) -> Self {
- let CodePtr(raw) = self;
- CodePtr(NonNull::new(raw.as_ptr().wrapping_add(bytes)).unwrap())
- }
-
- pub fn into_i64(self) -> i64 {
- let CodePtr(ptr) = self;
- ptr.as_ptr() as i64
- }
-
- #[cfg(target_arch = "aarch64")]
- pub fn into_u64(self) -> u64 {
- let CodePtr(ptr) = self;
- ptr.as_ptr() as u64
- }
-
- pub fn into_usize(self) -> usize {
- let CodePtr(ptr) = self;
- ptr.as_ptr() as usize
- }
+/// Something that could provide a base pointer to compute a raw pointer from a [CodePtr].
+pub trait CodePtrBase {
+ fn base_ptr(&self) -> NonNull<u8>;
}
-impl From<*mut u8> for CodePtr {
- fn from(value: *mut u8) -> Self {
- assert!(value as usize != 0);
- return CodePtr(NonNull::new(value).unwrap());
+impl<A: Allocator> CodePtrBase for VirtualMemory<A> {
+ fn base_ptr(&self) -> NonNull<u8> {
+ self.region_start
}
}
@@ -416,7 +414,7 @@ pub mod tests {
let one_past_end = virt.start_ptr().add_bytes(virt.virtual_region_size());
assert_eq!(Err(OutOfBounds), virt.write_byte(one_past_end, 0));
- let end_of_addr_space = CodePtr(NonNull::new(usize::MAX as _).unwrap());
+ let end_of_addr_space = CodePtr(u32::MAX);
assert_eq!(Err(OutOfBounds), virt.write_byte(end_of_addr_space, 0));
}
diff --git a/yjit/src/yjit.rs b/yjit/src/yjit.rs
index 08440d7076..cc2c8fe066 100644
--- a/yjit/src/yjit.rs
+++ b/yjit/src/yjit.rs
@@ -5,18 +5,15 @@ use crate::invariants::*;
use crate::options::*;
use crate::stats::YjitExitLocations;
use crate::stats::incr_counter;
+use crate::stats::with_compile_time;
use std::os::raw;
-use std::sync::atomic::{AtomicBool, Ordering};
-/// For tracking whether the user enabled YJIT through command line arguments or environment
-/// variables. AtomicBool to avoid `unsafe`. On x86 it compiles to simple movs.
-/// See <https://doc.rust-lang.org/std/sync/atomic/enum.Ordering.html>
-/// See [rb_yjit_enabled_p]
-static YJIT_ENABLED: AtomicBool = AtomicBool::new(false);
-
-/// When false, we don't compile new iseqs, but might still service existing branch stubs.
-static COMPILE_NEW_ISEQS: AtomicBool = AtomicBool::new(false);
+/// Is YJIT on? The interpreter uses this variable to decide whether to trigger
+/// compilation. See jit_exec() and jit_compile().
+#[allow(non_upper_case_globals)]
+#[no_mangle]
+pub static mut rb_yjit_enabled_p: bool = false;
/// Parse one command-line option.
/// This is called from ruby.c
@@ -25,35 +22,25 @@ pub extern "C" fn rb_yjit_parse_option(str_ptr: *const raw::c_char) -> bool {
return parse_option(str_ptr).is_some();
}
-/// Is YJIT on? The interpreter uses this function to decide whether to increment
-/// ISEQ call counters. See jit_exec().
-/// This is used frequently since it's used on every method call in the interpreter.
-#[no_mangle]
-pub extern "C" fn rb_yjit_enabled_p() -> raw::c_int {
- // Note that we might want to call this function from signal handlers so
- // might need to ensure signal-safety(7).
- YJIT_ENABLED.load(Ordering::Acquire).into()
-}
-
-#[no_mangle]
-pub extern "C" fn rb_yjit_compile_new_iseqs() -> bool {
- COMPILE_NEW_ISEQS.load(Ordering::Acquire).into()
-}
-
/// Like rb_yjit_enabled_p, but for Rust code.
pub fn yjit_enabled_p() -> bool {
- YJIT_ENABLED.load(Ordering::Acquire)
+ unsafe { rb_yjit_enabled_p }
}
-/// After how many calls YJIT starts compiling a method
+/// This function is called from C code
#[no_mangle]
-pub extern "C" fn rb_yjit_call_threshold() -> raw::c_uint {
- get_option!(call_threshold) as raw::c_uint
+pub extern "C" fn rb_yjit_init(yjit_enabled: bool) {
+ // Register the method codegen functions. This must be done at boot.
+ yjit_reg_method_codegen_fns();
+
+ // If --yjit-disable, yjit_init() will not be called until RubyVM::YJIT.enable.
+ if yjit_enabled && !get_option!(disable) {
+ yjit_init();
+ }
}
-/// This function is called from C code
-#[no_mangle]
-pub extern "C" fn rb_yjit_init_rust() {
+/// Initialize and enable YJIT. You should call this at boot or with GVL.
+fn yjit_init() {
// TODO: need to make sure that command-line options have been
// initialized by CRuby
@@ -63,19 +50,32 @@ pub extern "C" fn rb_yjit_init_rust() {
Invariants::init();
CodegenGlobals::init();
YjitExitLocations::init();
+ ids::init();
rb_bug_panic_hook();
// YJIT enabled and initialized successfully
- YJIT_ENABLED.store(true, Ordering::Release);
-
- COMPILE_NEW_ISEQS.store(!get_option!(pause), Ordering::Release);
+ assert!(unsafe{ !rb_yjit_enabled_p });
+ unsafe { rb_yjit_enabled_p = true; }
});
if let Err(_) = result {
- println!("YJIT: rb_yjit_init_rust() panicked. Aborting.");
+ println!("YJIT: yjit_init() panicked. Aborting.");
std::process::abort();
}
+
+ // Make sure --yjit-perf doesn't append symbols to an old file
+ if get_option!(perf_map).is_some() {
+ let perf_map = format!("/tmp/perf-{}.map", std::process::id());
+ let _ = std::fs::remove_file(&perf_map);
+ println!("YJIT perf map: {perf_map}");
+ }
+
+ // Initialize the GC hooks. Do this at last as some code depend on Rust initialization.
+ extern "C" {
+ fn rb_yjit_init_gc_hooks();
+ }
+ unsafe { rb_yjit_init_gc_hooks() }
}
/// At the moment, we abort in all cases we panic.
@@ -108,8 +108,15 @@ fn rb_bug_panic_hook() {
/// Called from C code to begin compiling a function
/// NOTE: this should be wrapped in RB_VM_LOCK_ENTER(), rb_vm_barrier() on the C side
+/// If jit_exception is true, compile JIT code for handling exceptions.
+/// See [jit_compile_exception] for details.
#[no_mangle]
-pub extern "C" fn rb_yjit_iseq_gen_entry_point(iseq: IseqPtr, ec: EcPtr) -> *const u8 {
+pub extern "C" fn rb_yjit_iseq_gen_entry_point(iseq: IseqPtr, ec: EcPtr, jit_exception: bool) -> *const u8 {
+ // Don't compile when there is insufficient native stack space
+ if unsafe { rb_ec_stack_check(ec as _) } != 0 {
+ return std::ptr::null();
+ }
+
// Reject ISEQs with very large temp stacks,
// this will allow us to use u8/i8 values to track stack_size and sp_offset
let stack_max = unsafe { rb_get_iseq_body_stack_max(iseq) };
@@ -127,10 +134,18 @@ pub extern "C" fn rb_yjit_iseq_gen_entry_point(iseq: IseqPtr, ec: EcPtr) -> *con
return std::ptr::null();
}
- let maybe_code_ptr = gen_entry_point(iseq, ec);
+ // If a custom call threshold was not specified on the command-line and
+ // this is a large application (has very many ISEQs), switch to
+ // using the call threshold for large applications after this entry point
+ use crate::stats::rb_yjit_live_iseq_count;
+ if unsafe { rb_yjit_call_threshold } == SMALL_CALL_THRESHOLD && unsafe { rb_yjit_live_iseq_count } > LARGE_ISEQ_COUNT {
+ unsafe { rb_yjit_call_threshold = LARGE_CALL_THRESHOLD; };
+ }
+
+ let maybe_code_ptr = with_compile_time(|| { gen_entry_point(iseq, ec, jit_exception) });
match maybe_code_ptr {
- Some(ptr) => ptr.raw_ptr(),
+ Some(ptr) => ptr,
None => std::ptr::null(),
}
}
@@ -151,13 +166,27 @@ pub extern "C" fn rb_yjit_code_gc(_ec: EcPtr, _ruby_self: VALUE) -> VALUE {
Qnil
}
+/// Enable YJIT compilation, returning true if YJIT was previously disabled
#[no_mangle]
-pub extern "C" fn rb_yjit_resume(_ec: EcPtr, _ruby_self: VALUE) -> VALUE {
- if yjit_enabled_p() {
- COMPILE_NEW_ISEQS.store(true, Ordering::Release);
- }
-
- Qnil
+pub extern "C" fn rb_yjit_enable(_ec: EcPtr, _ruby_self: VALUE, gen_stats: VALUE, print_stats: VALUE) -> VALUE {
+ with_vm_lock(src_loc!(), || {
+ // Initialize and enable YJIT
+ if gen_stats.test() {
+ unsafe {
+ OPTIONS.gen_stats = gen_stats.test();
+ OPTIONS.print_stats = print_stats.test();
+ }
+ }
+ yjit_init();
+
+ // Add "+YJIT" to RUBY_DESCRIPTION
+ extern "C" {
+ fn ruby_set_yjit_description();
+ }
+ unsafe { ruby_set_yjit_description(); }
+
+ Qtrue
+ })
}
/// Simulate a situation where we are out of executable memory
@@ -178,3 +207,19 @@ pub extern "C" fn rb_yjit_simulate_oom_bang(_ec: EcPtr, _ruby_self: VALUE) -> VA
return Qnil;
}
+
+/// Push a C method frame if the given PC is supposed to lazily push one.
+/// This is called from rb_raise() (at rb_exc_new_str()) and other functions
+/// that may make a method call (e.g. rb_to_int()).
+#[no_mangle]
+pub extern "C" fn rb_yjit_lazy_push_frame(pc: *mut VALUE) {
+ if !yjit_enabled_p() {
+ return;
+ }
+
+ incr_counter!(num_lazy_frame_check);
+ if let Some(&(cme, recv_idx)) = CodegenGlobals::get_pc_to_cfunc().get(&pc) {
+ incr_counter!(num_lazy_frame_push);
+ unsafe { rb_vm_push_cfunc_frame(cme, recv_idx as i32) }
+ }
+}
diff --git a/yjit/yjit.mk b/yjit/yjit.mk
index c2553fc0bd..137085376d 100644
--- a/yjit/yjit.mk
+++ b/yjit/yjit.mk
@@ -23,7 +23,7 @@ YJIT_LIB_TOUCH = touch $@
ifeq ($(YJIT_SUPPORT),yes)
$(YJIT_LIBS): $(YJIT_SRC_FILES)
$(ECHO) 'building Rust YJIT (release mode)'
- $(Q) $(RUSTC) $(YJIT_RUSTC_ARGS)
+ +$(Q) $(RUSTC) $(YJIT_RUSTC_ARGS)
$(YJIT_LIB_TOUCH)
else ifeq ($(YJIT_SUPPORT),no)
$(YJIT_LIBS):
@@ -32,7 +32,7 @@ $(YJIT_LIBS):
else ifeq ($(YJIT_SUPPORT),$(filter dev dev_nodebug stats,$(YJIT_SUPPORT)))
$(YJIT_LIBS): $(YJIT_SRC_FILES)
$(ECHO) 'building Rust YJIT ($(YJIT_SUPPORT) mode)'
- $(Q)$(CHDIR) $(top_srcdir)/yjit && \
+ +$(Q)$(CHDIR) $(top_srcdir)/yjit && \
CARGO_TARGET_DIR='$(CARGO_TARGET_DIR)' \
CARGO_TERM_PROGRESS_WHEN='never' \
$(CARGO) $(CARGO_VERBOSE) build $(CARGO_BUILD_ARGS)
@@ -42,17 +42,14 @@ endif
yjit-libobj: $(YJIT_LIBOBJ)
-# Note, BSD handling is in yjit/not_gmake.mk
YJIT_LIB_SYMBOLS = $(YJIT_LIBS:.a=).symbols
$(YJIT_LIBOBJ): $(YJIT_LIBS)
$(ECHO) 'partial linking $(YJIT_LIBS) into $@'
-ifneq ($(findstring linux,$(target_os)),)
- $(Q) $(LD) -r -o $@ --whole-archive $(YJIT_LIBS)
- -$(Q) $(OBJCOPY) --wildcard --keep-global-symbol='$(SYMBOL_PREFIX)rb_*' $(@)
-else ifneq ($(findstring darwin,$(target_os)),)
+ifneq ($(findstring darwin,$(target_os)),)
$(Q) $(CC) -nodefaultlibs -r -o $@ -exported_symbols_list $(YJIT_LIB_SYMBOLS) $(YJIT_LIBS)
else
- false
+ $(Q) $(LD) -r -o $@ --whole-archive $(YJIT_LIBS)
+ -$(Q) $(OBJCOPY) --wildcard --keep-global-symbol='$(SYMBOL_PREFIX)rb_*' $(@)
endif
# For Darwin only: a list of symbols that we want the glommed Rust static lib to export.
@@ -97,13 +94,15 @@ endif
$(MAKE) btest RUN_OPTS='--yjit-call-threshold=1' BTESTS=-j
$(MAKE) test-all TESTS='$(top_srcdir)/test/ruby/test_yjit.rb'
+YJIT_BINDGEN_DIFF_OPTS =
+
# Generate Rust bindings. See source for details.
# Needs `./configure --enable-yjit=dev` and Clang.
ifneq ($(strip $(CARGO)),) # if configure found Cargo
.PHONY: yjit-bindgen yjit-bindgen-show-unused
yjit-bindgen: yjit.$(OBJEXT)
YJIT_SRC_ROOT_PATH='$(top_srcdir)' $(CARGO) run --manifest-path '$(top_srcdir)/yjit/bindgen/Cargo.toml' -- $(CFLAGS) $(XCFLAGS) $(CPPFLAGS)
- $(Q) if [ 'x$(HAVE_GIT)' = xyes ]; then $(GIT) -C "$(top_srcdir)" diff --exit-code yjit/src/cruby_bindings.inc.rs; fi
+ $(Q) if [ 'x$(HAVE_GIT)' = xyes ]; then $(GIT) -C "$(top_srcdir)" diff $(YJIT_BINDGEN_DIFF_OPTS) yjit/src/cruby_bindings.inc.rs; fi
check-yjit-bindgen-unused: yjit.$(OBJEXT)
RUST_LOG=warn YJIT_SRC_ROOT_PATH='$(top_srcdir)' $(CARGO) run --manifest-path '$(top_srcdir)/yjit/bindgen/Cargo.toml' -- $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) 2>&1 | (! grep "unused option: --allow")